Internal change

PiperOrigin-RevId: 334866521
Change-Id: Ib4c3dbf35cc5846075c06536e68a1c23de4b3a28
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fa89fde
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,232 @@
+// Copyright (c) 2008-2014 Marshall A. Greenblatt. Portions Copyright (c)
+// 2006-2009 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/patch/patch.cfg b/patch/patch.cfg
new file mode 100755
index 0000000..c158d63
--- /dev/null
+++ b/patch/patch.cfg
@@ -0,0 +1,93 @@
+# Patch configuration file.
+#
+# Each dictionary entry passed to "register_google_patches" represents a single
+# patch file. Supported key/value pairs are as follows:
+#
+# - 'name'       Required. The name of the patch file without the .patch
+#                extension that will be read from the patches subdirectory.
+# - 'path'       Optional. The repository root for the patch file. Defaults to
+#                the Chromium "src" root. All patch file contents must be
+#                relative to this repository root.
+# - 'condition'  Optional. The patch file will only be applied if an environment
+#                variable with this name exists.
+#
+# Each entry should also include a comment linking the bug the patch relates to.
+
+register_google_patches(
+  {
+    # Make CEF compatible with google3 code
+    #
+    # This fixes the google3 incompatibilities in CEF -- such as conflicting
+    # symbols, macros, and header guards -- to allow CEF and common google3
+    # libraries like //base to be used in client apps.
+    #
+    #  - Do not check for Chromium header include guards when compiling
+    #    //third_party/cef files in google3 (i.e., when USING_GOOGLE3_INCLUDES
+    #    is defined).
+    #
+    #  - Replace the definitions in CEF's include/base/cef_atomicops.h header
+    #    with the google3 definitions in //base/atomicops.h header.
+    #
+    #  - Replace the definitions in CEF's include/base/cef_basictypes.h header
+    #    with the google3 definitions in //base/integral_types.h (and also
+    #    define the char16 type required by CEF).
+    #
+    #  - Replace the definitions in CEF's include/base/cef_logging.h header with
+    #    the google3 definitions in //base/logging.h (and also define the
+    #    NOTREACHED and DCHECK_IS_ON macros required by CEF).
+    #
+    #  - Replace the definitions in CEF's include/base/cef_macros.h header with
+    #    the google3 definitions in //base/macros.h.
+    #
+    #  - Add a kludge to CEF's include/base/cef_callback.h header to support its
+    #    use in the same translation unit as the google3 //base/callback.h
+    #    header.
+    #
+    # See go/thor-desktop-cef-google3-support for more details.
+    #
+    # Bug: b/132197443
+    'name': 'google3_compatibility',
+    'path': 'cef',
+  },
+  {
+    # Include Piper changelist in CEF_VERSION and add CEF_PIPER_CL macro
+    #
+    # This updates the CEF_VERSION macro in cef_version.h to something like
+    # "cef_binary_73.1.12+gee4b49f+chromium-73.0.3683.75+piper-240653526". It
+    # also adds a CEF_PIPER_CL macro with just the changelist number.
+    #
+    # Bug: b/128862468
+    'name': 'cef_version',
+    'path': 'cef',
+  },
+  {
+    # Prefer using non-Depot Tools clang-format for Linux builds
+    #
+    # The clang-format from Depot Tools fails on some Linux Kokoro machines
+    # because of dynamic linker errors. This patch uses the clang-format we
+    # explicitly install instead (if it is in fact installed on the machine).
+    #
+    # Bug: b/122266829
+    'name': 'clang_format_fix',
+    'path': 'cef',
+  },
+  {
+    # Allow service account authentication when fetching the Mac toolchain
+    #
+    # Fetching the Mac toolchain requires cipd be authenticated. The scripts do
+    # not have support for this in a build bot; to fix this, we reinstate a
+    # temporary piece of code removed in
+    # https://crrev.com/a459f7dc6f606c988d58d72e6c71dce69bf5727b.
+    #
+    # Bug: b/159071415
+    'name': 'mac_toolchain_creds',
+  },
+  {
+    # Temporary patch to remove reference to hermetic_xcode_path (now private
+    # and no longer needed).
+    #
+    # Bug: b/159071415
+    'name': 'private_hermetic_xcode',
+    'path': 'cef',
+  },
+)
diff --git a/patch/patches/cef_version.patch b/patch/patches/cef_version.patch
new file mode 100644
index 0000000..789551e
--- /dev/null
+++ b/patch/patches/cef_version.patch
@@ -0,0 +1,67 @@
+diff --git tools/cef_version.py tools/cef_version.py
+index 132438ba..6972680a 100644
+--- tools/cef_version.py
++++ tools/cef_version.py
+@@ -92,6 +92,10 @@ class VersionFormatter:
+       self._branch_version = {'MINOR': minor, 'PATCH': bugfix}
+     return self._branch_version
+ 
++  def get_piper_cl(self):
++    """ Returns the Piper CL. """
++    return os.getenv('CEF_PIPER_CL', '0')
++
+   def get_chromium_version_string(self):
+     """ Returns the Chromium version number string. """
+     chrome_version = self.get_chrome_version_components()
+@@ -170,6 +174,7 @@ class VersionFormatter:
+     chrome_version = self.get_chrome_version_components()
+     chrome_major = chrome_version['MAJOR']
+     chrome_version_part = 'chromium-' + self.get_chromium_version_string()
++    piper_cl_part = 'piper-' + self.get_piper_cl()
+ 
+     cef_commit = self.get_cef_commit_components()
+     cef_commit_hash = self._format_commit_hash(cef_commit['HASH'])
+@@ -187,9 +192,9 @@ class VersionFormatter:
+       cef_branch_name = git.get_branch_name(self.cef_path).split('/')[-1]
+ 
+       self._version_parts = {'MAJOR': int(chrome_major), 'MINOR': 0, 'PATCH': 0}
+-      self._version_string = '%s.0.0-%s.%s+%s+%s' % \
++      self._version_string = '%s.0.0-%s.%s+%s+%s+%s' % \
+              (chrome_major, cef_branch_name, cef_commit['NUMBER'],
+-              cef_commit_hash, chrome_version_part)
++              cef_commit_hash, chrome_version_part, piper_cl_part)
+     else:
+       cef_branch = self.get_cef_branch_version_components()
+ 
+@@ -198,9 +203,9 @@ class VersionFormatter:
+           'MINOR': cef_branch['MINOR'],
+           'PATCH': cef_branch['PATCH']
+       }
+-      self._version_string = '%s.%d.%d+%s+%s' % \
++      self._version_string = '%s.%d.%d+%s+%s+%s' % \
+              (chrome_major, cef_branch['MINOR'], cef_branch['PATCH'],
+-              cef_commit_hash, chrome_version_part)
++              cef_commit_hash, chrome_version_part, piper_cl_part)
+ 
+   def _get_version_string(self):
+     self._compute_version()
+diff --git tools/make_version_header.py tools/make_version_header.py
+index fa2325aa..a7ee5a43 100644
+--- tools/make_version_header.py
++++ tools/make_version_header.py
+@@ -31,6 +31,7 @@ def make_version_header(header):
+ #define CEF_VERSION_PATCH $VERSION_PATCH$
+ #define CEF_COMMIT_NUMBER $COMMIT_NUMBER$
+ #define CEF_COMMIT_HASH "$COMMIT_HASH$"
++#define CEF_PIPER_CL $PIPER_CL$
+ #define COPYRIGHT_YEAR $YEAR$
+ 
+ #define CHROME_VERSION_MAJOR $CHROME_MAJOR$
+@@ -76,6 +77,7 @@ CEF_EXPORT int cef_version_info(int entry);
+   # Substitute hash values for placeholders.
+   result = result.replace('$YEAR$', get_year())
+   result = result.replace('$VERSION$', formatter.get_version_string())
++  result = result.replace('$PIPER_CL$', formatter.get_piper_cl())
+ 
+   commit_components = formatter.get_cef_commit_components()
+   for key in ('HASH', 'NUMBER'):
diff --git a/patch/patches/clang_format_fix.patch b/patch/patches/clang_format_fix.patch
new file mode 100644
index 0000000..40839b2
--- /dev/null
+++ b/patch/patches/clang_format_fix.patch
@@ -0,0 +1,19 @@
+diff --git tools/clang_util.py tools/clang_util.py
+index 48354721..055d3710 100644
+--- tools/clang_util.py
++++ tools/clang_util.py
+@@ -18,6 +18,14 @@ if sys.platform == 'win32':
+ else:
+   clang_format_exe = 'clang-format'
+ 
++# Prefer using non-Depot Tools clang-format for Linux builds. The clang-format
++# from Depot Tools fails on some Linux Kokoro machines because of dynamic linker
++# errors. Use the clang-format we explicitly install instead (if it is in fact
++# installed on the machine). See b/122266829.
++if (sys.platform == 'linux2' and
++    os.path.isfile('/usr/lib/llvm-3.9/bin/clang-format')):
++  clang_format_exe = '/usr/lib/llvm-3.9/bin/clang-format'
++
+ 
+ def clang_format(file_name, file_contents):
+   # -assume-filename is necessary to find the .clang-format file and determine
diff --git a/patch/patches/google3_compatibility.patch b/patch/patches/google3_compatibility.patch
new file mode 100644
index 0000000..ae94e8e
--- /dev/null
+++ b/patch/patches/google3_compatibility.patch
@@ -0,0 +1,417 @@
+diff --git include/base/cef_atomicops.h include/base/cef_atomicops.h
+index 96aebab..501788f 100644
+--- include/base/cef_atomicops.h
++++ include/base/cef_atomicops.h
+@@ -54,7 +54,7 @@
+ #define CEF_INCLUDE_BASE_CEF_ATOMICOPS_H_
+ #pragma once
+ 
+-#if defined(BASE_ATOMICOPS_H_)
++#if defined(BASE_ATOMICOPS_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+@@ -92,9 +92,13 @@
+ // http://code.google.com/p/nativeclient/issues/detail?id=1162
+ typedef int64_t Atomic64;
+ #else
++#if defined(USING_GOOGLE3_INCLUDES)
++typedef int64_t Atomic64;
++#else
+ typedef intptr_t Atomic64;
+ #endif
+ #endif
++#endif
+ 
+ // Use AtomicWord for a machine-sized pointer.  It will use the Atomic32 or
+ // Atomic64 routines below, depending on your architecture.
+diff --git include/base/cef_basictypes.h include/base/cef_basictypes.h
+index e38f4f729..fcb0a5916 100644
+--- include/base/cef_basictypes.h
++++ include/base/cef_basictypes.h
+@@ -37,6 +37,11 @@
+ 
+ #include "include/base/cef_build.h"
+ 
++#if defined(USING_GOOGLE3_INCLUDES)
++// When building CEF in google3, use the google3 header directly.
++#include "base/integral_types.h"
++#else  // !USING_GOOGLE3_INCLUDES
++
+ // The NSPR system headers define 64-bit as |long| when possible, except on
+ // Mac OS X.  In order to not have typedef mismatches, we do the same on LP64.
+ //
+@@ -74,6 +79,8 @@ typedef short int16;
+ typedef unsigned short uint16;
+ #endif
+ 
++#endif  // !USING_GOOGLE3_INCLUDES
++
+ // UTF-16 character type.
+ // This should be kept synchronized with base/strings/string16.h
+ #ifndef char16
+diff --git include/base/cef_bind.h include/base/cef_bind.h
+index 77c9c5573..a917a1887 100644
+--- include/base/cef_bind.h
++++ include/base/cef_bind.h
+@@ -32,7 +32,7 @@
+ #define CEF_INCLUDE_BASE_CEF_BIND_H_
+ #pragma once
+ 
+-#if defined(BASE_BIND_H_)
++#if defined(BASE_BIND_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_bind_helpers.h include/base/cef_bind_helpers.h
+index 2b4798b2c..b220b9934 100644
+--- include/base/cef_bind_helpers.h
++++ include/base/cef_bind_helpers.h
+@@ -170,7 +170,7 @@
+ #define CEF_INCLUDE_BASE_CEF_BIND_HELPERS_H_
+ #pragma once
+ 
+-#if defined(BASE_BIND_HELPERS_H_)
++#if defined(BASE_BIND_HELPERS_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_callback.h include/base/cef_callback.h
+index 16e238a97..83cb0ebe4 100644
+--- include/base/cef_callback.h
++++ include/base/cef_callback.h
+@@ -32,7 +32,7 @@
+ #define CEF_INCLUDE_BASE_CEF_CALLBACK_H_
+ #pragma once
+ 
+-#if defined(BASE_CALLBACK_H_)
++#if defined(BASE_CALLBACK_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_callback_forward.h include/base/cef_callback_forward.h
+index d604d7cfa..e434bd12f 100644
+--- include/base/cef_callback_forward.h
++++ include/base/cef_callback_forward.h
+@@ -32,7 +32,7 @@
+ #define INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_
+ #pragma once
+ 
+-#if defined(BASE_CALLBACK_FORWARD_H_)
++#if defined(BASE_CALLBACK_FORWARD_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+@@ -54,6 +54,27 @@ typedef Callback<void(void)> Closure;
+ 
+ }  // namespace base
+ 
++#if defined(USING_GOOGLE3_INCLUDES)
++
++// The google3 //base/callback-specializations.h header declares class templates
++// in the base::callback_internal namespace that derive from ::Closure. However,
++// those class templates use " : public Closure" to achieve that relationship.
++// Because CEF declares a conflicting Closure type in the base namespace, the
++// google3 header picks up the incorrect Closure (CEF's base::Closure rather
++// than google3's ::Closure) when used in conjunction with the CEF header. The
++// kludge below resolves this issue by pulling the global ::Closure into the
++// base::callback_internal namespace.
++
++class Closure;
++
++namespace base {
++namespace callback_internal {
++using Closure = ::Closure;
++}  // namespace callback_internal
++}  // namespace base
++
++#endif  // USING_GOOGLE3_INCLUDES
++
+ #endif  // !!USING_CHROMIUM_INCLUDES
+ 
+ #endif  // INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_
+diff --git include/base/cef_callback_helpers.h include/base/cef_callback_helpers.h
+index ebe074a1f..1692cfde5 100644
+--- include/base/cef_callback_helpers.h
++++ include/base/cef_callback_helpers.h
+@@ -41,7 +41,7 @@
+ #define CEF_INCLUDE_BASE_CEF_CALLBACK_HELPERS_H_
+ #pragma once
+ 
+-#if defined(BASE_CALLBACK_HELPERS_H_)
++#if defined(BASE_CALLBACK_HELPERS_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_callback_list.h include/base/cef_callback_list.h
+index e0ef3665b..8e0a5d6e5 100644
+--- include/base/cef_callback_list.h
++++ include/base/cef_callback_list.h
+@@ -32,7 +32,7 @@
+ #define CEF_INCLUDE_BASE_CEF_CALLBACK_LIST_H_
+ #pragma once
+ 
+-#if defined(BASE_CALLBACK_LIST_H_)
++#if defined(BASE_CALLBACK_LIST_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_cancelable_callback.h include/base/cef_cancelable_callback.h
+index febce3a32..c1cfbf03d 100644
+--- include/base/cef_cancelable_callback.h
++++ include/base/cef_cancelable_callback.h
+@@ -69,7 +69,7 @@
+ #define CEF_INCLUDE_BASE_CEF_CANCELABLE_CALLBACK_H_
+ #pragma once
+ 
+-#if defined(BASE_CANCELABLE_CALLBACK_H_)
++#if defined(BASE_CANCELABLE_CALLBACK_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_lock.h include/base/cef_lock.h
+index 6909bd6f2..cc122cafd 100644
+--- include/base/cef_lock.h
++++ include/base/cef_lock.h
+@@ -32,7 +32,7 @@
+ #define CEF_INCLUDE_BASE_CEF_LOCK_H_
+ #pragma once
+ 
+-#if defined(BASE_SYNCHRONIZATION_LOCK_H_)
++#if defined(BASE_SYNCHRONIZATION_LOCK_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_logging.h include/base/cef_logging.h
+index 8d8bb889e..fef75d78b 100644
+--- include/base/cef_logging.h
++++ include/base/cef_logging.h
+@@ -136,7 +136,7 @@
+ #define CEF_INCLUDE_BASE_CEF_LOGGING_H_
+ #pragma once
+ 
+-#if defined(DCHECK)
++#if defined(DCHECK) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the macros provided by this header already exist.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+@@ -162,6 +162,12 @@
+ #include <sstream>
+ #include <string>
+ 
++#if defined(USING_GOOGLE3_INCLUDES)
++// When building CEF in google3, use the google3 header directly, but keep the
++// namespaced CEF logging declarations and implementations (but not the macros).
++#include "base/logging.h"
++#endif  // USING_GOOGLE3_INCLUDES
++
+ #include "include/base/cef_build.h"
+ #include "include/base/cef_macros.h"
+ #include "include/internal/cef_logging_internal.h"
+@@ -198,6 +204,8 @@ const LogSeverity LOG_DFATAL = LOG_ERROR;
+ const LogSeverity LOG_DFATAL = LOG_FATAL;
+ #endif
+ 
++#if !defined(USING_GOOGLE3_INCLUDES)
++
+ // A few definitions of macros that don't generate much code. These are used
+ // by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+ // better to have compact code for these operations.
+@@ -355,6 +363,8 @@ const LogSeverity LOG_0 = LOG_ERROR;
+           (val1), (val2), #val1 " " #op " " #val2))           \
+   cef::logging::LogMessage(__FILE__, __LINE__, _result).stream()
+ 
++#endif  // !USING_GOOGLE3_INCLUDES
++
+ // Build the error message string.  This is separate from the "Impl"
+ // function template because it is not performance critical and so can
+ // be out of line, while the "Impl" code should be inline.  Caller
+@@ -392,6 +402,8 @@ extern template std::string* MakeCheckOpString<std::string, std::string>(
+     const char* name);
+ #endif
+ 
++#if !defined(USING_GOOGLE3_INCLUDES)
++
+ // Helper functions for CHECK_OP macro.
+ // The (int, int) specialization works around the issue that the compiler
+ // will not instantiate the template version of the function on values of
+@@ -432,12 +444,16 @@ DEFINE_CHECK_OP_IMPL(GT, >)
+ #define ENABLE_DLOG 1
+ #endif
+ 
++#endif  // !USING_GOOGLE3_INCLUDES
++
+ #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+ #define DCHECK_IS_ON() 0
+ #else
+ #define DCHECK_IS_ON() 1
+ #endif
+ 
++#if !defined(USING_GOOGLE3_INCLUDES)
++
+ // Definitions for DLOG et al.
+ 
+ #if ENABLE_DLOG
+@@ -553,6 +569,8 @@ const LogSeverity LOG_DCHECK = LOG_INFO;
+ #define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+ #define DCHECK_GT(val1, val2) DCHECK_OP(GT, >, val1, val2)
+ 
++#endif  // !USING_GOOGLE3_INCLUDES
++
+ #if defined(NDEBUG) && defined(OS_CHROMEOS)
+ #define NOTREACHED() \
+   LOG(ERROR) << "NOTREACHED() hit in " << __FUNCTION__ << ". "
+@@ -560,10 +578,14 @@ const LogSeverity LOG_DCHECK = LOG_INFO;
+ #define NOTREACHED() DCHECK(false)
+ #endif
+ 
++#if !defined(USING_GOOGLE3_INCLUDES)
++
+ // Redefine the standard assert to use our nice log files
+ #undef assert
+ #define assert(x) DLOG_ASSERT(x)
+ 
++#endif  // !USING_GOOGLE3_INCLUDES
++
+ // This class more or less represents a particular log message.  You
+ // create an instance of LogMessage and then stream stuff to it.
+ // When you finish streaming to it, ~LogMessage is called and the
+@@ -706,6 +728,8 @@ inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+   return out << wstr.c_str();
+ }
+ 
++#if !defined(USING_GOOGLE3_INCLUDES)
++
+ // The NOTIMPLEMENTED() macro annotates codepaths which have
+ // not been implemented yet.
+ //
+@@ -755,6 +779,8 @@ inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+   EAT_STREAM_PARAMETERS
+ #endif
+ 
++#endif  // !USING_GOOGLE3_INCLUDES
++
+ #endif  // !USING_CHROMIUM_INCLUDES
+ 
+ #endif  // CEF_INCLUDE_BASE_CEF_LOGGING_H_
+diff --git include/base/cef_macros.h include/base/cef_macros.h
+index e714529cd..67588c615 100644
+--- include/base/cef_macros.h
++++ include/base/cef_macros.h
+@@ -35,8 +35,10 @@
+ #if defined(USING_CHROMIUM_INCLUDES)
+ // When building CEF include the Chromium header directly.
+ #include "base/macros.h"
+-
+-#else  // !USING_CHROMIUM_INCLUDES
++#elif defined(USING_GOOGLE3_INCLUDES)
++// When building CEF in google3, use the google3 header directly.
++#include "base/macros.h"
++#else  // !USING_GOOGLE3_INCLUDES
+ // The following is substantially similar to the Chromium implementation.
+ // If the Chromium implementation diverges the below implementation should be
+ // updated to match.
+diff --git include/base/cef_platform_thread.h include/base/cef_platform_thread.h
+index d3fdd798e..15cb0951b 100644
+--- include/base/cef_platform_thread.h
++++ include/base/cef_platform_thread.h
+@@ -35,7 +35,7 @@
+ #ifndef CEF_INCLUDE_BASE_PLATFORM_THREAD_H_
+ #define CEF_INCLUDE_BASE_PLATFORM_THREAD_H_
+ 
+-#if defined(BASE_THREADING_PLATFORM_THREAD_H_)
++#if defined(BASE_THREADING_PLATFORM_THREAD_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_ref_counted.h include/base/cef_ref_counted.h
+index 7a687070c..93d8cbeaa 100644
+--- include/base/cef_ref_counted.h
++++ include/base/cef_ref_counted.h
+@@ -33,7 +33,7 @@
+ #define CEF_INCLUDE_BASE_CEF_REF_COUNTED_H_
+ #pragma once
+ 
+-#if defined(BASE_MEMORY_REF_COUNTED_H_)
++#if defined(BASE_MEMORY_REF_COUNTED_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_scoped_ptr.h include/base/cef_scoped_ptr.h
+index eb9e0e29b..6efc7ca85 100644
+--- include/base/cef_scoped_ptr.h
++++ include/base/cef_scoped_ptr.h
+@@ -114,7 +114,7 @@
+ #define CEF_INCLUDE_BASE_CEF_MEMORY_SCOPED_PTR_H_
+ #pragma once
+ 
+-#if defined(BASE_MEMORY_SCOPED_PTR_H_)
++#if defined(BASE_MEMORY_SCOPED_PTR_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_string16.h include/base/cef_string16.h
+index 6afcb79bd..e5cbaa861 100644
+--- include/base/cef_string16.h
++++ include/base/cef_string16.h
+@@ -32,7 +32,7 @@
+ #define CEF_INCLUDE_BASE_CEF_STRING16_H_
+ #pragma once
+ 
+-#if defined(BASE_STRINGS_STRING16_H_)
++#if defined(BASE_STRINGS_STRING16_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_template_util.h include/base/cef_template_util.h
+index 38fa5839c..6c70dd2ac 100644
+--- include/base/cef_template_util.h
++++ include/base/cef_template_util.h
+@@ -32,7 +32,7 @@
+ #define CEF_INCLUDE_BASE_CEF_TEMPLATE_UTIL_H_
+ #pragma once
+ 
+-#if defined(BASE_TEMPLATE_UTIL_H_)
++#if defined(BASE_TEMPLATE_UTIL_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_thread_checker.h include/base/cef_thread_checker.h
+index e48c8d033..c582e1377 100644
+--- include/base/cef_thread_checker.h
++++ include/base/cef_thread_checker.h
+@@ -32,7 +32,7 @@
+ #define CEF_INCLUDE_BASE_THREAD_CHECKER_H_
+ #pragma once
+ 
+-#if defined(BASE_THREADING_THREAD_CHECKER_H_)
++#if defined(BASE_THREADING_THREAD_CHECKER_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_tuple.h include/base/cef_tuple.h
+index aeb6e9cea..f6b86167f 100644
+--- include/base/cef_tuple.h
++++ include/base/cef_tuple.h
+@@ -56,7 +56,7 @@
+ #define CEF_INCLUDE_BASE_CEF_TUPLE_H_
+ #pragma once
+ 
+-#if defined(BASE_TUPLE_H_)
++#if defined(BASE_TUPLE_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
+diff --git include/base/cef_weak_ptr.h include/base/cef_weak_ptr.h
+index 1ba34b9db..2cf2f1787 100644
+--- include/base/cef_weak_ptr.h
++++ include/base/cef_weak_ptr.h
+@@ -96,7 +96,7 @@
+ #define CEF_INCLUDE_BASE_CEF_WEAK_PTR_H_
+ #pragma once
+ 
+-#if defined(BASE_MEMORY_WEAK_PTR_H_)
++#if defined(BASE_MEMORY_WEAK_PTR_H_) && !defined(USING_GOOGLE3_INCLUDES)
+ // Do nothing if the Chromium header has already been included.
+ // This can happen in cases where Chromium code is used directly by the
+ // client application. When using Chromium code directly always include
diff --git a/patch/patches/mac_toolchain_creds.patch b/patch/patches/mac_toolchain_creds.patch
new file mode 100644
index 0000000..c6a53bb
--- /dev/null
+++ b/patch/patches/mac_toolchain_creds.patch
@@ -0,0 +1,18 @@
+diff --git build/mac_toolchain.py build/mac_toolchain.py
+index 866b5f8..56f640c 100755
+--- build/mac_toolchain.py
++++ build/mac_toolchain.py
+@@ -107,6 +107,13 @@
+       'cipd', 'ensure', '-root', binaries_root, '-ensure-file', '-'
+   ]
+ 
++  # Buildbot slaves need to use explicit credentials. LUCI bots should NOT set
++  # this variable. This is temporary code used to make official Xcode bots
++  # happy. https://crbug.com/986488
++  creds = os.environ.get('MAC_TOOLCHAIN_CREDS')
++  if creds:
++    args.extend(['--service-account-json', creds])
++
+   p = subprocess.Popen(
+       args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+       stderr=subprocess.PIPE)
diff --git a/patch/patches/private_hermetic_xcode.patch b/patch/patches/private_hermetic_xcode.patch
new file mode 100644
index 0000000..dffc6f4
--- /dev/null
+++ b/patch/patches/private_hermetic_xcode.patch
@@ -0,0 +1,17 @@
+diff --git BUILD.gn BUILD.gn
+index cd202499..6858350a 100644
+--- BUILD.gn
++++ BUILD.gn
+@@ -169,12 +169,6 @@ if (is_mac) {
+             "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension",
+             root_build_dir),
+       ]
+-      if (!use_system_xcode) {
+-        args += [
+-          "--developer_dir",
+-          hermetic_xcode_path,
+-        ]
+-      }
+       args += ibtool_flags
+     }
+   }
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..c2f0cec
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,56 @@
+*.gypcmd
+*.mk
+*.ncb
+*.opensdf
+*.props
+*.pyc
+*.rules
+*.sdf
+*.sln
+*.sublime-project
+*.sublime-workspace
+*.suo
+*.targets
+*.user
+*.vcproj
+*.vcxproj
+*.vcxproj.filters
+*.vpj
+*.vpw
+*.vpwhistu
+*.vtg
+*.xcodeproj
+*.xcworkspace
+*_proto.xml
+*_proto_cpp.xml
+*~
+!Android.mk
+.*.sw?
+.DS_Store
+.classpath
+.cproject
+.gdb_history
+.gdbinit
+.landmines
+.metadata
+.project
+.pydevproject
+.vscode
+# Settings directory for eclipse
+/.settings
+.checkstyle
+cscope.*
+Session.vim
+tags
+Thumbs.db
+# IDE's
+.vs/
+.kdev4/
+*.kdev4
+# CEF generated directories
+/binary_distrib
+/docs
+# CEF generated files
+/include/cef_config.h
+/include/cef_version.h
+.ccls-cache/
diff --git a/src/.style.cfg b/src/.style.cfg
new file mode 100644
index 0000000..f39eb03
--- /dev/null
+++ b/src/.style.cfg
@@ -0,0 +1,9 @@
+# Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file
+
+# Configuration settings for tools/fix_style.py
+{
+  # Directories containing these path components will be ignored.
+  'ignore_directories': ['yapf'],
+}
diff --git a/src/.style.yapf b/src/.style.yapf
new file mode 100644
index 0000000..de0c6a7
--- /dev/null
+++ b/src/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = chromium
diff --git a/src/AUTHORS.txt b/src/AUTHORS.txt
new file mode 100644
index 0000000..a5dced5
--- /dev/null
+++ b/src/AUTHORS.txt
@@ -0,0 +1,35 @@
+# This file is an addendum to the Chromium AUTHORS file. It lists authors
+# through March 16, 2015 when Git was introduced for source code management.
+# A list of additional authors added after that date can be found by executing
+# this command on a local Git checkout:
+# git log --all --format="%aN <%aE>" | sort -u
+
+Marshall Greenblatt <magreenblatt@gmail.com>
+Jamie Kirkpatrick <jkp@spotify.com>
+Johan Lindström <johanl@spotify.com>
+Igor Pavlov <igor.arabesc.pavlov@gmail.com>
+Yanko Yankov <yyankov@gmail.com>
+Emerick Rogul <emerick@gmail.com>
+Valve Corporation <mac@valvesoftware.com>
+Anthony Taranto <anthony.taranto@gmail.com>
+Joe Andrieu <joe@joeandrieu.com>
+Keith Poole <platima@gmail.com>
+Aviv Rind <avivrind@gmail.com>
+Michael Kaminski <mikeyk730@gmail.com>
+ADInstruments Ltd. <dev.team@adinstruments.com>
+Gus Verdun <gusverdun@gmail.com>
+Joinerysoft Ltd. <cpinfold@joinerysoft.com>
+Johan Björk <phb@spotify.com>
+Dmitry Azaraev <dmitry.azaraev@gmail.com>
+David Xue <ddxue27@gmail.com>
+Russell (Rusty) Richards <fe3o4y@gmail.com>
+Brian Power <powerbf.it@gmail.com>
+Corey Lucier <clucier@adobe.com>
+Mihai Tica <mitica@adobe.com>
+Czarek Tomczak <czarek.tomczak@gmail.com>
+Felix Bruns <felixbruns@spotify.com>
+YuTeh Shen <shenyute@gmail.com>
+Andrei Kurushin <ajax16384@gmail.com>
+Gonzo Berman <gberman@factset.com>
+Jakub Trzebiatowski <kuba.trzebiatowski@gmail.com>
+Nishant Kaushik <nishantk@adobe.com>
diff --git a/src/BUILD.gn b/src/BUILD.gn
new file mode 100644
index 0000000..2722b23
--- /dev/null
+++ b/src/BUILD.gn
@@ -0,0 +1,2382 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+# 2014 the Chromium Authors. All rights reserved. Use of this source code is
+# governed by a BSD-style license that can be found in the LICENSE file.
+#
+# This file provides the GN configuration for the CEF project. This is not a
+# stand-alone configuration -- it must be built from inside the Chromium source
+# tree. See https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding
+# for complete CEF build instructions. See below for links to additional GN
+# documentation.
+#
+# GN Setup:
+#
+#    Optionally configure GN by setting the `GN_DEFINES` and/or `GN_ARGUMENTS`
+#    environment variables.
+#
+#    Example A: Create an official build on Windows:
+#
+#      > set GN_DEFINES=is_official_build=true
+#
+#    Example B: Generate VS2017 project files in addition to the default Ninja
+#               build files on Windows:
+#
+#      > set GN_ARGUMENTS=--ide=vs2017 --sln=cef --filters=//cef/*
+#
+#      After completing the "GN Automated Build" or "GN Manual Build" section
+#      open "out\<build_dir>\cef.sln" for editing and debugging. Building must
+#      still be performed using the Ninja command-line.
+#
+# GN Automated Build:
+#
+#    Run the `automate-git.py` script as described on the BranchesAndBuilding
+#    Wiki page. GN, unlike GYP, supports parallel build configurations. The
+#    following command-line flags have special meaning with GN builds:
+#
+#    --x64-build    Perform an x64 build if specified (requires out/*_GN_x64
+#                   directories), otherwise perform an x86 build (requires
+#                   out/*_GN_x86 directories).
+#
+#    Directories are created subject to your platform and `GN_DEFINES` settings.
+#    See Step 1B in the "GN Manual Build" section below for details.
+#
+# GN Manual Build:
+#
+# 1. Run the `cef_create_projects.[bin|sh]` script. Upon successful completion
+#    proceed to Step 2.
+#
+#    The `cef_create_projects.[bin|sh]` script will automatically do all of the
+#    following:
+#
+#    A. Apply patch files to the Chromium source tree. This includes
+#       `patch/patches/gn_config.patch` which is required for GN integration.
+#
+#    B. Create multiple build output directories appropriate to your platform
+#       and `GN_DEFINES` settings. For example:
+#
+#         x86 debug build   -> out/Debug_GN_x86
+#         x86 release build -> out/Release_GN_x86
+#         x64 debug build   -> out/Debug_GN_x64
+#         x64 release build -> out/Release_GN_x64
+#
+#       Build output directories will be created subject to the following rules
+#       (defined in `tools/gn_args.py` GetAllPlatformConfigs):
+#
+#       - Debug and Release directories will be created on all platforms by
+#         default. Debug directories will not be created when `is_asan=true`.
+#       - x64 directories will always be created on all platforms.
+#       - x86 directories will always be created on Windows.
+#       - x86 directories will be created on Linux when `use_sysroot=true`.
+#
+#    C. Write the `args.gn` file for each build output directory using the
+#       `tools/gn_args.py` script to determine the GN arguments. Some arguments
+#       are required and some are optional. See script output for details in
+#       case of conflicts or recommendations.
+#
+#    D. Run `gn gen` for each build output directory to generate project files.
+#       Ninja files will be generated by default. Additional command-line
+#       arguments (including other project formats) can be specified via the
+#       `GN_ARGUMENTS` environment variable. Run `gn gen --help` for a list of
+#       supported arguments.
+#
+# 2. Run Ninja from the command-line to build. If the build configuration has
+#    changed it will automatically re-run `gn gen` with the same arguments.
+#
+#    > ninja -C out/<build_dir> cefclient cefsimple ceftests
+#
+# GN Manual Packaging:
+#
+#    Run the `make_distrib.[bat|sh]` script as described on the
+#    BranchesAndBuilding Wiki page.
+#
+# Additional GN documentation:
+# http://www.chromium.org/developers/gn-build-configuration
+# https://chromium.googlesource.com/chromium/src/+/master/tools/gn/docs/language.md
+#
+
+import("//base/allocator/allocator.gni")
+import("//build/config/features.gni")
+import("//build/config/locales.gni")
+import("//build/config/sanitizers/sanitizers.gni")
+import("//build/config/ui.gni")
+import("//cef/cef_repack_locales.gni")
+import("//chrome/common/features.gni")
+import("//content/public/app/mac_helpers.gni")
+import("//extensions/buildflags/buildflags.gni")
+import("//media/media_options.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+import("//ppapi/buildflags/buildflags.gni")
+import("//printing/buildflags/buildflags.gni")
+import("//testing/test.gni")
+import("//third_party/icu/config.gni")
+import("//third_party/widevine/cdm/widevine.gni")
+import("//tools/grit/repack.gni")
+import("//tools/grit/grit_rule.gni")
+import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
+import("//ui/gl/features.gni")
+import("//v8/gni/v8.gni")
+if (is_clang) {
+  import("//build/config/clang/clang.gni")
+}
+if (is_linux) {
+  import("//build/config/linux/pkg_config.gni")
+  import("//third_party/fontconfig/fontconfig.gni")
+}
+if (is_mac) {
+  import("//build/config/mac/rules.gni")
+  import("//build/mac/tweak_info_plist.gni")
+  import("//build/util/version.gni")
+  import("//media/cdm/library_cdm/cdm_paths.gni")
+  import("//build/config/mac/base_rules.gni")
+
+  # Template to compile .xib and .storyboard files.
+  #
+  # Arguments
+  #
+  #     sources:
+  #         list of string, sources to compile
+  #
+  #     ibtool_flags:
+  #         (optional) list of string, additional flags to pass to the ibtool
+  template("compile_ib_files") {
+    action_foreach(target_name) {
+      forward_variables_from(invoker,
+                            [
+                              "testonly",
+                              "visibility",
+                            ])
+      assert(defined(invoker.sources),
+            "sources must be specified for $target_name")
+      assert(defined(invoker.output_extension),
+            "output_extension must be specified for $target_name")
+
+      ibtool_flags = []
+      if (defined(invoker.ibtool_flags)) {
+        ibtool_flags = invoker.ibtool_flags
+      }
+
+      _output_extension = invoker.output_extension
+
+      script = "//cef/tools/compile_ib_files.py"
+      sources = invoker.sources
+      outputs = [
+        "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension",
+      ]
+      args = [
+        "--input",
+        "{{source}}",
+        "--output",
+        rebase_path(
+            "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension",
+            root_build_dir),
+      ]
+      if (!use_system_xcode) {
+        args += [
+          "--developer_dir",
+          hermetic_xcode_path,
+        ]
+      }
+      args += ibtool_flags
+    }
+  }
+
+  # Template to compile and package Mac XIB files as bundle data.
+  #
+  # Arguments
+  #
+  #     sources:
+  #         list of string, sources to comiple
+  #
+  #     output_path:
+  #         (optional) string, the path to use for the outputs list in the
+  #         bundle_data step. If unspecified, defaults to bundle_resources_dir.
+  template("mac_xib_bundle_data") {
+    _target_name = target_name
+    _compile_target_name = _target_name + "_compile_ibtool"
+
+    compile_ib_files(_compile_target_name) {
+      forward_variables_from(invoker, [ "testonly" ])
+      visibility = [ ":$_target_name" ]
+      sources = invoker.sources
+      output_extension = "nib"
+      ibtool_flags = [
+        "--minimum-deployment-target",
+        mac_deployment_target,
+
+        # TODO(rsesek): Enable this once all the bots are on Xcode 7+.
+        # "--target-device",
+        # "mac",
+      ]
+    }
+
+    bundle_data(_target_name) {
+      forward_variables_from(invoker,
+                            [
+                              "testonly",
+                              "visibility",
+                            ])
+
+      public_deps = [
+        ":$_compile_target_name",
+      ]
+      sources = get_target_outputs(":$_compile_target_name")
+
+      _output_path = "{{bundle_resources_dir}}"
+      if (defined(invoker.output_path)) {
+        _output_path = invoker.output_path
+      }
+
+      outputs = [
+        "$_output_path/{{source_file_part}}",
+      ]
+    }
+  }
+}
+if (is_win) {
+  import("//build/config/win/console_app.gni")
+  import("//build/config/win/manifest.gni")
+}
+
+if (is_linux) {
+  declare_args() {
+    # The cefclient target depends on GTK packages that are not available in the
+    # default sysroot environment. So we should only use them when we are not
+    # using the sysroot. Alternatively, the developer might not want to use the
+    # GTK dependencies at all, in which case they can set `cef_use_gtk=false`.
+    cef_use_gtk = !use_sysroot
+  }
+}
+
+#
+# Verify required global arguments configured via `gn args`.
+# Set by GetRequiredArgs() in //cef/tools/gn_args.py.
+#
+
+# Set ENABLE_PRINTING=1 ENABLE_BASIC_PRINTING=1.
+assert(enable_basic_printing)
+assert(enable_print_preview)
+
+# Enable support for Widevine CDM.
+assert(enable_widevine)
+
+if (is_clang) {
+  # Don't use the chrome style plugin.
+  assert(!clang_use_chrome_plugins)
+}
+
+if (is_mac) {
+  # Always generate dSYM files. The make_distrib script will fail if
+  # enable_dsyms=true is not explicitly set when is_official_build=false.
+  assert(enable_dsyms)
+}
+
+
+#
+# Local variables.
+#
+
+if (is_mac) {
+  # The tweak_info_plist.py script requires a version number with 4 parts. CEF
+  # uses a version number with 3 parts so just set the last part to 0.
+  cef_plist_version = exec_script(
+    "//cef/tools/cef_version.py", [ "plist" ], "trim string", [])
+
+  # Need to be creative to match dylib version formatting requirements.
+  cef_dylib_version = exec_script(
+    "//cef/tools/cef_version.py", [ "dylib" ], "trim string", [])
+}
+
+# Read file lists from gypi files. The gypi_to_gn.py script does not support
+# variable references so all required variables must be explicitly specified in
+# the below configurations.
+gypi_paths = exec_script("//cef/tools/gypi_to_gn.py",
+                         [ rebase_path("cef_paths.gypi") ],
+                         "scope",
+                         [ "cef_paths.gypi" ])
+gypi_paths2 = exec_script("//cef/tools/gypi_to_gn.py",
+                          [ rebase_path("cef_paths2.gypi") ],
+                          "scope",
+                          [ "cef_paths2.gypi" ])
+
+includes_common = gypi_paths2.includes_common + gypi_paths2.includes_common_capi
+includes_mac = gypi_paths2.includes_mac + gypi_paths2.includes_mac_capi
+includes_linux = gypi_paths2.includes_linux + gypi_paths2.includes_linux_capi
+includes_win = gypi_paths2.includes_win + gypi_paths2.includes_win_capi
+
+#
+# Targets that will be built when depending on "//cef".
+#
+
+group("cef") {
+  testonly = true
+  deps = [
+    ":cefsimple",
+    ":ceftests",
+    ":libcef_static_unittests",
+  ]
+
+  if (!is_linux || use_x11) {
+    deps += [ ":cefclient" ]
+  }
+}
+
+
+#
+# libcef static target.
+#
+
+if (is_win) {
+  # Target for building code that accesses chrome_elf internals. Included from
+  # the //chrome_elf:crash target. Defined as a static_library instead of a
+  # source_set because (a) the source files don't export any symbols and (b)
+  # *_switches.cc duplication otherwise causes linker errors.
+  static_library("chrome_elf_set") {
+    sources = [
+      "libcef/common/crash_reporter_client.cc",
+      "libcef/common/crash_reporter_client.h",
+
+      # Required for crash_keys::GetChromeCrashKeys.
+      # Otherwise we need to copy this array into CEF, which would be difficult
+      # to maintain.
+      "//chrome/common/crash_keys.cc",
+      "//chrome/common/chrome_switches.cc",
+      "//components/flags_ui/flags_ui_switches.cc",
+      "//content/public/common/content_switches.cc",
+    ]
+
+    configs += [
+      "libcef/features:config",
+      "//build/config:precompiled_headers",
+    ]
+
+    if (is_component_build) {
+      # Avoid linker errors with content_switches.cc in component build by not
+      # defining CONTENT_EXPORT.
+      defines = ["COMPILE_CONTENT_STATICALLY"]
+    }
+
+    deps = [
+      "//components/crash/core/common",  # crash_keys
+
+      # Required by chrome_switches.cc
+      "//chrome/common:buildflags",
+      "//ppapi/buildflags:buildflags",
+      "//printing/buildflags:buildflags",
+      "//ui/base:buildflags",
+
+      # Required by content_switches.cc
+      "//media:media_buildflags",
+    ]
+  }
+}
+
+# libcef_static source files that have unit tests.
+source_set("libcef_static_unittested") {
+  sources = [
+    "libcef/browser/devtools/devtools_util.cc",
+    "libcef/browser/devtools/devtools_util.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+
+  configs += [
+    "libcef/features:config",
+    "//build/config:precompiled_headers",
+  ]
+}
+
+# Executable target for libcef_static unit tests.
+test("libcef_static_unittests") {
+  sources = [
+    "libcef/browser/devtools/devtools_util_unittest.cc",
+  ]
+
+  deps = [
+    ":libcef_static_unittested",
+    "//base/test:run_all_unittests",
+    "//testing/gtest",
+  ]
+
+  configs += [
+    "libcef/features:config",
+    "//build/config:precompiled_headers",
+  ]
+}
+
+static_library("libcef_static") {
+  sources = includes_common +
+            gypi_paths.autogen_cpp_includes + [
+    "libcef/browser/audio_capturer.cc",
+    "libcef/browser/audio_capturer.h",
+    "libcef/browser/browser_context.cc",
+    "libcef/browser/browser_context.h",
+    "libcef/browser/browser_context_keyed_service_factories.cc",
+    "libcef/browser/browser_context_keyed_service_factories.h",
+    "libcef/browser/browser_host_impl.cc",
+    "libcef/browser/browser_host_impl.h",
+    "libcef/browser/browser_info.cc",
+    "libcef/browser/browser_info.h",
+    "libcef/browser/browser_info_manager.cc",
+    "libcef/browser/browser_info_manager.h",
+    "libcef/browser/browser_main.cc",
+    "libcef/browser/browser_main.h",
+    "libcef/browser/browser_message_filter.cc",
+    "libcef/browser/browser_message_filter.h",
+    "libcef/browser/browser_message_loop.cc",
+    "libcef/browser/browser_message_loop.h",
+    "libcef/browser/browser_platform_delegate.cc",
+    "libcef/browser/browser_platform_delegate.h",
+    "libcef/browser/browser_platform_delegate_create.cc",
+    "libcef/browser/browser_util.cc",
+    "libcef/browser/browser_util.h",
+    "libcef/browser/chrome_browser_process_stub.cc",
+    "libcef/browser/chrome_browser_process_stub.h",
+    "libcef/browser/chrome_crash_reporter_client_stub.cc",
+    "libcef/browser/chrome_profile_manager_stub.cc",
+    "libcef/browser/chrome_profile_manager_stub.h",
+    "libcef/browser/chrome_profile_stub.cc",
+    "libcef/browser/chrome_profile_stub.h",
+    "libcef/browser/content_browser_client.cc",
+    "libcef/browser/content_browser_client.h",
+    "libcef/browser/context.cc",
+    "libcef/browser/context.h",
+    "libcef/browser/context_menu_params_impl.cc",
+    "libcef/browser/context_menu_params_impl.h",
+    "libcef/browser/devtools/devtools_controller.cc",
+    "libcef/browser/devtools/devtools_controller.h",
+    "libcef/browser/devtools/devtools_file_manager.cc",
+    "libcef/browser/devtools/devtools_file_manager.h",
+    "libcef/browser/devtools/devtools_frontend.cc",
+    "libcef/browser/devtools/devtools_frontend.h",
+    "libcef/browser/devtools/devtools_manager.cc",
+    "libcef/browser/devtools/devtools_manager.h",
+    "libcef/browser/devtools/devtools_manager_delegate.cc",
+    "libcef/browser/devtools/devtools_manager_delegate.h",
+    "libcef/browser/download_item_impl.cc",
+    "libcef/browser/download_item_impl.h",
+    "libcef/browser/download_manager_delegate.cc",
+    "libcef/browser/download_manager_delegate.h",
+    "libcef/browser/extension_impl.cc",
+    "libcef/browser/extension_impl.h",
+    "libcef/browser/extensions/api/storage/sync_value_store_cache.cc",
+    "libcef/browser/extensions/api/storage/sync_value_store_cache.h",
+    "libcef/browser/extensions/api/tabs/tabs_api.cc",
+    "libcef/browser/extensions/api/tabs/tabs_api.h",
+    "libcef/browser/extensions/browser_extensions_util.cc",
+    "libcef/browser/extensions/browser_extensions_util.h",
+    "libcef/browser/extensions/browser_platform_delegate_background.cc",
+    "libcef/browser/extensions/browser_platform_delegate_background.h",
+    "libcef/browser/extensions/chrome_api_registration.cc",
+    "libcef/browser/extensions/chrome_api_registration.h",
+    "libcef/browser/extensions/component_extension_resource_manager.cc",
+    "libcef/browser/extensions/component_extension_resource_manager.h",
+    "libcef/browser/extensions/extensions_api_client.cc",
+    "libcef/browser/extensions/extensions_api_client.h",
+    "libcef/browser/extensions/extensions_browser_api_provider.cc",
+    "libcef/browser/extensions/extensions_browser_api_provider.h",
+    "libcef/browser/extensions/extensions_browser_client.cc",
+    "libcef/browser/extensions/extensions_browser_client.h",
+    "libcef/browser/extensions/extension_background_host.cc",
+    "libcef/browser/extensions/extension_background_host.h",
+    "libcef/browser/extensions/extension_function_details.cc",
+    "libcef/browser/extensions/extension_function_details.h",
+    "libcef/browser/extensions/extension_host_delegate.cc",
+    "libcef/browser/extensions/extension_host_delegate.h",
+    "libcef/browser/extensions/extension_system.cc",
+    "libcef/browser/extensions/extension_system.h",
+    "libcef/browser/extensions/extension_system_factory.cc",
+    "libcef/browser/extensions/extension_system_factory.h",
+    "libcef/browser/extensions/extension_view_host.cc",
+    "libcef/browser/extensions/extension_view_host.h",
+    "libcef/browser/extensions/extension_web_contents_observer.cc",
+    "libcef/browser/extensions/extension_web_contents_observer.h",
+    "libcef/browser/extensions/mime_handler_view_guest_delegate.cc",
+    "libcef/browser/extensions/mime_handler_view_guest_delegate.h",
+    "libcef/browser/extensions/pdf_extension_util.cc",
+    "libcef/browser/extensions/pdf_extension_util.h",
+    "libcef/browser/extensions/pdf_web_contents_helper_client.cc",
+    "libcef/browser/extensions/pdf_web_contents_helper_client.h",
+    "libcef/browser/extensions/value_store/cef_value_store.cc",
+    "libcef/browser/extensions/value_store/cef_value_store.h",
+    "libcef/browser/extensions/value_store/cef_value_store_factory.cc",
+    "libcef/browser/extensions/value_store/cef_value_store_factory.h",
+    "libcef/browser/file_dialog_runner.h",
+    "libcef/browser/file_dialog_manager.cc",
+    "libcef/browser/file_dialog_manager.h",
+    "libcef/browser/frame_host_impl.cc",
+    "libcef/browser/frame_host_impl.h",
+    "libcef/browser/image_impl.cc",
+    "libcef/browser/image_impl.h",
+    "libcef/browser/javascript_dialog_runner.h",
+    "libcef/browser/javascript_dialog_manager.cc",
+    "libcef/browser/javascript_dialog_manager.h",
+    "libcef/browser/media_capture_devices_dispatcher.cc",
+    "libcef/browser/media_capture_devices_dispatcher.h",
+    "libcef/browser/media_router/media_route_impl.cc",
+    "libcef/browser/media_router/media_route_impl.h",
+    "libcef/browser/media_router/media_router_impl.cc",
+    "libcef/browser/media_router/media_router_impl.h",
+    "libcef/browser/media_router/media_router_manager.cc",
+    "libcef/browser/media_router/media_router_manager.h",
+    "libcef/browser/media_router/media_sink_impl.cc",
+    "libcef/browser/media_router/media_sink_impl.h",
+    "libcef/browser/media_router/media_source_impl.cc",
+    "libcef/browser/media_router/media_source_impl.h",
+    "libcef/browser/menu_manager.cc",
+    "libcef/browser/menu_manager.h",
+    "libcef/browser/menu_model_impl.cc",
+    "libcef/browser/menu_model_impl.h",
+    "libcef/browser/menu_runner.h",
+    "libcef/browser/native/browser_platform_delegate_native.cc",
+    "libcef/browser/native/browser_platform_delegate_native.h",
+    "libcef/browser/navigate_params.cc",
+    "libcef/browser/navigate_params.h",
+    "libcef/browser/navigation_entry_impl.cc",
+    "libcef/browser/navigation_entry_impl.h",
+    "libcef/browser/net/chrome_scheme_handler.cc",
+    "libcef/browser/net/chrome_scheme_handler.h",
+    "libcef/browser/net/crlset_file_util_impl.cc",
+    "libcef/browser/net/devtools_scheme_handler.cc",
+    "libcef/browser/net/devtools_scheme_handler.h",
+    "libcef/browser/net/internal_scheme_handler.cc",
+    "libcef/browser/net/internal_scheme_handler.h",
+    "libcef/browser/net/scheme_handler.cc",
+    "libcef/browser/net/scheme_handler.h",
+    "libcef/browser/net_service/browser_urlrequest_impl.cc",
+    "libcef/browser/net_service/browser_urlrequest_impl.h",
+    "libcef/browser/net_service/cookie_helper.cc",
+    "libcef/browser/net_service/cookie_helper.h",
+    "libcef/browser/net_service/cookie_manager_impl.cc",
+    "libcef/browser/net_service/cookie_manager_impl.h",
+    "libcef/browser/net_service/login_delegate.cc",
+    "libcef/browser/net_service/login_delegate.h",
+    "libcef/browser/net_service/proxy_url_loader_factory.cc",
+    "libcef/browser/net_service/proxy_url_loader_factory.h",
+    "libcef/browser/net_service/resource_handler_wrapper.cc",
+    "libcef/browser/net_service/resource_handler_wrapper.h",
+    "libcef/browser/net_service/resource_request_handler_wrapper.cc",
+    "libcef/browser/net_service/resource_request_handler_wrapper.h",
+    "libcef/browser/net_service/response_filter_wrapper.cc",
+    "libcef/browser/net_service/response_filter_wrapper.h",
+    "libcef/browser/net_service/stream_reader_url_loader.cc",
+    "libcef/browser/net_service/stream_reader_url_loader.h",
+    "libcef/browser/net_service/url_loader_factory_getter.cc",
+    "libcef/browser/net_service/url_loader_factory_getter.h",
+    "libcef/browser/origin_whitelist_impl.cc",
+    "libcef/browser/origin_whitelist_impl.h",
+    "libcef/browser/osr/browser_platform_delegate_osr.cc",
+    "libcef/browser/osr/browser_platform_delegate_osr.h",
+    "libcef/browser/osr/host_display_client_osr.cc",
+    "libcef/browser/osr/host_display_client_osr.h",
+    "libcef/browser/osr/motion_event_osr.cc",
+    "libcef/browser/osr/motion_event_osr.h",
+    "libcef/browser/osr/osr_accessibility_util.cc",
+    "libcef/browser/osr/osr_accessibility_util.h",
+    "libcef/browser/osr/osr_util.cc",
+    "libcef/browser/osr/osr_util.h",
+    "libcef/browser/osr/render_widget_host_view_osr.cc",
+    "libcef/browser/osr/render_widget_host_view_osr.h",
+    "libcef/browser/osr/synthetic_gesture_target_osr.cc",
+    "libcef/browser/osr/synthetic_gesture_target_osr.h",
+    "libcef/browser/osr/video_consumer_osr.cc",
+    "libcef/browser/osr/video_consumer_osr.h",
+    "libcef/browser/osr/web_contents_view_osr.cc",
+    "libcef/browser/osr/web_contents_view_osr.h",
+    "libcef/browser/path_util_impl.cc",
+    "libcef/browser/plugins/plugin_service_filter.cc",
+    "libcef/browser/plugins/plugin_service_filter.h",
+    "libcef/browser/prefs/browser_prefs.cc",
+    "libcef/browser/prefs/browser_prefs.h",
+    "libcef/browser/prefs/pref_store.cc",
+    "libcef/browser/prefs/pref_store.h",
+    "libcef/browser/prefs/renderer_prefs.cc",
+    "libcef/browser/prefs/renderer_prefs.h",
+    "libcef/browser/print_settings_impl.cc",
+    "libcef/browser/print_settings_impl.h",
+    "libcef/browser/printing/constrained_window_views_client.cc",
+    "libcef/browser/printing/constrained_window_views_client.h",
+    "libcef/browser/printing/printing_message_filter.cc",
+    "libcef/browser/printing/printing_message_filter.h",
+    "libcef/browser/printing/print_view_manager.cc",
+    "libcef/browser/printing/print_view_manager.h",
+    "libcef/browser/process_util_impl.cc",
+    "libcef/browser/resource_context.cc",
+    "libcef/browser/resource_context.h",
+    "libcef/browser/request_context_handler_map.cc",
+    "libcef/browser/request_context_handler_map.h",
+    "libcef/browser/request_context_impl.cc",
+    "libcef/browser/request_context_impl.h",
+    "libcef/browser/scheme_impl.cc",
+    "libcef/browser/server_impl.cc",
+    "libcef/browser/server_impl.h",
+    "libcef/browser/speech_recognition_manager_delegate.cc",
+    "libcef/browser/speech_recognition_manager_delegate.h",
+    "libcef/browser/ssl_host_state_delegate.cc",
+    "libcef/browser/ssl_host_state_delegate.h",
+    "libcef/browser/ssl_info_impl.cc",
+    "libcef/browser/ssl_info_impl.h",
+    "libcef/browser/ssl_status_impl.cc",
+    "libcef/browser/ssl_status_impl.h",
+    "libcef/browser/stream_impl.cc",
+    "libcef/browser/stream_impl.h",
+    "libcef/browser/trace_impl.cc",
+    "libcef/browser/trace_subscriber.cc",
+    "libcef/browser/trace_subscriber.h",
+    "libcef/browser/thread_util.h",
+    "libcef/browser/web_contents_dialog_helper.cc",
+    "libcef/browser/web_contents_dialog_helper.h",
+    "libcef/browser/web_plugin_impl.cc",
+    "libcef/browser/web_plugin_impl.h",
+    "libcef/browser/x509_certificate_impl.cc",
+    "libcef/browser/x509_certificate_impl.h",
+    "libcef/browser/x509_cert_principal_impl.cc",
+    "libcef/browser/x509_cert_principal_impl.h",
+    "libcef/browser/xml_reader_impl.cc",
+    "libcef/browser/xml_reader_impl.h",
+    "libcef/browser/zip_reader_impl.cc",
+    "libcef/browser/zip_reader_impl.h",
+    "libcef/common/base_impl.cc",
+    "libcef/common/cef_message_generator.cc",
+    "libcef/common/cef_message_generator.h",
+    "libcef/common/cef_messages.cc",
+    "libcef/common/cef_messages.h",
+    "libcef/common/cef_switches.cc",
+    "libcef/common/cef_switches.h",
+    "libcef/common/command_line_impl.cc",
+    "libcef/common/command_line_impl.h",
+    "libcef/common/content_client.cc",
+    "libcef/common/content_client.h",
+    "libcef/common/crash_reporter_client.cc",
+    "libcef/common/crash_reporter_client.h",
+    "libcef/common/crash_reporting.cc",
+    "libcef/common/crash_reporting.h",
+    "libcef/common/drag_data_impl.cc",
+    "libcef/common/drag_data_impl.h",
+    "libcef/common/extensions/chrome_generated_schemas.cc",
+    "libcef/common/extensions/chrome_generated_schemas.h",
+    "libcef/common/extensions/extensions_api_provider.cc",
+    "libcef/common/extensions/extensions_api_provider.h",
+    "libcef/common/extensions/extensions_client.cc",
+    "libcef/common/extensions/extensions_client.h",
+    "libcef/common/extensions/extensions_util.cc",
+    "libcef/common/extensions/extensions_util.h",
+    "libcef/common/file_util_impl.cc",
+    "libcef/common/frame_util.cc",
+    "libcef/common/frame_util.h",
+    "libcef/common/json_impl.cc",
+    "libcef/common/main_delegate.cc",
+    "libcef/common/main_delegate.h",
+    "libcef/common/net/http_header_utils.cc",
+    "libcef/common/net/http_header_utils.h",
+    "libcef/common/net/net_resource_provider.cc",
+    "libcef/common/net/net_resource_provider.h",
+    "libcef/common/net/scheme_registration.cc",
+    "libcef/common/net/scheme_registration.h",
+    "libcef/common/net/upload_data.cc",
+    "libcef/common/net/upload_data.h",
+    "libcef/common/net/upload_element.cc",
+    "libcef/common/net/upload_element.h",
+    "libcef/common/net_service/net_service_util.cc",
+    "libcef/common/net_service/net_service_util.h",
+    "libcef/common/parser_impl.cc",
+    "libcef/common/process_message_impl.cc",
+    "libcef/common/process_message_impl.h",
+    "libcef/common/request_impl.cc",
+    "libcef/common/request_impl.h",
+    "libcef/common/resource_bundle_delegate.cc",
+    "libcef/common/resource_bundle_delegate.h",
+    "libcef/common/resource_bundle_impl.cc",
+    "libcef/common/resource_bundle_impl.h",
+    "libcef/common/response_impl.cc",
+    "libcef/common/response_impl.h",
+    "libcef/common/response_manager.cc",
+    "libcef/common/response_manager.h",
+    "libcef/common/scheme_registrar_impl.cc",
+    "libcef/common/scheme_registrar_impl.h",
+    "libcef/common/string_list_impl.cc",
+    "libcef/common/string_map_impl.cc",
+    "libcef/common/string_multimap_impl.cc",
+    "libcef/common/string_types_impl.cc",
+    "libcef/common/task_impl.cc",
+    "libcef/common/task_runner_impl.cc",
+    "libcef/common/task_runner_impl.h",
+    "libcef/common/test/translator_test_impl.cc",
+    "libcef/common/thread_impl.cc",
+    "libcef/common/thread_impl.h",
+    "libcef/common/time_impl.cc",
+    "libcef/common/time_util.h",
+    "libcef/common/tracker.cc",
+    "libcef/common/tracker.h",
+    "libcef/common/urlrequest_impl.cc",
+    "libcef/common/value_base.cc",
+    "libcef/common/value_base.h",
+    "libcef/common/values_impl.cc",
+    "libcef/common/values_impl.h",
+    "libcef/common/waitable_event_impl.cc",
+    "libcef/common/waitable_event_impl.h",
+    "libcef/common/widevine_loader.cc",
+    "libcef/common/widevine_loader.h",
+    "libcef/renderer/browser_impl.cc",
+    "libcef/renderer/browser_impl.h",
+    "libcef/renderer/content_renderer_client.cc",
+    "libcef/renderer/content_renderer_client.h",
+    "libcef/renderer/dom_document_impl.cc",
+    "libcef/renderer/dom_document_impl.h",
+    "libcef/renderer/dom_node_impl.cc",
+    "libcef/renderer/dom_node_impl.h",
+    "libcef/renderer/extensions/extensions_dispatcher_delegate.cc",
+    "libcef/renderer/extensions/extensions_dispatcher_delegate.h",
+    "libcef/renderer/extensions/extensions_renderer_client.cc",
+    "libcef/renderer/extensions/extensions_renderer_client.h",
+    "libcef/renderer/extensions/print_render_frame_helper_delegate.cc",
+    "libcef/renderer/extensions/print_render_frame_helper_delegate.h",
+    "libcef/renderer/frame_impl.cc",
+    "libcef/renderer/frame_impl.h",
+    "libcef/renderer/render_frame_observer.cc",
+    "libcef/renderer/render_frame_observer.h",
+    "libcef/renderer/render_frame_util.cc",
+    "libcef/renderer/render_frame_util.h",
+    "libcef/renderer/render_thread_observer.cc",
+    "libcef/renderer/render_thread_observer.h",
+    "libcef/renderer/render_urlrequest_impl.cc",
+    "libcef/renderer/render_urlrequest_impl.h",
+    "libcef/renderer/thread_util.h",
+    "libcef/renderer/url_loader_throttle_provider_impl.cc",
+    "libcef/renderer/url_loader_throttle_provider_impl.h",
+    "libcef/renderer/v8_impl.cc",
+    "libcef/renderer/v8_impl.h",
+  ]
+
+  configs += [
+    "libcef/features:config",
+    "//build/config:precompiled_headers",
+
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
+
+  public_configs = [
+    "libcef/features:config",
+  ]
+
+  include_dirs = [
+    # Crashpad code uses paths relative to this directory.
+    "//third_party/crashpad/crashpad",
+  ]
+
+  public_deps = [
+    # Bring in feature flag defines.
+    "//cef/libcef/features",
+  ]
+
+  deps = [
+    ":cef_make_headers",
+    ":cef_service_manifests",
+
+    ":libcef_static_unittested",
+
+    # Generate API bindings for extensions.
+    # TODO(cef): Enable if/when CEF exposes its own Mojo APIs. See
+    # libcef/common/extensions/api/README.txt for details.
+    #"libcef/common/extensions/api",
+    #"libcef/common/extensions/api:api_registration",
+    "libcef/common/extensions/api:extensions_features",
+
+    # Normal build dependencies. Should be sorted alphabetically.
+    "//base",
+    "//base:base_static",
+    "//base/third_party/dynamic_annotations",
+    "//cc",
+    "//chrome/browser",
+    "//chrome/child",
+    "//chrome/common",
+    "//chrome/renderer",
+    "//chrome/services/printing:lib",
+    "//chrome/utility",
+    "//components/cdm/renderer",
+    "//components/certificate_transparency",
+    "//components/content_settings/core/browser",
+    "//components/content_settings/core/common",
+    "//components/crx_file",
+    "//components/data_use_measurement/core",
+    "//components/google/core/common",
+    "//components/keyed_service/content:content",
+    "//components/keyed_service/core:core",
+    "//components/navigation_interception",
+    "//components/network_session_configurator/browser",
+    "//components/pdf/browser",
+    "//components/pdf/renderer",
+    "//components/plugins/renderer",
+    "//components/pref_registry",
+    "//components/printing/browser",
+    "//components/printing/common",
+    "//components/printing/renderer",
+    "//components/proxy_config",
+    "//components/safe_browsing/core/db:test_database_manager",
+    "//components/services/print_compositor/public/cpp",
+    "//components/services/print_compositor/public/mojom",
+    "//components/update_client",
+    "//components/url_formatter",
+    "//components/user_prefs",
+    "//components/version_info",
+    "//components/visitedlink/browser",
+    "//components/visitedlink/common",
+    "//components/visitedlink/renderer",
+    "//components/viz/service",
+    "//components/web_cache/renderer",
+    "//content/public/app:both",
+    "//content/public/browser",
+    "//content/public/child",
+    "//content/public/common",
+    "//content/public/gpu",
+    "//content/public/renderer",
+    "//content/public/utility",
+    "//crypto",
+    "//device/base",
+    "//extensions/browser",
+    "//extensions/browser:core_api_provider",
+    "//extensions/buildflags",
+    "//extensions/common/api",
+    "//extensions/common:core_api_provider",
+    "//extensions/renderer",
+    "//gpu",
+    "//ipc",
+    "//media",
+    "//media/blink",
+    "//net",
+    "//pdf",
+    "//ppapi/buildflags",
+    "//printing/buildflags",
+    "//services/network:network_service",
+    "//services/network/public/cpp",
+    "//services/service_manager/embedder",
+    "//services/service_manager/public/cpp",
+    "//skia",
+    "//storage/browser",
+    "//third_party/blink/public:blink",
+    "//third_party/brotli:dec",
+    "//third_party/cld_3/src/src:cld_3",
+    "//third_party/hunspell",
+    "//third_party/leveldatabase",
+    "//third_party/libxml:libxml",
+    "//third_party/widevine/cdm:headers",
+    "//third_party/widevine/cdm",
+    "//third_party/icu",
+    "//third_party/zlib:minizip",
+    "//ui/base",
+    "//ui/base/ime",
+    "//ui/events:events_base",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gfx/ipc",
+    "//ui/gfx/ipc/geometry",
+    "//ui/gfx/ipc/skia",
+    "//ui/gl",
+    "//url",
+    "//v8",
+  ]
+
+  if (is_win) {
+    sources += includes_win + [
+      "libcef/browser/browser_main_win.cc",
+      "libcef/browser/native/browser_platform_delegate_native_win.cc",
+      "libcef/browser/native/browser_platform_delegate_native_win.h",
+      "libcef/browser/native/file_dialog_runner_win.cc",
+      "libcef/browser/native/file_dialog_runner_win.h",
+      "libcef/browser/native/javascript_dialog_runner_win.cc",
+      "libcef/browser/native/javascript_dialog_runner_win.h",
+      "libcef/browser/native/menu_2.cc",
+      "libcef/browser/native/menu_2.h",
+      "libcef/browser/native/menu_runner_win.cc",
+      "libcef/browser/native/menu_runner_win.h",
+      "libcef/browser/native/menu_wrapper.h",
+      "libcef/browser/native/native_menu_win.cc",
+      "libcef/browser/native/native_menu_win.h",
+      "libcef/browser/osr/browser_platform_delegate_osr_win.cc",
+      "libcef/browser/osr/browser_platform_delegate_osr_win.h",
+      "libcef/browser/osr/render_widget_host_view_osr_win.cc",
+
+      # Part of //chrome/utility.
+      "//chrome/utility/printing_handler.cc",
+      "//chrome/utility/printing_handler.h",
+    ]
+
+    deps += [
+      "//chrome/install_static:secondary_module",
+      "//chrome/chrome_elf",
+    ]
+
+    if (is_component_build) {
+      deps += [ "//content:sandbox_helper_win" ]
+    }
+
+    libs = [
+      "comctl32.lib",
+      # For D3D11_DECODER_PROFILE_H264_VLD_NOFGT.
+      "dxguid.lib",
+    ]
+
+    data_deps = [
+      "//chrome/elevation_service",
+    ]
+  }
+
+  if (is_linux) {
+    sources += includes_linux + [
+      "libcef/browser/native/browser_platform_delegate_native_linux.cc",
+      "libcef/browser/native/browser_platform_delegate_native_linux.h",
+      "libcef/browser/native/menu_runner_linux.cc",
+      "libcef/browser/native/menu_runner_linux.h",
+      "libcef/browser/osr/browser_platform_delegate_osr_linux.cc",
+      "libcef/browser/osr/browser_platform_delegate_osr_linux.h",
+      "libcef/browser/osr/render_widget_host_view_osr_linux.cc",
+      "libcef/browser/printing/print_dialog_linux.cc",
+      "libcef/browser/printing/print_dialog_linux.h",
+    ]
+
+    if (use_x11) {
+      sources += [
+        "libcef/browser/native/window_x11.cc",
+        "libcef/browser/native/window_x11.h",
+      ]
+    }
+
+    deps += [
+      "//build/config/freetype",
+      "//third_party/fontconfig",
+    ]
+
+    if (is_linux && !use_x11) {
+      deps += [
+        "//third_party/angle:libEGL",
+      ]
+    }
+  }
+
+  if (is_mac) {
+    sources += includes_mac + [
+      "libcef/browser/native/browser_platform_delegate_native_mac.h",
+      "libcef/browser/native/browser_platform_delegate_native_mac.mm",
+      "libcef/browser/native/file_dialog_runner_mac.h",
+      "libcef/browser/native/file_dialog_runner_mac.mm",
+      "libcef/browser/native/javascript_dialog_runner_mac.h",
+      "libcef/browser/native/javascript_dialog_runner_mac.mm",
+      "libcef/browser/native/menu_runner_mac.h",
+      "libcef/browser/native/menu_runner_mac.mm",
+      "libcef/browser/osr/browser_platform_delegate_osr_mac.h",
+      "libcef/browser/osr/browser_platform_delegate_osr_mac.mm",
+      "libcef/common/util_mac.h",
+      "libcef/common/util_mac.mm",
+    ]
+  }
+
+  if (is_win || is_mac) {
+    deps += [ "//third_party/crashpad/crashpad/handler" ]
+  }
+
+  if (use_x11) {
+    deps += [ "//ui/events/devices/x11" ]
+  }
+
+  if (is_posix && !is_mac) {
+    sources += [
+      "libcef/common/cef_crash_report_utils.cc",
+      "libcef/common/cef_crash_report_utils.h",
+    ]
+
+    deps += [
+      "//components/crash/core/app",
+      "//components/crash/content/browser",
+    ]
+  }
+
+  if (v8_use_external_startup_data && use_v8_context_snapshot) {
+    deps += [ "//tools/v8_context_snapshot" ]
+  }
+
+  if (toolkit_views) {
+    deps += [
+      "//ui/views",
+    ]
+  }
+
+  if (use_aura) {
+    sources += [
+      "libcef/browser/native/window_delegate_view.cc",
+      "libcef/browser/native/window_delegate_view.h",
+      "libcef/browser/views/basic_label_button_impl.cc",
+      "libcef/browser/views/basic_label_button_impl.h",
+      "libcef/browser/views/basic_label_button_view.cc",
+      "libcef/browser/views/basic_label_button_view.h",
+      "libcef/browser/views/basic_panel_impl.cc",
+      "libcef/browser/views/basic_panel_impl.h",
+      "libcef/browser/views/basic_panel_view.cc",
+      "libcef/browser/views/basic_panel_view.h",
+      "libcef/browser/views/box_layout_impl.cc",
+      "libcef/browser/views/box_layout_impl.h",
+      "libcef/browser/views/browser_platform_delegate_views.cc",
+      "libcef/browser/views/browser_platform_delegate_views.h",
+      "libcef/browser/views/browser_view_impl.cc",
+      "libcef/browser/views/browser_view_impl.h",
+      "libcef/browser/views/browser_view_view.cc",
+      "libcef/browser/views/browser_view_view.h",
+      "libcef/browser/views/button_impl.h",
+      "libcef/browser/views/button_view.h",
+      "libcef/browser/views/display_impl.cc",
+      "libcef/browser/views/display_impl.h",
+      "libcef/browser/views/fill_layout_impl.cc",
+      "libcef/browser/views/fill_layout_impl.h",
+      "libcef/browser/views/label_button_impl.h",
+      "libcef/browser/views/label_button_view.h",
+      "libcef/browser/views/layout_impl.h",
+      "libcef/browser/views/layout_adapter.cc",
+      "libcef/browser/views/layout_adapter.h",
+      "libcef/browser/views/layout_util.cc",
+      "libcef/browser/views/layout_util.h",
+      "libcef/browser/views/menu_button_impl.cc",
+      "libcef/browser/views/menu_button_impl.h",
+      "libcef/browser/views/menu_button_view.cc",
+      "libcef/browser/views/menu_button_view.h",
+      "libcef/browser/views/menu_runner_views.cc",
+      "libcef/browser/views/menu_runner_views.h",
+      "libcef/browser/views/panel_impl.h",
+      "libcef/browser/views/panel_view.h",
+      "libcef/browser/views/scroll_view_impl.cc",
+      "libcef/browser/views/scroll_view_impl.h",
+      "libcef/browser/views/scroll_view_view.cc",
+      "libcef/browser/views/scroll_view_view.h",
+      "libcef/browser/views/textfield_impl.cc",
+      "libcef/browser/views/textfield_impl.h",
+      "libcef/browser/views/textfield_view.cc",
+      "libcef/browser/views/textfield_view.h",
+      "libcef/browser/views/view_adapter.cc",
+      "libcef/browser/views/view_adapter.h",
+      "libcef/browser/views/view_impl.h",
+      "libcef/browser/views/view_util.cc",
+      "libcef/browser/views/view_util.h",
+      "libcef/browser/views/view_view.h",
+      "libcef/browser/views/window_impl.cc",
+      "libcef/browser/views/window_impl.h",
+      "libcef/browser/views/window_view.cc",
+      "libcef/browser/views/window_view.h",
+
+      # Part of //ui/views:test_support which is testingonly.
+      "//ui/views/test/desktop_test_views_delegate.h",
+      "//ui/views/test/desktop_test_views_delegate_aura.cc",
+      "//ui/views/test/test_views_delegate.h",
+      "//ui/views/test/test_views_delegate_aura.cc",
+
+      # Support for UI input events.
+      # Part of //ui/base:test_support which is testingonly.
+      "//ui/base/test/ui_controls.h",
+      "//ui/base/test/ui_controls_aura.cc",
+      "//ui/aura/test/ui_controls_factory_aura.h",
+    ]
+
+    if (is_linux && !use_x11) {
+      sources += [
+        "//ui/aura/test/ui_controls_factory_ozone.cc",
+        "//ui/events/test/events_test_utils.cc"
+      ]
+    }
+
+    deps += [
+      "//ui/aura",
+      "//ui/events",
+      "//ui/strings",
+      "//ui/wm",
+      "//ui/wm/public",
+    ]
+
+    if (toolkit_views) {
+      deps += [
+        "//ui/views/controls/webview",
+      ]
+    }
+    
+    if (is_win) {
+      sources += [
+        # Support for UI input events.
+        # Part of //base/test:test_config which is testingonly.
+        "//base/test/test_switches.cc",
+        "//base/test/test_switches.h",
+        "//base/test/test_timeouts.cc",
+        "//base/test/test_timeouts.h",
+        # Part of //ui/aura:test_support which is testingonly.
+        "//ui/aura/test/ui_controls_factory_aurawin.cc",
+        # Part of //ui/base:test_support which is testingonly.
+        "//ui/base/test/ui_controls_internal_win.cc",
+        "//ui/base/test/ui_controls_internal_win.h",
+      ]
+    }
+
+    if (is_linux) {
+      sources += [
+        # Support for UI input events.
+        # Part of //ui/aura:test_support which is testingonly.
+        "//ui/aura/test/aura_test_utils.cc",
+        "//ui/aura/test/aura_test_utils.h",
+        # Part of //ui/events:test_support which is testingonly.
+        "//ui/events/test/x11_event_waiter.cc",
+        "//ui/events/test/x11_event_waiter.h",
+      ]
+
+      if (use_x11) {
+        sources += [
+          # Support for UI input events.
+          # Part of //ui/aura:test_support which is testingonly.
+          "//ui/aura/test/ui_controls_factory_aurax11.cc",
+          "//ui/aura/test/x11_event_sender.cc",
+          "//ui/aura/test/x11_event_sender.h",
+          # Part of //ui/views:test_support which is testingonly.
+          "//ui/views/test/ui_controls_factory_desktop_aurax11.cc",
+          "//ui/views/test/ui_controls_factory_desktop_aurax11.h",
+        ]
+      }
+    }
+
+    if (is_win || is_linux) {
+      sources += [
+        "libcef/browser/native/browser_platform_delegate_native_aura.cc",
+        "libcef/browser/native/browser_platform_delegate_native_aura.h",
+      ]
+    }
+  } else {
+    sources += [
+      # Provides stub implementations for the views static methods.
+      "libcef_dll/views_stub.cc",
+    ]
+  }
+}
+
+
+#
+# libcef_dll_wrapper static targets.
+#
+
+# Configuration that will be applied to all targets that depend on
+# libcef_dll_wrapper.
+config("libcef_dll_wrapper_config") {
+  include_dirs = [
+    # CEF sources use include paths relative to the CEF root directory.
+    ".",
+    # CEF generates some header files that also need to be discoverable.
+    # They will be copied to the include/ directory in the binary distribution.
+    "$root_out_dir/includes",
+  ]
+}
+
+# libcef_dll_wrapper target.
+static_library("libcef_dll_wrapper") {
+  sources = includes_common +
+            gypi_paths.autogen_cpp_includes +
+            gypi_paths2.includes_capi +
+            gypi_paths.autogen_capi_includes +
+            gypi_paths2.includes_wrapper +
+            gypi_paths2.libcef_dll_wrapper_sources_base +
+            gypi_paths2.libcef_dll_wrapper_sources_common +
+            gypi_paths.autogen_client_side
+
+  if (is_mac) {
+    sources += gypi_paths2.libcef_dll_wrapper_sources_mac
+  }
+
+  defines = [ "WRAPPING_CEF_SHARED" ]
+
+  configs += [ ":libcef_dll_wrapper_config" ]
+  public_configs = [ ":libcef_dll_wrapper_config" ]
+}
+
+
+#
+# cef_sandbox target.
+#
+
+if (is_win) {
+  static_library("cef_sandbox") {
+    sources = [ "libcef_dll/sandbox/sandbox_win.cc" ]
+    # CEF sources use include paths relative to the CEF root directory.
+    include_dirs = [ "." ]
+    deps = [ "//sandbox" ]
+  }
+}
+
+if (is_mac) {
+  static_library("cef_sandbox") {
+    sources = [ "libcef_dll/sandbox/sandbox_mac.mm" ]
+    # CEF sources use include paths relative to the CEF root directory.
+    include_dirs = [ "." ]
+    deps = [ "//sandbox/mac:seatbelt" ]
+  }
+}
+
+#
+# Service manifests.
+#
+
+source_set("cef_content_browser_overlay_manifest") {
+  sources = [
+    "libcef/common/service_manifests/cef_content_browser_overlay_manifest.cc",
+    "libcef/common/service_manifests/cef_content_browser_overlay_manifest.h",
+  ]
+
+  configs += [
+    "libcef/features:config"
+  ]
+
+  deps = [
+    "//base",
+    "//extensions/buildflags",
+    "//extensions/common:mojom",
+    "//extensions/common/api:mojom",
+    "//services/service_manager/public/cpp",
+    "//third_party/blink/public/common",
+  ]
+}
+
+source_set("cef_content_renderer_overlay_manifest") {
+  sources = [
+    "libcef/common/service_manifests/cef_content_renderer_overlay_manifest.h",
+  ]
+
+  configs += [
+    "libcef/features:config"
+  ]
+
+  deps = [
+    "//base",
+    "//components/subresource_filter/content/mojom",
+    "//extensions/buildflags",
+    "//extensions/common:mojom",
+    "//services/service_manager/public/cpp",
+    "//third_party/blink/public/common",
+  ]
+}
+
+source_set("cef_service_manifests") {
+  public_deps = [
+    ":cef_content_browser_overlay_manifest",
+    ":cef_content_renderer_overlay_manifest",
+  ]
+}
+
+#
+# Resource grit/pack targets.
+#
+
+# Helper for generating scaled resource packs.
+template("cef_pak_scaled") {
+  percent = invoker.percent
+
+  repack("pak_${target_name}") {
+    # Each input pak file should also have a deps line for completeness.
+    # Add associated .h files in the make_pack_header("resources") target.
+    sources = [
+      "$root_gen_dir/chrome/renderer_resources_${percent}_percent.pak",
+      "$root_gen_dir/components/components_resources_${percent}_percent.pak",
+      "$root_gen_dir/content/app/resources/content_resources_${percent}_percent.pak",
+      "$root_gen_dir/extensions/extensions_browser_resources_${percent}_percent.pak",
+      "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_${percent}_percent.pak",
+      "$root_gen_dir/ui/resources/ui_resources_${percent}_percent.pak",
+    ]
+
+    # Use public_deps so that generated grit headers are discoverable from
+    # the libcef_static target. Grit deps that generate .cc files must be
+    # listed both here and in the libcef_static target.
+    public_deps = [
+      "//chrome/renderer:resources",
+      "//components/resources:components_scaled_resources",
+      "//content/app/resources",
+      "//extensions:extensions_browser_resources",
+      "//third_party/blink/public:scaled_resources",
+      "//ui/resources:ui_resources_grd",
+    ]
+
+    deps = [
+      # This repack target generates the blink_scaled_resources_*_percent.pak
+      # file but doesn't expose the public_deps required by make_pack_header.
+      "//third_party/blink/public:scaled_resources_${percent}_percent",
+    ]
+
+    if (toolkit_views) {
+      sources += [
+        "$root_gen_dir/ui/views/resources/views_resources_${percent}_percent.pak"
+      ]
+
+      public_deps += [
+        "//ui/views/resources:resources_grd"
+      ]
+    }
+
+    output = "$root_out_dir/cef_${percent}_percent.pak"
+  }
+}
+
+# Generate cef_100_percent.pak.
+cef_pak_scaled("100_percent") {
+  percent = "100"
+}
+
+# Generate cef_200_percent.pak.
+cef_pak_scaled("200_percent") {
+  percent = "200"
+}
+
+# Generate devtools_resources.pak.
+repack("pak_devtools") {
+  # Each input pak file should also have a deps line for completeness.
+  # Add associated .h files in the make_pack_header("resources") target.
+  sources = [
+    "$root_gen_dir/content/browser/devtools/devtools_resources.pak",
+  ]
+
+  # Use public_deps so that generated grit headers are discoverable from
+  # the libcef_static target. Grit deps that generate .cc files must be
+  # listed both here and in the libcef_static target.
+  public_deps = [
+    "//content/browser/devtools:resources",
+  ]
+
+  output = "$root_out_dir/devtools_resources.pak"
+}
+
+# Generate cef_extensions.pak.
+repack("pak_extensions") {
+  # Each input pak file should also have a deps line for completeness.
+  # Add associated .h files in the make_pack_header("resources") target.
+  sources = [
+    "$root_gen_dir/chrome/component_extension_resources.pak",
+    "$root_gen_dir/content/browser/resources/media/media_internals_resources.pak",
+    "$root_gen_dir/content/browser/webrtc/resources/webrtc_internals_resources.pak",
+    "$root_gen_dir/extensions/extensions_renderer_resources.pak",
+    "$root_gen_dir/extensions/extensions_resources.pak",
+    "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
+    "$root_gen_dir/ui/resources/webui_resources.pak",
+  ]
+
+  # Use public_deps so that generated grit headers are discoverable from
+  # the libcef_static target. Grit deps that generate .cc files must be
+  # listed both here and in the libcef_static target.
+  public_deps = [
+    "//chrome/browser/resources:component_extension_resources",
+    "//content/browser/resources/media:media_internals_resources",
+    "//content/browser/webrtc/resources",
+    "//extensions:extensions_renderer_resources",
+    "//extensions:extensions_resources_grd",
+    "//mojo/public/js:resources",
+    "//ui/resources:webui_resources_grd",
+  ]
+
+  output = "$root_out_dir/cef_extensions.pak"
+}
+
+grit("cef_strings") {
+  visibility = [ ":*" ]
+
+  source = "libcef/resources/cef_strings.grd"
+  outputs = [
+    "grit/cef_strings.h",
+  ]
+  all_locales = locales + [ "fake-bidi" ]
+  foreach(locale, all_locales) {
+    outputs += [ "cef_strings_${locale}.pak" ]
+  }
+}
+
+# Generate locales/<locale>.pak.
+# See cef_repack_locales.gni for the list of input pak files and deps.
+cef_repack_locales("repack_locales_pack") {
+  visibility = [ ":*" ]
+
+  input_locales = locales
+
+  if (is_mac) {
+    output_locales = locales_as_mac_outputs
+  } else {
+    output_locales = locales
+  }
+}
+
+grit("cef_resources") {
+  visibility = [ ":*" ]
+  source = "libcef/resources/cef_resources.grd"
+  outputs = [
+    "grit/cef_resources.h",
+    "cef_resources.pak",
+  ]
+  grit_flags = [
+    "-E",
+    "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
+  ]
+}
+
+# Generate cef.pak.
+repack("pak") {
+  # Each input pak file should also have a deps line for completeness.
+  # Add associated .h files in the make_pack_header("resources") target.
+  sources = [
+    "$root_gen_dir/chrome/browser_resources.pak",
+    "$root_gen_dir/chrome/dev_ui_browser_resources.pak",
+    "$root_gen_dir/chrome/net_internals_resources.pak",
+    "$root_gen_dir/chrome/print_preview_resources.pak",
+    "$root_gen_dir/chrome/common_resources.pak",
+    "$root_gen_dir/components/components_resources.pak",
+    "$root_gen_dir/components/dev_ui_components_resources.pak",
+    "$root_gen_dir/cef/cef_resources.pak",
+    "$root_gen_dir/content/browser/tracing/tracing_resources.pak",
+    "$root_gen_dir/content/content_resources.pak",
+    "$root_gen_dir/content/dev_ui_content_resources.pak",
+    "$root_gen_dir/net/net_resources.pak",
+    "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
+  ]
+
+  # Use public_deps so that generated grit headers are discoverable from
+  # the libcef_static target. Grit deps that generate .cc files must be
+  # listed both here and in the libcef_static target.
+  public_deps = [
+    "//chrome/browser:dev_ui_browser_resources",
+    "//chrome/browser:resources",
+    "//chrome/browser/resources/net_internals:net_internals_resources",
+    "//chrome/browser/resources:print_preview_resources",
+    "//chrome/common:resources",
+    "//components/resources:components_resources",
+    "//components/resources:dev_ui_components_resources",
+    ":cef_resources",
+    "//content/browser/tracing:resources",
+    "//content:content_resources",
+    "//content:dev_ui_content_resources",
+    "//net:net_resources",
+    "//third_party/blink/public:resources",
+  ]
+
+  output = "$root_out_dir/cef.pak"
+}
+
+# Helper for generating pack header files.
+template("make_pack_header") {
+  assert(defined(invoker.header))
+  assert(defined(invoker.inputs))
+
+  action("make_pack_header_${target_name}") {
+    script = "tools/make_pack_header.py"
+
+    inputs = invoker.inputs
+    outputs = [ invoker.header ]
+
+    args = rebase_path(outputs, root_build_dir) +
+           rebase_path(inputs, root_build_dir)
+
+    deps = [
+      # List all targets that generate pack files here. The grit targets that
+      # generate |inputs| will be picked up via public_deps.
+      ":pak",
+      ":pak_100_percent",
+      ":pak_200_percent",
+      ":pak_devtools",
+      ":pak_extensions",
+      ":repack_locales_pack",
+    ]
+  }
+}
+
+# Generate cef_pack_resources.h.
+make_pack_header("resources") {
+  header = "$root_out_dir/includes/include/cef_pack_resources.h"
+  inputs = [
+    "$root_gen_dir/cef/grit/cef_resources.h",
+    "$root_gen_dir/chrome/grit/browser_resources.h",
+    "$root_gen_dir/chrome/grit/common_resources.h",
+    "$root_gen_dir/chrome/grit/component_extension_resources.h",
+    "$root_gen_dir/chrome/grit/dev_ui_browser_resources.h",
+    "$root_gen_dir/chrome/grit/net_internals_resources.h",
+    "$root_gen_dir/chrome/grit/renderer_resources.h",
+    "$root_gen_dir/components/grit/components_resources.h",
+    "$root_gen_dir/components/grit/dev_ui_components_resources.h",
+    "$root_gen_dir/content/browser/devtools/grit/devtools_resources.h",
+    "$root_gen_dir/content/browser/tracing/grit/tracing_resources.h",
+    "$root_gen_dir/content/grit/content_resources.h",
+    "$root_gen_dir/content/grit/dev_ui_content_resources.h",
+    "$root_gen_dir/extensions/grit/extensions_browser_resources.h",
+    "$root_gen_dir/extensions/grit/extensions_renderer_resources.h",
+    "$root_gen_dir/extensions/grit/extensions_resources.h",
+    "$root_gen_dir/net/grit/net_resources.h",
+    "$root_gen_dir/third_party/blink/public/resources/grit/blink_resources.h",
+    "$root_gen_dir/ui/resources/grit/ui_resources.h",
+    "$root_gen_dir/ui/resources/grit/webui_resources.h",
+    "$root_gen_dir/ui/views/resources/grit/views_resources.h",
+  ]
+}
+
+# Generate cef_pack_strings.h.
+make_pack_header("strings") {
+  header = "$root_out_dir/includes/include/cef_pack_strings.h"
+  inputs = [
+    "$root_gen_dir/cef/grit/cef_strings.h",
+    "$root_gen_dir/chrome/grit/chromium_strings.h",
+    "$root_gen_dir/chrome/grit/generated_resources.h",
+    "$root_gen_dir/chrome/grit/locale_settings.h",
+    "$root_gen_dir/chrome/grit/platform_locale_settings.h",
+    "$root_gen_dir/components/strings/grit/components_strings.h",
+    "$root_gen_dir/extensions/strings/grit/extensions_strings.h",
+    "$root_gen_dir/services/strings/grit/services_strings.h",
+    "$root_gen_dir/third_party/blink/public/strings/grit/blink_strings.h",
+    "$root_gen_dir/ui/strings/grit/ui_strings.h",
+  ]
+}
+
+# Generate cef_api_hash.h.
+action("make_api_hash_header") {
+  script = "tools/make_api_hash_header.py"
+
+  # List of all C API files that will be checked for changes by cef_api_hash.py.
+  inputs = gypi_paths2.includes_common_capi +
+           gypi_paths2.includes_linux_capi +
+           gypi_paths2.includes_mac_capi +
+           gypi_paths2.includes_win_capi +
+           gypi_paths2.includes_capi +
+           gypi_paths.autogen_capi_includes
+  include_dir = [ "include" ]
+  outputs = [ "$root_out_dir/includes/include/cef_api_hash.h" ]
+
+  args = rebase_path(outputs + include_dir, root_build_dir)
+}
+
+# Generate pack files and associated CEF header files.
+group("cef_make_headers") {
+  deps = [
+    ":make_pack_header_resources",
+    ":make_pack_header_strings",
+    ":make_api_hash_header",
+  ]
+}
+
+
+#
+# libcef dll/framework target.
+#
+
+if (is_mac) {
+  cef_framework_name = "Chromium Embedded Framework"
+
+  tweak_info_plist("cef_framework_plist") {
+    info_plist = "libcef/resources/framework-Info.plist"
+    args = [
+      "--breakpad=0",
+      "--keystone=0",
+      "--scm=1",
+      "--version",
+      cef_plist_version,
+      "--branding",
+      cef_framework_name,
+    ]
+  }
+
+  bundle_data("cef_framework_locales") {
+    sources = []
+    foreach(locale, locales_as_mac_outputs) {
+      sources += [ "$root_gen_dir/repack/locales/$locale.pak" ]
+    }
+
+    public_deps = [
+      ":repack_locales_pack",
+    ]
+
+    outputs = [
+      "{{bundle_resources_dir}}/{{source_name_part}}.lproj/locale.pak",
+    ]
+  }
+
+  bundle_data("cef_framework_resources") {
+    sources = [
+      "$root_out_dir/cef.pak",
+      "$root_out_dir/cef_100_percent.pak",
+      "$root_out_dir/cef_200_percent.pak",
+      "$root_out_dir/cef_extensions.pak",
+      "$root_out_dir/devtools_resources.pak",
+    ]
+
+    public_deps = [
+      ":pak",
+      ":pak_100_percent",
+      ":pak_200_percent",
+      ":pak_devtools",
+      ":pak_extensions",
+    ]
+
+    if (icu_use_data_file) {
+      sources += [ "$root_out_dir/icudtl.dat" ]
+      public_deps += [ "//third_party/icu:icudata", ]
+    }
+
+    if (v8_use_external_startup_data) {
+      sources += [
+        "$root_out_dir/snapshot_blob.bin",
+      ]
+      public_deps += [ "//v8" ]
+      if (use_v8_context_snapshot) {
+        sources += [ "$root_out_dir/v8_context_snapshot.bin" ]
+        public_deps += [ "//tools/v8_context_snapshot" ]
+      }
+    }
+
+    outputs = [
+      "{{bundle_resources_dir}}/{{source_file_part}}",
+    ]
+  }
+
+  if (use_egl) {
+    # Add the ANGLE .dylibs in the MODULE_DIR of the Framework app bundle.
+    bundle_data("cef_framework_angle_binaries") {
+      sources = [
+        "$root_out_dir/egl_intermediates/libEGL.dylib",
+        "$root_out_dir/egl_intermediates/libGLESv2.dylib",
+      ]
+      outputs = [
+        "{{bundle_contents_dir}}/Libraries/{{source_file_part}}",
+      ]
+      public_deps = [
+        "//ui/gl:angle_library_copy",
+      ]
+    }
+
+    # Add the SwiftShader .dylibs in the MODULE_DIR of the Framework app bundle.
+    bundle_data("cef_framework_swiftshader_binaries") {
+      sources = [
+        "$root_out_dir/egl_intermediates/libswiftshader_libEGL.dylib",
+        "$root_out_dir/egl_intermediates/libswiftshader_libGLESv2.dylib",
+        "$root_out_dir/vk_intermediates/libvk_swiftshader.dylib",
+        "$root_out_dir/vk_intermediates/vk_swiftshader_icd.json",
+      ]
+      outputs = [
+        "{{bundle_contents_dir}}/Libraries/{{source_file_part}}",
+      ]
+      public_deps = [
+        "//ui/gl:swiftshader_egl_library_copy",
+        "//ui/gl:swiftshader_vk_library_copy",
+      ]
+    }
+  }
+
+  group("cef_framework_angle_library") {
+    if (use_egl) {
+      deps = [
+        ":cef_framework_angle_binaries",
+      ]
+    }
+  }
+
+  group("cef_framework_swiftshader_library") {
+    if (use_egl) {
+      deps = [
+        ":cef_framework_swiftshader_binaries",
+      ]
+    }
+  }
+
+  mac_framework_bundle("cef_framework") {
+    output_name = cef_framework_name
+
+    framework_version = "A"
+    framework_contents = [
+      "Libraries",
+      "Resources",
+    ]
+
+    sources = includes_common +
+              includes_mac +
+              gypi_paths.autogen_cpp_includes +
+              gypi_paths2.includes_capi +
+              gypi_paths.autogen_capi_includes +
+              gypi_paths2.libcef_sources_common +
+              gypi_paths.autogen_library_side
+
+    # TODO(rsesek): Handle these missing pieces:
+    #   - crash_inspector
+    #   - crash_report_sender.app
+
+    deps = [
+      ":cef_framework_angle_library",
+      ":cef_framework_locales",
+      ":cef_framework_resources",
+      ":cef_framework_swiftshader_library",
+      ":libcef_static",
+    ]
+
+    # We don't link the framework so just use the path from the main executable.
+    ldflags = [
+      "-Wl,-install_name,@executable_path/../Frameworks/$output_name.framework/$output_name",
+      "-compatibility_version",
+      cef_dylib_version,
+      "-current_version",
+      cef_dylib_version,
+    ]
+
+    if (is_component_build) {
+      # Set up the rpath for the framework so that it can find dylibs in the
+      # root output directory. The framework is at
+      # $app_name.app/Contents/Frameworks/$output_name.framework/Versions/A/$output_name
+      # so use loader_path to go back to the root output directory.
+      ldflags += [
+        "-rpath",
+        "@loader_path/../../../../../..",
+      ]
+    }
+
+    info_plist_target = ":cef_framework_plist"
+  }
+} else {
+  shared_library("libcef") {
+    sources = includes_common +
+              gypi_paths.autogen_cpp_includes +
+              gypi_paths2.includes_capi +
+              gypi_paths.autogen_capi_includes +
+              gypi_paths2.libcef_sources_common +
+              gypi_paths.autogen_library_side
+
+    deps = [
+      ":libcef_static",
+    ]
+
+    if (is_win) {
+      sources += includes_win + [
+        "libcef_dll/libcef_dll.rc",
+      ]
+
+      deps += [ 
+        # Bring in ui_unscaled_resources.rc which contains custom cursors.
+        # TODO(cef): Remove this once custom cursors can be loaded via
+        # ResourceBundle. See crbug.com/147663.
+        "//ui/resources:ui_unscaled_resources_grd",
+      ]
+    }
+
+    if (is_linux && !is_debug && use_allocator=="none") {
+      # Only export necessary symbols from libcef.so.
+      # Don't do this in Debug builds because it causes the resulting
+      # application to crash.
+      # Also need to do this for ASAN builds to work around
+      # https://crbug.com/832808.
+      ldflags = [ "-Wl,--version-script=" +
+                  rebase_path("//cef/libcef_dll/libcef.lst") ]
+    }
+  }
+}
+
+
+#
+# Executable/app targets.
+#
+
+if (is_mac) {
+  # Helper for generating the CEF app bundle.
+  template("cef_app") {
+    assert(defined(invoker.helper_info_plist))
+    assert(defined(invoker.helper_sources))
+    assert(defined(invoker.info_plist))
+    assert(defined(invoker.sources))
+
+    app_name = target_name
+    app_helper_name = "$app_name Helper"
+    app_testonly = defined(invoker.testonly) && invoker.testonly
+
+    tweak_info_plist("${app_name}_helper_plist") {
+      testonly = app_testonly
+      info_plist = invoker.helper_info_plist
+      args = [
+        "--breakpad=0",
+        "--keystone=0",
+        "--scm=0",
+        "--version",
+        cef_plist_version,
+      ]
+    }
+
+    template("cef_helper_app") {
+      mac_app_bundle(target_name) {
+        assert(defined(invoker.helper_sources))
+        assert(defined(invoker.helper_name_suffix))
+        assert(defined(invoker.helper_bundle_id_suffix))
+
+        testonly = app_testonly
+        output_name = app_helper_name + invoker.helper_name_suffix
+
+        sources = invoker.helper_sources
+
+        extra_substitutions = [
+          "BUNDLE_ID_SUFFIX=${invoker.helper_bundle_id_suffix}",
+        ]
+
+        deps = [
+          ":cef_make_headers",
+          ":cef_sandbox",
+          ":libcef_dll_wrapper",
+        ]
+        if (defined(invoker.helper_deps)) {
+          deps += invoker.helper_deps
+        }
+
+        ldflags = [
+          # The helper is in $app_name.app/Contents/Frameworks/$app_name Helper.app/Contents/MacOS/
+          # so set rpath up to the base.
+          "-rpath",
+           "@executable_path/../../../../../..",
+        ]
+
+        info_plist_target = ":${app_name}_helper_plist"
+
+        if (defined(invoker.helper_defines)) {
+          defines = invoker.helper_defines
+        }
+      }
+    }
+
+    foreach(helper_params, content_mac_helpers) {
+      _helper_target = helper_params[0]
+      _helper_bundle_id = helper_params[1]
+      _helper_suffix = helper_params[2]
+      cef_helper_app("${app_name}_helper_app_${_helper_target}") {
+        helper_sources = invoker.helper_sources
+        if (defined(invoker.helper_deps)) {
+          helper_deps = invoker.helper_deps
+        }
+        if (defined(invoker.helper_defines)) {
+          helper_defines = invoker.helper_defines
+        }
+
+        helper_name_suffix = _helper_suffix
+        helper_bundle_id_suffix = _helper_bundle_id
+      }
+    }
+
+    bundle_data("${app_name}_framework_bundle_data") {
+      testonly = app_testonly
+      sources = [
+        "$root_out_dir/$cef_framework_name.framework",
+      ]
+
+      public_deps = [
+        ":cef_framework",
+      ]
+
+      foreach(helper_params, content_mac_helpers) {
+        sources += [
+          "$root_out_dir/${app_helper_name}${helper_params[2]}.app",
+        ]
+        public_deps += [ ":${app_name}_helper_app_${helper_params[0]}" ]
+      }
+
+      outputs = [
+        "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}",
+      ]
+    }
+
+    tweak_info_plist("${app_name}_plist") {
+      testonly = app_testonly
+      info_plist = invoker.info_plist
+      args = [
+        "--scm=1",
+        "--version",
+        cef_plist_version,
+      ]
+    }
+
+    mac_app_bundle(app_name) {
+      testonly = app_testonly
+      output_name = app_name
+
+      sources = invoker.sources
+
+      deps = [
+        ":libcef_dll_wrapper",
+        ":${app_name}_framework_bundle_data",
+      ]
+      if (defined(invoker.deps)) {
+        deps += invoker.deps
+      }
+
+      if (defined(invoker.libs)) {
+        libs = invoker.libs
+      }
+
+      if (defined(invoker.defines)) {
+        defines = invoker.defines
+      }
+
+      info_plist_target = ":${app_name}_plist"
+    }
+  }
+
+
+  #
+  # cefclient app targets.
+  #
+
+  bundle_data("cefclient_resources_bundle_data") {
+    sources = gypi_paths2.shared_sources_resources +
+              gypi_paths2.cefclient_sources_resources + [
+      "tests/cefclient/resources/mac/cefclient.icns",
+    ]
+
+    outputs = [
+      "{{bundle_resources_dir}}/{{source_file_part}}",
+    ]
+  }
+
+  bundle_data("cefclient_resources_bundle_data_extensions_set_page_color") {
+    sources = gypi_paths2.cefclient_sources_resources_extensions_set_page_color
+    outputs = [
+      "{{bundle_resources_dir}}/extensions/set_page_color/{{source_file_part}}",
+    ]
+  }
+
+  bundle_data("cefclient_resources_bundle_data_english") {
+    sources = [
+      "tests/cefclient/resources/mac/English.lproj/InfoPlist.strings",
+    ]
+
+    outputs = [
+      "{{bundle_resources_dir}}/English.lproj/{{source_file_part}}",
+    ]
+  }
+
+  mac_xib_bundle_data("cefclient_xibs") {
+    sources = [
+      "tests/cefclient/resources/mac/English.lproj/MainMenu.xib",
+    ]
+
+    output_path = "{{bundle_resources_dir}}/English.lproj"
+  }
+
+  cef_app("cefclient") {
+    helper_info_plist = "tests/cefclient/resources/mac/helper-Info.plist"
+    helper_sources = includes_common +
+                     includes_mac +
+                     gypi_paths2.includes_wrapper +
+                     gypi_paths2.includes_wrapper_mac +
+                     gypi_paths2.shared_sources_common +
+                     gypi_paths2.shared_sources_renderer +
+                     gypi_paths2.shared_sources_mac_helper +
+                     gypi_paths2.cefclient_sources_common +
+                     gypi_paths2.cefclient_sources_renderer
+    helper_defines = [
+      "CEF_USE_SANDBOX",
+    ]
+
+    info_plist = "tests/cefclient/resources/mac/Info.plist"
+    sources = includes_common +
+              includes_mac +
+              gypi_paths2.includes_wrapper +
+              gypi_paths2.includes_wrapper_mac +
+              gypi_paths2.shared_sources_browser +
+              gypi_paths2.shared_sources_common +
+              gypi_paths2.shared_sources_mac +
+              gypi_paths2.cefclient_sources_browser +
+              gypi_paths2.cefclient_sources_common +
+              gypi_paths2.cefclient_sources_mac
+    deps = [
+      ":cefclient_resources_bundle_data",
+      ":cefclient_resources_bundle_data_extensions_set_page_color",
+      ":cefclient_resources_bundle_data_english",
+      ":cefclient_xibs",
+    ]
+    libs = [
+      "AppKit.framework",
+      "OpenGL.framework",
+    ]
+    defines = [
+      "CEF_USE_SANDBOX",
+    ]
+  }
+
+
+  #
+  # cefsimple app targets.
+  #
+
+  bundle_data("cefsimple_resources_bundle_data") {
+    sources = [
+      "tests/cefsimple/mac/cefsimple.icns",
+    ]
+
+    outputs = [
+      "{{bundle_resources_dir}}/{{source_file_part}}",
+    ]
+  }
+
+  bundle_data("cefsimple_resources_bundle_data_english") {
+    sources = [
+      "tests/cefsimple/mac/English.lproj/InfoPlist.strings",
+    ]
+
+    outputs = [
+      "{{bundle_resources_dir}}/English.lproj/{{source_file_part}}",
+    ]
+  }
+
+  mac_xib_bundle_data("cefsimple_xibs") {
+    sources = [
+      "tests/cefsimple/mac/English.lproj/MainMenu.xib",
+    ]
+    output_path = "{{bundle_resources_dir}}/English.lproj"
+  }
+
+  cef_app("cefsimple") {
+    helper_info_plist = "tests/cefsimple/mac/helper-Info.plist"
+    helper_sources = includes_common +
+                     includes_mac +
+                     gypi_paths2.includes_wrapper +
+                     gypi_paths2.includes_wrapper_mac +
+                     gypi_paths2.cefsimple_sources_mac_helper
+    helper_defines = [
+      "CEF_USE_SANDBOX",
+    ]
+
+    info_plist = "tests/cefsimple/mac/Info.plist"
+    sources = includes_common +
+              includes_mac +
+              gypi_paths2.includes_wrapper +
+              gypi_paths2.includes_wrapper_mac +
+              gypi_paths2.cefsimple_sources_common +
+              gypi_paths2.cefsimple_sources_mac
+    deps = [
+      ":cefsimple_resources_bundle_data",
+      ":cefsimple_resources_bundle_data_english",
+      ":cefsimple_xibs",
+    ]
+    libs = [
+      "AppKit.framework",
+    ]
+    defines = [
+      "CEF_USE_SANDBOX",
+    ]
+  }
+
+
+  #
+  # ceftests app targets.
+  #
+
+  bundle_data("ceftests_resources_bundle_data") {
+    sources = gypi_paths2.shared_sources_resources + [
+      "tests/ceftests/resources/mac/ceftests.icns",
+    ]
+
+    outputs = [
+      "{{bundle_resources_dir}}/{{source_file_part}}",
+    ]
+  }
+
+  bundle_data("ceftests_resources_bundle_data_english") {
+    sources = [
+      "tests/ceftests/resources/mac/English.lproj/InfoPlist.strings",
+    ]
+
+    outputs = [
+      "{{bundle_resources_dir}}/English.lproj/{{source_file_part}}",
+    ]
+  }
+
+  mac_xib_bundle_data("ceftests_xibs") {
+    sources = [
+      "tests/ceftests/resources/mac/English.lproj/MainMenu.xib",
+    ]
+    output_path = "{{bundle_resources_dir}}/English.lproj"
+  }
+
+  cef_app("ceftests") {
+    testonly = true
+
+    helper_info_plist = "tests/ceftests/resources/mac/helper-Info.plist"
+    helper_sources = gypi_paths2.shared_sources_common +
+                     gypi_paths2.shared_sources_renderer +
+                     gypi_paths2.shared_sources_mac_helper +
+                     gypi_paths2.ceftests_sources_mac_helper
+    helper_deps = [
+      "//testing/gtest",
+    ]
+    helper_defines = [
+      "CEF_USE_SANDBOX",
+    ]
+
+    info_plist = "tests/ceftests/resources/mac/Info.plist"
+    sources = includes_common +
+              includes_mac +
+              gypi_paths2.includes_wrapper +
+              gypi_paths2.includes_wrapper_mac +
+              gypi_paths2.shared_sources_browser +
+              gypi_paths2.shared_sources_common +
+              gypi_paths2.shared_sources_mac +
+              gypi_paths2.ceftests_sources_common +
+              gypi_paths2.ceftests_sources_mac
+    deps = [
+      ":ceftests_resources_bundle_data",
+      ":ceftests_resources_bundle_data_english",
+      ":ceftests_xibs",
+      "//testing/gtest",
+    ]
+    libs = [
+      "AppKit.framework",
+    ]
+    defines = [
+      "CEF_USE_SANDBOX",
+    ]
+  }
+} else {
+  #
+  # cefclient targets.
+  #
+
+  # The cefclient target depends on packages that are not available in the
+  # default sysroot environment.
+  if (is_linux && !use_sysroot) {
+    pkg_config("glib") {
+      packages = [
+        "glib-2.0",
+      ]
+    }
+  }
+
+  if (is_linux && cef_use_gtk) {
+    pkg_config("gtk") {
+      packages = [
+        "gmodule-2.0",
+        "gtk+-2.0",
+        "gthread-2.0",
+        "gtk+-unix-print-2.0",
+        "xi",
+      ]
+    }
+
+    pkg_config("gtkglext") {
+      packages = [
+        "gtkglext-1.0",
+      ]
+    }
+  }
+
+  if (is_linux) {
+    copy("copy_cefclient_files") {
+      sources = gypi_paths2.shared_sources_resources +
+                gypi_paths2.cefclient_sources_resources
+      outputs = [ "${root_out_dir}/cefclient_files/{{source_file_part}}" ]
+    }
+
+    copy("copy_cefclient_files_extensions_set_page_color") {
+      sources = gypi_paths2.cefclient_sources_resources_extensions_set_page_color
+      outputs = [ "${root_out_dir}/cefclient_files/extensions/set_page_color/{{source_file_part}}" ]
+    }
+  }
+
+  executable("cefclient") {
+    sources = includes_common +
+              gypi_paths2.includes_wrapper +
+              gypi_paths2.shared_sources_browser +
+              gypi_paths2.shared_sources_common +
+              gypi_paths2.shared_sources_renderer +
+              gypi_paths2.cefclient_sources_browser +
+              gypi_paths2.cefclient_sources_common +
+              gypi_paths2.cefclient_sources_renderer
+
+    deps = [
+      ":libcef",
+      ":libcef_dll_wrapper",
+    ]
+
+    defines = [
+      "CEF_USE_SANDBOX",
+    ]
+
+    if (is_win) {
+      sources += includes_win +
+                 gypi_paths2.shared_sources_win +
+                 gypi_paths2.cefclient_sources_win
+
+      # Set /SUBSYSTEM:WINDOWS.
+      configs -= [ "//build/config/win:console" ]
+      configs += [ "//build/config/win:windowed" ]
+
+      defines += [
+        "CEF_USE_ATL",
+      ]
+
+      deps += [
+        ":cef_sandbox",
+        "//build/win:default_exe_manifest",
+      ]
+
+      libs = [
+        "comctl32.lib",
+        "d3d11.lib",
+        "imm32.lib",
+        "oleacc.lib",
+        "rpcrt4.lib",
+        "shlwapi.lib",
+      ]
+
+      if (target_cpu != "arm64") {
+        libs += [
+          "opengl32.lib",
+          "glu32.lib"
+        ]
+      }
+    }
+
+    if (is_linux) {
+      sources += includes_linux +
+                 gypi_paths2.shared_sources_linux +
+                 gypi_paths2.cefclient_sources_linux
+
+      deps += [
+        ":copy_cefclient_files",
+        ":copy_cefclient_files_extensions_set_page_color",
+      ]
+
+      libs = [
+        "X11",
+      ]
+
+      if (cef_use_gtk) {
+        configs += [
+          ":gtk",
+          ":gtkglext",
+        ]
+      }
+
+      if (is_component_build) {
+        if (use_allocator=="tcmalloc") {
+          # Link to base to initialize tcmalloc allocator shims, otherwise
+          # base::allocator::IsAllocatorInitialized check fails
+          deps += [ "//base" ]
+        }
+      } else {
+        # Set rpath to find our own libfreetype even in a non-component build.
+        configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
+      }
+    }
+  }
+
+
+  #
+  # cefsimple targets.
+  #
+
+  executable("cefsimple") {
+    sources = includes_common +
+              gypi_paths2.includes_wrapper +
+              gypi_paths2.cefsimple_sources_common
+
+    deps = [
+      ":libcef",
+      ":libcef_dll_wrapper",
+    ]
+
+    defines = [
+      "CEF_USE_SANDBOX",
+    ]
+
+    if (is_win) {
+      sources += includes_win +
+                 gypi_paths2.cefsimple_sources_win
+
+      # Set /SUBSYSTEM:WINDOWS.
+      configs -= [ "//build/config/win:console" ]
+      configs += [ "//build/config/win:windowed" ]
+
+      deps += [
+        ":cef_sandbox",
+        "//build/win:default_exe_manifest",
+      ]
+
+      libs = [
+        "comctl32.lib",
+        "shlwapi.lib",
+        "rpcrt4.lib",
+      ]
+    }
+
+    if (is_linux) {
+      sources += includes_linux +
+                 gypi_paths2.cefsimple_sources_linux
+
+      if (use_x11) {
+        libs = [
+          "X11",
+        ]
+      }
+
+      if (!is_component_build) {
+        # Set rpath to find our own libfreetype even in a non-component build.
+        configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
+      }
+    }
+  }
+
+
+  #
+  # ceftests targets.
+  #
+  
+  if (is_linux) {
+    copy("copy_ceftests_files") {
+      sources = gypi_paths2.shared_sources_resources
+      outputs = [ "${root_out_dir}/ceftests_files/{{source_file_part}}" ]
+    }
+  }
+
+  executable("ceftests") {
+    testonly = true
+
+    sources = includes_common +
+              gypi_paths2.includes_wrapper +
+              gypi_paths2.shared_sources_browser +
+              gypi_paths2.shared_sources_common +
+              gypi_paths2.shared_sources_renderer +
+              gypi_paths2.ceftests_sources_common +
+              gypi_paths2.ceftests_sources_views
+
+    deps = [
+      ":libcef",
+      ":libcef_dll_wrapper",
+      "//testing/gtest",
+    ]
+
+    defines = [
+      "CEF_USE_SANDBOX",
+    ]
+
+    if (is_win) {
+      sources += gypi_paths2.shared_sources_win +
+                 gypi_paths2.ceftests_sources_win
+
+      deps += [
+        ":cef_sandbox",
+        "//build/win:default_exe_manifest",
+      ]
+    }
+
+    if (is_linux) {
+      sources += gypi_paths2.shared_sources_linux +
+                 gypi_paths2.ceftests_sources_linux
+
+      if (use_x11) {
+        libs = [
+          "X11",
+        ]
+      } else {
+        if (!use_sysroot) {
+          configs += [ ":glib" ]
+        }
+      }
+
+      deps += [
+        ":copy_ceftests_files",
+      ]
+    }
+
+    if (is_linux && !is_component_build) {
+      # Set rpath to find our own libfreetype even in a non-component build.
+      configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
+    }
+  }
+}
diff --git a/src/CHROMIUM_BUILD_COMPATIBILITY.txt b/src/CHROMIUM_BUILD_COMPATIBILITY.txt
new file mode 100644
index 0000000..a9d0b66
--- /dev/null
+++ b/src/CHROMIUM_BUILD_COMPATIBILITY.txt
@@ -0,0 +1,12 @@
+# The Chromium Embedded Framework (CEF) project is built on top of the Chromium
+# project source tree. Chromium should be updated to the URL and revision listed
+# below before building CEF. Chromium compatibility information for older CEF
+# revisions is available by viewing this file's change history.
+#
+# Instructions for building CEF are available at:
+# https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding
+
+{
+  'chromium_checkout': 'refs/tags/83.0.4103.106',
+  'depot_tools_checkout': 'fc5e103221'
+}
diff --git a/src/CHROMIUM_UPDATE.txt b/src/CHROMIUM_UPDATE.txt
new file mode 100644
index 0000000..4c31f4e
--- /dev/null
+++ b/src/CHROMIUM_UPDATE.txt
@@ -0,0 +1,68 @@
+# The Chromium Embedded Framework (CEF) project is built on top of the Chromium
+# project source tree. When updating Chromium to a new version certain files and
+# patterns should be observed for changes. If changes are detected then the CEF
+# source code or patch files will likely need to be updated.
+#
+# Add `--log-chromium-changes` to the automate-git.py command-line to output
+# the following files in the <download-dir>:
+#
+# * chromium_update_changes.diff
+#   Files in the chromium/src directory that have changed. See the 'files'
+#   section below.
+#
+# * chromium_update_patterns.txt
+#   Files in the chromium/src directory that contain invalid/unexpected
+#   patterns. See the 'patterns' section below. Failure of this step is
+#   considered a fatal error during update.
+#
+# * chromium_update_patches.txt
+#   Output from attempting to update existing Chromium patch files using the
+#   patch_updater.py tool. Failure of this step is considered a fatal error
+#   during update.
+#
+# For complete update instructions see:
+# https://bitbucket.org/chromiumembedded/cef/wiki/ChromiumUpdate.md
+
+{
+  # Files in the chromium/src directory that should be evaluated for changes.
+  # Similar changes may need to be applied to the CEF source code.
+  'files': [
+    'chrome/app/chrome_*_manifest.*',
+    'chrome/app/chrome_*_manifests.*',
+    'chrome/browser/browser_process.h',
+    'chrome/browser/extensions/api/tabs/tabs_api.*',
+    'chrome/browser/extensions/chrome_component_extension_resource_manager.*',
+    'chrome/browser/extensions/chrome_extension_web_contents_observer.*',
+    'chrome/browser/extensions/component_loader.*',
+    'chrome/browser/extensions/extension_service.*',
+    'chrome/browser/printing/print_view_manager*',
+    'chrome/browser/printing/printing_message_filter*',
+    'chrome/browser/profiles/profile.h',
+    'chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.*',
+    'chrome/common/extensions/api/*_features.json',
+    'chrome/renderer/chrome_content_renderer_client.*',
+    'chrome/renderer/extensions/chrome_extensions_renderer_client.*',
+    'content/browser/renderer_host/render_widget_host_view_base.*',
+    'content/public/browser/content_browser_client.*',
+    'content/public/browser/render_widget_host_view.h',
+    'content/public/browser/storage_partition.h',
+    'content/public/browser/web_contents_delegate.h',
+    'content/public/common/content_features.cc',
+    'content/shell/BUILD.gn',
+    'content/shell/app/*',
+    'content/shell/browser/shell_*',
+    'content/shell/browser/renderer_host/shell_*',
+    'content/shell/common/shell_*',
+    'content/shell/gpu/shell_*',
+    'content/shell/renderer/shell_*',
+    'content/shell/utility/shell_*',
+    'extensions/shell/*',
+    'net/base/features.cc',
+    'net/cookies/cookie_store.h',
+    'services/network/public/cpp/features.cc',
+    'ui/base/ui_base_features.cc',
+  ],
+  # Patterns that should not be found in the chromium/src directory after
+  # applying patch files.
+  'patterns': [],
+}
diff --git a/src/CMakeLists.txt.in b/src/CMakeLists.txt.in
new file mode 100644
index 0000000..120db2b
--- /dev/null
+++ b/src/CMakeLists.txt.in
@@ -0,0 +1,213 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+# OVERVIEW
+#
+# CMake is a cross-platform open-source build system that can generate project
+# files in many different formats. It can be downloaded from
+# http://www.cmake.org or installed via a platform package manager.
+#
+# CMake-generated project formats that have been tested with this CEF binary
+# distribution include:
+#
+# Linux:      Ninja, Unix Makefiles
+# Mac OS X:   Ninja, Xcode 5+
+# Windows:    Ninja, Visual Studio 2010+
+#
+# Ninja is a cross-platform open-source tool for running fast builds using
+# pre-installed platform toolchains (GNU, clang, Xcode or MSVC). It can be
+# downloaded from http://martine.github.io/ninja/ or installed via a platform
+# package manager.
+#
+# CMAKE STRUCTURE
+#
+# This CEF binary distribution includes the following CMake files:
+#
+# CMakeLists.txt              Bootstrap that sets up the CMake environment.
+# cmake/*.cmake               CEF configuration files shared by all targets.
+# libcef_dll/CMakeLists.txt   Defines the libcef_dll_wrapper target.
+# tests/*/CMakeLists.txt      Defines the test application target.
+#
+# See the "TODO:" comments below for guidance on how to integrate this CEF
+# binary distribution into a new or existing CMake project.
+#
+# BUILD REQUIREMENTS
+#
+# The below requirements must be met to build this CEF binary distribution.
+#
+# - CMake version 2.8.12.1 or newer.
+#
+# - Linux requirements:
+#   Currently supported distributions include Debian Wheezy, Ubuntu Precise, and
+#   related. Ubuntu 14.04 64-bit is recommended. Newer versions will likely also
+#   work but may not have been tested.
+#   Required packages include:
+#     build-essential
+#     libgtk2.0-dev     (required by the cefclient target only)
+#     libgtkglext1-dev  (required by the cefclient target only)
+#
+# - Mac OS X requirements:
+#   Xcode 5 or newer building on Mac OS X 10.9 (Mavericks) or newer. Xcode 8.3
+#   and OS X 10.12 are recommended. The Xcode command-line tools must also be
+#   installed. Only 64-bit builds are supported on OS X.
+#
+# - Windows requirements:
+#   Visual Studio 2010 or newer building on Windows 7 or newer. Visual Studio
+#   2015 Update 3 and Windows 10 64-bit are recommended.
+#
+# BUILD EXAMPLES
+#
+# The below commands will generate project files and create a Debug build of all
+# CEF targets using CMake and the platform toolchain.
+#
+# Start by creating and entering the CMake build output directory:
+# > cd path/to/cef_binary_*
+# > mkdir build && cd build
+#
+# To perform a Linux build using a 32-bit CEF binary distribution on a 32-bit
+# Linux platform or a 64-bit CEF binary distribution on a 64-bit Linux platform:
+#   Using Unix Makefiles:
+#     > cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ..
+#     > make -j4 cefclient cefsimple
+#
+#   Using Ninja:
+#     > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug ..
+#     > ninja cefclient cefsimple
+#
+# To perform a Mac OS X build using a 64-bit CEF binary distribution:
+#   Using the Xcode IDE:
+#     > cmake -G "Xcode" -DPROJECT_ARCH="x86_64" ..
+#     Open build\cef.xcodeproj in Xcode and select Product > Build.
+#
+#   Using Ninja:
+#     > cmake -G "Ninja" -DPROJECT_ARCH="x86_64" -DCMAKE_BUILD_TYPE=Debug ..
+#     > ninja cefclient cefsimple
+#
+# To perform a Windows build using a 32-bit CEF binary distribution:
+#   Using the Visual Studio 2015 IDE:
+#     > cmake -G "Visual Studio 14" ..
+#     Open build\cef.sln in Visual Studio and select Build > Build Solution.
+#
+#   Using Ninja with Visual Studio 2015 command-line tools:
+#     (this path may be different depending on your Visual Studio installation)
+#     > "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"
+#     > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug ..
+#     > ninja cefclient cefsimple
+#
+# To perform a Windows build using a 64-bit CEF binary distribution:
+#   Using the Visual Studio 2015 IDE:
+#     > cmake -G "Visual Studio 14 Win64" ..
+#     Open build\cef.sln in Visual Studio and select Build > Build Solution.
+#
+#   Using Ninja with Visual Studio 2015 command-line tools:
+#     (this path may be different depending on your Visual Studio installation)
+#     > "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"
+#     > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug ..
+#     > ninja cefclient cefsimple
+
+#
+# Global setup.
+#
+
+cmake_minimum_required(VERSION 2.8.12.1)
+
+# Only generate Debug and Release configuration types.
+set(CMAKE_CONFIGURATION_TYPES Debug Release)
+
+# Project name.
+# TODO: Change this line to match your project name when you copy this file.
+project(cef)
+
+# Use folders in the resulting project files.
+set_property(GLOBAL PROPERTY OS_FOLDERS ON)
+
+
+#
+# CEF_ROOT setup.
+# This variable must be set to locate the binary distribution.
+# TODO: Choose one of the below examples and comment out the rest.
+#
+
+# Example 1: The current directory contains both the complete binary
+#            distribution and your project.
+# A. Comment in these lines:
+#
+set(CEF_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CEF_ROOT}/cmake")
+
+# Example 2: The binary distribution is in a separate directory from your
+#            project. Locate the binary distribution using the CEF_ROOT CMake
+#            variable.
+# A. Create a directory structure for your project like the following:
+#    myproject/
+#      CMakeLists.txt    <= top-level CMake configuration
+#      mytarget/
+#        CMakeLists.txt  <= CMake configuration for `mytarget`
+#        ... other `mytarget` source files
+# B. Copy this file to "myproject/CMakeLists.txt" as the top-level CMake
+#    configuration.
+# C. Create the target-specific "myproject/mytarget/CMakeLists.txt" file for
+#    your application. See the included cefclient and cefsimple CMakeLists.txt
+#    files as an example.
+# D. Comment in these lines:
+#
+# set(CEF_ROOT "c:/path/to/cef_binary_3.2704.xxxx.gyyyyyyy_windows32")
+# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CEF_ROOT}/cmake")
+
+# Example 3: The binary distribution is in a separate directory from your
+#            project. Locate the binary distribution using the CEF_ROOT
+#            environment variable.
+# A. Create a directory structure for your project like the following:
+#    myproject/
+#      CMakeLists.txt    <= top-level CMake configuration
+#      cmake/
+#        FindCEF.cmake   <= CEF CMake configuration entry point
+#      mytarget/
+#        CMakeLists.txt  <= CMake configuration for `mytarget`
+#        ... other `mytarget` source files
+# B. Copy this file to "myproject/CMakeLists.txt" as the top-level CMake
+#    configuration.
+# C. Copy the cmake/FindCEF.cmake file to "myproject/cmake/FindCEF.cmake".
+# D. Create the target-specific "myproject/mytarget/CMakeLists.txt" file for
+#    your application. See the included cefclient and cefsimple CMakeLists.txt
+#    files as an example.
+# E. Set the CEF_ROOT environment variable before executing CMake. For example:
+#    > set CEF_ROOT=c:\path\to\cef_binary_3.2704.xxxx.gyyyyyyy_windows32
+# F. Comment in these lines:
+#
+# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+
+#
+# Load the CEF configuration.
+#
+
+# Execute FindCEF.cmake which must exist in CMAKE_MODULE_PATH.
+find_package(CEF REQUIRED)
+
+
+#
+# Define CEF-based targets.
+#
+
+# Include the libcef_dll_wrapper target.
+# Comes from the libcef_dll/CMakeLists.txt file in the binary distribution
+# directory.
+add_subdirectory(${CEF_LIBCEF_DLL_WRAPPER_PATH} libcef_dll_wrapper)
+
+# Include application targets.
+# Comes from the <target>/CMakeLists.txt file in the current directory.
+# TODO: Change these lines to match your project target when you copy this file.
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests")
+  add_subdirectory(tests/cefsimple)
+  add_subdirectory(tests/gtest)
+  add_subdirectory(tests/ceftests)
+endif()
+
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/cefclient")
+  add_subdirectory(tests/cefclient)
+endif()
+
+# Display configuration settings.
+PRINT_CEF_CONFIG()
diff --git a/src/DEPS b/src/DEPS
new file mode 100644
index 0000000..0a7dc22
--- /dev/null
+++ b/src/DEPS
@@ -0,0 +1,7 @@
+hooks = [
+  {
+    # A change to a .gyp, .gypi, or to GYP itself should run the generator.
+    "pattern": ".",
+    "action": ["python", "src/cef/tools/gclient_hook.py"],
+  },
+]
diff --git a/src/LICENSE.txt b/src/LICENSE.txt
new file mode 100644
index 0000000..b55d613
--- /dev/null
+++ b/src/LICENSE.txt
@@ -0,0 +1,29 @@
+// Copyright (c) 2008-2020 Marshall A. Greenblatt. Portions Copyright (c)
+// 2006-2009 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..9b69d59
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,87 @@
+The Chromium Embedded Framework (CEF) is a simple framework for embedding Chromium-based browsers in other applications.

+

+# Quick Links

+

+* Project Page - https://bitbucket.org/chromiumembedded/cef

+* Tutorial - https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial

+* General Usage - https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage

+* Master Build Quick-Start - https://bitbucket.org/chromiumembedded/cef/wiki/MasterBuildQuickStart

+* Branches and Building - https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding

+* Announcements - https://groups.google.com/forum/#!forum/cef-announce

+* Support Forum - http://www.magpcss.org/ceforum/

+* CEF1 C++ API Docs - http://magpcss.org/ceforum/apidocs/

+* CEF3 C++ API Docs - http://magpcss.org/ceforum/apidocs3/

+* Downloads - http://opensource.spotify.com/cefbuilds/index.html

+* Donations - http://www.magpcss.org/ceforum/donate.php

+

+# Introduction

+

+CEF is a BSD-licensed open source project founded by Marshall Greenblatt in 2008 and based on the [Google Chromium](http://www.chromium.org/Home) project. Unlike the Chromium project itself, which focuses mainly on Google Chrome application development, CEF focuses on facilitating embedded browser use cases in third-party applications. CEF insulates the user from the underlying Chromium and Blink code complexity by offering production-quality stable APIs, release branches tracking specific Chromium releases, and binary distributions. Most features in CEF have default implementations that provide rich functionality while requiring little or no integration work from the user. There are currently over 100 million installed instances of CEF around the world embedded in products from a wide range of companies and industries. A partial list of companies and products using CEF is available on the [CEF Wikipedia page](http://en.wikipedia.org/wiki/Chromium_Embedded_Framework#Applications_using_CEF). Some use cases for CEF include:

+

+* Embedding an HTML5-compliant Web browser control in an existing native application.

+* Creating a light-weight native “shell” application that hosts a user interface developed primarily using Web technologies.

+* Rendering Web content “off-screen” in applications that have their own custom drawing frameworks.

+* Acting as a host for automated testing of existing Web properties and applications.

+

+CEF supports a wide range of programming languages and operating systems and can be easily integrated into both new and existing applications. It was designed from the ground up with both performance and ease of use in mind. The base framework includes C and C++ programming interfaces exposed via native libraries that insulate the host application from Chromium and Blink implementation details. It provides close integration between the browser and the host application including support for custom plugins, protocols, JavaScript objects and JavaScript extensions. The host application can optionally control resource loading, navigation, context menus, printing and more, while taking advantage of the same performance and HTML5 technologies available in the Google Chrome Web browser.

+

+Numerous individuals and organizations contribute time and resources to support CEF development, but more involvement from the community is always welcome. This includes support for both the core CEF project and external projects that integrate CEF with additional programming languages and frameworks (see the "External Projects" section below). If you are interested in donating time to help with CEF development please see the "Helping Out" section below. If you are interested in donating money to support general CEF development and infrastructure efforts please visit the [CEF Donations](http://www.magpcss.org/ceforum/donate.php) page.

+

+# Getting Started

+

+Users new to CEF development should start by reading the [Tutorial](https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial) Wiki page for an overview of CEF usage and then proceed to the [GeneralUsage](https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage) Wiki page for a more in-depth discussion or architectural and usage issues. Complete API documentation is available [here](http://magpcss.org/ceforum/apidocs3/). CEF support and related discussion is available on the [CEF Forum](http://www.magpcss.org/ceforum/).

+

+# Binary Distributions

+

+Binary distributions, which include all files necessary to build a CEF-based application, are available on the [Downloads](http://opensource.spotify.com/cefbuilds/index.html) page. Binary distributions are stand-alone and do not require the download of CEF or Chromium source code. Symbol files for debugging binary distributions of libcef can also be downloaded from the above links.

+

+# Source Distributions

+

+The CEF project is an extension of the Chromium project. CEF maintains development and release branches that track Chromium branches. CEF source code can be downloaded, built and packaged manually or with automated tools. Visit the [BranchesAndBuilding](https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding) Wiki page for more information.

+

+# External Projects

+

+The base CEF framework includes support for the C and C++ programming languages. Thanks to the hard work of external maintainers CEF can integrate with a number of other programming languages and frameworks. These external projects are not maintained by CEF so please contact the respective project maintainer if you have any questions or issues.

+

+* .Net (CEF3) - https://github.com/cefsharp/CefSharp

+* .Net (CEF1) - https://bitbucket.org/fddima/cefglue

+* .Net/Mono (CEF3) - https://bitbucket.org/xilium/xilium.cefglue

+* .Net (CEF3) - https://bitbucket.org/chromiumfx/chromiumfx

+* Delphi (CEF1) - http://code.google.com/p/delphichromiumembedded/

+* Delphi (CEF3) - https://github.com/hgourvest/dcef3

+* Delphi (CEF3) - https://github.com/salvadordf/CEF4Delphi

+* Go - https://github.com/richardwilkes/cef

+* Go - https://github.com/CzarekTomczak/cef2go

+* Java - https://bitbucket.org/chromiumembedded/java-cef

+* Java - http://code.google.com/p/javacef/

+* Python - http://code.google.com/p/cefpython/

+

+If you're the maintainer of a project not listed above and would like your project listed here please either post to the [CEF Forum](http://www.magpcss.org/ceforum/) or contact Marshall directly.

+

+# Helping Out

+

+CEF is still very much a work in progress. Some ways that you can help out:

+

+\- Vote for issues in the [CEF issue tracker](https://bitbucket.org/chromiumembedded/cef/issues?status=new&status=open) that are important to you. This helps with development prioritization.

+

+\- Report any bugs that you find or feature requests that are important to you. Make sure to first search for existing issues before creating new ones. Please use the [CEF Forum](http://magpcss.org/ceforum) and not the issue tracker for usage questions. Each CEF issue should:

+

+* Include the CEF revision or binary distribution version.

+* Include information about your OS and compiler version.

+* If the issue is a bug please provide detailed reproduction information.

+* If the issue is a feature please describe why the feature is beneficial.

+

+\- Write unit tests for new or existing functionality.

+

+\- Pull requests and patches are welcome. View open issues in the [CEF issue tracker](https://bitbucket.org/chromiumembedded/cef/issues?status=new&status=open) or search for TODO(cef) in the source code for ideas.

+

+If you would like to contribute source code changes to CEF please follow the below guidelines:

+

+\- Create or find an appropriate issue for each distinct bug, feature or change. 

+

+\- Submit a [pull request](https://bitbucket.org/chromiumembedded/cef/wiki/ContributingWithGit) or create a patch with your changes and attach it to the CEF issue. Changes should:

+

+* Be submitted against the current [CEF master branch](https://bitbucket.org/chromiumembedded/cef/src/?at=master) unless explicitly fixing a bug in a CEF release branch.

+* Follow the style of existing CEF source files. In general CEF uses the [Chromium coding style](http://www.chromium.org/developers/coding-style).

+* Include new or modified unit tests as appropriate to the functionality.

+* Not include unnecessary or unrelated changes.

diff --git a/src/VERSION.in b/src/VERSION.in
new file mode 100644
index 0000000..2ecb980
--- /dev/null
+++ b/src/VERSION.in
@@ -0,0 +1 @@
+CEF_MAJOR=3
diff --git a/src/cef_create_projects.bat b/src/cef_create_projects.bat
new file mode 100644
index 0000000..8dc7784
--- /dev/null
+++ b/src/cef_create_projects.bat
@@ -0,0 +1,2 @@
+@echo off

+python.bat tools\gclient_hook.py

diff --git a/src/cef_create_projects.sh b/src/cef_create_projects.sh
new file mode 100755
index 0000000..a9c94cd
--- /dev/null
+++ b/src/cef_create_projects.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python tools/gclient_hook.py
diff --git a/src/cef_paths.gypi b/src/cef_paths.gypi
new file mode 100644
index 0000000..a972dea
--- /dev/null
+++ b/src/cef_paths.gypi
@@ -0,0 +1,831 @@
+# Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+#
+# ---------------------------------------------------------------------------
+#
+# This file was generated by the CEF translator tool and should not edited
+# by hand. See the translator.README.txt file in the tools directory for
+# more information.
+#
+# $hash=72268a78a76d7d91b8ad47f6b6e9f6d9cb04d9cf$
+#
+
+{
+  'variables': {
+    'autogen_cpp_includes': [
+      'include/cef_accessibility_handler.h',
+      'include/cef_app.h',
+      'include/cef_audio_handler.h',
+      'include/cef_auth_callback.h',
+      'include/cef_browser.h',
+      'include/cef_browser_process_handler.h',
+      'include/cef_callback.h',
+      'include/cef_client.h',
+      'include/cef_command_line.h',
+      'include/cef_context_menu_handler.h',
+      'include/cef_cookie.h',
+      'include/cef_crash_util.h',
+      'include/cef_devtools_message_observer.h',
+      'include/cef_dialog_handler.h',
+      'include/cef_display_handler.h',
+      'include/cef_dom.h',
+      'include/cef_download_handler.h',
+      'include/cef_download_item.h',
+      'include/cef_drag_data.h',
+      'include/cef_drag_handler.h',
+      'include/cef_extension.h',
+      'include/cef_extension_handler.h',
+      'include/cef_file_util.h',
+      'include/cef_find_handler.h',
+      'include/cef_focus_handler.h',
+      'include/cef_frame.h',
+      'include/cef_image.h',
+      'include/cef_jsdialog_handler.h',
+      'include/cef_keyboard_handler.h',
+      'include/cef_life_span_handler.h',
+      'include/cef_load_handler.h',
+      'include/cef_media_router.h',
+      'include/cef_menu_model.h',
+      'include/cef_menu_model_delegate.h',
+      'include/cef_navigation_entry.h',
+      'include/cef_origin_whitelist.h',
+      'include/cef_parser.h',
+      'include/cef_path_util.h',
+      'include/cef_print_handler.h',
+      'include/cef_print_settings.h',
+      'include/cef_process_message.h',
+      'include/cef_process_util.h',
+      'include/cef_registration.h',
+      'include/cef_render_handler.h',
+      'include/cef_render_process_handler.h',
+      'include/cef_request.h',
+      'include/cef_request_callback.h',
+      'include/cef_request_context.h',
+      'include/cef_request_context_handler.h',
+      'include/cef_request_handler.h',
+      'include/cef_resource_bundle.h',
+      'include/cef_resource_bundle_handler.h',
+      'include/cef_resource_handler.h',
+      'include/cef_resource_request_handler.h',
+      'include/cef_response.h',
+      'include/cef_response_filter.h',
+      'include/cef_scheme.h',
+      'include/cef_server.h',
+      'include/cef_ssl_info.h',
+      'include/cef_ssl_status.h',
+      'include/cef_stream.h',
+      'include/cef_string_visitor.h',
+      'include/cef_task.h',
+      'include/cef_thread.h',
+      'include/cef_trace.h',
+      'include/cef_urlrequest.h',
+      'include/cef_v8.h',
+      'include/cef_values.h',
+      'include/cef_waitable_event.h',
+      'include/cef_web_plugin.h',
+      'include/cef_x509_certificate.h',
+      'include/cef_xml_reader.h',
+      'include/cef_zip_reader.h',
+      'include/test/cef_test_helpers.h',
+      'include/test/cef_translator_test.h',
+      'include/views/cef_box_layout.h',
+      'include/views/cef_browser_view.h',
+      'include/views/cef_browser_view_delegate.h',
+      'include/views/cef_button.h',
+      'include/views/cef_button_delegate.h',
+      'include/views/cef_display.h',
+      'include/views/cef_fill_layout.h',
+      'include/views/cef_label_button.h',
+      'include/views/cef_layout.h',
+      'include/views/cef_menu_button.h',
+      'include/views/cef_menu_button_delegate.h',
+      'include/views/cef_panel.h',
+      'include/views/cef_panel_delegate.h',
+      'include/views/cef_scroll_view.h',
+      'include/views/cef_textfield.h',
+      'include/views/cef_textfield_delegate.h',
+      'include/views/cef_view.h',
+      'include/views/cef_view_delegate.h',
+      'include/views/cef_window.h',
+      'include/views/cef_window_delegate.h',
+    ],
+    'autogen_capi_includes': [
+      'include/capi/cef_accessibility_handler_capi.h',
+      'include/capi/cef_app_capi.h',
+      'include/capi/cef_audio_handler_capi.h',
+      'include/capi/cef_auth_callback_capi.h',
+      'include/capi/cef_browser_capi.h',
+      'include/capi/cef_browser_process_handler_capi.h',
+      'include/capi/cef_callback_capi.h',
+      'include/capi/cef_client_capi.h',
+      'include/capi/cef_command_line_capi.h',
+      'include/capi/cef_context_menu_handler_capi.h',
+      'include/capi/cef_cookie_capi.h',
+      'include/capi/cef_crash_util_capi.h',
+      'include/capi/cef_devtools_message_observer_capi.h',
+      'include/capi/cef_dialog_handler_capi.h',
+      'include/capi/cef_display_handler_capi.h',
+      'include/capi/cef_dom_capi.h',
+      'include/capi/cef_download_handler_capi.h',
+      'include/capi/cef_download_item_capi.h',
+      'include/capi/cef_drag_data_capi.h',
+      'include/capi/cef_drag_handler_capi.h',
+      'include/capi/cef_extension_capi.h',
+      'include/capi/cef_extension_handler_capi.h',
+      'include/capi/cef_file_util_capi.h',
+      'include/capi/cef_find_handler_capi.h',
+      'include/capi/cef_focus_handler_capi.h',
+      'include/capi/cef_frame_capi.h',
+      'include/capi/cef_image_capi.h',
+      'include/capi/cef_jsdialog_handler_capi.h',
+      'include/capi/cef_keyboard_handler_capi.h',
+      'include/capi/cef_life_span_handler_capi.h',
+      'include/capi/cef_load_handler_capi.h',
+      'include/capi/cef_media_router_capi.h',
+      'include/capi/cef_menu_model_capi.h',
+      'include/capi/cef_menu_model_delegate_capi.h',
+      'include/capi/cef_navigation_entry_capi.h',
+      'include/capi/cef_origin_whitelist_capi.h',
+      'include/capi/cef_parser_capi.h',
+      'include/capi/cef_path_util_capi.h',
+      'include/capi/cef_print_handler_capi.h',
+      'include/capi/cef_print_settings_capi.h',
+      'include/capi/cef_process_message_capi.h',
+      'include/capi/cef_process_util_capi.h',
+      'include/capi/cef_registration_capi.h',
+      'include/capi/cef_render_handler_capi.h',
+      'include/capi/cef_render_process_handler_capi.h',
+      'include/capi/cef_request_capi.h',
+      'include/capi/cef_request_callback_capi.h',
+      'include/capi/cef_request_context_capi.h',
+      'include/capi/cef_request_context_handler_capi.h',
+      'include/capi/cef_request_handler_capi.h',
+      'include/capi/cef_resource_bundle_capi.h',
+      'include/capi/cef_resource_bundle_handler_capi.h',
+      'include/capi/cef_resource_handler_capi.h',
+      'include/capi/cef_resource_request_handler_capi.h',
+      'include/capi/cef_response_capi.h',
+      'include/capi/cef_response_filter_capi.h',
+      'include/capi/cef_scheme_capi.h',
+      'include/capi/cef_server_capi.h',
+      'include/capi/cef_ssl_info_capi.h',
+      'include/capi/cef_ssl_status_capi.h',
+      'include/capi/cef_stream_capi.h',
+      'include/capi/cef_string_visitor_capi.h',
+      'include/capi/cef_task_capi.h',
+      'include/capi/cef_thread_capi.h',
+      'include/capi/cef_trace_capi.h',
+      'include/capi/cef_urlrequest_capi.h',
+      'include/capi/cef_v8_capi.h',
+      'include/capi/cef_values_capi.h',
+      'include/capi/cef_waitable_event_capi.h',
+      'include/capi/cef_web_plugin_capi.h',
+      'include/capi/cef_x509_certificate_capi.h',
+      'include/capi/cef_xml_reader_capi.h',
+      'include/capi/cef_zip_reader_capi.h',
+      'include/capi/test/cef_test_helpers_capi.h',
+      'include/capi/test/cef_translator_test_capi.h',
+      'include/capi/views/cef_box_layout_capi.h',
+      'include/capi/views/cef_browser_view_capi.h',
+      'include/capi/views/cef_browser_view_delegate_capi.h',
+      'include/capi/views/cef_button_capi.h',
+      'include/capi/views/cef_button_delegate_capi.h',
+      'include/capi/views/cef_display_capi.h',
+      'include/capi/views/cef_fill_layout_capi.h',
+      'include/capi/views/cef_label_button_capi.h',
+      'include/capi/views/cef_layout_capi.h',
+      'include/capi/views/cef_menu_button_capi.h',
+      'include/capi/views/cef_menu_button_delegate_capi.h',
+      'include/capi/views/cef_panel_capi.h',
+      'include/capi/views/cef_panel_delegate_capi.h',
+      'include/capi/views/cef_scroll_view_capi.h',
+      'include/capi/views/cef_textfield_capi.h',
+      'include/capi/views/cef_textfield_delegate_capi.h',
+      'include/capi/views/cef_view_capi.h',
+      'include/capi/views/cef_view_delegate_capi.h',
+      'include/capi/views/cef_window_capi.h',
+      'include/capi/views/cef_window_delegate_capi.h',
+    ],
+    'autogen_library_side': [
+      'libcef_dll/ctocpp/accessibility_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/accessibility_handler_ctocpp.h',
+      'libcef_dll/ctocpp/app_ctocpp.cc',
+      'libcef_dll/ctocpp/app_ctocpp.h',
+      'libcef_dll/ctocpp/audio_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/audio_handler_ctocpp.h',
+      'libcef_dll/cpptoc/auth_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/auth_callback_cpptoc.h',
+      'libcef_dll/cpptoc/before_download_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/before_download_callback_cpptoc.h',
+      'libcef_dll/cpptoc/binary_value_cpptoc.cc',
+      'libcef_dll/cpptoc/binary_value_cpptoc.h',
+      'libcef_dll/cpptoc/views/box_layout_cpptoc.cc',
+      'libcef_dll/cpptoc/views/box_layout_cpptoc.h',
+      'libcef_dll/cpptoc/browser_cpptoc.cc',
+      'libcef_dll/cpptoc/browser_cpptoc.h',
+      'libcef_dll/cpptoc/browser_host_cpptoc.cc',
+      'libcef_dll/cpptoc/browser_host_cpptoc.h',
+      'libcef_dll/ctocpp/browser_process_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/browser_process_handler_ctocpp.h',
+      'libcef_dll/cpptoc/views/browser_view_cpptoc.cc',
+      'libcef_dll/cpptoc/views/browser_view_cpptoc.h',
+      'libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h',
+      'libcef_dll/cpptoc/views/button_cpptoc.cc',
+      'libcef_dll/cpptoc/views/button_cpptoc.h',
+      'libcef_dll/ctocpp/views/button_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/views/button_delegate_ctocpp.h',
+      'libcef_dll/cpptoc/callback_cpptoc.cc',
+      'libcef_dll/cpptoc/callback_cpptoc.h',
+      'libcef_dll/ctocpp/client_ctocpp.cc',
+      'libcef_dll/ctocpp/client_ctocpp.h',
+      'libcef_dll/cpptoc/command_line_cpptoc.cc',
+      'libcef_dll/cpptoc/command_line_cpptoc.h',
+      'libcef_dll/ctocpp/completion_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/completion_callback_ctocpp.h',
+      'libcef_dll/ctocpp/context_menu_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/context_menu_handler_ctocpp.h',
+      'libcef_dll/cpptoc/context_menu_params_cpptoc.cc',
+      'libcef_dll/cpptoc/context_menu_params_cpptoc.h',
+      'libcef_dll/ctocpp/cookie_access_filter_ctocpp.cc',
+      'libcef_dll/ctocpp/cookie_access_filter_ctocpp.h',
+      'libcef_dll/cpptoc/cookie_manager_cpptoc.cc',
+      'libcef_dll/cpptoc/cookie_manager_cpptoc.h',
+      'libcef_dll/ctocpp/cookie_visitor_ctocpp.cc',
+      'libcef_dll/ctocpp/cookie_visitor_ctocpp.h',
+      'libcef_dll/cpptoc/domdocument_cpptoc.cc',
+      'libcef_dll/cpptoc/domdocument_cpptoc.h',
+      'libcef_dll/cpptoc/domnode_cpptoc.cc',
+      'libcef_dll/cpptoc/domnode_cpptoc.h',
+      'libcef_dll/ctocpp/domvisitor_ctocpp.cc',
+      'libcef_dll/ctocpp/domvisitor_ctocpp.h',
+      'libcef_dll/ctocpp/delete_cookies_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h',
+      'libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.cc',
+      'libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h',
+      'libcef_dll/ctocpp/dialog_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/dialog_handler_ctocpp.h',
+      'libcef_dll/cpptoc/dictionary_value_cpptoc.cc',
+      'libcef_dll/cpptoc/dictionary_value_cpptoc.h',
+      'libcef_dll/cpptoc/views/display_cpptoc.cc',
+      'libcef_dll/cpptoc/views/display_cpptoc.h',
+      'libcef_dll/ctocpp/display_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/display_handler_ctocpp.h',
+      'libcef_dll/ctocpp/download_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/download_handler_ctocpp.h',
+      'libcef_dll/ctocpp/download_image_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/download_image_callback_ctocpp.h',
+      'libcef_dll/cpptoc/download_item_cpptoc.cc',
+      'libcef_dll/cpptoc/download_item_cpptoc.h',
+      'libcef_dll/cpptoc/download_item_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/download_item_callback_cpptoc.h',
+      'libcef_dll/cpptoc/drag_data_cpptoc.cc',
+      'libcef_dll/cpptoc/drag_data_cpptoc.h',
+      'libcef_dll/ctocpp/drag_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/drag_handler_ctocpp.h',
+      'libcef_dll/ctocpp/end_tracing_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/end_tracing_callback_ctocpp.h',
+      'libcef_dll/cpptoc/extension_cpptoc.cc',
+      'libcef_dll/cpptoc/extension_cpptoc.h',
+      'libcef_dll/ctocpp/extension_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/extension_handler_ctocpp.h',
+      'libcef_dll/cpptoc/file_dialog_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/file_dialog_callback_cpptoc.h',
+      'libcef_dll/cpptoc/views/fill_layout_cpptoc.cc',
+      'libcef_dll/cpptoc/views/fill_layout_cpptoc.h',
+      'libcef_dll/ctocpp/find_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/find_handler_ctocpp.h',
+      'libcef_dll/ctocpp/focus_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/focus_handler_ctocpp.h',
+      'libcef_dll/cpptoc/frame_cpptoc.cc',
+      'libcef_dll/cpptoc/frame_cpptoc.h',
+      'libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.h',
+      'libcef_dll/cpptoc/image_cpptoc.cc',
+      'libcef_dll/cpptoc/image_cpptoc.h',
+      'libcef_dll/cpptoc/jsdialog_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/jsdialog_callback_cpptoc.h',
+      'libcef_dll/ctocpp/jsdialog_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/jsdialog_handler_ctocpp.h',
+      'libcef_dll/ctocpp/keyboard_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/keyboard_handler_ctocpp.h',
+      'libcef_dll/cpptoc/views/label_button_cpptoc.cc',
+      'libcef_dll/cpptoc/views/label_button_cpptoc.h',
+      'libcef_dll/cpptoc/views/layout_cpptoc.cc',
+      'libcef_dll/cpptoc/views/layout_cpptoc.h',
+      'libcef_dll/ctocpp/life_span_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/life_span_handler_ctocpp.h',
+      'libcef_dll/cpptoc/list_value_cpptoc.cc',
+      'libcef_dll/cpptoc/list_value_cpptoc.h',
+      'libcef_dll/ctocpp/load_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/load_handler_ctocpp.h',
+      'libcef_dll/ctocpp/media_observer_ctocpp.cc',
+      'libcef_dll/ctocpp/media_observer_ctocpp.h',
+      'libcef_dll/cpptoc/media_route_cpptoc.cc',
+      'libcef_dll/cpptoc/media_route_cpptoc.h',
+      'libcef_dll/ctocpp/media_route_create_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/media_route_create_callback_ctocpp.h',
+      'libcef_dll/cpptoc/media_router_cpptoc.cc',
+      'libcef_dll/cpptoc/media_router_cpptoc.h',
+      'libcef_dll/cpptoc/media_sink_cpptoc.cc',
+      'libcef_dll/cpptoc/media_sink_cpptoc.h',
+      'libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.h',
+      'libcef_dll/cpptoc/media_source_cpptoc.cc',
+      'libcef_dll/cpptoc/media_source_cpptoc.h',
+      'libcef_dll/cpptoc/views/menu_button_cpptoc.cc',
+      'libcef_dll/cpptoc/views/menu_button_cpptoc.h',
+      'libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h',
+      'libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.cc',
+      'libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.h',
+      'libcef_dll/cpptoc/menu_model_cpptoc.cc',
+      'libcef_dll/cpptoc/menu_model_cpptoc.h',
+      'libcef_dll/ctocpp/menu_model_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/menu_model_delegate_ctocpp.h',
+      'libcef_dll/cpptoc/navigation_entry_cpptoc.cc',
+      'libcef_dll/cpptoc/navigation_entry_cpptoc.h',
+      'libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.cc',
+      'libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h',
+      'libcef_dll/cpptoc/views/panel_cpptoc.cc',
+      'libcef_dll/cpptoc/views/panel_cpptoc.h',
+      'libcef_dll/ctocpp/views/panel_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/views/panel_delegate_ctocpp.h',
+      'libcef_dll/ctocpp/pdf_print_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/pdf_print_callback_ctocpp.h',
+      'libcef_dll/cpptoc/post_data_cpptoc.cc',
+      'libcef_dll/cpptoc/post_data_cpptoc.h',
+      'libcef_dll/cpptoc/post_data_element_cpptoc.cc',
+      'libcef_dll/cpptoc/post_data_element_cpptoc.h',
+      'libcef_dll/cpptoc/print_dialog_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/print_dialog_callback_cpptoc.h',
+      'libcef_dll/ctocpp/print_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/print_handler_ctocpp.h',
+      'libcef_dll/cpptoc/print_job_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/print_job_callback_cpptoc.h',
+      'libcef_dll/cpptoc/print_settings_cpptoc.cc',
+      'libcef_dll/cpptoc/print_settings_cpptoc.h',
+      'libcef_dll/cpptoc/process_message_cpptoc.cc',
+      'libcef_dll/cpptoc/process_message_cpptoc.h',
+      'libcef_dll/ctocpp/read_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/read_handler_ctocpp.h',
+      'libcef_dll/ctocpp/register_cdm_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/register_cdm_callback_ctocpp.h',
+      'libcef_dll/cpptoc/registration_cpptoc.cc',
+      'libcef_dll/cpptoc/registration_cpptoc.h',
+      'libcef_dll/ctocpp/render_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/render_handler_ctocpp.h',
+      'libcef_dll/ctocpp/render_process_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/render_process_handler_ctocpp.h',
+      'libcef_dll/cpptoc/request_cpptoc.cc',
+      'libcef_dll/cpptoc/request_cpptoc.h',
+      'libcef_dll/cpptoc/request_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/request_callback_cpptoc.h',
+      'libcef_dll/cpptoc/request_context_cpptoc.cc',
+      'libcef_dll/cpptoc/request_context_cpptoc.h',
+      'libcef_dll/ctocpp/request_context_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/request_context_handler_ctocpp.h',
+      'libcef_dll/ctocpp/request_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/request_handler_ctocpp.h',
+      'libcef_dll/ctocpp/resolve_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/resolve_callback_ctocpp.h',
+      'libcef_dll/cpptoc/resource_bundle_cpptoc.cc',
+      'libcef_dll/cpptoc/resource_bundle_cpptoc.h',
+      'libcef_dll/ctocpp/resource_bundle_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/resource_bundle_handler_ctocpp.h',
+      'libcef_dll/ctocpp/resource_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/resource_handler_ctocpp.h',
+      'libcef_dll/cpptoc/resource_read_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/resource_read_callback_cpptoc.h',
+      'libcef_dll/ctocpp/resource_request_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/resource_request_handler_ctocpp.h',
+      'libcef_dll/cpptoc/resource_skip_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/resource_skip_callback_cpptoc.h',
+      'libcef_dll/cpptoc/response_cpptoc.cc',
+      'libcef_dll/cpptoc/response_cpptoc.h',
+      'libcef_dll/ctocpp/response_filter_ctocpp.cc',
+      'libcef_dll/ctocpp/response_filter_ctocpp.h',
+      'libcef_dll/cpptoc/run_context_menu_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/run_context_menu_callback_cpptoc.h',
+      'libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h',
+      'libcef_dll/cpptoc/sslinfo_cpptoc.cc',
+      'libcef_dll/cpptoc/sslinfo_cpptoc.h',
+      'libcef_dll/cpptoc/sslstatus_cpptoc.cc',
+      'libcef_dll/cpptoc/sslstatus_cpptoc.h',
+      'libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc',
+      'libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h',
+      'libcef_dll/cpptoc/scheme_registrar_cpptoc.cc',
+      'libcef_dll/cpptoc/scheme_registrar_cpptoc.h',
+      'libcef_dll/cpptoc/views/scroll_view_cpptoc.cc',
+      'libcef_dll/cpptoc/views/scroll_view_cpptoc.h',
+      'libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h',
+      'libcef_dll/cpptoc/server_cpptoc.cc',
+      'libcef_dll/cpptoc/server_cpptoc.h',
+      'libcef_dll/ctocpp/server_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/server_handler_ctocpp.h',
+      'libcef_dll/ctocpp/set_cookie_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/set_cookie_callback_ctocpp.h',
+      'libcef_dll/cpptoc/stream_reader_cpptoc.cc',
+      'libcef_dll/cpptoc/stream_reader_cpptoc.h',
+      'libcef_dll/cpptoc/stream_writer_cpptoc.cc',
+      'libcef_dll/cpptoc/stream_writer_cpptoc.h',
+      'libcef_dll/ctocpp/string_visitor_ctocpp.cc',
+      'libcef_dll/ctocpp/string_visitor_ctocpp.h',
+      'libcef_dll/ctocpp/task_ctocpp.cc',
+      'libcef_dll/ctocpp/task_ctocpp.h',
+      'libcef_dll/cpptoc/task_runner_cpptoc.cc',
+      'libcef_dll/cpptoc/task_runner_cpptoc.h',
+      'libcef_dll/cpptoc/views/textfield_cpptoc.cc',
+      'libcef_dll/cpptoc/views/textfield_cpptoc.h',
+      'libcef_dll/ctocpp/views/textfield_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h',
+      'libcef_dll/cpptoc/thread_cpptoc.cc',
+      'libcef_dll/cpptoc/thread_cpptoc.h',
+      'libcef_dll/cpptoc/test/translator_test_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_cpptoc.h',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.h',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.h',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.h',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.h',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.h',
+      'libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.h',
+      'libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.h',
+      'libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.h',
+      'libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.h',
+      'libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.h',
+      'libcef_dll/cpptoc/urlrequest_cpptoc.cc',
+      'libcef_dll/cpptoc/urlrequest_cpptoc.h',
+      'libcef_dll/ctocpp/urlrequest_client_ctocpp.cc',
+      'libcef_dll/ctocpp/urlrequest_client_ctocpp.h',
+      'libcef_dll/ctocpp/v8accessor_ctocpp.cc',
+      'libcef_dll/ctocpp/v8accessor_ctocpp.h',
+      'libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.h',
+      'libcef_dll/cpptoc/v8context_cpptoc.cc',
+      'libcef_dll/cpptoc/v8context_cpptoc.h',
+      'libcef_dll/cpptoc/v8exception_cpptoc.cc',
+      'libcef_dll/cpptoc/v8exception_cpptoc.h',
+      'libcef_dll/ctocpp/v8handler_ctocpp.cc',
+      'libcef_dll/ctocpp/v8handler_ctocpp.h',
+      'libcef_dll/ctocpp/v8interceptor_ctocpp.cc',
+      'libcef_dll/ctocpp/v8interceptor_ctocpp.h',
+      'libcef_dll/cpptoc/v8stack_frame_cpptoc.cc',
+      'libcef_dll/cpptoc/v8stack_frame_cpptoc.h',
+      'libcef_dll/cpptoc/v8stack_trace_cpptoc.cc',
+      'libcef_dll/cpptoc/v8stack_trace_cpptoc.h',
+      'libcef_dll/cpptoc/v8value_cpptoc.cc',
+      'libcef_dll/cpptoc/v8value_cpptoc.h',
+      'libcef_dll/cpptoc/value_cpptoc.cc',
+      'libcef_dll/cpptoc/value_cpptoc.h',
+      'libcef_dll/cpptoc/views/view_cpptoc.cc',
+      'libcef_dll/cpptoc/views/view_cpptoc.h',
+      'libcef_dll/ctocpp/views/view_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/views/view_delegate_ctocpp.h',
+      'libcef_dll/cpptoc/waitable_event_cpptoc.cc',
+      'libcef_dll/cpptoc/waitable_event_cpptoc.h',
+      'libcef_dll/cpptoc/web_plugin_info_cpptoc.cc',
+      'libcef_dll/cpptoc/web_plugin_info_cpptoc.h',
+      'libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.cc',
+      'libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.h',
+      'libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.h',
+      'libcef_dll/cpptoc/views/window_cpptoc.cc',
+      'libcef_dll/cpptoc/views/window_cpptoc.h',
+      'libcef_dll/ctocpp/views/window_delegate_ctocpp.cc',
+      'libcef_dll/ctocpp/views/window_delegate_ctocpp.h',
+      'libcef_dll/ctocpp/write_handler_ctocpp.cc',
+      'libcef_dll/ctocpp/write_handler_ctocpp.h',
+      'libcef_dll/cpptoc/x509cert_principal_cpptoc.cc',
+      'libcef_dll/cpptoc/x509cert_principal_cpptoc.h',
+      'libcef_dll/cpptoc/x509certificate_cpptoc.cc',
+      'libcef_dll/cpptoc/x509certificate_cpptoc.h',
+      'libcef_dll/cpptoc/xml_reader_cpptoc.cc',
+      'libcef_dll/cpptoc/xml_reader_cpptoc.h',
+      'libcef_dll/cpptoc/zip_reader_cpptoc.cc',
+      'libcef_dll/cpptoc/zip_reader_cpptoc.h',
+    ],
+    'autogen_client_side': [
+      'libcef_dll/cpptoc/accessibility_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/accessibility_handler_cpptoc.h',
+      'libcef_dll/cpptoc/app_cpptoc.cc',
+      'libcef_dll/cpptoc/app_cpptoc.h',
+      'libcef_dll/cpptoc/audio_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/audio_handler_cpptoc.h',
+      'libcef_dll/ctocpp/auth_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/auth_callback_ctocpp.h',
+      'libcef_dll/ctocpp/before_download_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/before_download_callback_ctocpp.h',
+      'libcef_dll/ctocpp/binary_value_ctocpp.cc',
+      'libcef_dll/ctocpp/binary_value_ctocpp.h',
+      'libcef_dll/ctocpp/views/box_layout_ctocpp.cc',
+      'libcef_dll/ctocpp/views/box_layout_ctocpp.h',
+      'libcef_dll/ctocpp/browser_ctocpp.cc',
+      'libcef_dll/ctocpp/browser_ctocpp.h',
+      'libcef_dll/ctocpp/browser_host_ctocpp.cc',
+      'libcef_dll/ctocpp/browser_host_ctocpp.h',
+      'libcef_dll/cpptoc/browser_process_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/browser_process_handler_cpptoc.h',
+      'libcef_dll/ctocpp/views/browser_view_ctocpp.cc',
+      'libcef_dll/ctocpp/views/browser_view_ctocpp.h',
+      'libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h',
+      'libcef_dll/ctocpp/views/button_ctocpp.cc',
+      'libcef_dll/ctocpp/views/button_ctocpp.h',
+      'libcef_dll/cpptoc/views/button_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/views/button_delegate_cpptoc.h',
+      'libcef_dll/ctocpp/callback_ctocpp.cc',
+      'libcef_dll/ctocpp/callback_ctocpp.h',
+      'libcef_dll/cpptoc/client_cpptoc.cc',
+      'libcef_dll/cpptoc/client_cpptoc.h',
+      'libcef_dll/ctocpp/command_line_ctocpp.cc',
+      'libcef_dll/ctocpp/command_line_ctocpp.h',
+      'libcef_dll/cpptoc/completion_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/completion_callback_cpptoc.h',
+      'libcef_dll/cpptoc/context_menu_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/context_menu_handler_cpptoc.h',
+      'libcef_dll/ctocpp/context_menu_params_ctocpp.cc',
+      'libcef_dll/ctocpp/context_menu_params_ctocpp.h',
+      'libcef_dll/cpptoc/cookie_access_filter_cpptoc.cc',
+      'libcef_dll/cpptoc/cookie_access_filter_cpptoc.h',
+      'libcef_dll/ctocpp/cookie_manager_ctocpp.cc',
+      'libcef_dll/ctocpp/cookie_manager_ctocpp.h',
+      'libcef_dll/cpptoc/cookie_visitor_cpptoc.cc',
+      'libcef_dll/cpptoc/cookie_visitor_cpptoc.h',
+      'libcef_dll/ctocpp/domdocument_ctocpp.cc',
+      'libcef_dll/ctocpp/domdocument_ctocpp.h',
+      'libcef_dll/ctocpp/domnode_ctocpp.cc',
+      'libcef_dll/ctocpp/domnode_ctocpp.h',
+      'libcef_dll/cpptoc/domvisitor_cpptoc.cc',
+      'libcef_dll/cpptoc/domvisitor_cpptoc.h',
+      'libcef_dll/cpptoc/delete_cookies_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h',
+      'libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.cc',
+      'libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h',
+      'libcef_dll/cpptoc/dialog_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/dialog_handler_cpptoc.h',
+      'libcef_dll/ctocpp/dictionary_value_ctocpp.cc',
+      'libcef_dll/ctocpp/dictionary_value_ctocpp.h',
+      'libcef_dll/ctocpp/views/display_ctocpp.cc',
+      'libcef_dll/ctocpp/views/display_ctocpp.h',
+      'libcef_dll/cpptoc/display_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/display_handler_cpptoc.h',
+      'libcef_dll/cpptoc/download_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/download_handler_cpptoc.h',
+      'libcef_dll/cpptoc/download_image_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/download_image_callback_cpptoc.h',
+      'libcef_dll/ctocpp/download_item_ctocpp.cc',
+      'libcef_dll/ctocpp/download_item_ctocpp.h',
+      'libcef_dll/ctocpp/download_item_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/download_item_callback_ctocpp.h',
+      'libcef_dll/ctocpp/drag_data_ctocpp.cc',
+      'libcef_dll/ctocpp/drag_data_ctocpp.h',
+      'libcef_dll/cpptoc/drag_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/drag_handler_cpptoc.h',
+      'libcef_dll/cpptoc/end_tracing_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/end_tracing_callback_cpptoc.h',
+      'libcef_dll/ctocpp/extension_ctocpp.cc',
+      'libcef_dll/ctocpp/extension_ctocpp.h',
+      'libcef_dll/cpptoc/extension_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/extension_handler_cpptoc.h',
+      'libcef_dll/ctocpp/file_dialog_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/file_dialog_callback_ctocpp.h',
+      'libcef_dll/ctocpp/views/fill_layout_ctocpp.cc',
+      'libcef_dll/ctocpp/views/fill_layout_ctocpp.h',
+      'libcef_dll/cpptoc/find_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/find_handler_cpptoc.h',
+      'libcef_dll/cpptoc/focus_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/focus_handler_cpptoc.h',
+      'libcef_dll/ctocpp/frame_ctocpp.cc',
+      'libcef_dll/ctocpp/frame_ctocpp.h',
+      'libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.h',
+      'libcef_dll/ctocpp/image_ctocpp.cc',
+      'libcef_dll/ctocpp/image_ctocpp.h',
+      'libcef_dll/ctocpp/jsdialog_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/jsdialog_callback_ctocpp.h',
+      'libcef_dll/cpptoc/jsdialog_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/jsdialog_handler_cpptoc.h',
+      'libcef_dll/cpptoc/keyboard_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/keyboard_handler_cpptoc.h',
+      'libcef_dll/ctocpp/views/label_button_ctocpp.cc',
+      'libcef_dll/ctocpp/views/label_button_ctocpp.h',
+      'libcef_dll/ctocpp/views/layout_ctocpp.cc',
+      'libcef_dll/ctocpp/views/layout_ctocpp.h',
+      'libcef_dll/cpptoc/life_span_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/life_span_handler_cpptoc.h',
+      'libcef_dll/ctocpp/list_value_ctocpp.cc',
+      'libcef_dll/ctocpp/list_value_ctocpp.h',
+      'libcef_dll/cpptoc/load_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/load_handler_cpptoc.h',
+      'libcef_dll/cpptoc/media_observer_cpptoc.cc',
+      'libcef_dll/cpptoc/media_observer_cpptoc.h',
+      'libcef_dll/ctocpp/media_route_ctocpp.cc',
+      'libcef_dll/ctocpp/media_route_ctocpp.h',
+      'libcef_dll/cpptoc/media_route_create_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/media_route_create_callback_cpptoc.h',
+      'libcef_dll/ctocpp/media_router_ctocpp.cc',
+      'libcef_dll/ctocpp/media_router_ctocpp.h',
+      'libcef_dll/ctocpp/media_sink_ctocpp.cc',
+      'libcef_dll/ctocpp/media_sink_ctocpp.h',
+      'libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.h',
+      'libcef_dll/ctocpp/media_source_ctocpp.cc',
+      'libcef_dll/ctocpp/media_source_ctocpp.h',
+      'libcef_dll/ctocpp/views/menu_button_ctocpp.cc',
+      'libcef_dll/ctocpp/views/menu_button_ctocpp.h',
+      'libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h',
+      'libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.cc',
+      'libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.h',
+      'libcef_dll/ctocpp/menu_model_ctocpp.cc',
+      'libcef_dll/ctocpp/menu_model_ctocpp.h',
+      'libcef_dll/cpptoc/menu_model_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/menu_model_delegate_cpptoc.h',
+      'libcef_dll/ctocpp/navigation_entry_ctocpp.cc',
+      'libcef_dll/ctocpp/navigation_entry_ctocpp.h',
+      'libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.cc',
+      'libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h',
+      'libcef_dll/ctocpp/views/panel_ctocpp.cc',
+      'libcef_dll/ctocpp/views/panel_ctocpp.h',
+      'libcef_dll/cpptoc/views/panel_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/views/panel_delegate_cpptoc.h',
+      'libcef_dll/cpptoc/pdf_print_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/pdf_print_callback_cpptoc.h',
+      'libcef_dll/ctocpp/post_data_ctocpp.cc',
+      'libcef_dll/ctocpp/post_data_ctocpp.h',
+      'libcef_dll/ctocpp/post_data_element_ctocpp.cc',
+      'libcef_dll/ctocpp/post_data_element_ctocpp.h',
+      'libcef_dll/ctocpp/print_dialog_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/print_dialog_callback_ctocpp.h',
+      'libcef_dll/cpptoc/print_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/print_handler_cpptoc.h',
+      'libcef_dll/ctocpp/print_job_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/print_job_callback_ctocpp.h',
+      'libcef_dll/ctocpp/print_settings_ctocpp.cc',
+      'libcef_dll/ctocpp/print_settings_ctocpp.h',
+      'libcef_dll/ctocpp/process_message_ctocpp.cc',
+      'libcef_dll/ctocpp/process_message_ctocpp.h',
+      'libcef_dll/cpptoc/read_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/read_handler_cpptoc.h',
+      'libcef_dll/cpptoc/register_cdm_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/register_cdm_callback_cpptoc.h',
+      'libcef_dll/ctocpp/registration_ctocpp.cc',
+      'libcef_dll/ctocpp/registration_ctocpp.h',
+      'libcef_dll/cpptoc/render_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/render_handler_cpptoc.h',
+      'libcef_dll/cpptoc/render_process_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/render_process_handler_cpptoc.h',
+      'libcef_dll/ctocpp/request_ctocpp.cc',
+      'libcef_dll/ctocpp/request_ctocpp.h',
+      'libcef_dll/ctocpp/request_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/request_callback_ctocpp.h',
+      'libcef_dll/ctocpp/request_context_ctocpp.cc',
+      'libcef_dll/ctocpp/request_context_ctocpp.h',
+      'libcef_dll/cpptoc/request_context_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/request_context_handler_cpptoc.h',
+      'libcef_dll/cpptoc/request_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/request_handler_cpptoc.h',
+      'libcef_dll/cpptoc/resolve_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/resolve_callback_cpptoc.h',
+      'libcef_dll/ctocpp/resource_bundle_ctocpp.cc',
+      'libcef_dll/ctocpp/resource_bundle_ctocpp.h',
+      'libcef_dll/cpptoc/resource_bundle_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/resource_bundle_handler_cpptoc.h',
+      'libcef_dll/cpptoc/resource_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/resource_handler_cpptoc.h',
+      'libcef_dll/ctocpp/resource_read_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/resource_read_callback_ctocpp.h',
+      'libcef_dll/cpptoc/resource_request_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/resource_request_handler_cpptoc.h',
+      'libcef_dll/ctocpp/resource_skip_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/resource_skip_callback_ctocpp.h',
+      'libcef_dll/ctocpp/response_ctocpp.cc',
+      'libcef_dll/ctocpp/response_ctocpp.h',
+      'libcef_dll/cpptoc/response_filter_cpptoc.cc',
+      'libcef_dll/cpptoc/response_filter_cpptoc.h',
+      'libcef_dll/ctocpp/run_context_menu_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/run_context_menu_callback_ctocpp.h',
+      'libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h',
+      'libcef_dll/ctocpp/sslinfo_ctocpp.cc',
+      'libcef_dll/ctocpp/sslinfo_ctocpp.h',
+      'libcef_dll/ctocpp/sslstatus_ctocpp.cc',
+      'libcef_dll/ctocpp/sslstatus_ctocpp.h',
+      'libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc',
+      'libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h',
+      'libcef_dll/ctocpp/scheme_registrar_ctocpp.cc',
+      'libcef_dll/ctocpp/scheme_registrar_ctocpp.h',
+      'libcef_dll/ctocpp/views/scroll_view_ctocpp.cc',
+      'libcef_dll/ctocpp/views/scroll_view_ctocpp.h',
+      'libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.cc',
+      'libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h',
+      'libcef_dll/ctocpp/server_ctocpp.cc',
+      'libcef_dll/ctocpp/server_ctocpp.h',
+      'libcef_dll/cpptoc/server_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/server_handler_cpptoc.h',
+      'libcef_dll/cpptoc/set_cookie_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/set_cookie_callback_cpptoc.h',
+      'libcef_dll/ctocpp/stream_reader_ctocpp.cc',
+      'libcef_dll/ctocpp/stream_reader_ctocpp.h',
+      'libcef_dll/ctocpp/stream_writer_ctocpp.cc',
+      'libcef_dll/ctocpp/stream_writer_ctocpp.h',
+      'libcef_dll/cpptoc/string_visitor_cpptoc.cc',
+      'libcef_dll/cpptoc/string_visitor_cpptoc.h',
+      'libcef_dll/cpptoc/task_cpptoc.cc',
+      'libcef_dll/cpptoc/task_cpptoc.h',
+      'libcef_dll/ctocpp/task_runner_ctocpp.cc',
+      'libcef_dll/ctocpp/task_runner_ctocpp.h',
+      'libcef_dll/ctocpp/views/textfield_ctocpp.cc',
+      'libcef_dll/ctocpp/views/textfield_ctocpp.h',
+      'libcef_dll/cpptoc/views/textfield_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h',
+      'libcef_dll/ctocpp/thread_ctocpp.cc',
+      'libcef_dll/ctocpp/thread_ctocpp.h',
+      'libcef_dll/ctocpp/test/translator_test_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_ctocpp.h',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.h',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.h',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.h',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.h',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.h',
+      'libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.h',
+      'libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.cc',
+      'libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.h',
+      'libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.h',
+      'libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.h',
+      'libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.cc',
+      'libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.h',
+      'libcef_dll/ctocpp/urlrequest_ctocpp.cc',
+      'libcef_dll/ctocpp/urlrequest_ctocpp.h',
+      'libcef_dll/cpptoc/urlrequest_client_cpptoc.cc',
+      'libcef_dll/cpptoc/urlrequest_client_cpptoc.h',
+      'libcef_dll/cpptoc/v8accessor_cpptoc.cc',
+      'libcef_dll/cpptoc/v8accessor_cpptoc.h',
+      'libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.h',
+      'libcef_dll/ctocpp/v8context_ctocpp.cc',
+      'libcef_dll/ctocpp/v8context_ctocpp.h',
+      'libcef_dll/ctocpp/v8exception_ctocpp.cc',
+      'libcef_dll/ctocpp/v8exception_ctocpp.h',
+      'libcef_dll/cpptoc/v8handler_cpptoc.cc',
+      'libcef_dll/cpptoc/v8handler_cpptoc.h',
+      'libcef_dll/cpptoc/v8interceptor_cpptoc.cc',
+      'libcef_dll/cpptoc/v8interceptor_cpptoc.h',
+      'libcef_dll/ctocpp/v8stack_frame_ctocpp.cc',
+      'libcef_dll/ctocpp/v8stack_frame_ctocpp.h',
+      'libcef_dll/ctocpp/v8stack_trace_ctocpp.cc',
+      'libcef_dll/ctocpp/v8stack_trace_ctocpp.h',
+      'libcef_dll/ctocpp/v8value_ctocpp.cc',
+      'libcef_dll/ctocpp/v8value_ctocpp.h',
+      'libcef_dll/ctocpp/value_ctocpp.cc',
+      'libcef_dll/ctocpp/value_ctocpp.h',
+      'libcef_dll/ctocpp/views/view_ctocpp.cc',
+      'libcef_dll/ctocpp/views/view_ctocpp.h',
+      'libcef_dll/cpptoc/views/view_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/views/view_delegate_cpptoc.h',
+      'libcef_dll/ctocpp/waitable_event_ctocpp.cc',
+      'libcef_dll/ctocpp/waitable_event_ctocpp.h',
+      'libcef_dll/ctocpp/web_plugin_info_ctocpp.cc',
+      'libcef_dll/ctocpp/web_plugin_info_ctocpp.h',
+      'libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.cc',
+      'libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.h',
+      'libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.cc',
+      'libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.h',
+      'libcef_dll/ctocpp/views/window_ctocpp.cc',
+      'libcef_dll/ctocpp/views/window_ctocpp.h',
+      'libcef_dll/cpptoc/views/window_delegate_cpptoc.cc',
+      'libcef_dll/cpptoc/views/window_delegate_cpptoc.h',
+      'libcef_dll/cpptoc/write_handler_cpptoc.cc',
+      'libcef_dll/cpptoc/write_handler_cpptoc.h',
+      'libcef_dll/ctocpp/x509cert_principal_ctocpp.cc',
+      'libcef_dll/ctocpp/x509cert_principal_ctocpp.h',
+      'libcef_dll/ctocpp/x509certificate_ctocpp.cc',
+      'libcef_dll/ctocpp/x509certificate_ctocpp.h',
+      'libcef_dll/ctocpp/xml_reader_ctocpp.cc',
+      'libcef_dll/ctocpp/xml_reader_ctocpp.h',
+      'libcef_dll/ctocpp/zip_reader_ctocpp.cc',
+      'libcef_dll/ctocpp/zip_reader_ctocpp.h',
+    ],
+  },
+}
diff --git a/src/cef_paths2.gypi b/src/cef_paths2.gypi
new file mode 100644
index 0000000..0d5d3c3
--- /dev/null
+++ b/src/cef_paths2.gypi
@@ -0,0 +1,610 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+{
+  'variables': {
+    'includes_common': [
+      'include/base/cef_atomic_ref_count.h',
+      'include/base/cef_atomicops.h',
+      'include/base/cef_basictypes.h',
+      'include/base/cef_bind.h',
+      'include/base/cef_bind_helpers.h',
+      'include/base/cef_build.h',
+      'include/base/cef_callback.h',
+      'include/base/cef_callback_forward.h',
+      'include/base/cef_callback_helpers.h',
+      'include/base/cef_callback_list.h',
+      'include/base/cef_cancelable_callback.h',
+      'include/base/cef_lock.h',
+      'include/base/cef_logging.h',
+      'include/base/cef_macros.h',
+      'include/base/cef_move.h',
+      'include/base/cef_platform_thread.h',
+      'include/base/cef_ref_counted.h',
+      'include/base/cef_scoped_ptr.h',
+      'include/base/cef_string16.h',
+      'include/base/cef_template_util.h',
+      'include/base/cef_thread_checker.h',
+      'include/base/cef_trace_event.h',
+      'include/base/cef_tuple.h',
+      'include/base/cef_weak_ptr.h',
+      'include/base/internal/cef_bind_internal.h',
+      'include/base/internal/cef_callback_internal.h',
+      'include/base/internal/cef_lock_impl.h',
+      'include/base/internal/cef_raw_scoped_refptr_mismatch_checker.h',
+      'include/base/internal/cef_thread_checker_impl.h',
+      'include/cef_api_hash.h',
+      'include/cef_base.h',
+      'include/cef_config.h',
+      'include/cef_version.h',
+      'include/internal/cef_export.h',
+      'include/internal/cef_ptr.h',
+      'include/internal/cef_string_wrappers.h',
+      'include/internal/cef_types_wrappers.h',
+    ],
+    'includes_common_capi': [
+      'include/internal/cef_logging_internal.h',
+      'include/internal/cef_string.h',
+      'include/internal/cef_string_list.h',
+      'include/internal/cef_string_map.h',
+      'include/internal/cef_string_multimap.h',
+      'include/internal/cef_string_types.h',
+      'include/internal/cef_thread_internal.h',
+      'include/internal/cef_time.h',
+      'include/internal/cef_trace_event_internal.h',
+      'include/internal/cef_types.h',
+    ],
+    'includes_capi': [
+      'include/capi/cef_base_capi.h',
+    ],
+    'includes_wrapper': [
+      'include/wrapper/cef_byte_read_handler.h',
+      'include/wrapper/cef_closure_task.h',
+      'include/wrapper/cef_helpers.h',
+      'include/wrapper/cef_message_router.h',
+      'include/wrapper/cef_resource_manager.h',
+      'include/wrapper/cef_scoped_temp_dir.h',
+      'include/wrapper/cef_stream_resource_handler.h',
+      'include/wrapper/cef_xml_object.h',
+      'include/wrapper/cef_zip_archive.h',
+    ],
+    'includes_wrapper_mac': [
+      'include/wrapper/cef_library_loader.h',
+    ],
+    'includes_win': [
+      'include/base/internal/cef_atomicops_arm64_msvc.h',
+      'include/base/internal/cef_atomicops_x86_msvc.h',
+      'include/base/internal/cef_bind_internal_win.h',
+      'include/cef_sandbox_win.h',
+      'include/internal/cef_win.h',
+    ],
+    'includes_win_capi': [
+      'include/internal/cef_types_win.h',
+    ],
+    'includes_mac': [
+      'include/base/internal/cef_atomicops_atomicword_compat.h',
+      'include/base/internal/cef_atomicops_mac.h',
+      'include/cef_application_mac.h',
+      'include/cef_sandbox_mac.h',
+      'include/internal/cef_mac.h',
+    ],
+    'includes_mac_capi': [
+      'include/internal/cef_types_mac.h',
+    ],
+    'includes_linux': [
+      'include/base/internal/cef_atomicops_atomicword_compat.h',
+      'include/base/internal/cef_atomicops_arm_gcc.h',
+      'include/base/internal/cef_atomicops_arm64_gcc.h',
+      'include/base/internal/cef_atomicops_x86_gcc.h',
+      'include/internal/cef_linux.h',
+    ],
+    'includes_linux_capi': [
+      'include/internal/cef_types_linux.h',
+    ],
+    'libcef_sources_common': [
+      'libcef_dll/cpptoc/cpptoc_ref_counted.h',
+      'libcef_dll/cpptoc/cpptoc_scoped.h',
+      'libcef_dll/ctocpp/base_ref_counted_ctocpp.cc',
+      'libcef_dll/ctocpp/base_ref_counted_ctocpp.h',
+      'libcef_dll/ctocpp/base_scoped_ctocpp.cc',
+      'libcef_dll/ctocpp/base_scoped_ctocpp.h',
+      'libcef_dll/ctocpp/ctocpp_ref_counted.h',
+      'libcef_dll/ctocpp/ctocpp_scoped.h',
+      'libcef_dll/libcef_dll.cc',
+      'libcef_dll/libcef_dll2.cc',
+      'libcef_dll/ptr_util.h',
+      'libcef_dll/resource.h',
+      'libcef_dll/shutdown_checker.cc',
+      'libcef_dll/shutdown_checker.h',
+      'libcef_dll/transfer_util.cc',
+      'libcef_dll/transfer_util.h',
+      'libcef_dll/wrapper_types.h',
+    ],
+    'libcef_dll_wrapper_sources_base': [
+      'libcef_dll/base/cef_atomicops_x86_gcc.cc',
+      'libcef_dll/base/cef_bind_helpers.cc',
+      'libcef_dll/base/cef_callback_helpers.cc',
+      'libcef_dll/base/cef_callback_internal.cc',
+      'libcef_dll/base/cef_lock.cc',
+      'libcef_dll/base/cef_lock_impl.cc',
+      'libcef_dll/base/cef_logging.cc',
+      'libcef_dll/base/cef_ref_counted.cc',
+      'libcef_dll/base/cef_string16.cc',
+      'libcef_dll/base/cef_thread_checker_impl.cc',
+      'libcef_dll/base/cef_weak_ptr.cc',
+    ],
+    'libcef_dll_wrapper_sources_common': [
+      'libcef_dll/cpptoc/base_ref_counted_cpptoc.cc',
+      'libcef_dll/cpptoc/base_ref_counted_cpptoc.h',
+      'libcef_dll/cpptoc/base_scoped_cpptoc.cc',
+      'libcef_dll/cpptoc/base_scoped_cpptoc.h',
+      'libcef_dll/cpptoc/cpptoc_ref_counted.h',
+      'libcef_dll/cpptoc/cpptoc_scoped.h',
+      'libcef_dll/ctocpp/ctocpp_ref_counted.h',
+      'libcef_dll/ctocpp/ctocpp_scoped.h',
+      'libcef_dll/ptr_util.h',
+      'libcef_dll/shutdown_checker.cc',
+      'libcef_dll/shutdown_checker.h',
+      'libcef_dll/transfer_util.cc',
+      'libcef_dll/transfer_util.h',
+      'libcef_dll/wrapper_types.h',
+      'libcef_dll/wrapper/cef_browser_info_map.h',
+      'libcef_dll/wrapper/cef_byte_read_handler.cc',
+      'libcef_dll/wrapper/cef_closure_task.cc',
+      'libcef_dll/wrapper/cef_message_router.cc',
+      'libcef_dll/wrapper/cef_resource_manager.cc',
+      'libcef_dll/wrapper/cef_scoped_temp_dir.cc',
+      'libcef_dll/wrapper/cef_stream_resource_handler.cc',
+      'libcef_dll/wrapper/cef_xml_object.cc',
+      'libcef_dll/wrapper/cef_zip_archive.cc',
+      'libcef_dll/wrapper/libcef_dll_wrapper.cc',
+      'libcef_dll/wrapper/libcef_dll_wrapper2.cc',
+    ],
+    'libcef_dll_wrapper_sources_mac': [
+      'libcef_dll/wrapper/cef_library_loader_mac.mm',
+      'libcef_dll/wrapper/libcef_dll_dylib.cc',
+    ],
+    'shared_sources_browser': [
+      'tests/shared/browser/client_app_browser.cc',
+      'tests/shared/browser/client_app_browser.h',
+      'tests/shared/browser/extension_util.cc',
+      'tests/shared/browser/extension_util.h',
+      'tests/shared/browser/file_util.cc',
+      'tests/shared/browser/file_util.h',
+      'tests/shared/browser/geometry_util.cc',
+      'tests/shared/browser/geometry_util.h',
+      'tests/shared/browser/main_message_loop.cc',
+      'tests/shared/browser/main_message_loop.h',
+      'tests/shared/browser/main_message_loop_external_pump.cc',
+      'tests/shared/browser/main_message_loop_external_pump.h',
+      'tests/shared/browser/main_message_loop_std.cc',
+      'tests/shared/browser/main_message_loop_std.h',
+      'tests/shared/browser/resource_util.h',
+    ],
+    'shared_sources_common': [
+      'tests/shared/common/client_app.cc',
+      'tests/shared/common/client_app.h',
+      'tests/shared/common/client_app_other.cc',
+      'tests/shared/common/client_app_other.h',
+      'tests/shared/common/client_switches.cc',
+      'tests/shared/common/client_switches.h',
+    ],
+    'shared_sources_renderer': [
+      'tests/shared/renderer/client_app_renderer.cc',
+      'tests/shared/renderer/client_app_renderer.h',
+    ],
+    'shared_sources_resources': [
+      'tests/shared/resources/osr_test.html',
+      'tests/shared/resources/pdf.html',
+      'tests/shared/resources/pdf.pdf',
+      'tests/shared/resources/window_icon.1x.png',
+      'tests/shared/resources/window_icon.2x.png',
+    ],
+    'shared_sources_linux': [
+      'tests/shared/browser/main_message_loop_external_pump_linux.cc',
+      'tests/shared/browser/resource_util_posix.cc',
+    ],
+    'shared_sources_mac': [
+      'tests/shared/browser/main_message_loop_external_pump_mac.mm',
+      'tests/shared/browser/resource_util_mac.mm',
+      'tests/shared/browser/resource_util_posix.cc',
+    ],
+    'shared_sources_mac_helper': [
+      'tests/shared/process_helper_mac.cc',
+    ],
+    'shared_sources_win': [
+      'tests/shared/browser/main_message_loop_external_pump_win.cc',
+      'tests/shared/browser/resource_util_win.cc',
+      'tests/shared/browser/util_win.cc',
+      'tests/shared/browser/util_win.h',
+    ],
+    'cefclient_sources_browser': [
+      'tests/cefclient/browser/binding_test.cc',
+      'tests/cefclient/browser/binding_test.h',
+      'tests/cefclient/browser/browser_window.cc',
+      'tests/cefclient/browser/browser_window.h',
+      'tests/cefclient/browser/bytes_write_handler.cc',
+      'tests/cefclient/browser/bytes_write_handler.h',
+      'tests/cefclient/browser/client_app_delegates_browser.cc',
+      'tests/cefclient/browser/client_browser.cc',
+      'tests/cefclient/browser/client_browser.h',
+      'tests/cefclient/browser/client_handler.cc',
+      'tests/cefclient/browser/client_handler.h',
+      'tests/cefclient/browser/client_handler_osr.cc',
+      'tests/cefclient/browser/client_handler_osr.h',
+      'tests/cefclient/browser/client_handler_std.cc',
+      'tests/cefclient/browser/client_handler_std.h',
+      'tests/cefclient/browser/client_types.h',
+      'tests/cefclient/browser/dialog_test.cc',
+      'tests/cefclient/browser/dialog_test.h',
+      'tests/cefclient/browser/drm_test.cc',
+      'tests/cefclient/browser/drm_test.h',
+      'tests/cefclient/browser/image_cache.cc',
+      'tests/cefclient/browser/image_cache.h',
+      'tests/cefclient/browser/main_context.cc',
+      'tests/cefclient/browser/main_context.h',
+      'tests/cefclient/browser/main_context_impl.cc',
+      'tests/cefclient/browser/main_context_impl.h',
+      'tests/cefclient/browser/media_router_test.cc',
+      'tests/cefclient/browser/media_router_test.h',
+      'tests/cefclient/browser/osr_dragdrop_events.h',
+      'tests/cefclient/browser/osr_renderer.h',
+      'tests/cefclient/browser/osr_renderer.cc',
+      'tests/cefclient/browser/osr_renderer_settings.h',
+      'tests/cefclient/browser/preferences_test.cc',
+      'tests/cefclient/browser/preferences_test.h',
+      'tests/cefclient/browser/resource.h',
+      'tests/cefclient/browser/response_filter_test.cc',
+      'tests/cefclient/browser/response_filter_test.h',
+      'tests/cefclient/browser/root_window.cc',
+      'tests/cefclient/browser/root_window.h',
+      'tests/cefclient/browser/root_window_create.cc',
+      'tests/cefclient/browser/root_window_manager.cc',
+      'tests/cefclient/browser/root_window_manager.h',
+      'tests/cefclient/browser/scheme_test.cc',
+      'tests/cefclient/browser/scheme_test.h',
+      'tests/cefclient/browser/server_test.cc',
+      'tests/cefclient/browser/server_test.h',
+      'tests/cefclient/browser/temp_window.h',
+      'tests/cefclient/browser/test_runner.cc',
+      'tests/cefclient/browser/test_runner.h',
+      'tests/cefclient/browser/urlrequest_test.cc',
+      'tests/cefclient/browser/urlrequest_test.h',
+      'tests/cefclient/browser/window_test.cc',
+      'tests/cefclient/browser/window_test.h',
+      'tests/cefclient/browser/window_test_runner.cc',
+      'tests/cefclient/browser/window_test_runner.h',
+    ],
+    'cefclient_sources_common': [
+      'tests/cefclient/common/client_app_delegates_common.cc',
+      'tests/cefclient/common/scheme_test_common.cc',
+      'tests/cefclient/common/scheme_test_common.h',
+    ],
+    'cefclient_sources_renderer': [
+      'tests/cefclient/renderer/client_app_delegates_renderer.cc',
+      'tests/cefclient/renderer/client_renderer.cc',
+      'tests/cefclient/renderer/client_renderer.h',
+      'tests/cefclient/renderer/performance_test.cc',
+      'tests/cefclient/renderer/performance_test.h',
+      'tests/cefclient/renderer/performance_test_setup.h',
+      'tests/cefclient/renderer/performance_test_tests.cc',
+    ],
+    'cefclient_sources_resources': [
+      'tests/cefclient/resources/binding.html',
+      'tests/cefclient/resources/dialogs.html',
+      'tests/cefclient/resources/draggable.html',
+      'tests/cefclient/resources/drm.html',
+      'tests/cefclient/resources/localstorage.html',
+      'tests/cefclient/resources/logo.png',
+      'tests/cefclient/resources/media_router.html',
+      'tests/cefclient/resources/menu_icon.1x.png',
+      'tests/cefclient/resources/menu_icon.2x.png',
+      'tests/cefclient/resources/other_tests.html',
+      'tests/cefclient/resources/performance.html',
+      'tests/cefclient/resources/performance2.html',
+      'tests/cefclient/resources/preferences.html',
+      'tests/cefclient/resources/response_filter.html',
+      'tests/cefclient/resources/server.html',
+      'tests/cefclient/resources/transparency.html',
+      'tests/cefclient/resources/urlrequest.html',
+      'tests/cefclient/resources/websocket.html',
+      'tests/cefclient/resources/window.html',
+      'tests/cefclient/resources/xmlhttprequest.html',
+    ],
+    'cefclient_sources_resources_extensions_set_page_color': [
+      'tests/cefclient/resources/extensions/set_page_color/icon.png',
+      'tests/cefclient/resources/extensions/set_page_color/manifest.json',
+      'tests/cefclient/resources/extensions/set_page_color/popup.html',
+      'tests/cefclient/resources/extensions/set_page_color/popup.js',
+      'tests/cefclient/resources/extensions/set_page_color/README.md',
+    ],
+    'cefclient_sources_win': [
+      'tests/cefclient/browser/browser_window_osr_win.cc',
+      'tests/cefclient/browser/browser_window_osr_win.h',
+      'tests/cefclient/browser/browser_window_std_win.cc',
+      'tests/cefclient/browser/browser_window_std_win.h',
+      'tests/cefclient/browser/main_context_impl_win.cc',
+      'tests/cefclient/browser/main_message_loop_multithreaded_win.cc',
+      'tests/cefclient/browser/main_message_loop_multithreaded_win.h',
+      'tests/cefclient/browser/osr_accessibility_helper.cc',
+      'tests/cefclient/browser/osr_accessibility_helper.h',
+      'tests/cefclient/browser/osr_accessibility_node.cc',
+      'tests/cefclient/browser/osr_accessibility_node.h',
+      'tests/cefclient/browser/osr_accessibility_node_win.cc',
+      'tests/cefclient/browser/osr_dragdrop_win.cc',
+      'tests/cefclient/browser/osr_dragdrop_win.h',
+      'tests/cefclient/browser/osr_ime_handler_win.cc',
+      'tests/cefclient/browser/osr_ime_handler_win.h',
+      'tests/cefclient/browser/osr_d3d11_win.cc',
+      'tests/cefclient/browser/osr_d3d11_win.h',
+      'tests/cefclient/browser/osr_render_handler_win.cc',
+      'tests/cefclient/browser/osr_render_handler_win.h',
+      'tests/cefclient/browser/osr_render_handler_win_d3d11.cc',
+      'tests/cefclient/browser/osr_render_handler_win_d3d11.h',
+      'tests/cefclient/browser/osr_render_handler_win_gl.cc',
+      'tests/cefclient/browser/osr_render_handler_win_gl.h',
+      'tests/cefclient/browser/osr_window_win.cc',
+      'tests/cefclient/browser/osr_window_win.h',
+      'tests/cefclient/browser/resource_util_win_idmap.cc',
+      'tests/cefclient/browser/root_window_views.cc',
+      'tests/cefclient/browser/root_window_views.h',
+      'tests/cefclient/browser/root_window_win.cc',
+      'tests/cefclient/browser/root_window_win.h',
+      'tests/cefclient/browser/temp_window_win.cc',
+      'tests/cefclient/browser/temp_window_win.h',
+      'tests/cefclient/browser/views_menu_bar.cc',
+      'tests/cefclient/browser/views_menu_bar.h',
+      'tests/cefclient/browser/views_style.cc',
+      'tests/cefclient/browser/views_style.h',
+      'tests/cefclient/browser/views_window.cc',
+      'tests/cefclient/browser/views_window.h',
+      'tests/cefclient/browser/window_test_runner_views.cc',
+      'tests/cefclient/browser/window_test_runner_views.h',
+      'tests/cefclient/browser/window_test_runner_win.cc',
+      'tests/cefclient/browser/window_test_runner_win.h',
+      'tests/cefclient/cefclient_win.cc',
+      'tests/cefclient/resources/win/cefclient.rc',
+    ],
+    'cefclient_sources_resources_win': [
+      'tests/cefclient/resources/win/cefclient.exe.manifest',
+      'tests/cefclient/resources/win/cefclient.ico',
+      'tests/cefclient/resources/win/small.ico',
+    ],
+    'cefclient_sources_mac': [
+      'tests/cefclient/browser/browser_window_osr_mac.h',
+      'tests/cefclient/browser/browser_window_osr_mac.mm',
+      'tests/cefclient/browser/browser_window_std_mac.h',
+      'tests/cefclient/browser/browser_window_std_mac.mm',
+      'tests/cefclient/browser/main_context_impl_posix.cc',
+      'tests/cefclient/browser/osr_accessibility_helper.cc',
+      'tests/cefclient/browser/osr_accessibility_helper.h',
+      'tests/cefclient/browser/osr_accessibility_node.cc',
+      'tests/cefclient/browser/osr_accessibility_node.h',
+      'tests/cefclient/browser/osr_accessibility_node_mac.mm',
+      'tests/cefclient/browser/root_window_mac.h',
+      'tests/cefclient/browser/root_window_mac.mm',
+      'tests/cefclient/browser/temp_window_mac.h',
+      'tests/cefclient/browser/temp_window_mac.mm',
+      'tests/cefclient/browser/text_input_client_osr_mac.h',
+      'tests/cefclient/browser/text_input_client_osr_mac.mm',
+      'tests/cefclient/browser/window_test_runner_mac.h',
+      'tests/cefclient/browser/window_test_runner_mac.mm',
+      'tests/cefclient/cefclient_mac.mm',
+   ],
+    'cefclient_bundle_resources_mac': [
+      'tests/cefclient/resources/mac/cefclient.icns',
+      'tests/cefclient/resources/mac/English.lproj/InfoPlist.strings',
+      'tests/cefclient/resources/mac/English.lproj/MainMenu.xib',
+      'tests/cefclient/resources/mac/Info.plist',
+    ],
+    'cefclient_sources_linux': [
+      'tests/cefclient/browser/browser_window_osr_gtk.cc',
+      'tests/cefclient/browser/browser_window_osr_gtk.h',
+      'tests/cefclient/browser/browser_window_std_gtk.cc',
+      'tests/cefclient/browser/browser_window_std_gtk.h',
+      'tests/cefclient/browser/dialog_handler_gtk.cc',
+      'tests/cefclient/browser/dialog_handler_gtk.h',
+      'tests/cefclient/browser/main_context_impl_posix.cc',
+      'tests/cefclient/browser/main_message_loop_multithreaded_gtk.cc',
+      'tests/cefclient/browser/main_message_loop_multithreaded_gtk.h',
+      'tests/cefclient/browser/print_handler_gtk.cc',
+      'tests/cefclient/browser/print_handler_gtk.h',
+      'tests/cefclient/browser/resource_util_linux.cc',
+      'tests/cefclient/browser/root_window_gtk.cc',
+      'tests/cefclient/browser/root_window_gtk.h',
+      'tests/cefclient/browser/root_window_views.cc',
+      'tests/cefclient/browser/root_window_views.h',
+      'tests/cefclient/browser/temp_window_x11.cc',
+      'tests/cefclient/browser/temp_window_x11.h',
+      'tests/cefclient/browser/util_gtk.cc',
+      'tests/cefclient/browser/util_gtk.h',
+      'tests/cefclient/browser/views_menu_bar.cc',
+      'tests/cefclient/browser/views_menu_bar.h',
+      'tests/cefclient/browser/views_style.cc',
+      'tests/cefclient/browser/views_style.h',
+      'tests/cefclient/browser/views_window.cc',
+      'tests/cefclient/browser/views_window.h',
+      'tests/cefclient/browser/window_test_runner_gtk.cc',
+      'tests/cefclient/browser/window_test_runner_gtk.h',
+      'tests/cefclient/browser/window_test_runner_views.cc',
+      'tests/cefclient/browser/window_test_runner_views.h',
+      'tests/cefclient/cefclient_gtk.cc',
+    ],
+    'cefsimple_sources_common': [
+      'tests/cefsimple/simple_app.cc',
+      'tests/cefsimple/simple_app.h',
+      'tests/cefsimple/simple_handler.cc',
+      'tests/cefsimple/simple_handler.h',
+    ],
+    'cefsimple_sources_win': [
+      'tests/cefsimple/cefsimple.rc',
+      'tests/cefsimple/cefsimple_win.cc',
+      'tests/cefsimple/simple_handler_win.cc',
+      'tests/cefsimple/resource.h',
+    ],
+    'cefsimple_sources_resources_win': [
+      'tests/cefsimple/cefsimple.exe.manifest',
+      'tests/cefsimple/res/cefsimple.ico',
+      'tests/cefsimple/res/small.ico',
+    ],
+    'cefsimple_sources_mac': [
+      'tests/cefsimple/cefsimple_mac.mm',
+      'tests/cefsimple/simple_handler_mac.mm',
+    ],
+    'cefsimple_sources_mac_helper': [
+      'tests/cefsimple/process_helper_mac.cc',
+    ],
+    'cefsimple_bundle_resources_mac': [
+      'tests/cefsimple/mac/cefsimple.icns',
+      'tests/cefsimple/mac/English.lproj/InfoPlist.strings',
+      'tests/cefsimple/mac/English.lproj/MainMenu.xib',
+      'tests/cefsimple/mac/Info.plist',
+    ],
+    'cefsimple_sources_linux': [
+      'tests/cefsimple/cefsimple_linux.cc',
+      'tests/cefsimple/simple_handler_linux.cc',
+    ],
+    'ceftests_sources_common': [
+      'tests/ceftests/audio_output_unittest.cc',
+      'tests/ceftests/browser_info_map_unittest.cc',
+      'tests/ceftests/command_line_unittest.cc',
+      'tests/ceftests/cookie_unittest.cc',
+      'tests/ceftests/devtools_message_unittest.cc',
+      'tests/ceftests/dialog_unittest.cc',
+      'tests/ceftests/display_unittest.cc',
+      'tests/ceftests/dom_unittest.cc',
+      'tests/ceftests/download_unittest.cc',
+      'tests/ceftests/draggable_regions_unittest.cc',
+      'tests/ceftests/extensions/background_unittest.cc',
+      'tests/ceftests/extensions/chrome_alarms_unittest.cc',
+      'tests/ceftests/extensions/chrome_storage_unittest.cc',
+      'tests/ceftests/extensions/chrome_tabs_unittest.cc',
+      'tests/ceftests/extensions/extension_test_handler.cc',
+      'tests/ceftests/extensions/extension_test_handler.h',
+      'tests/ceftests/extensions/view_unittest.cc',
+      'tests/ceftests/file_util_unittest.cc',
+      'tests/ceftests/frame_unittest.cc',
+      'tests/ceftests/image_unittest.cc',
+      'tests/ceftests/image_util.cc',
+      'tests/ceftests/image_util.h',
+      'tests/ceftests/jsdialog_unittest.cc',
+      'tests/ceftests/life_span_unittest.cc',
+      'tests/ceftests/message_router_unittest.cc',
+      'tests/ceftests/navigation_unittest.cc',
+      'tests/ceftests/os_rendering_unittest.cc',
+      'tests/ceftests/osr_accessibility_unittest.cc',
+      'tests/ceftests/osr_display_unittest.cc',
+      'tests/ceftests/parser_unittest.cc',
+      'tests/ceftests/plugin_unittest.cc',
+      'tests/ceftests/preference_unittest.cc',
+      'tests/ceftests/print_unittest.cc',
+      'tests/ceftests/process_message_unittest.cc',
+      'tests/ceftests/request_context_unittest.cc',
+      'tests/ceftests/request_handler_unittest.cc',
+      'tests/ceftests/request_unittest.cc',
+      'tests/ceftests/response_unittest.cc',
+      'tests/ceftests/resource.h',
+      'tests/ceftests/resource_manager_unittest.cc',
+      'tests/ceftests/resource_request_handler_unittest.cc',
+      'tests/ceftests/routing_test_handler.cc',
+      'tests/ceftests/routing_test_handler.h',
+      'tests/ceftests/run_all_unittests.cc',
+      'tests/ceftests/scheme_handler_unittest.cc',
+      'tests/ceftests/scoped_temp_dir_unittest.cc',
+      'tests/ceftests/server_unittest.cc',
+      'tests/ceftests/stream_unittest.cc',
+      'tests/ceftests/stream_resource_handler_unittest.cc',
+      'tests/ceftests/string_unittest.cc',
+      'tests/ceftests/client_app_delegates.cc',
+      'tests/ceftests/task_unittest.cc',
+      'tests/ceftests/test_handler.cc',
+      'tests/ceftests/test_handler.h',
+      'tests/ceftests/test_suite.cc',
+      'tests/ceftests/test_suite.h',
+      'tests/ceftests/test_util.cc',
+      'tests/ceftests/test_util.h',
+      'tests/ceftests/thread_helper.cc',
+      'tests/ceftests/thread_helper.h',
+      'tests/ceftests/thread_unittest.cc',
+      'tests/ceftests/tracing_unittest.cc',
+      'tests/ceftests/translator_unittest.cc',
+      'tests/ceftests/urlrequest_unittest.cc',
+      'tests/ceftests/v8_unittest.cc',
+      'tests/ceftests/values_unittest.cc',
+      'tests/ceftests/version_unittest.cc',
+      'tests/ceftests/waitable_event_unittest.cc',
+      'tests/ceftests/webui_unittest.cc',
+      'tests/ceftests/xml_reader_unittest.cc',
+      'tests/ceftests/zip_reader_unittest.cc',
+    ],
+    'ceftests_sources_views': [
+      'tests/ceftests/views/button_unittest.cc',
+      'tests/ceftests/views/panel_unittest.cc',
+      'tests/ceftests/views/scroll_view_unittest.cc',
+      'tests/ceftests/views/test_window_delegate.cc',
+      'tests/ceftests/views/test_window_delegate.h',
+      'tests/ceftests/views/textfield_unittest.cc',
+      'tests/ceftests/views/window_unittest.cc',
+    ],
+    'ceftests_sources_win': [
+      'tests/ceftests/resource_util_win_idmap.cc',
+      'tests/ceftests/resources/win/ceftests.rc',
+    ],
+    'ceftests_sources_resources_win': [
+      'tests/ceftests/resources/win/ceftests.exe.manifest',
+      'tests/ceftests/resources/win/ceftests.ico',
+      'tests/ceftests/resources/win/small.ico',
+    ],
+    'ceftests_sources_mac': [
+      'tests/ceftests/os_rendering_unittest_mac.h',
+      'tests/ceftests/os_rendering_unittest_mac.mm',
+      'tests/ceftests/run_all_unittests_mac.mm',
+    ],
+    'ceftests_sources_mac_helper': [
+      'tests/shared/browser/file_util.cc',
+      'tests/shared/browser/file_util.h',
+      'tests/shared/browser/resource_util.h',
+      'tests/shared/browser/resource_util_mac.mm',
+      'tests/shared/browser/resource_util_posix.cc',
+      'tests/ceftests/audio_output_unittest.cc',
+      'tests/ceftests/client_app_delegates.cc',
+      'tests/ceftests/cookie_unittest.cc',
+      'tests/ceftests/dom_unittest.cc',
+      'tests/ceftests/frame_unittest.cc',
+      'tests/ceftests/message_router_unittest.cc',
+      'tests/ceftests/navigation_unittest.cc',
+      'tests/ceftests/plugin_unittest.cc',
+      'tests/ceftests/preference_unittest.cc',
+      'tests/ceftests/process_message_unittest.cc',
+      'tests/ceftests/request_handler_unittest.cc',
+      'tests/ceftests/request_unittest.cc',
+      'tests/ceftests/response_unittest.cc',
+      'tests/ceftests/resource_request_handler_unittest.cc',
+      'tests/ceftests/routing_test_handler.cc',
+      'tests/ceftests/routing_test_handler.h',
+      'tests/ceftests/scheme_handler_unittest.cc',
+      'tests/ceftests/urlrequest_unittest.cc',
+      'tests/ceftests/test_handler.cc',
+      'tests/ceftests/test_handler.h',
+      'tests/ceftests/test_suite.cc',
+      'tests/ceftests/test_suite.h',
+      'tests/ceftests/test_util.cc',
+      'tests/ceftests/test_util.h',
+      'tests/ceftests/thread_helper.cc',
+      'tests/ceftests/thread_helper.h',
+      'tests/ceftests/thread_unittest.cc',
+      'tests/ceftests/tracing_unittest.cc',
+      'tests/ceftests/v8_unittest.cc',
+    ],
+    'ceftests_bundle_resources_mac': [
+      'tests/ceftests/resources/mac/ceftests.icns',
+      'tests/ceftests/resources/mac/English.lproj/InfoPlist.strings',
+      'tests/ceftests/resources/mac/English.lproj/MainMenu.xib',
+      'tests/ceftests/resources/mac/Info.plist',
+    ],
+    'ceftests_sources_linux': [
+      'tests/ceftests/resource_util_linux.cc',
+    ],
+  },
+}
diff --git a/src/cef_repack_locales.gni b/src/cef_repack_locales.gni
new file mode 100644
index 0000000..51e9189
--- /dev/null
+++ b/src/cef_repack_locales.gni
@@ -0,0 +1,132 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+# 2014 the Chromium Authors. All rights reserved. Use of this source code is
+# governed by a BSD-style license that can be found in the LICENSE file.
+#
+# This is a copy of src/chrome/chrome_repack_locales.gni with the necessary
+# modifications to meet CEF's requirements.
+
+import("//build/config/chrome_build.gni")
+import("//build/config/features.gni")
+import("//build/config/ui.gni")
+import("//tools/grit/repack.gni")
+
+# Arguments:
+#
+#   locale
+#       Internal name of locale. e.g. "pt-BR"
+#
+#   output
+#       Output file name.
+#
+#   visibility
+#       Normal meaning.
+template("_repack_one_locale") {
+  locale = invoker.locale
+
+  repack(target_name) {
+    visibility = invoker.visibility
+
+    # Each input pak file should also have a deps line for completeness.
+    # Add associated .h files in the make_pack_header("strings") target.
+    sources = [
+      "${root_gen_dir}/cef/cef_strings_${locale}.pak",
+      "${root_gen_dir}/chrome/chromium_strings_${locale}.pak",
+      "${root_gen_dir}/chrome/generated_resources_${locale}.pak",
+      "${root_gen_dir}/chrome/locale_settings_${locale}.pak",
+      "${root_gen_dir}/chrome/platform_locale_settings_${locale}.pak",
+      "${root_gen_dir}/components/strings/components_locale_settings_${locale}.pak",
+      "${root_gen_dir}/components/strings/components_strings_${locale}.pak",
+      "${root_gen_dir}/extensions/strings/extensions_strings_${locale}.pak",
+      "${root_gen_dir}/services/strings/services_strings_${locale}.pak",
+      "${root_gen_dir}/third_party/blink/public/strings/blink_strings_${locale}.pak",
+      "${root_gen_dir}/ui/strings/app_locale_settings_${locale}.pak",
+      "${root_gen_dir}/ui/strings/ui_strings_${locale}.pak",
+    ]
+
+    # Use public_deps so that generated grit headers are discoverable from
+    # the libcef_static target. Grit deps that generate .cc files must be
+    # listed both here and in the libcef_static target.
+    public_deps = [
+      ":cef_strings",
+      "//chrome/app:chromium_strings",
+      "//chrome/app:generated_resources",
+      "//chrome/app/resources:locale_settings",
+      "//chrome/app/resources:platform_locale_settings",
+      "//components/strings:components_locale_settings",
+      "//components/strings:components_strings",
+      "//extensions/strings",
+      "//services/strings",
+      "//third_party/blink/public/strings",
+      "//ui/strings:app_locale_settings",
+      "//ui/strings:ui_strings",
+    ]
+
+    output = invoker.output
+  }
+}
+
+# Creates an action to call the repack_locales script.
+#
+# The GYP version generates the locales in the "gen" directory and then copies
+# it to the root build directory. This isn't easy to express in a GN copy
+# rule since the files on Mac have a complex structure. So we generate the
+# files into the final place and skip the "gen" directory.
+#
+# This template uses GN's looping constructs to avoid the complex call to
+# chrome/tools/build/repack_locales.py which wraps the repack commands in the
+# GYP build.
+#
+# Arguments
+#
+#   input_locales
+#       List of locale names to use as inputs.
+#
+#   output_locales
+#       A list containing the corresponding output names for each of the
+#       input names. Mac uses different names in some cases.
+#
+#   visibility
+template("cef_repack_locales") {
+  # This is the name of the group below that will collect all the invidual
+  # locale targets. External targets will depend on this.
+  group_target_name = target_name
+
+  # GN's subscript is too stupid to do invoker.output_locales[foo] so we need
+  # to make a copy and do output_locales[foo].
+  output_locales = invoker.output_locales
+
+  # Collects all targets the loop generates.
+  locale_targets = []
+
+  # This loop iterates over the input locales and also keeps a counter so it
+  # can simultaneously iterate over the output locales (using GN's very
+  # limited looping capabilities).
+  current_index = 0
+  foreach(input_locale, invoker.input_locales) {
+    output_locale = output_locales[current_index]
+
+    # Compute the name of the target for the current file. Save it for the deps.
+    current_name = "${target_name}_${input_locale}"
+    locale_targets += [ ":$current_name" ]
+
+    _repack_one_locale(current_name) {
+      visibility = [ ":$group_target_name" ]
+      locale = input_locale
+
+      # Compute the output name. Mac uses a different location.
+      if (is_mac || is_ios) {
+        output = "${root_gen_dir}/repack/locales/${output_locale}.pak"
+      } else {
+        output = "${root_out_dir}/locales/${output_locale}.pak"
+      }
+    }
+
+    current_index = current_index + 1
+  }
+
+  # The group that external targets depend on which collects all deps.
+  group(group_target_name) {
+    forward_variables_from(invoker, [ "visibility" ])
+    public_deps = locale_targets
+  }
+}
diff --git a/src/cmake/FindCEF.cmake.in b/src/cmake/FindCEF.cmake.in
new file mode 100644
index 0000000..cd33a7d
--- /dev/null
+++ b/src/cmake/FindCEF.cmake.in
@@ -0,0 +1,39 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+#
+# This file is the CEF CMake configuration entry point and should be loaded
+# using `find_package(CEF REQUIRED)`. See the top-level CMakeLists.txt file
+# included with the CEF binary distribution for usage information.
+#
+
+# Find the CEF binary distribution root directory.
+set(_CEF_ROOT "")
+if(CEF_ROOT AND IS_DIRECTORY "${CEF_ROOT}")
+  set(_CEF_ROOT "${CEF_ROOT}")
+  set(_CEF_ROOT_EXPLICIT 1)
+else()
+  set(_ENV_CEF_ROOT "")
+  if(DEFINED ENV{CEF_ROOT})
+    file(TO_CMAKE_PATH "$ENV{CEF_ROOT}" _ENV_CEF_ROOT)
+  endif()
+  if(_ENV_CEF_ROOT AND IS_DIRECTORY "${_ENV_CEF_ROOT}")
+    set(_CEF_ROOT "${_ENV_CEF_ROOT}")
+    set(_CEF_ROOT_EXPLICIT 1)
+  endif()
+  unset(_ENV_CEF_ROOT)
+endif()
+
+if(NOT DEFINED _CEF_ROOT_EXPLICIT)
+  message(FATAL_ERROR "Must specify a CEF_ROOT value via CMake or environment variable.")
+endif()
+
+if(NOT IS_DIRECTORY "${_CEF_ROOT}/cmake")
+  message(FATAL_ERROR "No CMake bootstrap found for CEF binary distribution at: ${CEF_ROOT}.")
+endif()
+
+# Execute additional cmake files from the CEF binary distribution.
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${_CEF_ROOT}/cmake")
+include("cef_variables")
+include("cef_macros")
diff --git a/src/cmake/cef_macros.cmake.in b/src/cmake/cef_macros.cmake.in
new file mode 100644
index 0000000..1b667e1
--- /dev/null
+++ b/src/cmake/cef_macros.cmake.in
@@ -0,0 +1,366 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+# Must be loaded via FindCEF.cmake.
+if(NOT DEFINED _CEF_ROOT_EXPLICIT)
+  message(FATAL_ERROR "Use find_package(CEF) to load this file.")
+endif()
+
+
+#
+# Shared macros.
+#
+
+# Print the current CEF configuration.
+macro(PRINT_CEF_CONFIG)
+  message(STATUS "*** CEF CONFIGURATION SETTINGS ***")
+  message(STATUS "Generator:                    ${CMAKE_GENERATOR}")
+  message(STATUS "Platform:                     ${CMAKE_SYSTEM_NAME}")
+  message(STATUS "Project architecture:         ${PROJECT_ARCH}")
+
+  if(GEN_NINJA OR GEN_MAKEFILES)
+    message(STATUS "Build type:                   ${CMAKE_BUILD_TYPE}")
+  endif()
+
+  message(STATUS "Binary distribution root:     ${_CEF_ROOT}")
+
+  if(OS_MACOSX)
+    message(STATUS "Base SDK:                     ${CMAKE_OSX_SYSROOT}")
+    message(STATUS "Target SDK:                   ${CEF_TARGET_SDK}")
+  endif()
+
+  if(OS_WINDOWS)
+    message(STATUS "Visual Studio ATL support:    ${USE_ATL}")
+  endif()
+
+  message(STATUS "CEF sandbox:                  ${USE_SANDBOX}")
+
+  set(_libraries ${CEF_STANDARD_LIBS})
+  if(OS_WINDOWS AND USE_SANDBOX)
+    list(APPEND _libraries ${CEF_SANDBOX_STANDARD_LIBS})
+  endif()
+  message(STATUS "Standard libraries:           ${_libraries}")
+
+  message(STATUS "Compile defines:              ${CEF_COMPILER_DEFINES}")
+  message(STATUS "Compile defines (Debug):      ${CEF_COMPILER_DEFINES_DEBUG}")
+  message(STATUS "Compile defines (Release):    ${CEF_COMPILER_DEFINES_RELEASE}")
+  message(STATUS "C compile flags:              ${CEF_COMPILER_FLAGS} ${CEF_C_COMPILER_FLAGS}")
+  message(STATUS "C compile flags (Debug):      ${CEF_COMPILER_FLAGS_DEBUG} ${CEF_C_COMPILER_FLAGS_DEBUG}")
+  message(STATUS "C compile flags (Release):    ${CEF_COMPILER_FLAGS_RELEASE} ${CEF_C_COMPILER_FLAGS_RELEASE}")
+  message(STATUS "C++ compile flags:            ${CEF_COMPILER_FLAGS} ${CEF_CXX_COMPILER_FLAGS}")
+  message(STATUS "C++ compile flags (Debug):    ${CEF_COMPILER_FLAGS_DEBUG} ${CEF_CXX_COMPILER_FLAGS_DEBUG}")
+  message(STATUS "C++ compile flags (Release):  ${CEF_COMPILER_FLAGS_RELEASE} ${CEF_CXX_COMPILER_FLAGS_RELEASE}")
+  message(STATUS "Exe link flags:               ${CEF_LINKER_FLAGS} ${CEF_EXE_LINKER_FLAGS}")
+  message(STATUS "Exe link flags (Debug):       ${CEF_LINKER_FLAGS_DEBUG} ${CEF_EXE_LINKER_FLAGS_DEBUG}")
+  message(STATUS "Exe link flags (Release):     ${CEF_LINKER_FLAGS_RELEASE} ${CEF_EXE_LINKER_FLAGS_RELEASE}")
+  message(STATUS "Shared link flags:            ${CEF_LINKER_FLAGS} ${CEF_SHARED_LINKER_FLAGS}")
+  message(STATUS "Shared link flags (Debug):    ${CEF_LINKER_FLAGS_DEBUG} ${CEF_SHARED_LINKER_FLAGS_DEBUG}")
+  message(STATUS "Shared link flags (Release):  ${CEF_LINKER_FLAGS_RELEASE} ${CEF_SHARED_LINKER_FLAGS_RELEASE}")
+
+  if(OS_LINUX OR OS_WINDOWS)
+    message(STATUS "CEF Binary files:             ${CEF_BINARY_FILES}")
+    message(STATUS "CEF Resource files:           ${CEF_RESOURCE_FILES}")
+  endif()
+endmacro()
+
+# Append platform specific sources to a list of sources.
+macro(APPEND_PLATFORM_SOURCES name_of_list)
+  if(OS_LINUX AND ${name_of_list}_LINUX)
+    list(APPEND ${name_of_list} ${${name_of_list}_LINUX})
+  endif()
+  if(OS_POSIX AND ${name_of_list}_POSIX)
+    list(APPEND ${name_of_list} ${${name_of_list}_POSIX})
+  endif()
+  if(OS_WINDOWS AND ${name_of_list}_WINDOWS)
+    list(APPEND ${name_of_list} ${${name_of_list}_WINDOWS})
+  endif()
+  if(OS_MACOSX AND ${name_of_list}_MACOSX)
+    list(APPEND ${name_of_list} ${${name_of_list}_MACOSX})
+  endif()
+endmacro()
+
+# Determine the target output directory based on platform and generator.
+macro(SET_CEF_TARGET_OUT_DIR)
+  if(GEN_NINJA OR GEN_MAKEFILES)
+    # By default Ninja and Make builds don't create a subdirectory named after
+    # the configuration.
+    set(CEF_TARGET_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}")
+
+    # Output binaries (executables, libraries) to the correct directory.
+    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})
+    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})
+  else()
+    set(CEF_TARGET_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>")
+  endif()
+endmacro()
+
+# Copy a list of files from one directory to another. Relative files paths are maintained.
+# The path component of the source |file_list| will be removed.
+macro(COPY_FILES target file_list source_dir target_dir)
+  foreach(FILENAME ${file_list})
+    set(source_file ${source_dir}/${FILENAME})
+    get_filename_component(target_name ${FILENAME} NAME)
+    set(target_file ${target_dir}/${target_name})
+
+    string(FIND ${source_file} "$<CONFIGURATION>" _pos)
+    if(NOT ${_pos} EQUAL -1)
+      # Must test with an actual configuration directory.
+      string(REPLACE "$<CONFIGURATION>" "Release" existing_source_file ${source_file})
+      if(NOT EXISTS ${existing_source_file})
+        string(REPLACE "$<CONFIGURATION>" "Debug" existing_source_file ${source_file})
+      endif()
+    else()
+      set(existing_source_file ${source_file})
+    endif()
+
+    if(IS_DIRECTORY ${existing_source_file})
+      add_custom_command(
+        TARGET ${target}
+        POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy_directory "${source_file}" "${target_file}"
+        VERBATIM
+        )
+    else()
+      add_custom_command(
+        TARGET ${target}
+        POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different "${source_file}" "${target_file}"
+        VERBATIM
+        )
+    endif()
+  endforeach()
+endmacro()
+
+
+#
+# Linux macros.
+#
+
+if(OS_LINUX)
+
+# Use pkg-config to find Linux libraries and update compiler/linker variables.
+macro(FIND_LINUX_LIBRARIES libraries)
+  # Read pkg-config info into variables.
+  execute_process(COMMAND pkg-config --cflags ${libraries} OUTPUT_VARIABLE FLL_CFLAGS)
+  execute_process(COMMAND pkg-config --libs-only-L --libs-only-other ${libraries} OUTPUT_VARIABLE FLL_LDFLAGS)
+  execute_process(COMMAND pkg-config --libs-only-l ${libraries} OUTPUT_VARIABLE FLL_LIBS)
+
+  # Strip leading and trailing whitepspace.
+  STRING(STRIP "${FLL_CFLAGS}"  FLL_CFLAGS)
+  STRING(STRIP "${FLL_LDFLAGS}" FLL_LDFLAGS)
+  STRING(STRIP "${FLL_LIBS}"    FLL_LIBS)
+
+  # Convert to a list.
+  separate_arguments(FLL_CFLAGS)
+  separate_arguments(FLL_LDFLAGS)
+  separate_arguments(FLL_LIBS)
+
+  # Update build variables.
+  list(APPEND CEF_C_COMPILER_FLAGS    ${FLL_CFLAGS})
+  list(APPEND CEF_CXX_COMPILER_FLAGS  ${FLL_CFLAGS})
+  list(APPEND CEF_EXE_LINKER_FLAGS    ${FLL_LDFLAGS})
+  list(APPEND CEF_SHARED_LINKER_FLAGS ${FLL_LDFLAGS})
+  list(APPEND CEF_STANDARD_LIBS       ${FLL_LIBS})
+endmacro()
+
+# Set SUID permissions on the specified executable.
+macro(SET_LINUX_SUID_PERMISSIONS target executable)
+  add_custom_command(
+    TARGET ${target}
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E echo ""
+    COMMAND ${CMAKE_COMMAND} -E echo "*** Run the following command manually to set SUID permissions ***"
+    COMMAND ${CMAKE_COMMAND} -E echo "EXE=\"${executable}\" && sudo -- chown root:root $EXE && sudo -- chmod 4755 $EXE"
+    COMMAND ${CMAKE_COMMAND} -E echo ""
+    VERBATIM
+    )
+endmacro()
+
+endif(OS_LINUX)
+
+
+#
+# Mac OS X macros.
+#
+
+if(OS_MACOSX)
+
+# Manually process and copy over resource files.
+macro(COPY_MACOSX_RESOURCES resource_list prefix_list target source_dir app_path)
+  foreach(FILENAME ${resource_list})
+    # Remove one or more prefixes from the source paths.
+    set(TARGET_FILENAME "${FILENAME}")
+    foreach(PREFIX ${prefix_list})
+      string(REGEX REPLACE "^.*${PREFIX}" "" TARGET_FILENAME ${TARGET_FILENAME})
+    endforeach()
+
+    # Determine the absolute source and target paths.
+    set(TARGET_PATH "${app_path}/Contents/Resources/${TARGET_FILENAME}")
+    if(IS_ABSOLUTE ${FILENAME})
+      set(SOURCE_PATH ${FILENAME})
+    else()
+      set(SOURCE_PATH "${source_dir}/${FILENAME}")
+    endif()
+
+    if(${FILENAME} MATCHES ".xib$")
+      # Change the target file extension.
+      string(REGEX REPLACE ".xib$" ".nib" TARGET_PATH ${TARGET_PATH})
+
+      get_filename_component(TARGET_DIRECTORY ${TARGET_PATH} PATH)
+      add_custom_command(
+        TARGET ${target}
+        POST_BUILD
+        # Create the target directory.
+        COMMAND ${CMAKE_COMMAND} -E make_directory "${TARGET_DIRECTORY}"
+        # Compile the XIB file to a NIB.
+        COMMAND /usr/bin/ibtool --output-format binary1 --compile "${TARGET_PATH}" "${SOURCE_PATH}"
+        VERBATIM
+        )
+    elseif(NOT ${TARGET_FILENAME} STREQUAL "Info.plist")
+      # Copy the file as-is.
+      add_custom_command(
+        TARGET ${target}
+        POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_PATH}" "${TARGET_PATH}"
+        VERBATIM
+        )
+    endif()
+  endforeach()
+endmacro()
+
+endif(OS_MACOSX)
+
+
+#
+# Windows macros.
+#
+
+if(OS_WINDOWS)
+
+# Add custom manifest files to an executable target.
+macro(ADD_WINDOWS_MANIFEST manifest_path target extension)
+  add_custom_command(
+    TARGET ${target}
+    POST_BUILD
+    COMMAND "mt.exe" -nologo
+            -manifest \"${manifest_path}/${target}.${extension}.manifest\" \"${manifest_path}/compatibility.manifest\"
+            -outputresource:"${CEF_TARGET_OUT_DIR}/${target}.${extension}"\;\#1
+    COMMENT "Adding manifest..."
+    )
+endmacro()
+
+endif(OS_WINDOWS)
+
+
+#
+# Target configuration macros.
+#
+
+# Add a logical target that can be used to link the specified libraries into an
+# executable target.
+macro(ADD_LOGICAL_TARGET target debug_lib release_lib)
+  add_library(${target} ${CEF_LIBTYPE} IMPORTED)
+  set_target_properties(${target} PROPERTIES
+    IMPORTED_LOCATION "${release_lib}"
+    IMPORTED_LOCATION_DEBUG "${debug_lib}"
+    IMPORTED_LOCATION_RELEASE "${release_lib}"
+    )
+endmacro()
+
+# Set common target properties. Use SET_LIBRARY_TARGET_PROPERTIES() or
+# SET_EXECUTABLE_TARGET_PROPERTIES() instead of calling this macro directly.
+macro(SET_COMMON_TARGET_PROPERTIES target)
+  # Compile flags.
+  target_compile_options(${target} PRIVATE ${CEF_COMPILER_FLAGS} ${CEF_CXX_COMPILER_FLAGS})
+  target_compile_options(${target} PRIVATE $<$<CONFIG:Debug>:${CEF_COMPILER_FLAGS_DEBUG} ${CEF_CXX_COMPILER_FLAGS_DEBUG}>)
+  target_compile_options(${target} PRIVATE $<$<CONFIG:Release>:${CEF_COMPILER_FLAGS_RELEASE} ${CEF_CXX_COMPILER_FLAGS_RELEASE}>)
+
+  # Compile definitions.
+  target_compile_definitions(${target} PRIVATE ${CEF_COMPILER_DEFINES})
+  target_compile_definitions(${target} PRIVATE $<$<CONFIG:Debug>:${CEF_COMPILER_DEFINES_DEBUG}>)
+  target_compile_definitions(${target} PRIVATE $<$<CONFIG:Release>:${CEF_COMPILER_DEFINES_RELEASE}>)
+
+  # Include directories.
+  target_include_directories(${target} PRIVATE ${CEF_INCLUDE_PATH})
+
+  # Linker flags.
+  if(CEF_LINKER_FLAGS)
+    string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
+  endif()
+  if(CEF_LINKER_FLAGS_DEBUG)
+    string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS_DEBUG}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
+  endif()
+  if(CEF_LINKER_FLAGS_RELEASE)
+    string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS_RELEASE}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
+  endif()
+
+  if(OS_MACOSX)
+    # Set Xcode target properties.
+    set_target_properties(${target} PROPERTIES
+      XCODE_ATTRIBUTE_ALWAYS_SEARCH_USER_PATHS                    NO
+      XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD                 "gnu++11"   # -std=gnu++11
+      XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME                     NO          # -fno-objc-link-runtime
+      XCODE_ATTRIBUTE_CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS  YES         # -Wobjc-missing-property-synthesis
+      XCODE_ATTRIBUTE_COPY_PHASE_STRIP                            NO
+      XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING[variant=Release]        YES         # -Wl,-dead_strip
+      XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD                     "c99"       # -std=c99
+      XCODE_ATTRIBUTE_GCC_CW_ASM_SYNTAX                           NO          # No -fasm-blocks
+      XCODE_ATTRIBUTE_GCC_DYNAMIC_NO_PIC                          NO
+      XCODE_ATTRIBUTE_GCC_ENABLE_CPP_EXCEPTIONS                   NO          # -fno-exceptions
+      XCODE_ATTRIBUTE_GCC_ENABLE_CPP_RTTI                         NO          # -fno-rtti
+      XCODE_ATTRIBUTE_GCC_ENABLE_PASCAL_STRINGS                   NO          # No -mpascal-strings
+      XCODE_ATTRIBUTE_GCC_INLINES_ARE_PRIVATE_EXTERN              YES         # -fvisibility-inlines-hidden
+      XCODE_ATTRIBUTE_GCC_OBJC_CALL_CXX_CDTORS                    YES         # -fobjc-call-cxx-cdtors
+      XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN                  YES         # -fvisibility=hidden
+      XCODE_ATTRIBUTE_GCC_THREADSAFE_STATICS                      NO          # -fno-threadsafe-statics
+      XCODE_ATTRIBUTE_GCC_TREAT_WARNINGS_AS_ERRORS                YES         # -Werror
+      XCODE_ATTRIBUTE_GCC_VERSION                                 "com.apple.compilers.llvm.clang.1_0"
+      XCODE_ATTRIBUTE_GCC_WARN_ABOUT_MISSING_NEWLINE              YES         # -Wnewline-eof
+      XCODE_ATTRIBUTE_USE_HEADERMAP                               NO
+      OSX_ARCHITECTURES_DEBUG                                     "${CMAKE_OSX_ARCHITECTURES}"
+      OSX_ARCHITECTURES_RELEASE                                   "${CMAKE_OSX_ARCHITECTURES}"
+      )
+  endif()
+endmacro()
+
+# Set library-specific properties.
+macro(SET_LIBRARY_TARGET_PROPERTIES target)
+  SET_COMMON_TARGET_PROPERTIES(${target})
+
+  # Shared library linker flags.
+  if(CEF_SHARED_LINKER_FLAGS)
+    string(REPLACE ";" " " _flags_str "${CEF_SHARED_LINKER_FLAGS}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
+  endif()
+  if(CEF_SHARED_LINKER_FLAGS_DEBUG)
+    string(REPLACE ";" " " _flags_str "${CEF_SHARED_LINKER_FLAGS_DEBUG}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
+  endif()
+  if(CEF_SHARED_LINKER_FLAGS_RELEASE)
+    string(REPLACE ";" " " _flags_str "${CEF_SHARED_LINKER_FLAGS_RELEASE}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
+  endif()
+endmacro()
+
+# Set executable-specific properties.
+macro(SET_EXECUTABLE_TARGET_PROPERTIES target)
+  SET_COMMON_TARGET_PROPERTIES(${target})
+
+  # Executable linker flags.
+  if(CEF_EXE_LINKER_FLAGS)
+    string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
+  endif()
+  if(CEF_EXE_LINKER_FLAGS_DEBUG)
+    string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS_DEBUG}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
+  endif()
+  if(CEF_EXE_LINKER_FLAGS_RELEASE)
+    string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS_RELEASE}")
+    set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
+  endif()
+endmacro()
diff --git a/src/cmake/cef_variables.cmake.in b/src/cmake/cef_variables.cmake.in
new file mode 100644
index 0000000..4b3b58d
--- /dev/null
+++ b/src/cmake/cef_variables.cmake.in
@@ -0,0 +1,555 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+# Must be loaded via FindCEF.cmake.
+if(NOT DEFINED _CEF_ROOT_EXPLICIT)
+  message(FATAL_ERROR "Use find_package(CEF) to load this file.")
+endif()
+
+
+#
+# Shared configuration.
+#
+
+# Determine the platform.
+if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
+  set(OS_MACOSX 1)
+  set(OS_POSIX 1)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
+  set(OS_LINUX 1)
+  set(OS_POSIX 1)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
+  set(OS_WINDOWS 1)
+endif()
+
+# Determine the project architecture.
+if(NOT DEFINED PROJECT_ARCH)
+  if(CMAKE_SIZEOF_VOID_P MATCHES 8)
+    set(PROJECT_ARCH "x86_64")
+  else()
+    set(PROJECT_ARCH "x86")
+  endif()
+
+  if(OS_MACOSX)
+    # PROJECT_ARCH should be specified on Mac OS X.
+    message(WARNING "No PROJECT_ARCH value specified, using ${PROJECT_ARCH}")
+  endif()
+endif()
+
+if(${CMAKE_GENERATOR} STREQUAL "Ninja")
+  set(GEN_NINJA 1)
+elseif(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles")
+  set(GEN_MAKEFILES 1)
+endif()
+
+# Determine the build type.
+if(NOT CMAKE_BUILD_TYPE AND (GEN_NINJA OR GEN_MAKEFILES))
+  # CMAKE_BUILD_TYPE should be specified when using Ninja or Unix Makefiles.
+  set(CMAKE_BUILD_TYPE Release)
+  message(WARNING "No CMAKE_BUILD_TYPE value selected, using ${CMAKE_BUILD_TYPE}")
+endif()
+
+
+# Path to the include directory.
+set(CEF_INCLUDE_PATH "${_CEF_ROOT}")
+
+# Path to the libcef_dll_wrapper target.
+set(CEF_LIBCEF_DLL_WRAPPER_PATH "${_CEF_ROOT}/libcef_dll")
+
+
+# Shared compiler/linker flags.
+list(APPEND CEF_COMPILER_DEFINES
+  # Allow C++ programs to use stdint.h macros specified in the C99 standard that aren't 
+  # in the C++ standard (e.g. UINT8_MAX, INT64_MIN, etc)
+  __STDC_CONSTANT_MACROS __STDC_FORMAT_MACROS
+  )
+
+
+# Configure use of the sandbox.
+option(USE_SANDBOX "Enable or disable use of the sandbox." ON)
+
+
+#
+# Linux configuration.
+#
+
+if(OS_LINUX)
+  # Platform-specific compiler/linker flags.
+  set(CEF_LIBTYPE SHARED)
+  list(APPEND CEF_COMPILER_FLAGS
+    -fno-strict-aliasing            # Avoid assumptions regarding non-aliasing of objects of different types
+    -fPIC                           # Generate position-independent code for shared libraries
+    -fstack-protector               # Protect some vulnerable functions from stack-smashing (security feature)
+    -funwind-tables                 # Support stack unwinding for backtrace()
+    -fvisibility=hidden             # Give hidden visibility to declarations that are not explicitly marked as visible
+    --param=ssp-buffer-size=4       # Set the minimum buffer size protected by SSP (security feature, related to stack-protector)
+    -pipe                           # Use pipes rather than temporary files for communication between build stages
+    -pthread                        # Use the pthread library
+    -Wall                           # Enable all warnings
+    -Werror                         # Treat warnings as errors
+    -Wno-missing-field-initializers # Don't warn about missing field initializers
+    -Wno-unused-parameter           # Don't warn about unused parameters
+    -Wno-error=comment              # Don't warn about code in comments
+    -Wno-comment                    # Don't warn about code in comments
+    )
+  list(APPEND CEF_C_COMPILER_FLAGS
+    -std=c99                        # Use the C99 language standard
+    )
+  list(APPEND CEF_CXX_COMPILER_FLAGS
+    -fno-exceptions                 # Disable exceptions
+    -fno-rtti                       # Disable real-time type information
+    -fno-threadsafe-statics         # Don't generate thread-safe statics
+    -fvisibility-inlines-hidden     # Give hidden visibility to inlined class member functions
+    -std=gnu++11                    # Use the C++11 language standard including GNU extensions
+    -Wsign-compare                  # Warn about mixed signed/unsigned type comparisons
+    )
+  list(APPEND CEF_COMPILER_FLAGS_DEBUG
+    -O0                             # Disable optimizations
+    -g                              # Generate debug information
+    )
+  list(APPEND CEF_COMPILER_FLAGS_RELEASE
+    -O2                             # Optimize for maximum speed
+    -fdata-sections                 # Enable linker optimizations to improve locality of reference for data sections
+    -ffunction-sections             # Enable linker optimizations to improve locality of reference for function sections
+    -fno-ident                      # Ignore the #ident directive
+    -U_FORTIFY_SOURCE               # Undefine _FORTIFY_SOURCE in case it was previously defined
+    -D_FORTIFY_SOURCE=2             # Add memory and string function protection (security feature, related to stack-protector)
+    )
+  list(APPEND CEF_LINKER_FLAGS
+    -fPIC                           # Generate position-independent code for shared libraries
+    -pthread                        # Use the pthread library
+    -Wl,--disable-new-dtags         # Don't generate new-style dynamic tags in ELF
+    -Wl,--fatal-warnings            # Treat warnings as errors
+    -Wl,-rpath,.                    # Set rpath so that libraries can be placed next to the executable
+    -Wl,-z,noexecstack              # Mark the stack as non-executable (security feature)
+    -Wl,-z,now                      # Resolve symbols on program start instead of on first use (security feature)
+    -Wl,-z,relro                    # Mark relocation sections as read-only (security feature)
+    )
+  list(APPEND CEF_LINKER_FLAGS_RELEASE
+    -Wl,-O1                         # Enable linker optimizations
+    -Wl,--as-needed                 # Only link libraries that export symbols used by the binary
+    -Wl,--gc-sections               # Remove unused code resulting from -fdata-sections and -function-sections
+    )
+  list(APPEND CEF_COMPILER_DEFINES
+    _FILE_OFFSET_BITS=64            # Allow the Large File Support (LFS) interface to replace the old interface
+    )
+  list(APPEND CEF_COMPILER_DEFINES_RELEASE
+    NDEBUG                          # Not a debug build
+    )
+
+  include(CheckCCompilerFlag)
+  include(CheckCXXCompilerFlag)
+
+  CHECK_CXX_COMPILER_FLAG(-Wno-undefined-var-template COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
+  if(COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
+    list(APPEND CEF_CXX_COMPILER_FLAGS
+      -Wno-undefined-var-template   # Don't warn about potentially uninstantiated static members
+      )
+  endif()
+
+  CHECK_C_COMPILER_FLAG(-Wno-unused-local-typedefs COMPILER_SUPPORTS_NO_UNUSED_LOCAL_TYPEDEFS)
+  if(COMPILER_SUPPORTS_NO_UNUSED_LOCAL_TYPEDEFS)
+    list(APPEND CEF_C_COMPILER_FLAGS
+      -Wno-unused-local-typedefs  # Don't warn about unused local typedefs
+      )
+  endif()
+
+  CHECK_CXX_COMPILER_FLAG(-Wno-literal-suffix COMPILER_SUPPORTS_NO_LITERAL_SUFFIX)
+  if(COMPILER_SUPPORTS_NO_LITERAL_SUFFIX)
+    list(APPEND CEF_CXX_COMPILER_FLAGS
+      -Wno-literal-suffix         # Don't warn about invalid suffixes on literals
+      )
+  endif()
+
+  CHECK_CXX_COMPILER_FLAG(-Wno-narrowing COMPILER_SUPPORTS_NO_NARROWING)
+  if(COMPILER_SUPPORTS_NO_NARROWING)
+    list(APPEND CEF_CXX_COMPILER_FLAGS
+      -Wno-narrowing              # Don't warn about type narrowing
+      )
+  endif()
+
+  if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+    list(APPEND CEF_CXX_COMPILER_FLAGS
+      -Wno-attributes             # The cfi-icall attribute is not supported by the GNU C++ compiler
+      )
+  endif()
+
+  if(PROJECT_ARCH STREQUAL "x86_64")
+    # 64-bit architecture.
+    list(APPEND CEF_COMPILER_FLAGS
+      -m64
+      -march=x86-64
+      )
+    list(APPEND CEF_LINKER_FLAGS
+      -m64
+      )
+  elseif(PROJECT_ARCH STREQUAL "x86")
+    # 32-bit architecture.
+    list(APPEND CEF_COMPILER_FLAGS
+      -msse2
+      -mfpmath=sse
+      -mmmx
+      -m32
+      )
+    list(APPEND CEF_LINKER_FLAGS
+      -m32
+      )
+  endif()
+
+  # Standard libraries.
+  set(CEF_STANDARD_LIBS
+    X11
+    )
+
+  # CEF directory paths.
+  set(CEF_RESOURCE_DIR        "${_CEF_ROOT}/Resources")
+  set(CEF_BINARY_DIR          "${_CEF_ROOT}/${CMAKE_BUILD_TYPE}")
+  set(CEF_BINARY_DIR_DEBUG    "${_CEF_ROOT}/Debug")
+  set(CEF_BINARY_DIR_RELEASE  "${_CEF_ROOT}/Release")
+
+  # CEF library paths.
+  set(CEF_LIB_DEBUG   "${CEF_BINARY_DIR_DEBUG}/libcef.so")
+  set(CEF_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/libcef.so")
+
+  # List of CEF binary files.
+  set(CEF_BINARY_FILES
+    chrome-sandbox
+    libcef.so
+    libEGL.so
+    libGLESv2.so
+    snapshot_blob.bin
+    v8_context_snapshot.bin
+    swiftshader
+    )
+
+  # List of CEF resource files.
+  set(CEF_RESOURCE_FILES
+    cef.pak
+    cef_100_percent.pak
+    cef_200_percent.pak
+    cef_extensions.pak
+    devtools_resources.pak
+    icudtl.dat
+    locales
+    )
+
+  if(USE_SANDBOX)
+    list(APPEND CEF_COMPILER_DEFINES
+      CEF_USE_SANDBOX   # Used by apps to test if the sandbox is enabled
+      )
+  endif()
+endif()
+
+
+#
+# Mac OS X configuration.
+#
+
+if(OS_MACOSX)
+  # Platform-specific compiler/linker flags.
+  # See also Xcode target properties in cef_macros.cmake.
+  set(CEF_LIBTYPE SHARED)
+  list(APPEND CEF_COMPILER_FLAGS
+    -fno-strict-aliasing            # Avoid assumptions regarding non-aliasing of objects of different types
+    -fstack-protector               # Protect some vulnerable functions from stack-smashing (security feature)
+    -funwind-tables                 # Support stack unwinding for backtrace()
+    -fvisibility=hidden             # Give hidden visibility to declarations that are not explicitly marked as visible
+    -Wall                           # Enable all warnings
+    -Werror                         # Treat warnings as errors
+    -Wextra                         # Enable additional warnings
+    -Wendif-labels                  # Warn whenever an #else or an #endif is followed by text
+    -Wnewline-eof                   # Warn about no newline at end of file
+    -Wno-missing-field-initializers # Don't warn about missing field initializers
+    -Wno-unused-parameter           # Don't warn about unused parameters
+    )
+  list(APPEND CEF_C_COMPILER_FLAGS
+    -std=c99                        # Use the C99 language standard
+    )
+  list(APPEND CEF_CXX_COMPILER_FLAGS
+    -fno-exceptions                 # Disable exceptions
+    -fno-rtti                       # Disable real-time type information
+    -fno-threadsafe-statics         # Don't generate thread-safe statics
+    -fobjc-call-cxx-cdtors          # Call the constructor/destructor of C++ instance variables in ObjC objects
+    -fvisibility-inlines-hidden     # Give hidden visibility to inlined class member functions
+    -std=gnu++11                    # Use the C++11 language standard including GNU extensions
+    -Wno-narrowing                  # Don't warn about type narrowing
+    -Wsign-compare                  # Warn about mixed signed/unsigned type comparisons
+    )
+  list(APPEND CEF_COMPILER_FLAGS_DEBUG
+    -O0                             # Disable optimizations
+    -g                              # Generate debug information
+    )
+  list(APPEND CEF_COMPILER_FLAGS_RELEASE
+    -O3                             # Optimize for maximum speed plus a few extras
+    )
+  list(APPEND CEF_LINKER_FLAGS
+    -Wl,-search_paths_first         # Search for static or shared library versions in the same pass
+    -Wl,-ObjC                       # Support creation of ObjC static libraries
+    -Wl,-pie                        # Generate position-independent code suitable for executables only
+    )
+  list(APPEND CEF_LINKER_FLAGS_RELEASE
+    -Wl,-dead_strip                 # Strip dead code
+    )
+
+  include(CheckCXXCompilerFlag)
+
+  CHECK_CXX_COMPILER_FLAG(-Wno-undefined-var-template COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
+  if(COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
+    list(APPEND CEF_CXX_COMPILER_FLAGS
+      -Wno-undefined-var-template   # Don't warn about potentially uninstantiated static members
+      )
+  endif()
+
+  # Standard libraries.
+  set(CEF_STANDARD_LIBS
+    -lpthread
+    "-framework Cocoa"
+    "-framework AppKit"
+    )
+
+  # Find the newest available base SDK.
+  execute_process(COMMAND xcode-select --print-path OUTPUT_VARIABLE XCODE_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
+  foreach(OS_VERSION 10.11 10.10 10.9)
+    set(SDK "${XCODE_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${OS_VERSION}.sdk")
+    if(NOT "${CMAKE_OSX_SYSROOT}" AND EXISTS "${SDK}" AND IS_DIRECTORY "${SDK}")
+      set(CMAKE_OSX_SYSROOT ${SDK})
+    endif()
+  endforeach()
+
+  # Target SDK.
+  set(CEF_TARGET_SDK               "10.9")
+  list(APPEND CEF_COMPILER_FLAGS
+    -mmacosx-version-min=${CEF_TARGET_SDK}
+  )
+  set(CMAKE_OSX_DEPLOYMENT_TARGET  ${CEF_TARGET_SDK})
+
+  # Target architecture.
+  if(PROJECT_ARCH STREQUAL "x86_64")
+    set(CMAKE_OSX_ARCHITECTURES "x86_64")
+  else()
+    set(CMAKE_OSX_ARCHITECTURES "i386")
+  endif()
+
+  # Prevent Xcode 11 from doing automatic codesigning.
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+
+  # CEF directory paths.
+  set(CEF_BINARY_DIR          "${_CEF_ROOT}/$<CONFIGURATION>")
+  set(CEF_BINARY_DIR_DEBUG    "${_CEF_ROOT}/Debug")
+  set(CEF_BINARY_DIR_RELEASE  "${_CEF_ROOT}/Release")
+
+  if(USE_SANDBOX)
+    list(APPEND CEF_COMPILER_DEFINES
+      CEF_USE_SANDBOX   # Used by apps to test if the sandbox is enabled
+      )
+
+    # CEF sandbox library paths.
+    set(CEF_SANDBOX_LIB_DEBUG "${CEF_BINARY_DIR_DEBUG}/cef_sandbox.a")
+    set(CEF_SANDBOX_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/cef_sandbox.a")
+  endif()
+
+  # CEF Helper app suffixes.
+  # Format is "<name suffix>:<target suffix>:<plist suffix>".
+  set(CEF_HELPER_APP_SUFFIXES
+    "::"
+    " (GPU):_gpu:.gpu"
+    " (Plugin):_plugin:.plugin"
+    " (Renderer):_renderer:.renderer"
+    )
+endif()
+
+
+#
+# Windows configuration.
+#
+
+if(OS_WINDOWS)
+  if (GEN_NINJA)
+    # When using the Ninja generator clear the CMake defaults to avoid excessive
+    # console warnings (see issue #2120).
+    set(CMAKE_CXX_FLAGS "")
+    set(CMAKE_CXX_FLAGS_DEBUG "")
+    set(CMAKE_CXX_FLAGS_RELEASE "")
+  endif()
+
+  if(USE_SANDBOX)
+    # Check if the current MSVC version is compatible with the cef_sandbox.lib
+    # static library. For a list of all version numbers see
+    # https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering
+    list(APPEND supported_msvc_versions
+      1900  # VS2015 and updates 1, 2, & 3
+      1910  # VS2017 version 15.1 & 15.2
+      1911  # VS2017 version 15.3 & 15.4
+      1912  # VS2017 version 15.5
+      1913  # VS2017 version 15.6
+      1914  # VS2017 version 15.7
+      1915  # VS2017 version 15.8
+      1916  # VS2017 version 15.9
+      1920  # VS2019 version 16.0
+      1921  # VS2019 version 16.1
+      1922  # VS2019 version 16.2
+      1923  # VS2019 version 16.3
+      1924  # VS2019 version 16.4
+      )
+    list(FIND supported_msvc_versions ${MSVC_VERSION} _index)
+    if (${_index} EQUAL -1)
+      message(WARNING "CEF sandbox is not compatible with the current MSVC version (${MSVC_VERSION})")
+      set(USE_SANDBOX OFF)
+    endif()
+  endif()
+
+  # Consumers who run into LNK4099 warnings can pass /Z7 instead (see issue #385).
+  set(CEF_DEBUG_INFO_FLAG "/Zi" CACHE STRING "Optional flag specifying specific /Z flag to use")
+
+  # Consumers using different runtime types may want to pass different flags
+  set(CEF_RUNTIME_LIBRARY_FLAG "/MT" CACHE STRING "Optional flag specifying which runtime to use")
+  if (CEF_RUNTIME_LIBRARY_FLAG)
+    list(APPEND CEF_COMPILER_FLAGS_DEBUG ${CEF_RUNTIME_LIBRARY_FLAG}d)
+    list(APPEND CEF_COMPILER_FLAGS_RELEASE ${CEF_RUNTIME_LIBRARY_FLAG})
+  endif()
+
+  # Platform-specific compiler/linker flags.
+  set(CEF_LIBTYPE STATIC)
+  list(APPEND CEF_COMPILER_FLAGS
+    /MP           # Multiprocess compilation
+    /Gy           # Enable function-level linking
+    /GR-          # Disable run-time type information
+    /W4           # Warning level 4
+    /WX           # Treat warnings as errors
+    /wd4100       # Ignore "unreferenced formal parameter" warning
+    /wd4127       # Ignore "conditional expression is constant" warning
+    /wd4244       # Ignore "conversion possible loss of data" warning
+    /wd4481       # Ignore "nonstandard extension used: override" warning
+    /wd4512       # Ignore "assignment operator could not be generated" warning
+    /wd4701       # Ignore "potentially uninitialized local variable" warning
+    /wd4702       # Ignore "unreachable code" warning
+    /wd4996       # Ignore "function or variable may be unsafe" warning
+    ${CEF_DEBUG_INFO_FLAG}
+    )
+  list(APPEND CEF_COMPILER_FLAGS_DEBUG
+    /RTC1         # Disable optimizations
+    /Od           # Enable basic run-time checks
+    )
+  list(APPEND CEF_COMPILER_FLAGS_RELEASE
+    /O2           # Optimize for maximum speed
+    /Ob2          # Inline any suitable function
+    /GF           # Enable string pooling
+    )
+  list(APPEND CEF_LINKER_FLAGS_DEBUG
+    /DEBUG        # Generate debug information
+    )
+  list(APPEND CEF_EXE_LINKER_FLAGS
+    /MANIFEST:NO        # No default manifest (see ADD_WINDOWS_MANIFEST macro usage)
+    /LARGEADDRESSAWARE  # Allow 32-bit processes to access 3GB of RAM
+    )
+  list(APPEND CEF_COMPILER_DEFINES
+    WIN32 _WIN32 _WINDOWS             # Windows platform
+    UNICODE _UNICODE                  # Unicode build
+    WINVER=0x0601 _WIN32_WINNT=0x601  # Targeting Windows 7
+    NOMINMAX                          # Use the standard's templated min/max
+    WIN32_LEAN_AND_MEAN               # Exclude less common API declarations
+    _HAS_EXCEPTIONS=0                 # Disable exceptions
+    )
+  list(APPEND CEF_COMPILER_DEFINES_RELEASE
+    NDEBUG _NDEBUG                    # Not a debug build
+    )
+
+  # Standard libraries.
+  set(CEF_STANDARD_LIBS
+    comctl32.lib
+    rpcrt4.lib
+    shlwapi.lib
+    ws2_32.lib
+    )
+
+  # CEF directory paths.
+  set(CEF_RESOURCE_DIR        "${_CEF_ROOT}/Resources")
+  set(CEF_BINARY_DIR          "${_CEF_ROOT}/$<CONFIGURATION>")
+  set(CEF_BINARY_DIR_DEBUG    "${_CEF_ROOT}/Debug")
+  set(CEF_BINARY_DIR_RELEASE  "${_CEF_ROOT}/Release")
+
+  # CEF library paths.
+  set(CEF_LIB_DEBUG   "${CEF_BINARY_DIR_DEBUG}/libcef.lib")
+  set(CEF_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/libcef.lib")
+
+  # List of CEF binary files.
+  set(CEF_BINARY_FILES
+    chrome_elf.dll
+    d3dcompiler_47.dll
+    libcef.dll
+    libEGL.dll
+    libGLESv2.dll
+    snapshot_blob.bin
+    v8_context_snapshot.bin
+    swiftshader
+    )
+
+  # List of CEF resource files.
+  set(CEF_RESOURCE_FILES
+    cef.pak
+    cef_100_percent.pak
+    cef_200_percent.pak
+    cef_extensions.pak
+    devtools_resources.pak
+    icudtl.dat
+    locales
+    )
+
+  if(USE_SANDBOX)
+    list(APPEND CEF_COMPILER_DEFINES
+      PSAPI_VERSION=1   # Required by cef_sandbox.lib
+      CEF_USE_SANDBOX   # Used by apps to test if the sandbox is enabled
+      )
+
+    # Libraries required by cef_sandbox.lib.
+    set(CEF_SANDBOX_STANDARD_LIBS
+      dbghelp.lib
+      Delayimp.lib
+      PowrProf.lib
+      Propsys.lib
+      psapi.lib
+      SetupAPI.lib
+      version.lib
+      wbemuuid.lib
+      winmm.lib
+      )
+
+    # CEF sandbox library paths.
+    set(CEF_SANDBOX_LIB_DEBUG "${CEF_BINARY_DIR_DEBUG}/cef_sandbox.lib")
+    set(CEF_SANDBOX_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/cef_sandbox.lib")
+  endif()
+
+  # Configure use of ATL.
+  option(USE_ATL "Enable or disable use of ATL." ON)
+  if(USE_ATL)
+    # Locate the atlmfc directory if it exists. It may be at any depth inside
+    # the VC directory. The cl.exe path returned by CMAKE_CXX_COMPILER may also
+    # be at different depths depending on the toolchain version
+    # (e.g. "VC/bin/cl.exe", "VC/bin/amd64_x86/cl.exe",
+    # "VC/Tools/MSVC/14.10.25017/bin/HostX86/x86/cl.exe", etc).
+    set(HAS_ATLMFC 0)
+    get_filename_component(VC_DIR ${CMAKE_CXX_COMPILER} DIRECTORY)
+    get_filename_component(VC_DIR_NAME ${VC_DIR} NAME)
+    while(NOT ${VC_DIR_NAME} STREQUAL "VC")
+      get_filename_component(VC_DIR ${VC_DIR} DIRECTORY)
+      if(IS_DIRECTORY "${VC_DIR}/atlmfc")
+        set(HAS_ATLMFC 1)
+        break()
+      endif()
+      get_filename_component(VC_DIR_NAME ${VC_DIR} NAME)
+    endwhile()
+
+    # Determine if the Visual Studio install supports ATL.
+    if(NOT HAS_ATLMFC)
+      message(WARNING "ATL is not supported by your VC installation.")
+      set(USE_ATL OFF)
+    endif()
+  endif()
+
+  if(USE_ATL)
+    list(APPEND CEF_COMPILER_DEFINES
+      CEF_USE_ATL   # Used by apps to test if ATL support is enabled
+      )
+  endif()
+endif()
diff --git a/src/include/base/cef_atomic_ref_count.h b/src/include/base/cef_atomic_ref_count.h
new file mode 100644
index 0000000..4d67779
--- /dev/null
+++ b/src/include/base/cef_atomic_ref_count.h
@@ -0,0 +1,165 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This is a low level implementation of atomic semantics for reference
+// counting.  Please use cef_ref_counted.h directly instead.
+//
+// The Chromium implementation includes annotations to avoid some false
+// positives when using data race detection tools. Annotations are not
+// currently supported by the CEF implementation.
+
+#ifndef CEF_INCLUDE_BASE_CEF_ATOMIC_REF_COUNT_H_
+#define CEF_INCLUDE_BASE_CEF_ATOMIC_REF_COUNT_H_
+#pragma once
+
+#if defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/atomic_ref_count.h"
+
+// Used when declaring a base::AtomicRefCount value. This is an object type with
+// Chromium headers.
+#define ATOMIC_DECLARATION (0)
+
+// Maintaining compatibility with AtompicRefCount* functions that were removed
+// from Chromium in http://crrev.com/ee96d561.
+namespace base {
+
+// Increment a reference count by 1.
+inline void AtomicRefCountInc(volatile AtomicRefCount* ptr) {
+  const_cast<AtomicRefCount*>(ptr)->Increment();
+}
+
+// Decrement a reference count by 1 and return whether the result is non-zero.
+// Insert barriers to ensure that state written before the reference count
+// became zero will be visible to a thread that has just made the count zero.
+inline bool AtomicRefCountDec(volatile AtomicRefCount* ptr) {
+  return const_cast<AtomicRefCount*>(ptr)->Decrement();
+}
+
+// Return whether the reference count is one.  If the reference count is used
+// in the conventional way, a refrerence count of 1 implies that the current
+// thread owns the reference and no other thread shares it.  This call performs
+// the test for a reference count of one, and performs the memory barrier
+// needed for the owning thread to act on the object, knowing that it has
+// exclusive access to the object.
+inline bool AtomicRefCountIsOne(volatile AtomicRefCount* ptr) {
+  return const_cast<AtomicRefCount*>(ptr)->IsOne();
+}
+
+// Return whether the reference count is zero.  With conventional object
+// referencing counting, the object will be destroyed, so the reference count
+// should never be zero.  Hence this is generally used for a debug check.
+inline bool AtomicRefCountIsZero(volatile AtomicRefCount* ptr) {
+  return const_cast<AtomicRefCount*>(ptr)->IsZero();
+}
+
+}  // namespace base
+
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_atomicops.h"
+
+// Annotations are not currently supported.
+#define ANNOTATE_HAPPENS_BEFORE(obj) /* empty */
+#define ANNOTATE_HAPPENS_AFTER(obj)  /* empty */
+
+// Used when declaring a base::AtomicRefCount value. This is an integer/ptr type
+// with CEF headers.
+#define ATOMIC_DECLARATION = 0
+
+namespace base {
+
+typedef subtle::Atomic32 AtomicRefCount;
+
+// Increment a reference count by "increment", which must exceed 0.
+inline void AtomicRefCountIncN(volatile AtomicRefCount* ptr,
+                               AtomicRefCount increment) {
+  subtle::NoBarrier_AtomicIncrement(ptr, increment);
+}
+
+// Decrement a reference count by "decrement", which must exceed 0,
+// and return whether the result is non-zero.
+// Insert barriers to ensure that state written before the reference count
+// became zero will be visible to a thread that has just made the count zero.
+inline bool AtomicRefCountDecN(volatile AtomicRefCount* ptr,
+                               AtomicRefCount decrement) {
+  ANNOTATE_HAPPENS_BEFORE(ptr);
+  bool res = (subtle::Barrier_AtomicIncrement(ptr, -decrement) != 0);
+  if (!res) {
+    ANNOTATE_HAPPENS_AFTER(ptr);
+  }
+  return res;
+}
+
+// Increment a reference count by 1.
+inline void AtomicRefCountInc(volatile AtomicRefCount* ptr) {
+  base::AtomicRefCountIncN(ptr, 1);
+}
+
+// Decrement a reference count by 1 and return whether the result is non-zero.
+// Insert barriers to ensure that state written before the reference count
+// became zero will be visible to a thread that has just made the count zero.
+inline bool AtomicRefCountDec(volatile AtomicRefCount* ptr) {
+  return base::AtomicRefCountDecN(ptr, 1);
+}
+
+// Return whether the reference count is one.  If the reference count is used
+// in the conventional way, a refrerence count of 1 implies that the current
+// thread owns the reference and no other thread shares it.  This call performs
+// the test for a reference count of one, and performs the memory barrier
+// needed for the owning thread to act on the object, knowing that it has
+// exclusive access to the object.
+inline bool AtomicRefCountIsOne(volatile AtomicRefCount* ptr) {
+  bool res = (subtle::Acquire_Load(ptr) == 1);
+  if (res) {
+    ANNOTATE_HAPPENS_AFTER(ptr);
+  }
+  return res;
+}
+
+// Return whether the reference count is zero.  With conventional object
+// referencing counting, the object will be destroyed, so the reference count
+// should never be zero.  Hence this is generally used for a debug check.
+inline bool AtomicRefCountIsZero(volatile AtomicRefCount* ptr) {
+  bool res = (subtle::Acquire_Load(ptr) == 0);
+  if (res) {
+    ANNOTATE_HAPPENS_AFTER(ptr);
+  }
+  return res;
+}
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_ATOMIC_REF_COUNT_H_
diff --git a/src/include/base/cef_atomicops.h b/src/include/base/cef_atomicops.h
new file mode 100644
index 0000000..a5b6459
--- /dev/null
+++ b/src/include/base/cef_atomicops.h
@@ -0,0 +1,203 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// For atomic operations on reference counts, see cef_atomic_ref_count.h.
+
+// The routines exported by this module are subtle.  If you use them, even if
+// you get the code right, it will depend on careful reasoning about atomicity
+// and memory ordering; it will be less readable, and harder to maintain.  If
+// you plan to use these routines, you should have a good reason, such as solid
+// evidence that performance would otherwise suffer, or there being no
+// alternative.  You should assume only properties explicitly guaranteed by the
+// specifications in this file.  You are almost certainly _not_ writing code
+// just for the x86; if you assume x86 semantics, x86 hardware bugs and
+// implementations on other archtectures will cause your code to break.  If you
+// do not know what you are doing, avoid these routines, and use a Mutex.
+//
+// It is incorrect to make direct assignments to/from an atomic variable.
+// You should use one of the Load or Store routines.  The NoBarrier
+// versions are provided when no barriers are needed:
+//   NoBarrier_Store()
+//   NoBarrier_Load()
+// Although there are currently no compiler enforcement, you are encouraged
+// to use these.
+//
+
+#ifndef CEF_INCLUDE_BASE_CEF_ATOMICOPS_H_
+#define CEF_INCLUDE_BASE_CEF_ATOMICOPS_H_
+#pragma once
+
+#if defined(BASE_ATOMICOPS_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/atomicops.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include <stdint.h>
+
+#include "include/base/cef_build.h"
+
+#if defined(OS_WIN) && defined(ARCH_CPU_64_BITS)
+// windows.h #defines this (only on x64). This causes problems because the
+// public API also uses MemoryBarrier at the public name for this fence. So, on
+// X64, undef it, and call its documented
+// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
+// implementation directly.
+#undef MemoryBarrier
+#endif
+
+namespace base {
+namespace subtle {
+
+typedef int32_t Atomic32;
+#ifdef ARCH_CPU_64_BITS
+// We need to be able to go between Atomic64 and AtomicWord implicitly.  This
+// means Atomic64 and AtomicWord should be the same type on 64-bit.
+#if defined(__ILP32__) || defined(OS_NACL)
+// NaCl's intptr_t is not actually 64-bits on 64-bit!
+// http://code.google.com/p/nativeclient/issues/detail?id=1162
+typedef int64_t Atomic64;
+#else
+typedef intptr_t Atomic64;
+#endif
+#endif
+
+// Use AtomicWord for a machine-sized pointer.  It will use the Atomic32 or
+// Atomic64 routines below, depending on your architecture.
+typedef intptr_t AtomicWord;
+
+// Atomically execute:
+//      result = *ptr;
+//      if (*ptr == old_value)
+//        *ptr = new_value;
+//      return result;
+//
+// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
+// Always return the old value of "*ptr"
+//
+// This routine implies no memory barriers.
+Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                  Atomic32 old_value,
+                                  Atomic32 new_value);
+
+// Atomically store new_value into *ptr, returning the previous value held in
+// *ptr.  This routine implies no memory barriers.
+Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
+
+// Atomically increment *ptr by "increment".  Returns the new value of
+// *ptr with the increment applied.  This routine implies no memory barriers.
+Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment);
+
+Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment);
+
+// These following lower-level operations are typically useful only to people
+// implementing higher-level synchronization operations like spinlocks,
+// mutexes, and condition-variables.  They combine CompareAndSwap(), a load, or
+// a store with appropriate memory-ordering instructions.  "Acquire" operations
+// ensure that no later memory access can be reordered ahead of the operation.
+// "Release" operations ensure that no previous memory access can be reordered
+// after the operation.  "Barrier" operations have both "Acquire" and "Release"
+// semantics.   A MemoryBarrier() has "Barrier" semantics, but does no memory
+// access.
+Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                Atomic32 old_value,
+                                Atomic32 new_value);
+Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                Atomic32 old_value,
+                                Atomic32 new_value);
+
+void MemoryBarrier();
+void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
+void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
+void Release_Store(volatile Atomic32* ptr, Atomic32 value);
+
+Atomic32 NoBarrier_Load(volatile const Atomic32* ptr);
+Atomic32 Acquire_Load(volatile const Atomic32* ptr);
+Atomic32 Release_Load(volatile const Atomic32* ptr);
+
+// 64-bit atomic operations (only available on 64-bit processors).
+#ifdef ARCH_CPU_64_BITS
+Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                  Atomic64 old_value,
+                                  Atomic64 new_value);
+Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
+Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
+Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
+
+Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                Atomic64 old_value,
+                                Atomic64 new_value);
+Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                Atomic64 old_value,
+                                Atomic64 new_value);
+void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value);
+void Acquire_Store(volatile Atomic64* ptr, Atomic64 value);
+void Release_Store(volatile Atomic64* ptr, Atomic64 value);
+Atomic64 NoBarrier_Load(volatile const Atomic64* ptr);
+Atomic64 Acquire_Load(volatile const Atomic64* ptr);
+Atomic64 Release_Load(volatile const Atomic64* ptr);
+#endif  // ARCH_CPU_64_BITS
+
+}  // namespace subtle
+}  // namespace base
+
+// Include our platform specific implementation.
+#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY)
+#include "include/base/internal/cef_atomicops_x86_msvc.h"
+#elif defined(OS_WIN) && (defined(__ARM_ARCH_ISA_A64) || defined(_M_ARM64))
+#include "include/base/internal/cef_atomicops_arm64_msvc.h"
+#elif defined(OS_MACOSX)
+#include "include/base/internal/cef_atomicops_mac.h"
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+#include "include/base/internal/cef_atomicops_x86_gcc.h"
+#elif defined(COMPILER_GCC) && defined(__ARM_ARCH_ISA_A64)
+#include "include/base/internal/cef_atomicops_arm64_gcc.h"
+#elif defined(COMPILER_GCC) && defined(__ARM_ARCH)
+#include "include/base/internal/cef_atomicops_arm_gcc.h"
+#else
+#error "Atomic operations are not supported on your platform"
+#endif
+
+// On some platforms we need additional declarations to make
+// AtomicWord compatible with our other Atomic* types.
+#if defined(OS_MACOSX) || defined(OS_OPENBSD)
+#include "include/base/internal/cef_atomicops_atomicword_compat.h"
+#endif
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_ATOMICOPS_H_
diff --git a/src/include/base/cef_basictypes.h b/src/include/base/cef_basictypes.h
new file mode 100644
index 0000000..e38f4f7
--- /dev/null
+++ b/src/include/base/cef_basictypes.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_BASICTYPES_H_
+#define CEF_INCLUDE_BASE_CEF_BASICTYPES_H_
+#pragma once
+
+#include <limits.h>  // For UINT_MAX
+#include <stddef.h>  // For size_t
+
+#include "include/base/cef_build.h"
+
+// The NSPR system headers define 64-bit as |long| when possible, except on
+// Mac OS X.  In order to not have typedef mismatches, we do the same on LP64.
+//
+// On Mac OS X, |long long| is used for 64-bit types for compatibility with
+// <inttypes.h> format macros even in the LP64 model.
+#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+typedef long int64;
+typedef unsigned long uint64;
+#else
+typedef long long int64;
+typedef unsigned long long uint64;
+#endif
+
+// TODO: Remove these type guards.  These are to avoid conflicts with
+// obsolete/protypes.h in the Gecko SDK.
+#ifndef _INT32
+#define _INT32
+typedef int int32;
+#endif
+
+// TODO: Remove these type guards.  These are to avoid conflicts with
+// obsolete/protypes.h in the Gecko SDK.
+#ifndef _UINT32
+#define _UINT32
+typedef unsigned int uint32;
+#endif
+
+#ifndef _INT16
+#define _INT16
+typedef short int16;
+#endif
+
+#ifndef _UINT16
+#define _UINT16
+typedef unsigned short uint16;
+#endif
+
+// UTF-16 character type.
+// This should be kept synchronized with base/strings/string16.h
+#ifndef char16
+#if defined(WCHAR_T_IS_UTF16)
+typedef wchar_t char16;
+#elif defined(WCHAR_T_IS_UTF32)
+typedef unsigned short char16;
+#endif
+#endif
+
+#endif  // CEF_INCLUDE_BASE_CEF_BASICTYPES_H_
diff --git a/src/include/base/cef_bind.h b/src/include/base/cef_bind.h
new file mode 100644
index 0000000..77c9c55
--- /dev/null
+++ b/src/include/base/cef_bind.h
@@ -0,0 +1,575 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_BIND_H_
+#define CEF_INCLUDE_BASE_CEF_BIND_H_
+#pragma once
+
+#if defined(BASE_BIND_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/bind.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/internal/cef_bind_internal.h"
+#include "include/base/internal/cef_callback_internal.h"
+
+// -----------------------------------------------------------------------------
+// Usage documentation
+// -----------------------------------------------------------------------------
+//
+// See base/cef_callback.h for documentation.
+//
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// If you're reading the implementation, before proceeding further, you should
+// read the top comment of base/bind_internal.h for a definition of common
+// terms and concepts.
+//
+// RETURN TYPES
+//
+// Though Bind()'s result is meant to be stored in a Callback<> type, it
+// cannot actually return the exact type without requiring a large amount
+// of extra template specializations. The problem is that in order to
+// discern the correct specialization of Callback<>, Bind would need to
+// unwrap the function signature to determine the signature's arity, and
+// whether or not it is a method.
+//
+// Each unique combination of (arity, function_type, num_prebound) where
+// function_type is one of {function, method, const_method} would require
+// one specialization.  We eventually have to do a similar number of
+// specializations anyways in the implementation (see the Invoker<>,
+// classes).  However, it is avoidable in Bind if we return the result
+// via an indirection like we do below.
+//
+// TODO(ajwong): We might be able to avoid this now, but need to test.
+//
+// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>,
+// but it feels a little nicer to have the asserts here so people do not
+// need to crack open bind_internal.h.  On the other hand, it makes Bind()
+// harder to read.
+
+namespace base {
+
+template <typename Functor>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void()>::UnboundRunType>
+Bind(Functor functor) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  typedef cef_internal::BindState<RunnableType, RunType, void()> BindState;
+
+  return Callback<typename BindState::UnboundRunType>(
+      new BindState(cef_internal::MakeRunnable(functor)));
+}
+
+template <typename Functor, typename P1>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void(typename cef_internal::CallbackParamTraits<P1>::StorageType)>::
+                   UnboundRunType>
+Bind(Functor functor, const P1& p1) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  // Use RunnableType::RunType instead of RunType above because our
+  // checks should below for bound references need to know what the actual
+  // functor is going to interpret the argument as.
+  typedef cef_internal::FunctionTraits<typename RunnableType::RunType>
+      BoundFunctorTraits;
+
+  // Do not allow binding a non-const reference parameter. Non-const reference
+  // parameters are disallowed by the Google style guide.  Also, binding a
+  // non-const reference parameter can make for subtle bugs because the
+  // invoked function will receive a reference to the stored copy of the
+  // argument and not the original.
+  COMPILE_ASSERT(
+      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value),
+      do_not_bind_functions_with_nonconst_ref);
+
+  // For methods, we need to be careful for parameter 1.  We do not require
+  // a scoped_refptr because BindState<> itself takes care of AddRef() for
+  // methods. We also disallow binding of an array as the method's target
+  // object.
+  COMPILE_ASSERT(cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !cef_internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+                 p1_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !is_array<P1>::value,
+                 first_bound_argument_to_method_cannot_be_array);
+  typedef cef_internal::BindState<
+      RunnableType, RunType,
+      void(typename cef_internal::CallbackParamTraits<P1>::StorageType)>
+      BindState;
+
+  return Callback<typename BindState::UnboundRunType>(
+      new BindState(cef_internal::MakeRunnable(functor), p1));
+}
+
+template <typename Functor, typename P1, typename P2>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+         typename cef_internal::CallbackParamTraits<P2>::StorageType)>::
+                   UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  // Use RunnableType::RunType instead of RunType above because our
+  // checks should below for bound references need to know what the actual
+  // functor is going to interpret the argument as.
+  typedef cef_internal::FunctionTraits<typename RunnableType::RunType>
+      BoundFunctorTraits;
+
+  // Do not allow binding a non-const reference parameter. Non-const reference
+  // parameters are disallowed by the Google style guide.  Also, binding a
+  // non-const reference parameter can make for subtle bugs because the
+  // invoked function will receive a reference to the stored copy of the
+  // argument and not the original.
+  COMPILE_ASSERT(
+      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A2Type>::value),
+      do_not_bind_functions_with_nonconst_ref);
+
+  // For methods, we need to be careful for parameter 1.  We do not require
+  // a scoped_refptr because BindState<> itself takes care of AddRef() for
+  // methods. We also disallow binding of an array as the method's target
+  // object.
+  COMPILE_ASSERT(cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !cef_internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+                 p1_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !is_array<P1>::value,
+                 first_bound_argument_to_method_cannot_be_array);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+                 p2_is_refcounted_type_and_needs_scoped_refptr);
+  typedef cef_internal::BindState<
+      RunnableType, RunType,
+      void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+           typename cef_internal::CallbackParamTraits<P2>::StorageType)>
+      BindState;
+
+  return Callback<typename BindState::UnboundRunType>(
+      new BindState(cef_internal::MakeRunnable(functor), p1, p2));
+}
+
+template <typename Functor, typename P1, typename P2, typename P3>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+         typename cef_internal::CallbackParamTraits<P2>::StorageType,
+         typename cef_internal::CallbackParamTraits<P3>::StorageType)>::
+                   UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  // Use RunnableType::RunType instead of RunType above because our
+  // checks should below for bound references need to know what the actual
+  // functor is going to interpret the argument as.
+  typedef cef_internal::FunctionTraits<typename RunnableType::RunType>
+      BoundFunctorTraits;
+
+  // Do not allow binding a non-const reference parameter. Non-const reference
+  // parameters are disallowed by the Google style guide.  Also, binding a
+  // non-const reference parameter can make for subtle bugs because the
+  // invoked function will receive a reference to the stored copy of the
+  // argument and not the original.
+  COMPILE_ASSERT(
+      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A3Type>::value),
+      do_not_bind_functions_with_nonconst_ref);
+
+  // For methods, we need to be careful for parameter 1.  We do not require
+  // a scoped_refptr because BindState<> itself takes care of AddRef() for
+  // methods. We also disallow binding of an array as the method's target
+  // object.
+  COMPILE_ASSERT(cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !cef_internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+                 p1_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !is_array<P1>::value,
+                 first_bound_argument_to_method_cannot_be_array);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+                 p2_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+                 p3_is_refcounted_type_and_needs_scoped_refptr);
+  typedef cef_internal::BindState<
+      RunnableType, RunType,
+      void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+           typename cef_internal::CallbackParamTraits<P2>::StorageType,
+           typename cef_internal::CallbackParamTraits<P3>::StorageType)>
+      BindState;
+
+  return Callback<typename BindState::UnboundRunType>(
+      new BindState(cef_internal::MakeRunnable(functor), p1, p2, p3));
+}
+
+template <typename Functor, typename P1, typename P2, typename P3, typename P4>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+         typename cef_internal::CallbackParamTraits<P2>::StorageType,
+         typename cef_internal::CallbackParamTraits<P3>::StorageType,
+         typename cef_internal::CallbackParamTraits<P4>::StorageType)>::
+                   UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  // Use RunnableType::RunType instead of RunType above because our
+  // checks should below for bound references need to know what the actual
+  // functor is going to interpret the argument as.
+  typedef cef_internal::FunctionTraits<typename RunnableType::RunType>
+      BoundFunctorTraits;
+
+  // Do not allow binding a non-const reference parameter. Non-const reference
+  // parameters are disallowed by the Google style guide.  Also, binding a
+  // non-const reference parameter can make for subtle bugs because the
+  // invoked function will receive a reference to the stored copy of the
+  // argument and not the original.
+  COMPILE_ASSERT(
+      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A4Type>::value),
+      do_not_bind_functions_with_nonconst_ref);
+
+  // For methods, we need to be careful for parameter 1.  We do not require
+  // a scoped_refptr because BindState<> itself takes care of AddRef() for
+  // methods. We also disallow binding of an array as the method's target
+  // object.
+  COMPILE_ASSERT(cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !cef_internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+                 p1_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !is_array<P1>::value,
+                 first_bound_argument_to_method_cannot_be_array);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+                 p2_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+                 p3_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+                 p4_is_refcounted_type_and_needs_scoped_refptr);
+  typedef cef_internal::BindState<
+      RunnableType, RunType,
+      void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+           typename cef_internal::CallbackParamTraits<P2>::StorageType,
+           typename cef_internal::CallbackParamTraits<P3>::StorageType,
+           typename cef_internal::CallbackParamTraits<P4>::StorageType)>
+      BindState;
+
+  return Callback<typename BindState::UnboundRunType>(
+      new BindState(cef_internal::MakeRunnable(functor), p1, p2, p3, p4));
+}
+
+template <typename Functor,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+         typename cef_internal::CallbackParamTraits<P2>::StorageType,
+         typename cef_internal::CallbackParamTraits<P3>::StorageType,
+         typename cef_internal::CallbackParamTraits<P4>::StorageType,
+         typename cef_internal::CallbackParamTraits<P5>::StorageType)>::
+                   UnboundRunType>
+Bind(Functor functor,
+     const P1& p1,
+     const P2& p2,
+     const P3& p3,
+     const P4& p4,
+     const P5& p5) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  // Use RunnableType::RunType instead of RunType above because our
+  // checks should below for bound references need to know what the actual
+  // functor is going to interpret the argument as.
+  typedef cef_internal::FunctionTraits<typename RunnableType::RunType>
+      BoundFunctorTraits;
+
+  // Do not allow binding a non-const reference parameter. Non-const reference
+  // parameters are disallowed by the Google style guide.  Also, binding a
+  // non-const reference parameter can make for subtle bugs because the
+  // invoked function will receive a reference to the stored copy of the
+  // argument and not the original.
+  COMPILE_ASSERT(
+      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A5Type>::value),
+      do_not_bind_functions_with_nonconst_ref);
+
+  // For methods, we need to be careful for parameter 1.  We do not require
+  // a scoped_refptr because BindState<> itself takes care of AddRef() for
+  // methods. We also disallow binding of an array as the method's target
+  // object.
+  COMPILE_ASSERT(cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !cef_internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+                 p1_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !is_array<P1>::value,
+                 first_bound_argument_to_method_cannot_be_array);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+                 p2_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+                 p3_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+                 p4_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P5>::value,
+                 p5_is_refcounted_type_and_needs_scoped_refptr);
+  typedef cef_internal::BindState<
+      RunnableType, RunType,
+      void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+           typename cef_internal::CallbackParamTraits<P2>::StorageType,
+           typename cef_internal::CallbackParamTraits<P3>::StorageType,
+           typename cef_internal::CallbackParamTraits<P4>::StorageType,
+           typename cef_internal::CallbackParamTraits<P5>::StorageType)>
+      BindState;
+
+  return Callback<typename BindState::UnboundRunType>(
+      new BindState(cef_internal::MakeRunnable(functor), p1, p2, p3, p4, p5));
+}
+
+template <typename Functor,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5,
+          typename P6>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+         typename cef_internal::CallbackParamTraits<P2>::StorageType,
+         typename cef_internal::CallbackParamTraits<P3>::StorageType,
+         typename cef_internal::CallbackParamTraits<P4>::StorageType,
+         typename cef_internal::CallbackParamTraits<P5>::StorageType,
+         typename cef_internal::CallbackParamTraits<P6>::StorageType)>::
+                   UnboundRunType>
+Bind(Functor functor,
+     const P1& p1,
+     const P2& p2,
+     const P3& p3,
+     const P4& p4,
+     const P5& p5,
+     const P6& p6) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  // Use RunnableType::RunType instead of RunType above because our
+  // checks should below for bound references need to know what the actual
+  // functor is going to interpret the argument as.
+  typedef cef_internal::FunctionTraits<typename RunnableType::RunType>
+      BoundFunctorTraits;
+
+  // Do not allow binding a non-const reference parameter. Non-const reference
+  // parameters are disallowed by the Google style guide.  Also, binding a
+  // non-const reference parameter can make for subtle bugs because the
+  // invoked function will receive a reference to the stored copy of the
+  // argument and not the original.
+  COMPILE_ASSERT(
+      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A5Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A6Type>::value),
+      do_not_bind_functions_with_nonconst_ref);
+
+  // For methods, we need to be careful for parameter 1.  We do not require
+  // a scoped_refptr because BindState<> itself takes care of AddRef() for
+  // methods. We also disallow binding of an array as the method's target
+  // object.
+  COMPILE_ASSERT(cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !cef_internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+                 p1_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !is_array<P1>::value,
+                 first_bound_argument_to_method_cannot_be_array);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+                 p2_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+                 p3_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+                 p4_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P5>::value,
+                 p5_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P6>::value,
+                 p6_is_refcounted_type_and_needs_scoped_refptr);
+  typedef cef_internal::BindState<
+      RunnableType, RunType,
+      void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+           typename cef_internal::CallbackParamTraits<P2>::StorageType,
+           typename cef_internal::CallbackParamTraits<P3>::StorageType,
+           typename cef_internal::CallbackParamTraits<P4>::StorageType,
+           typename cef_internal::CallbackParamTraits<P5>::StorageType,
+           typename cef_internal::CallbackParamTraits<P6>::StorageType)>
+      BindState;
+
+  return Callback<typename BindState::UnboundRunType>(new BindState(
+      cef_internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6));
+}
+
+template <typename Functor,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5,
+          typename P6,
+          typename P7>
+base::Callback<typename cef_internal::BindState<
+    typename cef_internal::FunctorTraits<Functor>::RunnableType,
+    typename cef_internal::FunctorTraits<Functor>::RunType,
+    void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+         typename cef_internal::CallbackParamTraits<P2>::StorageType,
+         typename cef_internal::CallbackParamTraits<P3>::StorageType,
+         typename cef_internal::CallbackParamTraits<P4>::StorageType,
+         typename cef_internal::CallbackParamTraits<P5>::StorageType,
+         typename cef_internal::CallbackParamTraits<P6>::StorageType,
+         typename cef_internal::CallbackParamTraits<P7>::StorageType)>::
+                   UnboundRunType>
+Bind(Functor functor,
+     const P1& p1,
+     const P2& p2,
+     const P3& p3,
+     const P4& p4,
+     const P5& p5,
+     const P6& p6,
+     const P7& p7) {
+  // Typedefs for how to store and run the functor.
+  typedef
+      typename cef_internal::FunctorTraits<Functor>::RunnableType RunnableType;
+  typedef typename cef_internal::FunctorTraits<Functor>::RunType RunType;
+
+  // Use RunnableType::RunType instead of RunType above because our
+  // checks should below for bound references need to know what the actual
+  // functor is going to interpret the argument as.
+  typedef cef_internal::FunctionTraits<typename RunnableType::RunType>
+      BoundFunctorTraits;
+
+  // Do not allow binding a non-const reference parameter. Non-const reference
+  // parameters are disallowed by the Google style guide.  Also, binding a
+  // non-const reference parameter can make for subtle bugs because the
+  // invoked function will receive a reference to the stored copy of the
+  // argument and not the original.
+  COMPILE_ASSERT(
+      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A5Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A6Type>::value ||
+        is_non_const_reference<typename BoundFunctorTraits::A7Type>::value),
+      do_not_bind_functions_with_nonconst_ref);
+
+  // For methods, we need to be careful for parameter 1.  We do not require
+  // a scoped_refptr because BindState<> itself takes care of AddRef() for
+  // methods. We also disallow binding of an array as the method's target
+  // object.
+  COMPILE_ASSERT(cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !cef_internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+                 p1_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::HasIsMethodTag<RunnableType>::value ||
+                     !is_array<P1>::value,
+                 first_bound_argument_to_method_cannot_be_array);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+                 p2_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+                 p3_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+                 p4_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P5>::value,
+                 p5_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P6>::value,
+                 p6_is_refcounted_type_and_needs_scoped_refptr);
+  COMPILE_ASSERT(!cef_internal::NeedsScopedRefptrButGetsRawPtr<P7>::value,
+                 p7_is_refcounted_type_and_needs_scoped_refptr);
+  typedef cef_internal::BindState<
+      RunnableType, RunType,
+      void(typename cef_internal::CallbackParamTraits<P1>::StorageType,
+           typename cef_internal::CallbackParamTraits<P2>::StorageType,
+           typename cef_internal::CallbackParamTraits<P3>::StorageType,
+           typename cef_internal::CallbackParamTraits<P4>::StorageType,
+           typename cef_internal::CallbackParamTraits<P5>::StorageType,
+           typename cef_internal::CallbackParamTraits<P6>::StorageType,
+           typename cef_internal::CallbackParamTraits<P7>::StorageType)>
+      BindState;
+
+  return Callback<typename BindState::UnboundRunType>(new BindState(
+      cef_internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6, p7));
+}
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_BIND_H_
diff --git a/src/include/base/cef_bind_helpers.h b/src/include/base/cef_bind_helpers.h
new file mode 100644
index 0000000..2b4798b
--- /dev/null
+++ b/src/include/base/cef_bind_helpers.h
@@ -0,0 +1,579 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This defines a set of argument wrappers and related factory methods that
+// can be used specify the refcounting and reference semantics of arguments
+// that are bound by the Bind() function in base/bind.h.
+//
+// It also defines a set of simple functions and utilities that people want
+// when using Callback<> and Bind().
+//
+//
+// ARGUMENT BINDING WRAPPERS
+//
+// The wrapper functions are base::Unretained(), base::Owned(), base::Passed(),
+// base::ConstRef(), and base::IgnoreResult().
+//
+// Unretained() allows Bind() to bind a non-refcounted class, and to disable
+// refcounting on arguments that are refcounted objects.
+//
+// Owned() transfers ownership of an object to the Callback resulting from
+// bind; the object will be deleted when the Callback is deleted.
+//
+// Passed() is for transferring movable-but-not-copyable types (eg. scoped_ptr)
+// through a Callback. Logically, this signifies a destructive transfer of
+// the state of the argument into the target function.  Invoking
+// Callback::Run() twice on a Callback that was created with a Passed()
+// argument will CHECK() because the first invocation would have already
+// transferred ownership to the target function.
+//
+// ConstRef() allows binding a constant reference to an argument rather
+// than a copy.
+//
+// IgnoreResult() is used to adapt a function or Callback with a return type to
+// one with a void return. This is most useful if you have a function with,
+// say, a pesky ignorable bool return that you want to use with PostTask or
+// something else that expect a Callback with a void return.
+//
+// EXAMPLE OF Unretained():
+//
+//   class Foo {
+//    public:
+//     void func() { cout << "Foo:f" << endl; }
+//   };
+//
+//   // In some function somewhere.
+//   Foo foo;
+//   Closure foo_callback =
+//       Bind(&Foo::func, Unretained(&foo));
+//   foo_callback.Run();  // Prints "Foo:f".
+//
+// Without the Unretained() wrapper on |&foo|, the above call would fail
+// to compile because Foo does not support the AddRef() and Release() methods.
+//
+//
+// EXAMPLE OF Owned():
+//
+//   void foo(int* arg) { cout << *arg << endl }
+//
+//   int* pn = new int(1);
+//   Closure foo_callback = Bind(&foo, Owned(pn));
+//
+//   foo_callback.Run();  // Prints "1"
+//   foo_callback.Run();  // Prints "1"
+//   *n = 2;
+//   foo_callback.Run();  // Prints "2"
+//
+//   foo_callback.Reset();  // |pn| is deleted.  Also will happen when
+//                          // |foo_callback| goes out of scope.
+//
+// Without Owned(), someone would have to know to delete |pn| when the last
+// reference to the Callback is deleted.
+//
+//
+// EXAMPLE OF ConstRef():
+//
+//   void foo(int arg) { cout << arg << endl }
+//
+//   int n = 1;
+//   Closure no_ref = Bind(&foo, n);
+//   Closure has_ref = Bind(&foo, ConstRef(n));
+//
+//   no_ref.Run();  // Prints "1"
+//   has_ref.Run();  // Prints "1"
+//
+//   n = 2;
+//   no_ref.Run();  // Prints "1"
+//   has_ref.Run();  // Prints "2"
+//
+// Note that because ConstRef() takes a reference on |n|, |n| must outlive all
+// its bound callbacks.
+//
+//
+// EXAMPLE OF IgnoreResult():
+//
+//   int DoSomething(int arg) { cout << arg << endl; }
+//
+//   // Assign to a Callback with a void return type.
+//   Callback<void(int)> cb = Bind(IgnoreResult(&DoSomething));
+//   cb->Run(1);  // Prints "1".
+//
+//   // Prints "1" on |ml|.
+//   ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1);
+//
+//
+// EXAMPLE OF Passed():
+//
+//   void TakesOwnership(scoped_ptr<Foo> arg) { }
+//   scoped_ptr<Foo> CreateFoo() { return scoped_ptr<Foo>(new Foo()); }
+//
+//   scoped_ptr<Foo> f(new Foo());
+//
+//   // |cb| is given ownership of Foo(). |f| is now NULL.
+//   // You can use f.Pass() in place of &f, but it's more verbose.
+//   Closure cb = Bind(&TakesOwnership, Passed(&f));
+//
+//   // Run was never called so |cb| still owns Foo() and deletes
+//   // it on Reset().
+//   cb.Reset();
+//
+//   // |cb| is given a new Foo created by CreateFoo().
+//   cb = Bind(&TakesOwnership, Passed(CreateFoo()));
+//
+//   // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
+//   // no longer owns Foo() and, if reset, would not delete Foo().
+//   cb.Run();  // Foo() is now transferred to |arg| and deleted.
+//   cb.Run();  // This CHECK()s since Foo() already been used once.
+//
+// Passed() is particularly useful with PostTask() when you are transferring
+// ownership of an argument into a task, but don't necessarily know if the
+// task will always be executed. This can happen if the task is cancellable
+// or if it is posted to a MessageLoopProxy.
+//
+//
+// SIMPLE FUNCTIONS AND UTILITIES.
+//
+//   DoNothing() - Useful for creating a Closure that does nothing when called.
+//   DeletePointer<T>() - Useful for creating a Closure that will delete a
+//                        pointer when invoked. Only use this when necessary.
+//                        In most cases MessageLoop::DeleteSoon() is a better
+//                        fit.
+
+#ifndef CEF_INCLUDE_BASE_CEF_BIND_HELPERS_H_
+#define CEF_INCLUDE_BASE_CEF_BIND_HELPERS_H_
+#pragma once
+
+#if defined(BASE_BIND_HELPERS_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/bind_helpers.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_basictypes.h"
+#include "include/base/cef_callback.h"
+#include "include/base/cef_template_util.h"
+#include "include/base/cef_weak_ptr.h"
+
+namespace base {
+namespace cef_internal {
+
+// Use the Substitution Failure Is Not An Error (SFINAE) trick to inspect T
+// for the existence of AddRef() and Release() functions of the correct
+// signature.
+//
+// http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
+// http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
+// http://stackoverflow.com/questions/4358584/sfinae-approach-comparison
+// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions
+//
+// The last link in particular show the method used below.
+//
+// For SFINAE to work with inherited methods, we need to pull some extra tricks
+// with multiple inheritance.  In the more standard formulation, the overloads
+// of Check would be:
+//
+//   template <typename C>
+//   Yes NotTheCheckWeWant(Helper<&C::TargetFunc>*);
+//
+//   template <typename C>
+//   No NotTheCheckWeWant(...);
+//
+//   static const bool value = sizeof(NotTheCheckWeWant<T>(0)) == sizeof(Yes);
+//
+// The problem here is that template resolution will not match
+// C::TargetFunc if TargetFunc does not exist directly in C.  That is, if
+// TargetFunc in inherited from an ancestor, &C::TargetFunc will not match,
+// |value| will be false.  This formulation only checks for whether or
+// not TargetFunc exist directly in the class being introspected.
+//
+// To get around this, we play a dirty trick with multiple inheritance.
+// First, We create a class BaseMixin that declares each function that we
+// want to probe for.  Then we create a class Base that inherits from both T
+// (the class we wish to probe) and BaseMixin.  Note that the function
+// signature in BaseMixin does not need to match the signature of the function
+// we are probing for; thus it's easiest to just use void(void).
+//
+// Now, if TargetFunc exists somewhere in T, then &Base::TargetFunc has an
+// ambiguous resolution between BaseMixin and T.  This lets us write the
+// following:
+//
+//   template <typename C>
+//   No GoodCheck(Helper<&C::TargetFunc>*);
+//
+//   template <typename C>
+//   Yes GoodCheck(...);
+//
+//   static const bool value = sizeof(GoodCheck<Base>(0)) == sizeof(Yes);
+//
+// Notice here that the variadic version of GoodCheck() returns Yes here
+// instead of No like the previous one. Also notice that we calculate |value|
+// by specializing GoodCheck() on Base instead of T.
+//
+// We've reversed the roles of the variadic, and Helper overloads.
+// GoodCheck(Helper<&C::TargetFunc>*), when C = Base, fails to be a valid
+// substitution if T::TargetFunc exists. Thus GoodCheck<Base>(0) will resolve
+// to the variadic version if T has TargetFunc.  If T::TargetFunc does not
+// exist, then &C::TargetFunc is not ambiguous, and the overload resolution
+// will prefer GoodCheck(Helper<&C::TargetFunc>*).
+//
+// This method of SFINAE will correctly probe for inherited names, but it cannot
+// typecheck those names.  It's still a good enough sanity check though.
+//
+// Works on gcc-4.2, gcc-4.4, and Visual Studio 2008.
+//
+// TODO(ajwong): Move to ref_counted.h or template_util.h when we've vetted
+// this works well.
+//
+// TODO(ajwong): Make this check for Release() as well.
+// See http://crbug.com/82038.
+template <typename T>
+class SupportsAddRefAndRelease {
+  typedef char Yes[1];
+  typedef char No[2];
+
+  struct BaseMixin {
+    void AddRef();
+  };
+
+// MSVC warns when you try to use Base if T has a private destructor, the
+// common pattern for refcounted types. It does this even though no attempt to
+// instantiate Base is made.  We disable the warning for this definition.
+#if defined(OS_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4624)
+#endif
+  struct Base : public T, public BaseMixin {};
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+  template <void (BaseMixin::*)(void)>
+  struct Helper {};
+
+  template <typename C>
+  static No& Check(Helper<&C::AddRef>*);
+
+  template <typename>
+  static Yes& Check(...);
+
+ public:
+  static const bool value = sizeof(Check<Base>(0)) == sizeof(Yes);
+};
+
+// Helpers to assert that arguments of a recounted type are bound with a
+// scoped_refptr.
+template <bool IsClasstype, typename T>
+struct UnsafeBindtoRefCountedArgHelper : false_type {};
+
+template <typename T>
+struct UnsafeBindtoRefCountedArgHelper<true, T>
+    : integral_constant<bool, SupportsAddRefAndRelease<T>::value> {};
+
+template <typename T>
+struct UnsafeBindtoRefCountedArg : false_type {};
+
+template <typename T>
+struct UnsafeBindtoRefCountedArg<T*>
+    : UnsafeBindtoRefCountedArgHelper<is_class<T>::value, T> {};
+
+template <typename T>
+class HasIsMethodTag {
+  typedef char Yes[1];
+  typedef char No[2];
+
+  template <typename U>
+  static Yes& Check(typename U::IsMethod*);
+
+  template <typename U>
+  static No& Check(...);
+
+ public:
+  static const bool value = sizeof(Check<T>(0)) == sizeof(Yes);
+};
+
+template <typename T>
+class UnretainedWrapper {
+ public:
+  explicit UnretainedWrapper(T* o) : ptr_(o) {}
+  T* get() const { return ptr_; }
+
+ private:
+  T* ptr_;
+};
+
+template <typename T>
+class ConstRefWrapper {
+ public:
+  explicit ConstRefWrapper(const T& o) : ptr_(&o) {}
+  const T& get() const { return *ptr_; }
+
+ private:
+  const T* ptr_;
+};
+
+template <typename T>
+struct IgnoreResultHelper {
+  explicit IgnoreResultHelper(T functor) : functor_(functor) {}
+
+  T functor_;
+};
+
+template <typename T>
+struct IgnoreResultHelper<Callback<T>> {
+  explicit IgnoreResultHelper(const Callback<T>& functor) : functor_(functor) {}
+
+  const Callback<T>& functor_;
+};
+
+// An alternate implementation is to avoid the destructive copy, and instead
+// specialize ParamTraits<> for OwnedWrapper<> to change the StorageType to
+// a class that is essentially a scoped_ptr<>.
+//
+// The current implementation has the benefit though of leaving ParamTraits<>
+// fully in callback_internal.h as well as avoiding type conversions during
+// storage.
+template <typename T>
+class OwnedWrapper {
+ public:
+  explicit OwnedWrapper(T* o) : ptr_(o) {}
+  ~OwnedWrapper() { delete ptr_; }
+  T* get() const { return ptr_; }
+  OwnedWrapper(const OwnedWrapper& other) {
+    ptr_ = other.ptr_;
+    other.ptr_ = NULL;
+  }
+
+ private:
+  mutable T* ptr_;
+};
+
+// PassedWrapper is a copyable adapter for a scoper that ignores const.
+//
+// It is needed to get around the fact that Bind() takes a const reference to
+// all its arguments.  Because Bind() takes a const reference to avoid
+// unnecessary copies, it is incompatible with movable-but-not-copyable
+// types; doing a destructive "move" of the type into Bind() would violate
+// the const correctness.
+//
+// This conundrum cannot be solved without either C++11 rvalue references or
+// a O(2^n) blowup of Bind() templates to handle each combination of regular
+// types and movable-but-not-copyable types.  Thus we introduce a wrapper type
+// that is copyable to transmit the correct type information down into
+// BindState<>. Ignoring const in this type makes sense because it is only
+// created when we are explicitly trying to do a destructive move.
+//
+// Two notes:
+//  1) PassedWrapper supports any type that has a "Pass()" function.
+//     This is intentional. The whitelisting of which specific types we
+//     support is maintained by CallbackParamTraits<>.
+//  2) is_valid_ is distinct from NULL because it is valid to bind a "NULL"
+//     scoper to a Callback and allow the Callback to execute once.
+template <typename T>
+class PassedWrapper {
+ public:
+  explicit PassedWrapper(T scoper) : is_valid_(true), scoper_(scoper.Pass()) {}
+  PassedWrapper(const PassedWrapper& other)
+      : is_valid_(other.is_valid_), scoper_(other.scoper_.Pass()) {}
+  T Pass() const {
+    CHECK(is_valid_);
+    is_valid_ = false;
+    return scoper_.Pass();
+  }
+
+ private:
+  mutable bool is_valid_;
+  mutable T scoper_;
+};
+
+// Unwrap the stored parameters for the wrappers above.
+template <typename T>
+struct UnwrapTraits {
+  typedef const T& ForwardType;
+  static ForwardType Unwrap(const T& o) { return o; }
+};
+
+template <typename T>
+struct UnwrapTraits<UnretainedWrapper<T>> {
+  typedef T* ForwardType;
+  static ForwardType Unwrap(UnretainedWrapper<T> unretained) {
+    return unretained.get();
+  }
+};
+
+template <typename T>
+struct UnwrapTraits<ConstRefWrapper<T>> {
+  typedef const T& ForwardType;
+  static ForwardType Unwrap(ConstRefWrapper<T> const_ref) {
+    return const_ref.get();
+  }
+};
+
+template <typename T>
+struct UnwrapTraits<scoped_refptr<T>> {
+  typedef T* ForwardType;
+  static ForwardType Unwrap(const scoped_refptr<T>& o) { return o.get(); }
+};
+
+template <typename T>
+struct UnwrapTraits<WeakPtr<T>> {
+  typedef const WeakPtr<T>& ForwardType;
+  static ForwardType Unwrap(const WeakPtr<T>& o) { return o; }
+};
+
+template <typename T>
+struct UnwrapTraits<OwnedWrapper<T>> {
+  typedef T* ForwardType;
+  static ForwardType Unwrap(const OwnedWrapper<T>& o) { return o.get(); }
+};
+
+template <typename T>
+struct UnwrapTraits<PassedWrapper<T>> {
+  typedef T ForwardType;
+  static T Unwrap(PassedWrapper<T>& o) { return o.Pass(); }
+};
+
+// Utility for handling different refcounting semantics in the Bind()
+// function.
+template <bool is_method, typename T>
+struct MaybeRefcount;
+
+template <typename T>
+struct MaybeRefcount<false, T> {
+  static void AddRef(const T&) {}
+  static void Release(const T&) {}
+};
+
+template <typename T, size_t n>
+struct MaybeRefcount<false, T[n]> {
+  static void AddRef(const T*) {}
+  static void Release(const T*) {}
+};
+
+template <typename T>
+struct MaybeRefcount<true, T> {
+  static void AddRef(const T&) {}
+  static void Release(const T&) {}
+};
+
+template <typename T>
+struct MaybeRefcount<true, T*> {
+  static void AddRef(T* o) { o->AddRef(); }
+  static void Release(T* o) { o->Release(); }
+};
+
+// No need to additionally AddRef() and Release() since we are storing a
+// scoped_refptr<> inside the storage object already.
+template <typename T>
+struct MaybeRefcount<true, scoped_refptr<T>> {
+  static void AddRef(const scoped_refptr<T>& o) {}
+  static void Release(const scoped_refptr<T>& o) {}
+};
+
+template <typename T>
+struct MaybeRefcount<true, const T*> {
+  static void AddRef(const T* o) { o->AddRef(); }
+  static void Release(const T* o) { o->Release(); }
+};
+
+// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a
+// method.  It is used internally by Bind() to select the correct
+// InvokeHelper that will no-op itself in the event the WeakPtr<> for
+// the target object is invalidated.
+//
+// P1 should be the type of the object that will be received of the method.
+template <bool IsMethod, typename P1>
+struct IsWeakMethod : public false_type {};
+
+template <typename T>
+struct IsWeakMethod<true, WeakPtr<T>> : public true_type {};
+
+template <typename T>
+struct IsWeakMethod<true, ConstRefWrapper<WeakPtr<T>>> : public true_type {};
+
+}  // namespace cef_internal
+
+template <typename T>
+static inline cef_internal::UnretainedWrapper<T> Unretained(T* o) {
+  return cef_internal::UnretainedWrapper<T>(o);
+}
+
+template <typename T>
+static inline cef_internal::ConstRefWrapper<T> ConstRef(const T& o) {
+  return cef_internal::ConstRefWrapper<T>(o);
+}
+
+template <typename T>
+static inline cef_internal::OwnedWrapper<T> Owned(T* o) {
+  return cef_internal::OwnedWrapper<T>(o);
+}
+
+// We offer 2 syntaxes for calling Passed().  The first takes a temporary and
+// is best suited for use with the return value of a function. The second
+// takes a pointer to the scoper and is just syntactic sugar to avoid having
+// to write Passed(scoper.Pass()).
+template <typename T>
+static inline cef_internal::PassedWrapper<T> Passed(T scoper) {
+  return cef_internal::PassedWrapper<T>(scoper.Pass());
+}
+template <typename T>
+static inline cef_internal::PassedWrapper<T> Passed(T* scoper) {
+  return cef_internal::PassedWrapper<T>(scoper->Pass());
+}
+
+template <typename T>
+static inline cef_internal::IgnoreResultHelper<T> IgnoreResult(T data) {
+  return cef_internal::IgnoreResultHelper<T>(data);
+}
+
+template <typename T>
+static inline cef_internal::IgnoreResultHelper<Callback<T>> IgnoreResult(
+    const Callback<T>& data) {
+  return cef_internal::IgnoreResultHelper<Callback<T>>(data);
+}
+
+void DoNothing();
+
+template <typename T>
+void DeletePointer(T* obj) {
+  delete obj;
+}
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_BIND_HELPERS_H_
diff --git a/src/include/base/cef_build.h b/src/include/base/cef_build.h
new file mode 100644
index 0000000..61dc317
--- /dev/null
+++ b/src/include/base/cef_build.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_BUILD_H_
+#define CEF_INCLUDE_BASE_CEF_BUILD_H_
+#pragma once
+
+#if defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/compiler_specific.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#if defined(_WIN32)
+#ifndef OS_WIN
+#define OS_WIN 1
+#endif
+#elif defined(__APPLE__)
+#ifndef OS_MACOSX
+#define OS_MACOSX 1
+#endif
+#elif defined(__linux__)
+#ifndef OS_LINUX
+#define OS_LINUX 1
+#endif
+#else
+#error Please add support for your platform in cef_build.h
+#endif
+
+// For access to standard POSIXish features, use OS_POSIX instead of a
+// more specific macro.
+#if defined(OS_MACOSX) || defined(OS_LINUX)
+#ifndef OS_POSIX
+#define OS_POSIX 1
+#endif
+#endif
+
+// Compiler detection.
+#if defined(__GNUC__)
+#ifndef COMPILER_GCC
+#define COMPILER_GCC 1
+#endif
+#elif defined(_MSC_VER)
+#ifndef COMPILER_MSVC
+#define COMPILER_MSVC 1
+#endif
+#else
+#error Please add support for your compiler in cef_build.h
+#endif
+
+// Processor architecture detection.  For more info on what's defined, see:
+//   http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+//   http://www.agner.org/optimize/calling_conventions.pdf
+//   or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86_64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__ARMEL__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARMEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__aarch64__) || defined(_M_ARM64)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARM64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__pnacl__)
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__MIPSEL__)
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPSEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#error Please add support for your architecture in cef_build.h
+#endif
+
+// Type detection for wchar_t.
+#if defined(OS_WIN)
+#define WCHAR_T_IS_UTF16
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define WCHAR_T_IS_UTF32
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to
+// compile in this mode (in particular, Chrome doesn't). This is intended for
+// other projects using base who manage their own dependencies and make sure
+// short wchar works for them.
+#define WCHAR_T_IS_UTF16
+#else
+#error Please add support for your compiler in cef_build.h
+#endif
+
+// Annotate a function indicating the caller must examine the return value.
+// Use like:
+//   int foo() WARN_UNUSED_RESULT;
+// To explicitly ignore a result, see |ignore_result()| in <base/macros.h>.
+#ifndef WARN_UNUSED_RESULT
+#if defined(COMPILER_GCC)
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+#endif  // WARN_UNUSED_RESULT
+
+// Annotate a typedef or function indicating it's ok if it's not used.
+// Use like:
+//   typedef Foo Bar ALLOW_UNUSED_TYPE;
+#ifndef ALLOW_UNUSED_TYPE
+#if defined(COMPILER_GCC)
+#define ALLOW_UNUSED_TYPE __attribute__((unused))
+#else
+#define ALLOW_UNUSED_TYPE
+#endif
+#endif  // ALLOW_UNUSED_TYPE
+
+// Annotate a variable indicating it's ok if the variable is not used.
+// (Typically used to silence a compiler warning when the assignment
+// is important for some other reason.)
+// Use like:
+//   int x = ...;
+//   ALLOW_UNUSED_LOCAL(x);
+#ifndef ALLOW_UNUSED_LOCAL
+#define ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0
+#endif
+
+// Sanitizers annotations.
+#if defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+#define NO_SANITIZE(what) __attribute__((no_sanitize(what)))
+#endif
+#endif
+#if !defined(NO_SANITIZE)
+#define NO_SANITIZE(what)
+#endif
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+// Annotate a virtual method indicating it must be overriding a virtual method
+// in the parent class.
+// Use like:
+//   void foo() OVERRIDE;
+// NOTE: This define should only be used in classes exposed to the client since
+// C++11 support may not be enabled in client applications. CEF internal classes
+// should use the `override` keyword directly.
+#ifndef OVERRIDE
+#if defined(__clang__)
+#define OVERRIDE override
+#elif defined(COMPILER_MSVC) && _MSC_VER >= 1600
+// Visual Studio 2010 and later support override.
+#define OVERRIDE override
+#elif defined(COMPILER_GCC) && __cplusplus >= 201103 && \
+    (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700
+// GCC 4.7 supports explicit virtual overrides when C++11 support is enabled.
+#define OVERRIDE override
+#else
+#define OVERRIDE
+#endif
+#endif  // OVERRIDE
+
+// Check for C++11 template alias support which was added in VS2013 and GCC4.7.
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf
+#if __cplusplus > 199711L || (defined(_MSC_VER) && _MSC_VER >= 1800) || \
+    (defined(__GNUC__) &&                                               \
+     (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ >= 40700))
+#define HAS_CPP11_TEMPLATE_ALIAS_SUPPORT
+#endif
+
+#endif  // CEF_INCLUDE_BASE_CEF_BUILD_H_
diff --git a/src/include/base/cef_callback.h b/src/include/base/cef_callback.h
new file mode 100644
index 0000000..16e238a
--- /dev/null
+++ b/src/include/base/cef_callback.h
@@ -0,0 +1,801 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_CALLBACK_H_
+#define CEF_INCLUDE_BASE_CEF_CALLBACK_H_
+#pragma once
+
+#if defined(BASE_CALLBACK_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/callback.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_callback_forward.h"
+#include "include/base/cef_template_util.h"
+#include "include/base/internal/cef_callback_internal.h"
+
+// NOTE: Header files that do not require the full definition of Callback or
+// Closure should #include "base/cef_callback_forward.h" instead of this file.
+
+// -----------------------------------------------------------------------------
+// Introduction
+// -----------------------------------------------------------------------------
+//
+// The templated Callback class is a generalized function object. Together
+// with the Bind() function in bind.h, they provide a type-safe method for
+// performing partial application of functions.
+//
+// Partial application (or "currying") is the process of binding a subset of
+// a function's arguments to produce another function that takes fewer
+// arguments. This can be used to pass around a unit of delayed execution,
+// much like lexical closures are used in other languages. For example, it
+// is used in Chromium code to schedule tasks on different MessageLoops.
+//
+// A callback with no unbound input parameters (base::Callback<void(void)>)
+// is called a base::Closure. Note that this is NOT the same as what other
+// languages refer to as a closure -- it does not retain a reference to its
+// enclosing environment.
+//
+// MEMORY MANAGEMENT AND PASSING
+//
+// The Callback objects themselves should be passed by const-reference, and
+// stored by copy. They internally store their state via a refcounted class
+// and thus do not need to be deleted.
+//
+// The reason to pass via a const-reference is to avoid unnecessary
+// AddRef/Release pairs to the internal state.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for basic stuff
+// -----------------------------------------------------------------------------
+//
+// BINDING A BARE FUNCTION
+//
+//   int Return5() { return 5; }
+//   base::Callback<int(void)> func_cb = base::Bind(&Return5);
+//   LOG(INFO) << func_cb.Run();  // Prints 5.
+//
+// BINDING A CLASS METHOD
+//
+//   The first argument to bind is the member function to call, the second is
+//   the object on which to call it.
+//
+//   class Ref : public base::RefCountedThreadSafe<Ref> {
+//    public:
+//     int Foo() { return 3; }
+//     void PrintBye() { LOG(INFO) << "bye."; }
+//   };
+//   scoped_refptr<Ref> ref = new Ref();
+//   base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref);
+//   LOG(INFO) << ref_cb.Run();  // Prints out 3.
+//
+//   By default the object must support RefCounted or you will get a compiler
+//   error. If you're passing between threads, be sure it's
+//   RefCountedThreadSafe! See "Advanced binding of member functions" below if
+//   you don't want to use reference counting.
+//
+// RUNNING A CALLBACK
+//
+//   Callbacks can be run with their "Run" method, which has the same
+//   signature as the template argument to the callback.
+//
+//   void DoSomething(const base::Callback<void(int, std::string)>& callback) {
+//     callback.Run(5, "hello");
+//   }
+//
+//   Callbacks can be run more than once (they don't get deleted or marked when
+//   run). However, this precludes using base::Passed (see below).
+//
+//   void DoSomething(const base::Callback<double(double)>& callback) {
+//     double myresult = callback.Run(3.14159);
+//     myresult += callback.Run(2.71828);
+//   }
+//
+// PASSING UNBOUND INPUT PARAMETERS
+//
+//   Unbound parameters are specified at the time a callback is Run(). They are
+//   specified in the Callback template type:
+//
+//   void MyFunc(int i, const std::string& str) {}
+//   base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
+//   cb.Run(23, "hello, world");
+//
+// PASSING BOUND INPUT PARAMETERS
+//
+//   Bound parameters are specified when you create thee callback as arguments
+//   to Bind(). They will be passed to the function and the Run()ner of the
+//   callback doesn't see those values or even know that the function it's
+//   calling.
+//
+//   void MyFunc(int i, const std::string& str) {}
+//   base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world");
+//   cb.Run();
+//
+//   A callback with no unbound input parameters (base::Callback<void(void)>)
+//   is called a base::Closure. So we could have also written:
+//
+//   base::Closure cb = base::Bind(&MyFunc, 23, "hello world");
+//
+//   When calling member functions, bound parameters just go after the object
+//   pointer.
+//
+//   base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
+//
+// PARTIAL BINDING OF PARAMETERS
+//
+//   You can specify some parameters when you create the callback, and specify
+//   the rest when you execute the callback.
+//
+//   void MyFunc(int i, const std::string& str) {}
+//   base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23);
+//   cb.Run("hello world");
+//
+//   When calling a function bound parameters are first, followed by unbound
+//   parameters.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for advanced binding
+// -----------------------------------------------------------------------------
+//
+// BINDING A CLASS METHOD WITH WEAK POINTERS
+//
+//   base::Bind(&MyClass::Foo, GetWeakPtr());
+//
+//   The callback will not be run if the object has already been destroyed.
+//   DANGER: weak pointers are not threadsafe, so don't use this
+//   when passing between threads!
+//
+// BINDING A CLASS METHOD WITH MANUAL LIFETIME MANAGEMENT
+//
+//   base::Bind(&MyClass::Foo, base::Unretained(this));
+//
+//   This disables all lifetime management on the object. You're responsible
+//   for making sure the object is alive at the time of the call. You break it,
+//   you own it!
+//
+// BINDING A CLASS METHOD AND HAVING THE CALLBACK OWN THE CLASS
+//
+//   MyClass* myclass = new MyClass;
+//   base::Bind(&MyClass::Foo, base::Owned(myclass));
+//
+//   The object will be deleted when the callback is destroyed, even if it's
+//   not run (like if you post a task during shutdown). Potentially useful for
+//   "fire and forget" cases.
+//
+// IGNORING RETURN VALUES
+//
+//   Sometimes you want to call a function that returns a value in a callback
+//   that doesn't expect a return value.
+//
+//   int DoSomething(int arg) { cout << arg << endl; }
+//   base::Callback<void<int>) cb =
+//       base::Bind(base::IgnoreResult(&DoSomething));
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for binding parameters to Bind()
+// -----------------------------------------------------------------------------
+//
+// Bound parameters are specified as arguments to Bind() and are passed to the
+// function. A callback with no parameters or no unbound parameters is called a
+// Closure (base::Callback<void(void)> and base::Closure are the same thing).
+//
+// PASSING PARAMETERS OWNED BY THE CALLBACK
+//
+//   void Foo(int* arg) { cout << *arg << endl; }
+//   int* pn = new int(1);
+//   base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));
+//
+//   The parameter will be deleted when the callback is destroyed, even if it's
+//   not run (like if you post a task during shutdown).
+//
+// PASSING PARAMETERS AS A scoped_ptr
+//
+//   void TakesOwnership(scoped_ptr<Foo> arg) {}
+//   scoped_ptr<Foo> f(new Foo);
+//   // f becomes null during the following call.
+//   base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));
+//
+//   Ownership of the parameter will be with the callback until the it is run,
+//   when ownership is passed to the callback function. This means the callback
+//   can only be run once. If the callback is never run, it will delete the
+//   object when it's destroyed.
+//
+// PASSING PARAMETERS AS A scoped_refptr
+//
+//   void TakesOneRef(scoped_refptr<Foo> arg) {}
+//   scoped_refptr<Foo> f(new Foo)
+//   base::Closure cb = base::Bind(&TakesOneRef, f);
+//
+//   This should "just work." The closure will take a reference as long as it
+//   is alive, and another reference will be taken for the called function.
+//
+// PASSING PARAMETERS BY REFERENCE
+//
+//   Const references are *copied* unless ConstRef is used. Example:
+//
+//   void foo(const int& arg) { printf("%d %p\n", arg, &arg); }
+//   int n = 1;
+//   base::Closure has_copy = base::Bind(&foo, n);
+//   base::Closure has_ref = base::Bind(&foo, base::ConstRef(n));
+//   n = 2;
+//   foo(n);                        // Prints "2 0xaaaaaaaaaaaa"
+//   has_copy.Run();                // Prints "1 0xbbbbbbbbbbbb"
+//   has_ref.Run();                 // Prints "2 0xaaaaaaaaaaaa"
+//
+//   Normally parameters are copied in the closure. DANGER: ConstRef stores a
+//   const reference instead, referencing the original parameter. This means
+//   that you must ensure the object outlives the callback!
+//
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// WHERE IS THIS DESIGN FROM:
+//
+// The design Callback and Bind is heavily influenced by C++'s
+// tr1::function/tr1::bind, and by the "Google Callback" system used inside
+// Google.
+//
+//
+// HOW THE IMPLEMENTATION WORKS:
+//
+// There are three main components to the system:
+//   1) The Callback classes.
+//   2) The Bind() functions.
+//   3) The arguments wrappers (e.g., Unretained() and ConstRef()).
+//
+// The Callback classes represent a generic function pointer. Internally,
+// it stores a refcounted piece of state that represents the target function
+// and all its bound parameters.  Each Callback specialization has a templated
+// constructor that takes an BindState<>*.  In the context of the constructor,
+// the static type of this BindState<> pointer uniquely identifies the
+// function it is representing, all its bound parameters, and a Run() method
+// that is capable of invoking the target.
+//
+// Callback's constructor takes the BindState<>* that has the full static type
+// and erases the target function type as well as the types of the bound
+// parameters.  It does this by storing a pointer to the specific Run()
+// function, and upcasting the state of BindState<>* to a
+// BindStateBase*. This is safe as long as this BindStateBase pointer
+// is only used with the stored Run() pointer.
+//
+// To BindState<> objects are created inside the Bind() functions.
+// These functions, along with a set of internal templates, are responsible for
+//
+//  - Unwrapping the function signature into return type, and parameters
+//  - Determining the number of parameters that are bound
+//  - Creating the BindState storing the bound parameters
+//  - Performing compile-time asserts to avoid error-prone behavior
+//  - Returning an Callback<> with an arity matching the number of unbound
+//    parameters and that knows the correct refcounting semantics for the
+//    target object if we are binding a method.
+//
+// The Bind functions do the above using type-inference, and template
+// specializations.
+//
+// By default Bind() will store copies of all bound parameters, and attempt
+// to refcount a target object if the function being bound is a class method.
+// These copies are created even if the function takes parameters as const
+// references. (Binding to non-const references is forbidden, see bind.h.)
+//
+// To change this behavior, we introduce a set of argument wrappers
+// (e.g., Unretained(), and ConstRef()).  These are simple container templates
+// that are passed by value, and wrap a pointer to argument.  See the
+// file-level comment in base/bind_helpers.h for more info.
+//
+// These types are passed to the Unwrap() functions, and the MaybeRefcount()
+// functions respectively to modify the behavior of Bind().  The Unwrap()
+// and MaybeRefcount() functions change behavior by doing partial
+// specialization based on whether or not a parameter is a wrapper type.
+//
+// ConstRef() is similar to tr1::cref.  Unretained() is specific to Chromium.
+//
+//
+// WHY NOT TR1 FUNCTION/BIND?
+//
+// Direct use of tr1::function and tr1::bind was considered, but ultimately
+// rejected because of the number of copy constructors invocations involved
+// in the binding of arguments during construction, and the forwarding of
+// arguments during invocation.  These copies will no longer be an issue in
+// C++0x because C++0x will support rvalue reference allowing for the compiler
+// to avoid these copies.  However, waiting for C++0x is not an option.
+//
+// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the
+// tr1::bind call itself will invoke a non-trivial copy constructor three times
+// for each bound parameter.  Also, each when passing a tr1::function, each
+// bound argument will be copied again.
+//
+// In addition to the copies taken at binding and invocation, copying a
+// tr1::function causes a copy to be made of all the bound parameters and
+// state.
+//
+// Furthermore, in Chromium, it is desirable for the Callback to take a
+// reference on a target object when representing a class method call.  This
+// is not supported by tr1.
+//
+// Lastly, tr1::function and tr1::bind has a more general and flexible API.
+// This includes things like argument reordering by use of
+// tr1::bind::placeholder, support for non-const reference parameters, and some
+// limited amount of subtyping of the tr1::function object (e.g.,
+// tr1::function<int(int)> is convertible to tr1::function<void(int)>).
+//
+// These are not features that are required in Chromium. Some of them, such as
+// allowing for reference parameters, and subtyping of functions, may actually
+// become a source of errors. Removing support for these features actually
+// allows for a simpler implementation, and a terser Currying API.
+//
+//
+// WHY NOT GOOGLE CALLBACKS?
+//
+// The Google callback system also does not support refcounting.  Furthermore,
+// its implementation has a number of strange edge cases with respect to type
+// conversion of its arguments.  In particular, the argument's constness must
+// at times match exactly the function signature, or the type-inference might
+// break.  Given the above, writing a custom solution was easier.
+//
+//
+// MISSING FUNCTIONALITY
+//  - Invoking the return of Bind.  Bind(&foo).Run() does not work;
+//  - Binding arrays to functions that take a non-const pointer.
+//    Example:
+//      void Foo(const char* ptr);
+//      void Bar(char* ptr);
+//      Bind(&Foo, "test");
+//      Bind(&Bar, "test");  // This fails because ptr is not const.
+
+namespace base {
+
+// First, we forward declare the Callback class template. This informs the
+// compiler that the template only has 1 type parameter which is the function
+// signature that the Callback is representing.
+//
+// After this, create template specializations for 0-7 parameters. Note that
+// even though the template typelist grows, the specialization still
+// only has one type: the function signature.
+//
+// If you are thinking of forward declaring Callback in your own header file,
+// please include "base/callback_forward.h" instead.
+template <typename Sig>
+class Callback;
+
+namespace cef_internal {
+template <typename Runnable, typename RunType, typename BoundArgsType>
+struct BindState;
+}  // namespace cef_internal
+
+template <typename R>
+class Callback<R(void)> : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)();
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run() const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(bind_state_.get());
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(cef_internal::BindStateBase*);
+};
+
+template <typename R, typename A1>
+class Callback<R(A1)> : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)(A1);
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1) const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(bind_state_.get(), cef_internal::CallbackForward(a1));
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(
+      cef_internal::BindStateBase*,
+      typename cef_internal::CallbackParamTraits<A1>::ForwardType);
+};
+
+template <typename R, typename A1, typename A2>
+class Callback<R(A1, A2)> : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)(A1, A2);
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+        typename cef_internal::CallbackParamTraits<A2>::ForwardType a2) const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(bind_state_.get(), cef_internal::CallbackForward(a1),
+             cef_internal::CallbackForward(a2));
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(
+      cef_internal::BindStateBase*,
+      typename cef_internal::CallbackParamTraits<A1>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A2>::ForwardType);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class Callback<R(A1, A2, A3)> : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)(A1, A2, A3);
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+        typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+        typename cef_internal::CallbackParamTraits<A3>::ForwardType a3) const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(bind_state_.get(), cef_internal::CallbackForward(a1),
+             cef_internal::CallbackForward(a2),
+             cef_internal::CallbackForward(a3));
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(
+      cef_internal::BindStateBase*,
+      typename cef_internal::CallbackParamTraits<A1>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A2>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A3>::ForwardType);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class Callback<R(A1, A2, A3, A4)> : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4);
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+        typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+        typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+        typename cef_internal::CallbackParamTraits<A4>::ForwardType a4) const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(bind_state_.get(), cef_internal::CallbackForward(a1),
+             cef_internal::CallbackForward(a2),
+             cef_internal::CallbackForward(a3),
+             cef_internal::CallbackForward(a4));
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(
+      cef_internal::BindStateBase*,
+      typename cef_internal::CallbackParamTraits<A1>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A2>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A3>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A4>::ForwardType);
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+class Callback<R(A1, A2, A3, A4, A5)> : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5);
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+        typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+        typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+        typename cef_internal::CallbackParamTraits<A4>::ForwardType a4,
+        typename cef_internal::CallbackParamTraits<A5>::ForwardType a5) const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(
+        bind_state_.get(), cef_internal::CallbackForward(a1),
+        cef_internal::CallbackForward(a2), cef_internal::CallbackForward(a3),
+        cef_internal::CallbackForward(a4), cef_internal::CallbackForward(a5));
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(
+      cef_internal::BindStateBase*,
+      typename cef_internal::CallbackParamTraits<A1>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A2>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A3>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A4>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A5>::ForwardType);
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+class Callback<R(A1, A2, A3, A4, A5, A6)> : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6);
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+        typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+        typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+        typename cef_internal::CallbackParamTraits<A4>::ForwardType a4,
+        typename cef_internal::CallbackParamTraits<A5>::ForwardType a5,
+        typename cef_internal::CallbackParamTraits<A6>::ForwardType a6) const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(
+        bind_state_.get(), cef_internal::CallbackForward(a1),
+        cef_internal::CallbackForward(a2), cef_internal::CallbackForward(a3),
+        cef_internal::CallbackForward(a4), cef_internal::CallbackForward(a5),
+        cef_internal::CallbackForward(a6));
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(
+      cef_internal::BindStateBase*,
+      typename cef_internal::CallbackParamTraits<A1>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A2>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A3>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A4>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A5>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A6>::ForwardType);
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+class Callback<R(A1, A2, A3, A4, A5, A6, A7)>
+    : public cef_internal::CallbackBase {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+  Callback() : CallbackBase(NULL) {}
+
+  // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+  // return the exact Callback<> type.  See base/bind.h for details.
+  template <typename Runnable, typename BindRunType, typename BoundArgsType>
+  Callback(
+      cef_internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state)
+      : CallbackBase(bind_state) {
+    // Force the assignment to a local variable of PolymorphicInvoke
+    // so the compiler will typecheck that the passed in Run() method has
+    // the correct type.
+    PolymorphicInvoke invoke_func =
+        &cef_internal::BindState<Runnable, BindRunType,
+                                 BoundArgsType>::InvokerType::Run;
+    polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+  }
+
+  bool Equals(const Callback& other) const {
+    return CallbackBase::Equals(other);
+  }
+
+  R Run(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+        typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+        typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+        typename cef_internal::CallbackParamTraits<A4>::ForwardType a4,
+        typename cef_internal::CallbackParamTraits<A5>::ForwardType a5,
+        typename cef_internal::CallbackParamTraits<A6>::ForwardType a6,
+        typename cef_internal::CallbackParamTraits<A7>::ForwardType a7) const {
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+
+    return f(
+        bind_state_.get(), cef_internal::CallbackForward(a1),
+        cef_internal::CallbackForward(a2), cef_internal::CallbackForward(a3),
+        cef_internal::CallbackForward(a4), cef_internal::CallbackForward(a5),
+        cef_internal::CallbackForward(a6), cef_internal::CallbackForward(a7));
+  }
+
+ private:
+  typedef R (*PolymorphicInvoke)(
+      cef_internal::BindStateBase*,
+      typename cef_internal::CallbackParamTraits<A1>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A2>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A3>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A4>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A5>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A6>::ForwardType,
+      typename cef_internal::CallbackParamTraits<A7>::ForwardType);
+};
+
+// Syntactic sugar to make Callbacks<void(void)> easier to declare since it
+// will be used in a lot of APIs with delayed execution.
+typedef Callback<void(void)> Closure;
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_CALLBACK_H_
diff --git a/src/include/base/cef_callback_forward.h b/src/include/base/cef_callback_forward.h
new file mode 100644
index 0000000..d604d7c
--- /dev/null
+++ b/src/include/base/cef_callback_forward.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_
+#define INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_
+#pragma once
+
+#if defined(BASE_CALLBACK_FORWARD_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/callback_forward.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+namespace base {
+
+template <typename Sig>
+class Callback;
+
+typedef Callback<void(void)> Closure;
+
+}  // namespace base
+
+#endif  // !!USING_CHROMIUM_INCLUDES
+
+#endif  // INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_
diff --git a/src/include/base/cef_callback_helpers.h b/src/include/base/cef_callback_helpers.h
new file mode 100644
index 0000000..ebe074a
--- /dev/null
+++ b/src/include/base/cef_callback_helpers.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This defines helpful methods for dealing with Callbacks.  Because Callbacks
+// are implemented using templates, with a class per callback signature, adding
+// methods to Callback<> itself is unattractive (lots of extra code gets
+// generated).  Instead, consider adding methods here.
+//
+// ResetAndReturn(&cb) is like cb.Reset() but allows executing a callback (via a
+// copy) after the original callback is Reset().  This can be handy if Run()
+// reads/writes the variable holding the Callback.
+
+#ifndef CEF_INCLUDE_BASE_CEF_CALLBACK_HELPERS_H_
+#define CEF_INCLUDE_BASE_CEF_CALLBACK_HELPERS_H_
+#pragma once
+
+#if defined(BASE_CALLBACK_HELPERS_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/callback_helpers.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_basictypes.h"
+#include "include/base/cef_build.h"
+#include "include/base/cef_callback.h"
+#include "include/base/cef_macros.h"
+
+namespace base {
+
+template <typename Sig>
+base::Callback<Sig> ResetAndReturn(base::Callback<Sig>* cb) {
+  base::Callback<Sig> ret(*cb);
+  cb->Reset();
+  return ret;
+}
+
+// ScopedClosureRunner is akin to scoped_ptr for Closures. It ensures that the
+// Closure is executed and deleted no matter how the current scope exits.
+class ScopedClosureRunner {
+ public:
+  ScopedClosureRunner();
+  explicit ScopedClosureRunner(const Closure& closure);
+  ~ScopedClosureRunner();
+
+  void Reset();
+  void Reset(const Closure& closure);
+  Closure Release() WARN_UNUSED_RESULT;
+
+ private:
+  Closure closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedClosureRunner);
+};
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_CALLBACK_HELPERS_H_
diff --git a/src/include/base/cef_callback_list.h b/src/include/base/cef_callback_list.h
new file mode 100644
index 0000000..e0ef366
--- /dev/null
+++ b/src/include/base/cef_callback_list.h
@@ -0,0 +1,449 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2013
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_CALLBACK_LIST_H_
+#define CEF_INCLUDE_BASE_CEF_CALLBACK_LIST_H_
+#pragma once
+
+#if defined(BASE_CALLBACK_LIST_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/callback_list.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include <list>
+
+#include "include/base/cef_basictypes.h"
+#include "include/base/cef_build.h"
+#include "include/base/cef_callback.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/base/internal/cef_callback_internal.h"
+
+// OVERVIEW:
+//
+// A container for a list of callbacks.  Unlike a normal STL vector or list,
+// this container can be modified during iteration without invalidating the
+// iterator. It safely handles the case of a callback removing itself
+// or another callback from the list while callbacks are being run.
+//
+// TYPICAL USAGE:
+//
+// class MyWidget {
+//  public:
+//   ...
+//
+//   typedef base::Callback<void(const Foo&)> OnFooCallback;
+//
+//   scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
+//   RegisterCallback(const OnFooCallback& cb) {
+//     return callback_list_.Add(cb);
+//   }
+//
+//  private:
+//   void NotifyFoo(const Foo& foo) {
+//      callback_list_.Notify(foo);
+//   }
+//
+//   base::CallbackList<void(const Foo&)> callback_list_;
+//
+//   DISALLOW_COPY_AND_ASSIGN(MyWidget);
+// };
+//
+//
+// class MyWidgetListener {
+//  public:
+//   MyWidgetListener::MyWidgetListener() {
+//     foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback(
+//             base::Bind(&MyWidgetListener::OnFoo, this)));
+//   }
+//
+//   MyWidgetListener::~MyWidgetListener() {
+//      // Subscription gets deleted automatically and will deregister
+//      // the callback in the process.
+//   }
+//
+//  private:
+//   void OnFoo(const Foo& foo) {
+//     // Do something.
+//   }
+//
+//   scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
+//       foo_subscription_;
+//
+//   DISALLOW_COPY_AND_ASSIGN(MyWidgetListener);
+// };
+
+namespace base {
+
+namespace cef_internal {
+
+template <typename CallbackType>
+class CallbackListBase {
+ public:
+  class Subscription {
+   public:
+    Subscription(CallbackListBase<CallbackType>* list,
+                 typename std::list<CallbackType>::iterator iter)
+        : list_(list), iter_(iter) {}
+
+    ~Subscription() {
+      if (list_->active_iterator_count_) {
+        iter_->Reset();
+      } else {
+        list_->callbacks_.erase(iter_);
+        if (!list_->removal_callback_.is_null())
+          list_->removal_callback_.Run();
+      }
+    }
+
+   private:
+    CallbackListBase<CallbackType>* list_;
+    typename std::list<CallbackType>::iterator iter_;
+
+    DISALLOW_COPY_AND_ASSIGN(Subscription);
+  };
+
+  // Add a callback to the list. The callback will remain registered until the
+  // returned Subscription is destroyed, which must occur before the
+  // CallbackList is destroyed.
+  scoped_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
+    DCHECK(!cb.is_null());
+    return scoped_ptr<Subscription>(
+        new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
+  }
+
+  // Sets a callback which will be run when a subscription list is changed.
+  void set_removal_callback(const Closure& callback) {
+    removal_callback_ = callback;
+  }
+
+  // Returns true if there are no subscriptions. This is only valid to call when
+  // not looping through the list.
+  bool empty() {
+    DCHECK_EQ(0, active_iterator_count_);
+    return callbacks_.empty();
+  }
+
+ protected:
+  // An iterator class that can be used to access the list of callbacks.
+  class Iterator {
+   public:
+    explicit Iterator(CallbackListBase<CallbackType>* list)
+        : list_(list), list_iter_(list_->callbacks_.begin()) {
+      ++list_->active_iterator_count_;
+    }
+
+    Iterator(const Iterator& iter)
+        : list_(iter.list_), list_iter_(iter.list_iter_) {
+      ++list_->active_iterator_count_;
+    }
+
+    ~Iterator() {
+      if (list_ && --list_->active_iterator_count_ == 0) {
+        list_->Compact();
+      }
+    }
+
+    CallbackType* GetNext() {
+      while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null())
+        ++list_iter_;
+
+      CallbackType* cb = NULL;
+      if (list_iter_ != list_->callbacks_.end()) {
+        cb = &(*list_iter_);
+        ++list_iter_;
+      }
+      return cb;
+    }
+
+   private:
+    CallbackListBase<CallbackType>* list_;
+    typename std::list<CallbackType>::iterator list_iter_;
+  };
+
+  CallbackListBase() : active_iterator_count_(0) {}
+
+  ~CallbackListBase() {
+    DCHECK_EQ(0, active_iterator_count_);
+    DCHECK_EQ(0U, callbacks_.size());
+  }
+
+  // Returns an instance of a CallbackListBase::Iterator which can be used
+  // to run callbacks.
+  Iterator GetIterator() { return Iterator(this); }
+
+  // Compact the list: remove any entries which were NULLed out during
+  // iteration.
+  void Compact() {
+    typename std::list<CallbackType>::iterator it = callbacks_.begin();
+    bool updated = false;
+    while (it != callbacks_.end()) {
+      if ((*it).is_null()) {
+        updated = true;
+        it = callbacks_.erase(it);
+      } else {
+        ++it;
+      }
+
+      if (updated && !removal_callback_.is_null())
+        removal_callback_.Run();
+    }
+  }
+
+ private:
+  std::list<CallbackType> callbacks_;
+  int active_iterator_count_;
+  Closure removal_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
+};
+
+}  // namespace cef_internal
+
+template <typename Sig>
+class CallbackList;
+
+template <>
+class CallbackList<void(void)>
+    : public cef_internal::CallbackListBase<Callback<void(void)>> {
+ public:
+  typedef Callback<void(void)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify() {
+    cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run();
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1>
+class CallbackList<void(A1)>
+    : public cef_internal::CallbackListBase<Callback<void(A1)>> {
+ public:
+  typedef Callback<void(A1)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1) {
+    typename cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run(a1);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2>
+class CallbackList<void(A1, A2)>
+    : public cef_internal::CallbackListBase<Callback<void(A1, A2)>> {
+ public:
+  typedef Callback<void(A1, A2)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+              typename cef_internal::CallbackParamTraits<A2>::ForwardType a2) {
+    typename cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run(a1, a2);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3>
+class CallbackList<void(A1, A2, A3)>
+    : public cef_internal::CallbackListBase<Callback<void(A1, A2, A3)>> {
+ public:
+  typedef Callback<void(A1, A2, A3)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+              typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+              typename cef_internal::CallbackParamTraits<A3>::ForwardType a3) {
+    typename cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run(a1, a2, a3);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3, typename A4>
+class CallbackList<void(A1, A2, A3, A4)>
+    : public cef_internal::CallbackListBase<Callback<void(A1, A2, A3, A4)>> {
+ public:
+  typedef Callback<void(A1, A2, A3, A4)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+              typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+              typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+              typename cef_internal::CallbackParamTraits<A4>::ForwardType a4) {
+    typename cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run(a1, a2, a3, a4);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+class CallbackList<void(A1, A2, A3, A4, A5)>
+    : public cef_internal::CallbackListBase<
+          Callback<void(A1, A2, A3, A4, A5)>> {
+ public:
+  typedef Callback<void(A1, A2, A3, A4, A5)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+              typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+              typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+              typename cef_internal::CallbackParamTraits<A4>::ForwardType a4,
+              typename cef_internal::CallbackParamTraits<A5>::ForwardType a5) {
+    typename cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run(a1, a2, a3, a4, a5);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+class CallbackList<void(A1, A2, A3, A4, A5, A6)>
+    : public cef_internal::CallbackListBase<
+          Callback<void(A1, A2, A3, A4, A5, A6)>> {
+ public:
+  typedef Callback<void(A1, A2, A3, A4, A5, A6)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+              typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+              typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+              typename cef_internal::CallbackParamTraits<A4>::ForwardType a4,
+              typename cef_internal::CallbackParamTraits<A5>::ForwardType a5,
+              typename cef_internal::CallbackParamTraits<A6>::ForwardType a6) {
+    typename cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run(a1, a2, a3, a4, a5, a6);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+class CallbackList<void(A1, A2, A3, A4, A5, A6, A7)>
+    : public cef_internal::CallbackListBase<
+          Callback<void(A1, A2, A3, A4, A5, A6, A7)>> {
+ public:
+  typedef Callback<void(A1, A2, A3, A4, A5, A6, A7)> CallbackType;
+
+  CallbackList() {}
+
+  void Notify(typename cef_internal::CallbackParamTraits<A1>::ForwardType a1,
+              typename cef_internal::CallbackParamTraits<A2>::ForwardType a2,
+              typename cef_internal::CallbackParamTraits<A3>::ForwardType a3,
+              typename cef_internal::CallbackParamTraits<A4>::ForwardType a4,
+              typename cef_internal::CallbackParamTraits<A5>::ForwardType a5,
+              typename cef_internal::CallbackParamTraits<A6>::ForwardType a6,
+              typename cef_internal::CallbackParamTraits<A7>::ForwardType a7) {
+    typename cef_internal::CallbackListBase<CallbackType>::Iterator it =
+        this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != NULL) {
+      cb->Run(a1, a2, a3, a4, a5, a6, a7);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_CALLBACK_LIST_H_
diff --git a/src/include/base/cef_cancelable_callback.h b/src/include/base/cef_cancelable_callback.h
new file mode 100644
index 0000000..febce3a
--- /dev/null
+++ b/src/include/base/cef_cancelable_callback.h
@@ -0,0 +1,293 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// CancelableCallback is a wrapper around base::Callback that allows
+// cancellation of a callback. CancelableCallback takes a reference on the
+// wrapped callback until this object is destroyed or Reset()/Cancel() are
+// called.
+//
+// NOTE:
+//
+// Calling CancelableCallback::Cancel() brings the object back to its natural,
+// default-constructed state, i.e., CancelableCallback::callback() will return
+// a null callback.
+//
+// THREAD-SAFETY:
+//
+// CancelableCallback objects must be created on, posted to, cancelled on, and
+// destroyed on the same thread.
+//
+//
+// EXAMPLE USAGE:
+//
+// In the following example, the test is verifying that RunIntensiveTest()
+// Quit()s the message loop within 4 seconds. The cancelable callback is posted
+// to the message loop, the intensive test runs, the message loop is run,
+// then the callback is cancelled.
+//
+// void TimeoutCallback(const std::string& timeout_message) {
+//   FAIL() << timeout_message;
+//   MessageLoop::current()->QuitWhenIdle();
+// }
+//
+// CancelableClosure timeout(base::Bind(&TimeoutCallback, "Test timed out."));
+// MessageLoop::current()->PostDelayedTask(FROM_HERE, timeout.callback(),
+//                                         4000)  // 4 seconds to run.
+// RunIntensiveTest();
+// MessageLoop::current()->Run();
+// timeout.Cancel();  // Hopefully this is hit before the timeout callback runs.
+//
+
+#ifndef CEF_INCLUDE_BASE_CEF_CANCELABLE_CALLBACK_H_
+#define CEF_INCLUDE_BASE_CEF_CANCELABLE_CALLBACK_H_
+#pragma once
+
+#if defined(BASE_CANCELABLE_CALLBACK_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/cancelable_callback.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_build.h"
+#include "include/base/cef_callback.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_weak_ptr.h"
+#include "include/base/internal/cef_callback_internal.h"
+
+namespace base {
+
+template <typename Sig>
+class CancelableCallback;
+
+template <>
+class CancelableCallback<void(void)> {
+ public:
+  CancelableCallback() : weak_factory_(this) {}
+
+  // |callback| must not be null.
+  explicit CancelableCallback(const base::Callback<void(void)>& callback)
+      : weak_factory_(this), callback_(callback) {
+    DCHECK(!callback.is_null());
+    InitializeForwarder();
+  }
+
+  ~CancelableCallback() {}
+
+  // Cancels and drops the reference to the wrapped callback.
+  void Cancel() {
+    weak_factory_.InvalidateWeakPtrs();
+    forwarder_.Reset();
+    callback_.Reset();
+  }
+
+  // Returns true if the wrapped callback has been cancelled.
+  bool IsCancelled() const { return callback_.is_null(); }
+
+  // Sets |callback| as the closure that may be cancelled. |callback| may not
+  // be null. Outstanding and any previously wrapped callbacks are cancelled.
+  void Reset(const base::Callback<void(void)>& callback) {
+    DCHECK(!callback.is_null());
+
+    // Outstanding tasks (e.g., posted to a message loop) must not be called.
+    Cancel();
+
+    // |forwarder_| is no longer valid after Cancel(), so re-bind.
+    InitializeForwarder();
+
+    callback_ = callback;
+  }
+
+  // Returns a callback that can be disabled by calling Cancel().
+  const base::Callback<void(void)>& callback() const { return forwarder_; }
+
+ private:
+  void Forward() { callback_.Run(); }
+
+  // Helper method to bind |forwarder_| using a weak pointer from
+  // |weak_factory_|.
+  void InitializeForwarder() {
+    forwarder_ = base::Bind(&CancelableCallback<void(void)>::Forward,
+                            weak_factory_.GetWeakPtr());
+  }
+
+  // Used to ensure Forward() is not run when this object is destroyed.
+  base::WeakPtrFactory<CancelableCallback<void(void)>> weak_factory_;
+
+  // The wrapper closure.
+  base::Callback<void(void)> forwarder_;
+
+  // The stored closure that may be cancelled.
+  base::Callback<void(void)> callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
+};
+
+template <typename A1>
+class CancelableCallback<void(A1)> {
+ public:
+  CancelableCallback() : weak_factory_(this) {}
+
+  // |callback| must not be null.
+  explicit CancelableCallback(const base::Callback<void(A1)>& callback)
+      : weak_factory_(this), callback_(callback) {
+    DCHECK(!callback.is_null());
+    InitializeForwarder();
+  }
+
+  ~CancelableCallback() {}
+
+  // Cancels and drops the reference to the wrapped callback.
+  void Cancel() {
+    weak_factory_.InvalidateWeakPtrs();
+    forwarder_.Reset();
+    callback_.Reset();
+  }
+
+  // Returns true if the wrapped callback has been cancelled.
+  bool IsCancelled() const { return callback_.is_null(); }
+
+  // Sets |callback| as the closure that may be cancelled. |callback| may not
+  // be null. Outstanding and any previously wrapped callbacks are cancelled.
+  void Reset(const base::Callback<void(A1)>& callback) {
+    DCHECK(!callback.is_null());
+
+    // Outstanding tasks (e.g., posted to a message loop) must not be called.
+    Cancel();
+
+    // |forwarder_| is no longer valid after Cancel(), so re-bind.
+    InitializeForwarder();
+
+    callback_ = callback;
+  }
+
+  // Returns a callback that can be disabled by calling Cancel().
+  const base::Callback<void(A1)>& callback() const { return forwarder_; }
+
+ private:
+  void Forward(A1 a1) const { callback_.Run(a1); }
+
+  // Helper method to bind |forwarder_| using a weak pointer from
+  // |weak_factory_|.
+  void InitializeForwarder() {
+    forwarder_ = base::Bind(&CancelableCallback<void(A1)>::Forward,
+                            weak_factory_.GetWeakPtr());
+  }
+
+  // Used to ensure Forward() is not run when this object is destroyed.
+  base::WeakPtrFactory<CancelableCallback<void(A1)>> weak_factory_;
+
+  // The wrapper closure.
+  base::Callback<void(A1)> forwarder_;
+
+  // The stored closure that may be cancelled.
+  base::Callback<void(A1)> callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
+};
+
+template <typename A1, typename A2>
+class CancelableCallback<void(A1, A2)> {
+ public:
+  CancelableCallback() : weak_factory_(this) {}
+
+  // |callback| must not be null.
+  explicit CancelableCallback(const base::Callback<void(A1, A2)>& callback)
+      : weak_factory_(this), callback_(callback) {
+    DCHECK(!callback.is_null());
+    InitializeForwarder();
+  }
+
+  ~CancelableCallback() {}
+
+  // Cancels and drops the reference to the wrapped callback.
+  void Cancel() {
+    weak_factory_.InvalidateWeakPtrs();
+    forwarder_.Reset();
+    callback_.Reset();
+  }
+
+  // Returns true if the wrapped callback has been cancelled.
+  bool IsCancelled() const { return callback_.is_null(); }
+
+  // Sets |callback| as the closure that may be cancelled. |callback| may not
+  // be null. Outstanding and any previously wrapped callbacks are cancelled.
+  void Reset(const base::Callback<void(A1, A2)>& callback) {
+    DCHECK(!callback.is_null());
+
+    // Outstanding tasks (e.g., posted to a message loop) must not be called.
+    Cancel();
+
+    // |forwarder_| is no longer valid after Cancel(), so re-bind.
+    InitializeForwarder();
+
+    callback_ = callback;
+  }
+
+  // Returns a callback that can be disabled by calling Cancel().
+  const base::Callback<void(A1, A2)>& callback() const { return forwarder_; }
+
+ private:
+  void Forward(A1 a1, A2 a2) const { callback_.Run(a1, a2); }
+
+  // Helper method to bind |forwarder_| using a weak pointer from
+  // |weak_factory_|.
+  void InitializeForwarder() {
+    forwarder_ = base::Bind(&CancelableCallback<void(A1, A2)>::Forward,
+                            weak_factory_.GetWeakPtr());
+  }
+
+  // Used to ensure Forward() is not run when this object is destroyed.
+  base::WeakPtrFactory<CancelableCallback<void(A1, A2)>> weak_factory_;
+
+  // The wrapper closure.
+  base::Callback<void(A1, A2)> forwarder_;
+
+  // The stored closure that may be cancelled.
+  base::Callback<void(A1, A2)> callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
+};
+
+typedef CancelableCallback<void(void)> CancelableClosure;
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_CANCELABLE_CALLBACK_H_
diff --git a/src/include/base/cef_lock.h b/src/include/base/cef_lock.h
new file mode 100644
index 0000000..6909bd6
--- /dev/null
+++ b/src/include/base/cef_lock.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_LOCK_H_
+#define CEF_INCLUDE_BASE_CEF_LOCK_H_
+#pragma once
+
+#if defined(BASE_SYNCHRONIZATION_LOCK_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/synchronization/lock.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_platform_thread.h"
+#include "include/base/internal/cef_lock_impl.h"
+
+namespace base {
+namespace cef_internal {
+
+// A convenient wrapper for an OS specific critical section.  The only real
+// intelligence in this class is in debug mode for the support for the
+// AssertAcquired() method.
+class Lock {
+ public:
+#if !DCHECK_IS_ON()  // Optimized wrapper implementation
+  Lock() : lock_() {}
+  ~Lock() {}
+  void Acquire() { lock_.Lock(); }
+  void Release() { lock_.Unlock(); }
+
+  // If the lock is not held, take it and return true. If the lock is already
+  // held by another thread, immediately return false. This must not be called
+  // by a thread already holding the lock (what happens is undefined and an
+  // assertion may fail).
+  bool Try() { return lock_.Try(); }
+
+  // Null implementation if not debug.
+  void AssertAcquired() const {}
+#else
+  Lock();
+  ~Lock();
+
+  // NOTE: Although windows critical sections support recursive locks, we do not
+  // allow this, and we will commonly fire a DCHECK() if a thread attempts to
+  // acquire the lock a second time (while already holding it).
+  void Acquire() {
+    lock_.Lock();
+    CheckUnheldAndMark();
+  }
+  void Release() {
+    CheckHeldAndUnmark();
+    lock_.Unlock();
+  }
+
+  bool Try() {
+    bool rv = lock_.Try();
+    if (rv) {
+      CheckUnheldAndMark();
+    }
+    return rv;
+  }
+
+  void AssertAcquired() const;
+#endif  // !DCHECK_IS_ON()
+
+ private:
+#if DCHECK_IS_ON()
+  // Members and routines taking care of locks assertions.
+  // Note that this checks for recursive locks and allows them
+  // if the variable is set.  This is allowed by the underlying implementation
+  // on windows but not on Posix, so we're doing unneeded checks on Posix.
+  // It's worth it to share the code.
+  void CheckHeldAndUnmark();
+  void CheckUnheldAndMark();
+
+  // All private data is implicitly protected by lock_.
+  // Be VERY careful to only access members under that lock.
+  base::PlatformThreadRef owning_thread_ref_;
+#endif  // DCHECK_IS_ON()
+
+  // Platform specific underlying lock implementation.
+  LockImpl lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(Lock);
+};
+
+// A helper class that acquires the given Lock while the AutoLock is in scope.
+class AutoLock {
+ public:
+  struct AlreadyAcquired {};
+
+  explicit AutoLock(Lock& lock) : lock_(lock) { lock_.Acquire(); }
+
+  AutoLock(Lock& lock, const AlreadyAcquired&) : lock_(lock) {
+    lock_.AssertAcquired();
+  }
+
+  ~AutoLock() {
+    lock_.AssertAcquired();
+    lock_.Release();
+  }
+
+ private:
+  Lock& lock_;
+  DISALLOW_COPY_AND_ASSIGN(AutoLock);
+};
+
+// AutoUnlock is a helper that will Release() the |lock| argument in the
+// constructor, and re-Acquire() it in the destructor.
+class AutoUnlock {
+ public:
+  explicit AutoUnlock(Lock& lock) : lock_(lock) {
+    // We require our caller to have the lock.
+    lock_.AssertAcquired();
+    lock_.Release();
+  }
+
+  ~AutoUnlock() { lock_.Acquire(); }
+
+ private:
+  Lock& lock_;
+  DISALLOW_COPY_AND_ASSIGN(AutoUnlock);
+};
+
+}  // namespace cef_internal
+
+// Implement classes in the cef_internal namespace and then expose them to the
+// base namespace. This avoids conflicts with the base.lib implementation when
+// linking sandbox support on Windows.
+using cef_internal::Lock;
+using cef_internal::AutoLock;
+using cef_internal::AutoUnlock;
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_LOCK_H_
diff --git a/src/include/base/cef_logging.h b/src/include/base/cef_logging.h
new file mode 100644
index 0000000..ba93097
--- /dev/null
+++ b/src/include/base/cef_logging.h
@@ -0,0 +1,760 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+// WARNING: Logging macros should not be used in the main/browser process before
+// calling CefInitialize or in sub-processes before calling CefExecuteProcess.
+//
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging.  The way to log things is to stream
+// things to LOG(<a particular severity level>).  E.g.,
+//
+//   LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+//   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+//   DLOG(INFO) << "Found cookies";
+//
+//   DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles.  LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+//   LOG_ASSERT(assertion);
+//   DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// There are "verbose level" logging macros.  They look like
+//
+//   VLOG(1) << "I'm printed when you run the program with --v=1 or more";
+//   VLOG(2) << "I'm printed when you run the program with --v=2 or more";
+//
+// These always log at the INFO log level (when they log at all).
+// The verbose logging can also be turned on module-by-module.  For instance,
+//    --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
+// will cause:
+//   a. VLOG(2) and lower messages to be printed from profile.{h,cc}
+//   b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc}
+//   c. VLOG(3) and lower messages to be printed from files prefixed with
+//      "browser"
+//   d. VLOG(4) and lower messages to be printed from files under a
+//     "chromeos" directory.
+//   e. VLOG(0) and lower messages to be printed from elsewhere
+//
+// The wildcarding functionality shown by (c) supports both '*' (match
+// 0 or more characters) and '?' (match any single character)
+// wildcards.  Any pattern containing a forward or backward slash will
+// be tested against the whole pathname and not just the module.
+// E.g., "*/foo/bar/*=2" would change the logging level for all code
+// in source files under a "foo/bar" directory.
+//
+// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as
+//
+//   if (VLOG_IS_ON(2)) {
+//     // do some logging preparation and logging
+//     // that can't be accomplished with just VLOG(2) << ...;
+//   }
+//
+// There is also a VLOG_IF "verbose level" condition macro for sample
+// cases, when some extra computation and preparation for logs is not
+// needed.
+//
+//   VLOG_IF(1, (size > 1024))
+//      << "I'm printed when size is more than 1024 and when you run the "
+//         "program with --v=1 or more";
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// Lastly, there is:
+//
+//   PLOG(ERROR) << "Couldn't do foo";
+//   DPLOG(ERROR) << "Couldn't do foo";
+//   PLOG_IF(ERROR, cond) << "Couldn't do foo";
+//   DPLOG_IF(ERROR, cond) << "Couldn't do foo";
+//   PCHECK(condition) << "Couldn't do foo";
+//   DPCHECK(condition) << "Couldn't do foo";
+//
+// which append the last system error to the message in string form (taken from
+// GetLastError() on Windows and errno on POSIX).
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+//
+// There is the special severity of DFATAL, which logs FATAL in debug mode,
+// ERROR in normal mode.
+//
+
+#ifndef CEF_INCLUDE_BASE_CEF_LOGGING_H_
+#define CEF_INCLUDE_BASE_CEF_LOGGING_H_
+#pragma once
+
+#if defined(DCHECK)
+// Do nothing if the macros provided by this header already exist.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+
+// Always define the DCHECK_IS_ON macro which is used from other CEF headers.
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() false
+#else
+#define DCHECK_IS_ON() true
+#endif
+
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/logging.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include <cassert>
+#include <cstring>
+#include <sstream>
+#include <string>
+
+#include "include/base/cef_build.h"
+#include "include/base/cef_macros.h"
+#include "include/internal/cef_logging_internal.h"
+
+namespace cef {
+namespace logging {
+
+// Gets the current log level.
+inline int GetMinLogLevel() {
+  return cef_get_min_log_level();
+}
+
+// Gets the current vlog level for the given file (usually taken from
+// __FILE__). Note that |N| is the size *with* the null terminator.
+template <size_t N>
+int GetVlogLevel(const char (&file)[N]) {
+  return cef_get_vlog_level(file, N);
+}
+
+typedef int LogSeverity;
+const LogSeverity LOG_VERBOSE = -1;  // This is level 1 verbosity
+// Note: the log severities are used to index into the array of names,
+// see log_severity_names.
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode
+#ifdef NDEBUG
+const LogSeverity LOG_DFATAL = LOG_ERROR;
+#else
+const LogSeverity LOG_DFATAL = LOG_FATAL;
+#endif
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...)                    \
+  cef::logging::ClassName(__FILE__, __LINE__, cef::logging::LOG_INFO, \
+                          ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...)                    \
+  cef::logging::ClassName(__FILE__, __LINE__, cef::logging::LOG_WARNING, \
+                          ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...)                    \
+  cef::logging::ClassName(__FILE__, __LINE__, cef::logging::LOG_ERROR, \
+                          ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...)                    \
+  cef::logging::ClassName(__FILE__, __LINE__, cef::logging::LOG_FATAL, \
+                          ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...)                    \
+  cef::logging::ClassName(__FILE__, __LINE__, cef::logging::LOG_DFATAL, \
+                          ##__VA_ARGS__)
+
+#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
+#define COMPACT_GOOGLE_LOG_WARNING COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
+#define COMPACT_GOOGLE_LOG_ERROR COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
+#define COMPACT_GOOGLE_LOG_FATAL COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
+
+#if defined(OS_WIN)
+// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
+// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
+// to keep using this syntax, we define this macro to do the same thing
+// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
+// the Windows SDK does for consistency.
+#define ERROR 0
+#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \
+  COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
+// Needed for LOG_IS_ON(ERROR).
+const LogSeverity LOG_0 = LOG_ERROR;
+#endif
+
+// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also,
+// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will
+// always fire if they fail.
+#define LOG_IS_ON(severity) \
+  ((::cef::logging::LOG_##severity) >= ::cef::logging::GetMinLogLevel())
+
+// We can't do any caching tricks with VLOG_IS_ON() like the
+// google-glog version since it requires GCC extensions.  This means
+// that using the v-logging functions in conjunction with --vmodule
+// may be slow.
+#define VLOG_IS_ON(verboselevel) \
+  ((verboselevel) <= ::cef::logging::GetVlogLevel(__FILE__))
+
+// Helper macro which avoids evaluating the arguments to a stream if
+// the condition doesn't hold.
+#define LAZY_STREAM(stream, condition) \
+  !(condition) ? (void)0 : ::cef::logging::LogMessageVoidify() & (stream)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO.  There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_##severity.stream()
+
+#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
+#define LOG_IF(severity, condition) \
+  LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+#define SYSLOG(severity) LOG(severity)
+#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition)
+
+// The VLOG macros log with negative verbosities.
+#define VLOG_STREAM(verbose_level) \
+  cef::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+
+#define VLOG(verbose_level) \
+  LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VLOG_IF(verbose_level, condition) \
+  LAZY_STREAM(VLOG_STREAM(verbose_level), \
+              VLOG_IS_ON(verbose_level) && (condition))
+
+#if defined(OS_WIN)
+#define VPLOG_STREAM(verbose_level)                                            \
+  cef::logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level,       \
+                                     ::cef::logging::GetLastSystemErrorCode()) \
+      .stream()
+#elif defined(OS_POSIX)
+#define VPLOG_STREAM(verbose_level)                                       \
+  cef::logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level,       \
+                                ::cef::logging::GetLastSystemErrorCode()) \
+      .stream()
+#endif
+
+#define VPLOG(verbose_level) \
+  LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VPLOG_IF(verbose_level, condition) \
+  LAZY_STREAM(VPLOG_STREAM(verbose_level), \
+              VLOG_IS_ON(verbose_level) && (condition))
+
+// TODO(akalin): Add more VLOG variants, e.g. VPLOG.
+
+#define LOG_ASSERT(condition) \
+  LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#define SYSLOG_ASSERT(condition) \
+  SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+
+#if defined(OS_WIN)
+#define PLOG_STREAM(severity)                                                \
+  COMPACT_GOOGLE_LOG_EX_##severity(Win32ErrorLogMessage,                     \
+                                   ::cef::logging::GetLastSystemErrorCode()) \
+      .stream()
+#elif defined(OS_POSIX)
+#define PLOG_STREAM(severity)                                                \
+  COMPACT_GOOGLE_LOG_EX_##severity(ErrnoLogMessage,                          \
+                                   ::cef::logging::GetLastSystemErrorCode()) \
+      .stream()
+#endif
+
+#define PLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))
+
+#define PLOG_IF(severity, condition) \
+  LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+// The actual stream used isn't important.
+#define EAT_STREAM_PARAMETERS \
+  true ? (void)0 : ::cef::logging::LogMessageVoidify() & LOG_STREAM(FATAL)
+
+// CHECK dies with a fatal error if condition is not true.  It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+//
+// We make sure CHECK et al. always evaluates their arguments, as
+// doing CHECK(FunctionWithSideEffect()) is a common idiom.
+
+#define CHECK(condition)                       \
+  LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \
+      << "Check failed: " #condition ". "
+
+#define PCHECK(condition)                       \
+  LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
+      << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use CHECK_EQ et al below.
+//
+// TODO(akalin): Rewrite this so that constructs like if (...)
+// CHECK_EQ(...) else { ... } work properly.
+#define CHECK_OP(name, op, val1, val2)                        \
+  if (std::string* _result = cef::logging::Check##name##Impl( \
+          (val1), (val2), #val1 " " #op " " #val2))           \
+  cef::logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+// Build the error message string.  This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline.  Caller
+// takes ownership of the returned string.
+template <class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+  std::ostringstream ss;
+  ss << names << " (" << v1 << " vs. " << v2 << ")";
+  std::string* msg = new std::string(ss.str());
+  return msg;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
+// in logging.cc.
+extern template std::string* MakeCheckOpString<int, int>(const int&,
+                                                         const int&,
+                                                         const char* names);
+extern template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+    const unsigned long&,
+    const unsigned long&,
+    const char* names);
+extern template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+    const unsigned long&,
+    const unsigned int&,
+    const char* names);
+extern template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+    const unsigned int&,
+    const unsigned long&,
+    const char* names);
+extern template std::string* MakeCheckOpString<std::string, std::string>(
+    const std::string&,
+    const std::string&,
+    const char* name);
+#endif
+
+// Helper functions for CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_CHECK_OP_IMPL(name, op)                                       \
+  template <class t1, class t2>                                              \
+  inline std::string* Check##name##Impl(const t1& v1, const t2& v2,          \
+                                        const char* names) {                 \
+    if (v1 op v2)                                                            \
+      return NULL;                                                           \
+    else                                                                     \
+      return MakeCheckOpString(v1, v2, names);                               \
+  }                                                                          \
+  inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+    if (v1 op v2)                                                            \
+      return NULL;                                                           \
+    else                                                                     \
+      return MakeCheckOpString(v1, v2, names);                               \
+  }
+DEFINE_CHECK_OP_IMPL(EQ, ==)
+DEFINE_CHECK_OP_IMPL(NE, !=)
+DEFINE_CHECK_OP_IMPL(LE, <=)
+DEFINE_CHECK_OP_IMPL(LT, <)
+DEFINE_CHECK_OP_IMPL(GE, >=)
+DEFINE_CHECK_OP_IMPL(GT, >)
+#undef DEFINE_CHECK_OP_IMPL
+
+#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
+#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
+#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
+#define CHECK_LT(val1, val2) CHECK_OP(LT, <, val1, val2)
+#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2)
+#define CHECK_GT(val1, val2) CHECK_OP(GT, >, val1, val2)
+
+#if defined(NDEBUG)
+#define ENABLE_DLOG 0
+#else
+#define ENABLE_DLOG 1
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() 0
+#else
+#define DCHECK_IS_ON() 1
+#endif
+
+// Definitions for DLOG et al.
+
+#if ENABLE_DLOG
+
+#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
+#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)
+#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)
+
+#else  // ENABLE_DLOG
+
+// If ENABLE_DLOG is off, we want to avoid emitting any references to
+// |condition| (which may reference a variable defined only if NDEBUG
+// is not defined).  Contrast this with DCHECK et al., which has
+// different behavior.
+
+#define DLOG_IS_ON(severity) false
+#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
+#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+
+#endif  // ENABLE_DLOG
+
+// DEBUG_MODE is for uses like
+//   if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+//   #ifndef NDEBUG
+//     foo.CheckThatFoo();
+//   #endif
+//
+// We tie its state to ENABLE_DLOG.
+enum { DEBUG_MODE = ENABLE_DLOG };
+
+#undef ENABLE_DLOG
+
+#define DLOG(severity) LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DPLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DVLOG(verboselevel) DVLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+// Definitions for DCHECK et al.
+
+#if DCHECK_IS_ON()
+
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+  COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+
+#else  // DCHECK_IS_ON()
+
+// These are just dummy values.
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+  COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO
+const LogSeverity LOG_DCHECK = LOG_INFO;
+
+#endif  // DCHECK_IS_ON()
+
+// DCHECK et al. make sure to reference |condition| regardless of
+// whether DCHECKs are enabled; this is so that we don't get unused
+// variable warnings if the only use of a variable is in a DCHECK.
+// This behavior is different from DLOG_IF et al.
+
+#define DCHECK(condition)                                         \
+  LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \
+      << "Check failed: " #condition ". "
+
+#define DPCHECK(condition)                                         \
+  LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \
+      << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2)                                    \
+  if (DCHECK_IS_ON())                                                      \
+    if (std::string* _result = cef::logging::Check##name##Impl(            \
+            (val1), (val2), #val1 " " #op " " #val2))                      \
+  cef::logging::LogMessage(__FILE__, __LINE__, ::cef::logging::LOG_DCHECK, \
+                           _result)                                        \
+      .stream()
+
+// Equality/Inequality checks - compare two values, and log a
+// LOG_DCHECK message including the two values when the result is not
+// as expected.  The values must have operator<<(ostream, ...)
+// defined.
+//
+// You may append to the error message like so:
+//   DCHECK_NE(1, 2) << ": The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here.  In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+//   DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These may not compile correctly if one of the arguments is a pointer
+// and the other is NULL. To work around this, simply static_cast NULL to the
+// type of the desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, <, val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, >, val1, val2)
+
+#if defined(NDEBUG) && defined(OS_CHROMEOS)
+#define NOTREACHED() \
+  LOG(ERROR) << "NOTREACHED() hit in " << __FUNCTION__ << ". "
+#else
+#define NOTREACHED() DCHECK(false)
+#endif
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message.  You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though.  You should use the LOG() macro (and variants thereof)
+// above.
+class LogMessage {
+ public:
+  // Used for LOG(severity).
+  LogMessage(const char* file, int line, LogSeverity severity);
+
+  // Used for CHECK_EQ(), etc. Takes ownership of the given string.
+  // Implied severity = LOG_FATAL.
+  LogMessage(const char* file, int line, std::string* result);
+
+  // Used for DCHECK_EQ(), etc. Takes ownership of the given string.
+  LogMessage(const char* file,
+             int line,
+             LogSeverity severity,
+             std::string* result);
+
+  ~LogMessage();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  LogSeverity severity_;
+  std::ostringstream stream_;
+
+  // The file and line information passed in to the constructor.
+  const char* file_;
+  const int line_;
+
+#if defined(OS_WIN)
+  // Stores the current value of GetLastError in the constructor and restores
+  // it in the destructor by calling SetLastError.
+  // This is useful since the LogMessage class uses a lot of Win32 calls
+  // that will lose the value of GLE and the code that called the log function
+  // will have lost the thread error value when the log call returns.
+  class SaveLastError {
+   public:
+    SaveLastError();
+    ~SaveLastError();
+
+    unsigned long get_error() const { return last_error_; }
+
+   protected:
+    unsigned long last_error_;
+  };
+
+  SaveLastError last_error_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// A non-macro interface to the log facility; (useful
+// when the logging level is not a compile-time constant).
+inline void LogAtLevel(int const log_level, std::string const& msg) {
+  LogMessage(__FILE__, __LINE__, log_level).stream() << msg;
+}
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros.  This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+  LogMessageVoidify() {}
+  // This has to be an operator with a precedence lower than << but
+  // higher than ?:
+  void operator&(std::ostream&) {}
+};
+
+#if defined(OS_WIN)
+typedef unsigned long SystemErrorCode;
+#elif defined(OS_POSIX)
+typedef int SystemErrorCode;
+#endif
+
+// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
+// pull in windows.h just for GetLastError() and DWORD.
+SystemErrorCode GetLastSystemErrorCode();
+std::string SystemErrorCodeToString(SystemErrorCode error_code);
+
+#if defined(OS_WIN)
+// Appends a formatted system message of the GetLastError() type.
+class Win32ErrorLogMessage {
+ public:
+  Win32ErrorLogMessage(const char* file,
+                       int line,
+                       LogSeverity severity,
+                       SystemErrorCode err);
+
+  // Appends the error message before destructing the encapsulated class.
+  ~Win32ErrorLogMessage();
+
+  std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+  SystemErrorCode err_;
+  LogMessage log_message_;
+
+  DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
+};
+#elif defined(OS_POSIX)
+// Appends a formatted system message of the errno type
+class ErrnoLogMessage {
+ public:
+  ErrnoLogMessage(const char* file,
+                  int line,
+                  LogSeverity severity,
+                  SystemErrorCode err);
+
+  // Appends the error message before destructing the encapsulated class.
+  ~ErrnoLogMessage();
+
+  std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+  SystemErrorCode err_;
+  LogMessage log_message_;
+
+  DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
+};
+#endif  // OS_WIN
+
+}  // namespace logging
+}  // namespace cef
+
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these
+// operators.
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+  return out << wstr.c_str();
+}
+
+// The NOTIMPLEMENTED() macro annotates codepaths which have
+// not been implemented yet.
+//
+// The implementation of this macro is controlled by NOTIMPLEMENTED_POLICY:
+//   0 -- Do nothing (stripped by compiler)
+//   1 -- Warn at compile time
+//   2 -- Fail at compile time
+//   3 -- Fail at runtime (DCHECK)
+//   4 -- [default] LOG(ERROR) at runtime
+//   5 -- LOG(ERROR) at runtime, only once per call-site
+
+#ifndef NOTIMPLEMENTED_POLICY
+#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
+#define NOTIMPLEMENTED_POLICY 0
+#else
+// Select default policy: LOG(ERROR)
+#define NOTIMPLEMENTED_POLICY 4
+#endif
+#endif
+
+#if defined(COMPILER_GCC)
+// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
+// of the current function in the NOTIMPLEMENTED message.
+#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
+#else
+#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
+#endif
+
+#if NOTIMPLEMENTED_POLICY == 0
+#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
+#elif NOTIMPLEMENTED_POLICY == 1
+// TODO, figure out how to generate a warning
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 2
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 3
+#define NOTIMPLEMENTED() NOTREACHED()
+#elif NOTIMPLEMENTED_POLICY == 4
+#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
+#elif NOTIMPLEMENTED_POLICY == 5
+#define NOTIMPLEMENTED()                               \
+  do {                                                 \
+    static bool logged_once = false;                   \
+    LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
+    logged_once = true;                                \
+  } while (0);                                         \
+  EAT_STREAM_PARAMETERS
+#endif
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_LOGGING_H_
diff --git a/src/include/base/cef_macros.h b/src/include/base/cef_macros.h
new file mode 100644
index 0000000..e714529
--- /dev/null
+++ b/src/include/base/cef_macros.h
@@ -0,0 +1,220 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_MACROS_H_
+#define CEF_INCLUDE_BASE_CEF_MACROS_H_
+#pragma once
+
+#if defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/macros.h"
+
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include <stddef.h>                  // For size_t.
+#include "include/base/cef_build.h"  // For COMPILER_MSVC
+
+#if !defined(arraysize)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example.  If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function.  In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below.  This is
+// due to a limitation in C++'s template system.  The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+// That gcc wants both of these prototypes seems mysterious. VC, for
+// its part, can't decide which to use (another mystery). Matching of
+// template overloads: the final frontier.
+#ifndef _MSC_VER
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+#endif  // !arraysize
+
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+#endif  // !DISALLOW_COPY_AND_ASSIGN
+
+#if !defined(DISALLOW_IMPLICIT_CONSTRUCTORS)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName();                                    \
+  DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+#endif  // !DISALLOW_IMPLICIT_CONSTRUCTORS
+
+#if !defined(COMPILE_ASSERT)
+
+// The COMPILE_ASSERT macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+//   COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES,
+//                  content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+//   COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+#if __cplusplus >= 201103L
+
+// Under C++11, just use static_assert.
+#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
+
+#else
+
+namespace cef {
+
+template <bool>
+struct CompileAssert {};
+
+}  // namespace cef
+
+#define COMPILE_ASSERT(expr, msg)          \
+  typedef cef::CompileAssert<(bool(expr))> \
+      msg[bool(expr) ? 1 : -1] ALLOW_UNUSED_TYPE
+
+// Implementation details of COMPILE_ASSERT:
+//
+// - COMPILE_ASSERT works by defining an array type that has -1
+//   elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+//     #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+//   does not work, as gcc supports variable-length arrays whose sizes
+//   are determined at run-time (this is gcc's extension and not part
+//   of the C++ standard).  As a result, gcc fails to reject the
+//   following code with the simple definition:
+//
+//     int foo;
+//     COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
+//                               // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+//   expr is a compile-time constant.  (Template arguments must be
+//   determined at compile-time.)
+//
+// - The outer parentheses in CompileAssert<(bool(expr))> are necessary
+//   to work around a bug in gcc 3.4.4 and 4.0.1.  If we had written
+//
+//     CompileAssert<bool(expr)>
+//
+//   instead, these compilers will refuse to compile
+//
+//     COMPILE_ASSERT(5 > 0, some_message);
+//
+//   (They seem to think the ">" in "5 > 0" marks the end of the
+//   template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+//     ((expr) ? 1 : -1).
+//
+//   This is to avoid running into a bug in MS VC 7.1, which
+//   causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+#endif  // !(__cplusplus >= 201103L)
+
+#endif  // !defined(COMPILE_ASSERT)
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#if !defined(MSVC_PUSH_DISABLE_WARNING) && defined(COMPILER_MSVC)
+
+// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
+// The warning remains disabled until popped by MSVC_POP_WARNING.
+#define MSVC_PUSH_DISABLE_WARNING(n) \
+  __pragma(warning(push)) __pragma(warning(disable : n))
+
+// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level.  The level
+// remains in effect until popped by MSVC_POP_WARNING().  Use 0 to disable all
+// warnings.
+#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n))
+
+// Pop effects of innermost MSVC_PUSH_* macro.
+#define MSVC_POP_WARNING() __pragma(warning(pop))
+
+#endif  // !defined(MSVC_PUSH_DISABLE_WARNING) && defined(COMPILER_MSVC)
+
+#if !defined(ALLOW_THIS_IN_INITIALIZER_LIST)
+#if defined(COMPILER_MSVC)
+// Allows |this| to be passed as an argument in constructor initializer lists.
+// This uses push/pop instead of the seemingly simpler suppress feature to avoid
+// having the warning be disabled for more than just |code|.
+//
+// Example usage:
+// Foo::Foo() : x(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(y(this)), z(3) {}
+//
+// Compiler warning C4355: 'this': used in base member initializer list:
+// http://msdn.microsoft.com/en-us/library/3c594ae3(VS.80).aspx
+#define ALLOW_THIS_IN_INITIALIZER_LIST(code) \
+  MSVC_PUSH_DISABLE_WARNING(4355)            \
+  code MSVC_POP_WARNING()
+#else  // !COMPILER_MSVC
+#define ALLOW_THIS_IN_INITIALIZER_LIST(code) code
+#endif  // !COMPILER_MSVC
+#endif  // !ALLOW_THIS_IN_INITIALIZER_LIST
+
+#endif  // CEF_INCLUDE_BASE_CEF_MACROS_H_
diff --git a/src/include/base/cef_move.h b/src/include/base/cef_move.h
new file mode 100644
index 0000000..da47d2d
--- /dev/null
+++ b/src/include/base/cef_move.h
@@ -0,0 +1,261 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_MOVE_H_
+#define CEF_INCLUDE_BASE_CEF_MOVE_H_
+
+#if defined(MOVE_ONLY_TYPE_FOR_CPP_03)
+// Do nothing if the macro in this header has already been defined.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/move.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+// Macro with the boilerplate that makes a type move-only in C++03.
+//
+// USAGE
+//
+// This macro should be used instead of DISALLOW_COPY_AND_ASSIGN to create
+// a "move-only" type.  Unlike DISALLOW_COPY_AND_ASSIGN, this macro should be
+// the first line in a class declaration.
+//
+// A class using this macro must call .Pass() (or somehow be an r-value already)
+// before it can be:
+//
+//   * Passed as a function argument
+//   * Used as the right-hand side of an assignment
+//   * Returned from a function
+//
+// Each class will still need to define their own "move constructor" and "move
+// operator=" to make this useful.  Here's an example of the macro, the move
+// constructor, and the move operator= from the scoped_ptr class:
+//
+//  template <typename T>
+//  class scoped_ptr {
+//     MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
+//   public:
+//    scoped_ptr(RValue& other) : ptr_(other.release()) { }
+//    scoped_ptr& operator=(RValue& other) {
+//      swap(other);
+//      return *this;
+//    }
+//  };
+//
+// Note that the constructor must NOT be marked explicit.
+//
+// For consistency, the second parameter to the macro should always be RValue
+// unless you have a strong reason to do otherwise.  It is only exposed as a
+// macro parameter so that the move constructor and move operator= don't look
+// like they're using a phantom type.
+//
+//
+// HOW THIS WORKS
+//
+// For a thorough explanation of this technique, see:
+//
+//   http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor
+//
+// The summary is that we take advantage of 2 properties:
+//
+//   1) non-const references will not bind to r-values.
+//   2) C++ can apply one user-defined conversion when initializing a
+//      variable.
+//
+// The first lets us disable the copy constructor and assignment operator
+// by declaring private version of them with a non-const reference parameter.
+//
+// For l-values, direct initialization still fails like in
+// DISALLOW_COPY_AND_ASSIGN because the copy constructor and assignment
+// operators are private.
+//
+// For r-values, the situation is different. The copy constructor and
+// assignment operator are not viable due to (1), so we are trying to call
+// a non-existent constructor and non-existing operator= rather than a private
+// one.  Since we have not committed an error quite yet, we can provide an
+// alternate conversion sequence and a constructor.  We add
+//
+//   * a private struct named "RValue"
+//   * a user-defined conversion "operator RValue()"
+//   * a "move constructor" and "move operator=" that take the RValue& as
+//     their sole parameter.
+//
+// Only r-values will trigger this sequence and execute our "move constructor"
+// or "move operator=."  L-values will match the private copy constructor and
+// operator= first giving a "private in this context" error.  This combination
+// gives us a move-only type.
+//
+// For signaling a destructive transfer of data from an l-value, we provide a
+// method named Pass() which creates an r-value for the current instance
+// triggering the move constructor or move operator=.
+//
+// Other ways to get r-values is to use the result of an expression like a
+// function call.
+//
+// Here's an example with comments explaining what gets triggered where:
+//
+//    class Foo {
+//      MOVE_ONLY_TYPE_FOR_CPP_03(Foo, RValue);
+//
+//     public:
+//       ... API ...
+//       Foo(RValue other);           // Move constructor.
+//       Foo& operator=(RValue rhs);  // Move operator=
+//    };
+//
+//    Foo MakeFoo();  // Function that returns a Foo.
+//
+//    Foo f;
+//    Foo f_copy(f);  // ERROR: Foo(Foo&) is private in this context.
+//    Foo f_assign;
+//    f_assign = f;   // ERROR: operator=(Foo&) is private in this context.
+//
+//
+//    Foo f(MakeFoo());      // R-value so alternate conversion executed.
+//    Foo f_copy(f.Pass());  // R-value so alternate conversion executed.
+//    f = f_copy.Pass();     // R-value so alternate conversion executed.
+//
+//
+// IMPLEMENTATION SUBTLETIES WITH RValue
+//
+// The RValue struct is just a container for a pointer back to the original
+// object. It should only ever be created as a temporary, and no external
+// class should ever declare it or use it in a parameter.
+//
+// It is tempting to want to use the RValue type in function parameters, but
+// excluding the limited usage here for the move constructor and move
+// operator=, doing so would mean that the function could take both r-values
+// and l-values equially which is unexpected.  See COMPARED To Boost.Move for
+// more details.
+//
+// An alternate, and incorrect, implementation of the RValue class used by
+// Boost.Move makes RValue a fieldless child of the move-only type. RValue&
+// is then used in place of RValue in the various operators.  The RValue& is
+// "created" by doing *reinterpret_cast<RValue*>(this).  This has the appeal
+// of never creating a temporary RValue struct even with optimizations
+// disabled.  Also, by virtue of inheritance you can treat the RValue
+// reference as if it were the move-only type itself.  Unfortunately,
+// using the result of this reinterpret_cast<> is actually undefined behavior
+// due to C++98 5.2.10.7. In certain compilers (e.g., NaCl) the optimizer
+// will generate non-working code.
+//
+// In optimized builds, both implementations generate the same assembly so we
+// choose the one that adheres to the standard.
+//
+//
+// WHY HAVE typedef void MoveOnlyTypeForCPP03
+//
+// Callback<>/Bind() needs to understand movable-but-not-copyable semantics
+// to call .Pass() appropriately when it is expected to transfer the value.
+// The cryptic typedef MoveOnlyTypeForCPP03 is added to make this check
+// easy and automatic in helper templates for Callback<>/Bind().
+// See IsMoveOnlyType template and its usage in base/callback_internal.h
+// for more details.
+//
+//
+// COMPARED TO C++11
+//
+// In C++11, you would implement this functionality using an r-value reference
+// and our .Pass() method would be replaced with a call to std::move().
+//
+// This emulation also has a deficiency where it uses up the single
+// user-defined conversion allowed by C++ during initialization.  This can
+// cause problems in some API edge cases.  For instance, in scoped_ptr, it is
+// impossible to make a function "void Foo(scoped_ptr<Parent> p)" accept a
+// value of type scoped_ptr<Child> even if you add a constructor to
+// scoped_ptr<> that would make it look like it should work.  C++11 does not
+// have this deficiency.
+//
+//
+// COMPARED TO Boost.Move
+//
+// Our implementation similar to Boost.Move, but we keep the RValue struct
+// private to the move-only type, and we don't use the reinterpret_cast<> hack.
+//
+// In Boost.Move, RValue is the boost::rv<> template.  This type can be used
+// when writing APIs like:
+//
+//   void MyFunc(boost::rv<Foo>& f)
+//
+// that can take advantage of rv<> to avoid extra copies of a type.  However you
+// would still be able to call this version of MyFunc with an l-value:
+//
+//   Foo f;
+//   MyFunc(f);  // Uh oh, we probably just destroyed |f| w/o calling Pass().
+//
+// unless someone is very careful to also declare a parallel override like:
+//
+//   void MyFunc(const Foo& f)
+//
+// that would catch the l-values first.  This was declared unsafe in C++11 and
+// a C++11 compiler will explicitly fail MyFunc(f).  Unfortunately, we cannot
+// ensure this in C++03.
+//
+// Since we have no need for writing such APIs yet, our implementation keeps
+// RValue private and uses a .Pass() method to do the conversion instead of
+// trying to write a version of "std::move()." Writing an API like std::move()
+// would require the RValue struct to be public.
+//
+//
+// CAVEATS
+//
+// If you include a move-only type as a field inside a class that does not
+// explicitly declare a copy constructor, the containing class's implicit
+// copy constructor will change from Containing(const Containing&) to
+// Containing(Containing&).  This can cause some unexpected errors.
+//
+//   http://llvm.org/bugs/show_bug.cgi?id=11528
+//
+// The workaround is to explicitly declare your copy constructor.
+//
+#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type)       \
+ private:                                                  \
+  struct rvalue_type {                                     \
+    explicit rvalue_type(type* object) : object(object) {} \
+    type* object;                                          \
+  };                                                       \
+  type(type&);                                             \
+  void operator=(type&);                                   \
+                                                           \
+ public:                                                   \
+  operator rvalue_type() { return rvalue_type(this); }     \
+  type Pass() { return type(rvalue_type(this)); }          \
+  typedef void MoveOnlyTypeForCPP03;                       \
+                                                           \
+ private:
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_MOVE_H_
diff --git a/src/include/base/cef_platform_thread.h b/src/include/base/cef_platform_thread.h
new file mode 100644
index 0000000..d3fdd79
--- /dev/null
+++ b/src/include/base/cef_platform_thread.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// WARNING: You should *NOT* be using this class directly.  PlatformThread is
+// the low-level platform-specific abstraction to the OS's threading interface.
+// You should instead be using a message-loop driven Thread, see thread.h.
+
+#ifndef CEF_INCLUDE_BASE_PLATFORM_THREAD_H_
+#define CEF_INCLUDE_BASE_PLATFORM_THREAD_H_
+
+#if defined(BASE_THREADING_PLATFORM_THREAD_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/threading/platform_thread.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_basictypes.h"
+#include "include/base/cef_build.h"
+#include "include/internal/cef_thread_internal.h"
+
+namespace base {
+
+// Used for logging. Always an integer value.
+typedef cef_platform_thread_id_t PlatformThreadId;
+
+// Used for thread checking and debugging.
+// Meant to be as fast as possible.
+// These are produced by PlatformThread::CurrentRef(), and used to later
+// check if we are on the same thread or not by using ==. These are safe
+// to copy between threads, but can't be copied to another process as they
+// have no meaning there. Also, the internal identifier can be re-used
+// after a thread dies, so a PlatformThreadRef cannot be reliably used
+// to distinguish a new thread from an old, dead thread.
+class PlatformThreadRef {
+ public:
+  typedef cef_platform_thread_handle_t RefType;
+
+  PlatformThreadRef() : id_(0) {}
+
+  explicit PlatformThreadRef(RefType id) : id_(id) {}
+
+  bool operator==(PlatformThreadRef other) const { return id_ == other.id_; }
+
+  bool is_null() const { return id_ == 0; }
+
+ private:
+  RefType id_;
+};
+
+// A namespace for low-level thread functions.
+// Chromium uses a class with static methods but CEF uses an actual namespace
+// to avoid linker problems with the sandbox libaries on Windows.
+namespace PlatformThread {
+
+// Gets the current thread id, which may be useful for logging purposes.
+inline PlatformThreadId CurrentId() {
+  return cef_get_current_platform_thread_id();
+}
+
+// Gets the current thread reference, which can be used to check if
+// we're on the right thread quickly.
+inline PlatformThreadRef CurrentRef() {
+  return PlatformThreadRef(cef_get_current_platform_thread_handle());
+}
+
+}  // namespace PlatformThread
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_PLATFORM_THREAD_H_
diff --git a/src/include/base/cef_ref_counted.h b/src/include/base/cef_ref_counted.h
new file mode 100644
index 0000000..7a68707
--- /dev/null
+++ b/src/include/base/cef_ref_counted.h
@@ -0,0 +1,370 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef CEF_INCLUDE_BASE_CEF_REF_COUNTED_H_
+#define CEF_INCLUDE_BASE_CEF_REF_COUNTED_H_
+#pragma once
+
+#if defined(BASE_MEMORY_REF_COUNTED_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/memory/ref_counted.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include <cassert>
+
+#include "include/base/cef_atomic_ref_count.h"
+#include "include/base/cef_build.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+
+namespace base {
+
+namespace cef_subtle {
+
+class RefCountedBase {
+ public:
+  bool HasOneRef() const { return ref_count_ == 1; }
+  bool HasAtLeastOneRef() const { return ref_count_ >= 1; }
+
+ protected:
+  RefCountedBase()
+      : ref_count_(0)
+#if DCHECK_IS_ON()
+        ,
+        in_dtor_(false)
+#endif
+  {
+  }
+
+  ~RefCountedBase() {
+#if DCHECK_IS_ON()
+    DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
+#endif
+  }
+
+  void AddRef() const {
+#if DCHECK_IS_ON()
+    DCHECK(!in_dtor_);
+#endif
+    ++ref_count_;
+  }
+
+  // Returns true if the object should self-delete.
+  bool Release() const {
+#if DCHECK_IS_ON()
+    DCHECK(!in_dtor_);
+#endif
+    if (--ref_count_ == 0) {
+#if DCHECK_IS_ON()
+      in_dtor_ = true;
+#endif
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  mutable int ref_count_;
+#if DCHECK_IS_ON()
+  mutable bool in_dtor_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
+};
+
+class RefCountedThreadSafeBase {
+ public:
+  bool HasOneRef() const;
+  bool HasAtLeastOneRef() const;
+
+ protected:
+  RefCountedThreadSafeBase();
+  ~RefCountedThreadSafeBase();
+
+  void AddRef() const;
+
+  // Returns true if the object should self-delete.
+  bool Release() const;
+
+ private:
+  mutable AtomicRefCount ref_count_;
+#if DCHECK_IS_ON()
+  mutable bool in_dtor_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase);
+};
+
+}  // namespace cef_subtle
+
+//
+// A base class for reference counted classes.  Otherwise, known as a cheap
+// knock-off of WebKit's RefCounted<T> class.  To use this guy just extend your
+// class from it like so:
+//
+//   class MyFoo : public base::RefCounted<MyFoo> {
+//    ...
+//    private:
+//     friend class base::RefCounted<MyFoo>;
+//     ~MyFoo();
+//   };
+//
+// You should always make your destructor private, to avoid any code deleting
+// the object accidently while there are references to it.
+template <class T>
+class RefCounted : public cef_subtle::RefCountedBase {
+ public:
+  RefCounted() {}
+
+  void AddRef() const { cef_subtle::RefCountedBase::AddRef(); }
+
+  void Release() const {
+    if (cef_subtle::RefCountedBase::Release()) {
+      delete static_cast<const T*>(this);
+    }
+  }
+
+ protected:
+  ~RefCounted() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RefCounted<T>);
+};
+
+// Forward declaration.
+template <class T, typename Traits>
+class RefCountedThreadSafe;
+
+// Default traits for RefCountedThreadSafe<T>.  Deletes the object when its ref
+// count reaches 0.  Overload to delete it on a different thread etc.
+template <typename T>
+struct DefaultRefCountedThreadSafeTraits {
+  static void Destruct(const T* x) {
+    // Delete through RefCountedThreadSafe to make child classes only need to be
+    // friend with RefCountedThreadSafe instead of this struct, which is an
+    // implementation detail.
+    RefCountedThreadSafe<T, DefaultRefCountedThreadSafeTraits>::DeleteInternal(
+        x);
+  }
+};
+
+//
+// A thread-safe variant of RefCounted<T>
+//
+//   class MyFoo : public base::RefCountedThreadSafe<MyFoo> {
+//    ...
+//   };
+//
+// If you're using the default trait, then you should add compile time
+// asserts that no one else is deleting your object.  i.e.
+//    private:
+//     friend class base::RefCountedThreadSafe<MyFoo>;
+//     ~MyFoo();
+template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T>>
+class RefCountedThreadSafe : public cef_subtle::RefCountedThreadSafeBase {
+ public:
+  RefCountedThreadSafe() {}
+
+  void AddRef() const { cef_subtle::RefCountedThreadSafeBase::AddRef(); }
+
+  void Release() const {
+    if (cef_subtle::RefCountedThreadSafeBase::Release()) {
+      Traits::Destruct(static_cast<const T*>(this));
+    }
+  }
+
+ protected:
+  ~RefCountedThreadSafe() {}
+
+ private:
+  friend struct DefaultRefCountedThreadSafeTraits<T>;
+  static void DeleteInternal(const T* x) { delete x; }
+
+  DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe);
+};
+
+//
+// A thread-safe wrapper for some piece of data so we can place other
+// things in scoped_refptrs<>.
+//
+template <typename T>
+class RefCountedData
+    : public base::RefCountedThreadSafe<base::RefCountedData<T>> {
+ public:
+  RefCountedData() : data() {}
+  RefCountedData(const T& in_value) : data(in_value) {}
+
+  T data;
+
+ private:
+  friend class base::RefCountedThreadSafe<base::RefCountedData<T>>;
+  ~RefCountedData() {}
+};
+
+}  // namespace base
+
+//
+// A smart pointer class for reference counted objects.  Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference.  Sample usage:
+//
+//   class MyFoo : public RefCounted<MyFoo> {
+//    ...
+//   };
+//
+//   void some_function() {
+//     scoped_refptr<MyFoo> foo = new MyFoo();
+//     foo->Method(param);
+//     // |foo| is released when this function returns
+//   }
+//
+//   void some_other_function() {
+//     scoped_refptr<MyFoo> foo = new MyFoo();
+//     ...
+//     foo = NULL;  // explicitly releases |foo|
+//     ...
+//     if (foo)
+//       foo->Method(param);
+//   }
+//
+// The above examples show how scoped_refptr<T> acts like a pointer to T.
+// Given two scoped_refptr<T> classes, it is also possible to exchange
+// references between the two objects, like so:
+//
+//   {
+//     scoped_refptr<MyFoo> a = new MyFoo();
+//     scoped_refptr<MyFoo> b;
+//
+//     b.swap(a);
+//     // now, |b| references the MyFoo object, and |a| references NULL.
+//   }
+//
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+//
+//   {
+//     scoped_refptr<MyFoo> a = new MyFoo();
+//     scoped_refptr<MyFoo> b;
+//
+//     b = a;
+//     // now, |a| and |b| each own a reference to the same MyFoo object.
+//   }
+//
+template <class T>
+class scoped_refptr {
+ public:
+  typedef T element_type;
+
+  scoped_refptr() : ptr_(NULL) {}
+
+  scoped_refptr(T* p) : ptr_(p) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  template <typename U>
+  scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  ~scoped_refptr() {
+    if (ptr_)
+      ptr_->Release();
+  }
+
+  T* get() const { return ptr_; }
+
+  // Allow scoped_refptr<C> to be used in boolean expression
+  // and comparison operations.
+  operator T*() const { return ptr_; }
+
+  T* operator->() const {
+    assert(ptr_ != NULL);
+    return ptr_;
+  }
+
+  scoped_refptr<T>& operator=(T* p) {
+    // AddRef first so that self assignment should work
+    if (p)
+      p->AddRef();
+    T* old_ptr = ptr_;
+    ptr_ = p;
+    if (old_ptr)
+      old_ptr->Release();
+    return *this;
+  }
+
+  scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
+    return *this = r.ptr_;
+  }
+
+  template <typename U>
+  scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
+    return *this = r.get();
+  }
+
+  void swap(T** pp) {
+    T* p = ptr_;
+    ptr_ = *pp;
+    *pp = p;
+  }
+
+  void swap(scoped_refptr<T>& r) { swap(&r.ptr_); }
+
+ protected:
+  T* ptr_;
+};
+
+// Handy utility for creating a scoped_refptr<T> out of a T* explicitly without
+// having to retype all the template arguments
+template <typename T>
+scoped_refptr<T> make_scoped_refptr(T* t) {
+  return scoped_refptr<T>(t);
+}
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_REF_COUNTED_H_
diff --git a/src/include/base/cef_scoped_ptr.h b/src/include/base/cef_scoped_ptr.h
new file mode 100644
index 0000000..eb9e0e2
--- /dev/null
+++ b/src/include/base/cef_scoped_ptr.h
@@ -0,0 +1,625 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Scopers help you manage ownership of a pointer, helping you easily manage a
+// pointer within a scope, and automatically destroying the pointer at the end
+// of a scope.  There are two main classes you will use, which correspond to the
+// operators new/delete and new[]/delete[].
+//
+// Example usage (scoped_ptr<T>):
+//   {
+//     scoped_ptr<Foo> foo(new Foo("wee"));
+//   }  // foo goes out of scope, releasing the pointer with it.
+//
+//   {
+//     scoped_ptr<Foo> foo;          // No pointer managed.
+//     foo.reset(new Foo("wee"));    // Now a pointer is managed.
+//     foo.reset(new Foo("wee2"));   // Foo("wee") was destroyed.
+//     foo.reset(new Foo("wee3"));   // Foo("wee2") was destroyed.
+//     foo->Method();                // Foo::Method() called.
+//     foo.get()->Method();          // Foo::Method() called.
+//     SomeFunc(foo.release());      // SomeFunc takes ownership, foo no longer
+//                                   // manages a pointer.
+//     foo.reset(new Foo("wee4"));   // foo manages a pointer again.
+//     foo.reset();                  // Foo("wee4") destroyed, foo no longer
+//                                   // manages a pointer.
+//   }  // foo wasn't managing a pointer, so nothing was destroyed.
+//
+// Example usage (scoped_ptr<T[]>):
+//   {
+//     scoped_ptr<Foo[]> foo(new Foo[100]);
+//     foo.get()->Method();  // Foo::Method on the 0th element.
+//     foo[10].Method();     // Foo::Method on the 10th element.
+//   }
+//
+// These scopers also implement part of the functionality of C++11 unique_ptr
+// in that they are "movable but not copyable."  You can use the scopers in
+// the parameter and return types of functions to signify ownership transfer
+// in to and out of a function.  When calling a function that has a scoper
+// as the argument type, it must be called with the result of an analogous
+// scoper's Pass() function or another function that generates a temporary;
+// passing by copy will NOT work.  Here is an example using scoped_ptr:
+//
+//   void TakesOwnership(scoped_ptr<Foo> arg) {
+//     // Do something with arg
+//   }
+//   scoped_ptr<Foo> CreateFoo() {
+//     // No need for calling Pass() because we are constructing a temporary
+//     // for the return value.
+//     return scoped_ptr<Foo>(new Foo("new"));
+//   }
+//   scoped_ptr<Foo> PassThru(scoped_ptr<Foo> arg) {
+//     return arg.Pass();
+//   }
+//
+//   {
+//     scoped_ptr<Foo> ptr(new Foo("yay"));  // ptr manages Foo("yay").
+//     TakesOwnership(ptr.Pass());           // ptr no longer owns Foo("yay").
+//     scoped_ptr<Foo> ptr2 = CreateFoo();   // ptr2 owns the return Foo.
+//     scoped_ptr<Foo> ptr3 =                // ptr3 now owns what was in ptr2.
+//         PassThru(ptr2.Pass());            // ptr2 is correspondingly NULL.
+//   }
+//
+// Notice that if you do not call Pass() when returning from PassThru(), or
+// when invoking TakesOwnership(), the code will not compile because scopers
+// are not copyable; they only implement move semantics which require calling
+// the Pass() function to signify a destructive transfer of state. CreateFoo()
+// is different though because we are constructing a temporary on the return
+// line and thus can avoid needing to call Pass().
+//
+// Pass() properly handles upcast in initialization, i.e. you can use a
+// scoped_ptr<Child> to initialize a scoped_ptr<Parent>:
+//
+//   scoped_ptr<Foo> foo(new Foo());
+//   scoped_ptr<FooParent> parent(foo.Pass());
+//
+// PassAs<>() should be used to upcast return value in return statement:
+//
+//   scoped_ptr<Foo> CreateFoo() {
+//     scoped_ptr<FooChild> result(new FooChild());
+//     return result.PassAs<Foo>();
+//   }
+//
+// Note that PassAs<>() is implemented only for scoped_ptr<T>, but not for
+// scoped_ptr<T[]>. This is because casting array pointers may not be safe.
+
+#ifndef CEF_INCLUDE_BASE_CEF_MEMORY_SCOPED_PTR_H_
+#define CEF_INCLUDE_BASE_CEF_MEMORY_SCOPED_PTR_H_
+#pragma once
+
+#if defined(BASE_MEMORY_SCOPED_PTR_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// Do nothing when building CEF.
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+// This is an implementation designed to match the anticipated future TR2
+// implementation of the scoped_ptr class.
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <algorithm>  // For std::swap().
+
+#include "include/base/cef_basictypes.h"
+#include "include/base/cef_build.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_move.h"
+#include "include/base/cef_template_util.h"
+
+namespace base {
+
+namespace subtle {
+class RefCountedBase;
+class RefCountedThreadSafeBase;
+}  // namespace subtle
+
+// Function object which deletes its parameter, which must be a pointer.
+// If C is an array type, invokes 'delete[]' on the parameter; otherwise,
+// invokes 'delete'. The default deleter for scoped_ptr<T>.
+template <class T>
+struct DefaultDeleter {
+  DefaultDeleter() {}
+  template <typename U>
+  DefaultDeleter(const DefaultDeleter<U>& other) {
+    // IMPLEMENTATION NOTE: C++11 20.7.1.1.2p2 only provides this constructor
+    // if U* is implicitly convertible to T* and U is not an array type.
+    //
+    // Correct implementation should use SFINAE to disable this
+    // constructor. However, since there are no other 1-argument constructors,
+    // using a COMPILE_ASSERT() based on is_convertible<> and requiring
+    // complete types is simpler and will cause compile failures for equivalent
+    // misuses.
+    //
+    // Note, the is_convertible<U*, T*> check also ensures that U is not an
+    // array. T is guaranteed to be a non-array, so any U* where U is an array
+    // cannot convert to T*.
+    enum { T_must_be_complete = sizeof(T) };
+    enum { U_must_be_complete = sizeof(U) };
+    COMPILE_ASSERT((base::is_convertible<U*, T*>::value),
+                   U_ptr_must_implicitly_convert_to_T_ptr);
+  }
+  inline void operator()(T* ptr) const {
+    enum { type_must_be_complete = sizeof(T) };
+    delete ptr;
+  }
+};
+
+// Specialization of DefaultDeleter for array types.
+template <class T>
+struct DefaultDeleter<T[]> {
+  inline void operator()(T* ptr) const {
+    enum { type_must_be_complete = sizeof(T) };
+    delete[] ptr;
+  }
+
+ private:
+  // Disable this operator for any U != T because it is undefined to execute
+  // an array delete when the static type of the array mismatches the dynamic
+  // type.
+  //
+  // References:
+  //   C++98 [expr.delete]p3
+  //   http://cplusplus.github.com/LWG/lwg-defects.html#938
+  template <typename U>
+  void operator()(U* array) const;
+};
+
+template <class T, int n>
+struct DefaultDeleter<T[n]> {
+  // Never allow someone to declare something like scoped_ptr<int[10]>.
+  COMPILE_ASSERT(sizeof(T) == -1, do_not_use_array_with_size_as_type);
+};
+
+// Function object which invokes 'free' on its parameter, which must be
+// a pointer. Can be used to store malloc-allocated pointers in scoped_ptr:
+//
+// scoped_ptr<int, base::FreeDeleter> foo_ptr(
+//     static_cast<int*>(malloc(sizeof(int))));
+struct FreeDeleter {
+  inline void operator()(void* ptr) const { free(ptr); }
+};
+
+namespace cef_internal {
+
+template <typename T>
+struct IsNotRefCounted {
+  enum {
+    value =
+        !base::is_convertible<T*, base::subtle::RefCountedBase*>::value &&
+        !base::is_convertible<T*,
+                              base::subtle::RefCountedThreadSafeBase*>::value
+  };
+};
+
+// Minimal implementation of the core logic of scoped_ptr, suitable for
+// reuse in both scoped_ptr and its specializations.
+template <class T, class D>
+class scoped_ptr_impl {
+ public:
+  explicit scoped_ptr_impl(T* p) : data_(p) {}
+
+  // Initializer for deleters that have data parameters.
+  scoped_ptr_impl(T* p, const D& d) : data_(p, d) {}
+
+  // Templated constructor that destructively takes the value from another
+  // scoped_ptr_impl.
+  template <typename U, typename V>
+  scoped_ptr_impl(scoped_ptr_impl<U, V>* other)
+      : data_(other->release(), other->get_deleter()) {
+    // We do not support move-only deleters.  We could modify our move
+    // emulation to have base::subtle::move() and base::subtle::forward()
+    // functions that are imperfect emulations of their C++11 equivalents,
+    // but until there's a requirement, just assume deleters are copyable.
+  }
+
+  template <typename U, typename V>
+  void TakeState(scoped_ptr_impl<U, V>* other) {
+    // See comment in templated constructor above regarding lack of support
+    // for move-only deleters.
+    reset(other->release());
+    get_deleter() = other->get_deleter();
+  }
+
+  ~scoped_ptr_impl() {
+    if (data_.ptr != NULL) {
+      // Not using get_deleter() saves one function call in non-optimized
+      // builds.
+      static_cast<D&>(data_)(data_.ptr);
+    }
+  }
+
+  void reset(T* p) {
+    // This is a self-reset, which is no longer allowed: http://crbug.com/162971
+    if (p != NULL && p == data_.ptr)
+      abort();
+
+    // Note that running data_.ptr = p can lead to undefined behavior if
+    // get_deleter()(get()) deletes this. In order to prevent this, reset()
+    // should update the stored pointer before deleting its old value.
+    //
+    // However, changing reset() to use that behavior may cause current code to
+    // break in unexpected ways. If the destruction of the owned object
+    // dereferences the scoped_ptr when it is destroyed by a call to reset(),
+    // then it will incorrectly dispatch calls to |p| rather than the original
+    // value of |data_.ptr|.
+    //
+    // During the transition period, set the stored pointer to NULL while
+    // deleting the object. Eventually, this safety check will be removed to
+    // prevent the scenario initially described from occuring and
+    // http://crbug.com/176091 can be closed.
+    T* old = data_.ptr;
+    data_.ptr = NULL;
+    if (old != NULL)
+      static_cast<D&>(data_)(old);
+    data_.ptr = p;
+  }
+
+  T* get() const { return data_.ptr; }
+
+  D& get_deleter() { return data_; }
+  const D& get_deleter() const { return data_; }
+
+  void swap(scoped_ptr_impl& p2) {
+    // Standard swap idiom: 'using std::swap' ensures that std::swap is
+    // present in the overload set, but we call swap unqualified so that
+    // any more-specific overloads can be used, if available.
+    using std::swap;
+    swap(static_cast<D&>(data_), static_cast<D&>(p2.data_));
+    swap(data_.ptr, p2.data_.ptr);
+  }
+
+  T* release() {
+    T* old_ptr = data_.ptr;
+    data_.ptr = NULL;
+    return old_ptr;
+  }
+
+ private:
+  // Needed to allow type-converting constructor.
+  template <typename U, typename V>
+  friend class scoped_ptr_impl;
+
+  // Use the empty base class optimization to allow us to have a D
+  // member, while avoiding any space overhead for it when D is an
+  // empty class.  See e.g. http://www.cantrip.org/emptyopt.html for a good
+  // discussion of this technique.
+  struct Data : public D {
+    explicit Data(T* ptr_in) : ptr(ptr_in) {}
+    Data(T* ptr_in, const D& other) : D(other), ptr(ptr_in) {}
+    T* ptr;
+  };
+
+  Data data_;
+
+  DISALLOW_COPY_AND_ASSIGN(scoped_ptr_impl);
+};
+
+}  // namespace cef_internal
+
+}  // namespace base
+
+// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
+// automatically deletes the pointer it holds (if any).
+// That is, scoped_ptr<T> owns the T object that it points to.
+// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
+// Also like T*, scoped_ptr<T> is thread-compatible, and once you
+// dereference it, you get the thread safety guarantees of T.
+//
+// The size of scoped_ptr is small. On most compilers, when using the
+// DefaultDeleter, sizeof(scoped_ptr<T>) == sizeof(T*). Custom deleters will
+// increase the size proportional to whatever state they need to have. See
+// comments inside scoped_ptr_impl<> for details.
+//
+// Current implementation targets having a strict subset of  C++11's
+// unique_ptr<> features. Known deficiencies include not supporting move-only
+// deleteres, function pointers as deleters, and deleters with reference
+// types.
+template <class T, class D = base::DefaultDeleter<T>>
+class scoped_ptr {
+  MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
+
+  COMPILE_ASSERT(base::cef_internal::IsNotRefCounted<T>::value,
+                 T_is_refcounted_type_and_needs_scoped_refptr);
+
+ public:
+  // The element and deleter types.
+  typedef T element_type;
+  typedef D deleter_type;
+
+  // Constructor.  Defaults to initializing with NULL.
+  scoped_ptr() : impl_(NULL) {}
+
+  // Constructor.  Takes ownership of p.
+  explicit scoped_ptr(element_type* p) : impl_(p) {}
+
+  // Constructor.  Allows initialization of a stateful deleter.
+  scoped_ptr(element_type* p, const D& d) : impl_(p, d) {}
+
+  // Constructor.  Allows construction from a scoped_ptr rvalue for a
+  // convertible type and deleter.
+  //
+  // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this constructor distinct
+  // from the normal move constructor. By C++11 20.7.1.2.1.21, this constructor
+  // has different post-conditions if D is a reference type. Since this
+  // implementation does not support deleters with reference type,
+  // we do not need a separate move constructor allowing us to avoid one
+  // use of SFINAE. You only need to care about this if you modify the
+  // implementation of scoped_ptr.
+  template <typename U, typename V>
+  scoped_ptr(scoped_ptr<U, V> other) : impl_(&other.impl_) {
+    COMPILE_ASSERT(!base::is_array<U>::value, U_cannot_be_an_array);
+  }
+
+  // Constructor.  Move constructor for C++03 move emulation of this type.
+  scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) {}
+
+  // operator=.  Allows assignment from a scoped_ptr rvalue for a convertible
+  // type and deleter.
+  //
+  // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this operator= distinct from
+  // the normal move assignment operator. By C++11 20.7.1.2.3.4, this templated
+  // form has different requirements on for move-only Deleters. Since this
+  // implementation does not support move-only Deleters, we do not need a
+  // separate move assignment operator allowing us to avoid one use of SFINAE.
+  // You only need to care about this if you modify the implementation of
+  // scoped_ptr.
+  template <typename U, typename V>
+  scoped_ptr& operator=(scoped_ptr<U, V> rhs) {
+    COMPILE_ASSERT(!base::is_array<U>::value, U_cannot_be_an_array);
+    impl_.TakeState(&rhs.impl_);
+    return *this;
+  }
+
+  // Reset.  Deletes the currently owned object, if any.
+  // Then takes ownership of a new object, if given.
+  void reset(element_type* p = NULL) { impl_.reset(p); }
+
+  // Accessors to get the owned object.
+  // operator* and operator-> will assert() if there is no current object.
+  element_type& operator*() const {
+    assert(impl_.get() != NULL);
+    return *impl_.get();
+  }
+  element_type* operator->() const {
+    assert(impl_.get() != NULL);
+    return impl_.get();
+  }
+  element_type* get() const { return impl_.get(); }
+
+  // Access to the deleter.
+  deleter_type& get_deleter() { return impl_.get_deleter(); }
+  const deleter_type& get_deleter() const { return impl_.get_deleter(); }
+
+  // Allow scoped_ptr<element_type> to be used in boolean expressions, but not
+  // implicitly convertible to a real bool (which is dangerous).
+  //
+  // Note that this trick is only safe when the == and != operators
+  // are declared explicitly, as otherwise "scoped_ptr1 ==
+  // scoped_ptr2" will compile but do the wrong thing (i.e., convert
+  // to Testable and then do the comparison).
+ private:
+  typedef base::cef_internal::scoped_ptr_impl<element_type, deleter_type>
+      scoped_ptr::*Testable;
+
+ public:
+  operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; }
+
+  // Comparison operators.
+  // These return whether two scoped_ptr refer to the same object, not just to
+  // two different but equal objects.
+  bool operator==(const element_type* p) const { return impl_.get() == p; }
+  bool operator!=(const element_type* p) const { return impl_.get() != p; }
+
+  // Swap two scoped pointers.
+  void swap(scoped_ptr& p2) { impl_.swap(p2.impl_); }
+
+  // Release a pointer.
+  // The return value is the current pointer held by this object.
+  // If this object holds a NULL pointer, the return value is NULL.
+  // After this operation, this object will hold a NULL pointer,
+  // and will not own the object any more.
+  element_type* release() WARN_UNUSED_RESULT { return impl_.release(); }
+
+  // C++98 doesn't support functions templates with default parameters which
+  // makes it hard to write a PassAs() that understands converting the deleter
+  // while preserving simple calling semantics.
+  //
+  // Until there is a use case for PassAs() with custom deleters, just ignore
+  // the custom deleter.
+  template <typename PassAsType>
+  scoped_ptr<PassAsType> PassAs() {
+    return scoped_ptr<PassAsType>(Pass());
+  }
+
+ private:
+  // Needed to reach into |impl_| in the constructor.
+  template <typename U, typename V>
+  friend class scoped_ptr;
+  base::cef_internal::scoped_ptr_impl<element_type, deleter_type> impl_;
+
+  // Forbidden for API compatibility with std::unique_ptr.
+  explicit scoped_ptr(int disallow_construction_from_null);
+
+  // Forbid comparison of scoped_ptr types.  If U != T, it totally
+  // doesn't make sense, and if U == T, it still doesn't make sense
+  // because you should never have the same object owned by two different
+  // scoped_ptrs.
+  template <class U>
+  bool operator==(scoped_ptr<U> const& p2) const;
+  template <class U>
+  bool operator!=(scoped_ptr<U> const& p2) const;
+};
+
+template <class T, class D>
+class scoped_ptr<T[], D> {
+  MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
+
+ public:
+  // The element and deleter types.
+  typedef T element_type;
+  typedef D deleter_type;
+
+  // Constructor.  Defaults to initializing with NULL.
+  scoped_ptr() : impl_(NULL) {}
+
+  // Constructor. Stores the given array. Note that the argument's type
+  // must exactly match T*. In particular:
+  // - it cannot be a pointer to a type derived from T, because it is
+  //   inherently unsafe in the general case to access an array through a
+  //   pointer whose dynamic type does not match its static type (eg., if
+  //   T and the derived types had different sizes access would be
+  //   incorrectly calculated). Deletion is also always undefined
+  //   (C++98 [expr.delete]p3). If you're doing this, fix your code.
+  // - it cannot be NULL, because NULL is an integral expression, not a
+  //   pointer to T. Use the no-argument version instead of explicitly
+  //   passing NULL.
+  // - it cannot be const-qualified differently from T per unique_ptr spec
+  //   (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting
+  //   to work around this may use implicit_cast<const T*>().
+  //   However, because of the first bullet in this comment, users MUST
+  //   NOT use implicit_cast<Base*>() to upcast the static type of the array.
+  explicit scoped_ptr(element_type* array) : impl_(array) {}
+
+  // Constructor.  Move constructor for C++03 move emulation of this type.
+  scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) {}
+
+  // operator=.  Move operator= for C++03 move emulation of this type.
+  scoped_ptr& operator=(RValue rhs) {
+    impl_.TakeState(&rhs.object->impl_);
+    return *this;
+  }
+
+  // Reset.  Deletes the currently owned array, if any.
+  // Then takes ownership of a new object, if given.
+  void reset(element_type* array = NULL) { impl_.reset(array); }
+
+  // Accessors to get the owned array.
+  element_type& operator[](size_t i) const {
+    assert(impl_.get() != NULL);
+    return impl_.get()[i];
+  }
+  element_type* get() const { return impl_.get(); }
+
+  // Access to the deleter.
+  deleter_type& get_deleter() { return impl_.get_deleter(); }
+  const deleter_type& get_deleter() const { return impl_.get_deleter(); }
+
+  // Allow scoped_ptr<element_type> to be used in boolean expressions, but not
+  // implicitly convertible to a real bool (which is dangerous).
+ private:
+  typedef base::cef_internal::scoped_ptr_impl<element_type, deleter_type>
+      scoped_ptr::*Testable;
+
+ public:
+  operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; }
+
+  // Comparison operators.
+  // These return whether two scoped_ptr refer to the same object, not just to
+  // two different but equal objects.
+  bool operator==(element_type* array) const { return impl_.get() == array; }
+  bool operator!=(element_type* array) const { return impl_.get() != array; }
+
+  // Swap two scoped pointers.
+  void swap(scoped_ptr& p2) { impl_.swap(p2.impl_); }
+
+  // Release a pointer.
+  // The return value is the current pointer held by this object.
+  // If this object holds a NULL pointer, the return value is NULL.
+  // After this operation, this object will hold a NULL pointer,
+  // and will not own the object any more.
+  element_type* release() WARN_UNUSED_RESULT { return impl_.release(); }
+
+ private:
+  // Force element_type to be a complete type.
+  enum { type_must_be_complete = sizeof(element_type) };
+
+  // Actually hold the data.
+  base::cef_internal::scoped_ptr_impl<element_type, deleter_type> impl_;
+
+  // Disable initialization from any type other than element_type*, by
+  // providing a constructor that matches such an initialization, but is
+  // private and has no definition. This is disabled because it is not safe to
+  // call delete[] on an array whose static type does not match its dynamic
+  // type.
+  template <typename U>
+  explicit scoped_ptr(U* array);
+  explicit scoped_ptr(int disallow_construction_from_null);
+
+  // Disable reset() from any type other than element_type*, for the same
+  // reasons as the constructor above.
+  template <typename U>
+  void reset(U* array);
+  void reset(int disallow_reset_from_null);
+
+  // Forbid comparison of scoped_ptr types.  If U != T, it totally
+  // doesn't make sense, and if U == T, it still doesn't make sense
+  // because you should never have the same object owned by two different
+  // scoped_ptrs.
+  template <class U>
+  bool operator==(scoped_ptr<U> const& p2) const;
+  template <class U>
+  bool operator!=(scoped_ptr<U> const& p2) const;
+};
+
+// Free functions
+template <class T, class D>
+void swap(scoped_ptr<T, D>& p1, scoped_ptr<T, D>& p2) {
+  p1.swap(p2);
+}
+
+template <class T, class D>
+bool operator==(T* p1, const scoped_ptr<T, D>& p2) {
+  return p1 == p2.get();
+}
+
+template <class T, class D>
+bool operator!=(T* p1, const scoped_ptr<T, D>& p2) {
+  return p1 != p2.get();
+}
+
+// A function to convert T* into scoped_ptr<T>
+// Doing e.g. make_scoped_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for scoped_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+scoped_ptr<T> make_scoped_ptr(T* ptr) {
+  return scoped_ptr<T>(ptr);
+}
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_MEMORY_SCOPED_PTR_H_
diff --git a/src/include/base/cef_string16.h b/src/include/base/cef_string16.h
new file mode 100644
index 0000000..6afcb79
--- /dev/null
+++ b/src/include/base/cef_string16.h
@@ -0,0 +1,223 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2013
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_STRING16_H_
+#define CEF_INCLUDE_BASE_CEF_STRING16_H_
+#pragma once
+
+#if defined(BASE_STRINGS_STRING16_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/strings/string16.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+// WHAT:
+// A version of std::basic_string that provides 2-byte characters even when
+// wchar_t is not implemented as a 2-byte type. You can access this class as
+// string16. We also define char16, which string16 is based upon.
+//
+// WHY:
+// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2
+// data. Plenty of existing code operates on strings encoded as UTF-16.
+//
+// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make
+// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails
+// at run time, because it calls some functions (like wcslen) that come from
+// the system's native C library -- which was built with a 4-byte wchar_t!
+// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's
+// entirely improper on those systems where the encoding of wchar_t is defined
+// as UTF-32.
+//
+// Here, we define string16, which is similar to std::wstring but replaces all
+// libc functions with custom, 2-byte-char compatible routines. It is capable
+// of carrying UTF-16-encoded data.
+
+#include <stdio.h>
+#include <string>
+
+#include "include/base/cef_basictypes.h"
+
+#if defined(WCHAR_T_IS_UTF16)
+
+namespace base {
+
+typedef wchar_t char16;
+typedef std::wstring string16;
+typedef std::char_traits<wchar_t> string16_char_traits;
+
+}  // namespace base
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+#include <stdint.h>  // For uint16_t
+
+#include "include/base/cef_macros.h"
+
+namespace cef {
+namespace base {
+
+typedef uint16_t char16;
+
+// char16 versions of the functions required by string16_char_traits; these
+// are based on the wide character functions of similar names ("w" or "wcs"
+// instead of "c16").
+int c16memcmp(const char16* s1, const char16* s2, size_t n);
+size_t c16len(const char16* s);
+const char16* c16memchr(const char16* s, char16 c, size_t n);
+char16* c16memmove(char16* s1, const char16* s2, size_t n);
+char16* c16memcpy(char16* s1, const char16* s2, size_t n);
+char16* c16memset(char16* s, char16 c, size_t n);
+
+struct string16_char_traits {
+  typedef char16 char_type;
+  typedef int int_type;
+
+  // int_type needs to be able to hold each possible value of char_type, and in
+  // addition, the distinct value of eof().
+  COMPILE_ASSERT(sizeof(int_type) > sizeof(char_type), unexpected_type_width);
+
+  typedef std::streamoff off_type;
+  typedef mbstate_t state_type;
+  typedef std::fpos<state_type> pos_type;
+
+  static void assign(char_type& c1, const char_type& c2) { c1 = c2; }
+
+  static bool eq(const char_type& c1, const char_type& c2) { return c1 == c2; }
+  static bool lt(const char_type& c1, const char_type& c2) { return c1 < c2; }
+
+  static int compare(const char_type* s1, const char_type* s2, size_t n) {
+    return c16memcmp(s1, s2, n);
+  }
+
+  static size_t length(const char_type* s) { return c16len(s); }
+
+  static const char_type* find(const char_type* s,
+                               size_t n,
+                               const char_type& a) {
+    return c16memchr(s, a, n);
+  }
+
+  static char_type* move(char_type* s1, const char_type* s2, int_type n) {
+    return c16memmove(s1, s2, n);
+  }
+
+  static char_type* copy(char_type* s1, const char_type* s2, size_t n) {
+    return c16memcpy(s1, s2, n);
+  }
+
+  static char_type* assign(char_type* s, size_t n, char_type a) {
+    return c16memset(s, a, n);
+  }
+
+  static int_type not_eof(const int_type& c) {
+    return eq_int_type(c, eof()) ? 0 : c;
+  }
+
+  static char_type to_char_type(const int_type& c) { return char_type(c); }
+
+  static int_type to_int_type(const char_type& c) { return int_type(c); }
+
+  static bool eq_int_type(const int_type& c1, const int_type& c2) {
+    return c1 == c2;
+  }
+
+  static int_type eof() { return static_cast<int_type>(EOF); }
+};
+
+typedef std::basic_string<char16, string16_char_traits> string16;
+
+}  // namespace base
+}  // namespace cef
+
+namespace base {
+
+typedef cef::base::char16 char16;
+typedef cef::base::string16 string16;
+
+extern std::ostream& operator<<(std::ostream& out, const string16& str);
+
+// This is required by googletest to print a readable output on test failures.
+extern void PrintTo(const string16& str, std::ostream* out);
+
+}  // namespace base
+
+// The string class will be explicitly instantiated only once, in string16.cc.
+//
+// std::basic_string<> in GNU libstdc++ contains a static data member,
+// _S_empty_rep_storage, to represent empty strings.  When an operation such
+// as assignment or destruction is performed on a string, causing its existing
+// data member to be invalidated, it must not be freed if this static data
+// member is being used.  Otherwise, it counts as an attempt to free static
+// (and not allocated) data, which is a memory error.
+//
+// Generally, due to C++ template magic, _S_empty_rep_storage will be marked
+// as a coalesced symbol, meaning that the linker will combine multiple
+// instances into a single one when generating output.
+//
+// If a string class is used by multiple shared libraries, a problem occurs.
+// Each library will get its own copy of _S_empty_rep_storage.  When strings
+// are passed across a library boundary for alteration or destruction, memory
+// errors will result.  GNU libstdc++ contains a configuration option,
+// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which
+// disables the static data member optimization, but it's a good optimization
+// and non-STL code is generally at the mercy of the system's STL
+// configuration.  Fully-dynamic strings are not the default for GNU libstdc++
+// libstdc++ itself or for the libstdc++ installations on the systems we care
+// about, such as Mac OS X and relevant flavors of Linux.
+//
+// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 .
+//
+// To avoid problems, string classes need to be explicitly instantiated only
+// once, in exactly one library.  All other string users see it via an "extern"
+// declaration.  This is precisely how GNU libstdc++ handles
+// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring).
+//
+// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2),
+// in which the linker does not fully coalesce symbols when dead code
+// stripping is enabled.  This bug causes the memory errors described above
+// to occur even when a std::basic_string<> does not cross shared library
+// boundaries, such as in statically-linked executables.
+//
+// TODO(mark): File this bug with Apple and update this note with a bug number.
+
+extern template class std::basic_string<cef::base::char16,
+                                        cef::base::string16_char_traits>;
+
+#endif  // WCHAR_T_IS_UTF32
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_STRING16_H_
diff --git a/src/include/base/cef_template_util.h b/src/include/base/cef_template_util.h
new file mode 100644
index 0000000..38fa583
--- /dev/null
+++ b/src/include/base/cef_template_util.h
@@ -0,0 +1,214 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_CEF_TEMPLATE_UTIL_H_
+#define CEF_INCLUDE_BASE_CEF_TEMPLATE_UTIL_H_
+#pragma once
+
+#if defined(BASE_TEMPLATE_UTIL_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/template_util.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include <cstddef>  // For size_t.
+
+#include "include/base/cef_build.h"
+
+namespace base {
+
+// template definitions from tr1
+
+template <class T, T v>
+struct integral_constant {
+  static const T value = v;
+  typedef T value_type;
+  typedef integral_constant<T, v> type;
+};
+
+template <class T, T v>
+const T integral_constant<T, v>::value;
+
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+
+template <class T>
+struct is_pointer : false_type {};
+template <class T>
+struct is_pointer<T*> : true_type {};
+
+// Member function pointer detection up to four params. Add more as needed
+// below. This is built-in to C++ 11, and we can remove this when we switch.
+template <typename T>
+struct is_member_function_pointer : false_type {};
+
+template <typename R, typename Z>
+struct is_member_function_pointer<R (Z::*)()> : true_type {};
+template <typename R, typename Z>
+struct is_member_function_pointer<R (Z::*)() const> : true_type {};
+
+template <typename R, typename Z, typename A>
+struct is_member_function_pointer<R (Z::*)(A)> : true_type {};
+template <typename R, typename Z, typename A>
+struct is_member_function_pointer<R (Z::*)(A) const> : true_type {};
+
+template <typename R, typename Z, typename A, typename B>
+struct is_member_function_pointer<R (Z::*)(A, B)> : true_type {};
+template <typename R, typename Z, typename A, typename B>
+struct is_member_function_pointer<R (Z::*)(A, B) const> : true_type {};
+
+template <typename R, typename Z, typename A, typename B, typename C>
+struct is_member_function_pointer<R (Z::*)(A, B, C)> : true_type {};
+template <typename R, typename Z, typename A, typename B, typename C>
+struct is_member_function_pointer<R (Z::*)(A, B, C) const> : true_type {};
+
+template <typename R,
+          typename Z,
+          typename A,
+          typename B,
+          typename C,
+          typename D>
+struct is_member_function_pointer<R (Z::*)(A, B, C, D)> : true_type {};
+template <typename R,
+          typename Z,
+          typename A,
+          typename B,
+          typename C,
+          typename D>
+struct is_member_function_pointer<R (Z::*)(A, B, C, D) const> : true_type {};
+
+template <class T, class U>
+struct is_same : public false_type {};
+template <class T>
+struct is_same<T, T> : true_type {};
+
+template <class>
+struct is_array : public false_type {};
+template <class T, size_t n>
+struct is_array<T[n]> : public true_type {};
+template <class T>
+struct is_array<T[]> : public true_type {};
+
+template <class T>
+struct is_non_const_reference : false_type {};
+template <class T>
+struct is_non_const_reference<T&> : true_type {};
+template <class T>
+struct is_non_const_reference<const T&> : false_type {};
+
+template <class T>
+struct is_const : false_type {};
+template <class T>
+struct is_const<const T> : true_type {};
+
+template <class T>
+struct is_void : false_type {};
+template <>
+struct is_void<void> : true_type {};
+
+namespace cef_internal {
+
+// Types YesType and NoType are guaranteed such that sizeof(YesType) <
+// sizeof(NoType).
+typedef char YesType;
+
+struct NoType {
+  YesType dummy[2];
+};
+
+// This class is an implementation detail for is_convertible, and you
+// don't need to know how it works to use is_convertible. For those
+// who care: we declare two different functions, one whose argument is
+// of type To and one with a variadic argument list. We give them
+// return types of different size, so we can use sizeof to trick the
+// compiler into telling us which function it would have chosen if we
+// had called it with an argument of type From.  See Alexandrescu's
+// _Modern C++ Design_ for more details on this sort of trick.
+
+struct ConvertHelper {
+  template <typename To>
+  static YesType Test(To);
+
+  template <typename To>
+  static NoType Test(...);
+
+  template <typename From>
+  static From& Create();
+};
+
+// Used to determine if a type is a struct/union/class. Inspired by Boost's
+// is_class type_trait implementation.
+struct IsClassHelper {
+  template <typename C>
+  static YesType Test(void (C::*)(void));
+
+  template <typename C>
+  static NoType Test(...);
+};
+
+}  // namespace cef_internal
+
+// Inherits from true_type if From is convertible to To, false_type otherwise.
+//
+// Note that if the type is convertible, this will be a true_type REGARDLESS
+// of whether or not the conversion would emit a warning.
+template <typename From, typename To>
+struct is_convertible
+    : integral_constant<bool,
+                        sizeof(cef_internal::ConvertHelper::Test<To>(
+                            cef_internal::ConvertHelper::Create<From>())) ==
+                            sizeof(cef_internal::YesType)> {};
+
+template <typename T>
+struct is_class
+    : integral_constant<bool,
+                        sizeof(cef_internal::IsClassHelper::Test<T>(0)) ==
+                            sizeof(cef_internal::YesType)> {};
+
+template <bool B, class T = void>
+struct enable_if {};
+
+template <class T>
+struct enable_if<true, T> {
+  typedef T type;
+};
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_TEMPLATE_UTIL_H_
diff --git a/src/include/base/cef_thread_checker.h b/src/include/base/cef_thread_checker.h
new file mode 100644
index 0000000..e48c8d0
--- /dev/null
+++ b/src/include/base/cef_thread_checker.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_BASE_THREAD_CHECKER_H_
+#define CEF_INCLUDE_BASE_THREAD_CHECKER_H_
+#pragma once
+
+#if defined(BASE_THREADING_THREAD_CHECKER_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/threading/thread_checker.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_logging.h"
+#include "include/base/internal/cef_thread_checker_impl.h"
+
+// Apart from debug builds, we also enable the thread checker in
+// builds with DCHECK_ALWAYS_ON so that trybots and waterfall bots
+// with this define will get the same level of thread checking as
+// debug bots.
+#if DCHECK_IS_ON()
+#define ENABLE_THREAD_CHECKER 1
+#else
+#define ENABLE_THREAD_CHECKER 0
+#endif
+
+namespace base {
+
+namespace cef_internal {
+
+// Do nothing implementation, for use in release mode.
+//
+// Note: You should almost always use the ThreadChecker class to get the
+// right version for your build configuration.
+class ThreadCheckerDoNothing {
+ public:
+  bool CalledOnValidThread() const { return true; }
+
+  void DetachFromThread() {}
+};
+
+}  // namespace cef_internal
+
+// ThreadChecker is a helper class used to help verify that some methods of a
+// class are called from the same thread. It provides identical functionality to
+// base::NonThreadSafe, but it is meant to be held as a member variable, rather
+// than inherited from base::NonThreadSafe.
+//
+// While inheriting from base::NonThreadSafe may give a clear indication about
+// the thread-safety of a class, it may also lead to violations of the style
+// guide with regard to multiple inheritance. The choice between having a
+// ThreadChecker member and inheriting from base::NonThreadSafe should be based
+// on whether:
+//  - Derived classes need to know the thread they belong to, as opposed to
+//    having that functionality fully encapsulated in the base class.
+//  - Derived classes should be able to reassign the base class to another
+//    thread, via DetachFromThread.
+//
+// If neither of these are true, then having a ThreadChecker member and calling
+// CalledOnValidThread is the preferable solution.
+//
+// Example:
+// class MyClass {
+//  public:
+//   void Foo() {
+//     DCHECK(thread_checker_.CalledOnValidThread());
+//     ... (do stuff) ...
+//   }
+//
+//  private:
+//   ThreadChecker thread_checker_;
+// }
+//
+// In Release mode, CalledOnValidThread will always return true.
+#if ENABLE_THREAD_CHECKER
+class ThreadChecker : public cef_internal::ThreadCheckerImpl {};
+#else
+class ThreadChecker : public cef_internal::ThreadCheckerDoNothing {};
+#endif  // ENABLE_THREAD_CHECKER
+
+#undef ENABLE_THREAD_CHECKER
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_THREAD_CHECKER_H_
diff --git a/src/include/base/cef_trace_event.h b/src/include/base/cef_trace_event.h
new file mode 100644
index 0000000..1b2cad0
--- /dev/null
+++ b/src/include/base/cef_trace_event.h
@@ -0,0 +1,420 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+///
+// Trace events are for tracking application performance and resource usage.
+// Macros are provided to track:
+//    Begin and end of function calls
+//    Counters
+//
+// Events are issued against categories. Whereas LOG's categories are statically
+// defined, TRACE categories are created implicitly with a string. For example:
+//   TRACE_EVENT_INSTANT0("MY_SUBSYSTEM", "SomeImportantEvent")
+//
+// Events can be INSTANT, or can be pairs of BEGIN and END in the same scope:
+//   TRACE_EVENT_BEGIN0("MY_SUBSYSTEM", "SomethingCostly")
+//   doSomethingCostly()
+//   TRACE_EVENT_END0("MY_SUBSYSTEM", "SomethingCostly")
+// Note: Our tools can't always determine the correct BEGIN/END pairs unless
+// these are used in the same scope. Use ASYNC_BEGIN/ASYNC_END macros if you
+// need them to be in separate scopes.
+//
+// A common use case is to trace entire function scopes. This issues a trace
+// BEGIN and END automatically:
+//   void doSomethingCostly() {
+//     TRACE_EVENT0("MY_SUBSYSTEM", "doSomethingCostly");
+//     ...
+//   }
+//
+// Additional parameters can be associated with an event:
+//   void doSomethingCostly2(int howMuch) {
+//     TRACE_EVENT1("MY_SUBSYSTEM", "doSomethingCostly",
+//         "howMuch", howMuch);
+//     ...
+//   }
+//
+// The trace system will automatically add to this information the current
+// process id, thread id, and a timestamp in microseconds.
+//
+// To trace an asynchronous procedure such as an IPC send/receive, use
+// ASYNC_BEGIN and ASYNC_END:
+//   [single threaded sender code]
+//     static int send_count = 0;
+//     ++send_count;
+//     TRACE_EVENT_ASYNC_BEGIN0("ipc", "message", send_count);
+//     Send(new MyMessage(send_count));
+//   [receive code]
+//     void OnMyMessage(send_count) {
+//       TRACE_EVENT_ASYNC_END0("ipc", "message", send_count);
+//     }
+// The third parameter is a unique ID to match ASYNC_BEGIN/ASYNC_END pairs.
+// ASYNC_BEGIN and ASYNC_END can occur on any thread of any traced process.
+// Pointers can be used for the ID parameter, and they will be mangled
+// internally so that the same pointer on two different processes will not
+// match. For example:
+//   class MyTracedClass {
+//    public:
+//     MyTracedClass() {
+//       TRACE_EVENT_ASYNC_BEGIN0("category", "MyTracedClass", this);
+//     }
+//     ~MyTracedClass() {
+//       TRACE_EVENT_ASYNC_END0("category", "MyTracedClass", this);
+//     }
+//   }
+//
+// The trace event also supports counters, which is a way to track a quantity
+// as it varies over time. Counters are created with the following macro:
+//   TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter", g_myCounterValue);
+//
+// Counters are process-specific. The macro itself can be issued from any
+// thread, however.
+//
+// Sometimes, you want to track two counters at once. You can do this with two
+// counter macros:
+//   TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter0", g_myCounterValue[0]);
+//   TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter1", g_myCounterValue[1]);
+// Or you can do it with a combined macro:
+//   TRACE_COUNTER2("MY_SUBSYSTEM", "myCounter",
+//       "bytesPinned", g_myCounterValue[0],
+//       "bytesAllocated", g_myCounterValue[1]);
+// This indicates to the tracing UI that these counters should be displayed
+// in a single graph, as a summed area chart.
+//
+// Since counters are in a global namespace, you may want to disembiguate with a
+// unique ID, by using the TRACE_COUNTER_ID* variations.
+//
+// By default, trace collection is compiled in, but turned off at runtime.
+// Collecting trace data is the responsibility of the embedding application. In
+// CEF's case, calling BeginTracing will turn on tracing on all active
+// processes.
+//
+//
+// Memory scoping note:
+// Tracing copies the pointers, not the string content, of the strings passed
+// in for category, name, and arg_names.  Thus, the following code will cause
+// problems:
+//     char* str = strdup("impprtantName");
+//     TRACE_EVENT_INSTANT0("SUBSYSTEM", str);  // BAD!
+//     free(str);                   // Trace system now has dangling pointer
+//
+// To avoid this issue with the |name| and |arg_name| parameters, use the
+// TRACE_EVENT_COPY_XXX overloads of the macros at additional runtime
+// overhead.
+// Notes: The category must always be in a long-lived char* (i.e. static const).
+//        The |arg_values|, when used, are always deep copied with the _COPY
+//        macros.
+//
+//
+// Thread Safety:
+// All macros are thread safe and can be used from any process.
+///
+
+#ifndef CEF_INCLUDE_BASE_CEF_TRACE_EVENT_H_
+#define CEF_INCLUDE_BASE_CEF_TRACE_EVENT_H_
+#pragma once
+
+#if defined(TRACE_EVENT0)
+// Do nothing if the macros provided by this header already exist.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/trace_event/trace_event.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/internal/cef_trace_event_internal.h"
+
+// Records a pair of begin and end events called "name" for the current
+// scope, with 0, 1 or 2 associated arguments. If the category is not
+// enabled, then this does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT0(category, name)                              \
+  cef_trace_event_begin(category, name, NULL, 0, NULL, 0, false); \
+  CEF_INTERNAL_TRACE_END_ON_SCOPE_CLOSE(category, name)
+#define TRACE_EVENT1(category, name, arg1_name, arg1_val)                     \
+  cef_trace_event_begin(category, name, arg1_name, arg1_val, NULL, 0, false); \
+  CEF_INTERNAL_TRACE_END_ON_SCOPE_CLOSE(category, name)
+#define TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \
+  cef_trace_event_begin(category, name, arg1_name, arg1_val, arg2_name,        \
+                        arg2_val, false);                                      \
+  CEF_INTERNAL_TRACE_END_ON_SCOPE_CLOSE(category, name)
+
+// Implementation detail: trace event macros create temporary variable names.
+// These macros give each temporary variable a unique name based on the line
+// number to prevent name collisions.
+#define CEF_INTERNAL_TRACE_EVENT_UID3(a, b) cef_trace_event_unique_##a##b
+#define CEF_INTERNAL_TRACE_EVENT_UID2(a, b) CEF_INTERNAL_TRACE_EVENT_UID3(a, b)
+#define CEF_INTERNAL_TRACE_EVENT_UID(name_prefix) \
+  CEF_INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__)
+
+// Implementation detail: internal macro to end end event when the scope ends.
+#define CEF_INTERNAL_TRACE_END_ON_SCOPE_CLOSE(category, name)            \
+  cef_trace_event::CefTraceEndOnScopeClose CEF_INTERNAL_TRACE_EVENT_UID( \
+      profileScope)(category, name)
+
+// Records a single event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT_INSTANT0(category, name) \
+  cef_trace_event_instant(category, name, NULL, 0, NULL, 0, false)
+#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \
+  cef_trace_event_instant(category, name, arg1_name, arg1_val, NULL, 0, false)
+#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, arg2_name, \
+                             arg2_val)                                       \
+  cef_trace_event_instant(category, name, arg1_name, arg1_val, arg2_name,    \
+                          arg2_val, false)
+#define TRACE_EVENT_COPY_INSTANT0(category, name) \
+  cef_trace_event_instant(category, name, NULL, 0, NULL, 0, true)
+#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \
+  cef_trace_event_instant(category, name, arg1_name, arg1_val, NULL, 0, true)
+#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val,    \
+                                  arg2_name, arg2_val)                    \
+  cef_trace_event_instant(category, name, arg1_name, arg1_val, arg2_name, \
+                          arg2_val, true)
+
+// Records a single BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT_BEGIN0(category, name) \
+  cef_trace_event_begin(category, name, NULL, 0, NULL, 0, false)
+#define TRACE_EVENT_BEGIN1(category, name, arg1_name, arg1_val) \
+  cef_trace_event_begin(category, name, arg1_name, arg1_val, NULL, 0, false)
+#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, arg2_name, \
+                           arg2_val)                                       \
+  cef_trace_event_begin(category, name, arg1_name, arg1_val, arg2_name,    \
+                        arg2_val, false)
+#define TRACE_EVENT_COPY_BEGIN0(category, name) \
+  cef_trace_event_begin(category, name, NULL, 0, NULL, 0, true)
+#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) \
+  cef_trace_event_begin(category, name, arg1_name, arg1_val, NULL, 0, true)
+#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val,    \
+                                arg2_name, arg2_val)                    \
+  cef_trace_event_begin(category, name, arg1_name, arg1_val, arg2_name, \
+                        arg2_val, true)
+
+// Records a single END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT_END0(category, name) \
+  cef_trace_event_end(category, name, NULL, 0, NULL, 0, false)
+#define TRACE_EVENT_END1(category, name, arg1_name, arg1_val) \
+  cef_trace_event_end(category, name, arg1_name, arg1_val, NULL, 0, false)
+#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, arg2_name, \
+                         arg2_val)                                       \
+  cef_trace_event_end(category, name, arg1_name, arg1_val, arg2_name,    \
+                      arg2_val, false)
+#define TRACE_EVENT_COPY_END0(category, name) \
+  cef_trace_event_end(category, name, NULL, 0, NULL, 0, true)
+#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) \
+  cef_trace_event_end(category, name, arg1_name, arg1_val, NULL, 0, true)
+#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, arg2_name, \
+                              arg2_val)                                       \
+  cef_trace_event_end(category, name, arg1_name, arg1_val, arg2_name,         \
+                      arg2_val, true)
+
+// Records the value of a counter called "name" immediately. Value
+// must be representable as a 32 bit integer.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_COUNTER1(category, name, value) \
+  cef_trace_counter(category, name, NULL, value, NULL, 0, false)
+#define TRACE_COPY_COUNTER1(category, name, value) \
+  cef_trace_counter(category, name, NULL, value, NULL, 0, true)
+
+// Records the values of a multi-parted counter called "name" immediately.
+// The UI will treat value1 and value2 as parts of a whole, displaying their
+// values as a stacked-bar chart.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_COUNTER2(category, name, value1_name, value1_val, value2_name, \
+                       value2_val)                                           \
+  cef_trace_counter(category, name, value1_name, value1_val, value2_name,    \
+                    value2_val, false)
+#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val,      \
+                            value2_name, value2_val)                      \
+  cef_trace_counter(category, name, value1_name, value1_val, value2_name, \
+                    value2_val, true)
+
+// Records the value of a counter called "name" immediately. Value
+// must be representable as a 32 bit integer.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+// - |id| is used to disambiguate counters with the same name. It must either
+//   be a pointer or an integer value up to 64 bits. If it's a pointer, the
+//   bits will be xored with a hash of the process ID so that the same pointer
+//   on two different processes will not collide.
+#define TRACE_COUNTER_ID1(category, name, id, value) \
+  cef_trace_counter_id(category, name, id, NULL, value, NULL, 0, false)
+#define TRACE_COPY_COUNTER_ID1(category, name, id, value) \
+  cef_trace_counter_id(category, name, id, NULL, value, NULL, 0, true)
+
+// Records the values of a multi-parted counter called "name" immediately.
+// The UI will treat value1 and value2 as parts of a whole, displaying their
+// values as a stacked-bar chart.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+// - |id| is used to disambiguate counters with the same name. It must either
+//   be a pointer or an integer value up to 64 bits. If it's a pointer, the
+//   bits will be xored with a hash of the process ID so that the same pointer
+//   on two different processes will not collide.
+#define TRACE_COUNTER_ID2(category, name, id, value1_name, value1_val, \
+                          value2_name, value2_val)                     \
+  cef_trace_counter_id(category, name, id, value1_name, value1_val,    \
+                       value2_name, value2_val, false)
+#define TRACE_COPY_COUNTER_ID2(category, name, id, value1_name, value1_val, \
+                               value2_name, value2_val)                     \
+  cef_trace_counter_id(category, name, id, value1_name, value1_val,         \
+                       value2_name, value2_val, true)
+
+// Records a single ASYNC_BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+// - |id| is used to match the ASYNC_BEGIN event with the ASYNC_END event.
+//   ASYNC events are considered to match if their category, name and id values
+//   all match. |id| must either be a pointer or an integer value up to 64
+//   bits. If it's a pointer, the bits will be xored with a hash of the process
+//   ID sothat the same pointer on two different processes will not collide.
+// An asynchronous operation can consist of multiple phases. The first phase is
+// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the
+// ASYNC_STEP_BEGIN macros. When the operation completes, call ASYNC_END.
+// An async operation can span threads and processes, but all events in that
+// operation must use the same |name| and |id|. Each event can have its own
+// args.
+#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) \
+  cef_trace_event_async_begin(category, name, id, NULL, 0, NULL, 0, false)
+#define TRACE_EVENT_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val)    \
+  cef_trace_event_async_begin(category, name, id, arg1_name, arg1_val, NULL, \
+                              0, false)
+#define TRACE_EVENT_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \
+                                 arg2_name, arg2_val)                     \
+  cef_trace_event_async_begin(category, name, id, arg1_name, arg1_val,    \
+                              arg2_name, arg2_val, false)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category, name, id) \
+  cef_trace_event_async_begin(category, name, id, NULL, 0, NULL, 0, true)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \
+  cef_trace_event_async_begin(category, name, id, arg1_name, arg1_val, NULL,   \
+                              0, true)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \
+                                      arg2_name, arg2_val)                     \
+  cef_trace_event_async_begin(category, name, id, arg1_name, arg1_val,         \
+                              arg2_name, arg2_val, true)
+
+// Records a single ASYNC_STEP_INTO event for |step| immediately. If the
+// category is not enabled, then this does nothing. The |name| and |id| must
+// match the ASYNC_BEGIN event above. The |step| param identifies this step
+// within the async event. This should be called at the beginning of the next
+// phase of an asynchronous operation. The ASYNC_BEGIN event must not have any
+// ASYNC_STEP_PAST events.
+#define TRACE_EVENT_ASYNC_STEP_INTO0(category, name, id, step) \
+  cef_trace_event_async_step_into(category, name, id, step, NULL, 0, false)
+#define TRACE_EVENT_ASYNC_STEP_INTO1(category, name, id, step, arg1_name, \
+                                     arg1_val)                            \
+  cef_trace_event_async_step_into(category, name, id, step, arg1_name,    \
+                                  arg1_val, false)
+#define TRACE_EVENT_COPY_ASYNC_STEP_INTO0(category, name, id, step) \
+  cef_trace_event_async_step_into(category, name, id, step, NULL, 0, true)
+#define TRACE_EVENT_COPY_ASYNC_STEP_INTO1(category, name, id, step, arg1_name, \
+                                          arg1_val)                            \
+  cef_trace_event_async_step_into(category, name, id, step, arg1_name,         \
+                                  arg1_val, true)
+
+// Records a single ASYNC_STEP_PAST event for |step| immediately. If the
+// category is not enabled, then this does nothing. The |name| and |id| must
+// match the ASYNC_BEGIN event above. The |step| param identifies this step
+// within the async event. This should be called at the beginning of the next
+// phase of an asynchronous operation. The ASYNC_BEGIN event must not have any
+// ASYNC_STEP_INTO events.
+#define TRACE_EVENT_ASYNC_STEP_PAST0(category, name, id, step) \
+  cef_trace_event_async_step_past(category, name, id, step, NULL, 0, false)
+#define TRACE_EVENT_ASYNC_STEP_PAST1(category, name, id, step, arg1_name, \
+                                     arg1_val)                            \
+  cef_trace_event_async_step_past(category, name, id, step, arg1_name,    \
+                                  arg1_val, false)
+#define TRACE_EVENT_COPY_ASYNC_STEP_PAST0(category, name, id, step) \
+  cef_trace_event_async_step_past(category, name, id, step, NULL, 0, true)
+#define TRACE_EVENT_COPY_ASYNC_STEP_PAST1(category, name, id, step, arg1_name, \
+                                          arg1_val)                            \
+  cef_trace_event_async_step_past(category, name, id, step, arg1_name,         \
+                                  arg1_val, true)
+
+// Records a single ASYNC_END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+#define TRACE_EVENT_ASYNC_END0(category, name, id) \
+  cef_trace_event_async_end(category, name, id, NULL, 0, NULL, 0, false)
+#define TRACE_EVENT_ASYNC_END1(category, name, id, arg1_name, arg1_val)       \
+  cef_trace_event_async_end(category, name, id, arg1_name, arg1_val, NULL, 0, \
+                            false)
+#define TRACE_EVENT_ASYNC_END2(category, name, id, arg1_name, arg1_val, \
+                               arg2_name, arg2_val)                     \
+  cef_trace_event_async_end(category, name, id, arg1_name, arg1_val,    \
+                            arg2_name, arg2_val, false)
+#define TRACE_EVENT_COPY_ASYNC_END0(category, name, id) \
+  cef_trace_event_async_end(category, name, id, NULL, 0, NULL, 0, true)
+#define TRACE_EVENT_COPY_ASYNC_END1(category, name, id, arg1_name, arg1_val)  \
+  cef_trace_event_async_end(category, name, id, arg1_name, arg1_val, NULL, 0, \
+                            true)
+#define TRACE_EVENT_COPY_ASYNC_END2(category, name, id, arg1_name, arg1_val, \
+                                    arg2_name, arg2_val)                     \
+  cef_trace_event_async_end(category, name, id, arg1_name, arg1_val,         \
+                            arg2_name, arg2_val, true)
+
+namespace cef_trace_event {
+
+// Used by TRACE_EVENTx macro. Do not use directly.
+class CefTraceEndOnScopeClose {
+ public:
+  CefTraceEndOnScopeClose(const char* category, const char* name)
+      : category_(category), name_(name) {}
+  ~CefTraceEndOnScopeClose() {
+    cef_trace_event_end(category_, name_, NULL, 0, NULL, 0, false);
+  }
+
+ private:
+  const char* category_;
+  const char* name_;
+};
+
+}  // cef_trace_event
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_TRACE_EVENT_H_
diff --git a/src/include/base/cef_tuple.h b/src/include/base/cef_tuple.h
new file mode 100644
index 0000000..aeb6e9c
--- /dev/null
+++ b/src/include/base/cef_tuple.h
@@ -0,0 +1,1610 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A Tuple is a generic templatized container, similar in concept to std::pair.
+// There are classes Tuple0 to Tuple6, cooresponding to the number of elements
+// it contains.  The convenient MakeTuple() function takes 0 to 6 arguments,
+// and will construct and return the appropriate Tuple object.  The functions
+// DispatchToMethod and DispatchToFunction take a function pointer or instance
+// and method pointer, and unpack a tuple into arguments to the call.
+//
+// Tuple elements are copied by value, and stored in the tuple.  See the unit
+// tests for more details of how/when the values are copied.
+//
+// Example usage:
+//   // These two methods of creating a Tuple are identical.
+//   Tuple2<int, const char*> tuple_a(1, "wee");
+//   Tuple2<int, const char*> tuple_b = MakeTuple(1, "wee");
+//
+//   void SomeFunc(int a, const char* b) { }
+//   DispatchToFunction(&SomeFunc, tuple_a);  // SomeFunc(1, "wee")
+//   DispatchToFunction(
+//       &SomeFunc, MakeTuple(10, "foo"));    // SomeFunc(10, "foo")
+//
+//   struct { void SomeMeth(int a, int b, int c) { } } foo;
+//   DispatchToMethod(&foo, &Foo::SomeMeth, MakeTuple(1, 2, 3));
+//   // foo->SomeMeth(1, 2, 3);
+
+#ifndef CEF_INCLUDE_BASE_CEF_TUPLE_H_
+#define CEF_INCLUDE_BASE_CEF_TUPLE_H_
+#pragma once
+
+#if defined(BASE_TUPLE_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/tuple.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_bind_helpers.h"
+
+namespace base {
+
+// Traits ----------------------------------------------------------------------
+//
+// A simple traits class for tuple arguments.
+//
+// ValueType: the bare, nonref version of a type (same as the type for nonrefs).
+// RefType: the ref version of a type (same as the type for refs).
+// ParamType: what type to pass to functions (refs should not be constified).
+
+template <class P>
+struct TupleTraits {
+  typedef P ValueType;
+  typedef P& RefType;
+  typedef const P& ParamType;
+};
+
+template <class P>
+struct TupleTraits<P&> {
+  typedef P ValueType;
+  typedef P& RefType;
+  typedef P& ParamType;
+};
+
+template <class P>
+struct TupleTypes {};
+
+// Tuple -----------------------------------------------------------------------
+//
+// This set of classes is useful for bundling 0 or more heterogeneous data types
+// into a single variable.  The advantage of this is that it greatly simplifies
+// function objects that need to take an arbitrary number of parameters; see
+// RunnableMethod and IPC::MessageWithTuple.
+//
+// Tuple0 is supplied to act as a 'void' type.  It can be used, for example,
+// when dispatching to a function that accepts no arguments (see the
+// Dispatchers below).
+// Tuple1<A> is rarely useful.  One such use is when A is non-const ref that you
+// want filled by the dispatchee, and the tuple is merely a container for that
+// output (a "tier").  See MakeRefTuple and its usages.
+
+struct Tuple0 {
+  typedef Tuple0 ValueTuple;
+  typedef Tuple0 RefTuple;
+  typedef Tuple0 ParamTuple;
+};
+
+template <class A>
+struct Tuple1 {
+ public:
+  typedef A TypeA;
+
+  Tuple1() {}
+  explicit Tuple1(typename TupleTraits<A>::ParamType a) : a(a) {}
+
+  A a;
+};
+
+template <class A, class B>
+struct Tuple2 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+
+  Tuple2() {}
+  Tuple2(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b)
+      : a(a), b(b) {}
+
+  A a;
+  B b;
+};
+
+template <class A, class B, class C>
+struct Tuple3 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+
+  Tuple3() {}
+  Tuple3(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c)
+      : a(a), b(b), c(c) {}
+
+  A a;
+  B b;
+  C c;
+};
+
+template <class A, class B, class C, class D>
+struct Tuple4 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef D TypeD;
+
+  Tuple4() {}
+  Tuple4(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c,
+         typename TupleTraits<D>::ParamType d)
+      : a(a), b(b), c(c), d(d) {}
+
+  A a;
+  B b;
+  C c;
+  D d;
+};
+
+template <class A, class B, class C, class D, class E>
+struct Tuple5 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef D TypeD;
+  typedef E TypeE;
+
+  Tuple5() {}
+  Tuple5(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c,
+         typename TupleTraits<D>::ParamType d,
+         typename TupleTraits<E>::ParamType e)
+      : a(a), b(b), c(c), d(d), e(e) {}
+
+  A a;
+  B b;
+  C c;
+  D d;
+  E e;
+};
+
+template <class A, class B, class C, class D, class E, class F>
+struct Tuple6 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef D TypeD;
+  typedef E TypeE;
+  typedef F TypeF;
+
+  Tuple6() {}
+  Tuple6(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c,
+         typename TupleTraits<D>::ParamType d,
+         typename TupleTraits<E>::ParamType e,
+         typename TupleTraits<F>::ParamType f)
+      : a(a), b(b), c(c), d(d), e(e), f(f) {}
+
+  A a;
+  B b;
+  C c;
+  D d;
+  E e;
+  F f;
+};
+
+template <class A, class B, class C, class D, class E, class F, class G>
+struct Tuple7 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef D TypeD;
+  typedef E TypeE;
+  typedef F TypeF;
+  typedef G TypeG;
+
+  Tuple7() {}
+  Tuple7(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c,
+         typename TupleTraits<D>::ParamType d,
+         typename TupleTraits<E>::ParamType e,
+         typename TupleTraits<F>::ParamType f,
+         typename TupleTraits<G>::ParamType g)
+      : a(a), b(b), c(c), d(d), e(e), f(f), g(g) {}
+
+  A a;
+  B b;
+  C c;
+  D d;
+  E e;
+  F f;
+  G g;
+};
+
+template <class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G,
+          class H>
+struct Tuple8 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef D TypeD;
+  typedef E TypeE;
+  typedef F TypeF;
+  typedef G TypeG;
+  typedef H TypeH;
+
+  Tuple8() {}
+  Tuple8(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c,
+         typename TupleTraits<D>::ParamType d,
+         typename TupleTraits<E>::ParamType e,
+         typename TupleTraits<F>::ParamType f,
+         typename TupleTraits<G>::ParamType g,
+         typename TupleTraits<H>::ParamType h)
+      : a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h) {}
+
+  A a;
+  B b;
+  C c;
+  D d;
+  E e;
+  F f;
+  G g;
+  H h;
+};
+
+// Tuple types ----------------------------------------------------------------
+//
+// Allows for selection of ValueTuple/RefTuple/ParamTuple without needing the
+// definitions of class types the tuple takes as parameters.
+
+template <>
+struct TupleTypes<Tuple0> {
+  typedef Tuple0 ValueTuple;
+  typedef Tuple0 RefTuple;
+  typedef Tuple0 ParamTuple;
+};
+
+template <class A>
+struct TupleTypes<Tuple1<A>> {
+  typedef Tuple1<typename TupleTraits<A>::ValueType> ValueTuple;
+  typedef Tuple1<typename TupleTraits<A>::RefType> RefTuple;
+  typedef Tuple1<typename TupleTraits<A>::ParamType> ParamTuple;
+};
+
+template <class A, class B>
+struct TupleTypes<Tuple2<A, B>> {
+  typedef Tuple2<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType>
+      ValueTuple;
+  typedef Tuple2<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType>
+      RefTuple;
+  typedef Tuple2<typename TupleTraits<A>::ParamType,
+                 typename TupleTraits<B>::ParamType>
+      ParamTuple;
+};
+
+template <class A, class B, class C>
+struct TupleTypes<Tuple3<A, B, C>> {
+  typedef Tuple3<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType>
+      ValueTuple;
+  typedef Tuple3<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType>
+      RefTuple;
+  typedef Tuple3<typename TupleTraits<A>::ParamType,
+                 typename TupleTraits<B>::ParamType,
+                 typename TupleTraits<C>::ParamType>
+      ParamTuple;
+};
+
+template <class A, class B, class C, class D>
+struct TupleTypes<Tuple4<A, B, C, D>> {
+  typedef Tuple4<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType,
+                 typename TupleTraits<D>::ValueType>
+      ValueTuple;
+  typedef Tuple4<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType,
+                 typename TupleTraits<D>::RefType>
+      RefTuple;
+  typedef Tuple4<typename TupleTraits<A>::ParamType,
+                 typename TupleTraits<B>::ParamType,
+                 typename TupleTraits<C>::ParamType,
+                 typename TupleTraits<D>::ParamType>
+      ParamTuple;
+};
+
+template <class A, class B, class C, class D, class E>
+struct TupleTypes<Tuple5<A, B, C, D, E>> {
+  typedef Tuple5<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType,
+                 typename TupleTraits<D>::ValueType,
+                 typename TupleTraits<E>::ValueType>
+      ValueTuple;
+  typedef Tuple5<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType,
+                 typename TupleTraits<D>::RefType,
+                 typename TupleTraits<E>::RefType>
+      RefTuple;
+  typedef Tuple5<typename TupleTraits<A>::ParamType,
+                 typename TupleTraits<B>::ParamType,
+                 typename TupleTraits<C>::ParamType,
+                 typename TupleTraits<D>::ParamType,
+                 typename TupleTraits<E>::ParamType>
+      ParamTuple;
+};
+
+template <class A, class B, class C, class D, class E, class F>
+struct TupleTypes<Tuple6<A, B, C, D, E, F>> {
+  typedef Tuple6<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType,
+                 typename TupleTraits<D>::ValueType,
+                 typename TupleTraits<E>::ValueType,
+                 typename TupleTraits<F>::ValueType>
+      ValueTuple;
+  typedef Tuple6<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType,
+                 typename TupleTraits<D>::RefType,
+                 typename TupleTraits<E>::RefType,
+                 typename TupleTraits<F>::RefType>
+      RefTuple;
+  typedef Tuple6<typename TupleTraits<A>::ParamType,
+                 typename TupleTraits<B>::ParamType,
+                 typename TupleTraits<C>::ParamType,
+                 typename TupleTraits<D>::ParamType,
+                 typename TupleTraits<E>::ParamType,
+                 typename TupleTraits<F>::ParamType>
+      ParamTuple;
+};
+
+template <class A, class B, class C, class D, class E, class F, class G>
+struct TupleTypes<Tuple7<A, B, C, D, E, F, G>> {
+  typedef Tuple7<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType,
+                 typename TupleTraits<D>::ValueType,
+                 typename TupleTraits<E>::ValueType,
+                 typename TupleTraits<F>::ValueType,
+                 typename TupleTraits<G>::ValueType>
+      ValueTuple;
+  typedef Tuple7<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType,
+                 typename TupleTraits<D>::RefType,
+                 typename TupleTraits<E>::RefType,
+                 typename TupleTraits<F>::RefType,
+                 typename TupleTraits<G>::RefType>
+      RefTuple;
+  typedef Tuple7<typename TupleTraits<A>::ParamType,
+                 typename TupleTraits<B>::ParamType,
+                 typename TupleTraits<C>::ParamType,
+                 typename TupleTraits<D>::ParamType,
+                 typename TupleTraits<E>::ParamType,
+                 typename TupleTraits<F>::ParamType,
+                 typename TupleTraits<G>::ParamType>
+      ParamTuple;
+};
+
+template <class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G,
+          class H>
+struct TupleTypes<Tuple8<A, B, C, D, E, F, G, H>> {
+  typedef Tuple8<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType,
+                 typename TupleTraits<D>::ValueType,
+                 typename TupleTraits<E>::ValueType,
+                 typename TupleTraits<F>::ValueType,
+                 typename TupleTraits<G>::ValueType,
+                 typename TupleTraits<H>::ValueType>
+      ValueTuple;
+  typedef Tuple8<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType,
+                 typename TupleTraits<D>::RefType,
+                 typename TupleTraits<E>::RefType,
+                 typename TupleTraits<F>::RefType,
+                 typename TupleTraits<G>::RefType,
+                 typename TupleTraits<H>::RefType>
+      RefTuple;
+  typedef Tuple8<typename TupleTraits<A>::ParamType,
+                 typename TupleTraits<B>::ParamType,
+                 typename TupleTraits<C>::ParamType,
+                 typename TupleTraits<D>::ParamType,
+                 typename TupleTraits<E>::ParamType,
+                 typename TupleTraits<F>::ParamType,
+                 typename TupleTraits<G>::ParamType,
+                 typename TupleTraits<H>::ParamType>
+      ParamTuple;
+};
+
+// Tuple creators -------------------------------------------------------------
+//
+// Helper functions for constructing tuples while inferring the template
+// argument types.
+
+inline Tuple0 MakeTuple() {
+  return Tuple0();
+}
+
+template <class A>
+inline Tuple1<A> MakeTuple(const A& a) {
+  return Tuple1<A>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A, B> MakeTuple(const A& a, const B& b) {
+  return Tuple2<A, B>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A, B, C> MakeTuple(const A& a, const B& b, const C& c) {
+  return Tuple3<A, B, C>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A, B, C, D> MakeTuple(const A& a,
+                                    const B& b,
+                                    const C& c,
+                                    const D& d) {
+  return Tuple4<A, B, C, D>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A, B, C, D, E> MakeTuple(const A& a,
+                                       const B& b,
+                                       const C& c,
+                                       const D& d,
+                                       const E& e) {
+  return Tuple5<A, B, C, D, E>(a, b, c, d, e);
+}
+
+template <class A, class B, class C, class D, class E, class F>
+inline Tuple6<A, B, C, D, E, F> MakeTuple(const A& a,
+                                          const B& b,
+                                          const C& c,
+                                          const D& d,
+                                          const E& e,
+                                          const F& f) {
+  return Tuple6<A, B, C, D, E, F>(a, b, c, d, e, f);
+}
+
+template <class A, class B, class C, class D, class E, class F, class G>
+inline Tuple7<A, B, C, D, E, F, G> MakeTuple(const A& a,
+                                             const B& b,
+                                             const C& c,
+                                             const D& d,
+                                             const E& e,
+                                             const F& f,
+                                             const G& g) {
+  return Tuple7<A, B, C, D, E, F, G>(a, b, c, d, e, f, g);
+}
+
+template <class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G,
+          class H>
+inline Tuple8<A, B, C, D, E, F, G, H> MakeTuple(const A& a,
+                                                const B& b,
+                                                const C& c,
+                                                const D& d,
+                                                const E& e,
+                                                const F& f,
+                                                const G& g,
+                                                const H& h) {
+  return Tuple8<A, B, C, D, E, F, G, H>(a, b, c, d, e, f, g, h);
+}
+
+// The following set of helpers make what Boost refers to as "Tiers" - a tuple
+// of references.
+
+template <class A>
+inline Tuple1<A&> MakeRefTuple(A& a) {
+  return Tuple1<A&>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A&, B&> MakeRefTuple(A& a, B& b) {
+  return Tuple2<A&, B&>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A&, B&, C&> MakeRefTuple(A& a, B& b, C& c) {
+  return Tuple3<A&, B&, C&>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A&, B&, C&, D&> MakeRefTuple(A& a, B& b, C& c, D& d) {
+  return Tuple4<A&, B&, C&, D&>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A&, B&, C&, D&, E&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e) {
+  return Tuple5<A&, B&, C&, D&, E&>(a, b, c, d, e);
+}
+
+template <class A, class B, class C, class D, class E, class F>
+inline Tuple6<A&, B&, C&, D&, E&, F&> MakeRefTuple(A& a,
+                                                   B& b,
+                                                   C& c,
+                                                   D& d,
+                                                   E& e,
+                                                   F& f) {
+  return Tuple6<A&, B&, C&, D&, E&, F&>(a, b, c, d, e, f);
+}
+
+template <class A, class B, class C, class D, class E, class F, class G>
+inline Tuple7<A&, B&, C&, D&, E&, F&, G&>
+MakeRefTuple(A& a, B& b, C& c, D& d, E& e, F& f, G& g) {
+  return Tuple7<A&, B&, C&, D&, E&, F&, G&>(a, b, c, d, e, f, g);
+}
+
+template <class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G,
+          class H>
+inline Tuple8<A&, B&, C&, D&, E&, F&, G&, H&>
+MakeRefTuple(A& a, B& b, C& c, D& d, E& e, F& f, G& g, H& h) {
+  return Tuple8<A&, B&, C&, D&, E&, F&, G&, H&>(a, b, c, d, e, f, g, h);
+}
+
+// Dispatchers ----------------------------------------------------------------
+//
+// Helper functions that call the given method on an object, with the unpacked
+// tuple arguments.  Notice that they all have the same number of arguments,
+// so you need only write:
+//   DispatchToMethod(object, &Object::method, args);
+// This is very useful for templated dispatchers, since they don't need to know
+// what type |args| is.
+
+// Non-Static Dispatchers with no out params.
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) {
+  (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg));
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a));
+}
+
+template <class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple2<A, B>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b));
+}
+
+template <class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple3<A, B, C>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c));
+}
+
+template <class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple4<A, B, C, D>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d));
+}
+
+template <class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple5<A, B, C, D, E>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+                 base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e));
+}
+
+template <class ObjT,
+          class Method,
+          class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple6<A, B, C, D, E, F>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+                 base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e),
+                 base::cef_internal::UnwrapTraits<F>::Unwrap(arg.f));
+}
+
+template <class ObjT,
+          class Method,
+          class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple7<A, B, C, D, E, F, G>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+                 base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e),
+                 base::cef_internal::UnwrapTraits<F>::Unwrap(arg.f),
+                 base::cef_internal::UnwrapTraits<G>::Unwrap(arg.g));
+}
+
+template <class ObjT,
+          class Method,
+          class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G,
+          class H>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple8<A, B, C, D, E, F, G, H>& arg) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+                 base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e),
+                 base::cef_internal::UnwrapTraits<F>::Unwrap(arg.f),
+                 base::cef_internal::UnwrapTraits<G>::Unwrap(arg.g),
+                 base::cef_internal::UnwrapTraits<H>::Unwrap(arg.h));
+}
+
+// Static Dispatchers with no out params.
+
+template <class Function>
+inline void DispatchToFunction(Function function, const Tuple0& arg) {
+  (*function)();
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const A& arg) {
+  (*function)(arg);
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const Tuple1<A>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a));
+}
+
+template <class Function, class A, class B>
+inline void DispatchToFunction(Function function, const Tuple2<A, B>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+              base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b));
+}
+
+template <class Function, class A, class B, class C>
+inline void DispatchToFunction(Function function, const Tuple3<A, B, C>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+              base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+              base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c));
+}
+
+template <class Function, class A, class B, class C, class D>
+inline void DispatchToFunction(Function function,
+                               const Tuple4<A, B, C, D>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+              base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+              base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+              base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d));
+}
+
+template <class Function, class A, class B, class C, class D, class E>
+inline void DispatchToFunction(Function function,
+                               const Tuple5<A, B, C, D, E>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+              base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+              base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+              base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+              base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e));
+}
+
+template <class Function, class A, class B, class C, class D, class E, class F>
+inline void DispatchToFunction(Function function,
+                               const Tuple6<A, B, C, D, E, F>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+              base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+              base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+              base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+              base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e),
+              base::cef_internal::UnwrapTraits<F>::Unwrap(arg.f));
+}
+
+template <class Function,
+          class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G>
+inline void DispatchToFunction(Function function,
+                               const Tuple7<A, B, C, D, E, F, G>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+              base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+              base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+              base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+              base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e),
+              base::cef_internal::UnwrapTraits<F>::Unwrap(arg.f),
+              base::cef_internal::UnwrapTraits<G>::Unwrap(arg.g));
+}
+
+template <class Function,
+          class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F,
+          class G,
+          class H>
+inline void DispatchToFunction(Function function,
+                               const Tuple8<A, B, C, D, E, F, G, H>& arg) {
+  (*function)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+              base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+              base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+              base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+              base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e),
+              base::cef_internal::UnwrapTraits<F>::Unwrap(arg.f),
+              base::cef_internal::UnwrapTraits<G>::Unwrap(arg.g),
+              base::cef_internal::UnwrapTraits<H>::Unwrap(arg.h));
+}
+
+// Dispatchers with 0 out param (as a Tuple0).
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple0& arg,
+                             Tuple0*) {
+  (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg));
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple1<A>& arg,
+                             Tuple0*) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a));
+}
+
+template <class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple2<A, B>& arg,
+                             Tuple0*) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b));
+}
+
+template <class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple3<A, B, C>& arg,
+                             Tuple0*) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c));
+}
+
+template <class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple4<A, B, C, D>& arg,
+                             Tuple0*) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d));
+}
+
+template <class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple5<A, B, C, D, E>& arg,
+                             Tuple0*) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+                 base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e));
+}
+
+template <class ObjT,
+          class Method,
+          class A,
+          class B,
+          class C,
+          class D,
+          class E,
+          class F>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple6<A, B, C, D, E, F>& arg,
+                             Tuple0*) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<A>::Unwrap(arg.a),
+                 base::cef_internal::UnwrapTraits<B>::Unwrap(arg.b),
+                 base::cef_internal::UnwrapTraits<C>::Unwrap(arg.c),
+                 base::cef_internal::UnwrapTraits<D>::Unwrap(arg.d),
+                 base::cef_internal::UnwrapTraits<E>::Unwrap(arg.e),
+                 base::cef_internal::UnwrapTraits<F>::Unwrap(arg.f));
+}
+
+// Dispatchers with 1 out param.
+
+template <class ObjT, class Method, class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple0& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(&out->a);
+}
+
+template <class ObjT, class Method, class InA, class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const InA& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(in, &out->a);
+}
+
+template <class ObjT, class Method, class InA, class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple1<InA>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a), &out->a);
+}
+
+template <class ObjT, class Method, class InA, class InB, class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b), &out->a);
+}
+
+template <class ObjT, class Method, class InA, class InB, class InC, class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c), &out->a);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d), &out->a);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e), &out->a);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class InF,
+          class OutA>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e),
+                 base::cef_internal::UnwrapTraits<InF>::Unwrap(in.f), &out->a);
+}
+
+// Dispatchers with 2 out params.
+
+template <class ObjT, class Method, class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple0& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(&out->a, &out->b);
+}
+
+template <class ObjT, class Method, class InA, class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const InA& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(in, &out->a, &out->b);
+}
+
+template <class ObjT, class Method, class InA, class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple1<InA>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a), &out->a,
+                 &out->b);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class OutA,
+          class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b), &out->a,
+                 &out->b);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class OutA,
+          class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c), &out->a,
+                 &out->b);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class OutA,
+          class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d), &out->a,
+                 &out->b);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class OutA,
+          class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e), &out->a,
+                 &out->b);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class InF,
+          class OutA,
+          class OutB>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e),
+                 base::cef_internal::UnwrapTraits<InF>::Unwrap(in.f), &out->a,
+                 &out->b);
+}
+
+// Dispatchers with 3 out params.
+
+template <class ObjT, class Method, class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple0& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(&out->a, &out->b, &out->c);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class OutA,
+          class OutB,
+          class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const InA& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(in, &out->a, &out->b, &out->c);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class OutA,
+          class OutB,
+          class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple1<InA>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a), &out->a,
+                 &out->b, &out->c);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class OutA,
+          class OutB,
+          class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b), &out->a,
+                 &out->b, &out->c);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class OutA,
+          class OutB,
+          class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c), &out->a,
+                 &out->b, &out->c);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class OutA,
+          class OutB,
+          class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d), &out->a,
+                 &out->b, &out->c);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class OutA,
+          class OutB,
+          class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e), &out->a,
+                 &out->b, &out->c);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class InF,
+          class OutA,
+          class OutB,
+          class OutC>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e),
+                 base::cef_internal::UnwrapTraits<InF>::Unwrap(in.f), &out->a,
+                 &out->b, &out->c);
+}
+
+// Dispatchers with 4 out params.
+
+template <class ObjT,
+          class Method,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple0& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(&out->a, &out->b, &out->c, &out->d);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const InA& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in), &out->a,
+                 &out->b, &out->c, &out->d);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple1<InA>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a), &out->a,
+                 &out->b, &out->c, &out->d);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b), &out->a,
+                 &out->b, &out->c, &out->d);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c), &out->a,
+                 &out->b, &out->c, &out->d);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d), &out->a,
+                 &out->b, &out->c, &out->d);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e), &out->a,
+                 &out->b, &out->c, &out->d);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class InF,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e),
+                 base::cef_internal::UnwrapTraits<InF>::Unwrap(in.f), &out->a,
+                 &out->b, &out->c, &out->d);
+}
+
+// Dispatchers with 5 out params.
+
+template <class ObjT,
+          class Method,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple0& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const InA& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in), &out->a,
+                 &out->b, &out->c, &out->d, &out->e);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple1<InA>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a), &out->a,
+                 &out->b, &out->c, &out->d, &out->e);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b), &out->a,
+                 &out->b, &out->c, &out->d, &out->e);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c), &out->a,
+                 &out->b, &out->c, &out->d, &out->e);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d), &out->a,
+                 &out->b, &out->c, &out->d, &out->e);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e), &out->a,
+                 &out->b, &out->c, &out->d, &out->e);
+}
+
+template <class ObjT,
+          class Method,
+          class InA,
+          class InB,
+          class InC,
+          class InD,
+          class InE,
+          class InF,
+          class OutA,
+          class OutB,
+          class OutC,
+          class OutD,
+          class OutE>
+inline void DispatchToMethod(ObjT* obj,
+                             Method method,
+                             const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(base::cef_internal::UnwrapTraits<InA>::Unwrap(in.a),
+                 base::cef_internal::UnwrapTraits<InB>::Unwrap(in.b),
+                 base::cef_internal::UnwrapTraits<InC>::Unwrap(in.c),
+                 base::cef_internal::UnwrapTraits<InD>::Unwrap(in.d),
+                 base::cef_internal::UnwrapTraits<InE>::Unwrap(in.e),
+                 base::cef_internal::UnwrapTraits<InF>::Unwrap(in.f), &out->a,
+                 &out->b, &out->c, &out->d, &out->e);
+}
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_TUPLE_H_
diff --git a/src/include/base/cef_weak_ptr.h b/src/include/base/cef_weak_ptr.h
new file mode 100644
index 0000000..1ba34b9
--- /dev/null
+++ b/src/include/base/cef_weak_ptr.h
@@ -0,0 +1,385 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Weak pointers are pointers to an object that do not affect its lifetime,
+// and which may be invalidated (i.e. reset to NULL) by the object, or its
+// owner, at any time, most commonly when the object is about to be deleted.
+
+// Weak pointers are useful when an object needs to be accessed safely by one
+// or more objects other than its owner, and those callers can cope with the
+// object vanishing and e.g. tasks posted to it being silently dropped.
+// Reference-counting such an object would complicate the ownership graph and
+// make it harder to reason about the object's lifetime.
+
+// EXAMPLE:
+//
+//  class Controller {
+//   public:
+//    Controller() : weak_factory_(this) {}
+//    void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
+//    void WorkComplete(const Result& result) { ... }
+//   private:
+//    // Member variables should appear before the WeakPtrFactory, to ensure
+//    // that any WeakPtrs to Controller are invalidated before its members
+//    // variable's destructors are executed, rendering them invalid.
+//    WeakPtrFactory<Controller> weak_factory_;
+//  };
+//
+//  class Worker {
+//   public:
+//    static void StartNew(const WeakPtr<Controller>& controller) {
+//      Worker* worker = new Worker(controller);
+//      // Kick off asynchronous processing...
+//    }
+//   private:
+//    Worker(const WeakPtr<Controller>& controller)
+//        : controller_(controller) {}
+//    void DidCompleteAsynchronousProcessing(const Result& result) {
+//      if (controller_)
+//        controller_->WorkComplete(result);
+//    }
+//    WeakPtr<Controller> controller_;
+//  };
+//
+// With this implementation a caller may use SpawnWorker() to dispatch multiple
+// Workers and subsequently delete the Controller, without waiting for all
+// Workers to have completed.
+
+// ------------------------- IMPORTANT: Thread-safety -------------------------
+
+// Weak pointers may be passed safely between threads, but must always be
+// dereferenced and invalidated on the same thread otherwise checking the
+// pointer would be racey.
+//
+// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory
+// is dereferenced, the factory and its WeakPtrs become bound to the calling
+// thread, and cannot be dereferenced or invalidated on any other thread. Bound
+// WeakPtrs can still be handed off to other threads, e.g. to use to post tasks
+// back to object on the bound thread.
+//
+// If all WeakPtr objects are destroyed or invalidated then the factory is
+// unbound from the SequencedTaskRunner/Thread. The WeakPtrFactory may then be
+// destroyed, or new WeakPtr objects may be used, from a different sequence.
+//
+// Thus, at least one WeakPtr object must exist and have been dereferenced on
+// the correct thread to enforce that other WeakPtr objects will enforce they
+// are used on the desired thread.
+
+#ifndef CEF_INCLUDE_BASE_CEF_WEAK_PTR_H_
+#define CEF_INCLUDE_BASE_CEF_WEAK_PTR_H_
+#pragma once
+
+#if defined(BASE_MEMORY_WEAK_PTR_H_)
+// Do nothing if the Chromium header has already been included.
+// This can happen in cases where Chromium code is used directly by the
+// client application. When using Chromium code directly always include
+// the Chromium header first to avoid type conflicts.
+#elif defined(USING_CHROMIUM_INCLUDES)
+// When building CEF include the Chromium header directly.
+#include "base/memory/weak_ptr.h"
+#else  // !USING_CHROMIUM_INCLUDES
+// The following is substantially similar to the Chromium implementation.
+// If the Chromium implementation diverges the below implementation should be
+// updated to match.
+
+#include "include/base/cef_basictypes.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/base/cef_template_util.h"
+#include "include/base/cef_thread_checker.h"
+
+namespace base {
+
+template <typename T>
+class SupportsWeakPtr;
+template <typename T>
+class WeakPtr;
+
+namespace cef_internal {
+// These classes are part of the WeakPtr implementation.
+// DO NOT USE THESE CLASSES DIRECTLY YOURSELF.
+
+class WeakReference {
+ public:
+  // Although Flag is bound to a specific thread, it may be deleted from another
+  // via base::WeakPtr::~WeakPtr().
+  class Flag : public RefCountedThreadSafe<Flag> {
+   public:
+    Flag();
+
+    void Invalidate();
+    bool IsValid() const;
+
+   private:
+    friend class base::RefCountedThreadSafe<Flag>;
+
+    ~Flag();
+
+    // The current Chromium implementation uses SequenceChecker instead of
+    // ThreadChecker to support SequencedWorkerPools. CEF does not yet expose
+    // the concept of SequencedWorkerPools.
+    ThreadChecker thread_checker_;
+    bool is_valid_;
+  };
+
+  WeakReference();
+  explicit WeakReference(const Flag* flag);
+  ~WeakReference();
+
+  bool is_valid() const;
+
+ private:
+  scoped_refptr<const Flag> flag_;
+};
+
+class WeakReferenceOwner {
+ public:
+  WeakReferenceOwner();
+  ~WeakReferenceOwner();
+
+  WeakReference GetRef() const;
+
+  bool HasRefs() const { return flag_.get() && !flag_->HasOneRef(); }
+
+  void Invalidate();
+
+ private:
+  mutable scoped_refptr<WeakReference::Flag> flag_;
+};
+
+// This class simplifies the implementation of WeakPtr's type conversion
+// constructor by avoiding the need for a public accessor for ref_.  A
+// WeakPtr<T> cannot access the private members of WeakPtr<U>, so this
+// base class gives us a way to access ref_ in a protected fashion.
+class WeakPtrBase {
+ public:
+  WeakPtrBase();
+  ~WeakPtrBase();
+
+ protected:
+  explicit WeakPtrBase(const WeakReference& ref);
+
+  WeakReference ref_;
+};
+
+// This class provides a common implementation of common functions that would
+// otherwise get instantiated separately for each distinct instantiation of
+// SupportsWeakPtr<>.
+class SupportsWeakPtrBase {
+ public:
+  // A safe static downcast of a WeakPtr<Base> to WeakPtr<Derived>. This
+  // conversion will only compile if there is exists a Base which inherits
+  // from SupportsWeakPtr<Base>. See base::AsWeakPtr() below for a helper
+  // function that makes calling this easier.
+  template <typename Derived>
+  static WeakPtr<Derived> StaticAsWeakPtr(Derived* t) {
+    typedef is_convertible<Derived, cef_internal::SupportsWeakPtrBase&>
+        convertible;
+    COMPILE_ASSERT(convertible::value,
+                   AsWeakPtr_argument_inherits_from_SupportsWeakPtr);
+    return AsWeakPtrImpl<Derived>(t, *t);
+  }
+
+ private:
+  // This template function uses type inference to find a Base of Derived
+  // which is an instance of SupportsWeakPtr<Base>. We can then safely
+  // static_cast the Base* to a Derived*.
+  template <typename Derived, typename Base>
+  static WeakPtr<Derived> AsWeakPtrImpl(Derived* t,
+                                        const SupportsWeakPtr<Base>&) {
+    WeakPtr<Base> ptr = t->Base::AsWeakPtr();
+    return WeakPtr<Derived>(ptr.ref_, static_cast<Derived*>(ptr.ptr_));
+  }
+};
+
+}  // namespace cef_internal
+
+template <typename T>
+class WeakPtrFactory;
+
+// The WeakPtr class holds a weak reference to |T*|.
+//
+// This class is designed to be used like a normal pointer.  You should always
+// null-test an object of this class before using it or invoking a method that
+// may result in the underlying object being destroyed.
+//
+// EXAMPLE:
+//
+//   class Foo { ... };
+//   WeakPtr<Foo> foo;
+//   if (foo)
+//     foo->method();
+//
+template <typename T>
+class WeakPtr : public cef_internal::WeakPtrBase {
+ public:
+  WeakPtr() : ptr_(NULL) {}
+
+  // Allow conversion from U to T provided U "is a" T. Note that this
+  // is separate from the (implicit) copy constructor.
+  template <typename U>
+  WeakPtr(const WeakPtr<U>& other) : WeakPtrBase(other), ptr_(other.ptr_) {}
+
+  T* get() const { return ref_.is_valid() ? ptr_ : NULL; }
+
+  T& operator*() const {
+    DCHECK(get() != NULL);
+    return *get();
+  }
+  T* operator->() const {
+    DCHECK(get() != NULL);
+    return get();
+  }
+
+  // Allow WeakPtr<element_type> to be used in boolean expressions, but not
+  // implicitly convertible to a real bool (which is dangerous).
+  //
+  // Note that this trick is only safe when the == and != operators
+  // are declared explicitly, as otherwise "weak_ptr1 == weak_ptr2"
+  // will compile but do the wrong thing (i.e., convert to Testable
+  // and then do the comparison).
+ private:
+  typedef T* WeakPtr::*Testable;
+
+ public:
+  operator Testable() const { return get() ? &WeakPtr::ptr_ : NULL; }
+
+  void reset() {
+    ref_ = cef_internal::WeakReference();
+    ptr_ = NULL;
+  }
+
+ private:
+  // Explicitly declare comparison operators as required by the bool
+  // trick, but keep them private.
+  template <class U>
+  bool operator==(WeakPtr<U> const&) const;
+  template <class U>
+  bool operator!=(WeakPtr<U> const&) const;
+
+  friend class cef_internal::SupportsWeakPtrBase;
+  template <typename U>
+  friend class WeakPtr;
+  friend class SupportsWeakPtr<T>;
+  friend class WeakPtrFactory<T>;
+
+  WeakPtr(const cef_internal::WeakReference& ref, T* ptr)
+      : WeakPtrBase(ref), ptr_(ptr) {}
+
+  // This pointer is only valid when ref_.is_valid() is true.  Otherwise, its
+  // value is undefined (as opposed to NULL).
+  T* ptr_;
+};
+
+// A class may be composed of a WeakPtrFactory and thereby
+// control how it exposes weak pointers to itself.  This is helpful if you only
+// need weak pointers within the implementation of a class.  This class is also
+// useful when working with primitive types.  For example, you could have a
+// WeakPtrFactory<bool> that is used to pass around a weak reference to a bool.
+template <class T>
+class WeakPtrFactory {
+ public:
+  explicit WeakPtrFactory(T* ptr) : ptr_(ptr) {}
+
+  ~WeakPtrFactory() { ptr_ = NULL; }
+
+  WeakPtr<T> GetWeakPtr() {
+    DCHECK(ptr_);
+    return WeakPtr<T>(weak_reference_owner_.GetRef(), ptr_);
+  }
+
+  // Call this method to invalidate all existing weak pointers.
+  void InvalidateWeakPtrs() {
+    DCHECK(ptr_);
+    weak_reference_owner_.Invalidate();
+  }
+
+  // Call this method to determine if any weak pointers exist.
+  bool HasWeakPtrs() const {
+    DCHECK(ptr_);
+    return weak_reference_owner_.HasRefs();
+  }
+
+ private:
+  cef_internal::WeakReferenceOwner weak_reference_owner_;
+  T* ptr_;
+  DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory);
+};
+
+// A class may extend from SupportsWeakPtr to let others take weak pointers to
+// it. This avoids the class itself implementing boilerplate to dispense weak
+// pointers.  However, since SupportsWeakPtr's destructor won't invalidate
+// weak pointers to the class until after the derived class' members have been
+// destroyed, its use can lead to subtle use-after-destroy issues.
+template <class T>
+class SupportsWeakPtr : public cef_internal::SupportsWeakPtrBase {
+ public:
+  SupportsWeakPtr() {}
+
+  WeakPtr<T> AsWeakPtr() {
+    return WeakPtr<T>(weak_reference_owner_.GetRef(), static_cast<T*>(this));
+  }
+
+ protected:
+  ~SupportsWeakPtr() {}
+
+ private:
+  cef_internal::WeakReferenceOwner weak_reference_owner_;
+  DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr);
+};
+
+// Helper function that uses type deduction to safely return a WeakPtr<Derived>
+// when Derived doesn't directly extend SupportsWeakPtr<Derived>, instead it
+// extends a Base that extends SupportsWeakPtr<Base>.
+//
+// EXAMPLE:
+//   class Base : public base::SupportsWeakPtr<Producer> {};
+//   class Derived : public Base {};
+//
+//   Derived derived;
+//   base::WeakPtr<Derived> ptr = base::AsWeakPtr(&derived);
+//
+// Note that the following doesn't work (invalid type conversion) since
+// Derived::AsWeakPtr() is WeakPtr<Base> SupportsWeakPtr<Base>::AsWeakPtr(),
+// and there's no way to safely cast WeakPtr<Base> to WeakPtr<Derived> at
+// the caller.
+//
+//   base::WeakPtr<Derived> ptr = derived.AsWeakPtr();  // Fails.
+
+template <typename Derived>
+WeakPtr<Derived> AsWeakPtr(Derived* t) {
+  return cef_internal::SupportsWeakPtrBase::StaticAsWeakPtr<Derived>(t);
+}
+
+}  // namespace base
+
+#endif  // !USING_CHROMIUM_INCLUDES
+
+#endif  // CEF_INCLUDE_BASE_CEF_WEAK_PTR_H_
diff --git a/src/include/base/internal/cef_atomicops_arm64_gcc.h b/src/include/base/internal/cef_atomicops_arm64_gcc.h
new file mode 100644
index 0000000..787b3ca
--- /dev/null
+++ b/src/include/base/internal/cef_atomicops_arm64_gcc.h
@@ -0,0 +1,335 @@
+// Copyright (c) 2012 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Do not include this header file directly. Use base/cef_atomicops.h
+// instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM64_GCC_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM64_GCC_H_
+
+namespace base {
+namespace subtle {
+
+inline void MemoryBarrier() {
+  __asm__ __volatile__ ("dmb ish" ::: "memory");  // NOLINT
+}
+
+// NoBarrier versions of the operation include "memory" in the clobber list.
+// This is not required for direct usage of the NoBarrier versions of the
+// operations. However this is required for correctness when they are used as
+// part of the Acquire or Release versions, to ensure that nothing from outside
+// the call is reordered between the operation and the memory barrier. This does
+// not change the code generated, so has no or minimal impact on the
+// NoBarrier operations.
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %w[prev], %[ptr]                 \n\t"  // Load the previous value.
+    "cmp %w[prev], %w[old_value]           \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %w[new_value], %[ptr]  \n\t"  // Try to store the new value.
+    "cbnz %w[temp], 0b                     \n\t"  // Retry if it did not work.
+    "1:                                    \n\t"
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"IJr" (old_value),
+      [new_value]"r" (new_value)
+    : "cc", "memory"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  Atomic32 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %w[result], %[ptr]               \n\t"  // Load the previous value.
+    "stxr %w[temp], %w[new_value], %[ptr]  \n\t"  // Try to store the new value.
+    "cbnz %w[temp], 0b                     \n\t"  // Retry if it did not work.
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [new_value]"r" (new_value)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  Atomic32 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                       \n\t"
+    "ldxr %w[result], %[ptr]                  \n\t"  // Load the previous value.
+    "add %w[result], %w[result], %w[increment]\n\t"
+    "stxr %w[temp], %w[result], %[ptr]        \n\t"  // Try to store the result.
+    "cbnz %w[temp], 0b                        \n\t"  // Retry on failure.
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [increment]"IJr" (increment)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  Atomic32 result;
+
+  MemoryBarrier();
+  result = NoBarrier_AtomicIncrement(ptr, increment);
+  MemoryBarrier();
+
+  return result;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 prev;
+
+  prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+  MemoryBarrier();
+
+  return prev;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 prev;
+
+  MemoryBarrier();
+  prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+
+  return prev;
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  __asm__ __volatile__ (  // NOLINT
+    "stlr %w[value], %[ptr]  \n\t"
+    : [ptr]"=Q" (*ptr)
+    : [value]"r" (value)
+    : "memory"
+  );  // NOLINT
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value;
+
+  __asm__ __volatile__ (  // NOLINT
+    "ldar %w[value], %[ptr]  \n\t"
+    : [value]"=r" (value)
+    : [ptr]"Q" (*ptr)
+    : "memory"
+  );  // NOLINT
+
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+// 64-bit versions of the operations.
+// See the 32-bit versions for comments.
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  Atomic64 prev;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %[prev], %[ptr]                  \n\t"
+    "cmp %[prev], %[old_value]             \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %[new_value], %[ptr]   \n\t"
+    "cbnz %w[temp], 0b                     \n\t"
+    "1:                                    \n\t"
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"IJr" (old_value),
+      [new_value]"r" (new_value)
+    : "cc", "memory"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  Atomic64 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %[result], %[ptr]                \n\t"
+    "stxr %w[temp], %[new_value], %[ptr]   \n\t"
+    "cbnz %w[temp], 0b                     \n\t"
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [new_value]"r" (new_value)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  Atomic64 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                     \n\t"
+    "ldxr %[result], %[ptr]                 \n\t"
+    "add %[result], %[result], %[increment] \n\t"
+    "stxr %w[temp], %[result], %[ptr]       \n\t"
+    "cbnz %w[temp], 0b                      \n\t"
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [increment]"IJr" (increment)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  Atomic64 result;
+
+  MemoryBarrier();
+  result = NoBarrier_AtomicIncrement(ptr, increment);
+  MemoryBarrier();
+
+  return result;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  Atomic64 prev;
+
+  prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+  MemoryBarrier();
+
+  return prev;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  Atomic64 prev;
+
+  MemoryBarrier();
+  prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+
+  return prev;
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  __asm__ __volatile__ (  // NOLINT
+    "stlr %x[value], %[ptr]  \n\t"
+    : [ptr]"=Q" (*ptr)
+    : [value]"r" (value)
+    : "memory"
+  );  // NOLINT
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+  return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value;
+
+  __asm__ __volatile__ (  // NOLINT
+    "ldar %x[value], %[ptr]  \n\t"
+    : [value]"=r" (value)
+    : [ptr]"Q" (*ptr)
+    : "memory"
+  );  // NOLINT
+
+  return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+} }  // namespace base::subtle
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM64_GCC_H_
+
diff --git a/src/include/base/internal/cef_atomicops_arm64_msvc.h b/src/include/base/internal/cef_atomicops_arm64_msvc.h
new file mode 100644
index 0000000..86d950f
--- /dev/null
+++ b/src/include/base/internal/cef_atomicops_arm64_msvc.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2008 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_atomicops.h
+// instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM64_MSVC_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM64_MSVC_H_
+
+#include <windows.h>
+
+#include <intrin.h>
+
+#include "include/base/cef_macros.h"
+
+namespace base {
+namespace subtle {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  LONG result = _InterlockedCompareExchange(
+      reinterpret_cast<volatile LONG*>(ptr), static_cast<LONG>(new_value),
+      static_cast<LONG>(old_value));
+  return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  LONG result = _InterlockedExchange(reinterpret_cast<volatile LONG*>(ptr),
+                                     static_cast<LONG>(new_value));
+  return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  return _InterlockedExchangeAdd(reinterpret_cast<volatile LONG*>(ptr),
+                                 static_cast<LONG>(increment)) +
+         increment;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+
+#if !(defined(_MSC_VER) && _MSC_VER >= 1400)
+#error "We require at least vs2005 for MemoryBarrier"
+#endif
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  NoBarrier_AtomicExchange(ptr, value);
+  // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  // See comments in Atomic64 version of Release_Store() below.
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+#if defined(_WIN64)
+
+// 64-bit low-level operations on 64-bit platform.
+
+COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  PVOID result = InterlockedCompareExchangePointer(
+      reinterpret_cast<volatile PVOID*>(ptr),
+      reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
+  return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  PVOID result =
+      InterlockedExchangePointer(reinterpret_cast<volatile PVOID*>(ptr),
+                                 reinterpret_cast<PVOID>(new_value));
+  return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  return InterlockedExchangeAdd64(reinterpret_cast<volatile LONGLONG*>(ptr),
+                                  static_cast<LONGLONG>(increment)) +
+         increment;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  NoBarrier_AtomicExchange(ptr, value);
+  // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+  return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value = *ptr;
+  return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+#endif  // defined(_WIN64)
+
+}  // namespace base::subtle
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM64_MSVC_H_
+
diff --git a/src/include/base/internal/cef_atomicops_arm_gcc.h b/src/include/base/internal/cef_atomicops_arm_gcc.h
new file mode 100644
index 0000000..2e39ce3
--- /dev/null
+++ b/src/include/base/internal/cef_atomicops_arm_gcc.h
@@ -0,0 +1,325 @@
+// Copyright (c) 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Do not include this header file directly. Use base/cef_atomicops.h
+// instead.
+//
+// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM_GCC_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM_GCC_H_
+
+#if defined(OS_QNX)
+#include <sys/cpuinline.h>
+#endif
+
+namespace base {
+namespace subtle {
+
+// Memory barriers on ARM are funky, but the kernel is here to help:
+//
+// * ARMv5 didn't support SMP, there is no memory barrier instruction at
+//   all on this architecture, or when targeting its machine code.
+//
+// * Some ARMv6 CPUs support SMP. A full memory barrier can be produced by
+//   writing a random value to a very specific coprocessor register.
+//
+// * On ARMv7, the "dmb" instruction is used to perform a full memory
+//   barrier (though writing to the co-processor will still work).
+//   However, on single core devices (e.g. Nexus One, or Nexus S),
+//   this instruction will take up to 200 ns, which is huge, even though
+//   it's completely un-needed on these devices.
+//
+// * There is no easy way to determine at runtime if the device is
+//   single or multi-core. However, the kernel provides a useful helper
+//   function at a fixed memory address (0xffff0fa0), which will always
+//   perform a memory barrier in the most efficient way. I.e. on single
+//   core devices, this is an empty function that exits immediately.
+//   On multi-core devices, it implements a full memory barrier.
+//
+// * This source could be compiled to ARMv5 machine code that runs on a
+//   multi-core ARMv6 or ARMv7 device. In this case, memory barriers
+//   are needed for correct execution. Always call the kernel helper, even
+//   when targeting ARMv5TE.
+//
+
+inline void MemoryBarrier() {
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  // Note: This is a function call, which is also an implicit compiler barrier.
+  typedef void (*KernelMemoryBarrierFunc)();
+  ((KernelMemoryBarrierFunc)0xffff0fa0)();
+#elif defined(OS_QNX)
+  __cpu_membarrier();
+#else
+#error MemoryBarrier() is not implemented on this platform.
+#endif
+}
+
+// An ARM toolchain would only define one of these depending on which
+// variant of the target architecture is being used. This tests against
+// any known ARMv6 or ARMv7 variant, where it is possible to directly
+// use ldrex/strex instructions to implement fast atomic operations.
+#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) ||  \
+    defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || \
+    defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) ||  \
+    defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \
+    defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__)
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev_value;
+  int reloop;
+  do {
+    // The following is equivalent to:
+    //
+    //   prev_value = LDREX(ptr)
+    //   reloop = 0
+    //   if (prev_value != old_value)
+    //      reloop = STREX(ptr, new_value)
+    __asm__ __volatile__(
+        "    ldrex %0, [%3]\n"
+        "    mov %1, #0\n"
+        "    cmp %0, %4\n"
+#ifdef __thumb2__
+        "    it eq\n"
+#endif
+        "    strexeq %1, %5, [%3]\n"
+        : "=&r"(prev_value), "=&r"(reloop), "+m"(*ptr)
+        : "r"(ptr), "r"(old_value), "r"(new_value)
+        : "cc", "memory");
+  } while (reloop != 0);
+  return prev_value;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 result = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+  MemoryBarrier();
+  return result;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  MemoryBarrier();
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  Atomic32 value;
+  int reloop;
+  do {
+    // Equivalent to:
+    //
+    //  value = LDREX(ptr)
+    //  value += increment
+    //  reloop = STREX(ptr, value)
+    //
+    __asm__ __volatile__(
+        "    ldrex %0, [%3]\n"
+        "    add %0, %0, %4\n"
+        "    strex %1, %0, [%3]\n"
+        : "=&r"(value), "=&r"(reloop), "+m"(*ptr)
+        : "r"(ptr), "r"(increment)
+        : "cc", "memory");
+  } while (reloop);
+  return value;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  // TODO(digit): Investigate if it's possible to implement this with
+  // a single MemoryBarrier() operation between the LDREX and STREX.
+  // See http://crbug.com/246514
+  MemoryBarrier();
+  Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment);
+  MemoryBarrier();
+  return result;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  Atomic32 old_value;
+  int reloop;
+  do {
+    // old_value = LDREX(ptr)
+    // reloop = STREX(ptr, new_value)
+    __asm__ __volatile__(
+        "   ldrex %0, [%3]\n"
+        "   strex %1, %4, [%3]\n"
+        : "=&r"(old_value), "=&r"(reloop), "+m"(*ptr)
+        : "r"(ptr), "r"(new_value)
+        : "cc", "memory");
+  } while (reloop != 0);
+  return old_value;
+}
+
+// This tests against any known ARMv5 variant.
+#elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \
+    defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__)
+
+// The kernel also provides a helper function to perform an atomic
+// compare-and-swap operation at the hard-wired address 0xffff0fc0.
+// On ARMv5, this is implemented by a special code path that the kernel
+// detects and treats specially when thread pre-emption happens.
+// On ARMv6 and higher, it uses LDREX/STREX instructions instead.
+//
+// Note that this always perform a full memory barrier, there is no
+// need to add calls MemoryBarrier() before or after it. It also
+// returns 0 on success, and 1 on exit.
+//
+// Available and reliable since Linux 2.6.24. Both Android and ChromeOS
+// use newer kernel revisions, so this should not be a concern.
+namespace {
+
+inline int LinuxKernelCmpxchg(Atomic32 old_value,
+                              Atomic32 new_value,
+                              volatile Atomic32* ptr) {
+  typedef int (*KernelCmpxchgFunc)(Atomic32, Atomic32, volatile Atomic32*);
+  return ((KernelCmpxchgFunc)0xffff0fc0)(old_value, new_value, ptr);
+}
+
+}  // namespace
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev_value;
+  for (;;) {
+    prev_value = *ptr;
+    if (prev_value != old_value)
+      return prev_value;
+    if (!LinuxKernelCmpxchg(old_value, new_value, ptr))
+      return old_value;
+  }
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  Atomic32 old_value;
+  do {
+    old_value = *ptr;
+  } while (LinuxKernelCmpxchg(old_value, new_value, ptr));
+  return old_value;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  for (;;) {
+    // Atomic exchange the old value with an incremented one.
+    Atomic32 old_value = *ptr;
+    Atomic32 new_value = old_value + increment;
+    if (!LinuxKernelCmpxchg(old_value, new_value, ptr)) {
+      // The exchange took place as expected.
+      return new_value;
+    }
+    // Otherwise, *ptr changed mid-loop and we need to retry.
+  }
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 prev_value;
+  for (;;) {
+    prev_value = *ptr;
+    if (prev_value != old_value) {
+      // Always ensure acquire semantics.
+      MemoryBarrier();
+      return prev_value;
+    }
+    if (!LinuxKernelCmpxchg(old_value, new_value, ptr))
+      return old_value;
+  }
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  // This could be implemented as:
+  //    MemoryBarrier();
+  //    return NoBarrier_CompareAndSwap();
+  //
+  // But would use 3 barriers per succesful CAS. To save performance,
+  // use Acquire_CompareAndSwap(). Its implementation guarantees that:
+  // - A succesful swap uses only 2 barriers (in the kernel helper).
+  // - An early return due to (prev_value != old_value) performs
+  //   a memory barrier with no store, which is equivalent to the
+  //   generic implementation above.
+  return Acquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+#else
+#error "Your CPU's ARM architecture is not supported yet"
+#endif
+
+// NOTE: Atomicity of the following load and store operations is only
+// guaranteed in case of 32-bit alignement of |ptr| values.
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+}  // namespace base::subtle
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ARM_GCC_H_
diff --git a/src/include/base/internal/cef_atomicops_atomicword_compat.h b/src/include/base/internal/cef_atomicops_atomicword_compat.h
new file mode 100644
index 0000000..f905de8
--- /dev/null
+++ b/src/include/base/internal/cef_atomicops_atomicword_compat.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_atomicops.h
+// instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ATOMICWORD_COMPAT_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ATOMICWORD_COMPAT_H_
+
+// AtomicWord is a synonym for intptr_t, and Atomic32 is a synonym for int32,
+// which in turn means int. On some LP32 platforms, intptr_t is an int, but
+// on others, it's a long. When AtomicWord and Atomic32 are based on different
+// fundamental types, their pointers are incompatible.
+//
+// This file defines function overloads to allow both AtomicWord and Atomic32
+// data to be used with this interface.
+//
+// On LP64 platforms, AtomicWord and Atomic64 are both always long,
+// so this problem doesn't occur.
+
+#if !defined(ARCH_CPU_64_BITS)
+
+namespace base {
+namespace subtle {
+
+inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr,
+                                           AtomicWord old_value,
+                                           AtomicWord new_value) {
+  return NoBarrier_CompareAndSwap(reinterpret_cast<volatile Atomic32*>(ptr),
+                                  old_value, new_value);
+}
+
+inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr,
+                                           AtomicWord new_value) {
+  return NoBarrier_AtomicExchange(reinterpret_cast<volatile Atomic32*>(ptr),
+                                  new_value);
+}
+
+inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr,
+                                            AtomicWord increment) {
+  return NoBarrier_AtomicIncrement(reinterpret_cast<volatile Atomic32*>(ptr),
+                                   increment);
+}
+
+inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr,
+                                          AtomicWord increment) {
+  return Barrier_AtomicIncrement(reinterpret_cast<volatile Atomic32*>(ptr),
+                                 increment);
+}
+
+inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
+                                         AtomicWord old_value,
+                                         AtomicWord new_value) {
+  return base::subtle::Acquire_CompareAndSwap(
+      reinterpret_cast<volatile Atomic32*>(ptr), old_value, new_value);
+}
+
+inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr,
+                                         AtomicWord old_value,
+                                         AtomicWord new_value) {
+  return base::subtle::Release_CompareAndSwap(
+      reinterpret_cast<volatile Atomic32*>(ptr), old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile AtomicWord* ptr, AtomicWord value) {
+  NoBarrier_Store(reinterpret_cast<volatile Atomic32*>(ptr), value);
+}
+
+inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) {
+  return base::subtle::Acquire_Store(reinterpret_cast<volatile Atomic32*>(ptr),
+                                     value);
+}
+
+inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
+  return base::subtle::Release_Store(reinterpret_cast<volatile Atomic32*>(ptr),
+                                     value);
+}
+
+inline AtomicWord NoBarrier_Load(volatile const AtomicWord* ptr) {
+  return NoBarrier_Load(reinterpret_cast<volatile const Atomic32*>(ptr));
+}
+
+inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
+  return base::subtle::Acquire_Load(
+      reinterpret_cast<volatile const Atomic32*>(ptr));
+}
+
+inline AtomicWord Release_Load(volatile const AtomicWord* ptr) {
+  return base::subtle::Release_Load(
+      reinterpret_cast<volatile const Atomic32*>(ptr));
+}
+
+}  // namespace base::subtle
+}  // namespace base
+
+#endif  // !defined(ARCH_CPU_64_BITS)
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_ATOMICWORD_COMPAT_H_
diff --git a/src/include/base/internal/cef_atomicops_mac.h b/src/include/base/internal/cef_atomicops_mac.h
new file mode 100644
index 0000000..374ae35
--- /dev/null
+++ b/src/include/base/internal/cef_atomicops_mac.h
@@ -0,0 +1,223 @@
+// Copyright (c) 2012 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_atomicops.h
+// instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_MAC_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_MAC_H_
+
+#include <libkern/OSAtomic.h>
+
+namespace base {
+namespace subtle {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev_value;
+  do {
+    if (OSAtomicCompareAndSwap32(old_value, new_value,
+                                 const_cast<Atomic32*>(ptr))) {
+      return old_value;
+    }
+    prev_value = *ptr;
+  } while (prev_value == old_value);
+  return prev_value;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  Atomic32 old_value;
+  do {
+    old_value = *ptr;
+  } while (!OSAtomicCompareAndSwap32(old_value, new_value,
+                                     const_cast<Atomic32*>(ptr)));
+  return old_value;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr));
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr));
+}
+
+inline void MemoryBarrier() {
+  OSMemoryBarrier();
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 prev_value;
+  do {
+    if (OSAtomicCompareAndSwap32Barrier(old_value, new_value,
+                                        const_cast<Atomic32*>(ptr))) {
+      return old_value;
+    }
+    prev_value = *ptr;
+  } while (prev_value == old_value);
+  return prev_value;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  return Acquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+#ifdef __LP64__
+
+// 64-bit implementation on 64-bit platform
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  Atomic64 prev_value;
+  do {
+    if (OSAtomicCompareAndSwap64(old_value, new_value,
+                                 reinterpret_cast<volatile int64_t*>(ptr))) {
+      return old_value;
+    }
+    prev_value = *ptr;
+  } while (prev_value == old_value);
+  return prev_value;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  Atomic64 old_value;
+  do {
+    old_value = *ptr;
+  } while (!OSAtomicCompareAndSwap64(old_value, new_value,
+                                     reinterpret_cast<volatile int64_t*>(ptr)));
+  return old_value;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  return OSAtomicAdd64(increment, reinterpret_cast<volatile int64_t*>(ptr));
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  return OSAtomicAdd64Barrier(increment,
+                              reinterpret_cast<volatile int64_t*>(ptr));
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  Atomic64 prev_value;
+  do {
+    if (OSAtomicCompareAndSwap64Barrier(
+            old_value, new_value, reinterpret_cast<volatile int64_t*>(ptr))) {
+      return old_value;
+    }
+    prev_value = *ptr;
+  } while (prev_value == old_value);
+  return prev_value;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  // The lib kern interface does not distinguish between
+  // Acquire and Release memory barriers; they are equivalent.
+  return Acquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+  return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+#endif  // defined(__LP64__)
+
+}  // namespace base::subtle
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_MAC_H_
diff --git a/src/include/base/internal/cef_atomicops_x86_gcc.h b/src/include/base/internal/cef_atomicops_x86_gcc.h
new file mode 100644
index 0000000..b93df21
--- /dev/null
+++ b/src/include/base/internal/cef_atomicops_x86_gcc.h
@@ -0,0 +1,268 @@
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_atomicops.h
+// instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_GCC_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_GCC_H_
+
+// This struct is not part of the public API of this module; clients may not
+// use it.
+// Features of this x86.  Values may not be correct before main() is run,
+// but are set conservatively.
+struct AtomicOps_x86CPUFeatureStruct {
+  bool has_amd_lock_mb_bug;  // Processor has AMD memory-barrier bug; do lfence
+                             // after acquire compare-and-swap.
+};
+extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
+
+#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
+
+namespace base {
+namespace subtle {
+
+// 32-bit low-level operations on any platform.
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev;
+  __asm__ __volatile__("lock; cmpxchgl %1,%2"
+                       : "=a"(prev)
+                       : "q"(new_value), "m"(*ptr), "0"(old_value)
+                       : "memory");
+  return prev;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  __asm__ __volatile__("xchgl %1,%0"  // The lock prefix is implicit for xchg.
+                       : "=r"(new_value)
+                       : "m"(*ptr), "0"(new_value)
+                       : "memory");
+  return new_value;  // Now it's the previous value.
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  Atomic32 temp = increment;
+  __asm__ __volatile__("lock; xaddl %0,%1"
+                       : "+r"(temp), "+m"(*ptr)
+                       :
+                       : "memory");
+  // temp now holds the old value of *ptr
+  return temp + increment;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  Atomic32 temp = increment;
+  __asm__ __volatile__("lock; xaddl %0,%1"
+                       : "+r"(temp), "+m"(*ptr)
+                       :
+                       : "memory");
+  // temp now holds the old value of *ptr
+  if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+    __asm__ __volatile__("lfence" : : : "memory");
+  }
+  return temp + increment;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+  if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+    __asm__ __volatile__("lfence" : : : "memory");
+  }
+  return x;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void MemoryBarrier() {
+  __asm__ __volatile__("mfence" : : : "memory");
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  ATOMICOPS_COMPILER_BARRIER();
+  *ptr = value;  // An x86 store acts as a release barrier.
+  // See comments in Atomic64 version of Release_Store(), below.
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;  // An x86 load acts as a acquire barrier.
+  // See comments in Atomic64 version of Release_Store(), below.
+  ATOMICOPS_COMPILER_BARRIER();
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+#if defined(__x86_64__)
+
+// 64-bit low-level operations on 64-bit platform.
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  Atomic64 prev;
+  __asm__ __volatile__("lock; cmpxchgq %1,%2"
+                       : "=a"(prev)
+                       : "q"(new_value), "m"(*ptr), "0"(old_value)
+                       : "memory");
+  return prev;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  __asm__ __volatile__("xchgq %1,%0"  // The lock prefix is implicit for xchg.
+                       : "=r"(new_value)
+                       : "m"(*ptr), "0"(new_value)
+                       : "memory");
+  return new_value;  // Now it's the previous value.
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  Atomic64 temp = increment;
+  __asm__ __volatile__("lock; xaddq %0,%1"
+                       : "+r"(temp), "+m"(*ptr)
+                       :
+                       : "memory");
+  // temp now contains the previous value of *ptr
+  return temp + increment;
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  Atomic64 temp = increment;
+  __asm__ __volatile__("lock; xaddq %0,%1"
+                       : "+r"(temp), "+m"(*ptr)
+                       :
+                       : "memory");
+  // temp now contains the previous value of *ptr
+  if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+    __asm__ __volatile__("lfence" : : : "memory");
+  }
+  return temp + increment;
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  ATOMICOPS_COMPILER_BARRIER();
+
+  *ptr = value;  // An x86 store acts as a release barrier
+                 // for current AMD/Intel chips as of Jan 2008.
+                 // See also Acquire_Load(), below.
+
+  // When new chips come out, check:
+  //  IA-32 Intel Architecture Software Developer's Manual, Volume 3:
+  //  System Programming Guide, Chatper 7: Multiple-processor management,
+  //  Section 7.2, Memory Ordering.
+  // Last seen at:
+  //   http://developer.intel.com/design/pentium4/manuals/index_new.htm
+  //
+  // x86 stores/loads fail to act as barriers for a few instructions (clflush
+  // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are
+  // not generated by the compiler, and are rare.  Users of these instructions
+  // need to know about cache behaviour in any case since all of these involve
+  // either flushing cache lines or non-temporal cache hints.
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+  return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value = *ptr;  // An x86 load acts as a acquire barrier,
+                          // for current AMD/Intel chips as of Jan 2008.
+                          // See also Release_Store(), above.
+  ATOMICOPS_COMPILER_BARRIER();
+  return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+  if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+    __asm__ __volatile__("lfence" : : : "memory");
+  }
+  return x;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+#endif  // defined(__x86_64__)
+
+}  // namespace base::subtle
+}  // namespace base
+
+#undef ATOMICOPS_COMPILER_BARRIER
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_GCC_H_
diff --git a/src/include/base/internal/cef_atomicops_x86_msvc.h b/src/include/base/internal/cef_atomicops_x86_msvc.h
new file mode 100644
index 0000000..a262c81
--- /dev/null
+++ b/src/include/base/internal/cef_atomicops_x86_msvc.h
@@ -0,0 +1,221 @@
+// Copyright (c) 2008 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_atomicops.h
+// instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_MSVC_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_MSVC_H_
+
+#include <windows.h>
+
+#include <intrin.h>
+
+#include "include/base/cef_macros.h"
+
+#if defined(ARCH_CPU_64_BITS)
+// windows.h #defines this (only on x64). This causes problems because the
+// public API also uses MemoryBarrier at the public name for this fence. So, on
+// X64, undef it, and call its documented
+// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
+// implementation directly.
+#undef MemoryBarrier
+#endif
+
+namespace base {
+namespace subtle {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  LONG result = _InterlockedCompareExchange(
+      reinterpret_cast<volatile LONG*>(ptr), static_cast<LONG>(new_value),
+      static_cast<LONG>(old_value));
+  return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  LONG result = _InterlockedExchange(reinterpret_cast<volatile LONG*>(ptr),
+                                     static_cast<LONG>(new_value));
+  return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  return _InterlockedExchangeAdd(reinterpret_cast<volatile LONG*>(ptr),
+                                 static_cast<LONG>(increment)) +
+         increment;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+
+#if !(defined(_MSC_VER) && _MSC_VER >= 1400)
+#error "We require at least vs2005 for MemoryBarrier"
+#endif
+inline void MemoryBarrier() {
+#if defined(ARCH_CPU_64_BITS)
+  // See #undef and note at the top of this file.
+  __faststorefence();
+#else
+  // We use MemoryBarrier from WinNT.h
+  ::MemoryBarrier();
+#endif
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  NoBarrier_AtomicExchange(ptr, value);
+  // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;  // works w/o barrier for current Intel chips as of June 2005
+  // See comments in Atomic64 version of Release_Store() below.
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+#if defined(_WIN64)
+
+// 64-bit low-level operations on 64-bit platform.
+
+COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  PVOID result = InterlockedCompareExchangePointer(
+      reinterpret_cast<volatile PVOID*>(ptr),
+      reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
+  return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  PVOID result =
+      InterlockedExchangePointer(reinterpret_cast<volatile PVOID*>(ptr),
+                                 reinterpret_cast<PVOID>(new_value));
+  return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  return InterlockedExchangeAdd64(reinterpret_cast<volatile LONGLONG*>(ptr),
+                                  static_cast<LONGLONG>(increment)) +
+         increment;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  NoBarrier_AtomicExchange(ptr, value);
+  // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;  // works w/o barrier for current Intel chips as of June 2005
+
+  // When new chips come out, check:
+  //  IA-32 Intel Architecture Software Developer's Manual, Volume 3:
+  //  System Programming Guide, Chatper 7: Multiple-processor management,
+  //  Section 7.2, Memory Ordering.
+  // Last seen at:
+  //   http://developer.intel.com/design/pentium4/manuals/index_new.htm
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+  return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value = *ptr;
+  return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+#endif  // defined(_WIN64)
+
+}  // namespace base::subtle
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_MSVC_H_
diff --git a/src/include/base/internal/cef_bind_internal.h b/src/include/base/internal/cef_bind_internal.h
new file mode 100644
index 0000000..64eeb3f
--- /dev/null
+++ b/src/include/base/internal/cef_bind_internal.h
@@ -0,0 +1,3190 @@
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_bind.h instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_BIND_INTERNAL_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_BIND_INTERNAL_H_
+
+#include "include/base/cef_bind_helpers.h"
+#include "include/base/cef_build.h"
+#include "include/base/cef_template_util.h"
+#include "include/base/cef_weak_ptr.h"
+#include "include/base/internal/cef_callback_internal.h"
+#include "include/base/internal/cef_raw_scoped_refptr_mismatch_checker.h"
+
+#if defined(OS_WIN)
+#include "include/base/internal/cef_bind_internal_win.h"
+#endif
+
+namespace base {
+namespace cef_internal {
+
+// See base/callback.h for user documentation.
+//
+//
+// CONCEPTS:
+//  Runnable -- A type (really a type class) that has a single Run() method
+//              and a RunType typedef that corresponds to the type of Run().
+//              A Runnable can declare that it should treated like a method
+//              call by including a typedef named IsMethod.  The value of
+//              this typedef is NOT inspected, only the existence.  When a
+//              Runnable declares itself a method, Bind() will enforce special
+//              refcounting + WeakPtr handling semantics for the first
+//              parameter which is expected to be an object.
+//  Functor -- A copyable type representing something that should be called.
+//             All function pointers, Callback<>, and Runnables are functors
+//             even if the invocation syntax differs.
+//  RunType -- A function type (as opposed to function _pointer_ type) for
+//             a Run() function.  Usually just a convenience typedef.
+//  (Bound)ArgsType -- A function type that is being (ab)used to store the
+//                     types of set of arguments.  The "return" type is always
+//                     void here.  We use this hack so that we do not need
+//                     a new type name for each arity of type. (eg.,
+//                     BindState1, BindState2).  This makes forward
+//                     declarations and friending much much easier.
+//
+// Types:
+//  RunnableAdapter<> -- Wraps the various "function" pointer types into an
+//                       object that adheres to the Runnable interface.
+//                       There are |3*ARITY| RunnableAdapter types.
+//  FunctionTraits<> -- Type traits that unwrap a function signature into a
+//                      a set of easier to use typedefs.  Used mainly for
+//                      compile time asserts.
+//                      There are |ARITY| FunctionTraits types.
+//  ForceVoidReturn<> -- Helper class for translating function signatures to
+//                       equivalent forms with a "void" return type.
+//                    There are |ARITY| ForceVoidReturn types.
+//  FunctorTraits<> -- Type traits used determine the correct RunType and
+//                     RunnableType for a Functor.  This is where function
+//                     signature adapters are applied.
+//                    There are |ARITY| ForceVoidReturn types.
+//  MakeRunnable<> -- Takes a Functor and returns an object in the Runnable
+//                    type class that represents the underlying Functor.
+//                    There are |O(1)| MakeRunnable types.
+//  InvokeHelper<> -- Take a Runnable + arguments and actully invokes it.
+// Handle the differing syntaxes needed for WeakPtr<> support,
+//                    and for ignoring return values.  This is separate from
+//                    Invoker to avoid creating multiple version of Invoker<>
+//                    which grows at O(n^2) with the arity.
+//                    There are |k*ARITY| InvokeHelper types.
+//  Invoker<> -- Unwraps the curried parameters and executes the Runnable.
+//               There are |(ARITY^2 + ARITY)/2| Invoketypes.
+//  BindState<> -- Stores the curried parameters, and is the main entry point
+//                 into the Bind() system, doing most of the type resolution.
+//                 There are ARITY BindState types.
+
+// RunnableAdapter<>
+//
+// The RunnableAdapter<> templates provide a uniform interface for invoking
+// a function pointer, method pointer, or const method pointer. The adapter
+// exposes a Run() method with an appropriate signature. Using this wrapper
+// allows for writing code that supports all three pointer types without
+// undue repetition.  Without it, a lot of code would need to be repeated 3
+// times.
+//
+// For method pointers and const method pointers the first argument to Run()
+// is considered to be the received of the method.  This is similar to STL's
+// mem_fun().
+//
+// This class also exposes a RunType typedef that is the function type of the
+// Run() function.
+//
+// If and only if the wrapper contains a method or const method pointer, an
+// IsMethod typedef is exposed.  The existence of this typedef (NOT the value)
+// marks that the wrapper should be considered a method wrapper.
+
+template <typename Functor>
+class RunnableAdapter;
+
+// Function: Arity 0.
+template <typename R>
+class RunnableAdapter<R (*)()> {
+ public:
+  typedef R(RunType)();
+
+  explicit RunnableAdapter(R (*function)()) : function_(function) {}
+
+  R Run() { return function_(); }
+
+ private:
+  R (*function_)();
+};
+
+// Method: Arity 0.
+template <typename R, typename T>
+class RunnableAdapter<R (T::*)()> {
+ public:
+  typedef R(RunType)(T*);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)()) : method_(method) {}
+
+  R Run(T* object) { return (object->*method_)(); }
+
+ private:
+  R (T::*method_)();
+};
+
+// Const Method: Arity 0.
+template <typename R, typename T>
+class RunnableAdapter<R (T::*)() const> {
+ public:
+  typedef R(RunType)(const T*);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)() const) : method_(method) {}
+
+  R Run(const T* object) { return (object->*method_)(); }
+
+ private:
+  R (T::*method_)() const;
+};
+
+// Function: Arity 1.
+template <typename R, typename A1>
+class RunnableAdapter<R (*)(A1)> {
+ public:
+  typedef R(RunType)(A1);
+
+  explicit RunnableAdapter(R (*function)(A1)) : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1) {
+    return function_(CallbackForward(a1));
+  }
+
+ private:
+  R (*function_)(A1);
+};
+
+// Method: Arity 1.
+template <typename R, typename T, typename A1>
+class RunnableAdapter<R (T::*)(A1)> {
+ public:
+  typedef R(RunType)(T*, A1);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1)) : method_(method) {}
+
+  R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1) {
+    return (object->*method_)(CallbackForward(a1));
+  }
+
+ private:
+  R (T::*method_)(A1);
+};
+
+// Const Method: Arity 1.
+template <typename R, typename T, typename A1>
+class RunnableAdapter<R (T::*)(A1) const> {
+ public:
+  typedef R(RunType)(const T*, A1);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1) const) : method_(method) {}
+
+  R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1) {
+    return (object->*method_)(CallbackForward(a1));
+  }
+
+ private:
+  R (T::*method_)(A1) const;
+};
+
+// Function: Arity 2.
+template <typename R, typename A1, typename A2>
+class RunnableAdapter<R (*)(A1, A2)> {
+ public:
+  typedef R(RunType)(A1, A2);
+
+  explicit RunnableAdapter(R (*function)(A1, A2)) : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2) {
+    return function_(CallbackForward(a1), CallbackForward(a2));
+  }
+
+ private:
+  R (*function_)(A1, A2);
+};
+
+// Method: Arity 2.
+template <typename R, typename T, typename A1, typename A2>
+class RunnableAdapter<R (T::*)(A1, A2)> {
+ public:
+  typedef R(RunType)(T*, A1, A2);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2)) : method_(method) {}
+
+  R Run(T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2));
+  }
+
+ private:
+  R (T::*method_)(A1, A2);
+};
+
+// Const Method: Arity 2.
+template <typename R, typename T, typename A1, typename A2>
+class RunnableAdapter<R (T::*)(A1, A2) const> {
+ public:
+  typedef R(RunType)(const T*, A1, A2);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2) const) : method_(method) {}
+
+  R Run(const T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2));
+  }
+
+ private:
+  R (T::*method_)(A1, A2) const;
+};
+
+// Function: Arity 3.
+template <typename R, typename A1, typename A2, typename A3>
+class RunnableAdapter<R (*)(A1, A2, A3)> {
+ public:
+  typedef R(RunType)(A1, A2, A3);
+
+  explicit RunnableAdapter(R (*function)(A1, A2, A3)) : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3) {
+    return function_(CallbackForward(a1), CallbackForward(a2),
+                     CallbackForward(a3));
+  }
+
+ private:
+  R (*function_)(A1, A2, A3);
+};
+
+// Method: Arity 3.
+template <typename R, typename T, typename A1, typename A2, typename A3>
+class RunnableAdapter<R (T::*)(A1, A2, A3)> {
+ public:
+  typedef R(RunType)(T*, A1, A2, A3);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3)) : method_(method) {}
+
+  R Run(T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3);
+};
+
+// Const Method: Arity 3.
+template <typename R, typename T, typename A1, typename A2, typename A3>
+class RunnableAdapter<R (T::*)(A1, A2, A3) const> {
+ public:
+  typedef R(RunType)(const T*, A1, A2, A3);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3) const)
+      : method_(method) {}
+
+  R Run(const T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3) const;
+};
+
+// Function: Arity 4.
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class RunnableAdapter<R (*)(A1, A2, A3, A4)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4);
+
+  explicit RunnableAdapter(R (*function)(A1, A2, A3, A4))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4) {
+    return function_(CallbackForward(a1), CallbackForward(a2),
+                     CallbackForward(a3), CallbackForward(a4));
+  }
+
+ private:
+  R (*function_)(A1, A2, A3, A4);
+};
+
+// Method: Arity 4.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4)> {
+ public:
+  typedef R(RunType)(T*, A1, A2, A3, A4);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4)) : method_(method) {}
+
+  R Run(T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4);
+};
+
+// Const Method: Arity 4.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4) const> {
+ public:
+  typedef R(RunType)(const T*, A1, A2, A3, A4);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4) const)
+      : method_(method) {}
+
+  R Run(const T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4) const;
+};
+
+// Function: Arity 5.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+class RunnableAdapter<R (*)(A1, A2, A3, A4, A5)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5);
+
+  explicit RunnableAdapter(R (*function)(A1, A2, A3, A4, A5))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5) {
+    return function_(CallbackForward(a1), CallbackForward(a2),
+                     CallbackForward(a3), CallbackForward(a4),
+                     CallbackForward(a5));
+  }
+
+ private:
+  R (*function_)(A1, A2, A3, A4, A5);
+};
+
+// Method: Arity 5.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4, A5)> {
+ public:
+  typedef R(RunType)(T*, A1, A2, A3, A4, A5);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4, A5))
+      : method_(method) {}
+
+  R Run(T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4),
+                              CallbackForward(a5));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4, A5);
+};
+
+// Const Method: Arity 5.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4, A5) const> {
+ public:
+  typedef R(RunType)(const T*, A1, A2, A3, A4, A5);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4, A5) const)
+      : method_(method) {}
+
+  R Run(const T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4),
+                              CallbackForward(a5));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4, A5) const;
+};
+
+// Function: Arity 6.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+class RunnableAdapter<R (*)(A1, A2, A3, A4, A5, A6)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6);
+
+  explicit RunnableAdapter(R (*function)(A1, A2, A3, A4, A5, A6))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6) {
+    return function_(CallbackForward(a1), CallbackForward(a2),
+                     CallbackForward(a3), CallbackForward(a4),
+                     CallbackForward(a5), CallbackForward(a6));
+  }
+
+ private:
+  R (*function_)(A1, A2, A3, A4, A5, A6);
+};
+
+// Method: Arity 6.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4, A5, A6)> {
+ public:
+  typedef R(RunType)(T*, A1, A2, A3, A4, A5, A6);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4, A5, A6))
+      : method_(method) {}
+
+  R Run(T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4),
+                              CallbackForward(a5), CallbackForward(a6));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4, A5, A6);
+};
+
+// Const Method: Arity 6.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4, A5, A6) const> {
+ public:
+  typedef R(RunType)(const T*, A1, A2, A3, A4, A5, A6);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4, A5, A6) const)
+      : method_(method) {}
+
+  R Run(const T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4),
+                              CallbackForward(a5), CallbackForward(a6));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4, A5, A6) const;
+};
+
+// Function: Arity 7.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+class RunnableAdapter<R (*)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+  explicit RunnableAdapter(R (*function)(A1, A2, A3, A4, A5, A6, A7))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6,
+        typename CallbackParamTraits<A7>::ForwardType a7) {
+    return function_(CallbackForward(a1), CallbackForward(a2),
+                     CallbackForward(a3), CallbackForward(a4),
+                     CallbackForward(a5), CallbackForward(a6),
+                     CallbackForward(a7));
+  }
+
+ private:
+  R (*function_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+// Method: Arity 7.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+  typedef R(RunType)(T*, A1, A2, A3, A4, A5, A6, A7);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4, A5, A6, A7))
+      : method_(method) {}
+
+  R Run(T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6,
+        typename CallbackParamTraits<A7>::ForwardType a7) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4),
+                              CallbackForward(a5), CallbackForward(a6),
+                              CallbackForward(a7));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+// Const Method: Arity 7.
+template <typename R,
+          typename T,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+class RunnableAdapter<R (T::*)(A1, A2, A3, A4, A5, A6, A7) const> {
+ public:
+  typedef R(RunType)(const T*, A1, A2, A3, A4, A5, A6, A7);
+  typedef true_type IsMethod;
+
+  explicit RunnableAdapter(R (T::*method)(A1, A2, A3, A4, A5, A6, A7) const)
+      : method_(method) {}
+
+  R Run(const T* object,
+        typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6,
+        typename CallbackParamTraits<A7>::ForwardType a7) {
+    return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+                              CallbackForward(a3), CallbackForward(a4),
+                              CallbackForward(a5), CallbackForward(a6),
+                              CallbackForward(a7));
+  }
+
+ private:
+  R (T::*method_)(A1, A2, A3, A4, A5, A6, A7) const;
+};
+
+// FunctionTraits<>
+//
+// Breaks a function signature apart into typedefs for easier introspection.
+template <typename Sig>
+struct FunctionTraits;
+
+template <typename R>
+struct FunctionTraits<R()> {
+  typedef R ReturnType;
+};
+
+template <typename R, typename A1>
+struct FunctionTraits<R(A1)> {
+  typedef R ReturnType;
+  typedef A1 A1Type;
+};
+
+template <typename R, typename A1, typename A2>
+struct FunctionTraits<R(A1, A2)> {
+  typedef R ReturnType;
+  typedef A1 A1Type;
+  typedef A2 A2Type;
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+struct FunctionTraits<R(A1, A2, A3)> {
+  typedef R ReturnType;
+  typedef A1 A1Type;
+  typedef A2 A2Type;
+  typedef A3 A3Type;
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+struct FunctionTraits<R(A1, A2, A3, A4)> {
+  typedef R ReturnType;
+  typedef A1 A1Type;
+  typedef A2 A2Type;
+  typedef A3 A3Type;
+  typedef A4 A4Type;
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+struct FunctionTraits<R(A1, A2, A3, A4, A5)> {
+  typedef R ReturnType;
+  typedef A1 A1Type;
+  typedef A2 A2Type;
+  typedef A3 A3Type;
+  typedef A4 A4Type;
+  typedef A5 A5Type;
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+struct FunctionTraits<R(A1, A2, A3, A4, A5, A6)> {
+  typedef R ReturnType;
+  typedef A1 A1Type;
+  typedef A2 A2Type;
+  typedef A3 A3Type;
+  typedef A4 A4Type;
+  typedef A5 A5Type;
+  typedef A6 A6Type;
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+struct FunctionTraits<R(A1, A2, A3, A4, A5, A6, A7)> {
+  typedef R ReturnType;
+  typedef A1 A1Type;
+  typedef A2 A2Type;
+  typedef A3 A3Type;
+  typedef A4 A4Type;
+  typedef A5 A5Type;
+  typedef A6 A6Type;
+  typedef A7 A7Type;
+};
+
+// ForceVoidReturn<>
+//
+// Set of templates that support forcing the function return type to void.
+template <typename Sig>
+struct ForceVoidReturn;
+
+template <typename R>
+struct ForceVoidReturn<R()> {
+  typedef void(RunType)();
+};
+
+template <typename R, typename A1>
+struct ForceVoidReturn<R(A1)> {
+  typedef void(RunType)(A1);
+};
+
+template <typename R, typename A1, typename A2>
+struct ForceVoidReturn<R(A1, A2)> {
+  typedef void(RunType)(A1, A2);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+struct ForceVoidReturn<R(A1, A2, A3)> {
+  typedef void(RunType)(A1, A2, A3);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+struct ForceVoidReturn<R(A1, A2, A3, A4)> {
+  typedef void(RunType)(A1, A2, A3, A4);
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+struct ForceVoidReturn<R(A1, A2, A3, A4, A5)> {
+  typedef void(RunType)(A1, A2, A3, A4, A5);
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+struct ForceVoidReturn<R(A1, A2, A3, A4, A5, A6)> {
+  typedef void(RunType)(A1, A2, A3, A4, A5, A6);
+};
+
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+struct ForceVoidReturn<R(A1, A2, A3, A4, A5, A6, A7)> {
+  typedef void(RunType)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+// FunctorTraits<>
+//
+// See description at top of file.
+template <typename T>
+struct FunctorTraits {
+  typedef RunnableAdapter<T> RunnableType;
+  typedef typename RunnableType::RunType RunType;
+};
+
+template <typename T>
+struct FunctorTraits<IgnoreResultHelper<T>> {
+  typedef typename FunctorTraits<T>::RunnableType RunnableType;
+  typedef
+      typename ForceVoidReturn<typename RunnableType::RunType>::RunType RunType;
+};
+
+template <typename T>
+struct FunctorTraits<Callback<T>> {
+  typedef Callback<T> RunnableType;
+  typedef typename Callback<T>::RunType RunType;
+};
+
+// MakeRunnable<>
+//
+// Converts a passed in functor to a RunnableType using type inference.
+
+template <typename T>
+typename FunctorTraits<T>::RunnableType MakeRunnable(const T& t) {
+  return RunnableAdapter<T>(t);
+}
+
+template <typename T>
+typename FunctorTraits<T>::RunnableType MakeRunnable(
+    const IgnoreResultHelper<T>& t) {
+  return MakeRunnable(t.functor_);
+}
+
+template <typename T>
+const typename FunctorTraits<Callback<T>>::RunnableType& MakeRunnable(
+    const Callback<T>& t) {
+  DCHECK(!t.is_null());
+  return t;
+}
+
+// InvokeHelper<>
+//
+// There are 3 logical InvokeHelper<> specializations: normal, void-return,
+// WeakCalls.
+//
+// The normal type just calls the underlying runnable.
+//
+// We need a InvokeHelper to handle void return types in order to support
+// IgnoreResult().  Normally, if the Runnable's RunType had a void return,
+// the template system would just accept "return functor.Run()" ignoring
+// the fact that a void function is being used with return. This piece of
+// sugar breaks though when the Runnable's RunType is not void.  Thus, we
+// need a partial specialization to change the syntax to drop the "return"
+// from the invocation call.
+//
+// WeakCalls similarly need special syntax that is applied to the first
+// argument to check if they should no-op themselves.
+template <bool IsWeakCall,
+          typename ReturnType,
+          typename Runnable,
+          typename ArgsType>
+struct InvokeHelper;
+
+template <typename ReturnType, typename Runnable>
+struct InvokeHelper<false, ReturnType, Runnable, void()> {
+  static ReturnType MakeItSo(Runnable runnable) { return runnable.Run(); }
+};
+
+template <typename Runnable>
+struct InvokeHelper<false, void, Runnable, void()> {
+  static void MakeItSo(Runnable runnable) { runnable.Run(); }
+};
+
+template <typename ReturnType, typename Runnable, typename A1>
+struct InvokeHelper<false, ReturnType, Runnable, void(A1)> {
+  static ReturnType MakeItSo(Runnable runnable, A1 a1) {
+    return runnable.Run(CallbackForward(a1));
+  }
+};
+
+template <typename Runnable, typename A1>
+struct InvokeHelper<false, void, Runnable, void(A1)> {
+  static void MakeItSo(Runnable runnable, A1 a1) {
+    runnable.Run(CallbackForward(a1));
+  }
+};
+
+template <typename Runnable, typename BoundWeakPtr>
+struct InvokeHelper<true, void, Runnable, void(BoundWeakPtr)> {
+  static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr) {
+    if (!weak_ptr.get()) {
+      return;
+    }
+    runnable.Run(weak_ptr.get());
+  }
+};
+
+template <typename ReturnType, typename Runnable, typename A1, typename A2>
+struct InvokeHelper<false, ReturnType, Runnable, void(A1, A2)> {
+  static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2) {
+    return runnable.Run(CallbackForward(a1), CallbackForward(a2));
+  }
+};
+
+template <typename Runnable, typename A1, typename A2>
+struct InvokeHelper<false, void, Runnable, void(A1, A2)> {
+  static void MakeItSo(Runnable runnable, A1 a1, A2 a2) {
+    runnable.Run(CallbackForward(a1), CallbackForward(a2));
+  }
+};
+
+template <typename Runnable, typename BoundWeakPtr, typename A2>
+struct InvokeHelper<true, void, Runnable, void(BoundWeakPtr, A2)> {
+  static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2) {
+    if (!weak_ptr.get()) {
+      return;
+    }
+    runnable.Run(weak_ptr.get(), CallbackForward(a2));
+  }
+};
+
+template <typename ReturnType,
+          typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3>
+struct InvokeHelper<false, ReturnType, Runnable, void(A1, A2, A3)> {
+  static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) {
+    return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+                        CallbackForward(a3));
+  }
+};
+
+template <typename Runnable, typename A1, typename A2, typename A3>
+struct InvokeHelper<false, void, Runnable, void(A1, A2, A3)> {
+  static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) {
+    runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3));
+  }
+};
+
+template <typename Runnable, typename BoundWeakPtr, typename A2, typename A3>
+struct InvokeHelper<true, void, Runnable, void(BoundWeakPtr, A2, A3)> {
+  static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3) {
+    if (!weak_ptr.get()) {
+      return;
+    }
+    runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3));
+  }
+};
+
+template <typename ReturnType,
+          typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4>
+struct InvokeHelper<false, ReturnType, Runnable, void(A1, A2, A3, A4)> {
+  static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) {
+    return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+                        CallbackForward(a3), CallbackForward(a4));
+  }
+};
+
+template <typename Runnable, typename A1, typename A2, typename A3, typename A4>
+struct InvokeHelper<false, void, Runnable, void(A1, A2, A3, A4)> {
+  static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) {
+    runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4));
+  }
+};
+
+template <typename Runnable,
+          typename BoundWeakPtr,
+          typename A2,
+          typename A3,
+          typename A4>
+struct InvokeHelper<true, void, Runnable, void(BoundWeakPtr, A2, A3, A4)> {
+  static void MakeItSo(Runnable runnable,
+                       BoundWeakPtr weak_ptr,
+                       A2 a2,
+                       A3 a3,
+                       A4 a4) {
+    if (!weak_ptr.get()) {
+      return;
+    }
+    runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4));
+  }
+};
+
+template <typename ReturnType,
+          typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+struct InvokeHelper<false, ReturnType, Runnable, void(A1, A2, A3, A4, A5)> {
+  static ReturnType MakeItSo(Runnable runnable,
+                             A1 a1,
+                             A2 a2,
+                             A3 a3,
+                             A4 a4,
+                             A5 a5) {
+    return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+                        CallbackForward(a3), CallbackForward(a4),
+                        CallbackForward(a5));
+  }
+};
+
+template <typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+struct InvokeHelper<false, void, Runnable, void(A1, A2, A3, A4, A5)> {
+  static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+    runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4), CallbackForward(a5));
+  }
+};
+
+template <typename Runnable,
+          typename BoundWeakPtr,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+struct InvokeHelper<true, void, Runnable, void(BoundWeakPtr, A2, A3, A4, A5)> {
+  static void MakeItSo(Runnable runnable,
+                       BoundWeakPtr weak_ptr,
+                       A2 a2,
+                       A3 a3,
+                       A4 a4,
+                       A5 a5) {
+    if (!weak_ptr.get()) {
+      return;
+    }
+    runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4), CallbackForward(a5));
+  }
+};
+
+template <typename ReturnType,
+          typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+struct InvokeHelper<false, ReturnType, Runnable, void(A1, A2, A3, A4, A5, A6)> {
+  static ReturnType
+  MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+    return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+                        CallbackForward(a3), CallbackForward(a4),
+                        CallbackForward(a5), CallbackForward(a6));
+  }
+};
+
+template <typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+struct InvokeHelper<false, void, Runnable, void(A1, A2, A3, A4, A5, A6)> {
+  static void
+  MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+    runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4), CallbackForward(a5), CallbackForward(a6));
+  }
+};
+
+template <typename Runnable,
+          typename BoundWeakPtr,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+struct InvokeHelper<true,
+                    void,
+                    Runnable,
+                    void(BoundWeakPtr, A2, A3, A4, A5, A6)> {
+  static void MakeItSo(Runnable runnable,
+                       BoundWeakPtr weak_ptr,
+                       A2 a2,
+                       A3 a3,
+                       A4 a4,
+                       A5 a5,
+                       A6 a6) {
+    if (!weak_ptr.get()) {
+      return;
+    }
+    runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4), CallbackForward(a5), CallbackForward(a6));
+  }
+};
+
+template <typename ReturnType,
+          typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+struct InvokeHelper<false,
+                    ReturnType,
+                    Runnable,
+                    void(A1, A2, A3, A4, A5, A6, A7)> {
+  static ReturnType
+  MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
+    return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+                        CallbackForward(a3), CallbackForward(a4),
+                        CallbackForward(a5), CallbackForward(a6),
+                        CallbackForward(a7));
+  }
+};
+
+template <typename Runnable,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+struct InvokeHelper<false, void, Runnable, void(A1, A2, A3, A4, A5, A6, A7)> {
+  static void
+  MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
+    runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4), CallbackForward(a5), CallbackForward(a6),
+                 CallbackForward(a7));
+  }
+};
+
+template <typename Runnable,
+          typename BoundWeakPtr,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+struct InvokeHelper<true,
+                    void,
+                    Runnable,
+                    void(BoundWeakPtr, A2, A3, A4, A5, A6, A7)> {
+  static void MakeItSo(Runnable runnable,
+                       BoundWeakPtr weak_ptr,
+                       A2 a2,
+                       A3 a3,
+                       A4 a4,
+                       A5 a5,
+                       A6 a6,
+                       A7 a7) {
+    if (!weak_ptr.get()) {
+      return;
+    }
+    runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3),
+                 CallbackForward(a4), CallbackForward(a5), CallbackForward(a6),
+                 CallbackForward(a7));
+  }
+};
+
+#if !defined(_MSC_VER)
+
+template <typename ReturnType, typename Runnable, typename ArgsType>
+struct InvokeHelper<true, ReturnType, Runnable, ArgsType> {
+  // WeakCalls are only supported for functions with a void return type.
+  // Otherwise, the function result would be undefined if the the WeakPtr<>
+  // is invalidated.
+  COMPILE_ASSERT(is_void<ReturnType>::value,
+                 weak_ptrs_can_only_bind_to_methods_without_return_values);
+};
+
+#endif
+
+// Invoker<>
+//
+// See description at the top of the file.
+template <int NumBound, typename Storage, typename RunType>
+struct Invoker;
+
+// Arity 0 -> 0.
+template <typename StorageType, typename R>
+struct Invoker<0, StorageType, R()> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void()>::MakeItSo(storage->runnable_);
+  }
+};
+
+// Arity 1 -> 1.
+template <typename StorageType, typename R, typename X1>
+struct Invoker<0, StorageType, R(X1)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X1>::ForwardType);
+
+  typedef R(UnboundRunType)(X1);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X1>::ForwardType x1) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename CallbackParamTraits<X1>::ForwardType
+                                 x1)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1));
+  }
+};
+
+// Arity 1 -> 0.
+template <typename StorageType, typename R, typename X1>
+struct Invoker<1, StorageType, R(X1)> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType)>::
+        MakeItSo(storage->runnable_, CallbackForward(x1));
+  }
+};
+
+// Arity 2 -> 2.
+template <typename StorageType, typename R, typename X1, typename X2>
+struct Invoker<0, StorageType, R(X1, X2)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X1>::ForwardType,
+                     typename CallbackParamTraits<X2>::ForwardType);
+
+  typedef R(UnboundRunType)(X1, X2);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X1>::ForwardType x1,
+               typename CallbackParamTraits<X2>::ForwardType x2) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename CallbackParamTraits<X1>::ForwardType x1,
+                             typename CallbackParamTraits<X2>::ForwardType
+                                 x2)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2));
+  }
+};
+
+// Arity 2 -> 1.
+template <typename StorageType, typename R, typename X1, typename X2>
+struct Invoker<1, StorageType, R(X1, X2)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X2>::ForwardType);
+
+  typedef R(UnboundRunType)(X2);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X2>::ForwardType x2) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename CallbackParamTraits<X2>::ForwardType
+                                 x2)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2));
+  }
+};
+
+// Arity 2 -> 0.
+template <typename StorageType, typename R, typename X1, typename X2>
+struct Invoker<2, StorageType, R(X1, X2)> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType)>::
+        MakeItSo(storage->runnable_, CallbackForward(x1), CallbackForward(x2));
+  }
+};
+
+// Arity 3 -> 3.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3>
+struct Invoker<0, StorageType, R(X1, X2, X3)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X1>::ForwardType,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType);
+
+  typedef R(UnboundRunType)(X1, X2, X3);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X1>::ForwardType x1,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename CallbackParamTraits<X1>::ForwardType x1,
+                             typename CallbackParamTraits<X2>::ForwardType x2,
+                             typename CallbackParamTraits<X3>::ForwardType
+                                 x3)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2),
+                                                CallbackForward(x3));
+  }
+};
+
+// Arity 3 -> 2.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3>
+struct Invoker<1, StorageType, R(X1, X2, X3)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType);
+
+  typedef R(UnboundRunType)(X2, X3);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename CallbackParamTraits<X2>::ForwardType x2,
+                             typename CallbackParamTraits<X3>::ForwardType
+                                 x3)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2),
+                                                CallbackForward(x3));
+  }
+};
+
+// Arity 3 -> 1.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3>
+struct Invoker<2, StorageType, R(X1, X2, X3)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X3>::ForwardType);
+
+  typedef R(UnboundRunType)(X3);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X3>::ForwardType x3) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename CallbackParamTraits<X3>::ForwardType
+                                 x3)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2),
+                                                CallbackForward(x3));
+  }
+};
+
+// Arity 3 -> 0.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3>
+struct Invoker<3, StorageType, R(X1, X2, X3)> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename Bound3UnwrapTraits::ForwardType)>::
+        MakeItSo(storage->runnable_, CallbackForward(x1), CallbackForward(x2),
+                 CallbackForward(x3));
+  }
+};
+
+// Arity 4 -> 4.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X1>::ForwardType,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType);
+
+  typedef R(UnboundRunType)(X1, X2, X3, X4);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X1>::ForwardType x1,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename CallbackParamTraits<X1>::ForwardType x1,
+                             typename CallbackParamTraits<X2>::ForwardType x2,
+                             typename CallbackParamTraits<X3>::ForwardType x3,
+                             typename CallbackParamTraits<X4>::ForwardType
+                                 x4)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2),
+                                                CallbackForward(x3),
+                                                CallbackForward(x4));
+  }
+};
+
+// Arity 4 -> 3.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType);
+
+  typedef R(UnboundRunType)(X2, X3, X4);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename CallbackParamTraits<X2>::ForwardType x2,
+                             typename CallbackParamTraits<X3>::ForwardType x3,
+                             typename CallbackParamTraits<X4>::ForwardType
+                                 x4)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2),
+                                                CallbackForward(x3),
+                                                CallbackForward(x4));
+  }
+};
+
+// Arity 4 -> 2.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType);
+
+  typedef R(UnboundRunType)(X3, X4);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename CallbackParamTraits<X3>::ForwardType x3,
+                             typename CallbackParamTraits<X4>::ForwardType
+                                 x4)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2),
+                                                CallbackForward(x3),
+                                                CallbackForward(x4));
+  }
+};
+
+// Arity 4 -> 1.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X4>::ForwardType);
+
+  typedef R(UnboundRunType)(X4);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X4>::ForwardType x4) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename Bound3UnwrapTraits::ForwardType,
+                             typename CallbackParamTraits<X4>::ForwardType
+                                 x4)>::MakeItSo(storage->runnable_,
+                                                CallbackForward(x1),
+                                                CallbackForward(x2),
+                                                CallbackForward(x3),
+                                                CallbackForward(x4));
+  }
+};
+
+// Arity 4 -> 0.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4)> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename Bound3UnwrapTraits::ForwardType,
+                             typename Bound4UnwrapTraits::ForwardType)>::
+        MakeItSo(storage->runnable_, CallbackForward(x1), CallbackForward(x2),
+                 CallbackForward(x3), CallbackForward(x4));
+  }
+};
+
+// Arity 5 -> 5.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X1>::ForwardType,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType);
+
+  typedef R(UnboundRunType)(X1, X2, X3, X4, X5);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X1>::ForwardType x1,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename CallbackParamTraits<X1>::ForwardType x1,
+             typename CallbackParamTraits<X2>::ForwardType x2,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType
+                 x5)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5));
+  }
+};
+
+// Arity 5 -> 4.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType);
+
+  typedef R(UnboundRunType)(X2, X3, X4, X5);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X2>::ForwardType x2,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType
+                 x5)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5));
+  }
+};
+
+// Arity 5 -> 3.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType);
+
+  typedef R(UnboundRunType)(X3, X4, X5);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType
+                 x5)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5));
+  }
+};
+
+// Arity 5 -> 2.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType);
+
+  typedef R(UnboundRunType)(X4, X5);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType
+                 x5)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5));
+  }
+};
+
+// Arity 5 -> 1.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X5>::ForwardType);
+
+  typedef R(UnboundRunType)(X5);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X5>::ForwardType x5) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename Bound4UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X5>::ForwardType
+                 x5)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5));
+  }
+};
+
+// Arity 5 -> 0.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5>
+struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5)> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+    typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    typename Bound5UnwrapTraits::ForwardType x5 =
+        Bound5UnwrapTraits::Unwrap(storage->p5_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename Bound3UnwrapTraits::ForwardType,
+                             typename Bound4UnwrapTraits::ForwardType,
+                             typename Bound5UnwrapTraits::ForwardType)>::
+        MakeItSo(storage->runnable_, CallbackForward(x1), CallbackForward(x2),
+                 CallbackForward(x3), CallbackForward(x4), CallbackForward(x5));
+  }
+};
+
+// Arity 6 -> 6.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X1>::ForwardType,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType);
+
+  typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X1>::ForwardType x1,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename CallbackParamTraits<X1>::ForwardType x1,
+             typename CallbackParamTraits<X2>::ForwardType x2,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType
+                 x6)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6));
+  }
+};
+
+// Arity 6 -> 5.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType);
+
+  typedef R(UnboundRunType)(X2, X3, X4, X5, X6);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X2>::ForwardType x2,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType
+                 x6)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6));
+  }
+};
+
+// Arity 6 -> 4.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType);
+
+  typedef R(UnboundRunType)(X3, X4, X5, X6);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType
+                 x6)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6));
+  }
+};
+
+// Arity 6 -> 3.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType);
+
+  typedef R(UnboundRunType)(X4, X5, X6);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType
+                 x6)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6));
+  }
+};
+
+// Arity 6 -> 2.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType);
+
+  typedef R(UnboundRunType)(X5, X6);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename Bound4UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType
+                 x6)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6));
+  }
+};
+
+// Arity 6 -> 1.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6>
+struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X6>::ForwardType);
+
+  typedef R(UnboundRunType)(X6);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X6>::ForwardType x6) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+    typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    typename Bound5UnwrapTraits::ForwardType x5 =
+        Bound5UnwrapTraits::Unwrap(storage->p5_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename Bound4UnwrapTraits::ForwardType,
+             typename Bound5UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X6>::ForwardType
+                 x6)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6));
+  }
+};
+
+// Arity 6 -> 0.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6>
+struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+    typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+    typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    typename Bound5UnwrapTraits::ForwardType x5 =
+        Bound5UnwrapTraits::Unwrap(storage->p5_);
+    typename Bound6UnwrapTraits::ForwardType x6 =
+        Bound6UnwrapTraits::Unwrap(storage->p6_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename Bound3UnwrapTraits::ForwardType,
+                             typename Bound4UnwrapTraits::ForwardType,
+                             typename Bound5UnwrapTraits::ForwardType,
+                             typename Bound6UnwrapTraits::ForwardType)>::
+        MakeItSo(storage->runnable_, CallbackForward(x1), CallbackForward(x2),
+                 CallbackForward(x3), CallbackForward(x4), CallbackForward(x5),
+                 CallbackForward(x6));
+  }
+};
+
+// Arity 7 -> 7.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X1>::ForwardType,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType,
+                     typename CallbackParamTraits<X7>::ForwardType);
+
+  typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6, X7);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X1>::ForwardType x1,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6,
+               typename CallbackParamTraits<X7>::ForwardType x7) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename CallbackParamTraits<X1>::ForwardType x1,
+             typename CallbackParamTraits<X2>::ForwardType x2,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType x6,
+             typename CallbackParamTraits<X7>::ForwardType
+                 x7)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// Arity 7 -> 6.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X2>::ForwardType,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType,
+                     typename CallbackParamTraits<X7>::ForwardType);
+
+  typedef R(UnboundRunType)(X2, X3, X4, X5, X6, X7);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X2>::ForwardType x2,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6,
+               typename CallbackParamTraits<X7>::ForwardType x7) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X2>::ForwardType x2,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType x6,
+             typename CallbackParamTraits<X7>::ForwardType
+                 x7)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// Arity 7 -> 5.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X3>::ForwardType,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType,
+                     typename CallbackParamTraits<X7>::ForwardType);
+
+  typedef R(UnboundRunType)(X3, X4, X5, X6, X7);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X3>::ForwardType x3,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6,
+               typename CallbackParamTraits<X7>::ForwardType x7) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X3>::ForwardType x3,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType x6,
+             typename CallbackParamTraits<X7>::ForwardType
+                 x7)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// Arity 7 -> 4.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X4>::ForwardType,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType,
+                     typename CallbackParamTraits<X7>::ForwardType);
+
+  typedef R(UnboundRunType)(X4, X5, X6, X7);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X4>::ForwardType x4,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6,
+               typename CallbackParamTraits<X7>::ForwardType x7) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X4>::ForwardType x4,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType x6,
+             typename CallbackParamTraits<X7>::ForwardType
+                 x7)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// Arity 7 -> 3.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X5>::ForwardType,
+                     typename CallbackParamTraits<X6>::ForwardType,
+                     typename CallbackParamTraits<X7>::ForwardType);
+
+  typedef R(UnboundRunType)(X5, X6, X7);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X5>::ForwardType x5,
+               typename CallbackParamTraits<X6>::ForwardType x6,
+               typename CallbackParamTraits<X7>::ForwardType x7) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename Bound4UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X5>::ForwardType x5,
+             typename CallbackParamTraits<X6>::ForwardType x6,
+             typename CallbackParamTraits<X7>::ForwardType
+                 x7)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// Arity 7 -> 2.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X6>::ForwardType,
+                     typename CallbackParamTraits<X7>::ForwardType);
+
+  typedef R(UnboundRunType)(X6, X7);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X6>::ForwardType x6,
+               typename CallbackParamTraits<X7>::ForwardType x7) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+    typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    typename Bound5UnwrapTraits::ForwardType x5 =
+        Bound5UnwrapTraits::Unwrap(storage->p5_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename Bound4UnwrapTraits::ForwardType,
+             typename Bound5UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X6>::ForwardType x6,
+             typename CallbackParamTraits<X7>::ForwardType
+                 x7)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// Arity 7 -> 1.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*,
+                     typename CallbackParamTraits<X7>::ForwardType);
+
+  typedef R(UnboundRunType)(X7);
+
+  static R Run(BindStateBase* base,
+               typename CallbackParamTraits<X7>::ForwardType x7) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+    typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+    typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    typename Bound5UnwrapTraits::ForwardType x5 =
+        Bound5UnwrapTraits::Unwrap(storage->p5_);
+    typename Bound6UnwrapTraits::ForwardType x6 =
+        Bound6UnwrapTraits::Unwrap(storage->p6_);
+    return InvokeHelper<
+        StorageType::IsWeakCall::value, R, typename StorageType::RunnableType,
+        void(typename Bound1UnwrapTraits::ForwardType,
+             typename Bound2UnwrapTraits::ForwardType,
+             typename Bound3UnwrapTraits::ForwardType,
+             typename Bound4UnwrapTraits::ForwardType,
+             typename Bound5UnwrapTraits::ForwardType,
+             typename Bound6UnwrapTraits::ForwardType,
+             typename CallbackParamTraits<X7>::ForwardType
+                 x7)>::MakeItSo(storage->runnable_, CallbackForward(x1),
+                                CallbackForward(x2), CallbackForward(x3),
+                                CallbackForward(x4), CallbackForward(x5),
+                                CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// Arity 7 -> 0.
+template <typename StorageType,
+          typename R,
+          typename X1,
+          typename X2,
+          typename X3,
+          typename X4,
+          typename X5,
+          typename X6,
+          typename X7>
+struct Invoker<7, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+  typedef R(RunType)(BindStateBase*);
+
+  typedef R(UnboundRunType)();
+
+  static R Run(BindStateBase* base) {
+    StorageType* storage = static_cast<StorageType*>(base);
+
+    // Local references to make debugger stepping easier. If in a debugger,
+    // you really want to warp ahead and step through the
+    // InvokeHelper<>::MakeItSo() call below.
+    typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+    typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+    typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+    typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+    typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+    typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits;
+    typedef typename StorageType::Bound7UnwrapTraits Bound7UnwrapTraits;
+
+    typename Bound1UnwrapTraits::ForwardType x1 =
+        Bound1UnwrapTraits::Unwrap(storage->p1_);
+    typename Bound2UnwrapTraits::ForwardType x2 =
+        Bound2UnwrapTraits::Unwrap(storage->p2_);
+    typename Bound3UnwrapTraits::ForwardType x3 =
+        Bound3UnwrapTraits::Unwrap(storage->p3_);
+    typename Bound4UnwrapTraits::ForwardType x4 =
+        Bound4UnwrapTraits::Unwrap(storage->p4_);
+    typename Bound5UnwrapTraits::ForwardType x5 =
+        Bound5UnwrapTraits::Unwrap(storage->p5_);
+    typename Bound6UnwrapTraits::ForwardType x6 =
+        Bound6UnwrapTraits::Unwrap(storage->p6_);
+    typename Bound7UnwrapTraits::ForwardType x7 =
+        Bound7UnwrapTraits::Unwrap(storage->p7_);
+    return InvokeHelper<StorageType::IsWeakCall::value, R,
+                        typename StorageType::RunnableType,
+                        void(typename Bound1UnwrapTraits::ForwardType,
+                             typename Bound2UnwrapTraits::ForwardType,
+                             typename Bound3UnwrapTraits::ForwardType,
+                             typename Bound4UnwrapTraits::ForwardType,
+                             typename Bound5UnwrapTraits::ForwardType,
+                             typename Bound6UnwrapTraits::ForwardType,
+                             typename Bound7UnwrapTraits::ForwardType)>::
+        MakeItSo(storage->runnable_, CallbackForward(x1), CallbackForward(x2),
+                 CallbackForward(x3), CallbackForward(x4), CallbackForward(x5),
+                 CallbackForward(x6), CallbackForward(x7));
+  }
+};
+
+// BindState<>
+//
+// This stores all the state passed into Bind() and is also where most
+// of the template resolution magic occurs.
+//
+// Runnable is the functor we are binding arguments to.
+// RunType is type of the Run() function that the Invoker<> should use.
+// Normally, this is the same as the RunType of the Runnable, but it can
+// be different if an adapter like IgnoreResult() has been used.
+//
+// BoundArgsType contains the storage type for all the bound arguments by
+// (ab)using a function type.
+template <typename Runnable, typename RunType, typename BoundArgsType>
+struct BindState;
+
+template <typename Runnable, typename RunType>
+struct BindState<Runnable, RunType, void()> : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef false_type IsWeakCall;
+  typedef Invoker<0, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+  explicit BindState(const Runnable& runnable)
+      : BindStateBase(&Destroy), runnable_(runnable) {}
+
+  ~BindState() {}
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+};
+
+template <typename Runnable, typename RunType, typename P1>
+struct BindState<Runnable, RunType, void(P1)> : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+  typedef Invoker<1, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+  // Convenience typedefs for bound argument types.
+  typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+
+  BindState(const Runnable& runnable, const P1& p1)
+      : BindStateBase(&Destroy), runnable_(runnable), p1_(p1) {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+  }
+
+  ~BindState() {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+  }
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+  P1 p1_;
+};
+
+template <typename Runnable, typename RunType, typename P1, typename P2>
+struct BindState<Runnable, RunType, void(P1, P2)> : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+  typedef Invoker<2, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+  // Convenience typedefs for bound argument types.
+  typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+  typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+
+  BindState(const Runnable& runnable, const P1& p1, const P2& p2)
+      : BindStateBase(&Destroy), runnable_(runnable), p1_(p1), p2_(p2) {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+  }
+
+  ~BindState() {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+  }
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+  P1 p1_;
+  P2 p2_;
+};
+
+template <typename Runnable,
+          typename RunType,
+          typename P1,
+          typename P2,
+          typename P3>
+struct BindState<Runnable, RunType, void(P1, P2, P3)> : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+  typedef Invoker<3, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+  // Convenience typedefs for bound argument types.
+  typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+  typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+  typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+
+  BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3)
+      : BindStateBase(&Destroy),
+        runnable_(runnable),
+        p1_(p1),
+        p2_(p2),
+        p3_(p3) {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+  }
+
+  ~BindState() {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+  }
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+};
+
+template <typename Runnable,
+          typename RunType,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4>
+struct BindState<Runnable, RunType, void(P1, P2, P3, P4)>
+    : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+  typedef Invoker<4, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+  // Convenience typedefs for bound argument types.
+  typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+  typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+  typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+  typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+
+  BindState(const Runnable& runnable,
+            const P1& p1,
+            const P2& p2,
+            const P3& p3,
+            const P4& p4)
+      : BindStateBase(&Destroy),
+        runnable_(runnable),
+        p1_(p1),
+        p2_(p2),
+        p3_(p3),
+        p4_(p4) {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+  }
+
+  ~BindState() {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+  }
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+};
+
+template <typename Runnable,
+          typename RunType,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+struct BindState<Runnable, RunType, void(P1, P2, P3, P4, P5)>
+    : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+  typedef Invoker<5, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+  // Convenience typedefs for bound argument types.
+  typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+  typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+  typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+  typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+  typedef UnwrapTraits<P5> Bound5UnwrapTraits;
+
+  BindState(const Runnable& runnable,
+            const P1& p1,
+            const P2& p2,
+            const P3& p3,
+            const P4& p4,
+            const P5& p5)
+      : BindStateBase(&Destroy),
+        runnable_(runnable),
+        p1_(p1),
+        p2_(p2),
+        p3_(p3),
+        p4_(p4),
+        p5_(p5) {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+  }
+
+  ~BindState() {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+  }
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+  P5 p5_;
+};
+
+template <typename Runnable,
+          typename RunType,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5,
+          typename P6>
+struct BindState<Runnable, RunType, void(P1, P2, P3, P4, P5, P6)>
+    : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+  typedef Invoker<6, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+  // Convenience typedefs for bound argument types.
+  typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+  typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+  typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+  typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+  typedef UnwrapTraits<P5> Bound5UnwrapTraits;
+  typedef UnwrapTraits<P6> Bound6UnwrapTraits;
+
+  BindState(const Runnable& runnable,
+            const P1& p1,
+            const P2& p2,
+            const P3& p3,
+            const P4& p4,
+            const P5& p5,
+            const P6& p6)
+      : BindStateBase(&Destroy),
+        runnable_(runnable),
+        p1_(p1),
+        p2_(p2),
+        p3_(p3),
+        p4_(p4),
+        p5_(p5),
+        p6_(p6) {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+  }
+
+  ~BindState() {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+  }
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+  P5 p5_;
+  P6 p6_;
+};
+
+template <typename Runnable,
+          typename RunType,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5,
+          typename P6,
+          typename P7>
+struct BindState<Runnable, RunType, void(P1, P2, P3, P4, P5, P6, P7)>
+    : public BindStateBase {
+  typedef Runnable RunnableType;
+  typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+  typedef Invoker<7, BindState, RunType> InvokerType;
+  typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+  // Convenience typedefs for bound argument types.
+  typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+  typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+  typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+  typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+  typedef UnwrapTraits<P5> Bound5UnwrapTraits;
+  typedef UnwrapTraits<P6> Bound6UnwrapTraits;
+  typedef UnwrapTraits<P7> Bound7UnwrapTraits;
+
+  BindState(const Runnable& runnable,
+            const P1& p1,
+            const P2& p2,
+            const P3& p3,
+            const P4& p4,
+            const P5& p5,
+            const P6& p6,
+            const P7& p7)
+      : BindStateBase(&Destroy),
+        runnable_(runnable),
+        p1_(p1),
+        p2_(p2),
+        p3_(p3),
+        p4_(p4),
+        p5_(p5),
+        p6_(p6),
+        p7_(p7) {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+  }
+
+  ~BindState() {
+    MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+  }
+
+  static void Destroy(BindStateBase* self) {
+    delete static_cast<BindState*>(self);
+  }
+
+  RunnableType runnable_;
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+  P5 p5_;
+  P6 p6_;
+  P7 p7_;
+};
+
+}  // namespace cef_internal
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_BIND_INTERNAL_H_
diff --git a/src/include/base/internal/cef_bind_internal_win.h b/src/include/base/internal/cef_bind_internal_win.h
new file mode 100644
index 0000000..2d98fd1
--- /dev/null
+++ b/src/include/base/internal/cef_bind_internal_win.h
@@ -0,0 +1,398 @@
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_bind.h instead.
+
+// Specializations of RunnableAdapter<> for Windows specific calling
+// conventions.  Please see base/bind_internal.h for more info.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_BIND_INTERNAL_WIN_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_BIND_INTERNAL_WIN_H_
+
+// In the x64 architecture in Windows, __fastcall, __stdcall, etc, are all
+// the same as __cdecl which would turn the following specializations into
+// multiple definitions.
+#if defined(ARCH_CPU_X86_FAMILY)
+#if defined(ARCH_CPU_32_BITS)
+
+namespace base {
+namespace cef_internal {
+
+template <typename Functor>
+class RunnableAdapter;
+
+// __stdcall Function: Arity 0.
+template <typename R>
+class RunnableAdapter<R(__stdcall*)()> {
+ public:
+  typedef R(RunType)();
+
+  explicit RunnableAdapter(R(__stdcall* function)()) : function_(function) {}
+
+  R Run() { return function_(); }
+
+ private:
+  R(__stdcall* function_)();
+};
+
+// __fastcall Function: Arity 0.
+template <typename R>
+class RunnableAdapter<R(__fastcall*)()> {
+ public:
+  typedef R(RunType)();
+
+  explicit RunnableAdapter(R(__fastcall* function)()) : function_(function) {}
+
+  R Run() { return function_(); }
+
+ private:
+  R(__fastcall* function_)();
+};
+
+// __stdcall Function: Arity 1.
+template <typename R, typename A1>
+class RunnableAdapter<R(__stdcall*)(A1)> {
+ public:
+  typedef R(RunType)(A1);
+
+  explicit RunnableAdapter(R(__stdcall* function)(A1)) : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1) {
+    return function_(a1);
+  }
+
+ private:
+  R(__stdcall* function_)(A1);
+};
+
+// __fastcall Function: Arity 1.
+template <typename R, typename A1>
+class RunnableAdapter<R(__fastcall*)(A1)> {
+ public:
+  typedef R(RunType)(A1);
+
+  explicit RunnableAdapter(R(__fastcall* function)(A1)) : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1) {
+    return function_(a1);
+  }
+
+ private:
+  R(__fastcall* function_)(A1);
+};
+
+// __stdcall Function: Arity 2.
+template <typename R, typename A1, typename A2>
+class RunnableAdapter<R(__stdcall*)(A1, A2)> {
+ public:
+  typedef R(RunType)(A1, A2);
+
+  explicit RunnableAdapter(R(__stdcall* function)(A1, A2))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2) {
+    return function_(a1, a2);
+  }
+
+ private:
+  R(__stdcall* function_)(A1, A2);
+};
+
+// __fastcall Function: Arity 2.
+template <typename R, typename A1, typename A2>
+class RunnableAdapter<R(__fastcall*)(A1, A2)> {
+ public:
+  typedef R(RunType)(A1, A2);
+
+  explicit RunnableAdapter(R(__fastcall* function)(A1, A2))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2) {
+    return function_(a1, a2);
+  }
+
+ private:
+  R(__fastcall* function_)(A1, A2);
+};
+
+// __stdcall Function: Arity 3.
+template <typename R, typename A1, typename A2, typename A3>
+class RunnableAdapter<R(__stdcall*)(A1, A2, A3)> {
+ public:
+  typedef R(RunType)(A1, A2, A3);
+
+  explicit RunnableAdapter(R(__stdcall* function)(A1, A2, A3))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3) {
+    return function_(a1, a2, a3);
+  }
+
+ private:
+  R(__stdcall* function_)(A1, A2, A3);
+};
+
+// __fastcall Function: Arity 3.
+template <typename R, typename A1, typename A2, typename A3>
+class RunnableAdapter<R(__fastcall*)(A1, A2, A3)> {
+ public:
+  typedef R(RunType)(A1, A2, A3);
+
+  explicit RunnableAdapter(R(__fastcall* function)(A1, A2, A3))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3) {
+    return function_(a1, a2, a3);
+  }
+
+ private:
+  R(__fastcall* function_)(A1, A2, A3);
+};
+
+// __stdcall Function: Arity 4.
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class RunnableAdapter<R(__stdcall*)(A1, A2, A3, A4)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4);
+
+  explicit RunnableAdapter(R(__stdcall* function)(A1, A2, A3, A4))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4) {
+    return function_(a1, a2, a3, a4);
+  }
+
+ private:
+  R(__stdcall* function_)(A1, A2, A3, A4);
+};
+
+// __fastcall Function: Arity 4.
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class RunnableAdapter<R(__fastcall*)(A1, A2, A3, A4)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4);
+
+  explicit RunnableAdapter(R(__fastcall* function)(A1, A2, A3, A4))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4) {
+    return function_(a1, a2, a3, a4);
+  }
+
+ private:
+  R(__fastcall* function_)(A1, A2, A3, A4);
+};
+
+// __stdcall Function: Arity 5.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+class RunnableAdapter<R(__stdcall*)(A1, A2, A3, A4, A5)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5);
+
+  explicit RunnableAdapter(R(__stdcall* function)(A1, A2, A3, A4, A5))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5) {
+    return function_(a1, a2, a3, a4, a5);
+  }
+
+ private:
+  R(__stdcall* function_)(A1, A2, A3, A4, A5);
+};
+
+// __fastcall Function: Arity 5.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5>
+class RunnableAdapter<R(__fastcall*)(A1, A2, A3, A4, A5)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5);
+
+  explicit RunnableAdapter(R(__fastcall* function)(A1, A2, A3, A4, A5))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5) {
+    return function_(a1, a2, a3, a4, a5);
+  }
+
+ private:
+  R(__fastcall* function_)(A1, A2, A3, A4, A5);
+};
+
+// __stdcall Function: Arity 6.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+class RunnableAdapter<R(__stdcall*)(A1, A2, A3, A4, A5, A6)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6);
+
+  explicit RunnableAdapter(R(__stdcall* function)(A1, A2, A3, A4, A5, A6))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6) {
+    return function_(a1, a2, a3, a4, a5, a6);
+  }
+
+ private:
+  R(__stdcall* function_)(A1, A2, A3, A4, A5, A6);
+};
+
+// __fastcall Function: Arity 6.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6>
+class RunnableAdapter<R(__fastcall*)(A1, A2, A3, A4, A5, A6)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6);
+
+  explicit RunnableAdapter(R(__fastcall* function)(A1, A2, A3, A4, A5, A6))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6) {
+    return function_(a1, a2, a3, a4, a5, a6);
+  }
+
+ private:
+  R(__fastcall* function_)(A1, A2, A3, A4, A5, A6);
+};
+
+// __stdcall Function: Arity 7.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+class RunnableAdapter<R(__stdcall*)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+  explicit RunnableAdapter(R(__stdcall* function)(A1, A2, A3, A4, A5, A6, A7))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6,
+        typename CallbackParamTraits<A7>::ForwardType a7) {
+    return function_(a1, a2, a3, a4, a5, a6, a7);
+  }
+
+ private:
+  R(__stdcall* function_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+// __fastcall Function: Arity 7.
+template <typename R,
+          typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7>
+class RunnableAdapter<R(__fastcall*)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+  typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+  explicit RunnableAdapter(R(__fastcall* function)(A1, A2, A3, A4, A5, A6, A7))
+      : function_(function) {}
+
+  R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+        typename CallbackParamTraits<A2>::ForwardType a2,
+        typename CallbackParamTraits<A3>::ForwardType a3,
+        typename CallbackParamTraits<A4>::ForwardType a4,
+        typename CallbackParamTraits<A5>::ForwardType a5,
+        typename CallbackParamTraits<A6>::ForwardType a6,
+        typename CallbackParamTraits<A7>::ForwardType a7) {
+    return function_(a1, a2, a3, a4, a5, a6, a7);
+  }
+
+ private:
+  R(__fastcall* function_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+}  // namespace cef_internal
+}  // namespace base
+
+#endif  // defined(ARCH_CPU_32_BITS)
+#endif  // defined(ARCH_CPU_X86_FAMILY)
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_BIND_INTERNAL_WIN_H_
diff --git a/src/include/base/internal/cef_callback_internal.h b/src/include/base/internal/cef_callback_internal.h
new file mode 100644
index 0000000..1f2e16c
--- /dev/null
+++ b/src/include/base/internal/cef_callback_internal.h
@@ -0,0 +1,224 @@
+// Copyright (c) 2012 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_bind.h or
+// base/cef_callback.h instead.
+
+// This file contains utility functions and classes that help the
+// implementation, and management of the Callback objects.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_CALLBACK_INTERNAL_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_CALLBACK_INTERNAL_H_
+
+#include <stddef.h>
+
+#include "include/base/cef_atomic_ref_count.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/base/cef_template_util.h"
+
+template <typename T>
+class ScopedVector;
+
+namespace base {
+namespace cef_internal {
+class CallbackBase;
+
+// At the base level, the only task is to add reference counting data. Don't use
+// RefCountedThreadSafe since it requires the destructor to be a virtual method.
+// Creating a vtable for every BindState template instantiation results in a lot
+// of bloat. Its only task is to call the destructor which can be done with a
+// function pointer.
+class BindStateBase {
+ protected:
+  explicit BindStateBase(void (*destructor)(BindStateBase*))
+      : ref_count_(0), destructor_(destructor) {}
+  ~BindStateBase() {}
+
+ private:
+  friend class scoped_refptr<BindStateBase>;
+  friend class CallbackBase;
+
+  void AddRef();
+  void Release();
+
+  AtomicRefCount ref_count_;
+
+  // Pointer to a function that will properly destroy |this|.
+  void (*destructor_)(BindStateBase*);
+
+  DISALLOW_COPY_AND_ASSIGN(BindStateBase);
+};
+
+// Holds the Callback methods that don't require specialization to reduce
+// template bloat.
+class CallbackBase {
+ public:
+  // Returns true if Callback is null (doesn't refer to anything).
+  bool is_null() const { return bind_state_.get() == NULL; }
+
+  // Returns the Callback into an uninitialized state.
+  void Reset();
+
+ protected:
+  // In C++, it is safe to cast function pointers to function pointers of
+  // another type. It is not okay to use void*. We create a InvokeFuncStorage
+  // that that can store our function pointer, and then cast it back to
+  // the original type on usage.
+  typedef void (*InvokeFuncStorage)(void);
+
+  // Returns true if this callback equals |other|. |other| may be null.
+  bool Equals(const CallbackBase& other) const;
+
+  // Allow initializing of |bind_state_| via the constructor to avoid default
+  // initialization of the scoped_refptr.  We do not also initialize
+  // |polymorphic_invoke_| here because doing a normal assignment in the
+  // derived Callback templates makes for much nicer compiler errors.
+  explicit CallbackBase(BindStateBase* bind_state);
+
+  // Force the destructor to be instantiated inside this translation unit so
+  // that our subclasses will not get inlined versions.  Avoids more template
+  // bloat.
+  ~CallbackBase();
+
+  scoped_refptr<BindStateBase> bind_state_;
+  InvokeFuncStorage polymorphic_invoke_;
+};
+
+// A helper template to determine if given type is non-const move-only-type,
+// i.e. if a value of the given type should be passed via .Pass() in a
+// destructive way.
+template <typename T>
+struct IsMoveOnlyType {
+  template <typename U>
+  static YesType Test(const typename U::MoveOnlyTypeForCPP03*);
+
+  template <typename U>
+  static NoType Test(...);
+
+  static const bool value =
+      sizeof(Test<T>(0)) == sizeof(YesType) && !is_const<T>::value;
+};
+
+// This is a typetraits object that's used to take an argument type, and
+// extract a suitable type for storing and forwarding arguments.
+//
+// In particular, it strips off references, and converts arrays to
+// pointers for storage; and it avoids accidentally trying to create a
+// "reference of a reference" if the argument is a reference type.
+//
+// This array type becomes an issue for storage because we are passing bound
+// parameters by const reference. In this case, we end up passing an actual
+// array type in the initializer list which C++ does not allow.  This will
+// break passing of C-string literals.
+template <typename T, bool is_move_only = IsMoveOnlyType<T>::value>
+struct CallbackParamTraits {
+  typedef const T& ForwardType;
+  typedef T StorageType;
+};
+
+// The Storage should almost be impossible to trigger unless someone manually
+// specifies type of the bind parameters.  However, in case they do,
+// this will guard against us accidentally storing a reference parameter.
+//
+// The ForwardType should only be used for unbound arguments.
+template <typename T>
+struct CallbackParamTraits<T&, false> {
+  typedef T& ForwardType;
+  typedef T StorageType;
+};
+
+// Note that for array types, we implicitly add a const in the conversion. This
+// means that it is not possible to bind array arguments to functions that take
+// a non-const pointer. Trying to specialize the template based on a "const
+// T[n]" does not seem to match correctly, so we are stuck with this
+// restriction.
+template <typename T, size_t n>
+struct CallbackParamTraits<T[n], false> {
+  typedef const T* ForwardType;
+  typedef const T* StorageType;
+};
+
+// See comment for CallbackParamTraits<T[n]>.
+template <typename T>
+struct CallbackParamTraits<T[], false> {
+  typedef const T* ForwardType;
+  typedef const T* StorageType;
+};
+
+// Parameter traits for movable-but-not-copyable scopers.
+//
+// Callback<>/Bind() understands movable-but-not-copyable semantics where
+// the type cannot be copied but can still have its state destructively
+// transferred (aka. moved) to another instance of the same type by calling a
+// helper function.  When used with Bind(), this signifies transferal of the
+// object's state to the target function.
+//
+// For these types, the ForwardType must not be a const reference, or a
+// reference.  A const reference is inappropriate, and would break const
+// correctness, because we are implementing a destructive move.  A non-const
+// reference cannot be used with temporaries which means the result of a
+// function or a cast would not be usable with Callback<> or Bind().
+template <typename T>
+struct CallbackParamTraits<T, true> {
+  typedef T ForwardType;
+  typedef T StorageType;
+};
+
+// CallbackForward() is a very limited simulation of C++11's std::forward()
+// used by the Callback/Bind system for a set of movable-but-not-copyable
+// types.  It is needed because forwarding a movable-but-not-copyable
+// argument to another function requires us to invoke the proper move
+// operator to create a rvalue version of the type.  The supported types are
+// whitelisted below as overloads of the CallbackForward() function. The
+// default template compiles out to be a no-op.
+//
+// In C++11, std::forward would replace all uses of this function.  However, it
+// is impossible to implement a general std::forward with C++11 due to a lack
+// of rvalue references.
+//
+// In addition to Callback/Bind, this is used by PostTaskAndReplyWithResult to
+// simulate std::forward() and forward the result of one Callback as a
+// parameter to another callback. This is to support Callbacks that return
+// the movable-but-not-copyable types whitelisted above.
+template <typename T>
+typename enable_if<!IsMoveOnlyType<T>::value, T>::type& CallbackForward(T& t) {
+  return t;
+}
+
+template <typename T>
+typename enable_if<IsMoveOnlyType<T>::value, T>::type CallbackForward(T& t) {
+  return t.Pass();
+}
+
+}  // namespace cef_internal
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_CALLBACK_INTERNAL_H_
diff --git a/src/include/base/internal/cef_lock_impl.h b/src/include/base/internal/cef_lock_impl.h
new file mode 100644
index 0000000..470547f
--- /dev/null
+++ b/src/include/base/internal/cef_lock_impl.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_lock.h instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_LOCK_IMPL_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_LOCK_IMPL_H_
+
+#include "include/base/cef_build.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <pthread.h>
+#endif
+
+#include "include/base/cef_macros.h"
+
+namespace base {
+namespace cef_internal {
+
+// This class implements the underlying platform-specific spin-lock mechanism
+// used for the Lock class.  Most users should not use LockImpl directly, but
+// should instead use Lock.
+class LockImpl {
+ public:
+#if defined(OS_WIN)
+  typedef CRITICAL_SECTION NativeHandle;
+#elif defined(OS_POSIX)
+  typedef pthread_mutex_t NativeHandle;
+#endif
+
+  LockImpl();
+  ~LockImpl();
+
+  // If the lock is not held, take it and return true.  If the lock is already
+  // held by something else, immediately return false.
+  bool Try();
+
+  // Take the lock, blocking until it is available if necessary.
+  void Lock();
+
+  // Release the lock.  This must only be called by the lock's holder: after
+  // a successful call to Try, or a call to Lock.
+  void Unlock();
+
+  // Return the native underlying lock.
+  // TODO(awalker): refactor lock and condition variables so that this is
+  // unnecessary.
+  NativeHandle* native_handle() { return &native_handle_; }
+
+ private:
+  NativeHandle native_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(LockImpl);
+};
+
+}  // namespace cef_internal
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_LOCK_IMPL_H_
diff --git a/src/include/base/internal/cef_net_error_list.h b/src/include/base/internal/cef_net_error_list.h
new file mode 100644
index 0000000..b0445fb
--- /dev/null
+++ b/src/include/base/internal/cef_net_error_list.h
@@ -0,0 +1,8 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// Include net error codes from the Chromium source location. When creating a
+// CEF binary distribution this file will be replaced with the Chromium version.
+
+#include "net/base/net_error_list.h"
diff --git a/src/include/base/internal/cef_raw_scoped_refptr_mismatch_checker.h b/src/include/base/internal/cef_raw_scoped_refptr_mismatch_checker.h
new file mode 100644
index 0000000..9f2f932
--- /dev/null
+++ b/src/include/base/internal/cef_raw_scoped_refptr_mismatch_checker.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_callback.h instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+#define CEF_INCLUDE_BASE_INTERNAL_CEF_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+
+#include "include/base/cef_build.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/base/cef_template_util.h"
+#include "include/base/cef_tuple.h"
+
+// It is dangerous to post a task with a T* argument where T is a subtype of
+// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the
+// object may already have been deleted since it was not held with a
+// scoped_refptr. Example: http://crbug.com/27191
+// The following set of traits are designed to generate a compile error
+// whenever this antipattern is attempted.
+
+namespace base {
+
+namespace cef_internal {
+
+template <typename T>
+struct NeedsScopedRefptrButGetsRawPtr {
+#if defined(OS_WIN)
+  enum { value = base::false_type::value };
+#else
+  enum {
+    // Human readable translation: you needed to be a scoped_refptr if you are a
+    // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase)
+    // type.
+    value = (is_pointer<T>::value &&
+             (is_convertible<T, subtle::RefCountedBase*>::value ||
+              is_convertible<T, subtle::RefCountedThreadSafeBase*>::value))
+  };
+#endif
+};
+
+template <typename Params>
+struct ParamsUseScopedRefptrCorrectly {
+  enum { value = 0 };
+};
+
+template <>
+struct ParamsUseScopedRefptrCorrectly<Tuple0> {
+  enum { value = 1 };
+};
+
+template <typename A>
+struct ParamsUseScopedRefptrCorrectly<Tuple1<A>> {
+  enum { value = !NeedsScopedRefptrButGetsRawPtr<A>::value };
+};
+
+template <typename A, typename B>
+struct ParamsUseScopedRefptrCorrectly<Tuple2<A, B>> {
+  enum {
+    value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+              NeedsScopedRefptrButGetsRawPtr<B>::value)
+  };
+};
+
+template <typename A, typename B, typename C>
+struct ParamsUseScopedRefptrCorrectly<Tuple3<A, B, C>> {
+  enum {
+    value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+              NeedsScopedRefptrButGetsRawPtr<B>::value ||
+              NeedsScopedRefptrButGetsRawPtr<C>::value)
+  };
+};
+
+template <typename A, typename B, typename C, typename D>
+struct ParamsUseScopedRefptrCorrectly<Tuple4<A, B, C, D>> {
+  enum {
+    value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+              NeedsScopedRefptrButGetsRawPtr<B>::value ||
+              NeedsScopedRefptrButGetsRawPtr<C>::value ||
+              NeedsScopedRefptrButGetsRawPtr<D>::value)
+  };
+};
+
+template <typename A, typename B, typename C, typename D, typename E>
+struct ParamsUseScopedRefptrCorrectly<Tuple5<A, B, C, D, E>> {
+  enum {
+    value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+              NeedsScopedRefptrButGetsRawPtr<B>::value ||
+              NeedsScopedRefptrButGetsRawPtr<C>::value ||
+              NeedsScopedRefptrButGetsRawPtr<D>::value ||
+              NeedsScopedRefptrButGetsRawPtr<E>::value)
+  };
+};
+
+template <typename A,
+          typename B,
+          typename C,
+          typename D,
+          typename E,
+          typename F>
+struct ParamsUseScopedRefptrCorrectly<Tuple6<A, B, C, D, E, F>> {
+  enum {
+    value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+              NeedsScopedRefptrButGetsRawPtr<B>::value ||
+              NeedsScopedRefptrButGetsRawPtr<C>::value ||
+              NeedsScopedRefptrButGetsRawPtr<D>::value ||
+              NeedsScopedRefptrButGetsRawPtr<E>::value ||
+              NeedsScopedRefptrButGetsRawPtr<F>::value)
+  };
+};
+
+template <typename A,
+          typename B,
+          typename C,
+          typename D,
+          typename E,
+          typename F,
+          typename G>
+struct ParamsUseScopedRefptrCorrectly<Tuple7<A, B, C, D, E, F, G>> {
+  enum {
+    value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+              NeedsScopedRefptrButGetsRawPtr<B>::value ||
+              NeedsScopedRefptrButGetsRawPtr<C>::value ||
+              NeedsScopedRefptrButGetsRawPtr<D>::value ||
+              NeedsScopedRefptrButGetsRawPtr<E>::value ||
+              NeedsScopedRefptrButGetsRawPtr<F>::value ||
+              NeedsScopedRefptrButGetsRawPtr<G>::value)
+  };
+};
+
+template <typename A,
+          typename B,
+          typename C,
+          typename D,
+          typename E,
+          typename F,
+          typename G,
+          typename H>
+struct ParamsUseScopedRefptrCorrectly<Tuple8<A, B, C, D, E, F, G, H>> {
+  enum {
+    value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+              NeedsScopedRefptrButGetsRawPtr<B>::value ||
+              NeedsScopedRefptrButGetsRawPtr<C>::value ||
+              NeedsScopedRefptrButGetsRawPtr<D>::value ||
+              NeedsScopedRefptrButGetsRawPtr<E>::value ||
+              NeedsScopedRefptrButGetsRawPtr<F>::value ||
+              NeedsScopedRefptrButGetsRawPtr<G>::value ||
+              NeedsScopedRefptrButGetsRawPtr<H>::value)
+  };
+};
+
+}  // namespace cef_internal
+
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_CEF_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
diff --git a/src/include/base/internal/cef_thread_checker_impl.h b/src/include/base/internal/cef_thread_checker_impl.h
new file mode 100644
index 0000000..2654698
--- /dev/null
+++ b/src/include/base/internal/cef_thread_checker_impl.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Do not include this header file directly. Use base/cef_thread_checker.h
+// instead.
+
+#ifndef CEF_INCLUDE_BASE_INTERNAL_THREAD_CHECKER_IMPL_H_
+#define CEF_INCLUDE_BASE_INTERNAL_THREAD_CHECKER_IMPL_H_
+
+#include "include/base/cef_lock.h"
+#include "include/base/cef_platform_thread.h"
+
+namespace base {
+namespace cef_internal {
+
+// Real implementation of ThreadChecker, for use in debug mode, or
+// for temporary use in release mode (e.g. to CHECK on a threading issue
+// seen only in the wild).
+//
+// Note: You should almost always use the ThreadChecker class to get the
+// right version for your build configuration.
+class ThreadCheckerImpl {
+ public:
+  ThreadCheckerImpl();
+  ~ThreadCheckerImpl();
+
+  bool CalledOnValidThread() const;
+
+  // Changes the thread that is checked for in CalledOnValidThread.  This may
+  // be useful when an object may be created on one thread and then used
+  // exclusively on another thread.
+  void DetachFromThread();
+
+ private:
+  void EnsureThreadIdAssigned() const;
+
+  mutable base::Lock lock_;
+  // This is mutable so that CalledOnValidThread can set it.
+  // It's guarded by |lock_|.
+  mutable PlatformThreadRef valid_thread_id_;
+};
+
+}  // namespace cef_internal
+}  // namespace base
+
+#endif  // CEF_INCLUDE_BASE_INTERNAL_THREAD_CHECKER_IMPL_H_
diff --git a/src/include/capi/cef_accessibility_handler_capi.h b/src/include/capi/cef_accessibility_handler_capi.h
new file mode 100644
index 0000000..3bb8363
--- /dev/null
+++ b/src/include/capi/cef_accessibility_handler_capi.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=00d5124d346e3f3cc3f53d67bcb766d1d798bf12$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_ACCESSIBILITY_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_ACCESSIBILITY_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to receive accessibility notification when
+// accessibility events have been registered. The functions of this structure
+// will be called on the UI thread.
+///
+typedef struct _cef_accessibility_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called after renderer process sends accessibility tree changes to the
+  // browser process.
+  ///
+  void(CEF_CALLBACK* on_accessibility_tree_change)(
+      struct _cef_accessibility_handler_t* self,
+      struct _cef_value_t* value);
+
+  ///
+  // Called after renderer process sends accessibility location changes to the
+  // browser process.
+  ///
+  void(CEF_CALLBACK* on_accessibility_location_change)(
+      struct _cef_accessibility_handler_t* self,
+      struct _cef_value_t* value);
+} cef_accessibility_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_ACCESSIBILITY_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_app_capi.h b/src/include/capi/cef_app_capi.h
new file mode 100644
index 0000000..033ebb0
--- /dev/null
+++ b/src/include/capi/cef_app_capi.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=04cfae434fe901644c1c78f1c30c0921518cc666$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_APP_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_APP_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_process_handler_capi.h"
+#include "include/capi/cef_command_line_capi.h"
+#include "include/capi/cef_render_process_handler_capi.h"
+#include "include/capi/cef_resource_bundle_handler_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_app_t;
+
+///
+// Implement this structure to provide handler implementations. Methods will be
+// called by the process and/or thread indicated.
+///
+typedef struct _cef_app_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Provides an opportunity to view and/or modify command-line arguments before
+  // processing by CEF and Chromium. The |process_type| value will be NULL for
+  // the browser process. Do not keep a reference to the cef_command_line_t
+  // object passed to this function. The CefSettings.command_line_args_disabled
+  // value can be used to start with an NULL command-line object. Any values
+  // specified in CefSettings that equate to command-line arguments will be set
+  // before this function is called. Be cautious when using this function to
+  // modify command-line arguments for non-browser processes as this may result
+  // in undefined behavior including crashes.
+  ///
+  void(CEF_CALLBACK* on_before_command_line_processing)(
+      struct _cef_app_t* self,
+      const cef_string_t* process_type,
+      struct _cef_command_line_t* command_line);
+
+  ///
+  // Provides an opportunity to register custom schemes. Do not keep a reference
+  // to the |registrar| object. This function is called on the main thread for
+  // each process and the registered schemes should be the same across all
+  // processes.
+  ///
+  void(CEF_CALLBACK* on_register_custom_schemes)(
+      struct _cef_app_t* self,
+      struct _cef_scheme_registrar_t* registrar);
+
+  ///
+  // Return the handler for resource bundle events. If
+  // CefSettings.pack_loading_disabled is true (1) a handler must be returned.
+  // If no handler is returned resources will be loaded from pack files. This
+  // function is called by the browser and render processes on multiple threads.
+  ///
+  struct _cef_resource_bundle_handler_t*(
+      CEF_CALLBACK* get_resource_bundle_handler)(struct _cef_app_t* self);
+
+  ///
+  // Return the handler for functionality specific to the browser process. This
+  // function is called on multiple threads in the browser process.
+  ///
+  struct _cef_browser_process_handler_t*(
+      CEF_CALLBACK* get_browser_process_handler)(struct _cef_app_t* self);
+
+  ///
+  // Return the handler for functionality specific to the render process. This
+  // function is called on the render process main thread.
+  ///
+  struct _cef_render_process_handler_t*(
+      CEF_CALLBACK* get_render_process_handler)(struct _cef_app_t* self);
+} cef_app_t;
+
+///
+// This function should be called from the application entry point function to
+// execute a secondary process. It can be used to run secondary processes from
+// the browser client executable (default behavior) or from a separate
+// executable specified by the CefSettings.browser_subprocess_path value. If
+// called for the browser process (identified by no "type" command-line value)
+// it will return immediately with a value of -1. If called for a recognized
+// secondary process it will block until the process should exit and then return
+// the process exit code. The |application| parameter may be NULL. The
+// |windows_sandbox_info| parameter is only used on Windows and may be NULL (see
+// cef_sandbox_win.h for details).
+///
+CEF_EXPORT int cef_execute_process(const struct _cef_main_args_t* args,
+                                   cef_app_t* application,
+                                   void* windows_sandbox_info);
+
+///
+// This function should be called on the main application thread to initialize
+// the CEF browser process. The |application| parameter may be NULL. A return
+// value of true (1) indicates that it succeeded and false (0) indicates that it
+// failed. The |windows_sandbox_info| parameter is only used on Windows and may
+// be NULL (see cef_sandbox_win.h for details).
+///
+CEF_EXPORT int cef_initialize(const struct _cef_main_args_t* args,
+                              const struct _cef_settings_t* settings,
+                              cef_app_t* application,
+                              void* windows_sandbox_info);
+
+///
+// This function should be called on the main application thread to shut down
+// the CEF browser process before the application exits.
+///
+CEF_EXPORT void cef_shutdown();
+
+///
+// Perform a single iteration of CEF message loop processing. This function is
+// provided for cases where the CEF message loop must be integrated into an
+// existing application message loop. Use of this function is not recommended
+// for most users; use either the cef_run_message_loop() function or
+// CefSettings.multi_threaded_message_loop if possible. When using this function
+// care must be taken to balance performance against excessive CPU usage. It is
+// recommended to enable the CefSettings.external_message_pump option when using
+// this function so that
+// cef_browser_process_handler_t::on_schedule_message_pump_work() callbacks can
+// facilitate the scheduling process. This function should only be called on the
+// main application thread and only if cef_initialize() is called with a
+// CefSettings.multi_threaded_message_loop value of false (0). This function
+// will not block.
+///
+CEF_EXPORT void cef_do_message_loop_work();
+
+///
+// Run the CEF message loop. Use this function instead of an application-
+// provided message loop to get the best balance between performance and CPU
+// usage. This function should only be called on the main application thread and
+// only if cef_initialize() is called with a
+// CefSettings.multi_threaded_message_loop value of false (0). This function
+// will block until a quit message is received by the system.
+///
+CEF_EXPORT void cef_run_message_loop();
+
+///
+// Quit the CEF message loop that was started by calling cef_run_message_loop().
+// This function should only be called on the main application thread and only
+// if cef_run_message_loop() was used.
+///
+CEF_EXPORT void cef_quit_message_loop();
+
+///
+// Set to true (1) before calling Windows APIs like TrackPopupMenu that enter a
+// modal message loop. Set to false (0) after exiting the modal message loop.
+///
+CEF_EXPORT void cef_set_osmodal_loop(int osModalLoop);
+
+///
+// Call during process startup to enable High-DPI support on Windows 7 or newer.
+// Older versions of Windows should be left DPI-unaware because they do not
+// support DirectWrite and GDI fonts are kerned very badly.
+///
+CEF_EXPORT void cef_enable_highdpi_support();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_APP_CAPI_H_
diff --git a/src/include/capi/cef_audio_handler_capi.h b/src/include/capi/cef_audio_handler_capi.h
new file mode 100644
index 0000000..1f6783b
--- /dev/null
+++ b/src/include/capi/cef_audio_handler_capi.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=430877d950508a545d0baa18c8c8c0d2d183fec4$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_AUDIO_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_AUDIO_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle audio events.
+///
+typedef struct _cef_audio_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the UI thread to allow configuration of audio stream parameters.
+  // Return true (1) to proceed with audio stream capture, or false (0) to
+  // cancel it. All members of |params| can optionally be configured here, but
+  // they are also pre-filled with some sensible defaults.
+  ///
+  int(CEF_CALLBACK* get_audio_parameters)(struct _cef_audio_handler_t* self,
+                                          struct _cef_browser_t* browser,
+                                          cef_audio_parameters_t* params);
+
+  ///
+  // Called on a browser audio capture thread when the browser starts streaming
+  // audio. OnAudioSteamStopped will always be called after
+  // OnAudioStreamStarted; both functions may be called multiple times for the
+  // same browser. |params| contains the audio parameters like sample rate and
+  // channel layout. |channels| is the number of channels.
+  ///
+  void(CEF_CALLBACK* on_audio_stream_started)(
+      struct _cef_audio_handler_t* self,
+      struct _cef_browser_t* browser,
+      const cef_audio_parameters_t* params,
+      int channels);
+
+  ///
+  // Called on the audio stream thread when a PCM packet is received for the
+  // stream. |data| is an array representing the raw PCM data as a floating
+  // point type, i.e. 4-byte value(s). |frames| is the number of frames in the
+  // PCM packet. |pts| is the presentation timestamp (in milliseconds since the
+  // Unix Epoch) and represents the time at which the decompressed packet should
+  // be presented to the user. Based on |frames| and the |channel_layout| value
+  // passed to OnAudioStreamStarted you can calculate the size of the |data|
+  // array in bytes.
+  ///
+  void(CEF_CALLBACK* on_audio_stream_packet)(struct _cef_audio_handler_t* self,
+                                             struct _cef_browser_t* browser,
+                                             const float** data,
+                                             int frames,
+                                             int64 pts);
+
+  ///
+  // Called on the UI thread when the stream has stopped. OnAudioSteamStopped
+  // will always be called after OnAudioStreamStarted; both functions may be
+  // called multiple times for the same stream.
+  ///
+  void(CEF_CALLBACK* on_audio_stream_stopped)(struct _cef_audio_handler_t* self,
+                                              struct _cef_browser_t* browser);
+
+  ///
+  // Called on the UI or audio stream thread when an error occurred. During the
+  // stream creation phase this callback will be called on the UI thread while
+  // in the capturing phase it will be called on the audio stream thread. The
+  // stream will be stopped immediately.
+  ///
+  void(CEF_CALLBACK* on_audio_stream_error)(struct _cef_audio_handler_t* self,
+                                            struct _cef_browser_t* browser,
+                                            const cef_string_t* message);
+} cef_audio_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_AUDIO_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_auth_callback_capi.h b/src/include/capi/cef_auth_callback_capi.h
new file mode 100644
index 0000000..5e7ea36
--- /dev/null
+++ b/src/include/capi/cef_auth_callback_capi.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=58be0e24b46373bbdad28031891396ea246f446c$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_AUTH_CALLBACK_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_AUTH_CALLBACK_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback structure used for asynchronous continuation of authentication
+// requests.
+///
+typedef struct _cef_auth_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Continue the authentication request.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_auth_callback_t* self,
+                           const cef_string_t* username,
+                           const cef_string_t* password);
+
+  ///
+  // Cancel the authentication request.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_auth_callback_t* self);
+} cef_auth_callback_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_AUTH_CALLBACK_CAPI_H_
diff --git a/src/include/capi/cef_base_capi.h b/src/include/capi/cef_base_capi.h
new file mode 100644
index 0000000..dbd0b9f
--- /dev/null
+++ b/src/include/capi/cef_base_capi.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_CAPI_CEF_BASE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_BASE_CAPI_H_
+
+#include <stdint.h>
+
+#include "include/internal/cef_export.h"
+#include "include/internal/cef_string.h"
+#include "include/internal/cef_string_list.h"
+#include "include/internal/cef_string_map.h"
+#include "include/internal/cef_string_multimap.h"
+#include "include/internal/cef_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// All ref-counted framework structures must include this structure first.
+///
+typedef struct _cef_base_ref_counted_t {
+  ///
+  // Size of the data structure.
+  ///
+  size_t size;
+
+  ///
+  // Called to increment the reference count for the object. Should be called
+  // for every new copy of a pointer to a given object.
+  ///
+  void(CEF_CALLBACK* add_ref)(struct _cef_base_ref_counted_t* self);
+
+  ///
+  // Called to decrement the reference count for the object. If the reference
+  // count falls to 0 the object should self-delete. Returns true (1) if the
+  // resulting reference count is 0.
+  ///
+  int(CEF_CALLBACK* release)(struct _cef_base_ref_counted_t* self);
+
+  ///
+  // Returns true (1) if the current reference count is 1.
+  ///
+  int(CEF_CALLBACK* has_one_ref)(struct _cef_base_ref_counted_t* self);
+
+  ///
+  // Returns true (1) if the current reference count is at least 1.
+  ///
+  int(CEF_CALLBACK* has_at_least_one_ref)(struct _cef_base_ref_counted_t* self);
+} cef_base_ref_counted_t;
+
+///
+// All scoped framework structures must include this structure first.
+///
+typedef struct _cef_base_scoped_t {
+  ///
+  // Size of the data structure.
+  ///
+  size_t size;
+
+  ///
+  // Called to delete this object. May be NULL if the object is not owned.
+  ///
+  void(CEF_CALLBACK* del)(struct _cef_base_scoped_t* self);
+
+} cef_base_scoped_t;
+
+// Check that the structure |s|, which is defined with a size_t member at the
+// top, is large enough to contain the specified member |f|.
+#define CEF_MEMBER_EXISTS(s, f) \
+  ((intptr_t) &                 \
+   ((s)->f) - (intptr_t)(s) + sizeof((s)->f) <= *reinterpret_cast<size_t*>(s))
+
+#define CEF_MEMBER_MISSING(s, f) (!CEF_MEMBER_EXISTS(s, f) || !((s)->f))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_BASE_CAPI_H_
diff --git a/src/include/capi/cef_browser_capi.h b/src/include/capi/cef_browser_capi.h
new file mode 100644
index 0000000..4336c8d
--- /dev/null
+++ b/src/include/capi/cef_browser_capi.h
@@ -0,0 +1,962 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=6cb00a0fa3631a46903abb3a783f315895511db2$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_devtools_message_observer_capi.h"
+#include "include/capi/cef_drag_data_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_image_capi.h"
+#include "include/capi/cef_navigation_entry_capi.h"
+#include "include/capi/cef_registration_capi.h"
+#include "include/capi/cef_request_context_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_browser_host_t;
+struct _cef_client_t;
+
+///
+// Structure used to represent a browser window. When used in the browser
+// process the functions of this structure may be called on any thread unless
+// otherwise indicated in the comments. When used in the render process the
+// functions of this structure may only be called on the main thread.
+///
+typedef struct _cef_browser_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the browser host object. This function can only be called in the
+  // browser process.
+  ///
+  struct _cef_browser_host_t*(CEF_CALLBACK* get_host)(
+      struct _cef_browser_t* self);
+
+  ///
+  // Returns true (1) if the browser can navigate backwards.
+  ///
+  int(CEF_CALLBACK* can_go_back)(struct _cef_browser_t* self);
+
+  ///
+  // Navigate backwards.
+  ///
+  void(CEF_CALLBACK* go_back)(struct _cef_browser_t* self);
+
+  ///
+  // Returns true (1) if the browser can navigate forwards.
+  ///
+  int(CEF_CALLBACK* can_go_forward)(struct _cef_browser_t* self);
+
+  ///
+  // Navigate forwards.
+  ///
+  void(CEF_CALLBACK* go_forward)(struct _cef_browser_t* self);
+
+  ///
+  // Returns true (1) if the browser is currently loading.
+  ///
+  int(CEF_CALLBACK* is_loading)(struct _cef_browser_t* self);
+
+  ///
+  // Reload the current page.
+  ///
+  void(CEF_CALLBACK* reload)(struct _cef_browser_t* self);
+
+  ///
+  // Reload the current page ignoring any cached data.
+  ///
+  void(CEF_CALLBACK* reload_ignore_cache)(struct _cef_browser_t* self);
+
+  ///
+  // Stop loading the page.
+  ///
+  void(CEF_CALLBACK* stop_load)(struct _cef_browser_t* self);
+
+  ///
+  // Returns the globally unique identifier for this browser. This value is also
+  // used as the tabId for extension APIs.
+  ///
+  int(CEF_CALLBACK* get_identifier)(struct _cef_browser_t* self);
+
+  ///
+  // Returns true (1) if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_browser_t* self,
+                             struct _cef_browser_t* that);
+
+  ///
+  // Returns true (1) if the window is a popup window.
+  ///
+  int(CEF_CALLBACK* is_popup)(struct _cef_browser_t* self);
+
+  ///
+  // Returns true (1) if a document has been loaded in the browser.
+  ///
+  int(CEF_CALLBACK* has_document)(struct _cef_browser_t* self);
+
+  ///
+  // Returns the main (top-level) frame for the browser window.
+  ///
+  struct _cef_frame_t*(CEF_CALLBACK* get_main_frame)(
+      struct _cef_browser_t* self);
+
+  ///
+  // Returns the focused frame for the browser window.
+  ///
+  struct _cef_frame_t*(CEF_CALLBACK* get_focused_frame)(
+      struct _cef_browser_t* self);
+
+  ///
+  // Returns the frame with the specified identifier, or NULL if not found.
+  ///
+  struct _cef_frame_t*(CEF_CALLBACK* get_frame_byident)(
+      struct _cef_browser_t* self,
+      int64 identifier);
+
+  ///
+  // Returns the frame with the specified name, or NULL if not found.
+  ///
+  struct _cef_frame_t*(CEF_CALLBACK* get_frame)(struct _cef_browser_t* self,
+                                                const cef_string_t* name);
+
+  ///
+  // Returns the number of frames that currently exist.
+  ///
+  size_t(CEF_CALLBACK* get_frame_count)(struct _cef_browser_t* self);
+
+  ///
+  // Returns the identifiers of all existing frames.
+  ///
+  void(CEF_CALLBACK* get_frame_identifiers)(struct _cef_browser_t* self,
+                                            size_t* identifiersCount,
+                                            int64* identifiers);
+
+  ///
+  // Returns the names of all existing frames.
+  ///
+  void(CEF_CALLBACK* get_frame_names)(struct _cef_browser_t* self,
+                                      cef_string_list_t names);
+} cef_browser_t;
+
+///
+// Callback structure for cef_browser_host_t::RunFileDialog. The functions of
+// this structure will be called on the browser process UI thread.
+///
+typedef struct _cef_run_file_dialog_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called asynchronously after the file dialog is dismissed.
+  // |selected_accept_filter| is the 0-based index of the value selected from
+  // the accept filters array passed to cef_browser_host_t::RunFileDialog.
+  // |file_paths| will be a single value or a list of values depending on the
+  // dialog mode. If the selection was cancelled |file_paths| will be NULL.
+  ///
+  void(CEF_CALLBACK* on_file_dialog_dismissed)(
+      struct _cef_run_file_dialog_callback_t* self,
+      int selected_accept_filter,
+      cef_string_list_t file_paths);
+} cef_run_file_dialog_callback_t;
+
+///
+// Callback structure for cef_browser_host_t::GetNavigationEntries. The
+// functions of this structure will be called on the browser process UI thread.
+///
+typedef struct _cef_navigation_entry_visitor_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be executed. Do not keep a reference to |entry| outside of
+  // this callback. Return true (1) to continue visiting entries or false (0) to
+  // stop. |current| is true (1) if this entry is the currently loaded
+  // navigation entry. |index| is the 0-based index of this entry and |total| is
+  // the total number of entries.
+  ///
+  int(CEF_CALLBACK* visit)(struct _cef_navigation_entry_visitor_t* self,
+                           struct _cef_navigation_entry_t* entry,
+                           int current,
+                           int index,
+                           int total);
+} cef_navigation_entry_visitor_t;
+
+///
+// Callback structure for cef_browser_host_t::PrintToPDF. The functions of this
+// structure will be called on the browser process UI thread.
+///
+typedef struct _cef_pdf_print_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be executed when the PDF printing has completed. |path| is
+  // the output path. |ok| will be true (1) if the printing completed
+  // successfully or false (0) otherwise.
+  ///
+  void(CEF_CALLBACK* on_pdf_print_finished)(
+      struct _cef_pdf_print_callback_t* self,
+      const cef_string_t* path,
+      int ok);
+} cef_pdf_print_callback_t;
+
+///
+// Callback structure for cef_browser_host_t::DownloadImage. The functions of
+// this structure will be called on the browser process UI thread.
+///
+typedef struct _cef_download_image_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be executed when the image download has completed.
+  // |image_url| is the URL that was downloaded and |http_status_code| is the
+  // resulting HTTP status code. |image| is the resulting image, possibly at
+  // multiple scale factors, or NULL if the download failed.
+  ///
+  void(CEF_CALLBACK* on_download_image_finished)(
+      struct _cef_download_image_callback_t* self,
+      const cef_string_t* image_url,
+      int http_status_code,
+      struct _cef_image_t* image);
+} cef_download_image_callback_t;
+
+///
+// Structure used to represent the browser process aspects of a browser window.
+// The functions of this structure can only be called in the browser process.
+// They may be called on any thread in that process unless otherwise indicated
+// in the comments.
+///
+typedef struct _cef_browser_host_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the hosted browser object.
+  ///
+  struct _cef_browser_t*(CEF_CALLBACK* get_browser)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Request that the browser close. The JavaScript 'onbeforeunload' event will
+  // be fired. If |force_close| is false (0) the event handler, if any, will be
+  // allowed to prompt the user and the user can optionally cancel the close. If
+  // |force_close| is true (1) the prompt will not be displayed and the close
+  // will proceed. Results in a call to cef_life_span_handler_t::do_close() if
+  // the event handler allows the close or if |force_close| is true (1). See
+  // cef_life_span_handler_t::do_close() documentation for additional usage
+  // information.
+  ///
+  void(CEF_CALLBACK* close_browser)(struct _cef_browser_host_t* self,
+                                    int force_close);
+
+  ///
+  // Helper for closing a browser. Call this function from the top-level window
+  // close handler. Internally this calls CloseBrowser(false (0)) if the close
+  // has not yet been initiated. This function returns false (0) while the close
+  // is pending and true (1) after the close has completed. See close_browser()
+  // and cef_life_span_handler_t::do_close() documentation for additional usage
+  // information. This function must be called on the browser process UI thread.
+  ///
+  int(CEF_CALLBACK* try_close_browser)(struct _cef_browser_host_t* self);
+
+  ///
+  // Set whether the browser is focused.
+  ///
+  void(CEF_CALLBACK* set_focus)(struct _cef_browser_host_t* self, int focus);
+
+  ///
+  // Retrieve the window handle for this browser. If this browser is wrapped in
+  // a cef_browser_view_t this function should be called on the browser process
+  // UI thread and it will return the handle for the top-level native window.
+  ///
+  cef_window_handle_t(CEF_CALLBACK* get_window_handle)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Retrieve the window handle of the browser that opened this browser. Will
+  // return NULL for non-popup windows or if this browser is wrapped in a
+  // cef_browser_view_t. This function can be used in combination with custom
+  // handling of modal windows.
+  ///
+  cef_window_handle_t(CEF_CALLBACK* get_opener_window_handle)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Returns true (1) if this browser is wrapped in a cef_browser_view_t.
+  ///
+  int(CEF_CALLBACK* has_view)(struct _cef_browser_host_t* self);
+
+  ///
+  // Returns the client for this browser.
+  ///
+  struct _cef_client_t*(CEF_CALLBACK* get_client)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Returns the request context for this browser.
+  ///
+  struct _cef_request_context_t*(CEF_CALLBACK* get_request_context)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Get the current zoom level. The default zoom level is 0.0. This function
+  // can only be called on the UI thread.
+  ///
+  double(CEF_CALLBACK* get_zoom_level)(struct _cef_browser_host_t* self);
+
+  ///
+  // Change the zoom level to the specified value. Specify 0.0 to reset the zoom
+  // level. If called on the UI thread the change will be applied immediately.
+  // Otherwise, the change will be applied asynchronously on the UI thread.
+  ///
+  void(CEF_CALLBACK* set_zoom_level)(struct _cef_browser_host_t* self,
+                                     double zoomLevel);
+
+  ///
+  // Call to run a file chooser dialog. Only a single file chooser dialog may be
+  // pending at any given time. |mode| represents the type of dialog to display.
+  // |title| to the title to be used for the dialog and may be NULL to show the
+  // default title ("Open" or "Save" depending on the mode). |default_file_path|
+  // is the path with optional directory and/or file name component that will be
+  // initially selected in the dialog. |accept_filters| are used to restrict the
+  // selectable file types and may any combination of (a) valid lower-cased MIME
+  // types (e.g. "text/*" or "image/*"), (b) individual file extensions (e.g.
+  // ".txt" or ".png"), or (c) combined description and file extension delimited
+  // using "|" and ";" (e.g. "Image Types|.png;.gif;.jpg").
+  // |selected_accept_filter| is the 0-based index of the filter that will be
+  // selected by default. |callback| will be executed after the dialog is
+  // dismissed or immediately if another dialog is already pending. The dialog
+  // will be initiated asynchronously on the UI thread.
+  ///
+  void(CEF_CALLBACK* run_file_dialog)(
+      struct _cef_browser_host_t* self,
+      cef_file_dialog_mode_t mode,
+      const cef_string_t* title,
+      const cef_string_t* default_file_path,
+      cef_string_list_t accept_filters,
+      int selected_accept_filter,
+      struct _cef_run_file_dialog_callback_t* callback);
+
+  ///
+  // Download the file at |url| using cef_download_handler_t.
+  ///
+  void(CEF_CALLBACK* start_download)(struct _cef_browser_host_t* self,
+                                     const cef_string_t* url);
+
+  ///
+  // Download |image_url| and execute |callback| on completion with the images
+  // received from the renderer. If |is_favicon| is true (1) then cookies are
+  // not sent and not accepted during download. Images with density independent
+  // pixel (DIP) sizes larger than |max_image_size| are filtered out from the
+  // image results. Versions of the image at different scale factors may be
+  // downloaded up to the maximum scale factor supported by the system. If there
+  // are no image results <= |max_image_size| then the smallest image is resized
+  // to |max_image_size| and is the only result. A |max_image_size| of 0 means
+  // unlimited. If |bypass_cache| is true (1) then |image_url| is requested from
+  // the server even if it is present in the browser cache.
+  ///
+  void(CEF_CALLBACK* download_image)(
+      struct _cef_browser_host_t* self,
+      const cef_string_t* image_url,
+      int is_favicon,
+      uint32 max_image_size,
+      int bypass_cache,
+      struct _cef_download_image_callback_t* callback);
+
+  ///
+  // Print the current browser contents.
+  ///
+  void(CEF_CALLBACK* print)(struct _cef_browser_host_t* self);
+
+  ///
+  // Print the current browser contents to the PDF file specified by |path| and
+  // execute |callback| on completion. The caller is responsible for deleting
+  // |path| when done. For PDF printing to work on Linux you must implement the
+  // cef_print_handler_t::GetPdfPaperSize function.
+  ///
+  void(CEF_CALLBACK* print_to_pdf)(
+      struct _cef_browser_host_t* self,
+      const cef_string_t* path,
+      const struct _cef_pdf_print_settings_t* settings,
+      struct _cef_pdf_print_callback_t* callback);
+
+  ///
+  // Search for |searchText|. |identifier| must be a unique ID and these IDs
+  // must strictly increase so that newer requests always have greater IDs than
+  // older requests. If |identifier| is zero or less than the previous ID value
+  // then it will be automatically assigned a new valid ID. |forward| indicates
+  // whether to search forward or backward within the page. |matchCase|
+  // indicates whether the search should be case-sensitive. |findNext| indicates
+  // whether this is the first request or a follow-up. The cef_find_handler_t
+  // instance, if any, returned via cef_client_t::GetFindHandler will be called
+  // to report find results.
+  ///
+  void(CEF_CALLBACK* find)(struct _cef_browser_host_t* self,
+                           int identifier,
+                           const cef_string_t* searchText,
+                           int forward,
+                           int matchCase,
+                           int findNext);
+
+  ///
+  // Cancel all searches that are currently going on.
+  ///
+  void(CEF_CALLBACK* stop_finding)(struct _cef_browser_host_t* self,
+                                   int clearSelection);
+
+  ///
+  // Open developer tools (DevTools) in its own browser. The DevTools browser
+  // will remain associated with this browser. If the DevTools browser is
+  // already open then it will be focused, in which case the |windowInfo|,
+  // |client| and |settings| parameters will be ignored. If |inspect_element_at|
+  // is non-NULL then the element at the specified (x,y) location will be
+  // inspected. The |windowInfo| parameter will be ignored if this browser is
+  // wrapped in a cef_browser_view_t.
+  ///
+  void(CEF_CALLBACK* show_dev_tools)(
+      struct _cef_browser_host_t* self,
+      const struct _cef_window_info_t* windowInfo,
+      struct _cef_client_t* client,
+      const struct _cef_browser_settings_t* settings,
+      const cef_point_t* inspect_element_at);
+
+  ///
+  // Explicitly close the associated DevTools browser, if any.
+  ///
+  void(CEF_CALLBACK* close_dev_tools)(struct _cef_browser_host_t* self);
+
+  ///
+  // Returns true (1) if this browser currently has an associated DevTools
+  // browser. Must be called on the browser process UI thread.
+  ///
+  int(CEF_CALLBACK* has_dev_tools)(struct _cef_browser_host_t* self);
+
+  ///
+  // Send a function call message over the DevTools protocol. |message| must be
+  // a UTF8-encoded JSON dictionary that contains "id" (int), "function"
+  // (string) and "params" (dictionary, optional) values. See the DevTools
+  // protocol documentation at https://chromedevtools.github.io/devtools-
+  // protocol/ for details of supported functions and the expected "params"
+  // dictionary contents. |message| will be copied if necessary. This function
+  // will return true (1) if called on the UI thread and the message was
+  // successfully submitted for validation, otherwise false (0). Validation will
+  // be applied asynchronously and any messages that fail due to formatting
+  // errors or missing parameters may be discarded without notification. Prefer
+  // ExecuteDevToolsMethod if a more structured approach to message formatting
+  // is desired.
+  //
+  // Every valid function call will result in an asynchronous function result or
+  // error message that references the sent message "id". Event messages are
+  // received while notifications are enabled (for example, between function
+  // calls for "Page.enable" and "Page.disable"). All received messages will be
+  // delivered to the observer(s) registered with AddDevToolsMessageObserver.
+  // See cef_dev_tools_message_observer_t::OnDevToolsMessage documentation for
+  // details of received message contents.
+  //
+  // Usage of the SendDevToolsMessage, ExecuteDevToolsMethod and
+  // AddDevToolsMessageObserver functions does not require an active DevTools
+  // front-end or remote-debugging session. Other active DevTools sessions will
+  // continue to function independently. However, any modification of global
+  // browser state by one session may not be reflected in the UI of other
+  // sessions.
+  //
+  // Communication with the DevTools front-end (when displayed) can be logged
+  // for development purposes by passing the `--devtools-protocol-log-
+  // file=<path>` command-line flag.
+  ///
+  int(CEF_CALLBACK* send_dev_tools_message)(struct _cef_browser_host_t* self,
+                                            const void* message,
+                                            size_t message_size);
+
+  ///
+  // Execute a function call over the DevTools protocol. This is a more
+  // structured version of SendDevToolsMessage. |message_id| is an incremental
+  // number that uniquely identifies the message (pass 0 to have the next number
+  // assigned automatically based on previous values). |function| is the
+  // function name. |params| are the function parameters, which may be NULL. See
+  // the DevTools protocol documentation (linked above) for details of supported
+  // functions and the expected |params| dictionary contents. This function will
+  // return the assigned message ID if called on the UI thread and the message
+  // was successfully submitted for validation, otherwise 0. See the
+  // SendDevToolsMessage documentation for additional usage information.
+  ///
+  int(CEF_CALLBACK* execute_dev_tools_method)(
+      struct _cef_browser_host_t* self,
+      int message_id,
+      const cef_string_t* method,
+      struct _cef_dictionary_value_t* params);
+
+  ///
+  // Add an observer for DevTools protocol messages (function results and
+  // events). The observer will remain registered until the returned
+  // Registration object is destroyed. See the SendDevToolsMessage documentation
+  // for additional usage information.
+  ///
+  struct _cef_registration_t*(CEF_CALLBACK* add_dev_tools_message_observer)(
+      struct _cef_browser_host_t* self,
+      struct _cef_dev_tools_message_observer_t* observer);
+
+  ///
+  // Retrieve a snapshot of current navigation entries as values sent to the
+  // specified visitor. If |current_only| is true (1) only the current
+  // navigation entry will be sent, otherwise all navigation entries will be
+  // sent.
+  ///
+  void(CEF_CALLBACK* get_navigation_entries)(
+      struct _cef_browser_host_t* self,
+      struct _cef_navigation_entry_visitor_t* visitor,
+      int current_only);
+
+  ///
+  // Set whether mouse cursor change is disabled.
+  ///
+  void(CEF_CALLBACK* set_mouse_cursor_change_disabled)(
+      struct _cef_browser_host_t* self,
+      int disabled);
+
+  ///
+  // Returns true (1) if mouse cursor change is disabled.
+  ///
+  int(CEF_CALLBACK* is_mouse_cursor_change_disabled)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // If a misspelled word is currently selected in an editable node calling this
+  // function will replace it with the specified |word|.
+  ///
+  void(CEF_CALLBACK* replace_misspelling)(struct _cef_browser_host_t* self,
+                                          const cef_string_t* word);
+
+  ///
+  // Add the specified |word| to the spelling dictionary.
+  ///
+  void(CEF_CALLBACK* add_word_to_dictionary)(struct _cef_browser_host_t* self,
+                                             const cef_string_t* word);
+
+  ///
+  // Returns true (1) if window rendering is disabled.
+  ///
+  int(CEF_CALLBACK* is_window_rendering_disabled)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Notify the browser that the widget has been resized. The browser will first
+  // call cef_render_handler_t::GetViewRect to get the new size and then call
+  // cef_render_handler_t::OnPaint asynchronously with the updated regions. This
+  // function is only used when window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* was_resized)(struct _cef_browser_host_t* self);
+
+  ///
+  // Notify the browser that it has been hidden or shown. Layouting and
+  // cef_render_handler_t::OnPaint notification will stop when the browser is
+  // hidden. This function is only used when window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* was_hidden)(struct _cef_browser_host_t* self, int hidden);
+
+  ///
+  // Send a notification to the browser that the screen info has changed. The
+  // browser will then call cef_render_handler_t::GetScreenInfo to update the
+  // screen information with the new values. This simulates moving the webview
+  // window from one display to another, or changing the properties of the
+  // current display. This function is only used when window rendering is
+  // disabled.
+  ///
+  void(CEF_CALLBACK* notify_screen_info_changed)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Invalidate the view. The browser will call cef_render_handler_t::OnPaint
+  // asynchronously. This function is only used when window rendering is
+  // disabled.
+  ///
+  void(CEF_CALLBACK* invalidate)(struct _cef_browser_host_t* self,
+                                 cef_paint_element_type_t type);
+
+  ///
+  // Issue a BeginFrame request to Chromium.  Only valid when
+  // cef_window_tInfo::external_begin_frame_enabled is set to true (1).
+  ///
+  void(CEF_CALLBACK* send_external_begin_frame)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Send a key event to the browser.
+  ///
+  void(CEF_CALLBACK* send_key_event)(struct _cef_browser_host_t* self,
+                                     const struct _cef_key_event_t* event);
+
+  ///
+  // Send a mouse click event to the browser. The |x| and |y| coordinates are
+  // relative to the upper-left corner of the view.
+  ///
+  void(CEF_CALLBACK* send_mouse_click_event)(
+      struct _cef_browser_host_t* self,
+      const struct _cef_mouse_event_t* event,
+      cef_mouse_button_type_t type,
+      int mouseUp,
+      int clickCount);
+
+  ///
+  // Send a mouse move event to the browser. The |x| and |y| coordinates are
+  // relative to the upper-left corner of the view.
+  ///
+  void(CEF_CALLBACK* send_mouse_move_event)(
+      struct _cef_browser_host_t* self,
+      const struct _cef_mouse_event_t* event,
+      int mouseLeave);
+
+  ///
+  // Send a mouse wheel event to the browser. The |x| and |y| coordinates are
+  // relative to the upper-left corner of the view. The |deltaX| and |deltaY|
+  // values represent the movement delta in the X and Y directions respectively.
+  // In order to scroll inside select popups with window rendering disabled
+  // cef_render_handler_t::GetScreenPoint should be implemented properly.
+  ///
+  void(CEF_CALLBACK* send_mouse_wheel_event)(
+      struct _cef_browser_host_t* self,
+      const struct _cef_mouse_event_t* event,
+      int deltaX,
+      int deltaY);
+
+  ///
+  // Send a touch event to the browser for a windowless browser.
+  ///
+  void(CEF_CALLBACK* send_touch_event)(struct _cef_browser_host_t* self,
+                                       const struct _cef_touch_event_t* event);
+
+  ///
+  // Send a focus event to the browser.
+  ///
+  void(CEF_CALLBACK* send_focus_event)(struct _cef_browser_host_t* self,
+                                       int setFocus);
+
+  ///
+  // Send a capture lost event to the browser.
+  ///
+  void(CEF_CALLBACK* send_capture_lost_event)(struct _cef_browser_host_t* self);
+
+  ///
+  // Notify the browser that the window hosting it is about to be moved or
+  // resized. This function is only used on Windows and Linux.
+  ///
+  void(CEF_CALLBACK* notify_move_or_resize_started)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Returns the maximum rate in frames per second (fps) that
+  // cef_render_handler_t:: OnPaint will be called for a windowless browser. The
+  // actual fps may be lower if the browser cannot generate frames at the
+  // requested rate. The minimum value is 1 and the maximum value is 60 (default
+  // 30). This function can only be called on the UI thread.
+  ///
+  int(CEF_CALLBACK* get_windowless_frame_rate)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Set the maximum rate in frames per second (fps) that cef_render_handler_t::
+  // OnPaint will be called for a windowless browser. The actual fps may be
+  // lower if the browser cannot generate frames at the requested rate. The
+  // minimum value is 1 and the maximum value is 60 (default 30). Can also be
+  // set at browser creation via cef_browser_tSettings.windowless_frame_rate.
+  ///
+  void(CEF_CALLBACK* set_windowless_frame_rate)(
+      struct _cef_browser_host_t* self,
+      int frame_rate);
+
+  ///
+  // Begins a new composition or updates the existing composition. Blink has a
+  // special node (a composition node) that allows the input function to change
+  // text without affecting other DOM nodes. |text| is the optional text that
+  // will be inserted into the composition node. |underlines| is an optional set
+  // of ranges that will be underlined in the resulting text.
+  // |replacement_range| is an optional range of the existing text that will be
+  // replaced. |selection_range| is an optional range of the resulting text that
+  // will be selected after insertion or replacement. The |replacement_range|
+  // value is only used on OS X.
+  //
+  // This function may be called multiple times as the composition changes. When
+  // the client is done making changes the composition should either be canceled
+  // or completed. To cancel the composition call ImeCancelComposition. To
+  // complete the composition call either ImeCommitText or
+  // ImeFinishComposingText. Completion is usually signaled when:
+  //   A. The client receives a WM_IME_COMPOSITION message with a GCS_RESULTSTR
+  //      flag (on Windows), or;
+  //   B. The client receives a "commit" signal of GtkIMContext (on Linux), or;
+  //   C. insertText of NSTextInput is called (on Mac).
+  //
+  // This function is only used when window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* ime_set_composition)(
+      struct _cef_browser_host_t* self,
+      const cef_string_t* text,
+      size_t underlinesCount,
+      cef_composition_underline_t const* underlines,
+      const cef_range_t* replacement_range,
+      const cef_range_t* selection_range);
+
+  ///
+  // Completes the existing composition by optionally inserting the specified
+  // |text| into the composition node. |replacement_range| is an optional range
+  // of the existing text that will be replaced. |relative_cursor_pos| is where
+  // the cursor will be positioned relative to the current cursor position. See
+  // comments on ImeSetComposition for usage. The |replacement_range| and
+  // |relative_cursor_pos| values are only used on OS X. This function is only
+  // used when window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* ime_commit_text)(struct _cef_browser_host_t* self,
+                                      const cef_string_t* text,
+                                      const cef_range_t* replacement_range,
+                                      int relative_cursor_pos);
+
+  ///
+  // Completes the existing composition by applying the current composition node
+  // contents. If |keep_selection| is false (0) the current selection, if any,
+  // will be discarded. See comments on ImeSetComposition for usage. This
+  // function is only used when window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* ime_finish_composing_text)(
+      struct _cef_browser_host_t* self,
+      int keep_selection);
+
+  ///
+  // Cancels the existing composition and discards the composition node contents
+  // without applying them. See comments on ImeSetComposition for usage. This
+  // function is only used when window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* ime_cancel_composition)(struct _cef_browser_host_t* self);
+
+  ///
+  // Call this function when the user drags the mouse into the web view (before
+  // calling DragTargetDragOver/DragTargetLeave/DragTargetDrop). |drag_data|
+  // should not contain file contents as this type of data is not allowed to be
+  // dragged into the web view. File contents can be removed using
+  // cef_drag_data_t::ResetFileContents (for example, if |drag_data| comes from
+  // cef_render_handler_t::StartDragging). This function is only used when
+  // window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* drag_target_drag_enter)(
+      struct _cef_browser_host_t* self,
+      struct _cef_drag_data_t* drag_data,
+      const struct _cef_mouse_event_t* event,
+      cef_drag_operations_mask_t allowed_ops);
+
+  ///
+  // Call this function each time the mouse is moved across the web view during
+  // a drag operation (after calling DragTargetDragEnter and before calling
+  // DragTargetDragLeave/DragTargetDrop). This function is only used when window
+  // rendering is disabled.
+  ///
+  void(CEF_CALLBACK* drag_target_drag_over)(
+      struct _cef_browser_host_t* self,
+      const struct _cef_mouse_event_t* event,
+      cef_drag_operations_mask_t allowed_ops);
+
+  ///
+  // Call this function when the user drags the mouse out of the web view (after
+  // calling DragTargetDragEnter). This function is only used when window
+  // rendering is disabled.
+  ///
+  void(CEF_CALLBACK* drag_target_drag_leave)(struct _cef_browser_host_t* self);
+
+  ///
+  // Call this function when the user completes the drag operation by dropping
+  // the object onto the web view (after calling DragTargetDragEnter). The
+  // object being dropped is |drag_data|, given as an argument to the previous
+  // DragTargetDragEnter call. This function is only used when window rendering
+  // is disabled.
+  ///
+  void(CEF_CALLBACK* drag_target_drop)(struct _cef_browser_host_t* self,
+                                       const struct _cef_mouse_event_t* event);
+
+  ///
+  // Call this function when the drag operation started by a
+  // cef_render_handler_t::StartDragging call has ended either in a drop or by
+  // being cancelled. |x| and |y| are mouse coordinates relative to the upper-
+  // left corner of the view. If the web view is both the drag source and the
+  // drag target then all DragTarget* functions should be called before
+  // DragSource* mthods. This function is only used when window rendering is
+  // disabled.
+  ///
+  void(CEF_CALLBACK* drag_source_ended_at)(struct _cef_browser_host_t* self,
+                                           int x,
+                                           int y,
+                                           cef_drag_operations_mask_t op);
+
+  ///
+  // Call this function when the drag operation started by a
+  // cef_render_handler_t::StartDragging call has completed. This function may
+  // be called immediately without first calling DragSourceEndedAt to cancel a
+  // drag operation. If the web view is both the drag source and the drag target
+  // then all DragTarget* functions should be called before DragSource* mthods.
+  // This function is only used when window rendering is disabled.
+  ///
+  void(CEF_CALLBACK* drag_source_system_drag_ended)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Returns the current visible navigation entry for this browser. This
+  // function can only be called on the UI thread.
+  ///
+  struct _cef_navigation_entry_t*(CEF_CALLBACK* get_visible_navigation_entry)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Set accessibility state for all frames. |accessibility_state| may be
+  // default, enabled or disabled. If |accessibility_state| is STATE_DEFAULT
+  // then accessibility will be disabled by default and the state may be further
+  // controlled with the "force-renderer-accessibility" and "disable-renderer-
+  // accessibility" command-line switches. If |accessibility_state| is
+  // STATE_ENABLED then accessibility will be enabled. If |accessibility_state|
+  // is STATE_DISABLED then accessibility will be completely disabled.
+  //
+  // For windowed browsers accessibility will be enabled in Complete mode (which
+  // corresponds to kAccessibilityModeComplete in Chromium). In this mode all
+  // platform accessibility objects will be created and managed by Chromium's
+  // internal implementation. The client needs only to detect the screen reader
+  // and call this function appropriately. For example, on macOS the client can
+  // handle the @"AXEnhancedUserStructure" accessibility attribute to detect
+  // VoiceOver state changes and on Windows the client can handle WM_GETOBJECT
+  // with OBJID_CLIENT to detect accessibility readers.
+  //
+  // For windowless browsers accessibility will be enabled in TreeOnly mode
+  // (which corresponds to kAccessibilityModeWebContentsOnly in Chromium). In
+  // this mode renderer accessibility is enabled, the full tree is computed, and
+  // events are passed to CefAccessibiltyHandler, but platform accessibility
+  // objects are not created. The client may implement platform accessibility
+  // objects using CefAccessibiltyHandler callbacks if desired.
+  ///
+  void(CEF_CALLBACK* set_accessibility_state)(struct _cef_browser_host_t* self,
+                                              cef_state_t accessibility_state);
+
+  ///
+  // Enable notifications of auto resize via
+  // cef_display_handler_t::OnAutoResize. Notifications are disabled by default.
+  // |min_size| and |max_size| define the range of allowed sizes.
+  ///
+  void(CEF_CALLBACK* set_auto_resize_enabled)(struct _cef_browser_host_t* self,
+                                              int enabled,
+                                              const cef_size_t* min_size,
+                                              const cef_size_t* max_size);
+
+  ///
+  // Returns the extension hosted in this browser or NULL if no extension is
+  // hosted. See cef_request_context_t::LoadExtension for details.
+  ///
+  struct _cef_extension_t*(CEF_CALLBACK* get_extension)(
+      struct _cef_browser_host_t* self);
+
+  ///
+  // Returns true (1) if this browser is hosting an extension background script.
+  // Background hosts do not have a window and are not displayable. See
+  // cef_request_context_t::LoadExtension for details.
+  ///
+  int(CEF_CALLBACK* is_background_host)(struct _cef_browser_host_t* self);
+
+  ///
+  //  Set whether the browser's audio is muted.
+  ///
+  void(CEF_CALLBACK* set_audio_muted)(struct _cef_browser_host_t* self,
+                                      int mute);
+
+  ///
+  // Returns true (1) if the browser's audio is muted.  This function can only
+  // be called on the UI thread.
+  ///
+  int(CEF_CALLBACK* is_audio_muted)(struct _cef_browser_host_t* self);
+} cef_browser_host_t;
+
+///
+// Create a new browser window using the window parameters specified by
+// |windowInfo|. All values will be copied internally and the actual window will
+// be created on the UI thread. If |request_context| is NULL the global request
+// context will be used. This function can be called on any browser process
+// thread and will not block. The optional |extra_info| parameter provides an
+// opportunity to specify extra information specific to the created browser that
+// will be passed to cef_render_process_handler_t::on_browser_created() in the
+// render process.
+///
+CEF_EXPORT int cef_browser_host_create_browser(
+    const cef_window_info_t* windowInfo,
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context);
+
+///
+// Create a new browser window using the window parameters specified by
+// |windowInfo|. If |request_context| is NULL the global request context will be
+// used. This function can only be called on the browser process UI thread. The
+// optional |extra_info| parameter provides an opportunity to specify extra
+// information specific to the created browser that will be passed to
+// cef_render_process_handler_t::on_browser_created() in the render process.
+///
+CEF_EXPORT cef_browser_t* cef_browser_host_create_browser_sync(
+    const cef_window_info_t* windowInfo,
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_
diff --git a/src/include/capi/cef_browser_process_handler_capi.h b/src/include/capi/cef_browser_process_handler_capi.h
new file mode 100644
index 0000000..2d9ee46
--- /dev/null
+++ b/src/include/capi/cef_browser_process_handler_capi.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=b15ba2c750f5227b6b40fea59965817ba4431ee0$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_PROCESS_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_BROWSER_PROCESS_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_command_line_capi.h"
+#include "include/capi/cef_print_handler_capi.h"
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to implement browser process callbacks. The functions of this
+// structure will be called on the browser process main thread unless otherwise
+// indicated.
+///
+typedef struct _cef_browser_process_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the browser process UI thread immediately after the CEF context
+  // has been initialized.
+  ///
+  void(CEF_CALLBACK* on_context_initialized)(
+      struct _cef_browser_process_handler_t* self);
+
+  ///
+  // Called before a child process is launched. Will be called on the browser
+  // process UI thread when launching a render process and on the browser
+  // process IO thread when launching a GPU or plugin process. Provides an
+  // opportunity to modify the child process command line. Do not keep a
+  // reference to |command_line| outside of this function.
+  ///
+  void(CEF_CALLBACK* on_before_child_process_launch)(
+      struct _cef_browser_process_handler_t* self,
+      struct _cef_command_line_t* command_line);
+
+  ///
+  // Called on the browser process IO thread after the main thread has been
+  // created for a new render process. Provides an opportunity to specify extra
+  // information that will be passed to
+  // cef_render_process_handler_t::on_render_thread_created() in the render
+  // process. Do not keep a reference to |extra_info| outside of this function.
+  ///
+  void(CEF_CALLBACK* on_render_process_thread_created)(
+      struct _cef_browser_process_handler_t* self,
+      struct _cef_list_value_t* extra_info);
+
+  ///
+  // Return the handler for printing on Linux. If a print handler is not
+  // provided then printing will not be supported on the Linux platform.
+  ///
+  struct _cef_print_handler_t*(CEF_CALLBACK* get_print_handler)(
+      struct _cef_browser_process_handler_t* self);
+
+  ///
+  // Called from any thread when work has been scheduled for the browser process
+  // main (UI) thread. This callback is used in combination with CefSettings.
+  // external_message_pump and cef_do_message_loop_work() in cases where the CEF
+  // message loop must be integrated into an existing application message loop
+  // (see additional comments and warnings on CefDoMessageLoopWork). This
+  // callback should schedule a cef_do_message_loop_work() call to happen on the
+  // main (UI) thread. |delay_ms| is the requested delay in milliseconds. If
+  // |delay_ms| is <= 0 then the call should happen reasonably soon. If
+  // |delay_ms| is > 0 then the call should be scheduled to happen after the
+  // specified delay and any currently pending scheduled call should be
+  // cancelled.
+  ///
+  void(CEF_CALLBACK* on_schedule_message_pump_work)(
+      struct _cef_browser_process_handler_t* self,
+      int64 delay_ms);
+} cef_browser_process_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_BROWSER_PROCESS_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_callback_capi.h b/src/include/capi/cef_callback_capi.h
new file mode 100644
index 0000000..02b41ee
--- /dev/null
+++ b/src/include/capi/cef_callback_capi.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=5c540e617cf2782876defad365e85cd43932ffce$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_CALLBACK_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_CALLBACK_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Generic callback structure used for asynchronous continuation.
+///
+typedef struct _cef_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Continue processing.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_callback_t* self);
+
+  ///
+  // Cancel processing.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_callback_t* self);
+} cef_callback_t;
+
+///
+// Generic callback structure used for asynchronous completion.
+///
+typedef struct _cef_completion_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called once the task is complete.
+  ///
+  void(CEF_CALLBACK* on_complete)(struct _cef_completion_callback_t* self);
+} cef_completion_callback_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_CALLBACK_CAPI_H_
diff --git a/src/include/capi/cef_client_capi.h b/src/include/capi/cef_client_capi.h
new file mode 100644
index 0000000..a7eb509
--- /dev/null
+++ b/src/include/capi/cef_client_capi.h
@@ -0,0 +1,178 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=8d4cb3e0bbf230804c93898daa4a8b2866a2c1ce$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_CLIENT_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_CLIENT_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_audio_handler_capi.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_context_menu_handler_capi.h"
+#include "include/capi/cef_dialog_handler_capi.h"
+#include "include/capi/cef_display_handler_capi.h"
+#include "include/capi/cef_download_handler_capi.h"
+#include "include/capi/cef_drag_handler_capi.h"
+#include "include/capi/cef_find_handler_capi.h"
+#include "include/capi/cef_focus_handler_capi.h"
+#include "include/capi/cef_jsdialog_handler_capi.h"
+#include "include/capi/cef_keyboard_handler_capi.h"
+#include "include/capi/cef_life_span_handler_capi.h"
+#include "include/capi/cef_load_handler_capi.h"
+#include "include/capi/cef_process_message_capi.h"
+#include "include/capi/cef_render_handler_capi.h"
+#include "include/capi/cef_request_handler_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to provide handler implementations.
+///
+typedef struct _cef_client_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Return the handler for audio rendering events.
+  ///
+  struct _cef_audio_handler_t*(CEF_CALLBACK* get_audio_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for context menus. If no handler is provided the default
+  // implementation will be used.
+  ///
+  struct _cef_context_menu_handler_t*(CEF_CALLBACK* get_context_menu_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for dialogs. If no handler is provided the default
+  // implementation will be used.
+  ///
+  struct _cef_dialog_handler_t*(CEF_CALLBACK* get_dialog_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for browser display state events.
+  ///
+  struct _cef_display_handler_t*(CEF_CALLBACK* get_display_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for download events. If no handler is returned downloads
+  // will not be allowed.
+  ///
+  struct _cef_download_handler_t*(CEF_CALLBACK* get_download_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for drag events.
+  ///
+  struct _cef_drag_handler_t*(CEF_CALLBACK* get_drag_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for find result events.
+  ///
+  struct _cef_find_handler_t*(CEF_CALLBACK* get_find_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for focus events.
+  ///
+  struct _cef_focus_handler_t*(CEF_CALLBACK* get_focus_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for JavaScript dialogs. If no handler is provided the
+  // default implementation will be used.
+  ///
+  struct _cef_jsdialog_handler_t*(CEF_CALLBACK* get_jsdialog_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for keyboard events.
+  ///
+  struct _cef_keyboard_handler_t*(CEF_CALLBACK* get_keyboard_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for browser life span events.
+  ///
+  struct _cef_life_span_handler_t*(CEF_CALLBACK* get_life_span_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for browser load status events.
+  ///
+  struct _cef_load_handler_t*(CEF_CALLBACK* get_load_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for off-screen rendering events.
+  ///
+  struct _cef_render_handler_t*(CEF_CALLBACK* get_render_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Return the handler for browser request events.
+  ///
+  struct _cef_request_handler_t*(CEF_CALLBACK* get_request_handler)(
+      struct _cef_client_t* self);
+
+  ///
+  // Called when a new message is received from a different process. Return true
+  // (1) if the message was handled or false (0) otherwise. Do not keep a
+  // reference to or attempt to access the message outside of this callback.
+  ///
+  int(CEF_CALLBACK* on_process_message_received)(
+      struct _cef_client_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      cef_process_id_t source_process,
+      struct _cef_process_message_t* message);
+} cef_client_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_CLIENT_CAPI_H_
diff --git a/src/include/capi/cef_command_line_capi.h b/src/include/capi/cef_command_line_capi.h
new file mode 100644
index 0000000..3ffc030
--- /dev/null
+++ b/src/include/capi/cef_command_line_capi.h
@@ -0,0 +1,214 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=72ba5fe0cc6fe8081ec7b2b556e9022d1c6e8c61$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_COMMAND_LINE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_COMMAND_LINE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to create and/or parse command line arguments. Arguments with
+// '--', '-' and, on Windows, '/' prefixes are considered switches. Switches
+// will always precede any arguments without switch prefixes. Switches can
+// optionally have a value specified using the '=' delimiter (e.g.
+// "-switch=value"). An argument of "--" will terminate switch parsing with all
+// subsequent tokens, regardless of prefix, being interpreted as non-switch
+// arguments. Switch names are considered case-insensitive. This structure can
+// be used before cef_initialize() is called.
+///
+typedef struct _cef_command_line_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. Do not call any other functions
+  // if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_command_line_t* self);
+
+  ///
+  // Returns true (1) if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_command_line_t* self);
+
+  ///
+  // Returns a writable copy of this object.
+  ///
+  struct _cef_command_line_t*(CEF_CALLBACK* copy)(
+      struct _cef_command_line_t* self);
+
+  ///
+  // Initialize the command line with the specified |argc| and |argv| values.
+  // The first argument must be the name of the program. This function is only
+  // supported on non-Windows platforms.
+  ///
+  void(CEF_CALLBACK* init_from_argv)(struct _cef_command_line_t* self,
+                                     int argc,
+                                     const char* const* argv);
+
+  ///
+  // Initialize the command line with the string returned by calling
+  // GetCommandLineW(). This function is only supported on Windows.
+  ///
+  void(CEF_CALLBACK* init_from_string)(struct _cef_command_line_t* self,
+                                       const cef_string_t* command_line);
+
+  ///
+  // Reset the command-line switches and arguments but leave the program
+  // component unchanged.
+  ///
+  void(CEF_CALLBACK* reset)(struct _cef_command_line_t* self);
+
+  ///
+  // Retrieve the original command line string as a vector of strings. The argv
+  // array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* }
+  ///
+  void(CEF_CALLBACK* get_argv)(struct _cef_command_line_t* self,
+                               cef_string_list_t argv);
+
+  ///
+  // Constructs and returns the represented command line string. Use this
+  // function cautiously because quoting behavior is unclear.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_command_line_string)(
+      struct _cef_command_line_t* self);
+
+  ///
+  // Get the program part of the command line string (the first item).
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_program)(
+      struct _cef_command_line_t* self);
+
+  ///
+  // Set the program part of the command line string (the first item).
+  ///
+  void(CEF_CALLBACK* set_program)(struct _cef_command_line_t* self,
+                                  const cef_string_t* program);
+
+  ///
+  // Returns true (1) if the command line has switches.
+  ///
+  int(CEF_CALLBACK* has_switches)(struct _cef_command_line_t* self);
+
+  ///
+  // Returns true (1) if the command line contains the given switch.
+  ///
+  int(CEF_CALLBACK* has_switch)(struct _cef_command_line_t* self,
+                                const cef_string_t* name);
+
+  ///
+  // Returns the value associated with the given switch. If the switch has no
+  // value or isn't present this function returns the NULL string.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_switch_value)(
+      struct _cef_command_line_t* self,
+      const cef_string_t* name);
+
+  ///
+  // Returns the map of switch names and values. If a switch has no value an
+  // NULL string is returned.
+  ///
+  void(CEF_CALLBACK* get_switches)(struct _cef_command_line_t* self,
+                                   cef_string_map_t switches);
+
+  ///
+  // Add a switch to the end of the command line. If the switch has no value
+  // pass an NULL value string.
+  ///
+  void(CEF_CALLBACK* append_switch)(struct _cef_command_line_t* self,
+                                    const cef_string_t* name);
+
+  ///
+  // Add a switch with the specified value to the end of the command line.
+  ///
+  void(CEF_CALLBACK* append_switch_with_value)(struct _cef_command_line_t* self,
+                                               const cef_string_t* name,
+                                               const cef_string_t* value);
+
+  ///
+  // True if there are remaining command line arguments.
+  ///
+  int(CEF_CALLBACK* has_arguments)(struct _cef_command_line_t* self);
+
+  ///
+  // Get the remaining command line arguments.
+  ///
+  void(CEF_CALLBACK* get_arguments)(struct _cef_command_line_t* self,
+                                    cef_string_list_t arguments);
+
+  ///
+  // Add an argument to the end of the command line.
+  ///
+  void(CEF_CALLBACK* append_argument)(struct _cef_command_line_t* self,
+                                      const cef_string_t* argument);
+
+  ///
+  // Insert a command before the current command. Common for debuggers, like
+  // "valgrind" or "gdb --args".
+  ///
+  void(CEF_CALLBACK* prepend_wrapper)(struct _cef_command_line_t* self,
+                                      const cef_string_t* wrapper);
+} cef_command_line_t;
+
+///
+// Create a new cef_command_line_t instance.
+///
+CEF_EXPORT cef_command_line_t* cef_command_line_create();
+
+///
+// Returns the singleton global cef_command_line_t object. The returned object
+// will be read-only.
+///
+CEF_EXPORT cef_command_line_t* cef_command_line_get_global();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_COMMAND_LINE_CAPI_H_
diff --git a/src/include/capi/cef_context_menu_handler_capi.h b/src/include/capi/cef_context_menu_handler_capi.h
new file mode 100644
index 0000000..e4cb71b
--- /dev/null
+++ b/src/include/capi/cef_context_menu_handler_capi.h
@@ -0,0 +1,308 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=fcb0328c54e5f629c24bfd232d75c31c372ab6ac$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_CONTEXT_MENU_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_CONTEXT_MENU_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_menu_model_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_context_menu_params_t;
+
+///
+// Callback structure used for continuation of custom context menu display.
+///
+typedef struct _cef_run_context_menu_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Complete context menu display by selecting the specified |command_id| and
+  // |event_flags|.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_run_context_menu_callback_t* self,
+                           int command_id,
+                           cef_event_flags_t event_flags);
+
+  ///
+  // Cancel context menu display.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_run_context_menu_callback_t* self);
+} cef_run_context_menu_callback_t;
+
+///
+// Implement this structure to handle context menu events. The functions of this
+// structure will be called on the UI thread.
+///
+typedef struct _cef_context_menu_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called before a context menu is displayed. |params| provides information
+  // about the context menu state. |model| initially contains the default
+  // context menu. The |model| can be cleared to show no context menu or
+  // modified to show a custom menu. Do not keep references to |params| or
+  // |model| outside of this callback.
+  ///
+  void(CEF_CALLBACK* on_before_context_menu)(
+      struct _cef_context_menu_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_context_menu_params_t* params,
+      struct _cef_menu_model_t* model);
+
+  ///
+  // Called to allow custom display of the context menu. |params| provides
+  // information about the context menu state. |model| contains the context menu
+  // model resulting from OnBeforeContextMenu. For custom display return true
+  // (1) and execute |callback| either synchronously or asynchronously with the
+  // selected command ID. For default display return false (0). Do not keep
+  // references to |params| or |model| outside of this callback.
+  ///
+  int(CEF_CALLBACK* run_context_menu)(
+      struct _cef_context_menu_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_context_menu_params_t* params,
+      struct _cef_menu_model_t* model,
+      struct _cef_run_context_menu_callback_t* callback);
+
+  ///
+  // Called to execute a command selected from the context menu. Return true (1)
+  // if the command was handled or false (0) for the default implementation. See
+  // cef_menu_id_t for the command ids that have default implementations. All
+  // user-defined command ids should be between MENU_ID_USER_FIRST and
+  // MENU_ID_USER_LAST. |params| will have the same values as what was passed to
+  // on_before_context_menu(). Do not keep a reference to |params| outside of
+  // this callback.
+  ///
+  int(CEF_CALLBACK* on_context_menu_command)(
+      struct _cef_context_menu_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_context_menu_params_t* params,
+      int command_id,
+      cef_event_flags_t event_flags);
+
+  ///
+  // Called when the context menu is dismissed irregardless of whether the menu
+  // was NULL or a command was selected.
+  ///
+  void(CEF_CALLBACK* on_context_menu_dismissed)(
+      struct _cef_context_menu_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame);
+} cef_context_menu_handler_t;
+
+///
+// Provides information about the context menu state. The ethods of this
+// structure can only be accessed on browser process the UI thread.
+///
+typedef struct _cef_context_menu_params_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the X coordinate of the mouse where the context menu was invoked.
+  // Coords are relative to the associated RenderView's origin.
+  ///
+  int(CEF_CALLBACK* get_xcoord)(struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the Y coordinate of the mouse where the context menu was invoked.
+  // Coords are relative to the associated RenderView's origin.
+  ///
+  int(CEF_CALLBACK* get_ycoord)(struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns flags representing the type of node that the context menu was
+  // invoked on.
+  ///
+  cef_context_menu_type_flags_t(CEF_CALLBACK* get_type_flags)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the URL of the link, if any, that encloses the node that the
+  // context menu was invoked on.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_link_url)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the link URL, if any, to be used ONLY for "copy link address". We
+  // don't validate this field in the frontend process.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_unfiltered_link_url)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the source URL, if any, for the element that the context menu was
+  // invoked on. Example of elements with source URLs are img, audio, and video.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_source_url)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns true (1) if the context menu was invoked on an image which has non-
+  // NULL contents.
+  ///
+  int(CEF_CALLBACK* has_image_contents)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the title text or the alt text if the context menu was invoked on
+  // an image.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_title_text)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the URL of the top level page that the context menu was invoked on.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_page_url)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the URL of the subframe that the context menu was invoked on.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_frame_url)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the character encoding of the subframe that the context menu was
+  // invoked on.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_frame_charset)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the type of context node that the context menu was invoked on.
+  ///
+  cef_context_menu_media_type_t(CEF_CALLBACK* get_media_type)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns flags representing the actions supported by the media element, if
+  // any, that the context menu was invoked on.
+  ///
+  cef_context_menu_media_state_flags_t(CEF_CALLBACK* get_media_state_flags)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the text of the selection, if any, that the context menu was
+  // invoked on.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_selection_text)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns the text of the misspelled word, if any, that the context menu was
+  // invoked on.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_misspelled_word)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns true (1) if suggestions exist, false (0) otherwise. Fills in
+  // |suggestions| from the spell check service for the misspelled word if there
+  // is one.
+  ///
+  int(CEF_CALLBACK* get_dictionary_suggestions)(
+      struct _cef_context_menu_params_t* self,
+      cef_string_list_t suggestions);
+
+  ///
+  // Returns true (1) if the context menu was invoked on an editable node.
+  ///
+  int(CEF_CALLBACK* is_editable)(struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns true (1) if the context menu was invoked on an editable node where
+  // spell-check is enabled.
+  ///
+  int(CEF_CALLBACK* is_spell_check_enabled)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns flags representing the actions supported by the editable node, if
+  // any, that the context menu was invoked on.
+  ///
+  cef_context_menu_edit_state_flags_t(CEF_CALLBACK* get_edit_state_flags)(
+      struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns true (1) if the context menu contains items specified by the
+  // renderer process (for example, plugin placeholder or pepper plugin menu
+  // items).
+  ///
+  int(CEF_CALLBACK* is_custom_menu)(struct _cef_context_menu_params_t* self);
+
+  ///
+  // Returns true (1) if the context menu was invoked from a pepper plugin.
+  ///
+  int(CEF_CALLBACK* is_pepper_menu)(struct _cef_context_menu_params_t* self);
+} cef_context_menu_params_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_CONTEXT_MENU_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_cookie_capi.h b/src/include/capi/cef_cookie_capi.h
new file mode 100644
index 0000000..7a4a13e
--- /dev/null
+++ b/src/include/capi/cef_cookie_capi.h
@@ -0,0 +1,214 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=2f5721138da26a9d7cce300a635b58dae9f51a4a$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_COOKIE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_COOKIE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_callback_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_cookie_visitor_t;
+struct _cef_delete_cookies_callback_t;
+struct _cef_set_cookie_callback_t;
+
+///
+// Structure used for managing cookies. The functions of this structure may be
+// called on any thread unless otherwise indicated.
+///
+typedef struct _cef_cookie_manager_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Set the schemes supported by this manager. If |include_defaults| is true
+  // (1) the default schemes ("http", "https", "ws" and "wss") will also be
+  // supported. Calling this function with an NULL |schemes| value and
+  // |include_defaults| set to false (0) will disable all loading and saving of
+  // cookies for this manager. If |callback| is non-NULL it will be executed
+  // asnychronously on the UI thread after the change has been applied. Must be
+  // called before any cookies are accessed.
+  ///
+  void(CEF_CALLBACK* set_supported_schemes)(
+      struct _cef_cookie_manager_t* self,
+      cef_string_list_t schemes,
+      int include_defaults,
+      struct _cef_completion_callback_t* callback);
+
+  ///
+  // Visit all cookies on the UI thread. The returned cookies are ordered by
+  // longest path, then by earliest creation date. Returns false (0) if cookies
+  // cannot be accessed.
+  ///
+  int(CEF_CALLBACK* visit_all_cookies)(struct _cef_cookie_manager_t* self,
+                                       struct _cef_cookie_visitor_t* visitor);
+
+  ///
+  // Visit a subset of cookies on the UI thread. The results are filtered by the
+  // given url scheme, host, domain and path. If |includeHttpOnly| is true (1)
+  // HTTP-only cookies will also be included in the results. The returned
+  // cookies are ordered by longest path, then by earliest creation date.
+  // Returns false (0) if cookies cannot be accessed.
+  ///
+  int(CEF_CALLBACK* visit_url_cookies)(struct _cef_cookie_manager_t* self,
+                                       const cef_string_t* url,
+                                       int includeHttpOnly,
+                                       struct _cef_cookie_visitor_t* visitor);
+
+  ///
+  // Sets a cookie given a valid URL and explicit user-provided cookie
+  // attributes. This function expects each attribute to be well-formed. It will
+  // check for disallowed characters (e.g. the ';' character is disallowed
+  // within the cookie value attribute) and fail without setting the cookie if
+  // such characters are found. If |callback| is non-NULL it will be executed
+  // asnychronously on the UI thread after the cookie has been set. Returns
+  // false (0) if an invalid URL is specified or if cookies cannot be accessed.
+  ///
+  int(CEF_CALLBACK* set_cookie)(struct _cef_cookie_manager_t* self,
+                                const cef_string_t* url,
+                                const struct _cef_cookie_t* cookie,
+                                struct _cef_set_cookie_callback_t* callback);
+
+  ///
+  // Delete all cookies that match the specified parameters. If both |url| and
+  // |cookie_name| values are specified all host and domain cookies matching
+  // both will be deleted. If only |url| is specified all host cookies (but not
+  // domain cookies) irrespective of path will be deleted. If |url| is NULL all
+  // cookies for all hosts and domains will be deleted. If |callback| is non-
+  // NULL it will be executed asnychronously on the UI thread after the cookies
+  // have been deleted. Returns false (0) if a non-NULL invalid URL is specified
+  // or if cookies cannot be accessed. Cookies can alternately be deleted using
+  // the Visit*Cookies() functions.
+  ///
+  int(CEF_CALLBACK* delete_cookies)(
+      struct _cef_cookie_manager_t* self,
+      const cef_string_t* url,
+      const cef_string_t* cookie_name,
+      struct _cef_delete_cookies_callback_t* callback);
+
+  ///
+  // Flush the backing store (if any) to disk. If |callback| is non-NULL it will
+  // be executed asnychronously on the UI thread after the flush is complete.
+  // Returns false (0) if cookies cannot be accessed.
+  ///
+  int(CEF_CALLBACK* flush_store)(struct _cef_cookie_manager_t* self,
+                                 struct _cef_completion_callback_t* callback);
+} cef_cookie_manager_t;
+
+///
+// Returns the global cookie manager. By default data will be stored at
+// CefSettings.cache_path if specified or in memory otherwise. If |callback| is
+// non-NULL it will be executed asnychronously on the UI thread after the
+// manager's storage has been initialized. Using this function is equivalent to
+// calling cef_request_context_t::cef_request_context_get_global_context()->GetD
+// efaultCookieManager().
+///
+CEF_EXPORT cef_cookie_manager_t* cef_cookie_manager_get_global_manager(
+    struct _cef_completion_callback_t* callback);
+
+///
+// Structure to implement for visiting cookie values. The functions of this
+// structure will always be called on the UI thread.
+///
+typedef struct _cef_cookie_visitor_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called once for each cookie. |count| is the 0-based
+  // index for the current cookie. |total| is the total number of cookies. Set
+  // |deleteCookie| to true (1) to delete the cookie currently being visited.
+  // Return false (0) to stop visiting cookies. This function may never be
+  // called if no cookies are found.
+  ///
+  int(CEF_CALLBACK* visit)(struct _cef_cookie_visitor_t* self,
+                           const struct _cef_cookie_t* cookie,
+                           int count,
+                           int total,
+                           int* deleteCookie);
+} cef_cookie_visitor_t;
+
+///
+// Structure to implement to be notified of asynchronous completion via
+// cef_cookie_manager_t::set_cookie().
+///
+typedef struct _cef_set_cookie_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called upon completion. |success| will be true (1) if
+  // the cookie was set successfully.
+  ///
+  void(CEF_CALLBACK* on_complete)(struct _cef_set_cookie_callback_t* self,
+                                  int success);
+} cef_set_cookie_callback_t;
+
+///
+// Structure to implement to be notified of asynchronous completion via
+// cef_cookie_manager_t::delete_cookies().
+///
+typedef struct _cef_delete_cookies_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called upon completion. |num_deleted| will be the
+  // number of cookies that were deleted.
+  ///
+  void(CEF_CALLBACK* on_complete)(struct _cef_delete_cookies_callback_t* self,
+                                  int num_deleted);
+} cef_delete_cookies_callback_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_COOKIE_CAPI_H_
diff --git a/src/include/capi/cef_crash_util_capi.h b/src/include/capi/cef_crash_util_capi.h
new file mode 100644
index 0000000..98a3be5
--- /dev/null
+++ b/src/include/capi/cef_crash_util_capi.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=2b24c7d99c59c669719b822f5ea19763d140b001$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_CRASH_UTIL_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_CRASH_UTIL_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Crash reporting is configured using an INI-style config file named
+// "crash_reporter.cfg". On Windows and Linux this file must be placed next to
+// the main application executable. On macOS this file must be placed in the
+// top-level app bundle Resources directory (e.g.
+// "<appname>.app/Contents/Resources"). File contents are as follows:
+//
+//  # Comments start with a hash character and must be on their own line.
+//
+//  [Config]
+//  ProductName=<Value of the "prod" crash key; defaults to "cef">
+//  ProductVersion=<Value of the "ver" crash key; defaults to the CEF version>
+//  AppName=<Windows only; App-specific folder name component for storing crash
+//           information; default to "CEF">
+//  ExternalHandler=<Windows only; Name of the external handler exe to use
+//                   instead of re-launching the main exe; default to empty>
+//  BrowserCrashForwardingEnabled=<macOS only; True if browser process crashes
+//                                 should be forwarded to the system crash
+//                                 reporter; default to false>
+//  ServerURL=<crash server URL; default to empty>
+//  RateLimitEnabled=<True if uploads should be rate limited; default to true>
+//  MaxUploadsPerDay=<Max uploads per 24 hours, used if rate limit is enabled;
+//                    default to 5>
+//  MaxDatabaseSizeInMb=<Total crash report disk usage greater than this value
+//                       will cause older reports to be deleted; default to 20>
+//  MaxDatabaseAgeInDays=<Crash reports older than this value will be deleted;
+//                        default to 5>
+//
+//  [CrashKeys]
+//  my_key1=<small|medium|large>
+//  my_key2=<small|medium|large>
+//
+// Config section:
+//
+// If "ProductName" and/or "ProductVersion" are set then the specified values
+// will be included in the crash dump metadata. On macOS if these values are set
+// to NULL then they will be retrieved from the Info.plist file using the
+// "CFBundleName" and "CFBundleShortVersionString" keys respectively.
+//
+// If "AppName" is set on Windows then crash report information (metrics,
+// database and dumps) will be stored locally on disk under the
+// "C:\Users\[CurrentUser]\AppData\Local\[AppName]\User Data" folder. On other
+// platforms the CefSettings.user_data_path value will be used.
+//
+// If "ExternalHandler" is set on Windows then the specified exe will be
+// launched as the crashpad-handler instead of re-launching the main process
+// exe. The value can be an absolute path or a path relative to the main exe
+// directory. On Linux the CefSettings.browser_subprocess_path value will be
+// used. On macOS the existing subprocess app bundle will be used.
+//
+// If "BrowserCrashForwardingEnabled" is set to true (1) on macOS then browser
+// process crashes will be forwarded to the system crash reporter. This results
+// in the crash UI dialog being displayed to the user and crash reports being
+// logged under "~/Library/Logs/DiagnosticReports". Forwarding of crash reports
+// from non-browser processes and Debug builds is always disabled.
+//
+// If "ServerURL" is set then crashes will be uploaded as a multi-part POST
+// request to the specified URL. Otherwise, reports will only be stored locally
+// on disk.
+//
+// If "RateLimitEnabled" is set to true (1) then crash report uploads will be
+// rate limited as follows:
+//  1. If "MaxUploadsPerDay" is set to a positive value then at most the
+//     specified number of crashes will be uploaded in each 24 hour period.
+//  2. If crash upload fails due to a network or server error then an
+//     incremental backoff delay up to a maximum of 24 hours will be applied for
+//     retries.
+//  3. If a backoff delay is applied and "MaxUploadsPerDay" is > 1 then the
+//     "MaxUploadsPerDay" value will be reduced to 1 until the client is
+//     restarted. This helps to avoid an upload flood when the network or
+//     server error is resolved.
+// Rate limiting is not supported on Linux.
+//
+// If "MaxDatabaseSizeInMb" is set to a positive value then crash report storage
+// on disk will be limited to that size in megabytes. For example, on Windows
+// each dump is about 600KB so a "MaxDatabaseSizeInMb" value of 20 equates to
+// about 34 crash reports stored on disk. Not supported on Linux.
+//
+// If "MaxDatabaseAgeInDays" is set to a positive value then crash reports older
+// than the specified age in days will be deleted. Not supported on Linux.
+//
+// CrashKeys section:
+//
+// A maximum of 26 crash keys of each size can be specified for use by the
+// application. Crash key values will be truncated based on the specified size
+// (small = 64 bytes, medium = 256 bytes, large = 1024 bytes). The value of
+// crash keys can be set from any thread or process using the
+// CefSetCrashKeyValue function. These key/value pairs will be sent to the crash
+// server along with the crash dump file.
+///
+CEF_EXPORT int cef_crash_reporting_enabled();
+
+///
+// Sets or clears a specific key-value pair from the crash metadata.
+///
+CEF_EXPORT void cef_set_crash_key_value(const cef_string_t* key,
+                                        const cef_string_t* value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_CRASH_UTIL_CAPI_H_
diff --git a/src/include/capi/cef_devtools_message_observer_capi.h b/src/include/capi/cef_devtools_message_observer_capi.h
new file mode 100644
index 0000000..bb0a21c
--- /dev/null
+++ b/src/include/capi/cef_devtools_message_observer_capi.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=86906c2e971fea7e479738f59bbf85d71ce31953$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DEVTOOLS_MESSAGE_OBSERVER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DEVTOOLS_MESSAGE_OBSERVER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_browser_t;
+
+///
+// Callback structure for cef_browser_host_t::AddDevToolsMessageObserver. The
+// functions of this structure will be called on the browser process UI thread.
+///
+typedef struct _cef_dev_tools_message_observer_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called on receipt of a DevTools protocol message.
+  // |browser| is the originating browser instance. |message| is a UTF8-encoded
+  // JSON dictionary representing either a function result or an event.
+  // |message| is only valid for the scope of this callback and should be copied
+  // if necessary. Return true (1) if the message was handled or false (0) if
+  // the message should be further processed and passed to the
+  // OnDevToolsMethodResult or OnDevToolsEvent functions as appropriate.
+  //
+  // Method result dictionaries include an "id" (int) value that identifies the
+  // orginating function call sent from cef_browser_host_t::SendDevToolsMessage,
+  // and optionally either a "result" (dictionary) or "error" (dictionary)
+  // value. The "error" dictionary will contain "code" (int) and "message"
+  // (string) values. Event dictionaries include a "function" (string) value and
+  // optionally a "params" (dictionary) value. See the DevTools protocol
+  // documentation at https://chromedevtools.github.io/devtools-protocol/ for
+  // details of supported function calls and the expected "result" or "params"
+  // dictionary contents. JSON dictionaries can be parsed using the CefParseJSON
+  // function if desired, however be aware of performance considerations when
+  // parsing large messages (some of which may exceed 1MB in size).
+  ///
+  int(CEF_CALLBACK* on_dev_tools_message)(
+      struct _cef_dev_tools_message_observer_t* self,
+      struct _cef_browser_t* browser,
+      const void* message,
+      size_t message_size);
+
+  ///
+  // Method that will be called after attempted execution of a DevTools protocol
+  // function. |browser| is the originating browser instance. |message_id| is
+  // the "id" value that identifies the originating function call message. If
+  // the function succeeded |success| will be true (1) and |result| will be the
+  // UTF8-encoded JSON "result" dictionary value (which may be NULL). If the
+  // function failed |success| will be false (0) and |result| will be the
+  // UTF8-encoded JSON "error" dictionary value. |result| is only valid for the
+  // scope of this callback and should be copied if necessary. See the
+  // OnDevToolsMessage documentation for additional details on |result|
+  // contents.
+  ///
+  void(CEF_CALLBACK* on_dev_tools_method_result)(
+      struct _cef_dev_tools_message_observer_t* self,
+      struct _cef_browser_t* browser,
+      int message_id,
+      int success,
+      const void* result,
+      size_t result_size);
+
+  ///
+  // Method that will be called on receipt of a DevTools protocol event.
+  // |browser| is the originating browser instance. |function| is the "function"
+  // value. |params| is the UTF8-encoded JSON "params" dictionary value (which
+  // may be NULL). |params| is only valid for the scope of this callback and
+  // should be copied if necessary. See the OnDevToolsMessage documentation for
+  // additional details on |params| contents.
+  ///
+  void(CEF_CALLBACK* on_dev_tools_event)(
+      struct _cef_dev_tools_message_observer_t* self,
+      struct _cef_browser_t* browser,
+      const cef_string_t* method,
+      const void* params,
+      size_t params_size);
+
+  ///
+  // Method that will be called when the DevTools agent has attached. |browser|
+  // is the originating browser instance. This will generally occur in response
+  // to the first message sent while the agent is detached.
+  ///
+  void(CEF_CALLBACK* on_dev_tools_agent_attached)(
+      struct _cef_dev_tools_message_observer_t* self,
+      struct _cef_browser_t* browser);
+
+  ///
+  // Method that will be called when the DevTools agent has detached. |browser|
+  // is the originating browser instance. Any function results that were pending
+  // before the agent became detached will not be delivered, and any active
+  // event subscriptions will be canceled.
+  ///
+  void(CEF_CALLBACK* on_dev_tools_agent_detached)(
+      struct _cef_dev_tools_message_observer_t* self,
+      struct _cef_browser_t* browser);
+} cef_dev_tools_message_observer_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DEVTOOLS_MESSAGE_OBSERVER_CAPI_H_
diff --git a/src/include/capi/cef_dialog_handler_capi.h b/src/include/capi/cef_dialog_handler_capi.h
new file mode 100644
index 0000000..93258f1
--- /dev/null
+++ b/src/include/capi/cef_dialog_handler_capi.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3253c217564ae9a85a1e971298c32a35e4cad136$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DIALOG_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DIALOG_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback structure for asynchronous continuation of file dialog requests.
+///
+typedef struct _cef_file_dialog_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Continue the file selection. |selected_accept_filter| should be the 0-based
+  // index of the value selected from the accept filters array passed to
+  // cef_dialog_handler_t::OnFileDialog. |file_paths| should be a single value
+  // or a list of values depending on the dialog mode. An NULL |file_paths|
+  // value is treated the same as calling cancel().
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_file_dialog_callback_t* self,
+                           int selected_accept_filter,
+                           cef_string_list_t file_paths);
+
+  ///
+  // Cancel the file selection.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_file_dialog_callback_t* self);
+} cef_file_dialog_callback_t;
+
+///
+// Implement this structure to handle dialog events. The functions of this
+// structure will be called on the browser process UI thread.
+///
+typedef struct _cef_dialog_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called to run a file chooser dialog. |mode| represents the type of dialog
+  // to display. |title| to the title to be used for the dialog and may be NULL
+  // to show the default title ("Open" or "Save" depending on the mode).
+  // |default_file_path| is the path with optional directory and/or file name
+  // component that should be initially selected in the dialog. |accept_filters|
+  // are used to restrict the selectable file types and may any combination of
+  // (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"), (b)
+  // individual file extensions (e.g. ".txt" or ".png"), or (c) combined
+  // description and file extension delimited using "|" and ";" (e.g. "Image
+  // Types|.png;.gif;.jpg"). |selected_accept_filter| is the 0-based index of
+  // the filter that should be selected by default. To display a custom dialog
+  // return true (1) and execute |callback| either inline or at a later time. To
+  // display the default dialog return false (0).
+  ///
+  int(CEF_CALLBACK* on_file_dialog)(
+      struct _cef_dialog_handler_t* self,
+      struct _cef_browser_t* browser,
+      cef_file_dialog_mode_t mode,
+      const cef_string_t* title,
+      const cef_string_t* default_file_path,
+      cef_string_list_t accept_filters,
+      int selected_accept_filter,
+      struct _cef_file_dialog_callback_t* callback);
+} cef_dialog_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DIALOG_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_display_handler_capi.h b/src/include/capi/cef_display_handler_capi.h
new file mode 100644
index 0000000..d74f4a2
--- /dev/null
+++ b/src/include/capi/cef_display_handler_capi.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=951c936c8070dbf9bd246cc766b81cdfe06a3d81$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DISPLAY_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DISPLAY_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle events related to browser display state.
+// The functions of this structure will be called on the UI thread.
+///
+typedef struct _cef_display_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called when a frame's address has changed.
+  ///
+  void(CEF_CALLBACK* on_address_change)(struct _cef_display_handler_t* self,
+                                        struct _cef_browser_t* browser,
+                                        struct _cef_frame_t* frame,
+                                        const cef_string_t* url);
+
+  ///
+  // Called when the page title changes.
+  ///
+  void(CEF_CALLBACK* on_title_change)(struct _cef_display_handler_t* self,
+                                      struct _cef_browser_t* browser,
+                                      const cef_string_t* title);
+
+  ///
+  // Called when the page icon changes.
+  ///
+  void(CEF_CALLBACK* on_favicon_urlchange)(struct _cef_display_handler_t* self,
+                                           struct _cef_browser_t* browser,
+                                           cef_string_list_t icon_urls);
+
+  ///
+  // Called when web content in the page has toggled fullscreen mode. If
+  // |fullscreen| is true (1) the content will automatically be sized to fill
+  // the browser content area. If |fullscreen| is false (0) the content will
+  // automatically return to its original size and position. The client is
+  // responsible for resizing the browser if desired.
+  ///
+  void(CEF_CALLBACK* on_fullscreen_mode_change)(
+      struct _cef_display_handler_t* self,
+      struct _cef_browser_t* browser,
+      int fullscreen);
+
+  ///
+  // Called when the browser is about to display a tooltip. |text| contains the
+  // text that will be displayed in the tooltip. To handle the display of the
+  // tooltip yourself return true (1). Otherwise, you can optionally modify
+  // |text| and then return false (0) to allow the browser to display the
+  // tooltip. When window rendering is disabled the application is responsible
+  // for drawing tooltips and the return value is ignored.
+  ///
+  int(CEF_CALLBACK* on_tooltip)(struct _cef_display_handler_t* self,
+                                struct _cef_browser_t* browser,
+                                cef_string_t* text);
+
+  ///
+  // Called when the browser receives a status message. |value| contains the
+  // text that will be displayed in the status message.
+  ///
+  void(CEF_CALLBACK* on_status_message)(struct _cef_display_handler_t* self,
+                                        struct _cef_browser_t* browser,
+                                        const cef_string_t* value);
+
+  ///
+  // Called to display a console message. Return true (1) to stop the message
+  // from being output to the console.
+  ///
+  int(CEF_CALLBACK* on_console_message)(struct _cef_display_handler_t* self,
+                                        struct _cef_browser_t* browser,
+                                        cef_log_severity_t level,
+                                        const cef_string_t* message,
+                                        const cef_string_t* source,
+                                        int line);
+
+  ///
+  // Called when auto-resize is enabled via
+  // cef_browser_host_t::SetAutoResizeEnabled and the contents have auto-
+  // resized. |new_size| will be the desired size in view coordinates. Return
+  // true (1) if the resize was handled or false (0) for default handling.
+  ///
+  int(CEF_CALLBACK* on_auto_resize)(struct _cef_display_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    const cef_size_t* new_size);
+
+  ///
+  // Called when the overall page loading progress has changed. |progress|
+  // ranges from 0.0 to 1.0.
+  ///
+  void(CEF_CALLBACK* on_loading_progress_change)(
+      struct _cef_display_handler_t* self,
+      struct _cef_browser_t* browser,
+      double progress);
+} cef_display_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DISPLAY_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_dom_capi.h b/src/include/capi/cef_dom_capi.h
new file mode 100644
index 0000000..54122f4
--- /dev/null
+++ b/src/include/capi/cef_dom_capi.h
@@ -0,0 +1,347 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=055c506e7950abba3ec1c12adbbb1a9989cf5ac5$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DOM_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DOM_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_domdocument_t;
+struct _cef_domnode_t;
+
+///
+// Structure to implement for visiting the DOM. The functions of this structure
+// will be called on the render process main thread.
+///
+typedef struct _cef_domvisitor_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method executed for visiting the DOM. The document object passed to this
+  // function represents a snapshot of the DOM at the time this function is
+  // executed. DOM objects are only valid for the scope of this function. Do not
+  // keep references to or attempt to access any DOM objects outside the scope
+  // of this function.
+  ///
+  void(CEF_CALLBACK* visit)(struct _cef_domvisitor_t* self,
+                            struct _cef_domdocument_t* document);
+} cef_domvisitor_t;
+
+///
+// Structure used to represent a DOM document. The functions of this structure
+// should only be called on the render process main thread thread.
+///
+typedef struct _cef_domdocument_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the document type.
+  ///
+  cef_dom_document_type_t(CEF_CALLBACK* get_type)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the root document node.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_document)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the BODY node of an HTML document.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_body)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the HEAD node of an HTML document.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_head)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the title of an HTML document.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_title)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the document element with the specified ID value.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_element_by_id)(
+      struct _cef_domdocument_t* self,
+      const cef_string_t* id);
+
+  ///
+  // Returns the node that currently has keyboard focus.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_focused_node)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns true (1) if a portion of the document is selected.
+  ///
+  int(CEF_CALLBACK* has_selection)(struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the selection offset within the start node.
+  ///
+  int(CEF_CALLBACK* get_selection_start_offset)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the selection offset within the end node.
+  ///
+  int(CEF_CALLBACK* get_selection_end_offset)(struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the contents of this selection as markup.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_selection_as_markup)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the contents of this selection as text.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_selection_as_text)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns the base URL for the document.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_base_url)(
+      struct _cef_domdocument_t* self);
+
+  ///
+  // Returns a complete URL based on the document base URL and the specified
+  // partial URL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_complete_url)(
+      struct _cef_domdocument_t* self,
+      const cef_string_t* partialURL);
+} cef_domdocument_t;
+
+///
+// Structure used to represent a DOM node. The functions of this structure
+// should only be called on the render process main thread.
+///
+typedef struct _cef_domnode_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the type for this node.
+  ///
+  cef_dom_node_type_t(CEF_CALLBACK* get_type)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this is a text node.
+  ///
+  int(CEF_CALLBACK* is_text)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this is an element node.
+  ///
+  int(CEF_CALLBACK* is_element)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this is an editable node.
+  ///
+  int(CEF_CALLBACK* is_editable)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this is a form control element node.
+  ///
+  int(CEF_CALLBACK* is_form_control_element)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns the type of this form control element node.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_form_control_element_type)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_domnode_t* self,
+                             struct _cef_domnode_t* that);
+
+  ///
+  // Returns the name of this node.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_name)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns the value of this node.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_value)(struct _cef_domnode_t* self);
+
+  ///
+  // Set the value of this node. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_value)(struct _cef_domnode_t* self,
+                               const cef_string_t* value);
+
+  ///
+  // Returns the contents of this node as markup.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_as_markup)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns the document associated with this node.
+  ///
+  struct _cef_domdocument_t*(CEF_CALLBACK* get_document)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns the parent node.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_parent)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns the previous sibling node.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_previous_sibling)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns the next sibling node.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_next_sibling)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this node has child nodes.
+  ///
+  int(CEF_CALLBACK* has_children)(struct _cef_domnode_t* self);
+
+  ///
+  // Return the first child node.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_first_child)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns the last child node.
+  ///
+  struct _cef_domnode_t*(CEF_CALLBACK* get_last_child)(
+      struct _cef_domnode_t* self);
+
+  // The following functions are valid only for element nodes.
+
+  ///
+  // Returns the tag name of this element.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_element_tag_name)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this element has attributes.
+  ///
+  int(CEF_CALLBACK* has_element_attributes)(struct _cef_domnode_t* self);
+
+  ///
+  // Returns true (1) if this element has an attribute named |attrName|.
+  ///
+  int(CEF_CALLBACK* has_element_attribute)(struct _cef_domnode_t* self,
+                                           const cef_string_t* attrName);
+
+  ///
+  // Returns the element attribute named |attrName|.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_element_attribute)(
+      struct _cef_domnode_t* self,
+      const cef_string_t* attrName);
+
+  ///
+  // Returns a map of all element attributes.
+  ///
+  void(CEF_CALLBACK* get_element_attributes)(struct _cef_domnode_t* self,
+                                             cef_string_map_t attrMap);
+
+  ///
+  // Set the value for the element attribute named |attrName|. Returns true (1)
+  // on success.
+  ///
+  int(CEF_CALLBACK* set_element_attribute)(struct _cef_domnode_t* self,
+                                           const cef_string_t* attrName,
+                                           const cef_string_t* value);
+
+  ///
+  // Returns the inner text of the element.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_element_inner_text)(
+      struct _cef_domnode_t* self);
+
+  ///
+  // Returns the bounds of the element.
+  ///
+  cef_rect_t(CEF_CALLBACK* get_element_bounds)(struct _cef_domnode_t* self);
+} cef_domnode_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DOM_CAPI_H_
diff --git a/src/include/capi/cef_download_handler_capi.h b/src/include/capi/cef_download_handler_capi.h
new file mode 100644
index 0000000..bc57341
--- /dev/null
+++ b/src/include/capi/cef_download_handler_capi.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3399f17cc69d8fbd5c09f63f81680aa1f68454f0$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DOWNLOAD_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DOWNLOAD_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_download_item_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback structure used to asynchronously continue a download.
+///
+typedef struct _cef_before_download_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Call to continue the download. Set |download_path| to the full file path
+  // for the download including the file name or leave blank to use the
+  // suggested name and the default temp directory. Set |show_dialog| to true
+  // (1) if you do wish to show the default "Save As" dialog.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_before_download_callback_t* self,
+                           const cef_string_t* download_path,
+                           int show_dialog);
+} cef_before_download_callback_t;
+
+///
+// Callback structure used to asynchronously cancel a download.
+///
+typedef struct _cef_download_item_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Call to cancel the download.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_download_item_callback_t* self);
+
+  ///
+  // Call to pause the download.
+  ///
+  void(CEF_CALLBACK* pause)(struct _cef_download_item_callback_t* self);
+
+  ///
+  // Call to resume the download.
+  ///
+  void(CEF_CALLBACK* resume)(struct _cef_download_item_callback_t* self);
+} cef_download_item_callback_t;
+
+///
+// Structure used to handle file downloads. The functions of this structure will
+// called on the browser process UI thread.
+///
+typedef struct _cef_download_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called before a download begins. |suggested_name| is the suggested name for
+  // the download file. By default the download will be canceled. Execute
+  // |callback| either asynchronously or in this function to continue the
+  // download if desired. Do not keep a reference to |download_item| outside of
+  // this function.
+  ///
+  void(CEF_CALLBACK* on_before_download)(
+      struct _cef_download_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_download_item_t* download_item,
+      const cef_string_t* suggested_name,
+      struct _cef_before_download_callback_t* callback);
+
+  ///
+  // Called when a download's status or progress information has been updated.
+  // This may be called multiple times before and after on_before_download().
+  // Execute |callback| either asynchronously or in this function to cancel the
+  // download if desired. Do not keep a reference to |download_item| outside of
+  // this function.
+  ///
+  void(CEF_CALLBACK* on_download_updated)(
+      struct _cef_download_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_download_item_t* download_item,
+      struct _cef_download_item_callback_t* callback);
+} cef_download_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DOWNLOAD_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_download_item_capi.h b/src/include/capi/cef_download_item_capi.h
new file mode 100644
index 0000000..e422cc1
--- /dev/null
+++ b/src/include/capi/cef_download_item_capi.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=d6366977af5e2a3a71b4f57042208ff7ed524c6c$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DOWNLOAD_ITEM_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DOWNLOAD_ITEM_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to represent a download item.
+///
+typedef struct _cef_download_item_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. Do not call any other functions
+  // if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns true (1) if the download is in progress.
+  ///
+  int(CEF_CALLBACK* is_in_progress)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns true (1) if the download is complete.
+  ///
+  int(CEF_CALLBACK* is_complete)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns true (1) if the download has been canceled or interrupted.
+  ///
+  int(CEF_CALLBACK* is_canceled)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns a simple speed estimate in bytes/s.
+  ///
+  int64(CEF_CALLBACK* get_current_speed)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns the rough percent complete or -1 if the receive total size is
+  // unknown.
+  ///
+  int(CEF_CALLBACK* get_percent_complete)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns the total number of bytes.
+  ///
+  int64(CEF_CALLBACK* get_total_bytes)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns the number of received bytes.
+  ///
+  int64(CEF_CALLBACK* get_received_bytes)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns the time that the download started.
+  ///
+  cef_time_t(CEF_CALLBACK* get_start_time)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns the time that the download ended.
+  ///
+  cef_time_t(CEF_CALLBACK* get_end_time)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns the full path to the downloaded or downloading file.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_full_path)(
+      struct _cef_download_item_t* self);
+
+  ///
+  // Returns the unique identifier for this download.
+  ///
+  uint32(CEF_CALLBACK* get_id)(struct _cef_download_item_t* self);
+
+  ///
+  // Returns the URL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_url)(
+      struct _cef_download_item_t* self);
+
+  ///
+  // Returns the original URL before any redirections.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_original_url)(
+      struct _cef_download_item_t* self);
+
+  ///
+  // Returns the suggested file name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_suggested_file_name)(
+      struct _cef_download_item_t* self);
+
+  ///
+  // Returns the content disposition.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_content_disposition)(
+      struct _cef_download_item_t* self);
+
+  ///
+  // Returns the mime type.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_mime_type)(
+      struct _cef_download_item_t* self);
+} cef_download_item_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DOWNLOAD_ITEM_CAPI_H_
diff --git a/src/include/capi/cef_drag_data_capi.h b/src/include/capi/cef_drag_data_capi.h
new file mode 100644
index 0000000..e181709
--- /dev/null
+++ b/src/include/capi/cef_drag_data_capi.h
@@ -0,0 +1,228 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=6c8c654be3e69d872b3cfa6bdfb1adf615bff3ac$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DRAG_DATA_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DRAG_DATA_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_image_capi.h"
+#include "include/capi/cef_stream_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to represent drag data. The functions of this structure may be
+// called on any thread.
+///
+typedef struct _cef_drag_data_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns a copy of the current object.
+  ///
+  struct _cef_drag_data_t*(CEF_CALLBACK* clone)(struct _cef_drag_data_t* self);
+
+  ///
+  // Returns true (1) if this object is read-only.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_drag_data_t* self);
+
+  ///
+  // Returns true (1) if the drag data is a link.
+  ///
+  int(CEF_CALLBACK* is_link)(struct _cef_drag_data_t* self);
+
+  ///
+  // Returns true (1) if the drag data is a text or html fragment.
+  ///
+  int(CEF_CALLBACK* is_fragment)(struct _cef_drag_data_t* self);
+
+  ///
+  // Returns true (1) if the drag data is a file.
+  ///
+  int(CEF_CALLBACK* is_file)(struct _cef_drag_data_t* self);
+
+  ///
+  // Return the link URL that is being dragged.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_link_url)(
+      struct _cef_drag_data_t* self);
+
+  ///
+  // Return the title associated with the link being dragged.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_link_title)(
+      struct _cef_drag_data_t* self);
+
+  ///
+  // Return the metadata, if any, associated with the link being dragged.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_link_metadata)(
+      struct _cef_drag_data_t* self);
+
+  ///
+  // Return the plain text fragment that is being dragged.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_fragment_text)(
+      struct _cef_drag_data_t* self);
+
+  ///
+  // Return the text/html fragment that is being dragged.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_fragment_html)(
+      struct _cef_drag_data_t* self);
+
+  ///
+  // Return the base URL that the fragment came from. This value is used for
+  // resolving relative URLs and may be NULL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_fragment_base_url)(
+      struct _cef_drag_data_t* self);
+
+  ///
+  // Return the name of the file being dragged out of the browser window.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_file_name)(
+      struct _cef_drag_data_t* self);
+
+  ///
+  // Write the contents of the file being dragged out of the web view into
+  // |writer|. Returns the number of bytes sent to |writer|. If |writer| is NULL
+  // this function will return the size of the file contents in bytes. Call
+  // get_file_name() to get a suggested name for the file.
+  ///
+  size_t(CEF_CALLBACK* get_file_contents)(struct _cef_drag_data_t* self,
+                                          struct _cef_stream_writer_t* writer);
+
+  ///
+  // Retrieve the list of file names that are being dragged into the browser
+  // window.
+  ///
+  int(CEF_CALLBACK* get_file_names)(struct _cef_drag_data_t* self,
+                                    cef_string_list_t names);
+
+  ///
+  // Set the link URL that is being dragged.
+  ///
+  void(CEF_CALLBACK* set_link_url)(struct _cef_drag_data_t* self,
+                                   const cef_string_t* url);
+
+  ///
+  // Set the title associated with the link being dragged.
+  ///
+  void(CEF_CALLBACK* set_link_title)(struct _cef_drag_data_t* self,
+                                     const cef_string_t* title);
+
+  ///
+  // Set the metadata associated with the link being dragged.
+  ///
+  void(CEF_CALLBACK* set_link_metadata)(struct _cef_drag_data_t* self,
+                                        const cef_string_t* data);
+
+  ///
+  // Set the plain text fragment that is being dragged.
+  ///
+  void(CEF_CALLBACK* set_fragment_text)(struct _cef_drag_data_t* self,
+                                        const cef_string_t* text);
+
+  ///
+  // Set the text/html fragment that is being dragged.
+  ///
+  void(CEF_CALLBACK* set_fragment_html)(struct _cef_drag_data_t* self,
+                                        const cef_string_t* html);
+
+  ///
+  // Set the base URL that the fragment came from.
+  ///
+  void(CEF_CALLBACK* set_fragment_base_url)(struct _cef_drag_data_t* self,
+                                            const cef_string_t* base_url);
+
+  ///
+  // Reset the file contents. You should do this before calling
+  // cef_browser_host_t::DragTargetDragEnter as the web view does not allow us
+  // to drag in this kind of data.
+  ///
+  void(CEF_CALLBACK* reset_file_contents)(struct _cef_drag_data_t* self);
+
+  ///
+  // Add a file that is being dragged into the webview.
+  ///
+  void(CEF_CALLBACK* add_file)(struct _cef_drag_data_t* self,
+                               const cef_string_t* path,
+                               const cef_string_t* display_name);
+
+  ///
+  // Get the image representation of drag data. May return NULL if no image
+  // representation is available.
+  ///
+  struct _cef_image_t*(CEF_CALLBACK* get_image)(struct _cef_drag_data_t* self);
+
+  ///
+  // Get the image hotspot (drag start location relative to image dimensions).
+  ///
+  cef_point_t(CEF_CALLBACK* get_image_hotspot)(struct _cef_drag_data_t* self);
+
+  ///
+  // Returns true (1) if an image representation of drag data is available.
+  ///
+  int(CEF_CALLBACK* has_image)(struct _cef_drag_data_t* self);
+} cef_drag_data_t;
+
+///
+// Create a new cef_drag_data_t object.
+///
+CEF_EXPORT cef_drag_data_t* cef_drag_data_create();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DRAG_DATA_CAPI_H_
diff --git a/src/include/capi/cef_drag_handler_capi.h b/src/include/capi/cef_drag_handler_capi.h
new file mode 100644
index 0000000..7a6430e
--- /dev/null
+++ b/src/include/capi/cef_drag_handler_capi.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=78022908355fbf836799545e67ce2e4663b85fdf$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_DRAG_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_DRAG_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_drag_data_capi.h"
+#include "include/capi/cef_frame_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle events related to dragging. The functions
+// of this structure will be called on the UI thread.
+///
+typedef struct _cef_drag_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called when an external drag event enters the browser window. |dragData|
+  // contains the drag event data and |mask| represents the type of drag
+  // operation. Return false (0) for default drag handling behavior or true (1)
+  // to cancel the drag event.
+  ///
+  int(CEF_CALLBACK* on_drag_enter)(struct _cef_drag_handler_t* self,
+                                   struct _cef_browser_t* browser,
+                                   struct _cef_drag_data_t* dragData,
+                                   cef_drag_operations_mask_t mask);
+
+  ///
+  // Called whenever draggable regions for the browser window change. These can
+  // be specified using the '-webkit-app-region: drag/no-drag' CSS-property. If
+  // draggable regions are never defined in a document this function will also
+  // never be called. If the last draggable region is removed from a document
+  // this function will be called with an NULL vector.
+  ///
+  void(CEF_CALLBACK* on_draggable_regions_changed)(
+      struct _cef_drag_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      size_t regionsCount,
+      cef_draggable_region_t const* regions);
+} cef_drag_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_DRAG_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_extension_capi.h b/src/include/capi/cef_extension_capi.h
new file mode 100644
index 0000000..ccef427
--- /dev/null
+++ b/src/include/capi/cef_extension_capi.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=b50087959cb679e4132f0fccfd23f01f76079018$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_EXTENSION_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_EXTENSION_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_extension_handler_t;
+struct _cef_request_context_t;
+
+///
+// Object representing an extension. Methods may be called on any thread unless
+// otherwise indicated.
+///
+typedef struct _cef_extension_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the unique extension identifier. This is calculated based on the
+  // extension public key, if available, or on the extension path. See
+  // https://developer.chrome.com/extensions/manifest/key for details.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_identifier)(
+      struct _cef_extension_t* self);
+
+  ///
+  // Returns the absolute path to the extension directory on disk. This value
+  // will be prefixed with PK_DIR_RESOURCES if a relative path was passed to
+  // cef_request_context_t::LoadExtension.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_path)(struct _cef_extension_t* self);
+
+  ///
+  // Returns the extension manifest contents as a cef_dictionary_value_t object.
+  // See https://developer.chrome.com/extensions/manifest for details.
+  ///
+  struct _cef_dictionary_value_t*(CEF_CALLBACK* get_manifest)(
+      struct _cef_extension_t* self);
+
+  ///
+  // Returns true (1) if this object is the same extension as |that| object.
+  // Extensions are considered the same if identifier, path and loader context
+  // match.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_extension_t* self,
+                             struct _cef_extension_t* that);
+
+  ///
+  // Returns the handler for this extension. Will return NULL for internal
+  // extensions or if no handler was passed to
+  // cef_request_context_t::LoadExtension.
+  ///
+  struct _cef_extension_handler_t*(CEF_CALLBACK* get_handler)(
+      struct _cef_extension_t* self);
+
+  ///
+  // Returns the request context that loaded this extension. Will return NULL
+  // for internal extensions or if the extension has been unloaded. See the
+  // cef_request_context_t::LoadExtension documentation for more information
+  // about loader contexts. Must be called on the browser process UI thread.
+  ///
+  struct _cef_request_context_t*(CEF_CALLBACK* get_loader_context)(
+      struct _cef_extension_t* self);
+
+  ///
+  // Returns true (1) if this extension is currently loaded. Must be called on
+  // the browser process UI thread.
+  ///
+  int(CEF_CALLBACK* is_loaded)(struct _cef_extension_t* self);
+
+  ///
+  // Unload this extension if it is not an internal extension and is currently
+  // loaded. Will result in a call to
+  // cef_extension_handler_t::OnExtensionUnloaded on success.
+  ///
+  void(CEF_CALLBACK* unload)(struct _cef_extension_t* self);
+} cef_extension_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_EXTENSION_CAPI_H_
diff --git a/src/include/capi/cef_extension_handler_capi.h b/src/include/capi/cef_extension_handler_capi.h
new file mode 100644
index 0000000..4b3dfde
--- /dev/null
+++ b/src/include/capi/cef_extension_handler_capi.h
@@ -0,0 +1,210 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=a13b5b607d5a2108fac5fe75f5ebd2ede7eaef6a$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_EXTENSION_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_EXTENSION_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_extension_capi.h"
+#include "include/capi/cef_stream_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_client_t;
+
+///
+// Callback structure used for asynchronous continuation of
+// cef_extension_handler_t::GetExtensionResource.
+///
+typedef struct _cef_get_extension_resource_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Continue the request. Read the resource contents from |stream|.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_get_extension_resource_callback_t* self,
+                           struct _cef_stream_reader_t* stream);
+
+  ///
+  // Cancel the request.
+  ///
+  void(CEF_CALLBACK* cancel)(
+      struct _cef_get_extension_resource_callback_t* self);
+} cef_get_extension_resource_callback_t;
+
+///
+// Implement this structure to handle events related to browser extensions. The
+// functions of this structure will be called on the UI thread. See
+// cef_request_context_t::LoadExtension for information about extension loading.
+///
+typedef struct _cef_extension_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called if the cef_request_context_t::LoadExtension request fails. |result|
+  // will be the error code.
+  ///
+  void(CEF_CALLBACK* on_extension_load_failed)(
+      struct _cef_extension_handler_t* self,
+      cef_errorcode_t result);
+
+  ///
+  // Called if the cef_request_context_t::LoadExtension request succeeds.
+  // |extension| is the loaded extension.
+  ///
+  void(CEF_CALLBACK* on_extension_loaded)(struct _cef_extension_handler_t* self,
+                                          struct _cef_extension_t* extension);
+
+  ///
+  // Called after the cef_extension_t::Unload request has completed.
+  ///
+  void(CEF_CALLBACK* on_extension_unloaded)(
+      struct _cef_extension_handler_t* self,
+      struct _cef_extension_t* extension);
+
+  ///
+  // Called when an extension needs a browser to host a background script
+  // specified via the "background" manifest key. The browser will have no
+  // visible window and cannot be displayed. |extension| is the extension that
+  // is loading the background script. |url| is an internally generated
+  // reference to an HTML page that will be used to load the background script
+  // via a <script> src attribute. To allow creation of the browser optionally
+  // modify |client| and |settings| and return false (0). To cancel creation of
+  // the browser (and consequently cancel load of the background script) return
+  // true (1). Successful creation will be indicated by a call to
+  // cef_life_span_handler_t::OnAfterCreated, and
+  // cef_browser_host_t::IsBackgroundHost will return true (1) for the resulting
+  // browser. See https://developer.chrome.com/extensions/event_pages for more
+  // information about extension background script usage.
+  ///
+  int(CEF_CALLBACK* on_before_background_browser)(
+      struct _cef_extension_handler_t* self,
+      struct _cef_extension_t* extension,
+      const cef_string_t* url,
+      struct _cef_client_t** client,
+      struct _cef_browser_settings_t* settings);
+
+  ///
+  // Called when an extension API (e.g. chrome.tabs.create) requests creation of
+  // a new browser. |extension| and |browser| are the source of the API call.
+  // |active_browser| may optionally be specified via the windowId property or
+  // returned via the get_active_browser() callback and provides the default
+  // |client| and |settings| values for the new browser. |index| is the position
+  // value optionally specified via the index property. |url| is the URL that
+  // will be loaded in the browser. |active| is true (1) if the new browser
+  // should be active when opened.  To allow creation of the browser optionally
+  // modify |windowInfo|, |client| and |settings| and return false (0). To
+  // cancel creation of the browser return true (1). Successful creation will be
+  // indicated by a call to cef_life_span_handler_t::OnAfterCreated. Any
+  // modifications to |windowInfo| will be ignored if |active_browser| is
+  // wrapped in a cef_browser_view_t.
+  ///
+  int(CEF_CALLBACK* on_before_browser)(
+      struct _cef_extension_handler_t* self,
+      struct _cef_extension_t* extension,
+      struct _cef_browser_t* browser,
+      struct _cef_browser_t* active_browser,
+      int index,
+      const cef_string_t* url,
+      int active,
+      struct _cef_window_info_t* windowInfo,
+      struct _cef_client_t** client,
+      struct _cef_browser_settings_t* settings);
+
+  ///
+  // Called when no tabId is specified to an extension API call that accepts a
+  // tabId parameter (e.g. chrome.tabs.*). |extension| and |browser| are the
+  // source of the API call. Return the browser that will be acted on by the API
+  // call or return NULL to act on |browser|. The returned browser must share
+  // the same cef_request_context_t as |browser|. Incognito browsers should not
+  // be considered unless the source extension has incognito access enabled, in
+  // which case |include_incognito| will be true (1).
+  ///
+  struct _cef_browser_t*(CEF_CALLBACK* get_active_browser)(
+      struct _cef_extension_handler_t* self,
+      struct _cef_extension_t* extension,
+      struct _cef_browser_t* browser,
+      int include_incognito);
+
+  ///
+  // Called when the tabId associated with |target_browser| is specified to an
+  // extension API call that accepts a tabId parameter (e.g. chrome.tabs.*).
+  // |extension| and |browser| are the source of the API call. Return true (1)
+  // to allow access of false (0) to deny access. Access to incognito browsers
+  // should not be allowed unless the source extension has incognito access
+  // enabled, in which case |include_incognito| will be true (1).
+  ///
+  int(CEF_CALLBACK* can_access_browser)(struct _cef_extension_handler_t* self,
+                                        struct _cef_extension_t* extension,
+                                        struct _cef_browser_t* browser,
+                                        int include_incognito,
+                                        struct _cef_browser_t* target_browser);
+
+  ///
+  // Called to retrieve an extension resource that would normally be loaded from
+  // disk (e.g. if a file parameter is specified to chrome.tabs.executeScript).
+  // |extension| and |browser| are the source of the resource request. |file| is
+  // the requested relative file path. To handle the resource request return
+  // true (1) and execute |callback| either synchronously or asynchronously. For
+  // the default behavior which reads the resource from the extension directory
+  // on disk return false (0). Localization substitutions will not be applied to
+  // resources handled via this function.
+  ///
+  int(CEF_CALLBACK* get_extension_resource)(
+      struct _cef_extension_handler_t* self,
+      struct _cef_extension_t* extension,
+      struct _cef_browser_t* browser,
+      const cef_string_t* file,
+      struct _cef_get_extension_resource_callback_t* callback);
+} cef_extension_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_EXTENSION_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_file_util_capi.h b/src/include/capi/cef_file_util_capi.h
new file mode 100644
index 0000000..ce603b3
--- /dev/null
+++ b/src/include/capi/cef_file_util_capi.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=c930140791b9e7d4238110e24fe17b9566a34ec9$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_FILE_UTIL_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_FILE_UTIL_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Creates a directory and all parent directories if they don't already exist.
+// Returns true (1) on successful creation or if the directory already exists.
+// The directory is only readable by the current user. Calling this function on
+// the browser process UI or IO threads is not allowed.
+///
+CEF_EXPORT int cef_create_directory(const cef_string_t* full_path);
+
+///
+// Get the temporary directory provided by the system.
+//
+// WARNING: In general, you should use the temp directory variants below instead
+// of this function. Those variants will ensure that the proper permissions are
+// set so that other users on the system can't edit them while they're open
+// (which could lead to security issues).
+///
+CEF_EXPORT int cef_get_temp_directory(cef_string_t* temp_dir);
+
+///
+// Creates a new directory. On Windows if |prefix| is provided the new directory
+// name is in the format of "prefixyyyy". Returns true (1) on success and sets
+// |new_temp_path| to the full path of the directory that was created. The
+// directory is only readable by the current user. Calling this function on the
+// browser process UI or IO threads is not allowed.
+///
+CEF_EXPORT int cef_create_new_temp_directory(const cef_string_t* prefix,
+                                             cef_string_t* new_temp_path);
+
+///
+// Creates a directory within another directory. Extra characters will be
+// appended to |prefix| to ensure that the new directory does not have the same
+// name as an existing directory. Returns true (1) on success and sets |new_dir|
+// to the full path of the directory that was created. The directory is only
+// readable by the current user. Calling this function on the browser process UI
+// or IO threads is not allowed.
+///
+CEF_EXPORT int cef_create_temp_directory_in_directory(
+    const cef_string_t* base_dir,
+    const cef_string_t* prefix,
+    cef_string_t* new_dir);
+
+///
+// Returns true (1) if the given path exists and is a directory. Calling this
+// function on the browser process UI or IO threads is not allowed.
+///
+CEF_EXPORT int cef_directory_exists(const cef_string_t* path);
+
+///
+// Deletes the given path whether it's a file or a directory. If |path| is a
+// directory all contents will be deleted.  If |recursive| is true (1) any sub-
+// directories and their contents will also be deleted (equivalent to executing
+// "rm -rf", so use with caution). On POSIX environments if |path| is a symbolic
+// link then only the symlink will be deleted. Returns true (1) on successful
+// deletion or if |path| does not exist. Calling this function on the browser
+// process UI or IO threads is not allowed.
+///
+CEF_EXPORT int cef_delete_file(const cef_string_t* path, int recursive);
+
+///
+// Writes the contents of |src_dir| into a zip archive at |dest_file|. If
+// |include_hidden_files| is true (1) files starting with "." will be included.
+// Returns true (1) on success.  Calling this function on the browser process UI
+// or IO threads is not allowed.
+///
+CEF_EXPORT int cef_zip_directory(const cef_string_t* src_dir,
+                                 const cef_string_t* dest_file,
+                                 int include_hidden_files);
+
+///
+// Loads the existing "Certificate Revocation Lists" file that is managed by
+// Google Chrome. This file can generally be found in Chrome's User Data
+// directory (e.g. "C:\Users\[User]\AppData\Local\Google\Chrome\User Data\" on
+// Windows) and is updated periodically by Chrome's component updater service.
+// Must be called in the browser process after the context has been initialized.
+// See https://dev.chromium.org/Home/chromium-security/crlsets for background.
+///
+CEF_EXPORT void cef_load_crlsets_file(const cef_string_t* path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_FILE_UTIL_CAPI_H_
diff --git a/src/include/capi/cef_find_handler_capi.h b/src/include/capi/cef_find_handler_capi.h
new file mode 100644
index 0000000..c7be471
--- /dev/null
+++ b/src/include/capi/cef_find_handler_capi.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=2aa57426a91e10985a5e92830bc3bcd9287708d4$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_FIND_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_FIND_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle events related to find results. The
+// functions of this structure will be called on the UI thread.
+///
+typedef struct _cef_find_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called to report find results returned by cef_browser_host_t::find().
+  // |identifer| is the identifier passed to find(), |count| is the number of
+  // matches currently identified, |selectionRect| is the location of where the
+  // match was found (in window coordinates), |activeMatchOrdinal| is the
+  // current position in the search results, and |finalUpdate| is true (1) if
+  // this is the last find notification.
+  ///
+  void(CEF_CALLBACK* on_find_result)(struct _cef_find_handler_t* self,
+                                     struct _cef_browser_t* browser,
+                                     int identifier,
+                                     int count,
+                                     const cef_rect_t* selectionRect,
+                                     int activeMatchOrdinal,
+                                     int finalUpdate);
+} cef_find_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_FIND_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_focus_handler_capi.h b/src/include/capi/cef_focus_handler_capi.h
new file mode 100644
index 0000000..a5c2ecc
--- /dev/null
+++ b/src/include/capi/cef_focus_handler_capi.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=091dd994f37070e9d7c27d0e2f7411ea9cf068f5$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_FOCUS_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_FOCUS_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_dom_capi.h"
+#include "include/capi/cef_frame_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle events related to focus. The functions of
+// this structure will be called on the UI thread.
+///
+typedef struct _cef_focus_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called when the browser component is about to loose focus. For instance, if
+  // focus was on the last HTML element and the user pressed the TAB key. |next|
+  // will be true (1) if the browser is giving focus to the next component and
+  // false (0) if the browser is giving focus to the previous component.
+  ///
+  void(CEF_CALLBACK* on_take_focus)(struct _cef_focus_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    int next);
+
+  ///
+  // Called when the browser component is requesting focus. |source| indicates
+  // where the focus request is originating from. Return false (0) to allow the
+  // focus to be set or true (1) to cancel setting the focus.
+  ///
+  int(CEF_CALLBACK* on_set_focus)(struct _cef_focus_handler_t* self,
+                                  struct _cef_browser_t* browser,
+                                  cef_focus_source_t source);
+
+  ///
+  // Called when the browser component has received focus.
+  ///
+  void(CEF_CALLBACK* on_got_focus)(struct _cef_focus_handler_t* self,
+                                   struct _cef_browser_t* browser);
+} cef_focus_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_FOCUS_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_frame_capi.h b/src/include/capi/cef_frame_capi.h
new file mode 100644
index 0000000..3be029d
--- /dev/null
+++ b/src/include/capi/cef_frame_capi.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=d8f114b44d02d96b5da0ec399c99091b9ceb6871$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_FRAME_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_FRAME_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_dom_capi.h"
+#include "include/capi/cef_process_message_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_stream_capi.h"
+#include "include/capi/cef_string_visitor_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_browser_t;
+struct _cef_urlrequest_client_t;
+struct _cef_urlrequest_t;
+struct _cef_v8context_t;
+
+///
+// Structure used to represent a frame in the browser window. When used in the
+// browser process the functions of this structure may be called on any thread
+// unless otherwise indicated in the comments. When used in the render process
+// the functions of this structure may only be called on the main thread.
+///
+typedef struct _cef_frame_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // True if this object is currently attached to a valid frame.
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_frame_t* self);
+
+  ///
+  // Execute undo in this frame.
+  ///
+  void(CEF_CALLBACK* undo)(struct _cef_frame_t* self);
+
+  ///
+  // Execute redo in this frame.
+  ///
+  void(CEF_CALLBACK* redo)(struct _cef_frame_t* self);
+
+  ///
+  // Execute cut in this frame.
+  ///
+  void(CEF_CALLBACK* cut)(struct _cef_frame_t* self);
+
+  ///
+  // Execute copy in this frame.
+  ///
+  void(CEF_CALLBACK* copy)(struct _cef_frame_t* self);
+
+  ///
+  // Execute paste in this frame.
+  ///
+  void(CEF_CALLBACK* paste)(struct _cef_frame_t* self);
+
+  ///
+  // Execute delete in this frame.
+  ///
+  void(CEF_CALLBACK* del)(struct _cef_frame_t* self);
+
+  ///
+  // Execute select all in this frame.
+  ///
+  void(CEF_CALLBACK* select_all)(struct _cef_frame_t* self);
+
+  ///
+  // Save this frame's HTML source to a temporary file and open it in the
+  // default text viewing application. This function can only be called from the
+  // browser process.
+  ///
+  void(CEF_CALLBACK* view_source)(struct _cef_frame_t* self);
+
+  ///
+  // Retrieve this frame's HTML source as a string sent to the specified
+  // visitor.
+  ///
+  void(CEF_CALLBACK* get_source)(struct _cef_frame_t* self,
+                                 struct _cef_string_visitor_t* visitor);
+
+  ///
+  // Retrieve this frame's display text as a string sent to the specified
+  // visitor.
+  ///
+  void(CEF_CALLBACK* get_text)(struct _cef_frame_t* self,
+                               struct _cef_string_visitor_t* visitor);
+
+  ///
+  // Load the request represented by the |request| object.
+  //
+  // WARNING: This function will fail with "bad IPC message" reason
+  // INVALID_INITIATOR_ORIGIN (213) unless you first navigate to the request
+  // origin using some other mechanism (LoadURL, link click, etc).
+  ///
+  void(CEF_CALLBACK* load_request)(struct _cef_frame_t* self,
+                                   struct _cef_request_t* request);
+
+  ///
+  // Load the specified |url|.
+  ///
+  void(CEF_CALLBACK* load_url)(struct _cef_frame_t* self,
+                               const cef_string_t* url);
+
+  ///
+  // Execute a string of JavaScript code in this frame. The |script_url|
+  // parameter is the URL where the script in question can be found, if any. The
+  // renderer may request this URL to show the developer the source of the
+  // error.  The |start_line| parameter is the base line number to use for error
+  // reporting.
+  ///
+  void(CEF_CALLBACK* execute_java_script)(struct _cef_frame_t* self,
+                                          const cef_string_t* code,
+                                          const cef_string_t* script_url,
+                                          int start_line);
+
+  ///
+  // Returns true (1) if this is the main (top-level) frame.
+  ///
+  int(CEF_CALLBACK* is_main)(struct _cef_frame_t* self);
+
+  ///
+  // Returns true (1) if this is the focused frame.
+  ///
+  int(CEF_CALLBACK* is_focused)(struct _cef_frame_t* self);
+
+  ///
+  // Returns the name for this frame. If the frame has an assigned name (for
+  // example, set via the iframe "name" attribute) then that value will be
+  // returned. Otherwise a unique name will be constructed based on the frame
+  // parent hierarchy. The main (top-level) frame will always have an NULL name
+  // value.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_name)(struct _cef_frame_t* self);
+
+  ///
+  // Returns the globally unique identifier for this frame or < 0 if the
+  // underlying frame does not yet exist.
+  ///
+  int64(CEF_CALLBACK* get_identifier)(struct _cef_frame_t* self);
+
+  ///
+  // Returns the parent of this frame or NULL if this is the main (top-level)
+  // frame.
+  ///
+  struct _cef_frame_t*(CEF_CALLBACK* get_parent)(struct _cef_frame_t* self);
+
+  ///
+  // Returns the URL currently loaded in this frame.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_url)(struct _cef_frame_t* self);
+
+  ///
+  // Returns the browser that this frame belongs to.
+  ///
+  struct _cef_browser_t*(CEF_CALLBACK* get_browser)(struct _cef_frame_t* self);
+
+  ///
+  // Get the V8 context associated with the frame. This function can only be
+  // called from the render process.
+  ///
+  struct _cef_v8context_t*(CEF_CALLBACK* get_v8context)(
+      struct _cef_frame_t* self);
+
+  ///
+  // Visit the DOM document. This function can only be called from the render
+  // process.
+  ///
+  void(CEF_CALLBACK* visit_dom)(struct _cef_frame_t* self,
+                                struct _cef_domvisitor_t* visitor);
+
+  ///
+  // Create a new URL request that will be treated as originating from this
+  // frame and the associated browser. This request may be intercepted by the
+  // client via cef_resource_request_handler_t or cef_scheme_handler_factory_t.
+  // Use cef_urlrequest_t::Create instead if you do not want the request to have
+  // this association, in which case it may be handled differently (see
+  // documentation on that function). Requests may originate from both the
+  // browser process and the render process.
+  //
+  // For requests originating from the browser process:
+  //   - POST data may only contain a single element of type PDE_TYPE_FILE or
+  //     PDE_TYPE_BYTES.
+  // For requests originating from the render process:
+  //   - POST data may only contain a single element of type PDE_TYPE_BYTES.
+  //   - If the response contains Content-Disposition or Mime-Type header values
+  //     that would not normally be rendered then the response may receive
+  //     special handling inside the browser (for example, via the file download
+  //     code path instead of the URL request code path).
+  //
+  // The |request| object will be marked as read-only after calling this
+  // function.
+  ///
+  struct _cef_urlrequest_t*(CEF_CALLBACK* create_urlrequest)(
+      struct _cef_frame_t* self,
+      struct _cef_request_t* request,
+      struct _cef_urlrequest_client_t* client);
+
+  ///
+  // Send a message to the specified |target_process|. Message delivery is not
+  // guaranteed in all cases (for example, if the browser is closing,
+  // navigating, or if the target process crashes). Send an ACK message back
+  // from the target process if confirmation is required.
+  ///
+  void(CEF_CALLBACK* send_process_message)(
+      struct _cef_frame_t* self,
+      cef_process_id_t target_process,
+      struct _cef_process_message_t* message);
+} cef_frame_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_FRAME_CAPI_H_
diff --git a/src/include/capi/cef_image_capi.h b/src/include/capi/cef_image_capi.h
new file mode 100644
index 0000000..d3836d6
--- /dev/null
+++ b/src/include/capi/cef_image_capi.h
@@ -0,0 +1,205 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=5afa8e95e6e7bddbd3c442e99b4c2843efb18c49$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_IMAGE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_IMAGE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Container for a single image represented at different scale factors. All
+// image representations should be the same size in density independent pixel
+// (DIP) units. For example, if the image at scale factor 1.0 is 100x100 pixels
+// then the image at scale factor 2.0 should be 200x200 pixels -- both images
+// will display with a DIP size of 100x100 units. The functions of this
+// structure can be called on any browser process thread.
+///
+typedef struct _cef_image_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this Image is NULL.
+  ///
+  int(CEF_CALLBACK* is_empty)(struct _cef_image_t* self);
+
+  ///
+  // Returns true (1) if this Image and |that| Image share the same underlying
+  // storage. Will also return true (1) if both images are NULL.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_image_t* self,
+                             struct _cef_image_t* that);
+
+  ///
+  // Add a bitmap image representation for |scale_factor|. Only 32-bit RGBA/BGRA
+  // formats are supported. |pixel_width| and |pixel_height| are the bitmap
+  // representation size in pixel coordinates. |pixel_data| is the array of
+  // pixel data and should be |pixel_width| x |pixel_height| x 4 bytes in size.
+  // |color_type| and |alpha_type| values specify the pixel format.
+  ///
+  int(CEF_CALLBACK* add_bitmap)(struct _cef_image_t* self,
+                                float scale_factor,
+                                int pixel_width,
+                                int pixel_height,
+                                cef_color_type_t color_type,
+                                cef_alpha_type_t alpha_type,
+                                const void* pixel_data,
+                                size_t pixel_data_size);
+
+  ///
+  // Add a PNG image representation for |scale_factor|. |png_data| is the image
+  // data of size |png_data_size|. Any alpha transparency in the PNG data will
+  // be maintained.
+  ///
+  int(CEF_CALLBACK* add_png)(struct _cef_image_t* self,
+                             float scale_factor,
+                             const void* png_data,
+                             size_t png_data_size);
+
+  ///
+  // Create a JPEG image representation for |scale_factor|. |jpeg_data| is the
+  // image data of size |jpeg_data_size|. The JPEG format does not support
+  // transparency so the alpha byte will be set to 0xFF for all pixels.
+  ///
+  int(CEF_CALLBACK* add_jpeg)(struct _cef_image_t* self,
+                              float scale_factor,
+                              const void* jpeg_data,
+                              size_t jpeg_data_size);
+
+  ///
+  // Returns the image width in density independent pixel (DIP) units.
+  ///
+  size_t(CEF_CALLBACK* get_width)(struct _cef_image_t* self);
+
+  ///
+  // Returns the image height in density independent pixel (DIP) units.
+  ///
+  size_t(CEF_CALLBACK* get_height)(struct _cef_image_t* self);
+
+  ///
+  // Returns true (1) if this image contains a representation for
+  // |scale_factor|.
+  ///
+  int(CEF_CALLBACK* has_representation)(struct _cef_image_t* self,
+                                        float scale_factor);
+
+  ///
+  // Removes the representation for |scale_factor|. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* remove_representation)(struct _cef_image_t* self,
+                                           float scale_factor);
+
+  ///
+  // Returns information for the representation that most closely matches
+  // |scale_factor|. |actual_scale_factor| is the actual scale factor for the
+  // representation. |pixel_width| and |pixel_height| are the representation
+  // size in pixel coordinates. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* get_representation_info)(struct _cef_image_t* self,
+                                             float scale_factor,
+                                             float* actual_scale_factor,
+                                             int* pixel_width,
+                                             int* pixel_height);
+
+  ///
+  // Returns the bitmap representation that most closely matches |scale_factor|.
+  // Only 32-bit RGBA/BGRA formats are supported. |color_type| and |alpha_type|
+  // values specify the desired output pixel format. |pixel_width| and
+  // |pixel_height| are the output representation size in pixel coordinates.
+  // Returns a cef_binary_value_t containing the pixel data on success or NULL
+  // on failure.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_as_bitmap)(
+      struct _cef_image_t* self,
+      float scale_factor,
+      cef_color_type_t color_type,
+      cef_alpha_type_t alpha_type,
+      int* pixel_width,
+      int* pixel_height);
+
+  ///
+  // Returns the PNG representation that most closely matches |scale_factor|. If
+  // |with_transparency| is true (1) any alpha transparency in the image will be
+  // represented in the resulting PNG data. |pixel_width| and |pixel_height| are
+  // the output representation size in pixel coordinates. Returns a
+  // cef_binary_value_t containing the PNG image data on success or NULL on
+  // failure.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_as_png)(
+      struct _cef_image_t* self,
+      float scale_factor,
+      int with_transparency,
+      int* pixel_width,
+      int* pixel_height);
+
+  ///
+  // Returns the JPEG representation that most closely matches |scale_factor|.
+  // |quality| determines the compression level with 0 == lowest and 100 ==
+  // highest. The JPEG format does not support alpha transparency and the alpha
+  // channel, if any, will be discarded. |pixel_width| and |pixel_height| are
+  // the output representation size in pixel coordinates. Returns a
+  // cef_binary_value_t containing the JPEG image data on success or NULL on
+  // failure.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_as_jpeg)(
+      struct _cef_image_t* self,
+      float scale_factor,
+      int quality,
+      int* pixel_width,
+      int* pixel_height);
+} cef_image_t;
+
+///
+// Create a new cef_image_t. It will initially be NULL. Use the Add*() functions
+// to add representations at different scale factors.
+///
+CEF_EXPORT cef_image_t* cef_image_create();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_IMAGE_CAPI_H_
diff --git a/src/include/capi/cef_jsdialog_handler_capi.h b/src/include/capi/cef_jsdialog_handler_capi.h
new file mode 100644
index 0000000..0aed471
--- /dev/null
+++ b/src/include/capi/cef_jsdialog_handler_capi.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=e68da1a5db612699b7b727edea2bb629f5d67103$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_JSDIALOG_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_JSDIALOG_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback structure used for asynchronous continuation of JavaScript dialog
+// requests.
+///
+typedef struct _cef_jsdialog_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Continue the JS dialog request. Set |success| to true (1) if the OK button
+  // was pressed. The |user_input| value should be specified for prompt dialogs.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_jsdialog_callback_t* self,
+                           int success,
+                           const cef_string_t* user_input);
+} cef_jsdialog_callback_t;
+
+///
+// Implement this structure to handle events related to JavaScript dialogs. The
+// functions of this structure will be called on the UI thread.
+///
+typedef struct _cef_jsdialog_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called to run a JavaScript dialog. If |origin_url| is non-NULL it can be
+  // passed to the CefFormatUrlForSecurityDisplay function to retrieve a secure
+  // and user-friendly display string. The |default_prompt_text| value will be
+  // specified for prompt dialogs only. Set |suppress_message| to true (1) and
+  // return false (0) to suppress the message (suppressing messages is
+  // preferable to immediately executing the callback as this is used to detect
+  // presumably malicious behavior like spamming alert messages in
+  // onbeforeunload). Set |suppress_message| to false (0) and return false (0)
+  // to use the default implementation (the default implementation will show one
+  // modal dialog at a time and suppress any additional dialog requests until
+  // the displayed dialog is dismissed). Return true (1) if the application will
+  // use a custom dialog or if the callback has been executed immediately.
+  // Custom dialogs may be either modal or modeless. If a custom dialog is used
+  // the application must execute |callback| once the custom dialog is
+  // dismissed.
+  ///
+  int(CEF_CALLBACK* on_jsdialog)(struct _cef_jsdialog_handler_t* self,
+                                 struct _cef_browser_t* browser,
+                                 const cef_string_t* origin_url,
+                                 cef_jsdialog_type_t dialog_type,
+                                 const cef_string_t* message_text,
+                                 const cef_string_t* default_prompt_text,
+                                 struct _cef_jsdialog_callback_t* callback,
+                                 int* suppress_message);
+
+  ///
+  // Called to run a dialog asking the user if they want to leave a page. Return
+  // false (0) to use the default dialog implementation. Return true (1) if the
+  // application will use a custom dialog or if the callback has been executed
+  // immediately. Custom dialogs may be either modal or modeless. If a custom
+  // dialog is used the application must execute |callback| once the custom
+  // dialog is dismissed.
+  ///
+  int(CEF_CALLBACK* on_before_unload_dialog)(
+      struct _cef_jsdialog_handler_t* self,
+      struct _cef_browser_t* browser,
+      const cef_string_t* message_text,
+      int is_reload,
+      struct _cef_jsdialog_callback_t* callback);
+
+  ///
+  // Called to cancel any pending dialogs and reset any saved dialog state. Will
+  // be called due to events like page navigation irregardless of whether any
+  // dialogs are currently pending.
+  ///
+  void(CEF_CALLBACK* on_reset_dialog_state)(
+      struct _cef_jsdialog_handler_t* self,
+      struct _cef_browser_t* browser);
+
+  ///
+  // Called when the default implementation dialog is closed.
+  ///
+  void(CEF_CALLBACK* on_dialog_closed)(struct _cef_jsdialog_handler_t* self,
+                                       struct _cef_browser_t* browser);
+} cef_jsdialog_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_JSDIALOG_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_keyboard_handler_capi.h b/src/include/capi/cef_keyboard_handler_capi.h
new file mode 100644
index 0000000..356803d
--- /dev/null
+++ b/src/include/capi/cef_keyboard_handler_capi.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=70108de432674485dee079e541e0dacd6a437961$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_KEYBOARD_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_KEYBOARD_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle events related to keyboard input. The
+// functions of this structure will be called on the UI thread.
+///
+typedef struct _cef_keyboard_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called before a keyboard event is sent to the renderer. |event| contains
+  // information about the keyboard event. |os_event| is the operating system
+  // event message, if any. Return true (1) if the event was handled or false
+  // (0) otherwise. If the event will be handled in on_key_event() as a keyboard
+  // shortcut set |is_keyboard_shortcut| to true (1) and return false (0).
+  ///
+  int(CEF_CALLBACK* on_pre_key_event)(struct _cef_keyboard_handler_t* self,
+                                      struct _cef_browser_t* browser,
+                                      const struct _cef_key_event_t* event,
+                                      cef_event_handle_t os_event,
+                                      int* is_keyboard_shortcut);
+
+  ///
+  // Called after the renderer and JavaScript in the page has had a chance to
+  // handle the event. |event| contains information about the keyboard event.
+  // |os_event| is the operating system event message, if any. Return true (1)
+  // if the keyboard event was handled or false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* on_key_event)(struct _cef_keyboard_handler_t* self,
+                                  struct _cef_browser_t* browser,
+                                  const struct _cef_key_event_t* event,
+                                  cef_event_handle_t os_event);
+} cef_keyboard_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_KEYBOARD_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_life_span_handler_capi.h b/src/include/capi/cef_life_span_handler_capi.h
new file mode 100644
index 0000000..c691fcf
--- /dev/null
+++ b/src/include/capi/cef_life_span_handler_capi.h
@@ -0,0 +1,221 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=d6e91d55d41f729dca94ba5766f57849f29d0796$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_LIFE_SPAN_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_LIFE_SPAN_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_client_t;
+
+///
+// Implement this structure to handle events related to browser life span. The
+// functions of this structure will be called on the UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_life_span_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the UI thread before a new popup browser is created. The
+  // |browser| and |frame| values represent the source of the popup request. The
+  // |target_url| and |target_frame_name| values indicate where the popup
+  // browser should navigate and may be NULL if not specified with the request.
+  // The |target_disposition| value indicates where the user intended to open
+  // the popup (e.g. current tab, new tab, etc). The |user_gesture| value will
+  // be true (1) if the popup was opened via explicit user gesture (e.g.
+  // clicking a link) or false (0) if the popup opened automatically (e.g. via
+  // the DomContentLoaded event). The |popupFeatures| structure contains
+  // additional information about the requested popup window. To allow creation
+  // of the popup browser optionally modify |windowInfo|, |client|, |settings|
+  // and |no_javascript_access| and return false (0). To cancel creation of the
+  // popup browser return true (1). The |client| and |settings| values will
+  // default to the source browser's values. If the |no_javascript_access| value
+  // is set to false (0) the new browser will not be scriptable and may not be
+  // hosted in the same renderer process as the source browser. Any
+  // modifications to |windowInfo| will be ignored if the parent browser is
+  // wrapped in a cef_browser_view_t. Popup browser creation will be canceled if
+  // the parent browser is destroyed before the popup browser creation completes
+  // (indicated by a call to OnAfterCreated for the popup browser). The
+  // |extra_info| parameter provides an opportunity to specify extra information
+  // specific to the created popup browser that will be passed to
+  // cef_render_process_handler_t::on_browser_created() in the render process.
+  ///
+  int(CEF_CALLBACK* on_before_popup)(
+      struct _cef_life_span_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      const cef_string_t* target_url,
+      const cef_string_t* target_frame_name,
+      cef_window_open_disposition_t target_disposition,
+      int user_gesture,
+      const struct _cef_popup_features_t* popupFeatures,
+      struct _cef_window_info_t* windowInfo,
+      struct _cef_client_t** client,
+      struct _cef_browser_settings_t* settings,
+      struct _cef_dictionary_value_t** extra_info,
+      int* no_javascript_access);
+
+  ///
+  // Called after a new browser is created. This callback will be the first
+  // notification that references |browser|.
+  ///
+  void(CEF_CALLBACK* on_after_created)(struct _cef_life_span_handler_t* self,
+                                       struct _cef_browser_t* browser);
+
+  ///
+  // Called when a browser has recieved a request to close. This may result
+  // directly from a call to cef_browser_host_t::*close_browser() or indirectly
+  // if the browser is parented to a top-level window created by CEF and the
+  // user attempts to close that window (by clicking the 'X', for example). The
+  // do_close() function will be called after the JavaScript 'onunload' event
+  // has been fired.
+  //
+  // An application should handle top-level owner window close notifications by
+  // calling cef_browser_host_t::try_close_browser() or
+  // cef_browser_host_t::CloseBrowser(false (0)) instead of allowing the window
+  // to close immediately (see the examples below). This gives CEF an
+  // opportunity to process the 'onbeforeunload' event and optionally cancel the
+  // close before do_close() is called.
+  //
+  // When windowed rendering is enabled CEF will internally create a window or
+  // view to host the browser. In that case returning false (0) from do_close()
+  // will send the standard close notification to the browser's top-level owner
+  // window (e.g. WM_CLOSE on Windows, performClose: on OS X, "delete_event" on
+  // Linux or cef_window_delegate_t::can_close() callback from Views). If the
+  // browser's host window/view has already been destroyed (via view hierarchy
+  // tear-down, for example) then do_close() will not be called for that browser
+  // since is no longer possible to cancel the close.
+  //
+  // When windowed rendering is disabled returning false (0) from do_close()
+  // will cause the browser object to be destroyed immediately.
+  //
+  // If the browser's top-level owner window requires a non-standard close
+  // notification then send that notification from do_close() and return true
+  // (1).
+  //
+  // The cef_life_span_handler_t::on_before_close() function will be called
+  // after do_close() (if do_close() is called) and immediately before the
+  // browser object is destroyed. The application should only exit after
+  // on_before_close() has been called for all existing browsers.
+  //
+  // The below examples describe what should happen during window close when the
+  // browser is parented to an application-provided top-level window.
+  //
+  // Example 1: Using cef_browser_host_t::try_close_browser(). This is
+  // recommended for clients using standard close handling and windows created
+  // on the browser process UI thread. 1.  User clicks the window close button
+  // which sends a close notification to
+  //     the application's top-level window.
+  // 2.  Application's top-level window receives the close notification and
+  //     calls TryCloseBrowser() (which internally calls CloseBrowser(false)).
+  //     TryCloseBrowser() returns false so the client cancels the window close.
+  // 3.  JavaScript 'onbeforeunload' handler executes and shows the close
+  //     confirmation dialog (which can be overridden via
+  //     CefJSDialogHandler::OnBeforeUnloadDialog()).
+  // 4.  User approves the close. 5.  JavaScript 'onunload' handler executes. 6.
+  // CEF sends a close notification to the application's top-level window
+  //     (because DoClose() returned false by default).
+  // 7.  Application's top-level window receives the close notification and
+  //     calls TryCloseBrowser(). TryCloseBrowser() returns true so the client
+  //     allows the window close.
+  // 8.  Application's top-level window is destroyed. 9.  Application's
+  // on_before_close() handler is called and the browser object
+  //     is destroyed.
+  // 10. Application exits by calling cef_quit_message_loop() if no other
+  // browsers
+  //     exist.
+  //
+  // Example 2: Using cef_browser_host_t::CloseBrowser(false (0)) and
+  // implementing the do_close() callback. This is recommended for clients using
+  // non-standard close handling or windows that were not created on the browser
+  // process UI thread. 1.  User clicks the window close button which sends a
+  // close notification to
+  //     the application's top-level window.
+  // 2.  Application's top-level window receives the close notification and:
+  //     A. Calls CefBrowserHost::CloseBrowser(false).
+  //     B. Cancels the window close.
+  // 3.  JavaScript 'onbeforeunload' handler executes and shows the close
+  //     confirmation dialog (which can be overridden via
+  //     CefJSDialogHandler::OnBeforeUnloadDialog()).
+  // 4.  User approves the close. 5.  JavaScript 'onunload' handler executes. 6.
+  // Application's do_close() handler is called. Application will:
+  //     A. Set a flag to indicate that the next close attempt will be allowed.
+  //     B. Return false.
+  // 7.  CEF sends an close notification to the application's top-level window.
+  // 8.  Application's top-level window receives the close notification and
+  //     allows the window to close based on the flag from #6B.
+  // 9.  Application's top-level window is destroyed. 10. Application's
+  // on_before_close() handler is called and the browser object
+  //     is destroyed.
+  // 11. Application exits by calling cef_quit_message_loop() if no other
+  // browsers
+  //     exist.
+  ///
+  int(CEF_CALLBACK* do_close)(struct _cef_life_span_handler_t* self,
+                              struct _cef_browser_t* browser);
+
+  ///
+  // Called just before a browser is destroyed. Release all references to the
+  // browser object and do not attempt to execute any functions on the browser
+  // object (other than GetIdentifier or IsSame) after this callback returns.
+  // This callback will be the last notification that references |browser| on
+  // the UI thread. Any in-progress network requests associated with |browser|
+  // will be aborted when the browser is destroyed, and
+  // cef_resource_request_handler_t callbacks related to those requests may
+  // still arrive on the IO thread after this function is called. See do_close()
+  // documentation for additional usage information.
+  ///
+  void(CEF_CALLBACK* on_before_close)(struct _cef_life_span_handler_t* self,
+                                      struct _cef_browser_t* browser);
+} cef_life_span_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_LIFE_SPAN_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_load_handler_capi.h b/src/include/capi/cef_load_handler_capi.h
new file mode 100644
index 0000000..186eea3
--- /dev/null
+++ b/src/include/capi/cef_load_handler_capi.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=fa3cb1461b9d363c6c7d961f9e291c2fe736170e$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_LOAD_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_LOAD_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle events related to browser load status. The
+// functions of this structure will be called on the browser process UI thread
+// or render process main thread (TID_RENDERER).
+///
+typedef struct _cef_load_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called when the loading state has changed. This callback will be executed
+  // twice -- once when loading is initiated either programmatically or by user
+  // action, and once when loading is terminated due to completion, cancellation
+  // of failure. It will be called before any calls to OnLoadStart and after all
+  // calls to OnLoadError and/or OnLoadEnd.
+  ///
+  void(CEF_CALLBACK* on_loading_state_change)(struct _cef_load_handler_t* self,
+                                              struct _cef_browser_t* browser,
+                                              int isLoading,
+                                              int canGoBack,
+                                              int canGoForward);
+
+  ///
+  // Called after a navigation has been committed and before the browser begins
+  // loading contents in the frame. The |frame| value will never be NULL -- call
+  // the is_main() function to check if this frame is the main frame.
+  // |transition_type| provides information about the source of the navigation
+  // and an accurate value is only available in the browser process. Multiple
+  // frames may be loading at the same time. Sub-frames may start or continue
+  // loading after the main frame load has ended. This function will not be
+  // called for same page navigations (fragments, history state, etc.) or for
+  // navigations that fail or are canceled before commit. For notification of
+  // overall browser load status use OnLoadingStateChange instead.
+  ///
+  void(CEF_CALLBACK* on_load_start)(struct _cef_load_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    struct _cef_frame_t* frame,
+                                    cef_transition_type_t transition_type);
+
+  ///
+  // Called when the browser is done loading a frame. The |frame| value will
+  // never be NULL -- call the is_main() function to check if this frame is the
+  // main frame. Multiple frames may be loading at the same time. Sub-frames may
+  // start or continue loading after the main frame load has ended. This
+  // function will not be called for same page navigations (fragments, history
+  // state, etc.) or for navigations that fail or are canceled before commit.
+  // For notification of overall browser load status use OnLoadingStateChange
+  // instead.
+  ///
+  void(CEF_CALLBACK* on_load_end)(struct _cef_load_handler_t* self,
+                                  struct _cef_browser_t* browser,
+                                  struct _cef_frame_t* frame,
+                                  int httpStatusCode);
+
+  ///
+  // Called when a navigation fails or is canceled. This function may be called
+  // by itself if before commit or in combination with OnLoadStart/OnLoadEnd if
+  // after commit. |errorCode| is the error code number, |errorText| is the
+  // error text and |failedUrl| is the URL that failed to load. See
+  // net\base\net_error_list.h for complete descriptions of the error codes.
+  ///
+  void(CEF_CALLBACK* on_load_error)(struct _cef_load_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    struct _cef_frame_t* frame,
+                                    cef_errorcode_t errorCode,
+                                    const cef_string_t* errorText,
+                                    const cef_string_t* failedUrl);
+} cef_load_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_LOAD_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_media_router_capi.h b/src/include/capi/cef_media_router_capi.h
new file mode 100644
index 0000000..a26f12e
--- /dev/null
+++ b/src/include/capi/cef_media_router_capi.h
@@ -0,0 +1,355 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=9d5077fdb3fe7fa0ff1141f839988ac821eba4bf$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_MEDIA_ROUTER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_MEDIA_ROUTER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_registration_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_media_observer_t;
+struct _cef_media_route_create_callback_t;
+struct _cef_media_route_t;
+struct _cef_media_sink_device_info_callback_t;
+struct _cef_media_sink_t;
+struct _cef_media_source_t;
+
+///
+// Supports discovery of and communication with media devices on the local
+// network via the Cast and DIAL protocols. The functions of this structure may
+// be called on any browser process thread unless otherwise indicated.
+///
+typedef struct _cef_media_router_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Add an observer for MediaRouter events. The observer will remain registered
+  // until the returned Registration object is destroyed.
+  ///
+  struct _cef_registration_t*(CEF_CALLBACK* add_observer)(
+      struct _cef_media_router_t* self,
+      struct _cef_media_observer_t* observer);
+
+  ///
+  // Returns a MediaSource object for the specified media source URN. Supported
+  // URN schemes include "cast:" and "dial:", and will be already known by the
+  // client application (e.g. "cast:<appId>?clientId=<clientId>").
+  ///
+  struct _cef_media_source_t*(CEF_CALLBACK* get_source)(
+      struct _cef_media_router_t* self,
+      const cef_string_t* urn);
+
+  ///
+  // Trigger an asynchronous call to cef_media_observer_t::OnSinks on all
+  // registered observers.
+  ///
+  void(CEF_CALLBACK* notify_current_sinks)(struct _cef_media_router_t* self);
+
+  ///
+  // Create a new route between |source| and |sink|. Source and sink must be
+  // valid, compatible (as reported by cef_media_sink_t::IsCompatibleWith), and
+  // a route between them must not already exist. |callback| will be executed on
+  // success or failure. If route creation succeeds it will also trigger an
+  // asynchronous call to cef_media_observer_t::OnRoutes on all registered
+  // observers.
+  ///
+  void(CEF_CALLBACK* create_route)(
+      struct _cef_media_router_t* self,
+      struct _cef_media_source_t* source,
+      struct _cef_media_sink_t* sink,
+      struct _cef_media_route_create_callback_t* callback);
+
+  ///
+  // Trigger an asynchronous call to cef_media_observer_t::OnRoutes on all
+  // registered observers.
+  ///
+  void(CEF_CALLBACK* notify_current_routes)(struct _cef_media_router_t* self);
+} cef_media_router_t;
+
+///
+// Returns the MediaRouter object associated with the global request context.
+// Equivalent to calling cef_request_context_t::cef_request_context_get_global_c
+// ontext()->get_media_router().
+///
+CEF_EXPORT cef_media_router_t* cef_media_router_get_global();
+
+///
+// Implemented by the client to observe MediaRouter events and registered via
+// cef_media_router_t::AddObserver. The functions of this structure will be
+// called on the browser process UI thread.
+///
+typedef struct _cef_media_observer_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // The list of available media sinks has changed or
+  // cef_media_router_t::NotifyCurrentSinks was called.
+  ///
+  void(CEF_CALLBACK* on_sinks)(struct _cef_media_observer_t* self,
+                               size_t sinksCount,
+                               struct _cef_media_sink_t* const* sinks);
+
+  ///
+  // The list of available media routes has changed or
+  // cef_media_router_t::NotifyCurrentRoutes was called.
+  ///
+  void(CEF_CALLBACK* on_routes)(struct _cef_media_observer_t* self,
+                                size_t routesCount,
+                                struct _cef_media_route_t* const* routes);
+
+  ///
+  // The connection state of |route| has changed.
+  ///
+  void(CEF_CALLBACK* on_route_state_changed)(
+      struct _cef_media_observer_t* self,
+      struct _cef_media_route_t* route,
+      cef_media_route_connection_state_t state);
+
+  ///
+  // A message was recieved over |route|. |message| is only valid for the scope
+  // of this callback and should be copied if necessary.
+  ///
+  void(CEF_CALLBACK* on_route_message_received)(
+      struct _cef_media_observer_t* self,
+      struct _cef_media_route_t* route,
+      const void* message,
+      size_t message_size);
+} cef_media_observer_t;
+
+///
+// Represents the route between a media source and sink. Instances of this
+// object are created via cef_media_router_t::CreateRoute and retrieved via
+// cef_media_observer_t::OnRoutes. Contains the status and metadata of a routing
+// operation. The functions of this structure may be called on any browser
+// process thread unless otherwise indicated.
+///
+typedef struct _cef_media_route_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the ID for this route.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_id)(struct _cef_media_route_t* self);
+
+  ///
+  // Returns the source associated with this route.
+  ///
+  struct _cef_media_source_t*(CEF_CALLBACK* get_source)(
+      struct _cef_media_route_t* self);
+
+  ///
+  // Returns the sink associated with this route.
+  ///
+  struct _cef_media_sink_t*(CEF_CALLBACK* get_sink)(
+      struct _cef_media_route_t* self);
+
+  ///
+  // Send a message over this route. |message| will be copied if necessary.
+  ///
+  void(CEF_CALLBACK* send_route_message)(struct _cef_media_route_t* self,
+                                         const void* message,
+                                         size_t message_size);
+
+  ///
+  // Terminate this route. Will result in an asynchronous call to
+  // cef_media_observer_t::OnRoutes on all registered observers.
+  ///
+  void(CEF_CALLBACK* terminate)(struct _cef_media_route_t* self);
+} cef_media_route_t;
+
+///
+// Callback structure for cef_media_router_t::CreateRoute. The functions of this
+// structure will be called on the browser process UI thread.
+///
+typedef struct _cef_media_route_create_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be executed when the route creation has finished. |result|
+  // will be CEF_MRCR_OK if the route creation succeeded. |error| will be a
+  // description of the error if the route creation failed. |route| is the
+  // resulting route, or NULL if the route creation failed.
+  ///
+  void(CEF_CALLBACK* on_media_route_create_finished)(
+      struct _cef_media_route_create_callback_t* self,
+      cef_media_route_create_result_t result,
+      const cef_string_t* error,
+      struct _cef_media_route_t* route);
+} cef_media_route_create_callback_t;
+
+///
+// Represents a sink to which media can be routed. Instances of this object are
+// retrieved via cef_media_observer_t::OnSinks. The functions of this structure
+// may be called on any browser process thread unless otherwise indicated.
+///
+typedef struct _cef_media_sink_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the ID for this sink.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_id)(struct _cef_media_sink_t* self);
+
+  ///
+  // Returns true (1) if this sink is valid.
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_media_sink_t* self);
+
+  ///
+  // Returns the name of this sink.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_name)(struct _cef_media_sink_t* self);
+
+  ///
+  // Returns the description of this sink.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_description)(
+      struct _cef_media_sink_t* self);
+
+  ///
+  // Returns the icon type for this sink.
+  ///
+  cef_media_sink_icon_type_t(CEF_CALLBACK* get_icon_type)(
+      struct _cef_media_sink_t* self);
+
+  ///
+  // Asynchronously retrieves device info.
+  ///
+  void(CEF_CALLBACK* get_device_info)(
+      struct _cef_media_sink_t* self,
+      struct _cef_media_sink_device_info_callback_t* callback);
+
+  ///
+  // Returns true (1) if this sink accepts content via Cast.
+  ///
+  int(CEF_CALLBACK* is_cast_sink)(struct _cef_media_sink_t* self);
+
+  ///
+  // Returns true (1) if this sink accepts content via DIAL.
+  ///
+  int(CEF_CALLBACK* is_dial_sink)(struct _cef_media_sink_t* self);
+
+  ///
+  // Returns true (1) if this sink is compatible with |source|.
+  ///
+  int(CEF_CALLBACK* is_compatible_with)(struct _cef_media_sink_t* self,
+                                        struct _cef_media_source_t* source);
+} cef_media_sink_t;
+
+///
+// Callback structure for cef_media_sink_t::GetDeviceInfo. The functions of this
+// structure will be called on the browser process UI thread.
+///
+typedef struct _cef_media_sink_device_info_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be executed asyncronously once device information has been
+  // retrieved.
+  ///
+  void(CEF_CALLBACK* on_media_sink_device_info)(
+      struct _cef_media_sink_device_info_callback_t* self,
+      const struct _cef_media_sink_device_info_t* device_info);
+} cef_media_sink_device_info_callback_t;
+
+///
+// Represents a source from which media can be routed. Instances of this object
+// are retrieved via cef_media_router_t::GetSource. The functions of this
+// structure may be called on any browser process thread unless otherwise
+// indicated.
+///
+typedef struct _cef_media_source_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the ID (media source URN or URL) for this source.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_id)(struct _cef_media_source_t* self);
+
+  ///
+  // Returns true (1) if this source is valid.
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_media_source_t* self);
+
+  ///
+  // Returns true (1) if this source outputs its content via Cast.
+  ///
+  int(CEF_CALLBACK* is_cast_source)(struct _cef_media_source_t* self);
+
+  ///
+  // Returns true (1) if this source outputs its content via DIAL.
+  ///
+  int(CEF_CALLBACK* is_dial_source)(struct _cef_media_source_t* self);
+} cef_media_source_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_MEDIA_ROUTER_CAPI_H_
diff --git a/src/include/capi/cef_menu_model_capi.h b/src/include/capi/cef_menu_model_capi.h
new file mode 100644
index 0000000..6af46d0
--- /dev/null
+++ b/src/include/capi/cef_menu_model_capi.h
@@ -0,0 +1,511 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=cce24dba079162b10f359769eea176c4009b5ce5$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_MENU_MODEL_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_MENU_MODEL_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_menu_model_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Supports creation and modification of menus. See cef_menu_id_t for the
+// command ids that have default implementations. All user-defined command ids
+// should be between MENU_ID_USER_FIRST and MENU_ID_USER_LAST. The functions of
+// this structure can only be accessed on the browser process the UI thread.
+///
+typedef struct _cef_menu_model_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this menu is a submenu.
+  ///
+  int(CEF_CALLBACK* is_sub_menu)(struct _cef_menu_model_t* self);
+
+  ///
+  // Clears the menu. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* clear)(struct _cef_menu_model_t* self);
+
+  ///
+  // Returns the number of items in this menu.
+  ///
+  int(CEF_CALLBACK* get_count)(struct _cef_menu_model_t* self);
+
+  ///
+  // Add a separator to the menu. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* add_separator)(struct _cef_menu_model_t* self);
+
+  ///
+  // Add an item to the menu. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* add_item)(struct _cef_menu_model_t* self,
+                              int command_id,
+                              const cef_string_t* label);
+
+  ///
+  // Add a check item to the menu. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* add_check_item)(struct _cef_menu_model_t* self,
+                                    int command_id,
+                                    const cef_string_t* label);
+
+  ///
+  // Add a radio item to the menu. Only a single item with the specified
+  // |group_id| can be checked at a time. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* add_radio_item)(struct _cef_menu_model_t* self,
+                                    int command_id,
+                                    const cef_string_t* label,
+                                    int group_id);
+
+  ///
+  // Add a sub-menu to the menu. The new sub-menu is returned.
+  ///
+  struct _cef_menu_model_t*(CEF_CALLBACK* add_sub_menu)(
+      struct _cef_menu_model_t* self,
+      int command_id,
+      const cef_string_t* label);
+
+  ///
+  // Insert a separator in the menu at the specified |index|. Returns true (1)
+  // on success.
+  ///
+  int(CEF_CALLBACK* insert_separator_at)(struct _cef_menu_model_t* self,
+                                         int index);
+
+  ///
+  // Insert an item in the menu at the specified |index|. Returns true (1) on
+  // success.
+  ///
+  int(CEF_CALLBACK* insert_item_at)(struct _cef_menu_model_t* self,
+                                    int index,
+                                    int command_id,
+                                    const cef_string_t* label);
+
+  ///
+  // Insert a check item in the menu at the specified |index|. Returns true (1)
+  // on success.
+  ///
+  int(CEF_CALLBACK* insert_check_item_at)(struct _cef_menu_model_t* self,
+                                          int index,
+                                          int command_id,
+                                          const cef_string_t* label);
+
+  ///
+  // Insert a radio item in the menu at the specified |index|. Only a single
+  // item with the specified |group_id| can be checked at a time. Returns true
+  // (1) on success.
+  ///
+  int(CEF_CALLBACK* insert_radio_item_at)(struct _cef_menu_model_t* self,
+                                          int index,
+                                          int command_id,
+                                          const cef_string_t* label,
+                                          int group_id);
+
+  ///
+  // Insert a sub-menu in the menu at the specified |index|. The new sub-menu is
+  // returned.
+  ///
+  struct _cef_menu_model_t*(CEF_CALLBACK* insert_sub_menu_at)(
+      struct _cef_menu_model_t* self,
+      int index,
+      int command_id,
+      const cef_string_t* label);
+
+  ///
+  // Removes the item with the specified |command_id|. Returns true (1) on
+  // success.
+  ///
+  int(CEF_CALLBACK* remove)(struct _cef_menu_model_t* self, int command_id);
+
+  ///
+  // Removes the item at the specified |index|. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* remove_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Returns the index associated with the specified |command_id| or -1 if not
+  // found due to the command id not existing in the menu.
+  ///
+  int(CEF_CALLBACK* get_index_of)(struct _cef_menu_model_t* self,
+                                  int command_id);
+
+  ///
+  // Returns the command id at the specified |index| or -1 if not found due to
+  // invalid range or the index being a separator.
+  ///
+  int(CEF_CALLBACK* get_command_id_at)(struct _cef_menu_model_t* self,
+                                       int index);
+
+  ///
+  // Sets the command id at the specified |index|. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_command_id_at)(struct _cef_menu_model_t* self,
+                                       int index,
+                                       int command_id);
+
+  ///
+  // Returns the label for the specified |command_id| or NULL if not found.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_label)(struct _cef_menu_model_t* self,
+                                                 int command_id);
+
+  ///
+  // Returns the label at the specified |index| or NULL if not found due to
+  // invalid range or the index being a separator.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(
+      CEF_CALLBACK* get_label_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Sets the label for the specified |command_id|. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_label)(struct _cef_menu_model_t* self,
+                               int command_id,
+                               const cef_string_t* label);
+
+  ///
+  // Set the label at the specified |index|. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_label_at)(struct _cef_menu_model_t* self,
+                                  int index,
+                                  const cef_string_t* label);
+
+  ///
+  // Returns the item type for the specified |command_id|.
+  ///
+  cef_menu_item_type_t(CEF_CALLBACK* get_type)(struct _cef_menu_model_t* self,
+                                               int command_id);
+
+  ///
+  // Returns the item type at the specified |index|.
+  ///
+  cef_menu_item_type_t(
+      CEF_CALLBACK* get_type_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Returns the group id for the specified |command_id| or -1 if invalid.
+  ///
+  int(CEF_CALLBACK* get_group_id)(struct _cef_menu_model_t* self,
+                                  int command_id);
+
+  ///
+  // Returns the group id at the specified |index| or -1 if invalid.
+  ///
+  int(CEF_CALLBACK* get_group_id_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Sets the group id for the specified |command_id|. Returns true (1) on
+  // success.
+  ///
+  int(CEF_CALLBACK* set_group_id)(struct _cef_menu_model_t* self,
+                                  int command_id,
+                                  int group_id);
+
+  ///
+  // Sets the group id at the specified |index|. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_group_id_at)(struct _cef_menu_model_t* self,
+                                     int index,
+                                     int group_id);
+
+  ///
+  // Returns the submenu for the specified |command_id| or NULL if invalid.
+  ///
+  struct _cef_menu_model_t*(CEF_CALLBACK* get_sub_menu)(
+      struct _cef_menu_model_t* self,
+      int command_id);
+
+  ///
+  // Returns the submenu at the specified |index| or NULL if invalid.
+  ///
+  struct _cef_menu_model_t*(
+      CEF_CALLBACK* get_sub_menu_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Returns true (1) if the specified |command_id| is visible.
+  ///
+  int(CEF_CALLBACK* is_visible)(struct _cef_menu_model_t* self, int command_id);
+
+  ///
+  // Returns true (1) if the specified |index| is visible.
+  ///
+  int(CEF_CALLBACK* is_visible_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Change the visibility of the specified |command_id|. Returns true (1) on
+  // success.
+  ///
+  int(CEF_CALLBACK* set_visible)(struct _cef_menu_model_t* self,
+                                 int command_id,
+                                 int visible);
+
+  ///
+  // Change the visibility at the specified |index|. Returns true (1) on
+  // success.
+  ///
+  int(CEF_CALLBACK* set_visible_at)(struct _cef_menu_model_t* self,
+                                    int index,
+                                    int visible);
+
+  ///
+  // Returns true (1) if the specified |command_id| is enabled.
+  ///
+  int(CEF_CALLBACK* is_enabled)(struct _cef_menu_model_t* self, int command_id);
+
+  ///
+  // Returns true (1) if the specified |index| is enabled.
+  ///
+  int(CEF_CALLBACK* is_enabled_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Change the enabled status of the specified |command_id|. Returns true (1)
+  // on success.
+  ///
+  int(CEF_CALLBACK* set_enabled)(struct _cef_menu_model_t* self,
+                                 int command_id,
+                                 int enabled);
+
+  ///
+  // Change the enabled status at the specified |index|. Returns true (1) on
+  // success.
+  ///
+  int(CEF_CALLBACK* set_enabled_at)(struct _cef_menu_model_t* self,
+                                    int index,
+                                    int enabled);
+
+  ///
+  // Returns true (1) if the specified |command_id| is checked. Only applies to
+  // check and radio items.
+  ///
+  int(CEF_CALLBACK* is_checked)(struct _cef_menu_model_t* self, int command_id);
+
+  ///
+  // Returns true (1) if the specified |index| is checked. Only applies to check
+  // and radio items.
+  ///
+  int(CEF_CALLBACK* is_checked_at)(struct _cef_menu_model_t* self, int index);
+
+  ///
+  // Check the specified |command_id|. Only applies to check and radio items.
+  // Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_checked)(struct _cef_menu_model_t* self,
+                                 int command_id,
+                                 int checked);
+
+  ///
+  // Check the specified |index|. Only applies to check and radio items. Returns
+  // true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_checked_at)(struct _cef_menu_model_t* self,
+                                    int index,
+                                    int checked);
+
+  ///
+  // Returns true (1) if the specified |command_id| has a keyboard accelerator
+  // assigned.
+  ///
+  int(CEF_CALLBACK* has_accelerator)(struct _cef_menu_model_t* self,
+                                     int command_id);
+
+  ///
+  // Returns true (1) if the specified |index| has a keyboard accelerator
+  // assigned.
+  ///
+  int(CEF_CALLBACK* has_accelerator_at)(struct _cef_menu_model_t* self,
+                                        int index);
+
+  ///
+  // Set the keyboard accelerator for the specified |command_id|. |key_code| can
+  // be any virtual key or character value. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_accelerator)(struct _cef_menu_model_t* self,
+                                     int command_id,
+                                     int key_code,
+                                     int shift_pressed,
+                                     int ctrl_pressed,
+                                     int alt_pressed);
+
+  ///
+  // Set the keyboard accelerator at the specified |index|. |key_code| can be
+  // any virtual key or character value. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_accelerator_at)(struct _cef_menu_model_t* self,
+                                        int index,
+                                        int key_code,
+                                        int shift_pressed,
+                                        int ctrl_pressed,
+                                        int alt_pressed);
+
+  ///
+  // Remove the keyboard accelerator for the specified |command_id|. Returns
+  // true (1) on success.
+  ///
+  int(CEF_CALLBACK* remove_accelerator)(struct _cef_menu_model_t* self,
+                                        int command_id);
+
+  ///
+  // Remove the keyboard accelerator at the specified |index|. Returns true (1)
+  // on success.
+  ///
+  int(CEF_CALLBACK* remove_accelerator_at)(struct _cef_menu_model_t* self,
+                                           int index);
+
+  ///
+  // Retrieves the keyboard accelerator for the specified |command_id|. Returns
+  // true (1) on success.
+  ///
+  int(CEF_CALLBACK* get_accelerator)(struct _cef_menu_model_t* self,
+                                     int command_id,
+                                     int* key_code,
+                                     int* shift_pressed,
+                                     int* ctrl_pressed,
+                                     int* alt_pressed);
+
+  ///
+  // Retrieves the keyboard accelerator for the specified |index|. Returns true
+  // (1) on success.
+  ///
+  int(CEF_CALLBACK* get_accelerator_at)(struct _cef_menu_model_t* self,
+                                        int index,
+                                        int* key_code,
+                                        int* shift_pressed,
+                                        int* ctrl_pressed,
+                                        int* alt_pressed);
+
+  ///
+  // Set the explicit color for |command_id| and |color_type| to |color|.
+  // Specify a |color| value of 0 to remove the explicit color. If no explicit
+  // color or default color is set for |color_type| then the system color will
+  // be used. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_color)(struct _cef_menu_model_t* self,
+                               int command_id,
+                               cef_menu_color_type_t color_type,
+                               cef_color_t color);
+
+  ///
+  // Set the explicit color for |command_id| and |index| to |color|. Specify a
+  // |color| value of 0 to remove the explicit color. Specify an |index| value
+  // of -1 to set the default color for items that do not have an explicit color
+  // set. If no explicit color or default color is set for |color_type| then the
+  // system color will be used. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_color_at)(struct _cef_menu_model_t* self,
+                                  int index,
+                                  cef_menu_color_type_t color_type,
+                                  cef_color_t color);
+
+  ///
+  // Returns in |color| the color that was explicitly set for |command_id| and
+  // |color_type|. If a color was not set then 0 will be returned in |color|.
+  // Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* get_color)(struct _cef_menu_model_t* self,
+                               int command_id,
+                               cef_menu_color_type_t color_type,
+                               cef_color_t* color);
+
+  ///
+  // Returns in |color| the color that was explicitly set for |command_id| and
+  // |color_type|. Specify an |index| value of -1 to return the default color in
+  // |color|. If a color was not set then 0 will be returned in |color|. Returns
+  // true (1) on success.
+  ///
+  int(CEF_CALLBACK* get_color_at)(struct _cef_menu_model_t* self,
+                                  int index,
+                                  cef_menu_color_type_t color_type,
+                                  cef_color_t* color);
+
+  ///
+  // Sets the font list for the specified |command_id|. If |font_list| is NULL
+  // the system font will be used. Returns true (1) on success. The format is
+  // "<FONT_FAMILY_LIST>,[STYLES] <SIZE>", where: - FONT_FAMILY_LIST is a comma-
+  // separated list of font family names, - STYLES is an optional space-
+  // separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings: - "Arial, Helvetica,
+  // Bold Italic 14px" - "Arial, 14px"
+  ///
+  int(CEF_CALLBACK* set_font_list)(struct _cef_menu_model_t* self,
+                                   int command_id,
+                                   const cef_string_t* font_list);
+
+  ///
+  // Sets the font list for the specified |index|. Specify an |index| value of
+  // -1 to set the default font. If |font_list| is NULL the system font will be
+  // used. Returns true (1) on success. The format is
+  // "<FONT_FAMILY_LIST>,[STYLES] <SIZE>", where: - FONT_FAMILY_LIST is a comma-
+  // separated list of font family names, - STYLES is an optional space-
+  // separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings: - "Arial, Helvetica,
+  // Bold Italic 14px" - "Arial, 14px"
+  ///
+  int(CEF_CALLBACK* set_font_list_at)(struct _cef_menu_model_t* self,
+                                      int index,
+                                      const cef_string_t* font_list);
+} cef_menu_model_t;
+
+///
+// Create a new MenuModel with the specified |delegate|.
+///
+CEF_EXPORT cef_menu_model_t* cef_menu_model_create(
+    struct _cef_menu_model_delegate_t* delegate);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_MENU_MODEL_CAPI_H_
diff --git a/src/include/capi/cef_menu_model_delegate_capi.h b/src/include/capi/cef_menu_model_delegate_capi.h
new file mode 100644
index 0000000..1d9b0f8
--- /dev/null
+++ b/src/include/capi/cef_menu_model_delegate_capi.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=071ec8a0e17d3b33acbf36c7ccc26d0995657cf3$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_MENU_MODEL_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_MENU_MODEL_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_menu_model_t;
+
+///
+// Implement this structure to handle menu model events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_menu_model_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Perform the action associated with the specified |command_id| and optional
+  // |event_flags|.
+  ///
+  void(CEF_CALLBACK* execute_command)(struct _cef_menu_model_delegate_t* self,
+                                      struct _cef_menu_model_t* menu_model,
+                                      int command_id,
+                                      cef_event_flags_t event_flags);
+
+  ///
+  // Called when the user moves the mouse outside the menu and over the owning
+  // window.
+  ///
+  void(CEF_CALLBACK* mouse_outside_menu)(
+      struct _cef_menu_model_delegate_t* self,
+      struct _cef_menu_model_t* menu_model,
+      const cef_point_t* screen_point);
+
+  ///
+  // Called on unhandled open submenu keyboard commands. |is_rtl| will be true
+  // (1) if the menu is displaying a right-to-left language.
+  ///
+  void(CEF_CALLBACK* unhandled_open_submenu)(
+      struct _cef_menu_model_delegate_t* self,
+      struct _cef_menu_model_t* menu_model,
+      int is_rtl);
+
+  ///
+  // Called on unhandled close submenu keyboard commands. |is_rtl| will be true
+  // (1) if the menu is displaying a right-to-left language.
+  ///
+  void(CEF_CALLBACK* unhandled_close_submenu)(
+      struct _cef_menu_model_delegate_t* self,
+      struct _cef_menu_model_t* menu_model,
+      int is_rtl);
+
+  ///
+  // The menu is about to show.
+  ///
+  void(CEF_CALLBACK* menu_will_show)(struct _cef_menu_model_delegate_t* self,
+                                     struct _cef_menu_model_t* menu_model);
+
+  ///
+  // The menu has closed.
+  ///
+  void(CEF_CALLBACK* menu_closed)(struct _cef_menu_model_delegate_t* self,
+                                  struct _cef_menu_model_t* menu_model);
+
+  ///
+  // Optionally modify a menu item label. Return true (1) if |label| was
+  // modified.
+  ///
+  int(CEF_CALLBACK* format_label)(struct _cef_menu_model_delegate_t* self,
+                                  struct _cef_menu_model_t* menu_model,
+                                  cef_string_t* label);
+} cef_menu_model_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_MENU_MODEL_DELEGATE_CAPI_H_
diff --git a/src/include/capi/cef_navigation_entry_capi.h b/src/include/capi/cef_navigation_entry_capi.h
new file mode 100644
index 0000000..6dc23c9
--- /dev/null
+++ b/src/include/capi/cef_navigation_entry_capi.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=c6252024911652a4881d753aeeeb2615e6be3904$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_NAVIGATION_ENTRY_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_NAVIGATION_ENTRY_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_ssl_status_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to represent an entry in navigation history.
+///
+typedef struct _cef_navigation_entry_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. Do not call any other functions
+  // if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns the actual URL of the page. For some pages this may be data: URL or
+  // similar. Use get_display_url() to return a display-friendly version.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_url)(
+      struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns a display-friendly version of the URL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_display_url)(
+      struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns the original URL that was entered by the user before any redirects.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_original_url)(
+      struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns the title set by the page. This value may be NULL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_title)(
+      struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns the transition type which indicates what the user did to move to
+  // this page from the previous page.
+  ///
+  cef_transition_type_t(CEF_CALLBACK* get_transition_type)(
+      struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns true (1) if this navigation includes post data.
+  ///
+  int(CEF_CALLBACK* has_post_data)(struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns the time for the last known successful navigation completion. A
+  // navigation may be completed more than once if the page is reloaded. May be
+  // 0 if the navigation has not yet completed.
+  ///
+  cef_time_t(CEF_CALLBACK* get_completion_time)(
+      struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns the HTTP status code for the last known successful navigation
+  // response. May be 0 if the response has not yet been received or if the
+  // navigation has not yet completed.
+  ///
+  int(CEF_CALLBACK* get_http_status_code)(struct _cef_navigation_entry_t* self);
+
+  ///
+  // Returns the SSL information for this navigation entry.
+  ///
+  struct _cef_sslstatus_t*(CEF_CALLBACK* get_sslstatus)(
+      struct _cef_navigation_entry_t* self);
+} cef_navigation_entry_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_NAVIGATION_ENTRY_CAPI_H_
diff --git a/src/include/capi/cef_origin_whitelist_capi.h b/src/include/capi/cef_origin_whitelist_capi.h
new file mode 100644
index 0000000..6742165
--- /dev/null
+++ b/src/include/capi/cef_origin_whitelist_capi.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=8a26e2f8273298dcf44d6fbf32fd565f6aaa912c$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_ORIGIN_WHITELIST_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_ORIGIN_WHITELIST_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Add an entry to the cross-origin access whitelist.
+//
+// The same-origin policy restricts how scripts hosted from different origins
+// (scheme + domain + port) can communicate. By default, scripts can only access
+// resources with the same origin. Scripts hosted on the HTTP and HTTPS schemes
+// (but no other schemes) can use the "Access-Control-Allow-Origin" header to
+// allow cross-origin requests. For example, https://source.example.com can make
+// XMLHttpRequest requests on http://target.example.com if the
+// http://target.example.com request returns an "Access-Control-Allow-Origin:
+// https://source.example.com" response header.
+//
+// Scripts in separate frames or iframes and hosted from the same protocol and
+// domain suffix can execute cross-origin JavaScript if both pages set the
+// document.domain value to the same domain suffix. For example,
+// scheme://foo.example.com and scheme://bar.example.com can communicate using
+// JavaScript if both domains set document.domain="example.com".
+//
+// This function is used to allow access to origins that would otherwise violate
+// the same-origin policy. Scripts hosted underneath the fully qualified
+// |source_origin| URL (like http://www.example.com) will be allowed access to
+// all resources hosted on the specified |target_protocol| and |target_domain|.
+// If |target_domain| is non-NULL and |allow_target_subdomains| if false (0)
+// only exact domain matches will be allowed. If |target_domain| contains a top-
+// level domain component (like "example.com") and |allow_target_subdomains| is
+// true (1) sub-domain matches will be allowed. If |target_domain| is NULL and
+// |allow_target_subdomains| if true (1) all domains and IP addresses will be
+// allowed.
+//
+// This function cannot be used to bypass the restrictions on local or display
+// isolated schemes. See the comments on CefRegisterCustomScheme for more
+// information.
+//
+// This function may be called on any thread. Returns false (0) if
+// |source_origin| is invalid or the whitelist cannot be accessed.
+///
+CEF_EXPORT int cef_add_cross_origin_whitelist_entry(
+    const cef_string_t* source_origin,
+    const cef_string_t* target_protocol,
+    const cef_string_t* target_domain,
+    int allow_target_subdomains);
+
+///
+// Remove an entry from the cross-origin access whitelist. Returns false (0) if
+// |source_origin| is invalid or the whitelist cannot be accessed.
+///
+CEF_EXPORT int cef_remove_cross_origin_whitelist_entry(
+    const cef_string_t* source_origin,
+    const cef_string_t* target_protocol,
+    const cef_string_t* target_domain,
+    int allow_target_subdomains);
+
+///
+// Remove all entries from the cross-origin access whitelist. Returns false (0)
+// if the whitelist cannot be accessed.
+///
+CEF_EXPORT int cef_clear_cross_origin_whitelist();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_ORIGIN_WHITELIST_CAPI_H_
diff --git a/src/include/capi/cef_parser_capi.h b/src/include/capi/cef_parser_capi.h
new file mode 100644
index 0000000..51e51bb
--- /dev/null
+++ b/src/include/capi/cef_parser_capi.h
@@ -0,0 +1,178 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=14cf03e02d8ca3416e65f756470afd8185c7bc78$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_PARSER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_PARSER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Parse the specified |url| into its component parts. Returns false (0) if the
+// URL is NULL or invalid.
+///
+CEF_EXPORT int cef_parse_url(const cef_string_t* url,
+                             struct _cef_urlparts_t* parts);
+
+///
+// Creates a URL from the specified |parts|, which must contain a non-NULL spec
+// or a non-NULL host and path (at a minimum), but not both. Returns false (0)
+// if |parts| isn't initialized as described.
+///
+CEF_EXPORT int cef_create_url(const struct _cef_urlparts_t* parts,
+                              cef_string_t* url);
+
+///
+// This is a convenience function for formatting a URL in a concise and human-
+// friendly way to help users make security-related decisions (or in other
+// circumstances when people need to distinguish sites, origins, or otherwise-
+// simplified URLs from each other). Internationalized domain names (IDN) may be
+// presented in Unicode if the conversion is considered safe. The returned value
+// will (a) omit the path for standard schemes, excepting file and filesystem,
+// and (b) omit the port if it is the default for the scheme. Do not use this
+// for URLs which will be parsed or sent to other applications.
+///
+// The resulting string must be freed by calling cef_string_userfree_free().
+CEF_EXPORT cef_string_userfree_t
+cef_format_url_for_security_display(const cef_string_t* origin_url);
+
+///
+// Returns the mime type for the specified file extension or an NULL string if
+// unknown.
+///
+// The resulting string must be freed by calling cef_string_userfree_free().
+CEF_EXPORT cef_string_userfree_t
+cef_get_mime_type(const cef_string_t* extension);
+
+///
+// Get the extensions associated with the given mime type. This should be passed
+// in lower case. There could be multiple extensions for a given mime type, like
+// "html,htm" for "text/html", or "txt,text,html,..." for "text/*". Any existing
+// elements in the provided vector will not be erased.
+///
+CEF_EXPORT void cef_get_extensions_for_mime_type(const cef_string_t* mime_type,
+                                                 cef_string_list_t extensions);
+
+///
+// Encodes |data| as a base64 string.
+///
+// The resulting string must be freed by calling cef_string_userfree_free().
+CEF_EXPORT cef_string_userfree_t cef_base64encode(const void* data,
+                                                  size_t data_size);
+
+///
+// Decodes the base64 encoded string |data|. The returned value will be NULL if
+// the decoding fails.
+///
+CEF_EXPORT struct _cef_binary_value_t* cef_base64decode(
+    const cef_string_t* data);
+
+///
+// Escapes characters in |text| which are unsuitable for use as a query
+// parameter value. Everything except alphanumerics and -_.!~*'() will be
+// converted to "%XX". If |use_plus| is true (1) spaces will change to "+". The
+// result is basically the same as encodeURIComponent in Javacript.
+///
+// The resulting string must be freed by calling cef_string_userfree_free().
+CEF_EXPORT cef_string_userfree_t cef_uriencode(const cef_string_t* text,
+                                               int use_plus);
+
+///
+// Unescapes |text| and returns the result. Unescaping consists of looking for
+// the exact pattern "%XX" where each X is a hex digit and converting to the
+// character with the numerical value of those digits (e.g. "i%20=%203%3b"
+// unescapes to "i = 3;"). If |convert_to_utf8| is true (1) this function will
+// attempt to interpret the initial decoded result as UTF-8. If the result is
+// convertable into UTF-8 it will be returned as converted. Otherwise the
+// initial decoded result will be returned.  The |unescape_rule| parameter
+// supports further customization the decoding process.
+///
+// The resulting string must be freed by calling cef_string_userfree_free().
+CEF_EXPORT cef_string_userfree_t
+cef_uridecode(const cef_string_t* text,
+              int convert_to_utf8,
+              cef_uri_unescape_rule_t unescape_rule);
+
+///
+// Parses the specified |json_string| and returns a dictionary or list
+// representation. If JSON parsing fails this function returns NULL.
+///
+CEF_EXPORT struct _cef_value_t* cef_parse_json(
+    const cef_string_t* json_string,
+    cef_json_parser_options_t options);
+
+///
+// Parses the specified UTF8-encoded |json| buffer of size |json_size| and
+// returns a dictionary or list representation. If JSON parsing fails this
+// function returns NULL.
+///
+CEF_EXPORT struct _cef_value_t* cef_parse_json_buffer(
+    const void* json,
+    size_t json_size,
+    cef_json_parser_options_t options);
+
+///
+// Parses the specified |json_string| and returns a dictionary or list
+// representation. If JSON parsing fails this function returns NULL and
+// populates |error_code_out| and |error_msg_out| with an error code and a
+// formatted error message respectively.
+///
+CEF_EXPORT struct _cef_value_t* cef_parse_jsonand_return_error(
+    const cef_string_t* json_string,
+    cef_json_parser_options_t options,
+    cef_json_parser_error_t* error_code_out,
+    cef_string_t* error_msg_out);
+
+///
+// Generates a JSON string from the specified root |node| which should be a
+// dictionary or list value. Returns an NULL string on failure. This function
+// requires exclusive access to |node| including any underlying data.
+///
+// The resulting string must be freed by calling cef_string_userfree_free().
+CEF_EXPORT cef_string_userfree_t
+cef_write_json(struct _cef_value_t* node, cef_json_writer_options_t options);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_PARSER_CAPI_H_
diff --git a/src/include/capi/cef_path_util_capi.h b/src/include/capi/cef_path_util_capi.h
new file mode 100644
index 0000000..f4405be
--- /dev/null
+++ b/src/include/capi/cef_path_util_capi.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=1b218a91d7f3ba0e68f0c3be21a0df91e515d28a$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_PATH_UTIL_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_PATH_UTIL_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Retrieve the path associated with the specified |key|. Returns true (1) on
+// success. Can be called on any thread in the browser process.
+///
+CEF_EXPORT int cef_get_path(cef_path_key_t key, cef_string_t* path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_PATH_UTIL_CAPI_H_
diff --git a/src/include/capi/cef_print_handler_capi.h b/src/include/capi/cef_print_handler_capi.h
new file mode 100644
index 0000000..8876ab6
--- /dev/null
+++ b/src/include/capi/cef_print_handler_capi.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=b8d7be1399d3426a3f872b12bc1438e041a16308$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_PRINT_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_PRINT_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_print_settings_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback structure for asynchronous continuation of print dialog requests.
+///
+typedef struct _cef_print_dialog_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Continue printing with the specified |settings|.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_print_dialog_callback_t* self,
+                           struct _cef_print_settings_t* settings);
+
+  ///
+  // Cancel the printing.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_print_dialog_callback_t* self);
+} cef_print_dialog_callback_t;
+
+///
+// Callback structure for asynchronous continuation of print job requests.
+///
+typedef struct _cef_print_job_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Indicate completion of the print job.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_print_job_callback_t* self);
+} cef_print_job_callback_t;
+
+///
+// Implement this structure to handle printing on Linux. Each browser will have
+// only one print job in progress at a time. The functions of this structure
+// will be called on the browser process UI thread.
+///
+typedef struct _cef_print_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called when printing has started for the specified |browser|. This function
+  // will be called before the other OnPrint*() functions and irrespective of
+  // how printing was initiated (e.g. cef_browser_host_t::print(), JavaScript
+  // window.print() or PDF extension print button).
+  ///
+  void(CEF_CALLBACK* on_print_start)(struct _cef_print_handler_t* self,
+                                     struct _cef_browser_t* browser);
+
+  ///
+  // Synchronize |settings| with client state. If |get_defaults| is true (1)
+  // then populate |settings| with the default print settings. Do not keep a
+  // reference to |settings| outside of this callback.
+  ///
+  void(CEF_CALLBACK* on_print_settings)(struct _cef_print_handler_t* self,
+                                        struct _cef_browser_t* browser,
+                                        struct _cef_print_settings_t* settings,
+                                        int get_defaults);
+
+  ///
+  // Show the print dialog. Execute |callback| once the dialog is dismissed.
+  // Return true (1) if the dialog will be displayed or false (0) to cancel the
+  // printing immediately.
+  ///
+  int(CEF_CALLBACK* on_print_dialog)(
+      struct _cef_print_handler_t* self,
+      struct _cef_browser_t* browser,
+      int has_selection,
+      struct _cef_print_dialog_callback_t* callback);
+
+  ///
+  // Send the print job to the printer. Execute |callback| once the job is
+  // completed. Return true (1) if the job will proceed or false (0) to cancel
+  // the job immediately.
+  ///
+  int(CEF_CALLBACK* on_print_job)(struct _cef_print_handler_t* self,
+                                  struct _cef_browser_t* browser,
+                                  const cef_string_t* document_name,
+                                  const cef_string_t* pdf_file_path,
+                                  struct _cef_print_job_callback_t* callback);
+
+  ///
+  // Reset client state related to printing.
+  ///
+  void(CEF_CALLBACK* on_print_reset)(struct _cef_print_handler_t* self,
+                                     struct _cef_browser_t* browser);
+
+  ///
+  // Return the PDF paper size in device units. Used in combination with
+  // cef_browser_host_t::print_to_pdf().
+  ///
+  cef_size_t(CEF_CALLBACK* get_pdf_paper_size)(
+      struct _cef_print_handler_t* self,
+      int device_units_per_inch);
+} cef_print_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_PRINT_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_print_settings_capi.h b/src/include/capi/cef_print_settings_capi.h
new file mode 100644
index 0000000..3b3d0c1
--- /dev/null
+++ b/src/include/capi/cef_print_settings_capi.h
@@ -0,0 +1,202 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=8f7d7993691e07f4a8a42d63522c751cfba3c168$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_PRINT_SETTINGS_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_PRINT_SETTINGS_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure representing print settings.
+///
+typedef struct _cef_print_settings_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. Do not call any other functions
+  // if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_print_settings_t* self);
+
+  ///
+  // Returns true (1) if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_print_settings_t* self);
+
+  ///
+  // Set the page orientation.
+  ///
+  void(CEF_CALLBACK* set_orientation)(struct _cef_print_settings_t* self,
+                                      int landscape);
+
+  ///
+  // Returns true (1) if the orientation is landscape.
+  ///
+  int(CEF_CALLBACK* is_landscape)(struct _cef_print_settings_t* self);
+
+  ///
+  // Set the printer printable area in device units. Some platforms already
+  // provide flipped area. Set |landscape_needs_flip| to false (0) on those
+  // platforms to avoid double flipping.
+  ///
+  void(CEF_CALLBACK* set_printer_printable_area)(
+      struct _cef_print_settings_t* self,
+      const cef_size_t* physical_size_device_units,
+      const cef_rect_t* printable_area_device_units,
+      int landscape_needs_flip);
+
+  ///
+  // Set the device name.
+  ///
+  void(CEF_CALLBACK* set_device_name)(struct _cef_print_settings_t* self,
+                                      const cef_string_t* name);
+
+  ///
+  // Get the device name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_device_name)(
+      struct _cef_print_settings_t* self);
+
+  ///
+  // Set the DPI (dots per inch).
+  ///
+  void(CEF_CALLBACK* set_dpi)(struct _cef_print_settings_t* self, int dpi);
+
+  ///
+  // Get the DPI (dots per inch).
+  ///
+  int(CEF_CALLBACK* get_dpi)(struct _cef_print_settings_t* self);
+
+  ///
+  // Set the page ranges.
+  ///
+  void(CEF_CALLBACK* set_page_ranges)(struct _cef_print_settings_t* self,
+                                      size_t rangesCount,
+                                      cef_range_t const* ranges);
+
+  ///
+  // Returns the number of page ranges that currently exist.
+  ///
+  size_t(CEF_CALLBACK* get_page_ranges_count)(
+      struct _cef_print_settings_t* self);
+
+  ///
+  // Retrieve the page ranges.
+  ///
+  void(CEF_CALLBACK* get_page_ranges)(struct _cef_print_settings_t* self,
+                                      size_t* rangesCount,
+                                      cef_range_t* ranges);
+
+  ///
+  // Set whether only the selection will be printed.
+  ///
+  void(CEF_CALLBACK* set_selection_only)(struct _cef_print_settings_t* self,
+                                         int selection_only);
+
+  ///
+  // Returns true (1) if only the selection will be printed.
+  ///
+  int(CEF_CALLBACK* is_selection_only)(struct _cef_print_settings_t* self);
+
+  ///
+  // Set whether pages will be collated.
+  ///
+  void(CEF_CALLBACK* set_collate)(struct _cef_print_settings_t* self,
+                                  int collate);
+
+  ///
+  // Returns true (1) if pages will be collated.
+  ///
+  int(CEF_CALLBACK* will_collate)(struct _cef_print_settings_t* self);
+
+  ///
+  // Set the color model.
+  ///
+  void(CEF_CALLBACK* set_color_model)(struct _cef_print_settings_t* self,
+                                      cef_color_model_t model);
+
+  ///
+  // Get the color model.
+  ///
+  cef_color_model_t(CEF_CALLBACK* get_color_model)(
+      struct _cef_print_settings_t* self);
+
+  ///
+  // Set the number of copies.
+  ///
+  void(CEF_CALLBACK* set_copies)(struct _cef_print_settings_t* self,
+                                 int copies);
+
+  ///
+  // Get the number of copies.
+  ///
+  int(CEF_CALLBACK* get_copies)(struct _cef_print_settings_t* self);
+
+  ///
+  // Set the duplex mode.
+  ///
+  void(CEF_CALLBACK* set_duplex_mode)(struct _cef_print_settings_t* self,
+                                      cef_duplex_mode_t mode);
+
+  ///
+  // Get the duplex mode.
+  ///
+  cef_duplex_mode_t(CEF_CALLBACK* get_duplex_mode)(
+      struct _cef_print_settings_t* self);
+} cef_print_settings_t;
+
+///
+// Create a new cef_print_settings_t object.
+///
+CEF_EXPORT cef_print_settings_t* cef_print_settings_create();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_PRINT_SETTINGS_CAPI_H_
diff --git a/src/include/capi/cef_process_message_capi.h b/src/include/capi/cef_process_message_capi.h
new file mode 100644
index 0000000..aff3b40
--- /dev/null
+++ b/src/include/capi/cef_process_message_capi.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=79ec6d99ea47e1cf9b2cca0433704f205e14d3bd$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_PROCESS_MESSAGE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_PROCESS_MESSAGE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure representing a message. Can be used on any process and thread.
+///
+typedef struct _cef_process_message_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. Do not call any other functions
+  // if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_process_message_t* self);
+
+  ///
+  // Returns true (1) if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_process_message_t* self);
+
+  ///
+  // Returns a writable copy of this object.
+  ///
+  struct _cef_process_message_t*(CEF_CALLBACK* copy)(
+      struct _cef_process_message_t* self);
+
+  ///
+  // Returns the message name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_name)(
+      struct _cef_process_message_t* self);
+
+  ///
+  // Returns the list of arguments.
+  ///
+  struct _cef_list_value_t*(CEF_CALLBACK* get_argument_list)(
+      struct _cef_process_message_t* self);
+} cef_process_message_t;
+
+///
+// Create a new cef_process_message_t object with the specified name.
+///
+CEF_EXPORT cef_process_message_t* cef_process_message_create(
+    const cef_string_t* name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_PROCESS_MESSAGE_CAPI_H_
diff --git a/src/include/capi/cef_process_util_capi.h b/src/include/capi/cef_process_util_capi.h
new file mode 100644
index 0000000..1a8a003
--- /dev/null
+++ b/src/include/capi/cef_process_util_capi.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=75b16fd9d592c1d22b94d740e1deb61efe3afb97$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_PROCESS_UTIL_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_PROCESS_UTIL_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Launches the process specified via |command_line|. Returns true (1) upon
+// success. Must be called on the browser process TID_PROCESS_LAUNCHER thread.
+//
+// Unix-specific notes: - All file descriptors open in the parent process will
+// be closed in the
+//   child process except for stdin, stdout, and stderr.
+// - If the first argument on the command line does not contain a slash,
+//   PATH will be searched. (See man execvp.)
+///
+CEF_EXPORT int cef_launch_process(struct _cef_command_line_t* command_line);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_PROCESS_UTIL_CAPI_H_
diff --git a/src/include/capi/cef_registration_capi.h b/src/include/capi/cef_registration_capi.h
new file mode 100644
index 0000000..8b7354a
--- /dev/null
+++ b/src/include/capi/cef_registration_capi.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=029e237cf80f94a25453bac5a9b1e0765bb56f37$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_REGISTRATION_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_REGISTRATION_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Generic callback structure used for managing the lifespan of a registration.
+///
+typedef struct _cef_registration_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+} cef_registration_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_REGISTRATION_CAPI_H_
diff --git a/src/include/capi/cef_render_handler_capi.h b/src/include/capi/cef_render_handler_capi.h
new file mode 100644
index 0000000..df7e88f
--- /dev/null
+++ b/src/include/capi/cef_render_handler_capi.h
@@ -0,0 +1,251 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=e642fc1fe3b97a90c0eae7f0fc0a5cfd385e3e17$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RENDER_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RENDER_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_accessibility_handler_capi.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_drag_data_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle events when window rendering is disabled.
+// The functions of this structure will be called on the UI thread.
+///
+typedef struct _cef_render_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Return the handler for accessibility notifications. If no handler is
+  // provided the default implementation will be used.
+  ///
+  struct _cef_accessibility_handler_t*(CEF_CALLBACK* get_accessibility_handler)(
+      struct _cef_render_handler_t* self);
+
+  ///
+  // Called to retrieve the root window rectangle in screen coordinates. Return
+  // true (1) if the rectangle was provided. If this function returns false (0)
+  // the rectangle from GetViewRect will be used.
+  ///
+  int(CEF_CALLBACK* get_root_screen_rect)(struct _cef_render_handler_t* self,
+                                          struct _cef_browser_t* browser,
+                                          cef_rect_t* rect);
+
+  ///
+  // Called to retrieve the view rectangle which is relative to screen
+  // coordinates. This function must always provide a non-NULL rectangle.
+  ///
+  void(CEF_CALLBACK* get_view_rect)(struct _cef_render_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    cef_rect_t* rect);
+
+  ///
+  // Called to retrieve the translation from view coordinates to actual screen
+  // coordinates. Return true (1) if the screen coordinates were provided.
+  ///
+  int(CEF_CALLBACK* get_screen_point)(struct _cef_render_handler_t* self,
+                                      struct _cef_browser_t* browser,
+                                      int viewX,
+                                      int viewY,
+                                      int* screenX,
+                                      int* screenY);
+
+  ///
+  // Called to allow the client to fill in the CefScreenInfo object with
+  // appropriate values. Return true (1) if the |screen_info| structure has been
+  // modified.
+  //
+  // If the screen info rectangle is left NULL the rectangle from GetViewRect
+  // will be used. If the rectangle is still NULL or invalid popups may not be
+  // drawn correctly.
+  ///
+  int(CEF_CALLBACK* get_screen_info)(struct _cef_render_handler_t* self,
+                                     struct _cef_browser_t* browser,
+                                     struct _cef_screen_info_t* screen_info);
+
+  ///
+  // Called when the browser wants to show or hide the popup widget. The popup
+  // should be shown if |show| is true (1) and hidden if |show| is false (0).
+  ///
+  void(CEF_CALLBACK* on_popup_show)(struct _cef_render_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    int show);
+
+  ///
+  // Called when the browser wants to move or resize the popup widget. |rect|
+  // contains the new location and size in view coordinates.
+  ///
+  void(CEF_CALLBACK* on_popup_size)(struct _cef_render_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    const cef_rect_t* rect);
+
+  ///
+  // Called when an element should be painted. Pixel values passed to this
+  // function are scaled relative to view coordinates based on the value of
+  // CefScreenInfo.device_scale_factor returned from GetScreenInfo. |type|
+  // indicates whether the element is the view or the popup widget. |buffer|
+  // contains the pixel data for the whole image. |dirtyRects| contains the set
+  // of rectangles in pixel coordinates that need to be repainted. |buffer| will
+  // be |width|*|height|*4 bytes in size and represents a BGRA image with an
+  // upper-left origin. This function is only called when
+  // cef_window_tInfo::shared_texture_enabled is set to false (0).
+  ///
+  void(CEF_CALLBACK* on_paint)(struct _cef_render_handler_t* self,
+                               struct _cef_browser_t* browser,
+                               cef_paint_element_type_t type,
+                               size_t dirtyRectsCount,
+                               cef_rect_t const* dirtyRects,
+                               const void* buffer,
+                               int width,
+                               int height);
+
+  ///
+  // Called when an element has been rendered to the shared texture handle.
+  // |type| indicates whether the element is the view or the popup widget.
+  // |dirtyRects| contains the set of rectangles in pixel coordinates that need
+  // to be repainted. |shared_handle| is the handle for a D3D11 Texture2D that
+  // can be accessed via ID3D11Device using the OpenSharedResource function.
+  // This function is only called when cef_window_tInfo::shared_texture_enabled
+  // is set to true (1), and is currently only supported on Windows.
+  ///
+  void(CEF_CALLBACK* on_accelerated_paint)(struct _cef_render_handler_t* self,
+                                           struct _cef_browser_t* browser,
+                                           cef_paint_element_type_t type,
+                                           size_t dirtyRectsCount,
+                                           cef_rect_t const* dirtyRects,
+                                           void* shared_handle);
+
+  ///
+  // Called when the browser's cursor has changed. If |type| is CT_CUSTOM then
+  // |custom_cursor_info| will be populated with the custom cursor information.
+  ///
+  void(CEF_CALLBACK* on_cursor_change)(
+      struct _cef_render_handler_t* self,
+      struct _cef_browser_t* browser,
+      cef_cursor_handle_t cursor,
+      cef_cursor_type_t type,
+      const struct _cef_cursor_info_t* custom_cursor_info);
+
+  ///
+  // Called when the user starts dragging content in the web view. Contextual
+  // information about the dragged content is supplied by |drag_data|. (|x|,
+  // |y|) is the drag start location in screen coordinates. OS APIs that run a
+  // system message loop may be used within the StartDragging call.
+  //
+  // Return false (0) to abort the drag operation. Don't call any of
+  // cef_browser_host_t::DragSource*Ended* functions after returning false (0).
+  //
+  // Return true (1) to handle the drag operation. Call
+  // cef_browser_host_t::DragSourceEndedAt and DragSourceSystemDragEnded either
+  // synchronously or asynchronously to inform the web view that the drag
+  // operation has ended.
+  ///
+  int(CEF_CALLBACK* start_dragging)(struct _cef_render_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    struct _cef_drag_data_t* drag_data,
+                                    cef_drag_operations_mask_t allowed_ops,
+                                    int x,
+                                    int y);
+
+  ///
+  // Called when the web view wants to update the mouse cursor during a drag &
+  // drop operation. |operation| describes the allowed operation (none, move,
+  // copy, link).
+  ///
+  void(CEF_CALLBACK* update_drag_cursor)(struct _cef_render_handler_t* self,
+                                         struct _cef_browser_t* browser,
+                                         cef_drag_operations_mask_t operation);
+
+  ///
+  // Called when the scroll offset has changed.
+  ///
+  void(CEF_CALLBACK* on_scroll_offset_changed)(
+      struct _cef_render_handler_t* self,
+      struct _cef_browser_t* browser,
+      double x,
+      double y);
+
+  ///
+  // Called when the IME composition range has changed. |selected_range| is the
+  // range of characters that have been selected. |character_bounds| is the
+  // bounds of each character in view coordinates.
+  ///
+  void(CEF_CALLBACK* on_ime_composition_range_changed)(
+      struct _cef_render_handler_t* self,
+      struct _cef_browser_t* browser,
+      const cef_range_t* selected_range,
+      size_t character_boundsCount,
+      cef_rect_t const* character_bounds);
+
+  ///
+  // Called when text selection has changed for the specified |browser|.
+  // |selected_text| is the currently selected text and |selected_range| is the
+  // character range.
+  ///
+  void(CEF_CALLBACK* on_text_selection_changed)(
+      struct _cef_render_handler_t* self,
+      struct _cef_browser_t* browser,
+      const cef_string_t* selected_text,
+      const cef_range_t* selected_range);
+
+  ///
+  // Called when an on-screen keyboard should be shown or hidden for the
+  // specified |browser|. |input_mode| specifies what kind of keyboard should be
+  // opened. If |input_mode| is CEF_TEXT_INPUT_MODE_NONE, any existing keyboard
+  // for this browser should be hidden.
+  ///
+  void(CEF_CALLBACK* on_virtual_keyboard_requested)(
+      struct _cef_render_handler_t* self,
+      struct _cef_browser_t* browser,
+      cef_text_input_mode_t input_mode);
+} cef_render_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RENDER_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_render_process_handler_capi.h b/src/include/capi/cef_render_process_handler_capi.h
new file mode 100644
index 0000000..e23d499
--- /dev/null
+++ b/src/include/capi/cef_render_process_handler_capi.h
@@ -0,0 +1,178 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3630a82a4ea731b43ed4ba468a57c5dfe15f8679$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RENDER_PROCESS_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RENDER_PROCESS_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_dom_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_load_handler_capi.h"
+#include "include/capi/cef_process_message_capi.h"
+#include "include/capi/cef_v8_capi.h"
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to implement render process callbacks. The functions of this
+// structure will be called on the render process main thread (TID_RENDERER)
+// unless otherwise indicated.
+///
+typedef struct _cef_render_process_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called after the render process main thread has been created. |extra_info|
+  // is a read-only value originating from
+  // cef_browser_process_handler_t::on_render_process_thread_created(). Do not
+  // keep a reference to |extra_info| outside of this function.
+  ///
+  void(CEF_CALLBACK* on_render_thread_created)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_list_value_t* extra_info);
+
+  ///
+  // Called after WebKit has been initialized.
+  ///
+  void(CEF_CALLBACK* on_web_kit_initialized)(
+      struct _cef_render_process_handler_t* self);
+
+  ///
+  // Called after a browser has been created. When browsing cross-origin a new
+  // browser will be created before the old browser with the same identifier is
+  // destroyed. |extra_info| is a read-only value originating from
+  // cef_browser_host_t::cef_browser_host_create_browser(),
+  // cef_browser_host_t::cef_browser_host_create_browser_sync(),
+  // cef_life_span_handler_t::on_before_popup() or
+  // cef_browser_view_t::cef_browser_view_create().
+  ///
+  void(CEF_CALLBACK* on_browser_created)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_dictionary_value_t* extra_info);
+
+  ///
+  // Called before a browser is destroyed.
+  ///
+  void(CEF_CALLBACK* on_browser_destroyed)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_browser_t* browser);
+
+  ///
+  // Return the handler for browser load status events.
+  ///
+  struct _cef_load_handler_t*(CEF_CALLBACK* get_load_handler)(
+      struct _cef_render_process_handler_t* self);
+
+  ///
+  // Called immediately after the V8 context for a frame has been created. To
+  // retrieve the JavaScript 'window' object use the
+  // cef_v8context_t::get_global() function. V8 handles can only be accessed
+  // from the thread on which they are created. A task runner for posting tasks
+  // on the associated thread can be retrieved via the
+  // cef_v8context_t::get_task_runner() function.
+  ///
+  void(CEF_CALLBACK* on_context_created)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_v8context_t* context);
+
+  ///
+  // Called immediately before the V8 context for a frame is released. No
+  // references to the context should be kept after this function is called.
+  ///
+  void(CEF_CALLBACK* on_context_released)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_v8context_t* context);
+
+  ///
+  // Called for global uncaught exceptions in a frame. Execution of this
+  // callback is disabled by default. To enable set
+  // CefSettings.uncaught_exception_stack_size > 0.
+  ///
+  void(CEF_CALLBACK* on_uncaught_exception)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_v8context_t* context,
+      struct _cef_v8exception_t* exception,
+      struct _cef_v8stack_trace_t* stackTrace);
+
+  ///
+  // Called when a new node in the the browser gets focus. The |node| value may
+  // be NULL if no specific node has gained focus. The node object passed to
+  // this function represents a snapshot of the DOM at the time this function is
+  // executed. DOM objects are only valid for the scope of this function. Do not
+  // keep references to or attempt to access any DOM objects outside the scope
+  // of this function.
+  ///
+  void(CEF_CALLBACK* on_focused_node_changed)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_domnode_t* node);
+
+  ///
+  // Called when a new message is received from a different process. Return true
+  // (1) if the message was handled or false (0) otherwise. Do not keep a
+  // reference to or attempt to access the message outside of this callback.
+  ///
+  int(CEF_CALLBACK* on_process_message_received)(
+      struct _cef_render_process_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      cef_process_id_t source_process,
+      struct _cef_process_message_t* message);
+} cef_render_process_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RENDER_PROCESS_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_request_callback_capi.h b/src/include/capi/cef_request_callback_capi.h
new file mode 100644
index 0000000..36316e9
--- /dev/null
+++ b/src/include/capi/cef_request_callback_capi.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3efd81a4bfdfca579a77f14bd37b8192122ebda4$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_CALLBACK_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_REQUEST_CALLBACK_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback structure used for asynchronous continuation of url requests.
+///
+typedef struct _cef_request_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Continue the url request. If |allow| is true (1) the request will be
+  // continued. Otherwise, the request will be canceled.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_request_callback_t* self, int allow);
+
+  ///
+  // Cancel the url request.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_request_callback_t* self);
+} cef_request_callback_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_REQUEST_CALLBACK_CAPI_H_
diff --git a/src/include/capi/cef_request_capi.h b/src/include/capi/cef_request_capi.h
new file mode 100644
index 0000000..9904974
--- /dev/null
+++ b/src/include/capi/cef_request_capi.h
@@ -0,0 +1,353 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=b3725b8fa4118936caacda69504dc597f3620d82$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_REQUEST_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_post_data_element_t;
+struct _cef_post_data_t;
+
+///
+// Structure used to represent a web request. The functions of this structure
+// may be called on any thread.
+///
+typedef struct _cef_request_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is read-only.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_request_t* self);
+
+  ///
+  // Get the fully qualified URL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_url)(struct _cef_request_t* self);
+
+  ///
+  // Set the fully qualified URL.
+  ///
+  void(CEF_CALLBACK* set_url)(struct _cef_request_t* self,
+                              const cef_string_t* url);
+
+  ///
+  // Get the request function type. The value will default to POST if post data
+  // is provided and GET otherwise.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_method)(struct _cef_request_t* self);
+
+  ///
+  // Set the request function type.
+  ///
+  void(CEF_CALLBACK* set_method)(struct _cef_request_t* self,
+                                 const cef_string_t* method);
+
+  ///
+  // Set the referrer URL and policy. If non-NULL the referrer URL must be fully
+  // qualified with an HTTP or HTTPS scheme component. Any username, password or
+  // ref component will be removed.
+  ///
+  void(CEF_CALLBACK* set_referrer)(struct _cef_request_t* self,
+                                   const cef_string_t* referrer_url,
+                                   cef_referrer_policy_t policy);
+
+  ///
+  // Get the referrer URL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_referrer_url)(
+      struct _cef_request_t* self);
+
+  ///
+  // Get the referrer policy.
+  ///
+  cef_referrer_policy_t(CEF_CALLBACK* get_referrer_policy)(
+      struct _cef_request_t* self);
+
+  ///
+  // Get the post data.
+  ///
+  struct _cef_post_data_t*(CEF_CALLBACK* get_post_data)(
+      struct _cef_request_t* self);
+
+  ///
+  // Set the post data.
+  ///
+  void(CEF_CALLBACK* set_post_data)(struct _cef_request_t* self,
+                                    struct _cef_post_data_t* postData);
+
+  ///
+  // Get the header values. Will not include the Referer value if any.
+  ///
+  void(CEF_CALLBACK* get_header_map)(struct _cef_request_t* self,
+                                     cef_string_multimap_t headerMap);
+
+  ///
+  // Set the header values. If a Referer value exists in the header map it will
+  // be removed and ignored.
+  ///
+  void(CEF_CALLBACK* set_header_map)(struct _cef_request_t* self,
+                                     cef_string_multimap_t headerMap);
+
+  ///
+  // Returns the first header value for |name| or an NULL string if not found.
+  // Will not return the Referer value if any. Use GetHeaderMap instead if
+  // |name| might have multiple values.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_header_by_name)(
+      struct _cef_request_t* self,
+      const cef_string_t* name);
+
+  ///
+  // Set the header |name| to |value|. If |overwrite| is true (1) any existing
+  // values will be replaced with the new value. If |overwrite| is false (0) any
+  // existing values will not be overwritten. The Referer value cannot be set
+  // using this function.
+  ///
+  void(CEF_CALLBACK* set_header_by_name)(struct _cef_request_t* self,
+                                         const cef_string_t* name,
+                                         const cef_string_t* value,
+                                         int overwrite);
+
+  ///
+  // Set all values at one time.
+  ///
+  void(CEF_CALLBACK* set)(struct _cef_request_t* self,
+                          const cef_string_t* url,
+                          const cef_string_t* method,
+                          struct _cef_post_data_t* postData,
+                          cef_string_multimap_t headerMap);
+
+  ///
+  // Get the flags used in combination with cef_urlrequest_t. See
+  // cef_urlrequest_flags_t for supported values.
+  ///
+  int(CEF_CALLBACK* get_flags)(struct _cef_request_t* self);
+
+  ///
+  // Set the flags used in combination with cef_urlrequest_t.  See
+  // cef_urlrequest_flags_t for supported values.
+  ///
+  void(CEF_CALLBACK* set_flags)(struct _cef_request_t* self, int flags);
+
+  ///
+  // Get the URL to the first party for cookies used in combination with
+  // cef_urlrequest_t.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_first_party_for_cookies)(
+      struct _cef_request_t* self);
+
+  ///
+  // Set the URL to the first party for cookies used in combination with
+  // cef_urlrequest_t.
+  ///
+  void(CEF_CALLBACK* set_first_party_for_cookies)(struct _cef_request_t* self,
+                                                  const cef_string_t* url);
+
+  ///
+  // Get the resource type for this request. Only available in the browser
+  // process.
+  ///
+  cef_resource_type_t(CEF_CALLBACK* get_resource_type)(
+      struct _cef_request_t* self);
+
+  ///
+  // Get the transition type for this request. Only available in the browser
+  // process and only applies to requests that represent a main frame or sub-
+  // frame navigation.
+  ///
+  cef_transition_type_t(CEF_CALLBACK* get_transition_type)(
+      struct _cef_request_t* self);
+
+  ///
+  // Returns the globally unique identifier for this request or 0 if not
+  // specified. Can be used by cef_resource_request_handler_t implementations in
+  // the browser process to track a single request across multiple callbacks.
+  ///
+  uint64(CEF_CALLBACK* get_identifier)(struct _cef_request_t* self);
+} cef_request_t;
+
+///
+// Create a new cef_request_t object.
+///
+CEF_EXPORT cef_request_t* cef_request_create();
+
+///
+// Structure used to represent post data for a web request. The functions of
+// this structure may be called on any thread.
+///
+typedef struct _cef_post_data_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is read-only.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_post_data_t* self);
+
+  ///
+  // Returns true (1) if the underlying POST data includes elements that are not
+  // represented by this cef_post_data_t object (for example, multi-part file
+  // upload data). Modifying cef_post_data_t objects with excluded elements may
+  // result in the request failing.
+  ///
+  int(CEF_CALLBACK* has_excluded_elements)(struct _cef_post_data_t* self);
+
+  ///
+  // Returns the number of existing post data elements.
+  ///
+  size_t(CEF_CALLBACK* get_element_count)(struct _cef_post_data_t* self);
+
+  ///
+  // Retrieve the post data elements.
+  ///
+  void(CEF_CALLBACK* get_elements)(struct _cef_post_data_t* self,
+                                   size_t* elementsCount,
+                                   struct _cef_post_data_element_t** elements);
+
+  ///
+  // Remove the specified post data element.  Returns true (1) if the removal
+  // succeeds.
+  ///
+  int(CEF_CALLBACK* remove_element)(struct _cef_post_data_t* self,
+                                    struct _cef_post_data_element_t* element);
+
+  ///
+  // Add the specified post data element.  Returns true (1) if the add succeeds.
+  ///
+  int(CEF_CALLBACK* add_element)(struct _cef_post_data_t* self,
+                                 struct _cef_post_data_element_t* element);
+
+  ///
+  // Remove all existing post data elements.
+  ///
+  void(CEF_CALLBACK* remove_elements)(struct _cef_post_data_t* self);
+} cef_post_data_t;
+
+///
+// Create a new cef_post_data_t object.
+///
+CEF_EXPORT cef_post_data_t* cef_post_data_create();
+
+///
+// Structure used to represent a single element in the request post data. The
+// functions of this structure may be called on any thread.
+///
+typedef struct _cef_post_data_element_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is read-only.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_post_data_element_t* self);
+
+  ///
+  // Remove all contents from the post data element.
+  ///
+  void(CEF_CALLBACK* set_to_empty)(struct _cef_post_data_element_t* self);
+
+  ///
+  // The post data element will represent a file.
+  ///
+  void(CEF_CALLBACK* set_to_file)(struct _cef_post_data_element_t* self,
+                                  const cef_string_t* fileName);
+
+  ///
+  // The post data element will represent bytes.  The bytes passed in will be
+  // copied.
+  ///
+  void(CEF_CALLBACK* set_to_bytes)(struct _cef_post_data_element_t* self,
+                                   size_t size,
+                                   const void* bytes);
+
+  ///
+  // Return the type of this post data element.
+  ///
+  cef_postdataelement_type_t(CEF_CALLBACK* get_type)(
+      struct _cef_post_data_element_t* self);
+
+  ///
+  // Return the file name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_file)(
+      struct _cef_post_data_element_t* self);
+
+  ///
+  // Return the number of bytes.
+  ///
+  size_t(CEF_CALLBACK* get_bytes_count)(struct _cef_post_data_element_t* self);
+
+  ///
+  // Read up to |size| bytes into |bytes| and return the number of bytes
+  // actually read.
+  ///
+  size_t(CEF_CALLBACK* get_bytes)(struct _cef_post_data_element_t* self,
+                                  size_t size,
+                                  void* bytes);
+} cef_post_data_element_t;
+
+///
+// Create a new cef_post_data_element_t object.
+///
+CEF_EXPORT cef_post_data_element_t* cef_post_data_element_create();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_REQUEST_CAPI_H_
diff --git a/src/include/capi/cef_request_context_capi.h b/src/include/capi/cef_request_context_capi.h
new file mode 100644
index 0000000..679204c
--- /dev/null
+++ b/src/include/capi/cef_request_context_capi.h
@@ -0,0 +1,390 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=7ce0953f069204a4dd2037c4a05ac9454c5e66a6$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_callback_capi.h"
+#include "include/capi/cef_cookie_capi.h"
+#include "include/capi/cef_extension_capi.h"
+#include "include/capi/cef_extension_handler_capi.h"
+#include "include/capi/cef_media_router_capi.h"
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_request_context_handler_t;
+struct _cef_scheme_handler_factory_t;
+
+///
+// Callback structure for cef_request_context_t::ResolveHost.
+///
+typedef struct _cef_resolve_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the UI thread after the ResolveHost request has completed.
+  // |result| will be the result code. |resolved_ips| will be the list of
+  // resolved IP addresses or NULL if the resolution failed.
+  ///
+  void(CEF_CALLBACK* on_resolve_completed)(struct _cef_resolve_callback_t* self,
+                                           cef_errorcode_t result,
+                                           cef_string_list_t resolved_ips);
+} cef_resolve_callback_t;
+
+///
+// A request context provides request handling for a set of related browser or
+// URL request objects. A request context can be specified when creating a new
+// browser via the cef_browser_host_t static factory functions or when creating
+// a new URL request via the cef_urlrequest_t static factory functions. Browser
+// objects with different request contexts will never be hosted in the same
+// render process. Browser objects with the same request context may or may not
+// be hosted in the same render process depending on the process model. Browser
+// objects created indirectly via the JavaScript window.open function or
+// targeted links will share the same render process and the same request
+// context as the source browser. When running in single-process mode there is
+// only a single render process (the main process) and so all browsers created
+// in single-process mode will share the same request context. This will be the
+// first request context passed into a cef_browser_host_t static factory
+// function and all other request context objects will be ignored.
+///
+typedef struct _cef_request_context_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is pointing to the same context as |that|
+  // object.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_request_context_t* self,
+                             struct _cef_request_context_t* other);
+
+  ///
+  // Returns true (1) if this object is sharing the same storage as |that|
+  // object.
+  ///
+  int(CEF_CALLBACK* is_sharing_with)(struct _cef_request_context_t* self,
+                                     struct _cef_request_context_t* other);
+
+  ///
+  // Returns true (1) if this object is the global context. The global context
+  // is used by default when creating a browser or URL request with a NULL
+  // context argument.
+  ///
+  int(CEF_CALLBACK* is_global)(struct _cef_request_context_t* self);
+
+  ///
+  // Returns the handler for this context if any.
+  ///
+  struct _cef_request_context_handler_t*(CEF_CALLBACK* get_handler)(
+      struct _cef_request_context_t* self);
+
+  ///
+  // Returns the cache path for this object. If NULL an "incognito mode" in-
+  // memory cache is being used.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_cache_path)(
+      struct _cef_request_context_t* self);
+
+  ///
+  // Returns the cookie manager for this object. If |callback| is non-NULL it
+  // will be executed asnychronously on the IO thread after the manager's
+  // storage has been initialized.
+  ///
+  struct _cef_cookie_manager_t*(CEF_CALLBACK* get_cookie_manager)(
+      struct _cef_request_context_t* self,
+      struct _cef_completion_callback_t* callback);
+
+  ///
+  // Register a scheme handler factory for the specified |scheme_name| and
+  // optional |domain_name|. An NULL |domain_name| value for a standard scheme
+  // will cause the factory to match all domain names. The |domain_name| value
+  // will be ignored for non-standard schemes. If |scheme_name| is a built-in
+  // scheme and no handler is returned by |factory| then the built-in scheme
+  // handler factory will be called. If |scheme_name| is a custom scheme then
+  // you must also implement the cef_app_t::on_register_custom_schemes()
+  // function in all processes. This function may be called multiple times to
+  // change or remove the factory that matches the specified |scheme_name| and
+  // optional |domain_name|. Returns false (0) if an error occurs. This function
+  // may be called on any thread in the browser process.
+  ///
+  int(CEF_CALLBACK* register_scheme_handler_factory)(
+      struct _cef_request_context_t* self,
+      const cef_string_t* scheme_name,
+      const cef_string_t* domain_name,
+      struct _cef_scheme_handler_factory_t* factory);
+
+  ///
+  // Clear all registered scheme handler factories. Returns false (0) on error.
+  // This function may be called on any thread in the browser process.
+  ///
+  int(CEF_CALLBACK* clear_scheme_handler_factories)(
+      struct _cef_request_context_t* self);
+
+  ///
+  // Tells all renderer processes associated with this context to throw away
+  // their plugin list cache. If |reload_pages| is true (1) they will also
+  // reload all pages with plugins.
+  // cef_request_context_handler_t::OnBeforePluginLoad may be called to rebuild
+  // the plugin list cache.
+  ///
+  void(CEF_CALLBACK* purge_plugin_list_cache)(
+      struct _cef_request_context_t* self,
+      int reload_pages);
+
+  ///
+  // Returns true (1) if a preference with the specified |name| exists. This
+  // function must be called on the browser process UI thread.
+  ///
+  int(CEF_CALLBACK* has_preference)(struct _cef_request_context_t* self,
+                                    const cef_string_t* name);
+
+  ///
+  // Returns the value for the preference with the specified |name|. Returns
+  // NULL if the preference does not exist. The returned object contains a copy
+  // of the underlying preference value and modifications to the returned object
+  // will not modify the underlying preference value. This function must be
+  // called on the browser process UI thread.
+  ///
+  struct _cef_value_t*(CEF_CALLBACK* get_preference)(
+      struct _cef_request_context_t* self,
+      const cef_string_t* name);
+
+  ///
+  // Returns all preferences as a dictionary. If |include_defaults| is true (1)
+  // then preferences currently at their default value will be included. The
+  // returned object contains a copy of the underlying preference values and
+  // modifications to the returned object will not modify the underlying
+  // preference values. This function must be called on the browser process UI
+  // thread.
+  ///
+  struct _cef_dictionary_value_t*(CEF_CALLBACK* get_all_preferences)(
+      struct _cef_request_context_t* self,
+      int include_defaults);
+
+  ///
+  // Returns true (1) if the preference with the specified |name| can be
+  // modified using SetPreference. As one example preferences set via the
+  // command-line usually cannot be modified. This function must be called on
+  // the browser process UI thread.
+  ///
+  int(CEF_CALLBACK* can_set_preference)(struct _cef_request_context_t* self,
+                                        const cef_string_t* name);
+
+  ///
+  // Set the |value| associated with preference |name|. Returns true (1) if the
+  // value is set successfully and false (0) otherwise. If |value| is NULL the
+  // preference will be restored to its default value. If setting the preference
+  // fails then |error| will be populated with a detailed description of the
+  // problem. This function must be called on the browser process UI thread.
+  ///
+  int(CEF_CALLBACK* set_preference)(struct _cef_request_context_t* self,
+                                    const cef_string_t* name,
+                                    struct _cef_value_t* value,
+                                    cef_string_t* error);
+
+  ///
+  // Clears all certificate exceptions that were added as part of handling
+  // cef_request_handler_t::on_certificate_error(). If you call this it is
+  // recommended that you also call close_all_connections() or you risk not
+  // being prompted again for server certificates if you reconnect quickly. If
+  // |callback| is non-NULL it will be executed on the UI thread after
+  // completion.
+  ///
+  void(CEF_CALLBACK* clear_certificate_exceptions)(
+      struct _cef_request_context_t* self,
+      struct _cef_completion_callback_t* callback);
+
+  ///
+  // Clears all HTTP authentication credentials that were added as part of
+  // handling GetAuthCredentials. If |callback| is non-NULL it will be executed
+  // on the UI thread after completion.
+  ///
+  void(CEF_CALLBACK* clear_http_auth_credentials)(
+      struct _cef_request_context_t* self,
+      struct _cef_completion_callback_t* callback);
+
+  ///
+  // Clears all active and idle connections that Chromium currently has. This is
+  // only recommended if you have released all other CEF objects but don't yet
+  // want to call cef_shutdown(). If |callback| is non-NULL it will be executed
+  // on the UI thread after completion.
+  ///
+  void(CEF_CALLBACK* close_all_connections)(
+      struct _cef_request_context_t* self,
+      struct _cef_completion_callback_t* callback);
+
+  ///
+  // Attempts to resolve |origin| to a list of associated IP addresses.
+  // |callback| will be executed on the UI thread after completion.
+  ///
+  void(CEF_CALLBACK* resolve_host)(struct _cef_request_context_t* self,
+                                   const cef_string_t* origin,
+                                   struct _cef_resolve_callback_t* callback);
+
+  ///
+  // Load an extension.
+  //
+  // If extension resources will be read from disk using the default load
+  // implementation then |root_directory| should be the absolute path to the
+  // extension resources directory and |manifest| should be NULL. If extension
+  // resources will be provided by the client (e.g. via cef_request_handler_t
+  // and/or cef_extension_handler_t) then |root_directory| should be a path
+  // component unique to the extension (if not absolute this will be internally
+  // prefixed with the PK_DIR_RESOURCES path) and |manifest| should contain the
+  // contents that would otherwise be read from the "manifest.json" file on
+  // disk.
+  //
+  // The loaded extension will be accessible in all contexts sharing the same
+  // storage (HasExtension returns true (1)). However, only the context on which
+  // this function was called is considered the loader (DidLoadExtension returns
+  // true (1)) and only the loader will receive cef_request_context_handler_t
+  // callbacks for the extension.
+  //
+  // cef_extension_handler_t::OnExtensionLoaded will be called on load success
+  // or cef_extension_handler_t::OnExtensionLoadFailed will be called on load
+  // failure.
+  //
+  // If the extension specifies a background script via the "background"
+  // manifest key then cef_extension_handler_t::OnBeforeBackgroundBrowser will
+  // be called to create the background browser. See that function for
+  // additional information about background scripts.
+  //
+  // For visible extension views the client application should evaluate the
+  // manifest to determine the correct extension URL to load and then pass that
+  // URL to the cef_browser_host_t::CreateBrowser* function after the extension
+  // has loaded. For example, the client can look for the "browser_action"
+  // manifest key as documented at
+  // https://developer.chrome.com/extensions/browserAction. Extension URLs take
+  // the form "chrome-extension://<extension_id>/<path>".
+  //
+  // Browsers that host extensions differ from normal browsers as follows:
+  //  - Can access chrome.* JavaScript APIs if allowed by the manifest. Visit
+  //    chrome://extensions-support for the list of extension APIs currently
+  //    supported by CEF.
+  //  - Main frame navigation to non-extension content is blocked.
+  //  - Pinch-zooming is disabled.
+  //  - CefBrowserHost::GetExtension returns the hosted extension.
+  //  - CefBrowserHost::IsBackgroundHost returns true for background hosts.
+  //
+  // See https://developer.chrome.com/extensions for extension implementation
+  // and usage documentation.
+  ///
+  void(CEF_CALLBACK* load_extension)(struct _cef_request_context_t* self,
+                                     const cef_string_t* root_directory,
+                                     struct _cef_dictionary_value_t* manifest,
+                                     struct _cef_extension_handler_t* handler);
+
+  ///
+  // Returns true (1) if this context was used to load the extension identified
+  // by |extension_id|. Other contexts sharing the same storage will also have
+  // access to the extension (see HasExtension). This function must be called on
+  // the browser process UI thread.
+  ///
+  int(CEF_CALLBACK* did_load_extension)(struct _cef_request_context_t* self,
+                                        const cef_string_t* extension_id);
+
+  ///
+  // Returns true (1) if this context has access to the extension identified by
+  // |extension_id|. This may not be the context that was used to load the
+  // extension (see DidLoadExtension). This function must be called on the
+  // browser process UI thread.
+  ///
+  int(CEF_CALLBACK* has_extension)(struct _cef_request_context_t* self,
+                                   const cef_string_t* extension_id);
+
+  ///
+  // Retrieve the list of all extensions that this context has access to (see
+  // HasExtension). |extension_ids| will be populated with the list of extension
+  // ID values. Returns true (1) on success. This function must be called on the
+  // browser process UI thread.
+  ///
+  int(CEF_CALLBACK* get_extensions)(struct _cef_request_context_t* self,
+                                    cef_string_list_t extension_ids);
+
+  ///
+  // Returns the extension matching |extension_id| or NULL if no matching
+  // extension is accessible in this context (see HasExtension). This function
+  // must be called on the browser process UI thread.
+  ///
+  struct _cef_extension_t*(CEF_CALLBACK* get_extension)(
+      struct _cef_request_context_t* self,
+      const cef_string_t* extension_id);
+
+  ///
+  // Returns the MediaRouter object associated with this context.
+  ///
+  struct _cef_media_router_t*(CEF_CALLBACK* get_media_router)(
+      struct _cef_request_context_t* self);
+} cef_request_context_t;
+
+///
+// Returns the global context object.
+///
+CEF_EXPORT cef_request_context_t* cef_request_context_get_global_context();
+
+///
+// Creates a new context object with the specified |settings| and optional
+// |handler|.
+///
+CEF_EXPORT cef_request_context_t* cef_request_context_create_context(
+    const struct _cef_request_context_settings_t* settings,
+    struct _cef_request_context_handler_t* handler);
+
+///
+// Creates a new context object that shares storage with |other| and uses an
+// optional |handler|.
+///
+CEF_EXPORT cef_request_context_t* cef_create_context_shared(
+    cef_request_context_t* other,
+    struct _cef_request_context_handler_t* handler);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_CAPI_H_
diff --git a/src/include/capi/cef_request_context_handler_capi.h b/src/include/capi/cef_request_context_handler_capi.h
new file mode 100644
index 0000000..2d94686
--- /dev/null
+++ b/src/include/capi/cef_request_context_handler_capi.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=e758d8c53334b91bce818cc6e9f84915778d7827$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_resource_request_handler_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to provide handler implementations. The handler
+// instance will not be released until all objects related to the context have
+// been destroyed.
+///
+typedef struct _cef_request_context_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the browser process UI thread immediately after the request
+  // context has been initialized.
+  ///
+  void(CEF_CALLBACK* on_request_context_initialized)(
+      struct _cef_request_context_handler_t* self,
+      struct _cef_request_context_t* request_context);
+
+  ///
+  // Called on multiple browser process threads before a plugin instance is
+  // loaded. |mime_type| is the mime type of the plugin that will be loaded.
+  // |plugin_url| is the content URL that the plugin will load and may be NULL.
+  // |is_main_frame| will be true (1) if the plugin is being loaded in the main
+  // (top-level) frame, |top_origin_url| is the URL for the top-level frame that
+  // contains the plugin when loading a specific plugin instance or NULL when
+  // building the initial list of enabled plugins for 'navigator.plugins'
+  // JavaScript state. |plugin_info| includes additional information about the
+  // plugin that will be loaded. |plugin_policy| is the recommended policy.
+  // Modify |plugin_policy| and return true (1) to change the policy. Return
+  // false (0) to use the recommended policy. The default plugin policy can be
+  // set at runtime using the `--plugin-policy=[allow|detect|block]` command-
+  // line flag. Decisions to mark a plugin as disabled by setting
+  // |plugin_policy| to PLUGIN_POLICY_DISABLED may be cached when
+  // |top_origin_url| is NULL. To purge the plugin list cache and potentially
+  // trigger new calls to this function call
+  // cef_request_context_t::PurgePluginListCache.
+  ///
+  int(CEF_CALLBACK* on_before_plugin_load)(
+      struct _cef_request_context_handler_t* self,
+      const cef_string_t* mime_type,
+      const cef_string_t* plugin_url,
+      int is_main_frame,
+      const cef_string_t* top_origin_url,
+      struct _cef_web_plugin_info_t* plugin_info,
+      cef_plugin_policy_t* plugin_policy);
+
+  ///
+  // Called on the browser process IO thread before a resource request is
+  // initiated. The |browser| and |frame| values represent the source of the
+  // request, and may be NULL for requests originating from service workers or
+  // cef_urlrequest_t. |request| represents the request contents and cannot be
+  // modified in this callback. |is_navigation| will be true (1) if the resource
+  // request is a navigation. |is_download| will be true (1) if the resource
+  // request is a download. |request_initiator| is the origin (scheme + domain)
+  // of the page that initiated the request. Set |disable_default_handling| to
+  // true (1) to disable default handling of the request, in which case it will
+  // need to be handled via cef_resource_request_handler_t::GetResourceHandler
+  // or it will be canceled. To allow the resource load to proceed with default
+  // handling return NULL. To specify a handler for the resource return a
+  // cef_resource_request_handler_t object. This function will not be called if
+  // the client associated with |browser| returns a non-NULL value from
+  // cef_request_handler_t::GetResourceRequestHandler for the same request
+  // (identified by cef_request_t::GetIdentifier).
+  ///
+  struct _cef_resource_request_handler_t*(
+      CEF_CALLBACK* get_resource_request_handler)(
+      struct _cef_request_context_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      int is_navigation,
+      int is_download,
+      const cef_string_t* request_initiator,
+      int* disable_default_handling);
+} cef_request_context_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_request_handler_capi.h b/src/include/capi/cef_request_handler_capi.h
new file mode 100644
index 0000000..cd6be4f
--- /dev/null
+++ b/src/include/capi/cef_request_handler_capi.h
@@ -0,0 +1,270 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=0167eb1abe614bd6391d273a8085fa3e53e7c217$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_auth_callback_capi.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_request_callback_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_resource_request_handler_capi.h"
+#include "include/capi/cef_ssl_info_capi.h"
+#include "include/capi/cef_x509_certificate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback structure used to select a client certificate for authentication.
+///
+typedef struct _cef_select_client_certificate_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Chooses the specified certificate for client certificate authentication.
+  // NULL value means that no client certificate should be used.
+  ///
+  void(CEF_CALLBACK* select)(
+      struct _cef_select_client_certificate_callback_t* self,
+      struct _cef_x509certificate_t* cert);
+} cef_select_client_certificate_callback_t;
+
+///
+// Implement this structure to handle events related to browser requests. The
+// functions of this structure will be called on the thread indicated.
+///
+typedef struct _cef_request_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the UI thread before browser navigation. Return true (1) to
+  // cancel the navigation or false (0) to allow the navigation to proceed. The
+  // |request| object cannot be modified in this callback.
+  // cef_load_handler_t::OnLoadingStateChange will be called twice in all cases.
+  // If the navigation is allowed cef_load_handler_t::OnLoadStart and
+  // cef_load_handler_t::OnLoadEnd will be called. If the navigation is canceled
+  // cef_load_handler_t::OnLoadError will be called with an |errorCode| value of
+  // ERR_ABORTED. The |user_gesture| value will be true (1) if the browser
+  // navigated via explicit user gesture (e.g. clicking a link) or false (0) if
+  // it navigated automatically (e.g. via the DomContentLoaded event).
+  ///
+  int(CEF_CALLBACK* on_before_browse)(struct _cef_request_handler_t* self,
+                                      struct _cef_browser_t* browser,
+                                      struct _cef_frame_t* frame,
+                                      struct _cef_request_t* request,
+                                      int user_gesture,
+                                      int is_redirect);
+
+  ///
+  // Called on the UI thread before OnBeforeBrowse in certain limited cases
+  // where navigating a new or different browser might be desirable. This
+  // includes user-initiated navigation that might open in a special way (e.g.
+  // links clicked via middle-click or ctrl + left-click) and certain types of
+  // cross-origin navigation initiated from the renderer process (e.g.
+  // navigating the top-level frame to/from a file URL). The |browser| and
+  // |frame| values represent the source of the navigation. The
+  // |target_disposition| value indicates where the user intended to navigate
+  // the browser based on standard Chromium behaviors (e.g. current tab, new
+  // tab, etc). The |user_gesture| value will be true (1) if the browser
+  // navigated via explicit user gesture (e.g. clicking a link) or false (0) if
+  // it navigated automatically (e.g. via the DomContentLoaded event). Return
+  // true (1) to cancel the navigation or false (0) to allow the navigation to
+  // proceed in the source browser's top-level frame.
+  ///
+  int(CEF_CALLBACK* on_open_urlfrom_tab)(
+      struct _cef_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      const cef_string_t* target_url,
+      cef_window_open_disposition_t target_disposition,
+      int user_gesture);
+
+  ///
+  // Called on the browser process IO thread before a resource request is
+  // initiated. The |browser| and |frame| values represent the source of the
+  // request. |request| represents the request contents and cannot be modified
+  // in this callback. |is_navigation| will be true (1) if the resource request
+  // is a navigation. |is_download| will be true (1) if the resource request is
+  // a download. |request_initiator| is the origin (scheme + domain) of the page
+  // that initiated the request. Set |disable_default_handling| to true (1) to
+  // disable default handling of the request, in which case it will need to be
+  // handled via cef_resource_request_handler_t::GetResourceHandler or it will
+  // be canceled. To allow the resource load to proceed with default handling
+  // return NULL. To specify a handler for the resource return a
+  // cef_resource_request_handler_t object. If this callback returns NULL the
+  // same function will be called on the associated
+  // cef_request_context_handler_t, if any.
+  ///
+  struct _cef_resource_request_handler_t*(
+      CEF_CALLBACK* get_resource_request_handler)(
+      struct _cef_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      int is_navigation,
+      int is_download,
+      const cef_string_t* request_initiator,
+      int* disable_default_handling);
+
+  ///
+  // Called on the IO thread when the browser needs credentials from the user.
+  // |origin_url| is the origin making this authentication request. |isProxy|
+  // indicates whether the host is a proxy server. |host| contains the hostname
+  // and |port| contains the port number. |realm| is the realm of the challenge
+  // and may be NULL. |scheme| is the authentication scheme used, such as
+  // "basic" or "digest", and will be NULL if the source of the request is an
+  // FTP server. Return true (1) to continue the request and call
+  // cef_auth_callback_t::cont() either in this function or at a later time when
+  // the authentication information is available. Return false (0) to cancel the
+  // request immediately.
+  ///
+  int(CEF_CALLBACK* get_auth_credentials)(
+      struct _cef_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      const cef_string_t* origin_url,
+      int isProxy,
+      const cef_string_t* host,
+      int port,
+      const cef_string_t* realm,
+      const cef_string_t* scheme,
+      struct _cef_auth_callback_t* callback);
+
+  ///
+  // Called on the IO thread when JavaScript requests a specific storage quota
+  // size via the webkitStorageInfo.requestQuota function. |origin_url| is the
+  // origin of the page making the request. |new_size| is the requested quota
+  // size in bytes. Return true (1) to continue the request and call
+  // cef_request_callback_t::cont() either in this function or at a later time
+  // to grant or deny the request. Return false (0) to cancel the request
+  // immediately.
+  ///
+  int(CEF_CALLBACK* on_quota_request)(struct _cef_request_handler_t* self,
+                                      struct _cef_browser_t* browser,
+                                      const cef_string_t* origin_url,
+                                      int64 new_size,
+                                      struct _cef_request_callback_t* callback);
+
+  ///
+  // Called on the UI thread to handle requests for URLs with an invalid SSL
+  // certificate. Return true (1) and call cef_request_callback_t::cont() either
+  // in this function or at a later time to continue or cancel the request.
+  // Return false (0) to cancel the request immediately. If
+  // CefSettings.ignore_certificate_errors is set all invalid certificates will
+  // be accepted without calling this function.
+  ///
+  int(CEF_CALLBACK* on_certificate_error)(
+      struct _cef_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      cef_errorcode_t cert_error,
+      const cef_string_t* request_url,
+      struct _cef_sslinfo_t* ssl_info,
+      struct _cef_request_callback_t* callback);
+
+  ///
+  // Called on the UI thread when a client certificate is being requested for
+  // authentication. Return false (0) to use the default behavior and
+  // automatically select the first certificate available. Return true (1) and
+  // call cef_select_client_certificate_callback_t::Select either in this
+  // function or at a later time to select a certificate. Do not call Select or
+  // call it with NULL to continue without using any certificate. |isProxy|
+  // indicates whether the host is an HTTPS proxy or the origin server. |host|
+  // and |port| contains the hostname and port of the SSL server. |certificates|
+  // is the list of certificates to choose from; this list has already been
+  // pruned by Chromium so that it only contains certificates from issuers that
+  // the server trusts.
+  ///
+  int(CEF_CALLBACK* on_select_client_certificate)(
+      struct _cef_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      int isProxy,
+      const cef_string_t* host,
+      int port,
+      size_t certificatesCount,
+      struct _cef_x509certificate_t* const* certificates,
+      struct _cef_select_client_certificate_callback_t* callback);
+
+  ///
+  // Called on the browser process UI thread when a plugin has crashed.
+  // |plugin_path| is the path of the plugin that crashed.
+  ///
+  void(CEF_CALLBACK* on_plugin_crashed)(struct _cef_request_handler_t* self,
+                                        struct _cef_browser_t* browser,
+                                        const cef_string_t* plugin_path);
+
+  ///
+  // Called on the browser process UI thread when the render view associated
+  // with |browser| is ready to receive/handle IPC messages in the render
+  // process.
+  ///
+  void(CEF_CALLBACK* on_render_view_ready)(struct _cef_request_handler_t* self,
+                                           struct _cef_browser_t* browser);
+
+  ///
+  // Called on the browser process UI thread when the render process terminates
+  // unexpectedly. |status| indicates how the process terminated.
+  ///
+  void(CEF_CALLBACK* on_render_process_terminated)(
+      struct _cef_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      cef_termination_status_t status);
+
+  ///
+  // Called on the browser process UI thread when the window.document object of
+  // the main frame has been created.
+  ///
+  void(CEF_CALLBACK* on_document_available_in_main_frame)(
+      struct _cef_request_handler_t* self,
+      struct _cef_browser_t* browser);
+} cef_request_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_resource_bundle_capi.h b/src/include/capi/cef_resource_bundle_capi.h
new file mode 100644
index 0000000..3a88870
--- /dev/null
+++ b/src/include/capi/cef_resource_bundle_capi.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=b9577b495df3990284d4e4a3db2824196175dc91$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RESOURCE_BUNDLE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RESOURCE_BUNDLE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used for retrieving resources from the resource bundle (*.pak)
+// files loaded by CEF during startup or via the cef_resource_bundle_handler_t
+// returned from cef_app_t::GetResourceBundleHandler. See CefSettings for
+// additional options related to resource bundle loading. The functions of this
+// structure may be called on any thread unless otherwise indicated.
+///
+typedef struct _cef_resource_bundle_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the localized string for the specified |string_id| or an NULL
+  // string if the value is not found. Include cef_pack_strings.h for a listing
+  // of valid string ID values.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_localized_string)(
+      struct _cef_resource_bundle_t* self,
+      int string_id);
+
+  ///
+  // Retrieves the contents of the specified scale independent |resource_id|. If
+  // the value is found then |data| and |data_size| will be populated and this
+  // function will return true (1). If the value is not found then this function
+  // will return false (0). The returned |data| pointer will remain resident in
+  // memory and should not be freed. Include cef_pack_resources.h for a listing
+  // of valid resource ID values.
+  ///
+  int(CEF_CALLBACK* get_data_resource)(struct _cef_resource_bundle_t* self,
+                                       int resource_id,
+                                       void** data,
+                                       size_t* data_size);
+
+  ///
+  // Retrieves the contents of the specified |resource_id| nearest the scale
+  // factor |scale_factor|. Use a |scale_factor| value of SCALE_FACTOR_NONE for
+  // scale independent resources or call GetDataResource instead. If the value
+  // is found then |data| and |data_size| will be populated and this function
+  // will return true (1). If the value is not found then this function will
+  // return false (0). The returned |data| pointer will remain resident in
+  // memory and should not be freed. Include cef_pack_resources.h for a listing
+  // of valid resource ID values.
+  ///
+  int(CEF_CALLBACK* get_data_resource_for_scale)(
+      struct _cef_resource_bundle_t* self,
+      int resource_id,
+      cef_scale_factor_t scale_factor,
+      void** data,
+      size_t* data_size);
+} cef_resource_bundle_t;
+
+///
+// Returns the global resource bundle instance.
+///
+CEF_EXPORT cef_resource_bundle_t* cef_resource_bundle_get_global();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RESOURCE_BUNDLE_CAPI_H_
diff --git a/src/include/capi/cef_resource_bundle_handler_capi.h b/src/include/capi/cef_resource_bundle_handler_capi.h
new file mode 100644
index 0000000..1cbae99
--- /dev/null
+++ b/src/include/capi/cef_resource_bundle_handler_capi.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=fd90a707c59a8c04b1b1bfc6129a90e27934f501$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RESOURCE_BUNDLE_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RESOURCE_BUNDLE_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to implement a custom resource bundle structure. See
+// CefSettings for additional options related to resource bundle loading. The
+// functions of this structure may be called on multiple threads.
+///
+typedef struct _cef_resource_bundle_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called to retrieve a localized translation for the specified |string_id|.
+  // To provide the translation set |string| to the translation string and
+  // return true (1). To use the default translation return false (0). Include
+  // cef_pack_strings.h for a listing of valid string ID values.
+  ///
+  int(CEF_CALLBACK* get_localized_string)(
+      struct _cef_resource_bundle_handler_t* self,
+      int string_id,
+      cef_string_t* string);
+
+  ///
+  // Called to retrieve data for the specified scale independent |resource_id|.
+  // To provide the resource data set |data| and |data_size| to the data pointer
+  // and size respectively and return true (1). To use the default resource data
+  // return false (0). The resource data will not be copied and must remain
+  // resident in memory. Include cef_pack_resources.h for a listing of valid
+  // resource ID values.
+  ///
+  int(CEF_CALLBACK* get_data_resource)(
+      struct _cef_resource_bundle_handler_t* self,
+      int resource_id,
+      void** data,
+      size_t* data_size);
+
+  ///
+  // Called to retrieve data for the specified |resource_id| nearest the scale
+  // factor |scale_factor|. To provide the resource data set |data| and
+  // |data_size| to the data pointer and size respectively and return true (1).
+  // To use the default resource data return false (0). The resource data will
+  // not be copied and must remain resident in memory. Include
+  // cef_pack_resources.h for a listing of valid resource ID values.
+  ///
+  int(CEF_CALLBACK* get_data_resource_for_scale)(
+      struct _cef_resource_bundle_handler_t* self,
+      int resource_id,
+      cef_scale_factor_t scale_factor,
+      void** data,
+      size_t* data_size);
+} cef_resource_bundle_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RESOURCE_BUNDLE_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_resource_handler_capi.h b/src/include/capi/cef_resource_handler_capi.h
new file mode 100644
index 0000000..1dc34f4
--- /dev/null
+++ b/src/include/capi/cef_resource_handler_capi.h
@@ -0,0 +1,209 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=5241e3dd5d3fa0b17dd6d6ea2f30734a32150c88$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RESOURCE_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RESOURCE_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_callback_capi.h"
+#include "include/capi/cef_cookie_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_response_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Callback for asynchronous continuation of cef_resource_handler_t::skip().
+///
+typedef struct _cef_resource_skip_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Callback for asynchronous continuation of skip(). If |bytes_skipped| > 0
+  // then either skip() will be called again until the requested number of bytes
+  // have been skipped or the request will proceed. If |bytes_skipped| <= 0 the
+  // request will fail with ERR_REQUEST_RANGE_NOT_SATISFIABLE.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_resource_skip_callback_t* self,
+                           int64 bytes_skipped);
+} cef_resource_skip_callback_t;
+
+///
+// Callback for asynchronous continuation of cef_resource_handler_t::read().
+///
+typedef struct _cef_resource_read_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Callback for asynchronous continuation of read(). If |bytes_read| == 0 the
+  // response will be considered complete. If |bytes_read| > 0 then read() will
+  // be called again until the request is complete (based on either the result
+  // or the expected content length). If |bytes_read| < 0 then the request will
+  // fail and the |bytes_read| value will be treated as the error code.
+  ///
+  void(CEF_CALLBACK* cont)(struct _cef_resource_read_callback_t* self,
+                           int bytes_read);
+} cef_resource_read_callback_t;
+
+///
+// Structure used to implement a custom request handler structure. The functions
+// of this structure will be called on the IO thread unless otherwise indicated.
+///
+typedef struct _cef_resource_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Open the response stream. To handle the request immediately set
+  // |handle_request| to true (1) and return true (1). To decide at a later time
+  // set |handle_request| to false (0), return true (1), and execute |callback|
+  // to continue or cancel the request. To cancel the request immediately set
+  // |handle_request| to true (1) and return false (0). This function will be
+  // called in sequence but not from a dedicated thread. For backwards
+  // compatibility set |handle_request| to false (0) and return false (0) and
+  // the ProcessRequest function will be called.
+  ///
+  int(CEF_CALLBACK* open)(struct _cef_resource_handler_t* self,
+                          struct _cef_request_t* request,
+                          int* handle_request,
+                          struct _cef_callback_t* callback);
+
+  ///
+  // Begin processing the request. To handle the request return true (1) and
+  // call cef_callback_t::cont() once the response header information is
+  // available (cef_callback_t::cont() can also be called from inside this
+  // function if header information is available immediately). To cancel the
+  // request return false (0).
+  //
+  // WARNING: This function is deprecated. Use Open instead.
+  ///
+  int(CEF_CALLBACK* process_request)(struct _cef_resource_handler_t* self,
+                                     struct _cef_request_t* request,
+                                     struct _cef_callback_t* callback);
+
+  ///
+  // Retrieve response header information. If the response length is not known
+  // set |response_length| to -1 and read_response() will be called until it
+  // returns false (0). If the response length is known set |response_length| to
+  // a positive value and read_response() will be called until it returns false
+  // (0) or the specified number of bytes have been read. Use the |response|
+  // object to set the mime type, http status code and other optional header
+  // values. To redirect the request to a new URL set |redirectUrl| to the new
+  // URL. |redirectUrl| can be either a relative or fully qualified URL. It is
+  // also possible to set |response| to a redirect http status code and pass the
+  // new URL via a Location header. Likewise with |redirectUrl| it is valid to
+  // set a relative or fully qualified URL as the Location header value. If an
+  // error occured while setting up the request you can call set_error() on
+  // |response| to indicate the error condition.
+  ///
+  void(CEF_CALLBACK* get_response_headers)(struct _cef_resource_handler_t* self,
+                                           struct _cef_response_t* response,
+                                           int64* response_length,
+                                           cef_string_t* redirectUrl);
+
+  ///
+  // Skip response data when requested by a Range header. Skip over and discard
+  // |bytes_to_skip| bytes of response data. If data is available immediately
+  // set |bytes_skipped| to the number of bytes skipped and return true (1). To
+  // read the data at a later time set |bytes_skipped| to 0, return true (1) and
+  // execute |callback| when the data is available. To indicate failure set
+  // |bytes_skipped| to < 0 (e.g. -2 for ERR_FAILED) and return false (0). This
+  // function will be called in sequence but not from a dedicated thread.
+  ///
+  int(CEF_CALLBACK* skip)(struct _cef_resource_handler_t* self,
+                          int64 bytes_to_skip,
+                          int64* bytes_skipped,
+                          struct _cef_resource_skip_callback_t* callback);
+
+  ///
+  // Read response data. If data is available immediately copy up to
+  // |bytes_to_read| bytes into |data_out|, set |bytes_read| to the number of
+  // bytes copied, and return true (1). To read the data at a later time keep a
+  // pointer to |data_out|, set |bytes_read| to 0, return true (1) and execute
+  // |callback| when the data is available (|data_out| will remain valid until
+  // the callback is executed). To indicate response completion set |bytes_read|
+  // to 0 and return false (0). To indicate failure set |bytes_read| to < 0
+  // (e.g. -2 for ERR_FAILED) and return false (0). This function will be called
+  // in sequence but not from a dedicated thread. For backwards compatibility
+  // set |bytes_read| to -1 and return false (0) and the ReadResponse function
+  // will be called.
+  ///
+  int(CEF_CALLBACK* read)(struct _cef_resource_handler_t* self,
+                          void* data_out,
+                          int bytes_to_read,
+                          int* bytes_read,
+                          struct _cef_resource_read_callback_t* callback);
+
+  ///
+  // Read response data. If data is available immediately copy up to
+  // |bytes_to_read| bytes into |data_out|, set |bytes_read| to the number of
+  // bytes copied, and return true (1). To read the data at a later time set
+  // |bytes_read| to 0, return true (1) and call cef_callback_t::cont() when the
+  // data is available. To indicate response completion return false (0).
+  //
+  // WARNING: This function is deprecated. Use Skip and Read instead.
+  ///
+  int(CEF_CALLBACK* read_response)(struct _cef_resource_handler_t* self,
+                                   void* data_out,
+                                   int bytes_to_read,
+                                   int* bytes_read,
+                                   struct _cef_callback_t* callback);
+
+  ///
+  // Request processing has been canceled.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_resource_handler_t* self);
+} cef_resource_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RESOURCE_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_resource_request_handler_capi.h b/src/include/capi/cef_resource_request_handler_capi.h
new file mode 100644
index 0000000..428df69
--- /dev/null
+++ b/src/include/capi/cef_resource_request_handler_capi.h
@@ -0,0 +1,254 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=afc96f188710bd336d09ce479a650aaa3a55357a$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RESOURCE_REQUEST_HANDLER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RESOURCE_REQUEST_HANDLER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_request_callback_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/capi/cef_response_capi.h"
+#include "include/capi/cef_response_filter_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_cookie_access_filter_t;
+
+///
+// Implement this structure to handle events related to browser requests. The
+// functions of this structure will be called on the IO thread unless otherwise
+// indicated.
+///
+typedef struct _cef_resource_request_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the IO thread before a resource request is loaded. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or cef_urlrequest_t. To
+  // optionally filter cookies for the request return a
+  // cef_cookie_access_filter_t object. The |request| object cannot not be
+  // modified in this callback.
+  ///
+  struct _cef_cookie_access_filter_t*(CEF_CALLBACK* get_cookie_access_filter)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request);
+
+  ///
+  // Called on the IO thread before a resource request is loaded. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or cef_urlrequest_t. To redirect
+  // or change the resource load optionally modify |request|. Modification of
+  // the request URL will be treated as a redirect. Return RV_CONTINUE to
+  // continue the request immediately. Return RV_CONTINUE_ASYNC and call
+  // cef_request_callback_t:: cont() at a later time to continue or cancel the
+  // request asynchronously. Return RV_CANCEL to cancel the request immediately.
+  //
+  ///
+  cef_return_value_t(CEF_CALLBACK* on_before_resource_load)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      struct _cef_request_callback_t* callback);
+
+  ///
+  // Called on the IO thread before a resource is loaded. The |browser| and
+  // |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or cef_urlrequest_t. To allow the
+  // resource to load using the default network loader return NULL. To specify a
+  // handler for the resource return a cef_resource_handler_t object. The
+  // |request| object cannot not be modified in this callback.
+  ///
+  struct _cef_resource_handler_t*(CEF_CALLBACK* get_resource_handler)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request);
+
+  ///
+  // Called on the IO thread when a resource load is redirected. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or cef_urlrequest_t. The
+  // |request| parameter will contain the old URL and other request-related
+  // information. The |response| parameter will contain the response that
+  // resulted in the redirect. The |new_url| parameter will contain the new URL
+  // and can be changed if desired. The |request| and |response| objects cannot
+  // be modified in this callback.
+  ///
+  void(CEF_CALLBACK* on_resource_redirect)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      struct _cef_response_t* response,
+      cef_string_t* new_url);
+
+  ///
+  // Called on the IO thread when a resource response is received. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or cef_urlrequest_t. To allow the
+  // resource load to proceed without modification return false (0). To redirect
+  // or retry the resource load optionally modify |request| and return true (1).
+  // Modification of the request URL will be treated as a redirect. Requests
+  // handled using the default network loader cannot be redirected in this
+  // callback. The |response| object cannot be modified in this callback.
+  //
+  // WARNING: Redirecting using this function is deprecated. Use
+  // OnBeforeResourceLoad or GetResourceHandler to perform redirects.
+  ///
+  int(CEF_CALLBACK* on_resource_response)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      struct _cef_response_t* response);
+
+  ///
+  // Called on the IO thread to optionally filter resource response content. The
+  // |browser| and |frame| values represent the source of the request, and may
+  // be NULL for requests originating from service workers or cef_urlrequest_t.
+  // |request| and |response| represent the request and response respectively
+  // and cannot be modified in this callback.
+  ///
+  struct _cef_response_filter_t*(CEF_CALLBACK* get_resource_response_filter)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      struct _cef_response_t* response);
+
+  ///
+  // Called on the IO thread when a resource load has completed. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or cef_urlrequest_t. |request|
+  // and |response| represent the request and response respectively and cannot
+  // be modified in this callback. |status| indicates the load completion
+  // status. |received_content_length| is the number of response bytes actually
+  // read. This function will be called for all requests, including requests
+  // that are aborted due to CEF shutdown or destruction of the associated
+  // browser. In cases where the associated browser is destroyed this callback
+  // may arrive after the cef_life_span_handler_t::OnBeforeClose callback for
+  // that browser. The cef_frame_t::IsValid function can be used to test for
+  // this situation, and care should be taken not to call |browser| or |frame|
+  // functions that modify state (like LoadURL, SendProcessMessage, etc.) if the
+  // frame is invalid.
+  ///
+  void(CEF_CALLBACK* on_resource_load_complete)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      struct _cef_response_t* response,
+      cef_urlrequest_status_t status,
+      int64 received_content_length);
+
+  ///
+  // Called on the IO thread to handle requests for URLs with an unknown
+  // protocol component. The |browser| and |frame| values represent the source
+  // of the request, and may be NULL for requests originating from service
+  // workers or cef_urlrequest_t. |request| cannot be modified in this callback.
+  // Set |allow_os_execution| to true (1) to attempt execution via the
+  // registered OS protocol handler, if any. SECURITY WARNING: YOU SHOULD USE
+  // THIS METHOD TO ENFORCE RESTRICTIONS BASED ON SCHEME, HOST OR OTHER URL
+  // ANALYSIS BEFORE ALLOWING OS EXECUTION.
+  ///
+  void(CEF_CALLBACK* on_protocol_execution)(
+      struct _cef_resource_request_handler_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      struct _cef_request_t* request,
+      int* allow_os_execution);
+} cef_resource_request_handler_t;
+
+///
+// Implement this structure to filter cookies that may be sent or received from
+// resource requests. The functions of this structure will be called on the IO
+// thread unless otherwise indicated.
+///
+typedef struct _cef_cookie_access_filter_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called on the IO thread before a resource request is sent. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or cef_urlrequest_t. |request|
+  // cannot be modified in this callback. Return true (1) if the specified
+  // cookie can be sent with the request or false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* can_send_cookie)(struct _cef_cookie_access_filter_t* self,
+                                     struct _cef_browser_t* browser,
+                                     struct _cef_frame_t* frame,
+                                     struct _cef_request_t* request,
+                                     const struct _cef_cookie_t* cookie);
+
+  ///
+  // Called on the IO thread after a resource response is received. The
+  // |browser| and |frame| values represent the source of the request, and may
+  // be NULL for requests originating from service workers or cef_urlrequest_t.
+  // |request| cannot be modified in this callback. Return true (1) if the
+  // specified cookie returned with the response can be saved or false (0)
+  // otherwise.
+  ///
+  int(CEF_CALLBACK* can_save_cookie)(struct _cef_cookie_access_filter_t* self,
+                                     struct _cef_browser_t* browser,
+                                     struct _cef_frame_t* frame,
+                                     struct _cef_request_t* request,
+                                     struct _cef_response_t* response,
+                                     const struct _cef_cookie_t* cookie);
+} cef_cookie_access_filter_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RESOURCE_REQUEST_HANDLER_CAPI_H_
diff --git a/src/include/capi/cef_response_capi.h b/src/include/capi/cef_response_capi.h
new file mode 100644
index 0000000..d7b0da4
--- /dev/null
+++ b/src/include/capi/cef_response_capi.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=2bccae35945ecea55c4c79bba840b44a691f1aa3$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RESPONSE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RESPONSE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure used to represent a web response. The functions of this structure
+// may be called on any thread.
+///
+typedef struct _cef_response_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is read-only.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_response_t* self);
+
+  ///
+  // Get the response error code. Returns ERR_NONE if there was no error.
+  ///
+  cef_errorcode_t(CEF_CALLBACK* get_error)(struct _cef_response_t* self);
+
+  ///
+  // Set the response error code. This can be used by custom scheme handlers to
+  // return errors during initial request processing.
+  ///
+  void(CEF_CALLBACK* set_error)(struct _cef_response_t* self,
+                                cef_errorcode_t error);
+
+  ///
+  // Get the response status code.
+  ///
+  int(CEF_CALLBACK* get_status)(struct _cef_response_t* self);
+
+  ///
+  // Set the response status code.
+  ///
+  void(CEF_CALLBACK* set_status)(struct _cef_response_t* self, int status);
+
+  ///
+  // Get the response status text.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_status_text)(
+      struct _cef_response_t* self);
+
+  ///
+  // Set the response status text.
+  ///
+  void(CEF_CALLBACK* set_status_text)(struct _cef_response_t* self,
+                                      const cef_string_t* statusText);
+
+  ///
+  // Get the response mime type.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_mime_type)(
+      struct _cef_response_t* self);
+
+  ///
+  // Set the response mime type.
+  ///
+  void(CEF_CALLBACK* set_mime_type)(struct _cef_response_t* self,
+                                    const cef_string_t* mimeType);
+
+  ///
+  // Get the response charset.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_charset)(
+      struct _cef_response_t* self);
+
+  ///
+  // Set the response charset.
+  ///
+  void(CEF_CALLBACK* set_charset)(struct _cef_response_t* self,
+                                  const cef_string_t* charset);
+
+  ///
+  // Get the value for the specified response header field.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_header_by_name)(
+      struct _cef_response_t* self,
+      const cef_string_t* name);
+
+  ///
+  // Set the header |name| to |value|. If |overwrite| is true (1) any existing
+  // values will be replaced with the new value. If |overwrite| is false (0) any
+  // existing values will not be overwritten.
+  ///
+  void(CEF_CALLBACK* set_header_by_name)(struct _cef_response_t* self,
+                                         const cef_string_t* name,
+                                         const cef_string_t* value,
+                                         int overwrite);
+
+  ///
+  // Get all response header fields.
+  ///
+  void(CEF_CALLBACK* get_header_map)(struct _cef_response_t* self,
+                                     cef_string_multimap_t headerMap);
+
+  ///
+  // Set all response header fields.
+  ///
+  void(CEF_CALLBACK* set_header_map)(struct _cef_response_t* self,
+                                     cef_string_multimap_t headerMap);
+
+  ///
+  // Get the resolved URL after redirects or changed as a result of HSTS.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_url)(struct _cef_response_t* self);
+
+  ///
+  // Set the resolved URL after redirects or changed as a result of HSTS.
+  ///
+  void(CEF_CALLBACK* set_url)(struct _cef_response_t* self,
+                              const cef_string_t* url);
+} cef_response_t;
+
+///
+// Create a new cef_response_t object.
+///
+CEF_EXPORT cef_response_t* cef_response_create();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RESPONSE_CAPI_H_
diff --git a/src/include/capi/cef_response_filter_capi.h b/src/include/capi/cef_response_filter_capi.h
new file mode 100644
index 0000000..fcb4f3a
--- /dev/null
+++ b/src/include/capi/cef_response_filter_capi.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=5b2602702a13a71ac012808eecb09bb8b9494551$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_RESPONSE_FILTER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_RESPONSE_FILTER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to filter resource response content. The functions
+// of this structure will be called on the browser process IO thread.
+///
+typedef struct _cef_response_filter_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Initialize the response filter. Will only be called a single time. The
+  // filter will not be installed if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* init_filter)(struct _cef_response_filter_t* self);
+
+  ///
+  // Called to filter a chunk of data. Expected usage is as follows:
+  //
+  //  A. Read input data from |data_in| and set |data_in_read| to the number of
+  //     bytes that were read up to a maximum of |data_in_size|. |data_in| will
+  //     be NULL if |data_in_size| is zero.
+  //  B. Write filtered output data to |data_out| and set |data_out_written| to
+  //     the number of bytes that were written up to a maximum of
+  //     |data_out_size|. If no output data was written then all data must be
+  //     read from |data_in| (user must set |data_in_read| = |data_in_size|).
+  //  C. Return RESPONSE_FILTER_DONE if all output data was written or
+  //     RESPONSE_FILTER_NEED_MORE_DATA if output data is still pending.
+  //
+  // This function will be called repeatedly until the input buffer has been
+  // fully read (user sets |data_in_read| = |data_in_size|) and there is no more
+  // input data to filter (the resource response is complete). This function may
+  // then be called an additional time with an NULL input buffer if the user
+  // filled the output buffer (set |data_out_written| = |data_out_size|) and
+  // returned RESPONSE_FILTER_NEED_MORE_DATA to indicate that output data is
+  // still pending.
+  //
+  // Calls to this function will stop when one of the following conditions is
+  // met:
+  //
+  //  A. There is no more input data to filter (the resource response is
+  //     complete) and the user sets |data_out_written| = 0 or returns
+  //     RESPONSE_FILTER_DONE to indicate that all data has been written, or;
+  //  B. The user returns RESPONSE_FILTER_ERROR to indicate an error.
+  //
+  // Do not keep a reference to the buffers passed to this function.
+  ///
+  cef_response_filter_status_t(CEF_CALLBACK* filter)(
+      struct _cef_response_filter_t* self,
+      void* data_in,
+      size_t data_in_size,
+      size_t* data_in_read,
+      void* data_out,
+      size_t data_out_size,
+      size_t* data_out_written);
+} cef_response_filter_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_RESPONSE_FILTER_CAPI_H_
diff --git a/src/include/capi/cef_scheme_capi.h b/src/include/capi/cef_scheme_capi.h
new file mode 100644
index 0000000..d288091
--- /dev/null
+++ b/src/include/capi/cef_scheme_capi.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=d93b4ad0b71ffe0a05326b39c3ed0bdb26a73fac$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_SCHEME_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_SCHEME_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/capi/cef_response_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_scheme_handler_factory_t;
+
+///
+// Structure that manages custom scheme registrations.
+///
+typedef struct _cef_scheme_registrar_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_scoped_t base;
+
+  ///
+  // Register a custom scheme. This function should not be called for the built-
+  // in HTTP, HTTPS, FILE, FTP, ABOUT and DATA schemes.
+  //
+  // See cef_scheme_options_t for possible values for |options|.
+  //
+  // This function may be called on any thread. It should only be called once
+  // per unique |scheme_name| value. If |scheme_name| is already registered or
+  // if an error occurs this function will return false (0).
+  ///
+  int(CEF_CALLBACK* add_custom_scheme)(struct _cef_scheme_registrar_t* self,
+                                       const cef_string_t* scheme_name,
+                                       int options);
+} cef_scheme_registrar_t;
+
+///
+// Structure that creates cef_resource_handler_t instances for handling scheme
+// requests. The functions of this structure will always be called on the IO
+// thread.
+///
+typedef struct _cef_scheme_handler_factory_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Return a new resource handler instance to handle the request or an NULL
+  // reference to allow default handling of the request. |browser| and |frame|
+  // will be the browser window and frame respectively that originated the
+  // request or NULL if the request did not originate from a browser window (for
+  // example, if the request came from cef_urlrequest_t). The |request| object
+  // passed to this function cannot be modified.
+  ///
+  struct _cef_resource_handler_t*(CEF_CALLBACK* create)(
+      struct _cef_scheme_handler_factory_t* self,
+      struct _cef_browser_t* browser,
+      struct _cef_frame_t* frame,
+      const cef_string_t* scheme_name,
+      struct _cef_request_t* request);
+} cef_scheme_handler_factory_t;
+
+///
+// Register a scheme handler factory with the global request context. An NULL
+// |domain_name| value for a standard scheme will cause the factory to match all
+// domain names. The |domain_name| value will be ignored for non-standard
+// schemes. If |scheme_name| is a built-in scheme and no handler is returned by
+// |factory| then the built-in scheme handler factory will be called. If
+// |scheme_name| is a custom scheme then you must also implement the
+// cef_app_t::on_register_custom_schemes() function in all processes. This
+// function may be called multiple times to change or remove the factory that
+// matches the specified |scheme_name| and optional |domain_name|. Returns false
+// (0) if an error occurs. This function may be called on any thread in the
+// browser process. Using this function is equivalent to calling cef_request_con
+// text_t::cef_request_context_get_global_context()->register_scheme_handler_fac
+// tory().
+///
+CEF_EXPORT int cef_register_scheme_handler_factory(
+    const cef_string_t* scheme_name,
+    const cef_string_t* domain_name,
+    cef_scheme_handler_factory_t* factory);
+
+///
+// Clear all scheme handler factories registered with the global request
+// context. Returns false (0) on error. This function may be called on any
+// thread in the browser process. Using this function is equivalent to calling c
+// ef_request_context_t::cef_request_context_get_global_context()->clear_scheme_
+// handler_factories().
+///
+CEF_EXPORT int cef_clear_scheme_handler_factories();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_SCHEME_CAPI_H_
diff --git a/src/include/capi/cef_server_capi.h b/src/include/capi/cef_server_capi.h
new file mode 100644
index 0000000..86571db
--- /dev/null
+++ b/src/include/capi/cef_server_capi.h
@@ -0,0 +1,327 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=ffd489adc301ed88e1f30f8f38cec1730411a4b5$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_SERVER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_SERVER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_callback_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_task_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_server_handler_t;
+
+///
+// Structure representing a server that supports HTTP and WebSocket requests.
+// Server capacity is limited and is intended to handle only a small number of
+// simultaneous connections (e.g. for communicating between applications on
+// localhost). The functions of this structure are safe to call from any thread
+// in the brower process unless otherwise indicated.
+///
+typedef struct _cef_server_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the task runner for the dedicated server thread.
+  ///
+  struct _cef_task_runner_t*(CEF_CALLBACK* get_task_runner)(
+      struct _cef_server_t* self);
+
+  ///
+  // Stop the server and shut down the dedicated server thread. See
+  // cef_server_handler_t::OnServerCreated documentation for a description of
+  // server lifespan.
+  ///
+  void(CEF_CALLBACK* shutdown)(struct _cef_server_t* self);
+
+  ///
+  // Returns true (1) if the server is currently running and accepting incoming
+  // connections. See cef_server_handler_t::OnServerCreated documentation for a
+  // description of server lifespan. This function must be called on the
+  // dedicated server thread.
+  ///
+  int(CEF_CALLBACK* is_running)(struct _cef_server_t* self);
+
+  ///
+  // Returns the server address including the port number.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_address)(struct _cef_server_t* self);
+
+  ///
+  // Returns true (1) if the server currently has a connection. This function
+  // must be called on the dedicated server thread.
+  ///
+  int(CEF_CALLBACK* has_connection)(struct _cef_server_t* self);
+
+  ///
+  // Returns true (1) if |connection_id| represents a valid connection. This
+  // function must be called on the dedicated server thread.
+  ///
+  int(CEF_CALLBACK* is_valid_connection)(struct _cef_server_t* self,
+                                         int connection_id);
+
+  ///
+  // Send an HTTP 200 "OK" response to the connection identified by
+  // |connection_id|. |content_type| is the response content type (e.g.
+  // "text/html"), |data| is the response content, and |data_size| is the size
+  // of |data| in bytes. The contents of |data| will be copied. The connection
+  // will be closed automatically after the response is sent.
+  ///
+  void(CEF_CALLBACK* send_http200response)(struct _cef_server_t* self,
+                                           int connection_id,
+                                           const cef_string_t* content_type,
+                                           const void* data,
+                                           size_t data_size);
+
+  ///
+  // Send an HTTP 404 "Not Found" response to the connection identified by
+  // |connection_id|. The connection will be closed automatically after the
+  // response is sent.
+  ///
+  void(CEF_CALLBACK* send_http404response)(struct _cef_server_t* self,
+                                           int connection_id);
+
+  ///
+  // Send an HTTP 500 "Internal Server Error" response to the connection
+  // identified by |connection_id|. |error_message| is the associated error
+  // message. The connection will be closed automatically after the response is
+  // sent.
+  ///
+  void(CEF_CALLBACK* send_http500response)(struct _cef_server_t* self,
+                                           int connection_id,
+                                           const cef_string_t* error_message);
+
+  ///
+  // Send a custom HTTP response to the connection identified by
+  // |connection_id|. |response_code| is the HTTP response code sent in the
+  // status line (e.g. 200), |content_type| is the response content type sent as
+  // the "Content-Type" header (e.g. "text/html"), |content_length| is the
+  // expected content length, and |extra_headers| is the map of extra response
+  // headers. If |content_length| is >= 0 then the "Content-Length" header will
+  // be sent. If |content_length| is 0 then no content is expected and the
+  // connection will be closed automatically after the response is sent. If
+  // |content_length| is < 0 then no "Content-Length" header will be sent and
+  // the client will continue reading until the connection is closed. Use the
+  // SendRawData function to send the content, if applicable, and call
+  // CloseConnection after all content has been sent.
+  ///
+  void(CEF_CALLBACK* send_http_response)(struct _cef_server_t* self,
+                                         int connection_id,
+                                         int response_code,
+                                         const cef_string_t* content_type,
+                                         int64 content_length,
+                                         cef_string_multimap_t extra_headers);
+
+  ///
+  // Send raw data directly to the connection identified by |connection_id|.
+  // |data| is the raw data and |data_size| is the size of |data| in bytes. The
+  // contents of |data| will be copied. No validation of |data| is performed
+  // internally so the client should be careful to send the amount indicated by
+  // the "Content-Length" header, if specified. See SendHttpResponse
+  // documentation for intended usage.
+  ///
+  void(CEF_CALLBACK* send_raw_data)(struct _cef_server_t* self,
+                                    int connection_id,
+                                    const void* data,
+                                    size_t data_size);
+
+  ///
+  // Close the connection identified by |connection_id|. See SendHttpResponse
+  // documentation for intended usage.
+  ///
+  void(CEF_CALLBACK* close_connection)(struct _cef_server_t* self,
+                                       int connection_id);
+
+  ///
+  // Send a WebSocket message to the connection identified by |connection_id|.
+  // |data| is the response content and |data_size| is the size of |data| in
+  // bytes. The contents of |data| will be copied. See
+  // cef_server_handler_t::OnWebSocketRequest documentation for intended usage.
+  ///
+  void(CEF_CALLBACK* send_web_socket_message)(struct _cef_server_t* self,
+                                              int connection_id,
+                                              const void* data,
+                                              size_t data_size);
+} cef_server_t;
+
+///
+// Create a new server that binds to |address| and |port|. |address| must be a
+// valid IPv4 or IPv6 address (e.g. 127.0.0.1 or ::1) and |port| must be a port
+// number outside of the reserved range (e.g. between 1025 and 65535 on most
+// platforms). |backlog| is the maximum number of pending connections. A new
+// thread will be created for each CreateServer call (the "dedicated server
+// thread"). It is therefore recommended to use a different cef_server_handler_t
+// instance for each CreateServer call to avoid thread safety issues in the
+// cef_server_handler_t implementation. The
+// cef_server_handler_t::OnServerCreated function will be called on the
+// dedicated server thread to report success or failure. See
+// cef_server_handler_t::OnServerCreated documentation for a description of
+// server lifespan.
+///
+CEF_EXPORT void cef_server_create(const cef_string_t* address,
+                                  uint16 port,
+                                  int backlog,
+                                  struct _cef_server_handler_t* handler);
+
+///
+// Implement this structure to handle HTTP server requests. A new thread will be
+// created for each cef_server_t::CreateServer call (the "dedicated server
+// thread"), and the functions of this structure will be called on that thread.
+// It is therefore recommended to use a different cef_server_handler_t instance
+// for each cef_server_t::CreateServer call to avoid thread safety issues in the
+// cef_server_handler_t implementation.
+///
+typedef struct _cef_server_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called when |server| is created. If the server was started successfully
+  // then cef_server_t::IsRunning will return true (1). The server will continue
+  // running until cef_server_t::Shutdown is called, after which time
+  // OnServerDestroyed will be called. If the server failed to start then
+  // OnServerDestroyed will be called immediately after this function returns.
+  ///
+  void(CEF_CALLBACK* on_server_created)(struct _cef_server_handler_t* self,
+                                        struct _cef_server_t* server);
+
+  ///
+  // Called when |server| is destroyed. The server thread will be stopped after
+  // this function returns. The client should release any references to |server|
+  // when this function is called. See OnServerCreated documentation for a
+  // description of server lifespan.
+  ///
+  void(CEF_CALLBACK* on_server_destroyed)(struct _cef_server_handler_t* self,
+                                          struct _cef_server_t* server);
+
+  ///
+  // Called when a client connects to |server|. |connection_id| uniquely
+  // identifies the connection. Each call to this function will have a matching
+  // call to OnClientDisconnected.
+  ///
+  void(CEF_CALLBACK* on_client_connected)(struct _cef_server_handler_t* self,
+                                          struct _cef_server_t* server,
+                                          int connection_id);
+
+  ///
+  // Called when a client disconnects from |server|. |connection_id| uniquely
+  // identifies the connection. The client should release any data associated
+  // with |connection_id| when this function is called and |connection_id|
+  // should no longer be passed to cef_server_t functions. Disconnects can
+  // originate from either the client or the server. For example, the server
+  // will disconnect automatically after a cef_server_t::SendHttpXXXResponse
+  // function is called.
+  ///
+  void(CEF_CALLBACK* on_client_disconnected)(struct _cef_server_handler_t* self,
+                                             struct _cef_server_t* server,
+                                             int connection_id);
+
+  ///
+  // Called when |server| receives an HTTP request. |connection_id| uniquely
+  // identifies the connection, |client_address| is the requesting IPv4 or IPv6
+  // client address including port number, and |request| contains the request
+  // contents (URL, function, headers and optional POST data). Call cef_server_t
+  // functions either synchronously or asynchronusly to send a response.
+  ///
+  void(CEF_CALLBACK* on_http_request)(struct _cef_server_handler_t* self,
+                                      struct _cef_server_t* server,
+                                      int connection_id,
+                                      const cef_string_t* client_address,
+                                      struct _cef_request_t* request);
+
+  ///
+  // Called when |server| receives a WebSocket request. |connection_id| uniquely
+  // identifies the connection, |client_address| is the requesting IPv4 or IPv6
+  // client address including port number, and |request| contains the request
+  // contents (URL, function, headers and optional POST data). Execute
+  // |callback| either synchronously or asynchronously to accept or decline the
+  // WebSocket connection. If the request is accepted then OnWebSocketConnected
+  // will be called after the WebSocket has connected and incoming messages will
+  // be delivered to the OnWebSocketMessage callback. If the request is declined
+  // then the client will be disconnected and OnClientDisconnected will be
+  // called. Call the cef_server_t::SendWebSocketMessage function after
+  // receiving the OnWebSocketConnected callback to respond with WebSocket
+  // messages.
+  ///
+  void(CEF_CALLBACK* on_web_socket_request)(struct _cef_server_handler_t* self,
+                                            struct _cef_server_t* server,
+                                            int connection_id,
+                                            const cef_string_t* client_address,
+                                            struct _cef_request_t* request,
+                                            struct _cef_callback_t* callback);
+
+  ///
+  // Called after the client has accepted the WebSocket connection for |server|
+  // and |connection_id| via the OnWebSocketRequest callback. See
+  // OnWebSocketRequest documentation for intended usage.
+  ///
+  void(CEF_CALLBACK* on_web_socket_connected)(
+      struct _cef_server_handler_t* self,
+      struct _cef_server_t* server,
+      int connection_id);
+
+  ///
+  // Called when |server| receives an WebSocket message. |connection_id|
+  // uniquely identifies the connection, |data| is the message content and
+  // |data_size| is the size of |data| in bytes. Do not keep a reference to
+  // |data| outside of this function. See OnWebSocketRequest documentation for
+  // intended usage.
+  ///
+  void(CEF_CALLBACK* on_web_socket_message)(struct _cef_server_handler_t* self,
+                                            struct _cef_server_t* server,
+                                            int connection_id,
+                                            const void* data,
+                                            size_t data_size);
+} cef_server_handler_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_SERVER_CAPI_H_
diff --git a/src/include/capi/cef_ssl_info_capi.h b/src/include/capi/cef_ssl_info_capi.h
new file mode 100644
index 0000000..da83e0b
--- /dev/null
+++ b/src/include/capi/cef_ssl_info_capi.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=badaadcff4641fea876fb626b8ffe5a6f34a376c$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_SSL_INFO_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_SSL_INFO_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_values_capi.h"
+#include "include/capi/cef_x509_certificate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure representing SSL information.
+///
+typedef struct _cef_sslinfo_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns a bitmask containing any and all problems verifying the server
+  // certificate.
+  ///
+  cef_cert_status_t(CEF_CALLBACK* get_cert_status)(struct _cef_sslinfo_t* self);
+
+  ///
+  // Returns the X.509 certificate.
+  ///
+  struct _cef_x509certificate_t*(CEF_CALLBACK* get_x509certificate)(
+      struct _cef_sslinfo_t* self);
+} cef_sslinfo_t;
+
+///
+// Returns true (1) if the certificate status represents an error.
+///
+CEF_EXPORT int cef_is_cert_status_error(cef_cert_status_t status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_SSL_INFO_CAPI_H_
diff --git a/src/include/capi/cef_ssl_status_capi.h b/src/include/capi/cef_ssl_status_capi.h
new file mode 100644
index 0000000..df49ac9
--- /dev/null
+++ b/src/include/capi/cef_ssl_status_capi.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3755121a7b89de52a67885ac1c6d12de23f4b657$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_SSL_STATUS_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_SSL_STATUS_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_values_capi.h"
+#include "include/capi/cef_x509_certificate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure representing the SSL information for a navigation entry.
+///
+typedef struct _cef_sslstatus_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if the status is related to a secure SSL/TLS connection.
+  ///
+  int(CEF_CALLBACK* is_secure_connection)(struct _cef_sslstatus_t* self);
+
+  ///
+  // Returns a bitmask containing any and all problems verifying the server
+  // certificate.
+  ///
+  cef_cert_status_t(CEF_CALLBACK* get_cert_status)(
+      struct _cef_sslstatus_t* self);
+
+  ///
+  // Returns the SSL version used for the SSL connection.
+  ///
+  cef_ssl_version_t(CEF_CALLBACK* get_sslversion)(
+      struct _cef_sslstatus_t* self);
+
+  ///
+  // Returns a bitmask containing the page security content status.
+  ///
+  cef_ssl_content_status_t(CEF_CALLBACK* get_content_status)(
+      struct _cef_sslstatus_t* self);
+
+  ///
+  // Returns the X.509 certificate.
+  ///
+  struct _cef_x509certificate_t*(CEF_CALLBACK* get_x509certificate)(
+      struct _cef_sslstatus_t* self);
+} cef_sslstatus_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_SSL_STATUS_CAPI_H_
diff --git a/src/include/capi/cef_stream_capi.h b/src/include/capi/cef_stream_capi.h
new file mode 100644
index 0000000..d1f779d
--- /dev/null
+++ b/src/include/capi/cef_stream_capi.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=bd5bbcdc385f83512bf64304e180f1a05b765c16$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_STREAM_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_STREAM_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure the client can implement to provide a custom stream reader. The
+// functions of this structure may be called on any thread.
+///
+typedef struct _cef_read_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Read raw binary data.
+  ///
+  size_t(CEF_CALLBACK* read)(struct _cef_read_handler_t* self,
+                             void* ptr,
+                             size_t size,
+                             size_t n);
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of SEEK_CUR,
+  // SEEK_END or SEEK_SET. Return zero on success and non-zero on failure.
+  ///
+  int(CEF_CALLBACK* seek)(struct _cef_read_handler_t* self,
+                          int64 offset,
+                          int whence);
+
+  ///
+  // Return the current offset position.
+  ///
+  int64(CEF_CALLBACK* tell)(struct _cef_read_handler_t* self);
+
+  ///
+  // Return non-zero if at end of file.
+  ///
+  int(CEF_CALLBACK* eof)(struct _cef_read_handler_t* self);
+
+  ///
+  // Return true (1) if this handler performs work like accessing the file
+  // system which may block. Used as a hint for determining the thread to access
+  // the handler from.
+  ///
+  int(CEF_CALLBACK* may_block)(struct _cef_read_handler_t* self);
+} cef_read_handler_t;
+
+///
+// Structure used to read data from a stream. The functions of this structure
+// may be called on any thread.
+///
+typedef struct _cef_stream_reader_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Read raw binary data.
+  ///
+  size_t(CEF_CALLBACK* read)(struct _cef_stream_reader_t* self,
+                             void* ptr,
+                             size_t size,
+                             size_t n);
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of SEEK_CUR,
+  // SEEK_END or SEEK_SET. Returns zero on success and non-zero on failure.
+  ///
+  int(CEF_CALLBACK* seek)(struct _cef_stream_reader_t* self,
+                          int64 offset,
+                          int whence);
+
+  ///
+  // Return the current offset position.
+  ///
+  int64(CEF_CALLBACK* tell)(struct _cef_stream_reader_t* self);
+
+  ///
+  // Return non-zero if at end of file.
+  ///
+  int(CEF_CALLBACK* eof)(struct _cef_stream_reader_t* self);
+
+  ///
+  // Returns true (1) if this reader performs work like accessing the file
+  // system which may block. Used as a hint for determining the thread to access
+  // the reader from.
+  ///
+  int(CEF_CALLBACK* may_block)(struct _cef_stream_reader_t* self);
+} cef_stream_reader_t;
+
+///
+// Create a new cef_stream_reader_t object from a file.
+///
+CEF_EXPORT cef_stream_reader_t* cef_stream_reader_create_for_file(
+    const cef_string_t* fileName);
+
+///
+// Create a new cef_stream_reader_t object from data.
+///
+CEF_EXPORT cef_stream_reader_t* cef_stream_reader_create_for_data(void* data,
+                                                                  size_t size);
+
+///
+// Create a new cef_stream_reader_t object from a custom handler.
+///
+CEF_EXPORT cef_stream_reader_t* cef_stream_reader_create_for_handler(
+    cef_read_handler_t* handler);
+
+///
+// Structure the client can implement to provide a custom stream writer. The
+// functions of this structure may be called on any thread.
+///
+typedef struct _cef_write_handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Write raw binary data.
+  ///
+  size_t(CEF_CALLBACK* write)(struct _cef_write_handler_t* self,
+                              const void* ptr,
+                              size_t size,
+                              size_t n);
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of SEEK_CUR,
+  // SEEK_END or SEEK_SET. Return zero on success and non-zero on failure.
+  ///
+  int(CEF_CALLBACK* seek)(struct _cef_write_handler_t* self,
+                          int64 offset,
+                          int whence);
+
+  ///
+  // Return the current offset position.
+  ///
+  int64(CEF_CALLBACK* tell)(struct _cef_write_handler_t* self);
+
+  ///
+  // Flush the stream.
+  ///
+  int(CEF_CALLBACK* flush)(struct _cef_write_handler_t* self);
+
+  ///
+  // Return true (1) if this handler performs work like accessing the file
+  // system which may block. Used as a hint for determining the thread to access
+  // the handler from.
+  ///
+  int(CEF_CALLBACK* may_block)(struct _cef_write_handler_t* self);
+} cef_write_handler_t;
+
+///
+// Structure used to write data to a stream. The functions of this structure may
+// be called on any thread.
+///
+typedef struct _cef_stream_writer_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Write raw binary data.
+  ///
+  size_t(CEF_CALLBACK* write)(struct _cef_stream_writer_t* self,
+                              const void* ptr,
+                              size_t size,
+                              size_t n);
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of SEEK_CUR,
+  // SEEK_END or SEEK_SET. Returns zero on success and non-zero on failure.
+  ///
+  int(CEF_CALLBACK* seek)(struct _cef_stream_writer_t* self,
+                          int64 offset,
+                          int whence);
+
+  ///
+  // Return the current offset position.
+  ///
+  int64(CEF_CALLBACK* tell)(struct _cef_stream_writer_t* self);
+
+  ///
+  // Flush the stream.
+  ///
+  int(CEF_CALLBACK* flush)(struct _cef_stream_writer_t* self);
+
+  ///
+  // Returns true (1) if this writer performs work like accessing the file
+  // system which may block. Used as a hint for determining the thread to access
+  // the writer from.
+  ///
+  int(CEF_CALLBACK* may_block)(struct _cef_stream_writer_t* self);
+} cef_stream_writer_t;
+
+///
+// Create a new cef_stream_writer_t object for a file.
+///
+CEF_EXPORT cef_stream_writer_t* cef_stream_writer_create_for_file(
+    const cef_string_t* fileName);
+
+///
+// Create a new cef_stream_writer_t object for a custom handler.
+///
+CEF_EXPORT cef_stream_writer_t* cef_stream_writer_create_for_handler(
+    cef_write_handler_t* handler);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_STREAM_CAPI_H_
diff --git a/src/include/capi/cef_string_visitor_capi.h b/src/include/capi/cef_string_visitor_capi.h
new file mode 100644
index 0000000..0a7ba35
--- /dev/null
+++ b/src/include/capi/cef_string_visitor_capi.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=bba3a9719860f9a81c63cbb052a4c501416b2ada$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_STRING_VISITOR_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_STRING_VISITOR_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to receive string values asynchronously.
+///
+typedef struct _cef_string_visitor_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be executed.
+  ///
+  void(CEF_CALLBACK* visit)(struct _cef_string_visitor_t* self,
+                            const cef_string_t* string);
+} cef_string_visitor_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_STRING_VISITOR_CAPI_H_
diff --git a/src/include/capi/cef_task_capi.h b/src/include/capi/cef_task_capi.h
new file mode 100644
index 0000000..c52a549
--- /dev/null
+++ b/src/include/capi/cef_task_capi.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=025daa5db3bf16029953da7703e3e5968bd97fe2$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_TASK_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_TASK_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure for asynchronous task execution. If the task is
+// posted successfully and if the associated message loop is still running then
+// the execute() function will be called on the target thread. If the task fails
+// to post then the task object may be destroyed on the source thread instead of
+// the target thread. For this reason be cautious when performing work in the
+// task object destructor.
+///
+typedef struct _cef_task_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be executed on the target thread.
+  ///
+  void(CEF_CALLBACK* execute)(struct _cef_task_t* self);
+} cef_task_t;
+
+///
+// Structure that asynchronously executes tasks on the associated thread. It is
+// safe to call the functions of this structure on any thread.
+//
+// CEF maintains multiple internal threads that are used for handling different
+// types of tasks in different processes. The cef_thread_id_t definitions in
+// cef_types.h list the common CEF threads. Task runners are also available for
+// other CEF threads as appropriate (for example, V8 WebWorker threads).
+///
+typedef struct _cef_task_runner_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is pointing to the same task runner as
+  // |that| object.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_task_runner_t* self,
+                             struct _cef_task_runner_t* that);
+
+  ///
+  // Returns true (1) if this task runner belongs to the current thread.
+  ///
+  int(CEF_CALLBACK* belongs_to_current_thread)(struct _cef_task_runner_t* self);
+
+  ///
+  // Returns true (1) if this task runner is for the specified CEF thread.
+  ///
+  int(CEF_CALLBACK* belongs_to_thread)(struct _cef_task_runner_t* self,
+                                       cef_thread_id_t threadId);
+
+  ///
+  // Post a task for execution on the thread associated with this task runner.
+  // Execution will occur asynchronously.
+  ///
+  int(CEF_CALLBACK* post_task)(struct _cef_task_runner_t* self,
+                               struct _cef_task_t* task);
+
+  ///
+  // Post a task for delayed execution on the thread associated with this task
+  // runner. Execution will occur asynchronously. Delayed tasks are not
+  // supported on V8 WebWorker threads and will be executed without the
+  // specified delay.
+  ///
+  int(CEF_CALLBACK* post_delayed_task)(struct _cef_task_runner_t* self,
+                                       struct _cef_task_t* task,
+                                       int64 delay_ms);
+} cef_task_runner_t;
+
+///
+// Returns the task runner for the current thread. Only CEF threads will have
+// task runners. An NULL reference will be returned if this function is called
+// on an invalid thread.
+///
+CEF_EXPORT cef_task_runner_t* cef_task_runner_get_for_current_thread();
+
+///
+// Returns the task runner for the specified CEF thread.
+///
+CEF_EXPORT cef_task_runner_t* cef_task_runner_get_for_thread(
+    cef_thread_id_t threadId);
+
+///
+// Returns true (1) if called on the specified thread. Equivalent to using
+// cef_task_runner_t::GetForThread(threadId)->belongs_to_current_thread().
+///
+CEF_EXPORT int cef_currently_on(cef_thread_id_t threadId);
+
+///
+// Post a task for execution on the specified thread. Equivalent to using
+// cef_task_runner_t::GetForThread(threadId)->PostTask(task).
+///
+CEF_EXPORT int cef_post_task(cef_thread_id_t threadId, cef_task_t* task);
+
+///
+// Post a task for delayed execution on the specified thread. Equivalent to
+// using cef_task_runner_t::GetForThread(threadId)->PostDelayedTask(task,
+// delay_ms).
+///
+CEF_EXPORT int cef_post_delayed_task(cef_thread_id_t threadId,
+                                     cef_task_t* task,
+                                     int64 delay_ms);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_TASK_CAPI_H_
diff --git a/src/include/capi/cef_thread_capi.h b/src/include/capi/cef_thread_capi.h
new file mode 100644
index 0000000..1a9e9a3
--- /dev/null
+++ b/src/include/capi/cef_thread_capi.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=370cdeaa3252a9ed0e1a627d858dcab23af24ee1$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_THREAD_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_THREAD_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_task_capi.h"
+#include "include/internal/cef_thread_internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// A simple thread abstraction that establishes a message loop on a new thread.
+// The consumer uses cef_task_runner_t to execute code on the thread's message
+// loop. The thread is terminated when the cef_thread_t object is destroyed or
+// stop() is called. All pending tasks queued on the thread's message loop will
+// run to completion before the thread is terminated. cef_thread_create() can be
+// called on any valid CEF thread in either the browser or render process. This
+// structure should only be used for tasks that require a dedicated thread. In
+// most cases you can post tasks to an existing CEF thread instead of creating a
+// new one; see cef_task.h for details.
+///
+typedef struct _cef_thread_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the cef_task_runner_t that will execute code on this thread's
+  // message loop. This function is safe to call from any thread.
+  ///
+  struct _cef_task_runner_t*(CEF_CALLBACK* get_task_runner)(
+      struct _cef_thread_t* self);
+
+  ///
+  // Returns the platform thread ID. It will return the same value after stop()
+  // is called. This function is safe to call from any thread.
+  ///
+  cef_platform_thread_id_t(CEF_CALLBACK* get_platform_thread_id)(
+      struct _cef_thread_t* self);
+
+  ///
+  // Stop and join the thread. This function must be called from the same thread
+  // that called cef_thread_create(). Do not call this function if
+  // cef_thread_create() was called with a |stoppable| value of false (0).
+  ///
+  void(CEF_CALLBACK* stop)(struct _cef_thread_t* self);
+
+  ///
+  // Returns true (1) if the thread is currently running. This function must be
+  // called from the same thread that called cef_thread_create().
+  ///
+  int(CEF_CALLBACK* is_running)(struct _cef_thread_t* self);
+} cef_thread_t;
+
+///
+// Create and start a new thread. This function does not block waiting for the
+// thread to run initialization. |display_name| is the name that will be used to
+// identify the thread. |priority| is the thread execution priority.
+// |message_loop_type| indicates the set of asynchronous events that the thread
+// can process. If |stoppable| is true (1) the thread will stopped and joined on
+// destruction or when stop() is called; otherwise, the the thread cannot be
+// stopped and will be leaked on shutdown. On Windows the |com_init_mode| value
+// specifies how COM will be initialized for the thread. If |com_init_mode| is
+// set to COM_INIT_MODE_STA then |message_loop_type| must be set to ML_TYPE_UI.
+///
+CEF_EXPORT cef_thread_t* cef_thread_create(
+    const cef_string_t* display_name,
+    cef_thread_priority_t priority,
+    cef_message_loop_type_t message_loop_type,
+    int stoppable,
+    cef_com_init_mode_t com_init_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_THREAD_CAPI_H_
diff --git a/src/include/capi/cef_trace_capi.h b/src/include/capi/cef_trace_capi.h
new file mode 100644
index 0000000..336e08b
--- /dev/null
+++ b/src/include/capi/cef_trace_capi.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=b1b96b7cb636afbd201b88bc1544afc58099c0b6$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_TRACE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_TRACE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_callback_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to receive notification when tracing has completed.
+// The functions of this structure will be called on the browser process UI
+// thread.
+///
+typedef struct _cef_end_tracing_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called after all processes have sent their trace data. |tracing_file| is
+  // the path at which tracing data was written. The client is responsible for
+  // deleting |tracing_file|.
+  ///
+  void(CEF_CALLBACK* on_end_tracing_complete)(
+      struct _cef_end_tracing_callback_t* self,
+      const cef_string_t* tracing_file);
+} cef_end_tracing_callback_t;
+
+///
+// Start tracing events on all processes. Tracing is initialized asynchronously
+// and |callback| will be executed on the UI thread after initialization is
+// complete.
+//
+// If CefBeginTracing was called previously, or if a CefEndTracingAsync call is
+// pending, CefBeginTracing will fail and return false (0).
+//
+// |categories| is a comma-delimited list of category wildcards. A category can
+// have an optional '-' prefix to make it an excluded category. Having both
+// included and excluded categories in the same list is not supported.
+//
+// Example: "test_MyTest*" Example: "test_MyTest*,test_OtherStuff" Example:
+// "-excluded_category1,-excluded_category2"
+//
+// This function must be called on the browser process UI thread.
+///
+CEF_EXPORT int cef_begin_tracing(const cef_string_t* categories,
+                                 struct _cef_completion_callback_t* callback);
+
+///
+// Stop tracing events on all processes.
+//
+// This function will fail and return false (0) if a previous call to
+// CefEndTracingAsync is already pending or if CefBeginTracing was not called.
+//
+// |tracing_file| is the path at which tracing data will be written and
+// |callback| is the callback that will be executed once all processes have sent
+// their trace data. If |tracing_file| is NULL a new temporary file path will be
+// used. If |callback| is NULL no trace data will be written.
+//
+// This function must be called on the browser process UI thread.
+///
+CEF_EXPORT int cef_end_tracing(const cef_string_t* tracing_file,
+                               cef_end_tracing_callback_t* callback);
+
+///
+// Returns the current system trace time or, if none is defined, the current
+// high-res time. Can be used by clients to synchronize with the time
+// information in trace events.
+///
+CEF_EXPORT int64 cef_now_from_system_trace_time();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_TRACE_CAPI_H_
diff --git a/src/include/capi/cef_urlrequest_capi.h b/src/include/capi/cef_urlrequest_capi.h
new file mode 100644
index 0000000..81eeaa6
--- /dev/null
+++ b/src/include/capi/cef_urlrequest_capi.h
@@ -0,0 +1,216 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=63d875f5a922dd2c2e1efaaf0ddaa20475f79ef8$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_URLREQUEST_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_URLREQUEST_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_auth_callback_capi.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_request_context_capi.h"
+#include "include/capi/cef_response_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_urlrequest_client_t;
+
+///
+// Structure used to make a URL request. URL requests are not associated with a
+// browser instance so no cef_client_t callbacks will be executed. URL requests
+// can be created on any valid CEF thread in either the browser or render
+// process. Once created the functions of the URL request object must be
+// accessed on the same thread that created it.
+///
+typedef struct _cef_urlrequest_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the request object used to create this URL request. The returned
+  // object is read-only and should not be modified.
+  ///
+  struct _cef_request_t*(CEF_CALLBACK* get_request)(
+      struct _cef_urlrequest_t* self);
+
+  ///
+  // Returns the client.
+  ///
+  struct _cef_urlrequest_client_t*(CEF_CALLBACK* get_client)(
+      struct _cef_urlrequest_t* self);
+
+  ///
+  // Returns the request status.
+  ///
+  cef_urlrequest_status_t(CEF_CALLBACK* get_request_status)(
+      struct _cef_urlrequest_t* self);
+
+  ///
+  // Returns the request error if status is UR_CANCELED or UR_FAILED, or 0
+  // otherwise.
+  ///
+  cef_errorcode_t(CEF_CALLBACK* get_request_error)(
+      struct _cef_urlrequest_t* self);
+
+  ///
+  // Returns the response, or NULL if no response information is available.
+  // Response information will only be available after the upload has completed.
+  // The returned object is read-only and should not be modified.
+  ///
+  struct _cef_response_t*(CEF_CALLBACK* get_response)(
+      struct _cef_urlrequest_t* self);
+
+  ///
+  // Returns true (1) if the response body was served from the cache. This
+  // includes responses for which revalidation was required.
+  ///
+  int(CEF_CALLBACK* response_was_cached)(struct _cef_urlrequest_t* self);
+
+  ///
+  // Cancel the request.
+  ///
+  void(CEF_CALLBACK* cancel)(struct _cef_urlrequest_t* self);
+} cef_urlrequest_t;
+
+///
+// Create a new URL request that is not associated with a specific browser or
+// frame. Use cef_frame_t::CreateURLRequest instead if you want the request to
+// have this association, in which case it may be handled differently (see
+// documentation on that function). Requests may originate from the both browser
+// process and the render process.
+//
+// For requests originating from the browser process:
+//   - It may be intercepted by the client via CefResourceRequestHandler or
+//     CefSchemeHandlerFactory.
+//   - POST data may only contain only a single element of type PDE_TYPE_FILE
+//     or PDE_TYPE_BYTES.
+//   - If |request_context| is empty the global request context will be used.
+// For requests originating from the render process:
+//   - It cannot be intercepted by the client so only http(s) and blob schemes
+//     are supported.
+//   - POST data may only contain a single element of type PDE_TYPE_BYTES.
+//   - The |request_context| parameter must be NULL.
+//
+// The |request| object will be marked as read-only after calling this function.
+///
+CEF_EXPORT cef_urlrequest_t* cef_urlrequest_create(
+    struct _cef_request_t* request,
+    struct _cef_urlrequest_client_t* client,
+    struct _cef_request_context_t* request_context);
+
+///
+// Structure that should be implemented by the cef_urlrequest_t client. The
+// functions of this structure will be called on the same thread that created
+// the request unless otherwise documented.
+///
+typedef struct _cef_urlrequest_client_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Notifies the client that the request has completed. Use the
+  // cef_urlrequest_t::GetRequestStatus function to determine if the request was
+  // successful or not.
+  ///
+  void(CEF_CALLBACK* on_request_complete)(struct _cef_urlrequest_client_t* self,
+                                          struct _cef_urlrequest_t* request);
+
+  ///
+  // Notifies the client of upload progress. |current| denotes the number of
+  // bytes sent so far and |total| is the total size of uploading data (or -1 if
+  // chunked upload is enabled). This function will only be called if the
+  // UR_FLAG_REPORT_UPLOAD_PROGRESS flag is set on the request.
+  ///
+  void(CEF_CALLBACK* on_upload_progress)(struct _cef_urlrequest_client_t* self,
+                                         struct _cef_urlrequest_t* request,
+                                         int64 current,
+                                         int64 total);
+
+  ///
+  // Notifies the client of download progress. |current| denotes the number of
+  // bytes received up to the call and |total| is the expected total size of the
+  // response (or -1 if not determined).
+  ///
+  void(CEF_CALLBACK* on_download_progress)(
+      struct _cef_urlrequest_client_t* self,
+      struct _cef_urlrequest_t* request,
+      int64 current,
+      int64 total);
+
+  ///
+  // Called when some part of the response is read. |data| contains the current
+  // bytes received since the last call. This function will not be called if the
+  // UR_FLAG_NO_DOWNLOAD_DATA flag is set on the request.
+  ///
+  void(CEF_CALLBACK* on_download_data)(struct _cef_urlrequest_client_t* self,
+                                       struct _cef_urlrequest_t* request,
+                                       const void* data,
+                                       size_t data_length);
+
+  ///
+  // Called on the IO thread when the browser needs credentials from the user.
+  // |isProxy| indicates whether the host is a proxy server. |host| contains the
+  // hostname and |port| contains the port number. Return true (1) to continue
+  // the request and call cef_auth_callback_t::cont() when the authentication
+  // information is available. If the request has an associated browser/frame
+  // then returning false (0) will result in a call to GetAuthCredentials on the
+  // cef_request_handler_t associated with that browser, if any. Otherwise,
+  // returning false (0) will cancel the request immediately. This function will
+  // only be called for requests initiated from the browser process.
+  ///
+  int(CEF_CALLBACK* get_auth_credentials)(
+      struct _cef_urlrequest_client_t* self,
+      int isProxy,
+      const cef_string_t* host,
+      int port,
+      const cef_string_t* realm,
+      const cef_string_t* scheme,
+      struct _cef_auth_callback_t* callback);
+} cef_urlrequest_client_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_URLREQUEST_CAPI_H_
diff --git a/src/include/capi/cef_v8_capi.h b/src/include/capi/cef_v8_capi.h
new file mode 100644
index 0000000..c54f309
--- /dev/null
+++ b/src/include/capi/cef_v8_capi.h
@@ -0,0 +1,988 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3bb8f9801a153172981120926c7a5629e08d7131$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_V8_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_V8_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_task_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_v8exception_t;
+struct _cef_v8handler_t;
+struct _cef_v8stack_frame_t;
+struct _cef_v8value_t;
+
+///
+// Structure representing a V8 context handle. V8 handles can only be accessed
+// from the thread on which they are created. Valid threads for creating a V8
+// handle include the render process main thread (TID_RENDERER) and WebWorker
+// threads. A task runner for posting tasks on the associated thread can be
+// retrieved via the cef_v8context_t::get_task_runner() function.
+///
+typedef struct _cef_v8context_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the task runner associated with this context. V8 handles can only
+  // be accessed from the thread on which they are created. This function can be
+  // called on any render process thread.
+  ///
+  struct _cef_task_runner_t*(CEF_CALLBACK* get_task_runner)(
+      struct _cef_v8context_t* self);
+
+  ///
+  // Returns true (1) if the underlying handle is valid and it can be accessed
+  // on the current thread. Do not call any other functions if this function
+  // returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_v8context_t* self);
+
+  ///
+  // Returns the browser for this context. This function will return an NULL
+  // reference for WebWorker contexts.
+  ///
+  struct _cef_browser_t*(CEF_CALLBACK* get_browser)(
+      struct _cef_v8context_t* self);
+
+  ///
+  // Returns the frame for this context. This function will return an NULL
+  // reference for WebWorker contexts.
+  ///
+  struct _cef_frame_t*(CEF_CALLBACK* get_frame)(struct _cef_v8context_t* self);
+
+  ///
+  // Returns the global object for this context. The context must be entered
+  // before calling this function.
+  ///
+  struct _cef_v8value_t*(CEF_CALLBACK* get_global)(
+      struct _cef_v8context_t* self);
+
+  ///
+  // Enter this context. A context must be explicitly entered before creating a
+  // V8 Object, Array, Function or Date asynchronously. exit() must be called
+  // the same number of times as enter() before releasing this context. V8
+  // objects belong to the context in which they are created. Returns true (1)
+  // if the scope was entered successfully.
+  ///
+  int(CEF_CALLBACK* enter)(struct _cef_v8context_t* self);
+
+  ///
+  // Exit this context. Call this function only after calling enter(). Returns
+  // true (1) if the scope was exited successfully.
+  ///
+  int(CEF_CALLBACK* exit)(struct _cef_v8context_t* self);
+
+  ///
+  // Returns true (1) if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_v8context_t* self,
+                             struct _cef_v8context_t* that);
+
+  ///
+  // Execute a string of JavaScript code in this V8 context. The |script_url|
+  // parameter is the URL where the script in question can be found, if any. The
+  // |start_line| parameter is the base line number to use for error reporting.
+  // On success |retval| will be set to the return value, if any, and the
+  // function will return true (1). On failure |exception| will be set to the
+  // exception, if any, and the function will return false (0).
+  ///
+  int(CEF_CALLBACK* eval)(struct _cef_v8context_t* self,
+                          const cef_string_t* code,
+                          const cef_string_t* script_url,
+                          int start_line,
+                          struct _cef_v8value_t** retval,
+                          struct _cef_v8exception_t** exception);
+} cef_v8context_t;
+
+///
+// Returns the current (top) context object in the V8 context stack.
+///
+CEF_EXPORT cef_v8context_t* cef_v8context_get_current_context();
+
+///
+// Returns the entered (bottom) context object in the V8 context stack.
+///
+CEF_EXPORT cef_v8context_t* cef_v8context_get_entered_context();
+
+///
+// Returns true (1) if V8 is currently inside a context.
+///
+CEF_EXPORT int cef_v8context_in_context();
+
+///
+// Structure that should be implemented to handle V8 function calls. The
+// functions of this structure will be called on the thread associated with the
+// V8 function.
+///
+typedef struct _cef_v8handler_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Handle execution of the function identified by |name|. |object| is the
+  // receiver ('this' object) of the function. |arguments| is the list of
+  // arguments passed to the function. If execution succeeds set |retval| to the
+  // function return value. If execution fails set |exception| to the exception
+  // that will be thrown. Return true (1) if execution was handled.
+  ///
+  int(CEF_CALLBACK* execute)(struct _cef_v8handler_t* self,
+                             const cef_string_t* name,
+                             struct _cef_v8value_t* object,
+                             size_t argumentsCount,
+                             struct _cef_v8value_t* const* arguments,
+                             struct _cef_v8value_t** retval,
+                             cef_string_t* exception);
+} cef_v8handler_t;
+
+///
+// Structure that should be implemented to handle V8 accessor calls. Accessor
+// identifiers are registered by calling cef_v8value_t::set_value(). The
+// functions of this structure will be called on the thread associated with the
+// V8 accessor.
+///
+typedef struct _cef_v8accessor_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Handle retrieval the accessor value identified by |name|. |object| is the
+  // receiver ('this' object) of the accessor. If retrieval succeeds set
+  // |retval| to the return value. If retrieval fails set |exception| to the
+  // exception that will be thrown. Return true (1) if accessor retrieval was
+  // handled.
+  ///
+  int(CEF_CALLBACK* get)(struct _cef_v8accessor_t* self,
+                         const cef_string_t* name,
+                         struct _cef_v8value_t* object,
+                         struct _cef_v8value_t** retval,
+                         cef_string_t* exception);
+
+  ///
+  // Handle assignment of the accessor value identified by |name|. |object| is
+  // the receiver ('this' object) of the accessor. |value| is the new value
+  // being assigned to the accessor. If assignment fails set |exception| to the
+  // exception that will be thrown. Return true (1) if accessor assignment was
+  // handled.
+  ///
+  int(CEF_CALLBACK* set)(struct _cef_v8accessor_t* self,
+                         const cef_string_t* name,
+                         struct _cef_v8value_t* object,
+                         struct _cef_v8value_t* value,
+                         cef_string_t* exception);
+} cef_v8accessor_t;
+
+///
+// Structure that should be implemented to handle V8 interceptor calls. The
+// functions of this structure will be called on the thread associated with the
+// V8 interceptor. Interceptor's named property handlers (with first argument of
+// type CefString) are called when object is indexed by string. Indexed property
+// handlers (with first argument of type int) are called when object is indexed
+// by integer.
+///
+typedef struct _cef_v8interceptor_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Handle retrieval of the interceptor value identified by |name|. |object| is
+  // the receiver ('this' object) of the interceptor. If retrieval succeeds, set
+  // |retval| to the return value. If the requested value does not exist, don't
+  // set either |retval| or |exception|. If retrieval fails, set |exception| to
+  // the exception that will be thrown. If the property has an associated
+  // accessor, it will be called only if you don't set |retval|. Return true (1)
+  // if interceptor retrieval was handled, false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* get_byname)(struct _cef_v8interceptor_t* self,
+                                const cef_string_t* name,
+                                struct _cef_v8value_t* object,
+                                struct _cef_v8value_t** retval,
+                                cef_string_t* exception);
+
+  ///
+  // Handle retrieval of the interceptor value identified by |index|. |object|
+  // is the receiver ('this' object) of the interceptor. If retrieval succeeds,
+  // set |retval| to the return value. If the requested value does not exist,
+  // don't set either |retval| or |exception|. If retrieval fails, set
+  // |exception| to the exception that will be thrown. Return true (1) if
+  // interceptor retrieval was handled, false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* get_byindex)(struct _cef_v8interceptor_t* self,
+                                 int index,
+                                 struct _cef_v8value_t* object,
+                                 struct _cef_v8value_t** retval,
+                                 cef_string_t* exception);
+
+  ///
+  // Handle assignment of the interceptor value identified by |name|. |object|
+  // is the receiver ('this' object) of the interceptor. |value| is the new
+  // value being assigned to the interceptor. If assignment fails, set
+  // |exception| to the exception that will be thrown. This setter will always
+  // be called, even when the property has an associated accessor. Return true
+  // (1) if interceptor assignment was handled, false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* set_byname)(struct _cef_v8interceptor_t* self,
+                                const cef_string_t* name,
+                                struct _cef_v8value_t* object,
+                                struct _cef_v8value_t* value,
+                                cef_string_t* exception);
+
+  ///
+  // Handle assignment of the interceptor value identified by |index|. |object|
+  // is the receiver ('this' object) of the interceptor. |value| is the new
+  // value being assigned to the interceptor. If assignment fails, set
+  // |exception| to the exception that will be thrown. Return true (1) if
+  // interceptor assignment was handled, false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* set_byindex)(struct _cef_v8interceptor_t* self,
+                                 int index,
+                                 struct _cef_v8value_t* object,
+                                 struct _cef_v8value_t* value,
+                                 cef_string_t* exception);
+} cef_v8interceptor_t;
+
+///
+// Structure representing a V8 exception. The functions of this structure may be
+// called on any render process thread.
+///
+typedef struct _cef_v8exception_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the exception message.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_message)(
+      struct _cef_v8exception_t* self);
+
+  ///
+  // Returns the line of source code that the exception occurred within.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_source_line)(
+      struct _cef_v8exception_t* self);
+
+  ///
+  // Returns the resource name for the script from where the function causing
+  // the error originates.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_script_resource_name)(
+      struct _cef_v8exception_t* self);
+
+  ///
+  // Returns the 1-based number of the line where the error occurred or 0 if the
+  // line number is unknown.
+  ///
+  int(CEF_CALLBACK* get_line_number)(struct _cef_v8exception_t* self);
+
+  ///
+  // Returns the index within the script of the first character where the error
+  // occurred.
+  ///
+  int(CEF_CALLBACK* get_start_position)(struct _cef_v8exception_t* self);
+
+  ///
+  // Returns the index within the script of the last character where the error
+  // occurred.
+  ///
+  int(CEF_CALLBACK* get_end_position)(struct _cef_v8exception_t* self);
+
+  ///
+  // Returns the index within the line of the first character where the error
+  // occurred.
+  ///
+  int(CEF_CALLBACK* get_start_column)(struct _cef_v8exception_t* self);
+
+  ///
+  // Returns the index within the line of the last character where the error
+  // occurred.
+  ///
+  int(CEF_CALLBACK* get_end_column)(struct _cef_v8exception_t* self);
+} cef_v8exception_t;
+
+///
+// Callback structure that is passed to cef_v8value_t::CreateArrayBuffer.
+///
+typedef struct _cef_v8array_buffer_release_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Called to release |buffer| when the ArrayBuffer JS object is garbage
+  // collected. |buffer| is the value that was passed to CreateArrayBuffer along
+  // with this object.
+  ///
+  void(CEF_CALLBACK* release_buffer)(
+      struct _cef_v8array_buffer_release_callback_t* self,
+      void* buffer);
+} cef_v8array_buffer_release_callback_t;
+
+///
+// Structure representing a V8 value handle. V8 handles can only be accessed
+// from the thread on which they are created. Valid threads for creating a V8
+// handle include the render process main thread (TID_RENDERER) and WebWorker
+// threads. A task runner for posting tasks on the associated thread can be
+// retrieved via the cef_v8context_t::get_task_runner() function.
+///
+typedef struct _cef_v8value_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if the underlying handle is valid and it can be accessed
+  // on the current thread. Do not call any other functions if this function
+  // returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is undefined.
+  ///
+  int(CEF_CALLBACK* is_undefined)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is null.
+  ///
+  int(CEF_CALLBACK* is_null)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is bool.
+  ///
+  int(CEF_CALLBACK* is_bool)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is int.
+  ///
+  int(CEF_CALLBACK* is_int)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is unsigned int.
+  ///
+  int(CEF_CALLBACK* is_uint)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is double.
+  ///
+  int(CEF_CALLBACK* is_double)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is Date.
+  ///
+  int(CEF_CALLBACK* is_date)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is string.
+  ///
+  int(CEF_CALLBACK* is_string)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is object.
+  ///
+  int(CEF_CALLBACK* is_object)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is array.
+  ///
+  int(CEF_CALLBACK* is_array)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is an ArrayBuffer.
+  ///
+  int(CEF_CALLBACK* is_array_buffer)(struct _cef_v8value_t* self);
+
+  ///
+  // True if the value type is function.
+  ///
+  int(CEF_CALLBACK* is_function)(struct _cef_v8value_t* self);
+
+  ///
+  // Returns true (1) if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_v8value_t* self,
+                             struct _cef_v8value_t* that);
+
+  ///
+  // Return a bool value.
+  ///
+  int(CEF_CALLBACK* get_bool_value)(struct _cef_v8value_t* self);
+
+  ///
+  // Return an int value.
+  ///
+  int32(CEF_CALLBACK* get_int_value)(struct _cef_v8value_t* self);
+
+  ///
+  // Return an unsigned int value.
+  ///
+  uint32(CEF_CALLBACK* get_uint_value)(struct _cef_v8value_t* self);
+
+  ///
+  // Return a double value.
+  ///
+  double(CEF_CALLBACK* get_double_value)(struct _cef_v8value_t* self);
+
+  ///
+  // Return a Date value.
+  ///
+  cef_time_t(CEF_CALLBACK* get_date_value)(struct _cef_v8value_t* self);
+
+  ///
+  // Return a string value.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_string_value)(
+      struct _cef_v8value_t* self);
+
+  // OBJECT METHODS - These functions are only available on objects. Arrays and
+  // functions are also objects. String- and integer-based keys can be used
+  // interchangably with the framework converting between them as necessary.
+
+  ///
+  // Returns true (1) if this is a user created object.
+  ///
+  int(CEF_CALLBACK* is_user_created)(struct _cef_v8value_t* self);
+
+  ///
+  // Returns true (1) if the last function call resulted in an exception. This
+  // attribute exists only in the scope of the current CEF value object.
+  ///
+  int(CEF_CALLBACK* has_exception)(struct _cef_v8value_t* self);
+
+  ///
+  // Returns the exception resulting from the last function call. This attribute
+  // exists only in the scope of the current CEF value object.
+  ///
+  struct _cef_v8exception_t*(CEF_CALLBACK* get_exception)(
+      struct _cef_v8value_t* self);
+
+  ///
+  // Clears the last exception and returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* clear_exception)(struct _cef_v8value_t* self);
+
+  ///
+  // Returns true (1) if this object will re-throw future exceptions. This
+  // attribute exists only in the scope of the current CEF value object.
+  ///
+  int(CEF_CALLBACK* will_rethrow_exceptions)(struct _cef_v8value_t* self);
+
+  ///
+  // Set whether this object will re-throw future exceptions. By default
+  // exceptions are not re-thrown. If a exception is re-thrown the current
+  // context should not be accessed again until after the exception has been
+  // caught and not re-thrown. Returns true (1) on success. This attribute
+  // exists only in the scope of the current CEF value object.
+  ///
+  int(CEF_CALLBACK* set_rethrow_exceptions)(struct _cef_v8value_t* self,
+                                            int rethrow);
+
+  ///
+  // Returns true (1) if the object has a value with the specified identifier.
+  ///
+  int(CEF_CALLBACK* has_value_bykey)(struct _cef_v8value_t* self,
+                                     const cef_string_t* key);
+
+  ///
+  // Returns true (1) if the object has a value with the specified identifier.
+  ///
+  int(CEF_CALLBACK* has_value_byindex)(struct _cef_v8value_t* self, int index);
+
+  ///
+  // Deletes the value with the specified identifier and returns true (1) on
+  // success. Returns false (0) if this function is called incorrectly or an
+  // exception is thrown. For read-only and don't-delete values this function
+  // will return true (1) even though deletion failed.
+  ///
+  int(CEF_CALLBACK* delete_value_bykey)(struct _cef_v8value_t* self,
+                                        const cef_string_t* key);
+
+  ///
+  // Deletes the value with the specified identifier and returns true (1) on
+  // success. Returns false (0) if this function is called incorrectly, deletion
+  // fails or an exception is thrown. For read-only and don't-delete values this
+  // function will return true (1) even though deletion failed.
+  ///
+  int(CEF_CALLBACK* delete_value_byindex)(struct _cef_v8value_t* self,
+                                          int index);
+
+  ///
+  // Returns the value with the specified identifier on success. Returns NULL if
+  // this function is called incorrectly or an exception is thrown.
+  ///
+  struct _cef_v8value_t*(CEF_CALLBACK* get_value_bykey)(
+      struct _cef_v8value_t* self,
+      const cef_string_t* key);
+
+  ///
+  // Returns the value with the specified identifier on success. Returns NULL if
+  // this function is called incorrectly or an exception is thrown.
+  ///
+  struct _cef_v8value_t*(
+      CEF_CALLBACK* get_value_byindex)(struct _cef_v8value_t* self, int index);
+
+  ///
+  // Associates a value with the specified identifier and returns true (1) on
+  // success. Returns false (0) if this function is called incorrectly or an
+  // exception is thrown. For read-only values this function will return true
+  // (1) even though assignment failed.
+  ///
+  int(CEF_CALLBACK* set_value_bykey)(struct _cef_v8value_t* self,
+                                     const cef_string_t* key,
+                                     struct _cef_v8value_t* value,
+                                     cef_v8_propertyattribute_t attribute);
+
+  ///
+  // Associates a value with the specified identifier and returns true (1) on
+  // success. Returns false (0) if this function is called incorrectly or an
+  // exception is thrown. For read-only values this function will return true
+  // (1) even though assignment failed.
+  ///
+  int(CEF_CALLBACK* set_value_byindex)(struct _cef_v8value_t* self,
+                                       int index,
+                                       struct _cef_v8value_t* value);
+
+  ///
+  // Registers an identifier and returns true (1) on success. Access to the
+  // identifier will be forwarded to the cef_v8accessor_t instance passed to
+  // cef_v8value_t::cef_v8value_create_object(). Returns false (0) if this
+  // function is called incorrectly or an exception is thrown. For read-only
+  // values this function will return true (1) even though assignment failed.
+  ///
+  int(CEF_CALLBACK* set_value_byaccessor)(struct _cef_v8value_t* self,
+                                          const cef_string_t* key,
+                                          cef_v8_accesscontrol_t settings,
+                                          cef_v8_propertyattribute_t attribute);
+
+  ///
+  // Read the keys for the object's values into the specified vector. Integer-
+  // based keys will also be returned as strings.
+  ///
+  int(CEF_CALLBACK* get_keys)(struct _cef_v8value_t* self,
+                              cef_string_list_t keys);
+
+  ///
+  // Sets the user data for this object and returns true (1) on success. Returns
+  // false (0) if this function is called incorrectly. This function can only be
+  // called on user created objects.
+  ///
+  int(CEF_CALLBACK* set_user_data)(struct _cef_v8value_t* self,
+                                   struct _cef_base_ref_counted_t* user_data);
+
+  ///
+  // Returns the user data, if any, assigned to this object.
+  ///
+  struct _cef_base_ref_counted_t*(CEF_CALLBACK* get_user_data)(
+      struct _cef_v8value_t* self);
+
+  ///
+  // Returns the amount of externally allocated memory registered for the
+  // object.
+  ///
+  int(CEF_CALLBACK* get_externally_allocated_memory)(
+      struct _cef_v8value_t* self);
+
+  ///
+  // Adjusts the amount of registered external memory for the object. Used to
+  // give V8 an indication of the amount of externally allocated memory that is
+  // kept alive by JavaScript objects. V8 uses this information to decide when
+  // to perform global garbage collection. Each cef_v8value_t tracks the amount
+  // of external memory associated with it and automatically decreases the
+  // global total by the appropriate amount on its destruction.
+  // |change_in_bytes| specifies the number of bytes to adjust by. This function
+  // returns the number of bytes associated with the object after the
+  // adjustment. This function can only be called on user created objects.
+  ///
+  int(CEF_CALLBACK* adjust_externally_allocated_memory)(
+      struct _cef_v8value_t* self,
+      int change_in_bytes);
+
+  // ARRAY METHODS - These functions are only available on arrays.
+
+  ///
+  // Returns the number of elements in the array.
+  ///
+  int(CEF_CALLBACK* get_array_length)(struct _cef_v8value_t* self);
+
+  // ARRAY BUFFER METHODS - These functions are only available on ArrayBuffers.
+
+  ///
+  // Returns the ReleaseCallback object associated with the ArrayBuffer or NULL
+  // if the ArrayBuffer was not created with CreateArrayBuffer.
+  ///
+  struct _cef_v8array_buffer_release_callback_t*(
+      CEF_CALLBACK* get_array_buffer_release_callback)(
+      struct _cef_v8value_t* self);
+
+  ///
+  // Prevent the ArrayBuffer from using it's memory block by setting the length
+  // to zero. This operation cannot be undone. If the ArrayBuffer was created
+  // with CreateArrayBuffer then
+  // cef_v8array_buffer_release_callback_t::ReleaseBuffer will be called to
+  // release the underlying buffer.
+  ///
+  int(CEF_CALLBACK* neuter_array_buffer)(struct _cef_v8value_t* self);
+
+  // FUNCTION METHODS - These functions are only available on functions.
+
+  ///
+  // Returns the function name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_function_name)(
+      struct _cef_v8value_t* self);
+
+  ///
+  // Returns the function handler or NULL if not a CEF-created function.
+  ///
+  struct _cef_v8handler_t*(CEF_CALLBACK* get_function_handler)(
+      struct _cef_v8value_t* self);
+
+  ///
+  // Execute the function using the current V8 context. This function should
+  // only be called from within the scope of a cef_v8handler_t or
+  // cef_v8accessor_t callback, or in combination with calling enter() and
+  // exit() on a stored cef_v8context_t reference. |object| is the receiver
+  // ('this' object) of the function. If |object| is NULL the current context's
+  // global object will be used. |arguments| is the list of arguments that will
+  // be passed to the function. Returns the function return value on success.
+  // Returns NULL if this function is called incorrectly or an exception is
+  // thrown.
+  ///
+  struct _cef_v8value_t*(CEF_CALLBACK* execute_function)(
+      struct _cef_v8value_t* self,
+      struct _cef_v8value_t* object,
+      size_t argumentsCount,
+      struct _cef_v8value_t* const* arguments);
+
+  ///
+  // Execute the function using the specified V8 context. |object| is the
+  // receiver ('this' object) of the function. If |object| is NULL the specified
+  // context's global object will be used. |arguments| is the list of arguments
+  // that will be passed to the function. Returns the function return value on
+  // success. Returns NULL if this function is called incorrectly or an
+  // exception is thrown.
+  ///
+  struct _cef_v8value_t*(CEF_CALLBACK* execute_function_with_context)(
+      struct _cef_v8value_t* self,
+      struct _cef_v8context_t* context,
+      struct _cef_v8value_t* object,
+      size_t argumentsCount,
+      struct _cef_v8value_t* const* arguments);
+} cef_v8value_t;
+
+///
+// Create a new cef_v8value_t object of type undefined.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_undefined();
+
+///
+// Create a new cef_v8value_t object of type null.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_null();
+
+///
+// Create a new cef_v8value_t object of type bool.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_bool(int value);
+
+///
+// Create a new cef_v8value_t object of type int.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_int(int32 value);
+
+///
+// Create a new cef_v8value_t object of type unsigned int.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_uint(uint32 value);
+
+///
+// Create a new cef_v8value_t object of type double.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_double(double value);
+
+///
+// Create a new cef_v8value_t object of type Date. This function should only be
+// called from within the scope of a cef_render_process_handler_t,
+// cef_v8handler_t or cef_v8accessor_t callback, or in combination with calling
+// enter() and exit() on a stored cef_v8context_t reference.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_date(const cef_time_t* date);
+
+///
+// Create a new cef_v8value_t object of type string.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_string(const cef_string_t* value);
+
+///
+// Create a new cef_v8value_t object of type object with optional accessor
+// and/or interceptor. This function should only be called from within the scope
+// of a cef_render_process_handler_t, cef_v8handler_t or cef_v8accessor_t
+// callback, or in combination with calling enter() and exit() on a stored
+// cef_v8context_t reference.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_object(
+    cef_v8accessor_t* accessor,
+    cef_v8interceptor_t* interceptor);
+
+///
+// Create a new cef_v8value_t object of type array with the specified |length|.
+// If |length| is negative the returned array will have length 0. This function
+// should only be called from within the scope of a
+// cef_render_process_handler_t, cef_v8handler_t or cef_v8accessor_t callback,
+// or in combination with calling enter() and exit() on a stored cef_v8context_t
+// reference.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_array(int length);
+
+///
+// Create a new cef_v8value_t object of type ArrayBuffer which wraps the
+// provided |buffer| of size |length| bytes. The ArrayBuffer is externalized,
+// meaning that it does not own |buffer|. The caller is responsible for freeing
+// |buffer| when requested via a call to cef_v8array_buffer_release_callback_t::
+// ReleaseBuffer. This function should only be called from within the scope of a
+// cef_render_process_handler_t, cef_v8handler_t or cef_v8accessor_t callback,
+// or in combination with calling enter() and exit() on a stored cef_v8context_t
+// reference.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer(
+    void* buffer,
+    size_t length,
+    cef_v8array_buffer_release_callback_t* release_callback);
+
+///
+// Create a new cef_v8value_t object of type function. This function should only
+// be called from within the scope of a cef_render_process_handler_t,
+// cef_v8handler_t or cef_v8accessor_t callback, or in combination with calling
+// enter() and exit() on a stored cef_v8context_t reference.
+///
+CEF_EXPORT cef_v8value_t* cef_v8value_create_function(const cef_string_t* name,
+                                                      cef_v8handler_t* handler);
+
+///
+// Structure representing a V8 stack trace handle. V8 handles can only be
+// accessed from the thread on which they are created. Valid threads for
+// creating a V8 handle include the render process main thread (TID_RENDERER)
+// and WebWorker threads. A task runner for posting tasks on the associated
+// thread can be retrieved via the cef_v8context_t::get_task_runner() function.
+///
+typedef struct _cef_v8stack_trace_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if the underlying handle is valid and it can be accessed
+  // on the current thread. Do not call any other functions if this function
+  // returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_v8stack_trace_t* self);
+
+  ///
+  // Returns the number of stack frames.
+  ///
+  int(CEF_CALLBACK* get_frame_count)(struct _cef_v8stack_trace_t* self);
+
+  ///
+  // Returns the stack frame at the specified 0-based index.
+  ///
+  struct _cef_v8stack_frame_t*(
+      CEF_CALLBACK* get_frame)(struct _cef_v8stack_trace_t* self, int index);
+} cef_v8stack_trace_t;
+
+///
+// Returns the stack trace for the currently active context. |frame_limit| is
+// the maximum number of frames that will be captured.
+///
+CEF_EXPORT cef_v8stack_trace_t* cef_v8stack_trace_get_current(int frame_limit);
+
+///
+// Structure representing a V8 stack frame handle. V8 handles can only be
+// accessed from the thread on which they are created. Valid threads for
+// creating a V8 handle include the render process main thread (TID_RENDERER)
+// and WebWorker threads. A task runner for posting tasks on the associated
+// thread can be retrieved via the cef_v8context_t::get_task_runner() function.
+///
+typedef struct _cef_v8stack_frame_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if the underlying handle is valid and it can be accessed
+  // on the current thread. Do not call any other functions if this function
+  // returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_v8stack_frame_t* self);
+
+  ///
+  // Returns the name of the resource script that contains the function.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_script_name)(
+      struct _cef_v8stack_frame_t* self);
+
+  ///
+  // Returns the name of the resource script that contains the function or the
+  // sourceURL value if the script name is undefined and its source ends with a
+  // "//@ sourceURL=..." string.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_script_name_or_source_url)(
+      struct _cef_v8stack_frame_t* self);
+
+  ///
+  // Returns the name of the function.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_function_name)(
+      struct _cef_v8stack_frame_t* self);
+
+  ///
+  // Returns the 1-based line number for the function call or 0 if unknown.
+  ///
+  int(CEF_CALLBACK* get_line_number)(struct _cef_v8stack_frame_t* self);
+
+  ///
+  // Returns the 1-based column offset on the line for the function call or 0 if
+  // unknown.
+  ///
+  int(CEF_CALLBACK* get_column)(struct _cef_v8stack_frame_t* self);
+
+  ///
+  // Returns true (1) if the function was compiled using eval().
+  ///
+  int(CEF_CALLBACK* is_eval)(struct _cef_v8stack_frame_t* self);
+
+  ///
+  // Returns true (1) if the function was called as a constructor via "new".
+  ///
+  int(CEF_CALLBACK* is_constructor)(struct _cef_v8stack_frame_t* self);
+} cef_v8stack_frame_t;
+
+///
+// Register a new V8 extension with the specified JavaScript extension code and
+// handler. Functions implemented by the handler are prototyped using the
+// keyword 'native'. The calling of a native function is restricted to the scope
+// in which the prototype of the native function is defined. This function may
+// only be called on the render process main thread.
+//
+// Example JavaScript extension code: <pre>
+//   // create the 'example' global object if it doesn't already exist.
+//   if (!example)
+//     example = {};
+//   // create the 'example.test' global object if it doesn't already exist.
+//   if (!example.test)
+//     example.test = {};
+//   (function() {
+//     // Define the function 'example.test.myfunction'.
+//     example.test.myfunction = function() {
+//       // Call CefV8Handler::Execute() with the function name 'MyFunction'
+//       // and no arguments.
+//       native function MyFunction();
+//       return MyFunction();
+//     };
+//     // Define the getter function for parameter 'example.test.myparam'.
+//     example.test.__defineGetter__('myparam', function() {
+//       // Call CefV8Handler::Execute() with the function name 'GetMyParam'
+//       // and no arguments.
+//       native function GetMyParam();
+//       return GetMyParam();
+//     });
+//     // Define the setter function for parameter 'example.test.myparam'.
+//     example.test.__defineSetter__('myparam', function(b) {
+//       // Call CefV8Handler::Execute() with the function name 'SetMyParam'
+//       // and a single argument.
+//       native function SetMyParam();
+//       if(b) SetMyParam(b);
+//     });
+//
+//     // Extension definitions can also contain normal JavaScript variables
+//     // and functions.
+//     var myint = 0;
+//     example.test.increment = function() {
+//       myint += 1;
+//       return myint;
+//     };
+//   })();
+// </pre> Example usage in the page: <pre>
+//   // Call the function.
+//   example.test.myfunction();
+//   // Set the parameter.
+//   example.test.myparam = value;
+//   // Get the parameter.
+//   value = example.test.myparam;
+//   // Call another function.
+//   example.test.increment();
+// </pre>
+///
+CEF_EXPORT int cef_register_extension(const cef_string_t* extension_name,
+                                      const cef_string_t* javascript_code,
+                                      cef_v8handler_t* handler);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_V8_CAPI_H_
diff --git a/src/include/capi/cef_values_capi.h b/src/include/capi/cef_values_capi.h
new file mode 100644
index 0000000..c12867b
--- /dev/null
+++ b/src/include/capi/cef_values_capi.h
@@ -0,0 +1,753 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=22f935968cd7f2549def42f5d84694311bde125e$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_VALUES_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_VALUES_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_binary_value_t;
+struct _cef_dictionary_value_t;
+struct _cef_list_value_t;
+
+///
+// Structure that wraps other data value types. Complex types (binary,
+// dictionary and list) will be referenced but not owned by this object. Can be
+// used on any process and thread.
+///
+typedef struct _cef_value_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if the underlying data is valid. This will always be true
+  // (1) for simple types. For complex types (binary, dictionary and list) the
+  // underlying data may become invalid if owned by another object (e.g. list or
+  // dictionary) and that other object is then modified or destroyed. This value
+  // object can be re-used by calling Set*() even if the underlying data is
+  // invalid.
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_value_t* self);
+
+  ///
+  // Returns true (1) if the underlying data is owned by another object.
+  ///
+  int(CEF_CALLBACK* is_owned)(struct _cef_value_t* self);
+
+  ///
+  // Returns true (1) if the underlying data is read-only. Some APIs may expose
+  // read-only objects.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_value_t* self);
+
+  ///
+  // Returns true (1) if this object and |that| object have the same underlying
+  // data. If true (1) modifications to this object will also affect |that|
+  // object and vice-versa.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_value_t* self,
+                             struct _cef_value_t* that);
+
+  ///
+  // Returns true (1) if this object and |that| object have an equivalent
+  // underlying value but are not necessarily the same object.
+  ///
+  int(CEF_CALLBACK* is_equal)(struct _cef_value_t* self,
+                              struct _cef_value_t* that);
+
+  ///
+  // Returns a copy of this object. The underlying data will also be copied.
+  ///
+  struct _cef_value_t*(CEF_CALLBACK* copy)(struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value type.
+  ///
+  cef_value_type_t(CEF_CALLBACK* get_type)(struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value as type bool.
+  ///
+  int(CEF_CALLBACK* get_bool)(struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value as type int.
+  ///
+  int(CEF_CALLBACK* get_int)(struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value as type double.
+  ///
+  double(CEF_CALLBACK* get_double)(struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value as type string.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_string)(struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value as type binary. The returned reference may
+  // become invalid if the value is owned by another object or if ownership is
+  // transferred to another object in the future. To maintain a reference to the
+  // value after assigning ownership to a dictionary or list pass this object to
+  // the set_value() function instead of passing the returned reference to
+  // set_binary().
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_binary)(
+      struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value as type dictionary. The returned reference may
+  // become invalid if the value is owned by another object or if ownership is
+  // transferred to another object in the future. To maintain a reference to the
+  // value after assigning ownership to a dictionary or list pass this object to
+  // the set_value() function instead of passing the returned reference to
+  // set_dictionary().
+  ///
+  struct _cef_dictionary_value_t*(CEF_CALLBACK* get_dictionary)(
+      struct _cef_value_t* self);
+
+  ///
+  // Returns the underlying value as type list. The returned reference may
+  // become invalid if the value is owned by another object or if ownership is
+  // transferred to another object in the future. To maintain a reference to the
+  // value after assigning ownership to a dictionary or list pass this object to
+  // the set_value() function instead of passing the returned reference to
+  // set_list().
+  ///
+  struct _cef_list_value_t*(CEF_CALLBACK* get_list)(struct _cef_value_t* self);
+
+  ///
+  // Sets the underlying value as type null. Returns true (1) if the value was
+  // set successfully.
+  ///
+  int(CEF_CALLBACK* set_null)(struct _cef_value_t* self);
+
+  ///
+  // Sets the underlying value as type bool. Returns true (1) if the value was
+  // set successfully.
+  ///
+  int(CEF_CALLBACK* set_bool)(struct _cef_value_t* self, int value);
+
+  ///
+  // Sets the underlying value as type int. Returns true (1) if the value was
+  // set successfully.
+  ///
+  int(CEF_CALLBACK* set_int)(struct _cef_value_t* self, int value);
+
+  ///
+  // Sets the underlying value as type double. Returns true (1) if the value was
+  // set successfully.
+  ///
+  int(CEF_CALLBACK* set_double)(struct _cef_value_t* self, double value);
+
+  ///
+  // Sets the underlying value as type string. Returns true (1) if the value was
+  // set successfully.
+  ///
+  int(CEF_CALLBACK* set_string)(struct _cef_value_t* self,
+                                const cef_string_t* value);
+
+  ///
+  // Sets the underlying value as type binary. Returns true (1) if the value was
+  // set successfully. This object keeps a reference to |value| and ownership of
+  // the underlying data remains unchanged.
+  ///
+  int(CEF_CALLBACK* set_binary)(struct _cef_value_t* self,
+                                struct _cef_binary_value_t* value);
+
+  ///
+  // Sets the underlying value as type dict. Returns true (1) if the value was
+  // set successfully. This object keeps a reference to |value| and ownership of
+  // the underlying data remains unchanged.
+  ///
+  int(CEF_CALLBACK* set_dictionary)(struct _cef_value_t* self,
+                                    struct _cef_dictionary_value_t* value);
+
+  ///
+  // Sets the underlying value as type list. Returns true (1) if the value was
+  // set successfully. This object keeps a reference to |value| and ownership of
+  // the underlying data remains unchanged.
+  ///
+  int(CEF_CALLBACK* set_list)(struct _cef_value_t* self,
+                              struct _cef_list_value_t* value);
+} cef_value_t;
+
+///
+// Creates a new object.
+///
+CEF_EXPORT cef_value_t* cef_value_create();
+
+///
+// Structure representing a binary value. Can be used on any process and thread.
+///
+typedef struct _cef_binary_value_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. This object may become invalid if
+  // the underlying data is owned by another object (e.g. list or dictionary)
+  // and that other object is then modified or destroyed. Do not call any other
+  // functions if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_binary_value_t* self);
+
+  ///
+  // Returns true (1) if this object is currently owned by another object.
+  ///
+  int(CEF_CALLBACK* is_owned)(struct _cef_binary_value_t* self);
+
+  ///
+  // Returns true (1) if this object and |that| object have the same underlying
+  // data.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_binary_value_t* self,
+                             struct _cef_binary_value_t* that);
+
+  ///
+  // Returns true (1) if this object and |that| object have an equivalent
+  // underlying value but are not necessarily the same object.
+  ///
+  int(CEF_CALLBACK* is_equal)(struct _cef_binary_value_t* self,
+                              struct _cef_binary_value_t* that);
+
+  ///
+  // Returns a copy of this object. The data in this object will also be copied.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* copy)(
+      struct _cef_binary_value_t* self);
+
+  ///
+  // Returns the data size.
+  ///
+  size_t(CEF_CALLBACK* get_size)(struct _cef_binary_value_t* self);
+
+  ///
+  // Read up to |buffer_size| number of bytes into |buffer|. Reading begins at
+  // the specified byte |data_offset|. Returns the number of bytes read.
+  ///
+  size_t(CEF_CALLBACK* get_data)(struct _cef_binary_value_t* self,
+                                 void* buffer,
+                                 size_t buffer_size,
+                                 size_t data_offset);
+} cef_binary_value_t;
+
+///
+// Creates a new object that is not owned by any other object. The specified
+// |data| will be copied.
+///
+CEF_EXPORT cef_binary_value_t* cef_binary_value_create(const void* data,
+                                                       size_t data_size);
+
+///
+// Structure representing a dictionary value. Can be used on any process and
+// thread.
+///
+typedef struct _cef_dictionary_value_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. This object may become invalid if
+  // the underlying data is owned by another object (e.g. list or dictionary)
+  // and that other object is then modified or destroyed. Do not call any other
+  // functions if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_dictionary_value_t* self);
+
+  ///
+  // Returns true (1) if this object is currently owned by another object.
+  ///
+  int(CEF_CALLBACK* is_owned)(struct _cef_dictionary_value_t* self);
+
+  ///
+  // Returns true (1) if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_dictionary_value_t* self);
+
+  ///
+  // Returns true (1) if this object and |that| object have the same underlying
+  // data. If true (1) modifications to this object will also affect |that|
+  // object and vice-versa.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_dictionary_value_t* self,
+                             struct _cef_dictionary_value_t* that);
+
+  ///
+  // Returns true (1) if this object and |that| object have an equivalent
+  // underlying value but are not necessarily the same object.
+  ///
+  int(CEF_CALLBACK* is_equal)(struct _cef_dictionary_value_t* self,
+                              struct _cef_dictionary_value_t* that);
+
+  ///
+  // Returns a writable copy of this object. If |exclude_NULL_children| is true
+  // (1) any NULL dictionaries or lists will be excluded from the copy.
+  ///
+  struct _cef_dictionary_value_t*(CEF_CALLBACK* copy)(
+      struct _cef_dictionary_value_t* self,
+      int exclude_empty_children);
+
+  ///
+  // Returns the number of values.
+  ///
+  size_t(CEF_CALLBACK* get_size)(struct _cef_dictionary_value_t* self);
+
+  ///
+  // Removes all values. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* clear)(struct _cef_dictionary_value_t* self);
+
+  ///
+  // Returns true (1) if the current dictionary has a value for the given key.
+  ///
+  int(CEF_CALLBACK* has_key)(struct _cef_dictionary_value_t* self,
+                             const cef_string_t* key);
+
+  ///
+  // Reads all keys for this dictionary into the specified vector.
+  ///
+  int(CEF_CALLBACK* get_keys)(struct _cef_dictionary_value_t* self,
+                              cef_string_list_t keys);
+
+  ///
+  // Removes the value at the specified key. Returns true (1) is the value was
+  // removed successfully.
+  ///
+  int(CEF_CALLBACK* remove)(struct _cef_dictionary_value_t* self,
+                            const cef_string_t* key);
+
+  ///
+  // Returns the value type for the specified key.
+  ///
+  cef_value_type_t(CEF_CALLBACK* get_type)(struct _cef_dictionary_value_t* self,
+                                           const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key. For simple types the returned value
+  // will copy existing data and modifications to the value will not modify this
+  // object. For complex types (binary, dictionary and list) the returned value
+  // will reference existing data and modifications to the value will modify
+  // this object.
+  ///
+  struct _cef_value_t*(CEF_CALLBACK* get_value)(
+      struct _cef_dictionary_value_t* self,
+      const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key as type bool.
+  ///
+  int(CEF_CALLBACK* get_bool)(struct _cef_dictionary_value_t* self,
+                              const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key as type int.
+  ///
+  int(CEF_CALLBACK* get_int)(struct _cef_dictionary_value_t* self,
+                             const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key as type double.
+  ///
+  double(CEF_CALLBACK* get_double)(struct _cef_dictionary_value_t* self,
+                                   const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key as type string.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_string)(
+      struct _cef_dictionary_value_t* self,
+      const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key as type binary. The returned value
+  // will reference existing data.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_binary)(
+      struct _cef_dictionary_value_t* self,
+      const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key as type dictionary. The returned
+  // value will reference existing data and modifications to the value will
+  // modify this object.
+  ///
+  struct _cef_dictionary_value_t*(CEF_CALLBACK* get_dictionary)(
+      struct _cef_dictionary_value_t* self,
+      const cef_string_t* key);
+
+  ///
+  // Returns the value at the specified key as type list. The returned value
+  // will reference existing data and modifications to the value will modify
+  // this object.
+  ///
+  struct _cef_list_value_t*(CEF_CALLBACK* get_list)(
+      struct _cef_dictionary_value_t* self,
+      const cef_string_t* key);
+
+  ///
+  // Sets the value at the specified key. Returns true (1) if the value was set
+  // successfully. If |value| represents simple data then the underlying data
+  // will be copied and modifications to |value| will not modify this object. If
+  // |value| represents complex data (binary, dictionary or list) then the
+  // underlying data will be referenced and modifications to |value| will modify
+  // this object.
+  ///
+  int(CEF_CALLBACK* set_value)(struct _cef_dictionary_value_t* self,
+                               const cef_string_t* key,
+                               struct _cef_value_t* value);
+
+  ///
+  // Sets the value at the specified key as type null. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_null)(struct _cef_dictionary_value_t* self,
+                              const cef_string_t* key);
+
+  ///
+  // Sets the value at the specified key as type bool. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_bool)(struct _cef_dictionary_value_t* self,
+                              const cef_string_t* key,
+                              int value);
+
+  ///
+  // Sets the value at the specified key as type int. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_int)(struct _cef_dictionary_value_t* self,
+                             const cef_string_t* key,
+                             int value);
+
+  ///
+  // Sets the value at the specified key as type double. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_double)(struct _cef_dictionary_value_t* self,
+                                const cef_string_t* key,
+                                double value);
+
+  ///
+  // Sets the value at the specified key as type string. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_string)(struct _cef_dictionary_value_t* self,
+                                const cef_string_t* key,
+                                const cef_string_t* value);
+
+  ///
+  // Sets the value at the specified key as type binary. Returns true (1) if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  int(CEF_CALLBACK* set_binary)(struct _cef_dictionary_value_t* self,
+                                const cef_string_t* key,
+                                struct _cef_binary_value_t* value);
+
+  ///
+  // Sets the value at the specified key as type dict. Returns true (1) if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  int(CEF_CALLBACK* set_dictionary)(struct _cef_dictionary_value_t* self,
+                                    const cef_string_t* key,
+                                    struct _cef_dictionary_value_t* value);
+
+  ///
+  // Sets the value at the specified key as type list. Returns true (1) if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  int(CEF_CALLBACK* set_list)(struct _cef_dictionary_value_t* self,
+                              const cef_string_t* key,
+                              struct _cef_list_value_t* value);
+} cef_dictionary_value_t;
+
+///
+// Creates a new object that is not owned by any other object.
+///
+CEF_EXPORT cef_dictionary_value_t* cef_dictionary_value_create();
+
+///
+// Structure representing a list value. Can be used on any process and thread.
+///
+typedef struct _cef_list_value_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns true (1) if this object is valid. This object may become invalid if
+  // the underlying data is owned by another object (e.g. list or dictionary)
+  // and that other object is then modified or destroyed. Do not call any other
+  // functions if this function returns false (0).
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_list_value_t* self);
+
+  ///
+  // Returns true (1) if this object is currently owned by another object.
+  ///
+  int(CEF_CALLBACK* is_owned)(struct _cef_list_value_t* self);
+
+  ///
+  // Returns true (1) if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_list_value_t* self);
+
+  ///
+  // Returns true (1) if this object and |that| object have the same underlying
+  // data. If true (1) modifications to this object will also affect |that|
+  // object and vice-versa.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_list_value_t* self,
+                             struct _cef_list_value_t* that);
+
+  ///
+  // Returns true (1) if this object and |that| object have an equivalent
+  // underlying value but are not necessarily the same object.
+  ///
+  int(CEF_CALLBACK* is_equal)(struct _cef_list_value_t* self,
+                              struct _cef_list_value_t* that);
+
+  ///
+  // Returns a writable copy of this object.
+  ///
+  struct _cef_list_value_t*(CEF_CALLBACK* copy)(struct _cef_list_value_t* self);
+
+  ///
+  // Sets the number of values. If the number of values is expanded all new
+  // value slots will default to type null. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* set_size)(struct _cef_list_value_t* self, size_t size);
+
+  ///
+  // Returns the number of values.
+  ///
+  size_t(CEF_CALLBACK* get_size)(struct _cef_list_value_t* self);
+
+  ///
+  // Removes all values. Returns true (1) on success.
+  ///
+  int(CEF_CALLBACK* clear)(struct _cef_list_value_t* self);
+
+  ///
+  // Removes the value at the specified index.
+  ///
+  int(CEF_CALLBACK* remove)(struct _cef_list_value_t* self, size_t index);
+
+  ///
+  // Returns the value type at the specified index.
+  ///
+  cef_value_type_t(CEF_CALLBACK* get_type)(struct _cef_list_value_t* self,
+                                           size_t index);
+
+  ///
+  // Returns the value at the specified index. For simple types the returned
+  // value will copy existing data and modifications to the value will not
+  // modify this object. For complex types (binary, dictionary and list) the
+  // returned value will reference existing data and modifications to the value
+  // will modify this object.
+  ///
+  struct _cef_value_t*(CEF_CALLBACK* get_value)(struct _cef_list_value_t* self,
+                                                size_t index);
+
+  ///
+  // Returns the value at the specified index as type bool.
+  ///
+  int(CEF_CALLBACK* get_bool)(struct _cef_list_value_t* self, size_t index);
+
+  ///
+  // Returns the value at the specified index as type int.
+  ///
+  int(CEF_CALLBACK* get_int)(struct _cef_list_value_t* self, size_t index);
+
+  ///
+  // Returns the value at the specified index as type double.
+  ///
+  double(CEF_CALLBACK* get_double)(struct _cef_list_value_t* self,
+                                   size_t index);
+
+  ///
+  // Returns the value at the specified index as type string.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(
+      CEF_CALLBACK* get_string)(struct _cef_list_value_t* self, size_t index);
+
+  ///
+  // Returns the value at the specified index as type binary. The returned value
+  // will reference existing data.
+  ///
+  struct _cef_binary_value_t*(
+      CEF_CALLBACK* get_binary)(struct _cef_list_value_t* self, size_t index);
+
+  ///
+  // Returns the value at the specified index as type dictionary. The returned
+  // value will reference existing data and modifications to the value will
+  // modify this object.
+  ///
+  struct _cef_dictionary_value_t*(CEF_CALLBACK* get_dictionary)(
+      struct _cef_list_value_t* self,
+      size_t index);
+
+  ///
+  // Returns the value at the specified index as type list. The returned value
+  // will reference existing data and modifications to the value will modify
+  // this object.
+  ///
+  struct _cef_list_value_t*(
+      CEF_CALLBACK* get_list)(struct _cef_list_value_t* self, size_t index);
+
+  ///
+  // Sets the value at the specified index. Returns true (1) if the value was
+  // set successfully. If |value| represents simple data then the underlying
+  // data will be copied and modifications to |value| will not modify this
+  // object. If |value| represents complex data (binary, dictionary or list)
+  // then the underlying data will be referenced and modifications to |value|
+  // will modify this object.
+  ///
+  int(CEF_CALLBACK* set_value)(struct _cef_list_value_t* self,
+                               size_t index,
+                               struct _cef_value_t* value);
+
+  ///
+  // Sets the value at the specified index as type null. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_null)(struct _cef_list_value_t* self, size_t index);
+
+  ///
+  // Sets the value at the specified index as type bool. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_bool)(struct _cef_list_value_t* self,
+                              size_t index,
+                              int value);
+
+  ///
+  // Sets the value at the specified index as type int. Returns true (1) if the
+  // value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_int)(struct _cef_list_value_t* self,
+                             size_t index,
+                             int value);
+
+  ///
+  // Sets the value at the specified index as type double. Returns true (1) if
+  // the value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_double)(struct _cef_list_value_t* self,
+                                size_t index,
+                                double value);
+
+  ///
+  // Sets the value at the specified index as type string. Returns true (1) if
+  // the value was set successfully.
+  ///
+  int(CEF_CALLBACK* set_string)(struct _cef_list_value_t* self,
+                                size_t index,
+                                const cef_string_t* value);
+
+  ///
+  // Sets the value at the specified index as type binary. Returns true (1) if
+  // the value was set successfully. If |value| is currently owned by another
+  // object then the value will be copied and the |value| reference will not
+  // change. Otherwise, ownership will be transferred to this object and the
+  // |value| reference will be invalidated.
+  ///
+  int(CEF_CALLBACK* set_binary)(struct _cef_list_value_t* self,
+                                size_t index,
+                                struct _cef_binary_value_t* value);
+
+  ///
+  // Sets the value at the specified index as type dict. Returns true (1) if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  int(CEF_CALLBACK* set_dictionary)(struct _cef_list_value_t* self,
+                                    size_t index,
+                                    struct _cef_dictionary_value_t* value);
+
+  ///
+  // Sets the value at the specified index as type list. Returns true (1) if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  int(CEF_CALLBACK* set_list)(struct _cef_list_value_t* self,
+                              size_t index,
+                              struct _cef_list_value_t* value);
+} cef_list_value_t;
+
+///
+// Creates a new object that is not owned by any other object.
+///
+CEF_EXPORT cef_list_value_t* cef_list_value_create();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_VALUES_CAPI_H_
diff --git a/src/include/capi/cef_waitable_event_capi.h b/src/include/capi/cef_waitable_event_capi.h
new file mode 100644
index 0000000..e046586
--- /dev/null
+++ b/src/include/capi/cef_waitable_event_capi.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=aedfa5758cbf37dff244c065d55d273231470877$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_WAITABLE_EVENT_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_WAITABLE_EVENT_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// WaitableEvent is a thread synchronization tool that allows one thread to wait
+// for another thread to finish some work. This is equivalent to using a
+// Lock+ConditionVariable to protect a simple boolean value. However, using
+// WaitableEvent in conjunction with a Lock to wait for a more complex state
+// change (e.g., for an item to be added to a queue) is not recommended. In that
+// case consider using a ConditionVariable instead of a WaitableEvent. It is
+// safe to create and/or signal a WaitableEvent from any thread. Blocking on a
+// WaitableEvent by calling the *wait() functions is not allowed on the browser
+// process UI or IO threads.
+///
+typedef struct _cef_waitable_event_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Put the event in the un-signaled state.
+  ///
+  void(CEF_CALLBACK* reset)(struct _cef_waitable_event_t* self);
+
+  ///
+  // Put the event in the signaled state. This causes any thread blocked on Wait
+  // to be woken up.
+  ///
+  void(CEF_CALLBACK* signal)(struct _cef_waitable_event_t* self);
+
+  ///
+  // Returns true (1) if the event is in the signaled state, else false (0). If
+  // the event was created with |automatic_reset| set to true (1) then calling
+  // this function will also cause a reset.
+  ///
+  int(CEF_CALLBACK* is_signaled)(struct _cef_waitable_event_t* self);
+
+  ///
+  // Wait indefinitely for the event to be signaled. This function will not
+  // return until after the call to signal() has completed. This function cannot
+  // be called on the browser process UI or IO threads.
+  ///
+  void(CEF_CALLBACK* wait)(struct _cef_waitable_event_t* self);
+
+  ///
+  // Wait up to |max_ms| milliseconds for the event to be signaled. Returns true
+  // (1) if the event was signaled. A return value of false (0) does not
+  // necessarily mean that |max_ms| was exceeded. This function will not return
+  // until after the call to signal() has completed. This function cannot be
+  // called on the browser process UI or IO threads.
+  ///
+  int(CEF_CALLBACK* timed_wait)(struct _cef_waitable_event_t* self,
+                                int64 max_ms);
+} cef_waitable_event_t;
+
+///
+// Create a new waitable event. If |automatic_reset| is true (1) then the event
+// state is automatically reset to un-signaled after a single waiting thread has
+// been released; otherwise, the state remains signaled until reset() is called
+// manually. If |initially_signaled| is true (1) then the event will start in
+// the signaled state.
+///
+CEF_EXPORT cef_waitable_event_t* cef_waitable_event_create(
+    int automatic_reset,
+    int initially_signaled);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_WAITABLE_EVENT_CAPI_H_
diff --git a/src/include/capi/cef_web_plugin_capi.h b/src/include/capi/cef_web_plugin_capi.h
new file mode 100644
index 0000000..6cef957
--- /dev/null
+++ b/src/include/capi/cef_web_plugin_capi.h
@@ -0,0 +1,240 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=f1b2b6203d45fdf76d72ea1e79fcef0bb2a26138$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_WEB_PLUGIN_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_WEB_PLUGIN_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_browser_t;
+
+///
+// Information about a specific web plugin.
+///
+typedef struct _cef_web_plugin_info_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the plugin name (i.e. Flash).
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_name)(
+      struct _cef_web_plugin_info_t* self);
+
+  ///
+  // Returns the plugin file path (DLL/bundle/library).
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_path)(
+      struct _cef_web_plugin_info_t* self);
+
+  ///
+  // Returns the version of the plugin (may be OS-specific).
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_version)(
+      struct _cef_web_plugin_info_t* self);
+
+  ///
+  // Returns a description of the plugin from the version information.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_description)(
+      struct _cef_web_plugin_info_t* self);
+} cef_web_plugin_info_t;
+
+///
+// Structure to implement for visiting web plugin information. The functions of
+// this structure will be called on the browser process UI thread.
+///
+typedef struct _cef_web_plugin_info_visitor_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called once for each plugin. |count| is the 0-based
+  // index for the current plugin. |total| is the total number of plugins.
+  // Return false (0) to stop visiting plugins. This function may never be
+  // called if no plugins are found.
+  ///
+  int(CEF_CALLBACK* visit)(struct _cef_web_plugin_info_visitor_t* self,
+                           struct _cef_web_plugin_info_t* info,
+                           int count,
+                           int total);
+} cef_web_plugin_info_visitor_t;
+
+///
+// Structure to implement for receiving unstable plugin information. The
+// functions of this structure will be called on the browser process IO thread.
+///
+typedef struct _cef_web_plugin_unstable_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called for the requested plugin. |unstable| will be
+  // true (1) if the plugin has reached the crash count threshold of 3 times in
+  // 120 seconds.
+  ///
+  void(CEF_CALLBACK* is_unstable)(
+      struct _cef_web_plugin_unstable_callback_t* self,
+      const cef_string_t* path,
+      int unstable);
+} cef_web_plugin_unstable_callback_t;
+
+///
+// Implement this structure to receive notification when CDM registration is
+// complete. The functions of this structure will be called on the browser
+// process UI thread.
+///
+typedef struct _cef_register_cdm_callback_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Method that will be called when CDM registration is complete. |result| will
+  // be CEF_CDM_REGISTRATION_ERROR_NONE if registration completed successfully.
+  // Otherwise, |result| and |error_message| will contain additional information
+  // about why registration failed.
+  ///
+  void(CEF_CALLBACK* on_cdm_registration_complete)(
+      struct _cef_register_cdm_callback_t* self,
+      cef_cdm_registration_error_t result,
+      const cef_string_t* error_message);
+} cef_register_cdm_callback_t;
+
+///
+// Visit web plugin information. Can be called on any thread in the browser
+// process.
+///
+CEF_EXPORT void cef_visit_web_plugin_info(
+    cef_web_plugin_info_visitor_t* visitor);
+
+///
+// Cause the plugin list to refresh the next time it is accessed regardless of
+// whether it has already been loaded. Can be called on any thread in the
+// browser process.
+///
+CEF_EXPORT void cef_refresh_web_plugins();
+
+///
+// Unregister an internal plugin. This may be undone the next time
+// cef_refresh_web_plugins() is called. Can be called on any thread in the
+// browser process.
+///
+CEF_EXPORT void cef_unregister_internal_web_plugin(const cef_string_t* path);
+
+///
+// Register a plugin crash. Can be called on any thread in the browser process
+// but will be executed on the IO thread.
+///
+CEF_EXPORT void cef_register_web_plugin_crash(const cef_string_t* path);
+
+///
+// Query if a plugin is unstable. Can be called on any thread in the browser
+// process.
+///
+CEF_EXPORT void cef_is_web_plugin_unstable(
+    const cef_string_t* path,
+    cef_web_plugin_unstable_callback_t* callback);
+
+///
+// Register the Widevine CDM plugin.
+//
+// The client application is responsible for downloading an appropriate
+// platform-specific CDM binary distribution from Google, extracting the
+// contents, and building the required directory structure on the local machine.
+// The cef_browser_host_t::StartDownload function and CefZipArchive structure
+// can be used to implement this functionality in CEF. Contact Google via
+// https://www.widevine.com/contact.html for details on CDM download.
+//
+// |path| is a directory that must contain the following files:
+//   1. manifest.json file from the CDM binary distribution (see below).
+//   2. widevinecdm file from the CDM binary distribution (e.g.
+//      widevinecdm.dll on on Windows, libwidevinecdm.dylib on OS X,
+//      libwidevinecdm.so on Linux).
+//
+// If any of these files are missing or if the manifest file has incorrect
+// contents the registration will fail and |callback| will receive a |result|
+// value of CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS.
+//
+// The manifest.json file must contain the following keys:
+//   A. "os": Supported OS (e.g. "mac", "win" or "linux").
+//   B. "arch": Supported architecture (e.g. "ia32" or "x64").
+//   C. "x-cdm-module-versions": Module API version (e.g. "4").
+//   D. "x-cdm-interface-versions": Interface API version (e.g. "8").
+//   E. "x-cdm-host-versions": Host API version (e.g. "8").
+//   F. "version": CDM version (e.g. "1.4.8.903").
+//   G. "x-cdm-codecs": List of supported codecs (e.g. "vp8,vp9.0,avc1").
+//
+// A through E are used to verify compatibility with the current Chromium
+// version. If the CDM is not compatible the registration will fail and
+// |callback| will receive a |result| value of
+// CEF_CDM_REGISTRATION_ERROR_INCOMPATIBLE.
+//
+// |callback| will be executed asynchronously once registration is complete.
+//
+// On Linux this function must be called before cef_initialize() and the
+// registration cannot be changed during runtime. If registration is not
+// supported at the time that cef_register_widevine_cdm() is called then
+// |callback| will receive a |result| value of
+// CEF_CDM_REGISTRATION_ERROR_NOT_SUPPORTED.
+///
+CEF_EXPORT void cef_register_widevine_cdm(
+    const cef_string_t* path,
+    cef_register_cdm_callback_t* callback);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_WEB_PLUGIN_CAPI_H_
diff --git a/src/include/capi/cef_x509_certificate_capi.h b/src/include/capi/cef_x509_certificate_capi.h
new file mode 100644
index 0000000..f177f0a
--- /dev/null
+++ b/src/include/capi/cef_x509_certificate_capi.h
@@ -0,0 +1,213 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=2d04c2cc1791b90ddb9333fe830ad07042e9df2d$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_X509_CERTIFICATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_X509_CERTIFICATE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_values_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure representing the issuer or subject field of an X.509 certificate.
+///
+typedef struct _cef_x509cert_principal_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns a name that can be used to represent the issuer. It tries in this
+  // order: Common Name (CN), Organization Name (O) and Organizational Unit Name
+  // (OU) and returns the first non-NULL one found.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_display_name)(
+      struct _cef_x509cert_principal_t* self);
+
+  ///
+  // Returns the common name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_common_name)(
+      struct _cef_x509cert_principal_t* self);
+
+  ///
+  // Returns the locality name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_locality_name)(
+      struct _cef_x509cert_principal_t* self);
+
+  ///
+  // Returns the state or province name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_state_or_province_name)(
+      struct _cef_x509cert_principal_t* self);
+
+  ///
+  // Returns the country name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_country_name)(
+      struct _cef_x509cert_principal_t* self);
+
+  ///
+  // Retrieve the list of street addresses.
+  ///
+  void(CEF_CALLBACK* get_street_addresses)(
+      struct _cef_x509cert_principal_t* self,
+      cef_string_list_t addresses);
+
+  ///
+  // Retrieve the list of organization names.
+  ///
+  void(CEF_CALLBACK* get_organization_names)(
+      struct _cef_x509cert_principal_t* self,
+      cef_string_list_t names);
+
+  ///
+  // Retrieve the list of organization unit names.
+  ///
+  void(CEF_CALLBACK* get_organization_unit_names)(
+      struct _cef_x509cert_principal_t* self,
+      cef_string_list_t names);
+
+  ///
+  // Retrieve the list of domain components.
+  ///
+  void(CEF_CALLBACK* get_domain_components)(
+      struct _cef_x509cert_principal_t* self,
+      cef_string_list_t components);
+} cef_x509cert_principal_t;
+
+///
+// Structure representing a X.509 certificate.
+///
+typedef struct _cef_x509certificate_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the subject of the X.509 certificate. For HTTPS server certificates
+  // this represents the web server.  The common name of the subject should
+  // match the host name of the web server.
+  ///
+  struct _cef_x509cert_principal_t*(CEF_CALLBACK* get_subject)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the issuer of the X.509 certificate.
+  ///
+  struct _cef_x509cert_principal_t*(CEF_CALLBACK* get_issuer)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the DER encoded serial number for the X.509 certificate. The value
+  // possibly includes a leading 00 byte.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_serial_number)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the date before which the X.509 certificate is invalid.
+  // CefTime.GetTimeT() will return 0 if no date was specified.
+  ///
+  cef_time_t(CEF_CALLBACK* get_valid_start)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the date after which the X.509 certificate is invalid.
+  // CefTime.GetTimeT() will return 0 if no date was specified.
+  ///
+  cef_time_t(CEF_CALLBACK* get_valid_expiry)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the DER encoded data for the X.509 certificate.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_derencoded)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the PEM encoded data for the X.509 certificate.
+  ///
+  struct _cef_binary_value_t*(CEF_CALLBACK* get_pemencoded)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the number of certificates in the issuer chain. If 0, the
+  // certificate is self-signed.
+  ///
+  size_t(CEF_CALLBACK* get_issuer_chain_size)(
+      struct _cef_x509certificate_t* self);
+
+  ///
+  // Returns the DER encoded data for the certificate issuer chain. If we failed
+  // to encode a certificate in the chain it is still present in the array but
+  // is an NULL string.
+  ///
+  void(CEF_CALLBACK* get_derencoded_issuer_chain)(
+      struct _cef_x509certificate_t* self,
+      size_t* chainCount,
+      struct _cef_binary_value_t** chain);
+
+  ///
+  // Returns the PEM encoded data for the certificate issuer chain. If we failed
+  // to encode a certificate in the chain it is still present in the array but
+  // is an NULL string.
+  ///
+  void(CEF_CALLBACK* get_pemencoded_issuer_chain)(
+      struct _cef_x509certificate_t* self,
+      size_t* chainCount,
+      struct _cef_binary_value_t** chain);
+} cef_x509certificate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_X509_CERTIFICATE_CAPI_H_
diff --git a/src/include/capi/cef_xml_reader_capi.h b/src/include/capi/cef_xml_reader_capi.h
new file mode 100644
index 0000000..84a4554
--- /dev/null
+++ b/src/include/capi/cef_xml_reader_capi.h
@@ -0,0 +1,282 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=64f6b6477ec81b1d64517cf0af2e3b2121ff39bd$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_XML_READER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_XML_READER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_stream_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure that supports the reading of XML data via the libxml streaming API.
+// The functions of this structure should only be called on the thread that
+// creates the object.
+///
+typedef struct _cef_xml_reader_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Moves the cursor to the next node in the document. This function must be
+  // called at least once to set the current cursor position. Returns true (1)
+  // if the cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_next_node)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Close the document. This should be called directly to ensure that cleanup
+  // occurs on the correct thread.
+  ///
+  int(CEF_CALLBACK* close)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns true (1) if an error has been reported by the XML parser.
+  ///
+  int(CEF_CALLBACK* has_error)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the error string.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_error)(
+      struct _cef_xml_reader_t* self);
+
+  // The below functions retrieve data for the node at the current cursor
+  // position.
+
+  ///
+  // Returns the node type.
+  ///
+  cef_xml_node_type_t(CEF_CALLBACK* get_type)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the node depth. Depth starts at 0 for the root node.
+  ///
+  int(CEF_CALLBACK* get_depth)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the local name. See http://www.w3.org/TR/REC-xml-names/#NT-
+  // LocalPart for additional details.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_local_name)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the namespace prefix. See http://www.w3.org/TR/REC-xml-names/ for
+  // additional details.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_prefix)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the qualified name, equal to (Prefix:)LocalName. See
+  // http://www.w3.org/TR/REC-xml-names/#ns-qualnames for additional details.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_qualified_name)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the URI defining the namespace associated with the node. See
+  // http://www.w3.org/TR/REC-xml-names/ for additional details.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_namespace_uri)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the base URI of the node. See http://www.w3.org/TR/xmlbase/ for
+  // additional details.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_base_uri)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the xml:lang scope within which the node resides. See
+  // http://www.w3.org/TR/REC-xml/#sec-lang-tag for additional details.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_xml_lang)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns true (1) if the node represents an NULL element. <a/> is considered
+  // NULL but <a></a> is not.
+  ///
+  int(CEF_CALLBACK* is_empty_element)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns true (1) if the node has a text value.
+  ///
+  int(CEF_CALLBACK* has_value)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the text value.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_value)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns true (1) if the node has attributes.
+  ///
+  int(CEF_CALLBACK* has_attributes)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the number of attributes.
+  ///
+  size_t(CEF_CALLBACK* get_attribute_count)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the value of the attribute at the specified 0-based index.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_attribute_byindex)(
+      struct _cef_xml_reader_t* self,
+      int index);
+
+  ///
+  // Returns the value of the attribute with the specified qualified name.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_attribute_byqname)(
+      struct _cef_xml_reader_t* self,
+      const cef_string_t* qualifiedName);
+
+  ///
+  // Returns the value of the attribute with the specified local name and
+  // namespace URI.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_attribute_bylname)(
+      struct _cef_xml_reader_t* self,
+      const cef_string_t* localName,
+      const cef_string_t* namespaceURI);
+
+  ///
+  // Returns an XML representation of the current node's children.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_inner_xml)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns an XML representation of the current node including its children.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_outer_xml)(
+      struct _cef_xml_reader_t* self);
+
+  ///
+  // Returns the line number for the current node.
+  ///
+  int(CEF_CALLBACK* get_line_number)(struct _cef_xml_reader_t* self);
+
+  // Attribute nodes are not traversed by default. The below functions can be
+  // used to move the cursor to an attribute node. move_to_carrying_element()
+  // can be called afterwards to return the cursor to the carrying element. The
+  // depth of an attribute node will be 1 + the depth of the carrying element.
+
+  ///
+  // Moves the cursor to the attribute at the specified 0-based index. Returns
+  // true (1) if the cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_attribute_byindex)(struct _cef_xml_reader_t* self,
+                                               int index);
+
+  ///
+  // Moves the cursor to the attribute with the specified qualified name.
+  // Returns true (1) if the cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_attribute_byqname)(
+      struct _cef_xml_reader_t* self,
+      const cef_string_t* qualifiedName);
+
+  ///
+  // Moves the cursor to the attribute with the specified local name and
+  // namespace URI. Returns true (1) if the cursor position was set
+  // successfully.
+  ///
+  int(CEF_CALLBACK* move_to_attribute_bylname)(
+      struct _cef_xml_reader_t* self,
+      const cef_string_t* localName,
+      const cef_string_t* namespaceURI);
+
+  ///
+  // Moves the cursor to the first attribute in the current element. Returns
+  // true (1) if the cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_first_attribute)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Moves the cursor to the next attribute in the current element. Returns true
+  // (1) if the cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_next_attribute)(struct _cef_xml_reader_t* self);
+
+  ///
+  // Moves the cursor back to the carrying element. Returns true (1) if the
+  // cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_carrying_element)(struct _cef_xml_reader_t* self);
+} cef_xml_reader_t;
+
+///
+// Create a new cef_xml_reader_t object. The returned object's functions can
+// only be called from the thread that created the object.
+///
+CEF_EXPORT cef_xml_reader_t* cef_xml_reader_create(
+    struct _cef_stream_reader_t* stream,
+    cef_xml_encoding_type_t encodingType,
+    const cef_string_t* URI);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_XML_READER_CAPI_H_
diff --git a/src/include/capi/cef_zip_reader_capi.h b/src/include/capi/cef_zip_reader_capi.h
new file mode 100644
index 0000000..8138559
--- /dev/null
+++ b/src/include/capi/cef_zip_reader_capi.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=f8b7ec1654c7d62153e2670b52ed18eb4c9c58d5$
+//
+
+#ifndef CEF_INCLUDE_CAPI_CEF_ZIP_READER_CAPI_H_
+#define CEF_INCLUDE_CAPI_CEF_ZIP_READER_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/capi/cef_stream_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure that supports the reading of zip archives via the zlib unzip API.
+// The functions of this structure should only be called on the thread that
+// creates the object.
+///
+typedef struct _cef_zip_reader_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Moves the cursor to the first file in the archive. Returns true (1) if the
+  // cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_first_file)(struct _cef_zip_reader_t* self);
+
+  ///
+  // Moves the cursor to the next file in the archive. Returns true (1) if the
+  // cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_next_file)(struct _cef_zip_reader_t* self);
+
+  ///
+  // Moves the cursor to the specified file in the archive. If |caseSensitive|
+  // is true (1) then the search will be case sensitive. Returns true (1) if the
+  // cursor position was set successfully.
+  ///
+  int(CEF_CALLBACK* move_to_file)(struct _cef_zip_reader_t* self,
+                                  const cef_string_t* fileName,
+                                  int caseSensitive);
+
+  ///
+  // Closes the archive. This should be called directly to ensure that cleanup
+  // occurs on the correct thread.
+  ///
+  int(CEF_CALLBACK* close)(struct _cef_zip_reader_t* self);
+
+  // The below functions act on the file at the current cursor position.
+
+  ///
+  // Returns the name of the file.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_file_name)(
+      struct _cef_zip_reader_t* self);
+
+  ///
+  // Returns the uncompressed size of the file.
+  ///
+  int64(CEF_CALLBACK* get_file_size)(struct _cef_zip_reader_t* self);
+
+  ///
+  // Returns the last modified timestamp for the file.
+  ///
+  cef_time_t(CEF_CALLBACK* get_file_last_modified)(
+      struct _cef_zip_reader_t* self);
+
+  ///
+  // Opens the file for reading of uncompressed data. A read password may
+  // optionally be specified.
+  ///
+  int(CEF_CALLBACK* open_file)(struct _cef_zip_reader_t* self,
+                               const cef_string_t* password);
+
+  ///
+  // Closes the file.
+  ///
+  int(CEF_CALLBACK* close_file)(struct _cef_zip_reader_t* self);
+
+  ///
+  // Read uncompressed file contents into the specified buffer. Returns < 0 if
+  // an error occurred, 0 if at the end of file, or the number of bytes read.
+  ///
+  int(CEF_CALLBACK* read_file)(struct _cef_zip_reader_t* self,
+                               void* buffer,
+                               size_t bufferSize);
+
+  ///
+  // Returns the current offset in the uncompressed file contents.
+  ///
+  int64(CEF_CALLBACK* tell)(struct _cef_zip_reader_t* self);
+
+  ///
+  // Returns true (1) if at end of the file contents.
+  ///
+  int(CEF_CALLBACK* eof)(struct _cef_zip_reader_t* self);
+} cef_zip_reader_t;
+
+///
+// Create a new cef_zip_reader_t object. The returned object's functions can
+// only be called from the thread that created the object.
+///
+CEF_EXPORT cef_zip_reader_t* cef_zip_reader_create(
+    struct _cef_stream_reader_t* stream);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_CEF_ZIP_READER_CAPI_H_
diff --git a/src/include/capi/test/cef_test_helpers_capi.h b/src/include/capi/test/cef_test_helpers_capi.h
new file mode 100644
index 0000000..51d808c
--- /dev/null
+++ b/src/include/capi/test/cef_test_helpers_capi.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=44811580a3bddb5efed7d697ef8fa0b7e0925ef2$
+//
+
+#ifndef CEF_INCLUDE_CAPI_TEST_CEF_TEST_HELPERS_CAPI_H_
+#define CEF_INCLUDE_CAPI_TEST_CEF_TEST_HELPERS_CAPI_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \
+    !defined(UNIT_TEST)
+#error This file can be included for unit tests only
+#endif
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Execute JavaScript with a user gesture to trigger functionality like
+// onbeforeunload handlers that will otherwise be blocked.
+///
+CEF_EXPORT void cef_execute_java_script_with_user_gesture_for_tests(
+    struct _cef_frame_t* frame,
+    const cef_string_t* javascript);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_TEST_CEF_TEST_HELPERS_CAPI_H_
diff --git a/src/include/capi/test/cef_translator_test_capi.h b/src/include/capi/test/cef_translator_test_capi.h
new file mode 100644
index 0000000..42a9b6e
--- /dev/null
+++ b/src/include/capi/test/cef_translator_test_capi.h
@@ -0,0 +1,781 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=a5d8033127cf2d21f1cb0c87f76d2d59ec3eace0$
+//
+
+#ifndef CEF_INCLUDE_CAPI_TEST_CEF_TRANSLATOR_TEST_CAPI_H_
+#define CEF_INCLUDE_CAPI_TEST_CEF_TRANSLATOR_TEST_CAPI_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \
+    !defined(UNIT_TEST)
+#error This file can be included for unit tests only
+#endif
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_translator_test_ref_ptr_client_child_t;
+struct _cef_translator_test_ref_ptr_client_t;
+struct _cef_translator_test_ref_ptr_library_child_t;
+struct _cef_translator_test_ref_ptr_library_t;
+struct _cef_translator_test_scoped_client_child_t;
+struct _cef_translator_test_scoped_client_t;
+struct _cef_translator_test_scoped_library_child_t;
+struct _cef_translator_test_scoped_library_t;
+
+///
+// Structure for testing all of the possible data transfer types.
+///
+typedef struct _cef_translator_test_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  // PRIMITIVE VALUES
+
+  ///
+  // Return a void value.
+  ///
+  void(CEF_CALLBACK* get_void)(struct _cef_translator_test_t* self);
+
+  ///
+  // Return a bool value.
+  ///
+  int(CEF_CALLBACK* get_bool)(struct _cef_translator_test_t* self);
+
+  ///
+  // Return an int value.
+  ///
+  int(CEF_CALLBACK* get_int)(struct _cef_translator_test_t* self);
+
+  ///
+  // Return a double value.
+  ///
+  double(CEF_CALLBACK* get_double)(struct _cef_translator_test_t* self);
+
+  ///
+  // Return a long value.
+  ///
+  long(CEF_CALLBACK* get_long)(struct _cef_translator_test_t* self);
+
+  ///
+  // Return a size_t value.
+  ///
+  size_t(CEF_CALLBACK* get_sizet)(struct _cef_translator_test_t* self);
+
+  ///
+  // Set a void value.
+  ///
+  int(CEF_CALLBACK* set_void)(struct _cef_translator_test_t* self);
+
+  ///
+  // Set a bool value.
+  ///
+  int(CEF_CALLBACK* set_bool)(struct _cef_translator_test_t* self, int val);
+
+  ///
+  // Set an int value.
+  ///
+  int(CEF_CALLBACK* set_int)(struct _cef_translator_test_t* self, int val);
+
+  ///
+  // Set a double value.
+  ///
+  int(CEF_CALLBACK* set_double)(struct _cef_translator_test_t* self,
+                                double val);
+
+  ///
+  // Set a long value.
+  ///
+  int(CEF_CALLBACK* set_long)(struct _cef_translator_test_t* self, long val);
+
+  ///
+  // Set a size_t value.
+  ///
+  int(CEF_CALLBACK* set_sizet)(struct _cef_translator_test_t* self, size_t val);
+
+  ///
+  // Set a int list value.
+  ///
+  int(CEF_CALLBACK* set_int_list)(struct _cef_translator_test_t* self,
+                                  size_t valCount,
+                                  int const* val);
+
+  ///
+  // Return an int list value by out-param.
+  ///
+  int(CEF_CALLBACK* get_int_list_by_ref)(struct _cef_translator_test_t* self,
+                                         size_t* valCount,
+                                         int* val);
+
+  ///
+  // Return the number of points that will be output above.
+  ///
+  size_t(CEF_CALLBACK* get_int_list_size)(struct _cef_translator_test_t* self);
+
+  // STRING VALUES
+
+  ///
+  // Return a string value.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_string)(
+      struct _cef_translator_test_t* self);
+
+  ///
+  // Set a string value.
+  ///
+  int(CEF_CALLBACK* set_string)(struct _cef_translator_test_t* self,
+                                const cef_string_t* val);
+
+  ///
+  // Return a string value by out-param.
+  ///
+  void(CEF_CALLBACK* get_string_by_ref)(struct _cef_translator_test_t* self,
+                                        cef_string_t* val);
+
+  ///
+  // Set a string list value.
+  ///
+  int(CEF_CALLBACK* set_string_list)(struct _cef_translator_test_t* self,
+                                     cef_string_list_t val);
+
+  ///
+  // Return a string list value by out-param.
+  ///
+  int(CEF_CALLBACK* get_string_list_by_ref)(struct _cef_translator_test_t* self,
+                                            cef_string_list_t val);
+
+  ///
+  // Set a string map value.
+  ///
+  int(CEF_CALLBACK* set_string_map)(struct _cef_translator_test_t* self,
+                                    cef_string_map_t val);
+
+  ///
+  // Return a string map value by out-param.
+  ///
+  int(CEF_CALLBACK* get_string_map_by_ref)(struct _cef_translator_test_t* self,
+                                           cef_string_map_t val);
+
+  ///
+  // Set a string multimap value.
+  ///
+  int(CEF_CALLBACK* set_string_multimap)(struct _cef_translator_test_t* self,
+                                         cef_string_multimap_t val);
+
+  ///
+  // Return a string multimap value by out-param.
+  ///
+  int(CEF_CALLBACK* get_string_multimap_by_ref)(
+      struct _cef_translator_test_t* self,
+      cef_string_multimap_t val);
+
+  // STRUCT VALUES
+
+  ///
+  // Return a point value.
+  ///
+  cef_point_t(CEF_CALLBACK* get_point)(struct _cef_translator_test_t* self);
+
+  ///
+  // Set a point value.
+  ///
+  int(CEF_CALLBACK* set_point)(struct _cef_translator_test_t* self,
+                               const cef_point_t* val);
+
+  ///
+  // Return a point value by out-param.
+  ///
+  void(CEF_CALLBACK* get_point_by_ref)(struct _cef_translator_test_t* self,
+                                       cef_point_t* val);
+
+  ///
+  // Set a point list vlaue.
+  ///
+  int(CEF_CALLBACK* set_point_list)(struct _cef_translator_test_t* self,
+                                    size_t valCount,
+                                    cef_point_t const* val);
+
+  ///
+  // Return a point list value by out-param.
+  ///
+  int(CEF_CALLBACK* get_point_list_by_ref)(struct _cef_translator_test_t* self,
+                                           size_t* valCount,
+                                           cef_point_t* val);
+
+  ///
+  // Return the number of points that will be output above.
+  ///
+  size_t(CEF_CALLBACK* get_point_list_size)(
+      struct _cef_translator_test_t* self);
+
+  // LIBRARY-SIDE REFPTR VALUES
+
+  ///
+  // Return an new library-side object.
+  ///
+  struct _cef_translator_test_ref_ptr_library_t*(
+      CEF_CALLBACK* get_ref_ptr_library)(struct _cef_translator_test_t* self,
+                                         int val);
+
+  ///
+  // Set an object. Returns the value from
+  // cef_translator_test_ref_ptr_library_t::get_value(). This tests input and
+  // execution of a library-side object type.
+  ///
+  int(CEF_CALLBACK* set_ref_ptr_library)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_library_t* val);
+
+  ///
+  // Set an object. Returns the object passed in. This tests input and output of
+  // a library-side object type.
+  ///
+  struct _cef_translator_test_ref_ptr_library_t*(
+      CEF_CALLBACK* set_ref_ptr_library_and_return)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_library_t* val);
+
+  ///
+  // Set a child object. Returns the value from
+  // cef_translator_test_ref_ptr_library_t::get_value(). This tests input of a
+  // library- side child object type and execution as the parent type.
+  ///
+  int(CEF_CALLBACK* set_child_ref_ptr_library)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_library_child_t* val);
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests input
+  // of a library-side child object type and return as the parent type.
+  ///
+  struct _cef_translator_test_ref_ptr_library_t*(
+      CEF_CALLBACK* set_child_ref_ptr_library_and_return_parent)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_library_child_t* val);
+
+  ///
+  // Set an object list vlaue.
+  ///
+  int(CEF_CALLBACK* set_ref_ptr_library_list)(
+      struct _cef_translator_test_t* self,
+      size_t valCount,
+      struct _cef_translator_test_ref_ptr_library_t* const* val,
+      int val1,
+      int val2);
+
+  ///
+  // Return an object list value by out-param.
+  ///
+  int(CEF_CALLBACK* get_ref_ptr_library_list_by_ref)(
+      struct _cef_translator_test_t* self,
+      size_t* valCount,
+      struct _cef_translator_test_ref_ptr_library_t** val,
+      int val1,
+      int val2);
+
+  ///
+  // Return the number of object that will be output above.
+  ///
+  size_t(CEF_CALLBACK* get_ref_ptr_library_list_size)(
+      struct _cef_translator_test_t* self);
+
+  // CLIENT-SIDE REFPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // cef_translator_test_ref_ptr_client_t::get_value(). This tests input and
+  // execution of a client-side object type.
+  ///
+  int(CEF_CALLBACK* set_ref_ptr_client)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_client_t* val);
+
+  ///
+  // Set an object. Returns the handler passed in. This tests input and output
+  // of a client-side object type.
+  ///
+  struct _cef_translator_test_ref_ptr_client_t*(
+      CEF_CALLBACK* set_ref_ptr_client_and_return)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_client_t* val);
+
+  ///
+  // Set a child object. Returns the value from
+  // cef_translator_test_ref_ptr_client_t::get_value(). This tests input of a
+  // client- side child object type and execution as the parent type.
+  ///
+  int(CEF_CALLBACK* set_child_ref_ptr_client)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_client_child_t* val);
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests input
+  // of a client-side child object type and return as the parent type.
+  ///
+  struct _cef_translator_test_ref_ptr_client_t*(
+      CEF_CALLBACK* set_child_ref_ptr_client_and_return_parent)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_ref_ptr_client_child_t* val);
+
+  ///
+  // Set an object list vlaue.
+  ///
+  int(CEF_CALLBACK* set_ref_ptr_client_list)(
+      struct _cef_translator_test_t* self,
+      size_t valCount,
+      struct _cef_translator_test_ref_ptr_client_t* const* val,
+      int val1,
+      int val2);
+
+  ///
+  // Return an object list value by out-param.
+  ///
+  int(CEF_CALLBACK* get_ref_ptr_client_list_by_ref)(
+      struct _cef_translator_test_t* self,
+      size_t* valCount,
+      struct _cef_translator_test_ref_ptr_client_t** val,
+      struct _cef_translator_test_ref_ptr_client_t* val1,
+      struct _cef_translator_test_ref_ptr_client_t* val2);
+
+  ///
+  // Return the number of object that will be output above.
+  ///
+  size_t(CEF_CALLBACK* get_ref_ptr_client_list_size)(
+      struct _cef_translator_test_t* self);
+
+  // LIBRARY-SIDE OWNPTR VALUES
+
+  ///
+  // Return an new library-side object.
+  ///
+  struct _cef_translator_test_scoped_library_t*(
+      CEF_CALLBACK* get_own_ptr_library)(struct _cef_translator_test_t* self,
+                                         int val);
+
+  ///
+  // Set an object. Returns the value from
+  // cef_translator_test_scoped_library_t::get_value(). This tests input and
+  // execution of a library-side object type.
+  ///
+  int(CEF_CALLBACK* set_own_ptr_library)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_library_t* val);
+
+  ///
+  // Set an object. Returns the object passed in. This tests input and output of
+  // a library-side object type.
+  ///
+  struct _cef_translator_test_scoped_library_t*(
+      CEF_CALLBACK* set_own_ptr_library_and_return)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_library_t* val);
+
+  ///
+  // Set a child object. Returns the value from
+  // cef_translator_test_scoped_library_t::get_value(). This tests input of a
+  // library- side child object type and execution as the parent type.
+  ///
+  int(CEF_CALLBACK* set_child_own_ptr_library)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_library_child_t* val);
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests input
+  // of a library-side child object type and return as the parent type.
+  ///
+  struct _cef_translator_test_scoped_library_t*(
+      CEF_CALLBACK* set_child_own_ptr_library_and_return_parent)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_library_child_t* val);
+
+  // CLIENT-SIDE OWNPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // cef_translator_test_scoped_client_t::get_value(). This tests input and
+  // execution of a client-side object type.
+  ///
+  int(CEF_CALLBACK* set_own_ptr_client)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_client_t* val);
+
+  ///
+  // Set an object. Returns the handler passed in. This tests input and output
+  // of a client-side object type.
+  ///
+  struct _cef_translator_test_scoped_client_t*(
+      CEF_CALLBACK* set_own_ptr_client_and_return)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_client_t* val);
+
+  ///
+  // Set a child object. Returns the value from
+  // cef_translator_test_scoped_client_t::get_value(). This tests input of a
+  // client- side child object type and execution as the parent type.
+  ///
+  int(CEF_CALLBACK* set_child_own_ptr_client)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_client_child_t* val);
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests input
+  // of a client-side child object type and return as the parent type.
+  ///
+  struct _cef_translator_test_scoped_client_t*(
+      CEF_CALLBACK* set_child_own_ptr_client_and_return_parent)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_client_child_t* val);
+
+  // LIBRARY-SIDE RAWPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // cef_translator_test_scoped_library_t::get_value(). This tests input and
+  // execution of a library-side object type.
+  ///
+  int(CEF_CALLBACK* set_raw_ptr_library)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_library_t* val);
+
+  ///
+  // Set a child object. Returns the value from
+  // cef_translator_test_scoped_library_t::get_value(). This tests input of a
+  // library- side child object type and execution as the parent type.
+  ///
+  int(CEF_CALLBACK* set_child_raw_ptr_library)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_library_child_t* val);
+
+  ///
+  // Set an object list vlaue.
+  ///
+  int(CEF_CALLBACK* set_raw_ptr_library_list)(
+      struct _cef_translator_test_t* self,
+      size_t valCount,
+      struct _cef_translator_test_scoped_library_t* const* val,
+      int val1,
+      int val2);
+
+  // CLIENT-SIDE RAWPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // cef_translator_test_scoped_client_t::get_value(). This tests input and
+  // execution of a client-side object type.
+  ///
+  int(CEF_CALLBACK* set_raw_ptr_client)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_client_t* val);
+
+  ///
+  // Set a child object. Returns the value from
+  // cef_translator_test_scoped_client_t::get_value(). This tests input of a
+  // client- side child object type and execution as the parent type.
+  ///
+  int(CEF_CALLBACK* set_child_raw_ptr_client)(
+      struct _cef_translator_test_t* self,
+      struct _cef_translator_test_scoped_client_child_t* val);
+
+  ///
+  // Set an object list vlaue.
+  ///
+  int(CEF_CALLBACK* set_raw_ptr_client_list)(
+      struct _cef_translator_test_t* self,
+      size_t valCount,
+      struct _cef_translator_test_scoped_client_t* const* val,
+      int val1,
+      int val2);
+} cef_translator_test_t;
+
+///
+// Create the test object.
+///
+CEF_EXPORT cef_translator_test_t* cef_translator_test_create();
+
+///
+// Library-side test object for RefPtr.
+///
+typedef struct _cef_translator_test_ref_ptr_library_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_value)(
+      struct _cef_translator_test_ref_ptr_library_t* self);
+
+  ///
+  // Set a value.
+  ///
+  void(CEF_CALLBACK* set_value)(
+      struct _cef_translator_test_ref_ptr_library_t* self,
+      int value);
+} cef_translator_test_ref_ptr_library_t;
+
+///
+// Create the test object.
+///
+CEF_EXPORT cef_translator_test_ref_ptr_library_t*
+cef_translator_test_ref_ptr_library_create(int value);
+
+///
+// Library-side child test object for RefPtr.
+///
+typedef struct _cef_translator_test_ref_ptr_library_child_t {
+  ///
+  // Base structure.
+  ///
+  cef_translator_test_ref_ptr_library_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_other_value)(
+      struct _cef_translator_test_ref_ptr_library_child_t* self);
+
+  ///
+  // Set a value.
+  ///
+  void(CEF_CALLBACK* set_other_value)(
+      struct _cef_translator_test_ref_ptr_library_child_t* self,
+      int value);
+} cef_translator_test_ref_ptr_library_child_t;
+
+///
+// Create the test object.
+///
+CEF_EXPORT cef_translator_test_ref_ptr_library_child_t*
+cef_translator_test_ref_ptr_library_child_create(int value, int other_value);
+
+///
+// Another library-side child test object for RefPtr.
+///
+typedef struct _cef_translator_test_ref_ptr_library_child_child_t {
+  ///
+  // Base structure.
+  ///
+  cef_translator_test_ref_ptr_library_child_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_other_other_value)(
+      struct _cef_translator_test_ref_ptr_library_child_child_t* self);
+
+  ///
+  // Set a value.
+  ///
+  void(CEF_CALLBACK* set_other_other_value)(
+      struct _cef_translator_test_ref_ptr_library_child_child_t* self,
+      int value);
+} cef_translator_test_ref_ptr_library_child_child_t;
+
+///
+// Create the test object.
+///
+CEF_EXPORT cef_translator_test_ref_ptr_library_child_child_t*
+cef_translator_test_ref_ptr_library_child_child_create(int value,
+                                                       int other_value,
+                                                       int other_other_value);
+
+///
+// Client-side test object for RefPtr.
+///
+typedef struct _cef_translator_test_ref_ptr_client_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_value)(
+      struct _cef_translator_test_ref_ptr_client_t* self);
+} cef_translator_test_ref_ptr_client_t;
+
+///
+// Client-side child test object for RefPtr.
+///
+typedef struct _cef_translator_test_ref_ptr_client_child_t {
+  ///
+  // Base structure.
+  ///
+  cef_translator_test_ref_ptr_client_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_other_value)(
+      struct _cef_translator_test_ref_ptr_client_child_t* self);
+} cef_translator_test_ref_ptr_client_child_t;
+
+///
+// Library-side test object for OwnPtr/RawPtr.
+///
+typedef struct _cef_translator_test_scoped_library_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_scoped_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_value)(
+      struct _cef_translator_test_scoped_library_t* self);
+
+  ///
+  // Set a value.
+  ///
+  void(CEF_CALLBACK* set_value)(
+      struct _cef_translator_test_scoped_library_t* self,
+      int value);
+} cef_translator_test_scoped_library_t;
+
+///
+// Create the test object.
+///
+CEF_EXPORT cef_translator_test_scoped_library_t*
+cef_translator_test_scoped_library_create(int value);
+
+///
+// Library-side child test object for OwnPtr/RawPtr.
+///
+typedef struct _cef_translator_test_scoped_library_child_t {
+  ///
+  // Base structure.
+  ///
+  cef_translator_test_scoped_library_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_other_value)(
+      struct _cef_translator_test_scoped_library_child_t* self);
+
+  ///
+  // Set a value.
+  ///
+  void(CEF_CALLBACK* set_other_value)(
+      struct _cef_translator_test_scoped_library_child_t* self,
+      int value);
+} cef_translator_test_scoped_library_child_t;
+
+///
+// Create the test object.
+///
+CEF_EXPORT cef_translator_test_scoped_library_child_t*
+cef_translator_test_scoped_library_child_create(int value, int other_value);
+
+///
+// Another library-side child test object for OwnPtr/RawPtr.
+///
+typedef struct _cef_translator_test_scoped_library_child_child_t {
+  ///
+  // Base structure.
+  ///
+  cef_translator_test_scoped_library_child_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_other_other_value)(
+      struct _cef_translator_test_scoped_library_child_child_t* self);
+
+  ///
+  // Set a value.
+  ///
+  void(CEF_CALLBACK* set_other_other_value)(
+      struct _cef_translator_test_scoped_library_child_child_t* self,
+      int value);
+} cef_translator_test_scoped_library_child_child_t;
+
+///
+// Create the test object.
+///
+CEF_EXPORT cef_translator_test_scoped_library_child_child_t*
+cef_translator_test_scoped_library_child_child_create(int value,
+                                                      int other_value,
+                                                      int other_other_value);
+
+///
+// Client-side test object for OwnPtr/RawPtr.
+///
+typedef struct _cef_translator_test_scoped_client_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_scoped_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_value)(
+      struct _cef_translator_test_scoped_client_t* self);
+} cef_translator_test_scoped_client_t;
+
+///
+// Client-side child test object for OwnPtr/RawPtr.
+///
+typedef struct _cef_translator_test_scoped_client_child_t {
+  ///
+  // Base structure.
+  ///
+  cef_translator_test_scoped_client_t base;
+
+  ///
+  // Return a value.
+  ///
+  int(CEF_CALLBACK* get_other_value)(
+      struct _cef_translator_test_scoped_client_child_t* self);
+} cef_translator_test_scoped_client_child_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_TEST_CEF_TRANSLATOR_TEST_CAPI_H_
diff --git a/src/include/capi/views/cef_box_layout_capi.h b/src/include/capi/views/cef_box_layout_capi.h
new file mode 100644
index 0000000..8328d67
--- /dev/null
+++ b/src/include/capi/views/cef_box_layout_capi.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=ddf96505520b7f8df0662d9f66221917a665b029$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_BOX_LAYOUT_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_BOX_LAYOUT_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_layout_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_view_t;
+
+///
+// A Layout manager that arranges child views vertically or horizontally in a
+// side-by-side fashion with spacing around and between the child views. The
+// child views are always sized according to their preferred size. If the host's
+// bounds provide insufficient space, child views will be clamped. Excess space
+// will not be distributed. Methods must be called on the browser process UI
+// thread unless otherwise indicated.
+///
+typedef struct _cef_box_layout_t {
+  ///
+  // Base structure.
+  ///
+  cef_layout_t base;
+
+  ///
+  // Set the flex weight for the given |view|. Using the preferred size as the
+  // basis, free space along the main axis is distributed to views in the ratio
+  // of their flex weights. Similarly, if the views will overflow the parent,
+  // space is subtracted in these ratios. A flex of 0 means this view is not
+  // resized. Flex values must not be negative.
+  ///
+  void(CEF_CALLBACK* set_flex_for_view)(struct _cef_box_layout_t* self,
+                                        struct _cef_view_t* view,
+                                        int flex);
+
+  ///
+  // Clears the flex for the given |view|, causing it to use the default flex
+  // specified via cef_box_layout_tSettings.default_flex.
+  ///
+  void(CEF_CALLBACK* clear_flex_for_view)(struct _cef_box_layout_t* self,
+                                          struct _cef_view_t* view);
+} cef_box_layout_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_BOX_LAYOUT_CAPI_H_
diff --git a/src/include/capi/views/cef_browser_view_capi.h b/src/include/capi/views/cef_browser_view_capi.h
new file mode 100644
index 0000000..f597c87
--- /dev/null
+++ b/src/include/capi/views/cef_browser_view_capi.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3ac114f5bd9aa0e6d62e7540e9205adce485a09d$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_BROWSER_VIEW_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_BROWSER_VIEW_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/views/cef_browser_view_delegate_capi.h"
+#include "include/capi/views/cef_view_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// A View hosting a cef_browser_t instance. Methods must be called on the
+// browser process UI thread unless otherwise indicated.
+///
+typedef struct _cef_browser_view_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_t base;
+
+  ///
+  // Returns the cef_browser_t hosted by this BrowserView. Will return NULL if
+  // the browser has not yet been created or has already been destroyed.
+  ///
+  struct _cef_browser_t*(CEF_CALLBACK* get_browser)(
+      struct _cef_browser_view_t* self);
+
+  ///
+  // Sets whether accelerators registered with cef_window_t::SetAccelerator are
+  // triggered before or after the event is sent to the cef_browser_t. If
+  // |prefer_accelerators| is true (1) then the matching accelerator will be
+  // triggered immediately and the event will not be sent to the cef_browser_t.
+  // If |prefer_accelerators| is false (0) then the matching accelerator will
+  // only be triggered if the event is not handled by web content or by
+  // cef_keyboard_handler_t. The default value is false (0).
+  ///
+  void(CEF_CALLBACK* set_prefer_accelerators)(struct _cef_browser_view_t* self,
+                                              int prefer_accelerators);
+} cef_browser_view_t;
+
+///
+// Create a new BrowserView. The underlying cef_browser_t will not be created
+// until this view is added to the views hierarchy. The optional |extra_info|
+// parameter provides an opportunity to specify extra information specific to
+// the created browser that will be passed to
+// cef_render_process_handler_t::on_browser_created() in the render process.
+///
+CEF_EXPORT cef_browser_view_t* cef_browser_view_create(
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context,
+    struct _cef_browser_view_delegate_t* delegate);
+
+///
+// Returns the BrowserView associated with |browser|.
+///
+CEF_EXPORT cef_browser_view_t* cef_browser_view_get_for_browser(
+    struct _cef_browser_t* browser);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_BROWSER_VIEW_CAPI_H_
diff --git a/src/include/capi/views/cef_browser_view_delegate_capi.h b/src/include/capi/views/cef_browser_view_delegate_capi.h
new file mode 100644
index 0000000..d707169
--- /dev/null
+++ b/src/include/capi/views/cef_browser_view_delegate_capi.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=f850005cb52b08b69b803fc020c77fc7f623839c$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_BROWSER_VIEW_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_BROWSER_VIEW_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_client_capi.h"
+#include "include/capi/views/cef_view_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_browser_t;
+struct _cef_browser_view_t;
+
+///
+// Implement this structure to handle BrowserView events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_browser_view_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_delegate_t base;
+
+  ///
+  // Called when |browser| associated with |browser_view| is created. This
+  // function will be called after cef_life_span_handler_t::on_after_created()
+  // is called for |browser| and before on_popup_browser_view_created() is
+  // called for |browser|'s parent delegate if |browser| is a popup.
+  ///
+  void(CEF_CALLBACK* on_browser_created)(
+      struct _cef_browser_view_delegate_t* self,
+      struct _cef_browser_view_t* browser_view,
+      struct _cef_browser_t* browser);
+
+  ///
+  // Called when |browser| associated with |browser_view| is destroyed. Release
+  // all references to |browser| and do not attempt to execute any functions on
+  // |browser| after this callback returns. This function will be called before
+  // cef_life_span_handler_t::on_before_close() is called for |browser|.
+  ///
+  void(CEF_CALLBACK* on_browser_destroyed)(
+      struct _cef_browser_view_delegate_t* self,
+      struct _cef_browser_view_t* browser_view,
+      struct _cef_browser_t* browser);
+
+  ///
+  // Called before a new popup BrowserView is created. The popup originated from
+  // |browser_view|. |settings| and |client| are the values returned from
+  // cef_life_span_handler_t::on_before_popup(). |is_devtools| will be true (1)
+  // if the popup will be a DevTools browser. Return the delegate that will be
+  // used for the new popup BrowserView.
+  ///
+  struct _cef_browser_view_delegate_t*(
+      CEF_CALLBACK* get_delegate_for_popup_browser_view)(
+      struct _cef_browser_view_delegate_t* self,
+      struct _cef_browser_view_t* browser_view,
+      const struct _cef_browser_settings_t* settings,
+      struct _cef_client_t* client,
+      int is_devtools);
+
+  ///
+  // Called after |popup_browser_view| is created. This function will be called
+  // after cef_life_span_handler_t::on_after_created() and on_browser_created()
+  // are called for the new popup browser. The popup originated from
+  // |browser_view|. |is_devtools| will be true (1) if the popup is a DevTools
+  // browser. Optionally add |popup_browser_view| to the views hierarchy
+  // yourself and return true (1). Otherwise return false (0) and a default
+  // cef_window_t will be created for the popup.
+  ///
+  int(CEF_CALLBACK* on_popup_browser_view_created)(
+      struct _cef_browser_view_delegate_t* self,
+      struct _cef_browser_view_t* browser_view,
+      struct _cef_browser_view_t* popup_browser_view,
+      int is_devtools);
+} cef_browser_view_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_BROWSER_VIEW_DELEGATE_CAPI_H_
diff --git a/src/include/capi/views/cef_button_capi.h b/src/include/capi/views/cef_button_capi.h
new file mode 100644
index 0000000..2c3d992
--- /dev/null
+++ b/src/include/capi/views/cef_button_capi.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=066842f6905c8eed7de1fc8cf831e06801029b97$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_BUTTON_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_BUTTON_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_view_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_label_button_t;
+
+///
+// A View representing a button. Depending on the specific type, the button
+// could be implemented by a native control or custom rendered. Methods must be
+// called on the browser process UI thread unless otherwise indicated.
+///
+typedef struct _cef_button_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_t base;
+
+  ///
+  // Returns this Button as a LabelButton or NULL if this is not a LabelButton.
+  ///
+  struct _cef_label_button_t*(CEF_CALLBACK* as_label_button)(
+      struct _cef_button_t* self);
+
+  ///
+  // Sets the current display state of the Button.
+  ///
+  void(CEF_CALLBACK* set_state)(struct _cef_button_t* self,
+                                cef_button_state_t state);
+
+  ///
+  // Returns the current display state of the Button.
+  ///
+  cef_button_state_t(CEF_CALLBACK* get_state)(struct _cef_button_t* self);
+
+  ///
+  // Sets the Button will use an ink drop effect for displaying state changes.
+  ///
+  void(CEF_CALLBACK* set_ink_drop_enabled)(struct _cef_button_t* self,
+                                           int enabled);
+
+  ///
+  // Sets the tooltip text that will be displayed when the user hovers the mouse
+  // cursor over the Button.
+  ///
+  void(CEF_CALLBACK* set_tooltip_text)(struct _cef_button_t* self,
+                                       const cef_string_t* tooltip_text);
+
+  ///
+  // Sets the accessible name that will be exposed to assistive technology (AT).
+  ///
+  void(CEF_CALLBACK* set_accessible_name)(struct _cef_button_t* self,
+                                          const cef_string_t* name);
+} cef_button_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_BUTTON_CAPI_H_
diff --git a/src/include/capi/views/cef_button_delegate_capi.h b/src/include/capi/views/cef_button_delegate_capi.h
new file mode 100644
index 0000000..da67a0b
--- /dev/null
+++ b/src/include/capi/views/cef_button_delegate_capi.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=71def746b63431f9aa779bbb67e85bc2e0176615$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_BUTTON_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_BUTTON_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_view_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_button_t;
+
+///
+// Implement this structure to handle Button events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_button_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_delegate_t base;
+
+  ///
+  // Called when |button| is pressed.
+  ///
+  void(CEF_CALLBACK* on_button_pressed)(struct _cef_button_delegate_t* self,
+                                        struct _cef_button_t* button);
+
+  ///
+  // Called when the state of |button| changes.
+  ///
+  void(CEF_CALLBACK* on_button_state_changed)(
+      struct _cef_button_delegate_t* self,
+      struct _cef_button_t* button);
+} cef_button_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_BUTTON_DELEGATE_CAPI_H_
diff --git a/src/include/capi/views/cef_display_capi.h b/src/include/capi/views/cef_display_capi.h
new file mode 100644
index 0000000..89bb5ad
--- /dev/null
+++ b/src/include/capi/views/cef_display_capi.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=422243fda6e1404222aca7bdd4e7b84b961a9626$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_DISPLAY_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_DISPLAY_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// This structure typically, but not always, corresponds to a physical display
+// connected to the system. A fake Display may exist on a headless system, or a
+// Display may correspond to a remote, virtual display. All size and position
+// values are in density independent pixels (DIP) unless otherwise indicated.
+// Methods must be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_display_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns the unique identifier for this Display.
+  ///
+  int64(CEF_CALLBACK* get_id)(struct _cef_display_t* self);
+
+  ///
+  // Returns this Display's device pixel scale factor. This specifies how much
+  // the UI should be scaled when the actual output has more pixels than
+  // standard displays (which is around 100~120dpi). The potential return values
+  // differ by platform.
+  ///
+  float(CEF_CALLBACK* get_device_scale_factor)(struct _cef_display_t* self);
+
+  ///
+  // Convert |point| from density independent pixels (DIP) to pixel coordinates
+  // using this Display's device scale factor.
+  ///
+  void(CEF_CALLBACK* convert_point_to_pixels)(struct _cef_display_t* self,
+                                              cef_point_t* point);
+
+  ///
+  // Convert |point| from pixel coordinates to density independent pixels (DIP)
+  // using this Display's device scale factor.
+  ///
+  void(CEF_CALLBACK* convert_point_from_pixels)(struct _cef_display_t* self,
+                                                cef_point_t* point);
+
+  ///
+  // Returns this Display's bounds. This is the full size of the display.
+  ///
+  cef_rect_t(CEF_CALLBACK* get_bounds)(struct _cef_display_t* self);
+
+  ///
+  // Returns this Display's work area. This excludes areas of the display that
+  // are occupied for window manager toolbars, etc.
+  ///
+  cef_rect_t(CEF_CALLBACK* get_work_area)(struct _cef_display_t* self);
+
+  ///
+  // Returns this Display's rotation in degrees.
+  ///
+  int(CEF_CALLBACK* get_rotation)(struct _cef_display_t* self);
+} cef_display_t;
+
+///
+// Returns the primary Display.
+///
+CEF_EXPORT cef_display_t* cef_display_get_primary();
+
+///
+// Returns the Display nearest |point|. Set |input_pixel_coords| to true (1) if
+// |point| is in pixel coordinates instead of density independent pixels (DIP).
+///
+CEF_EXPORT cef_display_t* cef_display_get_nearest_point(
+    const cef_point_t* point,
+    int input_pixel_coords);
+
+///
+// Returns the Display that most closely intersects |bounds|.  Set
+// |input_pixel_coords| to true (1) if |bounds| is in pixel coordinates instead
+// of density independent pixels (DIP).
+///
+CEF_EXPORT cef_display_t* cef_display_get_matching_bounds(
+    const cef_rect_t* bounds,
+    int input_pixel_coords);
+
+///
+// Returns the total number of Displays. Mirrored displays are excluded; this
+// function is intended to return the number of distinct, usable displays.
+///
+CEF_EXPORT size_t cef_display_get_count();
+
+///
+// Returns all Displays. Mirrored displays are excluded; this function is
+// intended to return distinct, usable displays.
+///
+CEF_EXPORT void cef_display_get_alls(size_t* displaysCount,
+                                     cef_display_t** displays);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_DISPLAY_CAPI_H_
diff --git a/src/include/capi/views/cef_fill_layout_capi.h b/src/include/capi/views/cef_fill_layout_capi.h
new file mode 100644
index 0000000..edd56c6
--- /dev/null
+++ b/src/include/capi/views/cef_fill_layout_capi.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=14016ebf5d63f625436aebd14396e403b673703f$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_FILL_LAYOUT_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_FILL_LAYOUT_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_layout_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// A simple Layout that causes the associated Panel's one child to be sized to
+// match the bounds of its parent. Methods must be called on the browser process
+// UI thread unless otherwise indicated.
+///
+typedef struct _cef_fill_layout_t {
+  ///
+  // Base structure.
+  ///
+  cef_layout_t base;
+} cef_fill_layout_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_FILL_LAYOUT_CAPI_H_
diff --git a/src/include/capi/views/cef_label_button_capi.h b/src/include/capi/views/cef_label_button_capi.h
new file mode 100644
index 0000000..05a930f
--- /dev/null
+++ b/src/include/capi/views/cef_label_button_capi.h
@@ -0,0 +1,161 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=d947a566036f44439f352aea45098dfb46043b9a$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_LABEL_BUTTON_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_LABEL_BUTTON_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_image_capi.h"
+#include "include/capi/views/cef_button_capi.h"
+#include "include/capi/views/cef_button_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_menu_button_t;
+
+///
+// LabelButton is a button with optional text and/or icon. Methods must be
+// called on the browser process UI thread unless otherwise indicated.
+///
+typedef struct _cef_label_button_t {
+  ///
+  // Base structure.
+  ///
+  cef_button_t base;
+
+  ///
+  // Returns this LabelButton as a MenuButton or NULL if this is not a
+  // MenuButton.
+  ///
+  struct _cef_menu_button_t*(CEF_CALLBACK* as_menu_button)(
+      struct _cef_label_button_t* self);
+
+  ///
+  // Sets the text shown on the LabelButton. By default |text| will also be used
+  // as the accessible name.
+  ///
+  void(CEF_CALLBACK* set_text)(struct _cef_label_button_t* self,
+                               const cef_string_t* text);
+
+  ///
+  // Returns the text shown on the LabelButton.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_text)(
+      struct _cef_label_button_t* self);
+
+  ///
+  // Sets the image shown for |button_state|. When this Button is drawn if no
+  // image exists for the current state then the image for
+  // CEF_BUTTON_STATE_NORMAL, if any, will be shown.
+  ///
+  void(CEF_CALLBACK* set_image)(struct _cef_label_button_t* self,
+                                cef_button_state_t button_state,
+                                struct _cef_image_t* image);
+
+  ///
+  // Returns the image shown for |button_state|. If no image exists for that
+  // state then the image for CEF_BUTTON_STATE_NORMAL will be returned.
+  ///
+  struct _cef_image_t*(CEF_CALLBACK* get_image)(
+      struct _cef_label_button_t* self,
+      cef_button_state_t button_state);
+
+  ///
+  // Sets the text color shown for the specified button |for_state| to |color|.
+  ///
+  void(CEF_CALLBACK* set_text_color)(struct _cef_label_button_t* self,
+                                     cef_button_state_t for_state,
+                                     cef_color_t color);
+
+  ///
+  // Sets the text colors shown for the non-disabled states to |color|.
+  ///
+  void(CEF_CALLBACK* set_enabled_text_colors)(struct _cef_label_button_t* self,
+                                              cef_color_t color);
+
+  ///
+  // Sets the font list. The format is "<FONT_FAMILY_LIST>,[STYLES] <SIZE>",
+  // where: - FONT_FAMILY_LIST is a comma-separated list of font family names, -
+  // STYLES is an optional space-separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings: - "Arial, Helvetica,
+  // Bold Italic 14px" - "Arial, 14px"
+  ///
+  void(CEF_CALLBACK* set_font_list)(struct _cef_label_button_t* self,
+                                    const cef_string_t* font_list);
+
+  ///
+  // Sets the horizontal alignment; reversed in RTL. Default is
+  // CEF_HORIZONTAL_ALIGNMENT_CENTER.
+  ///
+  void(CEF_CALLBACK* set_horizontal_alignment)(
+      struct _cef_label_button_t* self,
+      cef_horizontal_alignment_t alignment);
+
+  ///
+  // Reset the minimum size of this LabelButton to |size|.
+  ///
+  void(CEF_CALLBACK* set_minimum_size)(struct _cef_label_button_t* self,
+                                       const cef_size_t* size);
+
+  ///
+  // Reset the maximum size of this LabelButton to |size|.
+  ///
+  void(CEF_CALLBACK* set_maximum_size)(struct _cef_label_button_t* self,
+                                       const cef_size_t* size);
+} cef_label_button_t;
+
+///
+// Create a new LabelButton. A |delegate| must be provided to handle the button
+// click. |text| will be shown on the LabelButton and used as the default
+// accessible name.
+///
+CEF_EXPORT cef_label_button_t* cef_label_button_create(
+    struct _cef_button_delegate_t* delegate,
+    const cef_string_t* text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_LABEL_BUTTON_CAPI_H_
diff --git a/src/include/capi/views/cef_layout_capi.h b/src/include/capi/views/cef_layout_capi.h
new file mode 100644
index 0000000..2ecfd7b
--- /dev/null
+++ b/src/include/capi/views/cef_layout_capi.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=7db4a977d7fbbff0783d0334e53fa47f00fc9fac$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_LAYOUT_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_LAYOUT_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_box_layout_t;
+struct _cef_fill_layout_t;
+
+///
+// A Layout handles the sizing of the children of a Panel according to
+// implementation-specific heuristics. Methods must be called on the browser
+// process UI thread unless otherwise indicated.
+///
+typedef struct _cef_layout_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns this Layout as a BoxLayout or NULL if this is not a BoxLayout.
+  ///
+  struct _cef_box_layout_t*(CEF_CALLBACK* as_box_layout)(
+      struct _cef_layout_t* self);
+
+  ///
+  // Returns this Layout as a FillLayout or NULL if this is not a FillLayout.
+  ///
+  struct _cef_fill_layout_t*(CEF_CALLBACK* as_fill_layout)(
+      struct _cef_layout_t* self);
+
+  ///
+  // Returns true (1) if this Layout is valid.
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_layout_t* self);
+} cef_layout_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_LAYOUT_CAPI_H_
diff --git a/src/include/capi/views/cef_menu_button_capi.h b/src/include/capi/views/cef_menu_button_capi.h
new file mode 100644
index 0000000..37f1bb8
--- /dev/null
+++ b/src/include/capi/views/cef_menu_button_capi.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=8e1972f8c40d4c7a708f32016dbfeb5bf2a2ff0b$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_MENU_BUTTON_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_MENU_BUTTON_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_menu_model_capi.h"
+#include "include/capi/views/cef_label_button_capi.h"
+#include "include/capi/views/cef_menu_button_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// MenuButton is a button with optional text, icon and/or menu marker that shows
+// a menu when clicked with the left mouse button. All size and position values
+// are in density independent pixels (DIP) unless otherwise indicated. Methods
+// must be called on the browser process UI thread unless otherwise indicated.
+///
+typedef struct _cef_menu_button_t {
+  ///
+  // Base structure.
+  ///
+  cef_label_button_t base;
+
+  ///
+  // Show a menu with contents |menu_model|. |screen_point| specifies the menu
+  // position in screen coordinates. |anchor_position| specifies how the menu
+  // will be anchored relative to |screen_point|. This function should be called
+  // from cef_menu_button_delegate_t::on_menu_button_pressed().
+  ///
+  void(CEF_CALLBACK* show_menu)(struct _cef_menu_button_t* self,
+                                struct _cef_menu_model_t* menu_model,
+                                const cef_point_t* screen_point,
+                                cef_menu_anchor_position_t anchor_position);
+
+  ///
+  // Show the menu for this button. Results in a call to
+  // cef_menu_button_delegate_t::on_menu_button_pressed().
+  ///
+  void(CEF_CALLBACK* trigger_menu)(struct _cef_menu_button_t* self);
+} cef_menu_button_t;
+
+///
+// Create a new MenuButton. A |delegate| must be provided to call show_menu()
+// when the button is clicked. |text| will be shown on the MenuButton and used
+// as the default accessible name. If |with_frame| is true (1) the button will
+// have a visible frame at all times, center alignment, additional padding and a
+// default minimum size of 70x33 DIP. If |with_frame| is false (0) the button
+// will only have a visible frame on hover/press, left alignment, less padding
+// and no default minimum size.
+///
+CEF_EXPORT cef_menu_button_t* cef_menu_button_create(
+    struct _cef_menu_button_delegate_t* delegate,
+    const cef_string_t* text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_MENU_BUTTON_CAPI_H_
diff --git a/src/include/capi/views/cef_menu_button_delegate_capi.h b/src/include/capi/views/cef_menu_button_delegate_capi.h
new file mode 100644
index 0000000..b2c4a5c
--- /dev/null
+++ b/src/include/capi/views/cef_menu_button_delegate_capi.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=6668ddfd94bb501f0cb35b2c23043333c1cafe63$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_MENU_BUTTON_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_MENU_BUTTON_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_button_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_menu_button_t;
+
+///
+// MenuButton pressed lock is released when this object is destroyed.
+///
+typedef struct _cef_menu_button_pressed_lock_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+} cef_menu_button_pressed_lock_t;
+
+///
+// Implement this structure to handle MenuButton events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_menu_button_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_button_delegate_t base;
+
+  ///
+  // Called when |button| is pressed. Call cef_menu_button_t::show_menu() to
+  // show a popup menu at |screen_point|. When showing a custom popup such as a
+  // window keep a reference to |button_pressed_lock| until the popup is hidden
+  // to maintain the pressed button state.
+  ///
+  void(CEF_CALLBACK* on_menu_button_pressed)(
+      struct _cef_menu_button_delegate_t* self,
+      struct _cef_menu_button_t* menu_button,
+      const cef_point_t* screen_point,
+      struct _cef_menu_button_pressed_lock_t* button_pressed_lock);
+} cef_menu_button_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_MENU_BUTTON_DELEGATE_CAPI_H_
diff --git a/src/include/capi/views/cef_panel_capi.h b/src/include/capi/views/cef_panel_capi.h
new file mode 100644
index 0000000..cf82398
--- /dev/null
+++ b/src/include/capi/views/cef_panel_capi.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=28db02fcdd51238b61d74195dae36fe23e0717da$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_PANEL_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_PANEL_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_panel_delegate_capi.h"
+#include "include/capi/views/cef_view_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_box_layout_t;
+struct _cef_fill_layout_t;
+struct _cef_layout_t;
+struct _cef_window_t;
+
+///
+// A Panel is a container in the views hierarchy that can contain other Views as
+// children. Methods must be called on the browser process UI thread unless
+// otherwise indicated.
+///
+typedef struct _cef_panel_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_t base;
+
+  ///
+  // Returns this Panel as a Window or NULL if this is not a Window.
+  ///
+  struct _cef_window_t*(CEF_CALLBACK* as_window)(struct _cef_panel_t* self);
+
+  ///
+  // Set this Panel's Layout to FillLayout and return the FillLayout object.
+  ///
+  struct _cef_fill_layout_t*(CEF_CALLBACK* set_to_fill_layout)(
+      struct _cef_panel_t* self);
+
+  ///
+  // Set this Panel's Layout to BoxLayout and return the BoxLayout object.
+  ///
+  struct _cef_box_layout_t*(CEF_CALLBACK* set_to_box_layout)(
+      struct _cef_panel_t* self,
+      const struct _cef_box_layout_settings_t* settings);
+
+  ///
+  // Get the Layout.
+  ///
+  struct _cef_layout_t*(CEF_CALLBACK* get_layout)(struct _cef_panel_t* self);
+
+  ///
+  // Lay out the child Views (set their bounds based on sizing heuristics
+  // specific to the current Layout).
+  ///
+  void(CEF_CALLBACK* layout)(struct _cef_panel_t* self);
+
+  ///
+  // Add a child View.
+  ///
+  void(CEF_CALLBACK* add_child_view)(struct _cef_panel_t* self,
+                                     struct _cef_view_t* view);
+
+  ///
+  // Add a child View at the specified |index|. If |index| matches the result of
+  // GetChildCount() then the View will be added at the end.
+  ///
+  void(CEF_CALLBACK* add_child_view_at)(struct _cef_panel_t* self,
+                                        struct _cef_view_t* view,
+                                        int index);
+
+  ///
+  // Move the child View to the specified |index|. A negative value for |index|
+  // will move the View to the end.
+  ///
+  void(CEF_CALLBACK* reorder_child_view)(struct _cef_panel_t* self,
+                                         struct _cef_view_t* view,
+                                         int index);
+
+  ///
+  // Remove a child View. The View can then be added to another Panel.
+  ///
+  void(CEF_CALLBACK* remove_child_view)(struct _cef_panel_t* self,
+                                        struct _cef_view_t* view);
+
+  ///
+  // Remove all child Views. The removed Views will be deleted if the client
+  // holds no references to them.
+  ///
+  void(CEF_CALLBACK* remove_all_child_views)(struct _cef_panel_t* self);
+
+  ///
+  // Returns the number of child Views.
+  ///
+  size_t(CEF_CALLBACK* get_child_view_count)(struct _cef_panel_t* self);
+
+  ///
+  // Returns the child View at the specified |index|.
+  ///
+  struct _cef_view_t*(
+      CEF_CALLBACK* get_child_view_at)(struct _cef_panel_t* self, int index);
+} cef_panel_t;
+
+///
+// Create a new Panel.
+///
+CEF_EXPORT cef_panel_t* cef_panel_create(
+    struct _cef_panel_delegate_t* delegate);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_PANEL_CAPI_H_
diff --git a/src/include/capi/views/cef_panel_delegate_capi.h b/src/include/capi/views/cef_panel_delegate_capi.h
new file mode 100644
index 0000000..9e178b7
--- /dev/null
+++ b/src/include/capi/views/cef_panel_delegate_capi.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=8a0ded4e62ff61b04fc1694228905daa995197b4$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_PANEL_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_PANEL_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_view_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Implement this structure to handle Panel events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_panel_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_delegate_t base;
+} cef_panel_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_PANEL_DELEGATE_CAPI_H_
diff --git a/src/include/capi/views/cef_scroll_view_capi.h b/src/include/capi/views/cef_scroll_view_capi.h
new file mode 100644
index 0000000..89de452
--- /dev/null
+++ b/src/include/capi/views/cef_scroll_view_capi.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=3b9789f2149c3e6d6a2e671e5427c19654442ede$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_SCROLL_VIEW_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_SCROLL_VIEW_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_view_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// A ScrollView will show horizontal and/or vertical scrollbars when necessary
+// based on the size of the attached content view. Methods must be called on the
+// browser process UI thread unless otherwise indicated.
+///
+typedef struct _cef_scroll_view_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_t base;
+
+  ///
+  // Set the content View. The content View must have a specified size (e.g. via
+  // cef_view_t::SetBounds or cef_view_delegate_t::GetPreferredSize).
+  ///
+  void(CEF_CALLBACK* set_content_view)(struct _cef_scroll_view_t* self,
+                                       struct _cef_view_t* view);
+
+  ///
+  // Returns the content View.
+  ///
+  struct _cef_view_t*(CEF_CALLBACK* get_content_view)(
+      struct _cef_scroll_view_t* self);
+
+  ///
+  // Returns the visible region of the content View.
+  ///
+  cef_rect_t(CEF_CALLBACK* get_visible_content_rect)(
+      struct _cef_scroll_view_t* self);
+
+  ///
+  // Returns true (1) if the horizontal scrollbar is currently showing.
+  ///
+  int(CEF_CALLBACK* has_horizontal_scrollbar)(struct _cef_scroll_view_t* self);
+
+  ///
+  // Returns the height of the horizontal scrollbar.
+  ///
+  int(CEF_CALLBACK* get_horizontal_scrollbar_height)(
+      struct _cef_scroll_view_t* self);
+
+  ///
+  // Returns true (1) if the vertical scrollbar is currently showing.
+  ///
+  int(CEF_CALLBACK* has_vertical_scrollbar)(struct _cef_scroll_view_t* self);
+
+  ///
+  // Returns the width of the vertical scrollbar.
+  ///
+  int(CEF_CALLBACK* get_vertical_scrollbar_width)(
+      struct _cef_scroll_view_t* self);
+} cef_scroll_view_t;
+
+///
+// Create a new ScrollView.
+///
+CEF_EXPORT cef_scroll_view_t* cef_scroll_view_create(
+    struct _cef_view_delegate_t* delegate);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_SCROLL_VIEW_CAPI_H_
diff --git a/src/include/capi/views/cef_textfield_capi.h b/src/include/capi/views/cef_textfield_capi.h
new file mode 100644
index 0000000..1b49658
--- /dev/null
+++ b/src/include/capi/views/cef_textfield_capi.h
@@ -0,0 +1,276 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=71ba0e5714400e2bea3ca564cab4527d95e4b52c$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_TEXTFIELD_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_TEXTFIELD_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_textfield_delegate_capi.h"
+#include "include/capi/views/cef_view_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// A Textfield supports editing of text. This control is custom rendered with no
+// platform-specific code. Methods must be called on the browser process UI
+// thread unless otherwise indicated.
+///
+typedef struct _cef_textfield_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_t base;
+
+  ///
+  // Sets whether the text will be displayed as asterisks.
+  ///
+  void(CEF_CALLBACK* set_password_input)(struct _cef_textfield_t* self,
+                                         int password_input);
+
+  ///
+  // Returns true (1) if the text will be displayed as asterisks.
+  ///
+  int(CEF_CALLBACK* is_password_input)(struct _cef_textfield_t* self);
+
+  ///
+  // Sets whether the text will read-only.
+  ///
+  void(CEF_CALLBACK* set_read_only)(struct _cef_textfield_t* self,
+                                    int read_only);
+
+  ///
+  // Returns true (1) if the text is read-only.
+  ///
+  int(CEF_CALLBACK* is_read_only)(struct _cef_textfield_t* self);
+
+  ///
+  // Returns the currently displayed text.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_text)(struct _cef_textfield_t* self);
+
+  ///
+  // Sets the contents to |text|. The cursor will be moved to end of the text if
+  // the current position is outside of the text range.
+  ///
+  void(CEF_CALLBACK* set_text)(struct _cef_textfield_t* self,
+                               const cef_string_t* text);
+
+  ///
+  // Appends |text| to the previously-existing text.
+  ///
+  void(CEF_CALLBACK* append_text)(struct _cef_textfield_t* self,
+                                  const cef_string_t* text);
+
+  ///
+  // Inserts |text| at the current cursor position replacing any selected text.
+  ///
+  void(CEF_CALLBACK* insert_or_replace_text)(struct _cef_textfield_t* self,
+                                             const cef_string_t* text);
+
+  ///
+  // Returns true (1) if there is any selected text.
+  ///
+  int(CEF_CALLBACK* has_selection)(struct _cef_textfield_t* self);
+
+  ///
+  // Returns the currently selected text.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_selected_text)(
+      struct _cef_textfield_t* self);
+
+  ///
+  // Selects all text. If |reversed| is true (1) the range will end at the
+  // logical beginning of the text; this generally shows the leading portion of
+  // text that overflows its display area.
+  ///
+  void(CEF_CALLBACK* select_all)(struct _cef_textfield_t* self, int reversed);
+
+  ///
+  // Clears the text selection and sets the caret to the end.
+  ///
+  void(CEF_CALLBACK* clear_selection)(struct _cef_textfield_t* self);
+
+  ///
+  // Returns the selected logical text range.
+  ///
+  cef_range_t(CEF_CALLBACK* get_selected_range)(struct _cef_textfield_t* self);
+
+  ///
+  // Selects the specified logical text range.
+  ///
+  void(CEF_CALLBACK* select_range)(struct _cef_textfield_t* self,
+                                   const cef_range_t* range);
+
+  ///
+  // Returns the current cursor position.
+  ///
+  size_t(CEF_CALLBACK* get_cursor_position)(struct _cef_textfield_t* self);
+
+  ///
+  // Sets the text color.
+  ///
+  void(CEF_CALLBACK* set_text_color)(struct _cef_textfield_t* self,
+                                     cef_color_t color);
+
+  ///
+  // Returns the text color.
+  ///
+  cef_color_t(CEF_CALLBACK* get_text_color)(struct _cef_textfield_t* self);
+
+  ///
+  // Sets the selection text color.
+  ///
+  void(CEF_CALLBACK* set_selection_text_color)(struct _cef_textfield_t* self,
+                                               cef_color_t color);
+
+  ///
+  // Returns the selection text color.
+  ///
+  cef_color_t(CEF_CALLBACK* get_selection_text_color)(
+      struct _cef_textfield_t* self);
+
+  ///
+  // Sets the selection background color.
+  ///
+  void(CEF_CALLBACK* set_selection_background_color)(
+      struct _cef_textfield_t* self,
+      cef_color_t color);
+
+  ///
+  // Returns the selection background color.
+  ///
+  cef_color_t(CEF_CALLBACK* get_selection_background_color)(
+      struct _cef_textfield_t* self);
+
+  ///
+  // Sets the font list. The format is "<FONT_FAMILY_LIST>,[STYLES] <SIZE>",
+  // where: - FONT_FAMILY_LIST is a comma-separated list of font family names, -
+  // STYLES is an optional space-separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings: - "Arial, Helvetica,
+  // Bold Italic 14px" - "Arial, 14px"
+  ///
+  void(CEF_CALLBACK* set_font_list)(struct _cef_textfield_t* self,
+                                    const cef_string_t* font_list);
+
+  ///
+  // Applies |color| to the specified |range| without changing the default
+  // color. If |range| is NULL the color will be set on the complete text
+  // contents.
+  ///
+  void(CEF_CALLBACK* apply_text_color)(struct _cef_textfield_t* self,
+                                       cef_color_t color,
+                                       const cef_range_t* range);
+
+  ///
+  // Applies |style| to the specified |range| without changing the default
+  // style. If |add| is true (1) the style will be added, otherwise the style
+  // will be removed. If |range| is NULL the style will be set on the complete
+  // text contents.
+  ///
+  void(CEF_CALLBACK* apply_text_style)(struct _cef_textfield_t* self,
+                                       cef_text_style_t style,
+                                       int add,
+                                       const cef_range_t* range);
+
+  ///
+  // Returns true (1) if the action associated with the specified command id is
+  // enabled. See additional comments on execute_command().
+  ///
+  int(CEF_CALLBACK* is_command_enabled)(struct _cef_textfield_t* self,
+                                        int command_id);
+
+  ///
+  // Performs the action associated with the specified command id. Valid values
+  // include IDS_APP_UNDO, IDS_APP_REDO, IDS_APP_CUT, IDS_APP_COPY,
+  // IDS_APP_PASTE, IDS_APP_DELETE, IDS_APP_SELECT_ALL, IDS_DELETE_* and
+  // IDS_MOVE_*. See include/cef_pack_strings.h for definitions.
+  ///
+  void(CEF_CALLBACK* execute_command)(struct _cef_textfield_t* self,
+                                      int command_id);
+
+  ///
+  // Clears Edit history.
+  ///
+  void(CEF_CALLBACK* clear_edit_history)(struct _cef_textfield_t* self);
+
+  ///
+  // Sets the placeholder text that will be displayed when the Textfield is
+  // NULL.
+  ///
+  void(CEF_CALLBACK* set_placeholder_text)(struct _cef_textfield_t* self,
+                                           const cef_string_t* text);
+
+  ///
+  // Returns the placeholder text that will be displayed when the Textfield is
+  // NULL.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_placeholder_text)(
+      struct _cef_textfield_t* self);
+
+  ///
+  // Sets the placeholder text color.
+  ///
+  void(CEF_CALLBACK* set_placeholder_text_color)(struct _cef_textfield_t* self,
+                                                 cef_color_t color);
+
+  ///
+  // Set the accessible name that will be exposed to assistive technology (AT).
+  ///
+  void(CEF_CALLBACK* set_accessible_name)(struct _cef_textfield_t* self,
+                                          const cef_string_t* name);
+} cef_textfield_t;
+
+///
+// Create a new Textfield.
+///
+CEF_EXPORT cef_textfield_t* cef_textfield_create(
+    struct _cef_textfield_delegate_t* delegate);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_TEXTFIELD_CAPI_H_
diff --git a/src/include/capi/views/cef_textfield_delegate_capi.h b/src/include/capi/views/cef_textfield_delegate_capi.h
new file mode 100644
index 0000000..6442fe5
--- /dev/null
+++ b/src/include/capi/views/cef_textfield_delegate_capi.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=ba43938d4c400bcdd25e48b4f84758b18609813b$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_TEXTFIELD_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_TEXTFIELD_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_view_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_textfield_t;
+
+///
+// Implement this structure to handle Textfield events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_textfield_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_view_delegate_t base;
+
+  ///
+  // Called when |textfield| recieves a keyboard event. |event| contains
+  // information about the keyboard event. Return true (1) if the keyboard event
+  // was handled or false (0) otherwise for default handling.
+  ///
+  int(CEF_CALLBACK* on_key_event)(struct _cef_textfield_delegate_t* self,
+                                  struct _cef_textfield_t* textfield,
+                                  const struct _cef_key_event_t* event);
+
+  ///
+  // Called after performing a user action that may change |textfield|.
+  ///
+  void(CEF_CALLBACK* on_after_user_action)(
+      struct _cef_textfield_delegate_t* self,
+      struct _cef_textfield_t* textfield);
+} cef_textfield_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_TEXTFIELD_DELEGATE_CAPI_H_
diff --git a/src/include/capi/views/cef_view_capi.h b/src/include/capi/views/cef_view_capi.h
new file mode 100644
index 0000000..e50c73d
--- /dev/null
+++ b/src/include/capi/views/cef_view_capi.h
@@ -0,0 +1,389 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=45b421dfcf8ee7cf61b1991a336bc65d33fbe10a$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_VIEW_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_VIEW_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_view_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_browser_view_t;
+struct _cef_button_t;
+struct _cef_panel_t;
+struct _cef_scroll_view_t;
+struct _cef_textfield_t;
+struct _cef_window_t;
+
+///
+// A View is a rectangle within the views View hierarchy. It is the base
+// structure for all Views. All size and position values are in density
+// independent pixels (DIP) unless otherwise indicated. Methods must be called
+// on the browser process UI thread unless otherwise indicated.
+///
+typedef struct _cef_view_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Returns this View as a BrowserView or NULL if this is not a BrowserView.
+  ///
+  struct _cef_browser_view_t*(CEF_CALLBACK* as_browser_view)(
+      struct _cef_view_t* self);
+
+  ///
+  // Returns this View as a Button or NULL if this is not a Button.
+  ///
+  struct _cef_button_t*(CEF_CALLBACK* as_button)(struct _cef_view_t* self);
+
+  ///
+  // Returns this View as a Panel or NULL if this is not a Panel.
+  ///
+  struct _cef_panel_t*(CEF_CALLBACK* as_panel)(struct _cef_view_t* self);
+
+  ///
+  // Returns this View as a ScrollView or NULL if this is not a ScrollView.
+  ///
+  struct _cef_scroll_view_t*(CEF_CALLBACK* as_scroll_view)(
+      struct _cef_view_t* self);
+
+  ///
+  // Returns this View as a Textfield or NULL if this is not a Textfield.
+  ///
+  struct _cef_textfield_t*(CEF_CALLBACK* as_textfield)(
+      struct _cef_view_t* self);
+
+  ///
+  // Returns the type of this View as a string. Used primarily for testing
+  // purposes.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_type_string)(
+      struct _cef_view_t* self);
+
+  ///
+  // Returns a string representation of this View which includes the type and
+  // various type-specific identifying attributes. If |include_children| is true
+  // (1) any child Views will also be included. Used primarily for testing
+  // purposes.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* to_string)(struct _cef_view_t* self,
+                                                 int include_children);
+
+  ///
+  // Returns true (1) if this View is valid.
+  ///
+  int(CEF_CALLBACK* is_valid)(struct _cef_view_t* self);
+
+  ///
+  // Returns true (1) if this View is currently attached to another View. A View
+  // can only be attached to one View at a time.
+  ///
+  int(CEF_CALLBACK* is_attached)(struct _cef_view_t* self);
+
+  ///
+  // Returns true (1) if this View is the same as |that| View.
+  ///
+  int(CEF_CALLBACK* is_same)(struct _cef_view_t* self,
+                             struct _cef_view_t* that);
+
+  ///
+  // Returns the delegate associated with this View, if any.
+  ///
+  struct _cef_view_delegate_t*(CEF_CALLBACK* get_delegate)(
+      struct _cef_view_t* self);
+
+  ///
+  // Returns the top-level Window hosting this View, if any.
+  ///
+  struct _cef_window_t*(CEF_CALLBACK* get_window)(struct _cef_view_t* self);
+
+  ///
+  // Returns the ID for this View.
+  ///
+  int(CEF_CALLBACK* get_id)(struct _cef_view_t* self);
+
+  ///
+  // Sets the ID for this View. ID should be unique within the subtree that you
+  // intend to search for it. 0 is the default ID for views.
+  ///
+  void(CEF_CALLBACK* set_id)(struct _cef_view_t* self, int id);
+
+  ///
+  // Returns the group id of this View, or -1 if not set.
+  ///
+  int(CEF_CALLBACK* get_group_id)(struct _cef_view_t* self);
+
+  ///
+  // A group id is used to tag Views which are part of the same logical group.
+  // Focus can be moved between views with the same group using the arrow keys.
+  // The group id is immutable once it's set.
+  ///
+  void(CEF_CALLBACK* set_group_id)(struct _cef_view_t* self, int group_id);
+
+  ///
+  // Returns the View that contains this View, if any.
+  ///
+  struct _cef_view_t*(CEF_CALLBACK* get_parent_view)(struct _cef_view_t* self);
+
+  ///
+  // Recursively descends the view tree starting at this View, and returns the
+  // first child that it encounters with the given ID. Returns NULL if no
+  // matching child view is found.
+  ///
+  struct _cef_view_t*(CEF_CALLBACK* get_view_for_id)(struct _cef_view_t* self,
+                                                     int id);
+
+  ///
+  // Sets the bounds (size and position) of this View. Position is in parent
+  // coordinates.
+  ///
+  void(CEF_CALLBACK* set_bounds)(struct _cef_view_t* self,
+                                 const cef_rect_t* bounds);
+
+  ///
+  // Returns the bounds (size and position) of this View. Position is in parent
+  // coordinates.
+  ///
+  cef_rect_t(CEF_CALLBACK* get_bounds)(struct _cef_view_t* self);
+
+  ///
+  // Returns the bounds (size and position) of this View. Position is in screen
+  // coordinates.
+  ///
+  cef_rect_t(CEF_CALLBACK* get_bounds_in_screen)(struct _cef_view_t* self);
+
+  ///
+  // Sets the size of this View without changing the position.
+  ///
+  void(CEF_CALLBACK* set_size)(struct _cef_view_t* self,
+                               const cef_size_t* size);
+
+  ///
+  // Returns the size of this View.
+  ///
+  cef_size_t(CEF_CALLBACK* get_size)(struct _cef_view_t* self);
+
+  ///
+  // Sets the position of this View without changing the size. |position| is in
+  // parent coordinates.
+  ///
+  void(CEF_CALLBACK* set_position)(struct _cef_view_t* self,
+                                   const cef_point_t* position);
+
+  ///
+  // Returns the position of this View. Position is in parent coordinates.
+  ///
+  cef_point_t(CEF_CALLBACK* get_position)(struct _cef_view_t* self);
+
+  ///
+  // Returns the size this View would like to be if enough space is available.
+  ///
+  cef_size_t(CEF_CALLBACK* get_preferred_size)(struct _cef_view_t* self);
+
+  ///
+  // Size this View to its preferred size.
+  ///
+  void(CEF_CALLBACK* size_to_preferred_size)(struct _cef_view_t* self);
+
+  ///
+  // Returns the minimum size for this View.
+  ///
+  cef_size_t(CEF_CALLBACK* get_minimum_size)(struct _cef_view_t* self);
+
+  ///
+  // Returns the maximum size for this View.
+  ///
+  cef_size_t(CEF_CALLBACK* get_maximum_size)(struct _cef_view_t* self);
+
+  ///
+  // Returns the height necessary to display this View with the provided width.
+  ///
+  int(CEF_CALLBACK* get_height_for_width)(struct _cef_view_t* self, int width);
+
+  ///
+  // Indicate that this View and all parent Views require a re-layout. This
+  // ensures the next call to layout() will propagate to this View even if the
+  // bounds of parent Views do not change.
+  ///
+  void(CEF_CALLBACK* invalidate_layout)(struct _cef_view_t* self);
+
+  ///
+  // Sets whether this View is visible. Windows are hidden by default and other
+  // views are visible by default. This View and any parent views must be set as
+  // visible for this View to be drawn in a Window. If this View is set as
+  // hidden then it and any child views will not be drawn and, if any of those
+  // views currently have focus, then focus will also be cleared. Painting is
+  // scheduled as needed. If this View is a Window then calling this function is
+  // equivalent to calling the Window show() and hide() functions.
+  ///
+  void(CEF_CALLBACK* set_visible)(struct _cef_view_t* self, int visible);
+
+  ///
+  // Returns whether this View is visible. A view may be visible but still not
+  // drawn in a Window if any parent views are hidden. If this View is a Window
+  // then a return value of true (1) indicates that this Window is currently
+  // visible to the user on-screen. If this View is not a Window then call
+  // is_drawn() to determine whether this View and all parent views are visible
+  // and will be drawn.
+  ///
+  int(CEF_CALLBACK* is_visible)(struct _cef_view_t* self);
+
+  ///
+  // Returns whether this View is visible and drawn in a Window. A view is drawn
+  // if it and all parent views are visible. If this View is a Window then
+  // calling this function is equivalent to calling is_visible(). Otherwise, to
+  // determine if the containing Window is visible to the user on-screen call
+  // is_visible() on the Window.
+  ///
+  int(CEF_CALLBACK* is_drawn)(struct _cef_view_t* self);
+
+  ///
+  // Set whether this View is enabled. A disabled View does not receive keyboard
+  // or mouse inputs. If |enabled| differs from the current value the View will
+  // be repainted. Also, clears focus if the focused View is disabled.
+  ///
+  void(CEF_CALLBACK* set_enabled)(struct _cef_view_t* self, int enabled);
+
+  ///
+  // Returns whether this View is enabled.
+  ///
+  int(CEF_CALLBACK* is_enabled)(struct _cef_view_t* self);
+
+  ///
+  // Sets whether this View is capable of taking focus. It will clear focus if
+  // the focused View is set to be non-focusable. This is false (0) by default
+  // so that a View used as a container does not get the focus.
+  ///
+  void(CEF_CALLBACK* set_focusable)(struct _cef_view_t* self, int focusable);
+
+  ///
+  // Returns true (1) if this View is focusable, enabled and drawn.
+  ///
+  int(CEF_CALLBACK* is_focusable)(struct _cef_view_t* self);
+
+  ///
+  // Return whether this View is focusable when the user requires full keyboard
+  // access, even though it may not be normally focusable.
+  ///
+  int(CEF_CALLBACK* is_accessibility_focusable)(struct _cef_view_t* self);
+
+  ///
+  // Request keyboard focus. If this View is focusable it will become the
+  // focused View.
+  ///
+  void(CEF_CALLBACK* request_focus)(struct _cef_view_t* self);
+
+  ///
+  // Sets the background color for this View.
+  ///
+  void(CEF_CALLBACK* set_background_color)(struct _cef_view_t* self,
+                                           cef_color_t color);
+
+  ///
+  // Returns the background color for this View.
+  ///
+  cef_color_t(CEF_CALLBACK* get_background_color)(struct _cef_view_t* self);
+
+  ///
+  // Convert |point| from this View's coordinate system to that of the screen.
+  // This View must belong to a Window when calling this function. Returns true
+  // (1) if the conversion is successful or false (0) otherwise. Use
+  // cef_display_t::convert_point_to_pixels() after calling this function if
+  // further conversion to display-specific pixel coordinates is desired.
+  ///
+  int(CEF_CALLBACK* convert_point_to_screen)(struct _cef_view_t* self,
+                                             cef_point_t* point);
+
+  ///
+  // Convert |point| to this View's coordinate system from that of the screen.
+  // This View must belong to a Window when calling this function. Returns true
+  // (1) if the conversion is successful or false (0) otherwise. Use
+  // cef_display_t::convert_point_from_pixels() before calling this function if
+  // conversion from display-specific pixel coordinates is necessary.
+  ///
+  int(CEF_CALLBACK* convert_point_from_screen)(struct _cef_view_t* self,
+                                               cef_point_t* point);
+
+  ///
+  // Convert |point| from this View's coordinate system to that of the Window.
+  // This View must belong to a Window when calling this function. Returns true
+  // (1) if the conversion is successful or false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* convert_point_to_window)(struct _cef_view_t* self,
+                                             cef_point_t* point);
+
+  ///
+  // Convert |point| to this View's coordinate system from that of the Window.
+  // This View must belong to a Window when calling this function. Returns true
+  // (1) if the conversion is successful or false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* convert_point_from_window)(struct _cef_view_t* self,
+                                               cef_point_t* point);
+
+  ///
+  // Convert |point| from this View's coordinate system to that of |view|.
+  // |view| needs to be in the same Window but not necessarily the same view
+  // hierarchy. Returns true (1) if the conversion is successful or false (0)
+  // otherwise.
+  ///
+  int(CEF_CALLBACK* convert_point_to_view)(struct _cef_view_t* self,
+                                           struct _cef_view_t* view,
+                                           cef_point_t* point);
+
+  ///
+  // Convert |point| to this View's coordinate system from that |view|. |view|
+  // needs to be in the same Window but not necessarily the same view hierarchy.
+  // Returns true (1) if the conversion is successful or false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* convert_point_from_view)(struct _cef_view_t* self,
+                                             struct _cef_view_t* view,
+                                             cef_point_t* point);
+} cef_view_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_VIEW_CAPI_H_
diff --git a/src/include/capi/views/cef_view_delegate_capi.h b/src/include/capi/views/cef_view_delegate_capi.h
new file mode 100644
index 0000000..585f166
--- /dev/null
+++ b/src/include/capi/views/cef_view_delegate_capi.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=52cd273ebe5d458482a830c8ca777907c6c21cc2$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_VIEW_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_VIEW_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_view_t;
+
+///
+// Implement this structure to handle view events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_view_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_base_ref_counted_t base;
+
+  ///
+  // Return the preferred size for |view|. The Layout will use this information
+  // to determine the display size.
+  ///
+  cef_size_t(CEF_CALLBACK* get_preferred_size)(
+      struct _cef_view_delegate_t* self,
+      struct _cef_view_t* view);
+
+  ///
+  // Return the minimum size for |view|.
+  ///
+  cef_size_t(CEF_CALLBACK* get_minimum_size)(struct _cef_view_delegate_t* self,
+                                             struct _cef_view_t* view);
+
+  ///
+  // Return the maximum size for |view|.
+  ///
+  cef_size_t(CEF_CALLBACK* get_maximum_size)(struct _cef_view_delegate_t* self,
+                                             struct _cef_view_t* view);
+
+  ///
+  // Return the height necessary to display |view| with the provided |width|. If
+  // not specified the result of get_preferred_size().height will be used by
+  // default. Override if |view|'s preferred height depends upon the width (for
+  // example, with Labels).
+  ///
+  int(CEF_CALLBACK* get_height_for_width)(struct _cef_view_delegate_t* self,
+                                          struct _cef_view_t* view,
+                                          int width);
+
+  ///
+  // Called when the parent of |view| has changed. If |view| is being added to
+  // |parent| then |added| will be true (1). If |view| is being removed from
+  // |parent| then |added| will be false (0). If |view| is being reparented the
+  // remove notification will be sent before the add notification. Do not modify
+  // the view hierarchy in this callback.
+  ///
+  void(CEF_CALLBACK* on_parent_view_changed)(struct _cef_view_delegate_t* self,
+                                             struct _cef_view_t* view,
+                                             int added,
+                                             struct _cef_view_t* parent);
+
+  ///
+  // Called when a child of |view| has changed. If |child| is being added to
+  // |view| then |added| will be true (1). If |child| is being removed from
+  // |view| then |added| will be false (0). If |child| is being reparented the
+  // remove notification will be sent to the old parent before the add
+  // notification is sent to the new parent. Do not modify the view hierarchy in
+  // this callback.
+  ///
+  void(CEF_CALLBACK* on_child_view_changed)(struct _cef_view_delegate_t* self,
+                                            struct _cef_view_t* view,
+                                            int added,
+                                            struct _cef_view_t* child);
+
+  ///
+  // Called when |view| gains focus.
+  ///
+  void(CEF_CALLBACK* on_focus)(struct _cef_view_delegate_t* self,
+                               struct _cef_view_t* view);
+
+  ///
+  // Called when |view| loses focus.
+  ///
+  void(CEF_CALLBACK* on_blur)(struct _cef_view_delegate_t* self,
+                              struct _cef_view_t* view);
+} cef_view_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_VIEW_DELEGATE_CAPI_H_
diff --git a/src/include/capi/views/cef_window_capi.h b/src/include/capi/views/cef_window_capi.h
new file mode 100644
index 0000000..a62fda5
--- /dev/null
+++ b/src/include/capi/views/cef_window_capi.h
@@ -0,0 +1,314 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=adedd44a4276c3900bce9abc7dc434411407143c$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_WINDOW_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_WINDOW_CAPI_H_
+#pragma once
+
+#include "include/capi/cef_image_capi.h"
+#include "include/capi/cef_menu_model_capi.h"
+#include "include/capi/views/cef_display_capi.h"
+#include "include/capi/views/cef_panel_capi.h"
+#include "include/capi/views/cef_window_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// A Window is a top-level Window/widget in the Views hierarchy. By default it
+// will have a non-client area with title bar, icon and buttons that supports
+// moving and resizing. All size and position values are in density independent
+// pixels (DIP) unless otherwise indicated. Methods must be called on the
+// browser process UI thread unless otherwise indicated.
+///
+typedef struct _cef_window_t {
+  ///
+  // Base structure.
+  ///
+  cef_panel_t base;
+
+  ///
+  // Show the Window.
+  ///
+  void(CEF_CALLBACK* show)(struct _cef_window_t* self);
+
+  ///
+  // Hide the Window.
+  ///
+  void(CEF_CALLBACK* hide)(struct _cef_window_t* self);
+
+  ///
+  // Sizes the Window to |size| and centers it in the current display.
+  ///
+  void(CEF_CALLBACK* center_window)(struct _cef_window_t* self,
+                                    const cef_size_t* size);
+
+  ///
+  // Close the Window.
+  ///
+  void(CEF_CALLBACK* close)(struct _cef_window_t* self);
+
+  ///
+  // Returns true (1) if the Window has been closed.
+  ///
+  int(CEF_CALLBACK* is_closed)(struct _cef_window_t* self);
+
+  ///
+  // Activate the Window, assuming it already exists and is visible.
+  ///
+  void(CEF_CALLBACK* activate)(struct _cef_window_t* self);
+
+  ///
+  // Deactivate the Window, making the next Window in the Z order the active
+  // Window.
+  ///
+  void(CEF_CALLBACK* deactivate)(struct _cef_window_t* self);
+
+  ///
+  // Returns whether the Window is the currently active Window.
+  ///
+  int(CEF_CALLBACK* is_active)(struct _cef_window_t* self);
+
+  ///
+  // Bring this Window to the top of other Windows in the Windowing system.
+  ///
+  void(CEF_CALLBACK* bring_to_top)(struct _cef_window_t* self);
+
+  ///
+  // Set the Window to be on top of other Windows in the Windowing system.
+  ///
+  void(CEF_CALLBACK* set_always_on_top)(struct _cef_window_t* self, int on_top);
+
+  ///
+  // Returns whether the Window has been set to be on top of other Windows in
+  // the Windowing system.
+  ///
+  int(CEF_CALLBACK* is_always_on_top)(struct _cef_window_t* self);
+
+  ///
+  // Maximize the Window.
+  ///
+  void(CEF_CALLBACK* maximize)(struct _cef_window_t* self);
+
+  ///
+  // Minimize the Window.
+  ///
+  void(CEF_CALLBACK* minimize)(struct _cef_window_t* self);
+
+  ///
+  // Restore the Window.
+  ///
+  void(CEF_CALLBACK* restore)(struct _cef_window_t* self);
+
+  ///
+  // Set fullscreen Window state.
+  ///
+  void(CEF_CALLBACK* set_fullscreen)(struct _cef_window_t* self,
+                                     int fullscreen);
+
+  ///
+  // Returns true (1) if the Window is maximized.
+  ///
+  int(CEF_CALLBACK* is_maximized)(struct _cef_window_t* self);
+
+  ///
+  // Returns true (1) if the Window is minimized.
+  ///
+  int(CEF_CALLBACK* is_minimized)(struct _cef_window_t* self);
+
+  ///
+  // Returns true (1) if the Window is fullscreen.
+  ///
+  int(CEF_CALLBACK* is_fullscreen)(struct _cef_window_t* self);
+
+  ///
+  // Set the Window title.
+  ///
+  void(CEF_CALLBACK* set_title)(struct _cef_window_t* self,
+                                const cef_string_t* title);
+
+  ///
+  // Get the Window title.
+  ///
+  // The resulting string must be freed by calling cef_string_userfree_free().
+  cef_string_userfree_t(CEF_CALLBACK* get_title)(struct _cef_window_t* self);
+
+  ///
+  // Set the Window icon. This should be a 16x16 icon suitable for use in the
+  // Windows's title bar.
+  ///
+  void(CEF_CALLBACK* set_window_icon)(struct _cef_window_t* self,
+                                      struct _cef_image_t* image);
+
+  ///
+  // Get the Window icon.
+  ///
+  struct _cef_image_t*(CEF_CALLBACK* get_window_icon)(
+      struct _cef_window_t* self);
+
+  ///
+  // Set the Window App icon. This should be a larger icon for use in the host
+  // environment app switching UI. On Windows, this is the ICON_BIG used in Alt-
+  // Tab list and Windows taskbar. The Window icon will be used by default if no
+  // Window App icon is specified.
+  ///
+  void(CEF_CALLBACK* set_window_app_icon)(struct _cef_window_t* self,
+                                          struct _cef_image_t* image);
+
+  ///
+  // Get the Window App icon.
+  ///
+  struct _cef_image_t*(CEF_CALLBACK* get_window_app_icon)(
+      struct _cef_window_t* self);
+
+  ///
+  // Show a menu with contents |menu_model|. |screen_point| specifies the menu
+  // position in screen coordinates. |anchor_position| specifies how the menu
+  // will be anchored relative to |screen_point|.
+  ///
+  void(CEF_CALLBACK* show_menu)(struct _cef_window_t* self,
+                                struct _cef_menu_model_t* menu_model,
+                                const cef_point_t* screen_point,
+                                cef_menu_anchor_position_t anchor_position);
+
+  ///
+  // Cancel the menu that is currently showing, if any.
+  ///
+  void(CEF_CALLBACK* cancel_menu)(struct _cef_window_t* self);
+
+  ///
+  // Returns the Display that most closely intersects the bounds of this Window.
+  // May return NULL if this Window is not currently displayed.
+  ///
+  struct _cef_display_t*(CEF_CALLBACK* get_display)(struct _cef_window_t* self);
+
+  ///
+  // Returns the bounds (size and position) of this Window's client area.
+  // Position is in screen coordinates.
+  ///
+  cef_rect_t(CEF_CALLBACK* get_client_area_bounds_in_screen)(
+      struct _cef_window_t* self);
+
+  ///
+  // Set the regions where mouse events will be intercepted by this Window to
+  // support drag operations. Call this function with an NULL vector to clear
+  // the draggable regions. The draggable region bounds should be in window
+  // coordinates.
+  ///
+  void(CEF_CALLBACK* set_draggable_regions)(
+      struct _cef_window_t* self,
+      size_t regionsCount,
+      cef_draggable_region_t const* regions);
+
+  ///
+  // Retrieve the platform window handle for this Window.
+  ///
+  cef_window_handle_t(CEF_CALLBACK* get_window_handle)(
+      struct _cef_window_t* self);
+
+  ///
+  // Simulate a key press. |key_code| is the VKEY_* value from Chromium's
+  // ui/events/keycodes/keyboard_codes.h header (VK_* values on Windows).
+  // |event_flags| is some combination of EVENTFLAG_SHIFT_DOWN,
+  // EVENTFLAG_CONTROL_DOWN and/or EVENTFLAG_ALT_DOWN. This function is exposed
+  // primarily for testing purposes.
+  ///
+  void(CEF_CALLBACK* send_key_press)(struct _cef_window_t* self,
+                                     int key_code,
+                                     uint32 event_flags);
+
+  ///
+  // Simulate a mouse move. The mouse cursor will be moved to the specified
+  // (screen_x, screen_y) position. This function is exposed primarily for
+  // testing purposes.
+  ///
+  void(CEF_CALLBACK* send_mouse_move)(struct _cef_window_t* self,
+                                      int screen_x,
+                                      int screen_y);
+
+  ///
+  // Simulate mouse down and/or mouse up events. |button| is the mouse button
+  // type. If |mouse_down| is true (1) a mouse down event will be sent. If
+  // |mouse_up| is true (1) a mouse up event will be sent. If both are true (1)
+  // a mouse down event will be sent followed by a mouse up event (equivalent to
+  // clicking the mouse button). The events will be sent using the current
+  // cursor position so make sure to call send_mouse_move() first to position
+  // the mouse. This function is exposed primarily for testing purposes.
+  ///
+  void(CEF_CALLBACK* send_mouse_events)(struct _cef_window_t* self,
+                                        cef_mouse_button_type_t button,
+                                        int mouse_down,
+                                        int mouse_up);
+
+  ///
+  // Set the keyboard accelerator for the specified |command_id|. |key_code| can
+  // be any virtual key or character value. cef_window_delegate_t::OnAccelerator
+  // will be called if the keyboard combination is triggered while this window
+  // has focus.
+  ///
+  void(CEF_CALLBACK* set_accelerator)(struct _cef_window_t* self,
+                                      int command_id,
+                                      int key_code,
+                                      int shift_pressed,
+                                      int ctrl_pressed,
+                                      int alt_pressed);
+
+  ///
+  // Remove the keyboard accelerator for the specified |command_id|.
+  ///
+  void(CEF_CALLBACK* remove_accelerator)(struct _cef_window_t* self,
+                                         int command_id);
+
+  ///
+  // Remove all keyboard accelerators.
+  ///
+  void(CEF_CALLBACK* remove_all_accelerators)(struct _cef_window_t* self);
+} cef_window_t;
+
+///
+// Create a new Window.
+///
+CEF_EXPORT cef_window_t* cef_window_create_top_level(
+    struct _cef_window_delegate_t* delegate);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_WINDOW_CAPI_H_
diff --git a/src/include/capi/views/cef_window_delegate_capi.h b/src/include/capi/views/cef_window_delegate_capi.h
new file mode 100644
index 0000000..c26f096
--- /dev/null
+++ b/src/include/capi/views/cef_window_delegate_capi.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=a0cec778fbaf0b1f5c9b3ef75dc7bbeeba777a44$
+//
+
+#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_WINDOW_DELEGATE_CAPI_H_
+#define CEF_INCLUDE_CAPI_VIEWS_CEF_WINDOW_DELEGATE_CAPI_H_
+#pragma once
+
+#include "include/capi/views/cef_panel_delegate_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _cef_window_t;
+
+///
+// Implement this structure to handle window events. The functions of this
+// structure will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+typedef struct _cef_window_delegate_t {
+  ///
+  // Base structure.
+  ///
+  cef_panel_delegate_t base;
+
+  ///
+  // Called when |window| is created.
+  ///
+  void(CEF_CALLBACK* on_window_created)(struct _cef_window_delegate_t* self,
+                                        struct _cef_window_t* window);
+
+  ///
+  // Called when |window| is destroyed. Release all references to |window| and
+  // do not attempt to execute any functions on |window| after this callback
+  // returns.
+  ///
+  void(CEF_CALLBACK* on_window_destroyed)(struct _cef_window_delegate_t* self,
+                                          struct _cef_window_t* window);
+
+  ///
+  // Return the parent for |window| or NULL if the |window| does not have a
+  // parent. Windows with parents will not get a taskbar button. Set |is_menu|
+  // to true (1) if |window| will be displayed as a menu, in which case it will
+  // not be clipped to the parent window bounds. Set |can_activate_menu| to
+  // false (0) if |is_menu| is true (1) and |window| should not be activated
+  // (given keyboard focus) when displayed.
+  ///
+  struct _cef_window_t*(CEF_CALLBACK* get_parent_window)(
+      struct _cef_window_delegate_t* self,
+      struct _cef_window_t* window,
+      int* is_menu,
+      int* can_activate_menu);
+
+  ///
+  // Return true (1) if |window| should be created without a frame or title bar.
+  // The window will be resizable if can_resize() returns true (1). Use
+  // cef_window_t::set_draggable_regions() to specify draggable regions.
+  ///
+  int(CEF_CALLBACK* is_frameless)(struct _cef_window_delegate_t* self,
+                                  struct _cef_window_t* window);
+
+  ///
+  // Return true (1) if |window| can be resized.
+  ///
+  int(CEF_CALLBACK* can_resize)(struct _cef_window_delegate_t* self,
+                                struct _cef_window_t* window);
+
+  ///
+  // Return true (1) if |window| can be maximized.
+  ///
+  int(CEF_CALLBACK* can_maximize)(struct _cef_window_delegate_t* self,
+                                  struct _cef_window_t* window);
+
+  ///
+  // Return true (1) if |window| can be minimized.
+  ///
+  int(CEF_CALLBACK* can_minimize)(struct _cef_window_delegate_t* self,
+                                  struct _cef_window_t* window);
+
+  ///
+  // Return true (1) if |window| can be closed. This will be called for user-
+  // initiated window close actions and when cef_window_t::close() is called.
+  ///
+  int(CEF_CALLBACK* can_close)(struct _cef_window_delegate_t* self,
+                               struct _cef_window_t* window);
+
+  ///
+  // Called when a keyboard accelerator registered with
+  // cef_window_t::SetAccelerator is triggered. Return true (1) if the
+  // accelerator was handled or false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* on_accelerator)(struct _cef_window_delegate_t* self,
+                                    struct _cef_window_t* window,
+                                    int command_id);
+
+  ///
+  // Called after all other controls in the window have had a chance to handle
+  // the event. |event| contains information about the keyboard event. Return
+  // true (1) if the keyboard event was handled or false (0) otherwise.
+  ///
+  int(CEF_CALLBACK* on_key_event)(struct _cef_window_delegate_t* self,
+                                  struct _cef_window_t* window,
+                                  const struct _cef_key_event_t* event);
+} cef_window_delegate_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_CAPI_VIEWS_CEF_WINDOW_DELEGATE_CAPI_H_
diff --git a/src/include/cef_accessibility_handler.h b/src/include/cef_accessibility_handler.h
new file mode 100644
index 0000000..c34e0c5
--- /dev/null
+++ b/src/include/cef_accessibility_handler.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_ACCESSIBILITY_HANDLER_H_
+#define CEF_INCLUDE_CEF_ACCESSIBILITY_HANDLER_H_
+#pragma once
+
+#include "include/cef_values.h"
+
+///
+// Implement this interface to receive accessibility notification when
+// accessibility events have been registered. The methods of this class will
+// be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefAccessibilityHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called after renderer process sends accessibility tree changes to the
+  // browser process.
+  ///
+  /*--cef()--*/
+  virtual void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) = 0;
+
+  ///
+  // Called after renderer process sends accessibility location changes to the
+  // browser process.
+  ///
+  /*--cef()--*/
+  virtual void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_ACCESSIBILITY_HANDLER_H_
diff --git a/src/include/cef_api_hash.h b/src/include/cef_api_hash.h
new file mode 100644
index 0000000..9595afd
--- /dev/null
+++ b/src/include/cef_api_hash.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the make_api_hash_header.py tool.
+//
+
+#ifndef CEF_INCLUDE_API_HASH_H_
+#define CEF_INCLUDE_API_HASH_H_
+
+#include "include/internal/cef_export.h"
+
+// The API hash is created by analyzing CEF header files for C API type
+// definitions. The hash value will change when header files are modified in a
+// way that may cause binary incompatibility with other builds. The universal
+// hash value will change if any platform is affected whereas the platform hash
+// values will change only if that particular platform is affected.
+#define CEF_API_HASH_UNIVERSAL "385d8af3c6da76f123f3aa292d3a3308ef456297"
+#if defined(OS_WIN)
+#define CEF_API_HASH_PLATFORM "4bb528b0bb076ed5177d6581cfca12dd7b2fee04"
+#elif defined(OS_MACOSX)
+#define CEF_API_HASH_PLATFORM "f6580fc0858f3580b7d25e1d094dd0a336132522"
+#elif defined(OS_LINUX)
+#define CEF_API_HASH_PLATFORM "83f3053840abba3b83e43ade58230f10b48591dd"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Returns CEF API hashes for the libcef library. The returned string is owned
+// by the library and should not be freed. The |entry| parameter describes which
+// hash value will be returned:
+// 0 - CEF_API_HASH_PLATFORM
+// 1 - CEF_API_HASH_UNIVERSAL
+// 2 - CEF_COMMIT_HASH (from cef_version.h)
+///
+CEF_EXPORT const char* cef_api_hash(int entry);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  // CEF_INCLUDE_API_HASH_H_
diff --git a/src/include/cef_app.h b/src/include/cef_app.h
new file mode 100644
index 0000000..56fb715
--- /dev/null
+++ b/src/include/cef_app.h
@@ -0,0 +1,203 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_APP_H_
+#define CEF_INCLUDE_CEF_APP_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser_process_handler.h"
+#include "include/cef_command_line.h"
+#include "include/cef_render_process_handler.h"
+#include "include/cef_resource_bundle_handler.h"
+#include "include/cef_scheme.h"
+
+class CefApp;
+
+///
+// This function should be called from the application entry point function to
+// execute a secondary process. It can be used to run secondary processes from
+// the browser client executable (default behavior) or from a separate
+// executable specified by the CefSettings.browser_subprocess_path value. If
+// called for the browser process (identified by no "type" command-line value)
+// it will return immediately with a value of -1. If called for a recognized
+// secondary process it will block until the process should exit and then return
+// the process exit code. The |application| parameter may be empty. The
+// |windows_sandbox_info| parameter is only used on Windows and may be NULL (see
+// cef_sandbox_win.h for details).
+///
+/*--cef(api_hash_check,optional_param=application,
+        optional_param=windows_sandbox_info)--*/
+int CefExecuteProcess(const CefMainArgs& args,
+                      CefRefPtr<CefApp> application,
+                      void* windows_sandbox_info);
+
+///
+// This function should be called on the main application thread to initialize
+// the CEF browser process. The |application| parameter may be empty. A return
+// value of true indicates that it succeeded and false indicates that it failed.
+// The |windows_sandbox_info| parameter is only used on Windows and may be NULL
+// (see cef_sandbox_win.h for details).
+///
+/*--cef(api_hash_check,optional_param=application,
+        optional_param=windows_sandbox_info)--*/
+bool CefInitialize(const CefMainArgs& args,
+                   const CefSettings& settings,
+                   CefRefPtr<CefApp> application,
+                   void* windows_sandbox_info);
+
+///
+// This function should be called on the main application thread to shut down
+// the CEF browser process before the application exits.
+///
+/*--cef()--*/
+void CefShutdown();
+
+///
+// Perform a single iteration of CEF message loop processing. This function is
+// provided for cases where the CEF message loop must be integrated into an
+// existing application message loop. Use of this function is not recommended
+// for most users; use either the CefRunMessageLoop() function or
+// CefSettings.multi_threaded_message_loop if possible. When using this function
+// care must be taken to balance performance against excessive CPU usage. It is
+// recommended to enable the CefSettings.external_message_pump option when using
+// this function so that CefBrowserProcessHandler::OnScheduleMessagePumpWork()
+// callbacks can facilitate the scheduling process. This function should only be
+// called on the main application thread and only if CefInitialize() is called
+// with a CefSettings.multi_threaded_message_loop value of false. This function
+// will not block.
+///
+/*--cef()--*/
+void CefDoMessageLoopWork();
+
+///
+// Run the CEF message loop. Use this function instead of an application-
+// provided message loop to get the best balance between performance and CPU
+// usage. This function should only be called on the main application thread and
+// only if CefInitialize() is called with a
+// CefSettings.multi_threaded_message_loop value of false. This function will
+// block until a quit message is received by the system.
+///
+/*--cef()--*/
+void CefRunMessageLoop();
+
+///
+// Quit the CEF message loop that was started by calling CefRunMessageLoop().
+// This function should only be called on the main application thread and only
+// if CefRunMessageLoop() was used.
+///
+/*--cef()--*/
+void CefQuitMessageLoop();
+
+///
+// Set to true before calling Windows APIs like TrackPopupMenu that enter a
+// modal message loop. Set to false after exiting the modal message loop.
+///
+/*--cef()--*/
+void CefSetOSModalLoop(bool osModalLoop);
+
+///
+// Call during process startup to enable High-DPI support on Windows 7 or newer.
+// Older versions of Windows should be left DPI-unaware because they do not
+// support DirectWrite and GDI fonts are kerned very badly.
+///
+/*--cef(capi_name=cef_enable_highdpi_support)--*/
+void CefEnableHighDPISupport();
+
+///
+// Implement this interface to provide handler implementations. Methods will be
+// called by the process and/or thread indicated.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefApp : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Provides an opportunity to view and/or modify command-line arguments before
+  // processing by CEF and Chromium. The |process_type| value will be empty for
+  // the browser process. Do not keep a reference to the CefCommandLine object
+  // passed to this method. The CefSettings.command_line_args_disabled value
+  // can be used to start with an empty command-line object. Any values
+  // specified in CefSettings that equate to command-line arguments will be set
+  // before this method is called. Be cautious when using this method to modify
+  // command-line arguments for non-browser processes as this may result in
+  // undefined behavior including crashes.
+  ///
+  /*--cef(optional_param=process_type)--*/
+  virtual void OnBeforeCommandLineProcessing(
+      const CefString& process_type,
+      CefRefPtr<CefCommandLine> command_line) {}
+
+  ///
+  // Provides an opportunity to register custom schemes. Do not keep a reference
+  // to the |registrar| object. This method is called on the main thread for
+  // each process and the registered schemes should be the same across all
+  // processes.
+  ///
+  /*--cef()--*/
+  virtual void OnRegisterCustomSchemes(
+      CefRawPtr<CefSchemeRegistrar> registrar) {}
+
+  ///
+  // Return the handler for resource bundle events. If
+  // CefSettings.pack_loading_disabled is true a handler must be returned. If no
+  // handler is returned resources will be loaded from pack files. This method
+  // is called by the browser and render processes on multiple threads.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefResourceBundleHandler> GetResourceBundleHandler() {
+    return nullptr;
+  }
+
+  ///
+  // Return the handler for functionality specific to the browser process. This
+  // method is called on multiple threads in the browser process.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() {
+    return nullptr;
+  }
+
+  ///
+  // Return the handler for functionality specific to the render process. This
+  // method is called on the render process main thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() {
+    return nullptr;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_APP_H_
diff --git a/src/include/cef_application_mac.h b/src/include/cef_application_mac.h
new file mode 100644
index 0000000..e2858c4
--- /dev/null
+++ b/src/include/cef_application_mac.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_CEF_APPLICATION_MAC_H_
+#define CEF_INCLUDE_CEF_APPLICATION_MAC_H_
+#pragma once
+
+#ifdef __cplusplus
+#include "include/cef_base.h"
+#endif  // __cplusplus
+
+#if defined(OS_MACOSX) && defined(__OBJC__)
+
+#ifdef USING_CHROMIUM_INCLUDES
+
+// Use the existing CrAppControlProtocol definition.
+#import "base/mac/scoped_sending_event.h"
+
+// Use the existing CrAppProtocol definition.
+#import "base/message_loop/message_pump_mac.h"
+
+// Use the existing UnderlayableSurface definition.
+#import "ui/base/cocoa/underlay_opengl_hosting_window.h"
+
+// Use the existing empty protocol definitions.
+#import "base/mac/cocoa_protocols.h"
+
+// Use the existing empty protocol definitions.
+#import "base/mac/sdk_forward_declarations.h"
+
+#else  // USING_CHROMIUM_INCLUDES
+
+#import <AppKit/AppKit.h>
+#import <Cocoa/Cocoa.h>
+
+// Copy of definition from base/message_loop/message_pump_mac.h.
+@protocol CrAppProtocol
+// Must return true if -[NSApplication sendEvent:] is currently on the stack.
+- (BOOL)isHandlingSendEvent;
+@end
+
+// Copy of definition from base/mac/scoped_sending_event.h.
+@protocol CrAppControlProtocol<CrAppProtocol>
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent;
+@end
+
+// Copy of definition from ui/base/cocoa/underlay_opengl_hosting_window.h.
+// Common base class for windows that host a OpenGL surface that renders under
+// the window. Contains methods relating to hole punching so that the OpenGL
+// surface is visible through the window.
+@interface UnderlayOpenGLHostingWindow : NSWindow
+@end
+
+#endif  // USING_CHROMIUM_INCLUDES
+
+// All CEF client applications must subclass NSApplication and implement this
+// protocol.
+@protocol CefAppProtocol<CrAppControlProtocol>
+@end
+
+#ifdef __cplusplus
+
+// Controls the state of |isHandlingSendEvent| in the event loop so that it is
+// reset properly.
+class CefScopedSendingEvent {
+ public:
+  CefScopedSendingEvent()
+      : app_(static_cast<NSApplication<CefAppProtocol>*>(
+            [NSApplication sharedApplication])),
+        handling_([app_ isHandlingSendEvent]) {
+    [app_ setHandlingSendEvent:YES];
+  }
+  ~CefScopedSendingEvent() { [app_ setHandlingSendEvent:handling_]; }
+
+ private:
+  NSApplication<CefAppProtocol>* app_;
+  BOOL handling_;
+};
+
+#endif  // __cplusplus
+
+#endif  // defined(OS_MACOSX) && defined(__OBJC__)
+
+#endif  // CEF_INCLUDE_CEF_APPLICATION_MAC_H_
diff --git a/src/include/cef_audio_handler.h b/src/include/cef_audio_handler.h
new file mode 100644
index 0000000..f581e6f
--- /dev/null
+++ b/src/include/cef_audio_handler.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2019 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_AUDIO_HANDLER_H_
+#define CEF_INCLUDE_CEF_AUDIO_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+
+///
+// Implement this interface to handle audio events.
+///
+/*--cef(source=client)--*/
+class CefAudioHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_channel_layout_t ChannelLayout;
+
+  ///
+  // Called on the UI thread to allow configuration of audio stream parameters.
+  // Return true to proceed with audio stream capture, or false to cancel it.
+  // All members of |params| can optionally be configured here, but they are
+  // also pre-filled with some sensible defaults.
+  ///
+  /*--cef()--*/
+  virtual bool GetAudioParameters(CefRefPtr<CefBrowser> browser,
+                                  CefAudioParameters& params) {
+    return true;
+  }
+
+  ///
+  // Called on a browser audio capture thread when the browser starts
+  // streaming audio. OnAudioSteamStopped will always be called after
+  // OnAudioStreamStarted; both methods may be called multiple times
+  // for the same browser. |params| contains the audio parameters like
+  // sample rate and channel layout. |channels| is the number of channels.
+  ///
+  /*--cef()--*/
+  virtual void OnAudioStreamStarted(CefRefPtr<CefBrowser> browser,
+                                    const CefAudioParameters& params,
+                                    int channels) = 0;
+
+  ///
+  // Called on the audio stream thread when a PCM packet is received for the
+  // stream. |data| is an array representing the raw PCM data as a floating
+  // point type, i.e. 4-byte value(s). |frames| is the number of frames in the
+  // PCM packet. |pts| is the presentation timestamp (in milliseconds since the
+  // Unix Epoch) and represents the time at which the decompressed packet should
+  // be presented to the user. Based on |frames| and the |channel_layout| value
+  // passed to OnAudioStreamStarted you can calculate the size of the |data|
+  // array in bytes.
+  ///
+  /*--cef()--*/
+  virtual void OnAudioStreamPacket(CefRefPtr<CefBrowser> browser,
+                                   const float** data,
+                                   int frames,
+                                   int64 pts) = 0;
+
+  ///
+  // Called on the UI thread when the stream has stopped. OnAudioSteamStopped
+  // will always be called after OnAudioStreamStarted; both methods may be
+  // called multiple times for the same stream.
+  ///
+  /*--cef()--*/
+  virtual void OnAudioStreamStopped(CefRefPtr<CefBrowser> browser) = 0;
+
+  ///
+  // Called on the UI or audio stream thread when an error occurred. During the
+  // stream creation phase this callback will be called on the UI thread while
+  // in the capturing phase it will be called on the audio stream thread. The
+  // stream will be stopped immediately.
+  ///
+  /*--cef()--*/
+  virtual void OnAudioStreamError(CefRefPtr<CefBrowser> browser,
+                                  const CefString& message) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_AUDIO_HANDLER_H_
diff --git a/src/include/cef_auth_callback.h b/src/include/cef_auth_callback.h
new file mode 100644
index 0000000..fc8a10a
--- /dev/null
+++ b/src/include/cef_auth_callback.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_AUTH_CALLBACK_H_
+#define CEF_INCLUDE_CEF_AUTH_CALLBACK_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Callback interface used for asynchronous continuation of authentication
+// requests.
+///
+/*--cef(source=library)--*/
+class CefAuthCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Continue the authentication request.
+  ///
+  /*--cef(capi_name=cont,optional_param=username,optional_param=password)--*/
+  virtual void Continue(const CefString& username,
+                        const CefString& password) = 0;
+
+  ///
+  // Cancel the authentication request.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_AUTH_CALLBACK_H_
diff --git a/src/include/cef_base.h b/src/include/cef_base.h
new file mode 100644
index 0000000..0db0f3a
--- /dev/null
+++ b/src/include/cef_base.h
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_CEF_BASE_H_
+#define CEF_INCLUDE_CEF_BASE_H_
+#pragma once
+
+#include "include/base/cef_atomic_ref_count.h"
+#include "include/base/cef_build.h"
+#include "include/base/cef_macros.h"
+
+// Bring in common C++ type definitions used by CEF consumers.
+#include "include/internal/cef_ptr.h"
+#include "include/internal/cef_types_wrappers.h"
+#if defined(OS_WIN)
+#include "include/internal/cef_win.h"
+#elif defined(OS_MACOSX)
+#include "include/internal/cef_mac.h"
+#elif defined(OS_LINUX)
+#include "include/internal/cef_linux.h"
+#endif
+
+///
+// All ref-counted framework classes must extend this class.
+///
+class CefBaseRefCounted {
+ public:
+  ///
+  // Called to increment the reference count for the object. Should be called
+  // for every new copy of a pointer to a given object.
+  ///
+  virtual void AddRef() const = 0;
+
+  ///
+  // Called to decrement the reference count for the object. Returns true if
+  // the reference count is 0, in which case the object should self-delete.
+  ///
+  virtual bool Release() const = 0;
+
+  ///
+  // Returns true if the reference count is 1.
+  ///
+  virtual bool HasOneRef() const = 0;
+
+  ///
+  // Returns true if the reference count is at least 1.
+  ///
+  virtual bool HasAtLeastOneRef() const = 0;
+
+ protected:
+  virtual ~CefBaseRefCounted() {}
+};
+
+///
+// All scoped framework classes must extend this class.
+///
+class CefBaseScoped {
+ public:
+  virtual ~CefBaseScoped() {}
+};
+
+///
+// Class that implements atomic reference counting.
+///
+class CefRefCount {
+ public:
+  CefRefCount() : ref_count_(0) {}
+
+  ///
+  // Increment the reference count.
+  ///
+  void AddRef() const { base::AtomicRefCountInc(&ref_count_); }
+
+  ///
+  // Decrement the reference count. Returns true if the reference count is 0.
+  ///
+  bool Release() const { return !base::AtomicRefCountDec(&ref_count_); }
+
+  ///
+  // Returns true if the reference count is 1.
+  ///
+  bool HasOneRef() const { return base::AtomicRefCountIsOne(&ref_count_); }
+
+  ///
+  // Returns true if the reference count is at least 1.
+  ///
+  bool HasAtLeastOneRef() const {
+    return !base::AtomicRefCountIsZero(&ref_count_);
+  }
+
+ private:
+  mutable base::AtomicRefCount ref_count_;
+  DISALLOW_COPY_AND_ASSIGN(CefRefCount);
+};
+
+///
+// Macro that provides a reference counting implementation for classes extending
+// CefBase.
+///
+#define IMPLEMENT_REFCOUNTING(ClassName)                             \
+ public:                                                             \
+  void AddRef() const OVERRIDE { ref_count_.AddRef(); }              \
+  bool Release() const OVERRIDE {                                    \
+    if (ref_count_.Release()) {                                      \
+      delete static_cast<const ClassName*>(this);                    \
+      return true;                                                   \
+    }                                                                \
+    return false;                                                    \
+  }                                                                  \
+  bool HasOneRef() const OVERRIDE { return ref_count_.HasOneRef(); } \
+  bool HasAtLeastOneRef() const OVERRIDE {                           \
+    return ref_count_.HasAtLeastOneRef();                            \
+  }                                                                  \
+                                                                     \
+ private:                                                            \
+  CefRefCount ref_count_
+
+///
+// Macro that provides a locking implementation. Use the Lock() and Unlock()
+// methods to protect a section of code from simultaneous access by multiple
+// threads. The AutoLock class is a helper that will hold the lock while in
+// scope.
+//
+// THIS MACRO IS DEPRECATED. Use an explicit base::Lock member variable and
+// base::AutoLock instead. For example:
+//
+// #include "include/base/cef_lock.h"
+//
+// // Class declaration.
+// class MyClass : public CefBaseRefCounted {
+//  public:
+//   MyClass() : value_(0) {}
+//   // Method that may be called on multiple threads.
+//   void IncrementValue();
+//  private:
+//   // Value that may be accessed on multiple theads.
+//   int value_;
+//   // Lock used to protect access to |value_|.
+//   base::Lock lock_;
+//   IMPLEMENT_REFCOUNTING(MyClass);
+// };
+//
+// // Class implementation.
+// void MyClass::IncrementValue() {
+//   // Acquire the lock for the scope of this method.
+//   base::AutoLock lock_scope(lock_);
+//   // |value_| can now be modified safely.
+//   value_++;
+// }
+///
+#define IMPLEMENT_LOCKING(ClassName)                                    \
+ public:                                                                \
+  class AutoLock {                                                      \
+   public:                                                              \
+    explicit AutoLock(ClassName* base) : base_(base) { base_->Lock(); } \
+    ~AutoLock() { base_->Unlock(); }                                    \
+                                                                        \
+   private:                                                             \
+    ClassName* base_;                                                   \
+    DISALLOW_COPY_AND_ASSIGN(AutoLock);                                 \
+  };                                                                    \
+  void Lock() { lock_.Acquire(); }                                      \
+  void Unlock() { lock_.Release(); }                                    \
+                                                                        \
+ private:                                                               \
+  base::Lock lock_;
+
+#endif  // CEF_INCLUDE_CEF_BASE_H_
diff --git a/src/include/cef_browser.h b/src/include/cef_browser.h
new file mode 100644
index 0000000..172d0a1
--- /dev/null
+++ b/src/include/cef_browser.h
@@ -0,0 +1,953 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_BROWSER_H_
+#define CEF_INCLUDE_CEF_BROWSER_H_
+#pragma once
+
+#include <vector>
+#include "include/cef_base.h"
+#include "include/cef_devtools_message_observer.h"
+#include "include/cef_drag_data.h"
+#include "include/cef_frame.h"
+#include "include/cef_image.h"
+#include "include/cef_navigation_entry.h"
+#include "include/cef_registration.h"
+#include "include/cef_request_context.h"
+
+class CefBrowserHost;
+class CefClient;
+
+///
+// Class used to represent a browser window. When used in the browser process
+// the methods of this class may be called on any thread unless otherwise
+// indicated in the comments. When used in the render process the methods of
+// this class may only be called on the main thread.
+///
+/*--cef(source=library)--*/
+class CefBrowser : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the browser host object. This method can only be called in the
+  // browser process.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowserHost> GetHost() = 0;
+
+  ///
+  // Returns true if the browser can navigate backwards.
+  ///
+  /*--cef()--*/
+  virtual bool CanGoBack() = 0;
+
+  ///
+  // Navigate backwards.
+  ///
+  /*--cef()--*/
+  virtual void GoBack() = 0;
+
+  ///
+  // Returns true if the browser can navigate forwards.
+  ///
+  /*--cef()--*/
+  virtual bool CanGoForward() = 0;
+
+  ///
+  // Navigate forwards.
+  ///
+  /*--cef()--*/
+  virtual void GoForward() = 0;
+
+  ///
+  // Returns true if the browser is currently loading.
+  ///
+  /*--cef()--*/
+  virtual bool IsLoading() = 0;
+
+  ///
+  // Reload the current page.
+  ///
+  /*--cef()--*/
+  virtual void Reload() = 0;
+
+  ///
+  // Reload the current page ignoring any cached data.
+  ///
+  /*--cef()--*/
+  virtual void ReloadIgnoreCache() = 0;
+
+  ///
+  // Stop loading the page.
+  ///
+  /*--cef()--*/
+  virtual void StopLoad() = 0;
+
+  ///
+  // Returns the globally unique identifier for this browser. This value is also
+  // used as the tabId for extension APIs.
+  ///
+  /*--cef()--*/
+  virtual int GetIdentifier() = 0;
+
+  ///
+  // Returns true if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefBrowser> that) = 0;
+
+  ///
+  // Returns true if the window is a popup window.
+  ///
+  /*--cef()--*/
+  virtual bool IsPopup() = 0;
+
+  ///
+  // Returns true if a document has been loaded in the browser.
+  ///
+  /*--cef()--*/
+  virtual bool HasDocument() = 0;
+
+  ///
+  // Returns the main (top-level) frame for the browser window.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFrame> GetMainFrame() = 0;
+
+  ///
+  // Returns the focused frame for the browser window.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFrame> GetFocusedFrame() = 0;
+
+  ///
+  // Returns the frame with the specified identifier, or NULL if not found.
+  ///
+  /*--cef(capi_name=get_frame_byident)--*/
+  virtual CefRefPtr<CefFrame> GetFrame(int64 identifier) = 0;
+
+  ///
+  // Returns the frame with the specified name, or NULL if not found.
+  ///
+  /*--cef(optional_param=name)--*/
+  virtual CefRefPtr<CefFrame> GetFrame(const CefString& name) = 0;
+
+  ///
+  // Returns the number of frames that currently exist.
+  ///
+  /*--cef()--*/
+  virtual size_t GetFrameCount() = 0;
+
+  ///
+  // Returns the identifiers of all existing frames.
+  ///
+  /*--cef(count_func=identifiers:GetFrameCount)--*/
+  virtual void GetFrameIdentifiers(std::vector<int64>& identifiers) = 0;
+
+  ///
+  // Returns the names of all existing frames.
+  ///
+  /*--cef()--*/
+  virtual void GetFrameNames(std::vector<CefString>& names) = 0;
+};
+
+///
+// Callback interface for CefBrowserHost::RunFileDialog. The methods of this
+// class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefRunFileDialogCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called asynchronously after the file dialog is dismissed.
+  // |selected_accept_filter| is the 0-based index of the value selected from
+  // the accept filters array passed to CefBrowserHost::RunFileDialog.
+  // |file_paths| will be a single value or a list of values depending on the
+  // dialog mode. If the selection was cancelled |file_paths| will be empty.
+  ///
+  /*--cef(index_param=selected_accept_filter,optional_param=file_paths)--*/
+  virtual void OnFileDialogDismissed(
+      int selected_accept_filter,
+      const std::vector<CefString>& file_paths) = 0;
+};
+
+///
+// Callback interface for CefBrowserHost::GetNavigationEntries. The methods of
+// this class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefNavigationEntryVisitor : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be executed. Do not keep a reference to |entry| outside of
+  // this callback. Return true to continue visiting entries or false to stop.
+  // |current| is true if this entry is the currently loaded navigation entry.
+  // |index| is the 0-based index of this entry and |total| is the total number
+  // of entries.
+  ///
+  /*--cef()--*/
+  virtual bool Visit(CefRefPtr<CefNavigationEntry> entry,
+                     bool current,
+                     int index,
+                     int total) = 0;
+};
+
+///
+// Callback interface for CefBrowserHost::PrintToPDF. The methods of this class
+// will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefPdfPrintCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be executed when the PDF printing has completed. |path|
+  // is the output path. |ok| will be true if the printing completed
+  // successfully or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual void OnPdfPrintFinished(const CefString& path, bool ok) = 0;
+};
+
+///
+// Callback interface for CefBrowserHost::DownloadImage. The methods of this
+// class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefDownloadImageCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be executed when the image download has completed.
+  // |image_url| is the URL that was downloaded and |http_status_code| is the
+  // resulting HTTP status code. |image| is the resulting image, possibly at
+  // multiple scale factors, or empty if the download failed.
+  ///
+  /*--cef(optional_param=image)--*/
+  virtual void OnDownloadImageFinished(const CefString& image_url,
+                                       int http_status_code,
+                                       CefRefPtr<CefImage> image) = 0;
+};
+
+///
+// Class used to represent the browser process aspects of a browser window. The
+// methods of this class can only be called in the browser process. They may be
+// called on any thread in that process unless otherwise indicated in the
+// comments.
+///
+/*--cef(source=library)--*/
+class CefBrowserHost : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_drag_operations_mask_t DragOperationsMask;
+  typedef cef_file_dialog_mode_t FileDialogMode;
+  typedef cef_mouse_button_type_t MouseButtonType;
+  typedef cef_paint_element_type_t PaintElementType;
+
+  ///
+  // Create a new browser window using the window parameters specified by
+  // |windowInfo|. All values will be copied internally and the actual window
+  // will be created on the UI thread. If |request_context| is empty the
+  // global request context will be used. This method can be called on any
+  // browser process thread and will not block. The optional |extra_info|
+  // parameter provides an opportunity to specify extra information specific
+  // to the created browser that will be passed to
+  // CefRenderProcessHandler::OnBrowserCreated() in the render process.
+  ///
+  /*--cef(optional_param=client,optional_param=url,
+          optional_param=request_context,optional_param=extra_info)--*/
+  static bool CreateBrowser(const CefWindowInfo& windowInfo,
+                            CefRefPtr<CefClient> client,
+                            const CefString& url,
+                            const CefBrowserSettings& settings,
+                            CefRefPtr<CefDictionaryValue> extra_info,
+                            CefRefPtr<CefRequestContext> request_context);
+
+  ///
+  // Create a new browser window using the window parameters specified by
+  // |windowInfo|. If |request_context| is empty the global request context
+  // will be used. This method can only be called on the browser process UI
+  // thread. The optional |extra_info| parameter provides an opportunity to
+  // specify extra information specific to the created browser that will be
+  // passed to CefRenderProcessHandler::OnBrowserCreated() in the render
+  // process.
+  ///
+  /*--cef(optional_param=client,optional_param=url,
+          optional_param=request_context,optional_param=extra_info)--*/
+  static CefRefPtr<CefBrowser> CreateBrowserSync(
+      const CefWindowInfo& windowInfo,
+      CefRefPtr<CefClient> client,
+      const CefString& url,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefDictionaryValue> extra_info,
+      CefRefPtr<CefRequestContext> request_context);
+
+  ///
+  // Returns the hosted browser object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
+
+  ///
+  // Request that the browser close. The JavaScript 'onbeforeunload' event will
+  // be fired. If |force_close| is false the event handler, if any, will be
+  // allowed to prompt the user and the user can optionally cancel the close.
+  // If |force_close| is true the prompt will not be displayed and the close
+  // will proceed. Results in a call to CefLifeSpanHandler::DoClose() if the
+  // event handler allows the close or if |force_close| is true. See
+  // CefLifeSpanHandler::DoClose() documentation for additional usage
+  // information.
+  ///
+  /*--cef()--*/
+  virtual void CloseBrowser(bool force_close) = 0;
+
+  ///
+  // Helper for closing a browser. Call this method from the top-level window
+  // close handler. Internally this calls CloseBrowser(false) if the close has
+  // not yet been initiated. This method returns false while the close is
+  // pending and true after the close has completed. See CloseBrowser() and
+  // CefLifeSpanHandler::DoClose() documentation for additional usage
+  // information. This method must be called on the browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool TryCloseBrowser() = 0;
+
+  ///
+  // Set whether the browser is focused.
+  ///
+  /*--cef()--*/
+  virtual void SetFocus(bool focus) = 0;
+
+  ///
+  // Retrieve the window handle for this browser. If this browser is wrapped in
+  // a CefBrowserView this method should be called on the browser process UI
+  // thread and it will return the handle for the top-level native window.
+  ///
+  /*--cef()--*/
+  virtual CefWindowHandle GetWindowHandle() = 0;
+
+  ///
+  // Retrieve the window handle of the browser that opened this browser. Will
+  // return NULL for non-popup windows or if this browser is wrapped in a
+  // CefBrowserView. This method can be used in combination with custom handling
+  // of modal windows.
+  ///
+  /*--cef()--*/
+  virtual CefWindowHandle GetOpenerWindowHandle() = 0;
+
+  ///
+  // Returns true if this browser is wrapped in a CefBrowserView.
+  ///
+  /*--cef()--*/
+  virtual bool HasView() = 0;
+
+  ///
+  // Returns the client for this browser.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefClient> GetClient() = 0;
+
+  ///
+  // Returns the request context for this browser.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRequestContext> GetRequestContext() = 0;
+
+  ///
+  // Get the current zoom level. The default zoom level is 0.0. This method can
+  // only be called on the UI thread.
+  ///
+  /*--cef()--*/
+  virtual double GetZoomLevel() = 0;
+
+  ///
+  // Change the zoom level to the specified value. Specify 0.0 to reset the
+  // zoom level. If called on the UI thread the change will be applied
+  // immediately. Otherwise, the change will be applied asynchronously on the
+  // UI thread.
+  ///
+  /*--cef()--*/
+  virtual void SetZoomLevel(double zoomLevel) = 0;
+
+  ///
+  // Call to run a file chooser dialog. Only a single file chooser dialog may be
+  // pending at any given time. |mode| represents the type of dialog to display.
+  // |title| to the title to be used for the dialog and may be empty to show the
+  // default title ("Open" or "Save" depending on the mode). |default_file_path|
+  // is the path with optional directory and/or file name component that will be
+  // initially selected in the dialog. |accept_filters| are used to restrict the
+  // selectable file types and may any combination of (a) valid lower-cased MIME
+  // types (e.g. "text/*" or "image/*"), (b) individual file extensions (e.g.
+  // ".txt" or ".png"), or (c) combined description and file extension delimited
+  // using "|" and ";" (e.g. "Image Types|.png;.gif;.jpg").
+  // |selected_accept_filter| is the 0-based index of the filter that will be
+  // selected by default. |callback| will be executed after the dialog is
+  // dismissed or immediately if another dialog is already pending. The dialog
+  // will be initiated asynchronously on the UI thread.
+  ///
+  /*--cef(optional_param=title,optional_param=default_file_path,
+          optional_param=accept_filters,index_param=selected_accept_filter)--*/
+  virtual void RunFileDialog(FileDialogMode mode,
+                             const CefString& title,
+                             const CefString& default_file_path,
+                             const std::vector<CefString>& accept_filters,
+                             int selected_accept_filter,
+                             CefRefPtr<CefRunFileDialogCallback> callback) = 0;
+
+  ///
+  // Download the file at |url| using CefDownloadHandler.
+  ///
+  /*--cef()--*/
+  virtual void StartDownload(const CefString& url) = 0;
+
+  ///
+  // Download |image_url| and execute |callback| on completion with the images
+  // received from the renderer. If |is_favicon| is true then cookies are not
+  // sent and not accepted during download. Images with density independent
+  // pixel (DIP) sizes larger than |max_image_size| are filtered out from the
+  // image results. Versions of the image at different scale factors may be
+  // downloaded up to the maximum scale factor supported by the system. If there
+  // are no image results <= |max_image_size| then the smallest image is resized
+  // to |max_image_size| and is the only result. A |max_image_size| of 0 means
+  // unlimited. If |bypass_cache| is true then |image_url| is requested from the
+  // server even if it is present in the browser cache.
+  ///
+  /*--cef()--*/
+  virtual void DownloadImage(const CefString& image_url,
+                             bool is_favicon,
+                             uint32 max_image_size,
+                             bool bypass_cache,
+                             CefRefPtr<CefDownloadImageCallback> callback) = 0;
+
+  ///
+  // Print the current browser contents.
+  ///
+  /*--cef()--*/
+  virtual void Print() = 0;
+
+  ///
+  // Print the current browser contents to the PDF file specified by |path| and
+  // execute |callback| on completion. The caller is responsible for deleting
+  // |path| when done. For PDF printing to work on Linux you must implement the
+  // CefPrintHandler::GetPdfPaperSize method.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual void PrintToPDF(const CefString& path,
+                          const CefPdfPrintSettings& settings,
+                          CefRefPtr<CefPdfPrintCallback> callback) = 0;
+
+  ///
+  // Search for |searchText|. |identifier| must be a unique ID and these IDs
+  // must strictly increase so that newer requests always have greater IDs than
+  // older requests. If |identifier| is zero or less than the previous ID value
+  // then it will be automatically assigned a new valid ID. |forward| indicates
+  // whether to search forward or backward within the page. |matchCase|
+  // indicates whether the search should be case-sensitive. |findNext| indicates
+  // whether this is the first request or a follow-up. The CefFindHandler
+  // instance, if any, returned via CefClient::GetFindHandler will be called to
+  // report find results.
+  ///
+  /*--cef()--*/
+  virtual void Find(int identifier,
+                    const CefString& searchText,
+                    bool forward,
+                    bool matchCase,
+                    bool findNext) = 0;
+
+  ///
+  // Cancel all searches that are currently going on.
+  ///
+  /*--cef()--*/
+  virtual void StopFinding(bool clearSelection) = 0;
+
+  ///
+  // Open developer tools (DevTools) in its own browser. The DevTools browser
+  // will remain associated with this browser. If the DevTools browser is
+  // already open then it will be focused, in which case the |windowInfo|,
+  // |client| and |settings| parameters will be ignored. If |inspect_element_at|
+  // is non-empty then the element at the specified (x,y) location will be
+  // inspected. The |windowInfo| parameter will be ignored if this browser is
+  // wrapped in a CefBrowserView.
+  ///
+  /*--cef(optional_param=windowInfo,optional_param=client,
+          optional_param=settings,optional_param=inspect_element_at)--*/
+  virtual void ShowDevTools(const CefWindowInfo& windowInfo,
+                            CefRefPtr<CefClient> client,
+                            const CefBrowserSettings& settings,
+                            const CefPoint& inspect_element_at) = 0;
+
+  ///
+  // Explicitly close the associated DevTools browser, if any.
+  ///
+  /*--cef()--*/
+  virtual void CloseDevTools() = 0;
+
+  ///
+  // Returns true if this browser currently has an associated DevTools browser.
+  // Must be called on the browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool HasDevTools() = 0;
+
+  ///
+  // Send a method call message over the DevTools protocol. |message| must be a
+  // UTF8-encoded JSON dictionary that contains "id" (int), "method" (string)
+  // and "params" (dictionary, optional) values. See the DevTools protocol
+  // documentation at https://chromedevtools.github.io/devtools-protocol/ for
+  // details of supported methods and the expected "params" dictionary contents.
+  // |message| will be copied if necessary. This method will return true if
+  // called on the UI thread and the message was successfully submitted for
+  // validation, otherwise false. Validation will be applied asynchronously and
+  // any messages that fail due to formatting errors or missing parameters may
+  // be discarded without notification. Prefer ExecuteDevToolsMethod if a more
+  // structured approach to message formatting is desired.
+  //
+  // Every valid method call will result in an asynchronous method result or
+  // error message that references the sent message "id". Event messages are
+  // received while notifications are enabled (for example, between method calls
+  // for "Page.enable" and "Page.disable"). All received messages will be
+  // delivered to the observer(s) registered with AddDevToolsMessageObserver.
+  // See CefDevToolsMessageObserver::OnDevToolsMessage documentation for details
+  // of received message contents.
+  //
+  // Usage of the SendDevToolsMessage, ExecuteDevToolsMethod and
+  // AddDevToolsMessageObserver methods does not require an active DevTools
+  // front-end or remote-debugging session. Other active DevTools sessions will
+  // continue to function independently. However, any modification of global
+  // browser state by one session may not be reflected in the UI of other
+  // sessions.
+  //
+  // Communication with the DevTools front-end (when displayed) can be logged
+  // for development purposes by passing the
+  // `--devtools-protocol-log-file=<path>` command-line flag.
+  ///
+  /*--cef()--*/
+  virtual bool SendDevToolsMessage(const void* message,
+                                   size_t message_size) = 0;
+
+  ///
+  // Execute a method call over the DevTools protocol. This is a more structured
+  // version of SendDevToolsMessage. |message_id| is an incremental number that
+  // uniquely identifies the message (pass 0 to have the next number assigned
+  // automatically based on previous values). |method| is the method name.
+  // |params| are the method parameters, which may be empty. See the DevTools
+  // protocol documentation (linked above) for details of supported methods and
+  // the expected |params| dictionary contents. This method will return the
+  // assigned message ID if called on the UI thread and the message was
+  // successfully submitted for validation, otherwise 0. See the
+  // SendDevToolsMessage documentation for additional usage information.
+  ///
+  /*--cef(optional_param=params)--*/
+  virtual int ExecuteDevToolsMethod(int message_id,
+                                    const CefString& method,
+                                    CefRefPtr<CefDictionaryValue> params) = 0;
+
+  ///
+  // Add an observer for DevTools protocol messages (method results and events).
+  // The observer will remain registered until the returned Registration object
+  // is destroyed. See the SendDevToolsMessage documentation for additional
+  // usage information.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRegistration> AddDevToolsMessageObserver(
+      CefRefPtr<CefDevToolsMessageObserver> observer) = 0;
+
+  ///
+  // Retrieve a snapshot of current navigation entries as values sent to the
+  // specified visitor. If |current_only| is true only the current navigation
+  // entry will be sent, otherwise all navigation entries will be sent.
+  ///
+  /*--cef()--*/
+  virtual void GetNavigationEntries(
+      CefRefPtr<CefNavigationEntryVisitor> visitor,
+      bool current_only) = 0;
+
+  ///
+  // Set whether mouse cursor change is disabled.
+  ///
+  /*--cef()--*/
+  virtual void SetMouseCursorChangeDisabled(bool disabled) = 0;
+
+  ///
+  // Returns true if mouse cursor change is disabled.
+  ///
+  /*--cef()--*/
+  virtual bool IsMouseCursorChangeDisabled() = 0;
+
+  ///
+  // If a misspelled word is currently selected in an editable node calling
+  // this method will replace it with the specified |word|.
+  ///
+  /*--cef()--*/
+  virtual void ReplaceMisspelling(const CefString& word) = 0;
+
+  ///
+  // Add the specified |word| to the spelling dictionary.
+  ///
+  /*--cef()--*/
+  virtual void AddWordToDictionary(const CefString& word) = 0;
+
+  ///
+  // Returns true if window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual bool IsWindowRenderingDisabled() = 0;
+
+  ///
+  // Notify the browser that the widget has been resized. The browser will first
+  // call CefRenderHandler::GetViewRect to get the new size and then call
+  // CefRenderHandler::OnPaint asynchronously with the updated regions. This
+  // method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void WasResized() = 0;
+
+  ///
+  // Notify the browser that it has been hidden or shown. Layouting and
+  // CefRenderHandler::OnPaint notification will stop when the browser is
+  // hidden. This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void WasHidden(bool hidden) = 0;
+
+  ///
+  // Send a notification to the browser that the screen info has changed. The
+  // browser will then call CefRenderHandler::GetScreenInfo to update the
+  // screen information with the new values. This simulates moving the webview
+  // window from one display to another, or changing the properties of the
+  // current display. This method is only used when window rendering is
+  // disabled.
+  ///
+  /*--cef()--*/
+  virtual void NotifyScreenInfoChanged() = 0;
+
+  ///
+  // Invalidate the view. The browser will call CefRenderHandler::OnPaint
+  // asynchronously. This method is only used when window rendering is
+  // disabled.
+  ///
+  /*--cef()--*/
+  virtual void Invalidate(PaintElementType type) = 0;
+
+  ///
+  // Issue a BeginFrame request to Chromium.  Only valid when
+  // CefWindowInfo::external_begin_frame_enabled is set to true.
+  ///
+  /*--cef()--*/
+  virtual void SendExternalBeginFrame() = 0;
+
+  ///
+  // Send a key event to the browser.
+  ///
+  /*--cef()--*/
+  virtual void SendKeyEvent(const CefKeyEvent& event) = 0;
+
+  ///
+  // Send a mouse click event to the browser. The |x| and |y| coordinates are
+  // relative to the upper-left corner of the view.
+  ///
+  /*--cef()--*/
+  virtual void SendMouseClickEvent(const CefMouseEvent& event,
+                                   MouseButtonType type,
+                                   bool mouseUp,
+                                   int clickCount) = 0;
+
+  ///
+  // Send a mouse move event to the browser. The |x| and |y| coordinates are
+  // relative to the upper-left corner of the view.
+  ///
+  /*--cef()--*/
+  virtual void SendMouseMoveEvent(const CefMouseEvent& event,
+                                  bool mouseLeave) = 0;
+
+  ///
+  // Send a mouse wheel event to the browser. The |x| and |y| coordinates are
+  // relative to the upper-left corner of the view. The |deltaX| and |deltaY|
+  // values represent the movement delta in the X and Y directions respectively.
+  // In order to scroll inside select popups with window rendering disabled
+  // CefRenderHandler::GetScreenPoint should be implemented properly.
+  ///
+  /*--cef()--*/
+  virtual void SendMouseWheelEvent(const CefMouseEvent& event,
+                                   int deltaX,
+                                   int deltaY) = 0;
+
+  ///
+  // Send a touch event to the browser for a windowless browser.
+  ///
+  /*--cef()--*/
+  virtual void SendTouchEvent(const CefTouchEvent& event) = 0;
+
+  ///
+  // Send a focus event to the browser.
+  ///
+  /*--cef()--*/
+  virtual void SendFocusEvent(bool setFocus) = 0;
+
+  ///
+  // Send a capture lost event to the browser.
+  ///
+  /*--cef()--*/
+  virtual void SendCaptureLostEvent() = 0;
+
+  ///
+  // Notify the browser that the window hosting it is about to be moved or
+  // resized. This method is only used on Windows and Linux.
+  ///
+  /*--cef()--*/
+  virtual void NotifyMoveOrResizeStarted() = 0;
+
+  ///
+  // Returns the maximum rate in frames per second (fps) that CefRenderHandler::
+  // OnPaint will be called for a windowless browser. The actual fps may be
+  // lower if the browser cannot generate frames at the requested rate. The
+  // minimum value is 1 and the maximum value is 60 (default 30). This method
+  // can only be called on the UI thread.
+  ///
+  /*--cef()--*/
+  virtual int GetWindowlessFrameRate() = 0;
+
+  ///
+  // Set the maximum rate in frames per second (fps) that CefRenderHandler::
+  // OnPaint will be called for a windowless browser. The actual fps may be
+  // lower if the browser cannot generate frames at the requested rate. The
+  // minimum value is 1 and the maximum value is 60 (default 30). Can also be
+  // set at browser creation via CefBrowserSettings.windowless_frame_rate.
+  ///
+  /*--cef()--*/
+  virtual void SetWindowlessFrameRate(int frame_rate) = 0;
+
+  ///
+  // Begins a new composition or updates the existing composition. Blink has a
+  // special node (a composition node) that allows the input method to change
+  // text without affecting other DOM nodes. |text| is the optional text that
+  // will be inserted into the composition node. |underlines| is an optional set
+  // of ranges that will be underlined in the resulting text.
+  // |replacement_range| is an optional range of the existing text that will be
+  // replaced. |selection_range| is an optional range of the resulting text that
+  // will be selected after insertion or replacement. The |replacement_range|
+  // value is only used on OS X.
+  //
+  // This method may be called multiple times as the composition changes. When
+  // the client is done making changes the composition should either be canceled
+  // or completed. To cancel the composition call ImeCancelComposition. To
+  // complete the composition call either ImeCommitText or
+  // ImeFinishComposingText. Completion is usually signaled when:
+  //   A. The client receives a WM_IME_COMPOSITION message with a GCS_RESULTSTR
+  //      flag (on Windows), or;
+  //   B. The client receives a "commit" signal of GtkIMContext (on Linux), or;
+  //   C. insertText of NSTextInput is called (on Mac).
+  //
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef(optional_param=text, optional_param=underlines)--*/
+  virtual void ImeSetComposition(
+      const CefString& text,
+      const std::vector<CefCompositionUnderline>& underlines,
+      const CefRange& replacement_range,
+      const CefRange& selection_range) = 0;
+
+  ///
+  // Completes the existing composition by optionally inserting the specified
+  // |text| into the composition node. |replacement_range| is an optional range
+  // of the existing text that will be replaced. |relative_cursor_pos| is where
+  // the cursor will be positioned relative to the current cursor position. See
+  // comments on ImeSetComposition for usage. The |replacement_range| and
+  // |relative_cursor_pos| values are only used on OS X.
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef(optional_param=text)--*/
+  virtual void ImeCommitText(const CefString& text,
+                             const CefRange& replacement_range,
+                             int relative_cursor_pos) = 0;
+
+  ///
+  // Completes the existing composition by applying the current composition node
+  // contents. If |keep_selection| is false the current selection, if any, will
+  // be discarded. See comments on ImeSetComposition for usage.
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void ImeFinishComposingText(bool keep_selection) = 0;
+
+  ///
+  // Cancels the existing composition and discards the composition node
+  // contents without applying them. See comments on ImeSetComposition for
+  // usage.
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void ImeCancelComposition() = 0;
+
+  ///
+  // Call this method when the user drags the mouse into the web view (before
+  // calling DragTargetDragOver/DragTargetLeave/DragTargetDrop).
+  // |drag_data| should not contain file contents as this type of data is not
+  // allowed to be dragged into the web view. File contents can be removed using
+  // CefDragData::ResetFileContents (for example, if |drag_data| comes from
+  // CefRenderHandler::StartDragging).
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
+                                   const CefMouseEvent& event,
+                                   DragOperationsMask allowed_ops) = 0;
+
+  ///
+  // Call this method each time the mouse is moved across the web view during
+  // a drag operation (after calling DragTargetDragEnter and before calling
+  // DragTargetDragLeave/DragTargetDrop).
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void DragTargetDragOver(const CefMouseEvent& event,
+                                  DragOperationsMask allowed_ops) = 0;
+
+  ///
+  // Call this method when the user drags the mouse out of the web view (after
+  // calling DragTargetDragEnter).
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void DragTargetDragLeave() = 0;
+
+  ///
+  // Call this method when the user completes the drag operation by dropping
+  // the object onto the web view (after calling DragTargetDragEnter).
+  // The object being dropped is |drag_data|, given as an argument to
+  // the previous DragTargetDragEnter call.
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void DragTargetDrop(const CefMouseEvent& event) = 0;
+
+  ///
+  // Call this method when the drag operation started by a
+  // CefRenderHandler::StartDragging call has ended either in a drop or
+  // by being cancelled. |x| and |y| are mouse coordinates relative to the
+  // upper-left corner of the view. If the web view is both the drag source
+  // and the drag target then all DragTarget* methods should be called before
+  // DragSource* mthods.
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void DragSourceEndedAt(int x, int y, DragOperationsMask op) = 0;
+
+  ///
+  // Call this method when the drag operation started by a
+  // CefRenderHandler::StartDragging call has completed. This method may be
+  // called immediately without first calling DragSourceEndedAt to cancel a
+  // drag operation. If the web view is both the drag source and the drag
+  // target then all DragTarget* methods should be called before DragSource*
+  // mthods.
+  // This method is only used when window rendering is disabled.
+  ///
+  /*--cef()--*/
+  virtual void DragSourceSystemDragEnded() = 0;
+
+  ///
+  // Returns the current visible navigation entry for this browser. This method
+  // can only be called on the UI thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefNavigationEntry> GetVisibleNavigationEntry() = 0;
+
+  ///
+  // Set accessibility state for all frames. |accessibility_state| may be
+  // default, enabled or disabled. If |accessibility_state| is STATE_DEFAULT
+  // then accessibility will be disabled by default and the state may be further
+  // controlled with the "force-renderer-accessibility" and
+  // "disable-renderer-accessibility" command-line switches. If
+  // |accessibility_state| is STATE_ENABLED then accessibility will be enabled.
+  // If |accessibility_state| is STATE_DISABLED then accessibility will be
+  // completely disabled.
+  //
+  // For windowed browsers accessibility will be enabled in Complete mode (which
+  // corresponds to kAccessibilityModeComplete in Chromium). In this mode all
+  // platform accessibility objects will be created and managed by Chromium's
+  // internal implementation. The client needs only to detect the screen reader
+  // and call this method appropriately. For example, on macOS the client can
+  // handle the @"AXEnhancedUserInterface" accessibility attribute to detect
+  // VoiceOver state changes and on Windows the client can handle WM_GETOBJECT
+  // with OBJID_CLIENT to detect accessibility readers.
+  //
+  // For windowless browsers accessibility will be enabled in TreeOnly mode
+  // (which corresponds to kAccessibilityModeWebContentsOnly in Chromium). In
+  // this mode renderer accessibility is enabled, the full tree is computed, and
+  // events are passed to CefAccessibiltyHandler, but platform accessibility
+  // objects are not created. The client may implement platform accessibility
+  // objects using CefAccessibiltyHandler callbacks if desired.
+  ///
+  /*--cef()--*/
+  virtual void SetAccessibilityState(cef_state_t accessibility_state) = 0;
+
+  ///
+  // Enable notifications of auto resize via CefDisplayHandler::OnAutoResize.
+  // Notifications are disabled by default. |min_size| and |max_size| define the
+  // range of allowed sizes.
+  ///
+  /*--cef()--*/
+  virtual void SetAutoResizeEnabled(bool enabled,
+                                    const CefSize& min_size,
+                                    const CefSize& max_size) = 0;
+
+  ///
+  // Returns the extension hosted in this browser or NULL if no extension is
+  // hosted. See CefRequestContext::LoadExtension for details.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefExtension> GetExtension() = 0;
+
+  ///
+  // Returns true if this browser is hosting an extension background script.
+  // Background hosts do not have a window and are not displayable. See
+  // CefRequestContext::LoadExtension for details.
+  ///
+  /*--cef()--*/
+  virtual bool IsBackgroundHost() = 0;
+
+  ///
+  //  Set whether the browser's audio is muted.
+  ///
+  /*--cef()--*/
+  virtual void SetAudioMuted(bool mute) = 0;
+
+  ///
+  // Returns true if the browser's audio is muted.  This method can only be
+  // called on the UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool IsAudioMuted() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_BROWSER_H_
diff --git a/src/include/cef_browser_process_handler.h b/src/include/cef_browser_process_handler.h
new file mode 100644
index 0000000..7120606
--- /dev/null
+++ b/src/include/cef_browser_process_handler.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_BROWSER_PROCESS_HANDLER_H_
+#define CEF_INCLUDE_CEF_BROWSER_PROCESS_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_command_line.h"
+#include "include/cef_print_handler.h"
+#include "include/cef_values.h"
+
+///
+// Class used to implement browser process callbacks. The methods of this class
+// will be called on the browser process main thread unless otherwise indicated.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefBrowserProcessHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called on the browser process UI thread immediately after the CEF context
+  // has been initialized.
+  ///
+  /*--cef()--*/
+  virtual void OnContextInitialized() {}
+
+  ///
+  // Called before a child process is launched. Will be called on the browser
+  // process UI thread when launching a render process and on the browser
+  // process IO thread when launching a GPU or plugin process. Provides an
+  // opportunity to modify the child process command line. Do not keep a
+  // reference to |command_line| outside of this method.
+  ///
+  /*--cef()--*/
+  virtual void OnBeforeChildProcessLaunch(
+      CefRefPtr<CefCommandLine> command_line) {}
+
+  ///
+  // Called on the browser process IO thread after the main thread has been
+  // created for a new render process. Provides an opportunity to specify extra
+  // information that will be passed to
+  // CefRenderProcessHandler::OnRenderThreadCreated() in the render process. Do
+  // not keep a reference to |extra_info| outside of this method.
+  ///
+  /*--cef()--*/
+  virtual void OnRenderProcessThreadCreated(
+      CefRefPtr<CefListValue> extra_info) {}
+
+  ///
+  // Return the handler for printing on Linux. If a print handler is not
+  // provided then printing will not be supported on the Linux platform.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefPrintHandler> GetPrintHandler() { return nullptr; }
+
+  ///
+  // Called from any thread when work has been scheduled for the browser process
+  // main (UI) thread. This callback is used in combination with CefSettings.
+  // external_message_pump and CefDoMessageLoopWork() in cases where the CEF
+  // message loop must be integrated into an existing application message loop
+  // (see additional comments and warnings on CefDoMessageLoopWork). This
+  // callback should schedule a CefDoMessageLoopWork() call to happen on the
+  // main (UI) thread. |delay_ms| is the requested delay in milliseconds. If
+  // |delay_ms| is <= 0 then the call should happen reasonably soon. If
+  // |delay_ms| is > 0 then the call should be scheduled to happen after the
+  // specified delay and any currently pending scheduled call should be
+  // cancelled.
+  ///
+  /*--cef()--*/
+  virtual void OnScheduleMessagePumpWork(int64 delay_ms) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_BROWSER_PROCESS_HANDLER_H_
diff --git a/src/include/cef_callback.h b/src/include/cef_callback.h
new file mode 100644
index 0000000..e206cb9
--- /dev/null
+++ b/src/include/cef_callback.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_CALLBACK_H_
+#define CEF_INCLUDE_CEF_CALLBACK_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Generic callback interface used for asynchronous continuation.
+///
+/*--cef(source=library)--*/
+class CefCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Continue processing.
+  ///
+  /*--cef(capi_name=cont)--*/
+  virtual void Continue() = 0;
+
+  ///
+  // Cancel processing.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+///
+// Generic callback interface used for asynchronous completion.
+///
+/*--cef(source=client)--*/
+class CefCompletionCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called once the task is complete.
+  ///
+  /*--cef()--*/
+  virtual void OnComplete() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_CALLBACK_H_
diff --git a/src/include/cef_client.h b/src/include/cef_client.h
new file mode 100644
index 0000000..ada7a6a
--- /dev/null
+++ b/src/include/cef_client.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_CLIENT_H_
+#define CEF_INCLUDE_CEF_CLIENT_H_
+#pragma once
+
+#include "include/cef_audio_handler.h"
+#include "include/cef_base.h"
+#include "include/cef_context_menu_handler.h"
+#include "include/cef_dialog_handler.h"
+#include "include/cef_display_handler.h"
+#include "include/cef_download_handler.h"
+#include "include/cef_drag_handler.h"
+#include "include/cef_find_handler.h"
+#include "include/cef_focus_handler.h"
+#include "include/cef_jsdialog_handler.h"
+#include "include/cef_keyboard_handler.h"
+#include "include/cef_life_span_handler.h"
+#include "include/cef_load_handler.h"
+#include "include/cef_process_message.h"
+#include "include/cef_render_handler.h"
+#include "include/cef_request_handler.h"
+
+///
+// Implement this interface to provide handler implementations.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefClient : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Return the handler for audio rendering events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefAudioHandler> GetAudioHandler() { return nullptr; }
+
+  ///
+  // Return the handler for context menus. If no handler is provided the default
+  // implementation will be used.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() {
+    return nullptr;
+  }
+
+  ///
+  // Return the handler for dialogs. If no handler is provided the default
+  // implementation will be used.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDialogHandler> GetDialogHandler() { return nullptr; }
+
+  ///
+  // Return the handler for browser display state events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() { return nullptr; }
+
+  ///
+  // Return the handler for download events. If no handler is returned downloads
+  // will not be allowed.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() { return nullptr; }
+
+  ///
+  // Return the handler for drag events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDragHandler> GetDragHandler() { return nullptr; }
+
+  ///
+  // Return the handler for find result events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFindHandler> GetFindHandler() { return nullptr; }
+
+  ///
+  // Return the handler for focus events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFocusHandler> GetFocusHandler() { return nullptr; }
+
+  ///
+  // Return the handler for JavaScript dialogs. If no handler is provided the
+  // default implementation will be used.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() { return nullptr; }
+
+  ///
+  // Return the handler for keyboard events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() { return nullptr; }
+
+  ///
+  // Return the handler for browser life span events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() { return nullptr; }
+
+  ///
+  // Return the handler for browser load status events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefLoadHandler> GetLoadHandler() { return nullptr; }
+
+  ///
+  // Return the handler for off-screen rendering events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRenderHandler> GetRenderHandler() { return nullptr; }
+
+  ///
+  // Return the handler for browser request events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRequestHandler> GetRequestHandler() { return nullptr; }
+
+  ///
+  // Called when a new message is received from a different process. Return true
+  // if the message was handled or false otherwise. Do not keep a reference to
+  // or attempt to access the message outside of this callback.
+  ///
+  /*--cef()--*/
+  virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                        CefRefPtr<CefFrame> frame,
+                                        CefProcessId source_process,
+                                        CefRefPtr<CefProcessMessage> message) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_CLIENT_H_
diff --git a/src/include/cef_command_line.h b/src/include/cef_command_line.h
new file mode 100644
index 0000000..dd5491c
--- /dev/null
+++ b/src/include/cef_command_line.h
@@ -0,0 +1,208 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_COMMAND_LINE_H_
+#define CEF_INCLUDE_CEF_COMMAND_LINE_H_
+#pragma once
+
+#include <map>
+#include <vector>
+#include "include/cef_base.h"
+
+///
+// Class used to create and/or parse command line arguments. Arguments with
+// '--', '-' and, on Windows, '/' prefixes are considered switches. Switches
+// will always precede any arguments without switch prefixes. Switches can
+// optionally have a value specified using the '=' delimiter (e.g.
+// "-switch=value"). An argument of "--" will terminate switch parsing with all
+// subsequent tokens, regardless of prefix, being interpreted as non-switch
+// arguments. Switch names are considered case-insensitive. This class can be
+// used before CefInitialize() is called.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefCommandLine : public virtual CefBaseRefCounted {
+ public:
+  typedef std::vector<CefString> ArgumentList;
+  typedef std::map<CefString, CefString> SwitchMap;
+
+  ///
+  // Create a new CefCommandLine instance.
+  ///
+  /*--cef(api_hash_check)--*/
+  static CefRefPtr<CefCommandLine> CreateCommandLine();
+
+  ///
+  // Returns the singleton global CefCommandLine object. The returned object
+  // will be read-only.
+  ///
+  /*--cef(api_hash_check)--*/
+  static CefRefPtr<CefCommandLine> GetGlobalCommandLine();
+
+  ///
+  // Returns true if this object is valid. Do not call any other methods if this
+  // function returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns a writable copy of this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefCommandLine> Copy() = 0;
+
+  ///
+  // Initialize the command line with the specified |argc| and |argv| values.
+  // The first argument must be the name of the program. This method is only
+  // supported on non-Windows platforms.
+  ///
+  /*--cef()--*/
+  virtual void InitFromArgv(int argc, const char* const* argv) = 0;
+
+  ///
+  // Initialize the command line with the string returned by calling
+  // GetCommandLineW(). This method is only supported on Windows.
+  ///
+  /*--cef()--*/
+  virtual void InitFromString(const CefString& command_line) = 0;
+
+  ///
+  // Reset the command-line switches and arguments but leave the program
+  // component unchanged.
+  ///
+  /*--cef()--*/
+  virtual void Reset() = 0;
+
+  ///
+  // Retrieve the original command line string as a vector of strings.
+  // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* }
+  ///
+  /*--cef()--*/
+  virtual void GetArgv(std::vector<CefString>& argv) = 0;
+
+  ///
+  // Constructs and returns the represented command line string. Use this method
+  // cautiously because quoting behavior is unclear.
+  ///
+  /*--cef()--*/
+  virtual CefString GetCommandLineString() = 0;
+
+  ///
+  // Get the program part of the command line string (the first item).
+  ///
+  /*--cef()--*/
+  virtual CefString GetProgram() = 0;
+
+  ///
+  // Set the program part of the command line string (the first item).
+  ///
+  /*--cef()--*/
+  virtual void SetProgram(const CefString& program) = 0;
+
+  ///
+  // Returns true if the command line has switches.
+  ///
+  /*--cef()--*/
+  virtual bool HasSwitches() = 0;
+
+  ///
+  // Returns true if the command line contains the given switch.
+  ///
+  /*--cef()--*/
+  virtual bool HasSwitch(const CefString& name) = 0;
+
+  ///
+  // Returns the value associated with the given switch. If the switch has no
+  // value or isn't present this method returns the empty string.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSwitchValue(const CefString& name) = 0;
+
+  ///
+  // Returns the map of switch names and values. If a switch has no value an
+  // empty string is returned.
+  ///
+  /*--cef()--*/
+  virtual void GetSwitches(SwitchMap& switches) = 0;
+
+  ///
+  // Add a switch to the end of the command line. If the switch has no value
+  // pass an empty value string.
+  ///
+  /*--cef()--*/
+  virtual void AppendSwitch(const CefString& name) = 0;
+
+  ///
+  // Add a switch with the specified value to the end of the command line.
+  ///
+  /*--cef()--*/
+  virtual void AppendSwitchWithValue(const CefString& name,
+                                     const CefString& value) = 0;
+
+  ///
+  // True if there are remaining command line arguments.
+  ///
+  /*--cef()--*/
+  virtual bool HasArguments() = 0;
+
+  ///
+  // Get the remaining command line arguments.
+  ///
+  /*--cef()--*/
+  virtual void GetArguments(ArgumentList& arguments) = 0;
+
+  ///
+  // Add an argument to the end of the command line.
+  ///
+  /*--cef()--*/
+  virtual void AppendArgument(const CefString& argument) = 0;
+
+  ///
+  // Insert a command before the current command.
+  // Common for debuggers, like "valgrind" or "gdb --args".
+  ///
+  /*--cef()--*/
+  virtual void PrependWrapper(const CefString& wrapper) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_COMMAND_LINE_H_
diff --git a/src/include/cef_context_menu_handler.h b/src/include/cef_context_menu_handler.h
new file mode 100644
index 0000000..2a2a73e
--- /dev/null
+++ b/src/include/cef_context_menu_handler.h
@@ -0,0 +1,292 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_CONTEXT_MENU_HANDLER_H_
+#define CEF_INCLUDE_CEF_CONTEXT_MENU_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_menu_model.h"
+
+class CefContextMenuParams;
+
+///
+// Callback interface used for continuation of custom context menu display.
+///
+/*--cef(source=library)--*/
+class CefRunContextMenuCallback : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_event_flags_t EventFlags;
+
+  ///
+  // Complete context menu display by selecting the specified |command_id| and
+  // |event_flags|.
+  ///
+  /*--cef(capi_name=cont)--*/
+  virtual void Continue(int command_id, EventFlags event_flags) = 0;
+
+  ///
+  // Cancel context menu display.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+///
+// Implement this interface to handle context menu events. The methods of this
+// class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefContextMenuHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_event_flags_t EventFlags;
+
+  ///
+  // Called before a context menu is displayed. |params| provides information
+  // about the context menu state. |model| initially contains the default
+  // context menu. The |model| can be cleared to show no context menu or
+  // modified to show a custom menu. Do not keep references to |params| or
+  // |model| outside of this callback.
+  ///
+  /*--cef()--*/
+  virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
+                                   CefRefPtr<CefFrame> frame,
+                                   CefRefPtr<CefContextMenuParams> params,
+                                   CefRefPtr<CefMenuModel> model) {}
+
+  ///
+  // Called to allow custom display of the context menu. |params| provides
+  // information about the context menu state. |model| contains the context menu
+  // model resulting from OnBeforeContextMenu. For custom display return true
+  // and execute |callback| either synchronously or asynchronously with the
+  // selected command ID. For default display return false. Do not keep
+  // references to |params| or |model| outside of this callback.
+  ///
+  /*--cef()--*/
+  virtual bool RunContextMenu(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefContextMenuParams> params,
+                              CefRefPtr<CefMenuModel> model,
+                              CefRefPtr<CefRunContextMenuCallback> callback) {
+    return false;
+  }
+
+  ///
+  // Called to execute a command selected from the context menu. Return true if
+  // the command was handled or false for the default implementation. See
+  // cef_menu_id_t for the command ids that have default implementations. All
+  // user-defined command ids should be between MENU_ID_USER_FIRST and
+  // MENU_ID_USER_LAST. |params| will have the same values as what was passed to
+  // OnBeforeContextMenu(). Do not keep a reference to |params| outside of this
+  // callback.
+  ///
+  /*--cef()--*/
+  virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
+                                    CefRefPtr<CefFrame> frame,
+                                    CefRefPtr<CefContextMenuParams> params,
+                                    int command_id,
+                                    EventFlags event_flags) {
+    return false;
+  }
+
+  ///
+  // Called when the context menu is dismissed irregardless of whether the menu
+  // was empty or a command was selected.
+  ///
+  /*--cef()--*/
+  virtual void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
+                                      CefRefPtr<CefFrame> frame) {}
+};
+
+///
+// Provides information about the context menu state. The ethods of this class
+// can only be accessed on browser process the UI thread.
+///
+/*--cef(source=library)--*/
+class CefContextMenuParams : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_context_menu_type_flags_t TypeFlags;
+  typedef cef_context_menu_media_type_t MediaType;
+  typedef cef_context_menu_media_state_flags_t MediaStateFlags;
+  typedef cef_context_menu_edit_state_flags_t EditStateFlags;
+
+  ///
+  // Returns the X coordinate of the mouse where the context menu was invoked.
+  // Coords are relative to the associated RenderView's origin.
+  ///
+  /*--cef()--*/
+  virtual int GetXCoord() = 0;
+
+  ///
+  // Returns the Y coordinate of the mouse where the context menu was invoked.
+  // Coords are relative to the associated RenderView's origin.
+  ///
+  /*--cef()--*/
+  virtual int GetYCoord() = 0;
+
+  ///
+  // Returns flags representing the type of node that the context menu was
+  // invoked on.
+  ///
+  /*--cef(default_retval=CM_TYPEFLAG_NONE)--*/
+  virtual TypeFlags GetTypeFlags() = 0;
+
+  ///
+  // Returns the URL of the link, if any, that encloses the node that the
+  // context menu was invoked on.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLinkUrl() = 0;
+
+  ///
+  // Returns the link URL, if any, to be used ONLY for "copy link address". We
+  // don't validate this field in the frontend process.
+  ///
+  /*--cef()--*/
+  virtual CefString GetUnfilteredLinkUrl() = 0;
+
+  ///
+  // Returns the source URL, if any, for the element that the context menu was
+  // invoked on. Example of elements with source URLs are img, audio, and video.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSourceUrl() = 0;
+
+  ///
+  // Returns true if the context menu was invoked on an image which has
+  // non-empty contents.
+  ///
+  /*--cef()--*/
+  virtual bool HasImageContents() = 0;
+
+  ///
+  // Returns the title text or the alt text if the context menu was invoked on
+  // an image.
+  ///
+  /*--cef()--*/
+  virtual CefString GetTitleText() = 0;
+
+  ///
+  // Returns the URL of the top level page that the context menu was invoked on.
+  ///
+  /*--cef()--*/
+  virtual CefString GetPageUrl() = 0;
+
+  ///
+  // Returns the URL of the subframe that the context menu was invoked on.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFrameUrl() = 0;
+
+  ///
+  // Returns the character encoding of the subframe that the context menu was
+  // invoked on.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFrameCharset() = 0;
+
+  ///
+  // Returns the type of context node that the context menu was invoked on.
+  ///
+  /*--cef(default_retval=CM_MEDIATYPE_NONE)--*/
+  virtual MediaType GetMediaType() = 0;
+
+  ///
+  // Returns flags representing the actions supported by the media element, if
+  // any, that the context menu was invoked on.
+  ///
+  /*--cef(default_retval=CM_MEDIAFLAG_NONE)--*/
+  virtual MediaStateFlags GetMediaStateFlags() = 0;
+
+  ///
+  // Returns the text of the selection, if any, that the context menu was
+  // invoked on.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSelectionText() = 0;
+
+  ///
+  // Returns the text of the misspelled word, if any, that the context menu was
+  // invoked on.
+  ///
+  /*--cef()--*/
+  virtual CefString GetMisspelledWord() = 0;
+
+  ///
+  // Returns true if suggestions exist, false otherwise. Fills in |suggestions|
+  // from the spell check service for the misspelled word if there is one.
+  ///
+  /*--cef()--*/
+  virtual bool GetDictionarySuggestions(
+      std::vector<CefString>& suggestions) = 0;
+
+  ///
+  // Returns true if the context menu was invoked on an editable node.
+  ///
+  /*--cef()--*/
+  virtual bool IsEditable() = 0;
+
+  ///
+  // Returns true if the context menu was invoked on an editable node where
+  // spell-check is enabled.
+  ///
+  /*--cef()--*/
+  virtual bool IsSpellCheckEnabled() = 0;
+
+  ///
+  // Returns flags representing the actions supported by the editable node, if
+  // any, that the context menu was invoked on.
+  ///
+  /*--cef(default_retval=CM_EDITFLAG_NONE)--*/
+  virtual EditStateFlags GetEditStateFlags() = 0;
+
+  ///
+  // Returns true if the context menu contains items specified by the renderer
+  // process (for example, plugin placeholder or pepper plugin menu items).
+  ///
+  /*--cef()--*/
+  virtual bool IsCustomMenu() = 0;
+
+  ///
+  // Returns true if the context menu was invoked from a pepper plugin.
+  ///
+  /*--cef()--*/
+  virtual bool IsPepperMenu() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_CONTEXT_MENU_HANDLER_H_
diff --git a/src/include/cef_cookie.h b/src/include/cef_cookie.h
new file mode 100644
index 0000000..f48c7df
--- /dev/null
+++ b/src/include/cef_cookie.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_COOKIE_H_
+#define CEF_INCLUDE_CEF_COOKIE_H_
+#pragma once
+
+#include <vector>
+#include "include/cef_base.h"
+#include "include/cef_callback.h"
+
+class CefCookieVisitor;
+class CefSetCookieCallback;
+class CefDeleteCookiesCallback;
+
+///
+// Class used for managing cookies. The methods of this class may be called on
+// any thread unless otherwise indicated.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefCookieManager : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the global cookie manager. By default data will be stored at
+  // CefSettings.cache_path if specified or in memory otherwise. If |callback|
+  // is non-NULL it will be executed asnychronously on the UI thread after the
+  // manager's storage has been initialized. Using this method is equivalent to
+  // calling CefRequestContext::GetGlobalContext()->GetDefaultCookieManager().
+  ///
+  /*--cef(optional_param=callback)--*/
+  static CefRefPtr<CefCookieManager> GetGlobalManager(
+      CefRefPtr<CefCompletionCallback> callback);
+
+  ///
+  // Set the schemes supported by this manager. If |include_defaults| is true
+  // the default schemes ("http", "https", "ws" and "wss") will also be
+  // supported. Calling this method with an empty |schemes| value and
+  // |include_defaults| set to false will disable all loading and saving of
+  // cookies for this manager. If |callback| is non-NULL it will be executed
+  // asnychronously on the UI thread after the change has been applied. Must be
+  // called before any cookies are accessed.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual void SetSupportedSchemes(
+      const std::vector<CefString>& schemes,
+      bool include_defaults,
+      CefRefPtr<CefCompletionCallback> callback) = 0;
+
+  ///
+  // Visit all cookies on the UI thread. The returned cookies are ordered by
+  // longest path, then by earliest creation date. Returns false if cookies
+  // cannot be accessed.
+  ///
+  /*--cef()--*/
+  virtual bool VisitAllCookies(CefRefPtr<CefCookieVisitor> visitor) = 0;
+
+  ///
+  // Visit a subset of cookies on the UI thread. The results are filtered by the
+  // given url scheme, host, domain and path. If |includeHttpOnly| is true
+  // HTTP-only cookies will also be included in the results. The returned
+  // cookies are ordered by longest path, then by earliest creation date.
+  // Returns false if cookies cannot be accessed.
+  ///
+  /*--cef()--*/
+  virtual bool VisitUrlCookies(const CefString& url,
+                               bool includeHttpOnly,
+                               CefRefPtr<CefCookieVisitor> visitor) = 0;
+
+  ///
+  // Sets a cookie given a valid URL and explicit user-provided cookie
+  // attributes. This function expects each attribute to be well-formed. It will
+  // check for disallowed characters (e.g. the ';' character is disallowed
+  // within the cookie value attribute) and fail without setting the cookie if
+  // such characters are found. If |callback| is non-NULL it will be executed
+  // asnychronously on the UI thread after the cookie has been set. Returns
+  // false if an invalid URL is specified or if cookies cannot be accessed.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual bool SetCookie(const CefString& url,
+                         const CefCookie& cookie,
+                         CefRefPtr<CefSetCookieCallback> callback) = 0;
+
+  ///
+  // Delete all cookies that match the specified parameters. If both |url| and
+  // |cookie_name| values are specified all host and domain cookies matching
+  // both will be deleted. If only |url| is specified all host cookies (but not
+  // domain cookies) irrespective of path will be deleted. If |url| is empty all
+  // cookies for all hosts and domains will be deleted. If |callback| is
+  // non-NULL it will be executed asnychronously on the UI thread after the
+  // cookies have been deleted. Returns false if a non-empty invalid URL is
+  // specified or if cookies cannot be accessed. Cookies can alternately be
+  // deleted using the Visit*Cookies() methods.
+  ///
+  /*--cef(optional_param=url,optional_param=cookie_name,
+          optional_param=callback)--*/
+  virtual bool DeleteCookies(const CefString& url,
+                             const CefString& cookie_name,
+                             CefRefPtr<CefDeleteCookiesCallback> callback) = 0;
+
+  ///
+  // Flush the backing store (if any) to disk. If |callback| is non-NULL it will
+  // be executed asnychronously on the UI thread after the flush is complete.
+  // Returns false if cookies cannot be accessed.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual bool FlushStore(CefRefPtr<CefCompletionCallback> callback) = 0;
+};
+
+///
+// Interface to implement for visiting cookie values. The methods of this class
+// will always be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefCookieVisitor : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called once for each cookie. |count| is the 0-based
+  // index for the current cookie. |total| is the total number of cookies.
+  // Set |deleteCookie| to true to delete the cookie currently being visited.
+  // Return false to stop visiting cookies. This method may never be called if
+  // no cookies are found.
+  ///
+  /*--cef()--*/
+  virtual bool Visit(const CefCookie& cookie,
+                     int count,
+                     int total,
+                     bool& deleteCookie) = 0;
+};
+
+///
+// Interface to implement to be notified of asynchronous completion via
+// CefCookieManager::SetCookie().
+///
+/*--cef(source=client)--*/
+class CefSetCookieCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called upon completion. |success| will be true if the
+  // cookie was set successfully.
+  ///
+  /*--cef()--*/
+  virtual void OnComplete(bool success) = 0;
+};
+
+///
+// Interface to implement to be notified of asynchronous completion via
+// CefCookieManager::DeleteCookies().
+///
+/*--cef(source=client)--*/
+class CefDeleteCookiesCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called upon completion. |num_deleted| will be the
+  // number of cookies that were deleted.
+  ///
+  /*--cef()--*/
+  virtual void OnComplete(int num_deleted) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_COOKIE_H_
diff --git a/src/include/cef_crash_util.h b/src/include/cef_crash_util.h
new file mode 100644
index 0000000..e569df7
--- /dev/null
+++ b/src/include/cef_crash_util.h
@@ -0,0 +1,142 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_CRASH_UTIL_H_
+#define CEF_INCLUDE_CEF_CRASH_UTIL_H_
+#pragma once
+
+///
+// Crash reporting is configured using an INI-style config file named
+// "crash_reporter.cfg". On Windows and Linux this file must be placed next to
+// the main application executable. On macOS this file must be placed in the
+// top-level app bundle Resources directory (e.g.
+// "<appname>.app/Contents/Resources"). File contents are as follows:
+//
+//  # Comments start with a hash character and must be on their own line.
+//
+//  [Config]
+//  ProductName=<Value of the "prod" crash key; defaults to "cef">
+//  ProductVersion=<Value of the "ver" crash key; defaults to the CEF version>
+//  AppName=<Windows only; App-specific folder name component for storing crash
+//           information; default to "CEF">
+//  ExternalHandler=<Windows only; Name of the external handler exe to use
+//                   instead of re-launching the main exe; default to empty>
+//  BrowserCrashForwardingEnabled=<macOS only; True if browser process crashes
+//                                 should be forwarded to the system crash
+//                                 reporter; default to false>
+//  ServerURL=<crash server URL; default to empty>
+//  RateLimitEnabled=<True if uploads should be rate limited; default to true>
+//  MaxUploadsPerDay=<Max uploads per 24 hours, used if rate limit is enabled;
+//                    default to 5>
+//  MaxDatabaseSizeInMb=<Total crash report disk usage greater than this value
+//                       will cause older reports to be deleted; default to 20>
+//  MaxDatabaseAgeInDays=<Crash reports older than this value will be deleted;
+//                        default to 5>
+//
+//  [CrashKeys]
+//  my_key1=<small|medium|large>
+//  my_key2=<small|medium|large>
+//
+// Config section:
+//
+// If "ProductName" and/or "ProductVersion" are set then the specified values
+// will be included in the crash dump metadata. On macOS if these values are set
+// to empty then they will be retrieved from the Info.plist file using the
+// "CFBundleName" and "CFBundleShortVersionString" keys respectively.
+//
+// If "AppName" is set on Windows then crash report information (metrics,
+// database and dumps) will be stored locally on disk under the
+// "C:\Users\[CurrentUser]\AppData\Local\[AppName]\User Data" folder. On other
+// platforms the CefSettings.user_data_path value will be used.
+//
+// If "ExternalHandler" is set on Windows then the specified exe will be
+// launched as the crashpad-handler instead of re-launching the main process
+// exe. The value can be an absolute path or a path relative to the main exe
+// directory. On Linux the CefSettings.browser_subprocess_path value will be
+// used. On macOS the existing subprocess app bundle will be used.
+//
+// If "BrowserCrashForwardingEnabled" is set to true on macOS then browser
+// process crashes will be forwarded to the system crash reporter. This results
+// in the crash UI dialog being displayed to the user and crash reports being
+// logged under "~/Library/Logs/DiagnosticReports". Forwarding of crash reports
+// from non-browser processes and Debug builds is always disabled.
+//
+// If "ServerURL" is set then crashes will be uploaded as a multi-part POST
+// request to the specified URL. Otherwise, reports will only be stored locally
+// on disk.
+//
+// If "RateLimitEnabled" is set to true then crash report uploads will be rate
+// limited as follows:
+//  1. If "MaxUploadsPerDay" is set to a positive value then at most the
+//     specified number of crashes will be uploaded in each 24 hour period.
+//  2. If crash upload fails due to a network or server error then an
+//     incremental backoff delay up to a maximum of 24 hours will be applied for
+//     retries.
+//  3. If a backoff delay is applied and "MaxUploadsPerDay" is > 1 then the
+//     "MaxUploadsPerDay" value will be reduced to 1 until the client is
+//     restarted. This helps to avoid an upload flood when the network or
+//     server error is resolved.
+// Rate limiting is not supported on Linux.
+//
+// If "MaxDatabaseSizeInMb" is set to a positive value then crash report storage
+// on disk will be limited to that size in megabytes. For example, on Windows
+// each dump is about 600KB so a "MaxDatabaseSizeInMb" value of 20 equates to
+// about 34 crash reports stored on disk. Not supported on Linux.
+//
+// If "MaxDatabaseAgeInDays" is set to a positive value then crash reports older
+// than the specified age in days will be deleted. Not supported on Linux.
+//
+// CrashKeys section:
+//
+// A maximum of 26 crash keys of each size can be specified for use by the
+// application. Crash key values will be truncated based on the specified size
+// (small = 64 bytes, medium = 256 bytes, large = 1024 bytes). The value of
+// crash keys can be set from any thread or process using the
+// CefSetCrashKeyValue function. These key/value pairs will be sent to the crash
+// server along with the crash dump file.
+///
+/*--cef()--*/
+bool CefCrashReportingEnabled();
+
+#include "include/cef_base.h"
+
+///
+// Sets or clears a specific key-value pair from the crash metadata.
+///
+/*--cef(optional_param=value)--*/
+void CefSetCrashKeyValue(const CefString& key, const CefString& value);
+
+#endif  // CEF_INCLUDE_CEF_CRASH_UTIL_H_
diff --git a/src/include/cef_devtools_message_observer.h b/src/include/cef_devtools_message_observer.h
new file mode 100644
index 0000000..c65aee9
--- /dev/null
+++ b/src/include/cef_devtools_message_observer.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DEVTOOLS_MESSAGE_OBSERVER_H_
+#define CEF_INCLUDE_CEF_DEVTOOLS_MESSAGE_OBSERVER_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+class CefBrowser;
+
+///
+// Callback interface for CefBrowserHost::AddDevToolsMessageObserver. The
+// methods of this class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefDevToolsMessageObserver : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called on receipt of a DevTools protocol message.
+  // |browser| is the originating browser instance. |message| is a UTF8-encoded
+  // JSON dictionary representing either a method result or an event. |message|
+  // is only valid for the scope of this callback and should be copied if
+  // necessary. Return true if the message was handled or false if the message
+  // should be further processed and passed to the OnDevToolsMethodResult or
+  // OnDevToolsEvent methods as appropriate.
+  //
+  // Method result dictionaries include an "id" (int) value that identifies the
+  // orginating method call sent from CefBrowserHost::SendDevToolsMessage, and
+  // optionally either a "result" (dictionary) or "error" (dictionary) value.
+  // The "error" dictionary will contain "code" (int) and "message" (string)
+  // values. Event dictionaries include a "method" (string) value and optionally
+  // a "params" (dictionary) value. See the DevTools protocol documentation at
+  // https://chromedevtools.github.io/devtools-protocol/ for details of
+  // supported method calls and the expected "result" or "params" dictionary
+  // contents. JSON dictionaries can be parsed using the CefParseJSON function
+  // if desired, however be aware of performance considerations when parsing
+  // large messages (some of which may exceed 1MB in size).
+  ///
+  /*--cef()--*/
+  virtual bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser,
+                                 const void* message,
+                                 size_t message_size) {
+    return false;
+  }
+
+  ///
+  // Method that will be called after attempted execution of a DevTools protocol
+  // method. |browser| is the originating browser instance. |message_id| is the
+  // "id" value that identifies the originating method call message. If the
+  // method succeeded |success| will be true and |result| will be the
+  // UTF8-encoded JSON "result" dictionary value (which may be empty). If the
+  // method failed |success| will be false and |result| will be the UTF8-encoded
+  // JSON "error" dictionary value. |result| is only valid for the scope of this
+  // callback and should be copied if necessary. See the OnDevToolsMessage
+  // documentation for additional details on |result| contents.
+  ///
+  /*--cef(optional_param=result)--*/
+  virtual void OnDevToolsMethodResult(CefRefPtr<CefBrowser> browser,
+                                      int message_id,
+                                      bool success,
+                                      const void* result,
+                                      size_t result_size) {}
+
+  ///
+  // Method that will be called on receipt of a DevTools protocol event.
+  // |browser| is the originating browser instance. |method| is the "method"
+  // value. |params| is the UTF8-encoded JSON "params" dictionary value (which
+  // may be empty). |params| is only valid for the scope of this callback and
+  // should be copied if necessary. See the OnDevToolsMessage documentation for
+  // additional details on |params| contents.
+  ///
+  /*--cef(optional_param=params)--*/
+  virtual void OnDevToolsEvent(CefRefPtr<CefBrowser> browser,
+                               const CefString& method,
+                               const void* params,
+                               size_t params_size) {}
+
+  ///
+  // Method that will be called when the DevTools agent has attached. |browser|
+  // is the originating browser instance. This will generally occur in response
+  // to the first message sent while the agent is detached.
+  ///
+  /*--cef()--*/
+  virtual void OnDevToolsAgentAttached(CefRefPtr<CefBrowser> browser) {}
+
+  ///
+  // Method that will be called when the DevTools agent has detached. |browser|
+  // is the originating browser instance. Any method results that were pending
+  // before the agent became detached will not be delivered, and any active
+  // event subscriptions will be canceled.
+  ///
+  /*--cef()--*/
+  virtual void OnDevToolsAgentDetached(CefRefPtr<CefBrowser> browser) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_DEVTOOLS_MESSAGE_OBSERVER_H_
diff --git a/src/include/cef_dialog_handler.h b/src/include/cef_dialog_handler.h
new file mode 100644
index 0000000..3ce02e6
--- /dev/null
+++ b/src/include/cef_dialog_handler.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DIALOG_HANDLER_H_
+#define CEF_INCLUDE_CEF_DIALOG_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+
+///
+// Callback interface for asynchronous continuation of file dialog requests.
+///
+/*--cef(source=library)--*/
+class CefFileDialogCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Continue the file selection. |selected_accept_filter| should be the 0-based
+  // index of the value selected from the accept filters array passed to
+  // CefDialogHandler::OnFileDialog. |file_paths| should be a single value or a
+  // list of values depending on the dialog mode. An empty |file_paths| value is
+  // treated the same as calling Cancel().
+  ///
+  /*--cef(capi_name=cont,index_param=selected_accept_filter,
+          optional_param=file_paths)--*/
+  virtual void Continue(int selected_accept_filter,
+                        const std::vector<CefString>& file_paths) = 0;
+
+  ///
+  // Cancel the file selection.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+///
+// Implement this interface to handle dialog events. The methods of this class
+// will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefDialogHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_file_dialog_mode_t FileDialogMode;
+
+  ///
+  // Called to run a file chooser dialog. |mode| represents the type of dialog
+  // to display. |title| to the title to be used for the dialog and may be empty
+  // to show the default title ("Open" or "Save" depending on the mode).
+  // |default_file_path| is the path with optional directory and/or file name
+  // component that should be initially selected in the dialog. |accept_filters|
+  // are used to restrict the selectable file types and may any combination of
+  // (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"),
+  // (b) individual file extensions (e.g. ".txt" or ".png"), or (c) combined
+  // description and file extension delimited using "|" and ";" (e.g.
+  // "Image Types|.png;.gif;.jpg"). |selected_accept_filter| is the 0-based
+  // index of the filter that should be selected by default. To display a custom
+  // dialog return true and execute |callback| either inline or at a later time.
+  // To display the default dialog return false.
+  ///
+  /*--cef(optional_param=title,optional_param=default_file_path,
+          optional_param=accept_filters,index_param=selected_accept_filter)--*/
+  virtual bool OnFileDialog(CefRefPtr<CefBrowser> browser,
+                            FileDialogMode mode,
+                            const CefString& title,
+                            const CefString& default_file_path,
+                            const std::vector<CefString>& accept_filters,
+                            int selected_accept_filter,
+                            CefRefPtr<CefFileDialogCallback> callback) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_DIALOG_HANDLER_H_
diff --git a/src/include/cef_display_handler.h b/src/include/cef_display_handler.h
new file mode 100644
index 0000000..4fdc3c3
--- /dev/null
+++ b/src/include/cef_display_handler.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DISPLAY_HANDLER_H_
+#define CEF_INCLUDE_CEF_DISPLAY_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+
+///
+// Implement this interface to handle events related to browser display state.
+// The methods of this class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefDisplayHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called when a frame's address has changed.
+  ///
+  /*--cef()--*/
+  virtual void OnAddressChange(CefRefPtr<CefBrowser> browser,
+                               CefRefPtr<CefFrame> frame,
+                               const CefString& url) {}
+
+  ///
+  // Called when the page title changes.
+  ///
+  /*--cef(optional_param=title)--*/
+  virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
+                             const CefString& title) {}
+
+  ///
+  // Called when the page icon changes.
+  ///
+  /*--cef(optional_param=icon_urls)--*/
+  virtual void OnFaviconURLChange(CefRefPtr<CefBrowser> browser,
+                                  const std::vector<CefString>& icon_urls) {}
+
+  ///
+  // Called when web content in the page has toggled fullscreen mode. If
+  // |fullscreen| is true the content will automatically be sized to fill the
+  // browser content area. If |fullscreen| is false the content will
+  // automatically return to its original size and position. The client is
+  // responsible for resizing the browser if desired.
+  ///
+  /*--cef()--*/
+  virtual void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
+                                      bool fullscreen) {}
+
+  ///
+  // Called when the browser is about to display a tooltip. |text| contains the
+  // text that will be displayed in the tooltip. To handle the display of the
+  // tooltip yourself return true. Otherwise, you can optionally modify |text|
+  // and then return false to allow the browser to display the tooltip.
+  // When window rendering is disabled the application is responsible for
+  // drawing tooltips and the return value is ignored.
+  ///
+  /*--cef(optional_param=text)--*/
+  virtual bool OnTooltip(CefRefPtr<CefBrowser> browser, CefString& text) {
+    return false;
+  }
+
+  ///
+  // Called when the browser receives a status message. |value| contains the
+  // text that will be displayed in the status message.
+  ///
+  /*--cef(optional_param=value)--*/
+  virtual void OnStatusMessage(CefRefPtr<CefBrowser> browser,
+                               const CefString& value) {}
+
+  ///
+  // Called to display a console message. Return true to stop the message from
+  // being output to the console.
+  ///
+  /*--cef(optional_param=message,optional_param=source)--*/
+  virtual bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
+                                cef_log_severity_t level,
+                                const CefString& message,
+                                const CefString& source,
+                                int line) {
+    return false;
+  }
+
+  ///
+  // Called when auto-resize is enabled via CefBrowserHost::SetAutoResizeEnabled
+  // and the contents have auto-resized. |new_size| will be the desired size in
+  // view coordinates. Return true if the resize was handled or false for
+  // default handling.
+  ///
+  /*--cef()--*/
+  virtual bool OnAutoResize(CefRefPtr<CefBrowser> browser,
+                            const CefSize& new_size) {
+    return false;
+  }
+
+  ///
+  // Called when the overall page loading progress has changed. |progress|
+  // ranges from 0.0 to 1.0.
+  ///
+  /*--cef()--*/
+  virtual void OnLoadingProgressChange(CefRefPtr<CefBrowser> browser,
+                                       double progress) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_DISPLAY_HANDLER_H_
diff --git a/src/include/cef_dom.h b/src/include/cef_dom.h
new file mode 100644
index 0000000..382b193
--- /dev/null
+++ b/src/include/cef_dom.h
@@ -0,0 +1,332 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DOM_H_
+#define CEF_INCLUDE_CEF_DOM_H_
+#pragma once
+
+#include <map>
+#include "include/cef_base.h"
+
+class CefDOMDocument;
+class CefDOMNode;
+
+///
+// Interface to implement for visiting the DOM. The methods of this class will
+// be called on the render process main thread.
+///
+/*--cef(source=client)--*/
+class CefDOMVisitor : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method executed for visiting the DOM. The document object passed to this
+  // method represents a snapshot of the DOM at the time this method is
+  // executed. DOM objects are only valid for the scope of this method. Do not
+  // keep references to or attempt to access any DOM objects outside the scope
+  // of this method.
+  ///
+  /*--cef()--*/
+  virtual void Visit(CefRefPtr<CefDOMDocument> document) = 0;
+};
+
+///
+// Class used to represent a DOM document. The methods of this class should only
+// be called on the render process main thread thread.
+///
+/*--cef(source=library)--*/
+class CefDOMDocument : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_dom_document_type_t Type;
+
+  ///
+  // Returns the document type.
+  ///
+  /*--cef(default_retval=DOM_DOCUMENT_TYPE_UNKNOWN)--*/
+  virtual Type GetType() = 0;
+
+  ///
+  // Returns the root document node.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetDocument() = 0;
+
+  ///
+  // Returns the BODY node of an HTML document.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetBody() = 0;
+
+  ///
+  // Returns the HEAD node of an HTML document.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetHead() = 0;
+
+  ///
+  // Returns the title of an HTML document.
+  ///
+  /*--cef()--*/
+  virtual CefString GetTitle() = 0;
+
+  ///
+  // Returns the document element with the specified ID value.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetElementById(const CefString& id) = 0;
+
+  ///
+  // Returns the node that currently has keyboard focus.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetFocusedNode() = 0;
+
+  ///
+  // Returns true if a portion of the document is selected.
+  ///
+  /*--cef()--*/
+  virtual bool HasSelection() = 0;
+
+  ///
+  // Returns the selection offset within the start node.
+  ///
+  /*--cef()--*/
+  virtual int GetSelectionStartOffset() = 0;
+
+  ///
+  // Returns the selection offset within the end node.
+  ///
+  /*--cef()--*/
+  virtual int GetSelectionEndOffset() = 0;
+
+  ///
+  // Returns the contents of this selection as markup.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSelectionAsMarkup() = 0;
+
+  ///
+  // Returns the contents of this selection as text.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSelectionAsText() = 0;
+
+  ///
+  // Returns the base URL for the document.
+  ///
+  /*--cef()--*/
+  virtual CefString GetBaseURL() = 0;
+
+  ///
+  // Returns a complete URL based on the document base URL and the specified
+  // partial URL.
+  ///
+  /*--cef()--*/
+  virtual CefString GetCompleteURL(const CefString& partialURL) = 0;
+};
+
+///
+// Class used to represent a DOM node. The methods of this class should only be
+// called on the render process main thread.
+///
+/*--cef(source=library)--*/
+class CefDOMNode : public virtual CefBaseRefCounted {
+ public:
+  typedef std::map<CefString, CefString> AttributeMap;
+  typedef cef_dom_node_type_t Type;
+
+  ///
+  // Returns the type for this node.
+  ///
+  /*--cef(default_retval=DOM_NODE_TYPE_UNSUPPORTED)--*/
+  virtual Type GetType() = 0;
+
+  ///
+  // Returns true if this is a text node.
+  ///
+  /*--cef()--*/
+  virtual bool IsText() = 0;
+
+  ///
+  // Returns true if this is an element node.
+  ///
+  /*--cef()--*/
+  virtual bool IsElement() = 0;
+
+  ///
+  // Returns true if this is an editable node.
+  ///
+  /*--cef()--*/
+  virtual bool IsEditable() = 0;
+
+  ///
+  // Returns true if this is a form control element node.
+  ///
+  /*--cef()--*/
+  virtual bool IsFormControlElement() = 0;
+
+  ///
+  // Returns the type of this form control element node.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFormControlElementType() = 0;
+
+  ///
+  // Returns true if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefDOMNode> that) = 0;
+
+  ///
+  // Returns the name of this node.
+  ///
+  /*--cef()--*/
+  virtual CefString GetName() = 0;
+
+  ///
+  // Returns the value of this node.
+  ///
+  /*--cef()--*/
+  virtual CefString GetValue() = 0;
+
+  ///
+  // Set the value of this node. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetValue(const CefString& value) = 0;
+
+  ///
+  // Returns the contents of this node as markup.
+  ///
+  /*--cef()--*/
+  virtual CefString GetAsMarkup() = 0;
+
+  ///
+  // Returns the document associated with this node.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMDocument> GetDocument() = 0;
+
+  ///
+  // Returns the parent node.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetParent() = 0;
+
+  ///
+  // Returns the previous sibling node.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetPreviousSibling() = 0;
+
+  ///
+  // Returns the next sibling node.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetNextSibling() = 0;
+
+  ///
+  // Returns true if this node has child nodes.
+  ///
+  /*--cef()--*/
+  virtual bool HasChildren() = 0;
+
+  ///
+  // Return the first child node.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetFirstChild() = 0;
+
+  ///
+  // Returns the last child node.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDOMNode> GetLastChild() = 0;
+
+  // The following methods are valid only for element nodes.
+
+  ///
+  // Returns the tag name of this element.
+  ///
+  /*--cef()--*/
+  virtual CefString GetElementTagName() = 0;
+
+  ///
+  // Returns true if this element has attributes.
+  ///
+  /*--cef()--*/
+  virtual bool HasElementAttributes() = 0;
+
+  ///
+  // Returns true if this element has an attribute named |attrName|.
+  ///
+  /*--cef()--*/
+  virtual bool HasElementAttribute(const CefString& attrName) = 0;
+
+  ///
+  // Returns the element attribute named |attrName|.
+  ///
+  /*--cef()--*/
+  virtual CefString GetElementAttribute(const CefString& attrName) = 0;
+
+  ///
+  // Returns a map of all element attributes.
+  ///
+  /*--cef()--*/
+  virtual void GetElementAttributes(AttributeMap& attrMap) = 0;
+
+  ///
+  // Set the value for the element attribute named |attrName|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool SetElementAttribute(const CefString& attrName,
+                                   const CefString& value) = 0;
+
+  ///
+  // Returns the inner text of the element.
+  ///
+  /*--cef()--*/
+  virtual CefString GetElementInnerText() = 0;
+
+  ///
+  // Returns the bounds of the element.
+  ///
+  /*--cef()--*/
+  virtual CefRect GetElementBounds() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_DOM_H_
diff --git a/src/include/cef_download_handler.h b/src/include/cef_download_handler.h
new file mode 100644
index 0000000..dfaeba5
--- /dev/null
+++ b/src/include/cef_download_handler.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DOWNLOAD_HANDLER_H_
+#define CEF_INCLUDE_CEF_DOWNLOAD_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_download_item.h"
+
+///
+// Callback interface used to asynchronously continue a download.
+///
+/*--cef(source=library)--*/
+class CefBeforeDownloadCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Call to continue the download. Set |download_path| to the full file path
+  // for the download including the file name or leave blank to use the
+  // suggested name and the default temp directory. Set |show_dialog| to true
+  // if you do wish to show the default "Save As" dialog.
+  ///
+  /*--cef(capi_name=cont,optional_param=download_path)--*/
+  virtual void Continue(const CefString& download_path, bool show_dialog) = 0;
+};
+
+///
+// Callback interface used to asynchronously cancel a download.
+///
+/*--cef(source=library)--*/
+class CefDownloadItemCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Call to cancel the download.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+
+  ///
+  // Call to pause the download.
+  ///
+  /*--cef()--*/
+  virtual void Pause() = 0;
+
+  ///
+  // Call to resume the download.
+  ///
+  /*--cef()--*/
+  virtual void Resume() = 0;
+};
+
+///
+// Class used to handle file downloads. The methods of this class will called
+// on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefDownloadHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called before a download begins. |suggested_name| is the suggested name for
+  // the download file. By default the download will be canceled. Execute
+  // |callback| either asynchronously or in this method to continue the download
+  // if desired. Do not keep a reference to |download_item| outside of this
+  // method.
+  ///
+  /*--cef()--*/
+  virtual void OnBeforeDownload(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefDownloadItem> download_item,
+      const CefString& suggested_name,
+      CefRefPtr<CefBeforeDownloadCallback> callback) = 0;
+
+  ///
+  // Called when a download's status or progress information has been updated.
+  // This may be called multiple times before and after OnBeforeDownload().
+  // Execute |callback| either asynchronously or in this method to cancel the
+  // download if desired. Do not keep a reference to |download_item| outside of
+  // this method.
+  ///
+  /*--cef()--*/
+  virtual void OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
+                                 CefRefPtr<CefDownloadItem> download_item,
+                                 CefRefPtr<CefDownloadItemCallback> callback) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_DOWNLOAD_HANDLER_H_
diff --git a/src/include/cef_download_item.h b/src/include/cef_download_item.h
new file mode 100644
index 0000000..930f53b
--- /dev/null
+++ b/src/include/cef_download_item.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DOWNLOAD_ITEM_H_
+#define CEF_INCLUDE_CEF_DOWNLOAD_ITEM_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Class used to represent a download item.
+///
+/*--cef(source=library)--*/
+class CefDownloadItem : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns true if this object is valid. Do not call any other methods if this
+  // function returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if the download is in progress.
+  ///
+  /*--cef()--*/
+  virtual bool IsInProgress() = 0;
+
+  ///
+  // Returns true if the download is complete.
+  ///
+  /*--cef()--*/
+  virtual bool IsComplete() = 0;
+
+  ///
+  // Returns true if the download has been canceled or interrupted.
+  ///
+  /*--cef()--*/
+  virtual bool IsCanceled() = 0;
+
+  ///
+  // Returns a simple speed estimate in bytes/s.
+  ///
+  /*--cef()--*/
+  virtual int64 GetCurrentSpeed() = 0;
+
+  ///
+  // Returns the rough percent complete or -1 if the receive total size is
+  // unknown.
+  ///
+  /*--cef()--*/
+  virtual int GetPercentComplete() = 0;
+
+  ///
+  // Returns the total number of bytes.
+  ///
+  /*--cef()--*/
+  virtual int64 GetTotalBytes() = 0;
+
+  ///
+  // Returns the number of received bytes.
+  ///
+  /*--cef()--*/
+  virtual int64 GetReceivedBytes() = 0;
+
+  ///
+  // Returns the time that the download started.
+  ///
+  /*--cef()--*/
+  virtual CefTime GetStartTime() = 0;
+
+  ///
+  // Returns the time that the download ended.
+  ///
+  /*--cef()--*/
+  virtual CefTime GetEndTime() = 0;
+
+  ///
+  // Returns the full path to the downloaded or downloading file.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFullPath() = 0;
+
+  ///
+  // Returns the unique identifier for this download.
+  ///
+  /*--cef()--*/
+  virtual uint32 GetId() = 0;
+
+  ///
+  // Returns the URL.
+  ///
+  /*--cef()--*/
+  virtual CefString GetURL() = 0;
+
+  ///
+  // Returns the original URL before any redirections.
+  ///
+  /*--cef()--*/
+  virtual CefString GetOriginalUrl() = 0;
+
+  ///
+  // Returns the suggested file name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSuggestedFileName() = 0;
+
+  ///
+  // Returns the content disposition.
+  ///
+  /*--cef()--*/
+  virtual CefString GetContentDisposition() = 0;
+
+  ///
+  // Returns the mime type.
+  ///
+  /*--cef()--*/
+  virtual CefString GetMimeType() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_DOWNLOAD_ITEM_H_
diff --git a/src/include/cef_drag_data.h b/src/include/cef_drag_data.h
new file mode 100644
index 0000000..35661f8
--- /dev/null
+++ b/src/include/cef_drag_data.h
@@ -0,0 +1,219 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DRAG_DATA_H_
+#define CEF_INCLUDE_CEF_DRAG_DATA_H_
+#pragma once
+
+#include <vector>
+#include "include/cef_base.h"
+#include "include/cef_image.h"
+#include "include/cef_stream.h"
+
+///
+// Class used to represent drag data. The methods of this class may be called
+// on any thread.
+///
+/*--cef(source=library)--*/
+class CefDragData : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Create a new CefDragData object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefDragData> Create();
+
+  ///
+  // Returns a copy of the current object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDragData> Clone() = 0;
+
+  ///
+  // Returns true if this object is read-only.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns true if the drag data is a link.
+  ///
+  /*--cef()--*/
+  virtual bool IsLink() = 0;
+
+  ///
+  // Returns true if the drag data is a text or html fragment.
+  ///
+  /*--cef()--*/
+  virtual bool IsFragment() = 0;
+
+  ///
+  // Returns true if the drag data is a file.
+  ///
+  /*--cef()--*/
+  virtual bool IsFile() = 0;
+
+  ///
+  // Return the link URL that is being dragged.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLinkURL() = 0;
+
+  ///
+  // Return the title associated with the link being dragged.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLinkTitle() = 0;
+
+  ///
+  // Return the metadata, if any, associated with the link being dragged.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLinkMetadata() = 0;
+
+  ///
+  // Return the plain text fragment that is being dragged.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFragmentText() = 0;
+
+  ///
+  // Return the text/html fragment that is being dragged.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFragmentHtml() = 0;
+
+  ///
+  // Return the base URL that the fragment came from. This value is used for
+  // resolving relative URLs and may be empty.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFragmentBaseURL() = 0;
+
+  ///
+  // Return the name of the file being dragged out of the browser window.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFileName() = 0;
+
+  ///
+  // Write the contents of the file being dragged out of the web view into
+  // |writer|. Returns the number of bytes sent to |writer|. If |writer| is
+  // NULL this method will return the size of the file contents in bytes.
+  // Call GetFileName() to get a suggested name for the file.
+  ///
+  /*--cef(optional_param=writer)--*/
+  virtual size_t GetFileContents(CefRefPtr<CefStreamWriter> writer) = 0;
+
+  ///
+  // Retrieve the list of file names that are being dragged into the browser
+  // window.
+  ///
+  /*--cef()--*/
+  virtual bool GetFileNames(std::vector<CefString>& names) = 0;
+
+  ///
+  // Set the link URL that is being dragged.
+  ///
+  /*--cef(optional_param=url)--*/
+  virtual void SetLinkURL(const CefString& url) = 0;
+
+  ///
+  // Set the title associated with the link being dragged.
+  ///
+  /*--cef(optional_param=title)--*/
+  virtual void SetLinkTitle(const CefString& title) = 0;
+
+  ///
+  // Set the metadata associated with the link being dragged.
+  ///
+  /*--cef(optional_param=data)--*/
+  virtual void SetLinkMetadata(const CefString& data) = 0;
+
+  ///
+  // Set the plain text fragment that is being dragged.
+  ///
+  /*--cef(optional_param=text)--*/
+  virtual void SetFragmentText(const CefString& text) = 0;
+
+  ///
+  // Set the text/html fragment that is being dragged.
+  ///
+  /*--cef(optional_param=html)--*/
+  virtual void SetFragmentHtml(const CefString& html) = 0;
+
+  ///
+  // Set the base URL that the fragment came from.
+  ///
+  /*--cef(optional_param=base_url)--*/
+  virtual void SetFragmentBaseURL(const CefString& base_url) = 0;
+
+  ///
+  // Reset the file contents. You should do this before calling
+  // CefBrowserHost::DragTargetDragEnter as the web view does not allow us to
+  // drag in this kind of data.
+  ///
+  /*--cef()--*/
+  virtual void ResetFileContents() = 0;
+
+  ///
+  // Add a file that is being dragged into the webview.
+  ///
+  /*--cef(optional_param=display_name)--*/
+  virtual void AddFile(const CefString& path,
+                       const CefString& display_name) = 0;
+
+  ///
+  // Get the image representation of drag data. May return NULL if no image
+  // representation is available.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefImage> GetImage() = 0;
+
+  ///
+  // Get the image hotspot (drag start location relative to image dimensions).
+  ///
+  /*--cef()--*/
+  virtual CefPoint GetImageHotspot() = 0;
+
+  ///
+  // Returns true if an image representation of drag data is available.
+  ///
+  /*--cef()--*/
+  virtual bool HasImage() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_DRAG_DATA_H_
diff --git a/src/include/cef_drag_handler.h b/src/include/cef_drag_handler.h
new file mode 100644
index 0000000..dfa226e
--- /dev/null
+++ b/src/include/cef_drag_handler.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_DRAG_HANDLER_H_
+#define CEF_INCLUDE_CEF_DRAG_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_drag_data.h"
+#include "include/cef_frame.h"
+
+///
+// Implement this interface to handle events related to dragging. The methods of
+// this class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefDragHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_drag_operations_mask_t DragOperationsMask;
+
+  ///
+  // Called when an external drag event enters the browser window. |dragData|
+  // contains the drag event data and |mask| represents the type of drag
+  // operation. Return false for default drag handling behavior or true to
+  // cancel the drag event.
+  ///
+  /*--cef()--*/
+  virtual bool OnDragEnter(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefDragData> dragData,
+                           DragOperationsMask mask) {
+    return false;
+  }
+
+  ///
+  // Called whenever draggable regions for the browser window change. These can
+  // be specified using the '-webkit-app-region: drag/no-drag' CSS-property. If
+  // draggable regions are never defined in a document this method will also
+  // never be called. If the last draggable region is removed from a document
+  // this method will be called with an empty vector.
+  ///
+  /*--cef()--*/
+  virtual void OnDraggableRegionsChanged(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const std::vector<CefDraggableRegion>& regions) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_DRAG_HANDLER_H_
diff --git a/src/include/cef_extension.h b/src/include/cef_extension.h
new file mode 100644
index 0000000..c34d013
--- /dev/null
+++ b/src/include/cef_extension.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_EXTENSION_H_
+#define CEF_INCLUDE_CEF_EXTENSION_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_values.h"
+
+class CefExtensionHandler;
+class CefRequestContext;
+
+///
+// Object representing an extension. Methods may be called on any thread unless
+// otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefExtension : public CefBaseRefCounted {
+ public:
+  ///
+  // Returns the unique extension identifier. This is calculated based on the
+  // extension public key, if available, or on the extension path. See
+  // https://developer.chrome.com/extensions/manifest/key for details.
+  ///
+  /*--cef()--*/
+  virtual CefString GetIdentifier() = 0;
+
+  ///
+  // Returns the absolute path to the extension directory on disk. This value
+  // will be prefixed with PK_DIR_RESOURCES if a relative path was passed to
+  // CefRequestContext::LoadExtension.
+  ///
+  /*--cef()--*/
+  virtual CefString GetPath() = 0;
+
+  ///
+  // Returns the extension manifest contents as a CefDictionaryValue object. See
+  // https://developer.chrome.com/extensions/manifest for details.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDictionaryValue> GetManifest() = 0;
+
+  ///
+  // Returns true if this object is the same extension as |that| object.
+  // Extensions are considered the same if identifier, path and loader context
+  // match.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefExtension> that) = 0;
+
+  ///
+  // Returns the handler for this extension. Will return NULL for internal
+  // extensions or if no handler was passed to CefRequestContext::LoadExtension.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefExtensionHandler> GetHandler() = 0;
+
+  ///
+  // Returns the request context that loaded this extension. Will return NULL
+  // for internal extensions or if the extension has been unloaded. See the
+  // CefRequestContext::LoadExtension documentation for more information about
+  // loader contexts. Must be called on the browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRequestContext> GetLoaderContext() = 0;
+
+  ///
+  // Returns true if this extension is currently loaded. Must be called on the
+  // browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool IsLoaded() = 0;
+
+  ///
+  // Unload this extension if it is not an internal extension and is currently
+  // loaded. Will result in a call to CefExtensionHandler::OnExtensionUnloaded
+  // on success.
+  ///
+  /*--cef()--*/
+  virtual void Unload() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_EXTENSION_H_
diff --git a/src/include/cef_extension_handler.h b/src/include/cef_extension_handler.h
new file mode 100644
index 0000000..67dda33
--- /dev/null
+++ b/src/include/cef_extension_handler.h
@@ -0,0 +1,199 @@
+// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_EXTENSION_HANDLER_H_
+#define CEF_INCLUDE_CEF_EXTENSION_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_extension.h"
+#include "include/cef_stream.h"
+
+class CefClient;
+
+///
+// Callback interface used for asynchronous continuation of
+// CefExtensionHandler::GetExtensionResource.
+///
+/*--cef(source=library)--*/
+class CefGetExtensionResourceCallback : public CefBaseRefCounted {
+ public:
+  ///
+  // Continue the request. Read the resource contents from |stream|.
+  ///
+  /*--cef(capi_name=cont,optional_param=stream)--*/
+  virtual void Continue(CefRefPtr<CefStreamReader> stream) = 0;
+
+  ///
+  // Cancel the request.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+///
+// Implement this interface to handle events related to browser extensions.
+// The methods of this class will be called on the UI thread. See
+// CefRequestContext::LoadExtension for information about extension loading.
+///
+/*--cef(source=client)--*/
+class CefExtensionHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called if the CefRequestContext::LoadExtension request fails. |result| will
+  // be the error code.
+  ///
+  /*--cef()--*/
+  virtual void OnExtensionLoadFailed(cef_errorcode_t result) {}
+
+  ///
+  // Called if the CefRequestContext::LoadExtension request succeeds.
+  // |extension| is the loaded extension.
+  ///
+  /*--cef()--*/
+  virtual void OnExtensionLoaded(CefRefPtr<CefExtension> extension) {}
+
+  ///
+  // Called after the CefExtension::Unload request has completed.
+  ///
+  /*--cef()--*/
+  virtual void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) {}
+
+  ///
+  // Called when an extension needs a browser to host a background script
+  // specified via the "background" manifest key. The browser will have no
+  // visible window and cannot be displayed. |extension| is the extension that
+  // is loading the background script. |url| is an internally generated
+  // reference to an HTML page that will be used to load the background script
+  // via a <script> src attribute. To allow creation of the browser optionally
+  // modify |client| and |settings| and return false. To cancel creation of the
+  // browser (and consequently cancel load of the background script) return
+  // true. Successful creation will be indicated by a call to
+  // CefLifeSpanHandler::OnAfterCreated, and CefBrowserHost::IsBackgroundHost
+  // will return true for the resulting browser. See
+  // https://developer.chrome.com/extensions/event_pages for more information
+  // about extension background script usage.
+  ///
+  /*--cef()--*/
+  virtual bool OnBeforeBackgroundBrowser(CefRefPtr<CefExtension> extension,
+                                         const CefString& url,
+                                         CefRefPtr<CefClient>& client,
+                                         CefBrowserSettings& settings) {
+    return false;
+  }
+
+  ///
+  // Called when an extension API (e.g. chrome.tabs.create) requests creation of
+  // a new browser. |extension| and |browser| are the source of the API call.
+  // |active_browser| may optionally be specified via the windowId property or
+  // returned via the GetActiveBrowser() callback and provides the default
+  // |client| and |settings| values for the new browser. |index| is the position
+  // value optionally specified via the index property. |url| is the URL that
+  // will be loaded in the browser. |active| is true if the new browser should
+  // be active when opened.  To allow creation of the browser optionally modify
+  // |windowInfo|, |client| and |settings| and return false. To cancel creation
+  // of the browser return true. Successful creation will be indicated by a call
+  // to CefLifeSpanHandler::OnAfterCreated. Any modifications to |windowInfo|
+  // will be ignored if |active_browser| is wrapped in a CefBrowserView.
+  ///
+  /*--cef()--*/
+  virtual bool OnBeforeBrowser(CefRefPtr<CefExtension> extension,
+                               CefRefPtr<CefBrowser> browser,
+                               CefRefPtr<CefBrowser> active_browser,
+                               int index,
+                               const CefString& url,
+                               bool active,
+                               CefWindowInfo& windowInfo,
+                               CefRefPtr<CefClient>& client,
+                               CefBrowserSettings& settings) {
+    return false;
+  }
+
+  ///
+  // Called when no tabId is specified to an extension API call that accepts a
+  // tabId parameter (e.g. chrome.tabs.*). |extension| and |browser| are the
+  // source of the API call. Return the browser that will be acted on by the API
+  // call or return NULL to act on |browser|. The returned browser must share
+  // the same CefRequestContext as |browser|. Incognito browsers should not be
+  // considered unless the source extension has incognito access enabled, in
+  // which case |include_incognito| will be true.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowser> GetActiveBrowser(
+      CefRefPtr<CefExtension> extension,
+      CefRefPtr<CefBrowser> browser,
+      bool include_incognito) {
+    return nullptr;
+  }
+
+  ///
+  // Called when the tabId associated with |target_browser| is specified to an
+  // extension API call that accepts a tabId parameter (e.g. chrome.tabs.*).
+  // |extension| and |browser| are the source of the API call. Return true
+  // to allow access of false to deny access. Access to incognito browsers
+  // should not be allowed unless the source extension has incognito access
+  // enabled, in which case |include_incognito| will be true.
+  ///
+  /*--cef()--*/
+  virtual bool CanAccessBrowser(CefRefPtr<CefExtension> extension,
+                                CefRefPtr<CefBrowser> browser,
+                                bool include_incognito,
+                                CefRefPtr<CefBrowser> target_browser) {
+    return true;
+  }
+
+  ///
+  // Called to retrieve an extension resource that would normally be loaded from
+  // disk (e.g. if a file parameter is specified to chrome.tabs.executeScript).
+  // |extension| and |browser| are the source of the resource request. |file| is
+  // the requested relative file path. To handle the resource request return
+  // true and execute |callback| either synchronously or asynchronously. For the
+  // default behavior which reads the resource from the extension directory on
+  // disk return false. Localization substitutions will not be applied to
+  // resources handled via this method.
+  ///
+  /*--cef()--*/
+  virtual bool GetExtensionResource(
+      CefRefPtr<CefExtension> extension,
+      CefRefPtr<CefBrowser> browser,
+      const CefString& file,
+      CefRefPtr<CefGetExtensionResourceCallback> callback) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_EXTENSION_HANDLER_H_
diff --git a/src/include/cef_file_util.h b/src/include/cef_file_util.h
new file mode 100644
index 0000000..8dd1ff6
--- /dev/null
+++ b/src/include/cef_file_util.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. Portions copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_FILE_UTIL_H_
+#define CEF_INCLUDE_CEF_FILE_UTIL_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Creates a directory and all parent directories if they don't already exist.
+// Returns true on successful creation or if the directory already exists. The
+// directory is only readable by the current user. Calling this function on the
+// browser process UI or IO threads is not allowed.
+///
+/*--cef()--*/
+bool CefCreateDirectory(const CefString& full_path);
+
+///
+// Get the temporary directory provided by the system.
+//
+// WARNING: In general, you should use the temp directory variants below instead
+// of this function. Those variants will ensure that the proper permissions are
+// set so that other users on the system can't edit them while they're open
+// (which could lead to security issues).
+///
+/*--cef()--*/
+bool CefGetTempDirectory(CefString& temp_dir);
+
+///
+// Creates a new directory. On Windows if |prefix| is provided the new directory
+// name is in the format of "prefixyyyy". Returns true on success and sets
+// |new_temp_path| to the full path of the directory that was created. The
+// directory is only readable by the current user. Calling this function on the
+// browser process UI or IO threads is not allowed.
+///
+/*--cef(optional_param=prefix)--*/
+bool CefCreateNewTempDirectory(const CefString& prefix,
+                               CefString& new_temp_path);
+
+///
+// Creates a directory within another directory. Extra characters will be
+// appended to |prefix| to ensure that the new directory does not have the same
+// name as an existing directory. Returns true on success and sets |new_dir| to
+// the full path of the directory that was created. The directory is only
+// readable by the current user. Calling this function on the browser process
+// UI or IO threads is not allowed.
+///
+/*--cef(optional_param=prefix)--*/
+bool CefCreateTempDirectoryInDirectory(const CefString& base_dir,
+                                       const CefString& prefix,
+                                       CefString& new_dir);
+
+///
+// Returns true if the given path exists and is a directory. Calling this
+// function on the browser process UI or IO threads is not allowed.
+///
+/*--cef()--*/
+bool CefDirectoryExists(const CefString& path);
+
+///
+// Deletes the given path whether it's a file or a directory. If |path| is a
+// directory all contents will be deleted.  If |recursive| is true any sub-
+// directories and their contents will also be deleted (equivalent to executing
+// "rm -rf", so use with caution). On POSIX environments if |path| is a symbolic
+// link then only the symlink will be deleted. Returns true on successful
+// deletion or if |path| does not exist. Calling this function on the browser
+// process UI or IO threads is not allowed.
+///
+/*--cef()--*/
+bool CefDeleteFile(const CefString& path, bool recursive);
+
+///
+// Writes the contents of |src_dir| into a zip archive at |dest_file|. If
+// |include_hidden_files| is true files starting with "." will be included.
+// Returns true on success.  Calling this function on the browser process UI or
+// IO threads is not allowed.
+///
+/*--cef()--*/
+bool CefZipDirectory(const CefString& src_dir,
+                     const CefString& dest_file,
+                     bool include_hidden_files);
+
+///
+// Loads the existing "Certificate Revocation Lists" file that is managed by
+// Google Chrome. This file can generally be found in Chrome's User Data
+// directory (e.g. "C:\Users\[User]\AppData\Local\Google\Chrome\User Data\" on
+// Windows) and is updated periodically by Chrome's component updater service.
+// Must be called in the browser process after the context has been initialized.
+// See https://dev.chromium.org/Home/chromium-security/crlsets for background.
+///
+/*--cef()--*/
+void CefLoadCRLSetsFile(const CefString& path);
+
+#endif  // CEF_INCLUDE_CEF_FILE_UTIL_H_
diff --git a/src/include/cef_find_handler.h b/src/include/cef_find_handler.h
new file mode 100644
index 0000000..a9cac0a
--- /dev/null
+++ b/src/include/cef_find_handler.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2015 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_FIND_HANDLER_H_
+#define CEF_INCLUDE_CEF_FIND_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+
+///
+// Implement this interface to handle events related to find results. The
+// methods of this class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefFindHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called to report find results returned by CefBrowserHost::Find().
+  // |identifer| is the identifier passed to Find(), |count| is the number of
+  // matches currently identified, |selectionRect| is the location of where the
+  // match was found (in window coordinates), |activeMatchOrdinal| is the
+  // current position in the search results, and |finalUpdate| is true if this
+  // is the last find notification.
+  ///
+  /*--cef()--*/
+  virtual void OnFindResult(CefRefPtr<CefBrowser> browser,
+                            int identifier,
+                            int count,
+                            const CefRect& selectionRect,
+                            int activeMatchOrdinal,
+                            bool finalUpdate) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_FIND_HANDLER_H_
diff --git a/src/include/cef_focus_handler.h b/src/include/cef_focus_handler.h
new file mode 100644
index 0000000..6fd44f5
--- /dev/null
+++ b/src/include/cef_focus_handler.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_FOCUS_HANDLER_H_
+#define CEF_INCLUDE_CEF_FOCUS_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_dom.h"
+#include "include/cef_frame.h"
+
+///
+// Implement this interface to handle events related to focus. The methods of
+// this class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefFocusHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_focus_source_t FocusSource;
+
+  ///
+  // Called when the browser component is about to loose focus. For instance, if
+  // focus was on the last HTML element and the user pressed the TAB key. |next|
+  // will be true if the browser is giving focus to the next component and false
+  // if the browser is giving focus to the previous component.
+  ///
+  /*--cef()--*/
+  virtual void OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) {}
+
+  ///
+  // Called when the browser component is requesting focus. |source| indicates
+  // where the focus request is originating from. Return false to allow the
+  // focus to be set or true to cancel setting the focus.
+  ///
+  /*--cef()--*/
+  virtual bool OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source) {
+    return false;
+  }
+
+  ///
+  // Called when the browser component has received focus.
+  ///
+  /*--cef()--*/
+  virtual void OnGotFocus(CefRefPtr<CefBrowser> browser) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_FOCUS_HANDLER_H_
diff --git a/src/include/cef_frame.h b/src/include/cef_frame.h
new file mode 100644
index 0000000..a1d0bba
--- /dev/null
+++ b/src/include/cef_frame.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_FRAME_H_
+#define CEF_INCLUDE_CEF_FRAME_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_dom.h"
+#include "include/cef_process_message.h"
+#include "include/cef_request.h"
+#include "include/cef_stream.h"
+#include "include/cef_string_visitor.h"
+
+class CefBrowser;
+class CefURLRequest;
+class CefURLRequestClient;
+class CefV8Context;
+
+///
+// Class used to represent a frame in the browser window. When used in the
+// browser process the methods of this class may be called on any thread unless
+// otherwise indicated in the comments. When used in the render process the
+// methods of this class may only be called on the main thread.
+///
+/*--cef(source=library)--*/
+class CefFrame : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // True if this object is currently attached to a valid frame.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Execute undo in this frame.
+  ///
+  /*--cef()--*/
+  virtual void Undo() = 0;
+
+  ///
+  // Execute redo in this frame.
+  ///
+  /*--cef()--*/
+  virtual void Redo() = 0;
+
+  ///
+  // Execute cut in this frame.
+  ///
+  /*--cef()--*/
+  virtual void Cut() = 0;
+
+  ///
+  // Execute copy in this frame.
+  ///
+  /*--cef()--*/
+  virtual void Copy() = 0;
+
+  ///
+  // Execute paste in this frame.
+  ///
+  /*--cef()--*/
+  virtual void Paste() = 0;
+
+  ///
+  // Execute delete in this frame.
+  ///
+  /*--cef(capi_name=del)--*/
+  virtual void Delete() = 0;
+
+  ///
+  // Execute select all in this frame.
+  ///
+  /*--cef()--*/
+  virtual void SelectAll() = 0;
+
+  ///
+  // Save this frame's HTML source to a temporary file and open it in the
+  // default text viewing application. This method can only be called from the
+  // browser process.
+  ///
+  /*--cef()--*/
+  virtual void ViewSource() = 0;
+
+  ///
+  // Retrieve this frame's HTML source as a string sent to the specified
+  // visitor.
+  ///
+  /*--cef()--*/
+  virtual void GetSource(CefRefPtr<CefStringVisitor> visitor) = 0;
+
+  ///
+  // Retrieve this frame's display text as a string sent to the specified
+  // visitor.
+  ///
+  /*--cef()--*/
+  virtual void GetText(CefRefPtr<CefStringVisitor> visitor) = 0;
+
+  ///
+  // Load the request represented by the |request| object.
+  //
+  // WARNING: This method will fail with "bad IPC message" reason
+  // INVALID_INITIATOR_ORIGIN (213) unless you first navigate to the
+  // request origin using some other mechanism (LoadURL, link click, etc).
+  ///
+  /*--cef()--*/
+  virtual void LoadRequest(CefRefPtr<CefRequest> request) = 0;
+
+  ///
+  // Load the specified |url|.
+  ///
+  /*--cef()--*/
+  virtual void LoadURL(const CefString& url) = 0;
+
+  ///
+  // Execute a string of JavaScript code in this frame. The |script_url|
+  // parameter is the URL where the script in question can be found, if any.
+  // The renderer may request this URL to show the developer the source of the
+  // error.  The |start_line| parameter is the base line number to use for error
+  // reporting.
+  ///
+  /*--cef(optional_param=script_url)--*/
+  virtual void ExecuteJavaScript(const CefString& code,
+                                 const CefString& script_url,
+                                 int start_line) = 0;
+
+  ///
+  // Returns true if this is the main (top-level) frame.
+  ///
+  /*--cef()--*/
+  virtual bool IsMain() = 0;
+
+  ///
+  // Returns true if this is the focused frame.
+  ///
+  /*--cef()--*/
+  virtual bool IsFocused() = 0;
+
+  ///
+  // Returns the name for this frame. If the frame has an assigned name (for
+  // example, set via the iframe "name" attribute) then that value will be
+  // returned. Otherwise a unique name will be constructed based on the frame
+  // parent hierarchy. The main (top-level) frame will always have an empty name
+  // value.
+  ///
+  /*--cef()--*/
+  virtual CefString GetName() = 0;
+
+  ///
+  // Returns the globally unique identifier for this frame or < 0 if the
+  // underlying frame does not yet exist.
+  ///
+  /*--cef()--*/
+  virtual int64 GetIdentifier() = 0;
+
+  ///
+  // Returns the parent of this frame or NULL if this is the main (top-level)
+  // frame.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFrame> GetParent() = 0;
+
+  ///
+  // Returns the URL currently loaded in this frame.
+  ///
+  /*--cef()--*/
+  virtual CefString GetURL() = 0;
+
+  ///
+  // Returns the browser that this frame belongs to.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
+
+  ///
+  // Get the V8 context associated with the frame. This method can only be
+  // called from the render process.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefV8Context> GetV8Context() = 0;
+
+  ///
+  // Visit the DOM document. This method can only be called from the render
+  // process.
+  ///
+  /*--cef()--*/
+  virtual void VisitDOM(CefRefPtr<CefDOMVisitor> visitor) = 0;
+
+  ///
+  // Create a new URL request that will be treated as originating from this
+  // frame and the associated browser. This request may be intercepted by the
+  // client via CefResourceRequestHandler or CefSchemeHandlerFactory. Use
+  // CefURLRequest::Create instead if you do not want the request to have this
+  // association, in which case it may be handled differently (see documentation
+  // on that method). Requests may originate from both the browser process and
+  // the render process.
+  //
+  // For requests originating from the browser process:
+  //   - POST data may only contain a single element of type PDE_TYPE_FILE or
+  //     PDE_TYPE_BYTES.
+  // For requests originating from the render process:
+  //   - POST data may only contain a single element of type PDE_TYPE_BYTES.
+  //   - If the response contains Content-Disposition or Mime-Type header values
+  //     that would not normally be rendered then the response may receive
+  //     special handling inside the browser (for example, via the file download
+  //     code path instead of the URL request code path).
+  //
+  // The |request| object will be marked as read-only after calling this method.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefURLRequest> CreateURLRequest(
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefURLRequestClient> client) = 0;
+
+  ///
+  // Send a message to the specified |target_process|. Message delivery is not
+  // guaranteed in all cases (for example, if the browser is closing,
+  // navigating, or if the target process crashes). Send an ACK message back
+  // from the target process if confirmation is required.
+  ///
+  /*--cef()--*/
+  virtual void SendProcessMessage(CefProcessId target_process,
+                                  CefRefPtr<CefProcessMessage> message) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_FRAME_H_
diff --git a/src/include/cef_image.h b/src/include/cef_image.h
new file mode 100644
index 0000000..6346812
--- /dev/null
+++ b/src/include/cef_image.h
@@ -0,0 +1,191 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_IMAGE_H_
+#define CEF_INCLUDE_CEF_IMAGE_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_values.h"
+
+///
+// Container for a single image represented at different scale factors. All
+// image representations should be the same size in density independent pixel
+// (DIP) units. For example, if the image at scale factor 1.0 is 100x100 pixels
+// then the image at scale factor 2.0 should be 200x200 pixels -- both images
+// will display with a DIP size of 100x100 units. The methods of this class can
+// be called on any browser process thread.
+///
+/*--cef(source=library)--*/
+class CefImage : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Create a new CefImage. It will initially be empty. Use the Add*() methods
+  // to add representations at different scale factors.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefImage> CreateImage();
+
+  ///
+  // Returns true if this Image is empty.
+  ///
+  /*--cef()--*/
+  virtual bool IsEmpty() = 0;
+
+  ///
+  // Returns true if this Image and |that| Image share the same underlying
+  // storage. Will also return true if both images are empty.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefImage> that) = 0;
+
+  ///
+  // Add a bitmap image representation for |scale_factor|. Only 32-bit RGBA/BGRA
+  // formats are supported. |pixel_width| and |pixel_height| are the bitmap
+  // representation size in pixel coordinates. |pixel_data| is the array of
+  // pixel data and should be |pixel_width| x |pixel_height| x 4 bytes in size.
+  // |color_type| and |alpha_type| values specify the pixel format.
+  ///
+  /*--cef()--*/
+  virtual bool AddBitmap(float scale_factor,
+                         int pixel_width,
+                         int pixel_height,
+                         cef_color_type_t color_type,
+                         cef_alpha_type_t alpha_type,
+                         const void* pixel_data,
+                         size_t pixel_data_size) = 0;
+
+  ///
+  // Add a PNG image representation for |scale_factor|. |png_data| is the image
+  // data of size |png_data_size|. Any alpha transparency in the PNG data will
+  // be maintained.
+  ///
+  /*--cef()--*/
+  virtual bool AddPNG(float scale_factor,
+                      const void* png_data,
+                      size_t png_data_size) = 0;
+
+  ///
+  // Create a JPEG image representation for |scale_factor|. |jpeg_data| is the
+  // image data of size |jpeg_data_size|. The JPEG format does not support
+  // transparency so the alpha byte will be set to 0xFF for all pixels.
+  ///
+  /*--cef()--*/
+  virtual bool AddJPEG(float scale_factor,
+                       const void* jpeg_data,
+                       size_t jpeg_data_size) = 0;
+
+  ///
+  // Returns the image width in density independent pixel (DIP) units.
+  ///
+  /*--cef()--*/
+  virtual size_t GetWidth() = 0;
+
+  ///
+  // Returns the image height in density independent pixel (DIP) units.
+  ///
+  /*--cef()--*/
+  virtual size_t GetHeight() = 0;
+
+  ///
+  // Returns true if this image contains a representation for |scale_factor|.
+  ///
+  /*--cef()--*/
+  virtual bool HasRepresentation(float scale_factor) = 0;
+
+  ///
+  // Removes the representation for |scale_factor|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool RemoveRepresentation(float scale_factor) = 0;
+
+  ///
+  // Returns information for the representation that most closely matches
+  // |scale_factor|. |actual_scale_factor| is the actual scale factor for the
+  // representation. |pixel_width| and |pixel_height| are the representation
+  // size in pixel coordinates. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool GetRepresentationInfo(float scale_factor,
+                                     float& actual_scale_factor,
+                                     int& pixel_width,
+                                     int& pixel_height) = 0;
+
+  ///
+  // Returns the bitmap representation that most closely matches |scale_factor|.
+  // Only 32-bit RGBA/BGRA formats are supported. |color_type| and |alpha_type|
+  // values specify the desired output pixel format. |pixel_width| and
+  // |pixel_height| are the output representation size in pixel coordinates.
+  // Returns a CefBinaryValue containing the pixel data on success or NULL on
+  // failure.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetAsBitmap(float scale_factor,
+                                                cef_color_type_t color_type,
+                                                cef_alpha_type_t alpha_type,
+                                                int& pixel_width,
+                                                int& pixel_height) = 0;
+
+  ///
+  // Returns the PNG representation that most closely matches |scale_factor|. If
+  // |with_transparency| is true any alpha transparency in the image will be
+  // represented in the resulting PNG data. |pixel_width| and |pixel_height| are
+  // the output representation size in pixel coordinates. Returns a
+  // CefBinaryValue containing the PNG image data on success or NULL on failure.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetAsPNG(float scale_factor,
+                                             bool with_transparency,
+                                             int& pixel_width,
+                                             int& pixel_height) = 0;
+
+  ///
+  // Returns the JPEG representation that most closely matches |scale_factor|.
+  // |quality| determines the compression level with 0 == lowest and 100 ==
+  // highest. The JPEG format does not support alpha transparency and the alpha
+  // channel, if any, will be discarded. |pixel_width| and |pixel_height| are
+  // the output representation size in pixel coordinates. Returns a
+  // CefBinaryValue containing the JPEG image data on success or NULL on
+  // failure.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetAsJPEG(float scale_factor,
+                                              int quality,
+                                              int& pixel_width,
+                                              int& pixel_height) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_IMAGE_H_
diff --git a/src/include/cef_jsdialog_handler.h b/src/include/cef_jsdialog_handler.h
new file mode 100644
index 0000000..b7e0945
--- /dev/null
+++ b/src/include/cef_jsdialog_handler.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_JSDIALOG_HANDLER_H_
+#define CEF_INCLUDE_CEF_JSDIALOG_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+
+///
+// Callback interface used for asynchronous continuation of JavaScript dialog
+// requests.
+///
+/*--cef(source=library)--*/
+class CefJSDialogCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Continue the JS dialog request. Set |success| to true if the OK button was
+  // pressed. The |user_input| value should be specified for prompt dialogs.
+  ///
+  /*--cef(capi_name=cont,optional_param=user_input)--*/
+  virtual void Continue(bool success, const CefString& user_input) = 0;
+};
+
+///
+// Implement this interface to handle events related to JavaScript dialogs. The
+// methods of this class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefJSDialogHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_jsdialog_type_t JSDialogType;
+
+  ///
+  // Called to run a JavaScript dialog. If |origin_url| is non-empty it can be
+  // passed to the CefFormatUrlForSecurityDisplay function to retrieve a secure
+  // and user-friendly display string. The |default_prompt_text| value will be
+  // specified for prompt dialogs only. Set |suppress_message| to true and
+  // return false to suppress the message (suppressing messages is preferable to
+  // immediately executing the callback as this is used to detect presumably
+  // malicious behavior like spamming alert messages in onbeforeunload). Set
+  // |suppress_message| to false and return false to use the default
+  // implementation (the default implementation will show one modal dialog at a
+  // time and suppress any additional dialog requests until the displayed dialog
+  // is dismissed). Return true if the application will use a custom dialog or
+  // if the callback has been executed immediately. Custom dialogs may be either
+  // modal or modeless. If a custom dialog is used the application must execute
+  // |callback| once the custom dialog is dismissed.
+  ///
+  /*--cef(optional_param=origin_url,optional_param=accept_lang,
+          optional_param=message_text,optional_param=default_prompt_text)--*/
+  virtual bool OnJSDialog(CefRefPtr<CefBrowser> browser,
+                          const CefString& origin_url,
+                          JSDialogType dialog_type,
+                          const CefString& message_text,
+                          const CefString& default_prompt_text,
+                          CefRefPtr<CefJSDialogCallback> callback,
+                          bool& suppress_message) {
+    return false;
+  }
+
+  ///
+  // Called to run a dialog asking the user if they want to leave a page. Return
+  // false to use the default dialog implementation. Return true if the
+  // application will use a custom dialog or if the callback has been executed
+  // immediately. Custom dialogs may be either modal or modeless. If a custom
+  // dialog is used the application must execute |callback| once the custom
+  // dialog is dismissed.
+  ///
+  /*--cef(optional_param=message_text)--*/
+  virtual bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
+                                    const CefString& message_text,
+                                    bool is_reload,
+                                    CefRefPtr<CefJSDialogCallback> callback) {
+    return false;
+  }
+
+  ///
+  // Called to cancel any pending dialogs and reset any saved dialog state. Will
+  // be called due to events like page navigation irregardless of whether any
+  // dialogs are currently pending.
+  ///
+  /*--cef()--*/
+  virtual void OnResetDialogState(CefRefPtr<CefBrowser> browser) {}
+
+  ///
+  // Called when the default implementation dialog is closed.
+  ///
+  /*--cef()--*/
+  virtual void OnDialogClosed(CefRefPtr<CefBrowser> browser) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_JSDIALOG_HANDLER_H_
diff --git a/src/include/cef_keyboard_handler.h b/src/include/cef_keyboard_handler.h
new file mode 100644
index 0000000..386491e
--- /dev/null
+++ b/src/include/cef_keyboard_handler.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_KEYBOARD_HANDLER_H_
+#define CEF_INCLUDE_CEF_KEYBOARD_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+
+///
+// Implement this interface to handle events related to keyboard input. The
+// methods of this class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefKeyboardHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called before a keyboard event is sent to the renderer. |event| contains
+  // information about the keyboard event. |os_event| is the operating system
+  // event message, if any. Return true if the event was handled or false
+  // otherwise. If the event will be handled in OnKeyEvent() as a keyboard
+  // shortcut set |is_keyboard_shortcut| to true and return false.
+  ///
+  /*--cef()--*/
+  virtual bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
+                             const CefKeyEvent& event,
+                             CefEventHandle os_event,
+                             bool* is_keyboard_shortcut) {
+    return false;
+  }
+
+  ///
+  // Called after the renderer and JavaScript in the page has had a chance to
+  // handle the event. |event| contains information about the keyboard event.
+  // |os_event| is the operating system event message, if any. Return true if
+  // the keyboard event was handled or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual bool OnKeyEvent(CefRefPtr<CefBrowser> browser,
+                          const CefKeyEvent& event,
+                          CefEventHandle os_event) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_KEYBOARD_HANDLER_H_
diff --git a/src/include/cef_life_span_handler.h b/src/include/cef_life_span_handler.h
new file mode 100644
index 0000000..c08cfcf
--- /dev/null
+++ b/src/include/cef_life_span_handler.h
@@ -0,0 +1,210 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_LIFE_SPAN_HANDLER_H_
+#define CEF_INCLUDE_CEF_LIFE_SPAN_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+
+class CefClient;
+
+///
+// Implement this interface to handle events related to browser life span. The
+// methods of this class will be called on the UI thread unless otherwise
+// indicated.
+///
+/*--cef(source=client)--*/
+class CefLifeSpanHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_window_open_disposition_t WindowOpenDisposition;
+
+  ///
+  // Called on the UI thread before a new popup browser is created. The
+  // |browser| and |frame| values represent the source of the popup request. The
+  // |target_url| and |target_frame_name| values indicate where the popup
+  // browser should navigate and may be empty if not specified with the request.
+  // The |target_disposition| value indicates where the user intended to open
+  // the popup (e.g. current tab, new tab, etc). The |user_gesture| value will
+  // be true if the popup was opened via explicit user gesture (e.g. clicking a
+  // link) or false if the popup opened automatically (e.g. via the
+  // DomContentLoaded event). The |popupFeatures| structure contains additional
+  // information about the requested popup window. To allow creation of the
+  // popup browser optionally modify |windowInfo|, |client|, |settings| and
+  // |no_javascript_access| and return false. To cancel creation of the popup
+  // browser return true. The |client| and |settings| values will default to the
+  // source browser's values. If the |no_javascript_access| value is set to
+  // false the new browser will not be scriptable and may not be hosted in the
+  // same renderer process as the source browser. Any modifications to
+  // |windowInfo| will be ignored if the parent browser is wrapped in a
+  // CefBrowserView. Popup browser creation will be canceled if the parent
+  // browser is destroyed before the popup browser creation completes (indicated
+  // by a call to OnAfterCreated for the popup browser). The |extra_info|
+  // parameter provides an opportunity to specify extra information specific
+  // to the created popup browser that will be passed to
+  // CefRenderProcessHandler::OnBrowserCreated() in the render process.
+  ///
+  /*--cef(optional_param=target_url,optional_param=target_frame_name)--*/
+  virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefFrame> frame,
+                             const CefString& target_url,
+                             const CefString& target_frame_name,
+                             WindowOpenDisposition target_disposition,
+                             bool user_gesture,
+                             const CefPopupFeatures& popupFeatures,
+                             CefWindowInfo& windowInfo,
+                             CefRefPtr<CefClient>& client,
+                             CefBrowserSettings& settings,
+                             CefRefPtr<CefDictionaryValue>& extra_info,
+                             bool* no_javascript_access) {
+    return false;
+  }
+
+  ///
+  // Called after a new browser is created. This callback will be the first
+  // notification that references |browser|.
+  ///
+  /*--cef()--*/
+  virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) {}
+
+  ///
+  // Called when a browser has recieved a request to close. This may result
+  // directly from a call to CefBrowserHost::*CloseBrowser() or indirectly if
+  // the browser is parented to a top-level window created by CEF and the user
+  // attempts to close that window (by clicking the 'X', for example). The
+  // DoClose() method will be called after the JavaScript 'onunload' event has
+  // been fired.
+  //
+  // An application should handle top-level owner window close notifications by
+  // calling CefBrowserHost::TryCloseBrowser() or
+  // CefBrowserHost::CloseBrowser(false) instead of allowing the window to close
+  // immediately (see the examples below). This gives CEF an opportunity to
+  // process the 'onbeforeunload' event and optionally cancel the close before
+  // DoClose() is called.
+  //
+  // When windowed rendering is enabled CEF will internally create a window or
+  // view to host the browser. In that case returning false from DoClose() will
+  // send the standard close notification to the browser's top-level owner
+  // window (e.g. WM_CLOSE on Windows, performClose: on OS X, "delete_event" on
+  // Linux or CefWindowDelegate::CanClose() callback from Views). If the
+  // browser's host window/view has already been destroyed (via view hierarchy
+  // tear-down, for example) then DoClose() will not be called for that browser
+  // since is no longer possible to cancel the close.
+  //
+  // When windowed rendering is disabled returning false from DoClose() will
+  // cause the browser object to be destroyed immediately.
+  //
+  // If the browser's top-level owner window requires a non-standard close
+  // notification then send that notification from DoClose() and return true.
+  //
+  // The CefLifeSpanHandler::OnBeforeClose() method will be called after
+  // DoClose() (if DoClose() is called) and immediately before the browser
+  // object is destroyed. The application should only exit after OnBeforeClose()
+  // has been called for all existing browsers.
+  //
+  // The below examples describe what should happen during window close when the
+  // browser is parented to an application-provided top-level window.
+  //
+  // Example 1: Using CefBrowserHost::TryCloseBrowser(). This is recommended for
+  // clients using standard close handling and windows created on the browser
+  // process UI thread.
+  // 1.  User clicks the window close button which sends a close notification to
+  //     the application's top-level window.
+  // 2.  Application's top-level window receives the close notification and
+  //     calls TryCloseBrowser() (which internally calls CloseBrowser(false)).
+  //     TryCloseBrowser() returns false so the client cancels the window close.
+  // 3.  JavaScript 'onbeforeunload' handler executes and shows the close
+  //     confirmation dialog (which can be overridden via
+  //     CefJSDialogHandler::OnBeforeUnloadDialog()).
+  // 4.  User approves the close.
+  // 5.  JavaScript 'onunload' handler executes.
+  // 6.  CEF sends a close notification to the application's top-level window
+  //     (because DoClose() returned false by default).
+  // 7.  Application's top-level window receives the close notification and
+  //     calls TryCloseBrowser(). TryCloseBrowser() returns true so the client
+  //     allows the window close.
+  // 8.  Application's top-level window is destroyed.
+  // 9.  Application's OnBeforeClose() handler is called and the browser object
+  //     is destroyed.
+  // 10. Application exits by calling CefQuitMessageLoop() if no other browsers
+  //     exist.
+  //
+  // Example 2: Using CefBrowserHost::CloseBrowser(false) and implementing the
+  // DoClose() callback. This is recommended for clients using non-standard
+  // close handling or windows that were not created on the browser process UI
+  // thread.
+  // 1.  User clicks the window close button which sends a close notification to
+  //     the application's top-level window.
+  // 2.  Application's top-level window receives the close notification and:
+  //     A. Calls CefBrowserHost::CloseBrowser(false).
+  //     B. Cancels the window close.
+  // 3.  JavaScript 'onbeforeunload' handler executes and shows the close
+  //     confirmation dialog (which can be overridden via
+  //     CefJSDialogHandler::OnBeforeUnloadDialog()).
+  // 4.  User approves the close.
+  // 5.  JavaScript 'onunload' handler executes.
+  // 6.  Application's DoClose() handler is called. Application will:
+  //     A. Set a flag to indicate that the next close attempt will be allowed.
+  //     B. Return false.
+  // 7.  CEF sends an close notification to the application's top-level window.
+  // 8.  Application's top-level window receives the close notification and
+  //     allows the window to close based on the flag from #6B.
+  // 9.  Application's top-level window is destroyed.
+  // 10. Application's OnBeforeClose() handler is called and the browser object
+  //     is destroyed.
+  // 11. Application exits by calling CefQuitMessageLoop() if no other browsers
+  //     exist.
+  ///
+  /*--cef()--*/
+  virtual bool DoClose(CefRefPtr<CefBrowser> browser) { return false; }
+
+  ///
+  // Called just before a browser is destroyed. Release all references to the
+  // browser object and do not attempt to execute any methods on the browser
+  // object (other than GetIdentifier or IsSame) after this callback returns.
+  // This callback will be the last notification that references |browser| on
+  // the UI thread. Any in-progress network requests associated with |browser|
+  // will be aborted when the browser is destroyed, and
+  // CefResourceRequestHandler callbacks related to those requests may still
+  // arrive on the IO thread after this method is called. See DoClose()
+  // documentation for additional usage information.
+  ///
+  /*--cef()--*/
+  virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_LIFE_SPAN_HANDLER_H_
diff --git a/src/include/cef_load_handler.h b/src/include/cef_load_handler.h
new file mode 100644
index 0000000..66b5567
--- /dev/null
+++ b/src/include/cef_load_handler.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_LOAD_HANDLER_H_
+#define CEF_INCLUDE_CEF_LOAD_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+
+///
+// Implement this interface to handle events related to browser load status. The
+// methods of this class will be called on the browser process UI thread or
+// render process main thread (TID_RENDERER).
+///
+/*--cef(source=client)--*/
+class CefLoadHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_errorcode_t ErrorCode;
+  typedef cef_transition_type_t TransitionType;
+
+  ///
+  // Called when the loading state has changed. This callback will be executed
+  // twice -- once when loading is initiated either programmatically or by user
+  // action, and once when loading is terminated due to completion, cancellation
+  // of failure. It will be called before any calls to OnLoadStart and after all
+  // calls to OnLoadError and/or OnLoadEnd.
+  ///
+  /*--cef()--*/
+  virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                                    bool isLoading,
+                                    bool canGoBack,
+                                    bool canGoForward) {}
+
+  ///
+  // Called after a navigation has been committed and before the browser begins
+  // loading contents in the frame. The |frame| value will never be empty --
+  // call the IsMain() method to check if this frame is the main frame.
+  // |transition_type| provides information about the source of the navigation
+  // and an accurate value is only available in the browser process. Multiple
+  // frames may be loading at the same time. Sub-frames may start or continue
+  // loading after the main frame load has ended. This method will not be called
+  // for same page navigations (fragments, history state, etc.) or for
+  // navigations that fail or are canceled before commit. For notification of
+  // overall browser load status use OnLoadingStateChange instead.
+  ///
+  /*--cef()--*/
+  virtual void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           TransitionType transition_type) {}
+
+  ///
+  // Called when the browser is done loading a frame. The |frame| value will
+  // never be empty -- call the IsMain() method to check if this frame is the
+  // main frame. Multiple frames may be loading at the same time. Sub-frames may
+  // start or continue loading after the main frame load has ended. This method
+  // will not be called for same page navigations (fragments, history state,
+  // etc.) or for navigations that fail or are canceled before commit. For
+  // notification of overall browser load status use OnLoadingStateChange
+  // instead.
+  ///
+  /*--cef()--*/
+  virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         int httpStatusCode) {}
+
+  ///
+  // Called when a navigation fails or is canceled. This method may be called
+  // by itself if before commit or in combination with OnLoadStart/OnLoadEnd if
+  // after commit. |errorCode| is the error code number, |errorText| is the
+  // error text and |failedUrl| is the URL that failed to load.
+  // See net\base\net_error_list.h for complete descriptions of the error codes.
+  ///
+  /*--cef(optional_param=errorText)--*/
+  virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           ErrorCode errorCode,
+                           const CefString& errorText,
+                           const CefString& failedUrl) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_LOAD_HANDLER_H_
diff --git a/src/include/cef_media_router.h b/src/include/cef_media_router.h
new file mode 100644
index 0000000..2c7cb59
--- /dev/null
+++ b/src/include/cef_media_router.h
@@ -0,0 +1,332 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_MEDIA_ROUTER_H_
+#define CEF_INCLUDE_CEF_MEDIA_ROUTER_H_
+#pragma once
+
+#include <vector>
+#include "include/cef_base.h"
+#include "include/cef_registration.h"
+
+class CefMediaObserver;
+class CefMediaRoute;
+class CefMediaRouteCreateCallback;
+class CefMediaSink;
+class CefMediaSinkDeviceInfoCallback;
+class CefMediaSource;
+
+///
+// Supports discovery of and communication with media devices on the local
+// network via the Cast and DIAL protocols. The methods of this class may be
+// called on any browser process thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefMediaRouter : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the MediaRouter object associated with the global request context.
+  // Equivalent to calling
+  // CefRequestContext::GetGlobalContext()->GetMediaRouter().
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefMediaRouter> GetGlobalMediaRouter();
+
+  ///
+  // Add an observer for MediaRouter events. The observer will remain registered
+  // until the returned Registration object is destroyed.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRegistration> AddObserver(
+      CefRefPtr<CefMediaObserver> observer) = 0;
+
+  ///
+  // Returns a MediaSource object for the specified media source URN. Supported
+  // URN schemes include "cast:" and "dial:", and will be already known by the
+  // client application (e.g. "cast:<appId>?clientId=<clientId>").
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMediaSource> GetSource(const CefString& urn) = 0;
+
+  ///
+  // Trigger an asynchronous call to CefMediaObserver::OnSinks on all
+  // registered observers.
+  ///
+  /*--cef()--*/
+  virtual void NotifyCurrentSinks() = 0;
+
+  ///
+  // Create a new route between |source| and |sink|. Source and sink must be
+  // valid, compatible (as reported by CefMediaSink::IsCompatibleWith), and a
+  // route between them must not already exist. |callback| will be executed
+  // on success or failure. If route creation succeeds it will also trigger an
+  // asynchronous call to CefMediaObserver::OnRoutes on all registered
+  // observers.
+  ///
+  /*--cef()--*/
+  virtual void CreateRoute(CefRefPtr<CefMediaSource> source,
+                           CefRefPtr<CefMediaSink> sink,
+                           CefRefPtr<CefMediaRouteCreateCallback> callback) = 0;
+
+  ///
+  // Trigger an asynchronous call to CefMediaObserver::OnRoutes on all
+  // registered observers.
+  ///
+  /*--cef()--*/
+  virtual void NotifyCurrentRoutes() = 0;
+};
+
+///
+// Implemented by the client to observe MediaRouter events and registered via
+// CefMediaRouter::AddObserver. The methods of this class will be called on the
+// browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefMediaObserver : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_media_route_connection_state_t ConnectionState;
+
+  ///
+  // The list of available media sinks has changed or
+  // CefMediaRouter::NotifyCurrentSinks was called.
+  ///
+  /*--cef()--*/
+  virtual void OnSinks(const std::vector<CefRefPtr<CefMediaSink>>& sinks) = 0;
+
+  ///
+  // The list of available media routes has changed or
+  // CefMediaRouter::NotifyCurrentRoutes was called.
+  ///
+  /*--cef()--*/
+  virtual void OnRoutes(
+      const std::vector<CefRefPtr<CefMediaRoute>>& routes) = 0;
+
+  ///
+  // The connection state of |route| has changed.
+  ///
+  /*--cef()--*/
+  virtual void OnRouteStateChanged(CefRefPtr<CefMediaRoute> route,
+                                   ConnectionState state) = 0;
+
+  ///
+  // A message was recieved over |route|. |message| is only valid for
+  // the scope of this callback and should be copied if necessary.
+  ///
+  /*--cef()--*/
+  virtual void OnRouteMessageReceived(CefRefPtr<CefMediaRoute> route,
+                                      const void* message,
+                                      size_t message_size) = 0;
+};
+
+///
+// Represents the route between a media source and sink. Instances of this
+// object are created via CefMediaRouter::CreateRoute and retrieved via
+// CefMediaObserver::OnRoutes. Contains the status and metadata of a
+// routing operation. The methods of this class may be called on any browser
+// process thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefMediaRoute : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the ID for this route.
+  ///
+  /*--cef()--*/
+  virtual CefString GetId() = 0;
+
+  ///
+  // Returns the source associated with this route.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMediaSource> GetSource() = 0;
+
+  ///
+  // Returns the sink associated with this route.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMediaSink> GetSink() = 0;
+
+  ///
+  // Send a message over this route. |message| will be copied if necessary.
+  ///
+  /*--cef()--*/
+  virtual void SendRouteMessage(const void* message, size_t message_size) = 0;
+
+  ///
+  // Terminate this route. Will result in an asynchronous call to
+  // CefMediaObserver::OnRoutes on all registered observers.
+  ///
+  /*--cef()--*/
+  virtual void Terminate() = 0;
+};
+
+///
+// Callback interface for CefMediaRouter::CreateRoute. The methods of this
+// class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefMediaRouteCreateCallback : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_media_route_create_result_t RouteCreateResult;
+
+  ///
+  // Method that will be executed when the route creation has finished. |result|
+  // will be CEF_MRCR_OK if the route creation succeeded. |error| will be a
+  // description of the error if the route creation failed. |route| is the
+  // resulting route, or empty if the route creation failed.
+  ///
+  /*--cef(optional_param=error,optional_param=route)--*/
+  virtual void OnMediaRouteCreateFinished(RouteCreateResult result,
+                                          const CefString& error,
+                                          CefRefPtr<CefMediaRoute> route) = 0;
+};
+
+///
+// Represents a sink to which media can be routed. Instances of this object are
+// retrieved via CefMediaObserver::OnSinks. The methods of this class may
+// be called on any browser process thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefMediaSink : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_media_sink_icon_type_t IconType;
+
+  ///
+  // Returns the ID for this sink.
+  ///
+  /*--cef()--*/
+  virtual CefString GetId() = 0;
+
+  ///
+  // Returns true if this sink is valid.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns the name of this sink.
+  ///
+  /*--cef()--*/
+  virtual CefString GetName() = 0;
+
+  ///
+  // Returns the description of this sink.
+  ///
+  /*--cef()--*/
+  virtual CefString GetDescription() = 0;
+
+  ///
+  // Returns the icon type for this sink.
+  ///
+  /*--cef(default_retval=CEF_MSIT_GENERIC)--*/
+  virtual IconType GetIconType() = 0;
+
+  ///
+  // Asynchronously retrieves device info.
+  ///
+  /*--cef()--*/
+  virtual void GetDeviceInfo(
+      CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) = 0;
+
+  ///
+  // Returns true if this sink accepts content via Cast.
+  ///
+  /*--cef()--*/
+  virtual bool IsCastSink() = 0;
+
+  ///
+  // Returns true if this sink accepts content via DIAL.
+  ///
+  /*--cef()--*/
+  virtual bool IsDialSink() = 0;
+
+  ///
+  // Returns true if this sink is compatible with |source|.
+  ///
+  /*--cef()--*/
+  virtual bool IsCompatibleWith(CefRefPtr<CefMediaSource> source) = 0;
+};
+
+///
+// Callback interface for CefMediaSink::GetDeviceInfo. The methods of this
+// class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefMediaSinkDeviceInfoCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be executed asyncronously once device information has been
+  // retrieved.
+  ///
+  /*--cef()--*/
+  virtual void OnMediaSinkDeviceInfo(
+      const CefMediaSinkDeviceInfo& device_info) = 0;
+};
+
+///
+// Represents a source from which media can be routed. Instances of this object
+// are retrieved via CefMediaRouter::GetSource. The methods of this class may be
+// called on any browser process thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefMediaSource : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the ID (media source URN or URL) for this source.
+  ///
+  /*--cef()--*/
+  virtual CefString GetId() = 0;
+
+  ///
+  // Returns true if this source is valid.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if this source outputs its content via Cast.
+  ///
+  /*--cef()--*/
+  virtual bool IsCastSource() = 0;
+
+  ///
+  // Returns true if this source outputs its content via DIAL.
+  ///
+  /*--cef()--*/
+  virtual bool IsDialSource() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_MEDIA_ROUTER_H_
diff --git a/src/include/cef_menu_model.h b/src/include/cef_menu_model.h
new file mode 100644
index 0000000..1c9a7c0
--- /dev/null
+++ b/src/include/cef_menu_model.h
@@ -0,0 +1,491 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_MENU_MODEL_H_
+#define CEF_INCLUDE_CEF_MENU_MODEL_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_menu_model_delegate.h"
+
+///
+// Supports creation and modification of menus. See cef_menu_id_t for the
+// command ids that have default implementations. All user-defined command ids
+// should be between MENU_ID_USER_FIRST and MENU_ID_USER_LAST. The methods of
+// this class can only be accessed on the browser process the UI thread.
+///
+/*--cef(source=library)--*/
+class CefMenuModel : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_menu_item_type_t MenuItemType;
+
+  ///
+  // Create a new MenuModel with the specified |delegate|.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefMenuModel> CreateMenuModel(
+      CefRefPtr<CefMenuModelDelegate> delegate);
+
+  ///
+  // Returns true if this menu is a submenu.
+  ///
+  /*--cef()--*/
+  virtual bool IsSubMenu() = 0;
+
+  ///
+  // Clears the menu. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool Clear() = 0;
+
+  ///
+  // Returns the number of items in this menu.
+  ///
+  /*--cef()--*/
+  virtual int GetCount() = 0;
+
+  ///
+  // Add a separator to the menu. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool AddSeparator() = 0;
+
+  ///
+  // Add an item to the menu. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool AddItem(int command_id, const CefString& label) = 0;
+
+  ///
+  // Add a check item to the menu. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool AddCheckItem(int command_id, const CefString& label) = 0;
+  ///
+  // Add a radio item to the menu. Only a single item with the specified
+  // |group_id| can be checked at a time. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool AddRadioItem(int command_id,
+                            const CefString& label,
+                            int group_id) = 0;
+
+  ///
+  // Add a sub-menu to the menu. The new sub-menu is returned.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMenuModel> AddSubMenu(int command_id,
+                                             const CefString& label) = 0;
+
+  ///
+  // Insert a separator in the menu at the specified |index|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool InsertSeparatorAt(int index) = 0;
+
+  ///
+  // Insert an item in the menu at the specified |index|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool InsertItemAt(int index,
+                            int command_id,
+                            const CefString& label) = 0;
+
+  ///
+  // Insert a check item in the menu at the specified |index|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool InsertCheckItemAt(int index,
+                                 int command_id,
+                                 const CefString& label) = 0;
+
+  ///
+  // Insert a radio item in the menu at the specified |index|. Only a single
+  // item with the specified |group_id| can be checked at a time. Returns true
+  // on success.
+  ///
+  /*--cef()--*/
+  virtual bool InsertRadioItemAt(int index,
+                                 int command_id,
+                                 const CefString& label,
+                                 int group_id) = 0;
+
+  ///
+  // Insert a sub-menu in the menu at the specified |index|. The new sub-menu
+  // is returned.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMenuModel> InsertSubMenuAt(int index,
+                                                  int command_id,
+                                                  const CefString& label) = 0;
+
+  ///
+  // Removes the item with the specified |command_id|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool Remove(int command_id) = 0;
+
+  ///
+  // Removes the item at the specified |index|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool RemoveAt(int index) = 0;
+
+  ///
+  // Returns the index associated with the specified |command_id| or -1 if not
+  // found due to the command id not existing in the menu.
+  ///
+  /*--cef()--*/
+  virtual int GetIndexOf(int command_id) = 0;
+
+  ///
+  // Returns the command id at the specified |index| or -1 if not found due to
+  // invalid range or the index being a separator.
+  ///
+  /*--cef()--*/
+  virtual int GetCommandIdAt(int index) = 0;
+
+  ///
+  // Sets the command id at the specified |index|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetCommandIdAt(int index, int command_id) = 0;
+
+  ///
+  // Returns the label for the specified |command_id| or empty if not found.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLabel(int command_id) = 0;
+
+  ///
+  // Returns the label at the specified |index| or empty if not found due to
+  // invalid range or the index being a separator.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLabelAt(int index) = 0;
+
+  ///
+  // Sets the label for the specified |command_id|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetLabel(int command_id, const CefString& label) = 0;
+
+  ///
+  // Set the label at the specified |index|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetLabelAt(int index, const CefString& label) = 0;
+
+  ///
+  // Returns the item type for the specified |command_id|.
+  ///
+  /*--cef(default_retval=MENUITEMTYPE_NONE)--*/
+  virtual MenuItemType GetType(int command_id) = 0;
+
+  ///
+  // Returns the item type at the specified |index|.
+  ///
+  /*--cef(default_retval=MENUITEMTYPE_NONE)--*/
+  virtual MenuItemType GetTypeAt(int index) = 0;
+
+  ///
+  // Returns the group id for the specified |command_id| or -1 if invalid.
+  ///
+  /*--cef()--*/
+  virtual int GetGroupId(int command_id) = 0;
+
+  ///
+  // Returns the group id at the specified |index| or -1 if invalid.
+  ///
+  /*--cef()--*/
+  virtual int GetGroupIdAt(int index) = 0;
+
+  ///
+  // Sets the group id for the specified |command_id|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetGroupId(int command_id, int group_id) = 0;
+
+  ///
+  // Sets the group id at the specified |index|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetGroupIdAt(int index, int group_id) = 0;
+
+  ///
+  // Returns the submenu for the specified |command_id| or empty if invalid.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMenuModel> GetSubMenu(int command_id) = 0;
+
+  ///
+  // Returns the submenu at the specified |index| or empty if invalid.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMenuModel> GetSubMenuAt(int index) = 0;
+
+  ///
+  // Returns true if the specified |command_id| is visible.
+  ///
+  /*--cef()--*/
+  virtual bool IsVisible(int command_id) = 0;
+
+  ///
+  // Returns true if the specified |index| is visible.
+  ///
+  /*--cef()--*/
+  virtual bool IsVisibleAt(int index) = 0;
+
+  ///
+  // Change the visibility of the specified |command_id|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool SetVisible(int command_id, bool visible) = 0;
+
+  ///
+  // Change the visibility at the specified |index|. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetVisibleAt(int index, bool visible) = 0;
+
+  ///
+  // Returns true if the specified |command_id| is enabled.
+  ///
+  /*--cef()--*/
+  virtual bool IsEnabled(int command_id) = 0;
+
+  ///
+  // Returns true if the specified |index| is enabled.
+  ///
+  /*--cef()--*/
+  virtual bool IsEnabledAt(int index) = 0;
+
+  ///
+  // Change the enabled status of the specified |command_id|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool SetEnabled(int command_id, bool enabled) = 0;
+
+  ///
+  // Change the enabled status at the specified |index|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool SetEnabledAt(int index, bool enabled) = 0;
+
+  ///
+  // Returns true if the specified |command_id| is checked. Only applies to
+  // check and radio items.
+  ///
+  /*--cef()--*/
+  virtual bool IsChecked(int command_id) = 0;
+
+  ///
+  // Returns true if the specified |index| is checked. Only applies to check
+  // and radio items.
+  ///
+  /*--cef()--*/
+  virtual bool IsCheckedAt(int index) = 0;
+
+  ///
+  // Check the specified |command_id|. Only applies to check and radio items.
+  // Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetChecked(int command_id, bool checked) = 0;
+
+  ///
+  // Check the specified |index|. Only applies to check and radio items. Returns
+  // true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetCheckedAt(int index, bool checked) = 0;
+
+  ///
+  // Returns true if the specified |command_id| has a keyboard accelerator
+  // assigned.
+  ///
+  /*--cef()--*/
+  virtual bool HasAccelerator(int command_id) = 0;
+
+  ///
+  // Returns true if the specified |index| has a keyboard accelerator assigned.
+  ///
+  /*--cef()--*/
+  virtual bool HasAcceleratorAt(int index) = 0;
+
+  ///
+  // Set the keyboard accelerator for the specified |command_id|. |key_code| can
+  // be any virtual key or character value. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetAccelerator(int command_id,
+                              int key_code,
+                              bool shift_pressed,
+                              bool ctrl_pressed,
+                              bool alt_pressed) = 0;
+
+  ///
+  // Set the keyboard accelerator at the specified |index|. |key_code| can be
+  // any virtual key or character value. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetAcceleratorAt(int index,
+                                int key_code,
+                                bool shift_pressed,
+                                bool ctrl_pressed,
+                                bool alt_pressed) = 0;
+
+  ///
+  // Remove the keyboard accelerator for the specified |command_id|. Returns
+  // true on success.
+  ///
+  /*--cef()--*/
+  virtual bool RemoveAccelerator(int command_id) = 0;
+
+  ///
+  // Remove the keyboard accelerator at the specified |index|. Returns true on
+  // success.
+  ///
+  /*--cef()--*/
+  virtual bool RemoveAcceleratorAt(int index) = 0;
+
+  ///
+  // Retrieves the keyboard accelerator for the specified |command_id|. Returns
+  // true on success.
+  ///
+  /*--cef()--*/
+  virtual bool GetAccelerator(int command_id,
+                              int& key_code,
+                              bool& shift_pressed,
+                              bool& ctrl_pressed,
+                              bool& alt_pressed) = 0;
+
+  ///
+  // Retrieves the keyboard accelerator for the specified |index|. Returns true
+  // on success.
+  ///
+  /*--cef()--*/
+  virtual bool GetAcceleratorAt(int index,
+                                int& key_code,
+                                bool& shift_pressed,
+                                bool& ctrl_pressed,
+                                bool& alt_pressed) = 0;
+
+  ///
+  // Set the explicit color for |command_id| and |color_type| to |color|.
+  // Specify a |color| value of 0 to remove the explicit color. If no explicit
+  // color or default color is set for |color_type| then the system color will
+  // be used. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetColor(int command_id,
+                        cef_menu_color_type_t color_type,
+                        cef_color_t color) = 0;
+
+  ///
+  // Set the explicit color for |command_id| and |index| to |color|. Specify a
+  // |color| value of 0 to remove the explicit color. Specify an |index| value
+  // of -1 to set the default color for items that do not have an explicit
+  // color set. If no explicit color or default color is set for |color_type|
+  // then the system color will be used. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetColorAt(int index,
+                          cef_menu_color_type_t color_type,
+                          cef_color_t color) = 0;
+
+  ///
+  // Returns in |color| the color that was explicitly set for |command_id| and
+  // |color_type|. If a color was not set then 0 will be returned in |color|.
+  // Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool GetColor(int command_id,
+                        cef_menu_color_type_t color_type,
+                        cef_color_t& color) = 0;
+
+  ///
+  // Returns in |color| the color that was explicitly set for |command_id| and
+  // |color_type|. Specify an |index| value of -1 to return the default color
+  // in |color|. If a color was not set then 0 will be returned in |color|.
+  // Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool GetColorAt(int index,
+                          cef_menu_color_type_t color_type,
+                          cef_color_t& color) = 0;
+
+  ///
+  // Sets the font list for the specified |command_id|. If |font_list| is empty
+  // the system font will be used. Returns true on success. The format is
+  // "<FONT_FAMILY_LIST>,[STYLES] <SIZE>", where:
+  // - FONT_FAMILY_LIST is a comma-separated list of font family names,
+  // - STYLES is an optional space-separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings:
+  // - "Arial, Helvetica, Bold Italic 14px"
+  // - "Arial, 14px"
+  ///
+  /*--cef(optional_param=font_list)--*/
+  virtual bool SetFontList(int command_id, const CefString& font_list) = 0;
+
+  ///
+  // Sets the font list for the specified |index|. Specify an |index| value of
+  // -1 to set the default font. If |font_list| is empty the system font will
+  // be used. Returns true on success. The format is
+  // "<FONT_FAMILY_LIST>,[STYLES] <SIZE>", where:
+  // - FONT_FAMILY_LIST is a comma-separated list of font family names,
+  // - STYLES is an optional space-separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings:
+  // - "Arial, Helvetica, Bold Italic 14px"
+  // - "Arial, 14px"
+  ///
+  /*--cef(optional_param=font_list)--*/
+  virtual bool SetFontListAt(int index, const CefString& font_list) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_MENU_MODEL_H_
diff --git a/src/include/cef_menu_model_delegate.h b/src/include/cef_menu_model_delegate.h
new file mode 100644
index 0000000..e3ddfbd
--- /dev/null
+++ b/src/include/cef_menu_model_delegate.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_MENU_MODEL_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_MENU_MODEL_DELEGATE_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+class CefMenuModel;
+
+///
+// Implement this interface to handle menu model events. The methods of this
+// class will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+/*--cef(source=client)--*/
+class CefMenuModelDelegate : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Perform the action associated with the specified |command_id| and
+  // optional |event_flags|.
+  ///
+  /*--cef()--*/
+  virtual void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                              int command_id,
+                              cef_event_flags_t event_flags) = 0;
+
+  ///
+  // Called when the user moves the mouse outside the menu and over the owning
+  // window.
+  ///
+  /*--cef()--*/
+  virtual void MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
+                                const CefPoint& screen_point) {}
+
+  ///
+  // Called on unhandled open submenu keyboard commands. |is_rtl| will be true
+  // if the menu is displaying a right-to-left language.
+  ///
+  /*--cef()--*/
+  virtual void UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                                    bool is_rtl) {}
+
+  ///
+  // Called on unhandled close submenu keyboard commands. |is_rtl| will be true
+  // if the menu is displaying a right-to-left language.
+  ///
+  /*--cef()--*/
+  virtual void UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                                     bool is_rtl) {}
+
+  ///
+  // The menu is about to show.
+  ///
+  /*--cef()--*/
+  virtual void MenuWillShow(CefRefPtr<CefMenuModel> menu_model) {}
+
+  ///
+  // The menu has closed.
+  ///
+  /*--cef()--*/
+  virtual void MenuClosed(CefRefPtr<CefMenuModel> menu_model) {}
+
+  ///
+  // Optionally modify a menu item label. Return true if |label| was modified.
+  ///
+  /*--cef()--*/
+  virtual bool FormatLabel(CefRefPtr<CefMenuModel> menu_model,
+                           CefString& label) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_MENU_MODEL_DELEGATE_H_
diff --git a/src/include/cef_navigation_entry.h b/src/include/cef_navigation_entry.h
new file mode 100644
index 0000000..292f5b6
--- /dev/null
+++ b/src/include/cef_navigation_entry.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_NAVIGATION_ENTRY_H_
+#define CEF_INCLUDE_CEF_NAVIGATION_ENTRY_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_ssl_status.h"
+
+///
+// Class used to represent an entry in navigation history.
+///
+/*--cef(source=library)--*/
+class CefNavigationEntry : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_transition_type_t TransitionType;
+
+  ///
+  // Returns true if this object is valid. Do not call any other methods if this
+  // function returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns the actual URL of the page. For some pages this may be data: URL or
+  // similar. Use GetDisplayURL() to return a display-friendly version.
+  ///
+  /*--cef()--*/
+  virtual CefString GetURL() = 0;
+
+  ///
+  // Returns a display-friendly version of the URL.
+  ///
+  /*--cef()--*/
+  virtual CefString GetDisplayURL() = 0;
+
+  ///
+  // Returns the original URL that was entered by the user before any redirects.
+  ///
+  /*--cef()--*/
+  virtual CefString GetOriginalURL() = 0;
+
+  ///
+  // Returns the title set by the page. This value may be empty.
+  ///
+  /*--cef()--*/
+  virtual CefString GetTitle() = 0;
+
+  ///
+  // Returns the transition type which indicates what the user did to move to
+  // this page from the previous page.
+  ///
+  /*--cef(default_retval=TT_EXPLICIT)--*/
+  virtual TransitionType GetTransitionType() = 0;
+
+  ///
+  // Returns true if this navigation includes post data.
+  ///
+  /*--cef()--*/
+  virtual bool HasPostData() = 0;
+
+  ///
+  // Returns the time for the last known successful navigation completion. A
+  // navigation may be completed more than once if the page is reloaded. May be
+  // 0 if the navigation has not yet completed.
+  ///
+  /*--cef()--*/
+  virtual CefTime GetCompletionTime() = 0;
+
+  ///
+  // Returns the HTTP status code for the last known successful navigation
+  // response. May be 0 if the response has not yet been received or if the
+  // navigation has not yet completed.
+  ///
+  /*--cef()--*/
+  virtual int GetHttpStatusCode() = 0;
+
+  ///
+  // Returns the SSL information for this navigation entry.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefSSLStatus> GetSSLStatus() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_NAVIGATION_ENTRY_H_
diff --git a/src/include/cef_origin_whitelist.h b/src/include/cef_origin_whitelist.h
new file mode 100644
index 0000000..ad17318
--- /dev/null
+++ b/src/include/cef_origin_whitelist.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_ORIGIN_WHITELIST_H_
+#define CEF_INCLUDE_CEF_ORIGIN_WHITELIST_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Add an entry to the cross-origin access whitelist.
+//
+// The same-origin policy restricts how scripts hosted from different origins
+// (scheme + domain + port) can communicate. By default, scripts can only access
+// resources with the same origin. Scripts hosted on the HTTP and HTTPS schemes
+// (but no other schemes) can use the "Access-Control-Allow-Origin" header to
+// allow cross-origin requests. For example, https://source.example.com can make
+// XMLHttpRequest requests on http://target.example.com if the
+// http://target.example.com request returns an "Access-Control-Allow-Origin:
+// https://source.example.com" response header.
+//
+// Scripts in separate frames or iframes and hosted from the same protocol and
+// domain suffix can execute cross-origin JavaScript if both pages set the
+// document.domain value to the same domain suffix. For example,
+// scheme://foo.example.com and scheme://bar.example.com can communicate using
+// JavaScript if both domains set document.domain="example.com".
+//
+// This method is used to allow access to origins that would otherwise violate
+// the same-origin policy. Scripts hosted underneath the fully qualified
+// |source_origin| URL (like http://www.example.com) will be allowed access to
+// all resources hosted on the specified |target_protocol| and |target_domain|.
+// If |target_domain| is non-empty and |allow_target_subdomains| if false only
+// exact domain matches will be allowed. If |target_domain| contains a top-
+// level domain component (like "example.com") and |allow_target_subdomains| is
+// true sub-domain matches will be allowed. If |target_domain| is empty and
+// |allow_target_subdomains| if true all domains and IP addresses will be
+// allowed.
+//
+// This method cannot be used to bypass the restrictions on local or display
+// isolated schemes. See the comments on CefRegisterCustomScheme for more
+// information.
+//
+// This function may be called on any thread. Returns false if |source_origin|
+// is invalid or the whitelist cannot be accessed.
+///
+/*--cef(optional_param=target_domain)--*/
+bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin,
+                                     const CefString& target_protocol,
+                                     const CefString& target_domain,
+                                     bool allow_target_subdomains);
+
+///
+// Remove an entry from the cross-origin access whitelist. Returns false if
+// |source_origin| is invalid or the whitelist cannot be accessed.
+///
+/*--cef(optional_param=target_domain)--*/
+bool CefRemoveCrossOriginWhitelistEntry(const CefString& source_origin,
+                                        const CefString& target_protocol,
+                                        const CefString& target_domain,
+                                        bool allow_target_subdomains);
+
+///
+// Remove all entries from the cross-origin access whitelist. Returns false if
+// the whitelist cannot be accessed.
+///
+/*--cef()--*/
+bool CefClearCrossOriginWhitelist();
+
+#endif  // CEF_INCLUDE_CEF_ORIGIN_WHITELIST_H_
diff --git a/src/include/cef_parser.h b/src/include/cef_parser.h
new file mode 100644
index 0000000..bae958b
--- /dev/null
+++ b/src/include/cef_parser.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_PARSER_H_
+#define CEF_INCLUDE_CEF_PARSER_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_base.h"
+#include "include/cef_values.h"
+
+///
+// Parse the specified |url| into its component parts.
+// Returns false if the URL is empty or invalid.
+///
+/*--cef()--*/
+bool CefParseURL(const CefString& url, CefURLParts& parts);
+
+///
+// Creates a URL from the specified |parts|, which must contain a non-empty
+// spec or a non-empty host and path (at a minimum), but not both.
+// Returns false if |parts| isn't initialized as described.
+///
+/*--cef()--*/
+bool CefCreateURL(const CefURLParts& parts, CefString& url);
+
+///
+// This is a convenience function for formatting a URL in a concise and human-
+// friendly way to help users make security-related decisions (or in other
+// circumstances when people need to distinguish sites, origins, or otherwise-
+// simplified URLs from each other). Internationalized domain names (IDN) may be
+// presented in Unicode if the conversion is considered safe. The returned value
+// will (a) omit the path for standard schemes, excepting file and filesystem,
+// and (b) omit the port if it is the default for the scheme. Do not use this
+// for URLs which will be parsed or sent to other applications.
+///
+/*--cef(optional_param=languages)--*/
+CefString CefFormatUrlForSecurityDisplay(const CefString& origin_url);
+
+///
+// Returns the mime type for the specified file extension or an empty string if
+// unknown.
+///
+/*--cef()--*/
+CefString CefGetMimeType(const CefString& extension);
+
+///
+// Get the extensions associated with the given mime type. This should be passed
+// in lower case. There could be multiple extensions for a given mime type, like
+// "html,htm" for "text/html", or "txt,text,html,..." for "text/*". Any existing
+// elements in the provided vector will not be erased.
+///
+/*--cef()--*/
+void CefGetExtensionsForMimeType(const CefString& mime_type,
+                                 std::vector<CefString>& extensions);
+
+///
+// Encodes |data| as a base64 string.
+///
+/*--cef()--*/
+CefString CefBase64Encode(const void* data, size_t data_size);
+
+///
+// Decodes the base64 encoded string |data|. The returned value will be NULL if
+// the decoding fails.
+///
+/*--cef()--*/
+CefRefPtr<CefBinaryValue> CefBase64Decode(const CefString& data);
+
+///
+// Escapes characters in |text| which are unsuitable for use as a query
+// parameter value. Everything except alphanumerics and -_.!~*'() will be
+// converted to "%XX". If |use_plus| is true spaces will change to "+". The
+// result is basically the same as encodeURIComponent in Javacript.
+///
+/*--cef()--*/
+CefString CefURIEncode(const CefString& text, bool use_plus);
+
+///
+// Unescapes |text| and returns the result. Unescaping consists of looking for
+// the exact pattern "%XX" where each X is a hex digit and converting to the
+// character with the numerical value of those digits (e.g. "i%20=%203%3b"
+// unescapes to "i = 3;"). If |convert_to_utf8| is true this function will
+// attempt to interpret the initial decoded result as UTF-8. If the result is
+// convertable into UTF-8 it will be returned as converted. Otherwise the
+// initial decoded result will be returned.  The |unescape_rule| parameter
+// supports further customization the decoding process.
+///
+/*--cef()--*/
+CefString CefURIDecode(const CefString& text,
+                       bool convert_to_utf8,
+                       cef_uri_unescape_rule_t unescape_rule);
+
+///
+// Parses the specified |json_string| and returns a dictionary or list
+// representation. If JSON parsing fails this method returns NULL.
+///
+/*--cef()--*/
+CefRefPtr<CefValue> CefParseJSON(const CefString& json_string,
+                                 cef_json_parser_options_t options);
+
+///
+// Parses the specified UTF8-encoded |json| buffer of size |json_size| and
+// returns a dictionary or list representation. If JSON parsing fails this
+// method returns NULL.
+///
+/*--cef(capi_name=cef_parse_json_buffer)--*/
+CefRefPtr<CefValue> CefParseJSON(const void* json,
+                                 size_t json_size,
+                                 cef_json_parser_options_t options);
+
+///
+// Parses the specified |json_string| and returns a dictionary or list
+// representation. If JSON parsing fails this method returns NULL and populates
+// |error_code_out| and |error_msg_out| with an error code and a formatted error
+// message respectively.
+///
+/*--cef()--*/
+CefRefPtr<CefValue> CefParseJSONAndReturnError(
+    const CefString& json_string,
+    cef_json_parser_options_t options,
+    cef_json_parser_error_t& error_code_out,
+    CefString& error_msg_out);
+
+///
+// Generates a JSON string from the specified root |node| which should be a
+// dictionary or list value. Returns an empty string on failure. This method
+// requires exclusive access to |node| including any underlying data.
+///
+/*--cef()--*/
+CefString CefWriteJSON(CefRefPtr<CefValue> node,
+                       cef_json_writer_options_t options);
+
+#endif  // CEF_INCLUDE_CEF_PARSER_H_
diff --git a/src/include/cef_path_util.h b/src/include/cef_path_util.h
new file mode 100644
index 0000000..552f4ba
--- /dev/null
+++ b/src/include/cef_path_util.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_PATH_UTIL_H_
+#define CEF_INCLUDE_CEF_PATH_UTIL_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+typedef cef_path_key_t PathKey;
+
+///
+// Retrieve the path associated with the specified |key|. Returns true on
+// success. Can be called on any thread in the browser process.
+///
+/*--cef()--*/
+bool CefGetPath(PathKey key, CefString& path);
+
+#endif  // CEF_INCLUDE_CEF_PATH_UTIL_H_
diff --git a/src/include/cef_print_handler.h b/src/include/cef_print_handler.h
new file mode 100644
index 0000000..c668bd4
--- /dev/null
+++ b/src/include/cef_print_handler.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_PRINT_HANDLER_H_
+#define CEF_INCLUDE_CEF_PRINT_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_print_settings.h"
+
+///
+// Callback interface for asynchronous continuation of print dialog requests.
+///
+/*--cef(source=library)--*/
+class CefPrintDialogCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Continue printing with the specified |settings|.
+  ///
+  /*--cef(capi_name=cont)--*/
+  virtual void Continue(CefRefPtr<CefPrintSettings> settings) = 0;
+
+  ///
+  // Cancel the printing.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+///
+// Callback interface for asynchronous continuation of print job requests.
+///
+/*--cef(source=library)--*/
+class CefPrintJobCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Indicate completion of the print job.
+  ///
+  /*--cef(capi_name=cont)--*/
+  virtual void Continue() = 0;
+};
+
+///
+// Implement this interface to handle printing on Linux. Each browser will have
+// only one print job in progress at a time. The methods of this class will be
+// called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefPrintHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called when printing has started for the specified |browser|. This method
+  // will be called before the other OnPrint*() methods and irrespective of how
+  // printing was initiated (e.g. CefBrowserHost::Print(), JavaScript
+  // window.print() or PDF extension print button).
+  ///
+  /*--cef()--*/
+  virtual void OnPrintStart(CefRefPtr<CefBrowser> browser) = 0;
+
+  ///
+  // Synchronize |settings| with client state. If |get_defaults| is true then
+  // populate |settings| with the default print settings. Do not keep a
+  // reference to |settings| outside of this callback.
+  ///
+  /*--cef()--*/
+  virtual void OnPrintSettings(CefRefPtr<CefBrowser> browser,
+                               CefRefPtr<CefPrintSettings> settings,
+                               bool get_defaults) = 0;
+
+  ///
+  // Show the print dialog. Execute |callback| once the dialog is dismissed.
+  // Return true if the dialog will be displayed or false to cancel the
+  // printing immediately.
+  ///
+  /*--cef()--*/
+  virtual bool OnPrintDialog(CefRefPtr<CefBrowser> browser,
+                             bool has_selection,
+                             CefRefPtr<CefPrintDialogCallback> callback) = 0;
+
+  ///
+  // Send the print job to the printer. Execute |callback| once the job is
+  // completed. Return true if the job will proceed or false to cancel the job
+  // immediately.
+  ///
+  /*--cef()--*/
+  virtual bool OnPrintJob(CefRefPtr<CefBrowser> browser,
+                          const CefString& document_name,
+                          const CefString& pdf_file_path,
+                          CefRefPtr<CefPrintJobCallback> callback) = 0;
+
+  ///
+  // Reset client state related to printing.
+  ///
+  /*--cef()--*/
+  virtual void OnPrintReset(CefRefPtr<CefBrowser> browser) = 0;
+
+  ///
+  // Return the PDF paper size in device units. Used in combination with
+  // CefBrowserHost::PrintToPDF().
+  ///
+  /*--cef()--*/
+  virtual CefSize GetPdfPaperSize(int device_units_per_inch) {
+    return CefSize();
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_PRINT_HANDLER_H_
diff --git a/src/include/cef_print_settings.h b/src/include/cef_print_settings.h
new file mode 100644
index 0000000..ba1c294
--- /dev/null
+++ b/src/include/cef_print_settings.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_PRINT_SETTINGS_H_
+#define CEF_INCLUDE_CEF_PRINT_SETTINGS_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_base.h"
+
+///
+// Class representing print settings.
+///
+/*--cef(source=library)--*/
+class CefPrintSettings : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_color_model_t ColorModel;
+  typedef cef_duplex_mode_t DuplexMode;
+  typedef std::vector<CefRange> PageRangeList;
+
+  ///
+  // Create a new CefPrintSettings object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefPrintSettings> Create();
+
+  ///
+  // Returns true if this object is valid. Do not call any other methods if this
+  // function returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Set the page orientation.
+  ///
+  /*--cef()--*/
+  virtual void SetOrientation(bool landscape) = 0;
+
+  ///
+  // Returns true if the orientation is landscape.
+  ///
+  /*--cef()--*/
+  virtual bool IsLandscape() = 0;
+
+  ///
+  // Set the printer printable area in device units.
+  // Some platforms already provide flipped area. Set |landscape_needs_flip|
+  // to false on those platforms to avoid double flipping.
+  ///
+  /*--cef()--*/
+  virtual void SetPrinterPrintableArea(
+      const CefSize& physical_size_device_units,
+      const CefRect& printable_area_device_units,
+      bool landscape_needs_flip) = 0;
+
+  ///
+  // Set the device name.
+  ///
+  /*--cef(optional_param=name)--*/
+  virtual void SetDeviceName(const CefString& name) = 0;
+
+  ///
+  // Get the device name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetDeviceName() = 0;
+
+  ///
+  // Set the DPI (dots per inch).
+  ///
+  /*--cef()--*/
+  virtual void SetDPI(int dpi) = 0;
+
+  ///
+  // Get the DPI (dots per inch).
+  ///
+  /*--cef()--*/
+  virtual int GetDPI() = 0;
+
+  ///
+  // Set the page ranges.
+  ///
+  /*--cef()--*/
+  virtual void SetPageRanges(const PageRangeList& ranges) = 0;
+
+  ///
+  // Returns the number of page ranges that currently exist.
+  ///
+  /*--cef()--*/
+  virtual size_t GetPageRangesCount() = 0;
+
+  ///
+  // Retrieve the page ranges.
+  ///
+  /*--cef(count_func=ranges:GetPageRangesCount)--*/
+  virtual void GetPageRanges(PageRangeList& ranges) = 0;
+
+  ///
+  // Set whether only the selection will be printed.
+  ///
+  /*--cef()--*/
+  virtual void SetSelectionOnly(bool selection_only) = 0;
+
+  ///
+  // Returns true if only the selection will be printed.
+  ///
+  /*--cef()--*/
+  virtual bool IsSelectionOnly() = 0;
+
+  ///
+  // Set whether pages will be collated.
+  ///
+  /*--cef()--*/
+  virtual void SetCollate(bool collate) = 0;
+
+  ///
+  // Returns true if pages will be collated.
+  ///
+  /*--cef()--*/
+  virtual bool WillCollate() = 0;
+
+  ///
+  // Set the color model.
+  ///
+  /*--cef()--*/
+  virtual void SetColorModel(ColorModel model) = 0;
+
+  ///
+  // Get the color model.
+  ///
+  /*--cef(default_retval=COLOR_MODEL_UNKNOWN)--*/
+  virtual ColorModel GetColorModel() = 0;
+
+  ///
+  // Set the number of copies.
+  ///
+  /*--cef()--*/
+  virtual void SetCopies(int copies) = 0;
+
+  ///
+  // Get the number of copies.
+  ///
+  /*--cef()--*/
+  virtual int GetCopies() = 0;
+
+  ///
+  // Set the duplex mode.
+  ///
+  /*--cef()--*/
+  virtual void SetDuplexMode(DuplexMode mode) = 0;
+
+  ///
+  // Get the duplex mode.
+  ///
+  /*--cef(default_retval=DUPLEX_MODE_UNKNOWN)--*/
+  virtual DuplexMode GetDuplexMode() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_PRINT_SETTINGS_H_
diff --git a/src/include/cef_process_message.h b/src/include/cef_process_message.h
new file mode 100644
index 0000000..c4daeb8
--- /dev/null
+++ b/src/include/cef_process_message.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_MESSAGE_H_
+#define CEF_INCLUDE_CEF_MESSAGE_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_values.h"
+
+typedef cef_process_id_t CefProcessId;
+
+///
+// Class representing a message. Can be used on any process and thread.
+///
+/*--cef(source=library)--*/
+class CefProcessMessage : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Create a new CefProcessMessage object with the specified name.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefProcessMessage> Create(const CefString& name);
+
+  ///
+  // Returns true if this object is valid. Do not call any other methods if this
+  // function returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns a writable copy of this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefProcessMessage> Copy() = 0;
+
+  ///
+  // Returns the message name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetName() = 0;
+
+  ///
+  // Returns the list of arguments.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefListValue> GetArgumentList() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_MESSAGE_H_
diff --git a/src/include/cef_process_util.h b/src/include/cef_process_util.h
new file mode 100644
index 0000000..4fce778
--- /dev/null
+++ b/src/include/cef_process_util.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_PROCESS_UTIL_H_
+#define CEF_INCLUDE_CEF_PROCESS_UTIL_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_command_line.h"
+
+///
+// Launches the process specified via |command_line|. Returns true upon
+// success. Must be called on the browser process TID_PROCESS_LAUNCHER thread.
+//
+// Unix-specific notes:
+// - All file descriptors open in the parent process will be closed in the
+//   child process except for stdin, stdout, and stderr.
+// - If the first argument on the command line does not contain a slash,
+//   PATH will be searched. (See man execvp.)
+///
+/*--cef()--*/
+bool CefLaunchProcess(CefRefPtr<CefCommandLine> command_line);
+
+#endif  // CEF_INCLUDE_CEF_PROCESS_UTIL_H_
diff --git a/src/include/cef_registration.h b/src/include/cef_registration.h
new file mode 100644
index 0000000..e8e6caa
--- /dev/null
+++ b/src/include/cef_registration.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_REGISTRATION_H_
+#define CEF_INCLUDE_CEF_REGISTRATION_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Generic callback interface used for managing the lifespan of a registration.
+///
+/*--cef(source=library)--*/
+class CefRegistration : public virtual CefBaseRefCounted {};
+
+#endif  // CEF_INCLUDE_CEF_REGISTRATION_H_
diff --git a/src/include/cef_render_handler.h b/src/include/cef_render_handler.h
new file mode 100644
index 0000000..c6c40f8
--- /dev/null
+++ b/src/include/cef_render_handler.h
@@ -0,0 +1,247 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RENDER_HANDLER_H_
+#define CEF_INCLUDE_CEF_RENDER_HANDLER_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_accessibility_handler.h"
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_drag_data.h"
+
+///
+// Implement this interface to handle events when window rendering is disabled.
+// The methods of this class will be called on the UI thread.
+///
+/*--cef(source=client)--*/
+class CefRenderHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_cursor_type_t CursorType;
+  typedef cef_drag_operations_mask_t DragOperation;
+  typedef cef_drag_operations_mask_t DragOperationsMask;
+  typedef cef_paint_element_type_t PaintElementType;
+  typedef std::vector<CefRect> RectList;
+  typedef cef_text_input_mode_t TextInputMode;
+
+  ///
+  // Return the handler for accessibility notifications. If no handler is
+  // provided the default implementation will be used.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() {
+    return nullptr;
+  }
+
+  ///
+  // Called to retrieve the root window rectangle in screen coordinates. Return
+  // true if the rectangle was provided. If this method returns false the
+  // rectangle from GetViewRect will be used.
+  ///
+  /*--cef()--*/
+  virtual bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) {
+    return false;
+  }
+
+  ///
+  // Called to retrieve the view rectangle which is relative to screen
+  // coordinates. This method must always provide a non-empty rectangle.
+  ///
+  /*--cef()--*/
+  virtual void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) = 0;
+
+  ///
+  // Called to retrieve the translation from view coordinates to actual screen
+  // coordinates. Return true if the screen coordinates were provided.
+  ///
+  /*--cef()--*/
+  virtual bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                              int viewX,
+                              int viewY,
+                              int& screenX,
+                              int& screenY) {
+    return false;
+  }
+
+  ///
+  // Called to allow the client to fill in the CefScreenInfo object with
+  // appropriate values. Return true if the |screen_info| structure has been
+  // modified.
+  //
+  // If the screen info rectangle is left empty the rectangle from GetViewRect
+  // will be used. If the rectangle is still empty or invalid popups may not be
+  // drawn correctly.
+  ///
+  /*--cef()--*/
+  virtual bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                             CefScreenInfo& screen_info) {
+    return false;
+  }
+
+  ///
+  // Called when the browser wants to show or hide the popup widget. The popup
+  // should be shown if |show| is true and hidden if |show| is false.
+  ///
+  /*--cef()--*/
+  virtual void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {}
+
+  ///
+  // Called when the browser wants to move or resize the popup widget. |rect|
+  // contains the new location and size in view coordinates.
+  ///
+  /*--cef()--*/
+  virtual void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) {
+  }
+
+  ///
+  // Called when an element should be painted. Pixel values passed to this
+  // method are scaled relative to view coordinates based on the value of
+  // CefScreenInfo.device_scale_factor returned from GetScreenInfo. |type|
+  // indicates whether the element is the view or the popup widget. |buffer|
+  // contains the pixel data for the whole image. |dirtyRects| contains the set
+  // of rectangles in pixel coordinates that need to be repainted. |buffer| will
+  // be |width|*|height|*4 bytes in size and represents a BGRA image with an
+  // upper-left origin. This method is only called when
+  // CefWindowInfo::shared_texture_enabled is set to false.
+  ///
+  /*--cef()--*/
+  virtual void OnPaint(CefRefPtr<CefBrowser> browser,
+                       PaintElementType type,
+                       const RectList& dirtyRects,
+                       const void* buffer,
+                       int width,
+                       int height) = 0;
+
+  ///
+  // Called when an element has been rendered to the shared texture handle.
+  // |type| indicates whether the element is the view or the popup widget.
+  // |dirtyRects| contains the set of rectangles in pixel coordinates that need
+  // to be repainted. |shared_handle| is the handle for a D3D11 Texture2D that
+  // can be accessed via ID3D11Device using the OpenSharedResource method. This
+  // method is only called when CefWindowInfo::shared_texture_enabled is set to
+  // true, and is currently only supported on Windows.
+  ///
+  /*--cef()--*/
+  virtual void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                                  PaintElementType type,
+                                  const RectList& dirtyRects,
+                                  void* shared_handle) {}
+
+  ///
+  // Called when the browser's cursor has changed. If |type| is CT_CUSTOM then
+  // |custom_cursor_info| will be populated with the custom cursor information.
+  ///
+  /*--cef()--*/
+  virtual void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                              CefCursorHandle cursor,
+                              CursorType type,
+                              const CefCursorInfo& custom_cursor_info) {}
+
+  ///
+  // Called when the user starts dragging content in the web view. Contextual
+  // information about the dragged content is supplied by |drag_data|.
+  // (|x|, |y|) is the drag start location in screen coordinates.
+  // OS APIs that run a system message loop may be used within the
+  // StartDragging call.
+  //
+  // Return false to abort the drag operation. Don't call any of
+  // CefBrowserHost::DragSource*Ended* methods after returning false.
+  //
+  // Return true to handle the drag operation. Call
+  // CefBrowserHost::DragSourceEndedAt and DragSourceSystemDragEnded either
+  // synchronously or asynchronously to inform the web view that the drag
+  // operation has ended.
+  ///
+  /*--cef()--*/
+  virtual bool StartDragging(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefDragData> drag_data,
+                             DragOperationsMask allowed_ops,
+                             int x,
+                             int y) {
+    return false;
+  }
+
+  ///
+  // Called when the web view wants to update the mouse cursor during a
+  // drag & drop operation. |operation| describes the allowed operation
+  // (none, move, copy, link).
+  ///
+  /*--cef()--*/
+  virtual void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                                DragOperation operation) {}
+
+  ///
+  // Called when the scroll offset has changed.
+  ///
+  /*--cef()--*/
+  virtual void OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser,
+                                     double x,
+                                     double y) {}
+
+  ///
+  // Called when the IME composition range has changed. |selected_range| is the
+  // range of characters that have been selected. |character_bounds| is the
+  // bounds of each character in view coordinates.
+  ///
+  /*--cef()--*/
+  virtual void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
+                                            const CefRange& selected_range,
+                                            const RectList& character_bounds) {}
+
+  ///
+  // Called when text selection has changed for the specified |browser|.
+  // |selected_text| is the currently selected text and |selected_range| is
+  // the character range.
+  ///
+  /*--cef(optional_param=selected_text,optional_param=selected_range)--*/
+  virtual void OnTextSelectionChanged(CefRefPtr<CefBrowser> browser,
+                                      const CefString& selected_text,
+                                      const CefRange& selected_range) {}
+
+  ///
+  // Called when an on-screen keyboard should be shown or hidden for the
+  // specified |browser|. |input_mode| specifies what kind of keyboard
+  // should be opened. If |input_mode| is CEF_TEXT_INPUT_MODE_NONE, any
+  // existing keyboard for this browser should be hidden.
+  ///
+  /*--cef()--*/
+  virtual void OnVirtualKeyboardRequested(CefRefPtr<CefBrowser> browser,
+                                          TextInputMode input_mode) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_RENDER_HANDLER_H_
diff --git a/src/include/cef_render_process_handler.h b/src/include/cef_render_process_handler.h
new file mode 100644
index 0000000..8ba4ec4
--- /dev/null
+++ b/src/include/cef_render_process_handler.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RENDER_PROCESS_HANDLER_H_
+#define CEF_INCLUDE_CEF_RENDER_PROCESS_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_dom.h"
+#include "include/cef_frame.h"
+#include "include/cef_load_handler.h"
+#include "include/cef_process_message.h"
+#include "include/cef_v8.h"
+#include "include/cef_values.h"
+
+///
+// Class used to implement render process callbacks. The methods of this class
+// will be called on the render process main thread (TID_RENDERER) unless
+// otherwise indicated.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefRenderProcessHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_navigation_type_t NavigationType;
+
+  ///
+  // Called after the render process main thread has been created. |extra_info|
+  // is a read-only value originating from
+  // CefBrowserProcessHandler::OnRenderProcessThreadCreated(). Do not keep a
+  // reference to |extra_info| outside of this method.
+  ///
+  /*--cef()--*/
+  virtual void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) {}
+
+  ///
+  // Called after WebKit has been initialized.
+  ///
+  /*--cef()--*/
+  virtual void OnWebKitInitialized() {}
+
+  ///
+  // Called after a browser has been created. When browsing cross-origin a new
+  // browser will be created before the old browser with the same identifier is
+  // destroyed. |extra_info| is a read-only value originating from
+  // CefBrowserHost::CreateBrowser(), CefBrowserHost::CreateBrowserSync(),
+  // CefLifeSpanHandler::OnBeforePopup() or CefBrowserView::CreateBrowserView().
+  ///
+  /*--cef()--*/
+  virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefDictionaryValue> extra_info) {}
+
+  ///
+  // Called before a browser is destroyed.
+  ///
+  /*--cef()--*/
+  virtual void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) {}
+
+  ///
+  // Return the handler for browser load status events.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefLoadHandler> GetLoadHandler() { return nullptr; }
+
+  ///
+  // Called immediately after the V8 context for a frame has been created. To
+  // retrieve the JavaScript 'window' object use the CefV8Context::GetGlobal()
+  // method. V8 handles can only be accessed from the thread on which they are
+  // created. A task runner for posting tasks on the associated thread can be
+  // retrieved via the CefV8Context::GetTaskRunner() method.
+  ///
+  /*--cef()--*/
+  virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefRefPtr<CefV8Context> context) {}
+
+  ///
+  // Called immediately before the V8 context for a frame is released. No
+  // references to the context should be kept after this method is called.
+  ///
+  /*--cef()--*/
+  virtual void OnContextReleased(CefRefPtr<CefBrowser> browser,
+                                 CefRefPtr<CefFrame> frame,
+                                 CefRefPtr<CefV8Context> context) {}
+
+  ///
+  // Called for global uncaught exceptions in a frame. Execution of this
+  // callback is disabled by default. To enable set
+  // CefSettings.uncaught_exception_stack_size > 0.
+  ///
+  /*--cef()--*/
+  virtual void OnUncaughtException(CefRefPtr<CefBrowser> browser,
+                                   CefRefPtr<CefFrame> frame,
+                                   CefRefPtr<CefV8Context> context,
+                                   CefRefPtr<CefV8Exception> exception,
+                                   CefRefPtr<CefV8StackTrace> stackTrace) {}
+
+  ///
+  // Called when a new node in the the browser gets focus. The |node| value may
+  // be empty if no specific node has gained focus. The node object passed to
+  // this method represents a snapshot of the DOM at the time this method is
+  // executed. DOM objects are only valid for the scope of this method. Do not
+  // keep references to or attempt to access any DOM objects outside the scope
+  // of this method.
+  ///
+  /*--cef(optional_param=frame,optional_param=node)--*/
+  virtual void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
+                                    CefRefPtr<CefFrame> frame,
+                                    CefRefPtr<CefDOMNode> node) {}
+
+  ///
+  // Called when a new message is received from a different process. Return true
+  // if the message was handled or false otherwise. Do not keep a reference to
+  // or attempt to access the message outside of this callback.
+  ///
+  /*--cef()--*/
+  virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                        CefRefPtr<CefFrame> frame,
+                                        CefProcessId source_process,
+                                        CefRefPtr<CefProcessMessage> message) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_RENDER_PROCESS_HANDLER_H_
diff --git a/src/include/cef_request.h b/src/include/cef_request.h
new file mode 100644
index 0000000..cbe2d03
--- /dev/null
+++ b/src/include/cef_request.h
@@ -0,0 +1,354 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_REQUEST_H_
+#define CEF_INCLUDE_CEF_REQUEST_H_
+#pragma once
+
+#include <map>
+#include <vector>
+#include "include/cef_base.h"
+
+class CefPostData;
+class CefPostDataElement;
+
+///
+// Class used to represent a web request. The methods of this class may be
+// called on any thread.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefRequest : public virtual CefBaseRefCounted {
+ public:
+  typedef std::multimap<CefString, CefString> HeaderMap;
+  typedef cef_referrer_policy_t ReferrerPolicy;
+  typedef cef_resource_type_t ResourceType;
+  typedef cef_transition_type_t TransitionType;
+
+  ///
+  // Create a new CefRequest object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefRequest> Create();
+
+  ///
+  // Returns true if this object is read-only.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Get the fully qualified URL.
+  ///
+  /*--cef()--*/
+  virtual CefString GetURL() = 0;
+
+  ///
+  // Set the fully qualified URL.
+  ///
+  /*--cef()--*/
+  virtual void SetURL(const CefString& url) = 0;
+
+  ///
+  // Get the request method type. The value will default to POST if post data
+  // is provided and GET otherwise.
+  ///
+  /*--cef()--*/
+  virtual CefString GetMethod() = 0;
+
+  ///
+  // Set the request method type.
+  ///
+  /*--cef()--*/
+  virtual void SetMethod(const CefString& method) = 0;
+
+  ///
+  // Set the referrer URL and policy. If non-empty the referrer URL must be
+  // fully qualified with an HTTP or HTTPS scheme component. Any username,
+  // password or ref component will be removed.
+  ///
+  /*--cef(optional_param=referrer_url)--*/
+  virtual void SetReferrer(const CefString& referrer_url,
+                           ReferrerPolicy policy) = 0;
+
+  ///
+  // Get the referrer URL.
+  ///
+  /*--cef()--*/
+  virtual CefString GetReferrerURL() = 0;
+
+  ///
+  // Get the referrer policy.
+  ///
+  /*--cef(default_retval=REFERRER_POLICY_DEFAULT)--*/
+  virtual ReferrerPolicy GetReferrerPolicy() = 0;
+
+  ///
+  // Get the post data.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefPostData> GetPostData() = 0;
+
+  ///
+  // Set the post data.
+  ///
+  /*--cef()--*/
+  virtual void SetPostData(CefRefPtr<CefPostData> postData) = 0;
+
+  ///
+  // Get the header values. Will not include the Referer value if any.
+  ///
+  /*--cef()--*/
+  virtual void GetHeaderMap(HeaderMap& headerMap) = 0;
+
+  ///
+  // Set the header values. If a Referer value exists in the header map it will
+  // be removed and ignored.
+  ///
+  /*--cef()--*/
+  virtual void SetHeaderMap(const HeaderMap& headerMap) = 0;
+
+  ///
+  // Returns the first header value for |name| or an empty string if not found.
+  // Will not return the Referer value if any. Use GetHeaderMap instead if
+  // |name| might have multiple values.
+  ///
+  /*--cef()--*/
+  virtual CefString GetHeaderByName(const CefString& name) = 0;
+
+  ///
+  // Set the header |name| to |value|. If |overwrite| is true any existing
+  // values will be replaced with the new value. If |overwrite| is false any
+  // existing values will not be overwritten. The Referer value cannot be set
+  // using this method.
+  ///
+  /*--cef(optional_param=value)--*/
+  virtual void SetHeaderByName(const CefString& name,
+                               const CefString& value,
+                               bool overwrite) = 0;
+
+  ///
+  // Set all values at one time.
+  ///
+  /*--cef(optional_param=postData)--*/
+  virtual void Set(const CefString& url,
+                   const CefString& method,
+                   CefRefPtr<CefPostData> postData,
+                   const HeaderMap& headerMap) = 0;
+
+  ///
+  // Get the flags used in combination with CefURLRequest. See
+  // cef_urlrequest_flags_t for supported values.
+  ///
+  /*--cef(default_retval=UR_FLAG_NONE)--*/
+  virtual int GetFlags() = 0;
+
+  ///
+  // Set the flags used in combination with CefURLRequest.  See
+  // cef_urlrequest_flags_t for supported values.
+  ///
+  /*--cef()--*/
+  virtual void SetFlags(int flags) = 0;
+
+  ///
+  // Get the URL to the first party for cookies used in combination with
+  // CefURLRequest.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFirstPartyForCookies() = 0;
+
+  ///
+  // Set the URL to the first party for cookies used in combination with
+  // CefURLRequest.
+  ///
+  /*--cef(optional_param=url)--*/
+  virtual void SetFirstPartyForCookies(const CefString& url) = 0;
+
+  ///
+  // Get the resource type for this request. Only available in the browser
+  // process.
+  ///
+  /*--cef(default_retval=RT_SUB_RESOURCE)--*/
+  virtual ResourceType GetResourceType() = 0;
+
+  ///
+  // Get the transition type for this request. Only available in the browser
+  // process and only applies to requests that represent a main frame or
+  // sub-frame navigation.
+  ///
+  /*--cef(default_retval=TT_EXPLICIT)--*/
+  virtual TransitionType GetTransitionType() = 0;
+
+  ///
+  // Returns the globally unique identifier for this request or 0 if not
+  // specified. Can be used by CefResourceRequestHandler implementations in the
+  // browser process to track a single request across multiple callbacks.
+  ///
+  /*--cef()--*/
+  virtual uint64 GetIdentifier() = 0;
+};
+
+///
+// Class used to represent post data for a web request. The methods of this
+// class may be called on any thread.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefPostData : public virtual CefBaseRefCounted {
+ public:
+  typedef std::vector<CefRefPtr<CefPostDataElement>> ElementVector;
+
+  ///
+  // Create a new CefPostData object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefPostData> Create();
+
+  ///
+  // Returns true if this object is read-only.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns true if the underlying POST data includes elements that are not
+  // represented by this CefPostData object (for example, multi-part file upload
+  // data). Modifying CefPostData objects with excluded elements may result in
+  // the request failing.
+  ///
+  /*--cef()--*/
+  virtual bool HasExcludedElements() = 0;
+
+  ///
+  // Returns the number of existing post data elements.
+  ///
+  /*--cef()--*/
+  virtual size_t GetElementCount() = 0;
+
+  ///
+  // Retrieve the post data elements.
+  ///
+  /*--cef(count_func=elements:GetElementCount)--*/
+  virtual void GetElements(ElementVector& elements) = 0;
+
+  ///
+  // Remove the specified post data element.  Returns true if the removal
+  // succeeds.
+  ///
+  /*--cef()--*/
+  virtual bool RemoveElement(CefRefPtr<CefPostDataElement> element) = 0;
+
+  ///
+  // Add the specified post data element.  Returns true if the add succeeds.
+  ///
+  /*--cef()--*/
+  virtual bool AddElement(CefRefPtr<CefPostDataElement> element) = 0;
+
+  ///
+  // Remove all existing post data elements.
+  ///
+  /*--cef()--*/
+  virtual void RemoveElements() = 0;
+};
+
+///
+// Class used to represent a single element in the request post data. The
+// methods of this class may be called on any thread.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefPostDataElement : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Post data elements may represent either bytes or files.
+  ///
+  typedef cef_postdataelement_type_t Type;
+
+  ///
+  // Create a new CefPostDataElement object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefPostDataElement> Create();
+
+  ///
+  // Returns true if this object is read-only.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Remove all contents from the post data element.
+  ///
+  /*--cef()--*/
+  virtual void SetToEmpty() = 0;
+
+  ///
+  // The post data element will represent a file.
+  ///
+  /*--cef()--*/
+  virtual void SetToFile(const CefString& fileName) = 0;
+
+  ///
+  // The post data element will represent bytes.  The bytes passed
+  // in will be copied.
+  ///
+  /*--cef()--*/
+  virtual void SetToBytes(size_t size, const void* bytes) = 0;
+
+  ///
+  // Return the type of this post data element.
+  ///
+  /*--cef(default_retval=PDE_TYPE_EMPTY)--*/
+  virtual Type GetType() = 0;
+
+  ///
+  // Return the file name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFile() = 0;
+
+  ///
+  // Return the number of bytes.
+  ///
+  /*--cef()--*/
+  virtual size_t GetBytesCount() = 0;
+
+  ///
+  // Read up to |size| bytes into |bytes| and return the number of bytes
+  // actually read.
+  ///
+  /*--cef()--*/
+  virtual size_t GetBytes(size_t size, void* bytes) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_REQUEST_H_
diff --git a/src/include/cef_request_callback.h b/src/include/cef_request_callback.h
new file mode 100644
index 0000000..68094de
--- /dev/null
+++ b/src/include/cef_request_callback.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_REQUEST_CALLBACK_H_
+#define CEF_INCLUDE_CEF_REQUEST_CALLBACK_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Callback interface used for asynchronous continuation of url requests.
+///
+/*--cef(source=library)--*/
+class CefRequestCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Continue the url request. If |allow| is true the request will be continued.
+  // Otherwise, the request will be canceled.
+  ///
+  /*--cef(capi_name=cont)--*/
+  virtual void Continue(bool allow) = 0;
+
+  ///
+  // Cancel the url request.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_REQUEST_CALLBACK_H_
diff --git a/src/include/cef_request_context.h b/src/include/cef_request_context.h
new file mode 100644
index 0000000..edb65da
--- /dev/null
+++ b/src/include/cef_request_context.h
@@ -0,0 +1,373 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_REQUEST_CONTEXT_H_
+#define CEF_INCLUDE_CEF_REQUEST_CONTEXT_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_callback.h"
+#include "include/cef_cookie.h"
+#include "include/cef_extension.h"
+#include "include/cef_extension_handler.h"
+#include "include/cef_media_router.h"
+#include "include/cef_values.h"
+
+class CefRequestContextHandler;
+class CefSchemeHandlerFactory;
+
+///
+// Callback interface for CefRequestContext::ResolveHost.
+///
+/*--cef(source=client)--*/
+class CefResolveCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called on the UI thread after the ResolveHost request has completed.
+  // |result| will be the result code. |resolved_ips| will be the list of
+  // resolved IP addresses or empty if the resolution failed.
+  ///
+  /*--cef(optional_param=resolved_ips)--*/
+  virtual void OnResolveCompleted(
+      cef_errorcode_t result,
+      const std::vector<CefString>& resolved_ips) = 0;
+};
+
+///
+// A request context provides request handling for a set of related browser
+// or URL request objects. A request context can be specified when creating a
+// new browser via the CefBrowserHost static factory methods or when creating a
+// new URL request via the CefURLRequest static factory methods. Browser objects
+// with different request contexts will never be hosted in the same render
+// process. Browser objects with the same request context may or may not be
+// hosted in the same render process depending on the process model. Browser
+// objects created indirectly via the JavaScript window.open function or
+// targeted links will share the same render process and the same request
+// context as the source browser. When running in single-process mode there is
+// only a single render process (the main process) and so all browsers created
+// in single-process mode will share the same request context. This will be the
+// first request context passed into a CefBrowserHost static factory method and
+// all other request context objects will be ignored.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefRequestContext : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the global context object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefRequestContext> GetGlobalContext();
+
+  ///
+  // Creates a new context object with the specified |settings| and optional
+  // |handler|.
+  ///
+  /*--cef(optional_param=handler)--*/
+  static CefRefPtr<CefRequestContext> CreateContext(
+      const CefRequestContextSettings& settings,
+      CefRefPtr<CefRequestContextHandler> handler);
+
+  ///
+  // Creates a new context object that shares storage with |other| and uses an
+  // optional |handler|.
+  ///
+  /*--cef(capi_name=cef_create_context_shared,optional_param=handler)--*/
+  static CefRefPtr<CefRequestContext> CreateContext(
+      CefRefPtr<CefRequestContext> other,
+      CefRefPtr<CefRequestContextHandler> handler);
+
+  ///
+  // Returns true if this object is pointing to the same context as |that|
+  // object.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefRequestContext> other) = 0;
+
+  ///
+  // Returns true if this object is sharing the same storage as |that| object.
+  ///
+  /*--cef()--*/
+  virtual bool IsSharingWith(CefRefPtr<CefRequestContext> other) = 0;
+
+  ///
+  // Returns true if this object is the global context. The global context is
+  // used by default when creating a browser or URL request with a NULL context
+  // argument.
+  ///
+  /*--cef()--*/
+  virtual bool IsGlobal() = 0;
+
+  ///
+  // Returns the handler for this context if any.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRequestContextHandler> GetHandler() = 0;
+
+  ///
+  // Returns the cache path for this object. If empty an "incognito mode"
+  // in-memory cache is being used.
+  ///
+  /*--cef()--*/
+  virtual CefString GetCachePath() = 0;
+
+  ///
+  // Returns the cookie manager for this object. If |callback| is non-NULL it
+  // will be executed asnychronously on the IO thread after the manager's
+  // storage has been initialized.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual CefRefPtr<CefCookieManager> GetCookieManager(
+      CefRefPtr<CefCompletionCallback> callback) = 0;
+
+  ///
+  // Register a scheme handler factory for the specified |scheme_name| and
+  // optional |domain_name|. An empty |domain_name| value for a standard scheme
+  // will cause the factory to match all domain names. The |domain_name| value
+  // will be ignored for non-standard schemes. If |scheme_name| is a built-in
+  // scheme and no handler is returned by |factory| then the built-in scheme
+  // handler factory will be called. If |scheme_name| is a custom scheme then
+  // you must also implement the CefApp::OnRegisterCustomSchemes() method in all
+  // processes. This function may be called multiple times to change or remove
+  // the factory that matches the specified |scheme_name| and optional
+  // |domain_name|. Returns false if an error occurs. This function may be
+  // called on any thread in the browser process.
+  ///
+  /*--cef(optional_param=domain_name,optional_param=factory)--*/
+  virtual bool RegisterSchemeHandlerFactory(
+      const CefString& scheme_name,
+      const CefString& domain_name,
+      CefRefPtr<CefSchemeHandlerFactory> factory) = 0;
+
+  ///
+  // Clear all registered scheme handler factories. Returns false on error. This
+  // function may be called on any thread in the browser process.
+  ///
+  /*--cef()--*/
+  virtual bool ClearSchemeHandlerFactories() = 0;
+
+  ///
+  // Tells all renderer processes associated with this context to throw away
+  // their plugin list cache. If |reload_pages| is true they will also reload
+  // all pages with plugins. CefRequestContextHandler::OnBeforePluginLoad may
+  // be called to rebuild the plugin list cache.
+  ///
+  /*--cef()--*/
+  virtual void PurgePluginListCache(bool reload_pages) = 0;
+
+  ///
+  // Returns true if a preference with the specified |name| exists. This method
+  // must be called on the browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool HasPreference(const CefString& name) = 0;
+
+  ///
+  // Returns the value for the preference with the specified |name|. Returns
+  // NULL if the preference does not exist. The returned object contains a copy
+  // of the underlying preference value and modifications to the returned object
+  // will not modify the underlying preference value. This method must be called
+  // on the browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefValue> GetPreference(const CefString& name) = 0;
+
+  ///
+  // Returns all preferences as a dictionary. If |include_defaults| is true then
+  // preferences currently at their default value will be included. The returned
+  // object contains a copy of the underlying preference values and
+  // modifications to the returned object will not modify the underlying
+  // preference values. This method must be called on the browser process UI
+  // thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDictionaryValue> GetAllPreferences(
+      bool include_defaults) = 0;
+
+  ///
+  // Returns true if the preference with the specified |name| can be modified
+  // using SetPreference. As one example preferences set via the command-line
+  // usually cannot be modified. This method must be called on the browser
+  // process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool CanSetPreference(const CefString& name) = 0;
+
+  ///
+  // Set the |value| associated with preference |name|. Returns true if the
+  // value is set successfully and false otherwise. If |value| is NULL the
+  // preference will be restored to its default value. If setting the preference
+  // fails then |error| will be populated with a detailed description of the
+  // problem. This method must be called on the browser process UI thread.
+  ///
+  /*--cef(optional_param=value)--*/
+  virtual bool SetPreference(const CefString& name,
+                             CefRefPtr<CefValue> value,
+                             CefString& error) = 0;
+
+  ///
+  // Clears all certificate exceptions that were added as part of handling
+  // CefRequestHandler::OnCertificateError(). If you call this it is
+  // recommended that you also call CloseAllConnections() or you risk not
+  // being prompted again for server certificates if you reconnect quickly.
+  // If |callback| is non-NULL it will be executed on the UI thread after
+  // completion.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual void ClearCertificateExceptions(
+      CefRefPtr<CefCompletionCallback> callback) = 0;
+
+  ///
+  // Clears all HTTP authentication credentials that were added as part of
+  // handling GetAuthCredentials. If |callback| is non-NULL it will be executed
+  // on the UI thread after completion.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual void ClearHttpAuthCredentials(
+      CefRefPtr<CefCompletionCallback> callback) = 0;
+
+  ///
+  // Clears all active and idle connections that Chromium currently has.
+  // This is only recommended if you have released all other CEF objects but
+  // don't yet want to call CefShutdown(). If |callback| is non-NULL it will be
+  // executed on the UI thread after completion.
+  ///
+  /*--cef(optional_param=callback)--*/
+  virtual void CloseAllConnections(
+      CefRefPtr<CefCompletionCallback> callback) = 0;
+
+  ///
+  // Attempts to resolve |origin| to a list of associated IP addresses.
+  // |callback| will be executed on the UI thread after completion.
+  ///
+  /*--cef()--*/
+  virtual void ResolveHost(const CefString& origin,
+                           CefRefPtr<CefResolveCallback> callback) = 0;
+
+  ///
+  // Load an extension.
+  //
+  // If extension resources will be read from disk using the default load
+  // implementation then |root_directory| should be the absolute path to the
+  // extension resources directory and |manifest| should be NULL. If extension
+  // resources will be provided by the client (e.g. via CefRequestHandler and/or
+  // CefExtensionHandler) then |root_directory| should be a path component
+  // unique to the extension (if not absolute this will be internally prefixed
+  // with the PK_DIR_RESOURCES path) and |manifest| should contain the contents
+  // that would otherwise be read from the "manifest.json" file on disk.
+  //
+  // The loaded extension will be accessible in all contexts sharing the same
+  // storage (HasExtension returns true). However, only the context on which
+  // this method was called is considered the loader (DidLoadExtension returns
+  // true) and only the loader will receive CefRequestContextHandler callbacks
+  // for the extension.
+  //
+  // CefExtensionHandler::OnExtensionLoaded will be called on load success or
+  // CefExtensionHandler::OnExtensionLoadFailed will be called on load failure.
+  //
+  // If the extension specifies a background script via the "background"
+  // manifest key then CefExtensionHandler::OnBeforeBackgroundBrowser will be
+  // called to create the background browser. See that method for additional
+  // information about background scripts.
+  //
+  // For visible extension views the client application should evaluate the
+  // manifest to determine the correct extension URL to load and then pass that
+  // URL to the CefBrowserHost::CreateBrowser* function after the extension has
+  // loaded. For example, the client can look for the "browser_action" manifest
+  // key as documented at https://developer.chrome.com/extensions/browserAction.
+  // Extension URLs take the form "chrome-extension://<extension_id>/<path>".
+  //
+  // Browsers that host extensions differ from normal browsers as follows:
+  //  - Can access chrome.* JavaScript APIs if allowed by the manifest. Visit
+  //    chrome://extensions-support for the list of extension APIs currently
+  //    supported by CEF.
+  //  - Main frame navigation to non-extension content is blocked.
+  //  - Pinch-zooming is disabled.
+  //  - CefBrowserHost::GetExtension returns the hosted extension.
+  //  - CefBrowserHost::IsBackgroundHost returns true for background hosts.
+  //
+  // See https://developer.chrome.com/extensions for extension implementation
+  // and usage documentation.
+  ///
+  /*--cef(optional_param=manifest,optional_param=handler)--*/
+  virtual void LoadExtension(const CefString& root_directory,
+                             CefRefPtr<CefDictionaryValue> manifest,
+                             CefRefPtr<CefExtensionHandler> handler) = 0;
+
+  ///
+  // Returns true if this context was used to load the extension identified by
+  // |extension_id|. Other contexts sharing the same storage will also have
+  // access to the extension (see HasExtension). This method must be called on
+  // the browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool DidLoadExtension(const CefString& extension_id) = 0;
+
+  ///
+  // Returns true if this context has access to the extension identified by
+  // |extension_id|. This may not be the context that was used to load the
+  // extension (see DidLoadExtension). This method must be called on the browser
+  // process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool HasExtension(const CefString& extension_id) = 0;
+
+  ///
+  // Retrieve the list of all extensions that this context has access to (see
+  // HasExtension). |extension_ids| will be populated with the list of extension
+  // ID values. Returns true on success. This method must be called on the
+  // browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual bool GetExtensions(std::vector<CefString>& extension_ids) = 0;
+
+  ///
+  // Returns the extension matching |extension_id| or NULL if no matching
+  // extension is accessible in this context (see HasExtension). This method
+  // must be called on the browser process UI thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefExtension> GetExtension(
+      const CefString& extension_id) = 0;
+
+  ///
+  // Returns the MediaRouter object associated with this context.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMediaRouter> GetMediaRouter() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_REQUEST_CONTEXT_H_
diff --git a/src/include/cef_request_context_handler.h b/src/include/cef_request_context_handler.h
new file mode 100644
index 0000000..13cf164
--- /dev/null
+++ b/src/include/cef_request_context_handler.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_REQUEST_CONTEXT_HANDLER_H_
+#define CEF_INCLUDE_CEF_REQUEST_CONTEXT_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_request.h"
+#include "include/cef_resource_request_handler.h"
+#include "include/cef_web_plugin.h"
+
+///
+// Implement this interface to provide handler implementations. The handler
+// instance will not be released until all objects related to the context have
+// been destroyed.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefRequestContextHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_plugin_policy_t PluginPolicy;
+
+  ///
+  // Called on the browser process UI thread immediately after the request
+  // context has been initialized.
+  ///
+  /*--cef()--*/
+  virtual void OnRequestContextInitialized(
+      CefRefPtr<CefRequestContext> request_context) {}
+
+  ///
+  // Called on multiple browser process threads before a plugin instance is
+  // loaded. |mime_type| is the mime type of the plugin that will be loaded.
+  // |plugin_url| is the content URL that the plugin will load and may be empty.
+  // |is_main_frame| will be true if the plugin is being loaded in the main
+  // (top-level) frame, |top_origin_url| is the URL for the top-level frame that
+  // contains the plugin when loading a specific plugin instance or empty when
+  // building the initial list of enabled plugins for 'navigator.plugins'
+  // JavaScript state. |plugin_info| includes additional information about the
+  // plugin that will be loaded. |plugin_policy| is the recommended policy.
+  // Modify |plugin_policy| and return true to change the policy. Return false
+  // to use the recommended policy. The default plugin policy can be set at
+  // runtime using the `--plugin-policy=[allow|detect|block]` command-line flag.
+  // Decisions to mark a plugin as disabled by setting |plugin_policy| to
+  // PLUGIN_POLICY_DISABLED may be cached when |top_origin_url| is empty. To
+  // purge the plugin list cache and potentially trigger new calls to this
+  // method call CefRequestContext::PurgePluginListCache.
+  ///
+  /*--cef(optional_param=plugin_url,optional_param=top_origin_url)--*/
+  virtual bool OnBeforePluginLoad(const CefString& mime_type,
+                                  const CefString& plugin_url,
+                                  bool is_main_frame,
+                                  const CefString& top_origin_url,
+                                  CefRefPtr<CefWebPluginInfo> plugin_info,
+                                  PluginPolicy* plugin_policy) {
+    return false;
+  }
+
+  ///
+  // Called on the browser process IO thread before a resource request is
+  // initiated. The |browser| and |frame| values represent the source of the
+  // request, and may be NULL for requests originating from service workers or
+  // CefURLRequest. |request| represents the request contents and cannot be
+  // modified in this callback. |is_navigation| will be true if the resource
+  // request is a navigation. |is_download| will be true if the resource request
+  // is a download. |request_initiator| is the origin (scheme + domain) of the
+  // page that initiated the request. Set |disable_default_handling| to true to
+  // disable default handling of the request, in which case it will need to be
+  // handled via CefResourceRequestHandler::GetResourceHandler or it will be
+  // canceled. To allow the resource load to proceed with default handling
+  // return NULL. To specify a handler for the resource return a
+  // CefResourceRequestHandler object. This method will not be called if the
+  // client associated with |browser| returns a non-NULL value from
+  // CefRequestHandler::GetResourceRequestHandler for the same request
+  // (identified by CefRequest::GetIdentifier).
+  ///
+  /*--cef(optional_param=browser,optional_param=frame,
+          optional_param=request_initiator)--*/
+  virtual CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) {
+    return nullptr;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_REQUEST_CONTEXT_HANDLER_H_
diff --git a/src/include/cef_request_handler.h b/src/include/cef_request_handler.h
new file mode 100644
index 0000000..c255057
--- /dev/null
+++ b/src/include/cef_request_handler.h
@@ -0,0 +1,264 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_REQUEST_HANDLER_H_
+#define CEF_INCLUDE_CEF_REQUEST_HANDLER_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_auth_callback.h"
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_request.h"
+#include "include/cef_request_callback.h"
+#include "include/cef_resource_request_handler.h"
+#include "include/cef_ssl_info.h"
+#include "include/cef_x509_certificate.h"
+
+///
+// Callback interface used to select a client certificate for authentication.
+///
+/*--cef(source=library)--*/
+class CefSelectClientCertificateCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Chooses the specified certificate for client certificate authentication.
+  // NULL value means that no client certificate should be used.
+  ///
+  /*--cef(optional_param=cert)--*/
+  virtual void Select(CefRefPtr<CefX509Certificate> cert) = 0;
+};
+
+///
+// Implement this interface to handle events related to browser requests. The
+// methods of this class will be called on the thread indicated.
+///
+/*--cef(source=client)--*/
+class CefRequestHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_termination_status_t TerminationStatus;
+  typedef cef_window_open_disposition_t WindowOpenDisposition;
+  typedef std::vector<CefRefPtr<CefX509Certificate>> X509CertificateList;
+
+  ///
+  // Called on the UI thread before browser navigation. Return true to cancel
+  // the navigation or false to allow the navigation to proceed. The |request|
+  // object cannot be modified in this callback.
+  // CefLoadHandler::OnLoadingStateChange will be called twice in all cases.
+  // If the navigation is allowed CefLoadHandler::OnLoadStart and
+  // CefLoadHandler::OnLoadEnd will be called. If the navigation is canceled
+  // CefLoadHandler::OnLoadError will be called with an |errorCode| value of
+  // ERR_ABORTED. The |user_gesture| value will be true if the browser
+  // navigated via explicit user gesture (e.g. clicking a link) or false if it
+  // navigated automatically (e.g. via the DomContentLoaded event).
+  ///
+  /*--cef()--*/
+  virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefRequest> request,
+                              bool user_gesture,
+                              bool is_redirect) {
+    return false;
+  }
+
+  ///
+  // Called on the UI thread before OnBeforeBrowse in certain limited cases
+  // where navigating a new or different browser might be desirable. This
+  // includes user-initiated navigation that might open in a special way (e.g.
+  // links clicked via middle-click or ctrl + left-click) and certain types of
+  // cross-origin navigation initiated from the renderer process (e.g.
+  // navigating the top-level frame to/from a file URL). The |browser| and
+  // |frame| values represent the source of the navigation. The
+  // |target_disposition| value indicates where the user intended to navigate
+  // the browser based on standard Chromium behaviors (e.g. current tab,
+  // new tab, etc). The |user_gesture| value will be true if the browser
+  // navigated via explicit user gesture (e.g. clicking a link) or false if it
+  // navigated automatically (e.g. via the DomContentLoaded event). Return true
+  // to cancel the navigation or false to allow the navigation to proceed in the
+  // source browser's top-level frame.
+  ///
+  /*--cef()--*/
+  virtual bool OnOpenURLFromTab(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                const CefString& target_url,
+                                WindowOpenDisposition target_disposition,
+                                bool user_gesture) {
+    return false;
+  }
+
+  ///
+  // Called on the browser process IO thread before a resource request is
+  // initiated. The |browser| and |frame| values represent the source of the
+  // request. |request| represents the request contents and cannot be modified
+  // in this callback. |is_navigation| will be true if the resource request is a
+  // navigation. |is_download| will be true if the resource request is a
+  // download. |request_initiator| is the origin (scheme + domain) of the page
+  // that initiated the request. Set |disable_default_handling| to true to
+  // disable default handling of the request, in which case it will need to be
+  // handled via CefResourceRequestHandler::GetResourceHandler or it will be
+  // canceled. To allow the resource load to proceed with default handling
+  // return NULL. To specify a handler for the resource return a
+  // CefResourceRequestHandler object. If this callback returns NULL the same
+  // method will be called on the associated CefRequestContextHandler, if any.
+  ///
+  /*--cef(optional_param=request_initiator)--*/
+  virtual CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) {
+    return nullptr;
+  }
+
+  ///
+  // Called on the IO thread when the browser needs credentials from the user.
+  // |origin_url| is the origin making this authentication request. |isProxy|
+  // indicates whether the host is a proxy server. |host| contains the hostname
+  // and |port| contains the port number. |realm| is the realm of the challenge
+  // and may be empty. |scheme| is the authentication scheme used, such as
+  // "basic" or "digest", and will be empty if the source of the request is an
+  // FTP server. Return true to continue the request and call
+  // CefAuthCallback::Continue() either in this method or at a later time when
+  // the authentication information is available. Return false to cancel the
+  // request immediately.
+  ///
+  /*--cef(optional_param=realm,optional_param=scheme)--*/
+  virtual bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
+                                  const CefString& origin_url,
+                                  bool isProxy,
+                                  const CefString& host,
+                                  int port,
+                                  const CefString& realm,
+                                  const CefString& scheme,
+                                  CefRefPtr<CefAuthCallback> callback) {
+    return false;
+  }
+
+  ///
+  // Called on the IO thread when JavaScript requests a specific storage quota
+  // size via the webkitStorageInfo.requestQuota function. |origin_url| is the
+  // origin of the page making the request. |new_size| is the requested quota
+  // size in bytes. Return true to continue the request and call
+  // CefRequestCallback::Continue() either in this method or at a later time to
+  // grant or deny the request. Return false to cancel the request immediately.
+  ///
+  /*--cef()--*/
+  virtual bool OnQuotaRequest(CefRefPtr<CefBrowser> browser,
+                              const CefString& origin_url,
+                              int64 new_size,
+                              CefRefPtr<CefRequestCallback> callback) {
+    return false;
+  }
+
+  ///
+  // Called on the UI thread to handle requests for URLs with an invalid
+  // SSL certificate. Return true and call CefRequestCallback::Continue() either
+  // in this method or at a later time to continue or cancel the request. Return
+  // false to cancel the request immediately. If
+  // CefSettings.ignore_certificate_errors is set all invalid certificates will
+  // be accepted without calling this method.
+  ///
+  /*--cef()--*/
+  virtual bool OnCertificateError(CefRefPtr<CefBrowser> browser,
+                                  cef_errorcode_t cert_error,
+                                  const CefString& request_url,
+                                  CefRefPtr<CefSSLInfo> ssl_info,
+                                  CefRefPtr<CefRequestCallback> callback) {
+    return false;
+  }
+
+  ///
+  // Called on the UI thread when a client certificate is being requested for
+  // authentication. Return false to use the default behavior and automatically
+  // select the first certificate available. Return true and call
+  // CefSelectClientCertificateCallback::Select either in this method or at a
+  // later time to select a certificate. Do not call Select or call it with NULL
+  // to continue without using any certificate. |isProxy| indicates whether the
+  // host is an HTTPS proxy or the origin server. |host| and |port| contains the
+  // hostname and port of the SSL server. |certificates| is the list of
+  // certificates to choose from; this list has already been pruned by Chromium
+  // so that it only contains certificates from issuers that the server trusts.
+  ///
+  /*--cef()--*/
+  virtual bool OnSelectClientCertificate(
+      CefRefPtr<CefBrowser> browser,
+      bool isProxy,
+      const CefString& host,
+      int port,
+      const X509CertificateList& certificates,
+      CefRefPtr<CefSelectClientCertificateCallback> callback) {
+    return false;
+  }
+
+  ///
+  // Called on the browser process UI thread when a plugin has crashed.
+  // |plugin_path| is the path of the plugin that crashed.
+  ///
+  /*--cef()--*/
+  virtual void OnPluginCrashed(CefRefPtr<CefBrowser> browser,
+                               const CefString& plugin_path) {}
+
+  ///
+  // Called on the browser process UI thread when the render view associated
+  // with |browser| is ready to receive/handle IPC messages in the render
+  // process.
+  ///
+  /*--cef()--*/
+  virtual void OnRenderViewReady(CefRefPtr<CefBrowser> browser) {}
+
+  ///
+  // Called on the browser process UI thread when the render process
+  // terminates unexpectedly. |status| indicates how the process
+  // terminated.
+  ///
+  /*--cef()--*/
+  virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                         TerminationStatus status) {}
+
+  ///
+  // Called on the browser process UI thread when the window.document object of
+  // the main frame has been created.
+  ///
+  /*--cef()--*/
+  virtual void OnDocumentAvailableInMainFrame(CefRefPtr<CefBrowser> browser) {}
+};
+
+#endif  // CEF_INCLUDE_CEF_REQUEST_HANDLER_H_
diff --git a/src/include/cef_resource_bundle.h b/src/include/cef_resource_bundle.h
new file mode 100644
index 0000000..c08f5b0
--- /dev/null
+++ b/src/include/cef_resource_bundle.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2015 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RESOURCE_BUNDLE_H_
+#define CEF_INCLUDE_CEF_RESOURCE_BUNDLE_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Class used for retrieving resources from the resource bundle (*.pak) files
+// loaded by CEF during startup or via the CefResourceBundleHandler returned
+// from CefApp::GetResourceBundleHandler. See CefSettings for additional options
+// related to resource bundle loading. The methods of this class may be called
+// on any thread unless otherwise indicated.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefResourceBundle : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_scale_factor_t ScaleFactor;
+
+  ///
+  // Returns the global resource bundle instance.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefResourceBundle> GetGlobal();
+
+  ///
+  // Returns the localized string for the specified |string_id| or an empty
+  // string if the value is not found. Include cef_pack_strings.h for a listing
+  // of valid string ID values.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLocalizedString(int string_id) = 0;
+
+  ///
+  // Retrieves the contents of the specified scale independent |resource_id|.
+  // If the value is found then |data| and |data_size| will be populated and
+  // this method will return true. If the value is not found then this method
+  // will return false. The returned |data| pointer will remain resident in
+  // memory and should not be freed. Include cef_pack_resources.h for a listing
+  // of valid resource ID values.
+  ///
+  /*--cef()--*/
+  virtual bool GetDataResource(int resource_id,
+                               void*& data,
+                               size_t& data_size) = 0;
+
+  ///
+  // Retrieves the contents of the specified |resource_id| nearest the scale
+  // factor |scale_factor|. Use a |scale_factor| value of SCALE_FACTOR_NONE for
+  // scale independent resources or call GetDataResource instead. If the value
+  // is found then |data| and |data_size| will be populated and this method will
+  // return true. If the value is not found then this method will return false.
+  // The returned |data| pointer will remain resident in memory and should not
+  // be freed. Include cef_pack_resources.h for a listing of valid resource ID
+  // values.
+  ///
+  /*--cef()--*/
+  virtual bool GetDataResourceForScale(int resource_id,
+                                       ScaleFactor scale_factor,
+                                       void*& data,
+                                       size_t& data_size) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_RESOURCE_BUNDLE_H_
diff --git a/src/include/cef_resource_bundle_handler.h b/src/include/cef_resource_bundle_handler.h
new file mode 100644
index 0000000..6cd6c9a
--- /dev/null
+++ b/src/include/cef_resource_bundle_handler.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RESOURCE_BUNDLE_HANDLER_H_
+#define CEF_INCLUDE_CEF_RESOURCE_BUNDLE_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Class used to implement a custom resource bundle interface. See CefSettings
+// for additional options related to resource bundle loading. The methods of
+// this class may be called on multiple threads.
+///
+/*--cef(source=client)--*/
+class CefResourceBundleHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_scale_factor_t ScaleFactor;
+
+  ///
+  // Called to retrieve a localized translation for the specified |string_id|.
+  // To provide the translation set |string| to the translation string and
+  // return true. To use the default translation return false. Include
+  // cef_pack_strings.h for a listing of valid string ID values.
+  ///
+  /*--cef()--*/
+  virtual bool GetLocalizedString(int string_id, CefString& string) = 0;
+
+  ///
+  // Called to retrieve data for the specified scale independent |resource_id|.
+  // To provide the resource data set |data| and |data_size| to the data pointer
+  // and size respectively and return true. To use the default resource data
+  // return false. The resource data will not be copied and must remain resident
+  // in memory. Include cef_pack_resources.h for a listing of valid resource ID
+  // values.
+  ///
+  /*--cef()--*/
+  virtual bool GetDataResource(int resource_id,
+                               void*& data,
+                               size_t& data_size) = 0;
+
+  ///
+  // Called to retrieve data for the specified |resource_id| nearest the scale
+  // factor |scale_factor|. To provide the resource data set |data| and
+  // |data_size| to the data pointer and size respectively and return true. To
+  // use the default resource data return false. The resource data will not be
+  // copied and must remain resident in memory. Include cef_pack_resources.h for
+  // a listing of valid resource ID values.
+  ///
+  /*--cef()--*/
+  virtual bool GetDataResourceForScale(int resource_id,
+                                       ScaleFactor scale_factor,
+                                       void*& data,
+                                       size_t& data_size) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_RESOURCE_BUNDLE_HANDLER_H_
diff --git a/src/include/cef_resource_handler.h b/src/include/cef_resource_handler.h
new file mode 100644
index 0000000..4d9535e
--- /dev/null
+++ b/src/include/cef_resource_handler.h
@@ -0,0 +1,206 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RESOURCE_HANDLER_H_
+#define CEF_INCLUDE_CEF_RESOURCE_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_callback.h"
+#include "include/cef_cookie.h"
+#include "include/cef_request.h"
+#include "include/cef_response.h"
+
+///
+// Callback for asynchronous continuation of CefResourceHandler::Skip().
+///
+/*--cef(source=library)--*/
+class CefResourceSkipCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Callback for asynchronous continuation of Skip(). If |bytes_skipped| > 0
+  // then either Skip() will be called again until the requested number of
+  // bytes have been skipped or the request will proceed. If |bytes_skipped|
+  // <= 0 the request will fail with ERR_REQUEST_RANGE_NOT_SATISFIABLE.
+  ///
+  /*--cef(capi_name=cont)--*/
+  virtual void Continue(int64 bytes_skipped) = 0;
+};
+
+///
+// Callback for asynchronous continuation of CefResourceHandler::Read().
+///
+/*--cef(source=library)--*/
+class CefResourceReadCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Callback for asynchronous continuation of Read(). If |bytes_read| == 0
+  // the response will be considered complete. If |bytes_read| > 0 then Read()
+  // will be called again until the request is complete (based on either the
+  // result or the expected content length). If |bytes_read| < 0 then the
+  // request will fail and the |bytes_read| value will be treated as the error
+  // code.
+  ///
+  /*--cef(capi_name=cont)--*/
+  virtual void Continue(int bytes_read) = 0;
+};
+
+///
+// Class used to implement a custom request handler interface. The methods of
+// this class will be called on the IO thread unless otherwise indicated.
+///
+/*--cef(source=client)--*/
+class CefResourceHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Open the response stream. To handle the request immediately set
+  // |handle_request| to true and return true. To decide at a later time set
+  // |handle_request| to false, return true, and execute |callback| to continue
+  // or cancel the request. To cancel the request immediately set
+  // |handle_request| to true and return false. This method will be called in
+  // sequence but not from a dedicated thread. For backwards compatibility set
+  // |handle_request| to false and return false and the ProcessRequest method
+  // will be called.
+  ///
+  /*--cef()--*/
+  virtual bool Open(CefRefPtr<CefRequest> request,
+                    bool& handle_request,
+                    CefRefPtr<CefCallback> callback) {
+    handle_request = false;
+    return false;
+  }
+
+  ///
+  // Begin processing the request. To handle the request return true and call
+  // CefCallback::Continue() once the response header information is available
+  // (CefCallback::Continue() can also be called from inside this method if
+  // header information is available immediately). To cancel the request return
+  // false.
+  //
+  // WARNING: This method is deprecated. Use Open instead.
+  ///
+  /*--cef()--*/
+  virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
+                              CefRefPtr<CefCallback> callback) {
+    return false;
+  }
+
+  ///
+  // Retrieve response header information. If the response length is not known
+  // set |response_length| to -1 and ReadResponse() will be called until it
+  // returns false. If the response length is known set |response_length|
+  // to a positive value and ReadResponse() will be called until it returns
+  // false or the specified number of bytes have been read. Use the |response|
+  // object to set the mime type, http status code and other optional header
+  // values. To redirect the request to a new URL set |redirectUrl| to the new
+  // URL. |redirectUrl| can be either a relative or fully qualified URL.
+  // It is also possible to set |response| to a redirect http status code
+  // and pass the new URL via a Location header. Likewise with |redirectUrl| it
+  // is valid to set a relative or fully qualified URL as the Location header
+  // value. If an error occured while setting up the request you can call
+  // SetError() on |response| to indicate the error condition.
+  ///
+  /*--cef()--*/
+  virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                                  int64& response_length,
+                                  CefString& redirectUrl) = 0;
+
+  ///
+  // Skip response data when requested by a Range header. Skip over and discard
+  // |bytes_to_skip| bytes of response data. If data is available immediately
+  // set |bytes_skipped| to the number of bytes skipped and return true. To
+  // read the data at a later time set |bytes_skipped| to 0, return true and
+  // execute |callback| when the data is available. To indicate failure set
+  // |bytes_skipped| to < 0 (e.g. -2 for ERR_FAILED) and return false. This
+  // method will be called in sequence but not from a dedicated thread.
+  ///
+  /*--cef()--*/
+  virtual bool Skip(int64 bytes_to_skip,
+                    int64& bytes_skipped,
+                    CefRefPtr<CefResourceSkipCallback> callback) {
+    bytes_skipped = -2;
+    return false;
+  }
+
+  ///
+  // Read response data. If data is available immediately copy up to
+  // |bytes_to_read| bytes into |data_out|, set |bytes_read| to the number of
+  // bytes copied, and return true. To read the data at a later time keep a
+  // pointer to |data_out|, set |bytes_read| to 0, return true and execute
+  // |callback| when the data is available (|data_out| will remain valid until
+  // the callback is executed). To indicate response completion set |bytes_read|
+  // to 0 and return false. To indicate failure set |bytes_read| to < 0 (e.g. -2
+  // for ERR_FAILED) and return false. This method will be called in sequence
+  // but not from a dedicated thread. For backwards compatibility set
+  // |bytes_read| to -1 and return false and the ReadResponse method will be
+  // called.
+  ///
+  /*--cef()--*/
+  virtual bool Read(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefResourceReadCallback> callback) {
+    bytes_read = -1;
+    return false;
+  }
+
+  ///
+  // Read response data. If data is available immediately copy up to
+  // |bytes_to_read| bytes into |data_out|, set |bytes_read| to the number of
+  // bytes copied, and return true. To read the data at a later time set
+  // |bytes_read| to 0, return true and call CefCallback::Continue() when the
+  // data is available. To indicate response completion return false.
+  //
+  // WARNING: This method is deprecated. Use Skip and Read instead.
+  ///
+  /*--cef()--*/
+  virtual bool ReadResponse(void* data_out,
+                            int bytes_to_read,
+                            int& bytes_read,
+                            CefRefPtr<CefCallback> callback) {
+    bytes_read = -2;
+    return false;
+  }
+
+  ///
+  // Request processing has been canceled.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_RESOURCE_HANDLER_H_
diff --git a/src/include/cef_resource_request_handler.h b/src/include/cef_resource_request_handler.h
new file mode 100644
index 0000000..d13afb3
--- /dev/null
+++ b/src/include/cef_resource_request_handler.h
@@ -0,0 +1,249 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RESOURCE_REQUEST_HANDLER_H_
+#define CEF_INCLUDE_CEF_RESOURCE_REQUEST_HANDLER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_request.h"
+#include "include/cef_request_callback.h"
+#include "include/cef_resource_handler.h"
+#include "include/cef_response.h"
+#include "include/cef_response_filter.h"
+
+class CefCookieAccessFilter;
+
+///
+// Implement this interface to handle events related to browser requests. The
+// methods of this class will be called on the IO thread unless otherwise
+// indicated.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefResourceRequestHandler : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_return_value_t ReturnValue;
+  typedef cef_urlrequest_status_t URLRequestStatus;
+
+  ///
+  // Called on the IO thread before a resource request is loaded. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or CefURLRequest. To optionally
+  // filter cookies for the request return a CefCookieAccessFilter object. The
+  // |request| object cannot not be modified in this callback.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) {
+    return nullptr;
+  }
+
+  ///
+  // Called on the IO thread before a resource request is loaded. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or CefURLRequest. To redirect or
+  // change the resource load optionally modify |request|. Modification of the
+  // request URL will be treated as a redirect. Return RV_CONTINUE to continue
+  // the request immediately. Return RV_CONTINUE_ASYNC and call
+  // CefRequestCallback:: Continue() at a later time to continue or cancel the
+  // request asynchronously. Return RV_CANCEL to cancel the request immediately.
+  //
+  ///
+  /*--cef(optional_param=browser,optional_param=frame,
+          default_retval=RV_CONTINUE)--*/
+  virtual ReturnValue OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) {
+    return RV_CONTINUE;
+  }
+
+  ///
+  // Called on the IO thread before a resource is loaded. The |browser| and
+  // |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or CefURLRequest. To allow the
+  // resource to load using the default network loader return NULL. To specify a
+  // handler for the resource return a CefResourceHandler object. The |request|
+  // object cannot not be modified in this callback.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) {
+    return nullptr;
+  }
+
+  ///
+  // Called on the IO thread when a resource load is redirected. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or CefURLRequest. The |request|
+  // parameter will contain the old URL and other request-related information.
+  // The |response| parameter will contain the response that resulted in the
+  // redirect. The |new_url| parameter will contain the new URL and can be
+  // changed if desired. The |request| and |response| objects cannot be modified
+  // in this callback.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                                  CefRefPtr<CefFrame> frame,
+                                  CefRefPtr<CefRequest> request,
+                                  CefRefPtr<CefResponse> response,
+                                  CefString& new_url) {}
+
+  ///
+  // Called on the IO thread when a resource response is received. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or CefURLRequest. To allow the
+  // resource load to proceed without modification return false. To redirect or
+  // retry the resource load optionally modify |request| and return true.
+  // Modification of the request URL will be treated as a redirect. Requests
+  // handled using the default network loader cannot be redirected in this
+  // callback. The |response| object cannot be modified in this callback.
+  //
+  // WARNING: Redirecting using this method is deprecated. Use
+  // OnBeforeResourceLoad or GetResourceHandler to perform redirects.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                                  CefRefPtr<CefFrame> frame,
+                                  CefRefPtr<CefRequest> request,
+                                  CefRefPtr<CefResponse> response) {
+    return false;
+  }
+
+  ///
+  // Called on the IO thread to optionally filter resource response content. The
+  // |browser| and |frame| values represent the source of the request, and may
+  // be NULL for requests originating from service workers or CefURLRequest.
+  // |request| and |response| represent the request and response respectively
+  // and cannot be modified in this callback.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefResponse> response) {
+    return nullptr;
+  }
+
+  ///
+  // Called on the IO thread when a resource load has completed. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or CefURLRequest. |request| and
+  // |response| represent the request and response respectively and cannot be
+  // modified in this callback. |status| indicates the load completion status.
+  // |received_content_length| is the number of response bytes actually read.
+  // This method will be called for all requests, including requests that are
+  // aborted due to CEF shutdown or destruction of the associated browser. In
+  // cases where the associated browser is destroyed this callback may arrive
+  // after the CefLifeSpanHandler::OnBeforeClose callback for that browser. The
+  // CefFrame::IsValid method can be used to test for this situation, and care
+  // should be taken not to call |browser| or |frame| methods that modify state
+  // (like LoadURL, SendProcessMessage, etc.) if the frame is invalid.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                                      CefRefPtr<CefFrame> frame,
+                                      CefRefPtr<CefRequest> request,
+                                      CefRefPtr<CefResponse> response,
+                                      URLRequestStatus status,
+                                      int64 received_content_length) {}
+
+  ///
+  // Called on the IO thread to handle requests for URLs with an unknown
+  // protocol component. The |browser| and |frame| values represent the source
+  // of the request, and may be NULL for requests originating from service
+  // workers or CefURLRequest. |request| cannot be modified in this callback.
+  // Set |allow_os_execution| to true to attempt execution via the registered OS
+  // protocol handler, if any.
+  // SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS BASED
+  // ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
+                                   CefRefPtr<CefFrame> frame,
+                                   CefRefPtr<CefRequest> request,
+                                   bool& allow_os_execution) {}
+};
+
+///
+// Implement this interface to filter cookies that may be sent or received from
+// resource requests. The methods of this class will be called on the IO thread
+// unless otherwise indicated.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefCookieAccessFilter : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called on the IO thread before a resource request is sent. The |browser|
+  // and |frame| values represent the source of the request, and may be NULL for
+  // requests originating from service workers or CefURLRequest. |request|
+  // cannot be modified in this callback. Return true if the specified cookie
+  // can be sent with the request or false otherwise.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual bool CanSendCookie(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefFrame> frame,
+                             CefRefPtr<CefRequest> request,
+                             const CefCookie& cookie) {
+    return true;
+  }
+
+  ///
+  // Called on the IO thread after a resource response is received. The
+  // |browser| and |frame| values represent the source of the request, and may
+  // be NULL for requests originating from service workers or CefURLRequest.
+  // |request| cannot be modified in this callback. Return true if the specified
+  // cookie returned with the response can be saved or false otherwise.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual bool CanSaveCookie(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefFrame> frame,
+                             CefRefPtr<CefRequest> request,
+                             CefRefPtr<CefResponse> response,
+                             const CefCookie& cookie) {
+    return true;
+  }
+};
+
+#endif  // CEF_INCLUDE_CEF_RESOURCE_REQUEST_HANDLER_H_
diff --git a/src/include/cef_response.h b/src/include/cef_response.h
new file mode 100644
index 0000000..f871c11
--- /dev/null
+++ b/src/include/cef_response.h
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RESPONSE_H_
+#define CEF_INCLUDE_CEF_RESPONSE_H_
+#pragma once
+
+#include <map>
+#include "include/cef_base.h"
+
+///
+// Class used to represent a web response. The methods of this class may be
+// called on any thread.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefResponse : public virtual CefBaseRefCounted {
+ public:
+  typedef std::multimap<CefString, CefString> HeaderMap;
+
+  ///
+  // Create a new CefResponse object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefResponse> Create();
+
+  ///
+  // Returns true if this object is read-only.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Get the response error code. Returns ERR_NONE if there was no error.
+  ///
+  /*--cef(default_retval=ERR_NONE)--*/
+  virtual cef_errorcode_t GetError() = 0;
+
+  ///
+  // Set the response error code. This can be used by custom scheme handlers
+  // to return errors during initial request processing.
+  ///
+  /*--cef()--*/
+  virtual void SetError(cef_errorcode_t error) = 0;
+
+  ///
+  // Get the response status code.
+  ///
+  /*--cef()--*/
+  virtual int GetStatus() = 0;
+
+  ///
+  // Set the response status code.
+  ///
+  /*--cef()--*/
+  virtual void SetStatus(int status) = 0;
+
+  ///
+  // Get the response status text.
+  ///
+  /*--cef()--*/
+  virtual CefString GetStatusText() = 0;
+
+  ///
+  // Set the response status text.
+  ///
+  /*--cef(optional_param=statusText)--*/
+  virtual void SetStatusText(const CefString& statusText) = 0;
+
+  ///
+  // Get the response mime type.
+  ///
+  /*--cef()--*/
+  virtual CefString GetMimeType() = 0;
+
+  ///
+  // Set the response mime type.
+  ///
+  /*--cef(optional_param=mimeType)--*/
+  virtual void SetMimeType(const CefString& mimeType) = 0;
+
+  ///
+  // Get the response charset.
+  ///
+  /*--cef()--*/
+  virtual CefString GetCharset() = 0;
+
+  ///
+  // Set the response charset.
+  ///
+  /*--cef(optional_param=charset)--*/
+  virtual void SetCharset(const CefString& charset) = 0;
+
+  ///
+  // Get the value for the specified response header field.
+  ///
+  /*--cef()--*/
+  virtual CefString GetHeaderByName(const CefString& name) = 0;
+
+  ///
+  // Set the header |name| to |value|. If |overwrite| is true any existing
+  // values will be replaced with the new value. If |overwrite| is false any
+  // existing values will not be overwritten.
+  ///
+  /*--cef(optional_param=value)--*/
+  virtual void SetHeaderByName(const CefString& name,
+                               const CefString& value,
+                               bool overwrite) = 0;
+
+  ///
+  // Get all response header fields.
+  ///
+  /*--cef()--*/
+  virtual void GetHeaderMap(HeaderMap& headerMap) = 0;
+
+  ///
+  // Set all response header fields.
+  ///
+  /*--cef()--*/
+  virtual void SetHeaderMap(const HeaderMap& headerMap) = 0;
+
+  ///
+  // Get the resolved URL after redirects or changed as a result of HSTS.
+  ///
+  /*--cef()--*/
+  virtual CefString GetURL() = 0;
+
+  ///
+  // Set the resolved URL after redirects or changed as a result of HSTS.
+  ///
+  /*--cef(optional_param=url)--*/
+  virtual void SetURL(const CefString& url) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_RESPONSE_H_
diff --git a/src/include/cef_response_filter.h b/src/include/cef_response_filter.h
new file mode 100644
index 0000000..2eb446f
--- /dev/null
+++ b/src/include/cef_response_filter.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2015 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_RESPONSE_FILTER_H_
+#define CEF_INCLUDE_CEF_RESPONSE_FILTER_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// Implement this interface to filter resource response content. The methods of
+// this class will be called on the browser process IO thread.
+///
+/*--cef(source=client)--*/
+class CefResponseFilter : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_response_filter_status_t FilterStatus;
+
+  ///
+  // Initialize the response filter. Will only be called a single time. The
+  // filter will not be installed if this method returns false.
+  ///
+  /*--cef()--*/
+  virtual bool InitFilter() = 0;
+
+  ///
+  // Called to filter a chunk of data. Expected usage is as follows:
+  //
+  //  A. Read input data from |data_in| and set |data_in_read| to the number of
+  //     bytes that were read up to a maximum of |data_in_size|. |data_in| will
+  //     be NULL if |data_in_size| is zero.
+  //  B. Write filtered output data to |data_out| and set |data_out_written| to
+  //     the number of bytes that were written up to a maximum of
+  //     |data_out_size|. If no output data was written then all data must be
+  //     read from |data_in| (user must set |data_in_read| = |data_in_size|).
+  //  C. Return RESPONSE_FILTER_DONE if all output data was written or
+  //     RESPONSE_FILTER_NEED_MORE_DATA if output data is still pending.
+  //
+  // This method will be called repeatedly until the input buffer has been
+  // fully read (user sets |data_in_read| = |data_in_size|) and there is no
+  // more input data to filter (the resource response is complete). This method
+  // may then be called an additional time with an empty input buffer if the
+  // user filled the output buffer (set |data_out_written| = |data_out_size|)
+  // and returned RESPONSE_FILTER_NEED_MORE_DATA to indicate that output data is
+  // still pending.
+  //
+  // Calls to this method will stop when one of the following conditions is met:
+  //
+  //  A. There is no more input data to filter (the resource response is
+  //     complete) and the user sets |data_out_written| = 0 or returns
+  //     RESPONSE_FILTER_DONE to indicate that all data has been written, or;
+  //  B. The user returns RESPONSE_FILTER_ERROR to indicate an error.
+  //
+  // Do not keep a reference to the buffers passed to this method.
+  ///
+  /*--cef(optional_param=data_in,default_retval=RESPONSE_FILTER_ERROR)--*/
+  virtual FilterStatus Filter(void* data_in,
+                              size_t data_in_size,
+                              size_t& data_in_read,
+                              void* data_out,
+                              size_t data_out_size,
+                              size_t& data_out_written) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_RESPONSE_FILTER_H_
diff --git a/src/include/cef_sandbox_mac.h b/src/include/cef_sandbox_mac.h
new file mode 100644
index 0000000..48886ed
--- /dev/null
+++ b/src/include/cef_sandbox_mac.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2018 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_CEF_SANDBOX_MAC_H_
+#define CEF_INCLUDE_CEF_SANDBOX_MAC_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+#include "include/internal/cef_export.h"
+
+#if defined(OS_MACOSX)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The sandbox is used to restrict sub-processes (renderer, plugin, GPU, etc)
+// from directly accessing system resources. This helps to protect the user
+// from untrusted and potentially malicious Web content.
+// See http://www.chromium.org/developers/design-documents/sandbox for
+// complete details.
+//
+// To enable the sandbox on macOS the following requirements must be met:
+// 1. Link the helper process executable with the cef_sandbox static library.
+// 2. Call the cef_sandbox_initialize() function at the beginning of the
+//    helper executable main() function and before loading the CEF framework
+//    library. See include/wrapper/cef_library_loader.h for example usage.
+
+///
+// Initialize the sandbox for this process. Returns the sandbox context
+// handle on success or NULL on failure. The returned handle should be
+// passed to cef_sandbox_destroy() immediately before process termination.
+///
+CEF_EXPORT void* cef_sandbox_initialize(int argc, char** argv);
+
+///
+// Destroy the specified sandbox context handle.
+///
+CEF_EXPORT void cef_sandbox_destroy(void* sandbox_context);
+
+#ifdef __cplusplus
+}
+
+///
+// Scoped helper for managing the life span of a sandbox context handle.
+///
+class CEF_EXPORT CefScopedSandboxContext {
+ public:
+  CefScopedSandboxContext();
+  ~CefScopedSandboxContext();
+
+  // Load the sandbox for this process. Returns true on success.
+  bool Initialize(int argc, char** argv);
+
+ private:
+  void* sandbox_context_;
+};
+#endif  // __cplusplus
+
+#endif  // defined(OS_MACOSX)
+
+#endif  // CEF_INCLUDE_CEF_SANDBOX_MAC_H_
diff --git a/src/include/cef_sandbox_win.h b/src/include/cef_sandbox_win.h
new file mode 100644
index 0000000..3b7c4e0
--- /dev/null
+++ b/src/include/cef_sandbox_win.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_CEF_SANDBOX_WIN_H_
+#define CEF_INCLUDE_CEF_SANDBOX_WIN_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+
+#if defined(OS_WIN)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The sandbox is used to restrict sub-processes (renderer, plugin, GPU, etc)
+// from directly accessing system resources. This helps to protect the user
+// from untrusted and potentially malicious Web content.
+// See http://www.chromium.org/developers/design-documents/sandbox for
+// complete details.
+//
+// To enable the sandbox on Windows the following requirements must be met:
+// 1. Use the same executable for the browser process and all sub-processes.
+// 2. Link the executable with the cef_sandbox static library.
+// 3. Call the cef_sandbox_info_create() function from within the executable
+//    (not from a separate DLL) and pass the resulting pointer into both the
+//    CefExecutProcess() and CefInitialize() functions via the
+//    |windows_sandbox_info| parameter.
+
+///
+// Create the sandbox information object for this process. It is safe to create
+// multiple of this object and to destroy the object immediately after passing
+// into the CefExecutProcess() and/or CefInitialize() functions.
+///
+void* cef_sandbox_info_create();
+
+///
+// Destroy the specified sandbox information object.
+///
+void cef_sandbox_info_destroy(void* sandbox_info);
+
+#ifdef __cplusplus
+}
+
+///
+// Manages the life span of a sandbox information object.
+///
+class CefScopedSandboxInfo {
+ public:
+  CefScopedSandboxInfo() { sandbox_info_ = cef_sandbox_info_create(); }
+  ~CefScopedSandboxInfo() { cef_sandbox_info_destroy(sandbox_info_); }
+
+  void* sandbox_info() const { return sandbox_info_; }
+
+ private:
+  void* sandbox_info_;
+};
+#endif  // __cplusplus
+
+#endif  // defined(OS_WIN)
+
+#endif  // CEF_INCLUDE_CEF_SANDBOX_WIN_H_
diff --git a/src/include/cef_scheme.h b/src/include/cef_scheme.h
new file mode 100644
index 0000000..082f9ec
--- /dev/null
+++ b/src/include/cef_scheme.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_SCHEME_H_
+#define CEF_INCLUDE_CEF_SCHEME_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_request.h"
+#include "include/cef_resource_handler.h"
+#include "include/cef_response.h"
+
+class CefSchemeHandlerFactory;
+
+///
+// Register a scheme handler factory with the global request context. An empty
+// |domain_name| value for a standard scheme will cause the factory to match all
+// domain names. The |domain_name| value will be ignored for non-standard
+// schemes. If |scheme_name| is a built-in scheme and no handler is returned by
+// |factory| then the built-in scheme handler factory will be called. If
+// |scheme_name| is a custom scheme then you must also implement the
+// CefApp::OnRegisterCustomSchemes() method in all processes. This function may
+// be called multiple times to change or remove the factory that matches the
+// specified |scheme_name| and optional |domain_name|. Returns false if an error
+// occurs. This function may be called on any thread in the browser process.
+// Using this function is equivalent to calling
+// CefRequestContext::GetGlobalContext()->RegisterSchemeHandlerFactory().
+///
+/*--cef(optional_param=domain_name,optional_param=factory)--*/
+bool CefRegisterSchemeHandlerFactory(
+    const CefString& scheme_name,
+    const CefString& domain_name,
+    CefRefPtr<CefSchemeHandlerFactory> factory);
+
+///
+// Clear all scheme handler factories registered with the global request
+// context. Returns false on error. This function may be called on any thread in
+// the browser process. Using this function is equivalent to calling
+// CefRequestContext::GetGlobalContext()->ClearSchemeHandlerFactories().
+///
+/*--cef()--*/
+bool CefClearSchemeHandlerFactories();
+
+///
+// Class that manages custom scheme registrations.
+///
+/*--cef(source=library)--*/
+class CefSchemeRegistrar : public CefBaseScoped {
+ public:
+  ///
+  // Register a custom scheme. This method should not be called for the built-in
+  // HTTP, HTTPS, FILE, FTP, ABOUT and DATA schemes.
+  //
+  // See cef_scheme_options_t for possible values for |options|.
+  //
+  // This function may be called on any thread. It should only be called once
+  // per unique |scheme_name| value. If |scheme_name| is already registered or
+  // if an error occurs this method will return false.
+  ///
+  /*--cef()--*/
+  virtual bool AddCustomScheme(const CefString& scheme_name, int options) = 0;
+};
+
+///
+// Class that creates CefResourceHandler instances for handling scheme requests.
+// The methods of this class will always be called on the IO thread.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefSchemeHandlerFactory : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Return a new resource handler instance to handle the request or an empty
+  // reference to allow default handling of the request. |browser| and |frame|
+  // will be the browser window and frame respectively that originated the
+  // request or NULL if the request did not originate from a browser window
+  // (for example, if the request came from CefURLRequest). The |request| object
+  // passed to this method cannot be modified.
+  ///
+  /*--cef(optional_param=browser,optional_param=frame)--*/
+  virtual CefRefPtr<CefResourceHandler> Create(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const CefString& scheme_name,
+      CefRefPtr<CefRequest> request) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_SCHEME_H_
diff --git a/src/include/cef_server.h b/src/include/cef_server.h
new file mode 100644
index 0000000..e22b8a6
--- /dev/null
+++ b/src/include/cef_server.h
@@ -0,0 +1,315 @@
+// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_SERVER_H_
+#define CEF_INCLUDE_CEF_SERVER_H_
+#pragma once
+
+#include <map>
+#include "include/cef_base.h"
+#include "include/cef_callback.h"
+#include "include/cef_request.h"
+#include "include/cef_task.h"
+
+class CefServerHandler;
+
+///
+// Class representing a server that supports HTTP and WebSocket requests. Server
+// capacity is limited and is intended to handle only a small number of
+// simultaneous connections (e.g. for communicating between applications on
+// localhost). The methods of this class are safe to call from any thread in the
+// brower process unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefServer : public CefBaseRefCounted {
+ public:
+  typedef std::multimap<CefString, CefString> HeaderMap;
+
+  ///
+  // Create a new server that binds to |address| and |port|. |address| must be a
+  // valid IPv4 or IPv6 address (e.g. 127.0.0.1 or ::1) and |port| must be a
+  // port number outside of the reserved range (e.g. between 1025 and 65535 on
+  // most platforms). |backlog| is the maximum number of pending connections.
+  // A new thread will be created for each CreateServer call (the "dedicated
+  // server thread"). It is therefore recommended to use a different
+  // CefServerHandler instance for each CreateServer call to avoid thread safety
+  // issues in the CefServerHandler implementation. The
+  // CefServerHandler::OnServerCreated method will be called on the dedicated
+  // server thread to report success or failure. See
+  // CefServerHandler::OnServerCreated documentation for a description of server
+  // lifespan.
+  ///
+  /*--cef()--*/
+  static void CreateServer(const CefString& address,
+                           uint16 port,
+                           int backlog,
+                           CefRefPtr<CefServerHandler> handler);
+
+  ///
+  // Returns the task runner for the dedicated server thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTaskRunner> GetTaskRunner() = 0;
+
+  ///
+  // Stop the server and shut down the dedicated server thread. See
+  // CefServerHandler::OnServerCreated documentation for a description of
+  // server lifespan.
+  ///
+  /*--cef()--*/
+  virtual void Shutdown() = 0;
+
+  ///
+  // Returns true if the server is currently running and accepting incoming
+  // connections. See CefServerHandler::OnServerCreated documentation for a
+  // description of server lifespan. This method must be called on the dedicated
+  // server thread.
+  ///
+  /*--cef()--*/
+  virtual bool IsRunning() = 0;
+
+  ///
+  // Returns the server address including the port number.
+  ///
+  /*--cef()--*/
+  virtual CefString GetAddress() = 0;
+
+  ///
+  // Returns true if the server currently has a connection. This method must be
+  // called on the dedicated server thread.
+  ///
+  /*--cef()--*/
+  virtual bool HasConnection() = 0;
+
+  ///
+  // Returns true if |connection_id| represents a valid connection. This method
+  // must be called on the dedicated server thread.
+  ///
+  /*--cef()--*/
+  virtual bool IsValidConnection(int connection_id) = 0;
+
+  ///
+  // Send an HTTP 200 "OK" response to the connection identified by
+  // |connection_id|. |content_type| is the response content type (e.g.
+  // "text/html"), |data| is the response content, and |data_size| is the size
+  // of |data| in bytes. The contents of |data| will be copied. The connection
+  // will be closed automatically after the response is sent.
+  ///
+  /*--cef()--*/
+  virtual void SendHttp200Response(int connection_id,
+                                   const CefString& content_type,
+                                   const void* data,
+                                   size_t data_size) = 0;
+
+  ///
+  // Send an HTTP 404 "Not Found" response to the connection identified by
+  // |connection_id|. The connection will be closed automatically after the
+  // response is sent.
+  ///
+  /*--cef()--*/
+  virtual void SendHttp404Response(int connection_id) = 0;
+
+  ///
+  // Send an HTTP 500 "Internal Server Error" response to the connection
+  // identified by |connection_id|. |error_message| is the associated error
+  // message. The connection will be closed automatically after the response is
+  // sent.
+  ///
+  /*--cef()--*/
+  virtual void SendHttp500Response(int connection_id,
+                                   const CefString& error_message) = 0;
+
+  ///
+  // Send a custom HTTP response to the connection identified by
+  // |connection_id|. |response_code| is the HTTP response code sent in the
+  // status line (e.g. 200), |content_type| is the response content type sent
+  // as the "Content-Type" header (e.g. "text/html"), |content_length| is the
+  // expected content length, and |extra_headers| is the map of extra response
+  // headers. If |content_length| is >= 0 then the "Content-Length" header will
+  // be sent. If |content_length| is 0 then no content is expected and the
+  // connection will be closed automatically after the response is sent. If
+  // |content_length| is < 0 then no "Content-Length" header will be sent and
+  // the client will continue reading until the connection is closed. Use the
+  // SendRawData method to send the content, if applicable, and call
+  // CloseConnection after all content has been sent.
+  ///
+  /*--cef(optional_param=extra_headers)--*/
+  virtual void SendHttpResponse(int connection_id,
+                                int response_code,
+                                const CefString& content_type,
+                                int64 content_length,
+                                const HeaderMap& extra_headers) = 0;
+
+  ///
+  // Send raw data directly to the connection identified by |connection_id|.
+  // |data| is the raw data and |data_size| is the size of |data| in bytes.
+  // The contents of |data| will be copied. No validation of |data| is
+  // performed internally so the client should be careful to send the amount
+  // indicated by the "Content-Length" header, if specified. See
+  // SendHttpResponse documentation for intended usage.
+  ///
+  /*--cef()--*/
+  virtual void SendRawData(int connection_id,
+                           const void* data,
+                           size_t data_size) = 0;
+
+  ///
+  // Close the connection identified by |connection_id|. See SendHttpResponse
+  // documentation for intended usage.
+  ///
+  /*--cef()--*/
+  virtual void CloseConnection(int connection_id) = 0;
+
+  ///
+  // Send a WebSocket message to the connection identified by |connection_id|.
+  // |data| is the response content and |data_size| is the size of |data| in
+  // bytes. The contents of |data| will be copied. See
+  // CefServerHandler::OnWebSocketRequest documentation for intended usage.
+  ///
+  /*--cef()--*/
+  virtual void SendWebSocketMessage(int connection_id,
+                                    const void* data,
+                                    size_t data_size) = 0;
+};
+
+///
+// Implement this interface to handle HTTP server requests. A new thread will be
+// created for each CefServer::CreateServer call (the "dedicated server
+// thread"), and the methods of this class will be called on that thread. It is
+// therefore recommended to use a different CefServerHandler instance for each
+// CefServer::CreateServer call to avoid thread safety issues in the
+// CefServerHandler implementation.
+///
+/*--cef(source=client)--*/
+class CefServerHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called when |server| is created. If the server was started successfully
+  // then CefServer::IsRunning will return true. The server will continue
+  // running until CefServer::Shutdown is called, after which time
+  // OnServerDestroyed will be called. If the server failed to start then
+  // OnServerDestroyed will be called immediately after this method returns.
+  ///
+  /*--cef()--*/
+  virtual void OnServerCreated(CefRefPtr<CefServer> server) = 0;
+
+  ///
+  // Called when |server| is destroyed. The server thread will be stopped after
+  // this method returns. The client should release any references to |server|
+  // when this method is called. See OnServerCreated documentation for a
+  // description of server lifespan.
+  ///
+  /*--cef()--*/
+  virtual void OnServerDestroyed(CefRefPtr<CefServer> server) = 0;
+
+  ///
+  // Called when a client connects to |server|. |connection_id| uniquely
+  // identifies the connection. Each call to this method will have a matching
+  // call to OnClientDisconnected.
+  ///
+  /*--cef()--*/
+  virtual void OnClientConnected(CefRefPtr<CefServer> server,
+                                 int connection_id) = 0;
+
+  ///
+  // Called when a client disconnects from |server|. |connection_id| uniquely
+  // identifies the connection. The client should release any data associated
+  // with |connection_id| when this method is called and |connection_id| should
+  // no longer be passed to CefServer methods. Disconnects can originate from
+  // either the client or the server. For example, the server will disconnect
+  // automatically after a CefServer::SendHttpXXXResponse method is called.
+  ///
+  /*--cef()--*/
+  virtual void OnClientDisconnected(CefRefPtr<CefServer> server,
+                                    int connection_id) = 0;
+
+  ///
+  // Called when |server| receives an HTTP request. |connection_id| uniquely
+  // identifies the connection, |client_address| is the requesting IPv4 or IPv6
+  // client address including port number, and |request| contains the request
+  // contents (URL, method, headers and optional POST data). Call CefServer
+  // methods either synchronously or asynchronusly to send a response.
+  ///
+  /*--cef()--*/
+  virtual void OnHttpRequest(CefRefPtr<CefServer> server,
+                             int connection_id,
+                             const CefString& client_address,
+                             CefRefPtr<CefRequest> request) = 0;
+
+  ///
+  // Called when |server| receives a WebSocket request. |connection_id| uniquely
+  // identifies the connection, |client_address| is the requesting IPv4 or
+  // IPv6 client address including port number, and |request| contains the
+  // request contents (URL, method, headers and optional POST data). Execute
+  // |callback| either synchronously or asynchronously to accept or decline the
+  // WebSocket connection. If the request is accepted then OnWebSocketConnected
+  // will be called after the WebSocket has connected and incoming messages will
+  // be delivered to the OnWebSocketMessage callback. If the request is declined
+  // then the client will be disconnected and OnClientDisconnected will be
+  // called. Call the CefServer::SendWebSocketMessage method after receiving the
+  // OnWebSocketConnected callback to respond with WebSocket messages.
+  ///
+  /*--cef()--*/
+  virtual void OnWebSocketRequest(CefRefPtr<CefServer> server,
+                                  int connection_id,
+                                  const CefString& client_address,
+                                  CefRefPtr<CefRequest> request,
+                                  CefRefPtr<CefCallback> callback) = 0;
+
+  ///
+  // Called after the client has accepted the WebSocket connection for |server|
+  // and |connection_id| via the OnWebSocketRequest callback. See
+  // OnWebSocketRequest documentation for intended usage.
+  ///
+  /*--cef()--*/
+  virtual void OnWebSocketConnected(CefRefPtr<CefServer> server,
+                                    int connection_id) = 0;
+
+  ///
+  // Called when |server| receives an WebSocket message. |connection_id|
+  // uniquely identifies the connection, |data| is the message content and
+  // |data_size| is the size of |data| in bytes. Do not keep a reference to
+  // |data| outside of this method. See OnWebSocketRequest documentation for
+  // intended usage.
+  ///
+  /*--cef()--*/
+  virtual void OnWebSocketMessage(CefRefPtr<CefServer> server,
+                                  int connection_id,
+                                  const void* data,
+                                  size_t data_size) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_SERVER_H_
diff --git a/src/include/cef_ssl_info.h b/src/include/cef_ssl_info.h
new file mode 100644
index 0000000..40dc5c0
--- /dev/null
+++ b/src/include/cef_ssl_info.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2015 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_SSL_INFO_H_
+#define CEF_INCLUDE_CEF_SSL_INFO_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_values.h"
+
+#include "include/cef_x509_certificate.h"
+
+///
+// Class representing SSL information.
+///
+/*--cef(source=library)--*/
+class CefSSLInfo : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns a bitmask containing any and all problems verifying the server
+  // certificate.
+  ///
+  /*--cef(default_retval=CERT_STATUS_NONE)--*/
+  virtual cef_cert_status_t GetCertStatus() = 0;
+
+  ///
+  // Returns the X.509 certificate.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefX509Certificate> GetX509Certificate() = 0;
+};
+
+///
+// Returns true if the certificate status represents an error.
+///
+/*--cef()--*/
+bool CefIsCertStatusError(cef_cert_status_t status);
+
+#endif  // CEF_INCLUDE_CEF_SSL_INFO_H_
diff --git a/src/include/cef_ssl_status.h b/src/include/cef_ssl_status.h
new file mode 100644
index 0000000..002ac49
--- /dev/null
+++ b/src/include/cef_ssl_status.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_SSL_STATUS_H_
+#define CEF_INCLUDE_CEF_SSL_STATUS_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_values.h"
+#include "include/cef_x509_certificate.h"
+
+///
+// Class representing the SSL information for a navigation entry.
+///
+/*--cef(source=library)--*/
+class CefSSLStatus : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns true if the status is related to a secure SSL/TLS connection.
+  ///
+  /*--cef()--*/
+  virtual bool IsSecureConnection() = 0;
+
+  ///
+  // Returns a bitmask containing any and all problems verifying the server
+  // certificate.
+  ///
+  /*--cef(default_retval=CERT_STATUS_NONE)--*/
+  virtual cef_cert_status_t GetCertStatus() = 0;
+
+  ///
+  // Returns the SSL version used for the SSL connection.
+  ///
+  /*--cef(default_retval=SSL_CONNECTION_VERSION_UNKNOWN)--*/
+  virtual cef_ssl_version_t GetSSLVersion() = 0;
+
+  ///
+  // Returns a bitmask containing the page security content status.
+  ///
+  /*--cef(default_retval=SSL_CONTENT_NORMAL_CONTENT)--*/
+  virtual cef_ssl_content_status_t GetContentStatus() = 0;
+
+  ///
+  // Returns the X.509 certificate.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefX509Certificate> GetX509Certificate() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_SSL_STATUS_H_
diff --git a/src/include/cef_stream.h b/src/include/cef_stream.h
new file mode 100644
index 0000000..c40392e
--- /dev/null
+++ b/src/include/cef_stream.h
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_STREAM_H_
+#define CEF_INCLUDE_CEF_STREAM_H_
+
+#include "include/cef_base.h"
+
+///
+// Interface the client can implement to provide a custom stream reader. The
+// methods of this class may be called on any thread.
+///
+/*--cef(source=client)--*/
+class CefReadHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Read raw binary data.
+  ///
+  /*--cef()--*/
+  virtual size_t Read(void* ptr, size_t size, size_t n) = 0;
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of
+  // SEEK_CUR, SEEK_END or SEEK_SET. Return zero on success and non-zero on
+  // failure.
+  ///
+  /*--cef()--*/
+  virtual int Seek(int64 offset, int whence) = 0;
+
+  ///
+  // Return the current offset position.
+  ///
+  /*--cef()--*/
+  virtual int64 Tell() = 0;
+
+  ///
+  // Return non-zero if at end of file.
+  ///
+  /*--cef()--*/
+  virtual int Eof() = 0;
+
+  ///
+  // Return true if this handler performs work like accessing the file system
+  // which may block. Used as a hint for determining the thread to access the
+  // handler from.
+  ///
+  /*--cef()--*/
+  virtual bool MayBlock() = 0;
+};
+
+///
+// Class used to read data from a stream. The methods of this class may be
+// called on any thread.
+///
+/*--cef(source=library)--*/
+class CefStreamReader : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Create a new CefStreamReader object from a file.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefStreamReader> CreateForFile(const CefString& fileName);
+  ///
+  // Create a new CefStreamReader object from data.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefStreamReader> CreateForData(void* data, size_t size);
+  ///
+  // Create a new CefStreamReader object from a custom handler.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefStreamReader> CreateForHandler(
+      CefRefPtr<CefReadHandler> handler);
+
+  ///
+  // Read raw binary data.
+  ///
+  /*--cef()--*/
+  virtual size_t Read(void* ptr, size_t size, size_t n) = 0;
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of
+  // SEEK_CUR, SEEK_END or SEEK_SET. Returns zero on success and non-zero on
+  // failure.
+  ///
+  /*--cef()--*/
+  virtual int Seek(int64 offset, int whence) = 0;
+
+  ///
+  // Return the current offset position.
+  ///
+  /*--cef()--*/
+  virtual int64 Tell() = 0;
+
+  ///
+  // Return non-zero if at end of file.
+  ///
+  /*--cef()--*/
+  virtual int Eof() = 0;
+
+  ///
+  // Returns true if this reader performs work like accessing the file system
+  // which may block. Used as a hint for determining the thread to access the
+  // reader from.
+  ///
+  /*--cef()--*/
+  virtual bool MayBlock() = 0;
+};
+
+///
+// Interface the client can implement to provide a custom stream writer. The
+// methods of this class may be called on any thread.
+///
+/*--cef(source=client)--*/
+class CefWriteHandler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Write raw binary data.
+  ///
+  /*--cef()--*/
+  virtual size_t Write(const void* ptr, size_t size, size_t n) = 0;
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of
+  // SEEK_CUR, SEEK_END or SEEK_SET. Return zero on success and non-zero on
+  // failure.
+  ///
+  /*--cef()--*/
+  virtual int Seek(int64 offset, int whence) = 0;
+
+  ///
+  // Return the current offset position.
+  ///
+  /*--cef()--*/
+  virtual int64 Tell() = 0;
+
+  ///
+  // Flush the stream.
+  ///
+  /*--cef()--*/
+  virtual int Flush() = 0;
+
+  ///
+  // Return true if this handler performs work like accessing the file system
+  // which may block. Used as a hint for determining the thread to access the
+  // handler from.
+  ///
+  /*--cef()--*/
+  virtual bool MayBlock() = 0;
+};
+
+///
+// Class used to write data to a stream. The methods of this class may be called
+// on any thread.
+///
+/*--cef(source=library)--*/
+class CefStreamWriter : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Create a new CefStreamWriter object for a file.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefStreamWriter> CreateForFile(const CefString& fileName);
+  ///
+  // Create a new CefStreamWriter object for a custom handler.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefStreamWriter> CreateForHandler(
+      CefRefPtr<CefWriteHandler> handler);
+
+  ///
+  // Write raw binary data.
+  ///
+  /*--cef()--*/
+  virtual size_t Write(const void* ptr, size_t size, size_t n) = 0;
+
+  ///
+  // Seek to the specified offset position. |whence| may be any one of
+  // SEEK_CUR, SEEK_END or SEEK_SET. Returns zero on success and non-zero on
+  // failure.
+  ///
+  /*--cef()--*/
+  virtual int Seek(int64 offset, int whence) = 0;
+
+  ///
+  // Return the current offset position.
+  ///
+  /*--cef()--*/
+  virtual int64 Tell() = 0;
+
+  ///
+  // Flush the stream.
+  ///
+  /*--cef()--*/
+  virtual int Flush() = 0;
+
+  ///
+  // Returns true if this writer performs work like accessing the file system
+  // which may block. Used as a hint for determining the thread to access the
+  // writer from.
+  ///
+  /*--cef()--*/
+  virtual bool MayBlock() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_STREAM_H_
diff --git a/src/include/cef_string_visitor.h b/src/include/cef_string_visitor.h
new file mode 100644
index 0000000..f23dfb6
--- /dev/null
+++ b/src/include/cef_string_visitor.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_STRING_VISITOR_H_
+#define CEF_INCLUDE_CEF_STRING_VISITOR_H_
+
+#include "include/cef_base.h"
+
+///
+// Implement this interface to receive string values asynchronously.
+///
+/*--cef(source=client)--*/
+class CefStringVisitor : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be executed.
+  ///
+  /*--cef(optional_param=string)--*/
+  virtual void Visit(const CefString& string) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_STRING_VISITOR_H_
diff --git a/src/include/cef_task.h b/src/include/cef_task.h
new file mode 100644
index 0000000..642c69e
--- /dev/null
+++ b/src/include/cef_task.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_TASK_H_
+#define CEF_INCLUDE_CEF_TASK_H_
+
+#include "include/cef_base.h"
+
+typedef cef_thread_id_t CefThreadId;
+
+///
+// Implement this interface for asynchronous task execution. If the task is
+// posted successfully and if the associated message loop is still running then
+// the Execute() method will be called on the target thread. If the task fails
+// to post then the task object may be destroyed on the source thread instead of
+// the target thread. For this reason be cautious when performing work in the
+// task object destructor.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefTask : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be executed on the target thread.
+  ///
+  /*--cef()--*/
+  virtual void Execute() = 0;
+};
+
+///
+// Class that asynchronously executes tasks on the associated thread. It is safe
+// to call the methods of this class on any thread.
+//
+// CEF maintains multiple internal threads that are used for handling different
+// types of tasks in different processes. The cef_thread_id_t definitions in
+// cef_types.h list the common CEF threads. Task runners are also available for
+// other CEF threads as appropriate (for example, V8 WebWorker threads).
+///
+/*--cef(source=library)--*/
+class CefTaskRunner : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the task runner for the current thread. Only CEF threads will have
+  // task runners. An empty reference will be returned if this method is called
+  // on an invalid thread.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefTaskRunner> GetForCurrentThread();
+
+  ///
+  // Returns the task runner for the specified CEF thread.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefTaskRunner> GetForThread(CefThreadId threadId);
+
+  ///
+  // Returns true if this object is pointing to the same task runner as |that|
+  // object.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefTaskRunner> that) = 0;
+
+  ///
+  // Returns true if this task runner belongs to the current thread.
+  ///
+  /*--cef()--*/
+  virtual bool BelongsToCurrentThread() = 0;
+
+  ///
+  // Returns true if this task runner is for the specified CEF thread.
+  ///
+  /*--cef()--*/
+  virtual bool BelongsToThread(CefThreadId threadId) = 0;
+
+  ///
+  // Post a task for execution on the thread associated with this task runner.
+  // Execution will occur asynchronously.
+  ///
+  /*--cef()--*/
+  virtual bool PostTask(CefRefPtr<CefTask> task) = 0;
+
+  ///
+  // Post a task for delayed execution on the thread associated with this task
+  // runner. Execution will occur asynchronously. Delayed tasks are not
+  // supported on V8 WebWorker threads and will be executed without the
+  // specified delay.
+  ///
+  /*--cef()--*/
+  virtual bool PostDelayedTask(CefRefPtr<CefTask> task, int64 delay_ms) = 0;
+};
+
+///
+// Returns true if called on the specified thread. Equivalent to using
+// CefTaskRunner::GetForThread(threadId)->BelongsToCurrentThread().
+///
+/*--cef()--*/
+bool CefCurrentlyOn(CefThreadId threadId);
+
+///
+// Post a task for execution on the specified thread. Equivalent to
+// using CefTaskRunner::GetForThread(threadId)->PostTask(task).
+///
+/*--cef()--*/
+bool CefPostTask(CefThreadId threadId, CefRefPtr<CefTask> task);
+
+///
+// Post a task for delayed execution on the specified thread. Equivalent to
+// using CefTaskRunner::GetForThread(threadId)->PostDelayedTask(task, delay_ms).
+///
+/*--cef()--*/
+bool CefPostDelayedTask(CefThreadId threadId,
+                        CefRefPtr<CefTask> task,
+                        int64 delay_ms);
+
+#endif  // CEF_INCLUDE_CEF_TASK_H_
diff --git a/src/include/cef_thread.h b/src/include/cef_thread.h
new file mode 100644
index 0000000..1d0bd4c
--- /dev/null
+++ b/src/include/cef_thread.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_THREAD_H_
+#define CEF_INCLUDE_CEF_THREAD_H_
+#pragma once
+
+#include "include/cef_task.h"
+#include "include/internal/cef_thread_internal.h"
+
+///
+// A simple thread abstraction that establishes a message loop on a new thread.
+// The consumer uses CefTaskRunner to execute code on the thread's message loop.
+// The thread is terminated when the CefThread object is destroyed or Stop() is
+// called. All pending tasks queued on the thread's message loop will run to
+// completion before the thread is terminated. CreateThread() can be called on
+// any valid CEF thread in either the browser or render process. This class
+// should only be used for tasks that require a dedicated thread. In most cases
+// you can post tasks to an existing CEF thread instead of creating a new one;
+// see cef_task.h for details.
+///
+/*--cef(source=library)--*/
+class CefThread : public CefBaseRefCounted {
+ public:
+  ///
+  // Create and start a new thread. This method does not block waiting for the
+  // thread to run initialization. |display_name| is the name that will be used
+  // to identify the thread. |priority| is the thread execution priority.
+  // |message_loop_type| indicates the set of asynchronous events that the
+  // thread can process. If |stoppable| is true the thread will stopped and
+  // joined on destruction or when Stop() is called; otherwise, the the thread
+  // cannot be stopped and will be leaked on shutdown. On Windows the
+  // |com_init_mode| value specifies how COM will be initialized for the thread.
+  // If |com_init_mode| is set to COM_INIT_MODE_STA then |message_loop_type|
+  // must be set to ML_TYPE_UI.
+  ///
+  /*--cef(optional_param=display_name)--*/
+  static CefRefPtr<CefThread> CreateThread(
+      const CefString& display_name,
+      cef_thread_priority_t priority,
+      cef_message_loop_type_t message_loop_type,
+      bool stoppable,
+      cef_com_init_mode_t com_init_mode);
+
+  ///
+  // Create and start a new thread with default/recommended values.
+  // |display_name| is the name that will be used to identify the thread.
+  ///
+  static CefRefPtr<CefThread> CreateThread(const CefString& display_name) {
+    return CreateThread(display_name, TP_NORMAL, ML_TYPE_DEFAULT, true,
+                        COM_INIT_MODE_NONE);
+  }
+
+  ///
+  // Returns the CefTaskRunner that will execute code on this thread's message
+  // loop. This method is safe to call from any thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTaskRunner> GetTaskRunner() = 0;
+
+  ///
+  // Returns the platform thread ID. It will return the same value after Stop()
+  // is called. This method is safe to call from any thread.
+  ///
+  /*--cef(default_retval=kInvalidPlatformThreadId)--*/
+  virtual cef_platform_thread_id_t GetPlatformThreadId() = 0;
+
+  ///
+  // Stop and join the thread. This method must be called from the same thread
+  // that called CreateThread(). Do not call this method if CreateThread() was
+  // called with a |stoppable| value of false.
+  ///
+  /*--cef()--*/
+  virtual void Stop() = 0;
+
+  ///
+  // Returns true if the thread is currently running. This method must be called
+  // from the same thread that called CreateThread().
+  ///
+  /*--cef()--*/
+  virtual bool IsRunning() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_THREAD_H_
diff --git a/src/include/cef_trace.h b/src/include/cef_trace.h
new file mode 100644
index 0000000..bc92d38
--- /dev/null
+++ b/src/include/cef_trace.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. Portons copyright (c) 2012
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+// See cef_trace_event.h for trace macros and additonal documentation.
+
+#ifndef CEF_INCLUDE_CEF_TRACE_H_
+#define CEF_INCLUDE_CEF_TRACE_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_callback.h"
+
+///
+// Implement this interface to receive notification when tracing has completed.
+// The methods of this class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefEndTracingCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called after all processes have sent their trace data. |tracing_file| is
+  // the path at which tracing data was written. The client is responsible for
+  // deleting |tracing_file|.
+  ///
+  /*--cef()--*/
+  virtual void OnEndTracingComplete(const CefString& tracing_file) = 0;
+};
+
+///
+// Start tracing events on all processes. Tracing is initialized asynchronously
+// and |callback| will be executed on the UI thread after initialization is
+// complete.
+//
+// If CefBeginTracing was called previously, or if a CefEndTracingAsync call is
+// pending, CefBeginTracing will fail and return false.
+//
+// |categories| is a comma-delimited list of category wildcards. A category can
+// have an optional '-' prefix to make it an excluded category. Having both
+// included and excluded categories in the same list is not supported.
+//
+// Example: "test_MyTest*"
+// Example: "test_MyTest*,test_OtherStuff"
+// Example: "-excluded_category1,-excluded_category2"
+//
+// This function must be called on the browser process UI thread.
+///
+/*--cef(optional_param=categories,optional_param=callback)--*/
+bool CefBeginTracing(const CefString& categories,
+                     CefRefPtr<CefCompletionCallback> callback);
+
+///
+// Stop tracing events on all processes.
+//
+// This function will fail and return false if a previous call to
+// CefEndTracingAsync is already pending or if CefBeginTracing was not called.
+//
+// |tracing_file| is the path at which tracing data will be written and
+// |callback| is the callback that will be executed once all processes have
+// sent their trace data. If |tracing_file| is empty a new temporary file path
+// will be used. If |callback| is empty no trace data will be written.
+//
+// This function must be called on the browser process UI thread.
+///
+/*--cef(optional_param=tracing_file,optional_param=callback)--*/
+bool CefEndTracing(const CefString& tracing_file,
+                   CefRefPtr<CefEndTracingCallback> callback);
+
+///
+// Returns the current system trace time or, if none is defined, the current
+// high-res time. Can be used by clients to synchronize with the time
+// information in trace events.
+///
+/*--cef()--*/
+int64 CefNowFromSystemTraceTime();
+
+#endif  // CEF_INCLUDE_CEF_TRACE_H_
diff --git a/src/include/cef_urlrequest.h b/src/include/cef_urlrequest.h
new file mode 100644
index 0000000..8966eb8
--- /dev/null
+++ b/src/include/cef_urlrequest.h
@@ -0,0 +1,204 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_URLREQUEST_H_
+#define CEF_INCLUDE_CEF_URLREQUEST_H_
+#pragma once
+
+#include "include/cef_auth_callback.h"
+#include "include/cef_base.h"
+#include "include/cef_request.h"
+#include "include/cef_request_context.h"
+#include "include/cef_response.h"
+
+class CefURLRequestClient;
+
+///
+// Class used to make a URL request. URL requests are not associated with a
+// browser instance so no CefClient callbacks will be executed. URL requests
+// can be created on any valid CEF thread in either the browser or render
+// process. Once created the methods of the URL request object must be accessed
+// on the same thread that created it.
+///
+/*--cef(source=library)--*/
+class CefURLRequest : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_urlrequest_status_t Status;
+  typedef cef_errorcode_t ErrorCode;
+
+  ///
+  // Create a new URL request that is not associated with a specific browser or
+  // frame. Use CefFrame::CreateURLRequest instead if you want the request to
+  // have this association, in which case it may be handled differently (see
+  // documentation on that method). Requests may originate from the both browser
+  // process and the render process.
+  //
+  // For requests originating from the browser process:
+  //   - It may be intercepted by the client via CefResourceRequestHandler or
+  //     CefSchemeHandlerFactory.
+  //   - POST data may only contain only a single element of type PDE_TYPE_FILE
+  //     or PDE_TYPE_BYTES.
+  //   - If |request_context| is empty the global request context will be used.
+  // For requests originating from the render process:
+  //   - It cannot be intercepted by the client so only http(s) and blob schemes
+  //     are supported.
+  //   - POST data may only contain a single element of type PDE_TYPE_BYTES.
+  //   - The |request_context| parameter must be NULL.
+  //
+  // The |request| object will be marked as read-only after calling this method.
+  ///
+  /*--cef(optional_param=request_context)--*/
+  static CefRefPtr<CefURLRequest> Create(
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefURLRequestClient> client,
+      CefRefPtr<CefRequestContext> request_context);
+
+  ///
+  // Returns the request object used to create this URL request. The returned
+  // object is read-only and should not be modified.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefRequest> GetRequest() = 0;
+
+  ///
+  // Returns the client.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefURLRequestClient> GetClient() = 0;
+
+  ///
+  // Returns the request status.
+  ///
+  /*--cef(default_retval=UR_UNKNOWN)--*/
+  virtual Status GetRequestStatus() = 0;
+
+  ///
+  // Returns the request error if status is UR_CANCELED or UR_FAILED, or 0
+  // otherwise.
+  ///
+  /*--cef(default_retval=ERR_NONE)--*/
+  virtual ErrorCode GetRequestError() = 0;
+
+  ///
+  // Returns the response, or NULL if no response information is available.
+  // Response information will only be available after the upload has completed.
+  // The returned object is read-only and should not be modified.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefResponse> GetResponse() = 0;
+
+  ///
+  // Returns true if the response body was served from the cache. This includes
+  // responses for which revalidation was required.
+  ///
+  /*--cef()--*/
+  virtual bool ResponseWasCached() = 0;
+
+  ///
+  // Cancel the request.
+  ///
+  /*--cef()--*/
+  virtual void Cancel() = 0;
+};
+
+///
+// Interface that should be implemented by the CefURLRequest client. The
+// methods of this class will be called on the same thread that created the
+// request unless otherwise documented.
+///
+/*--cef(source=client)--*/
+class CefURLRequestClient : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Notifies the client that the request has completed. Use the
+  // CefURLRequest::GetRequestStatus method to determine if the request was
+  // successful or not.
+  ///
+  /*--cef()--*/
+  virtual void OnRequestComplete(CefRefPtr<CefURLRequest> request) = 0;
+
+  ///
+  // Notifies the client of upload progress. |current| denotes the number of
+  // bytes sent so far and |total| is the total size of uploading data (or -1 if
+  // chunked upload is enabled). This method will only be called if the
+  // UR_FLAG_REPORT_UPLOAD_PROGRESS flag is set on the request.
+  ///
+  /*--cef()--*/
+  virtual void OnUploadProgress(CefRefPtr<CefURLRequest> request,
+                                int64 current,
+                                int64 total) = 0;
+
+  ///
+  // Notifies the client of download progress. |current| denotes the number of
+  // bytes received up to the call and |total| is the expected total size of the
+  // response (or -1 if not determined).
+  ///
+  /*--cef()--*/
+  virtual void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
+                                  int64 current,
+                                  int64 total) = 0;
+
+  ///
+  // Called when some part of the response is read. |data| contains the current
+  // bytes received since the last call. This method will not be called if the
+  // UR_FLAG_NO_DOWNLOAD_DATA flag is set on the request.
+  ///
+  /*--cef()--*/
+  virtual void OnDownloadData(CefRefPtr<CefURLRequest> request,
+                              const void* data,
+                              size_t data_length) = 0;
+
+  ///
+  // Called on the IO thread when the browser needs credentials from the user.
+  // |isProxy| indicates whether the host is a proxy server. |host| contains the
+  // hostname and |port| contains the port number. Return true to continue the
+  // request and call CefAuthCallback::Continue() when the authentication
+  // information is available. If the request has an associated browser/frame
+  // then returning false will result in a call to GetAuthCredentials on the
+  // CefRequestHandler associated with that browser, if any. Otherwise,
+  // returning false will cancel the request immediately. This method will only
+  // be called for requests initiated from the browser process.
+  ///
+  /*--cef(optional_param=realm)--*/
+  virtual bool GetAuthCredentials(bool isProxy,
+                                  const CefString& host,
+                                  int port,
+                                  const CefString& realm,
+                                  const CefString& scheme,
+                                  CefRefPtr<CefAuthCallback> callback) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_URLREQUEST_H_
diff --git a/src/include/cef_v8.h b/src/include/cef_v8.h
new file mode 100644
index 0000000..8bbc028
--- /dev/null
+++ b/src/include/cef_v8.h
@@ -0,0 +1,998 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_V8_H_
+#define CEF_INCLUDE_CEF_V8_H_
+#pragma once
+
+#include <vector>
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_task.h"
+
+class CefV8Exception;
+class CefV8Handler;
+class CefV8StackFrame;
+class CefV8Value;
+
+///
+// Register a new V8 extension with the specified JavaScript extension code and
+// handler. Functions implemented by the handler are prototyped using the
+// keyword 'native'. The calling of a native function is restricted to the scope
+// in which the prototype of the native function is defined. This function may
+// only be called on the render process main thread.
+//
+// Example JavaScript extension code:
+// <pre>
+//   // create the 'example' global object if it doesn't already exist.
+//   if (!example)
+//     example = {};
+//   // create the 'example.test' global object if it doesn't already exist.
+//   if (!example.test)
+//     example.test = {};
+//   (function() {
+//     // Define the function 'example.test.myfunction'.
+//     example.test.myfunction = function() {
+//       // Call CefV8Handler::Execute() with the function name 'MyFunction'
+//       // and no arguments.
+//       native function MyFunction();
+//       return MyFunction();
+//     };
+//     // Define the getter function for parameter 'example.test.myparam'.
+//     example.test.__defineGetter__('myparam', function() {
+//       // Call CefV8Handler::Execute() with the function name 'GetMyParam'
+//       // and no arguments.
+//       native function GetMyParam();
+//       return GetMyParam();
+//     });
+//     // Define the setter function for parameter 'example.test.myparam'.
+//     example.test.__defineSetter__('myparam', function(b) {
+//       // Call CefV8Handler::Execute() with the function name 'SetMyParam'
+//       // and a single argument.
+//       native function SetMyParam();
+//       if(b) SetMyParam(b);
+//     });
+//
+//     // Extension definitions can also contain normal JavaScript variables
+//     // and functions.
+//     var myint = 0;
+//     example.test.increment = function() {
+//       myint += 1;
+//       return myint;
+//     };
+//   })();
+// </pre>
+// Example usage in the page:
+// <pre>
+//   // Call the function.
+//   example.test.myfunction();
+//   // Set the parameter.
+//   example.test.myparam = value;
+//   // Get the parameter.
+//   value = example.test.myparam;
+//   // Call another function.
+//   example.test.increment();
+// </pre>
+///
+/*--cef(optional_param=handler)--*/
+bool CefRegisterExtension(const CefString& extension_name,
+                          const CefString& javascript_code,
+                          CefRefPtr<CefV8Handler> handler);
+
+///
+// Class representing a V8 context handle. V8 handles can only be accessed from
+// the thread on which they are created. Valid threads for creating a V8 handle
+// include the render process main thread (TID_RENDERER) and WebWorker threads.
+// A task runner for posting tasks on the associated thread can be retrieved via
+// the CefV8Context::GetTaskRunner() method.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefV8Context : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the current (top) context object in the V8 context stack.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Context> GetCurrentContext();
+
+  ///
+  // Returns the entered (bottom) context object in the V8 context stack.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Context> GetEnteredContext();
+
+  ///
+  // Returns true if V8 is currently inside a context.
+  ///
+  /*--cef()--*/
+  static bool InContext();
+
+  ///
+  // Returns the task runner associated with this context. V8 handles can only
+  // be accessed from the thread on which they are created. This method can be
+  // called on any render process thread.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTaskRunner> GetTaskRunner() = 0;
+
+  ///
+  // Returns true if the underlying handle is valid and it can be accessed on
+  // the current thread. Do not call any other methods if this method returns
+  // false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns the browser for this context. This method will return an empty
+  // reference for WebWorker contexts.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
+
+  ///
+  // Returns the frame for this context. This method will return an empty
+  // reference for WebWorker contexts.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFrame> GetFrame() = 0;
+
+  ///
+  // Returns the global object for this context. The context must be entered
+  // before calling this method.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefV8Value> GetGlobal() = 0;
+
+  ///
+  // Enter this context. A context must be explicitly entered before creating a
+  // V8 Object, Array, Function or Date asynchronously. Exit() must be called
+  // the same number of times as Enter() before releasing this context. V8
+  // objects belong to the context in which they are created. Returns true if
+  // the scope was entered successfully.
+  ///
+  /*--cef()--*/
+  virtual bool Enter() = 0;
+
+  ///
+  // Exit this context. Call this method only after calling Enter(). Returns
+  // true if the scope was exited successfully.
+  ///
+  /*--cef()--*/
+  virtual bool Exit() = 0;
+
+  ///
+  // Returns true if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefV8Context> that) = 0;
+
+  ///
+  // Execute a string of JavaScript code in this V8 context. The |script_url|
+  // parameter is the URL where the script in question can be found, if any.
+  // The |start_line| parameter is the base line number to use for error
+  // reporting. On success |retval| will be set to the return value, if any, and
+  // the function will return true. On failure |exception| will be set to the
+  // exception, if any, and the function will return false.
+  ///
+  /*--cef(optional_param=script_url)--*/
+  virtual bool Eval(const CefString& code,
+                    const CefString& script_url,
+                    int start_line,
+                    CefRefPtr<CefV8Value>& retval,
+                    CefRefPtr<CefV8Exception>& exception) = 0;
+};
+
+typedef std::vector<CefRefPtr<CefV8Value>> CefV8ValueList;
+
+///
+// Interface that should be implemented to handle V8 function calls. The methods
+// of this class will be called on the thread associated with the V8 function.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefV8Handler : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Handle execution of the function identified by |name|. |object| is the
+  // receiver ('this' object) of the function. |arguments| is the list of
+  // arguments passed to the function. If execution succeeds set |retval| to the
+  // function return value. If execution fails set |exception| to the exception
+  // that will be thrown. Return true if execution was handled.
+  ///
+  /*--cef()--*/
+  virtual bool Execute(const CefString& name,
+                       CefRefPtr<CefV8Value> object,
+                       const CefV8ValueList& arguments,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) = 0;
+};
+
+///
+// Interface that should be implemented to handle V8 accessor calls. Accessor
+// identifiers are registered by calling CefV8Value::SetValue(). The methods
+// of this class will be called on the thread associated with the V8 accessor.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefV8Accessor : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Handle retrieval the accessor value identified by |name|. |object| is the
+  // receiver ('this' object) of the accessor. If retrieval succeeds set
+  // |retval| to the return value. If retrieval fails set |exception| to the
+  // exception that will be thrown. Return true if accessor retrieval was
+  // handled.
+  ///
+  /*--cef()--*/
+  virtual bool Get(const CefString& name,
+                   const CefRefPtr<CefV8Value> object,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) = 0;
+
+  ///
+  // Handle assignment of the accessor value identified by |name|. |object| is
+  // the receiver ('this' object) of the accessor. |value| is the new value
+  // being assigned to the accessor. If assignment fails set |exception| to the
+  // exception that will be thrown. Return true if accessor assignment was
+  // handled.
+  ///
+  /*--cef()--*/
+  virtual bool Set(const CefString& name,
+                   const CefRefPtr<CefV8Value> object,
+                   const CefRefPtr<CefV8Value> value,
+                   CefString& exception) = 0;
+};
+
+///
+// Interface that should be implemented to handle V8 interceptor calls. The
+// methods of this class will be called on the thread associated with the V8
+// interceptor. Interceptor's named property handlers (with first argument of
+// type CefString) are called when object is indexed by string. Indexed property
+// handlers (with first argument of type int) are called when object is indexed
+// by integer.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefV8Interceptor : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Handle retrieval of the interceptor value identified by |name|. |object| is
+  // the receiver ('this' object) of the interceptor. If retrieval succeeds, set
+  // |retval| to the return value. If the requested value does not exist, don't
+  // set either |retval| or |exception|. If retrieval fails, set |exception| to
+  // the exception that will be thrown. If the property has an associated
+  // accessor, it will be called only if you don't set |retval|.
+  // Return true if interceptor retrieval was handled, false otherwise.
+  ///
+  /*--cef(capi_name=get_byname)--*/
+  virtual bool Get(const CefString& name,
+                   const CefRefPtr<CefV8Value> object,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) = 0;
+
+  ///
+  // Handle retrieval of the interceptor value identified by |index|. |object|
+  // is the receiver ('this' object) of the interceptor. If retrieval succeeds,
+  // set |retval| to the return value. If the requested value does not exist,
+  // don't set either |retval| or |exception|. If retrieval fails, set
+  // |exception| to the exception that will be thrown.
+  // Return true if interceptor retrieval was handled, false otherwise.
+  ///
+  /*--cef(capi_name=get_byindex,index_param=index)--*/
+  virtual bool Get(int index,
+                   const CefRefPtr<CefV8Value> object,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) = 0;
+
+  ///
+  // Handle assignment of the interceptor value identified by |name|. |object|
+  // is the receiver ('this' object) of the interceptor. |value| is the new
+  // value being assigned to the interceptor. If assignment fails, set
+  // |exception| to the exception that will be thrown. This setter will always
+  // be called, even when the property has an associated accessor.
+  // Return true if interceptor assignment was handled, false otherwise.
+  ///
+  /*--cef(capi_name=set_byname)--*/
+  virtual bool Set(const CefString& name,
+                   const CefRefPtr<CefV8Value> object,
+                   const CefRefPtr<CefV8Value> value,
+                   CefString& exception) = 0;
+
+  ///
+  // Handle assignment of the interceptor value identified by |index|. |object|
+  // is the receiver ('this' object) of the interceptor. |value| is the new
+  // value being assigned to the interceptor. If assignment fails, set
+  // |exception| to the exception that will be thrown.
+  // Return true if interceptor assignment was handled, false otherwise.
+  ///
+  /*--cef(capi_name=set_byindex,index_param=index)--*/
+  virtual bool Set(int index,
+                   const CefRefPtr<CefV8Value> object,
+                   const CefRefPtr<CefV8Value> value,
+                   CefString& exception) = 0;
+};
+
+///
+// Class representing a V8 exception. The methods of this class may be called on
+// any render process thread.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefV8Exception : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the exception message.
+  ///
+  /*--cef()--*/
+  virtual CefString GetMessage() = 0;
+
+  ///
+  // Returns the line of source code that the exception occurred within.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSourceLine() = 0;
+
+  ///
+  // Returns the resource name for the script from where the function causing
+  // the error originates.
+  ///
+  /*--cef()--*/
+  virtual CefString GetScriptResourceName() = 0;
+
+  ///
+  // Returns the 1-based number of the line where the error occurred or 0 if the
+  // line number is unknown.
+  ///
+  /*--cef()--*/
+  virtual int GetLineNumber() = 0;
+
+  ///
+  // Returns the index within the script of the first character where the error
+  // occurred.
+  ///
+  /*--cef()--*/
+  virtual int GetStartPosition() = 0;
+
+  ///
+  // Returns the index within the script of the last character where the error
+  // occurred.
+  ///
+  /*--cef()--*/
+  virtual int GetEndPosition() = 0;
+
+  ///
+  // Returns the index within the line of the first character where the error
+  // occurred.
+  ///
+  /*--cef()--*/
+  virtual int GetStartColumn() = 0;
+
+  ///
+  // Returns the index within the line of the last character where the error
+  // occurred.
+  ///
+  /*--cef()--*/
+  virtual int GetEndColumn() = 0;
+};
+
+///
+// Callback interface that is passed to CefV8Value::CreateArrayBuffer.
+///
+/*--cef(source=client,no_debugct_check)--*/
+class CefV8ArrayBufferReleaseCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Called to release |buffer| when the ArrayBuffer JS object is garbage
+  // collected. |buffer| is the value that was passed to CreateArrayBuffer along
+  // with this object.
+  ///
+  /*--cef()--*/
+  virtual void ReleaseBuffer(void* buffer) = 0;
+};
+
+///
+// Class representing a V8 value handle. V8 handles can only be accessed from
+// the thread on which they are created. Valid threads for creating a V8 handle
+// include the render process main thread (TID_RENDERER) and WebWorker threads.
+// A task runner for posting tasks on the associated thread can be retrieved via
+// the CefV8Context::GetTaskRunner() method.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefV8Value : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_v8_accesscontrol_t AccessControl;
+  typedef cef_v8_propertyattribute_t PropertyAttribute;
+
+  ///
+  // Create a new CefV8Value object of type undefined.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateUndefined();
+
+  ///
+  // Create a new CefV8Value object of type null.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateNull();
+
+  ///
+  // Create a new CefV8Value object of type bool.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateBool(bool value);
+
+  ///
+  // Create a new CefV8Value object of type int.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateInt(int32 value);
+
+  ///
+  // Create a new CefV8Value object of type unsigned int.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateUInt(uint32 value);
+
+  ///
+  // Create a new CefV8Value object of type double.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateDouble(double value);
+
+  ///
+  // Create a new CefV8Value object of type Date. This method should only be
+  // called from within the scope of a CefRenderProcessHandler, CefV8Handler or
+  // CefV8Accessor callback, or in combination with calling Enter() and Exit()
+  // on a stored CefV8Context reference.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateDate(const CefTime& date);
+
+  ///
+  // Create a new CefV8Value object of type string.
+  ///
+  /*--cef(optional_param=value)--*/
+  static CefRefPtr<CefV8Value> CreateString(const CefString& value);
+
+  ///
+  // Create a new CefV8Value object of type object with optional accessor and/or
+  // interceptor. This method should only be called from within the scope of a
+  // CefRenderProcessHandler, CefV8Handler or CefV8Accessor callback, or in
+  // combination with calling Enter() and Exit() on a stored CefV8Context
+  // reference.
+  ///
+  /*--cef(optional_param=accessor, optional_param=interceptor)--*/
+  static CefRefPtr<CefV8Value> CreateObject(
+      CefRefPtr<CefV8Accessor> accessor,
+      CefRefPtr<CefV8Interceptor> interceptor);
+
+  ///
+  // Create a new CefV8Value object of type array with the specified |length|.
+  // If |length| is negative the returned array will have length 0. This method
+  // should only be called from within the scope of a CefRenderProcessHandler,
+  // CefV8Handler or CefV8Accessor callback, or in combination with calling
+  // Enter() and Exit() on a stored CefV8Context reference.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateArray(int length);
+
+  ///
+  // Create a new CefV8Value object of type ArrayBuffer which wraps the provided
+  // |buffer| of size |length| bytes. The ArrayBuffer is externalized, meaning
+  // that it does not own |buffer|. The caller is responsible for freeing
+  // |buffer| when requested via a call to CefV8ArrayBufferReleaseCallback::
+  // ReleaseBuffer. This method should only be called from within the scope of a
+  // CefRenderProcessHandler, CefV8Handler or CefV8Accessor callback, or in
+  // combination with calling Enter() and Exit() on a stored CefV8Context
+  // reference.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateArrayBuffer(
+      void* buffer,
+      size_t length,
+      CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback);
+
+  ///
+  // Create a new CefV8Value object of type function. This method should only be
+  // called from within the scope of a CefRenderProcessHandler, CefV8Handler or
+  // CefV8Accessor callback, or in combination with calling Enter() and Exit()
+  // on a stored CefV8Context reference.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8Value> CreateFunction(const CefString& name,
+                                              CefRefPtr<CefV8Handler> handler);
+
+  ///
+  // Returns true if the underlying handle is valid and it can be accessed on
+  // the current thread. Do not call any other methods if this method returns
+  // false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // True if the value type is undefined.
+  ///
+  /*--cef()--*/
+  virtual bool IsUndefined() = 0;
+
+  ///
+  // True if the value type is null.
+  ///
+  /*--cef()--*/
+  virtual bool IsNull() = 0;
+
+  ///
+  // True if the value type is bool.
+  ///
+  /*--cef()--*/
+  virtual bool IsBool() = 0;
+
+  ///
+  // True if the value type is int.
+  ///
+  /*--cef()--*/
+  virtual bool IsInt() = 0;
+
+  ///
+  // True if the value type is unsigned int.
+  ///
+  /*--cef()--*/
+  virtual bool IsUInt() = 0;
+
+  ///
+  // True if the value type is double.
+  ///
+  /*--cef()--*/
+  virtual bool IsDouble() = 0;
+
+  ///
+  // True if the value type is Date.
+  ///
+  /*--cef()--*/
+  virtual bool IsDate() = 0;
+
+  ///
+  // True if the value type is string.
+  ///
+  /*--cef()--*/
+  virtual bool IsString() = 0;
+
+  ///
+  // True if the value type is object.
+  ///
+  /*--cef()--*/
+  virtual bool IsObject() = 0;
+
+  ///
+  // True if the value type is array.
+  ///
+  /*--cef()--*/
+  virtual bool IsArray() = 0;
+
+  ///
+  // True if the value type is an ArrayBuffer.
+  ///
+  /*--cef()--*/
+  virtual bool IsArrayBuffer() = 0;
+
+  ///
+  // True if the value type is function.
+  ///
+  /*--cef()--*/
+  virtual bool IsFunction() = 0;
+
+  ///
+  // Returns true if this object is pointing to the same handle as |that|
+  // object.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefV8Value> that) = 0;
+
+  ///
+  // Return a bool value.
+  ///
+  /*--cef()--*/
+  virtual bool GetBoolValue() = 0;
+
+  ///
+  // Return an int value.
+  ///
+  /*--cef()--*/
+  virtual int32 GetIntValue() = 0;
+
+  ///
+  // Return an unsigned int value.
+  ///
+  /*--cef()--*/
+  virtual uint32 GetUIntValue() = 0;
+
+  ///
+  // Return a double value.
+  ///
+  /*--cef()--*/
+  virtual double GetDoubleValue() = 0;
+
+  ///
+  // Return a Date value.
+  ///
+  /*--cef()--*/
+  virtual CefTime GetDateValue() = 0;
+
+  ///
+  // Return a string value.
+  ///
+  /*--cef()--*/
+  virtual CefString GetStringValue() = 0;
+
+  // OBJECT METHODS - These methods are only available on objects. Arrays and
+  // functions are also objects. String- and integer-based keys can be used
+  // interchangably with the framework converting between them as necessary.
+
+  ///
+  // Returns true if this is a user created object.
+  ///
+  /*--cef()--*/
+  virtual bool IsUserCreated() = 0;
+
+  ///
+  // Returns true if the last method call resulted in an exception. This
+  // attribute exists only in the scope of the current CEF value object.
+  ///
+  /*--cef()--*/
+  virtual bool HasException() = 0;
+
+  ///
+  // Returns the exception resulting from the last method call. This attribute
+  // exists only in the scope of the current CEF value object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefV8Exception> GetException() = 0;
+
+  ///
+  // Clears the last exception and returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool ClearException() = 0;
+
+  ///
+  // Returns true if this object will re-throw future exceptions. This attribute
+  // exists only in the scope of the current CEF value object.
+  ///
+  /*--cef()--*/
+  virtual bool WillRethrowExceptions() = 0;
+
+  ///
+  // Set whether this object will re-throw future exceptions. By default
+  // exceptions are not re-thrown. If a exception is re-thrown the current
+  // context should not be accessed again until after the exception has been
+  // caught and not re-thrown. Returns true on success. This attribute exists
+  // only in the scope of the current CEF value object.
+  ///
+  /*--cef()--*/
+  virtual bool SetRethrowExceptions(bool rethrow) = 0;
+
+  ///
+  // Returns true if the object has a value with the specified identifier.
+  ///
+  /*--cef(capi_name=has_value_bykey,optional_param=key)--*/
+  virtual bool HasValue(const CefString& key) = 0;
+
+  ///
+  // Returns true if the object has a value with the specified identifier.
+  ///
+  /*--cef(capi_name=has_value_byindex,index_param=index)--*/
+  virtual bool HasValue(int index) = 0;
+
+  ///
+  // Deletes the value with the specified identifier and returns true on
+  // success. Returns false if this method is called incorrectly or an exception
+  // is thrown. For read-only and don't-delete values this method will return
+  // true even though deletion failed.
+  ///
+  /*--cef(capi_name=delete_value_bykey,optional_param=key)--*/
+  virtual bool DeleteValue(const CefString& key) = 0;
+
+  ///
+  // Deletes the value with the specified identifier and returns true on
+  // success. Returns false if this method is called incorrectly, deletion fails
+  // or an exception is thrown. For read-only and don't-delete values this
+  // method will return true even though deletion failed.
+  ///
+  /*--cef(capi_name=delete_value_byindex,index_param=index)--*/
+  virtual bool DeleteValue(int index) = 0;
+
+  ///
+  // Returns the value with the specified identifier on success. Returns NULL
+  // if this method is called incorrectly or an exception is thrown.
+  ///
+  /*--cef(capi_name=get_value_bykey,optional_param=key)--*/
+  virtual CefRefPtr<CefV8Value> GetValue(const CefString& key) = 0;
+
+  ///
+  // Returns the value with the specified identifier on success. Returns NULL
+  // if this method is called incorrectly or an exception is thrown.
+  ///
+  /*--cef(capi_name=get_value_byindex,index_param=index)--*/
+  virtual CefRefPtr<CefV8Value> GetValue(int index) = 0;
+
+  ///
+  // Associates a value with the specified identifier and returns true on
+  // success. Returns false if this method is called incorrectly or an exception
+  // is thrown. For read-only values this method will return true even though
+  // assignment failed.
+  ///
+  /*--cef(capi_name=set_value_bykey,optional_param=key)--*/
+  virtual bool SetValue(const CefString& key,
+                        CefRefPtr<CefV8Value> value,
+                        PropertyAttribute attribute) = 0;
+
+  ///
+  // Associates a value with the specified identifier and returns true on
+  // success. Returns false if this method is called incorrectly or an exception
+  // is thrown. For read-only values this method will return true even though
+  // assignment failed.
+  ///
+  /*--cef(capi_name=set_value_byindex,index_param=index)--*/
+  virtual bool SetValue(int index, CefRefPtr<CefV8Value> value) = 0;
+
+  ///
+  // Registers an identifier and returns true on success. Access to the
+  // identifier will be forwarded to the CefV8Accessor instance passed to
+  // CefV8Value::CreateObject(). Returns false if this method is called
+  // incorrectly or an exception is thrown. For read-only values this method
+  // will return true even though assignment failed.
+  ///
+  /*--cef(capi_name=set_value_byaccessor,optional_param=key)--*/
+  virtual bool SetValue(const CefString& key,
+                        AccessControl settings,
+                        PropertyAttribute attribute) = 0;
+
+  ///
+  // Read the keys for the object's values into the specified vector. Integer-
+  // based keys will also be returned as strings.
+  ///
+  /*--cef()--*/
+  virtual bool GetKeys(std::vector<CefString>& keys) = 0;
+
+  ///
+  // Sets the user data for this object and returns true on success. Returns
+  // false if this method is called incorrectly. This method can only be called
+  // on user created objects.
+  ///
+  /*--cef(optional_param=user_data)--*/
+  virtual bool SetUserData(CefRefPtr<CefBaseRefCounted> user_data) = 0;
+
+  ///
+  // Returns the user data, if any, assigned to this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBaseRefCounted> GetUserData() = 0;
+
+  ///
+  // Returns the amount of externally allocated memory registered for the
+  // object.
+  ///
+  /*--cef()--*/
+  virtual int GetExternallyAllocatedMemory() = 0;
+
+  ///
+  // Adjusts the amount of registered external memory for the object. Used to
+  // give V8 an indication of the amount of externally allocated memory that is
+  // kept alive by JavaScript objects. V8 uses this information to decide when
+  // to perform global garbage collection. Each CefV8Value tracks the amount of
+  // external memory associated with it and automatically decreases the global
+  // total by the appropriate amount on its destruction. |change_in_bytes|
+  // specifies the number of bytes to adjust by. This method returns the number
+  // of bytes associated with the object after the adjustment. This method can
+  // only be called on user created objects.
+  ///
+  /*--cef()--*/
+  virtual int AdjustExternallyAllocatedMemory(int change_in_bytes) = 0;
+
+  // ARRAY METHODS - These methods are only available on arrays.
+
+  ///
+  // Returns the number of elements in the array.
+  ///
+  /*--cef()--*/
+  virtual int GetArrayLength() = 0;
+
+  // ARRAY BUFFER METHODS - These methods are only available on ArrayBuffers.
+
+  ///
+  // Returns the ReleaseCallback object associated with the ArrayBuffer or NULL
+  // if the ArrayBuffer was not created with CreateArrayBuffer.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefV8ArrayBufferReleaseCallback>
+  GetArrayBufferReleaseCallback() = 0;
+
+  ///
+  // Prevent the ArrayBuffer from using it's memory block by setting the length
+  // to zero. This operation cannot be undone. If the ArrayBuffer was created
+  // with CreateArrayBuffer then CefV8ArrayBufferReleaseCallback::ReleaseBuffer
+  // will be called to release the underlying buffer.
+  ///
+  /*--cef()--*/
+  virtual bool NeuterArrayBuffer() = 0;
+
+  // FUNCTION METHODS - These methods are only available on functions.
+
+  ///
+  // Returns the function name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFunctionName() = 0;
+
+  ///
+  // Returns the function handler or NULL if not a CEF-created function.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefV8Handler> GetFunctionHandler() = 0;
+
+  ///
+  // Execute the function using the current V8 context. This method should only
+  // be called from within the scope of a CefV8Handler or CefV8Accessor
+  // callback, or in combination with calling Enter() and Exit() on a stored
+  // CefV8Context reference. |object| is the receiver ('this' object) of the
+  // function. If |object| is empty the current context's global object will be
+  // used. |arguments| is the list of arguments that will be passed to the
+  // function. Returns the function return value on success. Returns NULL if
+  // this method is called incorrectly or an exception is thrown.
+  ///
+  /*--cef(optional_param=object)--*/
+  virtual CefRefPtr<CefV8Value> ExecuteFunction(
+      CefRefPtr<CefV8Value> object,
+      const CefV8ValueList& arguments) = 0;
+
+  ///
+  // Execute the function using the specified V8 context. |object| is the
+  // receiver ('this' object) of the function. If |object| is empty the
+  // specified context's global object will be used. |arguments| is the list of
+  // arguments that will be passed to the function. Returns the function return
+  // value on success. Returns NULL if this method is called incorrectly or an
+  // exception is thrown.
+  ///
+  /*--cef(optional_param=object)--*/
+  virtual CefRefPtr<CefV8Value> ExecuteFunctionWithContext(
+      CefRefPtr<CefV8Context> context,
+      CefRefPtr<CefV8Value> object,
+      const CefV8ValueList& arguments) = 0;
+};
+
+///
+// Class representing a V8 stack trace handle. V8 handles can only be accessed
+// from the thread on which they are created. Valid threads for creating a V8
+// handle include the render process main thread (TID_RENDERER) and WebWorker
+// threads. A task runner for posting tasks on the associated thread can be
+// retrieved via the CefV8Context::GetTaskRunner() method.
+///
+/*--cef(source=library)--*/
+class CefV8StackTrace : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the stack trace for the currently active context. |frame_limit| is
+  // the maximum number of frames that will be captured.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefV8StackTrace> GetCurrent(int frame_limit);
+
+  ///
+  // Returns true if the underlying handle is valid and it can be accessed on
+  // the current thread. Do not call any other methods if this method returns
+  // false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns the number of stack frames.
+  ///
+  /*--cef()--*/
+  virtual int GetFrameCount() = 0;
+
+  ///
+  // Returns the stack frame at the specified 0-based index.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefV8StackFrame> GetFrame(int index) = 0;
+};
+
+///
+// Class representing a V8 stack frame handle. V8 handles can only be accessed
+// from the thread on which they are created. Valid threads for creating a V8
+// handle include the render process main thread (TID_RENDERER) and WebWorker
+// threads. A task runner for posting tasks on the associated thread can be
+// retrieved via the CefV8Context::GetTaskRunner() method.
+///
+/*--cef(source=library,no_debugct_check)--*/
+class CefV8StackFrame : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns true if the underlying handle is valid and it can be accessed on
+  // the current thread. Do not call any other methods if this method returns
+  // false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns the name of the resource script that contains the function.
+  ///
+  /*--cef()--*/
+  virtual CefString GetScriptName() = 0;
+
+  ///
+  // Returns the name of the resource script that contains the function or the
+  // sourceURL value if the script name is undefined and its source ends with
+  // a "//@ sourceURL=..." string.
+  ///
+  /*--cef()--*/
+  virtual CefString GetScriptNameOrSourceURL() = 0;
+
+  ///
+  // Returns the name of the function.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFunctionName() = 0;
+
+  ///
+  // Returns the 1-based line number for the function call or 0 if unknown.
+  ///
+  /*--cef()--*/
+  virtual int GetLineNumber() = 0;
+
+  ///
+  // Returns the 1-based column offset on the line for the function call or 0 if
+  // unknown.
+  ///
+  /*--cef()--*/
+  virtual int GetColumn() = 0;
+
+  ///
+  // Returns true if the function was compiled using eval().
+  ///
+  /*--cef()--*/
+  virtual bool IsEval() = 0;
+
+  ///
+  // Returns true if the function was called as a constructor via "new".
+  ///
+  /*--cef()--*/
+  virtual bool IsConstructor() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_V8_H_
diff --git a/src/include/cef_values.h b/src/include/cef_values.h
new file mode 100644
index 0000000..be88794
--- /dev/null
+++ b/src/include/cef_values.h
@@ -0,0 +1,748 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_VALUES_H_
+#define CEF_INCLUDE_CEF_VALUES_H_
+#pragma once
+
+#include <vector>
+#include "include/cef_base.h"
+
+class CefBinaryValue;
+class CefDictionaryValue;
+class CefListValue;
+
+typedef cef_value_type_t CefValueType;
+
+///
+// Class that wraps other data value types. Complex types (binary, dictionary
+// and list) will be referenced but not owned by this object. Can be used on any
+// process and thread.
+///
+/*--cef(source=library)--*/
+class CefValue : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Creates a new object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefValue> Create();
+
+  ///
+  // Returns true if the underlying data is valid. This will always be true for
+  // simple types. For complex types (binary, dictionary and list) the
+  // underlying data may become invalid if owned by another object (e.g. list or
+  // dictionary) and that other object is then modified or destroyed. This value
+  // object can be re-used by calling Set*() even if the underlying data is
+  // invalid.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if the underlying data is owned by another object.
+  ///
+  /*--cef()--*/
+  virtual bool IsOwned() = 0;
+
+  ///
+  // Returns true if the underlying data is read-only. Some APIs may expose
+  // read-only objects.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns true if this object and |that| object have the same underlying
+  // data. If true modifications to this object will also affect |that| object
+  // and vice-versa.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefValue> that) = 0;
+
+  ///
+  // Returns true if this object and |that| object have an equivalent underlying
+  // value but are not necessarily the same object.
+  ///
+  /*--cef()--*/
+  virtual bool IsEqual(CefRefPtr<CefValue> that) = 0;
+
+  ///
+  // Returns a copy of this object. The underlying data will also be copied.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefValue> Copy() = 0;
+
+  ///
+  // Returns the underlying value type.
+  ///
+  /*--cef(default_retval=VTYPE_INVALID)--*/
+  virtual CefValueType GetType() = 0;
+
+  ///
+  // Returns the underlying value as type bool.
+  ///
+  /*--cef()--*/
+  virtual bool GetBool() = 0;
+
+  ///
+  // Returns the underlying value as type int.
+  ///
+  /*--cef()--*/
+  virtual int GetInt() = 0;
+
+  ///
+  // Returns the underlying value as type double.
+  ///
+  /*--cef()--*/
+  virtual double GetDouble() = 0;
+
+  ///
+  // Returns the underlying value as type string.
+  ///
+  /*--cef()--*/
+  virtual CefString GetString() = 0;
+
+  ///
+  // Returns the underlying value as type binary. The returned reference may
+  // become invalid if the value is owned by another object or if ownership is
+  // transferred to another object in the future. To maintain a reference to
+  // the value after assigning ownership to a dictionary or list pass this
+  // object to the SetValue() method instead of passing the returned reference
+  // to SetBinary().
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetBinary() = 0;
+
+  ///
+  // Returns the underlying value as type dictionary. The returned reference may
+  // become invalid if the value is owned by another object or if ownership is
+  // transferred to another object in the future. To maintain a reference to
+  // the value after assigning ownership to a dictionary or list pass this
+  // object to the SetValue() method instead of passing the returned reference
+  // to SetDictionary().
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDictionaryValue> GetDictionary() = 0;
+
+  ///
+  // Returns the underlying value as type list. The returned reference may
+  // become invalid if the value is owned by another object or if ownership is
+  // transferred to another object in the future. To maintain a reference to
+  // the value after assigning ownership to a dictionary or list pass this
+  // object to the SetValue() method instead of passing the returned reference
+  // to SetList().
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefListValue> GetList() = 0;
+
+  ///
+  // Sets the underlying value as type null. Returns true if the value was set
+  // successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetNull() = 0;
+
+  ///
+  // Sets the underlying value as type bool. Returns true if the value was set
+  // successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetBool(bool value) = 0;
+
+  ///
+  // Sets the underlying value as type int. Returns true if the value was set
+  // successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetInt(int value) = 0;
+
+  ///
+  // Sets the underlying value as type double. Returns true if the value was set
+  // successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetDouble(double value) = 0;
+
+  ///
+  // Sets the underlying value as type string. Returns true if the value was set
+  // successfully.
+  ///
+  /*--cef(optional_param=value)--*/
+  virtual bool SetString(const CefString& value) = 0;
+
+  ///
+  // Sets the underlying value as type binary. Returns true if the value was set
+  // successfully. This object keeps a reference to |value| and ownership of the
+  // underlying data remains unchanged.
+  ///
+  /*--cef()--*/
+  virtual bool SetBinary(CefRefPtr<CefBinaryValue> value) = 0;
+
+  ///
+  // Sets the underlying value as type dict. Returns true if the value was set
+  // successfully. This object keeps a reference to |value| and ownership of the
+  // underlying data remains unchanged.
+  ///
+  /*--cef()--*/
+  virtual bool SetDictionary(CefRefPtr<CefDictionaryValue> value) = 0;
+
+  ///
+  // Sets the underlying value as type list. Returns true if the value was set
+  // successfully. This object keeps a reference to |value| and ownership of the
+  // underlying data remains unchanged.
+  ///
+  /*--cef()--*/
+  virtual bool SetList(CefRefPtr<CefListValue> value) = 0;
+};
+
+///
+// Class representing a binary value. Can be used on any process and thread.
+///
+/*--cef(source=library)--*/
+class CefBinaryValue : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Creates a new object that is not owned by any other object. The specified
+  // |data| will be copied.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefBinaryValue> Create(const void* data, size_t data_size);
+
+  ///
+  // Returns true if this object is valid. This object may become invalid if
+  // the underlying data is owned by another object (e.g. list or dictionary)
+  // and that other object is then modified or destroyed. Do not call any other
+  // methods if this method returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if this object is currently owned by another object.
+  ///
+  /*--cef()--*/
+  virtual bool IsOwned() = 0;
+
+  ///
+  // Returns true if this object and |that| object have the same underlying
+  // data.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefBinaryValue> that) = 0;
+
+  ///
+  // Returns true if this object and |that| object have an equivalent underlying
+  // value but are not necessarily the same object.
+  ///
+  /*--cef()--*/
+  virtual bool IsEqual(CefRefPtr<CefBinaryValue> that) = 0;
+
+  ///
+  // Returns a copy of this object. The data in this object will also be copied.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> Copy() = 0;
+
+  ///
+  // Returns the data size.
+  ///
+  /*--cef()--*/
+  virtual size_t GetSize() = 0;
+
+  ///
+  // Read up to |buffer_size| number of bytes into |buffer|. Reading begins at
+  // the specified byte |data_offset|. Returns the number of bytes read.
+  ///
+  /*--cef()--*/
+  virtual size_t GetData(void* buffer,
+                         size_t buffer_size,
+                         size_t data_offset) = 0;
+};
+
+///
+// Class representing a dictionary value. Can be used on any process and thread.
+///
+/*--cef(source=library)--*/
+class CefDictionaryValue : public virtual CefBaseRefCounted {
+ public:
+  typedef std::vector<CefString> KeyList;
+
+  ///
+  // Creates a new object that is not owned by any other object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefDictionaryValue> Create();
+
+  ///
+  // Returns true if this object is valid. This object may become invalid if
+  // the underlying data is owned by another object (e.g. list or dictionary)
+  // and that other object is then modified or destroyed. Do not call any other
+  // methods if this method returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if this object is currently owned by another object.
+  ///
+  /*--cef()--*/
+  virtual bool IsOwned() = 0;
+
+  ///
+  // Returns true if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns true if this object and |that| object have the same underlying
+  // data. If true modifications to this object will also affect |that| object
+  // and vice-versa.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefDictionaryValue> that) = 0;
+
+  ///
+  // Returns true if this object and |that| object have an equivalent underlying
+  // value but are not necessarily the same object.
+  ///
+  /*--cef()--*/
+  virtual bool IsEqual(CefRefPtr<CefDictionaryValue> that) = 0;
+
+  ///
+  // Returns a writable copy of this object. If |exclude_empty_children| is true
+  // any empty dictionaries or lists will be excluded from the copy.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDictionaryValue> Copy(bool exclude_empty_children) = 0;
+
+  ///
+  // Returns the number of values.
+  ///
+  /*--cef()--*/
+  virtual size_t GetSize() = 0;
+
+  ///
+  // Removes all values. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool Clear() = 0;
+
+  ///
+  // Returns true if the current dictionary has a value for the given key.
+  ///
+  /*--cef()--*/
+  virtual bool HasKey(const CefString& key) = 0;
+
+  ///
+  // Reads all keys for this dictionary into the specified vector.
+  ///
+  /*--cef()--*/
+  virtual bool GetKeys(KeyList& keys) = 0;
+
+  ///
+  // Removes the value at the specified key. Returns true is the value was
+  // removed successfully.
+  ///
+  /*--cef()--*/
+  virtual bool Remove(const CefString& key) = 0;
+
+  ///
+  // Returns the value type for the specified key.
+  ///
+  /*--cef(default_retval=VTYPE_INVALID)--*/
+  virtual CefValueType GetType(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key. For simple types the returned
+  // value will copy existing data and modifications to the value will not
+  // modify this object. For complex types (binary, dictionary and list) the
+  // returned value will reference existing data and modifications to the value
+  // will modify this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefValue> GetValue(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key as type bool.
+  ///
+  /*--cef()--*/
+  virtual bool GetBool(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key as type int.
+  ///
+  /*--cef()--*/
+  virtual int GetInt(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key as type double.
+  ///
+  /*--cef()--*/
+  virtual double GetDouble(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key as type string.
+  ///
+  /*--cef()--*/
+  virtual CefString GetString(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key as type binary. The returned
+  // value will reference existing data.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetBinary(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key as type dictionary. The returned
+  // value will reference existing data and modifications to the value will
+  // modify this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDictionaryValue> GetDictionary(const CefString& key) = 0;
+
+  ///
+  // Returns the value at the specified key as type list. The returned value
+  // will reference existing data and modifications to the value will modify
+  // this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefListValue> GetList(const CefString& key) = 0;
+
+  ///
+  // Sets the value at the specified key. Returns true if the value was set
+  // successfully. If |value| represents simple data then the underlying data
+  // will be copied and modifications to |value| will not modify this object. If
+  // |value| represents complex data (binary, dictionary or list) then the
+  // underlying data will be referenced and modifications to |value| will modify
+  // this object.
+  ///
+  /*--cef()--*/
+  virtual bool SetValue(const CefString& key, CefRefPtr<CefValue> value) = 0;
+
+  ///
+  // Sets the value at the specified key as type null. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetNull(const CefString& key) = 0;
+
+  ///
+  // Sets the value at the specified key as type bool. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetBool(const CefString& key, bool value) = 0;
+
+  ///
+  // Sets the value at the specified key as type int. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetInt(const CefString& key, int value) = 0;
+
+  ///
+  // Sets the value at the specified key as type double. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetDouble(const CefString& key, double value) = 0;
+
+  ///
+  // Sets the value at the specified key as type string. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef(optional_param=value)--*/
+  virtual bool SetString(const CefString& key, const CefString& value) = 0;
+
+  ///
+  // Sets the value at the specified key as type binary. Returns true if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  /*--cef()--*/
+  virtual bool SetBinary(const CefString& key,
+                         CefRefPtr<CefBinaryValue> value) = 0;
+
+  ///
+  // Sets the value at the specified key as type dict. Returns true if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  /*--cef()--*/
+  virtual bool SetDictionary(const CefString& key,
+                             CefRefPtr<CefDictionaryValue> value) = 0;
+
+  ///
+  // Sets the value at the specified key as type list. Returns true if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  /*--cef()--*/
+  virtual bool SetList(const CefString& key, CefRefPtr<CefListValue> value) = 0;
+};
+
+///
+// Class representing a list value. Can be used on any process and thread.
+///
+/*--cef(source=library)--*/
+class CefListValue : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Creates a new object that is not owned by any other object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefListValue> Create();
+
+  ///
+  // Returns true if this object is valid. This object may become invalid if
+  // the underlying data is owned by another object (e.g. list or dictionary)
+  // and that other object is then modified or destroyed. Do not call any other
+  // methods if this method returns false.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if this object is currently owned by another object.
+  ///
+  /*--cef()--*/
+  virtual bool IsOwned() = 0;
+
+  ///
+  // Returns true if the values of this object are read-only. Some APIs may
+  // expose read-only objects.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns true if this object and |that| object have the same underlying
+  // data. If true modifications to this object will also affect |that| object
+  // and vice-versa.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefListValue> that) = 0;
+
+  ///
+  // Returns true if this object and |that| object have an equivalent underlying
+  // value but are not necessarily the same object.
+  ///
+  /*--cef()--*/
+  virtual bool IsEqual(CefRefPtr<CefListValue> that) = 0;
+
+  ///
+  // Returns a writable copy of this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefListValue> Copy() = 0;
+
+  ///
+  // Sets the number of values. If the number of values is expanded all
+  // new value slots will default to type null. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool SetSize(size_t size) = 0;
+
+  ///
+  // Returns the number of values.
+  ///
+  /*--cef()--*/
+  virtual size_t GetSize() = 0;
+
+  ///
+  // Removes all values. Returns true on success.
+  ///
+  /*--cef()--*/
+  virtual bool Clear() = 0;
+
+  ///
+  // Removes the value at the specified index.
+  ///
+  /*--cef()--*/
+  virtual bool Remove(size_t index) = 0;
+
+  ///
+  // Returns the value type at the specified index.
+  ///
+  /*--cef(default_retval=VTYPE_INVALID)--*/
+  virtual CefValueType GetType(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index. For simple types the returned
+  // value will copy existing data and modifications to the value will not
+  // modify this object. For complex types (binary, dictionary and list) the
+  // returned value will reference existing data and modifications to the value
+  // will modify this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefValue> GetValue(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index as type bool.
+  ///
+  /*--cef()--*/
+  virtual bool GetBool(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index as type int.
+  ///
+  /*--cef()--*/
+  virtual int GetInt(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index as type double.
+  ///
+  /*--cef()--*/
+  virtual double GetDouble(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index as type string.
+  ///
+  /*--cef()--*/
+  virtual CefString GetString(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index as type binary. The returned
+  // value will reference existing data.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetBinary(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index as type dictionary. The returned
+  // value will reference existing data and modifications to the value will
+  // modify this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDictionaryValue> GetDictionary(size_t index) = 0;
+
+  ///
+  // Returns the value at the specified index as type list. The returned
+  // value will reference existing data and modifications to the value will
+  // modify this object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefListValue> GetList(size_t index) = 0;
+
+  ///
+  // Sets the value at the specified index. Returns true if the value was set
+  // successfully. If |value| represents simple data then the underlying data
+  // will be copied and modifications to |value| will not modify this object. If
+  // |value| represents complex data (binary, dictionary or list) then the
+  // underlying data will be referenced and modifications to |value| will modify
+  // this object.
+  ///
+  /*--cef()--*/
+  virtual bool SetValue(size_t index, CefRefPtr<CefValue> value) = 0;
+
+  ///
+  // Sets the value at the specified index as type null. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetNull(size_t index) = 0;
+
+  ///
+  // Sets the value at the specified index as type bool. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetBool(size_t index, bool value) = 0;
+
+  ///
+  // Sets the value at the specified index as type int. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetInt(size_t index, int value) = 0;
+
+  ///
+  // Sets the value at the specified index as type double. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool SetDouble(size_t index, double value) = 0;
+
+  ///
+  // Sets the value at the specified index as type string. Returns true if the
+  // value was set successfully.
+  ///
+  /*--cef(optional_param=value)--*/
+  virtual bool SetString(size_t index, const CefString& value) = 0;
+
+  ///
+  // Sets the value at the specified index as type binary. Returns true if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  /*--cef()--*/
+  virtual bool SetBinary(size_t index, CefRefPtr<CefBinaryValue> value) = 0;
+
+  ///
+  // Sets the value at the specified index as type dict. Returns true if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  /*--cef()--*/
+  virtual bool SetDictionary(size_t index,
+                             CefRefPtr<CefDictionaryValue> value) = 0;
+
+  ///
+  // Sets the value at the specified index as type list. Returns true if the
+  // value was set successfully. If |value| is currently owned by another object
+  // then the value will be copied and the |value| reference will not change.
+  // Otherwise, ownership will be transferred to this object and the |value|
+  // reference will be invalidated.
+  ///
+  /*--cef()--*/
+  virtual bool SetList(size_t index, CefRefPtr<CefListValue> value) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_VALUES_H_
diff --git a/src/include/cef_waitable_event.h b/src/include/cef_waitable_event.h
new file mode 100644
index 0000000..d89371e
--- /dev/null
+++ b/src/include/cef_waitable_event.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_WAITABLE_EVENT_H_
+#define CEF_INCLUDE_CEF_WAITABLE_EVENT_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+///
+// WaitableEvent is a thread synchronization tool that allows one thread to wait
+// for another thread to finish some work. This is equivalent to using a
+// Lock+ConditionVariable to protect a simple boolean value. However, using
+// WaitableEvent in conjunction with a Lock to wait for a more complex state
+// change (e.g., for an item to be added to a queue) is not recommended. In that
+// case consider using a ConditionVariable instead of a WaitableEvent. It is
+// safe to create and/or signal a WaitableEvent from any thread. Blocking on a
+// WaitableEvent by calling the *Wait() methods is not allowed on the browser
+// process UI or IO threads.
+///
+/*--cef(source=library)--*/
+class CefWaitableEvent : public CefBaseRefCounted {
+ public:
+  ///
+  // Create a new waitable event. If |automatic_reset| is true then the event
+  // state is automatically reset to un-signaled after a single waiting thread
+  // has been released; otherwise, the state remains signaled until Reset() is
+  // called manually. If |initially_signaled| is true then the event will start
+  // in the signaled state.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefWaitableEvent> CreateWaitableEvent(
+      bool automatic_reset,
+      bool initially_signaled);
+
+  ///
+  // Put the event in the un-signaled state.
+  ///
+  /*--cef()--*/
+  virtual void Reset() = 0;
+
+  ///
+  // Put the event in the signaled state. This causes any thread blocked on Wait
+  // to be woken up.
+  ///
+  /*--cef()--*/
+  virtual void Signal() = 0;
+
+  ///
+  // Returns true if the event is in the signaled state, else false. If the
+  // event was created with |automatic_reset| set to true then calling this
+  // method will also cause a reset.
+  ///
+  /*--cef()--*/
+  virtual bool IsSignaled() = 0;
+
+  ///
+  // Wait indefinitely for the event to be signaled. This method will not return
+  // until after the call to Signal() has completed. This method cannot be
+  // called on the browser process UI or IO threads.
+  ///
+  /*--cef()--*/
+  virtual void Wait() = 0;
+
+  ///
+  // Wait up to |max_ms| milliseconds for the event to be signaled. Returns true
+  // if the event was signaled. A return value of false does not necessarily
+  // mean that |max_ms| was exceeded. This method will not return until after
+  // the call to Signal() has completed. This method cannot be called on the
+  // browser process UI or IO threads.
+  ///
+  /*--cef()--*/
+  virtual bool TimedWait(int64 max_ms) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_WAITABLE_EVENT_H_
diff --git a/src/include/cef_web_plugin.h b/src/include/cef_web_plugin.h
new file mode 100644
index 0000000..118788b
--- /dev/null
+++ b/src/include/cef_web_plugin.h
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_WEB_PLUGIN_H_
+#define CEF_INCLUDE_CEF_WEB_PLUGIN_H_
+
+#include "include/cef_base.h"
+
+class CefBrowser;
+
+///
+// Information about a specific web plugin.
+///
+/*--cef(source=library)--*/
+class CefWebPluginInfo : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns the plugin name (i.e. Flash).
+  ///
+  /*--cef()--*/
+  virtual CefString GetName() = 0;
+
+  ///
+  // Returns the plugin file path (DLL/bundle/library).
+  ///
+  /*--cef()--*/
+  virtual CefString GetPath() = 0;
+
+  ///
+  // Returns the version of the plugin (may be OS-specific).
+  ///
+  /*--cef()--*/
+  virtual CefString GetVersion() = 0;
+
+  ///
+  // Returns a description of the plugin from the version information.
+  ///
+  /*--cef()--*/
+  virtual CefString GetDescription() = 0;
+};
+
+///
+// Interface to implement for visiting web plugin information. The methods of
+// this class will be called on the browser process UI thread.
+///
+/*--cef(source=client)--*/
+class CefWebPluginInfoVisitor : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called once for each plugin. |count| is the 0-based
+  // index for the current plugin. |total| is the total number of plugins.
+  // Return false to stop visiting plugins. This method may never be called if
+  // no plugins are found.
+  ///
+  /*--cef()--*/
+  virtual bool Visit(CefRefPtr<CefWebPluginInfo> info,
+                     int count,
+                     int total) = 0;
+};
+
+///
+// Visit web plugin information. Can be called on any thread in the browser
+// process.
+///
+/*--cef()--*/
+void CefVisitWebPluginInfo(CefRefPtr<CefWebPluginInfoVisitor> visitor);
+
+///
+// Cause the plugin list to refresh the next time it is accessed regardless
+// of whether it has already been loaded. Can be called on any thread in the
+// browser process.
+///
+/*--cef()--*/
+void CefRefreshWebPlugins();
+
+///
+// Unregister an internal plugin. This may be undone the next time
+// CefRefreshWebPlugins() is called. Can be called on any thread in the browser
+// process.
+///
+/*--cef()--*/
+void CefUnregisterInternalWebPlugin(const CefString& path);
+
+///
+// Register a plugin crash. Can be called on any thread in the browser process
+// but will be executed on the IO thread.
+///
+/*--cef()--*/
+void CefRegisterWebPluginCrash(const CefString& path);
+
+///
+// Interface to implement for receiving unstable plugin information. The methods
+// of this class will be called on the browser process IO thread.
+///
+/*--cef(source=client)--*/
+class CefWebPluginUnstableCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called for the requested plugin. |unstable| will be
+  // true if the plugin has reached the crash count threshold of 3 times in 120
+  // seconds.
+  ///
+  /*--cef()--*/
+  virtual void IsUnstable(const CefString& path, bool unstable) = 0;
+};
+
+///
+// Query if a plugin is unstable. Can be called on any thread in the browser
+// process.
+///
+/*--cef()--*/
+void CefIsWebPluginUnstable(const CefString& path,
+                            CefRefPtr<CefWebPluginUnstableCallback> callback);
+
+///
+// Implement this interface to receive notification when CDM registration is
+// complete. The methods of this class will be called on the browser process
+// UI thread.
+///
+/*--cef(source=client)--*/
+class CefRegisterCdmCallback : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Method that will be called when CDM registration is complete. |result|
+  // will be CEF_CDM_REGISTRATION_ERROR_NONE if registration completed
+  // successfully. Otherwise, |result| and |error_message| will contain
+  // additional information about why registration failed.
+  ///
+  /*--cef(optional_param=error_message)--*/
+  virtual void OnCdmRegistrationComplete(cef_cdm_registration_error_t result,
+                                         const CefString& error_message) = 0;
+};
+
+///
+// Register the Widevine CDM plugin.
+//
+// The client application is responsible for downloading an appropriate
+// platform-specific CDM binary distribution from Google, extracting the
+// contents, and building the required directory structure on the local machine.
+// The CefBrowserHost::StartDownload method and CefZipArchive class can be used
+// to implement this functionality in CEF. Contact Google via
+// https://www.widevine.com/contact.html for details on CDM download.
+//
+// |path| is a directory that must contain the following files:
+//   1. manifest.json file from the CDM binary distribution (see below).
+//   2. widevinecdm file from the CDM binary distribution (e.g.
+//      widevinecdm.dll on on Windows, libwidevinecdm.dylib on OS X,
+//      libwidevinecdm.so on Linux).
+//
+// If any of these files are missing or if the manifest file has incorrect
+// contents the registration will fail and |callback| will receive a |result|
+// value of CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS.
+//
+// The manifest.json file must contain the following keys:
+//   A. "os": Supported OS (e.g. "mac", "win" or "linux").
+//   B. "arch": Supported architecture (e.g. "ia32" or "x64").
+//   C. "x-cdm-module-versions": Module API version (e.g. "4").
+//   D. "x-cdm-interface-versions": Interface API version (e.g. "8").
+//   E. "x-cdm-host-versions": Host API version (e.g. "8").
+//   F. "version": CDM version (e.g. "1.4.8.903").
+//   G. "x-cdm-codecs": List of supported codecs (e.g. "vp8,vp9.0,avc1").
+//
+// A through E are used to verify compatibility with the current Chromium
+// version. If the CDM is not compatible the registration will fail and
+// |callback| will receive a |result| value of
+// CEF_CDM_REGISTRATION_ERROR_INCOMPATIBLE.
+//
+// |callback| will be executed asynchronously once registration is complete.
+//
+// On Linux this function must be called before CefInitialize() and the
+// registration cannot be changed during runtime. If registration is not
+// supported at the time that CefRegisterWidevineCdm() is called then |callback|
+// will receive a |result| value of CEF_CDM_REGISTRATION_ERROR_NOT_SUPPORTED.
+///
+/*--cef(optional_param=callback)--*/
+void CefRegisterWidevineCdm(const CefString& path,
+                            CefRefPtr<CefRegisterCdmCallback> callback);
+
+#endif  // CEF_INCLUDE_CEF_WEB_PLUGIN_H_
diff --git a/src/include/cef_x509_certificate.h b/src/include/cef_x509_certificate.h
new file mode 100644
index 0000000..b466a6a
--- /dev/null
+++ b/src/include/cef_x509_certificate.h
@@ -0,0 +1,188 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_X509_CERTIFICATE_H_
+#define CEF_INCLUDE_CEF_X509_CERTIFICATE_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_base.h"
+#include "include/cef_values.h"
+
+///
+// Class representing the issuer or subject field of an X.509 certificate.
+///
+/*--cef(source=library)--*/
+class CefX509CertPrincipal : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Returns a name that can be used to represent the issuer. It tries in this
+  // order: Common Name (CN), Organization Name (O) and Organizational Unit
+  // Name (OU) and returns the first non-empty one found.
+  ///
+  /*--cef()--*/
+  virtual CefString GetDisplayName() = 0;
+
+  ///
+  // Returns the common name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetCommonName() = 0;
+
+  ///
+  // Returns the locality name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLocalityName() = 0;
+
+  ///
+  // Returns the state or province name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetStateOrProvinceName() = 0;
+
+  ///
+  // Returns the country name.
+  ///
+  /*--cef()--*/
+  virtual CefString GetCountryName() = 0;
+
+  ///
+  // Retrieve the list of street addresses.
+  ///
+  /*--cef()--*/
+  virtual void GetStreetAddresses(std::vector<CefString>& addresses) = 0;
+
+  ///
+  // Retrieve the list of organization names.
+  ///
+  /*--cef()--*/
+  virtual void GetOrganizationNames(std::vector<CefString>& names) = 0;
+
+  ///
+  // Retrieve the list of organization unit names.
+  ///
+  /*--cef()--*/
+  virtual void GetOrganizationUnitNames(std::vector<CefString>& names) = 0;
+
+  ///
+  // Retrieve the list of domain components.
+  ///
+  /*--cef()--*/
+  virtual void GetDomainComponents(std::vector<CefString>& components) = 0;
+};
+
+///
+// Class representing a X.509 certificate.
+///
+/*--cef(source=library)--*/
+class CefX509Certificate : public virtual CefBaseRefCounted {
+ public:
+  typedef std::vector<CefRefPtr<CefBinaryValue>> IssuerChainBinaryList;
+
+  ///
+  // Returns the subject of the X.509 certificate. For HTTPS server
+  // certificates this represents the web server.  The common name of the
+  // subject should match the host name of the web server.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefX509CertPrincipal> GetSubject() = 0;
+
+  ///
+  // Returns the issuer of the X.509 certificate.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefX509CertPrincipal> GetIssuer() = 0;
+
+  ///
+  // Returns the DER encoded serial number for the X.509 certificate. The value
+  // possibly includes a leading 00 byte.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetSerialNumber() = 0;
+
+  ///
+  // Returns the date before which the X.509 certificate is invalid.
+  // CefTime.GetTimeT() will return 0 if no date was specified.
+  ///
+  /*--cef()--*/
+  virtual CefTime GetValidStart() = 0;
+
+  ///
+  // Returns the date after which the X.509 certificate is invalid.
+  // CefTime.GetTimeT() will return 0 if no date was specified.
+  ///
+  /*--cef()--*/
+  virtual CefTime GetValidExpiry() = 0;
+
+  ///
+  // Returns the DER encoded data for the X.509 certificate.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetDEREncoded() = 0;
+
+  ///
+  // Returns the PEM encoded data for the X.509 certificate.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBinaryValue> GetPEMEncoded() = 0;
+
+  ///
+  // Returns the number of certificates in the issuer chain.
+  // If 0, the certificate is self-signed.
+  ///
+  /*--cef()--*/
+  virtual size_t GetIssuerChainSize() = 0;
+
+  ///
+  // Returns the DER encoded data for the certificate issuer chain.
+  // If we failed to encode a certificate in the chain it is still
+  // present in the array but is an empty string.
+  ///
+  /*--cef(count_func=chain:GetIssuerChainSize)--*/
+  virtual void GetDEREncodedIssuerChain(IssuerChainBinaryList& chain) = 0;
+
+  ///
+  // Returns the PEM encoded data for the certificate issuer chain.
+  // If we failed to encode a certificate in the chain it is still
+  // present in the array but is an empty string.
+  ///
+  /*--cef(count_func=chain:GetIssuerChainSize)--*/
+  virtual void GetPEMEncodedIssuerChain(IssuerChainBinaryList& chain) = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_X509_CERTIFICATE_H_
diff --git a/src/include/cef_xml_reader.h b/src/include/cef_xml_reader.h
new file mode 100644
index 0000000..5269ef5
--- /dev/null
+++ b/src/include/cef_xml_reader.h
@@ -0,0 +1,266 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_XML_READER_H_
+#define CEF_INCLUDE_CEF_XML_READER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "include/cef_stream.h"
+
+///
+// Class that supports the reading of XML data via the libxml streaming API.
+// The methods of this class should only be called on the thread that creates
+// the object.
+///
+/*--cef(source=library)--*/
+class CefXmlReader : public virtual CefBaseRefCounted {
+ public:
+  typedef cef_xml_encoding_type_t EncodingType;
+  typedef cef_xml_node_type_t NodeType;
+
+  ///
+  // Create a new CefXmlReader object. The returned object's methods can only
+  // be called from the thread that created the object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefXmlReader> Create(CefRefPtr<CefStreamReader> stream,
+                                        EncodingType encodingType,
+                                        const CefString& URI);
+
+  ///
+  // Moves the cursor to the next node in the document. This method must be
+  // called at least once to set the current cursor position. Returns true if
+  // the cursor position was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool MoveToNextNode() = 0;
+
+  ///
+  // Close the document. This should be called directly to ensure that cleanup
+  // occurs on the correct thread.
+  ///
+  /*--cef()--*/
+  virtual bool Close() = 0;
+
+  ///
+  // Returns true if an error has been reported by the XML parser.
+  ///
+  /*--cef()--*/
+  virtual bool HasError() = 0;
+
+  ///
+  // Returns the error string.
+  ///
+  /*--cef()--*/
+  virtual CefString GetError() = 0;
+
+  // The below methods retrieve data for the node at the current cursor
+  // position.
+
+  ///
+  // Returns the node type.
+  ///
+  /*--cef(default_retval=XML_NODE_UNSUPPORTED)--*/
+  virtual NodeType GetType() = 0;
+
+  ///
+  // Returns the node depth. Depth starts at 0 for the root node.
+  ///
+  /*--cef()--*/
+  virtual int GetDepth() = 0;
+
+  ///
+  // Returns the local name. See
+  // http://www.w3.org/TR/REC-xml-names/#NT-LocalPart for additional details.
+  ///
+  /*--cef()--*/
+  virtual CefString GetLocalName() = 0;
+
+  ///
+  // Returns the namespace prefix. See http://www.w3.org/TR/REC-xml-names/ for
+  // additional details.
+  ///
+  /*--cef()--*/
+  virtual CefString GetPrefix() = 0;
+
+  ///
+  // Returns the qualified name, equal to (Prefix:)LocalName. See
+  // http://www.w3.org/TR/REC-xml-names/#ns-qualnames for additional details.
+  ///
+  /*--cef()--*/
+  virtual CefString GetQualifiedName() = 0;
+
+  ///
+  // Returns the URI defining the namespace associated with the node. See
+  // http://www.w3.org/TR/REC-xml-names/ for additional details.
+  ///
+  /*--cef()--*/
+  virtual CefString GetNamespaceURI() = 0;
+
+  ///
+  // Returns the base URI of the node. See http://www.w3.org/TR/xmlbase/ for
+  // additional details.
+  ///
+  /*--cef()--*/
+  virtual CefString GetBaseURI() = 0;
+
+  ///
+  // Returns the xml:lang scope within which the node resides. See
+  // http://www.w3.org/TR/REC-xml/#sec-lang-tag for additional details.
+  ///
+  /*--cef()--*/
+  virtual CefString GetXmlLang() = 0;
+
+  ///
+  // Returns true if the node represents an empty element. <a/> is considered
+  // empty but <a></a> is not.
+  ///
+  /*--cef()--*/
+  virtual bool IsEmptyElement() = 0;
+
+  ///
+  // Returns true if the node has a text value.
+  ///
+  /*--cef()--*/
+  virtual bool HasValue() = 0;
+
+  ///
+  // Returns the text value.
+  ///
+  /*--cef()--*/
+  virtual CefString GetValue() = 0;
+
+  ///
+  // Returns true if the node has attributes.
+  ///
+  /*--cef()--*/
+  virtual bool HasAttributes() = 0;
+
+  ///
+  // Returns the number of attributes.
+  ///
+  /*--cef()--*/
+  virtual size_t GetAttributeCount() = 0;
+
+  ///
+  // Returns the value of the attribute at the specified 0-based index.
+  ///
+  /*--cef(capi_name=get_attribute_byindex,index_param=index)--*/
+  virtual CefString GetAttribute(int index) = 0;
+
+  ///
+  // Returns the value of the attribute with the specified qualified name.
+  ///
+  /*--cef(capi_name=get_attribute_byqname)--*/
+  virtual CefString GetAttribute(const CefString& qualifiedName) = 0;
+
+  ///
+  // Returns the value of the attribute with the specified local name and
+  // namespace URI.
+  ///
+  /*--cef(capi_name=get_attribute_bylname)--*/
+  virtual CefString GetAttribute(const CefString& localName,
+                                 const CefString& namespaceURI) = 0;
+
+  ///
+  // Returns an XML representation of the current node's children.
+  ///
+  /*--cef()--*/
+  virtual CefString GetInnerXml() = 0;
+
+  ///
+  // Returns an XML representation of the current node including its children.
+  ///
+  /*--cef()--*/
+  virtual CefString GetOuterXml() = 0;
+
+  ///
+  // Returns the line number for the current node.
+  ///
+  /*--cef()--*/
+  virtual int GetLineNumber() = 0;
+
+  // Attribute nodes are not traversed by default. The below methods can be
+  // used to move the cursor to an attribute node. MoveToCarryingElement() can
+  // be called afterwards to return the cursor to the carrying element. The
+  // depth of an attribute node will be 1 + the depth of the carrying element.
+
+  ///
+  // Moves the cursor to the attribute at the specified 0-based index. Returns
+  // true if the cursor position was set successfully.
+  ///
+  /*--cef(capi_name=move_to_attribute_byindex,index_param=index)--*/
+  virtual bool MoveToAttribute(int index) = 0;
+
+  ///
+  // Moves the cursor to the attribute with the specified qualified name.
+  // Returns true if the cursor position was set successfully.
+  ///
+  /*--cef(capi_name=move_to_attribute_byqname)--*/
+  virtual bool MoveToAttribute(const CefString& qualifiedName) = 0;
+
+  ///
+  // Moves the cursor to the attribute with the specified local name and
+  // namespace URI. Returns true if the cursor position was set successfully.
+  ///
+  /*--cef(capi_name=move_to_attribute_bylname)--*/
+  virtual bool MoveToAttribute(const CefString& localName,
+                               const CefString& namespaceURI) = 0;
+
+  ///
+  // Moves the cursor to the first attribute in the current element. Returns
+  // true if the cursor position was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool MoveToFirstAttribute() = 0;
+
+  ///
+  // Moves the cursor to the next attribute in the current element. Returns
+  // true if the cursor position was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool MoveToNextAttribute() = 0;
+
+  ///
+  // Moves the cursor back to the carrying element. Returns true if the cursor
+  // position was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool MoveToCarryingElement() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_XML_READER_H_
diff --git a/src/include/cef_zip_reader.h b/src/include/cef_zip_reader.h
new file mode 100644
index 0000000..5dc2efd
--- /dev/null
+++ b/src/include/cef_zip_reader.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_CEF_ZIP_READER_H_
+#define CEF_INCLUDE_CEF_ZIP_READER_H_
+
+#include "include/cef_base.h"
+#include "include/cef_stream.h"
+
+///
+// Class that supports the reading of zip archives via the zlib unzip API.
+// The methods of this class should only be called on the thread that creates
+// the object.
+///
+/*--cef(source=library)--*/
+class CefZipReader : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Create a new CefZipReader object. The returned object's methods can only
+  // be called from the thread that created the object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefZipReader> Create(CefRefPtr<CefStreamReader> stream);
+
+  ///
+  // Moves the cursor to the first file in the archive. Returns true if the
+  // cursor position was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool MoveToFirstFile() = 0;
+
+  ///
+  // Moves the cursor to the next file in the archive. Returns true if the
+  // cursor position was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool MoveToNextFile() = 0;
+
+  ///
+  // Moves the cursor to the specified file in the archive. If |caseSensitive|
+  // is true then the search will be case sensitive. Returns true if the cursor
+  // position was set successfully.
+  ///
+  /*--cef()--*/
+  virtual bool MoveToFile(const CefString& fileName, bool caseSensitive) = 0;
+
+  ///
+  // Closes the archive. This should be called directly to ensure that cleanup
+  // occurs on the correct thread.
+  ///
+  /*--cef()--*/
+  virtual bool Close() = 0;
+
+  // The below methods act on the file at the current cursor position.
+
+  ///
+  // Returns the name of the file.
+  ///
+  /*--cef()--*/
+  virtual CefString GetFileName() = 0;
+
+  ///
+  // Returns the uncompressed size of the file.
+  ///
+  /*--cef()--*/
+  virtual int64 GetFileSize() = 0;
+
+  ///
+  // Returns the last modified timestamp for the file.
+  ///
+  /*--cef()--*/
+  virtual CefTime GetFileLastModified() = 0;
+
+  ///
+  // Opens the file for reading of uncompressed data. A read password may
+  // optionally be specified.
+  ///
+  /*--cef(optional_param=password)--*/
+  virtual bool OpenFile(const CefString& password) = 0;
+
+  ///
+  // Closes the file.
+  ///
+  /*--cef()--*/
+  virtual bool CloseFile() = 0;
+
+  ///
+  // Read uncompressed file contents into the specified buffer. Returns < 0 if
+  // an error occurred, 0 if at the end of file, or the number of bytes read.
+  ///
+  /*--cef()--*/
+  virtual int ReadFile(void* buffer, size_t bufferSize) = 0;
+
+  ///
+  // Returns the current offset in the uncompressed file contents.
+  ///
+  /*--cef()--*/
+  virtual int64 Tell() = 0;
+
+  ///
+  // Returns true if at end of the file contents.
+  ///
+  /*--cef()--*/
+  virtual bool Eof() = 0;
+};
+
+#endif  // CEF_INCLUDE_CEF_ZIP_READER_H_
diff --git a/src/include/internal/cef_export.h b/src/include/internal/cef_export.h
new file mode 100644
index 0000000..1915f5e
--- /dev/null
+++ b/src/include/internal/cef_export.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_EXPORT_H_
+#define CEF_INCLUDE_INTERNAL_CEF_EXPORT_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+
+#if defined(COMPILER_MSVC)
+
+#ifdef BUILDING_CEF_SHARED
+#define CEF_EXPORT __declspec(dllexport)
+#elif USING_CEF_SHARED
+#define CEF_EXPORT __declspec(dllimport)
+#else
+#define CEF_EXPORT
+#endif
+
+#elif defined(COMPILER_GCC)
+
+#define CEF_EXPORT __attribute__((visibility("default")))
+
+#endif  // COMPILER_GCC
+
+#if defined(OS_WIN)
+#define CEF_CALLBACK __stdcall
+#else
+#define CEF_CALLBACK
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_EXPORT_H_
diff --git a/src/include/internal/cef_linux.h b/src/include/internal/cef_linux.h
new file mode 100644
index 0000000..a247d44
--- /dev/null
+++ b/src/include/internal/cef_linux.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2010 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_LINUX_H_
+#define CEF_INCLUDE_INTERNAL_CEF_LINUX_H_
+#pragma once
+
+#include "include/internal/cef_types_linux.h"
+#include "include/internal/cef_types_wrappers.h"
+
+// Handle types.
+#define CefCursorHandle cef_cursor_handle_t
+#define CefEventHandle cef_event_handle_t
+#define CefWindowHandle cef_window_handle_t
+
+struct CefMainArgsTraits {
+  typedef cef_main_args_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->argc = src->argc;
+    target->argv = src->argv;
+  }
+};
+
+// Class representing CefExecuteProcess arguments.
+class CefMainArgs : public CefStructBase<CefMainArgsTraits> {
+ public:
+  typedef CefStructBase<CefMainArgsTraits> parent;
+
+  CefMainArgs() : parent() {}
+  explicit CefMainArgs(const cef_main_args_t& r) : parent(r) {}
+  explicit CefMainArgs(const CefMainArgs& r) : parent(r) {}
+  CefMainArgs(int argc_arg, char** argv_arg) : parent() {
+    argc = argc_arg;
+    argv = argv_arg;
+  }
+};
+
+struct CefWindowInfoTraits {
+  typedef cef_window_info_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->window_name);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    cef_string_set(src->window_name.str, src->window_name.length,
+                   &target->window_name, copy);
+    target->x = src->x;
+    target->y = src->y;
+    target->width = src->width;
+    target->height = src->height;
+    target->parent_window = src->parent_window;
+    target->windowless_rendering_enabled = src->windowless_rendering_enabled;
+    target->shared_texture_enabled = src->shared_texture_enabled;
+    target->external_begin_frame_enabled = src->external_begin_frame_enabled;
+    target->window = src->window;
+  }
+};
+
+// Class representing window information.
+class CefWindowInfo : public CefStructBase<CefWindowInfoTraits> {
+ public:
+  typedef CefStructBase<CefWindowInfoTraits> parent;
+
+  CefWindowInfo() : parent() {}
+  explicit CefWindowInfo(const cef_window_info_t& r) : parent(r) {}
+  explicit CefWindowInfo(const CefWindowInfo& r) : parent(r) {}
+
+  ///
+  // Create the browser as a child window.
+  ///
+  void SetAsChild(CefWindowHandle parent, const CefRect& windowRect) {
+    parent_window = parent;
+    x = windowRect.x;
+    y = windowRect.y;
+    width = windowRect.width;
+    height = windowRect.height;
+  }
+
+  ///
+  // Create the browser using windowless (off-screen) rendering. No window
+  // will be created for the browser and all rendering will occur via the
+  // CefRenderHandler interface. The |parent| value will be used to identify
+  // monitor info and to act as the parent window for dialogs, context menus,
+  // etc. If |parent| is not provided then the main screen monitor will be used
+  // and some functionality that requires a parent window may not function
+  // correctly. In order to create windowless browsers the
+  // CefSettings.windowless_rendering_enabled value must be set to true.
+  // Transparent painting is enabled by default but can be disabled by setting
+  // CefBrowserSettings.background_color to an opaque value.
+  ///
+  void SetAsWindowless(CefWindowHandle parent) {
+    windowless_rendering_enabled = true;
+    parent_window = parent;
+  }
+};
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_LINUX_H_
diff --git a/src/include/internal/cef_logging_internal.h b/src/include/internal/cef_logging_internal.h
new file mode 100644
index 0000000..598263d
--- /dev/null
+++ b/src/include/internal/cef_logging_internal.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_LOGGING_INTERNAL_H_
+#define CEF_INCLUDE_INTERNAL_CEF_LOGGING_INTERNAL_H_
+#pragma once
+
+#include <stddef.h>
+
+#include "include/internal/cef_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// See include/base/cef_logging.h for macros and intended usage.
+
+///
+// Gets the current log level.
+///
+CEF_EXPORT int cef_get_min_log_level();
+
+///
+// Gets the current vlog level for the given file (usually taken from
+// __FILE__). Note that |N| is the size *with* the null terminator.
+///
+CEF_EXPORT int cef_get_vlog_level(const char* file_start, size_t N);
+
+///
+// Add a log message. See the LogSeverity defines for supported |severity|
+// values.
+///
+CEF_EXPORT void cef_log(const char* file,
+                        int line,
+                        int severity,
+                        const char* message);
+
+#ifdef __cplusplus
+}
+#endif  // __cplusplus
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_LOGGING_INTERNAL_H_
diff --git a/src/include/internal/cef_mac.h b/src/include/internal/cef_mac.h
new file mode 100644
index 0000000..3123fb5
--- /dev/null
+++ b/src/include/internal/cef_mac.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2010 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_MAC_H_
+#define CEF_INCLUDE_INTERNAL_CEF_MAC_H_
+#pragma once
+
+#include "include/internal/cef_types_mac.h"
+#include "include/internal/cef_types_wrappers.h"
+
+// Handle types.
+#define CefCursorHandle cef_cursor_handle_t
+#define CefEventHandle cef_event_handle_t
+#define CefWindowHandle cef_window_handle_t
+
+struct CefMainArgsTraits {
+  typedef cef_main_args_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->argc = src->argc;
+    target->argv = src->argv;
+  }
+};
+
+// Class representing CefExecuteProcess arguments.
+class CefMainArgs : public CefStructBase<CefMainArgsTraits> {
+ public:
+  typedef CefStructBase<CefMainArgsTraits> parent;
+
+  CefMainArgs() : parent() {}
+  explicit CefMainArgs(const cef_main_args_t& r) : parent(r) {}
+  explicit CefMainArgs(const CefMainArgs& r) : parent(r) {}
+  CefMainArgs(int argc, char** argv) : parent() {
+    this->argc = argc;
+    this->argv = argv;
+  }
+};
+
+struct CefWindowInfoTraits {
+  typedef cef_window_info_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->window_name);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    cef_string_set(src->window_name.str, src->window_name.length,
+                   &target->window_name, copy);
+    target->x = src->x;
+    target->y = src->y;
+    target->width = src->width;
+    target->height = src->height;
+    target->hidden = src->hidden;
+    target->parent_view = src->parent_view;
+    target->windowless_rendering_enabled = src->windowless_rendering_enabled;
+    target->shared_texture_enabled = src->shared_texture_enabled;
+    target->external_begin_frame_enabled = src->external_begin_frame_enabled;
+    target->view = src->view;
+  }
+};
+
+// Class representing window information.
+class CefWindowInfo : public CefStructBase<CefWindowInfoTraits> {
+ public:
+  typedef CefStructBase<CefWindowInfoTraits> parent;
+
+  CefWindowInfo() : parent() {}
+  explicit CefWindowInfo(const cef_window_info_t& r) : parent(r) {}
+  explicit CefWindowInfo(const CefWindowInfo& r) : parent(r) {}
+
+  ///
+  // Create the browser as a child view.
+  ///
+  void SetAsChild(CefWindowHandle parent, int x, int y, int width, int height) {
+    parent_view = parent;
+    this->x = x;
+    this->y = y;
+    this->width = width;
+    this->height = height;
+    hidden = false;
+  }
+
+  ///
+  // Create the browser using windowless (off-screen) rendering. No view
+  // will be created for the browser and all rendering will occur via the
+  // CefRenderHandler interface. The |parent| value will be used to identify
+  // monitor info and to act as the parent view for dialogs, context menus,
+  // etc. If |parent| is not provided then the main screen monitor will be used
+  // and some functionality that requires a parent view may not function
+  // correctly. In order to create windowless browsers the
+  // CefSettings.windowless_rendering_enabled value must be set to true.
+  // Transparent painting is enabled by default but can be disabled by setting
+  // CefBrowserSettings.background_color to an opaque value.
+  ///
+  void SetAsWindowless(CefWindowHandle parent) {
+    windowless_rendering_enabled = true;
+    parent_view = parent;
+  }
+};
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_MAC_H_
diff --git a/src/include/internal/cef_ptr.h b/src/include/internal/cef_ptr.h
new file mode 100644
index 0000000..044c733
--- /dev/null
+++ b/src/include/internal/cef_ptr.h
@@ -0,0 +1,230 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_PTR_H_
+#define CEF_INCLUDE_INTERNAL_CEF_PTR_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+#include "include/base/cef_ref_counted.h"
+
+#if defined(USING_CHROMIUM_INCLUDES)
+#include <memory>  // For std::unique_ptr.
+#else
+#include "include/base/cef_scoped_ptr.h"
+#endif
+
+///
+// Smart pointer implementation that is an alias of scoped_refptr from
+// include/base/cef_ref_counted.h.
+// <p>
+// A smart pointer class for reference counted objects.  Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference.  Sample usage:
+// <pre>
+//   class MyFoo : public CefBaseRefCounted {
+//    ...
+//   };
+//
+//   void some_function() {
+//     // The MyFoo object that |foo| represents starts with a single
+//     // reference.
+//     CefRefPtr&lt;MyFoo&gt; foo = new MyFoo();
+//     foo-&gt;Method(param);
+//     // |foo| is released when this function returns
+//   }
+//
+//   void some_other_function() {
+//     CefRefPtr&lt;MyFoo&gt; foo = new MyFoo();
+//     ...
+//     foo = NULL;  // explicitly releases |foo|
+//     ...
+//     if (foo)
+//       foo-&gt;Method(param);
+//   }
+// </pre>
+// The above examples show how CefRefPtr&lt;T&gt; acts like a pointer to T.
+// Given two CefRefPtr&lt;T&gt; classes, it is also possible to exchange
+// references between the two objects, like so:
+// <pre>
+//   {
+//     CefRefPtr&lt;MyFoo&gt; a = new MyFoo();
+//     CefRefPtr&lt;MyFoo&gt; b;
+//
+//     b.swap(a);
+//     // now, |b| references the MyFoo object, and |a| references NULL.
+//   }
+// </pre>
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+// <pre>
+//   {
+//     CefRefPtr&lt;MyFoo&gt; a = new MyFoo();
+//     CefRefPtr&lt;MyFoo&gt; b;
+//
+//     b = a;
+//     // now, |a| and |b| each own a reference to the same MyFoo object.
+//     // the reference count of the underlying MyFoo object will be 2.
+//   }
+// </pre>
+// Reference counted objects can also be passed as function parameters and
+// used as function return values:
+// <pre>
+//   void some_func_with_param(CefRefPtr&lt;MyFoo&gt; param) {
+//     // A reference is added to the MyFoo object that |param| represents
+//     // during the scope of some_func_with_param() and released when
+//     // some_func_with_param() goes out of scope.
+//   }
+//
+//   CefRefPtr&lt;MyFoo&gt; some_func_with_retval() {
+//     // The MyFoo object that |foox| represents starts with a single
+//     // reference.
+//     CefRefPtr&lt;MyFoo&gt; foox = new MyFoo();
+//
+//     // Creating the return value adds an additional reference.
+//     return foox;
+//
+//     // When some_func_with_retval() goes out of scope the original |foox|
+//     // reference is released.
+//   }
+//
+//   void and_another_function() {
+//     CefRefPtr&lt;MyFoo&gt; foo = new MyFoo();
+//
+//     // pass |foo| as a parameter.
+//     some_function(foo);
+//
+//     CefRefPtr&lt;MyFoo&gt; foo2 = some_func_with_retval();
+//     // Now, since we kept a reference to the some_func_with_retval() return
+//     // value, |foo2| is the only class pointing to the MyFoo object created
+//     in some_func_with_retval(), and it has a reference count of 1.
+//
+//     some_func_with_retval();
+//     // Now, since we didn't keep a reference to the some_func_with_retval()
+//     // return value, the MyFoo object created in some_func_with_retval()
+//     // will automatically be released.
+//   }
+// </pre>
+// And in standard containers:
+// <pre>
+//   {
+//      // Create a vector that holds MyFoo objects.
+//      std::vector&lt;CefRefPtr&lt;MyFoo&gt; &gt; MyFooVec;
+//
+//     // The MyFoo object that |foo| represents starts with a single
+//     // reference.
+//     CefRefPtr&lt;MyFoo&gt; foo = new MyFoo();
+//
+//     // When the MyFoo object is added to |MyFooVec| the reference count
+//     // is increased to 2.
+//     MyFooVec.push_back(foo);
+//   }
+// </pre>
+// </p>
+///
+#if defined(HAS_CPP11_TEMPLATE_ALIAS_SUPPORT)
+template <class T>
+using CefRefPtr = scoped_refptr<T>;
+#else
+// When template aliases are not supported use a define instead of subclassing
+// because it's otherwise hard to get the constructors to behave correctly.
+#define CefRefPtr scoped_refptr
+#endif
+
+///
+// A CefOwnPtr<T> is like a T*, except that the destructor of CefOwnPtr<T>
+// automatically deletes the pointer it holds (if any). That is, CefOwnPtr<T>
+// owns the T object that it points to. Like a T*, a CefOwnPtr<T> may hold
+// either NULL or a pointer to a T object. Also like T*, CefOwnPtr<T> is
+// thread-compatible, and once you dereference it, you get the thread safety
+// guarantees of T.
+///
+#if defined(USING_CHROMIUM_INCLUDES)
+// Implementation-side code uses std::unique_ptr instead of scoped_ptr.
+template <class T, class D = std::default_delete<T>>
+using CefOwnPtr = std::unique_ptr<T, D>;
+#elif defined(HAS_CPP11_TEMPLATE_ALIAS_SUPPORT)
+template <class T, class D = base::DefaultDeleter<T>>
+using CefOwnPtr = scoped_ptr<T, D>;
+#else
+// When template aliases are not supported use a define instead of subclassing
+// because it's otherwise hard to get the constructors to behave correctly.
+#define CefOwnPtr scoped_ptr
+#endif
+
+///
+// A CefRawPtr<T> is the same as T*
+///
+#if defined(HAS_CPP11_TEMPLATE_ALIAS_SUPPORT)
+#define CEF_RAW_PTR_GET(r) r
+template <class T>
+using CefRawPtr = T*;
+#else
+// Simple wrapper implementation that behaves as much like T* as possible.
+// CEF_RAW_PTR_GET is required for VS2008 compatibility (Issue #2155).
+#define CEF_RAW_PTR_GET(r) r.get()
+template <class T>
+class CefRawPtr {
+ public:
+  CefRawPtr() : ptr_(nullptr) {}
+  CefRawPtr(T* p) : ptr_(p) {}
+  CefRawPtr(const CefRawPtr& r) : ptr_(r.ptr_) {}
+
+  template <typename U>
+  CefRawPtr(const CefRawPtr<U>& r) : ptr_(r.get()) {}
+
+  T* get() const { return ptr_; }
+
+  // Allow CefRawPtr to be used in boolean expression and comparison operations.
+  operator T*() const { return ptr_; }
+
+  T* operator->() const {
+    assert(ptr_ != NULL);
+    return ptr_;
+  }
+
+  CefRawPtr<T>& operator=(T* p) {
+    ptr_ = p;
+    return *this;
+  }
+
+  CefRawPtr<T>& operator=(const CefRawPtr<T>& r) { return *this = r.ptr_; }
+
+  template <typename U>
+  CefRawPtr<T>& operator=(const CefRawPtr<U>& r) {
+    return *this = r.get();
+  }
+
+ private:
+  T* ptr_;
+};
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_PTR_H_
diff --git a/src/include/internal/cef_string.h b/src/include/internal/cef_string.h
new file mode 100644
index 0000000..77c8ca3
--- /dev/null
+++ b/src/include/internal/cef_string.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2010 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_STRING_H_
+#define CEF_INCLUDE_INTERNAL_CEF_STRING_H_
+#pragma once
+
+// The CEF interface is built with one string type as the default. Comment out
+// all but one of the CEF_STRING_TYPE_* defines below to specify the default.
+// If you change the default you MUST recompile all of CEF.
+
+// Build with the UTF8 string type as default.
+// #define CEF_STRING_TYPE_UTF8 1
+
+// Build with the UTF16 string type as default.
+#define CEF_STRING_TYPE_UTF16 1
+
+// Build with the wide string type as default.
+// #define CEF_STRING_TYPE_WIDE 1
+
+#include "include/internal/cef_string_types.h"
+
+#ifdef __cplusplus
+#include "include/internal/cef_string_wrappers.h"
+#if defined(CEF_STRING_TYPE_UTF16)
+typedef CefStringUTF16 CefString;
+#elif defined(CEF_STRING_TYPE_UTF8)
+typedef CefStringUTF8 CefString;
+#elif defined(CEF_STRING_TYPE_WIDE)
+typedef CefStringWide CefString;
+#endif
+#endif  // __cplusplus
+
+#if defined(CEF_STRING_TYPE_UTF8)
+typedef char cef_char_t;
+typedef cef_string_utf8_t cef_string_t;
+typedef cef_string_userfree_utf8_t cef_string_userfree_t;
+#define cef_string_set cef_string_utf8_set
+#define cef_string_copy cef_string_utf8_copy
+#define cef_string_clear cef_string_utf8_clear
+#define cef_string_userfree_alloc cef_string_userfree_utf8_alloc
+#define cef_string_userfree_free cef_string_userfree_utf8_free
+#define cef_string_from_ascii cef_string_utf8_copy
+#define cef_string_to_utf8 cef_string_utf8_copy
+#define cef_string_from_utf8 cef_string_utf8_copy
+#define cef_string_to_utf16 cef_string_utf8_to_utf16
+#define cef_string_from_utf16 cef_string_utf16_to_utf8
+#define cef_string_to_wide cef_string_utf8_to_wide
+#define cef_string_from_wide cef_string_wide_to_utf8
+#elif defined(CEF_STRING_TYPE_UTF16)
+typedef char16 cef_char_t;
+typedef cef_string_userfree_utf16_t cef_string_userfree_t;
+typedef cef_string_utf16_t cef_string_t;
+#define cef_string_set cef_string_utf16_set
+#define cef_string_copy cef_string_utf16_copy
+#define cef_string_clear cef_string_utf16_clear
+#define cef_string_userfree_alloc cef_string_userfree_utf16_alloc
+#define cef_string_userfree_free cef_string_userfree_utf16_free
+#define cef_string_from_ascii cef_string_ascii_to_utf16
+#define cef_string_to_utf8 cef_string_utf16_to_utf8
+#define cef_string_from_utf8 cef_string_utf8_to_utf16
+#define cef_string_to_utf16 cef_string_utf16_copy
+#define cef_string_from_utf16 cef_string_utf16_copy
+#define cef_string_to_wide cef_string_utf16_to_wide
+#define cef_string_from_wide cef_string_wide_to_utf16
+#elif defined(CEF_STRING_TYPE_WIDE)
+typedef wchar_t cef_char_t;
+typedef cef_string_wide_t cef_string_t;
+typedef cef_string_userfree_wide_t cef_string_userfree_t;
+#define cef_string_set cef_string_wide_set
+#define cef_string_copy cef_string_wide_copy
+#define cef_string_clear cef_string_wide_clear
+#define cef_string_userfree_alloc cef_string_userfree_wide_alloc
+#define cef_string_userfree_free cef_string_userfree_wide_free
+#define cef_string_from_ascii cef_string_ascii_to_wide
+#define cef_string_to_utf8 cef_string_wide_to_utf8
+#define cef_string_from_utf8 cef_string_utf8_to_wide
+#define cef_string_to_utf16 cef_string_wide_to_utf16
+#define cef_string_from_utf16 cef_string_utf16_to_wide
+#define cef_string_to_wide cef_string_wide_copy
+#define cef_string_from_wide cef_string_wide_copy
+#else
+#error Please choose a string type.
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_STRING_H_
diff --git a/src/include/internal/cef_string_list.h b/src/include/internal/cef_string_list.h
new file mode 100644
index 0000000..b44bac7
--- /dev/null
+++ b/src/include/internal/cef_string_list.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2009 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_STRING_LIST_H_
+#define CEF_INCLUDE_INTERNAL_CEF_STRING_LIST_H_
+#pragma once
+
+#include "include/internal/cef_export.h"
+#include "include/internal/cef_string.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// CEF string maps are a set of key/value string pairs.
+///
+typedef void* cef_string_list_t;
+
+///
+// Allocate a new string map.
+///
+CEF_EXPORT cef_string_list_t cef_string_list_alloc();
+
+///
+// Return the number of elements in the string list.
+///
+CEF_EXPORT size_t cef_string_list_size(cef_string_list_t list);
+
+///
+// Retrieve the value at the specified zero-based string list index. Returns
+// true (1) if the value was successfully retrieved.
+///
+CEF_EXPORT int cef_string_list_value(cef_string_list_t list,
+                                     size_t index,
+                                     cef_string_t* value);
+
+///
+// Append a new value at the end of the string list.
+///
+CEF_EXPORT void cef_string_list_append(cef_string_list_t list,
+                                       const cef_string_t* value);
+
+///
+// Clear the string list.
+///
+CEF_EXPORT void cef_string_list_clear(cef_string_list_t list);
+
+///
+// Free the string list.
+///
+CEF_EXPORT void cef_string_list_free(cef_string_list_t list);
+
+///
+// Creates a copy of an existing string list.
+///
+CEF_EXPORT cef_string_list_t cef_string_list_copy(cef_string_list_t list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_STRING_LIST_H_
diff --git a/src/include/internal/cef_string_map.h b/src/include/internal/cef_string_map.h
new file mode 100644
index 0000000..0f33425
--- /dev/null
+++ b/src/include/internal/cef_string_map.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2009 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_STRING_MAP_H_
+#define CEF_INCLUDE_INTERNAL_CEF_STRING_MAP_H_
+#pragma once
+
+#include "include/internal/cef_export.h"
+#include "include/internal/cef_string.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// CEF string maps are a set of key/value string pairs.
+///
+typedef void* cef_string_map_t;
+
+///
+// Allocate a new string map.
+///
+CEF_EXPORT cef_string_map_t cef_string_map_alloc();
+
+///
+// Return the number of elements in the string map.
+///
+CEF_EXPORT size_t cef_string_map_size(cef_string_map_t map);
+
+///
+// Return the value assigned to the specified key.
+///
+CEF_EXPORT int cef_string_map_find(cef_string_map_t map,
+                                   const cef_string_t* key,
+                                   cef_string_t* value);
+
+///
+// Return the key at the specified zero-based string map index.
+///
+CEF_EXPORT int cef_string_map_key(cef_string_map_t map,
+                                  size_t index,
+                                  cef_string_t* key);
+
+///
+// Return the value at the specified zero-based string map index.
+///
+CEF_EXPORT int cef_string_map_value(cef_string_map_t map,
+                                    size_t index,
+                                    cef_string_t* value);
+
+///
+// Append a new key/value pair at the end of the string map.
+///
+CEF_EXPORT int cef_string_map_append(cef_string_map_t map,
+                                     const cef_string_t* key,
+                                     const cef_string_t* value);
+
+///
+// Clear the string map.
+///
+CEF_EXPORT void cef_string_map_clear(cef_string_map_t map);
+
+///
+// Free the string map.
+///
+CEF_EXPORT void cef_string_map_free(cef_string_map_t map);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_STRING_MAP_H_
diff --git a/src/include/internal/cef_string_multimap.h b/src/include/internal/cef_string_multimap.h
new file mode 100644
index 0000000..cd07746
--- /dev/null
+++ b/src/include/internal/cef_string_multimap.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_STRING_MULTIMAP_H_
+#define CEF_INCLUDE_INTERNAL_CEF_STRING_MULTIMAP_H_
+#pragma once
+
+#include "include/internal/cef_export.h"
+#include "include/internal/cef_string.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// CEF string multimaps are a set of key/value string pairs.
+// More than one value can be assigned to a single key.
+///
+typedef void* cef_string_multimap_t;
+
+///
+// Allocate a new string multimap.
+///
+CEF_EXPORT cef_string_multimap_t cef_string_multimap_alloc();
+
+///
+// Return the number of elements in the string multimap.
+///
+CEF_EXPORT size_t cef_string_multimap_size(cef_string_multimap_t map);
+
+///
+// Return the number of values with the specified key.
+///
+CEF_EXPORT size_t cef_string_multimap_find_count(cef_string_multimap_t map,
+                                                 const cef_string_t* key);
+
+///
+// Return the value_index-th value with the specified key.
+///
+CEF_EXPORT int cef_string_multimap_enumerate(cef_string_multimap_t map,
+                                             const cef_string_t* key,
+                                             size_t value_index,
+                                             cef_string_t* value);
+
+///
+// Return the key at the specified zero-based string multimap index.
+///
+CEF_EXPORT int cef_string_multimap_key(cef_string_multimap_t map,
+                                       size_t index,
+                                       cef_string_t* key);
+
+///
+// Return the value at the specified zero-based string multimap index.
+///
+CEF_EXPORT int cef_string_multimap_value(cef_string_multimap_t map,
+                                         size_t index,
+                                         cef_string_t* value);
+
+///
+// Append a new key/value pair at the end of the string multimap.
+///
+CEF_EXPORT int cef_string_multimap_append(cef_string_multimap_t map,
+                                          const cef_string_t* key,
+                                          const cef_string_t* value);
+
+///
+// Clear the string multimap.
+///
+CEF_EXPORT void cef_string_multimap_clear(cef_string_multimap_t map);
+
+///
+// Free the string multimap.
+///
+CEF_EXPORT void cef_string_multimap_free(cef_string_multimap_t map);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_STRING_MULTIMAP_H_
diff --git a/src/include/internal/cef_string_types.h b/src/include/internal/cef_string_types.h
new file mode 100644
index 0000000..ea91960
--- /dev/null
+++ b/src/include/internal/cef_string_types.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2010 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_STRING_TYPES_H_
+#define CEF_INCLUDE_INTERNAL_CEF_STRING_TYPES_H_
+#pragma once
+
+// CEF provides functions for converting between UTF-8, -16 and -32 strings.
+// CEF string types are safe for reading from multiple threads but not for
+// modification. It is the user's responsibility to provide synchronization if
+// modifying CEF strings from multiple threads.
+
+#include <stddef.h>
+
+#include "include/base/cef_basictypes.h"
+#include "include/internal/cef_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// CEF string type definitions. Whomever allocates |str| is responsible for
+// providing an appropriate |dtor| implementation that will free the string in
+// the same memory space. When reusing an existing string structure make sure
+// to call |dtor| for the old value before assigning new |str| and |dtor|
+// values. Static strings will have a NULL |dtor| value. Using the below
+// functions if you want this managed for you.
+
+typedef struct _cef_string_wide_t {
+  wchar_t* str;
+  size_t length;
+  void (*dtor)(wchar_t* str);
+} cef_string_wide_t;
+
+typedef struct _cef_string_utf8_t {
+  char* str;
+  size_t length;
+  void (*dtor)(char* str);
+} cef_string_utf8_t;
+
+typedef struct _cef_string_utf16_t {
+  char16* str;
+  size_t length;
+  void (*dtor)(char16* str);
+} cef_string_utf16_t;
+
+///
+// These functions set string values. If |copy| is true (1) the value will be
+// copied instead of referenced. It is up to the user to properly manage
+// the lifespan of references.
+///
+
+CEF_EXPORT int cef_string_wide_set(const wchar_t* src,
+                                   size_t src_len,
+                                   cef_string_wide_t* output,
+                                   int copy);
+CEF_EXPORT int cef_string_utf8_set(const char* src,
+                                   size_t src_len,
+                                   cef_string_utf8_t* output,
+                                   int copy);
+CEF_EXPORT int cef_string_utf16_set(const char16* src,
+                                    size_t src_len,
+                                    cef_string_utf16_t* output,
+                                    int copy);
+
+///
+// Convenience macros for copying values.
+///
+
+#define cef_string_wide_copy(src, src_len, output) \
+  cef_string_wide_set(src, src_len, output, true)
+#define cef_string_utf8_copy(src, src_len, output) \
+  cef_string_utf8_set(src, src_len, output, true)
+#define cef_string_utf16_copy(src, src_len, output) \
+  cef_string_utf16_set(src, src_len, output, true)
+
+///
+// These functions clear string values. The structure itself is not freed.
+///
+
+CEF_EXPORT void cef_string_wide_clear(cef_string_wide_t* str);
+CEF_EXPORT void cef_string_utf8_clear(cef_string_utf8_t* str);
+CEF_EXPORT void cef_string_utf16_clear(cef_string_utf16_t* str);
+
+///
+// These functions compare two string values with the same results as strcmp().
+///
+
+CEF_EXPORT int cef_string_wide_cmp(const cef_string_wide_t* str1,
+                                   const cef_string_wide_t* str2);
+CEF_EXPORT int cef_string_utf8_cmp(const cef_string_utf8_t* str1,
+                                   const cef_string_utf8_t* str2);
+CEF_EXPORT int cef_string_utf16_cmp(const cef_string_utf16_t* str1,
+                                    const cef_string_utf16_t* str2);
+
+///
+// These functions convert between UTF-8, -16, and -32 strings. They are
+// potentially slow so unnecessary conversions should be avoided. The best
+// possible result will always be written to |output| with the boolean return
+// value indicating whether the conversion is 100% valid.
+///
+
+CEF_EXPORT int cef_string_wide_to_utf8(const wchar_t* src,
+                                       size_t src_len,
+                                       cef_string_utf8_t* output);
+CEF_EXPORT int cef_string_utf8_to_wide(const char* src,
+                                       size_t src_len,
+                                       cef_string_wide_t* output);
+
+CEF_EXPORT int cef_string_wide_to_utf16(const wchar_t* src,
+                                        size_t src_len,
+                                        cef_string_utf16_t* output);
+CEF_EXPORT int cef_string_utf16_to_wide(const char16* src,
+                                        size_t src_len,
+                                        cef_string_wide_t* output);
+
+CEF_EXPORT int cef_string_utf8_to_utf16(const char* src,
+                                        size_t src_len,
+                                        cef_string_utf16_t* output);
+CEF_EXPORT int cef_string_utf16_to_utf8(const char16* src,
+                                        size_t src_len,
+                                        cef_string_utf8_t* output);
+
+///
+// These functions convert an ASCII string, typically a hardcoded constant, to a
+// Wide/UTF16 string. Use instead of the UTF8 conversion routines if you know
+// the string is ASCII.
+///
+
+CEF_EXPORT int cef_string_ascii_to_wide(const char* src,
+                                        size_t src_len,
+                                        cef_string_wide_t* output);
+CEF_EXPORT int cef_string_ascii_to_utf16(const char* src,
+                                         size_t src_len,
+                                         cef_string_utf16_t* output);
+
+///
+// It is sometimes necessary for the system to allocate string structures with
+// the expectation that the user will free them. The userfree types act as a
+// hint that the user is responsible for freeing the structure.
+///
+
+typedef cef_string_wide_t* cef_string_userfree_wide_t;
+typedef cef_string_utf8_t* cef_string_userfree_utf8_t;
+typedef cef_string_utf16_t* cef_string_userfree_utf16_t;
+
+///
+// These functions allocate a new string structure. They must be freed by
+// calling the associated free function.
+///
+
+CEF_EXPORT cef_string_userfree_wide_t cef_string_userfree_wide_alloc();
+CEF_EXPORT cef_string_userfree_utf8_t cef_string_userfree_utf8_alloc();
+CEF_EXPORT cef_string_userfree_utf16_t cef_string_userfree_utf16_alloc();
+
+///
+// These functions free the string structure allocated by the associated
+// alloc function. Any string contents will first be cleared.
+///
+
+CEF_EXPORT void cef_string_userfree_wide_free(cef_string_userfree_wide_t str);
+CEF_EXPORT void cef_string_userfree_utf8_free(cef_string_userfree_utf8_t str);
+CEF_EXPORT void cef_string_userfree_utf16_free(cef_string_userfree_utf16_t str);
+
+///
+// These functions convert utf16 string case using the current ICU locale. This
+// may change the length of the string in some cases.
+///
+
+CEF_EXPORT int cef_string_utf16_to_lower(const char16* src,
+                                         size_t src_len,
+                                         cef_string_utf16_t* output);
+CEF_EXPORT int cef_string_utf16_to_upper(const char16* src,
+                                         size_t src_len,
+                                         cef_string_utf16_t* output);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_STRING_TYPES_H_
diff --git a/src/include/internal/cef_string_wrappers.h b/src/include/internal/cef_string_wrappers.h
new file mode 100644
index 0000000..f965e9c
--- /dev/null
+++ b/src/include/internal/cef_string_wrappers.h
@@ -0,0 +1,714 @@
+// Copyright (c) 2010 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_STRING_WRAPPERS_H_
+#define CEF_INCLUDE_INTERNAL_CEF_STRING_WRAPPERS_H_
+#pragma once
+
+#include <memory.h>
+#include <string>
+
+#include "include/base/cef_string16.h"
+#include "include/internal/cef_string_types.h"
+
+#if defined(USING_CHROMIUM_INCLUDES)
+#include "base/files/file_path.h"
+#endif
+
+///
+// Traits implementation for wide character strings.
+///
+struct CefStringTraitsWide {
+  typedef wchar_t char_type;
+  typedef cef_string_wide_t struct_type;
+  typedef cef_string_userfree_wide_t userfree_struct_type;
+
+  static inline void clear(struct_type* s) { cef_string_wide_clear(s); }
+  static inline int set(const char_type* src,
+                        size_t src_size,
+                        struct_type* output,
+                        int copy) {
+    return cef_string_wide_set(src, src_size, output, copy);
+  }
+  static inline int compare(const struct_type* s1, const struct_type* s2) {
+    return cef_string_wide_cmp(s1, s2);
+  }
+  static inline userfree_struct_type userfree_alloc() {
+    return cef_string_userfree_wide_alloc();
+  }
+  static inline void userfree_free(userfree_struct_type ufs) {
+    return cef_string_userfree_wide_free(ufs);
+  }
+
+  // Conversion methods.
+  static inline bool from_ascii(const char* str, size_t len, struct_type* s) {
+    return cef_string_ascii_to_wide(str, len, s) ? true : false;
+  }
+  static inline std::string to_string(const struct_type* s) {
+    cef_string_utf8_t cstr;
+    memset(&cstr, 0, sizeof(cstr));
+    cef_string_wide_to_utf8(s->str, s->length, &cstr);
+    std::string str;
+    if (cstr.length > 0)
+      str = std::string(cstr.str, cstr.length);
+    cef_string_utf8_clear(&cstr);
+    return str;
+  }
+  static inline bool from_string(const std::string& str, struct_type* s) {
+    return cef_string_utf8_to_wide(str.c_str(), str.length(), s) ? true : false;
+  }
+  static inline std::wstring to_wstring(const struct_type* s) {
+    return std::wstring(s->str, s->length);
+  }
+  static inline bool from_wstring(const std::wstring& str, struct_type* s) {
+    return cef_string_wide_set(str.c_str(), str.length(), s, true) ? true
+                                                                   : false;
+  }
+#if defined(WCHAR_T_IS_UTF32)
+  static inline base::string16 to_string16(const struct_type* s) {
+    cef_string_utf16_t cstr;
+    memset(&cstr, 0, sizeof(cstr));
+    cef_string_wide_to_utf16(s->str, s->length, &cstr);
+    base::string16 str;
+    if (cstr.length > 0)
+      str = base::string16(cstr.str, cstr.length);
+    cef_string_utf16_clear(&cstr);
+    return str;
+  }
+  static inline bool from_string16(const base::string16& str, struct_type* s) {
+    return cef_string_utf16_to_wide(str.c_str(), str.length(), s) ? true
+                                                                  : false;
+  }
+#else   // WCHAR_T_IS_UTF32
+  static inline base::string16 to_string16(const struct_type* s) {
+    return base::string16(s->str, s->length);
+  }
+  static inline bool from_string16(const base::string16& str, struct_type* s) {
+    return cef_string_wide_set(str.c_str(), str.length(), s, true) ? true
+                                                                   : false;
+  }
+#endif  // WCHAR_T_IS_UTF32
+};
+
+///
+// Traits implementation for utf8 character strings.
+///
+struct CefStringTraitsUTF8 {
+  typedef char char_type;
+  typedef cef_string_utf8_t struct_type;
+  typedef cef_string_userfree_utf8_t userfree_struct_type;
+
+  static inline void clear(struct_type* s) { cef_string_utf8_clear(s); }
+  static inline int set(const char_type* src,
+                        size_t src_size,
+                        struct_type* output,
+                        int copy) {
+    return cef_string_utf8_set(src, src_size, output, copy);
+  }
+  static inline int compare(const struct_type* s1, const struct_type* s2) {
+    return cef_string_utf8_cmp(s1, s2);
+  }
+  static inline userfree_struct_type userfree_alloc() {
+    return cef_string_userfree_utf8_alloc();
+  }
+  static inline void userfree_free(userfree_struct_type ufs) {
+    return cef_string_userfree_utf8_free(ufs);
+  }
+
+  // Conversion methods.
+  static inline bool from_ascii(const char* str, size_t len, struct_type* s) {
+    return cef_string_utf8_copy(str, len, s) ? true : false;
+  }
+  static inline std::string to_string(const struct_type* s) {
+    return std::string(s->str, s->length);
+  }
+  static inline bool from_string(const std::string& str, struct_type* s) {
+    return cef_string_utf8_copy(str.c_str(), str.length(), s) ? true : false;
+  }
+  static inline std::wstring to_wstring(const struct_type* s) {
+    cef_string_wide_t cstr;
+    memset(&cstr, 0, sizeof(cstr));
+    cef_string_utf8_to_wide(s->str, s->length, &cstr);
+    std::wstring str;
+    if (cstr.length > 0)
+      str = std::wstring(cstr.str, cstr.length);
+    cef_string_wide_clear(&cstr);
+    return str;
+  }
+  static inline bool from_wstring(const std::wstring& str, struct_type* s) {
+    return cef_string_wide_to_utf8(str.c_str(), str.length(), s) ? true : false;
+  }
+  static inline base::string16 to_string16(const struct_type* s) {
+    cef_string_utf16_t cstr;
+    memset(&cstr, 0, sizeof(cstr));
+    cef_string_utf8_to_utf16(s->str, s->length, &cstr);
+    base::string16 str;
+    if (cstr.length > 0)
+      str = base::string16(cstr.str, cstr.length);
+    cef_string_utf16_clear(&cstr);
+    return str;
+  }
+  static inline bool from_string16(const base::string16& str, struct_type* s) {
+    return cef_string_utf16_to_utf8(str.c_str(), str.length(), s) ? true
+                                                                  : false;
+  }
+};
+
+///
+// Traits implementation for utf16 character strings.
+///
+struct CefStringTraitsUTF16 {
+  typedef char16 char_type;
+  typedef cef_string_utf16_t struct_type;
+  typedef cef_string_userfree_utf16_t userfree_struct_type;
+
+  static inline void clear(struct_type* s) { cef_string_utf16_clear(s); }
+  static inline int set(const char_type* src,
+                        size_t src_size,
+                        struct_type* output,
+                        int copy) {
+    return cef_string_utf16_set(src, src_size, output, copy);
+  }
+  static inline int compare(const struct_type* s1, const struct_type* s2) {
+    return cef_string_utf16_cmp(s1, s2);
+  }
+  static inline userfree_struct_type userfree_alloc() {
+    return cef_string_userfree_utf16_alloc();
+  }
+  static inline void userfree_free(userfree_struct_type ufs) {
+    return cef_string_userfree_utf16_free(ufs);
+  }
+
+  // Conversion methods.
+  static inline bool from_ascii(const char* str, size_t len, struct_type* s) {
+    return cef_string_ascii_to_utf16(str, len, s) ? true : false;
+  }
+  static inline std::string to_string(const struct_type* s) {
+    cef_string_utf8_t cstr;
+    memset(&cstr, 0, sizeof(cstr));
+    cef_string_utf16_to_utf8(s->str, s->length, &cstr);
+    std::string str;
+    if (cstr.length > 0)
+      str = std::string(cstr.str, cstr.length);
+    cef_string_utf8_clear(&cstr);
+    return str;
+  }
+  static inline bool from_string(const std::string& str, struct_type* s) {
+    return cef_string_utf8_to_utf16(str.c_str(), str.length(), s) ? true
+                                                                  : false;
+  }
+#if defined(WCHAR_T_IS_UTF32)
+  static inline std::wstring to_wstring(const struct_type* s) {
+    cef_string_wide_t cstr;
+    memset(&cstr, 0, sizeof(cstr));
+    cef_string_utf16_to_wide(s->str, s->length, &cstr);
+    std::wstring str;
+    if (cstr.length > 0)
+      str = std::wstring(cstr.str, cstr.length);
+    cef_string_wide_clear(&cstr);
+    return str;
+  }
+  static inline bool from_wstring(const std::wstring& str, struct_type* s) {
+    return cef_string_wide_to_utf16(str.c_str(), str.length(), s) ? true
+                                                                  : false;
+  }
+#else   // WCHAR_T_IS_UTF32
+  static inline std::wstring to_wstring(const struct_type* s) {
+    return std::wstring(s->str, s->length);
+  }
+  static inline bool from_wstring(const std::wstring& str, struct_type* s) {
+    return cef_string_utf16_set(str.c_str(), str.length(), s, true) ? true
+                                                                    : false;
+  }
+#endif  // WCHAR_T_IS_UTF32
+  static inline base::string16 to_string16(const struct_type* s) {
+    return base::string16(s->str, s->length);
+  }
+  static inline bool from_string16(const base::string16& str, struct_type* s) {
+    return cef_string_utf16_set(str.c_str(), str.length(), s, true) ? true
+                                                                    : false;
+  }
+};
+
+///
+// CEF string classes can convert between all supported string types. For
+// example, the CefStringWide class uses wchar_t as the underlying character
+// type and provides two approaches for converting data to/from a UTF8 string
+// (std::string).
+// <p>
+// 1. Implicit conversion using the assignment operator overload.
+// <pre>
+//   CefStringWide aCefString;
+//   std::string aUTF8String;
+//   aCefString = aUTF8String; // Assign std::string to CefStringWide
+//   aUTF8String = aCefString; // Assign CefStringWide to std::string
+// </pre>
+// 2. Explicit conversion using the FromString/ToString methods.
+// <pre>
+//   CefStringWide aCefString;
+//   std::string aUTF8String;
+//   aCefString.FromString(aUTF8String); // Assign std::string to CefStringWide
+//   aUTF8String = aCefString.ToString(); // Assign CefStringWide to std::string
+// </pre>
+// Conversion will only occur if the assigned value is a different string type.
+// Assigning a std::string to a CefStringUTF8, for example, will copy the data
+// without performing a conversion.
+// </p>
+// CEF string classes are safe for reading from multiple threads but not for
+// modification. It is the user's responsibility to provide synchronization if
+// modifying CEF strings from multiple threads.
+///
+template <class traits>
+class CefStringBase {
+ public:
+  typedef typename traits::char_type char_type;
+  typedef typename traits::struct_type struct_type;
+  typedef typename traits::userfree_struct_type userfree_struct_type;
+
+  ///
+  // Default constructor.
+  ///
+  CefStringBase() : string_(NULL), owner_(false) {}
+
+  ///
+  // Create a new string from an existing string. Data will always be copied.
+  ///
+  CefStringBase(const CefStringBase& str) : string_(NULL), owner_(false) {
+    FromString(str.c_str(), str.length(), true);
+  }
+
+  ///
+  // Create a new string from an existing std::string. Data will be always
+  // copied. Translation will occur if necessary based on the underlying string
+  // type.
+  ///
+  CefStringBase(const std::string& src) : string_(NULL), owner_(false) {
+    FromString(src);
+  }
+  CefStringBase(const char* src) : string_(NULL), owner_(false) {
+    if (src)
+      FromString(std::string(src));
+  }
+
+  ///
+  // Create a new string from an existing std::wstring. Data will be always
+  // copied. Translation will occur if necessary based on the underlying string
+  // type.
+  ///
+  CefStringBase(const std::wstring& src) : string_(NULL), owner_(false) {
+    FromWString(src);
+  }
+  CefStringBase(const wchar_t* src) : string_(NULL), owner_(false) {
+    if (src)
+      FromWString(std::wstring(src));
+  }
+
+#if defined(WCHAR_T_IS_UTF32)
+  ///
+  // Create a new string from an existing string16. Data will be always
+  // copied. Translation will occur if necessary based on the underlying string
+  // type.
+  ///
+  CefStringBase(const base::string16& src) : string_(NULL), owner_(false) {
+    FromString16(src);
+  }
+  CefStringBase(const char16* src) : string_(NULL), owner_(false) {
+    if (src)
+      FromString16(base::string16(src));
+  }
+#endif  // WCHAR_T_IS_UTF32
+
+  ///
+  // Create a new string from an existing character array. If |copy| is true
+  // this class will copy the data. Otherwise, this class will reference the
+  // existing data. Referenced data must exist for the lifetime of this class
+  // and will not be freed by this class.
+  ///
+  CefStringBase(const char_type* src, size_t src_len, bool copy)
+      : string_(NULL), owner_(false) {
+    if (src && src_len > 0)
+      FromString(src, src_len, copy);
+  }
+
+  ///
+  // Create a new string referencing an existing string structure without taking
+  // ownership. Referenced structures must exist for the lifetime of this class
+  // and will not be freed by this class.
+  ///
+  CefStringBase(const struct_type* src) : string_(NULL), owner_(false) {
+    if (!src)
+      return;
+    // Reference the existing structure without taking ownership.
+    Attach(const_cast<struct_type*>(src), false);
+  }
+
+  virtual ~CefStringBase() { ClearAndFree(); }
+
+  // The following methods are named for compatibility with the standard library
+  // string template types.
+
+  ///
+  // Return a read-only pointer to the string data.
+  ///
+  const char_type* c_str() const { return (string_ ? string_->str : NULL); }
+
+  ///
+  // Return the length of the string data.
+  ///
+  size_t length() const { return (string_ ? string_->length : 0); }
+
+  ///
+  // Return the length of the string data.
+  ///
+  inline size_t size() const { return length(); }
+
+  ///
+  // Returns true if the string is empty.
+  ///
+  bool empty() const { return (string_ == NULL || string_->length == 0); }
+
+  ///
+  // Compare this string to the specified string.
+  ///
+  int compare(const CefStringBase& str) const {
+    if (empty() && str.empty())
+      return 0;
+    if (empty())
+      return -1;
+    if (str.empty())
+      return 1;
+    return traits::compare(string_, str.GetStruct());
+  }
+
+  ///
+  // Clear the string data.
+  ///
+  void clear() {
+    if (string_)
+      traits::clear(string_);
+  }
+
+  ///
+  // Swap this string's contents with the specified string.
+  ///
+  void swap(CefStringBase& str) {
+    struct_type* tmp_string = string_;
+    bool tmp_owner = owner_;
+    string_ = str.string_;
+    owner_ = str.owner_;
+    str.string_ = tmp_string;
+    str.owner_ = tmp_owner;
+  }
+
+  // The following methods are unique to CEF string template types.
+
+  ///
+  // Returns true if this class owns the underlying string structure.
+  ///
+  bool IsOwner() const { return owner_; }
+
+  ///
+  // Returns a read-only pointer to the underlying string structure. May return
+  // NULL if no structure is currently allocated.
+  ///
+  const struct_type* GetStruct() const { return string_; }
+
+  ///
+  // Returns a writable pointer to the underlying string structure. Will never
+  // return NULL.
+  ///
+  struct_type* GetWritableStruct() {
+    AllocIfNeeded();
+    return string_;
+  }
+
+  ///
+  // Clear the state of this class. The underlying string structure and data
+  // will be freed if this class owns the structure.
+  ///
+  void ClearAndFree() {
+    if (!string_)
+      return;
+    if (owner_) {
+      clear();
+      delete string_;
+    }
+    string_ = NULL;
+    owner_ = false;
+  }
+
+  ///
+  // Attach to the specified string structure. If |owner| is true this class
+  // will take ownership of the structure.
+  ///
+  void Attach(struct_type* str, bool owner) {
+    // Free the previous structure and data, if any.
+    ClearAndFree();
+
+    string_ = str;
+    owner_ = owner;
+  }
+
+  ///
+  // Take ownership of the specified userfree structure's string data. The
+  // userfree structure itself will be freed. Only use this method with userfree
+  // structures.
+  ///
+  void AttachToUserFree(userfree_struct_type str) {
+    // Free the previous structure and data, if any.
+    ClearAndFree();
+
+    if (!str)
+      return;
+
+    AllocIfNeeded();
+    owner_ = true;
+    memcpy(string_, str, sizeof(struct_type));
+
+    // Free the |str| structure but not the data.
+    memset(str, 0, sizeof(struct_type));
+    traits::userfree_free(str);
+  }
+
+  ///
+  // Detach from the underlying string structure. To avoid memory leaks only use
+  // this method if you already hold a pointer to the underlying string
+  // structure.
+  ///
+  void Detach() {
+    string_ = NULL;
+    owner_ = false;
+  }
+
+  ///
+  // Create a userfree structure and give it ownership of this class' string
+  // data. This class will be disassociated from the data. May return NULL if
+  // this string class currently contains no data.
+  ///
+  userfree_struct_type DetachToUserFree() {
+    if (empty())
+      return NULL;
+
+    userfree_struct_type str = traits::userfree_alloc();
+    memcpy(str, string_, sizeof(struct_type));
+
+    // Free this class' structure but not the data.
+    memset(string_, 0, sizeof(struct_type));
+    ClearAndFree();
+
+    return str;
+  }
+
+  ///
+  // Set this string's data to the specified character array. If |copy| is true
+  // this class will copy the data. Otherwise, this class will reference the
+  // existing data. Referenced data must exist for the lifetime of this class
+  // and will not be freed by this class.
+  ///
+  bool FromString(const char_type* src, size_t src_len, bool copy) {
+    if (src == NULL || src_len == 0) {
+      clear();
+      return true;
+    }
+    AllocIfNeeded();
+    return traits::set(src, src_len, string_, copy) ? true : false;
+  }
+
+  ///
+  // Set this string's data from an existing ASCII string. Data will be always
+  // copied. Translation will occur if necessary based on the underlying string
+  // type.
+  ///
+  bool FromASCII(const char* str) {
+    size_t len = str ? strlen(str) : 0;
+    if (len == 0) {
+      clear();
+      return true;
+    }
+    AllocIfNeeded();
+    return traits::from_ascii(str, len, string_);
+  }
+
+  ///
+  // Return this string's data as a std::string. Translation will occur if
+  // necessary based on the underlying string type.
+  ///
+  std::string ToString() const {
+    if (empty())
+      return std::string();
+    return traits::to_string(string_);
+  }
+
+  ///
+  // Set this string's data from an existing std::string. Data will be always
+  // copied. Translation will occur if necessary based on the underlying string
+  // type.
+  ///
+  bool FromString(const std::string& str) {
+    if (str.empty()) {
+      clear();
+      return true;
+    }
+    AllocIfNeeded();
+    return traits::from_string(str, string_);
+  }
+
+  ///
+  // Return this string's data as a std::wstring. Translation will occur if
+  // necessary based on the underlying string type.
+  ///
+  std::wstring ToWString() const {
+    if (empty())
+      return std::wstring();
+    return traits::to_wstring(string_);
+  }
+
+  ///
+  // Set this string's data from an existing std::wstring. Data will be always
+  // copied. Translation will occur if necessary based on the underlying string
+  // type.
+  ///
+  bool FromWString(const std::wstring& str) {
+    if (str.empty()) {
+      clear();
+      return true;
+    }
+    AllocIfNeeded();
+    return traits::from_wstring(str, string_);
+  }
+
+  ///
+  // Return this string's data as a string16. Translation will occur if
+  // necessary based on the underlying string type.
+  ///
+  base::string16 ToString16() const {
+    if (empty())
+      return base::string16();
+    return traits::to_string16(string_);
+  }
+
+  ///
+  // Set this string's data from an existing string16. Data will be always
+  // copied. Translation will occur if necessary based on the underlying string
+  // type.
+  ///
+  bool FromString16(const base::string16& str) {
+    if (str.empty()) {
+      clear();
+      return true;
+    }
+    AllocIfNeeded();
+    return traits::from_string16(str, string_);
+  }
+
+  ///
+  // Comparison operator overloads.
+  ///
+  bool operator<(const CefStringBase& str) const { return (compare(str) < 0); }
+  bool operator<=(const CefStringBase& str) const {
+    return (compare(str) <= 0);
+  }
+  bool operator>(const CefStringBase& str) const { return (compare(str) > 0); }
+  bool operator>=(const CefStringBase& str) const {
+    return (compare(str) >= 0);
+  }
+  bool operator==(const CefStringBase& str) const {
+    return (compare(str) == 0);
+  }
+  bool operator!=(const CefStringBase& str) const {
+    return (compare(str) != 0);
+  }
+
+  ///
+  // Assignment operator overloads.
+  ///
+  CefStringBase& operator=(const CefStringBase& str) {
+    FromString(str.c_str(), str.length(), true);
+    return *this;
+  }
+  operator std::string() const { return ToString(); }
+  CefStringBase& operator=(const std::string& str) {
+    FromString(str);
+    return *this;
+  }
+  CefStringBase& operator=(const char* str) {
+    FromString(std::string(str));
+    return *this;
+  }
+  operator std::wstring() const { return ToWString(); }
+  CefStringBase& operator=(const std::wstring& str) {
+    FromWString(str);
+    return *this;
+  }
+  CefStringBase& operator=(const wchar_t* str) {
+    FromWString(std::wstring(str));
+    return *this;
+  }
+#if defined(WCHAR_T_IS_UTF32)
+  operator base::string16() const { return ToString16(); }
+  CefStringBase& operator=(const base::string16& str) {
+    FromString16(str);
+    return *this;
+  }
+  CefStringBase& operator=(const char16* str) {
+    FromString16(base::string16(str));
+    return *this;
+  }
+#endif  // WCHAR_T_IS_UTF32
+#if defined(USING_CHROMIUM_INCLUDES)
+  // The base::FilePath constructor is marked as explicit so provide the
+  // conversion here for convenience.
+  operator base::FilePath() const {
+#if defined(OS_WIN)
+    return base::FilePath(ToWString());
+#else
+    return base::FilePath(ToString());
+#endif
+  }
+#endif  // USING_CHROMIUM_INCLUDES
+
+ private:
+  // Allocate the string structure if it doesn't already exist.
+  void AllocIfNeeded() {
+    if (string_ == NULL) {
+      string_ = new struct_type;
+      memset(string_, 0, sizeof(struct_type));
+      owner_ = true;
+    }
+  }
+
+  struct_type* string_;
+  bool owner_;
+};
+
+typedef CefStringBase<CefStringTraitsWide> CefStringWide;
+typedef CefStringBase<CefStringTraitsUTF8> CefStringUTF8;
+typedef CefStringBase<CefStringTraitsUTF16> CefStringUTF16;
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_STRING_WRAPPERS_H_
diff --git a/src/include/internal/cef_thread_internal.h b/src/include/internal/cef_thread_internal.h
new file mode 100644
index 0000000..f326bcd
--- /dev/null
+++ b/src/include/internal/cef_thread_internal.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_THREAD_INTERNAL_H_
+#define CEF_INCLUDE_INTERNAL_CEF_THREAD_INTERNAL_H_
+#pragma once
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+#include "include/internal/cef_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(OS_WIN)
+typedef DWORD cef_platform_thread_id_t;
+#define kInvalidPlatformThreadId 0U
+#elif defined(OS_POSIX)
+typedef pid_t cef_platform_thread_id_t;
+#define kInvalidPlatformThreadId 0
+#endif
+
+///
+// Returns the current platform thread ID.
+///
+CEF_EXPORT cef_platform_thread_id_t cef_get_current_platform_thread_id();
+
+#if defined(OS_WIN)
+typedef DWORD cef_platform_thread_handle_t;
+#define kInvalidPlatformThreadHandle 0U
+#elif defined(OS_POSIX)
+typedef pthread_t cef_platform_thread_handle_t;
+#define kInvalidPlatformThreadHandle 0
+#endif
+
+///
+// Returns the current platform thread handle.
+///
+CEF_EXPORT cef_platform_thread_handle_t
+cef_get_current_platform_thread_handle();
+
+#ifdef __cplusplus
+}
+#endif  // __cplusplus
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_THREAD_INTERNAL_H_
diff --git a/src/include/internal/cef_time.h b/src/include/internal/cef_time.h
new file mode 100644
index 0000000..5435e59
--- /dev/null
+++ b/src/include/internal/cef_time.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_TIME_H_
+#define CEF_INCLUDE_INTERNAL_CEF_TIME_H_
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <time.h>
+#include "include/internal/cef_export.h"
+
+///
+// Time information. Values should always be in UTC.
+///
+typedef struct _cef_time_t {
+  int year;          // Four or five digit year "2007" (1601 to 30827 on
+                     //   Windows, 1970 to 2038 on 32-bit POSIX)
+  int month;         // 1-based month (values 1 = January, etc.)
+  int day_of_week;   // 0-based day of week (0 = Sunday, etc.)
+  int day_of_month;  // 1-based day of month (1-31)
+  int hour;          // Hour within the current day (0-23)
+  int minute;        // Minute within the current hour (0-59)
+  int second;        // Second within the current minute (0-59 plus leap
+                     //   seconds which may take it up to 60).
+  int millisecond;   // Milliseconds within the current second (0-999)
+} cef_time_t;
+
+///
+// Converts cef_time_t to/from time_t. Returns true (1) on success and false (0)
+// on failure.
+///
+CEF_EXPORT int cef_time_to_timet(const cef_time_t* cef_time, time_t* time);
+CEF_EXPORT int cef_time_from_timet(time_t time, cef_time_t* cef_time);
+
+///
+// Converts cef_time_t to/from a double which is the number of seconds since
+// epoch (Jan 1, 1970). Webkit uses this format to represent time. A value of 0
+// means "not initialized". Returns true (1) on success and false (0) on
+// failure.
+///
+CEF_EXPORT int cef_time_to_doublet(const cef_time_t* cef_time, double* time);
+CEF_EXPORT int cef_time_from_doublet(double time, cef_time_t* cef_time);
+
+///
+// Retrieve the current system time.
+//
+CEF_EXPORT int cef_time_now(cef_time_t* cef_time);
+
+///
+// Retrieve the delta in milliseconds between two time values.
+//
+CEF_EXPORT int cef_time_delta(const cef_time_t* cef_time1,
+                              const cef_time_t* cef_time2,
+                              long long* delta);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_TIME_H_
diff --git a/src/include/internal/cef_trace_event_internal.h b/src/include/internal/cef_trace_event_internal.h
new file mode 100644
index 0000000..6df8707
--- /dev/null
+++ b/src/include/internal/cef_trace_event_internal.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_TRACE_EVENT_INTERNAL_H_
+#define CEF_INCLUDE_INTERNAL_CEF_TRACE_EVENT_INTERNAL_H_
+#pragma once
+
+#include "include/internal/cef_export.h"
+#include "include/internal/cef_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// See include/base/cef_trace_event.h for macros and intended usage.
+
+// Functions for tracing counters and functions; called from macros.
+// - |category| string must have application lifetime (static or literal). They
+//   may not include "(quotes) chars.
+// - |argX_name|, |argX_val|, |valueX_name|, |valeX_val| are optional parameters
+//   and represent pairs of name and values of arguments
+// - |copy| is used to avoid memory scoping issues with the |name| and
+//   |arg_name| parameters by copying them
+// - |id| is used to disambiguate counters with the same name, or match async
+//   trace events
+
+CEF_EXPORT void cef_trace_event_instant(const char* category,
+                                        const char* name,
+                                        const char* arg1_name,
+                                        uint64 arg1_val,
+                                        const char* arg2_name,
+                                        uint64 arg2_val,
+                                        int copy);
+CEF_EXPORT void cef_trace_event_begin(const char* category,
+                                      const char* name,
+                                      const char* arg1_name,
+                                      uint64 arg1_val,
+                                      const char* arg2_name,
+                                      uint64 arg2_val,
+                                      int copy);
+CEF_EXPORT void cef_trace_event_end(const char* category,
+                                    const char* name,
+                                    const char* arg1_name,
+                                    uint64 arg1_val,
+                                    const char* arg2_name,
+                                    uint64 arg2_val,
+                                    int copy);
+CEF_EXPORT void cef_trace_counter(const char* category,
+                                  const char* name,
+                                  const char* value1_name,
+                                  uint64 value1_val,
+                                  const char* value2_name,
+                                  uint64 value2_val,
+                                  int copy);
+CEF_EXPORT void cef_trace_counter_id(const char* category,
+                                     const char* name,
+                                     uint64 id,
+                                     const char* value1_name,
+                                     uint64 value1_val,
+                                     const char* value2_name,
+                                     uint64 value2_val,
+                                     int copy);
+CEF_EXPORT void cef_trace_event_async_begin(const char* category,
+                                            const char* name,
+                                            uint64 id,
+                                            const char* arg1_name,
+                                            uint64 arg1_val,
+                                            const char* arg2_name,
+                                            uint64 arg2_val,
+                                            int copy);
+CEF_EXPORT void cef_trace_event_async_step_into(const char* category,
+                                                const char* name,
+                                                uint64 id,
+                                                uint64 step,
+                                                const char* arg1_name,
+                                                uint64 arg1_val,
+                                                int copy);
+CEF_EXPORT void cef_trace_event_async_step_past(const char* category,
+                                                const char* name,
+                                                uint64 id,
+                                                uint64 step,
+                                                const char* arg1_name,
+                                                uint64 arg1_val,
+                                                int copy);
+CEF_EXPORT void cef_trace_event_async_end(const char* category,
+                                          const char* name,
+                                          uint64 id,
+                                          const char* arg1_name,
+                                          uint64 arg1_val,
+                                          const char* arg2_name,
+                                          uint64 arg2_val,
+                                          int copy);
+
+#ifdef __cplusplus
+}
+#endif  // __cplusplus
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_TRACE_EVENT_INTERNAL_H_
diff --git a/src/include/internal/cef_types.h b/src/include/internal/cef_types.h
new file mode 100644
index 0000000..ab4c495
--- /dev/null
+++ b/src/include/internal/cef_types.h
@@ -0,0 +1,3196 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_H_
+#define CEF_INCLUDE_INTERNAL_CEF_TYPES_H_
+#pragma once
+
+#include "include/base/cef_basictypes.h"
+#include "include/internal/cef_string.h"
+#include "include/internal/cef_string_list.h"
+#include "include/internal/cef_time.h"
+
+// Bring in platform-specific definitions.
+#if defined(OS_WIN)
+#include "include/internal/cef_types_win.h"
+#elif defined(OS_MACOSX)
+#include "include/internal/cef_types_mac.h"
+#elif defined(OS_LINUX)
+#include "include/internal/cef_types_linux.h"
+#endif
+
+// 32-bit ARGB color value, not premultiplied. The color components are always
+// in a known order. Equivalent to the SkColor type.
+typedef uint32 cef_color_t;
+
+// Return the alpha byte from a cef_color_t value.
+#define CefColorGetA(color) (((color) >> 24) & 0xFF)
+// Return the red byte from a cef_color_t value.
+#define CefColorGetR(color) (((color) >> 16) & 0xFF)
+// Return the green byte from a cef_color_t value.
+#define CefColorGetG(color) (((color) >> 8) & 0xFF)
+// Return the blue byte from a cef_color_t value.
+#define CefColorGetB(color) (((color) >> 0) & 0xFF)
+
+// Return an cef_color_t value with the specified byte component values.
+#define CefColorSetARGB(a, r, g, b)                                         \
+  static_cast<cef_color_t>(                                                 \
+      (static_cast<unsigned>(a) << 24) | (static_cast<unsigned>(r) << 16) | \
+      (static_cast<unsigned>(g) << 8) | (static_cast<unsigned>(b) << 0))
+
+// Return an int64 value with the specified low and high int32 component values.
+#define CefInt64Set(int32_low, int32_high)                                \
+  static_cast<int64>((static_cast<uint32>(int32_low)) |                   \
+                     (static_cast<int64>(static_cast<int32>(int32_high))) \
+                         << 32)
+
+// Return the low int32 value from an int64 value.
+#define CefInt64GetLow(int64_val) static_cast<int32>(int64_val)
+// Return the high int32 value from an int64 value.
+#define CefInt64GetHigh(int64_val) \
+  static_cast<int32>((static_cast<int64>(int64_val) >> 32) & 0xFFFFFFFFL)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Log severity levels.
+///
+typedef enum {
+  ///
+  // Default logging (currently INFO logging).
+  ///
+  LOGSEVERITY_DEFAULT,
+
+  ///
+  // Verbose logging.
+  ///
+  LOGSEVERITY_VERBOSE,
+
+  ///
+  // DEBUG logging.
+  ///
+  LOGSEVERITY_DEBUG = LOGSEVERITY_VERBOSE,
+
+  ///
+  // INFO logging.
+  ///
+  LOGSEVERITY_INFO,
+
+  ///
+  // WARNING logging.
+  ///
+  LOGSEVERITY_WARNING,
+
+  ///
+  // ERROR logging.
+  ///
+  LOGSEVERITY_ERROR,
+
+  ///
+  // FATAL logging.
+  ///
+  LOGSEVERITY_FATAL,
+
+  ///
+  // Disable logging to file for all messages, and to stderr for messages with
+  // severity less than FATAL.
+  ///
+  LOGSEVERITY_DISABLE = 99
+} cef_log_severity_t;
+
+///
+// Represents the state of a setting.
+///
+typedef enum {
+  ///
+  // Use the default state for the setting.
+  ///
+  STATE_DEFAULT = 0,
+
+  ///
+  // Enable or allow the setting.
+  ///
+  STATE_ENABLED,
+
+  ///
+  // Disable or disallow the setting.
+  ///
+  STATE_DISABLED,
+} cef_state_t;
+
+///
+// Initialization settings. Specify NULL or 0 to get the recommended default
+// values. Many of these and other settings can also configured using command-
+// line switches.
+///
+typedef struct _cef_settings_t {
+  ///
+  // Size of this structure.
+  ///
+  size_t size;
+
+  ///
+  // Set to true (1) to disable the sandbox for sub-processes. See
+  // cef_sandbox_win.h for requirements to enable the sandbox on Windows. Also
+  // configurable using the "no-sandbox" command-line switch.
+  ///
+  int no_sandbox;
+
+  ///
+  // The path to a separate executable that will be launched for sub-processes.
+  // If this value is empty on Windows or Linux then the main process executable
+  // will be used. If this value is empty on macOS then a helper executable must
+  // exist at "Contents/Frameworks/<app> Helper.app/Contents/MacOS/<app> Helper"
+  // in the top-level app bundle. See the comments on CefExecuteProcess() for
+  // details. If this value is non-empty then it must be an absolute path. Also
+  // configurable using the "browser-subprocess-path" command-line switch.
+  ///
+  cef_string_t browser_subprocess_path;
+
+  ///
+  // The path to the CEF framework directory on macOS. If this value is empty
+  // then the framework must exist at "Contents/Frameworks/Chromium Embedded
+  // Framework.framework" in the top-level app bundle. If this value is
+  // non-empty then it must be an absolute path. Also configurable using the
+  // "framework-dir-path" command-line switch.
+  ///
+  cef_string_t framework_dir_path;
+
+  ///
+  // The path to the main bundle on macOS. If this value is empty then it
+  // defaults to the top-level app bundle. If this value is non-empty then it
+  // must be an absolute path. Also configurable using the "main-bundle-path"
+  // command-line switch.
+  ///
+  cef_string_t main_bundle_path;
+
+  ///
+  // Set to true (1) to have the browser process message loop run in a separate
+  // thread. If false (0) than the CefDoMessageLoopWork() function must be
+  // called from your application message loop. This option is only supported on
+  // Windows and Linux.
+  ///
+  int multi_threaded_message_loop;
+
+  ///
+  // Set to true (1) to control browser process main (UI) thread message pump
+  // scheduling via the CefBrowserProcessHandler::OnScheduleMessagePumpWork()
+  // callback. This option is recommended for use in combination with the
+  // CefDoMessageLoopWork() function in cases where the CEF message loop must be
+  // integrated into an existing application message loop (see additional
+  // comments and warnings on CefDoMessageLoopWork). Enabling this option is not
+  // recommended for most users; leave this option disabled and use either the
+  // CefRunMessageLoop() function or multi_threaded_message_loop if possible.
+  ///
+  int external_message_pump;
+
+  ///
+  // Set to true (1) to enable windowless (off-screen) rendering support. Do not
+  // enable this value if the application does not use windowless rendering as
+  // it may reduce rendering performance on some systems.
+  ///
+  int windowless_rendering_enabled;
+
+  ///
+  // Set to true (1) to disable configuration of browser process features using
+  // standard CEF and Chromium command-line arguments. Configuration can still
+  // be specified using CEF data structures or via the
+  // CefApp::OnBeforeCommandLineProcessing() method.
+  ///
+  int command_line_args_disabled;
+
+  ///
+  // The location where data for the global browser cache will be stored on
+  // disk. If this value is non-empty then it must be an absolute path that is
+  // either equal to or a child directory of CefSettings.root_cache_path. If
+  // this value is empty then browsers will be created in "incognito mode" where
+  // in-memory caches are used for storage and no data is persisted to disk.
+  // HTML5 databases such as localStorage will only persist across sessions if a
+  // cache path is specified. Can be overridden for individual CefRequestContext
+  // instances via the CefRequestContextSettings.cache_path value.
+  ///
+  cef_string_t cache_path;
+
+  ///
+  // The root directory that all CefSettings.cache_path and
+  // CefRequestContextSettings.cache_path values must have in common. If this
+  // value is empty and CefSettings.cache_path is non-empty then it will
+  // default to the CefSettings.cache_path value. If this value is non-empty
+  // then it must be an absolute path. Failure to set this value correctly may
+  // result in the sandbox blocking read/write access to the cache_path
+  // directory.
+  ///
+  cef_string_t root_cache_path;
+
+  ///
+  // The location where user data such as spell checking dictionary files will
+  // be stored on disk. If this value is empty then the default
+  // platform-specific user data directory will be used ("~/.cef_user_data"
+  // directory on Linux, "~/Library/Application Support/CEF/User Data" directory
+  // on Mac OS X, "Local Settings\Application Data\CEF\User Data" directory
+  // under the user profile directory on Windows). If this value is non-empty
+  // then it must be an absolute path.
+  ///
+  cef_string_t user_data_path;
+
+  ///
+  // To persist session cookies (cookies without an expiry date or validity
+  // interval) by default when using the global cookie manager set this value to
+  // true (1). Session cookies are generally intended to be transient and most
+  // Web browsers do not persist them. A |cache_path| value must also be
+  // specified to enable this feature. Also configurable using the
+  // "persist-session-cookies" command-line switch. Can be overridden for
+  // individual CefRequestContext instances via the
+  // CefRequestContextSettings.persist_session_cookies value.
+  ///
+  int persist_session_cookies;
+
+  ///
+  // To persist user preferences as a JSON file in the cache path directory set
+  // this value to true (1). A |cache_path| value must also be specified
+  // to enable this feature. Also configurable using the
+  // "persist-user-preferences" command-line switch. Can be overridden for
+  // individual CefRequestContext instances via the
+  // CefRequestContextSettings.persist_user_preferences value.
+  ///
+  int persist_user_preferences;
+
+  ///
+  // Value that will be returned as the User-Agent HTTP header. If empty the
+  // default User-Agent string will be used. Also configurable using the
+  // "user-agent" command-line switch.
+  ///
+  cef_string_t user_agent;
+
+  ///
+  // Value that will be inserted as the product portion of the default
+  // User-Agent string. If empty the Chromium product version will be used. If
+  // |userAgent| is specified this value will be ignored. Also configurable
+  // using the "product-version" command-line switch.
+  ///
+  cef_string_t product_version;
+
+  ///
+  // The locale string that will be passed to WebKit. If empty the default
+  // locale of "en-US" will be used. This value is ignored on Linux where locale
+  // is determined using environment variable parsing with the precedence order:
+  // LANGUAGE, LC_ALL, LC_MESSAGES and LANG. Also configurable using the "lang"
+  // command-line switch.
+  ///
+  cef_string_t locale;
+
+  ///
+  // The directory and file name to use for the debug log. If empty a default
+  // log file name and location will be used. On Windows and Linux a "debug.log"
+  // file will be written in the main executable directory. On Mac OS X a
+  // "~/Library/Logs/<app name>_debug.log" file will be written where <app name>
+  // is the name of the main app executable. Also configurable using the
+  // "log-file" command-line switch.
+  ///
+  cef_string_t log_file;
+
+  ///
+  // The log severity. Only messages of this severity level or higher will be
+  // logged. When set to DISABLE no messages will be written to the log file,
+  // but FATAL messages will still be output to stderr. Also configurable using
+  // the "log-severity" command-line switch with a value of "verbose", "info",
+  // "warning", "error", "fatal" or "disable".
+  ///
+  cef_log_severity_t log_severity;
+
+  ///
+  // Custom flags that will be used when initializing the V8 JavaScript engine.
+  // The consequences of using custom flags may not be well tested. Also
+  // configurable using the "js-flags" command-line switch.
+  ///
+  cef_string_t javascript_flags;
+
+  ///
+  // The fully qualified path for the resources directory. If this value is
+  // empty the cef.pak and/or devtools_resources.pak files must be located in
+  // the module directory on Windows/Linux or the app bundle Resources directory
+  // on Mac OS X. If this value is non-empty then it must be an absolute path.
+  // Also configurable using the "resources-dir-path" command-line switch.
+  ///
+  cef_string_t resources_dir_path;
+
+  ///
+  // The fully qualified path for the locales directory. If this value is empty
+  // the locales directory must be located in the module directory. If this
+  // value is non-empty then it must be an absolute path. This value is ignored
+  // on Mac OS X where pack files are always loaded from the app bundle
+  // Resources directory. Also configurable using the "locales-dir-path"
+  // command-line switch.
+  ///
+  cef_string_t locales_dir_path;
+
+  ///
+  // Set to true (1) to disable loading of pack files for resources and locales.
+  // A resource bundle handler must be provided for the browser and render
+  // processes via CefApp::GetResourceBundleHandler() if loading of pack files
+  // is disabled. Also configurable using the "disable-pack-loading" command-
+  // line switch.
+  ///
+  int pack_loading_disabled;
+
+  ///
+  // Set to a value between 1024 and 65535 to enable remote debugging on the
+  // specified port. For example, if 8080 is specified the remote debugging URL
+  // will be http://localhost:8080. CEF can be remotely debugged from any CEF or
+  // Chrome browser window. Also configurable using the "remote-debugging-port"
+  // command-line switch.
+  ///
+  int remote_debugging_port;
+
+  ///
+  // The number of stack trace frames to capture for uncaught exceptions.
+  // Specify a positive value to enable the CefRenderProcessHandler::
+  // OnUncaughtException() callback. Specify 0 (default value) and
+  // OnUncaughtException() will not be called. Also configurable using the
+  // "uncaught-exception-stack-size" command-line switch.
+  ///
+  int uncaught_exception_stack_size;
+
+  ///
+  // Set to true (1) to ignore errors related to invalid SSL certificates.
+  // Enabling this setting can lead to potential security vulnerabilities like
+  // "man in the middle" attacks. Applications that load content from the
+  // internet should not enable this setting. Also configurable using the
+  // "ignore-certificate-errors" command-line switch. Can be overridden for
+  // individual CefRequestContext instances via the
+  // CefRequestContextSettings.ignore_certificate_errors value.
+  ///
+  int ignore_certificate_errors;
+
+  ///
+  // Background color used for the browser before a document is loaded and when
+  // no document color is specified. The alpha component must be either fully
+  // opaque (0xFF) or fully transparent (0x00). If the alpha component is fully
+  // opaque then the RGB components will be used as the background color. If the
+  // alpha component is fully transparent for a windowed browser then the
+  // default value of opaque white be used. If the alpha component is fully
+  // transparent for a windowless (off-screen) browser then transparent painting
+  // will be enabled.
+  ///
+  cef_color_t background_color;
+
+  ///
+  // Comma delimited ordered list of language codes without any whitespace that
+  // will be used in the "Accept-Language" HTTP header. May be overridden on a
+  // per-browser basis using the CefBrowserSettings.accept_language_list value.
+  // If both values are empty then "en-US,en" will be used. Can be overridden
+  // for individual CefRequestContext instances via the
+  // CefRequestContextSettings.accept_language_list value.
+  ///
+  cef_string_t accept_language_list;
+
+  ///
+  // GUID string used for identifying the application. This is passed to the
+  // system AV function for scanning downloaded files. By default, the GUID
+  // will be an empty string and the file will be treated as an untrusted
+  // file when the GUID is empty.
+  ///
+  cef_string_t application_client_id_for_file_scanning;
+} cef_settings_t;
+
+///
+// Request context initialization settings. Specify NULL or 0 to get the
+// recommended default values.
+///
+typedef struct _cef_request_context_settings_t {
+  ///
+  // Size of this structure.
+  ///
+  size_t size;
+
+  ///
+  // The location where cache data for this request context will be stored on
+  // disk. If this value is non-empty then it must be an absolute path that is
+  // either equal to or a child directory of CefSettings.root_cache_path. If
+  // this value is empty then browsers will be created in "incognito mode" where
+  // in-memory caches are used for storage and no data is persisted to disk.
+  // HTML5 databases such as localStorage will only persist across sessions if a
+  // cache path is specified. To share the global browser cache and related
+  // configuration set this value to match the CefSettings.cache_path value.
+  ///
+  cef_string_t cache_path;
+
+  ///
+  // To persist session cookies (cookies without an expiry date or validity
+  // interval) by default when using the global cookie manager set this value to
+  // true (1). Session cookies are generally intended to be transient and most
+  // Web browsers do not persist them. Can be set globally using the
+  // CefSettings.persist_session_cookies value. This value will be ignored if
+  // |cache_path| is empty or if it matches the CefSettings.cache_path value.
+  ///
+  int persist_session_cookies;
+
+  ///
+  // To persist user preferences as a JSON file in the cache path directory set
+  // this value to true (1). Can be set globally using the
+  // CefSettings.persist_user_preferences value. This value will be ignored if
+  // |cache_path| is empty or if it matches the CefSettings.cache_path value.
+  ///
+  int persist_user_preferences;
+
+  ///
+  // Set to true (1) to ignore errors related to invalid SSL certificates.
+  // Enabling this setting can lead to potential security vulnerabilities like
+  // "man in the middle" attacks. Applications that load content from the
+  // internet should not enable this setting. Can be set globally using the
+  // CefSettings.ignore_certificate_errors value. This value will be ignored if
+  // |cache_path| matches the CefSettings.cache_path value.
+  ///
+  int ignore_certificate_errors;
+
+  ///
+  // Comma delimited ordered list of language codes without any whitespace that
+  // will be used in the "Accept-Language" HTTP header. Can be set globally
+  // using the CefSettings.accept_language_list value or overridden on a per-
+  // browser basis using the CefBrowserSettings.accept_language_list value. If
+  // all values are empty then "en-US,en" will be used. This value will be
+  // ignored if |cache_path| matches the CefSettings.cache_path value.
+  ///
+  cef_string_t accept_language_list;
+} cef_request_context_settings_t;
+
+///
+// Browser initialization settings. Specify NULL or 0 to get the recommended
+// default values. The consequences of using custom values may not be well
+// tested. Many of these and other settings can also configured using command-
+// line switches.
+///
+typedef struct _cef_browser_settings_t {
+  ///
+  // Size of this structure.
+  ///
+  size_t size;
+
+  ///
+  // The maximum rate in frames per second (fps) that CefRenderHandler::OnPaint
+  // will be called for a windowless browser. The actual fps may be lower if
+  // the browser cannot generate frames at the requested rate. The minimum
+  // value is 1 and the maximum value is 60 (default 30). This value can also be
+  // changed dynamically via CefBrowserHost::SetWindowlessFrameRate.
+  ///
+  int windowless_frame_rate;
+
+  // The below values map to WebPreferences settings.
+
+  ///
+  // Font settings.
+  ///
+  cef_string_t standard_font_family;
+  cef_string_t fixed_font_family;
+  cef_string_t serif_font_family;
+  cef_string_t sans_serif_font_family;
+  cef_string_t cursive_font_family;
+  cef_string_t fantasy_font_family;
+  int default_font_size;
+  int default_fixed_font_size;
+  int minimum_font_size;
+  int minimum_logical_font_size;
+
+  ///
+  // Default encoding for Web content. If empty "ISO-8859-1" will be used. Also
+  // configurable using the "default-encoding" command-line switch.
+  ///
+  cef_string_t default_encoding;
+
+  ///
+  // Controls the loading of fonts from remote sources. Also configurable using
+  // the "disable-remote-fonts" command-line switch.
+  ///
+  cef_state_t remote_fonts;
+
+  ///
+  // Controls whether JavaScript can be executed. Also configurable using the
+  // "disable-javascript" command-line switch.
+  ///
+  cef_state_t javascript;
+
+  ///
+  // Controls whether JavaScript can be used to close windows that were not
+  // opened via JavaScript. JavaScript can still be used to close windows that
+  // were opened via JavaScript or that have no back/forward history. Also
+  // configurable using the "disable-javascript-close-windows" command-line
+  // switch.
+  ///
+  cef_state_t javascript_close_windows;
+
+  ///
+  // Controls whether JavaScript can access the clipboard. Also configurable
+  // using the "disable-javascript-access-clipboard" command-line switch.
+  ///
+  cef_state_t javascript_access_clipboard;
+
+  ///
+  // Controls whether DOM pasting is supported in the editor via
+  // execCommand("paste"). The |javascript_access_clipboard| setting must also
+  // be enabled. Also configurable using the "disable-javascript-dom-paste"
+  // command-line switch.
+  ///
+  cef_state_t javascript_dom_paste;
+
+  ///
+  // Controls whether any plugins will be loaded. Also configurable using the
+  // "disable-plugins" command-line switch.
+  ///
+  cef_state_t plugins;
+
+  ///
+  // Controls whether file URLs will have access to all URLs. Also configurable
+  // using the "allow-universal-access-from-files" command-line switch.
+  ///
+  cef_state_t universal_access_from_file_urls;
+
+  ///
+  // Controls whether file URLs will have access to other file URLs. Also
+  // configurable using the "allow-access-from-files" command-line switch.
+  ///
+  cef_state_t file_access_from_file_urls;
+
+  ///
+  // Controls whether web security restrictions (same-origin policy) will be
+  // enforced. Disabling this setting is not recommend as it will allow risky
+  // security behavior such as cross-site scripting (XSS). Also configurable
+  // using the "disable-web-security" command-line switch.
+  ///
+  cef_state_t web_security;
+
+  ///
+  // Controls whether image URLs will be loaded from the network. A cached image
+  // will still be rendered if requested. Also configurable using the
+  // "disable-image-loading" command-line switch.
+  ///
+  cef_state_t image_loading;
+
+  ///
+  // Controls whether standalone images will be shrunk to fit the page. Also
+  // configurable using the "image-shrink-standalone-to-fit" command-line
+  // switch.
+  ///
+  cef_state_t image_shrink_standalone_to_fit;
+
+  ///
+  // Controls whether text areas can be resized. Also configurable using the
+  // "disable-text-area-resize" command-line switch.
+  ///
+  cef_state_t text_area_resize;
+
+  ///
+  // Controls whether the tab key can advance focus to links. Also configurable
+  // using the "disable-tab-to-links" command-line switch.
+  ///
+  cef_state_t tab_to_links;
+
+  ///
+  // Controls whether local storage can be used. Also configurable using the
+  // "disable-local-storage" command-line switch.
+  ///
+  cef_state_t local_storage;
+
+  ///
+  // Controls whether databases can be used. Also configurable using the
+  // "disable-databases" command-line switch.
+  ///
+  cef_state_t databases;
+
+  ///
+  // Controls whether the application cache can be used. Also configurable using
+  // the "disable-application-cache" command-line switch.
+  ///
+  cef_state_t application_cache;
+
+  ///
+  // Controls whether WebGL can be used. Note that WebGL requires hardware
+  // support and may not work on all systems even when enabled. Also
+  // configurable using the "disable-webgl" command-line switch.
+  ///
+  cef_state_t webgl;
+
+  ///
+  // Background color used for the browser before a document is loaded and when
+  // no document color is specified. The alpha component must be either fully
+  // opaque (0xFF) or fully transparent (0x00). If the alpha component is fully
+  // opaque then the RGB components will be used as the background color. If the
+  // alpha component is fully transparent for a windowed browser then the
+  // CefSettings.background_color value will be used. If the alpha component is
+  // fully transparent for a windowless (off-screen) browser then transparent
+  // painting will be enabled.
+  ///
+  cef_color_t background_color;
+
+  ///
+  // Comma delimited ordered list of language codes without any whitespace that
+  // will be used in the "Accept-Language" HTTP header. May be set globally
+  // using the CefBrowserSettings.accept_language_list value. If both values are
+  // empty then "en-US,en" will be used.
+  ///
+  cef_string_t accept_language_list;
+} cef_browser_settings_t;
+
+///
+// Return value types.
+///
+typedef enum {
+  ///
+  // Cancel immediately.
+  ///
+  RV_CANCEL = 0,
+
+  ///
+  // Continue immediately.
+  ///
+  RV_CONTINUE,
+
+  ///
+  // Continue asynchronously (usually via a callback).
+  ///
+  RV_CONTINUE_ASYNC,
+} cef_return_value_t;
+
+///
+// URL component parts.
+///
+typedef struct _cef_urlparts_t {
+  ///
+  // The complete URL specification.
+  ///
+  cef_string_t spec;
+
+  ///
+  // Scheme component not including the colon (e.g., "http").
+  ///
+  cef_string_t scheme;
+
+  ///
+  // User name component.
+  ///
+  cef_string_t username;
+
+  ///
+  // Password component.
+  ///
+  cef_string_t password;
+
+  ///
+  // Host component. This may be a hostname, an IPv4 address or an IPv6 literal
+  // surrounded by square brackets (e.g., "[2001:db8::1]").
+  ///
+  cef_string_t host;
+
+  ///
+  // Port number component.
+  ///
+  cef_string_t port;
+
+  ///
+  // Origin contains just the scheme, host, and port from a URL. Equivalent to
+  // clearing any username and password, replacing the path with a slash, and
+  // clearing everything after that. This value will be empty for non-standard
+  // URLs.
+  ///
+  cef_string_t origin;
+
+  ///
+  // Path component including the first slash following the host.
+  ///
+  cef_string_t path;
+
+  ///
+  // Query string component (i.e., everything following the '?').
+  ///
+  cef_string_t query;
+
+  ///
+  // Fragment (hash) identifier component (i.e., the string following the '#').
+  ///
+  cef_string_t fragment;
+} cef_urlparts_t;
+
+///
+// Cookie priority values.
+///
+typedef enum {
+  CEF_COOKIE_PRIORITY_LOW = -1,
+  CEF_COOKIE_PRIORITY_MEDIUM = 0,
+  CEF_COOKIE_PRIORITY_HIGH = 1,
+} cef_cookie_priority_t;
+
+///
+// Cookie same site values.
+///
+typedef enum {
+  CEF_COOKIE_SAME_SITE_UNSPECIFIED,
+  CEF_COOKIE_SAME_SITE_NO_RESTRICTION,
+  CEF_COOKIE_SAME_SITE_LAX_MODE,
+  CEF_COOKIE_SAME_SITE_STRICT_MODE,
+} cef_cookie_same_site_t;
+
+///
+// Cookie information.
+///
+typedef struct _cef_cookie_t {
+  ///
+  // The cookie name.
+  ///
+  cef_string_t name;
+
+  ///
+  // The cookie value.
+  ///
+  cef_string_t value;
+
+  ///
+  // If |domain| is empty a host cookie will be created instead of a domain
+  // cookie. Domain cookies are stored with a leading "." and are visible to
+  // sub-domains whereas host cookies are not.
+  ///
+  cef_string_t domain;
+
+  ///
+  // If |path| is non-empty only URLs at or below the path will get the cookie
+  // value.
+  ///
+  cef_string_t path;
+
+  ///
+  // If |secure| is true the cookie will only be sent for HTTPS requests.
+  ///
+  int secure;
+
+  ///
+  // If |httponly| is true the cookie will only be sent for HTTP requests.
+  ///
+  int httponly;
+
+  ///
+  // The cookie creation date. This is automatically populated by the system on
+  // cookie creation.
+  ///
+  cef_time_t creation;
+
+  ///
+  // The cookie last access date. This is automatically populated by the system
+  // on access.
+  ///
+  cef_time_t last_access;
+
+  ///
+  // The cookie expiration date is only valid if |has_expires| is true.
+  ///
+  int has_expires;
+  cef_time_t expires;
+
+  ///
+  // Same site.
+  ///
+  cef_cookie_same_site_t same_site;
+
+  ///
+  // Priority.
+  ///
+  cef_cookie_priority_t priority;
+} cef_cookie_t;
+
+///
+// Process termination status values.
+///
+typedef enum {
+  ///
+  // Non-zero exit status.
+  ///
+  TS_ABNORMAL_TERMINATION,
+
+  ///
+  // SIGKILL or task manager kill.
+  ///
+  TS_PROCESS_WAS_KILLED,
+
+  ///
+  // Segmentation fault.
+  ///
+  TS_PROCESS_CRASHED,
+
+  ///
+  // Out of memory. Some platforms may use TS_PROCESS_CRASHED instead.
+  ///
+  TS_PROCESS_OOM,
+} cef_termination_status_t;
+
+///
+// Path key values.
+///
+typedef enum {
+  ///
+  // Current directory.
+  ///
+  PK_DIR_CURRENT,
+
+  ///
+  // Directory containing PK_FILE_EXE.
+  ///
+  PK_DIR_EXE,
+
+  ///
+  // Directory containing PK_FILE_MODULE.
+  ///
+  PK_DIR_MODULE,
+
+  ///
+  // Temporary directory.
+  ///
+  PK_DIR_TEMP,
+
+  ///
+  // Path and filename of the current executable.
+  ///
+  PK_FILE_EXE,
+
+  ///
+  // Path and filename of the module containing the CEF code (usually the libcef
+  // module).
+  ///
+  PK_FILE_MODULE,
+
+  ///
+  // "Local Settings\Application Data" directory under the user profile
+  // directory on Windows.
+  ///
+  PK_LOCAL_APP_DATA,
+
+  ///
+  // "Application Data" directory under the user profile directory on Windows
+  // and "~/Library/Application Support" directory on Mac OS X.
+  ///
+  PK_USER_DATA,
+
+  ///
+  // Directory containing application resources. Can be configured via
+  // CefSettings.resources_dir_path.
+  ///
+  PK_DIR_RESOURCES,
+} cef_path_key_t;
+
+///
+// Storage types.
+///
+typedef enum {
+  ST_LOCALSTORAGE = 0,
+  ST_SESSIONSTORAGE,
+} cef_storage_type_t;
+
+///
+// Supported error code values.
+///
+typedef enum {
+  // No error.
+  ERR_NONE = 0,
+
+#define NET_ERROR(label, value) ERR_##label = value,
+#include "include/base/internal/cef_net_error_list.h"
+#undef NET_ERROR
+
+} cef_errorcode_t;
+
+///
+// Supported certificate status code values. See net\cert\cert_status_flags.h
+// for more information. CERT_STATUS_NONE is new in CEF because we use an
+// enum while cert_status_flags.h uses a typedef and static const variables.
+///
+typedef enum {
+  CERT_STATUS_NONE = 0,
+  CERT_STATUS_COMMON_NAME_INVALID = 1 << 0,
+  CERT_STATUS_DATE_INVALID = 1 << 1,
+  CERT_STATUS_AUTHORITY_INVALID = 1 << 2,
+  // 1 << 3 is reserved for ERR_CERT_CONTAINS_ERRORS (not useful with WinHTTP).
+  CERT_STATUS_NO_REVOCATION_MECHANISM = 1 << 4,
+  CERT_STATUS_UNABLE_TO_CHECK_REVOCATION = 1 << 5,
+  CERT_STATUS_REVOKED = 1 << 6,
+  CERT_STATUS_INVALID = 1 << 7,
+  CERT_STATUS_WEAK_SIGNATURE_ALGORITHM = 1 << 8,
+  // 1 << 9 was used for CERT_STATUS_NOT_IN_DNS
+  CERT_STATUS_NON_UNIQUE_NAME = 1 << 10,
+  CERT_STATUS_WEAK_KEY = 1 << 11,
+  // 1 << 12 was used for CERT_STATUS_WEAK_DH_KEY
+  CERT_STATUS_PINNED_KEY_MISSING = 1 << 13,
+  CERT_STATUS_NAME_CONSTRAINT_VIOLATION = 1 << 14,
+  CERT_STATUS_VALIDITY_TOO_LONG = 1 << 15,
+
+  // Bits 16 to 31 are for non-error statuses.
+  CERT_STATUS_IS_EV = 1 << 16,
+  CERT_STATUS_REV_CHECKING_ENABLED = 1 << 17,
+  // Bit 18 was CERT_STATUS_IS_DNSSEC
+  CERT_STATUS_SHA1_SIGNATURE_PRESENT = 1 << 19,
+  CERT_STATUS_CT_COMPLIANCE_FAILED = 1 << 20,
+} cef_cert_status_t;
+
+///
+// The manner in which a link click should be opened. These constants match
+// their equivalents in Chromium's window_open_disposition.h and should not be
+// renumbered.
+///
+typedef enum {
+  WOD_UNKNOWN,
+  WOD_CURRENT_TAB,
+  WOD_SINGLETON_TAB,
+  WOD_NEW_FOREGROUND_TAB,
+  WOD_NEW_BACKGROUND_TAB,
+  WOD_NEW_POPUP,
+  WOD_NEW_WINDOW,
+  WOD_SAVE_TO_DISK,
+  WOD_OFF_THE_RECORD,
+  WOD_IGNORE_ACTION
+} cef_window_open_disposition_t;
+
+///
+// "Verb" of a drag-and-drop operation as negotiated between the source and
+// destination. These constants match their equivalents in WebCore's
+// DragActions.h and should not be renumbered.
+///
+typedef enum {
+  DRAG_OPERATION_NONE = 0,
+  DRAG_OPERATION_COPY = 1,
+  DRAG_OPERATION_LINK = 2,
+  DRAG_OPERATION_GENERIC = 4,
+  DRAG_OPERATION_PRIVATE = 8,
+  DRAG_OPERATION_MOVE = 16,
+  DRAG_OPERATION_DELETE = 32,
+  DRAG_OPERATION_EVERY = UINT_MAX
+} cef_drag_operations_mask_t;
+
+///
+// Input mode of a virtual keyboard. These constants match their equivalents
+// in Chromium's text_input_mode.h and should not be renumbered.
+// See https://html.spec.whatwg.org/#input-modalities:-the-inputmode-attribute
+///
+typedef enum {
+  CEF_TEXT_INPUT_MODE_DEFAULT,
+  CEF_TEXT_INPUT_MODE_NONE,
+  CEF_TEXT_INPUT_MODE_TEXT,
+  CEF_TEXT_INPUT_MODE_TEL,
+  CEF_TEXT_INPUT_MODE_URL,
+  CEF_TEXT_INPUT_MODE_EMAIL,
+  CEF_TEXT_INPUT_MODE_NUMERIC,
+  CEF_TEXT_INPUT_MODE_DECIMAL,
+  CEF_TEXT_INPUT_MODE_SEARCH,
+
+  CEF_TEXT_INPUT_MODE_MAX = CEF_TEXT_INPUT_MODE_SEARCH,
+} cef_text_input_mode_t;
+
+///
+// V8 access control values.
+///
+typedef enum {
+  V8_ACCESS_CONTROL_DEFAULT = 0,
+  V8_ACCESS_CONTROL_ALL_CAN_READ = 1,
+  V8_ACCESS_CONTROL_ALL_CAN_WRITE = 1 << 1,
+  V8_ACCESS_CONTROL_PROHIBITS_OVERWRITING = 1 << 2
+} cef_v8_accesscontrol_t;
+
+///
+// V8 property attribute values.
+///
+typedef enum {
+  V8_PROPERTY_ATTRIBUTE_NONE = 0,            // Writeable, Enumerable,
+                                             //   Configurable
+  V8_PROPERTY_ATTRIBUTE_READONLY = 1 << 0,   // Not writeable
+  V8_PROPERTY_ATTRIBUTE_DONTENUM = 1 << 1,   // Not enumerable
+  V8_PROPERTY_ATTRIBUTE_DONTDELETE = 1 << 2  // Not configurable
+} cef_v8_propertyattribute_t;
+
+///
+// Post data elements may represent either bytes or files.
+///
+typedef enum {
+  PDE_TYPE_EMPTY = 0,
+  PDE_TYPE_BYTES,
+  PDE_TYPE_FILE,
+} cef_postdataelement_type_t;
+
+///
+// Resource type for a request.
+///
+typedef enum {
+  ///
+  // Top level page.
+  ///
+  RT_MAIN_FRAME = 0,
+
+  ///
+  // Frame or iframe.
+  ///
+  RT_SUB_FRAME,
+
+  ///
+  // CSS stylesheet.
+  ///
+  RT_STYLESHEET,
+
+  ///
+  // External script.
+  ///
+  RT_SCRIPT,
+
+  ///
+  // Image (jpg/gif/png/etc).
+  ///
+  RT_IMAGE,
+
+  ///
+  // Font.
+  ///
+  RT_FONT_RESOURCE,
+
+  ///
+  // Some other subresource. This is the default type if the actual type is
+  // unknown.
+  ///
+  RT_SUB_RESOURCE,
+
+  ///
+  // Object (or embed) tag for a plugin, or a resource that a plugin requested.
+  ///
+  RT_OBJECT,
+
+  ///
+  // Media resource.
+  ///
+  RT_MEDIA,
+
+  ///
+  // Main resource of a dedicated worker.
+  ///
+  RT_WORKER,
+
+  ///
+  // Main resource of a shared worker.
+  ///
+  RT_SHARED_WORKER,
+
+  ///
+  // Explicitly requested prefetch.
+  ///
+  RT_PREFETCH,
+
+  ///
+  // Favicon.
+  ///
+  RT_FAVICON,
+
+  ///
+  // XMLHttpRequest.
+  ///
+  RT_XHR,
+
+  ///
+  // A request for a <ping>
+  ///
+  RT_PING,
+
+  ///
+  // Main resource of a service worker.
+  ///
+  RT_SERVICE_WORKER,
+
+  ///
+  // A report of Content Security Policy violations.
+  ///
+  RT_CSP_REPORT,
+
+  ///
+  // A resource that a plugin requested.
+  ///
+  RT_PLUGIN_RESOURCE,
+} cef_resource_type_t;
+
+///
+// Transition type for a request. Made up of one source value and 0 or more
+// qualifiers.
+///
+typedef enum {
+  ///
+  // Source is a link click or the JavaScript window.open function. This is
+  // also the default value for requests like sub-resource loads that are not
+  // navigations.
+  ///
+  TT_LINK = 0,
+
+  ///
+  // Source is some other "explicit" navigation. This is the default value for
+  // navigations where the actual type is unknown. See also TT_DIRECT_LOAD_FLAG.
+  ///
+  TT_EXPLICIT = 1,
+
+  ///
+  // Source is a subframe navigation. This is any content that is automatically
+  // loaded in a non-toplevel frame. For example, if a page consists of several
+  // frames containing ads, those ad URLs will have this transition type.
+  // The user may not even realize the content in these pages is a separate
+  // frame, so may not care about the URL.
+  ///
+  TT_AUTO_SUBFRAME = 3,
+
+  ///
+  // Source is a subframe navigation explicitly requested by the user that will
+  // generate new navigation entries in the back/forward list. These are
+  // probably more important than frames that were automatically loaded in
+  // the background because the user probably cares about the fact that this
+  // link was loaded.
+  ///
+  TT_MANUAL_SUBFRAME = 4,
+
+  ///
+  // Source is a form submission by the user. NOTE: In some situations
+  // submitting a form does not result in this transition type. This can happen
+  // if the form uses a script to submit the contents.
+  ///
+  TT_FORM_SUBMIT = 7,
+
+  ///
+  // Source is a "reload" of the page via the Reload function or by re-visiting
+  // the same URL. NOTE: This is distinct from the concept of whether a
+  // particular load uses "reload semantics" (i.e. bypasses cached data).
+  ///
+  TT_RELOAD = 8,
+
+  ///
+  // General mask defining the bits used for the source values.
+  ///
+  TT_SOURCE_MASK = 0xFF,
+
+  // Qualifiers.
+  // Any of the core values above can be augmented by one or more qualifiers.
+  // These qualifiers further define the transition.
+
+  ///
+  // Attempted to visit a URL but was blocked.
+  ///
+  TT_BLOCKED_FLAG = 0x00800000,
+
+  ///
+  // Used the Forward or Back function to navigate among browsing history.
+  // Will be ORed to the transition type for the original load.
+  ///
+  TT_FORWARD_BACK_FLAG = 0x01000000,
+
+  ///
+  // Loaded a URL directly via CreateBrowser, LoadURL or LoadRequest.
+  ///
+  TT_DIRECT_LOAD_FLAG = 0x02000000,
+
+  ///
+  // The beginning of a navigation chain.
+  ///
+  TT_CHAIN_START_FLAG = 0x10000000,
+
+  ///
+  // The last transition in a redirect chain.
+  ///
+  TT_CHAIN_END_FLAG = 0x20000000,
+
+  ///
+  // Redirects caused by JavaScript or a meta refresh tag on the page.
+  ///
+  TT_CLIENT_REDIRECT_FLAG = 0x40000000,
+
+  ///
+  // Redirects sent from the server by HTTP headers.
+  ///
+  TT_SERVER_REDIRECT_FLAG = 0x80000000,
+
+  ///
+  // Used to test whether a transition involves a redirect.
+  ///
+  TT_IS_REDIRECT_MASK = 0xC0000000,
+
+  ///
+  // General mask defining the bits used for the qualifiers.
+  ///
+  TT_QUALIFIER_MASK = 0xFFFFFF00,
+} cef_transition_type_t;
+
+///
+// Flags used to customize the behavior of CefURLRequest.
+///
+typedef enum {
+  ///
+  // Default behavior.
+  ///
+  UR_FLAG_NONE = 0,
+
+  ///
+  // If set the cache will be skipped when handling the request. Setting this
+  // value is equivalent to specifying the "Cache-Control: no-cache" request
+  // header. Setting this value in combination with UR_FLAG_ONLY_FROM_CACHE will
+  // cause the request to fail.
+  ///
+  UR_FLAG_SKIP_CACHE = 1 << 0,
+
+  ///
+  // If set the request will fail if it cannot be served from the cache (or some
+  // equivalent local store). Setting this value is equivalent to specifying the
+  // "Cache-Control: only-if-cached" request header. Setting this value in
+  // combination with UR_FLAG_SKIP_CACHE or UR_FLAG_DISABLE_CACHE will cause the
+  // request to fail.
+  ///
+  UR_FLAG_ONLY_FROM_CACHE = 1 << 1,
+
+  ///
+  // If set the cache will not be used at all. Setting this value is equivalent
+  // to specifying the "Cache-Control: no-store" request header. Setting this
+  // value in combination with UR_FLAG_ONLY_FROM_CACHE will cause the request to
+  // fail.
+  ///
+  UR_FLAG_DISABLE_CACHE = 1 << 2,
+
+  ///
+  // If set user name, password, and cookies may be sent with the request, and
+  // cookies may be saved from the response.
+  ///
+  UR_FLAG_ALLOW_STORED_CREDENTIALS = 1 << 3,
+
+  ///
+  // If set upload progress events will be generated when a request has a body.
+  ///
+  UR_FLAG_REPORT_UPLOAD_PROGRESS = 1 << 4,
+
+  ///
+  // If set the CefURLRequestClient::OnDownloadData method will not be called.
+  ///
+  UR_FLAG_NO_DOWNLOAD_DATA = 1 << 5,
+
+  ///
+  // If set 5XX redirect errors will be propagated to the observer instead of
+  // automatically re-tried. This currently only applies for requests
+  // originated in the browser process.
+  ///
+  UR_FLAG_NO_RETRY_ON_5XX = 1 << 6,
+
+  ///
+  // If set 3XX responses will cause the fetch to halt immediately rather than
+  // continue through the redirect.
+  ///
+  UR_FLAG_STOP_ON_REDIRECT = 1 << 7,
+} cef_urlrequest_flags_t;
+
+///
+// Flags that represent CefURLRequest status.
+///
+typedef enum {
+  ///
+  // Unknown status.
+  ///
+  UR_UNKNOWN = 0,
+
+  ///
+  // Request succeeded.
+  ///
+  UR_SUCCESS,
+
+  ///
+  // An IO request is pending, and the caller will be informed when it is
+  // completed.
+  ///
+  UR_IO_PENDING,
+
+  ///
+  // Request was canceled programatically.
+  ///
+  UR_CANCELED,
+
+  ///
+  // Request failed for some reason.
+  ///
+  UR_FAILED,
+} cef_urlrequest_status_t;
+
+///
+// Structure representing a point.
+///
+typedef struct _cef_point_t {
+  int x;
+  int y;
+} cef_point_t;
+
+///
+// Structure representing a rectangle.
+///
+typedef struct _cef_rect_t {
+  int x;
+  int y;
+  int width;
+  int height;
+} cef_rect_t;
+
+///
+// Structure representing a size.
+///
+typedef struct _cef_size_t {
+  int width;
+  int height;
+} cef_size_t;
+
+///
+// Structure representing a range.
+///
+typedef struct _cef_range_t {
+  int from;
+  int to;
+} cef_range_t;
+
+///
+// Structure representing insets.
+///
+typedef struct _cef_insets_t {
+  int top;
+  int left;
+  int bottom;
+  int right;
+} cef_insets_t;
+
+///
+// Structure representing a draggable region.
+///
+typedef struct _cef_draggable_region_t {
+  ///
+  // Bounds of the region.
+  ///
+  cef_rect_t bounds;
+
+  ///
+  // True (1) this this region is draggable and false (0) otherwise.
+  ///
+  int draggable;
+} cef_draggable_region_t;
+
+///
+// Existing process IDs.
+///
+typedef enum {
+  ///
+  // Browser process.
+  ///
+  PID_BROWSER,
+  ///
+  // Renderer process.
+  ///
+  PID_RENDERER,
+} cef_process_id_t;
+
+///
+// Existing thread IDs.
+///
+typedef enum {
+  // BROWSER PROCESS THREADS -- Only available in the browser process.
+
+  ///
+  // The main thread in the browser. This will be the same as the main
+  // application thread if CefInitialize() is called with a
+  // CefSettings.multi_threaded_message_loop value of false. Do not perform
+  // blocking tasks on this thread. All tasks posted after
+  // CefBrowserProcessHandler::OnContextInitialized() and before CefShutdown()
+  // are guaranteed to run. This thread will outlive all other CEF threads.
+  ///
+  TID_UI,
+
+  ///
+  // Used for blocking tasks (e.g. file system access) where the user won't
+  // notice if the task takes an arbitrarily long time to complete. All tasks
+  // posted after CefBrowserProcessHandler::OnContextInitialized() and before
+  // CefShutdown() are guaranteed to run.
+  ///
+  TID_FILE_BACKGROUND,
+  TID_FILE = TID_FILE_BACKGROUND,
+
+  ///
+  // Used for blocking tasks (e.g. file system access) that affect UI or
+  // responsiveness of future user interactions. Do not use if an immediate
+  // response to a user interaction is expected. All tasks posted after
+  // CefBrowserProcessHandler::OnContextInitialized() and before CefShutdown()
+  // are guaranteed to run.
+  // Examples:
+  // - Updating the UI to reflect progress on a long task.
+  // - Loading data that might be shown in the UI after a future user
+  //   interaction.
+  ///
+  TID_FILE_USER_VISIBLE,
+
+  ///
+  // Used for blocking tasks (e.g. file system access) that affect UI
+  // immediately after a user interaction. All tasks posted after
+  // CefBrowserProcessHandler::OnContextInitialized() and before CefShutdown()
+  // are guaranteed to run.
+  // Example: Generating data shown in the UI immediately after a click.
+  ///
+  TID_FILE_USER_BLOCKING,
+
+  ///
+  // Used to launch and terminate browser processes.
+  ///
+  TID_PROCESS_LAUNCHER,
+
+  ///
+  // Used to process IPC and network messages. Do not perform blocking tasks on
+  // this thread. All tasks posted after
+  // CefBrowserProcessHandler::OnContextInitialized() and before CefShutdown()
+  // are guaranteed to run.
+  ///
+  TID_IO,
+
+  // RENDER PROCESS THREADS -- Only available in the render process.
+
+  ///
+  // The main thread in the renderer. Used for all WebKit and V8 interaction.
+  // Tasks may be posted to this thread after
+  // CefRenderProcessHandler::OnRenderThreadCreated but are not guaranteed to
+  // run before sub-process termination (sub-processes may be killed at any time
+  // without warning).
+  ///
+  TID_RENDERER,
+} cef_thread_id_t;
+
+///
+// Thread priority values listed in increasing order of importance.
+///
+typedef enum {
+  ///
+  // Suitable for threads that shouldn't disrupt high priority work.
+  ///
+  TP_BACKGROUND,
+
+  ///
+  // Default priority level.
+  ///
+  TP_NORMAL,
+
+  ///
+  // Suitable for threads which generate data for the display (at ~60Hz).
+  ///
+  TP_DISPLAY,
+
+  ///
+  // Suitable for low-latency, glitch-resistant audio.
+  ///
+  TP_REALTIME_AUDIO,
+} cef_thread_priority_t;
+
+///
+// Message loop types. Indicates the set of asynchronous events that a message
+// loop can process.
+///
+typedef enum {
+  ///
+  // Supports tasks and timers.
+  ///
+  ML_TYPE_DEFAULT,
+
+  ///
+  // Supports tasks, timers and native UI events (e.g. Windows messages).
+  ///
+  ML_TYPE_UI,
+
+  ///
+  // Supports tasks, timers and asynchronous IO events.
+  ///
+  ML_TYPE_IO,
+} cef_message_loop_type_t;
+
+///
+// Windows COM initialization mode. Specifies how COM will be initialized for a
+// new thread.
+///
+typedef enum {
+  ///
+  // No COM initialization.
+  ///
+  COM_INIT_MODE_NONE,
+
+  ///
+  // Initialize COM using single-threaded apartments.
+  ///
+  COM_INIT_MODE_STA,
+
+  ///
+  // Initialize COM using multi-threaded apartments.
+  ///
+  COM_INIT_MODE_MTA,
+} cef_com_init_mode_t;
+
+///
+// Supported value types.
+///
+typedef enum {
+  VTYPE_INVALID = 0,
+  VTYPE_NULL,
+  VTYPE_BOOL,
+  VTYPE_INT,
+  VTYPE_DOUBLE,
+  VTYPE_STRING,
+  VTYPE_BINARY,
+  VTYPE_DICTIONARY,
+  VTYPE_LIST,
+} cef_value_type_t;
+
+///
+// Supported JavaScript dialog types.
+///
+typedef enum {
+  JSDIALOGTYPE_ALERT = 0,
+  JSDIALOGTYPE_CONFIRM,
+  JSDIALOGTYPE_PROMPT,
+} cef_jsdialog_type_t;
+
+///
+// Screen information used when window rendering is disabled. This structure is
+// passed as a parameter to CefRenderHandler::GetScreenInfo and should be filled
+// in by the client.
+///
+typedef struct _cef_screen_info_t {
+  ///
+  // Device scale factor. Specifies the ratio between physical and logical
+  // pixels.
+  ///
+  float device_scale_factor;
+
+  ///
+  // The screen depth in bits per pixel.
+  ///
+  int depth;
+
+  ///
+  // The bits per color component. This assumes that the colors are balanced
+  // equally.
+  ///
+  int depth_per_component;
+
+  ///
+  // This can be true for black and white printers.
+  ///
+  int is_monochrome;
+
+  ///
+  // This is set from the rcMonitor member of MONITORINFOEX, to whit:
+  //   "A RECT structure that specifies the display monitor rectangle,
+  //   expressed in virtual-screen coordinates. Note that if the monitor
+  //   is not the primary display monitor, some of the rectangle's
+  //   coordinates may be negative values."
+  //
+  // The |rect| and |available_rect| properties are used to determine the
+  // available surface for rendering popup views.
+  ///
+  cef_rect_t rect;
+
+  ///
+  // This is set from the rcWork member of MONITORINFOEX, to whit:
+  //   "A RECT structure that specifies the work area rectangle of the
+  //   display monitor that can be used by applications, expressed in
+  //   virtual-screen coordinates. Windows uses this rectangle to
+  //   maximize an application on the monitor. The rest of the area in
+  //   rcMonitor contains system windows such as the task bar and side
+  //   bars. Note that if the monitor is not the primary display monitor,
+  //   some of the rectangle's coordinates may be negative values".
+  //
+  // The |rect| and |available_rect| properties are used to determine the
+  // available surface for rendering popup views.
+  ///
+  cef_rect_t available_rect;
+} cef_screen_info_t;
+
+///
+// Supported menu IDs. Non-English translations can be provided for the
+// IDS_MENU_* strings in CefResourceBundleHandler::GetLocalizedString().
+///
+typedef enum {
+  // Navigation.
+  MENU_ID_BACK = 100,
+  MENU_ID_FORWARD = 101,
+  MENU_ID_RELOAD = 102,
+  MENU_ID_RELOAD_NOCACHE = 103,
+  MENU_ID_STOPLOAD = 104,
+
+  // Editing.
+  MENU_ID_UNDO = 110,
+  MENU_ID_REDO = 111,
+  MENU_ID_CUT = 112,
+  MENU_ID_COPY = 113,
+  MENU_ID_PASTE = 114,
+  MENU_ID_DELETE = 115,
+  MENU_ID_SELECT_ALL = 116,
+
+  // Miscellaneous.
+  MENU_ID_FIND = 130,
+  MENU_ID_PRINT = 131,
+  MENU_ID_VIEW_SOURCE = 132,
+
+  // Spell checking word correction suggestions.
+  MENU_ID_SPELLCHECK_SUGGESTION_0 = 200,
+  MENU_ID_SPELLCHECK_SUGGESTION_1 = 201,
+  MENU_ID_SPELLCHECK_SUGGESTION_2 = 202,
+  MENU_ID_SPELLCHECK_SUGGESTION_3 = 203,
+  MENU_ID_SPELLCHECK_SUGGESTION_4 = 204,
+  MENU_ID_SPELLCHECK_SUGGESTION_LAST = 204,
+  MENU_ID_NO_SPELLING_SUGGESTIONS = 205,
+  MENU_ID_ADD_TO_DICTIONARY = 206,
+
+  // Custom menu items originating from the renderer process. For example,
+  // plugin placeholder menu items or Flash menu items.
+  MENU_ID_CUSTOM_FIRST = 220,
+  MENU_ID_CUSTOM_LAST = 250,
+
+  // All user-defined menu IDs should come between MENU_ID_USER_FIRST and
+  // MENU_ID_USER_LAST to avoid overlapping the Chromium and CEF ID ranges
+  // defined in the tools/gritsettings/resource_ids file.
+  MENU_ID_USER_FIRST = 26500,
+  MENU_ID_USER_LAST = 28500,
+} cef_menu_id_t;
+
+///
+// Mouse button types.
+///
+typedef enum {
+  MBT_LEFT = 0,
+  MBT_MIDDLE,
+  MBT_RIGHT,
+} cef_mouse_button_type_t;
+
+///
+// Structure representing mouse event information.
+///
+typedef struct _cef_mouse_event_t {
+  ///
+  // X coordinate relative to the left side of the view.
+  ///
+  int x;
+
+  ///
+  // Y coordinate relative to the top side of the view.
+  ///
+  int y;
+
+  ///
+  // Bit flags describing any pressed modifier keys. See
+  // cef_event_flags_t for values.
+  ///
+  uint32 modifiers;
+} cef_mouse_event_t;
+
+///
+// Touch points states types.
+///
+typedef enum {
+  CEF_TET_RELEASED = 0,
+  CEF_TET_PRESSED,
+  CEF_TET_MOVED,
+  CEF_TET_CANCELLED
+} cef_touch_event_type_t;
+
+///
+// The device type that caused the event.
+///
+typedef enum {
+  CEF_POINTER_TYPE_TOUCH = 0,
+  CEF_POINTER_TYPE_MOUSE,
+  CEF_POINTER_TYPE_PEN,
+  CEF_POINTER_TYPE_ERASER,
+  CEF_POINTER_TYPE_UNKNOWN
+} cef_pointer_type_t;
+
+///
+// Structure representing touch event information.
+///
+typedef struct _cef_touch_event_t {
+  ///
+  // Id of a touch point. Must be unique per touch, can be any number except -1.
+  // Note that a maximum of 16 concurrent touches will be tracked; touches
+  // beyond that will be ignored.
+  ///
+  int id;
+
+  ///
+  // X coordinate relative to the left side of the view.
+  ///
+  float x;
+
+  ///
+  // Y coordinate relative to the top side of the view.
+  ///
+  float y;
+
+  ///
+  // X radius in pixels. Set to 0 if not applicable.
+  ///
+  float radius_x;
+
+  ///
+  // Y radius in pixels. Set to 0 if not applicable.
+  ///
+  float radius_y;
+
+  ///
+  // Rotation angle in radians. Set to 0 if not applicable.
+  ///
+  float rotation_angle;
+
+  ///
+  // The normalized pressure of the pointer input in the range of [0,1].
+  // Set to 0 if not applicable.
+  ///
+  float pressure;
+
+  ///
+  // The state of the touch point. Touches begin with one CEF_TET_PRESSED event
+  // followed by zero or more CEF_TET_MOVED events and finally one
+  // CEF_TET_RELEASED or CEF_TET_CANCELLED event. Events not respecting this
+  // order will be ignored.
+  ///
+  cef_touch_event_type_t type;
+
+  ///
+  // Bit flags describing any pressed modifier keys. See
+  // cef_event_flags_t for values.
+  ///
+  uint32 modifiers;
+
+  ///
+  // The device type that caused the event.
+  ///
+  cef_pointer_type_t pointer_type;
+
+} cef_touch_event_t;
+
+///
+// Paint element types.
+///
+typedef enum {
+  PET_VIEW = 0,
+  PET_POPUP,
+} cef_paint_element_type_t;
+
+///
+// Supported event bit flags.
+///
+typedef enum {
+  EVENTFLAG_NONE = 0,
+  EVENTFLAG_CAPS_LOCK_ON = 1 << 0,
+  EVENTFLAG_SHIFT_DOWN = 1 << 1,
+  EVENTFLAG_CONTROL_DOWN = 1 << 2,
+  EVENTFLAG_ALT_DOWN = 1 << 3,
+  EVENTFLAG_LEFT_MOUSE_BUTTON = 1 << 4,
+  EVENTFLAG_MIDDLE_MOUSE_BUTTON = 1 << 5,
+  EVENTFLAG_RIGHT_MOUSE_BUTTON = 1 << 6,
+  // Mac OS-X command key.
+  EVENTFLAG_COMMAND_DOWN = 1 << 7,
+  EVENTFLAG_NUM_LOCK_ON = 1 << 8,
+  EVENTFLAG_IS_KEY_PAD = 1 << 9,
+  EVENTFLAG_IS_LEFT = 1 << 10,
+  EVENTFLAG_IS_RIGHT = 1 << 11,
+  EVENTFLAG_ALTGR_DOWN = 1 << 12,
+} cef_event_flags_t;
+
+///
+// Supported menu item types.
+///
+typedef enum {
+  MENUITEMTYPE_NONE,
+  MENUITEMTYPE_COMMAND,
+  MENUITEMTYPE_CHECK,
+  MENUITEMTYPE_RADIO,
+  MENUITEMTYPE_SEPARATOR,
+  MENUITEMTYPE_SUBMENU,
+} cef_menu_item_type_t;
+
+///
+// Supported context menu type flags.
+///
+typedef enum {
+  ///
+  // No node is selected.
+  ///
+  CM_TYPEFLAG_NONE = 0,
+  ///
+  // The top page is selected.
+  ///
+  CM_TYPEFLAG_PAGE = 1 << 0,
+  ///
+  // A subframe page is selected.
+  ///
+  CM_TYPEFLAG_FRAME = 1 << 1,
+  ///
+  // A link is selected.
+  ///
+  CM_TYPEFLAG_LINK = 1 << 2,
+  ///
+  // A media node is selected.
+  ///
+  CM_TYPEFLAG_MEDIA = 1 << 3,
+  ///
+  // There is a textual or mixed selection that is selected.
+  ///
+  CM_TYPEFLAG_SELECTION = 1 << 4,
+  ///
+  // An editable element is selected.
+  ///
+  CM_TYPEFLAG_EDITABLE = 1 << 5,
+} cef_context_menu_type_flags_t;
+
+///
+// Supported context menu media types.
+///
+typedef enum {
+  ///
+  // No special node is in context.
+  ///
+  CM_MEDIATYPE_NONE,
+  ///
+  // An image node is selected.
+  ///
+  CM_MEDIATYPE_IMAGE,
+  ///
+  // A video node is selected.
+  ///
+  CM_MEDIATYPE_VIDEO,
+  ///
+  // An audio node is selected.
+  ///
+  CM_MEDIATYPE_AUDIO,
+  ///
+  // A file node is selected.
+  ///
+  CM_MEDIATYPE_FILE,
+  ///
+  // A plugin node is selected.
+  ///
+  CM_MEDIATYPE_PLUGIN,
+} cef_context_menu_media_type_t;
+
+///
+// Supported context menu media state bit flags.
+///
+typedef enum {
+  CM_MEDIAFLAG_NONE = 0,
+  CM_MEDIAFLAG_ERROR = 1 << 0,
+  CM_MEDIAFLAG_PAUSED = 1 << 1,
+  CM_MEDIAFLAG_MUTED = 1 << 2,
+  CM_MEDIAFLAG_LOOP = 1 << 3,
+  CM_MEDIAFLAG_CAN_SAVE = 1 << 4,
+  CM_MEDIAFLAG_HAS_AUDIO = 1 << 5,
+  CM_MEDIAFLAG_HAS_VIDEO = 1 << 6,
+  CM_MEDIAFLAG_CONTROL_ROOT_ELEMENT = 1 << 7,
+  CM_MEDIAFLAG_CAN_PRINT = 1 << 8,
+  CM_MEDIAFLAG_CAN_ROTATE = 1 << 9,
+} cef_context_menu_media_state_flags_t;
+
+///
+// Supported context menu edit state bit flags.
+///
+typedef enum {
+  CM_EDITFLAG_NONE = 0,
+  CM_EDITFLAG_CAN_UNDO = 1 << 0,
+  CM_EDITFLAG_CAN_REDO = 1 << 1,
+  CM_EDITFLAG_CAN_CUT = 1 << 2,
+  CM_EDITFLAG_CAN_COPY = 1 << 3,
+  CM_EDITFLAG_CAN_PASTE = 1 << 4,
+  CM_EDITFLAG_CAN_DELETE = 1 << 5,
+  CM_EDITFLAG_CAN_SELECT_ALL = 1 << 6,
+  CM_EDITFLAG_CAN_TRANSLATE = 1 << 7,
+} cef_context_menu_edit_state_flags_t;
+
+///
+// Key event types.
+///
+typedef enum {
+  ///
+  // Notification that a key transitioned from "up" to "down".
+  ///
+  KEYEVENT_RAWKEYDOWN = 0,
+
+  ///
+  // Notification that a key was pressed. This does not necessarily correspond
+  // to a character depending on the key and language. Use KEYEVENT_CHAR for
+  // character input.
+  ///
+  KEYEVENT_KEYDOWN,
+
+  ///
+  // Notification that a key was released.
+  ///
+  KEYEVENT_KEYUP,
+
+  ///
+  // Notification that a character was typed. Use this for text input. Key
+  // down events may generate 0, 1, or more than one character event depending
+  // on the key, locale, and operating system.
+  ///
+  KEYEVENT_CHAR
+} cef_key_event_type_t;
+
+///
+// Structure representing keyboard event information.
+///
+typedef struct _cef_key_event_t {
+  ///
+  // The type of keyboard event.
+  ///
+  cef_key_event_type_t type;
+
+  ///
+  // Bit flags describing any pressed modifier keys. See
+  // cef_event_flags_t for values.
+  ///
+  uint32 modifiers;
+
+  ///
+  // The Windows key code for the key event. This value is used by the DOM
+  // specification. Sometimes it comes directly from the event (i.e. on
+  // Windows) and sometimes it's determined using a mapping function. See
+  // WebCore/platform/chromium/KeyboardCodes.h for the list of values.
+  ///
+  int windows_key_code;
+
+  ///
+  // The actual key code genenerated by the platform.
+  ///
+  int native_key_code;
+
+  ///
+  // Indicates whether the event is considered a "system key" event (see
+  // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx for details).
+  // This value will always be false on non-Windows platforms.
+  ///
+  int is_system_key;
+
+  ///
+  // The character generated by the keystroke.
+  ///
+  char16 character;
+
+  ///
+  // Same as |character| but unmodified by any concurrently-held modifiers
+  // (except shift). This is useful for working out shortcut keys.
+  ///
+  char16 unmodified_character;
+
+  ///
+  // True if the focus is currently on an editable field on the page. This is
+  // useful for determining if standard key events should be intercepted.
+  ///
+  int focus_on_editable_field;
+} cef_key_event_t;
+
+///
+// Focus sources.
+///
+typedef enum {
+  ///
+  // The source is explicit navigation via the API (LoadURL(), etc).
+  ///
+  FOCUS_SOURCE_NAVIGATION = 0,
+  ///
+  // The source is a system-generated focus event.
+  ///
+  FOCUS_SOURCE_SYSTEM,
+} cef_focus_source_t;
+
+///
+// Navigation types.
+///
+typedef enum {
+  NAVIGATION_LINK_CLICKED = 0,
+  NAVIGATION_FORM_SUBMITTED,
+  NAVIGATION_BACK_FORWARD,
+  NAVIGATION_RELOAD,
+  NAVIGATION_FORM_RESUBMITTED,
+  NAVIGATION_OTHER,
+} cef_navigation_type_t;
+
+///
+// Supported XML encoding types. The parser supports ASCII, ISO-8859-1, and
+// UTF16 (LE and BE) by default. All other types must be translated to UTF8
+// before being passed to the parser. If a BOM is detected and the correct
+// decoder is available then that decoder will be used automatically.
+///
+typedef enum {
+  XML_ENCODING_NONE = 0,
+  XML_ENCODING_UTF8,
+  XML_ENCODING_UTF16LE,
+  XML_ENCODING_UTF16BE,
+  XML_ENCODING_ASCII,
+} cef_xml_encoding_type_t;
+
+///
+// XML node types.
+///
+typedef enum {
+  XML_NODE_UNSUPPORTED = 0,
+  XML_NODE_PROCESSING_INSTRUCTION,
+  XML_NODE_DOCUMENT_TYPE,
+  XML_NODE_ELEMENT_START,
+  XML_NODE_ELEMENT_END,
+  XML_NODE_ATTRIBUTE,
+  XML_NODE_TEXT,
+  XML_NODE_CDATA,
+  XML_NODE_ENTITY_REFERENCE,
+  XML_NODE_WHITESPACE,
+  XML_NODE_COMMENT,
+} cef_xml_node_type_t;
+
+///
+// Popup window features.
+///
+typedef struct _cef_popup_features_t {
+  int x;
+  int xSet;
+  int y;
+  int ySet;
+  int width;
+  int widthSet;
+  int height;
+  int heightSet;
+
+  int menuBarVisible;
+  int statusBarVisible;
+  int toolBarVisible;
+  int scrollbarsVisible;
+} cef_popup_features_t;
+
+///
+// DOM document types.
+///
+typedef enum {
+  DOM_DOCUMENT_TYPE_UNKNOWN = 0,
+  DOM_DOCUMENT_TYPE_HTML,
+  DOM_DOCUMENT_TYPE_XHTML,
+  DOM_DOCUMENT_TYPE_PLUGIN,
+} cef_dom_document_type_t;
+
+///
+// DOM event category flags.
+///
+typedef enum {
+  DOM_EVENT_CATEGORY_UNKNOWN = 0x0,
+  DOM_EVENT_CATEGORY_UI = 0x1,
+  DOM_EVENT_CATEGORY_MOUSE = 0x2,
+  DOM_EVENT_CATEGORY_MUTATION = 0x4,
+  DOM_EVENT_CATEGORY_KEYBOARD = 0x8,
+  DOM_EVENT_CATEGORY_TEXT = 0x10,
+  DOM_EVENT_CATEGORY_COMPOSITION = 0x20,
+  DOM_EVENT_CATEGORY_DRAG = 0x40,
+  DOM_EVENT_CATEGORY_CLIPBOARD = 0x80,
+  DOM_EVENT_CATEGORY_MESSAGE = 0x100,
+  DOM_EVENT_CATEGORY_WHEEL = 0x200,
+  DOM_EVENT_CATEGORY_BEFORE_TEXT_INSERTED = 0x400,
+  DOM_EVENT_CATEGORY_OVERFLOW = 0x800,
+  DOM_EVENT_CATEGORY_PAGE_TRANSITION = 0x1000,
+  DOM_EVENT_CATEGORY_POPSTATE = 0x2000,
+  DOM_EVENT_CATEGORY_PROGRESS = 0x4000,
+  DOM_EVENT_CATEGORY_XMLHTTPREQUEST_PROGRESS = 0x8000,
+} cef_dom_event_category_t;
+
+///
+// DOM event processing phases.
+///
+typedef enum {
+  DOM_EVENT_PHASE_UNKNOWN = 0,
+  DOM_EVENT_PHASE_CAPTURING,
+  DOM_EVENT_PHASE_AT_TARGET,
+  DOM_EVENT_PHASE_BUBBLING,
+} cef_dom_event_phase_t;
+
+///
+// DOM node types.
+///
+typedef enum {
+  DOM_NODE_TYPE_UNSUPPORTED = 0,
+  DOM_NODE_TYPE_ELEMENT,
+  DOM_NODE_TYPE_ATTRIBUTE,
+  DOM_NODE_TYPE_TEXT,
+  DOM_NODE_TYPE_CDATA_SECTION,
+  DOM_NODE_TYPE_PROCESSING_INSTRUCTIONS,
+  DOM_NODE_TYPE_COMMENT,
+  DOM_NODE_TYPE_DOCUMENT,
+  DOM_NODE_TYPE_DOCUMENT_TYPE,
+  DOM_NODE_TYPE_DOCUMENT_FRAGMENT,
+} cef_dom_node_type_t;
+
+///
+// Supported file dialog modes.
+///
+typedef enum {
+  ///
+  // Requires that the file exists before allowing the user to pick it.
+  ///
+  FILE_DIALOG_OPEN = 0,
+
+  ///
+  // Like Open, but allows picking multiple files to open.
+  ///
+  FILE_DIALOG_OPEN_MULTIPLE,
+
+  ///
+  // Like Open, but selects a folder to open.
+  ///
+  FILE_DIALOG_OPEN_FOLDER,
+
+  ///
+  // Allows picking a nonexistent file, and prompts to overwrite if the file
+  // already exists.
+  ///
+  FILE_DIALOG_SAVE,
+
+  ///
+  // General mask defining the bits used for the type values.
+  ///
+  FILE_DIALOG_TYPE_MASK = 0xFF,
+
+  // Qualifiers.
+  // Any of the type values above can be augmented by one or more qualifiers.
+  // These qualifiers further define the dialog behavior.
+
+  ///
+  // Prompt to overwrite if the user selects an existing file with the Save
+  // dialog.
+  ///
+  FILE_DIALOG_OVERWRITEPROMPT_FLAG = 0x01000000,
+
+  ///
+  // Do not display read-only files.
+  ///
+  FILE_DIALOG_HIDEREADONLY_FLAG = 0x02000000,
+} cef_file_dialog_mode_t;
+
+///
+// Print job color mode values.
+///
+typedef enum {
+  COLOR_MODEL_UNKNOWN,
+  COLOR_MODEL_GRAY,
+  COLOR_MODEL_COLOR,
+  COLOR_MODEL_CMYK,
+  COLOR_MODEL_CMY,
+  COLOR_MODEL_KCMY,
+  COLOR_MODEL_CMY_K,  // CMY_K represents CMY+K.
+  COLOR_MODEL_BLACK,
+  COLOR_MODEL_GRAYSCALE,
+  COLOR_MODEL_RGB,
+  COLOR_MODEL_RGB16,
+  COLOR_MODEL_RGBA,
+  COLOR_MODEL_COLORMODE_COLOR,              // Used in samsung printer ppds.
+  COLOR_MODEL_COLORMODE_MONOCHROME,         // Used in samsung printer ppds.
+  COLOR_MODEL_HP_COLOR_COLOR,               // Used in HP color printer ppds.
+  COLOR_MODEL_HP_COLOR_BLACK,               // Used in HP color printer ppds.
+  COLOR_MODEL_PRINTOUTMODE_NORMAL,          // Used in foomatic ppds.
+  COLOR_MODEL_PRINTOUTMODE_NORMAL_GRAY,     // Used in foomatic ppds.
+  COLOR_MODEL_PROCESSCOLORMODEL_CMYK,       // Used in canon printer ppds.
+  COLOR_MODEL_PROCESSCOLORMODEL_GREYSCALE,  // Used in canon printer ppds.
+  COLOR_MODEL_PROCESSCOLORMODEL_RGB,        // Used in canon printer ppds
+} cef_color_model_t;
+
+///
+// Print job duplex mode values.
+///
+typedef enum {
+  DUPLEX_MODE_UNKNOWN = -1,
+  DUPLEX_MODE_SIMPLEX,
+  DUPLEX_MODE_LONG_EDGE,
+  DUPLEX_MODE_SHORT_EDGE,
+} cef_duplex_mode_t;
+
+///
+// Cursor type values.
+///
+typedef enum {
+  CT_POINTER = 0,
+  CT_CROSS,
+  CT_HAND,
+  CT_IBEAM,
+  CT_WAIT,
+  CT_HELP,
+  CT_EASTRESIZE,
+  CT_NORTHRESIZE,
+  CT_NORTHEASTRESIZE,
+  CT_NORTHWESTRESIZE,
+  CT_SOUTHRESIZE,
+  CT_SOUTHEASTRESIZE,
+  CT_SOUTHWESTRESIZE,
+  CT_WESTRESIZE,
+  CT_NORTHSOUTHRESIZE,
+  CT_EASTWESTRESIZE,
+  CT_NORTHEASTSOUTHWESTRESIZE,
+  CT_NORTHWESTSOUTHEASTRESIZE,
+  CT_COLUMNRESIZE,
+  CT_ROWRESIZE,
+  CT_MIDDLEPANNING,
+  CT_EASTPANNING,
+  CT_NORTHPANNING,
+  CT_NORTHEASTPANNING,
+  CT_NORTHWESTPANNING,
+  CT_SOUTHPANNING,
+  CT_SOUTHEASTPANNING,
+  CT_SOUTHWESTPANNING,
+  CT_WESTPANNING,
+  CT_MOVE,
+  CT_VERTICALTEXT,
+  CT_CELL,
+  CT_CONTEXTMENU,
+  CT_ALIAS,
+  CT_PROGRESS,
+  CT_NODROP,
+  CT_COPY,
+  CT_NONE,
+  CT_NOTALLOWED,
+  CT_ZOOMIN,
+  CT_ZOOMOUT,
+  CT_GRAB,
+  CT_GRABBING,
+  CT_MIDDLE_PANNING_VERTICAL,
+  CT_MIDDLE_PANNING_HORIZONTAL,
+  CT_CUSTOM,
+  CT_DND_NONE,
+  CT_DND_MOVE,
+  CT_DND_COPY,
+  CT_DND_LINK,
+} cef_cursor_type_t;
+
+///
+// Structure representing cursor information. |buffer| will be
+// |size.width|*|size.height|*4 bytes in size and represents a BGRA image with
+// an upper-left origin.
+///
+typedef struct _cef_cursor_info_t {
+  cef_point_t hotspot;
+  float image_scale_factor;
+  void* buffer;
+  cef_size_t size;
+} cef_cursor_info_t;
+
+///
+// URI unescape rules passed to CefURIDecode().
+///
+typedef enum {
+  ///
+  // Don't unescape anything at all.
+  ///
+  UU_NONE = 0,
+
+  ///
+  // Don't unescape anything special, but all normal unescaping will happen.
+  // This is a placeholder and can't be combined with other flags (since it's
+  // just the absence of them). All other unescape rules imply "normal" in
+  // addition to their special meaning. Things like escaped letters, digits,
+  // and most symbols will get unescaped with this mode.
+  ///
+  UU_NORMAL = 1 << 0,
+
+  ///
+  // Convert %20 to spaces. In some places where we're showing URLs, we may
+  // want this. In places where the URL may be copied and pasted out, then
+  // you wouldn't want this since it might not be interpreted in one piece
+  // by other applications.
+  ///
+  UU_SPACES = 1 << 1,
+
+  ///
+  // Unescapes '/' and '\\'. If these characters were unescaped, the resulting
+  // URL won't be the same as the source one. Moreover, they are dangerous to
+  // unescape in strings that will be used as file paths or names. This value
+  // should only be used when slashes don't have special meaning, like data
+  // URLs.
+  ///
+  UU_PATH_SEPARATORS = 1 << 2,
+
+  ///
+  // Unescapes various characters that will change the meaning of URLs,
+  // including '%', '+', '&', '#'. Does not unescape path separators.
+  // If these characters were unescaped, the resulting URL won't be the same
+  // as the source one. This flag is used when generating final output like
+  // filenames for URLs where we won't be interpreting as a URL and want to do
+  // as much unescaping as possible.
+  ///
+  UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS = 1 << 3,
+
+  ///
+  // URL queries use "+" for space. This flag controls that replacement.
+  ///
+  UU_REPLACE_PLUS_WITH_SPACE = 1 << 4,
+} cef_uri_unescape_rule_t;
+
+///
+// Options that can be passed to CefParseJSON.
+///
+typedef enum {
+  ///
+  // Parses the input strictly according to RFC 4627. See comments in Chromium's
+  // base/json/json_reader.h file for known limitations/deviations from the RFC.
+  ///
+  JSON_PARSER_RFC = 0,
+
+  ///
+  // Allows commas to exist after the last element in structures.
+  ///
+  JSON_PARSER_ALLOW_TRAILING_COMMAS = 1 << 0,
+} cef_json_parser_options_t;
+
+///
+// Error codes that can be returned from CefParseJSONAndReturnError.
+///
+typedef enum {
+  JSON_NO_ERROR = 0,
+  JSON_INVALID_ESCAPE,
+  JSON_SYNTAX_ERROR,
+  JSON_UNEXPECTED_TOKEN,
+  JSON_TRAILING_COMMA,
+  JSON_TOO_MUCH_NESTING,
+  JSON_UNEXPECTED_DATA_AFTER_ROOT,
+  JSON_UNSUPPORTED_ENCODING,
+  JSON_UNQUOTED_DICTIONARY_KEY,
+  JSON_PARSE_ERROR_COUNT
+} cef_json_parser_error_t;
+
+///
+// Options that can be passed to CefWriteJSON.
+///
+typedef enum {
+  ///
+  // Default behavior.
+  ///
+  JSON_WRITER_DEFAULT = 0,
+
+  ///
+  // This option instructs the writer that if a Binary value is encountered,
+  // the value (and key if within a dictionary) will be omitted from the
+  // output, and success will be returned. Otherwise, if a binary value is
+  // encountered, failure will be returned.
+  ///
+  JSON_WRITER_OMIT_BINARY_VALUES = 1 << 0,
+
+  ///
+  // This option instructs the writer to write doubles that have no fractional
+  // part as a normal integer (i.e., without using exponential notation
+  // or appending a '.0') as long as the value is within the range of a
+  // 64-bit int.
+  ///
+  JSON_WRITER_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1,
+
+  ///
+  // Return a slightly nicer formatted json string (pads with whitespace to
+  // help with readability).
+  ///
+  JSON_WRITER_PRETTY_PRINT = 1 << 2,
+} cef_json_writer_options_t;
+
+///
+// Margin type for PDF printing.
+///
+typedef enum {
+  ///
+  // Default margins.
+  ///
+  PDF_PRINT_MARGIN_DEFAULT,
+
+  ///
+  // No margins.
+  ///
+  PDF_PRINT_MARGIN_NONE,
+
+  ///
+  // Minimum margins.
+  ///
+  PDF_PRINT_MARGIN_MINIMUM,
+
+  ///
+  // Custom margins using the |margin_*| values from cef_pdf_print_settings_t.
+  ///
+  PDF_PRINT_MARGIN_CUSTOM,
+} cef_pdf_print_margin_type_t;
+
+///
+// Structure representing PDF print settings.
+///
+typedef struct _cef_pdf_print_settings_t {
+  ///
+  // Page title to display in the header. Only used if |header_footer_enabled|
+  // is set to true (1).
+  ///
+  cef_string_t header_footer_title;
+
+  ///
+  // URL to display in the footer. Only used if |header_footer_enabled| is set
+  // to true (1).
+  ///
+  cef_string_t header_footer_url;
+
+  ///
+  // Output page size in microns. If either of these values is less than or
+  // equal to zero then the default paper size (A4) will be used.
+  ///
+  int page_width;
+  int page_height;
+
+  ///
+  // The percentage to scale the PDF by before printing (e.g. 50 is 50%).
+  // If this value is less than or equal to zero the default value of 100
+  // will be used.
+  ///
+  int scale_factor;
+
+  ///
+  // Margins in points. Only used if |margin_type| is set to
+  // PDF_PRINT_MARGIN_CUSTOM.
+  ///
+  int margin_top;
+  int margin_right;
+  int margin_bottom;
+  int margin_left;
+
+  ///
+  // Margin type.
+  ///
+  cef_pdf_print_margin_type_t margin_type;
+
+  ///
+  // Set to true (1) to print headers and footers or false (0) to not print
+  // headers and footers.
+  ///
+  int header_footer_enabled;
+
+  ///
+  // Set to true (1) to print the selection only or false (0) to print all.
+  ///
+  int selection_only;
+
+  ///
+  // Set to true (1) for landscape mode or false (0) for portrait mode.
+  ///
+  int landscape;
+
+  ///
+  // Set to true (1) to print background graphics or false (0) to not print
+  // background graphics.
+  ///
+  int backgrounds_enabled;
+
+} cef_pdf_print_settings_t;
+
+///
+// Supported UI scale factors for the platform. SCALE_FACTOR_NONE is used for
+// density independent resources such as string, html/js files or an image that
+// can be used for any scale factors (such as wallpapers).
+///
+typedef enum {
+  SCALE_FACTOR_NONE = 0,
+  SCALE_FACTOR_100P,
+  SCALE_FACTOR_125P,
+  SCALE_FACTOR_133P,
+  SCALE_FACTOR_140P,
+  SCALE_FACTOR_150P,
+  SCALE_FACTOR_180P,
+  SCALE_FACTOR_200P,
+  SCALE_FACTOR_250P,
+  SCALE_FACTOR_300P,
+} cef_scale_factor_t;
+
+///
+// Plugin policies supported by CefRequestContextHandler::OnBeforePluginLoad.
+///
+typedef enum {
+  ///
+  // Allow the content.
+  ///
+  PLUGIN_POLICY_ALLOW,
+
+  ///
+  // Allow important content and block unimportant content based on heuristics.
+  // The user can manually load blocked content.
+  ///
+  PLUGIN_POLICY_DETECT_IMPORTANT,
+
+  ///
+  // Block the content. The user can manually load blocked content.
+  ///
+  PLUGIN_POLICY_BLOCK,
+
+  ///
+  // Disable the content. The user cannot load disabled content.
+  ///
+  PLUGIN_POLICY_DISABLE,
+} cef_plugin_policy_t;
+
+///
+// Policy for how the Referrer HTTP header value will be sent during navigation.
+// If the `--no-referrers` command-line flag is specified then the policy value
+// will be ignored and the Referrer value will never be sent.
+// Must be kept synchronized with net::URLRequest::ReferrerPolicy from Chromium.
+///
+typedef enum {
+  ///
+  // Clear the referrer header if the header value is HTTPS but the request
+  // destination is HTTP. This is the default behavior.
+  ///
+  REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+  REFERRER_POLICY_DEFAULT =
+      REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+
+  ///
+  // A slight variant on CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+  // If the request destination is HTTP, an HTTPS referrer will be cleared. If
+  // the request's destination is cross-origin with the referrer (but does not
+  // downgrade), the referrer's granularity will be stripped down to an origin
+  // rather than a full URL. Same-origin requests will send the full referrer.
+  ///
+  REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+
+  ///
+  // Strip the referrer down to an origin when the origin of the referrer is
+  // different from the destination's origin.
+  ///
+  REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+
+  ///
+  // Never change the referrer.
+  ///
+  REFERRER_POLICY_NEVER_CLEAR_REFERRER,
+
+  ///
+  // Strip the referrer down to the origin regardless of the redirect location.
+  ///
+  REFERRER_POLICY_ORIGIN,
+
+  ///
+  // Clear the referrer when the request's referrer is cross-origin with the
+  // request's destination.
+  ///
+  REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN,
+
+  ///
+  // Strip the referrer down to the origin, but clear it entirely if the
+  // referrer value is HTTPS and the destination is HTTP.
+  ///
+  REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+
+  ///
+  // Always clear the referrer regardless of the request destination.
+  ///
+  REFERRER_POLICY_NO_REFERRER,
+
+  // Always the last value in this enumeration.
+  REFERRER_POLICY_LAST_VALUE = REFERRER_POLICY_NO_REFERRER,
+} cef_referrer_policy_t;
+
+///
+// Return values for CefResponseFilter::Filter().
+///
+typedef enum {
+  ///
+  // Some or all of the pre-filter data was read successfully but more data is
+  // needed in order to continue filtering (filtered output is pending).
+  ///
+  RESPONSE_FILTER_NEED_MORE_DATA,
+
+  ///
+  // Some or all of the pre-filter data was read successfully and all available
+  // filtered output has been written.
+  ///
+  RESPONSE_FILTER_DONE,
+
+  ///
+  // An error occurred during filtering.
+  ///
+  RESPONSE_FILTER_ERROR
+} cef_response_filter_status_t;
+
+///
+// Describes how to interpret the components of a pixel.
+///
+typedef enum {
+  ///
+  // RGBA with 8 bits per pixel (32bits total).
+  ///
+  CEF_COLOR_TYPE_RGBA_8888,
+
+  ///
+  // BGRA with 8 bits per pixel (32bits total).
+  ///
+  CEF_COLOR_TYPE_BGRA_8888,
+} cef_color_type_t;
+
+///
+// Describes how to interpret the alpha component of a pixel.
+///
+typedef enum {
+  ///
+  // No transparency. The alpha component is ignored.
+  ///
+  CEF_ALPHA_TYPE_OPAQUE,
+
+  ///
+  // Transparency with pre-multiplied alpha component.
+  ///
+  CEF_ALPHA_TYPE_PREMULTIPLIED,
+
+  ///
+  // Transparency with post-multiplied alpha component.
+  ///
+  CEF_ALPHA_TYPE_POSTMULTIPLIED,
+} cef_alpha_type_t;
+
+///
+// Text style types. Should be kepy in sync with gfx::TextStyle.
+///
+typedef enum {
+  CEF_TEXT_STYLE_BOLD,
+  CEF_TEXT_STYLE_ITALIC,
+  CEF_TEXT_STYLE_STRIKE,
+  CEF_TEXT_STYLE_DIAGONAL_STRIKE,
+  CEF_TEXT_STYLE_UNDERLINE,
+} cef_text_style_t;
+
+///
+// Specifies where along the main axis the CefBoxLayout child views should be
+// laid out.
+///
+typedef enum {
+  ///
+  // Child views will be left-aligned.
+  ///
+  CEF_MAIN_AXIS_ALIGNMENT_START,
+
+  ///
+  // Child views will be center-aligned.
+  ///
+  CEF_MAIN_AXIS_ALIGNMENT_CENTER,
+
+  ///
+  // Child views will be right-aligned.
+  ///
+  CEF_MAIN_AXIS_ALIGNMENT_END,
+} cef_main_axis_alignment_t;
+
+///
+// Specifies where along the cross axis the CefBoxLayout child views should be
+// laid out.
+///
+typedef enum {
+  ///
+  // Child views will be stretched to fit.
+  ///
+  CEF_CROSS_AXIS_ALIGNMENT_STRETCH,
+
+  ///
+  // Child views will be left-aligned.
+  ///
+  CEF_CROSS_AXIS_ALIGNMENT_START,
+
+  ///
+  // Child views will be center-aligned.
+  ///
+  CEF_CROSS_AXIS_ALIGNMENT_CENTER,
+
+  ///
+  // Child views will be right-aligned.
+  ///
+  CEF_CROSS_AXIS_ALIGNMENT_END,
+} cef_cross_axis_alignment_t;
+
+///
+// Settings used when initializing a CefBoxLayout.
+///
+typedef struct _cef_box_layout_settings_t {
+  ///
+  // If true (1) the layout will be horizontal, otherwise the layout will be
+  // vertical.
+  ///
+  int horizontal;
+
+  ///
+  // Adds additional horizontal space between the child view area and the host
+  // view border.
+  ///
+  int inside_border_horizontal_spacing;
+
+  ///
+  // Adds additional vertical space between the child view area and the host
+  // view border.
+  ///
+  int inside_border_vertical_spacing;
+
+  ///
+  // Adds additional space around the child view area.
+  ///
+  cef_insets_t inside_border_insets;
+
+  ///
+  // Adds additional space between child views.
+  ///
+  int between_child_spacing;
+
+  ///
+  // Specifies where along the main axis the child views should be laid out.
+  ///
+  cef_main_axis_alignment_t main_axis_alignment;
+
+  ///
+  // Specifies where along the cross axis the child views should be laid out.
+  ///
+  cef_cross_axis_alignment_t cross_axis_alignment;
+
+  ///
+  // Minimum cross axis size.
+  ///
+  int minimum_cross_axis_size;
+
+  ///
+  // Default flex for views when none is specified via CefBoxLayout methods.
+  // Using the preferred size as the basis, free space along the main axis is
+  // distributed to views in the ratio of their flex weights. Similarly, if the
+  // views will overflow the parent, space is subtracted in these ratios. A flex
+  // of 0 means this view is not resized. Flex values must not be negative.
+  ///
+  int default_flex;
+} cef_box_layout_settings_t;
+
+///
+// Specifies the button display state.
+///
+typedef enum {
+  CEF_BUTTON_STATE_NORMAL,
+  CEF_BUTTON_STATE_HOVERED,
+  CEF_BUTTON_STATE_PRESSED,
+  CEF_BUTTON_STATE_DISABLED,
+} cef_button_state_t;
+
+///
+// Specifies the horizontal text alignment mode.
+///
+typedef enum {
+  ///
+  // Align the text's left edge with that of its display area.
+  ///
+  CEF_HORIZONTAL_ALIGNMENT_LEFT,
+
+  ///
+  // Align the text's center with that of its display area.
+  ///
+  CEF_HORIZONTAL_ALIGNMENT_CENTER,
+
+  ///
+  // Align the text's right edge with that of its display area.
+  ///
+  CEF_HORIZONTAL_ALIGNMENT_RIGHT,
+} cef_horizontal_alignment_t;
+
+///
+// Specifies how a menu will be anchored for non-RTL languages. The opposite
+// position will be used for RTL languages.
+///
+typedef enum {
+  CEF_MENU_ANCHOR_TOPLEFT,
+  CEF_MENU_ANCHOR_TOPRIGHT,
+  CEF_MENU_ANCHOR_BOTTOMCENTER,
+} cef_menu_anchor_position_t;
+
+///
+// Supported color types for menu items.
+///
+typedef enum {
+  CEF_MENU_COLOR_TEXT,
+  CEF_MENU_COLOR_TEXT_HOVERED,
+  CEF_MENU_COLOR_TEXT_ACCELERATOR,
+  CEF_MENU_COLOR_TEXT_ACCELERATOR_HOVERED,
+  CEF_MENU_COLOR_BACKGROUND,
+  CEF_MENU_COLOR_BACKGROUND_HOVERED,
+  CEF_MENU_COLOR_COUNT,
+} cef_menu_color_type_t;
+
+// Supported SSL version values. See net/ssl/ssl_connection_status_flags.h
+// for more information.
+typedef enum {
+  SSL_CONNECTION_VERSION_UNKNOWN = 0,  // Unknown SSL version.
+  SSL_CONNECTION_VERSION_SSL2 = 1,
+  SSL_CONNECTION_VERSION_SSL3 = 2,
+  SSL_CONNECTION_VERSION_TLS1 = 3,
+  SSL_CONNECTION_VERSION_TLS1_1 = 4,
+  SSL_CONNECTION_VERSION_TLS1_2 = 5,
+  SSL_CONNECTION_VERSION_TLS1_3 = 6,
+  SSL_CONNECTION_VERSION_QUIC = 7,
+} cef_ssl_version_t;
+
+// Supported SSL content status flags. See content/public/common/ssl_status.h
+// for more information.
+typedef enum {
+  SSL_CONTENT_NORMAL_CONTENT = 0,
+  SSL_CONTENT_DISPLAYED_INSECURE_CONTENT = 1 << 0,
+  SSL_CONTENT_RAN_INSECURE_CONTENT = 1 << 1,
+} cef_ssl_content_status_t;
+
+//
+// Configuration options for registering a custom scheme.
+// These values are used when calling AddCustomScheme.
+//
+typedef enum {
+  CEF_SCHEME_OPTION_NONE = 0,
+
+  ///
+  // If CEF_SCHEME_OPTION_STANDARD is set the scheme will be treated as a
+  // standard scheme. Standard schemes are subject to URL canonicalization and
+  // parsing rules as defined in the Common Internet Scheme Syntax RFC 1738
+  // Section 3.1 available at http://www.ietf.org/rfc/rfc1738.txt
+  //
+  // In particular, the syntax for standard scheme URLs must be of the form:
+  // <pre>
+  //  [scheme]://[username]:[password]@[host]:[port]/[url-path]
+  // </pre> Standard scheme URLs must have a host component that is a fully
+  // qualified domain name as defined in Section 3.5 of RFC 1034 [13] and
+  // Section 2.1 of RFC 1123. These URLs will be canonicalized to
+  // "scheme://host/path" in the simplest case and
+  // "scheme://username:password@host:port/path" in the most explicit case. For
+  // example, "scheme:host/path" and "scheme:///host/path" will both be
+  // canonicalized to "scheme://host/path". The origin of a standard scheme URL
+  // is the combination of scheme, host and port (i.e., "scheme://host:port" in
+  // the most explicit case).
+  //
+  // For non-standard scheme URLs only the "scheme:" component is parsed and
+  // canonicalized. The remainder of the URL will be passed to the handler as-
+  // is. For example, "scheme:///some%20text" will remain the same. Non-standard
+  // scheme URLs cannot be used as a target for form submission.
+  ///
+  CEF_SCHEME_OPTION_STANDARD = 1 << 0,
+
+  ///
+  // If CEF_SCHEME_OPTION_LOCAL is set the scheme will be treated with the same
+  // security rules as those applied to "file" URLs. Normal pages cannot link to
+  // or access local URLs. Also, by default, local URLs can only perform
+  // XMLHttpRequest calls to the same URL (origin + path) that originated the
+  // request. To allow XMLHttpRequest calls from a local URL to other URLs with
+  // the same origin set the CefSettings.file_access_from_file_urls_allowed
+  // value to true (1). To allow XMLHttpRequest calls from a local URL to all
+  // origins set the CefSettings.universal_access_from_file_urls_allowed value
+  // to true (1).
+  ///
+  CEF_SCHEME_OPTION_LOCAL = 1 << 1,
+
+  ///
+  // If CEF_SCHEME_OPTION_DISPLAY_ISOLATED is set the scheme can only be
+  // displayed from other content hosted with the same scheme. For example,
+  // pages in other origins cannot create iframes or hyperlinks to URLs with the
+  // scheme. For schemes that must be accessible from other schemes don't set
+  // this, set CEF_SCHEME_OPTION_CORS_ENABLED, and use CORS
+  // "Access-Control-Allow-Origin" headers to further restrict access.
+  ///
+  CEF_SCHEME_OPTION_DISPLAY_ISOLATED = 1 << 2,
+
+  ///
+  // If CEF_SCHEME_OPTION_SECURE is set the scheme will be treated with the same
+  // security rules as those applied to "https" URLs. For example, loading this
+  // scheme from other secure schemes will not trigger mixed content warnings.
+  ///
+  CEF_SCHEME_OPTION_SECURE = 1 << 3,
+
+  ///
+  // If CEF_SCHEME_OPTION_CORS_ENABLED is set the scheme can be sent CORS
+  // requests. This value should be set in most cases where
+  // CEF_SCHEME_OPTION_STANDARD is set.
+  ///
+  CEF_SCHEME_OPTION_CORS_ENABLED = 1 << 4,
+
+  ///
+  // If CEF_SCHEME_OPTION_CSP_BYPASSING is set the scheme can bypass Content-
+  // Security-Policy (CSP) checks. This value should not be set in most cases
+  // where CEF_SCHEME_OPTION_STANDARD is set.
+  ///
+  CEF_SCHEME_OPTION_CSP_BYPASSING = 1 << 5,
+
+  ///
+  // If CEF_SCHEME_OPTION_FETCH_ENABLED is set the scheme can perform Fetch API
+  // requests.
+  ///
+  CEF_SCHEME_OPTION_FETCH_ENABLED = 1 << 6,
+} cef_scheme_options_t;
+
+///
+// Error codes for CDM registration. See cef_web_plugin.h for details.
+///
+typedef enum {
+  ///
+  // No error. Registration completed successfully.
+  ///
+  CEF_CDM_REGISTRATION_ERROR_NONE,
+
+  ///
+  // Required files or manifest contents are missing.
+  ///
+  CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS,
+
+  ///
+  // The CDM is incompatible with the current Chromium version.
+  ///
+  CEF_CDM_REGISTRATION_ERROR_INCOMPATIBLE,
+
+  ///
+  // CDM registration is not supported at this time.
+  ///
+  CEF_CDM_REGISTRATION_ERROR_NOT_SUPPORTED,
+} cef_cdm_registration_error_t;
+
+///
+// Composition underline style.
+///
+typedef enum {
+  CEF_CUS_SOLID,
+  CEF_CUS_DOT,
+  CEF_CUS_DASH,
+  CEF_CUS_NONE,
+} cef_composition_underline_style_t;
+
+///
+// Structure representing IME composition underline information. This is a thin
+// wrapper around Blink's WebCompositionUnderline class and should be kept in
+// sync with that.
+///
+typedef struct _cef_composition_underline_t {
+  ///
+  // Underline character range.
+  ///
+  cef_range_t range;
+
+  ///
+  // Text color.
+  ///
+  cef_color_t color;
+
+  ///
+  // Background color.
+  ///
+  cef_color_t background_color;
+
+  ///
+  // Set to true (1) for thick underline.
+  ///
+  int thick;
+
+  ///
+  // Style.
+  ///
+  cef_composition_underline_style_t style;
+} cef_composition_underline_t;
+
+///
+// Enumerates the various representations of the ordering of audio channels.
+// Must be kept synchronized with media::ChannelLayout from Chromium.
+// See media\base\channel_layout.h
+///
+typedef enum {
+  CEF_CHANNEL_LAYOUT_NONE = 0,
+  CEF_CHANNEL_LAYOUT_UNSUPPORTED = 1,
+
+  // Front C
+  CEF_CHANNEL_LAYOUT_MONO = 2,
+
+  // Front L, Front R
+  CEF_CHANNEL_LAYOUT_STEREO = 3,
+
+  // Front L, Front R, Back C
+  CEF_CHANNEL_LAYOUT_2_1 = 4,
+
+  // Front L, Front R, Front C
+  CEF_CHANNEL_LAYOUT_SURROUND = 5,
+
+  // Front L, Front R, Front C, Back C
+  CEF_CHANNEL_LAYOUT_4_0 = 6,
+
+  // Front L, Front R, Side L, Side R
+  CEF_CHANNEL_LAYOUT_2_2 = 7,
+
+  // Front L, Front R, Back L, Back R
+  CEF_CHANNEL_LAYOUT_QUAD = 8,
+
+  // Front L, Front R, Front C, Side L, Side R
+  CEF_CHANNEL_LAYOUT_5_0 = 9,
+
+  // Front L, Front R, Front C, LFE, Side L, Side R
+  CEF_CHANNEL_LAYOUT_5_1 = 10,
+
+  // Front L, Front R, Front C, Back L, Back R
+  CEF_CHANNEL_LAYOUT_5_0_BACK = 11,
+
+  // Front L, Front R, Front C, LFE, Back L, Back R
+  CEF_CHANNEL_LAYOUT_5_1_BACK = 12,
+
+  // Front L, Front R, Front C, Side L, Side R, Back L, Back R
+  CEF_CHANNEL_LAYOUT_7_0 = 13,
+
+  // Front L, Front R, Front C, LFE, Side L, Side R, Back L, Back R
+  CEF_CHANNEL_LAYOUT_7_1 = 14,
+
+  // Front L, Front R, Front C, LFE, Side L, Side R, Front LofC, Front RofC
+  CEF_CHANNEL_LAYOUT_7_1_WIDE = 15,
+
+  // Stereo L, Stereo R
+  CEF_CHANNEL_LAYOUT_STEREO_DOWNMIX = 16,
+
+  // Stereo L, Stereo R, LFE
+  CEF_CHANNEL_LAYOUT_2POINT1 = 17,
+
+  // Stereo L, Stereo R, Front C, LFE
+  CEF_CHANNEL_LAYOUT_3_1 = 18,
+
+  // Stereo L, Stereo R, Front C, Rear C, LFE
+  CEF_CHANNEL_LAYOUT_4_1 = 19,
+
+  // Stereo L, Stereo R, Front C, Side L, Side R, Back C
+  CEF_CHANNEL_LAYOUT_6_0 = 20,
+
+  // Stereo L, Stereo R, Side L, Side R, Front LofC, Front RofC
+  CEF_CHANNEL_LAYOUT_6_0_FRONT = 21,
+
+  // Stereo L, Stereo R, Front C, Rear L, Rear R, Rear C
+  CEF_CHANNEL_LAYOUT_HEXAGONAL = 22,
+
+  // Stereo L, Stereo R, Front C, LFE, Side L, Side R, Rear Center
+  CEF_CHANNEL_LAYOUT_6_1 = 23,
+
+  // Stereo L, Stereo R, Front C, LFE, Back L, Back R, Rear Center
+  CEF_CHANNEL_LAYOUT_6_1_BACK = 24,
+
+  // Stereo L, Stereo R, Side L, Side R, Front LofC, Front RofC, LFE
+  CEF_CHANNEL_LAYOUT_6_1_FRONT = 25,
+
+  // Front L, Front R, Front C, Side L, Side R, Front LofC, Front RofC
+  CEF_CHANNEL_LAYOUT_7_0_FRONT = 26,
+
+  // Front L, Front R, Front C, LFE, Back L, Back R, Front LofC, Front RofC
+  CEF_CHANNEL_LAYOUT_7_1_WIDE_BACK = 27,
+
+  // Front L, Front R, Front C, Side L, Side R, Rear L, Back R, Back C.
+  CEF_CHANNEL_LAYOUT_OCTAGONAL = 28,
+
+  // Channels are not explicitly mapped to speakers.
+  CEF_CHANNEL_LAYOUT_DISCRETE = 29,
+
+  // Front L, Front R, Front C. Front C contains the keyboard mic audio. This
+  // layout is only intended for input for WebRTC. The Front C channel
+  // is stripped away in the WebRTC audio input pipeline and never seen outside
+  // of that.
+  CEF_CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC = 30,
+
+  // Front L, Front R, Side L, Side R, LFE
+  CEF_CHANNEL_LAYOUT_4_1_QUAD_SIDE = 31,
+
+  // Actual channel layout is specified in the bitstream and the actual channel
+  // count is unknown at Chromium media pipeline level (useful for audio
+  // pass-through mode).
+  CEF_CHANNEL_LAYOUT_BITSTREAM = 32,
+
+  // Max value, must always equal the largest entry ever logged.
+  CEF_CHANNEL_LAYOUT_MAX = CEF_CHANNEL_LAYOUT_BITSTREAM
+} cef_channel_layout_t;
+
+///
+// Structure representing the audio parameters for setting up the audio handler.
+///
+typedef struct _cef_audio_parameters_t {
+  ///
+  // Layout of the audio channels
+  ///
+  cef_channel_layout_t channel_layout;
+
+  ///
+  // Sample rate
+  //
+  int sample_rate;
+
+  ///
+  // Number of frames per buffer
+  ///
+  int frames_per_buffer;
+} cef_audio_parameters_t;
+
+///
+// Result codes for CefMediaRouter::CreateRoute. Should be kept in sync with
+// Chromium's media_router::RouteRequestResult::ResultCode type.
+///
+typedef enum {
+  CEF_MRCR_UNKNOWN_ERROR = 0,
+  CEF_MRCR_OK = 1,
+  CEF_MRCR_TIMED_OUT = 2,
+  CEF_MRCR_ROUTE_NOT_FOUND = 3,
+  CEF_MRCR_SINK_NOT_FOUND = 4,
+  CEF_MRCR_INVALID_ORIGIN = 5,
+  CEF_MRCR_NO_SUPPORTED_PROVIDER = 7,
+  CEF_MRCR_CANCELLED = 8,
+  CEF_MRCR_ROUTE_ALREADY_EXISTS = 9,
+
+  CEF_MRCR_TOTAL_COUNT = 11  // The total number of values.
+} cef_media_route_create_result_t;
+
+///
+// Connection state for a MediaRoute object.
+///
+typedef enum {
+  CEF_MRCS_UNKNOWN,
+  CEF_MRCS_CONNECTING,
+  CEF_MRCS_CONNECTED,
+  CEF_MRCS_CLOSED,
+  CEF_MRCS_TERMINATED,
+} cef_media_route_connection_state_t;
+
+///
+// Icon types for a MediaSink object. Should be kept in sync with Chromium's
+// media_router::SinkIconType type.
+///
+typedef enum {
+  CEF_MSIT_CAST,
+  CEF_MSIT_CAST_AUDIO_GROUP,
+  CEF_MSIT_CAST_AUDIO,
+  CEF_MSIT_MEETING,
+  CEF_MSIT_HANGOUT,
+  CEF_MSIT_EDUCATION,
+  CEF_MSIT_WIRED_DISPLAY,
+  CEF_MSIT_GENERIC,
+
+  CEF_MSIT_TOTAL_COUNT,  // The total number of values.
+} cef_media_sink_icon_type_t;
+
+///
+// Device information for a MediaSink object.
+///
+typedef struct _cef_media_sink_device_info_t {
+  cef_string_t ip_address;
+  int port;
+  cef_string_t model_name;
+} cef_media_sink_device_info_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_TYPES_H_
diff --git a/src/include/internal/cef_types_linux.h b/src/include/internal/cef_types_linux.h
new file mode 100644
index 0000000..b21d655
--- /dev/null
+++ b/src/include/internal/cef_types_linux.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_
+#define CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+#include "include/cef_config.h"
+
+#if defined(OS_LINUX)
+
+#if defined(CEF_X11)
+typedef union _XEvent XEvent;
+typedef struct _XDisplay XDisplay;
+#endif
+
+#include "include/internal/cef_export.h"
+#include "include/internal/cef_string.h"
+
+// Handle types.
+#if defined(CEF_X11)
+#define cef_cursor_handle_t unsigned long
+#define cef_event_handle_t XEvent*
+#else
+#define cef_cursor_handle_t void*
+#define cef_event_handle_t void*
+#endif
+
+#define cef_window_handle_t unsigned long
+
+#define kNullCursorHandle 0
+#define kNullEventHandle NULL
+#define kNullWindowHandle 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Return the singleton X11 display shared with Chromium. The display is not
+// thread-safe and must only be accessed on the browser process UI thread.
+///
+#if defined(CEF_X11)
+CEF_EXPORT XDisplay* cef_get_xdisplay();
+#endif
+
+///
+// Structure representing CefExecuteProcess arguments.
+///
+typedef struct _cef_main_args_t {
+  int argc;
+  char** argv;
+} cef_main_args_t;
+
+///
+// Class representing window information.
+///
+typedef struct _cef_window_info_t {
+  ///
+  // The initial title of the window, to be set when the window is created.
+  // Some layout managers (e.g., Compiz) can look at the window title
+  // in order to decide where to place the window when it is
+  // created. When this attribute is not empty, the window title will
+  // be set before the window is mapped to the dispay. Otherwise the
+  // title will be initially empty.
+  ///
+  cef_string_t window_name;
+
+  unsigned int x;
+  unsigned int y;
+  unsigned int width;
+  unsigned int height;
+
+  ///
+  // Pointer for the parent window.
+  ///
+  cef_window_handle_t parent_window;
+
+  ///
+  // Set to true (1) to create the browser using windowless (off-screen)
+  // rendering. No window will be created for the browser and all rendering will
+  // occur via the CefRenderHandler interface. The |parent_window| value will be
+  // used to identify monitor info and to act as the parent window for dialogs,
+  // context menus, etc. If |parent_window| is not provided then the main screen
+  // monitor will be used and some functionality that requires a parent window
+  // may not function correctly. In order to create windowless browsers the
+  // CefSettings.windowless_rendering_enabled value must be set to true.
+  // Transparent painting is enabled by default but can be disabled by setting
+  // CefBrowserSettings.background_color to an opaque value.
+  ///
+  int windowless_rendering_enabled;
+
+  ///
+  // Set to true (1) to enable shared textures for windowless rendering. Only
+  // valid if windowless_rendering_enabled above is also set to true. Currently
+  // only supported on Windows (D3D11).
+  ///
+  int shared_texture_enabled;
+
+  ///
+  // Set to true (1) to enable the ability to issue BeginFrame requests from the
+  // client application by calling CefBrowserHost::SendExternalBeginFrame.
+  ///
+  int external_begin_frame_enabled;
+
+  ///
+  // Pointer for the new browser window. Only used with windowed rendering.
+  ///
+  cef_window_handle_t window;
+} cef_window_info_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OS_LINUX
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_TYPES_LINUX_H_
diff --git a/src/include/internal/cef_types_mac.h b/src/include/internal/cef_types_mac.h
new file mode 100644
index 0000000..14e61da
--- /dev/null
+++ b/src/include/internal/cef_types_mac.h
@@ -0,0 +1,142 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_
+#define CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+
+#if defined(OS_MACOSX)
+#include "include/internal/cef_string.h"
+
+// Handle types.
+// Actually NSCursor*
+#define cef_cursor_handle_t void*
+// Acutally NSEvent*
+#define cef_event_handle_t void*
+// Actually NSView*
+#define cef_window_handle_t void*
+
+#define kNullCursorHandle NULL
+#define kNullEventHandle NULL
+#define kNullWindowHandle NULL
+
+#ifdef __OBJC__
+#if __has_feature(objc_arc)
+#define CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(handle) ((__bridge NSCursor*)handle)
+#define CAST_CEF_EVENT_HANDLE_TO_NSEVENT(handle) ((__bridge NSEvent*)handle)
+#define CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(handle) ((__bridge NSView*)handle)
+
+#define CAST_NSCURSOR_TO_CEF_CURSOR_HANDLE(cursor) ((__bridge void*)cursor)
+#define CAST_NSEVENT_TO_CEF_EVENT_HANDLE(event) ((__bridge void*)event)
+#define CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(view) ((__bridge void*)view)
+#else  // __has_feature(objc_arc)
+#define CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(handle) ((NSCursor*)handle)
+#define CAST_CEF_EVENT_HANDLE_TO_NSEVENT(handle) ((NSEvent*)handle)
+#define CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(handle) ((NSView*)handle)
+
+#define CAST_NSCURSOR_TO_CEF_CURSOR_HANDLE(cursor) ((void*)cursor)
+#define CAST_NSEVENT_TO_CEF_EVENT_HANDLE(event) ((void*)event)
+#define CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(view) ((void*)view)
+#endif  // __has_feature(objc_arc)
+#endif  // __OBJC__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure representing CefExecuteProcess arguments.
+///
+typedef struct _cef_main_args_t {
+  int argc;
+  char** argv;
+} cef_main_args_t;
+
+///
+// Class representing window information.
+///
+typedef struct _cef_window_info_t {
+  cef_string_t window_name;
+  int x;
+  int y;
+  int width;
+  int height;
+
+  ///
+  // Set to true (1) to create the view initially hidden.
+  ///
+  int hidden;
+
+  ///
+  // NSView pointer for the parent view.
+  ///
+  cef_window_handle_t parent_view;
+
+  ///
+  // Set to true (1) to create the browser using windowless (off-screen)
+  // rendering. No view will be created for the browser and all rendering will
+  // occur via the CefRenderHandler interface. The |parent_view| value will be
+  // used to identify monitor info and to act as the parent view for dialogs,
+  // context menus, etc. If |parent_view| is not provided then the main screen
+  // monitor will be used and some functionality that requires a parent view
+  // may not function correctly. In order to create windowless browsers the
+  // CefSettings.windowless_rendering_enabled value must be set to true.
+  // Transparent painting is enabled by default but can be disabled by setting
+  // CefBrowserSettings.background_color to an opaque value.
+  ///
+  int windowless_rendering_enabled;
+
+  ///
+  // Set to true (1) to enable shared textures for windowless rendering. Only
+  // valid if windowless_rendering_enabled above is also set to true. Currently
+  // only supported on Windows (D3D11).
+  ///
+  int shared_texture_enabled;
+
+  ///
+  // Set to true (1) to enable the ability to issue BeginFrame from the client
+  // application.
+  ///
+  int external_begin_frame_enabled;
+
+  ///
+  // NSView pointer for the new browser view. Only used with windowed rendering.
+  ///
+  cef_window_handle_t view;
+} cef_window_info_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OS_MACOSX
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_TYPES_MAC_H_
diff --git a/src/include/internal/cef_types_win.h b/src/include/internal/cef_types_win.h
new file mode 100644
index 0000000..dce8967
--- /dev/null
+++ b/src/include/internal/cef_types_win.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2009 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_
+#define CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "include/internal/cef_string.h"
+
+// Handle types.
+#define cef_cursor_handle_t HCURSOR
+#define cef_event_handle_t MSG*
+#define cef_window_handle_t HWND
+
+#define kNullCursorHandle NULL
+#define kNullEventHandle NULL
+#define kNullWindowHandle NULL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Structure representing CefExecuteProcess arguments.
+///
+typedef struct _cef_main_args_t { HINSTANCE instance; } cef_main_args_t;
+
+///
+// Structure representing window information.
+///
+typedef struct _cef_window_info_t {
+  // Standard parameters required by CreateWindowEx()
+  DWORD ex_style;
+  cef_string_t window_name;
+  DWORD style;
+  int x;
+  int y;
+  int width;
+  int height;
+  cef_window_handle_t parent_window;
+  HMENU menu;
+
+  ///
+  // Set to true (1) to create the browser using windowless (off-screen)
+  // rendering. No window will be created for the browser and all rendering will
+  // occur via the CefRenderHandler interface. The |parent_window| value will be
+  // used to identify monitor info and to act as the parent window for dialogs,
+  // context menus, etc. If |parent_window| is not provided then the main screen
+  // monitor will be used and some functionality that requires a parent window
+  // may not function correctly. In order to create windowless browsers the
+  // CefSettings.windowless_rendering_enabled value must be set to true.
+  // Transparent painting is enabled by default but can be disabled by setting
+  // CefBrowserSettings.background_color to an opaque value.
+  ///
+  int windowless_rendering_enabled;
+
+  ///
+  // Set to true (1) to enable shared textures for windowless rendering. Only
+  // valid if windowless_rendering_enabled above is also set to true. Currently
+  // only supported on Windows (D3D11).
+  ///
+  int shared_texture_enabled;
+
+  ///
+  // Set to true (1) to enable the ability to issue BeginFrame requests from the
+  // client application by calling CefBrowserHost::SendExternalBeginFrame.
+  ///
+  int external_begin_frame_enabled;
+
+  ///
+  // Handle for the new browser window. Only used with windowed rendering.
+  ///
+  cef_window_handle_t window;
+} cef_window_info_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OS_WIN
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_TYPES_WIN_H_
diff --git a/src/include/internal/cef_types_wrappers.h b/src/include/internal/cef_types_wrappers.h
new file mode 100644
index 0000000..0e8caec
--- /dev/null
+++ b/src/include/internal/cef_types_wrappers.h
@@ -0,0 +1,1008 @@
+// Copyright (c) 2013 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_TYPES_WRAPPERS_H_
+#define CEF_INCLUDE_INTERNAL_CEF_TYPES_WRAPPERS_H_
+#pragma once
+
+#include "include/internal/cef_string.h"
+#include "include/internal/cef_string_list.h"
+#include "include/internal/cef_types.h"
+
+///
+// Template class that provides common functionality for CEF structure wrapping.
+///
+template <class traits>
+class CefStructBase : public traits::struct_type {
+ public:
+  typedef typename traits::struct_type struct_type;
+
+  CefStructBase() : attached_to_(NULL) { Init(); }
+  virtual ~CefStructBase() {
+    // Only clear this object's data if it isn't currently attached to a
+    // structure.
+    if (!attached_to_)
+      Clear(this);
+  }
+
+  CefStructBase(const CefStructBase& r) {
+    Init();
+    *this = r;
+  }
+  CefStructBase(const struct_type& r) {
+    Init();
+    *this = r;
+  }
+
+  ///
+  // Clear this object's values.
+  ///
+  void Reset() {
+    Clear(this);
+    Init();
+  }
+
+  ///
+  // Attach to the source structure's existing values. DetachTo() can be called
+  // to insert the values back into the existing structure.
+  ///
+  void AttachTo(struct_type& source) {
+    // Only clear this object's data if it isn't currently attached to a
+    // structure.
+    if (!attached_to_)
+      Clear(this);
+
+    // This object is now attached to the new structure.
+    attached_to_ = &source;
+
+    // Transfer ownership of the values from the source structure.
+    memcpy(static_cast<struct_type*>(this), &source, sizeof(struct_type));
+  }
+
+  ///
+  // Relinquish ownership of values to the target structure.
+  ///
+  void DetachTo(struct_type& target) {
+    if (attached_to_ != &target) {
+      // Clear the target structure's values only if we are not currently
+      // attached to that structure.
+      Clear(&target);
+    }
+
+    // Transfer ownership of the values to the target structure.
+    memcpy(&target, static_cast<struct_type*>(this), sizeof(struct_type));
+
+    // Remove the references from this object.
+    Init();
+  }
+
+  ///
+  // Set this object's values. If |copy| is true the source structure's values
+  // will be copied instead of referenced.
+  ///
+  void Set(const struct_type& source, bool copy) {
+    traits::set(&source, this, copy);
+  }
+
+  CefStructBase& operator=(const CefStructBase& s) {
+    return operator=(static_cast<const struct_type&>(s));
+  }
+
+  CefStructBase& operator=(const struct_type& s) {
+    Set(s, true);
+    return *this;
+  }
+
+ protected:
+  void Init() {
+    memset(static_cast<struct_type*>(this), 0, sizeof(struct_type));
+    attached_to_ = NULL;
+    traits::init(this);
+  }
+
+  static void Clear(struct_type* s) { traits::clear(s); }
+
+  struct_type* attached_to_;
+};
+
+struct CefPointTraits {
+  typedef cef_point_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing a point.
+///
+class CefPoint : public CefStructBase<CefPointTraits> {
+ public:
+  typedef CefStructBase<CefPointTraits> parent;
+
+  CefPoint() : parent() {}
+  CefPoint(const cef_point_t& r) : parent(r) {}
+  CefPoint(const CefPoint& r) : parent(r) {}
+  CefPoint(int x, int y) : parent() { Set(x, y); }
+
+  bool IsEmpty() const { return x <= 0 && y <= 0; }
+  void Set(int x_val, int y_val) { x = x_val, y = y_val; }
+};
+
+inline bool operator==(const CefPoint& a, const CefPoint& b) {
+  return a.x == b.x && a.y == b.y;
+}
+
+inline bool operator!=(const CefPoint& a, const CefPoint& b) {
+  return !(a == b);
+}
+
+struct CefRectTraits {
+  typedef cef_rect_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing a rectangle.
+///
+class CefRect : public CefStructBase<CefRectTraits> {
+ public:
+  typedef CefStructBase<CefRectTraits> parent;
+
+  CefRect() : parent() {}
+  CefRect(const cef_rect_t& r) : parent(r) {}
+  CefRect(const CefRect& r) : parent(r) {}
+  CefRect(int x, int y, int width, int height) : parent() {
+    Set(x, y, width, height);
+  }
+
+  bool IsEmpty() const { return width <= 0 || height <= 0; }
+  void Set(int x_val, int y_val, int width_val, int height_val) {
+    x = x_val, y = y_val, width = width_val, height = height_val;
+  }
+
+  // Returns true if the point identified by point_x and point_y falls inside
+  // this rectangle.  The point (x, y) is inside the rectangle, but the
+  // point (x + width, y + height) is not.
+  bool Contains(int point_x, int point_y) const {
+    return (point_x >= x) && (point_x < x + width) && (point_y >= y) &&
+           (point_y < y + height);
+  }
+  bool Contains(const CefPoint& point) const {
+    return Contains(point.x, point.y);
+  }
+};
+
+inline bool operator==(const CefRect& a, const CefRect& b) {
+  return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height;
+}
+
+inline bool operator!=(const CefRect& a, const CefRect& b) {
+  return !(a == b);
+}
+
+struct CefSizeTraits {
+  typedef cef_size_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing a size.
+///
+class CefSize : public CefStructBase<CefSizeTraits> {
+ public:
+  typedef CefStructBase<CefSizeTraits> parent;
+
+  CefSize() : parent() {}
+  CefSize(const cef_size_t& r) : parent(r) {}
+  CefSize(const CefSize& r) : parent(r) {}
+  CefSize(int width, int height) : parent() { Set(width, height); }
+
+  bool IsEmpty() const { return width <= 0 || height <= 0; }
+  void Set(int width_val, int height_val) {
+    width = width_val, height = height_val;
+  }
+};
+
+inline bool operator==(const CefSize& a, const CefSize& b) {
+  return a.width == b.width && a.height == b.height;
+}
+
+inline bool operator!=(const CefSize& a, const CefSize& b) {
+  return !(a == b);
+}
+
+struct CefRangeTraits {
+  typedef cef_range_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing a range.
+///
+class CefRange : public CefStructBase<CefRangeTraits> {
+ public:
+  typedef CefStructBase<CefRangeTraits> parent;
+
+  CefRange() : parent() {}
+  CefRange(const cef_range_t& r) : parent(r) {}
+  CefRange(const CefRange& r) : parent(r) {}
+  CefRange(int from, int to) : parent() { Set(from, to); }
+
+  void Set(int from_val, int to_val) { from = from_val, to = to_val; }
+};
+
+inline bool operator==(const CefRange& a, const CefRange& b) {
+  return a.from == b.from && a.to == b.to;
+}
+
+inline bool operator!=(const CefRange& a, const CefRange& b) {
+  return !(a == b);
+}
+
+struct CefInsetsTraits {
+  typedef cef_insets_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing insets.
+///
+class CefInsets : public CefStructBase<CefInsetsTraits> {
+ public:
+  typedef CefStructBase<CefInsetsTraits> parent;
+
+  CefInsets() : parent() {}
+  CefInsets(const cef_insets_t& r) : parent(r) {}
+  CefInsets(const CefInsets& r) : parent(r) {}
+  CefInsets(int top, int left, int bottom, int right) : parent() {
+    Set(top, left, bottom, right);
+  }
+
+  void Set(int top_val, int left_val, int bottom_val, int right_val) {
+    top = top_val, left = left_val, bottom = bottom_val, right = right_val;
+  }
+};
+
+inline bool operator==(const CefInsets& a, const CefInsets& b) {
+  return a.top == b.top && a.left == b.left && a.bottom == b.bottom &&
+         a.right == b.right;
+}
+
+inline bool operator!=(const CefInsets& a, const CefInsets& b) {
+  return !(a == b);
+}
+
+struct CefDraggableRegionTraits {
+  typedef cef_draggable_region_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing a draggable region.
+///
+class CefDraggableRegion : public CefStructBase<CefDraggableRegionTraits> {
+ public:
+  typedef CefStructBase<CefDraggableRegionTraits> parent;
+
+  CefDraggableRegion() : parent() {}
+  CefDraggableRegion(const cef_draggable_region_t& r) : parent(r) {}
+  CefDraggableRegion(const CefDraggableRegion& r) : parent(r) {}
+  CefDraggableRegion(const CefRect& bounds, bool draggable) : parent() {
+    Set(bounds, draggable);
+  }
+
+  void Set(const CefRect& bounds_val, bool draggable_val) {
+    bounds = bounds_val, draggable = draggable_val;
+  }
+};
+
+inline bool operator==(const CefDraggableRegion& a,
+                       const CefDraggableRegion& b) {
+  return a.bounds == b.bounds && a.draggable == b.draggable;
+}
+
+inline bool operator!=(const CefDraggableRegion& a,
+                       const CefDraggableRegion& b) {
+  return !(a == b);
+}
+
+struct CefScreenInfoTraits {
+  typedef cef_screen_info_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->device_scale_factor = src->device_scale_factor;
+    target->depth = src->depth;
+    target->depth_per_component = src->depth_per_component;
+    target->is_monochrome = src->is_monochrome;
+    target->rect = src->rect;
+    target->available_rect = src->available_rect;
+  }
+};
+
+///
+// Class representing the virtual screen information for use when window
+// rendering is disabled.
+///
+class CefScreenInfo : public CefStructBase<CefScreenInfoTraits> {
+ public:
+  typedef CefStructBase<CefScreenInfoTraits> parent;
+
+  CefScreenInfo() : parent() {}
+  CefScreenInfo(const cef_screen_info_t& r) : parent(r) {}
+  CefScreenInfo(const CefScreenInfo& r) : parent(r) {}
+  CefScreenInfo(float device_scale_factor,
+                int depth,
+                int depth_per_component,
+                bool is_monochrome,
+                const CefRect& rect,
+                const CefRect& available_rect)
+      : parent() {
+    Set(device_scale_factor, depth, depth_per_component, is_monochrome, rect,
+        available_rect);
+  }
+
+  void Set(float device_scale_factor_val,
+           int depth_val,
+           int depth_per_component_val,
+           bool is_monochrome_val,
+           const CefRect& rect_val,
+           const CefRect& available_rect_val) {
+    device_scale_factor = device_scale_factor_val;
+    depth = depth_val;
+    depth_per_component = depth_per_component_val;
+    is_monochrome = is_monochrome_val;
+    rect = rect_val;
+    available_rect = available_rect_val;
+  }
+};
+
+struct CefKeyEventTraits {
+  typedef cef_key_event_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->type = src->type;
+    target->modifiers = src->modifiers;
+    target->windows_key_code = src->windows_key_code;
+    target->native_key_code = src->native_key_code;
+    target->is_system_key = src->is_system_key;
+    target->character = src->character;
+    target->unmodified_character = src->unmodified_character;
+    target->focus_on_editable_field = src->focus_on_editable_field;
+  }
+};
+
+///
+// Class representing a a keyboard event.
+///
+typedef CefStructBase<CefKeyEventTraits> CefKeyEvent;
+
+struct CefMouseEventTraits {
+  typedef cef_mouse_event_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->x = src->x;
+    target->y = src->y;
+    target->modifiers = src->modifiers;
+  }
+};
+
+///
+// Class representing a mouse event.
+///
+typedef CefStructBase<CefMouseEventTraits> CefMouseEvent;
+
+struct CefTouchEventTraits {
+  typedef cef_touch_event_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing a touch event.
+///
+typedef CefStructBase<CefTouchEventTraits> CefTouchEvent;
+
+struct CefPopupFeaturesTraits {
+  typedef cef_popup_features_t struct_type;
+
+  static inline void init(struct_type* s) {
+    s->menuBarVisible = true;
+    s->statusBarVisible = true;
+    s->toolBarVisible = true;
+    s->scrollbarsVisible = true;
+  }
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->x = src->x;
+    target->xSet = src->xSet;
+    target->y = src->y;
+    target->ySet = src->ySet;
+    target->width = src->width;
+    target->widthSet = src->widthSet;
+    target->height = src->height;
+    target->heightSet = src->heightSet;
+    target->menuBarVisible = src->menuBarVisible;
+    target->statusBarVisible = src->statusBarVisible;
+    target->toolBarVisible = src->toolBarVisible;
+    target->scrollbarsVisible = src->scrollbarsVisible;
+  }
+};
+
+///
+// Class representing popup window features.
+///
+typedef CefStructBase<CefPopupFeaturesTraits> CefPopupFeatures;
+
+struct CefSettingsTraits {
+  typedef cef_settings_t struct_type;
+
+  static inline void init(struct_type* s) { s->size = sizeof(struct_type); }
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->browser_subprocess_path);
+    cef_string_clear(&s->framework_dir_path);
+    cef_string_clear(&s->main_bundle_path);
+    cef_string_clear(&s->cache_path);
+    cef_string_clear(&s->root_cache_path);
+    cef_string_clear(&s->user_data_path);
+    cef_string_clear(&s->user_agent);
+    cef_string_clear(&s->product_version);
+    cef_string_clear(&s->locale);
+    cef_string_clear(&s->log_file);
+    cef_string_clear(&s->javascript_flags);
+    cef_string_clear(&s->resources_dir_path);
+    cef_string_clear(&s->locales_dir_path);
+    cef_string_clear(&s->accept_language_list);
+    cef_string_clear(&s->application_client_id_for_file_scanning);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->no_sandbox = src->no_sandbox;
+    cef_string_set(src->browser_subprocess_path.str,
+                   src->browser_subprocess_path.length,
+                   &target->browser_subprocess_path, copy);
+    cef_string_set(src->framework_dir_path.str, src->framework_dir_path.length,
+                   &target->framework_dir_path, copy);
+    cef_string_set(src->main_bundle_path.str, src->main_bundle_path.length,
+                   &target->main_bundle_path, copy);
+    target->multi_threaded_message_loop = src->multi_threaded_message_loop;
+    target->external_message_pump = src->external_message_pump;
+    target->windowless_rendering_enabled = src->windowless_rendering_enabled;
+    target->command_line_args_disabled = src->command_line_args_disabled;
+
+    cef_string_set(src->cache_path.str, src->cache_path.length,
+                   &target->cache_path, copy);
+    cef_string_set(src->root_cache_path.str, src->root_cache_path.length,
+                   &target->root_cache_path, copy);
+    cef_string_set(src->user_data_path.str, src->user_data_path.length,
+                   &target->user_data_path, copy);
+    target->persist_session_cookies = src->persist_session_cookies;
+    target->persist_user_preferences = src->persist_user_preferences;
+
+    cef_string_set(src->user_agent.str, src->user_agent.length,
+                   &target->user_agent, copy);
+    cef_string_set(src->product_version.str, src->product_version.length,
+                   &target->product_version, copy);
+    cef_string_set(src->locale.str, src->locale.length, &target->locale, copy);
+
+    cef_string_set(src->log_file.str, src->log_file.length, &target->log_file,
+                   copy);
+    target->log_severity = src->log_severity;
+    cef_string_set(src->javascript_flags.str, src->javascript_flags.length,
+                   &target->javascript_flags, copy);
+
+    cef_string_set(src->resources_dir_path.str, src->resources_dir_path.length,
+                   &target->resources_dir_path, copy);
+    cef_string_set(src->locales_dir_path.str, src->locales_dir_path.length,
+                   &target->locales_dir_path, copy);
+    target->pack_loading_disabled = src->pack_loading_disabled;
+    target->remote_debugging_port = src->remote_debugging_port;
+    target->uncaught_exception_stack_size = src->uncaught_exception_stack_size;
+    target->ignore_certificate_errors = src->ignore_certificate_errors;
+    target->background_color = src->background_color;
+
+    cef_string_set(src->accept_language_list.str,
+                   src->accept_language_list.length,
+                   &target->accept_language_list, copy);
+    cef_string_set(src->application_client_id_for_file_scanning.str,
+                   src->application_client_id_for_file_scanning.length,
+                   &target->application_client_id_for_file_scanning, copy);
+  }
+};
+
+///
+// Class representing initialization settings.
+///
+typedef CefStructBase<CefSettingsTraits> CefSettings;
+
+struct CefRequestContextSettingsTraits {
+  typedef cef_request_context_settings_t struct_type;
+
+  static inline void init(struct_type* s) { s->size = sizeof(struct_type); }
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->cache_path);
+    cef_string_clear(&s->accept_language_list);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    cef_string_set(src->cache_path.str, src->cache_path.length,
+                   &target->cache_path, copy);
+    target->persist_session_cookies = src->persist_session_cookies;
+    target->persist_user_preferences = src->persist_user_preferences;
+    target->ignore_certificate_errors = src->ignore_certificate_errors;
+    cef_string_set(src->accept_language_list.str,
+                   src->accept_language_list.length,
+                   &target->accept_language_list, copy);
+  }
+};
+
+///
+// Class representing request context initialization settings.
+///
+typedef CefStructBase<CefRequestContextSettingsTraits>
+    CefRequestContextSettings;
+
+struct CefBrowserSettingsTraits {
+  typedef cef_browser_settings_t struct_type;
+
+  static inline void init(struct_type* s) { s->size = sizeof(struct_type); }
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->standard_font_family);
+    cef_string_clear(&s->fixed_font_family);
+    cef_string_clear(&s->serif_font_family);
+    cef_string_clear(&s->sans_serif_font_family);
+    cef_string_clear(&s->cursive_font_family);
+    cef_string_clear(&s->fantasy_font_family);
+    cef_string_clear(&s->default_encoding);
+    cef_string_clear(&s->accept_language_list);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->windowless_frame_rate = src->windowless_frame_rate;
+
+    cef_string_set(src->standard_font_family.str,
+                   src->standard_font_family.length,
+                   &target->standard_font_family, copy);
+    cef_string_set(src->fixed_font_family.str, src->fixed_font_family.length,
+                   &target->fixed_font_family, copy);
+    cef_string_set(src->serif_font_family.str, src->serif_font_family.length,
+                   &target->serif_font_family, copy);
+    cef_string_set(src->sans_serif_font_family.str,
+                   src->sans_serif_font_family.length,
+                   &target->sans_serif_font_family, copy);
+    cef_string_set(src->cursive_font_family.str,
+                   src->cursive_font_family.length,
+                   &target->cursive_font_family, copy);
+    cef_string_set(src->fantasy_font_family.str,
+                   src->fantasy_font_family.length,
+                   &target->fantasy_font_family, copy);
+
+    target->default_font_size = src->default_font_size;
+    target->default_fixed_font_size = src->default_fixed_font_size;
+    target->minimum_font_size = src->minimum_font_size;
+    target->minimum_logical_font_size = src->minimum_logical_font_size;
+
+    cef_string_set(src->default_encoding.str, src->default_encoding.length,
+                   &target->default_encoding, copy);
+
+    target->remote_fonts = src->remote_fonts;
+    target->javascript = src->javascript;
+    target->javascript_close_windows = src->javascript_close_windows;
+    target->javascript_access_clipboard = src->javascript_access_clipboard;
+    target->javascript_dom_paste = src->javascript_dom_paste;
+    target->plugins = src->plugins;
+    target->universal_access_from_file_urls =
+        src->universal_access_from_file_urls;
+    target->file_access_from_file_urls = src->file_access_from_file_urls;
+    target->web_security = src->web_security;
+    target->image_loading = src->image_loading;
+    target->image_shrink_standalone_to_fit =
+        src->image_shrink_standalone_to_fit;
+    target->text_area_resize = src->text_area_resize;
+    target->tab_to_links = src->tab_to_links;
+    target->local_storage = src->local_storage;
+    target->databases = src->databases;
+    target->application_cache = src->application_cache;
+    target->webgl = src->webgl;
+
+    target->background_color = src->background_color;
+
+    cef_string_set(src->accept_language_list.str,
+                   src->accept_language_list.length,
+                   &target->accept_language_list, copy);
+  }
+};
+
+///
+// Class representing browser initialization settings.
+///
+typedef CefStructBase<CefBrowserSettingsTraits> CefBrowserSettings;
+
+struct CefURLPartsTraits {
+  typedef cef_urlparts_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->spec);
+    cef_string_clear(&s->scheme);
+    cef_string_clear(&s->username);
+    cef_string_clear(&s->password);
+    cef_string_clear(&s->host);
+    cef_string_clear(&s->port);
+    cef_string_clear(&s->origin);
+    cef_string_clear(&s->path);
+    cef_string_clear(&s->query);
+    cef_string_clear(&s->fragment);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    cef_string_set(src->spec.str, src->spec.length, &target->spec, copy);
+    cef_string_set(src->scheme.str, src->scheme.length, &target->scheme, copy);
+    cef_string_set(src->username.str, src->username.length, &target->username,
+                   copy);
+    cef_string_set(src->password.str, src->password.length, &target->password,
+                   copy);
+    cef_string_set(src->host.str, src->host.length, &target->host, copy);
+    cef_string_set(src->port.str, src->port.length, &target->port, copy);
+    cef_string_set(src->origin.str, src->origin.length, &target->origin, copy);
+    cef_string_set(src->path.str, src->path.length, &target->path, copy);
+    cef_string_set(src->query.str, src->query.length, &target->query, copy);
+    cef_string_set(src->fragment.str, src->fragment.length, &target->fragment,
+                   copy);
+  }
+};
+
+///
+// Class representing a URL's component parts.
+///
+typedef CefStructBase<CefURLPartsTraits> CefURLParts;
+
+struct CefTimeTraits {
+  typedef cef_time_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing a time.
+///
+class CefTime : public CefStructBase<CefTimeTraits> {
+ public:
+  typedef CefStructBase<CefTimeTraits> parent;
+
+  CefTime() : parent() {}
+  CefTime(const cef_time_t& r) : parent(r) {}
+  CefTime(const CefTime& r) : parent(r) {}
+  explicit CefTime(time_t r) : parent() { SetTimeT(r); }
+  explicit CefTime(double r) : parent() { SetDoubleT(r); }
+
+  // Converts to/from time_t.
+  void SetTimeT(time_t r) { cef_time_from_timet(r, this); }
+  time_t GetTimeT() const {
+    time_t time = 0;
+    cef_time_to_timet(this, &time);
+    return time;
+  }
+
+  // Converts to/from a double which is the number of seconds since epoch
+  // (Jan 1, 1970). Webkit uses this format to represent time. A value of 0
+  // means "not initialized".
+  void SetDoubleT(double r) { cef_time_from_doublet(r, this); }
+  double GetDoubleT() const {
+    double time = 0;
+    cef_time_to_doublet(this, &time);
+    return time;
+  }
+
+  // Set this object to now.
+  void Now() { cef_time_now(this); }
+
+  // Return the delta between this object and |other| in milliseconds.
+  long long Delta(const CefTime& other) {
+    long long delta = 0;
+    cef_time_delta(this, &other, &delta);
+    return delta;
+  }
+};
+
+struct CefCookieTraits {
+  typedef cef_cookie_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->name);
+    cef_string_clear(&s->value);
+    cef_string_clear(&s->domain);
+    cef_string_clear(&s->path);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    cef_string_set(src->name.str, src->name.length, &target->name, copy);
+    cef_string_set(src->value.str, src->value.length, &target->value, copy);
+    cef_string_set(src->domain.str, src->domain.length, &target->domain, copy);
+    cef_string_set(src->path.str, src->path.length, &target->path, copy);
+    target->secure = src->secure;
+    target->httponly = src->httponly;
+    target->creation = src->creation;
+    target->last_access = src->last_access;
+    target->has_expires = src->has_expires;
+    target->expires = src->expires;
+    target->same_site = src->same_site;
+    target->priority = src->priority;
+  }
+};
+
+///
+// Class representing a cookie.
+///
+typedef CefStructBase<CefCookieTraits> CefCookie;
+
+struct CefCursorInfoTraits {
+  typedef cef_cursor_info_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing cursor information.
+///
+typedef CefStructBase<CefCursorInfoTraits> CefCursorInfo;
+
+struct CefPdfPrintSettingsTraits {
+  typedef cef_pdf_print_settings_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->header_footer_title);
+    cef_string_clear(&s->header_footer_url);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    cef_string_set(src->header_footer_title.str,
+                   src->header_footer_title.length,
+                   &target->header_footer_title, copy);
+    cef_string_set(src->header_footer_url.str, src->header_footer_url.length,
+                   &target->header_footer_url, copy);
+
+    target->page_width = src->page_width;
+    target->page_height = src->page_height;
+
+    target->scale_factor = src->scale_factor;
+
+    target->margin_top = src->margin_top;
+    target->margin_right = src->margin_right;
+    target->margin_bottom = src->margin_bottom;
+    target->margin_left = src->margin_left;
+    target->margin_type = src->margin_type;
+
+    target->header_footer_enabled = src->header_footer_enabled;
+    target->selection_only = src->selection_only;
+    target->landscape = src->landscape;
+    target->backgrounds_enabled = src->backgrounds_enabled;
+  }
+};
+
+///
+// Class representing PDF print settings
+///
+typedef CefStructBase<CefPdfPrintSettingsTraits> CefPdfPrintSettings;
+
+struct CefBoxLayoutSettingsTraits {
+  typedef cef_box_layout_settings_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing CefBoxLayout settings.
+///
+typedef CefStructBase<CefBoxLayoutSettingsTraits> CefBoxLayoutSettings;
+
+struct CefCompositionUnderlineTraits {
+  typedef cef_composition_underline_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing IME composition underline.
+///
+typedef CefStructBase<CefCompositionUnderlineTraits> CefCompositionUnderline;
+
+struct CefAudioParametersTraits {
+  typedef cef_audio_parameters_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    *target = *src;
+  }
+};
+
+///
+// Class representing CefAudioParameters settings
+///
+typedef CefStructBase<CefAudioParametersTraits> CefAudioParameters;
+
+struct CefMediaSinkDeviceInfoTraits {
+  typedef cef_media_sink_device_info_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->ip_address);
+    cef_string_clear(&s->model_name);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    cef_string_set(src->ip_address.str, src->ip_address.length,
+                   &target->ip_address, copy);
+    target->port = src->port;
+    cef_string_set(src->model_name.str, src->model_name.length,
+                   &target->model_name, copy);
+  }
+};
+
+///
+// Class representing MediaSink device info.
+///
+typedef CefStructBase<CefMediaSinkDeviceInfoTraits> CefMediaSinkDeviceInfo;
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_TYPES_WRAPPERS_H_
diff --git a/src/include/internal/cef_win.h b/src/include/internal/cef_win.h
new file mode 100644
index 0000000..292864e
--- /dev/null
+++ b/src/include/internal/cef_win.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2008 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_INTERNAL_CEF_WIN_H_
+#define CEF_INCLUDE_INTERNAL_CEF_WIN_H_
+#pragma once
+
+#include "include/internal/cef_types_win.h"
+#include "include/internal/cef_types_wrappers.h"
+
+///
+// Handle types.
+///
+#define CefCursorHandle cef_cursor_handle_t
+#define CefEventHandle cef_event_handle_t
+#define CefWindowHandle cef_window_handle_t
+
+struct CefMainArgsTraits {
+  typedef cef_main_args_t struct_type;
+
+  static inline void init(struct_type* s) {}
+  static inline void clear(struct_type* s) {}
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->instance = src->instance;
+  }
+};
+
+// Class representing CefExecuteProcess arguments.
+class CefMainArgs : public CefStructBase<CefMainArgsTraits> {
+ public:
+  typedef CefStructBase<CefMainArgsTraits> parent;
+
+  CefMainArgs() : parent() {}
+  explicit CefMainArgs(const cef_main_args_t& r) : parent(r) {}
+  explicit CefMainArgs(const CefMainArgs& r) : parent(r) {}
+  explicit CefMainArgs(HINSTANCE hInstance) : parent() { instance = hInstance; }
+};
+
+struct CefWindowInfoTraits {
+  typedef cef_window_info_t struct_type;
+
+  static inline void init(struct_type* s) {}
+
+  static inline void clear(struct_type* s) {
+    cef_string_clear(&s->window_name);
+  }
+
+  static inline void set(const struct_type* src,
+                         struct_type* target,
+                         bool copy) {
+    target->ex_style = src->ex_style;
+    cef_string_set(src->window_name.str, src->window_name.length,
+                   &target->window_name, copy);
+    target->style = src->style;
+    target->x = src->x;
+    target->y = src->y;
+    target->width = src->width;
+    target->height = src->height;
+    target->parent_window = src->parent_window;
+    target->menu = src->menu;
+    target->windowless_rendering_enabled = src->windowless_rendering_enabled;
+    target->shared_texture_enabled = src->shared_texture_enabled;
+    target->external_begin_frame_enabled = src->external_begin_frame_enabled;
+    target->window = src->window;
+  }
+};
+
+///
+// Class representing window information.
+///
+class CefWindowInfo : public CefStructBase<CefWindowInfoTraits> {
+ public:
+  typedef CefStructBase<CefWindowInfoTraits> parent;
+
+  CefWindowInfo() : parent() {}
+  explicit CefWindowInfo(const cef_window_info_t& r) : parent(r) {}
+  explicit CefWindowInfo(const CefWindowInfo& r) : parent(r) {}
+
+  ///
+  // Create the browser as a child window.
+  ///
+  void SetAsChild(CefWindowHandle parent, RECT windowRect) {
+    style =
+        WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE;
+    parent_window = parent;
+    x = windowRect.left;
+    y = windowRect.top;
+    width = windowRect.right - windowRect.left;
+    height = windowRect.bottom - windowRect.top;
+  }
+
+  ///
+  // Create the browser as a popup window.
+  ///
+  void SetAsPopup(CefWindowHandle parent, const CefString& windowName) {
+    style =
+        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE;
+    parent_window = parent;
+    x = CW_USEDEFAULT;
+    y = CW_USEDEFAULT;
+    width = CW_USEDEFAULT;
+    height = CW_USEDEFAULT;
+
+    cef_string_copy(windowName.c_str(), windowName.length(), &window_name);
+  }
+
+  ///
+  // Create the browser using windowless (off-screen) rendering. No window
+  // will be created for the browser and all rendering will occur via the
+  // CefRenderHandler interface. The |parent| value will be used to identify
+  // monitor info and to act as the parent window for dialogs, context menus,
+  // etc. If |parent| is not provided then the main screen monitor will be used
+  // and some functionality that requires a parent window may not function
+  // correctly. In order to create windowless browsers the
+  // CefSettings.windowless_rendering_enabled value must be set to true.
+  // Transparent painting is enabled by default but can be disabled by setting
+  // CefBrowserSettings.background_color to an opaque value.
+  ///
+  void SetAsWindowless(CefWindowHandle parent) {
+    windowless_rendering_enabled = TRUE;
+    parent_window = parent;
+  }
+};
+
+#endif  // CEF_INCLUDE_INTERNAL_CEF_WIN_H_
diff --git a/src/include/test/cef_test_helpers.h b/src/include/test/cef_test_helpers.h
new file mode 100644
index 0000000..a4e45b8
--- /dev/null
+++ b/src/include/test/cef_test_helpers.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+// THIS FILE IS FOR TESTING PURPOSES ONLY.
+//
+// The APIs defined in this file are for testing purposes only. They should only
+// be included from unit test targets.
+//
+
+#ifndef CEF_INCLUDE_TEST_CEF_TEST_HELPERS_H_
+#define CEF_INCLUDE_TEST_CEF_TEST_HELPERS_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \
+    !defined(UNIT_TEST)
+#error This file can be included for unit tests only
+#endif
+
+#include "include/cef_frame.h"
+
+///
+// Execute JavaScript with a user gesture to trigger functionality like
+// onbeforeunload handlers that will otherwise be blocked.
+///
+/*--cef(optional_param=javascript)--*/
+void CefExecuteJavaScriptWithUserGestureForTests(CefRefPtr<CefFrame> frame,
+                                                 const CefString& javascript);
+
+#endif  // CEF_INCLUDE_TEST_CEF_TEST_HELPERS_H_
diff --git a/src/include/test/cef_translator_test.h b/src/include/test/cef_translator_test.h
new file mode 100644
index 0000000..590b5a2
--- /dev/null
+++ b/src/include/test/cef_translator_test.h
@@ -0,0 +1,808 @@
+// Copyright (c) 2015 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+// THIS FILE IS FOR TESTING PURPOSES ONLY.
+//
+// The APIs defined in this file are for testing purposes only. They should only
+// be included from unit test targets.
+//
+
+#ifndef CEF_INCLUDE_TEST_CEF_TEST_H_
+#define CEF_INCLUDE_TEST_CEF_TEST_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \
+    !defined(UNIT_TEST)
+#error This file can be included for unit tests only
+#endif
+
+#include <map>
+#include <vector>
+
+#include "include/cef_base.h"
+
+class CefTranslatorTestRefPtrClient;
+class CefTranslatorTestRefPtrClientChild;
+class CefTranslatorTestRefPtrLibrary;
+class CefTranslatorTestRefPtrLibraryChild;
+class CefTranslatorTestScopedClient;
+class CefTranslatorTestScopedClientChild;
+class CefTranslatorTestScopedLibrary;
+class CefTranslatorTestScopedLibraryChild;
+
+// Test values.
+#define TEST_INT_VAL 5
+#define TEST_INT_VAL2 60
+#define TEST_BOOL_VAL true
+#define TEST_DOUBLE_VAL 4.543
+#define TEST_LONG_VAL -65
+#define TEST_SIZET_VAL 3U
+#define TEST_STRING_VAL "My test string"
+#define TEST_STRING_VAL2 "My 2nd test string"
+#define TEST_STRING_VAL3 "My 3rd test string"
+#define TEST_STRING_KEY "key0"
+#define TEST_STRING_KEY2 "key1"
+#define TEST_STRING_KEY3 "key2"
+#define TEST_X_VAL 44
+#define TEST_Y_VAL 754
+#define TEST_X_VAL2 900
+#define TEST_Y_VAL2 300
+
+///
+// Class for testing all of the possible data transfer types.
+///
+/*--cef(source=library)--*/
+class CefTranslatorTest : public CefBaseRefCounted {
+ public:
+  ///
+  // Create the test object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefTranslatorTest> Create();
+
+  // PRIMITIVE VALUES
+
+  ///
+  // Return a void value.
+  ///
+  /*--cef()--*/
+  virtual void GetVoid() = 0;
+
+  ///
+  // Return a bool value.
+  ///
+  /*--cef()--*/
+  virtual bool GetBool() = 0;
+
+  ///
+  // Return an int value.
+  ///
+  /*--cef()--*/
+  virtual int GetInt() = 0;
+
+  ///
+  // Return a double value.
+  ///
+  /*--cef()--*/
+  virtual double GetDouble() = 0;
+
+  ///
+  // Return a long value.
+  ///
+  /*--cef()--*/
+  virtual long GetLong() = 0;
+
+  ///
+  // Return a size_t value.
+  ///
+  /*--cef()--*/
+  virtual size_t GetSizet() = 0;
+
+  ///
+  // Set a void value.
+  ///
+  /*--cef()--*/
+  virtual bool SetVoid() = 0;
+
+  ///
+  // Set a bool value.
+  ///
+  /*--cef()--*/
+  virtual bool SetBool(bool val) = 0;
+
+  ///
+  // Set an int value.
+  ///
+  /*--cef()--*/
+  virtual bool SetInt(int val) = 0;
+
+  ///
+  // Set a double value.
+  ///
+  /*--cef()--*/
+  virtual bool SetDouble(double val) = 0;
+
+  ///
+  // Set a long value.
+  ///
+  /*--cef()--*/
+  virtual bool SetLong(long val) = 0;
+
+  ///
+  // Set a size_t value.
+  ///
+  /*--cef()--*/
+  virtual bool SetSizet(size_t val) = 0;
+
+  // PRIMITIVE LIST VALUES
+
+  // Test both with and without a typedef.
+  typedef std::vector<int> IntList;
+
+  ///
+  // Set a int list value.
+  ///
+  /*--cef()--*/
+  virtual bool SetIntList(const std::vector<int>& val) = 0;
+
+  ///
+  // Return an int list value by out-param.
+  ///
+  /*--cef(count_func=val:GetIntListSize)--*/
+  virtual bool GetIntListByRef(IntList& val) = 0;
+
+  ///
+  // Return the number of points that will be output above.
+  ///
+  /*--cef()--*/
+  virtual size_t GetIntListSize() = 0;
+
+  // STRING VALUES
+
+  ///
+  // Return a string value.
+  ///
+  /*--cef()--*/
+  virtual CefString GetString() = 0;
+
+  ///
+  // Set a string value.
+  ///
+  /*--cef()--*/
+  virtual bool SetString(const CefString& val) = 0;
+
+  ///
+  // Return a string value by out-param.
+  ///
+  /*--cef()--*/
+  virtual void GetStringByRef(CefString& val) = 0;
+
+  // STRING LIST VALUES
+
+  // Test both with and without a typedef.
+  typedef std::vector<CefString> StringList;
+
+  ///
+  // Set a string list value.
+  ///
+  /*--cef()--*/
+  virtual bool SetStringList(const std::vector<CefString>& val) = 0;
+
+  ///
+  // Return a string list value by out-param.
+  ///
+  /*--cef()--*/
+  virtual bool GetStringListByRef(StringList& val) = 0;
+
+  // STRING MAP VALUES
+
+  // Test both with and without a typedef.
+  typedef std::map<CefString, CefString> StringMap;
+
+  ///
+  // Set a string map value.
+  ///
+  /*--cef()--*/
+  virtual bool SetStringMap(const StringMap& val) = 0;
+
+  ///
+  // Return a string map value by out-param.
+  ///
+  /*--cef()--*/
+  virtual bool GetStringMapByRef(std::map<CefString, CefString>& val) = 0;
+
+  // STRING MULTIMAP VALUES
+
+  // Test both with and without a typedef.
+  typedef std::multimap<CefString, CefString> StringMultimap;
+
+  ///
+  // Set a string multimap value.
+  ///
+  /*--cef()--*/
+  virtual bool SetStringMultimap(
+      const std::multimap<CefString, CefString>& val) = 0;
+
+  ///
+  // Return a string multimap value by out-param.
+  ///
+  /*--cef()--*/
+  virtual bool GetStringMultimapByRef(StringMultimap& val) = 0;
+
+  // STRUCT VALUES
+
+  ///
+  // Return a point value.
+  ///
+  /*--cef()--*/
+  virtual CefPoint GetPoint() = 0;
+
+  ///
+  // Set a point value.
+  ///
+  /*--cef()--*/
+  virtual bool SetPoint(const CefPoint& val) = 0;
+
+  ///
+  // Return a point value by out-param.
+  ///
+  /*--cef()--*/
+  virtual void GetPointByRef(CefPoint& val) = 0;
+
+  // STRUCT LIST VALUES
+
+  // Test both with and without a typedef.
+  typedef std::vector<CefPoint> PointList;
+
+  ///
+  // Set a point list vlaue.
+  ///
+  /*--cef()--*/
+  virtual bool SetPointList(const std::vector<CefPoint>& val) = 0;
+
+  ///
+  // Return a point list value by out-param.
+  ///
+  /*--cef(count_func=val:GetPointListSize)--*/
+  virtual bool GetPointListByRef(PointList& val) = 0;
+
+  ///
+  // Return the number of points that will be output above.
+  ///
+  /*--cef()--*/
+  virtual size_t GetPointListSize() = 0;
+
+  // LIBRARY-SIDE REFPTR VALUES
+
+  ///
+  // Return an new library-side object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTranslatorTestRefPtrLibrary> GetRefPtrLibrary(
+      int val) = 0;
+
+  ///
+  // Set an object. Returns the value from
+  // CefTranslatorTestRefPtrLibrary::GetValue().
+  // This tests input and execution of a library-side object type.
+  ///
+  /*--cef()--*/
+  virtual int SetRefPtrLibrary(
+      CefRefPtr<CefTranslatorTestRefPtrLibrary> val) = 0;
+
+  ///
+  // Set an object. Returns the object passed in. This tests input and output
+  // of a library-side object type.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTranslatorTestRefPtrLibrary> SetRefPtrLibraryAndReturn(
+      CefRefPtr<CefTranslatorTestRefPtrLibrary> val) = 0;
+
+  ///
+  // Set a child object. Returns the value from
+  // CefTranslatorTestRefPtrLibrary::GetValue(). This tests input of a library-
+  // side child object type and execution as the parent type.
+  ///
+  /*--cef()--*/
+  virtual int SetChildRefPtrLibrary(
+      CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) = 0;
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests input
+  // of a library-side child object type and return as the parent type.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTranslatorTestRefPtrLibrary>
+  SetChildRefPtrLibraryAndReturnParent(
+      CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) = 0;
+
+  // LIBRARY-SIDE REFPTR LIST VALUES
+
+  // Test both with and without a typedef.
+  typedef std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>>
+      RefPtrLibraryList;
+
+  ///
+  // Set an object list vlaue.
+  ///
+  /*--cef()--*/
+  virtual bool SetRefPtrLibraryList(
+      const std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>>& val,
+      int val1,
+      int val2) = 0;
+
+  ///
+  // Return an object list value by out-param.
+  ///
+  /*--cef(count_func=val:GetRefPtrLibraryListSize)--*/
+  virtual bool GetRefPtrLibraryListByRef(RefPtrLibraryList& val,
+                                         int val1,
+                                         int val2) = 0;
+
+  ///
+  // Return the number of object that will be output above.
+  ///
+  /*--cef()--*/
+  virtual size_t GetRefPtrLibraryListSize() = 0;
+
+  // CLIENT-SIDE REFPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // CefTranslatorTestRefPtrClient::GetValue().
+  // This tests input and execution of a client-side object type.
+  ///
+  /*--cef()--*/
+  virtual int SetRefPtrClient(CefRefPtr<CefTranslatorTestRefPtrClient> val) = 0;
+
+  ///
+  // Set an object. Returns the handler passed in. This tests input and output
+  // of a client-side object type.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTranslatorTestRefPtrClient> SetRefPtrClientAndReturn(
+      CefRefPtr<CefTranslatorTestRefPtrClient> val) = 0;
+
+  ///
+  // Set a child object. Returns the value from
+  // CefTranslatorTestRefPtrClient::GetValue(). This tests input of a client-
+  // side child object type and execution as the parent type.
+  ///
+  /*--cef()--*/
+  virtual int SetChildRefPtrClient(
+      CefRefPtr<CefTranslatorTestRefPtrClientChild> val) = 0;
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests
+  // input of a client-side child object type and return as the parent type.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTranslatorTestRefPtrClient>
+  SetChildRefPtrClientAndReturnParent(
+      CefRefPtr<CefTranslatorTestRefPtrClientChild> val) = 0;
+
+  // CLIENT-SIDE REFPTR LIST VALUES
+
+  // Test both with and without a typedef.
+  typedef std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>>
+      RefPtrClientList;
+
+  ///
+  // Set an object list vlaue.
+  ///
+  /*--cef()--*/
+  virtual bool SetRefPtrClientList(
+      const std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>>& val,
+      int val1,
+      int val2) = 0;
+
+  ///
+  // Return an object list value by out-param.
+  ///
+  /*--cef(count_func=val:GetRefPtrLibraryListSize)--*/
+  virtual bool GetRefPtrClientListByRef(
+      RefPtrClientList& val,
+      CefRefPtr<CefTranslatorTestRefPtrClient> val1,
+      CefRefPtr<CefTranslatorTestRefPtrClient> val2) = 0;
+
+  ///
+  // Return the number of object that will be output above.
+  ///
+  /*--cef()--*/
+  virtual size_t GetRefPtrClientListSize() = 0;
+
+  // LIBRARY-SIDE OWNPTR VALUES
+
+  ///
+  // Return an new library-side object.
+  ///
+  /*--cef()--*/
+  virtual CefOwnPtr<CefTranslatorTestScopedLibrary> GetOwnPtrLibrary(
+      int val) = 0;
+
+  ///
+  // Set an object. Returns the value from
+  // CefTranslatorTestScopedLibrary::GetValue().
+  // This tests input and execution of a library-side object type.
+  ///
+  /*--cef()--*/
+  virtual int SetOwnPtrLibrary(
+      CefOwnPtr<CefTranslatorTestScopedLibrary> val) = 0;
+
+  ///
+  // Set an object. Returns the object passed in. This tests input and output
+  // of a library-side object type.
+  ///
+  /*--cef()--*/
+  virtual CefOwnPtr<CefTranslatorTestScopedLibrary> SetOwnPtrLibraryAndReturn(
+      CefOwnPtr<CefTranslatorTestScopedLibrary> val) = 0;
+
+  ///
+  // Set a child object. Returns the value from
+  // CefTranslatorTestScopedLibrary::GetValue(). This tests input of a library-
+  // side child object type and execution as the parent type.
+  ///
+  /*--cef()--*/
+  virtual int SetChildOwnPtrLibrary(
+      CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) = 0;
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests input
+  // of a library-side child object type and return as the parent type.
+  ///
+  /*--cef()--*/
+  virtual CefOwnPtr<CefTranslatorTestScopedLibrary>
+  SetChildOwnPtrLibraryAndReturnParent(
+      CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) = 0;
+
+  // CLIENT-SIDE OWNPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // CefTranslatorTestScopedClient::GetValue().
+  // This tests input and execution of a client-side object type.
+  ///
+  /*--cef()--*/
+  virtual int SetOwnPtrClient(CefOwnPtr<CefTranslatorTestScopedClient> val) = 0;
+
+  ///
+  // Set an object. Returns the handler passed in. This tests input and output
+  // of a client-side object type.
+  ///
+  /*--cef()--*/
+  virtual CefOwnPtr<CefTranslatorTestScopedClient> SetOwnPtrClientAndReturn(
+      CefOwnPtr<CefTranslatorTestScopedClient> val) = 0;
+
+  ///
+  // Set a child object. Returns the value from
+  // CefTranslatorTestScopedClient::GetValue(). This tests input of a client-
+  // side child object type and execution as the parent type.
+  ///
+  /*--cef()--*/
+  virtual int SetChildOwnPtrClient(
+      CefOwnPtr<CefTranslatorTestScopedClientChild> val) = 0;
+
+  ///
+  // Set a child object. Returns the object as the parent type. This tests
+  // input of a client-side child object type and return as the parent type.
+  ///
+  /*--cef()--*/
+  virtual CefOwnPtr<CefTranslatorTestScopedClient>
+  SetChildOwnPtrClientAndReturnParent(
+      CefOwnPtr<CefTranslatorTestScopedClientChild> val) = 0;
+
+  // LIBRARY-SIDE RAWPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // CefTranslatorTestScopedLibrary::GetValue().
+  // This tests input and execution of a library-side object type.
+  ///
+  /*--cef()--*/
+  virtual int SetRawPtrLibrary(
+      CefRawPtr<CefTranslatorTestScopedLibrary> val) = 0;
+
+  ///
+  // Set a child object. Returns the value from
+  // CefTranslatorTestScopedLibrary::GetValue(). This tests input of a library-
+  // side child object type and execution as the parent type.
+  ///
+  /*--cef()--*/
+  virtual int SetChildRawPtrLibrary(
+      CefRawPtr<CefTranslatorTestScopedLibraryChild> val) = 0;
+
+  // LIBRARY-SIDE RAWPTR LIST VALUES
+
+  // Test both with and without a typedef.
+  typedef std::vector<CefRawPtr<CefTranslatorTestScopedLibrary>>
+      RawPtrLibraryList;
+
+  ///
+  // Set an object list vlaue.
+  ///
+  /*--cef()--*/
+  virtual bool SetRawPtrLibraryList(
+      const std::vector<CefRawPtr<CefTranslatorTestScopedLibrary>>& val,
+      int val1,
+      int val2) = 0;
+
+  // CLIENT-SIDE RAWPTR VALUES
+
+  ///
+  // Set an object. Returns the value from
+  // CefTranslatorTestScopedClient::GetValue().
+  // This tests input and execution of a client-side object type.
+  ///
+  /*--cef()--*/
+  virtual int SetRawPtrClient(CefRawPtr<CefTranslatorTestScopedClient> val) = 0;
+
+  ///
+  // Set a child object. Returns the value from
+  // CefTranslatorTestScopedClient::GetValue(). This tests input of a client-
+  // side child object type and execution as the parent type.
+  ///
+  /*--cef()--*/
+  virtual int SetChildRawPtrClient(
+      CefRawPtr<CefTranslatorTestScopedClientChild> val) = 0;
+
+  // CLIENT-SIDE RAWPTR LIST VALUES
+
+  // Test both with and without a typedef.
+  typedef std::vector<CefRawPtr<CefTranslatorTestScopedClient>>
+      RawPtrClientList;
+
+  ///
+  // Set an object list vlaue.
+  ///
+  /*--cef()--*/
+  virtual bool SetRawPtrClientList(
+      const std::vector<CefRawPtr<CefTranslatorTestScopedClient>>& val,
+      int val1,
+      int val2) = 0;
+};
+
+///
+// Library-side test object for RefPtr.
+///
+/*--cef(source=library)--*/
+class CefTranslatorTestRefPtrLibrary : public CefBaseRefCounted {
+ public:
+  ///
+  // Create the test object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefTranslatorTestRefPtrLibrary> Create(int value);
+
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetValue() = 0;
+
+  ///
+  // Set a value.
+  ///
+  /*--cef()--*/
+  virtual void SetValue(int value) = 0;
+};
+
+///
+// Library-side child test object for RefPtr.
+///
+/*--cef(source=library)--*/
+class CefTranslatorTestRefPtrLibraryChild
+    : public CefTranslatorTestRefPtrLibrary {
+ public:
+  ///
+  // Create the test object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefTranslatorTestRefPtrLibraryChild> Create(int value,
+                                                               int other_value);
+
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetOtherValue() = 0;
+
+  ///
+  // Set a value.
+  ///
+  /*--cef()--*/
+  virtual void SetOtherValue(int value) = 0;
+};
+
+///
+// Another library-side child test object for RefPtr.
+///
+/*--cef(source=library)--*/
+class CefTranslatorTestRefPtrLibraryChildChild
+    : public CefTranslatorTestRefPtrLibraryChild {
+ public:
+  ///
+  // Create the test object.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefTranslatorTestRefPtrLibraryChildChild>
+  Create(int value, int other_value, int other_other_value);
+
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetOtherOtherValue() = 0;
+
+  ///
+  // Set a value.
+  ///
+  /*--cef()--*/
+  virtual void SetOtherOtherValue(int value) = 0;
+};
+
+///
+// Client-side test object for RefPtr.
+///
+/*--cef(source=client)--*/
+class CefTranslatorTestRefPtrClient : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetValue() = 0;
+};
+
+///
+// Client-side child test object for RefPtr.
+///
+/*--cef(source=client)--*/
+class CefTranslatorTestRefPtrClientChild
+    : public CefTranslatorTestRefPtrClient {
+ public:
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetOtherValue() = 0;
+};
+
+///
+// Library-side test object for OwnPtr/RawPtr.
+///
+/*--cef(source=library)--*/
+class CefTranslatorTestScopedLibrary : public CefBaseScoped {
+ public:
+  ///
+  // Create the test object.
+  ///
+  /*--cef()--*/
+  static CefOwnPtr<CefTranslatorTestScopedLibrary> Create(int value);
+
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetValue() = 0;
+
+  ///
+  // Set a value.
+  ///
+  /*--cef()--*/
+  virtual void SetValue(int value) = 0;
+};
+
+///
+// Library-side child test object for OwnPtr/RawPtr.
+///
+/*--cef(source=library)--*/
+class CefTranslatorTestScopedLibraryChild
+    : public CefTranslatorTestScopedLibrary {
+ public:
+  ///
+  // Create the test object.
+  ///
+  /*--cef()--*/
+  static CefOwnPtr<CefTranslatorTestScopedLibraryChild> Create(int value,
+                                                               int other_value);
+
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetOtherValue() = 0;
+
+  ///
+  // Set a value.
+  ///
+  /*--cef()--*/
+  virtual void SetOtherValue(int value) = 0;
+};
+
+///
+// Another library-side child test object for OwnPtr/RawPtr.
+///
+/*--cef(source=library)--*/
+class CefTranslatorTestScopedLibraryChildChild
+    : public CefTranslatorTestScopedLibraryChild {
+ public:
+  ///
+  // Create the test object.
+  ///
+  /*--cef()--*/
+  static CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>
+  Create(int value, int other_value, int other_other_value);
+
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetOtherOtherValue() = 0;
+
+  ///
+  // Set a value.
+  ///
+  /*--cef()--*/
+  virtual void SetOtherOtherValue(int value) = 0;
+};
+
+///
+// Client-side test object for OwnPtr/RawPtr.
+///
+/*--cef(source=client)--*/
+class CefTranslatorTestScopedClient : public virtual CefBaseScoped {
+ public:
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetValue() = 0;
+};
+
+///
+// Client-side child test object for OwnPtr/RawPtr.
+///
+/*--cef(source=client)--*/
+class CefTranslatorTestScopedClientChild
+    : public CefTranslatorTestScopedClient {
+ public:
+  ///
+  // Return a value.
+  ///
+  /*--cef()--*/
+  virtual int GetOtherValue() = 0;
+};
+
+#endif  // CEF_INCLUDE_TEST_CEF_TEST_H_
diff --git a/src/include/views/cef_box_layout.h b/src/include/views/cef_box_layout.h
new file mode 100644
index 0000000..7498919
--- /dev/null
+++ b/src/include/views/cef_box_layout.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_BOX_LAYOUT_H_
+#define CEF_INCLUDE_VIEWS_CEF_BOX_LAYOUT_H_
+#pragma once
+
+#include "include/views/cef_layout.h"
+
+class CefView;
+
+///
+// A Layout manager that arranges child views vertically or horizontally in a
+// side-by-side fashion with spacing around and between the child views. The
+// child views are always sized according to their preferred size. If the
+// host's bounds provide insufficient space, child views will be clamped.
+// Excess space will not be distributed. Methods must be called on the browser
+// process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefBoxLayout : public CefLayout {
+ public:
+  ///
+  // Set the flex weight for the given |view|. Using the preferred size as
+  // the basis, free space along the main axis is distributed to views in the
+  // ratio of their flex weights. Similarly, if the views will overflow the
+  // parent, space is subtracted in these ratios. A flex of 0 means this view is
+  // not resized. Flex values must not be negative.
+  ///
+  /*--cef()--*/
+  virtual void SetFlexForView(CefRefPtr<CefView> view, int flex) = 0;
+
+  ///
+  // Clears the flex for the given |view|, causing it to use the default flex
+  // specified via CefBoxLayoutSettings.default_flex.
+  ///
+  /*--cef()--*/
+  virtual void ClearFlexForView(CefRefPtr<CefView> view) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_BOX_LAYOUT_H_
diff --git a/src/include/views/cef_browser_view.h b/src/include/views/cef_browser_view.h
new file mode 100644
index 0000000..a310eb0
--- /dev/null
+++ b/src/include/views/cef_browser_view.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_BROWSER_VIEW_H_
+#define CEF_INCLUDE_VIEWS_CEF_BROWSER_VIEW_H_
+#pragma once
+
+#include "include/cef_browser.h"
+#include "include/views/cef_browser_view_delegate.h"
+#include "include/views/cef_view.h"
+
+///
+// A View hosting a CefBrowser instance. Methods must be called on the browser
+// process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefBrowserView : public CefView {
+ public:
+  ///
+  // Create a new BrowserView. The underlying CefBrowser will not be created
+  // until this view is added to the views hierarchy. The optional |extra_info|
+  // parameter provides an opportunity to specify extra information specific
+  // to the created browser that will be passed to
+  // CefRenderProcessHandler::OnBrowserCreated() in the render process.
+  ///
+  /*--cef(optional_param=client,optional_param=url,
+          optional_param=request_context,optional_param=delegate,
+          optional_param=extra_info)--*/
+  static CefRefPtr<CefBrowserView> CreateBrowserView(
+      CefRefPtr<CefClient> client,
+      const CefString& url,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefDictionaryValue> extra_info,
+      CefRefPtr<CefRequestContext> request_context,
+      CefRefPtr<CefBrowserViewDelegate> delegate);
+
+  ///
+  // Returns the BrowserView associated with |browser|.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefBrowserView> GetForBrowser(CefRefPtr<CefBrowser> browser);
+
+  ///
+  // Returns the CefBrowser hosted by this BrowserView. Will return NULL if the
+  // browser has not yet been created or has already been destroyed.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
+
+  ///
+  // Sets whether accelerators registered with CefWindow::SetAccelerator are
+  // triggered before or after the event is sent to the CefBrowser. If
+  // |prefer_accelerators| is true then the matching accelerator will be
+  // triggered immediately and the event will not be sent to the CefBrowser. If
+  // |prefer_accelerators| is false then the matching accelerator will only be
+  // triggered if the event is not handled by web content or by
+  // CefKeyboardHandler. The default value is false.
+  ///
+  /*--cef()--*/
+  virtual void SetPreferAccelerators(bool prefer_accelerators) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_BROWSER_VIEW_H_
diff --git a/src/include/views/cef_browser_view_delegate.h b/src/include/views/cef_browser_view_delegate.h
new file mode 100644
index 0000000..93eea91
--- /dev/null
+++ b/src/include/views/cef_browser_view_delegate.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_BROWSER_VIEW_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_BROWSER_VIEW_DELEGATE_H_
+#pragma once
+
+#include "include/cef_client.h"
+#include "include/views/cef_view_delegate.h"
+
+class CefBrowser;
+class CefBrowserView;
+
+///
+// Implement this interface to handle BrowserView events. The methods of this
+// class will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+/*--cef(source=client)--*/
+class CefBrowserViewDelegate : public CefViewDelegate {
+ public:
+  ///
+  // Called when |browser| associated with |browser_view| is created. This
+  // method will be called after CefLifeSpanHandler::OnAfterCreated() is called
+  // for |browser| and before OnPopupBrowserViewCreated() is called for
+  // |browser|'s parent delegate if |browser| is a popup.
+  ///
+  /*--cef()--*/
+  virtual void OnBrowserCreated(CefRefPtr<CefBrowserView> browser_view,
+                                CefRefPtr<CefBrowser> browser) {}
+
+  ///
+  // Called when |browser| associated with |browser_view| is destroyed. Release
+  // all references to |browser| and do not attempt to execute any methods on
+  // |browser| after this callback returns. This method will be called before
+  // CefLifeSpanHandler::OnBeforeClose() is called for |browser|.
+  ///
+  /*--cef()--*/
+  virtual void OnBrowserDestroyed(CefRefPtr<CefBrowserView> browser_view,
+                                  CefRefPtr<CefBrowser> browser) {}
+
+  ///
+  // Called before a new popup BrowserView is created. The popup originated
+  // from |browser_view|. |settings| and |client| are the values returned from
+  // CefLifeSpanHandler::OnBeforePopup(). |is_devtools| will be true if the
+  // popup will be a DevTools browser. Return the delegate that will be used for
+  // the new popup BrowserView.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
+      CefRefPtr<CefBrowserView> browser_view,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefClient> client,
+      bool is_devtools) {
+    return this;
+  }
+
+  ///
+  // Called after |popup_browser_view| is created. This method will be called
+  // after CefLifeSpanHandler::OnAfterCreated() and OnBrowserCreated() are
+  // called for the new popup browser. The popup originated from |browser_view|.
+  // |is_devtools| will be true if the popup is a DevTools browser. Optionally
+  // add |popup_browser_view| to the views hierarchy yourself and return true.
+  // Otherwise return false and a default CefWindow will be created for the
+  // popup.
+  ///
+  /*--cef()--*/
+  virtual bool OnPopupBrowserViewCreated(
+      CefRefPtr<CefBrowserView> browser_view,
+      CefRefPtr<CefBrowserView> popup_browser_view,
+      bool is_devtools) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_BROWSER_VIEW_DELEGATE_H_
diff --git a/src/include/views/cef_button.h b/src/include/views/cef_button.h
new file mode 100644
index 0000000..039446b
--- /dev/null
+++ b/src/include/views/cef_button.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_BUTTON_H_
+#define CEF_INCLUDE_VIEWS_CEF_BUTTON_H_
+#pragma once
+
+#include "include/views/cef_view.h"
+
+class CefLabelButton;
+
+///
+// A View representing a button. Depending on the specific type, the button
+// could be implemented by a native control or custom rendered. Methods must be
+// called on the browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefButton : public CefView {
+ public:
+  ///
+  // Returns this Button as a LabelButton or NULL if this is not a LabelButton.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefLabelButton> AsLabelButton() = 0;
+
+  ///
+  // Sets the current display state of the Button.
+  ///
+  /*--cef()--*/
+  virtual void SetState(cef_button_state_t state) = 0;
+
+  ///
+  // Returns the current display state of the Button.
+  ///
+  /*--cef(default_retval=CEF_BUTTON_STATE_NORMAL)--*/
+  virtual cef_button_state_t GetState() = 0;
+
+  ///
+  // Sets the Button will use an ink drop effect for displaying state changes.
+  ///
+  /*--cef()--*/
+  virtual void SetInkDropEnabled(bool enabled) = 0;
+
+  ///
+  // Sets the tooltip text that will be displayed when the user hovers the mouse
+  // cursor over the Button.
+  ///
+  /*--cef()--*/
+  virtual void SetTooltipText(const CefString& tooltip_text) = 0;
+
+  ///
+  // Sets the accessible name that will be exposed to assistive technology (AT).
+  ///
+  /*--cef()--*/
+  virtual void SetAccessibleName(const CefString& name) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_BUTTON_H_
diff --git a/src/include/views/cef_button_delegate.h b/src/include/views/cef_button_delegate.h
new file mode 100644
index 0000000..60d8a2f
--- /dev/null
+++ b/src/include/views/cef_button_delegate.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_BUTTON_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_BUTTON_DELEGATE_H_
+#pragma once
+
+#include "include/views/cef_view_delegate.h"
+
+class CefButton;
+
+///
+// Implement this interface to handle Button events. The methods of this class
+// will be called on the browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=client)--*/
+class CefButtonDelegate : public CefViewDelegate {
+ public:
+  ///
+  // Called when |button| is pressed.
+  ///
+  /*--cef()--*/
+  virtual void OnButtonPressed(CefRefPtr<CefButton> button) = 0;
+
+  ///
+  // Called when the state of |button| changes.
+  ///
+  /*--cef()--*/
+  virtual void OnButtonStateChanged(CefRefPtr<CefButton> button) {}
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_BUTTON_DELEGATE_H_
diff --git a/src/include/views/cef_display.h b/src/include/views/cef_display.h
new file mode 100644
index 0000000..de3e7ab
--- /dev/null
+++ b/src/include/views/cef_display.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_DISPLAY_H_
+#define CEF_INCLUDE_VIEWS_CEF_DISPLAY_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_base.h"
+
+///
+// This class typically, but not always, corresponds to a physical display
+// connected to the system. A fake Display may exist on a headless system, or a
+// Display may correspond to a remote, virtual display. All size and position
+// values are in density independent pixels (DIP) unless otherwise indicated.
+// Methods must be called on the browser process UI thread unless otherwise
+// indicated.
+///
+/*--cef(source=library)--*/
+class CefDisplay : public CefBaseRefCounted {
+ public:
+  ///
+  // Returns the primary Display.
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefDisplay> GetPrimaryDisplay();
+
+  ///
+  // Returns the Display nearest |point|. Set |input_pixel_coords| to true if
+  // |point| is in pixel coordinates instead of density independent pixels
+  // (DIP).
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefDisplay> GetDisplayNearestPoint(const CefPoint& point,
+                                                      bool input_pixel_coords);
+
+  ///
+  // Returns the Display that most closely intersects |bounds|.  Set
+  // |input_pixel_coords| to true if |bounds| is in pixel coordinates instead of
+  // density independent pixels (DIP).
+  ///
+  /*--cef()--*/
+  static CefRefPtr<CefDisplay> GetDisplayMatchingBounds(
+      const CefRect& bounds,
+      bool input_pixel_coords);
+
+  ///
+  // Returns the total number of Displays. Mirrored displays are excluded; this
+  // method is intended to return the number of distinct, usable displays.
+  ///
+  /*--cef()--*/
+  static size_t GetDisplayCount();
+
+  ///
+  // Returns all Displays. Mirrored displays are excluded; this method is
+  // intended to return distinct, usable displays.
+  ///
+  /*--cef(count_func=displays:GetDisplayCount)--*/
+  static void GetAllDisplays(std::vector<CefRefPtr<CefDisplay>>& displays);
+
+  ///
+  // Returns the unique identifier for this Display.
+  ///
+  /*--cef()--*/
+  virtual int64 GetID() = 0;
+
+  ///
+  // Returns this Display's device pixel scale factor. This specifies how much
+  // the UI should be scaled when the actual output has more pixels than
+  // standard displays (which is around 100~120dpi). The potential return values
+  // differ by platform.
+  ///
+  /*--cef()--*/
+  virtual float GetDeviceScaleFactor() = 0;
+
+  ///
+  // Convert |point| from density independent pixels (DIP) to pixel coordinates
+  // using this Display's device scale factor.
+  ///
+  /*--cef()--*/
+  virtual void ConvertPointToPixels(CefPoint& point) = 0;
+
+  ///
+  // Convert |point| from pixel coordinates to density independent pixels (DIP)
+  // using this Display's device scale factor.
+  ///
+  /*--cef()--*/
+  virtual void ConvertPointFromPixels(CefPoint& point) = 0;
+
+  ///
+  // Returns this Display's bounds. This is the full size of the display.
+  ///
+  /*--cef()--*/
+  virtual CefRect GetBounds() = 0;
+
+  ///
+  // Returns this Display's work area. This excludes areas of the display that
+  // are occupied for window manager toolbars, etc.
+  ///
+  /*--cef()--*/
+  virtual CefRect GetWorkArea() = 0;
+
+  ///
+  // Returns this Display's rotation in degrees.
+  ///
+  /*--cef()--*/
+  virtual int GetRotation() = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_DISPLAY_H_
diff --git a/src/include/views/cef_fill_layout.h b/src/include/views/cef_fill_layout.h
new file mode 100644
index 0000000..a412f63
--- /dev/null
+++ b/src/include/views/cef_fill_layout.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_FILL_LAYOUT_H_
+#define CEF_INCLUDE_VIEWS_CEF_FILL_LAYOUT_H_
+#pragma once
+
+#include "include/views/cef_layout.h"
+
+///
+// A simple Layout that causes the associated Panel's one child to be sized to
+// match the bounds of its parent. Methods must be called on the browser process
+// UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefFillLayout : public CefLayout {};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_FILL_LAYOUT_H_
diff --git a/src/include/views/cef_label_button.h b/src/include/views/cef_label_button.h
new file mode 100644
index 0000000..c2c7fe9
--- /dev/null
+++ b/src/include/views/cef_label_button.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_LABEL_BUTTON_H_
+#define CEF_INCLUDE_VIEWS_CEF_LABEL_BUTTON_H_
+#pragma once
+
+#include "include/cef_image.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_button_delegate.h"
+
+class CefMenuButton;
+
+///
+// LabelButton is a button with optional text and/or icon. Methods must be
+// called on the browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefLabelButton : public CefButton {
+ public:
+  ///
+  // Create a new LabelButton. A |delegate| must be provided to handle the
+  // button click. |text| will be shown on the LabelButton and used as the
+  // default accessible name.
+  ///
+  /*--cef(optional_param=text)--*/
+  static CefRefPtr<CefLabelButton> CreateLabelButton(
+      CefRefPtr<CefButtonDelegate> delegate,
+      const CefString& text);
+
+  ///
+  // Returns this LabelButton as a MenuButton or NULL if this is not a
+  // MenuButton.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefMenuButton> AsMenuButton() = 0;
+
+  ///
+  // Sets the text shown on the LabelButton. By default |text| will also be used
+  // as the accessible name.
+  ///
+  /*--cef()--*/
+  virtual void SetText(const CefString& text) = 0;
+
+  ///
+  // Returns the text shown on the LabelButton.
+  ///
+  /*--cef()--*/
+  virtual CefString GetText() = 0;
+
+  ///
+  // Sets the image shown for |button_state|. When this Button is drawn if no
+  // image exists for the current state then the image for
+  // CEF_BUTTON_STATE_NORMAL, if any, will be shown.
+  ///
+  /*--cef(optional_param=image)--*/
+  virtual void SetImage(cef_button_state_t button_state,
+                        CefRefPtr<CefImage> image) = 0;
+
+  ///
+  // Returns the image shown for |button_state|. If no image exists for that
+  // state then the image for CEF_BUTTON_STATE_NORMAL will be returned.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefImage> GetImage(cef_button_state_t button_state) = 0;
+
+  ///
+  // Sets the text color shown for the specified button |for_state| to |color|.
+  ///
+  /*--cef()--*/
+  virtual void SetTextColor(cef_button_state_t for_state,
+                            cef_color_t color) = 0;
+
+  ///
+  // Sets the text colors shown for the non-disabled states to |color|.
+  ///
+  /*--cef()--*/
+  virtual void SetEnabledTextColors(cef_color_t color) = 0;
+
+  ///
+  // Sets the font list. The format is "<FONT_FAMILY_LIST>,[STYLES] <SIZE>",
+  // where:
+  // - FONT_FAMILY_LIST is a comma-separated list of font family names,
+  // - STYLES is an optional space-separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings:
+  // - "Arial, Helvetica, Bold Italic 14px"
+  // - "Arial, 14px"
+  ///
+  /*--cef()--*/
+  virtual void SetFontList(const CefString& font_list) = 0;
+
+  ///
+  // Sets the horizontal alignment; reversed in RTL. Default is
+  // CEF_HORIZONTAL_ALIGNMENT_CENTER.
+  ///
+  /*--cef()--*/
+  virtual void SetHorizontalAlignment(cef_horizontal_alignment_t alignment) = 0;
+
+  ///
+  // Reset the minimum size of this LabelButton to |size|.
+  ///
+  /*--cef()--*/
+  virtual void SetMinimumSize(const CefSize& size) = 0;
+
+  ///
+  // Reset the maximum size of this LabelButton to |size|.
+  ///
+  /*--cef()--*/
+  virtual void SetMaximumSize(const CefSize& size) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_LABEL_BUTTON_H_
diff --git a/src/include/views/cef_layout.h b/src/include/views/cef_layout.h
new file mode 100644
index 0000000..454ff01
--- /dev/null
+++ b/src/include/views/cef_layout.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_LAYOUT_H_
+#define CEF_INCLUDE_VIEWS_CEF_LAYOUT_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+class CefBoxLayout;
+class CefFillLayout;
+
+///
+// A Layout handles the sizing of the children of a Panel according to
+// implementation-specific heuristics. Methods must be called on the browser
+// process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefLayout : public CefBaseRefCounted {
+ public:
+  ///
+  // Returns this Layout as a BoxLayout or NULL if this is not a BoxLayout.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBoxLayout> AsBoxLayout() = 0;
+
+  ///
+  // Returns this Layout as a FillLayout or NULL if this is not a FillLayout.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFillLayout> AsFillLayout() = 0;
+
+  ///
+  // Returns true if this Layout is valid.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_LAYOUT_H_
diff --git a/src/include/views/cef_menu_button.h b/src/include/views/cef_menu_button.h
new file mode 100644
index 0000000..429444d
--- /dev/null
+++ b/src/include/views/cef_menu_button.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_MENU_BUTTON_H_
+#define CEF_INCLUDE_VIEWS_CEF_MENU_BUTTON_H_
+#pragma once
+
+#include "include/cef_menu_model.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+
+///
+// MenuButton is a button with optional text, icon and/or menu marker that shows
+// a menu when clicked with the left mouse button. All size and position values
+// are in density independent pixels (DIP) unless otherwise indicated. Methods
+// must be called on the browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefMenuButton : public CefLabelButton {
+ public:
+  ///
+  // Create a new MenuButton. A |delegate| must be provided to call ShowMenu()
+  // when the button is clicked. |text| will be shown on the MenuButton and used
+  // as the default accessible name. If |with_frame| is true the button will
+  // have a visible frame at all times, center alignment, additional padding and
+  // a default minimum size of 70x33 DIP. If |with_frame| is false the button
+  // will only have a visible frame on hover/press, left alignment, less padding
+  // and no default minimum size.
+  ///
+  /*--cef(optional_param=text)--*/
+  static CefRefPtr<CefMenuButton> CreateMenuButton(
+      CefRefPtr<CefMenuButtonDelegate> delegate,
+      const CefString& text);
+
+  ///
+  // Show a menu with contents |menu_model|. |screen_point| specifies the menu
+  // position in screen coordinates. |anchor_position| specifies how the menu
+  // will be anchored relative to |screen_point|. This method should be called
+  // from CefMenuButtonDelegate::OnMenuButtonPressed().
+  ///
+  /*--cef()--*/
+  virtual void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                        const CefPoint& screen_point,
+                        cef_menu_anchor_position_t anchor_position) = 0;
+
+  ///
+  // Show the menu for this button. Results in a call to
+  // CefMenuButtonDelegate::OnMenuButtonPressed().
+  ///
+  /*--cef()--*/
+  virtual void TriggerMenu() = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_MENU_BUTTON_H_
diff --git a/src/include/views/cef_menu_button_delegate.h b/src/include/views/cef_menu_button_delegate.h
new file mode 100644
index 0000000..787b179
--- /dev/null
+++ b/src/include/views/cef_menu_button_delegate.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_MENU_BUTTON_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_MENU_BUTTON_DELEGATE_H_
+#pragma once
+
+#include "include/views/cef_button_delegate.h"
+
+class CefMenuButton;
+
+///
+// MenuButton pressed lock is released when this object is destroyed.
+///
+/*--cef(source=library)--*/
+class CefMenuButtonPressedLock : public CefBaseRefCounted {};
+
+///
+// Implement this interface to handle MenuButton events. The methods of this
+// class will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+/*--cef(source=client)--*/
+class CefMenuButtonDelegate : public CefButtonDelegate {
+ public:
+  ///
+  // Called when |button| is pressed. Call CefMenuButton::ShowMenu() to show a
+  // popup menu at |screen_point|. When showing a custom popup such as a window
+  // keep a reference to |button_pressed_lock| until the popup is hidden to
+  // maintain the pressed button state.
+  ///
+  /*--cef()--*/
+  virtual void OnMenuButtonPressed(
+      CefRefPtr<CefMenuButton> menu_button,
+      const CefPoint& screen_point,
+      CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_MENU_BUTTON_DELEGATE_H_
diff --git a/src/include/views/cef_panel.h b/src/include/views/cef_panel.h
new file mode 100644
index 0000000..4a4fb90
--- /dev/null
+++ b/src/include/views/cef_panel.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_PANEL_H_
+#define CEF_INCLUDE_VIEWS_CEF_PANEL_H_
+#pragma once
+
+#include "include/views/cef_panel_delegate.h"
+#include "include/views/cef_view.h"
+
+class CefBoxLayout;
+class CefFillLayout;
+class CefLayout;
+class CefWindow;
+
+///
+// A Panel is a container in the views hierarchy that can contain other Views
+// as children. Methods must be called on the browser process UI thread unless
+// otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefPanel : public CefView {
+ public:
+  ///
+  // Create a new Panel.
+  ///
+  /*--cef(optional_param=delegate)--*/
+  static CefRefPtr<CefPanel> CreatePanel(CefRefPtr<CefPanelDelegate> delegate);
+
+  ///
+  // Returns this Panel as a Window or NULL if this is not a Window.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefWindow> AsWindow() = 0;
+
+  ///
+  // Set this Panel's Layout to FillLayout and return the FillLayout object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefFillLayout> SetToFillLayout() = 0;
+
+  ///
+  // Set this Panel's Layout to BoxLayout and return the BoxLayout object.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBoxLayout> SetToBoxLayout(
+      const CefBoxLayoutSettings& settings) = 0;
+
+  ///
+  // Get the Layout.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefLayout> GetLayout() = 0;
+
+  ///
+  // Lay out the child Views (set their bounds based on sizing heuristics
+  // specific to the current Layout).
+  ///
+  /*--cef()--*/
+  virtual void Layout() = 0;
+
+  ///
+  // Add a child View.
+  ///
+  /*--cef()--*/
+  virtual void AddChildView(CefRefPtr<CefView> view) = 0;
+
+  ///
+  // Add a child View at the specified |index|. If |index| matches the result of
+  // GetChildCount() then the View will be added at the end.
+  ///
+  /*--cef(index_param=index)--*/
+  virtual void AddChildViewAt(CefRefPtr<CefView> view, int index) = 0;
+
+  ///
+  // Move the child View to the specified |index|. A negative value for |index|
+  // will move the View to the end.
+  ///
+  /*--cef()--*/
+  virtual void ReorderChildView(CefRefPtr<CefView> view, int index) = 0;
+
+  ///
+  // Remove a child View. The View can then be added to another Panel.
+  ///
+  /*--cef()--*/
+  virtual void RemoveChildView(CefRefPtr<CefView> view) = 0;
+
+  ///
+  // Remove all child Views. The removed Views will be deleted if the client
+  // holds no references to them.
+  ///
+  /*--cef()--*/
+  virtual void RemoveAllChildViews() = 0;
+
+  ///
+  // Returns the number of child Views.
+  ///
+  /*--cef()--*/
+  virtual size_t GetChildViewCount() = 0;
+
+  ///
+  // Returns the child View at the specified |index|.
+  ///
+  /*--cef(index_param=index)--*/
+  virtual CefRefPtr<CefView> GetChildViewAt(int index) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_PANEL_H_
diff --git a/src/include/views/cef_panel_delegate.h b/src/include/views/cef_panel_delegate.h
new file mode 100644
index 0000000..cd49611
--- /dev/null
+++ b/src/include/views/cef_panel_delegate.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_PANEL_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_PANEL_DELEGATE_H_
+#pragma once
+
+#include "include/views/cef_view_delegate.h"
+
+///
+// Implement this interface to handle Panel events. The methods of this class
+// will be called on the browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=client)--*/
+class CefPanelDelegate : public CefViewDelegate {};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_PANEL_DELEGATE_H_
diff --git a/src/include/views/cef_scroll_view.h b/src/include/views/cef_scroll_view.h
new file mode 100644
index 0000000..da472bd
--- /dev/null
+++ b/src/include/views/cef_scroll_view.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_SCROLL_VIEW_H_
+#define CEF_INCLUDE_VIEWS_CEF_SCROLL_VIEW_H_
+#pragma once
+
+#include "include/views/cef_view.h"
+
+///
+// A ScrollView will show horizontal and/or vertical scrollbars when necessary
+// based on the size of the attached content view. Methods must be called on the
+// browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefScrollView : public CefView {
+ public:
+  ///
+  // Create a new ScrollView.
+  ///
+  /*--cef(optional_param=delegate)--*/
+  static CefRefPtr<CefScrollView> CreateScrollView(
+      CefRefPtr<CefViewDelegate> delegate);
+
+  ///
+  // Set the content View. The content View must have a specified size (e.g.
+  // via CefView::SetBounds or CefViewDelegate::GetPreferredSize).
+  ///
+  /*--cef()--*/
+  virtual void SetContentView(CefRefPtr<CefView> view) = 0;
+
+  ///
+  // Returns the content View.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefView> GetContentView() = 0;
+
+  ///
+  // Returns the visible region of the content View.
+  ///
+  /*--cef()--*/
+  virtual CefRect GetVisibleContentRect() = 0;
+
+  ///
+  // Returns true if the horizontal scrollbar is currently showing.
+  ///
+  /*--cef()--*/
+  virtual bool HasHorizontalScrollbar() = 0;
+
+  ///
+  // Returns the height of the horizontal scrollbar.
+  ///
+  /*--cef()--*/
+  virtual int GetHorizontalScrollbarHeight() = 0;
+
+  ///
+  // Returns true if the vertical scrollbar is currently showing.
+  ///
+  /*--cef()--*/
+  virtual bool HasVerticalScrollbar() = 0;
+
+  ///
+  // Returns the width of the vertical scrollbar.
+  ///
+  /*--cef()--*/
+  virtual int GetVerticalScrollbarWidth() = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_SCROLL_VIEW_H_
diff --git a/src/include/views/cef_textfield.h b/src/include/views/cef_textfield.h
new file mode 100644
index 0000000..9b53f72
--- /dev/null
+++ b/src/include/views/cef_textfield.h
@@ -0,0 +1,271 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_TEXTFIELD_H_
+#define CEF_INCLUDE_VIEWS_CEF_TEXTFIELD_H_
+#pragma once
+
+#include "include/views/cef_textfield_delegate.h"
+#include "include/views/cef_view.h"
+
+///
+// A Textfield supports editing of text. This control is custom rendered with no
+// platform-specific code. Methods must be called on the browser process UI
+// thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefTextfield : public CefView {
+ public:
+  ///
+  // Create a new Textfield.
+  ///
+  /*--cef(optional_param=delegate)--*/
+  static CefRefPtr<CefTextfield> CreateTextfield(
+      CefRefPtr<CefTextfieldDelegate> delegate);
+
+  ///
+  // Sets whether the text will be displayed as asterisks.
+  ///
+  /*--cef()--*/
+  virtual void SetPasswordInput(bool password_input) = 0;
+
+  ///
+  // Returns true if the text will be displayed as asterisks.
+  ///
+  /*--cef()--*/
+  virtual bool IsPasswordInput() = 0;
+
+  ///
+  // Sets whether the text will read-only.
+  ///
+  /*--cef()--*/
+  virtual void SetReadOnly(bool read_only) = 0;
+
+  ///
+  // Returns true if the text is read-only.
+  ///
+  /*--cef()--*/
+  virtual bool IsReadOnly() = 0;
+
+  ///
+  // Returns the currently displayed text.
+  ///
+  /*--cef()--*/
+  virtual CefString GetText() = 0;
+
+  ///
+  // Sets the contents to |text|. The cursor will be moved to end of the text if
+  // the current position is outside of the text range.
+  ///
+  /*--cef()--*/
+  virtual void SetText(const CefString& text) = 0;
+
+  ///
+  // Appends |text| to the previously-existing text.
+  ///
+  /*--cef()--*/
+  virtual void AppendText(const CefString& text) = 0;
+
+  ///
+  // Inserts |text| at the current cursor position replacing any selected text.
+  ///
+  /*--cef()--*/
+  virtual void InsertOrReplaceText(const CefString& text) = 0;
+
+  ///
+  // Returns true if there is any selected text.
+  ///
+  /*--cef()--*/
+  virtual bool HasSelection() = 0;
+
+  ///
+  // Returns the currently selected text.
+  ///
+  /*--cef()--*/
+  virtual CefString GetSelectedText() = 0;
+
+  ///
+  // Selects all text. If |reversed| is true the range will end at the logical
+  // beginning of the text; this generally shows the leading portion of text
+  // that overflows its display area.
+  ///
+  /*--cef()--*/
+  virtual void SelectAll(bool reversed) = 0;
+
+  ///
+  // Clears the text selection and sets the caret to the end.
+  ///
+  /*--cef()--*/
+  virtual void ClearSelection() = 0;
+
+  ///
+  // Returns the selected logical text range.
+  ///
+  /*--cef()--*/
+  virtual CefRange GetSelectedRange() = 0;
+
+  ///
+  // Selects the specified logical text range.
+  ///
+  /*--cef()--*/
+  virtual void SelectRange(const CefRange& range) = 0;
+
+  ///
+  // Returns the current cursor position.
+  ///
+  /*--cef()--*/
+  virtual size_t GetCursorPosition() = 0;
+
+  ///
+  // Sets the text color.
+  ///
+  /*--cef()--*/
+  virtual void SetTextColor(cef_color_t color) = 0;
+
+  ///
+  // Returns the text color.
+  ///
+  /*--cef()--*/
+  virtual cef_color_t GetTextColor() = 0;
+
+  ///
+  // Sets the selection text color.
+  ///
+  /*--cef()--*/
+  virtual void SetSelectionTextColor(cef_color_t color) = 0;
+
+  ///
+  // Returns the selection text color.
+  ///
+  /*--cef()--*/
+  virtual cef_color_t GetSelectionTextColor() = 0;
+
+  ///
+  // Sets the selection background color.
+  ///
+  /*--cef()--*/
+  virtual void SetSelectionBackgroundColor(cef_color_t color) = 0;
+
+  ///
+  // Returns the selection background color.
+  ///
+  /*--cef()--*/
+  virtual cef_color_t GetSelectionBackgroundColor() = 0;
+
+  ///
+  // Sets the font list. The format is "<FONT_FAMILY_LIST>,[STYLES] <SIZE>",
+  // where:
+  // - FONT_FAMILY_LIST is a comma-separated list of font family names,
+  // - STYLES is an optional space-separated list of style names (case-sensitive
+  //   "Bold" and "Italic" are supported), and
+  // - SIZE is an integer font size in pixels with the suffix "px".
+  //
+  // Here are examples of valid font description strings:
+  // - "Arial, Helvetica, Bold Italic 14px"
+  // - "Arial, 14px"
+  ///
+  /*--cef()--*/
+  virtual void SetFontList(const CefString& font_list) = 0;
+
+  ///
+  // Applies |color| to the specified |range| without changing the default
+  // color. If |range| is empty the color will be set on the complete text
+  // contents.
+  ///
+  /*--cef()--*/
+  virtual void ApplyTextColor(cef_color_t color, const CefRange& range) = 0;
+
+  ///
+  // Applies |style| to the specified |range| without changing the default
+  // style. If |add| is true the style will be added, otherwise the style will
+  // be removed. If |range| is empty the style will be set on the complete text
+  // contents.
+  ///
+  /*--cef()--*/
+  virtual void ApplyTextStyle(cef_text_style_t style,
+                              bool add,
+                              const CefRange& range) = 0;
+
+  ///
+  // Returns true if the action associated with the specified command id is
+  // enabled. See additional comments on ExecuteCommand().
+  ///
+  /*--cef()--*/
+  virtual bool IsCommandEnabled(int command_id) = 0;
+
+  ///
+  // Performs the action associated with the specified command id. Valid values
+  // include IDS_APP_UNDO, IDS_APP_REDO, IDS_APP_CUT, IDS_APP_COPY,
+  // IDS_APP_PASTE, IDS_APP_DELETE, IDS_APP_SELECT_ALL, IDS_DELETE_* and
+  // IDS_MOVE_*. See include/cef_pack_strings.h for definitions.
+  ///
+  /*--cef()--*/
+  virtual void ExecuteCommand(int command_id) = 0;
+
+  ///
+  // Clears Edit history.
+  ///
+  /*--cef()--*/
+  virtual void ClearEditHistory() = 0;
+
+  ///
+  // Sets the placeholder text that will be displayed when the Textfield is
+  // empty.
+  ///
+  /*--cef()--*/
+  virtual void SetPlaceholderText(const CefString& text) = 0;
+
+  ///
+  // Returns the placeholder text that will be displayed when the Textfield is
+  // empty.
+  ///
+  /*--cef()--*/
+  virtual CefString GetPlaceholderText() = 0;
+
+  ///
+  // Sets the placeholder text color.
+  ///
+  /*--cef()--*/
+  virtual void SetPlaceholderTextColor(cef_color_t color) = 0;
+
+  ///
+  // Set the accessible name that will be exposed to assistive technology (AT).
+  ///
+  /*--cef()--*/
+  virtual void SetAccessibleName(const CefString& name) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_TEXTFIELD_H_
diff --git a/src/include/views/cef_textfield_delegate.h b/src/include/views/cef_textfield_delegate.h
new file mode 100644
index 0000000..c3fddaf
--- /dev/null
+++ b/src/include/views/cef_textfield_delegate.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_TEXTFIELD_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_TEXTFIELD_DELEGATE_H_
+#pragma once
+
+#include "include/views/cef_view_delegate.h"
+
+class CefTextfield;
+
+///
+// Implement this interface to handle Textfield events. The methods of this
+// class will be called on the browser process UI thread unless otherwise
+// indicated.
+///
+/*--cef(source=client)--*/
+class CefTextfieldDelegate : public CefViewDelegate {
+ public:
+  ///
+  // Called when |textfield| recieves a keyboard event. |event| contains
+  // information about the keyboard event. Return true if the keyboard event was
+  // handled or false otherwise for default handling.
+  ///
+  /*--cef()--*/
+  virtual bool OnKeyEvent(CefRefPtr<CefTextfield> textfield,
+                          const CefKeyEvent& event) {
+    return false;
+  }
+
+  ///
+  // Called after performing a user action that may change |textfield|.
+  ///
+  /*--cef()--*/
+  virtual void OnAfterUserAction(CefRefPtr<CefTextfield> textfield) {}
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_TEXTFIELD_DELEGATE_H_
diff --git a/src/include/views/cef_view.h b/src/include/views/cef_view.h
new file mode 100644
index 0000000..eed14f3
--- /dev/null
+++ b/src/include/views/cef_view.h
@@ -0,0 +1,401 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_VIEW_H_
+#define CEF_INCLUDE_VIEWS_CEF_VIEW_H_
+#pragma once
+
+#include "include/views/cef_view_delegate.h"
+
+class CefBrowserView;
+class CefButton;
+class CefPanel;
+class CefScrollView;
+class CefTextfield;
+class CefWindow;
+
+///
+// A View is a rectangle within the views View hierarchy. It is the base class
+// for all Views. All size and position values are in density independent pixels
+// (DIP) unless otherwise indicated. Methods must be called on the browser
+// process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefView : public CefBaseRefCounted {
+ public:
+  ///
+  // Returns this View as a BrowserView or NULL if this is not a BrowserView.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefBrowserView> AsBrowserView() = 0;
+
+  ///
+  // Returns this View as a Button or NULL if this is not a Button.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefButton> AsButton() = 0;
+
+  ///
+  // Returns this View as a Panel or NULL if this is not a Panel.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefPanel> AsPanel() = 0;
+
+  ///
+  // Returns this View as a ScrollView or NULL if this is not a ScrollView.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefScrollView> AsScrollView() = 0;
+
+  ///
+  // Returns this View as a Textfield or NULL if this is not a Textfield.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefTextfield> AsTextfield() = 0;
+
+  ///
+  // Returns the type of this View as a string. Used primarily for testing
+  // purposes.
+  ///
+  /*--cef()--*/
+  virtual CefString GetTypeString() = 0;
+
+  ///
+  // Returns a string representation of this View which includes the type and
+  // various type-specific identifying attributes. If |include_children| is true
+  // any child Views will also be included. Used primarily for testing purposes.
+  ///
+  /*--cef()--*/
+  virtual CefString ToString(bool include_children) = 0;
+
+  ///
+  // Returns true if this View is valid.
+  ///
+  /*--cef()--*/
+  virtual bool IsValid() = 0;
+
+  ///
+  // Returns true if this View is currently attached to another View. A View can
+  // only be attached to one View at a time.
+  ///
+  /*--cef()--*/
+  virtual bool IsAttached() = 0;
+
+  ///
+  // Returns true if this View is the same as |that| View.
+  ///
+  /*--cef()--*/
+  virtual bool IsSame(CefRefPtr<CefView> that) = 0;
+
+  ///
+  // Returns the delegate associated with this View, if any.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefViewDelegate> GetDelegate() = 0;
+
+  ///
+  // Returns the top-level Window hosting this View, if any.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefWindow> GetWindow() = 0;
+
+  ///
+  // Returns the ID for this View.
+  ///
+  /*--cef()--*/
+  virtual int GetID() = 0;
+
+  ///
+  // Sets the ID for this View. ID should be unique within the subtree that you
+  // intend to search for it. 0 is the default ID for views.
+  ///
+  /*--cef()--*/
+  virtual void SetID(int id) = 0;
+
+  ///
+  // Returns the group id of this View, or -1 if not set.
+  ///
+  /*--cef()--*/
+  virtual int GetGroupID() = 0;
+
+  ///
+  // A group id is used to tag Views which are part of the same logical group.
+  // Focus can be moved between views with the same group using the arrow keys.
+  // The group id is immutable once it's set.
+  ///
+  /*--cef()--*/
+  virtual void SetGroupID(int group_id) = 0;
+
+  ///
+  // Returns the View that contains this View, if any.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefView> GetParentView() = 0;
+
+  ///
+  // Recursively descends the view tree starting at this View, and returns the
+  // first child that it encounters with the given ID. Returns NULL if no
+  // matching child view is found.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefView> GetViewForID(int id) = 0;
+
+  ///
+  // Sets the bounds (size and position) of this View. Position is in parent
+  // coordinates.
+  ///
+  /*--cef()--*/
+  virtual void SetBounds(const CefRect& bounds) = 0;
+
+  ///
+  // Returns the bounds (size and position) of this View. Position is in parent
+  // coordinates.
+  ///
+  /*--cef()--*/
+  virtual CefRect GetBounds() = 0;
+
+  ///
+  // Returns the bounds (size and position) of this View. Position is in screen
+  // coordinates.
+  ///
+  /*--cef()--*/
+  virtual CefRect GetBoundsInScreen() = 0;
+
+  ///
+  // Sets the size of this View without changing the position.
+  ///
+  /*--cef()--*/
+  virtual void SetSize(const CefSize& size) = 0;
+
+  ///
+  // Returns the size of this View.
+  ///
+  /*--cef()--*/
+  virtual CefSize GetSize() = 0;
+
+  ///
+  // Sets the position of this View without changing the size. |position| is in
+  // parent coordinates.
+  ///
+  /*--cef()--*/
+  virtual void SetPosition(const CefPoint& position) = 0;
+
+  ///
+  // Returns the position of this View. Position is in parent coordinates.
+  ///
+  /*--cef()--*/
+  virtual CefPoint GetPosition() = 0;
+
+  ///
+  // Returns the size this View would like to be if enough space is available.
+  ///
+  /*--cef()--*/
+  virtual CefSize GetPreferredSize() = 0;
+
+  ///
+  // Size this View to its preferred size.
+  ///
+  /*--cef()--*/
+  virtual void SizeToPreferredSize() = 0;
+
+  ///
+  // Returns the minimum size for this View.
+  ///
+  /*--cef()--*/
+  virtual CefSize GetMinimumSize() = 0;
+
+  ///
+  // Returns the maximum size for this View.
+  ///
+  /*--cef()--*/
+  virtual CefSize GetMaximumSize() = 0;
+
+  ///
+  // Returns the height necessary to display this View with the provided width.
+  ///
+  /*--cef()--*/
+  virtual int GetHeightForWidth(int width) = 0;
+
+  ///
+  // Indicate that this View and all parent Views require a re-layout. This
+  // ensures the next call to Layout() will propagate to this View even if the
+  // bounds of parent Views do not change.
+  ///
+  /*--cef()--*/
+  virtual void InvalidateLayout() = 0;
+
+  ///
+  // Sets whether this View is visible. Windows are hidden by default and other
+  // views are visible by default. This View and any parent views must be set as
+  // visible for this View to be drawn in a Window. If this View is set as
+  // hidden then it and any child views will not be drawn and, if any of those
+  // views currently have focus, then focus will also be cleared. Painting is
+  // scheduled as needed. If this View is a Window then calling this method is
+  // equivalent to calling the Window Show() and Hide() methods.
+  ///
+  /*--cef()--*/
+  virtual void SetVisible(bool visible) = 0;
+
+  ///
+  // Returns whether this View is visible. A view may be visible but still not
+  // drawn in a Window if any parent views are hidden. If this View is a Window
+  // then a return value of true indicates that this Window is currently visible
+  // to the user on-screen. If this View is not a Window then call IsDrawn() to
+  // determine whether this View and all parent views are visible and will be
+  // drawn.
+  ///
+  /*--cef()--*/
+  virtual bool IsVisible() = 0;
+
+  ///
+  // Returns whether this View is visible and drawn in a Window. A view is drawn
+  // if it and all parent views are visible. If this View is a Window then
+  // calling this method is equivalent to calling IsVisible(). Otherwise, to
+  // determine if the containing Window is visible to the user on-screen call
+  // IsVisible() on the Window.
+  ///
+  /*--cef()--*/
+  virtual bool IsDrawn() = 0;
+
+  ///
+  // Set whether this View is enabled. A disabled View does not receive keyboard
+  // or mouse inputs. If |enabled| differs from the current value the View will
+  // be repainted. Also, clears focus if the focused View is disabled.
+  ///
+  /*--cef()--*/
+  virtual void SetEnabled(bool enabled) = 0;
+
+  ///
+  // Returns whether this View is enabled.
+  ///
+  /*--cef()--*/
+  virtual bool IsEnabled() = 0;
+
+  ///
+  // Sets whether this View is capable of taking focus. It will clear focus if
+  // the focused View is set to be non-focusable. This is false by default so
+  // that a View used as a container does not get the focus.
+  ///
+  /*--cef()--*/
+  virtual void SetFocusable(bool focusable) = 0;
+
+  ///
+  // Returns true if this View is focusable, enabled and drawn.
+  ///
+  /*--cef()--*/
+  virtual bool IsFocusable() = 0;
+
+  ///
+  // Return whether this View is focusable when the user requires full keyboard
+  // access, even though it may not be normally focusable.
+  ///
+  /*--cef()--*/
+  virtual bool IsAccessibilityFocusable() = 0;
+
+  ///
+  // Request keyboard focus. If this View is focusable it will become the
+  // focused View.
+  ///
+  /*--cef()--*/
+  virtual void RequestFocus() = 0;
+
+  ///
+  // Sets the background color for this View.
+  ///
+  /*--cef()--*/
+  virtual void SetBackgroundColor(cef_color_t color) = 0;
+
+  ///
+  // Returns the background color for this View.
+  ///
+  /*--cef()--*/
+  virtual cef_color_t GetBackgroundColor() = 0;
+
+  ///
+  // Convert |point| from this View's coordinate system to that of the screen.
+  // This View must belong to a Window when calling this method. Returns true
+  // if the conversion is successful or false otherwise. Use
+  // CefDisplay::ConvertPointToPixels() after calling this method if further
+  // conversion to display-specific pixel coordinates is desired.
+  ///
+  /*--cef()--*/
+  virtual bool ConvertPointToScreen(CefPoint& point) = 0;
+
+  ///
+  // Convert |point| to this View's coordinate system from that of the screen.
+  // This View must belong to a Window when calling this method. Returns true if
+  // the conversion is successful or false otherwise. Use
+  // CefDisplay::ConvertPointFromPixels() before calling this method if
+  // conversion from display-specific pixel coordinates is necessary.
+  ///
+  /*--cef()--*/
+  virtual bool ConvertPointFromScreen(CefPoint& point) = 0;
+
+  ///
+  // Convert |point| from this View's coordinate system to that of the Window.
+  // This View must belong to a Window when calling this method. Returns true if
+  // the conversion is successful or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual bool ConvertPointToWindow(CefPoint& point) = 0;
+
+  ///
+  // Convert |point| to this View's coordinate system from that of the Window.
+  // This View must belong to a Window when calling this method. Returns true if
+  // the conversion is successful or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual bool ConvertPointFromWindow(CefPoint& point) = 0;
+
+  ///
+  // Convert |point| from this View's coordinate system to that of |view|.
+  // |view| needs to be in the same Window but not necessarily the same view
+  // hierarchy. Returns true if the conversion is successful or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) = 0;
+
+  ///
+  // Convert |point| to this View's coordinate system from that |view|. |view|
+  // needs to be in the same Window but not necessarily the same view hierarchy.
+  // Returns true if the conversion is successful or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual bool ConvertPointFromView(CefRefPtr<CefView> view,
+                                    CefPoint& point) = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_VIEW_H_
diff --git a/src/include/views/cef_view_delegate.h b/src/include/views/cef_view_delegate.h
new file mode 100644
index 0000000..438896c
--- /dev/null
+++ b/src/include/views/cef_view_delegate.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_VIEW_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_VIEW_DELEGATE_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+class CefView;
+
+///
+// Implement this interface to handle view events. The methods of this class
+// will be called on the browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=client)--*/
+class CefViewDelegate : public virtual CefBaseRefCounted {
+ public:
+  ///
+  // Return the preferred size for |view|. The Layout will use this information
+  // to determine the display size.
+  ///
+  /*--cef()--*/
+  virtual CefSize GetPreferredSize(CefRefPtr<CefView> view) {
+    return CefSize();
+  }
+
+  ///
+  // Return the minimum size for |view|.
+  ///
+  /*--cef()--*/
+  virtual CefSize GetMinimumSize(CefRefPtr<CefView> view) { return CefSize(); }
+
+  ///
+  // Return the maximum size for |view|.
+  ///
+  /*--cef()--*/
+  virtual CefSize GetMaximumSize(CefRefPtr<CefView> view) { return CefSize(); }
+
+  ///
+  // Return the height necessary to display |view| with the provided |width|.
+  // If not specified the result of GetPreferredSize().height will be used by
+  // default. Override if |view|'s preferred height depends upon the width
+  // (for example, with Labels).
+  ///
+  /*--cef()--*/
+  virtual int GetHeightForWidth(CefRefPtr<CefView> view, int width) {
+    return 0;
+  }
+
+  ///
+  // Called when the parent of |view| has changed. If |view| is being added to
+  // |parent| then |added| will be true. If |view| is being removed from
+  // |parent| then |added| will be false. If |view| is being reparented the
+  // remove notification will be sent before the add notification. Do not modify
+  // the view hierarchy in this callback.
+  ///
+  /*--cef()--*/
+  virtual void OnParentViewChanged(CefRefPtr<CefView> view,
+                                   bool added,
+                                   CefRefPtr<CefView> parent) {}
+
+  ///
+  // Called when a child of |view| has changed. If |child| is being added to
+  // |view| then |added| will be true. If |child| is being removed from |view|
+  // then |added| will be false. If |child| is being reparented the remove
+  // notification will be sent to the old parent before the add notification is
+  // sent to the new parent. Do not modify the view hierarchy in this callback.
+  ///
+  /*--cef()--*/
+  virtual void OnChildViewChanged(CefRefPtr<CefView> view,
+                                  bool added,
+                                  CefRefPtr<CefView> child) {}
+
+  ///
+  // Called when |view| gains focus.
+  ///
+  /*--cef()--*/
+  virtual void OnFocus(CefRefPtr<CefView> view) {}
+
+  ///
+  // Called when |view| loses focus.
+  ///
+  /*--cef()--*/
+  virtual void OnBlur(CefRefPtr<CefView> view) {}
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_WINDOW_DELEGATE_H_
diff --git a/src/include/views/cef_window.h b/src/include/views/cef_window.h
new file mode 100644
index 0000000..2dce068
--- /dev/null
+++ b/src/include/views/cef_window.h
@@ -0,0 +1,317 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_WINDOW_H_
+#define CEF_INCLUDE_VIEWS_CEF_WINDOW_H_
+#pragma once
+
+#include "include/cef_image.h"
+#include "include/cef_menu_model.h"
+#include "include/views/cef_display.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_window_delegate.h"
+
+///
+// A Window is a top-level Window/widget in the Views hierarchy. By default it
+// will have a non-client area with title bar, icon and buttons that supports
+// moving and resizing. All size and position values are in density independent
+// pixels (DIP) unless otherwise indicated. Methods must be called on the
+// browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=library)--*/
+class CefWindow : public CefPanel {
+ public:
+  ///
+  // Create a new Window.
+  ///
+  /*--cef(optional_param=delegate)--*/
+  static CefRefPtr<CefWindow> CreateTopLevelWindow(
+      CefRefPtr<CefWindowDelegate> delegate);
+
+  ///
+  // Show the Window.
+  ///
+  /*--cef()--*/
+  virtual void Show() = 0;
+
+  ///
+  // Hide the Window.
+  ///
+  /*--cef()--*/
+  virtual void Hide() = 0;
+
+  ///
+  // Sizes the Window to |size| and centers it in the current display.
+  ///
+  /*--cef()--*/
+  virtual void CenterWindow(const CefSize& size) = 0;
+
+  ///
+  // Close the Window.
+  ///
+  /*--cef()--*/
+  virtual void Close() = 0;
+
+  ///
+  // Returns true if the Window has been closed.
+  ///
+  /*--cef()--*/
+  virtual bool IsClosed() = 0;
+
+  ///
+  // Activate the Window, assuming it already exists and is visible.
+  ///
+  /*--cef()--*/
+  virtual void Activate() = 0;
+
+  ///
+  // Deactivate the Window, making the next Window in the Z order the active
+  // Window.
+  ///
+  /*--cef()--*/
+  virtual void Deactivate() = 0;
+
+  ///
+  // Returns whether the Window is the currently active Window.
+  ///
+  /*--cef()--*/
+  virtual bool IsActive() = 0;
+
+  ///
+  // Bring this Window to the top of other Windows in the Windowing system.
+  ///
+  /*--cef()--*/
+  virtual void BringToTop() = 0;
+
+  ///
+  // Set the Window to be on top of other Windows in the Windowing system.
+  ///
+  /*--cef()--*/
+  virtual void SetAlwaysOnTop(bool on_top) = 0;
+
+  ///
+  // Returns whether the Window has been set to be on top of other Windows in
+  // the Windowing system.
+  ///
+  /*--cef()--*/
+  virtual bool IsAlwaysOnTop() = 0;
+
+  ///
+  // Maximize the Window.
+  ///
+  /*--cef()--*/
+  virtual void Maximize() = 0;
+
+  ///
+  // Minimize the Window.
+  ///
+  /*--cef()--*/
+  virtual void Minimize() = 0;
+
+  ///
+  // Restore the Window.
+  ///
+  /*--cef()--*/
+  virtual void Restore() = 0;
+
+  ///
+  // Set fullscreen Window state.
+  ///
+  /*--cef()--*/
+  virtual void SetFullscreen(bool fullscreen) = 0;
+
+  ///
+  // Returns true if the Window is maximized.
+  ///
+  /*--cef()--*/
+  virtual bool IsMaximized() = 0;
+
+  ///
+  // Returns true if the Window is minimized.
+  ///
+  /*--cef()--*/
+  virtual bool IsMinimized() = 0;
+
+  ///
+  // Returns true if the Window is fullscreen.
+  ///
+  /*--cef()--*/
+  virtual bool IsFullscreen() = 0;
+
+  ///
+  // Set the Window title.
+  ///
+  /*--cef(optional_param=title)--*/
+  virtual void SetTitle(const CefString& title) = 0;
+
+  ///
+  // Get the Window title.
+  ///
+  /*--cef()--*/
+  virtual CefString GetTitle() = 0;
+
+  ///
+  // Set the Window icon. This should be a 16x16 icon suitable for use in the
+  // Windows's title bar.
+  ///
+  /*--cef()--*/
+  virtual void SetWindowIcon(CefRefPtr<CefImage> image) = 0;
+
+  ///
+  // Get the Window icon.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefImage> GetWindowIcon() = 0;
+
+  ///
+  // Set the Window App icon. This should be a larger icon for use in the host
+  // environment app switching UI. On Windows, this is the ICON_BIG used in
+  // Alt-Tab list and Windows taskbar. The Window icon will be used by default
+  // if no Window App icon is specified.
+  ///
+  /*--cef()--*/
+  virtual void SetWindowAppIcon(CefRefPtr<CefImage> image) = 0;
+
+  ///
+  // Get the Window App icon.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefImage> GetWindowAppIcon() = 0;
+
+  ///
+  // Show a menu with contents |menu_model|. |screen_point| specifies the menu
+  // position in screen coordinates. |anchor_position| specifies how the menu
+  // will be anchored relative to |screen_point|.
+  ///
+  /*--cef()--*/
+  virtual void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                        const CefPoint& screen_point,
+                        cef_menu_anchor_position_t anchor_position) = 0;
+
+  ///
+  // Cancel the menu that is currently showing, if any.
+  ///
+  /*--cef()--*/
+  virtual void CancelMenu() = 0;
+
+  ///
+  // Returns the Display that most closely intersects the bounds of this Window.
+  // May return NULL if this Window is not currently displayed.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefDisplay> GetDisplay() = 0;
+
+  ///
+  // Returns the bounds (size and position) of this Window's client area.
+  // Position is in screen coordinates.
+  ///
+  /*--cef()--*/
+  virtual CefRect GetClientAreaBoundsInScreen() = 0;
+
+  ///
+  // Set the regions where mouse events will be intercepted by this Window to
+  // support drag operations. Call this method with an empty vector to clear the
+  // draggable regions. The draggable region bounds should be in window
+  // coordinates.
+  ///
+  /*--cef(optional_param=regions)--*/
+  virtual void SetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) = 0;
+
+  ///
+  // Retrieve the platform window handle for this Window.
+  ///
+  /*--cef()--*/
+  virtual CefWindowHandle GetWindowHandle() = 0;
+
+  ///
+  // Simulate a key press. |key_code| is the VKEY_* value from Chromium's
+  // ui/events/keycodes/keyboard_codes.h header (VK_* values on Windows).
+  // |event_flags| is some combination of EVENTFLAG_SHIFT_DOWN,
+  // EVENTFLAG_CONTROL_DOWN and/or EVENTFLAG_ALT_DOWN. This method is exposed
+  // primarily for testing purposes.
+  ///
+  /*--cef()--*/
+  virtual void SendKeyPress(int key_code, uint32 event_flags) = 0;
+
+  ///
+  // Simulate a mouse move. The mouse cursor will be moved to the specified
+  // (screen_x, screen_y) position. This method is exposed primarily for testing
+  // purposes.
+  ///
+  /*--cef()--*/
+  virtual void SendMouseMove(int screen_x, int screen_y) = 0;
+
+  ///
+  // Simulate mouse down and/or mouse up events. |button| is the mouse button
+  // type. If |mouse_down| is true a mouse down event will be sent. If
+  // |mouse_up| is true a mouse up event will be sent. If both are true a mouse
+  // down event will be sent followed by a mouse up event (equivalent to
+  // clicking the mouse button). The events will be sent using the current
+  // cursor position so make sure to call SendMouseMove() first to position the
+  // mouse. This method is exposed primarily for testing purposes.
+  ///
+  /*--cef()--*/
+  virtual void SendMouseEvents(cef_mouse_button_type_t button,
+                               bool mouse_down,
+                               bool mouse_up) = 0;
+  ///
+  // Set the keyboard accelerator for the specified |command_id|. |key_code| can
+  // be any virtual key or character value. CefWindowDelegate::OnAccelerator
+  // will be called if the keyboard combination is triggered while this window
+  // has focus.
+  ///
+  /*--cef()--*/
+  virtual void SetAccelerator(int command_id,
+                              int key_code,
+                              bool shift_pressed,
+                              bool ctrl_pressed,
+                              bool alt_pressed) = 0;
+
+  ///
+  // Remove the keyboard accelerator for the specified |command_id|.
+  ///
+  /*--cef()--*/
+  virtual void RemoveAccelerator(int command_id) = 0;
+
+  ///
+  // Remove all keyboard accelerators.
+  ///
+  /*--cef()--*/
+  virtual void RemoveAllAccelerators() = 0;
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_WINDOW_H_
diff --git a/src/include/views/cef_window_delegate.h b/src/include/views/cef_window_delegate.h
new file mode 100644
index 0000000..a145782
--- /dev/null
+++ b/src/include/views/cef_window_delegate.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file must follow a specific format in order to
+// support the CEF translator tool. See the translator.README.txt file in the
+// tools directory for more information.
+//
+
+#ifndef CEF_INCLUDE_VIEWS_CEF_WINDOW_DELEGATE_H_
+#define CEF_INCLUDE_VIEWS_CEF_WINDOW_DELEGATE_H_
+#pragma once
+
+#include "include/views/cef_panel_delegate.h"
+
+class CefWindow;
+
+///
+// Implement this interface to handle window events. The methods of this class
+// will be called on the browser process UI thread unless otherwise indicated.
+///
+/*--cef(source=client)--*/
+class CefWindowDelegate : public CefPanelDelegate {
+ public:
+  ///
+  // Called when |window| is created.
+  ///
+  /*--cef()--*/
+  virtual void OnWindowCreated(CefRefPtr<CefWindow> window) {}
+
+  ///
+  // Called when |window| is destroyed. Release all references to |window| and
+  // do not attempt to execute any methods on |window| after this callback
+  // returns.
+  ///
+  /*--cef()--*/
+  virtual void OnWindowDestroyed(CefRefPtr<CefWindow> window) {}
+
+  ///
+  // Return the parent for |window| or NULL if the |window| does not have a
+  // parent. Windows with parents will not get a taskbar button. Set |is_menu|
+  // to true if |window| will be displayed as a menu, in which case it will not
+  // be clipped to the parent window bounds. Set |can_activate_menu| to false
+  // if |is_menu| is true and |window| should not be activated (given keyboard
+  // focus) when displayed.
+  ///
+  /*--cef()--*/
+  virtual CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
+                                               bool* is_menu,
+                                               bool* can_activate_menu) {
+    return nullptr;
+  }
+
+  ///
+  // Return true if |window| should be created without a frame or title bar. The
+  // window will be resizable if CanResize() returns true. Use
+  // CefWindow::SetDraggableRegions() to specify draggable regions.
+  ///
+  /*--cef()--*/
+  virtual bool IsFrameless(CefRefPtr<CefWindow> window) { return false; }
+
+  ///
+  // Return true if |window| can be resized.
+  ///
+  /*--cef()--*/
+  virtual bool CanResize(CefRefPtr<CefWindow> window) { return true; }
+
+  ///
+  // Return true if |window| can be maximized.
+  ///
+  /*--cef()--*/
+  virtual bool CanMaximize(CefRefPtr<CefWindow> window) { return true; }
+
+  ///
+  // Return true if |window| can be minimized.
+  ///
+  /*--cef()--*/
+  virtual bool CanMinimize(CefRefPtr<CefWindow> window) { return true; }
+
+  ///
+  // Return true if |window| can be closed. This will be called for user-
+  // initiated window close actions and when CefWindow::Close() is called.
+  ///
+  /*--cef()--*/
+  virtual bool CanClose(CefRefPtr<CefWindow> window) { return true; }
+
+  ///
+  // Called when a keyboard accelerator registered with
+  // CefWindow::SetAccelerator is triggered. Return true if the accelerator was
+  // handled or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
+    return false;
+  }
+
+  ///
+  // Called after all other controls in the window have had a chance to
+  // handle the event. |event| contains information about the keyboard event.
+  // Return true if the keyboard event was handled or false otherwise.
+  ///
+  /*--cef()--*/
+  virtual bool OnKeyEvent(CefRefPtr<CefWindow> window,
+                          const CefKeyEvent& event) {
+    return false;
+  }
+};
+
+#endif  // CEF_INCLUDE_VIEWS_CEF_WINDOW_DELEGATE_H_
diff --git a/src/include/wrapper/cef_byte_read_handler.h b/src/include/wrapper/cef_byte_read_handler.h
new file mode 100644
index 0000000..bf593d0
--- /dev/null
+++ b/src/include/wrapper/cef_byte_read_handler.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_BYTE_READ_HANDLER_H_
+#define CEF_INCLUDE_WRAPPER_CEF_BYTE_READ_HANDLER_H_
+#pragma once
+
+#include "include/base/cef_lock.h"
+#include "include/base/cef_macros.h"
+#include "include/cef_base.h"
+#include "include/cef_stream.h"
+
+///
+// Thread safe implementation of the CefReadHandler class for reading an
+// in-memory array of bytes.
+///
+class CefByteReadHandler : public CefReadHandler {
+ public:
+  ///
+  // Create a new object for reading an array of bytes. An optional |source|
+  // reference can be kept to keep the underlying data source from being
+  // released while the reader exists.
+  ///
+  CefByteReadHandler(const unsigned char* bytes,
+                     size_t size,
+                     CefRefPtr<CefBaseRefCounted> source);
+
+  // CefReadHandler methods.
+  virtual size_t Read(void* ptr, size_t size, size_t n) OVERRIDE;
+  virtual int Seek(int64 offset, int whence) OVERRIDE;
+  virtual int64 Tell() OVERRIDE;
+  virtual int Eof() OVERRIDE;
+  virtual bool MayBlock() OVERRIDE { return false; }
+
+ private:
+  const unsigned char* bytes_;
+  int64 size_;
+  int64 offset_;
+  CefRefPtr<CefBaseRefCounted> source_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefByteReadHandler);
+  DISALLOW_COPY_AND_ASSIGN(CefByteReadHandler);
+};
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_BYTE_READ_HANDLER_H_
diff --git a/src/include/wrapper/cef_closure_task.h b/src/include/wrapper/cef_closure_task.h
new file mode 100644
index 0000000..71f54b5
--- /dev/null
+++ b/src/include/wrapper/cef_closure_task.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_CLOSURE_TASK_H_
+#define CEF_INCLUDE_WRAPPER_CEF_CLOSURE_TASK_H_
+#pragma once
+
+#include "include/base/cef_callback_forward.h"
+#include "include/base/cef_macros.h"
+#include "include/cef_task.h"
+
+///
+// Helpers for asynchronously executing a base::Closure (bound function or
+// method) on a CEF thread. Creation of base::Closures can be facilitated using
+// base::Bind. See include/base/cef_callback.h for complete usage instructions.
+//
+// TO use these helpers you should include this header and the header that
+// defines base::Bind.
+//
+// #include "include/base/cef_bind.h"
+// #include "include/wrapper/cef_closure_task.h"
+//
+// Example of executing a bound function:
+//
+// // Define a function.
+// void MyFunc(int arg) { /* do something with |arg| on the UI thread */ }
+//
+// // Post a task that will execute MyFunc on the UI thread and pass an |arg|
+// // value of 5.
+// CefPostTask(TID_UI, base::Bind(&MyFunc, 5));
+//
+// Example of executing a bound method:
+//
+// // Define a class.
+// class MyClass : public CefBaseRefCounted {
+//  public:
+//   MyClass() {}
+//   void MyMethod(int arg) { /* do something with |arg| on the UI thread */ }
+//  private:
+//   IMPLEMENT_REFCOUNTING(MyClass);
+// };
+//
+// // Create an instance of MyClass.
+// CefRefPtr<MyClass> instance = new MyClass();
+//
+// // Post a task that will execute MyClass::MyMethod on the UI thread and pass
+// // an |arg| value of 5. |instance| will be kept alive until after the task
+// // completes.
+// CefPostTask(TID_UI, base::Bind(&MyClass::MyMethod, instance, 5));
+///
+
+///
+// Create a CefTask that wraps a base::Closure. Can be used in combination with
+// CefTaskRunner.
+///
+CefRefPtr<CefTask> CefCreateClosureTask(const base::Closure& closure);
+
+///
+// Post a Closure for execution on the specified thread.
+///
+bool CefPostTask(CefThreadId threadId, const base::Closure& closure);
+
+///
+// Post a Closure for delayed execution on the specified thread.
+///
+bool CefPostDelayedTask(CefThreadId threadId,
+                        const base::Closure& closure,
+                        int64 delay_ms);
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_CLOSURE_TASK_H_
diff --git a/src/include/wrapper/cef_helpers.h b/src/include/wrapper/cef_helpers.h
new file mode 100644
index 0000000..93c4a51
--- /dev/null
+++ b/src/include/wrapper/cef_helpers.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_HELPERS_H_
+#define CEF_INCLUDE_WRAPPER_CEF_HELPERS_H_
+#pragma once
+
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/cef_task.h"
+
+#define CEF_REQUIRE_UI_THREAD() DCHECK(CefCurrentlyOn(TID_UI));
+#define CEF_REQUIRE_IO_THREAD() DCHECK(CefCurrentlyOn(TID_IO));
+#define CEF_REQUIRE_FILE_THREAD() DCHECK(CefCurrentlyOn(TID_FILE));
+#define CEF_REQUIRE_RENDERER_THREAD() DCHECK(CefCurrentlyOn(TID_RENDERER));
+
+// Use this struct in conjuction with refcounted types to ensure that an
+// object is deleted on the specified thread. For example:
+//
+// class Foo : public base::RefCountedThreadSafe<Foo, CefDeleteOnUIThread> {
+//  public:
+//   Foo();
+//   void DoSomething();
+//
+//  private:
+//   // Allow deletion via scoped_refptr only.
+//   friend struct CefDeleteOnThread<TID_UI>;
+//   friend class base::RefCountedThreadSafe<Foo, CefDeleteOnUIThread>;
+//
+//   virtual ~Foo() {}
+// };
+//
+// base::scoped_refptr<Foo> foo = new Foo();
+// foo->DoSomething();
+// foo = NULL;  // Deletion of |foo| will occur on the UI thread.
+//
+template <CefThreadId thread>
+struct CefDeleteOnThread {
+  template <typename T>
+  static void Destruct(const T* x) {
+    if (CefCurrentlyOn(thread)) {
+      delete x;
+    } else {
+      CefPostTask(thread,
+                  base::Bind(&CefDeleteOnThread<thread>::Destruct<T>, x));
+    }
+  }
+};
+
+struct CefDeleteOnUIThread : public CefDeleteOnThread<TID_UI> {};
+struct CefDeleteOnIOThread : public CefDeleteOnThread<TID_IO> {};
+struct CefDeleteOnFileThread : public CefDeleteOnThread<TID_FILE> {};
+struct CefDeleteOnRendererThread : public CefDeleteOnThread<TID_RENDERER> {};
+
+///
+// Helper class to manage a scoped copy of |argv|.
+///
+class CefScopedArgArray {
+ public:
+  CefScopedArgArray(int argc, char* argv[]) {
+    // argv should have (argc + 1) elements, the last one always being NULL.
+    array_ = new char*[argc + 1];
+    values_.resize(argc);
+    for (int i = 0; i < argc; ++i) {
+      values_[i] = argv[i];
+      array_[i] = const_cast<char*>(values_[i].c_str());
+    }
+    array_[argc] = NULL;
+  }
+  ~CefScopedArgArray() { delete[] array_; }
+
+  char** array() const { return array_; }
+
+ private:
+  char** array_;
+
+  // Keep values in a vector separate from |array_| because various users may
+  // modify |array_| and we still want to clean up memory properly.
+  std::vector<std::string> values_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefScopedArgArray);
+};
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_HELPERS_H_
diff --git a/src/include/wrapper/cef_library_loader.h b/src/include/wrapper/cef_library_loader.h
new file mode 100644
index 0000000..c15b855
--- /dev/null
+++ b/src/include/wrapper/cef_library_loader.h
@@ -0,0 +1,128 @@
+// Copyright (c) 2018 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_LIBRARY_LOADER_H_
+#define CEF_INCLUDE_WRAPPER_CEF_LIBRARY_LOADER_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+
+#ifdef __cplusplus
+#include <string>
+
+#include "include/base/cef_macros.h"
+
+extern "C" {
+#endif  // __cplusplus
+
+///
+// Load the CEF library at the specified |path|. Returns true (1) on
+// success and false (0) on failure.
+///
+int cef_load_library(const char* path);
+
+///
+// Unload the CEF library that was previously loaded. Returns true (1)
+// on success and false (0) on failure.
+///
+int cef_unload_library();
+
+#ifdef __cplusplus
+}
+
+#if defined(OS_MACOSX)
+
+///
+// Scoped helper for loading and unloading the CEF framework library at
+// runtime from the expected location in the app bundle. Loading at runtime
+// instead of linking directly is a requirement of the macOS sandbox
+// implementation.
+//
+// Example usage in the main process:
+//
+//   #include "include/wrapper/cef_library_loader.h"
+//
+//   int main(int argc, char* argv[]) {
+//     // Dynamically load the CEF framework library.
+//     CefScopedLibraryLoader library_loader;
+//     if (!library_loader.LoadInMain())
+//       return 1;
+//
+//     // Continue with CEF initialization...
+//   }
+//
+// Example usage in the helper process:
+//
+//   #include "include/cef_sandbox_mac.h"
+//   #include "include/wrapper/cef_library_loader.h"
+//
+//   int main(int argc, char* argv[]) {
+//     // Initialize the macOS sandbox for this helper process.
+//     CefScopedSandboxContext sandbox_context;
+//     if (!sandbox_context.Initialize(argc, argv))
+//       return 1;
+//
+//     // Dynamically load the CEF framework library.
+//     CefScopedLibraryLoader library_loader;
+//     if (!library_loader.LoadInHelper())
+//       return 1;
+//
+//     // Continue with CEF initialization...
+//   }
+///
+class CefScopedLibraryLoader {
+ public:
+  CefScopedLibraryLoader();
+  ~CefScopedLibraryLoader();
+
+  ///
+  // Load the CEF framework in the main process from the expected app
+  // bundle location relative to the executable. Returns true if the
+  // load succeeds.
+  ///
+  bool LoadInMain() { return Load(false); }
+
+  ///
+  // Load the CEF framework in the helper process from the expected app
+  // bundle location relative to the executable. Returns true if the
+  // load succeeds.
+  ///
+  bool LoadInHelper() { return Load(true); }
+
+ private:
+  bool Load(bool helper);
+
+  bool loaded_;
+  DISALLOW_COPY_AND_ASSIGN(CefScopedLibraryLoader);
+};
+
+#endif  // defined(OS_MACOSX)
+#endif  // __cplusplus
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_LIBRARY_LOADER_H_
diff --git a/src/include/wrapper/cef_message_router.h b/src/include/wrapper/cef_message_router.h
new file mode 100644
index 0000000..9344222
--- /dev/null
+++ b/src/include/wrapper/cef_message_router.h
@@ -0,0 +1,429 @@
+// Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_
+#define CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_
+#pragma once
+
+#include "include/base/cef_ref_counted.h"
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+#include "include/cef_process_message.h"
+#include "include/cef_v8.h"
+
+// The below classes implement support for routing aynchronous messages between
+// JavaScript running in the renderer process and C++ running in the browser
+// process. An application interacts with the router by passing it data from
+// standard CEF C++ callbacks (OnBeforeBrowse, OnProcessMessageRecieved,
+// OnContextCreated, etc). The renderer-side router supports generic JavaScript
+// callback registration and execution while the browser-side router supports
+// application-specific logic via one or more application-provided Handler
+// instances.
+//
+// The renderer-side router implementation exposes a query function and a cancel
+// function via the JavaScript 'window' object:
+//
+//    // Create and send a new query.
+//    var request_id = window.cefQuery({
+//        request: 'my_request',
+//        persistent: false,
+//        onSuccess: function(response) {},
+//        onFailure: function(error_code, error_message) {}
+//    });
+//
+//    // Optionally cancel the query.
+//    window.cefQueryCancel(request_id);
+//
+// When |window.cefQuery| is executed the request is sent asynchronously to one
+// or more C++ Handler objects registered in the browser process. Each C++
+// Handler can choose to either handle or ignore the query in the
+// Handler::OnQuery callback. If a Handler chooses to handle the query then it
+// should execute Callback::Success when a response is available or
+// Callback::Failure if an error occurs. This will result in asynchronous
+// execution of the associated JavaScript callback in the renderer process. Any
+// queries unhandled by C++ code in the browser process will be automatically
+// canceled and the associated JavaScript onFailure callback will be executed
+// with an error code of -1.
+//
+// Queries can be either persistent or non-persistent. If the query is
+// persistent than the callbacks will remain registered until one of the
+// following conditions are met:
+//
+// A. The query is canceled in JavaScript using the |window.cefQueryCancel|
+//    function.
+// B. The query is canceled in C++ code using the Callback::Failure function.
+// C. The context associated with the query is released due to browser
+//    destruction, navigation or renderer process termination.
+//
+// If the query is non-persistent then the registration will be removed after
+// the JavaScript callback is executed a single time. If a query is canceled for
+// a reason other than Callback::Failure being executed then the associated
+// Handler's OnQueryCanceled method will be called.
+//
+// Some possible usage patterns include:
+//
+// One-time Request. Use a non-persistent query to send a JavaScript request.
+//    The Handler evaluates the request and returns the response. The query is
+//    then discarded.
+//
+// Broadcast. Use a persistent query to register as a JavaScript broadcast
+//    receiver. The Handler keeps track of all registered Callbacks and executes
+//    them sequentially to deliver the broadcast message.
+//
+// Subscription. Use a persistent query to register as a JavaScript subscription
+//    receiver. The Handler initiates the subscription feed on the first request
+//    and delivers responses to all registered subscribers as they become
+//    available. The Handler cancels the subscription feed when there are no
+//    longer any registered JavaScript receivers.
+//
+// Message routing occurs on a per-browser and per-context basis. Consequently,
+// additional application logic can be applied by restricting which browser or
+// context instances are passed into the router. If you choose to use this
+// approach do so cautiously. In order for the router to function correctly any
+// browser or context instance passed into a single router callback must then
+// be passed into all router callbacks.
+//
+// There is generally no need to have multiple renderer-side routers unless you
+// wish to have multiple bindings with different JavaScript function names. It
+// can be useful to have multiple browser-side routers with different client-
+// provided Handler instances when implementing different behaviors on a per-
+// browser basis.
+//
+// This implementation places no formatting restrictions on payload content.
+// An application may choose to exchange anything from simple formatted
+// strings to serialized XML or JSON data.
+//
+//
+// EXAMPLE USAGE
+//
+// 1. Define the router configuration. You can optionally specify settings
+//    like the JavaScript function names. The configuration must be the same in
+//    both the browser and renderer processes. If using multiple routers in the
+//    same application make sure to specify unique function names for each
+//    router configuration.
+//
+//    // Example config object showing the default values.
+//    CefMessageRouterConfig config;
+//    config.js_query_function = "cefQuery";
+//    config.js_cancel_function = "cefQueryCancel";
+//
+// 2. Create an instance of CefMessageRouterBrowserSide in the browser process.
+//    You might choose to make it a member of your CefClient implementation,
+//    for example.
+//
+//    browser_side_router_ = CefMessageRouterBrowserSide::Create(config);
+//
+// 3. Register one or more Handlers. The Handler instances must either outlive
+//    the router or be removed from the router before they're deleted.
+//
+//    browser_side_router_->AddHandler(my_handler);
+//
+// 4. Call all required CefMessageRouterBrowserSide methods from other callbacks
+//    in your CefClient implementation (OnBeforeClose, etc). See the
+//    CefMessageRouterBrowserSide class documentation for the complete list of
+//    methods.
+//
+// 5. Create an instance of CefMessageRouterRendererSide in the renderer
+// process.
+//    You might choose to make it a member of your CefApp implementation, for
+//    example.
+//
+//    renderer_side_router_ = CefMessageRouterRendererSide::Create(config);
+//
+// 6. Call all required CefMessageRouterRendererSide methods from other
+//    callbacks in your CefRenderProcessHandler implementation
+//    (OnContextCreated, etc). See the CefMessageRouterRendererSide class
+//    documentation for the complete list of methods.
+//
+// 7. Execute the query function from JavaScript code.
+//
+//    window.cefQuery({request: 'my_request',
+//                     persistent: false,
+//                     onSuccess: function(response) { print(response); },
+//                     onFailure: function(error_code, error_message) {} });
+//
+// 8. Handle the query in your Handler::OnQuery implementation and execute the
+//    appropriate callback either immediately or asynchronously.
+//
+//    void MyHandler::OnQuery(int64 query_id,
+//                            CefRefPtr<CefBrowser> browser,
+//                            CefRefPtr<CefFrame> frame,
+//                            const CefString& request,
+//                            bool persistent,
+//                            CefRefPtr<Callback> callback) {
+//      if (request == "my_request") {
+//        callback->Continue("my_response");
+//        return true;
+//      }
+//      return false;  // Not handled.
+//    }
+//
+// 9. Notice that the onSuccess callback is executed in JavaScript.
+
+///
+// Used to configure the query router. The same values must be passed to both
+// CefMessageRouterBrowserSide and CefMessageRouterRendererSide. If using
+// multiple router pairs make sure to choose values that do not conflict.
+///
+struct CefMessageRouterConfig {
+  CefMessageRouterConfig();
+
+  // Name of the JavaScript function that will be added to the 'window' object
+  // for sending a query. The default value is "cefQuery".
+  CefString js_query_function;
+
+  // Name of the JavaScript function that will be added to the 'window' object
+  // for canceling a pending query. The default value is "cefQueryCancel".
+  CefString js_cancel_function;
+};
+
+///
+// Implements the browser side of query routing. The methods of this class may
+// be called on any browser process thread unless otherwise indicated.
+///
+class CefMessageRouterBrowserSide
+    : public base::RefCountedThreadSafe<CefMessageRouterBrowserSide> {
+ public:
+  ///
+  // Callback associated with a single pending asynchronous query. Execute the
+  // Success or Failure method to send an asynchronous response to the
+  // associated JavaScript handler. It is a runtime error to destroy a Callback
+  // object associated with an uncanceled query without first executing one of
+  // the callback methods. The methods of this class may be called on any
+  // browser process thread.
+  ///
+  class Callback : public CefBaseRefCounted {
+   public:
+    ///
+    // Notify the associated JavaScript onSuccess callback that the query has
+    // completed successfully with the specified |response|.
+    ///
+    virtual void Success(const CefString& response) = 0;
+
+    ///
+    // Notify the associated JavaScript onFailure callback that the query has
+    // failed with the specified |error_code| and |error_message|.
+    ///
+    virtual void Failure(int error_code, const CefString& error_message) = 0;
+  };
+
+  ///
+  // Implement this interface to handle queries. All methods will be executed on
+  // the browser process UI thread.
+  ///
+  class Handler {
+   public:
+    typedef CefMessageRouterBrowserSide::Callback Callback;
+
+    ///
+    // Executed when a new query is received. |query_id| uniquely identifies the
+    // query for the life span of the router. Return true to handle the query
+    // or false to propagate the query to other registered handlers, if any. If
+    // no handlers return true from this method then the query will be
+    // automatically canceled with an error code of -1 delivered to the
+    // JavaScript onFailure callback. If this method returns true then a
+    // Callback method must be executed either in this method or asynchronously
+    // to complete the query.
+    ///
+    virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         int64 query_id,
+                         const CefString& request,
+                         bool persistent,
+                         CefRefPtr<Callback> callback) {
+      return false;
+    }
+
+    ///
+    // Executed when a query has been canceled either explicitly using the
+    // JavaScript cancel function or implicitly due to browser destruction,
+    // navigation or renderer process termination. It will only be called for
+    // the single handler that returned true from OnQuery for the same
+    // |query_id|. No references to the associated Callback object should be
+    // kept after this method is called, nor should any Callback methods be
+    // executed.
+    ///
+    virtual void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                                 CefRefPtr<CefFrame> frame,
+                                 int64 query_id) {}
+
+    virtual ~Handler() {}
+  };
+
+  ///
+  // Create a new router with the specified configuration.
+  ///
+  static CefRefPtr<CefMessageRouterBrowserSide> Create(
+      const CefMessageRouterConfig& config);
+
+  ///
+  // Add a new query handler. If |first| is true it will be added as the first
+  // handler, otherwise it will be added as the last handler. Returns true if
+  // the handler is added successfully or false if the handler has already been
+  // added. Must be called on the browser process UI thread. The Handler object
+  // must either outlive the router or be removed before deletion.
+  ///
+  virtual bool AddHandler(Handler* handler, bool first) = 0;
+
+  ///
+  // Remove an existing query handler. Any pending queries associated with the
+  // handler will be canceled. Handler::OnQueryCanceled will be called and the
+  // associated JavaScript onFailure callback will be executed with an error
+  // code of -1. Returns true if the handler is removed successfully or false
+  // if the handler is not found. Must be called on the browser process UI
+  // thread.
+  ///
+  virtual bool RemoveHandler(Handler* handler) = 0;
+
+  ///
+  // Cancel all pending queries associated with either |browser| or |handler|.
+  // If both |browser| and |handler| are NULL all pending queries will be
+  // canceled. Handler::OnQueryCanceled will be called and the associated
+  // JavaScript onFailure callback will be executed in all cases with an error
+  // code of -1.
+  ///
+  virtual void CancelPending(CefRefPtr<CefBrowser> browser,
+                             Handler* handler) = 0;
+
+  ///
+  // Returns the number of queries currently pending for the specified |browser|
+  // and/or |handler|. Either or both values may be empty. Must be called on the
+  // browser process UI thread.
+  ///
+  virtual int GetPendingCount(CefRefPtr<CefBrowser> browser,
+                              Handler* handler) = 0;
+
+  // The below methods should be called from other CEF handlers. They must be
+  // called exactly as documented for the router to function correctly.
+
+  ///
+  // Call from CefLifeSpanHandler::OnBeforeClose. Any pending queries associated
+  // with |browser| will be canceled and Handler::OnQueryCanceled will be
+  // called. No JavaScript callbacks will be executed since this indicates
+  // destruction of the browser.
+  ///
+  virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) = 0;
+
+  ///
+  // Call from CefRequestHandler::OnRenderProcessTerminated. Any pending queries
+  // associated with |browser| will be canceled and Handler::OnQueryCanceled
+  // will be called. No JavaScript callbacks will be executed since this
+  // indicates destruction of the context.
+  ///
+  virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser) = 0;
+
+  ///
+  // Call from CefRequestHandler::OnBeforeBrowse only if the navigation is
+  // allowed to proceed. If |frame| is the main frame then any pending queries
+  // associated with |browser| will be canceled and Handler::OnQueryCanceled
+  // will be called. No JavaScript callbacks will be executed since this
+  // indicates destruction of the context.
+  ///
+  virtual void OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame) = 0;
+
+  ///
+  // Call from CefClient::OnProcessMessageReceived. Returns true if the message
+  // is handled by this router or false otherwise.
+  ///
+  virtual bool OnProcessMessageReceived(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefProcessId source_process,
+      CefRefPtr<CefProcessMessage> message) = 0;
+
+ protected:
+  // Protect against accidental deletion of this object.
+  friend class base::RefCountedThreadSafe<CefMessageRouterBrowserSide>;
+  virtual ~CefMessageRouterBrowserSide() {}
+};
+
+///
+// Implements the renderer side of query routing. The methods of this class must
+// be called on the render process main thread.
+///
+class CefMessageRouterRendererSide
+    : public base::RefCountedThreadSafe<CefMessageRouterRendererSide> {
+ public:
+  ///
+  // Create a new router with the specified configuration.
+  ///
+  static CefRefPtr<CefMessageRouterRendererSide> Create(
+      const CefMessageRouterConfig& config);
+
+  ///
+  // Returns the number of queries currently pending for the specified |browser|
+  // and/or |context|. Either or both values may be empty.
+  ///
+  virtual int GetPendingCount(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefV8Context> context) = 0;
+
+  // The below methods should be called from other CEF handlers. They must be
+  // called exactly as documented for the router to function correctly.
+
+  ///
+  // Call from CefRenderProcessHandler::OnContextCreated. Registers the
+  // JavaScripts functions with the new context.
+  ///
+  virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefRefPtr<CefV8Context> context) = 0;
+
+  ///
+  // Call from CefRenderProcessHandler::OnContextReleased. Any pending queries
+  // associated with the released context will be canceled and
+  // Handler::OnQueryCanceled will be called in the browser process.
+  ///
+  virtual void OnContextReleased(CefRefPtr<CefBrowser> browser,
+                                 CefRefPtr<CefFrame> frame,
+                                 CefRefPtr<CefV8Context> context) = 0;
+
+  ///
+  // Call from CefRenderProcessHandler::OnProcessMessageReceived. Returns true
+  // if the message is handled by this router or false otherwise.
+  ///
+  virtual bool OnProcessMessageReceived(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefProcessId source_process,
+      CefRefPtr<CefProcessMessage> message) = 0;
+
+ protected:
+  // Protect against accidental deletion of this object.
+  friend class base::RefCountedThreadSafe<CefMessageRouterRendererSide>;
+  virtual ~CefMessageRouterRendererSide() {}
+};
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_
diff --git a/src/include/wrapper/cef_resource_manager.h b/src/include/wrapper/cef_resource_manager.h
new file mode 100644
index 0000000..91190b7
--- /dev/null
+++ b/src/include/wrapper/cef_resource_manager.h
@@ -0,0 +1,371 @@
+// Copyright (c) 2015 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_RESOURCE_MANAGER_H_
+#define CEF_INCLUDE_WRAPPER_CEF_RESOURCE_MANAGER_H_
+#pragma once
+
+#include <list>
+
+#include "include/base/cef_macros.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/base/cef_weak_ptr.h"
+#include "include/cef_request_handler.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+
+///
+// Class for managing multiple resource providers. For each resource request
+// providers will be called in order and have the option to (a) handle the
+// request by returning a CefResourceHandler, (b) pass the request to the next
+// provider in order, or (c) stop handling the request. See comments on the
+// Request object for additional usage information. The methods of this class
+// may be called on any browser process thread unless otherwise indicated.
+///
+class CefResourceManager
+    : public base::RefCountedThreadSafe<CefResourceManager,
+                                        CefDeleteOnIOThread> {
+ public:
+  ///
+  // Provides an opportunity to modify |url| before it is passed to a provider.
+  // For example, the implementation could rewrite |url| to include a default
+  // file extension. |url| will be fully qualified and may contain query or
+  // fragment components.
+  ///
+  typedef base::Callback<std::string(const std::string& /*url*/)> UrlFilter;
+
+  ///
+  // Used to resolve mime types for URLs, usually based on the file extension.
+  // |url| will be fully qualified and may contain query or fragment components.
+  ///
+  typedef base::Callback<std::string(const std::string& /*url*/)>
+      MimeTypeResolver;
+
+ private:
+  // Values that stay with a request as it moves between providers.
+  struct RequestParams {
+    std::string url_;
+    CefRefPtr<CefBrowser> browser_;
+    CefRefPtr<CefFrame> frame_;
+    CefRefPtr<CefRequest> request_;
+    UrlFilter url_filter_;
+    MimeTypeResolver mime_type_resolver_;
+  };
+
+  // Values that are associated with the pending request only.
+  struct RequestState;
+
+ public:
+  ///
+  // Object representing a request. Each request object is used for a single
+  // call to Provider::OnRequest and will become detached (meaning the callbacks
+  // will no longer trigger) after Request::Continue or Request::Stop is called.
+  // A request passed to Provider::OnRequestCanceled will already have been
+  // detached. The methods of this class may be called on any browser process
+  // thread.
+  ///
+  class Request : public base::RefCountedThreadSafe<Request> {
+   public:
+    ///
+    // Returns the URL associated with this request. The returned value will be
+    // fully qualified but will not contain query or fragment components. It
+    // will already have been passed through the URL filter.
+    ///
+    std::string url() const { return params_.url_; }
+
+    ///
+    // Returns the CefBrowser associated with this request.
+    ///
+    CefRefPtr<CefBrowser> browser() const { return params_.browser_; }
+
+    ///
+    // Returns the CefFrame associated with this request.
+    ///
+    CefRefPtr<CefFrame> frame() const { return params_.frame_; }
+
+    ///
+    // Returns the CefRequest associated with this request.
+    ///
+    CefRefPtr<CefRequest> request() const { return params_.request_; }
+
+    ///
+    // Returns the current URL filter.
+    ///
+    const CefResourceManager::UrlFilter& url_filter() const {
+      return params_.url_filter_;
+    }
+
+    ///
+    // Returns the current mime type resolver.
+    ///
+    const CefResourceManager::MimeTypeResolver& mime_type_resolver() const {
+      return params_.mime_type_resolver_;
+    }
+
+    ///
+    // Continue handling the request. If |handler| is non-NULL then no
+    // additional providers will be called and the |handler| value will be
+    // returned via CefResourceManager::GetResourceHandler. If |handler| is NULL
+    // then the next provider in order, if any, will be called. If there are no
+    // additional providers then NULL will be returned via CefResourceManager::
+    // GetResourceHandler.
+    ///
+    void Continue(CefRefPtr<CefResourceHandler> handler);
+
+    ///
+    // Stop handling the request. No additional providers will be called and
+    // NULL will be returned via CefResourceManager::GetResourceHandler.
+    ///
+    void Stop();
+
+   private:
+    // Only allow deletion via scoped_refptr.
+    friend class base::RefCountedThreadSafe<Request>;
+
+    friend class CefResourceManager;
+
+    // The below methods are called on the browser process IO thread.
+
+    explicit Request(scoped_ptr<RequestState> state);
+
+    scoped_ptr<RequestState> SendRequest();
+    bool HasState();
+
+    static void ContinueOnIOThread(scoped_ptr<RequestState> state,
+                                   CefRefPtr<CefResourceHandler> handler);
+    static void StopOnIOThread(scoped_ptr<RequestState> state);
+
+    // Will be non-NULL while the request is pending. Only accessed on the
+    // browser process IO thread.
+    scoped_ptr<RequestState> state_;
+
+    // Params that stay with this request object. Safe to access on any thread.
+    RequestParams params_;
+
+    DISALLOW_COPY_AND_ASSIGN(Request);
+  };
+
+  typedef std::list<scoped_refptr<Request>> RequestList;
+
+  ///
+  // Interface implemented by resource providers. A provider may be created on
+  // any thread but the methods will be called on, and the object will be
+  // destroyed on, the browser process IO thread.
+  ///
+  class Provider {
+   public:
+    ///
+    // Called to handle a request. If the provider knows immediately that it
+    // will not handle the request return false. Otherwise, return true and call
+    // Request::Continue or Request::Stop either in this method or
+    // asynchronously to indicate completion. See comments on Request for
+    // additional usage information.
+    ///
+    virtual bool OnRequest(scoped_refptr<Request> request) = 0;
+
+    ///
+    // Called when a request has been canceled. It is still safe to dereference
+    // |request| but any calls to Request::Continue or Request::Stop will be
+    // ignored.
+    ///
+    virtual void OnRequestCanceled(scoped_refptr<Request> request) {}
+
+    virtual ~Provider() {}
+  };
+
+  CefResourceManager();
+
+  ///
+  // Add a provider that maps requests for |url| to |content|. |url| should be
+  // fully qualified but not include a query or fragment component. If
+  // |mime_type| is empty the MimeTypeResolver will be used. See comments on
+  // AddProvider for usage of the |order| and |identifier| parameters.
+  ///
+  void AddContentProvider(const std::string& url,
+                          const std::string& content,
+                          const std::string& mime_type,
+                          int order,
+                          const std::string& identifier);
+
+  ///
+  // Add a provider that maps requests that start with |url_path| to files under
+  // |directory_path|. |url_path| should include an origin and optional path
+  // component only. Files will be loaded when a matching URL is requested.
+  // See comments on AddProvider for usage of the |order| and |identifier|
+  // parameters.
+  ///
+  void AddDirectoryProvider(const std::string& url_path,
+                            const std::string& directory_path,
+                            int order,
+                            const std::string& identifier);
+
+  ///
+  // Add a provider that maps requests that start with |url_path| to files
+  // stored in the archive file at |archive_path|. |url_path| should include an
+  // origin and optional path component only. The archive file will be loaded
+  // when a matching URL is requested for the first time. See comments on
+  // AddProvider for usage of the |order| and |identifier| parameters.
+  ///
+  void AddArchiveProvider(const std::string& url_path,
+                          const std::string& archive_path,
+                          const std::string& password,
+                          int order,
+                          const std::string& identifier);
+
+  ///
+  // Add a provider. This object takes ownership of |provider|. Providers will
+  // be called in ascending order based on the |order| value. Multiple providers
+  // sharing the same |order| value will be called in the order that they were
+  // added. The |identifier| value, which does not need to be unique, can be
+  // used to remove the provider at a later time.
+  ///
+  void AddProvider(Provider* provider,
+                   int order,
+                   const std::string& identifier);
+
+  ///
+  // Remove all providers with the specified |identifier| value. If any removed
+  // providers have pending requests the Provider::OnRequestCancel method will
+  // be called. The removed providers may be deleted immediately or at a later
+  // time.
+  ///
+  void RemoveProviders(const std::string& identifier);
+
+  ///
+  // Remove all providers. If any removed providers have pending requests the
+  // Provider::OnRequestCancel method will be called. The removed providers may
+  // be deleted immediately or at a later time.
+  ///
+  void RemoveAllProviders();
+
+  ///
+  // Set the url filter. If not set the default no-op filter will be used.
+  // Changes to this value will not affect currently pending requests.
+  ///
+  void SetUrlFilter(const UrlFilter& filter);
+
+  ///
+  // Set the mime type resolver. If not set the default resolver will be used.
+  // Changes to this value will not affect currently pending requests.
+  ///
+  void SetMimeTypeResolver(const MimeTypeResolver& resolver);
+
+  // The below methods should be called from other CEF handlers. They must be
+  // called exactly as documented for the manager to function correctly.
+
+  ///
+  // Called from CefRequestHandler::OnBeforeResourceLoad on the browser process
+  // IO thread.
+  ///
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback);
+
+  ///
+  // Called from CefRequestHandler::GetResourceHandler on the browser process
+  // IO thread.
+  ///
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request);
+
+ private:
+  // Only allow deletion via scoped_refptr.
+  friend struct CefDeleteOnThread<TID_IO>;
+  friend class base::RefCountedThreadSafe<CefResourceManager,
+                                          CefDeleteOnIOThread>;
+
+  ~CefResourceManager();
+
+  // Provider and associated information.
+  struct ProviderEntry;
+  typedef std::list<ProviderEntry*> ProviderEntryList;
+
+  // Values associated with the pending request only. Ownership will be passed
+  // between requests and the resource manager as request handling proceeds.
+  struct RequestState {
+    ~RequestState();
+
+    base::WeakPtr<CefResourceManager> manager_;
+
+    // Callback to execute once request handling is complete.
+    CefRefPtr<CefRequestCallback> callback_;
+
+    // Position of the currently associated ProviderEntry in the |providers_|
+    // list.
+    ProviderEntryList::iterator current_entry_pos_;
+
+    // Position of this request object in the currently associated
+    // ProviderEntry's |pending_requests_| list.
+    RequestList::iterator current_request_pos_;
+
+    // Params that will be copied to each request object.
+    RequestParams params_;
+  };
+
+  // Methods that manage request state between requests. Called on the browser
+  // process IO thread.
+  bool SendRequest(scoped_ptr<RequestState> state);
+  void ContinueRequest(scoped_ptr<RequestState> state,
+                       CefRefPtr<CefResourceHandler> handler);
+  void StopRequest(scoped_ptr<RequestState> state);
+  bool IncrementProvider(RequestState* state);
+  void DetachRequestFromProvider(RequestState* state);
+  void GetNextValidProvider(ProviderEntryList::iterator& iterator);
+  void DeleteProvider(ProviderEntryList::iterator& iterator, bool stop);
+
+  // The below members are only accessed on the browser process IO thread.
+
+  // List of providers including additional associated information.
+  ProviderEntryList providers_;
+
+  // Map of response ID to pending CefResourceHandler object.
+  typedef std::map<uint64, CefRefPtr<CefResourceHandler>> PendingHandlersMap;
+  PendingHandlersMap pending_handlers_;
+
+  UrlFilter url_filter_;
+  MimeTypeResolver mime_type_resolver_;
+
+  // Must be the last member. Created and accessed on the IO thread.
+  scoped_ptr<base::WeakPtrFactory<CefResourceManager>> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefResourceManager);
+};
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_RESOURCE_MANAGER_H_
diff --git a/src/include/wrapper/cef_scoped_temp_dir.h b/src/include/wrapper/cef_scoped_temp_dir.h
new file mode 100644
index 0000000..f40fa7d
--- /dev/null
+++ b/src/include/wrapper/cef_scoped_temp_dir.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2016 Marshall A. Greenblatt. Portions copyright (c) 2011
+// Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_SCOPED_TEMP_DIR_H_
+#define CEF_INCLUDE_SCOPED_TEMP_DIR_H_
+#pragma once
+
+#include "include/base/cef_build.h"
+#include "include/base/cef_macros.h"
+#include "include/cef_base.h"
+
+///
+// An object representing a temporary / scratch directory that should be cleaned
+// up (recursively) when this object goes out of scope.  Note that since
+// deletion occurs during the destructor, no further error handling is possible
+// if the directory fails to be deleted.  As a result, deletion is not
+// guaranteed by this class.
+//
+// Multiple calls to the methods which establish a temporary directory
+// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have
+// intervening calls to Delete or Take, or the calls will fail.
+///
+class CefScopedTempDir {
+ public:
+  ///
+  // No directory is owned/created initially.
+  ///
+  CefScopedTempDir();
+
+  ///
+  // Recursively delete path.
+  ///
+  ~CefScopedTempDir();
+
+  ///
+  // Creates a unique directory in TempPath, and takes ownership of it.
+  // See file_util::CreateNewTemporaryDirectory.
+  ///
+  bool CreateUniqueTempDir() WARN_UNUSED_RESULT;
+
+  ///
+  // Creates a unique directory under a given path, and takes ownership of it.
+  ///
+  bool CreateUniqueTempDirUnderPath(const CefString& path) WARN_UNUSED_RESULT;
+
+  ///
+  // Takes ownership of directory at |path|, creating it if necessary.
+  // Don't call multiple times unless Take() has been called first.
+  ///
+  bool Set(const CefString& path) WARN_UNUSED_RESULT;
+
+  ///
+  // Deletes the temporary directory wrapped by this object.
+  ///
+  bool Delete() WARN_UNUSED_RESULT;
+
+  ///
+  // Caller takes ownership of the temporary directory so it won't be destroyed
+  // when this object goes out of scope.
+  ///
+  CefString Take();
+
+  ///
+  // Returns the path to the created directory. Call one of the
+  // CreateUniqueTempDir* methods before getting the path.
+  ///
+  const CefString& GetPath() const;
+
+  ///
+  // Returns true if path_ is empty.
+  ///
+  bool IsEmpty() const;
+
+  ///
+  // Returns true if path_ is non-empty and exists.
+  ///
+  bool IsValid() const;
+
+ private:
+  CefString path_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefScopedTempDir);
+};
+
+#endif  // CEF_INCLUDE_SCOPED_TEMP_DIR_H_
diff --git a/src/include/wrapper/cef_stream_resource_handler.h b/src/include/wrapper/cef_stream_resource_handler.h
new file mode 100644
index 0000000..130e20b
--- /dev/null
+++ b/src/include/wrapper/cef_stream_resource_handler.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_STREAM_RESOURCE_HANDLER_H_
+#define CEF_INCLUDE_WRAPPER_CEF_STREAM_RESOURCE_HANDLER_H_
+#pragma once
+
+#include "include/base/cef_macros.h"
+#include "include/cef_resource_handler.h"
+#include "include/cef_response.h"
+#include "include/cef_stream.h"
+
+///
+// Implementation of the CefResourceHandler class for reading from a CefStream.
+///
+class CefStreamResourceHandler : public CefResourceHandler {
+ public:
+  ///
+  // Create a new object with default response values.
+  ///
+  CefStreamResourceHandler(const CefString& mime_type,
+                           CefRefPtr<CefStreamReader> stream);
+  ///
+  // Create a new object with explicit response values.
+  ///
+  CefStreamResourceHandler(int status_code,
+                           const CefString& status_text,
+                           const CefString& mime_type,
+                           CefResponse::HeaderMap header_map,
+                           CefRefPtr<CefStreamReader> stream);
+
+  // CefResourceHandler methods.
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) OVERRIDE;
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) OVERRIDE;
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) OVERRIDE;
+  void Cancel() OVERRIDE;
+
+ private:
+  const int status_code_;
+  const CefString status_text_;
+  const CefString mime_type_;
+  const CefResponse::HeaderMap header_map_;
+  const CefRefPtr<CefStreamReader> stream_;
+
+  IMPLEMENT_REFCOUNTING(CefStreamResourceHandler);
+  DISALLOW_COPY_AND_ASSIGN(CefStreamResourceHandler);
+};
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_STREAM_RESOURCE_HANDLER_H_
diff --git a/src/include/wrapper/cef_xml_object.h b/src/include/wrapper/cef_xml_object.h
new file mode 100644
index 0000000..9315adc
--- /dev/null
+++ b/src/include/wrapper/cef_xml_object.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_XML_OBJECT_H_
+#define CEF_INCLUDE_WRAPPER_CEF_XML_OBJECT_H_
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "include/base/cef_lock.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/cef_base.h"
+#include "include/cef_xml_reader.h"
+
+class CefStreamReader;
+
+///
+// Thread safe class for representing XML data as a structured object. This
+// class should not be used with large XML documents because all data will be
+// resident in memory at the same time. This implementation supports a
+// restricted set of XML features:
+// <pre>
+// (1) Processing instructions, whitespace and comments are ignored.
+// (2) Elements and attributes must always be referenced using the fully
+//     qualified name (ie, namespace:localname).
+// (3) Empty elements (<a/>) and elements with zero-length values (<a></a>)
+//     are considered the same.
+// (4) Element nodes are considered part of a value if:
+//     (a) The element node follows a non-element node at the same depth
+//         (see 5), or
+//     (b) The element node does not have a namespace and the parent node does.
+// (5) Mixed node types at the same depth are combined into a single element
+//     value as follows:
+//     (a) All node values are concatenated to form a single string value.
+//     (b) Entity reference nodes are resolved to the corresponding entity
+//         value.
+//     (c) Element nodes are represented by their outer XML string.
+// </pre>
+///
+class CefXmlObject : public base::RefCountedThreadSafe<CefXmlObject> {
+ public:
+  typedef std::vector<CefRefPtr<CefXmlObject>> ObjectVector;
+  typedef std::map<CefString, CefString> AttributeMap;
+
+  ///
+  // Create a new object with the specified name. An object name must always be
+  // at least one character long.
+  ///
+  explicit CefXmlObject(const CefString& name);
+
+  ///
+  // Load the contents of the specified XML stream into this object.  The
+  // existing children and attributes, if any, will first be cleared.
+  ///
+  bool Load(CefRefPtr<CefStreamReader> stream,
+            CefXmlReader::EncodingType encodingType,
+            const CefString& URI,
+            CefString* loadError);
+
+  ///
+  // Set the name, children and attributes of this object to a duplicate of the
+  // specified object's contents. The existing children and attributes, if any,
+  // will first be cleared.
+  ///
+  void Set(CefRefPtr<CefXmlObject> object);
+
+  ///
+  // Append a duplicate of the children and attributes of the specified object
+  // to this object. If |overwriteAttributes| is true then any attributes in
+  // this object that also exist in the specified object will be overwritten
+  // with the new values. The name of this object is not changed.
+  ///
+  void Append(CefRefPtr<CefXmlObject> object, bool overwriteAttributes);
+
+  ///
+  // Return a new object with the same name, children and attributes as this
+  // object. The parent of the new object will be NULL.
+  ///
+  CefRefPtr<CefXmlObject> Duplicate();
+
+  ///
+  // Clears this object's children and attributes. The name and parenting of
+  // this object are not changed.
+  ///
+  void Clear();
+
+  ///
+  // Access the object's name. An object name must always be at least one
+  // character long.
+  ///
+  CefString GetName();
+  bool SetName(const CefString& name);
+
+  ///
+  // Access the object's parent. The parent can be NULL if this object has not
+  // been added as the child on another object.
+  ///
+  bool HasParent();
+  CefRefPtr<CefXmlObject> GetParent();
+
+  ///
+  // Access the object's value. An object cannot have a value if it also has
+  // children. Attempting to set the value while children exist will fail.
+  ///
+  bool HasValue();
+  CefString GetValue();
+  bool SetValue(const CefString& value);
+
+  ///
+  // Access the object's attributes. Attributes must have unique names.
+  ///
+  bool HasAttributes();
+  size_t GetAttributeCount();
+  bool HasAttribute(const CefString& name);
+  CefString GetAttributeValue(const CefString& name);
+  bool SetAttributeValue(const CefString& name, const CefString& value);
+  size_t GetAttributes(AttributeMap& attributes);
+  void ClearAttributes();
+
+  ///
+  // Access the object's children. Each object can only have one parent so
+  // attempting to add an object that already has a parent will fail. Removing a
+  // child will set the child's parent to NULL. Adding a child will set the
+  // child's parent to this object. This object's value, if any, will be cleared
+  // if a child is added.
+  ///
+  bool HasChildren();
+  size_t GetChildCount();
+  bool HasChild(CefRefPtr<CefXmlObject> child);
+  bool AddChild(CefRefPtr<CefXmlObject> child);
+  bool RemoveChild(CefRefPtr<CefXmlObject> child);
+  size_t GetChildren(ObjectVector& children);
+  void ClearChildren();
+
+  ///
+  // Find the first child with the specified name.
+  ///
+  CefRefPtr<CefXmlObject> FindChild(const CefString& name);
+
+  ///
+  // Find all children with the specified name.
+  ///
+  size_t FindChildren(const CefString& name, ObjectVector& children);
+
+ private:
+  // Protect against accidental deletion of this object.
+  friend class base::RefCountedThreadSafe<CefXmlObject>;
+  ~CefXmlObject();
+
+  void SetParent(CefXmlObject* parent);
+
+  CefString name_;
+  CefXmlObject* parent_;
+  CefString value_;
+  AttributeMap attributes_;
+  ObjectVector children_;
+
+  base::Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefXmlObject);
+};
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_XML_OBJECT_H_
diff --git a/src/include/wrapper/cef_zip_archive.h b/src/include/wrapper/cef_zip_archive.h
new file mode 100644
index 0000000..9eb2812
--- /dev/null
+++ b/src/include/wrapper/cef_zip_archive.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// The contents of this file are only available to applications that link
+// against the libcef_dll_wrapper target.
+//
+
+#ifndef CEF_INCLUDE_WRAPPER_CEF_ZIP_ARCHIVE_H_
+#define CEF_INCLUDE_WRAPPER_CEF_ZIP_ARCHIVE_H_
+#pragma once
+
+#include <map>
+
+#include "include/base/cef_lock.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/cef_base.h"
+
+class CefStreamReader;
+
+///
+// Thread-safe class for accessing zip archive file contents. This class should
+// not be used with large archive files because all data will be resident in
+// memory at the same time. This implementation supports a restricted set of zip
+// archive features:
+// (1) All file names are stored and compared in lower case.
+// (2) File ordering from the original zip archive is not maintained. This
+//     means that files from the same folder may not be located together in the
+//     file content map.
+///
+class CefZipArchive : public base::RefCountedThreadSafe<CefZipArchive> {
+ public:
+  ///
+  // Class representing a file in the archive. Accessing the file data from
+  // multiple threads is safe provided a reference to the File object is kept.
+  ///
+  class File : public CefBaseRefCounted {
+   public:
+    ///
+    // Returns the read-only data contained in the file.
+    ///
+    virtual const unsigned char* GetData() const = 0;
+
+    ///
+    // Returns the size of the data in the file.
+    ///
+    virtual size_t GetDataSize() const = 0;
+
+    ///
+    // Returns a CefStreamReader object for streaming the contents of the file.
+    ///
+    virtual CefRefPtr<CefStreamReader> GetStreamReader() const = 0;
+  };
+
+  typedef std::map<CefString, CefRefPtr<File>> FileMap;
+
+  ///
+  // Create a new object.
+  ///
+  CefZipArchive();
+
+  ///
+  // Load the contents of the specified zip archive stream into this object.
+  // If the zip archive requires a password then provide it via |password|.
+  // If |overwriteExisting| is true then any files in this object that also
+  // exist in the specified archive will be replaced with the new files.
+  // Returns the number of files successfully loaded.
+  ///
+  size_t Load(CefRefPtr<CefStreamReader> stream,
+              const CefString& password,
+              bool overwriteExisting);
+
+  ///
+  // Clears the contents of this object.
+  ///
+  void Clear();
+
+  ///
+  // Returns the number of files in the archive.
+  ///
+  size_t GetFileCount() const;
+
+  ///
+  // Returns true if the specified file exists and has contents.
+  ///
+  bool HasFile(const CefString& fileName) const;
+
+  ///
+  // Returns the specified file.
+  ///
+  CefRefPtr<File> GetFile(const CefString& fileName) const;
+
+  ///
+  // Removes the specified file.
+  ///
+  bool RemoveFile(const CefString& fileName);
+
+  ///
+  // Returns the map of all files.
+  ///
+  size_t GetFiles(FileMap& map) const;
+
+ private:
+  // Protect against accidental deletion of this object.
+  friend class base::RefCountedThreadSafe<CefZipArchive>;
+  ~CefZipArchive();
+
+  FileMap contents_;
+
+  mutable base::Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefZipArchive);
+};
+
+#endif  // CEF_INCLUDE_WRAPPER_CEF_ZIP_ARCHIVE_H_
diff --git a/src/libcef/browser/audio_capturer.cc b/src/libcef/browser/audio_capturer.cc
new file mode 100644
index 0000000..3eed7a7
--- /dev/null
+++ b/src/libcef/browser/audio_capturer.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/audio_capturer.h"
+#include "libcef/browser/browser_host_impl.h"
+
+#include "components/mirroring/service/captured_audio_input.h"
+#include "content/public/browser/audio_loopback_stream_creator.h"
+#include "media/audio/audio_input_device.h"
+
+namespace {
+
+media::ChannelLayout TranslateChannelLayout(
+    cef_channel_layout_t channel_layout) {
+  // Verify that our enum matches Chromium's values. The enum values match
+  // between those enums and existing values don't ever change, so it's enough
+  // to check that there are no new ones added.
+  static_assert(
+      static_cast<int>(CEF_CHANNEL_LAYOUT_MAX) ==
+          static_cast<int>(media::CHANNEL_LAYOUT_MAX),
+      "cef_channel_layout_t must match the ChannelLayout enum in Chromium");
+  return static_cast<media::ChannelLayout>(channel_layout);
+}
+
+void StreamCreatorHelper(
+    content::WebContents* source_web_contents,
+    content::AudioLoopbackStreamCreator* audio_stream_creator,
+    mojo::PendingRemote<mirroring::mojom::AudioStreamCreatorClient> client,
+    const media::AudioParameters& params,
+    uint32_t total_segments) {
+  audio_stream_creator->CreateLoopbackStream(
+      source_web_contents, params, total_segments,
+      base::BindRepeating(
+          [](mojo::PendingRemote<mirroring::mojom::AudioStreamCreatorClient>
+                 client,
+             mojo::PendingRemote<media::mojom::AudioInputStream> stream,
+             mojo::PendingReceiver<media::mojom::AudioInputStreamClient>
+                 client_receiver,
+             media::mojom::ReadOnlyAudioDataPipePtr data_pipe) {
+            mojo::Remote<mirroring::mojom::AudioStreamCreatorClient>
+                audio_client(std::move(client));
+            audio_client->StreamCreated(
+                std::move(stream), std::move(client_receiver),
+                std::move(data_pipe), false /* initially_muted */);
+          },
+          base::Passed(&client)));
+}
+
+}  // namespace
+
+CefAudioCapturer::CefAudioCapturer(const CefAudioParameters& params,
+                                   CefRefPtr<CefBrowserHostImpl> browser,
+                                   CefRefPtr<CefAudioHandler> audio_handler)
+    : params_(params),
+      browser_(browser),
+      audio_handler_(audio_handler),
+      audio_stream_creator_(content::AudioLoopbackStreamCreator::
+                                CreateInProcessAudioLoopbackStreamCreator()) {
+  media::AudioParameters audio_params(
+      media::AudioParameters::AUDIO_PCM_LINEAR,
+      TranslateChannelLayout(params.channel_layout), params.sample_rate,
+      params.frames_per_buffer);
+
+  if (!audio_params.IsValid()) {
+    LOG(ERROR) << "Invalid audio parameters";
+    return;
+  }
+
+  DCHECK(browser_);
+  DCHECK(audio_handler_);
+  DCHECK(browser_->web_contents());
+
+  channels_ = audio_params.channels();
+  audio_input_device_ = new media::AudioInputDevice(
+      std::make_unique<mirroring::CapturedAudioInput>(base::BindRepeating(
+          &StreamCreatorHelper, base::Unretained(browser_->web_contents()),
+          base::Unretained(audio_stream_creator_.get()))),
+      media::AudioInputDevice::kLoopback);
+
+  audio_input_device_->Initialize(audio_params, this);
+  audio_input_device_->Start();
+}
+
+CefAudioCapturer::~CefAudioCapturer() {
+  StopStream();
+}
+
+void CefAudioCapturer::OnCaptureStarted() {
+  audio_handler_->OnAudioStreamStarted(browser_, params_, channels_);
+  DCHECK(!capturing_);
+  capturing_ = true;
+}
+
+void CefAudioCapturer::Capture(const media::AudioBus* source,
+                               base::TimeTicks audio_capture_time,
+                               double /*volume*/,
+                               bool /*key_pressed*/) {
+  const int channels = source->channels();
+  std::array<const float*, media::CHANNELS_MAX> data;
+  DCHECK(channels == channels_);
+  DCHECK(channels <= static_cast<int>(data.size()));
+  for (int c = 0; c < channels; ++c) {
+    data[c] = source->channel(c);
+  }
+  base::TimeDelta pts = audio_capture_time - base::TimeTicks::UnixEpoch();
+  audio_handler_->OnAudioStreamPacket(browser_, data.data(), source->frames(),
+                                      pts.InMilliseconds());
+}
+
+void CefAudioCapturer::OnCaptureError(const std::string& message) {
+  audio_handler_->OnAudioStreamError(browser_, message);
+  StopStream();
+}
+
+void CefAudioCapturer::StopStream() {
+  if (audio_input_device_)
+    audio_input_device_->Stop();
+  if (capturing_)
+    audio_handler_->OnAudioStreamStopped(browser_);
+
+  audio_input_device_ = nullptr;
+  capturing_ = false;
+}
\ No newline at end of file
diff --git a/src/libcef/browser/audio_capturer.h b/src/libcef/browser/audio_capturer.h
new file mode 100644
index 0000000..9801481
--- /dev/null
+++ b/src/libcef/browser/audio_capturer.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_AUDIO_CAPTURER_H_
+#define CEF_LIBCEF_BROWSER_AUDIO_CAPTURER_H_
+#pragma once
+
+#include "include/internal/cef_ptr.h"
+#include "include/internal/cef_types_wrappers.h"
+
+#include "media/base/audio_capturer_source.h"
+
+namespace content {
+class AudioLoopbackStreamCreator;
+}  // namespace content
+
+namespace media {
+class AudioInputDevice;
+}  // namespace media
+
+class CefAudioHandler;
+class CefBrowserHostImpl;
+
+class CefAudioCapturer : public media::AudioCapturerSource::CaptureCallback {
+ public:
+  CefAudioCapturer(const CefAudioParameters& params,
+                   CefRefPtr<CefBrowserHostImpl> browser,
+                   CefRefPtr<CefAudioHandler> audio_handler);
+  ~CefAudioCapturer() override;
+
+ private:
+  void OnCaptureStarted() override;
+  void Capture(const media::AudioBus* audio_source,
+               base::TimeTicks audio_capture_time,
+               double volume,
+               bool key_pressed) override;
+  void OnCaptureError(const std::string& message) override;
+  void OnCaptureMuted(bool is_muted) override {}
+
+  void StopStream();
+
+  CefAudioParameters params_;
+  CefRefPtr<CefBrowserHostImpl> browser_;
+  CefRefPtr<CefAudioHandler> audio_handler_;
+  std::unique_ptr<content::AudioLoopbackStreamCreator> audio_stream_creator_;
+  scoped_refptr<media::AudioInputDevice> audio_input_device_;
+  bool capturing_ = false;
+  int channels_ = 0;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_AUDIO_CAPTURER_H_
\ No newline at end of file
diff --git a/src/libcef/browser/browser_context.cc b/src/libcef/browser/browser_context.cc
new file mode 100644
index 0000000..2a81bca
--- /dev/null
+++ b/src/libcef/browser/browser_context.cc
@@ -0,0 +1,817 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/browser_context.h"
+
+#include <map>
+#include <utility>
+
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/download_manager_delegate.h"
+#include "libcef/browser/extensions/extension_system.h"
+#include "libcef/browser/media_router/media_router_manager.h"
+#include "libcef/browser/prefs/browser_prefs.h"
+#include "libcef/browser/request_context_impl.h"
+#include "libcef/browser/ssl_host_state_delegate.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "chrome/browser/font_family_cache.h"
+#include "chrome/browser/plugins/chrome_plugin_service_filter.h"
+#include "chrome/browser/profiles/profile_key.h"
+#include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
+#include "chrome/common/pref_names.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/core/simple_dependency_manager.h"
+#include "components/keyed_service/core/simple_key_map.h"
+#include "components/prefs/pref_service.h"
+#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "components/user_prefs/user_prefs.h"
+#include "components/visitedlink/browser/visitedlink_event_listener.h"
+#include "components/visitedlink/browser/visitedlink_writer.h"
+#include "components/zoom/zoom_event_manager.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/storage_partition.h"
+#include "extensions/browser/extension_protocols.h"
+#include "extensions/browser/process_manager.h"
+#include "extensions/common/constants.h"
+#include "net/proxy_resolution/proxy_config_service.h"
+#include "services/network/public/mojom/cors_origin_pattern.mojom.h"
+
+using content::BrowserThread;
+
+namespace {
+
+// Manages the global list of Impl instances.
+class ImplManager {
+ public:
+  typedef std::vector<CefBrowserContext*> Vector;
+
+  ImplManager() {}
+  ~ImplManager() {
+    DCHECK(all_.empty());
+    DCHECK(map_.empty());
+  }
+
+  void AddImpl(CefBrowserContext* impl) {
+    CEF_REQUIRE_UIT();
+    DCHECK(!IsValidImpl(impl));
+    all_.push_back(impl);
+  }
+
+  void RemoveImpl(CefBrowserContext* impl, const base::FilePath& path) {
+    CEF_REQUIRE_UIT();
+
+    Vector::iterator it = GetImplPos(impl);
+    DCHECK(it != all_.end());
+    all_.erase(it);
+
+    if (!path.empty()) {
+      PathMap::iterator it = map_.find(path);
+      DCHECK(it != map_.end());
+      if (it != map_.end())
+        map_.erase(it);
+    }
+  }
+
+  bool IsValidImpl(const CefBrowserContext* impl) {
+    CEF_REQUIRE_UIT();
+    return GetImplPos(impl) != all_.end();
+  }
+
+  CefBrowserContext* GetImplForIDs(int render_process_id,
+                                   int render_frame_id,
+                                   int frame_tree_node_id,
+                                   bool require_frame_match) {
+    CEF_REQUIRE_UIT();
+    for (const auto& context : all_) {
+      if (context->IsAssociatedContext(render_process_id, render_frame_id,
+                                       frame_tree_node_id,
+                                       require_frame_match)) {
+        return context;
+      }
+    }
+    return nullptr;
+  }
+
+  CefBrowserContext* GetImplForContext(const content::BrowserContext* context) {
+    CEF_REQUIRE_UIT();
+    if (!context)
+      return nullptr;
+
+    Vector::iterator it = all_.begin();
+    for (; it != all_.end(); ++it) {
+      if (*it == context)
+        return *it;
+    }
+    return nullptr;
+  }
+
+  void SetImplPath(CefBrowserContext* impl, const base::FilePath& path) {
+    CEF_REQUIRE_UIT();
+    DCHECK(!path.empty());
+    DCHECK(IsValidImpl(impl));
+    DCHECK(GetImplForPath(path) == nullptr);
+    map_.insert(std::make_pair(path, impl));
+  }
+
+  CefBrowserContext* GetImplForPath(const base::FilePath& path) {
+    CEF_REQUIRE_UIT();
+    DCHECK(!path.empty());
+    PathMap::const_iterator it = map_.find(path);
+    if (it != map_.end())
+      return it->second;
+    return nullptr;
+  }
+
+  const Vector GetAllImpl() const { return all_; }
+
+ private:
+  Vector::iterator GetImplPos(const CefBrowserContext* impl) {
+    Vector::iterator it = all_.begin();
+    for (; it != all_.end(); ++it) {
+      if (*it == impl)
+        return it;
+    }
+    return all_.end();
+  }
+
+  typedef std::map<base::FilePath, CefBrowserContext*> PathMap;
+  PathMap map_;
+
+  Vector all_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImplManager);
+};
+
+#if DCHECK_IS_ON()
+// Because of DCHECK()s in the object destructor.
+base::LazyInstance<ImplManager>::DestructorAtExit g_manager =
+    LAZY_INSTANCE_INITIALIZER;
+#else
+base::LazyInstance<ImplManager>::Leaky g_manager = LAZY_INSTANCE_INITIALIZER;
+#endif
+
+CefBrowserContext* GetSelf(base::WeakPtr<CefBrowserContext> self) {
+  CEF_REQUIRE_UIT();
+  return self.get();
+}
+
+}  // namespace
+
+// Creates and manages VisitedLinkEventListener objects for each
+// CefBrowserContext sharing the same VisitedLinkWriter.
+class CefVisitedLinkListener : public visitedlink::VisitedLinkWriter::Listener {
+ public:
+  CefVisitedLinkListener() { DCHECK(listener_map_.empty()); }
+
+  void CreateListenerForContext(const CefBrowserContext* context) {
+    CEF_REQUIRE_UIT();
+    auto listener = std::make_unique<visitedlink::VisitedLinkEventListener>(
+        const_cast<CefBrowserContext*>(context));
+    listener_map_.insert(std::make_pair(context, std::move(listener)));
+  }
+
+  void RemoveListenerForContext(const CefBrowserContext* context) {
+    CEF_REQUIRE_UIT();
+    ListenerMap::iterator it = listener_map_.find(context);
+    DCHECK(it != listener_map_.end());
+    listener_map_.erase(it);
+  }
+
+  // visitedlink::VisitedLinkWriter::Listener methods.
+
+  void NewTable(base::ReadOnlySharedMemoryRegion* table_region) override {
+    CEF_REQUIRE_UIT();
+    ListenerMap::iterator it = listener_map_.begin();
+    for (; it != listener_map_.end(); ++it)
+      it->second->NewTable(table_region);
+  }
+
+  void Add(visitedlink::VisitedLinkCommon::Fingerprint fingerprint) override {
+    CEF_REQUIRE_UIT();
+    ListenerMap::iterator it = listener_map_.begin();
+    for (; it != listener_map_.end(); ++it)
+      it->second->Add(fingerprint);
+  }
+
+  void Reset(bool invalidate_hashes) override {
+    CEF_REQUIRE_UIT();
+    ListenerMap::iterator it = listener_map_.begin();
+    for (; it != listener_map_.end(); ++it)
+      it->second->Reset(invalidate_hashes);
+  }
+
+ private:
+  // Map of CefBrowserContext to the associated VisitedLinkEventListener.
+  typedef std::map<const CefBrowserContext*,
+                   std::unique_ptr<visitedlink::VisitedLinkEventListener>>
+      ListenerMap;
+  ListenerMap listener_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefVisitedLinkListener);
+};
+
+CefBrowserContext::CefBrowserContext(const CefRequestContextSettings& settings)
+    : settings_(settings), weak_ptr_factory_(this) {
+  g_manager.Get().AddImpl(this);
+  getter_ = base::BindRepeating(GetSelf, weak_ptr_factory_.GetWeakPtr());
+}
+
+CefBrowserContext::~CefBrowserContext() {
+  CEF_REQUIRE_UIT();
+
+  // No CefRequestContext should be referencing this object any longer.
+  DCHECK(request_context_set_.empty());
+
+  // Unregister the context first to avoid re-entrancy during shutdown.
+  g_manager.Get().RemoveImpl(this, cache_path_);
+
+  // Destroy objects that may hold references to the MediaRouter.
+  media_router_manager_.reset();
+
+  // Send notifications to clean up objects associated with this Profile.
+  MaybeSendDestroyedNotification();
+
+  ChromePluginServiceFilter::GetInstance()->UnregisterProfile(this);
+
+  // Remove any BrowserContextKeyedServiceFactory associations. This must be
+  // called before the ProxyService owned by CefBrowserContext is destroyed.
+  // The SimpleDependencyManager should always be passed after the
+  // BrowserContextDependencyManager. This is because the KeyedService instances
+  // in the BrowserContextDependencyManager's dependency graph can depend on the
+  // ones in the SimpleDependencyManager's graph.
+  DependencyManager::PerformInterlockedTwoPhaseShutdown(
+      BrowserContextDependencyManager::GetInstance(), this,
+      SimpleDependencyManager::GetInstance(), key_.get());
+
+  key_.reset();
+  SimpleKeyMap::GetInstance()->Dissociate(this);
+
+  // Shuts down the storage partitions associated with this browser context.
+  // This must be called before the browser context is actually destroyed
+  // and before a clean-up task for its corresponding IO thread residents
+  // (e.g. ResourceContext) is posted, so that the classes that hung on
+  // StoragePartition can have time to do necessary cleanups on IO thread.
+  ShutdownStoragePartitions();
+
+  if (resource_context_.get()) {
+    // Destruction of the ResourceContext will trigger destruction of all
+    // associated network requests.
+    content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE,
+                                       resource_context_.release());
+  }
+
+  visitedlink_listener_->RemoveListenerForContext(this);
+
+  // The FontFamilyCache references the ProxyService so delete it before the
+  // ProxyService is deleted.
+  SetUserData(&kFontFamilyCacheKey, nullptr);
+
+  pref_proxy_config_tracker_->DetachFromPrefService();
+
+  if (host_content_settings_map_)
+    host_content_settings_map_->ShutdownOnUIThread();
+
+  // Delete the download manager delegate here because otherwise we'll crash
+  // when it's accessed from the content::BrowserContext destructor.
+  if (download_manager_delegate_)
+    download_manager_delegate_.reset(nullptr);
+}
+
+void CefBrowserContext::Initialize() {
+  cache_path_ = base::FilePath(CefString(&settings_.cache_path));
+
+  if (!cache_path_.empty())
+    g_manager.Get().SetImplPath(this, cache_path_);
+
+  if (!!settings_.persist_session_cookies) {
+    set_should_persist_session_cookies(true);
+  }
+
+  key_ = std::make_unique<ProfileKey>(cache_path_);
+  SimpleKeyMap::GetInstance()->Associate(this, key_.get());
+
+  // Initialize the PrefService object.
+  pref_service_ = browser_prefs::CreatePrefService(
+      this, cache_path_, !!settings_.persist_user_preferences);
+
+  content::BrowserContext::Initialize(this, cache_path_);
+
+  resource_context_.reset(new CefResourceContext(IsOffTheRecord()));
+
+  // This must be called before creating any services to avoid hitting
+  // DependencyManager::AssertContextWasntDestroyed when creating/destroying
+  // multiple browser contexts (due to pointer address reuse).
+  BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices(
+      this);
+
+  const bool extensions_enabled = extensions::ExtensionsEnabled();
+  if (extensions_enabled) {
+    // Create the custom ExtensionSystem first because other KeyedServices
+    // depend on it.
+    extension_system_ = static_cast<extensions::CefExtensionSystem*>(
+        extensions::ExtensionSystem::Get(this));
+    extension_system_->InitForRegularProfile(true);
+
+    // Make sure the ProcessManager is created so that it receives extension
+    // load notifications. This is necessary for the proper initialization of
+    // background/event pages.
+    extensions::ProcessManager::Get(this);
+  }
+
+  // Initialize visited links management.
+  base::FilePath visited_link_path;
+  if (!cache_path_.empty())
+    visited_link_path = cache_path_.Append(FILE_PATH_LITERAL("Visited Links"));
+  visitedlink_listener_ = new CefVisitedLinkListener;
+  visitedlink_master_.reset(new visitedlink::VisitedLinkWriter(
+      visitedlink_listener_, this, !visited_link_path.empty(), false,
+      visited_link_path, 0));
+  visitedlink_listener_->CreateListenerForContext(this);
+  visitedlink_master_->Init();
+
+  // Initialize proxy configuration tracker.
+  pref_proxy_config_tracker_.reset(new PrefProxyConfigTrackerImpl(
+      GetPrefs(), base::CreateSingleThreadTaskRunner({BrowserThread::IO})));
+
+  // Spell checking support and possibly other subsystems retrieve the
+  // PrefService associated with a BrowserContext via UserPrefs::Get().
+  PrefService* pref_service = GetPrefs();
+  DCHECK(pref_service);
+  user_prefs::UserPrefs::Set(this, pref_service);
+  key_->SetPrefs(pref_service);
+
+  if (extensions_enabled)
+    extension_system_->Init();
+
+  ChromePluginServiceFilter::GetInstance()->RegisterProfile(this);
+}
+
+void CefBrowserContext::AddCefRequestContext(CefRequestContextImpl* context) {
+  CEF_REQUIRE_UIT();
+  request_context_set_.insert(context);
+}
+
+void CefBrowserContext::RemoveCefRequestContext(
+    CefRequestContextImpl* context) {
+  CEF_REQUIRE_UIT();
+
+  if (extensions::ExtensionsEnabled()) {
+    extension_system()->OnRequestContextDeleted(context);
+  }
+
+  request_context_set_.erase(context);
+
+  // Delete ourselves when the reference count reaches zero.
+  if (request_context_set_.empty())
+    delete this;
+}
+
+// static
+CefBrowserContext* CefBrowserContext::GetForCachePath(
+    const base::FilePath& cache_path) {
+  return g_manager.Get().GetImplForPath(cache_path);
+}
+
+// static
+CefBrowserContext* CefBrowserContext::GetForIDs(int render_process_id,
+                                                int render_frame_id,
+                                                int frame_tree_node_id,
+                                                bool require_frame_match) {
+  return g_manager.Get().GetImplForIDs(render_process_id, render_frame_id,
+                                       frame_tree_node_id, require_frame_match);
+}
+
+// static
+CefBrowserContext* CefBrowserContext::GetForContext(
+    content::BrowserContext* context) {
+  return g_manager.Get().GetImplForContext(context);
+}
+
+// static
+std::vector<CefBrowserContext*> CefBrowserContext::GetAll() {
+  return g_manager.Get().GetAllImpl();
+}
+
+content::ResourceContext* CefBrowserContext::GetResourceContext() {
+  return resource_context_.get();
+}
+
+content::ClientHintsControllerDelegate*
+CefBrowserContext::GetClientHintsControllerDelegate() {
+  return nullptr;
+}
+
+void CefBrowserContext::SetCorsOriginAccessListForOrigin(
+    const url::Origin& source_origin,
+    std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns,
+    std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
+    base::OnceClosure closure) {
+  // This method is called for Extension support.
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(closure));
+}
+
+ChromeZoomLevelPrefs* CefBrowserContext::GetZoomLevelPrefs() {
+  return static_cast<ChromeZoomLevelPrefs*>(
+      GetStoragePartition(this, nullptr)->GetZoomLevelDelegate());
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+CefBrowserContext::GetURLLoaderFactory() {
+  return GetDefaultStoragePartition(this)
+      ->GetURLLoaderFactoryForBrowserProcess();
+}
+
+base::FilePath CefBrowserContext::GetPath() {
+  return cache_path_;
+}
+
+base::FilePath CefBrowserContext::GetPath() const {
+  return cache_path_;
+}
+
+std::unique_ptr<content::ZoomLevelDelegate>
+CefBrowserContext::CreateZoomLevelDelegate(
+    const base::FilePath& partition_path) {
+  if (cache_path_.empty())
+    return std::unique_ptr<content::ZoomLevelDelegate>();
+
+  return base::WrapUnique(new ChromeZoomLevelPrefs(
+      GetPrefs(), cache_path_, partition_path,
+      zoom::ZoomEventManager::GetForBrowserContext(this)->GetWeakPtr()));
+}
+
+bool CefBrowserContext::IsOffTheRecord() const {
+  // CEF contexts are never flagged as off-the-record. It causes problems
+  // for the extension system.
+  return false;
+}
+
+content::DownloadManagerDelegate*
+CefBrowserContext::GetDownloadManagerDelegate() {
+  if (!download_manager_delegate_) {
+    content::DownloadManager* manager =
+        BrowserContext::GetDownloadManager(this);
+    download_manager_delegate_.reset(new CefDownloadManagerDelegate(manager));
+  }
+  return download_manager_delegate_.get();
+}
+
+content::BrowserPluginGuestManager* CefBrowserContext::GetGuestManager() {
+  DCHECK(extensions::ExtensionsEnabled());
+  return guest_view::GuestViewManager::FromBrowserContext(this);
+}
+
+storage::SpecialStoragePolicy* CefBrowserContext::GetSpecialStoragePolicy() {
+  return nullptr;
+}
+
+content::PushMessagingService* CefBrowserContext::GetPushMessagingService() {
+  return nullptr;
+}
+
+content::StorageNotificationService*
+CefBrowserContext::GetStorageNotificationService() {
+  return nullptr;
+}
+
+content::SSLHostStateDelegate* CefBrowserContext::GetSSLHostStateDelegate() {
+  if (!ssl_host_state_delegate_.get())
+    ssl_host_state_delegate_.reset(new CefSSLHostStateDelegate());
+  return ssl_host_state_delegate_.get();
+}
+
+content::PermissionControllerDelegate*
+CefBrowserContext::GetPermissionControllerDelegate() {
+  return nullptr;
+}
+
+content::BackgroundFetchDelegate*
+CefBrowserContext::GetBackgroundFetchDelegate() {
+  return nullptr;
+}
+
+content::BackgroundSyncController*
+CefBrowserContext::GetBackgroundSyncController() {
+  return nullptr;
+}
+
+content::BrowsingDataRemoverDelegate*
+CefBrowserContext::GetBrowsingDataRemoverDelegate() {
+  return nullptr;
+}
+
+PrefService* CefBrowserContext::GetPrefs() {
+  return pref_service_.get();
+}
+
+const PrefService* CefBrowserContext::GetPrefs() const {
+  return pref_service_.get();
+}
+
+ProfileKey* CefBrowserContext::GetProfileKey() const {
+  DCHECK(key_);
+  return key_.get();
+}
+
+policy::SchemaRegistryService*
+CefBrowserContext::GetPolicySchemaRegistryService() {
+  NOTREACHED();
+  return nullptr;
+}
+
+policy::UserCloudPolicyManager* CefBrowserContext::GetUserCloudPolicyManager() {
+  NOTREACHED();
+  return nullptr;
+}
+
+policy::ProfilePolicyConnector* CefBrowserContext::GetProfilePolicyConnector() {
+  NOTREACHED();
+  return nullptr;
+}
+
+const policy::ProfilePolicyConnector*
+CefBrowserContext::GetProfilePolicyConnector() const {
+  NOTREACHED();
+  return nullptr;
+}
+
+const CefRequestContextSettings& CefBrowserContext::GetSettings() const {
+  return settings_;
+}
+
+HostContentSettingsMap* CefBrowserContext::GetHostContentSettingsMap() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!host_content_settings_map_.get()) {
+    // The |is_incognito_profile| and |is_guest_profile| arguments are
+    // intentionally set to false as they otherwise limit the types of values
+    // that can be stored in the settings map (for example, default values set
+    // via DefaultProvider::SetWebsiteSetting).
+    host_content_settings_map_ =
+        new HostContentSettingsMap(GetPrefs(), false, false, false);
+
+    // Change the default plugin policy.
+    const base::CommandLine* command_line =
+        base::CommandLine::ForCurrentProcess();
+    const std::string& plugin_policy_str =
+        command_line->GetSwitchValueASCII(switches::kPluginPolicy);
+    if (!plugin_policy_str.empty()) {
+      ContentSetting plugin_policy = CONTENT_SETTING_ALLOW;
+      if (base::LowerCaseEqualsASCII(plugin_policy_str,
+                                     switches::kPluginPolicy_Detect)) {
+        plugin_policy = CONTENT_SETTING_DETECT_IMPORTANT_CONTENT;
+      } else if (base::LowerCaseEqualsASCII(plugin_policy_str,
+                                            switches::kPluginPolicy_Block)) {
+        plugin_policy = CONTENT_SETTING_BLOCK;
+      }
+      host_content_settings_map_->SetDefaultContentSetting(
+          ContentSettingsType::PLUGINS, plugin_policy);
+    }
+  }
+  return host_content_settings_map_.get();
+}
+
+void CefBrowserContext::AddVisitedURLs(const std::vector<GURL>& urls) {
+  visitedlink_master_->AddURLs(urls);
+}
+
+void CefBrowserContext::RebuildTable(
+    const scoped_refptr<URLEnumerator>& enumerator) {
+  // Called when visited links will not or cannot be loaded from disk.
+  enumerator->OnComplete(true);
+}
+
+void CefBrowserContext::OnRenderFrameCreated(
+    CefRequestContextImpl* request_context,
+    int render_process_id,
+    int render_frame_id,
+    int frame_tree_node_id,
+    bool is_main_frame,
+    bool is_guest_view) {
+  CEF_REQUIRE_UIT();
+  DCHECK_GE(render_process_id, 0);
+  DCHECK_GE(render_frame_id, 0);
+  DCHECK_GE(frame_tree_node_id, 0);
+
+  render_id_set_.insert(std::make_pair(render_process_id, render_frame_id));
+  node_id_set_.insert(frame_tree_node_id);
+
+  CefRefPtr<CefRequestContextHandler> handler = request_context->GetHandler();
+  if (handler) {
+    handler_map_.AddHandler(render_process_id, render_frame_id,
+                            frame_tree_node_id, handler);
+
+    if (resource_context_) {
+      // Using base::Unretained() is safe because both this callback and
+      // possible deletion of |resource_context_| will execute on the IO thread,
+      // and this callback will be executed first.
+      CEF_POST_TASK(CEF_IOT,
+                    base::Bind(&CefResourceContext::AddHandler,
+                               base::Unretained(resource_context_.get()),
+                               render_process_id, render_frame_id,
+                               frame_tree_node_id, handler));
+    }
+  }
+}
+
+void CefBrowserContext::OnRenderFrameDeleted(
+    CefRequestContextImpl* request_context,
+    int render_process_id,
+    int render_frame_id,
+    int frame_tree_node_id,
+    bool is_main_frame,
+    bool is_guest_view) {
+  CEF_REQUIRE_UIT();
+  DCHECK_GE(render_process_id, 0);
+  DCHECK_GE(render_frame_id, 0);
+  DCHECK_GE(frame_tree_node_id, 0);
+
+  auto it1 =
+      render_id_set_.find(std::make_pair(render_process_id, render_frame_id));
+  if (it1 != render_id_set_.end())
+    render_id_set_.erase(it1);
+
+  auto it2 = node_id_set_.find(frame_tree_node_id);
+  if (it2 != node_id_set_.end())
+    node_id_set_.erase(it2);
+
+  CefRefPtr<CefRequestContextHandler> handler = request_context->GetHandler();
+  if (handler) {
+    handler_map_.RemoveHandler(render_process_id, render_frame_id,
+                               frame_tree_node_id);
+
+    if (resource_context_) {
+      // Using base::Unretained() is safe because both this callback and
+      // possible deletion of |resource_context_| will execute on the IO thread,
+      // and this callback will be executed first.
+      CEF_POST_TASK(
+          CEF_IOT,
+          base::Bind(&CefResourceContext::RemoveHandler,
+                     base::Unretained(resource_context_.get()),
+                     render_process_id, render_frame_id, frame_tree_node_id));
+    }
+  }
+
+  if (is_main_frame) {
+    ClearPluginLoadDecision(render_process_id);
+  }
+}
+
+CefRefPtr<CefRequestContextHandler> CefBrowserContext::GetHandler(
+    int render_process_id,
+    int render_frame_id,
+    int frame_tree_node_id,
+    bool require_frame_match) const {
+  CEF_REQUIRE_UIT();
+  return handler_map_.GetHandler(render_process_id, render_frame_id,
+                                 frame_tree_node_id, require_frame_match);
+}
+
+bool CefBrowserContext::IsAssociatedContext(int render_process_id,
+                                            int render_frame_id,
+                                            int frame_tree_node_id,
+                                            bool require_frame_match) const {
+  CEF_REQUIRE_UIT();
+
+  if (render_process_id >= 0 && render_frame_id >= 0) {
+    const auto it1 =
+        render_id_set_.find(std::make_pair(render_process_id, render_frame_id));
+    if (it1 != render_id_set_.end())
+      return true;
+  }
+
+  if (frame_tree_node_id >= 0) {
+    const auto it2 = node_id_set_.find(frame_tree_node_id);
+    if (it2 != node_id_set_.end())
+      return true;
+  }
+
+  if (render_process_id >= 0 && !require_frame_match) {
+    // Choose an arbitrary handler for the same process.
+    for (const auto& render_ids : render_id_set_) {
+      if (render_ids.first == render_process_id)
+        return true;
+    }
+  }
+
+  return false;
+}
+
+void CefBrowserContext::AddPluginLoadDecision(
+    int render_process_id,
+    const base::FilePath& plugin_path,
+    bool is_main_frame,
+    const url::Origin& main_frame_origin,
+    chrome::mojom::PluginStatus status) {
+  CEF_REQUIRE_UIT();
+  DCHECK_GE(render_process_id, 0);
+  DCHECK(!plugin_path.empty());
+
+  plugin_load_decision_map_.insert(std::make_pair(
+      std::make_pair(std::make_pair(render_process_id, plugin_path),
+                     std::make_pair(is_main_frame, main_frame_origin)),
+      status));
+}
+
+bool CefBrowserContext::HasPluginLoadDecision(
+    int render_process_id,
+    const base::FilePath& plugin_path,
+    bool is_main_frame,
+    const url::Origin& main_frame_origin,
+    chrome::mojom::PluginStatus* status) const {
+  CEF_REQUIRE_UIT();
+  DCHECK_GE(render_process_id, 0);
+  DCHECK(!plugin_path.empty());
+
+  PluginLoadDecisionMap::const_iterator it = plugin_load_decision_map_.find(
+      std::make_pair(std::make_pair(render_process_id, plugin_path),
+                     std::make_pair(is_main_frame, main_frame_origin)));
+  if (it == plugin_load_decision_map_.end())
+    return false;
+
+  *status = it->second;
+  return true;
+}
+
+void CefBrowserContext::ClearPluginLoadDecision(int render_process_id) {
+  CEF_REQUIRE_UIT();
+
+  if (render_process_id == -1) {
+    plugin_load_decision_map_.clear();
+  } else {
+    PluginLoadDecisionMap::iterator it = plugin_load_decision_map_.begin();
+    while (it != plugin_load_decision_map_.end()) {
+      if (it->first.first.first == render_process_id)
+        it = plugin_load_decision_map_.erase(it);
+      else
+        ++it;
+    }
+  }
+}
+
+void CefBrowserContext::RegisterSchemeHandlerFactory(
+    const std::string& scheme_name,
+    const std::string& domain_name,
+    CefRefPtr<CefSchemeHandlerFactory> factory) {
+  if (resource_context_) {
+    // Using base::Unretained() is safe because both this callback and possible
+    // deletion of |resource_context_| will execute on the IO thread, and this
+    // callback will be executed first.
+    CEF_POST_TASK(CEF_IOT,
+                  base::Bind(&CefResourceContext::RegisterSchemeHandlerFactory,
+                             base::Unretained(resource_context_.get()),
+                             scheme_name, domain_name, factory));
+  }
+}
+
+void CefBrowserContext::ClearSchemeHandlerFactories() {
+  if (resource_context_) {
+    // Using base::Unretained() is safe because both this callback and possible
+    // deletion of |resource_context_| will execute on the IO thread, and this
+    // callback will be executed first.
+    CEF_POST_TASK(CEF_IOT,
+                  base::Bind(&CefResourceContext::ClearSchemeHandlerFactories,
+                             base::Unretained(resource_context_.get())));
+  }
+}
+
+network::mojom::NetworkContext* CefBrowserContext::GetNetworkContext() {
+  CEF_REQUIRE_UIT();
+  return GetDefaultStoragePartition(this)->GetNetworkContext();
+}
+
+DownloadPrefs* CefBrowserContext::GetDownloadPrefs() {
+  CEF_REQUIRE_UIT();
+  if (!download_prefs_) {
+    download_prefs_.reset(new DownloadPrefs(this));
+  }
+  return download_prefs_.get();
+}
+
+bool CefBrowserContext::IsPrintPreviewSupported() const {
+  CEF_REQUIRE_UIT();
+  if (!extensions::PrintPreviewEnabled())
+    return false;
+
+  return !GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled);
+}
+
+CefMediaRouterManager* CefBrowserContext::GetMediaRouterManager() {
+  CEF_REQUIRE_UIT();
+  if (!media_router_manager_) {
+    media_router_manager_.reset(new CefMediaRouterManager(this));
+  }
+  return media_router_manager_.get();
+}
diff --git a/src/libcef/browser/browser_context.h b/src/libcef/browser/browser_context.h
new file mode 100644
index 0000000..232de11
--- /dev/null
+++ b/src/libcef/browser/browser_context.h
@@ -0,0 +1,359 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_CONTEXT_IMPL_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_CONTEXT_IMPL_H_
+#pragma once
+
+#include <set>
+
+#include "include/cef_request_context_handler.h"
+#include "libcef/browser/chrome_profile_stub.h"
+#include "libcef/browser/request_context_handler_map.h"
+#include "libcef/browser/resource_context.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/common/plugin.mojom.h"
+#include "components/proxy_config/pref_proxy_config_tracker.h"
+#include "components/visitedlink/browser/visitedlink_delegate.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "url/origin.h"
+
+/*
+// Classes used in request processing (network, storage, service, etc.):
+//
+// WC = WebContents
+//  Content API representation of a browser. Created by BHI or the system (for
+//  popups) and owned by BHI. Keeps a pointer to BC.
+//
+// BHI = CefBrowserHostImpl
+//  Implements the CefBrowser and CefBrowserHost interfaces which are exposed
+//  to clients. References an RCI instance. Owns a WC. Life span is controlled
+//  by client references and CefContentBrowserClient.
+//
+// RCI = CefRequestContextImpl
+//  Implements the CefRequestContext interface which is exposed to clients.
+//  References the isolated BC.
+//
+// BC = CefBrowserContext
+//   Entry point from WC when using an isolated RCI. Owns the RC and creates the
+//   SPI indirectly. Owned by CefBrowserMainParts for the global context or RCI
+//   for non-global contexts.
+//
+// SPI = content::StoragePartitionImpl
+//   Owns storage-related objects like Quota, IndexedDB, Cache, etc. Created by
+//   StoragePartitionImplMap::Get(). Provides access to the URCG. Life span is
+//   controlled indirectly by BC.
+//
+// RC = CefResourceContext
+//   Acts as a bridge for resource loading. Network request life span is tied to
+//   this object. Must be destroyed before the associated URCG. Life span is
+//   controlled by BC.
+//
+//
+// Relationship diagram:
+//   ref = reference (CefRefPtr/scoped_refptr)
+//   own = ownership (std::unique_ptr)
+//   ptr = raw pointer
+//
+//               CefBrowserMainParts
+//                       |
+//                      own
+//                       v
+// BHI -own-> WC -ptr-> BC -own-> SPI
+//
+// BHI -ref-> RCI -own-> BC -own-> RC
+//
+//
+// How shutdown works:
+// 1. CefBrowserHostImpl is destroyed on any thread due to browser close,
+//    ref release, etc.
+// 2. CefRequestContextImpl is destroyed (possibly asynchronously) on the UI
+//    thread due to CefBrowserHostImpl destruction, ref release, etc.
+// 3. CefBrowserContext is destroyed on the UI thread due to
+//    CefRequestContextImpl destruction or deletion in
+//    CefBrowserMainParts::PostMainMessageLoopRun().
+// 4. CefResourceContext is destroyed asynchronously on the IO thread due to
+//    CefBrowserContext destruction. This cancels/destroys any pending
+//    network requests.
+*/
+
+class CefDownloadManagerDelegate;
+class CefMediaRouterManager;
+class CefRequestContextImpl;
+class CefSSLHostStateDelegate;
+class CefVisitedLinkListener;
+class HostContentSettingsMap;
+class PrefService;
+
+namespace extensions {
+class CefExtensionSystem;
+}
+
+namespace visitedlink {
+class VisitedLinkWriter;
+}
+
+// Main entry point for configuring behavior on a per-browser basis. An instance
+// of this class is passed to WebContents::Create in CefBrowserHostImpl::
+// CreateInternal. Only accessed on the UI thread unless otherwise indicated.
+class CefBrowserContext : public ChromeProfileStub,
+                          public visitedlink::VisitedLinkDelegate {
+ public:
+  explicit CefBrowserContext(const CefRequestContextSettings& settings);
+
+  // Returns the existing instance, if any, associated with the specified
+  // |cache_path|.
+  static CefBrowserContext* GetForCachePath(const base::FilePath& cache_path);
+
+  // Returns the existing instance, if any, associated with the specified IDs.
+  // See comments on IsAssociatedContext() for usage.
+  static CefBrowserContext* GetForIDs(int render_process_id,
+                                      int render_frame_id,
+                                      int frame_tree_node_id,
+                                      bool require_frame_match);
+
+  // Returns the underlying CefBrowserContext if any.
+  static CefBrowserContext* GetForContext(content::BrowserContext* context);
+
+  // Returns all existing CefBrowserContext.
+  static std::vector<CefBrowserContext*> GetAll();
+
+  // Must be called immediately after this object is created.
+  void Initialize();
+
+  // Track associated CefRequestContextImpl objects. This object will delete
+  // itself when the count reaches zero.
+  void AddCefRequestContext(CefRequestContextImpl* context);
+  void RemoveCefRequestContext(CefRequestContextImpl* context);
+
+  // BrowserContext methods.
+  content::ResourceContext* GetResourceContext() override;
+  content::ClientHintsControllerDelegate* GetClientHintsControllerDelegate()
+      override;
+  void SetCorsOriginAccessListForOrigin(
+      const url::Origin& source_origin,
+      std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns,
+      std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
+      base::OnceClosure closure) override;
+  base::FilePath GetPath() override;
+  base::FilePath GetPath() const override;
+  std::unique_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate(
+      const base::FilePath& partition_path) override;
+  bool IsOffTheRecord() const override;
+  content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
+  content::BrowserPluginGuestManager* GetGuestManager() override;
+  storage::SpecialStoragePolicy* GetSpecialStoragePolicy() override;
+  content::PushMessagingService* GetPushMessagingService() override;
+  content::StorageNotificationService* GetStorageNotificationService() override;
+  content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
+  content::PermissionControllerDelegate* GetPermissionControllerDelegate()
+      override;
+  content::BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
+  content::BackgroundSyncController* GetBackgroundSyncController() override;
+  content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate()
+      override;
+
+  // Profile methods.
+  ChromeZoomLevelPrefs* GetZoomLevelPrefs() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
+  PrefService* GetPrefs() override;
+  bool AllowsBrowserWindows() const override { return false; }
+  const PrefService* GetPrefs() const override;
+  ProfileKey* GetProfileKey() const override;
+  policy::SchemaRegistryService* GetPolicySchemaRegistryService() override;
+  policy::UserCloudPolicyManager* GetUserCloudPolicyManager() override;
+  policy::ProfilePolicyConnector* GetProfilePolicyConnector() override;
+  const policy::ProfilePolicyConnector* GetProfilePolicyConnector()
+      const override;
+
+  // Values checked in ProfileNetworkContextService::CreateNetworkContextParams
+  // when creating the NetworkContext.
+  bool ShouldRestoreOldSessionCookies() override {
+    return should_persist_session_cookies_;
+  }
+  bool ShouldPersistSessionCookies() override {
+    return should_persist_session_cookies_;
+  }
+  base::Optional<std::vector<std::string>> GetCookieableSchemes() override {
+    return cookieable_schemes_;
+  }
+
+  // visitedlink::VisitedLinkDelegate methods.
+  void RebuildTable(const scoped_refptr<URLEnumerator>& enumerator) override;
+
+  // Returns the settings associated with this object. Safe to call from any
+  // thread.
+  const CefRequestContextSettings& GetSettings() const;
+
+  // Settings for plugins and extensions.
+  HostContentSettingsMap* GetHostContentSettingsMap();
+
+  // Called from CefBrowserHostImpl::DidNavigateAnyFrame to update the table of
+  // visited links.
+  void AddVisitedURLs(const std::vector<GURL>& urls);
+
+  // Called from CefRequestContextImpl::OnRenderFrameCreated.
+  void OnRenderFrameCreated(CefRequestContextImpl* request_context,
+                            int render_process_id,
+                            int render_frame_id,
+                            int frame_tree_node_id,
+                            bool is_main_frame,
+                            bool is_guest_view);
+
+  // Called from CefRequestContextImpl::OnRenderFrameDeleted.
+  void OnRenderFrameDeleted(CefRequestContextImpl* request_context,
+                            int render_process_id,
+                            int render_frame_id,
+                            int frame_tree_node_id,
+                            bool is_main_frame,
+                            bool is_guest_view);
+
+  // Returns the handler that matches the specified IDs. Pass -1 for unknown
+  // values. If |require_frame_match| is true only exact matches will be
+  // returned. If |require_frame_match| is false, and there is not an exact
+  // match, then the first handler for the same |render_process_id| will be
+  // returned.
+  CefRefPtr<CefRequestContextHandler> GetHandler(
+      int render_process_id,
+      int render_frame_id,
+      int frame_tree_node_id,
+      bool require_frame_match) const;
+
+  // Returns true if this context is associated with the specified IDs. Pass -1
+  // for unknown values. If |require_frame_match| is true only exact matches
+  // will qualify. If |require_frame_match| is false, and there is not an exact
+  // match, then any match for |render_process_id| will qualify.
+  bool IsAssociatedContext(int render_process_id,
+                           int render_frame_id,
+                           int frame_tree_node_id,
+                           bool require_frame_match) const;
+
+  // Remember the plugin load decision for plugin status requests that arrive
+  // via CefPluginServiceFilter::IsPluginAvailable.
+  void AddPluginLoadDecision(int render_process_id,
+                             const base::FilePath& plugin_path,
+                             bool is_main_frame,
+                             const url::Origin& main_frame_origin,
+                             chrome::mojom::PluginStatus status);
+  bool HasPluginLoadDecision(int render_process_id,
+                             const base::FilePath& plugin_path,
+                             bool is_main_frame,
+                             const url::Origin& main_frame_origin,
+                             chrome::mojom::PluginStatus* status) const;
+
+  // Clear the plugin load decisions associated with |render_process_id|, or all
+  // plugin load decisions if |render_process_id| is -1.
+  void ClearPluginLoadDecision(int render_process_id);
+
+  // Called from CefRequestContextImpl methods of the same name.
+  void RegisterSchemeHandlerFactory(const std::string& scheme_name,
+                                    const std::string& domain_name,
+                                    CefRefPtr<CefSchemeHandlerFactory> factory);
+  void ClearSchemeHandlerFactories();
+
+  network::mojom::NetworkContext* GetNetworkContext();
+
+  void set_should_persist_session_cookies(bool value) {
+    should_persist_session_cookies_ = value;
+  }
+  void set_cookieable_schemes(
+      base::Optional<std::vector<std::string>> schemes) {
+    cookieable_schemes_ = schemes;
+  }
+
+  CefResourceContext* resource_context() const {
+    return resource_context_.get();
+  }
+  extensions::CefExtensionSystem* extension_system() const {
+    return extension_system_;
+  }
+
+  // Called from DownloadPrefs::FromBrowserContext.
+  DownloadPrefs* GetDownloadPrefs();
+
+  // Returns true if this context supports print preview.
+  bool IsPrintPreviewSupported() const;
+
+  CefMediaRouterManager* GetMediaRouterManager();
+
+  // Returns the BrowserContext, or nullptr if the BrowserContext has already
+  // been destroyed.
+  using Getter = base::RepeatingCallback<CefBrowserContext*()>;
+  Getter getter() const { return getter_; }
+
+ private:
+  // Allow deletion via std::unique_ptr().
+  friend std::default_delete<CefBrowserContext>;
+
+  ~CefBrowserContext() override;
+
+  // Members initialized during construction are safe to access from any thread.
+  const CefRequestContextSettings settings_;
+  base::FilePath cache_path_;
+
+  // CefRequestContextImpl objects referencing this object.
+  std::set<CefRequestContextImpl*> request_context_set_;
+
+  std::unique_ptr<PrefService> pref_service_;
+  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
+
+  std::unique_ptr<CefDownloadManagerDelegate> download_manager_delegate_;
+  std::unique_ptr<CefSSLHostStateDelegate> ssl_host_state_delegate_;
+  scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
+  std::unique_ptr<visitedlink::VisitedLinkWriter> visitedlink_master_;
+  // |visitedlink_listener_| is owned by visitedlink_master_.
+  CefVisitedLinkListener* visitedlink_listener_;
+  bool should_persist_session_cookies_ = false;
+  base::Optional<std::vector<std::string>> cookieable_schemes_;
+
+  std::unique_ptr<CefResourceContext> resource_context_;
+
+  // Owned by the KeyedService system.
+  extensions::CefExtensionSystem* extension_system_ = nullptr;
+
+  // The key to index KeyedService instances created by
+  // SimpleKeyedServiceFactory.
+  std::unique_ptr<ProfileKey> key_;
+
+  std::unique_ptr<DownloadPrefs> download_prefs_;
+
+  std::unique_ptr<CefMediaRouterManager> media_router_manager_;
+
+  // Map IDs to CefRequestContextHandler objects.
+  CefRequestContextHandlerMap handler_map_;
+
+  // Map (render_process_id, plugin_path, is_main_frame, main_frame_origin) to
+  // plugin load decision.
+  typedef std::map<
+      std::pair<std::pair<int, base::FilePath>, std::pair<bool, url::Origin>>,
+      chrome::mojom::PluginStatus>
+      PluginLoadDecisionMap;
+  PluginLoadDecisionMap plugin_load_decision_map_;
+
+  // Set of (render_process_id, render_frame_id) associated with this context.
+  typedef std::set<std::pair<int, int>> RenderIdSet;
+  RenderIdSet render_id_set_;
+
+  // Set of frame_tree_node_id associated with this context. Keeping this list
+  // is necessary because, when navigating the main frame, a new (pre-commit)
+  // network request will be created before the RenderFrameHost. Consequently we
+  // can't rely on valid render IDs. See https://crbug.com/776884 for
+  // background.
+  typedef std::set<int> NodeIdSet;
+  NodeIdSet node_id_set_;
+
+  Getter getter_;
+  base::WeakPtrFactory<CefBrowserContext> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserContext);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_CONTEXT_IMPL_H_
diff --git a/src/libcef/browser/browser_context_keyed_service_factories.cc b/src/libcef/browser/browser_context_keyed_service_factories.cc
new file mode 100644
index 0000000..f89c3e8
--- /dev/null
+++ b/src/libcef/browser/browser_context_keyed_service_factories.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/browser_context_keyed_service_factories.h"
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/plugins/plugin_prefs_factory.h"
+#include "chrome/browser/profiles/renderer_updater_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/prefs/prefs_tab_helper.h"
+#include "extensions/browser/api/alarms/alarm_manager.h"
+#include "extensions/browser/api/storage/storage_frontend.h"
+#include "extensions/browser/renderer_startup_helper.h"
+
+namespace cef {
+
+void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
+  CookieSettingsFactory::GetInstance();
+  PluginPrefsFactory::GetInstance();
+  PrefsTabHelper::GetServiceInstance();
+  RendererUpdaterFactory::GetInstance();
+  SpellcheckServiceFactory::GetInstance();
+  ThemeServiceFactory::GetInstance();
+
+  if (extensions::ExtensionsEnabled()) {
+    extensions::AlarmManager::GetFactoryInstance();
+    extensions::RendererStartupHelperFactory::GetInstance();
+    extensions::StorageFrontend::GetFactoryInstance();
+  }
+}
+
+}  // namespace cef
diff --git a/src/libcef/browser/browser_context_keyed_service_factories.h b/src/libcef/browser/browser_context_keyed_service_factories.h
new file mode 100644
index 0000000..ba19492
--- /dev/null
+++ b/src/libcef/browser/browser_context_keyed_service_factories.h
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_
+
+namespace cef {
+
+// Ensures the existence of any BrowserContextKeyedServiceFactory provided by
+// the CEF extensions code or otherwise required by CEF. See
+// libcef/common/extensions/api/README.txt for additional details.
+void EnsureBrowserContextKeyedServiceFactoriesBuilt();
+
+}  // namespace cef
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_
diff --git a/src/libcef/browser/browser_host_impl.cc b/src/libcef/browser/browser_host_impl.cc
new file mode 100644
index 0000000..865786b
--- /dev/null
+++ b/src/libcef/browser/browser_host_impl.cc
@@ -0,0 +1,3216 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include <string>
+#include <utility>
+
+#include "libcef/browser/audio_capturer.h"
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/browser_info.h"
+#include "libcef/browser/browser_info_manager.h"
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/browser_util.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/devtools/devtools_manager.h"
+#include "libcef/browser/extensions/browser_extensions_util.h"
+#include "libcef/browser/extensions/extension_background_host.h"
+#include "libcef/browser/extensions/extension_system.h"
+#include "libcef/browser/extensions/extension_view_host.h"
+#include "libcef/browser/extensions/extension_web_contents_observer.h"
+#include "libcef/browser/image_impl.h"
+#include "libcef/browser/media_capture_devices_dispatcher.h"
+#include "libcef/browser/navigation_entry_impl.h"
+#include "libcef/browser/net/chrome_scheme_handler.h"
+#include "libcef/browser/net/scheme_handler.h"
+#include "libcef/browser/osr/osr_util.h"
+#include "libcef/browser/printing/print_view_manager.h"
+#include "libcef/browser/request_context_impl.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/drag_data_impl.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/common/main_delegate.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
+#include "chrome/browser/printing/print_view_manager.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "chrome/browser/ui/prefs/prefs_tab_helper.h"
+#include "components/favicon/core/favicon_url.h"
+#include "components/spellcheck/common/spellcheck_features.h"
+#include "components/zoom/zoom_controller.h"
+#include "content/browser/gpu/compositor_util.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/widget_messages.h"
+#include "content/public/browser/desktop_media_id.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/download_request_utils.h"
+#include "content/public/browser/file_select_listener.h"
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/browser/keyboard_event_processing_result.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/process_manager.h"
+#include "net/base/net_errors.h"
+#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
+#include "ui/events/base_event_utils.h"
+
+#if defined(OS_MACOSX)
+#include "components/spellcheck/browser/spellcheck_platform.h"
+#endif
+
+using content::KeyboardEventProcessingResult;
+
+namespace {
+
+// Associates a CefBrowserHostImpl instance with a WebContents. This object will
+// be deleted automatically when the WebContents is destroyed.
+class WebContentsUserDataAdapter : public base::SupportsUserData::Data {
+ public:
+  static void Register(CefBrowserHostImpl* browser) {
+    new WebContentsUserDataAdapter(browser);
+  }
+
+  static CefBrowserHostImpl* Get(const content::WebContents* web_contents) {
+    WebContentsUserDataAdapter* adapter =
+        static_cast<WebContentsUserDataAdapter*>(
+            web_contents->GetUserData(UserDataKey()));
+    if (adapter)
+      return adapter->browser_;
+    return nullptr;
+  }
+
+ private:
+  WebContentsUserDataAdapter(CefBrowserHostImpl* browser) : browser_(browser) {
+    browser->web_contents()->SetUserData(UserDataKey(), base::WrapUnique(this));
+  }
+
+  static void* UserDataKey() {
+    // We just need a unique constant. Use the address of a static that
+    // COMDAT folding won't touch in an optimizing linker.
+    static int data_key = 0;
+    return reinterpret_cast<void*>(&data_key);
+  }
+
+  CefBrowserHostImpl* browser_;  // Not owned.
+};
+
+class CreateBrowserHelper {
+ public:
+  CreateBrowserHelper(const CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient> client,
+                      const CefString& url,
+                      const CefBrowserSettings& settings,
+                      CefRefPtr<CefDictionaryValue> extra_info,
+                      CefRefPtr<CefRequestContext> request_context)
+      : window_info_(windowInfo),
+        client_(client),
+        url_(url),
+        settings_(settings),
+        extra_info_(extra_info),
+        request_context_(request_context) {}
+
+  CefWindowInfo window_info_;
+  CefRefPtr<CefClient> client_;
+  CefString url_;
+  CefBrowserSettings settings_;
+  CefRefPtr<CefDictionaryValue> extra_info_;
+  CefRefPtr<CefRequestContext> request_context_;
+};
+
+void CreateBrowserWithHelper(CreateBrowserHelper* helper) {
+  CefBrowserHost::CreateBrowserSync(
+      helper->window_info_, helper->client_, helper->url_, helper->settings_,
+      helper->extra_info_, helper->request_context_);
+  delete helper;
+}
+
+class ShowDevToolsHelper {
+ public:
+  ShowDevToolsHelper(CefRefPtr<CefBrowserHostImpl> browser,
+                     const CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient> client,
+                     const CefBrowserSettings& settings,
+                     const CefPoint& inspect_element_at)
+      : browser_(browser),
+        window_info_(windowInfo),
+        client_(client),
+        settings_(settings),
+        inspect_element_at_(inspect_element_at) {}
+
+  CefRefPtr<CefBrowserHostImpl> browser_;
+  CefWindowInfo window_info_;
+  CefRefPtr<CefClient> client_;
+  CefBrowserSettings settings_;
+  CefPoint inspect_element_at_;
+};
+
+void ShowDevToolsWithHelper(ShowDevToolsHelper* helper) {
+  helper->browser_->ShowDevTools(helper->window_info_, helper->client_,
+                                 helper->settings_,
+                                 helper->inspect_element_at_);
+  delete helper;
+}
+
+// Callback from CefBrowserHostImpl::DownloadImage.
+void OnDownloadImage(uint32 max_image_size,
+                     CefRefPtr<CefDownloadImageCallback> callback,
+                     int id,
+                     int http_status_code,
+                     const GURL& image_url,
+                     const std::vector<SkBitmap>& bitmaps,
+                     const std::vector<gfx::Size>& sizes) {
+  CEF_REQUIRE_UIT();
+
+  CefRefPtr<CefImageImpl> image_impl;
+
+  if (!bitmaps.empty()) {
+    image_impl = new CefImageImpl();
+    image_impl->AddBitmaps(max_image_size, bitmaps);
+  }
+
+  callback->OnDownloadImageFinished(image_url.spec(), http_status_code,
+                                    image_impl.get());
+}
+
+static constexpr base::TimeDelta kRecentlyAudibleTimeout =
+    base::TimeDelta::FromSeconds(2);
+
+}  // namespace
+
+// CefBrowserHost static methods.
+// -----------------------------------------------------------------------------
+
+// static
+bool CefBrowserHost::CreateBrowser(
+    const CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return false;
+  }
+
+  // Verify that the settings structure is a valid size.
+  if (settings.size != sizeof(cef_browser_settings_t)) {
+    NOTREACHED() << "invalid CefBrowserSettings structure size";
+    return false;
+  }
+
+  // Verify windowless rendering requirements.
+  if (windowInfo.windowless_rendering_enabled &&
+      !client->GetRenderHandler().get()) {
+    NOTREACHED() << "CefRenderHandler implementation is required";
+    return false;
+  }
+
+  if (windowInfo.windowless_rendering_enabled &&
+      !CefContext::Get()->settings().windowless_rendering_enabled) {
+    LOG(ERROR) << "Creating a windowless browser without setting "
+                  "CefSettings.windowless_rendering_enabled may result in "
+                  "reduced performance or runtime errors.";
+  }
+
+  // Create the browser on the UI thread.
+  CreateBrowserHelper* helper = new CreateBrowserHelper(
+      windowInfo, client, url, settings, extra_info, request_context);
+  CEF_POST_TASK(CEF_UIT, base::BindOnce(CreateBrowserWithHelper, helper));
+
+  return true;
+}
+
+// static
+CefRefPtr<CefBrowser> CefBrowserHost::CreateBrowserSync(
+    const CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return nullptr;
+  }
+
+  // Verify that the settings structure is a valid size.
+  if (settings.size != sizeof(cef_browser_settings_t)) {
+    NOTREACHED() << "invalid CefBrowserSettings structure size";
+    return nullptr;
+  }
+
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  // Verify windowless rendering requirements.
+  if (windowInfo.windowless_rendering_enabled &&
+      !client->GetRenderHandler().get()) {
+    NOTREACHED() << "CefRenderHandler implementation is required";
+    return nullptr;
+  }
+
+  CefBrowserHostImpl::CreateParams create_params;
+  create_params.window_info.reset(new CefWindowInfo(windowInfo));
+  create_params.client = client;
+  create_params.url = GURL(url.ToString());
+  if (!url.empty() && !create_params.url.is_valid() &&
+      !create_params.url.has_scheme()) {
+    std::string new_url = std::string("http://") + url.ToString();
+    create_params.url = GURL(new_url);
+  }
+  create_params.settings = settings;
+  create_params.extra_info = extra_info;
+  create_params.request_context = request_context;
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CefBrowserHostImpl::Create(create_params);
+  return browser.get();
+}
+
+// CefBrowserHostImpl static methods.
+// -----------------------------------------------------------------------------
+
+// static
+CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::Create(
+    CreateParams& create_params) {
+  std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate =
+      CefBrowserPlatformDelegate::Create(create_params);
+  CHECK(platform_delegate);
+
+  const bool is_devtools_popup = !!create_params.devtools_opener;
+
+  scoped_refptr<CefBrowserInfo> info =
+      CefBrowserInfoManager::GetInstance()->CreateBrowserInfo(
+          is_devtools_popup, platform_delegate->IsWindowless(),
+          create_params.extra_info);
+
+  // Get or create the request context and browser context.
+  CefRefPtr<CefRequestContextImpl> request_context_impl =
+      CefRequestContextImpl::GetOrCreateForRequestContext(
+          create_params.request_context);
+  DCHECK(request_context_impl);
+  CefBrowserContext* browser_context =
+      request_context_impl->GetBrowserContext();
+  DCHECK(browser_context);
+
+  if (!create_params.request_context) {
+    // Using the global request context.
+    create_params.request_context = request_context_impl.get();
+  }
+
+  CefRefPtr<CefExtension> cef_extension;
+  scoped_refptr<content::SiteInstance> site_instance;
+  if (extensions::ExtensionsEnabled() && !create_params.url.is_empty()) {
+    if (!create_params.extension) {
+      // We might be loading an extension app view where the extension URL is
+      // provided by the client.
+      create_params.extension =
+          extensions::GetExtensionForUrl(browser_context, create_params.url);
+    }
+    if (create_params.extension) {
+      cef_extension = browser_context->extension_system()->GetExtension(
+          create_params.extension->id());
+      DCHECK(cef_extension);
+
+      if (create_params.extension_host_type == extensions::VIEW_TYPE_INVALID) {
+        // Default to dialog behavior.
+        create_params.extension_host_type =
+            extensions::VIEW_TYPE_EXTENSION_DIALOG;
+      }
+
+      // Extension resources will fail to load if we don't use a SiteInstance
+      // associated with the extension.
+      // (CefContentBrowserClient::SiteInstanceGotProcess won't find the
+      // extension to register with InfoMap, and AllowExtensionResourceLoad in
+      // ExtensionProtocolHandler::MaybeCreateJob will return false resulting in
+      // ERR_BLOCKED_BY_CLIENT).
+      site_instance = extensions::ProcessManager::Get(browser_context)
+                          ->GetSiteInstanceForURL(create_params.url);
+      DCHECK(site_instance);
+    }
+  }
+
+  content::WebContents::CreateParams wc_create_params(browser_context,
+                                                      site_instance);
+
+  if (platform_delegate->IsWindowless()) {
+    // Create the OSR view for the WebContents.
+    platform_delegate->CreateViewForWebContents(
+        &wc_create_params.view, &wc_create_params.delegate_view);
+  }
+
+  std::unique_ptr<content::WebContents> web_contents =
+      content::WebContents::Create(wc_create_params);
+  DCHECK(web_contents);
+
+  CefRefPtr<CefBrowserHostImpl> browser = CreateInternal(
+      create_params.settings, create_params.client, web_contents.release(),
+      true, info, create_params.devtools_opener, is_devtools_popup,
+      static_cast<CefRequestContextImpl*>(create_params.request_context.get()),
+      std::move(platform_delegate), cef_extension);
+  if (!browser)
+    return nullptr;
+
+  if (create_params.extension) {
+    browser->CreateExtensionHost(create_params.extension, browser_context,
+                                 browser->web_contents(), create_params.url,
+                                 create_params.extension_host_type);
+  } else if (!create_params.url.is_empty()) {
+    browser->LoadMainFrameURL(create_params.url.spec(), content::Referrer(),
+                              CefFrameHostImpl::kPageTransitionExplicit,
+                              std::string());
+  }
+
+  return browser.get();
+}
+
+// static
+CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::CreateInternal(
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefClient> client,
+    content::WebContents* web_contents,
+    bool own_web_contents,
+    scoped_refptr<CefBrowserInfo> browser_info,
+    CefRefPtr<CefBrowserHostImpl> opener,
+    bool is_devtools_popup,
+    CefRefPtr<CefRequestContextImpl> request_context,
+    std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
+    CefRefPtr<CefExtension> extension) {
+  CEF_REQUIRE_UIT();
+  DCHECK(web_contents);
+  DCHECK(browser_info);
+  DCHECK(request_context);
+  DCHECK(platform_delegate);
+
+  // If |opener| is non-NULL it must be a popup window.
+  DCHECK(!opener.get() || browser_info->is_popup());
+
+  if (opener) {
+    if (!opener->platform_delegate_) {
+      // The opener window is being destroyed. Cancel the popup.
+      return nullptr;
+    }
+
+    // Give the opener browser's platform delegate an opportunity to modify the
+    // new browser's platform delegate.
+    opener->platform_delegate_->PopupWebContentsCreated(
+        settings, client, web_contents, platform_delegate.get(),
+        is_devtools_popup);
+  }
+
+  platform_delegate->WebContentsCreated(web_contents);
+
+  CefRefPtr<CefBrowserHostImpl> browser = new CefBrowserHostImpl(
+      settings, client, web_contents, browser_info, opener, request_context,
+      std::move(platform_delegate), extension);
+  if (own_web_contents)
+    browser->set_owned_web_contents(web_contents);
+  if (!browser->CreateHostWindow())
+    return nullptr;
+
+  // Notify that the browser has been created. These must be delivered in the
+  // expected order.
+
+  // 1. Notify the browser's LifeSpanHandler. This must always be the first
+  // notification for the browser.
+  if (client.get()) {
+    CefRefPtr<CefLifeSpanHandler> handler = client->GetLifeSpanHandler();
+    if (handler.get())
+      handler->OnAfterCreated(browser.get());
+  }
+
+  // 2. Notify the platform delegate. With Views this will result in a call to
+  // CefBrowserViewDelegate::OnBrowserCreated().
+  browser->platform_delegate_->NotifyBrowserCreated();
+
+  if (opener && opener->platform_delegate_) {
+    // 3. Notify the opener browser's platform delegate. With Views this will
+    // result in a call to CefBrowserViewDelegate::OnPopupBrowserViewCreated().
+    opener->platform_delegate_->PopupBrowserCreated(browser.get(),
+                                                    is_devtools_popup);
+  }
+
+  return browser;
+}
+
+// static
+CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::GetBrowserForHost(
+    const content::RenderViewHost* host) {
+  DCHECK(host);
+  CEF_REQUIRE_UIT();
+  content::WebContents* web_contents = content::WebContents::FromRenderViewHost(
+      const_cast<content::RenderViewHost*>(host));
+  if (web_contents)
+    return GetBrowserForContents(web_contents);
+  return nullptr;
+}
+
+// static
+CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::GetBrowserForHost(
+    const content::RenderFrameHost* host) {
+  DCHECK(host);
+  CEF_REQUIRE_UIT();
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(
+          const_cast<content::RenderFrameHost*>(host));
+  if (web_contents)
+    return GetBrowserForContents(web_contents);
+  return nullptr;
+}
+
+// static
+CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::GetBrowserForContents(
+    const content::WebContents* contents) {
+  DCHECK(contents);
+  CEF_REQUIRE_UIT();
+  return WebContentsUserDataAdapter::Get(contents);
+}
+
+// static
+CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::GetBrowserForFrameTreeNode(
+    int frame_tree_node_id) {
+  // Use the thread-safe approach.
+  scoped_refptr<CefBrowserInfo> info =
+      CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameTreeNode(
+          frame_tree_node_id);
+  if (info.get()) {
+    CefRefPtr<CefBrowserHostImpl> browser = info->browser();
+    if (!browser.get()) {
+      LOG(WARNING) << "Found browser id " << info->browser_id()
+                   << " but no browser object matching frame tree node id "
+                   << frame_tree_node_id;
+    }
+    return browser;
+  }
+
+  return nullptr;
+}
+
+// static
+CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::GetBrowserForFrameRoute(
+    int render_process_id,
+    int render_routing_id) {
+  if (render_process_id == -1 || render_routing_id == MSG_ROUTING_NONE)
+    return nullptr;
+
+  if (CEF_CURRENTLY_ON_UIT()) {
+    // Use the non-thread-safe but potentially faster approach.
+    content::RenderFrameHost* render_frame_host =
+        content::RenderFrameHost::FromID(render_process_id, render_routing_id);
+    if (!render_frame_host)
+      return nullptr;
+    return GetBrowserForHost(render_frame_host);
+  } else {
+    // Use the thread-safe approach.
+    bool is_guest_view = false;
+    scoped_refptr<CefBrowserInfo> info =
+        CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameRoute(
+            render_process_id, render_routing_id, &is_guest_view);
+    if (info.get() && !is_guest_view) {
+      CefRefPtr<CefBrowserHostImpl> browser = info->browser();
+      if (!browser.get()) {
+        LOG(WARNING) << "Found browser id " << info->browser_id()
+                     << " but no browser object matching frame process id "
+                     << render_process_id << " and routing id "
+                     << render_routing_id;
+      }
+      return browser;
+    }
+    return nullptr;
+  }
+}
+
+// CefBrowserHostImpl methods.
+// -----------------------------------------------------------------------------
+
+CefBrowserHostImpl::~CefBrowserHostImpl() {}
+
+CefRefPtr<CefBrowser> CefBrowserHostImpl::GetBrowser() {
+  return this;
+}
+
+void CefBrowserHostImpl::CloseBrowser(bool force_close) {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    // Exit early if a close attempt is already pending and this method is
+    // called again from somewhere other than WindowDestroyed().
+    if (destruction_state_ >= DESTRUCTION_STATE_PENDING &&
+        (IsWindowless() || !window_destroyed_)) {
+      if (force_close && destruction_state_ == DESTRUCTION_STATE_PENDING) {
+        // Upgrade the destruction state.
+        destruction_state_ = DESTRUCTION_STATE_ACCEPTED;
+      }
+      return;
+    }
+
+    if (destruction_state_ < DESTRUCTION_STATE_ACCEPTED) {
+      destruction_state_ = (force_close ? DESTRUCTION_STATE_ACCEPTED
+                                        : DESTRUCTION_STATE_PENDING);
+    }
+
+    content::WebContents* contents = web_contents();
+    if (contents && contents->NeedToFireBeforeUnloadOrUnload()) {
+      // Will result in a call to BeforeUnloadFired() and, if the close isn't
+      // canceled, CloseContents().
+      contents->DispatchBeforeUnload(false /* auto_cancel */);
+    } else {
+      CloseContents(contents);
+    }
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::CloseBrowser,
+                                          this, force_close));
+  }
+}
+
+bool CefBrowserHostImpl::TryCloseBrowser() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  // Protect against multiple requests to close while the close is pending.
+  if (destruction_state_ <= DESTRUCTION_STATE_PENDING) {
+    if (destruction_state_ == DESTRUCTION_STATE_NONE) {
+      // Request that the browser close.
+      CloseBrowser(false);
+    }
+
+    // Cancel the close.
+    return false;
+  }
+
+  // Allow the close.
+  return true;
+}
+
+void CefBrowserHostImpl::SetFocus(bool focus) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::SetFocus, this, focus));
+    return;
+  }
+
+  if (focus)
+    OnSetFocus(FOCUS_SOURCE_SYSTEM);
+  else if (platform_delegate_)
+    platform_delegate_->SendFocusEvent(false);
+}
+
+CefWindowHandle CefBrowserHostImpl::GetWindowHandle() {
+  if (IsViewsHosted() && CEF_CURRENTLY_ON_UIT()) {
+    // Always return the most up-to-date window handle for a views-hosted
+    // browser since it may change if the view is re-parented.
+    if (platform_delegate_)
+      return platform_delegate_->GetHostWindowHandle();
+  }
+  return host_window_handle_;
+}
+
+CefWindowHandle CefBrowserHostImpl::GetOpenerWindowHandle() {
+  return opener_;
+}
+
+bool CefBrowserHostImpl::HasView() {
+  return IsViewsHosted();
+}
+
+CefRefPtr<CefClient> CefBrowserHostImpl::GetClient() {
+  return client_;
+}
+
+CefRefPtr<CefRequestContext> CefBrowserHostImpl::GetRequestContext() {
+  return request_context_;
+}
+
+double CefBrowserHostImpl::GetZoomLevel() {
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return 0;
+  }
+
+  if (web_contents())
+    return content::HostZoomMap::GetZoomLevel(web_contents());
+
+  return 0;
+}
+
+void CefBrowserHostImpl::SetZoomLevel(double zoomLevel) {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (web_contents())
+      content::HostZoomMap::SetZoomLevel(web_contents(), zoomLevel);
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SetZoomLevel,
+                                          this, zoomLevel));
+  }
+}
+
+void CefBrowserHostImpl::RunFileDialog(
+    FileDialogMode mode,
+    const CefString& title,
+    const CefString& default_file_path,
+    const std::vector<CefString>& accept_filters,
+    int selected_accept_filter,
+    CefRefPtr<CefRunFileDialogCallback> callback) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::RunFileDialog, this, mode,
+                                 title, default_file_path, accept_filters,
+                                 selected_accept_filter, callback));
+    return;
+  }
+
+  EnsureFileDialogManager();
+  file_dialog_manager_->RunFileDialog(mode, title, default_file_path,
+                                      accept_filters, selected_accept_filter,
+                                      callback);
+}
+
+void CefBrowserHostImpl::StartDownload(const CefString& url) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT, base::BindOnce(&CefBrowserHostImpl::StartDownload, this, url));
+    return;
+  }
+
+  GURL gurl = GURL(url.ToString());
+  if (gurl.is_empty() || !gurl.is_valid())
+    return;
+
+  if (!web_contents())
+    return;
+
+  CefBrowserContext* context =
+      static_cast<CefBrowserContext*>(web_contents()->GetBrowserContext());
+  if (!context)
+    return;
+
+  content::DownloadManager* manager =
+      content::BrowserContext::GetDownloadManager(context);
+  if (!manager)
+    return;
+
+  std::unique_ptr<download::DownloadUrlParameters> params(
+      content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame(
+          web_contents(), gurl, MISSING_TRAFFIC_ANNOTATION));
+  manager->DownloadUrl(std::move(params));
+}
+
+void CefBrowserHostImpl::DownloadImage(
+    const CefString& image_url,
+    bool is_favicon,
+    uint32 max_image_size,
+    bool bypass_cache,
+    CefRefPtr<CefDownloadImageCallback> callback) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::DownloadImage, this, image_url,
+                       is_favicon, max_image_size, bypass_cache, callback));
+    return;
+  }
+
+  if (!callback)
+    return;
+
+  GURL gurl = GURL(image_url.ToString());
+  if (gurl.is_empty() || !gurl.is_valid())
+    return;
+
+  if (!web_contents())
+    return;
+
+  web_contents()->DownloadImage(
+      gurl, is_favicon, max_image_size,
+      max_image_size * gfx::ImageSkia::GetMaxSupportedScale(), bypass_cache,
+      base::BindOnce(OnDownloadImage, max_image_size, callback));
+}
+
+void CefBrowserHostImpl::Print() {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    auto actionable_contents = GetActionableWebContents();
+    if (!actionable_contents)
+      return;
+
+    auto rfh = actionable_contents->GetMainFrame();
+
+    if (IsPrintPreviewSupported()) {
+      printing::CefPrintViewManager::FromWebContents(actionable_contents)
+          ->PrintPreviewNow(rfh, false);
+    } else {
+      printing::PrintViewManager::FromWebContents(actionable_contents)
+          ->PrintNow(rfh);
+    }
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::Print, this));
+  }
+}
+
+void CefBrowserHostImpl::PrintToPDF(const CefString& path,
+                                    const CefPdfPrintSettings& settings,
+                                    CefRefPtr<CefPdfPrintCallback> callback) {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    content::WebContents* actionable_contents = GetActionableWebContents();
+    if (!actionable_contents)
+      return;
+
+    printing::CefPrintViewManager::PdfPrintCallback pdf_callback;
+    if (callback.get()) {
+      pdf_callback = base::Bind(&CefPdfPrintCallback::OnPdfPrintFinished,
+                                callback.get(), path);
+    }
+    printing::CefPrintViewManager::FromWebContents(actionable_contents)
+        ->PrintToPDF(actionable_contents->GetMainFrame(), base::FilePath(path),
+                     settings, pdf_callback);
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::PrintToPDF, this,
+                                          path, settings, callback));
+  }
+}
+
+void CefBrowserHostImpl::Find(int identifier,
+                              const CefString& searchText,
+                              bool forward,
+                              bool matchCase,
+                              bool findNext) {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (!web_contents())
+      return;
+
+    // Every find request must have a unique ID and these IDs must strictly
+    // increase so that newer requests always have greater IDs than older
+    // requests.
+    if (identifier <= find_request_id_counter_)
+      identifier = ++find_request_id_counter_;
+    else
+      find_request_id_counter_ = identifier;
+
+    auto options = blink::mojom::FindOptions::New();
+    options->forward = forward;
+    options->match_case = matchCase;
+    options->find_next = findNext;
+    web_contents()->Find(identifier, searchText, std::move(options));
+  } else {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::Find, this, identifier,
+                                 searchText, forward, matchCase, findNext));
+  }
+}
+
+void CefBrowserHostImpl::StopFinding(bool clearSelection) {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (!web_contents())
+      return;
+
+    content::StopFindAction action =
+        clearSelection ? content::STOP_FIND_ACTION_CLEAR_SELECTION
+                       : content::STOP_FIND_ACTION_KEEP_SELECTION;
+    web_contents()->StopFinding(action);
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::StopFinding,
+                                          this, clearSelection));
+  }
+}
+
+void CefBrowserHostImpl::ShowDevTools(const CefWindowInfo& windowInfo,
+                                      CefRefPtr<CefClient> client,
+                                      const CefBrowserSettings& settings,
+                                      const CefPoint& inspect_element_at) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    ShowDevToolsHelper* helper = new ShowDevToolsHelper(
+        this, windowInfo, client, settings, inspect_element_at);
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(ShowDevToolsWithHelper, helper));
+    return;
+  }
+
+  if (!EnsureDevToolsManager())
+    return;
+  devtools_manager_->ShowDevTools(windowInfo, client, settings,
+                                  inspect_element_at);
+}
+
+void CefBrowserHostImpl::CloseDevTools() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::CloseDevTools, this));
+    return;
+  }
+
+  if (!devtools_manager_)
+    return;
+  devtools_manager_->CloseDevTools();
+}
+
+bool CefBrowserHostImpl::HasDevTools() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  if (!devtools_manager_)
+    return false;
+  return devtools_manager_->HasDevTools();
+}
+
+bool CefBrowserHostImpl::SendDevToolsMessage(const void* message,
+                                             size_t message_size) {
+  if (!message || message_size == 0)
+    return false;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    std::string message_str(static_cast<const char*>(message), message_size);
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(
+            [](CefRefPtr<CefBrowserHostImpl> self, std::string message_str) {
+              self->SendDevToolsMessage(message_str.data(), message_str.size());
+            },
+            CefRefPtr<CefBrowserHostImpl>(this), std::move(message_str)));
+    return false;
+  }
+
+  if (!EnsureDevToolsManager())
+    return false;
+  return devtools_manager_->SendDevToolsMessage(message, message_size);
+}
+
+int CefBrowserHostImpl::ExecuteDevToolsMethod(
+    int message_id,
+    const CefString& method,
+    CefRefPtr<CefDictionaryValue> params) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT, base::BindOnce(base::IgnoreResult(
+                                    &CefBrowserHostImpl::ExecuteDevToolsMethod),
+                                this, message_id, method, params));
+    return 0;
+  }
+
+  if (!EnsureDevToolsManager())
+    return 0;
+  return devtools_manager_->ExecuteDevToolsMethod(message_id, method, params);
+}
+
+CefRefPtr<CefRegistration> CefBrowserHostImpl::AddDevToolsMessageObserver(
+    CefRefPtr<CefDevToolsMessageObserver> observer) {
+  if (!observer)
+    return nullptr;
+  auto registration = CefDevToolsManager::CreateRegistration(observer);
+  InitializeDevToolsRegistrationOnUIThread(registration);
+  return registration.get();
+}
+
+bool CefBrowserHostImpl::EnsureDevToolsManager() {
+  CEF_REQUIRE_UIT();
+  if (!web_contents())
+    return false;
+
+  if (!devtools_manager_) {
+    devtools_manager_.reset(new CefDevToolsManager(this));
+  }
+  return true;
+}
+
+void CefBrowserHostImpl::InitializeDevToolsRegistrationOnUIThread(
+    CefRefPtr<CefRegistration> registration) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(
+            &CefBrowserHostImpl::InitializeDevToolsRegistrationOnUIThread, this,
+            registration));
+    return;
+  }
+
+  if (!EnsureDevToolsManager())
+    return;
+  devtools_manager_->InitializeRegistrationOnUIThread(registration);
+}
+
+void CefBrowserHostImpl::GetNavigationEntries(
+    CefRefPtr<CefNavigationEntryVisitor> visitor,
+    bool current_only) {
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT, base::BindOnce(&CefBrowserHostImpl::GetNavigationEntries, this,
+                                visitor, current_only));
+    return;
+  }
+
+  if (!web_contents())
+    return;
+
+  content::NavigationController& controller = web_contents()->GetController();
+  const int total = controller.GetEntryCount();
+  const int current = controller.GetCurrentEntryIndex();
+
+  if (current_only) {
+    // Visit only the current entry.
+    CefRefPtr<CefNavigationEntryImpl> entry =
+        new CefNavigationEntryImpl(controller.GetEntryAtIndex(current));
+    visitor->Visit(entry.get(), true, current, total);
+    entry->Detach(nullptr);
+  } else {
+    // Visit all entries.
+    bool cont = true;
+    for (int i = 0; i < total && cont; ++i) {
+      CefRefPtr<CefNavigationEntryImpl> entry =
+          new CefNavigationEntryImpl(controller.GetEntryAtIndex(i));
+      cont = visitor->Visit(entry.get(), (i == current), i, total);
+      entry->Detach(nullptr);
+    }
+  }
+}
+
+CefRefPtr<CefNavigationEntry> CefBrowserHostImpl::GetVisibleNavigationEntry() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  content::NavigationEntry* entry = nullptr;
+  if (web_contents())
+    entry = web_contents()->GetController().GetVisibleEntry();
+
+  if (!entry)
+    return nullptr;
+
+  return new CefNavigationEntryImpl(entry);
+}
+
+void CefBrowserHostImpl::SetAccessibilityState(
+    cef_state_t accessibility_state) {
+  // Do nothing if state is set to default. It'll be disabled by default and
+  // controlled by the commmand-line flags "force-renderer-accessibility" and
+  // "disable-renderer-accessibility".
+  if (accessibility_state == STATE_DEFAULT)
+    return;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::SetAccessibilityState,
+                                 this, accessibility_state));
+    return;
+  }
+
+  content::WebContentsImpl* web_contents_impl =
+      static_cast<content::WebContentsImpl*>(web_contents());
+
+  if (!web_contents_impl)
+    return;
+
+  ui::AXMode accMode;
+  // In windowless mode set accessibility to TreeOnly mode. Else native
+  // accessibility APIs, specific to each platform, are also created.
+  if (accessibility_state == STATE_ENABLED) {
+    accMode = IsWindowless() ? ui::kAXModeWebContentsOnly : ui::kAXModeComplete;
+  }
+  web_contents_impl->SetAccessibilityMode(accMode);
+}
+
+void CefBrowserHostImpl::SetAutoResizeEnabled(bool enabled,
+                                              const CefSize& min_size,
+                                              const CefSize& max_size) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SetAutoResizeEnabled, this,
+                                enabled, min_size, max_size));
+    return;
+  }
+
+  if (enabled == auto_resize_enabled_)
+    return;
+
+  auto_resize_enabled_ = enabled;
+  if (enabled) {
+    auto_resize_min_ = gfx::Size(min_size.width, min_size.height);
+    auto_resize_max_ = gfx::Size(max_size.width, max_size.height);
+  } else {
+    auto_resize_min_ = auto_resize_max_ = gfx::Size();
+  }
+  ConfigureAutoResize();
+}
+
+CefRefPtr<CefExtension> CefBrowserHostImpl::GetExtension() {
+  return extension_;
+}
+
+bool CefBrowserHostImpl::IsBackgroundHost() {
+  return is_background_host_;
+}
+
+void CefBrowserHostImpl::SetMouseCursorChangeDisabled(bool disabled) {
+  base::AutoLock lock_scope(state_lock_);
+  mouse_cursor_change_disabled_ = disabled;
+}
+
+bool CefBrowserHostImpl::IsMouseCursorChangeDisabled() {
+  base::AutoLock lock_scope(state_lock_);
+  return mouse_cursor_change_disabled_;
+}
+
+bool CefBrowserHostImpl::IsWindowRenderingDisabled() {
+  return IsWindowless();
+}
+
+void CefBrowserHostImpl::ReplaceMisspelling(const CefString& word) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::ReplaceMisspelling, this, word));
+    return;
+  }
+
+  if (web_contents())
+    web_contents()->ReplaceMisspelling(word);
+}
+
+void CefBrowserHostImpl::AddWordToDictionary(const CefString& word) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::AddWordToDictionary, this, word));
+    return;
+  }
+
+  if (!web_contents())
+    return;
+
+  SpellcheckService* spellcheck = nullptr;
+  content::BrowserContext* browser_context =
+      web_contents()->GetBrowserContext();
+  if (browser_context) {
+    spellcheck = SpellcheckServiceFactory::GetForContext(browser_context);
+    if (spellcheck)
+      spellcheck->GetCustomDictionary()->AddWord(word);
+  }
+#if defined(OS_MACOSX)
+  if (spellcheck && spellcheck::UseBrowserSpellChecker()) {
+    spellcheck_platform::AddWord(spellcheck->platform_spell_checker(), word);
+  }
+#endif
+}
+
+void CefBrowserHostImpl::WasResized() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::WasResized, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->WasResized();
+}
+
+void CefBrowserHostImpl::WasHidden(bool hidden) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHost::WasHidden, this, hidden));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->WasHidden(hidden);
+}
+
+void CefBrowserHostImpl::NotifyScreenInfoChanged() {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::NotifyScreenInfoChanged, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->NotifyScreenInfoChanged();
+}
+
+void CefBrowserHostImpl::Invalidate(PaintElementType type) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::Invalidate, this, type));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->Invalidate(type);
+}
+
+void CefBrowserHostImpl::SendExternalBeginFrame() {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT, base::Bind(&CefBrowserHostImpl::SendExternalBeginFrame, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->SendExternalBeginFrame();
+}
+
+void CefBrowserHostImpl::SendKeyEvent(const CefKeyEvent& event) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendKeyEvent,
+                                          this, event));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->SendKeyEvent(event);
+}
+
+void CefBrowserHostImpl::SendMouseClickEvent(const CefMouseEvent& event,
+                                             MouseButtonType type,
+                                             bool mouseUp,
+                                             int clickCount) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::SendMouseClickEvent, this,
+                                 event, type, mouseUp, clickCount));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->SendMouseClickEvent(event, type, mouseUp, clickCount);
+}
+
+void CefBrowserHostImpl::SendMouseMoveEvent(const CefMouseEvent& event,
+                                            bool mouseLeave) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::SendMouseMoveEvent, this,
+                                 event, mouseLeave));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->SendMouseMoveEvent(event, mouseLeave);
+}
+
+void CefBrowserHostImpl::SendMouseWheelEvent(const CefMouseEvent& event,
+                                             int deltaX,
+                                             int deltaY) {
+  if (deltaX == 0 && deltaY == 0) {
+    // Nothing to do.
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::SendMouseWheelEvent, this,
+                                 event, deltaX, deltaY));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->SendMouseWheelEvent(event, deltaX, deltaY);
+}
+
+void CefBrowserHostImpl::SendFocusEvent(bool setFocus) {
+  SetFocus(setFocus);
+}
+
+void CefBrowserHostImpl::SendCaptureLostEvent() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::SendCaptureLostEvent, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->SendCaptureLostEvent();
+}
+
+void CefBrowserHostImpl::NotifyMoveOrResizeStarted() {
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::NotifyMoveOrResizeStarted, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->NotifyMoveOrResizeStarted();
+#endif
+}
+
+int CefBrowserHostImpl::GetWindowlessFrameRate() {
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return 0;
+  }
+
+  return osr_util::ClampFrameRate(settings_.windowless_frame_rate);
+}
+
+void CefBrowserHostImpl::SetWindowlessFrameRate(int frame_rate) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::SetWindowlessFrameRate,
+                                 this, frame_rate));
+    return;
+  }
+
+  settings_.windowless_frame_rate = frame_rate;
+
+  if (platform_delegate_)
+    platform_delegate_->SetWindowlessFrameRate(frame_rate);
+}
+
+// CefBrowser methods.
+// -----------------------------------------------------------------------------
+
+CefRefPtr<CefBrowserHost> CefBrowserHostImpl::GetHost() {
+  return this;
+}
+
+bool CefBrowserHostImpl::CanGoBack() {
+  base::AutoLock lock_scope(state_lock_);
+  return can_go_back_;
+}
+
+void CefBrowserHostImpl::GoBack() {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (navigation_locked()) {
+      // Try again after the lock has been released.
+      set_pending_navigation_action(
+          base::BindOnce(&CefBrowserHostImpl::GoBack, this));
+      return;
+    }
+
+    if (web_contents() && web_contents()->GetController().CanGoBack())
+      web_contents()->GetController().GoBack();
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::GoBack, this));
+  }
+}
+
+bool CefBrowserHostImpl::CanGoForward() {
+  base::AutoLock lock_scope(state_lock_);
+  return can_go_forward_;
+}
+
+void CefBrowserHostImpl::GoForward() {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (navigation_locked()) {
+      // Try again after the lock has been released.
+      set_pending_navigation_action(
+          base::BindOnce(&CefBrowserHostImpl::GoForward, this));
+      return;
+    }
+
+    if (web_contents() && web_contents()->GetController().CanGoForward())
+      web_contents()->GetController().GoForward();
+  } else {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::GoForward, this));
+  }
+}
+
+bool CefBrowserHostImpl::IsLoading() {
+  base::AutoLock lock_scope(state_lock_);
+  return is_loading_;
+}
+
+void CefBrowserHostImpl::Reload() {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (navigation_locked()) {
+      // Try again after the lock has been released.
+      set_pending_navigation_action(
+          base::BindOnce(&CefBrowserHostImpl::Reload, this));
+      return;
+    }
+
+    if (web_contents())
+      web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::Reload, this));
+  }
+}
+
+void CefBrowserHostImpl::ReloadIgnoreCache() {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (navigation_locked()) {
+      // Try again after the lock has been released.
+      set_pending_navigation_action(
+          base::BindOnce(&CefBrowserHostImpl::ReloadIgnoreCache, this));
+      return;
+    }
+
+    if (web_contents()) {
+      web_contents()->GetController().Reload(
+          content::ReloadType::BYPASSING_CACHE, true);
+    }
+  } else {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::ReloadIgnoreCache, this));
+  }
+}
+
+void CefBrowserHostImpl::StopLoad() {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    if (navigation_locked()) {
+      // Try again after the lock has been released.
+      set_pending_navigation_action(
+          base::BindOnce(&CefBrowserHostImpl::StopLoad, this));
+      return;
+    }
+
+    if (web_contents())
+      web_contents()->Stop();
+  } else {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::StopLoad, this));
+  }
+}
+
+int CefBrowserHostImpl::GetIdentifier() {
+  return browser_id();
+}
+
+bool CefBrowserHostImpl::IsSame(CefRefPtr<CefBrowser> that) {
+  CefBrowserHostImpl* impl = static_cast<CefBrowserHostImpl*>(that.get());
+  return (impl == this);
+}
+
+bool CefBrowserHostImpl::IsPopup() {
+  return browser_info_->is_popup();
+}
+
+bool CefBrowserHostImpl::HasDocument() {
+  base::AutoLock lock_scope(state_lock_);
+  return has_document_;
+}
+
+CefRefPtr<CefFrame> CefBrowserHostImpl::GetMainFrame() {
+  return GetFrame(CefFrameHostImpl::kMainFrameId);
+}
+
+CefRefPtr<CefFrame> CefBrowserHostImpl::GetFocusedFrame() {
+  return GetFrame(CefFrameHostImpl::kFocusedFrameId);
+}
+
+CefRefPtr<CefFrame> CefBrowserHostImpl::GetFrame(int64 identifier) {
+  if (identifier == CefFrameHostImpl::kInvalidFrameId) {
+    return nullptr;
+  } else if (identifier == CefFrameHostImpl::kMainFrameId) {
+    return browser_info_->GetMainFrame();
+  } else if (identifier == CefFrameHostImpl::kFocusedFrameId) {
+    base::AutoLock lock_scope(state_lock_);
+    if (!focused_frame_) {
+      // The main frame is focused by default.
+      return browser_info_->GetMainFrame();
+    }
+    return focused_frame_;
+  }
+
+  return browser_info_->GetFrameForId(identifier);
+}
+
+CefRefPtr<CefFrame> CefBrowserHostImpl::GetFrame(const CefString& name) {
+  for (const auto& frame : browser_info_->GetAllFrames()) {
+    if (frame->GetName() == name)
+      return frame;
+  }
+  return nullptr;
+}
+
+size_t CefBrowserHostImpl::GetFrameCount() {
+  return browser_info_->GetAllFrames().size();
+}
+
+void CefBrowserHostImpl::GetFrameIdentifiers(std::vector<int64>& identifiers) {
+  if (identifiers.size() > 0)
+    identifiers.clear();
+
+  const auto frames = browser_info_->GetAllFrames();
+  if (frames.empty())
+    return;
+
+  identifiers.reserve(frames.size());
+  for (const auto& frame : frames) {
+    identifiers.push_back(frame->GetIdentifier());
+  }
+}
+
+void CefBrowserHostImpl::GetFrameNames(std::vector<CefString>& names) {
+  if (names.size() > 0)
+    names.clear();
+
+  const auto frames = browser_info_->GetAllFrames();
+  if (frames.empty())
+    return;
+
+  names.reserve(frames.size());
+  for (const auto& frame : frames) {
+    names.push_back(frame->GetName());
+  }
+}
+
+// CefBrowserHostImpl public methods.
+// -----------------------------------------------------------------------------
+
+bool CefBrowserHostImpl::IsWindowless() const {
+  return is_windowless_;
+}
+
+bool CefBrowserHostImpl::IsViewsHosted() const {
+  return is_views_hosted_;
+}
+
+bool CefBrowserHostImpl::IsPrintPreviewSupported() const {
+  CEF_REQUIRE_UIT();
+  auto actionable_contents = GetActionableWebContents();
+  if (!actionable_contents)
+    return false;
+
+  if (!CefBrowserContext::GetForContext(
+           actionable_contents->GetBrowserContext())
+           ->IsPrintPreviewSupported()) {
+    return false;
+  }
+
+  // Print preview is not currently supported with OSR.
+  return !IsWindowless();
+}
+
+bool CefBrowserHostImpl::IsPictureInPictureSupported() const {
+  // Not currently supported with OSR.
+  return !IsWindowless();
+}
+
+void CefBrowserHostImpl::WindowDestroyed() {
+  CEF_REQUIRE_UIT();
+  DCHECK(!window_destroyed_);
+  window_destroyed_ = true;
+  CloseBrowser(true);
+}
+
+void CefBrowserHostImpl::DestroyBrowser() {
+  CEF_REQUIRE_UIT();
+
+  destruction_state_ = DESTRUCTION_STATE_COMPLETED;
+
+  // Notify that this browser has been destroyed. These must be delivered in
+  // the expected order.
+
+  // 1. Notify the platform delegate. With Views this will result in a call to
+  // CefBrowserViewDelegate::OnBrowserDestroyed().
+  platform_delegate_->NotifyBrowserDestroyed();
+
+  // 2. Notify the browser's LifeSpanHandler. This must always be the last
+  // notification for this browser.
+  if (client_.get()) {
+    CefRefPtr<CefLifeSpanHandler> handler = client_->GetLifeSpanHandler();
+    if (handler.get()) {
+      // Notify the handler that the window is about to be closed.
+      handler->OnBeforeClose(this);
+    }
+  }
+
+  // Destroy any platform constructs first.
+  if (file_dialog_manager_.get())
+    file_dialog_manager_->Destroy();
+  if (javascript_dialog_manager_.get())
+    javascript_dialog_manager_->Destroy();
+  if (menu_manager_.get())
+    menu_manager_->Destroy();
+  DestroyExtensionHost();
+
+  // Notify any observers that may have state associated with this browser.
+  for (auto& observer : observers_)
+    observer.OnBrowserDestroyed(this);
+
+  // Disassociate the platform delegate from this browser.
+  platform_delegate_->BrowserDestroyed(this);
+
+  registrar_.reset(nullptr);
+  content::WebContentsObserver::Observe(nullptr);
+  if (owned_web_contents_)
+    owned_web_contents_.reset(nullptr);
+
+  // Delete objects created by the platform delegate that may be referenced by
+  // the WebContents.
+  file_dialog_manager_.reset(nullptr);
+  javascript_dialog_manager_.reset(nullptr);
+  menu_manager_.reset(nullptr);
+
+  // Delete the audio capturer
+  recently_audible_timer_.Stop();
+  audio_capturer_.reset(nullptr);
+
+  devtools_manager_.reset(nullptr);
+
+  // Delete the platform delegate.
+  platform_delegate_.reset(nullptr);
+
+  CefBrowserInfoManager::GetInstance()->RemoveBrowserInfo(browser_info_);
+  browser_info_->SetBrowser(nullptr);
+}
+
+#if defined(USE_AURA)
+views::Widget* CefBrowserHostImpl::GetWindowWidget() const {
+  CEF_REQUIRE_UIT();
+  if (!platform_delegate_)
+    return nullptr;
+  return platform_delegate_->GetWindowWidget();
+}
+
+CefRefPtr<CefBrowserView> CefBrowserHostImpl::GetBrowserView() const {
+  CEF_REQUIRE_UIT();
+  if (IsViewsHosted() && platform_delegate_)
+    return platform_delegate_->GetBrowserView();
+  return nullptr;
+}
+#endif
+
+void CefBrowserHostImpl::CancelContextMenu() {
+  CEF_REQUIRE_UIT();
+  if (menu_manager_)
+    menu_manager_->CancelContextMenu();
+}
+
+CefRefPtr<CefFrame> CefBrowserHostImpl::GetFrameForHost(
+    const content::RenderFrameHost* host) {
+  CEF_REQUIRE_UIT();
+  if (!host)
+    return nullptr;
+
+  return browser_info_->GetFrameForHost(host);
+}
+
+CefRefPtr<CefFrame> CefBrowserHostImpl::GetFrameForFrameTreeNode(
+    int frame_tree_node_id) {
+  return browser_info_->GetFrameForFrameTreeNode(frame_tree_node_id, nullptr);
+}
+
+void CefBrowserHostImpl::LoadMainFrameURL(const std::string& url,
+                                          const content::Referrer& referrer,
+                                          ui::PageTransition transition,
+                                          const std::string& extra_headers) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::LoadMainFrameURL, this,
+                                 url, referrer, transition, extra_headers));
+    return;
+  }
+
+  // Go through the navigation controller.
+  if (navigation_locked()) {
+    // Try again after the lock has been released.
+    set_pending_navigation_action(
+        base::BindOnce(&CefBrowserHostImpl::LoadMainFrameURL, this, url,
+                       referrer, transition, extra_headers));
+    return;
+  }
+
+  if (web_contents()) {
+    GURL gurl = GURL(url);
+
+    if (!gurl.is_valid() && !gurl.has_scheme()) {
+      // Try to add "http://" at the beginning
+      std::string new_url = std::string("http://") + url;
+      gurl = GURL(new_url);
+    }
+
+    if (!gurl.is_valid()) {
+      LOG(ERROR)
+          << "Invalid URL passed to CefBrowserHostImpl::LoadMainFrameURL: "
+          << url;
+      return;
+    }
+
+    web_contents()->GetController().LoadURL(gurl, referrer, transition,
+                                            extra_headers);
+    OnSetFocus(FOCUS_SOURCE_NAVIGATION);
+  }
+}
+
+void CefBrowserHostImpl::OnDidFinishLoad(CefRefPtr<CefFrameHostImpl> frame,
+                                         const GURL& validated_url,
+                                         int http_status_code) {
+  frame->RefreshAttributes();
+
+  // Give internal scheme handlers an opportunity to update content.
+  scheme::DidFinishLoad(frame, validated_url);
+
+  OnLoadEnd(frame, validated_url, http_status_code);
+}
+
+void CefBrowserHostImpl::ViewText(const std::string& text) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::ViewText, this, text));
+    return;
+  }
+
+  if (platform_delegate_)
+    platform_delegate_->ViewText(text);
+}
+
+SkColor CefBrowserHostImpl::GetBackgroundColor() const {
+  // Don't use |platform_delegate_| because it's not thread-safe.
+  return CefContext::Get()->GetBackgroundColor(
+      &settings_, is_windowless_ ? STATE_ENABLED : STATE_DISABLED);
+}
+
+int CefBrowserHostImpl::browser_id() const {
+  return browser_info_->browser_id();
+}
+
+content::BrowserContext* CefBrowserHostImpl::GetBrowserContext() {
+  CEF_REQUIRE_UIT();
+  if (web_contents())
+    return web_contents()->GetBrowserContext();
+  return nullptr;
+}
+
+void CefBrowserHostImpl::OnSetFocus(cef_focus_source_t source) {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    // SetFocus() might be called while inside the OnSetFocus() callback. If
+    // so, don't re-enter the callback.
+    if (!is_in_onsetfocus_) {
+      if (client_.get()) {
+        CefRefPtr<CefFocusHandler> handler = client_->GetFocusHandler();
+        if (handler.get()) {
+          is_in_onsetfocus_ = true;
+          bool handled = handler->OnSetFocus(this, source);
+          is_in_onsetfocus_ = false;
+
+          if (handled)
+            return;
+        }
+      }
+    }
+
+    if (platform_delegate_)
+      platform_delegate_->SendFocusEvent(true);
+  } else {
+    CEF_POST_TASK(
+        CEF_UIT, base::BindOnce(&CefBrowserHostImpl::OnSetFocus, this, source));
+  }
+}
+
+void CefBrowserHostImpl::RunFileChooser(
+    const CefFileDialogRunner::FileChooserParams& params,
+    CefFileDialogRunner::RunFileChooserCallback callback) {
+  EnsureFileDialogManager();
+  file_dialog_manager_->RunFileChooser(params, std::move(callback));
+}
+
+bool CefBrowserHostImpl::EmbedsFullscreenWidget() {
+  // When using windowless rendering do not allow Flash to create its own
+  // full- screen widget.
+  return IsWindowless();
+}
+
+void CefBrowserHostImpl::EnterFullscreenModeForTab(
+    content::WebContents* web_contents,
+    const GURL& origin,
+    const blink::mojom::FullscreenOptions& options) {
+  OnFullscreenModeChange(true);
+}
+
+void CefBrowserHostImpl::ExitFullscreenModeForTab(
+    content::WebContents* web_contents) {
+  OnFullscreenModeChange(false);
+}
+
+bool CefBrowserHostImpl::IsFullscreenForTabOrPending(
+    const content::WebContents* web_contents) {
+  return is_fullscreen_;
+}
+
+blink::mojom::DisplayMode CefBrowserHostImpl::GetDisplayMode(
+    const content::WebContents* web_contents) {
+  return is_fullscreen_ ? blink::mojom::DisplayMode::kFullscreen
+                        : blink::mojom::DisplayMode::kBrowser;
+}
+
+void CefBrowserHostImpl::FindReply(content::WebContents* web_contents,
+                                   int request_id,
+                                   int number_of_matches,
+                                   const gfx::Rect& selection_rect,
+                                   int active_match_ordinal,
+                                   bool final_update) {
+  if (client_.get()) {
+    CefRefPtr<CefFindHandler> handler = client_->GetFindHandler();
+    if (handler.get()) {
+      CefRect rect(selection_rect.x(), selection_rect.y(),
+                   selection_rect.width(), selection_rect.height());
+      handler->OnFindResult(this, request_id, number_of_matches, rect,
+                            active_match_ordinal, final_update);
+    }
+  }
+}
+
+void CefBrowserHostImpl::ImeSetComposition(
+    const CefString& text,
+    const std::vector<CefCompositionUnderline>& underlines,
+    const CefRange& replacement_range,
+    const CefRange& selection_range) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::ImeSetComposition, this, text,
+                       underlines, replacement_range, selection_range));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->ImeSetComposition(text, underlines, replacement_range,
+                                        selection_range);
+}
+
+void CefBrowserHostImpl::ImeCommitText(const CefString& text,
+                                       const CefRange& replacement_range,
+                                       int relative_cursor_pos) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::ImeCommitText, this, text,
+                                 replacement_range, relative_cursor_pos));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->ImeCommitText(text, replacement_range,
+                                    relative_cursor_pos);
+}
+
+void CefBrowserHostImpl::ImeFinishComposingText(bool keep_selection) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::ImeFinishComposingText,
+                                 this, keep_selection));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->ImeFinishComposingText(keep_selection);
+}
+
+void CefBrowserHostImpl::ImeCancelComposition() {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::ImeCancelComposition, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->ImeCancelComposition();
+}
+
+void CefBrowserHostImpl::DragTargetDragEnter(
+    CefRefPtr<CefDragData> drag_data,
+    const CefMouseEvent& event,
+    CefBrowserHost::DragOperationsMask allowed_ops) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::DragTargetDragEnter, this,
+                                 drag_data, event, allowed_ops));
+    return;
+  }
+
+  if (!drag_data.get()) {
+    NOTREACHED();
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->DragTargetDragEnter(drag_data, event, allowed_ops);
+}
+
+void CefBrowserHostImpl::DragTargetDragOver(
+    const CefMouseEvent& event,
+    CefBrowserHost::DragOperationsMask allowed_ops) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefBrowserHostImpl::DragTargetDragOver, this,
+                                 event, allowed_ops));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->DragTargetDragOver(event, allowed_ops);
+}
+
+void CefBrowserHostImpl::DragTargetDragLeave() {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(
+                               &CefBrowserHostImpl::DragTargetDragLeave, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->DragTargetDragLeave();
+}
+
+void CefBrowserHostImpl::DragTargetDrop(const CefMouseEvent& event) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::DragTargetDrop,
+                                          this, event));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->DragTargetDrop(event);
+}
+
+void CefBrowserHostImpl::DragSourceSystemDragEnded() {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::DragSourceSystemDragEnded, this));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->DragSourceSystemDragEnded();
+}
+
+void CefBrowserHostImpl::DragSourceEndedAt(
+    int x,
+    int y,
+    CefBrowserHost::DragOperationsMask op) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserHostImpl::DragSourceEndedAt, this, x, y, op));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->DragSourceEndedAt(x, y, op);
+}
+
+void CefBrowserHostImpl::SetAudioMuted(bool mute) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefBrowserHostImpl::SetAudioMuted, this, mute));
+    return;
+  }
+  if (!web_contents())
+    return;
+  web_contents()->SetAudioMuted(mute);
+}
+
+bool CefBrowserHostImpl::IsAudioMuted() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+  if (!web_contents())
+    return false;
+  return web_contents()->IsAudioMuted();
+}
+
+// content::WebContentsDelegate methods.
+// -----------------------------------------------------------------------------
+
+// |source| may be NULL if the navigation originates from a guest view via
+// CefContentBrowserClient::CanCreateWindow.
+content::WebContents* CefBrowserHostImpl::OpenURLFromTab(
+    content::WebContents* source,
+    const content::OpenURLParams& params) {
+  bool cancel = false;
+
+  if (client_.get()) {
+    CefRefPtr<CefRequestHandler> handler = client_->GetRequestHandler();
+    if (handler.get()) {
+      cancel = handler->OnOpenURLFromTab(
+          this, GetFrame(params.frame_tree_node_id), params.url.spec(),
+          static_cast<cef_window_open_disposition_t>(params.disposition),
+          params.user_gesture);
+    }
+  }
+
+  if (!cancel) {
+    // Start a navigation in the current browser that will result in the
+    // creation of a new render process.
+    LoadMainFrameURL(params.url.spec(), params.referrer, params.transition,
+                     params.extra_headers);
+    return source;
+  }
+
+  // We don't know where the navigation, if any, will occur.
+  return nullptr;
+}
+
+bool CefBrowserHostImpl::ShouldTransferNavigation(
+    bool is_main_frame_navigation) {
+  if (extension_host_) {
+    return extension_host_->ShouldTransferNavigation(is_main_frame_navigation);
+  }
+  return true;
+}
+
+void CefBrowserHostImpl::AddNewContents(
+    content::WebContents* source,
+    std::unique_ptr<content::WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture,
+    bool* was_blocked) {
+  CefRefPtr<CefBrowserHostImpl> owner =
+      GetBrowserForContents(new_contents.get());
+  if (owner) {
+    // Taking ownership of |new_contents|.
+    owner->set_owned_web_contents(new_contents.release());
+    return;
+  }
+
+  if (extension_host_) {
+    extension_host_->AddNewContents(source, std::move(new_contents),
+                                    disposition, initial_rect, user_gesture,
+                                    was_blocked);
+  }
+}
+
+void CefBrowserHostImpl::LoadingStateChanged(content::WebContents* source,
+                                             bool to_different_document) {
+  const int current_index =
+      source->GetController().GetLastCommittedEntryIndex();
+  const int max_index = source->GetController().GetEntryCount() - 1;
+
+  const bool is_loading = source->IsLoading();
+  const bool can_go_back = (current_index > 0);
+  const bool can_go_forward = (current_index < max_index);
+
+  {
+    base::AutoLock lock_scope(state_lock_);
+
+    // This method may be called multiple times in a row with |is_loading|
+    // true as a result of https://crrev.com/5e750ad0. Ignore the 2nd+ times.
+    if (is_loading_ == is_loading && can_go_back_ == can_go_back &&
+        can_go_forward_ == can_go_forward) {
+      return;
+    }
+
+    is_loading_ = is_loading;
+    can_go_back_ = can_go_back;
+    can_go_forward_ = can_go_forward;
+  }
+
+  if (client_.get()) {
+    CefRefPtr<CefLoadHandler> handler = client_->GetLoadHandler();
+    if (handler.get()) {
+      handler->OnLoadingStateChange(this, is_loading, can_go_back,
+                                    can_go_forward);
+    }
+  }
+}
+
+void CefBrowserHostImpl::LoadProgressChanged(double progress) {
+  if (client_.get()) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler.get()) {
+      handler->OnLoadingProgressChange(this, progress);
+    }
+  }
+}
+
+void CefBrowserHostImpl::CloseContents(content::WebContents* source) {
+  CEF_REQUIRE_UIT();
+
+  if (destruction_state_ == DESTRUCTION_STATE_COMPLETED)
+    return;
+
+  bool close_browser = true;
+
+  // If this method is called in response to something other than
+  // WindowDestroyed() ask the user if the browser should close.
+  if (client_.get() && (IsWindowless() || !window_destroyed_)) {
+    CefRefPtr<CefLifeSpanHandler> handler = client_->GetLifeSpanHandler();
+    if (handler.get()) {
+      close_browser = !handler->DoClose(this);
+    }
+  }
+
+  if (close_browser) {
+    if (destruction_state_ != DESTRUCTION_STATE_ACCEPTED)
+      destruction_state_ = DESTRUCTION_STATE_ACCEPTED;
+
+    if (!IsWindowless() && !window_destroyed_) {
+      // A window exists so try to close it using the platform method. Will
+      // result in a call to WindowDestroyed() if/when the window is destroyed
+      // via the platform window destruction mechanism.
+      platform_delegate_->CloseHostWindow();
+    } else {
+      // Keep a reference to the browser while it's in the process of being
+      // destroyed.
+      CefRefPtr<CefBrowserHostImpl> browser(this);
+
+      // No window exists. Destroy the browser immediately. Don't call other
+      // browser methods after calling DestroyBrowser().
+      DestroyBrowser();
+    }
+  } else if (destruction_state_ != DESTRUCTION_STATE_NONE) {
+    destruction_state_ = DESTRUCTION_STATE_NONE;
+  }
+}
+
+void CefBrowserHostImpl::UpdateTargetURL(content::WebContents* source,
+                                         const GURL& url) {
+  if (client_.get()) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler.get())
+      handler->OnStatusMessage(this, url.spec());
+  }
+}
+
+bool CefBrowserHostImpl::DidAddMessageToConsole(
+    content::WebContents* source,
+    blink::mojom::ConsoleMessageLevel level,
+    const base::string16& message,
+    int32_t line_no,
+    const base::string16& source_id) {
+  if (client_.get()) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler.get()) {
+      // Use LOGSEVERITY_DEBUG for unrecognized |level| values.
+      cef_log_severity_t log_level = LOGSEVERITY_DEBUG;
+      switch (level) {
+        case blink::mojom::ConsoleMessageLevel::kVerbose:
+          log_level = LOGSEVERITY_DEBUG;
+          break;
+        case blink::mojom::ConsoleMessageLevel::kInfo:
+          log_level = LOGSEVERITY_INFO;
+          break;
+        case blink::mojom::ConsoleMessageLevel::kWarning:
+          log_level = LOGSEVERITY_WARNING;
+          break;
+        case blink::mojom::ConsoleMessageLevel::kError:
+          log_level = LOGSEVERITY_ERROR;
+          break;
+      }
+
+      return handler->OnConsoleMessage(this, log_level, message, source_id,
+                                       line_no);
+    }
+  }
+
+  return false;
+}
+
+void CefBrowserHostImpl::BeforeUnloadFired(content::WebContents* source,
+                                           bool proceed,
+                                           bool* proceed_to_fire_unload) {
+  if (destruction_state_ == DESTRUCTION_STATE_ACCEPTED || proceed) {
+    *proceed_to_fire_unload = true;
+  } else if (!proceed) {
+    *proceed_to_fire_unload = false;
+    destruction_state_ = DESTRUCTION_STATE_NONE;
+  }
+}
+
+bool CefBrowserHostImpl::TakeFocus(content::WebContents* source, bool reverse) {
+  if (client_.get()) {
+    CefRefPtr<CefFocusHandler> handler = client_->GetFocusHandler();
+    if (handler.get())
+      handler->OnTakeFocus(this, !reverse);
+  }
+
+  return false;
+}
+
+bool CefBrowserHostImpl::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
+    const content::ContextMenuParams& params) {
+  return HandleContextMenu(web_contents(), params);
+}
+
+content::WebContents* CefBrowserHostImpl::GetActionableWebContents() const {
+  if (web_contents() && extensions::ExtensionsEnabled()) {
+    content::WebContents* guest_contents =
+        extensions::GetFullPageGuestForOwnerContents(web_contents());
+    if (guest_contents)
+      return guest_contents;
+  }
+  return web_contents();
+}
+
+KeyboardEventProcessingResult CefBrowserHostImpl::PreHandleKeyboardEvent(
+    content::WebContents* source,
+    const content::NativeWebKeyboardEvent& event) {
+  if (platform_delegate_ && client_.get()) {
+    CefRefPtr<CefKeyboardHandler> handler = client_->GetKeyboardHandler();
+    if (handler.get()) {
+      CefKeyEvent cef_event;
+      if (browser_util::GetCefKeyEvent(event, cef_event)) {
+        cef_event.focus_on_editable_field = focus_on_editable_field_;
+
+        CefEventHandle event_handle = platform_delegate_->GetEventHandle(event);
+        bool is_keyboard_shortcut = false;
+        bool result = handler->OnPreKeyEvent(this, cef_event, event_handle,
+                                             &is_keyboard_shortcut);
+        if (result)
+          return KeyboardEventProcessingResult::HANDLED;
+        else if (is_keyboard_shortcut)
+          return KeyboardEventProcessingResult::NOT_HANDLED_IS_SHORTCUT;
+      }
+    }
+  }
+
+  return KeyboardEventProcessingResult::NOT_HANDLED;
+}
+
+bool CefBrowserHostImpl::HandleKeyboardEvent(
+    content::WebContents* source,
+    const content::NativeWebKeyboardEvent& event) {
+  // Check to see if event should be ignored.
+  if (event.skip_in_browser)
+    return false;
+
+  if (!platform_delegate_)
+    return false;
+
+  if (client_.get()) {
+    CefRefPtr<CefKeyboardHandler> handler = client_->GetKeyboardHandler();
+    if (handler.get()) {
+      CefKeyEvent cef_event;
+      if (browser_util::GetCefKeyEvent(event, cef_event)) {
+        cef_event.focus_on_editable_field = focus_on_editable_field_;
+
+        CefEventHandle event_handle = platform_delegate_->GetEventHandle(event);
+        if (handler->OnKeyEvent(this, cef_event, event_handle))
+          return true;
+      }
+    }
+  }
+
+  return platform_delegate_->HandleKeyboardEvent(event);
+}
+
+bool CefBrowserHostImpl::PreHandleGestureEvent(
+    content::WebContents* source,
+    const blink::WebGestureEvent& event) {
+  if (extension_host_)
+    return extension_host_->PreHandleGestureEvent(source, event);
+  return false;
+}
+
+bool CefBrowserHostImpl::CanDragEnter(content::WebContents* source,
+                                      const content::DropData& data,
+                                      blink::WebDragOperationsMask mask) {
+  CefRefPtr<CefDragHandler> handler = client_->GetDragHandler();
+  if (handler.get()) {
+    CefRefPtr<CefDragDataImpl> drag_data(new CefDragDataImpl(data));
+    drag_data->SetReadOnly(true);
+    if (handler->OnDragEnter(
+            this, drag_data.get(),
+            static_cast<CefDragHandler::DragOperationsMask>(mask))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void CefBrowserHostImpl::GetCustomWebContentsView(
+    content::WebContents* web_contents,
+    const GURL& target_url,
+    int opener_render_process_id,
+    int opener_render_frame_id,
+    content::WebContentsView** view,
+    content::RenderViewHostDelegateView** delegate_view) {
+  CefBrowserInfoManager::GetInstance()->GetCustomWebContentsView(
+      target_url, opener_render_process_id, opener_render_frame_id, view,
+      delegate_view);
+}
+
+void CefBrowserHostImpl::WebContentsCreated(
+    content::WebContents* source_contents,
+    int opener_render_process_id,
+    int opener_render_frame_id,
+    const std::string& frame_name,
+    const GURL& target_url,
+    content::WebContents* new_contents) {
+  CefBrowserSettings settings;
+  CefRefPtr<CefClient> client;
+  std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate;
+  CefRefPtr<CefDictionaryValue> extra_info;
+
+  CefBrowserInfoManager::GetInstance()->WebContentsCreated(
+      target_url, opener_render_process_id, opener_render_frame_id, settings,
+      client, platform_delegate, extra_info);
+
+  scoped_refptr<CefBrowserInfo> info =
+      CefBrowserInfoManager::GetInstance()->CreatePopupBrowserInfo(
+          new_contents, platform_delegate->IsWindowless(), extra_info);
+  DCHECK(info.get());
+  DCHECK(info->is_popup());
+
+  CefRefPtr<CefBrowserHostImpl> opener = GetBrowserForContents(source_contents);
+  if (!opener.get())
+    return;
+
+  // Popups must share the same RequestContext as the parent.
+  CefRefPtr<CefRequestContextImpl> request_context = opener->request_context();
+  DCHECK(request_context);
+
+  // We don't officially own |new_contents| until AddNewContents() is called.
+  // However, we need to install observers/delegates here.
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CreateInternal(settings, client, new_contents, false, info, opener, false,
+                     request_context, std::move(platform_delegate), nullptr);
+}
+
+void CefBrowserHostImpl::DidNavigateMainFramePostCommit(
+    content::WebContents* web_contents) {
+  base::AutoLock lock_scope(state_lock_);
+  has_document_ = false;
+}
+
+content::JavaScriptDialogManager*
+CefBrowserHostImpl::GetJavaScriptDialogManager(content::WebContents* source) {
+  if (!javascript_dialog_manager_.get() && platform_delegate_) {
+    javascript_dialog_manager_.reset(new CefJavaScriptDialogManager(
+        this, platform_delegate_->CreateJavaScriptDialogRunner()));
+  }
+  return javascript_dialog_manager_.get();
+}
+
+void CefBrowserHostImpl::RunFileChooser(
+    content::RenderFrameHost* render_frame_host,
+    std::unique_ptr<content::FileSelectListener> listener,
+    const blink::mojom::FileChooserParams& params) {
+  EnsureFileDialogManager();
+  file_dialog_manager_->RunFileChooser(std::move(listener), params);
+}
+
+bool CefBrowserHostImpl::HandleContextMenu(
+    content::WebContents* web_contents,
+    const content::ContextMenuParams& params) {
+  CEF_REQUIRE_UIT();
+  if (!menu_manager_.get() && platform_delegate_) {
+    menu_manager_.reset(
+        new CefMenuManager(this, platform_delegate_->CreateMenuRunner()));
+  }
+  return menu_manager_->CreateContextMenu(params);
+}
+
+void CefBrowserHostImpl::UpdatePreferredSize(content::WebContents* source,
+                                             const gfx::Size& pref_size) {
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+  CEF_REQUIRE_UIT();
+  if (platform_delegate_)
+    platform_delegate_->SizeTo(pref_size.width(), pref_size.height());
+#endif
+}
+
+void CefBrowserHostImpl::ResizeDueToAutoResize(content::WebContents* source,
+                                               const gfx::Size& new_size) {
+  CEF_REQUIRE_UIT();
+
+  if (client_) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler && handler->OnAutoResize(
+                       this, CefSize(new_size.width(), new_size.height()))) {
+      return;
+    }
+  }
+
+  UpdatePreferredSize(source, new_size);
+}
+
+void CefBrowserHostImpl::RequestMediaAccessPermission(
+    content::WebContents* web_contents,
+    const content::MediaStreamRequest& request,
+    content::MediaResponseCallback callback) {
+  CEF_REQUIRE_UIT();
+
+  blink::MediaStreamDevices devices;
+
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kEnableMediaStream)) {
+    // Cancel the request.
+    std::move(callback).Run(
+        devices, blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+        std::unique_ptr<content::MediaStreamUI>());
+    return;
+  }
+
+  // Based on chrome/browser/media/media_stream_devices_controller.cc
+  bool microphone_requested =
+      (request.audio_type ==
+       blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
+  bool webcam_requested = (request.video_type ==
+                           blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
+  bool screen_requested =
+      (request.video_type ==
+       blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE);
+  if (microphone_requested || webcam_requested || screen_requested) {
+    // Pick the desired device or fall back to the first available of the
+    // given type.
+    if (microphone_requested) {
+      CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice(
+          request.requested_audio_device_id, true, false, &devices);
+    }
+    if (webcam_requested) {
+      CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice(
+          request.requested_video_device_id, false, true, &devices);
+    }
+    if (screen_requested) {
+      content::DesktopMediaID media_id;
+      if (request.requested_video_device_id.empty()) {
+        media_id =
+            content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
+                                    -1 /* webrtc::kFullDesktopScreenId */);
+      } else {
+        media_id =
+            content::DesktopMediaID::Parse(request.requested_video_device_id);
+      }
+      devices.push_back(blink::MediaStreamDevice(
+          blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+          media_id.ToString(), "Screen"));
+    }
+  }
+
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
+                          std::unique_ptr<content::MediaStreamUI>());
+}
+
+bool CefBrowserHostImpl::CheckMediaAccessPermission(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& security_origin,
+    blink::mojom::MediaStreamType type) {
+  // Check media access permission without prompting the user. This is called
+  // when loading the Pepper Flash plugin.
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  return command_line->HasSwitch(switches::kEnableMediaStream);
+}
+
+bool CefBrowserHostImpl::IsNeverComposited(content::WebContents* web_contents) {
+  if (extension_host_)
+    return extension_host_->IsNeverComposited(web_contents);
+  return false;
+}
+
+content::PictureInPictureResult CefBrowserHostImpl::EnterPictureInPicture(
+    content::WebContents* web_contents,
+    const viz::SurfaceId& surface_id,
+    const gfx::Size& natural_size) {
+  if (!IsPictureInPictureSupported()) {
+    return content::PictureInPictureResult::kNotSupported;
+  }
+
+  return PictureInPictureWindowManager::GetInstance()->EnterPictureInPicture(
+      web_contents, surface_id, natural_size);
+}
+
+void CefBrowserHostImpl::ExitPictureInPicture() {
+  DCHECK(IsPictureInPictureSupported());
+  PictureInPictureWindowManager::GetInstance()->ExitPictureInPicture();
+}
+
+// content::WebContentsObserver methods.
+// -----------------------------------------------------------------------------
+
+void CefBrowserHostImpl::RenderFrameCreated(
+    content::RenderFrameHost* render_frame_host) {
+  browser_info_->MaybeCreateFrame(render_frame_host, false /* is_guest_view */);
+}
+
+void CefBrowserHostImpl::RenderFrameHostChanged(
+    content::RenderFrameHost* old_host,
+    content::RenderFrameHost* new_host) {
+  // Just in case RenderFrameCreated wasn't called for some reason.
+  RenderFrameCreated(new_host);
+}
+
+void CefBrowserHostImpl::RenderFrameDeleted(
+    content::RenderFrameHost* render_frame_host) {
+  const auto frame_id = CefFrameHostImpl::MakeFrameId(render_frame_host);
+  browser_info_->RemoveFrame(render_frame_host);
+
+  base::AutoLock lock_scope(state_lock_);
+
+  if (focused_frame_ && focused_frame_->GetIdentifier() == frame_id) {
+    focused_frame_ = nullptr;
+  }
+}
+
+void CefBrowserHostImpl::RenderViewCreated(
+    content::RenderViewHost* render_view_host) {
+  // May be already registered if the renderer crashed previously.
+  if (!registrar_->IsRegistered(
+          this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
+          content::Source<content::RenderViewHost>(render_view_host))) {
+    registrar_->Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
+                    content::Source<content::RenderViewHost>(render_view_host));
+  }
+
+  // RenderFrameCreated is otherwise not called for new popup browsers.
+  RenderFrameCreated(render_view_host->GetMainFrame());
+
+  platform_delegate_->RenderViewCreated(render_view_host);
+}
+
+void CefBrowserHostImpl::RenderViewDeleted(
+    content::RenderViewHost* render_view_host) {
+  if (registrar_->IsRegistered(
+          this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
+          content::Source<content::RenderViewHost>(render_view_host))) {
+    registrar_->Remove(
+        this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
+        content::Source<content::RenderViewHost>(render_view_host));
+  }
+}
+
+void CefBrowserHostImpl::RenderViewReady() {
+  ConfigureAutoResize();
+
+  if (client_.get()) {
+    CefRefPtr<CefRequestHandler> handler = client_->GetRequestHandler();
+    if (handler.get())
+      handler->OnRenderViewReady(this);
+  }
+}
+
+void CefBrowserHostImpl::RenderProcessGone(base::TerminationStatus status) {
+  cef_termination_status_t ts = TS_ABNORMAL_TERMINATION;
+  if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED)
+    ts = TS_PROCESS_WAS_KILLED;
+  else if (status == base::TERMINATION_STATUS_PROCESS_CRASHED)
+    ts = TS_PROCESS_CRASHED;
+  else if (status == base::TERMINATION_STATUS_OOM)
+    ts = TS_PROCESS_OOM;
+  else if (status != base::TERMINATION_STATUS_ABNORMAL_TERMINATION)
+    return;
+
+  if (client_.get()) {
+    CefRefPtr<CefRequestHandler> handler = client_->GetRequestHandler();
+    if (handler.get()) {
+      std::unique_ptr<NavigationLock> navigation_lock = CreateNavigationLock();
+      handler->OnRenderProcessTerminated(this, ts);
+    }
+  }
+}
+
+void CefBrowserHostImpl::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  const net::Error error_code = navigation_handle->GetNetErrorCode();
+
+  // Skip calls where the navigation has not yet committed and there is no
+  // error code. For example, when creating a browser without loading a URL.
+  if (!navigation_handle->HasCommitted() && error_code == net::OK)
+    return;
+
+  const bool is_main_frame = navigation_handle->IsInMainFrame();
+  const GURL& url =
+      (error_code == net::OK ? navigation_handle->GetURL() : GURL());
+
+  // May return NULL when starting a new navigation if the previous navigation
+  // caused the renderer process to crash during load.
+  CefRefPtr<CefFrameHostImpl> frame = browser_info_->GetFrameForFrameTreeNode(
+      navigation_handle->GetFrameTreeNodeId());
+  if (!frame) {
+    if (is_main_frame) {
+      frame = browser_info_->GetMainFrame();
+    } else {
+      frame =
+          browser_info_->CreateTempSubFrame(CefFrameHostImpl::kInvalidFrameId);
+    }
+  }
+  frame->RefreshAttributes();
+
+  if (error_code == net::OK) {
+    // The navigation has been committed and there is no error.
+    DCHECK(navigation_handle->HasCommitted());
+
+    // Don't call OnLoadStart for same page navigations (fragments,
+    // history state).
+    if (!navigation_handle->IsSameDocument())
+      OnLoadStart(frame.get(), navigation_handle->GetPageTransition());
+
+    if (is_main_frame)
+      OnAddressChange(url);
+  } else {
+    // The navigation failed with an error. This may happen before commit
+    // (e.g. network error) or after commit (e.g. response filter error).
+    // If the error happened before commit then this call will originate from
+    // RenderFrameHostImpl::OnDidFailProvisionalLoadWithError.
+    // OnLoadStart/OnLoadEnd will not be called.
+    OnLoadError(frame.get(), navigation_handle->GetURL(), error_code);
+  }
+
+  if (web_contents()) {
+    CefBrowserContext* context =
+        static_cast<CefBrowserContext*>(web_contents()->GetBrowserContext());
+    if (context) {
+      context->AddVisitedURLs(navigation_handle->GetRedirectChain());
+    }
+  }
+}
+
+void CefBrowserHostImpl::DidStopLoading() {
+  // Notify all renderers that loading has stopped. We used to use
+  // RenderFrameObserver::DidStopLoading which was removed in
+  // https://crrev.com/3e37dd0ead. However, that callback wasn't necessarily
+  // accurate because it wasn't called in all of the cases where
+  // RenderFrameImpl sends the FrameHostMsg_DidStopLoading message. This adds
+  // an additional round trip but should provide the same or improved
+  // functionality.
+  for (const auto& frame : browser_info_->GetAllFrames()) {
+    frame->MaybeSendDidStopLoading();
+  }
+}
+
+void CefBrowserHostImpl::DocumentAvailableInMainFrame() {
+  {
+    base::AutoLock lock_scope(state_lock_);
+    has_document_ = true;
+  }
+
+  if (client_) {
+    CefRefPtr<CefRequestHandler> handler = client_->GetRequestHandler();
+    if (handler)
+      handler->OnDocumentAvailableInMainFrame(this);
+  }
+}
+
+void CefBrowserHostImpl::DidFailLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url,
+    int error_code) {
+  // The navigation failed after commit. OnLoadStart was called so we also
+  // call OnLoadEnd.
+  auto frame = browser_info_->GetFrameForHost(render_frame_host);
+  frame->RefreshAttributes();
+  OnLoadError(frame, validated_url, error_code);
+  OnLoadEnd(frame, validated_url, error_code);
+}
+
+void CefBrowserHostImpl::TitleWasSet(content::NavigationEntry* entry) {
+  // |entry| may be NULL if a popup is created via window.open and never
+  // navigated.
+  if (entry)
+    OnTitleChange(entry->GetTitle());
+  else if (web_contents())
+    OnTitleChange(web_contents()->GetTitle());
+}
+
+void CefBrowserHostImpl::PluginCrashed(const base::FilePath& plugin_path,
+                                       base::ProcessId plugin_pid) {
+  if (client_.get()) {
+    CefRefPtr<CefRequestHandler> handler = client_->GetRequestHandler();
+    if (handler.get())
+      handler->OnPluginCrashed(this, plugin_path.value());
+  }
+}
+
+void CefBrowserHostImpl::DidUpdateFaviconURL(
+    const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
+  if (client_.get()) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler.get()) {
+      std::vector<CefString> icon_urls;
+      for (const auto& icon : candidates) {
+        if (icon->icon_type == blink::mojom::FaviconIconType::kFavicon)
+          icon_urls.push_back(icon->icon_url.spec());
+      }
+      if (!icon_urls.empty())
+        handler->OnFaviconURLChange(this, icon_urls);
+    }
+  }
+}
+
+void CefBrowserHostImpl::OnAudioStateChanged(bool audible) {
+  if (audible) {
+    recently_audible_timer_.Stop();
+    StartAudioCapturer();
+  } else if (audio_capturer_) {
+    // If you have a media playing that has a short quiet moment, web_contents
+    // will immediately switch to non-audible state. We don't want to stop
+    // audio stream so quickly, let's give the stream some time to resume
+    // playing.
+    recently_audible_timer_.Start(
+        FROM_HERE, kRecentlyAudibleTimeout,
+        base::BindOnce(&CefBrowserHostImpl::OnRecentlyAudibleTimerFired, this));
+  }
+}
+
+void CefBrowserHostImpl::OnRecentlyAudibleTimerFired() {
+  audio_capturer_.reset();
+}
+
+bool CefBrowserHostImpl::OnMessageReceived(const IPC::Message& message) {
+  // Handle the cursor message here if mouse cursor change is disabled instead
+  // of propegating the message to the normal handler.
+  if (message.type() == WidgetHostMsg_SetCursor::ID)
+    return IsMouseCursorChangeDisabled();
+
+  return false;
+}
+
+bool CefBrowserHostImpl::OnMessageReceived(
+    const IPC::Message& message,
+    content::RenderFrameHost* render_frame_host) {
+  // Messages may arrive after a frame is detached. Ignore those messages.
+  auto frame = GetFrameForHost(render_frame_host);
+  if (frame) {
+    return static_cast<CefFrameHostImpl*>(frame.get())
+        ->OnMessageReceived(message);
+  }
+  return false;
+}
+
+void CefBrowserHostImpl::OnFrameFocused(
+    content::RenderFrameHost* render_frame_host) {
+  CefRefPtr<CefFrameHostImpl> frame =
+      static_cast<CefFrameHostImpl*>(GetFrameForHost(render_frame_host).get());
+  if (!frame || frame->IsFocused())
+    return;
+
+  CefRefPtr<CefFrameHostImpl> previous_frame;
+  {
+    base::AutoLock lock_scope(state_lock_);
+    previous_frame = focused_frame_;
+    if (frame->IsMain())
+      focused_frame_ = nullptr;
+    else
+      focused_frame_ = frame;
+  }
+
+  if (!previous_frame) {
+    // The main frame is focused by default.
+    previous_frame = browser_info_->GetMainFrame();
+  }
+
+  if (previous_frame->GetIdentifier() != frame->GetIdentifier()) {
+    previous_frame->SetFocused(false);
+    frame->SetFocused(true);
+  }
+}
+
+void CefBrowserHostImpl::AccessibilityEventReceived(
+    const content::AXEventNotificationDetails& content_event_bundle) {
+  // Only needed in windowless mode.
+  if (IsWindowless()) {
+    if (!web_contents() || !platform_delegate_)
+      return;
+
+    platform_delegate_->AccessibilityEventReceived(content_event_bundle);
+  }
+}
+
+void CefBrowserHostImpl::AccessibilityLocationChangesReceived(
+    const std::vector<content::AXLocationChangeNotificationDetails>& locData) {
+  // Only needed in windowless mode.
+  if (IsWindowless()) {
+    if (!web_contents() || !platform_delegate_)
+      return;
+
+    platform_delegate_->AccessibilityLocationChangesReceived(locData);
+  }
+}
+
+void CefBrowserHostImpl::OnWebContentsFocused(
+    content::RenderWidgetHost* render_widget_host) {
+  if (client_.get()) {
+    CefRefPtr<CefFocusHandler> handler = client_->GetFocusHandler();
+    if (handler.get())
+      handler->OnGotFocus(this);
+  }
+}
+
+void CefBrowserHostImpl::AddObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.AddObserver(observer);
+}
+
+void CefBrowserHostImpl::RemoveObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.RemoveObserver(observer);
+}
+
+bool CefBrowserHostImpl::HasObserver(Observer* observer) const {
+  CEF_REQUIRE_UIT();
+  return observers_.HasObserver(observer);
+}
+
+void CefBrowserHostImpl::StartAudioCapturer() {
+  if (!client_.get() || audio_capturer_)
+    return;
+
+  CefRefPtr<CefAudioHandler> audio_handler = client_->GetAudioHandler();
+  if (!audio_handler.get())
+    return;
+
+  CefAudioParameters params;
+  params.channel_layout = CEF_CHANNEL_LAYOUT_STEREO;
+  params.sample_rate = media::AudioParameters::kAudioCDSampleRate;
+  params.frames_per_buffer = 1024;
+
+  if (!audio_handler->GetAudioParameters(this, params))
+    return;
+
+  audio_capturer_.reset(new CefAudioCapturer(params, this, audio_handler));
+}
+
+CefBrowserHostImpl::NavigationLock::NavigationLock(
+    CefRefPtr<CefBrowserHostImpl> browser)
+    : browser_(browser) {
+  CEF_REQUIRE_UIT();
+  browser_->navigation_lock_count_++;
+}
+
+CefBrowserHostImpl::NavigationLock::~NavigationLock() {
+  CEF_REQUIRE_UIT();
+  if (--browser_->navigation_lock_count_ == 0) {
+    if (!browser_->pending_navigation_action_.is_null()) {
+      CEF_POST_TASK(CEF_UIT, std::move(browser_->pending_navigation_action_));
+    }
+  }
+}
+
+std::unique_ptr<CefBrowserHostImpl::NavigationLock>
+CefBrowserHostImpl::CreateNavigationLock() {
+  return base::WrapUnique(new NavigationLock(this));
+}
+
+bool CefBrowserHostImpl::navigation_locked() const {
+  CEF_REQUIRE_UIT();
+  return navigation_lock_count_ > 0;
+}
+
+void CefBrowserHostImpl::set_pending_navigation_action(
+    base::OnceClosure action) {
+  CEF_REQUIRE_UIT();
+  pending_navigation_action_ = std::move(action);
+}
+
+// content::NotificationObserver methods.
+// -----------------------------------------------------------------------------
+
+void CefBrowserHostImpl::Observe(int type,
+                                 const content::NotificationSource& source,
+                                 const content::NotificationDetails& details) {
+  DCHECK(type == content::NOTIFICATION_LOAD_STOP ||
+         type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE);
+
+  if (type == content::NOTIFICATION_LOAD_STOP) {
+    content::NavigationController* controller =
+        content::Source<content::NavigationController>(source).ptr();
+    OnTitleChange(controller->GetWebContents()->GetTitle());
+  } else if (type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE) {
+    focus_on_editable_field_ = *content::Details<bool>(details).ptr();
+  }
+}
+
+// CefBrowserHostImpl private methods.
+// -----------------------------------------------------------------------------
+
+CefBrowserHostImpl::CefBrowserHostImpl(
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefClient> client,
+    content::WebContents* web_contents,
+    scoped_refptr<CefBrowserInfo> browser_info,
+    CefRefPtr<CefBrowserHostImpl> opener,
+    CefRefPtr<CefRequestContextImpl> request_context,
+    std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
+    CefRefPtr<CefExtension> extension)
+    : content::WebContentsObserver(web_contents),
+      settings_(settings),
+      client_(client),
+      browser_info_(browser_info),
+      opener_(kNullWindowHandle),
+      request_context_(request_context),
+      platform_delegate_(std::move(platform_delegate)),
+      is_windowless_(platform_delegate_->IsWindowless()),
+      is_views_hosted_(platform_delegate_->IsViewsHosted()),
+      extension_(extension) {
+  if (opener.get() && !platform_delegate_->IsViewsHosted()) {
+    // GetOpenerWindowHandle() only returns a value for non-views-hosted
+    // popup browsers.
+    opener_ = opener->GetWindowHandle();
+  }
+
+  DCHECK(!browser_info_->browser().get());
+  browser_info_->SetBrowser(this);
+
+  web_contents->SetDelegate(this);
+
+  // Associate the WebContents with this browser object.
+  WebContentsUserDataAdapter::Register(this);
+
+  registrar_.reset(new content::NotificationRegistrar);
+
+  // When navigating through the history, the restored NavigationEntry's title
+  // will be used. If the entry ends up having the same title after we return
+  // to it, as will usually be the case, the
+  // NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED will then be suppressed, since
+  // the NavigationEntry's title hasn't changed.
+  registrar_->Add(this, content::NOTIFICATION_LOAD_STOP,
+                  content::Source<content::NavigationController>(
+                      &web_contents->GetController()));
+
+  PrefsTabHelper::CreateForWebContents(web_contents);
+  printing::CefPrintViewManager::CreateForWebContents(web_contents);
+
+  if (extensions::ExtensionsEnabled()) {
+    extensions::CefExtensionWebContentsObserver::CreateForWebContents(
+        web_contents);
+
+    // Used by the tabs extension API.
+    zoom::ZoomController::CreateForWebContents(web_contents);
+  }
+
+  // Make sure RenderViewCreated is called at least one time.
+  RenderViewCreated(web_contents->GetRenderViewHost());
+
+  // Associate the platform delegate with this browser.
+  platform_delegate_->BrowserCreated(this);
+}
+
+void CefBrowserHostImpl::set_owned_web_contents(
+    content::WebContents* owned_contents) {
+  // Should not currently own a WebContents.
+  CHECK(!owned_web_contents_);
+  // Should already be associated with |owned_contents|.
+  CHECK(web_contents() == owned_contents);
+  owned_web_contents_.reset(owned_contents);
+}
+
+bool CefBrowserHostImpl::CreateHostWindow() {
+  // |host_window_handle_| will not change after initial host creation for
+  // non-views-hosted browsers.
+  bool success = true;
+  if (!IsWindowless())
+    success = platform_delegate_->CreateHostWindow();
+  if (success && !IsViewsHosted())
+    host_window_handle_ = platform_delegate_->GetHostWindowHandle();
+  return success;
+}
+
+void CefBrowserHostImpl::CreateExtensionHost(
+    const extensions::Extension* extension,
+    content::BrowserContext* browser_context,
+    content::WebContents* host_contents,
+    const GURL& url,
+    extensions::ViewType host_type) {
+  DCHECK(!extension_host_);
+
+  if (host_type == extensions::VIEW_TYPE_EXTENSION_DIALOG ||
+      host_type == extensions::VIEW_TYPE_EXTENSION_POPUP) {
+    // Create an extension host that we own.
+    extension_host_ = new extensions::CefExtensionViewHost(
+        this, extension, browser_context, host_contents, url, host_type);
+    // Trigger load of the extension URL.
+    extension_host_->CreateRenderViewSoon();
+  } else if (host_type == extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
+    is_background_host_ = true;
+    // Create an extension host that will be owned by ProcessManager.
+    extension_host_ = new extensions::CefExtensionBackgroundHost(
+        this, base::BindOnce(&CefBrowserHostImpl::OnExtensionHostDeleted, this),
+        extension, browser_context, host_contents, url, host_type);
+    // Load will be triggered by ProcessManager::CreateBackgroundHost.
+  } else {
+    NOTREACHED() << " Unsupported extension host type: " << host_type;
+  }
+}
+
+void CefBrowserHostImpl::DestroyExtensionHost() {
+  if (!extension_host_)
+    return;
+  if (extension_host_->extension_host_type() ==
+      extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
+    DCHECK(is_background_host_);
+    // Close notification for background pages arrives via CloseContents.
+    // The extension host will be deleted by
+    // ProcessManager::CloseBackgroundHost and OnExtensionHostDeleted will be
+    // called to notify us.
+    extension_host_->Close();
+  } else {
+    DCHECK(!is_background_host_);
+    // We own the extension host and must delete it.
+    delete extension_host_;
+    extension_host_ = nullptr;
+  }
+}
+
+void CefBrowserHostImpl::OnExtensionHostDeleted() {
+  DCHECK(is_background_host_);
+  DCHECK(extension_host_);
+  extension_host_ = nullptr;
+}
+
+gfx::Point CefBrowserHostImpl::GetScreenPoint(const gfx::Point& view) const {
+  CEF_REQUIRE_UIT();
+  if (platform_delegate_)
+    return platform_delegate_->GetScreenPoint(view);
+  return gfx::Point();
+}
+
+void CefBrowserHostImpl::StartDragging(
+    const content::DropData& drop_data,
+    blink::WebDragOperationsMask allowed_ops,
+    const gfx::ImageSkia& image,
+    const gfx::Vector2d& image_offset,
+    const content::DragEventSourceInfo& event_info,
+    content::RenderWidgetHostImpl* source_rwh) {
+  if (platform_delegate_) {
+    platform_delegate_->StartDragging(drop_data, allowed_ops, image,
+                                      image_offset, event_info, source_rwh);
+  }
+}
+
+void CefBrowserHostImpl::UpdateDragCursor(blink::WebDragOperation operation) {
+  if (platform_delegate_)
+    platform_delegate_->UpdateDragCursor(operation);
+}
+
+void CefBrowserHostImpl::OnAddressChange(const GURL& url) {
+  if (client_.get()) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler.get()) {
+      // Notify the handler of an address change.
+      handler->OnAddressChange(this, GetMainFrame(), url.spec());
+    }
+  }
+}
+
+void CefBrowserHostImpl::OnLoadStart(CefRefPtr<CefFrame> frame,
+                                     ui::PageTransition transition_type) {
+  if (client_.get()) {
+    CefRefPtr<CefLoadHandler> handler = client_->GetLoadHandler();
+    if (handler.get()) {
+      // Notify the handler that loading has started.
+      handler->OnLoadStart(this, frame,
+                           static_cast<cef_transition_type_t>(transition_type));
+    }
+  }
+}
+
+void CefBrowserHostImpl::OnLoadError(CefRefPtr<CefFrame> frame,
+                                     const GURL& url,
+                                     int error_code) {
+  if (client_.get()) {
+    CefRefPtr<CefLoadHandler> handler = client_->GetLoadHandler();
+    if (handler.get()) {
+      std::unique_ptr<NavigationLock> navigation_lock = CreateNavigationLock();
+      // Notify the handler that loading has failed.
+      handler->OnLoadError(this, frame,
+                           static_cast<cef_errorcode_t>(error_code),
+                           net::ErrorToShortString(error_code), url.spec());
+    }
+  }
+}
+
+void CefBrowserHostImpl::OnLoadEnd(CefRefPtr<CefFrame> frame,
+                                   const GURL& url,
+                                   int http_status_code) {
+  if (client_.get()) {
+    CefRefPtr<CefLoadHandler> handler = client_->GetLoadHandler();
+    if (handler.get())
+      handler->OnLoadEnd(this, frame, http_status_code);
+  }
+}
+
+void CefBrowserHostImpl::OnFullscreenModeChange(bool fullscreen) {
+  if (is_fullscreen_ == fullscreen)
+    return;
+
+  is_fullscreen_ = fullscreen;
+  WasResized();
+
+  if (client_.get()) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler.get())
+      handler->OnFullscreenModeChange(this, fullscreen);
+  }
+}
+
+void CefBrowserHostImpl::OnTitleChange(const base::string16& title) {
+  if (client_.get()) {
+    CefRefPtr<CefDisplayHandler> handler = client_->GetDisplayHandler();
+    if (handler.get())
+      handler->OnTitleChange(this, title);
+  }
+}
+
+void CefBrowserHostImpl::EnsureFileDialogManager() {
+  CEF_REQUIRE_UIT();
+  if (!file_dialog_manager_.get() && platform_delegate_) {
+    file_dialog_manager_.reset(new CefFileDialogManager(
+        this, platform_delegate_->CreateFileDialogRunner()));
+  }
+}
+
+void CefBrowserHostImpl::ConfigureAutoResize() {
+  CEF_REQUIRE_UIT();
+  if (!web_contents() || !web_contents()->GetRenderWidgetHostView()) {
+    return;
+  }
+
+  if (auto_resize_enabled_) {
+    web_contents()->GetRenderWidgetHostView()->EnableAutoResize(
+        auto_resize_min_, auto_resize_max_);
+  } else {
+    web_contents()->GetRenderWidgetHostView()->DisableAutoResize(gfx::Size());
+  }
+}
+
+void CefBrowserHostImpl::SendTouchEvent(const CefTouchEvent& event) {
+  if (!IsWindowless()) {
+    NOTREACHED() << "Window rendering is not disabled";
+    return;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefBrowserHostImpl::SendTouchEvent, this, event));
+    return;
+  }
+
+  if (!web_contents() || !platform_delegate_)
+    return;
+
+  platform_delegate_->SendTouchEvent(event);
+}
diff --git a/src/libcef/browser/browser_host_impl.h b/src/libcef/browser/browser_host_impl.h
new file mode 100644
index 0000000..0fb19b4
--- /dev/null
+++ b/src/libcef/browser/browser_host_impl.h
@@ -0,0 +1,696 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_HOST_IMPL_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_HOST_IMPL_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "include/cef_frame.h"
+#include "include/views/cef_browser_view.h"
+#include "libcef/browser/browser_info.h"
+#include "libcef/browser/file_dialog_manager.h"
+#include "libcef/browser/frame_host_impl.h"
+#include "libcef/browser/javascript_dialog_manager.h"
+#include "libcef/browser/menu_manager.h"
+#include "libcef/browser/request_context_impl.h"
+
+#include "base/observer_list.h"
+#include "base/strings/string16.h"
+#include "base/synchronization/lock.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "extensions/common/view_type.h"
+
+namespace content {
+struct DragEventSourceInfo;
+class RenderWidgetHostImpl;
+}  // namespace content
+
+namespace extensions {
+class Extension;
+class ExtensionHost;
+}  // namespace extensions
+
+#if defined(USE_AURA)
+namespace views {
+class Widget;
+}
+#endif  // defined(USE_AURA)
+
+class CefAudioCapturer;
+class CefBrowserInfo;
+class CefBrowserPlatformDelegate;
+class CefDevToolsManager;
+class SiteInstance;
+
+// Implementation of CefBrowser.
+//
+// WebContentsDelegate: Interface for handling WebContents delegations. There is
+// a one-to-one relationship between CefBrowserHostImpl and WebContents
+// instances.
+//
+// WebContentsObserver: Interface for observing WebContents notifications and
+// IPC messages. There is a one-to-one relationship between WebContents and
+// RenderViewHost instances. IPC messages received by the RenderViewHost will be
+// forwarded to this WebContentsObserver implementation via WebContents. IPC
+// messages sent using CefBrowserHostImpl::Send() will be forwarded to the
+// RenderViewHost (after posting to the UI thread if necessary). Use
+// WebContentsObserver::routing_id() when sending IPC messages.
+//
+// NotificationObserver: Interface for observing post-processed notifications.
+class CefBrowserHostImpl : public CefBrowserHost,
+                           public CefBrowser,
+                           public content::WebContentsDelegate,
+                           public content::WebContentsObserver,
+                           public content::NotificationObserver {
+ public:
+  // Used for handling the response to command messages.
+  class CommandResponseHandler : public virtual CefBaseRefCounted {
+   public:
+    virtual void OnResponse(const std::string& response) = 0;
+  };
+
+  // Interface to implement for observers that wish to be informed of changes
+  // to the CefBrowserHostImpl. All methods will be called on the UI thread.
+  class Observer {
+   public:
+    // Called before |browser| is destroyed. Any references to |browser| should
+    // be cleared when this method is called.
+    virtual void OnBrowserDestroyed(CefBrowserHostImpl* browser) = 0;
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  ~CefBrowserHostImpl() override;
+
+  struct CreateParams {
+    // Platform-specific window creation info. Will be nullptr when creating a
+    // views-hosted browser.
+    std::unique_ptr<CefWindowInfo> window_info;
+
+#if defined(USE_AURA)
+    // The BrowserView that will own a views-hosted browser. Will be nullptr for
+    // popup browsers (the BrowserView will be created later in that case).
+    CefRefPtr<CefBrowserView> browser_view;
+#endif
+
+    // Client implementation. May be nullptr.
+    CefRefPtr<CefClient> client;
+
+    // Initial URL to load. May be empty. If this is a valid extension URL then
+    // the browser will be created as an app view extension host.
+    GURL url;
+
+    // Browser settings.
+    CefBrowserSettings settings;
+
+    // Other browser that opened this DevTools browser. Will be nullptr for non-
+    // DevTools browsers.
+    CefRefPtr<CefBrowserHostImpl> devtools_opener;
+
+    // Request context to use when creating the browser. If nullptr the global
+    // request context will be used.
+    CefRefPtr<CefRequestContext> request_context;
+
+    CefRefPtr<CefDictionaryValue> extra_info;
+
+    // Used when explicitly creating the browser as an extension host via
+    // ProcessManager::CreateBackgroundHost.
+    const extensions::Extension* extension = nullptr;
+    extensions::ViewType extension_host_type = extensions::VIEW_TYPE_INVALID;
+  };
+
+  // Create a new CefBrowserHostImpl instance.
+  static CefRefPtr<CefBrowserHostImpl> Create(CreateParams& create_params);
+
+  // Returns the browser associated with the specified RenderViewHost.
+  static CefRefPtr<CefBrowserHostImpl> GetBrowserForHost(
+      const content::RenderViewHost* host);
+  // Returns the browser associated with the specified RenderFrameHost.
+  static CefRefPtr<CefBrowserHostImpl> GetBrowserForHost(
+      const content::RenderFrameHost* host);
+  // Returns the browser associated with the specified WebContents.
+  static CefRefPtr<CefBrowserHostImpl> GetBrowserForContents(
+      const content::WebContents* contents);
+  // Returns the browser associated with the specified FrameTreeNode ID.
+  static CefRefPtr<CefBrowserHostImpl> GetBrowserForFrameTreeNode(
+      int frame_tree_node_id);
+  // Returns the browser associated with the specified frame routing IDs.
+  static CefRefPtr<CefBrowserHostImpl> GetBrowserForFrameRoute(
+      int render_process_id,
+      int render_routing_id);
+
+  // CefBrowserHost methods.
+  CefRefPtr<CefBrowser> GetBrowser() override;
+  void CloseBrowser(bool force_close) override;
+  bool TryCloseBrowser() override;
+  void SetFocus(bool focus) override;
+  CefWindowHandle GetWindowHandle() override;
+  CefWindowHandle GetOpenerWindowHandle() override;
+  bool HasView() override;
+  CefRefPtr<CefClient> GetClient() override;
+  CefRefPtr<CefRequestContext> GetRequestContext() override;
+  double GetZoomLevel() override;
+  void SetZoomLevel(double zoomLevel) override;
+  void RunFileDialog(FileDialogMode mode,
+                     const CefString& title,
+                     const CefString& default_file_path,
+                     const std::vector<CefString>& accept_filters,
+                     int selected_accept_filter,
+                     CefRefPtr<CefRunFileDialogCallback> callback) override;
+  void StartDownload(const CefString& url) override;
+  void DownloadImage(const CefString& image_url,
+                     bool is_favicon,
+                     uint32 max_image_size,
+                     bool bypass_cache,
+                     CefRefPtr<CefDownloadImageCallback> callback) override;
+  void Print() override;
+  void PrintToPDF(const CefString& path,
+                  const CefPdfPrintSettings& settings,
+                  CefRefPtr<CefPdfPrintCallback> callback) override;
+  void Find(int identifier,
+            const CefString& searchText,
+            bool forward,
+            bool matchCase,
+            bool findNext) override;
+  void StopFinding(bool clearSelection) override;
+  void ShowDevTools(const CefWindowInfo& windowInfo,
+                    CefRefPtr<CefClient> client,
+                    const CefBrowserSettings& settings,
+                    const CefPoint& inspect_element_at) override;
+  void CloseDevTools() override;
+  bool HasDevTools() override;
+  bool SendDevToolsMessage(const void* message, size_t message_size) override;
+  int ExecuteDevToolsMethod(int message_id,
+                            const CefString& method,
+                            CefRefPtr<CefDictionaryValue> params) override;
+  CefRefPtr<CefRegistration> AddDevToolsMessageObserver(
+      CefRefPtr<CefDevToolsMessageObserver> observer) override;
+  void GetNavigationEntries(CefRefPtr<CefNavigationEntryVisitor> visitor,
+                            bool current_only) override;
+  void SetMouseCursorChangeDisabled(bool disabled) override;
+  bool IsMouseCursorChangeDisabled() override;
+  bool IsWindowRenderingDisabled() override;
+  void ReplaceMisspelling(const CefString& word) override;
+  void AddWordToDictionary(const CefString& word) override;
+  void WasResized() override;
+  void WasHidden(bool hidden) override;
+  void NotifyScreenInfoChanged() override;
+  void Invalidate(PaintElementType type) override;
+  void SendExternalBeginFrame() override;
+  void SendKeyEvent(const CefKeyEvent& event) override;
+  void SendMouseClickEvent(const CefMouseEvent& event,
+                           MouseButtonType type,
+                           bool mouseUp,
+                           int clickCount) override;
+  void SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) override;
+  void SendMouseWheelEvent(const CefMouseEvent& event,
+                           int deltaX,
+                           int deltaY) override;
+  void SendTouchEvent(const CefTouchEvent& event) override;
+  void SendFocusEvent(bool setFocus) override;
+  void SendCaptureLostEvent() override;
+  void NotifyMoveOrResizeStarted() override;
+  int GetWindowlessFrameRate() override;
+  void SetWindowlessFrameRate(int frame_rate) override;
+  void ImeSetComposition(const CefString& text,
+                         const std::vector<CefCompositionUnderline>& underlines,
+                         const CefRange& replacement_range,
+                         const CefRange& selection_range) override;
+  void ImeCommitText(const CefString& text,
+                     const CefRange& replacement_range,
+                     int relative_cursor_pos) override;
+  void ImeFinishComposingText(bool keep_selection) override;
+  void ImeCancelComposition() override;
+  void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
+                           const CefMouseEvent& event,
+                           DragOperationsMask allowed_ops) override;
+  void DragTargetDragOver(const CefMouseEvent& event,
+                          DragOperationsMask allowed_ops) override;
+  void DragTargetDragLeave() override;
+  void DragTargetDrop(const CefMouseEvent& event) override;
+  void DragSourceSystemDragEnded() override;
+  void DragSourceEndedAt(int x, int y, DragOperationsMask op) override;
+  void SetAudioMuted(bool mute) override;
+  bool IsAudioMuted() override;
+  CefRefPtr<CefNavigationEntry> GetVisibleNavigationEntry() override;
+  void SetAccessibilityState(cef_state_t accessibility_state) override;
+  void SetAutoResizeEnabled(bool enabled,
+                            const CefSize& min_size,
+                            const CefSize& max_size) override;
+  CefRefPtr<CefExtension> GetExtension() override;
+  bool IsBackgroundHost() override;
+
+  // CefBrowser methods.
+  CefRefPtr<CefBrowserHost> GetHost() override;
+  bool CanGoBack() override;
+  void GoBack() override;
+  bool CanGoForward() override;
+  void GoForward() override;
+  bool IsLoading() override;
+  void Reload() override;
+  void ReloadIgnoreCache() override;
+  void StopLoad() override;
+  int GetIdentifier() override;
+  bool IsSame(CefRefPtr<CefBrowser> that) override;
+  bool IsPopup() override;
+  bool HasDocument() override;
+  CefRefPtr<CefFrame> GetMainFrame() override;
+  CefRefPtr<CefFrame> GetFocusedFrame() override;
+  CefRefPtr<CefFrame> GetFrame(int64 identifier) override;
+  CefRefPtr<CefFrame> GetFrame(const CefString& name) override;
+  size_t GetFrameCount() override;
+  void GetFrameIdentifiers(std::vector<int64>& identifiers) override;
+  void GetFrameNames(std::vector<CefString>& names) override;
+
+  // Returns true if windowless rendering is enabled.
+  bool IsWindowless() const;
+
+  // Returns true if this browser is views-hosted.
+  bool IsViewsHosted() const;
+
+  // Returns true if this browser supports print preview.
+  bool IsPrintPreviewSupported() const;
+
+  // Returns true if this browser supports picture-in-picture.
+  bool IsPictureInPictureSupported() const;
+
+  // Called when the OS window hosting the browser is destroyed.
+  void WindowDestroyed();
+
+  // Destroy the browser members. This method should only be called after the
+  // native browser window is not longer processing messages.
+  void DestroyBrowser();
+
+  // Cancel display of the context menu, if any.
+  void CancelContextMenu();
+
+#if defined(USE_AURA)
+  // Returns the Widget owner for the browser window. Only used with windowed
+  // rendering.
+  views::Widget* GetWindowWidget() const;
+
+  // Returns the BrowserView associated with this browser. Only used with views-
+  // based browsers.
+  CefRefPtr<CefBrowserView> GetBrowserView() const;
+#endif
+
+  // Returns the frame associated with the specified RenderFrameHost.
+  CefRefPtr<CefFrame> GetFrameForHost(const content::RenderFrameHost* host);
+
+  // Returns the frame associated with the specified FrameTreeNode ID.
+  CefRefPtr<CefFrame> GetFrameForFrameTreeNode(int frame_tree_node_id);
+
+  // Load the specified URL in the main frame.
+  void LoadMainFrameURL(const std::string& url,
+                        const content::Referrer& referrer,
+                        ui::PageTransition transition,
+                        const std::string& extra_headers);
+
+  // Called from CefFrameHostImpl.
+  void OnDidFinishLoad(CefRefPtr<CefFrameHostImpl> frame,
+                       const GURL& validated_url,
+                       int http_status_code);
+
+  // Open the specified text in the default text editor.
+  void ViewText(const std::string& text);
+
+  // Convert from view coordinates to screen coordinates. Potential display
+  // scaling will be applied to the result.
+  gfx::Point GetScreenPoint(const gfx::Point& view) const;
+
+  void StartDragging(const content::DropData& drop_data,
+                     blink::WebDragOperationsMask allowed_ops,
+                     const gfx::ImageSkia& image,
+                     const gfx::Vector2d& image_offset,
+                     const content::DragEventSourceInfo& event_info,
+                     content::RenderWidgetHostImpl* source_rwh);
+  void UpdateDragCursor(blink::WebDragOperation operation);
+
+  // Thread safe accessors.
+  const CefBrowserSettings& settings() const { return settings_; }
+  SkColor GetBackgroundColor() const;
+  CefRefPtr<CefClient> client() const { return client_; }
+  scoped_refptr<CefBrowserInfo> browser_info() const { return browser_info_; }
+  int browser_id() const;
+  CefRefPtr<CefRequestContextImpl> request_context() const {
+    return request_context_;
+  }
+
+  // Accessors that must be called on the UI thread.
+  content::BrowserContext* GetBrowserContext();
+  extensions::ExtensionHost* extension_host() const { return extension_host_; }
+
+  void OnSetFocus(cef_focus_source_t source);
+
+  // Run the file chooser dialog specified by |params|. Only a single dialog may
+  // be pending at any given time. |callback| will be executed asynchronously
+  // after the dialog is dismissed or if another dialog is already pending.
+  void RunFileChooser(const CefFileDialogRunner::FileChooserParams& params,
+                      CefFileDialogRunner::RunFileChooserCallback callback);
+
+  bool HandleContextMenu(content::WebContents* web_contents,
+                         const content::ContextMenuParams& params);
+
+  // Returns the WebContents most likely to handle an action. If extensions are
+  // enabled and this browser has a full-page guest (for example, a full-page
+  // PDF viewer extension) then the guest's WebContents will be returned.
+  // Otherwise, the browser's WebContents will be returned.
+  content::WebContents* GetActionableWebContents() const;
+
+  enum DestructionState {
+    DESTRUCTION_STATE_NONE = 0,
+    DESTRUCTION_STATE_PENDING,
+    DESTRUCTION_STATE_ACCEPTED,
+    DESTRUCTION_STATE_COMPLETED
+  };
+  DestructionState destruction_state() const { return destruction_state_; }
+
+  // content::WebContentsDelegate methods.
+  content::WebContents* OpenURLFromTab(
+      content::WebContents* source,
+      const content::OpenURLParams& params) override;
+  bool ShouldTransferNavigation(bool is_main_frame_navigation) override;
+  void AddNewContents(content::WebContents* source,
+                      std::unique_ptr<content::WebContents> new_contents,
+                      WindowOpenDisposition disposition,
+                      const gfx::Rect& initial_rect,
+                      bool user_gesture,
+                      bool* was_blocked) override;
+  void LoadingStateChanged(content::WebContents* source,
+                           bool to_different_document) override;
+  void LoadProgressChanged(double progress) override;
+  void CloseContents(content::WebContents* source) override;
+  void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
+  bool DidAddMessageToConsole(content::WebContents* source,
+                              blink::mojom::ConsoleMessageLevel log_level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
+  void BeforeUnloadFired(content::WebContents* source,
+                         bool proceed,
+                         bool* proceed_to_fire_unload) override;
+  bool TakeFocus(content::WebContents* source, bool reverse) override;
+
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
+  content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
+      content::WebContents* source,
+      const content::NativeWebKeyboardEvent& event) override;
+  bool HandleKeyboardEvent(
+      content::WebContents* source,
+      const content::NativeWebKeyboardEvent& event) override;
+  bool PreHandleGestureEvent(content::WebContents* source,
+                             const blink::WebGestureEvent& event) override;
+  bool CanDragEnter(content::WebContents* source,
+                    const content::DropData& data,
+                    blink::WebDragOperationsMask operations_allowed) override;
+  void GetCustomWebContentsView(
+      content::WebContents* web_contents,
+      const GURL& target_url,
+      int opener_render_process_id,
+      int opener_render_frame_id,
+      content::WebContentsView** view,
+      content::RenderViewHostDelegateView** delegate_view) override;
+  void WebContentsCreated(content::WebContents* source_contents,
+                          int opener_render_process_id,
+                          int opener_render_frame_id,
+                          const std::string& frame_name,
+                          const GURL& target_url,
+                          content::WebContents* new_contents) override;
+  void DidNavigateMainFramePostCommit(
+      content::WebContents* web_contents) override;
+  content::JavaScriptDialogManager* GetJavaScriptDialogManager(
+      content::WebContents* source) override;
+  void RunFileChooser(content::RenderFrameHost* render_frame_host,
+                      std::unique_ptr<content::FileSelectListener> listener,
+                      const blink::mojom::FileChooserParams& params) override;
+  bool EmbedsFullscreenWidget() override;
+  void EnterFullscreenModeForTab(
+      content::WebContents* web_contents,
+      const GURL& origin,
+      const blink::mojom::FullscreenOptions& options) override;
+  void ExitFullscreenModeForTab(content::WebContents* web_contents) override;
+  bool IsFullscreenForTabOrPending(
+      const content::WebContents* web_contents) override;
+  blink::mojom::DisplayMode GetDisplayMode(
+      const content::WebContents* web_contents) override;
+  void FindReply(content::WebContents* web_contents,
+                 int request_id,
+                 int number_of_matches,
+                 const gfx::Rect& selection_rect,
+                 int active_match_ordinal,
+                 bool final_update) override;
+  void UpdatePreferredSize(content::WebContents* source,
+                           const gfx::Size& pref_size) override;
+  void ResizeDueToAutoResize(content::WebContents* source,
+                             const gfx::Size& new_size) override;
+  void RequestMediaAccessPermission(
+      content::WebContents* web_contents,
+      const content::MediaStreamRequest& request,
+      content::MediaResponseCallback callback) override;
+  bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
+                                  const GURL& security_origin,
+                                  blink::mojom::MediaStreamType type) override;
+  bool IsNeverComposited(content::WebContents* web_contents) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
+  void ExitPictureInPicture() override;
+
+  // content::WebContentsObserver methods.
+  using content::WebContentsObserver::BeforeUnloadFired;
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
+                              content::RenderFrameHost* new_host) override;
+  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
+  void RenderViewCreated(content::RenderViewHost* render_view_host) override;
+  void RenderViewDeleted(content::RenderViewHost* render_view_host) override;
+  void RenderViewReady() override;
+  void RenderProcessGone(base::TerminationStatus status) override;
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void DidStopLoading() override;
+  void DocumentAvailableInMainFrame() override;
+  void DidFailLoad(content::RenderFrameHost* render_frame_host,
+                   const GURL& validated_url,
+                   int error_code) override;
+  void TitleWasSet(content::NavigationEntry* entry) override;
+  void PluginCrashed(const base::FilePath& plugin_path,
+                     base::ProcessId plugin_pid) override;
+  void DidUpdateFaviconURL(
+      const std::vector<blink::mojom::FaviconURLPtr>& candidates) override;
+  void OnAudioStateChanged(bool audible) override;
+  bool OnMessageReceived(const IPC::Message& message) override;
+  bool OnMessageReceived(const IPC::Message& message,
+                         content::RenderFrameHost* render_frame_host) override;
+  void OnFrameFocused(content::RenderFrameHost* render_frame_host) override;
+  void AccessibilityEventReceived(
+      const content::AXEventNotificationDetails& content_event_bundle) override;
+  void AccessibilityLocationChangesReceived(
+      const std::vector<content::AXLocationChangeNotificationDetails>& locData)
+      override;
+
+  void OnWebContentsFocused(
+      content::RenderWidgetHost* render_widget_host) override;
+
+  // Manage observer objects. The observer must either outlive this object or
+  // remove itself before destruction. These methods can only be called on the
+  // UI thread.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+  bool HasObserver(Observer* observer) const;
+
+  class NavigationLock final {
+   private:
+    friend class CefBrowserHostImpl;
+    friend std::unique_ptr<NavigationLock>::deleter_type;
+
+    explicit NavigationLock(CefRefPtr<CefBrowserHostImpl> browser);
+    ~NavigationLock();
+
+    CefRefPtr<CefBrowserHostImpl> browser_;
+  };
+
+  // Block navigation-related events on NavigationLock life span.
+  std::unique_ptr<NavigationLock> CreateNavigationLock();
+
+ private:
+  static CefRefPtr<CefBrowserHostImpl> CreateInternal(
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefClient> client,
+      content::WebContents* web_contents,
+      bool own_web_contents,
+      scoped_refptr<CefBrowserInfo> browser_info,
+      CefRefPtr<CefBrowserHostImpl> opener,
+      bool is_devtools_popup,
+      CefRefPtr<CefRequestContextImpl> request_context,
+      std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
+      CefRefPtr<CefExtension> extension);
+
+  // content::NotificationObserver methods.
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
+
+  CefBrowserHostImpl(
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefClient> client,
+      content::WebContents* web_contents,
+      scoped_refptr<CefBrowserInfo> browser_info,
+      CefRefPtr<CefBrowserHostImpl> opener,
+      CefRefPtr<CefRequestContextImpl> request_context,
+      std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
+      CefRefPtr<CefExtension> extension);
+
+  void set_owned_web_contents(content::WebContents* owned_contents);
+
+  // Give the platform delegate an opportunity to create the host window.
+  bool CreateHostWindow();
+
+  // Create/delete the host for extensions.
+  void CreateExtensionHost(const extensions::Extension* extension,
+                           content::BrowserContext* browser_context,
+                           content::WebContents* host_contents,
+                           const GURL& url,
+                           extensions::ViewType host_type);
+  void DestroyExtensionHost();
+  void OnExtensionHostDeleted();
+
+  // Returns true if navigation actions are currently locked.
+  bool navigation_locked() const;
+  // Action to be executed once the navigation lock is released.
+  void set_pending_navigation_action(base::OnceClosure action);
+
+  void OnAddressChange(const GURL& url);
+  void OnLoadStart(CefRefPtr<CefFrame> frame,
+                   ui::PageTransition transition_type);
+  void OnLoadError(CefRefPtr<CefFrame> frame, const GURL& url, int error_code);
+  void OnLoadEnd(CefRefPtr<CefFrame> frame,
+                 const GURL& url,
+                 int http_status_code);
+  void OnFullscreenModeChange(bool fullscreen);
+  void OnTitleChange(const base::string16& title);
+
+  // Create the CefFileDialogManager if it doesn't already exist.
+  void EnsureFileDialogManager();
+
+  void ConfigureAutoResize();
+
+  void StartAudioCapturer();
+
+  void OnRecentlyAudibleTimerFired();
+
+  bool EnsureDevToolsManager();
+  void InitializeDevToolsRegistrationOnUIThread(
+      CefRefPtr<CefRegistration> registration);
+
+  CefBrowserSettings settings_;
+  CefRefPtr<CefClient> client_;
+  scoped_refptr<CefBrowserInfo> browser_info_;
+  CefWindowHandle opener_;
+  CefRefPtr<CefRequestContextImpl> request_context_;
+  std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate_;
+  const bool is_windowless_;
+  const bool is_views_hosted_;
+  CefWindowHandle host_window_handle_ = kNullWindowHandle;
+
+  // Non-nullptr if this object owns the WebContents. Will be nullptr for popup
+  // browsers between the calls to WebContentsCreated() and AddNewContents(),
+  // and may never be set if the parent browser is destroyed during popup
+  // creation.
+  std::unique_ptr<content::WebContents> owned_web_contents_;
+
+  // Volatile state information. All access must be protected by the state lock.
+  base::Lock state_lock_;
+  bool is_loading_ = false;
+  bool can_go_back_ = false;
+  bool can_go_forward_ = false;
+  bool has_document_ = false;
+  bool is_fullscreen_ = false;
+
+  // The currently focused frame, or nullptr if the main frame is focused.
+  CefRefPtr<CefFrameHostImpl> focused_frame_;
+
+  // Represents the current browser destruction state. Only accessed on the UI
+  // thread.
+  DestructionState destruction_state_ = DESTRUCTION_STATE_NONE;
+
+  // Navigation will not occur while |navigation_lock_count_| > 0.
+  // |pending_navigation_action_| will be executed when the lock is released.
+  // Only accessed on the UI thread.
+  int navigation_lock_count_ = 0;
+  base::OnceClosure pending_navigation_action_;
+
+  // True if the OS window hosting the browser has been destroyed. Only accessed
+  // on the UI thread.
+  bool window_destroyed_ = false;
+
+  // True if currently in the OnSetFocus callback. Only accessed on the UI
+  // thread.
+  bool is_in_onsetfocus_ = false;
+
+  // True if the focus is currently on an editable field on the page. Only
+  // accessed on the UI thread.
+  bool focus_on_editable_field_ = false;
+
+  // True if mouse cursor change is disabled.
+  bool mouse_cursor_change_disabled_ = false;
+
+  // Used for managing notification subscriptions.
+  std::unique_ptr<content::NotificationRegistrar> registrar_;
+
+  // Used for creating and managing file dialogs.
+  std::unique_ptr<CefFileDialogManager> file_dialog_manager_;
+
+  // Used for creating and managing JavaScript dialogs.
+  std::unique_ptr<CefJavaScriptDialogManager> javascript_dialog_manager_;
+
+  // Used for creating and managing context menus.
+  std::unique_ptr<CefMenuManager> menu_manager_;
+
+  // Used for creating and managing DevTools instances.
+  std::unique_ptr<CefDevToolsManager> devtools_manager_;
+
+  // Observers that want to be notified of changes to this object.
+  base::ObserverList<Observer>::Unchecked observers_;
+
+  // Used to provide unique incremental IDs for each find request.
+  int find_request_id_counter_ = 0;
+
+  // Used when the browser is hosting an extension.
+  extensions::ExtensionHost* extension_host_ = nullptr;
+  CefRefPtr<CefExtension> extension_;
+  bool is_background_host_ = false;
+
+  // Used for capturing audio for CefAudioHandler.
+  std::unique_ptr<CefAudioCapturer> audio_capturer_;
+
+  // Timer for determining when "recently audible" transitions to false. This
+  // starts running when a tab stops being audible, and is canceled if it starts
+  // being audible again before it fires.
+  base::OneShotTimer recently_audible_timer_;
+
+  // Used with auto-resize.
+  bool auto_resize_enabled_ = false;
+  gfx::Size auto_resize_min_;
+  gfx::Size auto_resize_max_;
+
+  IMPLEMENT_REFCOUNTING(CefBrowserHostImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserHostImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_HOST_IMPL_H_
diff --git a/src/libcef/browser/browser_info.cc b/src/libcef/browser/browser_info.cc
new file mode 100644
index 0000000..851e7d5
--- /dev/null
+++ b/src/libcef/browser/browser_info.cc
@@ -0,0 +1,373 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/browser_info.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/logging.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/public/browser/render_process_host.h"
+#include "ipc/ipc_message.h"
+
+CefBrowserInfo::FrameInfo::~FrameInfo() {
+  if (frame_ && !is_main_frame_) {
+    // Disassociate sub-frames from the browser.
+    frame_->Detach();
+  }
+}
+
+CefBrowserInfo::CefBrowserInfo(int browser_id,
+                               bool is_popup,
+                               bool is_windowless,
+                               CefRefPtr<CefDictionaryValue> extra_info)
+    : browser_id_(browser_id),
+      is_popup_(is_popup),
+      is_windowless_(is_windowless),
+      extra_info_(extra_info) {
+  DCHECK_GT(browser_id, 0);
+}
+
+CefBrowserInfo::~CefBrowserInfo() {}
+
+CefRefPtr<CefBrowserHostImpl> CefBrowserInfo::browser() const {
+  base::AutoLock lock_scope(lock_);
+  return browser_;
+}
+
+void CefBrowserInfo::SetBrowser(CefRefPtr<CefBrowserHostImpl> browser) {
+  base::AutoLock lock_scope(lock_);
+  browser_ = browser;
+
+  if (!browser) {
+    RemoveAllFrames();
+  }
+}
+
+void CefBrowserInfo::MaybeCreateFrame(content::RenderFrameHost* host,
+                                      bool is_guest_view) {
+  CEF_REQUIRE_UIT();
+
+  const auto frame_id = CefFrameHostImpl::MakeFrameId(host);
+  const int frame_tree_node_id = host->GetFrameTreeNodeId();
+  const bool is_main_frame = (host->GetParent() == nullptr);
+
+  // A speculative RFH will be created in response to a browser-initiated
+  // cross-origin navigation (e.g. via LoadURL) and eventually either discarded
+  // or swapped in based on whether the navigation is committed. We'll create a
+  // frame object for the speculative RFH so that it can be found by
+  // frame/routing ID. However, we won't replace the main frame with a
+  // speculative RFH until after it's swapped in, and we'll generally prefer to
+  // return a non-speculative RFH for the same node ID if one exists.
+  const bool is_speculative = (static_cast<content::RenderFrameHostImpl*>(host)
+                                   ->frame_tree_node()
+                                   ->render_manager()
+                                   ->current_frame_host() != host);
+
+  base::AutoLock lock_scope(lock_);
+  DCHECK(browser_);
+
+  const auto it = frame_id_map_.find(frame_id);
+  if (it != frame_id_map_.end()) {
+    auto info = it->second;
+
+#if DCHECK_IS_ON()
+    // Check that the frame info hasn't changed unexpectedly.
+    DCHECK_EQ(info->frame_id_, frame_id);
+    DCHECK_EQ(info->frame_tree_node_id_, frame_tree_node_id);
+    DCHECK_EQ(info->is_guest_view_, is_guest_view);
+    DCHECK_EQ(info->is_main_frame_, is_main_frame);
+#endif
+
+    if (!info->is_guest_view_ && info->is_speculative_ && !is_speculative) {
+      // Upgrade the frame info from speculative to non-speculative.
+      if (info->is_main_frame_) {
+        if (main_frame_) {
+          // Update the existing main frame object.
+          main_frame_->SetRenderFrameHost(host);
+          info->frame_ = main_frame_;
+        } else {
+          // Set the main frame object.
+          main_frame_ = info->frame_;
+        }
+      }
+      info->is_speculative_ = false;
+      MaybeUpdateFrameTreeNodeIdMap(info);
+    }
+    return;
+  }
+
+  auto frame_info = new FrameInfo;
+  frame_info->host_ = host;
+  frame_info->frame_id_ = frame_id;
+  frame_info->frame_tree_node_id_ = frame_tree_node_id;
+  frame_info->is_guest_view_ = is_guest_view;
+  frame_info->is_main_frame_ = is_main_frame;
+  frame_info->is_speculative_ = is_speculative;
+
+  // Guest views don't get their own CefBrowser or CefFrame objects.
+  if (!is_guest_view) {
+    if (is_main_frame && main_frame_ && !is_speculative) {
+      // Update the existing main frame object.
+      main_frame_->SetRenderFrameHost(host);
+      frame_info->frame_ = main_frame_;
+    } else {
+      // Create a new frame object.
+      frame_info->frame_ = new CefFrameHostImpl(this, host);
+      if (is_main_frame && !is_speculative) {
+        main_frame_ = frame_info->frame_;
+      }
+    }
+#if DCHECK_IS_ON()
+    // Check that the frame info hasn't changed unexpectedly.
+    DCHECK_EQ(frame_id, frame_info->frame_->GetIdentifier());
+    DCHECK_EQ(frame_info->is_main_frame_, frame_info->frame_->IsMain());
+#endif
+  }
+
+  browser_->request_context()->OnRenderFrameCreated(
+      host->GetProcess()->GetID(), host->GetRoutingID(), frame_tree_node_id,
+      is_main_frame, is_guest_view);
+
+  // Populate the lookup maps.
+  frame_id_map_.insert(std::make_pair(frame_id, frame_info));
+  MaybeUpdateFrameTreeNodeIdMap(frame_info);
+
+  // And finally set the ownership.
+  frame_info_set_.insert(base::WrapUnique(frame_info));
+}
+
+void CefBrowserInfo::RemoveFrame(content::RenderFrameHost* host) {
+  CEF_REQUIRE_UIT();
+
+  base::AutoLock lock_scope(lock_);
+
+  const auto frame_id = CefFrameHostImpl::MakeFrameId(host);
+
+  auto it = frame_id_map_.find(frame_id);
+  DCHECK(it != frame_id_map_.end());
+
+  auto frame_info = it->second;
+
+  browser_->request_context()->OnRenderFrameDeleted(
+      host->GetProcess()->GetID(), host->GetRoutingID(),
+      frame_info->frame_tree_node_id_, frame_info->is_main_frame_,
+      frame_info->is_guest_view_);
+
+  // Remove from the lookup maps.
+  frame_id_map_.erase(it);
+
+  // A new RFH with the same node ID may be added before the old RFH is deleted,
+  // or this might be a speculative RFH. Therefore only delete the map entry if
+  // it's currently pointing to the to-be-deleted frame info object.
+  {
+    auto it2 = frame_tree_node_id_map_.find(frame_info->frame_tree_node_id_);
+    if (it2 != frame_tree_node_id_map_.end() && it2->second == frame_info) {
+      frame_tree_node_id_map_.erase(frame_info->frame_tree_node_id_);
+    }
+  }
+
+  // And finally delete the frame info.
+  {
+    auto it2 = frame_info_set_.find(frame_info);
+    frame_info_set_.erase(it2);
+  }
+}
+
+CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetMainFrame() {
+  base::AutoLock lock_scope(lock_);
+  DCHECK(browser_);
+  if (!main_frame_) {
+    // Create a temporary object that will eventually be updated with real
+    // routing information.
+    main_frame_ =
+        new CefFrameHostImpl(this, true, CefFrameHostImpl::kInvalidFrameId);
+  }
+  return main_frame_;
+}
+
+CefRefPtr<CefFrameHostImpl> CefBrowserInfo::CreateTempSubFrame(
+    int64_t parent_frame_id) {
+  CefRefPtr<CefFrameHostImpl> parent = GetFrameForId(parent_frame_id);
+  if (!parent)
+    parent = GetMainFrame();
+  return new CefFrameHostImpl(this, false, parent->GetIdentifier());
+}
+
+CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForHost(
+    const content::RenderFrameHost* host,
+    bool* is_guest_view) const {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  if (!host)
+    return nullptr;
+
+  return GetFrameForId(CefFrameHostImpl::MakeFrameId(host), is_guest_view);
+}
+
+CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForRoute(
+    int32_t render_process_id,
+    int32_t render_routing_id,
+    bool* is_guest_view) const {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  if (render_process_id < 0 || render_routing_id < 0)
+    return nullptr;
+
+  return GetFrameForId(
+      CefFrameHostImpl::MakeFrameId(render_process_id, render_routing_id),
+      is_guest_view);
+}
+
+CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForId(
+    int64_t frame_id,
+    bool* is_guest_view) const {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  if (frame_id < 0)
+    return nullptr;
+
+  base::AutoLock lock_scope(lock_);
+
+  const auto it = frame_id_map_.find(frame_id);
+  if (it != frame_id_map_.end()) {
+    const auto info = it->second;
+
+    if (info->is_guest_view_) {
+      if (is_guest_view)
+        *is_guest_view = true;
+      return nullptr;
+    }
+
+    if (info->is_speculative_) {
+      if (info->is_main_frame_ && main_frame_) {
+        // Always prefer the non-speculative main frame.
+        return main_frame_;
+      } else {
+        // Always prefer an existing non-speculative frame for the same node ID.
+        bool is_guest_view_tmp;
+        auto frame = GetFrameForFrameTreeNodeInternal(info->frame_tree_node_id_,
+                                                      &is_guest_view_tmp);
+        if (is_guest_view_tmp) {
+          if (is_guest_view)
+            *is_guest_view = true;
+          return nullptr;
+        }
+        if (frame)
+          return frame;
+      }
+
+      LOG(WARNING) << "Returning a speculative frame for frame id " << frame_id;
+    }
+
+    DCHECK(info->frame_);
+    return info->frame_;
+  }
+
+  return nullptr;
+}
+
+CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForFrameTreeNode(
+    int frame_tree_node_id,
+    bool* is_guest_view) const {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  if (frame_tree_node_id < 0)
+    return nullptr;
+
+  base::AutoLock lock_scope(lock_);
+  return GetFrameForFrameTreeNodeInternal(frame_tree_node_id, is_guest_view);
+}
+
+CefBrowserInfo::FrameHostList CefBrowserInfo::GetAllFrames() const {
+  base::AutoLock lock_scope(lock_);
+  FrameHostList frames;
+  for (const auto& info : frame_info_set_) {
+    if (info->frame_ && !info->is_speculative_)
+      frames.insert(info->frame_);
+  }
+  return frames;
+}
+
+void CefBrowserInfo::MaybeUpdateFrameTreeNodeIdMap(FrameInfo* info) {
+  lock_.AssertAcquired();
+
+  auto it = frame_tree_node_id_map_.find(info->frame_tree_node_id_);
+  const bool has_entry = (it != frame_tree_node_id_map_.end());
+
+  if (has_entry && it->second == info) {
+    // Already mapping to |info|.
+    return;
+  }
+
+  // Don't replace an existing node ID entry with a speculative RFH, but do
+  // add an entry if one doesn't already exist.
+  if (!info->is_speculative_ || !has_entry) {
+    // A new RFH with the same node ID may be added before the old RFH is
+    // deleted. To avoid duplicate entries in the map remove the old entry, if
+    // any, before adding the new entry.
+    if (has_entry)
+      frame_tree_node_id_map_.erase(it);
+
+    frame_tree_node_id_map_.insert(
+        std::make_pair(info->frame_tree_node_id_, info));
+  }
+}
+
+CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForFrameTreeNodeInternal(
+    int frame_tree_node_id,
+    bool* is_guest_view) const {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  lock_.AssertAcquired();
+
+  const auto it = frame_tree_node_id_map_.find(frame_tree_node_id);
+  if (it != frame_tree_node_id_map_.end()) {
+    const auto info = it->second;
+
+    LOG_IF(WARNING, info->is_speculative_)
+        << "Returning a speculative frame for node id " << frame_tree_node_id;
+
+    if (info->is_guest_view_) {
+      if (is_guest_view)
+        *is_guest_view = true;
+      return nullptr;
+    }
+
+    DCHECK(info->frame_);
+    return info->frame_;
+  }
+
+  return nullptr;
+}
+
+void CefBrowserInfo::RemoveAllFrames() {
+  lock_.AssertAcquired();
+
+  // Clear the lookup maps.
+  frame_id_map_.clear();
+  frame_tree_node_id_map_.clear();
+
+  // Explicitly Detach main frames.
+  for (auto& info : frame_info_set_) {
+    if (info->frame_ && info->is_main_frame_)
+      info->frame_->Detach();
+  }
+
+  if (main_frame_) {
+    main_frame_->Detach();
+    main_frame_ = nullptr;
+  }
+
+  // And finally delete the frame info.
+  frame_info_set_.clear();
+}
diff --git a/src/libcef/browser/browser_info.h b/src/libcef/browser/browser_info.h
new file mode 100644
index 0000000..c48328f
--- /dev/null
+++ b/src/libcef/browser/browser_info.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_INFO_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_INFO_H_
+#pragma once
+
+#include <set>
+#include <unordered_map>
+
+#include "include/internal/cef_ptr.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/values.h"
+
+namespace content {
+class RenderFrameHost;
+}
+
+class CefBrowserHostImpl;
+class CefFrameHostImpl;
+
+// CefBrowserInfo is used to associate a browser ID and render view/process
+// IDs with a particular CefBrowserHostImpl. Render view/process IDs may change
+// during the lifetime of a single CefBrowserHostImpl.
+//
+// CefBrowserInfo objects are managed by CefBrowserInfoManager and should not be
+// created directly.
+class CefBrowserInfo : public base::RefCountedThreadSafe<CefBrowserInfo> {
+ public:
+  CefBrowserInfo(int browser_id,
+                 bool is_popup,
+                 bool is_windowless,
+                 CefRefPtr<CefDictionaryValue> extra_info);
+
+  int browser_id() const { return browser_id_; }
+  bool is_popup() const { return is_popup_; }
+  bool is_windowless() const { return is_windowless_; }
+  CefRefPtr<CefDictionaryValue> extra_info() const { return extra_info_; }
+
+  // May return NULL if the browser has not yet been created or if the browser
+  // has been destroyed.
+  CefRefPtr<CefBrowserHostImpl> browser() const;
+
+  // Set or clear the browser. Called from the CefBrowserHostImpl constructor
+  // (to set) and DestroyBrowser (to clear).
+  void SetBrowser(CefRefPtr<CefBrowserHostImpl> browser);
+
+  // Ensure that a frame record exists for |host|. Called for the main frame
+  // when the RenderView is created, or for a sub-frame when the associated
+  // RenderFrame is created in the renderer process.
+  // Called from CefBrowserHostImpl::RenderFrameCreated (is_guest_view = false)
+  // or CefMimeHandlerViewGuestDelegate::OnGuestAttached (is_guest_view = true).
+  void MaybeCreateFrame(content::RenderFrameHost* host, bool is_guest_view);
+
+  // Remove the frame record for |host|. Called for the main frame when the
+  // RenderView is destroyed, or for a sub-frame when the associated RenderFrame
+  // is destroyed in the renderer process.
+  // Called from CefBrowserHostImpl::FrameDeleted or
+  // CefMimeHandlerViewGuestDelegate::OnGuestDetached.
+  void RemoveFrame(content::RenderFrameHost* host);
+
+  // Returns the main frame object. This object will remain valid until the
+  // browser is destroyed even though the indentifier may change with cross-
+  // origin navigations. Furthermore, calling LoadURL on this object will always
+  // behave as expected because the call is routed through the browser's
+  // NavigationController.
+  CefRefPtr<CefFrameHostImpl> GetMainFrame();
+
+  // Creates a temporary sub-frame object for situations during navigation or
+  // resource loading where a RFH does not yet exist. If |parent_frame_id|
+  // is invalid the current main frame will be specified as the parent.
+  // Temporary frame objects are not tracked but will be implicitly detached
+  // on browser destruction.
+  CefRefPtr<CefFrameHostImpl> CreateTempSubFrame(int64_t parent_frame_id);
+
+  // Returns the frame object matching the specified host or nullptr if no match
+  // is found. Nullptr will also be returned if a guest view match is found
+  // because we don't create frame objects for guest views. If |is_guest_view|
+  // is non-nullptr it will be set to true in this case. Must be called on the
+  // UI thread.
+  CefRefPtr<CefFrameHostImpl> GetFrameForHost(
+      const content::RenderFrameHost* host,
+      bool* is_guest_view = nullptr) const;
+
+  // Returns the frame object matching the specified IDs or nullptr if no match
+  // is found. Nullptr will also be returned if a guest view match is found
+  // because we don't create frame objects for guest views. If |is_guest_view|
+  // is non-nullptr it will be set to true in this case. Safe to call from any
+  // thread.
+  CefRefPtr<CefFrameHostImpl> GetFrameForRoute(
+      int32_t render_process_id,
+      int32_t render_routing_id,
+      bool* is_guest_view = nullptr) const;
+
+  // Returns the frame object matching the specified ID or nullptr if no match
+  // is found. Nullptr will also be returned if a guest view match is found
+  // because we don't create frame objects for guest views. If |is_guest_view|
+  // is non-nullptr it will be set to true in this case. Safe to call from any
+  // thread.
+  CefRefPtr<CefFrameHostImpl> GetFrameForId(
+      int64_t frame_id,
+      bool* is_guest_view = nullptr) const;
+
+  // Returns the frame object matching the specified ID or nullptr if no match
+  // is found. Nullptr will also be returned if a guest view match is found
+  // because we don't create frame objects for guest views. If |is_guest_view|
+  // is non-nullptr it will be set to true in this case. Safe to call from any
+  // thread.
+  CefRefPtr<CefFrameHostImpl> GetFrameForFrameTreeNode(
+      int frame_tree_node_id,
+      bool* is_guest_view = nullptr) const;
+
+  // Returns all non-speculative frame objects that currently exist. Guest views
+  // will be excluded because they don't have a frame object. Safe to call from
+  // any thread.
+  typedef std::set<CefRefPtr<CefFrameHostImpl>> FrameHostList;
+  FrameHostList GetAllFrames() const;
+
+ private:
+  friend class base::RefCountedThreadSafe<CefBrowserInfo>;
+
+  virtual ~CefBrowserInfo();
+
+  struct FrameInfo {
+    ~FrameInfo();
+
+    content::RenderFrameHost* host_;
+    int64_t frame_id_;  // Combination of render_process_id + render_routing_id.
+    int frame_tree_node_id_;
+    bool is_guest_view_;
+    bool is_main_frame_;
+    bool is_speculative_;
+    CefRefPtr<CefFrameHostImpl> frame_;
+  };
+
+  void MaybeUpdateFrameTreeNodeIdMap(FrameInfo* info);
+
+  CefRefPtr<CefFrameHostImpl> GetFrameForFrameTreeNodeInternal(
+      int frame_tree_node_id,
+      bool* is_guest_view = nullptr) const;
+
+  void RemoveAllFrames();
+
+  int browser_id_;
+  bool is_popup_;
+  bool is_windowless_;
+  CefRefPtr<CefDictionaryValue> extra_info_;
+
+  mutable base::Lock lock_;
+
+  // The below members must be protected by |lock_|.
+
+  CefRefPtr<CefBrowserHostImpl> browser_;
+
+  // Owner of FrameInfo structs.
+  typedef std::set<std::unique_ptr<FrameInfo>, base::UniquePtrComparator>
+      FrameInfoSet;
+  FrameInfoSet frame_info_set_;
+
+  // Map a frame ID (e.g. MakeFrameId(process_id, routing_id)) to one frame.
+  typedef std::unordered_map<int64_t, FrameInfo*> FrameIDMap;
+  FrameIDMap frame_id_map_;
+
+  // Map a frame_tree_node_id to one frame.
+  typedef std::unordered_map<int, FrameInfo*> FrameTreeNodeIDMap;
+  FrameTreeNodeIDMap frame_tree_node_id_map_;
+
+  // The current main frame.
+  CefRefPtr<CefFrameHostImpl> main_frame_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserInfo);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_INFO_H_
diff --git a/src/libcef/browser/browser_info_manager.cc b/src/libcef/browser/browser_info_manager.cc
new file mode 100644
index 0000000..23622dc
--- /dev/null
+++ b/src/libcef/browser/browser_info_manager.cc
@@ -0,0 +1,569 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/browser_info_manager.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/extensions/browser_extensions_util.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/child_process_host.h"
+
+namespace {
+
+// Timeout delay for new browser info responses.
+const int64_t kNewBrowserInfoResponseTimeoutMs = 2000;
+
+void TranslatePopupFeatures(const blink::mojom::WindowFeatures& webKitFeatures,
+                            CefPopupFeatures& features) {
+  features.x = static_cast<int>(webKitFeatures.x);
+  features.xSet = webKitFeatures.has_x;
+  features.y = static_cast<int>(webKitFeatures.y);
+  features.ySet = webKitFeatures.has_y;
+  features.width = static_cast<int>(webKitFeatures.width);
+  features.widthSet = webKitFeatures.has_width;
+  features.height = static_cast<int>(webKitFeatures.height);
+  features.heightSet = webKitFeatures.has_height;
+
+  features.menuBarVisible = webKitFeatures.menu_bar_visible;
+  features.statusBarVisible = webKitFeatures.status_bar_visible;
+  features.toolBarVisible = webKitFeatures.tool_bar_visible;
+  features.scrollbarsVisible = webKitFeatures.scrollbars_visible;
+}
+
+CefBrowserInfoManager* g_info_manager = nullptr;
+
+}  // namespace
+
+CefBrowserInfoManager::CefBrowserInfoManager() {
+  DCHECK(!g_info_manager);
+  g_info_manager = this;
+}
+
+CefBrowserInfoManager::~CefBrowserInfoManager() {
+  DCHECK(browser_info_list_.empty());
+  g_info_manager = nullptr;
+}
+
+// static
+CefBrowserInfoManager* CefBrowserInfoManager::GetInstance() {
+  return g_info_manager;
+}
+
+scoped_refptr<CefBrowserInfo> CefBrowserInfoManager::CreateBrowserInfo(
+    bool is_popup,
+    bool is_windowless,
+    CefRefPtr<CefDictionaryValue> extra_info) {
+  base::AutoLock lock_scope(browser_info_lock_);
+
+  scoped_refptr<CefBrowserInfo> browser_info = new CefBrowserInfo(
+      ++next_browser_id_, is_popup, is_windowless, extra_info);
+  browser_info_list_.push_back(browser_info);
+
+  return browser_info;
+}
+
+scoped_refptr<CefBrowserInfo> CefBrowserInfoManager::CreatePopupBrowserInfo(
+    content::WebContents* new_contents,
+    bool is_windowless,
+    CefRefPtr<CefDictionaryValue> extra_info) {
+  base::AutoLock lock_scope(browser_info_lock_);
+
+  auto frame_host = new_contents->GetMainFrame();
+  const int render_process_id = frame_host->GetProcess()->GetID();
+  const auto frame_id = CefFrameHostImpl::MakeFrameId(frame_host);
+
+  scoped_refptr<CefBrowserInfo> browser_info =
+      new CefBrowserInfo(++next_browser_id_, true, is_windowless, extra_info);
+  browser_info_list_.push_back(browser_info);
+
+  // Continue any pending NewBrowserInfo request.
+  auto it = pending_new_browser_info_map_.find(frame_id);
+  if (it != pending_new_browser_info_map_.end()) {
+    SendNewBrowserInfoResponse(render_process_id, browser_info,
+                               false /* is_guest_view */,
+                               it->second->reply_msg);
+    pending_new_browser_info_map_.erase(it);
+  }
+
+  return browser_info;
+}
+
+bool CefBrowserInfoManager::CanCreateWindow(
+    content::RenderFrameHost* opener,
+    const GURL& target_url,
+    const content::Referrer& referrer,
+    const std::string& frame_name,
+    WindowOpenDisposition disposition,
+    const blink::mojom::WindowFeatures& features,
+    bool user_gesture,
+    bool opener_suppressed,
+    bool* no_javascript_access) {
+  CEF_REQUIRE_UIT();
+
+  bool is_guest_view = false;
+  CefRefPtr<CefBrowserHostImpl> browser =
+      extensions::GetOwnerBrowserForHost(opener, &is_guest_view);
+  DCHECK(browser.get());
+  if (!browser.get()) {
+    // Cancel the popup.
+    return false;
+  }
+
+  if (is_guest_view) {
+    content::OpenURLParams params(target_url, referrer, disposition,
+                                  ui::PAGE_TRANSITION_LINK, true);
+    params.user_gesture = user_gesture;
+
+    // Pass navigation to the owner browser.
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefBrowserHostImpl::OpenURLFromTab),
+                   browser.get(), nullptr, params));
+
+    // Cancel the popup.
+    return false;
+  }
+
+  CefRefPtr<CefClient> client = browser->GetClient();
+  bool allow = true;
+
+  std::unique_ptr<CefWindowInfo> window_info(new CefWindowInfo);
+
+#if defined(OS_WIN)
+  window_info->SetAsPopup(nullptr, CefString());
+#endif
+
+  auto pending_popup = std::make_unique<CefBrowserInfoManager::PendingPopup>();
+  pending_popup->step = CefBrowserInfoManager::PendingPopup::CAN_CREATE_WINDOW;
+  pending_popup->opener_render_process_id = opener->GetProcess()->GetID();
+  pending_popup->opener_render_routing_id = opener->GetRoutingID();
+  pending_popup->target_url = target_url;
+  pending_popup->target_frame_name = frame_name;
+
+  // Start with the current browser's settings.
+  pending_popup->client = client;
+  pending_popup->settings = browser->settings();
+
+  if (client.get()) {
+    CefRefPtr<CefLifeSpanHandler> handler = client->GetLifeSpanHandler();
+    if (handler.get()) {
+      CefRefPtr<CefFrame> opener_frame = browser->GetFrameForHost(opener);
+      DCHECK(opener_frame);
+
+      CefPopupFeatures cef_features;
+      TranslatePopupFeatures(features, cef_features);
+
+#if (defined(OS_WIN) || defined(OS_MACOSX))
+      // Default to the size from the popup features.
+      if (cef_features.xSet)
+        window_info->x = cef_features.x;
+      if (cef_features.ySet)
+        window_info->y = cef_features.y;
+      if (cef_features.widthSet)
+        window_info->width = cef_features.width;
+      if (cef_features.heightSet)
+        window_info->height = cef_features.height;
+#endif
+
+      allow = !handler->OnBeforePopup(
+          browser.get(), opener_frame, pending_popup->target_url.spec(),
+          pending_popup->target_frame_name,
+          static_cast<cef_window_open_disposition_t>(disposition), user_gesture,
+          cef_features, *window_info, pending_popup->client,
+          pending_popup->settings, pending_popup->extra_info,
+          no_javascript_access);
+    }
+  }
+
+  if (allow) {
+    CefBrowserHostImpl::CreateParams create_params;
+
+    if (!browser->IsViewsHosted())
+      create_params.window_info = std::move(window_info);
+
+    create_params.settings = pending_popup->settings;
+    create_params.client = pending_popup->client;
+    create_params.extra_info = pending_popup->extra_info;
+
+    pending_popup->platform_delegate =
+        CefBrowserPlatformDelegate::Create(create_params);
+    CHECK(pending_popup->platform_delegate.get());
+
+    // Between the calls to CanCreateWindow and GetCustomWebContentsView
+    // RenderViewHostImpl::CreateNewWindow() will call
+    // RenderProcessHostImpl::FilterURL() which, in the case of "javascript:"
+    // URIs, rewrites the URL to "about:blank". We need to apply the same filter
+    // otherwise GetCustomWebContentsView will fail to retrieve the PopupInfo.
+    opener->GetProcess()->FilterURL(false, &pending_popup->target_url);
+
+    PushPendingPopup(std::move(pending_popup));
+  }
+
+  return allow;
+}
+
+void CefBrowserInfoManager::GetCustomWebContentsView(
+    const GURL& target_url,
+    int opener_render_process_id,
+    int opener_render_routing_id,
+    content::WebContentsView** view,
+    content::RenderViewHostDelegateView** delegate_view) {
+  CEF_REQUIRE_UIT();
+
+  std::unique_ptr<CefBrowserInfoManager::PendingPopup> pending_popup =
+      PopPendingPopup(CefBrowserInfoManager::PendingPopup::CAN_CREATE_WINDOW,
+                      opener_render_process_id, opener_render_routing_id,
+                      target_url);
+  DCHECK(pending_popup.get());
+  DCHECK(pending_popup->platform_delegate.get());
+
+  if (pending_popup->platform_delegate->IsWindowless()) {
+    pending_popup->platform_delegate->CreateViewForWebContents(view,
+                                                               delegate_view);
+  }
+
+  pending_popup->step =
+      CefBrowserInfoManager::PendingPopup::GET_CUSTOM_WEB_CONTENTS_VIEW;
+  PushPendingPopup(std::move(pending_popup));
+}
+
+void CefBrowserInfoManager::WebContentsCreated(
+    const GURL& target_url,
+    int opener_render_process_id,
+    int opener_render_routing_id,
+    CefBrowserSettings& settings,
+    CefRefPtr<CefClient>& client,
+    std::unique_ptr<CefBrowserPlatformDelegate>& platform_delegate,
+    CefRefPtr<CefDictionaryValue>& extra_info) {
+  CEF_REQUIRE_UIT();
+
+  std::unique_ptr<CefBrowserInfoManager::PendingPopup> pending_popup =
+      PopPendingPopup(
+          CefBrowserInfoManager::PendingPopup::GET_CUSTOM_WEB_CONTENTS_VIEW,
+          opener_render_process_id, opener_render_routing_id, target_url);
+  DCHECK(pending_popup.get());
+  DCHECK(pending_popup->platform_delegate.get());
+
+  settings = pending_popup->settings;
+  client = pending_popup->client;
+  platform_delegate = std::move(pending_popup->platform_delegate);
+  extra_info = pending_popup->extra_info;
+}
+
+void CefBrowserInfoManager::OnGetNewBrowserInfo(int render_process_id,
+                                                int render_routing_id,
+                                                IPC::Message* reply_msg) {
+  DCHECK_NE(render_process_id, content::ChildProcessHost::kInvalidUniqueID);
+  DCHECK_GT(render_routing_id, 0);
+  DCHECK(reply_msg);
+
+  base::AutoLock lock_scope(browser_info_lock_);
+
+  bool is_guest_view = false;
+
+  scoped_refptr<CefBrowserInfo> browser_info =
+      GetBrowserInfo(render_process_id, render_routing_id, &is_guest_view);
+
+  if (browser_info.get()) {
+    // Send the response immediately.
+    SendNewBrowserInfoResponse(render_process_id, browser_info, is_guest_view,
+                               reply_msg);
+    return;
+  }
+
+  const auto frame_id =
+      CefFrameHostImpl::MakeFrameId(render_process_id, render_routing_id);
+
+  // Verify that no request for the same route is currently queued.
+  DCHECK(pending_new_browser_info_map_.find(frame_id) ==
+         pending_new_browser_info_map_.end());
+
+  const int timeout_id = ++next_timeout_id_;
+
+  // Queue the request.
+  std::unique_ptr<PendingNewBrowserInfo> pending(new PendingNewBrowserInfo());
+  pending->render_process_id = render_process_id;
+  pending->render_routing_id = render_routing_id;
+  pending->timeout_id = timeout_id;
+  pending->reply_msg = reply_msg;
+  pending_new_browser_info_map_.insert(
+      std::make_pair(frame_id, std::move(pending)));
+
+  // Register a timeout for the pending response so that the renderer process
+  // doesn't hang forever.
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableNewBrowserInfoTimeout)) {
+    CEF_POST_DELAYED_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBrowserInfoManager::TimeoutNewBrowserInfoResponse,
+                       frame_id, timeout_id),
+        kNewBrowserInfoResponseTimeoutMs);
+  }
+}
+
+void CefBrowserInfoManager::RemoveBrowserInfo(
+    scoped_refptr<CefBrowserInfo> browser_info) {
+  base::AutoLock lock_scope(browser_info_lock_);
+
+  BrowserInfoList::iterator it = browser_info_list_.begin();
+  for (; it != browser_info_list_.end(); ++it) {
+    if (*it == browser_info) {
+      browser_info_list_.erase(it);
+      return;
+    }
+  }
+
+  NOTREACHED();
+}
+
+void CefBrowserInfoManager::DestroyAllBrowsers() {
+  BrowserInfoList list;
+
+  {
+    base::AutoLock lock_scope(browser_info_lock_);
+    list = browser_info_list_;
+  }
+
+  // Destroy any remaining browser windows.
+  if (!list.empty()) {
+    BrowserInfoList::iterator it = list.begin();
+    for (; it != list.end(); ++it) {
+      CefRefPtr<CefBrowserHostImpl> browser = (*it)->browser();
+      DCHECK(browser.get());
+      if (browser.get()) {
+        // DestroyBrowser will call RemoveBrowserInfo.
+        browser->DestroyBrowser();
+      }
+    }
+  }
+
+#if DCHECK_IS_ON()
+  {
+    // Verify that all browser windows have been destroyed.
+    base::AutoLock lock_scope(browser_info_lock_);
+    DCHECK(browser_info_list_.empty());
+  }
+#endif
+}
+
+scoped_refptr<CefBrowserInfo>
+CefBrowserInfoManager::GetBrowserInfoForFrameRoute(int render_process_id,
+                                                   int render_routing_id,
+                                                   bool* is_guest_view) {
+  base::AutoLock lock_scope(browser_info_lock_);
+  return GetBrowserInfo(render_process_id, render_routing_id, is_guest_view);
+}
+
+scoped_refptr<CefBrowserInfo>
+CefBrowserInfoManager::GetBrowserInfoForFrameTreeNode(int frame_tree_node_id,
+                                                      bool* is_guest_view) {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  if (frame_tree_node_id < 0)
+    return nullptr;
+
+  base::AutoLock lock_scope(browser_info_lock_);
+
+  for (const auto& browser_info : browser_info_list_) {
+    bool is_guest_view_tmp;
+    auto frame = browser_info->GetFrameForFrameTreeNode(frame_tree_node_id,
+                                                        &is_guest_view_tmp);
+    if (frame || is_guest_view_tmp) {
+      if (is_guest_view)
+        *is_guest_view = is_guest_view_tmp;
+      return browser_info;
+    }
+  }
+
+  return nullptr;
+}
+
+CefBrowserInfoManager::BrowserInfoList
+CefBrowserInfoManager::GetBrowserInfoList() {
+  base::AutoLock lock_scope(browser_info_lock_);
+  BrowserInfoList copy;
+  copy.assign(browser_info_list_.begin(), browser_info_list_.end());
+  return copy;
+}
+
+void CefBrowserInfoManager::RenderProcessHostDestroyed(
+    content::RenderProcessHost* host) {
+  CEF_REQUIRE_UIT();
+
+  const int render_process_id = host->GetID();
+  DCHECK_GT(render_process_id, 0);
+
+  // Remove all pending requests that reference the destroyed host.
+  {
+    base::AutoLock lock_scope(browser_info_lock_);
+
+    PendingNewBrowserInfoMap::iterator it =
+        pending_new_browser_info_map_.begin();
+    while (it != pending_new_browser_info_map_.end()) {
+      auto info = it->second.get();
+      if (info->render_process_id == render_process_id)
+        it = pending_new_browser_info_map_.erase(it);
+      else
+        ++it;
+    }
+  }
+
+  // Remove all pending popups that reference the destroyed host as the opener.
+  {
+    PendingPopupList::iterator it = pending_popup_list_.begin();
+    while (it != pending_popup_list_.end()) {
+      PendingPopup* popup = it->get();
+      if (popup->opener_render_process_id == render_process_id) {
+        it = pending_popup_list_.erase(it);
+      } else {
+        ++it;
+      }
+    }
+  }
+}
+
+void CefBrowserInfoManager::PushPendingPopup(
+    std::unique_ptr<PendingPopup> popup) {
+  CEF_REQUIRE_UIT();
+  pending_popup_list_.push_back(std::move(popup));
+}
+
+std::unique_ptr<CefBrowserInfoManager::PendingPopup>
+CefBrowserInfoManager::PopPendingPopup(PendingPopup::Step step,
+                                       int opener_render_process_id,
+                                       int opener_render_routing_id,
+                                       const GURL& target_url) {
+  CEF_REQUIRE_UIT();
+  DCHECK_GT(opener_render_process_id, 0);
+  DCHECK_GT(opener_render_routing_id, 0);
+
+  PendingPopupList::iterator it = pending_popup_list_.begin();
+  for (; it != pending_popup_list_.end(); ++it) {
+    PendingPopup* popup = it->get();
+    if (popup->step == step &&
+        popup->opener_render_process_id == opener_render_process_id &&
+        popup->opener_render_routing_id == opener_render_routing_id &&
+        popup->target_url == target_url) {
+      // Transfer ownership of the pointer.
+      it->release();
+      pending_popup_list_.erase(it);
+      return base::WrapUnique(popup);
+    }
+  }
+
+  return nullptr;
+}
+
+scoped_refptr<CefBrowserInfo> CefBrowserInfoManager::GetBrowserInfo(
+    int render_process_id,
+    int render_routing_id,
+    bool* is_guest_view) {
+  browser_info_lock_.AssertAcquired();
+
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  if (render_process_id < 0 || render_routing_id < 0)
+    return nullptr;
+
+  for (const auto& browser_info : browser_info_list_) {
+    bool is_guest_view_tmp;
+    auto frame = browser_info->GetFrameForRoute(
+        render_process_id, render_routing_id, &is_guest_view_tmp);
+    if (frame || is_guest_view_tmp) {
+      if (is_guest_view)
+        *is_guest_view = is_guest_view_tmp;
+      return browser_info;
+    }
+  }
+
+  return nullptr;
+}
+
+// static
+void CefBrowserInfoManager::SendNewBrowserInfoResponse(
+    int render_process_id,
+    scoped_refptr<CefBrowserInfo> browser_info,
+    bool is_guest_view,
+    IPC::Message* reply_msg) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(&CefBrowserInfoManager::SendNewBrowserInfoResponse,
+                   render_process_id, browser_info, is_guest_view, reply_msg));
+    return;
+  }
+
+  content::RenderProcessHost* host =
+      content::RenderProcessHost::FromID(render_process_id);
+  if (!host) {
+    delete reply_msg;
+    return;
+  }
+
+  CefProcessHostMsg_GetNewBrowserInfo_Params params;
+  params.is_guest_view = is_guest_view;
+
+  if (browser_info) {
+    params.browser_id = browser_info->browser_id();
+    params.is_windowless = browser_info->is_windowless();
+    params.is_popup = browser_info->is_popup();
+
+    auto extra_info = browser_info->extra_info();
+    if (extra_info) {
+      auto extra_info_impl =
+          static_cast<CefDictionaryValueImpl*>(extra_info.get());
+      auto extra_info_value = extra_info_impl->CopyValue();
+      extra_info_value->Swap(&params.extra_info);
+    }
+  } else {
+    // The new browser info response has timed out.
+    params.browser_id = -1;
+  }
+
+  CefProcessHostMsg_GetNewBrowserInfo::WriteReplyParams(reply_msg, params);
+  host->Send(reply_msg);
+}
+
+// static
+void CefBrowserInfoManager::TimeoutNewBrowserInfoResponse(int64_t frame_id,
+                                                          int timeout_id) {
+  if (!g_info_manager)
+    return;
+
+  base::AutoLock lock_scope(g_info_manager->browser_info_lock_);
+
+  // Continue the NewBrowserInfo request if it's still pending.
+  auto it = g_info_manager->pending_new_browser_info_map_.find(frame_id);
+  if (it != g_info_manager->pending_new_browser_info_map_.end()) {
+    const auto& pending_info = it->second;
+    // Don't accidentally timeout a new request for the same frame.
+    if (pending_info->timeout_id != timeout_id)
+      return;
+
+    LOG(ERROR) << "Timeout of new browser info response for frame process id "
+               << pending_info->render_process_id << " and routing id "
+               << pending_info->render_routing_id;
+
+    SendNewBrowserInfoResponse(pending_info->render_process_id, nullptr,
+                               false /* is_guest_view */,
+                               pending_info->reply_msg);
+    g_info_manager->pending_new_browser_info_map_.erase(it);
+  }
+}
diff --git a/src/libcef/browser/browser_info_manager.h b/src/libcef/browser/browser_info_manager.h
new file mode 100644
index 0000000..3bdc1c7
--- /dev/null
+++ b/src/libcef/browser/browser_info_manager.h
@@ -0,0 +1,236 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_INFO_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_INFO_MANAGER_H_
+#pragma once
+
+#include "include/cef_client.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "libcef/browser/browser_info.h"
+
+#include "base/synchronization/lock.h"
+#include "content/public/browser/render_process_host_observer.h"
+#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
+#include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
+
+namespace blink {
+struct WebWindowFeatures;
+}
+
+namespace content {
+struct Referrer;
+class RenderFrameHost;
+class RenderViewHostDelegateView;
+class WebContents;
+class WebContentsView;
+}  // namespace content
+
+namespace IPC {
+class Message;
+}
+
+class CefBrowserPlatformDelegate;
+
+// Singleton object for managing BrowserInfo instances.
+class CefBrowserInfoManager : public content::RenderProcessHostObserver {
+ public:
+  CefBrowserInfoManager();
+  ~CefBrowserInfoManager() override;
+
+  // Returns this singleton instance of this class.
+  static CefBrowserInfoManager* GetInstance();
+
+  // Called from CefBrowserHostImpl::Create when a new browser is being created
+  // directly. In this case |is_popup| will be true only for DevTools browsers.
+  scoped_refptr<CefBrowserInfo> CreateBrowserInfo(
+      bool is_popup,
+      bool is_windowless,
+      CefRefPtr<CefDictionaryValue> extra_info);
+
+  // Called from CefBrowserHostImpl::WebContentsCreated when a new browser is
+  // being created for a traditional popup (e.g. window.open() or targeted
+  // link). If any OnGetNewBrowserInfo requests are pending for the popup the
+  // response will be sent when this method is called.
+  scoped_refptr<CefBrowserInfo> CreatePopupBrowserInfo(
+      content::WebContents* new_contents,
+      bool is_windowless,
+      CefRefPtr<CefDictionaryValue> extra_info);
+
+  // Called from CefContentBrowserClient::CanCreateWindow. See comments on
+  // PendingPopup for more information.
+  bool CanCreateWindow(content::RenderFrameHost* opener,
+                       const GURL& target_url,
+                       const content::Referrer& referrer,
+                       const std::string& frame_name,
+                       WindowOpenDisposition disposition,
+                       const blink::mojom::WindowFeatures& features,
+                       bool user_gesture,
+                       bool opener_suppressed,
+                       bool* no_javascript_access);
+
+  // Called from CefBrowserHostImpl::GetCustomWebContentsView. See comments on
+  // PendingPopup for more information.
+  void GetCustomWebContentsView(
+      const GURL& target_url,
+      int opener_render_process_id,
+      int opener_render_routing_id,
+      content::WebContentsView** view,
+      content::RenderViewHostDelegateView** delegate_view);
+
+  // Called from CefBrowserHostImpl::WebContentsCreated. See comments on
+  // PendingPopup for more information.
+  void WebContentsCreated(
+      const GURL& target_url,
+      int opener_render_process_id,
+      int opener_render_routing_id,
+      CefBrowserSettings& settings,
+      CefRefPtr<CefClient>& client,
+      std::unique_ptr<CefBrowserPlatformDelegate>& platform_delegate,
+      CefRefPtr<CefDictionaryValue>& extra_info);
+
+  // Called from CefBrowserMessageFilter::OnGetNewBrowserInfo for delivering
+  // browser info to the renderer process. If the browser info already exists
+  // the response will be sent immediately. Otherwise, the response will be sent
+  // when CreatePopupBrowserInfo creates the browser info. The info will already
+  // exist for explicitly created browsers and guest views. It may sometimes
+  // already exist for traditional popup browsers depending on timing. See
+  // comments on PendingPopup for more information.
+  void OnGetNewBrowserInfo(int render_process_id,
+                           int render_routing_id,
+                           IPC::Message* reply_msg);
+
+  // Called from CefBrowserHostImpl::DestroyBrowser() when a browser is
+  // destroyed.
+  void RemoveBrowserInfo(scoped_refptr<CefBrowserInfo> browser_info);
+
+  // Called from CefContext::FinishShutdownOnUIThread() to destroy all browsers.
+  void DestroyAllBrowsers();
+
+  // Returns the CefBrowserInfo matching the specified IDs or nullptr if no
+  // match is found. It is allowed to add new callers of this method but
+  // consider using CefBrowserHostImpl::GetBrowserForFrameRoute() or
+  // extensions::GetOwnerBrowserForFrameRoute() instead. If |is_guest_view| is
+  // non-nullptr it will be set to true if the IDs match a guest view associated
+  // with the returned browser info instead of the browser itself.
+  scoped_refptr<CefBrowserInfo> GetBrowserInfoForFrameRoute(
+      int render_process_id,
+      int render_routing_id,
+      bool* is_guest_view = nullptr);
+
+  // Returns the CefBrowserInfo matching the specified ID or nullptr if no match
+  // is found. It is allowed to add new callers of this method but consider
+  // using CefBrowserHostImpl::GetBrowserForFrameTreeNode() instead. If
+  // |is_guest_view| is non-nullptr it will be set to true if the IDs match a
+  // guest view associated with the returned browser info instead of the browser
+  // itself.
+  scoped_refptr<CefBrowserInfo> GetBrowserInfoForFrameTreeNode(
+      int frame_tree_node_id,
+      bool* is_guest_view = nullptr);
+
+  // Returns all existing CefBrowserInfo objects.
+  typedef std::list<scoped_refptr<CefBrowserInfo>> BrowserInfoList;
+  BrowserInfoList GetBrowserInfoList();
+
+ private:
+  // RenderProcessHostObserver methods:
+  void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
+
+  // Store state information about pending popups. Call order is:
+  // - CefContentBrowserClient::CanCreateWindow (UIT)
+  //   Provides an opportunity to cancel the popup (calls OnBeforePopup) and
+  //   creates the new platform delegate for the popup. If the popup owner is
+  //   an extension guest view then the popup is canceled and
+  //   CefBrowserHostImpl::OpenURLFromTab is called.
+  // And then the following calls may occur at the same time:
+  // - CefBrowserHostImpl::GetCustomWebContentsView (UIT)
+  //   Creates the OSR views for windowless popups.
+  // - CefBrowserHostImpl::WebContentsCreated (UIT)
+  //   Creates the CefBrowserHostImpl representation for the popup.
+  // - CefBrowserMessageFilter::OnGetNewBrowserInfo (IOT)
+  //   Passes information about the popup to the renderer process.
+  struct PendingPopup {
+    // Track the last method that modified this PendingPopup instance. There may
+    // be multiple pending popups with the same identifiers and this allows us
+    // to differentiate between them at different processing steps.
+    enum Step {
+      CAN_CREATE_WINDOW,
+      GET_CUSTOM_WEB_CONTENTS_VIEW,
+    } step;
+
+    // Initial state from ViewHostMsg_CreateWindow.
+    // |target_url| will be empty if a popup is created via window.open() and
+    // never navigated. For example: javascript:window.open();
+    int opener_render_process_id;
+    int opener_render_routing_id;
+    GURL target_url;
+    std::string target_frame_name;
+
+    // Values specified by OnBeforePopup.
+    CefBrowserSettings settings;
+    CefRefPtr<CefClient> client;
+    CefRefPtr<CefDictionaryValue> extra_info;
+
+    // Platform delegate specific to the new popup.
+    std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate;
+  };
+
+  // Manage pending popups. Only called on the UI thread.
+  void PushPendingPopup(std::unique_ptr<PendingPopup> popup);
+  std::unique_ptr<PendingPopup> PopPendingPopup(PendingPopup::Step step,
+                                                int opener_process_id,
+                                                int opener_routing_id,
+                                                const GURL& target_url);
+
+  // Retrieves the BrowserInfo matching the specified IDs. If both sets are
+  // valid then this method makes sure both sets have been registered.
+  scoped_refptr<CefBrowserInfo> GetBrowserInfo(int render_process_id,
+                                               int render_routing_id,
+                                               bool* is_guest_view);
+
+  // Send the response for a pending OnGetNewBrowserInfo request.
+  static void SendNewBrowserInfoResponse(
+      int render_process_id,
+      scoped_refptr<CefBrowserInfo> browser_info,
+      bool is_guest_view,
+      IPC::Message* reply_msg);
+
+  // Time out a response if it's still pending.
+  static void TimeoutNewBrowserInfoResponse(int64_t frame_id, int timeout_id);
+
+  // Pending request for OnGetNewBrowserInfo.
+  struct PendingNewBrowserInfo {
+    int render_process_id;
+    int render_routing_id;
+    int timeout_id;
+    IPC::Message* reply_msg;
+  };
+
+  mutable base::Lock browser_info_lock_;
+
+  // Access to the below members must be protected by |browser_info_lock_|.
+
+  BrowserInfoList browser_info_list_;
+  int next_browser_id_ = 0;
+
+  // Map of frame ID to info.
+  using PendingNewBrowserInfoMap =
+      std::map<int64_t, std::unique_ptr<PendingNewBrowserInfo>>;
+  PendingNewBrowserInfoMap pending_new_browser_info_map_;
+
+  // Only accessed on the UI thread.
+  using PendingPopupList = std::vector<std::unique_ptr<PendingPopup>>;
+  PendingPopupList pending_popup_list_;
+
+  int next_timeout_id_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserInfoManager);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_INFO_H_
diff --git a/src/libcef/browser/browser_main.cc b/src/libcef/browser/browser_main.cc
new file mode 100644
index 0000000..b7567c3
--- /dev/null
+++ b/src/libcef/browser/browser_main.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/browser_main.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/browser_context_keyed_service_factories.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/devtools/devtools_manager_delegate.h"
+#include "libcef/browser/extensions/extension_system_factory.h"
+#include "libcef/browser/extensions/extensions_browser_client.h"
+#include "libcef/browser/net/chrome_scheme_handler.h"
+#include "libcef/browser/printing/constrained_window_views_client.h"
+#include "libcef/browser/printing/printing_message_filter.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/extensions/extensions_client.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/common/net/net_resource_provider.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/plugins/plugin_finder.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/browser/network_service_instance.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/constants.h"
+#include "net/base/net_module.h"
+#include "services/service_manager/embedder/result_codes.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(USE_AURA) && defined(USE_X11)
+#include "ui/events/devices/x11/touch_factory_x11.h"
+#endif
+
+#if defined(USE_AURA)
+#include "ui/aura/env.h"
+#include "ui/display/screen.h"
+#include "ui/views/widget/desktop_aura/desktop_screen.h"
+#include "ui/wm/core/wm_state.h"
+
+#if defined(OS_WIN)
+#include "chrome/browser/chrome_browser_main_win.h"
+#include "chrome/browser/win/parental_controls.h"
+#include "components/os_crypt/os_crypt.h"
+#include "ui/base/cursor/cursor_loader_win.h"
+#endif
+#endif  // defined(USE_AURA)
+
+#if defined(TOOLKIT_VIEWS)
+#if defined(OS_MACOSX)
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/chrome_views_delegate.h"
+#else
+#include "ui/views/test/desktop_test_views_delegate.h"
+#endif
+#endif  // defined(TOOLKIT_VIEWS)
+
+#if defined(USE_AURA) && defined(OS_LINUX)
+#include "ui/base/ime/init/input_method_initializer.h"
+#endif
+
+#if defined(OS_LINUX)
+#include "libcef/browser/printing/print_dialog_linux.h"
+#endif
+
+CefBrowserMainParts::CefBrowserMainParts(
+    const content::MainFunctionParams& parameters)
+    : BrowserMainParts(), devtools_delegate_(nullptr) {}
+
+CefBrowserMainParts::~CefBrowserMainParts() {
+  constrained_window::SetConstrainedWindowViewsClient(nullptr);
+}
+
+int CefBrowserMainParts::PreEarlyInitialization() {
+#if defined(USE_AURA) && defined(OS_LINUX)
+  // TODO(linux): Consider using a real input method or
+  // views::LinuxUI::SetInstance.
+  ui::InitializeInputMethodForTesting();
+#endif
+
+  return service_manager::RESULT_CODE_NORMAL_EXIT;
+}
+
+void CefBrowserMainParts::ToolkitInitialized() {
+  SetConstrainedWindowViewsClient(CreateCefConstrainedWindowViewsClient());
+#if defined(USE_AURA)
+  CHECK(aura::Env::GetInstance());
+
+  wm_state_.reset(new wm::WMState);
+
+#if defined(OS_WIN)
+  ui::CursorLoaderWin::SetCursorResourceModule(
+      CefContentBrowserClient::Get()->GetResourceDllName());
+#endif
+#endif  // defined(USE_AURA)
+
+#if defined(TOOLKIT_VIEWS)
+#if defined(OS_MACOSX)
+  views_delegate_ = std::make_unique<ChromeViewsDelegate>();
+  layout_provider_ = ChromeLayoutProvider::CreateLayoutProvider();
+#else
+  views_delegate_ = std::make_unique<views::DesktopTestViewsDelegate>();
+#endif
+#endif  // defined(TOOLKIT_VIEWS)
+}
+
+void CefBrowserMainParts::PreMainMessageLoopStart() {
+#if defined(USE_AURA) && defined(USE_X11)
+  ui::TouchFactory::SetTouchDeviceListFromCommandLine();
+#endif
+
+#if defined(OS_WIN)
+  // Initialize the OSCrypt.
+  PrefService* local_state = g_browser_process->local_state();
+  DCHECK(local_state);
+  bool os_crypt_init = OSCrypt::Init(local_state);
+  DCHECK(os_crypt_init);
+
+  // installer_util references strings that are normally compiled into
+  // setup.exe.  In Chrome, these strings are in the locale files.
+  ChromeBrowserMainPartsWin::SetupInstallerUtilStrings();
+#endif  // defined(OS_WIN)
+}
+
+void CefBrowserMainParts::PostMainMessageLoopStart() {
+#if defined(OS_LINUX)
+  printing::PrintingContextLinux::SetCreatePrintDialogFunction(
+      &CefPrintDialogLinux::CreatePrintDialog);
+  printing::PrintingContextLinux::SetPdfPaperSizeFunction(
+      &CefPrintDialogLinux::GetPdfPaperSize);
+#endif
+}
+
+int CefBrowserMainParts::PreCreateThreads() {
+#if defined(OS_WIN)
+  PlatformInitialize();
+#endif
+
+  net::NetModule::SetResourceProvider(&NetResourceProvider);
+
+  // Initialize these objects before IO access restrictions are applied and
+  // before the IO thread is started.
+  content::GpuDataManager::GetInstance();
+  SystemNetworkContextManager::CreateInstance(g_browser_process->local_state());
+
+  return 0;
+}
+
+void CefBrowserMainParts::PreMainMessageLoopRun() {
+#if defined(USE_AURA)
+  display::Screen::SetScreenInstance(views::CreateDesktopScreen());
+#endif
+
+  if (extensions::ExtensionsEnabled()) {
+    // Initialize extension global objects before creating the global
+    // BrowserContext.
+    extensions_client_.reset(new extensions::CefExtensionsClient());
+    extensions::ExtensionsClient::Set(extensions_client_.get());
+    extensions_browser_client_.reset(
+        new extensions::CefExtensionsBrowserClient);
+    extensions::ExtensionsBrowserClient::Set(extensions_browser_client_.get());
+
+    extensions::CefExtensionSystemFactory::GetInstance();
+  }
+
+  // Register additional KeyedService factories here. See
+  // ChromeBrowserMainExtraPartsProfiles for details.
+  cef::EnsureBrowserContextKeyedServiceFactoriesBuilt();
+
+  printing::CefPrintingMessageFilter::EnsureShutdownNotifierFactoryBuilt();
+
+  background_task_runner_ = base::CreateSingleThreadTaskRunner(
+      {base::ThreadPool(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::BLOCK_SHUTDOWN, base::MayBlock()});
+  user_visible_task_runner_ = base::CreateSingleThreadTaskRunner(
+      {base::ThreadPool(), base::TaskPriority::USER_VISIBLE,
+       base::TaskShutdownBehavior::BLOCK_SHUTDOWN, base::MayBlock()});
+  user_blocking_task_runner_ = base::CreateSingleThreadTaskRunner(
+      {base::ThreadPool(), base::TaskPriority::USER_BLOCKING,
+       base::TaskShutdownBehavior::BLOCK_SHUTDOWN, base::MayBlock()});
+
+  CefRequestContextSettings settings;
+  CefContext::Get()->PopulateGlobalRequestContextSettings(&settings);
+
+  // Create the global RequestContext.
+  global_request_context_ =
+      CefRequestContextImpl::CreateGlobalRequestContext(settings);
+  CefBrowserContext* browser_context = static_cast<CefBrowserContext*>(
+      global_request_context_->GetBrowserContext());
+
+  CefDevToolsManagerDelegate::StartHttpHandler(browser_context);
+
+#if defined(OS_WIN)
+  // Windows parental controls calls can be slow, so we do an early init here
+  // that calculates this value off of the UI thread.
+  InitializeWinParentalControls();
+#endif
+
+  // Triggers initialization of the singleton instance on UI thread.
+  PluginFinder::GetInstance()->Init();
+
+  scheme::RegisterWebUIControllerFactory();
+}
+
+void CefBrowserMainParts::PostMainMessageLoopRun() {
+  // NOTE: Destroy objects in reverse order of creation.
+  CefDevToolsManagerDelegate::StopHttpHandler();
+
+  // There should be no additional references to the global CefRequestContext
+  // during shutdown. Did you forget to release a CefBrowser reference?
+  DCHECK(global_request_context_->HasOneRef());
+  global_request_context_ = nullptr;
+}
+
+void CefBrowserMainParts::PostDestroyThreads() {
+  if (extensions::ExtensionsEnabled()) {
+    extensions::ExtensionsBrowserClient::Set(nullptr);
+    extensions_browser_client_.reset();
+  }
+
+#if defined(TOOLKIT_VIEWS)
+  views_delegate_.reset();
+#if defined(OS_MACOSX)
+  layout_provider_.reset();
+#endif
+#endif  // defined(TOOLKIT_VIEWS)
+}
diff --git a/src/libcef/browser/browser_main.h b/src/libcef/browser/browser_main.h
new file mode 100644
index 0000000..5827697
--- /dev/null
+++ b/src/libcef/browser/browser_main.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_MAIN_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_MAIN_H_
+#pragma once
+
+#include "libcef/browser/request_context_impl.h"
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_main_parts.h"
+
+namespace content {
+struct MainFunctionParams;
+}
+
+namespace extensions {
+class ExtensionsBrowserClient;
+class ExtensionsClient;
+}  // namespace extensions
+
+#if defined(USE_AURA)
+namespace wm {
+class WMState;
+}
+#endif
+
+#if defined(TOOLKIT_VIEWS)
+namespace views {
+class ViewsDelegate;
+#if defined(OS_MACOSX)
+class LayoutProvider;
+#endif
+}
+#endif  // defined(TOOLKIT_VIEWS)
+
+class CefDevToolsDelegate;
+
+class CefBrowserMainParts : public content::BrowserMainParts {
+ public:
+  explicit CefBrowserMainParts(const content::MainFunctionParams& parameters);
+  ~CefBrowserMainParts() override;
+
+  int PreEarlyInitialization() override;
+  void ToolkitInitialized() override;
+  void PreMainMessageLoopStart() override;
+  void PostMainMessageLoopStart() override;
+  int PreCreateThreads() override;
+  void PreMainMessageLoopRun() override;
+  void PostMainMessageLoopRun() override;
+  void PostDestroyThreads() override;
+
+  CefRefPtr<CefRequestContextImpl> request_context() const {
+    return global_request_context_;
+  }
+  CefDevToolsDelegate* devtools_delegate() const { return devtools_delegate_; }
+
+  scoped_refptr<base::SingleThreadTaskRunner> background_task_runner() const {
+    return background_task_runner_;
+  }
+  scoped_refptr<base::SingleThreadTaskRunner> user_visible_task_runner() const {
+    return user_visible_task_runner_;
+  }
+  scoped_refptr<base::SingleThreadTaskRunner> user_blocking_task_runner()
+      const {
+    return user_blocking_task_runner_;
+  }
+
+ private:
+#if defined(OS_WIN)
+  void PlatformInitialize();
+#endif  // defined(OS_WIN)
+
+  CefRefPtr<CefRequestContextImpl> global_request_context_;
+  CefDevToolsDelegate* devtools_delegate_;  // Deletes itself.
+
+  std::unique_ptr<extensions::ExtensionsClient> extensions_client_;
+  std::unique_ptr<extensions::ExtensionsBrowserClient>
+      extensions_browser_client_;
+
+  // Blocking task runners exposed via CefTaskRunner. For consistency with
+  // previous named thread behavior always execute all pending tasks before
+  // shutdown (e.g. to make sure critical data is saved to disk).
+  // |background_task_runner_| is also passed to SQLitePersistentCookieStore.
+  scoped_refptr<base::SingleThreadTaskRunner> background_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> user_visible_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> user_blocking_task_runner_;
+
+#if defined(USE_AURA)
+  std::unique_ptr<wm::WMState> wm_state_;
+#endif
+
+#if defined(TOOLKIT_VIEWS)
+  std::unique_ptr<views::ViewsDelegate> views_delegate_;
+#if defined(OS_MACOSX)
+  std::unique_ptr<views::LayoutProvider> layout_provider_;
+#endif
+#endif  // defined(TOOLKIT_VIEWS)
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserMainParts);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_MAIN_H_
diff --git a/src/libcef/browser/browser_main_win.cc b/src/libcef/browser/browser_main_win.cc
new file mode 100644
index 0000000..272fcbd
--- /dev/null
+++ b/src/libcef/browser/browser_main_win.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <Objbase.h>
+#include <commctrl.h>
+#include <windows.h>
+
+#include "libcef/browser/browser_main.h"
+
+#include "base/logging.h"
+
+void CefBrowserMainParts::PlatformInitialize() {
+  HRESULT res;
+
+  // Initialize common controls.
+  res = CoInitialize(nullptr);
+  DCHECK(SUCCEEDED(res));
+  INITCOMMONCONTROLSEX InitCtrlEx;
+  InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
+  InitCtrlEx.dwICC = ICC_STANDARD_CLASSES;
+  InitCommonControlsEx(&InitCtrlEx);
+
+  // Start COM stuff.
+  res = OleInitialize(nullptr);
+  DCHECK(SUCCEEDED(res));
+}
diff --git a/src/libcef/browser/browser_message_filter.cc b/src/libcef/browser/browser_message_filter.cc
new file mode 100644
index 0000000..b673a9f
--- /dev/null
+++ b/src/libcef/browser/browser_message_filter.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/browser_message_filter.h"
+
+#include "libcef/browser/browser_info_manager.h"
+#include "libcef/browser/origin_whitelist_impl.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/content_client.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "content/public/common/child_process_host.h"
+
+CefBrowserMessageFilter::CefBrowserMessageFilter(int render_process_id)
+    : content::BrowserMessageFilter(ExtensionMsgStart),
+      render_process_id_(render_process_id) {}
+
+CefBrowserMessageFilter::~CefBrowserMessageFilter() {}
+
+void CefBrowserMessageFilter::OnFilterRemoved() {
+  render_process_id_ = content::ChildProcessHost::kInvalidUniqueID;
+  content::BrowserMessageFilter::OnFilterRemoved();
+}
+
+bool CefBrowserMessageFilter::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+
+  IPC_BEGIN_MESSAGE_MAP(CefBrowserMessageFilter, message)
+    IPC_MESSAGE_HANDLER(CefProcessHostMsg_GetNewRenderThreadInfo,
+                        OnGetNewRenderThreadInfo)
+    IPC_MESSAGE_HANDLER_DELAY_REPLY(CefProcessHostMsg_GetNewBrowserInfo,
+                                    OnGetNewBrowserInfo)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void CefBrowserMessageFilter::OnGetNewRenderThreadInfo(
+    CefProcessHostMsg_GetNewRenderThreadInfo_Params* params) {
+  GetCrossOriginWhitelistEntries(&params->cross_origin_whitelist_entries);
+
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefBrowserProcessHandler> handler =
+        app->GetBrowserProcessHandler();
+    if (handler.get()) {
+      CefRefPtr<CefListValueImpl> listValuePtr(
+          new CefListValueImpl(&params->extra_info, false, false));
+      handler->OnRenderProcessThreadCreated(listValuePtr.get());
+      listValuePtr->Detach(nullptr);
+    }
+  }
+}
+
+void CefBrowserMessageFilter::OnGetNewBrowserInfo(int render_frame_routing_id,
+                                                  IPC::Message* reply_msg) {
+  if (render_process_id_ != content::ChildProcessHost::kInvalidUniqueID) {
+    CefBrowserInfoManager::GetInstance()->OnGetNewBrowserInfo(
+        render_process_id_, render_frame_routing_id, reply_msg);
+  } else {
+    delete reply_msg;
+  }
+}
diff --git a/src/libcef/browser/browser_message_filter.h b/src/libcef/browser/browser_message_filter.h
new file mode 100644
index 0000000..c4a28e4
--- /dev/null
+++ b/src/libcef/browser/browser_message_filter.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_FILTER_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_FILTER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "content/public/browser/browser_message_filter.h"
+
+struct CefProcessHostMsg_GetNewBrowserInfo_Params;
+struct CefProcessHostMsg_GetNewRenderThreadInfo_Params;
+
+// This class sends and receives control messages on the browser process.
+class CefBrowserMessageFilter : public content::BrowserMessageFilter {
+ public:
+  explicit CefBrowserMessageFilter(int render_process_id);
+  ~CefBrowserMessageFilter() override;
+
+  // IPC::ChannelProxy::MessageFilter implementation.
+  void OnFilterRemoved() override;
+  bool OnMessageReceived(const IPC::Message& message) override;
+
+ private:
+  // Message handlers.
+  void OnGetNewRenderThreadInfo(
+      CefProcessHostMsg_GetNewRenderThreadInfo_Params* params);
+  void OnGetNewBrowserInfo(int render_frame_routing_id,
+                           IPC::Message* reply_msg);
+
+  int render_process_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserMessageFilter);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_FILTER_H_
diff --git a/src/libcef/browser/browser_message_loop.cc b/src/libcef/browser/browser_message_loop.cc
new file mode 100644
index 0000000..d810884
--- /dev/null
+++ b/src/libcef/browser/browser_message_loop.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/browser_message_loop.h"
+#include "libcef/browser/context.h"
+#include "libcef/common/content_client.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_pump.h"
+#include "base/message_loop/message_pump_for_ui.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/message_loop/message_pump_mac.h"
+#endif
+
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+// MessagePump implementation that delegates to OnScheduleMessagePumpWork() for
+// scheduling.
+class MessagePumpExternal : public base::MessagePumpForUI {
+ public:
+  MessagePumpExternal(float max_time_slice,
+                      CefRefPtr<CefBrowserProcessHandler> handler)
+      : max_time_slice_(max_time_slice), handler_(handler) {}
+
+  void Run(Delegate* delegate) override {
+    base::TimeTicks start = base::TimeTicks::Now();
+    while (true) {
+#if defined(OS_MACOSX)
+      base::mac::ScopedNSAutoreleasePool autorelease_pool;
+#endif
+
+      base::TimeTicks next_run_time;  // is_null()
+      const bool has_more_work = DirectRunWork(delegate, &next_run_time);
+      if (!has_more_work)
+        break;
+
+      if (next_run_time.is_null()) {
+        // We have more work that should run immediately.
+        next_run_time = base::TimeTicks::Now();
+      }
+
+      const base::TimeDelta& delta = next_run_time - start;
+      if (delta.InSecondsF() > max_time_slice_)
+        break;
+    }
+  }
+
+  void Quit() override {}
+
+  void ScheduleWork() override { handler_->OnScheduleMessagePumpWork(0); }
+
+  void ScheduleDelayedWork(const base::TimeTicks& delayed_work_time) override {
+    const base::TimeDelta& delta = delayed_work_time - base::TimeTicks::Now();
+    handler_->OnScheduleMessagePumpWork(delta.InMilliseconds());
+  }
+
+ private:
+  static bool DirectRunWork(Delegate* delegate,
+                            base::TimeTicks* next_run_time) {
+    bool more_immediate_work = false;
+    bool more_idle_work = false;
+    bool more_delayed_work = false;
+
+    Delegate::NextWorkInfo next_work_info = delegate->DoWork();
+
+    // is_immediate() returns true if the next task is ready right away.
+    more_immediate_work = next_work_info.is_immediate();
+    if (!more_immediate_work) {
+      // DoIdleWork() returns true if idle work was all done.
+      more_idle_work = !delegate->DoIdleWork();
+
+      // Check the next PendingTask's |delayed_run_time|.
+      // is_max() returns true if there are no more immediate nor delayed tasks.
+      more_delayed_work = !next_work_info.delayed_run_time.is_max();
+      if (more_delayed_work && !more_idle_work) {
+        // The only remaining work that we know about is the PendingTask.
+        // Consider the run time for that task in the time slice calculation.
+        *next_run_time = next_work_info.delayed_run_time;
+      }
+    }
+
+    return more_immediate_work || more_idle_work || more_delayed_work;
+  }
+
+  const float max_time_slice_;
+  CefRefPtr<CefBrowserProcessHandler> handler_;
+};
+
+CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() {
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app)
+    return app->GetBrowserProcessHandler();
+  return nullptr;
+}
+
+std::unique_ptr<base::MessagePump> MessagePumpFactoryForUI() {
+  if (!content::BrowserThread::IsThreadInitialized(
+          content::BrowserThread::UI) ||
+      content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
+    CefRefPtr<CefBrowserProcessHandler> handler = GetBrowserProcessHandler();
+    if (handler)
+      return std::make_unique<MessagePumpExternal>(0.01f, handler);
+  }
+
+#if defined(OS_MACOSX)
+  return base::MessagePumpMac::Create();
+#else
+  return std::make_unique<base::MessagePumpForUI>();
+#endif
+}
+
+}  // namespace
+
+void InitMessagePumpFactoryForUI() {
+  const CefSettings& settings = CefContext::Get()->settings();
+  if (settings.external_message_pump) {
+    base::MessagePump::OverrideMessagePumpForUIFactory(MessagePumpFactoryForUI);
+  }
+}
diff --git a/src/libcef/browser/browser_message_loop.h b/src/libcef/browser/browser_message_loop.h
new file mode 100644
index 0000000..8510c1c
--- /dev/null
+++ b/src/libcef/browser/browser_message_loop.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_LOOP_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_LOOP_H_
+
+void InitMessagePumpFactoryForUI();
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_MESSAGE_LOOP_H_
diff --git a/src/libcef/browser/browser_platform_delegate.cc b/src/libcef/browser/browser_platform_delegate.cc
new file mode 100644
index 0000000..5a77906
--- /dev/null
+++ b/src/libcef/browser/browser_platform_delegate.cc
@@ -0,0 +1,269 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/browser_platform_delegate.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/osr/browser_platform_delegate_osr.h"
+#include "libcef/browser/web_contents_dialog_helper.h"
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "base/logging.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+
+CefBrowserPlatformDelegate::CefBrowserPlatformDelegate() {}
+
+CefBrowserPlatformDelegate::~CefBrowserPlatformDelegate() {
+  DCHECK(!browser_);
+}
+
+void CefBrowserPlatformDelegate::CreateViewForWebContents(
+    content::WebContentsView** view,
+    content::RenderViewHostDelegateView** delegate_view) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::WebContentsCreated(
+    content::WebContents* web_contents) {}
+
+void CefBrowserPlatformDelegate::RenderViewCreated(
+    content::RenderViewHost* render_view_host) {
+  // Indicate that the view has an external parent (namely us). This changes the
+  // default view behavior in some cases (e.g. focus handling on Linux).
+  if (!IsViewsHosted() && render_view_host->GetWidget()->GetView())
+    render_view_host->GetWidget()->GetView()->SetHasExternalParent(true);
+}
+
+void CefBrowserPlatformDelegate::BrowserCreated(CefBrowserHostImpl* browser) {
+  DCHECK(!browser_);
+  DCHECK(browser);
+  browser_ = browser;
+
+  if (browser_->IsPrintPreviewSupported()) {
+    web_contents_dialog_helper_.reset(
+        new CefWebContentsDialogHelper(browser_->web_contents(), this));
+  }
+}
+
+void CefBrowserPlatformDelegate::NotifyBrowserCreated() {}
+
+void CefBrowserPlatformDelegate::NotifyBrowserDestroyed() {}
+
+void CefBrowserPlatformDelegate::BrowserDestroyed(CefBrowserHostImpl* browser) {
+  DCHECK(browser_ && browser_ == browser);
+  browser_ = nullptr;
+}
+
+bool CefBrowserPlatformDelegate::CreateHostWindow() {
+  NOTREACHED();
+  return true;
+}
+
+void CefBrowserPlatformDelegate::CloseHostWindow() {
+  NOTREACHED();
+}
+
+#if defined(USE_AURA)
+views::Widget* CefBrowserPlatformDelegate::GetWindowWidget() const {
+  NOTREACHED();
+  return nullptr;
+}
+
+CefRefPtr<CefBrowserView> CefBrowserPlatformDelegate::GetBrowserView() const {
+  NOTREACHED();
+  return nullptr;
+}
+#endif
+
+void CefBrowserPlatformDelegate::PopupWebContentsCreated(
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefClient> client,
+    content::WebContents* new_web_contents,
+    CefBrowserPlatformDelegate* new_platform_delegate,
+    bool is_devtools) {}
+
+void CefBrowserPlatformDelegate::PopupBrowserCreated(
+    CefBrowserHostImpl* new_browser,
+    bool is_devtools) {}
+
+void CefBrowserPlatformDelegate::SendCaptureLostEvent() {
+  content::RenderWidgetHostImpl* widget = content::RenderWidgetHostImpl::From(
+      browser_->web_contents()->GetRenderViewHost()->GetWidget());
+  if (widget)
+    widget->LostCapture();
+}
+
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+void CefBrowserPlatformDelegate::NotifyMoveOrResizeStarted() {
+  // Dismiss any existing popups.
+  content::RenderViewHost* host = browser_->web_contents()->GetRenderViewHost();
+  if (host)
+    host->NotifyMoveOrResizeStarted();
+}
+
+void CefBrowserPlatformDelegate::SizeTo(int width, int height) {}
+#endif
+
+std::unique_ptr<CefFileDialogRunner>
+CefBrowserPlatformDelegate::CreateFileDialogRunner() {
+  return nullptr;
+}
+
+std::unique_ptr<CefJavaScriptDialogRunner>
+CefBrowserPlatformDelegate::CreateJavaScriptDialogRunner() {
+  return nullptr;
+}
+
+void CefBrowserPlatformDelegate::WasHidden(bool hidden) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::NotifyScreenInfoChanged() {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::Invalidate(cef_paint_element_type_t type) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::SendExternalBeginFrame() {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::SetWindowlessFrameRate(int frame_rate) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::ImeSetComposition(
+    const CefString& text,
+    const std::vector<CefCompositionUnderline>& underlines,
+    const CefRange& replacement_range,
+    const CefRange& selection_range) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::ImeCommitText(
+    const CefString& text,
+    const CefRange& replacement_range,
+    int relative_cursor_pos) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::ImeFinishComposingText(bool keep_selection) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::ImeCancelComposition() {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::DragTargetDragEnter(
+    CefRefPtr<CefDragData> drag_data,
+    const CefMouseEvent& event,
+    cef_drag_operations_mask_t allowed_ops) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::DragTargetDragOver(
+    const CefMouseEvent& event,
+    cef_drag_operations_mask_t allowed_ops) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::DragTargetDragLeave() {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::DragTargetDrop(const CefMouseEvent& event) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::StartDragging(
+    const content::DropData& drop_data,
+    blink::WebDragOperationsMask allowed_ops,
+    const gfx::ImageSkia& image,
+    const gfx::Vector2d& image_offset,
+    const content::DragEventSourceInfo& event_info,
+    content::RenderWidgetHostImpl* source_rwh) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::UpdateDragCursor(
+    blink::WebDragOperation operation) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::DragSourceEndedAt(
+    int x,
+    int y,
+    cef_drag_operations_mask_t op) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::DragSourceSystemDragEnded() {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::AccessibilityEventReceived(
+    const content::AXEventNotificationDetails& eventData) {
+  NOTREACHED();
+}
+
+void CefBrowserPlatformDelegate::AccessibilityLocationChangesReceived(
+    const std::vector<content::AXLocationChangeNotificationDetails>& locData) {
+  NOTREACHED();
+}
+
+gfx::Point CefBrowserPlatformDelegate::GetDialogPosition(
+    const gfx::Size& size) {
+  NOTREACHED();
+  return gfx::Point();
+}
+
+gfx::Size CefBrowserPlatformDelegate::GetMaximumDialogSize() {
+  NOTREACHED();
+  return gfx::Size();
+}
+
+base::RepeatingClosure CefBrowserPlatformDelegate::GetBoundsChangedCallback() {
+  if (web_contents_dialog_helper_) {
+    return web_contents_dialog_helper_->GetBoundsChangedCallback();
+  }
+
+  return base::RepeatingClosure();
+}
+
+// static
+int CefBrowserPlatformDelegate::TranslateWebEventModifiers(
+    uint32 cef_modifiers) {
+  int result = 0;
+  // Set modifiers based on key state.
+  if (cef_modifiers & EVENTFLAG_SHIFT_DOWN)
+    result |= blink::WebInputEvent::kShiftKey;
+  if (cef_modifiers & EVENTFLAG_CONTROL_DOWN)
+    result |= blink::WebInputEvent::kControlKey;
+  if (cef_modifiers & EVENTFLAG_ALT_DOWN)
+    result |= blink::WebInputEvent::kAltKey;
+  if (cef_modifiers & EVENTFLAG_COMMAND_DOWN)
+    result |= blink::WebInputEvent::kMetaKey;
+  if (cef_modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)
+    result |= blink::WebInputEvent::kLeftButtonDown;
+  if (cef_modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON)
+    result |= blink::WebInputEvent::kMiddleButtonDown;
+  if (cef_modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON)
+    result |= blink::WebInputEvent::kRightButtonDown;
+  if (cef_modifiers & EVENTFLAG_CAPS_LOCK_ON)
+    result |= blink::WebInputEvent::kCapsLockOn;
+  if (cef_modifiers & EVENTFLAG_NUM_LOCK_ON)
+    result |= blink::WebInputEvent::kNumLockOn;
+  if (cef_modifiers & EVENTFLAG_IS_LEFT)
+    result |= blink::WebInputEvent::kIsLeft;
+  if (cef_modifiers & EVENTFLAG_IS_RIGHT)
+    result |= blink::WebInputEvent::kIsRight;
+  if (cef_modifiers & EVENTFLAG_IS_KEY_PAD)
+    result |= blink::WebInputEvent::kIsKeyPad;
+  return result;
+}
diff --git a/src/libcef/browser/browser_platform_delegate.h b/src/libcef/browser/browser_platform_delegate.h
new file mode 100644
index 0000000..a14c7ff
--- /dev/null
+++ b/src/libcef/browser/browser_platform_delegate.h
@@ -0,0 +1,290 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_PLATFORM_DELEGATE_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_PLATFORM_DELEGATE_H_
+
+#include <string>
+#include <vector>
+
+#include "include/cef_client.h"
+#include "include/cef_drag_data.h"
+#include "include/internal/cef_types.h"
+#include "include/views/cef_browser_view.h"
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/callback_forward.h"
+#include "content/public/browser/web_contents.h"
+
+namespace blink {
+class WebMouseEvent;
+class WebMouseWheelEvent;
+class WebInputEvent;
+class WebTouchEvent;
+}  // namespace blink
+
+namespace content {
+struct NativeWebKeyboardEvent;
+class RenderViewHost;
+class RenderViewHostDelegateView;
+class WebContentsView;
+}  // namespace content
+
+#if defined(USE_AURA)
+namespace views {
+class Widget;
+}
+#endif
+
+class CefBrowserInfo;
+class CefFileDialogRunner;
+class CefJavaScriptDialogRunner;
+class CefMenuRunner;
+class CefWebContentsDialogHelper;
+
+// Provides platform-specific implementations of browser functionality. All
+// methods are called on the browser process UI thread unless otherwise
+// indicated.
+class CefBrowserPlatformDelegate {
+ public:
+  // Create a new CefBrowserPlatformDelegate instance. May be called on multiple
+  // threads.
+  static std::unique_ptr<CefBrowserPlatformDelegate> Create(
+      CefBrowserHostImpl::CreateParams& create_params);
+
+  // Called to create the view objects for a new WebContents. Will only be
+  // called a single time per instance. May be called on multiple threads. Only
+  // used with windowless rendering.
+  virtual void CreateViewForWebContents(
+      content::WebContentsView** view,
+      content::RenderViewHostDelegateView** delegate_view);
+
+  // Called after the WebContents for the browser is created. Will only be
+  // called a single time per instance.
+  virtual void WebContentsCreated(content::WebContents* web_contents);
+
+  // Called after the RenderViewHost is created.
+  virtual void RenderViewCreated(content::RenderViewHost* render_view_host);
+
+  // Called after the owning CefBrowserHostImpl is created. Will only be called
+  // a single time per instance. Do not send any client notifications from this
+  // method.
+  virtual void BrowserCreated(CefBrowserHostImpl* browser);
+
+  // Send any notifications related to browser creation. Called after
+  // BrowserCreated().
+  virtual void NotifyBrowserCreated();
+
+  // Send any notifications related to browser destruction. Called before
+  // BrowserDestroyed().
+  virtual void NotifyBrowserDestroyed();
+
+  // Called before the owning CefBrowserHostImpl is destroyed. Will only be
+  // called a single time per instance. All references to the CefBrowserHostImpl
+  // and WebContents should be cleared when this method is called. Do not send
+  // any client notifications from this method.
+  virtual void BrowserDestroyed(CefBrowserHostImpl* browser);
+
+  // Create the window that hosts the browser. Will only be called a single time
+  // per instance. Only used with windowed rendering.
+  virtual bool CreateHostWindow();
+
+  // Sends a message to close the window that hosts the browser. On native
+  // platforms this will be done via the OS. DestroyBrowser will be called after
+  // the native window has closed. Only used with windowed rendering.
+  virtual void CloseHostWindow();
+
+  // Return the OS handle for the window that hosts the browser. For windowed
+  // rendering this will return the most immediate parent window handle. For
+  // windowless rendering this will return the parent window handle specified by
+  // the client, which may be NULL. May be called on multiple threads.
+  virtual CefWindowHandle GetHostWindowHandle() const = 0;
+
+#if defined(USE_AURA)
+  // Returns the Widget owner for the browser window. Only used with windowed
+  // rendering.
+  virtual views::Widget* GetWindowWidget() const;
+
+  // Returns the BrowserView associated with this browser. Only used with views-
+  // based browsers.
+  virtual CefRefPtr<CefBrowserView> GetBrowserView() const;
+#endif
+
+  // Called after the WebContents have been created for a new popup browser
+  // parented to this browser but before the CefBrowserHostImpl is created for
+  // the popup. |is_devtools| will be true if the popup will host DevTools. This
+  // method will be called before WebContentsCreated() is called on
+  // |new_platform_delegate|. Do not make the new browser visible in this
+  // callback.
+  virtual void PopupWebContentsCreated(
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefClient> client,
+      content::WebContents* new_web_contents,
+      CefBrowserPlatformDelegate* new_platform_delegate,
+      bool is_devtools);
+
+  // Called after the CefBrowserHostImpl is created for a new popup browser
+  // parented to this browser. |is_devtools| will be true if the popup will host
+  // DevTools. This method will be called immediately after
+  // CefLifeSpanHandler::OnAfterCreated() for the popup browser. It is safe to
+  // make the new browser visible in this callback (for example, add the browser
+  // to a window and show it).
+  virtual void PopupBrowserCreated(CefBrowserHostImpl* new_browser,
+                                   bool is_devtools);
+
+  // Returns the background color for the browser. The alpha component will be
+  // either SK_AlphaTRANSPARENT or SK_AlphaOPAQUE (e.g. fully transparent or
+  // fully opaque). SK_AlphaOPAQUE will always be returned for windowed
+  // browsers. SK_ColorTRANSPARENT may be returned for windowless browsers to
+  // enable transparency.
+  virtual SkColor GetBackgroundColor() const = 0;
+
+  virtual bool CanUseSharedTexture() const = 0;
+  virtual bool CanUseExternalBeginFrame() const = 0;
+
+  // Notify the window that it was resized.
+  virtual void WasResized() = 0;
+
+  // Send input events.
+  virtual void SendKeyEvent(const CefKeyEvent& event) = 0;
+  virtual void SendMouseClickEvent(const CefMouseEvent& event,
+                                   CefBrowserHost::MouseButtonType type,
+                                   bool mouseUp,
+                                   int clickCount) = 0;
+  virtual void SendMouseMoveEvent(const CefMouseEvent& event,
+                                  bool mouseLeave) = 0;
+  virtual void SendMouseWheelEvent(const CefMouseEvent& event,
+                                   int deltaX,
+                                   int deltaY) = 0;
+  virtual void SendTouchEvent(const CefTouchEvent& event) = 0;
+
+  // Send focus event. The browser's WebContents may be NULL when this method is
+  // called.
+  virtual void SendFocusEvent(bool setFocus) = 0;
+
+  // Send capture lost event.
+  virtual void SendCaptureLostEvent();
+
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+  // The window hosting the browser is about to be moved or resized. Only used
+  // on Windows and Linux.
+  virtual void NotifyMoveOrResizeStarted();
+
+  // Resize the host window to the given dimensions. Only used with windowed
+  // rendering on Windows and Linux.
+  virtual void SizeTo(int width, int height);
+#endif
+
+  // Convert from view coordinates to screen coordinates. Potential display
+  // scaling will be applied to the result.
+  virtual gfx::Point GetScreenPoint(const gfx::Point& view) const = 0;
+
+  // Open the specified text in the default text editor.
+  virtual void ViewText(const std::string& text) = 0;
+
+  // Forward the keyboard event to the application or frame window to allow
+  // processing of shortcut keys.
+  virtual bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) = 0;
+
+  // Invoke platform specific handling for the external protocol.
+  static void HandleExternalProtocol(const GURL& url);
+
+  // Returns the OS event handle, if any, associated with |event|.
+  virtual CefEventHandle GetEventHandle(
+      const content::NativeWebKeyboardEvent& event) const = 0;
+
+  // Create the platform-specific file dialog runner.
+  virtual std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner();
+
+  // Create the platform-specific JavaScript dialog runner.
+  virtual std::unique_ptr<CefJavaScriptDialogRunner>
+  CreateJavaScriptDialogRunner();
+
+  // Create the platform-specific menu runner.
+  virtual std::unique_ptr<CefMenuRunner> CreateMenuRunner() = 0;
+
+  // Returns true if this delegate implements windowless rendering. May be
+  // called on multiple threads.
+  virtual bool IsWindowless() const = 0;
+
+  // Returns true if this delegate implements views-hosted browser handling. May
+  // be called on multiple threads.
+  virtual bool IsViewsHosted() const = 0;
+
+  // Notify the browser that it was hidden. Only used with windowless rendering.
+  virtual void WasHidden(bool hidden);
+
+  // Notify the browser that screen information has changed. Only used with
+  // windowless rendering.
+  virtual void NotifyScreenInfoChanged();
+
+  // Invalidate the view. Only used with windowless rendering.
+  virtual void Invalidate(cef_paint_element_type_t type);
+
+  virtual void SendExternalBeginFrame();
+
+  // Set the windowless frame rate. Only used with windowless rendering.
+  virtual void SetWindowlessFrameRate(int frame_rate);
+
+  // IME-related callbacks. See documentation in CefBrowser and
+  // CefRenderHandler. Only used with windowless rendering.
+  virtual void ImeSetComposition(
+      const CefString& text,
+      const std::vector<CefCompositionUnderline>& underlines,
+      const CefRange& replacement_range,
+      const CefRange& selection_range);
+  virtual void ImeCommitText(const CefString& text,
+                             const CefRange& replacement_range,
+                             int relative_cursor_pos);
+  virtual void ImeFinishComposingText(bool keep_selection);
+  virtual void ImeCancelComposition();
+
+  // Drag/drop-related callbacks. See documentation in CefRenderHandler. Only
+  // used with windowless rendering.
+  virtual void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
+                                   const CefMouseEvent& event,
+                                   cef_drag_operations_mask_t allowed_ops);
+  virtual void DragTargetDragOver(const CefMouseEvent& event,
+                                  cef_drag_operations_mask_t allowed_ops);
+  virtual void DragTargetDragLeave();
+  virtual void DragTargetDrop(const CefMouseEvent& event);
+  virtual void StartDragging(const content::DropData& drop_data,
+                             blink::WebDragOperationsMask allowed_ops,
+                             const gfx::ImageSkia& image,
+                             const gfx::Vector2d& image_offset,
+                             const content::DragEventSourceInfo& event_info,
+                             content::RenderWidgetHostImpl* source_rwh);
+  virtual void UpdateDragCursor(blink::WebDragOperation operation);
+  virtual void DragSourceEndedAt(int x, int y, cef_drag_operations_mask_t op);
+  virtual void DragSourceSystemDragEnded();
+  virtual void AccessibilityEventReceived(
+      const content::AXEventNotificationDetails& eventData);
+  virtual void AccessibilityLocationChangesReceived(
+      const std::vector<content::AXLocationChangeNotificationDetails>& locData);
+  virtual gfx::Point GetDialogPosition(const gfx::Size& size);
+  virtual gfx::Size GetMaximumDialogSize();
+
+ protected:
+  // Allow deletion via scoped_ptr only.
+  friend std::default_delete<CefBrowserPlatformDelegate>;
+
+  CefBrowserPlatformDelegate();
+  virtual ~CefBrowserPlatformDelegate();
+
+  base::RepeatingClosure GetBoundsChangedCallback();
+
+  static int TranslateWebEventModifiers(uint32 cef_modifiers);
+
+  CefBrowserHostImpl* browser_ = nullptr;  // Not owned by this object.
+
+ private:
+  // Used for the print preview dialog.
+  std::unique_ptr<CefWebContentsDialogHelper> web_contents_dialog_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserPlatformDelegate);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_PLATFORM_DELEGATE_H_
diff --git a/src/libcef/browser/browser_platform_delegate_create.cc b/src/libcef/browser/browser_platform_delegate_create.cc
new file mode 100644
index 0000000..75c05f4
--- /dev/null
+++ b/src/libcef/browser/browser_platform_delegate_create.cc
@@ -0,0 +1,121 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/browser_platform_delegate.h"
+
+#include <utility>
+
+#include "libcef/browser/context.h"
+
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+
+#include "libcef/browser/extensions/browser_platform_delegate_background.h"
+
+#if defined(OS_WIN)
+#include "libcef/browser/native/browser_platform_delegate_native_win.h"
+#include "libcef/browser/osr/browser_platform_delegate_osr_win.h"
+#elif defined(OS_MACOSX)
+#include "libcef/browser/native/browser_platform_delegate_native_mac.h"
+#include "libcef/browser/osr/browser_platform_delegate_osr_mac.h"
+#elif defined(OS_LINUX)
+#include "libcef/browser/native/browser_platform_delegate_native_linux.h"
+#include "libcef/browser/osr/browser_platform_delegate_osr_linux.h"
+#else
+#error A delegate implementation is not available for your platform.
+#endif
+
+#if defined(USE_AURA)
+#include "libcef/browser/views/browser_platform_delegate_views.h"
+#endif
+
+namespace {
+
+std::unique_ptr<CefBrowserPlatformDelegateNative> CreateNativeDelegate(
+    const CefWindowInfo& window_info,
+    SkColor background_color,
+    bool use_shared_texture,
+    bool use_external_begin_frame) {
+#if defined(OS_WIN)
+  return std::make_unique<CefBrowserPlatformDelegateNativeWin>(
+      window_info, background_color, use_shared_texture,
+      use_external_begin_frame);
+#elif defined(OS_MACOSX)
+  return std::make_unique<CefBrowserPlatformDelegateNativeMac>(
+      window_info, background_color);
+#elif defined(OS_LINUX)
+  return std::make_unique<CefBrowserPlatformDelegateNativeLinux>(
+      window_info, background_color, use_external_begin_frame);
+#endif
+}
+
+std::unique_ptr<CefBrowserPlatformDelegateOsr> CreateOSRDelegate(
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate) {
+#if defined(OS_WIN)
+  return std::make_unique<CefBrowserPlatformDelegateOsrWin>(
+      std::move(native_delegate));
+#elif defined(OS_MACOSX)
+  return std::make_unique<CefBrowserPlatformDelegateOsrMac>(
+      std::move(native_delegate));
+#elif defined(OS_LINUX)
+  return std::make_unique<CefBrowserPlatformDelegateOsrLinux>(
+      std::move(native_delegate));
+#endif
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<CefBrowserPlatformDelegate> CefBrowserPlatformDelegate::Create(
+    CefBrowserHostImpl::CreateParams& create_params) {
+  const bool is_windowless =
+      create_params.window_info &&
+      create_params.window_info->windowless_rendering_enabled &&
+      create_params.client && create_params.client->GetRenderHandler().get();
+  const SkColor background_color = CefContext::Get()->GetBackgroundColor(
+      &create_params.settings, is_windowless ? STATE_ENABLED : STATE_DISABLED);
+
+  bool use_shared_texture = false;
+  bool use_external_begin_frame = false;
+
+  if (is_windowless) {
+    use_shared_texture = create_params.window_info &&
+                         create_params.window_info->shared_texture_enabled;
+
+    use_external_begin_frame =
+        create_params.window_info &&
+        create_params.window_info->external_begin_frame_enabled;
+  }
+
+  if (create_params.window_info) {
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate =
+        CreateNativeDelegate(*create_params.window_info.get(), background_color,
+                             use_shared_texture, use_external_begin_frame);
+    if (is_windowless)
+      return CreateOSRDelegate(std::move(native_delegate));
+    return std::move(native_delegate);
+  } else if (create_params.extension_host_type ==
+             extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
+    // Creating a background extension host without a window.
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate =
+        CreateNativeDelegate(CefWindowInfo(), background_color,
+                             use_shared_texture, use_external_begin_frame);
+    return std::make_unique<CefBrowserPlatformDelegateBackground>(
+        std::move(native_delegate));
+  }
+#if defined(USE_AURA)
+  else {
+    // CefWindowInfo is not used in this case.
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate =
+        CreateNativeDelegate(CefWindowInfo(), background_color,
+                             use_shared_texture, use_external_begin_frame);
+    return std::make_unique<CefBrowserPlatformDelegateViews>(
+        std::move(native_delegate),
+        static_cast<CefBrowserViewImpl*>(create_params.browser_view.get()));
+  }
+#endif  // defined(USE_AURA)
+
+  NOTREACHED();
+  return nullptr;
+}
diff --git a/src/libcef/browser/browser_util.cc b/src/libcef/browser/browser_util.cc
new file mode 100644
index 0000000..fbe292e
--- /dev/null
+++ b/src/libcef/browser/browser_util.cc
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/browser_util.h"
+
+#include "content/public/browser/native_web_keyboard_event.h"
+
+namespace browser_util {
+
+bool GetCefKeyEvent(const content::NativeWebKeyboardEvent& event,
+                    CefKeyEvent& cef_event) {
+  switch (event.GetType()) {
+    case blink::WebKeyboardEvent::kRawKeyDown:
+      cef_event.type = KEYEVENT_RAWKEYDOWN;
+      break;
+    case blink::WebKeyboardEvent::kKeyDown:
+      cef_event.type = KEYEVENT_KEYDOWN;
+      break;
+    case blink::WebKeyboardEvent::kKeyUp:
+      cef_event.type = KEYEVENT_KEYUP;
+      break;
+    case blink::WebKeyboardEvent::kChar:
+      cef_event.type = KEYEVENT_CHAR;
+      break;
+    default:
+      return false;
+  }
+
+  cef_event.modifiers = 0;
+  if (event.GetModifiers() & blink::WebKeyboardEvent::kShiftKey)
+    cef_event.modifiers |= EVENTFLAG_SHIFT_DOWN;
+  if (event.GetModifiers() & blink::WebKeyboardEvent::kControlKey)
+    cef_event.modifiers |= EVENTFLAG_CONTROL_DOWN;
+  if (event.GetModifiers() & blink::WebKeyboardEvent::kAltKey)
+    cef_event.modifiers |= EVENTFLAG_ALT_DOWN;
+  if (event.GetModifiers() & blink::WebKeyboardEvent::kMetaKey)
+    cef_event.modifiers |= EVENTFLAG_COMMAND_DOWN;
+  if (event.GetModifiers() & blink::WebKeyboardEvent::kIsKeyPad)
+    cef_event.modifiers |= EVENTFLAG_IS_KEY_PAD;
+
+  cef_event.windows_key_code = event.windows_key_code;
+  cef_event.native_key_code = event.native_key_code;
+  cef_event.is_system_key = event.is_system_key;
+  cef_event.character = event.text[0];
+  cef_event.unmodified_character = event.unmodified_text[0];
+
+  return true;
+}
+
+bool GetCefKeyEvent(const ui::KeyEvent& event, CefKeyEvent& cef_event) {
+  content::NativeWebKeyboardEvent native_event(event);
+  return GetCefKeyEvent(native_event, cef_event);
+}
+
+}  // namespace browser_util
diff --git a/src/libcef/browser/browser_util.h b/src/libcef/browser/browser_util.h
new file mode 100644
index 0000000..74c36f1
--- /dev/null
+++ b/src/libcef/browser/browser_util.h
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_BROWSER_UTIL_H_
+#define CEF_LIBCEF_BROWSER_BROWSER_UTIL_H_
+#pragma once
+
+#include "include/internal/cef_types_wrappers.h"
+
+namespace content {
+struct NativeWebKeyboardEvent;
+}
+
+namespace ui {
+class KeyEvent;
+}
+
+namespace browser_util {
+
+// Convert a content::NativeWebKeyboardEvent to a CefKeyEvent.
+bool GetCefKeyEvent(const content::NativeWebKeyboardEvent& event,
+                    CefKeyEvent& cef_event);
+
+// Convert a ui::KeyEvent to a CefKeyEvent.
+bool GetCefKeyEvent(const ui::KeyEvent& event, CefKeyEvent& cef_event);
+
+}  // namespace browser_util
+
+#endif  // CEF_LIBCEF_BROWSER_BROWSER_UTIL_H_
diff --git a/src/libcef/browser/chrome_browser_process_stub.cc b/src/libcef/browser/chrome_browser_process_stub.cc
new file mode 100644
index 0000000..47b72a7
--- /dev/null
+++ b/src/libcef/browser/chrome_browser_process_stub.cc
@@ -0,0 +1,381 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/chrome_browser_process_stub.h"
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/chrome_profile_manager_stub.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/prefs/browser_prefs.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/cef_switches.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/browser/printing/background_printing_manager.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/browser/printing/print_preview_dialog_controller.h"
+#include "chrome/browser/ui/prefs/pref_watcher.h"
+#include "components/net_log/chrome_net_log.h"
+#include "components/prefs/pref_service.h"
+#include "content/browser/startup_helper.h"
+#include "content/public/common/content_switches.h"
+#include "net/log/net_log_capture_mode.h"
+#include "services/network/public/cpp/network_switches.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+ChromeBrowserProcessStub::ChromeBrowserProcessStub()
+    : initialized_(false),
+      context_initialized_(false),
+      shutdown_(false),
+      locale_("en-US") {}
+
+ChromeBrowserProcessStub::~ChromeBrowserProcessStub() {
+  DCHECK((!initialized_ && !context_initialized_) || shutdown_);
+}
+
+void ChromeBrowserProcessStub::Initialize() {
+  DCHECK(!initialized_);
+  DCHECK(!context_initialized_);
+  DCHECK(!shutdown_);
+  DCHECK(!field_trial_list_);
+
+  // Initialize this early before any code tries to check feature flags.
+  field_trial_list_ = content::SetUpFieldTrialsAndFeatureList();
+
+  initialized_ = true;
+}
+
+void ChromeBrowserProcessStub::OnContextInitialized() {
+  CEF_REQUIRE_UIT();
+  DCHECK(initialized_);
+  DCHECK(!context_initialized_);
+  DCHECK(!shutdown_);
+
+  // Must be created after the NotificationService.
+  print_job_manager_.reset(new printing::PrintJobManager());
+  profile_manager_.reset(new ChromeProfileManagerStub());
+  event_router_forwarder_ = new extensions::EventRouterForwarder();
+  context_initialized_ = true;
+}
+
+void ChromeBrowserProcessStub::Shutdown() {
+  CEF_REQUIRE_UIT();
+  DCHECK(initialized_);
+  DCHECK(context_initialized_);
+  DCHECK(!shutdown_);
+
+  // Wait for the pending print jobs to finish. Don't do this later, since
+  // this might cause a nested message loop to run, and we don't want pending
+  // tasks to run once teardown has started.
+  print_job_manager_->Shutdown();
+  print_job_manager_.reset(nullptr);
+  print_preview_dialog_controller_ = nullptr;
+
+  profile_manager_.reset();
+  event_router_forwarder_ = nullptr;
+
+  if (SystemNetworkContextManager::GetInstance()) {
+    SystemNetworkContextManager::DeleteInstance();
+  }
+
+  // Release any references held by objects associated with a Profile. The
+  // Profile will be deleted later.
+  for (const auto& profile : CefBrowserContext::GetAll()) {
+    // Release any references to |local_state_|.
+    PrefWatcher* pref_watcher = PrefWatcher::Get(profile);
+    if (pref_watcher)
+      pref_watcher->Shutdown();
+
+    // Unregister observers for |background_printing_manager_|.
+    if (background_printing_manager_) {
+      background_printing_manager_->DeletePreviewContentsForBrowserContext(
+          profile);
+    }
+  }
+
+  local_state_.reset();
+  browser_policy_connector_.reset();
+
+  background_printing_manager_.reset();
+
+  field_trial_list_.reset();
+
+  shutdown_ = true;
+}
+
+void ChromeBrowserProcessStub::EndSession() {
+  NOTREACHED();
+}
+
+void ChromeBrowserProcessStub::FlushLocalStateAndReply(
+    base::OnceClosure reply) {
+  NOTREACHED();
+}
+
+metrics_services_manager::MetricsServicesManager*
+ChromeBrowserProcessStub::GetMetricsServicesManager() {
+  NOTREACHED();
+  return nullptr;
+}
+
+metrics::MetricsService* ChromeBrowserProcessStub::metrics_service() {
+  NOTREACHED();
+  return nullptr;
+}
+
+rappor::RapporServiceImpl* ChromeBrowserProcessStub::rappor_service() {
+  // Called from PluginInfoHostImpl::ReportMetrics.
+  return nullptr;
+}
+
+SystemNetworkContextManager*
+ChromeBrowserProcessStub::system_network_context_manager() {
+  DCHECK(SystemNetworkContextManager::GetInstance());
+  return SystemNetworkContextManager::GetInstance();
+}
+
+network::NetworkQualityTracker*
+ChromeBrowserProcessStub::network_quality_tracker() {
+  NOTREACHED();
+  return nullptr;
+}
+
+WatchDogThread* ChromeBrowserProcessStub::watchdog_thread() {
+  NOTREACHED();
+  return nullptr;
+}
+
+ProfileManager* ChromeBrowserProcessStub::profile_manager() {
+  DCHECK(context_initialized_);
+  return profile_manager_.get();
+}
+
+PrefService* ChromeBrowserProcessStub::local_state() {
+  DCHECK(initialized_);
+  if (!local_state_) {
+    // Use a location that is shared by all request contexts.
+    const CefSettings& settings = CefContext::Get()->settings();
+    const base::FilePath& root_cache_path =
+        base::FilePath(CefString(&settings.root_cache_path));
+
+    // Used for very early NetworkService initialization.
+    // Always persist preferences for this PrefService if possible because it
+    // contains the cookie encryption key on Windows.
+    local_state_ =
+        browser_prefs::CreatePrefService(nullptr /* profile */, root_cache_path,
+                                         true /* persist_user_preferences */);
+  }
+  return local_state_.get();
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+ChromeBrowserProcessStub::shared_url_loader_factory() {
+  NOTREACHED();
+  return nullptr;
+}
+
+variations::VariationsService* ChromeBrowserProcessStub::variations_service() {
+  NOTREACHED();
+  return nullptr;
+}
+
+BrowserProcessPlatformPart* ChromeBrowserProcessStub::platform_part() {
+  NOTREACHED();
+  return nullptr;
+}
+
+extensions::EventRouterForwarder*
+ChromeBrowserProcessStub::extension_event_router_forwarder() {
+  DCHECK(context_initialized_);
+  return event_router_forwarder_.get();
+}
+
+NotificationUIManager* ChromeBrowserProcessStub::notification_ui_manager() {
+  NOTREACHED();
+  return nullptr;
+}
+
+NotificationPlatformBridge*
+ChromeBrowserProcessStub::notification_platform_bridge() {
+  NOTREACHED();
+  return nullptr;
+}
+
+policy::ChromeBrowserPolicyConnector*
+ChromeBrowserProcessStub::browser_policy_connector() {
+  if (!browser_policy_connector_) {
+    browser_policy_connector_ =
+        std::make_unique<policy::ChromeBrowserPolicyConnector>();
+  }
+  return browser_policy_connector_.get();
+}
+
+policy::PolicyService* ChromeBrowserProcessStub::policy_service() {
+  return browser_policy_connector()->GetPolicyService();
+}
+
+IconManager* ChromeBrowserProcessStub::icon_manager() {
+  NOTREACHED();
+  return nullptr;
+}
+
+GpuModeManager* ChromeBrowserProcessStub::gpu_mode_manager() {
+  NOTREACHED();
+  return nullptr;
+}
+
+void ChromeBrowserProcessStub::CreateDevToolsProtocolHandler() {
+  NOTREACHED();
+}
+
+void ChromeBrowserProcessStub::CreateDevToolsAutoOpener() {
+  NOTREACHED();
+}
+
+bool ChromeBrowserProcessStub::IsShuttingDown() {
+  NOTREACHED();
+  return false;
+}
+
+printing::PrintJobManager* ChromeBrowserProcessStub::print_job_manager() {
+  DCHECK(context_initialized_);
+  return print_job_manager_.get();
+}
+
+printing::PrintPreviewDialogController*
+ChromeBrowserProcessStub::print_preview_dialog_controller() {
+  if (!print_preview_dialog_controller_.get()) {
+    print_preview_dialog_controller_ =
+        new printing::PrintPreviewDialogController();
+  }
+  return print_preview_dialog_controller_.get();
+}
+
+printing::BackgroundPrintingManager*
+ChromeBrowserProcessStub::background_printing_manager() {
+  if (!background_printing_manager_.get()) {
+    background_printing_manager_.reset(
+        new printing::BackgroundPrintingManager());
+  }
+  return background_printing_manager_.get();
+}
+
+IntranetRedirectDetector*
+ChromeBrowserProcessStub::intranet_redirect_detector() {
+  NOTREACHED();
+  return nullptr;
+}
+
+const std::string& ChromeBrowserProcessStub::GetApplicationLocale() {
+  DCHECK(!locale_.empty());
+  return locale_;
+}
+
+void ChromeBrowserProcessStub::SetApplicationLocale(const std::string& locale) {
+  locale_ = locale;
+}
+
+DownloadStatusUpdater* ChromeBrowserProcessStub::download_status_updater() {
+  NOTREACHED();
+  return nullptr;
+}
+
+DownloadRequestLimiter* ChromeBrowserProcessStub::download_request_limiter() {
+  NOTREACHED();
+  return nullptr;
+}
+
+BackgroundModeManager* ChromeBrowserProcessStub::background_mode_manager() {
+  NOTREACHED();
+  return nullptr;
+}
+
+void ChromeBrowserProcessStub::set_background_mode_manager_for_test(
+    std::unique_ptr<BackgroundModeManager> manager) {
+  NOTREACHED();
+}
+
+StatusTray* ChromeBrowserProcessStub::status_tray() {
+  NOTREACHED();
+  return nullptr;
+}
+
+safe_browsing::SafeBrowsingService*
+ChromeBrowserProcessStub::safe_browsing_service() {
+  return nullptr;
+}
+
+safe_browsing::ClientSideDetectionService*
+ChromeBrowserProcessStub::safe_browsing_detection_service() {
+  NOTREACHED();
+  return nullptr;
+}
+
+subresource_filter::RulesetService*
+ChromeBrowserProcessStub::subresource_filter_ruleset_service() {
+  NOTREACHED();
+  return nullptr;
+}
+
+optimization_guide::OptimizationGuideService*
+ChromeBrowserProcessStub::optimization_guide_service() {
+  NOTREACHED();
+  return nullptr;
+}
+
+StartupData* ChromeBrowserProcessStub::startup_data() {
+  NOTREACHED();
+  return nullptr;
+}
+
+#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
+void ChromeBrowserProcessStub::StartAutoupdateTimer() {}
+#endif
+
+component_updater::ComponentUpdateService*
+ChromeBrowserProcessStub::component_updater() {
+  NOTREACHED();
+  return nullptr;
+}
+
+MediaFileSystemRegistry*
+ChromeBrowserProcessStub::media_file_system_registry() {
+  NOTREACHED();
+  return nullptr;
+}
+
+WebRtcLogUploader* ChromeBrowserProcessStub::webrtc_log_uploader() {
+  NOTREACHED();
+  return nullptr;
+}
+
+network_time::NetworkTimeTracker*
+ChromeBrowserProcessStub::network_time_tracker() {
+  NOTREACHED();
+  return nullptr;
+}
+
+gcm::GCMDriver* ChromeBrowserProcessStub::gcm_driver() {
+  NOTREACHED();
+  return nullptr;
+}
+
+resource_coordinator::TabManager* ChromeBrowserProcessStub::GetTabManager() {
+  NOTREACHED();
+  return nullptr;
+}
+
+resource_coordinator::ResourceCoordinatorParts*
+ChromeBrowserProcessStub::resource_coordinator_parts() {
+  NOTREACHED();
+  return nullptr;
+}
+
+BuildState* ChromeBrowserProcessStub::GetBuildState() {
+  NOTREACHED();
+  return nullptr;
+}
diff --git a/src/libcef/browser/chrome_browser_process_stub.h b/src/libcef/browser/chrome_browser_process_stub.h
new file mode 100644
index 0000000..4a322ac
--- /dev/null
+++ b/src/libcef/browser/chrome_browser_process_stub.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a stub implementation of Chrome's BrowserProcess object
+// for use as an interop layer between CEF and files that live in chrome/.
+
+#ifndef CEF_LIBCEF_BROWSER_CHROME_BROWSER_PROCESS_STUB_H_
+#define CEF_LIBCEF_BROWSER_CHROME_BROWSER_PROCESS_STUB_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/metrics/field_trial.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/event_router_forwarder.h"
+#include "media/media_buildflags.h"
+
+class ChromeProfileManagerStub;
+
+class BackgroundModeManager {
+ public:
+  BackgroundModeManager();
+  virtual ~BackgroundModeManager();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BackgroundModeManager);
+};
+
+class ChromeBrowserProcessStub : public BrowserProcess {
+ public:
+  ChromeBrowserProcessStub();
+  ~ChromeBrowserProcessStub() override;
+
+  void Initialize();
+  void OnContextInitialized();
+  void Shutdown();
+
+  // BrowserProcess implementation.
+  void EndSession() override;
+  void FlushLocalStateAndReply(base::OnceClosure reply) override;
+  metrics_services_manager::MetricsServicesManager* GetMetricsServicesManager()
+      override;
+  metrics::MetricsService* metrics_service() override;
+  rappor::RapporServiceImpl* rappor_service() override;
+  SystemNetworkContextManager* system_network_context_manager() override;
+  network::NetworkQualityTracker* network_quality_tracker() override;
+  WatchDogThread* watchdog_thread() override;
+  ProfileManager* profile_manager() override;
+  PrefService* local_state() override;
+  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory()
+      override;
+  variations::VariationsService* variations_service() override;
+  BrowserProcessPlatformPart* platform_part() override;
+  extensions::EventRouterForwarder* extension_event_router_forwarder() override;
+  NotificationUIManager* notification_ui_manager() override;
+  NotificationPlatformBridge* notification_platform_bridge() override;
+  policy::ChromeBrowserPolicyConnector* browser_policy_connector() override;
+  policy::PolicyService* policy_service() override;
+  IconManager* icon_manager() override;
+  GpuModeManager* gpu_mode_manager() override;
+  void CreateDevToolsProtocolHandler() override;
+  void CreateDevToolsAutoOpener() override;
+  bool IsShuttingDown() override;
+  printing::PrintJobManager* print_job_manager() override;
+  printing::PrintPreviewDialogController* print_preview_dialog_controller()
+      override;
+  printing::BackgroundPrintingManager* background_printing_manager() override;
+  IntranetRedirectDetector* intranet_redirect_detector() override;
+  const std::string& GetApplicationLocale() override;
+  void SetApplicationLocale(const std::string& locale) override;
+  DownloadStatusUpdater* download_status_updater() override;
+  DownloadRequestLimiter* download_request_limiter() override;
+  BackgroundModeManager* background_mode_manager() override;
+  void set_background_mode_manager_for_test(
+      std::unique_ptr<BackgroundModeManager> manager) override;
+  StatusTray* status_tray() override;
+  safe_browsing::SafeBrowsingService* safe_browsing_service() override;
+  safe_browsing::ClientSideDetectionService* safe_browsing_detection_service()
+      override;
+  subresource_filter::RulesetService* subresource_filter_ruleset_service()
+      override;
+  optimization_guide::OptimizationGuideService* optimization_guide_service()
+      override;
+  StartupData* startup_data() override;
+
+#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
+  void StartAutoupdateTimer() override;
+#endif
+
+  component_updater::ComponentUpdateService* component_updater() override;
+  MediaFileSystemRegistry* media_file_system_registry() override;
+  WebRtcLogUploader* webrtc_log_uploader() override;
+  network_time::NetworkTimeTracker* network_time_tracker() override;
+  gcm::GCMDriver* gcm_driver() override;
+  resource_coordinator::TabManager* GetTabManager() override;
+  resource_coordinator::ResourceCoordinatorParts* resource_coordinator_parts()
+      override;
+  BuildState* GetBuildState() override;
+
+ private:
+  bool initialized_;
+  bool context_initialized_;
+  bool shutdown_;
+
+  std::string locale_;
+  std::unique_ptr<printing::PrintJobManager> print_job_manager_;
+  std::unique_ptr<ChromeProfileManagerStub> profile_manager_;
+  scoped_refptr<extensions::EventRouterForwarder> event_router_forwarder_;
+  scoped_refptr<printing::PrintPreviewDialogController>
+      print_preview_dialog_controller_;
+  std::unique_ptr<printing::BackgroundPrintingManager>
+      background_printing_manager_;
+  std::unique_ptr<PrefService> local_state_;
+  // Must be destroyed after |local_state_|.
+  std::unique_ptr<policy::ChromeBrowserPolicyConnector>
+      browser_policy_connector_;
+  std::unique_ptr<base::FieldTrialList> field_trial_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserProcessStub);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_CHROME_BROWSER_PROCESS_STUB_H_
diff --git a/src/libcef/browser/chrome_crash_reporter_client_stub.cc b/src/libcef/browser/chrome_crash_reporter_client_stub.cc
new file mode 100644
index 0000000..837db31
--- /dev/null
+++ b/src/libcef/browser/chrome_crash_reporter_client_stub.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+
+#include "chrome/app/chrome_crash_reporter_client.h"
+
+// Required due to https://crrev.com/1c9f89a06f
+void ChromeCrashReporterClient::Create() {}
+
+#endif  // defined(OS_MACOSX)
diff --git a/src/libcef/browser/chrome_profile_manager_stub.cc b/src/libcef/browser/chrome_profile_manager_stub.cc
new file mode 100644
index 0000000..ba3e8ca
--- /dev/null
+++ b/src/libcef/browser/chrome_profile_manager_stub.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/chrome_profile_manager_stub.h"
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/content_browser_client.h"
+
+namespace {
+
+// Return the active browser context. This is primarily called from Chrome code
+// that handles WebUI views and wishes to associate the view's data with a
+// particular context (profile). Chrome stores multiple profiles in sub-
+// directories of |user_data_dir| and then uses ProfileManager to track which
+// profile (sub-directory name) was last active.
+//
+// TODO(cef): To most closely match Chrome behavior this should return the
+// context for the currently active browser (e.g. the browser with input focus).
+// Return the main context for now since we don't currently have a good way to
+// determine that.
+CefBrowserContext* GetActiveBrowserContext() {
+  return static_cast<CefBrowserContext*>(
+      CefContentBrowserClient::Get()->request_context()->GetBrowserContext());
+}
+
+}  // namespace
+
+ChromeProfileManagerStub::ChromeProfileManagerStub()
+    : ProfileManager(base::FilePath()) {}
+
+ChromeProfileManagerStub::~ChromeProfileManagerStub() {}
+
+Profile* ChromeProfileManagerStub::GetProfile(
+    const base::FilePath& profile_dir) {
+  CefBrowserContext* browser_context =
+      CefBrowserContext::GetForCachePath(profile_dir);
+  if (!browser_context) {
+    // ProfileManager makes assumptions about profile directory paths that do
+    // not match CEF usage. For example, the default Chrome profile name is
+    // "Default" so it will append that sub-directory name to an empty
+    // |user_data_dir| value and then call this method. Use the active context
+    // in cases such as this where we don't understand what ProfileManager is
+    // asking for.
+    browser_context = GetActiveBrowserContext();
+  }
+  return browser_context;
+}
+
+bool ChromeProfileManagerStub::IsValidProfile(const void* profile) {
+  if (!profile)
+    return false;
+  return !!CefBrowserContext::GetForContext(
+      reinterpret_cast<content::BrowserContext*>(const_cast<void*>(profile)));
+}
+
+Profile* ChromeProfileManagerStub::GetLastUsedProfile(
+    const base::FilePath& user_data_dir) {
+  // Override this method to avoid having to register prefs::kProfileLastUsed,
+  // usage of which doesn't make sense for CEF.
+  return GetActiveBrowserContext();
+}
diff --git a/src/libcef/browser/chrome_profile_manager_stub.h b/src/libcef/browser/chrome_profile_manager_stub.h
new file mode 100644
index 0000000..549ff75
--- /dev/null
+++ b/src/libcef/browser/chrome_profile_manager_stub.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a stub implementation of Chrome's ProfileManager object
+// for use as an interop layer between CEF and files that live in chrome/.
+
+#ifndef CEF_LIBCEF_BROWSER_CHROME_PROFILE_MANAGER_STUB_H_
+#define CEF_LIBCEF_BROWSER_CHROME_PROFILE_MANAGER_STUB_H_
+
+#include "chrome/browser/profiles/profile_manager.h"
+
+class ChromeProfileManagerStub : public ProfileManager {
+ public:
+  ChromeProfileManagerStub();
+  ~ChromeProfileManagerStub() override;
+
+  Profile* GetProfile(const base::FilePath& profile_dir) override;
+  bool IsValidProfile(const void* profile) override;
+  Profile* GetLastUsedProfile(const base::FilePath& user_data_dir) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChromeProfileManagerStub);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_CHROME_PROFILE_MANAGER_STUB_H_
diff --git a/src/libcef/browser/chrome_profile_stub.cc b/src/libcef/browser/chrome_profile_stub.cc
new file mode 100644
index 0000000..b52f826
--- /dev/null
+++ b/src/libcef/browser/chrome_profile_stub.cc
@@ -0,0 +1,163 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/chrome_profile_stub.h"
+
+#include "components/variations/variations_client.h"
+#include "components/variations/variations_http_header_provider.h"
+#include "content/public/browser/resource_context.h"
+#include "net/url_request/url_request_context.h"
+
+namespace {
+
+class CefVariationsClient : public variations::VariationsClient {
+ public:
+  explicit CefVariationsClient(content::BrowserContext* browser_context)
+      : browser_context_(browser_context) {}
+
+  ~CefVariationsClient() override = default;
+
+  bool IsIncognito() const override {
+    return browser_context_->IsOffTheRecord();
+  }
+
+  std::string GetVariationsHeader() const override {
+    return variations::VariationsHttpHeaderProvider::GetInstance()
+        ->GetClientDataHeader(false /* is_signed_in */);
+  }
+
+ private:
+  content::BrowserContext* browser_context_;
+};
+
+}  // namespace
+
+ChromeProfileStub::ChromeProfileStub() {}
+
+ChromeProfileStub::~ChromeProfileStub() {}
+
+bool ChromeProfileStub::IsOffTheRecord() {
+  return false;
+}
+
+bool ChromeProfileStub::IsOffTheRecord() const {
+  return false;
+}
+
+variations::VariationsClient* ChromeProfileStub::GetVariationsClient() {
+  if (!variations_client_)
+    variations_client_ = std::make_unique<CefVariationsClient>(this);
+  return variations_client_.get();
+}
+
+scoped_refptr<base::SequencedTaskRunner> ChromeProfileStub::GetIOTaskRunner() {
+  NOTREACHED();
+  return scoped_refptr<base::SequencedTaskRunner>();
+}
+
+std::string ChromeProfileStub::GetProfileUserName() const {
+  NOTREACHED();
+  return std::string();
+}
+
+Profile::ProfileType ChromeProfileStub::GetProfileType() const {
+  return REGULAR_PROFILE;
+}
+
+Profile* ChromeProfileStub::GetOffTheRecordProfile() {
+  NOTREACHED();
+  return nullptr;
+}
+
+void ChromeProfileStub::DestroyOffTheRecordProfile() {
+  NOTREACHED();
+}
+
+bool ChromeProfileStub::HasOffTheRecordProfile() {
+  return false;
+}
+
+Profile* ChromeProfileStub::GetOriginalProfile() {
+  return this;
+}
+
+const Profile* ChromeProfileStub::GetOriginalProfile() const {
+  return this;
+}
+
+bool ChromeProfileStub::IsSupervised() const {
+  return false;
+}
+
+bool ChromeProfileStub::IsChild() const {
+  return false;
+}
+
+bool ChromeProfileStub::IsLegacySupervised() const {
+  return false;
+}
+
+ExtensionSpecialStoragePolicy*
+ChromeProfileStub::GetExtensionSpecialStoragePolicy() {
+  NOTREACHED();
+  return nullptr;
+}
+
+PrefService* ChromeProfileStub::GetOffTheRecordPrefs() {
+  NOTREACHED();
+  return nullptr;
+}
+
+bool ChromeProfileStub::IsSameProfile(Profile* profile) {
+  NOTREACHED();
+  return false;
+}
+
+base::Time ChromeProfileStub::GetStartTime() const {
+  NOTREACHED();
+  return base::Time();
+}
+
+base::FilePath ChromeProfileStub::last_selected_directory() {
+  NOTREACHED();
+  return base::FilePath();
+}
+
+void ChromeProfileStub::set_last_selected_directory(
+    const base::FilePath& path) {
+  NOTREACHED();
+}
+
+GURL ChromeProfileStub::GetHomePage() {
+  NOTREACHED();
+  return GURL();
+}
+
+bool ChromeProfileStub::WasCreatedByVersionOrLater(const std::string& version) {
+  NOTREACHED();
+  return false;
+}
+
+bool ChromeProfileStub::IsIndependentOffTheRecordProfile() {
+  return false;
+}
+
+void ChromeProfileStub::SetExitType(ExitType exit_type) {
+  NOTREACHED();
+}
+
+Profile::ExitType ChromeProfileStub::GetLastSessionExitType() {
+  NOTREACHED();
+  return EXIT_NORMAL;
+}
+
+base::Time ChromeProfileStub::GetCreationTime() const {
+  NOTREACHED();
+  return base::Time();
+}
+
+void ChromeProfileStub::SetCreationTimeForTesting(base::Time creation_time) {
+  NOTREACHED();
+}
diff --git a/src/libcef/browser/chrome_profile_stub.h b/src/libcef/browser/chrome_profile_stub.h
new file mode 100644
index 0000000..c9859da
--- /dev/null
+++ b/src/libcef/browser/chrome_profile_stub.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This class gathers state related to a single user profile.
+
+#ifndef CEF_LIBCEF_BROWSER_CHROME_PROFILE_STUB_H_
+#define CEF_LIBCEF_BROWSER_CHROME_PROFILE_STUB_H_
+
+#include "chrome/browser/profiles/profile.h"
+
+// This file provides a stub implementation of Chrome's Profile object for use
+// as an interop layer between CEF and files that live in chrome/.
+
+class ChromeProfileStub : public Profile {
+ public:
+  ChromeProfileStub();
+  ~ChromeProfileStub() override;
+
+ protected:
+  // Profile methods.
+  bool IsOffTheRecord() override;
+  bool IsOffTheRecord() const override;
+  variations::VariationsClient* GetVariationsClient() override;
+  scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() override;
+  std::string GetProfileUserName() const override;
+  ProfileType GetProfileType() const override;
+  Profile* GetOffTheRecordProfile() override;
+  void DestroyOffTheRecordProfile() override;
+  bool HasOffTheRecordProfile() override;
+  Profile* GetOriginalProfile() override;
+  const Profile* GetOriginalProfile() const override;
+  bool IsSupervised() const override;
+  bool IsChild() const override;
+  bool IsLegacySupervised() const override;
+  ExtensionSpecialStoragePolicy* GetExtensionSpecialStoragePolicy() override;
+  PrefService* GetOffTheRecordPrefs() override;
+  bool IsSameProfile(Profile* profile) override;
+  base::Time GetStartTime() const override;
+  base::FilePath last_selected_directory() override;
+  void set_last_selected_directory(const base::FilePath& path) override;
+  GURL GetHomePage() override;
+  bool WasCreatedByVersionOrLater(const std::string& version) override;
+  bool IsIndependentOffTheRecordProfile() override;
+  void SetExitType(ExitType exit_type) override;
+  ExitType GetLastSessionExitType() override;
+  base::Time GetCreationTime() const override;
+  void SetCreationTimeForTesting(base::Time creation_time) override;
+
+ private:
+  std::unique_ptr<variations::VariationsClient> variations_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeProfileStub);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_CHROME_PROFILE_STUB_H_
diff --git a/src/libcef/browser/content_browser_client.cc b/src/libcef/browser/content_browser_client.cc
new file mode 100644
index 0000000..64f3b3c
--- /dev/null
+++ b/src/libcef/browser/content_browser_client.cc
@@ -0,0 +1,1515 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/content_browser_client.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "include/cef_version.h"
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/browser_info.h"
+#include "libcef/browser/browser_info_manager.h"
+#include "libcef/browser/browser_main.h"
+#include "libcef/browser/browser_message_filter.h"
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/devtools/devtools_manager_delegate.h"
+#include "libcef/browser/extensions/extension_system.h"
+#include "libcef/browser/extensions/extension_web_contents_observer.h"
+#include "libcef/browser/media_capture_devices_dispatcher.h"
+#include "libcef/browser/net/chrome_scheme_handler.h"
+#include "libcef/browser/net_service/login_delegate.h"
+#include "libcef/browser/net_service/proxy_url_loader_factory.h"
+#include "libcef/browser/net_service/resource_request_handler_wrapper.h"
+#include "libcef/browser/plugins/plugin_service_filter.h"
+#include "libcef/browser/prefs/renderer_prefs.h"
+#include "libcef/browser/printing/printing_message_filter.h"
+#include "libcef/browser/speech_recognition_manager_delegate.h"
+#include "libcef/browser/ssl_info_impl.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/x509_certificate_impl.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/command_line_impl.h"
+#include "libcef/common/content_client.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/common/net/scheme_registration.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/service_manifests/cef_content_browser_overlay_manifest.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/json/json_reader.h"
+#include "base/path_service.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "cef/grit/cef_resources.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_content_browser_client.h"
+#include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/plugins/plugin_info_host_impl.h"
+#include "chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h"
+#include "chrome/browser/plugins/plugin_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/renderer_updater.h"
+#include "chrome/browser/profiles/renderer_updater_factory.h"
+#include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
+#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/google_url_loader_throttle.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/services/printing/printing_service.h"
+#include "components/navigation_interception/intercept_navigation_throttle.h"
+#include "components/navigation_interception/navigation_params.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
+#include "components/variations/variations_http_header_provider.h"
+#include "components/version_info/version_info.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/plugin_service_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_ppapi_host.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/client_certificate_delegate.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/overlay_window.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/quota_permission_context.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui_url_loader_factory.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/service_names.mojom.h"
+#include "content/public/common/storage_quota_params.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/common/user_agent.h"
+#include "content/public/common/web_preferences.h"
+#include "extensions/browser/extension_message_filter.h"
+#include "extensions/browser/extension_protocols.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extensions_browser_client.h"
+#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
+#include "extensions/browser/url_loader_factory_manager.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/switches.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
+#include "net/base/auth.h"
+#include "net/ssl/ssl_cert_request_info.h"
+#include "ppapi/host/ppapi_host.h"
+#include "services/network/public/cpp/network_switches.h"
+#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
+#include "services/service_manager/embedder/switches.h"
+#include "services/service_manager/public/mojom/connector.mojom.h"
+#include "services/service_manager/sandbox/switches.h"
+#include "storage/browser/quota/quota_settings.h"
+#include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom.h"
+#include "third_party/blink/public/mojom/prerender/prerender.mojom.h"
+#include "third_party/blink/public/web/web_window_features.h"
+#include "third_party/widevine/cdm/buildflags.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_switches.h"
+#include "url/gurl.h"
+
+#if defined(OS_LINUX)
+#include "libcef/common/widevine_loader.h"
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#include "base/debug/leak_annotations.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/crash/content/browser/crash_handler_host_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
+#include "content/public/common/content_descriptors.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "net/ssl/client_cert_store_mac.h"
+#include "services/video_capture/public/mojom/constants.mojom.h"
+#endif
+
+#if defined(OS_WIN)
+#include "net/ssl/client_cert_store_win.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#endif
+
+#if defined(USE_NSS_CERTS)
+#include "net/ssl/client_cert_store_nss.h"
+#endif
+
+#if BUILDFLAG(HAS_SPELLCHECK_PANEL)
+#include "chrome/browser/spellchecker/spell_check_panel_host_impl.h"
+#endif
+
+namespace {
+
+class CefQuotaCallbackImpl : public CefRequestCallback {
+ public:
+  using CallbackType = content::QuotaPermissionContext::PermissionCallback;
+
+  explicit CefQuotaCallbackImpl(CallbackType callback)
+      : callback_(std::move(callback)) {}
+
+  ~CefQuotaCallbackImpl() {
+    if (!callback_.is_null()) {
+      // The callback is still pending. Cancel it now.
+      if (CEF_CURRENTLY_ON_IOT()) {
+        RunNow(std::move(callback_), false);
+      } else {
+        CEF_POST_TASK(CEF_IOT, base::BindOnce(&CefQuotaCallbackImpl::RunNow,
+                                              std::move(callback_), false));
+      }
+    }
+  }
+
+  void Continue(bool allow) override {
+    if (CEF_CURRENTLY_ON_IOT()) {
+      if (!callback_.is_null()) {
+        RunNow(std::move(callback_), allow);
+      }
+    } else {
+      CEF_POST_TASK(CEF_IOT, base::BindOnce(&CefQuotaCallbackImpl::Continue,
+                                            this, allow));
+    }
+  }
+
+  void Cancel() override { Continue(false); }
+
+  CallbackType Disconnect() WARN_UNUSED_RESULT { return std::move(callback_); }
+
+ private:
+  static void RunNow(CallbackType callback, bool allow) {
+    CEF_REQUIRE_IOT();
+    std::move(callback).Run(
+        allow ? content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW
+              : content::QuotaPermissionContext::
+                    QUOTA_PERMISSION_RESPONSE_DISALLOW);
+  }
+
+  CallbackType callback_;
+
+  IMPLEMENT_REFCOUNTING(CefQuotaCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefQuotaCallbackImpl);
+};
+
+class CefAllowCertificateErrorCallbackImpl : public CefRequestCallback {
+ public:
+  typedef base::OnceCallback<void(content::CertificateRequestResultType)>
+      CallbackType;
+
+  explicit CefAllowCertificateErrorCallbackImpl(CallbackType callback)
+      : callback_(std::move(callback)) {}
+
+  ~CefAllowCertificateErrorCallbackImpl() {
+    if (!callback_.is_null()) {
+      // The callback is still pending. Cancel it now.
+      if (CEF_CURRENTLY_ON_UIT()) {
+        RunNow(std::move(callback_), false);
+      } else {
+        CEF_POST_TASK(
+            CEF_UIT,
+            base::BindOnce(&CefAllowCertificateErrorCallbackImpl::RunNow,
+                           std::move(callback_), false));
+      }
+    }
+  }
+
+  void Continue(bool allow) override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (!callback_.is_null()) {
+        RunNow(std::move(callback_), allow);
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT,
+                    base::Bind(&CefAllowCertificateErrorCallbackImpl::Continue,
+                               this, allow));
+    }
+  }
+
+  void Cancel() override { Continue(false); }
+
+  CallbackType Disconnect() WARN_UNUSED_RESULT { return std::move(callback_); }
+
+ private:
+  static void RunNow(CallbackType callback, bool allow) {
+    CEF_REQUIRE_UIT();
+    std::move(callback).Run(
+        allow ? content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE
+              : content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
+  }
+
+  CallbackType callback_;
+
+  IMPLEMENT_REFCOUNTING(CefAllowCertificateErrorCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefAllowCertificateErrorCallbackImpl);
+};
+
+class CefSelectClientCertificateCallbackImpl
+    : public CefSelectClientCertificateCallback {
+ public:
+  explicit CefSelectClientCertificateCallbackImpl(
+      std::unique_ptr<content::ClientCertificateDelegate> delegate)
+      : delegate_(std::move(delegate)) {}
+
+  ~CefSelectClientCertificateCallbackImpl() {
+    // If Select has not been called, call it with NULL to continue without any
+    // client certificate.
+    if (delegate_)
+      DoSelect(nullptr);
+  }
+
+  void Select(CefRefPtr<CefX509Certificate> cert) override {
+    if (delegate_)
+      DoSelect(cert);
+  }
+
+ private:
+  void DoSelect(CefRefPtr<CefX509Certificate> cert) {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      RunNow(std::move(delegate_), cert);
+    } else {
+      CEF_POST_TASK(
+          CEF_UIT,
+          base::BindOnce(&CefSelectClientCertificateCallbackImpl::RunNow,
+                         std::move(delegate_), cert));
+    }
+  }
+
+  static void RunNow(
+      std::unique_ptr<content::ClientCertificateDelegate> delegate,
+      CefRefPtr<CefX509Certificate> cert) {
+    CEF_REQUIRE_UIT();
+
+    if (cert) {
+      CefX509CertificateImpl* certImpl =
+          static_cast<CefX509CertificateImpl*>(cert.get());
+      certImpl->AcquirePrivateKey(base::BindOnce(
+          &CefSelectClientCertificateCallbackImpl::RunWithPrivateKey,
+          std::move(delegate), cert));
+      return;
+    }
+
+    delegate->ContinueWithCertificate(nullptr, nullptr);
+  }
+
+  static void RunWithPrivateKey(
+      std::unique_ptr<content::ClientCertificateDelegate> delegate,
+      CefRefPtr<CefX509Certificate> cert,
+      scoped_refptr<net::SSLPrivateKey> key) {
+    CEF_REQUIRE_UIT();
+    DCHECK(cert);
+
+    if (key) {
+      CefX509CertificateImpl* certImpl =
+          static_cast<CefX509CertificateImpl*>(cert.get());
+      delegate->ContinueWithCertificate(certImpl->GetInternalCertObject(), key);
+    } else {
+      delegate->ContinueWithCertificate(nullptr, nullptr);
+    }
+  }
+
+  std::unique_ptr<content::ClientCertificateDelegate> delegate_;
+
+  IMPLEMENT_REFCOUNTING(CefSelectClientCertificateCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefSelectClientCertificateCallbackImpl);
+};
+
+class CefQuotaPermissionContext : public content::QuotaPermissionContext {
+ public:
+  CefQuotaPermissionContext() {}
+
+  // The callback will be dispatched on the IO thread.
+  void RequestQuotaPermission(const content::StorageQuotaParams& params,
+                              int render_process_id,
+                              PermissionCallback callback) override {
+    if (params.storage_type != blink::mojom::StorageType::kPersistent) {
+      // To match Chrome behavior we only support requesting quota with this
+      // interface for Persistent storage type.
+      std::move(callback).Run(QUOTA_PERMISSION_RESPONSE_DISALLOW);
+      return;
+    }
+
+    bool handled = false;
+
+    CefRefPtr<CefBrowserHostImpl> browser =
+        CefBrowserHostImpl::GetBrowserForFrameRoute(render_process_id,
+                                                    params.render_frame_id);
+    if (browser.get()) {
+      CefRefPtr<CefClient> client = browser->GetClient();
+      if (client.get()) {
+        CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
+        if (handler.get()) {
+          CefRefPtr<CefQuotaCallbackImpl> callbackImpl(
+              new CefQuotaCallbackImpl(std::move(callback)));
+          handled = handler->OnQuotaRequest(
+              browser.get(), params.origin_url.spec(), params.requested_size,
+              callbackImpl.get());
+          if (!handled) {
+            // May return nullptr if the client has already executed the
+            // callback.
+            callback = callbackImpl->Disconnect();
+          }
+        }
+      }
+    }
+
+    if (!handled && !callback.is_null()) {
+      // Disallow the request by default.
+      std::move(callback).Run(QUOTA_PERMISSION_RESPONSE_DISALLOW);
+    }
+  }
+
+ private:
+  ~CefQuotaPermissionContext() override {}
+
+  DISALLOW_COPY_AND_ASSIGN(CefQuotaPermissionContext);
+};
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
+    const std::string& process_type) {
+  base::FilePath dumps_path;
+  base::PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path);
+  {
+    ANNOTATE_SCOPED_MEMORY_LEAK;
+    // Uploads will only occur if a non-empty crash URL is specified in
+    // CefMainDelegate::InitCrashReporter.
+    breakpad::CrashHandlerHostLinux* crash_handler =
+        new breakpad::CrashHandlerHostLinux(process_type, dumps_path,
+                                            true /* upload */);
+    crash_handler->StartUploaderThread();
+    return crash_handler;
+  }
+}
+
+int GetCrashSignalFD(const base::CommandLine& command_line) {
+  if (!breakpad::IsCrashReporterEnabled())
+    return -1;
+
+  // Extensions have the same process type as renderers.
+  if (command_line.HasSwitch(extensions::switches::kExtensionProcess)) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost("extension");
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  std::string process_type =
+      command_line.GetSwitchValueASCII(switches::kProcessType);
+
+  if (process_type == switches::kRendererProcess) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost(process_type);
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  if (process_type == switches::kPpapiPluginProcess) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost(process_type);
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  if (process_type == switches::kGpuProcess) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost(process_type);
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  return -1;
+}
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
+
+// TODO(cef): We can't currently trust NavigationParams::is_main_frame() because
+// it's always set to true in
+// InterceptNavigationThrottle::CheckIfShouldIgnoreNavigation. Remove the
+// |is_main_frame| argument once this problem is fixed.
+bool NavigationOnUIThread(
+    bool is_main_frame,
+    int64_t frame_id,
+    int64_t parent_frame_id,
+    int frame_tree_node_id,
+    content::WebContents* source,
+    const navigation_interception::NavigationParams& params) {
+  CEF_REQUIRE_UIT();
+
+  bool ignore_navigation = false;
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CefBrowserHostImpl::GetBrowserForContents(source);
+  if (browser.get()) {
+    CefRefPtr<CefClient> client = browser->GetClient();
+    if (client.get()) {
+      CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
+      if (handler.get()) {
+        CefRefPtr<CefFrame> frame;
+        if (is_main_frame) {
+          frame = browser->GetMainFrame();
+        } else if (frame_id >= 0) {
+          frame = browser->GetFrame(frame_id);
+        }
+        if (!frame && frame_tree_node_id >= 0) {
+          frame = browser->GetFrameForFrameTreeNode(frame_tree_node_id);
+        }
+        if (!frame) {
+          // Create a temporary frame object for navigation of sub-frames that
+          // don't yet exist.
+          frame = browser->browser_info()->CreateTempSubFrame(parent_frame_id);
+        }
+
+        CefRefPtr<CefRequestImpl> request = new CefRequestImpl();
+        request->Set(params, is_main_frame);
+        request->SetReadOnly(true);
+
+        // Initiating a new navigation in OnBeforeBrowse will delete the
+        // InterceptNavigationThrottle that currently owns this callback,
+        // resulting in a crash. Use the lock to prevent that.
+        std::unique_ptr<CefBrowserHostImpl::NavigationLock> navigation_lock =
+            browser->CreateNavigationLock();
+        ignore_navigation = handler->OnBeforeBrowse(
+            browser.get(), frame, request.get(), params.has_user_gesture(),
+            params.is_redirect());
+      }
+    }
+  }
+
+  return ignore_navigation;
+}
+
+// From chrome/browser/plugins/chrome_content_browser_client_plugins_part.cc.
+void BindPluginInfoHost(
+    int render_process_id,
+    mojo::PendingAssociatedReceiver<chrome::mojom::PluginInfoHost> receiver) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  content::RenderProcessHost* host =
+      content::RenderProcessHost::FromID(render_process_id);
+  if (!host)
+    return;
+
+  Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
+  mojo::MakeSelfOwnedAssociatedReceiver(
+      std::make_unique<PluginInfoHostImpl>(render_process_id, profile),
+      std::move(receiver));
+}
+
+base::FilePath GetRootCachePath() {
+  // The CefContext::ValidateCachePath method enforces the requirement that all
+  // cache_path values be either equal to or a child of root_cache_path.
+  return base::FilePath(
+      CefString(&CefContext::Get()->settings().root_cache_path));
+}
+
+// Register BrowserInterfaceBroker's GetInterface() handler callbacks for
+// chrome-specific document-scoped interfaces.
+// Stub implementations to silence "Empty binder for interface
+// blink.mojom.[Name] for the frame/document scope" errors.
+// Based on chrome/browser/chrome_browser_interface_binders.cc.
+void PopulateChromeFrameBinders(
+    service_manager::BinderMapWithContext<content::RenderFrameHost*>* map) {
+  map->Add<blink::mojom::InsecureInputService>(base::BindRepeating(
+      [](content::RenderFrameHost* frame_host,
+         mojo::PendingReceiver<blink::mojom::InsecureInputService> receiver) {
+      }));
+
+  map->Add<blink::mojom::PrerenderProcessor>(base::BindRepeating(
+      [](content::RenderFrameHost* frame_host,
+         mojo::PendingReceiver<blink::mojom::PrerenderProcessor> receiver) {}));
+}
+
+}  // namespace
+
+CefContentBrowserClient::CefContentBrowserClient()
+    : browser_main_parts_(nullptr) {
+  plugin_service_filter_.reset(new CefPluginServiceFilter);
+  content::PluginServiceImpl::GetInstance()->SetFilter(
+      plugin_service_filter_.get());
+}
+
+CefContentBrowserClient::~CefContentBrowserClient() {}
+
+// static
+CefContentBrowserClient* CefContentBrowserClient::Get() {
+  if (!CefContentClient::Get())
+    return nullptr;
+  return static_cast<CefContentBrowserClient*>(
+      CefContentClient::Get()->browser());
+}
+
+std::unique_ptr<content::BrowserMainParts>
+CefContentBrowserClient::CreateBrowserMainParts(
+    const content::MainFunctionParams& parameters) {
+  browser_main_parts_ = new CefBrowserMainParts(parameters);
+  return base::WrapUnique(browser_main_parts_);
+}
+
+void CefContentBrowserClient::RenderProcessWillLaunch(
+    content::RenderProcessHost* host) {
+  const int id = host->GetID();
+  Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
+
+  host->AddFilter(new CefBrowserMessageFilter(id));
+  host->AddFilter(new printing::CefPrintingMessageFilter(id, profile));
+
+  if (extensions::ExtensionsEnabled()) {
+    host->AddFilter(new extensions::ExtensionMessageFilter(id, profile));
+    host->AddFilter(
+        new extensions::ExtensionsGuestViewMessageFilter(id, profile));
+  }
+
+  // If the renderer process crashes then the host may already have
+  // CefBrowserInfoManager as an observer. Try to remove it first before adding
+  // to avoid DCHECKs.
+  host->RemoveObserver(CefBrowserInfoManager::GetInstance());
+  host->AddObserver(CefBrowserInfoManager::GetInstance());
+
+  // Forwards dynamic parameters to CefRenderThreadObserver.
+  Profile* original_profile = profile->GetOriginalProfile();
+  RendererUpdaterFactory::GetForProfile(original_profile)
+      ->InitializeRenderer(host);
+}
+
+bool CefContentBrowserClient::ShouldUseProcessPerSite(
+    content::BrowserContext* browser_context,
+    const GURL& effective_url) {
+  if (!extensions::ExtensionsEnabled())
+    return false;
+
+  if (!effective_url.SchemeIs(extensions::kExtensionScheme))
+    return false;
+
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(browser_context);
+  if (!registry)
+    return false;
+
+  const extensions::Extension* extension =
+      registry->enabled_extensions().GetByID(effective_url.host());
+  if (!extension)
+    return false;
+
+  // TODO(extensions): Extra checks required if type is TYPE_HOSTED_APP.
+
+  // Hosted apps that have script access to their background page must use
+  // process per site, since all instances can make synchronous calls to the
+  // background window.  Other extensions should use process per site as well.
+  return true;
+}
+
+// Based on
+// ChromeContentBrowserClientExtensionsPart::DoesSiteRequireDedicatedProcess.
+bool CefContentBrowserClient::DoesSiteRequireDedicatedProcess(
+    content::BrowserContext* browser_context,
+    const GURL& effective_site_url) {
+  if (!extensions::ExtensionsEnabled())
+    return false;
+
+  const extensions::Extension* extension =
+      extensions::ExtensionRegistry::Get(browser_context)
+          ->enabled_extensions()
+          .GetExtensionOrAppByURL(effective_site_url);
+  // Isolate all extensions.
+  return extension != nullptr;
+}
+
+void CefContentBrowserClient::OverrideURLLoaderFactoryParams(
+    content::BrowserContext* browser_context,
+    const url::Origin& origin,
+    bool is_for_isolated_world,
+    network::mojom::URLLoaderFactoryParams* factory_params) {
+  if (extensions::ExtensionsEnabled()) {
+    extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams(
+        browser_context, origin, is_for_isolated_world, factory_params);
+  }
+}
+
+void CefContentBrowserClient::GetAdditionalWebUISchemes(
+    std::vector<std::string>* additional_schemes) {
+  // Any schemes listed here are treated as WebUI schemes but do not get WebUI
+  // bindings. Also, view-source is allowed for these schemes. WebUI schemes
+  // will not be passed to HandleExternalProtocol.
+}
+
+void CefContentBrowserClient::GetAdditionalViewSourceSchemes(
+    std::vector<std::string>* additional_schemes) {
+  GetAdditionalWebUISchemes(additional_schemes);
+
+  additional_schemes->push_back(extensions::kExtensionScheme);
+}
+
+void CefContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
+    std::vector<std::string>* additional_allowed_schemes) {
+  ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
+      additional_allowed_schemes);
+  additional_allowed_schemes->push_back(content::kChromeDevToolsScheme);
+  additional_allowed_schemes->push_back(content::kChromeUIScheme);
+}
+
+bool CefContentBrowserClient::IsWebUIAllowedToMakeNetworkRequests(
+    const url::Origin& origin) {
+  return scheme::IsWebUIAllowedToMakeNetworkRequests(origin);
+}
+
+bool CefContentBrowserClient::IsHandledURL(const GURL& url) {
+  if (!url.is_valid())
+    return false;
+  const std::string& scheme = url.scheme();
+  DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
+
+  if (scheme::IsInternalHandledScheme(scheme))
+    return true;
+
+  return CefContentClient::Get()->HasCustomScheme(scheme);
+}
+
+void CefContentBrowserClient::SiteInstanceGotProcess(
+    content::SiteInstance* site_instance) {
+  if (!extensions::ExtensionsEnabled())
+    return;
+
+  // If this isn't an extension renderer there's nothing to do.
+  const extensions::Extension* extension = GetExtension(site_instance);
+  if (!extension)
+    return;
+
+  CefBrowserContext* browser_context =
+      static_cast<CefBrowserContext*>(site_instance->GetBrowserContext());
+
+  extensions::ProcessMap::Get(browser_context)
+      ->Insert(extension->id(), site_instance->GetProcess()->GetID(),
+               site_instance->GetId());
+
+  CEF_POST_TASK(
+      CEF_IOT, base::Bind(&extensions::InfoMap::RegisterExtensionProcess,
+                          browser_context->extension_system()->info_map(),
+                          extension->id(), site_instance->GetProcess()->GetID(),
+                          site_instance->GetId()));
+}
+
+void CefContentBrowserClient::SiteInstanceDeleting(
+    content::SiteInstance* site_instance) {
+  if (!extensions::ExtensionsEnabled())
+    return;
+
+  // May be NULL during shutdown.
+  if (!extensions::ExtensionsBrowserClient::Get())
+    return;
+
+  // May be NULL during shutdown.
+  if (!site_instance->HasProcess())
+    return;
+
+  // If this isn't an extension renderer there's nothing to do.
+  const extensions::Extension* extension = GetExtension(site_instance);
+  if (!extension)
+    return;
+
+  CefBrowserContext* browser_context =
+      static_cast<CefBrowserContext*>(site_instance->GetBrowserContext());
+
+  extensions::ProcessMap::Get(browser_context)
+      ->Remove(extension->id(), site_instance->GetProcess()->GetID(),
+               site_instance->GetId());
+
+  CEF_POST_TASK(
+      CEF_IOT, base::Bind(&extensions::InfoMap::UnregisterExtensionProcess,
+                          browser_context->extension_system()->info_map(),
+                          extension->id(), site_instance->GetProcess()->GetID(),
+                          site_instance->GetId()));
+}
+
+void CefContentBrowserClient::BindHostReceiverForRenderer(
+    content::RenderProcessHost* render_process_host,
+    mojo::GenericPendingReceiver receiver) {
+  if (auto host_receiver = receiver.As<spellcheck::mojom::SpellCheckHost>()) {
+    SpellCheckHostChromeImpl::Create(render_process_host->GetID(),
+                                     std::move(host_receiver));
+    return;
+  }
+
+#if BUILDFLAG(HAS_SPELLCHECK_PANEL)
+  if (auto panel_host_receiver =
+          receiver.As<spellcheck::mojom::SpellCheckPanelHost>()) {
+    SpellCheckPanelHostImpl::Create(render_process_host->GetID(),
+                                    std::move(panel_host_receiver));
+    return;
+  }
+#endif  // BUILDFLAG(HAS_SPELLCHECK_PANEL)
+}
+
+base::Optional<service_manager::Manifest>
+CefContentBrowserClient::GetServiceManifestOverlay(base::StringPiece name) {
+  if (name == content::mojom::kBrowserServiceName) {
+    return GetCefContentBrowserOverlayManifest();
+  }
+
+  return base::nullopt;
+}
+
+void CefContentBrowserClient::AppendExtraCommandLineSwitches(
+    base::CommandLine* command_line,
+    int child_process_id) {
+  const base::CommandLine* browser_cmd = base::CommandLine::ForCurrentProcess();
+
+  {
+    // Propagate the following switches to all command lines (along with any
+    // associated values) if present in the browser command line.
+    static const char* const kSwitchNames[] = {
+      switches::kDisablePackLoading,
+#if defined(OS_MACOSX)
+      switches::kFrameworkDirPath,
+      switches::kMainBundlePath,
+#endif
+      switches::kLocalesDirPath,
+      switches::kLogFile,
+      switches::kLogSeverity,
+      switches::kProductVersion,
+      switches::kResourcesDirPath,
+      switches::kUserAgent,
+    };
+    command_line->CopySwitchesFrom(*browser_cmd, kSwitchNames,
+                                   base::size(kSwitchNames));
+  }
+
+  const std::string& process_type =
+      command_line->GetSwitchValueASCII(switches::kProcessType);
+  if (process_type == switches::kRendererProcess) {
+    // Propagate the following switches to the renderer command line (along with
+    // any associated values) if present in the browser command line.
+    static const char* const kSwitchNames[] = {
+        switches::kDisableExtensions,
+        switches::kDisablePdfExtension,
+        switches::kDisablePlugins,
+        switches::kDisablePrintPreview,
+        switches::kDisableScrollBounce,
+        switches::kDisableSpellChecking,
+        switches::kEnableSpeechInput,
+        switches::kEnableSystemFlash,
+        switches::kPpapiFlashArgs,
+        switches::kPpapiFlashPath,
+        switches::kPpapiFlashVersion,
+        switches::kUncaughtExceptionStackSize,
+        network::switches::kUnsafelyTreatInsecureOriginAsSecure,
+    };
+    command_line->CopySwitchesFrom(*browser_cmd, kSwitchNames,
+                                   base::size(kSwitchNames));
+
+    if (extensions::ExtensionsEnabled()) {
+      content::RenderProcessHost* process =
+          content::RenderProcessHost::FromID(child_process_id);
+      CefBrowserContext* context =
+          process
+              ? CefBrowserContext::GetForContext(process->GetBrowserContext())
+              : nullptr;
+      if (context) {
+        if (context->IsPrintPreviewSupported()) {
+          command_line->AppendSwitch(switches::kEnablePrintPreview);
+        }
+
+        // Based on ChromeContentBrowserClientExtensionsPart::
+        // AppendExtraRendererCommandLineSwitches
+        if (extensions::ProcessMap::Get(context)->Contains(process->GetID())) {
+          command_line->AppendSwitch(extensions::switches::kExtensionProcess);
+        }
+      }
+    }
+  } else {
+    // Propagate the following switches to non-renderer command line (along with
+    // any associated values) if present in the browser command line.
+    static const char* const kSwitchNames[] = {
+        switches::kLang,
+    };
+    command_line->CopySwitchesFrom(*browser_cmd, kSwitchNames,
+                                   base::size(kSwitchNames));
+  }
+
+#if defined(OS_LINUX)
+  if (process_type == service_manager::switches::kZygoteProcess) {
+    // Propagate the following switches to the zygote command line (along with
+    // any associated values) if present in the browser command line.
+    static const char* const kSwitchNames[] = {
+        switches::kPpapiFlashPath,
+        switches::kPpapiFlashVersion,
+    };
+    command_line->CopySwitchesFrom(*browser_cmd, kSwitchNames,
+                                   base::size(kSwitchNames));
+
+#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+    if (!browser_cmd->HasSwitch(service_manager::switches::kNoSandbox)) {
+      // Pass the Widevine CDM path to the Zygote process. See comments in
+      // CefWidevineLoader::AddContentDecryptionModules.
+      const base::FilePath& cdm_path = CefWidevineLoader::GetInstance()->path();
+      if (!cdm_path.empty())
+        command_line->AppendSwitchPath(switches::kWidevineCdmPath, cdm_path);
+    }
+#endif
+
+    if (browser_cmd->HasSwitch(switches::kBrowserSubprocessPath)) {
+      // Force use of the sub-process executable path for the zygote process.
+      const base::FilePath& subprocess_path =
+          browser_cmd->GetSwitchValuePath(switches::kBrowserSubprocessPath);
+      if (!subprocess_path.empty())
+        command_line->SetProgram(subprocess_path);
+    }
+  }
+#endif  // defined(OS_LINUX)
+
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefBrowserProcessHandler> handler =
+        app->GetBrowserProcessHandler();
+    if (handler.get()) {
+      CefRefPtr<CefCommandLineImpl> commandLinePtr(
+          new CefCommandLineImpl(command_line, false, false));
+      handler->OnBeforeChildProcessLaunch(commandLinePtr.get());
+      commandLinePtr->Detach(nullptr);
+    }
+  }
+}
+
+std::string CefContentBrowserClient::GetApplicationLocale() {
+  return g_browser_process->GetApplicationLocale();
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+CefContentBrowserClient::GetSystemSharedURLLoaderFactory() {
+  DCHECK(
+      content::BrowserThread::CurrentlyOn(content::BrowserThread::UI) ||
+      !content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI));
+
+  if (!SystemNetworkContextManager::GetInstance())
+    return nullptr;
+
+  return SystemNetworkContextManager::GetInstance()
+      ->GetSharedURLLoaderFactory();
+}
+
+network::mojom::NetworkContext*
+CefContentBrowserClient::GetSystemNetworkContext() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(SystemNetworkContextManager::GetInstance());
+  return SystemNetworkContextManager::GetInstance()->GetContext();
+}
+
+scoped_refptr<content::QuotaPermissionContext>
+CefContentBrowserClient::CreateQuotaPermissionContext() {
+  return new CefQuotaPermissionContext();
+}
+
+content::MediaObserver* CefContentBrowserClient::GetMediaObserver() {
+  return CefMediaCaptureDevicesDispatcher::GetInstance();
+}
+
+content::SpeechRecognitionManagerDelegate*
+CefContentBrowserClient::CreateSpeechRecognitionManagerDelegate() {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kEnableSpeechInput))
+    return new CefSpeechRecognitionManagerDelegate();
+
+  return nullptr;
+}
+
+content::GeneratedCodeCacheSettings
+CefContentBrowserClient::GetGeneratedCodeCacheSettings(
+    content::BrowserContext* context) {
+  // If we pass 0 for size, disk_cache will pick a default size using the
+  // heuristics based on available disk size. These are implemented in
+  // disk_cache::PreferredCacheSize in net/disk_cache/cache_util.cc.
+  const base::FilePath& cache_path = context->GetPath();
+  return content::GeneratedCodeCacheSettings(!cache_path.empty() /* enabled */,
+                                             0 /* size */, cache_path);
+}
+
+void CefContentBrowserClient::AllowCertificateError(
+    content::WebContents* web_contents,
+    int cert_error,
+    const net::SSLInfo& ssl_info,
+    const GURL& request_url,
+    bool is_main_frame_request,
+    bool strict_enforcement,
+    base::OnceCallback<void(content::CertificateRequestResultType)> callback) {
+  CEF_REQUIRE_UIT();
+
+  if (!is_main_frame_request) {
+    // A sub-resource has a certificate error. The user doesn't really
+    // have a context for making the right decision, so block the request
+    // hard.
+    std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
+    return;
+  }
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CefBrowserHostImpl::GetBrowserForContents(web_contents);
+  if (!browser.get())
+    return;
+  CefRefPtr<CefClient> client = browser->GetClient();
+  if (!client.get())
+    return;
+  CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
+  if (!handler.get())
+    return;
+
+  CefRefPtr<CefSSLInfo> cef_ssl_info = new CefSSLInfoImpl(ssl_info);
+
+  CefRefPtr<CefAllowCertificateErrorCallbackImpl> callbackImpl(
+      new CefAllowCertificateErrorCallbackImpl(std::move(callback)));
+
+  bool proceed = handler->OnCertificateError(
+      browser.get(), static_cast<cef_errorcode_t>(cert_error),
+      request_url.spec(), cef_ssl_info, callbackImpl.get());
+  if (!proceed) {
+    // |callback| may be null if the user executed it despite returning false.
+    callback = callbackImpl->Disconnect();
+    if (!callback.is_null()) {
+      std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
+    }
+  }
+}
+
+base::OnceClosure CefContentBrowserClient::SelectClientCertificate(
+    content::WebContents* web_contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    net::ClientCertIdentityList client_certs,
+    std::unique_ptr<content::ClientCertificateDelegate> delegate) {
+  CEF_REQUIRE_UIT();
+
+  CefRefPtr<CefRequestHandler> handler;
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CefBrowserHostImpl::GetBrowserForContents(web_contents);
+  if (browser.get()) {
+    CefRefPtr<CefClient> client = browser->GetClient();
+    if (client.get())
+      handler = client->GetRequestHandler();
+  }
+
+  if (!handler.get()) {
+    delegate->ContinueWithCertificate(nullptr, nullptr);
+    return base::OnceClosure();
+  }
+
+  CefRequestHandler::X509CertificateList certs;
+  for (net::ClientCertIdentityList::iterator iter = client_certs.begin();
+       iter != client_certs.end(); iter++) {
+    certs.push_back(new CefX509CertificateImpl(std::move(*iter)));
+  }
+
+  CefRefPtr<CefSelectClientCertificateCallbackImpl> callbackImpl(
+      new CefSelectClientCertificateCallbackImpl(std::move(delegate)));
+
+  bool proceed = handler->OnSelectClientCertificate(
+      browser.get(), cert_request_info->is_proxy,
+      cert_request_info->host_and_port.host(),
+      cert_request_info->host_and_port.port(), certs, callbackImpl.get());
+
+  if (!proceed && !certs.empty()) {
+    callbackImpl->Select(certs[0]);
+  }
+  return base::OnceClosure();
+}
+
+bool CefContentBrowserClient::CanCreateWindow(
+    content::RenderFrameHost* opener,
+    const GURL& opener_url,
+    const GURL& opener_top_level_frame_url,
+    const url::Origin& source_origin,
+    content::mojom::WindowContainerType container_type,
+    const GURL& target_url,
+    const content::Referrer& referrer,
+    const std::string& frame_name,
+    WindowOpenDisposition disposition,
+    const blink::mojom::WindowFeatures& features,
+    bool user_gesture,
+    bool opener_suppressed,
+    bool* no_javascript_access) {
+  CEF_REQUIRE_UIT();
+  *no_javascript_access = false;
+
+  return CefBrowserInfoManager::GetInstance()->CanCreateWindow(
+      opener, target_url, referrer, frame_name, disposition, features,
+      user_gesture, opener_suppressed, no_javascript_access);
+}
+
+void CefContentBrowserClient::OverrideWebkitPrefs(
+    content::RenderViewHost* rvh,
+    content::WebPreferences* prefs) {
+  // Using RVH instead of RFH here because rvh->GetMainFrame() may be nullptr
+  // when this method is called.
+  renderer_prefs::PopulateWebPreferences(rvh, *prefs);
+
+  if (rvh->GetWidget()->GetView()) {
+    rvh->GetWidget()->GetView()->SetBackgroundColor(
+        prefs->base_background_color);
+  }
+}
+
+void CefContentBrowserClient::BrowserURLHandlerCreated(
+    content::BrowserURLHandler* handler) {
+  scheme::BrowserURLHandlerCreated(handler);
+}
+
+std::string CefContentBrowserClient::GetDefaultDownloadName() {
+  return "download";
+}
+
+void CefContentBrowserClient::DidCreatePpapiPlugin(
+    content::BrowserPpapiHost* browser_host) {
+  browser_host->GetPpapiHost()->AddHostFactoryFilter(
+      std::unique_ptr<ppapi::host::HostFactory>(
+          new ChromeBrowserPepperHostFactory(browser_host)));
+}
+
+content::DevToolsManagerDelegate*
+CefContentBrowserClient::GetDevToolsManagerDelegate() {
+  return new CefDevToolsManagerDelegate();
+}
+
+std::vector<std::unique_ptr<content::NavigationThrottle>>
+CefContentBrowserClient::CreateThrottlesForNavigation(
+    content::NavigationHandle* navigation_handle) {
+  CEF_REQUIRE_UIT();
+
+  std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
+
+  const bool is_main_frame = navigation_handle->IsInMainFrame();
+
+  // Identify the RenderFrameHost that originated the navigation.
+  const int64_t parent_frame_id =
+      !is_main_frame
+          ? CefFrameHostImpl::MakeFrameId(navigation_handle->GetParentFrame())
+          : CefFrameHostImpl::kInvalidFrameId;
+
+  const int64_t frame_id = !is_main_frame && navigation_handle->HasCommitted()
+                               ? CefFrameHostImpl::MakeFrameId(
+                                     navigation_handle->GetRenderFrameHost())
+                               : CefFrameHostImpl::kInvalidFrameId;
+
+  // Must use SynchronyMode::kSync to ensure that OnBeforeBrowse is always
+  // called before OnBeforeResourceLoad.
+  std::unique_ptr<content::NavigationThrottle> throttle =
+      std::make_unique<navigation_interception::InterceptNavigationThrottle>(
+          navigation_handle,
+          base::Bind(&NavigationOnUIThread, is_main_frame, frame_id,
+                     parent_frame_id, navigation_handle->GetFrameTreeNodeId()),
+          navigation_interception::SynchronyMode::kSync);
+  throttles.push_back(std::move(throttle));
+
+  return throttles;
+}
+
+std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+CefContentBrowserClient::CreateURLLoaderThrottles(
+    const network::ResourceRequest& request,
+    content::BrowserContext* browser_context,
+    const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+    content::NavigationUIData* navigation_ui_data,
+    int frame_tree_node_id) {
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
+
+  // Used to substitute View ID for PDF contents when using the PDF plugin.
+  result.push_back(std::make_unique<PluginResponseInterceptorURLLoaderThrottle>(
+      request.resource_type, frame_tree_node_id));
+
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+
+  chrome::mojom::DynamicParams dynamic_params = {
+      profile->GetPrefs()->GetBoolean(prefs::kForceGoogleSafeSearch),
+      profile->GetPrefs()->GetInteger(prefs::kForceYouTubeRestrict),
+      profile->GetPrefs()->GetString(prefs::kAllowedDomainsForApps)};
+  result.push_back(
+      std::make_unique<GoogleURLLoaderThrottle>(std::move(dynamic_params)));
+
+  return result;
+}
+
+#if defined(OS_LINUX)
+void CefContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
+    const base::CommandLine& command_line,
+    int child_process_id,
+    content::PosixFileDescriptorInfo* mappings) {
+  int crash_signal_fd = GetCrashSignalFD(command_line);
+  if (crash_signal_fd >= 0) {
+    mappings->Share(service_manager::kCrashDumpSignal, crash_signal_fd);
+  }
+}
+#endif  // defined(OS_LINUX)
+
+#if defined(OS_WIN)
+const wchar_t* CefContentBrowserClient::GetResourceDllName() {
+  static wchar_t file_path[MAX_PATH + 1] = {0};
+
+  if (file_path[0] == 0) {
+    // Retrieve the module path (usually libcef.dll).
+    base::FilePath module;
+    base::PathService::Get(base::FILE_MODULE, &module);
+    const std::wstring wstr = module.value();
+    size_t count = std::min(static_cast<size_t>(MAX_PATH), wstr.size());
+    wcsncpy(file_path, wstr.c_str(), count);
+    file_path[count] = 0;
+  }
+
+  return file_path;
+}
+
+bool CefContentBrowserClient::PreSpawnRenderer(sandbox::TargetPolicy* policy,
+                                               RendererSpawnFlags flags) {
+  return true;
+}
+#endif  // defined(OS_WIN)
+
+void CefContentBrowserClient::ExposeInterfacesToRenderer(
+    service_manager::BinderRegistry* registry,
+    blink::AssociatedInterfaceRegistry* associated_registry,
+    content::RenderProcessHost* host) {
+  associated_registry->AddInterface(
+      base::BindRepeating(&BindPluginInfoHost, host->GetID()));
+}
+
+std::unique_ptr<net::ClientCertStore>
+CefContentBrowserClient::CreateClientCertStore(
+    content::BrowserContext* browser_context) {
+  // Match the logic in ProfileNetworkContextService::CreateClientCertStore.
+#if defined(USE_NSS_CERTS)
+  // TODO: Add support for client implementation of crypto password dialog.
+  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
+      net::ClientCertStoreNSS::PasswordDelegateFactory()));
+#elif defined(OS_WIN)
+  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
+#elif defined(OS_MACOSX)
+  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
+#else
+#error Unknown platform.
+#endif
+}
+
+std::unique_ptr<content::LoginDelegate>
+CefContentBrowserClient::CreateLoginDelegate(
+    const net::AuthChallengeInfo& auth_info,
+    content::WebContents* web_contents,
+    const content::GlobalRequestID& request_id,
+    bool is_request_for_main_frame,
+    const GURL& url,
+    scoped_refptr<net::HttpResponseHeaders> response_headers,
+    bool first_auth_attempt,
+    LoginAuthRequiredCallback auth_required_callback) {
+  return std::make_unique<net_service::LoginDelegate>(
+      auth_info, web_contents, request_id, url,
+      std::move(auth_required_callback));
+}
+
+void CefContentBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
+    int frame_tree_node_id,
+    NonNetworkURLLoaderFactoryMap* factories) {
+  if (!extensions::ExtensionsEnabled())
+    return;
+
+  content::WebContents* web_contents =
+      content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
+  factories->emplace(
+      extensions::kExtensionScheme,
+      extensions::CreateExtensionNavigationURLLoaderFactory(
+          web_contents->GetBrowserContext(),
+          !!extensions::WebViewGuest::FromWebContents(web_contents)));
+}
+
+void CefContentBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
+    int render_process_id,
+    int render_frame_id,
+    NonNetworkURLLoaderFactoryMap* factories) {
+  if (!extensions::ExtensionsEnabled())
+    return;
+
+  auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
+                                                             render_frame_id);
+  if (factory)
+    factories->emplace(extensions::kExtensionScheme, std::move(factory));
+
+  content::RenderFrameHost* frame_host =
+      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(frame_host);
+  if (!web_contents)
+    return;
+
+  extensions::CefExtensionWebContentsObserver* web_observer =
+      extensions::CefExtensionWebContentsObserver::FromWebContents(
+          web_contents);
+
+  // There is nothing to do if no CefExtensionWebContentsObserver is attached
+  // to the |web_contents|.
+  if (!web_observer)
+    return;
+
+  const extensions::Extension* extension =
+      web_observer->GetExtensionFromFrame(frame_host, false);
+  if (!extension)
+    return;
+
+  std::vector<std::string> allowed_webui_hosts;
+  // Support for chrome:// scheme if appropriate.
+  if ((extension->is_extension() || extension->is_platform_app()) &&
+      extensions::Manifest::IsComponentLocation(extension->location())) {
+    // Components of chrome that are implemented as extensions or platform apps
+    // are allowed to use chrome://resources/ and chrome://theme/ URLs.
+    allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost);
+    allowed_webui_hosts.emplace_back(chrome::kChromeUIThemeHost);
+  }
+  if (!allowed_webui_hosts.empty()) {
+    factories->emplace(
+        content::kChromeUIScheme,
+        content::CreateWebUIURLLoader(frame_host, content::kChromeUIScheme,
+                                      std::move(allowed_webui_hosts)));
+  }
+}
+
+bool CefContentBrowserClient::WillCreateURLLoaderFactory(
+    content::BrowserContext* browser_context,
+    content::RenderFrameHost* frame,
+    int render_process_id,
+    URLLoaderFactoryType type,
+    const url::Origin& request_initiator,
+    base::Optional<int64_t> navigation_id,
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
+    mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
+        header_client,
+    bool* bypass_redirect_checks,
+    bool* disable_secure_dns,
+    network::mojom::URLLoaderFactoryOverridePtr* factory_override) {
+  auto request_handler = net_service::CreateInterceptedRequestHandler(
+      browser_context, frame, render_process_id,
+      type == URLLoaderFactoryType::kNavigation,
+      type == URLLoaderFactoryType::kDownload, request_initiator);
+
+  net_service::ProxyURLLoaderFactory::CreateProxy(
+      browser_context, factory_receiver, header_client,
+      std::move(request_handler));
+  return true;
+}
+
+void CefContentBrowserClient::OnNetworkServiceCreated(
+    network::mojom::NetworkService* network_service) {
+  DCHECK(g_browser_process);
+  PrefService* local_state = g_browser_process->local_state();
+  DCHECK(local_state);
+
+  // Need to set up global NetworkService state before anything else uses it.
+  DCHECK(SystemNetworkContextManager::GetInstance());
+  SystemNetworkContextManager::GetInstance()->OnNetworkServiceCreated(
+      network_service);
+}
+
+mojo::Remote<network::mojom::NetworkContext>
+CefContentBrowserClient::CreateNetworkContext(
+    content::BrowserContext* context,
+    bool in_memory,
+    const base::FilePath& relative_partition_path) {
+  // This method may be called during shutdown when using multi-threaded
+  // message loop mode. In that case exit early to avoid crashes.
+  if (!SystemNetworkContextManager::GetInstance())
+    return mojo::Remote<network::mojom::NetworkContext>();
+
+  Profile* profile = Profile::FromBrowserContext(context);
+  return profile->CreateNetworkContext(in_memory, relative_partition_path);
+}
+
+// The sandbox may block read/write access from the NetworkService to
+// directories that are not returned by this method.
+std::vector<base::FilePath>
+CefContentBrowserClient::GetNetworkContextsParentDirectory() {
+  base::FilePath user_data_path;
+  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_path);
+  DCHECK(!user_data_path.empty());
+
+  const auto& root_cache_path = GetRootCachePath();
+
+  // root_cache_path may sometimes be empty or a child of user_data_path, so
+  // only return the one path in that case.
+  if (root_cache_path.empty() || user_data_path.IsParent(root_cache_path)) {
+    return {user_data_path};
+  }
+
+  return {user_data_path, root_cache_path};
+}
+
+bool CefContentBrowserClient::HandleExternalProtocol(
+    const GURL& url,
+    base::OnceCallback<content::WebContents*()> web_contents_getter,
+    int child_id,
+    content::NavigationUIData* navigation_data,
+    bool is_main_frame,
+    ui::PageTransition page_transition,
+    bool has_user_gesture,
+    const base::Optional<url::Origin>& initiating_origin,
+    mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
+  // Call the other HandleExternalProtocol variant.
+  return false;
+}
+
+bool CefContentBrowserClient::HandleExternalProtocol(
+    content::WebContents::Getter web_contents_getter,
+    int frame_tree_node_id,
+    content::NavigationUIData* navigation_data,
+    const network::ResourceRequest& resource_request,
+    mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
+  mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver =
+      out_factory->InitWithNewPipeAndPassReceiver();
+  // CefBrowserPlatformDelegate::HandleExternalProtocol may be called if
+  // nothing handles the request.
+  if (CEF_CURRENTLY_ON_IOT()) {
+    auto request_handler = net_service::CreateInterceptedRequestHandler(
+        web_contents_getter, frame_tree_node_id, resource_request);
+    net_service::ProxyURLLoaderFactory::CreateProxy(
+        web_contents_getter, std::move(receiver), std::move(request_handler));
+  } else {
+    auto request_handler = net_service::CreateInterceptedRequestHandler(
+        web_contents_getter, frame_tree_node_id, resource_request);
+    CEF_POST_TASK(
+        CEF_IOT,
+        base::BindOnce(
+            [](mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
+               std::unique_ptr<net_service::InterceptedRequestHandler>
+                   request_handler,
+               content::WebContents::Getter web_contents_getter) {
+              // Manages its own lifetime.
+
+              net_service::ProxyURLLoaderFactory::CreateProxy(
+                  web_contents_getter, std::move(receiver),
+                  std::move(request_handler));
+            },
+            std::move(receiver), std::move(request_handler),
+            std::move(web_contents_getter)));
+  }
+  return true;
+}
+
+std::unique_ptr<content::OverlayWindow>
+CefContentBrowserClient::CreateWindowForPictureInPicture(
+    content::PictureInPictureWindowController* controller) {
+  // Note: content::OverlayWindow::Create() is defined by platform-specific
+  // implementation in chrome/browser/ui/views. This layering hack, which goes
+  // through //content and ContentBrowserClient, allows us to work around the
+  // dependency constraints that disallow directly calling
+  // chrome/browser/ui/views code either from here or from other code in
+  // chrome/browser.
+  return content::OverlayWindow::Create(controller);
+}
+
+void CefContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
+    content::RenderFrameHost* render_frame_host,
+    service_manager::BinderMapWithContext<content::RenderFrameHost*>* map) {
+  PopulateChromeFrameBinders(map);
+
+  if (!extensions::ExtensionsEnabled())
+    return;
+
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host);
+  if (!web_contents)
+    return;
+
+  const GURL& site = render_frame_host->GetSiteInstance()->GetSiteURL();
+  if (!site.SchemeIs(extensions::kExtensionScheme))
+    return;
+
+  content::BrowserContext* browser_context =
+      render_frame_host->GetProcess()->GetBrowserContext();
+  auto* extension = extensions::ExtensionRegistry::Get(browser_context)
+                        ->enabled_extensions()
+                        .GetByID(site.host());
+  if (!extension)
+    return;
+  extensions::ExtensionsBrowserClient::Get()
+      ->RegisterBrowserInterfaceBindersForFrame(map, render_frame_host,
+                                                extension);
+}
+
+base::FilePath
+CefContentBrowserClient::GetSandboxedStorageServiceDataDirectory() {
+  return GetRootCachePath();
+}
+
+std::string CefContentBrowserClient::GetProduct() {
+  // Match the logic in chrome_content_browser_client.cc GetProduct().
+  return ::GetProduct();
+}
+
+std::string CefContentBrowserClient::GetChromeProduct() {
+  return version_info::GetProductNameAndVersionForUserAgent();
+}
+
+std::string CefContentBrowserClient::GetUserAgent() {
+  // Match the logic in chrome_content_browser_client.cc GetUserAgent().
+  return ::GetUserAgent();
+}
+
+blink::UserAgentMetadata CefContentBrowserClient::GetUserAgentMetadata() {
+  blink::UserAgentMetadata metadata;
+
+  metadata.brand = version_info::GetProductName();
+  metadata.full_version = version_info::GetVersionNumber();
+  metadata.platform = version_info::GetOSType();
+
+  // TODO(mkwst): Poke at BuildUserAgentFromProduct to split out these pieces.
+  metadata.architecture = "";
+  metadata.model = "";
+
+  return metadata;
+}
+
+base::flat_set<std::string>
+CefContentBrowserClient::GetPluginMimeTypesWithExternalHandlers(
+    content::BrowserContext* browser_context) {
+  base::flat_set<std::string> mime_types;
+  auto map = PluginUtils::GetMimeTypeToExtensionIdMap(browser_context);
+  for (const auto& pair : map)
+    mime_types.insert(pair.first);
+  return mime_types;
+}
+
+CefRefPtr<CefRequestContextImpl> CefContentBrowserClient::request_context()
+    const {
+  return browser_main_parts_->request_context();
+}
+
+CefDevToolsDelegate* CefContentBrowserClient::devtools_delegate() const {
+  return browser_main_parts_->devtools_delegate();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+CefContentBrowserClient::background_task_runner() const {
+  return browser_main_parts_->background_task_runner();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+CefContentBrowserClient::user_visible_task_runner() const {
+  return browser_main_parts_->user_visible_task_runner();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+CefContentBrowserClient::user_blocking_task_runner() const {
+  return browser_main_parts_->user_blocking_task_runner();
+}
+
+const extensions::Extension* CefContentBrowserClient::GetExtension(
+    content::SiteInstance* site_instance) {
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(site_instance->GetBrowserContext());
+  if (!registry)
+    return nullptr;
+  return registry->enabled_extensions().GetExtensionOrAppByURL(
+      site_instance->GetSiteURL());
+}
diff --git a/src/libcef/browser/content_browser_client.h b/src/libcef/browser/content_browser_client.h
new file mode 100644
index 0000000..a673791
--- /dev/null
+++ b/src/libcef/browser/content_browser_client.h
@@ -0,0 +1,230 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_CONTENT_BROWSER_CLIENT_H_
+#define CEF_LIBCEF_BROWSER_CONTENT_BROWSER_CLIENT_H_
+#pragma once
+
+#include <string>
+#include <utility>
+
+#include "include/cef_request_context_handler.h"
+#include "libcef/browser/request_context_impl.h"
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "build/build_config.h"
+#include "content/public/browser/content_browser_client.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+class CefBrowserMainParts;
+class CefDevToolsDelegate;
+
+namespace content {
+class PluginServiceFilter;
+class SiteInstance;
+}  // namespace content
+
+namespace extensions {
+class Extension;
+}
+
+class CefContentBrowserClient : public content::ContentBrowserClient {
+ public:
+  CefContentBrowserClient();
+  ~CefContentBrowserClient() override;
+
+  // Returns the singleton CefContentBrowserClient instance.
+  static CefContentBrowserClient* Get();
+
+  // ContentBrowserClient implementation.
+  std::unique_ptr<content::BrowserMainParts> CreateBrowserMainParts(
+      const content::MainFunctionParams& parameters) override;
+  void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
+  bool ShouldUseProcessPerSite(content::BrowserContext* browser_context,
+                               const GURL& effective_url) override;
+  bool DoesSiteRequireDedicatedProcess(content::BrowserContext* browser_context,
+                                       const GURL& effective_site_url) override;
+  void OverrideURLLoaderFactoryParams(
+      content::BrowserContext* browser_context,
+      const url::Origin& origin,
+      bool is_for_isolated_world,
+      network::mojom::URLLoaderFactoryParams* factory_params) override;
+  void GetAdditionalWebUISchemes(
+      std::vector<std::string>* additional_schemes) override;
+  void GetAdditionalViewSourceSchemes(
+      std::vector<std::string>* additional_schemes) override;
+  void GetAdditionalAllowedSchemesForFileSystem(
+      std::vector<std::string>* additional_allowed_schemes) override;
+  bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) override;
+  bool IsHandledURL(const GURL& url) override;
+  void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
+  void SiteInstanceDeleting(content::SiteInstance* site_instance) override;
+  void BindHostReceiverForRenderer(
+      content::RenderProcessHost* render_process_host,
+      mojo::GenericPendingReceiver receiver) override;
+  base::Optional<service_manager::Manifest> GetServiceManifestOverlay(
+      base::StringPiece name) override;
+  void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
+                                      int child_process_id) override;
+  std::string GetApplicationLocale() override;
+  scoped_refptr<network::SharedURLLoaderFactory>
+  GetSystemSharedURLLoaderFactory() override;
+  network::mojom::NetworkContext* GetSystemNetworkContext() override;
+  scoped_refptr<content::QuotaPermissionContext> CreateQuotaPermissionContext()
+      override;
+  content::MediaObserver* GetMediaObserver() override;
+  content::SpeechRecognitionManagerDelegate*
+  CreateSpeechRecognitionManagerDelegate() override;
+  content::GeneratedCodeCacheSettings GetGeneratedCodeCacheSettings(
+      content::BrowserContext* context) override;
+  void AllowCertificateError(
+      content::WebContents* web_contents,
+      int cert_error,
+      const net::SSLInfo& ssl_info,
+      const GURL& request_url,
+      bool is_main_frame_request,
+      bool strict_enforcement,
+      base::OnceCallback<void(content::CertificateRequestResultType)> callback)
+      override;
+  base::OnceClosure SelectClientCertificate(
+      content::WebContents* web_contents,
+      net::SSLCertRequestInfo* cert_request_info,
+      net::ClientCertIdentityList client_certs,
+      std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
+  bool CanCreateWindow(content::RenderFrameHost* opener,
+                       const GURL& opener_url,
+                       const GURL& opener_top_level_frame_url,
+                       const url::Origin& source_origin,
+                       content::mojom::WindowContainerType container_type,
+                       const GURL& target_url,
+                       const content::Referrer& referrer,
+                       const std::string& frame_name,
+                       WindowOpenDisposition disposition,
+                       const blink::mojom::WindowFeatures& features,
+                       bool user_gesture,
+                       bool opener_suppressed,
+                       bool* no_javascript_access) override;
+  void OverrideWebkitPrefs(content::RenderViewHost* rvh,
+                           content::WebPreferences* prefs) override;
+  void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) override;
+  std::string GetDefaultDownloadName() override;
+  void DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) override;
+  content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
+  std::vector<std::unique_ptr<content::NavigationThrottle>>
+  CreateThrottlesForNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+  CreateURLLoaderThrottles(
+      const network::ResourceRequest& request,
+      content::BrowserContext* browser_context,
+      const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+      content::NavigationUIData* navigation_ui_data,
+      int frame_tree_node_id) override;
+
+#if defined(OS_LINUX)
+  void GetAdditionalMappedFilesForChildProcess(
+      const base::CommandLine& command_line,
+      int child_process_id,
+      content::PosixFileDescriptorInfo* mappings) override;
+#endif
+
+#if defined(OS_WIN)
+  const wchar_t* GetResourceDllName();
+  bool PreSpawnRenderer(sandbox::TargetPolicy* policy,
+                        RendererSpawnFlags flags) override;
+#endif
+
+  void ExposeInterfacesToRenderer(
+      service_manager::BinderRegistry* registry,
+      blink::AssociatedInterfaceRegistry* associated_registry,
+      content::RenderProcessHost* render_process_host) override;
+  std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
+      content::BrowserContext* browser_context) override;
+  std::unique_ptr<content::LoginDelegate> CreateLoginDelegate(
+      const net::AuthChallengeInfo& auth_info,
+      content::WebContents* web_contents,
+      const content::GlobalRequestID& request_id,
+      bool is_request_for_main_frame,
+      const GURL& url,
+      scoped_refptr<net::HttpResponseHeaders> response_headers,
+      bool first_auth_attempt,
+      LoginAuthRequiredCallback auth_required_callback) override;
+  void RegisterNonNetworkNavigationURLLoaderFactories(
+      int frame_tree_node_id,
+      NonNetworkURLLoaderFactoryMap* factories) override;
+  void RegisterNonNetworkSubresourceURLLoaderFactories(
+      int render_process_id,
+      int render_frame_id,
+      NonNetworkURLLoaderFactoryMap* factories) override;
+  bool WillCreateURLLoaderFactory(
+      content::BrowserContext* browser_context,
+      content::RenderFrameHost* frame,
+      int render_process_id,
+      URLLoaderFactoryType type,
+      const url::Origin& request_initiator,
+      base::Optional<int64_t> navigation_id,
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
+      mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
+          header_client,
+      bool* bypass_redirect_checks,
+      bool* disable_secure_dns,
+      network::mojom::URLLoaderFactoryOverridePtr* factory_override) override;
+  void OnNetworkServiceCreated(
+      network::mojom::NetworkService* network_service) override;
+  mojo::Remote<network::mojom::NetworkContext> CreateNetworkContext(
+      content::BrowserContext* context,
+      bool in_memory,
+      const base::FilePath& relative_partition_path) override;
+  std::vector<base::FilePath> GetNetworkContextsParentDirectory() override;
+  bool HandleExternalProtocol(
+      const GURL& url,
+      base::OnceCallback<content::WebContents*()> web_contents_getter,
+      int child_id,
+      content::NavigationUIData* navigation_data,
+      bool is_main_frame,
+      ui::PageTransition page_transition,
+      bool has_user_gesture,
+      const base::Optional<url::Origin>& initiating_origin,
+      mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory)
+      override;
+  bool HandleExternalProtocol(
+      content::WebContents::Getter web_contents_getter,
+      int frame_tree_node_id,
+      content::NavigationUIData* navigation_data,
+      const network::ResourceRequest& request,
+      mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory)
+      override;
+  std::unique_ptr<content::OverlayWindow> CreateWindowForPictureInPicture(
+      content::PictureInPictureWindowController* controller) override;
+  void RegisterBrowserInterfaceBindersForFrame(
+      content::RenderFrameHost* render_frame_host,
+      service_manager::BinderMapWithContext<content::RenderFrameHost*>* map)
+      override;
+  base::FilePath GetSandboxedStorageServiceDataDirectory() override;
+  std::string GetProduct() override;
+  std::string GetChromeProduct() override;
+  std::string GetUserAgent() override;
+  blink::UserAgentMetadata GetUserAgentMetadata() override;
+  base::flat_set<std::string> GetPluginMimeTypesWithExternalHandlers(
+      content::BrowserContext* browser_context) override;
+
+  CefRefPtr<CefRequestContextImpl> request_context() const;
+  CefDevToolsDelegate* devtools_delegate() const;
+
+  scoped_refptr<base::SingleThreadTaskRunner> background_task_runner() const;
+  scoped_refptr<base::SingleThreadTaskRunner> user_visible_task_runner() const;
+  scoped_refptr<base::SingleThreadTaskRunner> user_blocking_task_runner() const;
+
+ private:
+  // Returns the extension or app associated with |site_instance| or NULL.
+  const extensions::Extension* GetExtension(
+      content::SiteInstance* site_instance);
+
+  CefBrowserMainParts* browser_main_parts_;
+
+  std::unique_ptr<content::PluginServiceFilter> plugin_service_filter_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_CONTENT_BROWSER_CLIENT_H_
diff --git a/src/libcef/browser/context.cc b/src/libcef/browser/context.cc
new file mode 100644
index 0000000..5fd2138
--- /dev/null
+++ b/src/libcef/browser/context.cc
@@ -0,0 +1,715 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/context.h"
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/browser_info.h"
+#include "libcef/browser/browser_info_manager.h"
+#include "libcef/browser/browser_main.h"
+#include "libcef/browser/chrome_browser_process_stub.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/trace_subscriber.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/main_delegate.h"
+#include "libcef/common/widevine_loader.h"
+#include "libcef/renderer/content_renderer_client.h"
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/files/file_util.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_restrictions.h"
+#include "components/network_session_configurator/common/network_switches.h"
+#include "content/app/content_service_manager_main_delegate.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_switches.h"
+#include "services/service_manager/embedder/main.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_switches.h"
+
+#if defined(OS_WIN)
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/chrome_elf/chrome_elf_main.h"
+#include "chrome/install_static/initialize_from_primary_module.h"
+#include "components/crash/core/app/crashpad.h"
+#include "content/public/app/sandbox_helper_win.h"
+#include "sandbox/win/src/sandbox_types.h"
+#endif
+
+#if defined(OS_MACOSX) || defined(OS_WIN)
+#include "components/crash/core/app/crash_switches.h"
+#include "third_party/crashpad/crashpad/handler/handler_main.h"
+#endif
+
+namespace {
+
+CefContext* g_context = nullptr;
+
+#if DCHECK_IS_ON()
+// When the process terminates check if CefShutdown() has been called.
+class CefShutdownChecker {
+ public:
+  ~CefShutdownChecker() { DCHECK(!g_context) << "CefShutdown was not called"; }
+} g_shutdown_checker;
+#endif  // DCHECK_IS_ON()
+
+#if defined(OS_WIN)
+#if defined(ARCH_CPU_X86_64)
+// VS2013 only checks the existence of FMA3 instructions, not the enabled-ness
+// of them at the OS level (this is fixed in VS2015). We force off usage of
+// FMA3 instructions in the CRT to avoid using that path and hitting illegal
+// instructions when running on CPUs that support FMA3, but OSs that don't.
+void DisableFMA3() {
+  static bool disabled = false;
+  if (disabled)
+    return;
+  disabled = true;
+  _set_FMA3_enable(0);
+}
+#endif  // defined(ARCH_CPU_X86_64)
+
+// Transfer state from chrome_elf.dll to the libcef.dll. Accessed when
+// loading chrome://system.
+void InitInstallDetails() {
+  static bool initialized = false;
+  if (initialized)
+    return;
+  initialized = true;
+  install_static::InitializeFromPrimaryModule();
+}
+
+// Signal chrome_elf to initialize crash reporting, rather than doing it in
+// DllMain. See https://crbug.com/656800 for details.
+void InitCrashReporter() {
+  static bool initialized = false;
+  if (initialized)
+    return;
+  initialized = true;
+  SignalInitializeCrashReporting();
+}
+#endif  // defined(OS_WIN)
+
+#if defined(OS_MACOSX) || defined(OS_WIN)
+
+// Based on components/crash/core/app/run_as_crashpad_handler_win.cc
+// Remove the "--type=crashpad-handler" command-line flag that will otherwise
+// confuse the crashpad handler.
+// Chrome uses an embedded crashpad handler on Windows only and imports this
+// function via the existing "run_as_crashpad_handler" target defined in
+// components/crash/core/app/BUILD.gn. CEF uses an embedded handler on both
+// Windows and macOS so we define the function here instead of using the
+// existing target (because we can't use that target on macOS).
+int RunAsCrashpadHandler(const base::CommandLine& command_line) {
+  base::CommandLine::StringVector argv = command_line.argv();
+  const base::CommandLine::StringType process_type =
+      FILE_PATH_LITERAL("--type=");
+  argv.erase(
+      std::remove_if(argv.begin(), argv.end(),
+                     [&process_type](const base::CommandLine::StringType& str) {
+                       return base::StartsWith(str, process_type,
+                                               base::CompareCase::SENSITIVE) ||
+                              (!str.empty() && str[0] == L'/');
+                     }),
+      argv.end());
+
+#if defined(OS_MACOSX)
+  // HandlerMain on macOS uses the system version of getopt_long which expects
+  // the first argument to be the program name.
+  argv.insert(argv.begin(), command_line.GetProgram().value());
+#endif
+
+  std::unique_ptr<char*[]> argv_as_utf8(new char*[argv.size() + 1]);
+  std::vector<std::string> storage;
+  storage.reserve(argv.size());
+  for (size_t i = 0; i < argv.size(); ++i) {
+#if defined(OS_WIN)
+    storage.push_back(base::UTF16ToUTF8(argv[i]));
+#else
+    storage.push_back(argv[i]);
+#endif
+    argv_as_utf8[i] = &storage[i][0];
+  }
+  argv_as_utf8[argv.size()] = nullptr;
+  argv.clear();
+  return crashpad::HandlerMain(static_cast<int>(storage.size()),
+                               argv_as_utf8.get(), nullptr);
+}
+
+#endif  // defined(OS_MACOSX) || defined(OS_WIN)
+
+bool GetColor(const cef_color_t cef_in, bool is_windowless, SkColor* sk_out) {
+  // Windowed browser colors must be fully opaque.
+  if (!is_windowless && CefColorGetA(cef_in) != SK_AlphaOPAQUE)
+    return false;
+
+  // Windowless browser colors may be fully transparent.
+  if (is_windowless && CefColorGetA(cef_in) == SK_AlphaTRANSPARENT) {
+    *sk_out = SK_ColorTRANSPARENT;
+    return true;
+  }
+
+  // Ignore the alpha component.
+  *sk_out = SkColorSetRGB(CefColorGetR(cef_in), CefColorGetG(cef_in),
+                          CefColorGetB(cef_in));
+  return true;
+}
+
+// Convert |path_str| to a normalized FilePath.
+base::FilePath NormalizePath(const cef_string_t& path_str,
+                             const char* name,
+                             bool* has_error = nullptr) {
+  if (has_error)
+    *has_error = false;
+
+  base::FilePath path = base::FilePath(CefString(&path_str));
+  if (path.EndsWithSeparator()) {
+    // Remove the trailing separator because it will interfere with future
+    // equality checks.
+    path = path.StripTrailingSeparators();
+  }
+
+  if (!path.empty() && !path.IsAbsolute()) {
+    LOG(ERROR) << "The " << name << " directory (" << path.value()
+               << ") is not an absolute path. Defaulting to empty.";
+    if (has_error)
+      *has_error = true;
+    path = base::FilePath();
+  }
+
+  return path;
+}
+
+void SetPath(cef_string_t& path_str, const base::FilePath& path) {
+#if defined(OS_WIN)
+  CefString(&path_str).FromWString(path.value());
+#else
+  CefString(&path_str).FromString(path.value());
+#endif
+}
+
+// Convert |path_str| to a normalized FilePath and update the |path_str| value.
+base::FilePath NormalizePathAndSet(cef_string_t& path_str, const char* name) {
+  const base::FilePath& path = NormalizePath(path_str, name);
+  SetPath(path_str, path);
+  return path;
+}
+
+// Verify that |cache_path| is valid and create it if necessary.
+bool ValidateCachePath(const base::FilePath& cache_path,
+                       const base::FilePath& root_cache_path) {
+  if (cache_path.empty())
+    return true;
+
+  if (!root_cache_path.empty() && root_cache_path != cache_path &&
+      !root_cache_path.IsParent(cache_path)) {
+    LOG(ERROR) << "The cache_path directory (" << cache_path.value()
+               << ") is not a child of the root_cache_path directory ("
+               << root_cache_path.value() << ")";
+    return false;
+  }
+
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+  if (!base::DirectoryExists(cache_path) &&
+      !base::CreateDirectory(cache_path)) {
+    LOG(ERROR) << "The cache_path directory (" << cache_path.value()
+               << ") could not be created.";
+    return false;
+  }
+
+  return true;
+}
+
+// Like NormalizePathAndSet but with additional checks specific to the
+// cache_path value.
+base::FilePath NormalizeCachePathAndSet(cef_string_t& path_str,
+                                        const base::FilePath& root_cache_path) {
+  bool has_error = false;
+  base::FilePath path = NormalizePath(path_str, "cache_path", &has_error);
+  if (has_error || !ValidateCachePath(path, root_cache_path)) {
+    LOG(ERROR) << "The cache_path is invalid. Defaulting to in-memory storage.";
+    path = base::FilePath();
+  }
+  SetPath(path_str, path);
+  return path;
+}
+
+}  // namespace
+
+int CefExecuteProcess(const CefMainArgs& args,
+                      CefRefPtr<CefApp> application,
+                      void* windows_sandbox_info) {
+#if defined(OS_WIN)
+#if defined(ARCH_CPU_X86_64)
+  DisableFMA3();
+#endif
+  InitInstallDetails();
+  InitCrashReporter();
+#endif
+
+  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+#if defined(OS_WIN)
+  command_line.ParseFromString(::GetCommandLineW());
+#else
+  command_line.InitFromArgv(args.argc, args.argv);
+#endif
+
+  // Wait for the debugger as early in process initialization as possible.
+  if (command_line.HasSwitch(switches::kWaitForDebugger))
+    base::debug::WaitForDebugger(60, true);
+
+  // If no process type is specified then it represents the browser process and
+  // we do nothing.
+  std::string process_type =
+      command_line.GetSwitchValueASCII(switches::kProcessType);
+  if (process_type.empty())
+    return -1;
+
+#if defined(OS_MACOSX) || defined(OS_WIN)
+  if (process_type == crash_reporter::switches::kCrashpadHandler)
+    return RunAsCrashpadHandler(command_line);
+#endif
+
+  CefMainDelegate main_delegate(application);
+
+// Execute the secondary process.
+#if defined(OS_WIN)
+  sandbox::SandboxInterfaceInfo sandbox_info = {0};
+  if (windows_sandbox_info == nullptr) {
+    content::InitializeSandboxInfo(&sandbox_info);
+    windows_sandbox_info = &sandbox_info;
+  }
+
+  content::ContentMainParams params(&main_delegate);
+  params.instance = args.instance;
+  params.sandbox_info =
+      static_cast<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
+
+  return content::ContentMain(params);
+#else
+  content::ContentMainParams params(&main_delegate);
+  params.argc = args.argc;
+  params.argv = const_cast<const char**>(args.argv);
+
+  return content::ContentMain(params);
+#endif
+}
+
+bool CefInitialize(const CefMainArgs& args,
+                   const CefSettings& settings,
+                   CefRefPtr<CefApp> application,
+                   void* windows_sandbox_info) {
+#if defined(OS_WIN)
+#if defined(ARCH_CPU_X86_64)
+  DisableFMA3();
+#endif
+  InitInstallDetails();
+  InitCrashReporter();
+#endif
+
+  // Return true if the global context already exists.
+  if (g_context)
+    return true;
+
+  if (settings.size != sizeof(cef_settings_t)) {
+    NOTREACHED() << "invalid CefSettings structure size";
+    return false;
+  }
+
+  g_browser_process = new ChromeBrowserProcessStub();
+
+  // Create the new global context object.
+  g_context = new CefContext();
+
+  // Initialize the global context.
+  return g_context->Initialize(args, settings, application,
+                               windows_sandbox_info);
+}
+
+void CefShutdown() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  // Must always be called on the same thread as Initialize.
+  if (!g_context->OnInitThread()) {
+    NOTREACHED() << "called on invalid thread";
+    return;
+  }
+
+  // Shut down the global context. This will block until shutdown is complete.
+  g_context->Shutdown();
+
+  // Delete the global context object.
+  delete g_context;
+  g_context = nullptr;
+}
+
+void CefDoMessageLoopWork() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  // Must always be called on the same thread as Initialize.
+  if (!g_context->OnInitThread()) {
+    NOTREACHED() << "called on invalid thread";
+    return;
+  }
+
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+}
+
+void CefRunMessageLoop() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  // Must always be called on the same thread as Initialize.
+  if (!g_context->OnInitThread()) {
+    NOTREACHED() << "called on invalid thread";
+    return;
+  }
+
+  base::RunLoop run_loop;
+  run_loop.Run();
+}
+
+void CefQuitMessageLoop() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  // Must always be called on the same thread as Initialize.
+  if (!g_context->OnInitThread()) {
+    NOTREACHED() << "called on invalid thread";
+    return;
+  }
+
+  // Quit the CefBrowserMessageLoop.
+  base::RunLoop::QuitCurrentWhenIdleDeprecated();
+}
+
+void CefSetOSModalLoop(bool osModalLoop) {
+#if defined(OS_WIN)
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  if (CEF_CURRENTLY_ON_UIT())
+    base::MessageLoopCurrent::Get()->set_os_modal_loop(osModalLoop);
+  else
+    CEF_POST_TASK(CEF_UIT, base::Bind(CefSetOSModalLoop, osModalLoop));
+#endif  // defined(OS_WIN)
+}
+
+// CefContext
+
+CefContext::CefContext()
+    : initialized_(false), shutting_down_(false), init_thread_id_(0) {}
+
+CefContext::~CefContext() {}
+
+// static
+CefContext* CefContext::Get() {
+  return g_context;
+}
+
+bool CefContext::Initialize(const CefMainArgs& args,
+                            const CefSettings& settings,
+                            CefRefPtr<CefApp> application,
+                            void* windows_sandbox_info) {
+  init_thread_id_ = base::PlatformThread::CurrentId();
+  settings_ = settings;
+
+#if !(defined(OS_WIN) || defined(OS_LINUX))
+  if (settings.multi_threaded_message_loop) {
+    NOTIMPLEMENTED() << "multi_threaded_message_loop is not supported.";
+    return false;
+  }
+#endif
+
+#if defined(OS_WIN)
+  // Signal Chrome Elf that Chrome has begun to start.
+  SignalChromeElf();
+#endif
+
+  const base::FilePath& root_cache_path =
+      NormalizePathAndSet(settings_.root_cache_path, "root_cache_path");
+  const base::FilePath& cache_path =
+      NormalizeCachePathAndSet(settings_.cache_path, root_cache_path);
+  if (root_cache_path.empty() && !cache_path.empty()) {
+    CefString(&settings_.root_cache_path) = cache_path.value();
+  }
+
+  // All other paths that need to be normalized.
+  NormalizePathAndSet(settings_.browser_subprocess_path,
+                      "browser_subprocess_path");
+  NormalizePathAndSet(settings_.framework_dir_path, "framework_dir_path");
+  NormalizePathAndSet(settings_.main_bundle_path, "main_bundle_path");
+  NormalizePathAndSet(settings_.user_data_path, "user_data_path");
+  NormalizePathAndSet(settings_.resources_dir_path, "resources_dir_path");
+  NormalizePathAndSet(settings_.locales_dir_path, "locales_dir_path");
+
+  main_delegate_.reset(new CefMainDelegate(application));
+  browser_info_manager_.reset(new CefBrowserInfoManager);
+
+  int exit_code;
+
+  // Initialize the content runner.
+  content::ContentMainParams params(main_delegate_.get());
+#if defined(OS_WIN)
+  sandbox::SandboxInterfaceInfo sandbox_info = {0};
+  if (windows_sandbox_info == nullptr) {
+    windows_sandbox_info = &sandbox_info;
+    settings_.no_sandbox = true;
+  }
+
+  params.instance = args.instance;
+  params.sandbox_info =
+      static_cast<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
+#else
+  params.argc = args.argc;
+  params.argv = const_cast<const char**>(args.argv);
+#endif
+
+  sm_main_delegate_.reset(
+      new content::ContentServiceManagerMainDelegate(params));
+  sm_main_params_.reset(
+      new service_manager::MainParams(sm_main_delegate_.get()));
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+  sm_main_params_->argc = params.argc;
+  sm_main_params_->argv = params.argv;
+#endif
+
+  exit_code = service_manager::MainInitialize(*sm_main_params_);
+  DCHECK_LT(exit_code, 0);
+  if (exit_code >= 0)
+    return false;
+
+  static_cast<ChromeBrowserProcessStub*>(g_browser_process)->Initialize();
+
+  if (settings.multi_threaded_message_loop) {
+    base::WaitableEvent uithread_startup_event(
+        base::WaitableEvent::ResetPolicy::AUTOMATIC,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+    if (!main_delegate_->CreateUIThread(base::BindOnce(
+            [](CefContext* context, base::WaitableEvent* event) {
+              service_manager::MainRun(*context->sm_main_params_);
+              event->Signal();
+            },
+            base::Unretained(this),
+            base::Unretained(&uithread_startup_event)))) {
+      return false;
+    }
+
+    initialized_ = true;
+
+    // We need to wait until service_manager::MainRun has finished.
+    uithread_startup_event.Wait();
+  } else {
+    initialized_ = true;
+    service_manager::MainRun(*sm_main_params_);
+  }
+
+  if (CEF_CURRENTLY_ON_UIT()) {
+    OnContextInitialized();
+  } else {
+    // Continue initialization on the UI thread.
+    CEF_POST_TASK(CEF_UIT, base::Bind(&CefContext::OnContextInitialized,
+                                      base::Unretained(this)));
+  }
+
+  return true;
+}
+
+void CefContext::Shutdown() {
+  // Must always be called on the same thread as Initialize.
+  DCHECK(OnInitThread());
+
+  shutting_down_ = true;
+
+  if (settings_.multi_threaded_message_loop) {
+    // Events that will be used to signal when shutdown is complete. Start in
+    // non-signaled mode so that the event will block.
+    base::WaitableEvent uithread_shutdown_event(
+        base::WaitableEvent::ResetPolicy::AUTOMATIC,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+    // Finish shutdown on the UI thread.
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefContext::FinishShutdownOnUIThread,
+                             base::Unretained(this), &uithread_shutdown_event));
+
+    /// Block until UI thread shutdown is complete.
+    uithread_shutdown_event.Wait();
+
+    FinalizeShutdown();
+  } else {
+    // Finish shutdown on the current thread, which should be the UI thread.
+    FinishShutdownOnUIThread(nullptr);
+
+    FinalizeShutdown();
+  }
+}
+
+bool CefContext::OnInitThread() {
+  return (base::PlatformThread::CurrentId() == init_thread_id_);
+}
+
+SkColor CefContext::GetBackgroundColor(
+    const CefBrowserSettings* browser_settings,
+    cef_state_t windowless_state) const {
+  bool is_windowless = windowless_state == STATE_ENABLED
+                           ? true
+                           : (windowless_state == STATE_DISABLED
+                                  ? false
+                                  : !!settings_.windowless_rendering_enabled);
+
+  // Default to opaque white if no acceptable color values are found.
+  SkColor sk_color = SK_ColorWHITE;
+
+  if (!browser_settings ||
+      !GetColor(browser_settings->background_color, is_windowless, &sk_color)) {
+    GetColor(settings_.background_color, is_windowless, &sk_color);
+  }
+  return sk_color;
+}
+
+CefTraceSubscriber* CefContext::GetTraceSubscriber() {
+  CEF_REQUIRE_UIT();
+  if (shutting_down_)
+    return nullptr;
+  if (!trace_subscriber_.get())
+    trace_subscriber_.reset(new CefTraceSubscriber());
+  return trace_subscriber_.get();
+}
+
+void CefContext::PopulateGlobalRequestContextSettings(
+    CefRequestContextSettings* settings) {
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+
+  // This value was already normalized in Initialize.
+  CefString(&settings->cache_path) = CefString(&settings_.cache_path);
+
+  settings->persist_session_cookies =
+      settings_.persist_session_cookies ||
+      command_line->HasSwitch(switches::kPersistSessionCookies);
+  settings->persist_user_preferences =
+      settings_.persist_user_preferences ||
+      command_line->HasSwitch(switches::kPersistUserPreferences);
+  settings->ignore_certificate_errors =
+      settings_.ignore_certificate_errors ||
+      command_line->HasSwitch(switches::kIgnoreCertificateErrors);
+  CefString(&settings->accept_language_list) =
+      CefString(&settings_.accept_language_list);
+}
+
+void CefContext::NormalizeRequestContextSettings(
+    CefRequestContextSettings* settings) {
+  // The |root_cache_path| value was already normalized in Initialize.
+  const base::FilePath& root_cache_path = CefString(&settings_.root_cache_path);
+  NormalizeCachePathAndSet(settings->cache_path, root_cache_path);
+
+  if (settings->accept_language_list.length == 0) {
+    // Use the global language list setting.
+    CefString(&settings->accept_language_list) =
+        CefString(&settings_.accept_language_list);
+  }
+}
+
+void CefContext::AddObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.AddObserver(observer);
+}
+
+void CefContext::RemoveObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.RemoveObserver(observer);
+}
+
+bool CefContext::HasObserver(Observer* observer) const {
+  CEF_REQUIRE_UIT();
+  return observers_.HasObserver(observer);
+}
+
+void CefContext::OnContextInitialized() {
+  CEF_REQUIRE_UIT();
+
+  static_cast<ChromeBrowserProcessStub*>(g_browser_process)
+      ->OnContextInitialized();
+
+#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+  CefWidevineLoader::GetInstance()->OnContextInitialized();
+#endif
+
+  // Notify the handler.
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefBrowserProcessHandler> handler =
+        app->GetBrowserProcessHandler();
+    if (handler.get())
+      handler->OnContextInitialized();
+  }
+}
+
+void CefContext::FinishShutdownOnUIThread(
+    base::WaitableEvent* uithread_shutdown_event) {
+  CEF_REQUIRE_UIT();
+
+  browser_info_manager_->DestroyAllBrowsers();
+
+  for (auto& observer : observers_)
+    observer.OnContextDestroyed();
+
+  if (trace_subscriber_.get())
+    trace_subscriber_.reset(nullptr);
+
+  static_cast<ChromeBrowserProcessStub*>(g_browser_process)->Shutdown();
+
+  ui::ResourceBundle::GetSharedInstance().CleanupOnUIThread();
+
+  sm_main_delegate_->ShutdownOnUIThread();
+
+  if (uithread_shutdown_event)
+    uithread_shutdown_event->Signal();
+}
+
+void CefContext::FinalizeShutdown() {
+  if (content::RenderProcessHost::run_renderer_in_process()) {
+    // Blocks until RenderProcess cleanup is complete.
+    CefContentRendererClient::Get()->RunSingleProcessCleanup();
+  }
+
+  // Shut down the browser runner or UI thread.
+  main_delegate_->ShutdownBrowser();
+
+  // Shut down the content runner.
+  service_manager::MainShutdown(*sm_main_params_);
+
+  browser_info_manager_.reset(nullptr);
+  sm_main_params_.reset(nullptr);
+  sm_main_delegate_.reset(nullptr);
+  main_delegate_.reset(nullptr);
+
+  delete g_browser_process;
+  g_browser_process = nullptr;
+}
diff --git a/src/libcef/browser/context.h b/src/libcef/browser/context.h
new file mode 100644
index 0000000..eacc02a
--- /dev/null
+++ b/src/libcef/browser/context.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_CONTEXT_H_
+#define CEF_LIBCEF_BROWSER_CONTEXT_H_
+#pragma once
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "include/cef_app.h"
+
+#include "base/observer_list.h"
+#include "base/threading/platform_thread.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace content {
+class ContentServiceManagerMainDelegate;
+}
+
+namespace service_manager {
+struct MainParams;
+}
+
+class CefBrowserHostImpl;
+class CefBrowserInfoManager;
+class CefMainDelegate;
+class CefTraceSubscriber;
+
+class CefContext {
+ public:
+  typedef std::list<CefRefPtr<CefBrowserHostImpl>> BrowserList;
+
+  // Interface to implement for observers that wish to be informed of changes
+  // to the context. All methods will be called on the UI thread.
+  class Observer {
+   public:
+    // Called before the context is destroyed.
+    virtual void OnContextDestroyed() = 0;
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  CefContext();
+  ~CefContext();
+
+  // Returns the singleton CefContext instance.
+  static CefContext* Get();
+
+  // These methods will be called on the main application thread.
+  bool Initialize(const CefMainArgs& args,
+                  const CefSettings& settings,
+                  CefRefPtr<CefApp> application,
+                  void* windows_sandbox_info);
+  void Shutdown();
+
+  // Returns true if the current thread is the initialization thread.
+  bool OnInitThread();
+
+  // Returns true if the context is initialized.
+  bool initialized() { return initialized_; }
+
+  // Returns true if the context is shutting down.
+  bool shutting_down() { return shutting_down_; }
+
+  const CefSettings& settings() const { return settings_; }
+
+  // Returns the background color for the browser. If |browser_settings| is
+  // nullptr or does not specify a color then the global settings will be used.
+  // The alpha component will be either SK_AlphaTRANSPARENT or SK_AlphaOPAQUE
+  // (e.g. fully transparent or fully opaque). If |is_windowless| is
+  // STATE_DISABLED then SK_AlphaTRANSPARENT will always be returned. If
+  // |is_windowless| is STATE_ENABLED then SK_ColorTRANSPARENT may be returned
+  // to enable transparency for windowless browsers. See additional comments on
+  // CefSettings.background_color and CefBrowserSettings.background_color.
+  SkColor GetBackgroundColor(const CefBrowserSettings* browser_settings,
+                             cef_state_t windowless_state) const;
+
+  CefTraceSubscriber* GetTraceSubscriber();
+
+  // Populate request context settings for the global system context based on
+  // CefSettings and command-line flags.
+  void PopulateGlobalRequestContextSettings(
+      CefRequestContextSettings* settings);
+
+  // Normalize and validate request context settings for user-created contexts.
+  void NormalizeRequestContextSettings(CefRequestContextSettings* settings);
+
+  // Manage observer objects. The observer must either outlive this object or
+  // remove itself before destruction. These methods can only be called on the
+  // UI thread.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+  bool HasObserver(Observer* observer) const;
+
+ private:
+  void OnContextInitialized();
+
+  // Performs shutdown actions that need to occur on the UI thread before any
+  // threads are destroyed.
+  void FinishShutdownOnUIThread(base::WaitableEvent* uithread_shutdown_event);
+
+  // Destroys the main runner and related objects.
+  void FinalizeShutdown();
+
+  // Track context state.
+  bool initialized_;
+  bool shutting_down_;
+
+  // The thread on which the context was initialized.
+  base::PlatformThreadId init_thread_id_;
+
+  CefSettings settings_;
+
+  std::unique_ptr<CefMainDelegate> main_delegate_;
+  std::unique_ptr<content::ContentServiceManagerMainDelegate> sm_main_delegate_;
+  std::unique_ptr<service_manager::MainParams> sm_main_params_;
+  std::unique_ptr<CefTraceSubscriber> trace_subscriber_;
+  std::unique_ptr<CefBrowserInfoManager> browser_info_manager_;
+
+  // Observers that want to be notified of changes to this object.
+  base::ObserverList<Observer>::Unchecked observers_;
+};
+
+// Helper macro that returns true if the global context is in a valid state.
+#define CONTEXT_STATE_VALID()                               \
+  (CefContext::Get() && CefContext::Get()->initialized() && \
+   !CefContext::Get()->shutting_down())
+
+#endif  // CEF_LIBCEF_BROWSER_CONTEXT_H_
diff --git a/src/libcef/browser/context_menu_params_impl.cc b/src/libcef/browser/context_menu_params_impl.cc
new file mode 100644
index 0000000..6047d1a
--- /dev/null
+++ b/src/libcef/browser/context_menu_params_impl.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/context_menu_params_impl.h"
+
+#include "base/logging.h"
+
+CefContextMenuParamsImpl::CefContextMenuParamsImpl(
+    content::ContextMenuParams* value)
+    : CefValueBase<CefContextMenuParams, content::ContextMenuParams>(
+          value,
+          nullptr,
+          kOwnerNoDelete,
+          true,
+          new CefValueControllerNonThreadSafe()) {
+  // Indicate that this object owns the controller.
+  SetOwnsController();
+}
+
+int CefContextMenuParamsImpl::GetXCoord() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().x;
+}
+
+int CefContextMenuParamsImpl::GetYCoord() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().y;
+}
+
+CefContextMenuParamsImpl::TypeFlags CefContextMenuParamsImpl::GetTypeFlags() {
+  CEF_VALUE_VERIFY_RETURN(false, CM_TYPEFLAG_NONE);
+  const content::ContextMenuParams& params = const_value();
+  int type_flags = CM_TYPEFLAG_NONE;
+  if (!params.page_url.is_empty())
+    type_flags |= CM_TYPEFLAG_PAGE;
+  if (!params.frame_url.is_empty())
+    type_flags |= CM_TYPEFLAG_FRAME;
+  if (!params.link_url.is_empty())
+    type_flags |= CM_TYPEFLAG_LINK;
+  if (params.media_type != blink::ContextMenuDataMediaType::kNone)
+    type_flags |= CM_TYPEFLAG_MEDIA;
+  if (!params.selection_text.empty())
+    type_flags |= CM_TYPEFLAG_SELECTION;
+  if (params.is_editable)
+    type_flags |= CM_TYPEFLAG_EDITABLE;
+  return static_cast<TypeFlags>(type_flags);
+}
+
+CefString CefContextMenuParamsImpl::GetLinkUrl() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().link_url.spec();
+}
+
+CefString CefContextMenuParamsImpl::GetUnfilteredLinkUrl() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().unfiltered_link_url.spec();
+}
+
+CefString CefContextMenuParamsImpl::GetSourceUrl() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().src_url.spec();
+}
+
+bool CefContextMenuParamsImpl::HasImageContents() {
+  CEF_VALUE_VERIFY_RETURN(false, true);
+  return const_value().has_image_contents;
+}
+
+CefString CefContextMenuParamsImpl::GetTitleText() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().title_text;
+}
+
+CefString CefContextMenuParamsImpl::GetPageUrl() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().page_url.spec();
+}
+
+CefString CefContextMenuParamsImpl::GetFrameUrl() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().frame_url.spec();
+}
+
+CefString CefContextMenuParamsImpl::GetFrameCharset() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().frame_charset;
+}
+
+CefContextMenuParamsImpl::MediaType CefContextMenuParamsImpl::GetMediaType() {
+  CEF_VALUE_VERIFY_RETURN(false, CM_MEDIATYPE_NONE);
+  return static_cast<MediaType>(const_value().media_type);
+}
+
+CefContextMenuParamsImpl::MediaStateFlags
+CefContextMenuParamsImpl::GetMediaStateFlags() {
+  CEF_VALUE_VERIFY_RETURN(false, CM_MEDIAFLAG_NONE);
+  return static_cast<MediaStateFlags>(const_value().media_flags);
+}
+
+CefString CefContextMenuParamsImpl::GetSelectionText() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().selection_text;
+}
+
+CefString CefContextMenuParamsImpl::GetMisspelledWord() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().misspelled_word;
+}
+
+bool CefContextMenuParamsImpl::GetDictionarySuggestions(
+    std::vector<CefString>& suggestions) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+
+  if (!suggestions.empty())
+    suggestions.clear();
+
+  if (const_value().dictionary_suggestions.empty())
+    return false;
+
+  std::vector<base::string16>::const_iterator it =
+      const_value().dictionary_suggestions.begin();
+  for (; it != const_value().dictionary_suggestions.end(); ++it)
+    suggestions.push_back(*it);
+
+  return true;
+}
+
+bool CefContextMenuParamsImpl::IsEditable() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().is_editable;
+}
+
+bool CefContextMenuParamsImpl::IsSpellCheckEnabled() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().spellcheck_enabled;
+}
+
+CefContextMenuParamsImpl::EditStateFlags
+CefContextMenuParamsImpl::GetEditStateFlags() {
+  CEF_VALUE_VERIFY_RETURN(false, CM_EDITFLAG_NONE);
+  return static_cast<EditStateFlags>(const_value().edit_flags);
+}
+
+bool CefContextMenuParamsImpl::IsCustomMenu() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return !const_value().custom_items.empty();
+}
+
+bool CefContextMenuParamsImpl::IsPepperMenu() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().custom_context.is_pepper_menu;
+}
diff --git a/src/libcef/browser/context_menu_params_impl.h b/src/libcef/browser/context_menu_params_impl.h
new file mode 100644
index 0000000..5d28efb
--- /dev/null
+++ b/src/libcef/browser/context_menu_params_impl.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_CONTEXT_MENU_PARAMS_IMPL_H_
+#define CEF_LIBCEF_BROWSER_CONTEXT_MENU_PARAMS_IMPL_H_
+#pragma once
+
+#include "include/cef_context_menu_handler.h"
+#include "libcef/common/value_base.h"
+
+#include "content/public/browser/context_menu_params.h"
+
+// CefContextMenuParams implementation. This class is not thread safe.
+class CefContextMenuParamsImpl
+    : public CefValueBase<CefContextMenuParams, content::ContextMenuParams> {
+ public:
+  explicit CefContextMenuParamsImpl(content::ContextMenuParams* value);
+
+  // CefContextMenuParams methods.
+  int GetXCoord() override;
+  int GetYCoord() override;
+  TypeFlags GetTypeFlags() override;
+  CefString GetLinkUrl() override;
+  CefString GetUnfilteredLinkUrl() override;
+  CefString GetSourceUrl() override;
+  bool HasImageContents() override;
+  CefString GetTitleText() override;
+  CefString GetPageUrl() override;
+  CefString GetFrameUrl() override;
+  CefString GetFrameCharset() override;
+  MediaType GetMediaType() override;
+  MediaStateFlags GetMediaStateFlags() override;
+  CefString GetSelectionText() override;
+  CefString GetMisspelledWord() override;
+  bool GetDictionarySuggestions(std::vector<CefString>& suggestions) override;
+  bool IsEditable() override;
+  bool IsSpellCheckEnabled() override;
+  EditStateFlags GetEditStateFlags() override;
+  bool IsCustomMenu() override;
+  bool IsPepperMenu() override;
+
+  DISALLOW_COPY_AND_ASSIGN(CefContextMenuParamsImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_CONTEXT_MENU_PARAMS_IMPL_H_
diff --git a/src/libcef/browser/devtools/devtools_controller.cc b/src/libcef/browser/devtools/devtools_controller.cc
new file mode 100644
index 0000000..9b3496e
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_controller.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/devtools/devtools_controller.h"
+
+#include "libcef/browser/devtools/devtools_util.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "content/public/browser/devtools_agent_host.h"
+
+CefDevToolsController::CefDevToolsController(
+    content::WebContents* inspected_contents)
+    : inspected_contents_(inspected_contents), weak_ptr_factory_(this) {
+  DCHECK(inspected_contents_);
+}
+
+CefDevToolsController::~CefDevToolsController() {
+  if (agent_host_) {
+    agent_host_->DetachClient(this);
+    AgentHostClosed(agent_host_.get());
+  }
+
+  for (auto& observer : observers_) {
+    observer.OnDevToolsControllerDestroyed();
+  }
+}
+
+bool CefDevToolsController::SendDevToolsMessage(
+    const base::StringPiece& message) {
+  CEF_REQUIRE_UIT();
+  if (!EnsureAgentHost())
+    return false;
+
+  agent_host_->DispatchProtocolMessage(
+      this, base::as_bytes(base::make_span(message)));
+  return true;
+}
+
+int CefDevToolsController::ExecuteDevToolsMethod(
+    int suggested_message_id,
+    const std::string& method,
+    const base::DictionaryValue* params) {
+  CEF_REQUIRE_UIT();
+  if (!EnsureAgentHost())
+    return 0;
+
+  // Message IDs must always be increasing and unique.
+  int message_id = suggested_message_id;
+  if (message_id < next_message_id_)
+    message_id = next_message_id_++;
+  else
+    next_message_id_ = message_id + 1;
+
+  base::DictionaryValue message;
+  message.SetIntKey("id", message_id);
+  message.SetStringKey("method", method);
+  if (params)
+    message.SetKey("params", params->Clone());
+
+  std::string protocol_message;
+  if (!base::JSONWriter::Write(message, &protocol_message))
+    return 0;
+
+  agent_host_->DispatchProtocolMessage(
+      this, base::as_bytes(base::make_span(protocol_message)));
+  return message_id;
+}
+
+void CefDevToolsController::AgentHostClosed(
+    content::DevToolsAgentHost* agent_host) {
+  DCHECK(agent_host == agent_host_.get());
+  agent_host_ = nullptr;
+  for (auto& observer : observers_) {
+    observer.OnDevToolsAgentDetached();
+  }
+}
+
+void CefDevToolsController::AddObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.AddObserver(observer);
+}
+
+void CefDevToolsController::RemoveObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.RemoveObserver(observer);
+}
+
+void CefDevToolsController::DispatchProtocolMessage(
+    content::DevToolsAgentHost* agent_host,
+    base::span<const uint8_t> message) {
+  if (!observers_.might_have_observers())
+    return;
+
+  base::StringPiece str_message(reinterpret_cast<const char*>(message.data()),
+                                message.size());
+  if (!devtools_util::ProtocolParser::IsValidMessage(str_message)) {
+    LOG(WARNING) << "Invalid message: " << str_message.substr(0, 100);
+    return;
+  }
+
+  devtools_util::ProtocolParser parser;
+
+  for (auto& observer : observers_) {
+    if (observer.OnDevToolsMessage(str_message)) {
+      continue;
+    }
+
+    // Only perform parsing a single time.
+    if (parser.Initialize(str_message) && parser.IsFailure()) {
+      LOG(WARNING) << "Failed to parse message: " << str_message.substr(0, 100);
+    }
+
+    if (parser.IsEvent()) {
+      observer.OnDevToolsEvent(parser.method_, parser.params_);
+    } else if (parser.IsResult()) {
+      observer.OnDevToolsMethodResult(parser.message_id_, parser.success_,
+                                      parser.params_);
+    }
+  }
+}
+
+bool CefDevToolsController::EnsureAgentHost() {
+  if (!agent_host_) {
+    agent_host_ =
+        content::DevToolsAgentHost::GetOrCreateFor(inspected_contents_);
+    if (agent_host_) {
+      agent_host_->AttachClient(this);
+      for (auto& observer : observers_) {
+        observer.OnDevToolsAgentAttached();
+      }
+    }
+  }
+  return !!agent_host_;
+}
diff --git a/src/libcef/browser/devtools/devtools_controller.h b/src/libcef/browser/devtools/devtools_controller.h
new file mode 100644
index 0000000..a1730b7
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_controller.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_CONTROLLER_H_
+#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_CONTROLLER_H_
+
+#include <memory>
+
+#include "content/public/browser/devtools_agent_host_client.h"
+
+#include "base/containers/span.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/values.h"
+
+namespace content {
+class WebContents;
+}
+
+class CefDevToolsController : public content::DevToolsAgentHostClient {
+ public:
+  class Observer : public base::CheckedObserver {
+   public:
+    // See CefDevToolsMessageObserver documentation.
+    virtual bool OnDevToolsMessage(const base::StringPiece& message) = 0;
+    virtual void OnDevToolsMethodResult(int message_id,
+                                        bool success,
+                                        const base::StringPiece& result) = 0;
+    virtual void OnDevToolsEvent(const base::StringPiece& method,
+                                 const base::StringPiece& params) = 0;
+    virtual void OnDevToolsAgentAttached() = 0;
+    virtual void OnDevToolsAgentDetached() = 0;
+
+    virtual void OnDevToolsControllerDestroyed() = 0;
+
+   protected:
+    ~Observer() override {}
+  };
+
+  // |inspected_contents| will outlive this object.
+  explicit CefDevToolsController(content::WebContents* inspected_contents);
+  ~CefDevToolsController() override;
+
+  // See CefBrowserHost methods of the same name for documentation.
+  bool SendDevToolsMessage(const base::StringPiece& message);
+  int ExecuteDevToolsMethod(int message_id,
+                            const std::string& method,
+                            const base::DictionaryValue* params);
+
+  // |observer| must outlive this object or be removed.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  base::WeakPtr<CefDevToolsController> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  // content::DevToolsAgentHostClient implementation:
+  void AgentHostClosed(content::DevToolsAgentHost* agent_host) override;
+  void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
+                               base::span<const uint8_t> message) override;
+
+  bool EnsureAgentHost();
+
+  content::WebContents* const inspected_contents_;
+  scoped_refptr<content::DevToolsAgentHost> agent_host_;
+  int next_message_id_ = 1;
+
+  base::ObserverList<Observer> observers_;
+
+  base::WeakPtrFactory<CefDevToolsController> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefDevToolsController);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_CONTROLLER_H_
diff --git a/src/libcef/browser/devtools/devtools_file_manager.cc b/src/libcef/browser/devtools/devtools_file_manager.cc
new file mode 100644
index 0000000..9307824
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_file_manager.cc
@@ -0,0 +1,204 @@
+// Copyright 2019 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/browser/devtools/devtools_file_manager.h"
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_writer.h"
+#include "base/lazy_instance.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+base::LazyInstance<base::FilePath>::Leaky g_last_save_path =
+    LAZY_INSTANCE_INITIALIZER;
+
+void WriteToFile(const base::FilePath& path, const std::string& content) {
+  DCHECK(!path.empty());
+  base::WriteFile(path, content.c_str(), content.length());
+}
+
+void AppendToFile(const base::FilePath& path, const std::string& content) {
+  DCHECK(!path.empty());
+  base::AppendToFile(path, content.c_str(), content.size());
+}
+
+}  // namespace
+
+CefDevToolsFileManager::CefDevToolsFileManager(CefBrowserHostImpl* browser_impl,
+                                               PrefService* prefs)
+    : browser_impl_(browser_impl),
+      prefs_(prefs),
+      file_task_runner_(base::CreateSequencedTaskRunner(
+          {base::ThreadPool(), base::MayBlock()})),
+      weak_factory_(this) {}
+
+void CefDevToolsFileManager::SaveToFile(const std::string& url,
+                                        const std::string& content,
+                                        bool save_as) {
+  Save(url, content, save_as,
+       base::Bind(&CefDevToolsFileManager::FileSavedAs,
+                  weak_factory_.GetWeakPtr(), url),
+       base::Bind(&CefDevToolsFileManager::CanceledFileSaveAs,
+                  weak_factory_.GetWeakPtr(), url));
+}
+
+void CefDevToolsFileManager::AppendToFile(const std::string& url,
+                                          const std::string& content) {
+  Append(url, content,
+         base::Bind(&CefDevToolsFileManager::AppendedTo,
+                    weak_factory_.GetWeakPtr(), url));
+}
+
+void CefDevToolsFileManager::Save(const std::string& url,
+                                  const std::string& content,
+                                  bool save_as,
+                                  const SaveCallback& saveCallback,
+                                  const CancelCallback& cancelCallback) {
+  auto it = saved_files_.find(url);
+  if (it != saved_files_.end() && !save_as) {
+    SaveAsFileSelected(url, content, saveCallback, it->second);
+    return;
+  }
+
+  const base::DictionaryValue* file_map =
+      prefs_->GetDictionary(prefs::kDevToolsEditedFiles);
+  base::FilePath initial_path;
+
+  const base::Value* path_value;
+  if (file_map->Get(base::MD5String(url), &path_value)) {
+    // Ignore base::GetValueAsFilePath() failure since we handle empty
+    // |initial_path| below.
+    ignore_result(base::GetValueAsFilePath(*path_value, &initial_path));
+  }
+
+  if (initial_path.empty()) {
+    GURL gurl(url);
+    std::string suggested_file_name =
+        gurl.is_valid() ? gurl.ExtractFileName() : url;
+
+    if (suggested_file_name.length() > 64)
+      suggested_file_name = suggested_file_name.substr(0, 64);
+
+    if (!g_last_save_path.Pointer()->empty()) {
+      initial_path = g_last_save_path.Pointer()->DirName().AppendASCII(
+          suggested_file_name);
+    } else {
+      // Use the temp directory. It may be an empty value.
+      base::PathService::Get(base::DIR_TEMP, &initial_path);
+      initial_path = initial_path.AppendASCII(suggested_file_name);
+    }
+  }
+
+  CefFileDialogRunner::FileChooserParams params;
+  params.mode = blink::mojom::FileChooserParams::Mode::kSave;
+  if (!initial_path.empty()) {
+    params.default_file_name = initial_path;
+    if (!initial_path.Extension().empty()) {
+      params.accept_types.push_back(CefString(initial_path.Extension()));
+    }
+  }
+
+  browser_impl_->RunFileChooser(
+      params, base::Bind(&CefDevToolsFileManager::SaveAsDialogDismissed,
+                         weak_factory_.GetWeakPtr(), url, content, saveCallback,
+                         cancelCallback));
+}
+
+void CefDevToolsFileManager::SaveAsDialogDismissed(
+    const std::string& url,
+    const std::string& content,
+    const SaveCallback& saveCallback,
+    const CancelCallback& cancelCallback,
+    int selected_accept_filter,
+    const std::vector<base::FilePath>& file_paths) {
+  if (file_paths.size() == 1) {
+    SaveAsFileSelected(url, content, saveCallback, file_paths[0]);
+  } else {
+    cancelCallback.Run();
+  }
+}
+
+void CefDevToolsFileManager::SaveAsFileSelected(const std::string& url,
+                                                const std::string& content,
+                                                const SaveCallback& callback,
+                                                const base::FilePath& path) {
+  *g_last_save_path.Pointer() = path;
+  saved_files_[url] = path;
+
+  DictionaryPrefUpdate update(prefs_, prefs::kDevToolsEditedFiles);
+  base::DictionaryValue* files_map = update.Get();
+  files_map->SetKey(base::MD5String(url), base::CreateFilePathValue(path));
+  std::string file_system_path = path.AsUTF8Unsafe();
+  callback.Run(file_system_path);
+  file_task_runner_->PostTask(FROM_HERE,
+                              base::BindOnce(&::WriteToFile, path, content));
+}
+
+void CefDevToolsFileManager::FileSavedAs(const std::string& url,
+                                         const std::string& file_system_path) {
+  base::Value url_value(url);
+  base::Value file_system_path_value(file_system_path);
+  CallClientFunction("DevToolsAPI.savedURL", &url_value,
+                     &file_system_path_value, nullptr);
+}
+
+void CefDevToolsFileManager::CanceledFileSaveAs(const std::string& url) {
+  base::Value url_value(url);
+  CallClientFunction("DevToolsAPI.canceledSaveURL", &url_value, nullptr,
+                     nullptr);
+}
+
+void CefDevToolsFileManager::Append(const std::string& url,
+                                    const std::string& content,
+                                    const AppendCallback& callback) {
+  auto it = saved_files_.find(url);
+  if (it == saved_files_.end())
+    return;
+  callback.Run();
+  file_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&::AppendToFile, it->second, content));
+}
+
+void CefDevToolsFileManager::AppendedTo(const std::string& url) {
+  base::Value url_value(url);
+  CallClientFunction("DevToolsAPI.appendedToURL", &url_value, nullptr, nullptr);
+}
+
+void CefDevToolsFileManager::CallClientFunction(
+    const std::string& function_name,
+    const base::Value* arg1,
+    const base::Value* arg2,
+    const base::Value* arg3) {
+  std::string javascript = function_name + "(";
+  if (arg1) {
+    std::string json;
+    base::JSONWriter::Write(*arg1, &json);
+    javascript.append(json);
+    if (arg2) {
+      base::JSONWriter::Write(*arg2, &json);
+      javascript.append(", ").append(json);
+      if (arg3) {
+        base::JSONWriter::Write(*arg3, &json);
+        javascript.append(", ").append(json);
+      }
+    }
+  }
+  javascript.append(");");
+  browser_impl_->web_contents()->GetMainFrame()->ExecuteJavaScript(
+      base::UTF8ToUTF16(javascript), base::NullCallback());
+}
diff --git a/src/libcef/browser/devtools/devtools_file_manager.h b/src/libcef/browser/devtools/devtools_file_manager.h
new file mode 100644
index 0000000..364f26d
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_file_manager.h
@@ -0,0 +1,82 @@
+// Copyright 2019 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FILE_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FILE_MANAGER_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+
+#include <map>
+#include <string>
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+class Value;
+}  // namespace base
+
+class CefBrowserHostImpl;
+class PrefService;
+
+// File management helper for DevTools.
+// Based on chrome/browser/devtools/devtools_ui_bindings.cc and
+// chrome/browser/devtools/devtools_file_helper.cc.
+class CefDevToolsFileManager {
+ public:
+  CefDevToolsFileManager(CefBrowserHostImpl* browser_impl, PrefService* prefs);
+
+  void SaveToFile(const std::string& url,
+                  const std::string& content,
+                  bool save_as);
+  void AppendToFile(const std::string& url, const std::string& content);
+
+ private:
+  // SaveToFile implementation:
+  typedef base::Callback<void(const std::string&)> SaveCallback;
+  typedef base::Callback<void()> CancelCallback;
+  void Save(const std::string& url,
+            const std::string& content,
+            bool save_as,
+            const SaveCallback& saveCallback,
+            const CancelCallback& cancelCallback);
+  void SaveAsDialogDismissed(const std::string& url,
+                             const std::string& content,
+                             const SaveCallback& saveCallback,
+                             const CancelCallback& cancelCallback,
+                             int selected_accept_filter,
+                             const std::vector<base::FilePath>& file_paths);
+  void SaveAsFileSelected(const std::string& url,
+                          const std::string& content,
+                          const SaveCallback& callback,
+                          const base::FilePath& path);
+  void FileSavedAs(const std::string& url, const std::string& file_system_path);
+  void CanceledFileSaveAs(const std::string& url);
+
+  // AppendToFile implementation:
+  typedef base::Callback<void(void)> AppendCallback;
+  void Append(const std::string& url,
+              const std::string& content,
+              const AppendCallback& callback);
+  void AppendedTo(const std::string& url);
+
+  void CallClientFunction(const std::string& function_name,
+                          const base::Value* arg1,
+                          const base::Value* arg2,
+                          const base::Value* arg3);
+
+  // Guaranteed to outlive this object.
+  CefBrowserHostImpl* browser_impl_;
+  PrefService* prefs_;
+
+  typedef std::map<std::string, base::FilePath> PathsMap;
+  PathsMap saved_files_;
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
+  base::WeakPtrFactory<CefDevToolsFileManager> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefDevToolsFileManager);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FILE_MANAGER_H_
diff --git a/src/libcef/browser/devtools/devtools_frontend.cc b/src/libcef/browser/devtools/devtools_frontend.cc
new file mode 100644
index 0000000..5c3b3ef
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_frontend.cc
@@ -0,0 +1,626 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/devtools/devtools_frontend.h"
+
+#include <stddef.h>
+
+#include <iomanip>
+#include <utility>
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/devtools/devtools_manager_delegate.h"
+#include "libcef/browser/net/devtools_scheme_handler.h"
+#include "libcef/common/cef_switches.h"
+
+#include "base/base64.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/guid.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/file_url_loader.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/shared_cors_origin_access_list.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/common/url_utils.h"
+#include "ipc/ipc_channel.h"
+#include "net/base/completion_once_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "storage/browser/file_system/native_file_util.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <time.h>
+#endif
+
+namespace {
+
+static std::string GetFrontendURL() {
+  return base::StringPrintf("%s://%s/devtools_app.html",
+                            content::kChromeDevToolsScheme,
+                            scheme::kChromeDevToolsHost);
+}
+
+std::unique_ptr<base::DictionaryValue> BuildObjectForResponse(
+    const net::HttpResponseHeaders* rh,
+    bool success,
+    int net_error) {
+  auto response = std::make_unique<base::DictionaryValue>();
+  int responseCode = 200;
+  if (rh) {
+    responseCode = rh->response_code();
+  } else if (!success) {
+    // In case of no headers, assume file:// URL and failed to load
+    responseCode = 404;
+  }
+  response->SetInteger("statusCode", responseCode);
+  response->SetInteger("netError", net_error);
+  response->SetString("netErrorName", net::ErrorToString(net_error));
+
+  auto headers = std::make_unique<base::DictionaryValue>();
+  size_t iterator = 0;
+  std::string name;
+  std::string value;
+  // TODO(caseq): this probably needs to handle duplicate header names
+  // correctly by folding them.
+  while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
+    headers->SetString(name, value);
+
+  response->Set("headers", std::move(headers));
+  return response;
+}
+
+const int kMaxLogLineLength = 1024;
+
+void WriteTimestamp(std::stringstream& stream) {
+#if defined(OS_WIN)
+  SYSTEMTIME local_time;
+  GetLocalTime(&local_time);
+  stream << std::setfill('0') << std::setw(2) << local_time.wMonth
+         << std::setw(2) << local_time.wDay << '/' << std::setw(2)
+         << local_time.wHour << std::setw(2) << local_time.wMinute
+         << std::setw(2) << local_time.wSecond << '.' << std::setw(3)
+         << local_time.wMilliseconds;
+#elif defined(OS_POSIX)
+  timeval tv;
+  gettimeofday(&tv, nullptr);
+  time_t t = tv.tv_sec;
+  struct tm local_time;
+  localtime_r(&t, &local_time);
+  struct tm* tm_time = &local_time;
+  stream << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
+         << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
+         << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
+         << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec;
+#else
+#error Unsupported platform
+#endif
+}
+
+void LogProtocolMessage(const base::FilePath& log_file,
+                        ProtocolMessageType type,
+                        std::string to_log) {
+  // Track if logging has failed, in which case we don't keep trying.
+  static bool log_error = false;
+  if (log_error)
+    return;
+
+  if (storage::NativeFileUtil::EnsureFileExists(log_file, nullptr) !=
+      base::File::FILE_OK) {
+    LOG(ERROR) << "Failed to create file " << log_file.value();
+    log_error = true;
+    return;
+  }
+
+  std::string type_label;
+  switch (type) {
+    case ProtocolMessageType::METHOD:
+      type_label = "METHOD";
+      break;
+    case ProtocolMessageType::RESULT:
+      type_label = "RESULT";
+      break;
+    case ProtocolMessageType::EVENT:
+      type_label = "EVENT";
+      break;
+  }
+
+  std::stringstream stream;
+  WriteTimestamp(stream);
+  stream << ": " << type_label << ": " << to_log << "\n";
+  const std::string& str = stream.str();
+  if (!base::AppendToFile(log_file, str.c_str(), str.size())) {
+    LOG(ERROR) << "Failed to write file " << log_file.value();
+    log_error = true;
+  }
+}
+
+}  // namespace
+
+class CefDevToolsFrontend::NetworkResourceLoader
+    : public network::SimpleURLLoaderStreamConsumer {
+ public:
+  NetworkResourceLoader(int stream_id,
+                        CefDevToolsFrontend* bindings,
+                        std::unique_ptr<network::SimpleURLLoader> loader,
+                        network::mojom::URLLoaderFactory* url_loader_factory,
+                        int request_id)
+      : stream_id_(stream_id),
+        bindings_(bindings),
+        loader_(std::move(loader)),
+        request_id_(request_id) {
+    loader_->SetOnResponseStartedCallback(base::BindOnce(
+        &NetworkResourceLoader::OnResponseStarted, base::Unretained(this)));
+    loader_->DownloadAsStream(url_loader_factory, this);
+  }
+
+ private:
+  void OnResponseStarted(const GURL& final_url,
+                         const network::mojom::URLResponseHead& response_head) {
+    response_headers_ = response_head.headers;
+  }
+
+  void OnDataReceived(base::StringPiece chunk,
+                      base::OnceClosure resume) override {
+    base::Value chunkValue;
+
+    bool encoded = !base::IsStringUTF8(chunk);
+    if (encoded) {
+      std::string encoded_string;
+      base::Base64Encode(chunk, &encoded_string);
+      chunkValue = base::Value(std::move(encoded_string));
+    } else {
+      chunkValue = base::Value(chunk);
+    }
+    base::Value id(stream_id_);
+    base::Value encodedValue(encoded);
+
+    bindings_->CallClientFunction("DevToolsAPI.streamWrite", &id, &chunkValue,
+                                  &encodedValue);
+    std::move(resume).Run();
+  }
+
+  void OnComplete(bool success) override {
+    auto response = BuildObjectForResponse(response_headers_.get(), success,
+                                           loader_->NetError());
+    bindings_->SendMessageAck(request_id_, response.get());
+
+    bindings_->loaders_.erase(bindings_->loaders_.find(this));
+  }
+
+  void OnRetry(base::OnceClosure start_retry) override { NOTREACHED(); }
+
+  const int stream_id_;
+  CefDevToolsFrontend* const bindings_;
+  std::unique_ptr<network::SimpleURLLoader> loader_;
+  int request_id_;
+  scoped_refptr<net::HttpResponseHeaders> response_headers_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkResourceLoader);
+};
+
+// This constant should be in sync with
+// the constant at devtools_ui_bindings.cc.
+const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
+
+// static
+CefDevToolsFrontend* CefDevToolsFrontend::Show(
+    CefBrowserHostImpl* inspected_browser,
+    const CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient> client,
+    const CefBrowserSettings& settings,
+    const CefPoint& inspect_element_at,
+    base::OnceClosure frontend_destroyed_callback) {
+  CefBrowserSettings new_settings = settings;
+  if (!windowInfo.windowless_rendering_enabled &&
+      CefColorGetA(new_settings.background_color) != SK_AlphaOPAQUE) {
+    // Use white as the default background color for windowed DevTools instead
+    // of the CefSettings.background_color value.
+    new_settings.background_color = SK_ColorWHITE;
+  }
+
+  CefBrowserHostImpl::CreateParams create_params;
+  if (!inspected_browser->IsViewsHosted())
+    create_params.window_info.reset(new CefWindowInfo(windowInfo));
+  create_params.client = client;
+  create_params.settings = new_settings;
+  create_params.devtools_opener = inspected_browser;
+  create_params.request_context = inspected_browser->GetRequestContext();
+  create_params.extra_info = inspected_browser->browser_info()->extra_info();
+
+  CefRefPtr<CefBrowserHostImpl> frontend_browser =
+      CefBrowserHostImpl::Create(create_params);
+
+  content::WebContents* inspected_contents = inspected_browser->web_contents();
+
+  // CefDevToolsFrontend will delete itself when the frontend WebContents is
+  // destroyed.
+  CefDevToolsFrontend* devtools_frontend = new CefDevToolsFrontend(
+      static_cast<CefBrowserHostImpl*>(frontend_browser.get()),
+      inspected_contents, inspect_element_at,
+      std::move(frontend_destroyed_callback));
+
+  // Need to load the URL after creating the DevTools objects.
+  frontend_browser->GetMainFrame()->LoadURL(GetFrontendURL());
+
+  return devtools_frontend;
+}
+
+void CefDevToolsFrontend::Activate() {
+  frontend_browser_->ActivateContents(web_contents());
+}
+
+void CefDevToolsFrontend::Focus() {
+  frontend_browser_->SetFocus(true);
+}
+
+void CefDevToolsFrontend::InspectElementAt(int x, int y) {
+  if (inspect_element_at_.x != x || inspect_element_at_.y != y)
+    inspect_element_at_.Set(x, y);
+  if (agent_host_)
+    agent_host_->InspectElement(inspected_contents_->GetFocusedFrame(), x, y);
+}
+
+void CefDevToolsFrontend::Close() {
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::Bind(&CefBrowserHostImpl::CloseBrowser,
+                            frontend_browser_.get(), true));
+}
+
+CefDevToolsFrontend::CefDevToolsFrontend(
+    CefBrowserHostImpl* frontend_browser,
+    content::WebContents* inspected_contents,
+    const CefPoint& inspect_element_at,
+    base::OnceClosure frontend_destroyed_callback)
+    : content::WebContentsObserver(frontend_browser->web_contents()),
+      frontend_browser_(frontend_browser),
+      inspected_contents_(inspected_contents),
+      inspect_element_at_(inspect_element_at),
+      frontend_destroyed_callback_(std::move(frontend_destroyed_callback)),
+      file_manager_(frontend_browser, GetPrefs()),
+      protocol_log_file_(
+          base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+              switches::kDevToolsProtocolLogFile)),
+      weak_factory_(this) {
+  DCHECK(!frontend_destroyed_callback_.is_null());
+}
+
+CefDevToolsFrontend::~CefDevToolsFrontend() {}
+
+void CefDevToolsFrontend::ReadyToCommitNavigation(
+    content::NavigationHandle* navigation_handle) {
+  content::RenderFrameHost* frame = navigation_handle->GetRenderFrameHost();
+  if (navigation_handle->IsInMainFrame()) {
+    frontend_host_ = content::DevToolsFrontendHost::Create(
+        frame,
+        base::Bind(&CefDevToolsFrontend::HandleMessageFromDevToolsFrontend,
+                   base::Unretained(this)));
+    return;
+  }
+
+  std::string origin = navigation_handle->GetURL().GetOrigin().spec();
+  auto it = extensions_api_.find(origin);
+  if (it == extensions_api_.end())
+    return;
+  std::string script = base::StringPrintf("%s(\"%s\")", it->second.c_str(),
+                                          base::GenerateGUID().c_str());
+  content::DevToolsFrontendHost::SetupExtensionsAPI(frame, script);
+}
+
+void CefDevToolsFrontend::DocumentAvailableInMainFrame() {
+  // Don't call AttachClient multiple times for the same DevToolsAgentHost.
+  // Otherwise it will call AgentHostClosed which closes the DevTools window.
+  // This may happen in cases where the DevTools content fails to load.
+  scoped_refptr<content::DevToolsAgentHost> agent_host =
+      content::DevToolsAgentHost::GetOrCreateFor(inspected_contents_);
+  if (agent_host != agent_host_) {
+    if (agent_host_)
+      agent_host_->DetachClient(this);
+    agent_host_ = agent_host;
+    agent_host_->AttachClient(this);
+    if (!inspect_element_at_.IsEmpty()) {
+      agent_host_->InspectElement(inspected_contents_->GetFocusedFrame(),
+                                  inspect_element_at_.x, inspect_element_at_.y);
+    }
+  }
+}
+
+void CefDevToolsFrontend::WebContentsDestroyed() {
+  if (agent_host_) {
+    agent_host_->DetachClient(this);
+    agent_host_ = nullptr;
+  }
+  std::move(frontend_destroyed_callback_).Run();
+  delete this;
+}
+
+void CefDevToolsFrontend::HandleMessageFromDevToolsFrontend(
+    const std::string& message) {
+  std::string method;
+  base::ListValue* params = nullptr;
+  base::DictionaryValue* dict = nullptr;
+  base::Optional<base::Value> parsed_message = base::JSONReader::Read(message);
+  if (!parsed_message || !parsed_message->GetAsDictionary(&dict) ||
+      !dict->GetString("method", &method)) {
+    return;
+  }
+  int request_id = 0;
+  dict->GetInteger("id", &request_id);
+  dict->GetList("params", &params);
+
+  if (method == "dispatchProtocolMessage" && params && params->GetSize() == 1) {
+    std::string protocol_message;
+    if (!agent_host_ || !params->GetString(0, &protocol_message))
+      return;
+    if (ProtocolLoggingEnabled()) {
+      LogProtocolMessage(ProtocolMessageType::METHOD, protocol_message);
+    }
+    agent_host_->DispatchProtocolMessage(
+        this, base::as_bytes(base::make_span(protocol_message)));
+  } else if (method == "loadCompleted") {
+    web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+        base::ASCIIToUTF16("DevToolsAPI.setUseSoftMenu(true);"),
+        base::NullCallback());
+  } else if (method == "loadNetworkResource" && params->GetSize() == 3) {
+    // TODO(pfeldman): handle some of the embedder messages in content.
+    std::string url;
+    std::string headers;
+    int stream_id;
+    if (!params->GetString(0, &url) || !params->GetString(1, &headers) ||
+        !params->GetInteger(2, &stream_id)) {
+      return;
+    }
+
+    GURL gurl(url);
+    if (!gurl.is_valid()) {
+      base::DictionaryValue response;
+      response.SetInteger("statusCode", 404);
+      response.SetBoolean("urlValid", false);
+      SendMessageAck(request_id, &response);
+      return;
+    }
+
+    net::NetworkTrafficAnnotationTag traffic_annotation =
+        net::DefineNetworkTrafficAnnotation(
+            "devtools_handle_front_end_messages", R"(
+            semantics {
+              sender: "Developer Tools"
+              description:
+                "When user opens Developer Tools, the browser may fetch "
+                "additional resources from the network to enrich the debugging "
+                "experience (e.g. source map resources)."
+              trigger: "User opens Developer Tools to debug a web page."
+              data: "Any resources requested by Developer Tools."
+              destination: OTHER
+            }
+            policy {
+              cookies_allowed: YES
+              cookies_store: "user"
+              setting:
+                "It's not possible to disable this feature from settings."
+              chrome_policy {
+                DeveloperToolsAvailability {
+                  policy_options {mode: MANDATORY}
+                  DeveloperToolsAvailability: 2
+                }
+              }
+            })");
+
+    // Based on DevToolsUIBindings::LoadNetworkResource.
+    auto resource_request = std::make_unique<network::ResourceRequest>();
+    resource_request->url = gurl;
+    // TODO(caseq): this preserves behavior of URLFetcher-based
+    // implementation. We really need to pass proper first party origin from
+    // the front-end.
+    resource_request->site_for_cookies = net::SiteForCookies::FromUrl(gurl);
+    resource_request->headers.AddHeadersFromString(headers);
+
+    std::unique_ptr<network::mojom::URLLoaderFactory> file_url_loader_factory;
+    scoped_refptr<network::SharedURLLoaderFactory> network_url_loader_factory;
+    std::unique_ptr<network::mojom::URLLoaderFactory> webui_url_loader_factory;
+    network::mojom::URLLoaderFactory* url_loader_factory;
+    if (gurl.SchemeIsFile()) {
+      file_url_loader_factory = content::CreateFileURLLoaderFactory(
+          base::FilePath() /* profile_path */,
+          nullptr /* shared_cors_origin_access_list */);
+      url_loader_factory = file_url_loader_factory.get();
+    } else if (content::HasWebUIScheme(gurl)) {
+      base::DictionaryValue response;
+      response.SetInteger("statusCode", 403);
+      SendMessageAck(request_id, &response);
+      return;
+    } else {
+      auto* partition = content::BrowserContext::GetStoragePartitionForSite(
+          web_contents()->GetBrowserContext(), gurl);
+      network_url_loader_factory =
+          partition->GetURLLoaderFactoryForBrowserProcess();
+      url_loader_factory = network_url_loader_factory.get();
+    }
+
+    auto simple_url_loader = network::SimpleURLLoader::Create(
+        std::move(resource_request), traffic_annotation);
+    auto resource_loader = std::make_unique<NetworkResourceLoader>(
+        stream_id, this, std::move(simple_url_loader), url_loader_factory,
+        request_id);
+    loaders_.insert(std::move(resource_loader));
+    return;
+  } else if (method == "getPreferences") {
+    SendMessageAck(request_id,
+                   GetPrefs()->GetDictionary(prefs::kDevToolsPreferences));
+    return;
+  } else if (method == "setPreference") {
+    std::string name;
+    std::string value;
+    if (!params->GetString(0, &name) || !params->GetString(1, &value)) {
+      return;
+    }
+    DictionaryPrefUpdate update(GetPrefs(), prefs::kDevToolsPreferences);
+    update.Get()->SetKey(name, base::Value(value));
+  } else if (method == "removePreference") {
+    std::string name;
+    if (!params->GetString(0, &name))
+      return;
+    DictionaryPrefUpdate update(GetPrefs(), prefs::kDevToolsPreferences);
+    update.Get()->RemoveWithoutPathExpansion(name, nullptr);
+  } else if (method == "requestFileSystems") {
+    web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+        base::ASCIIToUTF16("DevToolsAPI.fileSystemsLoaded([]);"),
+        base::NullCallback());
+  } else if (method == "reattach") {
+    if (!agent_host_)
+      return;
+    agent_host_->DetachClient(this);
+    agent_host_->AttachClient(this);
+  } else if (method == "registerExtensionsAPI") {
+    std::string origin;
+    std::string script;
+    if (!params->GetString(0, &origin) || !params->GetString(1, &script))
+      return;
+    extensions_api_[origin + "/"] = script;
+  } else if (method == "save" && params->GetSize() == 3) {
+    std::string url;
+    std::string content;
+    bool save_as;
+    if (!params->GetString(0, &url) || !params->GetString(1, &content) ||
+        !params->GetBoolean(2, &save_as)) {
+      return;
+    }
+    file_manager_.SaveToFile(url, content, save_as);
+  } else if (method == "append" && params->GetSize() == 2) {
+    std::string url;
+    std::string content;
+    if (!params->GetString(0, &url) || !params->GetString(1, &content)) {
+      return;
+    }
+    file_manager_.AppendToFile(url, content);
+  } else {
+    return;
+  }
+
+  if (request_id)
+    SendMessageAck(request_id, nullptr);
+}
+
+void CefDevToolsFrontend::DispatchProtocolMessage(
+    content::DevToolsAgentHost* agent_host,
+    base::span<const uint8_t> message) {
+  base::StringPiece str_message(reinterpret_cast<const char*>(message.data()),
+                                message.size());
+  if (ProtocolLoggingEnabled()) {
+    // Quick check to avoid parsing the JSON object. Events begin with a
+    // "method" value whereas method results begin with an "id" value.
+    LogProtocolMessage(str_message.starts_with("{\"method\":")
+                           ? ProtocolMessageType::EVENT
+                           : ProtocolMessageType::RESULT,
+                       str_message);
+  }
+  if (str_message.length() < kMaxMessageChunkSize) {
+    std::string param;
+    base::EscapeJSONString(str_message, true, &param);
+    std::string code = "DevToolsAPI.dispatchMessage(" + param + ");";
+    base::string16 javascript = base::UTF8ToUTF16(code);
+    web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+        javascript, base::NullCallback());
+    return;
+  }
+
+  size_t total_size = str_message.length();
+  for (size_t pos = 0; pos < str_message.length();
+       pos += kMaxMessageChunkSize) {
+    std::string param;
+    base::EscapeJSONString(str_message.substr(pos, kMaxMessageChunkSize), true,
+                           &param);
+    std::string code = "DevToolsAPI.dispatchMessageChunk(" + param + "," +
+                       std::to_string(pos ? 0 : total_size) + ");";
+    base::string16 javascript = base::UTF8ToUTF16(code);
+    web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+        javascript, base::NullCallback());
+  }
+}
+
+void CefDevToolsFrontend::CallClientFunction(const std::string& function_name,
+                                             const base::Value* arg1,
+                                             const base::Value* arg2,
+                                             const base::Value* arg3) {
+  std::string javascript = function_name + "(";
+  if (arg1) {
+    std::string json;
+    base::JSONWriter::Write(*arg1, &json);
+    javascript.append(json);
+    if (arg2) {
+      base::JSONWriter::Write(*arg2, &json);
+      javascript.append(", ").append(json);
+      if (arg3) {
+        base::JSONWriter::Write(*arg3, &json);
+        javascript.append(", ").append(json);
+      }
+    }
+  }
+  javascript.append(");");
+  web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::UTF8ToUTF16(javascript), base::NullCallback());
+}
+
+void CefDevToolsFrontend::SendMessageAck(int request_id,
+                                         const base::Value* arg) {
+  base::Value id_value(request_id);
+  CallClientFunction("DevToolsAPI.embedderMessageAck", &id_value, arg, nullptr);
+}
+
+bool CefDevToolsFrontend::ProtocolLoggingEnabled() const {
+  return !protocol_log_file_.empty();
+}
+
+void CefDevToolsFrontend::LogProtocolMessage(ProtocolMessageType type,
+                                             const base::StringPiece& message) {
+  DCHECK(ProtocolLoggingEnabled());
+
+  std::string to_log = message.substr(0, kMaxLogLineLength).as_string();
+
+  // Execute in an ordered context that allows blocking.
+  auto task_runner = CefContentBrowserClient::Get()->background_task_runner();
+  task_runner->PostTask(
+      FROM_HERE, base::BindOnce(::LogProtocolMessage, protocol_log_file_, type,
+                                std::move(to_log)));
+}
+
+void CefDevToolsFrontend::AgentHostClosed(
+    content::DevToolsAgentHost* agent_host) {
+  DCHECK(agent_host == agent_host_.get());
+  agent_host_ = nullptr;
+  Close();
+}
+
+PrefService* CefDevToolsFrontend::GetPrefs() const {
+  return static_cast<CefBrowserContext*>(
+             frontend_browser_->web_contents()->GetBrowserContext())
+      ->GetPrefs();
+}
diff --git a/src/libcef/browser/devtools/devtools_frontend.h b/src/libcef/browser/devtools/devtools_frontend.h
new file mode 100644
index 0000000..90e2def
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_frontend.h
@@ -0,0 +1,112 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_H_
+#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_H_
+
+#include <memory>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/devtools/devtools_file_manager.h"
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_frontend_host.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace base {
+class Value;
+}
+
+namespace content {
+class NavigationHandle;
+class RenderViewHost;
+class WebContents;
+}  // namespace content
+
+class PrefService;
+
+enum class ProtocolMessageType {
+  METHOD,
+  RESULT,
+  EVENT,
+};
+
+class CefDevToolsFrontend : public content::WebContentsObserver,
+                            public content::DevToolsAgentHostClient {
+ public:
+  static CefDevToolsFrontend* Show(
+      CefBrowserHostImpl* inspected_browser,
+      const CefWindowInfo& windowInfo,
+      CefRefPtr<CefClient> client,
+      const CefBrowserSettings& settings,
+      const CefPoint& inspect_element_at,
+      base::OnceClosure frontend_destroyed_callback);
+
+  void Activate();
+  void Focus();
+  void InspectElementAt(int x, int y);
+  void Close();
+
+  void CallClientFunction(const std::string& function_name,
+                          const base::Value* arg1,
+                          const base::Value* arg2,
+                          const base::Value* arg3);
+
+ private:
+  CefDevToolsFrontend(CefBrowserHostImpl* frontend_browser,
+                      content::WebContents* inspected_contents,
+                      const CefPoint& inspect_element_at,
+                      base::OnceClosure destroyed_callback);
+  ~CefDevToolsFrontend() override;
+
+  // content::DevToolsAgentHostClient implementation.
+  void AgentHostClosed(content::DevToolsAgentHost* agent_host) override;
+  void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
+                               base::span<const uint8_t> message) override;
+  void HandleMessageFromDevToolsFrontend(const std::string& message);
+
+ private:
+  // WebContentsObserver overrides
+  void ReadyToCommitNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void DocumentAvailableInMainFrame() override;
+  void WebContentsDestroyed() override;
+
+  void SendMessageAck(int request_id, const base::Value* arg1);
+
+  bool ProtocolLoggingEnabled() const;
+  void LogProtocolMessage(ProtocolMessageType type,
+                          const base::StringPiece& message);
+
+  PrefService* GetPrefs() const;
+
+  CefRefPtr<CefBrowserHostImpl> frontend_browser_;
+  content::WebContents* inspected_contents_;
+  scoped_refptr<content::DevToolsAgentHost> agent_host_;
+  CefPoint inspect_element_at_;
+  base::OnceClosure frontend_destroyed_callback_;
+  std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
+
+  class NetworkResourceLoader;
+  std::set<std::unique_ptr<NetworkResourceLoader>, base::UniquePtrComparator>
+      loaders_;
+
+  using ExtensionsAPIs = std::map<std::string, std::string>;
+  ExtensionsAPIs extensions_api_;
+  CefDevToolsFileManager file_manager_;
+
+  const base::FilePath protocol_log_file_;
+
+  base::WeakPtrFactory<CefDevToolsFrontend> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefDevToolsFrontend);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_H_
diff --git a/src/libcef/browser/devtools/devtools_manager.cc b/src/libcef/browser/devtools/devtools_manager.cc
new file mode 100644
index 0000000..c93b274
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_manager.cc
@@ -0,0 +1,200 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/devtools/devtools_manager.h"
+
+#include "libcef/browser/devtools/devtools_controller.h"
+#include "libcef/browser/devtools/devtools_frontend.h"
+
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+// May be created on any thread but will be destroyed on the UI thread.
+class CefDevToolsRegistrationImpl : public CefRegistration,
+                                    public CefDevToolsController::Observer {
+ public:
+  explicit CefDevToolsRegistrationImpl(
+      CefRefPtr<CefDevToolsMessageObserver> observer)
+      : observer_(observer) {
+    DCHECK(observer_);
+  }
+
+  ~CefDevToolsRegistrationImpl() override {
+    CEF_REQUIRE_UIT();
+
+    // May be null if OnDevToolsControllerDestroyed was called.
+    if (!controller_)
+      return;
+
+    controller_->RemoveObserver(this);
+  }
+
+  void Initialize(CefBrowserHostImpl* browser,
+                  base::WeakPtr<CefDevToolsController> controller) {
+    CEF_REQUIRE_UIT();
+    DCHECK(browser && controller);
+    DCHECK(!browser_ && !controller_);
+    browser_ = browser;
+    controller_ = controller;
+
+    controller_->AddObserver(this);
+  }
+
+ private:
+  // CefDevToolsController::Observer methods:
+  bool OnDevToolsMessage(const base::StringPiece& message) override {
+    CEF_REQUIRE_UIT();
+    return observer_->OnDevToolsMessage(browser_, message.data(),
+                                        message.size());
+  }
+
+  void OnDevToolsMethodResult(int message_id,
+                              bool success,
+                              const base::StringPiece& result) override {
+    CEF_REQUIRE_UIT();
+    observer_->OnDevToolsMethodResult(browser_, message_id, success,
+                                      result.data(), result.size());
+  }
+
+  void OnDevToolsEvent(const base::StringPiece& method,
+                       const base::StringPiece& params) override {
+    CEF_REQUIRE_UIT();
+    observer_->OnDevToolsEvent(browser_, method.as_string(), params.data(),
+                               params.size());
+  }
+
+  void OnDevToolsAgentAttached() override {
+    CEF_REQUIRE_UIT();
+    observer_->OnDevToolsAgentAttached(browser_);
+  }
+
+  void OnDevToolsAgentDetached() override {
+    CEF_REQUIRE_UIT();
+    observer_->OnDevToolsAgentDetached(browser_);
+  }
+
+  void OnDevToolsControllerDestroyed() override {
+    CEF_REQUIRE_UIT();
+    browser_ = nullptr;
+    controller_.reset();
+  }
+
+  CefRefPtr<CefDevToolsMessageObserver> observer_;
+
+  CefBrowserHostImpl* browser_ = nullptr;
+  base::WeakPtr<CefDevToolsController> controller_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefDevToolsRegistrationImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefDevToolsRegistrationImpl);
+};
+
+}  // namespace
+
+CefDevToolsManager::CefDevToolsManager(CefBrowserHostImpl* inspected_browser)
+    : inspected_browser_(inspected_browser), weak_ptr_factory_(this) {
+  CEF_REQUIRE_UIT();
+}
+
+CefDevToolsManager::~CefDevToolsManager() {
+  CEF_REQUIRE_UIT();
+}
+
+void CefDevToolsManager::ShowDevTools(const CefWindowInfo& windowInfo,
+                                      CefRefPtr<CefClient> client,
+                                      const CefBrowserSettings& settings,
+                                      const CefPoint& inspect_element_at) {
+  CEF_REQUIRE_UIT();
+  if (devtools_frontend_) {
+    if (!inspect_element_at.IsEmpty()) {
+      devtools_frontend_->InspectElementAt(inspect_element_at.x,
+                                           inspect_element_at.y);
+    }
+    devtools_frontend_->Focus();
+    return;
+  }
+
+  devtools_frontend_ = CefDevToolsFrontend::Show(
+      inspected_browser_, windowInfo, client, settings, inspect_element_at,
+      base::BindOnce(&CefDevToolsManager::OnFrontEndDestroyed,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CefDevToolsManager::CloseDevTools() {
+  CEF_REQUIRE_UIT();
+  if (!devtools_frontend_)
+    return;
+  devtools_frontend_->Close();
+}
+
+bool CefDevToolsManager::HasDevTools() {
+  CEF_REQUIRE_UIT();
+  return !!devtools_frontend_;
+}
+
+bool CefDevToolsManager::SendDevToolsMessage(const void* message,
+                                             size_t message_size) {
+  CEF_REQUIRE_UIT();
+  if (!message || message_size == 0)
+    return false;
+
+  if (!EnsureController())
+    return false;
+
+  return devtools_controller_->SendDevToolsMessage(
+      base::StringPiece(static_cast<const char*>(message), message_size));
+}
+
+int CefDevToolsManager::ExecuteDevToolsMethod(
+    int message_id,
+    const CefString& method,
+    CefRefPtr<CefDictionaryValue> params) {
+  CEF_REQUIRE_UIT();
+  if (method.empty())
+    return 0;
+
+  if (!EnsureController())
+    return 0;
+
+  if (params && params->IsValid()) {
+    CefDictionaryValueImpl* impl =
+        static_cast<CefDictionaryValueImpl*>(params.get());
+    CefValueController::AutoLock lock_scope(impl->controller());
+    return devtools_controller_->ExecuteDevToolsMethod(message_id, method,
+                                                       impl->GetValueUnsafe());
+  } else {
+    return devtools_controller_->ExecuteDevToolsMethod(message_id, method,
+                                                       nullptr);
+  }
+}
+
+// static
+CefRefPtr<CefRegistration> CefDevToolsManager::CreateRegistration(
+    CefRefPtr<CefDevToolsMessageObserver> observer) {
+  DCHECK(observer);
+  return new CefDevToolsRegistrationImpl(observer);
+}
+
+void CefDevToolsManager::InitializeRegistrationOnUIThread(
+    CefRefPtr<CefRegistration> registration) {
+  CEF_REQUIRE_UIT();
+
+  if (!EnsureController())
+    return;
+
+  static_cast<CefDevToolsRegistrationImpl*>(registration.get())
+      ->Initialize(inspected_browser_, devtools_controller_->GetWeakPtr());
+}
+
+void CefDevToolsManager::OnFrontEndDestroyed() {
+  devtools_frontend_ = nullptr;
+}
+
+bool CefDevToolsManager::EnsureController() {
+  if (!devtools_controller_) {
+    devtools_controller_.reset(
+        new CefDevToolsController(inspected_browser_->web_contents()));
+  }
+  return true;
+}
diff --git a/src/libcef/browser/devtools/devtools_manager.h b/src/libcef/browser/devtools/devtools_manager.h
new file mode 100644
index 0000000..ae1d5fe
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_manager.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
+#pragma once
+
+#include "include/cef_browser.h"
+
+#include "base/memory/weak_ptr.h"
+
+class CefBrowserHostImpl;
+class CefDevToolsController;
+class CefDevToolsFrontend;
+
+namespace content {
+class WebContents;
+}
+
+// Manages DevTools instances. Methods must be called on the UI thread unless
+// otherwise indicated.
+class CefDevToolsManager {
+ public:
+  // |inspected_browser| will outlive this object.
+  explicit CefDevToolsManager(CefBrowserHostImpl* inspected_browser);
+  ~CefDevToolsManager();
+
+  // See CefBrowserHost methods of the same name for documentation.
+  void ShowDevTools(const CefWindowInfo& windowInfo,
+                    CefRefPtr<CefClient> client,
+                    const CefBrowserSettings& settings,
+                    const CefPoint& inspect_element_at);
+  void CloseDevTools();
+  bool HasDevTools();
+  bool SendDevToolsMessage(const void* message, size_t message_size);
+  int ExecuteDevToolsMethod(int message_id,
+                            const CefString& method,
+                            CefRefPtr<CefDictionaryValue> param);
+
+  // These methods are used to implement
+  // CefBrowserHost::AddDevToolsMessageObserver. CreateRegistration is safe to
+  // call on any thread. InitializeRegistrationOnUIThread should be called
+  // immediately afterwards on the UI thread.
+  static CefRefPtr<CefRegistration> CreateRegistration(
+      CefRefPtr<CefDevToolsMessageObserver> observer);
+  void InitializeRegistrationOnUIThread(
+      CefRefPtr<CefRegistration> registration);
+
+ private:
+  void OnFrontEndDestroyed();
+
+  bool EnsureController();
+
+  CefBrowserHostImpl* const inspected_browser_;
+
+  // CefDevToolsFrontend will delete itself when the frontend WebContents is
+  // destroyed.
+  CefDevToolsFrontend* devtools_frontend_ = nullptr;
+
+  // Used for sending DevTools protocol messages without an active frontend.
+  std::unique_ptr<CefDevToolsController> devtools_controller_;
+
+  base::WeakPtrFactory<CefDevToolsManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefDevToolsManager);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
diff --git a/src/libcef/browser/devtools/devtools_manager_delegate.cc b/src/libcef/browser/devtools/devtools_manager_delegate.cc
new file mode 100644
index 0000000..41fa033
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_manager_delegate.cc
@@ -0,0 +1,142 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/devtools/devtools_manager_delegate.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/atomicops.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "cef/grit/cef_resources.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_frontend_host.h"
+#include "content/public/browser/devtools_socket_factory.h"
+#include "content/public/browser/favicon_status.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/common/user_agent.h"
+#include "net/base/net_errors.h"
+#include "net/log/net_log_source.h"
+#include "net/socket/tcp_server_socket.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+const int kBackLog = 10;
+
+class TCPServerSocketFactory : public content::DevToolsSocketFactory {
+ public:
+  TCPServerSocketFactory(const std::string& address, uint16_t port)
+      : address_(address), port_(port) {}
+
+ private:
+  // content::DevToolsSocketFactory.
+  std::unique_ptr<net::ServerSocket> CreateForHttpServer() override {
+    std::unique_ptr<net::ServerSocket> socket(
+        new net::TCPServerSocket(nullptr, net::NetLogSource()));
+    if (socket->ListenWithAddressAndPort(address_, port_, kBackLog) != net::OK)
+      return std::unique_ptr<net::ServerSocket>();
+    return socket;
+  }
+
+  std::unique_ptr<net::ServerSocket> CreateForTethering(
+      std::string* out_name) override {
+    return nullptr;
+  }
+
+  std::string address_;
+  uint16_t port_;
+
+  DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory);
+};
+
+std::unique_ptr<content::DevToolsSocketFactory> CreateSocketFactory() {
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  // See if the user specified a port on the command line. Specifying 0 would
+  // result in the selection of an ephemeral port but that doesn't make sense
+  // for CEF where the URL is otherwise undiscoverable. Also, don't allow
+  // binding of ports between 0 and 1024 exclusive because they're normally
+  // restricted to root on Posix-based systems.
+  uint16_t port = 0;
+  if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) {
+    int temp_port;
+    std::string port_str =
+        command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort);
+    if (base::StringToInt(port_str, &temp_port) && temp_port >= 1024 &&
+        temp_port < 65535) {
+      port = static_cast<uint16_t>(temp_port);
+    } else {
+      DLOG(WARNING) << "Invalid http debugger port number " << temp_port;
+    }
+  }
+  if (port == 0)
+    return nullptr;
+  return std::unique_ptr<content::DevToolsSocketFactory>(
+      new TCPServerSocketFactory("127.0.0.1", port));
+}
+
+}  //  namespace
+
+// CefDevToolsManagerDelegate ----------------------------------------------
+
+// static
+void CefDevToolsManagerDelegate::StartHttpHandler(
+    content::BrowserContext* browser_context) {
+  std::unique_ptr<content::DevToolsSocketFactory> socket_factory =
+      CreateSocketFactory();
+  if (!socket_factory)
+    return;
+  content::DevToolsAgentHost::StartRemoteDebuggingServer(
+      std::move(socket_factory), browser_context->GetPath(), base::FilePath());
+
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  if (command_line.HasSwitch(switches::kRemoteDebuggingPipe))
+    content::DevToolsAgentHost::StartRemoteDebuggingPipeHandler();
+}
+
+// static
+void CefDevToolsManagerDelegate::StopHttpHandler() {
+  // This is a no-op if the server was never started.
+  content::DevToolsAgentHost::StopRemoteDebuggingServer();
+}
+
+CefDevToolsManagerDelegate::CefDevToolsManagerDelegate() {}
+
+CefDevToolsManagerDelegate::~CefDevToolsManagerDelegate() {}
+
+scoped_refptr<content::DevToolsAgentHost>
+CefDevToolsManagerDelegate::CreateNewTarget(const GURL& url) {
+  // This is reached when the user selects "Open link in new tab" from the
+  // DevTools interface.
+  // TODO(cef): Consider exposing new API to support this.
+  return nullptr;
+}
+
+std::string CefDevToolsManagerDelegate::GetDiscoveryPageHTML() {
+  return ui::ResourceBundle::GetSharedInstance()
+      .GetRawDataResource(IDR_CEF_DEVTOOLS_DISCOVERY_PAGE)
+      .as_string();
+}
+
+bool CefDevToolsManagerDelegate::HasBundledFrontendResources() {
+  return true;
+}
diff --git a/src/libcef/browser/devtools/devtools_manager_delegate.h b/src/libcef/browser/devtools/devtools_manager_delegate.h
new file mode 100644
index 0000000..7e65fb2
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_manager_delegate.h
@@ -0,0 +1,34 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_DELEGATE_H_
+#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/devtools_manager_delegate.h"
+
+namespace content {
+class BrowserContext;
+}
+
+class CefDevToolsManagerDelegate : public content::DevToolsManagerDelegate {
+ public:
+  static void StartHttpHandler(content::BrowserContext* browser_context);
+  static void StopHttpHandler();
+
+  CefDevToolsManagerDelegate();
+  ~CefDevToolsManagerDelegate() override;
+
+  // DevToolsManagerDelegate implementation.
+  scoped_refptr<content::DevToolsAgentHost> CreateNewTarget(
+      const GURL& url) override;
+  std::string GetDiscoveryPageHTML() override;
+  bool HasBundledFrontendResources() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefDevToolsManagerDelegate);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_DELEGATE_H_
diff --git a/src/libcef/browser/devtools/devtools_util.cc b/src/libcef/browser/devtools/devtools_util.cc
new file mode 100644
index 0000000..c0200cf
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_util.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/devtools/devtools_util.h"
+
+#include "base/strings/string_number_conversions.h"
+
+namespace devtools_util {
+
+namespace {
+
+bool IsValidDictionary(const base::StringPiece& str, bool allow_empty) {
+  return str.length() >= (allow_empty ? 2 : 3) && str[0] == '{' &&
+         str[str.length() - 1] == '}';
+}
+
+// Example:
+// {"method":"Target.targetDestroyed","params":{"targetId":"1234..."}}
+bool ParseEvent(const base::StringPiece& message,
+                base::StringPiece& method,
+                base::StringPiece& params) {
+  static const char kMethodStart[] = "{\"method\":\"";
+  static const char kMethodEnd[] = "\"";
+  static const char kParamsStart[] = ",\"params\":";
+
+  if (!message.starts_with(kMethodStart))
+    return false;
+
+  const size_t method_start = sizeof(kMethodStart) - 1;
+  const size_t method_end = message.find(kMethodEnd, method_start);
+  if (method_end < 0U)
+    return false;
+  method = message.substr(method_start, method_end - method_start);
+  if (method.empty())
+    return false;
+
+  size_t remainder_start = method_end + sizeof(kMethodEnd) - 1;
+  if (remainder_start == message.size() - 1) {
+    // No more contents.
+    params = base::StringPiece();
+  } else {
+    const base::StringPiece& remainder = message.substr(remainder_start);
+    if (remainder.starts_with(kParamsStart)) {
+      // Stop immediately before the message closing bracket.
+      remainder_start += sizeof(kParamsStart) - 1;
+      params =
+          message.substr(remainder_start, message.size() - 1 - remainder_start);
+    } else {
+      // Invalid format.
+      return false;
+    }
+
+    if (!IsValidDictionary(params, /*allow_empty=*/true))
+      return false;
+  }
+
+  return true;
+}
+
+// Examples:
+// {"id":3,"result":{}}
+// {"id":4,"result":{"debuggerId":"-2193881606781505058.81393575456727957"}}
+// {"id":5,"error":{"code":-32000,"message":"Not supported"}}
+bool ParseResult(const base::StringPiece& message,
+                 int& message_id,
+                 bool& success,
+                 base::StringPiece& result) {
+  static const char kIdStart[] = "{\"id\":";
+  static const char kIdEnd[] = ",";
+  static const char kResultStart[] = "\"result\":";
+  static const char kErrorStart[] = "\"error\":";
+
+  if (!message.starts_with(kIdStart))
+    return false;
+
+  const size_t id_start = sizeof(kIdStart) - 1;
+  const size_t id_end = message.find(kIdEnd, id_start);
+  if (id_end < 0U)
+    return false;
+  const base::StringPiece& id_str = message.substr(id_start, id_end - id_start);
+  if (id_str.empty() || !base::StringToInt(id_str, &message_id))
+    return false;
+
+  size_t remainder_start = id_end + sizeof(kIdEnd) - 1;
+  const base::StringPiece& remainder = message.substr(remainder_start);
+  if (remainder.starts_with(kResultStart)) {
+    // Stop immediately before the message closing bracket.
+    remainder_start += sizeof(kResultStart) - 1;
+    result =
+        message.substr(remainder_start, message.size() - 1 - remainder_start);
+    success = true;
+  } else if (remainder.starts_with(kErrorStart)) {
+    // Stop immediately before the message closing bracket.
+    remainder_start += sizeof(kErrorStart) - 1;
+    result =
+        message.substr(remainder_start, message.size() - 1 - remainder_start);
+    success = false;
+  } else {
+    // Invalid format.
+    return false;
+  }
+
+  if (!IsValidDictionary(result, /*allow_empty=*/true))
+    return false;
+
+  return true;
+}
+
+}  // namespace
+
+// static
+bool ProtocolParser::IsValidMessage(const base::StringPiece& message) {
+  return IsValidDictionary(message, /*allow_empty=*/false);
+}
+
+bool ProtocolParser::Initialize(const base::StringPiece& message) {
+  if (status_ != UNINITIALIZED)
+    return false;
+
+  if (ParseEvent(message, method_, params_)) {
+    status_ = EVENT;
+  } else if (ParseResult(message, message_id_, success_, params_)) {
+    status_ = RESULT;
+  } else {
+    status_ = FAILURE;
+  }
+  return true;
+}
+
+}  // namespace devtools_util
diff --git a/src/libcef/browser/devtools/devtools_util.h b/src/libcef/browser/devtools/devtools_util.h
new file mode 100644
index 0000000..848e168
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_util.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_UTIL_H_
+#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_UTIL_H_
+#pragma once
+
+#include "base/strings/string_piece.h"
+
+namespace devtools_util {
+
+// Fast parser for DevTools JSON protocol messages. This implementation makes
+// certain assumptions about the JSON object structure (value order and
+// formatting) to avoid stateful parsing of messages that may be large
+// (sometimes > 1MB in size). The message must be a JSON dictionary that starts
+// with a "method" or "id" value which is non-empty and of the expected data
+// type. Messages that have a "method" value (event message) may optionally have
+// a "params" dictionary. Messages that have an "id" value (result message) must
+// have a "result" or "error" dictionary. The dictionary contents are not
+// validated and may be empty ("{}").
+//
+// Example event message:
+// {"method":"Target.targetDestroyed","params":{"targetId":"1234..."}}
+//
+// Example result messages:
+// {"id":3,"result":{}}
+// {"id":4,"result":{"debuggerId":"-2193881606781505058.81393575456727957"}}
+// {"id":5,"error":{"code":-32000,"message":"Not supported"}}
+struct ProtocolParser {
+  ProtocolParser() = default;
+
+  // Checks for a non-empty JSON dictionary.
+  static bool IsValidMessage(const base::StringPiece& message);
+
+  // Returns false if already initialized.
+  bool Initialize(const base::StringPiece& message);
+
+  bool IsInitialized() const { return status_ != UNINITIALIZED; }
+  bool IsEvent() const { return status_ == EVENT; }
+  bool IsResult() const { return status_ == RESULT; }
+  bool IsFailure() const { return status_ == FAILURE; }
+
+  void Reset() { status_ = UNINITIALIZED; }
+
+  // For event messages:
+  //   "method" string:
+  base::StringPiece method_;
+
+  // For result messages:
+  //   "id" int:
+  int message_id_ = 0;
+  //   true if "result" value, false if "error" value:
+  bool success_ = false;
+
+  // For both:
+  //   "params", "result" or "error" dictionary:
+  base::StringPiece params_;
+
+ private:
+  enum Status {
+    UNINITIALIZED,
+    EVENT,    // Event message.
+    RESULT,   // Result message.
+    FAILURE,  // Parsing failure.
+  };
+  Status status_ = UNINITIALIZED;
+};
+
+}  // namespace devtools_util
+
+#endif  // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_UTIL_H_
\ No newline at end of file
diff --git a/src/libcef/browser/devtools/devtools_util_unittest.cc b/src/libcef/browser/devtools/devtools_util_unittest.cc
new file mode 100644
index 0000000..0175aa0
--- /dev/null
+++ b/src/libcef/browser/devtools/devtools_util_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/devtools/devtools_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace devtools_util;
+
+TEST(DevToolsUtil, ProtocolParser_IsValidMessage) {
+  // Empty dictionary is not valid.
+  EXPECT_FALSE(ProtocolParser::IsValidMessage(""));
+  EXPECT_FALSE(ProtocolParser::IsValidMessage("{}"));
+
+  // Incorrectly formatted dictionary is not valid.
+  EXPECT_FALSE(ProtocolParser::IsValidMessage("{ ]"));
+
+  // Everything else is valid (we don't verify JSON structure).
+  EXPECT_TRUE(ProtocolParser::IsValidMessage("{ }"));
+  EXPECT_TRUE(ProtocolParser::IsValidMessage("{blah blah}"));
+  EXPECT_TRUE(ProtocolParser::IsValidMessage("{method:\"foobar\"}"));
+}
+
+TEST(DevToolsUtil, ProtocolParser_Initialize_IsFailure_Unknown) {
+  ProtocolParser parser;
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Empty message is invalid.
+  EXPECT_TRUE(parser.Initialize(""));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Empty dictionary is invalid.
+  EXPECT_TRUE(parser.Initialize("{}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Unrecognized dictionary type is invalid.
+  EXPECT_TRUE(parser.Initialize("{blah blah}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+}
+
+TEST(DevToolsUtil, ProtocolParser_Initialize_IsFailure_EventMalformed) {
+  ProtocolParser parser;
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Empty method is invalid.
+  EXPECT_TRUE(parser.Initialize("{\"method\":\"\"}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Unrecognized value is invalid.
+  EXPECT_TRUE(parser.Initialize("{\"method\":\"foo\",oops:false}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Params must be a dictionary.
+  EXPECT_TRUE(parser.Initialize("{\"method\":\",params:[]}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+}
+
+TEST(DevToolsUtil, ProtocolParser_Initialize_IsEvent) {
+  ProtocolParser parser;
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Method without params is valid.
+  std::string message = "{\"method\":\"Test.myMethod\"}";
+  EXPECT_TRUE(parser.Initialize(message));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsEvent());
+  EXPECT_STREQ("Test.myMethod", parser.method_.as_string().data());
+  EXPECT_TRUE(parser.params_.empty());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Method with empty params dictionary is valid.
+  message = "{\"method\":\"Test.myMethod2\",\"params\":{}}";
+  EXPECT_TRUE(parser.Initialize(message));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsEvent());
+  EXPECT_STREQ("Test.myMethod2", parser.method_.as_string().data());
+  EXPECT_STREQ("{}", parser.params_.as_string().data());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Method with non-empty params dictionary is valid.
+  message = "{\"method\":\"Test.myMethod3\",\"params\":{\"foo\":\"bar\"}}";
+  EXPECT_TRUE(parser.Initialize(message));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsEvent());
+  EXPECT_STREQ("Test.myMethod3", parser.method_.as_string().data());
+  EXPECT_STREQ("{\"foo\":\"bar\"}", parser.params_.as_string().data());
+}
+
+TEST(DevToolsUtil, ProtocolParser_Initialize_IsFailure_ResultMalformed) {
+  ProtocolParser parser;
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Empty ID is invalid.
+  EXPECT_TRUE(parser.Initialize("{\"id\":,result:{}}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Missing result or error value is invalid.
+  EXPECT_TRUE(parser.Initialize("{\"id\":1}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Unrecognized value is invalid.
+  EXPECT_TRUE(parser.Initialize("{\"id\":1,oops:false}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Result must be a dictionary.
+  EXPECT_TRUE(parser.Initialize("{\"id\":1,\"result\":[]}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Error must be a dictionary.
+  EXPECT_TRUE(parser.Initialize("{\"id\":1,\"error\":[]}"));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsFailure());
+}
+
+TEST(DevToolsUtil, ProtocolParser_Initialize_IsResult_Result) {
+  ProtocolParser parser;
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Id with empty result dictionary is valid.
+  std::string message = "{\"id\":1,\"result\":{}}";
+  EXPECT_TRUE(parser.Initialize(message));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsResult());
+  EXPECT_EQ(1, parser.message_id_);
+  EXPECT_TRUE(parser.success_);
+  EXPECT_STREQ("{}", parser.params_.as_string().data());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Id with non-empty result dictionary is valid.
+  message = "{\"id\":2,\"result\":{\"foo\":\"bar\"}}";
+  EXPECT_TRUE(parser.Initialize(message));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsResult());
+  EXPECT_EQ(2, parser.message_id_);
+  EXPECT_TRUE(parser.success_);
+  EXPECT_STREQ("{\"foo\":\"bar\"}", parser.params_.as_string().data());
+}
+
+TEST(DevToolsUtil, ProtocolParser_Initialize_IsResult_Error) {
+  ProtocolParser parser;
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Id with empty error dictionary is valid.
+  std::string message = "{\"id\":1,\"error\":{}}";
+  EXPECT_TRUE(parser.Initialize(message));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsResult());
+  EXPECT_EQ(1, parser.message_id_);
+  EXPECT_FALSE(parser.success_);
+  EXPECT_STREQ("{}", parser.params_.as_string().data());
+
+  parser.Reset();
+  EXPECT_FALSE(parser.IsInitialized());
+
+  // Id with non-empty error dictionary is valid.
+  message = "{\"id\":2,\"error\":{\"foo\":\"bar\"}}";
+  EXPECT_TRUE(parser.Initialize(message));
+  EXPECT_TRUE(parser.IsInitialized());
+  EXPECT_TRUE(parser.IsResult());
+  EXPECT_EQ(2, parser.message_id_);
+  EXPECT_FALSE(parser.success_);
+  EXPECT_STREQ("{\"foo\":\"bar\"}", parser.params_.as_string().data());
+}
diff --git a/src/libcef/browser/download_item_impl.cc b/src/libcef/browser/download_item_impl.cc
new file mode 100644
index 0000000..bffb74e
--- /dev/null
+++ b/src/libcef/browser/download_item_impl.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/download_item_impl.h"
+
+#include "libcef/common/time_util.h"
+
+#include "components/download/public/common/download_item.h"
+#include "url/gurl.h"
+
+CefDownloadItemImpl::CefDownloadItemImpl(download::DownloadItem* value)
+    : CefValueBase<CefDownloadItem, download::DownloadItem>(
+          value,
+          nullptr,
+          kOwnerNoDelete,
+          true,
+          new CefValueControllerNonThreadSafe()) {
+  // Indicate that this object owns the controller.
+  SetOwnsController();
+}
+
+bool CefDownloadItemImpl::IsValid() {
+  return !detached();
+}
+
+bool CefDownloadItemImpl::IsInProgress() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().GetState() == download::DownloadItem::IN_PROGRESS;
+}
+
+bool CefDownloadItemImpl::IsComplete() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().GetState() == download::DownloadItem::COMPLETE;
+}
+
+bool CefDownloadItemImpl::IsCanceled() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().GetState() == download::DownloadItem::CANCELLED;
+}
+
+int64 CefDownloadItemImpl::GetCurrentSpeed() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().CurrentSpeed();
+}
+
+int CefDownloadItemImpl::GetPercentComplete() {
+  CEF_VALUE_VERIFY_RETURN(false, -1);
+  return const_value().PercentComplete();
+}
+
+int64 CefDownloadItemImpl::GetTotalBytes() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().GetTotalBytes();
+}
+
+int64 CefDownloadItemImpl::GetReceivedBytes() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().GetReceivedBytes();
+}
+
+CefTime CefDownloadItemImpl::GetStartTime() {
+  CefTime time;
+  CEF_VALUE_VERIFY_RETURN(false, time);
+  cef_time_from_basetime(const_value().GetStartTime(), time);
+  return time;
+}
+
+CefTime CefDownloadItemImpl::GetEndTime() {
+  CefTime time;
+  CEF_VALUE_VERIFY_RETURN(false, time);
+  cef_time_from_basetime(const_value().GetEndTime(), time);
+  return time;
+}
+
+CefString CefDownloadItemImpl::GetFullPath() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetFullPath().value();
+}
+
+uint32 CefDownloadItemImpl::GetId() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().GetId();
+}
+
+CefString CefDownloadItemImpl::GetURL() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetURL().spec();
+}
+
+CefString CefDownloadItemImpl::GetOriginalUrl() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetOriginalUrl().spec();
+}
+
+CefString CefDownloadItemImpl::GetSuggestedFileName() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetSuggestedFilename();
+}
+
+CefString CefDownloadItemImpl::GetContentDisposition() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetContentDisposition();
+}
+
+CefString CefDownloadItemImpl::GetMimeType() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetMimeType();
+}
diff --git a/src/libcef/browser/download_item_impl.h b/src/libcef/browser/download_item_impl.h
new file mode 100644
index 0000000..ab7f4de
--- /dev/null
+++ b/src/libcef/browser/download_item_impl.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DOWNLOAD_ITEM_IMPL_H_
+#define CEF_LIBCEF_BROWSER_DOWNLOAD_ITEM_IMPL_H_
+#pragma once
+
+#include "include/cef_download_item.h"
+#include "libcef/common/value_base.h"
+
+namespace download {
+class DownloadItem;
+}
+
+// CefDownloadItem implementation
+class CefDownloadItemImpl
+    : public CefValueBase<CefDownloadItem, download::DownloadItem> {
+ public:
+  explicit CefDownloadItemImpl(download::DownloadItem* value);
+
+  // CefDownloadItem methods.
+  bool IsValid() override;
+  bool IsInProgress() override;
+  bool IsComplete() override;
+  bool IsCanceled() override;
+  int64 GetCurrentSpeed() override;
+  int GetPercentComplete() override;
+  int64 GetTotalBytes() override;
+  int64 GetReceivedBytes() override;
+  CefTime GetStartTime() override;
+  CefTime GetEndTime() override;
+  CefString GetFullPath() override;
+  uint32 GetId() override;
+  CefString GetURL() override;
+  CefString GetOriginalUrl() override;
+  CefString GetSuggestedFileName() override;
+  CefString GetContentDisposition() override;
+  CefString GetMimeType() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefDownloadItemImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_DOWNLOAD_ITEM_IMPL_H_
diff --git a/src/libcef/browser/download_manager_delegate.cc b/src/libcef/browser/download_manager_delegate.cc
new file mode 100644
index 0000000..e15c9a5
--- /dev/null
+++ b/src/libcef/browser/download_manager_delegate.cc
@@ -0,0 +1,457 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/download_manager_delegate.h"
+
+#include "include/cef_download_handler.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/download_item_impl.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/common/chrome_constants.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/download_item_utils.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/filename_util.h"
+#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
+
+using content::DownloadManager;
+using content::WebContents;
+using download::DownloadItem;
+
+namespace {
+
+// Helper function to retrieve the CefDownloadHandler.
+CefRefPtr<CefDownloadHandler> GetDownloadHandler(
+    CefRefPtr<CefBrowserHostImpl> browser) {
+  CefRefPtr<CefClient> client = browser->GetClient();
+  if (client.get())
+    return client->GetDownloadHandler();
+  return nullptr;
+}
+
+// CefBeforeDownloadCallback implementation.
+class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
+ public:
+  CefBeforeDownloadCallbackImpl(const base::WeakPtr<DownloadManager>& manager,
+                                uint32 download_id,
+                                const base::FilePath& suggested_name,
+                                content::DownloadTargetCallback callback)
+      : manager_(manager),
+        download_id_(download_id),
+        suggested_name_(suggested_name),
+        callback_(std::move(callback)) {}
+
+  void Continue(const CefString& download_path, bool show_dialog) override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (download_id_ <= 0)
+        return;
+
+      if (manager_) {
+        base::FilePath path = base::FilePath(download_path);
+        CEF_POST_USER_VISIBLE_TASK(
+            base::BindOnce(&CefBeforeDownloadCallbackImpl::GenerateFilename,
+                           manager_, download_id_, suggested_name_, path,
+                           show_dialog, std::move(callback_)));
+      }
+
+      download_id_ = 0;
+    } else {
+      CEF_POST_TASK(CEF_UIT,
+                    base::BindOnce(&CefBeforeDownloadCallbackImpl::Continue,
+                                   this, download_path, show_dialog));
+    }
+  }
+
+ private:
+  static void GenerateFilename(base::WeakPtr<DownloadManager> manager,
+                               uint32 download_id,
+                               const base::FilePath& suggested_name,
+                               const base::FilePath& download_path,
+                               bool show_dialog,
+                               content::DownloadTargetCallback callback) {
+    CEF_REQUIRE_BLOCKING();
+
+    base::FilePath suggested_path = download_path;
+    if (!suggested_path.empty()) {
+      // Create the directory if necessary.
+      base::FilePath dir_path = suggested_path.DirName();
+      if (!base::DirectoryExists(dir_path) &&
+          !base::CreateDirectory(dir_path)) {
+        NOTREACHED() << "failed to create the download directory";
+        suggested_path.clear();
+      }
+    }
+
+    if (suggested_path.empty()) {
+      if (base::PathService::Get(base::DIR_TEMP, &suggested_path)) {
+        // Use the temp directory.
+        suggested_path = suggested_path.Append(suggested_name);
+      } else {
+        // Use the current working directory.
+        suggested_path = suggested_name;
+      }
+    }
+
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefBeforeDownloadCallbackImpl::ChooseDownloadPath,
+                       manager, download_id, suggested_path, show_dialog,
+                       std::move(callback)));
+  }
+
+  static void ChooseDownloadPath(base::WeakPtr<DownloadManager> manager,
+                                 uint32 download_id,
+                                 const base::FilePath& suggested_path,
+                                 bool show_dialog,
+                                 content::DownloadTargetCallback callback) {
+    if (!manager)
+      return;
+
+    DownloadItem* item = manager->GetDownload(download_id);
+    if (!item || item->GetState() != DownloadItem::IN_PROGRESS)
+      return;
+
+    bool handled = false;
+
+    if (show_dialog) {
+      WebContents* web_contents =
+          content::DownloadItemUtils::GetWebContents(item);
+      CefRefPtr<CefBrowserHostImpl> browser =
+          CefBrowserHostImpl::GetBrowserForContents(web_contents);
+      if (browser.get()) {
+        handled = true;
+
+        CefFileDialogRunner::FileChooserParams params;
+        params.mode = blink::mojom::FileChooserParams::Mode::kSave;
+        if (!suggested_path.empty()) {
+          params.default_file_name = suggested_path;
+          if (!suggested_path.Extension().empty()) {
+            params.accept_types.push_back(
+                CefString(suggested_path.Extension()));
+          }
+        }
+
+        browser->RunFileChooser(
+            params,
+            base::BindOnce(
+                &CefBeforeDownloadCallbackImpl::ChooseDownloadPathCallback,
+                std::move(callback)));
+      }
+    }
+
+    if (!handled) {
+      std::move(callback).Run(
+          suggested_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+          download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+          download::DownloadItem::MixedContentStatus::UNKNOWN, suggested_path,
+          download::DOWNLOAD_INTERRUPT_REASON_NONE);
+    }
+  }
+
+  static void ChooseDownloadPathCallback(
+      content::DownloadTargetCallback callback,
+      int selected_accept_filter,
+      const std::vector<base::FilePath>& file_paths) {
+    DCHECK_LE(file_paths.size(), (size_t)1);
+
+    base::FilePath path;
+    if (file_paths.size() > 0)
+      path = file_paths.front();
+
+    // The download will be cancelled if |path| is empty.
+    std::move(callback).Run(path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+                            download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+                            download::DownloadItem::MixedContentStatus::UNKNOWN,
+                            path, download::DOWNLOAD_INTERRUPT_REASON_NONE);
+  }
+
+  base::WeakPtr<DownloadManager> manager_;
+  uint32 download_id_;
+  base::FilePath suggested_name_;
+  content::DownloadTargetCallback callback_;
+
+  IMPLEMENT_REFCOUNTING(CefBeforeDownloadCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefBeforeDownloadCallbackImpl);
+};
+
+// CefDownloadItemCallback implementation.
+class CefDownloadItemCallbackImpl : public CefDownloadItemCallback {
+ public:
+  explicit CefDownloadItemCallbackImpl(
+      const base::WeakPtr<DownloadManager>& manager,
+      uint32 download_id)
+      : manager_(manager), download_id_(download_id) {}
+
+  void Cancel() override {
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefDownloadItemCallbackImpl::DoCancel, this));
+  }
+
+  void Pause() override {
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefDownloadItemCallbackImpl::DoPause, this));
+  }
+
+  void Resume() override {
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefDownloadItemCallbackImpl::DoResume, this));
+  }
+
+ private:
+  void DoCancel() {
+    if (download_id_ <= 0)
+      return;
+
+    if (manager_) {
+      DownloadItem* item = manager_->GetDownload(download_id_);
+      if (item && item->GetState() == DownloadItem::IN_PROGRESS)
+        item->Cancel(true);
+    }
+
+    download_id_ = 0;
+  }
+
+  void DoPause() {
+    if (download_id_ <= 0)
+      return;
+
+    if (manager_) {
+      DownloadItem* item = manager_->GetDownload(download_id_);
+      if (item && item->GetState() == DownloadItem::IN_PROGRESS)
+        item->Pause();
+    }
+  }
+
+  void DoResume() {
+    if (download_id_ <= 0)
+      return;
+
+    if (manager_) {
+      DownloadItem* item = manager_->GetDownload(download_id_);
+      if (item && item->CanResume())
+        item->Resume(true);
+    }
+  }
+
+  base::WeakPtr<DownloadManager> manager_;
+  uint32 download_id_;
+
+  IMPLEMENT_REFCOUNTING(CefDownloadItemCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefDownloadItemCallbackImpl);
+};
+
+}  // namespace
+
+CefDownloadManagerDelegate::CefDownloadManagerDelegate(DownloadManager* manager)
+    : manager_(manager), manager_ptr_factory_(manager) {
+  DCHECK(manager);
+  manager->AddObserver(this);
+
+  DownloadManager::DownloadVector items;
+  manager->GetAllDownloads(&items);
+  DownloadManager::DownloadVector::const_iterator it = items.begin();
+  for (; it != items.end(); ++it)
+    OnDownloadCreated(manager, *it);
+}
+
+CefDownloadManagerDelegate::~CefDownloadManagerDelegate() {
+  if (manager_) {
+    manager_->SetDelegate(nullptr);
+    manager_->RemoveObserver(this);
+  }
+
+  while (!item_browser_map_.empty())
+    OnDownloadDestroyed(item_browser_map_.begin()->first);
+}
+
+void CefDownloadManagerDelegate::OnDownloadUpdated(DownloadItem* download) {
+  CefRefPtr<CefBrowserHostImpl> browser = GetBrowser(download);
+  CefRefPtr<CefDownloadHandler> handler;
+  if (browser.get())
+    handler = GetDownloadHandler(browser);
+
+  if (handler.get()) {
+    CefRefPtr<CefDownloadItemImpl> download_item(
+        new CefDownloadItemImpl(download));
+    CefRefPtr<CefDownloadItemCallback> callback(new CefDownloadItemCallbackImpl(
+        manager_ptr_factory_.GetWeakPtr(), download->GetId()));
+
+    handler->OnDownloadUpdated(browser.get(), download_item.get(), callback);
+
+    download_item->Detach(nullptr);
+  }
+}
+
+void CefDownloadManagerDelegate::OnDownloadDestroyed(DownloadItem* item) {
+  item->RemoveObserver(this);
+
+  CefBrowserHostImpl* browser = nullptr;
+
+  ItemBrowserMap::iterator it = item_browser_map_.find(item);
+  DCHECK(it != item_browser_map_.end());
+  if (it != item_browser_map_.end()) {
+    browser = it->second;
+    item_browser_map_.erase(it);
+  }
+
+  if (browser) {
+    // Determine if any remaining DownloadItems are associated with the same
+    // browser. If not, then unregister as an observer.
+    bool has_remaining = false;
+    ItemBrowserMap::const_iterator it2 = item_browser_map_.begin();
+    for (; it2 != item_browser_map_.end(); ++it2) {
+      if (it2->second == browser) {
+        has_remaining = true;
+        break;
+      }
+    }
+
+    if (!has_remaining)
+      browser->RemoveObserver(this);
+  }
+}
+
+void CefDownloadManagerDelegate::OnDownloadCreated(DownloadManager* manager,
+                                                   DownloadItem* item) {
+  // This callback may arrive after DetermineDownloadTarget, so we allow
+  // association from either method.
+  CefRefPtr<CefBrowserHostImpl> browser = GetOrAssociateBrowser(item);
+  if (!browser) {
+    // If the download is rejected (e.g. ALT+click on an invalid protocol link)
+    // then an "interrupted" download will be started via DownloadManagerImpl::
+    // StartDownloadWithId (originating from CreateInterruptedDownload) with no
+    // associated WebContents and consequently no associated CEF browser. In
+    // that case DetermineDownloadTarget will be called before this method.
+    // TODO(cef): Figure out how to expose this via a client callback.
+    const std::vector<GURL>& url_chain = item->GetUrlChain();
+    if (!url_chain.empty()) {
+      LOG(INFO) << "Rejected download of " << url_chain.back().spec();
+    }
+    item->Cancel(true);
+  }
+}
+
+void CefDownloadManagerDelegate::ManagerGoingDown(DownloadManager* manager) {
+  DCHECK_EQ(manager, manager_);
+  manager->SetDelegate(nullptr);
+  manager->RemoveObserver(this);
+  manager_ptr_factory_.InvalidateWeakPtrs();
+  manager_ = nullptr;
+}
+
+bool CefDownloadManagerDelegate::DetermineDownloadTarget(
+    DownloadItem* item,
+    content::DownloadTargetCallback* callback) {
+  if (!item->GetForcedFilePath().empty()) {
+    std::move(*callback).Run(
+        item->GetForcedFilePath(), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+        download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+        download::DownloadItem::MixedContentStatus::UNKNOWN,
+        item->GetForcedFilePath(), download::DOWNLOAD_INTERRUPT_REASON_NONE);
+    return true;
+  }
+
+  // This callback may arrive before OnDownloadCreated, so we allow association
+  // from either method.
+  CefRefPtr<CefBrowserHostImpl> browser = GetOrAssociateBrowser(item);
+  CefRefPtr<CefDownloadHandler> handler;
+  if (browser.get())
+    handler = GetDownloadHandler(browser);
+
+  if (handler.get()) {
+    base::FilePath suggested_name = net::GenerateFileName(
+        item->GetURL(), item->GetContentDisposition(), std::string(),
+        item->GetSuggestedFilename(), item->GetMimeType(), "download");
+
+    CefRefPtr<CefDownloadItemImpl> download_item(new CefDownloadItemImpl(item));
+    CefRefPtr<CefBeforeDownloadCallback> callbackObj(
+        new CefBeforeDownloadCallbackImpl(manager_ptr_factory_.GetWeakPtr(),
+                                          item->GetId(), suggested_name,
+                                          std::move(*callback)));
+
+    handler->OnBeforeDownload(browser.get(), download_item.get(),
+                              suggested_name.value(), callbackObj);
+
+    download_item->Detach(nullptr);
+  }
+
+  return true;
+}
+
+void CefDownloadManagerDelegate::GetNextId(
+    content::DownloadIdCallback callback) {
+  static uint32 next_id = DownloadItem::kInvalidId + 1;
+  std::move(callback).Run(next_id++);
+}
+
+std::string CefDownloadManagerDelegate::ApplicationClientIdForFileScanning() {
+  const CefSettings& settings = CefContext::Get()->settings();
+  if (settings.application_client_id_for_file_scanning.length > 0) {
+    return CefString(&settings.application_client_id_for_file_scanning)
+        .ToString();
+  }
+  return std::string();
+}
+
+void CefDownloadManagerDelegate::OnBrowserDestroyed(
+    CefBrowserHostImpl* browser) {
+  ItemBrowserMap::iterator it = item_browser_map_.begin();
+  for (; it != item_browser_map_.end(); ++it) {
+    if (it->second == browser) {
+      // Don't call back into browsers that have been destroyed. We're not
+      // canceling the download so it will continue silently until it completes
+      // or until the associated browser context is destroyed.
+      it->second = nullptr;
+    }
+  }
+}
+
+CefBrowserHostImpl* CefDownloadManagerDelegate::GetOrAssociateBrowser(
+    download::DownloadItem* item) {
+  ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
+  if (it != item_browser_map_.end())
+    return it->second;
+
+  CefBrowserHostImpl* browser = nullptr;
+  content::WebContents* contents =
+      content::DownloadItemUtils::GetWebContents(item);
+  if (contents) {
+    browser = CefBrowserHostImpl::GetBrowserForContents(contents).get();
+    DCHECK(browser);
+  }
+  if (!browser)
+    return nullptr;
+
+  item->AddObserver(this);
+
+  item_browser_map_.insert(std::make_pair(item, browser));
+
+  // Register as an observer so that we can cancel associated DownloadItems when
+  // the browser is destroyed.
+  if (!browser->HasObserver(this))
+    browser->AddObserver(this);
+
+  return browser;
+}
+
+CefBrowserHostImpl* CefDownloadManagerDelegate::GetBrowser(DownloadItem* item) {
+  ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
+  if (it != item_browser_map_.end())
+    return it->second;
+
+  // If the download is rejected (e.g. ALT+click on an invalid protocol link)
+  // then an "interrupted" download will be started via DownloadManagerImpl::
+  // StartDownloadWithId (originating from CreateInterruptedDownload) with no
+  // associated WebContents and consequently no associated CEF browser. In that
+  // case DetermineDownloadTarget will be called before OnDownloadCreated.
+  DCHECK(!content::DownloadItemUtils::GetWebContents(item));
+  return nullptr;
+}
diff --git a/src/libcef/browser/download_manager_delegate.h b/src/libcef/browser/download_manager_delegate.h
new file mode 100644
index 0000000..86a54ba
--- /dev/null
+++ b/src/libcef/browser/download_manager_delegate.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_H_
+#define CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_H_
+#pragma once
+
+#include <set>
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/public/common/download_item.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/download_manager_delegate.h"
+
+class CefDownloadManagerDelegate : public download::DownloadItem::Observer,
+                                   public content::DownloadManager::Observer,
+                                   public content::DownloadManagerDelegate,
+                                   public CefBrowserHostImpl::Observer {
+ public:
+  explicit CefDownloadManagerDelegate(content::DownloadManager* manager);
+  ~CefDownloadManagerDelegate() override;
+
+ private:
+  // DownloadItem::Observer methods.
+  void OnDownloadUpdated(download::DownloadItem* item) override;
+  void OnDownloadDestroyed(download::DownloadItem* item) override;
+
+  // DownloadManager::Observer methods.
+  void OnDownloadCreated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void ManagerGoingDown(content::DownloadManager* manager) override;
+
+  // DownloadManagerDelegate methods.
+  bool DetermineDownloadTarget(
+      download::DownloadItem* item,
+      content::DownloadTargetCallback* callback) override;
+  void GetNextId(content::DownloadIdCallback callback) override;
+  std::string ApplicationClientIdForFileScanning() override;
+
+  // CefBrowserHostImpl::Observer methods.
+  void OnBrowserDestroyed(CefBrowserHostImpl* browser) override;
+
+  CefBrowserHostImpl* GetOrAssociateBrowser(download::DownloadItem* item);
+  CefBrowserHostImpl* GetBrowser(download::DownloadItem* item);
+
+  content::DownloadManager* manager_;
+  base::WeakPtrFactory<content::DownloadManager> manager_ptr_factory_;
+
+  // Map of DownloadItem to originating CefBrowserHostImpl. Maintaining this
+  // map is necessary because DownloadItem::GetWebContents() may return NULL if
+  // the browser navigates while the download is in progress.
+  typedef std::map<download::DownloadItem*, CefBrowserHostImpl*> ItemBrowserMap;
+  ItemBrowserMap item_browser_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefDownloadManagerDelegate);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_DOWNLOAD_MANAGER_DELEGATE_H_
diff --git a/src/libcef/browser/extension_impl.cc b/src/libcef/browser/extension_impl.cc
new file mode 100644
index 0000000..5b7f677
--- /dev/null
+++ b/src/libcef/browser/extension_impl.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/extension_impl.h"
+
+#include "libcef/browser/extensions/extension_system.h"
+#include "libcef/browser/request_context_impl.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/values_impl.h"
+
+#include "extensions/common/extension.h"
+
+CefExtensionImpl::CefExtensionImpl(const extensions::Extension* extension,
+                                   CefRequestContext* loader_context,
+                                   CefRefPtr<CefExtensionHandler> handler)
+    : id_(extension->id()),
+      path_(extension->path().value()),
+      manifest_(
+          new CefDictionaryValueImpl(extension->manifest()->value()->DeepCopy(),
+                                     true,
+                                     true)),
+      loader_context_(loader_context),
+      handler_(handler) {}
+
+CefString CefExtensionImpl::GetIdentifier() {
+  return id_;
+}
+
+CefString CefExtensionImpl::GetPath() {
+  return path_;
+}
+
+CefRefPtr<CefDictionaryValue> CefExtensionImpl::GetManifest() {
+  return manifest_;
+}
+
+bool CefExtensionImpl::IsSame(CefRefPtr<CefExtension> that) {
+  CefExtensionImpl* that_impl = static_cast<CefExtensionImpl*>(that.get());
+  if (!that_impl)
+    return false;
+
+  // Maybe the same object.
+  if (this == that_impl)
+    return true;
+
+  return id_ == that_impl->id_ && path_ == that_impl->path_ &&
+         loader_context_ == that_impl->loader_context_;
+}
+
+CefRefPtr<CefExtensionHandler> CefExtensionImpl::GetHandler() {
+  return handler_;
+}
+
+CefRefPtr<CefRequestContext> CefExtensionImpl::GetLoaderContext() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  return loader_context_;
+}
+
+bool CefExtensionImpl::IsLoaded() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  return !unloaded_;
+}
+
+void CefExtensionImpl::Unload() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefExtensionImpl::Unload, this));
+    return;
+  }
+
+  // Will be NULL for internal extensions. They can't be unloaded.
+  if (!loader_context_)
+    return;
+
+  if (unloaded_)
+    return;
+
+  // CefExtensionHandler callbacks triggered by UnloadExtension may check this
+  // flag, so set it here.
+  unloaded_ = true;
+
+  const bool result = static_cast<CefRequestContextImpl*>(loader_context_)
+                          ->GetBrowserContext()
+                          ->extension_system()
+                          ->UnloadExtension(id_);
+  ALLOW_UNUSED_LOCAL(result);
+  DCHECK(result);
+}
+
+void CefExtensionImpl::OnExtensionLoaded() {
+  CEF_REQUIRE_UIT();
+  if (handler_)
+    handler_->OnExtensionLoaded(this);
+}
+
+void CefExtensionImpl::OnExtensionUnloaded() {
+  CEF_REQUIRE_UIT();
+  // Should not be called for internal extensions.
+  DCHECK(loader_context_);
+
+  unloaded_ = true;
+  loader_context_ = nullptr;
+
+  if (handler_)
+    handler_->OnExtensionUnloaded(this);
+}
diff --git a/src/libcef/browser/extension_impl.h b/src/libcef/browser/extension_impl.h
new file mode 100644
index 0000000..bcb6294
--- /dev/null
+++ b/src/libcef/browser/extension_impl.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSION_IMPL_H_
+#define CEF_LIBCEF_BROWSER_EXTENSION_IMPL_H_
+#pragma once
+
+#include <memory>
+
+#include "include/cef_extension.h"
+#include "include/cef_extension_handler.h"
+#include "include/cef_request_context.h"
+
+namespace extensions {
+class Extension;
+}
+
+// CefNavigationEntry implementation
+class CefExtensionImpl : public CefExtension {
+ public:
+  CefExtensionImpl(const extensions::Extension* extension,
+                   CefRequestContext* loader_context,
+                   CefRefPtr<CefExtensionHandler> handler);
+
+  // CefExtension methods.
+  CefString GetIdentifier() override;
+  CefString GetPath() override;
+  CefRefPtr<CefDictionaryValue> GetManifest() override;
+  bool IsSame(CefRefPtr<CefExtension> that) override;
+  CefRefPtr<CefExtensionHandler> GetHandler() override;
+  CefRefPtr<CefRequestContext> GetLoaderContext() override;
+  bool IsLoaded() override;
+  void Unload() override;
+
+  void OnExtensionLoaded();
+  void OnExtensionUnloaded();
+
+  // Use this instead of the GetLoaderContext version during
+  // CefRequestContext destruction.
+  CefRequestContext* loader_context() const { return loader_context_; }
+
+ private:
+  CefString id_;
+  CefString path_;
+  CefRefPtr<CefDictionaryValue> manifest_;
+
+  CefRequestContext* loader_context_;
+  CefRefPtr<CefExtensionHandler> handler_;
+
+  // Only accessed on the UI thread.
+  bool unloaded_ = false;
+
+  IMPLEMENT_REFCOUNTING(CefExtensionImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSION_IMPL_H_
diff --git a/src/libcef/browser/extensions/api/storage/sync_value_store_cache.cc b/src/libcef/browser/extensions/api/storage/sync_value_store_cache.cc
new file mode 100644
index 0000000..8a6ab7b
--- /dev/null
+++ b/src/libcef/browser/extensions/api/storage/sync_value_store_cache.cc
@@ -0,0 +1,97 @@
+// Copyright 2017 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/api/storage/sync_value_store_cache.h"
+
+#include <stddef.h>
+
+#include <limits>
+
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/api/storage/backend_task_runner.h"
+#include "extensions/browser/api/storage/weak_unlimited_settings_storage.h"
+#include "extensions/browser/value_store/value_store_factory.h"
+#include "extensions/common/api/storage.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/permissions/permissions_data.h"
+
+using content::BrowserThread;
+
+namespace extensions {
+
+namespace cef {
+
+namespace {
+
+// Returns the quota limit for local storage, taken from the schema in
+// extensions/common/api/storage.json.
+SettingsStorageQuotaEnforcer::Limits GetLocalQuotaLimits() {
+  SettingsStorageQuotaEnforcer::Limits limits = {
+      static_cast<size_t>(api::storage::local::QUOTA_BYTES),
+      std::numeric_limits<size_t>::max(), std::numeric_limits<size_t>::max()};
+  return limits;
+}
+
+}  // namespace
+
+SyncValueStoreCache::SyncValueStoreCache(
+    const scoped_refptr<ValueStoreFactory>& factory)
+    : storage_factory_(factory), quota_(GetLocalQuotaLimits()) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+SyncValueStoreCache::~SyncValueStoreCache() {
+  DCHECK(IsOnBackendSequence());
+}
+
+void SyncValueStoreCache::RunWithValueStoreForExtension(
+    const StorageCallback& callback,
+    scoped_refptr<const Extension> extension) {
+  DCHECK(IsOnBackendSequence());
+
+  ValueStore* storage = GetStorage(extension.get());
+
+  // A neat way to implement unlimited storage; if the extension has the
+  // unlimited storage permission, force through all calls to Set().
+  if (extension->permissions_data()->HasAPIPermission(
+          APIPermission::kUnlimitedStorage)) {
+    WeakUnlimitedSettingsStorage unlimited_storage(storage);
+    callback.Run(&unlimited_storage);
+  } else {
+    callback.Run(storage);
+  }
+}
+
+void SyncValueStoreCache::DeleteStorageSoon(const std::string& extension_id) {
+  DCHECK(IsOnBackendSequence());
+  storage_map_.erase(extension_id);
+  storage_factory_->DeleteSettings(settings_namespace::SYNC,
+                                   ValueStoreFactory::ModelType::APP,
+                                   extension_id);
+  storage_factory_->DeleteSettings(settings_namespace::SYNC,
+                                   ValueStoreFactory::ModelType::EXTENSION,
+                                   extension_id);
+}
+
+ValueStore* SyncValueStoreCache::GetStorage(const Extension* extension) {
+  StorageMap::iterator iter = storage_map_.find(extension->id());
+  if (iter != storage_map_.end())
+    return iter->second.get();
+
+  ValueStoreFactory::ModelType model_type =
+      extension->is_app() ? ValueStoreFactory::ModelType::APP
+                          : ValueStoreFactory::ModelType::EXTENSION;
+  std::unique_ptr<ValueStore> store = storage_factory_->CreateSettingsStore(
+      settings_namespace::SYNC, model_type, extension->id());
+  std::unique_ptr<SettingsStorageQuotaEnforcer> storage(
+      new SettingsStorageQuotaEnforcer(quota_, std::move(store)));
+  DCHECK(storage.get());
+
+  ValueStore* storage_ptr = storage.get();
+  storage_map_[extension->id()] = std::move(storage);
+  return storage_ptr;
+}
+}  // namespace cef
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/api/storage/sync_value_store_cache.h b/src/libcef/browser/extensions/api/storage/sync_value_store_cache.h
new file mode 100644
index 0000000..269fc7c
--- /dev/null
+++ b/src/libcef/browser/extensions/api/storage/sync_value_store_cache.h
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_API_STORAGE_SYNC_VALUE_STORE_CACHE_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_API_STORAGE_SYNC_VALUE_STORE_CACHE_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "extensions/browser/api/storage/settings_storage_quota_enforcer.h"
+#include "extensions/browser/api/storage/value_store_cache.h"
+
+namespace extensions {
+
+class ValueStoreFactory;
+
+namespace cef {
+
+// Based on LocalValueStoreCache
+// ValueStoreCache for the SYNC namespace. It owns a backend for apps and
+// another for extensions. Each backend takes care of persistence.
+class SyncValueStoreCache : public ValueStoreCache {
+ public:
+  explicit SyncValueStoreCache(const scoped_refptr<ValueStoreFactory>& factory);
+  ~SyncValueStoreCache() override;
+
+  // ValueStoreCache implementation:
+  void RunWithValueStoreForExtension(
+      const StorageCallback& callback,
+      scoped_refptr<const Extension> extension) override;
+  void DeleteStorageSoon(const std::string& extension_id) override;
+
+ private:
+  using StorageMap = std::map<std::string, std::unique_ptr<ValueStore>>;
+
+  ValueStore* GetStorage(const Extension* extension);
+
+  // The Factory to use for creating new ValueStores.
+  const scoped_refptr<ValueStoreFactory> storage_factory_;
+
+  // Quota limits (see SettingsStorageQuotaEnforcer).
+  const SettingsStorageQuotaEnforcer::Limits quota_;
+
+  // The collection of ValueStores for local storage.
+  StorageMap storage_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncValueStoreCache);
+};
+}  // namespace cef
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_API_STORAGE_SYNC_VALUE_STORE_CACHE_H_
diff --git a/src/libcef/browser/extensions/api/tabs/tabs_api.cc b/src/libcef/browser/extensions/api/tabs/tabs_api.cc
new file mode 100644
index 0000000..d732ae2
--- /dev/null
+++ b/src/libcef/browser/extensions/api/tabs/tabs_api.cc
@@ -0,0 +1,398 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2012 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/browser/extensions/api/tabs/tabs_api.h"
+
+#include "libcef/browser/extensions/extension_web_contents_observer.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "components/zoom/zoom_controller.h"
+#include "content/public/browser/render_frame_host.h"
+#include "extensions/browser/extension_api_frame_id_map.h"
+#include "extensions/browser/extension_zoom_request_client.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
+
+namespace extensions {
+namespace cef {
+
+namespace keys = extensions::tabs_constants;
+namespace tabs = api::tabs;
+
+using api::extension_types::InjectDetails;
+
+namespace {
+
+const char kNotImplementedError[] = "Not implemented";
+
+void ZoomModeToZoomSettings(zoom::ZoomController::ZoomMode zoom_mode,
+                            api::tabs::ZoomSettings* zoom_settings) {
+  DCHECK(zoom_settings);
+  switch (zoom_mode) {
+    case zoom::ZoomController::ZOOM_MODE_DEFAULT:
+      zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
+      zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN;
+      break;
+    case zoom::ZoomController::ZOOM_MODE_ISOLATED:
+      zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
+      zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
+      break;
+    case zoom::ZoomController::ZOOM_MODE_MANUAL:
+      zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_MANUAL;
+      zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
+      break;
+    case zoom::ZoomController::ZOOM_MODE_DISABLED:
+      zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_DISABLED;
+      zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
+      break;
+  }
+}
+
+template <typename T>
+void AssignOptionalValue(const std::unique_ptr<T>& source,
+                         std::unique_ptr<T>& destination) {
+  if (source.get()) {
+    destination.reset(new T(*source));
+  }
+}
+
+}  // namespace
+
+ExtensionFunction::ResponseAction TabsGetFunction::Run() {
+  return RespondNow(Error(kNotImplementedError));
+}
+
+TabsCreateFunction::TabsCreateFunction() : cef_details_(this) {}
+
+ExtensionFunction::ResponseAction TabsCreateFunction::Run() {
+  std::unique_ptr<tabs::Create::Params> params(
+      tabs::Create::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  CefExtensionFunctionDetails::OpenTabParams options;
+  AssignOptionalValue(params->create_properties.window_id, options.window_id);
+  AssignOptionalValue(params->create_properties.opener_tab_id,
+                      options.opener_tab_id);
+  AssignOptionalValue(params->create_properties.selected, options.active);
+  // The 'active' property has replaced the 'selected' property.
+  AssignOptionalValue(params->create_properties.active, options.active);
+  AssignOptionalValue(params->create_properties.pinned, options.pinned);
+  AssignOptionalValue(params->create_properties.index, options.index);
+  AssignOptionalValue(params->create_properties.url, options.url);
+
+  std::string error;
+  std::unique_ptr<base::DictionaryValue> result(
+      cef_details_.OpenTab(options, user_gesture(), &error));
+  if (!result)
+    return RespondNow(Error(error));
+
+  // Return data about the newly created tab.
+  return RespondNow(has_callback() ? OneArgument(std::move(result))
+                                   : NoArguments());
+}
+
+ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
+    : cef_details_(this), execute_tab_id_(-1) {}
+
+ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
+
+ExecuteCodeFunction::InitResult ExecuteCodeInTabFunction::Init() {
+  if (init_result_)
+    return init_result_.value();
+
+  // |tab_id| is optional so it's ok if it's not there.
+  int tab_id = -1;
+  if (args_->GetInteger(0, &tab_id) && tab_id < 0)
+    return set_init_result(VALIDATION_FAILURE);
+
+  // |details| are not optional.
+  base::DictionaryValue* details_value = nullptr;
+  if (!args_->GetDictionary(1, &details_value))
+    return set_init_result(VALIDATION_FAILURE);
+  std::unique_ptr<InjectDetails> details(new InjectDetails());
+  if (!InjectDetails::Populate(*details_value, details.get()))
+    return set_init_result(VALIDATION_FAILURE);
+
+  // Find a browser that we can access, or fail with error.
+  std::string error;
+  CefRefPtr<CefBrowserHostImpl> browser =
+      cef_details_.GetBrowserForTabIdFirstTime(tab_id, &error);
+  if (!browser)
+    return set_init_result_error(error);
+
+  execute_tab_id_ = browser->GetIdentifier();
+  details_ = std::move(details);
+  set_host_id(HostID(HostID::EXTENSIONS, extension()->id()));
+  return set_init_result(SUCCESS);
+}
+
+bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) {
+  CHECK_GE(execute_tab_id_, 0);
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      cef_details_.GetBrowserForTabIdAgain(execute_tab_id_, error);
+  if (!browser)
+    return false;
+
+  int frame_id = details_->frame_id ? *details_->frame_id
+                                    : ExtensionApiFrameIdMap::kTopFrameId;
+  content::RenderFrameHost* rfh =
+      ExtensionApiFrameIdMap::GetRenderFrameHostById(browser->web_contents(),
+                                                     frame_id);
+  if (!rfh) {
+    *error = ErrorUtils::FormatErrorMessage(
+        keys::kFrameNotFoundError, base::NumberToString(frame_id),
+        base::NumberToString(execute_tab_id_));
+    return false;
+  }
+
+  // Content scripts declared in manifest.json can access frames at about:-URLs
+  // if the extension has permission to access the frame's origin, so also allow
+  // programmatic content scripts at about:-URLs for allowed origins.
+  GURL effective_document_url(rfh->GetLastCommittedURL());
+  bool is_about_url = effective_document_url.SchemeIs(url::kAboutScheme);
+  if (is_about_url && details_->match_about_blank &&
+      *details_->match_about_blank) {
+    effective_document_url = GURL(rfh->GetLastCommittedOrigin().Serialize());
+  }
+
+  if (!effective_document_url.is_valid()) {
+    // Unknown URL, e.g. because no load was committed yet. Allow for now, the
+    // renderer will check again and fail the injection if needed.
+    return true;
+  }
+
+  // NOTE: This can give the wrong answer due to race conditions, but it is OK,
+  // we check again in the renderer.
+  if (!extension()->permissions_data()->CanAccessPage(effective_document_url,
+                                                      execute_tab_id_, error)) {
+    if (is_about_url &&
+        extension()->permissions_data()->active_permissions().HasAPIPermission(
+            APIPermission::kTab)) {
+      *error = ErrorUtils::FormatErrorMessage(
+          manifest_errors::kCannotAccessAboutUrl,
+          rfh->GetLastCommittedURL().spec(),
+          rfh->GetLastCommittedOrigin().Serialize());
+    }
+    return false;
+  }
+
+  return true;
+}
+
+ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor(
+    std::string* error) {
+  CHECK_GE(execute_tab_id_, 0);
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      cef_details_.GetBrowserForTabIdAgain(execute_tab_id_, error);
+  if (!browser)
+    return nullptr;
+
+  return CefExtensionWebContentsObserver::FromWebContents(
+             browser->web_contents())
+      ->script_executor();
+}
+
+bool ExecuteCodeInTabFunction::IsWebView() const {
+  return false;
+}
+
+const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const {
+  return GURL::EmptyGURL();
+}
+
+bool ExecuteCodeInTabFunction::LoadFile(const std::string& file,
+                                        std::string* error) {
+  if (cef_details_.LoadFile(
+          file, base::BindOnce(&ExecuteCodeInTabFunction::LoadFileComplete,
+                               this, file))) {
+    return true;
+  }
+
+  // Default handling.
+  return ExecuteCodeFunction::LoadFile(file, error);
+}
+
+void ExecuteCodeInTabFunction::LoadFileComplete(
+    const std::string& file,
+    std::unique_ptr<std::string> data) {
+  const bool success = !!data.get();
+  DidLoadAndLocalizeFile(file, success, std::move(data));
+}
+
+bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
+  return false;
+}
+
+bool TabsInsertCSSFunction::ShouldInsertCSS() const {
+  return true;
+}
+
+ZoomAPIFunction::ZoomAPIFunction() : cef_details_(this) {}
+
+content::WebContents* ZoomAPIFunction::GetWebContents(int tab_id) {
+  // Find a browser that we can access, or set |error_| and return nullptr.
+  CefRefPtr<CefBrowserHostImpl> browser =
+      cef_details_.GetBrowserForTabIdFirstTime(tab_id, &error_);
+  if (!browser)
+    return nullptr;
+
+  return browser->web_contents();
+}
+
+void ZoomAPIFunction::SendResponse(bool success) {
+  ResponseValue response;
+  if (success) {
+    response = ArgumentList(std::move(results_));
+  } else {
+    response = results_ ? ErrorWithArguments(std::move(results_), error_)
+                        : Error(error_);
+  }
+  Respond(std::move(response));
+}
+
+ExtensionFunction::ResponseAction ZoomAPIFunction::Run() {
+  if (RunAsync())
+    return RespondLater();
+  // TODO(devlin): Track these down and eliminate them if possible. We
+  // shouldn't return results and an error.
+  if (results_)
+    return RespondNow(ErrorWithArguments(std::move(results_), error_));
+  return RespondNow(Error(error_));
+}
+
+bool TabsSetZoomFunction::RunAsync() {
+  std::unique_ptr<tabs::SetZoom::Params> params(
+      tabs::SetZoom::Params::Create(*args_));
+  EXTENSION_FUNCTION_PRERUN_VALIDATE(params);
+
+  int tab_id = params->tab_id ? *params->tab_id : -1;
+  content::WebContents* web_contents = GetWebContents(tab_id);
+  if (!web_contents)
+    return false;
+
+  GURL url(web_contents->GetVisibleURL());
+  if (extension()->permissions_data()->IsRestrictedUrl(url, &error_))
+    return false;
+
+  zoom::ZoomController* zoom_controller =
+      zoom::ZoomController::FromWebContents(web_contents);
+  double zoom_level =
+      params->zoom_factor > 0
+          ? blink::PageZoomFactorToZoomLevel(params->zoom_factor)
+          : zoom_controller->GetDefaultZoomLevel();
+
+  scoped_refptr<extensions::ExtensionZoomRequestClient> client(
+      new extensions::ExtensionZoomRequestClient(extension()));
+  if (!zoom_controller->SetZoomLevelByClient(zoom_level, client)) {
+    // Tried to zoom a tab in disabled mode.
+    error_ = keys::kCannotZoomDisabledTabError;
+    return false;
+  }
+
+  SendResponse(true);
+  return true;
+}
+
+bool TabsGetZoomFunction::RunAsync() {
+  std::unique_ptr<tabs::GetZoom::Params> params(
+      tabs::GetZoom::Params::Create(*args_));
+  EXTENSION_FUNCTION_PRERUN_VALIDATE(params);
+
+  int tab_id = params->tab_id ? *params->tab_id : -1;
+  content::WebContents* web_contents = GetWebContents(tab_id);
+  if (!web_contents)
+    return false;
+
+  double zoom_level =
+      zoom::ZoomController::FromWebContents(web_contents)->GetZoomLevel();
+  double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
+  results_ = tabs::GetZoom::Results::Create(zoom_factor);
+  SendResponse(true);
+  return true;
+}
+
+bool TabsSetZoomSettingsFunction::RunAsync() {
+  using api::tabs::ZoomSettings;
+
+  std::unique_ptr<tabs::SetZoomSettings::Params> params(
+      tabs::SetZoomSettings::Params::Create(*args_));
+  EXTENSION_FUNCTION_PRERUN_VALIDATE(params);
+
+  int tab_id = params->tab_id ? *params->tab_id : -1;
+  content::WebContents* web_contents = GetWebContents(tab_id);
+  if (!web_contents)
+    return false;
+
+  GURL url(web_contents->GetVisibleURL());
+  if (extension()->permissions_data()->IsRestrictedUrl(url, &error_))
+    return false;
+
+  // "per-origin" scope is only available in "automatic" mode.
+  if (params->zoom_settings.scope == tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN &&
+      params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_AUTOMATIC &&
+      params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_NONE) {
+    error_ = keys::kPerOriginOnlyInAutomaticError;
+    return false;
+  }
+
+  // Determine the correct internal zoom mode to set |web_contents| to from the
+  // user-specified |zoom_settings|.
+  zoom::ZoomController::ZoomMode zoom_mode =
+      zoom::ZoomController::ZOOM_MODE_DEFAULT;
+  switch (params->zoom_settings.mode) {
+    case tabs::ZOOM_SETTINGS_MODE_NONE:
+    case tabs::ZOOM_SETTINGS_MODE_AUTOMATIC:
+      switch (params->zoom_settings.scope) {
+        case tabs::ZOOM_SETTINGS_SCOPE_NONE:
+        case tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN:
+          zoom_mode = zoom::ZoomController::ZOOM_MODE_DEFAULT;
+          break;
+        case tabs::ZOOM_SETTINGS_SCOPE_PER_TAB:
+          zoom_mode = zoom::ZoomController::ZOOM_MODE_ISOLATED;
+      }
+      break;
+    case tabs::ZOOM_SETTINGS_MODE_MANUAL:
+      zoom_mode = zoom::ZoomController::ZOOM_MODE_MANUAL;
+      break;
+    case tabs::ZOOM_SETTINGS_MODE_DISABLED:
+      zoom_mode = zoom::ZoomController::ZOOM_MODE_DISABLED;
+  }
+
+  zoom::ZoomController::FromWebContents(web_contents)->SetZoomMode(zoom_mode);
+
+  SendResponse(true);
+  return true;
+}
+
+bool TabsGetZoomSettingsFunction::RunAsync() {
+  std::unique_ptr<tabs::GetZoomSettings::Params> params(
+      tabs::GetZoomSettings::Params::Create(*args_));
+  EXTENSION_FUNCTION_PRERUN_VALIDATE(params);
+
+  int tab_id = params->tab_id ? *params->tab_id : -1;
+  content::WebContents* web_contents = GetWebContents(tab_id);
+  if (!web_contents)
+    return false;
+  zoom::ZoomController* zoom_controller =
+      zoom::ZoomController::FromWebContents(web_contents);
+
+  zoom::ZoomController::ZoomMode zoom_mode = zoom_controller->zoom_mode();
+  api::tabs::ZoomSettings zoom_settings;
+  ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
+  zoom_settings.default_zoom_factor.reset(
+      new double(blink::PageZoomLevelToZoomFactor(
+          zoom_controller->GetDefaultZoomLevel())));
+
+  results_ = api::tabs::GetZoomSettings::Results::Create(zoom_settings);
+  SendResponse(true);
+  return true;
+}
+
+}  // namespace cef
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/api/tabs/tabs_api.h b/src/libcef/browser/extensions/api/tabs/tabs_api.h
new file mode 100644
index 0000000..a195109
--- /dev/null
+++ b/src/libcef/browser/extensions/api/tabs/tabs_api.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_
+
+#include "libcef/browser/extensions/extension_function_details.h"
+
+#include "chrome/common/extensions/api/tabs.h"
+#include "extensions/browser/api/execute_code_function.h"
+#include "extensions/browser/extension_function.h"
+
+// The contents of this file are extracted from
+// chrome/browser/extensions/api/tabs/tabs_api.h.
+
+namespace content {
+class WebContents;
+}
+
+namespace extensions {
+namespace cef {
+
+class TabsGetFunction : public ExtensionFunction {
+  ~TabsGetFunction() override {}
+
+  ResponseAction Run() override;
+
+  DECLARE_EXTENSION_FUNCTION("tabs.get", TABS_GET)
+};
+
+class TabsCreateFunction : public ExtensionFunction {
+ public:
+  TabsCreateFunction();
+  ~TabsCreateFunction() override {}
+
+  ResponseAction Run() override;
+
+  DECLARE_EXTENSION_FUNCTION("tabs.create", TABS_CREATE)
+
+ private:
+  const CefExtensionFunctionDetails cef_details_;
+};
+
+// Implement API call tabs.executeScript and tabs.insertCSS.
+class ExecuteCodeInTabFunction : public ExecuteCodeFunction {
+ public:
+  ExecuteCodeInTabFunction();
+
+ protected:
+  ~ExecuteCodeInTabFunction() override;
+
+  // Initializes |execute_tab_id_| and |details_|.
+  InitResult Init() override;
+  bool CanExecuteScriptOnPage(std::string* error) override;
+  ScriptExecutor* GetScriptExecutor(std::string* error) override;
+  bool IsWebView() const override;
+  const GURL& GetWebViewSrc() const override;
+  bool LoadFile(const std::string& file, std::string* error) override;
+
+ private:
+  const CefExtensionFunctionDetails cef_details_;
+
+  void LoadFileComplete(const std::string& file,
+                        std::unique_ptr<std::string> data);
+
+  // Id of tab which executes code.
+  int execute_tab_id_;
+};
+
+class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction {
+ protected:
+  bool ShouldInsertCSS() const override;
+
+ private:
+  ~TabsExecuteScriptFunction() override {}
+
+  DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT)
+};
+
+class TabsInsertCSSFunction : public ExecuteCodeInTabFunction {
+ private:
+  ~TabsInsertCSSFunction() override {}
+
+  bool ShouldInsertCSS() const override;
+
+  DECLARE_EXTENSION_FUNCTION("tabs.insertCSS", TABS_INSERTCSS)
+};
+
+// Based on ChromeAsyncExtensionFunction.
+class ZoomAPIFunction : public ExtensionFunction {
+ public:
+  ZoomAPIFunction();
+
+ protected:
+  ~ZoomAPIFunction() override {}
+
+  // Gets the WebContents for |tab_id| if it is specified. Otherwise get the
+  // WebContents for the active tab in the current window. Calling this function
+  // may set |error_|.
+  content::WebContents* GetWebContents(int tab_id);
+
+  virtual bool RunAsync() = 0;
+
+  // Responds with success/failure. |results_| or |error_| should be set
+  // accordingly.
+  void SendResponse(bool success);
+
+  // Exposed versions of ExtensionFunction::results_ and
+  // ExtensionFunction::error_ that are curried into the response.
+  // These need to keep the same name to avoid breaking existing
+  // implementations, but this should be temporary with crbug.com/648275
+  // and crbug.com/634140.
+  std::unique_ptr<base::ListValue> results_;
+  std::string error_;
+
+ private:
+  ResponseAction Run() final;
+
+  const CefExtensionFunctionDetails cef_details_;
+};
+
+class TabsSetZoomFunction : public ZoomAPIFunction {
+ private:
+  ~TabsSetZoomFunction() override {}
+
+  bool RunAsync() override;
+
+  DECLARE_EXTENSION_FUNCTION("tabs.setZoom", TABS_SETZOOM)
+};
+
+class TabsGetZoomFunction : public ZoomAPIFunction {
+ private:
+  ~TabsGetZoomFunction() override {}
+
+  bool RunAsync() override;
+
+  DECLARE_EXTENSION_FUNCTION("tabs.getZoom", TABS_GETZOOM)
+};
+
+class TabsSetZoomSettingsFunction : public ZoomAPIFunction {
+ private:
+  ~TabsSetZoomSettingsFunction() override {}
+
+  bool RunAsync() override;
+
+  DECLARE_EXTENSION_FUNCTION("tabs.setZoomSettings", TABS_SETZOOMSETTINGS)
+};
+
+class TabsGetZoomSettingsFunction : public ZoomAPIFunction {
+ private:
+  ~TabsGetZoomSettingsFunction() override {}
+
+  bool RunAsync() override;
+
+  DECLARE_EXTENSION_FUNCTION("tabs.getZoomSettings", TABS_GETZOOMSETTINGS)
+};
+
+}  // namespace cef
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_
diff --git a/src/libcef/browser/extensions/browser_extensions_util.cc b/src/libcef/browser/extensions/browser_extensions_util.cc
new file mode 100644
index 0000000..c7ea3e1
--- /dev/null
+++ b/src/libcef/browser/extensions/browser_extensions_util.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/extensions/browser_extensions_util.h"
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/browser_info_manager.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/printing/print_preview_dialog_controller.h"
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_plugin_guest_manager.h"
+#include "extensions/browser/extension_registry.h"
+
+namespace extensions {
+
+namespace {
+
+bool InsertWebContents(std::vector<content::WebContents*>* vector,
+                       content::WebContents* web_contents) {
+  vector->push_back(web_contents);
+  return false;  // Continue iterating.
+}
+
+}  // namespace
+
+content::WebContents* GetFullPageGuestForOwnerContents(
+    content::WebContents* owner) {
+  content::WebContentsImpl* owner_impl =
+      static_cast<content::WebContentsImpl*>(owner);
+  content::BrowserPluginEmbedder* plugin_embedder =
+      owner_impl->GetBrowserPluginEmbedder();
+  if (plugin_embedder) {
+    content::BrowserPluginGuest* plugin_guest =
+        plugin_embedder->GetFullPageGuest();
+    if (plugin_guest)
+      return plugin_guest->web_contents();
+  }
+  return nullptr;
+}
+
+void GetAllGuestsForOwnerContents(content::WebContents* owner,
+                                  std::vector<content::WebContents*>* guests) {
+  content::BrowserPluginGuestManager* plugin_guest_manager =
+      owner->GetBrowserContext()->GetGuestManager();
+  plugin_guest_manager->ForEachGuest(owner,
+                                     base::Bind(InsertWebContents, guests));
+}
+
+content::WebContents* GetOwnerForGuestContents(content::WebContents* guest) {
+  content::WebContentsImpl* guest_impl =
+      static_cast<content::WebContentsImpl*>(guest);
+  content::BrowserPluginGuest* plugin_guest =
+      guest_impl->GetBrowserPluginGuest();
+  if (plugin_guest) {
+    return content::WebContents::FromRenderFrameHost(
+        plugin_guest->GetEmbedderFrame());
+  }
+
+  // Maybe it's a print preview dialog.
+  auto print_preview_controller =
+      g_browser_process->print_preview_dialog_controller();
+  return print_preview_controller->GetInitiator(guest);
+}
+
+CefRefPtr<CefBrowserHostImpl> GetOwnerBrowserForFrameRoute(
+    int render_process_id,
+    int render_routing_id,
+    bool* is_guest_view) {
+  if (CEF_CURRENTLY_ON_UIT()) {
+    // Use the non-thread-safe but potentially faster approach.
+    content::RenderFrameHost* host =
+        content::RenderFrameHost::FromID(render_process_id, render_routing_id);
+    if (host)
+      return GetOwnerBrowserForHost(host, is_guest_view);
+    return nullptr;
+  } else {
+    // Use the thread-safe approach.
+    scoped_refptr<CefBrowserInfo> info =
+        CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameRoute(
+            render_process_id, render_routing_id, is_guest_view);
+    if (info.get()) {
+      CefRefPtr<CefBrowserHostImpl> browser = info->browser();
+      if (!browser.get()) {
+        LOG(WARNING) << "Found browser id " << info->browser_id()
+                     << " but no browser object matching view process id "
+                     << render_process_id << " and frame routing id "
+                     << render_routing_id;
+      }
+      return browser;
+    }
+    return nullptr;
+  }
+}
+
+CefRefPtr<CefBrowserHostImpl> GetOwnerBrowserForHost(
+    content::RenderViewHost* host,
+    bool* is_guest_view) {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CefBrowserHostImpl::GetBrowserForHost(host);
+  if (!browser.get() && ExtensionsEnabled()) {
+    // Retrieve the owner browser, if any.
+    content::WebContents* owner = GetOwnerForGuestContents(
+        content::WebContents::FromRenderViewHost(host));
+    if (owner) {
+      browser = CefBrowserHostImpl::GetBrowserForContents(owner);
+      if (browser.get() && is_guest_view)
+        *is_guest_view = true;
+    }
+  }
+  return browser;
+}
+
+CefRefPtr<CefBrowserHostImpl> GetOwnerBrowserForHost(
+    content::RenderFrameHost* host,
+    bool* is_guest_view) {
+  if (is_guest_view)
+    *is_guest_view = false;
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CefBrowserHostImpl::GetBrowserForHost(host);
+  if (!browser.get() && ExtensionsEnabled()) {
+    // Retrieve the owner browser, if any.
+    content::WebContents* owner = GetOwnerForGuestContents(
+        content::WebContents::FromRenderFrameHost(host));
+    if (owner) {
+      browser = CefBrowserHostImpl::GetBrowserForContents(owner);
+      if (browser.get() && is_guest_view)
+        *is_guest_view = true;
+    }
+  }
+  return browser;
+}
+
+CefRefPtr<CefBrowserHostImpl> GetBrowserForTabId(
+    int tab_id,
+    content::BrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+  DCHECK(browser_context);
+  if (tab_id < 0 || !browser_context)
+    return nullptr;
+
+  CefBrowserContext* browser_context_impl =
+      CefBrowserContext::GetForContext(browser_context);
+
+  for (const auto& browser_info :
+       CefBrowserInfoManager::GetInstance()->GetBrowserInfoList()) {
+    CefRefPtr<CefBrowserHostImpl> current_browser = browser_info->browser();
+    if (current_browser && current_browser->GetIdentifier() == tab_id) {
+      // Make sure we're operating in the same BrowserContextImpl.
+      if (CefBrowserContext::GetForContext(
+              current_browser->GetBrowserContext()) == browser_context_impl) {
+        return current_browser;
+      } else {
+        LOG(WARNING) << "Browser with tabId " << tab_id
+                     << " cannot be accessed because is uses a different "
+                        "CefRequestContext";
+        break;
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+const Extension* GetExtensionForUrl(content::BrowserContext* browser_context,
+                                    const GURL& url) {
+  ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context);
+  if (!registry)
+    return nullptr;
+  std::string extension_id = url.host();
+  return registry->enabled_extensions().GetByID(extension_id);
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/browser_extensions_util.h b/src/libcef/browser/extensions/browser_extensions_util.h
new file mode 100644
index 0000000..a4de94b
--- /dev/null
+++ b/src/libcef/browser/extensions/browser_extensions_util.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_BROWSER_EXTENSIONS_UTIL_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_BROWSER_EXTENSIONS_UTIL_H_
+
+#include <vector>
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "url/gurl.h"
+
+namespace content {
+class BrowserContext;
+class RenderViewHost;
+class WebContents;
+}  // namespace content
+
+namespace extensions {
+
+class Extension;
+
+// Returns the full-page guest WebContents for the specified |owner|, if any.
+content::WebContents* GetFullPageGuestForOwnerContents(
+    content::WebContents* owner);
+
+// Populates |guests| with all guest WebContents with the specified |owner|.
+void GetAllGuestsForOwnerContents(content::WebContents* owner,
+                                  std::vector<content::WebContents*>* guests);
+
+// Returns the WebContents that owns the specified |guest|, if any.
+content::WebContents* GetOwnerForGuestContents(content::WebContents* guest);
+
+// Returns the CefBrowserHostImpl that owns the host identified by the specified
+// routing IDs, if any. |is_guest_view| will be set to true if the IDs
+// match a guest view associated with the returned browser instead of the
+// browser itself.
+CefRefPtr<CefBrowserHostImpl> GetOwnerBrowserForFrameRoute(
+    int render_process_id,
+    int render_routing_id,
+    bool* is_guest_view);
+
+// Returns the CefBrowserHostImpl that owns the specified |host|, if any.
+// |is_guest_view| will be set to true if the host matches a guest view
+// associated with the returned browser instead of the browser itself.
+// TODO(cef): Delete the RVH variant once the remaining use case
+// (via CefContentBrowserClient::OverrideWebkitPrefs) has been removed.
+CefRefPtr<CefBrowserHostImpl> GetOwnerBrowserForHost(
+    content::RenderViewHost* host,
+    bool* is_guest_view);
+CefRefPtr<CefBrowserHostImpl> GetOwnerBrowserForHost(
+    content::RenderFrameHost* host,
+    bool* is_guest_view);
+
+// Returns the browser matching |tab_id| and |browser_context|. Returns false if
+// |tab_id| is < 0 or a matching browser cannot be found within
+// |browser_context|. Similar in concept to ExtensionTabUtil::GetTabById.
+CefRefPtr<CefBrowserHostImpl> GetBrowserForTabId(
+    int tab_id,
+    content::BrowserContext* browser_context);
+
+// Returns the extension associated with |url| in |profile|. Returns nullptr
+// if the extension does not exist.
+const Extension* GetExtensionForUrl(content::BrowserContext* browser_context,
+                                    const GURL& url);
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_BROWSER_EXTENSIONS_UTIL_H_
diff --git a/src/libcef/browser/extensions/browser_platform_delegate_background.cc b/src/libcef/browser/extensions/browser_platform_delegate_background.cc
new file mode 100644
index 0000000..ac542b9
--- /dev/null
+++ b/src/libcef/browser/extensions/browser_platform_delegate_background.cc
@@ -0,0 +1,142 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/browser_platform_delegate_background.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/thread_util.h"
+
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+
+CefBrowserPlatformDelegateBackground::CefBrowserPlatformDelegateBackground(
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate)
+    : native_delegate_(std::move(native_delegate)) {
+  native_delegate_->set_windowless_handler(this);
+}
+
+bool CefBrowserPlatformDelegateBackground::CreateHostWindow() {
+  // Nothing to do here.
+  return true;
+}
+
+void CefBrowserPlatformDelegateBackground::CloseHostWindow() {
+  // No host window, so continue browser destruction now. Do it asynchronously
+  // so the call stack has a chance to unwind.
+  CEF_POST_TASK(CEF_UIT,
+                base::Bind(&CefBrowserHostImpl::WindowDestroyed, browser_));
+}
+
+CefWindowHandle CefBrowserPlatformDelegateBackground::GetHostWindowHandle()
+    const {
+  return kNullWindowHandle;
+}
+
+bool CefBrowserPlatformDelegateBackground::CanUseSharedTexture() const {
+  return native_delegate_->CanUseSharedTexture();
+}
+
+bool CefBrowserPlatformDelegateBackground::CanUseExternalBeginFrame() const {
+  return native_delegate_->CanUseExternalBeginFrame();
+}
+
+SkColor CefBrowserPlatformDelegateBackground::GetBackgroundColor() const {
+  return native_delegate_->GetBackgroundColor();
+}
+
+void CefBrowserPlatformDelegateBackground::WasResized() {
+  // Nothing to do here.
+}
+
+void CefBrowserPlatformDelegateBackground::SendKeyEvent(
+    const CefKeyEvent& event) {
+  // Nothing to do here.
+}
+
+void CefBrowserPlatformDelegateBackground::SendMouseClickEvent(
+    const CefMouseEvent& event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) {
+  // Nothing to do here.
+}
+
+void CefBrowserPlatformDelegateBackground::SendMouseMoveEvent(
+    const CefMouseEvent& event,
+    bool mouseLeave) {
+  // Nothing to do here.
+}
+
+void CefBrowserPlatformDelegateBackground::SendMouseWheelEvent(
+    const CefMouseEvent& event,
+    int deltaX,
+    int deltaY) {
+  // Nothing to do here.
+}
+
+void CefBrowserPlatformDelegateBackground::SendTouchEvent(
+    const CefTouchEvent& event) {
+  // Nothing to do here.
+}
+
+void CefBrowserPlatformDelegateBackground::SendFocusEvent(bool setFocus) {
+  // Nothing to do here.
+}
+
+gfx::Point CefBrowserPlatformDelegateBackground::GetScreenPoint(
+    const gfx::Point& view_pt) const {
+  // Nothing to do here.
+  return view_pt;
+}
+
+void CefBrowserPlatformDelegateBackground::ViewText(const std::string& text) {
+  native_delegate_->ViewText(text);
+}
+
+bool CefBrowserPlatformDelegateBackground::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  // Nothing to do here.
+  return false;
+}
+
+CefEventHandle CefBrowserPlatformDelegateBackground::GetEventHandle(
+    const content::NativeWebKeyboardEvent& event) const {
+  return native_delegate_->GetEventHandle(event);
+}
+
+std::unique_ptr<CefFileDialogRunner>
+CefBrowserPlatformDelegateBackground::CreateFileDialogRunner() {
+  return native_delegate_->CreateFileDialogRunner();
+}
+
+std::unique_ptr<CefJavaScriptDialogRunner>
+CefBrowserPlatformDelegateBackground::CreateJavaScriptDialogRunner() {
+  return native_delegate_->CreateJavaScriptDialogRunner();
+}
+
+std::unique_ptr<CefMenuRunner>
+CefBrowserPlatformDelegateBackground::CreateMenuRunner() {
+  // No default menu implementation for background browsers.
+  return nullptr;
+}
+
+bool CefBrowserPlatformDelegateBackground::IsWindowless() const {
+  return false;
+}
+
+bool CefBrowserPlatformDelegateBackground::IsViewsHosted() const {
+  return false;
+}
+
+CefWindowHandle CefBrowserPlatformDelegateBackground::GetParentWindowHandle()
+    const {
+  return GetHostWindowHandle();
+}
+
+gfx::Point CefBrowserPlatformDelegateBackground::GetParentScreenPoint(
+    const gfx::Point& view) const {
+  return GetScreenPoint(view);
+}
diff --git a/src/libcef/browser/extensions/browser_platform_delegate_background.h b/src/libcef/browser/extensions/browser_platform_delegate_background.h
new file mode 100644
index 0000000..2ded373
--- /dev/null
+++ b/src/libcef/browser/extensions/browser_platform_delegate_background.h
@@ -0,0 +1,60 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_BACKGROUND_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_BACKGROUND_H_
+
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/native/browser_platform_delegate_native.h"
+
+// Implementation of browser functionality for background script hosts.
+class CefBrowserPlatformDelegateBackground
+    : public CefBrowserPlatformDelegate,
+      public CefBrowserPlatformDelegateNative::WindowlessHandler {
+ public:
+  // Platform-specific behaviors will be delegated to |native_delegate|.
+  CefBrowserPlatformDelegateBackground(
+      std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate);
+
+  // CefBrowserPlatformDelegate methods:
+  bool CreateHostWindow() override;
+  void CloseHostWindow() override;
+  CefWindowHandle GetHostWindowHandle() const override;
+  SkColor GetBackgroundColor() const override;
+  bool CanUseSharedTexture() const override;
+  bool CanUseExternalBeginFrame() const override;
+  void WasResized() override;
+  void SendKeyEvent(const CefKeyEvent& event) override;
+  void SendMouseClickEvent(const CefMouseEvent& event,
+                           CefBrowserHost::MouseButtonType type,
+                           bool mouseUp,
+                           int clickCount) override;
+  void SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) override;
+  void SendMouseWheelEvent(const CefMouseEvent& event,
+                           int deltaX,
+                           int deltaY) override;
+  void SendTouchEvent(const CefTouchEvent& event) override;
+  void SendFocusEvent(bool setFocus) override;
+  gfx::Point GetScreenPoint(const gfx::Point& view) const override;
+  void ViewText(const std::string& text) override;
+  bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) override;
+  CefEventHandle GetEventHandle(
+      const content::NativeWebKeyboardEvent& event) const override;
+  std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
+  std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
+      override;
+  std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;
+  bool IsWindowless() const override;
+  bool IsViewsHosted() const override;
+
+  // CefBrowserPlatformDelegateNative::WindowlessHandler methods:
+  CefWindowHandle GetParentWindowHandle() const override;
+  gfx::Point GetParentScreenPoint(const gfx::Point& view) const override;
+
+ private:
+  std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_BACKGROUND_H_
diff --git a/src/libcef/browser/extensions/chrome_api_registration.cc b/src/libcef/browser/extensions/chrome_api_registration.cc
new file mode 100644
index 0000000..c082818
--- /dev/null
+++ b/src/libcef/browser/extensions/chrome_api_registration.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// APIs must also be registered in
+// libcef/common/extensions/api/_*_features.json files and possibly
+// CefExtensionsDispatcherDelegate::PopulateSourceMap. See
+// libcef/common/extensions/api/README.txt for additional details.
+
+#include "libcef/browser/extensions/chrome_api_registration.h"
+
+#include "libcef/browser/extensions/api/tabs/tabs_api.h"
+
+#include "chrome/browser/extensions/api/content_settings/content_settings_api.h"
+#include "chrome/browser/extensions/api/resources_private/resources_private_api.h"
+#include "extensions/browser/api/alarms/alarms_api.h"
+#include "extensions/browser/api/storage/storage_api.h"
+#include "extensions/browser/extension_function_registry.h"
+
+namespace extensions {
+namespace api {
+namespace cef {
+
+namespace cefimpl = extensions::cef;
+
+#define EXTENSION_FUNCTION_NAME(classname) classname::function_name()
+
+// Maintain the same order as https://developer.chrome.com/extensions/api_index
+// so chrome://extensions-support looks nice.
+const char* const kSupportedAPIs[] = {
+    "resourcesPrivate",
+    EXTENSION_FUNCTION_NAME(ResourcesPrivateGetStringsFunction),
+    "alarms",
+    EXTENSION_FUNCTION_NAME(AlarmsCreateFunction),
+    EXTENSION_FUNCTION_NAME(AlarmsGetFunction),
+    EXTENSION_FUNCTION_NAME(AlarmsGetAllFunction),
+    EXTENSION_FUNCTION_NAME(AlarmsClearFunction),
+    EXTENSION_FUNCTION_NAME(AlarmsClearAllFunction),
+    "contentSettings",
+    EXTENSION_FUNCTION_NAME(ContentSettingsContentSettingClearFunction),
+    EXTENSION_FUNCTION_NAME(ContentSettingsContentSettingGetFunction),
+    EXTENSION_FUNCTION_NAME(ContentSettingsContentSettingSetFunction),
+    EXTENSION_FUNCTION_NAME(
+        ContentSettingsContentSettingGetResourceIdentifiersFunction),
+    "storage",
+    EXTENSION_FUNCTION_NAME(StorageStorageAreaGetFunction),
+    EXTENSION_FUNCTION_NAME(StorageStorageAreaSetFunction),
+    EXTENSION_FUNCTION_NAME(StorageStorageAreaRemoveFunction),
+    EXTENSION_FUNCTION_NAME(StorageStorageAreaClearFunction),
+    EXTENSION_FUNCTION_NAME(StorageStorageAreaGetBytesInUseFunction),
+    "tabs",
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsGetFunction),
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsCreateFunction),
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsExecuteScriptFunction),
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsInsertCSSFunction),
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsSetZoomFunction),
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsGetZoomFunction),
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsSetZoomSettingsFunction),
+    EXTENSION_FUNCTION_NAME(cefimpl::TabsGetZoomSettingsFunction),
+    nullptr,  // Indicates end of array.
+};
+
+// Only add APIs to this list that have been tested in CEF.
+// static
+bool ChromeFunctionRegistry::IsSupported(const std::string& name) {
+  for (size_t i = 0; kSupportedAPIs[i] != nullptr; ++i) {
+    if (name == kSupportedAPIs[i])
+      return true;
+  }
+  return false;
+}
+
+// Only add APIs to this list that have been tested in CEF.
+// static
+void ChromeFunctionRegistry::RegisterAll(ExtensionFunctionRegistry* registry) {
+  registry->RegisterFunction<ResourcesPrivateGetStringsFunction>();
+  registry->RegisterFunction<AlarmsCreateFunction>();
+  registry->RegisterFunction<AlarmsGetFunction>();
+  registry->RegisterFunction<AlarmsGetAllFunction>();
+  registry->RegisterFunction<AlarmsClearFunction>();
+  registry->RegisterFunction<AlarmsClearAllFunction>();
+  registry->RegisterFunction<ContentSettingsContentSettingClearFunction>();
+  registry->RegisterFunction<ContentSettingsContentSettingGetFunction>();
+  registry->RegisterFunction<ContentSettingsContentSettingSetFunction>();
+  registry->RegisterFunction<
+      ContentSettingsContentSettingGetResourceIdentifiersFunction>();
+  registry->RegisterFunction<StorageStorageAreaGetFunction>();
+  registry->RegisterFunction<StorageStorageAreaSetFunction>();
+  registry->RegisterFunction<StorageStorageAreaRemoveFunction>();
+  registry->RegisterFunction<StorageStorageAreaClearFunction>();
+  registry->RegisterFunction<StorageStorageAreaGetBytesInUseFunction>();
+  registry->RegisterFunction<cefimpl::TabsExecuteScriptFunction>();
+  registry->RegisterFunction<cefimpl::TabsInsertCSSFunction>();
+  registry->RegisterFunction<cefimpl::TabsGetFunction>();
+  registry->RegisterFunction<cefimpl::TabsCreateFunction>();
+  registry->RegisterFunction<cefimpl::TabsSetZoomFunction>();
+  registry->RegisterFunction<cefimpl::TabsGetZoomFunction>();
+  registry->RegisterFunction<cefimpl::TabsSetZoomSettingsFunction>();
+  registry->RegisterFunction<cefimpl::TabsGetZoomSettingsFunction>();
+}
+
+}  // namespace cef
+}  // namespace api
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/chrome_api_registration.h b/src/libcef/browser/extensions/chrome_api_registration.h
new file mode 100644
index 0000000..52e86c3
--- /dev/null
+++ b/src/libcef/browser/extensions/chrome_api_registration.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_CHROME_API_REGISTRATION_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_CHROME_API_REGISTRATION_H_
+
+#include <string>
+
+class ExtensionFunctionRegistry;
+
+namespace extensions {
+namespace api {
+namespace cef {
+
+// Array of currently supported APIs.
+extern const char* const kSupportedAPIs[];
+
+class ChromeFunctionRegistry {
+ public:
+  static bool IsSupported(const std::string& name);
+  static void RegisterAll(ExtensionFunctionRegistry* registry);
+};
+
+}  // namespace cef
+}  // namespace api
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_CHROME_API_REGISTRATION_H_
diff --git a/src/libcef/browser/extensions/component_extension_resource_manager.cc b/src/libcef/browser/extensions/component_extension_resource_manager.cc
new file mode 100644
index 0000000..c76e867
--- /dev/null
+++ b/src/libcef/browser/extensions/component_extension_resource_manager.cc
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/component_extension_resource_manager.h"
+
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/grit/component_extension_resources_map.h"
+
+namespace extensions {
+
+CefComponentExtensionResourceManager::CefComponentExtensionResourceManager() {
+  AddComponentResourceEntries(kComponentExtensionResources,
+                              kComponentExtensionResourcesSize);
+}
+
+CefComponentExtensionResourceManager::~CefComponentExtensionResourceManager() {}
+
+bool CefComponentExtensionResourceManager::IsComponentExtensionResource(
+    const base::FilePath& extension_path,
+    const base::FilePath& resource_path,
+    int* resource_id) const {
+  base::FilePath directory_path = extension_path;
+  base::FilePath resources_dir;
+  base::FilePath relative_path;
+  if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_dir) ||
+      !resources_dir.AppendRelativePath(directory_path, &relative_path)) {
+    return false;
+  }
+  relative_path = relative_path.Append(resource_path);
+  relative_path = relative_path.NormalizePathSeparators();
+
+  auto entry = path_to_resource_info_.find(relative_path);
+  if (entry != path_to_resource_info_.end()) {
+    *resource_id = entry->second;
+    return true;
+  }
+
+  return false;
+}
+
+const ui::TemplateReplacements*
+CefComponentExtensionResourceManager::GetTemplateReplacementsForExtension(
+    const std::string& extension_id) const {
+  return nullptr;
+}
+
+void CefComponentExtensionResourceManager::AddComponentResourceEntries(
+    const GritResourceMap* entries,
+    size_t size) {
+  base::FilePath gen_folder_path = base::FilePath().AppendASCII(
+      "@out_folder@/gen/chrome/browser/resources/");
+  gen_folder_path = gen_folder_path.NormalizePathSeparators();
+
+  for (size_t i = 0; i < size; ++i) {
+    base::FilePath resource_path =
+        base::FilePath().AppendASCII(entries[i].name);
+    resource_path = resource_path.NormalizePathSeparators();
+
+    if (!gen_folder_path.IsParent(resource_path)) {
+      DCHECK(!base::Contains(path_to_resource_info_, resource_path));
+      path_to_resource_info_[resource_path] = entries[i].value;
+    } else {
+      // If the resource is a generated file, strip the generated folder's path,
+      // so that it can be served from a normal URL (as if it were not
+      // generated).
+      base::FilePath effective_path =
+          base::FilePath().AppendASCII(resource_path.AsUTF8Unsafe().substr(
+              gen_folder_path.value().length()));
+      DCHECK(!base::Contains(path_to_resource_info_, effective_path));
+      path_to_resource_info_[effective_path] = entries[i].value;
+    }
+  }
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/component_extension_resource_manager.h b/src/libcef/browser/extensions/component_extension_resource_manager.h
new file mode 100644
index 0000000..9f29af9
--- /dev/null
+++ b/src/libcef/browser/extensions/component_extension_resource_manager.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_
+
+#include <map>
+
+#include "base/files/file_path.h"
+#include "extensions/browser/component_extension_resource_manager.h"
+
+struct GritResourceMap;
+
+namespace extensions {
+
+class CefComponentExtensionResourceManager
+    : public ComponentExtensionResourceManager {
+ public:
+  CefComponentExtensionResourceManager();
+  ~CefComponentExtensionResourceManager() override;
+
+  // Overridden from ComponentExtensionResourceManager:
+  bool IsComponentExtensionResource(const base::FilePath& extension_path,
+                                    const base::FilePath& resource_path,
+                                    int* resource_id) const override;
+  const ui::TemplateReplacements* GetTemplateReplacementsForExtension(
+      const std::string& extension_id) const override;
+
+ private:
+  void AddComponentResourceEntries(const GritResourceMap* entries, size_t size);
+
+  // A map from a resource path to the resource ID.  Used by
+  // IsComponentExtensionResource.
+  std::map<base::FilePath, int> path_to_resource_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefComponentExtensionResourceManager);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_
diff --git a/src/libcef/browser/extensions/extension_background_host.cc b/src/libcef/browser/extensions/extension_background_host.cc
new file mode 100644
index 0000000..2f5cbc8
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_background_host.cc
@@ -0,0 +1,46 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/browser/extensions/extension_background_host.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/extensions/extension_host_delegate.h"
+
+#include "base/callback.h"
+
+namespace extensions {
+
+CefExtensionBackgroundHost::CefExtensionBackgroundHost(
+    CefBrowserHostImpl* browser,
+    base::OnceClosure deleted_callback,
+    const Extension* extension,
+    content::BrowserContext* browser_context,
+    content::WebContents* host_contents,
+    const GURL& url,
+    ViewType host_type)
+    : ExtensionHost(new CefExtensionHostDelegate(browser),
+                    extension,
+                    browser_context,
+                    host_contents,
+                    url,
+                    host_type),
+      deleted_callback_(std::move(deleted_callback)) {
+  DCHECK(!deleted_callback_.is_null());
+
+  // Only used for background pages.
+  DCHECK(host_type == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
+}
+
+CefExtensionBackgroundHost::~CefExtensionBackgroundHost() {
+  std::move(deleted_callback_).Run();
+}
+
+bool CefExtensionBackgroundHost::ShouldTransferNavigation(
+    bool is_main_frame_navigation) {
+  // Block navigations that cause the main frame to navigate to non-extension
+  // content (i.e. to web content).
+  return !is_main_frame_navigation;
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extension_background_host.h b/src/libcef/browser/extensions/extension_background_host.h
new file mode 100644
index 0000000..bdefe81
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_background_host.h
@@ -0,0 +1,48 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_BACKGROUND_HOST_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_BACKGROUND_HOST_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "extensions/browser/extension_host.h"
+
+class CefBrowserHostImpl;
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace extensions {
+
+// The ExtensionHost for a background page. This is a thin wrapper around the
+// ExtensionHost base class to support CEF-specific constructor. Object lifespan
+// is managed by ProcessManager.
+class CefExtensionBackgroundHost : public ExtensionHost {
+ public:
+  CefExtensionBackgroundHost(CefBrowserHostImpl* browser,
+                             base::OnceClosure deleted_callback,
+                             const Extension* extension,
+                             content::BrowserContext* browser_context,
+                             content::WebContents* host_contents,
+                             const GURL& url,
+                             ViewType host_type);
+  ~CefExtensionBackgroundHost() override;
+
+  // content::WebContentsDelegate methods:
+  bool ShouldTransferNavigation(bool is_main_frame_navigation) override;
+
+ private:
+  // Callback that will be executed on host deletion.
+  base::OnceClosure deleted_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionBackgroundHost);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_BACKGROUND_HOST_H_
diff --git a/src/libcef/browser/extensions/extension_function_details.cc b/src/libcef/browser/extensions/extension_function_details.cc
new file mode 100644
index 0000000..9ae9fe4
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_function_details.cc
@@ -0,0 +1,477 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2014 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/browser/extensions/extension_function_details.h"
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/extensions/browser_extensions_util.h"
+#include "libcef/browser/extensions/extension_system.h"
+#include "libcef/browser/navigate_params.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/favicon_status.h"
+#include "content/public/browser/navigation_entry.h"
+#include "extensions/browser/extension_function.h"
+#include "extensions/browser/extension_function_dispatcher.h"
+#include "extensions/common/error_utils.h"
+
+using content::RenderViewHost;
+using content::WebContents;
+
+namespace extensions {
+
+namespace keys = extensions::tabs_constants;
+
+namespace {
+
+class CefGetExtensionLoadFileCallbackImpl
+    : public CefGetExtensionResourceCallback {
+ public:
+  CefGetExtensionLoadFileCallbackImpl(
+      const std::string& file,
+      CefExtensionFunctionDetails::LoadFileCallback callback)
+      : file_(file), callback_(std::move(callback)) {}
+
+  ~CefGetExtensionLoadFileCallbackImpl() {
+    if (!callback_.is_null()) {
+      // The callback is still pending. Cancel it now.
+      if (CEF_CURRENTLY_ON_UIT()) {
+        RunNow(file_, std::move(callback_), nullptr);
+      } else {
+        CEF_POST_TASK(CEF_UIT, base::BindOnce(
+                                   &CefGetExtensionLoadFileCallbackImpl::RunNow,
+                                   file_, std::move(callback_), nullptr));
+      }
+    }
+  }
+
+  void Continue(CefRefPtr<CefStreamReader> stream) override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (!callback_.is_null()) {
+        // Always continue asynchronously.
+        CEF_POST_TASK(CEF_UIT, base::BindOnce(
+                                   &CefGetExtensionLoadFileCallbackImpl::RunNow,
+                                   file_, std::move(callback_), stream));
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT, base::BindOnce(
+                                 &CefGetExtensionLoadFileCallbackImpl::Continue,
+                                 this, stream));
+    }
+  }
+
+  void Cancel() override { Continue(nullptr); }
+
+  void Disconnect() { callback_.Reset(); }
+
+ private:
+  static void RunNow(const std::string& file,
+                     CefExtensionFunctionDetails::LoadFileCallback callback,
+                     CefRefPtr<CefStreamReader> stream) {
+    CEF_REQUIRE_UIT();
+
+    if (!stream) {
+      std::move(callback).Run(nullptr);
+      return;
+    }
+
+    base::PostTaskAndReplyWithResult(
+        FROM_HERE,
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce(LoadFileFromStream, file, stream), std::move(callback));
+  }
+
+  static std::unique_ptr<std::string> LoadFileFromStream(
+      const std::string& file,
+      CefRefPtr<CefStreamReader> stream) {
+    CEF_REQUIRE_BLOCKING();
+
+    // Move to the end of the stream.
+    stream->Seek(0, SEEK_END);
+    const int64 size = stream->Tell();
+    if (size == 0) {
+      LOG(WARNING) << "Extension resource " << file << " is empty.";
+      return nullptr;
+    }
+
+    std::unique_ptr<std::string> result(new std::string());
+    result->resize(size);
+
+    // Move to the beginning of the stream.
+    stream->Seek(0, SEEK_SET);
+
+    // Read all stream contents into the string.
+    int64 read, offset = 0;
+    do {
+      read =
+          static_cast<int>(stream->Read(&(*result)[offset], 1, size - offset));
+      offset += read;
+    } while (read > 0 && offset < size);
+
+    if (offset != size) {
+      LOG(WARNING) << "Extension resource " << file << " read failed; expected "
+                   << size << ", got " << offset << " bytes.";
+      return nullptr;
+    }
+
+    return result;
+  }
+
+  const std::string file_;
+  CefExtensionFunctionDetails::LoadFileCallback callback_;
+
+  IMPLEMENT_REFCOUNTING(CefGetExtensionLoadFileCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefGetExtensionLoadFileCallbackImpl);
+};
+
+}  // namespace
+
+CefExtensionFunctionDetails::CefExtensionFunctionDetails(
+    ExtensionFunction* function)
+    : function_(function) {}
+
+CefExtensionFunctionDetails::~CefExtensionFunctionDetails() {}
+
+Profile* CefExtensionFunctionDetails::GetProfile() const {
+  return Profile::FromBrowserContext(function_->browser_context());
+}
+
+CefRefPtr<CefBrowserHostImpl> CefExtensionFunctionDetails::GetSenderBrowser()
+    const {
+  content::WebContents* web_contents = function_->GetSenderWebContents();
+  if (web_contents)
+    return CefBrowserHostImpl::GetBrowserForContents(web_contents);
+  return nullptr;
+}
+
+CefRefPtr<CefBrowserHostImpl> CefExtensionFunctionDetails::GetCurrentBrowser()
+    const {
+  // Start with the browser hosting the extension.
+  CefRefPtr<CefBrowserHostImpl> browser = GetSenderBrowser();
+  if (browser && browser->client()) {
+    CefRefPtr<CefExtensionHandler> handler = GetCefExtension()->GetHandler();
+    if (handler) {
+      // Give the handler an opportunity to specify a different browser.
+      CefRefPtr<CefBrowser> active_browser =
+          handler->GetActiveBrowser(GetCefExtension(), browser.get(),
+                                    function_->include_incognito_information());
+      if (active_browser && active_browser != browser) {
+        CefRefPtr<CefBrowserHostImpl> active_browser_impl =
+            static_cast<CefBrowserHostImpl*>(active_browser.get());
+
+        // Make sure we're operating in the same BrowserContextImpl.
+        if (CefBrowserContext::GetForContext(browser->GetBrowserContext()) ==
+            CefBrowserContext::GetForContext(
+                active_browser_impl->GetBrowserContext())) {
+          browser = active_browser_impl;
+        } else {
+          LOG(WARNING) << "Browser with tabId "
+                       << active_browser->GetIdentifier()
+                       << " cannot be accessed because is uses a different "
+                          "CefRequestContext";
+        }
+      }
+    }
+  }
+
+  // May be null during startup/shutdown.
+  return browser;
+}
+
+bool CefExtensionFunctionDetails::CanAccessBrowser(
+    CefRefPtr<CefBrowserHostImpl> target) const {
+  DCHECK(target);
+
+  // Start with the browser hosting the extension.
+  CefRefPtr<CefBrowserHostImpl> browser = GetSenderBrowser();
+  if (browser == target) {
+    // A sender can always access itself.
+    return true;
+  }
+
+  if (browser && browser->client()) {
+    CefRefPtr<CefExtensionHandler> handler = GetCefExtension()->GetHandler();
+    if (handler) {
+      return handler->CanAccessBrowser(
+          GetCefExtension(), browser.get(),
+          function_->include_incognito_information(), target);
+    }
+  }
+
+  // Default to allowing access.
+  return true;
+}
+
+CefRefPtr<CefBrowserHostImpl>
+CefExtensionFunctionDetails::GetBrowserForTabIdFirstTime(
+    int tab_id,
+    std::string* error_message) const {
+  DCHECK(!get_browser_called_first_time_);
+  get_browser_called_first_time_ = true;
+
+  CefRefPtr<CefBrowserHostImpl> browser;
+
+  if (tab_id >= 0) {
+    // May be an invalid tabId or in the wrong BrowserContext.
+    browser = GetBrowserForTabId(tab_id, function_->browser_context());
+    if (!browser || !browser->web_contents() || !CanAccessBrowser(browser)) {
+      if (error_message) {
+        *error_message = ErrorUtils::FormatErrorMessage(
+            keys::kTabNotFoundError, base::NumberToString(tab_id));
+      }
+      return nullptr;
+    }
+  } else {
+    // May return NULL during shutdown.
+    browser = GetCurrentBrowser();
+    if (!browser || !browser->web_contents()) {
+      if (error_message) {
+        *error_message = keys::kNoCurrentWindowError;
+      }
+      return nullptr;
+    }
+  }
+
+  return browser;
+}
+
+CefRefPtr<CefBrowserHostImpl>
+CefExtensionFunctionDetails::GetBrowserForTabIdAgain(
+    int tab_id,
+    std::string* error_message) const {
+  DCHECK_GE(tab_id, 0);
+  DCHECK(get_browser_called_first_time_);
+
+  // May return NULL during shutdown.
+  CefRefPtr<CefBrowserHostImpl> browser =
+      GetBrowserForTabId(tab_id, function_->browser_context());
+  if (!browser || !browser->web_contents()) {
+    if (error_message) {
+      *error_message = ErrorUtils::FormatErrorMessage(
+          keys::kTabNotFoundError, base::NumberToString(tab_id));
+    }
+  }
+  return browser;
+}
+
+bool CefExtensionFunctionDetails::LoadFile(const std::string& file,
+                                           LoadFileCallback callback) const {
+  // Start with the browser hosting the extension.
+  CefRefPtr<CefBrowserHostImpl> browser = GetSenderBrowser();
+  if (browser && browser->client()) {
+    CefRefPtr<CefExtensionHandler> handler = GetCefExtension()->GetHandler();
+    if (handler) {
+      CefRefPtr<CefGetExtensionLoadFileCallbackImpl> cef_callback(
+          new CefGetExtensionLoadFileCallbackImpl(file, std::move(callback)));
+      if (handler->GetExtensionResource(GetCefExtension(), browser.get(), file,
+                                        cef_callback)) {
+        return true;
+      }
+      cef_callback->Disconnect();
+    }
+  }
+
+  return false;
+}
+
+CefExtensionFunctionDetails::OpenTabParams::OpenTabParams() {}
+
+CefExtensionFunctionDetails::OpenTabParams::~OpenTabParams() {}
+
+base::DictionaryValue* CefExtensionFunctionDetails::OpenTab(
+    const OpenTabParams& params,
+    bool user_gesture,
+    std::string* error_message) const {
+  CefRefPtr<CefBrowserHostImpl> sender_browser = GetSenderBrowser();
+  if (!sender_browser)
+    return nullptr;
+
+  // windowId defaults to "current" window.
+  int window_id = extension_misc::kCurrentWindowId;
+  if (params.window_id.get())
+    window_id = *params.window_id;
+
+  // CEF doesn't have the concept of windows containing tab strips so we'll
+  // select an "active browser" for BrowserContext sharing instead.
+  CefRefPtr<CefBrowserHostImpl> active_browser =
+      GetBrowserForTabIdFirstTime(window_id, error_message);
+  if (!active_browser)
+    return nullptr;
+
+  // If an opener browser was specified then we expect it to exist.
+  int opener_browser_id = -1;
+  if (params.opener_tab_id.get() && *params.opener_tab_id >= 0) {
+    if (GetBrowserForTabIdAgain(*params.opener_tab_id, error_message)) {
+      opener_browser_id = *params.opener_tab_id;
+    } else {
+      return nullptr;
+    }
+  }
+
+  GURL url;
+  if (params.url.get()) {
+    std::string url_string = *params.url;
+    url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
+                                                       function()->extension());
+    if (!url.is_valid()) {
+      if (error_message) {
+        *error_message =
+            ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string);
+      }
+      return nullptr;
+    }
+  }
+
+  // Don't let extensions crash the browser or renderers.
+  if (ExtensionTabUtil::IsKillURL(url)) {
+    if (error_message)
+      *error_message = keys::kNoCrashBrowserError;
+    return nullptr;
+  }
+
+  // Default to foreground for the new tab. The presence of 'active' property
+  // will override this default.
+  bool active = true;
+  if (params.active.get())
+    active = *params.active;
+
+  // CEF doesn't use the index value but we let the client see/modify it.
+  int index = 0;
+  if (params.index.get())
+    index = *params.index;
+
+  CefBrowserContext* browser_context_impl =
+      CefBrowserContext::GetForContext(active_browser->GetBrowserContext());
+
+  // A CEF representation should always exist.
+  CefRefPtr<CefExtension> cef_extension =
+      browser_context_impl->extension_system()->GetExtension(
+          function()->extension()->id());
+  DCHECK(cef_extension);
+  if (!cef_extension)
+    return nullptr;
+
+  // Always use the same request context that the extension was registered with.
+  // GetLoaderContext() will return NULL for internal extensions.
+  CefRefPtr<CefRequestContext> request_context =
+      cef_extension->GetLoaderContext();
+  if (!request_context)
+    return nullptr;
+
+  CefBrowserHostImpl::CreateParams create_params;
+  create_params.url = url;
+  create_params.request_context = request_context;
+  create_params.window_info.reset(new CefWindowInfo);
+
+#if defined(OS_WIN)
+  create_params.window_info->SetAsPopup(nullptr, CefString());
+#endif
+
+  // Start with the active browser's settings.
+  create_params.client = active_browser->GetClient();
+  create_params.settings = active_browser->settings();
+
+  CefRefPtr<CefExtensionHandler> handler = cef_extension->GetHandler();
+  if (handler.get() &&
+      handler->OnBeforeBrowser(cef_extension, sender_browser.get(),
+                               active_browser.get(), index, url.spec(), active,
+                               *create_params.window_info, create_params.client,
+                               create_params.settings)) {
+    // Cancel the browser creation.
+    return nullptr;
+  }
+
+  if (active_browser->IsViewsHosted()) {
+    // The new browser will also be Views hosted.
+    create_params.window_info.reset();
+  }
+
+  // Browser creation may fail under certain rare circumstances.
+  CefRefPtr<CefBrowserHostImpl> new_browser =
+      CefBrowserHostImpl::Create(create_params);
+  if (!new_browser)
+    return nullptr;
+
+  // Return data about the newly created tab.
+  auto extension = function()->extension();
+  auto web_contents = new_browser->web_contents();
+  auto result = CreateTabObject(new_browser, opener_browser_id, active, index);
+  auto scrub_tab_behavior = ExtensionTabUtil::GetScrubTabBehavior(
+      extension, extensions::Feature::Context::UNSPECIFIED_CONTEXT,
+      web_contents);
+  ExtensionTabUtil::ScrubTabForExtension(extension, web_contents, result.get(),
+                                         scrub_tab_behavior);
+  return result->ToValue().release();
+}
+
+std::unique_ptr<api::tabs::Tab> CefExtensionFunctionDetails::CreateTabObject(
+    CefRefPtr<CefBrowserHostImpl> new_browser,
+    int opener_browser_id,
+    bool active,
+    int index) const {
+  content::WebContents* contents = new_browser->web_contents();
+
+  bool is_loading = contents->IsLoading();
+  auto tab_object = std::make_unique<api::tabs::Tab>();
+  tab_object->id = std::make_unique<int>(new_browser->GetIdentifier());
+  tab_object->index = index;
+  tab_object->window_id = *tab_object->id;
+  tab_object->status = is_loading ? api::tabs::TAB_STATUS_LOADING
+                                  : api::tabs::TAB_STATUS_COMPLETE;
+  tab_object->active = active;
+  tab_object->selected = true;
+  tab_object->highlighted = true;
+  tab_object->pinned = false;
+  // TODO(extensions): Use RecentlyAudibleHelper to populate |audible|.
+  tab_object->discarded = false;
+  tab_object->auto_discardable = false;
+  tab_object->muted_info = CreateMutedInfo(contents);
+  tab_object->incognito = false;
+  gfx::Size contents_size = contents->GetContainerBounds().size();
+  tab_object->width = std::make_unique<int>(contents_size.width());
+  tab_object->height = std::make_unique<int>(contents_size.height());
+  tab_object->url = std::make_unique<std::string>(contents->GetURL().spec());
+  tab_object->title =
+      std::make_unique<std::string>(base::UTF16ToUTF8(contents->GetTitle()));
+
+  content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
+  if (entry && entry->GetFavicon().valid) {
+    tab_object->fav_icon_url =
+        std::make_unique<std::string>(entry->GetFavicon().url.spec());
+  }
+
+  if (opener_browser_id >= 0)
+    tab_object->opener_tab_id = std::make_unique<int>(opener_browser_id);
+
+  return tab_object;
+}
+
+// static
+std::unique_ptr<api::tabs::MutedInfo>
+CefExtensionFunctionDetails::CreateMutedInfo(content::WebContents* contents) {
+  DCHECK(contents);
+  std::unique_ptr<api::tabs::MutedInfo> info(new api::tabs::MutedInfo);
+  info->muted = contents->IsAudioMuted();
+  // TODO(cef): Maybe populate |info->reason|.
+  return info;
+}
+
+CefRefPtr<CefExtension> CefExtensionFunctionDetails::GetCefExtension() const {
+  if (!cef_extension_) {
+    cef_extension_ =
+        static_cast<CefBrowserContext*>(function_->browser_context())
+            ->extension_system()
+            ->GetExtension(function_->extension_id());
+    DCHECK(cef_extension_);
+  }
+  return cef_extension_;
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extension_function_details.h b/src/libcef/browser/extensions/extension_function_details.h
new file mode 100644
index 0000000..1ba7102
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_function_details.h
@@ -0,0 +1,149 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2014 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_FUNCTION_DETAILS_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_FUNCTION_DETAILS_H_
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "include/cef_extension.h"
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "chrome/common/extensions/api/tabs.h"
+#include "ui/gfx/native_widget_types.h"
+
+class Profile;
+class ExtensionFunction;
+
+namespace content {
+class WebContents;
+}
+
+namespace extensions {
+
+// Provides CEF-specific details to ExtensionFunction implementations.
+// Based on chrome/browser/extensions/chrome_extension_function_details.h.
+class CefExtensionFunctionDetails {
+ public:
+  // Constructs a new ChromeExtensionFunctionDetails instance for |function|.
+  // This instance does not own |function| and must outlive it.
+  explicit CefExtensionFunctionDetails(ExtensionFunction* function);
+  ~CefExtensionFunctionDetails();
+
+  Profile* GetProfile() const;
+
+  // Get the "sender" browser that is hosting the extension. May return NULL
+  // during startup/shutdown.
+  CefRefPtr<CefBrowserHostImpl> GetSenderBrowser() const;
+
+  // Get the "current" browser that will be acted on by this extension function,
+  // if any. When mapping from a tabId use the GetBrowserForTabId* methods
+  // instead of calling this method directly.
+  //
+  // Many extension APIs operate relative to the browser that the calling code
+  // is running inside of. For example, popups and tabs all have a containing
+  // browser, but background pages and notification bubbles do not. Other APIs,
+  // like chrome.tabs.*, can act on either a specific browser (specified via the
+  // tabId parameter) or should allow the client to determine the most
+  // appropriate browser (for example, the browser that representing the
+  // foreground window).
+  //
+  // Incognito browsers should not be considered unless the calling extension
+  // has incognito access enabled. CEF does not internally enforce incognito
+  // status so we pass this flag to client callbacks for consideration.
+  //
+  // This method can return NULL if there is no matching browser, which can
+  // happen if only incognito windows are open, or early in startup or shutdown
+  // shutdown when there are no active windows.
+  CefRefPtr<CefBrowserHostImpl> GetCurrentBrowser() const;
+
+  // Returns true if the sender browser can access |target|. When mapping from a
+  // tabId use the GetBrowserForTabId* methods instead of calling this method
+  // directly.
+  bool CanAccessBrowser(CefRefPtr<CefBrowserHostImpl> target) const;
+
+  // Returns the browser matching |tab_id| or NULL if the browser cannot be
+  // found or does not have a WebContents. If |tab_id| is < 0 the "current"
+  // browser will be returned. |error_message| can optionally be passed in and
+  // will be set with an appropriate message on error. This method should only
+  // be called one time per extension function and will check all necessary
+  // client permissions.
+  CefRefPtr<CefBrowserHostImpl> GetBrowserForTabIdFirstTime(
+      int tab_id,
+      std::string* error_message) const;
+
+  // Returns the browser matching |tab_id| or NULL if the browser cannot be
+  // found or does not have a WebContents. |tab_id| must be >= 0.
+  // |error_message| can optionally be passed in and will be set with an
+  // appropriate message on error. This method should be called only after
+  // GetBrowserForTabIdFirstTime() has succeeded for the same |tab_id|.
+  CefRefPtr<CefBrowserHostImpl> GetBrowserForTabIdAgain(
+      int tab_id,
+      std::string* error_message) const;
+
+  // Give the client a chance to handle |file|. |callback| will be executed
+  // once the file contents have been loaded. Returns false if the file is
+  // unhandled.
+  using LoadFileCallback =
+      base::OnceCallback<void(std::unique_ptr<std::string>)>;
+  bool LoadFile(const std::string& file, LoadFileCallback callback) const;
+
+  struct OpenTabParams {
+    OpenTabParams();
+    ~OpenTabParams();
+
+    std::unique_ptr<int> window_id;
+    std::unique_ptr<int> opener_tab_id;
+    std::unique_ptr<std::string> url;
+    std::unique_ptr<bool> active;
+    std::unique_ptr<bool> pinned;
+    std::unique_ptr<int> index;
+  };
+
+  // Opens a new tab given creation parameters |params|. Returns a Tab object
+  // if successful, or NULL and optionally sets |error_message| if an error
+  // occurs.
+  base::DictionaryValue* OpenTab(const OpenTabParams& params,
+                                 bool user_gesture,
+                                 std::string* error_message) const;
+
+  // Creates a Tab object (see chrome/common/extensions/api/tabs.json) with
+  // information about the state of a browser tab. Depending on the
+  // permissions of the extension, the object may or may not include sensitive
+  // data such as the tab's URL.
+  std::unique_ptr<api::tabs::Tab> CreateTabObject(
+      CefRefPtr<CefBrowserHostImpl> new_browser,
+      int opener_browser_id,
+      bool active,
+      int index) const;
+
+  // Creates a tab MutedInfo object (see chrome/common/extensions/api/tabs.json)
+  // with information about the mute state of a browser tab.
+  static std::unique_ptr<api::tabs::MutedInfo> CreateMutedInfo(
+      content::WebContents* contents);
+
+  // Returns a pointer to the associated ExtensionFunction
+  ExtensionFunction* function() { return function_; }
+  const ExtensionFunction* function() const { return function_; }
+
+ protected:
+  CefRefPtr<CefExtension> GetCefExtension() const;
+
+ private:
+  // The function for which these details have been created. Must outlive the
+  // CefExtensionFunctionDetails instance.
+  ExtensionFunction* function_;
+
+  mutable CefRefPtr<CefExtension> cef_extension_;
+
+  // Verifies correct usage of GetBrowserForTabId* methods.
+  mutable bool get_browser_called_first_time_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionFunctionDetails);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_FUNCTION_DETAILS_H_
diff --git a/src/libcef/browser/extensions/extension_host_delegate.cc b/src/libcef/browser/extensions/extension_host_delegate.cc
new file mode 100644
index 0000000..bd63230
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_host_delegate.cc
@@ -0,0 +1,74 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2014 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/browser/extensions/extension_host_delegate.h"
+
+#include "libcef/browser/extensions/extensions_browser_client.h"
+
+#include "base/logging.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+
+namespace extensions {
+
+CefExtensionHostDelegate::CefExtensionHostDelegate(
+    CefBrowserHostImpl* browser) {}
+
+CefExtensionHostDelegate::~CefExtensionHostDelegate() {}
+
+void CefExtensionHostDelegate::OnExtensionHostCreated(
+    content::WebContents* web_contents) {}
+
+void CefExtensionHostDelegate::OnRenderViewCreatedForBackgroundPage(
+    ExtensionHost* host) {}
+
+content::JavaScriptDialogManager*
+CefExtensionHostDelegate::GetJavaScriptDialogManager() {
+  // Never routed here from CefBrowserHostImpl.
+  NOTREACHED();
+  return nullptr;
+}
+
+void CefExtensionHostDelegate::CreateTab(
+    std::unique_ptr<content::WebContents> web_contents,
+    const std::string& extension_id,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture) {
+  // TODO(cef): Add support for extensions opening popup windows.
+  NOTIMPLEMENTED();
+}
+
+void CefExtensionHostDelegate::ProcessMediaAccessRequest(
+    content::WebContents* web_contents,
+    const content::MediaStreamRequest& request,
+    content::MediaResponseCallback callback,
+    const Extension* extension) {
+  // Never routed here from CefBrowserHostImpl.
+  NOTREACHED();
+}
+
+bool CefExtensionHostDelegate::CheckMediaAccessPermission(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& security_origin,
+    blink::mojom::MediaStreamType type,
+    const Extension* extension) {
+  // Never routed here from CefBrowserHostImpl.
+  NOTREACHED();
+  return false;
+}
+
+content::PictureInPictureResult CefExtensionHostDelegate::EnterPictureInPicture(
+    content::WebContents* web_contents,
+    const viz::SurfaceId& surface_id,
+    const gfx::Size& natural_size) {
+  NOTREACHED();
+  return content::PictureInPictureResult::kNotSupported;
+}
+
+void CefExtensionHostDelegate::ExitPictureInPicture() {
+  NOTREACHED();
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extension_host_delegate.h b/src/libcef/browser/extensions/extension_host_delegate.h
new file mode 100644
index 0000000..31e76fa
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_host_delegate.h
@@ -0,0 +1,49 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2014 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef LIBCEF_BROWSER_EXTENSIONS_EXTENSION_HOST_DELEGATE_H_
+#define LIBCEF_BROWSER_EXTENSIONS_EXTENSION_HOST_DELEGATE_H_
+
+#include "base/macros.h"
+#include "extensions/browser/extension_host_delegate.h"
+
+class CefBrowserHostImpl;
+
+namespace extensions {
+
+class CefExtensionHostDelegate : public ExtensionHostDelegate {
+ public:
+  explicit CefExtensionHostDelegate(CefBrowserHostImpl* browser);
+  ~CefExtensionHostDelegate() override;
+
+  // ExtensionHostDelegate implementation.
+  void OnExtensionHostCreated(content::WebContents* web_contents) override;
+  void OnRenderViewCreatedForBackgroundPage(ExtensionHost* host) override;
+  content::JavaScriptDialogManager* GetJavaScriptDialogManager() override;
+  void CreateTab(std::unique_ptr<content::WebContents> web_contents,
+                 const std::string& extension_id,
+                 WindowOpenDisposition disposition,
+                 const gfx::Rect& initial_rect,
+                 bool user_gesture) override;
+  void ProcessMediaAccessRequest(content::WebContents* web_contents,
+                                 const content::MediaStreamRequest& request,
+                                 content::MediaResponseCallback callback,
+                                 const Extension* extension) override;
+  bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
+                                  const GURL& security_origin,
+                                  blink::mojom::MediaStreamType type,
+                                  const Extension* extension) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
+  void ExitPictureInPicture() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionHostDelegate);
+};
+
+}  // namespace extensions
+
+#endif  // LIBCEF_BROWSER_EXTENSIONS_EXTENSION_HOST_DELEGATE_H_
diff --git a/src/libcef/browser/extensions/extension_system.cc b/src/libcef/browser/extensions/extension_system.cc
new file mode 100644
index 0000000..341142e
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_system.cc
@@ -0,0 +1,707 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/extension_system.h"
+
+#include <string>
+
+#include "libcef/browser/extension_impl.h"
+#include "libcef/browser/extensions/pdf_extension_util.h"
+#include "libcef/browser/extensions/value_store/cef_value_store_factory.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/path_service.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/crx_file/id_util.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/render_process_host.h"
+#include "extensions/browser/api/app_runtime/app_runtime_api.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/info_map.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/browser/null_app_sorting.h"
+#include "extensions/browser/quota_service.h"
+#include "extensions/browser/renderer_startup_helper.h"
+#include "extensions/browser/runtime_data.h"
+#include "extensions/browser/service_worker_manager.h"
+#include "extensions/browser/state_store.h"
+#include "extensions/browser/unloaded_extension_reason.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/common/file_util.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/mime_types_handler.h"
+#include "extensions/common/switches.h"
+#include "net/base/mime_util.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+
+namespace extensions {
+
+namespace {
+
+// Implementation based on ComponentLoader::ParseManifest.
+std::unique_ptr<base::DictionaryValue> ParseManifest(
+    const std::string& manifest_contents) {
+  JSONStringValueDeserializer deserializer(manifest_contents);
+  std::unique_ptr<base::Value> manifest(
+      deserializer.Deserialize(nullptr, nullptr));
+
+  if (!manifest.get() || !manifest->is_dict()) {
+    LOG(ERROR) << "Failed to parse extension manifest.";
+    return nullptr;
+  }
+  // Transfer ownership to the caller.
+  return base::WrapUnique(
+      static_cast<base::DictionaryValue*>(manifest.release()));
+}
+
+void ExecuteLoadFailure(CefRefPtr<CefExtensionHandler> handler,
+                        cef_errorcode_t result) {
+  if (!handler)
+    return;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(ExecuteLoadFailure, handler, result));
+    return;
+  }
+
+  handler->OnExtensionLoadFailed(result);
+}
+
+void LoadExtensionOnUIThread(base::WeakPtr<CefExtensionSystem> context,
+                             std::unique_ptr<base::DictionaryValue> manifest,
+                             const base::FilePath& root_directory,
+                             bool internal,
+                             CefRefPtr<CefRequestContext> loader_context,
+                             CefRefPtr<CefExtensionHandler> handler) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(LoadExtensionOnUIThread, context,
+                                          std::move(manifest), root_directory,
+                                          internal, loader_context, handler));
+    return;
+  }
+
+  if (context) {
+    context->LoadExtension(std::move(manifest), root_directory, internal,
+                           loader_context, handler);
+  }
+}
+
+void LoadExtensionWithManifest(base::WeakPtr<CefExtensionSystem> context,
+                               const std::string& manifest_contents,
+                               const base::FilePath& root_directory,
+                               bool internal,
+                               CefRefPtr<CefRequestContext> loader_context,
+                               CefRefPtr<CefExtensionHandler> handler) {
+  CEF_REQUIRE_BLOCKING();
+
+  std::unique_ptr<base::DictionaryValue> manifest =
+      ParseManifest(manifest_contents);
+  if (!manifest) {
+    LOG(WARNING) << "Failed to parse extension manifest";
+    ExecuteLoadFailure(handler, ERR_INVALID_ARGUMENT);
+    return;
+  }
+
+  LoadExtensionOnUIThread(context, std::move(manifest), root_directory,
+                          internal, loader_context, handler);
+}
+
+void LoadExtensionFromDisk(base::WeakPtr<CefExtensionSystem> context,
+                           const base::FilePath& root_directory,
+                           bool internal,
+                           CefRefPtr<CefRequestContext> loader_context,
+                           CefRefPtr<CefExtensionHandler> handler) {
+  CEF_REQUIRE_BLOCKING();
+
+  base::FilePath manifest_path = root_directory.AppendASCII("manifest.json");
+  std::string manifest_contents;
+  if (!base::ReadFileToString(manifest_path, &manifest_contents)) {
+    LOG(WARNING) << "Failed to read extension manifest from "
+                 << manifest_path.MaybeAsASCII();
+    ExecuteLoadFailure(handler, ERR_FILE_NOT_FOUND);
+    return;
+  }
+
+  LoadExtensionWithManifest(context, manifest_contents, root_directory,
+                            internal, loader_context, handler);
+}
+
+}  // namespace
+
+CefExtensionSystem::CefExtensionSystem(BrowserContext* browser_context)
+    : browser_context_(browser_context),
+      initialized_(false),
+      registry_(ExtensionRegistry::Get(browser_context)),
+      renderer_helper_(
+          extensions::RendererStartupHelperFactory::GetForBrowserContext(
+              browser_context)),
+      weak_ptr_factory_(this) {
+  InitPrefs();
+}
+
+CefExtensionSystem::~CefExtensionSystem() {}
+
+void CefExtensionSystem::Init() {
+  DCHECK(!initialized_);
+
+  // There's complexity here related to the ordering of message delivery. For
+  // an extension to load correctly both the ExtensionMsg_Loaded and
+  // ExtensionMsg_ActivateExtension messages must be sent. These messages are
+  // currently sent by RendererStartupHelper, ExtensionWebContentsObserver, and
+  // this class. ExtensionMsg_Loaded is handled by Dispatcher::OnLoaded and adds
+  // the extension to |extensions_|. ExtensionMsg_ActivateExtension is handled
+  // by Dispatcher::OnActivateExtension and adds the extension to
+  // |active_extension_ids_|. If these messages are not sent correctly then
+  // ScriptContextSet::Register called from Dispatcher::DidCreateScriptContext
+  // will classify the extension incorrectly and API bindings will not be added.
+
+  // Inform the rest of the extensions system to start.
+  ready_.Signal();
+  content::NotificationService::current()->Notify(
+      NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
+      content::Source<BrowserContext>(browser_context_),
+      content::NotificationService::NoDetails());
+
+  // Add the internal PDF extension. PDF loading works as follows:
+  // 1. The PDF PPAPI plugin is registered in libcef/common/content_client.cc
+  //    ComputeBuiltInPlugins to handle the kPDFPluginOutOfProcessMimeType.
+  // 2. The PDF extension is registered by the below call to AddExtension and
+  //    associated with the "application/pdf" mime type.
+  // 3. Web content running in the owner CefBrowser requests to load a PDF file
+  //    resource with the "application/pdf" mime type. This can be via a frame
+  //    (main frame/iframe) or object/embed tag.
+  // 4. PluginResponseInterceptorURLLoaderThrottle intercepts the PDF resource
+  //    load in the browser process and registers the PDF resource as a stream
+  //    via MimeHandlerStreamManager::AddStream.
+  // 5. PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse triggers
+  //    creation of a MimeHandlerViewEmbedder in the browser process via
+  //    MimeHandlerViewAttachHelper::OverrideBodyForInterceptedResponse.
+  // 6. MimeHandlerViewEmbedder::ReadyToCommitNavigation is called and sends a
+  //    Mojo message to MimeHandlerViewContainerManager::SetInternalId in the
+  //    owner renderer process.
+  // 7. The MimeHandlerViewContainerManager is created in the owner renderer
+  //    process via MimeHandlerViewContainerManager::BindReceiver and the
+  //    SetInternalId call arrives.
+  // 8. HTMLPlugInElement::RequestObject is called in the owner renderer process
+  //    to handle the PDF file frame/object/embed tag. This results in calls to
+  //    ContentBrowserClient::GetPluginMimeTypesWithExternalHandlers (browser
+  //    process) and ContentRendererClient::IsPluginHandledExternally (owner
+  //    renderer process), and determines that the plugin should be handled
+  //    externally (handled_externally=true).
+  // 9. MimeHandlerViewContainerManager::IsManagedByContainerManager sends a
+  //    Mojo message to MimeHandlerViewEmbedder::ReadyToCreateMimeHandlerView
+  //    in the browser process.
+  // 10.MimeHandlerViewEmbedder::RenderFrameCreated triggers creation of a
+  //    MimeHandlerViewGuest and CefMimeHandlerViewGuestDelegate in the browser
+  //    process.
+  // 11.MimeHandlerViewGuest::CreateWebContents creates a new guest WebContents
+  //    (is_guest_view=true) to host the PDF extension and the PDF resource
+  //    stream is retrieved via MimeHandlerStreamManager::ReleaseStream.
+  // 12.MimeHandlerViewGuest::DidAttachToEmbedder calls
+  //    CefMimeHandlerViewGuestDelegate::OnGuestAttached to associate the guest
+  //    WebContents routing IDs with the owner CefBrowser. MimeHandlerViewGuest
+  //    then loads the extension URL (index.html) in the guest WebContents.
+  // 13.Creation of the RenderFrame in the guest renderer process triggers a
+  //    sync IPC call from CefContentRendererClient::MaybeCreateBrowser to
+  //    CefBrowserInfoManager::GetBrowserInfo in the browser process to retrieve
+  //    the CefBrowser information, which will be immediately available due to
+  //    step 12.
+  // 14.The PDF extension begins to load. Extension resource requests are
+  //    handled via ExtensionURLLoaderFactory::CreateLoaderAndStart in the
+  //    browser process. Access to PDF extension resources is checked by
+  //    CefExtensionsBrowserClient::AllowCrossRendererResourceLoad and
+  //    PDF extension resources are provided from bundle via
+  //    CefExtensionsBrowserClient::LoadResourceFromResourceBundle
+  //    and CefComponentExtensionResourceManager. Access to chrome://resources
+  //    is granted via CefExtensionWebContentsObserver::RenderViewCreated.
+  // 15.The PDF extension calls chrome.mimeHandlerPrivate.getStreamInfo
+  //    (chrome/browser/resources/pdf/browser_api.js) to retrieve the PDF
+  //    resource stream. This API is implemented using Mojo as described in
+  //    libcef/common/extensions/api/README.txt.
+  // 16.The PDF extension requests the PDF PPAPI plugin to handle
+  //    kPDFPluginOutOfProcessMimeType. Approval arrives in the guest renderer
+  //    process via ExtensionFrameHelper::OnExtensionResponse which calls
+  //    NativeExtensionBindingsSystem::HandleResponse. This triggers creation of
+  //    an HTMLPlugInElement via native V8 bindings to host the PDF plugin.
+  // 17.HTMLPlugInElement::RequestObject is called in the guest renderer process
+  //    and determines that the PDF PPAPI plugin should be handled internally
+  //    (handled_externally=false). A PluginDocument is created and
+  //    CefContentRendererClient::OverrideCreatePlugin is called to create a
+  //    WebPlugin.
+  // 18.The PDF extension and PDF plugin are now loaded. Print commands, if
+  //    any, are handled in the guest renderer process by ChromePDFPrintClient
+  //    and CefPrintRenderFrameHelperDelegate.
+  // 19.When navigating away from the PDF file or closing the owner CefBrowser
+  //    the guest WebContents will be destroyed. This triggers a call to
+  //    CefMimeHandlerViewGuestDelegate::OnGuestDetached which removes the
+  //    routing ID association with the owner CefBrowser.
+  if (PdfExtensionEnabled()) {
+    LoadExtension(ParseManifest(pdf_extension_util::GetManifest()),
+                  base::FilePath(FILE_PATH_LITERAL("pdf")), true /* internal */,
+                  nullptr, nullptr);
+  }
+
+  initialized_ = true;
+}
+
+void CefExtensionSystem::LoadExtension(
+    const base::FilePath& root_directory,
+    bool internal,
+    CefRefPtr<CefRequestContext> loader_context,
+    CefRefPtr<CefExtensionHandler> handler) {
+  CEF_REQUIRE_UIT();
+  CEF_POST_USER_VISIBLE_TASK(
+      base::BindOnce(LoadExtensionFromDisk, weak_ptr_factory_.GetWeakPtr(),
+                     root_directory, internal, loader_context, handler));
+}
+
+void CefExtensionSystem::LoadExtension(
+    const std::string& manifest_contents,
+    const base::FilePath& root_directory,
+    bool internal,
+    CefRefPtr<CefRequestContext> loader_context,
+    CefRefPtr<CefExtensionHandler> handler) {
+  CEF_REQUIRE_UIT();
+  CEF_POST_USER_VISIBLE_TASK(base::BindOnce(
+      LoadExtensionWithManifest, weak_ptr_factory_.GetWeakPtr(),
+      manifest_contents, root_directory, internal, loader_context, handler));
+}
+
+// Implementation based on ComponentLoader::Add.
+void CefExtensionSystem::LoadExtension(
+    std::unique_ptr<base::DictionaryValue> manifest,
+    const base::FilePath& root_directory,
+    bool internal,
+    CefRefPtr<CefRequestContext> loader_context,
+    CefRefPtr<CefExtensionHandler> handler) {
+  CEF_REQUIRE_UIT();
+
+// Internal extensions don't have a loader context. External extensions should.
+#if DCHECK_IS_ON()
+  if (internal) {
+    DCHECK(!loader_context);
+  } else {
+    DCHECK(loader_context);
+  }
+#endif
+
+  ComponentExtensionInfo info(manifest.get(), root_directory, internal);
+  const Extension* extension = LoadExtension(info, loader_context, handler);
+  if (!extension)
+    ExecuteLoadFailure(handler, ERR_FAILED);
+}
+
+// Implementation based on ExtensionService::RemoveComponentExtension.
+bool CefExtensionSystem::UnloadExtension(const std::string& extension_id) {
+  CEF_REQUIRE_UIT();
+  ExtensionMap::iterator it = extension_map_.find(extension_id);
+  if (it == extension_map_.end()) {
+    // No CEF representation so we've already unloaded it.
+    return false;
+  }
+
+  CefRefPtr<CefExtensionImpl> cef_extension =
+      static_cast<CefExtensionImpl*>(it->second.get());
+
+  // Erase first so that callbacks can't retrieve the unloaded extension.
+  extension_map_.erase(it);
+
+  cef_extension->OnExtensionUnloaded();
+
+  scoped_refptr<const Extension> extension(
+      registry_->GetInstalledExtension(extension_id));
+  UnloadExtension(extension_id, UnloadedExtensionReason::UNINSTALL);
+  if (extension.get()) {
+    registry_->TriggerOnUninstalled(
+        extension.get(), extensions::UNINSTALL_REASON_COMPONENT_REMOVED);
+  }
+
+  return true;
+}
+
+bool CefExtensionSystem::HasExtension(const std::string& extension_id) const {
+  return !!GetExtension(extension_id);
+}
+
+CefRefPtr<CefExtension> CefExtensionSystem::GetExtension(
+    const std::string& extension_id) const {
+  CEF_REQUIRE_UIT();
+  ExtensionMap::const_iterator it = extension_map_.find(extension_id);
+  if (it != extension_map_.end())
+    return it->second;
+  return nullptr;
+}
+
+CefExtensionSystem::ExtensionMap CefExtensionSystem::GetExtensions() const {
+  CEF_REQUIRE_UIT();
+  return extension_map_;
+}
+
+void CefExtensionSystem::OnRequestContextDeleted(CefRequestContext* context) {
+  CEF_REQUIRE_UIT();
+  DCHECK(context);
+
+  // Make a copy of the map because UnloadExtension will modify it.
+  // Don't add any references to |context|.
+  ExtensionMap map = extension_map_;
+  ExtensionMap::const_iterator it = map.begin();
+  for (; it != map.end(); ++it) {
+    CefRefPtr<CefExtensionImpl> cef_extension =
+        static_cast<CefExtensionImpl*>(it->second.get());
+    if (cef_extension->loader_context() == context)
+      UnloadExtension(it->first);
+  }
+}
+
+void CefExtensionSystem::Shutdown() {
+  CEF_REQUIRE_UIT();
+// Only internal extensions should exist at this point.
+#if DCHECK_IS_ON()
+  ExtensionMap::iterator it = extension_map_.begin();
+  for (; it != extension_map_.end(); ++it) {
+    CefRefPtr<CefExtensionImpl> cef_extension =
+        static_cast<CefExtensionImpl*>(it->second.get());
+    DCHECK(!cef_extension->loader_context());
+  }
+#endif
+  extension_map_.clear();
+}
+
+void CefExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
+  DCHECK(!initialized_);
+  service_worker_manager_.reset(new ServiceWorkerManager(browser_context_));
+  runtime_data_.reset(new RuntimeData(registry_));
+  quota_service_.reset(new QuotaService);
+  app_sorting_.reset(new NullAppSorting);
+}
+
+ExtensionService* CefExtensionSystem::extension_service() {
+  return nullptr;
+}
+
+RuntimeData* CefExtensionSystem::runtime_data() {
+  return runtime_data_.get();
+}
+
+ManagementPolicy* CefExtensionSystem::management_policy() {
+  return nullptr;
+}
+
+ServiceWorkerManager* CefExtensionSystem::service_worker_manager() {
+  return service_worker_manager_.get();
+}
+
+SharedUserScriptMaster* CefExtensionSystem::shared_user_script_master() {
+  return nullptr;
+}
+
+StateStore* CefExtensionSystem::state_store() {
+  return state_store_.get();
+}
+
+StateStore* CefExtensionSystem::rules_store() {
+  return rules_store_.get();
+}
+
+scoped_refptr<ValueStoreFactory> CefExtensionSystem::store_factory() {
+  return store_factory_;
+}
+
+InfoMap* CefExtensionSystem::info_map() {
+  if (!info_map_.get())
+    info_map_ = new InfoMap;
+  return info_map_.get();
+}
+
+QuotaService* CefExtensionSystem::quota_service() {
+  return quota_service_.get();
+}
+
+AppSorting* CefExtensionSystem::app_sorting() {
+  return app_sorting_.get();
+}
+
+// Implementation based on
+// ExtensionSystemImpl::RegisterExtensionWithRequestContexts.
+void CefExtensionSystem::RegisterExtensionWithRequestContexts(
+    const Extension* extension,
+    base::OnceClosure callback) {
+  // TODO(extensions): The |incognito_enabled| value should be set based on
+  // manifest settings.
+  base::PostTaskAndReply(
+      FROM_HERE, {BrowserThread::IO},
+      base::Bind(&InfoMap::AddExtension, info_map(),
+                 base::RetainedRef(extension), base::Time::Now(),
+                 true,    // incognito_enabled
+                 false),  // notifications_disabled
+      std::move(callback));
+}
+
+// Implementation based on
+// ExtensionSystemImpl::UnregisterExtensionWithRequestContexts.
+void CefExtensionSystem::UnregisterExtensionWithRequestContexts(
+    const std::string& extension_id,
+    const UnloadedExtensionReason reason) {
+  base::PostTask(
+      FROM_HERE, {BrowserThread::IO},
+      base::Bind(&InfoMap::RemoveExtension, info_map(), extension_id, reason));
+}
+
+const base::OneShotEvent& CefExtensionSystem::ready() const {
+  return ready_;
+}
+
+ContentVerifier* CefExtensionSystem::content_verifier() {
+  return nullptr;
+}
+
+std::unique_ptr<ExtensionSet> CefExtensionSystem::GetDependentExtensions(
+    const Extension* extension) {
+  return std::make_unique<ExtensionSet>();
+}
+
+void CefExtensionSystem::InstallUpdate(
+    const std::string& extension_id,
+    const std::string& public_key,
+    const base::FilePath& temp_dir,
+    bool install_immediately,
+    InstallUpdateCallback install_update_callback) {
+  NOTREACHED();
+  base::DeleteFile(temp_dir, true /* recursive */);
+}
+
+bool CefExtensionSystem::FinishDelayedInstallationIfReady(
+    const std::string& extension_id,
+    bool install_immediately) {
+  NOTREACHED();
+  return false;
+}
+
+CefExtensionSystem::ComponentExtensionInfo::ComponentExtensionInfo(
+    const base::DictionaryValue* manifest,
+    const base::FilePath& directory,
+    bool internal)
+    : manifest(manifest), root_directory(directory), internal(internal) {
+  if (!root_directory.IsAbsolute()) {
+    // This path structure is required by
+    // url_request_util::MaybeCreateURLRequestResourceBundleJob.
+    CHECK(base::PathService::Get(chrome::DIR_RESOURCES, &root_directory));
+    root_directory = root_directory.Append(directory);
+  }
+}
+
+void CefExtensionSystem::InitPrefs() {
+  store_factory_ = new CefValueStoreFactory(browser_context_->GetPath());
+
+  Profile* profile = Profile::FromBrowserContext(browser_context_);
+
+  // Two state stores. The latter, which contains declarative rules, must be
+  // loaded immediately so that the rules are ready before we issue network
+  // requests.
+  state_store_.reset(new StateStore(
+      profile, store_factory_, ValueStoreFrontend::BackendType::STATE, true));
+
+  rules_store_.reset(new StateStore(
+      profile, store_factory_, ValueStoreFrontend::BackendType::RULES, false));
+}
+
+// Implementation based on ComponentLoader::CreateExtension.
+scoped_refptr<const Extension> CefExtensionSystem::CreateExtension(
+    const ComponentExtensionInfo& info,
+    std::string* utf8_error) {
+  // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated
+  //               our component extensions to the new manifest version.
+  int flags = 0;
+  if (info.internal) {
+    // Internal extensions must have kPublicKey in the manifest.
+    flags |= Extension::REQUIRE_KEY;
+  }
+  return Extension::Create(
+      info.root_directory,
+      // Tests should continue to use the Manifest::COMMAND_LINE value here
+      // Some Chrome APIs will cause undesired effects if this is incorrect
+      // e.g.: alarms API has 1 minute minimum applied to Packed Extensions
+      info.internal ? Manifest::COMPONENT : Manifest::COMMAND_LINE,
+      *info.manifest, flags, utf8_error);
+}
+
+// Implementation based on ComponentLoader::Load and
+// ExtensionService::AddExtension.
+const Extension* CefExtensionSystem::LoadExtension(
+    const ComponentExtensionInfo& info,
+    CefRefPtr<CefRequestContext> loader_context,
+    CefRefPtr<CefExtensionHandler> handler) {
+  std::string error;
+  scoped_refptr<const Extension> extension(CreateExtension(info, &error));
+  if (!extension.get()) {
+    LOG(ERROR) << error;
+    return nullptr;
+  }
+
+  if (registry_->GetInstalledExtension(extension->id())) {
+    LOG(ERROR) << "Extension with id " << extension->id()
+               << "is already installed";
+    return nullptr;
+  }
+
+  CefRefPtr<CefExtensionImpl> cef_extension =
+      new CefExtensionImpl(extension.get(), loader_context.get(), handler);
+
+  // Insert first so that callbacks can retrieve the loaded extension.
+  extension_map_.insert(std::make_pair(extension->id(), cef_extension));
+
+  cef_extension->OnExtensionLoaded();
+
+  // This may trigger additional callbacks.
+  registry_->AddEnabled(extension.get());
+  NotifyExtensionLoaded(extension.get());
+
+  return extension.get();
+}
+
+// Implementation based on ExtensionService::UnloadExtension.
+void CefExtensionSystem::UnloadExtension(const std::string& extension_id,
+                                         UnloadedExtensionReason reason) {
+  // Make sure the extension gets deleted after we return from this function.
+  int include_mask =
+      ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::TERMINATED;
+  scoped_refptr<const Extension> extension(
+      registry_->GetExtensionById(extension_id, include_mask));
+
+  // This method can be called via PostTask, so the extension may have been
+  // unloaded by the time this runs.
+  if (!extension.get()) {
+    // In case the extension may have crashed/uninstalled. Allow the profile to
+    // clean up its RequestContexts.
+    UnregisterExtensionWithRequestContexts(extension_id, reason);
+    return;
+  }
+
+  if (registry_->disabled_extensions().Contains(extension->id())) {
+    registry_->RemoveDisabled(extension->id());
+    // Make sure the profile cleans up its RequestContexts when an already
+    // disabled extension is unloaded (since they are also tracking the disabled
+    // extensions).
+    UnregisterExtensionWithRequestContexts(extension_id, reason);
+    // Don't send the unloaded notification. It was sent when the extension
+    // was disabled.
+  } else {
+    // Remove the extension from the enabled list.
+    registry_->RemoveEnabled(extension->id());
+    NotifyExtensionUnloaded(extension.get(), reason);
+  }
+
+  content::NotificationService::current()->Notify(
+      extensions::NOTIFICATION_EXTENSION_REMOVED,
+      content::Source<content::BrowserContext>(browser_context_),
+      content::Details<const Extension>(extension.get()));
+}
+
+// Implementation based on ExtensionService::NotifyExtensionLoaded.
+void CefExtensionSystem::NotifyExtensionLoaded(const Extension* extension) {
+  // The URLRequestContexts need to be first to know that the extension
+  // was loaded, otherwise a race can arise where a renderer that is created
+  // for the extension may try to load an extension URL with an extension id
+  // that the request context doesn't yet know about. The profile is responsible
+  // for ensuring its URLRequestContexts appropriately discover the loaded
+  // extension.
+  RegisterExtensionWithRequestContexts(
+      extension,
+      base::Bind(&CefExtensionSystem::OnExtensionRegisteredWithRequestContexts,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 base::WrapRefCounted(extension)));
+
+  // Tell renderers about the loaded extension.
+  renderer_helper_->OnExtensionLoaded(*extension);
+
+  // Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded
+  // about the new extension.
+  //
+  // NOTE: It is important that this happen after notifying the renderers about
+  // the new extensions so that if we navigate to an extension URL in
+  // ExtensionRegistryObserver::OnExtensionLoaded the renderer is guaranteed to
+  // know about it.
+  registry_->TriggerOnLoaded(extension);
+
+  // Register plugins included with the extension.
+  // Implementation based on PluginManager::OnExtensionLoaded.
+  const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
+  if (handler && !handler->handler_url().empty()) {
+    content::WebPluginInfo info;
+    info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
+    info.name = base::UTF8ToUTF16(extension->name());
+    info.path = base::FilePath::FromUTF8Unsafe(extension->url().spec());
+
+    for (std::set<std::string>::const_iterator mime_type =
+             handler->mime_type_set().begin();
+         mime_type != handler->mime_type_set().end(); ++mime_type) {
+      content::WebPluginMimeType mime_type_info;
+      mime_type_info.mime_type = *mime_type;
+      base::FilePath::StringType file_extension;
+      if (net::GetPreferredExtensionForMimeType(*mime_type, &file_extension)) {
+        mime_type_info.file_extensions.push_back(
+            base::FilePath(file_extension).AsUTF8Unsafe());
+      }
+      info.mime_types.push_back(mime_type_info);
+    }
+    content::PluginService* plugin_service =
+        content::PluginService::GetInstance();
+    plugin_service->RefreshPlugins();
+    plugin_service->RegisterInternalPlugin(info, true);
+  }
+}
+
+void CefExtensionSystem::OnExtensionRegisteredWithRequestContexts(
+    scoped_refptr<const extensions::Extension> extension) {
+  registry_->AddReady(extension);
+  if (registry_->enabled_extensions().Contains(extension->id()))
+    registry_->TriggerOnReady(extension.get());
+}
+
+// Implementation based on ExtensionService::NotifyExtensionUnloaded.
+void CefExtensionSystem::NotifyExtensionUnloaded(
+    const Extension* extension,
+    UnloadedExtensionReason reason) {
+  // Unregister plugins included with the extension.
+  // Implementation based on PluginManager::OnExtensionUnloaded.
+  const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
+  if (handler && !handler->handler_url().empty()) {
+    base::FilePath path =
+        base::FilePath::FromUTF8Unsafe(extension->url().spec());
+    content::PluginService* plugin_service =
+        content::PluginService::GetInstance();
+    plugin_service->UnregisterInternalPlugin(path);
+    plugin_service->RefreshPlugins();
+  }
+
+  registry_->TriggerOnUnloaded(extension, reason);
+
+  // Tell renderers about the unloaded extension.
+  renderer_helper_->OnExtensionUnloaded(*extension);
+
+  UnregisterExtensionWithRequestContexts(extension->id(), reason);
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extension_system.h b/src/libcef/browser/extensions/extension_system.h
new file mode 100644
index 0000000..cdb7480
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_system.h
@@ -0,0 +1,204 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_SYSTEM_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_SYSTEM_H_
+
+#include <map>
+#include <memory>
+
+#include "include/cef_extension_handler.h"
+#include "include/cef_request_context.h"
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/one_shot_event.h"
+#include "extensions/browser/extension_system.h"
+
+class BrowserContextKeyedServiceFactory;
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}
+
+namespace extensions {
+
+class ExtensionRegistry;
+class InfoMap;
+class ProcessManager;
+class RendererStartupHelper;
+class SharedUserScriptMaster;
+class ValueStoreFactory;
+
+// Used to manage extensions.
+class CefExtensionSystem : public ExtensionSystem {
+ public:
+  explicit CefExtensionSystem(content::BrowserContext* browser_context);
+  ~CefExtensionSystem() override;
+
+  // Initializes the extension system.
+  void Init();
+
+  // Load an extension. For internal (built-in) extensions set |internal| to
+  // true and |loader_context| and |handler| to NULL. For external extensions
+  // set |internal| to false and |loader_context| must be the request context
+  // that loaded the extension. |handler| is optional for internal extensions
+  // and, if specified, will receive extension-related callbacks.
+  void LoadExtension(const base::FilePath& root_directory,
+                     bool internal,
+                     CefRefPtr<CefRequestContext> loader_context,
+                     CefRefPtr<CefExtensionHandler> handler);
+  void LoadExtension(const std::string& manifest_contents,
+                     const base::FilePath& root_directory,
+                     bool internal,
+                     CefRefPtr<CefRequestContext> loader_context,
+                     CefRefPtr<CefExtensionHandler> handler);
+  void LoadExtension(std::unique_ptr<base::DictionaryValue> manifest,
+                     const base::FilePath& root_directory,
+                     bool internal,
+                     CefRefPtr<CefRequestContext> loader_context,
+                     CefRefPtr<CefExtensionHandler> handler);
+
+  // Unload the external extension identified by |extension_id|.
+  bool UnloadExtension(const std::string& extension_id);
+
+  // Returns true if an extension matching |extension_id| is loaded.
+  bool HasExtension(const std::string& extension_id) const;
+
+  // Returns the loaded extention matching |extension_id| or NULL if not found.
+  CefRefPtr<CefExtension> GetExtension(const std::string& extension_id) const;
+
+  using ExtensionMap = std::map<std::string, CefRefPtr<CefExtension>>;
+
+  // Returns the map of all loaded extensions.
+  ExtensionMap GetExtensions() const;
+
+  // Called when a request context is deleted. Unregisters any external
+  // extensions that were registered with this context.
+  void OnRequestContextDeleted(CefRequestContext* context);
+
+  // KeyedService implementation:
+  void Shutdown() override;
+
+  // ExtensionSystem implementation:
+  void InitForRegularProfile(bool extensions_enabled) override;
+  ExtensionService* extension_service() override;
+  RuntimeData* runtime_data() override;
+  ManagementPolicy* management_policy() override;
+  ServiceWorkerManager* service_worker_manager() override;
+  SharedUserScriptMaster* shared_user_script_master() override;
+  StateStore* state_store() override;
+  StateStore* rules_store() override;
+  scoped_refptr<ValueStoreFactory> store_factory() override;
+  InfoMap* info_map() override;
+  QuotaService* quota_service() override;
+  AppSorting* app_sorting() override;
+  void RegisterExtensionWithRequestContexts(
+      const Extension* extension,
+      base::OnceClosure callback) override;
+  void UnregisterExtensionWithRequestContexts(
+      const std::string& extension_id,
+      const UnloadedExtensionReason reason) override;
+  const base::OneShotEvent& ready() const override;
+  ContentVerifier* content_verifier() override;
+  std::unique_ptr<ExtensionSet> GetDependentExtensions(
+      const Extension* extension) override;
+  void InstallUpdate(const std::string& extension_id,
+                     const std::string& public_key,
+                     const base::FilePath& temp_dir,
+                     bool install_immediately,
+                     InstallUpdateCallback install_update_callback) override;
+  bool FinishDelayedInstallationIfReady(const std::string& extension_id,
+                                        bool install_immediately) override;
+
+  bool initialized() const { return initialized_; }
+
+ private:
+  virtual void InitPrefs();
+
+  // Information about a registered component extension.
+  struct ComponentExtensionInfo {
+    ComponentExtensionInfo(const base::DictionaryValue* manifest,
+                           const base::FilePath& root_directory,
+                           bool internal);
+
+    // The parsed contents of the extensions's manifest file.
+    const base::DictionaryValue* manifest;
+
+    // Directory where the extension is stored.
+    base::FilePath root_directory;
+
+    // True if the extension is an internal (built-in) component.
+    bool internal;
+  };
+
+  scoped_refptr<const Extension> CreateExtension(
+      const ComponentExtensionInfo& info,
+      std::string* utf8_error);
+
+  // Loads a registered component extension.
+  const Extension* LoadExtension(const ComponentExtensionInfo& info,
+                                 CefRefPtr<CefRequestContext> loader_context,
+                                 CefRefPtr<CefExtensionHandler> handler);
+
+  // Unload the specified extension.
+  void UnloadExtension(const std::string& extension_id,
+                       extensions::UnloadedExtensionReason reason);
+
+  // Handles sending notification that |extension| was loaded.
+  void NotifyExtensionLoaded(const Extension* extension);
+
+  // Handles sending notification that |extension| was unloaded.
+  void NotifyExtensionUnloaded(const Extension* extension,
+                               UnloadedExtensionReason reason);
+
+  // Completes extension loading after URLRequestContexts have been updated
+  // on the IO thread.
+  void OnExtensionRegisteredWithRequestContexts(
+      scoped_refptr<const extensions::Extension> extension);
+
+  content::BrowserContext* browser_context_;  // Not owned.
+
+  bool initialized_;
+
+  // Data to be accessed on the IO thread. Must outlive process_manager_.
+  scoped_refptr<InfoMap> info_map_;
+
+  std::unique_ptr<ServiceWorkerManager> service_worker_manager_;
+  std::unique_ptr<RuntimeData> runtime_data_;
+  std::unique_ptr<QuotaService> quota_service_;
+  std::unique_ptr<AppSorting> app_sorting_;
+
+  std::unique_ptr<StateStore> state_store_;
+  std::unique_ptr<StateStore> rules_store_;
+  scoped_refptr<ValueStoreFactory> store_factory_;
+
+  // Signaled when the extension system has completed its startup tasks.
+  base::OneShotEvent ready_;
+
+  // Sets of enabled/disabled/terminated/blacklisted extensions. Not owned.
+  ExtensionRegistry* registry_;
+
+  // The associated RendererStartupHelper. Guaranteed to outlive the
+  // ExtensionSystem, and thus us.
+  extensions::RendererStartupHelper* renderer_helper_;
+
+  // Map of extension ID to CEF extension object.
+  ExtensionMap extension_map_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<CefExtensionSystem> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionSystem);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_SYSTEM_H_
diff --git a/src/libcef/browser/extensions/extension_system_factory.cc b/src/libcef/browser/extensions/extension_system_factory.cc
new file mode 100644
index 0000000..ed8d9f1
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_system_factory.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/extension_system_factory.h"
+
+#include "libcef/browser/extensions/extension_system.h"
+
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/browser/extension_prefs_factory.h"
+#include "extensions/browser/extension_registry_factory.h"
+
+using content::BrowserContext;
+
+namespace extensions {
+
+ExtensionSystem* CefExtensionSystemFactory::GetForBrowserContext(
+    BrowserContext* context) {
+  return static_cast<CefExtensionSystem*>(
+      GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+// static
+CefExtensionSystemFactory* CefExtensionSystemFactory::GetInstance() {
+  return base::Singleton<CefExtensionSystemFactory>::get();
+}
+
+CefExtensionSystemFactory::CefExtensionSystemFactory()
+    : ExtensionSystemProvider("CefExtensionSystem",
+                              BrowserContextDependencyManager::GetInstance()) {
+  // Other factories that this factory depends on. See
+  // libcef/common/extensions/api/README.txt for additional details.
+  DependsOn(ExtensionPrefsFactory::GetInstance());
+  DependsOn(ExtensionRegistryFactory::GetInstance());
+}
+
+CefExtensionSystemFactory::~CefExtensionSystemFactory() {}
+
+KeyedService* CefExtensionSystemFactory::BuildServiceInstanceFor(
+    BrowserContext* context) const {
+  return new CefExtensionSystem(context);
+}
+
+BrowserContext* CefExtensionSystemFactory::GetBrowserContextToUse(
+    BrowserContext* context) const {
+  // Use a separate instance for incognito.
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
+
+bool CefExtensionSystemFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extension_system_factory.h b/src/libcef/browser/extensions/extension_system_factory.h
new file mode 100644
index 0000000..fd7b99f
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_system_factory.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_SYSTEM_FACTORY_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_SYSTEM_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "extensions/browser/extension_system_provider.h"
+
+namespace extensions {
+
+// Factory that provides CefExtensionSystem.
+class CefExtensionSystemFactory : public ExtensionSystemProvider {
+ public:
+  // ExtensionSystemProvider implementation:
+  ExtensionSystem* GetForBrowserContext(
+      content::BrowserContext* context) override;
+
+  static CefExtensionSystemFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<CefExtensionSystemFactory>;
+
+  CefExtensionSystemFactory();
+  ~CefExtensionSystemFactory() override;
+
+  // BrowserContextKeyedServiceFactory implementation:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionSystemFactory);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_SYSTEM_FACTORY_H_
diff --git a/src/libcef/browser/extensions/extension_view_host.cc b/src/libcef/browser/extensions/extension_view_host.cc
new file mode 100644
index 0000000..d7af716
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_view_host.cc
@@ -0,0 +1,97 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/browser/extensions/extension_view_host.h"
+
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/extensions/extension_host_delegate.h"
+
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/browser/runtime_data.h"
+#include "third_party/blink/public/common/input/web_gesture_event.h"
+
+using content::NativeWebKeyboardEvent;
+using content::OpenURLParams;
+using content::WebContents;
+using content::WebContentsObserver;
+
+namespace extensions {
+
+CefExtensionViewHost::CefExtensionViewHost(
+    CefBrowserHostImpl* browser,
+    const Extension* extension,
+    content::BrowserContext* browser_context,
+    content::WebContents* host_contents,
+    const GURL& url,
+    ViewType host_type)
+    : ExtensionHost(new CefExtensionHostDelegate(browser),
+                    extension,
+                    browser_context,
+                    host_contents,
+                    url,
+                    host_type) {
+  // Only used for dialogs and popups.
+  DCHECK(host_type == VIEW_TYPE_EXTENSION_DIALOG ||
+         host_type == VIEW_TYPE_EXTENSION_POPUP);
+}
+
+CefExtensionViewHost::~CefExtensionViewHost() {}
+
+void CefExtensionViewHost::OnDidStopFirstLoad() {
+  // Nothing to do here, but don't call the base class method.
+}
+
+void CefExtensionViewHost::LoadInitialURL() {
+  if (!ExtensionSystem::Get(browser_context())
+           ->runtime_data()
+           ->IsBackgroundPageReady(extension())) {
+    // Make sure the background page loads before any others.
+    registrar_.Add(this,
+                   extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
+                   content::Source<Extension>(extension()));
+    return;
+  }
+
+  ExtensionHost::LoadInitialURL();
+}
+
+bool CefExtensionViewHost::IsBackgroundPage() const {
+  return false;
+}
+
+bool CefExtensionViewHost::ShouldTransferNavigation(
+    bool is_main_frame_navigation) {
+  // Block navigations that cause the main frame to navigate to non-extension
+  // content (i.e. to web content).
+  return !is_main_frame_navigation;
+}
+
+bool CefExtensionViewHost::PreHandleGestureEvent(
+    content::WebContents* source,
+    const blink::WebGestureEvent& event) {
+  // Disable pinch zooming.
+  return blink::WebInputEvent::IsPinchGestureEventType(event.GetType());
+}
+
+WebContents* CefExtensionViewHost::GetVisibleWebContents() const {
+  if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP)
+    return host_contents();
+  return nullptr;
+}
+
+void CefExtensionViewHost::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  DCHECK_EQ(type, extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY);
+  DCHECK(ExtensionSystem::Get(browser_context())
+             ->runtime_data()
+             ->IsBackgroundPageReady(extension()));
+  LoadInitialURL();
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extension_view_host.h b/src/libcef/browser/extensions/extension_view_host.h
new file mode 100644
index 0000000..438f7e0
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_view_host.h
@@ -0,0 +1,64 @@
+// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_VIEW_HOST_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_VIEW_HOST_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "extensions/browser/extension_host.h"
+
+class CefBrowserHostImpl;
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace extensions {
+
+// The ExtensionHost for an extension that backs a view in the browser UI. For
+// example, this could be an extension popup or dialog, but not a background
+// page. Object lifespan is managed by CefBrowserHostImpl. Based on
+// chrome/browser/extensions/extension_view_host.h.
+class CefExtensionViewHost : public ExtensionHost,
+                             public content::NotificationObserver {
+ public:
+  CefExtensionViewHost(CefBrowserHostImpl* browser,
+                       const Extension* extension,
+                       content::BrowserContext* browser_context,
+                       content::WebContents* host_contents,
+                       const GURL& url,
+                       ViewType host_type);
+  ~CefExtensionViewHost() override;
+
+  // ExtensionHost methods:
+  void OnDidStopFirstLoad() override;
+  void LoadInitialURL() override;
+  bool IsBackgroundPage() const override;
+
+  // content::WebContentsDelegate methods:
+  bool ShouldTransferNavigation(bool is_main_frame_navigation) override;
+  bool PreHandleGestureEvent(content::WebContents* source,
+                             const blink::WebGestureEvent& event) override;
+
+  // extensions::ExtensionFunctionDispatcher::Delegate methods:
+  content::WebContents* GetVisibleWebContents() const override;
+
+  // content::NotificationObserver methods:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
+
+ private:
+  content::NotificationRegistrar registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionViewHost);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_VIEW_HOST_H_
diff --git a/src/libcef/browser/extensions/extension_web_contents_observer.cc b/src/libcef/browser/extensions/extension_web_contents_observer.cc
new file mode 100644
index 0000000..1f825d0
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_web_contents_observer.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/extension_web_contents_observer.h"
+
+#include "chrome/common/webui_url_constants.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/url_constants.h"
+
+namespace extensions {
+
+CefExtensionWebContentsObserver::CefExtensionWebContentsObserver(
+    content::WebContents* web_contents)
+    : ExtensionWebContentsObserver(web_contents),
+      script_executor_(new ScriptExecutor(web_contents)) {}
+
+CefExtensionWebContentsObserver::~CefExtensionWebContentsObserver() {}
+
+// static
+void CefExtensionWebContentsObserver::CreateForWebContents(
+    content::WebContents* web_contents) {
+  content::WebContentsUserData<
+      CefExtensionWebContentsObserver>::CreateForWebContents(web_contents);
+
+  // Initialize this instance if necessary.
+  FromWebContents(web_contents)->Initialize();
+}
+
+void CefExtensionWebContentsObserver::RenderFrameCreated(
+    content::RenderFrameHost* render_frame_host) {
+  ExtensionWebContentsObserver::RenderFrameCreated(render_frame_host);
+
+  const Extension* extension = GetExtensionFromFrame(render_frame_host, false);
+  if (!extension)
+    return;
+
+  int process_id = render_frame_host->GetProcess()->GetID();
+  auto policy = content::ChildProcessSecurityPolicy::GetInstance();
+
+  // Components of chrome that are implemented as extensions or platform apps
+  // are allowed to use chrome://resources/ and chrome://theme/ URLs.
+  if ((extension->is_extension() || extension->is_platform_app()) &&
+      Manifest::IsComponentLocation(extension->location())) {
+    policy->GrantRequestOrigin(
+        process_id, url::Origin::Create(GURL(content::kChromeUIResourcesURL)));
+    policy->GrantRequestOrigin(
+        process_id, url::Origin::Create(GURL(chrome::kChromeUIThemeURL)));
+  }
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(CefExtensionWebContentsObserver)
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extension_web_contents_observer.h b/src/libcef/browser/extensions/extension_web_contents_observer.h
new file mode 100644
index 0000000..742e535
--- /dev/null
+++ b/src/libcef/browser/extensions/extension_web_contents_observer.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_WEB_CONTENTS_OBSERVER_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_WEB_CONTENTS_OBSERVER_H_
+
+#include <memory>
+
+#include "base/observer_list.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "extensions/browser/extension_web_contents_observer.h"
+#include "extensions/browser/script_executor.h"
+
+namespace extensions {
+
+// The CEF version of ExtensionWebContentsObserver.
+class CefExtensionWebContentsObserver
+    : public ExtensionWebContentsObserver,
+      public content::WebContentsUserData<CefExtensionWebContentsObserver> {
+ public:
+  ~CefExtensionWebContentsObserver() override;
+
+  // Creates and initializes an instance of this class for the given
+  // |web_contents|, if it doesn't already exist.
+  static void CreateForWebContents(content::WebContents* web_contents);
+
+  ScriptExecutor* script_executor() { return script_executor_.get(); }
+
+ private:
+  friend class content::WebContentsUserData<CefExtensionWebContentsObserver>;
+
+  explicit CefExtensionWebContentsObserver(content::WebContents* web_contents);
+
+  // content::WebContentsObserver overrides.
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+
+  std::unique_ptr<ScriptExecutor> script_executor_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionWebContentsObserver);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSION_WEB_CONTENTS_OBSERVER_H_
diff --git a/src/libcef/browser/extensions/extensions_api_client.cc b/src/libcef/browser/extensions/extensions_api_client.cc
new file mode 100644
index 0000000..a41bfc3
--- /dev/null
+++ b/src/libcef/browser/extensions/extensions_api_client.cc
@@ -0,0 +1,78 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/extensions_api_client.h"
+
+#include "include/internal/cef_types_wrappers.h"
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/extensions/api/storage/sync_value_store_cache.h"
+#include "libcef/browser/extensions/extension_web_contents_observer.h"
+#include "libcef/browser/extensions/mime_handler_view_guest_delegate.h"
+#include "libcef/browser/extensions/pdf_web_contents_helper_client.h"
+#include "libcef/browser/printing/print_view_manager.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ui/prefs/prefs_tab_helper.h"
+#include "components/pdf/browser/pdf_web_contents_helper.h"
+#include "components/zoom/zoom_controller.h"
+#include "extensions/browser/guest_view/extensions_guest_view_manager_delegate.h"
+
+namespace extensions {
+
+CefExtensionsAPIClient::CefExtensionsAPIClient() {}
+
+AppViewGuestDelegate* CefExtensionsAPIClient::CreateAppViewGuestDelegate()
+    const {
+  // TODO(extensions): Implement to support Apps.
+  NOTREACHED();
+  return nullptr;
+}
+
+std::unique_ptr<guest_view::GuestViewManagerDelegate>
+CefExtensionsAPIClient::CreateGuestViewManagerDelegate(
+    content::BrowserContext* context) const {
+  // The GuestViewManager instance associated with the returned Delegate, which
+  // will be retrieved in the future via GuestViewManager::FromBrowserContext,
+  // will be associated with the CefBrowserContext.
+  return base::WrapUnique(new extensions::ExtensionsGuestViewManagerDelegate(
+      CefBrowserContext::GetForContext(context)));
+}
+
+std::unique_ptr<MimeHandlerViewGuestDelegate>
+CefExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate(
+    MimeHandlerViewGuest* guest) const {
+  return base::WrapUnique(new CefMimeHandlerViewGuestDelegate(guest));
+}
+
+void CefExtensionsAPIClient::AttachWebContentsHelpers(
+    content::WebContents* web_contents) const {
+  PrefsTabHelper::CreateForWebContents(web_contents);
+  printing::CefPrintViewManager::CreateForWebContents(web_contents);
+
+  CefExtensionWebContentsObserver::CreateForWebContents(web_contents);
+
+  // Used by the PDF extension.
+  pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(
+      web_contents, std::unique_ptr<pdf::PDFWebContentsHelperClient>(
+                        new CefPDFWebContentsHelperClient()));
+
+  // Used by the tabs extension API.
+  zoom::ZoomController::CreateForWebContents(web_contents);
+}
+
+void CefExtensionsAPIClient::AddAdditionalValueStoreCaches(
+    content::BrowserContext* context,
+    const scoped_refptr<ValueStoreFactory>& factory,
+    const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>&
+        observers,
+    std::map<settings_namespace::Namespace, ValueStoreCache*>* caches) {
+  // Add support for chrome.storage.sync.
+  // Because we don't support syncing with Google, we follow the behavior of
+  // chrome.storage.sync as if Chrome were permanently offline, by using a local
+  // store see: https://developer.chrome.com/apps/storage for more information
+  (*caches)[settings_namespace::SYNC] = new cef::SyncValueStoreCache(factory);
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extensions_api_client.h b/src/libcef/browser/extensions/extensions_api_client.h
new file mode 100644
index 0000000..821f0b6
--- /dev/null
+++ b/src/libcef/browser/extensions/extensions_api_client.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_API_CLIENT_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_API_CLIENT_H_
+
+#include "extensions/browser/api/extensions_api_client.h"
+
+namespace extensions {
+
+class CefExtensionsAPIClient : public ExtensionsAPIClient {
+ public:
+  CefExtensionsAPIClient();
+
+  // ExtensionsAPIClient implementation.
+  AppViewGuestDelegate* CreateAppViewGuestDelegate() const override;
+  std::unique_ptr<guest_view::GuestViewManagerDelegate>
+  CreateGuestViewManagerDelegate(
+      content::BrowserContext* context) const override;
+  std::unique_ptr<MimeHandlerViewGuestDelegate>
+  CreateMimeHandlerViewGuestDelegate(
+      MimeHandlerViewGuest* guest) const override;
+  void AttachWebContentsHelpers(
+      content::WebContents* web_contents) const override;
+
+  // Storage API support.
+
+  // Add any additional value store caches (e.g. for chrome.storage.managed)
+  // to |caches|. By default adds nothing.
+  void AddAdditionalValueStoreCaches(
+      content::BrowserContext* context,
+      const scoped_refptr<ValueStoreFactory>& factory,
+      const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>&
+          observers,
+      std::map<settings_namespace::Namespace, ValueStoreCache*>* caches)
+      override;
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_API_CLIENT_H_
diff --git a/src/libcef/browser/extensions/extensions_browser_api_provider.cc b/src/libcef/browser/extensions/extensions_browser_api_provider.cc
new file mode 100644
index 0000000..a2e28e0
--- /dev/null
+++ b/src/libcef/browser/extensions/extensions_browser_api_provider.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/chrome_api_registration.h"
+#include "libcef/browser/extensions/extensions_browser_api_provider.h"
+
+//#include "cef/libcef/browser/extensions/api/generated_api_registration.h"
+#include "extensions/browser/api/generated_api_registration.h"
+
+namespace extensions {
+
+CefExtensionsBrowserAPIProvider::CefExtensionsBrowserAPIProvider() =
+    default;
+CefExtensionsBrowserAPIProvider::~CefExtensionsBrowserAPIProvider() =
+    default;
+
+void CefExtensionsBrowserAPIProvider::RegisterExtensionFunctions(
+    ExtensionFunctionRegistry* registry) {
+  // CEF-only APIs.
+  // TODO(cef): Enable if/when CEF exposes its own Mojo APIs. See
+  // libcef/common/extensions/api/README.txt for details.
+  // api::cef::CefGeneratedFunctionRegistry::RegisterAll(registry);
+
+  // Chrome APIs whitelisted by CEF.
+  api::cef::ChromeFunctionRegistry::RegisterAll(registry);
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extensions_browser_api_provider.h b/src/libcef/browser/extensions/extensions_browser_api_provider.h
new file mode 100644
index 0000000..f6fce14
--- /dev/null
+++ b/src/libcef/browser/extensions/extensions_browser_api_provider.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_BROWSER_API_PROVIDER_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_BROWSER_API_PROVIDER_H_
+
+#include "base/macros.h"
+#include "extensions/browser/extensions_browser_api_provider.h"
+
+namespace extensions {
+
+class CefExtensionsBrowserAPIProvider : public ExtensionsBrowserAPIProvider {
+ public:
+  CefExtensionsBrowserAPIProvider();
+  ~CefExtensionsBrowserAPIProvider() override;
+
+  void RegisterExtensionFunctions(ExtensionFunctionRegistry* registry) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionsBrowserAPIProvider);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_BROWSER_API_PROVIDER_H_
diff --git a/src/libcef/browser/extensions/extensions_browser_client.cc b/src/libcef/browser/extensions/extensions_browser_client.cc
new file mode 100644
index 0000000..755f506
--- /dev/null
+++ b/src/libcef/browser/extensions/extensions_browser_client.cc
@@ -0,0 +1,367 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/extensions_browser_client.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/extensions/component_extension_resource_manager.h"
+#include "libcef/browser/extensions/extension_system.h"
+#include "libcef/browser/extensions/extension_system_factory.h"
+#include "libcef/browser/extensions/extension_web_contents_observer.h"
+#include "libcef/browser/extensions/extensions_api_client.h"
+#include "libcef/browser/extensions/extensions_browser_api_provider.h"
+#include "libcef/browser/request_context_impl.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/chrome_url_request_util.h"
+#include "chrome/browser/extensions/event_router_forwarder.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "extensions/browser/api/extensions_api_client.h"
+#include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
+#include "extensions/browser/api/runtime/runtime_api_delegate.h"
+#include "extensions/browser/app_sorting.h"
+#include "extensions/browser/core_extensions_browser_api_provider.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_host_delegate.h"
+#include "extensions/browser/extensions_browser_interface_binders.h"
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+#include "extensions/browser/url_request_util.h"
+#include "extensions/common/api/mime_handler.mojom.h"
+#include "extensions/common/constants.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+
+namespace extensions {
+
+namespace {
+
+void BindMimeHandlerService(
+    content::RenderFrameHost* frame_host,
+    mojo::PendingReceiver<extensions::mime_handler::MimeHandlerService>
+        receiver) {
+  auto* web_contents = content::WebContents::FromRenderFrameHost(frame_host);
+  if (!web_contents)
+    return;
+
+  auto* guest_view =
+      extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
+  if (!guest_view)
+    return;
+  extensions::MimeHandlerServiceImpl::Create(guest_view->GetStreamWeakPtr(),
+                                             std::move(receiver));
+}
+
+void BindBeforeUnloadControl(
+    content::RenderFrameHost* frame_host,
+    mojo::PendingReceiver<extensions::mime_handler::BeforeUnloadControl>
+        receiver) {
+  auto* web_contents = content::WebContents::FromRenderFrameHost(frame_host);
+  if (!web_contents)
+    return;
+
+  auto* guest_view =
+      extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
+  if (!guest_view)
+    return;
+  guest_view->FuseBeforeUnloadControl(std::move(receiver));
+}
+
+}  // namespace
+
+CefExtensionsBrowserClient::CefExtensionsBrowserClient()
+    : api_client_(new CefExtensionsAPIClient),
+      resource_manager_(new CefComponentExtensionResourceManager) {
+  AddAPIProvider(std::make_unique<CoreExtensionsBrowserAPIProvider>());
+  AddAPIProvider(std::make_unique<CefExtensionsBrowserAPIProvider>());
+}
+
+CefExtensionsBrowserClient::~CefExtensionsBrowserClient() {}
+
+// static
+CefExtensionsBrowserClient* CefExtensionsBrowserClient::Get() {
+  return static_cast<CefExtensionsBrowserClient*>(
+      ExtensionsBrowserClient::Get());
+}
+
+bool CefExtensionsBrowserClient::IsShuttingDown() {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::AreExtensionsDisabled(
+    const base::CommandLine& command_line,
+    BrowserContext* context) {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::IsValidContext(BrowserContext* context) {
+  return GetOriginalContext(context) != nullptr;
+}
+
+bool CefExtensionsBrowserClient::IsSameContext(BrowserContext* first,
+                                               BrowserContext* second) {
+  // Returns true if |first| and |second| share the same underlying
+  // CefBrowserContext.
+  return GetOriginalContext(first) == GetOriginalContext(second);
+}
+
+bool CefExtensionsBrowserClient::HasOffTheRecordContext(
+    BrowserContext* context) {
+  // CEF doesn't use incognito contexts.
+  return false;
+}
+
+BrowserContext* CefExtensionsBrowserClient::GetOffTheRecordContext(
+    BrowserContext* context) {
+  return nullptr;
+}
+
+BrowserContext* CefExtensionsBrowserClient::GetOriginalContext(
+    BrowserContext* context) {
+  return CefBrowserContext::GetForContext(context);
+}
+
+bool CefExtensionsBrowserClient::IsGuestSession(BrowserContext* context) const {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::IsExtensionIncognitoEnabled(
+    const std::string& extension_id,
+    content::BrowserContext* context) const {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::CanExtensionCrossIncognito(
+    const Extension* extension,
+    content::BrowserContext* context) const {
+  return false;
+}
+
+base::FilePath CefExtensionsBrowserClient::GetBundleResourcePath(
+    const network::ResourceRequest& request,
+    const base::FilePath& extension_resources_path,
+    int* resource_id) const {
+  return chrome_url_request_util::GetBundleResourcePath(
+      request, extension_resources_path, resource_id);
+}
+
+void CefExtensionsBrowserClient::LoadResourceFromResourceBundle(
+    const network::ResourceRequest& request,
+    mojo::PendingReceiver<network::mojom::URLLoader> loader,
+    const base::FilePath& resource_relative_path,
+    const int resource_id,
+    const std::string& content_security_policy,
+    mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+    bool send_cors_header) {
+  chrome_url_request_util::LoadResourceFromResourceBundle(
+      request, std::move(loader), resource_relative_path, resource_id,
+      content_security_policy, std::move(client), send_cors_header);
+}
+
+bool CefExtensionsBrowserClient::AllowCrossRendererResourceLoad(
+    const GURL& url,
+    blink::mojom::ResourceType resource_type,
+    ui::PageTransition page_transition,
+    int child_id,
+    bool is_incognito,
+    const Extension* extension,
+    const ExtensionSet& extensions,
+    const ProcessMap& process_map) {
+  bool allowed = false;
+  if (url_request_util::AllowCrossRendererResourceLoad(
+          url, resource_type, page_transition, child_id, is_incognito,
+          extension, extensions, process_map, &allowed)) {
+    return allowed;
+  }
+
+  // Couldn't determine if resource is allowed. Block the load.
+  return false;
+}
+
+PrefService* CefExtensionsBrowserClient::GetPrefServiceForContext(
+    BrowserContext* context) {
+  return static_cast<CefBrowserContext*>(context)->GetPrefs();
+}
+
+void CefExtensionsBrowserClient::GetEarlyExtensionPrefsObservers(
+    content::BrowserContext* context,
+    std::vector<EarlyExtensionPrefsObserver*>* observers) const {}
+
+ProcessManagerDelegate* CefExtensionsBrowserClient::GetProcessManagerDelegate()
+    const {
+  return nullptr;
+}
+
+std::unique_ptr<ExtensionHostDelegate>
+CefExtensionsBrowserClient::CreateExtensionHostDelegate() {
+  // CEF does not use the ExtensionHost constructor that calls this method.
+  NOTREACHED();
+  return std::unique_ptr<ExtensionHostDelegate>();
+}
+
+bool CefExtensionsBrowserClient::CreateBackgroundExtensionHost(
+    const Extension* extension,
+    content::BrowserContext* browser_context,
+    const GURL& url,
+    ExtensionHost** host) {
+  CefBrowserContext* browser_context_impl =
+      CefBrowserContext::GetForContext(browser_context);
+
+  // A CEF representation should always exist.
+  CefRefPtr<CefExtension> cef_extension =
+      browser_context_impl->extension_system()->GetExtension(extension->id());
+  DCHECK(cef_extension);
+  if (!cef_extension) {
+    // Cancel the background host creation.
+    return true;
+  }
+
+  // Always use the same request context that the extension was registered with.
+  // GetLoaderContext() will return NULL for internal extensions.
+  CefRefPtr<CefRequestContext> request_context =
+      cef_extension->GetLoaderContext();
+  if (!request_context) {
+    // Cancel the background host creation.
+    return true;
+  }
+
+  CefBrowserHostImpl::CreateParams create_params;
+  create_params.url = url;
+  create_params.request_context = request_context;
+
+  CefRefPtr<CefExtensionHandler> handler = cef_extension->GetHandler();
+  if (handler.get() && handler->OnBeforeBackgroundBrowser(
+                           cef_extension, url.spec(), create_params.client,
+                           create_params.settings)) {
+    // Cancel the background host creation.
+    return true;
+  }
+
+  // This triggers creation of the background host.
+  create_params.extension = extension;
+  create_params.extension_host_type =
+      extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE;
+
+  // Browser creation may fail under certain rare circumstances. Fail the
+  // background host creation in that case.
+  CefRefPtr<CefBrowserHostImpl> browser =
+      CefBrowserHostImpl::Create(create_params);
+  if (browser) {
+    *host = browser->extension_host();
+    DCHECK(*host);
+  }
+  return true;
+}
+
+bool CefExtensionsBrowserClient::DidVersionUpdate(BrowserContext* context) {
+  // TODO(jamescook): We might want to tell extensions when app_shell updates.
+  return false;
+}
+
+void CefExtensionsBrowserClient::PermitExternalProtocolHandler() {}
+
+bool CefExtensionsBrowserClient::IsInDemoMode() {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::IsScreensaverInDemoMode(
+    const std::string& app_id) {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::IsRunningInForcedAppMode() {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::IsAppModeForcedForApp(
+    const ExtensionId& extension_id) {
+  return false;
+}
+
+bool CefExtensionsBrowserClient::IsLoggedInAsPublicAccount() {
+  return false;
+}
+
+ExtensionSystemProvider*
+CefExtensionsBrowserClient::GetExtensionSystemFactory() {
+  return CefExtensionSystemFactory::GetInstance();
+}
+
+void CefExtensionsBrowserClient::RegisterBrowserInterfaceBindersForFrame(
+    service_manager::BinderMapWithContext<content::RenderFrameHost*>* map,
+    content::RenderFrameHost* render_frame_host,
+    const Extension* extension) const {
+  PopulateExtensionFrameBinders(map, render_frame_host, extension);
+
+  map->Add<extensions::mime_handler::MimeHandlerService>(
+      base::BindRepeating(&BindMimeHandlerService));
+  map->Add<extensions::mime_handler::BeforeUnloadControl>(
+      base::BindRepeating(&BindBeforeUnloadControl));
+}
+
+std::unique_ptr<RuntimeAPIDelegate>
+CefExtensionsBrowserClient::CreateRuntimeAPIDelegate(
+    content::BrowserContext* context) const {
+  // TODO(extensions): Implement to support Apps.
+  NOTREACHED();
+  return nullptr;
+}
+
+const ComponentExtensionResourceManager*
+CefExtensionsBrowserClient::GetComponentExtensionResourceManager() {
+  return resource_manager_.get();
+}
+
+void CefExtensionsBrowserClient::BroadcastEventToRenderers(
+    events::HistogramValue histogram_value,
+    const std::string& event_name,
+    std::unique_ptr<base::ListValue> args,
+    bool dispatch_to_off_the_record_profiles) {
+  g_browser_process->extension_event_router_forwarder()
+      ->BroadcastEventToRenderers(histogram_value, event_name, std::move(args),
+                                  GURL(), dispatch_to_off_the_record_profiles);
+}
+
+ExtensionCache* CefExtensionsBrowserClient::GetExtensionCache() {
+  // Only used by Chrome via ExtensionService.
+  NOTREACHED();
+  return nullptr;
+}
+
+bool CefExtensionsBrowserClient::IsBackgroundUpdateAllowed() {
+  return true;
+}
+
+bool CefExtensionsBrowserClient::IsMinBrowserVersionSupported(
+    const std::string& min_version) {
+  return true;
+}
+
+ExtensionWebContentsObserver*
+CefExtensionsBrowserClient::GetExtensionWebContentsObserver(
+    content::WebContents* web_contents) {
+  return CefExtensionWebContentsObserver::FromWebContents(web_contents);
+}
+
+KioskDelegate* CefExtensionsBrowserClient::GetKioskDelegate() {
+  NOTREACHED();
+  return nullptr;
+}
+
+bool CefExtensionsBrowserClient::IsLockScreenContext(
+    content::BrowserContext* context) {
+  return false;
+}
+
+std::string CefExtensionsBrowserClient::GetApplicationLocale() {
+  return g_browser_process->GetApplicationLocale();
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/extensions_browser_client.h b/src/libcef/browser/extensions/extensions_browser_client.h
new file mode 100644
index 0000000..cd82bbb
--- /dev/null
+++ b/src/libcef/browser/extensions/extensions_browser_client.h
@@ -0,0 +1,119 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_BROWSER_CLIENT_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_BROWSER_CLIENT_H_
+
+#include "base/compiler_specific.h"
+#include "extensions/browser/extensions_browser_client.h"
+
+namespace extensions {
+
+class ExtensionsAPIClient;
+
+// An ExtensionsBrowserClient that supports a single content::BrowserContent
+// with no related incognito context.
+class CefExtensionsBrowserClient : public ExtensionsBrowserClient {
+ public:
+  CefExtensionsBrowserClient();
+  ~CefExtensionsBrowserClient() override;
+
+  // Returns the singleton CefExtensionsBrowserClient instance.
+  static CefExtensionsBrowserClient* Get();
+
+  // ExtensionsBrowserClient overrides:
+  bool IsShuttingDown() override;
+  bool AreExtensionsDisabled(const base::CommandLine& command_line,
+                             content::BrowserContext* context) override;
+  bool IsValidContext(content::BrowserContext* context) override;
+  bool IsSameContext(content::BrowserContext* first,
+                     content::BrowserContext* second) override;
+  bool HasOffTheRecordContext(content::BrowserContext* context) override;
+  content::BrowserContext* GetOffTheRecordContext(
+      content::BrowserContext* context) override;
+  content::BrowserContext* GetOriginalContext(
+      content::BrowserContext* context) override;
+  bool IsGuestSession(content::BrowserContext* context) const override;
+  bool IsExtensionIncognitoEnabled(
+      const std::string& extension_id,
+      content::BrowserContext* context) const override;
+  bool CanExtensionCrossIncognito(
+      const Extension* extension,
+      content::BrowserContext* context) const override;
+  base::FilePath GetBundleResourcePath(
+      const network::ResourceRequest& request,
+      const base::FilePath& extension_resources_path,
+      int* resource_id) const override;
+  void LoadResourceFromResourceBundle(
+      const network::ResourceRequest& request,
+      mojo::PendingReceiver<network::mojom::URLLoader> loader,
+      const base::FilePath& resource_relative_path,
+      const int resource_id,
+      const std::string& content_security_policy,
+      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+      bool send_cors_header) override;
+  bool AllowCrossRendererResourceLoad(const GURL& url,
+                                      blink::mojom::ResourceType resource_type,
+                                      ui::PageTransition page_transition,
+                                      int child_id,
+                                      bool is_incognito,
+                                      const Extension* extension,
+                                      const ExtensionSet& extensions,
+                                      const ProcessMap& process_map) override;
+  PrefService* GetPrefServiceForContext(
+      content::BrowserContext* context) override;
+  void GetEarlyExtensionPrefsObservers(
+      content::BrowserContext* context,
+      std::vector<EarlyExtensionPrefsObserver*>* observers) const override;
+  ProcessManagerDelegate* GetProcessManagerDelegate() const override;
+  std::unique_ptr<ExtensionHostDelegate> CreateExtensionHostDelegate() override;
+  bool CreateBackgroundExtensionHost(const Extension* extension,
+                                     content::BrowserContext* browser_context,
+                                     const GURL& url,
+                                     ExtensionHost** host) override;
+  bool DidVersionUpdate(content::BrowserContext* context) override;
+  void PermitExternalProtocolHandler() override;
+  bool IsInDemoMode() override;
+  bool IsScreensaverInDemoMode(const std::string& app_id) override;
+  bool IsRunningInForcedAppMode() override;
+  bool IsAppModeForcedForApp(const ExtensionId& extension_id) override;
+  bool IsLoggedInAsPublicAccount() override;
+  ExtensionSystemProvider* GetExtensionSystemFactory() override;
+  void RegisterBrowserInterfaceBindersForFrame(
+      service_manager::BinderMapWithContext<content::RenderFrameHost*>*
+          binder_map,
+      content::RenderFrameHost* render_frame_host,
+      const Extension* extension) const override;
+  std::unique_ptr<RuntimeAPIDelegate> CreateRuntimeAPIDelegate(
+      content::BrowserContext* context) const override;
+  const ComponentExtensionResourceManager*
+  GetComponentExtensionResourceManager() override;
+  void BroadcastEventToRenderers(
+      events::HistogramValue histogram_value,
+      const std::string& event_name,
+      std::unique_ptr<base::ListValue> args,
+      bool dispatch_to_off_the_record_profiles) override;
+  ExtensionCache* GetExtensionCache() override;
+  bool IsBackgroundUpdateAllowed() override;
+  bool IsMinBrowserVersionSupported(const std::string& min_version) override;
+  ExtensionWebContentsObserver* GetExtensionWebContentsObserver(
+      content::WebContents* web_contents) override;
+  KioskDelegate* GetKioskDelegate() override;
+  bool IsLockScreenContext(content::BrowserContext* context) override;
+  std::string GetApplicationLocale() override;
+
+ private:
+  // Support for extension APIs.
+  std::unique_ptr<ExtensionsAPIClient> api_client_;
+
+  // Resource manager used to supply resources from pak files.
+  std::unique_ptr<ComponentExtensionResourceManager> resource_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionsBrowserClient);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_EXTENSIONS_BROWSER_CLIENT_H_
diff --git a/src/libcef/browser/extensions/mime_handler_view_guest_delegate.cc b/src/libcef/browser/extensions/mime_handler_view_guest_delegate.cc
new file mode 100644
index 0000000..32af32c
--- /dev/null
+++ b/src/libcef/browser/extensions/mime_handler_view_guest_delegate.cc
@@ -0,0 +1,88 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/mime_handler_view_guest_delegate.h"
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/browser_info.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/osr/web_contents_view_osr.h"
+
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+
+namespace extensions {
+
+CefMimeHandlerViewGuestDelegate::CefMimeHandlerViewGuestDelegate(
+    MimeHandlerViewGuest* guest)
+    : guest_(guest), owner_web_contents_(guest_->owner_web_contents()) {}
+
+CefMimeHandlerViewGuestDelegate::~CefMimeHandlerViewGuestDelegate() {}
+
+void CefMimeHandlerViewGuestDelegate::OverrideWebContentsCreateParams(
+    content::WebContents::CreateParams* params) {
+  DCHECK(params->guest_delegate);
+
+  CefRefPtr<CefBrowserHostImpl> owner_browser =
+      CefBrowserHostImpl::GetBrowserForContents(owner_web_contents_);
+  DCHECK(owner_browser);
+
+  if (owner_browser->IsWindowless()) {
+    CefWebContentsViewOSR* view_osr = new CefWebContentsViewOSR(
+        owner_browser->GetBackgroundColor(), false, false);
+    params->view = view_osr;
+    params->delegate_view = view_osr;
+  }
+}
+
+void CefMimeHandlerViewGuestDelegate::OnGuestAttached() {
+  content::WebContents* web_contents = guest_->web_contents();
+  DCHECK(web_contents);
+
+  CefRefPtr<CefBrowserHostImpl> owner_browser =
+      CefBrowserHostImpl::GetBrowserForContents(owner_web_contents_);
+  DCHECK(owner_browser);
+
+  // Associate guest state information with the owner browser.
+  owner_browser->browser_info()->MaybeCreateFrame(web_contents->GetMainFrame(),
+                                                  true /* is_guest_view */);
+}
+
+void CefMimeHandlerViewGuestDelegate::OnGuestDetached() {
+  content::WebContents* web_contents = guest_->web_contents();
+  DCHECK(web_contents);
+
+  CefRefPtr<CefBrowserHostImpl> owner_browser =
+      CefBrowserHostImpl::GetBrowserForContents(owner_web_contents_);
+  DCHECK(owner_browser);
+
+  // Disassociate guest state information with the owner browser.
+  owner_browser->browser_info()->RemoveFrame(web_contents->GetMainFrame());
+}
+
+bool CefMimeHandlerViewGuestDelegate::HandleContextMenu(
+    content::WebContents* web_contents,
+    const content::ContextMenuParams& params) {
+  content::ContextMenuParams new_params = params;
+
+  gfx::Point guest_coordinates =
+      static_cast<content::WebContentsImpl*>(web_contents)
+          ->GetBrowserPluginGuest()
+          ->GetScreenCoordinates(gfx::Point());
+
+  // Adjust (x,y) position for offset from guest to embedder.
+  new_params.x += guest_coordinates.x();
+  new_params.y += guest_coordinates.y();
+
+  CefRefPtr<CefBrowserHostImpl> owner_browser =
+      CefBrowserHostImpl::GetBrowserForContents(owner_web_contents_);
+  DCHECK(owner_browser);
+
+  return owner_browser->HandleContextMenu(web_contents, new_params);
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/mime_handler_view_guest_delegate.h b/src/libcef/browser/extensions/mime_handler_view_guest_delegate.h
new file mode 100644
index 0000000..46e34cf
--- /dev/null
+++ b/src/libcef/browser/extensions/mime_handler_view_guest_delegate.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_MIME_HANDLER_VIEW_GUEST_DELEGATE_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_MIME_HANDLER_VIEW_GUEST_DELEGATE_H_
+
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h"
+
+namespace content {
+struct ContextMenuParams;
+}
+
+namespace extensions {
+
+class CefMimeHandlerViewGuestDelegate : public MimeHandlerViewGuestDelegate {
+ public:
+  explicit CefMimeHandlerViewGuestDelegate(MimeHandlerViewGuest* guest);
+  ~CefMimeHandlerViewGuestDelegate() override;
+
+  // MimeHandlerViewGuestDelegate methods.
+  void OverrideWebContentsCreateParams(
+      content::WebContents::CreateParams* params) override;
+  void OnGuestAttached() override;
+  void OnGuestDetached() override;
+  bool HandleContextMenu(content::WebContents* web_contents,
+                         const content::ContextMenuParams& params) override;
+
+ private:
+  MimeHandlerViewGuest* guest_;  // Owns us.
+  content::WebContents* owner_web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefMimeHandlerViewGuestDelegate);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_MIME_HANDLER_VIEW_GUEST_DELEGATE_H_
diff --git a/src/libcef/browser/extensions/pdf_extension_util.cc b/src/libcef/browser/extensions/pdf_extension_util.cc
new file mode 100644
index 0000000..9e3de9e
--- /dev/null
+++ b/src/libcef/browser/extensions/pdf_extension_util.cc
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/pdf_extension_util.h"
+
+#include "base/strings/string_util.h"
+#include "chrome/grit/browser_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace extensions {
+namespace pdf_extension_util {
+
+namespace {
+
+// Tags in the manifest to be replaced.
+const char kNameTag[] = "<NAME>";
+
+}  // namespace
+
+// These should match the keys for the Chrome and Chromium PDF Viewer entries in
+// chrome/browser/resources/plugin_metadata/plugins_*.json.
+#if defined(GOOGLE_CHROME_BUILD)
+const char kPdfResourceIdentifier[] = "google-chrome-pdf";
+#else
+const char kPdfResourceIdentifier[] = "chromium-pdf";
+#endif
+
+// Match the GOOGLE_CHROME_BUILD value from ChromeContentClient::kPDFPluginName
+// to avoid breaking Websites that specifically look for this string in the
+// plugin list.
+const char kPdfPluginName[] = "Chrome PDF Viewer";
+
+std::string GetManifest() {
+  std::string manifest_contents =
+      ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
+          IDR_PDF_MANIFEST);
+  DCHECK(manifest_contents.find(kNameTag) != std::string::npos);
+  base::ReplaceFirstSubstringAfterOffset(&manifest_contents, 0, kNameTag,
+                                         kPdfPluginName);
+
+  return manifest_contents;
+}
+
+}  // namespace pdf_extension_util
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/pdf_extension_util.h b/src/libcef/browser/extensions/pdf_extension_util.h
new file mode 100644
index 0000000..7293af2
--- /dev/null
+++ b/src/libcef/browser/extensions/pdf_extension_util.h
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_PDF_EXTENSION_UTIL_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_PDF_EXTENSION_UTIL_H_
+
+#include <string>
+
+namespace extensions {
+namespace pdf_extension_util {
+
+// The ResourceIdentifier for the PDF Viewer plugin.
+extern const char kPdfResourceIdentifier[];
+
+// The name of the PDF Viewer plugin.
+extern const char kPdfPluginName[];
+
+// Return the extensions manifest for PDF. The manifest is loaded from
+// browser_resources.grd and certain fields are replaced based on what chrome
+// flags are enabled.
+std::string GetManifest();
+
+}  // namespace pdf_extension_util
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_PDF_EXTENSION_UTIL_H_
diff --git a/src/libcef/browser/extensions/pdf_web_contents_helper_client.cc b/src/libcef/browser/extensions/pdf_web_contents_helper_client.cc
new file mode 100644
index 0000000..910aa0e
--- /dev/null
+++ b/src/libcef/browser/extensions/pdf_web_contents_helper_client.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/pdf_web_contents_helper_client.h"
+
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+
+namespace extensions {
+
+CefPDFWebContentsHelperClient::CefPDFWebContentsHelperClient() {}
+
+CefPDFWebContentsHelperClient::~CefPDFWebContentsHelperClient() {}
+
+void CefPDFWebContentsHelperClient::UpdateContentRestrictions(
+    content::WebContents* contents,
+    int content_restrictions) {}
+
+void CefPDFWebContentsHelperClient::OnPDFHasUnsupportedFeature(
+    content::WebContents* contents) {}
+
+void CefPDFWebContentsHelperClient::OnSaveURL(content::WebContents* contents) {}
+
+void CefPDFWebContentsHelperClient::SetPluginCanSave(
+    content::WebContents* contents,
+    bool can_save) {
+  auto* guest_view =
+      extensions::MimeHandlerViewGuest::FromWebContents(contents);
+  if (guest_view)
+    guest_view->SetPluginCanSave(can_save);
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/pdf_web_contents_helper_client.h b/src/libcef/browser/extensions/pdf_web_contents_helper_client.h
new file mode 100644
index 0000000..13ed29e
--- /dev/null
+++ b/src/libcef/browser/extensions/pdf_web_contents_helper_client.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_PDF_WEB_CONTENTS_HELPER_CLIENT_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_PDF_WEB_CONTENTS_HELPER_CLIENT_H_
+
+#include "base/macros.h"
+#include "components/pdf/browser/pdf_web_contents_helper_client.h"
+
+namespace extensions {
+
+class CefPDFWebContentsHelperClient : public pdf::PDFWebContentsHelperClient {
+ public:
+  CefPDFWebContentsHelperClient();
+  ~CefPDFWebContentsHelperClient() override;
+
+ private:
+  // pdf::PDFWebContentsHelperClient:
+  void UpdateContentRestrictions(content::WebContents* contents,
+                                 int content_restrictions) override;
+  void OnPDFHasUnsupportedFeature(content::WebContents* contents) override;
+  void OnSaveURL(content::WebContents* contents) override;
+  void SetPluginCanSave(content::WebContents* contents, bool can_save) override;
+
+  DISALLOW_COPY_AND_ASSIGN(CefPDFWebContentsHelperClient);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_PDF_WEB_CONTENTS_HELPER_CLIENT_H_
diff --git a/src/libcef/browser/extensions/value_store/cef_value_store.cc b/src/libcef/browser/extensions/value_store/cef_value_store.cc
new file mode 100644
index 0000000..569c336
--- /dev/null
+++ b/src/libcef/browser/extensions/value_store/cef_value_store.cc
@@ -0,0 +1,140 @@
+// Copyright 2017 The Chromium Embedded Framework Authors.
+// Portions copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/value_store/cef_value_store.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+
+namespace {
+
+const char kGenericErrorMessage[] = "CefValueStore configured to error";
+
+// Having this utility function allows ValueStore::Status to not have a copy
+// constructor.
+ValueStore::Status CreateStatusCopy(const ValueStore::Status& status) {
+  return ValueStore::Status(status.code, status.restore_status, status.message);
+}
+
+}  // namespace
+
+CefValueStore::CefValueStore() : read_count_(0), write_count_(0) {}
+
+CefValueStore::~CefValueStore() {}
+
+void CefValueStore::set_status_code(StatusCode status_code) {
+  status_ = ValueStore::Status(status_code, kGenericErrorMessage);
+}
+
+size_t CefValueStore::GetBytesInUse(const std::string& key) {
+  // Let SettingsStorageQuotaEnforcer implement this.
+  NOTREACHED();
+  return 0;
+}
+
+size_t CefValueStore::GetBytesInUse(const std::vector<std::string>& keys) {
+  // Let SettingsStorageQuotaEnforcer implement this.
+  NOTREACHED();
+  return 0;
+}
+
+size_t CefValueStore::GetBytesInUse() {
+  // Let SettingsStorageQuotaEnforcer implement this.
+  NOTREACHED();
+  return 0;
+}
+
+ValueStore::ReadResult CefValueStore::Get(const std::string& key) {
+  return Get(std::vector<std::string>(1, key));
+}
+
+ValueStore::ReadResult CefValueStore::Get(
+    const std::vector<std::string>& keys) {
+  read_count_++;
+  if (!status_.ok())
+    return ReadResult(CreateStatusCopy(status_));
+
+  auto settings = std::make_unique<base::DictionaryValue>();
+  for (std::vector<std::string>::const_iterator it = keys.begin();
+       it != keys.end(); ++it) {
+    base::Value* value = nullptr;
+    if (storage_.GetWithoutPathExpansion(*it, &value)) {
+      settings->SetWithoutPathExpansion(*it, value->CreateDeepCopy());
+    }
+  }
+  return ReadResult(std::move(settings), CreateStatusCopy(status_));
+}
+
+ValueStore::ReadResult CefValueStore::Get() {
+  read_count_++;
+  if (!status_.ok())
+    return ReadResult(CreateStatusCopy(status_));
+  return ReadResult(storage_.CreateDeepCopy(), CreateStatusCopy(status_));
+}
+
+ValueStore::WriteResult CefValueStore::Set(WriteOptions options,
+                                           const std::string& key,
+                                           const base::Value& value) {
+  base::DictionaryValue settings;
+  settings.SetWithoutPathExpansion(key, value.CreateDeepCopy());
+  return Set(options, settings);
+}
+
+ValueStore::WriteResult CefValueStore::Set(
+    WriteOptions options,
+    const base::DictionaryValue& settings) {
+  write_count_++;
+  if (!status_.ok())
+    return WriteResult(CreateStatusCopy(status_));
+
+  std::unique_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
+  for (base::DictionaryValue::Iterator it(settings); !it.IsAtEnd();
+       it.Advance()) {
+    base::Value* old_value = nullptr;
+    if (!storage_.GetWithoutPathExpansion(it.key(), &old_value) ||
+        !old_value->Equals(&it.value())) {
+      changes->push_back(ValueStoreChange(
+          it.key(),
+          old_value ? base::Optional<base::Value>(old_value->Clone())
+                    : base::nullopt,
+          it.value().Clone()));
+      storage_.SetWithoutPathExpansion(it.key(), it.value().CreateDeepCopy());
+    }
+  }
+  return WriteResult(std::move(changes), CreateStatusCopy(status_));
+}
+
+ValueStore::WriteResult CefValueStore::Remove(const std::string& key) {
+  return Remove(std::vector<std::string>(1, key));
+}
+
+ValueStore::WriteResult CefValueStore::Remove(
+    const std::vector<std::string>& keys) {
+  write_count_++;
+  if (!status_.ok())
+    return WriteResult(CreateStatusCopy(status_));
+
+  std::unique_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
+  for (std::vector<std::string>::const_iterator it = keys.begin();
+       it != keys.end(); ++it) {
+    std::unique_ptr<base::Value> old_value;
+    if (storage_.RemoveWithoutPathExpansion(*it, &old_value)) {
+      changes->push_back(
+          ValueStoreChange(*it, std::move(*old_value), base::nullopt));
+    }
+  }
+  return WriteResult(std::move(changes), CreateStatusCopy(status_));
+}
+
+ValueStore::WriteResult CefValueStore::Clear() {
+  std::vector<std::string> keys;
+  for (base::DictionaryValue::Iterator it(storage_); !it.IsAtEnd();
+       it.Advance()) {
+    keys.push_back(it.key());
+  }
+  return Remove(keys);
+}
\ No newline at end of file
diff --git a/src/libcef/browser/extensions/value_store/cef_value_store.h b/src/libcef/browser/extensions/value_store/cef_value_store.h
new file mode 100644
index 0000000..c76b46f
--- /dev/null
+++ b/src/libcef/browser/extensions/value_store/cef_value_store.h
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Embedded Framework Authors.
+// Portions copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_VALUE_STORE_CEF_VALUE_STORE_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_VALUE_STORE_CEF_VALUE_STORE_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "extensions/browser/value_store/value_store.h"
+
+// Implementation Based on TestingValueStore
+// ValueStore with an in-memory storage but the ability to
+// optionally fail all operations.
+class CefValueStore : public ValueStore {
+ public:
+  CefValueStore();
+  ~CefValueStore() override;
+
+  // Accessors for the number of reads/writes done by this value store. Each
+  // Get* operation (except for the BytesInUse ones) counts as one read, and
+  // each Set*/Remove/Clear operation counts as one write. This is useful in
+  // tests seeking to assert that some number of reads/writes to their
+  // underlying value store have (or have not) happened.
+  int read_count() const { return read_count_; }
+  int write_count() const { return write_count_; }
+
+  // Sets the error code for requests. If OK, errors won't be thrown.
+  // Defaults to OK.
+  void set_status_code(StatusCode status_code);
+
+  // ValueStore implementation.
+  size_t GetBytesInUse(const std::string& key) override;
+  size_t GetBytesInUse(const std::vector<std::string>& keys) override;
+  size_t GetBytesInUse() override;
+  ReadResult Get(const std::string& key) override;
+  ReadResult Get(const std::vector<std::string>& keys) override;
+  ReadResult Get() override;
+  WriteResult Set(WriteOptions options,
+                  const std::string& key,
+                  const base::Value& value) override;
+  WriteResult Set(WriteOptions options,
+                  const base::DictionaryValue& values) override;
+  WriteResult Remove(const std::string& key) override;
+  WriteResult Remove(const std::vector<std::string>& keys) override;
+  WriteResult Clear() override;
+
+ private:
+  base::DictionaryValue storage_;
+  int read_count_;
+  int write_count_;
+  ValueStore::Status status_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefValueStore);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_VALUE_STORE_CEF_VALUE_STORE_H_
diff --git a/src/libcef/browser/extensions/value_store/cef_value_store_factory.cc b/src/libcef/browser/extensions/value_store/cef_value_store_factory.cc
new file mode 100644
index 0000000..cd52d61
--- /dev/null
+++ b/src/libcef/browser/extensions/value_store/cef_value_store_factory.cc
@@ -0,0 +1,191 @@
+// Copyright 2017 The Chromium Embedded Framework Authors.
+// Portions copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/extensions/value_store/cef_value_store_factory.h"
+
+#include "base/memory/ptr_util.h"
+#include "extensions/browser/value_store/leveldb_value_store.h"
+#include "libcef/browser/extensions/value_store/cef_value_store.h"
+
+namespace {
+
+const char kUMAClientName[] = "Cef";
+
+}  // namespace
+
+namespace extensions {
+
+using SettingsNamespace = settings_namespace::Namespace;
+
+CefValueStoreFactory::StorageHelper::StorageHelper() = default;
+
+CefValueStoreFactory::StorageHelper::~StorageHelper() = default;
+
+std::set<ExtensionId> CefValueStoreFactory::StorageHelper::GetKnownExtensionIDs(
+    ModelType model_type) const {
+  std::set<ExtensionId> ids;
+  switch (model_type) {
+    case ValueStoreFactory::ModelType::APP:
+      for (const auto& key : app_stores_)
+        ids.insert(key.first);
+      break;
+    case ValueStoreFactory::ModelType::EXTENSION:
+      for (const auto& key : extension_stores_)
+        ids.insert(key.first);
+      break;
+  }
+  return ids;
+}
+
+void CefValueStoreFactory::StorageHelper::Reset() {
+  app_stores_.clear();
+  extension_stores_.clear();
+}
+
+ValueStore* CefValueStoreFactory::StorageHelper::AddValueStore(
+    const ExtensionId& extension_id,
+    ValueStore* value_store,
+    ModelType model_type) {
+  if (model_type == ValueStoreFactory::ModelType::APP) {
+    DCHECK(app_stores_.find(extension_id) == app_stores_.end());
+    app_stores_[extension_id] = value_store;
+  } else {
+    DCHECK(extension_stores_.find(extension_id) == extension_stores_.end());
+    extension_stores_[extension_id] = value_store;
+  }
+  return value_store;
+}
+
+void CefValueStoreFactory::StorageHelper::DeleteSettings(
+    const ExtensionId& extension_id,
+    ModelType model_type) {
+  switch (model_type) {
+    case ValueStoreFactory::ModelType::APP:
+      app_stores_.erase(extension_id);
+      break;
+    case ValueStoreFactory::ModelType::EXTENSION:
+      extension_stores_.erase(extension_id);
+      break;
+  }
+}
+
+bool CefValueStoreFactory::StorageHelper::HasSettings(
+    const ExtensionId& extension_id,
+    ModelType model_type) const {
+  switch (model_type) {
+    case ValueStoreFactory::ModelType::APP:
+      return app_stores_.find(extension_id) != app_stores_.end();
+    case ValueStoreFactory::ModelType::EXTENSION:
+      return extension_stores_.find(extension_id) != extension_stores_.end();
+  }
+  NOTREACHED();
+  return false;
+}
+
+ValueStore* CefValueStoreFactory::StorageHelper::GetExisting(
+    const ExtensionId& extension_id) const {
+  auto it = app_stores_.find(extension_id);
+  if (it != app_stores_.end())
+    return it->second;
+  it = extension_stores_.find(extension_id);
+  if (it != extension_stores_.end())
+    return it->second;
+  return nullptr;
+}
+
+CefValueStoreFactory::CefValueStoreFactory() = default;
+
+CefValueStoreFactory::CefValueStoreFactory(const base::FilePath& db_path)
+    : db_path_(db_path) {}
+
+CefValueStoreFactory::~CefValueStoreFactory() {}
+
+std::unique_ptr<ValueStore> CefValueStoreFactory::CreateRulesStore() {
+  if (db_path_.empty())
+    last_created_store_ = new CefValueStore();
+  else
+    last_created_store_ = new LeveldbValueStore(kUMAClientName, db_path_);
+  return base::WrapUnique(last_created_store_);
+}
+
+std::unique_ptr<ValueStore> CefValueStoreFactory::CreateStateStore() {
+  return CreateRulesStore();
+}
+
+CefValueStoreFactory::StorageHelper& CefValueStoreFactory::GetStorageHelper(
+    SettingsNamespace settings_namespace) {
+  switch (settings_namespace) {
+    case settings_namespace::LOCAL:
+      return local_helper_;
+    case settings_namespace::SYNC:
+      return sync_helper_;
+    case settings_namespace::MANAGED:
+      return managed_helper_;
+    case settings_namespace::INVALID:
+      break;
+  }
+  NOTREACHED();
+  return local_helper_;
+}
+
+std::unique_ptr<ValueStore> CefValueStoreFactory::CreateSettingsStore(
+    SettingsNamespace settings_namespace,
+    ModelType model_type,
+    const ExtensionId& extension_id) {
+  std::unique_ptr<ValueStore> settings_store(CreateRulesStore());
+  // Note: This factory is purposely keeping the raw pointers to each ValueStore
+  //       created. Tests using CefValueStoreFactory must be careful to keep
+  //       those ValueStore's alive for the duration of their test.
+  GetStorageHelper(settings_namespace)
+      .AddValueStore(extension_id, settings_store.get(), model_type);
+  return settings_store;
+}
+
+ValueStore* CefValueStoreFactory::LastCreatedStore() const {
+  return last_created_store_;
+}
+
+void CefValueStoreFactory::DeleteSettings(SettingsNamespace settings_namespace,
+                                          ModelType model_type,
+                                          const ExtensionId& extension_id) {
+  GetStorageHelper(settings_namespace).DeleteSettings(extension_id, model_type);
+}
+
+bool CefValueStoreFactory::HasSettings(SettingsNamespace settings_namespace,
+                                       ModelType model_type,
+                                       const ExtensionId& extension_id) {
+  return GetStorageHelper(settings_namespace)
+      .HasSettings(extension_id, model_type);
+}
+
+std::set<ExtensionId> CefValueStoreFactory::GetKnownExtensionIDs(
+    SettingsNamespace settings_namespace,
+    ModelType model_type) const {
+  return const_cast<CefValueStoreFactory*>(this)
+      ->GetStorageHelper(settings_namespace)
+      .GetKnownExtensionIDs(model_type);
+}
+
+ValueStore* CefValueStoreFactory::GetExisting(
+    const ExtensionId& extension_id) const {
+  ValueStore* existing_store = local_helper_.GetExisting(extension_id);
+  if (existing_store)
+    return existing_store;
+  existing_store = sync_helper_.GetExisting(extension_id);
+  if (existing_store)
+    return existing_store;
+  existing_store = managed_helper_.GetExisting(extension_id);
+  DCHECK(existing_store != nullptr);
+  return existing_store;
+}
+
+void CefValueStoreFactory::Reset() {
+  last_created_store_ = nullptr;
+  local_helper_.Reset();
+  sync_helper_.Reset();
+  managed_helper_.Reset();
+}
+
+}  // namespace extensions
diff --git a/src/libcef/browser/extensions/value_store/cef_value_store_factory.h b/src/libcef/browser/extensions/value_store/cef_value_store_factory.h
new file mode 100644
index 0000000..073b5f7
--- /dev/null
+++ b/src/libcef/browser/extensions/value_store/cef_value_store_factory.h
@@ -0,0 +1,97 @@
+// Copyright 2017 The Chromium Embedded Framework Authors.
+// Portions copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_VALUE_STORE_CEF_VALUE_STORE_FACTORY_H_
+#define CEF_LIBCEF_BROWSER_EXTENSIONS_VALUE_STORE_CEF_VALUE_STORE_FACTORY_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/files/file_path.h"
+#include "extensions/browser/value_store/value_store_factory.h"
+#include "extensions/common/extension_id.h"
+
+class ValueStore;
+
+namespace extensions {
+
+// Will either open a database on disk (if path provided) returning a
+// |LeveldbValueStore|. Otherwise a new |CefValueStore| instance will be
+// returned.
+class CefValueStoreFactory : public ValueStoreFactory {
+ public:
+  CefValueStoreFactory();
+  explicit CefValueStoreFactory(const base::FilePath& db_path);
+
+  // ValueStoreFactory
+  std::unique_ptr<ValueStore> CreateRulesStore() override;
+  std::unique_ptr<ValueStore> CreateStateStore() override;
+  std::unique_ptr<ValueStore> CreateSettingsStore(
+      settings_namespace::Namespace settings_namespace,
+      ModelType model_type,
+      const ExtensionId& extension_id) override;
+  void DeleteSettings(settings_namespace::Namespace settings_namespace,
+                      ModelType model_type,
+                      const ExtensionId& extension_id) override;
+  bool HasSettings(settings_namespace::Namespace settings_namespace,
+                   ModelType model_type,
+                   const ExtensionId& extension_id) override;
+  std::set<ExtensionId> GetKnownExtensionIDs(
+      settings_namespace::Namespace settings_namespace,
+      ModelType model_type) const override;
+
+  // Return the last created |ValueStore|. Use with caution as this may return
+  // a dangling pointer since the creator now owns the ValueStore which can be
+  // deleted at any time.
+  ValueStore* LastCreatedStore() const;
+  // Return a previously created |ValueStore| for an extension.
+  ValueStore* GetExisting(const ExtensionId& extension_id) const;
+  // Reset this class (as if just created).
+  void Reset();
+
+ private:
+  // Manages a collection of |ValueStore|'s created for an app/extension.
+  // One of these exists for each setting type.
+  class StorageHelper {
+   public:
+    StorageHelper();
+    ~StorageHelper();
+    std::set<ExtensionId> GetKnownExtensionIDs(ModelType model_type) const;
+    ValueStore* AddValueStore(const ExtensionId& extension_id,
+                              ValueStore* value_store,
+                              ModelType model_type);
+    void DeleteSettings(const ExtensionId& extension_id, ModelType model_type);
+    bool HasSettings(const ExtensionId& extension_id,
+                     ModelType model_type) const;
+    void Reset();
+    ValueStore* GetExisting(const ExtensionId& extension_id) const;
+
+   private:
+    std::map<ExtensionId, ValueStore*> app_stores_;
+    std::map<ExtensionId, ValueStore*> extension_stores_;
+
+    DISALLOW_COPY_AND_ASSIGN(StorageHelper);
+  };
+
+  StorageHelper& GetStorageHelper(
+      settings_namespace::Namespace settings_namespace);
+
+  ~CefValueStoreFactory() override;
+  base::FilePath db_path_;
+  ValueStore* last_created_store_ = nullptr;
+
+  // None of these value stores are owned by this factory, so care must be
+  // taken when calling GetExisting.
+  StorageHelper local_helper_;
+  StorageHelper sync_helper_;
+  StorageHelper managed_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefValueStoreFactory);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_BROWSER_EXTENSIONS_VALUE_STORE_CEF_VALUE_STORE_FACTORY_H_
diff --git a/src/libcef/browser/file_dialog_manager.cc b/src/libcef/browser/file_dialog_manager.cc
new file mode 100644
index 0000000..eb25c9f
--- /dev/null
+++ b/src/libcef/browser/file_dialog_manager.cc
@@ -0,0 +1,376 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/file_dialog_manager.h"
+
+#include <utility>
+
+#include "include/cef_dialog_handler.h"
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/thread_util.h"
+
+#include "content/public/browser/file_select_listener.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/common/file_chooser_file_info.h"
+#include "net/base/directory_lister.h"
+
+namespace {
+
+class CefFileDialogCallbackImpl : public CefFileDialogCallback {
+ public:
+  using CallbackType = CefFileDialogRunner::RunFileChooserCallback;
+
+  explicit CefFileDialogCallbackImpl(CallbackType callback)
+      : callback_(std::move(callback)) {}
+
+  ~CefFileDialogCallbackImpl() override {
+    if (!callback_.is_null()) {
+      // The callback is still pending. Cancel it now.
+      if (CEF_CURRENTLY_ON_UIT()) {
+        CancelNow(std::move(callback_));
+      } else {
+        CEF_POST_TASK(CEF_UIT,
+                      base::BindOnce(&CefFileDialogCallbackImpl::CancelNow,
+                                     std::move(callback_)));
+      }
+    }
+  }
+
+  void Continue(int selected_accept_filter,
+                const std::vector<CefString>& file_paths) override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (!callback_.is_null()) {
+        std::vector<base::FilePath> vec;
+        if (!file_paths.empty()) {
+          std::vector<CefString>::const_iterator it = file_paths.begin();
+          for (; it != file_paths.end(); ++it)
+            vec.push_back(base::FilePath(*it));
+        }
+        std::move(callback_).Run(selected_accept_filter, vec);
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT,
+                    base::BindOnce(&CefFileDialogCallbackImpl::Continue, this,
+                                   selected_accept_filter, file_paths));
+    }
+  }
+
+  void Cancel() override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (!callback_.is_null()) {
+        CancelNow(std::move(callback_));
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT,
+                    base::BindOnce(&CefFileDialogCallbackImpl::Cancel, this));
+    }
+  }
+
+  CallbackType Disconnect() WARN_UNUSED_RESULT { return std::move(callback_); }
+
+ private:
+  static void CancelNow(CallbackType callback) {
+    CEF_REQUIRE_UIT();
+    std::vector<base::FilePath> file_paths;
+    std::move(callback).Run(0, file_paths);
+  }
+
+  CallbackType callback_;
+
+  IMPLEMENT_REFCOUNTING(CefFileDialogCallbackImpl);
+};
+
+void RunFileDialogDismissed(CefRefPtr<CefRunFileDialogCallback> callback,
+                            int selected_accept_filter,
+                            const std::vector<base::FilePath>& file_paths) {
+  std::vector<CefString> paths;
+  if (file_paths.size() > 0) {
+    for (size_t i = 0; i < file_paths.size(); ++i)
+      paths.push_back(file_paths[i].value());
+  }
+  callback->OnFileDialogDismissed(selected_accept_filter, paths);
+}
+
+class UploadFolderHelper
+    : public net::DirectoryLister::DirectoryListerDelegate {
+ public:
+  explicit UploadFolderHelper(
+      CefFileDialogRunner::RunFileChooserCallback callback)
+      : callback_(std::move(callback)) {}
+
+  ~UploadFolderHelper() override {
+    if (!callback_.is_null()) {
+      if (CEF_CURRENTLY_ON_UIT()) {
+        CancelNow(std::move(callback_));
+      } else {
+        CEF_POST_TASK(CEF_UIT, base::BindOnce(&UploadFolderHelper::CancelNow,
+                                              std::move(callback_)));
+      }
+    }
+  }
+
+  void OnListFile(
+      const net::DirectoryLister::DirectoryListerData& data) override {
+    CEF_REQUIRE_UIT();
+    if (!data.info.IsDirectory())
+      select_files_.push_back(data.path);
+  }
+
+  void OnListDone(int error) override {
+    CEF_REQUIRE_UIT();
+    if (!callback_.is_null()) {
+      std::move(callback_).Run(0, select_files_);
+    }
+  }
+
+ private:
+  static void CancelNow(CefFileDialogRunner::RunFileChooserCallback callback) {
+    CEF_REQUIRE_UIT();
+    std::vector<base::FilePath> file_paths;
+    std::move(callback).Run(0, file_paths);
+  }
+
+  CefFileDialogRunner::RunFileChooserCallback callback_;
+  std::vector<base::FilePath> select_files_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadFolderHelper);
+};
+
+}  // namespace
+
+CefFileDialogManager::CefFileDialogManager(
+    CefBrowserHostImpl* browser,
+    std::unique_ptr<CefFileDialogRunner> runner)
+    : browser_(browser),
+      runner_(std::move(runner)),
+      file_chooser_pending_(false),
+      weak_ptr_factory_(this) {}
+
+CefFileDialogManager::~CefFileDialogManager() {}
+
+void CefFileDialogManager::Destroy() {
+  DCHECK(!file_chooser_pending_);
+  runner_.reset(nullptr);
+}
+
+void CefFileDialogManager::RunFileDialog(
+    cef_file_dialog_mode_t mode,
+    const CefString& title,
+    const CefString& default_file_path,
+    const std::vector<CefString>& accept_filters,
+    int selected_accept_filter,
+    CefRefPtr<CefRunFileDialogCallback> callback) {
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  CefFileDialogRunner::FileChooserParams params;
+  switch (mode & FILE_DIALOG_TYPE_MASK) {
+    case FILE_DIALOG_OPEN:
+      params.mode = blink::mojom::FileChooserParams::Mode::kOpen;
+      break;
+    case FILE_DIALOG_OPEN_MULTIPLE:
+      params.mode = blink::mojom::FileChooserParams::Mode::kOpenMultiple;
+      break;
+    case FILE_DIALOG_OPEN_FOLDER:
+      params.mode = blink::mojom::FileChooserParams::Mode::kUploadFolder;
+      break;
+    case FILE_DIALOG_SAVE:
+      params.mode = blink::mojom::FileChooserParams::Mode::kSave;
+      break;
+  }
+
+  DCHECK_GE(selected_accept_filter, 0);
+  params.selected_accept_filter = selected_accept_filter;
+
+  params.overwriteprompt = !!(mode & FILE_DIALOG_OVERWRITEPROMPT_FLAG);
+  params.hidereadonly = !!(mode & FILE_DIALOG_HIDEREADONLY_FLAG);
+
+  params.title = title;
+  if (!default_file_path.empty())
+    params.default_file_name = base::FilePath(default_file_path);
+
+  if (!accept_filters.empty()) {
+    std::vector<CefString>::const_iterator it = accept_filters.begin();
+    for (; it != accept_filters.end(); ++it)
+      params.accept_types.push_back(*it);
+  }
+
+  RunFileChooser(params, base::Bind(RunFileDialogDismissed, callback));
+}
+
+void CefFileDialogManager::RunFileChooser(
+    std::unique_ptr<content::FileSelectListener> listener,
+    const blink::mojom::FileChooserParams& params) {
+  CEF_REQUIRE_UIT();
+
+  CefFileDialogRunner::FileChooserParams cef_params;
+  static_cast<blink::mojom::FileChooserParams&>(cef_params) = params;
+
+  CefFileDialogRunner::RunFileChooserCallback callback;
+  if (params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
+    callback = base::BindOnce(
+        &CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback,
+        weak_ptr_factory_.GetWeakPtr(), params.mode, std::move(listener));
+  } else {
+    callback = base::BindOnce(
+        &CefFileDialogManager::OnRunFileChooserDelegateCallback,
+        weak_ptr_factory_.GetWeakPtr(), params.mode, std::move(listener));
+  }
+
+  RunFileChooserInternal(cef_params, std::move(callback));
+}
+
+void CefFileDialogManager::RunFileChooser(
+    const CefFileDialogRunner::FileChooserParams& params,
+    CefFileDialogRunner::RunFileChooserCallback callback) {
+  CefFileDialogRunner::RunFileChooserCallback host_callback =
+      base::BindOnce(&CefFileDialogManager::OnRunFileChooserCallback,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+  RunFileChooserInternal(params, std::move(host_callback));
+}
+
+void CefFileDialogManager::RunFileChooserInternal(
+    const CefFileDialogRunner::FileChooserParams& params,
+    CefFileDialogRunner::RunFileChooserCallback callback) {
+  CEF_REQUIRE_UIT();
+
+  if (file_chooser_pending_) {
+    // Dismiss the new dialog immediately.
+    std::move(callback).Run(0, std::vector<base::FilePath>());
+    return;
+  }
+
+  file_chooser_pending_ = true;
+
+  bool handled = false;
+
+  if (browser_->client().get()) {
+    CefRefPtr<CefDialogHandler> handler =
+        browser_->client()->GetDialogHandler();
+    if (handler.get()) {
+      int mode = FILE_DIALOG_OPEN;
+      switch (params.mode) {
+        case blink::mojom::FileChooserParams::Mode::kOpen:
+          mode = FILE_DIALOG_OPEN;
+          break;
+        case blink::mojom::FileChooserParams::Mode::kOpenMultiple:
+          mode = FILE_DIALOG_OPEN_MULTIPLE;
+          break;
+        case blink::mojom::FileChooserParams::Mode::kUploadFolder:
+          mode = FILE_DIALOG_OPEN_FOLDER;
+          break;
+        case blink::mojom::FileChooserParams::Mode::kSave:
+          mode = FILE_DIALOG_SAVE;
+          break;
+        default:
+          NOTREACHED();
+          break;
+      }
+
+      if (params.overwriteprompt)
+        mode |= FILE_DIALOG_OVERWRITEPROMPT_FLAG;
+      if (params.hidereadonly)
+        mode |= FILE_DIALOG_HIDEREADONLY_FLAG;
+
+      std::vector<base::string16>::const_iterator it;
+
+      std::vector<CefString> accept_filters;
+      it = params.accept_types.begin();
+      for (; it != params.accept_types.end(); ++it)
+        accept_filters.push_back(*it);
+
+      CefRefPtr<CefFileDialogCallbackImpl> callbackImpl(
+          new CefFileDialogCallbackImpl(std::move(callback)));
+      handled = handler->OnFileDialog(
+          browser_, static_cast<cef_file_dialog_mode_t>(mode), params.title,
+          params.default_file_name.value(), accept_filters,
+          params.selected_accept_filter, callbackImpl.get());
+      if (!handled) {
+        // May return nullptr if the client has already executed the callback.
+        callback = callbackImpl->Disconnect();
+      }
+    }
+  }
+
+  if (!handled && !callback.is_null()) {
+    if (runner_.get()) {
+      runner_->Run(browser_, params, std::move(callback));
+    } else {
+      LOG(WARNING) << "No file dialog runner available for this platform";
+      std::move(callback).Run(0, std::vector<base::FilePath>());
+    }
+  }
+}
+
+void CefFileDialogManager::OnRunFileChooserCallback(
+    CefFileDialogRunner::RunFileChooserCallback callback,
+    int selected_accept_filter,
+    const std::vector<base::FilePath>& file_paths) {
+  CEF_REQUIRE_UIT();
+
+  Cleanup();
+
+  // Execute the callback asynchronously.
+  CEF_POST_TASK(CEF_UIT, base::BindOnce(std::move(callback),
+                                        selected_accept_filter, file_paths));
+}
+
+void CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback(
+    const blink::mojom::FileChooserParams::Mode mode,
+    std::unique_ptr<content::FileSelectListener> listener,
+    int selected_accept_filter,
+    const std::vector<base::FilePath>& file_paths) {
+  CEF_REQUIRE_UIT();
+  DCHECK_EQ(mode, blink::mojom::FileChooserParams::Mode::kUploadFolder);
+
+  if (file_paths.size() == 0) {
+    // Client canceled the file chooser.
+    OnRunFileChooserDelegateCallback(mode, std::move(listener),
+                                     selected_accept_filter, file_paths);
+  } else {
+    lister_.reset(new net::DirectoryLister(
+        file_paths[0], net::DirectoryLister::NO_SORT_RECURSIVE,
+        new UploadFolderHelper(base::BindOnce(
+            &CefFileDialogManager::OnRunFileChooserDelegateCallback,
+            weak_ptr_factory_.GetWeakPtr(), mode, std::move(listener)))));
+    lister_->Start();
+  }
+}
+
+void CefFileDialogManager::OnRunFileChooserDelegateCallback(
+    blink::mojom::FileChooserParams::Mode mode,
+    std::unique_ptr<content::FileSelectListener> listener,
+    int selected_accept_filter,
+    const std::vector<base::FilePath>& file_paths) {
+  CEF_REQUIRE_UIT();
+
+  base::FilePath base_dir;
+  std::vector<blink::mojom::FileChooserFileInfoPtr> selected_files;
+
+  if (!file_paths.empty()) {
+    if (mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
+      base_dir = file_paths[0].DirName();
+    }
+
+    // Convert FilePath list to SelectedFileInfo list.
+    for (size_t i = 0; i < file_paths.size(); ++i) {
+      auto info = blink::mojom::FileChooserFileInfo::NewNativeFile(
+          blink::mojom::NativeFileInfo::New(file_paths[i], base::string16()));
+      selected_files.push_back(std::move(info));
+    }
+  }
+
+  listener->FileSelected(std::move(selected_files), base_dir, mode);
+
+  Cleanup();
+}
+
+void CefFileDialogManager::Cleanup() {
+  if (lister_)
+    lister_.reset();
+
+  file_chooser_pending_ = false;
+}
diff --git a/src/libcef/browser/file_dialog_manager.h b/src/libcef/browser/file_dialog_manager.h
new file mode 100644
index 0000000..a739c51
--- /dev/null
+++ b/src/libcef/browser/file_dialog_manager.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_FILE_DIALOG_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_FILE_DIALOG_MANAGER_H_
+#pragma once
+
+#include "include/cef_browser.h"
+#include "libcef/browser/file_dialog_runner.h"
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+class FileSelectListener;
+class WebContents;
+}  // namespace content
+
+namespace net {
+class DirectoryLister;
+}
+
+class CefBrowserHostImpl;
+
+class CefFileDialogManager {
+ public:
+  // |runner| may be NULL if the platform doesn't implement dialogs.
+  CefFileDialogManager(CefBrowserHostImpl* browser,
+                       std::unique_ptr<CefFileDialogRunner> runner);
+  ~CefFileDialogManager();
+
+  // Delete the runner to free any platform constructs.
+  void Destroy();
+
+  // Called from CefBrowserHostImpl::RunFileChooser.
+  // See CefBrowserHost::RunFileDialog documentation.
+  void RunFileDialog(cef_file_dialog_mode_t mode,
+                     const CefString& title,
+                     const CefString& default_file_path,
+                     const std::vector<CefString>& accept_filters,
+                     int selected_accept_filter,
+                     CefRefPtr<CefRunFileDialogCallback> callback);
+
+  // Called from CefBrowserHostImpl::RunFileChooser.
+  // See WebContentsDelegate::RunFileChooser documentation.
+  void RunFileChooser(std::unique_ptr<content::FileSelectListener> listener,
+                      const blink::mojom::FileChooserParams& params);
+
+  // Run the file chooser dialog specified by |params|. Only a single dialog may
+  // be pending at any given time. |callback| will be executed asynchronously
+  // after the dialog is dismissed or if another dialog is already pending.
+  void RunFileChooser(const CefFileDialogRunner::FileChooserParams& params,
+                      CefFileDialogRunner::RunFileChooserCallback callback);
+
+ private:
+  void RunFileChooserInternal(
+      const CefFileDialogRunner::FileChooserParams& params,
+      CefFileDialogRunner::RunFileChooserCallback callback);
+
+  // Used with the RunFileChooser variant where the caller specifies a callback
+  // (no associated RenderFrameHost).
+  void OnRunFileChooserCallback(
+      CefFileDialogRunner::RunFileChooserCallback callback,
+      int selected_accept_filter,
+      const std::vector<base::FilePath>& file_paths);
+
+  // Used with WebContentsDelegate::RunFileChooser when mode is
+  // blink::mojom::FileChooserParams::Mode::kUploadFolder.
+  void OnRunFileChooserUploadFolderDelegateCallback(
+      const blink::mojom::FileChooserParams::Mode mode,
+      std::unique_ptr<content::FileSelectListener> listener,
+      int selected_accept_filter,
+      const std::vector<base::FilePath>& file_paths);
+
+  // Used with WebContentsDelegate::RunFileChooser to notify the
+  // RenderFrameHost.
+  void OnRunFileChooserDelegateCallback(
+      blink::mojom::FileChooserParams::Mode mode,
+      std::unique_ptr<content::FileSelectListener> listener,
+      int selected_accept_filter,
+      const std::vector<base::FilePath>& file_paths);
+
+  // Clean up state associated with the last run.
+  void Cleanup();
+
+  // CefBrowserHostImpl pointer is guaranteed to outlive this object.
+  CefBrowserHostImpl* browser_;
+
+  std::unique_ptr<CefFileDialogRunner> runner_;
+
+  // True if a file chooser is currently pending.
+  bool file_chooser_pending_;
+
+  // Used for asynchronously listing directory contents.
+  std::unique_ptr<net::DirectoryLister> lister_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<CefFileDialogManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefFileDialogManager);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_MANAGER_H_
diff --git a/src/libcef/browser/file_dialog_runner.h b/src/libcef/browser/file_dialog_runner.h
new file mode 100644
index 0000000..1ad1143
--- /dev/null
+++ b/src/libcef/browser/file_dialog_runner.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_FILE_DIALOG_RUNNER_H_
+#define CEF_LIBCEF_BROWSER_FILE_DIALOG_RUNNER_H_
+#pragma once
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
+
+class CefBrowserHostImpl;
+
+class CefFileDialogRunner {
+ public:
+  // Extend blink::mojom::FileChooserParams with some options unique to CEF.
+  struct FileChooserParams : public blink::mojom::FileChooserParams {
+    // 0-based index of the selected value in |accept_types|.
+    int selected_accept_filter = 0;
+
+    // True if the Save dialog should prompt before overwriting files.
+    bool overwriteprompt = true;
+
+    // True if read-only files should be hidden.
+    bool hidereadonly = true;
+  };
+
+  // The argument vector will be empty if the dialog was canceled.
+  typedef base::OnceCallback<void(int, const std::vector<base::FilePath>&)>
+      RunFileChooserCallback;
+
+  // Display the file chooser dialog. Execute |callback| on completion.
+  virtual void Run(CefBrowserHostImpl* browser,
+                   const FileChooserParams& params,
+                   RunFileChooserCallback callback) = 0;
+
+ protected:
+  // Allow deletion via scoped_ptr only.
+  friend std::default_delete<CefFileDialogRunner>;
+
+  CefFileDialogRunner() {}
+  virtual ~CefFileDialogRunner() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefFileDialogRunner);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_FILE_DIALOG_RUNNER_H_
diff --git a/src/libcef/browser/frame_host_impl.cc b/src/libcef/browser/frame_host_impl.cc
new file mode 100644
index 0000000..7b2f605
--- /dev/null
+++ b/src/libcef/browser/frame_host_impl.cc
@@ -0,0 +1,684 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/frame_host_impl.h"
+
+#include "include/cef_request.h"
+#include "include/cef_stream.h"
+#include "include/cef_v8.h"
+#include "include/test/cef_test_helpers.h"
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/navigate_params.h"
+#include "libcef/browser/net_service/browser_urlrequest_impl.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/frame_util.h"
+#include "libcef/common/process_message_impl.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/task_runner_impl.h"
+
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+
+namespace {
+
+// Implementation of CommandResponseHandler for calling a CefStringVisitor.
+class StringVisitHandler : public CefResponseManager::Handler {
+ public:
+  explicit StringVisitHandler(CefRefPtr<CefStringVisitor> visitor)
+      : visitor_(visitor) {}
+  void OnResponse(const Cef_Response_Params& params) override {
+    visitor_->Visit(params.response);
+  }
+
+ private:
+  CefRefPtr<CefStringVisitor> visitor_;
+
+  IMPLEMENT_REFCOUNTING(StringVisitHandler);
+};
+
+// Implementation of CommandResponseHandler for calling ViewText().
+class ViewTextHandler : public CefResponseManager::Handler {
+ public:
+  explicit ViewTextHandler(CefRefPtr<CefFrameHostImpl> frame) : frame_(frame) {}
+  void OnResponse(const Cef_Response_Params& params) override {
+    CefRefPtr<CefBrowser> browser = frame_->GetBrowser();
+    if (browser.get()) {
+      static_cast<CefBrowserHostImpl*>(browser.get())
+          ->ViewText(params.response);
+    }
+  }
+
+ private:
+  CefRefPtr<CefFrameHostImpl> frame_;
+
+  IMPLEMENT_REFCOUNTING(ViewTextHandler);
+};
+
+}  // namespace
+
+CefFrameHostImpl::CefFrameHostImpl(scoped_refptr<CefBrowserInfo> browser_info,
+                                   bool is_main_frame,
+                                   int64_t parent_frame_id)
+    : is_main_frame_(is_main_frame),
+      frame_id_(kInvalidFrameId),
+      browser_info_(browser_info),
+      is_focused_(is_main_frame_),  // The main frame always starts focused.
+      parent_frame_id_(parent_frame_id) {
+#if DCHECK_IS_ON()
+  DCHECK(browser_info_);
+  if (is_main_frame_) {
+    DCHECK_EQ(parent_frame_id_, kInvalidFrameId);
+  } else {
+    DCHECK_GT(parent_frame_id_, 0);
+  }
+#endif
+}
+
+CefFrameHostImpl::CefFrameHostImpl(scoped_refptr<CefBrowserInfo> browser_info,
+                                   content::RenderFrameHost* render_frame_host)
+    : is_main_frame_(render_frame_host->GetParent() == nullptr),
+      frame_id_(MakeFrameId(render_frame_host)),
+      browser_info_(browser_info),
+      is_focused_(is_main_frame_),  // The main frame always starts focused.
+      url_(render_frame_host->GetLastCommittedURL().spec()),
+      name_(render_frame_host->GetFrameName()),
+      parent_frame_id_(is_main_frame_
+                           ? kInvalidFrameId
+                           : MakeFrameId(render_frame_host->GetParent())),
+      render_frame_host_(render_frame_host),
+      response_manager_(new CefResponseManager) {
+  DCHECK(browser_info_);
+}
+
+CefFrameHostImpl::~CefFrameHostImpl() {}
+
+void CefFrameHostImpl::SetRenderFrameHost(content::RenderFrameHost* host) {
+  CEF_REQUIRE_UIT();
+
+  base::AutoLock lock_scope(state_lock_);
+
+  // We should not be detached.
+  CHECK(browser_info_);
+  // We should be the main frame.
+  CHECK(is_main_frame_);
+
+  render_frame_host_ = host;
+  frame_id_ = MakeFrameId(host);
+  url_ = host->GetLastCommittedURL().spec();
+  name_ = host->GetFrameName();
+
+  // Cancel any existing messages.
+  response_manager_.reset(new CefResponseManager);
+}
+
+bool CefFrameHostImpl::IsValid() {
+  return !!GetBrowserHostImpl();
+}
+
+void CefFrameHostImpl::Undo() {
+  SendCommand("Undo", nullptr);
+}
+
+void CefFrameHostImpl::Redo() {
+  SendCommand("Redo", nullptr);
+}
+
+void CefFrameHostImpl::Cut() {
+  SendCommand("Cut", nullptr);
+}
+
+void CefFrameHostImpl::Copy() {
+  SendCommand("Copy", nullptr);
+}
+
+void CefFrameHostImpl::Paste() {
+  SendCommand("Paste", nullptr);
+}
+
+void CefFrameHostImpl::Delete() {
+  SendCommand("Delete", nullptr);
+}
+
+void CefFrameHostImpl::SelectAll() {
+  SendCommand("SelectAll", nullptr);
+}
+
+void CefFrameHostImpl::ViewSource() {
+  SendCommand("GetSource", new ViewTextHandler(this));
+}
+
+void CefFrameHostImpl::GetSource(CefRefPtr<CefStringVisitor> visitor) {
+  SendCommand("GetSource", new StringVisitHandler(visitor));
+}
+
+void CefFrameHostImpl::GetText(CefRefPtr<CefStringVisitor> visitor) {
+  SendCommand("GetText", new StringVisitHandler(visitor));
+}
+
+void CefFrameHostImpl::LoadRequest(CefRefPtr<CefRequest> request) {
+  CefNavigateParams params(GURL(), kPageTransitionExplicit);
+  static_cast<CefRequestImpl*>(request.get())->Get(params);
+  Navigate(params);
+}
+
+void CefFrameHostImpl::LoadURL(const CefString& url) {
+  LoadURLWithExtras(url, content::Referrer(), kPageTransitionExplicit,
+                    std::string());
+}
+
+void CefFrameHostImpl::ExecuteJavaScript(const CefString& jsCode,
+                                         const CefString& scriptUrl,
+                                         int startLine) {
+  SendJavaScript(jsCode, scriptUrl, startLine);
+}
+
+bool CefFrameHostImpl::IsMain() {
+  return is_main_frame_;
+}
+
+bool CefFrameHostImpl::IsFocused() {
+  base::AutoLock lock_scope(state_lock_);
+  return is_focused_;
+}
+
+CefString CefFrameHostImpl::GetName() {
+  base::AutoLock lock_scope(state_lock_);
+  return name_;
+}
+
+int64 CefFrameHostImpl::GetIdentifier() {
+  base::AutoLock lock_scope(state_lock_);
+  return frame_id_;
+}
+
+CefRefPtr<CefFrame> CefFrameHostImpl::GetParent() {
+  int64 parent_frame_id;
+
+  {
+    base::AutoLock lock_scope(state_lock_);
+    if (is_main_frame_ || parent_frame_id_ == kInvalidFrameId)
+      return nullptr;
+    parent_frame_id = parent_frame_id_;
+  }
+
+  auto browser = GetBrowserHostImpl();
+  if (browser)
+    return browser->GetFrame(parent_frame_id);
+
+  return nullptr;
+}
+
+CefString CefFrameHostImpl::GetURL() {
+  base::AutoLock lock_scope(state_lock_);
+  return url_;
+}
+
+CefRefPtr<CefBrowser> CefFrameHostImpl::GetBrowser() {
+  return GetBrowserHostImpl().get();
+}
+
+CefRefPtr<CefV8Context> CefFrameHostImpl::GetV8Context() {
+  NOTREACHED() << "GetV8Context cannot be called from the browser process";
+  return nullptr;
+}
+
+void CefFrameHostImpl::VisitDOM(CefRefPtr<CefDOMVisitor> visitor) {
+  NOTREACHED() << "VisitDOM cannot be called from the browser process";
+}
+
+CefRefPtr<CefURLRequest> CefFrameHostImpl::CreateURLRequest(
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefURLRequestClient> client) {
+  if (!request || !client)
+    return nullptr;
+
+  if (!CefTaskRunnerImpl::GetCurrentTaskRunner()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  auto browser = GetBrowserHostImpl();
+  if (!browser)
+    return nullptr;
+
+  auto request_context = browser->request_context();
+
+  CefRefPtr<CefBrowserURLRequest> impl =
+      new CefBrowserURLRequest(this, request, client, request_context);
+  if (impl->Start())
+    return impl.get();
+  return nullptr;
+}
+
+void CefFrameHostImpl::SendProcessMessage(
+    CefProcessId target_process,
+    CefRefPtr<CefProcessMessage> message) {
+  DCHECK_EQ(PID_RENDERER, target_process);
+  DCHECK(message.get());
+
+  Cef_Request_Params params;
+  CefProcessMessageImpl* impl =
+      static_cast<CefProcessMessageImpl*>(message.get());
+  if (!impl->CopyTo(params))
+    return;
+
+  DCHECK(!params.name.empty());
+
+  params.user_initiated = true;
+  params.request_id = -1;
+  params.expect_response = false;
+
+  Send(new CefMsg_Request(MSG_ROUTING_NONE, params));
+}
+
+void CefFrameHostImpl::SetFocused(bool focused) {
+  base::AutoLock lock_scope(state_lock_);
+  is_focused_ = focused;
+}
+
+void CefFrameHostImpl::RefreshAttributes() {
+  CEF_REQUIRE_UIT();
+
+  base::AutoLock lock_scope(state_lock_);
+  if (!render_frame_host_)
+    return;
+  url_ = render_frame_host_->GetLastCommittedURL().spec();
+
+  // Use the assigned name if it is non-empty. This represents the name property
+  // on the frame DOM element. If the assigned name is empty, revert to the
+  // internal unique name. This matches the logic in render_frame_util::GetName.
+  name_ = render_frame_host_->GetFrameName();
+  if (name_.empty()) {
+    const auto node = content::FrameTreeNode::GloballyFindByID(
+        render_frame_host_->GetFrameTreeNodeId());
+    if (node) {
+      name_ = node->unique_name();
+    }
+  }
+
+  if (!is_main_frame_)
+    parent_frame_id_ = MakeFrameId(render_frame_host_->GetParent());
+}
+
+void CefFrameHostImpl::Navigate(const CefNavigateParams& params) {
+  CefMsg_LoadRequest_Params request;
+  request.url = params.url;
+  if (!request.url.is_valid()) {
+    LOG(ERROR) << "Invalid URL passed to CefFrameHostImpl::Navigate: "
+               << params.url;
+    return;
+  }
+
+  request.method = params.method;
+  request.referrer = params.referrer.url;
+  request.referrer_policy =
+      CefRequestImpl::BlinkReferrerPolicyToNetReferrerPolicy(
+          params.referrer.policy);
+  request.site_for_cookies = params.site_for_cookies;
+  request.headers = params.headers;
+  request.load_flags = params.load_flags;
+  request.upload_data = params.upload_data;
+
+  Send(new CefMsg_LoadRequest(MSG_ROUTING_NONE, request));
+
+  auto browser = GetBrowserHostImpl();
+  if (browser)
+    browser->OnSetFocus(FOCUS_SOURCE_NAVIGATION);
+}
+
+void CefFrameHostImpl::LoadURLWithExtras(const std::string& url,
+                                         const content::Referrer& referrer,
+                                         ui::PageTransition transition,
+                                         const std::string& extra_headers) {
+  // Only known frame ids or kMainFrameId are supported.
+  const auto frame_id = GetFrameId();
+  if (frame_id < CefFrameHostImpl::kMainFrameId)
+    return;
+
+  if (frame_id == CefFrameHostImpl::kMainFrameId) {
+    // Load via the browser using NavigationController.
+    auto browser = GetBrowserHostImpl();
+    if (browser) {
+      browser->LoadMainFrameURL(url, referrer, transition, extra_headers);
+    }
+  } else {
+    CefNavigateParams params(GURL(url), transition);
+    params.referrer = referrer;
+    params.headers = extra_headers;
+    Navigate(params);
+  }
+}
+
+void CefFrameHostImpl::SendCommand(
+    const std::string& command,
+    CefRefPtr<CefResponseManager::Handler> responseHandler) {
+  DCHECK(!command.empty());
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefFrameHostImpl::SendCommand, this,
+                                          command, responseHandler));
+    return;
+  }
+
+  // Only known frame ids or kMainFrameId are supported.
+  const auto frame_id = GetFrameId();
+  if (frame_id < CefFrameHostImpl::kMainFrameId)
+    return;
+
+  if (!render_frame_host_ || !response_manager_) {
+    // detached frame has no response_manager_
+    return;
+  }
+
+  TRACE_EVENT2("cef", "CefFrameHostImpl::SendCommand", "frame_id", frame_id,
+               "needsResponse", responseHandler.get() ? 1 : 0);
+  Cef_Request_Params params;
+  params.name = "execute-command";
+  params.user_initiated = false;
+
+  if (responseHandler.get()) {
+    params.request_id = response_manager_->RegisterHandler(responseHandler);
+    params.expect_response = true;
+  } else {
+    params.request_id = -1;
+    params.expect_response = false;
+  }
+
+  params.arguments.AppendString(command);
+
+  Send(new CefMsg_Request(MSG_ROUTING_NONE, params));
+}
+
+void CefFrameHostImpl::SendCode(
+    bool is_javascript,
+    const std::string& code,
+    const std::string& script_url,
+    int script_start_line,
+    CefRefPtr<CefResponseManager::Handler> responseHandler) {
+  DCHECK(!code.empty());
+  DCHECK_GE(script_start_line, 0);
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefFrameHostImpl::SendCode, this,
+                                          is_javascript, code, script_url,
+                                          script_start_line, responseHandler));
+    return;
+  }
+
+  // Only known frame ids or kMainFrameId are supported.
+  auto frame_id = GetFrameId();
+  if (frame_id < CefFrameHostImpl::kMainFrameId)
+    return;
+
+  if (!render_frame_host_ || !response_manager_) {
+    // detached frame has no response_manager_
+    return;
+  }
+
+  TRACE_EVENT2("cef", "CefFrameHostImpl::SendCommand", "frame_id", frame_id,
+               "needsResponse", responseHandler.get() ? 1 : 0);
+  Cef_Request_Params params;
+  params.name = "execute-code";
+  params.user_initiated = false;
+
+  if (responseHandler.get()) {
+    params.request_id = response_manager_->RegisterHandler(responseHandler);
+    params.expect_response = true;
+  } else {
+    params.request_id = -1;
+    params.expect_response = false;
+  }
+
+  params.arguments.AppendBoolean(is_javascript);
+  params.arguments.AppendString(code);
+  params.arguments.AppendString(script_url);
+  params.arguments.AppendInteger(script_start_line);
+
+  Send(new CefMsg_Request(MSG_ROUTING_NONE, params));
+}
+
+void CefFrameHostImpl::SendJavaScript(const std::string& jsCode,
+                                      const std::string& scriptUrl,
+                                      int startLine) {
+  if (jsCode.empty())
+    return;
+  if (startLine <= 0) {
+    // A value of 0 is v8::Message::kNoLineNumberInfo in V8. There is code in
+    // V8 that will assert on that value (e.g. V8StackTraceImpl::Frame::Frame
+    // if a JS exception is thrown) so make sure |startLine| > 0.
+    startLine = 1;
+  }
+
+  SendCode(true, jsCode, scriptUrl, startLine, nullptr);
+}
+
+void CefFrameHostImpl::MaybeSendDidStopLoading() {
+  auto rfh = GetRenderFrameHost();
+  if (!rfh)
+    return;
+
+  // We only want to notify for the highest-level LocalFrame in this frame's
+  // renderer process subtree. If this frame has a parent in the same process
+  // then the notification will be sent via the parent instead.
+  auto rfh_parent = rfh->GetParent();
+  if (rfh_parent && rfh_parent->GetProcess() == rfh->GetProcess()) {
+    return;
+  }
+
+  Send(new CefMsg_DidStopLoading(MSG_ROUTING_NONE));
+}
+
+bool CefFrameHostImpl::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(CefFrameHostImpl, message)
+    IPC_MESSAGE_HANDLER(CefHostMsg_FrameAttached, OnAttached)
+    IPC_MESSAGE_HANDLER(CefHostMsg_DidFinishLoad, OnDidFinishLoad)
+    IPC_MESSAGE_HANDLER(CefHostMsg_UpdateDraggableRegions,
+                        OnUpdateDraggableRegions)
+    IPC_MESSAGE_HANDLER(CefHostMsg_Request, OnRequest)
+    IPC_MESSAGE_HANDLER(CefHostMsg_Response, OnResponse)
+    IPC_MESSAGE_HANDLER(CefHostMsg_ResponseAck, OnResponseAck)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void CefFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests(
+    const CefString& javascript) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(
+            &CefFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests, this,
+            javascript));
+    return;
+  }
+
+  content::RenderFrameHost* rfh = GetRenderFrameHost();
+  if (rfh)
+    rfh->ExecuteJavaScriptWithUserGestureForTests(javascript);
+}
+
+content::RenderFrameHost* CefFrameHostImpl::GetRenderFrameHost() const {
+  CEF_REQUIRE_UIT();
+  return render_frame_host_;
+}
+
+void CefFrameHostImpl::Detach() {
+  CEF_REQUIRE_UIT();
+
+  {
+    base::AutoLock lock_scope(state_lock_);
+    browser_info_ = nullptr;
+  }
+
+  // In case we never attached, clean up.
+  while (!queued_messages_.empty()) {
+    queued_messages_.pop();
+  }
+
+  response_manager_.reset();
+  render_frame_host_ = nullptr;
+}
+
+// static
+int64_t CefFrameHostImpl::MakeFrameId(const content::RenderFrameHost* host) {
+  CEF_REQUIRE_UIT();
+  auto host_nonconst = const_cast<content::RenderFrameHost*>(host);
+  return MakeFrameId(host_nonconst->GetProcess()->GetID(),
+                     host_nonconst->GetRoutingID());
+}
+
+// static
+int64_t CefFrameHostImpl::MakeFrameId(int32_t render_process_id,
+                                      int32_t render_routing_id) {
+  return frame_util::MakeFrameId(render_process_id, render_routing_id);
+}
+
+// kMainFrameId must be -1 to align with renderer expectations.
+const int64_t CefFrameHostImpl::kMainFrameId = -1;
+const int64_t CefFrameHostImpl::kFocusedFrameId = -2;
+const int64_t CefFrameHostImpl::kUnspecifiedFrameId = -3;
+const int64_t CefFrameHostImpl::kInvalidFrameId = -4;
+
+// This equates to (TT_EXPLICIT | TT_DIRECT_LOAD_FLAG).
+const ui::PageTransition CefFrameHostImpl::kPageTransitionExplicit =
+    static_cast<ui::PageTransition>(ui::PAGE_TRANSITION_TYPED |
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+
+int64 CefFrameHostImpl::GetFrameId() const {
+  base::AutoLock lock_scope(state_lock_);
+  return is_main_frame_ ? kMainFrameId : frame_id_;
+}
+
+CefRefPtr<CefBrowserHostImpl> CefFrameHostImpl::GetBrowserHostImpl() const {
+  base::AutoLock lock_scope(state_lock_);
+  if (browser_info_)
+    return browser_info_->browser();
+  return nullptr;
+}
+
+void CefFrameHostImpl::OnAttached() {
+  if (!is_attached_) {
+    is_attached_ = true;
+    while (!queued_messages_.empty()) {
+      Send(queued_messages_.front().release());
+      queued_messages_.pop();
+    }
+  }
+}
+
+void CefFrameHostImpl::OnDidFinishLoad(const GURL& validated_url,
+                                       int http_status_code) {
+  auto browser = GetBrowserHostImpl();
+  if (browser)
+    browser->OnDidFinishLoad(this, validated_url, http_status_code);
+}
+
+void CefFrameHostImpl::OnUpdateDraggableRegions(
+    const std::vector<Cef_DraggableRegion_Params>& regions) {
+  auto browser = GetBrowserHostImpl();
+  if (!browser)
+    return;
+
+  CefRefPtr<CefDragHandler> handler;
+  auto client = browser->GetClient();
+  if (client)
+    handler = client->GetDragHandler();
+  if (!handler)
+    return;
+
+  std::vector<CefDraggableRegion> draggable_regions;
+  draggable_regions.reserve(regions.size());
+
+  std::vector<Cef_DraggableRegion_Params>::const_iterator it = regions.begin();
+  for (; it != regions.end(); ++it) {
+    const gfx::Rect& rect(it->bounds);
+    const CefRect bounds(rect.x(), rect.y(), rect.width(), rect.height());
+    draggable_regions.push_back(CefDraggableRegion(bounds, it->draggable));
+  }
+
+  handler->OnDraggableRegionsChanged(browser.get(), this, draggable_regions);
+}
+
+void CefFrameHostImpl::OnRequest(const Cef_Request_Params& params) {
+  CEF_REQUIRE_UIT();
+
+  bool success = false;
+  std::string response;
+  bool expect_response_ack = false;
+
+  if (params.user_initiated) {
+    auto browser = GetBrowserHostImpl();
+    if (browser && browser->client()) {
+      // Give the user a chance to handle the request.
+      CefRefPtr<CefProcessMessageImpl> message(new CefProcessMessageImpl(
+          const_cast<Cef_Request_Params*>(&params), false, true));
+      success = browser->client()->OnProcessMessageReceived(
+          browser.get(), this, PID_RENDERER, message.get());
+      message->Detach(nullptr);
+    }
+  } else {
+    // Invalid request.
+    NOTREACHED();
+  }
+
+  if (params.expect_response) {
+    DCHECK_GE(params.request_id, 0);
+
+    // Send a response to the renderer.
+    Cef_Response_Params response_params;
+    response_params.request_id = params.request_id;
+    response_params.success = success;
+    response_params.response = response;
+    response_params.expect_response_ack = expect_response_ack;
+    Send(new CefMsg_Response(MSG_ROUTING_NONE, response_params));
+  }
+}
+
+void CefFrameHostImpl::OnResponse(const Cef_Response_Params& params) {
+  CEF_REQUIRE_UIT();
+
+  response_manager_->RunHandler(params);
+  if (params.expect_response_ack)
+    Send(new CefMsg_ResponseAck(MSG_ROUTING_NONE, params.request_id));
+}
+
+void CefFrameHostImpl::OnResponseAck(int request_id) {
+  response_manager_->RunAckHandler(request_id);
+}
+
+void CefFrameHostImpl::Send(IPC::Message* message) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(base::IgnoreResult(&CefFrameHostImpl::Send),
+                                 this, message));
+    return;
+  }
+
+  if (!render_frame_host_) {
+    // Either we're a placeholder frame without a renderer representation, or
+    // we've been detached.
+    delete message;
+    return;
+  }
+
+  if (!is_attached_) {
+    // Queue messages until we're notified by the renderer that it's ready to
+    // handle them.
+    queued_messages_.push(base::WrapUnique(message));
+    return;
+  }
+
+  message->set_routing_id(render_frame_host_->GetRoutingID());
+  render_frame_host_->Send(message);
+}
+
+void CefExecuteJavaScriptWithUserGestureForTests(CefRefPtr<CefFrame> frame,
+                                                 const CefString& javascript) {
+  CefFrameHostImpl* impl = static_cast<CefFrameHostImpl*>(frame.get());
+  if (impl)
+    impl->ExecuteJavaScriptWithUserGestureForTests(javascript);
+}
diff --git a/src/libcef/browser/frame_host_impl.h b/src/libcef/browser/frame_host_impl.h
new file mode 100644
index 0000000..4342502
--- /dev/null
+++ b/src/libcef/browser/frame_host_impl.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_FRAME_HOST_IMPL_H_
+#define CEF_LIBCEF_BROWSER_FRAME_HOST_IMPL_H_
+#pragma once
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include "include/cef_frame.h"
+#include "libcef/common/response_manager.h"
+
+#include "base/synchronization/lock.h"
+#include "ui/base/page_transition_types.h"
+
+namespace content {
+class RenderFrameHost;
+struct Referrer;
+}  // namespace content
+
+namespace IPC {
+class Message;
+}
+
+class GURL;
+
+struct Cef_DraggableRegion_Params;
+struct Cef_Request_Params;
+struct Cef_Response_Params;
+class CefBrowserInfo;
+class CefBrowserHostImpl;
+struct CefNavigateParams;
+
+// Implementation of CefFrame. CefFrameHostImpl objects should always be created
+// or retrieved via CefBrowerInfo.
+class CefFrameHostImpl : public CefFrame {
+ public:
+  // Create a temporary frame.
+  CefFrameHostImpl(scoped_refptr<CefBrowserInfo> browser_info,
+                   bool is_main_frame,
+                   int64_t parent_frame_id);
+
+  // Create a frame backed by a RFH and owned by CefBrowserInfo.
+  CefFrameHostImpl(scoped_refptr<CefBrowserInfo> browser_info,
+                   content::RenderFrameHost* render_frame_host);
+
+  // Update an existing main frame object.
+  void SetRenderFrameHost(content::RenderFrameHost* host);
+
+  ~CefFrameHostImpl() override;
+
+  // CefFrame methods
+  bool IsValid() override;
+  void Undo() override;
+  void Redo() override;
+  void Cut() override;
+  void Copy() override;
+  void Paste() override;
+  void Delete() override;
+  void SelectAll() override;
+  void ViewSource() override;
+  void GetSource(CefRefPtr<CefStringVisitor> visitor) override;
+  void GetText(CefRefPtr<CefStringVisitor> visitor) override;
+  void LoadRequest(CefRefPtr<CefRequest> request) override;
+  void LoadURL(const CefString& url) override;
+  void ExecuteJavaScript(const CefString& jsCode,
+                         const CefString& scriptUrl,
+                         int startLine) override;
+  bool IsMain() override;
+  bool IsFocused() override;
+  CefString GetName() override;
+  int64 GetIdentifier() override;
+  CefRefPtr<CefFrame> GetParent() override;
+  CefString GetURL() override;
+  CefRefPtr<CefBrowser> GetBrowser() override;
+  CefRefPtr<CefV8Context> GetV8Context() override;
+  void VisitDOM(CefRefPtr<CefDOMVisitor> visitor) override;
+  CefRefPtr<CefURLRequest> CreateURLRequest(
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefURLRequestClient> client) override;
+  void SendProcessMessage(CefProcessId target_process,
+                          CefRefPtr<CefProcessMessage> message) override;
+
+  void SetFocused(bool focused);
+  void RefreshAttributes();
+
+  // Navigate as specified by the |params| argument.
+  void Navigate(const CefNavigateParams& params);
+
+  // Load the specified URL.
+  void LoadURLWithExtras(const std::string& url,
+                         const content::Referrer& referrer,
+                         ui::PageTransition transition,
+                         const std::string& extra_headers);
+
+  // Send a command to the renderer for execution.
+  void SendCommand(const std::string& command,
+                   CefRefPtr<CefResponseManager::Handler> responseHandler);
+
+  // Send code to the renderer for execution.
+  void SendCode(bool is_javascript,
+                const std::string& code,
+                const std::string& script_url,
+                int script_start_line,
+                CefRefPtr<CefResponseManager::Handler> responseHandler);
+
+  // Send JavaScript to the renderer for execution.
+  void SendJavaScript(const std::string& jsCode,
+                      const std::string& scriptUrl,
+                      int startLine);
+
+  // Called from CefBrowserHostImpl::DidStopLoading.
+  void MaybeSendDidStopLoading();
+
+  // Called from CefBrowserHostImpl::OnMessageReceived.
+  bool OnMessageReceived(const IPC::Message& message);
+
+  void ExecuteJavaScriptWithUserGestureForTests(const CefString& javascript);
+
+  // Returns the RFH associated with this frame. Must be called on the UI
+  // thread.
+  content::RenderFrameHost* GetRenderFrameHost() const;
+
+  // Owned frame objects will be detached explicitly when the associated
+  // RenderFrame is deleted. Temporary frame objects will be detached
+  // implicitly via CefBrowserInfo::browser() returning nullptr.
+  void Detach();
+
+  static int64_t MakeFrameId(const content::RenderFrameHost* host);
+  static int64_t MakeFrameId(int32_t render_process_id,
+                             int32_t render_routing_id);
+
+  static const int64_t kMainFrameId;
+  static const int64_t kFocusedFrameId;
+  static const int64_t kUnspecifiedFrameId;
+  static const int64_t kInvalidFrameId;
+
+  // PageTransition type for explicit navigations. This must pass the check in
+  // ContentBrowserClient::IsExplicitNavigation for debug URLs (HandleDebugURL)
+  // to work as expected.
+  static const ui::PageTransition kPageTransitionExplicit;
+
+ private:
+  int64 GetFrameId() const;
+  CefRefPtr<CefBrowserHostImpl> GetBrowserHostImpl() const;
+
+  // OnMessageReceived message handlers.
+  void OnAttached();
+  void OnDidFinishLoad(const GURL& validated_url, int http_status_code);
+  void OnUpdateDraggableRegions(
+      const std::vector<Cef_DraggableRegion_Params>& regions);
+  void OnRequest(const Cef_Request_Params& params);
+  void OnResponse(const Cef_Response_Params& params);
+  void OnResponseAck(int request_id);
+
+  // Send a message to the RenderFrameHost associated with this frame.
+  void Send(IPC::Message* message);
+
+  const bool is_main_frame_;
+
+  // The following members may be read/modified from any thread. All access must
+  // be protected by |state_lock_|.
+  mutable base::Lock state_lock_;
+  int64 frame_id_;
+  scoped_refptr<CefBrowserInfo> browser_info_;
+  bool is_focused_;
+  CefString url_;
+  CefString name_;
+  int64 parent_frame_id_;
+
+  // The following members are only accessed on the UI thread.
+  content::RenderFrameHost* render_frame_host_ = nullptr;
+
+  bool is_attached_ = false;
+
+  // Qeueud messages to send when the renderer process attaches.
+  std::queue<std::unique_ptr<IPC::Message>> queued_messages_;
+
+  // Manages response registrations.
+  std::unique_ptr<CefResponseManager> response_manager_;
+
+  IMPLEMENT_REFCOUNTING(CefFrameHostImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefFrameHostImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_FRAME_HOST_IMPL_H_
diff --git a/src/libcef/browser/gpu/external_texture_manager.cc b/src/libcef/browser/gpu/external_texture_manager.cc
new file mode 100644
index 0000000..08cd7da
--- /dev/null
+++ b/src/libcef/browser/gpu/external_texture_manager.cc
@@ -0,0 +1,341 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cef/libcef/browser/gpu/external_texture_manager.h"
+
+#include "gpu/command_buffer/service/service_utils.h"
+#include "third_party/khronos/EGL/egl.h"
+#include "third_party/khronos/EGL/eglext.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context_egl.h"
+#include "ui/gl/gl_image.h"
+#include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/init/gl_factory.h"
+
+#if defined(OS_WIN)
+#include <d3d11_1.h>
+#include "ui/gl/gl_angle_util_win.h"
+#include "ui/gl/gl_image_dxgi.h"
+#endif
+
+#ifndef EGL_ANGLE_d3d_texture_client_buffer
+#define EGL_ANGLE_d3d_texture_client_buffer 1
+#define EGL_D3D_TEXTURE_ANGLE 0x33A3
+#endif
+
+namespace gpu {
+namespace gles2 {
+
+namespace {
+
+#if defined(OS_WIN)
+
+class GLImageDXGISharedHandle : public gl::GLImageDXGI {
+ public:
+  GLImageDXGISharedHandle(const gfx::Size& size)
+      : GLImageDXGI(size, nullptr),
+        handle_((HANDLE)0),
+        surface_(EGL_NO_SURFACE),
+        texture_id_(0) {}
+
+  void* share_handle() const { return handle_; }
+
+  bool Initialize() {
+    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
+        gl::QueryD3D11DeviceObjectFromANGLE();
+    if (!d3d11_device) {
+      return false;
+    }
+
+    Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device1;
+    HRESULT hr = d3d11_device.As(&d3d11_device1);
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    D3D11_TEXTURE2D_DESC td = {0};
+    td.ArraySize = 1;
+    td.CPUAccessFlags = 0;
+    td.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+    td.Width = GetSize().width();
+    td.Height = GetSize().height();
+    td.MipLevels = 1;
+    td.SampleDesc.Count = 1;
+    td.SampleDesc.Quality = 0;
+    td.Usage = D3D11_USAGE_DEFAULT;
+    td.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+    td.MiscFlags = 0;
+
+    hr = d3d11_device1->CreateTexture2D(&td, nullptr, texture_.GetAddressOf());
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    // Create a staging texture that will not be a render-target, but will be
+    // shared.  We could make the render target directly shareable, but the
+    // staged copy is safer for synchronization and less problematic
+    td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    td.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+    hr = d3d11_device1->CreateTexture2D(&td, nullptr,
+                                        staging_texture_.GetAddressOf());
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    // If using a staging texture ... then we need the shared handle for that
+    Microsoft::WRL::ComPtr<IDXGIResource> dxgi_res;
+    if (staging_texture_.Get()) {
+      hr = staging_texture_.As(&dxgi_res);
+    } else {
+      hr = texture_.As(&dxgi_res);
+    }
+    if (SUCCEEDED(hr)) {
+      dxgi_res->GetSharedHandle(&handle_);
+    }
+
+    return true;
+  }
+
+  void Lock() {
+    // In the future a keyed mutex could be utilized here.
+  }
+
+  void Unlock() {
+    if (staging_texture_.Get() && texture_.Get()) {
+      Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device;
+      staging_texture_->GetDevice(&d3d11_device);
+      if (d3d11_device.Get()) {
+        Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_ctx;
+        d3d11_device->GetImmediateContext(&d3d11_ctx);
+        if (d3d11_ctx.Get()) {
+          d3d11_ctx->CopyResource(staging_texture_.Get(), texture_.Get());
+        }
+      }
+    }
+  }
+
+  void SetSurface(EGLSurface surface, GLuint texture_id) {
+    surface_ = surface;
+    texture_id_ = texture_id;
+  }
+
+  EGLSurface surface() const { return surface_; }
+
+  GLuint texture_id() const { return texture_id_; }
+
+ protected:
+  ~GLImageDXGISharedHandle() override {}
+
+ private:
+  HANDLE handle_;
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_texture_;
+  EGLSurface surface_;
+  GLuint texture_id_;
+};
+
+#endif  // defined(OS_WIN)
+
+}  // namespace
+
+ExternalTextureManager::ExternalTextureManager() {}
+
+ExternalTextureManager::~ExternalTextureManager() {}
+
+void* ExternalTextureManager::CreateTexture(GLuint texture_id,
+                                            uint32_t width,
+                                            uint32_t height,
+                                            TextureManager* tex_man) {
+  void* share_handle = nullptr;
+
+#if defined(OS_WIN)
+  EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
+  if (egl_display == EGL_NO_DISPLAY) {
+    return nullptr;
+  }
+
+  EGLContext curContext = eglGetCurrentContext();
+  if (curContext == EGL_NO_CONTEXT) {
+    return nullptr;
+  }
+
+  gfx::Size size(width, height);
+  scoped_refptr<gl::GLImage> image;
+  void* texture = nullptr;
+
+  GLImageDXGISharedHandle* dxgi_image = new GLImageDXGISharedHandle(size);
+  if (!dxgi_image->Initialize()) {
+    return nullptr;
+  }
+  image = dxgi_image;
+  share_handle = dxgi_image->share_handle();
+  texture = dxgi_image->texture().Get();
+
+  if (!image) {  // this check seems unnecessary
+    return nullptr;
+  }
+
+  EGLint numConfigs = 0;
+  EGLint configAttrs[] = {
+      EGL_RENDERABLE_TYPE,
+      EGL_OPENGL_ES3_BIT,  // must remain in this position for ES2 fallback
+      EGL_SURFACE_TYPE,
+      EGL_PBUFFER_BIT,
+      EGL_BUFFER_SIZE,
+      32,
+      EGL_RED_SIZE,
+      8,
+      EGL_GREEN_SIZE,
+      8,
+      EGL_BLUE_SIZE,
+      8,
+      EGL_ALPHA_SIZE,
+      8,
+      EGL_DEPTH_SIZE,
+      0,
+      EGL_STENCIL_SIZE,
+      0,
+      EGL_SAMPLE_BUFFERS,
+      0,
+      EGL_NONE};
+
+  EGLConfig config = nullptr;
+  if (eglChooseConfig(egl_display, configAttrs, &config, 1, &numConfigs) !=
+      EGL_TRUE) {
+    return nullptr;
+  }
+
+  EGLSurface surface = EGL_NO_SURFACE;
+  EGLint surfAttrs[] = {EGL_WIDTH,
+                        width,
+                        EGL_HEIGHT,
+                        height,
+                        EGL_TEXTURE_TARGET,
+                        EGL_TEXTURE_2D,
+                        EGL_TEXTURE_FORMAT,
+                        EGL_TEXTURE_RGBA,
+                        EGL_NONE};
+
+  surface = eglCreatePbufferFromClientBuffer(egl_display, EGL_D3D_TEXTURE_ANGLE,
+                                             texture, config, surfAttrs);
+  if (surface == EGL_NO_SURFACE) {
+    // fallback to ES2 - it could be that we're running on older hardware
+    // and ES3 isn't available
+
+    // EGL_RENDERABLE_TYPE is the bit at configAttrs[0]
+    configAttrs[1] = EGL_OPENGL_ES2_BIT;
+    config = nullptr;
+    if (eglChooseConfig(egl_display, configAttrs, &config, 1, &numConfigs) ==
+        EGL_TRUE) {
+      surface = eglCreatePbufferFromClientBuffer(
+          egl_display, EGL_D3D_TEXTURE_ANGLE, texture, config, surfAttrs);
+    }
+
+    // still no surface? we're done
+    if (surface == EGL_NO_SURFACE) {
+      return nullptr;
+    }
+  }
+
+  dxgi_image->SetSurface(surface, texture_id);
+
+  surfaceMap_[share_handle] = image;
+
+  EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW);
+  EGLSurface readSurface = eglGetCurrentSurface(EGL_READ);
+
+  eglMakeCurrent(egl_display, surface, surface, curContext);
+
+  if (eglBindTexImage(egl_display, surface, EGL_BACK_BUFFER)) {
+    if (tex_man) {
+      TextureRef* texture_ref = tex_man->GetTexture(texture_id);
+      tex_man->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_BGRA_EXT, width,
+                            height, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
+                            gfx::Rect(size));
+      tex_man->SetLevelImage(texture_ref, GL_TEXTURE_2D, 0, image.get(),
+                             Texture::BOUND);
+    }
+  }
+
+  eglMakeCurrent(egl_display, drawSurface, readSurface, curContext);
+
+#endif  // defined(OS_WIN)
+
+  return share_handle;
+}
+
+void ExternalTextureManager::LockTexture(void* handle) {
+#if defined(OS_WIN)
+  auto const img = surfaceMap_.find(handle);
+  if (img != surfaceMap_.end()) {
+    GLImageDXGISharedHandle* dxgi_image =
+        reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get());
+    dxgi_image->Lock();
+  }
+#endif  // defined(OS_WIN)
+}
+
+void ExternalTextureManager::UnlockTexture(void* handle) {
+#if defined(OS_WIN)
+  auto const img = surfaceMap_.find(handle);
+  if (img != surfaceMap_.end()) {
+    GLImageDXGISharedHandle* dxgi_image =
+        reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get());
+    dxgi_image->Unlock();
+  }
+#endif  // defined(OS_WIN)
+}
+
+void ExternalTextureManager::DeleteTexture(void* handle,
+                                           TextureManager* tex_man) {
+#if defined(OS_WIN)
+  EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
+  if (egl_display == EGL_NO_DISPLAY) {
+    return;
+  }
+  auto const img = surfaceMap_.find(handle);
+  if (img == surfaceMap_.end()) {
+    return;
+  }
+
+  EGLSurface surface = EGL_NO_SURFACE;
+  GLuint texture_id = 0;
+
+  GLImageDXGISharedHandle* dxgi_image =
+      reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get());
+  surface = dxgi_image->surface();
+  texture_id = dxgi_image->texture_id();
+
+  if (surface != EGL_NO_SURFACE) {
+    EGLContext curContext = eglGetCurrentContext();
+    if (curContext != EGL_NO_CONTEXT) {
+      EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW);
+      EGLSurface readSurface = eglGetCurrentSurface(EGL_READ);
+
+      eglMakeCurrent(egl_display, surface, surface, curContext);
+
+      TextureRef* texture_ref = nullptr;
+      if (tex_man) {
+        texture_ref = tex_man->GetTexture(texture_id);
+      }
+
+      eglReleaseTexImage(egl_display, surface, EGL_BACK_BUFFER);
+
+      if (tex_man && texture_ref) {
+        tex_man->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1,
+                              0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
+        tex_man->SetLevelImage(texture_ref, GL_TEXTURE_2D, 0, nullptr,
+                               Texture::UNBOUND);
+      }
+
+      eglMakeCurrent(egl_display, drawSurface, readSurface, curContext);
+
+      eglDestroySurface(egl_display, surface);
+    }
+  }
+  surfaceMap_.erase(img);
+#endif  // defined(OS_WIN)
+}
+
+}  // namespace gles2
+}  // namespace gpu
diff --git a/src/libcef/browser/gpu/external_texture_manager.h b/src/libcef/browser/gpu/external_texture_manager.h
new file mode 100644
index 0000000..b090d7c
--- /dev/null
+++ b/src/libcef/browser/gpu/external_texture_manager.h
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_GPU_EXTERNAL_TEXTURE_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_GPU_EXTERNAL_TEXTURE_MANAGER_H_
+#pragma once
+
+#include <map>
+
+#include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/gpu_export.h"
+#include "ui/gl/gl_image.h"
+#include "ui/gl/gl_surface_egl.h"
+
+namespace gl {
+class GLImage;
+}
+
+namespace gpu {
+namespace gles2 {
+
+class GPU_GLES2_EXPORT ExternalTextureManager {
+ public:
+  ExternalTextureManager();
+  ~ExternalTextureManager();
+
+  void* CreateTexture(GLuint texture_id,
+                      uint32_t width,
+                      uint32_t height,
+                      TextureManager* tex_man);
+
+  void LockTexture(void* handle);
+  void UnlockTexture(void* handle);
+
+  void DeleteTexture(void* handle, TextureManager* tex_man);
+
+ private:
+  typedef std::map<void*, scoped_refptr<gl::GLImage>> ExternalSurfaceMap;
+  ExternalSurfaceMap surfaceMap_;
+};
+
+}  // namespace gles2
+}  // namespace gpu
+
+#endif  // CEF_LIBCEF_BROWSER_GPU_EXTERNAL_TEXTURE_MANAGER_H_
diff --git a/src/libcef/browser/image_impl.cc b/src/libcef/browser/image_impl.cc
new file mode 100644
index 0000000..a4a77c9
--- /dev/null
+++ b/src/libcef/browser/image_impl.cc
@@ -0,0 +1,397 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/image_impl.h"
+
+#include <algorithm>
+
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_png_rep.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace {
+
+SkColorType GetSkColorType(cef_color_type_t color_type) {
+  switch (color_type) {
+    case CEF_COLOR_TYPE_RGBA_8888:
+      return kRGBA_8888_SkColorType;
+    case CEF_COLOR_TYPE_BGRA_8888:
+      return kBGRA_8888_SkColorType;
+    default:
+      break;
+  }
+
+  NOTREACHED();
+  return kUnknown_SkColorType;
+}
+
+SkAlphaType GetSkAlphaType(cef_alpha_type_t alpha_type) {
+  switch (alpha_type) {
+    case CEF_ALPHA_TYPE_OPAQUE:
+      return kOpaque_SkAlphaType;
+    case CEF_ALPHA_TYPE_PREMULTIPLIED:
+      return kPremul_SkAlphaType;
+    case CEF_ALPHA_TYPE_POSTMULTIPLIED:
+      return kUnpremul_SkAlphaType;
+    default:
+      break;
+  }
+
+  NOTREACHED();
+  return kUnknown_SkAlphaType;
+}
+
+// Compress as PNG. Requires post-multiplied alpha.
+bool PNGMethod(bool with_transparency,
+               const SkBitmap& bitmap,
+               std::vector<unsigned char>* compressed) {
+  return gfx::PNGCodec::Encode(
+      reinterpret_cast<unsigned char*>(bitmap.getPixels()),
+      bitmap.colorType() == kBGRA_8888_SkColorType ? gfx::PNGCodec::FORMAT_BGRA
+                                                   : gfx::PNGCodec::FORMAT_RGBA,
+      gfx::Size(bitmap.width(), bitmap.height()),
+      static_cast<int>(bitmap.rowBytes()),
+      bitmap.alphaType() == kOpaque_SkAlphaType || !with_transparency,
+      std::vector<gfx::PNGCodec::Comment>(), compressed);
+}
+
+// Compress as JPEG. This internally uses JCS_EXT_RGBX or JCS_EXT_BGRX which
+// causes the alpha channel to be ignored. Requires post-multiplied alpha.
+bool JPEGMethod(int quality,
+                const SkBitmap& bitmap,
+                std::vector<unsigned char>* compressed) {
+  return gfx::JPEGCodec::Encode(bitmap, quality, compressed);
+}
+
+}  // namespace
+
+// static
+CefRefPtr<CefImage> CefImage::CreateImage() {
+  return new CefImageImpl();
+}
+
+CefImageImpl::CefImageImpl(const gfx::ImageSkia& image_skia)
+    : image_(image_skia) {}
+
+bool CefImageImpl::IsEmpty() {
+  base::AutoLock lock_scope(lock_);
+  return image_.IsEmpty();
+}
+
+bool CefImageImpl::IsSame(CefRefPtr<CefImage> that) {
+  CefImageImpl* that_impl = static_cast<CefImageImpl*>(that.get());
+  if (!that_impl)
+    return false;
+
+  // Quick check for the same object.
+  if (this == that_impl)
+    return true;
+
+  base::AutoLock lock_scope(lock_);
+  return image_.AsImageSkia().BackedBySameObjectAs(
+      that_impl->image_.AsImageSkia());
+}
+
+bool CefImageImpl::AddBitmap(float scale_factor,
+                             int pixel_width,
+                             int pixel_height,
+                             cef_color_type_t color_type,
+                             cef_alpha_type_t alpha_type,
+                             const void* pixel_data,
+                             size_t pixel_data_size) {
+  const SkColorType ct = GetSkColorType(color_type);
+  const SkAlphaType at = GetSkAlphaType(alpha_type);
+
+  // Make sure the client passed in the expected values.
+  if (ct != kBGRA_8888_SkColorType && ct != kRGBA_8888_SkColorType)
+    return false;
+  if (pixel_data_size != pixel_width * pixel_height * 4U)
+    return false;
+
+  SkBitmap bitmap;
+  if (!bitmap.tryAllocPixels(
+          SkImageInfo::Make(pixel_width, pixel_height, ct, at))) {
+    return false;
+  }
+
+  DCHECK_EQ(pixel_data_size, bitmap.computeByteSize());
+  memcpy(bitmap.getPixels(), pixel_data, pixel_data_size);
+
+  return AddBitmap(scale_factor, bitmap);
+}
+
+bool CefImageImpl::AddPNG(float scale_factor,
+                          const void* png_data,
+                          size_t png_data_size) {
+  SkBitmap bitmap;
+  if (!gfx::PNGCodec::Decode(static_cast<const unsigned char*>(png_data),
+                             png_data_size, &bitmap)) {
+    return false;
+  }
+
+  return AddBitmap(scale_factor, bitmap);
+}
+
+bool CefImageImpl::AddJPEG(float scale_factor,
+                           const void* jpeg_data,
+                           size_t jpeg_data_size) {
+  std::unique_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode(
+      static_cast<const unsigned char*>(jpeg_data), jpeg_data_size));
+  if (!bitmap.get())
+    return false;
+
+  return AddBitmap(scale_factor, *bitmap);
+}
+
+size_t CefImageImpl::GetWidth() {
+  base::AutoLock lock_scope(lock_);
+  return image_.Width();
+}
+
+size_t CefImageImpl::GetHeight() {
+  base::AutoLock lock_scope(lock_);
+  return image_.Height();
+}
+
+bool CefImageImpl::HasRepresentation(float scale_factor) {
+  base::AutoLock lock_scope(lock_);
+  return image_.AsImageSkia().HasRepresentation(scale_factor);
+}
+
+bool CefImageImpl::RemoveRepresentation(float scale_factor) {
+  base::AutoLock lock_scope(lock_);
+  gfx::ImageSkia image_skia = image_.AsImageSkia();
+  if (image_skia.HasRepresentation(scale_factor)) {
+    image_skia.RemoveRepresentation(scale_factor);
+    return true;
+  }
+  return false;
+}
+
+bool CefImageImpl::GetRepresentationInfo(float scale_factor,
+                                         float& actual_scale_factor,
+                                         int& pixel_width,
+                                         int& pixel_height) {
+  base::AutoLock lock_scope(lock_);
+  gfx::ImageSkia image_skia = image_.AsImageSkia();
+  if (image_skia.isNull())
+    return false;
+
+  const gfx::ImageSkiaRep& rep = image_skia.GetRepresentation(scale_factor);
+  if (rep.is_null())
+    return false;
+
+  actual_scale_factor = rep.scale();
+  pixel_width = rep.GetBitmap().width();
+  pixel_height = rep.GetBitmap().height();
+  return true;
+}
+
+CefRefPtr<CefBinaryValue> CefImageImpl::GetAsBitmap(float scale_factor,
+                                                    cef_color_type_t color_type,
+                                                    cef_alpha_type_t alpha_type,
+                                                    int& pixel_width,
+                                                    int& pixel_height) {
+  const SkColorType desired_ct = GetSkColorType(color_type);
+  const SkAlphaType desired_at = GetSkAlphaType(alpha_type);
+
+  base::AutoLock lock_scope(lock_);
+  const SkBitmap* bitmap = GetBitmap(scale_factor);
+  if (!bitmap)
+    return nullptr;
+
+  DCHECK(bitmap->readyToDraw());
+
+  pixel_width = bitmap->width();
+  pixel_height = bitmap->height();
+
+  if (bitmap->colorType() == desired_ct && bitmap->alphaType() == desired_at) {
+    // No conversion necessary.
+    return CefBinaryValue::Create(bitmap->getPixels(),
+                                  bitmap->computeByteSize());
+  } else {
+    SkBitmap desired_bitmap;
+    if (!ConvertBitmap(*bitmap, &desired_bitmap, desired_ct, desired_at))
+      return nullptr;
+    DCHECK(desired_bitmap.readyToDraw());
+    return CefBinaryValue::Create(desired_bitmap.getPixels(),
+                                  desired_bitmap.computeByteSize());
+  }
+}
+
+CefRefPtr<CefBinaryValue> CefImageImpl::GetAsPNG(float scale_factor,
+                                                 bool with_transparency,
+                                                 int& pixel_width,
+                                                 int& pixel_height) {
+  base::AutoLock lock_scope(lock_);
+  const SkBitmap* bitmap = GetBitmap(scale_factor);
+  if (!bitmap)
+    return nullptr;
+
+  std::vector<unsigned char> compressed;
+  if (!WritePNG(*bitmap, &compressed, with_transparency))
+    return nullptr;
+
+  pixel_width = bitmap->width();
+  pixel_height = bitmap->height();
+
+  return CefBinaryValue::Create(&compressed.front(), compressed.size());
+}
+
+CefRefPtr<CefBinaryValue> CefImageImpl::GetAsJPEG(float scale_factor,
+                                                  int quality,
+                                                  int& pixel_width,
+                                                  int& pixel_height) {
+  base::AutoLock lock_scope(lock_);
+  const SkBitmap* bitmap = GetBitmap(scale_factor);
+  if (!bitmap)
+    return nullptr;
+
+  std::vector<unsigned char> compressed;
+  if (!WriteJPEG(*bitmap, &compressed, quality))
+    return nullptr;
+
+  pixel_width = bitmap->width();
+  pixel_height = bitmap->height();
+
+  return CefBinaryValue::Create(&compressed.front(), compressed.size());
+}
+
+void CefImageImpl::AddBitmaps(int32_t scale_1x_size,
+                              const std::vector<SkBitmap>& bitmaps) {
+  if (scale_1x_size == 0) {
+    // Set the scale 1x size to the smallest bitmap pixel size.
+    int32_t min_size = std::numeric_limits<int32_t>::max();
+    for (const SkBitmap& bitmap : bitmaps) {
+      const int32_t size = std::max(bitmap.width(), bitmap.height());
+      if (size < min_size)
+        min_size = size;
+    }
+    scale_1x_size = min_size;
+  }
+
+  for (const SkBitmap& bitmap : bitmaps) {
+    const int32_t size = std::max(bitmap.width(), bitmap.height());
+    const float scale_factor =
+        static_cast<float>(size) / static_cast<float>(scale_1x_size);
+    AddBitmap(scale_factor, bitmap);
+  }
+}
+
+gfx::ImageSkia CefImageImpl::GetForced1xScaleRepresentation(
+    float scale_factor) const {
+  base::AutoLock lock_scope(lock_);
+  if (scale_factor == 1.0f) {
+    // We can use the existing image without modification.
+    return image_.AsImageSkia();
+  }
+
+  const SkBitmap* bitmap = GetBitmap(scale_factor);
+  gfx::ImageSkia image_skia;
+  if (bitmap)
+    image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap, 1.0f));
+  return image_skia;
+}
+
+gfx::ImageSkia CefImageImpl::AsImageSkia() const {
+  base::AutoLock lock_scope(lock_);
+  return image_.AsImageSkia();
+}
+
+bool CefImageImpl::AddBitmap(float scale_factor, const SkBitmap& bitmap) {
+#if DCHECK_IS_ON()
+  DCHECK(bitmap.readyToDraw());
+#endif
+  DCHECK(bitmap.colorType() == kBGRA_8888_SkColorType ||
+         bitmap.colorType() == kRGBA_8888_SkColorType);
+
+  gfx::ImageSkiaRep skia_rep(bitmap, scale_factor);
+  base::AutoLock lock_scope(lock_);
+  if (image_.IsEmpty()) {
+    image_ = gfx::Image(gfx::ImageSkia(skia_rep));
+  } else {
+    image_.AsImageSkia().AddRepresentation(skia_rep);
+  }
+  return true;
+}
+
+const SkBitmap* CefImageImpl::GetBitmap(float scale_factor) const {
+  lock_.AssertAcquired();
+  gfx::ImageSkia image_skia = image_.AsImageSkia();
+  if (image_skia.isNull())
+    return nullptr;
+
+  const gfx::ImageSkiaRep& rep = image_skia.GetRepresentation(scale_factor);
+  if (rep.is_null())
+    return nullptr;
+
+  return &rep.GetBitmap();
+}
+
+// static
+bool CefImageImpl::ConvertBitmap(const SkBitmap& src_bitmap,
+                                 SkBitmap* target_bitmap,
+                                 SkColorType target_ct,
+                                 SkAlphaType target_at) {
+  DCHECK(src_bitmap.readyToDraw());
+  DCHECK(src_bitmap.colorType() != target_ct ||
+         src_bitmap.alphaType() != target_at);
+  DCHECK(target_bitmap);
+
+  SkImageInfo target_info = SkImageInfo::Make(
+      src_bitmap.width(), src_bitmap.height(), target_ct, target_at);
+  if (!target_bitmap->tryAllocPixels(target_info))
+    return false;
+
+  if (!src_bitmap.readPixels(target_info, target_bitmap->getPixels(),
+                             target_bitmap->rowBytes(), 0, 0)) {
+    return false;
+  }
+
+  DCHECK(target_bitmap->readyToDraw());
+  return true;
+}
+
+// static
+bool CefImageImpl::WriteCompressedFormat(const SkBitmap& bitmap,
+                                         std::vector<unsigned char>* compressed,
+                                         const CompressionMethod& method) {
+  const SkBitmap* bitmap_ptr = nullptr;
+  SkBitmap bitmap_postalpha;
+  if (bitmap.alphaType() == kPremul_SkAlphaType) {
+    // Compression methods require post-multiplied alpha values.
+    if (!ConvertBitmap(bitmap, &bitmap_postalpha, bitmap.colorType(),
+                       kUnpremul_SkAlphaType)) {
+      return false;
+    }
+    bitmap_ptr = &bitmap_postalpha;
+  } else {
+    bitmap_ptr = &bitmap;
+  }
+
+  DCHECK(bitmap_ptr->readyToDraw());
+  DCHECK(bitmap_ptr->colorType() == kBGRA_8888_SkColorType ||
+         bitmap_ptr->colorType() == kRGBA_8888_SkColorType);
+  DCHECK(bitmap_ptr->alphaType() == kOpaque_SkAlphaType ||
+         bitmap_ptr->alphaType() == kUnpremul_SkAlphaType);
+
+  return method.Run(*bitmap_ptr, compressed);
+}
+
+// static
+bool CefImageImpl::WritePNG(const SkBitmap& bitmap,
+                            std::vector<unsigned char>* compressed,
+                            bool with_transparency) {
+  return WriteCompressedFormat(bitmap, compressed,
+                               base::Bind(PNGMethod, with_transparency));
+}
+
+// static
+bool CefImageImpl::WriteJPEG(const SkBitmap& bitmap,
+                             std::vector<unsigned char>* compressed,
+                             int quality) {
+  return WriteCompressedFormat(bitmap, compressed,
+                               base::Bind(JPEGMethod, quality));
+}
diff --git a/src/libcef/browser/image_impl.h b/src/libcef/browser/image_impl.h
new file mode 100644
index 0000000..9859f37
--- /dev/null
+++ b/src/libcef/browser/image_impl.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_IMAGE_IMPL_H_
+#define CEF_LIBCEF_BROWSER_IMAGE_IMPL_H_
+#pragma once
+
+#include "include/cef_image.h"
+#include "libcef/browser/thread_util.h"
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image/image.h"
+
+class CefImageImpl : public CefImage {
+ public:
+  // Creates an empty image with no representations.
+  CefImageImpl() = default;
+
+  // Creates a new image by copying the ImageSkia for use as the default
+  // representation.
+  explicit CefImageImpl(const gfx::ImageSkia& image_skia);
+
+  // Deletes the image and, if the only owner of the storage, all of its cached
+  // representations.
+  ~CefImageImpl() override = default;
+
+  // CefImage methods:
+  bool IsEmpty() override;
+  bool IsSame(CefRefPtr<CefImage> that) override;
+  bool AddBitmap(float scale_factor,
+                 int pixel_width,
+                 int pixel_height,
+                 cef_color_type_t color_type,
+                 cef_alpha_type_t alpha_type,
+                 const void* pixel_data,
+                 size_t pixel_data_size) override;
+  bool AddPNG(float scale_factor,
+              const void* png_data,
+              size_t png_data_size) override;
+  bool AddJPEG(float scale_factor,
+               const void* jpeg_data,
+               size_t jpeg_data_size) override;
+  size_t GetWidth() override;
+  size_t GetHeight() override;
+  bool HasRepresentation(float scale_factor) override;
+  bool RemoveRepresentation(float scale_factor) override;
+  bool GetRepresentationInfo(float scale_factor,
+                             float& actual_scale_factor,
+                             int& pixel_width,
+                             int& pixel_height) override;
+  CefRefPtr<CefBinaryValue> GetAsBitmap(float scale_factor,
+                                        cef_color_type_t color_type,
+                                        cef_alpha_type_t alpha_type,
+                                        int& pixel_width,
+                                        int& pixel_height) override;
+  CefRefPtr<CefBinaryValue> GetAsPNG(float scale_factor,
+                                     bool with_transparency,
+                                     int& pixel_width,
+                                     int& pixel_height) override;
+  CefRefPtr<CefBinaryValue> GetAsJPEG(float scale_factor,
+                                      int quality,
+                                      int& pixel_width,
+                                      int& pixel_height) override;
+
+  // Add |bitmaps| which should be the same image at different scale factors.
+  // |scale_1x_size| is the size in pixels of the 1x factor image. If
+  // |scale_1x_size| is 0 the smallest image size in pixels will be used as the
+  // 1x factor size.
+  void AddBitmaps(int32_t scale_1x_size, const std::vector<SkBitmap>& bitmaps);
+
+  // Return a representation of this Image that contains only the bitmap nearest
+  // |scale_factor| as the 1x scale representation. Conceptually this is an
+  // incorrect representation but is necessary to work around bugs in the views
+  // architecture.
+  // TODO(cef): Remove once https://crbug.com/597732 is resolved.
+  gfx::ImageSkia GetForced1xScaleRepresentation(float scale_factor) const;
+
+  // Returns the skia representation of this Image.
+  gfx::ImageSkia AsImageSkia() const;
+
+ private:
+  // Add a bitmap.
+  bool AddBitmap(float scale_factor, const SkBitmap& bitmap);
+
+  // Returns the bitmap that most closely matches |scale_factor| or nullptr if
+  // one doesn't exist.
+  const SkBitmap* GetBitmap(float scale_factor) const;
+
+  // Convert |src_bitmap| to |target_bitmap| with |target_ct| and |target_at|.
+  static bool ConvertBitmap(const SkBitmap& src_bitmap,
+                            SkBitmap* target_bitmap,
+                            SkColorType target_ct,
+                            SkAlphaType target_at);
+
+  // The |bitmap| argument will be RGBA or BGRA and either opaque or transparent
+  // with post-multiplied alpha. Writes the compressed output into |compressed|.
+  typedef base::Callback<bool(const SkBitmap& /*bitmap*/,
+                              std::vector<unsigned char>* /*compressed*/)>
+      CompressionMethod;
+
+  // Write |bitmap| into |compressed| using |method|.
+  static bool WriteCompressedFormat(const SkBitmap& bitmap,
+                                    std::vector<unsigned char>* compressed,
+                                    const CompressionMethod& method);
+
+  // Write |bitmap| into |compressed| using PNG encoding.
+  static bool WritePNG(const SkBitmap& bitmap,
+                       std::vector<unsigned char>* compressed,
+                       bool with_transparency);
+
+  // Write |bitmap| into |compressed| using JPEG encoding. The alpha channel
+  // will be ignored.
+  static bool WriteJPEG(const SkBitmap& bitmap,
+                        std::vector<unsigned char>* compressed,
+                        int quality);
+
+  mutable base::Lock lock_;
+
+  // Access to |image_| must be protected by |lock_|.
+  gfx::Image image_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefImageImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefImageImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_IMAGE_IMPL_H_
diff --git a/src/libcef/browser/javascript_dialog_manager.cc b/src/libcef/browser/javascript_dialog_manager.cc
new file mode 100644
index 0000000..4e4c791
--- /dev/null
+++ b/src/libcef/browser/javascript_dialog_manager.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/javascript_dialog_manager.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/url_formatter/elide_url.h"
+
+namespace {
+
+class CefJSDialogCallbackImpl : public CefJSDialogCallback {
+ public:
+  using CallbackType = content::JavaScriptDialogManager::DialogClosedCallback;
+
+  CefJSDialogCallbackImpl(CallbackType callback)
+      : callback_(std::move(callback)) {}
+  ~CefJSDialogCallbackImpl() override {
+    if (!callback_.is_null()) {
+      // The callback is still pending. Cancel it now.
+      if (CEF_CURRENTLY_ON_UIT()) {
+        CancelNow(std::move(callback_));
+      } else {
+        CEF_POST_TASK(CEF_UIT,
+                      base::BindOnce(&CefJSDialogCallbackImpl::CancelNow,
+                                     std::move(callback_)));
+      }
+    }
+  }
+
+  void Continue(bool success, const CefString& user_input) override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (!callback_.is_null()) {
+        std::move(callback_).Run(success, user_input);
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefJSDialogCallbackImpl::Continue,
+                                            this, success, user_input));
+    }
+  }
+
+  CallbackType Disconnect() WARN_UNUSED_RESULT { return std::move(callback_); }
+
+ private:
+  static void CancelNow(CallbackType callback) {
+    CEF_REQUIRE_UIT();
+    std::move(callback).Run(false, base::string16());
+  }
+
+  CallbackType callback_;
+
+  IMPLEMENT_REFCOUNTING(CefJSDialogCallbackImpl);
+};
+
+}  // namespace
+
+CefJavaScriptDialogManager::CefJavaScriptDialogManager(
+    CefBrowserHostImpl* browser,
+    std::unique_ptr<CefJavaScriptDialogRunner> runner)
+    : browser_(browser),
+      runner_(std::move(runner)),
+      dialog_running_(false),
+      weak_ptr_factory_(this) {}
+
+CefJavaScriptDialogManager::~CefJavaScriptDialogManager() {}
+
+void CefJavaScriptDialogManager::Destroy() {
+  if (runner_.get()) {
+    runner_.reset(nullptr);
+  }
+}
+
+void CefJavaScriptDialogManager::RunJavaScriptDialog(
+    content::WebContents* web_contents,
+    content::RenderFrameHost* render_frame_host,
+    content::JavaScriptDialogType message_type,
+    const base::string16& message_text,
+    const base::string16& default_prompt_text,
+    DialogClosedCallback callback,
+    bool* did_suppress_message) {
+  const GURL& origin_url = render_frame_host->GetLastCommittedURL();
+
+  CefRefPtr<CefClient> client = browser_->GetClient();
+  if (client.get()) {
+    CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
+    if (handler.get()) {
+      *did_suppress_message = false;
+
+      CefRefPtr<CefJSDialogCallbackImpl> callbackPtr(
+          new CefJSDialogCallbackImpl(std::move(callback)));
+
+      // Execute the user callback.
+      bool handled = handler->OnJSDialog(
+          browser_, origin_url.spec(),
+          static_cast<cef_jsdialog_type_t>(message_type), message_text,
+          default_prompt_text, callbackPtr.get(), *did_suppress_message);
+      if (handled) {
+        // Invalid combination of values. Crash sooner rather than later.
+        CHECK(!*did_suppress_message);
+        return;
+      }
+
+      // |callback| may be null if the user executed it despite returning false.
+      callback = callbackPtr->Disconnect();
+      if (callback.is_null() || *did_suppress_message)
+        return;
+    }
+  }
+
+  *did_suppress_message = false;
+
+  if (!runner_.get() || dialog_running_) {
+    // Suppress the dialog if there is no platform runner or if the dialog is
+    // currently running.
+    if (!runner_.get())
+      LOG(WARNING) << "No javascript dialog runner available for this platform";
+    *did_suppress_message = true;
+    return;
+  }
+
+  dialog_running_ = true;
+
+  const base::string16& display_url =
+      url_formatter::FormatUrlForSecurityDisplay(origin_url);
+
+  DCHECK(!callback.is_null());
+  runner_->Run(
+      browser_, message_type, display_url, message_text, default_prompt_text,
+      base::BindOnce(&CefJavaScriptDialogManager::DialogClosed,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void CefJavaScriptDialogManager::RunBeforeUnloadDialog(
+    content::WebContents* web_contents,
+    content::RenderFrameHost* render_frame_host,
+    bool is_reload,
+    DialogClosedCallback callback) {
+  if (browser_->destruction_state() >=
+      CefBrowserHostImpl::DESTRUCTION_STATE_ACCEPTED) {
+    // Currently destroying the browser. Accept the unload without showing
+    // the prompt.
+    std::move(callback).Run(true, base::string16());
+    return;
+  }
+
+  const base::string16& message_text =
+      base::ASCIIToUTF16("Is it OK to leave/reload this page?");
+
+  CefRefPtr<CefClient> client = browser_->GetClient();
+  if (client.get()) {
+    CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
+    if (handler.get()) {
+      CefRefPtr<CefJSDialogCallbackImpl> callbackPtr(
+          new CefJSDialogCallbackImpl(std::move(callback)));
+
+      // Execute the user callback.
+      bool handled = handler->OnBeforeUnloadDialog(
+          browser_, message_text, is_reload, callbackPtr.get());
+      if (handled)
+        return;
+
+      // |callback| may be null if the user executed it despite returning false.
+      callback = callbackPtr->Disconnect();
+      if (callback.is_null())
+        return;
+    }
+  }
+
+  if (!runner_.get() || dialog_running_) {
+    if (!runner_.get())
+      LOG(WARNING) << "No javascript dialog runner available for this platform";
+    // Suppress the dialog if there is no platform runner or if the dialog is
+    // currently running.
+    std::move(callback).Run(true, base::string16());
+    return;
+  }
+
+  dialog_running_ = true;
+
+  DCHECK(!callback.is_null());
+  runner_->Run(
+      browser_, content::JAVASCRIPT_DIALOG_TYPE_CONFIRM,
+      base::string16(),  // display_url
+      message_text,
+      base::string16(),  // default_prompt_text
+      base::BindOnce(&CefJavaScriptDialogManager::DialogClosed,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void CefJavaScriptDialogManager::CancelDialogs(
+    content::WebContents* web_contents,
+    bool reset_state) {
+  CefRefPtr<CefClient> client = browser_->GetClient();
+  if (client.get()) {
+    CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
+    if (handler.get()) {
+      // Execute the user callback.
+      handler->OnResetDialogState(browser_);
+    }
+  }
+
+  if (runner_.get() && dialog_running_) {
+    runner_->Cancel();
+    dialog_running_ = false;
+  }
+}
+
+void CefJavaScriptDialogManager::DialogClosed(
+    DialogClosedCallback callback,
+    bool success,
+    const base::string16& user_input) {
+  CefRefPtr<CefClient> client = browser_->GetClient();
+  if (client.get()) {
+    CefRefPtr<CefJSDialogHandler> handler = client->GetJSDialogHandler();
+    if (handler.get())
+      handler->OnDialogClosed(browser_);
+  }
+
+  DCHECK(runner_.get());
+  DCHECK(dialog_running_);
+
+  dialog_running_ = false;
+
+  std::move(callback).Run(success, user_input);
+}
diff --git a/src/libcef/browser/javascript_dialog_manager.h b/src/libcef/browser/javascript_dialog_manager.h
new file mode 100644
index 0000000..227cb52
--- /dev/null
+++ b/src/libcef/browser/javascript_dialog_manager.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_MANAGER_H_
+#pragma once
+
+#include <string>
+
+#include "libcef/browser/javascript_dialog_runner.h"
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/javascript_dialog_manager.h"
+
+class CefBrowserHostImpl;
+
+class CefJavaScriptDialogManager : public content::JavaScriptDialogManager {
+ public:
+  // |runner| may be NULL if the platform doesn't implement dialogs.
+  CefJavaScriptDialogManager(CefBrowserHostImpl* browser,
+                             std::unique_ptr<CefJavaScriptDialogRunner> runner);
+  ~CefJavaScriptDialogManager() override;
+
+  // Delete the runner to free any platform constructs.
+  void Destroy();
+
+  // JavaScriptDialogManager methods.
+  void RunJavaScriptDialog(content::WebContents* web_contents,
+                           content::RenderFrameHost* render_frame_host,
+                           content::JavaScriptDialogType message_type,
+                           const base::string16& message_text,
+                           const base::string16& default_prompt_text,
+                           DialogClosedCallback callback,
+                           bool* did_suppress_message) override;
+  void RunBeforeUnloadDialog(content::WebContents* web_contents,
+                             content::RenderFrameHost* render_frame_host,
+                             bool is_reload,
+                             DialogClosedCallback callback) override;
+  void CancelDialogs(content::WebContents* web_contents,
+                     bool reset_state) override;
+
+ private:
+  // Method executed by the callback passed to CefJavaScriptDialogRunner::Run.
+  void DialogClosed(DialogClosedCallback callback,
+                    bool success,
+                    const base::string16& user_input);
+
+  // CefBrowserHostImpl pointer is guaranteed to outlive this object.
+  CefBrowserHostImpl* browser_;
+
+  std::unique_ptr<CefJavaScriptDialogRunner> runner_;
+
+  // True if a dialog is currently running.
+  bool dialog_running_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<CefJavaScriptDialogManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefJavaScriptDialogManager);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_MANAGER_H_
diff --git a/src/libcef/browser/javascript_dialog_runner.h b/src/libcef/browser/javascript_dialog_runner.h
new file mode 100644
index 0000000..8d79320
--- /dev/null
+++ b/src/libcef/browser/javascript_dialog_runner.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_RUNNER_H_
+#define CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_RUNNER_H_
+#pragma once
+
+#include "base/callback.h"
+#include "base/strings/string16.h"
+#include "content/public/common/javascript_dialog_type.h"
+
+class CefBrowserHostImpl;
+
+class CefJavaScriptDialogRunner {
+ public:
+  typedef base::OnceCallback<void(bool /* success */,
+                                  const base::string16& /* user_input */)>
+      DialogClosedCallback;
+
+  // Run the dialog. Execute |callback| on completion.
+  virtual void Run(CefBrowserHostImpl* browser,
+                   content::JavaScriptDialogType message_type,
+                   const base::string16& display_url,
+                   const base::string16& message_text,
+                   const base::string16& default_prompt_text,
+                   DialogClosedCallback callback) = 0;
+
+  // Cancel a dialog mid-flight.
+  virtual void Cancel() = 0;
+
+ protected:
+  // Allow deletion via scoped_ptr only.
+  friend std::default_delete<CefJavaScriptDialogRunner>;
+
+  CefJavaScriptDialogRunner() {}
+  virtual ~CefJavaScriptDialogRunner() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefJavaScriptDialogRunner);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_RUNNER_H_
diff --git a/src/libcef/browser/media_capture_devices_dispatcher.cc b/src/libcef/browser/media_capture_devices_dispatcher.cc
new file mode 100644
index 0000000..cc2c75e
--- /dev/null
+++ b/src/libcef/browser/media_capture_devices_dispatcher.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/media_capture_devices_dispatcher.h"
+
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/media_capture_devices.h"
+
+using blink::MediaStreamDevices;
+using content::BrowserThread;
+
+namespace {
+
+const blink::MediaStreamDevice* FindDefaultDeviceWithId(
+    const blink::MediaStreamDevices& devices,
+    const std::string& device_id) {
+  if (devices.empty())
+    return nullptr;
+
+  blink::MediaStreamDevices::const_iterator iter = devices.begin();
+  for (; iter != devices.end(); ++iter) {
+    if (iter->id == device_id) {
+      return &(*iter);
+    }
+  }
+
+  return &(*devices.begin());
+}
+
+}  // namespace
+
+CefMediaCaptureDevicesDispatcher*
+CefMediaCaptureDevicesDispatcher::GetInstance() {
+  return base::Singleton<CefMediaCaptureDevicesDispatcher>::get();
+}
+
+CefMediaCaptureDevicesDispatcher::CefMediaCaptureDevicesDispatcher() {}
+
+CefMediaCaptureDevicesDispatcher::~CefMediaCaptureDevicesDispatcher() {}
+
+void CefMediaCaptureDevicesDispatcher::RegisterPrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(prefs::kDefaultAudioCaptureDevice,
+                               std::string());
+  registry->RegisterStringPref(prefs::kDefaultVideoCaptureDevice,
+                               std::string());
+}
+
+void CefMediaCaptureDevicesDispatcher::GetDefaultDevices(
+    PrefService* prefs,
+    bool audio,
+    bool video,
+    blink::MediaStreamDevices* devices) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(audio || video);
+
+  std::string default_device;
+  if (audio) {
+    default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
+    GetRequestedDevice(default_device, true, false, devices);
+  }
+
+  if (video) {
+    default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
+    GetRequestedDevice(default_device, false, true, devices);
+  }
+}
+
+void CefMediaCaptureDevicesDispatcher::GetRequestedDevice(
+    const std::string& requested_device_id,
+    bool audio,
+    bool video,
+    blink::MediaStreamDevices* devices) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(audio || video);
+
+  if (audio) {
+    const blink::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
+    const blink::MediaStreamDevice* const device =
+        FindDefaultDeviceWithId(audio_devices, requested_device_id);
+    if (device)
+      devices->push_back(*device);
+  }
+  if (video) {
+    const blink::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
+    const blink::MediaStreamDevice* const device =
+        FindDefaultDeviceWithId(video_devices, requested_device_id);
+    if (device)
+      devices->push_back(*device);
+  }
+}
+
+void CefMediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {}
+
+void CefMediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {}
+
+void CefMediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
+    int render_process_id,
+    int render_frame_id,
+    int page_request_id,
+    const GURL& security_origin,
+    blink::mojom::MediaStreamType stream_type,
+    content::MediaRequestState state) {}
+
+void CefMediaCaptureDevicesDispatcher::OnCreatingAudioStream(
+    int render_process_id,
+    int render_view_id) {}
+
+void CefMediaCaptureDevicesDispatcher::OnSetCapturingLinkSecured(
+    int render_process_id,
+    int render_frame_id,
+    int page_request_id,
+    blink::mojom::MediaStreamType stream_type,
+    bool is_secure) {}
+
+const MediaStreamDevices&
+CefMediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  return content::MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
+}
+
+const MediaStreamDevices&
+CefMediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  return content::MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
+}
diff --git a/src/libcef/browser/media_capture_devices_dispatcher.h b/src/libcef/browser/media_capture_devices_dispatcher.h
new file mode 100644
index 0000000..1104206
--- /dev/null
+++ b/src/libcef/browser/media_capture_devices_dispatcher.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
+#define CEF_LIBCEF_BROWSER_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
+
+#include "base/callback.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list.h"
+#include "content/public/browser/media_observer.h"
+#include "content/public/browser/media_stream_request.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+// This singleton is used to receive updates about media events from the content
+// layer. Based on chrome/browser/media/media_capture_devices_dispatcher.[h|cc].
+class CefMediaCaptureDevicesDispatcher : public content::MediaObserver {
+ public:
+  static CefMediaCaptureDevicesDispatcher* GetInstance();
+
+  // Registers the preferences related to Media Stream default devices.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  // Helper to get the default devices which can be used by the media request,
+  // if the return list is empty, it means there is no available device on the
+  // OS. Called on the UI thread.
+  void GetDefaultDevices(PrefService* prefs,
+                         bool audio,
+                         bool video,
+                         blink::MediaStreamDevices* devices);
+
+  // Helper for picking the device that was requested for an OpenDevice request.
+  // If the device requested is not available it will revert to using the first
+  // available one instead or will return an empty list if no devices of the
+  // requested kind are present. Called on the UI thread.
+  void GetRequestedDevice(const std::string& requested_device_id,
+                          bool audio,
+                          bool video,
+                          blink::MediaStreamDevices* devices);
+
+  // Overridden from content::MediaObserver:
+  void OnAudioCaptureDevicesChanged() override;
+  void OnVideoCaptureDevicesChanged() override;
+  void OnMediaRequestStateChanged(int render_process_id,
+                                  int render_frame_id,
+                                  int page_request_id,
+                                  const GURL& security_origin,
+                                  blink::mojom::MediaStreamType stream_type,
+                                  content::MediaRequestState state) override;
+  void OnCreatingAudioStream(int render_process_id,
+                             int render_view_id) override;
+  void OnSetCapturingLinkSecured(int render_process_id,
+                                 int render_frame_id,
+                                 int page_request_id,
+                                 blink::mojom::MediaStreamType stream_type,
+                                 bool is_secure) override;
+
+ private:
+  friend struct base::DefaultSingletonTraits<CefMediaCaptureDevicesDispatcher>;
+
+  CefMediaCaptureDevicesDispatcher();
+  ~CefMediaCaptureDevicesDispatcher() override;
+
+  const blink::MediaStreamDevices& GetAudioCaptureDevices();
+  const blink::MediaStreamDevices& GetVideoCaptureDevices();
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
diff --git a/src/libcef/browser/media_router/media_route_impl.cc b/src/libcef/browser/media_router/media_route_impl.cc
new file mode 100644
index 0000000..928381d
--- /dev/null
+++ b/src/libcef/browser/media_router/media_route_impl.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/media_router/media_route_impl.h"
+
+#include "libcef/browser/media_router/media_router_manager.h"
+#include "libcef/browser/media_router/media_sink_impl.h"
+#include "libcef/browser/media_router/media_source_impl.h"
+#include "libcef/browser/thread_util.h"
+
+namespace {
+
+// Do not keep a reference to the object returned by this method.
+CefBrowserContext* GetBrowserContext(const CefBrowserContext::Getter& getter) {
+  CEF_REQUIRE_UIT();
+  DCHECK(!getter.is_null());
+
+  // Will return nullptr if the BrowserContext has been destroyed.
+  return getter.Run();
+}
+
+}  // namespace
+
+CefMediaRouteImpl::CefMediaRouteImpl(
+    const media_router::MediaRoute& route,
+    const CefBrowserContext::Getter& browser_context_getter)
+    : route_(route), browser_context_getter_(browser_context_getter) {
+  CEF_REQUIRE_UIT();
+}
+
+CefString CefMediaRouteImpl::GetId() {
+  return route_.media_route_id();
+}
+
+CefRefPtr<CefMediaSource> CefMediaRouteImpl::GetSource() {
+  return new CefMediaSourceImpl(route_.media_source().id());
+}
+
+CefRefPtr<CefMediaSink> CefMediaRouteImpl::GetSink() {
+  return new CefMediaSinkImpl(route_.media_sink_id(), route_.media_sink_name());
+}
+
+void CefMediaRouteImpl::SendRouteMessage(const void* message,
+                                         size_t message_size) {
+  std::string message_str(reinterpret_cast<const char*>(message), message_size);
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(
+            [](CefRefPtr<CefMediaRouteImpl> self, std::string message_str) {
+              self->SendRouteMessageInternal(std::move(message_str));
+            },
+            CefRefPtr<CefMediaRouteImpl>(this), std::move(message_str)));
+    return;
+  }
+
+  SendRouteMessageInternal(std::move(message_str));
+}
+
+void CefMediaRouteImpl::Terminate() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMediaRouteImpl::Terminate, this));
+    return;
+  }
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return;
+
+  browser_context->GetMediaRouterManager()->TerminateRoute(
+      route_.media_route_id());
+}
+
+void CefMediaRouteImpl::SendRouteMessageInternal(std::string message) {
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return;
+
+  browser_context->GetMediaRouterManager()->SendRouteMessage(
+      route_.media_route_id(), message);
+}
diff --git a/src/libcef/browser/media_router/media_route_impl.h b/src/libcef/browser/media_router/media_route_impl.h
new file mode 100644
index 0000000..9e707c6
--- /dev/null
+++ b/src/libcef/browser/media_router/media_route_impl.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTE_IMPL_H_
+#define CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTE_IMPL_H_
+#pragma once
+
+#include "include/cef_media_router.h"
+#include "libcef/browser/browser_context.h"
+
+#include "chrome/common/media_router/media_route.h"
+
+// Implementation of the CefMediaRoute interface. Only created on the UI thread.
+class CefMediaRouteImpl : public CefMediaRoute {
+ public:
+  CefMediaRouteImpl(const media_router::MediaRoute& route,
+                    const CefBrowserContext::Getter& browser_context_getter);
+
+  // CefMediaRoute methods.
+  CefString GetId() override;
+  CefRefPtr<CefMediaSource> GetSource() override;
+  CefRefPtr<CefMediaSink> GetSink() override;
+  void SendRouteMessage(const void* message, size_t message_size) override;
+  void Terminate() override;
+
+  const media_router::MediaRoute& route() const { return route_; }
+
+ private:
+  void SendRouteMessageInternal(std::string message);
+
+  // Read-only after creation.
+  const media_router::MediaRoute route_;
+  const CefBrowserContext::Getter browser_context_getter_;
+
+  IMPLEMENT_REFCOUNTING(CefMediaRouteImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefMediaRouteImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTE_IMPL_H_
diff --git a/src/libcef/browser/media_router/media_router_impl.cc b/src/libcef/browser/media_router/media_router_impl.cc
new file mode 100644
index 0000000..52c4f5e
--- /dev/null
+++ b/src/libcef/browser/media_router/media_router_impl.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/media_router/media_router_impl.h"
+
+#include "libcef/browser/media_router/media_route_impl.h"
+#include "libcef/browser/media_router/media_router_manager.h"
+#include "libcef/browser/media_router/media_sink_impl.h"
+#include "libcef/browser/media_router/media_source_impl.h"
+#include "libcef/browser/thread_util.h"
+
+namespace {
+
+// Do not keep a reference to the object returned by this method.
+CefBrowserContext* GetBrowserContext(const CefBrowserContext::Getter& getter) {
+  CEF_REQUIRE_UIT();
+  DCHECK(!getter.is_null());
+
+  // Will return nullptr if the BrowserContext has been destroyed.
+  return getter.Run();
+}
+
+}  // namespace
+
+class CefRegistrationImpl : public CefRegistration,
+                            public CefMediaRouterManager::Observer {
+ public:
+  explicit CefRegistrationImpl(CefRefPtr<CefMediaObserver> observer)
+      : observer_(observer) {
+    DCHECK(observer_);
+  }
+
+  ~CefRegistrationImpl() override {
+    CEF_REQUIRE_UIT();
+
+    // May be null if OnMediaRouterDestroyed was called.
+    if (browser_context_getter_.is_null())
+      return;
+
+    auto browser_context = GetBrowserContext(browser_context_getter_);
+    if (!browser_context)
+      return;
+
+    browser_context->GetMediaRouterManager()->RemoveObserver(this);
+  }
+
+  void Initialize(const CefBrowserContext::Getter& browser_context_getter) {
+    CEF_REQUIRE_UIT();
+    DCHECK(!browser_context_getter.is_null());
+    DCHECK(browser_context_getter_.is_null());
+    browser_context_getter_ = browser_context_getter;
+
+    auto browser_context = GetBrowserContext(browser_context_getter_);
+    if (!browser_context)
+      return;
+
+    browser_context->GetMediaRouterManager()->AddObserver(this);
+  }
+
+ private:
+  // CefMediaRouterManager::Observer methods:
+  void OnMediaRouterDestroyed() override { browser_context_getter_.Reset(); }
+
+  void OnMediaSinks(
+      const CefMediaRouterManager::MediaSinkVector& sinks) override {
+    std::vector<CefRefPtr<CefMediaSink>> cef_sinks;
+    for (const auto& sink : sinks) {
+      cef_sinks.push_back(new CefMediaSinkImpl(sink.sink));
+    }
+    observer_->OnSinks(cef_sinks);
+  }
+
+  void OnMediaRoutes(
+      const CefMediaRouterManager::MediaRouteVector& routes) override {
+    std::vector<CefRefPtr<CefMediaRoute>> cef_routes;
+    for (const auto& route : routes) {
+      cef_routes.push_back(MakeCefRoute(route));
+    }
+    observer_->OnRoutes(cef_routes);
+  }
+
+  void OnMediaRouteMessages(
+      const media_router::MediaRoute& route,
+      const CefMediaRouterManager::MediaMessageVector& messages) override {
+    CefRefPtr<CefMediaRoute> cef_route = MakeCefRoute(route);
+    for (const auto& message : messages) {
+      if (message->type == media_router::mojom::RouteMessage::Type::TEXT) {
+        if (message->message.has_value()) {
+          const std::string& str = *(message->message);
+          observer_->OnRouteMessageReceived(cef_route, str.c_str(), str.size());
+        }
+      } else if (message->type ==
+                 media_router::mojom::RouteMessage::Type::BINARY) {
+        if (message->data.has_value()) {
+          const std::vector<uint8_t>& data = *(message->data);
+          observer_->OnRouteMessageReceived(cef_route, data.data(),
+                                            data.size());
+        }
+      }
+    }
+  }
+
+  void OnMediaRouteStateChange(
+      const media_router::MediaRoute& route,
+      const content::PresentationConnectionStateChangeInfo& info) override {
+    observer_->OnRouteStateChanged(MakeCefRoute(route),
+                                   ToConnectionState(info.state));
+  }
+
+  CefRefPtr<CefMediaRoute> MakeCefRoute(const media_router::MediaRoute& route) {
+    return new CefMediaRouteImpl(route, browser_context_getter_);
+  }
+
+  static CefMediaObserver::ConnectionState ToConnectionState(
+      blink::mojom::PresentationConnectionState state) {
+    switch (state) {
+      case blink::mojom::PresentationConnectionState::CONNECTING:
+        return CEF_MRCS_CONNECTING;
+      case blink::mojom::PresentationConnectionState::CONNECTED:
+        return CEF_MRCS_CONNECTED;
+      case blink::mojom::PresentationConnectionState::CLOSED:
+        return CEF_MRCS_CLOSED;
+      case blink::mojom::PresentationConnectionState::TERMINATED:
+        return CEF_MRCS_TERMINATED;
+    }
+    NOTREACHED();
+    return CEF_MRCS_UNKNOWN;
+  }
+
+  CefRefPtr<CefMediaObserver> observer_;
+  CefBrowserContext::Getter browser_context_getter_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefRegistrationImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefRegistrationImpl);
+};
+
+CefMediaRouterImpl::CefMediaRouterImpl() {
+  // Verify that our enum matches Chromium's values.
+  static_assert(
+      static_cast<int>(CEF_MRCR_TOTAL_COUNT) ==
+          static_cast<int>(media_router::RouteRequestResult::TOTAL_COUNT),
+      "enum mismatch");
+}
+
+void CefMediaRouterImpl::Initialize(
+    const CefBrowserContext::Getter& browser_context_getter) {
+  CEF_REQUIRE_UIT();
+  DCHECK(!browser_context_getter.is_null());
+  DCHECK(browser_context_getter_.is_null());
+  browser_context_getter_ = browser_context_getter;
+}
+
+CefRefPtr<CefRegistration> CefMediaRouterImpl::AddObserver(
+    CefRefPtr<CefMediaObserver> observer) {
+  if (!observer)
+    return nullptr;
+  CefRefPtr<CefRegistrationImpl> registration =
+      new CefRegistrationImpl(observer);
+  InitializeRegistrationOnUIThread(registration);
+  return registration.get();
+}
+
+CefRefPtr<CefMediaSource> CefMediaRouterImpl::GetSource(const CefString& urn) {
+  if (urn.empty())
+    return nullptr;
+
+  // Check for a valid URL and supported Cast/DIAL schemes.
+  GURL presentation_url(urn.ToString());
+  if (!media_router::IsValidPresentationUrl(presentation_url)) {
+    return nullptr;
+  }
+
+  if (presentation_url.SchemeIsHTTPOrHTTPS()) {
+    // We don't support tab/desktop mirroring, which is what Cast uses for
+    // arbitrary HTTP/HTTPS URLs (see CastMediaSource).
+    return nullptr;
+  }
+
+  return new CefMediaSourceImpl(presentation_url);
+}
+
+void CefMediaRouterImpl::NotifyCurrentSinks() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT, base::BindOnce(&CefMediaRouterImpl::NotifyCurrentSinks, this));
+    return;
+  }
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return;
+
+  browser_context->GetMediaRouterManager()->NotifyCurrentSinks();
+}
+
+void CefMediaRouterImpl::CreateRoute(
+    CefRefPtr<CefMediaSource> source,
+    CefRefPtr<CefMediaSink> sink,
+    CefRefPtr<CefMediaRouteCreateCallback> callback) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMediaRouterImpl::CreateRoute,
+                                          this, source, sink, callback));
+    return;
+  }
+
+  std::string error;
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context) {
+    error = "Context has already been destroyed";
+  } else if (!source || !source->IsValid()) {
+    error = "Source is empty or invalid";
+  } else if (!sink || !sink->IsValid()) {
+    error = "Sink is empty or invalid";
+  } else if (!sink->IsCompatibleWith(source)) {
+    error = "Sink is not compatible with source";
+  }
+
+  if (!error.empty()) {
+    LOG(WARNING) << "Media route creation failed: " << error;
+    if (callback) {
+      callback->OnMediaRouteCreateFinished(CEF_MRCR_UNKNOWN_ERROR, error,
+                                           nullptr);
+    }
+    return;
+  }
+
+  auto source_impl = static_cast<CefMediaSourceImpl*>(source.get());
+  auto sink_impl = static_cast<CefMediaSinkImpl*>(sink.get());
+
+  browser_context->GetMediaRouterManager()->CreateRoute(
+      source_impl->source().id(), sink_impl->sink().id(), url::Origin(),
+      base::BindOnce(&CefMediaRouterImpl::CreateRouteCallback, this, callback));
+}
+
+void CefMediaRouterImpl::NotifyCurrentRoutes() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(
+                               &CefMediaRouterImpl::NotifyCurrentRoutes, this));
+    return;
+  }
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return;
+
+  browser_context->GetMediaRouterManager()->NotifyCurrentRoutes();
+}
+
+void CefMediaRouterImpl::InitializeRegistrationOnUIThread(
+    CefRefPtr<CefRegistrationImpl> registration) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefMediaRouterImpl::InitializeRegistrationOnUIThread,
+                       this, registration));
+    return;
+  }
+  registration->Initialize(browser_context_getter_);
+}
+
+void CefMediaRouterImpl::CreateRouteCallback(
+    CefRefPtr<CefMediaRouteCreateCallback> callback,
+    const media_router::RouteRequestResult& result) {
+  CEF_REQUIRE_UIT();
+
+  if (result.result_code() != media_router::RouteRequestResult::OK) {
+    LOG(WARNING) << "Media route creation failed: " << result.error() << " ("
+                 << result.result_code() << ")";
+  }
+
+  if (!callback)
+    return;
+
+  CefRefPtr<CefMediaRoute> route;
+  if (result.result_code() == media_router::RouteRequestResult::OK &&
+      result.route()) {
+    route = new CefMediaRouteImpl(*result.route(), browser_context_getter_);
+  }
+
+  callback->OnMediaRouteCreateFinished(
+      static_cast<cef_media_route_create_result_t>(result.result_code()),
+      result.error(), route);
+}
+
+// static
+CefRefPtr<CefMediaRouter> CefMediaRouter::GetGlobalMediaRouter() {
+  return CefRequestContext::GetGlobalContext()->GetMediaRouter();
+}
diff --git a/src/libcef/browser/media_router/media_router_impl.h b/src/libcef/browser/media_router/media_router_impl.h
new file mode 100644
index 0000000..6512f35
--- /dev/null
+++ b/src/libcef/browser/media_router/media_router_impl.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_IMPL_H_
+#define CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_IMPL_H_
+#pragma once
+
+#include "include/cef_media_router.h"
+#include "libcef/browser/browser_context.h"
+
+#include "chrome/common/media_router/mojom/media_router.mojom.h"
+
+class CefRegistrationImpl;
+
+// Implementation of the CefMediaRouter interface. May be created on any thread.
+class CefMediaRouterImpl : public CefMediaRouter {
+ public:
+  CefMediaRouterImpl();
+
+  // Called on the UI thread after object creation and before any other object
+  // methods are executed on the UI thread.
+  void Initialize(const CefBrowserContext::Getter& browser_context_getter);
+
+  // CefMediaRouter methods.
+  CefRefPtr<CefRegistration> AddObserver(
+      CefRefPtr<CefMediaObserver> observer) override;
+  CefRefPtr<CefMediaSource> GetSource(const CefString& urn) override;
+  void NotifyCurrentSinks() override;
+  void CreateRoute(CefRefPtr<CefMediaSource> source,
+                   CefRefPtr<CefMediaSink> sink,
+                   CefRefPtr<CefMediaRouteCreateCallback> callback) override;
+  void NotifyCurrentRoutes() override;
+
+ private:
+  void InitializeRegistrationOnUIThread(
+      CefRefPtr<CefRegistrationImpl> registration);
+
+  void CreateRouteCallback(CefRefPtr<CefMediaRouteCreateCallback> callback,
+                           const media_router::RouteRequestResult& result);
+
+  // Only accessed on the UI thread. Will be non-null after Initialize().
+  CefBrowserContext::Getter browser_context_getter_;
+
+  IMPLEMENT_REFCOUNTING(CefMediaRouterImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefMediaRouterImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_IMPL_H_
diff --git a/src/libcef/browser/media_router/media_router_manager.cc b/src/libcef/browser/media_router/media_router_manager.cc
new file mode 100644
index 0000000..d364751
--- /dev/null
+++ b/src/libcef/browser/media_router/media_router_manager.cc
@@ -0,0 +1,292 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/media_router/media_router_manager.h"
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/thread_util.h"
+
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/media_routes_observer.h"
+#include "chrome/browser/media/router/route_message_observer.h"
+#include "chrome/browser/media/router/route_message_util.h"
+
+namespace {
+
+const int kTimeoutMs = 5 * 1000;
+const char kDefaultPresentationUrl[] = "https://google.com";
+
+}  // namespace
+
+class CefMediaRoutesObserver : public media_router::MediaRoutesObserver {
+ public:
+  explicit CefMediaRoutesObserver(CefMediaRouterManager* manager)
+      : media_router::MediaRoutesObserver(manager->GetMediaRouter()),
+        manager_(manager) {}
+
+  void OnRoutesUpdated(const std::vector<media_router::MediaRoute>& routes,
+                       const std::vector<media_router::MediaRoute::Id>&
+                           joinable_route_ids) override {
+    manager_->routes_ = routes;
+    manager_->NotifyCurrentRoutes();
+  }
+
+ private:
+  CefMediaRouterManager* const manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefMediaRoutesObserver);
+};
+
+// Used to receive messages if PresentationConnection is not supported.
+class CefRouteMessageObserver : public media_router::RouteMessageObserver {
+ public:
+  CefRouteMessageObserver(CefMediaRouterManager* manager,
+                          const media_router::MediaRoute& route)
+      : media_router::RouteMessageObserver(manager->GetMediaRouter(),
+                                           route.media_route_id()),
+        manager_(manager),
+        route_(route) {}
+
+  void OnMessagesReceived(
+      CefMediaRouterManager::MediaMessageVector messages) override {
+    manager_->OnMessagesReceived(route_, messages);
+  }
+
+ private:
+  CefMediaRouterManager* const manager_;
+  const media_router::MediaRoute route_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefRouteMessageObserver);
+};
+
+// Used for messaging and route status notifications with Cast.
+class CefPresentationConnection : public blink::mojom::PresentationConnection {
+ public:
+  explicit CefPresentationConnection(
+      CefMediaRouterManager* manager,
+      const media_router::MediaRoute& route,
+      media_router::mojom::RoutePresentationConnectionPtr connections)
+      : manager_(manager),
+        route_(route),
+        connection_receiver_(this, std::move(connections->connection_receiver)),
+        connection_remote_(std::move(connections->connection_remote)) {}
+
+  void OnMessage(
+      blink::mojom::PresentationConnectionMessagePtr message) override {
+    CefMediaRouterManager::MediaMessageVector messages;
+    if (message->is_message()) {
+      messages.push_back(media_router::message_util::RouteMessageFromString(
+          message->get_message()));
+    } else if (message->is_data()) {
+      messages.push_back(media_router::message_util::RouteMessageFromData(
+          message->get_data()));
+    }
+    if (!messages.empty()) {
+      manager_->OnMessagesReceived(route_, messages);
+    }
+  }
+
+  void DidChangeState(
+      blink::mojom::PresentationConnectionState state) override {
+    // May result in |this| being deleted, so post async and allow the call
+    // stack to unwind.
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(&CefMediaRouterManager::OnRouteStateChange,
+                       manager_->weak_ptr_factory_.GetWeakPtr(), route_,
+                       content::PresentationConnectionStateChangeInfo(state)));
+  }
+
+  void DidClose(
+      blink::mojom::PresentationConnectionCloseReason reason) override {
+    DidChangeState(blink::mojom::PresentationConnectionState::CLOSED);
+  }
+
+  void SendRouteMessage(const std::string& message) {
+    connection_remote_->OnMessage(
+        blink::mojom::PresentationConnectionMessage::NewMessage(message));
+  }
+
+ private:
+  CefMediaRouterManager* const manager_;
+  const media_router::MediaRoute route_;
+
+  // Used to receive messages from the MRP.
+  mojo::Receiver<blink::mojom::PresentationConnection> connection_receiver_;
+
+  // Used to send messages to the MRP.
+  mojo::Remote<blink::mojom::PresentationConnection> connection_remote_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefPresentationConnection);
+};
+
+CefMediaRouterManager::CefMediaRouterManager(CefBrowserContext* browser_context)
+    : browser_context_(browser_context),
+      query_result_manager_(GetMediaRouter()),
+      weak_ptr_factory_(this) {
+  // Perform initialization.
+  GetMediaRouter()->OnUserGesture();
+
+  query_result_manager_.AddObserver(this);
+
+  // A non-empty presentation URL to required for discovery of Cast devices.
+  query_result_manager_.SetSourcesForCastMode(
+      media_router::MediaCastMode::PRESENTATION,
+      {media_router::MediaSource::ForPresentationUrl(
+          GURL(kDefaultPresentationUrl))},
+      url::Origin());
+
+  routes_observer_ = std::make_unique<CefMediaRoutesObserver>(this);
+}
+
+CefMediaRouterManager::~CefMediaRouterManager() {
+  CEF_REQUIRE_UIT();
+  for (auto& observer : observers_) {
+    observers_.RemoveObserver(&observer);
+    observer.OnMediaRouterDestroyed();
+  }
+
+  query_result_manager_.RemoveObserver(this);
+}
+
+void CefMediaRouterManager::AddObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.AddObserver(observer);
+}
+
+void CefMediaRouterManager::RemoveObserver(Observer* observer) {
+  CEF_REQUIRE_UIT();
+  observers_.RemoveObserver(observer);
+}
+
+void CefMediaRouterManager::NotifyCurrentSinks() {
+  CEF_REQUIRE_UIT();
+  for (auto& observer : observers_) {
+    observer.OnMediaSinks(sinks_);
+  }
+}
+
+void CefMediaRouterManager::NotifyCurrentRoutes() {
+  CEF_REQUIRE_UIT();
+  for (auto& observer : observers_) {
+    observer.OnMediaRoutes(routes_);
+  }
+}
+
+void CefMediaRouterManager::CreateRoute(
+    const media_router::MediaSource::Id& source_id,
+    const media_router::MediaSink::Id& sink_id,
+    const url::Origin& origin,
+    CreateRouteResultCallback callback) {
+  GetMediaRouter()->CreateRoute(
+      source_id, sink_id, origin, nullptr /* web_contents */,
+      base::BindOnce(&CefMediaRouterManager::OnCreateRoute,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
+      base::TimeDelta::FromMilliseconds(kTimeoutMs), false /* incognito */);
+}
+
+void CefMediaRouterManager::SendRouteMessage(
+    const media_router::MediaRoute::Id& route_id,
+    const std::string& message) {
+  // Must use PresentationConnection to send messages if it exists.
+  auto state = GetRouteState(route_id);
+  if (state && state->presentation_connection_) {
+    state->presentation_connection_->SendRouteMessage(message);
+    return;
+  }
+
+  GetMediaRouter()->SendRouteMessage(route_id, message);
+}
+
+void CefMediaRouterManager::TerminateRoute(
+    const media_router::MediaRoute::Id& route_id) {
+  GetMediaRouter()->TerminateRoute(route_id);
+}
+
+void CefMediaRouterManager::OnResultsUpdated(const MediaSinkVector& sinks) {
+  sinks_ = sinks;
+  NotifyCurrentSinks();
+}
+
+media_router::MediaRouter* CefMediaRouterManager::GetMediaRouter() const {
+  CEF_REQUIRE_UIT();
+  return media_router::MediaRouterFactory::GetApiForBrowserContext(
+      browser_context_);
+}
+
+void CefMediaRouterManager::OnCreateRoute(
+    CreateRouteResultCallback callback,
+    media_router::mojom::RoutePresentationConnectionPtr connection,
+    const media_router::RouteRequestResult& result) {
+  CEF_REQUIRE_UIT();
+  if (result.route()) {
+    CreateRouteState(*result.route(), std::move(connection));
+  }
+
+  std::move(callback).Run(result);
+}
+
+void CefMediaRouterManager::OnRouteStateChange(
+    const media_router::MediaRoute& route,
+    const content::PresentationConnectionStateChangeInfo& info) {
+  CEF_REQUIRE_UIT();
+  if (info.state == blink::mojom::PresentationConnectionState::CLOSED ||
+      info.state == blink::mojom::PresentationConnectionState::TERMINATED) {
+    RemoveRouteState(route.media_route_id());
+  }
+
+  for (auto& observer : observers_) {
+    observer.OnMediaRouteStateChange(route, info);
+  }
+}
+
+void CefMediaRouterManager::OnMessagesReceived(
+    const media_router::MediaRoute& route,
+    const MediaMessageVector& messages) {
+  CEF_REQUIRE_UIT();
+  for (auto& observer : observers_) {
+    observer.OnMediaRouteMessages(route, messages);
+  }
+}
+
+void CefMediaRouterManager::CreateRouteState(
+    const media_router::MediaRoute& route,
+    media_router::mojom::RoutePresentationConnectionPtr connection) {
+  const auto route_id = route.media_route_id();
+  auto state = std::make_unique<RouteState>();
+
+  if (!connection.is_null()) {
+    // PresentationConnection must be used for messaging and status
+    // notifications if it exists.
+    state->presentation_connection_ =
+        std::make_unique<CefPresentationConnection>(this, route,
+                                                    std::move(connection));
+  } else {
+    // Fallback if PresentationConnection is not supported.
+    state->message_observer_ =
+        std::make_unique<CefRouteMessageObserver>(this, route);
+    state->state_subscription_ =
+        GetMediaRouter()->AddPresentationConnectionStateChangedCallback(
+            route_id,
+            base::BindRepeating(&CefMediaRouterManager::OnRouteStateChange,
+                                weak_ptr_factory_.GetWeakPtr(), route));
+  }
+
+  route_state_map_.insert(std::make_pair(route_id, std::move(state)));
+}
+
+CefMediaRouterManager::RouteState* CefMediaRouterManager::GetRouteState(
+    const media_router::MediaRoute::Id& route_id) {
+  const auto it = route_state_map_.find(route_id);
+  if (it != route_state_map_.end())
+    return it->second.get();
+  return nullptr;
+}
+
+void CefMediaRouterManager::RemoveRouteState(
+    const media_router::MediaRoute::Id& route_id) {
+  auto it = route_state_map_.find(route_id);
+  if (it != route_state_map_.end())
+    route_state_map_.erase(it);
+}
diff --git a/src/libcef/browser/media_router/media_router_manager.h b/src/libcef/browser/media_router/media_router_manager.h
new file mode 100644
index 0000000..28b8f86
--- /dev/null
+++ b/src/libcef/browser/media_router/media_router_manager.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_MANAGER_H_
+#pragma once
+
+#include "include/cef_media_router.h"
+#include "libcef/browser/browser_context.h"
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chrome/browser/media/router/media_router.h"
+#include "chrome/browser/ui/media_router/query_result_manager.h"
+#include "chrome/common/media_router/mojom/media_router.mojom.h"
+
+class CefBrowserContext;
+class CefMediaRoutesObserver;
+class CefPresentationConnection;
+class CefRouteMessageObserver;
+
+// Manages CEF usage of MediaRouter. Owned by CefBrowserContext and only
+// accessed on the UI thread.
+class CefMediaRouterManager
+    : public media_router::QueryResultManager::Observer {
+ public:
+  using MediaRouteVector = std::vector<media_router::MediaRoute>;
+  using MediaSinkVector = std::vector<media_router::MediaSinkWithCastModes>;
+  using MediaMessageVector = std::vector<media_router::mojom::RouteMessagePtr>;
+
+  class Observer : public base::CheckedObserver {
+   public:
+    virtual void OnMediaRouterDestroyed() = 0;
+
+    virtual void OnMediaSinks(const MediaSinkVector& sinks) = 0;
+    virtual void OnMediaRoutes(const MediaRouteVector& routes) = 0;
+
+    virtual void OnMediaRouteMessages(const media_router::MediaRoute& route,
+                                      const MediaMessageVector& messages) = 0;
+    virtual void OnMediaRouteStateChange(
+        const media_router::MediaRoute& route,
+        const content::PresentationConnectionStateChangeInfo& info) = 0;
+
+   protected:
+    ~Observer() override {}
+  };
+
+  explicit CefMediaRouterManager(CefBrowserContext* browser_context);
+  ~CefMediaRouterManager() override;
+
+  // |observer| must outlive this object or be removed.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  void NotifyCurrentSinks();
+  void NotifyCurrentRoutes();
+
+  using CreateRouteResultCallback =
+      base::OnceCallback<void(const media_router::RouteRequestResult& result)>;
+
+  void CreateRoute(const media_router::MediaSource::Id& source_id,
+                   const media_router::MediaSink::Id& sink_id,
+                   const url::Origin& origin,
+                   CreateRouteResultCallback callback);
+
+  void SendRouteMessage(const media_router::MediaRoute::Id& route_id,
+                        const std::string& message);
+  void TerminateRoute(const media_router::MediaRoute::Id& route_id);
+
+  // QueryResultManager::Observer methods.
+  void OnResultsUpdated(const MediaSinkVector& sinks) override;
+
+ private:
+  friend class CefMediaRoutesObserver;
+  friend class CefPresentationConnection;
+  friend class CefRouteMessageObserver;
+
+  // Do not keep a reference to the object returned by this method.
+  media_router::MediaRouter* GetMediaRouter() const;
+
+  void OnCreateRoute(
+      CreateRouteResultCallback callback,
+      media_router::mojom::RoutePresentationConnectionPtr connection,
+      const media_router::RouteRequestResult& result);
+  void OnRouteStateChange(
+      const media_router::MediaRoute& route,
+      const content::PresentationConnectionStateChangeInfo& info);
+  void OnMessagesReceived(const media_router::MediaRoute& route,
+                          const MediaMessageVector& messages);
+
+  struct RouteState {
+    std::unique_ptr<CefPresentationConnection> presentation_connection_;
+
+    // Used if there is no RoutePresentationConnectionPtr.
+    std::unique_ptr<CefRouteMessageObserver> message_observer_;
+    std::unique_ptr<media_router::PresentationConnectionStateSubscription>
+        state_subscription_;
+  };
+  void CreateRouteState(
+      const media_router::MediaRoute& route,
+      media_router::mojom::RoutePresentationConnectionPtr connection);
+  RouteState* GetRouteState(const media_router::MediaRoute::Id& route_id);
+  void RemoveRouteState(const media_router::MediaRoute::Id& route_id);
+
+  CefBrowserContext* const browser_context_;
+
+  base::ObserverList<Observer> observers_;
+
+  media_router::QueryResultManager query_result_manager_;
+  std::unique_ptr<CefMediaRoutesObserver> routes_observer_;
+
+  MediaRouteVector routes_;
+  MediaSinkVector sinks_;
+
+  using RouteStateMap =
+      std::map<media_router::MediaRoute::Id, std::unique_ptr<RouteState>>;
+  RouteStateMap route_state_map_;
+
+  base::WeakPtrFactory<CefMediaRouterManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefMediaRouterManager);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_MANAGER_H_
diff --git a/src/libcef/browser/media_router/media_sink_impl.cc b/src/libcef/browser/media_router/media_sink_impl.cc
new file mode 100644
index 0000000..ea62d6c
--- /dev/null
+++ b/src/libcef/browser/media_router/media_sink_impl.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/media_router/media_sink_impl.h"
+
+#include "libcef/browser/thread_util.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/common/media_router/discovery/media_sink_internal.h"
+#include "chrome/common/media_router/discovery/media_sink_service_base.h"
+
+namespace {
+
+using SinkServiceVector = std::vector<media_router::MediaSinkServiceBase*>;
+
+SinkServiceVector GetSinkServices() {
+  CEF_REQUIRE_UIT();
+  auto sink_service = media_router::DualMediaSinkService::GetInstance();
+  return {sink_service->GetCastMediaSinkServiceImpl(),
+          sink_service->GetDialMediaSinkServiceImpl()};
+}
+
+void GetSinkInternalAndContinue(
+    SinkServiceVector services,
+    const media_router::MediaSink::Id& sink_id,
+    CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) {
+  CEF_REQUIRE_IOT();
+
+  CefMediaSinkDeviceInfo device_info;
+  const media_router::MediaSinkInternal* sink_internal = nullptr;
+
+  for (auto service : services) {
+    sink_internal = service->GetSinkById(sink_id);
+    if (sink_internal)
+      break;
+  }
+
+  if (sink_internal) {
+    if (sink_internal->is_cast_sink()) {
+      const auto& cast_data = sink_internal->cast_data();
+      CefString(&device_info.ip_address) =
+          cast_data.ip_endpoint.ToStringWithoutPort();
+      device_info.port = cast_data.ip_endpoint.port();
+      CefString(&device_info.model_name) = cast_data.model_name;
+    } else if (sink_internal->is_dial_sink()) {
+      const auto& dial_data = sink_internal->dial_data();
+      CefString(&device_info.ip_address) = dial_data.ip_address.ToString();
+      if (dial_data.app_url.is_valid() && dial_data.app_url.has_port()) {
+        base::StringToInt(dial_data.app_url.port_piece(), &device_info.port);
+      }
+      CefString(&device_info.model_name) = dial_data.model_name;
+    }
+  }
+
+  // Execute the callback on the UI thread.
+  CEF_POST_TASK(
+      CEF_UIT,
+      base::BindOnce(&CefMediaSinkDeviceInfoCallback::OnMediaSinkDeviceInfo,
+                     callback, device_info));
+}
+
+void GetDeviceInfo(const media_router::MediaSink::Id& sink_id,
+                   CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) {
+  auto next_step = base::BindOnce(
+      [](const media_router::MediaSink::Id& sink_id,
+         CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) {
+        CEF_POST_TASK(CEF_IOT,
+                      base::BindOnce(GetSinkInternalAndContinue,
+                                     GetSinkServices(), sink_id, callback));
+      },
+      sink_id, callback);
+
+  if (CEF_CURRENTLY_ON(CEF_UIT)) {
+    std::move(next_step).Run();
+  } else {
+    CEF_POST_TASK(CEF_UIT, std::move(next_step));
+  }
+}
+
+}  // namespace
+
+CefMediaSinkImpl::CefMediaSinkImpl(const media_router::MediaSink& sink)
+    : sink_(sink) {}
+
+CefMediaSinkImpl::CefMediaSinkImpl(const media_router::MediaSink::Id& sink_id,
+                                   const std::string& sink_name)
+    : sink_(sink_id, sink_name, media_router::SinkIconType::GENERIC) {}
+
+CefString CefMediaSinkImpl::GetId() {
+  return sink_.id();
+}
+
+bool CefMediaSinkImpl::IsValid() {
+  return true;
+}
+
+CefString CefMediaSinkImpl::GetName() {
+  return sink_.name();
+}
+
+CefString CefMediaSinkImpl::GetDescription() {
+  return sink_.description().value_or("");
+}
+
+CefMediaSink::IconType CefMediaSinkImpl::GetIconType() {
+  // Verify that our enum matches Chromium's values.
+  static_assert(static_cast<int>(CEF_MSIT_TOTAL_COUNT) ==
+                    static_cast<int>(media_router::SinkIconType::TOTAL_COUNT),
+                "enum mismatch");
+
+  return static_cast<CefMediaSink::IconType>(sink_.icon_type());
+}
+
+void CefMediaSinkImpl::GetDeviceInfo(
+    CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) {
+  ::GetDeviceInfo(sink_.id(), callback);
+}
+
+bool CefMediaSinkImpl::IsCastSink() {
+  return sink_.provider_id() == media_router::CAST;
+}
+
+bool CefMediaSinkImpl::IsDialSink() {
+  return sink_.provider_id() == media_router::DIAL;
+}
+
+bool CefMediaSinkImpl::IsCompatibleWith(CefRefPtr<CefMediaSource> source) {
+  if (source) {
+    if (IsCastSink())
+      return source->IsCastSource();
+    if (IsDialSink())
+      return source->IsDialSource();
+  }
+  return false;
+}
diff --git a/src/libcef/browser/media_router/media_sink_impl.h b/src/libcef/browser/media_router/media_sink_impl.h
new file mode 100644
index 0000000..bac3cae
--- /dev/null
+++ b/src/libcef/browser/media_router/media_sink_impl.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_SINK_IMPL_H_
+#define CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_SINK_IMPL_H_
+#pragma once
+
+#include "include/cef_media_router.h"
+
+#include "chrome/common/media_router/media_sink.h"
+
+// Implementation of the CefMediaSink interface. May be created on any thread.
+class CefMediaSinkImpl : public CefMediaSink {
+ public:
+  explicit CefMediaSinkImpl(const media_router::MediaSink& sink);
+  CefMediaSinkImpl(const media_router::MediaSink::Id& sink_id,
+                   const std::string& sink_name);
+
+  // CefMediaSink methods.
+  CefString GetId() override;
+  bool IsValid() override;
+  CefString GetName() override;
+  CefString GetDescription() override;
+  IconType GetIconType() override;
+  void GetDeviceInfo(
+      CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) override;
+  bool IsCastSink() override;
+  bool IsDialSink() override;
+  bool IsCompatibleWith(CefRefPtr<CefMediaSource> source) override;
+
+  const media_router::MediaSink& sink() const { return sink_; }
+
+ private:
+  // Read-only after creation.
+  const media_router::MediaSink sink_;
+
+  IMPLEMENT_REFCOUNTING(CefMediaSinkImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefMediaSinkImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_SINK_IMPL_H_
diff --git a/src/libcef/browser/media_router/media_source_impl.cc b/src/libcef/browser/media_router/media_source_impl.cc
new file mode 100644
index 0000000..2b722c7
--- /dev/null
+++ b/src/libcef/browser/media_router/media_source_impl.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/media_router/media_source_impl.h"
+
+CefMediaSourceImpl::CefMediaSourceImpl(
+    const media_router::MediaSource::Id& source_id)
+    : source_(source_id) {}
+
+CefMediaSourceImpl::CefMediaSourceImpl(const GURL& presentation_url)
+    : source_(presentation_url) {}
+
+CefString CefMediaSourceImpl::GetId() {
+  return source_.id();
+}
+
+bool CefMediaSourceImpl::IsValid() {
+  return source_.IsValid();
+}
+
+bool CefMediaSourceImpl::IsCastSource() {
+  return !IsDialSource();
+}
+
+bool CefMediaSourceImpl::IsDialSource() {
+  return source_.IsDialSource();
+}
diff --git a/src/libcef/browser/media_router/media_source_impl.h b/src/libcef/browser/media_router/media_source_impl.h
new file mode 100644
index 0000000..8e50158
--- /dev/null
+++ b/src/libcef/browser/media_router/media_source_impl.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_SOURCE_IMPL_H_
+#define CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_SOURCE_IMPL_H_
+#pragma once
+
+#include "include/cef_media_router.h"
+
+#include "chrome/common/media_router/media_source.h"
+
+// Implementation of the CefMediaSource interface. May be created on any thread.
+class CefMediaSourceImpl : public CefMediaSource {
+ public:
+  explicit CefMediaSourceImpl(const media_router::MediaSource::Id& source_id);
+  explicit CefMediaSourceImpl(const GURL& presentation_url);
+
+  // CefMediaSource methods.
+  CefString GetId() override;
+  bool IsValid() override;
+  bool IsCastSource() override;
+  bool IsDialSource() override;
+
+  const media_router::MediaSource& source() const { return source_; }
+
+ private:
+  // Read-only after creation.
+  const media_router::MediaSource source_;
+
+  IMPLEMENT_REFCOUNTING(CefMediaSourceImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefMediaSourceImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MEDIA_ROUTER_MEDIA_SOURCE_IMPL_H_
diff --git a/src/libcef/browser/menu_manager.cc b/src/libcef/browser/menu_manager.cc
new file mode 100644
index 0000000..8599ede
--- /dev/null
+++ b/src/libcef/browser/menu_manager.cc
@@ -0,0 +1,473 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/menu_manager.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/context_menu_params_impl.h"
+#include "libcef/browser/menu_runner.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/content_client.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "cef/grit/cef_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+
+namespace {
+
+CefString GetLabel(int message_id) {
+  base::string16 label =
+      CefContentClient::Get()->GetLocalizedString(message_id);
+  DCHECK(!label.empty());
+  return label;
+}
+
+const int kInvalidCommandId = -1;
+const cef_event_flags_t kEmptyEventFlags = static_cast<cef_event_flags_t>(0);
+
+class CefRunContextMenuCallbackImpl : public CefRunContextMenuCallback {
+ public:
+  typedef base::Callback<void(int, cef_event_flags_t)> Callback;
+
+  explicit CefRunContextMenuCallbackImpl(const Callback& callback)
+      : callback_(callback) {}
+
+  ~CefRunContextMenuCallbackImpl() {
+    if (!callback_.is_null()) {
+      // The callback is still pending. Cancel it now.
+      if (CEF_CURRENTLY_ON_UIT()) {
+        RunNow(callback_, kInvalidCommandId, kEmptyEventFlags);
+      } else {
+        CEF_POST_TASK(
+            CEF_UIT,
+            base::Bind(&CefRunContextMenuCallbackImpl::RunNow, callback_,
+                       kInvalidCommandId, kEmptyEventFlags));
+      }
+    }
+  }
+
+  void Continue(int command_id, cef_event_flags_t event_flags) override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (!callback_.is_null()) {
+        RunNow(callback_, command_id, event_flags);
+        callback_.Reset();
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT,
+                    base::Bind(&CefRunContextMenuCallbackImpl::Continue, this,
+                               command_id, event_flags));
+    }
+  }
+
+  void Cancel() override { Continue(kInvalidCommandId, kEmptyEventFlags); }
+
+  void Disconnect() { callback_.Reset(); }
+
+ private:
+  static void RunNow(const Callback& callback,
+                     int command_id,
+                     cef_event_flags_t event_flags) {
+    CEF_REQUIRE_UIT();
+    callback.Run(command_id, event_flags);
+  }
+
+  Callback callback_;
+
+  IMPLEMENT_REFCOUNTING(CefRunContextMenuCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefRunContextMenuCallbackImpl);
+};
+
+}  // namespace
+
+CefMenuManager::CefMenuManager(CefBrowserHostImpl* browser,
+                               std::unique_ptr<CefMenuRunner> runner)
+    : content::WebContentsObserver(browser->web_contents()),
+      browser_(browser),
+      runner_(std::move(runner)),
+      custom_menu_callback_(nullptr),
+      weak_ptr_factory_(this) {
+  DCHECK(web_contents());
+  model_ = new CefMenuModelImpl(this, nullptr, false);
+}
+
+CefMenuManager::~CefMenuManager() {
+  // The model may outlive the delegate if the context menu is visible when the
+  // application is closed.
+  model_->set_delegate(nullptr);
+}
+
+void CefMenuManager::Destroy() {
+  CancelContextMenu();
+  if (runner_)
+    runner_.reset(nullptr);
+}
+
+bool CefMenuManager::IsShowingContextMenu() {
+  if (!web_contents())
+    return false;
+  return web_contents()->IsShowingContextMenu();
+}
+
+bool CefMenuManager::CreateContextMenu(
+    const content::ContextMenuParams& params) {
+  // The renderer may send the "show context menu" message multiple times, one
+  // for each right click mouse event it receives. Normally, this doesn't happen
+  // because mouse events are not forwarded once the context menu is showing.
+  // However, there's a race - the context menu may not yet be showing when
+  // the second mouse event arrives. In this case, |HandleContextMenu()| will
+  // get called multiple times - if so, don't create another context menu.
+  // TODO(asvitkine): Fix the renderer so that it doesn't do this.
+  if (IsShowingContextMenu())
+    return true;
+
+  params_ = params;
+  model_->Clear();
+
+  // Create the default menu model.
+  CreateDefaultModel();
+
+  bool custom_menu = false;
+  DCHECK(!custom_menu_callback_);
+
+  // Give the client a chance to modify the model.
+  CefRefPtr<CefClient> client = browser_->GetClient();
+  if (client.get()) {
+    CefRefPtr<CefContextMenuHandler> handler = client->GetContextMenuHandler();
+    if (handler.get()) {
+      CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
+          new CefContextMenuParamsImpl(&params_));
+      CefRefPtr<CefFrame> frame = browser_->GetFocusedFrame();
+
+      handler->OnBeforeContextMenu(browser_, frame, paramsPtr.get(),
+                                   model_.get());
+
+      MenuWillShow(model_);
+
+      if (model_->GetCount() > 0) {
+        CefRefPtr<CefRunContextMenuCallbackImpl> callbackImpl(
+            new CefRunContextMenuCallbackImpl(
+                base::Bind(&CefMenuManager::ExecuteCommandCallback,
+                           weak_ptr_factory_.GetWeakPtr())));
+
+        // This reference will be cleared when the callback is executed or
+        // the callback object is deleted.
+        custom_menu_callback_ = callbackImpl.get();
+
+        if (handler->RunContextMenu(browser_, frame, paramsPtr.get(),
+                                    model_.get(), callbackImpl.get())) {
+          custom_menu = true;
+        } else {
+          // Callback should not be executed if the handler returns false.
+          DCHECK(custom_menu_callback_);
+          custom_menu_callback_ = nullptr;
+          callbackImpl->Disconnect();
+        }
+      }
+
+      // Do not keep references to the parameters in the callback.
+      paramsPtr->Detach(nullptr);
+      DCHECK(paramsPtr->HasOneRef());
+      DCHECK(model_->VerifyRefCount());
+
+      // Menu is empty so notify the client and return.
+      if (model_->GetCount() == 0 && !custom_menu) {
+        MenuClosed(model_);
+        return true;
+      }
+    }
+  }
+
+  if (custom_menu || !runner_)
+    return true;
+  return runner_->RunContextMenu(browser_, model_.get(), params_);
+}
+
+void CefMenuManager::CancelContextMenu() {
+  if (IsShowingContextMenu()) {
+    if (custom_menu_callback_)
+      custom_menu_callback_->Cancel();
+    else if (runner_)
+      runner_->CancelContextMenu();
+  }
+}
+
+void CefMenuManager::ExecuteCommand(CefRefPtr<CefMenuModelImpl> source,
+                                    int command_id,
+                                    cef_event_flags_t event_flags) {
+  // Give the client a chance to handle the command.
+  CefRefPtr<CefClient> client = browser_->GetClient();
+  if (client.get()) {
+    CefRefPtr<CefContextMenuHandler> handler = client->GetContextMenuHandler();
+    if (handler.get()) {
+      CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
+          new CefContextMenuParamsImpl(&params_));
+
+      bool handled = handler->OnContextMenuCommand(
+          browser_, browser_->GetFocusedFrame(), paramsPtr.get(), command_id,
+          event_flags);
+
+      // Do not keep references to the parameters in the callback.
+      paramsPtr->Detach(nullptr);
+      DCHECK(paramsPtr->HasOneRef());
+
+      if (handled)
+        return;
+    }
+  }
+
+  // Execute the default command handling.
+  ExecuteDefaultCommand(command_id);
+}
+
+void CefMenuManager::MenuWillShow(CefRefPtr<CefMenuModelImpl> source) {
+  // May be called for sub-menus as well.
+  if (source.get() != model_.get())
+    return;
+
+  if (!web_contents())
+    return;
+
+  // May be called multiple times.
+  if (IsShowingContextMenu())
+    return;
+
+  // Notify the host before showing the context menu.
+  web_contents()->SetShowingContextMenu(true);
+}
+
+void CefMenuManager::MenuClosed(CefRefPtr<CefMenuModelImpl> source) {
+  // May be called for sub-menus as well.
+  if (source.get() != model_.get())
+    return;
+
+  if (!web_contents())
+    return;
+
+  DCHECK(IsShowingContextMenu());
+
+  // Notify the client.
+  CefRefPtr<CefClient> client = browser_->GetClient();
+  if (client.get()) {
+    CefRefPtr<CefContextMenuHandler> handler = client->GetContextMenuHandler();
+    if (handler.get()) {
+      handler->OnContextMenuDismissed(browser_, browser_->GetFocusedFrame());
+    }
+  }
+
+  // Notify the host after closing the context menu.
+  web_contents()->SetShowingContextMenu(false);
+  web_contents()->NotifyContextMenuClosed(params_.custom_context);
+}
+
+bool CefMenuManager::FormatLabel(CefRefPtr<CefMenuModelImpl> source,
+                                 base::string16& label) {
+  if (!runner_)
+    return false;
+  return runner_->FormatLabel(label);
+}
+
+void CefMenuManager::ExecuteCommandCallback(int command_id,
+                                            cef_event_flags_t event_flags) {
+  DCHECK(IsShowingContextMenu());
+  DCHECK(custom_menu_callback_);
+  if (command_id != kInvalidCommandId)
+    ExecuteCommand(model_, command_id, event_flags);
+  MenuClosed(model_);
+  custom_menu_callback_ = nullptr;
+}
+
+void CefMenuManager::CreateDefaultModel() {
+  if (!params_.custom_items.empty()) {
+    // Custom menu items originating from the renderer process. For example,
+    // plugin placeholder menu items or Flash menu items.
+    for (size_t i = 0; i < params_.custom_items.size(); ++i) {
+      content::MenuItem menu_item = params_.custom_items[i];
+      menu_item.action += MENU_ID_CUSTOM_FIRST;
+      DCHECK_LE(static_cast<int>(menu_item.action), MENU_ID_CUSTOM_LAST);
+      model_->AddMenuItem(menu_item);
+    }
+    return;
+  }
+
+  if (params_.is_editable) {
+    // Editable node.
+    model_->AddItem(MENU_ID_UNDO, GetLabel(IDS_CONTENT_CONTEXT_UNDO));
+    model_->AddItem(MENU_ID_REDO, GetLabel(IDS_CONTENT_CONTEXT_REDO));
+
+    model_->AddSeparator();
+    model_->AddItem(MENU_ID_CUT, GetLabel(IDS_CONTENT_CONTEXT_CUT));
+    model_->AddItem(MENU_ID_COPY, GetLabel(IDS_CONTENT_CONTEXT_COPY));
+    model_->AddItem(MENU_ID_PASTE, GetLabel(IDS_CONTENT_CONTEXT_PASTE));
+
+    model_->AddSeparator();
+    model_->AddItem(MENU_ID_SELECT_ALL,
+                    GetLabel(IDS_CONTENT_CONTEXT_SELECTALL));
+
+    if (!(params_.edit_flags & CM_EDITFLAG_CAN_UNDO))
+      model_->SetEnabled(MENU_ID_UNDO, false);
+    if (!(params_.edit_flags & CM_EDITFLAG_CAN_REDO))
+      model_->SetEnabled(MENU_ID_REDO, false);
+    if (!(params_.edit_flags & CM_EDITFLAG_CAN_CUT))
+      model_->SetEnabled(MENU_ID_CUT, false);
+    if (!(params_.edit_flags & CM_EDITFLAG_CAN_COPY))
+      model_->SetEnabled(MENU_ID_COPY, false);
+    if (!(params_.edit_flags & CM_EDITFLAG_CAN_PASTE))
+      model_->SetEnabled(MENU_ID_PASTE, false);
+    if (!(params_.edit_flags & CM_EDITFLAG_CAN_DELETE))
+      model_->SetEnabled(MENU_ID_DELETE, false);
+    if (!(params_.edit_flags & CM_EDITFLAG_CAN_SELECT_ALL))
+      model_->SetEnabled(MENU_ID_SELECT_ALL, false);
+
+    if (!params_.misspelled_word.empty()) {
+      // Always add a separator before the list of dictionary suggestions or
+      // "No spelling suggestions".
+      model_->AddSeparator();
+
+      if (!params_.dictionary_suggestions.empty()) {
+        for (size_t i = 0; i < params_.dictionary_suggestions.size() &&
+                           MENU_ID_SPELLCHECK_SUGGESTION_0 + i <=
+                               MENU_ID_SPELLCHECK_SUGGESTION_LAST;
+             ++i) {
+          model_->AddItem(MENU_ID_SPELLCHECK_SUGGESTION_0 + static_cast<int>(i),
+                          params_.dictionary_suggestions[i].c_str());
+        }
+
+        // When there are dictionary suggestions add a separator before "Add to
+        // dictionary".
+        model_->AddSeparator();
+      } else {
+        model_->AddItem(MENU_ID_NO_SPELLING_SUGGESTIONS,
+                        GetLabel(IDS_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS));
+        model_->SetEnabled(MENU_ID_NO_SPELLING_SUGGESTIONS, false);
+      }
+
+      model_->AddItem(MENU_ID_ADD_TO_DICTIONARY,
+                      GetLabel(IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY));
+    }
+  } else if (!params_.selection_text.empty()) {
+    // Something is selected.
+    model_->AddItem(MENU_ID_COPY, GetLabel(IDS_CONTENT_CONTEXT_COPY));
+  } else if (!params_.page_url.is_empty() || !params_.frame_url.is_empty()) {
+    // Page or frame.
+    model_->AddItem(MENU_ID_BACK, GetLabel(IDS_CONTENT_CONTEXT_BACK));
+    model_->AddItem(MENU_ID_FORWARD, GetLabel(IDS_CONTENT_CONTEXT_FORWARD));
+
+    model_->AddSeparator();
+    model_->AddItem(MENU_ID_PRINT, GetLabel(IDS_CONTENT_CONTEXT_PRINT));
+    model_->AddItem(MENU_ID_VIEW_SOURCE,
+                    GetLabel(IDS_CONTENT_CONTEXT_VIEWPAGESOURCE));
+
+    if (!browser_->CanGoBack())
+      model_->SetEnabled(MENU_ID_BACK, false);
+    if (!browser_->CanGoForward())
+      model_->SetEnabled(MENU_ID_FORWARD, false);
+  }
+}
+
+void CefMenuManager::ExecuteDefaultCommand(int command_id) {
+  if (IsCustomContextMenuCommand(command_id)) {
+    if (web_contents()) {
+      web_contents()->ExecuteCustomContextMenuCommand(
+          command_id - MENU_ID_CUSTOM_FIRST, params_.custom_context);
+    }
+    return;
+  }
+
+  // If the user chose a replacement word for a misspelling, replace it here.
+  if (command_id >= MENU_ID_SPELLCHECK_SUGGESTION_0 &&
+      command_id <= MENU_ID_SPELLCHECK_SUGGESTION_LAST) {
+    const size_t suggestion_index =
+        static_cast<size_t>(command_id) - MENU_ID_SPELLCHECK_SUGGESTION_0;
+    if (suggestion_index < params_.dictionary_suggestions.size()) {
+      browser_->ReplaceMisspelling(
+          params_.dictionary_suggestions[suggestion_index]);
+    }
+    return;
+  }
+
+  switch (command_id) {
+    // Navigation.
+    case MENU_ID_BACK:
+      browser_->GoBack();
+      break;
+    case MENU_ID_FORWARD:
+      browser_->GoForward();
+      break;
+    case MENU_ID_RELOAD:
+      browser_->Reload();
+      break;
+    case MENU_ID_RELOAD_NOCACHE:
+      browser_->ReloadIgnoreCache();
+      break;
+    case MENU_ID_STOPLOAD:
+      browser_->StopLoad();
+      break;
+
+    // Editing.
+    case MENU_ID_UNDO:
+      browser_->GetFocusedFrame()->Undo();
+      break;
+    case MENU_ID_REDO:
+      browser_->GetFocusedFrame()->Redo();
+      break;
+    case MENU_ID_CUT:
+      browser_->GetFocusedFrame()->Cut();
+      break;
+    case MENU_ID_COPY:
+      browser_->GetFocusedFrame()->Copy();
+      break;
+    case MENU_ID_PASTE:
+      browser_->GetFocusedFrame()->Paste();
+      break;
+    case MENU_ID_DELETE:
+      browser_->GetFocusedFrame()->Delete();
+      break;
+    case MENU_ID_SELECT_ALL:
+      browser_->GetFocusedFrame()->SelectAll();
+      break;
+
+    // Miscellaneous.
+    case MENU_ID_FIND:
+      // TODO(cef): Implement.
+      NOTIMPLEMENTED();
+      break;
+    case MENU_ID_PRINT:
+      browser_->Print();
+      break;
+    case MENU_ID_VIEW_SOURCE:
+      browser_->GetFocusedFrame()->ViewSource();
+      break;
+
+    // Spell checking.
+    case MENU_ID_ADD_TO_DICTIONARY:
+      browser_->GetHost()->AddWordToDictionary(params_.misspelled_word);
+      break;
+
+    default:
+      break;
+  }
+}
+
+bool CefMenuManager::IsCustomContextMenuCommand(int command_id) {
+  // Verify that the command ID is in the correct range.
+  if (command_id < MENU_ID_CUSTOM_FIRST || command_id > MENU_ID_CUSTOM_LAST)
+    return false;
+
+  command_id -= MENU_ID_CUSTOM_FIRST;
+
+  // Verify that the specific command ID was passed from the renderer process.
+  if (!params_.custom_items.empty()) {
+    for (size_t i = 0; i < params_.custom_items.size(); ++i) {
+      if (static_cast<int>(params_.custom_items[i].action) == command_id)
+        return true;
+    }
+  }
+  return false;
+}
diff --git a/src/libcef/browser/menu_manager.h b/src/libcef/browser/menu_manager.h
new file mode 100644
index 0000000..3661479
--- /dev/null
+++ b/src/libcef/browser/menu_manager.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MENU_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_MENU_MANAGER_H_
+#pragma once
+
+#include "libcef/browser/menu_model_impl.h"
+
+#include "libcef/browser/menu_runner.h"
+
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/context_menu_params.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+class RenderFrameHost;
+class WebContents;
+}  // namespace content
+
+class CefBrowserHostImpl;
+class CefRunContextMenuCallback;
+
+class CefMenuManager : public CefMenuModelImpl::Delegate,
+                       public content::WebContentsObserver {
+ public:
+  CefMenuManager(CefBrowserHostImpl* browser,
+                 std::unique_ptr<CefMenuRunner> runner);
+  ~CefMenuManager() override;
+
+  // Delete the runner to free any platform constructs.
+  void Destroy();
+
+  // Returns true if the context menu is currently showing.
+  bool IsShowingContextMenu();
+
+  // Create the context menu.
+  bool CreateContextMenu(const content::ContextMenuParams& params);
+  void CancelContextMenu();
+
+ private:
+  // CefMenuModelImpl::Delegate methods.
+  void ExecuteCommand(CefRefPtr<CefMenuModelImpl> source,
+                      int command_id,
+                      cef_event_flags_t event_flags) override;
+  void MenuWillShow(CefRefPtr<CefMenuModelImpl> source) override;
+  void MenuClosed(CefRefPtr<CefMenuModelImpl> source) override;
+  bool FormatLabel(CefRefPtr<CefMenuModelImpl> source,
+                   base::string16& label) override;
+
+  void ExecuteCommandCallback(int command_id, cef_event_flags_t event_flags);
+
+  // Create the default menu model.
+  void CreateDefaultModel();
+  // Execute the default command handling.
+  void ExecuteDefaultCommand(int command_id);
+
+  // Returns true if the specified id is a custom context menu command.
+  bool IsCustomContextMenuCommand(int command_id);
+
+  // CefBrowserHostImpl pointer is guaranteed to outlive this object.
+  CefBrowserHostImpl* browser_;
+
+  std::unique_ptr<CefMenuRunner> runner_;
+
+  CefRefPtr<CefMenuModelImpl> model_;
+  content::ContextMenuParams params_;
+
+  // Not owned by this class.
+  CefRunContextMenuCallback* custom_menu_callback_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<CefMenuManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefMenuManager);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MENU_MANAGER_H_
diff --git a/src/libcef/browser/menu_model_impl.cc b/src/libcef/browser/menu_model_impl.cc
new file mode 100644
index 0000000..9b18b0c
--- /dev/null
+++ b/src/libcef/browser/menu_model_impl.cc
@@ -0,0 +1,1031 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/menu_model_impl.h"
+
+#include <vector>
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/task_runner_impl.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "content/public/common/menu_item.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/gfx/geometry/point.h"
+
+namespace {
+
+const int kSeparatorId = -1;
+const int kInvalidGroupId = -1;
+const int kInvalidCommandId = -1;
+const int kDefaultIndex = -1;
+const int kInvalidIndex = -2;
+
+// A simple MenuModel implementation that delegates to CefMenuModelImpl.
+class CefSimpleMenuModel : public ui::MenuModel {
+ public:
+  // The Delegate can be NULL, though if it is items can't be checked or
+  // disabled.
+  explicit CefSimpleMenuModel(CefMenuModelImpl* impl) : impl_(impl) {}
+
+  // MenuModel methods.
+  bool HasIcons() const override { return false; }
+
+  int GetItemCount() const override { return impl_->GetCount(); }
+
+  ItemType GetTypeAt(int index) const override {
+    switch (impl_->GetTypeAt(index)) {
+      case MENUITEMTYPE_COMMAND:
+        return TYPE_COMMAND;
+      case MENUITEMTYPE_CHECK:
+        return TYPE_CHECK;
+      case MENUITEMTYPE_RADIO:
+        return TYPE_RADIO;
+      case MENUITEMTYPE_SEPARATOR:
+        return TYPE_SEPARATOR;
+      case MENUITEMTYPE_SUBMENU:
+        return TYPE_SUBMENU;
+      default:
+        NOTREACHED();
+        return TYPE_COMMAND;
+    }
+  }
+
+  ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override {
+    return ui::NORMAL_SEPARATOR;
+  }
+
+  int GetCommandIdAt(int index) const override {
+    return impl_->GetCommandIdAt(index);
+  }
+
+  base::string16 GetLabelAt(int index) const override {
+    return impl_->GetFormattedLabelAt(index);
+  }
+
+  bool IsItemDynamicAt(int index) const override { return false; }
+
+  const gfx::FontList* GetLabelFontListAt(int index) const override {
+    return impl_->GetLabelFontListAt(index);
+  }
+
+  bool GetAcceleratorAt(int index,
+                        ui::Accelerator* accelerator) const override {
+    int key_code = 0;
+    bool shift_pressed = false;
+    bool ctrl_pressed = false;
+    bool alt_pressed = false;
+    if (impl_->GetAcceleratorAt(index, key_code, shift_pressed, ctrl_pressed,
+                                alt_pressed)) {
+      int modifiers = 0;
+      if (shift_pressed)
+        modifiers |= ui::EF_SHIFT_DOWN;
+      if (ctrl_pressed)
+        modifiers |= ui::EF_CONTROL_DOWN;
+      if (alt_pressed)
+        modifiers |= ui::EF_ALT_DOWN;
+
+      *accelerator =
+          ui::Accelerator(static_cast<ui::KeyboardCode>(key_code), modifiers);
+      return true;
+    }
+    return false;
+  }
+
+  bool IsItemCheckedAt(int index) const override {
+    return impl_->IsCheckedAt(index);
+  }
+
+  int GetGroupIdAt(int index) const override {
+    return impl_->GetGroupIdAt(index);
+  }
+
+  bool GetIconAt(int index, gfx::Image* icon) const override { return false; }
+
+  ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override {
+    return nullptr;
+  }
+
+  bool IsEnabledAt(int index) const override {
+    return impl_->IsEnabledAt(index);
+  }
+
+  bool IsVisibleAt(int index) const override {
+    return impl_->IsVisibleAt(index);
+  }
+
+  void ActivatedAt(int index) override { ActivatedAt(index, 0); }
+
+  void ActivatedAt(int index, int event_flags) override {
+    impl_->ActivatedAt(index, static_cast<cef_event_flags_t>(event_flags));
+  }
+
+  MenuModel* GetSubmenuModelAt(int index) const override {
+    CefRefPtr<CefMenuModel> submenu = impl_->GetSubMenuAt(index);
+    if (submenu.get())
+      return static_cast<CefMenuModelImpl*>(submenu.get())->model();
+    return nullptr;
+  }
+
+  void MouseOutsideMenu(const gfx::Point& screen_point) override {
+    impl_->MouseOutsideMenu(screen_point);
+  }
+
+  void UnhandledOpenSubmenu(bool is_rtl) override {
+    impl_->UnhandledOpenSubmenu(is_rtl);
+  }
+
+  void UnhandledCloseSubmenu(bool is_rtl) override {
+    impl_->UnhandledCloseSubmenu(is_rtl);
+  }
+
+  bool GetTextColor(int index,
+                    bool is_minor,
+                    bool is_hovered,
+                    SkColor* override_color) const override {
+    return impl_->GetTextColor(index, is_minor, is_hovered, override_color);
+  }
+
+  bool GetBackgroundColor(int index,
+                          bool is_hovered,
+                          SkColor* override_color) const override {
+    return impl_->GetBackgroundColor(index, is_hovered, override_color);
+  }
+
+  void MenuWillShow() override { impl_->MenuWillShow(); }
+
+  void MenuWillClose() override { impl_->MenuWillClose(); }
+
+ private:
+  CefMenuModelImpl* impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefSimpleMenuModel);
+};
+
+cef_menu_color_type_t GetMenuColorType(bool is_text,
+                                       bool is_accelerator,
+                                       bool is_hovered) {
+  if (is_text) {
+    if (is_accelerator) {
+      return is_hovered ? CEF_MENU_COLOR_TEXT_ACCELERATOR_HOVERED
+                        : CEF_MENU_COLOR_TEXT_ACCELERATOR;
+    }
+    return is_hovered ? CEF_MENU_COLOR_TEXT_HOVERED : CEF_MENU_COLOR_TEXT;
+  }
+
+  DCHECK(!is_accelerator);
+  return is_hovered ? CEF_MENU_COLOR_BACKGROUND_HOVERED
+                    : CEF_MENU_COLOR_BACKGROUND;
+}
+
+}  // namespace
+
+// static
+CefRefPtr<CefMenuModel> CefMenuModel::CreateMenuModel(
+    CefRefPtr<CefMenuModelDelegate> delegate) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  DCHECK(delegate);
+  if (!delegate)
+    return nullptr;
+
+  CefRefPtr<CefMenuModelImpl> menu_model =
+      new CefMenuModelImpl(nullptr, delegate, false);
+  return menu_model;
+}
+
+struct CefMenuModelImpl::Item {
+  Item(cef_menu_item_type_t type,
+       int command_id,
+       const CefString& label,
+       int group_id)
+      : type_(type),
+        command_id_(command_id),
+        label_(label),
+        group_id_(group_id) {}
+
+  // Basic information.
+  cef_menu_item_type_t type_;
+  int command_id_;
+  CefString label_;
+  int group_id_;
+  CefRefPtr<CefMenuModelImpl> submenu_;
+
+  // State information.
+  bool enabled_ = true;
+  bool visible_ = true;
+  bool checked_ = false;
+
+  // Accelerator information.
+  bool has_accelerator_ = false;
+  int key_code_ = 0;
+  bool shift_pressed_ = false;
+  bool ctrl_pressed_ = false;
+  bool alt_pressed_ = false;
+
+  cef_color_t colors_[CEF_MENU_COLOR_COUNT] = {0};
+  gfx::FontList font_list_;
+  bool has_font_list_ = false;
+};
+
+CefMenuModelImpl::CefMenuModelImpl(
+    Delegate* delegate,
+    CefRefPtr<CefMenuModelDelegate> menu_model_delegate,
+    bool is_submenu)
+    : supported_thread_id_(base::PlatformThread::CurrentId()),
+      delegate_(delegate),
+      menu_model_delegate_(menu_model_delegate),
+      is_submenu_(is_submenu) {
+  DCHECK(delegate_ || menu_model_delegate_);
+  model_.reset(new CefSimpleMenuModel(this));
+}
+
+CefMenuModelImpl::~CefMenuModelImpl() {}
+
+bool CefMenuModelImpl::IsSubMenu() {
+  if (!VerifyContext())
+    return false;
+  return is_submenu_;
+}
+
+bool CefMenuModelImpl::Clear() {
+  if (!VerifyContext())
+    return false;
+
+  items_.clear();
+  return true;
+}
+
+int CefMenuModelImpl::GetCount() {
+  if (!VerifyContext())
+    return 0;
+
+  return static_cast<int>(items_.size());
+}
+
+bool CefMenuModelImpl::AddSeparator() {
+  if (!VerifyContext())
+    return false;
+
+  AppendItem(
+      Item(MENUITEMTYPE_SEPARATOR, kSeparatorId, CefString(), kInvalidGroupId));
+  return true;
+}
+
+bool CefMenuModelImpl::AddItem(int command_id, const CefString& label) {
+  if (!VerifyContext())
+    return false;
+
+  AppendItem(Item(MENUITEMTYPE_COMMAND, command_id, label, kInvalidGroupId));
+  return true;
+}
+
+bool CefMenuModelImpl::AddCheckItem(int command_id, const CefString& label) {
+  if (!VerifyContext())
+    return false;
+
+  AppendItem(Item(MENUITEMTYPE_CHECK, command_id, label, kInvalidGroupId));
+  return true;
+}
+
+bool CefMenuModelImpl::AddRadioItem(int command_id,
+                                    const CefString& label,
+                                    int group_id) {
+  if (!VerifyContext())
+    return false;
+
+  AppendItem(Item(MENUITEMTYPE_RADIO, command_id, label, group_id));
+  return true;
+}
+
+CefRefPtr<CefMenuModel> CefMenuModelImpl::AddSubMenu(int command_id,
+                                                     const CefString& label) {
+  if (!VerifyContext())
+    return nullptr;
+
+  Item item(MENUITEMTYPE_SUBMENU, command_id, label, kInvalidGroupId);
+  item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_, true);
+  AppendItem(item);
+  return item.submenu_.get();
+}
+
+bool CefMenuModelImpl::InsertSeparatorAt(int index) {
+  if (!VerifyContext())
+    return false;
+
+  InsertItemAt(
+      Item(MENUITEMTYPE_SEPARATOR, kSeparatorId, CefString(), kInvalidGroupId),
+      index);
+  return true;
+}
+
+bool CefMenuModelImpl::InsertItemAt(int index,
+                                    int command_id,
+                                    const CefString& label) {
+  if (!VerifyContext())
+    return false;
+
+  InsertItemAt(Item(MENUITEMTYPE_COMMAND, command_id, label, kInvalidGroupId),
+               index);
+  return true;
+}
+
+bool CefMenuModelImpl::InsertCheckItemAt(int index,
+                                         int command_id,
+                                         const CefString& label) {
+  if (!VerifyContext())
+    return false;
+
+  InsertItemAt(Item(MENUITEMTYPE_CHECK, command_id, label, kInvalidGroupId),
+               index);
+  return true;
+}
+
+bool CefMenuModelImpl::InsertRadioItemAt(int index,
+                                         int command_id,
+                                         const CefString& label,
+                                         int group_id) {
+  if (!VerifyContext())
+    return false;
+
+  InsertItemAt(Item(MENUITEMTYPE_RADIO, command_id, label, group_id), index);
+  return true;
+}
+
+CefRefPtr<CefMenuModel> CefMenuModelImpl::InsertSubMenuAt(
+    int index,
+    int command_id,
+    const CefString& label) {
+  if (!VerifyContext())
+    return nullptr;
+
+  Item item(MENUITEMTYPE_SUBMENU, command_id, label, kInvalidGroupId);
+  item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_, true);
+  InsertItemAt(item, index);
+  return item.submenu_.get();
+}
+
+bool CefMenuModelImpl::Remove(int command_id) {
+  return RemoveAt(GetIndexOf(command_id));
+}
+
+bool CefMenuModelImpl::RemoveAt(int index) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    items_.erase(items_.begin() + index);
+    return true;
+  }
+  return false;
+}
+
+int CefMenuModelImpl::GetIndexOf(int command_id) {
+  if (!VerifyContext())
+    return kInvalidIndex;
+
+  for (ItemVector::iterator i = items_.begin(); i != items_.end(); ++i) {
+    if ((*i).command_id_ == command_id) {
+      return static_cast<int>(std::distance(items_.begin(), i));
+    }
+  }
+  return kInvalidIndex;
+}
+
+int CefMenuModelImpl::GetCommandIdAt(int index) {
+  if (!VerifyContext())
+    return kInvalidCommandId;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].command_id_;
+  return kInvalidCommandId;
+}
+
+bool CefMenuModelImpl::SetCommandIdAt(int index, int command_id) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    items_[index].command_id_ = command_id;
+    return true;
+  }
+  return false;
+}
+
+CefString CefMenuModelImpl::GetLabel(int command_id) {
+  return GetLabelAt(GetIndexOf(command_id));
+}
+
+CefString CefMenuModelImpl::GetLabelAt(int index) {
+  if (!VerifyContext())
+    return CefString();
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].label_;
+  return CefString();
+}
+
+bool CefMenuModelImpl::SetLabel(int command_id, const CefString& label) {
+  return SetLabelAt(GetIndexOf(command_id), label);
+}
+
+bool CefMenuModelImpl::SetLabelAt(int index, const CefString& label) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    items_[index].label_ = label;
+    return true;
+  }
+  return false;
+}
+
+CefMenuModelImpl::MenuItemType CefMenuModelImpl::GetType(int command_id) {
+  return GetTypeAt(GetIndexOf(command_id));
+}
+
+CefMenuModelImpl::MenuItemType CefMenuModelImpl::GetTypeAt(int index) {
+  if (!VerifyContext())
+    return MENUITEMTYPE_NONE;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].type_;
+  return MENUITEMTYPE_NONE;
+}
+
+int CefMenuModelImpl::GetGroupId(int command_id) {
+  return GetGroupIdAt(GetIndexOf(command_id));
+}
+
+int CefMenuModelImpl::GetGroupIdAt(int index) {
+  if (!VerifyContext())
+    return kInvalidGroupId;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].group_id_;
+  return kInvalidGroupId;
+}
+
+bool CefMenuModelImpl::SetGroupId(int command_id, int group_id) {
+  return SetGroupIdAt(GetIndexOf(command_id), group_id);
+}
+
+bool CefMenuModelImpl::SetGroupIdAt(int index, int group_id) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    items_[index].group_id_ = group_id;
+    return true;
+  }
+  return false;
+}
+
+CefRefPtr<CefMenuModel> CefMenuModelImpl::GetSubMenu(int command_id) {
+  return GetSubMenuAt(GetIndexOf(command_id));
+}
+
+CefRefPtr<CefMenuModel> CefMenuModelImpl::GetSubMenuAt(int index) {
+  if (!VerifyContext())
+    return nullptr;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].submenu_.get();
+  return nullptr;
+}
+
+bool CefMenuModelImpl::IsVisible(int command_id) {
+  return IsVisibleAt(GetIndexOf(command_id));
+}
+
+bool CefMenuModelImpl::IsVisibleAt(int index) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].visible_;
+  return false;
+}
+
+bool CefMenuModelImpl::SetVisible(int command_id, bool visible) {
+  return SetVisibleAt(GetIndexOf(command_id), visible);
+}
+
+bool CefMenuModelImpl::SetVisibleAt(int index, bool visible) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    items_[index].visible_ = visible;
+    return true;
+  }
+  return false;
+}
+
+bool CefMenuModelImpl::IsEnabled(int command_id) {
+  return IsEnabledAt(GetIndexOf(command_id));
+}
+
+bool CefMenuModelImpl::IsEnabledAt(int index) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].enabled_;
+  return false;
+}
+
+bool CefMenuModelImpl::SetEnabled(int command_id, bool enabled) {
+  return SetEnabledAt(GetIndexOf(command_id), enabled);
+}
+
+bool CefMenuModelImpl::SetEnabledAt(int index, bool enabled) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    items_[index].enabled_ = enabled;
+    return true;
+  }
+  return false;
+}
+
+bool CefMenuModelImpl::IsChecked(int command_id) {
+  return IsCheckedAt(GetIndexOf(command_id));
+}
+
+bool CefMenuModelImpl::IsCheckedAt(int index) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].checked_;
+  return false;
+}
+
+bool CefMenuModelImpl::SetChecked(int command_id, bool checked) {
+  return SetCheckedAt(GetIndexOf(command_id), checked);
+}
+
+bool CefMenuModelImpl::SetCheckedAt(int index, bool checked) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    items_[index].checked_ = checked;
+    return true;
+  }
+  return false;
+}
+
+bool CefMenuModelImpl::HasAccelerator(int command_id) {
+  return HasAcceleratorAt(GetIndexOf(command_id));
+}
+
+bool CefMenuModelImpl::HasAcceleratorAt(int index) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size()))
+    return items_[index].has_accelerator_;
+  return false;
+}
+
+bool CefMenuModelImpl::SetAccelerator(int command_id,
+                                      int key_code,
+                                      bool shift_pressed,
+                                      bool ctrl_pressed,
+                                      bool alt_pressed) {
+  return SetAcceleratorAt(GetIndexOf(command_id), key_code, shift_pressed,
+                          ctrl_pressed, alt_pressed);
+}
+
+bool CefMenuModelImpl::SetAcceleratorAt(int index,
+                                        int key_code,
+                                        bool shift_pressed,
+                                        bool ctrl_pressed,
+                                        bool alt_pressed) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    Item& item = items_[index];
+    item.has_accelerator_ = true;
+    item.key_code_ = key_code;
+    item.shift_pressed_ = shift_pressed;
+    item.ctrl_pressed_ = ctrl_pressed;
+    item.alt_pressed_ = alt_pressed;
+    return true;
+  }
+  return false;
+}
+
+bool CefMenuModelImpl::RemoveAccelerator(int command_id) {
+  return RemoveAcceleratorAt(GetIndexOf(command_id));
+}
+
+bool CefMenuModelImpl::RemoveAcceleratorAt(int index) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    Item& item = items_[index];
+    if (item.has_accelerator_) {
+      item.has_accelerator_ = false;
+      item.key_code_ = 0;
+      item.shift_pressed_ = false;
+      item.ctrl_pressed_ = false;
+      item.alt_pressed_ = false;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool CefMenuModelImpl::GetAccelerator(int command_id,
+                                      int& key_code,
+                                      bool& shift_pressed,
+                                      bool& ctrl_pressed,
+                                      bool& alt_pressed) {
+  return GetAcceleratorAt(GetIndexOf(command_id), key_code, shift_pressed,
+                          ctrl_pressed, alt_pressed);
+}
+
+bool CefMenuModelImpl::GetAcceleratorAt(int index,
+                                        int& key_code,
+                                        bool& shift_pressed,
+                                        bool& ctrl_pressed,
+                                        bool& alt_pressed) {
+  if (!VerifyContext())
+    return false;
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    const Item& item = items_[index];
+    if (item.has_accelerator_) {
+      key_code = item.key_code_;
+      shift_pressed = item.shift_pressed_;
+      ctrl_pressed = item.ctrl_pressed_;
+      alt_pressed = item.alt_pressed_;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CefMenuModelImpl::SetColor(int command_id,
+                                cef_menu_color_type_t color_type,
+                                cef_color_t color) {
+  return SetColorAt(GetIndexOf(command_id), color_type, color);
+}
+
+bool CefMenuModelImpl::SetColorAt(int index,
+                                  cef_menu_color_type_t color_type,
+                                  cef_color_t color) {
+  if (!VerifyContext())
+    return false;
+
+  if (color_type < 0 || color_type >= CEF_MENU_COLOR_COUNT)
+    return false;
+
+  if (index == kDefaultIndex) {
+    default_colors_[color_type] = color;
+    return true;
+  }
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    Item& item = items_[index];
+    item.colors_[color_type] = color;
+    return true;
+  }
+
+  return false;
+}
+
+bool CefMenuModelImpl::GetColor(int command_id,
+                                cef_menu_color_type_t color_type,
+                                cef_color_t& color) {
+  return GetColorAt(GetIndexOf(command_id), color_type, color);
+}
+
+bool CefMenuModelImpl::GetColorAt(int index,
+                                  cef_menu_color_type_t color_type,
+                                  cef_color_t& color) {
+  if (!VerifyContext())
+    return false;
+
+  if (color_type < 0 || color_type >= CEF_MENU_COLOR_COUNT)
+    return false;
+
+  if (index == kDefaultIndex) {
+    color = default_colors_[color_type];
+    return true;
+  }
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    Item& item = items_[index];
+    color = item.colors_[color_type];
+    return true;
+  }
+
+  return false;
+}
+
+bool CefMenuModelImpl::SetFontList(int command_id, const CefString& font_list) {
+  return SetFontListAt(GetIndexOf(command_id), font_list);
+}
+
+bool CefMenuModelImpl::SetFontListAt(int index, const CefString& font_list) {
+  if (!VerifyContext())
+    return false;
+
+  if (index == kDefaultIndex) {
+    if (font_list.empty()) {
+      has_default_font_list_ = false;
+    } else {
+      default_font_list_ = gfx::FontList(font_list);
+      has_default_font_list_ = true;
+    }
+    return true;
+  }
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    Item& item = items_[index];
+    if (font_list.empty()) {
+      item.has_font_list_ = false;
+    } else {
+      item.font_list_ = gfx::FontList(font_list);
+      item.has_font_list_ = true;
+    }
+    return true;
+  }
+  return false;
+}
+
+void CefMenuModelImpl::ActivatedAt(int index, cef_event_flags_t event_flags) {
+  if (!VerifyContext())
+    return;
+
+  const int command_id = GetCommandIdAt(index);
+  if (delegate_)
+    delegate_->ExecuteCommand(this, command_id, event_flags);
+  if (menu_model_delegate_)
+    menu_model_delegate_->ExecuteCommand(this, command_id, event_flags);
+}
+
+void CefMenuModelImpl::MouseOutsideMenu(const gfx::Point& screen_point) {
+  if (!VerifyContext())
+    return;
+
+  // Allow the callstack to unwind before notifying the delegate since it may
+  // result in the menu being destroyed.
+  CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
+      FROM_HERE,
+      base::Bind(&CefMenuModelImpl::OnMouseOutsideMenu, this, screen_point));
+}
+
+void CefMenuModelImpl::UnhandledOpenSubmenu(bool is_rtl) {
+  if (!VerifyContext())
+    return;
+
+  // Allow the callstack to unwind before notifying the delegate since it may
+  // result in the menu being destroyed.
+  CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
+      FROM_HERE,
+      base::Bind(&CefMenuModelImpl::OnUnhandledOpenSubmenu, this, is_rtl));
+}
+
+void CefMenuModelImpl::UnhandledCloseSubmenu(bool is_rtl) {
+  if (!VerifyContext())
+    return;
+
+  // Allow the callstack to unwind before notifying the delegate since it may
+  // result in the menu being destroyed.
+  CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
+      FROM_HERE,
+      base::Bind(&CefMenuModelImpl::OnUnhandledCloseSubmenu, this, is_rtl));
+}
+
+bool CefMenuModelImpl::GetTextColor(int index,
+                                    bool is_accelerator,
+                                    bool is_hovered,
+                                    SkColor* override_color) const {
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    const Item& item = items_[index];
+    if (!item.enabled_) {
+      // Use accelerator color for disabled item text.
+      is_accelerator = true;
+    }
+
+    const cef_menu_color_type_t color_type =
+        GetMenuColorType(true, is_accelerator, is_hovered);
+    if (item.colors_[color_type] != 0) {
+      *override_color = item.colors_[color_type];
+      return true;
+    }
+  }
+
+  const cef_menu_color_type_t color_type =
+      GetMenuColorType(true, is_accelerator, is_hovered);
+  if (default_colors_[color_type] != 0) {
+    *override_color = default_colors_[color_type];
+    return true;
+  }
+
+  return false;
+}
+
+bool CefMenuModelImpl::GetBackgroundColor(int index,
+                                          bool is_hovered,
+                                          SkColor* override_color) const {
+  const cef_menu_color_type_t color_type =
+      GetMenuColorType(false, false, is_hovered);
+
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    const Item& item = items_[index];
+    if (item.colors_[color_type] != 0) {
+      *override_color = item.colors_[color_type];
+      return true;
+    }
+  }
+
+  if (default_colors_[color_type] != 0) {
+    *override_color = default_colors_[color_type];
+    return true;
+  }
+
+  return false;
+}
+
+void CefMenuModelImpl::MenuWillShow() {
+  if (!VerifyContext())
+    return;
+
+  if (delegate_)
+    delegate_->MenuWillShow(this);
+  if (menu_model_delegate_)
+    menu_model_delegate_->MenuWillShow(this);
+}
+
+void CefMenuModelImpl::MenuWillClose() {
+  if (!VerifyContext())
+    return;
+
+  if (!auto_notify_menu_closed_)
+    return;
+
+  // Due to how menus work on the different platforms, ActivatedAt will be
+  // called after this.  It's more convenient for the delegate to be called
+  // afterwards, though, so post a task.
+  CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
+      FROM_HERE, base::Bind(&CefMenuModelImpl::OnMenuClosed, this));
+}
+
+base::string16 CefMenuModelImpl::GetFormattedLabelAt(int index) {
+  base::string16 label = GetLabelAt(index).ToString16();
+  if (delegate_)
+    delegate_->FormatLabel(this, label);
+  if (menu_model_delegate_) {
+    CefString new_label = label;
+    if (menu_model_delegate_->FormatLabel(this, new_label))
+      label = new_label;
+  }
+  return label;
+}
+
+const gfx::FontList* CefMenuModelImpl::GetLabelFontListAt(int index) const {
+  if (index >= 0 && index < static_cast<int>(items_.size())) {
+    const Item& item = items_[index];
+    if (item.has_font_list_)
+      return &item.font_list_;
+  }
+
+  if (has_default_font_list_)
+    return &default_font_list_;
+  return nullptr;
+}
+
+bool CefMenuModelImpl::VerifyRefCount() {
+  if (!VerifyContext())
+    return false;
+
+  if (!HasOneRef())
+    return false;
+
+  for (ItemVector::iterator i = items_.begin(); i != items_.end(); ++i) {
+    if ((*i).submenu_.get()) {
+      if (!(*i).submenu_->VerifyRefCount())
+        return false;
+    }
+  }
+
+  return true;
+}
+
+void CefMenuModelImpl::AddMenuItem(const content::MenuItem& menu_item) {
+  const int command_id = static_cast<int>(menu_item.action);
+
+  switch (menu_item.type) {
+    case content::MenuItem::OPTION:
+      AddItem(command_id, menu_item.label);
+      break;
+    case content::MenuItem::CHECKABLE_OPTION:
+      AddCheckItem(command_id, menu_item.label);
+      break;
+    case content::MenuItem::GROUP:
+      AddRadioItem(command_id, menu_item.label, 0);
+      break;
+    case content::MenuItem::SEPARATOR:
+      AddSeparator();
+      break;
+    case content::MenuItem::SUBMENU: {
+      CefRefPtr<CefMenuModelImpl> sub_menu = static_cast<CefMenuModelImpl*>(
+          AddSubMenu(command_id, menu_item.label).get());
+      for (size_t i = 0; i < menu_item.submenu.size(); ++i)
+        sub_menu->AddMenuItem(menu_item.submenu[i]);
+      break;
+    }
+  }
+
+  if (!menu_item.enabled && menu_item.type != content::MenuItem::SEPARATOR)
+    SetEnabled(command_id, false);
+
+  if (menu_item.checked &&
+      (menu_item.type == content::MenuItem::CHECKABLE_OPTION ||
+       menu_item.type == content::MenuItem::GROUP)) {
+    SetChecked(command_id, true);
+  }
+}
+
+void CefMenuModelImpl::NotifyMenuClosed() {
+  DCHECK(!auto_notify_menu_closed_);
+  OnMenuClosed();
+}
+
+void CefMenuModelImpl::AppendItem(const Item& item) {
+  ValidateItem(item);
+  items_.push_back(item);
+}
+
+void CefMenuModelImpl::InsertItemAt(const Item& item, int index) {
+  // Sanitize the index.
+  if (index < 0)
+    index = 0;
+  else if (index > static_cast<int>(items_.size()))
+    index = items_.size();
+
+  ValidateItem(item);
+  items_.insert(items_.begin() + index, item);
+}
+
+void CefMenuModelImpl::ValidateItem(const Item& item) {
+#if DCHECK_IS_ON()
+  if (item.type_ == MENUITEMTYPE_SEPARATOR) {
+    DCHECK_EQ(item.command_id_, kSeparatorId);
+  } else {
+    DCHECK_GE(item.command_id_, 0);
+  }
+#endif
+}
+
+void CefMenuModelImpl::OnMouseOutsideMenu(const gfx::Point& screen_point) {
+  if (delegate_)
+    delegate_->MouseOutsideMenu(this, screen_point);
+  if (menu_model_delegate_) {
+    menu_model_delegate_->MouseOutsideMenu(
+        this, CefPoint(screen_point.x(), screen_point.y()));
+  }
+}
+
+void CefMenuModelImpl::OnUnhandledOpenSubmenu(bool is_rtl) {
+  if (delegate_)
+    delegate_->UnhandledOpenSubmenu(this, is_rtl);
+  if (menu_model_delegate_)
+    menu_model_delegate_->UnhandledOpenSubmenu(this, is_rtl);
+}
+
+void CefMenuModelImpl::OnUnhandledCloseSubmenu(bool is_rtl) {
+  if (delegate_)
+    delegate_->UnhandledCloseSubmenu(this, is_rtl);
+  if (menu_model_delegate_)
+    menu_model_delegate_->UnhandledCloseSubmenu(this, is_rtl);
+}
+
+void CefMenuModelImpl::OnMenuClosed() {
+  if (delegate_)
+    delegate_->MenuClosed(this);
+  if (menu_model_delegate_)
+    menu_model_delegate_->MenuClosed(this);
+}
+
+bool CefMenuModelImpl::VerifyContext() {
+  if (base::PlatformThread::CurrentId() != supported_thread_id_) {
+    // This object should only be accessed from the thread that created it.
+    NOTREACHED();
+    return false;
+  }
+
+  return true;
+}
diff --git a/src/libcef/browser/menu_model_impl.h b/src/libcef/browser/menu_model_impl.h
new file mode 100644
index 0000000..fa44fbb
--- /dev/null
+++ b/src/libcef/browser/menu_model_impl.h
@@ -0,0 +1,235 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MENU_MODEL_IMPL_H_
+#define CEF_LIBCEF_BROWSER_MENU_MODEL_IMPL_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_menu_model.h"
+#include "include/cef_menu_model_delegate.h"
+
+#include "base/threading/platform_thread.h"
+#include "ui/base/models/menu_model.h"
+#include "ui/gfx/font_list.h"
+
+namespace content {
+struct MenuItem;
+}
+
+class CefMenuModelImpl : public CefMenuModel {
+ public:
+  class Delegate {
+   public:
+    // Perform the action associated with the specified |command_id| and
+    // optional |event_flags|.
+    virtual void ExecuteCommand(CefRefPtr<CefMenuModelImpl> source,
+                                int command_id,
+                                cef_event_flags_t event_flags) = 0;
+
+    // Called when the user moves the mouse outside the menu and over the owning
+    // window.
+    virtual void MouseOutsideMenu(CefRefPtr<CefMenuModelImpl> source,
+                                  const gfx::Point& screen_point) {}
+
+    // Called on unhandled open/close submenu keyboard commands. |is_rtl| will
+    // be true if the menu is displaying a right-to-left language.
+    virtual void UnhandledOpenSubmenu(CefRefPtr<CefMenuModelImpl> source,
+                                      bool is_rtl) {}
+    virtual void UnhandledCloseSubmenu(CefRefPtr<CefMenuModelImpl> source,
+                                       bool is_rtl) {}
+
+    // Called when the menu is about to show.
+    virtual void MenuWillShow(CefRefPtr<CefMenuModelImpl> source) = 0;
+
+    // Called when the menu has closed.
+    virtual void MenuClosed(CefRefPtr<CefMenuModelImpl> source) = 0;
+
+    // Allows the delegate to modify a menu item label before it's displayed.
+    virtual bool FormatLabel(CefRefPtr<CefMenuModelImpl> source,
+                             base::string16& label) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // Either |delegate| or |menu_model_delegate| must be non-nullptr.
+  // If |delegate| is non-nullptr it must outlive this class.
+  CefMenuModelImpl(Delegate* delegate,
+                   CefRefPtr<CefMenuModelDelegate> menu_model_delegate,
+                   bool is_submenu);
+  ~CefMenuModelImpl() override;
+
+  // CefMenuModel methods.
+  bool IsSubMenu() override;
+  bool Clear() override;
+  int GetCount() override;
+  bool AddSeparator() override;
+  bool AddItem(int command_id, const CefString& label) override;
+  bool AddCheckItem(int command_id, const CefString& label) override;
+  bool AddRadioItem(int command_id,
+                    const CefString& label,
+                    int group_id) override;
+  CefRefPtr<CefMenuModel> AddSubMenu(int command_id,
+                                     const CefString& label) override;
+  bool InsertSeparatorAt(int index) override;
+  bool InsertItemAt(int index, int command_id, const CefString& label) override;
+  bool InsertCheckItemAt(int index,
+                         int command_id,
+                         const CefString& label) override;
+  bool InsertRadioItemAt(int index,
+                         int command_id,
+                         const CefString& label,
+                         int group_id) override;
+  CefRefPtr<CefMenuModel> InsertSubMenuAt(int index,
+                                          int command_id,
+                                          const CefString& label) override;
+  bool Remove(int command_id) override;
+  bool RemoveAt(int index) override;
+  int GetIndexOf(int command_id) override;
+  int GetCommandIdAt(int index) override;
+  bool SetCommandIdAt(int index, int command_id) override;
+  CefString GetLabel(int command_id) override;
+  CefString GetLabelAt(int index) override;
+  bool SetLabel(int command_id, const CefString& label) override;
+  bool SetLabelAt(int index, const CefString& label) override;
+  MenuItemType GetType(int command_id) override;
+  MenuItemType GetTypeAt(int index) override;
+  int GetGroupId(int command_id) override;
+  int GetGroupIdAt(int index) override;
+  bool SetGroupId(int command_id, int group_id) override;
+  bool SetGroupIdAt(int index, int group_id) override;
+  CefRefPtr<CefMenuModel> GetSubMenu(int command_id) override;
+  CefRefPtr<CefMenuModel> GetSubMenuAt(int index) override;
+  bool IsVisible(int command_id) override;
+  bool IsVisibleAt(int index) override;
+  bool SetVisible(int command_id, bool visible) override;
+  bool SetVisibleAt(int index, bool visible) override;
+  bool IsEnabled(int command_id) override;
+  bool IsEnabledAt(int index) override;
+  bool SetEnabled(int command_id, bool enabled) override;
+  bool SetEnabledAt(int index, bool enabled) override;
+  bool IsChecked(int command_id) override;
+  bool IsCheckedAt(int index) override;
+  bool SetChecked(int command_id, bool checked) override;
+  bool SetCheckedAt(int index, bool checked) override;
+  bool HasAccelerator(int command_id) override;
+  bool HasAcceleratorAt(int index) override;
+  bool SetAccelerator(int command_id,
+                      int key_code,
+                      bool shift_pressed,
+                      bool ctrl_pressed,
+                      bool alt_pressed) override;
+  bool SetAcceleratorAt(int index,
+                        int key_code,
+                        bool shift_pressed,
+                        bool ctrl_pressed,
+                        bool alt_pressed) override;
+  bool RemoveAccelerator(int command_id) override;
+  bool RemoveAcceleratorAt(int index) override;
+  bool GetAccelerator(int command_id,
+                      int& key_code,
+                      bool& shift_pressed,
+                      bool& ctrl_pressed,
+                      bool& alt_pressed) override;
+  bool GetAcceleratorAt(int index,
+                        int& key_code,
+                        bool& shift_pressed,
+                        bool& ctrl_pressed,
+                        bool& alt_pressed) override;
+  bool SetColor(int command_id,
+                cef_menu_color_type_t color_type,
+                cef_color_t color) override;
+  bool SetColorAt(int index,
+                  cef_menu_color_type_t color_type,
+                  cef_color_t color) override;
+  bool GetColor(int command_id,
+                cef_menu_color_type_t color_type,
+                cef_color_t& color) override;
+  bool GetColorAt(int index,
+                  cef_menu_color_type_t color_type,
+                  cef_color_t& color) override;
+  bool SetFontList(int command_id, const CefString& font_list) override;
+  bool SetFontListAt(int index, const CefString& font_list) override;
+
+  // Callbacks from the ui::MenuModel implementation.
+  void ActivatedAt(int index, cef_event_flags_t event_flags);
+  void MouseOutsideMenu(const gfx::Point& screen_point);
+  void UnhandledOpenSubmenu(bool is_rtl);
+  void UnhandledCloseSubmenu(bool is_rtl);
+  bool GetTextColor(int index,
+                    bool is_accelerator,
+                    bool is_hovered,
+                    SkColor* override_color) const;
+  bool GetBackgroundColor(int index,
+                          bool is_hovered,
+                          SkColor* override_color) const;
+  void MenuWillShow();
+  void MenuWillClose();
+  base::string16 GetFormattedLabelAt(int index);
+  const gfx::FontList* GetLabelFontListAt(int index) const;
+
+  // Verify that only a single reference exists to all CefMenuModelImpl objects.
+  bool VerifyRefCount();
+
+  // Helper for adding custom menu items originating from the renderer process.
+  void AddMenuItem(const content::MenuItem& menu_item);
+
+  ui::MenuModel* model() const { return model_.get(); }
+
+  // Used when created via CefMenuManager.
+  Delegate* delegate() const { return delegate_; }
+  void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+  // Used for menus run via CefWindowImpl::ShowMenu to provide more accurate
+  // menu close notification.
+  void set_auto_notify_menu_closed(bool val) { auto_notify_menu_closed_ = val; }
+  void NotifyMenuClosed();
+
+ private:
+  struct Item;
+
+  typedef std::vector<Item> ItemVector;
+
+  // Functions for inserting items into |items_|.
+  void AppendItem(const Item& item);
+  void InsertItemAt(const Item& item, int index);
+  void ValidateItem(const Item& item);
+
+  // Notify the delegate asynchronously.
+  void OnMouseOutsideMenu(const gfx::Point& screen_point);
+  void OnUnhandledOpenSubmenu(bool is_rtl);
+  void OnUnhandledCloseSubmenu(bool is_rtl);
+  void OnMenuClosed();
+
+  // Verify that the object is being accessed from the correct thread.
+  bool VerifyContext();
+
+  base::PlatformThreadId supported_thread_id_;
+
+  // Used when created via CefMenuManager.
+  Delegate* delegate_;
+
+  // Used when created via CefMenuModel::CreateMenuModel().
+  CefRefPtr<CefMenuModelDelegate> menu_model_delegate_;
+
+  const bool is_submenu_;
+
+  ItemVector items_;
+  std::unique_ptr<ui::MenuModel> model_;
+
+  // Style information.
+  cef_color_t default_colors_[CEF_MENU_COLOR_COUNT] = {0};
+  gfx::FontList default_font_list_;
+  bool has_default_font_list_ = false;
+
+  bool auto_notify_menu_closed_ = true;
+
+  IMPLEMENT_REFCOUNTING(CefMenuModelImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefMenuModelImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MENU_MODEL_IMPL_H_
diff --git a/src/libcef/browser/menu_runner.h b/src/libcef/browser/menu_runner.h
new file mode 100644
index 0000000..6bb0e10
--- /dev/null
+++ b/src/libcef/browser/menu_runner.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_MENU_RUNNER_H_
+#define CEF_LIBCEF_BROWSER_MENU_RUNNER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+
+namespace content {
+struct ContextMenuParams;
+}
+
+class CefBrowserHostImpl;
+class CefMenuModelImpl;
+
+// Provides platform-specific menu implementations for CefMenuCreator.
+class CefMenuRunner {
+ public:
+  virtual bool RunContextMenu(CefBrowserHostImpl* browser,
+                              CefMenuModelImpl* model,
+                              const content::ContextMenuParams& params) = 0;
+  virtual void CancelContextMenu() {}
+  virtual bool FormatLabel(base::string16& label) { return false; }
+
+ protected:
+  // Allow deletion via scoped_ptr only.
+  friend std::default_delete<CefMenuRunner>;
+
+  CefMenuRunner() {}
+  virtual ~CefMenuRunner() {}
+
+  DISALLOW_COPY_AND_ASSIGN(CefMenuRunner);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_MENU_RUNNER_H_
diff --git a/src/libcef/browser/native/browser_platform_delegate_native.cc b/src/libcef/browser/native/browser_platform_delegate_native.cc
new file mode 100644
index 0000000..b26ee3e
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native.cc
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/browser_platform_delegate_native.h"
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
+
+CefBrowserPlatformDelegateNative::CefBrowserPlatformDelegateNative(
+    const CefWindowInfo& window_info,
+    SkColor background_color,
+    bool use_shared_texture,
+    bool use_external_begin_frame)
+    : window_info_(window_info),
+      background_color_(background_color),
+      use_shared_texture_(use_shared_texture),
+      use_external_begin_frame_(use_external_begin_frame),
+      windowless_handler_(nullptr) {}
+
+SkColor CefBrowserPlatformDelegateNative::GetBackgroundColor() const {
+  return background_color_;
+}
+
+bool CefBrowserPlatformDelegateNative::CanUseSharedTexture() const {
+  return use_shared_texture_;
+}
+
+bool CefBrowserPlatformDelegateNative::CanUseExternalBeginFrame() const {
+  return use_external_begin_frame_;
+}
+
+void CefBrowserPlatformDelegateNative::WasResized() {
+  content::RenderViewHost* host = browser_->web_contents()->GetRenderViewHost();
+  if (host)
+    host->GetWidget()->SynchronizeVisualProperties();
+}
+
+bool CefBrowserPlatformDelegateNative::IsWindowless() const {
+  return false;
+}
+
+bool CefBrowserPlatformDelegateNative::IsViewsHosted() const {
+  return false;
+}
diff --git a/src/libcef/browser/native/browser_platform_delegate_native.h b/src/libcef/browser/native/browser_platform_delegate_native.h
new file mode 100644
index 0000000..a7c3003
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native.h
@@ -0,0 +1,78 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_H_
+
+#include "libcef/browser/browser_platform_delegate.h"
+
+// Base implementation of native browser functionality.
+class CefBrowserPlatformDelegateNative : public CefBrowserPlatformDelegate {
+ public:
+  // Used by the windowless implementation to override specific functionality
+  // when delegating to the native implementation.
+  class WindowlessHandler {
+   public:
+    // Returns the parent window handle.
+    virtual CefWindowHandle GetParentWindowHandle() const = 0;
+
+    // Convert from view coordinates to screen coordinates.
+    virtual gfx::Point GetParentScreenPoint(const gfx::Point& view) const = 0;
+
+   protected:
+    virtual ~WindowlessHandler() {}
+  };
+
+  // CefBrowserPlatformDelegate methods:
+  bool CanUseSharedTexture() const override;
+  bool CanUseExternalBeginFrame() const override;
+  SkColor GetBackgroundColor() const override;
+  void WasResized() override;
+  bool IsWindowless() const override;
+  bool IsViewsHosted() const override;
+
+  // Translate CEF events to Chromium/Blink Web events.
+  virtual content::NativeWebKeyboardEvent TranslateWebKeyEvent(
+      const CefKeyEvent& key_event) const = 0;
+  virtual blink::WebMouseEvent TranslateWebClickEvent(
+      const CefMouseEvent& mouse_event,
+      CefBrowserHost::MouseButtonType type,
+      bool mouseUp,
+      int clickCount) const = 0;
+  virtual blink::WebMouseEvent TranslateWebMoveEvent(
+      const CefMouseEvent& mouse_event,
+      bool mouseLeave) const = 0;
+  virtual blink::WebMouseWheelEvent TranslateWebWheelEvent(
+      const CefMouseEvent& mouse_event,
+      int deltaX,
+      int deltaY) const = 0;
+
+  const CefWindowInfo& window_info() const { return window_info_; }
+
+ protected:
+  // Delegates that can wrap a native delegate.
+  friend class CefBrowserPlatformDelegateBackground;
+  friend class CefBrowserPlatformDelegateOsr;
+  friend class CefBrowserPlatformDelegateViews;
+
+  CefBrowserPlatformDelegateNative(const CefWindowInfo& window_info,
+                                   SkColor background_color,
+                                   bool use_shared_texture,
+                                   bool use_external_begin_frame);
+
+  // Methods used by delegates that can wrap a native delegate.
+  void set_windowless_handler(WindowlessHandler* handler) {
+    windowless_handler_ = handler;
+  }
+  void set_browser(CefBrowserHostImpl* browser) { browser_ = browser; }
+
+  CefWindowInfo window_info_;
+  const SkColor background_color_;
+  const bool use_shared_texture_;
+  const bool use_external_begin_frame_;
+
+  WindowlessHandler* windowless_handler_;  // Not owned by this object.
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_H_
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_aura.cc b/src/libcef/browser/native/browser_platform_delegate_native_aura.cc
new file mode 100644
index 0000000..db5ceb1
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_aura.cc
@@ -0,0 +1,235 @@
+// Copyright 2020 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/browser_platform_delegate_native_aura.h"
+
+#include "content/browser/renderer_host/render_widget_host_view_aura.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "ui/events/blink/blink_event_util.h"
+#include "ui/events/blink/web_input_event.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+CefBrowserPlatformDelegateNativeAura::CefBrowserPlatformDelegateNativeAura(
+    const CefWindowInfo& window_info,
+    SkColor background_color,
+    bool use_shared_texture,
+    bool use_external_begin_frame)
+    : CefBrowserPlatformDelegateNative(window_info,
+                                       background_color,
+                                       use_shared_texture,
+                                       use_external_begin_frame) {}
+
+void CefBrowserPlatformDelegateNativeAura::SendKeyEvent(
+    const CefKeyEvent& event) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  ui::KeyEvent ui_event = TranslateUiKeyEvent(event);
+  view->OnKeyEvent(&ui_event);
+}
+
+void CefBrowserPlatformDelegateNativeAura::SendMouseClickEvent(
+    const CefMouseEvent& event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  ui::MouseEvent ui_event =
+      TranslateUiClickEvent(event, type, mouseUp, clickCount);
+  view->OnMouseEvent(&ui_event);
+}
+
+void CefBrowserPlatformDelegateNativeAura::SendMouseMoveEvent(
+    const CefMouseEvent& event,
+    bool mouseLeave) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  ui::MouseEvent ui_event = TranslateUiMoveEvent(event, mouseLeave);
+  view->OnMouseEvent(&ui_event);
+}
+
+void CefBrowserPlatformDelegateNativeAura::SendMouseWheelEvent(
+    const CefMouseEvent& event,
+    int deltaX,
+    int deltaY) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  ui::MouseWheelEvent ui_event = TranslateUiWheelEvent(event, deltaX, deltaY);
+  view->OnMouseEvent(&ui_event);
+}
+
+void CefBrowserPlatformDelegateNativeAura::SendTouchEvent(
+    const CefTouchEvent& event) {
+  NOTIMPLEMENTED();
+}
+
+content::NativeWebKeyboardEvent
+CefBrowserPlatformDelegateNativeAura::TranslateWebKeyEvent(
+    const CefKeyEvent& key_event) const {
+  return content::NativeWebKeyboardEvent(TranslateUiKeyEvent(key_event));
+}
+
+blink::WebMouseEvent
+CefBrowserPlatformDelegateNativeAura::TranslateWebClickEvent(
+    const CefMouseEvent& mouse_event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) const {
+  return ui::MakeWebMouseEvent(
+      TranslateUiClickEvent(mouse_event, type, mouseUp, clickCount));
+}
+
+blink::WebMouseEvent
+CefBrowserPlatformDelegateNativeAura::TranslateWebMoveEvent(
+    const CefMouseEvent& mouse_event,
+    bool mouseLeave) const {
+  return ui::MakeWebMouseEvent(TranslateUiMoveEvent(mouse_event, mouseLeave));
+}
+
+blink::WebMouseWheelEvent
+CefBrowserPlatformDelegateNativeAura::TranslateWebWheelEvent(
+    const CefMouseEvent& mouse_event,
+    int deltaX,
+    int deltaY) const {
+  return ui::MakeWebMouseWheelEvent(
+      TranslateUiWheelEvent(mouse_event, deltaX, deltaY));
+}
+
+ui::MouseEvent CefBrowserPlatformDelegateNativeAura::TranslateUiClickEvent(
+    const CefMouseEvent& mouse_event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) const {
+  DCHECK_GE(clickCount, 1);
+
+  ui::EventType event_type =
+      mouseUp ? ui::ET_MOUSE_RELEASED : ui::ET_MOUSE_PRESSED;
+  gfx::PointF location(mouse_event.x, mouse_event.y);
+  gfx::PointF root_location(
+      GetScreenPoint(gfx::Point(mouse_event.x, mouse_event.y)));
+  base::TimeTicks time_stamp = GetEventTimeStamp();
+  int flags = TranslateUiEventModifiers(mouse_event.modifiers);
+
+  int changed_button_flags = 0;
+  switch (type) {
+    case MBT_LEFT:
+      changed_button_flags |= ui::EF_LEFT_MOUSE_BUTTON;
+      break;
+    case MBT_MIDDLE:
+      changed_button_flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
+      break;
+    case MBT_RIGHT:
+      changed_button_flags |= ui::EF_RIGHT_MOUSE_BUTTON;
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  ui::MouseEvent result(event_type, location, root_location, time_stamp, flags,
+                        changed_button_flags);
+  result.SetClickCount(clickCount);
+  return result;
+}
+
+ui::MouseEvent CefBrowserPlatformDelegateNativeAura::TranslateUiMoveEvent(
+    const CefMouseEvent& mouse_event,
+    bool mouseLeave) const {
+  ui::EventType event_type =
+      mouseLeave ? ui::ET_MOUSE_EXITED : ui::ET_MOUSE_MOVED;
+  gfx::PointF location(mouse_event.x, mouse_event.y);
+  gfx::PointF root_location(
+      GetScreenPoint(gfx::Point(mouse_event.x, mouse_event.y)));
+  base::TimeTicks time_stamp = GetEventTimeStamp();
+  int flags = TranslateUiEventModifiers(mouse_event.modifiers);
+
+  int changed_button_flags = 0;
+  if (!mouseLeave) {
+    changed_button_flags = TranslateUiChangedButtonFlags(mouse_event.modifiers);
+  }
+
+  return ui::MouseEvent(event_type, location, root_location, time_stamp, flags,
+                        changed_button_flags);
+}
+
+ui::MouseWheelEvent CefBrowserPlatformDelegateNativeAura::TranslateUiWheelEvent(
+    const CefMouseEvent& mouse_event,
+    int deltaX,
+    int deltaY) const {
+  gfx::Vector2d offset(GetUiWheelEventOffset(deltaX, deltaY));
+  DCHECK(!offset.IsZero());
+
+  gfx::PointF location(mouse_event.x, mouse_event.y);
+  gfx::PointF root_location(
+      GetScreenPoint(gfx::Point(mouse_event.x, mouse_event.y)));
+  base::TimeTicks time_stamp = GetEventTimeStamp();
+  int flags = TranslateUiEventModifiers(mouse_event.modifiers);
+  int changed_button_flags =
+      TranslateUiChangedButtonFlags(mouse_event.modifiers);
+
+  return ui::MouseWheelEvent(offset, location, root_location, time_stamp,
+                             (ui::EF_PRECISION_SCROLLING_DELTA | flags),
+                             changed_button_flags);
+}
+
+gfx::Vector2d CefBrowserPlatformDelegateNativeAura::GetUiWheelEventOffset(
+    int deltaX,
+    int deltaY) const {
+  return gfx::Vector2d(deltaX, deltaY);
+}
+
+// static
+int CefBrowserPlatformDelegateNativeAura::TranslateUiEventModifiers(
+    uint32 cef_modifiers) {
+  int result = 0;
+  // Set modifiers based on key state.
+  if (cef_modifiers & EVENTFLAG_SHIFT_DOWN)
+    result |= ui::EF_SHIFT_DOWN;
+  if (cef_modifiers & EVENTFLAG_CONTROL_DOWN)
+    result |= ui::EF_CONTROL_DOWN;
+  if (cef_modifiers & EVENTFLAG_ALT_DOWN)
+    result |= ui::EF_ALT_DOWN;
+  if (cef_modifiers & EVENTFLAG_COMMAND_DOWN)
+    result |= ui::EF_COMMAND_DOWN;
+  if (cef_modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)
+    result |= ui::EF_LEFT_MOUSE_BUTTON;
+  if (cef_modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON)
+    result |= ui::EF_MIDDLE_MOUSE_BUTTON;
+  if (cef_modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON)
+    result |= ui::EF_RIGHT_MOUSE_BUTTON;
+  if (cef_modifiers & EVENTFLAG_CAPS_LOCK_ON)
+    result |= ui::EF_CAPS_LOCK_ON;
+  if (cef_modifiers & EVENTFLAG_NUM_LOCK_ON)
+    result |= ui::EF_NUM_LOCK_ON;
+  if (cef_modifiers & EVENTFLAG_ALTGR_DOWN)
+    result |= ui::EF_ALTGR_DOWN;
+  return result;
+}
+
+// static
+int CefBrowserPlatformDelegateNativeAura::TranslateUiChangedButtonFlags(
+    uint32 cef_modifiers) {
+  int result = 0;
+  if (cef_modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)
+    result |= ui::EF_LEFT_MOUSE_BUTTON;
+  else if (cef_modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON)
+    result |= ui::EF_MIDDLE_MOUSE_BUTTON;
+  else if (cef_modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON)
+    result |= ui::EF_RIGHT_MOUSE_BUTTON;
+  return result;
+}
+
+content::RenderWidgetHostViewAura*
+CefBrowserPlatformDelegateNativeAura::GetHostView() const {
+  return static_cast<content::RenderWidgetHostViewAura*>(
+      browser_->web_contents()->GetRenderWidgetHostView());
+}
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_aura.h b/src/libcef/browser/native/browser_platform_delegate_native_aura.h
new file mode 100644
index 0000000..96b5a41
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_aura.h
@@ -0,0 +1,81 @@
+// Copyright 2020 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_AURA_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_AURA_H_
+
+#include "libcef/browser/native/browser_platform_delegate_native.h"
+
+#include "ui/events/event.h"
+
+namespace content {
+class RenderWidgetHostViewAura;
+}
+
+namespace gfx {
+class Vector2d;
+}
+
+// Windowed browser implementation for Aura platforms.
+class CefBrowserPlatformDelegateNativeAura
+    : public CefBrowserPlatformDelegateNative {
+ public:
+  CefBrowserPlatformDelegateNativeAura(const CefWindowInfo& window_info,
+                                       SkColor background_color,
+                                       bool use_shared_texture,
+                                       bool use_external_begin_frame);
+
+  // CefBrowserPlatformDelegate methods:
+  void SendKeyEvent(const CefKeyEvent& event) override;
+  void SendMouseClickEvent(const CefMouseEvent& event,
+                           CefBrowserHost::MouseButtonType type,
+                           bool mouseUp,
+                           int clickCount) override;
+  void SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) override;
+  void SendMouseWheelEvent(const CefMouseEvent& event,
+                           int deltaX,
+                           int deltaY) override;
+  void SendTouchEvent(const CefTouchEvent& event) override;
+
+  // CefBrowserPlatformDelegateNative methods:
+  content::NativeWebKeyboardEvent TranslateWebKeyEvent(
+      const CefKeyEvent& key_event) const override;
+  blink::WebMouseEvent TranslateWebClickEvent(
+      const CefMouseEvent& mouse_event,
+      CefBrowserHost::MouseButtonType type,
+      bool mouseUp,
+      int clickCount) const override;
+  blink::WebMouseEvent TranslateWebMoveEvent(const CefMouseEvent& mouse_event,
+                                             bool mouseLeave) const override;
+  blink::WebMouseWheelEvent TranslateWebWheelEvent(
+      const CefMouseEvent& mouse_event,
+      int deltaX,
+      int deltaY) const override;
+
+  // Translate CEF events to Chromium UI events.
+  virtual ui::KeyEvent TranslateUiKeyEvent(
+      const CefKeyEvent& key_event) const = 0;
+  virtual ui::MouseEvent TranslateUiClickEvent(
+      const CefMouseEvent& mouse_event,
+      CefBrowserHost::MouseButtonType type,
+      bool mouseUp,
+      int clickCount) const;
+  virtual ui::MouseEvent TranslateUiMoveEvent(const CefMouseEvent& mouse_event,
+                                              bool mouseLeave) const;
+  virtual ui::MouseWheelEvent TranslateUiWheelEvent(
+      const CefMouseEvent& mouse_event,
+      int deltaX,
+      int deltaY) const;
+  virtual gfx::Vector2d GetUiWheelEventOffset(int deltaX, int deltaY) const;
+  virtual base::TimeTicks GetEventTimeStamp() const = 0;
+
+ protected:
+  static int TranslateUiEventModifiers(uint32 cef_modifiers);
+  static int TranslateUiChangedButtonFlags(uint32 cef_modifiers);
+
+ private:
+  content::RenderWidgetHostViewAura* GetHostView() const;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_AURA_H_
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_linux.cc b/src/libcef/browser/native/browser_platform_delegate_native_linux.cc
new file mode 100644
index 0000000..b6364b2
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_linux.cc
@@ -0,0 +1,329 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/browser_platform_delegate_native_linux.h"
+
+#include <sys/sysinfo.h>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/native/menu_runner_linux.h"
+#include "libcef/browser/native/window_delegate_view.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/no_destructor.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/render_view_host.h"
+#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
+#include "ui/events/keycodes/dom/dom_key.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+#include "ui/events/keycodes/keyboard_code_conversion_x.h"
+#include "ui/events/keycodes/keyboard_code_conversion_xkb.h"
+#include "ui/events/keycodes/keysym_to_unicode.h"
+#include "ui/gfx/font_render_params.h"
+#include "ui/views/widget/widget.h"
+
+#if defined(USE_X11)
+#include "libcef/browser/native/window_x11.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
+#endif
+
+namespace {
+
+// Returns the number of seconds since system boot.
+long GetSystemUptime() {
+  struct sysinfo info;
+  if (sysinfo(&info) == 0)
+    return info.uptime;
+  return 0;
+}
+
+}  // namespace
+
+CefBrowserPlatformDelegateNativeLinux::CefBrowserPlatformDelegateNativeLinux(
+    const CefWindowInfo& window_info,
+    SkColor background_color,
+    bool use_external_begin_frame)
+    : CefBrowserPlatformDelegateNativeAura(window_info,
+                                           background_color,
+                                           false,
+                                           use_external_begin_frame),
+      host_window_created_(false),
+      window_widget_(nullptr) {}
+
+void CefBrowserPlatformDelegateNativeLinux::BrowserDestroyed(
+    CefBrowserHostImpl* browser) {
+  CefBrowserPlatformDelegate::BrowserDestroyed(browser);
+
+  if (host_window_created_) {
+    // Release the reference added in CreateHostWindow().
+    browser->Release();
+  }
+}
+
+bool CefBrowserPlatformDelegateNativeLinux::CreateHostWindow() {
+  DCHECK(!window_widget_);
+
+  if (window_info_.width == 0)
+    window_info_.width = 800;
+  if (window_info_.height == 0)
+    window_info_.height = 600;
+
+  gfx::Rect rect(window_info_.x, window_info_.y, window_info_.width,
+                 window_info_.height);
+
+#if defined(USE_X11)
+  DCHECK(!window_x11_);
+  // Create a new window object. It will delete itself when the associated X11
+  // window is destroyed.
+  window_x11_ =
+      new CefWindowX11(browser_, window_info_.parent_window, rect,
+                       CefString(&window_info_.window_name).ToString());
+  window_info_.window = window_x11_->xwindow();
+
+  host_window_created_ = true;
+
+  // Add a reference that will be released in BrowserDestroyed().
+  browser_->AddRef();
+
+  CefWindowDelegateView* delegate_view = new CefWindowDelegateView(
+      GetBackgroundColor(), window_x11_->TopLevelAlwaysOnTop(),
+      GetBoundsChangedCallback());
+  delegate_view->Init(window_info_.window, browser_->web_contents(),
+                      gfx::Rect(gfx::Point(), rect.size()));
+
+  window_widget_ = delegate_view->GetWidget();
+  window_widget_->Show();
+
+  window_x11_->Show();
+#endif  // defined(USE_X11)
+
+  // As an additional requirement on Linux, we must set the colors for the
+  // render widgets in webkit.
+  blink::mojom::RendererPreferences* prefs =
+      browser_->web_contents()->GetMutableRendererPrefs();
+  prefs->focus_ring_color = SkColorSetARGB(255, 229, 151, 0);
+
+  prefs->active_selection_bg_color = SkColorSetRGB(30, 144, 255);
+  prefs->active_selection_fg_color = SK_ColorWHITE;
+  prefs->inactive_selection_bg_color = SkColorSetRGB(200, 200, 200);
+  prefs->inactive_selection_fg_color = SkColorSetRGB(50, 50, 50);
+
+  // Set font-related attributes.
+  static const base::NoDestructor<gfx::FontRenderParams> params(
+      gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr));
+  prefs->should_antialias_text = params->antialiasing;
+  prefs->use_subpixel_positioning = params->subpixel_positioning;
+  prefs->hinting = params->hinting;
+  prefs->use_autohinter = params->autohinter;
+  prefs->use_bitmaps = params->use_bitmaps;
+  prefs->subpixel_rendering = params->subpixel_rendering;
+
+  browser_->web_contents()->SyncRendererPrefs();
+
+  return true;
+}
+
+void CefBrowserPlatformDelegateNativeLinux::CloseHostWindow() {
+#if defined(USE_X11)
+  if (window_x11_)
+    window_x11_->Close();
+#endif
+}
+
+CefWindowHandle CefBrowserPlatformDelegateNativeLinux::GetHostWindowHandle()
+    const {
+  if (windowless_handler_)
+    return windowless_handler_->GetParentWindowHandle();
+  return window_info_.window;
+}
+
+views::Widget* CefBrowserPlatformDelegateNativeLinux::GetWindowWidget() const {
+  return window_widget_;
+}
+
+void CefBrowserPlatformDelegateNativeLinux::SendFocusEvent(bool setFocus) {
+  if (!setFocus)
+    return;
+
+  if (browser_->web_contents()) {
+    // Give logical focus to the RenderWidgetHostViewAura in the views
+    // hierarchy. This does not change the native keyboard focus.
+    browser_->web_contents()->Focus();
+  }
+
+#if defined(USE_X11)
+  if (window_x11_) {
+    // Give native focus to the DesktopNativeWidgetAura for the root window.
+    // Needs to be done via the ::Window so that keyboard focus is assigned
+    // correctly.
+    window_x11_->Focus();
+  }
+#endif  // defined(USE_X11)
+}
+
+void CefBrowserPlatformDelegateNativeLinux::NotifyMoveOrResizeStarted() {
+  // Call the parent method to dismiss any existing popups.
+  CefBrowserPlatformDelegate::NotifyMoveOrResizeStarted();
+
+#if defined(USE_X11)
+  if (!window_x11_)
+    return;
+
+  views::DesktopWindowTreeHostX11* tree_host = window_x11_->GetHost();
+  if (!tree_host)
+    return;
+
+  // Explicitly set the screen bounds so that WindowTreeHost::*Screen()
+  // methods return the correct results.
+  const gfx::Rect& bounds = window_x11_->GetBoundsInScreen();
+  tree_host->set_screen_bounds(bounds);
+
+  // Send updated screen rectangle information to the renderer process so that
+  // popups are displayed in the correct location.
+  content::RenderWidgetHostImpl::From(
+      browser_->web_contents()->GetRenderViewHost()->GetWidget())
+      ->SendScreenRects();
+#endif  // defined(USE_X11)
+}
+
+void CefBrowserPlatformDelegateNativeLinux::SizeTo(int width, int height) {
+#if defined(USE_X11)
+  if (window_x11_) {
+    window_x11_->SetBounds(
+        gfx::Rect(window_x11_->bounds().origin(), gfx::Size(width, height)));
+  }
+#endif  // defined(USE_X11)
+}
+
+gfx::Point CefBrowserPlatformDelegateNativeLinux::GetScreenPoint(
+    const gfx::Point& view) const {
+  if (windowless_handler_)
+    return windowless_handler_->GetParentScreenPoint(view);
+
+#if defined(USE_X11)
+  if (!window_x11_)
+    return view;
+
+  // We can't use aura::Window::GetBoundsInScreen on Linux because it will
+  // return bounds from DesktopWindowTreeHostX11 which in our case is relative
+  // to the parent window instead of the root window (screen).
+  const gfx::Rect& bounds_in_screen = window_x11_->GetBoundsInScreen();
+  return gfx::Point(bounds_in_screen.x() + view.x(),
+                    bounds_in_screen.y() + view.y());
+#endif  // defined(USE_X11)
+  return gfx::Point();
+}
+
+void CefBrowserPlatformDelegateNativeLinux::ViewText(const std::string& text) {
+  char buff[] = "/tmp/CEFSourceXXXXXX";
+  int fd = mkstemp(buff);
+
+  if (fd == -1)
+    return;
+
+  FILE* srcOutput = fdopen(fd, "w+");
+  if (!srcOutput)
+    return;
+
+  if (fputs(text.c_str(), srcOutput) < 0) {
+    fclose(srcOutput);
+    return;
+  }
+
+  fclose(srcOutput);
+
+  std::string newName(buff);
+  newName.append(".txt");
+  if (rename(buff, newName.c_str()) != 0)
+    return;
+
+  std::string openCommand("xdg-open ");
+  openCommand += newName;
+
+  int result = system(openCommand.c_str());
+  ALLOW_UNUSED_LOCAL(result);
+}
+
+bool CefBrowserPlatformDelegateNativeLinux::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  // TODO(cef): Is something required here to handle shortcut keys?
+  return false;
+}
+
+// static
+void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {}
+
+CefEventHandle CefBrowserPlatformDelegateNativeLinux::GetEventHandle(
+    const content::NativeWebKeyboardEvent& event) const {
+  // TODO(cef): We need to return an XEvent* from this method, but
+  // |event.os_event->native_event()| now returns a ui::Event* instead.
+  // See https://crbug.com/965991.
+  return nullptr;
+}
+
+std::unique_ptr<CefMenuRunner>
+CefBrowserPlatformDelegateNativeLinux::CreateMenuRunner() {
+  return base::WrapUnique(new CefMenuRunnerLinux);
+}
+
+gfx::Point CefBrowserPlatformDelegateNativeLinux::GetDialogPosition(
+    const gfx::Size& size) {
+  const gfx::Size& max_size = GetMaximumDialogSize();
+  return gfx::Point((max_size.width() - size.width()) / 2,
+                    (max_size.height() - size.height()) / 2);
+}
+
+gfx::Size CefBrowserPlatformDelegateNativeLinux::GetMaximumDialogSize() {
+  return GetWindowWidget()->GetWindowBoundsInScreen().size();
+}
+
+ui::KeyEvent CefBrowserPlatformDelegateNativeLinux::TranslateUiKeyEvent(
+    const CefKeyEvent& key_event) const {
+  int flags = TranslateUiEventModifiers(key_event.modifiers);
+  ui::KeyboardCode key_code =
+      static_cast<ui::KeyboardCode>(key_event.windows_key_code);
+  ui::DomCode dom_code =
+      ui::KeycodeConverter::NativeKeycodeToDomCode(key_event.native_key_code);
+  int keysym = ui::XKeysymForWindowsKeyCode(
+      key_code, !!(key_event.modifiers & EVENTFLAG_SHIFT_DOWN));
+  base::char16 character = ui::GetUnicodeCharacterFromXKeySym(keysym);
+  base::TimeTicks time_stamp = GetEventTimeStamp();
+
+  if (key_event.type == KEYEVENT_CHAR) {
+    return ui::KeyEvent(character, key_code, dom_code, flags, time_stamp);
+  }
+
+  ui::EventType type = ui::ET_UNKNOWN;
+  switch (key_event.type) {
+    case KEYEVENT_RAWKEYDOWN:
+    case KEYEVENT_KEYDOWN:
+      type = ui::ET_KEY_PRESSED;
+      break;
+    case KEYEVENT_KEYUP:
+      type = ui::ET_KEY_RELEASED;
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  ui::DomKey dom_key = ui::XKeySymToDomKey(keysym, character);
+  return ui::KeyEvent(type, key_code, dom_code, flags, dom_key, time_stamp);
+}
+
+content::NativeWebKeyboardEvent
+CefBrowserPlatformDelegateNativeLinux::TranslateWebKeyEvent(
+    const CefKeyEvent& key_event) const {
+  ui::KeyEvent ui_event = TranslateUiKeyEvent(key_event);
+  if (key_event.type == KEYEVENT_CHAR) {
+    return content::NativeWebKeyboardEvent(ui_event, key_event.character);
+  }
+  return content::NativeWebKeyboardEvent(ui_event);
+}
+
+base::TimeTicks CefBrowserPlatformDelegateNativeLinux::GetEventTimeStamp()
+    const {
+  return base::TimeTicks() + base::TimeDelta::FromSeconds(GetSystemUptime());
+}
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_linux.h b/src/libcef/browser/native/browser_platform_delegate_native_linux.h
new file mode 100644
index 0000000..7f1834a
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_linux.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_LINUX_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_LINUX_H_
+
+#include "libcef/browser/native/browser_platform_delegate_native_aura.h"
+
+#if defined(USE_X11)
+class CefWindowX11;
+#endif
+
+// Windowed browser implementation for Linux.
+class CefBrowserPlatformDelegateNativeLinux
+    : public CefBrowserPlatformDelegateNativeAura {
+ public:
+  CefBrowserPlatformDelegateNativeLinux(const CefWindowInfo& window_info,
+                                        SkColor background_color,
+                                        bool use_external_begin_frame);
+
+  // CefBrowserPlatformDelegate methods:
+  void BrowserDestroyed(CefBrowserHostImpl* browser) override;
+  bool CreateHostWindow() override;
+  void CloseHostWindow() override;
+  CefWindowHandle GetHostWindowHandle() const override;
+  views::Widget* GetWindowWidget() const override;
+  void SendFocusEvent(bool setFocus) override;
+  void NotifyMoveOrResizeStarted() override;
+  void SizeTo(int width, int height) override;
+  gfx::Point GetScreenPoint(const gfx::Point& view) const override;
+  void ViewText(const std::string& text) override;
+  bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) override;
+  CefEventHandle GetEventHandle(
+      const content::NativeWebKeyboardEvent& event) const override;
+  std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;
+  gfx::Point GetDialogPosition(const gfx::Size& size) override;
+  gfx::Size GetMaximumDialogSize() override;
+
+  // CefBrowserPlatformDelegateNativeAura methods:
+  ui::KeyEvent TranslateUiKeyEvent(const CefKeyEvent& key_event) const override;
+  content::NativeWebKeyboardEvent TranslateWebKeyEvent(
+      const CefKeyEvent& key_event) const override;
+  base::TimeTicks GetEventTimeStamp() const override;
+
+ private:
+  // True if the host window has been created.
+  bool host_window_created_;
+
+  // Widget hosting the web contents. It will be deleted automatically when the
+  // associated root window is destroyed.
+  views::Widget* window_widget_;
+
+#if defined(USE_X11)
+  CefWindowX11* window_x11_ = nullptr;
+#endif
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_LINUX_H_
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_mac.h b/src/libcef/browser/native/browser_platform_delegate_native_mac.h
new file mode 100644
index 0000000..e421a39
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_mac.h
@@ -0,0 +1,75 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_MAC_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_MAC_H_
+
+#include "libcef/browser/native/browser_platform_delegate_native.h"
+
+namespace content {
+class RenderWidgetHostViewMac;
+}
+
+// Windowed browser implementation for Mac OS X.
+class CefBrowserPlatformDelegateNativeMac
+    : public CefBrowserPlatformDelegateNative {
+ public:
+  CefBrowserPlatformDelegateNativeMac(const CefWindowInfo& window_info,
+                                      SkColor background_color);
+
+  // CefBrowserPlatformDelegate methods:
+  void BrowserDestroyed(CefBrowserHostImpl* browser) override;
+  bool CreateHostWindow() override;
+  void CloseHostWindow() override;
+  CefWindowHandle GetHostWindowHandle() const override;
+  void SendKeyEvent(const CefKeyEvent& event) override;
+  void SendMouseClickEvent(const CefMouseEvent& event,
+                           CefBrowserHost::MouseButtonType type,
+                           bool mouseUp,
+                           int clickCount) override;
+  void SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) override;
+  void SendMouseWheelEvent(const CefMouseEvent& event,
+                           int deltaX,
+                           int deltaY) override;
+  void SendTouchEvent(const CefTouchEvent& event) override;
+  void SendFocusEvent(bool setFocus) override;
+  gfx::Point GetScreenPoint(const gfx::Point& view) const override;
+  void ViewText(const std::string& text) override;
+  bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) override;
+  CefEventHandle GetEventHandle(
+      const content::NativeWebKeyboardEvent& event) const override;
+  std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
+  std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
+      override;
+  std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;
+  gfx::Point GetDialogPosition(const gfx::Size& size) override;
+  gfx::Size GetMaximumDialogSize() override;
+
+  // CefBrowserPlatformDelegateNative methods:
+  content::NativeWebKeyboardEvent TranslateWebKeyEvent(
+      const CefKeyEvent& key_event) const override;
+  blink::WebMouseEvent TranslateWebClickEvent(
+      const CefMouseEvent& mouse_event,
+      CefBrowserHost::MouseButtonType type,
+      bool mouseUp,
+      int clickCount) const override;
+  blink::WebMouseEvent TranslateWebMoveEvent(const CefMouseEvent& mouse_event,
+                                             bool mouseLeave) const override;
+  blink::WebMouseWheelEvent TranslateWebWheelEvent(
+      const CefMouseEvent& mouse_event,
+      int deltaX,
+      int deltaY) const override;
+
+ private:
+  void TranslateWebMouseEvent(blink::WebMouseEvent& result,
+                              const CefMouseEvent& mouse_event) const;
+
+  content::RenderWidgetHostViewMac* GetHostView() const;
+
+  // True if the host window has been created.
+  bool host_window_created_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_MAC_H_
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_mac.mm b/src/libcef/browser/native/browser_platform_delegate_native_mac.mm
new file mode 100644
index 0000000..7a73491
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_mac.mm
@@ -0,0 +1,568 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/browser_platform_delegate_native_mac.h"
+
+#import <Cocoa/Cocoa.h>
+#import <CoreServices/CoreServices.h>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/native/file_dialog_runner_mac.h"
+#include "libcef/browser/native/javascript_dialog_runner_mac.h"
+#include "libcef/browser/native/menu_runner_mac.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/browser/renderer_host/render_widget_host_view_mac.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
+#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
+#import "ui/base/cocoa/cocoa_base_utils.h"
+#import "ui/base/cocoa/underlay_opengl_hosting_window.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+#include "ui/gfx/geometry/rect.h"
+
+// Wrapper NSView for the native view. Necessary to destroy the browser when
+// the view is deleted.
+@interface CefBrowserHostView : NSView {
+ @private
+  CefBrowserHostImpl* browser_;  // weak
+}
+
+@property(nonatomic, assign) CefBrowserHostImpl* browser;
+
+@end
+
+@implementation CefBrowserHostView
+
+@synthesize browser = browser_;
+
+- (void)dealloc {
+  if (browser_) {
+    // Force the browser to be destroyed and release the reference added in
+    // PlatformCreateWindow().
+    browser_->WindowDestroyed();
+  }
+
+  [super dealloc];
+}
+
+@end
+
+// Receives notifications from the browser window. Will delete itself when done.
+@interface CefWindowDelegate : NSObject <NSWindowDelegate> {
+ @private
+  CefBrowserHostImpl* browser_;  // weak
+  NSWindow* window_;
+}
+- (id)initWithWindow:(NSWindow*)window andBrowser:(CefBrowserHostImpl*)browser;
+@end
+
+@implementation CefWindowDelegate
+
+- (id)initWithWindow:(NSWindow*)window andBrowser:(CefBrowserHostImpl*)browser {
+  if (self = [super init]) {
+    window_ = window;
+    browser_ = browser;
+
+    [window_ setDelegate:self];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+  [super dealloc];
+}
+
+- (BOOL)windowShouldClose:(id)window {
+  if (browser_ && !browser_->TryCloseBrowser()) {
+    // Cancel the close.
+    return NO;
+  }
+
+  // Clean ourselves up after clearing the stack of anything that might have the
+  // window on it.
+  [self performSelectorOnMainThread:@selector(cleanup:)
+                         withObject:window
+                      waitUntilDone:NO];
+
+  // Allow the close.
+  return YES;
+}
+
+- (void)cleanup:(id)window {
+  [window_ setDelegate:nil];
+  [self release];
+}
+
+@end
+
+namespace {
+
+NSTimeInterval currentEventTimestamp() {
+  NSEvent* currentEvent = [NSApp currentEvent];
+  if (currentEvent)
+    return [currentEvent timestamp];
+  else {
+    // FIXME(API): In case there is no current event, the timestamp could be
+    // obtained by getting the time since the application started. This involves
+    // taking some more static functions from Chromium code.
+    // Another option is to have the timestamp as a field in CefEvent structures
+    // and let the client provide it.
+    return 0;
+  }
+}
+
+NSUInteger NativeModifiers(int cef_modifiers) {
+  NSUInteger native_modifiers = 0;
+  if (cef_modifiers & EVENTFLAG_SHIFT_DOWN)
+    native_modifiers |= NSShiftKeyMask;
+  if (cef_modifiers & EVENTFLAG_CONTROL_DOWN)
+    native_modifiers |= NSControlKeyMask;
+  if (cef_modifiers & EVENTFLAG_ALT_DOWN)
+    native_modifiers |= NSAlternateKeyMask;
+  if (cef_modifiers & EVENTFLAG_COMMAND_DOWN)
+    native_modifiers |= NSCommandKeyMask;
+  if (cef_modifiers & EVENTFLAG_CAPS_LOCK_ON)
+    native_modifiers |= NSAlphaShiftKeyMask;
+  if (cef_modifiers & EVENTFLAG_NUM_LOCK_ON)
+    native_modifiers |= NSNumericPadKeyMask;
+
+  return native_modifiers;
+}
+
+}  // namespace
+
+CefBrowserPlatformDelegateNativeMac::CefBrowserPlatformDelegateNativeMac(
+    const CefWindowInfo& window_info,
+    SkColor background_color)
+    : CefBrowserPlatformDelegateNative(window_info,
+                                       background_color,
+                                       false,
+                                       false),
+      host_window_created_(false) {}
+
+void CefBrowserPlatformDelegateNativeMac::BrowserDestroyed(
+    CefBrowserHostImpl* browser) {
+  CefBrowserPlatformDelegate::BrowserDestroyed(browser);
+
+  if (host_window_created_) {
+    // Release the reference added in CreateHostWindow().
+    browser->Release();
+  }
+}
+
+bool CefBrowserPlatformDelegateNativeMac::CreateHostWindow() {
+  base::mac::ScopedNSAutoreleasePool autorelease_pool;
+
+  NSWindow* newWnd = nil;
+
+  NSView* parentView =
+      CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.parent_view);
+  NSRect contentRect = {{window_info_.x, window_info_.y},
+                        {window_info_.width, window_info_.height}};
+  if (parentView == nil) {
+    // Create a new window.
+    NSRect screen_rect = [[NSScreen mainScreen] visibleFrame];
+    NSRect window_rect = {
+        {window_info_.x, screen_rect.size.height - window_info_.y},
+        {window_info_.width, window_info_.height}};
+    if (window_rect.size.width == 0)
+      window_rect.size.width = 750;
+    if (window_rect.size.height == 0)
+      window_rect.size.height = 750;
+
+    contentRect.origin.x = 0;
+    contentRect.origin.y = 0;
+    contentRect.size.width = window_rect.size.width;
+    contentRect.size.height = window_rect.size.height;
+
+    newWnd = [[UnderlayOpenGLHostingWindow alloc]
+        initWithContentRect:window_rect
+                  styleMask:(NSTitledWindowMask | NSClosableWindowMask |
+                             NSMiniaturizableWindowMask |
+                             NSResizableWindowMask |
+                             NSUnifiedTitleAndToolbarWindowMask)
+                    backing:NSBackingStoreBuffered
+                      defer:NO];
+
+    // Create the delegate for control and browser window events.
+    [[CefWindowDelegate alloc] initWithWindow:newWnd andBrowser:browser_];
+
+    parentView = [newWnd contentView];
+    window_info_.parent_view = parentView;
+
+    // Make the content view for the window have a layer. This will make all
+    // sub-views have layers. This is necessary to ensure correct layer
+    // ordering of all child views and their layers.
+    [parentView setWantsLayer:YES];
+  }
+
+  host_window_created_ = true;
+
+  // Add a reference that will be released in BrowserDestroyed().
+  browser_->AddRef();
+
+  // Create the browser view.
+  CefBrowserHostView* browser_view =
+      [[CefBrowserHostView alloc] initWithFrame:contentRect];
+  browser_view.browser = browser_;
+  [parentView addSubview:browser_view];
+  [browser_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
+  [browser_view setNeedsDisplay:YES];
+  [browser_view release];
+
+  // Parent the TabContents to the browser view.
+  const NSRect bounds = [browser_view bounds];
+  NSView* native_view =
+      browser_->web_contents()->GetNativeView().GetNativeNSView();
+  [browser_view addSubview:native_view];
+  [native_view setFrame:bounds];
+  [native_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
+  [native_view setNeedsDisplay:YES];
+
+  window_info_.view = browser_view;
+
+  if (newWnd != nil && !window_info_.hidden) {
+    // Show the window.
+    [newWnd makeKeyAndOrderFront:nil];
+  }
+
+  return true;
+}
+
+void CefBrowserPlatformDelegateNativeMac::CloseHostWindow() {
+  NSView* nsview = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.view);
+  if (nsview != nil) {
+    [[nsview window] performSelectorOnMainThread:@selector(performClose:)
+                                      withObject:nil
+                                   waitUntilDone:NO];
+  }
+}
+
+CefWindowHandle CefBrowserPlatformDelegateNativeMac::GetHostWindowHandle()
+    const {
+  if (windowless_handler_)
+    return windowless_handler_->GetParentWindowHandle();
+  return window_info_.view;
+}
+
+void CefBrowserPlatformDelegateNativeMac::SendKeyEvent(
+    const CefKeyEvent& event) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  content::NativeWebKeyboardEvent web_event = TranslateWebKeyEvent(event);
+  view->ForwardKeyboardEvent(web_event, ui::LatencyInfo());
+}
+
+void CefBrowserPlatformDelegateNativeMac::SendMouseClickEvent(
+    const CefMouseEvent& event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  blink::WebMouseEvent web_event =
+      TranslateWebClickEvent(event, type, mouseUp, clickCount);
+  view->RouteOrProcessMouseEvent(web_event);
+}
+
+void CefBrowserPlatformDelegateNativeMac::SendMouseMoveEvent(
+    const CefMouseEvent& event,
+    bool mouseLeave) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  blink::WebMouseEvent web_event = TranslateWebMoveEvent(event, mouseLeave);
+  view->RouteOrProcessMouseEvent(web_event);
+}
+
+void CefBrowserPlatformDelegateNativeMac::SendMouseWheelEvent(
+    const CefMouseEvent& event,
+    int deltaX,
+    int deltaY) {
+  auto view = GetHostView();
+  if (!view)
+    return;
+
+  blink::WebMouseWheelEvent web_event =
+      TranslateWebWheelEvent(event, deltaX, deltaY);
+  view->RouteOrProcessMouseEvent(web_event);
+}
+
+void CefBrowserPlatformDelegateNativeMac::SendTouchEvent(
+    const CefTouchEvent& event) {
+  NOTIMPLEMENTED();
+}
+
+void CefBrowserPlatformDelegateNativeMac::SendFocusEvent(bool setFocus) {
+  auto view = GetHostView();
+  if (view) {
+    view->SetActive(setFocus);
+
+    if (setFocus) {
+      // Give keyboard focus to the native view.
+      NSView* view =
+          browser_->web_contents()->GetContentNativeView().GetNativeNSView();
+      DCHECK([view canBecomeKeyView]);
+      [[view window] makeFirstResponder:view];
+    }
+  }
+}
+
+gfx::Point CefBrowserPlatformDelegateNativeMac::GetScreenPoint(
+    const gfx::Point& view) const {
+  if (windowless_handler_)
+    return windowless_handler_->GetParentScreenPoint(view);
+
+  NSView* nsview = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.parent_view);
+  if (nsview) {
+    NSRect bounds = [nsview bounds];
+    NSPoint view_pt = {view.x(), bounds.size.height - view.y()};
+    NSPoint window_pt = [nsview convertPoint:view_pt toView:nil];
+    NSPoint screen_pt =
+        ui::ConvertPointFromWindowToScreen([nsview window], window_pt);
+    return gfx::Point(screen_pt.x, screen_pt.y);
+  }
+  return gfx::Point();
+}
+
+void CefBrowserPlatformDelegateNativeMac::ViewText(const std::string& text) {
+  // TODO(cef): Implement this functionality.
+  NOTIMPLEMENTED();
+}
+
+bool CefBrowserPlatformDelegateNativeMac::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  // Give the top level menu equivalents a chance to handle the event.
+  if ([event.os_event type] == NSKeyDown)
+    return [[NSApp mainMenu] performKeyEquivalent:event.os_event];
+  return false;
+}
+
+// static
+void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {}
+
+CefEventHandle CefBrowserPlatformDelegateNativeMac::GetEventHandle(
+    const content::NativeWebKeyboardEvent& event) const {
+  return event.os_event;
+}
+
+std::unique_ptr<CefFileDialogRunner>
+CefBrowserPlatformDelegateNativeMac::CreateFileDialogRunner() {
+  return base::WrapUnique(new CefFileDialogRunnerMac);
+}
+
+std::unique_ptr<CefJavaScriptDialogRunner>
+CefBrowserPlatformDelegateNativeMac::CreateJavaScriptDialogRunner() {
+  return base::WrapUnique(new CefJavaScriptDialogRunnerMac);
+}
+
+std::unique_ptr<CefMenuRunner>
+CefBrowserPlatformDelegateNativeMac::CreateMenuRunner() {
+  return base::WrapUnique(new CefMenuRunnerMac);
+}
+
+gfx::Point CefBrowserPlatformDelegateNativeMac::GetDialogPosition(
+    const gfx::Size& size) {
+  // Dialogs are always re-positioned by the constrained window sheet controller
+  // so nothing interesting to return yet.
+  return gfx::Point();
+}
+
+gfx::Size CefBrowserPlatformDelegateNativeMac::GetMaximumDialogSize() {
+  // The dialog should try to fit within the overlay for the web contents.
+  // Note that, for things like print preview, this is just a suggested maximum.
+  return browser_->web_contents()->GetContainerBounds().size();
+}
+
+content::NativeWebKeyboardEvent
+CefBrowserPlatformDelegateNativeMac::TranslateWebKeyEvent(
+    const CefKeyEvent& key_event) const {
+  content::NativeWebKeyboardEvent result(blink::WebInputEvent::kUndefined,
+                                         blink::WebInputEvent::kNoModifiers,
+                                         ui::EventTimeForNow());
+
+  // Use a synthetic NSEvent in order to obtain the windowsKeyCode member from
+  // the NativeWebKeyboardEvent constructor. This is the only member which can
+  // not be easily translated (without hardcoding keyCodes)
+  // Determining whether a modifier key is left or right seems to be done
+  // through the key code as well.
+  NSEventType event_type;
+  if (key_event.character == 0 && key_event.unmodified_character == 0) {
+    // Check if both character and unmodified_characther are empty to determine
+    // if this was a NSFlagsChanged event.
+    // A dead key will have an empty character, but a non-empty unmodified
+    // character
+    event_type = NSFlagsChanged;
+  } else {
+    switch (key_event.type) {
+      case KEYEVENT_RAWKEYDOWN:
+      case KEYEVENT_KEYDOWN:
+      case KEYEVENT_CHAR:
+        event_type = NSKeyDown;
+        break;
+      case KEYEVENT_KEYUP:
+        event_type = NSKeyUp;
+        break;
+    }
+  }
+
+  NSString* charactersIgnoringModifiers =
+      [[[NSString alloc] initWithCharacters:&key_event.unmodified_character
+                                     length:1] autorelease];
+  NSString* characters =
+      [[[NSString alloc] initWithCharacters:&key_event.character
+                                     length:1] autorelease];
+
+  NSEvent* synthetic_event =
+      [NSEvent keyEventWithType:event_type
+                             location:NSMakePoint(0, 0)
+                        modifierFlags:NativeModifiers(key_event.modifiers)
+                            timestamp:currentEventTimestamp()
+                         windowNumber:0
+                              context:nil
+                           characters:characters
+          charactersIgnoringModifiers:charactersIgnoringModifiers
+                            isARepeat:NO
+                              keyCode:key_event.native_key_code];
+
+  result = content::NativeWebKeyboardEvent(synthetic_event);
+  if (key_event.type == KEYEVENT_CHAR)
+    result.SetType(blink::WebInputEvent::kChar);
+
+  result.is_system_key = key_event.is_system_key;
+
+  return result;
+}
+
+blink::WebMouseEvent
+CefBrowserPlatformDelegateNativeMac::TranslateWebClickEvent(
+    const CefMouseEvent& mouse_event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) const {
+  blink::WebMouseEvent result;
+  TranslateWebMouseEvent(result, mouse_event);
+
+  switch (type) {
+    case MBT_LEFT:
+      result.SetType(mouseUp ? blink::WebInputEvent::kMouseUp
+                             : blink::WebInputEvent::kMouseDown);
+      result.button = blink::WebMouseEvent::Button::kLeft;
+      break;
+    case MBT_MIDDLE:
+      result.SetType(mouseUp ? blink::WebInputEvent::kMouseUp
+                             : blink::WebInputEvent::kMouseDown);
+      result.button = blink::WebMouseEvent::Button::kMiddle;
+      break;
+    case MBT_RIGHT:
+      result.SetType(mouseUp ? blink::WebInputEvent::kMouseUp
+                             : blink::WebInputEvent::kMouseDown);
+      result.button = blink::WebMouseEvent::Button::kRight;
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  result.click_count = clickCount;
+
+  return result;
+}
+
+blink::WebMouseEvent CefBrowserPlatformDelegateNativeMac::TranslateWebMoveEvent(
+    const CefMouseEvent& mouse_event,
+    bool mouseLeave) const {
+  blink::WebMouseEvent result;
+  TranslateWebMouseEvent(result, mouse_event);
+
+  if (!mouseLeave) {
+    result.SetType(blink::WebInputEvent::kMouseMove);
+    if (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)
+      result.button = blink::WebMouseEvent::Button::kLeft;
+    else if (mouse_event.modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON)
+      result.button = blink::WebMouseEvent::Button::kMiddle;
+    else if (mouse_event.modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON)
+      result.button = blink::WebMouseEvent::Button::kRight;
+    else
+      result.button = blink::WebMouseEvent::Button::kNoButton;
+  } else {
+    result.SetType(blink::WebInputEvent::kMouseLeave);
+    result.button = blink::WebMouseEvent::Button::kNoButton;
+  }
+
+  result.click_count = 0;
+
+  return result;
+}
+
+blink::WebMouseWheelEvent
+CefBrowserPlatformDelegateNativeMac::TranslateWebWheelEvent(
+    const CefMouseEvent& mouse_event,
+    int deltaX,
+    int deltaY) const {
+  blink::WebMouseWheelEvent result;
+  TranslateWebMouseEvent(result, mouse_event);
+
+  result.SetType(blink::WebInputEvent::kMouseWheel);
+
+  static const double scrollbarPixelsPerCocoaTick = 40.0;
+  result.delta_x = deltaX;
+  result.delta_y = deltaY;
+  result.wheel_ticks_x = deltaX / scrollbarPixelsPerCocoaTick;
+  result.wheel_ticks_y = deltaY / scrollbarPixelsPerCocoaTick;
+  result.delta_units = ui::ScrollGranularity::kScrollByPrecisePixel;
+
+  if (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)
+    result.button = blink::WebMouseEvent::Button::kLeft;
+  else if (mouse_event.modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON)
+    result.button = blink::WebMouseEvent::Button::kMiddle;
+  else if (mouse_event.modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON)
+    result.button = blink::WebMouseEvent::Button::kRight;
+  else
+    result.button = blink::WebMouseEvent::Button::kNoButton;
+
+  return result;
+}
+
+void CefBrowserPlatformDelegateNativeMac::TranslateWebMouseEvent(
+    blink::WebMouseEvent& result,
+    const CefMouseEvent& mouse_event) const {
+  // position
+  result.SetPositionInWidget(mouse_event.x, mouse_event.y);
+
+  const gfx::Point& screen_pt =
+      GetScreenPoint(gfx::Point(mouse_event.x, mouse_event.y));
+  result.SetPositionInScreen(screen_pt.x(), screen_pt.y());
+
+  // modifiers
+  result.SetModifiers(result.GetModifiers() |
+                      TranslateWebEventModifiers(mouse_event.modifiers));
+
+  // timestamp
+  result.SetTimeStamp(base::TimeTicks() +
+                      base::TimeDelta::FromSeconds(currentEventTimestamp()));
+
+  result.pointer_type = blink::WebPointerProperties::PointerType::kMouse;
+}
+
+content::RenderWidgetHostViewMac*
+CefBrowserPlatformDelegateNativeMac::GetHostView() const {
+  return static_cast<content::RenderWidgetHostViewMac*>(
+      browser_->web_contents()->GetRenderWidgetHostView());
+}
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_win.cc b/src/libcef/browser/native/browser_platform_delegate_native_win.cc
new file mode 100644
index 0000000..30eac2a
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_win.cc
@@ -0,0 +1,637 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/browser_platform_delegate_native_win.h"
+
+#include <shellapi.h>
+#include <wininet.h>
+#include <winspool.h>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/native/file_dialog_runner_win.h"
+#include "libcef/browser/native/javascript_dialog_runner_win.h"
+#include "libcef/browser/native/menu_runner_win.h"
+#include "libcef/browser/native/window_delegate_view.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+#include "base/win/win_util.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
+#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
+#include "ui/aura/window.h"
+#include "ui/base/win/shell.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/events/keycodes/dom/dom_key.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+#include "ui/events/keycodes/keyboard_code_conversion_win.h"
+#include "ui/events/keycodes/platform_key_map_win.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/win/hwnd_util.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/win/hwnd_util.h"
+
+#pragma comment(lib, "dwmapi.lib")
+
+namespace {
+
+void WriteTempFileAndView(scoped_refptr<base::RefCountedString> str) {
+  CEF_REQUIRE_BLOCKING();
+
+  base::FilePath tmp_file;
+  if (!base::CreateTemporaryFile(&tmp_file))
+    return;
+
+  // The shell command will look at the file extension to identify the correct
+  // program to open.
+  tmp_file = tmp_file.AddExtension(L"txt");
+
+  const std::string& data = str->data();
+  int write_ct = base::WriteFile(tmp_file, data.c_str(), data.size());
+  DCHECK_EQ(static_cast<int>(data.size()), write_ct);
+
+  ui::win::OpenFileViaShell(tmp_file);
+}
+
+// According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp:
+// "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in
+// ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6
+// support URLS of 2083 chars in length, 2K is safe."
+const int kMaxAddressLengthChars = 2048;
+
+bool HasExternalHandler(const std::string& scheme) {
+  base::win::RegKey key;
+  const std::wstring registry_path =
+      base::ASCIIToUTF16(scheme + "\\shell\\open\\command");
+  key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ);
+  if (key.Valid()) {
+    DWORD size = 0;
+    key.ReadValue(NULL, NULL, &size, NULL);
+    if (size > 2) {
+      // ShellExecute crashes the process when the command is empty.
+      // We check for "2" because it always returns the trailing NULL.
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void ExecuteExternalProtocol(const GURL& url) {
+  CEF_REQUIRE_BLOCKING();
+
+  if (!HasExternalHandler(url.scheme()))
+    return;
+
+  const std::string& address = url.spec();
+  if (address.length() > kMaxAddressLengthChars)
+    return;
+
+  ShellExecuteA(NULL, "open", address.c_str(), NULL, NULL, SW_SHOWNORMAL);
+}
+
+// DPI value for 1x scale factor.
+#define DPI_1X 96.0f
+
+float GetWindowScaleFactor(HWND hwnd) {
+  DCHECK(hwnd);
+
+  if (base::win::IsProcessPerMonitorDpiAware()) {
+    // Let Windows tell us the correct DPI.
+    static auto get_dpi_for_window_func = []() {
+      return reinterpret_cast<decltype(::GetDpiForWindow)*>(
+          GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow"));
+    }();
+    if (get_dpi_for_window_func)
+      return static_cast<float>(get_dpi_for_window_func(hwnd)) / DPI_1X;
+  }
+
+  // Fallback to the monitor that contains the window center point.
+  RECT cr;
+  GetWindowRect(hwnd, &cr);
+  return display::Screen::GetScreen()
+      ->GetDisplayNearestPoint(
+          gfx::Point((cr.right - cr.left) / 2, (cr.bottom - cr.top) / 2))
+      .device_scale_factor();
+}
+
+}  // namespace
+
+CefBrowserPlatformDelegateNativeWin::CefBrowserPlatformDelegateNativeWin(
+    const CefWindowInfo& window_info,
+    SkColor background_color,
+    bool use_shared_texture,
+    bool use_external_begin_frame)
+    : CefBrowserPlatformDelegateNativeAura(window_info,
+                                           background_color,
+                                           use_shared_texture,
+                                           use_external_begin_frame),
+      host_window_created_(false),
+      window_widget_(nullptr) {}
+
+void CefBrowserPlatformDelegateNativeWin::BrowserDestroyed(
+    CefBrowserHostImpl* browser) {
+  CefBrowserPlatformDelegate::BrowserDestroyed(browser);
+
+  if (host_window_created_) {
+    // Release the reference added in CreateHostWindow().
+    browser->Release();
+  }
+}
+
+bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() {
+  RegisterWindowClass();
+
+  has_frame_ = !(window_info_.style & WS_CHILD);
+
+  std::wstring windowName(CefString(&window_info_.window_name));
+
+  // Create the new browser window.
+  CreateWindowEx(window_info_.ex_style, GetWndClass(), windowName.c_str(),
+                 window_info_.style, window_info_.x, window_info_.y,
+                 window_info_.width, window_info_.height,
+                 window_info_.parent_window, window_info_.menu,
+                 ::GetModuleHandle(NULL), this);
+
+  // It's possible for CreateWindowEx to fail if the parent window was
+  // destroyed between the call to CreateBrowser and the above one.
+  DCHECK(window_info_.window);
+  if (!window_info_.window)
+    return false;
+
+  host_window_created_ = true;
+
+  // Add a reference that will later be released in DestroyBrowser().
+  browser_->AddRef();
+
+  if (!called_enable_non_client_dpi_scaling_ && has_frame_ &&
+      base::win::IsProcessPerMonitorDpiAware()) {
+    // This call gets Windows to scale the non-client area when WM_DPICHANGED
+    // is fired on Windows versions < 10.0.14393.0.
+    // Derived signature; not available in headers.
+    static auto enable_child_window_dpi_message_func = []() {
+      using EnableChildWindowDpiMessagePtr = LRESULT(WINAPI*)(HWND, BOOL);
+      return reinterpret_cast<EnableChildWindowDpiMessagePtr>(GetProcAddress(
+          GetModuleHandle(L"user32.dll"), "EnableChildWindowDpiMessage"));
+    }();
+    if (enable_child_window_dpi_message_func)
+      enable_child_window_dpi_message_func(window_info_.window, TRUE);
+  }
+
+  DCHECK(!window_widget_);
+
+  // Convert from device coordinates to logical coordinates.
+  RECT cr;
+  GetClientRect(window_info_.window, &cr);
+  gfx::Point point = gfx::Point(cr.right, cr.bottom);
+  const float scale = GetWindowScaleFactor(window_info_.window);
+  point =
+      gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(point), 1.0f / scale));
+
+  // Stay on top if top-most window hosting the web view is topmost.
+  HWND top_level_window = GetAncestor(window_info_.window, GA_ROOT);
+  DWORD top_level_window_ex_styles =
+      GetWindowLongPtr(top_level_window, GWL_EXSTYLE);
+  bool always_on_top =
+      (top_level_window_ex_styles & WS_EX_TOPMOST) == WS_EX_TOPMOST;
+
+  CefWindowDelegateView* delegate_view = new CefWindowDelegateView(
+      GetBackgroundColor(), always_on_top, GetBoundsChangedCallback());
+  delegate_view->Init(window_info_.window, browser_->web_contents(),
+                      gfx::Rect(0, 0, point.x(), point.y()));
+
+  window_widget_ = delegate_view->GetWidget();
+
+  const HWND widget_hwnd = HWNDForWidget(window_widget_);
+  DCHECK(widget_hwnd);
+  const DWORD widget_ex_styles = GetWindowLongPtr(widget_hwnd, GWL_EXSTYLE);
+
+  if (window_info_.ex_style & WS_EX_NOACTIVATE) {
+    // Add the WS_EX_NOACTIVATE style on the DesktopWindowTreeHostWin HWND
+    // so that HWNDMessageHandler::Show() called via Widget::Show() does not
+    // activate the window.
+    SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE,
+                     widget_ex_styles | WS_EX_NOACTIVATE);
+  }
+
+  window_widget_->Show();
+
+  if (window_info_.ex_style & WS_EX_NOACTIVATE) {
+    // Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the
+    // browser correctly activate and focus the window.
+    SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE, widget_ex_styles);
+  }
+
+  return true;
+}
+
+void CefBrowserPlatformDelegateNativeWin::CloseHostWindow() {
+  if (window_info_.window != NULL) {
+    HWND frameWnd = GetAncestor(window_info_.window, GA_ROOT);
+    PostMessage(frameWnd, WM_CLOSE, 0, 0);
+  }
+}
+
+CefWindowHandle CefBrowserPlatformDelegateNativeWin::GetHostWindowHandle()
+    const {
+  if (windowless_handler_)
+    return windowless_handler_->GetParentWindowHandle();
+  return window_info_.window;
+}
+
+views::Widget* CefBrowserPlatformDelegateNativeWin::GetWindowWidget() const {
+  return window_widget_;
+}
+
+void CefBrowserPlatformDelegateNativeWin::SendFocusEvent(bool setFocus) {
+  if (!setFocus)
+    return;
+
+  if (browser_->web_contents()) {
+    // Give logical focus to the RenderWidgetHostViewAura in the views
+    // hierarchy. This does not change the native keyboard focus.
+    browser_->web_contents()->Focus();
+  }
+
+  if (window_widget_) {
+    // Give native focus to the DesktopWindowTreeHostWin associated with the
+    // root window.
+    //
+    // The DesktopWindowTreeHostWin HandleNativeFocus/HandleNativeBlur methods
+    // are called in response to WM_SETFOCUS/WM_KILLFOCUS respectively. The
+    // implementation has been patched to call HandleActivationChanged which
+    // results in the following behaviors:
+    // 1. Update focus/activation state of the aura::Window indirectly via
+    //    wm::FocusController. This allows focus-related behaviors (e.g. focus
+    //    rings, flashing caret, onFocus/onBlur JS events, etc.) to work as
+    //    expected (see issue #1677).
+    // 2. Update focus state of the ui::InputMethod. If this does not occur
+    //    then InputMethodBase::GetTextInputClient will return NULL and
+    //    InputMethodWin::OnChar will fail to sent character events to the
+    //    renderer (see issue #1700).
+    //
+    // This differs from activation in Chrome which is handled via
+    // HWNDMessageHandler::PostProcessActivateMessage (Widget::Show indirectly
+    // calls HWNDMessageHandler::Activate which calls ::SetForegroundWindow
+    // resulting in a WM_ACTIVATE message being sent to the window). The Chrome
+    // code path doesn't work for CEF because IsTopLevelWindow in
+    // hwnd_message_handler.cc will return false and consequently
+    // HWNDMessageHandler::PostProcessActivateMessage will not be called.
+    //
+    // Activation events are usually reserved for the top-level window so
+    // triggering activation based on focus events may be incorrect in some
+    // circumstances. Revisit this implementation if additional problems are
+    // discovered.
+    ::SetFocus(HWNDForWidget(window_widget_));
+  }
+}
+
+void CefBrowserPlatformDelegateNativeWin::NotifyMoveOrResizeStarted() {
+  // Call the parent method to dismiss any existing popups.
+  CefBrowserPlatformDelegate::NotifyMoveOrResizeStarted();
+
+  if (!window_widget_)
+    return;
+
+  // Notify DesktopWindowTreeHostWin of move events so that screen rectangle
+  // information is communicated to the renderer process and popups are
+  // displayed in the correct location.
+  views::DesktopWindowTreeHostWin* tree_host =
+      static_cast<views::DesktopWindowTreeHostWin*>(
+          aura::WindowTreeHost::GetForAcceleratedWidget(
+              HWNDForWidget(window_widget_)));
+  DCHECK(tree_host);
+  if (tree_host) {
+    // Cast to HWNDMessageHandlerDelegate so we can access HandleMove().
+    static_cast<views::HWNDMessageHandlerDelegate*>(tree_host)->HandleMove();
+  }
+}
+
+void CefBrowserPlatformDelegateNativeWin::SizeTo(int width, int height) {
+  HWND window = window_info_.window;
+
+  RECT rect = {0, 0, width, height};
+  DWORD style = GetWindowLong(window, GWL_STYLE);
+  DWORD ex_style = GetWindowLong(window, GWL_EXSTYLE);
+  bool has_menu = !(style & WS_CHILD) && (GetMenu(window) != NULL);
+
+  // The size value is for the client area. Calculate the whole window size
+  // based on the current style.
+  AdjustWindowRectEx(&rect, style, has_menu, ex_style);
+
+  // Size the window. The left/top values may be negative.
+  SetWindowPos(window, NULL, 0, 0, rect.right - rect.left,
+               rect.bottom - rect.top,
+               SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+}
+
+gfx::Point CefBrowserPlatformDelegateNativeWin::GetScreenPoint(
+    const gfx::Point& view) const {
+  if (windowless_handler_)
+    return windowless_handler_->GetParentScreenPoint(view);
+
+  if (!window_info_.window)
+    return view;
+
+  // Convert from logical coordinates to device coordinates.
+  const float scale = GetWindowScaleFactor(window_info_.window);
+  const gfx::Point& device_pt =
+      gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(view), scale));
+
+  // Convert from client coordinates to screen coordinates.
+  POINT screen_pt = {device_pt.x(), device_pt.y()};
+  ClientToScreen(window_info_.window, &screen_pt);
+
+  return gfx::Point(screen_pt.x, screen_pt.y);
+}
+
+void CefBrowserPlatformDelegateNativeWin::ViewText(const std::string& text) {
+  std::string str = text;
+  scoped_refptr<base::RefCountedString> str_ref =
+      base::RefCountedString::TakeString(&str);
+  CEF_POST_USER_VISIBLE_TASK(base::Bind(WriteTempFileAndView, str_ref));
+}
+
+bool CefBrowserPlatformDelegateNativeWin::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  // Any unhandled keyboard/character messages are sent to DefWindowProc so that
+  // shortcut keys work correctly.
+  if (event.os_event) {
+    const MSG& msg = event.os_event->native_event();
+    return !DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
+  } else {
+    MSG msg = {};
+
+    msg.hwnd = GetHostWindowHandle();
+    if (!msg.hwnd)
+      return false;
+
+    switch (event.GetType()) {
+      case blink::WebInputEvent::kRawKeyDown:
+        msg.message = event.is_system_key ? WM_SYSKEYDOWN : WM_KEYDOWN;
+        break;
+      case blink::WebInputEvent::kKeyUp:
+        msg.message = event.is_system_key ? WM_SYSKEYUP : WM_KEYUP;
+        break;
+      case blink::WebInputEvent::kChar:
+        msg.message = event.is_system_key ? WM_SYSCHAR : WM_CHAR;
+        break;
+      default:
+        NOTREACHED();
+        return false;
+    }
+
+    msg.wParam = event.windows_key_code;
+
+    UINT scan_code = ::MapVirtualKeyW(event.windows_key_code, MAPVK_VK_TO_VSC);
+    msg.lParam = (scan_code << 16) |  // key scan code
+                 1;                   // key repeat count
+    if (event.GetModifiers() & content::NativeWebKeyboardEvent::kAltKey)
+      msg.lParam |= (1 << 29);
+
+    return !DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
+  }
+}
+
+// static
+void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {
+  CEF_POST_USER_VISIBLE_TASK(base::Bind(ExecuteExternalProtocol, url));
+}
+
+CefEventHandle CefBrowserPlatformDelegateNativeWin::GetEventHandle(
+    const content::NativeWebKeyboardEvent& event) const {
+  if (!event.os_event)
+    return NULL;
+  return const_cast<CefEventHandle>(&event.os_event->native_event());
+}
+
+std::unique_ptr<CefFileDialogRunner>
+CefBrowserPlatformDelegateNativeWin::CreateFileDialogRunner() {
+  return base::WrapUnique(new CefFileDialogRunnerWin);
+}
+
+std::unique_ptr<CefJavaScriptDialogRunner>
+CefBrowserPlatformDelegateNativeWin::CreateJavaScriptDialogRunner() {
+  return base::WrapUnique(new CefJavaScriptDialogRunnerWin);
+}
+
+std::unique_ptr<CefMenuRunner>
+CefBrowserPlatformDelegateNativeWin::CreateMenuRunner() {
+  return base::WrapUnique(new CefMenuRunnerWin);
+}
+
+gfx::Point CefBrowserPlatformDelegateNativeWin::GetDialogPosition(
+    const gfx::Size& size) {
+  const gfx::Size& max_size = GetMaximumDialogSize();
+  return gfx::Point((max_size.width() - size.width()) / 2,
+                    (max_size.height() - size.height()) / 2);
+}
+
+gfx::Size CefBrowserPlatformDelegateNativeWin::GetMaximumDialogSize() {
+  return GetWindowWidget()->GetWindowBoundsInScreen().size();
+}
+
+ui::KeyEvent CefBrowserPlatformDelegateNativeWin::TranslateUiKeyEvent(
+    const CefKeyEvent& key_event) const {
+  int flags = TranslateUiEventModifiers(key_event.modifiers);
+  ui::KeyboardCode key_code =
+      ui::KeyboardCodeForWindowsKeyCode(key_event.windows_key_code);
+  ui::DomCode dom_code =
+      ui::KeycodeConverter::NativeKeycodeToDomCode(key_event.native_key_code);
+  base::TimeTicks time_stamp = GetEventTimeStamp();
+
+  if (key_event.type == KEYEVENT_CHAR) {
+    return ui::KeyEvent(key_event.windows_key_code /* character */, key_code,
+                        dom_code, flags, time_stamp);
+  }
+
+  ui::EventType type = ui::ET_UNKNOWN;
+  switch (key_event.type) {
+    case KEYEVENT_RAWKEYDOWN:
+    case KEYEVENT_KEYDOWN:
+      type = ui::ET_KEY_PRESSED;
+      break;
+    case KEYEVENT_KEYUP:
+      type = ui::ET_KEY_RELEASED;
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  ui::DomKey dom_key =
+      ui::PlatformKeyMap::DomKeyFromKeyboardCode(key_code, &flags);
+  return ui::KeyEvent(type, key_code, dom_code, flags, dom_key, time_stamp);
+}
+
+gfx::Vector2d CefBrowserPlatformDelegateNativeWin::GetUiWheelEventOffset(
+    int deltaX,
+    int deltaY) const {
+  static const ULONG defaultScrollCharsPerWheelDelta = 1;
+  static const FLOAT scrollbarPixelsPerLine = 100.0f / 3.0f;
+  static const ULONG defaultScrollLinesPerWheelDelta = 3;
+
+  float wheelDeltaX = float(deltaX) / WHEEL_DELTA;
+  float wheelDeltaY = float(deltaY) / WHEEL_DELTA;
+  float scrollDeltaX = wheelDeltaX;
+  float scrollDeltaY = wheelDeltaY;
+
+  ULONG scrollChars = defaultScrollCharsPerWheelDelta;
+  SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
+  scrollDeltaX *= static_cast<FLOAT>(scrollChars) * scrollbarPixelsPerLine;
+
+  ULONG scrollLines = defaultScrollLinesPerWheelDelta;
+  SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
+  scrollDeltaY *= static_cast<FLOAT>(scrollLines) * scrollbarPixelsPerLine;
+
+  return gfx::Vector2d(scrollDeltaX, scrollDeltaY);
+}
+
+base::TimeTicks CefBrowserPlatformDelegateNativeWin::GetEventTimeStamp() const {
+  return base::TimeTicks() +
+         base::TimeDelta::FromMilliseconds(GetMessageTime());
+}
+
+// static
+void CefBrowserPlatformDelegateNativeWin::RegisterWindowClass() {
+  static bool registered = false;
+  if (registered)
+    return;
+
+  // Register the window class
+  WNDCLASSEX wcex = {
+      /* cbSize = */ sizeof(WNDCLASSEX),
+      /* style = */ CS_HREDRAW | CS_VREDRAW,
+      /* lpfnWndProc = */ CefBrowserPlatformDelegateNativeWin::WndProc,
+      /* cbClsExtra = */ 0,
+      /* cbWndExtra = */ 0,
+      /* hInstance = */ ::GetModuleHandle(NULL),
+      /* hIcon = */ NULL,
+      /* hCursor = */ LoadCursor(NULL, IDC_ARROW),
+      /* hbrBackground = */ 0,
+      /* lpszMenuName = */ NULL,
+      /* lpszClassName = */ CefBrowserPlatformDelegateNativeWin::GetWndClass(),
+      /* hIconSm = */ NULL,
+  };
+  RegisterClassEx(&wcex);
+
+  registered = true;
+}
+
+// static
+LPCTSTR CefBrowserPlatformDelegateNativeWin::GetWndClass() {
+  return L"CefBrowserWindow";
+}
+
+// static
+LRESULT CALLBACK CefBrowserPlatformDelegateNativeWin::WndProc(HWND hwnd,
+                                                              UINT message,
+                                                              WPARAM wParam,
+                                                              LPARAM lParam) {
+  CefBrowserPlatformDelegateNativeWin* platform_delegate = nullptr;
+  CefBrowserHostImpl* browser = nullptr;
+
+  if (message != WM_NCCREATE) {
+    platform_delegate = static_cast<CefBrowserPlatformDelegateNativeWin*>(
+        gfx::GetWindowUserData(hwnd));
+    if (platform_delegate)
+      browser = platform_delegate->browser_;
+  }
+
+  switch (message) {
+    case WM_CLOSE:
+      if (browser && !browser->TryCloseBrowser()) {
+        // Cancel the close.
+        return 0;
+      }
+
+      // Allow the close.
+      break;
+
+    case WM_NCCREATE: {
+      CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
+      platform_delegate =
+          reinterpret_cast<CefBrowserPlatformDelegateNativeWin*>(
+              cs->lpCreateParams);
+      DCHECK(platform_delegate);
+      // Associate |platform_delegate| with the window handle.
+      gfx::SetWindowUserData(hwnd, platform_delegate);
+      platform_delegate->window_info_.window = hwnd;
+
+      if (platform_delegate->has_frame_ &&
+          base::win::IsProcessPerMonitorDpiAware()) {
+        // This call gets Windows to scale the non-client area when
+        // WM_DPICHANGED is fired on Windows versions >= 10.0.14393.0.
+        static auto enable_non_client_dpi_scaling_func = []() {
+          return reinterpret_cast<decltype(::EnableNonClientDpiScaling)*>(
+              GetProcAddress(GetModuleHandle(L"user32.dll"),
+                             "EnableNonClientDpiScaling"));
+        }();
+        platform_delegate->called_enable_non_client_dpi_scaling_ =
+            !!(enable_non_client_dpi_scaling_func &&
+               enable_non_client_dpi_scaling_func(hwnd));
+      }
+    } break;
+
+    case WM_NCDESTROY:
+      if (platform_delegate) {
+        // Clear the user data pointer.
+        gfx::SetWindowUserData(hwnd, NULL);
+
+        // Force the browser to be destroyed. This will result in a call to
+        // BrowserDestroyed() that will release the reference added in
+        // CreateHostWindow().
+        browser->WindowDestroyed();
+      }
+      break;
+
+    case WM_SIZE:
+      if (platform_delegate && platform_delegate->window_widget_) {
+        // Pass window resize events to the HWND for the DesktopNativeWidgetAura
+        // root window. Passing size 0x0 (wParam == SIZE_MINIMIZED, for example)
+        // will cause the widget to be hidden which reduces resource usage.
+        RECT rc;
+        GetClientRect(hwnd, &rc);
+        SetWindowPos(HWNDForWidget(platform_delegate->window_widget_), NULL,
+                     rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
+                     SWP_NOZORDER);
+      }
+      return 0;
+
+    case WM_MOVING:
+    case WM_MOVE:
+      if (browser)
+        browser->NotifyMoveOrResizeStarted();
+      return 0;
+
+    case WM_SETFOCUS:
+      // Selecting "Close window" from the task bar menu may send a focus
+      // notification even though the window is currently disabled (e.g. while
+      // a modal JS dialog is displayed).
+      if (browser && ::IsWindowEnabled(hwnd))
+        browser->SetFocus(true);
+      return 0;
+
+    case WM_ERASEBKGND:
+      return 0;
+
+    case WM_DPICHANGED:
+      if (platform_delegate && platform_delegate->has_frame_) {
+        // Suggested size and position of the current window scaled for the
+        // new DPI.
+        const RECT* rect = reinterpret_cast<RECT*>(lParam);
+        SetWindowPos(platform_delegate->GetHostWindowHandle(), NULL, rect->left,
+                     rect->top, rect->right - rect->left,
+                     rect->bottom - rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
+      }
+      break;
+  }
+
+  return DefWindowProc(hwnd, message, wParam, lParam);
+}
diff --git a/src/libcef/browser/native/browser_platform_delegate_native_win.h b/src/libcef/browser/native/browser_platform_delegate_native_win.h
new file mode 100644
index 0000000..e1dd0a0
--- /dev/null
+++ b/src/libcef/browser/native/browser_platform_delegate_native_win.h
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_WIN_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_WIN_H_
+
+#include <windows.h>
+
+#include "libcef/browser/native/browser_platform_delegate_native_aura.h"
+
+// Windowed browser implementation for Windows.
+class CefBrowserPlatformDelegateNativeWin
+    : public CefBrowserPlatformDelegateNativeAura {
+ public:
+  CefBrowserPlatformDelegateNativeWin(const CefWindowInfo& window_info,
+                                      SkColor background_color,
+                                      bool use_shared_texture,
+                                      bool use_external_begin_frame);
+
+  // CefBrowserPlatformDelegate methods:
+  void BrowserDestroyed(CefBrowserHostImpl* browser) override;
+  bool CreateHostWindow() override;
+  void CloseHostWindow() override;
+  CefWindowHandle GetHostWindowHandle() const override;
+  views::Widget* GetWindowWidget() const override;
+  void SendFocusEvent(bool setFocus) override;
+  void NotifyMoveOrResizeStarted() override;
+  void SizeTo(int width, int height) override;
+  gfx::Point GetScreenPoint(const gfx::Point& view) const override;
+  void ViewText(const std::string& text) override;
+  bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) override;
+  CefEventHandle GetEventHandle(
+      const content::NativeWebKeyboardEvent& event) const override;
+  std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
+  std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
+      override;
+  std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;
+  gfx::Point GetDialogPosition(const gfx::Size& size) override;
+  gfx::Size GetMaximumDialogSize() override;
+
+  // CefBrowserPlatformDelegateNativeAura methods:
+  ui::KeyEvent TranslateUiKeyEvent(const CefKeyEvent& key_event) const override;
+  gfx::Vector2d GetUiWheelEventOffset(int deltaX, int deltaY) const override;
+  base::TimeTicks GetEventTimeStamp() const override;
+
+ private:
+  static void RegisterWindowClass();
+  static LPCTSTR GetWndClass();
+  static LRESULT CALLBACK WndProc(HWND hwnd,
+                                  UINT message,
+                                  WPARAM wParam,
+                                  LPARAM lParam);
+
+  // True if the host window has been created.
+  bool host_window_created_;
+
+  // Widget hosting the web contents. It will be deleted automatically when the
+  // associated root window is destroyed.
+  views::Widget* window_widget_;
+
+  bool has_frame_ = false;
+  bool called_enable_non_client_dpi_scaling_ = false;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_WIN_H_
diff --git a/src/libcef/browser/native/file_dialog_runner_mac.h b/src/libcef/browser/native/file_dialog_runner_mac.h
new file mode 100644
index 0000000..4fa8fdc
--- /dev/null
+++ b/src/libcef/browser/native/file_dialog_runner_mac.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_MAC_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_MAC_H_
+#pragma once
+
+#include "libcef/browser/file_dialog_runner.h"
+
+#include "base/memory/weak_ptr.h"
+
+@class NSView;
+
+class CefFileDialogRunnerMac : public CefFileDialogRunner {
+ public:
+  CefFileDialogRunnerMac();
+
+  // CefFileDialogRunner methods:
+  void Run(CefBrowserHostImpl* browser,
+           const FileChooserParams& params,
+           RunFileChooserCallback callback) override;
+
+ private:
+  static void RunOpenFileDialog(
+      base::WeakPtr<CefFileDialogRunnerMac> weak_this,
+      const CefFileDialogRunner::FileChooserParams& params,
+      NSView* view,
+      int filter_index);
+  static void RunSaveFileDialog(
+      base::WeakPtr<CefFileDialogRunnerMac> weak_this,
+      const CefFileDialogRunner::FileChooserParams& params,
+      NSView* view,
+      int filter_index);
+
+  CefFileDialogRunner::RunFileChooserCallback callback_;
+  base::WeakPtrFactory<CefFileDialogRunnerMac> weak_ptr_factory_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_MAC_H_
diff --git a/src/libcef/browser/native/file_dialog_runner_mac.mm b/src/libcef/browser/native/file_dialog_runner_mac.mm
new file mode 100644
index 0000000..25ffa1a
--- /dev/null
+++ b/src/libcef/browser/native/file_dialog_runner_mac.mm
@@ -0,0 +1,406 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/file_dialog_runner_mac.h"
+
+#import <Cocoa/Cocoa.h>
+#import <CoreServices/CoreServices.h>
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/mac/mac_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "cef/grit/cef_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "net/base/mime_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/strings/grit/ui_strings.h"
+
+namespace {
+
+base::string16 GetDescriptionFromMimeType(const std::string& mime_type) {
+  // Check for wild card mime types and return an appropriate description.
+  static const struct {
+    const char* mime_type;
+    int string_id;
+  } kWildCardMimeTypes[] = {
+      {"audio", IDS_AUDIO_FILES},
+      {"image", IDS_IMAGE_FILES},
+      {"text", IDS_TEXT_FILES},
+      {"video", IDS_VIDEO_FILES},
+  };
+
+  for (size_t i = 0; i < base::size(kWildCardMimeTypes); ++i) {
+    if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
+      return l10n_util::GetStringUTF16(kWildCardMimeTypes[i].string_id);
+  }
+
+  return base::string16();
+}
+
+void AddFilters(NSPopUpButton* button,
+                const std::vector<base::string16>& accept_filters,
+                bool include_all_files,
+                std::vector<std::vector<base::string16>>* all_extensions) {
+  for (size_t i = 0; i < accept_filters.size(); ++i) {
+    const base::string16& filter = accept_filters[i];
+    if (filter.empty())
+      continue;
+
+    std::vector<base::string16> extensions;
+    base::string16 description;
+
+    size_t sep_index = filter.find('|');
+    if (sep_index != std::string::npos) {
+      // Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
+      description = filter.substr(0, sep_index);
+
+      const std::vector<base::string16>& ext = base::SplitString(
+          filter.substr(sep_index + 1), base::ASCIIToUTF16(";"),
+          base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+      for (size_t x = 0; x < ext.size(); ++x) {
+        const base::string16& file_ext = ext[x];
+        if (!file_ext.empty() && file_ext[0] == '.')
+          extensions.push_back(file_ext);
+      }
+    } else if (filter[0] == '.') {
+      // Treat as an extension beginning with the '.' character.
+      extensions.push_back(filter);
+    } else {
+      // Otherwise convert mime type to one or more extensions.
+      const std::string& ascii = base::UTF16ToASCII(filter);
+      std::vector<base::FilePath::StringType> ext;
+      net::GetExtensionsForMimeType(ascii, &ext);
+      if (!ext.empty()) {
+        for (size_t x = 0; x < ext.size(); ++x)
+          extensions.push_back(base::ASCIIToUTF16("." + ext[x]));
+        description = GetDescriptionFromMimeType(ascii);
+      }
+    }
+
+    if (extensions.empty())
+      continue;
+
+    // Don't display a crazy number of extensions since the NSPopUpButton width
+    // will keep growing.
+    const size_t kMaxExtensions = 10;
+
+    base::string16 ext_str;
+    for (size_t x = 0; x < std::min(kMaxExtensions, extensions.size()); ++x) {
+      const base::string16& pattern = base::ASCIIToUTF16("*") + extensions[x];
+      if (x != 0)
+        ext_str += base::ASCIIToUTF16(";");
+      ext_str += pattern;
+    }
+
+    if (extensions.size() > kMaxExtensions)
+      ext_str += base::ASCIIToUTF16(";...");
+
+    if (description.empty()) {
+      description = ext_str;
+    } else {
+      description +=
+          base::ASCIIToUTF16(" (") + ext_str + base::ASCIIToUTF16(")");
+    }
+
+    [button addItemWithTitle:base::SysUTF16ToNSString(description)];
+
+    all_extensions->push_back(extensions);
+  }
+
+  // Add the *.* filter, but only if we have added other filters (otherwise it
+  // is implied).
+  if (include_all_files && !all_extensions->empty()) {
+    [button addItemWithTitle:base::SysUTF8ToNSString("All Files (*)")];
+    all_extensions->push_back(std::vector<base::string16>());
+  }
+}
+
+}  // namespace
+
+// Used to manage the file type filter in the NSSavePanel/NSOpenPanel.
+@interface CefFilterDelegate : NSObject {
+ @private
+  NSSavePanel* panel_;
+  std::vector<std::vector<base::string16>> extensions_;
+  int selected_index_;
+}
+- (id)initWithPanel:(NSSavePanel*)panel
+    andAcceptFilters:(const std::vector<base::string16>&)accept_filters
+      andFilterIndex:(int)index;
+- (void)setFilter:(int)index;
+- (int)filter;
+- (void)filterSelectionChanged:(id)sender;
+- (void)setFileExtension;
+@end
+
+@implementation CefFilterDelegate
+
+- (id)initWithPanel:(NSSavePanel*)panel
+    andAcceptFilters:(const std::vector<base::string16>&)accept_filters
+      andFilterIndex:(int)index {
+  if (self = [super init]) {
+    DCHECK(panel);
+    panel_ = panel;
+    selected_index_ = 0;
+
+    NSPopUpButton* button = [[NSPopUpButton alloc] init];
+    AddFilters(button, accept_filters, true, &extensions_);
+    [button sizeToFit];
+    [button setTarget:self];
+    [button setAction:@selector(filterSelectionChanged:)];
+
+    if (index < static_cast<int>(extensions_.size())) {
+      [button selectItemAtIndex:index];
+      [self setFilter:index];
+    }
+
+    [panel_ setAccessoryView:button];
+  }
+  return self;
+}
+
+// Set the current filter index.
+- (void)setFilter:(int)index {
+  DCHECK(index >= 0 && index < static_cast<int>(extensions_.size()));
+  selected_index_ = index;
+
+  // Set the selectable file types. For open panels this limits the files that
+  // can be selected. For save panels this applies a default file extenion when
+  // the dialog is dismissed if none is already provided.
+  NSMutableArray* acceptArray = nil;
+  if (!extensions_[index].empty()) {
+    acceptArray = [[NSMutableArray alloc] init];
+    for (size_t i = 0; i < extensions_[index].size(); ++i) {
+      [acceptArray
+          addObject:base::SysUTF16ToNSString(extensions_[index][i].substr(1))];
+    }
+  }
+  [panel_ setAllowedFileTypes:acceptArray];
+
+  if (![panel_ isKindOfClass:[NSOpenPanel class]]) {
+    // For save panels set the file extension.
+    [self setFileExtension];
+  }
+}
+
+// Returns the current filter index.
+- (int)filter {
+  return selected_index_;
+}
+
+// Called when the selected filter is changed via the NSPopUpButton.
+- (void)filterSelectionChanged:(id)sender {
+  NSPopUpButton* button = (NSPopUpButton*)sender;
+  [self setFilter:[button indexOfSelectedItem]];
+}
+
+// Set the extension on the currently selected file name.
+- (void)setFileExtension {
+  const std::vector<base::string16>& filter = extensions_[selected_index_];
+  if (filter.empty()) {
+    // All extensions are allowed so don't change anything.
+    return;
+  }
+
+  base::FilePath path(base::SysNSStringToUTF8([panel_ nameFieldStringValue]));
+
+  // If the file name currently includes an extension from |filter| then don't
+  // change anything.
+  base::string16 extension = base::UTF8ToUTF16(path.Extension());
+  if (!extension.empty()) {
+    for (size_t i = 0; i < filter.size(); ++i) {
+      if (filter[i] == extension)
+        return;
+    }
+  }
+
+  // Change the extension to the first value in |filter|.
+  path = path.ReplaceExtension(base::UTF16ToUTF8(filter[0]));
+  [panel_ setNameFieldStringValue:base::SysUTF8ToNSString(path.value())];
+}
+
+@end
+
+CefFileDialogRunnerMac::CefFileDialogRunnerMac() : weak_ptr_factory_(this) {}
+
+void CefFileDialogRunnerMac::Run(CefBrowserHostImpl* browser,
+                                 const FileChooserParams& params,
+                                 RunFileChooserCallback callback) {
+  callback_ = std::move(callback);
+
+  int filter_index = params.selected_accept_filter;
+  NSView* owner = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetWindowHandle());
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+
+  if (params.mode == blink::mojom::FileChooserParams::Mode::kOpen ||
+      params.mode == blink::mojom::FileChooserParams::Mode::kOpenMultiple ||
+      params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
+    RunOpenFileDialog(weak_this, params, owner, filter_index);
+  } else if (params.mode == blink::mojom::FileChooserParams::Mode::kSave) {
+    RunSaveFileDialog(weak_this, params, owner, filter_index);
+  } else {
+    NOTIMPLEMENTED();
+  }
+}
+
+// static
+void CefFileDialogRunnerMac::RunOpenFileDialog(
+    base::WeakPtr<CefFileDialogRunnerMac> weak_this,
+    const CefFileDialogRunner::FileChooserParams& params,
+    NSView* view,
+    int filter_index) {
+  NSOpenPanel* openPanel = [NSOpenPanel openPanel];
+
+  base::string16 title;
+  if (!params.title.empty()) {
+    title = params.title;
+  } else {
+    title = l10n_util::GetStringUTF16(
+        params.mode == blink::mojom::FileChooserParams::Mode::kOpen
+            ? IDS_OPEN_FILE_DIALOG_TITLE
+            : (params.mode ==
+                       blink::mojom::FileChooserParams::Mode::kOpenMultiple
+                   ? IDS_OPEN_FILES_DIALOG_TITLE
+                   : IDS_SELECT_FOLDER_DIALOG_TITLE));
+  }
+  [openPanel setTitle:base::SysUTF16ToNSString(title)];
+
+  std::string filename, directory;
+  if (!params.default_file_name.empty()) {
+    if (params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder ||
+        params.default_file_name.EndsWithSeparator()) {
+      // The value is only a directory.
+      directory = params.default_file_name.value();
+    } else {
+      // The value is a file name and possibly a directory.
+      filename = params.default_file_name.BaseName().value();
+      directory = params.default_file_name.DirName().value();
+    }
+  }
+  if (!filename.empty()) {
+    [openPanel setNameFieldStringValue:base::SysUTF8ToNSString(filename)];
+  }
+  if (!directory.empty()) {
+    [openPanel setDirectoryURL:[NSURL fileURLWithPath:base::SysUTF8ToNSString(
+                                                          directory)]];
+  }
+
+  CefFilterDelegate* filter_delegate = nil;
+  if (params.mode != blink::mojom::FileChooserParams::Mode::kUploadFolder &&
+      !params.accept_types.empty()) {
+    // Add the file filter control.
+    filter_delegate =
+        [[CefFilterDelegate alloc] initWithPanel:openPanel
+                                andAcceptFilters:params.accept_types
+                                  andFilterIndex:filter_index];
+  }
+
+  // Further panel configuration.
+  [openPanel setAllowsOtherFileTypes:YES];
+  [openPanel setAllowsMultipleSelection:
+                 (params.mode ==
+                  blink::mojom::FileChooserParams::Mode::kOpenMultiple)];
+  [openPanel
+      setCanChooseFiles:(params.mode !=
+                         blink::mojom::FileChooserParams::Mode::kUploadFolder)];
+  [openPanel
+      setCanChooseDirectories:(params.mode == blink::mojom::FileChooserParams::
+                                                  Mode::kUploadFolder)];
+  [openPanel setShowsHiddenFiles:!params.hidereadonly];
+
+  // Show panel.
+  [openPanel
+      beginSheetModalForWindow:[view window]
+             completionHandler:^(NSInteger returnCode) {
+               int filter_index_to_use = (filter_delegate != nil)
+                                             ? [filter_delegate filter]
+                                             : filter_index;
+               if (returnCode == NSFileHandlingPanelOKButton) {
+                 std::vector<base::FilePath> files;
+                 files.reserve(openPanel.URLs.count);
+                 for (NSURL* url in openPanel.URLs) {
+                   if (url.isFileURL)
+                     files.push_back(base::FilePath(url.path.UTF8String));
+                 }
+                 std::move(weak_this->callback_)
+                     .Run(filter_index_to_use, files);
+               } else {
+                 std::move(weak_this->callback_)
+                     .Run(filter_index_to_use, std::vector<base::FilePath>());
+               }
+             }];
+}
+
+// static
+void CefFileDialogRunnerMac::RunSaveFileDialog(
+    base::WeakPtr<CefFileDialogRunnerMac> weak_this,
+    const CefFileDialogRunner::FileChooserParams& params,
+    NSView* view,
+    int filter_index) {
+  NSSavePanel* savePanel = [NSSavePanel savePanel];
+
+  base::string16 title;
+  if (!params.title.empty())
+    title = params.title;
+  else
+    title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE);
+  [savePanel setTitle:base::SysUTF16ToNSString(title)];
+
+  std::string filename, directory;
+  if (!params.default_file_name.empty()) {
+    if (params.default_file_name.EndsWithSeparator()) {
+      // The value is only a directory.
+      directory = params.default_file_name.value();
+    } else {
+      // The value is a file name and possibly a directory.
+      filename = params.default_file_name.BaseName().value();
+      directory = params.default_file_name.DirName().value();
+    }
+  }
+  if (!filename.empty()) {
+    [savePanel setNameFieldStringValue:base::SysUTF8ToNSString(filename)];
+  }
+  if (!directory.empty()) {
+    [savePanel setDirectoryURL:[NSURL fileURLWithPath:base::SysUTF8ToNSString(
+                                                          directory)]];
+  }
+
+  CefFilterDelegate* filter_delegate = nil;
+  if (!params.accept_types.empty()) {
+    // Add the file filter control.
+    filter_delegate =
+        [[CefFilterDelegate alloc] initWithPanel:savePanel
+                                andAcceptFilters:params.accept_types
+                                  andFilterIndex:filter_index];
+  }
+
+  [savePanel setAllowsOtherFileTypes:YES];
+  [savePanel setShowsHiddenFiles:!params.hidereadonly];
+
+  // Show panel.
+  [savePanel
+      beginSheetModalForWindow:view.window
+             completionHandler:^(NSInteger resultCode) {
+               int filter_index_to_use = (filter_delegate != nil)
+                                             ? [filter_delegate filter]
+                                             : filter_index;
+               if (resultCode == NSFileHandlingPanelOKButton) {
+                 NSURL* url = savePanel.URL;
+                 const char* path = url.path.UTF8String;
+                 std::vector<base::FilePath> files(1, base::FilePath(path));
+                 std::move(weak_this->callback_)
+                     .Run(filter_index_to_use, files);
+               } else {
+                 std::move(weak_this->callback_)
+                     .Run(filter_index_to_use, std::vector<base::FilePath>());
+               }
+             }];
+}
diff --git a/src/libcef/browser/native/file_dialog_runner_win.cc b/src/libcef/browser/native/file_dialog_runner_win.cc
new file mode 100644
index 0000000..83feeed
--- /dev/null
+++ b/src/libcef/browser/native/file_dialog_runner_win.cc
@@ -0,0 +1,524 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/file_dialog_runner_win.h"
+
+#include <windows.h>
+
+#include <commdlg.h>
+#include <shlobj.h>
+#include <wrl/client.h>
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/files/file_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+#include "cef/grit/cef_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "net/base/mime_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/win/shell.h"
+#include "ui/strings/grit/ui_strings.h"
+
+namespace {
+
+// From ui/base/dialogs/select_file_dialog_win.cc.
+
+// Get the file type description from the registry. This will be "Text Document"
+// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't
+// have an entry for the file type, we return false, true if the description was
+// found. 'file_ext' must be in form ".txt".
+static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext,
+                                                std::wstring* reg_description) {
+  DCHECK(reg_description);
+  base::win::RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ);
+  std::wstring reg_app;
+  if (reg_ext.ReadValue(NULL, &reg_app) == ERROR_SUCCESS && !reg_app.empty()) {
+    base::win::RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ);
+    if (reg_link.ReadValue(NULL, reg_description) == ERROR_SUCCESS)
+      return true;
+  }
+  return false;
+}
+
+// Set up a filter for a Save/Open dialog, which will consist of |file_ext| file
+// extensions (internally separated by semicolons), |ext_desc| as the text
+// descriptions of the |file_ext| types (optional), and (optionally) the default
+// 'All Files' view. The purpose of the filter is to show only files of a
+// particular type in a Windows Save/Open dialog box. The resulting filter is
+// returned. The filters created here are:
+//   1. only files that have 'file_ext' as their extension
+//   2. all files (only added if 'include_all_files' is true)
+// Example:
+//   file_ext: { "*.txt", "*.htm;*.html" }
+//   ext_desc: { "Text Document" }
+//   returned: "Text Document\0*.txt\0HTML Document\0*.htm;*.html\0"
+//             "All Files\0*.*\0\0" (in one big string)
+// If a description is not provided for a file extension, it will be retrieved
+// from the registry. If the file extension does not exist in the registry, it
+// will be omitted from the filter, as it is likely a bogus extension.
+std::wstring FormatFilterForExtensions(
+    const std::vector<std::wstring>& file_ext,
+    const std::vector<std::wstring>& ext_desc,
+    bool include_all_files) {
+  const std::wstring all_ext = L"*.*";
+  const std::wstring all_desc =
+      l10n_util::GetStringUTF16(IDS_APP_SAVEAS_ALL_FILES) + L" (" + all_ext +
+      L")";
+
+  DCHECK(file_ext.size() >= ext_desc.size());
+
+  if (file_ext.empty())
+    include_all_files = true;
+
+  std::wstring result;
+
+  for (size_t i = 0; i < file_ext.size(); ++i) {
+    std::wstring ext = file_ext[i];
+    std::wstring desc;
+    if (i < ext_desc.size())
+      desc = ext_desc[i];
+
+    if (ext.empty()) {
+      // Force something reasonable to appear in the dialog box if there is no
+      // extension provided.
+      include_all_files = true;
+      continue;
+    }
+
+    if (desc.empty()) {
+      DCHECK(ext.find(L'.') != std::wstring::npos);
+      std::wstring first_extension = ext.substr(ext.find(L'.'));
+      size_t first_separator_index = first_extension.find(L';');
+      if (first_separator_index != std::wstring::npos)
+        first_extension = first_extension.substr(0, first_separator_index);
+
+      // Find the extension name without the preceeding '.' character.
+      std::wstring ext_name = first_extension;
+      size_t ext_index = ext_name.find_first_not_of(L'.');
+      if (ext_index != std::wstring::npos)
+        ext_name = ext_name.substr(ext_index);
+
+      if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
+        // The extension doesn't exist in the registry.
+        include_all_files = true;
+      }
+    }
+
+    if (!desc.empty())
+      desc += L" (" + ext + L")";
+    else
+      desc = ext;
+
+    result.append(desc.c_str(), desc.size() + 1);  // Append NULL too.
+    result.append(ext.c_str(), ext.size() + 1);
+  }
+
+  if (include_all_files) {
+    result.append(all_desc.c_str(), all_desc.size() + 1);
+    result.append(all_ext.c_str(), all_ext.size() + 1);
+  }
+
+  result.append(1, '\0');  // Double NULL required.
+  return result;
+}
+
+std::wstring GetDescriptionFromMimeType(const std::string& mime_type) {
+  // Check for wild card mime types and return an appropriate description.
+  static const struct {
+    const char* mime_type;
+    int string_id;
+  } kWildCardMimeTypes[] = {
+      {"audio", IDS_AUDIO_FILES},
+      {"image", IDS_IMAGE_FILES},
+      {"text", IDS_TEXT_FILES},
+      {"video", IDS_VIDEO_FILES},
+  };
+
+  for (size_t i = 0; i < base::size(kWildCardMimeTypes); ++i) {
+    if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
+      return l10n_util::GetStringUTF16(kWildCardMimeTypes[i].string_id);
+  }
+
+  return std::wstring();
+}
+
+std::wstring GetFilterString(
+    const std::vector<base::string16>& accept_filters) {
+  std::vector<std::wstring> extensions;
+  std::vector<std::wstring> descriptions;
+
+  for (size_t i = 0; i < accept_filters.size(); ++i) {
+    const base::string16& filter = accept_filters[i];
+    if (filter.empty())
+      continue;
+
+    size_t sep_index = filter.find('|');
+    if (sep_index != base::string16::npos) {
+      // Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
+      const base::string16& desc = filter.substr(0, sep_index);
+      const std::vector<base::string16>& ext = base::SplitString(
+          filter.substr(sep_index + 1), base::ASCIIToUTF16(";"),
+          base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+      std::wstring ext_str;
+      for (size_t x = 0; x < ext.size(); ++x) {
+        const base::string16& file_ext = ext[x];
+        if (!file_ext.empty() && file_ext[0] == '.') {
+          if (!ext_str.empty())
+            ext_str += L";";
+          ext_str += L"*" + file_ext;
+        }
+      }
+      if (!ext_str.empty()) {
+        extensions.push_back(ext_str);
+        descriptions.push_back(desc);
+      }
+    } else if (filter[0] == L'.') {
+      // Treat as an extension beginning with the '.' character.
+      extensions.push_back(L"*" + filter);
+      descriptions.push_back(std::wstring());
+    } else {
+      // Otherwise convert mime type to one or more extensions.
+      const std::string& ascii = base::UTF16ToASCII(filter);
+      std::vector<base::FilePath::StringType> ext;
+      std::wstring ext_str;
+      net::GetExtensionsForMimeType(ascii, &ext);
+      if (!ext.empty()) {
+        for (size_t x = 0; x < ext.size(); ++x) {
+          if (x != 0)
+            ext_str += L";";
+          ext_str += L"*." + ext[x];
+        }
+        extensions.push_back(ext_str);
+        descriptions.push_back(GetDescriptionFromMimeType(ascii));
+      }
+    }
+  }
+
+  return FormatFilterForExtensions(extensions, descriptions, true);
+}
+
+// From chrome/browser/views/shell_dialogs_win.cc
+
+bool RunOpenFileDialog(const CefFileDialogRunner::FileChooserParams& params,
+                       HWND owner,
+                       int* filter_index,
+                       base::FilePath* path) {
+  OPENFILENAME ofn;
+
+  // We must do this otherwise the ofn's FlagsEx may be initialized to random
+  // junk in release builds which can cause the Places Bar not to show up!
+  ZeroMemory(&ofn, sizeof(ofn));
+  ofn.lStructSize = sizeof(ofn);
+  ofn.hwndOwner = owner;
+
+  wchar_t filename[MAX_PATH] = {0};
+
+  ofn.lpstrFile = filename;
+  ofn.nMaxFile = MAX_PATH;
+
+  std::wstring directory;
+  if (!params.default_file_name.empty()) {
+    if (params.default_file_name.EndsWithSeparator()) {
+      // The value is only a directory.
+      directory = params.default_file_name.value();
+    } else {
+      // The value is a file name and possibly a directory.
+      base::wcslcpy(filename, params.default_file_name.value().c_str(),
+                    base::size(filename));
+      directory = params.default_file_name.DirName().value();
+    }
+  }
+  if (!directory.empty())
+    ofn.lpstrInitialDir = directory.c_str();
+
+  std::wstring title;
+  if (!params.title.empty())
+    title = params.title;
+  else
+    title = l10n_util::GetStringUTF16(IDS_OPEN_FILE_DIALOG_TITLE);
+  if (!title.empty())
+    ofn.lpstrTitle = title.c_str();
+
+  // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory
+  // without having to close Chrome first.
+  ofn.Flags =
+      OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_ENABLESIZING;
+  if (params.hidereadonly)
+    ofn.Flags |= OFN_HIDEREADONLY;
+
+  const std::wstring& filter = GetFilterString(params.accept_types);
+  if (!filter.empty()) {
+    ofn.lpstrFilter = filter.c_str();
+    // Indices into |lpstrFilter| start at 1.
+    ofn.nFilterIndex = *filter_index + 1;
+  }
+
+  bool success = !!GetOpenFileName(&ofn);
+  if (success) {
+    *filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
+    *path = base::FilePath(filename);
+  }
+  return success;
+}
+
+bool RunOpenMultiFileDialog(
+    const CefFileDialogRunner::FileChooserParams& params,
+    HWND owner,
+    int* filter_index,
+    std::vector<base::FilePath>* paths) {
+  OPENFILENAME ofn;
+
+  // We must do this otherwise the ofn's FlagsEx may be initialized to random
+  // junk in release builds which can cause the Places Bar not to show up!
+  ZeroMemory(&ofn, sizeof(ofn));
+  ofn.lStructSize = sizeof(ofn);
+  ofn.hwndOwner = owner;
+
+  std::unique_ptr<wchar_t[]> filename(new wchar_t[UNICODE_STRING_MAX_CHARS]);
+  filename[0] = 0;
+
+  ofn.lpstrFile = filename.get();
+  ofn.nMaxFile = UNICODE_STRING_MAX_CHARS;
+
+  std::wstring directory;
+  if (!params.default_file_name.empty()) {
+    if (params.default_file_name.EndsWithSeparator()) {
+      // The value is only a directory.
+      directory = params.default_file_name.value();
+    } else {
+      // The value is a file name and possibly a directory.
+      directory = params.default_file_name.DirName().value();
+    }
+  }
+  if (!directory.empty())
+    ofn.lpstrInitialDir = directory.c_str();
+
+  std::wstring title;
+  if (!params.title.empty())
+    title = params.title;
+  else
+    title = l10n_util::GetStringUTF16(IDS_OPEN_FILES_DIALOG_TITLE);
+  if (!title.empty())
+    ofn.lpstrTitle = title.c_str();
+
+  // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory
+  // without having to close Chrome first.
+  ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER |
+              OFN_ALLOWMULTISELECT | OFN_ENABLESIZING;
+  if (params.hidereadonly)
+    ofn.Flags |= OFN_HIDEREADONLY;
+
+  const std::wstring& filter = GetFilterString(params.accept_types);
+  if (!filter.empty()) {
+    ofn.lpstrFilter = filter.c_str();
+    // Indices into |lpstrFilter| start at 1.
+    ofn.nFilterIndex = *filter_index + 1;
+  }
+
+  bool success = !!GetOpenFileName(&ofn);
+
+  if (success) {
+    std::vector<base::FilePath> files;
+    const wchar_t* selection = ofn.lpstrFile;
+    while (*selection) {  // Empty string indicates end of list.
+      files.push_back(base::FilePath(selection));
+      // Skip over filename and null-terminator.
+      selection += files.back().value().length() + 1;
+    }
+    if (files.empty()) {
+      success = false;
+    } else if (files.size() == 1) {
+      // When there is one file, it contains the path and filename.
+      paths->swap(files);
+    } else {
+      // Otherwise, the first string is the path, and the remainder are
+      // filenames.
+      std::vector<base::FilePath>::iterator path = files.begin();
+      for (std::vector<base::FilePath>::iterator file = path + 1;
+           file != files.end(); ++file) {
+        paths->push_back(path->Append(*file));
+      }
+    }
+  }
+
+  if (success)
+    *filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
+
+  return success;
+}
+
+// The callback function for when the select folder dialog is opened.
+int CALLBACK BrowseCallbackProc(HWND window,
+                                UINT message,
+                                LPARAM parameter,
+                                LPARAM data) {
+  if (message == BFFM_INITIALIZED) {
+    // WParam is TRUE since passing a path.
+    // data lParam member of the BROWSEINFO structure.
+    SendMessage(window, BFFM_SETSELECTION, TRUE, (LPARAM)data);
+  }
+  return 0;
+}
+
+bool RunOpenFolderDialog(const CefFileDialogRunner::FileChooserParams& params,
+                         HWND owner,
+                         base::FilePath* path) {
+  wchar_t dir_buffer[MAX_PATH + 1] = {0};
+
+  bool result = false;
+  BROWSEINFO browse_info = {0};
+  browse_info.hwndOwner = owner;
+  browse_info.pszDisplayName = dir_buffer;
+  browse_info.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
+
+  std::wstring title;
+  if (!params.title.empty())
+    title = params.title;
+  else
+    title = l10n_util::GetStringUTF16(IDS_SELECT_FOLDER_DIALOG_TITLE);
+  if (!title.empty())
+    browse_info.lpszTitle = title.c_str();
+
+  const std::wstring& file_path = params.default_file_name.value();
+  if (!file_path.empty()) {
+    // Highlight the current value.
+    browse_info.lParam = (LPARAM)file_path.c_str();
+    browse_info.lpfn = &BrowseCallbackProc;
+  }
+
+  LPITEMIDLIST list = SHBrowseForFolder(&browse_info);
+  if (list) {
+    STRRET out_dir_buffer;
+    ZeroMemory(&out_dir_buffer, sizeof(out_dir_buffer));
+    out_dir_buffer.uType = STRRET_WSTR;
+    Microsoft::WRL::ComPtr<IShellFolder> shell_folder;
+    if (SHGetDesktopFolder(shell_folder.GetAddressOf()) == NOERROR) {
+      HRESULT hr = shell_folder->GetDisplayNameOf(list, SHGDN_FORPARSING,
+                                                  &out_dir_buffer);
+      if (SUCCEEDED(hr) && out_dir_buffer.uType == STRRET_WSTR) {
+        *path = base::FilePath(out_dir_buffer.pOleStr);
+        CoTaskMemFree(out_dir_buffer.pOleStr);
+        result = true;
+      } else {
+        // Use old way if we don't get what we want.
+        wchar_t old_out_dir_buffer[MAX_PATH + 1];
+        if (SHGetPathFromIDList(list, old_out_dir_buffer)) {
+          *path = base::FilePath(old_out_dir_buffer);
+          result = true;
+        }
+      }
+    }
+    CoTaskMemFree(list);
+  }
+
+  return result;
+}
+
+bool RunSaveFileDialog(const CefFileDialogRunner::FileChooserParams& params,
+                       HWND owner,
+                       int* filter_index,
+                       base::FilePath* path) {
+  OPENFILENAME ofn;
+
+  // We must do this otherwise the ofn's FlagsEx may be initialized to random
+  // junk in release builds which can cause the Places Bar not to show up!
+  ZeroMemory(&ofn, sizeof(ofn));
+  ofn.lStructSize = sizeof(ofn);
+  ofn.hwndOwner = owner;
+
+  wchar_t filename[MAX_PATH] = {0};
+
+  ofn.lpstrFile = filename;
+  ofn.nMaxFile = MAX_PATH;
+
+  std::wstring directory;
+  if (!params.default_file_name.empty()) {
+    if (params.default_file_name.EndsWithSeparator()) {
+      // The value is only a directory.
+      directory = params.default_file_name.value();
+    } else {
+      // The value is a file name and possibly a directory.
+      base::wcslcpy(filename, params.default_file_name.value().c_str(),
+                    base::size(filename));
+      directory = params.default_file_name.DirName().value();
+    }
+  }
+  if (!directory.empty())
+    ofn.lpstrInitialDir = directory.c_str();
+
+  std::wstring title;
+  if (!params.title.empty())
+    title = params.title;
+  else
+    title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE);
+  if (!title.empty())
+    ofn.lpstrTitle = title.c_str();
+
+  // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory
+  // without having to close Chrome first.
+  ofn.Flags =
+      OFN_EXPLORER | OFN_ENABLESIZING | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
+  if (params.hidereadonly)
+    ofn.Flags |= OFN_HIDEREADONLY;
+  if (params.overwriteprompt)
+    ofn.Flags |= OFN_OVERWRITEPROMPT;
+
+  const std::wstring& filter = GetFilterString(params.accept_types);
+  if (!filter.empty()) {
+    ofn.lpstrFilter = filter.c_str();
+    // Indices into |lpstrFilter| start at 1.
+    ofn.nFilterIndex = *filter_index + 1;
+    // If a filter is specified and the default file name is changed then append
+    // a file extension to the new name.
+    ofn.lpstrDefExt = L"";
+  }
+
+  bool success = !!GetSaveFileName(&ofn);
+  if (success) {
+    *filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
+    *path = base::FilePath(filename);
+  }
+  return success;
+}
+
+}  // namespace
+
+CefFileDialogRunnerWin::CefFileDialogRunnerWin() {}
+
+void CefFileDialogRunnerWin::Run(CefBrowserHostImpl* browser,
+                                 const FileChooserParams& params,
+                                 RunFileChooserCallback callback) {
+  int filter_index = params.selected_accept_filter;
+  std::vector<base::FilePath> files;
+
+  HWND owner = browser->GetWindowHandle();
+
+  if (params.mode == blink::mojom::FileChooserParams::Mode::kOpen) {
+    base::FilePath file;
+    if (RunOpenFileDialog(params, owner, &filter_index, &file))
+      files.push_back(file);
+  } else if (params.mode ==
+             blink::mojom::FileChooserParams::Mode::kOpenMultiple) {
+    RunOpenMultiFileDialog(params, owner, &filter_index, &files);
+  } else if (params.mode ==
+             blink::mojom::FileChooserParams::Mode::kUploadFolder) {
+    base::FilePath file;
+    if (RunOpenFolderDialog(params, owner, &file))
+      files.push_back(file);
+  } else if (params.mode == blink::mojom::FileChooserParams::Mode::kSave) {
+    base::FilePath file;
+    if (RunSaveFileDialog(params, owner, &filter_index, &file))
+      files.push_back(file);
+  } else {
+    NOTIMPLEMENTED();
+  }
+
+  std::move(callback).Run(filter_index, files);
+}
diff --git a/src/libcef/browser/native/file_dialog_runner_win.h b/src/libcef/browser/native/file_dialog_runner_win.h
new file mode 100644
index 0000000..78c21c0
--- /dev/null
+++ b/src/libcef/browser/native/file_dialog_runner_win.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_WIN_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_WIN_H_
+#pragma once
+
+#include "libcef/browser/file_dialog_runner.h"
+
+class CefFileDialogRunnerWin : public CefFileDialogRunner {
+ public:
+  CefFileDialogRunnerWin();
+
+  // CefFileDialogRunner methods:
+  void Run(CefBrowserHostImpl* browser,
+           const FileChooserParams& params,
+           RunFileChooserCallback callback) override;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_WIN_H_
diff --git a/src/libcef/browser/native/javascript_dialog_runner_mac.h b/src/libcef/browser/native/javascript_dialog_runner_mac.h
new file mode 100644
index 0000000..0cd92ec
--- /dev/null
+++ b/src/libcef/browser/native/javascript_dialog_runner_mac.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_JAVASCRIPT_DIALOG_RUNNER_MAC_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_JAVASCRIPT_DIALOG_RUNNER_MAC_H_
+#pragma once
+
+#include "libcef/browser/javascript_dialog_runner.h"
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/weak_ptr.h"
+
+#if __OBJC__
+@class CefJavaScriptDialogHelper;
+#else
+class CefJavaScriptDialogHelper;
+#endif  // __OBJC__
+
+class CefJavaScriptDialogRunnerMac : public CefJavaScriptDialogRunner {
+ public:
+  CefJavaScriptDialogRunnerMac();
+  ~CefJavaScriptDialogRunnerMac() override;
+
+  // CefJavaScriptDialogRunner methods:
+  void Run(CefBrowserHostImpl* browser,
+           content::JavaScriptDialogType message_type,
+           const base::string16& display_url,
+           const base::string16& message_text,
+           const base::string16& default_prompt_text,
+           DialogClosedCallback callback) override;
+  void Cancel() override;
+
+  // Callback from CefJavaScriptDialogHelper when the dialog is closed.
+  void DialogClosed(bool success, const base::string16& user_input);
+
+ private:
+  DialogClosedCallback callback_;
+
+  base::scoped_nsobject<CefJavaScriptDialogHelper> helper_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<CefJavaScriptDialogRunnerMac> weak_ptr_factory_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_JAVASCRIPT_DIALOG_RUNNER_MAC_H_
diff --git a/src/libcef/browser/native/javascript_dialog_runner_mac.mm b/src/libcef/browser/native/javascript_dialog_runner_mac.mm
new file mode 100644
index 0000000..289a539
--- /dev/null
+++ b/src/libcef/browser/native/javascript_dialog_runner_mac.mm
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/javascript_dialog_runner_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/bind.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+
+// Helper object that receives the notification that the dialog/sheet is
+// going away. Is responsible for cleaning itself up.
+@interface CefJavaScriptDialogHelper : NSObject <NSAlertDelegate> {
+ @private
+  base::scoped_nsobject<NSAlert> alert_;
+  NSTextField* textField_;  // WEAK; owned by alert_
+
+  // Copies of the fields in CefJavaScriptDialog because they're private.
+  CefJavaScriptDialogRunner::DialogClosedCallback callback_;
+}
+
+- (id)initHelperWithCallback:
+    (CefJavaScriptDialogRunner::DialogClosedCallback)callback;
+- (NSAlert*)alert;
+- (NSTextField*)textField;
+- (void)alertDidEnd:(NSAlert*)alert
+         returnCode:(int)returnCode
+        contextInfo:(void*)contextInfo;
+- (void)cancel;
+
+@end
+
+@implementation CefJavaScriptDialogHelper
+
+- (id)initHelperWithCallback:
+    (CefJavaScriptDialogRunner::DialogClosedCallback)callback {
+  if (self = [super init])
+    callback_ = std::move(callback);
+
+  return self;
+}
+
+- (NSAlert*)alert {
+  alert_.reset([[NSAlert alloc] init]);
+  return alert_;
+}
+
+- (NSTextField*)textField {
+  textField_ = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 22)];
+  [[textField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail];
+  [alert_ setAccessoryView:textField_];
+  [[alert_ window] setInitialFirstResponder:textField_];
+  [textField_ release];
+
+  return textField_;
+}
+
+- (void)alertDidEnd:(NSAlert*)alert
+         returnCode:(int)returnCode
+        contextInfo:(void*)contextInfo {
+  if (returnCode == NSModalResponseStop)
+    return;
+
+  bool success = returnCode == NSAlertFirstButtonReturn;
+  base::string16 input;
+  if (textField_)
+    input = base::SysNSStringToUTF16([textField_ stringValue]);
+
+  std::move(callback_).Run(success, input);
+}
+
+- (void)cancel {
+  [NSApp endSheet:[alert_ window]];
+  alert_.reset();
+}
+
+@end
+
+CefJavaScriptDialogRunnerMac::CefJavaScriptDialogRunnerMac()
+    : weak_ptr_factory_(this) {}
+
+CefJavaScriptDialogRunnerMac::~CefJavaScriptDialogRunnerMac() {
+  Cancel();
+}
+
+void CefJavaScriptDialogRunnerMac::Run(
+    CefBrowserHostImpl* browser,
+    content::JavaScriptDialogType message_type,
+    const base::string16& display_url,
+    const base::string16& message_text,
+    const base::string16& default_prompt_text,
+    DialogClosedCallback callback) {
+  DCHECK(!helper_.get());
+  callback_ = std::move(callback);
+
+  bool text_field = message_type == content::JAVASCRIPT_DIALOG_TYPE_PROMPT;
+  bool one_button = message_type == content::JAVASCRIPT_DIALOG_TYPE_ALERT;
+
+  helper_.reset([[CefJavaScriptDialogHelper alloc]
+      initHelperWithCallback:base::BindOnce(
+                                 &CefJavaScriptDialogRunnerMac::DialogClosed,
+                                 weak_ptr_factory_.GetWeakPtr())]);
+
+  // Show the modal dialog.
+  NSAlert* alert = [helper_ alert];
+  NSTextField* field = nil;
+  if (text_field) {
+    field = [helper_ textField];
+    [field setStringValue:base::SysUTF16ToNSString(default_prompt_text)];
+  }
+  [alert setDelegate:helper_];
+  [alert setInformativeText:base::SysUTF16ToNSString(message_text)];
+
+  base::string16 label;
+  switch (message_type) {
+    case content::JAVASCRIPT_DIALOG_TYPE_ALERT:
+      label = base::ASCIIToUTF16("JavaScript Alert");
+      break;
+    case content::JAVASCRIPT_DIALOG_TYPE_PROMPT:
+      label = base::ASCIIToUTF16("JavaScript Prompt");
+      break;
+    case content::JAVASCRIPT_DIALOG_TYPE_CONFIRM:
+      label = base::ASCIIToUTF16("JavaScript Confirm");
+      break;
+  }
+  if (!display_url.empty())
+    label += base::ASCIIToUTF16(" - ") + display_url;
+
+  [alert setMessageText:base::SysUTF16ToNSString(label)];
+
+  [alert addButtonWithTitle:@"OK"];
+  if (!one_button) {
+    NSButton* other = [alert addButtonWithTitle:@"Cancel"];
+    [other setKeyEquivalent:@"\e"];
+  }
+
+  // Calling beginSheetModalForWindow:nil is wrong API usage. For now work
+  // around the "callee requires a non-null argument" error that occurs when
+  // building with the 10.11 SDK. See http://crbug.com/383820 for related
+  // discussion.
+  // We can't use the newer beginSheetModalForWindow:completionHandler: variant
+  // because it fails silently when passed a nil argument (see issue #2726).
+  id nilArg = nil;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  [alert beginSheetModalForWindow:nilArg  // nil here makes it app-modal
+                    modalDelegate:helper_
+                   didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
+                      contextInfo:this];
+#pragma clang diagnostic pop
+
+  if ([alert accessoryView])
+    [[alert window] makeFirstResponder:[alert accessoryView]];
+}
+
+void CefJavaScriptDialogRunnerMac::Cancel() {
+  if (helper_.get()) {
+    [helper_ cancel];
+    helper_.reset(nil);
+  }
+}
+
+void CefJavaScriptDialogRunnerMac::DialogClosed(
+    bool success,
+    const base::string16& user_input) {
+  helper_.reset(nil);
+  std::move(callback_).Run(success, user_input);
+}
diff --git a/src/libcef/browser/native/javascript_dialog_runner_win.cc b/src/libcef/browser/native/javascript_dialog_runner_win.cc
new file mode 100644
index 0000000..37b52eb
--- /dev/null
+++ b/src/libcef/browser/native/javascript_dialog_runner_win.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/javascript_dialog_runner_win.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef_dll/resource.h"
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+class CefJavaScriptDialogRunnerWin;
+
+HHOOK CefJavaScriptDialogRunnerWin::msg_hook_ = NULL;
+int CefJavaScriptDialogRunnerWin::msg_hook_user_count_ = 0;
+
+INT_PTR CALLBACK CefJavaScriptDialogRunnerWin::DialogProc(HWND dialog,
+                                                          UINT message,
+                                                          WPARAM wparam,
+                                                          LPARAM lparam) {
+  switch (message) {
+    case WM_INITDIALOG: {
+      SetWindowLongPtr(dialog, DWLP_USER, static_cast<LONG_PTR>(lparam));
+      CefJavaScriptDialogRunnerWin* owner =
+          reinterpret_cast<CefJavaScriptDialogRunnerWin*>(lparam);
+      owner->dialog_win_ = dialog;
+      SetDlgItemText(dialog, IDC_DIALOGTEXT, owner->message_text_.c_str());
+      if (owner->message_type_ == content::JAVASCRIPT_DIALOG_TYPE_PROMPT)
+        SetDlgItemText(dialog, IDC_PROMPTEDIT,
+                       owner->default_prompt_text_.c_str());
+      break;
+    }
+    case WM_CLOSE: {
+      CefJavaScriptDialogRunnerWin* owner =
+          reinterpret_cast<CefJavaScriptDialogRunnerWin*>(
+              GetWindowLongPtr(dialog, DWLP_USER));
+      if (owner) {
+        owner->CloseDialog(false, base::string16());
+
+        // No need for the system to call DestroyWindow() because it will be
+        // called by the Cancel() method.
+        return 0;
+      }
+      break;
+    }
+    case WM_COMMAND: {
+      CefJavaScriptDialogRunnerWin* owner =
+          reinterpret_cast<CefJavaScriptDialogRunnerWin*>(
+              GetWindowLongPtr(dialog, DWLP_USER));
+      base::string16 user_input;
+      bool finish = false;
+      bool result = false;
+      switch (LOWORD(wparam)) {
+        case IDOK:
+          finish = true;
+          result = true;
+          if (owner->message_type_ == content::JAVASCRIPT_DIALOG_TYPE_PROMPT) {
+            size_t length =
+                GetWindowTextLength(GetDlgItem(dialog, IDC_PROMPTEDIT)) + 1;
+            if (length > 1) {
+              GetDlgItemText(dialog, IDC_PROMPTEDIT,
+                             base::WriteInto(&user_input, length), length);
+            }
+          }
+          break;
+        case IDCANCEL:
+          finish = true;
+          result = false;
+          break;
+      }
+      if (finish) {
+        owner->CloseDialog(result, user_input);
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return 0;
+}
+
+CefJavaScriptDialogRunnerWin::CefJavaScriptDialogRunnerWin()
+    : dialog_win_(NULL), parent_win_(NULL), hook_installed_(false) {}
+
+CefJavaScriptDialogRunnerWin::~CefJavaScriptDialogRunnerWin() {
+  Cancel();
+}
+
+void CefJavaScriptDialogRunnerWin::Run(
+    CefBrowserHostImpl* browser,
+    content::JavaScriptDialogType message_type,
+    const base::string16& display_url,
+    const base::string16& message_text,
+    const base::string16& default_prompt_text,
+    DialogClosedCallback callback) {
+  DCHECK(!dialog_win_);
+
+  message_type_ = message_type;
+  message_text_ = message_text;
+  default_prompt_text_ = default_prompt_text;
+  callback_ = std::move(callback);
+
+  InstallMessageHook();
+  hook_installed_ = true;
+
+  int dialog_type;
+  if (message_type == content::JAVASCRIPT_DIALOG_TYPE_ALERT)
+    dialog_type = IDD_ALERT;
+  else if (message_type == content::JAVASCRIPT_DIALOG_TYPE_CONFIRM)
+    dialog_type = IDD_CONFIRM;
+  else  // JAVASCRIPT_DIALOG_TYPE_PROMPT
+    dialog_type = IDD_PROMPT;
+
+  base::FilePath file_path;
+  HMODULE hModule = NULL;
+
+  // Try to load the dialog from the DLL.
+  if (base::PathService::Get(base::DIR_MODULE, &file_path)) {
+    file_path = file_path.Append(L"libcef.dll");
+    hModule = ::GetModuleHandle(file_path.value().c_str());
+  }
+  if (!hModule)
+    hModule = ::GetModuleHandle(NULL);
+  DCHECK(hModule);
+
+  parent_win_ = GetAncestor(browser->GetWindowHandle(), GA_ROOT);
+  dialog_win_ =
+      CreateDialogParam(hModule, MAKEINTRESOURCE(dialog_type), parent_win_,
+                        DialogProc, reinterpret_cast<LPARAM>(this));
+  DCHECK(dialog_win_);
+
+  if (!display_url.empty()) {
+    // Add the display URL to the window title.
+    TCHAR text[64];
+    GetWindowText(dialog_win_, text, sizeof(text) / sizeof(TCHAR));
+
+    base::string16 new_window_text =
+        text + base::ASCIIToUTF16(" - ") + display_url;
+    SetWindowText(dialog_win_, new_window_text.c_str());
+  }
+
+  // Disable the parent window so the user can't interact with it.
+  if (IsWindowEnabled(parent_win_))
+    EnableWindow(parent_win_, FALSE);
+
+  ShowWindow(dialog_win_, SW_SHOWNORMAL);
+}
+
+void CefJavaScriptDialogRunnerWin::Cancel() {
+  // Re-enable the parent before closing the popup to avoid focus/activation/
+  // z-order issues.
+  if (parent_win_ && IsWindow(parent_win_) && !IsWindowEnabled(parent_win_)) {
+    EnableWindow(parent_win_, TRUE);
+    parent_win_ = NULL;
+  }
+
+  if (dialog_win_ && IsWindow(dialog_win_)) {
+    SetWindowLongPtr(dialog_win_, DWLP_USER, NULL);
+    DestroyWindow(dialog_win_);
+    dialog_win_ = NULL;
+  }
+
+  if (hook_installed_) {
+    UninstallMessageHook();
+    hook_installed_ = false;
+  }
+}
+
+void CefJavaScriptDialogRunnerWin::CloseDialog(
+    bool success,
+    const base::string16& user_input) {
+  // Run the callback first so that RenderProcessHostImpl::IsBlocked is
+  // cleared. Otherwise, RenderWidgetHostImpl::IsIgnoringInputEvents will
+  // return true and RenderWidgetHostViewAura::OnWindowFocused will fail to
+  // re-assign browser focus.
+  std::move(callback_).Run(success, user_input);
+  Cancel();
+}
+
+// static
+LRESULT CALLBACK CefJavaScriptDialogRunnerWin::GetMsgProc(int code,
+                                                          WPARAM wparam,
+                                                          LPARAM lparam) {
+  // Mostly borrowed from http://support.microsoft.com/kb/q187988/
+  // and http://www.codeproject.com/KB/atl/cdialogmessagehook.aspx.
+  LPMSG msg = reinterpret_cast<LPMSG>(lparam);
+  if (code >= 0 && wparam == PM_REMOVE && msg->message >= WM_KEYFIRST &&
+      msg->message <= WM_KEYLAST) {
+    HWND hwnd = GetActiveWindow();
+    if (::IsWindow(hwnd) && ::IsDialogMessage(hwnd, msg)) {
+      // The value returned from this hookproc is ignored, and it cannot
+      // be used to tell Windows the message has been handled. To avoid
+      // further processing, convert the message to WM_NULL before
+      // returning.
+      msg->hwnd = NULL;
+      msg->message = WM_NULL;
+      msg->lParam = 0L;
+      msg->wParam = 0;
+    }
+  }
+
+  // Passes the hook information to the next hook procedure in
+  // the current hook chain.
+  return ::CallNextHookEx(msg_hook_, code, wparam, lparam);
+}
+
+// static
+bool CefJavaScriptDialogRunnerWin::InstallMessageHook() {
+  msg_hook_user_count_++;
+
+  // Make sure we only call this once.
+  if (msg_hook_ != NULL)
+    return true;
+
+  msg_hook_ = ::SetWindowsHookEx(WH_GETMESSAGE,
+                                 &CefJavaScriptDialogRunnerWin::GetMsgProc,
+                                 NULL, GetCurrentThreadId());
+  DCHECK(msg_hook_ != NULL);
+  return msg_hook_ != NULL;
+}
+
+// static
+bool CefJavaScriptDialogRunnerWin::UninstallMessageHook() {
+  msg_hook_user_count_--;
+  DCHECK_GE(msg_hook_user_count_, 0);
+
+  if (msg_hook_user_count_ > 0)
+    return true;
+
+  DCHECK(msg_hook_ != NULL);
+  BOOL result = ::UnhookWindowsHookEx(msg_hook_);
+  DCHECK(result);
+  msg_hook_ = NULL;
+
+  return result != FALSE;
+}
diff --git a/src/libcef/browser/native/javascript_dialog_runner_win.h b/src/libcef/browser/native/javascript_dialog_runner_win.h
new file mode 100644
index 0000000..f8e33d8
--- /dev/null
+++ b/src/libcef/browser/native/javascript_dialog_runner_win.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_JAVASCRIPT_DIALOG_RUNNER_WIN_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_JAVASCRIPT_DIALOG_RUNNER_WIN_H_
+#pragma once
+
+#include <windows.h>
+
+#include "libcef/browser/javascript_dialog_runner.h"
+
+class CefJavaScriptDialogRunnerWin : public CefJavaScriptDialogRunner {
+ public:
+  CefJavaScriptDialogRunnerWin();
+  ~CefJavaScriptDialogRunnerWin() override;
+
+  // CefJavaScriptDialogRunner methods:
+  void Run(CefBrowserHostImpl* browser,
+           content::JavaScriptDialogType message_type,
+           const base::string16& display_url,
+           const base::string16& message_text,
+           const base::string16& default_prompt_text,
+           DialogClosedCallback callback) override;
+  void Cancel() override;
+
+ private:
+  void CloseDialog(bool success, const base::string16& user_input);
+
+  HWND dialog_win_;
+  HWND parent_win_;
+
+  content::JavaScriptDialogType message_type_;
+  base::string16 message_text_;
+  base::string16 default_prompt_text_;
+  DialogClosedCallback callback_;
+
+  bool hook_installed_;
+
+  static INT_PTR CALLBACK DialogProc(HWND dialog,
+                                     UINT message,
+                                     WPARAM wparam,
+                                     LPARAM lparam);
+
+  // Since the message loop we expect to run in isn't going to be nicely
+  // calling IsDialogMessage(), we need to hook the wnd proc and call it
+  // ourselves. See http://support.microsoft.com/kb/q187988/
+  static bool InstallMessageHook();
+  static bool UninstallMessageHook();
+  static LRESULT CALLBACK GetMsgProc(int code, WPARAM wparam, LPARAM lparam);
+  static HHOOK msg_hook_;
+  static int msg_hook_user_count_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_JAVASCRIPT_DIALOG_RUNNER_WIN_H_
diff --git a/src/libcef/browser/native/menu_2.cc b/src/libcef/browser/native/menu_2.cc
new file mode 100644
index 0000000..f3ea808
--- /dev/null
+++ b/src/libcef/browser/native/menu_2.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/menu_2.h"
+
+#include "ui/base/models/menu_model.h"
+
+namespace views {
+
+Menu2::Menu2(ui::MenuModel* model)
+    : model_(model), wrapper_(MenuWrapper::CreateWrapper(model)) {
+  Rebuild();
+}
+
+Menu2::~Menu2() {}
+
+HMENU Menu2::GetNativeMenu() const {
+  return wrapper_->GetNativeMenu();
+}
+
+void Menu2::RunMenuAt(const gfx::Point& point, Alignment alignment) {
+  wrapper_->RunMenuAt(point, alignment);
+}
+
+void Menu2::RunContextMenuAt(const gfx::Point& point) {
+  RunMenuAt(point, ALIGN_TOPLEFT);
+}
+
+void Menu2::CancelMenu() {
+  wrapper_->CancelMenu();
+}
+
+void Menu2::Rebuild() {
+  wrapper_->Rebuild(nullptr);
+}
+
+void Menu2::UpdateStates() {
+  wrapper_->UpdateStates();
+}
+
+MenuWrapper::MenuAction Menu2::GetMenuAction() const {
+  return wrapper_->GetMenuAction();
+}
+
+void Menu2::SetMinimumWidth(int width) {
+  wrapper_->SetMinimumWidth(width);
+}
+
+}  // namespace views
diff --git a/src/libcef/browser/native/menu_2.h b/src/libcef/browser/native/menu_2.h
new file mode 100644
index 0000000..7de1db0
--- /dev/null
+++ b/src/libcef/browser/native/menu_2.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_MENU_2_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_MENU_2_H_
+
+#include <memory>
+
+#include "libcef/browser/native/menu_wrapper.h"
+
+#include "base/macros.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+class MenuModel;
+}
+
+namespace views {
+
+// A menu. Populated from a model, and relies on a delegate to execute commands.
+//
+// WARNING: do NOT create and use Menu2 on the stack. Menu2 notifies the model
+// of selection AFTER a delay. This means that if use a Menu2 on the stack
+// ActivatedAt is never invoked.
+class Menu2 {
+ public:
+  // How the menu is aligned relative to the point it is shown at.
+  // The alignment is reversed by menu if text direction is right to left.
+  enum Alignment { ALIGN_TOPLEFT, ALIGN_TOPRIGHT };
+
+  // Creates a new menu populated with the contents of |model|.
+  // WARNING: this populates the menu on construction by invoking methods on
+  // the model. As such, it is typically not safe to use this as the model
+  // from the constructor. EG:
+  //   MyClass : menu_(this) {}
+  // is likely to have problems.
+  explicit Menu2(ui::MenuModel* model);
+  virtual ~Menu2();
+
+  // Runs the menu at the specified point. This method blocks until done.
+  // RunContextMenuAt is the same, but the alignment is the default for a
+  // context menu.
+  void RunMenuAt(const gfx::Point& point, Alignment alignment);
+  void RunContextMenuAt(const gfx::Point& point);
+
+  // Cancels the active menu.
+  void CancelMenu();
+
+  // Called when the model supplying data to this menu has changed, and the menu
+  // must be rebuilt.
+  void Rebuild();
+
+  // Called when the states of the menu items in the menu should be refreshed
+  // from the model.
+  void UpdateStates();
+
+  // For submenus.
+  HMENU GetNativeMenu() const;
+
+  // Get the result of the last call to RunMenuAt to determine whether an
+  // item was selected, the user navigated to a next or previous menu, or
+  // nothing.
+  MenuWrapper::MenuAction GetMenuAction() const;
+
+  // Accessors.
+  ui::MenuModel* model() const { return model_; }
+
+  // Sets the minimum width of the menu.
+  void SetMinimumWidth(int width);
+
+ private:
+  ui::MenuModel* model_;
+
+  // The object that actually implements the menu.
+  std::unique_ptr<MenuWrapper> wrapper_;
+
+  DISALLOW_COPY_AND_ASSIGN(Menu2);
+};
+
+}  // namespace views
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_MENU_2_H_
diff --git a/src/libcef/browser/native/menu_runner_linux.cc b/src/libcef/browser/native/menu_runner_linux.cc
new file mode 100644
index 0000000..72e8e46
--- /dev/null
+++ b/src/libcef/browser/native/menu_runner_linux.cc
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/native/menu_runner_linux.h"
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/compiler_specific.h"
+#include "base/strings/string_util.h"
+#include "ui/gfx/geometry/point.h"
+
+CefMenuRunnerLinux::CefMenuRunnerLinux() {}
+
+bool CefMenuRunnerLinux::RunContextMenu(
+    CefBrowserHostImpl* browser,
+    CefMenuModelImpl* model,
+    const content::ContextMenuParams& params) {
+  menu_.reset(
+      new views::MenuRunner(model->model(), views::MenuRunner::CONTEXT_MENU));
+
+  const gfx::Point& screen_point =
+      browser->GetScreenPoint(gfx::Point(params.x, params.y));
+
+  views::Widget* parent_widget = nullptr;
+  if (!browser->IsWindowless())
+    parent_widget = browser->GetWindowWidget();
+
+  menu_->RunMenuAt(parent_widget, nullptr, gfx::Rect(screen_point, gfx::Size()),
+                   views::MenuAnchorPosition::kTopRight, ui::MENU_SOURCE_NONE);
+
+  return true;
+}
+
+void CefMenuRunnerLinux::CancelContextMenu() {
+  if (menu_)
+    menu_->Cancel();
+}
+
+bool CefMenuRunnerLinux::FormatLabel(base::string16& label) {
+  // Remove the accelerator indicator (&) from label strings.
+  const char16 replace[] = {L'&', 0};
+  return base::ReplaceChars(label, replace, base::string16(), &label);
+}
diff --git a/src/libcef/browser/native/menu_runner_linux.h b/src/libcef/browser/native/menu_runner_linux.h
new file mode 100644
index 0000000..f48fc35
--- /dev/null
+++ b/src/libcef/browser/native/menu_runner_linux.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_LINUX_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_LINUX_H_
+#pragma once
+
+#include "libcef/browser/menu_runner.h"
+
+#include "ui/views/controls/menu/menu_runner.h"
+
+class CefMenuRunnerLinux : public CefMenuRunner {
+ public:
+  CefMenuRunnerLinux();
+
+  // CefMenuRunner methods.
+  bool RunContextMenu(CefBrowserHostImpl* browser,
+                      CefMenuModelImpl* model,
+                      const content::ContextMenuParams& params) override;
+  void CancelContextMenu() override;
+  bool FormatLabel(base::string16& label) override;
+
+ private:
+  std::unique_ptr<views::MenuRunner> menu_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_LINUX_H_
diff --git a/src/libcef/browser/native/menu_runner_mac.h b/src/libcef/browser/native/menu_runner_mac.h
new file mode 100644
index 0000000..8cb6a6d
--- /dev/null
+++ b/src/libcef/browser/native/menu_runner_mac.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_MAC_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_MAC_H_
+#pragma once
+
+#include "libcef/browser/menu_runner.h"
+
+#include "base/mac/scoped_nsobject.h"
+
+#if __OBJC__
+@class MenuControllerCocoa;
+#else
+class MenuControllerCocoa;
+#endif
+
+class CefMenuRunnerMac : public CefMenuRunner {
+ public:
+  CefMenuRunnerMac();
+  ~CefMenuRunnerMac() override;
+
+  // CefMenuRunner methods.
+  bool RunContextMenu(CefBrowserHostImpl* browser,
+                      CefMenuModelImpl* model,
+                      const content::ContextMenuParams& params) override;
+  void CancelContextMenu() override;
+
+ private:
+  base::scoped_nsobject<MenuControllerCocoa> menu_controller_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_MAC_H_
diff --git a/src/libcef/browser/native/menu_runner_mac.mm b/src/libcef/browser/native/menu_runner_mac.mm
new file mode 100644
index 0000000..7270a09
--- /dev/null
+++ b/src/libcef/browser/native/menu_runner_mac.mm
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/native/menu_runner_mac.h"
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/compiler_specific.h"
+#import "base/mac/scoped_sending_event.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
+#import "ui/base/cocoa/menu_controller.h"
+#include "ui/gfx/geometry/point.h"
+
+CefMenuRunnerMac::CefMenuRunnerMac() {}
+
+CefMenuRunnerMac::~CefMenuRunnerMac() {}
+
+bool CefMenuRunnerMac::RunContextMenu(
+    CefBrowserHostImpl* browser,
+    CefMenuModelImpl* model,
+    const content::ContextMenuParams& params) {
+  // Create a menu controller based on the model.
+  menu_controller_.reset([[MenuControllerCocoa alloc]
+               initWithModel:model->model()
+      useWithPopUpButtonCell:NO]);
+
+  // Keep the menu controller alive (by adding an additional retain) until after
+  // the menu has been dismissed. Otherwise it will crash if the browser is
+  // destroyed (and consequently the menu controller is destroyed) while the
+  // menu is still pending.
+  base::scoped_nsobject<MenuControllerCocoa> menu_controller_ref(
+      menu_controller_);
+
+  // Make sure events can be pumped while the menu is up.
+  base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
+
+  // One of the events that could be pumped is |window.close()|.
+  // User-initiated event-tracking loops protect against this by
+  // setting flags in -[CrApplication sendEvent:], but since
+  // web-content menus are initiated by IPC message the setup has to
+  // be done manually.
+  base::mac::ScopedSendingEvent sendingEventScoper;
+
+  // Show the menu. Blocks until the menu is dismissed.
+  if (browser->IsWindowless()) {
+    // Don't show the menu unless a native window handle exists.
+    if (!browser->GetWindowHandle())
+      return false;
+
+    const gfx::Point& screen_point =
+        browser->GetScreenPoint(gfx::Point(params.x, params.y));
+    NSPoint screen_position = NSPointFromCGPoint(screen_point.ToCGPoint());
+    [[menu_controller_ menu] popUpMenuPositioningItem:nil
+                                           atLocation:screen_position
+                                               inView:nil];
+  } else {
+    NSView* parent_view =
+        browser->web_contents()->GetContentNativeView().GetNativeNSView();
+
+    // Synthesize an event for the click, as there is no certainty that
+    // [NSApp currentEvent] will return a valid event.
+    NSEvent* currentEvent = [NSApp currentEvent];
+    NSWindow* window = [parent_view window];
+
+    NSPoint position = [window mouseLocationOutsideOfEventStream];
+
+    NSTimeInterval eventTime = [currentEvent timestamp];
+    NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown
+                                             location:position
+                                        modifierFlags:NSRightMouseDownMask
+                                            timestamp:eventTime
+                                         windowNumber:[window windowNumber]
+                                              context:nil
+                                          eventNumber:0
+                                           clickCount:1
+                                             pressure:1.0];
+
+    [NSMenu popUpContextMenu:[menu_controller_ menu]
+                   withEvent:clickEvent
+                     forView:parent_view];
+  }
+
+  return true;
+}
+
+void CefMenuRunnerMac::CancelContextMenu() {
+  if (menu_controller_.get())
+    [menu_controller_ cancel];
+}
diff --git a/src/libcef/browser/native/menu_runner_win.cc b/src/libcef/browser/native/menu_runner_win.cc
new file mode 100644
index 0000000..3ea125b
--- /dev/null
+++ b/src/libcef/browser/native/menu_runner_win.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/native/menu_runner_win.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/native/menu_2.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
+#include "ui/gfx/geometry/point.h"
+
+CefMenuRunnerWin::CefMenuRunnerWin() {}
+
+bool CefMenuRunnerWin::RunContextMenu(
+    CefBrowserHostImpl* browser,
+    CefMenuModelImpl* model,
+    const content::ContextMenuParams& params) {
+  // Create a menu based on the model.
+  menu_.reset(new views::CefNativeMenuWin(model->model(), nullptr));
+  menu_->Rebuild(nullptr);
+
+  // Make sure events can be pumped while the menu is up.
+  base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
+
+  const gfx::Point& screen_point =
+      browser->GetScreenPoint(gfx::Point(params.x, params.y));
+
+  // Show the menu. Blocks until the menu is dismissed.
+  menu_->RunMenuAt(screen_point, views::Menu2::ALIGN_TOPLEFT);
+
+  return true;
+}
diff --git a/src/libcef/browser/native/menu_runner_win.h b/src/libcef/browser/native/menu_runner_win.h
new file mode 100644
index 0000000..4f80797
--- /dev/null
+++ b/src/libcef/browser/native/menu_runner_win.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_WIN_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_WIN_H_
+#pragma once
+
+#include "libcef/browser/menu_runner.h"
+
+#include "libcef/browser/native/native_menu_win.h"
+
+class CefMenuRunnerWin : public CefMenuRunner {
+ public:
+  CefMenuRunnerWin();
+
+  // CefMenuRunner methods.
+  bool RunContextMenu(CefBrowserHostImpl* browser,
+                      CefMenuModelImpl* model,
+                      const content::ContextMenuParams& params) override;
+
+ private:
+  std::unique_ptr<views::CefNativeMenuWin> menu_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_WIN_H_
diff --git a/src/libcef/browser/native/menu_wrapper.h b/src/libcef/browser/native/menu_wrapper.h
new file mode 100644
index 0000000..0f4f0e3
--- /dev/null
+++ b/src/libcef/browser/native/menu_wrapper.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_MENU_WRAPPER_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_MENU_WRAPPER_H_
+
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+class MenuModel;
+}
+
+namespace views {
+
+class MenuInsertionDelegateWin;
+
+// An interface that wraps an object that implements a menu.
+class MenuWrapper {
+ public:
+  // All of the possible actions that can result from RunMenuAt.
+  enum MenuAction {
+    MENU_ACTION_NONE,      // Menu cancelled, or never opened.
+    MENU_ACTION_SELECTED,  // An item was selected.
+    MENU_ACTION_PREVIOUS,  // User wants to navigate to the previous menu.
+    MENU_ACTION_NEXT,      // User wants to navigate to the next menu.
+  };
+
+  virtual ~MenuWrapper() {}
+
+  // Creates the appropriate instance of this wrapper for the current platform.
+  static MenuWrapper* CreateWrapper(ui::MenuModel* model);
+
+  // Runs the menu at the specified point. This blocks until done.
+  virtual void RunMenuAt(const gfx::Point& point, int alignment) = 0;
+
+  // Cancels the active menu.
+  virtual void CancelMenu() = 0;
+
+  // Called when the model supplying data to this menu has changed, and the menu
+  // must be rebuilt.
+  virtual void Rebuild(MenuInsertionDelegateWin* delegate) = 0;
+
+  // Called when the states of the items in the menu must be updated from the
+  // model.
+  virtual void UpdateStates() = 0;
+
+  // Retrieve a native menu handle.
+  virtual HMENU GetNativeMenu() const = 0;
+
+  // Get the result of the last call to RunMenuAt to determine whether an
+  // item was selected, the user navigated to a next or previous menu, or
+  // nothing.
+  virtual MenuAction GetMenuAction() const = 0;
+
+  // Sets the minimum width of the menu.
+  virtual void SetMinimumWidth(int width) = 0;
+};
+
+}  // namespace views
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_MENU_WRAPPER_H_
diff --git a/src/libcef/browser/native/native_menu_win.cc b/src/libcef/browser/native/native_menu_win.cc
new file mode 100644
index 0000000..e9d773a
--- /dev/null
+++ b/src/libcef/browser/native/native_menu_win.cc
@@ -0,0 +1,774 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/native_menu_win.h"
+
+#include "libcef/browser/native/menu_2.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/win/wrapped_window_proc.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/skia_utils_win.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_win.h"
+#include "ui/base/models/menu_model.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/text_utils.h"
+#include "ui/gfx/win/hwnd_util.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/views/controls/menu/menu_config.h"
+#include "ui/views/controls/menu/menu_insertion_delegate_win.h"
+
+using ui::NativeTheme;
+
+namespace views {
+
+// The width of an icon, including the pixels between the icon and
+// the item label.
+static const int kIconWidth = 23;
+// Margins between the top of the item and the label.
+static const int kItemTopMargin = 3;
+// Margins between the bottom of the item and the label.
+static const int kItemBottomMargin = 4;
+// Margins between the left of the item and the icon.
+static const int kItemLeftMargin = 4;
+// The width for displaying the sub-menu arrow.
+static const int kArrowWidth = 10;
+
+// Horizontal spacing between the end of an item (i.e. an icon or a checkbox)
+// and the start of its corresponding text.
+constexpr int kItemLabelSpacing = 10;
+
+namespace {
+
+// Draws the top layer of the canvas into the specified HDC. Only works
+// with a SkCanvas with a BitmapPlatformDevice. Will create a temporary
+// HDC to back the canvas if one doesn't already exist, tearing it down
+// before returning. If |src_rect| is null, copies the entire canvas.
+// Deleted from skia/ext/platform_canvas.h in https://crbug.com/675977#c13
+void DrawToNativeContext(SkCanvas* canvas,
+                         HDC destination_hdc,
+                         int x,
+                         int y,
+                         const RECT* src_rect) {
+  RECT temp_rect;
+  if (!src_rect) {
+    temp_rect.left = 0;
+    temp_rect.right = canvas->imageInfo().width();
+    temp_rect.top = 0;
+    temp_rect.bottom = canvas->imageInfo().height();
+    src_rect = &temp_rect;
+  }
+  skia::CopyHDC(skia::GetNativeDrawingContext(canvas), destination_hdc, x, y,
+                canvas->imageInfo().isOpaque(), *src_rect,
+                canvas->getTotalMatrix());
+}
+
+HFONT CreateNativeFont(const gfx::Font& font) {
+  // Extracts |fonts| properties.
+  const DWORD italic = (font.GetStyle() & gfx::Font::ITALIC) ? TRUE : FALSE;
+  const DWORD underline =
+      (font.GetStyle() & gfx::Font::UNDERLINE) ? TRUE : FALSE;
+  // The font mapper matches its absolute value against the character height of
+  // the available fonts.
+  const int height = -font.GetFontSize();
+
+  // Select the primary font which forces a mapping to a physical font.
+  return ::CreateFont(height, 0, 0, 0, static_cast<int>(font.GetWeight()),
+                      italic, underline, FALSE, DEFAULT_CHARSET,
+                      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+                      DEFAULT_PITCH | FF_DONTCARE,
+                      base::UTF8ToUTF16(font.GetFontName()).c_str());
+}
+
+}  // namespace
+
+struct CefNativeMenuWin::ItemData {
+  // The Windows API requires that whoever creates the menus must own the
+  // strings used for labels, and keep them around for the lifetime of the
+  // created menu. So be it.
+  base::string16 label;
+
+  // Someone needs to own submenus, it may as well be us.
+  std::unique_ptr<Menu2> submenu;
+
+  // We need a pointer back to the containing menu in various circumstances.
+  CefNativeMenuWin* native_menu_win;
+
+  // The index of the item within the menu's model.
+  int model_index;
+};
+
+// Returns the CefNativeMenuWin for a particular HMENU.
+static CefNativeMenuWin* GetCefNativeMenuWinFromHMENU(HMENU hmenu) {
+  MENUINFO mi = {0};
+  mi.cbSize = sizeof(mi);
+  mi.fMask = MIM_MENUDATA | MIM_STYLE;
+  GetMenuInfo(hmenu, &mi);
+  return reinterpret_cast<CefNativeMenuWin*>(mi.dwMenuData);
+}
+
+// A window that receives messages from Windows relevant to the native menu
+// structure we have constructed in CefNativeMenuWin.
+class CefNativeMenuWin::MenuHostWindow {
+ public:
+  MenuHostWindow() {
+    RegisterClass();
+    hwnd_ = CreateWindowEx(l10n_util::GetExtendedStyles(), kWindowClassName,
+                           L"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
+    gfx::CheckWindowCreated(hwnd_);
+    gfx::SetWindowUserData(hwnd_, this);
+  }
+
+  ~MenuHostWindow() { DestroyWindow(hwnd_); }
+
+  HWND hwnd() const { return hwnd_; }
+
+ private:
+  static const wchar_t* kWindowClassName;
+
+  void RegisterClass() {
+    static bool registered = false;
+    if (registered)
+      return;
+
+    WNDCLASSEX window_class;
+    base::win::InitializeWindowClass(
+        kWindowClassName, &base::win::WrappedWindowProc<MenuHostWindowProc>,
+        CS_DBLCLKS, 0, 0, NULL, reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1),
+        NULL, NULL, NULL, &window_class);
+    ATOM clazz = RegisterClassEx(&window_class);
+    CHECK(clazz);
+    registered = true;
+  }
+
+  // Converts the WPARAM value passed to WM_MENUSELECT into an index
+  // corresponding to the menu item that was selected.
+  int GetMenuItemIndexFromWPARAM(HMENU menu, WPARAM w_param) const {
+    int count = GetMenuItemCount(menu);
+    // For normal command menu items, Windows passes a command id as the LOWORD
+    // of WPARAM for WM_MENUSELECT. We need to walk forward through the menu
+    // items to find an item with a matching ID. Ugh!
+    for (int i = 0; i < count; ++i) {
+      MENUITEMINFO mii = {0};
+      mii.cbSize = sizeof(mii);
+      mii.fMask = MIIM_ID;
+      GetMenuItemInfo(menu, i, MF_BYPOSITION, &mii);
+      if (mii.wID == w_param)
+        return i;
+    }
+    // If we didn't find a matching command ID, this means a submenu has been
+    // selected instead, and rather than passing a command ID in
+    // LOWORD(w_param), Windows has actually passed us a position, so we just
+    // return it.
+    return w_param;
+  }
+
+  CefNativeMenuWin::ItemData* GetItemData(ULONG_PTR item_data) {
+    return reinterpret_cast<CefNativeMenuWin::ItemData*>(item_data);
+  }
+
+  // Called when the user selects a specific item.
+  void OnMenuCommand(int position, HMENU menu) {
+    CefNativeMenuWin* menu_win = GetCefNativeMenuWinFromHMENU(menu);
+    ui::MenuModel* model = menu_win->model_;
+    CefNativeMenuWin* root_menu = menu_win;
+    while (root_menu->parent_)
+      root_menu = root_menu->parent_;
+
+    // Only notify the model if it didn't already send out notification.
+    // See comment in MenuMessageHook for details.
+    if (root_menu->menu_action_ == MenuWrapper::MENU_ACTION_NONE)
+      model->ActivatedAt(position);
+  }
+
+  // Called by Windows to measure the size of an owner-drawn menu item.
+  void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* measure_item_struct) {
+    CefNativeMenuWin::ItemData* data =
+        GetItemData(measure_item_struct->itemData);
+    if (data) {
+      gfx::FontList font_list;
+      measure_item_struct->itemWidth =
+          gfx::GetStringWidth(data->label, font_list) + kIconWidth +
+          kItemLeftMargin + kItemLabelSpacing -
+          GetSystemMetrics(SM_CXMENUCHECK);
+      if (data->submenu.get())
+        measure_item_struct->itemWidth += kArrowWidth;
+      // If the label contains an accelerator, make room for tab.
+      if (data->label.find(L'\t') != base::string16::npos)
+        measure_item_struct->itemWidth += gfx::GetStringWidth(L" ", font_list);
+      measure_item_struct->itemHeight =
+          font_list.GetHeight() + kItemBottomMargin + kItemTopMargin;
+    } else {
+      // Measure separator size.
+      measure_item_struct->itemHeight = GetSystemMetrics(SM_CYMENU) / 2;
+      measure_item_struct->itemWidth = 0;
+    }
+  }
+
+  // Called by Windows to paint an owner-drawn menu item.
+  void OnDrawItem(UINT w_param, DRAWITEMSTRUCT* draw_item_struct) {
+    HDC dc = draw_item_struct->hDC;
+    COLORREF prev_bg_color, prev_text_color;
+
+    // Set background color and text color
+    if (draw_item_struct->itemState & ODS_SELECTED) {
+      prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
+      prev_text_color = SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+    } else {
+      prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_MENU));
+      if (draw_item_struct->itemState & ODS_DISABLED)
+        prev_text_color = SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
+      else
+        prev_text_color = SetTextColor(dc, GetSysColor(COLOR_MENUTEXT));
+    }
+
+    if (draw_item_struct->itemData) {
+      CefNativeMenuWin::ItemData* data =
+          GetItemData(draw_item_struct->itemData);
+      // Draw the background.
+      HBRUSH hbr = CreateSolidBrush(GetBkColor(dc));
+      FillRect(dc, &draw_item_struct->rcItem, hbr);
+      DeleteObject(hbr);
+
+      // Draw the label.
+      RECT rect = draw_item_struct->rcItem;
+      rect.top += kItemTopMargin;
+      // Should we add kIconWidth only when icon.width() != 0 ?
+      rect.left += kItemLeftMargin + kIconWidth;
+      rect.right -= kItemLabelSpacing;
+      UINT format = DT_TOP | DT_SINGLELINE;
+      // Check whether the mnemonics should be underlined.
+      BOOL underline_mnemonics;
+      SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0);
+      if (!underline_mnemonics)
+        format |= DT_HIDEPREFIX;
+      gfx::FontList font_list;
+      HFONT new_font = CreateNativeFont(font_list.GetPrimaryFont());
+      HGDIOBJ old_font = SelectObject(dc, new_font);
+
+      // If an accelerator is specified (with a tab delimiting the rest of the
+      // label from the accelerator), we have to justify the fist part on the
+      // left and the accelerator on the right.
+      // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the
+      //                 window system UI font and will not hit here.
+      base::string16 label = data->label;
+      base::string16 accel;
+      base::string16::size_type tab_pos = label.find(L'\t');
+      if (tab_pos != base::string16::npos) {
+        accel = label.substr(tab_pos);
+        label = label.substr(0, tab_pos);
+      }
+      DrawTextEx(dc, const_cast<wchar_t*>(label.data()),
+                 static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL);
+      if (!accel.empty()) {
+        DrawTextEx(dc, const_cast<wchar_t*>(accel.data()),
+                   static_cast<int>(accel.size()), &rect, format | DT_RIGHT,
+                   NULL);
+      }
+      SelectObject(dc, old_font);
+      DeleteObject(new_font);
+
+      ui::MenuModel::ItemType type =
+          data->native_menu_win->model_->GetTypeAt(data->model_index);
+
+      // Draw the icon after the label, otherwise it would be covered
+      // by the label.
+      gfx::Image icon;
+      if (data->native_menu_win->model_->GetIconAt(data->model_index, &icon)) {
+        // We currently don't support items with both icons and checkboxes.
+        const gfx::ImageSkia skia_icon = icon.AsImageSkia();
+        DCHECK(type != ui::MenuModel::TYPE_CHECK);
+        std::unique_ptr<SkCanvas> canvas = skia::CreatePlatformCanvas(
+            skia_icon.width(), skia_icon.height(), false);
+        canvas->drawBitmap(*skia_icon.bitmap(), 0, 0);
+        DrawToNativeContext(
+            canvas.get(), dc, draw_item_struct->rcItem.left + kItemLeftMargin,
+            draw_item_struct->rcItem.top +
+                (draw_item_struct->rcItem.bottom -
+                 draw_item_struct->rcItem.top - skia_icon.height()) /
+                    2,
+            NULL);
+      } else if (type == ui::MenuModel::TYPE_CHECK &&
+                 data->native_menu_win->model_->IsItemCheckedAt(
+                     data->model_index)) {
+        // Manually render a checkbox.
+        const MenuConfig& config = MenuConfig::instance();
+        NativeTheme::State state;
+        if (draw_item_struct->itemState & ODS_DISABLED) {
+          state = NativeTheme::kDisabled;
+        } else {
+          state = draw_item_struct->itemState & ODS_SELECTED
+                      ? NativeTheme::kHovered
+                      : NativeTheme::kNormal;
+        }
+
+        std::unique_ptr<SkCanvas> canvas = skia::CreatePlatformCanvas(
+            config.check_width, config.check_height, false);
+        cc::SkiaPaintCanvas paint_canvas(canvas.get());
+
+        NativeTheme::ExtraParams extra;
+        extra.menu_check.is_radio = false;
+        gfx::Rect bounds(0, 0, config.check_width, config.check_height);
+
+        // Draw the background and the check.
+        ui::NativeTheme* native_theme =
+            ui::NativeTheme::GetInstanceForNativeUi();
+        native_theme->Paint(&paint_canvas, NativeTheme::kMenuCheckBackground,
+                            state, bounds, extra);
+        native_theme->Paint(&paint_canvas, NativeTheme::kMenuCheck, state,
+                            bounds, extra);
+
+        // Draw checkbox to menu.
+        DrawToNativeContext(
+            canvas.get(), dc, draw_item_struct->rcItem.left + kItemLeftMargin,
+            draw_item_struct->rcItem.top +
+                (draw_item_struct->rcItem.bottom -
+                 draw_item_struct->rcItem.top - config.check_height) /
+                    2,
+            NULL);
+      }
+    } else {
+      // Draw the separator
+      draw_item_struct->rcItem.top +=
+          (draw_item_struct->rcItem.bottom - draw_item_struct->rcItem.top) / 3;
+      DrawEdge(dc, &draw_item_struct->rcItem, EDGE_ETCHED, BF_TOP);
+    }
+
+    SetBkColor(dc, prev_bg_color);
+    SetTextColor(dc, prev_text_color);
+  }
+
+  bool ProcessWindowMessage(HWND window,
+                            UINT message,
+                            WPARAM w_param,
+                            LPARAM l_param,
+                            LRESULT* l_result) {
+    switch (message) {
+      case WM_MENUCOMMAND:
+        OnMenuCommand(w_param, reinterpret_cast<HMENU>(l_param));
+        *l_result = 0;
+        return true;
+      case WM_MENUSELECT:
+        *l_result = 0;
+        return true;
+      case WM_MEASUREITEM:
+        OnMeasureItem(w_param, reinterpret_cast<MEASUREITEMSTRUCT*>(l_param));
+        *l_result = 0;
+        return true;
+      case WM_DRAWITEM:
+        OnDrawItem(w_param, reinterpret_cast<DRAWITEMSTRUCT*>(l_param));
+        *l_result = 0;
+        return true;
+        // TODO(beng): bring over owner draw from old menu system.
+    }
+    return false;
+  }
+
+  static LRESULT CALLBACK MenuHostWindowProc(HWND window,
+                                             UINT message,
+                                             WPARAM w_param,
+                                             LPARAM l_param) {
+    MenuHostWindow* host =
+        reinterpret_cast<MenuHostWindow*>(gfx::GetWindowUserData(window));
+    // host is null during initial construction.
+    LRESULT l_result = 0;
+    if (!host || !host->ProcessWindowMessage(window, message, w_param, l_param,
+                                             &l_result)) {
+      return DefWindowProc(window, message, w_param, l_param);
+    }
+    return l_result;
+  }
+
+  HWND hwnd_;
+
+  DISALLOW_COPY_AND_ASSIGN(MenuHostWindow);
+};
+
+struct CefNativeMenuWin::HighlightedMenuItemInfo {
+  HighlightedMenuItemInfo()
+      : has_parent(false), has_submenu(false), menu(nullptr), position(-1) {}
+
+  bool has_parent;
+  bool has_submenu;
+
+  // The menu and position. These are only set for non-disabled menu items.
+  CefNativeMenuWin* menu;
+  int position;
+};
+
+// static
+const wchar_t* CefNativeMenuWin::MenuHostWindow::kWindowClassName =
+    L"ViewsMenuHostWindow";
+
+////////////////////////////////////////////////////////////////////////////////
+// CefNativeMenuWin, public:
+
+CefNativeMenuWin::CefNativeMenuWin(ui::MenuModel* model, HWND system_menu_for)
+    : model_(model),
+      menu_(nullptr),
+      owner_draw_(l10n_util::NeedOverrideDefaultUIFont(NULL, NULL) &&
+                  !system_menu_for),
+      system_menu_for_(system_menu_for),
+      first_item_index_(0),
+      menu_action_(MENU_ACTION_NONE),
+      menu_to_select_(nullptr),
+      position_to_select_(-1),
+      parent_(nullptr),
+      destroyed_flag_(nullptr),
+      menu_to_select_factory_(this) {}
+
+CefNativeMenuWin::~CefNativeMenuWin() {
+  if (destroyed_flag_)
+    *destroyed_flag_ = true;
+  DestroyMenu(menu_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CefNativeMenuWin, MenuWrapper implementation:
+
+void CefNativeMenuWin::RunMenuAt(const gfx::Point& point, int alignment) {
+  CreateHostWindow();
+  UpdateStates();
+  UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RECURSE;
+  flags |= GetAlignmentFlags(alignment);
+  menu_action_ = MENU_ACTION_NONE;
+
+  // Set a hook function so we can listen for keyboard events while the
+  // menu is open, and store a pointer to this object in a static
+  // variable so the hook has access to it (ugly, but it's the
+  // only way).
+  open_native_menu_win_ = this;
+  HHOOK hhook = SetWindowsHookEx(WH_MSGFILTER, MenuMessageHook,
+                                 GetModuleHandle(NULL), ::GetCurrentThreadId());
+
+  // Command dispatch is done through WM_MENUCOMMAND, handled by the host
+  // window.
+  menu_to_select_ = nullptr;
+  position_to_select_ = -1;
+  menu_to_select_factory_.InvalidateWeakPtrs();
+  bool destroyed = false;
+  destroyed_flag_ = &destroyed;
+  model_->MenuWillShow();
+  TrackPopupMenu(menu_, flags, point.x(), point.y(), 0, host_window_->hwnd(),
+                 NULL);
+  UnhookWindowsHookEx(hhook);
+  open_native_menu_win_ = nullptr;
+  if (destroyed)
+    return;
+  destroyed_flag_ = nullptr;
+  if (menu_to_select_) {
+    // Folks aren't too happy if we notify immediately. In particular, notifying
+    // the delegate can cause destruction leaving the stack in a weird
+    // state. Instead post a task, then notify. This mirrors what WM_MENUCOMMAND
+    // does.
+    menu_to_select_factory_.InvalidateWeakPtrs();
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&CefNativeMenuWin::DelayedSelect,
+                              menu_to_select_factory_.GetWeakPtr()));
+    menu_action_ = MENU_ACTION_SELECTED;
+  }
+  // Send MenuWillClose after we schedule the select, otherwise MenuWillClose is
+  // processed after the select (MenuWillClose posts a delayed task too).
+  model_->MenuWillClose();
+}
+
+void CefNativeMenuWin::CancelMenu() {
+  EndMenu();
+}
+
+void CefNativeMenuWin::Rebuild(MenuInsertionDelegateWin* delegate) {
+  ResetNativeMenu();
+  items_.clear();
+
+  owner_draw_ = model_->HasIcons() || owner_draw_;
+  first_item_index_ = delegate ? delegate->GetInsertionIndex(menu_) : 0;
+  for (int menu_index = first_item_index_;
+       menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) {
+    int model_index = menu_index - first_item_index_;
+    if (model_->GetTypeAt(model_index) == ui::MenuModel::TYPE_SEPARATOR)
+      AddSeparatorItemAt(menu_index, model_index);
+    else
+      AddMenuItemAt(menu_index, model_index);
+  }
+}
+
+void CefNativeMenuWin::UpdateStates() {
+  // A depth-first walk of the menu items, updating states.
+  int model_index = 0;
+  ItemDataList::const_iterator it;
+  for (it = items_.begin(); it != items_.end(); ++it, ++model_index) {
+    int menu_index = model_index + first_item_index_;
+    SetMenuItemState(menu_index, model_->IsEnabledAt(model_index),
+                     model_->IsItemCheckedAt(model_index), false);
+    if (model_->IsItemDynamicAt(model_index)) {
+      // TODO(atwilson): Update the icon as well (http://crbug.com/66508).
+      SetMenuItemLabel(menu_index, model_index,
+                       model_->GetLabelAt(model_index));
+    }
+    Menu2* submenu = (*it)->submenu.get();
+    if (submenu)
+      submenu->UpdateStates();
+  }
+}
+
+HMENU CefNativeMenuWin::GetNativeMenu() const {
+  return menu_;
+}
+
+CefNativeMenuWin::MenuAction CefNativeMenuWin::GetMenuAction() const {
+  return menu_action_;
+}
+
+void CefNativeMenuWin::SetMinimumWidth(int width) {
+  NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CefNativeMenuWin, private:
+
+// static
+CefNativeMenuWin* CefNativeMenuWin::open_native_menu_win_ = nullptr;
+
+void CefNativeMenuWin::DelayedSelect() {
+  if (menu_to_select_)
+    menu_to_select_->model_->ActivatedAt(position_to_select_);
+}
+
+// static
+bool CefNativeMenuWin::GetHighlightedMenuItemInfo(
+    HMENU menu,
+    HighlightedMenuItemInfo* info) {
+  for (int i = 0; i < ::GetMenuItemCount(menu); i++) {
+    UINT state = ::GetMenuState(menu, i, MF_BYPOSITION);
+    if (state & MF_HILITE) {
+      if (state & MF_POPUP) {
+        HMENU submenu = GetSubMenu(menu, i);
+        if (GetHighlightedMenuItemInfo(submenu, info))
+          info->has_parent = true;
+        else
+          info->has_submenu = true;
+      } else if (!(state & MF_SEPARATOR) && !(state & MF_DISABLED)) {
+        info->menu = GetCefNativeMenuWinFromHMENU(menu);
+        info->position = i;
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+// static
+LRESULT CALLBACK CefNativeMenuWin::MenuMessageHook(int n_code,
+                                                   WPARAM w_param,
+                                                   LPARAM l_param) {
+  LRESULT result = CallNextHookEx(NULL, n_code, w_param, l_param);
+
+  CefNativeMenuWin* this_ptr = open_native_menu_win_;
+  if (!this_ptr)
+    return result;
+
+  MSG* msg = reinterpret_cast<MSG*>(l_param);
+  if (msg->message == WM_LBUTTONUP || msg->message == WM_RBUTTONUP) {
+    HighlightedMenuItemInfo info;
+    if (GetHighlightedMenuItemInfo(this_ptr->menu_, &info) && info.menu) {
+      // It appears that when running a menu by way of TrackPopupMenu(Ex) win32
+      // gets confused if the underlying window paints itself. As its very easy
+      // for the underlying window to repaint itself (especially since some menu
+      // items trigger painting of the tabstrip on mouse over) we have this
+      // workaround. When the mouse is released on a menu item we remember the
+      // menu item and end the menu. When the nested message loop returns we
+      // schedule a task to notify the model. It's still possible to get a
+      // WM_MENUCOMMAND, so we have to be careful that we don't notify the model
+      // twice.
+      this_ptr->menu_to_select_ = info.menu;
+      this_ptr->position_to_select_ = info.position;
+      EndMenu();
+    }
+  } else if (msg->message == WM_KEYDOWN) {
+    HighlightedMenuItemInfo info;
+    if (GetHighlightedMenuItemInfo(this_ptr->menu_, &info)) {
+      if (msg->wParam == VK_LEFT && !info.has_parent) {
+        this_ptr->menu_action_ = MENU_ACTION_PREVIOUS;
+        ::EndMenu();
+      } else if (msg->wParam == VK_RIGHT && !info.has_parent &&
+                 !info.has_submenu) {
+        this_ptr->menu_action_ = MENU_ACTION_NEXT;
+        ::EndMenu();
+      }
+    }
+  }
+
+  return result;
+}
+
+bool CefNativeMenuWin::IsSeparatorItemAt(int menu_index) const {
+  MENUITEMINFO mii = {0};
+  mii.cbSize = sizeof(mii);
+  mii.fMask = MIIM_FTYPE;
+  GetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+  return !!(mii.fType & MF_SEPARATOR);
+}
+
+void CefNativeMenuWin::AddMenuItemAt(int menu_index, int model_index) {
+  MENUITEMINFO mii = {0};
+  mii.cbSize = sizeof(mii);
+  mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA;
+  if (!owner_draw_)
+    mii.fType = MFT_STRING;
+  else
+    mii.fType = MFT_OWNERDRAW;
+
+  std::unique_ptr<ItemData> item_data = std::make_unique<ItemData>();
+  item_data->label = base::string16();
+  ui::MenuModel::ItemType type = model_->GetTypeAt(model_index);
+  if (type == ui::MenuModel::TYPE_SUBMENU) {
+    item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index)));
+    mii.fMask |= MIIM_SUBMENU;
+    mii.hSubMenu = item_data->submenu->GetNativeMenu();
+    GetCefNativeMenuWinFromHMENU(mii.hSubMenu)->parent_ = this;
+  } else {
+    if (type == ui::MenuModel::TYPE_RADIO)
+      mii.fType |= MFT_RADIOCHECK;
+    mii.wID = model_->GetCommandIdAt(model_index);
+  }
+  item_data->native_menu_win = this;
+  item_data->model_index = model_index;
+  mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data.get());
+  items_.insert(items_.begin() + model_index, std::move(item_data));
+  UpdateMenuItemInfoForString(&mii, model_index,
+                              model_->GetLabelAt(model_index));
+  InsertMenuItem(menu_, menu_index, TRUE, &mii);
+}
+
+void CefNativeMenuWin::AddSeparatorItemAt(int menu_index, int model_index) {
+  MENUITEMINFO mii = {0};
+  mii.cbSize = sizeof(mii);
+  mii.fMask = MIIM_FTYPE;
+  mii.fType = MFT_SEPARATOR;
+  // Insert a dummy entry into our label list so we can index directly into it
+  // using item indices if need be.
+  items_.insert(items_.begin() + model_index, std::make_unique<ItemData>());
+  InsertMenuItem(menu_, menu_index, TRUE, &mii);
+}
+
+void CefNativeMenuWin::SetMenuItemState(int menu_index,
+                                        bool enabled,
+                                        bool checked,
+                                        bool is_default) {
+  if (IsSeparatorItemAt(menu_index))
+    return;
+
+  UINT state = enabled ? MFS_ENABLED : MFS_DISABLED;
+  if (checked)
+    state |= MFS_CHECKED;
+  if (is_default)
+    state |= MFS_DEFAULT;
+
+  MENUITEMINFO mii = {0};
+  mii.cbSize = sizeof(mii);
+  mii.fMask = MIIM_STATE;
+  mii.fState = state;
+  SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+}
+
+void CefNativeMenuWin::SetMenuItemLabel(int menu_index,
+                                        int model_index,
+                                        const base::string16& label) {
+  if (IsSeparatorItemAt(menu_index))
+    return;
+
+  MENUITEMINFO mii = {0};
+  mii.cbSize = sizeof(mii);
+  UpdateMenuItemInfoForString(&mii, model_index, label);
+  SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+}
+
+void CefNativeMenuWin::UpdateMenuItemInfoForString(
+    MENUITEMINFO* mii,
+    int model_index,
+    const base::string16& label) {
+  base::string16 formatted = label;
+  ui::MenuModel::ItemType type = model_->GetTypeAt(model_index);
+  // Strip out any tabs, otherwise they get interpreted as accelerators and can
+  // lead to weird behavior.
+  base::ReplaceSubstringsAfterOffset(&formatted, 0, L"\t", L" ");
+  if (type != ui::MenuModel::TYPE_SUBMENU) {
+    // Add accelerator details to the label if provided.
+    ui::Accelerator accelerator(ui::VKEY_UNKNOWN, ui::EF_NONE);
+    if (model_->GetAcceleratorAt(model_index, &accelerator)) {
+      formatted += L"\t";
+      formatted += accelerator.GetShortcutText();
+    }
+  }
+
+  // Update the owned string, since Windows will want us to keep this new
+  // version around.
+  items_[model_index]->label = formatted;
+
+  // Give Windows a pointer to the label string.
+  mii->fMask |= MIIM_STRING;
+  mii->dwTypeData = const_cast<wchar_t*>(items_[model_index]->label.c_str());
+}
+
+UINT CefNativeMenuWin::GetAlignmentFlags(int alignment) const {
+  UINT alignment_flags = TPM_TOPALIGN;
+  if (alignment == Menu2::ALIGN_TOPLEFT)
+    alignment_flags |= TPM_LEFTALIGN;
+  else if (alignment == Menu2::ALIGN_TOPRIGHT)
+    alignment_flags |= TPM_RIGHTALIGN;
+  return alignment_flags;
+}
+
+void CefNativeMenuWin::ResetNativeMenu() {
+  if (IsWindow(system_menu_for_)) {
+    if (menu_)
+      GetSystemMenu(system_menu_for_, TRUE);
+    menu_ = GetSystemMenu(system_menu_for_, FALSE);
+  } else {
+    if (menu_)
+      DestroyMenu(menu_);
+    menu_ = CreatePopupMenu();
+    // Rather than relying on the return value of TrackPopupMenuEx, which is
+    // always a command identifier, instead we tell the menu to notify us via
+    // our host window and the WM_MENUCOMMAND message.
+    MENUINFO mi = {0};
+    mi.cbSize = sizeof(mi);
+    mi.fMask = MIM_STYLE | MIM_MENUDATA;
+    mi.dwStyle = MNS_NOTIFYBYPOS;
+    mi.dwMenuData = reinterpret_cast<ULONG_PTR>(this);
+    SetMenuInfo(menu_, &mi);
+  }
+}
+
+void CefNativeMenuWin::CreateHostWindow() {
+  // This only gets called from RunMenuAt, and as such there is only ever one
+  // host window per menu hierarchy, no matter how many CefNativeMenuWin objects
+  // exist wrapping submenus.
+  if (!host_window_.get())
+    host_window_.reset(new MenuHostWindow());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// MenuWrapper, public:
+
+// static
+MenuWrapper* MenuWrapper::CreateWrapper(ui::MenuModel* model) {
+  return new CefNativeMenuWin(model, NULL);
+}
+
+}  // namespace views
diff --git a/src/libcef/browser/native/native_menu_win.h b/src/libcef/browser/native/native_menu_win.h
new file mode 100644
index 0000000..414722e
--- /dev/null
+++ b/src/libcef/browser/native/native_menu_win.h
@@ -0,0 +1,164 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_NATIVE_MENU_WIN_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_NATIVE_MENU_WIN_H_
+
+#include <memory>
+#include <vector>
+
+#include "libcef/browser/native/menu_wrapper.h"
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/strings/string16.h"
+
+namespace ui {
+class MenuModel;
+}
+
+namespace views {
+
+// A Windows implementation of MenuWrapper.
+// TODO(beng): rename to MenuWin once the old class is dead.
+class CefNativeMenuWin : public MenuWrapper {
+ public:
+  // Construct a CefNativeMenuWin, with a model and delegate. If
+  // |system_menu_for| is non-NULL, the CefNativeMenuWin wraps the system menu
+  // for that window.
+  // The caller owns the model and the delegate.
+  CefNativeMenuWin(ui::MenuModel* model, HWND system_menu_for);
+  ~CefNativeMenuWin() override;
+
+  // Overridden from MenuWrapper:
+  void RunMenuAt(const gfx::Point& point, int alignment) override;
+  void CancelMenu() override;
+  void Rebuild(MenuInsertionDelegateWin* delegate) override;
+  void UpdateStates() override;
+  HMENU GetNativeMenu() const override;
+  MenuAction GetMenuAction() const override;
+  void SetMinimumWidth(int width) override;
+
+ private:
+  // IMPORTANT: Note about indices.
+  //            Functions in this class deal in two index spaces:
+  //            1. menu_index - the index of an item within the actual Windows
+  //               native menu.
+  //            2. model_index - the index of the item within our model.
+  //            These two are most often but not always the same value! The
+  //            notable exception is when this object is used to wrap the
+  //            Windows System Menu. In this instance, the model indices start
+  //            at 0, but the insertion index into the existing menu is not.
+  //            It is important to take this into consideration when editing the
+  //            code in the functions in this class.
+
+  struct HighlightedMenuItemInfo;
+
+  // Returns true if the item at the specified index is a separator.
+  bool IsSeparatorItemAt(int menu_index) const;
+
+  // Add items. See note above about indices.
+  void AddMenuItemAt(int menu_index, int model_index);
+  void AddSeparatorItemAt(int menu_index, int model_index);
+
+  // Sets the state of the item at the specified index.
+  void SetMenuItemState(int menu_index,
+                        bool enabled,
+                        bool checked,
+                        bool is_default);
+
+  // Sets the label of the item at the specified index.
+  void SetMenuItemLabel(int menu_index,
+                        int model_index,
+                        const base::string16& label);
+
+  // Updates the local data structure with the correctly formatted version of
+  // |label| at the specified model_index, and adds string data to |mii| if
+  // the menu is not owner-draw. That's a mouthful. This function exists because
+  // of the peculiarities of the Windows menu API.
+  void UpdateMenuItemInfoForString(MENUITEMINFO* mii,
+                                   int model_index,
+                                   const base::string16& label);
+
+  // Returns the alignment flags to be passed to TrackPopupMenuEx, based on the
+  // supplied alignment and the UI text direction.
+  UINT GetAlignmentFlags(int alignment) const;
+
+  // Resets the native menu stored in |menu_| by destroying any old menu then
+  // creating a new empty one.
+  void ResetNativeMenu();
+
+  // Creates the host window that receives notifications from the menu.
+  void CreateHostWindow();
+
+  // Callback from task to notify menu it was selected.
+  void DelayedSelect();
+
+  // Given a menu that's currently popped-up, find the currently highlighted
+  // item. Returns true if a highlighted item was found.
+  static bool GetHighlightedMenuItemInfo(HMENU menu,
+                                         HighlightedMenuItemInfo* info);
+
+  // Hook to receive keyboard events while the menu is open.
+  static LRESULT CALLBACK MenuMessageHook(int n_code,
+                                          WPARAM w_param,
+                                          LPARAM l_param);
+
+  // Our attached model and delegate.
+  ui::MenuModel* model_;
+
+  HMENU menu_;
+
+  // True if the contents of menu items in this menu are drawn by the menu host
+  // window, rather than Windows.
+  bool owner_draw_;
+
+  // An object that collects all of the data associated with an individual menu
+  // item.
+  struct ItemData;
+  typedef std::vector<std::unique_ptr<ItemData>> ItemDataList;
+  ItemDataList items_;
+
+  // The window that receives notifications from the menu.
+  class MenuHostWindow;
+  friend MenuHostWindow;
+  std::unique_ptr<MenuHostWindow> host_window_;
+
+  // The HWND this menu is the system menu for, or NULL if the menu is not a
+  // system menu.
+  HWND system_menu_for_;
+
+  // The index of the first item in the model in the menu.
+  int first_item_index_;
+
+  // The action that took place during the call to RunMenuAt.
+  MenuAction menu_action_;
+
+  // See comment in MenuMessageHook for details on these.
+  CefNativeMenuWin* menu_to_select_;
+  int position_to_select_;
+
+  // If we're a submenu, this is our parent.
+  CefNativeMenuWin* parent_;
+
+  // If non-null the destructor sets this to true. This is set to non-null while
+  // the menu is showing. It is used to detect if the menu was deleted while
+  // running.
+  bool* destroyed_flag_;
+
+  base::WeakPtrFactory<CefNativeMenuWin> menu_to_select_factory_;
+
+  // Ugly: a static pointer to the instance of this class that currently
+  // has a menu open, because our hook function that receives keyboard
+  // events doesn't have a mechanism to get a user data pointer.
+  static CefNativeMenuWin* open_native_menu_win_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefNativeMenuWin);
+};
+
+}  // namespace views
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_NATIVE_MENU_WIN_H_
diff --git a/src/libcef/browser/native/window_delegate_view.cc b/src/libcef/browser/native/window_delegate_view.cc
new file mode 100644
index 0000000..1a408dc
--- /dev/null
+++ b/src/libcef/browser/native/window_delegate_view.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/native/window_delegate_view.h"
+
+#include <utility>
+
+#include "content/public/browser/web_contents.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/webview/webview.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+
+CefWindowDelegateView::CefWindowDelegateView(
+    SkColor background_color,
+    bool always_on_top,
+    base::RepeatingClosure on_bounds_changed)
+    : background_color_(background_color),
+      web_view_(nullptr),
+      always_on_top_(always_on_top),
+      on_bounds_changed_(on_bounds_changed) {}
+
+void CefWindowDelegateView::Init(gfx::AcceleratedWidget parent_widget,
+                                 content::WebContents* web_contents,
+                                 const gfx::Rect& bounds) {
+  DCHECK(!web_view_);
+  web_view_ = new views::WebView(web_contents->GetBrowserContext());
+  web_view_->SetWebContents(web_contents);
+  web_view_->SetPreferredSize(bounds.size());
+
+  views::Widget* widget = new views::Widget;
+
+  // See CalculateWindowStylesFromInitParams in
+  // ui/views/widget/widget_hwnd_utils.cc for the conversion of |params| to
+  // Windows style flags.
+  views::Widget::InitParams params;
+  params.parent_widget = parent_widget;
+  params.bounds = bounds;
+  params.delegate = this;
+  // Set the WS_CHILD flag.
+  params.child = true;
+  // Set the WS_VISIBLE flag.
+  params.type = views::Widget::InitParams::TYPE_CONTROL;
+  // Don't set the WS_EX_COMPOSITED flag.
+  params.opacity = views::Widget::InitParams::WindowOpacity::kOpaque;
+  // Tell Aura not to draw the window frame on resize.
+  params.remove_standard_frame = true;
+  // Cause WidgetDelegate::CanActivate to return true. See comments in
+  // CefBrowserHostImpl::PlatformSetFocus.
+  params.activatable = views::Widget::InitParams::ACTIVATABLE_YES;
+
+  params.z_order = always_on_top_ ? ui::ZOrderLevel::kFloatingWindow
+                                  : ui::ZOrderLevel::kNormal;
+
+  // Results in a call to InitContent().
+  widget->Init(std::move(params));
+
+  // |widget| should now be associated with |this|.
+  DCHECK_EQ(widget, GetWidget());
+  // |widget| must be top-level for focus handling to work correctly.
+  DCHECK(widget->is_top_level());
+  // |widget| must be activatable for focus handling to work correctly.
+  DCHECK(widget->widget_delegate()->CanActivate());
+}
+
+void CefWindowDelegateView::InitContent() {
+  SetBackground(views::CreateSolidBackground(background_color_));
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+  AddChildView(web_view_);
+}
+
+void CefWindowDelegateView::ViewHierarchyChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  if (details.is_add && details.child == this)
+    InitContent();
+}
+
+void CefWindowDelegateView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+  views::WidgetDelegateView::OnBoundsChanged(previous_bounds);
+  if (!on_bounds_changed_.is_null())
+    on_bounds_changed_.Run();
+}
diff --git a/src/libcef/browser/native/window_delegate_view.h b/src/libcef/browser/native/window_delegate_view.h
new file mode 100644
index 0000000..3fb25a6
--- /dev/null
+++ b/src/libcef/browser/native/window_delegate_view.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_WINDOW_DELEGATE_VIEW_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_WINDOW_DELEGATE_VIEW_H_
+#pragma once
+
+#include "ui/views/widget/widget_delegate.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace views {
+class WebView;
+}
+
+// Manages the views-based root window that hosts the web contents. This object
+// will be deleted automatically when the associated root window is destroyed.
+class CefWindowDelegateView : public views::WidgetDelegateView {
+ public:
+  CefWindowDelegateView(SkColor background_color,
+                        bool always_on_top,
+                        base::RepeatingClosure on_bounds_changed);
+
+  // Create the Widget and associated root window.
+  void Init(gfx::AcceleratedWidget parent_widget,
+            content::WebContents* web_contents,
+            const gfx::Rect& bounds);
+
+ private:
+  // Initialize the Widget's content.
+  void InitContent();
+
+  // WidgetDelegateView methods:
+  bool CanResize() const override { return true; }
+  bool CanMaximize() const override { return true; }
+  View* GetContentsView() override { return this; }
+
+  // View methods:
+  void ViewHierarchyChanged(
+      const views::ViewHierarchyChangedDetails& details) override;
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
+
+ private:
+  SkColor background_color_;
+  views::WebView* web_view_;
+  bool always_on_top_;
+  base::RepeatingClosure on_bounds_changed_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefWindowDelegateView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_WINDOW_DELEGATE_VIEW_H_
diff --git a/src/libcef/browser/native/window_x11.cc b/src/libcef/browser/native/window_x11.cc
new file mode 100644
index 0000000..7b05589
--- /dev/null
+++ b/src/libcef/browser/native/window_x11.cc
@@ -0,0 +1,475 @@
+// Copyright 2014 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/native/window_x11.h"
+#include "libcef/browser/thread_util.h"
+
+#include "ui/base/x/x11_util.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/x/x11_event_translation.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
+#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XInput2.h>
+
+namespace {
+
+const char kAtom[] = "ATOM";
+const char kWMDeleteWindow[] = "WM_DELETE_WINDOW";
+const char kWMProtocols[] = "WM_PROTOCOLS";
+const char kNetWMName[] = "_NET_WM_NAME";
+const char kNetWMPid[] = "_NET_WM_PID";
+const char kNetWMPing[] = "_NET_WM_PING";
+const char kNetWMState[] = "_NET_WM_STATE";
+const char kXdndProxy[] = "XdndProxy";
+const char kUTF8String[] = "UTF8_STRING";
+
+::Window FindChild(::Display* display, ::Window window) {
+  ::Window root;
+  ::Window parent;
+  ::Window* children;
+  ::Window child_window = x11::None;
+  unsigned int nchildren;
+  if (XQueryTree(display, window, &root, &parent, &children, &nchildren) &&
+      nchildren == 1) {
+    child_window = children[0];
+    XFree(children);
+  }
+  return child_window;
+}
+
+::Window FindToplevelParent(::Display* display, ::Window window) {
+  ::Window top_level_window = window;
+  ::Window root = x11::None;
+  ::Window parent = x11::None;
+  ::Window* children = nullptr;
+  unsigned int nchildren = 0;
+  // Enumerate all parents of "window" to find the highest level window
+  // that either:
+  //   - has a parent that does not contain the _NET_WM_PID property
+  //   - has a parent that is the root window.
+  while (XQueryTree(display, window, &root, &parent, &children, &nchildren)) {
+    if (children) {
+      XFree(children);
+    }
+
+    top_level_window = window;
+    if (!ui::PropertyExists(parent, kNetWMPid) || parent == root) {
+      break;
+    }
+    window = parent;
+  }
+  return top_level_window;
+}
+
+}  // namespace
+
+CEF_EXPORT XDisplay* cef_get_xdisplay() {
+  if (!CEF_CURRENTLY_ON(CEF_UIT))
+    return nullptr;
+  return gfx::GetXDisplay();
+}
+
+CefWindowX11::CefWindowX11(CefRefPtr<CefBrowserHostImpl> browser,
+                           ::Window parent_xwindow,
+                           const gfx::Rect& bounds,
+                           const std::string& title)
+    : browser_(browser),
+      xdisplay_(gfx::GetXDisplay()),
+      parent_xwindow_(parent_xwindow),
+      xwindow_(0),
+      window_mapped_(false),
+      bounds_(bounds),
+      focus_pending_(false),
+      weak_ptr_factory_(this) {
+  if (parent_xwindow_ == x11::None)
+    parent_xwindow_ = DefaultRootWindow(xdisplay_);
+
+  XSetWindowAttributes swa;
+  memset(&swa, 0, sizeof(swa));
+  swa.background_pixmap = x11::None;
+  swa.override_redirect = false;
+  xwindow_ = XCreateWindow(xdisplay_, parent_xwindow_, bounds.x(), bounds.y(),
+                           bounds.width(), bounds.height(),
+                           0,               // border width
+                           CopyFromParent,  // depth
+                           InputOutput,
+                           CopyFromParent,  // visual
+                           CWBackPixmap | CWOverrideRedirect, &swa);
+  CHECK(xwindow_);
+
+  DCHECK(ui::X11EventSource::HasInstance());
+  ui::X11EventSource::GetInstance()->AddXEventDispatcher(this);
+
+  long event_mask = FocusChangeMask | StructureNotifyMask | PropertyChangeMask;
+  XSelectInput(xdisplay_, xwindow_, event_mask);
+  XFlush(xdisplay_);
+
+  // TODO(erg): We currently only request window deletion events. We also
+  // should listen for activation events and anything else that GTK+ listens
+  // for, and do something useful.
+  ::Atom protocols[2];
+  protocols[0] = gfx::GetAtom(kWMDeleteWindow);
+  protocols[1] = gfx::GetAtom(kNetWMPing);
+  XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
+
+  // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
+  // the desktop environment.
+  XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
+
+  // Likewise, the X server needs to know this window's pid so it knows which
+  // program to kill if the window hangs.
+  // XChangeProperty() expects "pid" to be long.
+  static_assert(sizeof(long) >= sizeof(pid_t),
+                "pid_t should not be larger than long");
+  long pid = getpid();
+  XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom(kNetWMPid), XA_CARDINAL, 32,
+                  PropModeReplace, reinterpret_cast<unsigned char*>(&pid), 1);
+
+  // Set the initial window name, if provided.
+  if (!title.empty()) {
+    XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom(kNetWMName),
+                    gfx::GetAtom(kUTF8String), 8, PropModeReplace,
+                    reinterpret_cast<const unsigned char*>(title.c_str()),
+                    title.size());
+  }
+}
+
+CefWindowX11::~CefWindowX11() {
+  DCHECK(!xwindow_);
+  DCHECK(ui::X11EventSource::HasInstance());
+  ui::X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
+}
+
+void CefWindowX11::Close() {
+  XEvent ev = {0};
+  ev.xclient.type = ClientMessage;
+  ev.xclient.window = xwindow_;
+  ev.xclient.message_type = gfx::GetAtom(kWMProtocols);
+  ev.xclient.format = 32;
+  ev.xclient.data.l[0] = gfx::GetAtom(kWMDeleteWindow);
+  ev.xclient.data.l[1] = x11::CurrentTime;
+  XSendEvent(xdisplay_, xwindow_, false, NoEventMask, &ev);
+
+  auto host = GetHost();
+  if (host)
+    host->Close();
+}
+
+void CefWindowX11::Show() {
+  if (xwindow_ == x11::None)
+    return;
+
+  if (!window_mapped_) {
+    // Before we map the window, set size hints. Otherwise, some window managers
+    // will ignore toplevel XMoveWindow commands.
+    XSizeHints size_hints;
+    size_hints.flags = PPosition | PWinGravity;
+    size_hints.x = bounds_.x();
+    size_hints.y = bounds_.y();
+    // Set StaticGravity so that the window position is not affected by the
+    // frame width when running with window manager.
+    size_hints.win_gravity = StaticGravity;
+    XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
+
+    XMapWindow(xdisplay_, xwindow_);
+
+    // TODO(thomasanderson): Find out why this flush is necessary.
+    XFlush(xdisplay_);
+    window_mapped_ = true;
+
+    // Setup the drag and drop proxy on the top level window of the application
+    // to be the child of this window.
+    ::Window child = FindChild(xdisplay_, xwindow_);
+    ::Window toplevel_window = FindToplevelParent(xdisplay_, xwindow_);
+    DCHECK(toplevel_window);
+    if (child && toplevel_window) {
+      // Configure the drag&drop proxy property for the top-most window so
+      // that all drag&drop-related messages will be sent to the child
+      // DesktopWindowTreeHostX11. The proxy property is referenced by
+      // DesktopDragDropClientAuraX11::FindWindowFor.
+      ::Window proxy_target = gfx::kNullAcceleratedWidget;
+      ui::GetXIDProperty(toplevel_window, kXdndProxy, &proxy_target);
+
+      if (proxy_target != child) {
+        // Set the proxy target for the top-most window.
+        XChangeProperty(xdisplay_, toplevel_window, gfx::GetAtom(kXdndProxy),
+                        XA_WINDOW, 32, PropModeReplace,
+                        reinterpret_cast<unsigned char*>(&child), 1);
+        // Do the same for the proxy target per the spec.
+        XChangeProperty(xdisplay_, child, gfx::GetAtom(kXdndProxy), XA_WINDOW,
+                        32, PropModeReplace,
+                        reinterpret_cast<unsigned char*>(&child), 1);
+      }
+    }
+  }
+}
+
+void CefWindowX11::Hide() {
+  if (xwindow_ == x11::None)
+    return;
+
+  if (window_mapped_) {
+    XWithdrawWindow(xdisplay_, xwindow_, 0);
+    window_mapped_ = false;
+  }
+}
+
+void CefWindowX11::Focus() {
+  if (xwindow_ == x11::None || !window_mapped_)
+    return;
+
+  if (browser_.get()) {
+    ::Window child = FindChild(xdisplay_, xwindow_);
+    if (child && ui::IsWindowVisible(child)) {
+      // Give focus to the child DesktopWindowTreeHostX11.
+      XSetInputFocus(xdisplay_, child, RevertToParent, x11::CurrentTime);
+    }
+  } else {
+    XSetInputFocus(xdisplay_, xwindow_, RevertToParent, x11::CurrentTime);
+  }
+}
+
+void CefWindowX11::SetBounds(const gfx::Rect& bounds) {
+  if (xwindow_ == x11::None)
+    return;
+
+  bool origin_changed = bounds_.origin() != bounds.origin();
+  bool size_changed = bounds_.size() != bounds.size();
+  XWindowChanges changes = {0};
+  unsigned value_mask = 0;
+
+  if (size_changed) {
+    changes.width = bounds.width();
+    changes.height = bounds.height();
+    value_mask = CWHeight | CWWidth;
+  }
+
+  if (origin_changed) {
+    changes.x = bounds.x();
+    changes.y = bounds.y();
+    value_mask |= CWX | CWY;
+  }
+
+  if (value_mask)
+    XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
+}
+
+gfx::Rect CefWindowX11::GetBoundsInScreen() {
+  int x, y;
+  Window child;
+  if (XTranslateCoordinates(xdisplay_, xwindow_, DefaultRootWindow(xdisplay_),
+                            0, 0, &x, &y, &child)) {
+    return gfx::Rect(gfx::Point(x, y), bounds_.size());
+  }
+  return gfx::Rect();
+}
+
+views::DesktopWindowTreeHostX11* CefWindowX11::GetHost() {
+  if (browser_.get()) {
+    ::Window child = FindChild(xdisplay_, xwindow_);
+    if (child) {
+      return static_cast<views::DesktopWindowTreeHostX11*>(
+          views::DesktopWindowTreeHostLinux::GetHostForWidget(child));
+    }
+  }
+  return nullptr;
+}
+
+bool CefWindowX11::CanDispatchEvent(const ui::PlatformEvent& event) {
+  DCHECK_NE(xwindow_, x11::None);
+  return !!current_xevent_;
+}
+
+uint32_t CefWindowX11::DispatchEvent(const ui::PlatformEvent& event) {
+  DCHECK_NE(xwindow_, x11::None);
+  DCHECK(event);
+  DCHECK(current_xevent_);
+
+  ProcessXEvent(current_xevent_);
+  return ui::POST_DISPATCH_STOP_PROPAGATION;
+}
+
+// Called by X11EventSourceLibevent to determine whether this XEventDispatcher
+// implementation is able to process the next translated event sent by it.
+void CefWindowX11::CheckCanDispatchNextPlatformEvent(XEvent* xev) {
+  current_xevent_ = IsTargetedBy(*xev) ? xev : nullptr;
+}
+
+void CefWindowX11::PlatformEventDispatchFinished() {
+  current_xevent_ = nullptr;
+}
+
+ui::PlatformEventDispatcher* CefWindowX11::GetPlatformEventDispatcher() {
+  return this;
+}
+
+bool CefWindowX11::DispatchXEvent(XEvent* xev) {
+  if (!IsTargetedBy(*xev))
+    return false;
+  ProcessXEvent(xev);
+  return true;
+}
+
+void CefWindowX11::ContinueFocus() {
+  if (!focus_pending_)
+    return;
+  if (browser_.get())
+    browser_->SetFocus(true);
+  focus_pending_ = false;
+}
+
+bool CefWindowX11::TopLevelAlwaysOnTop() const {
+  ::Window toplevel_window = FindToplevelParent(xdisplay_, xwindow_);
+
+  Atom state_atom = gfx::GetAtom("_NET_WM_STATE");
+  Atom state_keep_above = gfx::GetAtom("_NET_WM_STATE_KEEP_ABOVE");
+  Atom* states;
+
+  Atom actual_type;
+  int actual_format;
+  unsigned long num_items;
+  unsigned long bytes_after;
+
+  XGetWindowProperty(xdisplay_, toplevel_window, state_atom, 0, 1024,
+                     x11::False, XA_ATOM, &actual_type, &actual_format,
+                     &num_items, &bytes_after,
+                     reinterpret_cast<unsigned char**>(&states));
+
+  bool always_on_top = false;
+
+  for (unsigned long i = 0; i < num_items; ++i) {
+    if (states[i] == state_keep_above) {
+      always_on_top = true;
+      break;
+    }
+  }
+
+  XFree(states);
+
+  return always_on_top;
+}
+
+bool CefWindowX11::IsTargetedBy(const XEvent& xev) const {
+  ::Window target_window =
+      (xev.type == GenericEvent)
+          ? static_cast<XIDeviceEvent*>(xev.xcookie.data)->event
+          : xev.xany.window;
+  return target_window == xwindow_;
+}
+
+void CefWindowX11::ProcessXEvent(XEvent* xev) {
+  switch (xev->type) {
+    case ConfigureNotify: {
+      DCHECK_EQ(xwindow_, xev->xconfigure.event);
+      DCHECK_EQ(xwindow_, xev->xconfigure.window);
+      // It's possible that the X window may be resized by some other means
+      // than from within Aura (e.g. the X window manager can change the
+      // size). Make sure the root window size is maintained properly.
+      gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y,
+                       xev->xconfigure.width, xev->xconfigure.height);
+      bounds_ = bounds;
+
+      if (browser_.get()) {
+        ::Window child = FindChild(xdisplay_, xwindow_);
+        if (child) {
+          // Resize the child DesktopWindowTreeHostX11 to match this window.
+          XWindowChanges changes = {0};
+          changes.width = bounds.width();
+          changes.height = bounds.height();
+          XConfigureWindow(xdisplay_, child, CWHeight | CWWidth, &changes);
+
+          browser_->NotifyMoveOrResizeStarted();
+        }
+      }
+      break;
+    }
+    case ClientMessage: {
+      Atom message_type = xev->xclient.message_type;
+      if (message_type == gfx::GetAtom(kWMProtocols)) {
+        Atom protocol = static_cast<Atom>(xev->xclient.data.l[0]);
+        if (protocol == gfx::GetAtom(kWMDeleteWindow)) {
+          // We have received a close message from the window manager.
+          if (!browser_ || browser_->TryCloseBrowser()) {
+            // Allow the close.
+            XDestroyWindow(xdisplay_, xwindow_);
+
+            xwindow_ = x11::None;
+
+            if (browser_.get()) {
+              // Force the browser to be destroyed and release the reference
+              // added in PlatformCreateWindow().
+              browser_->WindowDestroyed();
+            }
+
+            delete this;
+          }
+        } else if (protocol == gfx::GetAtom(kNetWMPing)) {
+          XEvent reply_event = *xev;
+          reply_event.xclient.window = parent_xwindow_;
+
+          XSendEvent(xdisplay_, reply_event.xclient.window, false,
+                     SubstructureRedirectMask | SubstructureNotifyMask,
+                     &reply_event);
+          XFlush(xdisplay_);
+        }
+      }
+      break;
+    }
+    case x11::FocusIn:
+      // This message is received first followed by a "_NET_ACTIVE_WINDOW"
+      // message sent to the root window. When X11DesktopHandler handles the
+      // "_NET_ACTIVE_WINDOW" message it will erroneously mark the WebView
+      // (hosted in a DesktopWindowTreeHostX11) as unfocused. Use a delayed
+      // task here to restore the WebView's focus state.
+      if (!focus_pending_) {
+        focus_pending_ = true;
+        CEF_POST_DELAYED_TASK(CEF_UIT,
+                              base::Bind(&CefWindowX11::ContinueFocus,
+                                         weak_ptr_factory_.GetWeakPtr()),
+                              100);
+      }
+      break;
+    case x11::FocusOut:
+      // Cancel the pending focus change if some other window has gained focus
+      // while waiting for the async task to run. Otherwise we can get stuck in
+      // a focus change loop.
+      if (focus_pending_)
+        focus_pending_ = false;
+      break;
+    case PropertyNotify: {
+      ::Atom changed_atom = xev->xproperty.atom;
+      if (changed_atom == gfx::GetAtom(kNetWMState)) {
+        // State change event like minimize/maximize.
+        if (browser_.get()) {
+          ::Window child = FindChild(xdisplay_, xwindow_);
+          if (child) {
+            // Forward the state change to the child DesktopWindowTreeHostX11
+            // window so that resource usage will be reduced while the window is
+            // minimized.
+            std::vector<::Atom> atom_list;
+            if (ui::GetAtomArrayProperty(xwindow_, kNetWMState, &atom_list) &&
+                !atom_list.empty()) {
+              ui::SetAtomArrayProperty(child, kNetWMState, "ATOM", atom_list);
+            } else {
+              // Set an empty list of property values to pass the check in
+              // DesktopWindowTreeHostX11::OnWMStateUpdated().
+              XChangeProperty(xdisplay_, child,
+                              gfx::GetAtom(kNetWMState),  // name
+                              gfx::GetAtom(kAtom),        // type
+                              32,  // size in bits of items in 'value'
+                              PropModeReplace, NULL,
+                              0);  // num items
+            }
+          }
+        }
+      }
+      break;
+    }
+  }
+}
diff --git a/src/libcef/browser/native/window_x11.h b/src/libcef/browser/native/window_x11.h
new file mode 100644
index 0000000..dbf841f
--- /dev/null
+++ b/src/libcef/browser/native/window_x11.h
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NATIVE_WINDOW_X11_H_
+#define CEF_LIBCEF_BROWSER_NATIVE_WINDOW_X11_H_
+#pragma once
+
+// Avoid including <X11/Xlib.h>
+typedef unsigned long Window;
+struct _XDisplay;
+typedef struct _XDisplay Display;
+
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/memory/weak_ptr.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/x/x11_atom_cache.h"
+
+namespace views {
+class DesktopWindowTreeHostX11;
+}
+
+// Object wrapper for an X11 Window.
+// Based on WindowTreeHostX11 and DesktopWindowTreeHostX11.
+class CefWindowX11 : public ui::PlatformEventDispatcher,
+                     public ui::XEventDispatcher {
+ public:
+  CefWindowX11(CefRefPtr<CefBrowserHostImpl> browser,
+               ::Window parent_xwindow,
+               const gfx::Rect& bounds,
+               const std::string& title);
+  ~CefWindowX11() override;
+
+  void Close();
+
+  void Show();
+  void Hide();
+
+  void Focus();
+
+  void SetBounds(const gfx::Rect& bounds);
+
+  gfx::Rect GetBoundsInScreen();
+
+  views::DesktopWindowTreeHostX11* GetHost();
+
+  // ui::PlatformEventDispatcher methods:
+  bool CanDispatchEvent(const ui::PlatformEvent& event) override;
+  uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
+
+  // ui::XEventDispatcher methods:
+  void CheckCanDispatchNextPlatformEvent(XEvent* xev) override;
+  void PlatformEventDispatchFinished() override;
+  ui::PlatformEventDispatcher* GetPlatformEventDispatcher() override;
+  bool DispatchXEvent(XEvent* event) override;
+
+  ::Window xwindow() const { return xwindow_; }
+  gfx::Rect bounds() const { return bounds_; }
+
+  bool TopLevelAlwaysOnTop() const;
+
+ private:
+  void ContinueFocus();
+
+  bool IsTargetedBy(const XEvent& xev) const;
+  void ProcessXEvent(XEvent* xev);
+
+  CefRefPtr<CefBrowserHostImpl> browser_;
+
+  // The display and the native X window hosting the root window.
+  ::Display* xdisplay_;
+  ::Window parent_xwindow_;
+  ::Window xwindow_;
+
+  // Is the window mapped to the screen?
+  bool window_mapped_;
+
+  // The bounds of |xwindow_|.
+  gfx::Rect bounds_;
+
+  bool focus_pending_;
+
+  // Tells if this dispatcher can process next translated event based on a
+  // previous check in ::CheckCanDispatchNextPlatformEvent based on a XID
+  // target.
+  XEvent* current_xevent_ = nullptr;
+
+  // Must always be the last member.
+  base::WeakPtrFactory<CefWindowX11> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefWindowX11);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_WINDOW_X11_H_
diff --git a/src/libcef/browser/navigate_params.cc b/src/libcef/browser/navigate_params.cc
new file mode 100644
index 0000000..be925ef
--- /dev/null
+++ b/src/libcef/browser/navigate_params.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/navigate_params.h"
+
+CefNavigateParams::CefNavigateParams(const GURL& a_url,
+                                     ui::PageTransition a_transition)
+    : url(a_url), transition(a_transition) {}
+
+CefNavigateParams::~CefNavigateParams() {}
diff --git a/src/libcef/browser/navigate_params.h b/src/libcef/browser/navigate_params.h
new file mode 100644
index 0000000..92b8980
--- /dev/null
+++ b/src/libcef/browser/navigate_params.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NAVIGATE_PARAMS_H_
+#define CEF_LIBCEF_BROWSER_NAVIGATE_PARAMS_H_
+#pragma once
+
+#include <string>
+
+#include "libcef/common/net/upload_data.h"
+
+#include "content/public/browser/global_request_id.h"
+#include "content/public/common/referrer.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
+
+// Parameters that tell CefFrameHostImpl::Navigate() what to do.
+struct CefNavigateParams {
+  CefNavigateParams(const GURL& a_url, ui::PageTransition a_transition);
+  ~CefNavigateParams();
+
+  // The following parameters are sent to the renderer via CefMsg_LoadRequest.
+  // ---------------------------------------------------------------------------
+
+  // Request method.
+  std::string method;
+
+  // The URL/referrer to be loaded.
+  GURL url;
+  content::Referrer referrer;
+
+  // Usually the URL of the document in the top-level window, which may be
+  // checked by the third-party cookie blocking policy. Leaving it empty may
+  // lead to undesired cookie blocking. Third-party cookie blocking can be
+  // bypassed by setting site_for_cookies = url, but this should ideally
+  // only be done if there really is no way to determine the correct value.
+  net::SiteForCookies site_for_cookies;
+
+  // Additional HTTP request headers.
+  std::string headers;
+
+  // net::URLRequest load flags (0 by default).
+  int load_flags = 0;
+
+  // Upload data (may be NULL).
+  scoped_refptr<net::UploadData> upload_data;
+
+  // The following parameters are used to define browser behavior when servicing
+  // the navigation request.
+  // ---------------------------------------------------------------------------
+
+  // The disposition requested by the navigation source. Default is CURRENT_TAB.
+  WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB;
+
+  // The transition type of the navigation.
+  ui::PageTransition transition;
+
+  // Whether this navigation was initiated by the renderer process.
+  bool is_renderer_initiated = false;
+
+  // If non-empty, the new tab contents encoding is overriden by this value.
+  std::string override_encoding;
+
+  // If false then the navigation was not initiated by a user gesture. Default
+  // is true.
+  bool user_gesture = true;
+
+  // Refers to a navigation that was parked in the browser in order to be
+  // transferred to another RVH. Only used in case of a redirection of a request
+  // to a different site that created a new RVH.
+  content::GlobalRequestID transferred_global_request_id;
+
+ private:
+  CefNavigateParams();
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NAVIGATE_PARAMS_H_
diff --git a/src/libcef/browser/navigation_entry_impl.cc b/src/libcef/browser/navigation_entry_impl.cc
new file mode 100644
index 0000000..f06bb54
--- /dev/null
+++ b/src/libcef/browser/navigation_entry_impl.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/navigation_entry_impl.h"
+
+#include "libcef/browser/ssl_status_impl.h"
+#include "libcef/common/time_util.h"
+
+#include "content/public/browser/navigation_entry.h"
+#include "url/gurl.h"
+
+CefNavigationEntryImpl::CefNavigationEntryImpl(content::NavigationEntry* value)
+    : CefValueBase<CefNavigationEntry, content::NavigationEntry>(
+          value,
+          nullptr,
+          kOwnerNoDelete,
+          false,
+          new CefValueControllerNonThreadSafe()) {
+  // Indicate that this object owns the controller.
+  SetOwnsController();
+}
+
+bool CefNavigationEntryImpl::IsValid() {
+  return !detached();
+}
+
+CefString CefNavigationEntryImpl::GetURL() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return mutable_value()->GetURL().spec();
+}
+
+CefString CefNavigationEntryImpl::GetDisplayURL() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return mutable_value()->GetVirtualURL().spec();
+}
+
+CefString CefNavigationEntryImpl::GetOriginalURL() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return mutable_value()->GetUserTypedURL().spec();
+}
+
+CefString CefNavigationEntryImpl::GetTitle() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return mutable_value()->GetTitle();
+}
+
+CefNavigationEntry::TransitionType CefNavigationEntryImpl::GetTransitionType() {
+  CEF_VALUE_VERIFY_RETURN(false, TT_EXPLICIT);
+  return static_cast<TransitionType>(mutable_value()->GetTransitionType());
+}
+
+bool CefNavigationEntryImpl::HasPostData() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return mutable_value()->GetHasPostData();
+}
+
+CefTime CefNavigationEntryImpl::GetCompletionTime() {
+  CefTime time;
+  CEF_VALUE_VERIFY_RETURN(false, time);
+  cef_time_from_basetime(mutable_value()->GetTimestamp(), time);
+  return time;
+}
+
+int CefNavigationEntryImpl::GetHttpStatusCode() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return mutable_value()->GetHttpStatusCode();
+}
+
+CefRefPtr<CefSSLStatus> CefNavigationEntryImpl::GetSSLStatus() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  return new CefSSLStatusImpl(mutable_value()->GetSSL());
+}
diff --git a/src/libcef/browser/navigation_entry_impl.h b/src/libcef/browser/navigation_entry_impl.h
new file mode 100644
index 0000000..23d10ce
--- /dev/null
+++ b/src/libcef/browser/navigation_entry_impl.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NAVIGATION_ENTRY_IMPL_H_
+#define CEF_LIBCEF_BROWSER_NAVIGATION_ENTRY_IMPL_H_
+#pragma once
+
+#include "include/cef_navigation_entry.h"
+#include "libcef/common/value_base.h"
+
+namespace content {
+class NavigationEntry;
+}
+
+// CefNavigationEntry implementation
+class CefNavigationEntryImpl
+    : public CefValueBase<CefNavigationEntry, content::NavigationEntry> {
+ public:
+  explicit CefNavigationEntryImpl(content::NavigationEntry* value);
+
+  // CefNavigationEntry methods.
+  bool IsValid() override;
+  CefString GetURL() override;
+  CefString GetDisplayURL() override;
+  CefString GetOriginalURL() override;
+  CefString GetTitle() override;
+  TransitionType GetTransitionType() override;
+  bool HasPostData() override;
+  CefTime GetCompletionTime() override;
+  int GetHttpStatusCode() override;
+  CefRefPtr<CefSSLStatus> GetSSLStatus() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefNavigationEntryImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NAVIGATION_ENTRY_IMPL_H_
diff --git a/src/libcef/browser/net/chrome_scheme_handler.cc b/src/libcef/browser/net/chrome_scheme_handler.cc
new file mode 100644
index 0000000..fcf6c8f
--- /dev/null
+++ b/src/libcef/browser/net/chrome_scheme_handler.cc
@@ -0,0 +1,747 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/net/chrome_scheme_handler.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "include/cef_version.h"
+#include "include/cef_web_plugin.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/extensions/chrome_api_registration.h"
+#include "libcef/browser/frame_host_impl.h"
+#include "libcef/browser/net/internal_scheme_handler.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/content_client.h"
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "cef/grit/cef_resources.h"
+#include "chrome/browser/browser_about_handler.h"
+#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/frame_host/debug_urls.h"
+#include "content/browser/webui/content_web_ui_controller_factory.h"
+#include "content/public/browser/browser_url_handler.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/common/url_utils.h"
+#include "content/public/common/user_agent.h"
+#include "ipc/ipc_channel.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "v8/include/v8.h"
+
+using extensions::api::cef::kSupportedAPIs;
+
+namespace scheme {
+
+const char kChromeURL[] = "chrome://";
+
+namespace {
+
+const char kChromeUIExtensionsSupportHost[] = "extensions-support";
+const char kChromeUILicenseHost[] = "license";
+const char kChromeUIWebUIHostsHost[] = "webui-hosts";
+
+// TODO(network): Consider handling content::kChromeDevToolsScheme via WebUI
+// (DevToolsUI class) with the following changes:
+// 1. Add an entry for content::kChromeDevToolsScheme in
+//    CefContentBrowserClient::GetAdditionalWebUISchemes.
+// 2. Allow the scheme in CefWebUIControllerFactory::AllowWebUIForURL.
+// 3. Add an entry for chrome::kChromeUIDevToolsHost in kAllowedWebUIHosts and
+//    kUnlistedHosts.
+// 4. Remove scheme::RegisterInternalHandlers and related plumbing.
+
+// Chrome hosts implemented by WebUI.
+// Some WebUI handlers have Chrome dependencies that may fail in CEF without
+// additional changes. Do not add new hosts to this list without also manually
+// testing all related functionality in CEF.
+const char* kAllowedWebUIHosts[] = {
+    content::kChromeUIAppCacheInternalsHost,
+    chrome::kChromeUIAccessibilityHost,
+    content::kChromeUIBlobInternalsHost,
+    chrome::kChromeUIChromeURLsHost,
+    chrome::kChromeUICreditsHost,
+    kChromeUIExtensionsSupportHost,
+    content::kChromeUIGpuHost,
+    content::kChromeUIHistogramHost,
+    content::kChromeUIIndexedDBInternalsHost,
+    kChromeUILicenseHost,
+    content::kChromeUIMediaInternalsHost,
+    chrome::kChromeUINetExportHost,
+    chrome::kChromeUINetInternalsHost,
+    content::kChromeUINetworkErrorHost,
+    content::kChromeUINetworkErrorsListingHost,
+    chrome::kChromeUIPrintHost,
+    content::kChromeUIProcessInternalsHost,
+    content::kChromeUIResourcesHost,
+    content::kChromeUIServiceWorkerInternalsHost,
+    chrome::kChromeUISystemInfoHost,
+    chrome::kChromeUIThemeHost,
+    content::kChromeUITracingHost,
+    chrome::kChromeUIVersionHost,
+    content::kChromeUIWebRTCInternalsHost,
+    kChromeUIWebUIHostsHost,
+};
+
+// Hosts that don't have useful output when linked directly. They'll be excluded
+// from the "chrome://webui-hosts" listing.
+const char* kUnlistedHosts[] = {
+    content::kChromeUINetworkErrorHost,
+    content::kChromeUIResourcesHost,
+    chrome::kChromeUIThemeHost,
+};
+
+enum ChromeHostId {
+  CHROME_UNKNOWN = 0,
+  CHROME_EXTENSIONS_SUPPORT,
+  CHROME_LICENSE,
+  CHROME_VERSION,
+  CHROME_WEBUI_HOSTS,
+};
+
+// Chrome hosts implemented by CEF.
+const struct {
+  const char* host;
+  ChromeHostId host_id;
+} kAllowedCefHosts[] = {
+    {chrome::kChromeUIChromeURLsHost, CHROME_WEBUI_HOSTS},
+    {kChromeUIExtensionsSupportHost, CHROME_EXTENSIONS_SUPPORT},
+    {kChromeUILicenseHost, CHROME_LICENSE},
+    {chrome::kChromeUIVersionHost, CHROME_VERSION},
+    {kChromeUIWebUIHostsHost, CHROME_WEBUI_HOSTS},
+};
+
+ChromeHostId GetChromeHostId(const std::string& host) {
+  for (size_t i = 0; i < sizeof(kAllowedCefHosts) / sizeof(kAllowedCefHosts[0]);
+       ++i) {
+    if (base::EqualsCaseInsensitiveASCII(kAllowedCefHosts[i].host,
+                                         host.c_str())) {
+      return kAllowedCefHosts[i].host_id;
+    }
+  }
+
+  return CHROME_UNKNOWN;
+}
+
+// Returns WebUI hosts. Does not include chrome debug hosts (for crashing, etc).
+void GetAllowedHosts(std::vector<std::string>* hosts) {
+  // Explicitly whitelisted WebUI hosts.
+  for (size_t i = 0;
+       i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
+    hosts->push_back(kAllowedWebUIHosts[i]);
+  }
+}
+
+// Returns true if a host should not be listed on "chrome://webui-hosts".
+bool IsUnlistedHost(const std::string& host) {
+  for (size_t i = 0; i < sizeof(kUnlistedHosts) / sizeof(kUnlistedHosts[0]);
+       ++i) {
+    if (host == kUnlistedHosts[i])
+      return true;
+  }
+  return false;
+}
+
+// Returns true if a host is WebUI and should be allowed to load.
+bool IsAllowedWebUIHost(const std::string& host) {
+  // Explicitly whitelisted WebUI hosts.
+  for (size_t i = 0;
+       i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
+    if (base::EqualsCaseInsensitiveASCII(kAllowedWebUIHosts[i], host.c_str())) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+// Additional debug URLs that are not included in chrome::kChromeDebugURLs.
+const char* kAllowedDebugURLs[] = {
+    content::kChromeUIBrowserCrashURL,
+};
+
+void GetDebugURLs(std::vector<std::string>* urls) {
+  for (size_t i = 0; i < chrome::kNumberOfChromeDebugURLs; ++i) {
+    urls->push_back(chrome::kChromeDebugURLs[i]);
+  }
+
+  for (size_t i = 0;
+       i < sizeof(kAllowedDebugURLs) / sizeof(kAllowedDebugURLs[0]); ++i) {
+    urls->push_back(kAllowedDebugURLs[i]);
+  }
+}
+
+std::string GetOSType() {
+#if defined(OS_WIN)
+  return "Windows";
+#elif defined(OS_MACOSX)
+  return "Mac OS X";
+#elif defined(OS_CHROMEOS)
+  return "Chromium OS";
+#elif defined(OS_ANDROID)
+  return "Android";
+#elif defined(OS_LINUX)
+  return "Linux";
+#elif defined(OS_FREEBSD)
+  return "FreeBSD";
+#elif defined(OS_OPENBSD)
+  return "OpenBSD";
+#elif defined(OS_SOLARIS)
+  return "Solaris";
+#else
+  return "Unknown";
+#endif
+}
+
+std::string GetCommandLine() {
+#if defined(OS_WIN)
+  return base::WideToUTF8(
+      base::CommandLine::ForCurrentProcess()->GetCommandLineString());
+#elif defined(OS_POSIX)
+  std::string command_line = "";
+  typedef std::vector<std::string> ArgvList;
+  const ArgvList& argv = base::CommandLine::ForCurrentProcess()->argv();
+  for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++)
+    command_line += " " + *iter;
+  // TODO(viettrungluu): |command_line| could really have any encoding, whereas
+  // below we assumes it's UTF-8.
+  return command_line;
+#endif
+}
+
+std::string GetModulePath() {
+  base::FilePath path;
+  if (base::PathService::Get(base::FILE_MODULE, &path))
+    return CefString(path.value());
+  return std::string();
+}
+
+class TemplateParser {
+ public:
+  TemplateParser() : ident_start_("$$"), ident_end_("$$") {}
+
+  TemplateParser(const std::string& ident_start, const std::string& ident_end)
+      : ident_start_(ident_start), ident_end_(ident_end) {}
+
+  void Add(const std::string& key, const std::string& value) {
+    values_.insert(std::make_pair(key, value));
+  }
+
+  void Parse(std::string* tmpl) {
+    int start_pos, end_pos = 0;
+    int ident_start_len = ident_start_.length();
+    int ident_end_len = ident_end_.length();
+
+    while (true) {
+      start_pos = tmpl->find(ident_start_, end_pos);
+      if (start_pos >= 0) {
+        end_pos = tmpl->find(ident_end_, start_pos + ident_start_len);
+        if (end_pos >= 0) {
+          // Found an identifier. Check if a substitution exists.
+          std::string key = tmpl->substr(start_pos + ident_start_len,
+                                         end_pos - start_pos - ident_start_len);
+          KeyMap::const_iterator it = values_.find(key);
+          if (it != values_.end()) {
+            // Peform the substitution.
+            tmpl->replace(start_pos, end_pos + ident_end_len - start_pos,
+                          it->second);
+            end_pos = start_pos + it->second.length();
+          } else {
+            // Leave the unknown identifier in place.
+            end_pos += ident_end_len;
+          }
+
+          if (end_pos >= static_cast<int>(tmpl->length()) - ident_start_len -
+                             ident_end_len) {
+            // Not enough room remaining for more identifiers.
+            break;
+          }
+        } else {
+          // No end identifier found.
+          break;
+        }
+      } else {
+        // No start identifier found.
+        break;
+      }
+    }
+  }
+
+ private:
+  typedef std::map<std::string, std::string> KeyMap;
+  KeyMap values_;
+  std::string ident_start_;
+  std::string ident_end_;
+};
+
+bool OnExtensionsSupportUI(std::string* mime_type, std::string* output) {
+  static const char kDevURL[] = "https://developer.chrome.com/extensions/";
+
+  std::string html =
+      "<html>\n<head><title>Extensions Support</title></head>\n"
+      "<body bgcolor=\"white\"><h3>Supported Chrome Extensions "
+      "APIs</h3>\nFollow <a "
+      "href=\"https://bitbucket.org/chromiumembedded/cef/issues/1947\" "
+      "target=\"new\">issue #1947</a> for development progress.\n<ul>\n";
+
+  bool has_top_level_name = false;
+  for (size_t i = 0; kSupportedAPIs[i] != nullptr; ++i) {
+    const std::string& api_name = kSupportedAPIs[i];
+    if (api_name.find("Private") != std::string::npos) {
+      // Don't list private APIs.
+      continue;
+    }
+
+    const size_t dot_pos = api_name.find('.');
+    if (dot_pos == std::string::npos) {
+      if (has_top_level_name) {
+        // End the previous top-level API entry.
+        html += "</ul></li>\n";
+      } else {
+        has_top_level_name = true;
+      }
+
+      // Start a new top-level API entry.
+      html += "<li><a href=\"" + std::string(kDevURL) + api_name +
+              "\" target=\"new\">" + api_name + "</a><ul>\n";
+    } else {
+      // Function name.
+      const std::string& group_name = api_name.substr(0, dot_pos);
+      const std::string& function_name = api_name.substr(dot_pos + 1);
+      html += "\t<li><a href=\"" + std::string(kDevURL) + group_name +
+              "#method-" + function_name + "\" target=\"new\">" + api_name +
+              "</a></li>\n";
+    }
+  }
+
+  if (has_top_level_name) {
+    // End the last top-level API entry.
+    html += "</ul></li>\n";
+  }
+
+  html += "</ul>\n</body>\n</html>";
+
+  *mime_type = "text/html";
+  *output = html;
+
+  return true;
+}
+
+bool OnLicenseUI(std::string* mime_type, std::string* output) {
+  std::string piece =
+      ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
+          IDR_CEF_LICENSE_TXT);
+  if (piece.empty()) {
+    NOTREACHED() << "Failed to load license txt resource.";
+    return false;
+  }
+
+  *mime_type = "text/html";
+  *output = "<html><head><title>License</title></head><body><pre>" + piece +
+            "</pre></body></html>";
+
+  return true;
+}
+
+bool OnVersionUI(Profile* profile,
+                 std::string* mime_type,
+                 std::string* output) {
+  std::string tmpl =
+      ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
+          IDR_CEF_VERSION_HTML);
+  if (tmpl.empty()) {
+    NOTREACHED() << "Failed to load version html resource.";
+    return false;
+  }
+
+  TemplateParser parser;
+  parser.Add("YEAR", MAKE_STRING(COPYRIGHT_YEAR));
+  parser.Add("CEF", CEF_VERSION);
+  parser.Add("CHROMIUM",
+             base::StringPrintf("%d.%d.%d.%d", CHROME_VERSION_MAJOR,
+                                CHROME_VERSION_MINOR, CHROME_VERSION_BUILD,
+                                CHROME_VERSION_PATCH));
+  parser.Add("OS", GetOSType());
+  parser.Add("WEBKIT", content::GetWebKitVersion());
+  parser.Add("JAVASCRIPT", v8::V8::GetVersion());
+  parser.Add("FLASH", std::string());  // Value populated asynchronously.
+  parser.Add("USERAGENT", CefContentClient::Get()->browser()->GetUserAgent());
+  parser.Add("COMMANDLINE", GetCommandLine());
+  parser.Add("MODULEPATH", GetModulePath());
+  parser.Add("CACHEPATH", CefString(profile->GetPath().value()));
+
+  parser.Parse(&tmpl);
+
+  *mime_type = "text/html";
+  *output = tmpl;
+
+  return true;
+}
+
+bool OnWebUIHostsUI(std::string* mime_type, std::string* output) {
+  std::string html =
+      "<html>\n<head><title>Chrome URLs</title></head>\n"
+      "<body bgcolor=\"white\"><h3>List of Chrome URLs</h3>\n<ul>\n";
+
+  std::vector<std::string> list;
+  GetAllowedHosts(&list);
+  std::sort(list.begin(), list.end());
+
+  for (size_t i = 0U; i < list.size(); ++i) {
+    if (IsUnlistedHost(list[i]))
+      continue;
+
+    html += "<li><a href=\"chrome://" + list[i] + "\">chrome://" + list[i] +
+            "</a></li>\n";
+  }
+
+  list.clear();
+  GetDebugURLs(&list);
+  std::sort(list.begin(), list.end());
+
+  html +=
+      "</ul>\n<h3>For Debug</h3>\n"
+      "<p>The following pages are for debugging purposes only. Because they "
+      "crash or hang the renderer, they're not linked directly; you can type "
+      "them into the address bar if you need them.</p>\n<ul>\n";
+  for (size_t i = 0U; i < list.size(); ++i) {
+    html += "<li>" + std::string(list[i]) + "</li>\n";
+  }
+  html += "</ul>\n";
+
+  html += "</body>\n</html>";
+
+  *mime_type = "text/html";
+  *output = html;
+
+  return true;
+}
+
+const content::WebUI::TypeID kCefWebUITypeID = &kCefWebUITypeID;
+
+class CefURLDataSource : public content::URLDataSource {
+ public:
+  CefURLDataSource(const std::string& host,
+                   ChromeHostId host_id,
+                   Profile* profile)
+      : host_(host), host_id_(host_id), profile_(profile) {
+    CEF_REQUIRE_UIT();
+    output_ = new base::RefCountedString();
+    bool handled = false;
+    switch (host_id_) {
+      case CHROME_EXTENSIONS_SUPPORT:
+        handled = OnExtensionsSupportUI(&mime_type_, &output_->data());
+        break;
+      case CHROME_LICENSE:
+        handled = OnLicenseUI(&mime_type_, &output_->data());
+        break;
+      case CHROME_VERSION:
+        handled = OnVersionUI(profile_, &mime_type_, &output_->data());
+        break;
+      case CHROME_WEBUI_HOSTS:
+        handled = OnWebUIHostsUI(&mime_type_, &output_->data());
+        break;
+      default:
+        break;
+    }
+    DCHECK(handled) << "Unhandled WebUI host: " << host;
+  }
+
+  ~CefURLDataSource() override = default;
+
+  // content::URLDataSource implementation.
+  std::string GetSource() override { return host_; }
+
+  void StartDataRequest(
+      const GURL& path,
+      const content::WebContents::Getter& wc_getter,
+      content::URLDataSource::GotDataCallback callback) override {
+    std::move(callback).Run(output_);
+  }
+
+  std::string GetMimeType(const std::string& path) override {
+    return mime_type_;
+  }
+
+  bool AllowCaching() override { return false; }
+
+ private:
+  const std::string host_;
+  const ChromeHostId host_id_;
+  Profile* const profile_;
+
+  std::string mime_type_;
+  scoped_refptr<base::RefCountedString> output_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefURLDataSource);
+};
+
+class CefWebUIController : public content::WebUIController {
+ public:
+  CefWebUIController(content::WebUI* web_ui,
+                     const std::string& host,
+                     ChromeHostId host_id)
+      : content::WebUIController(web_ui) {
+    Profile* profile = Profile::FromWebUI(web_ui);
+    content::URLDataSource::Add(
+        profile, std::make_unique<CefURLDataSource>(host, host_id, profile));
+  }
+
+  ~CefWebUIController() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefWebUIController);
+};
+
+// Intercepts all WebUI calls and either blocks them or forwards them to the
+// Content or Chrome WebUI factory as appropriate.
+class CefWebUIControllerFactory : public content::WebUIControllerFactory {
+ public:
+  // Returns true if WebUI is allowed to handle the specified |url|.
+  static bool AllowWebUIForURL(const GURL& url) {
+    if (!url.SchemeIs(content::kChromeUIScheme))
+      return false;
+
+    if (IsAllowedWebUIHost(url.host()))
+      return true;
+
+    return false;
+  }
+
+  // Returns true if WebUI is allowed to make network requests.
+  static bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) {
+    if (!AllowWebUIForURL(origin.GetURL()))
+      return false;
+
+    if (ChromeWebUIControllerFactory::IsWebUIAllowedToMakeNetworkRequests(
+            origin)) {
+      return true;
+    }
+
+    return false;
+  }
+
+  std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(
+      content::WebUI* web_ui,
+      const GURL& url) override {
+    std::unique_ptr<content::WebUIController> controller;
+    if (!AllowWebUIForURL(url))
+      return controller;
+
+    // Set up the chrome://theme/ source. These URLs are referenced from many
+    // places (WebUI and chrome://resources which live in //ui). WebUI code
+    // can live in both //content and //chrome. Since ThemeSource lives in
+    // //chrome the WebUI from //content is not performing this setup despite
+    // the fact that it's needed for proper handling of theme resource requests.
+    // See https://crbug.com/1011280.
+    Profile* profile = Profile::FromWebUI(web_ui);
+    content::URLDataSource::Add(profile,
+                                std::make_unique<ThemeSource>(profile));
+
+    const auto host_id = GetChromeHostId(url.host());
+    if (host_id != CHROME_UNKNOWN) {
+      return std::make_unique<CefWebUIController>(web_ui, url.host(), host_id);
+    }
+
+    controller = content::ContentWebUIControllerFactory::GetInstance()
+                     ->CreateWebUIControllerForURL(web_ui, url);
+    if (controller.get())
+      return controller;
+
+    return ChromeWebUIControllerFactory::GetInstance()
+        ->CreateWebUIControllerForURL(web_ui, url);
+  }
+
+  content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
+                                      const GURL& url) override {
+    content::WebUI::TypeID type = content::WebUI::kNoWebUI;
+    if (!AllowWebUIForURL(url))
+      return type;
+
+    const auto host_id = GetChromeHostId(url.host());
+    if (host_id != CHROME_UNKNOWN) {
+      return kCefWebUITypeID;
+    }
+
+    type = content::ContentWebUIControllerFactory::GetInstance()->GetWebUIType(
+        browser_context, url);
+    if (type != content::WebUI::kNoWebUI)
+      return type;
+
+    type = ChromeWebUIControllerFactory::GetInstance()->GetWebUIType(
+        browser_context, url);
+    if (type != content::WebUI::kNoWebUI)
+      return type;
+
+    return content::WebUI::kNoWebUI;
+  }
+
+  bool UseWebUIForURL(content::BrowserContext* browser_context,
+                      const GURL& url) override {
+    if (!AllowWebUIForURL(url))
+      return false;
+
+    const auto host_id = GetChromeHostId(url.host());
+    if (host_id != CHROME_UNKNOWN) {
+      return true;
+    }
+
+    if (content::ContentWebUIControllerFactory::GetInstance()->UseWebUIForURL(
+            browser_context, url) ||
+        ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
+            browser_context, url)) {
+      return true;
+    }
+
+    return false;
+  }
+
+  bool UseWebUIBindingsForURL(content::BrowserContext* browser_context,
+                              const GURL& url) override {
+    if (!AllowWebUIForURL(url))
+      return false;
+
+    const auto host_id = GetChromeHostId(url.host());
+    if (host_id != CHROME_UNKNOWN) {
+      // TODO(network): Use WebUI bindings to implement DidFinishChromeLoad.
+      return false;
+    }
+
+    if (content::ContentWebUIControllerFactory::GetInstance()
+            ->UseWebUIBindingsForURL(browser_context, url) ||
+        ChromeWebUIControllerFactory::GetInstance()->UseWebUIBindingsForURL(
+            browser_context, url)) {
+      return true;
+    }
+
+    return false;
+  }
+
+  static void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) {
+    // about: handler. Must come before chrome: handler, since it will
+    // rewrite about: urls to chrome: URLs and then expect chrome: to
+    // actually handle them.  Also relies on a preliminary fixup phase.
+    handler->SetFixupHandler(&FixupBrowserAboutURL);
+    handler->AddHandlerPair(&WillHandleBrowserAboutURL,
+                            content::BrowserURLHandler::null_handler());
+
+    // chrome: & friends.
+    handler->AddHandlerPair(&HandleWebUI, &HandleWebUIReverse);
+  }
+
+  static CefWebUIControllerFactory* GetInstance();
+
+ protected:
+  CefWebUIControllerFactory() {}
+  ~CefWebUIControllerFactory() override {}
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<CefWebUIControllerFactory>;
+
+  // From chrome/browser/chrome_content_browser_client.cc
+
+  // Handles rewriting Web UI URLs.
+  static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context) {
+    if (!GetInstance()->UseWebUIForURL(browser_context, *url))
+      return false;
+
+    return true;
+  }
+
+  // Reverse URL handler for Web UI.
+  static bool HandleWebUIReverse(GURL* url,
+                                 content::BrowserContext* browser_context) {
+    // No need to actually reverse-rewrite the URL.
+    return false;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(CefWebUIControllerFactory);
+};
+
+base::LazyInstance<CefWebUIControllerFactory>::Leaky
+    g_web_ui_controller_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+CefWebUIControllerFactory* CefWebUIControllerFactory::GetInstance() {
+  return &g_web_ui_controller_factory.Get();
+}
+
+void DidFinishChromeVersionLoad(CefRefPtr<CefFrame> frame) {
+  // Retieve Flash version information and update asynchronously.
+  class Visitor : public CefWebPluginInfoVisitor {
+   public:
+    Visitor(CefRefPtr<CefFrame> frame) : frame_(frame) {}
+
+    bool Visit(CefRefPtr<CefWebPluginInfo> info,
+               int count,
+               int total) override {
+      std::string name = info->GetName();
+      if (name == "Shockwave Flash") {
+        if (frame_->IsValid()) {
+          std::string version = info->GetVersion();
+          frame_->ExecuteJavaScript(
+              "document.getElementById('flash').innerText = '" + version + "';",
+              std::string(), 0);
+        }
+        return false;
+      }
+
+      return true;
+    }
+
+   private:
+    CefRefPtr<CefFrame> frame_;
+
+    IMPLEMENT_REFCOUNTING(Visitor);
+  };
+
+  CefVisitWebPluginInfo(new Visitor(frame));
+}
+
+}  // namespace
+
+void RegisterWebUIControllerFactory() {
+  // Channel all WebUI handling through CefWebUIControllerFactory.
+  content::WebUIControllerFactory::UnregisterFactoryForTesting(
+      content::ContentWebUIControllerFactory::GetInstance());
+
+  content::WebUIControllerFactory::RegisterFactory(
+      CefWebUIControllerFactory::GetInstance());
+}
+
+void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) {
+  CefWebUIControllerFactory::BrowserURLHandlerCreated(handler);
+}
+
+void DidFinishChromeLoad(CefRefPtr<CefFrame> frame, const GURL& validated_url) {
+  ChromeHostId host_id = GetChromeHostId(validated_url.host());
+  switch (host_id) {
+    case CHROME_VERSION:
+      DidFinishChromeVersionLoad(frame);
+      break;
+    default:
+      break;
+  }
+}
+
+bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) {
+  return CefWebUIControllerFactory::IsWebUIAllowedToMakeNetworkRequests(origin);
+}
+
+}  // namespace scheme
diff --git a/src/libcef/browser/net/chrome_scheme_handler.h b/src/libcef/browser/net/chrome_scheme_handler.h
new file mode 100644
index 0000000..f65cf34
--- /dev/null
+++ b/src/libcef/browser/net/chrome_scheme_handler.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_CHROME_SCHEME_HANDLER_H_
+#define CEF_LIBCEF_BROWSER_NET_CHROME_SCHEME_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_process_message.h"
+
+#include "url/gurl.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class BrowserURLHandler;
+}
+
+namespace url {
+class Origin;
+}
+
+namespace scheme {
+
+extern const char kChromeURL[];
+
+// Register the WebUI controller factory.
+void RegisterWebUIControllerFactory();
+
+// Register the WebUI handler.
+void BrowserURLHandlerCreated(content::BrowserURLHandler* handler);
+
+// Used to fire any asynchronous content updates.
+void DidFinishChromeLoad(CefRefPtr<CefFrame> frame, const GURL& validated_url);
+
+// Returns true if WebUI is allowed to make network requests.
+bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin);
+
+}  // namespace scheme
+
+#endif  // CEF_LIBCEF_BROWSER_CHROME_SCHEME_HANDLER_H_
diff --git a/src/libcef/browser/net/crlset_file_util_impl.cc b/src/libcef/browser/net/crlset_file_util_impl.cc
new file mode 100644
index 0000000..fecd791
--- /dev/null
+++ b/src/libcef/browser/net/crlset_file_util_impl.cc
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_file_util.h"
+
+#include "libcef/browser/context.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "content/public/browser/network_service_instance.h"
+#include "services/network/network_service.h"
+
+namespace {
+
+void UpdateCRLSet(const std::string& crl_set_bytes) {
+  CEF_REQUIRE_UIT();
+  content::GetNetworkService()->UpdateCRLSet(
+      base::as_bytes(base::make_span(crl_set_bytes)), base::DoNothing());
+}
+
+void LoadFromDisk(const base::FilePath& path) {
+  CEF_REQUIRE_BLOCKING();
+
+  std::string crl_set_bytes;
+  if (!base::ReadFileToString(path, &crl_set_bytes)) {
+    LOG(WARNING) << "Failed to read CRL set from " << path.MaybeAsASCII();
+    return;
+  }
+
+  VLOG(1) << "Loading " << crl_set_bytes.size()
+          << " bytes of CRL set from disk";
+  CEF_POST_TASK(CEF_UIT, base::BindOnce(&UpdateCRLSet, crl_set_bytes));
+}
+
+}  // namespace
+
+void CefLoadCRLSetsFile(const CefString& path) {
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  CEF_POST_USER_VISIBLE_TASK(base::BindOnce(&LoadFromDisk, path));
+}
diff --git a/src/libcef/browser/net/devtools_scheme_handler.cc b/src/libcef/browser/net/devtools_scheme_handler.cc
new file mode 100644
index 0000000..e16faaa
--- /dev/null
+++ b/src/libcef/browser/net/devtools_scheme_handler.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/net/devtools_scheme_handler.h"
+
+#include <string>
+
+#include "libcef/browser/net/internal_scheme_handler.h"
+#include "libcef/browser/resource_context.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "content/public/browser/devtools_frontend_host.h"
+#include "content/public/common/url_constants.h"
+
+namespace scheme {
+
+const char kChromeDevToolsHost[] = "devtools";
+
+namespace {
+
+class Delegate : public InternalHandlerDelegate {
+ public:
+  Delegate() {}
+
+  bool OnRequest(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefRequest> request,
+                 Action* action) override {
+    GURL url = GURL(request->GetURL().ToString());
+    std::string path = url.path();
+    if (path.length() > 0)
+      path = path.substr(1);
+
+    action->bytes =
+        content::DevToolsFrontendHost::GetFrontendResourceBytes(path);
+    return !!action->bytes;
+  }
+};
+
+}  // namespace
+
+void RegisterChromeDevToolsHandler(CefResourceContext* resource_context) {
+  resource_context->RegisterSchemeHandlerFactory(
+      content::kChromeDevToolsScheme, kChromeDevToolsHost,
+      CreateInternalHandlerFactory(base::WrapUnique(new Delegate())));
+}
+
+}  // namespace scheme
diff --git a/src/libcef/browser/net/devtools_scheme_handler.h b/src/libcef/browser/net/devtools_scheme_handler.h
new file mode 100644
index 0000000..667724c
--- /dev/null
+++ b/src/libcef/browser/net/devtools_scheme_handler.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_DEVTOOLS_SCHEME_HANDLER_H_
+#define CEF_LIBCEF_BROWSER_NET_DEVTOOLS_SCHEME_HANDLER_H_
+#pragma once
+
+class CefResourceContext;
+
+namespace scheme {
+
+extern const char kChromeDevToolsHost[];
+
+// Register the chrome-devtools scheme handler.
+void RegisterChromeDevToolsHandler(CefResourceContext* resource_context);
+
+}  // namespace scheme
+
+#endif  // CEF_LIBCEF_BROWSER_NET_DEVTOOLS_SCHEME_HANDLER_H_
diff --git a/src/libcef/browser/net/internal_scheme_handler.cc b/src/libcef/browser/net/internal_scheme_handler.cc
new file mode 100644
index 0000000..5deae26
--- /dev/null
+++ b/src/libcef/browser/net/internal_scheme_handler.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/net/internal_scheme_handler.h"
+
+#include <string>
+#include <utility>
+
+#include "libcef/common/content_client.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "net/base/mime_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace scheme {
+
+namespace {
+
+base::FilePath FilePathFromASCII(const std::string& str) {
+#if defined(OS_WIN)
+  return base::FilePath(base::ASCIIToUTF16(str));
+#else
+  return base::FilePath(str);
+#endif
+}
+
+std::string GetMimeType(const std::string& filename) {
+  // Requests should not block on the disk!  On POSIX this goes to disk.
+  // http://code.google.com/p/chromium/issues/detail?id=59849
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+  std::string mime_type;
+  const base::FilePath& file_path = FilePathFromASCII(filename);
+  if (net::GetMimeTypeFromFile(file_path, &mime_type))
+    return mime_type;
+
+  // Check for newer extensions used by internal resources but not yet
+  // recognized by the mime type detector.
+  const std::string& extension = CefString(file_path.FinalExtension());
+  if (extension == ".woff2")
+    return "application/font-woff2";
+
+  NOTREACHED() << "No known mime type for file: " << filename.c_str();
+  return "text/plain";
+}
+
+class RedirectHandler : public CefResourceHandler {
+ public:
+  explicit RedirectHandler(const GURL& url) : url_(url) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    response_length = 0;
+    redirectUrl = url_.spec();
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    NOTREACHED();
+    return false;
+  }
+
+  void Cancel() override {}
+
+ private:
+  GURL url_;
+
+  IMPLEMENT_REFCOUNTING(RedirectHandler);
+  DISALLOW_COPY_AND_ASSIGN(RedirectHandler);
+};
+
+class InternalHandler : public CefResourceHandler {
+ public:
+  InternalHandler(const std::string& mime_type,
+                  CefRefPtr<CefStreamReader> reader,
+                  int size)
+      : mime_type_(mime_type), reader_(reader), size_(size) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    response_length = size_;
+
+    response->SetMimeType(mime_type_);
+    response->SetStatus(200);
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    // Read until the buffer is full or until Read() returns 0 to indicate no
+    // more data.
+    bytes_read = 0;
+    int read = 0;
+    do {
+      read = static_cast<int>(
+          reader_->Read(static_cast<char*>(data_out) + bytes_read, 1,
+                        bytes_to_read - bytes_read));
+      bytes_read += read;
+    } while (read != 0 && bytes_read < bytes_to_read);
+
+    return (bytes_read > 0);
+  }
+
+  void Cancel() override {}
+
+ private:
+  std::string mime_type_;
+  CefRefPtr<CefStreamReader> reader_;
+  int size_;
+
+  IMPLEMENT_REFCOUNTING(InternalHandler);
+  DISALLOW_COPY_AND_ASSIGN(InternalHandler);
+};
+
+class InternalHandlerFactory : public CefSchemeHandlerFactory {
+ public:
+  explicit InternalHandlerFactory(
+      std::unique_ptr<InternalHandlerDelegate> delegate)
+      : delegate_(std::move(delegate)) {}
+
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) override {
+    GURL url = GURL(request->GetURL().ToString());
+
+    InternalHandlerDelegate::Action action;
+    if (delegate_->OnRequest(browser, request, &action)) {
+      if (!action.redirect_url.is_empty() && action.redirect_url.is_valid())
+        return new RedirectHandler(action.redirect_url);
+
+      if (action.mime_type.empty())
+        action.mime_type = GetMimeType(url.path());
+
+      if (!action.bytes && action.resource_id >= 0) {
+        std::string str =
+            ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
+                action.resource_id);
+        if (str.empty()) {
+          NOTREACHED() << "Failed to load internal resource for id: "
+                       << action.resource_id << " URL: " << url.spec().c_str();
+          return nullptr;
+        }
+        action.bytes = base::RefCountedString::TakeString(&str);
+      }
+
+      if (action.bytes) {
+        action.stream = CefStreamReader::CreateForData(
+            const_cast<unsigned char*>(action.bytes->data()),
+            action.bytes->size());
+        action.stream_size = action.bytes->size();
+      }
+
+      if (action.stream.get()) {
+        return new InternalHandler(action.mime_type, action.stream,
+                                   action.stream_size);
+      }
+    }
+
+    return nullptr;
+  }
+
+ private:
+  std::unique_ptr<InternalHandlerDelegate> delegate_;
+
+  IMPLEMENT_REFCOUNTING(InternalHandlerFactory);
+  DISALLOW_COPY_AND_ASSIGN(InternalHandlerFactory);
+};
+
+}  // namespace
+
+InternalHandlerDelegate::Action::Action() : stream_size(-1), resource_id(-1) {}
+
+CefRefPtr<CefSchemeHandlerFactory> CreateInternalHandlerFactory(
+    std::unique_ptr<InternalHandlerDelegate> delegate) {
+  DCHECK(delegate.get());
+  return new InternalHandlerFactory(std::move(delegate));
+}
+
+}  // namespace scheme
diff --git a/src/libcef/browser/net/internal_scheme_handler.h b/src/libcef/browser/net/internal_scheme_handler.h
new file mode 100644
index 0000000..3db3ef0
--- /dev/null
+++ b/src/libcef/browser/net/internal_scheme_handler.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_INTERNAL_SCHEME_HANDLER_H_
+#define CEF_LIBCEF_BROWSER_NET_INTERNAL_SCHEME_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "include/cef_scheme.h"
+
+#include "base/memory/ref_counted_memory.h"
+#include "url/gurl.h"
+
+namespace scheme {
+
+// All methods will be called on the browser process IO thread.
+class InternalHandlerDelegate {
+ public:
+  class Action {
+   public:
+    Action();
+
+    // Set to the appropriate value or leave empty to have it determined based
+    // on the file extension.
+    std::string mime_type;
+
+    // Option 1: Provide a stream for the resource contents. Set |stream_size|
+    // to the stream size or to -1 if unknown.
+    CefRefPtr<CefStreamReader> stream;
+    int stream_size;
+
+    // Option 2: Provide a base::RefCountedMemory for the resource contents.
+    scoped_refptr<base::RefCountedMemory> bytes;
+
+    // Option 3: Specify a resource id to load static content.
+    int resource_id;
+
+    // Option 4: Redirect to the specified URL.
+    GURL redirect_url;
+  };
+
+  virtual ~InternalHandlerDelegate() {}
+
+  // Populate |action| and return true if the request was handled.
+  virtual bool OnRequest(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefRequest> request,
+                         Action* action) = 0;
+};
+
+// Create an internal scheme handler factory. The factory will take ownership of
+// |delegate|.
+CefRefPtr<CefSchemeHandlerFactory> CreateInternalHandlerFactory(
+    std::unique_ptr<InternalHandlerDelegate> delegate);
+
+}  // namespace scheme
+
+#endif  // CEF_LIBCEF_BROWSER_NET_INTERNAL_SCHEME_HANDLER_H_
diff --git a/src/libcef/browser/net/scheme_handler.cc b/src/libcef/browser/net/scheme_handler.cc
new file mode 100644
index 0000000..d832a8c
--- /dev/null
+++ b/src/libcef/browser/net/scheme_handler.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/net/scheme_handler.h"
+
+#include <string>
+
+#include "libcef/browser/net/chrome_scheme_handler.h"
+#include "libcef/browser/net/devtools_scheme_handler.h"
+#include "libcef/common/net/scheme_registration.h"
+
+#include "content/public/common/url_constants.h"
+
+namespace scheme {
+
+void RegisterInternalHandlers(CefResourceContext* resource_context) {
+  scheme::RegisterChromeDevToolsHandler(resource_context);
+}
+
+void DidFinishLoad(CefRefPtr<CefFrame> frame, const GURL& validated_url) {
+  if (validated_url.scheme() == content::kChromeUIScheme)
+    scheme::DidFinishChromeLoad(frame, validated_url);
+}
+
+}  // namespace scheme
diff --git a/src/libcef/browser/net/scheme_handler.h b/src/libcef/browser/net/scheme_handler.h
new file mode 100644
index 0000000..737c187
--- /dev/null
+++ b/src/libcef/browser/net/scheme_handler.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SCHEME_HANDLER_H_
+#define CEF_LIBCEF_BROWSER_NET_SCHEME_HANDLER_H_
+#pragma once
+
+#include "include/cef_frame.h"
+
+#include "content/public/browser/browser_context.h"
+#include "url/gurl.h"
+
+class CefResourceContext;
+
+namespace scheme {
+
+// Register the internal scheme handlers that can be overridden.
+void RegisterInternalHandlers(CefResourceContext* resource_context);
+
+// Used to fire any asynchronous content updates.
+void DidFinishLoad(CefRefPtr<CefFrame> frame, const GURL& validated_url);
+
+}  // namespace scheme
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SCHEME_HANDLER_H_
diff --git a/src/libcef/browser/net_service/browser_urlrequest_impl.cc b/src/libcef/browser/net_service/browser_urlrequest_impl.cc
new file mode 100644
index 0000000..81a363f
--- /dev/null
+++ b/src/libcef/browser/net_service/browser_urlrequest_impl.cc
@@ -0,0 +1,656 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "libcef/browser/net_service/browser_urlrequest_impl.h"
+
+#include <string>
+#include <utility>
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/frame_host_impl.h"
+#include "libcef/browser/net_service/url_loader_factory_getter.h"
+#include "libcef/browser/request_context_impl.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/net_service/net_service_util.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/response_impl.h"
+#include "libcef/common/task_runner_impl.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "content/public/browser/global_request_id.h"
+#include "content/public/browser/render_frame_host.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
+
+namespace {
+
+const int32_t kInitialRequestID = -2;
+
+// Request ID for requests initiated by CefBrowserURLRequest. request_ids
+// generated by child processes are counted up from 0, while browser
+// created requests start at -2 and go down from there. (We need to start at -2
+// because -1 is used as a special value all over the resource_dispatcher_host
+// for uninitialized variables.) The resource_dispatcher_host code path is not
+// used when NetworkService is enabled so it's safe to repurpose the -2 and
+// below range here.
+// This method is only called on the UI thread.
+int32_t MakeRequestID() {
+  static int32_t request_id = kInitialRequestID;
+  return --request_id;
+}
+
+// Manages the mapping of request IDs to request objects.
+class RequestManager {
+ public:
+  RequestManager() {}
+  ~RequestManager() { DCHECK(map_.empty()); }
+
+  void Add(int32_t request_id,
+           CefRefPtr<CefBrowserURLRequest> request,
+           CefRefPtr<CefURLRequestClient> client) {
+    DCHECK_LE(request_id, kInitialRequestID);
+
+    base::AutoLock lock_scope(lock_);
+    DCHECK(map_.find(request_id) == map_.end());
+    map_.insert(std::make_pair(request_id, std::make_pair(request, client)));
+  }
+
+  void Remove(int32_t request_id) {
+    if (request_id > kInitialRequestID)
+      return;
+
+    base::AutoLock lock_scope(lock_);
+    RequestMap::iterator it = map_.find(request_id);
+    DCHECK(it != map_.end());
+    map_.erase(it);
+  }
+
+  base::Optional<CefBrowserURLRequest::RequestInfo> Get(int32_t request_id) {
+    if (request_id > kInitialRequestID)
+      return base::nullopt;
+
+    base::AutoLock lock_scope(lock_);
+    RequestMap::const_iterator it = map_.find(request_id);
+    if (it != map_.end()) {
+      return it->second;
+    }
+    return base::nullopt;
+  }
+
+ private:
+  base::Lock lock_;
+
+  using RequestMap = std::map<int32_t, CefBrowserURLRequest::RequestInfo>;
+  RequestMap map_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestManager);
+};
+
+#if DCHECK_IS_ON()
+// Because of DCHECK()s in the object destructor.
+base::LazyInstance<RequestManager>::DestructorAtExit g_manager =
+    LAZY_INSTANCE_INITIALIZER;
+#else
+base::LazyInstance<RequestManager>::Leaky g_manager = LAZY_INSTANCE_INITIALIZER;
+#endif
+
+}  // namespace
+
+// CefBrowserURLRequest::Context ----------------------------------------------
+
+class CefBrowserURLRequest::Context
+    : public network::SimpleURLLoaderStreamConsumer {
+ public:
+  Context(CefRefPtr<CefBrowserURLRequest> url_request,
+          CefRefPtr<CefFrame> frame,
+          CefRefPtr<CefRequest> request,
+          CefRefPtr<CefURLRequestClient> client,
+          CefRefPtr<CefRequestContext> request_context)
+      : url_request_(url_request),
+        frame_(frame),
+        request_(static_cast<CefRequestImpl*>(request.get())),
+        client_(client),
+        request_context_(request_context),
+        task_runner_(CefTaskRunnerImpl::GetCurrentTaskRunner()),
+        response_(new CefResponseImpl()),
+        weak_ptr_factory_(this) {
+    // Mark the request/response objects as read-only.
+    request_->SetReadOnly(true);
+    response_->SetReadOnly(true);
+  }
+  ~Context() override = default;
+
+  bool Start() {
+    DCHECK(CalledOnValidThread());
+
+    const GURL& url = GURL(request_->GetURL().ToString());
+    if (!url.is_valid())
+      return false;
+
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(
+            &CefBrowserURLRequest::Context::GetURLLoaderFactoryGetterOnUIThread,
+            frame_, request_context_, weak_ptr_factory_.GetWeakPtr(),
+            task_runner_));
+
+    return true;
+  }
+
+  void Cancel() {
+    DCHECK(CalledOnValidThread());
+
+    // The request may already be complete or canceled.
+    if (!url_request_)
+      return;
+
+    DCHECK_EQ(status_, UR_IO_PENDING);
+    status_ = UR_CANCELED;
+
+    response_->SetReadOnly(false);
+    response_->SetError(ERR_ABORTED);
+    response_->SetReadOnly(true);
+
+    cleanup_immediately_ = true;
+    OnComplete(false);
+  }
+
+  CefRefPtr<CefRequest> request() const { return request_.get(); }
+  CefRefPtr<CefURLRequestClient> client() const { return client_; }
+  CefURLRequest::Status status() const { return status_; }
+  CefRefPtr<CefResponse> response() const { return response_.get(); }
+  bool response_was_cached() const { return response_was_cached_; }
+
+  inline bool CalledOnValidThread() {
+    return task_runner_->RunsTasksInCurrentSequence();
+  }
+
+ private:
+  static void GetURLLoaderFactoryGetterOnUIThread(
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequestContext> request_context,
+      base::WeakPtr<CefBrowserURLRequest::Context> self,
+      scoped_refptr<base::SequencedTaskRunner> task_runner) {
+    CEF_REQUIRE_UIT();
+
+    // Get or create the request context and browser context.
+    CefRefPtr<CefRequestContextImpl> request_context_impl =
+        CefRequestContextImpl::GetOrCreateForRequestContext(request_context);
+    DCHECK(request_context_impl);
+    CefBrowserContext* browser_context =
+        request_context_impl->GetBrowserContext();
+    DCHECK(browser_context);
+
+    int render_frame_id = MSG_ROUTING_NONE;
+    scoped_refptr<net_service::URLLoaderFactoryGetter> loader_factory_getter;
+    if (frame) {
+      // The request will be associated with this frame/browser if it's valid,
+      // otherwise the request will be canceled.
+      content::RenderFrameHost* rfh =
+          static_cast<CefFrameHostImpl*>(frame.get())->GetRenderFrameHost();
+      if (rfh) {
+        // In cases where authentication is required this value will be passed
+        // as the |routing_id| parameter to
+        // NetworkServiceClient::OnAuthRequired. Despite the naming the
+        // GetWebContents method in network_service_client.cc expects it to be a
+        // FrameTreeNodeId. The |process_id| parameter will always be
+        // network::mojom::kBrowserProcessId (value 0) for these requests.
+        render_frame_id = rfh->GetFrameTreeNodeId();
+
+        loader_factory_getter =
+            net_service::URLLoaderFactoryGetter::Create(rfh, browser_context);
+      }
+    } else {
+      loader_factory_getter =
+          net_service::URLLoaderFactoryGetter::Create(nullptr, browser_context);
+    }
+
+    task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &CefBrowserURLRequest::Context::ContinueOnOriginatingThread, self,
+            render_frame_id, MakeRequestID(), loader_factory_getter));
+  }
+
+  void ContinueOnOriginatingThread(
+      int render_frame_id,
+      int32_t request_id,
+      scoped_refptr<net_service::URLLoaderFactoryGetter>
+          loader_factory_getter) {
+    DCHECK(CalledOnValidThread());
+
+    // The request may have been canceled.
+    if (!url_request_)
+      return;
+
+    if (!loader_factory_getter) {
+      // Cancel the request immediately.
+      Cancel();
+      return;
+    }
+
+    DCHECK_EQ(status_, UR_IO_PENDING);
+
+    loader_factory_getter_ = loader_factory_getter;
+
+    const int request_flags = request_->GetFlags();
+
+    // Create the URLLoaderFactory and bind to this thread.
+    auto loader_factory = loader_factory_getter_->GetURLLoaderFactory();
+
+    auto resource_request = std::make_unique<network::ResourceRequest>();
+    static_cast<CefRequestImpl*>(request_.get())
+        ->Get(resource_request.get(), false);
+
+    resource_request->render_frame_id = render_frame_id;
+
+    // Behave the same as a subresource load.
+    resource_request->fetch_request_context_type =
+        static_cast<int>(blink::mojom::RequestContextType::SUBRESOURCE);
+    resource_request->resource_type =
+        static_cast<int>(blink::mojom::ResourceType::kSubResource);
+
+    // Set the origin to match the request.
+    const GURL& url = GURL(request_->GetURL().ToString());
+    resource_request->request_initiator = url::Origin::Create(url);
+
+    if (request_flags & UR_FLAG_ALLOW_STORED_CREDENTIALS) {
+      // Include SameSite cookies.
+      resource_request->attach_same_site_cookies = true;
+      resource_request->site_for_cookies =
+          net::SiteForCookies::FromOrigin(*resource_request->request_initiator);
+    }
+
+    // SimpleURLLoader is picky about the body contents. Try to populate them
+    // correctly below.
+    auto request_body = resource_request->request_body;
+    resource_request->request_body = nullptr;
+
+    std::string content_type;
+    std::string method = resource_request->method;
+    if (request_body) {
+      if (method == "GET" || method == "HEAD") {
+        // Fix the method value to allow a request body.
+        method = "POST";
+        resource_request->method = method;
+
+        request_->SetReadOnly(false);
+        request_->SetMethod(method);
+        request_->SetReadOnly(true);
+      }
+      resource_request->headers.GetHeader(net::HttpRequestHeaders::kContentType,
+                                          &content_type);
+    }
+
+    loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                               MISSING_TRAFFIC_ANNOTATION);
+
+    // Associate the request with |request_id|.
+    request_id_ = request_id;
+    loader_->SetRequestID(request_id);
+    g_manager.Get().Add(request_id, url_request_, client_);
+
+    if (request_body) {
+      if (request_body->elements()->size() == 1) {
+        const auto& element = (*request_body->elements())[0];
+        if (element.type() == network::mojom::DataElementType::kFile) {
+          if (content_type.empty()) {
+            const auto& extension = element.path().Extension();
+            if (!extension.empty()) {
+              // Requests should not block on the disk! On POSIX this goes to
+              // disk. http://code.google.com/p/chromium/issues/detail?id=59849
+              base::ThreadRestrictions::ScopedAllowIO allow_io;
+              // Also remove the leading period.
+              net::GetMimeTypeFromExtension(extension.substr(1), &content_type);
+            }
+          }
+          loader_->AttachFileForUpload(element.path(), content_type);
+        } else if (element.type() == network::mojom::DataElementType::kBytes) {
+          if (content_type.empty()) {
+            content_type = net_service::kContentTypeApplicationFormURLEncoded;
+          }
+          loader_->AttachStringForUpload(
+              std::string(element.bytes() + element.offset(),
+                          element.length() - element.offset()),
+              content_type);
+
+          if (request_flags & UR_FLAG_REPORT_UPLOAD_PROGRESS) {
+            // Report the expected upload data size.
+            upload_data_size_ = element.length() - element.offset();
+          }
+        } else {
+          NOTIMPLEMENTED() << "Unsupported element type: " << element.type();
+        }
+      } else if (request_body->elements()->size() > 1) {
+        NOTIMPLEMENTED() << "Multi-part form data is not supported";
+      }
+    }
+
+    // Allow delivery of non-2xx response bodies.
+    loader_->SetAllowHttpErrorResults(true);
+
+    if (!(request_flags & UR_FLAG_NO_RETRY_ON_5XX)) {
+      // Allow 2 retries on 5xx response or network change.
+      // TODO(network): Consider exposing configuration of max retries and/or
+      // RETRY_ON_NETWORK_CHANGE as a separate flag.
+      loader_->SetRetryOptions(
+          2, network::SimpleURLLoader::RETRY_ON_5XX |
+                 network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+    }
+
+    if (request_flags & UR_FLAG_STOP_ON_REDIRECT) {
+      // The request will be canceled in OnRedirect.
+      loader_->SetOnRedirectCallback(
+          base::Bind(&CefBrowserURLRequest::Context::OnRedirect,
+                     weak_ptr_factory_.GetWeakPtr()));
+    }
+
+    if (request_flags & UR_FLAG_REPORT_UPLOAD_PROGRESS) {
+      loader_->SetOnUploadProgressCallback(
+          base::Bind(&CefBrowserURLRequest::Context::OnUploadProgress,
+                     weak_ptr_factory_.GetWeakPtr()));
+    }
+
+    if ((request_flags & UR_FLAG_NO_DOWNLOAD_DATA) || method == "HEAD") {
+      loader_->DownloadHeadersOnly(
+          loader_factory.get(),
+          base::BindOnce(&CefBrowserURLRequest::Context::OnHeadersOnly,
+                         weak_ptr_factory_.GetWeakPtr()));
+    } else {
+      loader_->SetOnResponseStartedCallback(
+          base::BindOnce(&CefBrowserURLRequest::Context::OnResponseStarted,
+                         weak_ptr_factory_.GetWeakPtr()));
+      loader_->SetOnDownloadProgressCallback(
+          base::Bind(&CefBrowserURLRequest::Context::OnDownloadProgress,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+      loader_->DownloadAsStream(loader_factory.get(), this);
+    }
+  }
+
+  void OnHeadersOnly(scoped_refptr<net::HttpResponseHeaders> headers) {
+    DCHECK(CalledOnValidThread());
+    DCHECK_EQ(status_, UR_IO_PENDING);
+
+    response_->SetReadOnly(false);
+    response_->SetResponseHeaders(*headers);
+    response_->SetReadOnly(true);
+
+    // Match the previous behavior of sending download progress notifications
+    // for UR_FLAG_NO_DOWNLOAD_DATA requests but not HEAD requests.
+    if (request_->GetMethod().ToString() != "HEAD") {
+      download_data_size_ = headers->GetContentLength();
+      OnDownloadProgress(0);
+    }
+
+    cleanup_immediately_ = true;
+    OnComplete(true);
+  }
+
+  void OnRedirect(const net::RedirectInfo& redirect_info,
+                  const network::mojom::URLResponseHead& response_head,
+                  std::vector<std::string>* removed_headers) {
+    DCHECK(CalledOnValidThread());
+    DCHECK_EQ(status_, UR_IO_PENDING);
+
+    // This method is only called if we intend to stop on redirects.
+    DCHECK(request_->GetFlags() | UR_FLAG_STOP_ON_REDIRECT);
+
+    response_->SetReadOnly(false);
+    response_->SetURL(redirect_info.new_url.spec());
+    response_->SetResponseHeaders(*response_head.headers);
+    response_->SetReadOnly(true);
+
+    Cancel();
+  }
+
+  void OnResponseStarted(const GURL& final_url,
+                         const network::mojom::URLResponseHead& response_head) {
+    DCHECK(CalledOnValidThread());
+    DCHECK_EQ(status_, UR_IO_PENDING);
+
+    response_->SetReadOnly(false);
+    response_->SetURL(final_url.spec());
+    response_->SetResponseHeaders(*response_head.headers);
+    response_->SetReadOnly(true);
+
+    download_data_size_ = response_head.content_length;
+  }
+
+  void OnUploadProgress(uint64_t position, uint64_t total) {
+    DCHECK(CalledOnValidThread());
+    DCHECK_EQ(status_, UR_IO_PENDING);
+
+    upload_data_size_ = total;
+    if (position == total)
+      got_upload_progress_complete_ = true;
+
+    client_->OnUploadProgress(url_request_.get(), position, total);
+  }
+
+  void OnDownloadProgress(uint64_t current) {
+    DCHECK(CalledOnValidThread());
+    DCHECK_EQ(status_, UR_IO_PENDING);
+
+    if (response_->GetStatus() == 0) {
+      // With failed requests this callback may arrive without a proceeding
+      // OnHeadersOnly or OnResponseStarted.
+      return;
+    }
+
+    NotifyUploadProgressIfNecessary();
+
+    client_->OnDownloadProgress(url_request_.get(), current,
+                                download_data_size_);
+  }
+
+  void NotifyUploadProgressIfNecessary() {
+    if (!got_upload_progress_complete_ && upload_data_size_ > 0) {
+      // URLLoader sends upload notifications using a timer and will not send
+      // a notification if the request completes too quickly. We therefore
+      // send the notification here if necessary.
+      client_->OnUploadProgress(url_request_.get(), upload_data_size_,
+                                upload_data_size_);
+      got_upload_progress_complete_ = true;
+    }
+  }
+
+  // SimpleURLLoaderStreamConsumer methods:
+  void OnDataReceived(base::StringPiece string_piece,
+                      base::OnceClosure resume) override {
+    DCHECK(CalledOnValidThread());
+    DCHECK_EQ(status_, UR_IO_PENDING);
+
+    client_->OnDownloadData(url_request_.get(), string_piece.data(),
+                            string_piece.length());
+    std::move(resume).Run();
+  }
+
+  void OnComplete(bool success) override {
+    DCHECK(CalledOnValidThread());
+
+    // The request may already be complete or canceled.
+    if (!url_request_)
+      return;
+
+    // Status will be UR_IO_PENDING if we're called when the request is complete
+    // (via SimpleURLLoaderStreamConsumer or OnHeadersOnly). We can only call
+    // these SimpleURLLoader methods if the request is complete.
+    if (status_ == UR_IO_PENDING) {
+      status_ = success ? UR_SUCCESS : UR_FAILED;
+
+      response_->SetReadOnly(false);
+      response_->SetURL(loader_->GetFinalURL().spec());
+      response_->SetError(static_cast<cef_errorcode_t>(loader_->NetError()));
+      response_->SetReadOnly(true);
+
+      response_was_cached_ = loader_->LoadedFromCache();
+    }
+
+    if (success)
+      NotifyUploadProgressIfNecessary();
+
+    client_->OnRequestComplete(url_request_.get());
+
+    // When called via SimpleURLLoaderStreamConsumer we need to cleanup
+    // asynchronously. If the load is still pending this will also cancel it.
+    Cleanup();
+  }
+
+  void OnRetry(base::OnceClosure start_retry) override {
+    DCHECK(CalledOnValidThread());
+    DCHECK_EQ(status_, UR_IO_PENDING);
+    std::move(start_retry).Run();
+  }
+
+  void Cleanup() {
+    DCHECK(CalledOnValidThread());
+    DCHECK(url_request_);
+
+    g_manager.Get().Remove(request_id_);
+
+    client_ = nullptr;
+    request_context_ = nullptr;
+
+    // We may be canceled before the loader is created.
+    if (loader_) {
+      // Must delete the loader before the factory.
+      if (cleanup_immediately_) {
+        // Most SimpleURLLoader callbacks let us delete the URLLoader objects
+        // immediately.
+        loader_.reset();
+        loader_factory_getter_ = nullptr;
+      } else {
+        // Delete the URLLoader objects asynchronously on the correct thread.
+        task_runner_->DeleteSoon(FROM_HERE, std::move(loader_));
+        task_runner_->ReleaseSoon(FROM_HERE, std::move(loader_factory_getter_));
+      }
+    }
+
+    // We may be holding the last reference to |url_request_|, destruction of
+    // which will delete |this|. Use a local variable to keep |url_request_|
+    // alive until this method returns.
+    auto url_request = url_request_;
+    url_request_ = nullptr;
+  }
+
+  // Members only accessed on the initialization thread.
+  CefRefPtr<CefBrowserURLRequest> url_request_;
+  CefRefPtr<CefFrame> frame_;
+  CefRefPtr<CefRequestImpl> request_;
+  CefRefPtr<CefURLRequestClient> client_;
+  CefRefPtr<CefRequestContext> request_context_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  scoped_refptr<net_service::URLLoaderFactoryGetter> loader_factory_getter_;
+  std::unique_ptr<network::SimpleURLLoader> loader_;
+
+  int32_t request_id_ = 0;
+
+  CefURLRequest::Status status_ = UR_IO_PENDING;
+  CefRefPtr<CefResponseImpl> response_;
+  bool response_was_cached_ = false;
+  int64 upload_data_size_ = 0;
+  int64 download_data_size_ = -1;
+  bool got_upload_progress_complete_ = false;
+  bool cleanup_immediately_ = false;
+
+  // Must be the last member.
+  base::WeakPtrFactory<CefBrowserURLRequest::Context> weak_ptr_factory_;
+};
+
+// CefBrowserURLRequest -------------------------------------------------------
+
+// static
+base::Optional<CefBrowserURLRequest::RequestInfo>
+CefBrowserURLRequest::FromRequestID(int32_t request_id) {
+  return g_manager.Get().Get(request_id);
+}
+
+// static
+base::Optional<CefBrowserURLRequest::RequestInfo>
+CefBrowserURLRequest::FromRequestID(
+    const content::GlobalRequestID& request_id) {
+  if (request_id.child_id == network::mojom::kBrowserProcessId) {
+    return FromRequestID(request_id.request_id);
+  }
+  return base::nullopt;
+}
+
+CefBrowserURLRequest::CefBrowserURLRequest(
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefURLRequestClient> client,
+    CefRefPtr<CefRequestContext> request_context) {
+  context_.reset(new Context(this, frame, request, client, request_context));
+}
+
+CefBrowserURLRequest::~CefBrowserURLRequest() {}
+
+bool CefBrowserURLRequest::Start() {
+  if (!VerifyContext())
+    return false;
+  return context_->Start();
+}
+
+CefRefPtr<CefRequest> CefBrowserURLRequest::GetRequest() {
+  if (!VerifyContext())
+    return nullptr;
+  return context_->request();
+}
+
+CefRefPtr<CefURLRequestClient> CefBrowserURLRequest::GetClient() {
+  if (!VerifyContext())
+    return nullptr;
+  return context_->client();
+}
+
+CefURLRequest::Status CefBrowserURLRequest::GetRequestStatus() {
+  if (!VerifyContext())
+    return UR_UNKNOWN;
+  return context_->status();
+}
+
+CefURLRequest::ErrorCode CefBrowserURLRequest::GetRequestError() {
+  if (!VerifyContext())
+    return ERR_NONE;
+  return context_->response()->GetError();
+}
+
+CefRefPtr<CefResponse> CefBrowserURLRequest::GetResponse() {
+  if (!VerifyContext())
+    return nullptr;
+  return context_->response();
+}
+
+bool CefBrowserURLRequest::ResponseWasCached() {
+  if (!VerifyContext())
+    return false;
+  return context_->response_was_cached();
+}
+
+void CefBrowserURLRequest::Cancel() {
+  if (!VerifyContext())
+    return;
+  return context_->Cancel();
+}
+
+bool CefBrowserURLRequest::VerifyContext() {
+  if (!context_->CalledOnValidThread()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  return true;
+}
diff --git a/src/libcef/browser/net_service/browser_urlrequest_impl.h b/src/libcef/browser/net_service/browser_urlrequest_impl.h
new file mode 100644
index 0000000..45f5a67
--- /dev/null
+++ b/src/libcef/browser/net_service/browser_urlrequest_impl.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_BROWSER_URLREQUEST_IMPL_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_BROWSER_URLREQUEST_IMPL_H_
+
+#include <memory>
+
+#include "include/cef_urlrequest.h"
+
+#include "base/optional.h"
+
+namespace content {
+struct GlobalRequestID;
+}
+
+class CefBrowserURLRequest : public CefURLRequest {
+ public:
+  class Context;
+
+  // TODO(network): After the old network code path is deleted move the
+  // CefURLRequestClient::GetAuthCredentials callback to the context thread and
+  // return just the CefBrowserURLRequest object here. The *Client object can
+  // then be retrieved by calling GetClient() from the required thread.
+  using RequestInfo = std::pair<CefRefPtr<CefBrowserURLRequest>,
+                                CefRefPtr<CefURLRequestClient>>;
+
+  // Retrieve the request objects, if any, associated with |request_id|.
+  static base::Optional<RequestInfo> FromRequestID(int32_t request_id);
+  static base::Optional<RequestInfo> FromRequestID(
+      const content::GlobalRequestID& request_id);
+
+  // If |frame| is nullptr requests can still be intercepted but no
+  // browser/frame will be associated with them.
+  CefBrowserURLRequest(CefRefPtr<CefFrame> frame,
+                       CefRefPtr<CefRequest> request,
+                       CefRefPtr<CefURLRequestClient> client,
+                       CefRefPtr<CefRequestContext> request_context);
+  ~CefBrowserURLRequest() override;
+
+  bool Start();
+
+  // CefURLRequest methods.
+  CefRefPtr<CefRequest> GetRequest() override;
+  CefRefPtr<CefURLRequestClient> GetClient() override;
+  Status GetRequestStatus() override;
+  ErrorCode GetRequestError() override;
+  CefRefPtr<CefResponse> GetResponse() override;
+  bool ResponseWasCached() override;
+  void Cancel() override;
+
+ private:
+  bool VerifyContext();
+
+  std::unique_ptr<Context> context_;
+
+  IMPLEMENT_REFCOUNTING(CefBrowserURLRequest);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_BROWSER_URLREQUEST_IMPL_H_
diff --git a/src/libcef/browser/net_service/cookie_helper.cc b/src/libcef/browser/net_service/cookie_helper.cc
new file mode 100644
index 0000000..25de20d
--- /dev/null
+++ b/src/libcef/browser/net_service/cookie_helper.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/net_service/cookie_helper.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/net_service/net_service_util.h"
+
+#include "base/bind.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/base/load_flags.h"
+#include "net/cookies/cookie_options.h"
+#include "net/cookies/cookie_util.h"
+#include "services/network/cookie_manager.h"
+#include "services/network/public/cpp/resource_request.h"
+
+namespace net_service {
+
+namespace {
+
+// Do not keep a reference to the CookieManager returned by this method.
+network::mojom::CookieManager* GetCookieManager(
+    content::BrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+  return content::BrowserContext::GetDefaultStoragePartition(browser_context)
+      ->GetCookieManagerForBrowserProcess();
+}
+
+//
+// LOADING COOKIES.
+//
+
+void ContinueWithLoadedCookies(const AllowCookieCallback& allow_cookie_callback,
+                               DoneCookieCallback done_callback,
+                               const net::CookieStatusList& cookies) {
+  CEF_REQUIRE_IOT();
+  net::CookieList allowed_cookies;
+  for (const auto& status : cookies) {
+    bool allow = false;
+    allow_cookie_callback.Run(status.cookie, &allow);
+    if (allow)
+      allowed_cookies.push_back(status.cookie);
+  }
+  std::move(done_callback).Run(cookies.size(), std::move(allowed_cookies));
+}
+
+void GetCookieListCallback(const AllowCookieCallback& allow_cookie_callback,
+                           DoneCookieCallback done_callback,
+                           const net::CookieStatusList& included_cookies,
+                           const net::CookieStatusList&) {
+  CEF_REQUIRE_UIT();
+  CEF_POST_TASK(CEF_IOT,
+                base::BindOnce(ContinueWithLoadedCookies, allow_cookie_callback,
+                               std::move(done_callback), included_cookies));
+}
+
+void LoadCookiesOnUIThread(content::BrowserContext* browser_context,
+                           const GURL& url,
+                           const net::CookieOptions& options,
+                           const AllowCookieCallback& allow_cookie_callback,
+                           DoneCookieCallback done_callback) {
+  CEF_REQUIRE_UIT();
+  GetCookieManager(browser_context)
+      ->GetCookieList(
+          url, options,
+          base::BindOnce(GetCookieListCallback, allow_cookie_callback,
+                         std::move(done_callback)));
+}
+
+//
+// SAVING COOKIES.
+//
+
+struct SaveCookiesProgress {
+  DoneCookieCallback done_callback_;
+  int total_count_;
+  net::CookieList allowed_cookies_;
+  int num_cookie_lines_left_;
+};
+
+void SetCanonicalCookieCallback(
+    SaveCookiesProgress* progress,
+    const net::CanonicalCookie& cookie,
+    net::CanonicalCookie::CookieInclusionStatus status) {
+  CEF_REQUIRE_UIT();
+  progress->num_cookie_lines_left_--;
+  if (status.IsInclude()) {
+    progress->allowed_cookies_.push_back(cookie);
+  }
+
+  // If all the cookie lines have been handled the request can be continued.
+  if (progress->num_cookie_lines_left_ == 0) {
+    CEF_POST_TASK(CEF_IOT,
+                  base::BindOnce(std::move(progress->done_callback_),
+                                 progress->total_count_,
+                                 std::move(progress->allowed_cookies_)));
+    delete progress;
+  }
+}
+
+void SaveCookiesOnUIThread(content::BrowserContext* browser_context,
+                           const GURL& url,
+                           const net::CookieOptions& options,
+                           int total_count,
+                           net::CookieList cookies,
+                           DoneCookieCallback done_callback) {
+  CEF_REQUIRE_UIT();
+  DCHECK(!cookies.empty());
+
+  network::mojom::CookieManager* cookie_manager =
+      GetCookieManager(browser_context);
+
+  // |done_callback| needs to be executed once and only once after the list has
+  // been fully processed. |num_cookie_lines_left_| keeps track of how many
+  // async callbacks are currently pending.
+  auto progress = new SaveCookiesProgress;
+  progress->done_callback_ = std::move(done_callback);
+  progress->total_count_ = total_count;
+
+  // Make sure to wait for the loop to complete.
+  progress->num_cookie_lines_left_ = 1;
+
+  for (const auto& cookie : cookies) {
+    progress->num_cookie_lines_left_++;
+    cookie_manager->SetCanonicalCookie(
+        cookie, url.scheme(), options,
+        base::BindOnce(&SetCanonicalCookieCallback, base::Unretained(progress),
+                       cookie));
+  }
+
+  SetCanonicalCookieCallback(
+      progress, net::CanonicalCookie(),
+      net::CanonicalCookie::CookieInclusionStatus(
+          net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR));
+}
+
+}  // namespace
+
+void LoadCookies(content::BrowserContext* browser_context,
+                 const network::ResourceRequest& request,
+                 const AllowCookieCallback& allow_cookie_callback,
+                 DoneCookieCallback done_callback) {
+  CEF_REQUIRE_IOT();
+
+  if ((request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) ||
+      request.credentials_mode == network::mojom::CredentialsMode::kOmit ||
+      request.url.IsAboutBlank()) {
+    // Continue immediately without loading cookies.
+    std::move(done_callback).Run(0, {});
+    return;
+  }
+
+  // Match the logic in URLRequestHttpJob::AddCookieHeaderAndStart.
+  net::CookieOptions options;
+  options.set_include_httponly();
+  options.set_same_site_cookie_context(
+      net::cookie_util::ComputeSameSiteContextForRequest(
+          request.method, request.url, request.site_for_cookies,
+          request.request_initiator, request.attach_same_site_cookies));
+
+  CEF_POST_TASK(
+      CEF_UIT,
+      base::BindOnce(LoadCookiesOnUIThread, browser_context, request.url,
+                     options, allow_cookie_callback, std::move(done_callback)));
+}
+
+void SaveCookies(content::BrowserContext* browser_context,
+                 const network::ResourceRequest& request,
+                 net::HttpResponseHeaders* headers,
+                 const AllowCookieCallback& allow_cookie_callback,
+                 DoneCookieCallback done_callback) {
+  CEF_REQUIRE_IOT();
+
+  if (request.credentials_mode == network::mojom::CredentialsMode::kOmit ||
+      request.url.IsAboutBlank() || !headers ||
+      !headers->HasHeader(net_service::kHTTPSetCookieHeaderName)) {
+    // Continue immediately without saving cookies.
+    std::move(done_callback).Run(0, {});
+    return;
+  }
+
+  // Match the logic in
+  // URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete.
+  base::Time response_date;
+  if (!headers->GetDateValue(&response_date))
+    response_date = base::Time();
+
+  net::CookieOptions options;
+  options.set_include_httponly();
+  options.set_same_site_cookie_context(
+      net::cookie_util::ComputeSameSiteContextForRequest(
+          request.method, request.url, request.site_for_cookies,
+          request.request_initiator, request.attach_same_site_cookies));
+
+  const base::StringPiece name(net_service::kHTTPSetCookieHeaderName);
+  std::string cookie_string;
+  size_t iter = 0;
+  net::CookieList allowed_cookies;
+  int total_count = 0;
+
+  while (headers->EnumerateHeader(&iter, name, &cookie_string)) {
+    total_count++;
+
+    net::CanonicalCookie::CookieInclusionStatus returned_status;
+    std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
+        request.url, cookie_string, base::Time::Now(),
+        base::make_optional(response_date), &returned_status);
+    if (!returned_status.IsInclude()) {
+      continue;
+    }
+
+    bool allow = false;
+    allow_cookie_callback.Run(*cookie, &allow);
+    if (allow)
+      allowed_cookies.push_back(*cookie);
+  }
+
+  if (!allowed_cookies.empty()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(SaveCookiesOnUIThread, browser_context, request.url,
+                       options, total_count, std::move(allowed_cookies),
+                       std::move(done_callback)));
+
+  } else {
+    std::move(done_callback).Run(total_count, std::move(allowed_cookies));
+  }
+}
+
+}  // namespace net_service
\ No newline at end of file
diff --git a/src/libcef/browser/net_service/cookie_helper.h b/src/libcef/browser/net_service/cookie_helper.h
new file mode 100644
index 0000000..00b2a3e
--- /dev/null
+++ b/src/libcef/browser/net_service/cookie_helper.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_COOKIE_HELPER_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_COOKIE_HELPER_H_
+
+#include "base/callback_forward.h"
+#include "net/cookies/canonical_cookie.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace net {
+class HttpResponseHeaders;
+}
+
+namespace network {
+struct ResourceRequest;
+}  // namespace network
+
+namespace net_service {
+
+using AllowCookieCallback =
+    base::Callback<void(const net::CanonicalCookie&, bool* /* allow */)>;
+using DoneCookieCallback =
+    base::OnceCallback<void(int /* total_count */,
+                            net::CookieList /* allowed_cookies */)>;
+
+// Load cookies for |request|. |allow_cookie_callback| will be executed for each
+// cookie and should return true to allow it. |done_callback| will be executed
+// on completion with |total_count| representing the total number of cookies
+// retrieved, and |allowed_cookies| representing the list of cookies that were
+// both retrieved and allowed by |allow_cookie_callback|. The loaded cookies
+// will not be set on |request|; that should be done in |done_callback|. Must be
+// called on the IO thread.
+void LoadCookies(content::BrowserContext* browser_context,
+                 const network::ResourceRequest& request,
+                 const AllowCookieCallback& allow_cookie_callback,
+                 DoneCookieCallback done_callback);
+
+// Save cookies from |head|. |allow_cookie_callback| will be executed for each
+// cookie and should return true to allow it. |done_callback| will be executed
+// on completion with |total_count| representing the total number of cookies
+// retrieved, and |allowed_cookies| representing the list of cookies that were
+// both allowed by |allow_cookie_callback| an successfully saved. Must be called
+// on the IO thread.
+void SaveCookies(content::BrowserContext* browser_context,
+                 const network::ResourceRequest& request,
+                 net::HttpResponseHeaders* headers,
+                 const AllowCookieCallback& allow_cookie_callback,
+                 DoneCookieCallback done_callback);
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_COOKIE_HELPER_H_
diff --git a/src/libcef/browser/net_service/cookie_manager_impl.cc b/src/libcef/browser/net_service/cookie_manager_impl.cc
new file mode 100644
index 0000000..e7f5beb
--- /dev/null
+++ b/src/libcef/browser/net_service/cookie_manager_impl.cc
@@ -0,0 +1,351 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/net_service/cookie_manager_impl.h"
+
+#include "libcef/common/net_service/net_service_util.h"
+#include "libcef/common/time_util.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+#include "url/gurl.h"
+
+using network::mojom::CookieManager;
+
+namespace {
+
+// Do not keep a reference to the object returned by this method.
+CefBrowserContext* GetBrowserContext(const CefBrowserContext::Getter& getter) {
+  CEF_REQUIRE_UIT();
+  DCHECK(!getter.is_null());
+
+  // Will return nullptr if the BrowserContext has been destroyed.
+  return getter.Run();
+}
+
+// Do not keep a reference to the object returned by this method.
+CookieManager* GetCookieManager(CefBrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+  return content::BrowserContext::GetDefaultStoragePartition(browser_context)
+      ->GetCookieManagerForBrowserProcess();
+}
+
+// Always execute the callback asynchronously.
+void RunAsyncCompletionOnUIThread(CefRefPtr<CefCompletionCallback> callback) {
+  if (!callback.get())
+    return;
+  CEF_POST_TASK(CEF_UIT,
+                base::Bind(&CefCompletionCallback::OnComplete, callback.get()));
+}
+
+// Always execute the callback asynchronously.
+void SetCookieCallbackImpl(CefRefPtr<CefSetCookieCallback> callback,
+                           net::CanonicalCookie::CookieInclusionStatus status) {
+  if (!callback.get())
+    return;
+  if (!status.IsInclude()) {
+    LOG(WARNING) << "SetCookie failed with reason: " << status.GetDebugString();
+  }
+  CEF_POST_TASK(CEF_UIT, base::Bind(&CefSetCookieCallback::OnComplete,
+                                    callback.get(), status.IsInclude()));
+}
+
+// Always execute the callback asynchronously.
+void DeleteCookiesCallbackImpl(CefRefPtr<CefDeleteCookiesCallback> callback,
+                               uint32_t num_deleted) {
+  if (!callback.get())
+    return;
+  CEF_POST_TASK(CEF_UIT, base::Bind(&CefDeleteCookiesCallback::OnComplete,
+                                    callback.get(), num_deleted));
+}
+
+void ExecuteVisitor(CefRefPtr<CefCookieVisitor> visitor,
+                    const CefBrowserContext::Getter& browser_context_getter,
+                    const std::vector<net::CanonicalCookie>& cookies) {
+  CEF_REQUIRE_UIT();
+
+  auto browser_context = GetBrowserContext(browser_context_getter);
+  if (!browser_context)
+    return;
+
+  auto cookie_manager = GetCookieManager(browser_context);
+
+  int total = cookies.size(), count = 0;
+  for (const auto& cc : cookies) {
+    CefCookie cookie;
+    net_service::MakeCefCookie(cc, cookie);
+
+    bool deleteCookie = false;
+    bool keepLooping = visitor->Visit(cookie, count, total, deleteCookie);
+    if (deleteCookie) {
+      cookie_manager->DeleteCanonicalCookie(
+          cc, CookieManager::DeleteCanonicalCookieCallback());
+    }
+    if (!keepLooping)
+      break;
+    count++;
+  }
+}
+
+// Always execute the callback asynchronously.
+void GetAllCookiesCallbackImpl(
+    CefRefPtr<CefCookieVisitor> visitor,
+    const CefBrowserContext::Getter& browser_context_getter,
+    const net::CookieList& cookies) {
+  CEF_POST_TASK(CEF_UIT, base::Bind(&ExecuteVisitor, visitor,
+                                    browser_context_getter, cookies));
+}
+
+void GetCookiesCallbackImpl(
+    CefRefPtr<CefCookieVisitor> visitor,
+    const CefBrowserContext::Getter& browser_context_getter,
+    const net::CookieStatusList& include_cookies,
+    const net::CookieStatusList&) {
+  net::CookieList cookies;
+  for (const auto& status : include_cookies) {
+    cookies.push_back(status.cookie);
+  }
+  GetAllCookiesCallbackImpl(visitor, browser_context_getter, cookies);
+}
+
+}  // namespace
+
+CefCookieManagerImpl::CefCookieManagerImpl() {}
+
+void CefCookieManagerImpl::Initialize(
+    CefBrowserContext::Getter browser_context_getter,
+    CefRefPtr<CefCompletionCallback> callback) {
+  CEF_REQUIRE_UIT();
+  DCHECK(!browser_context_getter.is_null());
+  DCHECK(browser_context_getter_.is_null());
+  browser_context_getter_ = browser_context_getter;
+  RunAsyncCompletionOnUIThread(callback);
+}
+
+void CefCookieManagerImpl::SetSupportedSchemes(
+    const std::vector<CefString>& schemes,
+    bool include_defaults,
+    CefRefPtr<CefCompletionCallback> callback) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefCookieManagerImpl::SetSupportedSchemes, this,
+                             schemes, include_defaults, callback));
+    return;
+  }
+
+  std::vector<std::string> all_schemes;
+  for (const auto& scheme : schemes)
+    all_schemes.push_back(scheme);
+
+  if (include_defaults) {
+    // Add default schemes that should always support cookies.
+    // This list should match CookieMonster::kDefaultCookieableSchemes.
+    all_schemes.push_back("http");
+    all_schemes.push_back("https");
+    all_schemes.push_back("ws");
+    all_schemes.push_back("wss");
+  }
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return;
+
+  // This will be forwarded to the CookieMonster that lives in the
+  // NetworkService process when the NetworkContext is created via
+  // CefContentBrowserClient::CreateNetworkContext.
+  browser_context->set_cookieable_schemes(base::make_optional(all_schemes));
+  RunAsyncCompletionOnUIThread(callback);
+}
+
+bool CefCookieManagerImpl::VisitAllCookies(
+    CefRefPtr<CefCookieVisitor> visitor) {
+  if (!visitor.get())
+    return false;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefCookieManagerImpl::VisitAllCookies),
+                   this, visitor));
+    return true;
+  }
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return false;
+
+  GetCookieManager(browser_context)
+      ->GetAllCookies(base::Bind(&GetAllCookiesCallbackImpl, visitor,
+                                 browser_context_getter_));
+  return true;
+}
+
+bool CefCookieManagerImpl::VisitUrlCookies(
+    const CefString& url,
+    bool includeHttpOnly,
+    CefRefPtr<CefCookieVisitor> visitor) {
+  if (!visitor.get())
+    return false;
+
+  GURL gurl = GURL(url.ToString());
+  if (!gurl.is_valid())
+    return false;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefCookieManagerImpl::VisitUrlCookies),
+                   this, url, includeHttpOnly, visitor));
+    return true;
+  }
+
+  net::CookieOptions options;
+  if (includeHttpOnly)
+    options.set_include_httponly();
+  options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::MakeInclusive());
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return false;
+
+  GetCookieManager(browser_context)
+      ->GetCookieList(gurl, options,
+                      base::Bind(&GetCookiesCallbackImpl, visitor,
+                                 browser_context_getter_));
+  return true;
+}
+
+bool CefCookieManagerImpl::SetCookie(const CefString& url,
+                                     const CefCookie& cookie,
+                                     CefRefPtr<CefSetCookieCallback> callback) {
+  GURL gurl = GURL(url.ToString());
+  if (!gurl.is_valid())
+    return false;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefCookieManagerImpl::SetCookie), this,
+                   url, cookie, callback));
+    return true;
+  }
+
+  std::string name = CefString(&cookie.name).ToString();
+  std::string value = CefString(&cookie.value).ToString();
+  std::string domain = CefString(&cookie.domain).ToString();
+  std::string path = CefString(&cookie.path).ToString();
+
+  base::Time expiration_time;
+  if (cookie.has_expires)
+    cef_time_to_basetime(cookie.expires, expiration_time);
+
+  net::CookieSameSite same_site =
+      net_service::MakeCookieSameSite(cookie.same_site);
+  net::CookiePriority priority =
+      net_service::MakeCookiePriority(cookie.priority);
+
+  auto canonical_cookie = net::CanonicalCookie::CreateSanitizedCookie(
+      gurl, name, value, domain, path,
+      base::Time(),  // Creation time.
+      expiration_time,
+      base::Time(),  // Last access time.
+      cookie.secure ? true : false, cookie.httponly ? true : false, same_site,
+      priority);
+
+  if (!canonical_cookie) {
+    SetCookieCallbackImpl(callback,
+                          net::CanonicalCookie::CookieInclusionStatus(
+                              net::CanonicalCookie::CookieInclusionStatus::
+                                  EXCLUDE_UNKNOWN_ERROR));
+    return true;
+  }
+
+  net::CookieOptions options;
+  if (cookie.httponly)
+    options.set_include_httponly();
+  options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::MakeInclusive());
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return false;
+
+  GetCookieManager(browser_context)
+      ->SetCanonicalCookie(*canonical_cookie, gurl.scheme(), options,
+                           base::Bind(SetCookieCallbackImpl, callback));
+  return true;
+}
+
+bool CefCookieManagerImpl::DeleteCookies(
+    const CefString& url,
+    const CefString& cookie_name,
+    CefRefPtr<CefDeleteCookiesCallback> callback) {
+  // Empty URLs are allowed but not invalid URLs.
+  GURL gurl = GURL(url.ToString());
+  if (!gurl.is_empty() && !gurl.is_valid())
+    return false;
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefCookieManagerImpl::DeleteCookies),
+                   this, url, cookie_name, callback));
+    return true;
+  }
+
+  network::mojom::CookieDeletionFilterPtr deletion_filter =
+      network::mojom::CookieDeletionFilter::New();
+
+  if (gurl.is_empty()) {
+    // Delete all cookies.
+  } else if (cookie_name.empty()) {
+    // Delete all matching host cookies.
+    deletion_filter->host_name = gurl.host();
+  } else {
+    // Delete all matching host and domain cookies.
+    deletion_filter->url = gurl;
+    deletion_filter->cookie_name = cookie_name;
+  }
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return false;
+
+  GetCookieManager(browser_context)
+      ->DeleteCookies(std::move(deletion_filter),
+                      base::Bind(DeleteCookiesCallbackImpl, callback));
+  return true;
+}
+
+bool CefCookieManagerImpl::FlushStore(
+    CefRefPtr<CefCompletionCallback> callback) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefCookieManagerImpl::FlushStore), this,
+                   callback));
+    return true;
+  }
+
+  auto browser_context = GetBrowserContext(browser_context_getter_);
+  if (!browser_context)
+    return false;
+
+  GetCookieManager(browser_context)
+      ->FlushCookieStore(base::Bind(RunAsyncCompletionOnUIThread, callback));
+  return true;
+}
+
+// CefCookieManager methods ----------------------------------------------------
+
+// static
+CefRefPtr<CefCookieManager> CefCookieManager::GetGlobalManager(
+    CefRefPtr<CefCompletionCallback> callback) {
+  CefRefPtr<CefRequestContext> context = CefRequestContext::GetGlobalContext();
+  return context ? context->GetCookieManager(callback) : nullptr;
+}
diff --git a/src/libcef/browser/net_service/cookie_manager_impl.h b/src/libcef/browser/net_service/cookie_manager_impl.h
new file mode 100644
index 0000000..19eff86
--- /dev/null
+++ b/src/libcef/browser/net_service/cookie_manager_impl.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_COOKIE_MANAGER_IMPL_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_COOKIE_MANAGER_IMPL_H_
+
+#include "include/cef_cookie.h"
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/files/file_path.h"
+
+// Implementation of the CefCookieManager interface. May be created on any
+// thread.
+class CefCookieManagerImpl : public CefCookieManager {
+ public:
+  CefCookieManagerImpl();
+
+  // Called on the UI thread after object creation and before any other object
+  // methods are executed on the UI thread.
+  void Initialize(CefBrowserContext::Getter browser_context_getter,
+                  CefRefPtr<CefCompletionCallback> callback);
+
+  // CefCookieManager methods.
+  void SetSupportedSchemes(const std::vector<CefString>& schemes,
+                           bool include_defaults,
+                           CefRefPtr<CefCompletionCallback> callback) override;
+  bool VisitAllCookies(CefRefPtr<CefCookieVisitor> visitor) override;
+  bool VisitUrlCookies(const CefString& url,
+                       bool includeHttpOnly,
+                       CefRefPtr<CefCookieVisitor> visitor) override;
+  bool SetCookie(const CefString& url,
+                 const CefCookie& cookie,
+                 CefRefPtr<CefSetCookieCallback> callback) override;
+  bool DeleteCookies(const CefString& url,
+                     const CefString& cookie_name,
+                     CefRefPtr<CefDeleteCookiesCallback> callback) override;
+  bool FlushStore(CefRefPtr<CefCompletionCallback> callback) override;
+
+ private:
+  // Only accessed on the UI thread. Will be non-null after Initialize().
+  CefBrowserContext::Getter browser_context_getter_;
+
+  IMPLEMENT_REFCOUNTING(CefCookieManagerImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefCookieManagerImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_COOKIE_MANAGER_IMPL_H_
diff --git a/src/libcef/browser/net_service/login_delegate.cc b/src/libcef/browser/net_service/login_delegate.cc
new file mode 100644
index 0000000..ff9ffb1
--- /dev/null
+++ b/src/libcef/browser/net_service/login_delegate.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/net_service/login_delegate.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/net_service/browser_urlrequest_impl.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "content/public/browser/global_request_id.h"
+#include "content/public/browser/web_contents.h"
+
+namespace net_service {
+
+namespace {
+
+class AuthCallbackImpl : public CefAuthCallback {
+ public:
+  explicit AuthCallbackImpl(base::WeakPtr<LoginDelegate> delegate)
+      : delegate_(delegate),
+        task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+
+  ~AuthCallbackImpl() override {
+    if (delegate_.MaybeValid()) {
+      // If |delegate_| isn't valid this will be a no-op.
+      task_runner_->PostTask(FROM_HERE,
+                             base::BindOnce(&LoginDelegate::Cancel, delegate_));
+    }
+  }
+
+  void Continue(const CefString& username, const CefString& password) override {
+    if (!task_runner_->RunsTasksInCurrentSequence()) {
+      task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(&AuthCallbackImpl::Continue, this, username,
+                                    password));
+      return;
+    }
+
+    if (delegate_) {
+      delegate_->Continue(username, password);
+      delegate_ = nullptr;
+    }
+  }
+
+  void Cancel() override {
+    if (!task_runner_->RunsTasksInCurrentSequence()) {
+      task_runner_->PostTask(FROM_HERE,
+                             base::BindOnce(&AuthCallbackImpl::Cancel, this));
+      return;
+    }
+
+    if (delegate_) {
+      delegate_->Cancel();
+      delegate_ = nullptr;
+    }
+  }
+
+ private:
+  base::WeakPtr<LoginDelegate> delegate_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  IMPLEMENT_REFCOUNTING(AuthCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(AuthCallbackImpl);
+};
+
+void RunCallbackOnIOThread(
+    CefRefPtr<CefBrowserHostImpl> browser,
+    base::Optional<CefBrowserURLRequest::RequestInfo> url_request_info,
+    const net::AuthChallengeInfo& auth_info,
+    const GURL& origin_url,
+    CefRefPtr<AuthCallbackImpl> callback_impl) {
+  CEF_REQUIRE_IOT();
+
+  // TODO(network): After the old network code path is deleted move this
+  // callback to the BrowserURLRequest's context thread.
+  if (url_request_info) {
+    bool handled = url_request_info->second->GetAuthCredentials(
+        auth_info.is_proxy, auth_info.challenger.host(),
+        auth_info.challenger.port(), auth_info.realm, auth_info.scheme,
+        callback_impl.get());
+    if (handled) {
+      // The user will execute the callback, or the request will be canceled on
+      // AuthCallbackImpl destruction.
+      return;
+    }
+  }
+
+  if (browser) {
+    CefRefPtr<CefClient> client = browser->GetClient();
+    if (client) {
+      CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
+      if (handler) {
+        bool handled = handler->GetAuthCredentials(
+            browser.get(), origin_url.spec(), auth_info.is_proxy,
+            auth_info.challenger.host(), auth_info.challenger.port(),
+            auth_info.realm, auth_info.scheme, callback_impl.get());
+        if (handled) {
+          // The user will execute the callback, or the request will be canceled
+          // on AuthCallbackImpl destruction.
+          return;
+        }
+      }
+    }
+  }
+
+  callback_impl->Cancel();
+}
+}  // namespace
+
+LoginDelegate::LoginDelegate(const net::AuthChallengeInfo& auth_info,
+                             content::WebContents* web_contents,
+                             const content::GlobalRequestID& request_id,
+                             const GURL& origin_url,
+                             LoginAuthRequiredCallback callback)
+    : callback_(std::move(callback)), weak_ptr_factory_(this) {
+  CEF_REQUIRE_UIT();
+
+  // May be nullptr for requests originating from CefURLRequest.
+  CefRefPtr<CefBrowserHostImpl> browser;
+  if (web_contents) {
+    browser = CefBrowserHostImpl::GetBrowserForContents(web_contents);
+  }
+
+  // |callback| needs to be executed asynchronously.
+  CEF_POST_TASK(CEF_UIT, base::BindOnce(&LoginDelegate::Start,
+                                        weak_ptr_factory_.GetWeakPtr(), browser,
+                                        auth_info, request_id, origin_url));
+}
+
+void LoginDelegate::Continue(const CefString& username,
+                             const CefString& password) {
+  CEF_REQUIRE_UIT();
+  if (!callback_.is_null()) {
+    std::move(callback_).Run(
+        net::AuthCredentials(username.ToString16(), password.ToString16()));
+  }
+}
+
+void LoginDelegate::Cancel() {
+  CEF_REQUIRE_UIT();
+  if (!callback_.is_null()) {
+    std::move(callback_).Run(base::nullopt);
+  }
+}
+
+void LoginDelegate::Start(CefRefPtr<CefBrowserHostImpl> browser,
+                          const net::AuthChallengeInfo& auth_info,
+                          const content::GlobalRequestID& request_id,
+                          const GURL& origin_url) {
+  CEF_REQUIRE_UIT();
+
+  auto url_request_info = CefBrowserURLRequest::FromRequestID(request_id);
+
+  if (browser || url_request_info) {
+    // AuthCallbackImpl is bound to the current thread.
+    CefRefPtr<AuthCallbackImpl> callbackImpl =
+        new AuthCallbackImpl(weak_ptr_factory_.GetWeakPtr());
+
+    // Execute callbacks on the IO thread to maintain the "old"
+    // network_delegate callback behaviour.
+    CEF_POST_TASK(CEF_IOT, base::BindOnce(&RunCallbackOnIOThread, browser,
+                                          url_request_info, auth_info,
+                                          origin_url, callbackImpl));
+  } else {
+    Cancel();
+  }
+}
+
+}  // namespace net_service
\ No newline at end of file
diff --git a/src/libcef/browser/net_service/login_delegate.h b/src/libcef/browser/net_service/login_delegate.h
new file mode 100644
index 0000000..1f831aa
--- /dev/null
+++ b/src/libcef/browser/net_service/login_delegate.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_LOGIN_DELEGATE_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_LOGIN_DELEGATE_H_
+
+#include "include/cef_base.h"
+
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/login_delegate.h"
+#include "net/base/auth.h"
+
+namespace content {
+struct GlobalRequestID;
+class WebContents;
+}  // namespace content
+
+class CefBrowserHostImpl;
+class GURL;
+
+namespace net_service {
+
+class LoginDelegate : public content::LoginDelegate {
+ public:
+  // This object will be deleted when |callback| is executed or the request is
+  // canceled. |callback| should not be executed after this object is deleted.
+  LoginDelegate(const net::AuthChallengeInfo& auth_info,
+                content::WebContents* web_contents,
+                const content::GlobalRequestID& request_id,
+                const GURL& origin_url,
+                LoginAuthRequiredCallback callback);
+
+  void Continue(const CefString& username, const CefString& password);
+  void Cancel();
+
+ private:
+  void Start(CefRefPtr<CefBrowserHostImpl> browser,
+             const net::AuthChallengeInfo& auth_info,
+             const content::GlobalRequestID& request_id,
+             const GURL& origin_url);
+
+  LoginAuthRequiredCallback callback_;
+  base::WeakPtrFactory<LoginDelegate> weak_ptr_factory_;
+};
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_LOGIN_DELEGATE_H_
diff --git a/src/libcef/browser/net_service/proxy_url_loader_factory.cc b/src/libcef/browser/net_service/proxy_url_loader_factory.cc
new file mode 100644
index 0000000..c6a78f9
--- /dev/null
+++ b/src/libcef/browser/net_service/proxy_url_loader_factory.cc
@@ -0,0 +1,1251 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "libcef/browser/net_service/proxy_url_loader_factory.h"
+
+#include "libcef/browser/context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/net_service/net_service_util.h"
+
+#include "base/barrier_closure.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/base/big_buffer.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/cors/cors.h"
+
+namespace net_service {
+
+namespace {
+
+// User data key for ResourceContextData.
+const void* const kResourceContextUserDataKey = &kResourceContextUserDataKey;
+
+}  // namespace
+
+// Owns all of the ProxyURLLoaderFactorys for a given BrowserContext. Since
+// these live on the IO thread this is done indirectly through the
+// ResourceContext.
+class ResourceContextData : public base::SupportsUserData::Data {
+ public:
+  ~ResourceContextData() override {}
+
+  static void AddProxyOnUIThread(
+      ProxyURLLoaderFactory* proxy,
+      content::WebContents::Getter web_contents_getter) {
+    CEF_REQUIRE_UIT();
+
+    content::WebContents* web_contents = web_contents_getter.Run();
+
+    // Maybe the browser was destroyed while AddProxyOnUIThread was pending.
+    if (!web_contents) {
+      // Delete on the IO thread as expected by mojo bindings.
+      content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE,
+                                         proxy);
+      return;
+    }
+
+    content::BrowserContext* browser_context =
+        web_contents->GetBrowserContext();
+    DCHECK(browser_context);
+
+    content::ResourceContext* resource_context =
+        browser_context->GetResourceContext();
+    DCHECK(resource_context);
+
+    CEF_POST_TASK(CEF_IOT, base::BindOnce(ResourceContextData::AddProxy,
+                                          base::Unretained(proxy),
+                                          base::Unretained(resource_context)));
+  }
+
+  static void AddProxy(ProxyURLLoaderFactory* proxy,
+                       content::ResourceContext* resource_context) {
+    CEF_REQUIRE_IOT();
+
+    // Maybe the proxy was destroyed while AddProxyOnUIThread was pending.
+    if (proxy->destroyed_) {
+      delete proxy;
+      return;
+    }
+
+    auto* self = static_cast<ResourceContextData*>(
+        resource_context->GetUserData(kResourceContextUserDataKey));
+    if (!self) {
+      self = new ResourceContextData();
+      resource_context->SetUserData(kResourceContextUserDataKey,
+                                    base::WrapUnique(self));
+    }
+
+    proxy->SetDisconnectCallback(base::BindOnce(
+        &ResourceContextData::RemoveProxy, self->weak_factory_.GetWeakPtr()));
+    self->proxies_.emplace(base::WrapUnique(proxy));
+  }
+
+ private:
+  void RemoveProxy(ProxyURLLoaderFactory* proxy) {
+    CEF_REQUIRE_IOT();
+
+    auto it = proxies_.find(proxy);
+    DCHECK(it != proxies_.end());
+    proxies_.erase(it);
+  }
+
+  ResourceContextData() : weak_factory_(this) {}
+
+  std::set<std::unique_ptr<ProxyURLLoaderFactory>, base::UniquePtrComparator>
+      proxies_;
+
+  base::WeakPtrFactory<ResourceContextData> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ResourceContextData);
+};
+
+//==============================
+// InterceptedRequest
+//=============================
+
+// Handles intercepted, in-progress requests/responses, so that they can be
+// controlled and modified accordingly.
+class InterceptedRequest : public network::mojom::URLLoader,
+                           public network::mojom::URLLoaderClient,
+                           public network::mojom::TrustedHeaderClient {
+ public:
+  InterceptedRequest(
+      ProxyURLLoaderFactory* factory,
+      RequestId id,
+      uint32_t options,
+      const network::ResourceRequest& request,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+      mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
+      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+      mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory);
+  ~InterceptedRequest() override;
+
+  // Restart the request. This happens on initial start and after redirect.
+  void Restart();
+
+  // Called from ProxyURLLoaderFactory::OnLoaderCreated.
+  void OnLoaderCreated(
+      mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver);
+
+  // Called from InterceptDelegate::OnInputStreamOpenFailed.
+  void InputStreamFailed(bool restart_needed);
+
+  // mojom::TrustedHeaderClient methods:
+  void OnBeforeSendHeaders(const net::HttpRequestHeaders& headers,
+                           OnBeforeSendHeadersCallback callback) override;
+  void OnHeadersReceived(const std::string& headers,
+                         const net::IPEndPoint& remote_endpoint,
+                         OnHeadersReceivedCallback callback) override;
+
+  // mojom::URLLoaderClient methods:
+  void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override;
+  void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
+                         network::mojom::URLResponseHeadPtr head) override;
+  void OnUploadProgress(int64_t current_position,
+                        int64_t total_size,
+                        OnUploadProgressCallback callback) override;
+  void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override;
+  void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle body) override;
+  void OnComplete(const network::URLLoaderCompletionStatus& status) override;
+
+  // mojom::URLLoader methods:
+  void FollowRedirect(const std::vector<std::string>& removed_headers,
+                      const net::HttpRequestHeaders& modified_headers,
+                      const base::Optional<GURL>& new_url) override;
+  void SetPriority(net::RequestPriority priority,
+                   int32_t intra_priority_value) override;
+  void PauseReadingBodyFromNet() override;
+  void ResumeReadingBodyFromNet() override;
+
+  const RequestId id() const { return id_; }
+
+ private:
+  // Helpers for determining the request handler.
+  void BeforeRequestReceived(const GURL& original_url,
+                             bool intercept_request,
+                             bool intercept_only);
+  void InterceptResponseReceived(const GURL& original_url,
+                                 std::unique_ptr<ResourceResponse> response);
+  void ContinueAfterIntercept();
+  void ContinueAfterInterceptWithOverride(
+      std::unique_ptr<ResourceResponse> response);
+
+  // Helpers for optionally overriding headers.
+  void HandleResponseOrRedirectHeaders(
+      base::Optional<net::RedirectInfo> redirect_info,
+      net::CompletionOnceCallback continuation);
+  void ContinueResponseOrRedirect(
+      net::CompletionOnceCallback continuation,
+      InterceptedRequestHandler::ResponseMode response_mode,
+      scoped_refptr<net::HttpResponseHeaders> override_headers,
+      const GURL& redirect_url);
+  void ContinueToHandleOverrideHeaders(int error_code);
+  net::RedirectInfo MakeRedirectResponseAndInfo(const GURL& new_location);
+
+  // Helpers for redirect handling.
+  void ContinueToBeforeRedirect(const net::RedirectInfo& redirect_info,
+                                int error_code);
+
+  // Helpers for response handling.
+  void ContinueToResponseStarted(int error_code);
+
+  void OnDestroy();
+
+  void OnProcessRequestHeaders(const GURL& redirect_url,
+                               net::HttpRequestHeaders* modified_headers,
+                               std::vector<std::string>* removed_headers);
+
+  // This is called when the original URLLoaderClient has a connection error.
+  void OnURLLoaderClientError();
+
+  // This is called when the original URLLoader has a connection error.
+  void OnURLLoaderError(uint32_t custom_reason, const std::string& description);
+
+  // Call OnComplete on |target_client_|. If |wait_for_loader_error| is true
+  // then this object will wait for |proxied_loader_binding_| to have a
+  // connection error before destructing.
+  void CallOnComplete(const network::URLLoaderCompletionStatus& status,
+                      bool wait_for_loader_error);
+
+  void SendErrorAndCompleteImmediately(int error_code);
+
+  void SendErrorCallback(int error_code, bool safebrowsing_hit);
+
+  void OnUploadProgressACK();
+
+  ProxyURLLoaderFactory* const factory_;
+  const RequestId id_;
+  const uint32_t options_;
+  bool input_stream_previously_failed_ = false;
+  bool request_was_redirected_ = false;
+
+  // To avoid sending multiple OnReceivedError callbacks.
+  bool sent_error_callback_ = false;
+
+  // When true, the loader will provide the option to intercept the request.
+  bool intercept_request_ = true;
+
+  // When true, the loader will not proceed unless the intercept request
+  // callback provided a non-null response.
+  bool intercept_only_ = false;
+
+  network::URLLoaderCompletionStatus status_;
+  bool got_loader_error_ = false;
+
+  // Used for rate limiting OnUploadProgress callbacks.
+  bool waiting_for_upload_progress_ack_ = false;
+
+  network::ResourceRequest request_;
+  network::mojom::URLResponseHeadPtr current_response_;
+  scoped_refptr<net::HttpResponseHeaders> current_headers_;
+  scoped_refptr<net::HttpResponseHeaders> override_headers_;
+  GURL original_url_;
+  GURL redirect_url_;
+  GURL header_client_redirect_url_;
+  const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
+
+  mojo::Binding<network::mojom::URLLoader> proxied_loader_binding_;
+  network::mojom::URLLoaderClientPtr target_client_;
+
+  mojo::Binding<network::mojom::URLLoaderClient> proxied_client_binding_;
+  network::mojom::URLLoaderPtr target_loader_;
+  network::mojom::URLLoaderFactoryPtr target_factory_;
+
+  bool current_request_uses_header_client_ = false;
+  OnHeadersReceivedCallback on_headers_received_callback_;
+  mojo::Receiver<network::mojom::TrustedHeaderClient> header_client_receiver_{
+      this};
+
+  StreamReaderURLLoader* stream_loader_ = nullptr;
+
+  base::WeakPtrFactory<InterceptedRequest> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterceptedRequest);
+};
+
+class InterceptDelegate : public StreamReaderURLLoader::Delegate {
+ public:
+  explicit InterceptDelegate(std::unique_ptr<ResourceResponse> response,
+                             base::WeakPtr<InterceptedRequest> request)
+      : response_(std::move(response)), request_(request) {}
+
+  bool OpenInputStream(const RequestId& request_id,
+                       const network::ResourceRequest& request,
+                       OpenCallback callback) override {
+    return response_->OpenInputStream(request_id, request, std::move(callback));
+  }
+
+  void OnInputStreamOpenFailed(const RequestId& request_id,
+                               bool* restarted) override {
+    request_->InputStreamFailed(false /* restart_needed */);
+    *restarted = false;
+  }
+
+  void GetResponseHeaders(const RequestId& request_id,
+                          int* status_code,
+                          std::string* reason_phrase,
+                          std::string* mime_type,
+                          std::string* charset,
+                          int64_t* content_length,
+                          HeaderMap* extra_headers) override {
+    response_->GetResponseHeaders(request_id, status_code, reason_phrase,
+                                  mime_type, charset, content_length,
+                                  extra_headers);
+  }
+
+ private:
+  std::unique_ptr<ResourceResponse> response_;
+  base::WeakPtr<InterceptedRequest> request_;
+};
+
+InterceptedRequest::InterceptedRequest(
+    ProxyURLLoaderFactory* factory,
+    RequestId id,
+    uint32_t options,
+    const network::ResourceRequest& request,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+    mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
+    mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory)
+    : factory_(factory),
+      id_(id),
+      options_(options),
+      request_(request),
+      traffic_annotation_(traffic_annotation),
+      proxied_loader_binding_(this, std::move(loader_receiver)),
+      target_client_(std::move(client)),
+      proxied_client_binding_(this),
+      target_factory_(std::move(target_factory)),
+      weak_factory_(this) {
+  status_ = network::URLLoaderCompletionStatus(net::OK);
+
+  net::HttpRequestHeaders modified_headers;
+  std::vector<std::string> removed_headers;
+  OnProcessRequestHeaders(GURL() /* redirect_url */, &modified_headers,
+                          &removed_headers);
+
+  // If there is a client error, clean up the request.
+  target_client_.set_connection_error_handler(base::BindOnce(
+      &InterceptedRequest::OnURLLoaderClientError, base::Unretained(this)));
+  proxied_loader_binding_.set_connection_error_with_reason_handler(
+      base::BindOnce(&InterceptedRequest::OnURLLoaderError,
+                     base::Unretained(this)));
+}
+
+InterceptedRequest::~InterceptedRequest() {
+  if (status_.error_code != net::OK)
+    SendErrorCallback(status_.error_code, false);
+  if (on_headers_received_callback_) {
+    std::move(on_headers_received_callback_)
+        .Run(net::ERR_ABORTED, base::nullopt, GURL());
+  }
+}
+
+void InterceptedRequest::Restart() {
+  stream_loader_ = nullptr;
+  if (proxied_client_binding_.is_bound()) {
+    proxied_client_binding_.Unbind();
+    target_loader_.reset();
+  }
+
+  if (header_client_receiver_.is_bound())
+    ignore_result(header_client_receiver_.Unbind());
+
+  current_request_uses_header_client_ =
+      !!factory_->url_loader_header_client_receiver_;
+
+  const GURL original_url = request_.url;
+
+  factory_->request_handler_->OnBeforeRequest(
+      id_, &request_, request_was_redirected_,
+      base::BindOnce(&InterceptedRequest::BeforeRequestReceived,
+                     weak_factory_.GetWeakPtr(), original_url),
+      base::BindOnce(&InterceptedRequest::SendErrorAndCompleteImmediately,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void InterceptedRequest::OnLoaderCreated(
+    mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
+  DCHECK(current_request_uses_header_client_);
+
+  // Only called if we're using the default loader.
+  header_client_receiver_.Bind(std::move(receiver));
+}
+
+void InterceptedRequest::InputStreamFailed(bool restart_needed) {
+  DCHECK(!input_stream_previously_failed_);
+
+  if (intercept_only_) {
+    // This can happen for unsupported schemes, when no proper
+    // response from the intercept handler is received, i.e.
+    // the provided input stream in response failed to load. In
+    // this case we send and error and stop loading.
+    SendErrorAndCompleteImmediately(net::ERR_UNKNOWN_URL_SCHEME);
+    return;
+  }
+
+  if (!restart_needed)
+    return;
+
+  input_stream_previously_failed_ = true;
+  Restart();
+}
+
+// TrustedHeaderClient methods.
+
+void InterceptedRequest::OnBeforeSendHeaders(
+    const net::HttpRequestHeaders& headers,
+    OnBeforeSendHeadersCallback callback) {
+  if (!current_request_uses_header_client_) {
+    std::move(callback).Run(net::OK, base::nullopt);
+    return;
+  }
+
+  request_.headers = headers;
+  std::move(callback).Run(net::OK, base::nullopt);
+
+  // Resume handling of client messages after continuing from an async callback.
+  if (proxied_client_binding_)
+    proxied_client_binding_.ResumeIncomingMethodCallProcessing();
+}
+
+void InterceptedRequest::OnHeadersReceived(
+    const std::string& headers,
+    const net::IPEndPoint& remote_endpoint,
+    OnHeadersReceivedCallback callback) {
+  if (!current_request_uses_header_client_) {
+    std::move(callback).Run(net::OK, base::nullopt, GURL());
+    return;
+  }
+
+  current_headers_ = base::MakeRefCounted<net::HttpResponseHeaders>(headers);
+  on_headers_received_callback_ = std::move(callback);
+
+  base::Optional<net::RedirectInfo> redirect_info;
+  std::string location;
+  if (current_headers_->IsRedirect(&location)) {
+    const GURL new_url = request_.url.Resolve(location);
+    redirect_info =
+        MakeRedirectInfo(request_, current_headers_.get(), new_url, 0);
+  }
+
+  HandleResponseOrRedirectHeaders(
+      redirect_info,
+      base::BindOnce(&InterceptedRequest::ContinueToHandleOverrideHeaders,
+                     weak_factory_.GetWeakPtr()));
+}
+
+// URLLoaderClient methods.
+
+void InterceptedRequest::OnReceiveResponse(
+    network::mojom::URLResponseHeadPtr head) {
+  current_response_ = std::move(head);
+
+  if (current_request_uses_header_client_) {
+    // Use the headers we got from OnHeadersReceived as that'll contain
+    // Set-Cookie if it existed.
+    DCHECK(current_headers_);
+    current_response_->headers = current_headers_;
+    current_headers_ = nullptr;
+    ContinueToResponseStarted(net::OK);
+  } else {
+    HandleResponseOrRedirectHeaders(
+        base::nullopt,
+        base::BindOnce(&InterceptedRequest::ContinueToResponseStarted,
+                       weak_factory_.GetWeakPtr()));
+  }
+}
+
+void InterceptedRequest::OnReceiveRedirect(
+    const net::RedirectInfo& redirect_info,
+    network::mojom::URLResponseHeadPtr head) {
+  bool needs_callback = false;
+
+  current_response_ = std::move(head);
+
+  if (current_request_uses_header_client_) {
+    // Use the headers we got from OnHeadersReceived as that'll contain
+    // Set-Cookie if it existed. May be null for synthetic redirects.
+    if (current_headers_) {
+      current_response_->headers = current_headers_;
+      current_headers_ = nullptr;
+    }
+  } else {
+    needs_callback = true;
+  }
+
+  net::RedirectInfo new_redirect_info;
+
+  // When we redirect via ContinueToHandleOverrideHeaders the |redirect_info|
+  // value is sometimes nonsense (HTTP_OK). Also, we won't get another call to
+  // OnHeadersReceived for the new URL so we need to execute the callback here.
+  if (header_client_redirect_url_.is_valid() &&
+      redirect_info.status_code == net::HTTP_OK) {
+    DCHECK(current_request_uses_header_client_);
+    needs_callback = true;
+    new_redirect_info =
+        MakeRedirectResponseAndInfo(header_client_redirect_url_);
+  } else {
+    new_redirect_info = redirect_info;
+  }
+
+  if (needs_callback) {
+    HandleResponseOrRedirectHeaders(
+        new_redirect_info,
+        base::BindOnce(&InterceptedRequest::ContinueToBeforeRedirect,
+                       weak_factory_.GetWeakPtr(), new_redirect_info));
+  } else {
+    ContinueToBeforeRedirect(new_redirect_info, net::OK);
+  }
+}
+
+void InterceptedRequest::OnUploadProgress(int64_t current_position,
+                                          int64_t total_size,
+                                          OnUploadProgressCallback callback) {
+  // Implement our own rate limiting for OnUploadProgress calls.
+  if (!waiting_for_upload_progress_ack_) {
+    waiting_for_upload_progress_ack_ = true;
+    target_client_->OnUploadProgress(
+        current_position, total_size,
+        base::BindOnce(&InterceptedRequest::OnUploadProgressACK,
+                       weak_factory_.GetWeakPtr()));
+  }
+
+  // Always execute the callback immediately to avoid a race between
+  // URLLoaderClient_OnUploadProgress_ProxyToResponder::Run() (which would
+  // otherwise be blocked on the target client executing the callback) and
+  // CallOnComplete(). If CallOnComplete() is executed first the interface pipe
+  // will be closed and the callback destructor will generate an assertion like:
+  // "URLLoaderClient::OnUploadProgressCallback was destroyed without first
+  // either being run or its corresponding binding being closed. It is an error
+  // to drop response callbacks which still correspond to an open interface
+  // pipe."
+  std::move(callback).Run();
+}
+
+void InterceptedRequest::OnReceiveCachedMetadata(mojo_base::BigBuffer data) {
+  target_client_->OnReceiveCachedMetadata(std::move(data));
+}
+
+void InterceptedRequest::OnTransferSizeUpdated(int32_t transfer_size_diff) {
+  target_client_->OnTransferSizeUpdated(transfer_size_diff);
+}
+
+void InterceptedRequest::OnStartLoadingResponseBody(
+    mojo::ScopedDataPipeConsumerHandle body) {
+  target_client_->OnStartLoadingResponseBody(
+      factory_->request_handler_->OnFilterResponseBody(id_, request_,
+                                                       std::move(body)));
+}
+
+void InterceptedRequest::OnComplete(
+    const network::URLLoaderCompletionStatus& status) {
+  // Only wait for the original loader to possibly have a custom error if the
+  // target loader exists and succeeded. If the target loader failed, then it
+  // was a race as to whether that error or the safe browsing error would be
+  // reported.
+  CallOnComplete(status, !stream_loader_ && status.error_code == net::OK);
+}
+
+// URLLoader methods.
+
+void InterceptedRequest::FollowRedirect(
+    const std::vector<std::string>& removed_headers_ext,
+    const net::HttpRequestHeaders& modified_headers_ext,
+    const base::Optional<GURL>& new_url) {
+  std::vector<std::string> removed_headers = removed_headers_ext;
+  net::HttpRequestHeaders modified_headers = modified_headers_ext;
+  OnProcessRequestHeaders(new_url.value_or(GURL()), &modified_headers,
+                          &removed_headers);
+
+  // If |OnURLLoaderClientError| was called then we're just waiting for the
+  // connection error handler of |proxied_loader_binding_|. Don't restart the
+  // job since that'll create another URLLoader.
+  if (!target_client_)
+    return;
+
+  // Normally we would call FollowRedirect on the target loader and it would
+  // begin loading the redirected request. However, the client might want to
+  // intercept that request so restart the job instead.
+  Restart();
+}
+
+void InterceptedRequest::SetPriority(net::RequestPriority priority,
+                                     int32_t intra_priority_value) {
+  if (target_loader_)
+    target_loader_->SetPriority(priority, intra_priority_value);
+}
+
+void InterceptedRequest::PauseReadingBodyFromNet() {
+  if (target_loader_)
+    target_loader_->PauseReadingBodyFromNet();
+}
+
+void InterceptedRequest::ResumeReadingBodyFromNet() {
+  if (target_loader_)
+    target_loader_->ResumeReadingBodyFromNet();
+}
+
+// Helper methods.
+
+void InterceptedRequest::BeforeRequestReceived(const GURL& original_url,
+                                               bool intercept_request,
+                                               bool intercept_only) {
+  intercept_request_ = intercept_request;
+  intercept_only_ = intercept_only;
+
+  if (input_stream_previously_failed_ || !intercept_request_) {
+    // Equivalent to no interception.
+    InterceptResponseReceived(original_url, nullptr);
+  } else {
+    // TODO(network): Verify the case when WebContents::RenderFrameDeleted is
+    // called before network request is intercepted (i.e. if that's possible
+    // and whether it can result in any issues).
+    factory_->request_handler_->ShouldInterceptRequest(
+        id_, &request_,
+        base::BindOnce(&InterceptedRequest::InterceptResponseReceived,
+                       weak_factory_.GetWeakPtr(), original_url));
+  }
+}
+
+void InterceptedRequest::InterceptResponseReceived(
+    const GURL& original_url,
+    std::unique_ptr<ResourceResponse> response) {
+  if (request_.url != original_url) {
+    // A response object shouldn't be created if we're redirecting.
+    DCHECK(!response);
+
+    // Perform the redirect.
+    current_response_ = network::mojom::URLResponseHead::New();
+    current_response_->request_start = base::TimeTicks::Now();
+    current_response_->response_start = base::TimeTicks::Now();
+
+    auto headers = MakeResponseHeaders(
+        net::HTTP_TEMPORARY_REDIRECT, std::string(), std::string(),
+        std::string(), -1, {}, false /* allow_existing_header_override */);
+    current_response_->headers = headers;
+
+    current_response_->encoded_data_length = headers->raw_headers().length();
+    current_response_->content_length = current_response_->encoded_body_length =
+        0;
+
+    std::string origin;
+    if (request_.headers.GetHeader(net::HttpRequestHeaders::kOrigin, &origin) &&
+        origin != url::Origin().Serialize()) {
+      // Allow redirects of cross-origin resource loads.
+      headers->AddHeader(MakeHeader(
+          network::cors::header_names::kAccessControlAllowOrigin, origin));
+    }
+
+    if (request_.credentials_mode ==
+        network::mojom::CredentialsMode::kInclude) {
+      headers->AddHeader(MakeHeader(
+          network::cors::header_names::kAccessControlAllowCredentials, "true"));
+    }
+
+    const net::RedirectInfo& redirect_info =
+        MakeRedirectInfo(request_, headers.get(), request_.url, 0);
+    HandleResponseOrRedirectHeaders(
+        redirect_info,
+        base::BindOnce(&InterceptedRequest::ContinueToBeforeRedirect,
+                       weak_factory_.GetWeakPtr(), redirect_info));
+    return;
+  }
+
+  if (response) {
+    // Non-null response: make sure to use it as an override for the
+    // normal network data.
+    ContinueAfterInterceptWithOverride(std::move(response));
+  } else {
+    // Request was not intercepted/overridden. Proceed with loading
+    // from network, unless this is a special |intercept_only_| loader,
+    // which happens for external schemes (e.g. unsupported schemes).
+    if (intercept_only_) {
+      SendErrorAndCompleteImmediately(net::ERR_UNKNOWN_URL_SCHEME);
+      return;
+    }
+    ContinueAfterIntercept();
+  }
+}
+
+void InterceptedRequest::ContinueAfterIntercept() {
+  if (!target_loader_ && target_factory_) {
+    network::mojom::URLLoaderClientPtr proxied_client;
+    proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client));
+
+    // Even if this request does not use the header client, future redirects
+    // might, so we need to set the option on the loader.
+    uint32_t options = options_ | network::mojom::kURLLoadOptionUseHeaderClient;
+    target_factory_->CreateLoaderAndStart(
+        mojo::MakeRequest(&target_loader_), id_.routing_id(), id_.request_id(),
+        options, request_, proxied_client.PassInterface(), traffic_annotation_);
+  }
+}
+
+void InterceptedRequest::ContinueAfterInterceptWithOverride(
+    std::unique_ptr<ResourceResponse> response) {
+  network::mojom::URLLoaderClientPtr proxied_client;
+  proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client));
+
+  // StreamReaderURLLoader will synthesize TrustedHeaderClient callbacks to
+  // avoid having Set-Cookie headers stripped by the IPC layer.
+  current_request_uses_header_client_ = true;
+  network::mojom::TrustedHeaderClientPtr header_client;
+  header_client_receiver_.Bind(mojo::MakeRequest(&header_client));
+
+  stream_loader_ = new StreamReaderURLLoader(
+      id_, request_, std::move(proxied_client), std::move(header_client),
+      traffic_annotation_,
+      std::make_unique<InterceptDelegate>(std::move(response),
+                                          weak_factory_.GetWeakPtr()));
+  stream_loader_->Start();
+}
+
+void InterceptedRequest::HandleResponseOrRedirectHeaders(
+    base::Optional<net::RedirectInfo> redirect_info,
+    net::CompletionOnceCallback continuation) {
+  override_headers_ = nullptr;
+  redirect_url_ = redirect_info.has_value() ? redirect_info->new_url : GURL();
+  original_url_ = request_.url;
+
+  // |current_response_| may be nullptr when called from OnHeadersReceived.
+  auto headers =
+      current_response_ ? current_response_->headers : current_headers_;
+
+  // Even though |head| is const we can get a non-const pointer to the headers
+  // and modifications we make are passed to the target client.
+  factory_->request_handler_->ProcessResponseHeaders(
+      id_, request_, redirect_url_, headers.get());
+
+  // Pause handling of client messages before waiting on an async callback.
+  if (proxied_client_binding_)
+    proxied_client_binding_.PauseIncomingMethodCallProcessing();
+
+  factory_->request_handler_->OnRequestResponse(
+      id_, &request_, headers.get(), redirect_info,
+      base::BindOnce(&InterceptedRequest::ContinueResponseOrRedirect,
+                     weak_factory_.GetWeakPtr(), std::move(continuation)));
+}
+
+void InterceptedRequest::ContinueResponseOrRedirect(
+    net::CompletionOnceCallback continuation,
+    InterceptedRequestHandler::ResponseMode response_mode,
+    scoped_refptr<net::HttpResponseHeaders> override_headers,
+    const GURL& redirect_url) {
+  if (response_mode == InterceptedRequestHandler::ResponseMode::CANCEL) {
+    std::move(continuation).Run(net::ERR_ABORTED);
+    return;
+  } else if (response_mode ==
+             InterceptedRequestHandler::ResponseMode::RESTART) {
+    Restart();
+    return;
+  }
+
+  override_headers_ = override_headers;
+  if (override_headers_) {
+    // Make sure to update current_response_, since when OnReceiveResponse
+    // is called we will not use its headers as it might be missing the
+    // Set-Cookie line (which gets stripped by the IPC layer).
+    current_response_->headers = override_headers_;
+  }
+  redirect_url_ = redirect_url;
+
+  std::move(continuation).Run(net::OK);
+}
+
+void InterceptedRequest::ContinueToHandleOverrideHeaders(int error_code) {
+  if (error_code != net::OK) {
+    SendErrorAndCompleteImmediately(error_code);
+    return;
+  }
+
+  DCHECK(on_headers_received_callback_);
+  base::Optional<std::string> headers;
+  if (override_headers_)
+    headers = override_headers_->raw_headers();
+  header_client_redirect_url_ = redirect_url_;
+  std::move(on_headers_received_callback_).Run(net::OK, headers, redirect_url_);
+
+  override_headers_ = nullptr;
+  redirect_url_ = GURL();
+
+  // Resume handling of client messages after continuing from an async callback.
+  if (proxied_client_binding_)
+    proxied_client_binding_.ResumeIncomingMethodCallProcessing();
+}
+
+net::RedirectInfo InterceptedRequest::MakeRedirectResponseAndInfo(
+    const GURL& new_location) {
+  // Clear the Content-Type values.
+  current_response_->mime_type = current_response_->charset = std::string();
+  current_response_->headers->RemoveHeader(
+      net::HttpRequestHeaders::kContentType);
+
+  // Clear the Content-Length values.
+  current_response_->content_length = current_response_->encoded_body_length =
+      0;
+  current_response_->headers->RemoveHeader(
+      net::HttpRequestHeaders::kContentLength);
+
+  current_response_->encoded_data_length =
+      current_response_->headers->raw_headers().size();
+
+  const net::RedirectInfo& redirect_info = MakeRedirectInfo(
+      request_, current_response_->headers.get(), new_location, 0);
+  current_response_->headers->ReplaceStatusLine(
+      MakeStatusLine(redirect_info.status_code, std::string(), true));
+
+  return redirect_info;
+}
+
+void InterceptedRequest::ContinueToBeforeRedirect(
+    const net::RedirectInfo& redirect_info,
+    int error_code) {
+  if (error_code != net::OK) {
+    SendErrorAndCompleteImmediately(error_code);
+    return;
+  }
+
+  request_was_redirected_ = true;
+
+  if (header_client_redirect_url_.is_valid())
+    header_client_redirect_url_ = GURL();
+
+  const GURL redirect_url = redirect_url_;
+  override_headers_ = nullptr;
+  redirect_url_ = GURL();
+
+  // Resume handling of client messages after continuing from an async callback.
+  if (proxied_client_binding_)
+    proxied_client_binding_.ResumeIncomingMethodCallProcessing();
+
+  if (redirect_url.is_valid()) {
+    net::RedirectInfo new_redirect_info = redirect_info;
+    new_redirect_info.new_url = redirect_url;
+    target_client_->OnReceiveRedirect(new_redirect_info,
+                                      std::move(current_response_));
+    request_.url = redirect_url;
+  } else {
+    target_client_->OnReceiveRedirect(redirect_info,
+                                      std::move(current_response_));
+    request_.url = redirect_info.new_url;
+  }
+
+  // If request_ changes from POST to GET, strip POST headers.
+  const bool post_to_get =
+      request_.method == "POST" &&
+      redirect_info.new_method == net::HttpRequestHeaders::kGetMethod;
+
+  request_.method = redirect_info.new_method;
+  request_.site_for_cookies = redirect_info.new_site_for_cookies;
+  request_.referrer = GURL(redirect_info.new_referrer);
+  request_.referrer_policy = redirect_info.new_referrer_policy;
+
+  // The request method can be changed to "GET". In this case we need to
+  // reset the request body manually, and strip the POST headers.
+  if (request_.method == net::HttpRequestHeaders::kGetMethod) {
+    request_.request_body = nullptr;
+
+    if (post_to_get) {
+      request_.headers.RemoveHeader(net::HttpRequestHeaders::kContentLength);
+      request_.headers.RemoveHeader(net::HttpRequestHeaders::kContentType);
+    }
+  }
+}
+
+void InterceptedRequest::ContinueToResponseStarted(int error_code) {
+  if (error_code != net::OK) {
+    SendErrorAndCompleteImmediately(error_code);
+    return;
+  }
+
+  const GURL redirect_url = redirect_url_;
+  override_headers_ = nullptr;
+  redirect_url_ = GURL();
+
+  std::string location;
+  const bool is_redirect = redirect_url.is_valid() ||
+                           (current_response_->headers &&
+                            current_response_->headers->IsRedirect(&location));
+  if (stream_loader_ && is_redirect) {
+    // Redirecting from OnReceiveResponse generally isn't supported by the
+    // NetworkService, so we can only support it when using a custom loader.
+    // TODO(network): Remove this special case.
+    const GURL new_location = redirect_url.is_valid()
+                                  ? redirect_url
+                                  : original_url_.Resolve(location);
+    const net::RedirectInfo& redirect_info =
+        MakeRedirectResponseAndInfo(new_location);
+
+    HandleResponseOrRedirectHeaders(
+        redirect_info,
+        base::BindOnce(&InterceptedRequest::ContinueToBeforeRedirect,
+                       weak_factory_.GetWeakPtr(), redirect_info));
+  } else {
+    LOG_IF(WARNING, is_redirect) << "Redirect at this time is not supported by "
+                                    "the default network loader.";
+
+    // Resume handling of client messages after continuing from an async
+    // callback.
+    if (proxied_client_binding_)
+      proxied_client_binding_.ResumeIncomingMethodCallProcessing();
+
+    target_client_->OnReceiveResponse(std::move(current_response_));
+  }
+
+  if (stream_loader_)
+    stream_loader_->ContinueResponse(is_redirect);
+}
+
+void InterceptedRequest::OnDestroy() {
+  // We don't want any callbacks after this point.
+  weak_factory_.InvalidateWeakPtrs();
+
+  factory_->request_handler_->OnRequestComplete(id_, request_, status_);
+
+  // Destroys |this|.
+  factory_->RemoveRequest(this);
+}
+
+void InterceptedRequest::OnProcessRequestHeaders(
+    const GURL& redirect_url,
+    net::HttpRequestHeaders* modified_headers,
+    std::vector<std::string>* removed_headers) {
+  factory_->request_handler_->ProcessRequestHeaders(
+      id_, request_, redirect_url, modified_headers, removed_headers);
+
+  if (!modified_headers->IsEmpty() || !removed_headers->empty()) {
+    request_.headers.MergeFrom(*modified_headers);
+    for (const std::string& name : *removed_headers)
+      request_.headers.RemoveHeader(name);
+  }
+}
+
+void InterceptedRequest::OnURLLoaderClientError() {
+  // We set |wait_for_loader_error| to true because if the loader did have a
+  // custom_reason error then the client would be reset as well and it would be
+  // a race as to which connection error we saw first.
+  CallOnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED),
+                 true /* wait_for_loader_error */);
+}
+
+void InterceptedRequest::OnURLLoaderError(uint32_t custom_reason,
+                                          const std::string& description) {
+  if (custom_reason == network::mojom::URLLoader::kClientDisconnectReason)
+    SendErrorCallback(safe_browsing::GetNetErrorCodeForSafeBrowsing(), true);
+
+  got_loader_error_ = true;
+
+  // If CallOnComplete was already called, then this object is ready to be
+  // deleted.
+  if (!target_client_)
+    OnDestroy();
+}
+
+void InterceptedRequest::CallOnComplete(
+    const network::URLLoaderCompletionStatus& status,
+    bool wait_for_loader_error) {
+  status_ = status;
+
+  if (target_client_)
+    target_client_->OnComplete(status);
+
+  if (proxied_loader_binding_ &&
+      (wait_for_loader_error && !got_loader_error_)) {
+    // Don't delete |this| yet, in case the |proxied_loader_binding_|'s
+    // error_handler is called with a reason to indicate an error which we want
+    // to send to the client bridge. Also reset |target_client_| so we don't
+    // get its error_handler called and then delete |this|.
+    target_client_.reset();
+
+    // Since the original client is gone no need to continue loading the
+    // request.
+    proxied_client_binding_.Close();
+    header_client_receiver_.reset();
+    target_loader_.reset();
+
+    // In case there are pending checks as to whether this request should be
+    // intercepted, we don't want that causing |target_client_| to be used
+    // later.
+    weak_factory_.InvalidateWeakPtrs();
+  } else {
+    OnDestroy();
+  }
+}
+
+void InterceptedRequest::SendErrorAndCompleteImmediately(int error_code) {
+  status_ = network::URLLoaderCompletionStatus(error_code);
+  SendErrorCallback(status_.error_code, false);
+  target_client_->OnComplete(status_);
+  OnDestroy();
+}
+
+void InterceptedRequest::SendErrorCallback(int error_code,
+                                           bool safebrowsing_hit) {
+  // Ensure we only send one error callback, e.g. to avoid sending two if
+  // there's both a networking error and safe browsing blocked the request.
+  if (sent_error_callback_)
+    return;
+
+  sent_error_callback_ = true;
+  factory_->request_handler_->OnRequestError(id_, request_, error_code,
+                                             safebrowsing_hit);
+}
+
+void InterceptedRequest::OnUploadProgressACK() {
+  DCHECK(waiting_for_upload_progress_ack_);
+  waiting_for_upload_progress_ack_ = false;
+}
+
+//==============================
+// InterceptedRequestHandler
+//==============================
+
+InterceptedRequestHandler::InterceptedRequestHandler() {}
+InterceptedRequestHandler::~InterceptedRequestHandler() {}
+
+void InterceptedRequestHandler::OnBeforeRequest(
+    const RequestId& id,
+    network::ResourceRequest* request,
+    bool request_was_redirected,
+    OnBeforeRequestResultCallback callback,
+    CancelRequestCallback cancel_callback) {
+  std::move(callback).Run(false, false);
+}
+
+void InterceptedRequestHandler::ShouldInterceptRequest(
+    const RequestId& id,
+    network::ResourceRequest* request,
+    ShouldInterceptRequestResultCallback callback) {
+  std::move(callback).Run(nullptr);
+}
+
+void InterceptedRequestHandler::OnRequestResponse(
+    const RequestId& id,
+    network::ResourceRequest* request,
+    net::HttpResponseHeaders* headers,
+    base::Optional<net::RedirectInfo> redirect_info,
+    OnRequestResponseResultCallback callback) {
+  std::move(callback).Run(
+      ResponseMode::CONTINUE, nullptr,
+      redirect_info.has_value() ? redirect_info->new_url : GURL());
+}
+
+mojo::ScopedDataPipeConsumerHandle
+InterceptedRequestHandler::OnFilterResponseBody(
+    const RequestId& id,
+    const network::ResourceRequest& request,
+    mojo::ScopedDataPipeConsumerHandle body) {
+  return body;
+}
+
+//==============================
+// ProxyURLLoaderFactory
+//==============================
+
+ProxyURLLoaderFactory::ProxyURLLoaderFactory(
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver,
+    network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
+    mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
+        header_client_receiver,
+    std::unique_ptr<InterceptedRequestHandler> request_handler)
+    : request_handler_(std::move(request_handler)), weak_factory_(this) {
+  CEF_REQUIRE_IOT();
+  DCHECK(request_handler_);
+
+  // Actual creation of the factory.
+  if (target_factory_info) {
+    target_factory_.Bind(std::move(target_factory_info));
+    target_factory_.set_connection_error_handler(base::BindOnce(
+        &ProxyURLLoaderFactory::OnTargetFactoryError, base::Unretained(this)));
+  }
+  proxy_bindings_.AddBinding(this, std::move(factory_receiver));
+  proxy_bindings_.set_connection_error_handler(base::BindRepeating(
+      &ProxyURLLoaderFactory::OnProxyBindingError, base::Unretained(this)));
+
+  if (header_client_receiver)
+    url_loader_header_client_receiver_.Bind(std::move(header_client_receiver));
+}
+
+ProxyURLLoaderFactory::~ProxyURLLoaderFactory() {
+  CEF_REQUIRE_IOT();
+}
+
+// static
+void ProxyURLLoaderFactory::CreateOnIOThread(
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver,
+    network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
+    mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
+        header_client_receiver,
+    content::ResourceContext* resource_context,
+    std::unique_ptr<InterceptedRequestHandler> request_handler) {
+  CEF_REQUIRE_IOT();
+  auto proxy = new ProxyURLLoaderFactory(
+      std::move(factory_receiver), std::move(target_factory_info),
+      std::move(header_client_receiver), std::move(request_handler));
+  ResourceContextData::AddProxy(proxy, resource_context);
+}
+
+void ProxyURLLoaderFactory::SetDisconnectCallback(
+    DisconnectCallback on_disconnect) {
+  CEF_REQUIRE_IOT();
+  DCHECK(!destroyed_);
+  DCHECK(!on_disconnect_);
+  on_disconnect_ = std::move(on_disconnect);
+}
+
+// static
+void ProxyURLLoaderFactory::CreateProxy(
+    content::BrowserContext* browser_context,
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
+    mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
+        header_client,
+    std::unique_ptr<InterceptedRequestHandler> request_handler) {
+  CEF_REQUIRE_UIT();
+  DCHECK(request_handler);
+
+  auto proxied_receiver = std::move(*factory_receiver);
+  network::mojom::URLLoaderFactoryPtrInfo target_factory_info;
+  *factory_receiver = mojo::MakeRequest(&target_factory_info);
+
+  mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
+      header_client_receiver;
+  if (header_client)
+    header_client_receiver = header_client->InitWithNewPipeAndPassReceiver();
+
+  content::ResourceContext* resource_context =
+      browser_context->GetResourceContext();
+  DCHECK(resource_context);
+
+  CEF_POST_TASK(
+      CEF_IOT,
+      base::BindOnce(
+          &ProxyURLLoaderFactory::CreateOnIOThread, std::move(proxied_receiver),
+          std::move(target_factory_info), std::move(header_client_receiver),
+          base::Unretained(resource_context), std::move(request_handler)));
+}
+
+// static
+ProxyURLLoaderFactory* ProxyURLLoaderFactory::CreateProxy(
+    content::WebContents::Getter web_contents_getter,
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
+    std::unique_ptr<InterceptedRequestHandler> request_handler) {
+  CEF_REQUIRE_IOT();
+  DCHECK(request_handler);
+
+  auto proxy = new ProxyURLLoaderFactory(
+      std::move(loader_receiver), nullptr,
+      mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>(),
+      std::move(request_handler));
+  CEF_POST_TASK(CEF_UIT,
+                base::BindOnce(ResourceContextData::AddProxyOnUIThread,
+                               base::Unretained(proxy), web_contents_getter));
+  return proxy;
+}
+
+void ProxyURLLoaderFactory::CreateLoaderAndStart(
+    mojo::PendingReceiver<network::mojom::URLLoader> receiver,
+    int32_t routing_id,
+    int32_t request_id,
+    uint32_t options,
+    const network::ResourceRequest& request,
+    mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  CEF_REQUIRE_IOT();
+  if (!CONTEXT_STATE_VALID()) {
+    // Don't start a request while we're shutting down.
+    return;
+  }
+
+  bool pass_through = false;
+  if (pass_through) {
+    // This is the so-called pass-through, no-op option.
+    target_factory_->CreateLoaderAndStart(
+        std::move(receiver), routing_id, request_id, options, request,
+        std::move(client), traffic_annotation);
+    return;
+  }
+
+  mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_clone;
+  if (target_factory_)
+    target_factory_->Clone(
+        target_factory_clone.InitWithNewPipeAndPassReceiver());
+
+  InterceptedRequest* req = new InterceptedRequest(
+      this, RequestId(request_id, routing_id), options, request,
+      traffic_annotation, std::move(receiver), std::move(client),
+      std::move(target_factory_clone));
+  requests_.insert(std::make_pair(request_id, base::WrapUnique(req)));
+  req->Restart();
+}
+
+void ProxyURLLoaderFactory::Clone(
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory) {
+  CEF_REQUIRE_IOT();
+  proxy_bindings_.AddBinding(this, std::move(factory));
+}
+
+void ProxyURLLoaderFactory::OnLoaderCreated(
+    int32_t request_id,
+    mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
+  CEF_REQUIRE_IOT();
+  auto request_it = requests_.find(request_id);
+  if (request_it != requests_.end())
+    request_it->second->OnLoaderCreated(std::move(receiver));
+}
+
+void ProxyURLLoaderFactory::OnLoaderForCorsPreflightCreated(
+    const ::network::ResourceRequest& request,
+    mojo::PendingReceiver<network::mojom::TrustedHeaderClient> header_client) {
+  CEF_REQUIRE_IOT();
+  // TODO(cef): Should we do something here?
+}
+
+void ProxyURLLoaderFactory::OnTargetFactoryError() {
+  // Stop calls to CreateLoaderAndStart() when |target_factory_| is invalid.
+  target_factory_.reset();
+  proxy_bindings_.CloseAllBindings();
+
+  MaybeDestroySelf();
+}
+
+void ProxyURLLoaderFactory::OnProxyBindingError() {
+  if (proxy_bindings_.empty())
+    target_factory_.reset();
+
+  MaybeDestroySelf();
+}
+
+void ProxyURLLoaderFactory::RemoveRequest(InterceptedRequest* request) {
+  auto it = requests_.find(request->id().request_id());
+  DCHECK(it != requests_.end());
+  requests_.erase(it);
+
+  MaybeDestroySelf();
+}
+
+void ProxyURLLoaderFactory::MaybeDestroySelf() {
+  // Even if all URLLoaderFactory pipes connected to this object have been
+  // closed it has to stay alive until all active requests have completed.
+  if (target_factory_.is_bound() || !requests_.empty())
+    return;
+
+  destroyed_ = true;
+
+  // In some cases we may be destroyed before SetDisconnectCallback is called.
+  if (on_disconnect_) {
+    // Deletes |this|.
+    std::move(on_disconnect_).Run(this);
+  }
+}
+
+}  // namespace net_service
diff --git a/src/libcef/browser/net_service/proxy_url_loader_factory.h b/src/libcef/browser/net_service/proxy_url_loader_factory.h
new file mode 100644
index 0000000..fc1b4f2
--- /dev/null
+++ b/src/libcef/browser/net_service/proxy_url_loader_factory.h
@@ -0,0 +1,222 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_PROXY_URL_LOADER_FACTORY_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_PROXY_URL_LOADER_FACTORY_H_
+
+#include "libcef/browser/net_service/stream_reader_url_loader.h"
+
+#include "base/callback.h"
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/hash/hash.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+
+namespace net_service {
+
+class InterceptedRequest;
+class ResourceContextData;
+
+// Implement this interface to to evaluate requests. All methods are called on
+// the IO thread, and all callbacks must be executed on the IO thread.
+class InterceptedRequestHandler {
+ public:
+  InterceptedRequestHandler();
+  virtual ~InterceptedRequestHandler();
+
+  // Optionally modify |request| and execute |callback| to continue the request.
+  // Set |intercept_request| to false if the request will not be intercepted.
+  // Set |intercept_only| to true if the loader should not proceed unless the
+  // request is intercepted. Keep a reference to |cancel_callback| and execute
+  // at any time to cancel the request.
+  using OnBeforeRequestResultCallback =
+      base::OnceCallback<void(bool /* intercept_request */,
+                              bool /* intercept_only */)>;
+  using CancelRequestCallback = base::OnceCallback<void(int /* error_code */)>;
+  virtual void OnBeforeRequest(const RequestId& id,
+                               network::ResourceRequest* request,
+                               bool request_was_redirected,
+                               OnBeforeRequestResultCallback callback,
+                               CancelRequestCallback cancel_callback);
+
+  // Optionally modify |request| and execute |callback| after determining if the
+  // request hould be intercepted.
+  using ShouldInterceptRequestResultCallback =
+      base::OnceCallback<void(std::unique_ptr<ResourceResponse>)>;
+  virtual void ShouldInterceptRequest(
+      const RequestId& id,
+      network::ResourceRequest* request,
+      ShouldInterceptRequestResultCallback callback);
+
+  // Called to evaluate and optionally modify request headers. |request| is the
+  // current request information. |redirect_url| will be non-empty if this
+  // method is called in response to a redirect. The |modified_headers| and
+  // |removed_headers| may be modified. If non-empty the |modified_headers|
+  // values will be merged first, and then any |removed_headers| values will be
+  // removed. This comparison is case sensitive.
+  virtual void ProcessRequestHeaders(
+      const RequestId& id,
+      const network::ResourceRequest& request,
+      const GURL& redirect_url,
+      net::HttpRequestHeaders* modified_headers,
+      std::vector<std::string>* removed_headers) {}
+
+  // Called to evaluate and optionally modify response headers. |request| is the
+  // current request information. |redirect_url| will be non-empty if this
+  // method is called in response to a redirect. Even though |head| is const the
+  // |head.headers| value is non-const and any changes will be passed to the
+  // client.
+  virtual void ProcessResponseHeaders(const RequestId& id,
+                                      const network::ResourceRequest& request,
+                                      const GURL& redirect_url,
+                                      net::HttpResponseHeaders* headers) {}
+
+  enum class ResponseMode {
+    // Continue the request.
+    CONTINUE,
+    // Restart the request.
+    RESTART,
+    // Cancel the request.
+    CANCEL
+  };
+
+  // Called on response. |request| is the current request information.
+  // |redirect_info| will be non-empty for redirect responses.
+  // Optionally modify |request| and execute the callback as appropriate.
+  using OnRequestResponseResultCallback = base::OnceCallback<void(
+      ResponseMode /* response_mode */,
+      scoped_refptr<net::HttpResponseHeaders> /* override_headers */,
+      const GURL& /* redirect_url */)>;
+  virtual void OnRequestResponse(
+      const RequestId& id,
+      network::ResourceRequest* request,
+      net::HttpResponseHeaders* headers,
+      base::Optional<net::RedirectInfo> redirect_info,
+      OnRequestResponseResultCallback callback);
+
+  // Called to optionally filter the response body.
+  virtual mojo::ScopedDataPipeConsumerHandle OnFilterResponseBody(
+      const RequestId& id,
+      const network::ResourceRequest& request,
+      mojo::ScopedDataPipeConsumerHandle body);
+
+  // Called on completion notification from the loader (successful or not).
+  virtual void OnRequestComplete(
+      const RequestId& id,
+      const network::ResourceRequest& request,
+      const network::URLLoaderCompletionStatus& status) {}
+
+  // Called on error.
+  virtual void OnRequestError(const RequestId& id,
+                              const network::ResourceRequest& request,
+                              int error_code,
+                              bool safebrowsing_hit) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InterceptedRequestHandler);
+};
+
+// URL Loader Factory that supports request/response interception, processing
+// and callback invocation.
+// Based on android_webview/browser/network_service/
+// aw_proxying_url_loader_factory.cc
+class ProxyURLLoaderFactory
+    : public network::mojom::URLLoaderFactory,
+      public network::mojom::TrustedURLLoaderHeaderClient {
+ public:
+  ~ProxyURLLoaderFactory() override;
+
+  // Create a proxy object on the UI thread.
+  static void CreateProxy(
+      content::BrowserContext* browser_context,
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
+      mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
+          header_client,
+      std::unique_ptr<InterceptedRequestHandler> request_handler);
+
+  // Create a proxy object on the IO thread.
+  static ProxyURLLoaderFactory* CreateProxy(
+      content::WebContents::Getter web_contents_getter,
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_request,
+      std::unique_ptr<InterceptedRequestHandler> request_handler);
+
+  // mojom::URLLoaderFactory methods:
+  void CreateLoaderAndStart(
+      mojo::PendingReceiver<network::mojom::URLLoader> receiver,
+      int32_t routing_id,
+      int32_t request_id,
+      uint32_t options,
+      const network::ResourceRequest& request,
+      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
+      override;
+  void Clone(
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory) override;
+
+  // network::mojom::TrustedURLLoaderHeaderClient:
+  void OnLoaderCreated(
+      int32_t request_id,
+      mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver)
+      override;
+  void OnLoaderForCorsPreflightCreated(
+      const ::network::ResourceRequest& request,
+      mojo::PendingReceiver<network::mojom::TrustedHeaderClient> header_client)
+      override;
+
+ private:
+  friend class InterceptedRequest;
+  friend class ResourceContextData;
+
+  ProxyURLLoaderFactory(
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver,
+      network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
+      mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
+          header_client_receiver,
+      std::unique_ptr<InterceptedRequestHandler> request_handler);
+
+  static void CreateOnIOThread(
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver,
+      network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
+      mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
+          header_client_receiver,
+      content::ResourceContext* resource_context,
+      std::unique_ptr<InterceptedRequestHandler> request_handler);
+
+  using DisconnectCallback = base::OnceCallback<void(ProxyURLLoaderFactory*)>;
+  void SetDisconnectCallback(DisconnectCallback on_disconnect);
+
+  void OnTargetFactoryError();
+  void OnProxyBindingError();
+  void RemoveRequest(InterceptedRequest* request);
+  void MaybeDestroySelf();
+
+  mojo::BindingSet<network::mojom::URLLoaderFactory> proxy_bindings_;
+  network::mojom::URLLoaderFactoryPtr target_factory_;
+  mojo::Binding<network::mojom::TrustedURLLoaderHeaderClient>
+      url_loader_header_client_receiver_{this};
+
+  std::unique_ptr<InterceptedRequestHandler> request_handler_;
+
+  bool destroyed_ = false;
+  DisconnectCallback on_disconnect_;
+
+  // Map of request ID to request object.
+  std::map<int32_t, std::unique_ptr<InterceptedRequest>> requests_;
+
+  base::WeakPtrFactory<ProxyURLLoaderFactory> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyURLLoaderFactory);
+};
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_PROXY_URL_LOADER_FACTORY_H_
diff --git a/src/libcef/browser/net_service/resource_handler_wrapper.cc b/src/libcef/browser/net_service/resource_handler_wrapper.cc
new file mode 100644
index 0000000..2f27bd1
--- /dev/null
+++ b/src/libcef/browser/net_service/resource_handler_wrapper.cc
@@ -0,0 +1,506 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/net_service/resource_handler_wrapper.h"
+
+#include "libcef/browser/net_service/proxy_url_loader_factory.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/net_service/net_service_util.h"
+#include "libcef/common/request_impl.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "net/http/http_status_code.h"
+
+namespace net_service {
+
+namespace {
+
+class SkipCallbackWrapper : public CefResourceSkipCallback {
+ public:
+  explicit SkipCallbackWrapper(InputStream::SkipCallback callback)
+      : callback_(std::move(callback)),
+        work_thread_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+
+  ~SkipCallbackWrapper() override {
+    if (!callback_.is_null()) {
+      // Make sure it executes on the correct thread.
+      work_thread_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback_), net::ERR_FAILED));
+    }
+  }
+
+  void Continue(int64 bytes_skipped) override {
+    if (!work_thread_task_runner_->RunsTasksInCurrentSequence()) {
+      work_thread_task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(&SkipCallbackWrapper::Continue, this, bytes_skipped));
+      return;
+    }
+    if (!callback_.is_null()) {
+      std::move(callback_).Run(bytes_skipped);
+    }
+  }
+
+  void Disconnect() { callback_.Reset(); }
+
+ private:
+  InputStream::SkipCallback callback_;
+
+  scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner_;
+
+  IMPLEMENT_REFCOUNTING(SkipCallbackWrapper);
+  DISALLOW_COPY_AND_ASSIGN(SkipCallbackWrapper);
+};
+
+class ReadCallbackWrapper : public CefResourceReadCallback {
+ public:
+  explicit ReadCallbackWrapper(InputStream::ReadCallback callback)
+      : callback_(std::move(callback)),
+        work_thread_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+
+  ~ReadCallbackWrapper() override {
+    if (!callback_.is_null()) {
+      // Make sure it executes on the correct thread.
+      work_thread_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback_), net::ERR_FAILED));
+    }
+  }
+
+  void Continue(int bytes_read) override {
+    if (!work_thread_task_runner_->RunsTasksInCurrentSequence()) {
+      work_thread_task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(&ReadCallbackWrapper::Continue, this, bytes_read));
+      return;
+    }
+    if (!callback_.is_null()) {
+      std::move(callback_).Run(bytes_read);
+    }
+  }
+
+  void Disconnect() { callback_.Reset(); }
+
+ private:
+  InputStream::ReadCallback callback_;
+
+  scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner_;
+
+  IMPLEMENT_REFCOUNTING(ReadCallbackWrapper);
+  DISALLOW_COPY_AND_ASSIGN(ReadCallbackWrapper);
+};
+
+// Helper for accessing a CefResourceHandler without creating reference loops.
+class HandlerProvider : public base::RefCountedThreadSafe<HandlerProvider> {
+ public:
+  explicit HandlerProvider(CefRefPtr<CefResourceHandler> handler)
+      : handler_(handler) {
+    DCHECK(handler_);
+  }
+  virtual ~HandlerProvider() {
+    // Detach should have been called.
+    DCHECK(!handler_);
+  }
+
+  CefRefPtr<CefResourceHandler> handler() const {
+    base::AutoLock lock_scope(lock_);
+    return handler_;
+  }
+
+  void Detach() {
+    base::AutoLock lock_scope(lock_);
+    if (!handler_)
+      return;
+
+    // Execute on the expected thread.
+    CEF_POST_TASK(CEF_IOT,
+                  base::BindOnce(&CefResourceHandler::Cancel, handler_));
+
+    handler_ = nullptr;
+  }
+
+ private:
+  mutable base::Lock lock_;
+  CefRefPtr<CefResourceHandler> handler_;
+};
+
+class ReadResponseCallbackWrapper : public CefCallback {
+ public:
+  ~ReadResponseCallbackWrapper() override {
+    if (callback_) {
+      // This will post to the correct thread if necessary.
+      callback_->Continue(net::ERR_FAILED);
+    }
+  }
+
+  void Continue() override {
+    CEF_POST_TASK(CEF_IOT,
+                  base::BindOnce(&ReadResponseCallbackWrapper::DoRead, this));
+  }
+
+  void Cancel() override {
+    CEF_POST_TASK(CEF_IOT,
+                  base::BindOnce(&ReadResponseCallbackWrapper::DoCancel, this));
+  }
+
+  static void ReadResponse(scoped_refptr<HandlerProvider> handler_provider,
+                           net::IOBuffer* dest,
+                           int length,
+                           CefRefPtr<ReadCallbackWrapper> callback) {
+    CEF_POST_TASK(
+        CEF_IOT,
+        base::BindOnce(ReadResponseCallbackWrapper::ReadResponseOnIOThread,
+                       handler_provider, base::Unretained(dest), length,
+                       callback));
+  }
+
+ private:
+  ReadResponseCallbackWrapper(scoped_refptr<HandlerProvider> handler_provider,
+                              net::IOBuffer* dest,
+                              int length,
+                              CefRefPtr<ReadCallbackWrapper> callback)
+      : handler_provider_(handler_provider),
+        dest_(dest),
+        length_(length),
+        callback_(callback) {}
+
+  static void ReadResponseOnIOThread(
+      scoped_refptr<HandlerProvider> handler_provider,
+      net::IOBuffer* dest,
+      int length,
+      CefRefPtr<ReadCallbackWrapper> callback) {
+    CEF_REQUIRE_IOT();
+    CefRefPtr<ReadResponseCallbackWrapper> callbackWrapper =
+        new ReadResponseCallbackWrapper(handler_provider, dest, length,
+                                        callback);
+    callbackWrapper->DoRead();
+  }
+
+  void DoRead() {
+    CEF_REQUIRE_IOT();
+    if (!callback_)
+      return;
+
+    auto handler = handler_provider_->handler();
+    if (!handler) {
+      DoCancel();
+      return;
+    }
+
+    int bytes_read = 0;
+    bool result =
+        handler->ReadResponse(dest_->data(), length_, bytes_read, this);
+    if (result) {
+      if (bytes_read > 0) {
+        // Continue immediately.
+        callback_->Continue(bytes_read);
+        callback_ = nullptr;
+      }
+      return;
+    }
+
+    // Signal response completion immediately.
+    callback_->Continue(0);
+    callback_ = nullptr;
+  }
+
+  void DoCancel() {
+    CEF_REQUIRE_IOT();
+    if (callback_) {
+      callback_->Continue(net::ERR_FAILED);
+      callback_ = nullptr;
+    }
+  }
+
+  scoped_refptr<HandlerProvider> handler_provider_;
+  net::IOBuffer* const dest_;
+  int length_;
+  CefRefPtr<ReadCallbackWrapper> callback_;
+
+  IMPLEMENT_REFCOUNTING(ReadResponseCallbackWrapper);
+  DISALLOW_COPY_AND_ASSIGN(ReadResponseCallbackWrapper);
+};
+
+class InputStreamWrapper : public InputStream {
+ public:
+  explicit InputStreamWrapper(scoped_refptr<HandlerProvider> handler_provider)
+      : handler_provider_(handler_provider) {}
+
+  ~InputStreamWrapper() override { Cancel(); }
+
+  // InputStream methods:
+  bool Skip(int64_t n, int64_t* bytes_skipped, SkipCallback callback) override {
+    auto handler = handler_provider_->handler();
+    if (!handler) {
+      // Cancel immediately.
+      *bytes_skipped = net::ERR_FAILED;
+      return false;
+    }
+
+    CefRefPtr<SkipCallbackWrapper> callbackWrapper =
+        new SkipCallbackWrapper(std::move(callback));
+    bool result = handler->Skip(n, *bytes_skipped, callbackWrapper.get());
+    if (result) {
+      if (*bytes_skipped > 0) {
+        // Continue immediately.
+        callbackWrapper->Disconnect();
+      }
+      return true;
+    }
+
+    // Cancel immediately.
+    return false;
+  }
+
+  bool Read(net::IOBuffer* dest,
+            int length,
+            int* bytes_read,
+            ReadCallback callback) override {
+    auto handler = handler_provider_->handler();
+    if (!handler) {
+      // Cancel immediately.
+      *bytes_read = net::ERR_FAILED;
+      return false;
+    }
+
+    CefRefPtr<ReadCallbackWrapper> callbackWrapper =
+        new ReadCallbackWrapper(std::move(callback));
+    bool result =
+        handler->Read(dest->data(), length, *bytes_read, callbackWrapper.get());
+    if (result) {
+      if (*bytes_read > 0) {
+        // Continue immediately.
+        callbackWrapper->Disconnect();
+      }
+      return true;
+    }
+
+    if (*bytes_read == -1) {
+      // Call ReadResponse on the IO thread.
+      ReadResponseCallbackWrapper::ReadResponse(handler_provider_, dest, length,
+                                                callbackWrapper);
+      *bytes_read = 0;
+      return true;
+    }
+
+    // Complete or cancel immediately.
+    return false;
+  }
+
+  void Cancel() {
+    // Triggers a call to Cancel on the handler.
+    handler_provider_->Detach();
+  }
+
+ private:
+  scoped_refptr<HandlerProvider> handler_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputStreamWrapper);
+};
+
+class OpenCallbackWrapper : public CefCallback {
+ public:
+  OpenCallbackWrapper(ResourceResponse::OpenCallback callback,
+                      std::unique_ptr<InputStreamWrapper> stream)
+      : callback_(std::move(callback)),
+        stream_(std::move(stream)),
+        work_thread_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+
+  ~OpenCallbackWrapper() override {
+    if (!callback_.is_null()) {
+      // Make sure it executes on the correct thread.
+      work_thread_task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(&OpenCallbackWrapper::Execute, std::move(callback_),
+                         std::move(stream_), false));
+    }
+  }
+
+  void Continue() override {
+    if (!work_thread_task_runner_->RunsTasksInCurrentSequence()) {
+      work_thread_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(&OpenCallbackWrapper::Continue, this));
+      return;
+    }
+    if (!callback_.is_null()) {
+      Execute(std::move(callback_), std::move(stream_), true);
+    }
+  }
+
+  void Cancel() override {
+    if (!work_thread_task_runner_->RunsTasksInCurrentSequence()) {
+      work_thread_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(&OpenCallbackWrapper::Cancel, this));
+      return;
+    }
+    if (!callback_.is_null()) {
+      Execute(std::move(callback_), std::move(stream_), false);
+    }
+  }
+
+ private:
+  static void Execute(ResourceResponse::OpenCallback callback,
+                      std::unique_ptr<InputStreamWrapper> stream,
+                      bool cont) {
+    std::move(callback).Run(cont ? std::move(stream) : nullptr);
+  }
+
+  ResourceResponse::OpenCallback callback_;
+  std::unique_ptr<InputStreamWrapper> stream_;
+
+  scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner_;
+
+  IMPLEMENT_REFCOUNTING(OpenCallbackWrapper);
+  DISALLOW_COPY_AND_ASSIGN(OpenCallbackWrapper);
+};
+
+void CallProcessRequestOnIOThread(
+    scoped_refptr<HandlerProvider> handler_provider,
+    CefRefPtr<CefRequestImpl> request,
+    CefRefPtr<OpenCallbackWrapper> callbackWrapper) {
+  CEF_REQUIRE_IOT();
+  auto handler = handler_provider->handler();
+  if (!handler) {
+    callbackWrapper->Cancel();
+    return;
+  }
+
+  if (!handler->ProcessRequest(request.get(), callbackWrapper.get())) {
+    callbackWrapper->Cancel();
+  }
+}
+
+class ResourceResponseWrapper : public ResourceResponse {
+ public:
+  ResourceResponseWrapper(const RequestId request_id,
+                          CefRefPtr<CefResourceHandler> handler)
+      : request_id_(request_id),
+        handler_provider_(new HandlerProvider(handler)) {}
+
+  ~ResourceResponseWrapper() override {
+    // Triggers a call to Cancel on the handler.
+    handler_provider_->Detach();
+  }
+
+  // ResourceResponse methods:
+  bool OpenInputStream(const RequestId& request_id,
+                       const network::ResourceRequest& request,
+                       OpenCallback callback) override {
+    DCHECK_EQ(request_id, request_id_);
+
+    auto handler = handler_provider_->handler();
+    if (!handler) {
+      // Cancel immediately.
+      return false;
+    }
+
+    // May be recreated on redirect.
+    request_ = new CefRequestImpl();
+    request_->Set(&request, request_id.hash());
+    request_->SetReadOnly(true);
+
+    CefRefPtr<OpenCallbackWrapper> callbackWrapper = new OpenCallbackWrapper(
+        std::move(callback),
+        std::make_unique<InputStreamWrapper>(handler_provider_));
+    bool handle_request = false;
+    bool result =
+        handler->Open(request_.get(), handle_request, callbackWrapper.get());
+    if (result) {
+      if (handle_request) {
+        // Continue immediately.
+        callbackWrapper->Continue();
+      }
+      return true;
+    }
+
+    if (handle_request) {
+      // Cancel immediately.
+      callbackWrapper->Cancel();
+      return true;
+    }
+
+    // Call ProcessRequest on the IO thread.
+    CEF_POST_TASK(
+        CEF_IOT, base::BindOnce(CallProcessRequestOnIOThread, handler_provider_,
+                                request_, callbackWrapper));
+    return true;
+  }
+
+  void GetResponseHeaders(const RequestId& request_id,
+                          int* status_code,
+                          std::string* reason_phrase,
+                          std::string* mime_type,
+                          std::string* charset,
+                          int64_t* content_length,
+                          HeaderMap* extra_headers) override {
+    DCHECK_EQ(request_id, request_id_);
+    CEF_REQUIRE_IOT();
+
+    auto handler = handler_provider_->handler();
+    if (!handler) {
+      // Cancel immediately.
+      *status_code = net::ERR_FAILED;
+      return;
+    }
+
+    CefRefPtr<CefResponse> response = CefResponse::Create();
+    int64_t response_length = -1;
+    CefString redirect_url;
+    handler->GetResponseHeaders(response, response_length, redirect_url);
+
+    const auto error_code = response->GetError();
+    if (error_code != ERR_NONE) {
+      // Early exit if the handler reported an error.
+      *status_code = error_code;
+      return;
+    }
+
+    if (!redirect_url.empty()) {
+      // Perform a redirect.
+      *status_code = net::HTTP_TEMPORARY_REDIRECT;
+      *reason_phrase = std::string();
+      extra_headers->insert(
+          std::make_pair(kHTTPLocationHeaderName, redirect_url));
+    } else {
+      *status_code = response->GetStatus();
+      *reason_phrase = response->GetStatusText();
+    }
+
+    if (reason_phrase->empty() && *status_code > 0) {
+      *reason_phrase = net::GetHttpReasonPhrase(
+          static_cast<net::HttpStatusCode>(*status_code));
+    }
+
+    *mime_type = response->GetMimeType();
+    *charset = response->GetCharset();
+
+    // A |content_length| value may already be specified if the request included
+    // a Range header.
+    if (response_length >= 0 && *content_length == -1)
+      *content_length = response_length;
+
+    CefResponse::HeaderMap headerMap;
+    response->GetHeaderMap(headerMap);
+    for (const auto& value : headerMap) {
+      extra_headers->insert(std::make_pair(value.first, value.second));
+    }
+  }
+
+ private:
+  const RequestId request_id_;
+
+  CefRefPtr<CefRequestImpl> request_;
+  scoped_refptr<HandlerProvider> handler_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(ResourceResponseWrapper);
+};
+
+}  // namespace
+
+std::unique_ptr<ResourceResponse> CreateResourceResponse(
+    const RequestId& request_id,
+    CefRefPtr<CefResourceHandler> handler) {
+  return std::make_unique<ResourceResponseWrapper>(request_id, handler);
+}
+
+}  // namespace net_service
diff --git a/src/libcef/browser/net_service/resource_handler_wrapper.h b/src/libcef/browser/net_service/resource_handler_wrapper.h
new file mode 100644
index 0000000..ce7da9b
--- /dev/null
+++ b/src/libcef/browser/net_service/resource_handler_wrapper.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_RESOURCE_HANDLER_WRAPPER_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_RESOURCE_HANDLER_WRAPPER_H_
+
+#include "include/cef_request.h"
+#include "include/cef_resource_handler.h"
+
+namespace net_service {
+
+class RequestId;
+class ResourceResponse;
+
+// Create a ResourceResponse that delegates to |handler|.
+// The resulting object should be passed to
+// InterceptedRequestHandler::ShouldInterceptRequestResultCallback.
+std::unique_ptr<ResourceResponse> CreateResourceResponse(
+    const RequestId& request_id,
+    CefRefPtr<CefResourceHandler> handler);
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_RESOURCE_HANDLER_WRAPPER_H_
diff --git a/src/libcef/browser/net_service/resource_request_handler_wrapper.cc b/src/libcef/browser/net_service/resource_request_handler_wrapper.cc
new file mode 100644
index 0000000..cb86313
--- /dev/null
+++ b/src/libcef/browser/net_service/resource_request_handler_wrapper.cc
@@ -0,0 +1,1311 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/net_service/resource_request_handler_wrapper.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/net_service/cookie_helper.h"
+#include "libcef/browser/net_service/proxy_url_loader_factory.h"
+#include "libcef/browser/net_service/resource_handler_wrapper.h"
+#include "libcef/browser/net_service/response_filter_wrapper.h"
+#include "libcef/browser/resource_context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/content_client.h"
+#include "libcef/common/net/scheme_registration.h"
+#include "libcef/common/net_service/net_service_util.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/response_impl.h"
+
+#include "chrome/common/chrome_features.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
+#include "ui/base/page_transition_types.h"
+#include "url/origin.h"
+
+namespace net_service {
+
+namespace {
+
+const int kLoadNoCookiesFlags =
+    net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+
+class RequestCallbackWrapper : public CefRequestCallback {
+ public:
+  using Callback = base::OnceCallback<void(bool /* allow */)>;
+  explicit RequestCallbackWrapper(Callback callback)
+      : callback_(std::move(callback)),
+        work_thread_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+
+  ~RequestCallbackWrapper() override {
+    if (!callback_.is_null()) {
+      // Make sure it executes on the correct thread.
+      work_thread_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback_), true));
+    }
+  }
+
+  void Continue(bool allow) override {
+    if (!work_thread_task_runner_->RunsTasksInCurrentSequence()) {
+      work_thread_task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(&RequestCallbackWrapper::Continue, this, allow));
+      return;
+    }
+    if (!callback_.is_null()) {
+      std::move(callback_).Run(allow);
+    }
+  }
+
+  void Cancel() override { Continue(false); }
+
+ private:
+  Callback callback_;
+
+  scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner_;
+
+  IMPLEMENT_REFCOUNTING(RequestCallbackWrapper);
+  DISALLOW_COPY_AND_ASSIGN(RequestCallbackWrapper);
+};
+
+std::string GetAcceptLanguageList(content::BrowserContext* browser_context,
+                                  CefRefPtr<CefBrowserHostImpl> browser) {
+  if (browser) {
+    const CefBrowserSettings& browser_settings = browser->settings();
+    if (browser_settings.accept_language_list.length > 0) {
+      return CefString(&browser_settings.accept_language_list);
+    }
+  }
+
+  // This defaults to the value from CefRequestContextSettings via
+  // browser_prefs::CreatePrefService().
+  auto prefs = Profile::FromBrowserContext(browser_context)->GetPrefs();
+  return prefs->GetString(language::prefs::kAcceptLanguages);
+}
+
+// Match the logic in chrome/browser/net/profile_network_context_service.cc.
+std::string ComputeAcceptLanguageFromPref(const std::string& language_pref) {
+  std::string accept_languages_str =
+      base::FeatureList::IsEnabled(features::kUseNewAcceptLanguageHeader)
+          ? net::HttpUtil::ExpandLanguageList(language_pref)
+          : language_pref;
+  return net::HttpUtil::GenerateAcceptLanguageHeader(accept_languages_str);
+}
+
+class InterceptedRequestHandlerWrapper : public InterceptedRequestHandler {
+ public:
+  struct RequestState {
+    RequestState() {}
+
+    void Reset(CefRefPtr<CefResourceRequestHandler> handler,
+               CefRefPtr<CefSchemeHandlerFactory> scheme_factory,
+               CefRefPtr<CefRequestImpl> request,
+               bool request_was_redirected,
+               CancelRequestCallback cancel_callback) {
+      handler_ = handler;
+      scheme_factory_ = scheme_factory;
+      cookie_filter_ = nullptr;
+      pending_request_ = request;
+      pending_response_ = nullptr;
+      request_was_redirected_ = request_was_redirected;
+      was_custom_handled_ = false;
+      cancel_callback_ = std::move(cancel_callback);
+    }
+
+    CefRefPtr<CefResourceRequestHandler> handler_;
+    CefRefPtr<CefSchemeHandlerFactory> scheme_factory_;
+    CefRefPtr<CefCookieAccessFilter> cookie_filter_;
+    CefRefPtr<CefRequestImpl> pending_request_;
+    CefRefPtr<CefResponseImpl> pending_response_;
+    bool request_was_redirected_ = false;
+    bool was_custom_handled_ = false;
+    CancelRequestCallback cancel_callback_;
+  };
+
+  struct PendingRequest {
+    PendingRequest(const RequestId& id,
+                   network::ResourceRequest* request,
+                   bool request_was_redirected,
+                   OnBeforeRequestResultCallback callback,
+                   CancelRequestCallback cancel_callback)
+        : id_(id),
+          request_(request),
+          request_was_redirected_(request_was_redirected),
+          callback_(std::move(callback)),
+          cancel_callback_(std::move(cancel_callback)) {}
+
+    ~PendingRequest() {
+      if (cancel_callback_) {
+        std::move(cancel_callback_).Run(net::ERR_ABORTED);
+      }
+    }
+
+    void Run(InterceptedRequestHandlerWrapper* self) {
+      self->OnBeforeRequest(id_, request_, request_was_redirected_,
+                            std::move(callback_), std::move(cancel_callback_));
+    }
+
+    const RequestId id_;
+    network::ResourceRequest* const request_;
+    const bool request_was_redirected_;
+    OnBeforeRequestResultCallback callback_;
+    CancelRequestCallback cancel_callback_;
+  };
+
+  // Observer to receive notification of CEF context or associated browser
+  // destruction. Only one of the *Destroyed() methods will be called.
+  class DestructionObserver : public CefBrowserHostImpl::Observer,
+                              public CefContext::Observer {
+   public:
+    explicit DestructionObserver(CefBrowserHostImpl* browser) {
+      if (browser) {
+        browser_info_ = browser->browser_info();
+        browser->AddObserver(this);
+      } else {
+        CefContext::Get()->AddObserver(this);
+      }
+    }
+
+    virtual ~DestructionObserver() {
+      CEF_REQUIRE_UIT();
+      if (!registered_)
+        return;
+
+      // Verify that the browser or context still exists before attempting to
+      // remove the observer.
+      if (browser_info_) {
+        auto browser = browser_info_->browser();
+        if (browser)
+          browser->RemoveObserver(this);
+      } else if (CefContext::Get()) {
+        // Network requests may be torn down during shutdown, so we can't check
+        // CONTEXT_STATE_VALID() here.
+        CefContext::Get()->RemoveObserver(this);
+      }
+    }
+
+    void SetWrapper(base::WeakPtr<InterceptedRequestHandlerWrapper> wrapper) {
+      CEF_REQUIRE_IOT();
+      wrapper_ = wrapper;
+    }
+
+    void OnBrowserDestroyed(CefBrowserHostImpl* browser) override {
+      CEF_REQUIRE_UIT();
+      browser->RemoveObserver(this);
+      registered_ = false;
+      browser_info_ = nullptr;
+      NotifyOnDestroyed();
+    }
+
+    void OnContextDestroyed() override {
+      CEF_REQUIRE_UIT();
+      CefContext::Get()->RemoveObserver(this);
+      registered_ = false;
+      NotifyOnDestroyed();
+    }
+
+   private:
+    void NotifyOnDestroyed() {
+      if (wrapper_.MaybeValid()) {
+        // This will be a no-op if the WeakPtr is invalid.
+        CEF_POST_TASK(
+            CEF_IOT,
+            base::BindOnce(&InterceptedRequestHandlerWrapper::OnDestroyed,
+                           wrapper_));
+      }
+    }
+
+    scoped_refptr<CefBrowserInfo> browser_info_;
+    bool registered_ = true;
+
+    base::WeakPtr<InterceptedRequestHandlerWrapper> wrapper_;
+    DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
+  };
+
+  // Holds state information for InterceptedRequestHandlerWrapper. State is
+  // initialized on the UI thread and later passed to the *Wrapper object on
+  // the IO thread.
+  struct InitState {
+    InitState() {}
+
+    ~InitState() {
+      if (destruction_observer_) {
+        if (initialized_) {
+          // Clear the reference added in
+          // InterceptedRequestHandlerWrapper::SetInitialized().
+          destruction_observer_->SetWrapper(nullptr);
+        }
+        DeleteDestructionObserver();
+      }
+    }
+
+    void Initialize(content::BrowserContext* browser_context,
+                    CefRefPtr<CefBrowserHostImpl> browser,
+                    CefRefPtr<CefFrame> frame,
+                    int render_process_id,
+                    int render_frame_id,
+                    int frame_tree_node_id,
+                    bool is_navigation,
+                    bool is_download,
+                    const url::Origin& request_initiator) {
+      CEF_REQUIRE_UIT();
+
+      browser_context_ = browser_context;
+      resource_context_ = static_cast<CefResourceContext*>(
+          browser_context->GetResourceContext());
+      DCHECK(resource_context_);
+
+      // We register to be notified of CEF context or browser destruction so
+      // that we can stop accepting new requests and cancel pending/in-progress
+      // requests in a timely manner (e.g. before we start asserting about
+      // leaked objects during CEF shutdown).
+      destruction_observer_.reset(new DestructionObserver(browser.get()));
+
+      if (browser) {
+        // These references will be released in OnDestroyed().
+        browser_ = browser;
+        frame_ = frame;
+      }
+
+      render_process_id_ = render_process_id;
+      render_frame_id_ = render_frame_id;
+      frame_tree_node_id_ = frame_tree_node_id;
+      is_navigation_ = is_navigation;
+      is_download_ = is_download;
+      request_initiator_ = request_initiator.Serialize();
+
+      // Default values for standard headers.
+      accept_language_ = ComputeAcceptLanguageFromPref(
+          GetAcceptLanguageList(browser_context, browser));
+      DCHECK(!accept_language_.empty());
+      user_agent_ = CefContentClient::Get()->browser()->GetUserAgent();
+      DCHECK(!user_agent_.empty());
+    }
+
+    void DeleteDestructionObserver() {
+      DCHECK(destruction_observer_);
+      CEF_POST_TASK(
+          CEF_UIT,
+          base::BindOnce(&InitState::DeleteDestructionObserverOnUIThread,
+                         std::move(destruction_observer_)));
+    }
+
+    static void DeleteDestructionObserverOnUIThread(
+        std::unique_ptr<DestructionObserver> observer) {}
+
+    // Only accessed on the UI thread.
+    content::BrowserContext* browser_context_ = nullptr;
+
+    bool initialized_ = false;
+
+    CefRefPtr<CefBrowserHostImpl> browser_;
+    CefRefPtr<CefFrame> frame_;
+    CefResourceContext* resource_context_ = nullptr;
+    int render_process_id_ = 0;
+    int render_frame_id_ = -1;
+    int frame_tree_node_id_ = -1;
+    bool is_navigation_ = true;
+    bool is_download_ = false;
+    CefString request_initiator_;
+
+    // Default values for standard headers.
+    std::string accept_language_;
+    std::string user_agent_;
+
+    // Used to receive destruction notification.
+    std::unique_ptr<DestructionObserver> destruction_observer_;
+  };
+
+  // Manages InterceptedRequestHandlerWrapper initialization. The *Wrapper
+  // object is owned by ProxyURLLoaderFactory and may be deleted before
+  // SetInitialized() is called.
+  struct InitHelper : base::RefCountedThreadSafe<InitHelper> {
+   public:
+    explicit InitHelper(InterceptedRequestHandlerWrapper* wrapper)
+        : wrapper_(wrapper) {}
+
+    void MaybeSetInitialized(std::unique_ptr<InitState> init_state) {
+      CEF_POST_TASK(CEF_IOT, base::BindOnce(&InitHelper::SetInitialized, this,
+                                            std::move(init_state)));
+    }
+
+    void Disconnect() {
+      base::AutoLock lock_scope(lock_);
+      wrapper_ = nullptr;
+    }
+
+   private:
+    void SetInitialized(std::unique_ptr<InitState> init_state) {
+      base::AutoLock lock_scope(lock_);
+      // May be nullptr if the InterceptedRequestHandlerWrapper has already
+      // been deleted.
+      if (!wrapper_)
+        return;
+      wrapper_->SetInitialized(std::move(init_state));
+      wrapper_ = nullptr;
+    }
+
+    base::Lock lock_;
+    InterceptedRequestHandlerWrapper* wrapper_;
+  };
+
+  InterceptedRequestHandlerWrapper()
+      : init_helper_(base::MakeRefCounted<InitHelper>(this)),
+        weak_ptr_factory_(this) {}
+
+  ~InterceptedRequestHandlerWrapper() override {
+    CEF_REQUIRE_IOT();
+
+    // There should be no in-progress requests during destruction.
+    DCHECK(request_map_.empty());
+
+    // Don't continue with initialization if we get deleted before
+    // SetInitialized is called asynchronously.
+    init_helper_->Disconnect();
+  }
+
+  scoped_refptr<InitHelper> init_helper() const { return init_helper_; }
+
+  void SetInitialized(std::unique_ptr<InitState> init_state) {
+    CEF_REQUIRE_IOT();
+    DCHECK(!init_state_);
+    init_state_ = std::move(init_state);
+
+    // Check that the CEF context or associated browser was not destroyed
+    // between the calls to Initialize and SetInitialized, in which case
+    // we won't get an OnDestroyed callback from DestructionObserver.
+    if (init_state_->browser_) {
+      if (!init_state_->browser_->browser_info()->browser()) {
+        OnDestroyed();
+        return;
+      }
+    } else if (!CONTEXT_STATE_VALID()) {
+      OnDestroyed();
+      return;
+    }
+
+    init_state_->initialized_ = true;
+    init_state_->destruction_observer_->SetWrapper(
+        weak_ptr_factory_.GetWeakPtr());
+
+    // Continue any pending requests.
+    if (!pending_requests_.empty()) {
+      for (const auto& request : pending_requests_)
+        request->Run(this);
+      pending_requests_.clear();
+    }
+  }
+
+  // InterceptedRequestHandler methods:
+  void OnBeforeRequest(const RequestId& id,
+                       network::ResourceRequest* request,
+                       bool request_was_redirected,
+                       OnBeforeRequestResultCallback callback,
+                       CancelRequestCallback cancel_callback) override {
+    CEF_REQUIRE_IOT();
+
+    if (shutting_down_) {
+      // Abort immediately.
+      std::move(cancel_callback).Run(net::ERR_ABORTED);
+      return;
+    }
+
+    if (!init_state_) {
+      // Queue requests until we're initialized.
+      pending_requests_.push_back(std::make_unique<PendingRequest>(
+          id, request, request_was_redirected, std::move(callback),
+          std::move(cancel_callback)));
+      return;
+    }
+
+    // State may already exist for restarted requests.
+    RequestState* state = GetOrCreateState(id);
+
+    // Add standard headers, if currently unspecified.
+    request->headers.SetHeaderIfMissing(
+        net::HttpRequestHeaders::kAcceptLanguage,
+        init_state_->accept_language_);
+    request->headers.SetHeaderIfMissing(net::HttpRequestHeaders::kUserAgent,
+                                        init_state_->user_agent_);
+
+    const bool is_external = IsExternalRequest(request);
+
+    // External requests will not have a default handler.
+    bool intercept_only = is_external;
+
+    CefRefPtr<CefRequestImpl> requestPtr;
+    CefRefPtr<CefResourceRequestHandler> handler =
+        GetHandler(id, request, &intercept_only, requestPtr);
+
+    CefRefPtr<CefSchemeHandlerFactory> scheme_factory =
+        init_state_->resource_context_->GetSchemeHandlerFactory(request->url);
+    if (scheme_factory && !requestPtr) {
+      requestPtr = MakeRequest(request, id.hash(), true);
+    }
+
+    // True if there's a possibility that the client might handle the request.
+    const bool maybe_intercept_request = handler || scheme_factory;
+    if (!maybe_intercept_request && requestPtr)
+      requestPtr = nullptr;
+
+    // May have a handler and/or scheme factory.
+    state->Reset(handler, scheme_factory, requestPtr, request_was_redirected,
+                 std::move(cancel_callback));
+
+    if (handler) {
+      state->cookie_filter_ = handler->GetCookieAccessFilter(
+          init_state_->browser_, init_state_->frame_, requestPtr.get());
+    }
+
+    auto exec_callback =
+        base::BindOnce(std::move(callback), maybe_intercept_request,
+                       is_external ? true : intercept_only);
+
+    if (!maybe_intercept_request) {
+      // Cookies will be handled by the NetworkService.
+      std::move(exec_callback).Run();
+      return;
+    }
+
+    MaybeLoadCookies(id, state, request, std::move(exec_callback));
+  }
+
+  void MaybeLoadCookies(const RequestId& id,
+                        RequestState* state,
+                        network::ResourceRequest* request,
+                        base::OnceClosure callback) {
+    CEF_REQUIRE_IOT();
+
+    // We need to load/save cookies ourselves for custom-handled requests, or
+    // if we're using a cookie filter.
+    auto allow_cookie_callback =
+        state->cookie_filter_
+            ? base::BindRepeating(
+                  &InterceptedRequestHandlerWrapper::AllowCookieLoad,
+                  weak_ptr_factory_.GetWeakPtr(), id)
+            : base::BindRepeating(
+                  &InterceptedRequestHandlerWrapper::AllowCookieAlways);
+    auto done_cookie_callback = base::BindOnce(
+        &InterceptedRequestHandlerWrapper::ContinueWithLoadedCookies,
+        weak_ptr_factory_.GetWeakPtr(), id, request, std::move(callback));
+    net_service::LoadCookies(init_state_->browser_context_, *request,
+                             allow_cookie_callback,
+                             std::move(done_cookie_callback));
+  }
+
+  static void AllowCookieAlways(const net::CanonicalCookie& cookie,
+                                bool* allow) {
+    *allow = true;
+  }
+
+  void AllowCookieLoad(const RequestId& id,
+                       const net::CanonicalCookie& cookie,
+                       bool* allow) {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled while the async callback was
+      // pending.
+      return;
+    }
+
+    DCHECK(state->cookie_filter_);
+
+    CefCookie cef_cookie;
+    if (net_service::MakeCefCookie(cookie, cef_cookie)) {
+      *allow = state->cookie_filter_->CanSendCookie(
+          init_state_->browser_, init_state_->frame_,
+          state->pending_request_.get(), cef_cookie);
+    }
+  }
+
+  void ContinueWithLoadedCookies(const RequestId& id,
+                                 network::ResourceRequest* request,
+                                 base::OnceClosure callback,
+                                 int total_count,
+                                 net::CookieList allowed_cookies) {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled while the async callback was
+      // pending.
+      return;
+    }
+
+    if (state->cookie_filter_) {
+      // Also add/save cookies ourselves for default-handled network requests
+      // so that we can filter them. This will be a no-op for custom-handled
+      // requests.
+      request->load_flags |= kLoadNoCookiesFlags;
+    }
+
+    if (!allowed_cookies.empty()) {
+      const std::string& cookie_line =
+          net::CanonicalCookie::BuildCookieLine(allowed_cookies);
+      request->headers.SetHeader(net::HttpRequestHeaders::kCookie, cookie_line);
+
+      state->pending_request_->SetReadOnly(false);
+      state->pending_request_->SetHeaderByName(net::HttpRequestHeaders::kCookie,
+                                               cookie_line, true);
+      state->pending_request_->SetReadOnly(true);
+    }
+
+    std::move(callback).Run();
+  }
+
+  void ShouldInterceptRequest(
+      const RequestId& id,
+      network::ResourceRequest* request,
+      ShouldInterceptRequestResultCallback callback) override {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled during destruction.
+      return;
+    }
+
+    // Must have a handler and/or scheme factory.
+    DCHECK(state->handler_ || state->scheme_factory_);
+    DCHECK(state->pending_request_);
+
+    if (state->handler_) {
+      // The client may modify |pending_request_| before executing the callback.
+      state->pending_request_->SetReadOnly(false);
+      state->pending_request_->SetTrackChanges(true,
+                                               true /* backup_on_change */);
+
+      CefRefPtr<RequestCallbackWrapper> callbackPtr =
+          new RequestCallbackWrapper(base::BindOnce(
+              &InterceptedRequestHandlerWrapper::ContinueShouldInterceptRequest,
+              weak_ptr_factory_.GetWeakPtr(), id, base::Unretained(request),
+              std::move(callback)));
+
+      cef_return_value_t retval = state->handler_->OnBeforeResourceLoad(
+          init_state_->browser_, init_state_->frame_,
+          state->pending_request_.get(), callbackPtr.get());
+      if (retval != RV_CONTINUE_ASYNC) {
+        // Continue or cancel the request immediately.
+        callbackPtr->Continue(retval == RV_CONTINUE);
+      }
+    } else {
+      // The scheme factory may choose to handle it.
+      ContinueShouldInterceptRequest(id, request, std::move(callback), true);
+    }
+  }
+
+  void ContinueShouldInterceptRequest(
+      const RequestId& id,
+      network::ResourceRequest* request,
+      ShouldInterceptRequestResultCallback callback,
+      bool allow) {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled while the async callback was
+      // pending.
+      return;
+    }
+
+    // Must have a handler and/or scheme factory.
+    DCHECK(state->handler_ || state->scheme_factory_);
+    DCHECK(state->pending_request_);
+
+    if (state->handler_) {
+      if (allow) {
+        // Apply any |requestPtr| changes to |request|.
+        state->pending_request_->Get(request, true /* changed_only */);
+      }
+
+      const bool redirect =
+          (state->pending_request_->GetChanges() & CefRequestImpl::kChangedUrl);
+      if (redirect) {
+        // Revert any changes for now. We'll get them back after the redirect.
+        state->pending_request_->RevertChanges();
+      }
+
+      state->pending_request_->SetReadOnly(true);
+      state->pending_request_->SetTrackChanges(false);
+
+      if (!allow) {
+        // Cancel the request.
+        if (state->cancel_callback_) {
+          std::move(state->cancel_callback_).Run(net::ERR_ABORTED);
+        }
+        return;
+      }
+
+      if (redirect) {
+        // Performing a redirect.
+        std::move(callback).Run(nullptr);
+        return;
+      }
+    }
+
+    CefRefPtr<CefResourceHandler> resource_handler;
+
+    if (state->handler_) {
+      // Does the client want to handle the request?
+      resource_handler = state->handler_->GetResourceHandler(
+          init_state_->browser_, init_state_->frame_,
+          state->pending_request_.get());
+    }
+    if (!resource_handler && state->scheme_factory_) {
+      // Does the scheme factory want to handle the request?
+      resource_handler = state->scheme_factory_->Create(
+          init_state_->browser_, init_state_->frame_, request->url.scheme(),
+          state->pending_request_.get());
+    }
+
+    std::unique_ptr<ResourceResponse> resource_response;
+    if (resource_handler) {
+      resource_response = CreateResourceResponse(id, resource_handler);
+      DCHECK(resource_response);
+      state->was_custom_handled_ = true;
+    } else {
+      // The request will be handled by the NetworkService. Remove the
+      // "Accept-Language" header here so that it can be re-added in
+      // URLRequestHttpJob::AddExtraHeaders with correct ordering applied.
+      request->headers.RemoveHeader(net::HttpRequestHeaders::kAcceptLanguage);
+    }
+
+    // Continue the request.
+    std::move(callback).Run(std::move(resource_response));
+  }
+
+  void ProcessResponseHeaders(const RequestId& id,
+                              const network::ResourceRequest& request,
+                              const GURL& redirect_url,
+                              net::HttpResponseHeaders* headers) override {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled during destruction.
+      return;
+    }
+
+    if (!state->handler_)
+      return;
+
+    if (!state->pending_response_)
+      state->pending_response_ = new CefResponseImpl();
+    else
+      state->pending_response_->SetReadOnly(false);
+
+    if (headers)
+      state->pending_response_->SetResponseHeaders(*headers);
+
+    state->pending_response_->SetReadOnly(true);
+  }
+
+  void OnRequestResponse(const RequestId& id,
+                         network::ResourceRequest* request,
+                         net::HttpResponseHeaders* headers,
+                         base::Optional<net::RedirectInfo> redirect_info,
+                         OnRequestResponseResultCallback callback) override {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled during destruction.
+      return;
+    }
+
+    if (state->cookie_filter_) {
+      // Remove the flags that were added in ContinueWithLoadedCookies.
+      request->load_flags &= ~kLoadNoCookiesFlags;
+    }
+
+    if (!state->handler_) {
+      // Cookies may come from a scheme handler.
+      MaybeSaveCookies(
+          id, state, request, headers,
+          base::BindOnce(
+              std::move(callback), ResponseMode::CONTINUE, nullptr,
+              redirect_info.has_value() ? redirect_info->new_url : GURL()));
+      return;
+    }
+
+    DCHECK(state->pending_request_);
+    DCHECK(state->pending_response_);
+
+    if (redirect_info.has_value()) {
+      HandleRedirect(id, state, request, headers, *redirect_info,
+                     std::move(callback));
+    } else {
+      HandleResponse(id, state, request, headers, std::move(callback));
+    }
+  }
+
+  void HandleRedirect(const RequestId& id,
+                      RequestState* state,
+                      network::ResourceRequest* request,
+                      net::HttpResponseHeaders* headers,
+                      const net::RedirectInfo& redirect_info,
+                      OnRequestResponseResultCallback callback) {
+    GURL new_url = redirect_info.new_url;
+    CefString newUrl = redirect_info.new_url.spec();
+    CefString oldUrl = newUrl;
+    bool url_changed = false;
+    state->handler_->OnResourceRedirect(
+        init_state_->browser_, init_state_->frame_,
+        state->pending_request_.get(), state->pending_response_.get(), newUrl);
+    if (newUrl != oldUrl) {
+      // Also support relative URLs.
+      const GURL& url = redirect_info.new_url.Resolve(newUrl.ToString());
+      if (url.is_valid()) {
+        url_changed = true;
+        new_url = url;
+      }
+    }
+
+    // Update the |pending_request_| object with the new info.
+    state->pending_request_->SetReadOnly(false);
+    state->pending_request_->Set(redirect_info);
+    if (url_changed) {
+      state->pending_request_->SetURL(new_url.spec());
+    }
+    state->pending_request_->SetReadOnly(true);
+
+    auto exec_callback = base::BindOnce(
+        std::move(callback), ResponseMode::CONTINUE, nullptr, new_url);
+
+    MaybeSaveCookies(id, state, request, headers, std::move(exec_callback));
+  }
+
+  void HandleResponse(const RequestId& id,
+                      RequestState* state,
+                      network::ResourceRequest* request,
+                      net::HttpResponseHeaders* headers,
+                      OnRequestResponseResultCallback callback) {
+    // The client may modify |pending_request_| in OnResourceResponse.
+    state->pending_request_->SetReadOnly(false);
+    state->pending_request_->SetTrackChanges(true, true /* backup_on_change */);
+
+    auto response_mode = ResponseMode::CONTINUE;
+    GURL new_url;
+
+    if (state->handler_->OnResourceResponse(
+            init_state_->browser_, init_state_->frame_,
+            state->pending_request_.get(), state->pending_response_.get())) {
+      // The request may have been modified.
+      const auto changes = state->pending_request_->GetChanges();
+      if (changes) {
+        state->pending_request_->Get(request, true /* changed_only */);
+
+        if (changes & CefRequestImpl::kChangedUrl) {
+          // Redirect to the new URL.
+          new_url = GURL(state->pending_request_->GetURL().ToString());
+        } else {
+          // Restart the request.
+          response_mode = ResponseMode::RESTART;
+        }
+      }
+    }
+
+    // Revert any changes for now. We'll get them back after the redirect or
+    // restart.
+    state->pending_request_->RevertChanges();
+
+    state->pending_request_->SetReadOnly(true);
+    state->pending_request_->SetTrackChanges(false);
+
+    auto exec_callback =
+        base::BindOnce(std::move(callback), response_mode, nullptr, new_url);
+
+    if (response_mode == ResponseMode::RESTART) {
+      // Get any cookies after the restart.
+      std::move(exec_callback).Run();
+      return;
+    }
+
+    MaybeSaveCookies(id, state, request, headers, std::move(exec_callback));
+  }
+
+  void MaybeSaveCookies(const RequestId& id,
+                        RequestState* state,
+                        network::ResourceRequest* request,
+                        net::HttpResponseHeaders* headers,
+                        base::OnceClosure callback) {
+    CEF_REQUIRE_IOT();
+
+    if (!state->cookie_filter_ && !state->was_custom_handled_) {
+      // The NetworkService saves the cookies for default-handled requests.
+      std::move(callback).Run();
+      return;
+    }
+
+    // We need to load/save cookies ourselves for custom-handled requests, or
+    // if we're using a cookie filter.
+    auto allow_cookie_callback =
+        state->cookie_filter_
+            ? base::BindRepeating(
+                  &InterceptedRequestHandlerWrapper::AllowCookieSave,
+                  weak_ptr_factory_.GetWeakPtr(), id)
+            : base::BindRepeating(
+                  &InterceptedRequestHandlerWrapper::AllowCookieAlways);
+    auto done_cookie_callback = base::BindOnce(
+        &InterceptedRequestHandlerWrapper::ContinueWithSavedCookies,
+        weak_ptr_factory_.GetWeakPtr(), id, std::move(callback));
+    net_service::SaveCookies(init_state_->browser_context_, *request, headers,
+                             allow_cookie_callback,
+                             std::move(done_cookie_callback));
+  }
+
+  void AllowCookieSave(const RequestId& id,
+                       const net::CanonicalCookie& cookie,
+                       bool* allow) {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled while the async callback was
+      // pending.
+      return;
+    }
+
+    DCHECK(state->cookie_filter_);
+
+    CefCookie cef_cookie;
+    if (net_service::MakeCefCookie(cookie, cef_cookie)) {
+      *allow = state->cookie_filter_->CanSaveCookie(
+          init_state_->browser_, init_state_->frame_,
+          state->pending_request_.get(), state->pending_response_.get(),
+          cef_cookie);
+    }
+  }
+
+  void ContinueWithSavedCookies(const RequestId& id,
+                                base::OnceClosure callback,
+                                int total_count,
+                                net::CookieList allowed_cookies) {
+    CEF_REQUIRE_IOT();
+    std::move(callback).Run();
+  }
+
+  mojo::ScopedDataPipeConsumerHandle OnFilterResponseBody(
+      const RequestId& id,
+      const network::ResourceRequest& request,
+      mojo::ScopedDataPipeConsumerHandle body) override {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled during destruction.
+      return body;
+    }
+
+    if (state->handler_) {
+      auto filter = state->handler_->GetResourceResponseFilter(
+          init_state_->browser_, init_state_->frame_,
+          state->pending_request_.get(), state->pending_response_.get());
+      if (filter) {
+        return CreateResponseFilterHandler(
+            filter, std::move(body),
+            base::BindOnce(&InterceptedRequestHandlerWrapper::OnFilterError,
+                           weak_ptr_factory_.GetWeakPtr(), id));
+      }
+    }
+
+    return body;
+  }
+
+  void OnFilterError(const RequestId& id) {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been canceled while the async callback was
+      // pending.
+      return;
+    }
+
+    if (state->cancel_callback_) {
+      std::move(state->cancel_callback_).Run(net::ERR_CONTENT_DECODING_FAILED);
+    }
+  }
+
+  void OnRequestComplete(
+      const RequestId& id,
+      const network::ResourceRequest& request,
+      const network::URLLoaderCompletionStatus& status) override {
+    CEF_REQUIRE_IOT();
+
+    RequestState* state = GetState(id);
+    if (!state) {
+      // The request may have been aborted during initialization or canceled
+      // during destruction. This method will always be called before a request
+      // is deleted, so if the request is currently pending also remove it from
+      // the list.
+      if (!pending_requests_.empty()) {
+        PendingRequests::iterator it = pending_requests_.begin();
+        for (; it != pending_requests_.end(); ++it) {
+          if ((*it)->id_ == id) {
+            pending_requests_.erase(it);
+            break;
+          }
+        }
+      }
+      return;
+    }
+
+    const bool is_external = IsExternalRequest(&request);
+
+    // Redirection of standard custom schemes is handled with a restart, so we
+    // get completion notifications for both the original (redirected) request
+    // and the final request. Don't report completion of the redirected request.
+    const bool ignore_result = is_external && request.url.IsStandard() &&
+                               status.error_code == net::ERR_ABORTED &&
+                               state->pending_response_.get() &&
+                               net::HttpResponseHeaders::IsRedirectResponseCode(
+                                   state->pending_response_->GetStatus());
+
+    if (state->handler_ && !ignore_result) {
+      DCHECK(state->pending_request_);
+
+      CallHandlerOnComplete(state, status);
+
+      if (status.error_code != 0 && is_external) {
+        bool allow_os_execution = false;
+        state->handler_->OnProtocolExecution(
+            init_state_->browser_, init_state_->frame_,
+            state->pending_request_.get(), allow_os_execution);
+        if (allow_os_execution) {
+          CefBrowserPlatformDelegate::HandleExternalProtocol(request.url);
+        }
+      }
+    }
+
+    RemoveState(id);
+  }
+
+ private:
+  void CallHandlerOnComplete(RequestState* state,
+                             const network::URLLoaderCompletionStatus& status) {
+    if (!state->handler_ || !state->pending_request_)
+      return;
+
+    // The request object may be currently flagged as writable in cases where we
+    // abort a request that is waiting on a pending callack.
+    if (!state->pending_request_->IsReadOnly()) {
+      state->pending_request_->SetReadOnly(true);
+    }
+
+    if (!state->pending_response_) {
+      // If the request failed there may not be a response object yet.
+      state->pending_response_ = new CefResponseImpl();
+    } else {
+      state->pending_response_->SetReadOnly(false);
+    }
+    state->pending_response_->SetError(
+        static_cast<cef_errorcode_t>(status.error_code));
+    state->pending_response_->SetReadOnly(true);
+
+    state->handler_->OnResourceLoadComplete(
+        init_state_->browser_, init_state_->frame_,
+        state->pending_request_.get(), state->pending_response_.get(),
+        status.error_code == 0 ? UR_SUCCESS : UR_FAILED,
+        status.encoded_body_length);
+  }
+
+  // Returns the handler, if any, that should be used for this request.
+  CefRefPtr<CefResourceRequestHandler> GetHandler(
+      const RequestId& id,
+      network::ResourceRequest* request,
+      bool* intercept_only,
+      CefRefPtr<CefRequestImpl>& requestPtr) const {
+    CefRefPtr<CefResourceRequestHandler> handler;
+
+    const int64 request_id = id.hash();
+
+    if (init_state_->browser_) {
+      // Maybe the browser's client wants to handle it?
+      CefRefPtr<CefClient> client =
+          init_state_->browser_->GetHost()->GetClient();
+      if (client) {
+        CefRefPtr<CefRequestHandler> request_handler =
+            client->GetRequestHandler();
+        if (request_handler) {
+          requestPtr = MakeRequest(request, request_id, true);
+
+          handler = request_handler->GetResourceRequestHandler(
+              init_state_->browser_, init_state_->frame_, requestPtr.get(),
+              init_state_->is_navigation_, init_state_->is_download_,
+              init_state_->request_initiator_, *intercept_only);
+        }
+      }
+    }
+
+    if (!handler) {
+      // Maybe the request context wants to handle it?
+      CefRefPtr<CefRequestContextHandler> context_handler =
+          init_state_->resource_context_->GetHandler(
+              init_state_->render_process_id_, request->render_frame_id,
+              init_state_->frame_tree_node_id_, false);
+      if (context_handler) {
+        if (!requestPtr)
+          requestPtr = MakeRequest(request, request_id, true);
+
+        handler = context_handler->GetResourceRequestHandler(
+            init_state_->browser_, init_state_->frame_, requestPtr.get(),
+            init_state_->is_navigation_, init_state_->is_download_,
+            init_state_->request_initiator_, *intercept_only);
+      }
+    }
+
+    return handler;
+  }
+
+  RequestState* GetOrCreateState(const RequestId& id) {
+    RequestState* state = GetState(id);
+    if (!state) {
+      state = new RequestState();
+      request_map_.insert(std::make_pair(id, base::WrapUnique(state)));
+    }
+    return state;
+  }
+
+  RequestState* GetState(const RequestId& id) const {
+    RequestMap::const_iterator it = request_map_.find(id);
+    if (it != request_map_.end())
+      return it->second.get();
+    return nullptr;
+  }
+
+  void RemoveState(const RequestId& id) {
+    RequestMap::iterator it = request_map_.find(id);
+    DCHECK(it != request_map_.end());
+    if (it != request_map_.end())
+      request_map_.erase(it);
+  }
+
+  // Stop accepting new requests and cancel pending/in-flight requests when the
+  // CEF context or associated browser is destroyed.
+  void OnDestroyed() {
+    CEF_REQUIRE_IOT();
+    DCHECK(init_state_);
+
+    init_state_->DeleteDestructionObserver();
+
+    // Stop accepting new requests.
+    shutting_down_ = true;
+
+    // Stop the delivery of pending callbacks.
+    weak_ptr_factory_.InvalidateWeakPtrs();
+
+    // Take ownership of any pending requests.
+    PendingRequests pending_requests;
+    pending_requests.swap(pending_requests_);
+
+    // Take ownership of any in-progress requests.
+    RequestMap request_map;
+    request_map.swap(request_map_);
+
+    // Notify handlers for in-progress requests.
+    for (const auto& pair : request_map) {
+      CallHandlerOnComplete(
+          pair.second.get(),
+          network::URLLoaderCompletionStatus(net::ERR_ABORTED));
+    }
+
+    if (init_state_->browser_) {
+      // Clear objects that reference the browser.
+      init_state_->browser_ = nullptr;
+      init_state_->frame_ = nullptr;
+    }
+
+    // Execute cancel callbacks and delete pending and in-progress requests.
+    // This may result in the request being torn down sooner, or it may be
+    // ignored if the request is already in the process of being torn down. When
+    // the last callback is executed it may result in |this| being deleted.
+    pending_requests.clear();
+
+    for (auto& pair : request_map) {
+      auto state = std::move(pair.second);
+      if (state->cancel_callback_) {
+        std::move(state->cancel_callback_).Run(net::ERR_ABORTED);
+      }
+    }
+  }
+
+  static CefRefPtr<CefRequestImpl> MakeRequest(
+      const network::ResourceRequest* request,
+      int64 request_id,
+      bool read_only) {
+    CefRefPtr<CefRequestImpl> requestPtr = new CefRequestImpl();
+    requestPtr->Set(request, request_id);
+    if (read_only)
+      requestPtr->SetReadOnly(true);
+    else
+      requestPtr->SetTrackChanges(true);
+    return requestPtr;
+  }
+
+  // Returns true if |request| cannot be handled internally.
+  static bool IsExternalRequest(const network::ResourceRequest* request) {
+    return !scheme::IsInternalHandledScheme(request->url.scheme());
+  }
+
+  scoped_refptr<InitHelper> init_helper_;
+  std::unique_ptr<InitState> init_state_;
+
+  bool shutting_down_ = false;
+
+  using RequestMap = std::map<RequestId, std::unique_ptr<RequestState>>;
+  RequestMap request_map_;
+
+  using PendingRequests = std::vector<std::unique_ptr<PendingRequest>>;
+  PendingRequests pending_requests_;
+
+  base::WeakPtrFactory<InterceptedRequestHandlerWrapper> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterceptedRequestHandlerWrapper);
+};
+
+void InitOnUIThread(
+    scoped_refptr<InterceptedRequestHandlerWrapper::InitHelper> init_helper,
+    content::WebContents::Getter web_contents_getter,
+    int frame_tree_node_id,
+    const network::ResourceRequest& request) {
+  CEF_REQUIRE_UIT();
+
+  // May return nullptr if the WebContents was destroyed while this callback was
+  // in-flight.
+  content::WebContents* web_contents = web_contents_getter.Run();
+  if (!web_contents) {
+    return;
+  }
+
+  content::BrowserContext* browser_context = web_contents->GetBrowserContext();
+  DCHECK(browser_context);
+
+  const int render_process_id =
+      web_contents->GetRenderViewHost()->GetProcess()->GetID();
+
+  content::RenderFrameHost* frame = nullptr;
+
+  if (request.render_frame_id >= 0) {
+    // TODO(network): Are these main frame checks equivalent?
+    if (request.is_main_frame ||
+        static_cast<blink::mojom::ResourceType>(request.resource_type) ==
+            blink::mojom::ResourceType::kMainFrame) {
+      frame = web_contents->GetMainFrame();
+      DCHECK(frame);
+    } else {
+      // May return null for newly created iframes.
+      frame = content::RenderFrameHost::FromID(render_process_id,
+                                               request.render_frame_id);
+      if (!frame && frame_tree_node_id >= 0) {
+        // May return null for frames in inner WebContents.
+        frame = web_contents->FindFrameByFrameTreeNodeId(frame_tree_node_id,
+                                                         render_process_id);
+      }
+      if (!frame) {
+        // Use the main frame for the CefBrowserHost.
+        frame = web_contents->GetMainFrame();
+        DCHECK(frame);
+      }
+    }
+  }
+
+  CefRefPtr<CefBrowserHostImpl> browserPtr;
+  CefRefPtr<CefFrame> framePtr;
+
+  // |frame| may be null for service worker requests.
+  if (frame) {
+    // May return nullptr for requests originating from guest views.
+    browserPtr = CefBrowserHostImpl::GetBrowserForHost(frame);
+    if (browserPtr) {
+      framePtr = browserPtr->GetFrameForHost(frame);
+      if (frame_tree_node_id < 0)
+        frame_tree_node_id = frame->GetFrameTreeNodeId();
+      DCHECK(framePtr);
+    }
+  }
+
+  const bool is_navigation = ui::PageTransitionIsNewNavigation(
+      static_cast<ui::PageTransition>(request.transition_type));
+  // TODO(navigation): Can we determine the |is_download| value?
+  const bool is_download = false;
+  url::Origin request_initiator;
+  if (request.request_initiator.has_value())
+    request_initiator = *request.request_initiator;
+
+  auto init_state =
+      std::make_unique<InterceptedRequestHandlerWrapper::InitState>();
+  init_state->Initialize(browser_context, browserPtr, framePtr,
+                         render_process_id, request.render_frame_id,
+                         frame_tree_node_id, is_navigation, is_download,
+                         request_initiator);
+
+  init_helper->MaybeSetInitialized(std::move(init_state));
+}
+
+}  // namespace
+
+std::unique_ptr<InterceptedRequestHandler> CreateInterceptedRequestHandler(
+    content::BrowserContext* browser_context,
+    content::RenderFrameHost* frame,
+    int render_process_id,
+    bool is_navigation,
+    bool is_download,
+    const url::Origin& request_initiator) {
+  CEF_REQUIRE_UIT();
+  CefRefPtr<CefBrowserHostImpl> browserPtr;
+  CefRefPtr<CefFrame> framePtr;
+  int render_frame_id = -1;
+  int frame_tree_node_id = -1;
+
+  // |frame| may be null for service worker requests.
+  if (frame) {
+    render_frame_id = frame->GetRoutingID();
+    frame_tree_node_id = frame->GetFrameTreeNodeId();
+
+    // May return nullptr for requests originating from guest views.
+    browserPtr = CefBrowserHostImpl::GetBrowserForHost(frame);
+    if (browserPtr) {
+      framePtr = browserPtr->GetFrameForHost(frame);
+      DCHECK(framePtr);
+    }
+  }
+
+  auto init_state =
+      std::make_unique<InterceptedRequestHandlerWrapper::InitState>();
+  init_state->Initialize(browser_context, browserPtr, framePtr,
+                         render_process_id, render_frame_id, frame_tree_node_id,
+                         is_navigation, is_download, request_initiator);
+
+  auto wrapper = std::make_unique<InterceptedRequestHandlerWrapper>();
+  wrapper->init_helper()->MaybeSetInitialized(std::move(init_state));
+
+  return wrapper;
+}
+
+std::unique_ptr<InterceptedRequestHandler> CreateInterceptedRequestHandler(
+    content::WebContents::Getter web_contents_getter,
+    int frame_tree_node_id,
+    const network::ResourceRequest& request) {
+  auto wrapper = std::make_unique<InterceptedRequestHandlerWrapper>();
+  CEF_POST_TASK(CEF_UIT, base::BindOnce(InitOnUIThread, wrapper->init_helper(),
+                                        web_contents_getter, frame_tree_node_id,
+                                        request));
+  return wrapper;
+}
+
+}  // namespace net_service
diff --git a/src/libcef/browser/net_service/resource_request_handler_wrapper.h b/src/libcef/browser/net_service/resource_request_handler_wrapper.h
new file mode 100644
index 0000000..4ed933a
--- /dev/null
+++ b/src/libcef/browser/net_service/resource_request_handler_wrapper.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_RESOURCE_REQUEST_HANDLER_WRAPPER_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_RESOURCE_REQUEST_HANDLER_WRAPPER_H_
+
+#include "content/public/browser/web_contents.h"
+
+namespace content {
+class BrowserContext;
+class RenderFrameHost;
+}  // namespace content
+
+namespace network {
+struct ResourceRequest;
+}
+
+namespace url {
+class Origin;
+}
+
+namespace net_service {
+
+class InterceptedRequestHandler;
+
+// Create an InterceptedRequestHandler that will delegate to a
+// CefResourceRequestHandler. The resulting object should be passed to
+// ProxyURLLoaderFactory::CreateProxy. Called on the UI thread only.
+std::unique_ptr<InterceptedRequestHandler> CreateInterceptedRequestHandler(
+    content::BrowserContext* browser_context,
+    content::RenderFrameHost* frame,
+    int render_process_id,
+    bool is_navigation,
+    bool is_download,
+    const url::Origin& request_initiator);
+
+// Create an InterceptedRequestHandler that will delegate to a
+// CefResourceRequestHandler. The resulting object should be passed to
+// ProxyURLLoaderFactory::CreateProxy. Called on the IO thread only.
+std::unique_ptr<InterceptedRequestHandler> CreateInterceptedRequestHandler(
+    content::WebContents::Getter web_contents_getter,
+    int frame_tree_node_id,
+    const network::ResourceRequest& request);
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_RESOURCE_REQUEST_HANDLER_WRAPPER_H_
diff --git a/src/libcef/browser/net_service/response_filter_wrapper.cc b/src/libcef/browser/net_service/response_filter_wrapper.cc
new file mode 100644
index 0000000..7702868
--- /dev/null
+++ b/src/libcef/browser/net_service/response_filter_wrapper.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/net_service/response_filter_wrapper.h"
+
+#include <queue>
+
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "mojo/public/cpp/system/string_data_source.h"
+
+namespace net_service {
+
+namespace {
+
+// Match the default |capacity_num_bytes| value from mojo::Core::CreateDataPipe.
+static const size_t kBufferSize = 64 * 1024;  // 64 Kbytes.
+static const size_t kMinBufferSpace = 1024;   // 1 Kbytes.
+
+class ResponseFilterWrapper {
+ public:
+  ResponseFilterWrapper(CefRefPtr<CefResponseFilter> filter,
+                        mojo::ScopedDataPipeConsumerHandle source_handle,
+                        base::OnceClosure error_callback);
+
+  // Creates and returns the output handle, or |source_handle| on failure.
+  bool CreateOutputHandle(mojo::ScopedDataPipeConsumerHandle* output_handle);
+
+ private:
+  void OnSourceReadable(MojoResult, const mojo::HandleSignalsState&);
+  void Filter(const char* data, size_t size);
+  void Write(std::unique_ptr<std::string> data);
+  void OnWriteComplete(std::unique_ptr<std::string>, MojoResult result);
+  void Drain(bool complete);
+  void MaybeSuccess();
+  void Cleanup(bool success);
+
+  CefRefPtr<CefResponseFilter> filter_;
+  mojo::ScopedDataPipeConsumerHandle source_handle_;
+  base::OnceClosure error_callback_;
+
+  std::unique_ptr<mojo::DataPipeProducer> forwarder_;
+  mojo::SimpleWatcher source_watcher_;
+
+  bool read_pending_ = false;
+  bool write_pending_ = false;
+  std::queue<std::unique_ptr<std::string>> pending_data_;
+  cef_response_filter_status_t last_status_ = RESPONSE_FILTER_NEED_MORE_DATA;
+
+  DISALLOW_COPY_AND_ASSIGN(ResponseFilterWrapper);
+};
+
+ResponseFilterWrapper::ResponseFilterWrapper(
+    CefRefPtr<CefResponseFilter> filter,
+    mojo::ScopedDataPipeConsumerHandle source_handle,
+    base::OnceClosure error_callback)
+    : filter_(filter),
+      source_handle_(std::move(source_handle)),
+      error_callback_(std::move(error_callback)),
+      source_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL) {}
+
+bool ResponseFilterWrapper::CreateOutputHandle(
+    mojo::ScopedDataPipeConsumerHandle* output_handle) {
+  if (!filter_->InitFilter()) {
+    *output_handle = std::move(source_handle_);
+    return false;
+  }
+
+  mojo::ScopedDataPipeProducerHandle forwarding_handle;
+  if (CreateDataPipe(nullptr, &forwarding_handle, output_handle) !=
+      MOJO_RESULT_OK) {
+    *output_handle = std::move(source_handle_);
+    return false;
+  }
+
+  forwarder_ =
+      std::make_unique<mojo::DataPipeProducer>(std::move(forwarding_handle));
+
+  source_watcher_.Watch(
+      source_handle_.get(),
+      MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+      base::BindRepeating(&ResponseFilterWrapper::OnSourceReadable,
+                          base::Unretained(this)));
+  source_watcher_.ArmOrNotify();
+  read_pending_ = true;
+
+  return true;
+}
+
+void ResponseFilterWrapper::OnSourceReadable(MojoResult,
+                                             const mojo::HandleSignalsState&) {
+  const void* buffer = nullptr;
+  uint32_t read_bytes = 0;
+  MojoResult result = source_handle_->BeginReadData(&buffer, &read_bytes,
+                                                    MOJO_READ_DATA_FLAG_NONE);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    source_watcher_.ArmOrNotify();
+    return;
+  }
+
+  if (result != MOJO_RESULT_OK) {
+    // Whole body has been read, or something went wrong.
+    Drain(result == MOJO_RESULT_FAILED_PRECONDITION);
+    return;
+  }
+
+  Filter(static_cast<const char*>(buffer), read_bytes);
+  if (last_status_ == RESPONSE_FILTER_ERROR) {
+    // Something went wrong.
+    Drain(false);
+    return;
+  }
+
+  source_handle_->EndReadData(read_bytes);
+  source_watcher_.ArmOrNotify();
+}
+
+void ResponseFilterWrapper::Filter(const char* data, size_t size) {
+  size_t data_in_size = size;
+  auto data_in_ptr = data_in_size > 0 ? data : nullptr;
+
+  size_t data_out_offset = 0;
+  std::unique_ptr<std::string> data_out;
+
+  while (true) {
+    size_t data_in_read = 0;
+
+    if (!data_out) {
+      // Start a new buffer. Should have no offset to begin with.
+      DCHECK_EQ(0U, data_out_offset);
+      data_out = std::make_unique<std::string>();
+      data_out->resize(kBufferSize);
+    }
+
+    auto data_out_ptr = data_out->data() + data_out_offset;
+    size_t data_out_size = kBufferSize - data_out_offset;
+    size_t data_out_written = 0;
+
+    last_status_ = filter_->Filter(
+        const_cast<char*>(data_in_ptr), data_in_size, data_in_read,
+        const_cast<char*>(data_out_ptr), data_out_size, data_out_written);
+    if (last_status_ == RESPONSE_FILTER_ERROR)
+      break;
+
+    // Validate the out values.
+    if (data_in_read > data_in_size) {
+      LOG(ERROR) << "potential buffer overflow; data_in_read > data_in_size";
+      last_status_ = RESPONSE_FILTER_ERROR;
+      break;
+    }
+    if (data_out_written > data_out_size) {
+      LOG(ERROR)
+          << "potential buffer overflow; data_out_written > data_out_size";
+      last_status_ = RESPONSE_FILTER_ERROR;
+      break;
+    }
+    if (data_out_written == 0 && data_in_read != data_in_size) {
+      LOG(ERROR) << "when no data is written all input must be consumed; "
+                    "data_out_written == 0 && data_in_read != data_in_size";
+      last_status_ = RESPONSE_FILTER_ERROR;
+      break;
+    }
+
+    if (data_out_written > 0) {
+      data_out_offset += data_out_written;
+      if (data_out_offset > kBufferSize - kMinBufferSpace) {
+        // The buffer is full or almost full. Write the data that we've
+        // received so far and start a new buffer.
+        data_out->resize(data_out_offset);
+        Write(std::move(data_out));
+        data_out_offset = 0;
+      }
+    }
+
+    if (data_in_read < data_in_size) {
+      // Keep going until the user reads all data.
+      data_in_ptr += data_in_read;
+      data_in_size -= data_in_read;
+      continue;
+    }
+
+    // At this point the user has read all data...
+    if (data_in_ptr) {
+      // Clear the input buffer.
+      data_in_read = data_in_size = 0;
+      data_in_ptr = nullptr;
+    }
+
+    if (data_out_written == data_out_size &&
+        last_status_ == RESPONSE_FILTER_NEED_MORE_DATA) {
+      // Output buffer was filled, but data is still pending.
+      continue;
+    }
+
+    if (data_out_offset > 0) {
+      // Write the last of the data that we've received.
+      data_out->resize(data_out_offset);
+      Write(std::move(data_out));
+    }
+
+    break;
+  }
+}
+
+void ResponseFilterWrapper::Write(std::unique_ptr<std::string> data) {
+  if (write_pending_) {
+    // Only one write at a time is supported.
+    pending_data_.push(std::move(data));
+    return;
+  }
+
+  write_pending_ = true;
+
+  base::StringPiece string_piece(*data);
+  forwarder_->Write(std::make_unique<mojo::StringDataSource>(
+                        string_piece, mojo::StringDataSource::AsyncWritingMode::
+                                          STRING_STAYS_VALID_UNTIL_COMPLETION),
+                    base::BindOnce(&ResponseFilterWrapper::OnWriteComplete,
+                                   base::Unretained(this), std::move(data)));
+}
+
+void ResponseFilterWrapper::OnWriteComplete(std::unique_ptr<std::string>,
+                                            MojoResult result) {
+  write_pending_ = false;
+
+  if (result != MOJO_RESULT_OK) {
+    // Something went wrong.
+    Cleanup(false);
+    return;
+  }
+
+  MaybeSuccess();
+}
+
+void ResponseFilterWrapper::Drain(bool complete) {
+  read_pending_ = false;
+  source_handle_.reset();
+  source_watcher_.Cancel();
+
+  if (!complete) {
+    // Something went wrong.
+    Cleanup(false);
+    return;
+  }
+
+  if (last_status_ == RESPONSE_FILTER_NEED_MORE_DATA) {
+    // Let the user write any remaining data.
+    Filter(nullptr, 0);
+    if (last_status_ != RESPONSE_FILTER_DONE) {
+      // Something went wrong.
+      Cleanup(false);
+      return;
+    }
+  }
+
+  MaybeSuccess();
+}
+
+void ResponseFilterWrapper::MaybeSuccess() {
+  if (!write_pending_ && !pending_data_.empty()) {
+    // Write the next data segment.
+    auto next = std::move(pending_data_.front());
+    pending_data_.pop();
+    Write(std::move(next));
+    return;
+  }
+
+  if (!read_pending_ && !write_pending_)
+    Cleanup(true);
+}
+
+void ResponseFilterWrapper::Cleanup(bool success) {
+  if (!success && error_callback_)
+    std::move(error_callback_).Run();
+  delete this;
+}
+
+}  // namespace
+
+mojo::ScopedDataPipeConsumerHandle CreateResponseFilterHandler(
+    CefRefPtr<CefResponseFilter> filter,
+    mojo::ScopedDataPipeConsumerHandle source_handle,
+    base::OnceClosure error_callback) {
+  // |filter_wrapper| will delete itself when filtering is complete if
+  // CreateOutputHandle returns true. Otherwise, it will return the
+  // original |source_handle|.
+  auto filter_wrapper = new ResponseFilterWrapper(
+      filter, std::move(source_handle), std::move(error_callback));
+  mojo::ScopedDataPipeConsumerHandle output_handle;
+  if (!filter_wrapper->CreateOutputHandle(&output_handle))
+    delete filter_wrapper;
+  return output_handle;
+}
+
+}  // namespace net_service
diff --git a/src/libcef/browser/net_service/response_filter_wrapper.h b/src/libcef/browser/net_service/response_filter_wrapper.h
new file mode 100644
index 0000000..241007f
--- /dev/null
+++ b/src/libcef/browser/net_service/response_filter_wrapper.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_RESPONSE_FILTER_WRAPPER_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_RESPONSE_FILTER_WRAPPER_H_
+
+#include "include/cef_response_filter.h"
+
+#include "mojo/public/cpp/system/data_pipe.h"
+
+namespace net_service {
+
+// Create a filter handler that will read from |source_handle| and pass the data
+// through |filter|. If filtering cannot be initialized then |source_handle|
+// will be returned, otherwise a new handle for retrieving the filtered output
+// will be returned. If filtering fails after initialization then
+// |error_callback| will be executed.
+mojo::ScopedDataPipeConsumerHandle CreateResponseFilterHandler(
+    CefRefPtr<CefResponseFilter> filter,
+    mojo::ScopedDataPipeConsumerHandle source_handle,
+    base::OnceClosure error_callback);
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_RESPONSE_FILTER_WRAPPER_H_
diff --git a/src/libcef/browser/net_service/stream_reader_url_loader.cc b/src/libcef/browser/net_service/stream_reader_url_loader.cc
new file mode 100644
index 0000000..362956a
--- /dev/null
+++ b/src/libcef/browser/net_service/stream_reader_url_loader.cc
@@ -0,0 +1,874 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "libcef/browser/net_service/stream_reader_url_loader.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/net_service/net_service_util.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
+#include "services/network/public/cpp/url_loader_completion_status.h"
+
+namespace net_service {
+
+namespace {
+
+using OnInputStreamOpenedCallback =
+    base::OnceCallback<void(std::unique_ptr<StreamReaderURLLoader::Delegate>,
+                            std::unique_ptr<InputStream>)>;
+
+// Helper for executing the OnInputStreamOpenedCallback.
+class OpenInputStreamWrapper
+    : public base::RefCountedThreadSafe<OpenInputStreamWrapper> {
+ public:
+  static base::OnceClosure Open(
+      std::unique_ptr<StreamReaderURLLoader::Delegate> delegate,
+      scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner,
+      const RequestId& request_id,
+      const network::ResourceRequest& request,
+      OnInputStreamOpenedCallback callback) WARN_UNUSED_RESULT {
+    scoped_refptr<OpenInputStreamWrapper> wrapper = new OpenInputStreamWrapper(
+        std::move(delegate), work_thread_task_runner,
+        base::ThreadTaskRunnerHandle::Get(), std::move(callback));
+    wrapper->Start(request_id, request);
+
+    return wrapper->GetCancelCallback();
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<OpenInputStreamWrapper>;
+
+  OpenInputStreamWrapper(
+      std::unique_ptr<StreamReaderURLLoader::Delegate> delegate,
+      scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> job_thread_task_runner,
+      OnInputStreamOpenedCallback callback)
+      : delegate_(std::move(delegate)),
+        work_thread_task_runner_(work_thread_task_runner),
+        job_thread_task_runner_(job_thread_task_runner),
+        callback_(std::move(callback)) {}
+  virtual ~OpenInputStreamWrapper() {}
+
+  void Start(const RequestId& request_id,
+             const network::ResourceRequest& request) {
+    work_thread_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&OpenInputStreamWrapper::OpenOnWorkThread,
+                       base::WrapRefCounted(this), request_id, request));
+  }
+
+  base::OnceClosure GetCancelCallback() {
+    return base::BindOnce(&OpenInputStreamWrapper::CancelOnJobThread,
+                          base::WrapRefCounted(this));
+  }
+
+  void CancelOnJobThread() {
+    DCHECK(job_thread_task_runner_->RunsTasksInCurrentSequence());
+    if (callback_.is_null())
+      return;
+
+    callback_.Reset();
+
+    work_thread_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&OpenInputStreamWrapper::CancelOnWorkThread,
+                                  base::WrapRefCounted(this)));
+  }
+
+  void CancelOnWorkThread() {
+    DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+    if (is_canceled_)
+      return;
+    is_canceled_ = true;
+    OnCallback(nullptr);
+  }
+
+  void OpenOnWorkThread(const RequestId& request_id,
+                        const network::ResourceRequest& request) {
+    DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+    if (is_canceled_)
+      return;
+
+    // |delegate_| will remain valid until OnCallback() is executed on
+    // |job_thread_task_runner_|.
+    if (!delegate_->OpenInputStream(
+            request_id, request,
+            base::BindOnce(&OpenInputStreamWrapper::OnCallback,
+                           base::WrapRefCounted(this)))) {
+      is_canceled_ = true;
+      OnCallback(nullptr);
+    }
+  }
+
+  void OnCallback(std::unique_ptr<InputStream> input_stream) {
+    if (!job_thread_task_runner_->RunsTasksInCurrentSequence()) {
+      job_thread_task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(&OpenInputStreamWrapper::OnCallback,
+                         base::WrapRefCounted(this), std::move(input_stream)));
+      return;
+    }
+
+    // May be null if CancelOnJobThread() was called on
+    // |job_thread_task_runner_| while OpenOnWorkThread() was pending on
+    // |work_thread_task_runner_|.
+    if (callback_.is_null()) {
+      delegate_.reset();
+      return;
+    }
+
+    std::move(callback_).Run(std::move(delegate_), std::move(input_stream));
+  }
+
+  std::unique_ptr<StreamReaderURLLoader::Delegate> delegate_;
+
+  scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> job_thread_task_runner_;
+
+  // Only accessed on |job_thread_task_runner_|.
+  OnInputStreamOpenedCallback callback_;
+
+  // Only accessed on |work_thread_task_runner_|.
+  bool is_canceled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(OpenInputStreamWrapper);
+};
+
+}  // namespace
+
+//==============================
+// InputStreamReader
+//=============================
+
+// Class responsible for reading from the InputStream.
+class InputStreamReader : public base::RefCountedThreadSafe<InputStreamReader> {
+ public:
+  // The constructor is called on the IO thread, not on the worker thread.
+  // Callbacks will be executed on the IO thread.
+  InputStreamReader(
+      std::unique_ptr<InputStream> stream,
+      scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner);
+
+  // Skip |skip_bytes| number of bytes from |stream_|. |callback| will be
+  // executed asynchronously on the IO thread. A negative value passed to
+  // |callback| will indicate an error code, a positive value will indicate the
+  // number of bytes skipped.
+  void Skip(int64_t skip_bytes, InputStream::SkipCallback callback);
+
+  // Read up to |dest_size| bytes from |stream_| into |dest|. |callback| will be
+  // executed asynchronously on the IO thread. A negative value passed to
+  // |callback| will indicate an error code, a positive value will indicate the
+  // number of bytes read.
+  void Read(scoped_refptr<net::IOBuffer> dest,
+            int dest_size,
+            InputStream::ReadCallback callback);
+
+ private:
+  friend class base::RefCountedThreadSafe<InputStreamReader>;
+  virtual ~InputStreamReader();
+
+  void SkipOnWorkThread(int64_t skip_bytes, InputStream::SkipCallback callback);
+  void ReadOnWorkThread(scoped_refptr<net::IOBuffer> buffer,
+                        int buffer_size,
+                        InputStream::ReadCallback callback);
+
+  void SkipToRequestedRange();
+
+  static void ContinueSkipCallback(
+      scoped_refptr<InputStreamReader> stream,
+      scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner,
+      int callback_id,
+      int64_t bytes_skipped);
+  static void ContinueReadCallback(
+      scoped_refptr<InputStreamReader> stream,
+      scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner,
+      int callback_id,
+      int bytes_read);
+
+  void ContinueSkipCallbackOnWorkThread(int callback_id, int64_t bytes_skipped);
+  void ContinueReadCallbackOnWorkThread(int callback_id, int bytes_read);
+
+  void RunSkipCallback(int64_t bytes_skipped);
+  void RunReadCallback(int bytes_read);
+
+  static void RunSkipCallbackOnJobThread(
+      int64_t bytes_skipped,
+      InputStream::SkipCallback skip_callback);
+  static void RunReadCallbackOnJobThread(
+      int bytes_read,
+      InputStream::ReadCallback read_callback);
+
+  std::unique_ptr<InputStream> stream_;
+
+  // All InputStream methods are called this task runner.
+  scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner_;
+
+  // All callbacks are executed on this task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> job_thread_task_runner_;
+
+  // The below members are only accessed on the work thread.
+  int64_t bytes_skipped_;
+  int64_t bytes_to_skip_;
+  InputStream::SkipCallback pending_skip_callback_;
+
+  scoped_refptr<net::IOBuffer> buffer_;
+  InputStream::ReadCallback pending_read_callback_;
+
+  int pending_callback_id_ = -1;
+
+  int next_callback_id_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(InputStreamReader);
+};
+
+InputStreamReader::InputStreamReader(
+    std::unique_ptr<InputStream> stream,
+    scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner)
+    : stream_(std::move(stream)),
+      work_thread_task_runner_(work_thread_task_runner),
+      job_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+  CEF_REQUIRE_IOT();
+  DCHECK(stream_);
+  DCHECK(work_thread_task_runner_);
+}
+
+InputStreamReader::~InputStreamReader() {}
+
+void InputStreamReader::Skip(int64_t skip_bytes,
+                             InputStream::SkipCallback callback) {
+  work_thread_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&InputStreamReader::SkipOnWorkThread,
+                                base::WrapRefCounted(this), skip_bytes,
+                                std::move(callback)));
+}
+
+void InputStreamReader::Read(scoped_refptr<net::IOBuffer> dest,
+                             int dest_size,
+                             InputStream::ReadCallback callback) {
+  work_thread_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&InputStreamReader::ReadOnWorkThread,
+                                base::WrapRefCounted(this), dest, dest_size,
+                                std::move(callback)));
+}
+
+void InputStreamReader::SkipOnWorkThread(int64_t skip_bytes,
+                                         InputStream::SkipCallback callback) {
+  DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  // No callback should currently be pending.
+  DCHECK_EQ(pending_callback_id_, -1);
+  DCHECK(pending_skip_callback_.is_null());
+
+  pending_skip_callback_ = std::move(callback);
+
+  if (skip_bytes <= 0) {
+    RunSkipCallback(0);
+    return;
+  }
+
+  bytes_skipped_ = bytes_to_skip_ = skip_bytes;
+  SkipToRequestedRange();
+}
+
+void InputStreamReader::ReadOnWorkThread(scoped_refptr<net::IOBuffer> dest,
+                                         int dest_size,
+                                         InputStream::ReadCallback callback) {
+  DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  // No callback should currently be pending.
+  DCHECK_EQ(pending_callback_id_, -1);
+  DCHECK(pending_read_callback_.is_null());
+
+  pending_read_callback_ = std::move(callback);
+
+  if (!dest_size) {
+    RunReadCallback(0);
+    return;
+  }
+
+  DCHECK_GT(dest_size, 0);
+
+  buffer_ = dest;
+  pending_callback_id_ = ++next_callback_id_;
+
+  int bytes_read = 0;
+  bool result = stream_->Read(
+      buffer_.get(), dest_size, &bytes_read,
+      base::BindOnce(&InputStreamReader::ContinueReadCallback,
+                     base::WrapRefCounted(this), work_thread_task_runner_,
+                     pending_callback_id_));
+
+  // Check if the callback will execute asynchronously.
+  if (result && bytes_read == 0)
+    return;
+
+  RunReadCallback(result || bytes_read <= 0 ? bytes_read : net::ERR_FAILED);
+}
+
+void InputStreamReader::SkipToRequestedRange() {
+  DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  // Skip to the start of the requested data. This has to be done in a loop
+  // because the underlying InputStream is not guaranteed to skip the requested
+  // number of bytes.
+  do {
+    pending_callback_id_ = ++next_callback_id_;
+
+    int64_t skipped = 0;
+    bool result = stream_->Skip(
+        bytes_to_skip_, &skipped,
+        base::BindOnce(&InputStreamReader::ContinueSkipCallback,
+                       base::WrapRefCounted(this), work_thread_task_runner_,
+                       pending_callback_id_));
+
+    // Check if the callback will execute asynchronously.
+    if (result && skipped == 0)
+      return;
+
+    if (!result || skipped <= 0) {
+      RunSkipCallback(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+      return;
+    }
+    DCHECK_LE(skipped, bytes_to_skip_);
+
+    bytes_to_skip_ -= skipped;
+  } while (bytes_to_skip_ > 0);
+
+  // All done, the requested number of bytes were skipped.
+  RunSkipCallback(bytes_skipped_);
+}
+
+// static
+void InputStreamReader::ContinueSkipCallback(
+    scoped_refptr<InputStreamReader> stream,
+    scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner,
+    int callback_id,
+    int64_t bytes_skipped) {
+  // Always execute asynchronously.
+  work_thread_task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&InputStreamReader::ContinueSkipCallbackOnWorkThread,
+                     stream, callback_id, bytes_skipped));
+}
+
+// static
+void InputStreamReader::ContinueReadCallback(
+    scoped_refptr<InputStreamReader> stream,
+    scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner,
+    int callback_id,
+    int bytes_read) {
+  // Always execute asynchronously.
+  work_thread_task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&InputStreamReader::ContinueReadCallbackOnWorkThread,
+                     stream, callback_id, bytes_read));
+}
+
+void InputStreamReader::ContinueSkipCallbackOnWorkThread(
+    int callback_id,
+    int64_t bytes_skipped) {
+  DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  // Check for out of order callbacks.
+  if (pending_callback_id_ != callback_id)
+    return;
+
+  DCHECK_LE(bytes_skipped, bytes_to_skip_);
+
+  if (bytes_to_skip_ > 0 && bytes_skipped > 0)
+    bytes_to_skip_ -= bytes_skipped;
+
+  if (bytes_skipped <= 0) {
+    RunSkipCallback(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+  } else if (bytes_to_skip_ > 0) {
+    // Continue execution asynchronously.
+    work_thread_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&InputStreamReader::SkipToRequestedRange, this));
+  } else {
+    // All done, the requested number of bytes were skipped.
+    RunSkipCallback(bytes_skipped_);
+  }
+}
+
+void InputStreamReader::ContinueReadCallbackOnWorkThread(int callback_id,
+                                                         int bytes_read) {
+  DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  // Check for out of order callbacks.
+  if (pending_callback_id_ != callback_id)
+    return;
+
+  RunReadCallback(bytes_read);
+}
+
+void InputStreamReader::RunSkipCallback(int64_t bytes_skipped) {
+  DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  DCHECK(!pending_skip_callback_.is_null());
+  job_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(InputStreamReader::RunSkipCallbackOnJobThread,
+                     bytes_skipped, std::move(pending_skip_callback_)));
+
+  // Reset callback state.
+  pending_callback_id_ = -1;
+  bytes_skipped_ = bytes_to_skip_ = -1;
+}
+
+void InputStreamReader::RunReadCallback(int bytes_read) {
+  DCHECK(work_thread_task_runner_->RunsTasksInCurrentSequence());
+
+  DCHECK(!pending_read_callback_.is_null());
+  job_thread_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(InputStreamReader::RunReadCallbackOnJobThread,
+                                bytes_read, std::move(pending_read_callback_)));
+
+  // Reset callback state.
+  pending_callback_id_ = -1;
+  buffer_ = nullptr;
+}
+
+// static
+void InputStreamReader::RunSkipCallbackOnJobThread(
+    int64_t bytes_skipped,
+    InputStream::SkipCallback skip_callback) {
+  std::move(skip_callback).Run(bytes_skipped);
+}
+
+// static
+void InputStreamReader::RunReadCallbackOnJobThread(
+    int bytes_read,
+    InputStream::ReadCallback read_callback) {
+  std::move(read_callback).Run(bytes_read);
+}
+
+//==============================
+// RequestId
+//==============================
+
+std::string RequestId::ToString() const {
+  return base::StringPrintf("RequestId(%u, %u)", request_id_, routing_id_);
+}
+
+std::string RequestId::ToString(base::StringPiece debug_label) const {
+  return base::StringPrintf("RequestId[%s](%u, %u)",
+                            debug_label.as_string().c_str(), request_id_,
+                            routing_id_);
+}
+
+std::ostream& operator<<(std::ostream& out, const RequestId& request_id) {
+  return out << request_id.ToString();
+}
+
+//==============================
+// StreamReaderURLLoader
+//=============================
+
+StreamReaderURLLoader::StreamReaderURLLoader(
+    const RequestId& request_id,
+    const network::ResourceRequest& request,
+    network::mojom::URLLoaderClientPtr client,
+    network::mojom::TrustedHeaderClientPtr header_client,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+    std::unique_ptr<Delegate> response_delegate)
+    : request_id_(request_id),
+      request_(request),
+      client_(std::move(client)),
+      header_client_(std::move(header_client)),
+      traffic_annotation_(traffic_annotation),
+      response_delegate_(std::move(response_delegate)),
+      writable_handle_watcher_(FROM_HERE,
+                               mojo::SimpleWatcher::ArmingPolicy::MANUAL,
+                               base::SequencedTaskRunnerHandle::Get()),
+      weak_factory_(this) {
+  DCHECK(response_delegate_);
+  // If there is a client error, clean up the request.
+  client_.set_connection_error_handler(
+      base::BindOnce(&StreamReaderURLLoader::RequestComplete,
+                     weak_factory_.GetWeakPtr(), net::ERR_ABORTED));
+
+  // All InputStream work will be performed on this task runner.
+  stream_work_task_runner_ =
+      base::CreateSequencedTaskRunner({base::ThreadPool(), base::MayBlock()});
+}
+
+StreamReaderURLLoader::~StreamReaderURLLoader() {
+  if (open_cancel_callback_) {
+    // Release the Delegate held by OpenInputStreamWrapper.
+    std::move(open_cancel_callback_).Run();
+  }
+}
+
+void StreamReaderURLLoader::Start() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!ParseRange(request_.headers)) {
+    RequestComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+    return;
+  }
+
+  if (header_client_.is_bound()) {
+    header_client_->OnBeforeSendHeaders(
+        request_.headers,
+        base::BindOnce(&StreamReaderURLLoader::ContinueWithRequestHeaders,
+                       weak_factory_.GetWeakPtr()));
+  } else {
+    ContinueWithRequestHeaders(net::OK, base::nullopt);
+  }
+}
+
+void StreamReaderURLLoader::ContinueWithRequestHeaders(
+    int32_t result,
+    const base::Optional<net::HttpRequestHeaders>& headers) {
+  if (result != net::OK) {
+    RequestComplete(result);
+    return;
+  }
+
+  if (headers) {
+    DCHECK(header_client_.is_bound());
+    request_.headers = *headers;
+  }
+
+  open_cancel_callback_ = OpenInputStreamWrapper::Open(
+      // This is intentional - the loader could be deleted while
+      // the callback is executing on the background thread. The
+      // delegate will be "returned" to the loader once the
+      // InputStream open attempt is completed.
+      std::move(response_delegate_), stream_work_task_runner_, request_id_,
+      request_,
+      base::BindOnce(&StreamReaderURLLoader::OnInputStreamOpened,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void StreamReaderURLLoader::FollowRedirect(
+    const std::vector<std::string>& removed_headers,
+    const net::HttpRequestHeaders& modified_headers,
+    const base::Optional<GURL>& new_url) {
+  NOTREACHED();
+}
+
+void StreamReaderURLLoader::SetPriority(net::RequestPriority priority,
+                                        int intra_priority_value) {}
+
+void StreamReaderURLLoader::PauseReadingBodyFromNet() {}
+
+void StreamReaderURLLoader::ResumeReadingBodyFromNet() {}
+
+void StreamReaderURLLoader::OnInputStreamOpened(
+    std::unique_ptr<StreamReaderURLLoader::Delegate> returned_delegate,
+    std::unique_ptr<InputStream> input_stream) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(returned_delegate);
+  response_delegate_ = std::move(returned_delegate);
+  open_cancel_callback_.Reset();
+
+  if (!input_stream) {
+    bool restarted = false;
+    response_delegate_->OnInputStreamOpenFailed(request_id_, &restarted);
+    if (restarted) {
+      // The request has been restarted with a new loader.
+      // |this| will be deleted.
+      CleanUp();
+    } else {
+      HeadersComplete(net::HTTP_NOT_FOUND, -1);
+    }
+    return;
+  }
+
+  input_stream_reader_ = base::MakeRefCounted<InputStreamReader>(
+      std::move(input_stream), stream_work_task_runner_);
+
+  if (!byte_range_valid()) {
+    OnReaderSkipCompleted(0);
+  } else {
+    input_stream_reader_->Skip(
+        byte_range_.first_byte_position(),
+        base::BindOnce(&StreamReaderURLLoader::OnReaderSkipCompleted,
+                       weak_factory_.GetWeakPtr()));
+  }
+}
+
+void StreamReaderURLLoader::OnReaderSkipCompleted(int64_t bytes_skipped) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!byte_range_valid()) {
+    // Expected content length is unspecified.
+    HeadersComplete(net::HTTP_OK, -1);
+  } else if (bytes_skipped == byte_range_.first_byte_position()) {
+    // We skipped the expected number of bytes.
+    int64_t expected_content_length = -1;
+    if (byte_range_.HasLastBytePosition()) {
+      expected_content_length = byte_range_.last_byte_position() -
+                                byte_range_.first_byte_position() + 1;
+      DCHECK_GE(expected_content_length, 0);
+    }
+    HeadersComplete(net::HTTP_OK, expected_content_length);
+  } else {
+    RequestComplete(bytes_skipped < 0 ? bytes_skipped : net::ERR_FAILED);
+  }
+}
+
+void StreamReaderURLLoader::HeadersComplete(int orig_status_code,
+                                            int64_t expected_content_length) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  int status_code = orig_status_code;
+  std::string status_text =
+      net::GetHttpReasonPhrase(static_cast<net::HttpStatusCode>(status_code));
+  std::string mime_type, charset;
+  int64_t content_length = expected_content_length;
+  ResourceResponse::HeaderMap extra_headers;
+  response_delegate_->GetResponseHeaders(request_id_, &status_code,
+                                         &status_text, &mime_type, &charset,
+                                         &content_length, &extra_headers);
+
+  if (status_code < 0) {
+    // Early exit if the handler reported an error.
+    RequestComplete(status_code);
+    return;
+  }
+
+  auto pending_response = network::mojom::URLResponseHead::New();
+  pending_response->request_start = base::TimeTicks::Now();
+  pending_response->response_start = base::TimeTicks::Now();
+
+  auto headers = MakeResponseHeaders(
+      status_code, status_text, mime_type, charset, content_length,
+      extra_headers, false /* allow_existing_header_override */);
+  pending_response->headers = headers;
+
+  if (content_length >= 0)
+    pending_response->content_length = content_length;
+
+  if (!mime_type.empty()) {
+    pending_response->mime_type = mime_type;
+    if (!charset.empty())
+      pending_response->charset = charset;
+  }
+
+  if (header_client_.is_bound()) {
+    header_client_->OnHeadersReceived(
+        headers->raw_headers(), net::IPEndPoint(),
+        base::BindOnce(&StreamReaderURLLoader::ContinueWithResponseHeaders,
+                       weak_factory_.GetWeakPtr(),
+                       std::move(pending_response)));
+  } else {
+    ContinueWithResponseHeaders(std::move(pending_response), net::OK,
+                                base::nullopt, base::nullopt);
+  }
+}
+
+void StreamReaderURLLoader::ContinueWithResponseHeaders(
+    network::mojom::URLResponseHeadPtr pending_response,
+    int32_t result,
+    const base::Optional<std::string>& headers,
+    const base::Optional<GURL>& redirect_url) {
+  if (result != net::OK) {
+    RequestComplete(result);
+    return;
+  }
+
+  if (headers) {
+    DCHECK(header_client_.is_bound());
+    pending_response->headers =
+        base::MakeRefCounted<net::HttpResponseHeaders>(*headers);
+  }
+
+  auto pending_headers = pending_response->headers;
+
+  // What the length would be if we sent headers over the network. Used to
+  // calculate data length.
+  header_length_ = pending_headers->raw_headers().length();
+
+  DCHECK(client_.is_bound());
+
+  std::string location;
+  const auto has_redirect_url = redirect_url && !redirect_url->is_empty();
+  if (has_redirect_url || pending_headers->IsRedirect(&location)) {
+    pending_response->encoded_data_length = header_length_;
+    pending_response->content_length = pending_response->encoded_body_length =
+        0;
+    const GURL new_location =
+        has_redirect_url ? *redirect_url : request_.url.Resolve(location);
+    client_->OnReceiveRedirect(
+        MakeRedirectInfo(request_, pending_headers.get(), new_location,
+                         pending_headers->response_code()),
+        std::move(pending_response));
+    // The client will restart the request with a new loader.
+    // |this| will be deleted.
+    CleanUp();
+  } else {
+    client_->OnReceiveResponse(std::move(pending_response));
+  }
+}
+
+void StreamReaderURLLoader::ContinueResponse(bool was_redirected) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (was_redirected) {
+    // Special case where we allow the client to perform the redirect.
+    // The client will restart the request with a new loader.
+    // |this| will be deleted.
+    CleanUp();
+  } else {
+    SendBody();
+  }
+}
+
+void StreamReaderURLLoader::SendBody() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  if (CreateDataPipe(nullptr /*options*/, &producer_handle_,
+                     &consumer_handle) != MOJO_RESULT_OK) {
+    RequestComplete(net::ERR_FAILED);
+    return;
+  }
+  writable_handle_watcher_.Watch(
+      producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+      base::BindRepeating(&StreamReaderURLLoader::OnDataPipeWritable,
+                          base::Unretained(this)));
+  client_->OnStartLoadingResponseBody(std::move(consumer_handle));
+
+  ReadMore();
+}
+
+void StreamReaderURLLoader::ReadMore() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!pending_buffer_.get());
+
+  uint32_t num_bytes;
+  MojoResult mojo_result = network::NetToMojoPendingBuffer::BeginWrite(
+      &producer_handle_, &pending_buffer_, &num_bytes);
+  if (mojo_result == MOJO_RESULT_SHOULD_WAIT) {
+    // The pipe is full. We need to wait for it to have more space.
+    writable_handle_watcher_.ArmOrNotify();
+    return;
+  } else if (mojo_result == MOJO_RESULT_FAILED_PRECONDITION) {
+    // The data pipe consumer handle has been closed.
+    RequestComplete(net::ERR_ABORTED);
+    return;
+  } else if (mojo_result != MOJO_RESULT_OK) {
+    // The body stream is in a bad state. Bail out.
+    RequestComplete(net::ERR_UNEXPECTED);
+    return;
+  }
+  scoped_refptr<net::IOBuffer> buffer(
+      new network::NetToMojoIOBuffer(pending_buffer_.get()));
+
+  if (!input_stream_reader_.get()) {
+    // This will happen if opening the InputStream fails in which case the
+    // error is communicated by setting the HTTP response status header rather
+    // than failing the request during the header fetch phase.
+    OnReaderReadCompleted(0);
+    return;
+  }
+
+  input_stream_reader_->Read(
+      buffer, base::checked_cast<int>(num_bytes),
+      base::BindOnce(&StreamReaderURLLoader::OnReaderReadCompleted,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void StreamReaderURLLoader::OnDataPipeWritable(MojoResult result) {
+  if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+    RequestComplete(net::ERR_ABORTED);
+    return;
+  }
+  DCHECK_EQ(result, MOJO_RESULT_OK) << result;
+
+  ReadMore();
+}
+
+void StreamReaderURLLoader::OnReaderReadCompleted(int bytes_read) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DCHECK(pending_buffer_);
+  if (bytes_read < 0) {
+    // Error case.
+    RequestComplete(bytes_read);
+    return;
+  }
+  if (bytes_read == 0) {
+    // Eof, read completed.
+    pending_buffer_->Complete(0);
+    RequestComplete(net::OK);
+    return;
+  }
+  producer_handle_ = pending_buffer_->Complete(bytes_read);
+  pending_buffer_ = nullptr;
+
+  client_->OnTransferSizeUpdated(bytes_read);
+  total_bytes_read_ += bytes_read;
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&StreamReaderURLLoader::ReadMore,
+                                weak_factory_.GetWeakPtr()));
+}
+
+void StreamReaderURLLoader::RequestComplete(int status_code) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto status = network::URLLoaderCompletionStatus(status_code);
+  status.completion_time = base::TimeTicks::Now();
+  status.encoded_data_length = total_bytes_read_ + header_length_;
+  status.encoded_body_length = total_bytes_read_;
+  // We don't support decoders, so use the same value.
+  status.decoded_body_length = total_bytes_read_;
+
+  client_->OnComplete(status);
+  CleanUp();
+}
+
+void StreamReaderURLLoader::CleanUp() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Resets the watchers and pipes, so that we will never be called back.
+  writable_handle_watcher_.Cancel();
+  pending_buffer_ = nullptr;
+  producer_handle_.reset();
+
+  // Manages its own lifetime.
+  delete this;
+}
+
+bool StreamReaderURLLoader::ParseRange(const net::HttpRequestHeaders& headers) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  std::string range_header;
+  if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
+    // This loader only cares about the Range header so that we know how many
+    // bytes in the stream to skip and how many to read after that.
+    std::vector<net::HttpByteRange> ranges;
+    if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
+      // In case of multi-range request only use the first range.
+      // We don't support multirange requests.
+      if (ranges.size() == 1)
+        byte_range_ = ranges[0];
+    } else {
+      // This happens if the range header could not be parsed or is invalid.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool StreamReaderURLLoader::byte_range_valid() const {
+  return byte_range_.IsValid() && byte_range_.first_byte_position() >= 0;
+}
+
+}  // namespace net_service
\ No newline at end of file
diff --git a/src/libcef/browser/net_service/stream_reader_url_loader.h b/src/libcef/browser/net_service/stream_reader_url_loader.h
new file mode 100644
index 0000000..7ec165f
--- /dev/null
+++ b/src/libcef/browser/net_service/stream_reader_url_loader.h
@@ -0,0 +1,237 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "net/http/http_byte_range.h"
+#include "services/network/public/cpp/net_adapters.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/public/mojom/url_loader.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+
+namespace net_service {
+
+class InputStreamReader;
+
+// Abstract class representing an input stream. All methods are called in
+// sequence on a worker thread, but not necessarily on the same thread.
+class InputStream {
+ public:
+  virtual ~InputStream() {}
+
+  // Callback for asynchronous continuation of Skip(). If |bytes_skipped| > 0
+  // then either Skip() will be called again until the requested number of
+  // bytes have been skipped or the request will proceed. If |bytes_skipped|
+  // <= 0 the request will fail with net::ERR_REQUEST_RANGE_NOT_SATISFIABLE.
+  using SkipCallback = base::OnceCallback<void(int64_t /* bytes_skipped */)>;
+
+  // Skip over and discard |n| bytes of data from this input stream. If data
+  // is available immediately set |bytes_skipped| to the number of of bytes
+  // skipped and return true. To read the data at a later time set
+  // |bytes_skipped| to 0, return true and execute |callback| when the data is
+  // available. To indicate failure set |bytes_skipped| to < 0 (e.g.
+  // net::ERR_FAILED) and return false.
+  virtual bool Skip(int64_t n,
+                    int64_t* bytes_skipped,
+                    SkipCallback callback) = 0;
+
+  // Callback for asynchronous continuation of Read(). If |bytes_read| == 0
+  // the response will be considered complete. If |bytes_read| > 0 then Read()
+  // will be called again until the request is complete (based on either the
+  // result or the expected content length). If |bytes_read| < 0 then the
+  // request will fail and the |bytes_read| value will be treated as the error
+  // code.
+  using ReadCallback = base::OnceCallback<void(int /* bytes_read */)>;
+
+  // Read response data. If data is available immediately copy up to |length|
+  // bytes into |dest|, set |bytes_read| to the number of bytes copied, and
+  // return true. To read the data at a later time set |bytes_read| to 0, return
+  // true and execute |callback| when the data is available. To indicate
+  // response completion set |bytes_read| to 0 and return false. To indicate
+  // failure set |bytes_read| to < 0 (e.g. net::ERR_FAILED) and return false.
+  virtual bool Read(net::IOBuffer* dest,
+                    int length,
+                    int* bytes_read,
+                    ReadCallback callback) = 0;
+};
+
+// Unique identifier for RequestHandler callbacks.
+// Based on components/viz/common/surfaces/frame_sink_id.h
+class RequestId {
+ public:
+  constexpr RequestId() : request_id_(0), routing_id_(0) {}
+
+  constexpr RequestId(const RequestId& other)
+      : request_id_(other.request_id_), routing_id_(other.routing_id_) {}
+
+  constexpr RequestId(uint32_t request_id, uint32_t routing_id)
+      : request_id_(request_id), routing_id_(routing_id) {}
+
+  constexpr bool is_valid() const {
+    return request_id_ != 0 || routing_id_ != 0;
+  }
+
+  constexpr uint32_t request_id() const { return request_id_; }
+
+  constexpr uint32_t routing_id() const { return routing_id_; }
+
+  bool operator==(const RequestId& other) const {
+    return request_id_ == other.request_id_ && routing_id_ == other.routing_id_;
+  }
+
+  bool operator!=(const RequestId& other) const { return !(*this == other); }
+
+  bool operator<(const RequestId& other) const {
+    return std::tie(request_id_, routing_id_) <
+           std::tie(other.request_id_, other.routing_id_);
+  }
+
+  size_t hash() const { return base::HashInts(request_id_, routing_id_); }
+
+  std::string ToString() const;
+
+  std::string ToString(base::StringPiece debug_label) const;
+
+ private:
+  uint32_t request_id_;
+  uint32_t routing_id_;
+};
+
+std::ostream& operator<<(std::ostream& out, const RequestId& request_id);
+
+struct RequestIdHash {
+  size_t operator()(const RequestId& key) const { return key.hash(); }
+};
+
+// Abstract class for handling intercepted resource responses. All methods are
+// called on the IO thread unless otherwise indicated.
+class ResourceResponse {
+ public:
+  virtual ~ResourceResponse() {}
+
+  // Callback for asynchronous continuation of Open(). If the InputStream is
+  // null the request will be canceled.
+  using OpenCallback = base::OnceCallback<void(std::unique_ptr<InputStream>)>;
+
+  // This method is called on a worker thread. Return true and execute
+  // |callback| to continue the request. Return false to cancel the request.
+  // |request| may be different from the request used to create the
+  // StreamReaderURLLoader if a redirect was followed.
+  virtual bool OpenInputStream(const RequestId& id,
+                               const network::ResourceRequest& request,
+                               OpenCallback callback) = 0;
+
+  // This method is called to populate the response headers.
+  using HeaderMap = std::multimap<std::string, std::string>;
+  virtual void GetResponseHeaders(const RequestId& id,
+                                  int* status_code,
+                                  std::string* reason_phrase,
+                                  std::string* mime_type,
+                                  std::string* charset,
+                                  int64_t* content_length,
+                                  HeaderMap* extra_headers) = 0;
+};
+
+// Custom URLLoader implementation for loading network responses from stream.
+// Methods are called on the IO thread unless otherwise indicated.
+// Based on android_webview/browser/network_service/
+// android_stream_reader_url_loader.h
+class StreamReaderURLLoader : public network::mojom::URLLoader {
+ public:
+  // Delegate abstraction for obtaining input streams. All methods are called
+  // on the IO thread unless otherwise indicated.
+  class Delegate : public ResourceResponse {
+   public:
+    // This method is called if the result of calling OpenInputStream was null.
+    // The |restarted| parameter is set to true if the request was restarted
+    // with a new loader.
+    virtual void OnInputStreamOpenFailed(const RequestId& id,
+                                         bool* restarted) = 0;
+  };
+
+  StreamReaderURLLoader(
+      const RequestId& request_id,
+      const network::ResourceRequest& request,
+      network::mojom::URLLoaderClientPtr client,
+      network::mojom::TrustedHeaderClientPtr header_client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+      std::unique_ptr<Delegate> response_delegate);
+  ~StreamReaderURLLoader() override;
+
+  void Start();
+
+  void ContinueResponse(bool was_redirected);
+
+  // network::mojom::URLLoader methods:
+  void FollowRedirect(const std::vector<std::string>& removed_headers,
+                      const net::HttpRequestHeaders& modified_headers,
+                      const base::Optional<GURL>& new_url) override;
+  void SetPriority(net::RequestPriority priority,
+                   int intra_priority_value) override;
+  void PauseReadingBodyFromNet() override;
+  void ResumeReadingBodyFromNet() override;
+
+ private:
+  void ContinueWithRequestHeaders(
+      int32_t result,
+      const base::Optional<net::HttpRequestHeaders>& headers);
+  void OnInputStreamOpened(std::unique_ptr<Delegate> returned_delegate,
+                           std::unique_ptr<InputStream> input_stream);
+
+  void OnReaderSkipCompleted(int64_t bytes_skipped);
+  void HeadersComplete(int status_code, int64_t expected_content_length);
+  void ContinueWithResponseHeaders(
+      network::mojom::URLResponseHeadPtr pending_response,
+      int32_t result,
+      const base::Optional<std::string>& headers,
+      const base::Optional<GURL>& redirect_url);
+
+  void SendBody();
+  void ReadMore();
+  void OnDataPipeWritable(MojoResult result);
+  void OnReaderReadCompleted(int bytes_read);
+  void RequestComplete(int status_code);
+
+  void CleanUp();
+
+  bool ParseRange(const net::HttpRequestHeaders& headers);
+  bool byte_range_valid() const;
+
+  const RequestId request_id_;
+
+  size_t header_length_ = 0;
+  int64_t total_bytes_read_ = 0;
+
+  net::HttpByteRange byte_range_;
+  network::ResourceRequest request_;
+  network::mojom::URLLoaderClientPtr client_;
+  network::mojom::TrustedHeaderClientPtr header_client_;
+  const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
+  std::unique_ptr<Delegate> response_delegate_;
+  scoped_refptr<InputStreamReader> input_stream_reader_;
+
+  mojo::ScopedDataPipeProducerHandle producer_handle_;
+  scoped_refptr<network::NetToMojoPendingBuffer> pending_buffer_;
+  mojo::SimpleWatcher writable_handle_watcher_;
+  base::ThreadChecker thread_checker_;
+
+  scoped_refptr<base::SequencedTaskRunner> stream_work_task_runner_;
+
+  base::OnceClosure open_cancel_callback_;
+
+  base::WeakPtrFactory<StreamReaderURLLoader> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(StreamReaderURLLoader);
+};
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_
diff --git a/src/libcef/browser/net_service/url_loader_factory_getter.cc b/src/libcef/browser/net_service/url_loader_factory_getter.cc
new file mode 100644
index 0000000..c1c370a
--- /dev/null
+++ b/src/libcef/browser/net_service/url_loader_factory_getter.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "libcef/browser/net_service/url_loader_factory_getter.h"
+
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/devtools/devtools_instrumentation.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/common/content_client.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
+
+namespace net_service {
+
+// Based on CreateDownloadURLLoaderFactoryGetter from
+// content/browser/download/download_manager_impl.cc.
+// static
+scoped_refptr<URLLoaderFactoryGetter> URLLoaderFactoryGetter::Create(
+    content::RenderFrameHost* render_frame_host,
+    content::BrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+  DCHECK(browser_context);
+
+  // Call this early because newly created BrowserContexts may need to
+  // initialize additional state, and that should be done on the UI thread
+  // instead of potentially racing with the WillCreateURLLoaderFactory
+  // implementation.
+  auto loader_factory =
+      content::BrowserContext::GetDefaultStoragePartition(browser_context)
+          ->GetURLLoaderFactoryForBrowserProcess();
+
+  network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info;
+  network::mojom::URLLoaderFactoryRequest proxy_factory_request;
+
+  // Create an intermediate pipe that can be used to proxy the request's
+  // URLLoaderFactory.
+  network::mojom::URLLoaderFactoryPtrInfo maybe_proxy_factory_ptr_info;
+  mojo::PendingReceiver<network::mojom::URLLoaderFactory>
+      maybe_proxy_factory_request = MakeRequest(&maybe_proxy_factory_ptr_info);
+
+  bool should_proxy = false;
+  int render_process_id = -1;
+
+  if (render_frame_host) {
+    render_process_id = render_frame_host->GetProcess()->GetID();
+
+    // Allow DevTools to potentially inject itself into the proxy pipe.
+    should_proxy =
+        content::devtools_instrumentation::WillCreateURLLoaderFactory(
+            static_cast<content::RenderFrameHostImpl*>(render_frame_host),
+            false /* is_navigation */, false /* is_download */,
+            &maybe_proxy_factory_request, nullptr /* factory_override */);
+  }
+
+  // Allow the Content embedder to inject itself if it wants to.
+  should_proxy |= CefContentBrowserClient::Get()->WillCreateURLLoaderFactory(
+      browser_context, render_frame_host, render_process_id,
+      content::ContentBrowserClient::URLLoaderFactoryType::kDocumentSubResource,
+      url::Origin(), base::nullopt /* navigation_id */,
+      &maybe_proxy_factory_request, nullptr /* header_client */,
+      nullptr /* bypass_redirect_checks */, nullptr /* disable_secure_dns */,
+      nullptr /* factory_override */);
+
+  // If anyone above indicated that they care about proxying, pass the
+  // intermediate pipe along to the URLLoaderFactoryGetter.
+  if (should_proxy) {
+    proxy_factory_ptr_info = std::move(maybe_proxy_factory_ptr_info);
+    proxy_factory_request = std::move(maybe_proxy_factory_request);
+  }
+
+  return base::WrapRefCounted(new URLLoaderFactoryGetter(
+      loader_factory->Clone(), std::move(proxy_factory_ptr_info),
+      std::move(proxy_factory_request)));
+}
+
+// Based on NetworkDownloadURLLoaderFactoryGetter from
+// content/browser/download/network_download_url_loader_factory_getter.cc.
+scoped_refptr<network::SharedURLLoaderFactory>
+URLLoaderFactoryGetter::GetURLLoaderFactory() {
+  // On first call we associate with the current thread.
+  if (!task_runner_) {
+    task_runner_ = base::ThreadTaskRunnerHandle::Get();
+  } else {
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  }
+
+  if (lazy_factory_)
+    return lazy_factory_;
+
+  // Bind on the current thread.
+  auto loader_factory =
+      network::SharedURLLoaderFactory::Create(std::move(loader_factory_info_));
+
+  if (proxy_factory_request_.is_pending()) {
+    loader_factory->Clone(std::move(proxy_factory_request_));
+    lazy_factory_ =
+        base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
+            std::move(proxy_factory_ptr_info_));
+  } else {
+    lazy_factory_ = loader_factory;
+  }
+  return lazy_factory_;
+}
+
+URLLoaderFactoryGetter::URLLoaderFactoryGetter(
+    std::unique_ptr<network::PendingSharedURLLoaderFactory> loader_factory_info,
+    network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info,
+    network::mojom::URLLoaderFactoryRequest proxy_factory_request)
+    : loader_factory_info_(std::move(loader_factory_info)),
+      proxy_factory_ptr_info_(std::move(proxy_factory_ptr_info)),
+      proxy_factory_request_(std::move(proxy_factory_request)) {}
+
+URLLoaderFactoryGetter::~URLLoaderFactoryGetter() = default;
+
+void URLLoaderFactoryGetter::DeleteOnCorrectThread() const {
+  if (task_runner_ && !task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->DeleteSoon(FROM_HERE, this);
+    return;
+  }
+  delete this;
+}
+
+}  // namespace net_service
\ No newline at end of file
diff --git a/src/libcef/browser/net_service/url_loader_factory_getter.h b/src/libcef/browser/net_service/url_loader_factory_getter.h
new file mode 100644
index 0000000..431515a
--- /dev/null
+++ b/src/libcef/browser/net_service/url_loader_factory_getter.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_URL_LOADER_FACTORY_GETTER_H_
+#define CEF_LIBCEF_BROWSER_NET_SERVICE_URL_LOADER_FACTORY_GETTER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+
+namespace content {
+class BrowserContext;
+class RenderFrameHost;
+}  // namespace content
+
+namespace network {
+class SharedURLLoaderFactory;
+class PendingSharedURLLoaderFactory;
+}  // namespace network
+
+namespace net_service {
+
+struct URLLoaderFactoryGetterDeleter;
+
+// Helper class for retrieving a URLLoaderFactory that can be bound on any
+// thread, and that correctly handles proxied requests.
+class URLLoaderFactoryGetter
+    : public base::RefCountedThreadSafe<URLLoaderFactoryGetter,
+                                        URLLoaderFactoryGetterDeleter> {
+ public:
+  // Create a URLLoaderFactoryGetter on the UI thread.
+  // |render_frame_host| may be nullptr.
+  static scoped_refptr<URLLoaderFactoryGetter> Create(
+      content::RenderFrameHost* render_frame_host,
+      content::BrowserContext* browser_context);
+
+  // Create a SharedURLLoaderFactory on the current thread. All future calls
+  // to this method must be on the same thread.
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
+
+ private:
+  friend class base::DeleteHelper<URLLoaderFactoryGetter>;
+  friend class base::RefCountedThreadSafe<URLLoaderFactoryGetter,
+                                          URLLoaderFactoryGetterDeleter>;
+  friend struct URLLoaderFactoryGetterDeleter;
+
+  URLLoaderFactoryGetter(
+      std::unique_ptr<network::PendingSharedURLLoaderFactory>
+          loader_factory_info,
+      network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info,
+      network::mojom::URLLoaderFactoryRequest proxy_factory_request);
+  ~URLLoaderFactoryGetter();
+
+  void DeleteOnCorrectThread() const;
+
+  std::unique_ptr<network::PendingSharedURLLoaderFactory> loader_factory_info_;
+  scoped_refptr<network::SharedURLLoaderFactory> lazy_factory_;
+  network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info_;
+  network::mojom::URLLoaderFactoryRequest proxy_factory_request_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryGetter);
+};
+
+struct URLLoaderFactoryGetterDeleter {
+  static void Destruct(const URLLoaderFactoryGetter* factory_getter) {
+    factory_getter->DeleteOnCorrectThread();
+  }
+};
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_BROWSER_NET_SERVICE_URL_LOADER_FACTORY_GETTER_H_
diff --git a/src/libcef/browser/origin_whitelist_impl.cc b/src/libcef/browser/origin_whitelist_impl.cc
new file mode 100644
index 0000000..6bd3bca
--- /dev/null
+++ b/src/libcef/browser/origin_whitelist_impl.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/origin_whitelist_impl.h"
+
+#include <string>
+#include <vector>
+
+#include "include/cef_origin_whitelist.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/cef_messages.h"
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/synchronization/lock.h"
+#include "content/public/browser/render_process_host.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Class that manages cross-origin whitelist registrations.
+class CefOriginWhitelistManager {
+ public:
+  CefOriginWhitelistManager() {}
+
+  // Retrieve the singleton instance.
+  static CefOriginWhitelistManager* GetInstance();
+
+  bool AddOriginEntry(const std::string& source_origin,
+                      const std::string& target_protocol,
+                      const std::string& target_domain,
+                      bool allow_target_subdomains) {
+    Cef_CrossOriginWhiteListEntry_Params info;
+    info.source_origin = source_origin;
+    info.target_protocol = target_protocol;
+    info.target_domain = target_domain;
+    info.allow_target_subdomains = allow_target_subdomains;
+
+    {
+      base::AutoLock lock_scope(lock_);
+
+      // Verify that the origin entry doesn't already exist.
+      OriginList::const_iterator it = origin_list_.begin();
+      for (; it != origin_list_.end(); ++it) {
+        if (IsEqual(*it, info))
+          return false;
+      }
+
+      origin_list_.push_back(info);
+    }
+
+    SendModifyCrossOriginWhitelistEntry(true, info);
+    return true;
+  }
+
+  bool RemoveOriginEntry(const std::string& source_origin,
+                         const std::string& target_protocol,
+                         const std::string& target_domain,
+                         bool allow_target_subdomains) {
+    Cef_CrossOriginWhiteListEntry_Params info;
+    info.source_origin = source_origin;
+    info.target_protocol = target_protocol;
+    info.target_domain = target_domain;
+    info.allow_target_subdomains = allow_target_subdomains;
+
+    bool found = false;
+
+    {
+      base::AutoLock lock_scope(lock_);
+
+      OriginList::iterator it = origin_list_.begin();
+      for (; it != origin_list_.end(); ++it) {
+        if (IsEqual(*it, info)) {
+          origin_list_.erase(it);
+          found = true;
+          break;
+        }
+      }
+    }
+
+    if (!found)
+      return false;
+
+    SendModifyCrossOriginWhitelistEntry(false, info);
+    return true;
+  }
+
+  void ClearOrigins() {
+    {
+      base::AutoLock lock_scope(lock_);
+      origin_list_.clear();
+    }
+
+    SendClearCrossOriginWhitelist();
+  }
+
+  void GetCrossOriginWhitelistEntries(
+      std::vector<Cef_CrossOriginWhiteListEntry_Params>* entries) {
+    base::AutoLock lock_scope(lock_);
+
+    if (origin_list_.empty())
+      return;
+    entries->insert(entries->end(), origin_list_.begin(), origin_list_.end());
+  }
+
+ private:
+  // Send the modify cross-origin whitelist entry message to all currently
+  // existing hosts.
+  static void SendModifyCrossOriginWhitelistEntry(
+      bool add,
+      Cef_CrossOriginWhiteListEntry_Params& params) {
+    CEF_REQUIRE_UIT();
+
+    content::RenderProcessHost::iterator i(
+        content::RenderProcessHost::AllHostsIterator());
+    for (; !i.IsAtEnd(); i.Advance()) {
+      i.GetCurrentValue()->Send(
+          new CefProcessMsg_ModifyCrossOriginWhitelistEntry(add, params));
+    }
+  }
+
+  // Send the clear cross-origin whitelists message to all currently existing
+  // hosts.
+  static void SendClearCrossOriginWhitelist() {
+    CEF_REQUIRE_UIT();
+
+    content::RenderProcessHost::iterator i(
+        content::RenderProcessHost::AllHostsIterator());
+    for (; !i.IsAtEnd(); i.Advance()) {
+      i.GetCurrentValue()->Send(new CefProcessMsg_ClearCrossOriginWhitelist);
+    }
+  }
+
+  static bool IsEqual(const Cef_CrossOriginWhiteListEntry_Params& param1,
+                      const Cef_CrossOriginWhiteListEntry_Params& param2) {
+    return (param1.source_origin == param2.source_origin &&
+            param1.target_protocol == param2.target_protocol &&
+            param1.target_domain == param2.target_domain &&
+            param1.allow_target_subdomains == param2.allow_target_subdomains);
+  }
+
+  base::Lock lock_;
+
+  // List of registered origins. Access must be protected by |lock_|.
+  typedef std::vector<Cef_CrossOriginWhiteListEntry_Params> OriginList;
+  OriginList origin_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefOriginWhitelistManager);
+};
+
+base::LazyInstance<CefOriginWhitelistManager>::Leaky g_manager =
+    LAZY_INSTANCE_INITIALIZER;
+
+CefOriginWhitelistManager* CefOriginWhitelistManager::GetInstance() {
+  return g_manager.Pointer();
+}
+
+bool IsMatch(const GURL& source_origin,
+             const GURL& target_origin,
+             const Cef_CrossOriginWhiteListEntry_Params& param) {
+  if (source_origin.GetOrigin() != GURL(param.source_origin)) {
+    // Source origin does not match.
+    return false;
+  }
+
+  if (target_origin.scheme() != param.target_protocol) {
+    // Target scheme does not match.
+    return false;
+  }
+
+  if (param.allow_target_subdomains) {
+    if (param.target_domain.empty()) {
+      // Any domain will match.
+      return true;
+    } else {
+      // Match sub-domains.
+      return target_origin.DomainIs(param.target_domain.c_str());
+    }
+  } else {
+    // Match full domain.
+    return (target_origin.host() == param.target_domain);
+  }
+}
+
+}  // namespace
+
+bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin,
+                                     const CefString& target_protocol,
+                                     const CefString& target_domain,
+                                     bool allow_target_subdomains) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED();
+    return false;
+  }
+
+  std::string source_url = source_origin;
+  GURL gurl = GURL(source_url);
+  if (gurl.is_empty() || !gurl.is_valid()) {
+    NOTREACHED() << "Invalid source_origin URL: " << source_url;
+    return false;
+  }
+
+  if (CEF_CURRENTLY_ON_UIT()) {
+    return CefOriginWhitelistManager::GetInstance()->AddOriginEntry(
+        source_origin, target_protocol, target_domain, allow_target_subdomains);
+  } else {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefAddCrossOriginWhitelistEntry),
+                   source_origin, target_protocol, target_domain,
+                   allow_target_subdomains));
+  }
+
+  return true;
+}
+
+bool CefRemoveCrossOriginWhitelistEntry(const CefString& source_origin,
+                                        const CefString& target_protocol,
+                                        const CefString& target_domain,
+                                        bool allow_target_subdomains) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED();
+    return false;
+  }
+
+  std::string source_url = source_origin;
+  GURL gurl = GURL(source_url);
+  if (gurl.is_empty() || !gurl.is_valid()) {
+    NOTREACHED() << "Invalid source_origin URL: " << source_url;
+    return false;
+  }
+
+  if (CEF_CURRENTLY_ON_UIT()) {
+    return CefOriginWhitelistManager::GetInstance()->RemoveOriginEntry(
+        source_origin, target_protocol, target_domain, allow_target_subdomains);
+  } else {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(base::IgnoreResult(&CefRemoveCrossOriginWhitelistEntry),
+                   source_origin, target_protocol, target_domain,
+                   allow_target_subdomains));
+  }
+
+  return true;
+}
+
+bool CefClearCrossOriginWhitelist() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED();
+    return false;
+  }
+
+  if (CEF_CURRENTLY_ON_UIT()) {
+    CefOriginWhitelistManager::GetInstance()->ClearOrigins();
+  } else {
+    CEF_POST_TASK(
+        CEF_UIT, base::Bind(base::IgnoreResult(&CefClearCrossOriginWhitelist)));
+  }
+
+  return true;
+}
+
+void GetCrossOriginWhitelistEntries(
+    std::vector<Cef_CrossOriginWhiteListEntry_Params>* entries) {
+  CefOriginWhitelistManager::GetInstance()->GetCrossOriginWhitelistEntries(
+      entries);
+}
+
+bool HasCrossOriginWhitelistEntry(const GURL& source, const GURL& target) {
+  std::vector<Cef_CrossOriginWhiteListEntry_Params> params;
+  CefOriginWhitelistManager::GetInstance()->GetCrossOriginWhitelistEntries(
+      &params);
+
+  if (params.empty())
+    return false;
+
+  std::vector<Cef_CrossOriginWhiteListEntry_Params>::const_iterator it =
+      params.begin();
+  for (; it != params.end(); ++it) {
+    if (IsMatch(source, target, *it))
+      return true;
+  }
+
+  return false;
+}
diff --git a/src/libcef/browser/origin_whitelist_impl.h b/src/libcef/browser/origin_whitelist_impl.h
new file mode 100644
index 0000000..bca948c
--- /dev/null
+++ b/src/libcef/browser/origin_whitelist_impl.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_ORIGIN_WHITELIST_IMPL_H_
+#define CEF_LIBCEF_BROWSER_ORIGIN_WHITELIST_IMPL_H_
+
+#include <list>
+#include <vector>
+
+namespace content {
+class RenderProcessHost;
+}
+
+class GURL;
+
+struct Cef_CrossOriginWhiteListEntry_Params;
+
+// Called to retrieve the current list of cross-origin white list entries. This
+// method is thread safe.
+void GetCrossOriginWhitelistEntries(
+    std::vector<Cef_CrossOriginWhiteListEntry_Params>* entries);
+
+// Returns true if |source| can access |target| based on the cross-origin white
+// list settings.
+bool HasCrossOriginWhitelistEntry(const GURL& source, const GURL& target);
+
+#endif  // CEF_LIBCEF_BROWSER_ORIGIN_WHITELIST_IMPL_H_
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr.cc b/src/libcef/browser/osr/browser_platform_delegate_osr.cc
new file mode 100644
index 0000000..42e003d
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr.cc
@@ -0,0 +1,599 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/browser_platform_delegate_osr.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/image_impl.h"
+#include "libcef/browser/osr/osr_accessibility_util.h"
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+#include "libcef/browser/osr/web_contents_view_osr.h"
+#include "libcef/common/drag_data_impl.h"
+
+#include "base/message_loop/message_loop_current.h"
+#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/render_view_host.h"
+#include "ui/events/base_event_utils.h"
+
+CefBrowserPlatformDelegateOsr::CefBrowserPlatformDelegateOsr(
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate)
+    : native_delegate_(std::move(native_delegate)), view_osr_(nullptr) {
+  native_delegate_->set_windowless_handler(this);
+}
+
+void CefBrowserPlatformDelegateOsr::CreateViewForWebContents(
+    content::WebContentsView** view,
+    content::RenderViewHostDelegateView** delegate_view) {
+  DCHECK(!view_osr_);
+
+  // Use the OSR view instead of the default platform view.
+  view_osr_ = new CefWebContentsViewOSR(
+      GetBackgroundColor(), CanUseSharedTexture(), CanUseExternalBeginFrame());
+  *view = view_osr_;
+  *delegate_view = view_osr_;
+}
+
+void CefBrowserPlatformDelegateOsr::WebContentsCreated(
+    content::WebContents* web_contents) {
+  CefBrowserPlatformDelegate::WebContentsCreated(web_contents);
+
+  DCHECK(view_osr_);
+  DCHECK(!view_osr_->web_contents());
+
+  // Associate the WebContents with the OSR view.
+  view_osr_->WebContentsCreated(web_contents);
+}
+
+void CefBrowserPlatformDelegateOsr::BrowserCreated(
+    CefBrowserHostImpl* browser) {
+  CefBrowserPlatformDelegate::BrowserCreated(browser);
+
+  if (browser->IsPopup()) {
+    // Associate the RenderWidget host view with the browser now because the
+    // browser wasn't known at the time that the host view was created.
+    content::RenderViewHost* host =
+        browser->web_contents()->GetRenderViewHost();
+    DCHECK(host);
+    CefRenderWidgetHostViewOSR* view =
+        static_cast<CefRenderWidgetHostViewOSR*>(host->GetWidget()->GetView());
+    // |view| will be null if the popup is a DevTools window.
+    if (view)
+      view->set_browser_impl(browser);
+  }
+}
+
+void CefBrowserPlatformDelegateOsr::BrowserDestroyed(
+    CefBrowserHostImpl* browser) {
+  CefBrowserPlatformDelegate::BrowserDestroyed(browser);
+
+  view_osr_ = nullptr;
+}
+
+bool CefBrowserPlatformDelegateOsr::CanUseSharedTexture() const {
+  return native_delegate_->CanUseSharedTexture();
+}
+
+bool CefBrowserPlatformDelegateOsr::CanUseExternalBeginFrame() const {
+  return native_delegate_->CanUseExternalBeginFrame();
+}
+
+SkColor CefBrowserPlatformDelegateOsr::GetBackgroundColor() const {
+  return native_delegate_->GetBackgroundColor();
+}
+
+void CefBrowserPlatformDelegateOsr::WasResized() {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->WasResized();
+}
+
+void CefBrowserPlatformDelegateOsr::SendKeyEvent(const CefKeyEvent& event) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (!view)
+    return;
+
+  content::NativeWebKeyboardEvent web_event =
+      native_delegate_->TranslateWebKeyEvent(event);
+  view->SendKeyEvent(web_event);
+}
+
+void CefBrowserPlatformDelegateOsr::SendMouseClickEvent(
+    const CefMouseEvent& event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (!view)
+    return;
+
+  blink::WebMouseEvent web_event = native_delegate_->TranslateWebClickEvent(
+      event, type, mouseUp, clickCount);
+  view->SendMouseEvent(web_event);
+}
+
+void CefBrowserPlatformDelegateOsr::SendMouseMoveEvent(
+    const CefMouseEvent& event,
+    bool mouseLeave) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (!view)
+    return;
+
+  blink::WebMouseEvent web_event =
+      native_delegate_->TranslateWebMoveEvent(event, mouseLeave);
+  view->SendMouseEvent(web_event);
+}
+
+void CefBrowserPlatformDelegateOsr::SendMouseWheelEvent(
+    const CefMouseEvent& event,
+    int deltaX,
+    int deltaY) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (!view)
+    return;
+
+  blink::WebMouseWheelEvent web_event =
+      native_delegate_->TranslateWebWheelEvent(event, deltaX, deltaY);
+  view->SendMouseWheelEvent(web_event);
+}
+
+void CefBrowserPlatformDelegateOsr::SendTouchEvent(const CefTouchEvent& event) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->SendTouchEvent(event);
+}
+
+void CefBrowserPlatformDelegateOsr::SendFocusEvent(bool setFocus) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->SendFocusEvent(setFocus);
+}
+
+gfx::Point CefBrowserPlatformDelegateOsr::GetScreenPoint(
+    const gfx::Point& view) const {
+  CefRefPtr<CefRenderHandler> handler = browser_->client()->GetRenderHandler();
+  if (handler.get()) {
+    int screenX = 0, screenY = 0;
+    if (handler->GetScreenPoint(browser_, view.x(), view.y(), screenX,
+                                screenY)) {
+      return gfx::Point(screenX, screenY);
+    }
+  }
+  return view;
+}
+
+void CefBrowserPlatformDelegateOsr::ViewText(const std::string& text) {
+  native_delegate_->ViewText(text);
+}
+
+bool CefBrowserPlatformDelegateOsr::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  return native_delegate_->HandleKeyboardEvent(event);
+}
+
+CefEventHandle CefBrowserPlatformDelegateOsr::GetEventHandle(
+    const content::NativeWebKeyboardEvent& event) const {
+  return native_delegate_->GetEventHandle(event);
+}
+
+std::unique_ptr<CefFileDialogRunner>
+CefBrowserPlatformDelegateOsr::CreateFileDialogRunner() {
+  return native_delegate_->CreateFileDialogRunner();
+}
+
+std::unique_ptr<CefJavaScriptDialogRunner>
+CefBrowserPlatformDelegateOsr::CreateJavaScriptDialogRunner() {
+  return native_delegate_->CreateJavaScriptDialogRunner();
+}
+
+std::unique_ptr<CefMenuRunner>
+CefBrowserPlatformDelegateOsr::CreateMenuRunner() {
+  return native_delegate_->CreateMenuRunner();
+}
+
+bool CefBrowserPlatformDelegateOsr::IsWindowless() const {
+  return true;
+}
+
+bool CefBrowserPlatformDelegateOsr::IsViewsHosted() const {
+  return false;
+}
+
+void CefBrowserPlatformDelegateOsr::WasHidden(bool hidden) {
+  // The WebContentsImpl will notify the OSR view.
+  content::WebContentsImpl* web_contents =
+      static_cast<content::WebContentsImpl*>(browser_->web_contents());
+  if (web_contents) {
+    if (hidden)
+      web_contents->WasHidden();
+    else
+      web_contents->WasShown();
+  }
+}
+
+void CefBrowserPlatformDelegateOsr::NotifyScreenInfoChanged() {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->OnScreenInfoChanged();
+}
+
+void CefBrowserPlatformDelegateOsr::Invalidate(cef_paint_element_type_t type) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->Invalidate(type);
+}
+
+void CefBrowserPlatformDelegateOsr::SendExternalBeginFrame() {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->SendExternalBeginFrame();
+}
+
+void CefBrowserPlatformDelegateOsr::SetWindowlessFrameRate(int frame_rate) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->UpdateFrameRate();
+}
+
+void CefBrowserPlatformDelegateOsr::ImeSetComposition(
+    const CefString& text,
+    const std::vector<CefCompositionUnderline>& underlines,
+    const CefRange& replacement_range,
+    const CefRange& selection_range) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view) {
+    view->ImeSetComposition(text, underlines, replacement_range,
+                            selection_range);
+  }
+}
+
+void CefBrowserPlatformDelegateOsr::ImeCommitText(
+    const CefString& text,
+    const CefRange& replacement_range,
+    int relative_cursor_pos) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->ImeCommitText(text, replacement_range, relative_cursor_pos);
+}
+
+void CefBrowserPlatformDelegateOsr::ImeFinishComposingText(
+    bool keep_selection) {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->ImeFinishComposingText(keep_selection);
+}
+
+void CefBrowserPlatformDelegateOsr::ImeCancelComposition() {
+  CefRenderWidgetHostViewOSR* view = GetOSRHostView();
+  if (view)
+    view->ImeCancelComposition();
+}
+
+void CefBrowserPlatformDelegateOsr::DragTargetDragEnter(
+    CefRefPtr<CefDragData> drag_data,
+    const CefMouseEvent& event,
+    cef_drag_operations_mask_t allowed_ops) {
+  content::WebContentsImpl* web_contents =
+      static_cast<content::WebContentsImpl*>(browser_->web_contents());
+  if (!web_contents)
+    return;
+
+  if (current_rvh_for_drag_)
+    DragTargetDragLeave();
+
+  const gfx::Point client_pt(event.x, event.y);
+  gfx::PointF transformed_pt;
+  current_rwh_for_drag_ =
+      web_contents->GetInputEventRouter()
+          ->GetRenderWidgetHostAtPoint(
+              web_contents->GetRenderViewHost()->GetWidget()->GetView(),
+              gfx::PointF(client_pt), &transformed_pt)
+          ->GetWeakPtr();
+  current_rvh_for_drag_ = web_contents->GetRenderViewHost();
+
+  drag_data_ = drag_data;
+  drag_allowed_ops_ = allowed_ops;
+
+  CefDragDataImpl* data_impl = static_cast<CefDragDataImpl*>(drag_data.get());
+  base::AutoLock lock_scope(data_impl->lock());
+  content::DropData* drop_data = data_impl->drop_data();
+  const gfx::Point& screen_pt = GetScreenPoint(client_pt);
+  blink::WebDragOperationsMask ops =
+      static_cast<blink::WebDragOperationsMask>(allowed_ops);
+  int modifiers = TranslateWebEventModifiers(event.modifiers);
+
+  current_rwh_for_drag_->FilterDropData(drop_data);
+
+  // Give the delegate an opportunity to cancel the drag.
+  if (web_contents->GetDelegate() && !web_contents->GetDelegate()->CanDragEnter(
+                                         web_contents, *drop_data, ops)) {
+    drag_data_ = nullptr;
+    return;
+  }
+
+  current_rwh_for_drag_->DragTargetDragEnter(
+      *drop_data, transformed_pt, gfx::PointF(screen_pt), ops, modifiers);
+}
+
+void CefBrowserPlatformDelegateOsr::DragTargetDragOver(
+    const CefMouseEvent& event,
+    cef_drag_operations_mask_t allowed_ops) {
+  if (!drag_data_)
+    return;
+
+  content::WebContentsImpl* web_contents =
+      static_cast<content::WebContentsImpl*>(browser_->web_contents());
+  if (!web_contents)
+    return;
+
+  const gfx::Point client_pt(event.x, event.y);
+  const gfx::Point& screen_pt = GetScreenPoint(client_pt);
+
+  gfx::PointF transformed_pt;
+  content::RenderWidgetHostImpl* target_rwh =
+      web_contents->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
+          web_contents->GetRenderViewHost()->GetWidget()->GetView(),
+          gfx::PointF(client_pt), &transformed_pt);
+
+  if (target_rwh != current_rwh_for_drag_.get()) {
+    if (current_rwh_for_drag_) {
+      gfx::PointF transformed_leave_point(client_pt);
+      gfx::PointF transformed_screen_point(screen_pt);
+      static_cast<content::RenderWidgetHostViewBase*>(
+          web_contents->GetRenderWidgetHostView())
+          ->TransformPointToCoordSpaceForView(
+              gfx::PointF(client_pt),
+              static_cast<content::RenderWidgetHostViewBase*>(
+                  current_rwh_for_drag_->GetView()),
+              &transformed_leave_point);
+      static_cast<content::RenderWidgetHostViewBase*>(
+          web_contents->GetRenderWidgetHostView())
+          ->TransformPointToCoordSpaceForView(
+              gfx::PointF(screen_pt),
+              static_cast<content::RenderWidgetHostViewBase*>(
+                  current_rwh_for_drag_->GetView()),
+              &transformed_screen_point);
+      current_rwh_for_drag_->DragTargetDragLeave(transformed_leave_point,
+                                                 transformed_screen_point);
+    }
+    DragTargetDragEnter(drag_data_, event, drag_allowed_ops_);
+  }
+
+  if (!drag_data_)
+    return;
+
+  blink::WebDragOperationsMask ops =
+      static_cast<blink::WebDragOperationsMask>(allowed_ops);
+  int modifiers = TranslateWebEventModifiers(event.modifiers);
+
+  target_rwh->DragTargetDragOver(transformed_pt, gfx::PointF(screen_pt), ops,
+                                 modifiers);
+}
+
+void CefBrowserPlatformDelegateOsr::DragTargetDragLeave() {
+  if (current_rvh_for_drag_ != browser_->web_contents()->GetRenderViewHost() ||
+      !drag_data_) {
+    return;
+  }
+
+  if (current_rwh_for_drag_) {
+    current_rwh_for_drag_->DragTargetDragLeave(gfx::PointF(), gfx::PointF());
+    current_rwh_for_drag_.reset();
+  }
+
+  drag_data_ = nullptr;
+}
+
+void CefBrowserPlatformDelegateOsr::DragTargetDrop(const CefMouseEvent& event) {
+  if (!drag_data_)
+    return;
+
+  content::WebContentsImpl* web_contents =
+      static_cast<content::WebContentsImpl*>(browser_->web_contents());
+  if (!web_contents)
+    return;
+
+  gfx::Point client_pt(event.x, event.y);
+  const gfx::Point& screen_pt = GetScreenPoint(client_pt);
+
+  gfx::PointF transformed_pt;
+  content::RenderWidgetHostImpl* target_rwh =
+      web_contents->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
+          web_contents->GetRenderViewHost()->GetWidget()->GetView(),
+          gfx::PointF(client_pt), &transformed_pt);
+
+  if (target_rwh != current_rwh_for_drag_.get()) {
+    if (current_rwh_for_drag_) {
+      gfx::PointF transformed_leave_point(client_pt);
+      gfx::PointF transformed_screen_point(screen_pt);
+      static_cast<content::RenderWidgetHostViewBase*>(
+          web_contents->GetRenderWidgetHostView())
+          ->TransformPointToCoordSpaceForView(
+              gfx::PointF(client_pt),
+              static_cast<content::RenderWidgetHostViewBase*>(
+                  current_rwh_for_drag_->GetView()),
+              &transformed_leave_point);
+      static_cast<content::RenderWidgetHostViewBase*>(
+          web_contents->GetRenderWidgetHostView())
+          ->TransformPointToCoordSpaceForView(
+              gfx::PointF(screen_pt),
+              static_cast<content::RenderWidgetHostViewBase*>(
+                  current_rwh_for_drag_->GetView()),
+              &transformed_screen_point);
+      current_rwh_for_drag_->DragTargetDragLeave(transformed_leave_point,
+                                                 transformed_screen_point);
+    }
+    DragTargetDragEnter(drag_data_, event, drag_allowed_ops_);
+  }
+
+  if (!drag_data_)
+    return;
+
+  {
+    CefDragDataImpl* data_impl =
+        static_cast<CefDragDataImpl*>(drag_data_.get());
+    base::AutoLock lock_scope(data_impl->lock());
+    content::DropData* drop_data = data_impl->drop_data();
+    int modifiers = TranslateWebEventModifiers(event.modifiers);
+
+    target_rwh->DragTargetDrop(*drop_data, transformed_pt,
+                               gfx::PointF(screen_pt), modifiers);
+  }
+
+  drag_data_ = nullptr;
+}
+
+void CefBrowserPlatformDelegateOsr::StartDragging(
+    const content::DropData& drop_data,
+    blink::WebDragOperationsMask allowed_ops,
+    const gfx::ImageSkia& image,
+    const gfx::Vector2d& image_offset,
+    const content::DragEventSourceInfo& event_info,
+    content::RenderWidgetHostImpl* source_rwh) {
+  drag_start_rwh_ = source_rwh->GetWeakPtr();
+
+  bool handled = false;
+
+  CefRefPtr<CefRenderHandler> handler =
+      browser_->GetClient()->GetRenderHandler();
+  if (handler.get()) {
+    CefRefPtr<CefImage> cef_image(new CefImageImpl(image));
+    CefPoint cef_image_pos(image_offset.x(), image_offset.y());
+    CefRefPtr<CefDragDataImpl> drag_data(
+        new CefDragDataImpl(drop_data, cef_image, cef_image_pos));
+    drag_data->SetReadOnly(true);
+    base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
+    handled = handler->StartDragging(
+        browser_, drag_data.get(),
+        static_cast<CefRenderHandler::DragOperationsMask>(allowed_ops),
+        event_info.event_location.x(), event_info.event_location.y());
+  }
+
+  if (!handled)
+    DragSourceSystemDragEnded();
+}
+
+void CefBrowserPlatformDelegateOsr::UpdateDragCursor(
+    blink::WebDragOperation operation) {
+  CefRefPtr<CefRenderHandler> handler =
+      browser_->GetClient()->GetRenderHandler();
+  if (handler.get()) {
+    handler->UpdateDragCursor(
+        browser_, static_cast<CefRenderHandler::DragOperation>(operation));
+  }
+}
+
+void CefBrowserPlatformDelegateOsr::DragSourceEndedAt(
+    int x,
+    int y,
+    cef_drag_operations_mask_t op) {
+  if (!drag_start_rwh_)
+    return;
+
+  content::WebContentsImpl* web_contents =
+      static_cast<content::WebContentsImpl*>(browser_->web_contents());
+  if (!web_contents)
+    return;
+
+  content::RenderWidgetHostImpl* source_rwh = drag_start_rwh_.get();
+  const gfx::Point client_loc(gfx::Point(x, y));
+  const gfx::Point& screen_loc = GetScreenPoint(client_loc);
+  blink::WebDragOperation drag_op = static_cast<blink::WebDragOperation>(op);
+
+  // |client_loc| and |screen_loc| are in the root coordinate space, for
+  // non-root RenderWidgetHosts they need to be transformed.
+  gfx::PointF transformed_point(client_loc);
+  gfx::PointF transformed_screen_point(screen_loc);
+  if (source_rwh && web_contents->GetRenderWidgetHostView()) {
+    static_cast<content::RenderWidgetHostViewBase*>(
+        web_contents->GetRenderWidgetHostView())
+        ->TransformPointToCoordSpaceForView(
+            gfx::PointF(client_loc),
+            static_cast<content::RenderWidgetHostViewBase*>(
+                source_rwh->GetView()),
+            &transformed_point);
+    static_cast<content::RenderWidgetHostViewBase*>(
+        web_contents->GetRenderWidgetHostView())
+        ->TransformPointToCoordSpaceForView(
+            gfx::PointF(screen_loc),
+            static_cast<content::RenderWidgetHostViewBase*>(
+                source_rwh->GetView()),
+            &transformed_screen_point);
+  }
+
+  web_contents->DragSourceEndedAt(transformed_point.x(), transformed_point.y(),
+                                  transformed_screen_point.x(),
+                                  transformed_screen_point.y(), drag_op,
+                                  source_rwh);
+}
+
+void CefBrowserPlatformDelegateOsr::DragSourceSystemDragEnded() {
+  if (!drag_start_rwh_)
+    return;
+
+  content::WebContents* web_contents = browser_->web_contents();
+  if (!web_contents)
+    return;
+
+  web_contents->SystemDragEnded(drag_start_rwh_.get());
+
+  drag_start_rwh_ = nullptr;
+}
+
+void CefBrowserPlatformDelegateOsr::AccessibilityEventReceived(
+    const content::AXEventNotificationDetails& eventData) {
+  CefRefPtr<CefRenderHandler> handler = browser_->client()->GetRenderHandler();
+  if (handler.get()) {
+    CefRefPtr<CefAccessibilityHandler> acchandler =
+        handler->GetAccessibilityHandler();
+
+    if (acchandler.get()) {
+      acchandler->OnAccessibilityTreeChange(
+          osr_accessibility_util::ParseAccessibilityEventData(eventData));
+    }
+  }
+}
+
+void CefBrowserPlatformDelegateOsr::AccessibilityLocationChangesReceived(
+    const std::vector<content::AXLocationChangeNotificationDetails>& locData) {
+  CefRefPtr<CefRenderHandler> handler = browser_->client()->GetRenderHandler();
+  if (handler.get()) {
+    CefRefPtr<CefAccessibilityHandler> acchandler =
+        handler->GetAccessibilityHandler();
+
+    if (acchandler.get()) {
+      acchandler->OnAccessibilityLocationChange(
+          osr_accessibility_util::ParseAccessibilityLocationData(locData));
+    }
+  }
+}
+
+CefWindowHandle CefBrowserPlatformDelegateOsr::GetParentWindowHandle() const {
+  return GetHostWindowHandle();
+}
+
+gfx::Point CefBrowserPlatformDelegateOsr::GetParentScreenPoint(
+    const gfx::Point& view) const {
+  return GetScreenPoint(view);
+}
+
+CefRenderWidgetHostViewOSR* CefBrowserPlatformDelegateOsr::GetOSRHostView()
+    const {
+  content::WebContents* web_contents = browser_->web_contents();
+  CefRenderWidgetHostViewOSR* fs_view =
+      static_cast<CefRenderWidgetHostViewOSR*>(
+          web_contents->GetFullscreenRenderWidgetHostView());
+  if (fs_view)
+    return fs_view;
+
+  content::RenderViewHost* host = web_contents->GetRenderViewHost();
+  if (host) {
+    return static_cast<CefRenderWidgetHostViewOSR*>(
+        host->GetWidget()->GetView());
+  }
+
+  return nullptr;
+}
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr.h b/src/libcef/browser/osr/browser_platform_delegate_osr.h
new file mode 100644
index 0000000..ffb6d85
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr.h
@@ -0,0 +1,128 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_H_
+#define CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_H_
+
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/native/browser_platform_delegate_native.h"
+
+class CefRenderWidgetHostViewOSR;
+class CefWebContentsViewOSR;
+
+namespace content {
+class RenderWidgetHostImpl;
+}
+
+// Base implementation of windowless browser functionality.
+class CefBrowserPlatformDelegateOsr
+    : public CefBrowserPlatformDelegate,
+      public CefBrowserPlatformDelegateNative::WindowlessHandler {
+ public:
+  // CefBrowserPlatformDelegate methods:
+  void CreateViewForWebContents(
+      content::WebContentsView** view,
+      content::RenderViewHostDelegateView** delegate_view) override;
+  void WebContentsCreated(content::WebContents* web_contents) override;
+  void BrowserCreated(CefBrowserHostImpl* browser) override;
+  void BrowserDestroyed(CefBrowserHostImpl* browser) override;
+  SkColor GetBackgroundColor() const override;
+  bool CanUseSharedTexture() const override;
+  bool CanUseExternalBeginFrame() const override;
+  void WasResized() override;
+  void SendKeyEvent(const CefKeyEvent& event) override;
+  void SendMouseClickEvent(const CefMouseEvent& event,
+                           CefBrowserHost::MouseButtonType type,
+                           bool mouseUp,
+                           int clickCount) override;
+  void SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) override;
+  void SendMouseWheelEvent(const CefMouseEvent& event,
+                           int deltaX,
+                           int deltaY) override;
+  void SendTouchEvent(const CefTouchEvent& event) override;
+  void SendFocusEvent(bool setFocus) override;
+  gfx::Point GetScreenPoint(const gfx::Point& view) const override;
+  void ViewText(const std::string& text) override;
+  bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) override;
+  CefEventHandle GetEventHandle(
+      const content::NativeWebKeyboardEvent& event) const override;
+  std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
+  std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
+      override;
+  std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;
+  bool IsWindowless() const override;
+  bool IsViewsHosted() const override;
+  void WasHidden(bool hidden) override;
+  void NotifyScreenInfoChanged() override;
+  void Invalidate(cef_paint_element_type_t type) override;
+  void SendExternalBeginFrame() override;
+  void SetWindowlessFrameRate(int frame_rate) override;
+  void ImeSetComposition(const CefString& text,
+                         const std::vector<CefCompositionUnderline>& underlines,
+                         const CefRange& replacement_range,
+                         const CefRange& selection_range) override;
+  void ImeCommitText(const CefString& text,
+                     const CefRange& replacement_range,
+                     int relative_cursor_pos) override;
+  void ImeFinishComposingText(bool keep_selection) override;
+  void ImeCancelComposition() override;
+  void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
+                           const CefMouseEvent& event,
+                           cef_drag_operations_mask_t allowed_ops) override;
+  void DragTargetDragOver(const CefMouseEvent& event,
+                          cef_drag_operations_mask_t allowed_ops) override;
+  void DragTargetDragLeave() override;
+  void DragTargetDrop(const CefMouseEvent& event) override;
+  void StartDragging(const content::DropData& drop_data,
+                     blink::WebDragOperationsMask allowed_ops,
+                     const gfx::ImageSkia& image,
+                     const gfx::Vector2d& image_offset,
+                     const content::DragEventSourceInfo& event_info,
+                     content::RenderWidgetHostImpl* source_rwh) override;
+  void UpdateDragCursor(blink::WebDragOperation operation) override;
+  void DragSourceEndedAt(int x, int y, cef_drag_operations_mask_t op) override;
+  void DragSourceSystemDragEnded() override;
+  void AccessibilityEventReceived(
+      const content::AXEventNotificationDetails& eventData) override;
+  void AccessibilityLocationChangesReceived(
+      const std::vector<content::AXLocationChangeNotificationDetails>& locData)
+      override;
+
+  // CefBrowserPlatformDelegateNative::WindowlessHandler methods:
+  CefWindowHandle GetParentWindowHandle() const override;
+  gfx::Point GetParentScreenPoint(const gfx::Point& view) const override;
+
+ protected:
+  // Platform-specific behaviors will be delegated to |native_delegate|.
+  explicit CefBrowserPlatformDelegateOsr(
+      std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate);
+
+  // Returns the primary OSR host view for the underlying browser. If a
+  // full-screen host view currently exists then it will be returned. Otherwise,
+  // the main host view will be returned.
+  CefRenderWidgetHostViewOSR* GetOSRHostView() const;
+
+  std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate_;
+  CefWebContentsViewOSR* view_osr_;  // Not owned by this class.
+
+  // Pending drag/drop data.
+  CefRefPtr<CefDragData> drag_data_;
+  cef_drag_operations_mask_t drag_allowed_ops_;
+
+  // We keep track of the RenderWidgetHost we're dragging over. If it changes
+  // during a drag, we need to re-send the DragEnter message.
+  base::WeakPtr<content::RenderWidgetHostImpl> current_rwh_for_drag_;
+
+  // We also keep track of the RenderViewHost we're dragging over to avoid
+  // sending the drag exited message after leaving the current
+  // view. |current_rvh_for_drag_| should not be dereferenced.
+  void* current_rvh_for_drag_;
+
+  // We keep track of the RenderWidgetHost from which the current drag started,
+  // in order to properly route the drag end message to it.
+  base::WeakPtr<content::RenderWidgetHostImpl> drag_start_rwh_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_H_
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr_linux.cc b/src/libcef/browser/osr/browser_platform_delegate_osr_linux.cc
new file mode 100644
index 0000000..fda16ef
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr_linux.cc
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/browser_platform_delegate_osr_linux.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+
+CefBrowserPlatformDelegateOsrLinux::CefBrowserPlatformDelegateOsrLinux(
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate)
+    : CefBrowserPlatformDelegateOsr(std::move(native_delegate)) {}
+
+CefWindowHandle CefBrowserPlatformDelegateOsrLinux::GetHostWindowHandle()
+    const {
+  return native_delegate_->window_info().parent_window;
+}
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr_linux.h b/src/libcef/browser/osr/browser_platform_delegate_osr_linux.h
new file mode 100644
index 0000000..4dc86ff
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr_linux.h
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_LINUX_H_
+#define CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_LINUX_H_
+
+#include "libcef/browser/osr/browser_platform_delegate_osr.h"
+
+// Windowless browser implementation for Linux.
+class CefBrowserPlatformDelegateOsrLinux
+    : public CefBrowserPlatformDelegateOsr {
+ public:
+  explicit CefBrowserPlatformDelegateOsrLinux(
+      std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate);
+
+  // CefBrowserPlatformDelegate methods:
+  CefWindowHandle GetHostWindowHandle() const override;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_OSR_LINUX_H_
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr_mac.h b/src/libcef/browser/osr/browser_platform_delegate_osr_mac.h
new file mode 100644
index 0000000..923e3c4
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr_mac.h
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_MAC_H_
+#define CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_MAC_H_
+
+#include "libcef/browser/osr/browser_platform_delegate_osr.h"
+
+// Windowless browser implementation for Mac OS X.
+class CefBrowserPlatformDelegateOsrMac : public CefBrowserPlatformDelegateOsr {
+ public:
+  explicit CefBrowserPlatformDelegateOsrMac(
+      std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate);
+
+  // CefBrowserPlatformDelegate methods:
+  CefWindowHandle GetHostWindowHandle() const override;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_OSR_MAC_H_
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr_mac.mm b/src/libcef/browser/osr/browser_platform_delegate_osr_mac.mm
new file mode 100644
index 0000000..a1f4afe
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr_mac.mm
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/browser_platform_delegate_osr_mac.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+
+CefBrowserPlatformDelegateOsrMac::CefBrowserPlatformDelegateOsrMac(
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate)
+    : CefBrowserPlatformDelegateOsr(std::move(native_delegate)) {}
+
+CefWindowHandle CefBrowserPlatformDelegateOsrMac::GetHostWindowHandle() const {
+  return native_delegate_->window_info().parent_view;
+}
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr_win.cc b/src/libcef/browser/osr/browser_platform_delegate_osr_win.cc
new file mode 100644
index 0000000..c09e79c
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr_win.cc
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/browser_platform_delegate_osr_win.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+
+CefBrowserPlatformDelegateOsrWin::CefBrowserPlatformDelegateOsrWin(
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate)
+    : CefBrowserPlatformDelegateOsr(std::move(native_delegate)) {}
+
+CefWindowHandle CefBrowserPlatformDelegateOsrWin::GetHostWindowHandle() const {
+  return native_delegate_->window_info().parent_window;
+}
diff --git a/src/libcef/browser/osr/browser_platform_delegate_osr_win.h b/src/libcef/browser/osr/browser_platform_delegate_osr_win.h
new file mode 100644
index 0000000..bcc9d8b
--- /dev/null
+++ b/src/libcef/browser/osr/browser_platform_delegate_osr_win.h
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_WIN_H_
+#define CEF_LIBCEF_BROWSER_OSR_BROWSER_PLATFORM_DELEGATE_OSR_WIN_H_
+
+#include "libcef/browser/osr/browser_platform_delegate_osr.h"
+
+// Windowless browser implementation for Windows.
+class CefBrowserPlatformDelegateOsrWin : public CefBrowserPlatformDelegateOsr {
+ public:
+  explicit CefBrowserPlatformDelegateOsrWin(
+      std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate);
+
+  // CefBrowserPlatformDelegate methods:
+  CefWindowHandle GetHostWindowHandle() const override;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_OSR_WIN_H_
diff --git a/src/libcef/browser/osr/host_display_client_osr.cc b/src/libcef/browser/osr/host_display_client_osr.cc
new file mode 100644
index 0000000..cc4321c
--- /dev/null
+++ b/src/libcef/browser/osr/host_display_client_osr.cc
@@ -0,0 +1,140 @@
+// Copyright 2019 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/host_display_client_osr.h"
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+
+#include "base/memory/shared_memory_mapping.h"
+#include "components/viz/common/resources/resource_format.h"
+#include "components/viz/common/resources/resource_sizes.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/src/core/SkDevice.h"
+#include "ui/gfx/skia_util.h"
+
+#if defined(OS_WIN)
+#include "skia/ext/skia_utils_win.h"
+#endif
+
+class CefLayeredWindowUpdaterOSR : public viz::mojom::LayeredWindowUpdater {
+ public:
+  CefLayeredWindowUpdaterOSR(
+      CefRenderWidgetHostViewOSR* const view,
+      mojo::PendingReceiver<viz::mojom::LayeredWindowUpdater> receiver);
+  ~CefLayeredWindowUpdaterOSR() override;
+
+  void SetActive(bool active);
+  const void* GetPixelMemory() const;
+  gfx::Size GetPixelSize() const;
+
+  // viz::mojom::LayeredWindowUpdater implementation.
+  void OnAllocatedSharedMemory(const gfx::Size& pixel_size,
+                               base::UnsafeSharedMemoryRegion region) override;
+  void Draw(const gfx::Rect& damage_rect, DrawCallback draw_callback) override;
+
+ private:
+  CefRenderWidgetHostViewOSR* const view_;
+  mojo::Receiver<viz::mojom::LayeredWindowUpdater> receiver_;
+  bool active_ = false;
+  base::WritableSharedMemoryMapping shared_memory_;
+  gfx::Size pixel_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefLayeredWindowUpdaterOSR);
+};
+
+CefLayeredWindowUpdaterOSR::CefLayeredWindowUpdaterOSR(
+    CefRenderWidgetHostViewOSR* const view,
+    mojo::PendingReceiver<viz::mojom::LayeredWindowUpdater> receiver)
+    : view_(view), receiver_(this, std::move(receiver)) {}
+
+CefLayeredWindowUpdaterOSR::~CefLayeredWindowUpdaterOSR() = default;
+
+void CefLayeredWindowUpdaterOSR::SetActive(bool active) {
+  active_ = active;
+}
+
+const void* CefLayeredWindowUpdaterOSR::GetPixelMemory() const {
+  return shared_memory_.memory();
+}
+
+gfx::Size CefLayeredWindowUpdaterOSR::GetPixelSize() const {
+  return pixel_size_;
+}
+
+void CefLayeredWindowUpdaterOSR::OnAllocatedSharedMemory(
+    const gfx::Size& pixel_size,
+    base::UnsafeSharedMemoryRegion region) {
+  // Make sure |pixel_size| is sane.
+  size_t expected_bytes;
+  bool size_result = viz::ResourceSizes::MaybeSizeInBytes(
+      pixel_size, viz::ResourceFormat::RGBA_8888, &expected_bytes);
+  if (!size_result)
+    return;
+
+  pixel_size_ = pixel_size;
+  shared_memory_ = region.Map();
+  DCHECK(shared_memory_.IsValid());
+}
+
+void CefLayeredWindowUpdaterOSR::Draw(const gfx::Rect& damage_rect,
+                                      DrawCallback draw_callback) {
+  if (active_) {
+    const void* memory = GetPixelMemory();
+    if (memory) {
+      view_->OnPaint(damage_rect, pixel_size_, memory);
+    } else {
+      LOG(WARNING) << "Failed to read pixels";
+    }
+  }
+
+  std::move(draw_callback).Run();
+}
+
+CefHostDisplayClientOSR::CefHostDisplayClientOSR(
+    CefRenderWidgetHostViewOSR* const view,
+    gfx::AcceleratedWidget widget)
+    : viz::HostDisplayClient(widget), view_(view) {}
+
+CefHostDisplayClientOSR::~CefHostDisplayClientOSR() {}
+
+void CefHostDisplayClientOSR::SetActive(bool active) {
+  active_ = active;
+  if (layered_window_updater_) {
+    layered_window_updater_->SetActive(active_);
+  }
+}
+
+const void* CefHostDisplayClientOSR::GetPixelMemory() const {
+  return layered_window_updater_ ? layered_window_updater_->GetPixelMemory()
+                                 : nullptr;
+}
+
+gfx::Size CefHostDisplayClientOSR::GetPixelSize() const {
+  return layered_window_updater_ ? layered_window_updater_->GetPixelSize()
+                                 : gfx::Size{};
+}
+
+void CefHostDisplayClientOSR::UseProxyOutputDevice(
+    UseProxyOutputDeviceCallback callback) {
+  std::move(callback).Run(true);
+}
+
+void CefHostDisplayClientOSR::CreateLayeredWindowUpdater(
+    mojo::PendingReceiver<viz::mojom::LayeredWindowUpdater> receiver) {
+  layered_window_updater_ =
+      std::make_unique<CefLayeredWindowUpdaterOSR>(view_, std::move(receiver));
+  layered_window_updater_->SetActive(active_);
+}
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+void CefHostDisplayClientOSR::DidCompleteSwapWithNewSize(
+    const gfx::Size& size) {}
+#endif
diff --git a/src/libcef/browser/osr/host_display_client_osr.h b/src/libcef/browser/osr/host_display_client_osr.h
new file mode 100644
index 0000000..7d724c6
--- /dev/null
+++ b/src/libcef/browser/osr/host_display_client_osr.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_HOST_DISPLAY_CLIENT_OSR_H_
+#define CEF_LIBCEF_BROWSER_OSR_HOST_DISPLAY_CLIENT_OSR_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "components/viz/host/host_display_client.h"
+#include "ui/gfx/native_widget_types.h"
+
+class CefLayeredWindowUpdaterOSR;
+class CefRenderWidgetHostViewOSR;
+
+class CefHostDisplayClientOSR : public viz::HostDisplayClient {
+ public:
+  CefHostDisplayClientOSR(CefRenderWidgetHostViewOSR* const view,
+                          gfx::AcceleratedWidget widget);
+  ~CefHostDisplayClientOSR() override;
+
+  void SetActive(bool active);
+  const void* GetPixelMemory() const;
+  gfx::Size GetPixelSize() const;
+
+ private:
+  // mojom::DisplayClient implementation.
+  void UseProxyOutputDevice(UseProxyOutputDeviceCallback callback) override;
+
+  void CreateLayeredWindowUpdater(
+      mojo::PendingReceiver<viz::mojom::LayeredWindowUpdater> receiver)
+      override;
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  void DidCompleteSwapWithNewSize(const gfx::Size& size) override;
+#endif
+
+  CefRenderWidgetHostViewOSR* const view_;
+  std::unique_ptr<CefLayeredWindowUpdaterOSR> layered_window_updater_;
+  bool active_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(CefHostDisplayClientOSR);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_OSR_HOST_DISPLAY_CLIENT_OSR_H_
diff --git a/src/libcef/browser/osr/motion_event_osr.cc b/src/libcef/browser/osr/motion_event_osr.cc
new file mode 100644
index 0000000..a4e6f14
--- /dev/null
+++ b/src/libcef/browser/osr/motion_event_osr.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/motion_event_osr.h"
+
+#include <algorithm>
+
+#include "ui/events/base_event_utils.h"
+#include "ui/events/gesture_detection/gesture_configuration.h"
+
+namespace {
+
+ui::MotionEvent::ToolType CefPointerTypeToMotionEventToolType(
+    cef_pointer_type_t pointer_type) {
+  switch (pointer_type) {
+    case CEF_POINTER_TYPE_TOUCH:
+      return ui::MotionEvent::ToolType::FINGER;
+    case CEF_POINTER_TYPE_MOUSE:
+      return ui::MotionEvent::ToolType::MOUSE;
+    case CEF_POINTER_TYPE_PEN:
+      return ui::MotionEvent::ToolType::STYLUS;
+    case CEF_POINTER_TYPE_ERASER:
+      return ui::MotionEvent::ToolType::ERASER;
+    case CEF_POINTER_TYPE_UNKNOWN:
+      return ui::MotionEvent::ToolType::UNKNOWN;
+  }
+  NOTREACHED();
+  return ui::MotionEvent::ToolType::UNKNOWN;
+}
+
+}  // namespace
+
+CefMotionEventOSR::CefMotionEventOSR() {
+  std::fill(id_map_, id_map_ + blink::WebTouchEvent::kTouchesLengthCap, -1);
+}
+
+CefMotionEventOSR::~CefMotionEventOSR() {}
+
+int CefMotionEventOSR::GetSourceDeviceId(size_t pointer_index) const {
+  if (IsValidIndex(pointer_index))
+    return pointer(pointer_index).source_device_id;
+  else
+    return -1;
+}
+
+// Returns true if the touch was valid.
+bool CefMotionEventOSR::OnTouch(const CefTouchEvent& touch) {
+  int id = LookupId(touch.id);
+  bool pointer_id_is_active = id != -1;
+
+  if (touch.type == CEF_TET_PRESSED && pointer_id_is_active) {
+    // Ignore pressed events for already active touches.
+    return false;
+  } else if (touch.type != CEF_TET_PRESSED && !pointer_id_is_active) {
+    // When a window begins capturing touch events, we could have an active
+    // touch stream transfered to us, resulting in touch move or touch up
+    // events without associated touch down events. Ignore them.
+    return false;
+  }
+
+  switch (touch.type) {
+    case CEF_TET_PRESSED:
+      id = AddId(touch.id);
+      if (id == -1)
+        return false;
+      if (!AddTouch(touch, id))
+        return false;
+      break;
+
+    case CEF_TET_MOVED: {
+      // Discard if touch is stationary.
+      int index = FindPointerIndexOfId(id);
+      if (IsValidIndex(index) &&
+          (touch.x == GetX(index) && touch.y == GetY(index))) {
+        return false;
+      }
+    }
+      [[fallthrough]];
+    // No break.
+    case CEF_TET_RELEASED:
+    case CEF_TET_CANCELLED:
+      // Removing these touch points needs to be postponed until after the
+      // MotionEvent has been dispatched. This cleanup occurs in
+      // CleanupRemovedTouchPoints.
+      UpdateTouch(touch, id);
+      break;
+  }
+
+  UpdateCachedAction(touch, id);
+  set_unique_event_id(ui::GetNextTouchEventId());
+  set_flags(touch.modifiers);
+  set_event_time(base::TimeTicks::Now());
+  return true;
+}
+
+// We can't cleanup removed touch points immediately upon receipt of a
+// TouchCancel or TouchRelease, as the MotionEvent needs to be able to report
+// information about those touch events. Once the MotionEvent has been
+// processed, we call CleanupRemovedTouchPoints to do the required
+// book-keeping.
+void CefMotionEventOSR::CleanupRemovedTouchPoints(const CefTouchEvent& event) {
+  if (event.type != CEF_TET_RELEASED && event.type != CEF_TET_CANCELLED) {
+    return;
+  }
+
+  DCHECK(GetPointerCount());
+
+  int id = LookupId(event.id);
+  int index_to_delete = FindPointerIndexOfId(id);
+  set_action_index(-1);
+  set_action(ui::MotionEvent::Action::NONE);
+  if (IsValidIndex(index_to_delete)) {
+    pointer(index_to_delete) = pointer(GetPointerCount() - 1);
+    PopPointer();
+    RemoveId(event.id);
+  }
+}
+
+// Reset unchanged touch point to StateStationary for touchmove and touchcancel.
+void CefMotionEventOSR::MarkUnchangedTouchPointsAsStationary(
+    blink::WebTouchEvent* event,
+    const CefTouchEvent& cef_event) {
+  int id = LookupId(cef_event.id);
+  if (event->GetType() == blink::WebInputEvent::kTouchMove ||
+      event->GetType() == blink::WebInputEvent::kTouchCancel) {
+    for (size_t i = 0; i < event->touches_length; ++i) {
+      if (event->touches[i].id != id)
+        event->touches[i].state = blink::WebTouchPoint::kStateStationary;
+    }
+  }
+}
+
+int CefMotionEventOSR::LookupId(int id) {
+  if (id == -1)
+    return -1;
+
+  for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
+    if (id_map_[i] == id) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+int CefMotionEventOSR::AddId(int id) {
+  if (id == -1 || LookupId(id) >= 0)
+    return -1;
+
+  for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
+    if (id_map_[i] == -1) {
+      id_map_[i] = id;
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+void CefMotionEventOSR::RemoveId(int id) {
+  for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
+    if (id_map_[i] == id) {
+      id_map_[i] = -1;
+    }
+  }
+}
+
+bool CefMotionEventOSR::AddTouch(const CefTouchEvent& touch, int id) {
+  if (GetPointerCount() == MotionEvent::MAX_TOUCH_POINT_COUNT)
+    return false;
+
+  PushPointer(GetPointerPropertiesFromTouchEvent(touch, id));
+  return true;
+}
+
+void CefMotionEventOSR::UpdateTouch(const CefTouchEvent& touch, int id) {
+  int index_to_update = FindPointerIndexOfId(id);
+  if (IsValidIndex(index_to_update))
+    pointer(index_to_update) = GetPointerPropertiesFromTouchEvent(touch, id);
+}
+
+void CefMotionEventOSR::UpdateCachedAction(const CefTouchEvent& touch, int id) {
+  DCHECK(GetPointerCount());
+  switch (touch.type) {
+    case CEF_TET_PRESSED:
+      if (GetPointerCount() == 1) {
+        set_action(ui::MotionEvent::Action::DOWN);
+      } else {
+        set_action(ui::MotionEvent::Action::POINTER_DOWN);
+        set_action_index(FindPointerIndexOfId(id));
+      }
+      break;
+    case CEF_TET_RELEASED:
+      if (GetPointerCount() == 1) {
+        set_action(ui::MotionEvent::Action::UP);
+      } else {
+        set_action(ui::MotionEvent::Action::POINTER_UP);
+        set_action_index(FindPointerIndexOfId(id));
+      }
+      break;
+    case CEF_TET_CANCELLED:
+      set_action(ui::MotionEvent::Action::CANCEL);
+      break;
+    case CEF_TET_MOVED:
+      set_action(ui::MotionEvent::Action::MOVE);
+      break;
+  }
+}
+
+bool CefMotionEventOSR::IsValidIndex(int index) const {
+  return (index >= 0) && (index < static_cast<int>(GetPointerCount()));
+}
+
+ui::PointerProperties CefMotionEventOSR::GetPointerPropertiesFromTouchEvent(
+    const CefTouchEvent& touch,
+    int id) {
+  ui::PointerProperties pointer_properties;
+  pointer_properties.x = touch.x;
+  pointer_properties.y = touch.y;
+  pointer_properties.raw_x = touch.x;
+  pointer_properties.raw_y = touch.y;
+  pointer_properties.id = id;
+  pointer_properties.pressure = touch.pressure;
+  pointer_properties.source_device_id = 0;
+
+  pointer_properties.SetAxesAndOrientation(touch.radius_x, touch.radius_y,
+                                           touch.rotation_angle);
+  if (!pointer_properties.touch_major) {
+    float default_size;
+    switch (touch.pointer_type) {
+      case CEF_POINTER_TYPE_PEN:
+      case CEF_POINTER_TYPE_ERASER:
+        // Default size for stylus events is 1x1.
+        default_size = 1;
+        break;
+      default:
+        default_size =
+            2.f * ui::GestureConfiguration::GetInstance()->default_radius();
+        break;
+    }
+    pointer_properties.touch_major = pointer_properties.touch_minor =
+        default_size;
+    pointer_properties.orientation = 0;
+  }
+
+  pointer_properties.tool_type =
+      CefPointerTypeToMotionEventToolType(touch.pointer_type);
+
+  return pointer_properties;
+}
diff --git a/src/libcef/browser/osr/motion_event_osr.h b/src/libcef/browser/osr/motion_event_osr.h
new file mode 100644
index 0000000..eefac5e
--- /dev/null
+++ b/src/libcef/browser/osr/motion_event_osr.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_MOTION_EVENT_OSR_H_
+#define CEF_LIBCEF_BROWSER_OSR_MOTION_EVENT_OSR_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+#include "third_party/blink/public/common/input/web_touch_event.h"
+#include "ui/events/gesture_detection/motion_event_generic.h"
+
+// Implementation of MotionEvent which takes a stream of CefTouchEvents.
+// This class is based on ui::MotionEventAura.
+class CefMotionEventOSR : public ui::MotionEventGeneric {
+ public:
+  CefMotionEventOSR();
+  ~CefMotionEventOSR() override;
+
+  int GetSourceDeviceId(size_t pointer_index) const override;
+
+  // Returns true if the touch was valid.
+  bool OnTouch(const CefTouchEvent& touch);
+
+  // We can't cleanup removed touch points immediately upon receipt of a
+  // TouchCancel or TouchRelease, as the MotionEvent needs to be able to report
+  // information about those touch events. Once the MotionEvent has been
+  // processed, we call CleanupRemovedTouchPoints to do the required
+  // book-keeping.
+  void CleanupRemovedTouchPoints(const CefTouchEvent& event);
+
+  // Reset unchanged touch point to StateStationary for touchmove and
+  // touchcancel to make sure only send one ack per WebTouchEvent.
+  void MarkUnchangedTouchPointsAsStationary(blink::WebTouchEvent* event,
+                                            const CefTouchEvent& cef_event);
+
+ private:
+  // Chromium can't cope with touch ids >31, so let's map the incoming
+  // ids to a safe range.
+  int id_map_[blink::WebTouchEvent::kTouchesLengthCap];
+
+  int LookupId(int id);
+  int AddId(int id);
+  void RemoveId(int id);
+
+  bool AddTouch(const CefTouchEvent& touch, int id);
+  void UpdateTouch(const CefTouchEvent& touch, int id);
+  void UpdateCachedAction(const CefTouchEvent& touch, int id);
+  bool IsValidIndex(int index) const;
+  ui::PointerProperties GetPointerPropertiesFromTouchEvent(
+      const CefTouchEvent& touch,
+      int id);
+
+  DISALLOW_COPY_AND_ASSIGN(CefMotionEventOSR);
+};
+
+#endif
diff --git a/src/libcef/browser/osr/osr_accessibility_util.cc b/src/libcef/browser/osr/osr_accessibility_util.cc
new file mode 100644
index 0000000..bffb273
--- /dev/null
+++ b/src/libcef/browser/osr/osr_accessibility_util.cc
@@ -0,0 +1,518 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/osr/osr_accessibility_util.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include "base/json/string_escape.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/browser/ax_event_notification_details.h"
+#include "ui/accessibility/ax_enum_util.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_text_utils.h"
+#include "ui/accessibility/ax_tree_update.h"
+#include "ui/gfx/transform.h"
+
+namespace {
+using ui::ToString;
+
+template <typename T>
+CefRefPtr<CefListValue> ToCefValue(const std::vector<T>& vecData);
+
+template <>
+CefRefPtr<CefListValue> ToCefValue<int>(const std::vector<int>& vecData) {
+  CefRefPtr<CefListValue> value = CefListValue::Create();
+  for (size_t i = 0; i < vecData.size(); i++)
+    value->SetInt(i, vecData[i]);
+
+  return value;
+}
+
+// Helper function for AXNodeData::ToCefValue - Converts AXState attributes to
+// CefListValue.
+CefRefPtr<CefListValue> ToCefValue(uint32_t state) {
+  CefRefPtr<CefListValue> value = CefListValue::Create();
+
+  int index = 0;
+  // Iterate and find which states are set.
+  for (unsigned i = static_cast<unsigned>(ax::mojom::Role::kMinValue) + 1;
+       i <= static_cast<unsigned>(ax::mojom::Role::kMaxValue); i++) {
+    if (state & (1 << i))
+      value->SetString(index++, ToString(static_cast<ax::mojom::State>(i)));
+  }
+  return value;
+}
+
+// Helper function for AXNodeData::ToCefValue - converts GfxRect to
+// CefDictionaryValue.
+CefRefPtr<CefDictionaryValue> ToCefValue(const gfx::RectF& bounds) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+  value->SetDouble("x", bounds.x());
+  value->SetDouble("y", bounds.y());
+  value->SetDouble("width", bounds.width());
+  value->SetDouble("height", bounds.height());
+  return value;
+}
+
+// Helper Functor for adding AxNodeData::attributes to AXNodeData::ToCefValue.
+struct PopulateAxNodeAttributes {
+  CefRefPtr<CefDictionaryValue> attributes;
+
+  explicit PopulateAxNodeAttributes(CefRefPtr<CefDictionaryValue> attrs)
+      : attributes(attrs) {}
+
+  // Int Attributes
+  void operator()(const std::pair<ax::mojom::IntAttribute, int32_t> attr) {
+    if (attr.first == ax::mojom::IntAttribute::kNone)
+      return;
+
+    switch (attr.first) {
+      case ax::mojom::IntAttribute::kNone:
+        break;
+      case ax::mojom::IntAttribute::kScrollX:
+      case ax::mojom::IntAttribute::kScrollXMin:
+      case ax::mojom::IntAttribute::kScrollXMax:
+      case ax::mojom::IntAttribute::kScrollY:
+      case ax::mojom::IntAttribute::kScrollYMin:
+      case ax::mojom::IntAttribute::kScrollYMax:
+      case ax::mojom::IntAttribute::kHasPopup:
+      case ax::mojom::IntAttribute::kHierarchicalLevel:
+      case ax::mojom::IntAttribute::kTextSelStart:
+      case ax::mojom::IntAttribute::kTextSelEnd:
+      case ax::mojom::IntAttribute::kAriaColumnCount:
+      case ax::mojom::IntAttribute::kAriaCellColumnIndex:
+      case ax::mojom::IntAttribute::kAriaRowCount:
+      case ax::mojom::IntAttribute::kAriaCellRowIndex:
+      case ax::mojom::IntAttribute::kTableRowCount:
+      case ax::mojom::IntAttribute::kTableColumnCount:
+      case ax::mojom::IntAttribute::kTableCellColumnIndex:
+      case ax::mojom::IntAttribute::kTableCellRowIndex:
+      case ax::mojom::IntAttribute::kTableCellColumnSpan:
+      case ax::mojom::IntAttribute::kTableCellRowSpan:
+      case ax::mojom::IntAttribute::kTableColumnHeaderId:
+      case ax::mojom::IntAttribute::kTableColumnIndex:
+      case ax::mojom::IntAttribute::kTableHeaderId:
+      case ax::mojom::IntAttribute::kTableRowHeaderId:
+      case ax::mojom::IntAttribute::kTableRowIndex:
+      case ax::mojom::IntAttribute::kActivedescendantId:
+      case ax::mojom::IntAttribute::kInPageLinkTargetId:
+      case ax::mojom::IntAttribute::kErrormessageId:
+      case ax::mojom::IntAttribute::kDOMNodeId:
+      case ax::mojom::IntAttribute::kDropeffect:
+      case ax::mojom::IntAttribute::kMemberOfId:
+      case ax::mojom::IntAttribute::kNextFocusId:
+      case ax::mojom::IntAttribute::kNextOnLineId:
+      case ax::mojom::IntAttribute::kPreviousFocusId:
+      case ax::mojom::IntAttribute::kPreviousOnLineId:
+      case ax::mojom::IntAttribute::kSetSize:
+      case ax::mojom::IntAttribute::kPosInSet:
+      case ax::mojom::IntAttribute::kPopupForId:
+        attributes->SetInt(ToString(attr.first), attr.second);
+        break;
+      case ax::mojom::IntAttribute::kDefaultActionVerb:
+        attributes->SetString(
+            ToString(attr.first),
+            ui::ActionVerbToUnlocalizedString(
+                static_cast<ax::mojom::DefaultActionVerb>(attr.second)));
+        break;
+      case ax::mojom::IntAttribute::kInvalidState: {
+        auto state = static_cast<ax::mojom::InvalidState>(attr.second);
+        if (ax::mojom::InvalidState::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kCheckedState: {
+        auto state = static_cast<ax::mojom::CheckedState>(attr.second);
+        if (ax::mojom::CheckedState::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kRestriction:
+        attributes->SetString(
+            ToString(attr.first),
+            ToString(static_cast<ax::mojom::Restriction>(attr.second)));
+        break;
+      case ax::mojom::IntAttribute::kListStyle: {
+        auto state = static_cast<ax::mojom::ListStyle>(attr.second);
+        if (ax::mojom::ListStyle::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kSortDirection: {
+        auto state = static_cast<ax::mojom::SortDirection>(attr.second);
+        if (ax::mojom::SortDirection::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kNameFrom:
+        attributes->SetString(
+            ToString(attr.first),
+            ToString(static_cast<ax::mojom::NameFrom>(attr.second)));
+        break;
+      case ax::mojom::IntAttribute::kColorValue:
+      case ax::mojom::IntAttribute::kBackgroundColor:
+      case ax::mojom::IntAttribute::kColor:
+        attributes->SetString(ToString(attr.first),
+                              base::StringPrintf("0x%X", attr.second));
+        break;
+      case ax::mojom::IntAttribute::kDescriptionFrom:
+        attributes->SetString(
+            ToString(attr.first),
+            ToString(static_cast<ax::mojom::DescriptionFrom>(attr.second)));
+        break;
+      case ax::mojom::IntAttribute::kAriaCurrentState: {
+        auto state = static_cast<ax::mojom::AriaCurrentState>(attr.second);
+        if (ax::mojom::AriaCurrentState::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kTextDirection: {
+        auto state = static_cast<ax::mojom::TextDirection>(attr.second);
+        if (ax::mojom::TextDirection::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kTextPosition: {
+        auto state = static_cast<ax::mojom::TextPosition>(attr.second);
+        if (ax::mojom::TextPosition::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kTextStyle: {
+        static ax::mojom::TextStyle textStyleArr[] = {
+            ax::mojom::TextStyle::kBold, ax::mojom::TextStyle::kItalic,
+            ax::mojom::TextStyle::kUnderline,
+            ax::mojom::TextStyle::kLineThrough,
+            ax::mojom::TextStyle::kOverline};
+
+        CefRefPtr<CefListValue> list = CefListValue::Create();
+        int index = 0;
+        // Iterate and find which states are set.
+        for (unsigned i = 0; i < base::size(textStyleArr); i++) {
+          if (attr.second & static_cast<int>(textStyleArr[i]))
+            list->SetString(index++, ToString(textStyleArr[i]));
+        }
+        attributes->SetList(ToString(attr.first), list);
+      } break;
+      case ax::mojom::IntAttribute::kTextOverlineStyle:
+      case ax::mojom::IntAttribute::kTextStrikethroughStyle:
+      case ax::mojom::IntAttribute::kTextUnderlineStyle: {
+        auto state = static_cast<ax::mojom::TextDecorationStyle>(attr.second);
+        if (ax::mojom::TextDecorationStyle::kNone != state) {
+          attributes->SetString(ToString(attr.first), ToString(state));
+        }
+      } break;
+      case ax::mojom::IntAttribute::kAriaCellColumnSpan:
+      case ax::mojom::IntAttribute::kAriaCellRowSpan:
+      case ax::mojom::IntAttribute::kImageAnnotationStatus: {
+        // TODO(cef): Implement support for Image Annotation Status,
+        // kAriaCellColumnSpan and kAriaCellRowSpan
+      } break;
+    }
+  }
+
+  // Set Bool Attributes.
+  void operator()(const std::pair<ax::mojom::BoolAttribute, bool> attr) {
+    if (attr.first != ax::mojom::BoolAttribute::kNone)
+      attributes->SetBool(ToString(attr.first), attr.second);
+  }
+  // Set String Attributes.
+  void operator()(
+      const std::pair<ax::mojom::StringAttribute, std::string>& attr) {
+    if (attr.first != ax::mojom::StringAttribute::kNone)
+      attributes->SetString(ToString(attr.first), attr.second);
+  }
+  // Set Float attributes.
+  void operator()(const std::pair<ax::mojom::FloatAttribute, float>& attr) {
+    if (attr.first != ax::mojom::FloatAttribute::kNone)
+      attributes->SetDouble(ToString(attr.first), attr.second);
+  }
+
+  // Set Int list attributes.
+  void operator()(const std::pair<ax::mojom::IntListAttribute,
+                                  std::vector<int32_t>>& attr) {
+    if (attr.first != ax::mojom::IntListAttribute::kNone) {
+      CefRefPtr<CefListValue> list;
+
+      if (ax::mojom::IntListAttribute::kMarkerTypes == attr.first) {
+        list = CefListValue::Create();
+        int index = 0;
+        for (size_t i = 0; i < attr.second.size(); ++i) {
+          auto type = static_cast<ax::mojom::MarkerType>(attr.second[i]);
+
+          if (type == ax::mojom::MarkerType::kNone)
+            continue;
+
+          static ax::mojom::MarkerType marktypeArr[] = {
+              ax::mojom::MarkerType::kSpelling, ax::mojom::MarkerType::kGrammar,
+              ax::mojom::MarkerType::kTextMatch};
+
+          // Iterate and find which markers are set.
+          for (unsigned j = 0; j < base::size(marktypeArr); j++) {
+            if (attr.second[i] & static_cast<int>(marktypeArr[j]))
+              list->SetString(index++, ToString(marktypeArr[j]));
+          }
+        }
+      } else {
+        list = ToCefValue(attr.second);
+      }
+      attributes->SetList(ToString(attr.first), list);
+    }
+  }
+};
+
+// Converts AXNodeData to CefDictionaryValue(like AXNodeData::ToString).
+CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXNodeData& node) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+
+  if (node.id != -1)
+    value->SetInt("id", node.id);
+
+  value->SetString("role", ToString(node.role));
+  value->SetList("state", ToCefValue(node.state));
+
+  if (node.relative_bounds.offset_container_id != -1) {
+    value->SetInt("offset_container_id",
+                  node.relative_bounds.offset_container_id);
+  }
+
+  value->SetDictionary("location", ToCefValue(node.relative_bounds.bounds));
+
+  // Transform matrix is private, so we set the string that Clients can parse
+  // and use if needed.
+  if (node.relative_bounds.transform &&
+      !node.relative_bounds.transform->IsIdentity()) {
+    value->SetString("transform", node.relative_bounds.transform->ToString());
+  }
+
+  if (!node.child_ids.empty()) {
+    value->SetList("child_ids", ToCefValue(node.child_ids));
+  }
+
+  CefRefPtr<CefListValue> actions_strings;
+  size_t actions_idx = 0;
+  for (int action_index = static_cast<int>(ax::mojom::Action::kMinValue) + 1;
+       action_index <= static_cast<int>(ax::mojom::Action::kMaxValue);
+       ++action_index) {
+    auto action = static_cast<ax::mojom::Action>(action_index);
+    if (node.HasAction(action)) {
+      if (!actions_strings)
+        actions_strings = CefListValue::Create();
+      actions_strings->SetString(actions_idx++, ToString(action));
+    }
+  }
+  if (actions_strings)
+    value->SetList("actions", actions_strings);
+
+  CefRefPtr<CefDictionaryValue> attributes = CefDictionaryValue::Create();
+  PopulateAxNodeAttributes func(attributes);
+
+  // Poupulate Int Attributes.
+  std::for_each(node.int_attributes.begin(), node.int_attributes.end(), func);
+
+  // Poupulate String Attributes.
+  std::for_each(node.string_attributes.begin(), node.string_attributes.end(),
+                func);
+
+  // Poupulate Float Attributes.
+  std::for_each(node.float_attributes.begin(), node.float_attributes.end(),
+                func);
+
+  // Poupulate Bool Attributes.
+  std::for_each(node.bool_attributes.begin(), node.bool_attributes.end(), func);
+
+  // Populate int list attributes.
+  std::for_each(node.intlist_attributes.begin(), node.intlist_attributes.end(),
+                func);
+
+  value->SetDictionary("attributes", attributes);
+
+  return value;
+}
+
+// Converts AXTreeData to CefDictionaryValue(like AXTreeData::ToString).
+CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXTreeData& treeData) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+
+  if (!treeData.tree_id.ToString().empty())
+    value->SetString("tree_id", treeData.tree_id.ToString());
+
+  if (!treeData.parent_tree_id.ToString().empty())
+    value->SetString("parent_tree_id", treeData.parent_tree_id.ToString());
+
+  if (!treeData.focused_tree_id.ToString().empty())
+    value->SetString("focused_tree_id", treeData.focused_tree_id.ToString());
+
+  if (!treeData.doctype.empty())
+    value->SetString("doctype", treeData.doctype);
+
+  value->SetBool("loaded", treeData.loaded);
+
+  if (treeData.loading_progress != 0.0)
+    value->SetDouble("loading_progress", treeData.loading_progress);
+
+  if (!treeData.mimetype.empty())
+    value->SetString("mimetype", treeData.mimetype);
+  if (!treeData.url.empty())
+    value->SetString("url", treeData.url);
+  if (!treeData.title.empty())
+    value->SetString("title", treeData.title);
+
+  if (treeData.sel_anchor_object_id != -1) {
+    value->SetInt("sel_anchor_object_id", treeData.sel_anchor_object_id);
+    value->SetInt("sel_anchor_offset", treeData.sel_anchor_offset);
+    value->SetString("sel_anchor_affinity",
+                     ToString(treeData.sel_anchor_affinity));
+  }
+  if (treeData.sel_focus_object_id != -1) {
+    value->SetInt("sel_focus_object_id", treeData.sel_anchor_object_id);
+    value->SetInt("sel_focus_offset", treeData.sel_anchor_offset);
+    value->SetString("sel_focus_affinity",
+                     ToString(treeData.sel_anchor_affinity));
+  }
+
+  if (treeData.focus_id != -1)
+    value->SetInt("focus_id", treeData.focus_id);
+
+  return value;
+}
+
+// Converts AXTreeUpdate to CefDictionaryValue(like AXTreeUpdate::ToString).
+CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXTreeUpdate& update) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+
+  if (update.has_tree_data) {
+    value->SetBool("has_tree_data", true);
+    value->SetDictionary("tree_data", ToCefValue(update.tree_data));
+  }
+
+  if (update.node_id_to_clear != 0)
+    value->SetInt("node_id_to_clear", update.node_id_to_clear);
+
+  if (update.root_id != 0)
+    value->SetInt("root_id", update.root_id);
+
+  value->SetList("nodes", ToCefValue(update.nodes));
+
+  return value;
+}
+
+// Converts AXEvent to CefDictionaryValue.
+CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXEvent& event) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+
+  if (event.event_type != ax::mojom::Event::kNone)
+    value->SetString("event_type", ToString(event.event_type));
+
+  if (event.id != -1)
+    value->SetInt("id", event.id);
+
+  if (event.event_from != ax::mojom::EventFrom::kNone)
+    value->SetString("event_from", ToString(event.event_from));
+
+  if (event.action_request_id != -1)
+    value->SetInt("action_request_id", event.action_request_id);
+
+  return value;
+}
+
+// Convert AXEventNotificationDetails to CefDictionaryValue.
+CefRefPtr<CefDictionaryValue> ToCefValue(
+    const content::AXEventNotificationDetails& eventData) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+
+  if (!eventData.ax_tree_id.ToString().empty())
+    value->SetString("ax_tree_id", eventData.ax_tree_id.ToString());
+
+  if (eventData.updates.size() > 0) {
+    CefRefPtr<CefListValue> updates = CefListValue::Create();
+    updates->SetSize(eventData.updates.size());
+    size_t i = 0;
+    for (const auto& update : eventData.updates) {
+      updates->SetDictionary(i++, ToCefValue(update));
+    }
+    value->SetList("updates", updates);
+  }
+
+  if (eventData.events.size() > 0) {
+    CefRefPtr<CefListValue> events = CefListValue::Create();
+    events->SetSize(eventData.events.size());
+    size_t i = 0;
+    for (const auto& event : eventData.events) {
+      events->SetDictionary(i++, ToCefValue(event));
+    }
+    value->SetList("events", events);
+  }
+
+  return value;
+}
+
+// Convert AXRelativeBounds to CefDictionaryValue. Similar to
+// AXRelativeBounds::ToString. See that for more details
+CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXRelativeBounds& location) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+
+  if (location.offset_container_id != -1)
+    value->SetInt("offset_container_id", location.offset_container_id);
+
+  value->SetDictionary("bounds", ToCefValue(location.bounds));
+
+  // Transform matrix is private, so we set the string that Clients can parse
+  // and use if needed.
+  if (location.transform && !location.transform->IsIdentity())
+    value->SetString("transform", location.transform->ToString());
+
+  return value;
+}
+
+// Convert AXLocationChangeNotificationDetails to CefDictionaryValue.
+CefRefPtr<CefDictionaryValue> ToCefValue(
+    const content::AXLocationChangeNotificationDetails& locData) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+
+  if (locData.id != -1)
+    value->SetInt("id", locData.id);
+
+  if (!locData.ax_tree_id.ToString().empty())
+    value->SetString("ax_tree_id", locData.ax_tree_id.ToString());
+
+  value->SetDictionary("new_location", ToCefValue(locData.new_location));
+
+  return value;
+}
+
+template <typename T>
+CefRefPtr<CefListValue> ToCefValue(const std::vector<T>& vecData) {
+  CefRefPtr<CefListValue> value = CefListValue::Create();
+
+  for (size_t i = 0; i < vecData.size(); i++)
+    value->SetDictionary(i, ToCefValue(vecData[i]));
+
+  return value;
+}
+
+}  // namespace
+
+namespace osr_accessibility_util {
+
+CefRefPtr<CefValue> ParseAccessibilityEventData(
+    const content::AXEventNotificationDetails& data) {
+  CefRefPtr<CefValue> value = CefValue::Create();
+  value->SetDictionary(ToCefValue(data));
+  return value;
+}
+
+CefRefPtr<CefValue> ParseAccessibilityLocationData(
+    const std::vector<content::AXLocationChangeNotificationDetails>& data) {
+  CefRefPtr<CefValue> value = CefValue::Create();
+  value->SetList(ToCefValue(data));
+  return value;
+}
+
+}  // namespace osr_accessibility_util
diff --git a/src/libcef/browser/osr/osr_accessibility_util.h b/src/libcef/browser/osr/osr_accessibility_util.h
new file mode 100644
index 0000000..486c91a
--- /dev/null
+++ b/src/libcef/browser/osr/osr_accessibility_util.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_ACCESSIBILITY_UTIL_H_
+#define CEF_LIBCEF_BROWSER_OSR_ACCESSIBILITY_UTIL_H_
+#pragma once
+
+#include <vector>
+#include "include/cef_values.h"
+
+namespace content {
+struct AXEventNotificationDetails;
+struct AXLocationChangeNotificationDetails;
+}  // namespace content
+
+namespace osr_accessibility_util {
+
+// Convert Accessibility Event and location updates to CefValue, which may be
+// consumed or serialized with CefJSONWrite.
+CefRefPtr<CefValue> ParseAccessibilityEventData(
+    const content::AXEventNotificationDetails& data);
+
+CefRefPtr<CefValue> ParseAccessibilityLocationData(
+    const std::vector<content::AXLocationChangeNotificationDetails>& data);
+
+}  // namespace osr_accessibility_util
+
+#endif  // CEF_LIBCEF_BROWSER_ACCESSIBILITY_UTIL_H_
diff --git a/src/libcef/browser/osr/osr_util.cc b/src/libcef/browser/osr/osr_util.cc
new file mode 100644
index 0000000..6068889
--- /dev/null
+++ b/src/libcef/browser/osr/osr_util.cc
@@ -0,0 +1,25 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/osr_util.h"
+
+namespace osr_util {
+
+namespace {
+
+// The rate at which new calls to OnPaint will be generated.
+const int kDefaultFrameRate = 30;
+const int kMaximumFrameRate = 60;
+
+}  // namespace
+
+int ClampFrameRate(int frame_rate) {
+  if (frame_rate < 1)
+    return kDefaultFrameRate;
+  else if (frame_rate > kMaximumFrameRate)
+    return kMaximumFrameRate;
+  return frame_rate;
+}
+
+}  // namespace osr_util
diff --git a/src/libcef/browser/osr/osr_util.h b/src/libcef/browser/osr/osr_util.h
new file mode 100644
index 0000000..e04039a
--- /dev/null
+++ b/src/libcef/browser/osr/osr_util.h
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_OSR_UTIL_H_
+#define CEF_LIBCEF_BROWSER_OSR_OSR_UTIL_H_
+
+namespace osr_util {
+
+int ClampFrameRate(int frame_rate);
+
+}  // namespace osr_util
+
+#endif  // CEF_LIBCEF_BROWSER_OSR_OSR_UTIL_H_
diff --git a/src/libcef/browser/osr/render_widget_host_view_osr.cc b/src/libcef/browser/osr/render_widget_host_view_osr.cc
new file mode 100644
index 0000000..77e6447
--- /dev/null
+++ b/src/libcef/browser/osr/render_widget_host_view_osr.cc
@@ -0,0 +1,1677 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/osr/osr_util.h"
+#include "libcef/browser/osr/synthetic_gesture_target_osr.h"
+#include "libcef/browser/osr/video_consumer_osr.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "cc/base/switches.h"
+#include "components/viz/common/features.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+#include "components/viz/common/frame_sinks/copy_output_request.h"
+#include "components/viz/common/frame_sinks/delay_based_time_source.h"
+#include "components/viz/common/switches.h"
+#include "content/browser/bad_message.h"
+#include "content/browser/gpu/gpu_data_manager_impl.h"
+#include "content/browser/renderer_host/cursor_manager.h"
+#include "content/browser/renderer_host/delegated_frame_host.h"
+#include "content/browser/renderer_host/dip_util.h"
+#include "content/browser/renderer_host/input/motion_event_web.h"
+#include "content/browser/renderer_host/input/synthetic_gesture_target_base.h"
+#include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
+#include "content/common/content_switches_internal.h"
+#include "content/common/input_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_factory.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/common/content_switches.h"
+#include "media/base/video_frame.h"
+#include "ui/compositor/compositor.h"
+#include "ui/events/blink/blink_event_util.h"
+#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
+#include "ui/events/gesture_detection/motion_event.h"
+#include "ui/gfx/geometry/dip_util.h"
+#include "ui/gfx/geometry/size_conversions.h"
+
+namespace {
+
+// The maximum number of damage rects to cache for outstanding frame requests
+// (for OnAcceleratedPaint).
+const size_t kMaxDamageRects = 10;
+
+const float kDefaultScaleFactor = 1.0;
+
+content::ScreenInfo ScreenInfoFrom(const CefScreenInfo& src) {
+  content::ScreenInfo screenInfo;
+  screenInfo.device_scale_factor = src.device_scale_factor;
+  screenInfo.depth = src.depth;
+  screenInfo.depth_per_component = src.depth_per_component;
+  screenInfo.is_monochrome = src.is_monochrome ? true : false;
+  screenInfo.rect =
+      gfx::Rect(src.rect.x, src.rect.y, src.rect.width, src.rect.height);
+  screenInfo.available_rect =
+      gfx::Rect(src.available_rect.x, src.available_rect.y,
+                src.available_rect.width, src.available_rect.height);
+
+  return screenInfo;
+}
+
+class CefDelegatedFrameHostClient : public content::DelegatedFrameHostClient {
+ public:
+  explicit CefDelegatedFrameHostClient(CefRenderWidgetHostViewOSR* view)
+      : view_(view) {}
+
+  ui::Layer* DelegatedFrameHostGetLayer() const override {
+    return view_->GetRootLayer();
+  }
+
+  bool DelegatedFrameHostIsVisible() const override {
+    // Called indirectly from DelegatedFrameHost::WasShown.
+    return view_->IsShowing();
+  }
+
+  SkColor DelegatedFrameHostGetGutterColor() const override {
+    // When making an element on the page fullscreen the element's background
+    // may not match the page's, so use black as the gutter color to avoid
+    // flashes of brighter colors during the transition.
+    if (view_->render_widget_host()->delegate() &&
+        view_->render_widget_host()->delegate()->IsFullscreenForCurrentTab()) {
+      return SK_ColorBLACK;
+    }
+    return *view_->GetBackgroundColor();
+  }
+
+  void OnFrameTokenChanged(uint32_t frame_token) override {
+    view_->render_widget_host()->DidProcessFrame(frame_token);
+  }
+
+  float GetDeviceScaleFactor() const override {
+    return view_->GetDeviceScaleFactor();
+  }
+
+  std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override {
+    return view_->render_widget_host()->CollectSurfaceIdsForEviction();
+  }
+
+  void InvalidateLocalSurfaceIdOnEviction() override {}
+
+  bool ShouldShowStaleContentOnEviction() override { return false; }
+
+ private:
+  CefRenderWidgetHostViewOSR* const view_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefDelegatedFrameHostClient);
+};
+
+ui::GestureProvider::Config CreateGestureProviderConfig() {
+  ui::GestureProvider::Config config = ui::GetGestureProviderConfig(
+      ui::GestureProviderConfigType::CURRENT_PLATFORM);
+  return config;
+}
+
+ui::LatencyInfo CreateLatencyInfo(const blink::WebInputEvent& event) {
+  ui::LatencyInfo latency_info;
+  // The latency number should only be added if the timestamp is valid.
+  base::TimeTicks time = event.TimeStamp();
+  if (!time.is_null()) {
+    latency_info.AddLatencyNumberWithTimestamp(
+        ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time);
+  }
+  return latency_info;
+}
+
+gfx::Rect GetViewBounds(CefBrowserHostImpl* browser) {
+  if (!browser)
+    return gfx::Rect();
+
+  CefRect rc;
+  CefRefPtr<CefRenderHandler> handler =
+      browser->GetClient()->GetRenderHandler();
+  CHECK(handler);
+
+  handler->GetViewRect(browser, rc);
+  CHECK_GT(rc.width, 0);
+  CHECK_GT(rc.height, 0);
+
+  return gfx::Rect(rc.x, rc.y, rc.width, rc.height);
+}
+
+float GetDeviceScaleFactor(CefBrowserHostImpl* browser) {
+  if (!browser)
+    return kDefaultScaleFactor;
+
+  CefScreenInfo screen_info(kDefaultScaleFactor, 0, 0, false, CefRect(),
+                            CefRect());
+  CefRefPtr<CefRenderHandler> handler = browser->client()->GetRenderHandler();
+  CHECK(handler);
+  if (!handler->GetScreenInfo(browser, screen_info))
+    return kDefaultScaleFactor;
+
+  return screen_info.device_scale_factor;
+}
+
+ui::ImeTextSpan::UnderlineStyle GetImeUnderlineStyle(
+    cef_composition_underline_style_t style) {
+  switch (style) {
+    case CEF_CUS_SOLID:
+      return ui::ImeTextSpan::UnderlineStyle::kSolid;
+    case CEF_CUS_DOT:
+      return ui::ImeTextSpan::UnderlineStyle::kDot;
+    case CEF_CUS_DASH:
+      return ui::ImeTextSpan::UnderlineStyle::kDash;
+    case CEF_CUS_NONE:
+      return ui::ImeTextSpan::UnderlineStyle::kNone;
+  }
+
+  NOTREACHED();
+  return ui::ImeTextSpan::UnderlineStyle::kSolid;
+}
+
+}  // namespace
+
+CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR(
+    SkColor background_color,
+    bool use_shared_texture,
+    bool use_external_begin_frame,
+    content::RenderWidgetHost* widget,
+    CefRenderWidgetHostViewOSR* parent_host_view)
+    : content::RenderWidgetHostViewBase(widget),
+      background_color_(background_color),
+      render_widget_host_(content::RenderWidgetHostImpl::From(widget)),
+      has_parent_(parent_host_view != nullptr),
+      parent_host_view_(parent_host_view),
+      pinch_zoom_enabled_(content::IsPinchToZoomEnabled()),
+      mouse_wheel_phase_handler_(this),
+      gesture_provider_(CreateGestureProviderConfig(), this),
+      weak_ptr_factory_(this) {
+  DCHECK(render_widget_host_);
+  DCHECK(!render_widget_host_->GetView());
+
+  current_device_scale_factor_ = kDefaultScaleFactor;
+
+  if (parent_host_view_) {
+    browser_impl_ = parent_host_view_->browser_impl();
+    DCHECK(browser_impl_);
+  } else if (content::RenderViewHost::From(render_widget_host_)) {
+    // CefBrowserHostImpl might not be created at this time for popups.
+    browser_impl_ = CefBrowserHostImpl::GetBrowserForHost(
+        content::RenderViewHost::From(render_widget_host_));
+  }
+
+  delegated_frame_host_client_.reset(new CefDelegatedFrameHostClient(this));
+
+  // Matching the attributes from BrowserCompositorMac.
+  delegated_frame_host_ = std::make_unique<content::DelegatedFrameHost>(
+      AllocateFrameSinkId(), delegated_frame_host_client_.get(),
+      false /* should_register_frame_sink_id */);
+
+  root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
+
+  bool opaque = SkColorGetA(background_color_) == SK_AlphaOPAQUE;
+  GetRootLayer()->SetFillsBoundsOpaquely(opaque);
+  GetRootLayer()->SetColor(background_color_);
+
+  external_begin_frame_enabled_ = use_external_begin_frame;
+
+  auto context_factory = content::GetContextFactory();
+
+  // Matching the attributes from RecyclableCompositorMac.
+  compositor_.reset(new ui::Compositor(
+      context_factory->AllocateFrameSinkId(), context_factory,
+      base::ThreadTaskRunnerHandle::Get(), false /* enable_pixel_canvas */,
+      use_external_begin_frame));
+  compositor_->SetAcceleratedWidget(gfx::kNullAcceleratedWidget);
+
+  compositor_->SetDelegate(this);
+  compositor_->SetRootLayer(root_layer_.get());
+  compositor_->AddChildFrameSink(GetFrameSinkId());
+
+  content::RenderWidgetHostImpl* render_widget_host_impl =
+      content::RenderWidgetHostImpl::From(render_widget_host_);
+  if (render_widget_host_impl)
+    render_widget_host_impl->SetCompositorForFlingScheduler(compositor_.get());
+
+  cursor_manager_.reset(new content::CursorManager(this));
+
+  // This may result in a call to GetFrameSinkId().
+  render_widget_host_->SetView(this);
+
+  if (GetTextInputManager())
+    GetTextInputManager()->AddObserver(this);
+
+  if (render_widget_host_->delegate() &&
+      render_widget_host_->delegate()->GetInputEventRouter()) {
+    render_widget_host_->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner(
+        GetFrameSinkId(), this);
+  }
+
+  if (browser_impl_ && !parent_host_view_) {
+    // For child/popup views this will be called from the associated InitAs*()
+    // method.
+    SetRootLayerSize(false /* force */);
+    if (!render_widget_host_->is_hidden())
+      Show();
+  }
+}
+
+CefRenderWidgetHostViewOSR::~CefRenderWidgetHostViewOSR() {
+  // Marking the DelegatedFrameHost as removed from the window hierarchy is
+  // necessary to remove all connections to its old ui::Compositor.
+  if (is_showing_) {
+    delegated_frame_host_->WasHidden(
+        content::DelegatedFrameHost::HiddenCause::kOther);
+  }
+  delegated_frame_host_->DetachFromCompositor();
+
+  delegated_frame_host_.reset(nullptr);
+  compositor_.reset(nullptr);
+  root_layer_.reset(nullptr);
+
+  DCHECK(!parent_host_view_);
+  DCHECK(!popup_host_view_);
+  DCHECK(!child_host_view_);
+  DCHECK(guest_host_views_.empty());
+
+  if (text_input_manager_)
+    text_input_manager_->RemoveObserver(this);
+}
+
+// Called for full-screen widgets.
+void CefRenderWidgetHostViewOSR::InitAsChild(gfx::NativeView parent_view) {
+  DCHECK(parent_host_view_);
+  DCHECK(browser_impl_);
+
+  if (parent_host_view_->child_host_view_) {
+    // Cancel the previous popup widget.
+    parent_host_view_->child_host_view_->CancelWidget();
+  }
+
+  parent_host_view_->set_child_host_view(this);
+
+  // The parent view should not render while the full-screen view exists.
+  parent_host_view_->Hide();
+
+  SetRootLayerSize(false /* force */);
+  Show();
+}
+
+void CefRenderWidgetHostViewOSR::SetSize(const gfx::Size& size) {}
+
+void CefRenderWidgetHostViewOSR::SetBounds(const gfx::Rect& rect) {}
+
+gfx::NativeView CefRenderWidgetHostViewOSR::GetNativeView() {
+  return gfx::NativeView();
+}
+
+gfx::NativeViewAccessible
+CefRenderWidgetHostViewOSR::GetNativeViewAccessible() {
+  return gfx::NativeViewAccessible();
+}
+
+void CefRenderWidgetHostViewOSR::Focus() {}
+
+bool CefRenderWidgetHostViewOSR::HasFocus() {
+  return false;
+}
+
+bool CefRenderWidgetHostViewOSR::IsSurfaceAvailableForCopy() {
+  return delegated_frame_host_->CanCopyFromCompositingSurface();
+}
+
+void CefRenderWidgetHostViewOSR::Show() {
+  if (is_showing_)
+    return;
+
+  if (!content::GpuDataManagerImpl::GetInstance()->IsGpuCompositingDisabled() &&
+      !browser_impl_ &&
+      (!parent_host_view_ || !parent_host_view_->browser_impl_)) {
+    return;
+  }
+
+  is_showing_ = true;
+
+  // If the viz::LocalSurfaceIdAllocation is invalid, we may have been evicted,
+  // and no other visual properties have since been changed. Allocate a new id
+  // and start synchronizing.
+  if (!GetLocalSurfaceIdAllocation().IsValid()) {
+    AllocateLocalSurfaceId();
+    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
+                                GetLocalSurfaceIdAllocation());
+  }
+
+  if (render_widget_host_) {
+    render_widget_host_->WasShown(
+        base::nullopt /* record_tab_switch_time_request */);
+  }
+
+  delegated_frame_host_->AttachToCompositor(compositor_.get());
+  delegated_frame_host_->WasShown(
+      GetLocalSurfaceIdAllocation().local_surface_id(), GetViewBounds().size(),
+      base::nullopt);
+
+  if (!content::GpuDataManagerImpl::GetInstance()->IsGpuCompositingDisabled()) {
+    // Start generating frames when we're visible and at the correct size.
+    if (!video_consumer_) {
+      video_consumer_.reset(new CefVideoConsumerOSR(this));
+      UpdateFrameRate();
+
+      // Call OnRenderFrameMetadataChangedAfterActivation for every frame.
+      content::RenderFrameMetadataProviderImpl* provider =
+          content::RenderWidgetHostImpl::From(render_widget_host_)
+              ->render_frame_metadata_provider();
+      provider->ReportAllFrameSubmissionsForTesting(true);
+    } else {
+      video_consumer_->SetActive(true);
+    }
+  }
+}
+
+void CefRenderWidgetHostViewOSR::Hide() {
+  if (!is_showing_)
+    return;
+
+  is_showing_ = false;
+
+  if (browser_impl_.get())
+    browser_impl_->CancelContextMenu();
+
+  if (video_consumer_) {
+    video_consumer_->SetActive(false);
+  }
+
+  if (render_widget_host_)
+    render_widget_host_->WasHidden();
+
+  delegated_frame_host_->WasHidden(
+      content::DelegatedFrameHost::HiddenCause::kOther);
+  delegated_frame_host_->DetachFromCompositor();
+}
+
+bool CefRenderWidgetHostViewOSR::IsShowing() {
+  return is_showing_;
+}
+
+void CefRenderWidgetHostViewOSR::EnsureSurfaceSynchronizedForWebTest() {
+  ++latest_capture_sequence_number_;
+  SynchronizeVisualProperties(cc::DeadlinePolicy::UseInfiniteDeadline(),
+                              base::nullopt);
+}
+
+gfx::Rect CefRenderWidgetHostViewOSR::GetViewBounds() {
+  if (IsPopupWidget())
+    return popup_position_;
+
+  return current_view_bounds_;
+}
+
+void CefRenderWidgetHostViewOSR::SetBackgroundColor(SkColor color) {
+  // The renderer will feed its color back to us with the first CompositorFrame.
+  // We short-cut here to show a sensible color before that happens.
+  UpdateBackgroundColorFromRenderer(color);
+
+  DCHECK(SkColorGetA(color) == SK_AlphaOPAQUE ||
+         SkColorGetA(color) == SK_AlphaTRANSPARENT);
+  content::RenderWidgetHostViewBase::SetBackgroundColor(color);
+}
+
+base::Optional<SkColor> CefRenderWidgetHostViewOSR::GetBackgroundColor() {
+  return background_color_;
+}
+
+void CefRenderWidgetHostViewOSR::UpdateBackgroundColor() {}
+
+blink::mojom::PointerLockResult CefRenderWidgetHostViewOSR::LockMouse(
+    bool request_unadjusted_movement) {
+  return blink::mojom::PointerLockResult::kPermissionDenied;
+}
+
+blink::mojom::PointerLockResult CefRenderWidgetHostViewOSR::ChangeMouseLock(
+    bool request_unadjusted_movement) {
+  return blink::mojom::PointerLockResult::kPermissionDenied;
+}
+
+void CefRenderWidgetHostViewOSR::UnlockMouse() {}
+
+void CefRenderWidgetHostViewOSR::TakeFallbackContentFrom(
+    content::RenderWidgetHostView* view) {
+  DCHECK(!static_cast<RenderWidgetHostViewBase*>(view)
+              ->IsRenderWidgetHostViewChildFrame());
+  CefRenderWidgetHostViewOSR* view_cef =
+      static_cast<CefRenderWidgetHostViewOSR*>(view);
+  SetBackgroundColor(view_cef->background_color_);
+  if (delegated_frame_host_ && view_cef->delegated_frame_host_) {
+    delegated_frame_host_->TakeFallbackContentFrom(
+        view_cef->delegated_frame_host_.get());
+  }
+  host()->GetContentRenderingTimeoutFrom(view_cef->host());
+}
+
+void CefRenderWidgetHostViewOSR::OnPresentCompositorFrame() {}
+
+void CefRenderWidgetHostViewOSR::OnDidUpdateVisualPropertiesComplete(
+    const cc::RenderFrameMetadata& metadata) {
+  if (host()->is_hidden()) {
+    // When an embedded child responds, we want to accept its changes to the
+    // viz::LocalSurfaceId. However we do not want to embed surfaces while
+    // hidden. Nor do we want to embed invalid ids when we are evicted. Becoming
+    // visible will generate a new id, if necessary, and begin embedding.
+    UpdateLocalSurfaceIdFromEmbeddedClient(
+        metadata.local_surface_id_allocation);
+  } else {
+    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
+                                metadata.local_surface_id_allocation);
+  }
+}
+
+void CefRenderWidgetHostViewOSR::AllocateLocalSurfaceId() {
+  if (!parent_local_surface_id_allocator_) {
+    parent_local_surface_id_allocator_ =
+        std::make_unique<viz::ParentLocalSurfaceIdAllocator>();
+  }
+  parent_local_surface_id_allocator_->GenerateId();
+}
+
+const viz::LocalSurfaceIdAllocation&
+CefRenderWidgetHostViewOSR::GetCurrentLocalSurfaceIdAllocation() const {
+  return parent_local_surface_id_allocator_
+      ->GetCurrentLocalSurfaceIdAllocation();
+}
+
+void CefRenderWidgetHostViewOSR::UpdateLocalSurfaceIdFromEmbeddedClient(
+    const base::Optional<viz::LocalSurfaceIdAllocation>&
+        embedded_client_local_surface_id_allocation) {
+  if (embedded_client_local_surface_id_allocation) {
+    parent_local_surface_id_allocator_->UpdateFromChild(
+        *embedded_client_local_surface_id_allocation);
+  } else {
+    AllocateLocalSurfaceId();
+  }
+}
+
+const viz::LocalSurfaceIdAllocation&
+CefRenderWidgetHostViewOSR::GetOrCreateLocalSurfaceIdAllocation() {
+  if (!parent_local_surface_id_allocator_)
+    AllocateLocalSurfaceId();
+  return GetCurrentLocalSurfaceIdAllocation();
+}
+
+void CefRenderWidgetHostViewOSR::InvalidateLocalSurfaceId() {
+  if (!parent_local_surface_id_allocator_)
+    return;
+  parent_local_surface_id_allocator_->Invalidate();
+}
+
+void CefRenderWidgetHostViewOSR::AddDamageRect(uint32_t sequence,
+                                               const gfx::Rect& rect) {
+  // Associate the given damage rect with the presentation token.
+  // For OnAcceleratedPaint we'll lookup the corresponding damage area based on
+  // the frame token which is passed back to OnPresentCompositorFrame.
+  base::AutoLock lock_scope(damage_rect_lock_);
+
+  // We assume our presentation_token is a counter. Since we're using an ordered
+  // map we can enforce a max size and remove oldest from the front. Worst case,
+  // if a damage rect isn't associated, we can simply pass the entire view size.
+  while (damage_rects_.size() >= kMaxDamageRects) {
+    damage_rects_.erase(damage_rects_.begin());
+  }
+  damage_rects_[sequence] = rect;
+}
+
+void CefRenderWidgetHostViewOSR::ResetFallbackToFirstNavigationSurface() {
+  delegated_frame_host_->ResetFallbackToFirstNavigationSurface();
+}
+
+void CefRenderWidgetHostViewOSR::InitAsPopup(
+    content::RenderWidgetHostView* parent_host_view,
+    const gfx::Rect& pos) {
+  DCHECK_EQ(parent_host_view_, parent_host_view);
+  DCHECK(browser_impl_);
+
+  if (parent_host_view_->popup_host_view_) {
+    // Cancel the previous popup widget.
+    parent_host_view_->popup_host_view_->CancelWidget();
+  }
+
+  parent_host_view_->set_popup_host_view(this);
+
+  CefRefPtr<CefRenderHandler> handler =
+      browser_impl_->GetClient()->GetRenderHandler();
+  CHECK(handler);
+
+  handler->OnPopupShow(browser_impl_.get(), true);
+
+  CefRect view_rect;
+  handler->GetViewRect(browser_impl_.get(), view_rect);
+  gfx::Rect client_pos(pos.x() - view_rect.x, pos.y() - view_rect.y,
+                       pos.width(), pos.height());
+
+  popup_position_ = client_pos;
+
+  CefRect widget_pos(client_pos.x(), client_pos.y(), client_pos.width(),
+                     client_pos.height());
+
+  if (handler.get())
+    handler->OnPopupSize(browser_impl_.get(), widget_pos);
+
+  // The size doesn't change for popups so we need to force the
+  // initialization.
+  SetRootLayerSize(true /* force */);
+  Show();
+}
+
+void CefRenderWidgetHostViewOSR::InitAsFullscreen(
+    content::RenderWidgetHostView* reference_host_view) {
+  NOTREACHED() << "Fullscreen widgets are not supported in OSR";
+}
+
+void CefRenderWidgetHostViewOSR::UpdateCursor(
+    const content::WebCursor& cursor) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::UpdateCursor");
+  if (!browser_impl_.get())
+    return;
+
+  CefRefPtr<CefRenderHandler> handler =
+      browser_impl_->GetClient()->GetRenderHandler();
+  CHECK(handler);
+
+  const auto& ui_cursor = cursor.cursor();
+
+  const cef_cursor_type_t cursor_type =
+      static_cast<cef_cursor_type_t>(ui_cursor.type());
+  CefCursorInfo custom_cursor_info;
+  if (ui_cursor.type() == ui::mojom::CursorType::kCustom) {
+    custom_cursor_info.hotspot.x = ui_cursor.custom_hotspot().x();
+    custom_cursor_info.hotspot.y = ui_cursor.custom_hotspot().y();
+    custom_cursor_info.image_scale_factor = ui_cursor.image_scale_factor();
+    custom_cursor_info.buffer = ui_cursor.custom_bitmap().getPixels();
+    custom_cursor_info.size.width = ui_cursor.custom_bitmap().width();
+    custom_cursor_info.size.height = ui_cursor.custom_bitmap().height();
+  }
+
+#if defined(USE_AURA)
+  content::WebCursor web_cursor(ui_cursor);
+
+  ui::PlatformCursor platform_cursor;
+  if (ui_cursor.type() == ui::mojom::CursorType::kCustom) {
+    // |web_cursor| owns the resulting |platform_cursor|.
+    platform_cursor = web_cursor.GetPlatformCursor(ui_cursor);
+  } else {
+    platform_cursor = GetPlatformCursor(ui_cursor.type());
+  }
+
+  handler->OnCursorChange(browser_impl_.get(), platform_cursor, cursor_type,
+                          custom_cursor_info);
+#elif defined(OS_MACOSX)
+  // |web_cursor| owns the resulting |native_cursor|.
+  content::WebCursor web_cursor(cursor);
+  CefCursorHandle native_cursor = web_cursor.GetNativeCursor();
+  handler->OnCursorChange(browser_impl_.get(), native_cursor, cursor_type,
+                          custom_cursor_info);
+#else
+  // TODO(port): Implement this method to work on other platforms as part of
+  // off-screen rendering support.
+  NOTREACHED();
+#endif
+}
+
+content::CursorManager* CefRenderWidgetHostViewOSR::GetCursorManager() {
+  return cursor_manager_.get();
+}
+
+void CefRenderWidgetHostViewOSR::SetIsLoading(bool is_loading) {
+  if (!is_loading)
+    return;
+  // Make sure gesture detection is fresh.
+  gesture_provider_.ResetDetection();
+  forward_touch_to_popup_ = false;
+}
+
+void CefRenderWidgetHostViewOSR::RenderProcessGone() {
+  Destroy();
+}
+
+void CefRenderWidgetHostViewOSR::Destroy() {
+  if (!is_destroyed_) {
+    is_destroyed_ = true;
+
+    if (has_parent_) {
+      CancelWidget();
+    } else {
+      if (popup_host_view_)
+        popup_host_view_->CancelWidget();
+      if (child_host_view_)
+        child_host_view_->CancelWidget();
+      if (!guest_host_views_.empty()) {
+        // Guest RWHVs will be destroyed when the associated RWHVGuest is
+        // destroyed. This parent RWHV may be destroyed first, so disassociate
+        // the guest RWHVs here without destroying them.
+        for (auto guest_host_view : guest_host_views_)
+          guest_host_view->parent_host_view_ = nullptr;
+        guest_host_views_.clear();
+      }
+      Hide();
+    }
+  }
+
+  delete this;
+}
+
+void CefRenderWidgetHostViewOSR::SetTooltipText(
+    const base::string16& tooltip_text) {
+  if (!browser_impl_.get())
+    return;
+
+  CefString tooltip(tooltip_text);
+  CefRefPtr<CefDisplayHandler> handler =
+      browser_impl_->GetClient()->GetDisplayHandler();
+  if (handler.get()) {
+    handler->OnTooltip(browser_impl_.get(), tooltip);
+  }
+}
+
+gfx::Size CefRenderWidgetHostViewOSR::GetCompositorViewportPixelSize() {
+  return gfx::ScaleToCeiledSize(GetRequestedRendererSize(),
+                                current_device_scale_factor_);
+}
+
+uint32_t CefRenderWidgetHostViewOSR::GetCaptureSequenceNumber() const {
+  return latest_capture_sequence_number_;
+}
+
+void CefRenderWidgetHostViewOSR::CopyFromSurface(
+    const gfx::Rect& src_rect,
+    const gfx::Size& output_size,
+    base::OnceCallback<void(const SkBitmap&)> callback) {
+  delegated_frame_host_->CopyFromCompositingSurface(src_rect, output_size,
+                                                    std::move(callback));
+}
+
+void CefRenderWidgetHostViewOSR::GetScreenInfo(content::ScreenInfo* results) {
+  if (!browser_impl_.get())
+    return;
+
+  CefScreenInfo screen_info(kDefaultScaleFactor, 0, 0, false, CefRect(),
+                            CefRect());
+
+  CefRefPtr<CefRenderHandler> handler =
+      browser_impl_->client()->GetRenderHandler();
+  CHECK(handler);
+  if (!handler->GetScreenInfo(browser_impl_.get(), screen_info) ||
+      screen_info.rect.width == 0 || screen_info.rect.height == 0 ||
+      screen_info.available_rect.width == 0 ||
+      screen_info.available_rect.height == 0) {
+    // If a screen rectangle was not provided, try using the view rectangle
+    // instead. Otherwise, popup views may be drawn incorrectly, or not at
+    // all.
+    CefRect screenRect;
+    handler->GetViewRect(browser_impl_.get(), screenRect);
+    CHECK_GT(screenRect.width, 0);
+    CHECK_GT(screenRect.height, 0);
+
+    if (screen_info.rect.width == 0 || screen_info.rect.height == 0) {
+      screen_info.rect = screenRect;
+    }
+
+    if (screen_info.available_rect.width == 0 ||
+        screen_info.available_rect.height == 0) {
+      screen_info.available_rect = screenRect;
+    }
+  }
+
+  *results = ScreenInfoFrom(screen_info);
+}
+
+void CefRenderWidgetHostViewOSR::TransformPointToRootSurface(
+    gfx::PointF* point) {}
+
+gfx::Rect CefRenderWidgetHostViewOSR::GetBoundsInRootWindow() {
+  if (!browser_impl_.get())
+    return gfx::Rect();
+
+  CefRect rc;
+  CefRefPtr<CefRenderHandler> handler =
+      browser_impl_->client()->GetRenderHandler();
+  CHECK(handler);
+  if (handler->GetRootScreenRect(browser_impl_.get(), rc))
+    return gfx::Rect(rc.x, rc.y, rc.width, rc.height);
+  return GetViewBounds();
+}
+
+#if !defined(OS_MACOSX)
+viz::ScopedSurfaceIdAllocator
+CefRenderWidgetHostViewOSR::DidUpdateVisualProperties(
+    const cc::RenderFrameMetadata& metadata) {
+  base::OnceCallback<void()> allocation_task = base::BindOnce(
+      &CefRenderWidgetHostViewOSR::OnDidUpdateVisualPropertiesComplete,
+      weak_ptr_factory_.GetWeakPtr(), metadata);
+  return viz::ScopedSurfaceIdAllocator(std::move(allocation_task));
+}
+#endif
+
+viz::SurfaceId CefRenderWidgetHostViewOSR::GetCurrentSurfaceId() const {
+  return delegated_frame_host_ ? delegated_frame_host_->GetCurrentSurfaceId()
+                               : viz::SurfaceId();
+}
+
+content::BrowserAccessibilityManager*
+CefRenderWidgetHostViewOSR::CreateBrowserAccessibilityManager(
+    content::BrowserAccessibilityDelegate* delegate,
+    bool for_root_frame) {
+  return nullptr;
+}
+
+void CefRenderWidgetHostViewOSR::ImeSetComposition(
+    const CefString& text,
+    const std::vector<CefCompositionUnderline>& underlines,
+    const CefRange& replacement_range,
+    const CefRange& selection_range) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::ImeSetComposition");
+  if (!render_widget_host_)
+    return;
+
+  std::vector<ui::ImeTextSpan> web_underlines;
+  web_underlines.reserve(underlines.size());
+  for (const CefCompositionUnderline& line : underlines) {
+    web_underlines.push_back(ui::ImeTextSpan(
+        ui::ImeTextSpan::Type::kComposition, line.range.from, line.range.to,
+        line.thick ? ui::ImeTextSpan::Thickness::kThick
+                   : ui::ImeTextSpan::Thickness::kThin,
+        GetImeUnderlineStyle(line.style), line.background_color, line.color,
+        std::vector<std::string>()));
+  }
+  gfx::Range range(replacement_range.from, replacement_range.to);
+
+  // Start Monitoring for composition updates before we set.
+  RequestImeCompositionUpdate(true);
+
+  render_widget_host_->ImeSetComposition(
+      text, web_underlines, range, selection_range.from, selection_range.to);
+}
+
+void CefRenderWidgetHostViewOSR::ImeCommitText(
+    const CefString& text,
+    const CefRange& replacement_range,
+    int relative_cursor_pos) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::ImeCommitText");
+  if (!render_widget_host_)
+    return;
+
+  gfx::Range range(replacement_range.from, replacement_range.to);
+  render_widget_host_->ImeCommitText(text, std::vector<ui::ImeTextSpan>(),
+                                     range, relative_cursor_pos);
+
+  // Stop Monitoring for composition updates after we are done.
+  RequestImeCompositionUpdate(false);
+}
+
+void CefRenderWidgetHostViewOSR::ImeFinishComposingText(bool keep_selection) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::ImeFinishComposingText");
+  if (!render_widget_host_)
+    return;
+
+  render_widget_host_->ImeFinishComposingText(keep_selection);
+
+  // Stop Monitoring for composition updates after we are done.
+  RequestImeCompositionUpdate(false);
+}
+
+void CefRenderWidgetHostViewOSR::ImeCancelComposition() {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::ImeCancelComposition");
+  if (!render_widget_host_)
+    return;
+
+  render_widget_host_->ImeCancelComposition();
+
+  // Stop Monitoring for composition updates after we are done.
+  RequestImeCompositionUpdate(false);
+}
+
+void CefRenderWidgetHostViewOSR::SelectionChanged(const base::string16& text,
+                                                  size_t offset,
+                                                  const gfx::Range& range) {
+  RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
+
+  if (!browser_impl_.get())
+    return;
+
+  CefString selected_text;
+  if (!range.is_empty() && !text.empty()) {
+    size_t pos = range.GetMin() - offset;
+    size_t n = range.length();
+    if (pos + n <= text.length())
+      selected_text = text.substr(pos, n);
+  }
+
+  CefRefPtr<CefRenderHandler> handler =
+      browser_impl_->GetClient()->GetRenderHandler();
+  CHECK(handler);
+
+  CefRange cef_range(range.start(), range.end());
+  handler->OnTextSelectionChanged(browser_impl_.get(), selected_text,
+                                  cef_range);
+}
+
+const viz::LocalSurfaceIdAllocation&
+CefRenderWidgetHostViewOSR::GetLocalSurfaceIdAllocation() const {
+  return const_cast<CefRenderWidgetHostViewOSR*>(this)
+      ->GetOrCreateLocalSurfaceIdAllocation();
+}
+
+const viz::FrameSinkId& CefRenderWidgetHostViewOSR::GetFrameSinkId() const {
+  return delegated_frame_host_->frame_sink_id();
+}
+
+viz::FrameSinkId CefRenderWidgetHostViewOSR::GetRootFrameSinkId() {
+  return compositor_->frame_sink_id();
+}
+
+void CefRenderWidgetHostViewOSR::OnRenderFrameMetadataChangedAfterActivation() {
+  if (video_consumer_) {
+    // Need to wait for the first frame of the new size before calling
+    // SizeChanged. Otherwise, the video frame will be letterboxed.
+    auto metadata =
+        host_->render_frame_metadata_provider()->LastRenderFrameMetadata();
+    video_consumer_->SizeChanged(metadata.viewport_size_in_pixels);
+  }
+}
+
+std::unique_ptr<content::SyntheticGestureTarget>
+CefRenderWidgetHostViewOSR::CreateSyntheticGestureTarget() {
+  return std::make_unique<CefSyntheticGestureTargetOSR>(host());
+}
+
+bool CefRenderWidgetHostViewOSR::TransformPointToCoordSpaceForView(
+    const gfx::PointF& point,
+    RenderWidgetHostViewBase* target_view,
+    gfx::PointF* transformed_point) {
+  if (target_view == this) {
+    *transformed_point = point;
+    return true;
+  }
+
+  return target_view->TransformPointToLocalCoordSpace(
+      point, GetCurrentSurfaceId(), transformed_point);
+}
+
+void CefRenderWidgetHostViewOSR::DidNavigate() {
+  if (!IsShowing()) {
+    // Navigating while hidden should not allocate a new LocalSurfaceID. Once
+    // sizes are ready, or we begin to Show, we can then allocate the new
+    // LocalSurfaceId.
+    InvalidateLocalSurfaceId();
+  } else {
+    if (is_first_navigation_) {
+      // The first navigation does not need a new LocalSurfaceID. The renderer
+      // can use the ID that was already provided.
+      SynchronizeVisualProperties(cc::DeadlinePolicy::UseExistingDeadline(),
+                                  GetLocalSurfaceIdAllocation());
+    } else {
+      SynchronizeVisualProperties(cc::DeadlinePolicy::UseExistingDeadline(),
+                                  base::nullopt);
+    }
+  }
+  if (delegated_frame_host_)
+    delegated_frame_host_->DidNavigate();
+  is_first_navigation_ = false;
+}
+
+void CefRenderWidgetHostViewOSR::OnFrameComplete(
+    const viz::BeginFrameAck& ack) {
+  // TODO(cef): is there something we need to track with this notification?
+}
+
+std::unique_ptr<viz::HostDisplayClient>
+CefRenderWidgetHostViewOSR::CreateHostDisplayClient() {
+  host_display_client_ =
+      new CefHostDisplayClientOSR(this, gfx::kNullAcceleratedWidget);
+  host_display_client_->SetActive(true);
+  return base::WrapUnique(host_display_client_);
+}
+
+bool CefRenderWidgetHostViewOSR::InstallTransparency() {
+  if (background_color_ == SK_ColorTRANSPARENT) {
+    SetBackgroundColor(background_color_);
+    if (compositor_) {
+      compositor_->SetBackgroundColor(background_color_);
+    }
+    return true;
+  }
+  return false;
+}
+
+void CefRenderWidgetHostViewOSR::WasResized() {
+  // Only one resize will be in-flight at a time.
+  if (hold_resize_) {
+    if (!pending_resize_)
+      pending_resize_ = true;
+    return;
+  }
+
+  SynchronizeVisualProperties(cc::DeadlinePolicy::UseExistingDeadline(),
+                              base::nullopt);
+}
+
+void CefRenderWidgetHostViewOSR::SynchronizeVisualProperties(
+    const cc::DeadlinePolicy& deadline_policy,
+    const base::Optional<viz::LocalSurfaceIdAllocation>&
+        child_local_surface_id_allocation) {
+  SetFrameRate();
+
+  const bool resized = ResizeRootLayer();
+  bool surface_id_updated = false;
+
+  if (!resized && child_local_surface_id_allocation) {
+    // Update the current surface ID.
+    parent_local_surface_id_allocator_->UpdateFromChild(
+        *child_local_surface_id_allocation);
+    surface_id_updated = true;
+  }
+
+  // Allocate a new surface ID if the surface has been resized or if the current
+  // ID is invalid (meaning we may have been evicted).
+  if (resized || !GetCurrentLocalSurfaceIdAllocation().IsValid()) {
+    AllocateLocalSurfaceId();
+    surface_id_updated = true;
+  }
+
+  if (surface_id_updated) {
+    delegated_frame_host_->EmbedSurface(
+        GetCurrentLocalSurfaceIdAllocation().local_surface_id(),
+        GetViewBounds().size(), deadline_policy);
+
+    // |render_widget_host_| will retrieve resize parameters from the
+    // DelegatedFrameHost and this view, so SynchronizeVisualProperties must be
+    // called last.
+    if (render_widget_host_) {
+      render_widget_host_->SynchronizeVisualProperties();
+    }
+  }
+}
+
+void CefRenderWidgetHostViewOSR::OnScreenInfoChanged() {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::OnScreenInfoChanged");
+  if (!render_widget_host_)
+    return;
+
+  SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
+                              base::nullopt);
+
+  if (render_widget_host_->delegate())
+    render_widget_host_->delegate()->SendScreenRects();
+  else
+    render_widget_host_->SendScreenRects();
+
+  render_widget_host_->NotifyScreenInfoChanged();
+
+  // We might want to change the cursor scale factor here as well - see the
+  // cache for the current_cursor_, as passed by UpdateCursor from the
+  // renderer in the rwhv_aura (current_cursor_.SetScaleFactor)
+
+  // Notify the guest hosts if any.
+  for (auto guest_host_view : guest_host_views_)
+    guest_host_view->OnScreenInfoChanged();
+}
+
+void CefRenderWidgetHostViewOSR::Invalidate(
+    CefBrowserHost::PaintElementType type) {
+  TRACE_EVENT1("cef", "CefRenderWidgetHostViewOSR::Invalidate", "type", type);
+  if (!IsPopupWidget() && type == PET_POPUP) {
+    if (popup_host_view_)
+      popup_host_view_->Invalidate(type);
+    return;
+  }
+  InvalidateInternal(gfx::Rect(SizeInPixels()));
+}
+
+void CefRenderWidgetHostViewOSR::SendExternalBeginFrame() {
+  DCHECK(external_begin_frame_enabled_);
+
+  base::TimeTicks frame_time = base::TimeTicks::Now();
+  base::TimeTicks deadline = base::TimeTicks();
+  base::TimeDelta interval = viz::BeginFrameArgs::DefaultInterval();
+
+  viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+      BEGINFRAME_FROM_HERE, begin_frame_source_.source_id(),
+      begin_frame_number_, frame_time, deadline, interval,
+      viz::BeginFrameArgs::NORMAL);
+
+  DCHECK(begin_frame_args.IsValid());
+  begin_frame_number_++;
+
+  if (render_widget_host_)
+    render_widget_host_->ProgressFlingIfNeeded(frame_time);
+
+  compositor_->IssueExternalBeginFrame(
+      begin_frame_args, /* force= */ true,
+      base::BindOnce(&CefRenderWidgetHostViewOSR::OnFrameComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  if (!IsPopupWidget() && popup_host_view_) {
+    popup_host_view_->SendExternalBeginFrame();
+  }
+}
+
+void CefRenderWidgetHostViewOSR::SendKeyEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::SendKeyEvent");
+  content::RenderWidgetHostImpl* target_host = render_widget_host_;
+
+  // If there are multiple widgets on the page (such as when there are
+  // out-of-process iframes), pick the one that should process this event.
+  if (render_widget_host_ && render_widget_host_->delegate()) {
+    target_host = render_widget_host_->delegate()->GetFocusedRenderWidgetHost(
+        render_widget_host_);
+  }
+
+  if (target_host && target_host->GetView()) {
+    // Direct routing requires that events go directly to the View.
+    target_host->ForwardKeyboardEventWithLatencyInfo(
+        event, ui::LatencyInfo(event.GetType() == blink::WebInputEvent::kChar ||
+                                       event.GetType() ==
+                                           blink::WebInputEvent::kRawKeyDown
+                                   ? ui::SourceEventType::KEY_PRESS
+                                   : ui::SourceEventType::OTHER));
+  }
+}
+
+void CefRenderWidgetHostViewOSR::SendMouseEvent(
+    const blink::WebMouseEvent& event) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::SendMouseEvent");
+  if (!IsPopupWidget()) {
+    if (browser_impl_.get() &&
+        event.GetType() == blink::WebMouseEvent::kMouseDown) {
+      browser_impl_->CancelContextMenu();
+    }
+
+    if (popup_host_view_) {
+      if (popup_host_view_->popup_position_.Contains(
+              event.PositionInWidget().x(), event.PositionInWidget().y())) {
+        blink::WebMouseEvent popup_event(event);
+        popup_event.SetPositionInWidget(
+            event.PositionInWidget().x() -
+                popup_host_view_->popup_position_.x(),
+            event.PositionInWidget().y() -
+                popup_host_view_->popup_position_.y());
+        popup_event.SetPositionInScreen(popup_event.PositionInWidget().x(),
+                                        popup_event.PositionInWidget().y());
+
+        popup_host_view_->SendMouseEvent(popup_event);
+        return;
+      }
+    } else if (!guest_host_views_.empty()) {
+      for (auto guest_host_view : guest_host_views_) {
+        if (!guest_host_view->render_widget_host_ ||
+            !guest_host_view->render_widget_host_->GetView()) {
+          continue;
+        }
+        const gfx::Rect& guest_bounds =
+            guest_host_view->render_widget_host_->GetView()->GetViewBounds();
+        if (guest_bounds.Contains(event.PositionInWidget().x(),
+                                  event.PositionInWidget().y())) {
+          blink::WebMouseEvent guest_event(event);
+          guest_event.SetPositionInWidget(
+              event.PositionInWidget().x() - guest_bounds.x(),
+              event.PositionInWidget().y() - guest_bounds.y());
+          guest_event.SetPositionInScreen(guest_event.PositionInWidget().x(),
+                                          guest_event.PositionInWidget().y());
+
+          guest_host_view->SendMouseEvent(guest_event);
+          return;
+        }
+      }
+    }
+  }
+
+  if (render_widget_host_ && render_widget_host_->GetView()) {
+    if (ShouldRouteEvents()) {
+      // RouteMouseEvent wants non-const pointer to WebMouseEvent, but it only
+      // forwards it to RenderWidgetTargeter::FindTargetAndDispatch as a const
+      // reference, so const_cast here is safe.
+      render_widget_host_->delegate()->GetInputEventRouter()->RouteMouseEvent(
+          this, const_cast<blink::WebMouseEvent*>(&event),
+          ui::LatencyInfo(ui::SourceEventType::OTHER));
+    } else {
+      render_widget_host_->GetView()->ProcessMouseEvent(
+          event, ui::LatencyInfo(ui::SourceEventType::OTHER));
+    }
+  }
+}
+
+void CefRenderWidgetHostViewOSR::SendMouseWheelEvent(
+    const blink::WebMouseWheelEvent& event) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::SendMouseWheelEvent");
+
+  if (!IsPopupWidget()) {
+    if (browser_impl_.get())
+      browser_impl_->CancelContextMenu();
+
+    if (popup_host_view_) {
+      if (popup_host_view_->popup_position_.Contains(
+              event.PositionInWidget().x(), event.PositionInWidget().y())) {
+        blink::WebMouseWheelEvent popup_mouse_wheel_event(event);
+        popup_mouse_wheel_event.SetPositionInWidget(
+            event.PositionInWidget().x() -
+                popup_host_view_->popup_position_.x(),
+            event.PositionInWidget().y() -
+                popup_host_view_->popup_position_.y());
+        popup_mouse_wheel_event.SetPositionInScreen(
+            popup_mouse_wheel_event.PositionInWidget().x(),
+            popup_mouse_wheel_event.PositionInWidget().y());
+
+        popup_host_view_->SendMouseWheelEvent(popup_mouse_wheel_event);
+        return;
+      } else {
+        // Scrolling outside of the popup widget so destroy it.
+        // Execute asynchronously to avoid deleting the widget from inside
+        // some other callback.
+        CEF_POST_TASK(
+            CEF_UIT,
+            base::Bind(&CefRenderWidgetHostViewOSR::CancelWidget,
+                       popup_host_view_->weak_ptr_factory_.GetWeakPtr()));
+      }
+    } else if (!guest_host_views_.empty()) {
+      for (auto guest_host_view : guest_host_views_) {
+        if (!guest_host_view->render_widget_host_ ||
+            !guest_host_view->render_widget_host_->GetView()) {
+          continue;
+        }
+        const gfx::Rect& guest_bounds =
+            guest_host_view->render_widget_host_->GetView()->GetViewBounds();
+        if (guest_bounds.Contains(event.PositionInWidget().x(),
+                                  event.PositionInWidget().y())) {
+          blink::WebMouseWheelEvent guest_mouse_wheel_event(event);
+          guest_mouse_wheel_event.SetPositionInWidget(
+              event.PositionInWidget().x() - guest_bounds.x(),
+              event.PositionInWidget().y() - guest_bounds.y());
+          guest_mouse_wheel_event.SetPositionInScreen(
+              guest_mouse_wheel_event.PositionInWidget().x(),
+              guest_mouse_wheel_event.PositionInWidget().y());
+
+          guest_host_view->SendMouseWheelEvent(guest_mouse_wheel_event);
+          return;
+        }
+      }
+    }
+  }
+
+  if (render_widget_host_ && render_widget_host_->GetView()) {
+    blink::WebMouseWheelEvent mouse_wheel_event(event);
+
+    mouse_wheel_phase_handler_.SendWheelEndForTouchpadScrollingIfNeeded(false);
+    mouse_wheel_phase_handler_.AddPhaseIfNeededAndScheduleEndEvent(
+        mouse_wheel_event, false);
+
+    if (ShouldRouteEvents()) {
+      render_widget_host_->delegate()
+          ->GetInputEventRouter()
+          ->RouteMouseWheelEvent(
+              this, const_cast<blink::WebMouseWheelEvent*>(&mouse_wheel_event),
+              ui::LatencyInfo(ui::SourceEventType::WHEEL));
+    } else {
+      render_widget_host_->GetView()->ProcessMouseWheelEvent(
+          mouse_wheel_event, ui::LatencyInfo(ui::SourceEventType::WHEEL));
+    }
+  }
+}
+
+void CefRenderWidgetHostViewOSR::SendTouchEvent(const CefTouchEvent& event) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::SendTouchEvent");
+
+  if (!IsPopupWidget() && popup_host_view_) {
+    if (!forward_touch_to_popup_ && event.type == CEF_TET_PRESSED &&
+        pointer_state_.GetPointerCount() == 0) {
+      forward_touch_to_popup_ =
+          popup_host_view_->popup_position_.Contains(event.x, event.y);
+    }
+
+    if (forward_touch_to_popup_) {
+      CefTouchEvent popup_event(event);
+      popup_event.x -= popup_host_view_->popup_position_.x();
+      popup_event.y -= popup_host_view_->popup_position_.y();
+      popup_host_view_->SendTouchEvent(popup_event);
+      return;
+    }
+  }
+
+  // Update the touch event first.
+  if (!pointer_state_.OnTouch(event))
+    return;
+
+  ui::FilteredGestureProvider::TouchHandlingResult result =
+      gesture_provider_.OnTouchEvent(pointer_state_);
+
+  blink::WebTouchEvent touch_event = ui::CreateWebTouchEventFromMotionEvent(
+      pointer_state_, result.moved_beyond_slop_region, false);
+
+  pointer_state_.CleanupRemovedTouchPoints(event);
+
+  // Set unchanged touch point to StateStationary for touchmove and
+  // touchcancel to make sure only send one ack per WebTouchEvent.
+  if (!result.succeeded)
+    pointer_state_.MarkUnchangedTouchPointsAsStationary(&touch_event, event);
+
+  if (!render_widget_host_)
+    return;
+
+  ui::LatencyInfo latency_info = CreateLatencyInfo(touch_event);
+  if (ShouldRouteEvents()) {
+    render_widget_host_->delegate()->GetInputEventRouter()->RouteTouchEvent(
+        this, &touch_event, latency_info);
+  } else {
+    render_widget_host_->ForwardTouchEventWithLatencyInfo(touch_event,
+                                                          latency_info);
+  }
+
+  bool touch_end = touch_event.GetType() == blink::WebInputEvent::kTouchEnd ||
+                   touch_event.GetType() == blink::WebInputEvent::kTouchCancel;
+
+  if (touch_end && IsPopupWidget() && parent_host_view_ &&
+      parent_host_view_->popup_host_view_ == this) {
+    parent_host_view_->forward_touch_to_popup_ = false;
+  }
+}
+
+bool CefRenderWidgetHostViewOSR::ShouldRouteEvents() const {
+  if (!render_widget_host_->delegate())
+    return false;
+
+  // Do not route events that are currently targeted to page popups such as
+  // <select> element drop-downs, since these cannot contain cross-process
+  // frames.
+  if (!render_widget_host_->delegate()->IsWidgetForMainFrame(
+          render_widget_host_)) {
+    return false;
+  }
+
+  return !!render_widget_host_->delegate()->GetInputEventRouter();
+}
+
+void CefRenderWidgetHostViewOSR::SendFocusEvent(bool focus) {
+  if (!render_widget_host_)
+    return;
+
+  content::RenderWidgetHostImpl* widget =
+      content::RenderWidgetHostImpl::From(render_widget_host_);
+  if (focus) {
+    widget->GotFocus();
+    widget->SetActive(true);
+  } else {
+    if (browser_impl_.get())
+      browser_impl_->CancelContextMenu();
+
+    widget->SetActive(false);
+    widget->LostFocus();
+  }
+}
+
+void CefRenderWidgetHostViewOSR::OnUpdateTextInputStateCalled(
+    content::TextInputManager* text_input_manager,
+    content::RenderWidgetHostViewBase* updated_view,
+    bool did_update_state) {
+  const content::TextInputState* state =
+      text_input_manager->GetTextInputState();
+  if (state && !state->show_ime_if_needed)
+    return;
+
+  CefRenderHandler::TextInputMode mode = CEF_TEXT_INPUT_MODE_NONE;
+  if (state && state->type != ui::TEXT_INPUT_TYPE_NONE) {
+    static_assert(
+        static_cast<int>(CEF_TEXT_INPUT_MODE_MAX) ==
+            static_cast<int>(ui::TEXT_INPUT_MODE_MAX),
+        "Enum values in cef_text_input_mode_t must match ui::TextInputMode");
+    mode = static_cast<CefRenderHandler::TextInputMode>(state->mode);
+  }
+
+  CefRefPtr<CefRenderHandler> handler =
+      browser_impl_->GetClient()->GetRenderHandler();
+  CHECK(handler);
+
+  handler->OnVirtualKeyboardRequested(browser_impl_->GetBrowser(), mode);
+}
+
+void CefRenderWidgetHostViewOSR::ProcessAckedTouchEvent(
+    const content::TouchEventWithLatencyInfo& touch,
+    content::InputEventAckState ack_result) {
+  const bool event_consumed =
+      ack_result == content::INPUT_EVENT_ACK_STATE_CONSUMED;
+  gesture_provider_.OnTouchEventAck(touch.event.unique_touch_event_id,
+                                    event_consumed, false);
+}
+
+void CefRenderWidgetHostViewOSR::OnGestureEvent(
+    const ui::GestureEventData& gesture) {
+  if ((gesture.type() == ui::ET_GESTURE_PINCH_BEGIN ||
+       gesture.type() == ui::ET_GESTURE_PINCH_UPDATE ||
+       gesture.type() == ui::ET_GESTURE_PINCH_END) &&
+      !pinch_zoom_enabled_) {
+    return;
+  }
+
+  blink::WebGestureEvent web_event =
+      ui::CreateWebGestureEventFromGestureEventData(gesture);
+
+  // without this check, forwarding gestures does not work!
+  if (web_event.GetType() == blink::WebInputEvent::kUndefined)
+    return;
+
+  ui::LatencyInfo latency_info = CreateLatencyInfo(web_event);
+  if (ShouldRouteEvents()) {
+    render_widget_host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
+        this, &web_event, latency_info);
+  } else {
+    render_widget_host_->ForwardGestureEventWithLatencyInfo(web_event,
+                                                            latency_info);
+  }
+}
+
+void CefRenderWidgetHostViewOSR::UpdateFrameRate() {
+  frame_rate_threshold_us_ = 0;
+  SetFrameRate();
+
+  if (video_consumer_) {
+    video_consumer_->SetFrameRate(
+        base::TimeDelta::FromMicroseconds(frame_rate_threshold_us_));
+  }
+
+  // Notify the guest hosts if any.
+  for (auto guest_host_view : guest_host_views_)
+    guest_host_view->UpdateFrameRate();
+}
+
+gfx::Size CefRenderWidgetHostViewOSR::SizeInPixels() {
+  return gfx::ScaleToCeiledSize(GetViewBounds().size(),
+                                current_device_scale_factor_);
+}
+
+#if defined(OS_MACOSX)
+void CefRenderWidgetHostViewOSR::SetActive(bool active) {}
+
+void CefRenderWidgetHostViewOSR::ShowDefinitionForSelection() {}
+
+void CefRenderWidgetHostViewOSR::SpeakSelection() {}
+#endif
+
+void CefRenderWidgetHostViewOSR::OnPaint(const gfx::Rect& damage_rect,
+                                         const gfx::Size& pixel_size,
+                                         const void* pixels) {
+  TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::OnPaint");
+
+  // Workaround for https://bitbucket.org/chromiumembedded/cef/issues/2817
+  if (!is_showing_) {
+    return;
+  }
+
+  if (!pixels) {
+    return;
+  }
+
+  CefRefPtr<CefRenderHandler> handler =
+      browser_impl_->client()->GetRenderHandler();
+  CHECK(handler);
+
+  gfx::Rect rect_in_pixels(0, 0, pixel_size.width(), pixel_size.height());
+  rect_in_pixels.Intersect(damage_rect);
+
+  CefRenderHandler::RectList rcList;
+  rcList.push_back(CefRect(rect_in_pixels.x(), rect_in_pixels.y(),
+                           rect_in_pixels.width(), rect_in_pixels.height()));
+
+  handler->OnPaint(browser_impl_.get(), IsPopupWidget() ? PET_POPUP : PET_VIEW,
+                   rcList, pixels, pixel_size.width(), pixel_size.height());
+
+  // Release the resize hold when we reach the desired size.
+  if (hold_resize_ && pixel_size == SizeInPixels())
+    ReleaseResizeHold();
+}
+
+ui::Layer* CefRenderWidgetHostViewOSR::GetRootLayer() const {
+  return root_layer_.get();
+}
+
+void CefRenderWidgetHostViewOSR::SetFrameRate() {
+  CefRefPtr<CefBrowserHostImpl> browser;
+  if (parent_host_view_) {
+    // Use the same frame rate as the embedding browser.
+    browser = parent_host_view_->browser_impl_;
+  } else {
+    browser = browser_impl_;
+  }
+  CHECK(browser);
+
+  // Only set the frame rate one time.
+  if (frame_rate_threshold_us_ != 0)
+    return;
+
+  int frame_rate =
+      osr_util::ClampFrameRate(browser->settings().windowless_frame_rate);
+
+  frame_rate_threshold_us_ = 1000000 / frame_rate;
+
+  if (compositor_) {
+    compositor_->SetDisplayVSyncParameters(
+        base::TimeTicks::Now(),
+        base::TimeDelta::FromMicroseconds(frame_rate_threshold_us_));
+  }
+
+  if (video_consumer_) {
+    video_consumer_->SetFrameRate(
+        base::TimeDelta::FromMicroseconds(frame_rate_threshold_us_));
+  }
+}
+
+bool CefRenderWidgetHostViewOSR::SetDeviceScaleFactor() {
+  // This method should not be called while the resize hold is active.
+  DCHECK(!hold_resize_);
+
+  const float new_scale_factor = ::GetDeviceScaleFactor(browser_impl_.get());
+  if (new_scale_factor == current_device_scale_factor_)
+    return false;
+
+  current_device_scale_factor_ = new_scale_factor;
+
+  // Notify the guest hosts if any.
+  for (auto guest_host_view : guest_host_views_) {
+    content::RenderWidgetHostImpl* rwhi = guest_host_view->render_widget_host();
+    if (!rwhi)
+      continue;
+    if (rwhi->GetView())
+      rwhi->GetView()->set_current_device_scale_factor(new_scale_factor);
+  }
+
+  return true;
+}
+
+bool CefRenderWidgetHostViewOSR::SetViewBounds() {
+  // This method should not be called while the resize hold is active.
+  DCHECK(!hold_resize_);
+
+  // Popup bounds are set in InitAsPopup.
+  if (IsPopupWidget())
+    return false;
+
+  const gfx::Rect& new_bounds = ::GetViewBounds(browser_impl_.get());
+  if (new_bounds == current_view_bounds_)
+    return false;
+
+  current_view_bounds_ = new_bounds;
+  return true;
+}
+
+bool CefRenderWidgetHostViewOSR::SetRootLayerSize(bool force) {
+  const bool scale_factor_changed = SetDeviceScaleFactor();
+  const bool view_bounds_changed = SetViewBounds();
+  if (!force && !scale_factor_changed && !view_bounds_changed)
+    return false;
+
+  GetRootLayer()->SetBounds(gfx::Rect(GetViewBounds().size()));
+
+  if (compositor_) {
+    compositor_local_surface_id_allocator_.GenerateId();
+    compositor_->SetScaleAndSize(current_device_scale_factor_, SizeInPixels(),
+                                 compositor_local_surface_id_allocator_
+                                     .GetCurrentLocalSurfaceIdAllocation());
+  }
+
+  return (scale_factor_changed || view_bounds_changed);
+}
+
+bool CefRenderWidgetHostViewOSR::ResizeRootLayer() {
+  if (!hold_resize_) {
+    // The resize hold is not currently active.
+    if (SetRootLayerSize(false /* force */)) {
+      // The size has changed. Avoid resizing again until ReleaseResizeHold() is
+      // called.
+      hold_resize_ = true;
+      return true;
+    }
+  } else if (!pending_resize_) {
+    // The resize hold is currently active. Another resize will be triggered
+    // from ReleaseResizeHold().
+    pending_resize_ = true;
+  }
+  return false;
+}
+
+void CefRenderWidgetHostViewOSR::ReleaseResizeHold() {
+  DCHECK(hold_resize_);
+  hold_resize_ = false;
+  if (pending_resize_) {
+    pending_resize_ = false;
+    CEF_POST_TASK(CEF_UIT, base::Bind(&CefRenderWidgetHostViewOSR::WasResized,
+                                      weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+void CefRenderWidgetHostViewOSR::CancelWidget() {
+  if (render_widget_host_)
+    render_widget_host_->LostCapture();
+
+  Hide();
+
+  if (IsPopupWidget() && browser_impl_.get()) {
+    CefRefPtr<CefRenderHandler> handler =
+        browser_impl_->client()->GetRenderHandler();
+    CHECK(handler);
+    handler->OnPopupShow(browser_impl_.get(), false);
+    browser_impl_ = nullptr;
+  }
+
+  if (parent_host_view_) {
+    if (parent_host_view_->popup_host_view_ == this) {
+      parent_host_view_->set_popup_host_view(nullptr);
+    } else if (parent_host_view_->child_host_view_ == this) {
+      parent_host_view_->set_child_host_view(nullptr);
+
+      // Start rendering the parent view again.
+      parent_host_view_->Show();
+    } else {
+      parent_host_view_->RemoveGuestHostView(this);
+    }
+    parent_host_view_ = nullptr;
+  }
+
+  if (render_widget_host_ && !is_destroyed_) {
+    is_destroyed_ = true;
+
+    // Don't delete the RWHI manually while owned by a scoped_ptr in RVHI.
+    // This matches a CHECK() in RenderWidgetHostImpl::Destroy().
+    const bool also_delete = !render_widget_host_->owner_delegate();
+
+    // Results in a call to Destroy().
+    render_widget_host_->ShutdownAndDestroyWidget(also_delete);
+  }
+}
+
+void CefRenderWidgetHostViewOSR::OnScrollOffsetChanged() {
+  if (browser_impl_.get()) {
+    CefRefPtr<CefRenderHandler> handler =
+        browser_impl_->client()->GetRenderHandler();
+    CHECK(handler);
+    handler->OnScrollOffsetChanged(browser_impl_.get(), last_scroll_offset_.x(),
+                                   last_scroll_offset_.y());
+  }
+  is_scroll_offset_changed_pending_ = false;
+}
+
+void CefRenderWidgetHostViewOSR::AddGuestHostView(
+    CefRenderWidgetHostViewOSR* guest_host) {
+  guest_host_views_.insert(guest_host);
+}
+
+void CefRenderWidgetHostViewOSR::RemoveGuestHostView(
+    CefRenderWidgetHostViewOSR* guest_host) {
+  guest_host_views_.erase(guest_host);
+}
+
+void CefRenderWidgetHostViewOSR::InvalidateInternal(
+    const gfx::Rect& bounds_in_pixels) {
+  if (video_consumer_) {
+    video_consumer_->RequestRefreshFrame(bounds_in_pixels);
+  } else if (host_display_client_) {
+    OnPaint(bounds_in_pixels, host_display_client_->GetPixelSize(),
+            host_display_client_->GetPixelMemory());
+  }
+}
+
+void CefRenderWidgetHostViewOSR::RequestImeCompositionUpdate(
+    bool start_monitoring) {
+  if (!render_widget_host_)
+    return;
+  render_widget_host_->RequestCompositionUpdates(false, start_monitoring);
+}
+
+void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged(
+    const gfx::Range& range,
+    const std::vector<gfx::Rect>& character_bounds) {
+  if (browser_impl_.get()) {
+    CefRange cef_range(range.start(), range.end());
+    CefRenderHandler::RectList rcList;
+
+    for (size_t i = 0; i < character_bounds.size(); ++i) {
+      rcList.push_back(CefRect(character_bounds[i].x(), character_bounds[i].y(),
+                               character_bounds[i].width(),
+                               character_bounds[i].height()));
+    }
+
+    CefRefPtr<CefRenderHandler> handler =
+        browser_impl_->GetClient()->GetRenderHandler();
+    CHECK(handler);
+    handler->OnImeCompositionRangeChanged(browser_impl_->GetBrowser(),
+                                          cef_range, rcList);
+  }
+}
+
+viz::FrameSinkId CefRenderWidgetHostViewOSR::AllocateFrameSinkId() {
+  return render_widget_host_->GetFrameSinkId();
+}
+
+void CefRenderWidgetHostViewOSR::UpdateBackgroundColorFromRenderer(
+    SkColor color) {
+  if (color == background_color_)
+    return;
+  background_color_ = color;
+
+  bool opaque = SkColorGetA(color) == SK_AlphaOPAQUE;
+  GetRootLayer()->SetFillsBoundsOpaquely(opaque);
+  GetRootLayer()->SetColor(color);
+}
diff --git a/src/libcef/browser/osr/render_widget_host_view_osr.h b/src/libcef/browser/osr/render_widget_host_view_osr.h
new file mode 100644
index 0000000..3e4a970
--- /dev/null
+++ b/src/libcef/browser/osr/render_widget_host_view_osr.h
@@ -0,0 +1,412 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_RENDER_WIDGET_HOST_VIEW_OSR_H_
+#define CEF_LIBCEF_BROWSER_OSR_RENDER_WIDGET_HOST_VIEW_OSR_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "include/cef_base.h"
+#include "include/cef_browser.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/osr/host_display_client_osr.h"
+#include "libcef/browser/osr/motion_event_osr.h"
+
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "build/build_config.h"
+#include "cc/layers/deadline_policy.h"
+#include "components/viz/common/frame_sinks/begin_frame_source.h"
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/renderer_host/text_input_manager.h"
+#include "content/public/common/widget_type.h"
+#include "ui/base/mojom/cursor_type.mojom-shared.h"
+#include "ui/compositor/compositor.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/gesture_detection/filtered_gesture_provider.h"
+#include "ui/events/gesture_detection/gesture_configuration.h"
+#include "ui/events/gesture_detection/motion_event_generic.h"
+#include "ui/gfx/geometry/rect.h"
+
+#if defined(OS_LINUX)
+#include "ui/base/x/x11_util.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "content/browser/renderer_host/browser_compositor_view_mac.h"
+#endif
+
+#if defined(OS_WIN)
+#include "ui/gfx/win/window_impl.h"
+#endif
+
+#if defined(USE_AURA)
+#include "ui/base/cursor/cursor.h"
+#endif
+
+namespace content {
+class DelegatedFrameHost;
+class DelegatedFrameHostClient;
+class RenderWidgetHost;
+class RenderWidgetHostImpl;
+class RenderWidgetHostViewGuest;
+class BackingStore;
+class CursorManager;
+}  // namespace content
+
+class CefCopyFrameGenerator;
+class CefSoftwareOutputDeviceOSR;
+class CefVideoConsumerOSR;
+class CefWebContentsViewOSR;
+
+#if defined(USE_X11)
+class CefWindowX11;
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// CefRenderWidgetHostViewOSR
+//
+//  An object representing the "View" of a rendered web page. This object is
+//  responsible for sending paint events to the the CefRenderHandler
+//  when window rendering is disabled. It is the implementation of the
+//  RenderWidgetHostView that the cross-platform RenderWidgetHost object uses
+//  to display the data.
+//
+//  Comment excerpted from render_widget_host.h:
+//
+//    "The lifetime of the RenderWidgetHostView is tied to the render process.
+//     If the render process dies, the RenderWidgetHostView goes away and all
+//     references to it must become NULL."
+//
+// RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(OS_MACOSX)
+class MacHelper;
+#endif
+
+class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase,
+                                   public ui::CompositorDelegate,
+                                   public content::TextInputManager::Observer,
+                                   public ui::GestureProviderClient {
+ public:
+  CefRenderWidgetHostViewOSR(SkColor background_color,
+                             bool use_shared_texture,
+                             bool use_external_begin_frame,
+                             content::RenderWidgetHost* widget,
+                             CefRenderWidgetHostViewOSR* parent_host_view);
+  ~CefRenderWidgetHostViewOSR() override;
+
+  // RenderWidgetHostView implementation.
+  void InitAsChild(gfx::NativeView parent_view) override;
+  void SetSize(const gfx::Size& size) override;
+  void SetBounds(const gfx::Rect& rect) override;
+  gfx::NativeView GetNativeView() override;
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
+  void Focus() override;
+  bool HasFocus() override;
+  uint32_t GetCaptureSequenceNumber() const override;
+  bool IsSurfaceAvailableForCopy() override;
+  void Show() override;
+  void Hide() override;
+  bool IsShowing() override;
+  void EnsureSurfaceSynchronizedForWebTest() override;
+  gfx::Rect GetViewBounds() override;
+  void SetBackgroundColor(SkColor color) override;
+  base::Optional<SkColor> GetBackgroundColor() override;
+  void UpdateBackgroundColor() override;
+  blink::mojom::PointerLockResult LockMouse(
+      bool request_unadjusted_movement) override;
+  blink::mojom::PointerLockResult ChangeMouseLock(
+      bool request_unadjusted_movement) override;
+  void UnlockMouse() override;
+  void TakeFallbackContentFrom(content::RenderWidgetHostView* view) override;
+
+#if defined(OS_MACOSX)
+  void SetActive(bool active) override;
+  void ShowDefinitionForSelection() override;
+  void SpeakSelection() override;
+#endif  // defined(OS_MACOSX)
+
+  // RenderWidgetHostViewBase implementation.
+  void ResetFallbackToFirstNavigationSurface() override;
+  void InitAsPopup(content::RenderWidgetHostView* parent_host_view,
+                   const gfx::Rect& pos) override;
+  void InitAsFullscreen(
+      content::RenderWidgetHostView* reference_host_view) override;
+  void UpdateCursor(const content::WebCursor& cursor) override;
+  void SetIsLoading(bool is_loading) override;
+  void RenderProcessGone() override;
+  void Destroy() override;
+  void SetTooltipText(const base::string16& tooltip_text) override;
+  content::CursorManager* GetCursorManager() override;
+  gfx::Size GetCompositorViewportPixelSize() override;
+  void CopyFromSurface(
+      const gfx::Rect& src_rect,
+      const gfx::Size& output_size,
+      base::OnceCallback<void(const SkBitmap&)> callback) override;
+  void GetScreenInfo(content::ScreenInfo* results) override;
+  void TransformPointToRootSurface(gfx::PointF* point) override;
+  gfx::Rect GetBoundsInRootWindow() override;
+
+#if !defined(OS_MACOSX)
+  viz::ScopedSurfaceIdAllocator DidUpdateVisualProperties(
+      const cc::RenderFrameMetadata& metadata) override;
+#endif
+
+  viz::SurfaceId GetCurrentSurfaceId() const override;
+  content::BrowserAccessibilityManager* CreateBrowserAccessibilityManager(
+      content::BrowserAccessibilityDelegate* delegate,
+      bool for_root_frame) override;
+  void ImeCompositionRangeChanged(
+      const gfx::Range& range,
+      const std::vector<gfx::Rect>& character_bounds) override;
+  std::unique_ptr<content::SyntheticGestureTarget>
+  CreateSyntheticGestureTarget() override;
+  bool TransformPointToCoordSpaceForView(
+      const gfx::PointF& point,
+      RenderWidgetHostViewBase* target_view,
+      gfx::PointF* transformed_point) override;
+  void DidNavigate() override;
+  void SelectionChanged(const base::string16& text,
+                        size_t offset,
+                        const gfx::Range& range) override;
+  const viz::LocalSurfaceIdAllocation& GetLocalSurfaceIdAllocation()
+      const override;
+  const viz::FrameSinkId& GetFrameSinkId() const override;
+  viz::FrameSinkId GetRootFrameSinkId() override;
+  void OnRenderFrameMetadataChangedAfterActivation() override;
+
+  void OnFrameComplete(const viz::BeginFrameAck& ack);
+
+  // ui::CompositorDelegate implementation.
+  std::unique_ptr<viz::HostDisplayClient> CreateHostDisplayClient() override;
+
+  // TextInputManager::Observer implementation.
+  void OnUpdateTextInputStateCalled(
+      content::TextInputManager* text_input_manager,
+      RenderWidgetHostViewBase* updated_view,
+      bool did_update_state) override;
+
+  // ui::GestureProviderClient implementation.
+  void ProcessAckedTouchEvent(const content::TouchEventWithLatencyInfo& touch,
+                              content::InputEventAckState ack_result) override;
+  void OnGestureEvent(const ui::GestureEventData& gesture) override;
+
+  bool InstallTransparency();
+
+  void WasResized();
+  void SynchronizeVisualProperties(
+      const cc::DeadlinePolicy& deadline_policy,
+      const base::Optional<viz::LocalSurfaceIdAllocation>&
+          child_local_surface_id_allocation);
+  void OnScreenInfoChanged();
+  void Invalidate(CefBrowserHost::PaintElementType type);
+  void SendExternalBeginFrame();
+  void SendKeyEvent(const content::NativeWebKeyboardEvent& event);
+  void SendMouseEvent(const blink::WebMouseEvent& event);
+  void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event);
+  void SendTouchEvent(const CefTouchEvent& event);
+  bool ShouldRouteEvents() const;
+  void SendFocusEvent(bool focus);
+  void UpdateFrameRate();
+
+  gfx::Size SizeInPixels();
+  void OnPaint(const gfx::Rect& damage_rect,
+               const gfx::Size& pixel_size,
+               const void* pixels);
+
+  void OnBeginFame(base::TimeTicks frame_time);
+
+  bool IsPopupWidget() const {
+    return widget_type_ == content::WidgetType::kPopup;
+  }
+
+  void ImeSetComposition(const CefString& text,
+                         const std::vector<CefCompositionUnderline>& underlines,
+                         const CefRange& replacement_range,
+                         const CefRange& selection_range);
+  void ImeCommitText(const CefString& text,
+                     const CefRange& replacement_range,
+                     int relative_cursor_pos);
+  void ImeFinishComposingText(bool keep_selection);
+  void ImeCancelComposition() override;
+
+  CefRefPtr<CefBrowserHostImpl> browser_impl() const { return browser_impl_; }
+  void set_browser_impl(CefRefPtr<CefBrowserHostImpl> browser) {
+    browser_impl_ = browser;
+  }
+
+  void set_popup_host_view(CefRenderWidgetHostViewOSR* popup_view) {
+    if (popup_view != popup_host_view_)
+      forward_touch_to_popup_ = false;
+    popup_host_view_ = popup_view;
+  }
+  void set_child_host_view(CefRenderWidgetHostViewOSR* popup_view) {
+    child_host_view_ = popup_view;
+  }
+
+  content::RenderWidgetHostImpl* render_widget_host() const {
+    return render_widget_host_;
+  }
+  ui::Layer* GetRootLayer() const;
+
+  void OnPresentCompositorFrame();
+
+  void OnDidUpdateVisualPropertiesComplete(
+      const cc::RenderFrameMetadata& metadata);
+
+ private:
+  void SetFrameRate();
+  bool SetDeviceScaleFactor();
+  bool SetViewBounds();
+  bool SetRootLayerSize(bool force);
+
+  // Manages resizing so that only one resize request is in-flight at a time.
+  bool ResizeRootLayer();
+  void ReleaseResizeHold();
+
+  void CancelWidget();
+
+  void OnScrollOffsetChanged();
+
+  void AddGuestHostView(CefRenderWidgetHostViewOSR* guest_host);
+  void RemoveGuestHostView(CefRenderWidgetHostViewOSR* guest_host);
+
+  // Register a callback that will be executed when |guest_host_view| receives
+  // OnSwapCompositorFrame. The callback triggers repaint of the embedder view.
+  void RegisterGuestViewFrameSwappedCallback(
+      content::RenderWidgetHostViewGuest* guest_host_view);
+
+  void OnGuestViewFrameSwapped(
+      content::RenderWidgetHostViewGuest* guest_host_view);
+
+  void InvalidateInternal(const gfx::Rect& bounds_in_pixels);
+
+  void RequestImeCompositionUpdate(bool start_monitoring);
+
+  viz::FrameSinkId AllocateFrameSinkId();
+
+  // Forces the view to allocate a new viz::LocalSurfaceId for the next
+  // CompositorFrame submission in anticipation of a synchronization operation
+  // that does not involve a resize or a device scale factor change.
+  void AllocateLocalSurfaceId();
+  const viz::LocalSurfaceIdAllocation& GetCurrentLocalSurfaceIdAllocation()
+      const;
+
+  // Sets the current viz::LocalSurfaceId, in cases where the embedded client
+  // has allocated one. Also sets child sequence number component of the
+  // viz::LocalSurfaceId allocator.
+  void UpdateLocalSurfaceIdFromEmbeddedClient(
+      const base::Optional<viz::LocalSurfaceIdAllocation>&
+          local_surface_id_allocation);
+
+  // Returns the current viz::LocalSurfaceIdAllocation.
+  const viz::LocalSurfaceIdAllocation& GetOrCreateLocalSurfaceIdAllocation();
+
+  // Marks the current viz::LocalSurfaceId as invalid. AllocateLocalSurfaceId
+  // must be called before submitting new CompositorFrames.
+  void InvalidateLocalSurfaceId();
+
+  void AddDamageRect(uint32_t sequence, const gfx::Rect& rect);
+
+  // Applies background color without notifying the RenderWidget about
+  // opaqueness changes.
+  void UpdateBackgroundColorFromRenderer(SkColor color);
+
+#if defined(USE_AURA)
+  ui::PlatformCursor GetPlatformCursor(ui::mojom::CursorType type);
+#endif
+
+  // The background color of the web content.
+  SkColor background_color_;
+
+  int frame_rate_threshold_us_ = 0;
+
+  std::unique_ptr<ui::Compositor> compositor_;
+  std::unique_ptr<content::DelegatedFrameHost> delegated_frame_host_;
+  std::unique_ptr<content::DelegatedFrameHostClient>
+      delegated_frame_host_client_;
+  std::unique_ptr<ui::Layer> root_layer_;
+
+  // Used to allocate LocalSurfaceIds when this is embedding external content.
+  std::unique_ptr<viz::ParentLocalSurfaceIdAllocator>
+      parent_local_surface_id_allocator_;
+  viz::ParentLocalSurfaceIdAllocator compositor_local_surface_id_allocator_;
+
+#if defined(USE_X11)
+  std::unique_ptr<ui::XScopedCursor> invisible_cursor_;
+#endif
+
+  std::unique_ptr<content::CursorManager> cursor_manager_;
+
+  // Provides |source_id| for BeginFrameArgs that we create.
+  viz::StubBeginFrameSource begin_frame_source_;
+  uint64_t begin_frame_number_ = viz::BeginFrameArgs::kStartingFrameNumber;
+
+  bool sync_frame_rate_ = false;
+  bool external_begin_frame_enabled_ = false;
+  bool needs_external_begin_frames_ = false;
+
+  CefHostDisplayClientOSR* host_display_client_ = nullptr;
+  std::unique_ptr<CefVideoConsumerOSR> video_consumer_;
+
+  bool hold_resize_ = false;
+  bool pending_resize_ = false;
+
+  // The associated Model.  While |this| is being Destroyed,
+  // |render_widget_host_| is NULL and the message loop is run one last time
+  // Message handlers must check for a NULL |render_widget_host_|.
+  content::RenderWidgetHostImpl* render_widget_host_;
+
+  bool has_parent_;
+  CefRenderWidgetHostViewOSR* parent_host_view_;
+  CefRenderWidgetHostViewOSR* popup_host_view_ = nullptr;
+  CefRenderWidgetHostViewOSR* child_host_view_ = nullptr;
+  std::set<CefRenderWidgetHostViewOSR*> guest_host_views_;
+
+  CefRefPtr<CefBrowserHostImpl> browser_impl_;
+
+  bool is_showing_ = false;
+  bool is_destroyed_ = false;
+  bool is_first_navigation_ = true;
+  gfx::Rect current_view_bounds_;
+  gfx::Rect popup_position_;
+  base::Lock damage_rect_lock_;
+  std::map<uint32_t, gfx::Rect> damage_rects_;
+
+  // Whether pinch-to-zoom should be enabled and pinch events forwarded to the
+  // renderer.
+  bool pinch_zoom_enabled_;
+
+  // The last scroll offset of the view.
+  gfx::Vector2dF last_scroll_offset_;
+  bool is_scroll_offset_changed_pending_ = false;
+
+  content::MouseWheelPhaseHandler mouse_wheel_phase_handler_;
+
+  // Latest capture sequence number which is incremented when the caller
+  // requests surfaces be synchronized via
+  // EnsureSurfaceSynchronizedForLayoutTest().
+  uint32_t latest_capture_sequence_number_ = 0u;
+
+  // ui::GestureProviderClient implementation.
+  ui::FilteredGestureProvider gesture_provider_;
+
+  CefMotionEventOSR pointer_state_;
+  bool forward_touch_to_popup_ = false;
+
+  base::WeakPtrFactory<CefRenderWidgetHostViewOSR> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefRenderWidgetHostViewOSR);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_OSR_RENDER_WIDGET_HOST_VIEW_OSR_H_
diff --git a/src/libcef/browser/osr/render_widget_host_view_osr_linux.cc b/src/libcef/browser/osr/render_widget_host_view_osr_linux.cc
new file mode 100644
index 0000000..0e4025b
--- /dev/null
+++ b/src/libcef/browser/osr/render_widget_host_view_osr_linux.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+
+#if defined(USE_X11)
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+#undef Status  // Avoid conflicts with url_request_status.h
+
+#include "libcef/browser/native/window_x11.h"
+
+#include "ui/base/x/x11_util.h"
+#include "ui/gfx/x/x11_types.h"
+#endif  // defined(USE_X11)
+
+#if defined(USE_X11)
+namespace {
+
+// Based on ui/base/cursor/cursor_loader_x11.cc.
+
+int ToCursorID(ui::mojom::CursorType type) {
+  switch (type) {
+    case ui::mojom::CursorType::kPointer:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kCross:
+      return XC_crosshair;
+    case ui::mojom::CursorType::kHand:
+      return XC_hand2;
+    case ui::mojom::CursorType::kIBeam:
+      return XC_xterm;
+    case ui::mojom::CursorType::kWait:
+      return XC_watch;
+    case ui::mojom::CursorType::kHelp:
+      return XC_question_arrow;
+    case ui::mojom::CursorType::kEastResize:
+      return XC_right_side;
+    case ui::mojom::CursorType::kNorthResize:
+      return XC_top_side;
+    case ui::mojom::CursorType::kNorthEastResize:
+      return XC_top_right_corner;
+    case ui::mojom::CursorType::kNorthWestResize:
+      return XC_top_left_corner;
+    case ui::mojom::CursorType::kSouthResize:
+      return XC_bottom_side;
+    case ui::mojom::CursorType::kSouthEastResize:
+      return XC_bottom_right_corner;
+    case ui::mojom::CursorType::kSouthWestResize:
+      return XC_bottom_left_corner;
+    case ui::mojom::CursorType::kWestResize:
+      return XC_left_side;
+    case ui::mojom::CursorType::kNorthSouthResize:
+      return XC_sb_v_double_arrow;
+    case ui::mojom::CursorType::kEastWestResize:
+      return XC_sb_h_double_arrow;
+    case ui::mojom::CursorType::kNorthEastSouthWestResize:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kNorthWestSouthEastResize:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kColumnResize:
+      return XC_sb_h_double_arrow;
+    case ui::mojom::CursorType::kRowResize:
+      return XC_sb_v_double_arrow;
+    case ui::mojom::CursorType::kMiddlePanning:
+      return XC_fleur;
+    case ui::mojom::CursorType::kEastPanning:
+      return XC_sb_right_arrow;
+    case ui::mojom::CursorType::kNorthPanning:
+      return XC_sb_up_arrow;
+    case ui::mojom::CursorType::kNorthEastPanning:
+      return XC_top_right_corner;
+    case ui::mojom::CursorType::kNorthWestPanning:
+      return XC_top_left_corner;
+    case ui::mojom::CursorType::kSouthPanning:
+      return XC_sb_down_arrow;
+    case ui::mojom::CursorType::kSouthEastPanning:
+      return XC_bottom_right_corner;
+    case ui::mojom::CursorType::kSouthWestPanning:
+      return XC_bottom_left_corner;
+    case ui::mojom::CursorType::kWestPanning:
+      return XC_sb_left_arrow;
+    case ui::mojom::CursorType::kMove:
+      return XC_fleur;
+    case ui::mojom::CursorType::kVerticalText:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kCell:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kContextMenu:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kAlias:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kProgress:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kNoDrop:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kCopy:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kNotAllowed:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kZoomIn:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kZoomOut:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kGrab:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kGrabbing:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kMiddlePanningVertical:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kMiddlePanningHorizontal:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kDndNone:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kDndMove:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kDndCopy:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kDndLink:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kNull:
+      return XC_left_ptr;
+    case ui::mojom::CursorType::kCustom:
+    case ui::mojom::CursorType::kNone:
+      break;
+  }
+  NOTREACHED();
+  return 0;
+}
+
+// The following XCursorCache code was deleted from ui/base/x/x11_util.cc in
+// https://crbug.com/665574#c2
+
+// A process wide singleton that manages the usage of X cursors.
+class XCursorCache {
+ public:
+  XCursorCache() {}
+  ~XCursorCache() { Clear(); }
+
+  ::Cursor GetCursor(int cursor_shape) {
+    // Lookup cursor by attempting to insert a null value, which avoids
+    // a second pass through the map after a cache miss.
+    std::pair<std::map<int, ::Cursor>::iterator, bool> it =
+        cache_.insert(std::make_pair(cursor_shape, 0));
+    if (it.second) {
+      XDisplay* display = gfx::GetXDisplay();
+      it.first->second = XCreateFontCursor(display, cursor_shape);
+    }
+    return it.first->second;
+  }
+
+  void Clear() {
+    XDisplay* display = gfx::GetXDisplay();
+    for (std::map<int, ::Cursor>::iterator it = cache_.begin();
+         it != cache_.end(); ++it) {
+      XFreeCursor(display, it->second);
+    }
+    cache_.clear();
+  }
+
+ private:
+  // Maps X11 font cursor shapes to Cursor IDs.
+  std::map<int, ::Cursor> cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(XCursorCache);
+};
+
+XCursorCache* cursor_cache = nullptr;
+
+// Returns an X11 Cursor, sharable across the process.
+// |cursor_shape| is an X font cursor shape, see XCreateFontCursor().
+::Cursor GetXCursor(int cursor_shape) {
+  if (!cursor_cache)
+    cursor_cache = new XCursorCache;
+  return cursor_cache->GetCursor(cursor_shape);
+}
+
+}  // namespace
+#endif  // defined(USE_X11)
+
+ui::PlatformCursor CefRenderWidgetHostViewOSR::GetPlatformCursor(
+    ui::mojom::CursorType type) {
+#if defined(USE_X11)
+  if (type == ui::mojom::CursorType::kNone) {
+    if (!invisible_cursor_) {
+      invisible_cursor_.reset(new ui::XScopedCursor(ui::CreateInvisibleCursor(),
+                                                    gfx::GetXDisplay()));
+    }
+    return invisible_cursor_->get();
+  } else {
+    return GetXCursor(ToCursorID(type));
+  }
+#endif  // defined(USE_X11)
+  return 0;
+}
diff --git a/src/libcef/browser/osr/render_widget_host_view_osr_win.cc b/src/libcef/browser/osr/render_widget_host_view_osr_win.cc
new file mode 100644
index 0000000..45c68a9
--- /dev/null
+++ b/src/libcef/browser/osr/render_widget_host_view_osr_win.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+
+#include <windows.h>
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/content_browser_client.h"
+
+#include "ui/resources/grit/ui_unscaled_resources.h"
+
+namespace {
+
+class CefCompositorHostWin : public gfx::WindowImpl {
+ public:
+  CefCompositorHostWin() {
+    // Create a hidden 1x1 borderless window.
+    set_window_style(WS_POPUP | WS_SYSMENU);
+    Init(NULL, gfx::Rect(0, 0, 1, 1));
+  }
+
+  ~CefCompositorHostWin() override { DestroyWindow(hwnd()); }
+
+ private:
+  CR_BEGIN_MSG_MAP_EX(CefCompositorHostWin)
+    CR_MSG_WM_PAINT(OnPaint)
+  CR_END_MSG_MAP()
+
+  CR_MSG_MAP_CLASS_DECLARATIONS(CefCompositorHostWin)
+
+  void OnPaint(HDC dc) { ValidateRect(hwnd(), NULL); }
+
+  DISALLOW_COPY_AND_ASSIGN(CefCompositorHostWin);
+};
+
+// From content/common/cursors/webcursor_win.cc.
+
+LPCWSTR ToCursorID(ui::mojom::CursorType type) {
+  switch (type) {
+    case ui::mojom::CursorType::kPointer:
+      return IDC_ARROW;
+    case ui::mojom::CursorType::kCross:
+      return IDC_CROSS;
+    case ui::mojom::CursorType::kHand:
+      return IDC_HAND;
+    case ui::mojom::CursorType::kIBeam:
+      return IDC_IBEAM;
+    case ui::mojom::CursorType::kWait:
+      return IDC_WAIT;
+    case ui::mojom::CursorType::kHelp:
+      return IDC_HELP;
+    case ui::mojom::CursorType::kEastResize:
+      return IDC_SIZEWE;
+    case ui::mojom::CursorType::kNorthResize:
+      return IDC_SIZENS;
+    case ui::mojom::CursorType::kNorthEastResize:
+      return IDC_SIZENESW;
+    case ui::mojom::CursorType::kNorthWestResize:
+      return IDC_SIZENWSE;
+    case ui::mojom::CursorType::kSouthResize:
+      return IDC_SIZENS;
+    case ui::mojom::CursorType::kSouthEastResize:
+      return IDC_SIZENWSE;
+    case ui::mojom::CursorType::kSouthWestResize:
+      return IDC_SIZENESW;
+    case ui::mojom::CursorType::kWestResize:
+      return IDC_SIZEWE;
+    case ui::mojom::CursorType::kNorthSouthResize:
+      return IDC_SIZENS;
+    case ui::mojom::CursorType::kEastWestResize:
+      return IDC_SIZEWE;
+    case ui::mojom::CursorType::kNorthEastSouthWestResize:
+      return IDC_SIZENESW;
+    case ui::mojom::CursorType::kNorthWestSouthEastResize:
+      return IDC_SIZENWSE;
+    case ui::mojom::CursorType::kColumnResize:
+      return MAKEINTRESOURCE(IDC_COLRESIZE);
+    case ui::mojom::CursorType::kRowResize:
+      return MAKEINTRESOURCE(IDC_ROWRESIZE);
+    case ui::mojom::CursorType::kMiddlePanning:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE);
+    case ui::mojom::CursorType::kEastPanning:
+      return MAKEINTRESOURCE(IDC_PAN_EAST);
+    case ui::mojom::CursorType::kNorthPanning:
+      return MAKEINTRESOURCE(IDC_PAN_NORTH);
+    case ui::mojom::CursorType::kNorthEastPanning:
+      return MAKEINTRESOURCE(IDC_PAN_NORTH_EAST);
+    case ui::mojom::CursorType::kNorthWestPanning:
+      return MAKEINTRESOURCE(IDC_PAN_NORTH_WEST);
+    case ui::mojom::CursorType::kSouthPanning:
+      return MAKEINTRESOURCE(IDC_PAN_SOUTH);
+    case ui::mojom::CursorType::kSouthEastPanning:
+      return MAKEINTRESOURCE(IDC_PAN_SOUTH_EAST);
+    case ui::mojom::CursorType::kSouthWestPanning:
+      return MAKEINTRESOURCE(IDC_PAN_SOUTH_WEST);
+    case ui::mojom::CursorType::kWestPanning:
+      return MAKEINTRESOURCE(IDC_PAN_WEST);
+    case ui::mojom::CursorType::kMove:
+      return IDC_SIZEALL;
+    case ui::mojom::CursorType::kVerticalText:
+      return MAKEINTRESOURCE(IDC_VERTICALTEXT);
+    case ui::mojom::CursorType::kCell:
+      return MAKEINTRESOURCE(IDC_CELL);
+    case ui::mojom::CursorType::kContextMenu:
+      return IDC_ARROW;
+    case ui::mojom::CursorType::kAlias:
+      return MAKEINTRESOURCE(IDC_ALIAS);
+    case ui::mojom::CursorType::kProgress:
+      return IDC_APPSTARTING;
+    case ui::mojom::CursorType::kNoDrop:
+      return IDC_NO;
+    case ui::mojom::CursorType::kCopy:
+      return MAKEINTRESOURCE(IDC_COPYCUR);
+    case ui::mojom::CursorType::kNone:
+      return MAKEINTRESOURCE(IDC_CURSOR_NONE);
+    case ui::mojom::CursorType::kNotAllowed:
+      return IDC_NO;
+    case ui::mojom::CursorType::kZoomIn:
+      return MAKEINTRESOURCE(IDC_ZOOMIN);
+    case ui::mojom::CursorType::kZoomOut:
+      return MAKEINTRESOURCE(IDC_ZOOMOUT);
+    case ui::mojom::CursorType::kGrab:
+      return MAKEINTRESOURCE(IDC_HAND_GRAB);
+    case ui::mojom::CursorType::kGrabbing:
+      return MAKEINTRESOURCE(IDC_HAND_GRABBING);
+    case ui::mojom::CursorType::kNull:
+      return IDC_NO;
+    case ui::mojom::CursorType::kMiddlePanningVertical:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_VERTICAL);
+    case ui::mojom::CursorType::kMiddlePanningHorizontal:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_HORIZONTAL);
+    // TODO(cef): Find better cursors for these things
+    case ui::mojom::CursorType::kDndNone:
+      return IDC_ARROW;
+    case ui::mojom::CursorType::kDndMove:
+      return IDC_ARROW;
+    case ui::mojom::CursorType::kDndCopy:
+      return IDC_ARROW;
+    case ui::mojom::CursorType::kDndLink:
+      return IDC_ARROW;
+    case ui::mojom::CursorType::kCustom:
+      break;
+  }
+  NOTREACHED();
+  return NULL;
+}
+
+bool IsSystemCursorID(LPCWSTR cursor_id) {
+  return cursor_id >= IDC_ARROW;  // See WinUser.h
+}
+
+}  // namespace
+
+ui::PlatformCursor CefRenderWidgetHostViewOSR::GetPlatformCursor(
+    ui::mojom::CursorType type) {
+  HMODULE module_handle = NULL;
+  const wchar_t* cursor_id = ToCursorID(type);
+  if (!IsSystemCursorID(cursor_id)) {
+    module_handle =
+        ::GetModuleHandle(CefContentBrowserClient::Get()->GetResourceDllName());
+    if (!module_handle)
+      module_handle = ::GetModuleHandle(NULL);
+  }
+
+  return LoadCursor(module_handle, cursor_id);
+}
diff --git a/src/libcef/browser/osr/software_output_device_proxy.cc b/src/libcef/browser/osr/software_output_device_proxy.cc
new file mode 100644
index 0000000..3f33466
--- /dev/null
+++ b/src/libcef/browser/osr/software_output_device_proxy.cc
@@ -0,0 +1,146 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cef/libcef/browser/osr/software_output_device_proxy.h"
+
+#include "base/memory/shared_memory_mapping.h"
+#include "base/trace_event/trace_event.h"
+#include "components/viz/common/resources/resource_sizes.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/skia_util.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "skia/ext/skia_utils_win.h"
+#include "ui/gfx/gdi_util.h"
+#include "ui/gfx/win/hwnd_util.h"
+#endif
+
+namespace viz {
+
+SoftwareOutputDeviceProxy::~SoftwareOutputDeviceProxy() = default;
+
+SoftwareOutputDeviceProxy::SoftwareOutputDeviceProxy(
+    mojom::LayeredWindowUpdaterPtr layered_window_updater)
+    : layered_window_updater_(std::move(layered_window_updater)) {
+  DCHECK(layered_window_updater_.is_bound());
+}
+
+void SoftwareOutputDeviceProxy::OnSwapBuffers(
+    SwapBuffersCallback swap_ack_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(swap_ack_callback_.is_null());
+
+  // We aren't waiting on DrawAck() and can immediately run the callback.
+  if (!waiting_on_draw_ack_) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_));
+    return;
+  }
+
+  swap_ack_callback_ =
+      base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_);
+}
+
+void SoftwareOutputDeviceProxy::Resize(const gfx::Size& viewport_pixel_size,
+                                       float scale_factor) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(!in_paint_);
+
+  if (viewport_pixel_size_ == viewport_pixel_size)
+    return;
+
+  viewport_pixel_size_ = viewport_pixel_size;
+
+  canvas_.reset();
+
+  size_t required_bytes;
+  if (!ResourceSizes::MaybeSizeInBytes(
+          viewport_pixel_size_, ResourceFormat::RGBA_8888, &required_bytes)) {
+    DLOG(ERROR) << "Invalid viewport size " << viewport_pixel_size_.ToString();
+    return;
+  }
+
+  base::UnsafeSharedMemoryRegion region =
+      base::UnsafeSharedMemoryRegion::Create(required_bytes);
+  if (!region.IsValid()) {
+    DLOG(ERROR) << "Failed to allocate " << required_bytes << " bytes";
+    return;
+  }
+
+#if !defined(OS_WIN)
+  auto shm = base::ReadOnlySharedMemoryRegion::Create(required_bytes);
+  if (!shm.IsValid()) {
+    DLOG(ERROR) << "Failed to allocate " << required_bytes << " bytes";
+    return;
+  }
+
+  shm_ = region.Map();
+  if (!shm_.IsValid()) {
+    DLOG(ERROR) << "Failed to map " << required_bytes << " bytes";
+    return;
+  }
+
+  canvas_ = skia::CreatePlatformCanvasWithPixels(
+      viewport_pixel_size_.width(), viewport_pixel_size_.height(), false,
+      static_cast<uint8_t*>(shm_.memory()), skia::CRASH_ON_FAILURE);
+#else
+  canvas_ = skia::CreatePlatformCanvasWithSharedSection(
+      viewport_pixel_size_.width(), viewport_pixel_size_.height(), false,
+      region.GetPlatformHandle(), skia::CRASH_ON_FAILURE);
+#endif
+
+  // Transfer region ownership to the browser process.
+  layered_window_updater_->OnAllocatedSharedMemory(viewport_pixel_size_,
+                                                   std::move(region));
+}
+
+SkCanvas* SoftwareOutputDeviceProxy::BeginPaint(const gfx::Rect& damage_rect) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(!in_paint_);
+
+  damage_rect_ = damage_rect;
+  in_paint_ = true;
+
+  return canvas_.get();
+}
+
+void SoftwareOutputDeviceProxy::EndPaint() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(in_paint_);
+  DCHECK(!waiting_on_draw_ack_);
+
+  in_paint_ = false;
+
+  gfx::Rect intersected_damage_rect = damage_rect_;
+  intersected_damage_rect.Intersect(gfx::Rect(viewport_pixel_size_));
+  if (intersected_damage_rect.IsEmpty())
+    return;
+
+  if (!canvas_)
+    return;
+
+  layered_window_updater_->Draw(
+      damage_rect_, base::BindOnce(&SoftwareOutputDeviceProxy::DrawAck,
+                                   base::Unretained(this)));
+  waiting_on_draw_ack_ = true;
+
+  TRACE_EVENT_ASYNC_BEGIN0("viz", "SoftwareOutputDeviceProxy::Draw", this);
+}
+
+void SoftwareOutputDeviceProxy::DrawAck() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(waiting_on_draw_ack_);
+  DCHECK(!swap_ack_callback_.is_null());
+
+  TRACE_EVENT_ASYNC_END0("viz", "SoftwareOutputDeviceProxy::Draw", this);
+
+  waiting_on_draw_ack_ = false;
+  std::move(swap_ack_callback_).Run();
+}
+
+}  // namespace viz
diff --git a/src/libcef/browser/osr/software_output_device_proxy.h b/src/libcef/browser/osr/software_output_device_proxy.h
new file mode 100644
index 0000000..38e9ce3
--- /dev/null
+++ b/src/libcef/browser/osr/software_output_device_proxy.h
@@ -0,0 +1,52 @@
+#ifndef CEF_LIBCEF_BROWSER_OSR_SOFTWARE_OUTPUT_DEVICE_PROXY_H_
+#define CEF_LIBCEF_BROWSER_OSR_SOFTWARE_OUTPUT_DEVICE_PROXY_H_
+
+#include "base/memory/shared_memory_mapping.h"
+#include "base/threading/thread_checker.h"
+#include "components/viz/service/display/software_output_device.h"
+#include "components/viz/service/viz_service_export.h"
+#include "services/viz/privileged/mojom/compositing/display_private.mojom.h"
+#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h"
+
+namespace viz {
+
+// SoftwareOutputDevice implementation that draws indirectly. An
+// implementation of mojom::LayeredWindowUpdater in the browser process
+// handles the actual drawing. Pixel backing is in SharedMemory so no copying
+// between processes is required.
+class VIZ_SERVICE_EXPORT SoftwareOutputDeviceProxy
+    : public SoftwareOutputDevice {
+ public:
+  explicit SoftwareOutputDeviceProxy(
+      mojom::LayeredWindowUpdaterPtr layered_window_updater);
+  ~SoftwareOutputDeviceProxy() override;
+
+  // SoftwareOutputDevice implementation.
+  void OnSwapBuffers(SwapBuffersCallback swap_ack_callback) override;
+
+  // SoftwareOutputDeviceBase implementation.
+  void Resize(const gfx::Size& viewport_pixel_size,
+              float scale_factor) override;
+  SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override;
+  void EndPaint() override;
+
+ private:
+  // Runs |swap_ack_callback_| after draw has happened.
+  void DrawAck();
+
+  mojom::LayeredWindowUpdaterPtr layered_window_updater_;
+
+  std::unique_ptr<SkCanvas> canvas_;
+  bool waiting_on_draw_ack_ = false;
+  bool in_paint_ = false;
+  base::OnceClosure swap_ack_callback_;
+  base::WritableSharedMemoryMapping shm_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceProxy);
+};
+
+}  // namespace viz
+
+#endif  // CEF_LIBCEF_BROWSER_OSR_SOFTWARE_OUTPUT_DEVICE_PROXY_H_
\ No newline at end of file
diff --git a/src/libcef/browser/osr/synthetic_gesture_target_osr.cc b/src/libcef/browser/osr/synthetic_gesture_target_osr.cc
new file mode 100644
index 0000000..977962f
--- /dev/null
+++ b/src/libcef/browser/osr/synthetic_gesture_target_osr.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/osr/synthetic_gesture_target_osr.h"
+
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/public/common/screen_info.h"
+#include "ui/events/gesture_detection/gesture_configuration.h"
+
+CefSyntheticGestureTargetOSR::CefSyntheticGestureTargetOSR(
+    content::RenderWidgetHostImpl* host)
+    : SyntheticGestureTargetBase(host) {}
+
+void CefSyntheticGestureTargetOSR::DispatchWebTouchEventToPlatform(
+    const blink::WebTouchEvent& web_touch,
+    const ui::LatencyInfo& latency_info) {
+  // We assume that platforms supporting touch have their own implementation of
+  // SyntheticGestureTarget to route the events through their respective input
+  // stack.
+  LOG(ERROR) << "Touch events not supported for this browser.";
+}
+
+void CefSyntheticGestureTargetOSR::DispatchWebMouseWheelEventToPlatform(
+    const blink::WebMouseWheelEvent& web_wheel,
+    const ui::LatencyInfo& latency_info) {
+  render_widget_host()->ForwardWheelEventWithLatencyInfo(web_wheel,
+                                                         latency_info);
+}
+
+void CefSyntheticGestureTargetOSR::DispatchWebGestureEventToPlatform(
+    const blink::WebGestureEvent& web_gesture,
+    const ui::LatencyInfo& latency_info) {
+  render_widget_host()->ForwardGestureEventWithLatencyInfo(web_gesture,
+                                                           latency_info);
+}
+
+void CefSyntheticGestureTargetOSR::DispatchWebMouseEventToPlatform(
+    const blink::WebMouseEvent& web_mouse,
+    const ui::LatencyInfo& latency_info) {
+  render_widget_host()->ForwardMouseEventWithLatencyInfo(web_mouse,
+                                                         latency_info);
+}
+
+content::SyntheticGestureParams::GestureSourceType
+CefSyntheticGestureTargetOSR::GetDefaultSyntheticGestureSourceType() const {
+  return content::SyntheticGestureParams::MOUSE_INPUT;
+}
+
+float CefSyntheticGestureTargetOSR::GetTouchSlopInDips() const {
+  return ui::GestureConfiguration::GetInstance()
+      ->max_touch_move_in_pixels_for_click();
+}
+
+float CefSyntheticGestureTargetOSR::GetSpanSlopInDips() const {
+  return ui::GestureConfiguration::GetInstance()->span_slop();
+}
+
+float CefSyntheticGestureTargetOSR::GetMinScalingSpanInDips() const {
+  return ui::GestureConfiguration::GetInstance()->min_scaling_span_in_pixels();
+}
diff --git a/src/libcef/browser/osr/synthetic_gesture_target_osr.h b/src/libcef/browser/osr/synthetic_gesture_target_osr.h
new file mode 100644
index 0000000..f6471a5
--- /dev/null
+++ b/src/libcef/browser/osr/synthetic_gesture_target_osr.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_SYNTHETIC_GESTURE_TARGET_OSR_H_
+#define CEF_LIBCEF_BROWSER_OSR_SYNTHETIC_GESTURE_TARGET_OSR_H_
+
+#include "base/macros.h"
+#include "content/browser/renderer_host/input/synthetic_gesture_target_base.h"
+
+// SyntheticGestureTarget implementation for OSR.
+class CefSyntheticGestureTargetOSR
+    : public content::SyntheticGestureTargetBase {
+ public:
+  explicit CefSyntheticGestureTargetOSR(content::RenderWidgetHostImpl* host);
+
+  // SyntheticGestureTargetBase:
+  void DispatchWebTouchEventToPlatform(
+      const blink::WebTouchEvent& web_touch,
+      const ui::LatencyInfo& latency_info) override;
+  void DispatchWebMouseWheelEventToPlatform(
+      const blink::WebMouseWheelEvent& web_wheel,
+      const ui::LatencyInfo& latency_info) override;
+  void DispatchWebGestureEventToPlatform(
+      const blink::WebGestureEvent& web_gesture,
+      const ui::LatencyInfo& latency_info) override;
+  void DispatchWebMouseEventToPlatform(
+      const blink::WebMouseEvent& web_mouse,
+      const ui::LatencyInfo& latency_info) override;
+
+  // SyntheticGestureTarget:
+  content::SyntheticGestureParams::GestureSourceType
+  GetDefaultSyntheticGestureSourceType() const override;
+  float GetTouchSlopInDips() const override;
+  float GetSpanSlopInDips() const override;
+  float GetMinScalingSpanInDips() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefSyntheticGestureTargetOSR);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_OSR_SYNTHETIC_GESTURE_TARGET_OSR_H_
diff --git a/src/libcef/browser/osr/video_consumer_osr.cc b/src/libcef/browser/osr/video_consumer_osr.cc
new file mode 100644
index 0000000..66e656d
--- /dev/null
+++ b/src/libcef/browser/osr/video_consumer_osr.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2015 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/video_consumer_osr.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+
+#include "media/base/video_frame_metadata.h"
+#include "media/capture/mojom/video_capture_types.mojom.h"
+#include "ui/gfx/skbitmap_operations.h"
+
+namespace {
+
+// Helper to always call Done() at the end of OnFrameCaptured().
+class ScopedVideoFrameDone {
+ public:
+  explicit ScopedVideoFrameDone(
+      mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
+          callbacks)
+      : callbacks_(std::move(callbacks)) {}
+  ~ScopedVideoFrameDone() { callbacks_->Done(); }
+
+ private:
+  mojo::Remote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> callbacks_;
+};
+
+}  // namespace
+
+CefVideoConsumerOSR::CefVideoConsumerOSR(CefRenderWidgetHostViewOSR* view)
+    : view_(view), video_capturer_(view->CreateVideoCapturer()) {
+  video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB,
+                             gfx::ColorSpace::CreateREC709());
+
+  // Always use the highest resolution within constraints that doesn't exceed
+  // the source size.
+  video_capturer_->SetAutoThrottlingEnabled(false);
+  video_capturer_->SetMinSizeChangePeriod(base::TimeDelta());
+
+  SizeChanged(view_->SizeInPixels());
+  SetActive(true);
+}
+
+CefVideoConsumerOSR::~CefVideoConsumerOSR() = default;
+
+void CefVideoConsumerOSR::SetActive(bool active) {
+  if (active) {
+    video_capturer_->Start(this);
+  } else {
+    video_capturer_->Stop();
+  }
+}
+
+void CefVideoConsumerOSR::SetFrameRate(base::TimeDelta frame_rate) {
+  video_capturer_->SetMinCapturePeriod(frame_rate);
+}
+
+void CefVideoConsumerOSR::SizeChanged(const gfx::Size& size_in_pixels) {
+  if (size_in_pixels_ == size_in_pixels)
+    return;
+  size_in_pixels_ = size_in_pixels;
+
+  // Capture resolution will be held constant.
+  video_capturer_->SetResolutionConstraints(size_in_pixels, size_in_pixels,
+                                            true /* use_fixed_aspect_ratio */);
+}
+
+void CefVideoConsumerOSR::RequestRefreshFrame(
+    const base::Optional<gfx::Rect>& bounds_in_pixels) {
+  bounds_in_pixels_ = bounds_in_pixels;
+  video_capturer_->RequestRefreshFrame();
+}
+
+// Frame size values are as follows:
+//   info->coded_size = Width and height of the video frame. Not all pixels in
+//   this region are valid.
+//   info->visible_rect = Region of coded_size that contains image data, also
+//   known as the clean aperture.
+//   content_rect = Region of the frame that contains the captured content, with
+//   the rest of the frame having been letterboxed to adhere to resolution
+//   constraints.
+void CefVideoConsumerOSR::OnFrameCaptured(
+    base::ReadOnlySharedMemoryRegion data,
+    ::media::mojom::VideoFrameInfoPtr info,
+    const gfx::Rect& content_rect,
+    mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
+        callbacks) {
+  ScopedVideoFrameDone scoped_done(std::move(callbacks));
+
+  if (!data.IsValid())
+    return;
+
+  base::ReadOnlySharedMemoryMapping mapping = data.Map();
+  if (!mapping.IsValid()) {
+    DLOG(ERROR) << "Shared memory mapping failed.";
+    return;
+  }
+  if (mapping.size() <
+      media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) {
+    DLOG(ERROR) << "Shared memory size was less than expected.";
+    return;
+  }
+
+  // The SkBitmap's pixels will be marked as immutable, but the installPixels()
+  // API requires a non-const pointer. So, cast away the const.
+  void* const pixels = const_cast<void*>(mapping.memory());
+
+  media::VideoFrameMetadata metadata;
+  metadata.MergeInternalValuesFrom(info->metadata);
+  gfx::Rect damage_rect;
+
+  if (bounds_in_pixels_) {
+    // Use the bounds passed to RequestRefreshFrame().
+    damage_rect = gfx::Rect(info->coded_size);
+    damage_rect.Intersect(*bounds_in_pixels_);
+    bounds_in_pixels_ = base::nullopt;
+  } else {
+    // Retrieve the rectangular region of the frame that has changed since the
+    // frame with the directly preceding CAPTURE_COUNTER. If that frame was not
+    // received, typically because it was dropped during transport from the
+    // producer, clients must assume that the entire frame has changed.
+    // This rectangle is relative to the full frame data, i.e. [0, 0,
+    // coded_size.width(), coded_size.height()]. It does not have to be
+    // fully contained within visible_rect.
+    if (!metadata.GetRect(media::VideoFrameMetadata::CAPTURE_UPDATE_RECT,
+                          &damage_rect) ||
+        damage_rect.IsEmpty()) {
+      damage_rect = gfx::Rect(info->coded_size);
+    }
+  }
+
+  view_->OnPaint(damage_rect, info->coded_size, pixels);
+}
+
+void CefVideoConsumerOSR::OnStopped() {}
diff --git a/src/libcef/browser/osr/video_consumer_osr.h b/src/libcef/browser/osr/video_consumer_osr.h
new file mode 100644
index 0000000..a4e2741
--- /dev/null
+++ b/src/libcef/browser/osr/video_consumer_osr.h
@@ -0,0 +1,41 @@
+#ifndef LIBCEF_BROWSER_OSR_VIDEO_CONSUMER_OSR_H_
+#define LIBCEF_BROWSER_OSR_VIDEO_CONSUMER_OSR_H_
+
+#include "base/callback.h"
+#include "base/optional.h"
+#include "components/viz/host/client_frame_sink_video_capturer.h"
+#include "media/capture/mojom/video_capture_types.mojom.h"
+
+class CefRenderWidgetHostViewOSR;
+
+class CefVideoConsumerOSR : public viz::mojom::FrameSinkVideoConsumer {
+ public:
+  explicit CefVideoConsumerOSR(CefRenderWidgetHostViewOSR* view);
+  ~CefVideoConsumerOSR() override;
+
+  void SetActive(bool active);
+  void SetFrameRate(base::TimeDelta frame_rate);
+  void SizeChanged(const gfx::Size& size_in_pixels);
+  void RequestRefreshFrame(const base::Optional<gfx::Rect>& bounds_in_pixels);
+
+ private:
+  // viz::mojom::FrameSinkVideoConsumer implementation.
+  void OnFrameCaptured(
+      base::ReadOnlySharedMemoryRegion data,
+      ::media::mojom::VideoFrameInfoPtr info,
+      const gfx::Rect& content_rect,
+      mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
+          callbacks) override;
+  void OnStopped() override;
+  void OnLog(const std::string& message) override {}
+
+  CefRenderWidgetHostViewOSR* const view_;
+  std::unique_ptr<viz::ClientFrameSinkVideoCapturer> video_capturer_;
+
+  gfx::Size size_in_pixels_;
+  base::Optional<gfx::Rect> bounds_in_pixels_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefVideoConsumerOSR);
+};
+
+#endif  // LIBCEF_BROWSER_OSR_VIDEO_CONSUMER_OSR_H_
\ No newline at end of file
diff --git a/src/libcef/browser/osr/web_contents_view_osr.cc b/src/libcef/browser/osr/web_contents_view_osr.cc
new file mode 100644
index 0000000..61a4058
--- /dev/null
+++ b/src/libcef/browser/osr/web_contents_view_osr.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/osr/web_contents_view_osr.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/osr/render_widget_host_view_osr.h"
+#include "libcef/common/drag_data_impl.h"
+
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/render_widget_host.h"
+
+CefWebContentsViewOSR::CefWebContentsViewOSR(SkColor background_color,
+                                             bool use_shared_texture,
+                                             bool use_external_begin_frame)
+    : background_color_(background_color),
+      use_shared_texture_(use_shared_texture),
+      use_external_begin_frame_(use_external_begin_frame),
+      web_contents_(nullptr) {}
+
+CefWebContentsViewOSR::~CefWebContentsViewOSR() {}
+
+void CefWebContentsViewOSR::WebContentsCreated(
+    content::WebContents* web_contents) {
+  DCHECK(!web_contents_);
+  web_contents_ = web_contents;
+
+  auto host = web_contents_->GetRenderViewHost();
+  CefRenderWidgetHostViewOSR* view =
+      static_cast<CefRenderWidgetHostViewOSR*>(host->GetWidget()->GetView());
+  if (view)
+    view->InstallTransparency();
+}
+
+gfx::NativeView CefWebContentsViewOSR::GetNativeView() const {
+  return gfx::NativeView();
+}
+
+gfx::NativeView CefWebContentsViewOSR::GetContentNativeView() const {
+  return gfx::NativeView();
+}
+
+gfx::NativeWindow CefWebContentsViewOSR::GetTopLevelNativeWindow() const {
+  return gfx::NativeWindow();
+}
+
+void CefWebContentsViewOSR::GetContainerBounds(gfx::Rect* out) const {
+  *out = GetViewBounds();
+}
+
+void CefWebContentsViewOSR::SizeContents(const gfx::Size& size) {}
+
+void CefWebContentsViewOSR::Focus() {}
+
+void CefWebContentsViewOSR::SetInitialFocus() {}
+
+void CefWebContentsViewOSR::StoreFocus() {}
+
+void CefWebContentsViewOSR::RestoreFocus() {}
+
+void CefWebContentsViewOSR::FocusThroughTabTraversal(bool reverse) {}
+
+void CefWebContentsViewOSR::GotFocus(
+    content::RenderWidgetHostImpl* render_widget_host) {
+  if (web_contents_) {
+    content::WebContentsImpl* web_contents_impl =
+        static_cast<content::WebContentsImpl*>(web_contents_);
+    if (web_contents_impl)
+      web_contents_impl->NotifyWebContentsFocused(render_widget_host);
+  }
+}
+
+void CefWebContentsViewOSR::LostFocus(
+    content::RenderWidgetHostImpl* render_widget_host) {
+  if (web_contents_) {
+    content::WebContentsImpl* web_contents_impl =
+        static_cast<content::WebContentsImpl*>(web_contents_);
+    if (web_contents_impl)
+      web_contents_impl->NotifyWebContentsLostFocus(render_widget_host);
+  }
+}
+
+void CefWebContentsViewOSR::TakeFocus(bool reverse) {
+  if (web_contents_->GetDelegate())
+    web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse);
+}
+
+content::DropData* CefWebContentsViewOSR::GetDropData() const {
+  return nullptr;
+}
+
+gfx::Rect CefWebContentsViewOSR::GetViewBounds() const {
+  CefRenderWidgetHostViewOSR* view = GetView();
+  return view ? view->GetViewBounds() : gfx::Rect();
+}
+
+void CefWebContentsViewOSR::CreateView(gfx::NativeView context) {}
+
+content::RenderWidgetHostViewBase* CefWebContentsViewOSR::CreateViewForWidget(
+    content::RenderWidgetHost* render_widget_host) {
+  if (render_widget_host->GetView()) {
+    return static_cast<content::RenderWidgetHostViewBase*>(
+        render_widget_host->GetView());
+  }
+
+  return new CefRenderWidgetHostViewOSR(background_color_, use_shared_texture_,
+                                        use_external_begin_frame_,
+                                        render_widget_host, nullptr);
+}
+
+// Called for popup and fullscreen widgets.
+content::RenderWidgetHostViewBase*
+CefWebContentsViewOSR::CreateViewForChildWidget(
+    content::RenderWidgetHost* render_widget_host) {
+  CefRenderWidgetHostViewOSR* view = GetView();
+  CHECK(view);
+
+  return new CefRenderWidgetHostViewOSR(background_color_, use_shared_texture_,
+                                        use_external_begin_frame_,
+                                        render_widget_host, view);
+}
+
+void CefWebContentsViewOSR::SetPageTitle(const base::string16& title) {}
+
+void CefWebContentsViewOSR::RenderViewReady() {}
+
+void CefWebContentsViewOSR::RenderViewHostChanged(
+    content::RenderViewHost* old_host,
+    content::RenderViewHost* new_host) {}
+
+void CefWebContentsViewOSR::SetOverscrollControllerEnabled(bool enabled) {}
+
+#if defined(OS_MACOSX)
+bool CefWebContentsViewOSR::CloseTabAfterEventTrackingIfNeeded() {
+  return false;
+}
+#endif  // defined(OS_MACOSX)
+
+void CefWebContentsViewOSR::StartDragging(
+    const content::DropData& drop_data,
+    blink::WebDragOperationsMask allowed_ops,
+    const gfx::ImageSkia& image,
+    const gfx::Vector2d& image_offset,
+    const content::DragEventSourceInfo& event_info,
+    content::RenderWidgetHostImpl* source_rwh) {
+  CefRefPtr<CefBrowserHostImpl> browser = GetBrowser();
+  if (browser.get()) {
+    browser->StartDragging(drop_data, allowed_ops, image, image_offset,
+                           event_info, source_rwh);
+  } else if (web_contents_) {
+    web_contents_->SystemDragEnded(source_rwh);
+  }
+}
+
+void CefWebContentsViewOSR::UpdateDragCursor(
+    blink::WebDragOperation operation) {
+  CefRefPtr<CefBrowserHostImpl> browser = GetBrowser();
+  if (browser.get())
+    browser->UpdateDragCursor(operation);
+}
+
+CefRenderWidgetHostViewOSR* CefWebContentsViewOSR::GetView() const {
+  if (web_contents_) {
+    return static_cast<CefRenderWidgetHostViewOSR*>(
+        web_contents_->GetRenderViewHost()->GetWidget()->GetView());
+  }
+  return nullptr;
+}
+
+CefBrowserHostImpl* CefWebContentsViewOSR::GetBrowser() const {
+  CefRenderWidgetHostViewOSR* view = GetView();
+  if (view)
+    return view->browser_impl().get();
+  return nullptr;
+}
diff --git a/src/libcef/browser/osr/web_contents_view_osr.h b/src/libcef/browser/osr/web_contents_view_osr.h
new file mode 100644
index 0000000..088edce
--- /dev/null
+++ b/src/libcef/browser/osr/web_contents_view_osr.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_OSR_WEB_CONTENTS_VIEW_OSR_H_
+#define CEF_LIBCEF_BROWSER_OSR_WEB_CONTENTS_VIEW_OSR_H_
+
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
+#include "content/browser/web_contents/web_contents_view.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace content {
+class BrowserPluginGuest;
+class WebContents;
+class WebContentsViewDelegate;
+}  // namespace content
+
+class CefBrowserHostImpl;
+class CefRenderWidgetHostViewOSR;
+
+// An implementation of WebContentsView for off-screen rendering.
+class CefWebContentsViewOSR : public content::WebContentsView,
+                              public content::RenderViewHostDelegateView {
+ public:
+  explicit CefWebContentsViewOSR(SkColor background_color,
+                                 bool use_shared_texture,
+                                 bool use_external_begin_frame);
+  ~CefWebContentsViewOSR() override;
+
+  void WebContentsCreated(content::WebContents* web_contents);
+  content::WebContents* web_contents() const { return web_contents_; }
+
+  // WebContentsView methods.
+  gfx::NativeView GetNativeView() const override;
+  gfx::NativeView GetContentNativeView() const override;
+  gfx::NativeWindow GetTopLevelNativeWindow() const override;
+  void GetContainerBounds(gfx::Rect* out) const override;
+  void SizeContents(const gfx::Size& size) override;
+  void Focus() override;
+  void SetInitialFocus() override;
+  void StoreFocus() override;
+  void RestoreFocus() override;
+  void FocusThroughTabTraversal(bool reverse) override;
+  content::DropData* GetDropData() const override;
+  gfx::Rect GetViewBounds() const override;
+  void CreateView(gfx::NativeView context) override;
+  content::RenderWidgetHostViewBase* CreateViewForWidget(
+      content::RenderWidgetHost* render_widget_host) override;
+  content::RenderWidgetHostViewBase* CreateViewForChildWidget(
+      content::RenderWidgetHost* render_widget_host) override;
+  void SetPageTitle(const base::string16& title) override;
+  void RenderViewReady() override;
+  void RenderViewHostChanged(content::RenderViewHost* old_host,
+                             content::RenderViewHost* new_host) override;
+  void SetOverscrollControllerEnabled(bool enabled) override;
+
+#if defined(OS_MACOSX)
+  bool CloseTabAfterEventTrackingIfNeeded() override;
+#endif
+
+  // RenderViewHostDelegateView methods.
+  void StartDragging(const content::DropData& drop_data,
+                     blink::WebDragOperationsMask allowed_ops,
+                     const gfx::ImageSkia& image,
+                     const gfx::Vector2d& image_offset,
+                     const content::DragEventSourceInfo& event_info,
+                     content::RenderWidgetHostImpl* source_rwh) override;
+  void UpdateDragCursor(blink::WebDragOperation operation) override;
+  virtual void GotFocus(
+      content::RenderWidgetHostImpl* render_widget_host) override;
+  virtual void LostFocus(
+      content::RenderWidgetHostImpl* render_widget_host) override;
+  virtual void TakeFocus(bool reverse) override;
+
+ private:
+  CefRenderWidgetHostViewOSR* GetView() const;
+  CefBrowserHostImpl* GetBrowser() const;
+
+  const SkColor background_color_;
+  const bool use_shared_texture_;
+  const bool use_external_begin_frame_;
+
+  content::WebContents* web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefWebContentsViewOSR);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_OSR_WEB_CONTENTS_VIEW_OSR_H_
diff --git a/src/libcef/browser/path_util_impl.cc b/src/libcef/browser/path_util_impl.cc
new file mode 100644
index 0000000..4d30d0d
--- /dev/null
+++ b/src/libcef/browser/path_util_impl.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_path_util.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+
+bool CefGetPath(PathKey key, CefString& path) {
+  int pref_key = base::PATH_START;
+  switch (key) {
+    case PK_DIR_CURRENT:
+      pref_key = base::DIR_CURRENT;
+      break;
+    case PK_DIR_EXE:
+      pref_key = base::DIR_EXE;
+      break;
+    case PK_DIR_MODULE:
+      pref_key = base::DIR_MODULE;
+      break;
+    case PK_DIR_TEMP:
+      pref_key = base::DIR_TEMP;
+      break;
+    case PK_FILE_EXE:
+      pref_key = base::FILE_EXE;
+      break;
+    case PK_FILE_MODULE:
+      pref_key = base::FILE_MODULE;
+      break;
+#if defined(OS_WIN)
+    case PK_LOCAL_APP_DATA:
+      pref_key = base::DIR_LOCAL_APP_DATA;
+      break;
+#endif
+    case PK_USER_DATA:
+      pref_key = chrome::DIR_USER_DATA;
+      break;
+    case PK_DIR_RESOURCES:
+      pref_key = chrome::DIR_RESOURCES;
+      break;
+    default:
+      NOTREACHED() << "invalid argument";
+      return false;
+  }
+
+  base::FilePath file_path;
+  if (base::PathService::Get(pref_key, &file_path)) {
+    path = file_path.value();
+    return true;
+  }
+
+  return false;
+}
diff --git a/src/libcef/browser/plugins/plugin_service_filter.cc b/src/libcef/browser/plugins/plugin_service_filter.cc
new file mode 100644
index 0000000..e1dbdcd
--- /dev/null
+++ b/src/libcef/browser/plugins/plugin_service_filter.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/plugins/plugin_service_filter.h"
+
+#include "include/cef_request_context_handler.h"
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/web_plugin_impl.h"
+#include "libcef/common/content_client.h"
+
+#include "extensions/common/constants.h"
+
+CefPluginServiceFilter::CefPluginServiceFilter() {}
+
+bool CefPluginServiceFilter::IsPluginAvailable(
+    int render_process_id,
+    int render_frame_id,
+    const GURL& url,
+    bool is_main_frame,
+    const url::Origin& main_frame_origin,
+    content::WebPluginInfo* plugin) {
+  CEF_REQUIRE_UIT();
+  DCHECK_GT(render_process_id, 0);
+
+  chrome::mojom::PluginStatus status = chrome::mojom::PluginStatus::kAllowed;
+
+  // Perform origin check here because we're passing an empty origin value to
+  // IsPluginAvailable() below.
+  const GURL& policy_url = main_frame_origin.GetURL();
+  if (!policy_url.is_empty() &&
+      policy_url.scheme() == extensions::kExtensionScheme) {
+    // Always allow extension origins to load plugins.
+    // TODO(extensions): Revisit this decision once CEF supports more than just
+    // the PDF extension.
+    return true;
+  }
+
+  // Blink requires this method to return a consistent value during renderer
+  // process initialization and page load, so we always call IsPluginAvailable()
+  // with an empty origin. If we return false then the plugin will not be listed
+  // in navigator.plugins and navigating to the plugin mime type will trigger
+  // the download code path. If we return true then individual plugin instance
+  // loads will be evaluated in CefContentRendererClient::OverrideCreatePlugin,
+  // which will result in a call to CefPluginInfoMessageFilter::PluginsLoaded to
+  // retrieve the actual load decision with a non-empty origin. That will
+  // determine whether the plugin load is allowed or the plugin placeholder is
+  // displayed.
+  return IsPluginAvailable(render_process_id, render_frame_id, url,
+                           is_main_frame, url::Origin(), plugin, &status);
+}
+
+bool CefPluginServiceFilter::CanLoadPlugin(int render_process_id,
+                                           const base::FilePath& path) {
+  return true;
+}
+
+bool CefPluginServiceFilter::IsPluginAvailable(
+    int render_process_id,
+    int render_frame_id,
+    const GURL& url,
+    bool is_main_frame,
+    const url::Origin& main_frame_origin,
+    content::WebPluginInfo* plugin,
+    chrome::mojom::PluginStatus* status) {
+  CEF_REQUIRE_UIT();
+  DCHECK_GT(render_process_id, 0);
+
+  if (*status == chrome::mojom::PluginStatus::kNotFound) {
+    // The plugin does not exist so no need to query the handler.
+    return false;
+  }
+
+  if (plugin->path == CefString(CefContentClient::kPDFPluginPath)) {
+    // Always allow the internal PDF plugin to load.
+    *status = chrome::mojom::PluginStatus::kAllowed;
+    return true;
+  }
+
+  const GURL& policy_url = main_frame_origin.GetURL();
+  if (!policy_url.is_empty() &&
+      policy_url.scheme() == extensions::kExtensionScheme) {
+    // Always allow extension origins to load plugins.
+    // TODO(extensions): Revisit this decision once CEF supports more than just
+    // the PDF extension.
+    *status = chrome::mojom::PluginStatus::kAllowed;
+    return true;
+  }
+
+  auto browser_context = CefBrowserContext::GetForIDs(
+      render_process_id, render_frame_id, -1, false);
+  CefRefPtr<CefRequestContextHandler> handler;
+  if (browser_context) {
+    handler = browser_context->GetHandler(render_process_id, render_frame_id,
+                                          -1, false);
+  }
+
+  if (!handler) {
+    // No handler so go with the default plugin load decision.
+    return *status != chrome::mojom::PluginStatus::kDisabled;
+  }
+
+  // Check for a cached plugin load decision.
+  if (browser_context->HasPluginLoadDecision(render_process_id, plugin->path,
+                                             is_main_frame, main_frame_origin,
+                                             status)) {
+    return *status != chrome::mojom::PluginStatus::kDisabled;
+  }
+
+  CefRefPtr<CefWebPluginInfoImpl> pluginInfo(new CefWebPluginInfoImpl(*plugin));
+
+  cef_plugin_policy_t plugin_policy = PLUGIN_POLICY_ALLOW;
+  switch (*status) {
+    case chrome::mojom::PluginStatus::kAllowed:
+      plugin_policy = PLUGIN_POLICY_ALLOW;
+      break;
+    case chrome::mojom::PluginStatus::kBlocked:
+    case chrome::mojom::PluginStatus::kBlockedByPolicy:
+    case chrome::mojom::PluginStatus::kOutdatedBlocked:
+    case chrome::mojom::PluginStatus::kOutdatedDisallowed:
+    case chrome::mojom::PluginStatus::kUnauthorized:
+      plugin_policy = PLUGIN_POLICY_BLOCK;
+      break;
+    case chrome::mojom::PluginStatus::kDisabled:
+      plugin_policy = PLUGIN_POLICY_DISABLE;
+      break;
+    case chrome::mojom::PluginStatus::kPlayImportantContent:
+      plugin_policy = PLUGIN_POLICY_DETECT_IMPORTANT;
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+
+  if (handler->OnBeforePluginLoad(plugin->mime_types[0].mime_type,
+                                  url.possibly_invalid_spec(), is_main_frame,
+                                  policy_url.possibly_invalid_spec(),
+                                  pluginInfo.get(), &plugin_policy)) {
+    switch (plugin_policy) {
+      case PLUGIN_POLICY_ALLOW:
+        *status = chrome::mojom::PluginStatus::kAllowed;
+        break;
+      case PLUGIN_POLICY_DETECT_IMPORTANT:
+        *status = chrome::mojom::PluginStatus::kPlayImportantContent;
+        break;
+      case PLUGIN_POLICY_BLOCK:
+        *status = chrome::mojom::PluginStatus::kBlocked;
+        break;
+      case PLUGIN_POLICY_DISABLE:
+        *status = chrome::mojom::PluginStatus::kDisabled;
+        break;
+    }
+  }
+
+  // Cache the plugin load decision.
+  browser_context->AddPluginLoadDecision(render_process_id, plugin->path,
+                                         is_main_frame, main_frame_origin,
+                                         *status);
+
+  return *status != chrome::mojom::PluginStatus::kDisabled;
+}
diff --git a/src/libcef/browser/plugins/plugin_service_filter.h b/src/libcef/browser/plugins/plugin_service_filter.h
new file mode 100644
index 0000000..3322bac
--- /dev/null
+++ b/src/libcef/browser/plugins/plugin_service_filter.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_SERVICE_FILTER_H_
+#define CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_SERVICE_FILTER_H_
+
+#include "chrome/common/plugin.mojom.h"
+#include "content/public/browser/plugin_service_filter.h"
+
+#include "include/internal/cef_types.h"
+
+#include "base/macros.h"
+
+namespace content {
+class ResourceContext;
+}
+
+class CefPluginServiceFilter : public content::PluginServiceFilter {
+ public:
+  CefPluginServiceFilter();
+
+  // Called whenever the plugin list is queried. For example, when choosing the
+  // plugin to handle a mime type or when determining the plugins that will be
+  // exposed to JavaScript via 'navigator.plugins'.
+  bool IsPluginAvailable(int render_process_id,
+                         int render_frame_id,
+                         const GURL& url,
+                         bool is_main_frame,
+                         const url::Origin& main_frame_origin,
+                         content::WebPluginInfo* plugin) override;
+
+  bool CanLoadPlugin(int render_process_id,
+                     const base::FilePath& path) override;
+
+  // Called from the above IsPluginAvailable method and from
+  // PluginInfoHostImpl::Context::FindEnabledPlugin.
+  // Returns false if the plugin is not found or disabled. May call
+  // CefRequestContextHandler::OnBeforePluginLoad if possible/necessary.
+  // See related discussion in issue #2015.
+  bool IsPluginAvailable(int render_process_id,
+                         int render_frame_id,
+                         const GURL& url,
+                         bool is_main_frame,
+                         const url::Origin& main_frame_origin,
+                         content::WebPluginInfo* plugin,
+                         chrome::mojom::PluginStatus* status);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefPluginServiceFilter);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_SERVICE_FILTER_H_
diff --git a/src/libcef/browser/prefs/browser_prefs.cc b/src/libcef/browser/prefs/browser_prefs.cc
new file mode 100644
index 0000000..5abda53
--- /dev/null
+++ b/src/libcef/browser/prefs/browser_prefs.cc
@@ -0,0 +1,298 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/prefs/browser_prefs.h"
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/media_capture_devices_dispatcher.h"
+#include "libcef/browser/prefs/pref_store.h"
+#include "libcef/browser/prefs/renderer_prefs.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "base/values.h"
+#include "chrome/browser/accessibility/accessibility_ui.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/browser/net/prediction_options.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/plugins/plugin_info_host_impl.h"
+#include "chrome/browser/prefs/chrome_command_line_pref_store.h"
+#include "chrome/browser/printing/print_preview_sticky_settings.h"
+#include "chrome/browser/renderer_host/pepper/device_id_fetcher.h"
+#include "chrome/browser/ssl/ssl_config_service_manager.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/common/buildflags.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/net/safe_search_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/certificate_transparency/pref_names.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/flags_ui/pref_service_flags_storage.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/language/core/browser/language_prefs.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/json_pref_store.h"
+#include "components/prefs/pref_filter.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "components/proxy_config/proxy_config_dictionary.h"
+#include "components/spellcheck/browser/pref_names.h"
+#include "components/sync_preferences/pref_service_syncable.h"
+#include "components/sync_preferences/pref_service_syncable_factory.h"
+#include "components/update_client/update_client.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/buildflags/buildflags.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_switches.h"
+
+#if defined(OS_WIN)
+#include "components/os_crypt/os_crypt.h"
+#endif
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/supervised_user_pref_store.h"
+#include "chrome/browser/supervised_user/supervised_user_settings_service.h"
+#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
+#endif
+
+namespace browser_prefs {
+
+namespace {
+
+std::string GetAcceptLanguageList(Profile* profile) {
+  const CefRequestContextSettings& context_settings =
+      static_cast<CefBrowserContext*>(profile)->GetSettings();
+  if (context_settings.accept_language_list.length > 0) {
+    return CefString(&context_settings.accept_language_list);
+  }
+  return std::string();
+}
+
+}  // namespace
+
+const char kUserPrefsFileName[] = "UserPrefs.json";
+const char kLocalPrefsFileName[] = "LocalPrefs.json";
+
+std::unique_ptr<PrefService> CreatePrefService(Profile* profile,
+                                               const base::FilePath& cache_path,
+                                               bool persist_user_preferences) {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+
+  // Use of PrefServiceSyncable is required by Chrome code such as
+  // HostContentSettingsMapFactory that calls PrefServiceSyncableFromProfile.
+  sync_preferences::PrefServiceSyncableFactory factory;
+
+  // Used to store command-line preferences, most of which will be evaluated in
+  // the CommandLinePrefStore constructor. Preferences set in this manner cannot
+  // be overridden by the user.
+  scoped_refptr<ChromeCommandLinePrefStore> command_line_pref_store(
+      new ChromeCommandLinePrefStore(command_line));
+  renderer_prefs::SetCommandLinePrefDefaults(command_line_pref_store.get());
+  factory.set_command_line_prefs(command_line_pref_store);
+
+  // True if preferences will be stored on disk.
+  const bool store_on_disk = !cache_path.empty() && persist_user_preferences;
+
+  scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner;
+  if (store_on_disk) {
+    // Get sequenced task runner for making sure that file operations are
+    // executed in expected order (what was previously assured by the FILE
+    // thread).
+    sequenced_task_runner = base::CreateSequencedTaskRunner(
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
+  }
+
+  // Used to store user preferences.
+  scoped_refptr<PersistentPrefStore> user_pref_store;
+  if (store_on_disk) {
+    const base::FilePath& pref_path = cache_path.AppendASCII(
+        profile ? kUserPrefsFileName : kLocalPrefsFileName);
+    scoped_refptr<JsonPrefStore> json_pref_store = new JsonPrefStore(
+        pref_path, std::unique_ptr<PrefFilter>(), sequenced_task_runner);
+    factory.set_user_prefs(json_pref_store.get());
+  } else {
+    scoped_refptr<CefPrefStore> cef_pref_store = new CefPrefStore();
+    cef_pref_store->SetInitializationCompleted();
+    factory.set_user_prefs(cef_pref_store.get());
+  }
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+  if (profile) {
+    // Used to store supervised user preferences.
+    SupervisedUserSettingsService* supervised_user_settings =
+        SupervisedUserSettingsServiceFactory::GetForKey(
+            profile->GetProfileKey());
+
+    if (store_on_disk) {
+      supervised_user_settings->Init(cache_path, sequenced_task_runner.get(),
+                                     true);
+    } else {
+      scoped_refptr<CefPrefStore> cef_pref_store = new CefPrefStore();
+      cef_pref_store->SetInitializationCompleted();
+      supervised_user_settings->Init(cef_pref_store);
+    }
+
+    scoped_refptr<PrefStore> supervised_user_prefs =
+        base::MakeRefCounted<SupervisedUserPrefStore>(supervised_user_settings);
+    DCHECK(supervised_user_prefs->IsInitializationComplete());
+    factory.set_supervised_user_prefs(supervised_user_prefs);
+  }
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
+
+  // Registry that will be populated with all known preferences. Preferences
+  // are registered with default values that may be changed via a *PrefStore.
+  scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
+      new user_prefs::PrefRegistrySyncable());
+
+  // Some preferences are specific to CEF and others are defined in Chromium.
+  // The preferred approach for registering preferences defined in Chromium is
+  // as follows:
+  //
+  // 1. If a non-static RegisterProfilePrefs() method exists in a *Factory
+  //    class then add a *Factory::GetInstance() call in
+  //    EnsureBrowserContextKeyedServiceFactoriesBuilt().
+  // 2. If a static RegisterPrefs() method exists then call that method in the
+  //    "Default preferences" section below.
+  // 3. If the default values are not appropriate but the set of registered
+  //    preferences is otherwise fine then change the defaults by calling
+  //    SetDefaultPrefValue after calling the existing registration method.
+  // 4. If the original registration method contains many unused preferences or
+  //    otherwise inappropiate logic (e.g. calls to objects that CEF doesn't
+  //    use) then register the preferences directly instead of calling the
+  //    existing registration method.
+
+  // Default preferences.
+  CefMediaCaptureDevicesDispatcher::RegisterPrefs(registry.get());
+  certificate_transparency::prefs::RegisterPrefs(registry.get());
+  flags_ui::PrefServiceFlagsStorage::RegisterPrefs(registry.get());
+  media_router::RegisterLocalStatePrefs(registry.get());
+  PluginInfoHostImpl::RegisterUserPrefs(registry.get());
+  PrefProxyConfigTrackerImpl::RegisterPrefs(registry.get());
+  ProfileNetworkContextService::RegisterLocalStatePrefs(registry.get());
+  SSLConfigServiceManager::RegisterPrefs(registry.get());
+  update_client::RegisterPrefs(registry.get());
+
+  if (!profile) {
+    SystemNetworkContextManager::RegisterPrefs(registry.get());
+#if defined(OS_WIN)
+    OSCrypt::RegisterLocalPrefs(registry.get());
+#endif
+  }
+
+  // Browser process preferences.
+  // Based on chrome/browser/browser_process_impl.cc RegisterPrefs.
+  registry->RegisterBooleanPref(prefs::kAllowCrossOriginAuthPrompt, false);
+
+  // Browser UI preferences.
+  // Based on chrome/browser/ui/browser_ui_prefs.cc RegisterBrowserPrefs.
+  registry->RegisterBooleanPref(prefs::kAllowFileSelectionDialogs, true);
+
+  // From Chrome::RegisterBrowserUserPrefs.
+  registry->RegisterBooleanPref(prefs::kPrintPreviewUseSystemDefaultPrinter,
+                                false);
+
+  if (command_line->HasSwitch(switches::kEnablePreferenceTesting)) {
+    // Preferences used with unit tests.
+    registry->RegisterBooleanPref("test.bool", true);
+    registry->RegisterIntegerPref("test.int", 2);
+    registry->RegisterDoublePref("test.double", 5.0);
+    registry->RegisterStringPref("test.string", "default");
+    registry->RegisterListPref("test.list");
+    registry->RegisterDictionaryPref("test.dict");
+  }
+
+  if (profile) {
+    // Call RegisterProfilePrefs() for all services listed by
+    // EnsureBrowserContextKeyedServiceFactoriesBuilt().
+    BrowserContextDependencyManager::GetInstance()
+        ->RegisterProfilePrefsForServices(registry.get());
+
+    // Default profile preferences.
+    AccessibilityUIMessageHandler::RegisterProfilePrefs(registry.get());
+    chrome_browser_net::RegisterPredictionOptionsProfilePrefs(registry.get());
+    DeviceIDFetcher::RegisterProfilePrefs(registry.get());
+    extensions::ExtensionPrefs::RegisterProfilePrefs(registry.get());
+    HostContentSettingsMap::RegisterProfilePrefs(registry.get());
+    language::LanguagePrefs::RegisterProfilePrefs(registry.get());
+    media_router::RegisterProfilePrefs(registry.get());
+    ProfileNetworkContextService::RegisterProfilePrefs(registry.get());
+
+    const std::string& locale =
+        command_line->GetSwitchValueASCII(switches::kLang);
+    DCHECK(!locale.empty());
+    renderer_prefs::RegisterProfilePrefs(registry.get(), locale);
+
+    // Print preferences.
+    // Based on ProfileImpl::RegisterProfilePrefs.
+    registry->RegisterBooleanPref(prefs::kForceGoogleSafeSearch, false);
+    registry->RegisterIntegerPref(prefs::kForceYouTubeRestrict,
+                                  safe_search_util::YOUTUBE_RESTRICT_OFF);
+    registry->RegisterStringPref(prefs::kAllowedDomainsForApps, std::string());
+    registry->RegisterBooleanPref(prefs::kPrintingEnabled, true);
+    registry->RegisterBooleanPref(prefs::kPrintPreviewDisabled,
+                                  !extensions::PrintPreviewEnabled());
+    registry->RegisterStringPref(
+        prefs::kPrintPreviewDefaultDestinationSelectionRules, std::string());
+    registry->RegisterBooleanPref(prefs::kCloudPrintSubmitEnabled, false);
+    printing::PrintPreviewStickySettings::RegisterProfilePrefs(registry.get());
+    DownloadPrefs::RegisterProfilePrefs(registry.get());
+
+    // Cache preferences.
+    // Based on ProfileImpl::RegisterProfilePrefs.
+    registry->RegisterFilePathPref(prefs::kDiskCacheDir, cache_path);
+    registry->RegisterIntegerPref(prefs::kDiskCacheSize, 0);
+
+    // Spell checking preferences.
+    // Modify defaults from SpellcheckServiceFactory::RegisterProfilePrefs.
+    std::string spellcheck_lang =
+        command_line->GetSwitchValueASCII(switches::kOverrideSpellCheckLang);
+    if (!spellcheck_lang.empty()) {
+      registry->SetDefaultPrefValue(spellcheck::prefs::kSpellCheckDictionary,
+                                    base::Value(spellcheck_lang));
+    }
+    const bool enable_spelling_service_ =
+        command_line->HasSwitch(switches::kEnableSpellingService);
+    registry->SetDefaultPrefValue(
+        spellcheck::prefs::kSpellCheckUseSpellingService,
+        base::Value(enable_spelling_service_));
+    registry->SetDefaultPrefValue(spellcheck::prefs::kSpellCheckEnable,
+                                  base::Value(!enable_spelling_service_));
+
+    // Pepper flash preferences.
+    // Modify defaults from DeviceIDFetcher::RegisterProfilePrefs.
+    registry->SetDefaultPrefValue(prefs::kEnableDRM, base::Value(false));
+
+    // DevTools preferences.
+    // Based on DevToolsWindow::RegisterProfilePrefs.
+    registry->RegisterDictionaryPref(prefs::kDevToolsPreferences);
+    registry->RegisterDictionaryPref(prefs::kDevToolsEditedFiles);
+
+    // Language preferences. Used by ProfileNetworkContextService and
+    // InterceptedRequestHandlerWrapper.
+    const std::string& accept_language_list = GetAcceptLanguageList(profile);
+    if (!accept_language_list.empty()) {
+      registry->SetDefaultPrefValue(language::prefs::kAcceptLanguages,
+                                    base::Value(accept_language_list));
+    }
+    registry->RegisterListPref(prefs::kWebRtcLocalIpsAllowedUrls);
+  }
+
+  // Build the PrefService that manages the PrefRegistry and PrefStores.
+  return factory.CreateSyncable(registry.get());
+}
+
+}  // namespace browser_prefs
diff --git a/src/libcef/browser/prefs/browser_prefs.h b/src/libcef/browser/prefs/browser_prefs.h
new file mode 100644
index 0000000..94c6d13
--- /dev/null
+++ b/src/libcef/browser/prefs/browser_prefs.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PREFS_BROWSER_PREFS_H_
+#define CEF_LIBCEF_BROWSER_PREFS_BROWSER_PREFS_H_
+
+#include <memory>
+
+namespace base {
+class FilePath;
+}
+
+class PrefService;
+class Profile;
+
+namespace browser_prefs {
+
+// Name for the user prefs JSON file.
+extern const char kUserPrefsFileName[];
+
+// Create the PrefService used to manage pref registration and storage.
+// |profile| will be nullptr for the system-level PrefService.
+std::unique_ptr<PrefService> CreatePrefService(Profile* profile,
+                                               const base::FilePath& cache_path,
+                                               bool persist_user_preferences);
+
+}  // namespace browser_prefs
+
+#endif  // CEF_LIBCEF_BROWSER_PREFS_BROWSER_PREFS_H_
diff --git a/src/libcef/browser/prefs/pref_store.cc b/src/libcef/browser/prefs/pref_store.cc
new file mode 100644
index 0000000..906dc0c
--- /dev/null
+++ b/src/libcef/browser/prefs/pref_store.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/prefs/pref_store.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+
+CefPrefStore::CefPrefStore()
+    : read_only_(true),
+      read_success_(true),
+      read_error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
+      block_async_read_(false),
+      pending_async_read_(false),
+      init_complete_(false),
+      committed_(true) {}
+
+bool CefPrefStore::GetValue(const std::string& key,
+                            const base::Value** value) const {
+  return prefs_.GetValue(key, value);
+}
+
+std::unique_ptr<base::DictionaryValue> CefPrefStore::GetValues() const {
+  return prefs_.AsDictionaryValue();
+}
+
+bool CefPrefStore::GetMutableValue(const std::string& key,
+                                   base::Value** value) {
+  return prefs_.GetValue(key, value);
+}
+
+void CefPrefStore::AddObserver(PrefStore::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void CefPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool CefPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
+}
+
+bool CefPrefStore::IsInitializationComplete() const {
+  return init_complete_;
+}
+
+void CefPrefStore::SetValue(const std::string& key,
+                            std::unique_ptr<base::Value> value,
+                            uint32_t flags) {
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) {
+    committed_ = false;
+    NotifyPrefValueChanged(key);
+  }
+}
+
+void CefPrefStore::SetValueSilently(const std::string& key,
+                                    std::unique_ptr<base::Value> value,
+                                    uint32_t flags) {
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value))))
+    committed_ = false;
+}
+
+void CefPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+  if (prefs_.RemoveValue(key)) {
+    committed_ = false;
+    NotifyPrefValueChanged(key);
+  }
+}
+
+bool CefPrefStore::ReadOnly() const {
+  return read_only_;
+}
+
+PersistentPrefStore::PrefReadError CefPrefStore::GetReadError() const {
+  return read_error_;
+}
+
+PersistentPrefStore::PrefReadError CefPrefStore::ReadPrefs() {
+  NotifyInitializationCompleted();
+  return read_error_;
+}
+
+void CefPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
+  DCHECK(!pending_async_read_);
+  error_delegate_.reset(error_delegate);
+  if (block_async_read_)
+    pending_async_read_ = true;
+  else
+    NotifyInitializationCompleted();
+}
+
+void CefPrefStore::CommitPendingWrite(
+    base::OnceClosure done_callback,
+    base::OnceClosure synchronous_done_callback) {
+  committed_ = true;
+  PersistentPrefStore::CommitPendingWrite(std::move(done_callback),
+                                          std::move(synchronous_done_callback));
+}
+
+void CefPrefStore::SchedulePendingLossyWrites() {}
+
+void CefPrefStore::ClearMutableValues() {
+  NOTIMPLEMENTED();
+}
+
+void CefPrefStore::OnStoreDeletionFromDisk() {}
+
+void CefPrefStore::SetInitializationCompleted() {
+  NotifyInitializationCompleted();
+}
+
+void CefPrefStore::NotifyPrefValueChanged(const std::string& key) {
+  for (Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+}
+
+void CefPrefStore::NotifyInitializationCompleted() {
+  DCHECK(!init_complete_);
+  init_complete_ = true;
+  if (read_success_ && read_error_ != PREF_READ_ERROR_NONE && error_delegate_)
+    error_delegate_->OnError(read_error_);
+  for (Observer& observer : observers_)
+    observer.OnInitializationCompleted(read_success_);
+}
+
+void CefPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) {
+  for (Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+}
+
+void CefPrefStore::SetString(const std::string& key, const std::string& value) {
+  SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void CefPrefStore::SetInteger(const std::string& key, int value) {
+  SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void CefPrefStore::SetBoolean(const std::string& key, bool value) {
+  SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS);
+}
+
+bool CefPrefStore::GetString(const std::string& key, std::string* value) const {
+  const base::Value* stored_value;
+  if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+    return false;
+
+  return stored_value->GetAsString(value);
+}
+
+bool CefPrefStore::GetInteger(const std::string& key, int* value) const {
+  const base::Value* stored_value;
+  if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+    return false;
+
+  return stored_value->GetAsInteger(value);
+}
+
+bool CefPrefStore::GetBoolean(const std::string& key, bool* value) const {
+  const base::Value* stored_value;
+  if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+    return false;
+
+  return stored_value->GetAsBoolean(value);
+}
+
+void CefPrefStore::SetBlockAsyncRead(bool block_async_read) {
+  DCHECK(!init_complete_);
+  block_async_read_ = block_async_read;
+  if (pending_async_read_ && !block_async_read_)
+    NotifyInitializationCompleted();
+}
+
+void CefPrefStore::set_read_only(bool read_only) {
+  read_only_ = read_only;
+}
+
+void CefPrefStore::set_read_success(bool read_success) {
+  DCHECK(!init_complete_);
+  read_success_ = read_success;
+}
+
+void CefPrefStore::set_read_error(
+    PersistentPrefStore::PrefReadError read_error) {
+  DCHECK(!init_complete_);
+  read_error_ = read_error;
+}
+
+CefPrefStore::~CefPrefStore() {}
diff --git a/src/libcef/browser/prefs/pref_store.h b/src/libcef/browser/prefs/pref_store.h
new file mode 100644
index 0000000..4128fac
--- /dev/null
+++ b/src/libcef/browser/prefs/pref_store.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PREFS_PREF_STORE_H_
+#define CEF_LIBCEF_BROWSER_PREFS_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_value_map.h"
+
+// Preference store implementation that supports explicit manipulation of the
+// contents of the store, triggering notifications where appropriate.
+// Based on components/prefs/testing_pref_store.h.
+class CefPrefStore : public PersistentPrefStore {
+ public:
+  CefPrefStore();
+
+  // Overriden from PrefStore.
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+  bool IsInitializationComplete() const override;
+
+  // PersistentPrefStore overrides:
+  bool GetMutableValue(const std::string& key, base::Value** result) override;
+  void ReportValueChanged(const std::string& key, uint32_t flags) override;
+  void SetValue(const std::string& key,
+                std::unique_ptr<base::Value> value,
+                uint32_t flags) override;
+  void SetValueSilently(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) override;
+  void RemoveValue(const std::string& key, uint32_t flags) override;
+  bool ReadOnly() const override;
+  PrefReadError GetReadError() const override;
+  PersistentPrefStore::PrefReadError ReadPrefs() override;
+  void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
+  virtual void CommitPendingWrite(
+      base::OnceClosure done_callback,
+      base::OnceClosure synchronous_done_callback) override;
+  void SchedulePendingLossyWrites() override;
+  void ClearMutableValues() override;
+  void OnStoreDeletionFromDisk() override;
+
+  // Marks the store as having completed initialization.
+  void SetInitializationCompleted();
+
+  // Used for tests to trigger notifications explicitly.
+  void NotifyPrefValueChanged(const std::string& key);
+  void NotifyInitializationCompleted();
+
+  // Some convenience getters/setters.
+  void SetString(const std::string& key, const std::string& value);
+  void SetInteger(const std::string& key, int value);
+  void SetBoolean(const std::string& key, bool value);
+
+  bool GetString(const std::string& key, std::string* value) const;
+  bool GetInteger(const std::string& key, int* value) const;
+  bool GetBoolean(const std::string& key, bool* value) const;
+
+  // Determines whether ReadPrefsAsync completes immediately. Defaults to false
+  // (non-blocking). To block, invoke this with true (blocking) before the call
+  // to ReadPrefsAsync. To unblock, invoke again with false (non-blocking) after
+  // the call to ReadPrefsAsync.
+  void SetBlockAsyncRead(bool block_async_read);
+
+  // Getter and Setter methods for setting and getting the state of the
+  // |TestingPrefStore|.
+  virtual void set_read_only(bool read_only);
+  void set_read_success(bool read_success);
+  void set_read_error(PersistentPrefStore::PrefReadError read_error);
+  bool committed() { return committed_; }
+
+ protected:
+  ~CefPrefStore() override;
+
+ private:
+  // Stores the preference values.
+  PrefValueMap prefs_;
+
+  // Flag that indicates if the PrefStore is read-only
+  bool read_only_;
+
+  // The result to pass to PrefStore::Observer::OnInitializationCompleted
+  bool read_success_;
+
+  // The result to return from ReadPrefs or ReadPrefsAsync.
+  PersistentPrefStore::PrefReadError read_error_;
+
+  // Whether a call to ReadPrefsAsync should block.
+  bool block_async_read_;
+
+  // Whether there is a pending call to ReadPrefsAsync.
+  bool pending_async_read_;
+
+  // Whether initialization has been completed.
+  bool init_complete_;
+
+  // Whether the store contents have been committed to disk since the last
+  // mutation.
+  bool committed_;
+
+  std::unique_ptr<ReadErrorDelegate> error_delegate_;
+  base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefPrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_TESTING_PREF_STORE_H_
diff --git a/src/libcef/browser/prefs/renderer_prefs.cc b/src/libcef/browser/prefs/renderer_prefs.cc
new file mode 100644
index 0000000..3941837
--- /dev/null
+++ b/src/libcef/browser/prefs/renderer_prefs.cc
@@ -0,0 +1,361 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/prefs/renderer_prefs.h"
+
+#include <string>
+
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/extensions/browser_extensions_util.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "base/command_line.h"
+#include "base/i18n/character_encoding.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/accessibility/animation_policy_prefs.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/extensions/extension_webkit_preferences.h"
+#include "chrome/browser/font_family_cache.h"
+#include "chrome/browser/ui/prefs/prefs_tab_helper.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/command_line_pref_store.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_store.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/web_preferences.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/view_type_utils.h"
+#include "extensions/common/constants.h"
+#include "media/media_buildflags.h"
+#include "third_party/blink/public/common/peerconnection/webrtc_ip_handling_policy.h"
+
+namespace renderer_prefs {
+
+namespace {
+
+// Set default values based on CEF command-line flags for preferences that are
+// not available via the PrefService. Chromium command-line flags should not
+// exist for these preferences.
+void SetDefaultPrefs(content::WebPreferences& web) {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+
+  web.javascript_enabled =
+      !command_line->HasSwitch(switches::kDisableJavascript);
+  web.allow_scripts_to_close_windows =
+      !command_line->HasSwitch(switches::kDisableJavascriptCloseWindows);
+  web.javascript_can_access_clipboard =
+      !command_line->HasSwitch(switches::kDisableJavascriptAccessClipboard);
+  web.allow_universal_access_from_file_urls =
+      command_line->HasSwitch(switches::kAllowUniversalAccessFromFileUrls);
+  web.shrinks_standalone_images_to_fit =
+      command_line->HasSwitch(switches::kImageShrinkStandaloneToFit);
+  web.text_areas_are_resizable =
+      !command_line->HasSwitch(switches::kDisableTextAreaResize);
+}
+
+// Chrome preferences.
+// Should match ChromeContentBrowserClient::OverrideWebkitPrefs.
+void SetChromePrefs(CefBrowserContext* profile, content::WebPreferences& web) {
+  PrefService* prefs = profile->GetPrefs();
+
+  // Fill per-script font preferences.
+  FontFamilyCache::FillFontFamilyMap(profile,
+                                     prefs::kWebKitStandardFontFamilyMap,
+                                     &web.standard_font_family_map);
+  FontFamilyCache::FillFontFamilyMap(profile, prefs::kWebKitFixedFontFamilyMap,
+                                     &web.fixed_font_family_map);
+  FontFamilyCache::FillFontFamilyMap(profile, prefs::kWebKitSerifFontFamilyMap,
+                                     &web.serif_font_family_map);
+  FontFamilyCache::FillFontFamilyMap(profile,
+                                     prefs::kWebKitSansSerifFontFamilyMap,
+                                     &web.sans_serif_font_family_map);
+  FontFamilyCache::FillFontFamilyMap(profile,
+                                     prefs::kWebKitCursiveFontFamilyMap,
+                                     &web.cursive_font_family_map);
+  FontFamilyCache::FillFontFamilyMap(profile,
+                                     prefs::kWebKitFantasyFontFamilyMap,
+                                     &web.fantasy_font_family_map);
+  FontFamilyCache::FillFontFamilyMap(profile,
+                                     prefs::kWebKitPictographFontFamilyMap,
+                                     &web.pictograph_font_family_map);
+
+  web.default_font_size = prefs->GetInteger(prefs::kWebKitDefaultFontSize);
+  web.default_fixed_font_size =
+      prefs->GetInteger(prefs::kWebKitDefaultFixedFontSize);
+  web.minimum_font_size = prefs->GetInteger(prefs::kWebKitMinimumFontSize);
+  web.minimum_logical_font_size =
+      prefs->GetInteger(prefs::kWebKitMinimumLogicalFontSize);
+
+  web.default_encoding = prefs->GetString(prefs::kDefaultCharset);
+
+  web.dom_paste_enabled = prefs->GetBoolean(prefs::kWebKitDomPasteEnabled);
+  web.tabs_to_links = prefs->GetBoolean(prefs::kWebkitTabsToLinks);
+
+  if (!prefs->GetBoolean(prefs::kWebKitJavascriptEnabled))
+    web.javascript_enabled = false;
+  if (!prefs->GetBoolean(prefs::kWebKitWebSecurityEnabled))
+    web.web_security_enabled = false;
+  if (!prefs->GetBoolean(prefs::kWebKitPluginsEnabled))
+    web.plugins_enabled = false;
+  web.loads_images_automatically =
+      prefs->GetBoolean(prefs::kWebKitLoadsImagesAutomatically);
+
+  if (prefs->GetBoolean(prefs::kDisable3DAPIs)) {
+    web.webgl1_enabled = false;
+    web.webgl2_enabled = false;
+  }
+
+  web.allow_running_insecure_content =
+      prefs->GetBoolean(prefs::kWebKitAllowRunningInsecureContent);
+
+  web.password_echo_enabled = browser_defaults::kPasswordEchoEnabled;
+
+  web.text_areas_are_resizable =
+      prefs->GetBoolean(prefs::kWebKitTextAreasAreResizable);
+  web.hyperlink_auditing_enabled =
+      prefs->GetBoolean(prefs::kEnableHyperlinkAuditing);
+
+  if (extensions::ExtensionsEnabled()) {
+    std::string image_animation_policy =
+        prefs->GetString(prefs::kAnimationPolicy);
+    if (image_animation_policy == kAnimationPolicyOnce)
+      web.animation_policy = content::IMAGE_ANIMATION_POLICY_ANIMATION_ONCE;
+    else if (image_animation_policy == kAnimationPolicyNone)
+      web.animation_policy = content::IMAGE_ANIMATION_POLICY_NO_ANIMATION;
+    else
+      web.animation_policy = content::IMAGE_ANIMATION_POLICY_ALLOWED;
+  }
+
+  // Make sure we will set the default_encoding with canonical encoding name.
+  web.default_encoding =
+      base::GetCanonicalEncodingNameByAliasName(web.default_encoding);
+  if (web.default_encoding.empty()) {
+    prefs->ClearPref(prefs::kDefaultCharset);
+    web.default_encoding = prefs->GetString(prefs::kDefaultCharset);
+  }
+  DCHECK(!web.default_encoding.empty());
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnablePotentiallyAnnoyingSecurityFeatures)) {
+    web.disable_reading_from_canvas = true;
+    web.strict_mixed_content_checking = true;
+    web.strict_powerful_feature_restrictions = true;
+  }
+}
+
+// Extension preferences.
+// Should match ChromeContentBrowserClientExtensionsPart::OverrideWebkitPrefs.
+void SetExtensionPrefs(content::RenderViewHost* rvh,
+                       content::WebPreferences& web) {
+  if (!extensions::ExtensionsEnabled())
+    return;
+
+  const extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(
+          rvh->GetProcess()->GetBrowserContext());
+  if (!registry)
+    return;
+
+  // Note: it's not possible for kExtensionsScheme to change during the lifetime
+  // of the process.
+  //
+  // Ensure that we are only granting extension preferences to URLs with the
+  // correct scheme. Without this check, chrome-guest:// schemes used by webview
+  // tags as well as hosts that happen to match the id of an installed extension
+  // would get the wrong preferences.
+  const GURL& site_url = rvh->GetSiteInstance()->GetSiteURL();
+  if (!site_url.SchemeIs(extensions::kExtensionScheme))
+    return;
+
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderViewHost(rvh);
+  extensions::ViewType view_type = extensions::GetViewType(web_contents);
+  const extensions::Extension* extension =
+      registry->enabled_extensions().GetByID(site_url.host());
+  extension_webkit_preferences::SetPreferences(extension, view_type, &web);
+}
+
+// Helper macro for setting a WebPreferences variable based on the value of a
+// CefBrowserSettings variable.
+#define SET_STATE(cef_var, web_var)   \
+  if (cef_var == STATE_ENABLED)       \
+    web_var = true;                   \
+  else if (cef_var == STATE_DISABLED) \
+    web_var = false;
+
+// Set preferences based on CefBrowserSettings.
+void SetCefPrefs(const CefBrowserSettings& cef, content::WebPreferences& web) {
+  if (cef.standard_font_family.length > 0) {
+    web.standard_font_family_map[content::kCommonScript] =
+        CefString(&cef.standard_font_family);
+  }
+  if (cef.fixed_font_family.length > 0) {
+    web.fixed_font_family_map[content::kCommonScript] =
+        CefString(&cef.fixed_font_family);
+  }
+  if (cef.serif_font_family.length > 0) {
+    web.serif_font_family_map[content::kCommonScript] =
+        CefString(&cef.serif_font_family);
+  }
+  if (cef.sans_serif_font_family.length > 0) {
+    web.sans_serif_font_family_map[content::kCommonScript] =
+        CefString(&cef.sans_serif_font_family);
+  }
+  if (cef.cursive_font_family.length > 0) {
+    web.cursive_font_family_map[content::kCommonScript] =
+        CefString(&cef.cursive_font_family);
+  }
+  if (cef.fantasy_font_family.length > 0) {
+    web.fantasy_font_family_map[content::kCommonScript] =
+        CefString(&cef.fantasy_font_family);
+  }
+
+  if (cef.default_font_size > 0)
+    web.default_font_size = cef.default_font_size;
+  if (cef.default_fixed_font_size > 0)
+    web.default_fixed_font_size = cef.default_fixed_font_size;
+  if (cef.minimum_font_size > 0)
+    web.minimum_font_size = cef.minimum_font_size;
+  if (cef.minimum_logical_font_size > 0)
+    web.minimum_logical_font_size = cef.minimum_logical_font_size;
+
+  if (cef.default_encoding.length > 0)
+    web.default_encoding = CefString(&cef.default_encoding);
+
+  SET_STATE(cef.remote_fonts, web.remote_fonts_enabled);
+  SET_STATE(cef.javascript, web.javascript_enabled);
+  SET_STATE(cef.javascript_close_windows, web.allow_scripts_to_close_windows);
+  SET_STATE(cef.javascript_access_clipboard,
+            web.javascript_can_access_clipboard);
+  SET_STATE(cef.javascript_dom_paste, web.dom_paste_enabled);
+  SET_STATE(cef.plugins, web.plugins_enabled);
+  SET_STATE(cef.universal_access_from_file_urls,
+            web.allow_universal_access_from_file_urls);
+  SET_STATE(cef.file_access_from_file_urls,
+            web.allow_file_access_from_file_urls);
+  SET_STATE(cef.web_security, web.web_security_enabled);
+  SET_STATE(cef.image_loading, web.loads_images_automatically);
+  SET_STATE(cef.image_shrink_standalone_to_fit,
+            web.shrinks_standalone_images_to_fit);
+  SET_STATE(cef.text_area_resize, web.text_areas_are_resizable);
+  SET_STATE(cef.tab_to_links, web.tabs_to_links);
+  SET_STATE(cef.local_storage, web.local_storage_enabled);
+  SET_STATE(cef.databases, web.databases_enabled);
+  SET_STATE(cef.application_cache, web.application_cache_enabled);
+
+  // Never explicitly enable GPU-related functions in this method because the
+  // GPU blacklist is not being checked here.
+  if (cef.webgl == STATE_DISABLED) {
+    web.webgl1_enabled = false;
+    web.webgl2_enabled = false;
+  }
+}
+
+void SetString(CommandLinePrefStore* prefs,
+               const std::string& key,
+               const std::string& value) {
+  prefs->SetValue(key, base::WrapUnique(new base::Value(value)),
+                  WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void SetBool(CommandLinePrefStore* prefs, const std::string& key, bool value) {
+  prefs->SetValue(key, base::WrapUnique(new base::Value(value)),
+                  WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+}
+
+}  // namespace
+
+void SetCommandLinePrefDefaults(CommandLinePrefStore* prefs) {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+
+  if (command_line->HasSwitch(switches::kDefaultEncoding)) {
+    SetString(prefs, prefs::kDefaultCharset,
+              command_line->GetSwitchValueASCII(switches::kDefaultEncoding));
+  }
+
+  if (command_line->HasSwitch(switches::kDisableJavascriptDomPaste))
+    SetBool(prefs, prefs::kWebKitDomPasteEnabled, false);
+  if (command_line->HasSwitch(switches::kDisableImageLoading))
+    SetBool(prefs, prefs::kWebKitLoadsImagesAutomatically, false);
+  if (command_line->HasSwitch(switches::kDisableTabToLinks))
+    SetBool(prefs, prefs::kWebkitTabsToLinks, false);
+  if (command_line->HasSwitch(switches::kDisablePlugins))
+    SetBool(prefs, prefs::kWebKitPluginsEnabled, false);
+}
+
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
+                          const std::string& locale) {
+  PrefsTabHelper::RegisterProfilePrefs(registry, locale);
+  RegisterAnimationPolicyPrefs(registry);
+
+  // From chrome::RegisterBrowserUserPrefs.
+  registry->RegisterBooleanPref(
+      prefs::kEnableDoNotTrack, false,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+
+  // TODO(guoweis): Remove next 2 options at M50.
+  registry->RegisterBooleanPref(prefs::kWebRTCMultipleRoutesEnabled, true);
+  registry->RegisterBooleanPref(prefs::kWebRTCNonProxiedUdpEnabled, true);
+  registry->RegisterStringPref(prefs::kWebRTCIPHandlingPolicy,
+                               blink::kWebRTCIPHandlingDefault);
+  registry->RegisterStringPref(prefs::kWebRTCUDPPortRange, std::string());
+
+#if !defined(OS_MACOSX)
+  registry->RegisterBooleanPref(prefs::kFullscreenAllowed, true);
+#endif
+
+  // From ChromeContentBrowserClient::RegisterProfilePrefs.
+  registry->RegisterBooleanPref(prefs::kDisable3DAPIs, false);
+  registry->RegisterBooleanPref(prefs::kEnableHyperlinkAuditing, true);
+
+  // From Profile::RegisterProfilePrefs.
+  registry->RegisterDictionaryPref(prefs::kPartitionDefaultZoomLevel);
+  registry->RegisterDictionaryPref(prefs::kPartitionPerHostZoomLevels);
+}
+
+void PopulateWebPreferences(content::RenderViewHost* rvh,
+                            content::WebPreferences& web) {
+  CefRefPtr<CefBrowserHostImpl> browser =
+      extensions::GetOwnerBrowserForHost(rvh, nullptr);
+
+  // Set defaults for preferences that are not handled by PrefService.
+  SetDefaultPrefs(web);
+
+  // Set preferences based on the context's PrefService.
+  if (browser) {
+    CefBrowserContext* profile = static_cast<CefBrowserContext*>(
+        browser->web_contents()->GetBrowserContext());
+    SetChromePrefs(profile, web);
+  }
+
+  // Set preferences based on the extension.
+  SetExtensionPrefs(rvh, web);
+
+  if (browser) {
+    // Set preferences based on CefBrowserSettings.
+    SetCefPrefs(browser->settings(), web);
+
+    web.picture_in_picture_enabled = browser->IsPictureInPictureSupported();
+
+    // Set the background color for the WebView.
+    web.base_background_color = browser->GetBackgroundColor();
+  } else {
+    // We don't know for sure that the browser will be windowless but assume
+    // that the global windowless state is likely to be accurate.
+    web.base_background_color =
+        CefContext::Get()->GetBackgroundColor(nullptr, STATE_DEFAULT);
+  }
+}
+
+}  // namespace renderer_prefs
diff --git a/src/libcef/browser/prefs/renderer_prefs.h b/src/libcef/browser/prefs/renderer_prefs.h
new file mode 100644
index 0000000..3fd4b1b
--- /dev/null
+++ b/src/libcef/browser/prefs/renderer_prefs.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PREFS_RENDERER_PREFS_H_
+#define CEF_LIBCEF_BROWSER_PREFS_RENDERER_PREFS_H_
+#pragma once
+
+#include "include/internal/cef_types_wrappers.h"
+
+class CommandLinePrefStore;
+
+namespace content {
+class RenderViewHost;
+struct WebPreferences;
+}  // namespace content
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace renderer_prefs {
+
+// Register additional renderer-related preferences.
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
+                          const std::string& locale);
+
+// Set default values based on CEF command-line flags for preferences that are
+// available via the PrefService. Chromium command-line flags should not exist
+// for these preferences.
+void SetCommandLinePrefDefaults(CommandLinePrefStore* prefs);
+
+// Populate WebPreferences based on a combination of command-line values,
+// PrefService and CefBrowserSettings.
+void PopulateWebPreferences(content::RenderViewHost* rvh,
+                            content::WebPreferences& web);
+
+}  // namespace renderer_prefs
+
+#endif  // CEF_LIBCEF_BROWSER_PREFS_RENDERER_PREFS_H_
diff --git a/src/libcef/browser/print_settings_impl.cc b/src/libcef/browser/print_settings_impl.cc
new file mode 100644
index 0000000..b91942f
--- /dev/null
+++ b/src/libcef/browser/print_settings_impl.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/print_settings_impl.h"
+
+#include "base/logging.h"
+
+CefPrintSettingsImpl::CefPrintSettingsImpl(
+    std::unique_ptr<printing::PrintSettings> settings,
+    bool read_only)
+    : CefValueBase<CefPrintSettings, printing::PrintSettings>(settings.get(),
+                                                              nullptr,
+                                                              kOwnerNoDelete,
+                                                              read_only,
+                                                              nullptr),
+      settings_(std::move(settings)) {}
+
+bool CefPrintSettingsImpl::IsValid() {
+  return !detached();
+}
+
+bool CefPrintSettingsImpl::IsReadOnly() {
+  return read_only();
+}
+
+void CefPrintSettingsImpl::SetOrientation(bool landscape) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->SetOrientation(landscape);
+}
+
+bool CefPrintSettingsImpl::IsLandscape() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().landscape();
+}
+
+void CefPrintSettingsImpl::SetPrinterPrintableArea(
+    const CefSize& physical_size_device_units,
+    const CefRect& printable_area_device_units,
+    bool landscape_needs_flip) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  gfx::Size size(physical_size_device_units.width,
+                 physical_size_device_units.height);
+  gfx::Rect rect(printable_area_device_units.x, printable_area_device_units.y,
+                 printable_area_device_units.width,
+                 printable_area_device_units.height);
+  mutable_value()->SetPrinterPrintableArea(size, rect, landscape_needs_flip);
+}
+
+void CefPrintSettingsImpl::SetDeviceName(const CefString& name) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->set_device_name(name.ToString16());
+}
+
+CefString CefPrintSettingsImpl::GetDeviceName() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().device_name();
+}
+
+void CefPrintSettingsImpl::SetDPI(int dpi) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->set_dpi(dpi);
+}
+
+int CefPrintSettingsImpl::GetDPI() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().dpi();
+}
+
+void CefPrintSettingsImpl::SetPageRanges(const PageRangeList& ranges) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  printing::PageRanges page_ranges;
+  PageRangeList::const_iterator it = ranges.begin();
+  for (; it != ranges.end(); ++it) {
+    const CefRange& cef_range = *it;
+    printing::PageRange range;
+    range.from = cef_range.from;
+    range.to = cef_range.to;
+    page_ranges.push_back(range);
+  }
+  mutable_value()->set_ranges(page_ranges);
+}
+
+size_t CefPrintSettingsImpl::GetPageRangesCount() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().ranges().size();
+}
+
+void CefPrintSettingsImpl::GetPageRanges(PageRangeList& ranges) {
+  CEF_VALUE_VERIFY_RETURN_VOID(false);
+  if (!ranges.empty())
+    ranges.clear();
+  const printing::PageRanges& page_ranges = const_value().ranges();
+  printing::PageRanges::const_iterator it = page_ranges.begin();
+  for (; it != page_ranges.end(); ++it) {
+    const printing::PageRange& range = *it;
+    ranges.push_back(CefRange(range.from, range.to));
+  }
+}
+
+void CefPrintSettingsImpl::SetSelectionOnly(bool selection_only) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->set_selection_only(selection_only);
+}
+
+bool CefPrintSettingsImpl::IsSelectionOnly() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().selection_only();
+}
+
+void CefPrintSettingsImpl::SetCollate(bool collate) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->set_collate(collate);
+}
+
+bool CefPrintSettingsImpl::WillCollate() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().collate();
+}
+
+void CefPrintSettingsImpl::SetColorModel(ColorModel model) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->set_color(static_cast<printing::ColorModel>(model));
+}
+
+CefPrintSettings::ColorModel CefPrintSettingsImpl::GetColorModel() {
+  CEF_VALUE_VERIFY_RETURN(false, COLOR_MODEL_UNKNOWN);
+  return static_cast<ColorModel>(const_value().color());
+}
+
+void CefPrintSettingsImpl::SetCopies(int copies) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->set_copies(copies);
+}
+
+int CefPrintSettingsImpl::GetCopies() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().copies();
+}
+
+void CefPrintSettingsImpl::SetDuplexMode(DuplexMode mode) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->set_duplex_mode(static_cast<printing::DuplexMode>(mode));
+}
+
+CefPrintSettings::DuplexMode CefPrintSettingsImpl::GetDuplexMode() {
+  CEF_VALUE_VERIFY_RETURN(false, DUPLEX_MODE_UNKNOWN);
+  return static_cast<DuplexMode>(const_value().duplex_mode());
+}
+
+std::unique_ptr<printing::PrintSettings> CefPrintSettingsImpl::TakeOwnership() {
+  Detach(nullptr);
+  return std::move(settings_);
+}
+
+// CefPrintSettings implementation.
+
+// static
+CefRefPtr<CefPrintSettings> CefPrintSettings::Create() {
+  return new CefPrintSettingsImpl(std::make_unique<printing::PrintSettings>(),
+                                  false);
+}
diff --git a/src/libcef/browser/print_settings_impl.h b/src/libcef/browser/print_settings_impl.h
new file mode 100644
index 0000000..589048e
--- /dev/null
+++ b/src/libcef/browser/print_settings_impl.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PRINT_SETTINGS_IMPL_H_
+#define CEF_LIBCEF_BROWSER_PRINT_SETTINGS_IMPL_H_
+#pragma once
+
+#include "include/cef_print_settings.h"
+#include "libcef/common/value_base.h"
+
+#include "printing/print_settings.h"
+
+// CefPrintSettings implementation
+class CefPrintSettingsImpl
+    : public CefValueBase<CefPrintSettings, printing::PrintSettings> {
+ public:
+  CefPrintSettingsImpl(std::unique_ptr<printing::PrintSettings> settings,
+                       bool read_only);
+
+  // CefPrintSettings methods.
+  bool IsValid() override;
+  bool IsReadOnly() override;
+  void SetOrientation(bool landscape) override;
+  bool IsLandscape() override;
+  void SetPrinterPrintableArea(const CefSize& physical_size_device_units,
+                               const CefRect& printable_area_device_units,
+                               bool landscape_needs_flip) override;
+  void SetDeviceName(const CefString& name) override;
+  CefString GetDeviceName() override;
+  void SetDPI(int dpi) override;
+  int GetDPI() override;
+  void SetPageRanges(const PageRangeList& ranges) override;
+  size_t GetPageRangesCount() override;
+  void GetPageRanges(PageRangeList& ranges) override;
+  void SetSelectionOnly(bool selection_only) override;
+  bool IsSelectionOnly() override;
+  void SetCollate(bool collate) override;
+  bool WillCollate() override;
+  void SetColorModel(ColorModel model) override;
+  ColorModel GetColorModel() override;
+  void SetCopies(int copies) override;
+  int GetCopies() override;
+  void SetDuplexMode(DuplexMode mode) override;
+  DuplexMode GetDuplexMode() override;
+
+  std::unique_ptr<printing::PrintSettings> TakeOwnership() WARN_UNUSED_RESULT;
+
+ private:
+  std::unique_ptr<printing::PrintSettings> settings_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefPrintSettingsImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_PRINT_SETTINGS_IMPL_H_
diff --git a/src/libcef/browser/printing/constrained_window_views_client.cc b/src/libcef/browser/printing/constrained_window_views_client.cc
new file mode 100644
index 0000000..1670f41
--- /dev/null
+++ b/src/libcef/browser/printing/constrained_window_views_client.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/printing/constrained_window_views_client.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "components/web_modal/web_contents_modal_dialog_host.h"
+
+namespace {
+
+class CefConstrainedWindowViewsClient
+    : public constrained_window::ConstrainedWindowViewsClient {
+ public:
+  CefConstrainedWindowViewsClient() {}
+  ~CefConstrainedWindowViewsClient() override {}
+
+ private:
+  // ConstrainedWindowViewsClient:
+  web_modal::ModalDialogHost* GetModalDialogHost(
+      gfx::NativeWindow parent) override {
+    NOTREACHED();
+    return nullptr;
+  }
+  gfx::NativeView GetDialogHostView(gfx::NativeWindow parent) override {
+    NOTREACHED();
+    return gfx::NativeView();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(CefConstrainedWindowViewsClient);
+};
+
+}  // namespace
+
+std::unique_ptr<constrained_window::ConstrainedWindowViewsClient>
+CreateCefConstrainedWindowViewsClient() {
+  return base::WrapUnique(new CefConstrainedWindowViewsClient);
+}
\ No newline at end of file
diff --git a/src/libcef/browser/printing/constrained_window_views_client.h b/src/libcef/browser/printing/constrained_window_views_client.h
new file mode 100644
index 0000000..0f18784
--- /dev/null
+++ b/src/libcef/browser/printing/constrained_window_views_client.h
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PRINTING_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_
+#define CEF_LIBCEF_BROWSER_PRINTING_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_
+
+#include <memory>
+
+#include "components/constrained_window/constrained_window_views_client.h"
+
+// Creates a ConstrainedWindowViewsClient for the Chrome environment.
+std::unique_ptr<constrained_window::ConstrainedWindowViewsClient>
+CreateCefConstrainedWindowViewsClient();
+
+#endif  // CEF_LIBCEF_BROWSER_PRINTING_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_
\ No newline at end of file
diff --git a/src/libcef/browser/printing/print_dialog_linux.cc b/src/libcef/browser/printing/print_dialog_linux.cc
new file mode 100644
index 0000000..02c51b0
--- /dev/null
+++ b/src/libcef/browser/printing/print_dialog_linux.cc
@@ -0,0 +1,324 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/printing/print_dialog_linux.h"
+
+#include <string>
+#include <vector>
+
+#include "libcef/browser/extensions/browser_extensions_util.h"
+#include "libcef/browser/print_settings_impl.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/content_client.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "printing/metafile.h"
+#include "printing/print_job_constants.h"
+#include "printing/print_settings.h"
+
+using content::BrowserThread;
+using printing::PageRanges;
+using printing::PrintSettings;
+
+class CefPrintDialogCallbackImpl : public CefPrintDialogCallback {
+ public:
+  explicit CefPrintDialogCallbackImpl(CefRefPtr<CefPrintDialogLinux> dialog)
+      : dialog_(dialog) {}
+
+  void Continue(CefRefPtr<CefPrintSettings> settings) override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (dialog_.get()) {
+        dialog_->OnPrintContinue(settings);
+        dialog_ = nullptr;
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT, base::Bind(&CefPrintDialogCallbackImpl::Continue,
+                                        this, settings));
+    }
+  }
+
+  void Cancel() override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (dialog_.get()) {
+        dialog_->OnPrintCancel();
+        dialog_ = nullptr;
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT,
+                    base::Bind(&CefPrintDialogCallbackImpl::Cancel, this));
+    }
+  }
+
+  void Disconnect() { dialog_ = nullptr; }
+
+ private:
+  CefRefPtr<CefPrintDialogLinux> dialog_;
+
+  IMPLEMENT_REFCOUNTING(CefPrintDialogCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefPrintDialogCallbackImpl);
+};
+
+class CefPrintJobCallbackImpl : public CefPrintJobCallback {
+ public:
+  explicit CefPrintJobCallbackImpl(CefRefPtr<CefPrintDialogLinux> dialog)
+      : dialog_(dialog) {}
+
+  void Continue() override {
+    if (CEF_CURRENTLY_ON_UIT()) {
+      if (dialog_.get()) {
+        dialog_->OnJobCompleted();
+        dialog_ = nullptr;
+      }
+    } else {
+      CEF_POST_TASK(CEF_UIT,
+                    base::Bind(&CefPrintJobCallbackImpl::Continue, this));
+    }
+  }
+
+  void Disconnect() { dialog_ = nullptr; }
+
+ private:
+  CefRefPtr<CefPrintDialogLinux> dialog_;
+
+  IMPLEMENT_REFCOUNTING(CefPrintJobCallbackImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefPrintJobCallbackImpl);
+};
+
+// static
+printing::PrintDialogGtkInterface* CefPrintDialogLinux::CreatePrintDialog(
+    PrintingContextLinux* context) {
+  CEF_REQUIRE_UIT();
+  return new CefPrintDialogLinux(context);
+}
+
+// static
+gfx::Size CefPrintDialogLinux::GetPdfPaperSize(
+    printing::PrintingContextLinux* context) {
+  CEF_REQUIRE_UIT();
+
+  gfx::Size size;
+
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefBrowserProcessHandler> browser_handler =
+        app->GetBrowserProcessHandler();
+    if (browser_handler.get()) {
+      CefRefPtr<CefPrintHandler> handler = browser_handler->GetPrintHandler();
+      if (handler.get()) {
+        const printing::PrintSettings& settings = context->settings();
+        CefSize cef_size =
+            handler->GetPdfPaperSize(settings.device_units_per_inch());
+        size.SetSize(cef_size.width, cef_size.height);
+      }
+    }
+  }
+
+  if (size.IsEmpty()) {
+    LOG(ERROR) << "Empty size value returned in GetPdfPaperSize; "
+                  "PDF printing will fail.";
+  }
+  return size;
+}
+
+// static
+void CefPrintDialogLinux::OnPrintStart(int render_process_id,
+                                       int render_routing_id) {
+  if (!CEF_CURRENTLY_ON(CEF_UIT)) {
+    CEF_POST_TASK(CEF_UIT, base::Bind(&CefPrintDialogLinux::OnPrintStart,
+                                      render_process_id, render_routing_id));
+    return;
+  }
+
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (!app.get())
+    return;
+
+  CefRefPtr<CefBrowserProcessHandler> browser_handler =
+      app->GetBrowserProcessHandler();
+  if (!browser_handler.get())
+    return;
+
+  CefRefPtr<CefPrintHandler> handler = browser_handler->GetPrintHandler();
+  if (!handler.get())
+    return;
+
+  CefRefPtr<CefBrowserHostImpl> browser =
+      extensions::GetOwnerBrowserForFrameRoute(render_process_id,
+                                               render_routing_id, nullptr);
+  if (browser.get())
+    handler->OnPrintStart(browser.get());
+}
+
+CefPrintDialogLinux::CefPrintDialogLinux(PrintingContextLinux* context)
+    : context_(context) {
+  DCHECK(context_);
+  browser_ = extensions::GetOwnerBrowserForFrameRoute(
+      context_->render_process_id(), context_->render_frame_id(), nullptr);
+  DCHECK(browser_);
+}
+
+CefPrintDialogLinux::~CefPrintDialogLinux() {
+  // It's not safe to dereference |context_| during the destruction of this
+  // object because the PrintJobWorker which owns |context_| may already have
+  // been deleted.
+  CEF_REQUIRE_UIT();
+  ReleaseHandler();
+}
+
+void CefPrintDialogLinux::UseDefaultSettings() {
+  UpdateSettings(std::make_unique<PrintSettings>(), true);
+}
+
+void CefPrintDialogLinux::UpdateSettings(
+    std::unique_ptr<PrintSettings> settings) {
+  UpdateSettings(std::move(settings), false);
+}
+
+void CefPrintDialogLinux::ShowDialog(
+    gfx::NativeView parent_view,
+    bool has_selection,
+    PrintingContextLinux::PrintSettingsCallback callback) {
+  CEF_REQUIRE_UIT();
+
+  SetHandler();
+  if (!handler_.get()) {
+    std::move(callback).Run(PrintingContextLinux::CANCEL);
+    return;
+  }
+
+  callback_ = std::move(callback);
+
+  CefRefPtr<CefPrintDialogCallbackImpl> callback_impl(
+      new CefPrintDialogCallbackImpl(this));
+
+  if (!handler_->OnPrintDialog(browser_.get(), has_selection,
+                               callback_impl.get())) {
+    callback_impl->Disconnect();
+    OnPrintCancel();
+  }
+}
+
+void CefPrintDialogLinux::PrintDocument(
+    const printing::MetafilePlayer& metafile,
+    const base::string16& document_name) {
+  // This runs on the print worker thread, does not block the UI thread.
+  DCHECK(!CEF_CURRENTLY_ON_UIT());
+
+  // The document printing tasks can outlive the PrintingContext that created
+  // this dialog.
+  AddRef();
+
+  bool success = base::CreateTemporaryFile(&path_to_pdf_);
+
+  if (success) {
+    base::File file;
+    file.Initialize(path_to_pdf_,
+                    base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+    success = metafile.SaveTo(&file);
+    file.Close();
+    if (!success)
+      base::DeleteFile(path_to_pdf_, false);
+  }
+
+  if (!success) {
+    LOG(ERROR) << "Saving metafile failed";
+    // Matches AddRef() above.
+    Release();
+    return;
+  }
+
+  // No errors, continue printing.
+  CEF_POST_TASK(CEF_UIT, base::Bind(&CefPrintDialogLinux::SendDocumentToPrinter,
+                                    this, document_name));
+}
+
+void CefPrintDialogLinux::AddRefToDialog() {
+  AddRef();
+}
+
+void CefPrintDialogLinux::ReleaseDialog() {
+  Release();
+}
+
+void CefPrintDialogLinux::SetHandler() {
+  if (handler_.get())
+    return;
+
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefBrowserProcessHandler> browser_handler =
+        app->GetBrowserProcessHandler();
+    if (browser_handler.get())
+      handler_ = browser_handler->GetPrintHandler();
+  }
+}
+
+void CefPrintDialogLinux::ReleaseHandler() {
+  if (handler_.get()) {
+    handler_->OnPrintReset(browser_.get());
+    handler_ = nullptr;
+  }
+}
+
+bool CefPrintDialogLinux::UpdateSettings(
+    std::unique_ptr<PrintSettings> settings,
+    bool get_defaults) {
+  CEF_REQUIRE_UIT();
+
+  SetHandler();
+  if (!handler_.get())
+    return false;
+
+  CefRefPtr<CefPrintSettingsImpl> settings_impl(
+      new CefPrintSettingsImpl(std::move(settings), false));
+  handler_->OnPrintSettings(browser_.get(), settings_impl.get(), get_defaults);
+
+  context_->InitWithSettings(settings_impl->TakeOwnership());
+  return true;
+}
+
+void CefPrintDialogLinux::SendDocumentToPrinter(
+    const base::string16& document_name) {
+  CEF_REQUIRE_UIT();
+
+  if (!handler_.get()) {
+    OnJobCompleted();
+    return;
+  }
+
+  CefRefPtr<CefPrintJobCallbackImpl> callback_impl(
+      new CefPrintJobCallbackImpl(this));
+
+  if (!handler_->OnPrintJob(browser_.get(), document_name, path_to_pdf_.value(),
+                            callback_impl.get())) {
+    callback_impl->Disconnect();
+    OnJobCompleted();
+  }
+}
+
+void CefPrintDialogLinux::OnPrintContinue(
+    CefRefPtr<CefPrintSettings> settings) {
+  CefPrintSettingsImpl* impl =
+      static_cast<CefPrintSettingsImpl*>(settings.get());
+  context_->InitWithSettings(impl->TakeOwnership());
+  std::move(callback_).Run(PrintingContextLinux::OK);
+}
+
+void CefPrintDialogLinux::OnPrintCancel() {
+  std::move(callback_).Run(PrintingContextLinux::CANCEL);
+}
+
+void CefPrintDialogLinux::OnJobCompleted() {
+  CEF_POST_BACKGROUND_TASK(
+      base::Bind(base::IgnoreResult(&base::DeleteFile), path_to_pdf_, false));
+
+  // Printing finished. Matches AddRef() in PrintDocument();
+  Release();
+}
diff --git a/src/libcef/browser/printing/print_dialog_linux.h b/src/libcef/browser/printing/print_dialog_linux.h
new file mode 100644
index 0000000..7f01e09
--- /dev/null
+++ b/src/libcef/browser/printing/print_dialog_linux.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBCEF_BROWSER_PRINTING_PRINT_DIALOG_LINUX_H_
+#define LIBCEF_BROWSER_PRINTING_PRINT_DIALOG_LINUX_H_
+
+#include "include/cef_print_handler.h"
+#include "libcef/browser/browser_host_impl.h"
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "content/public/browser/browser_thread.h"
+#include "printing/print_dialog_gtk_interface.h"
+#include "printing/printing_context_linux.h"
+
+namespace printing {
+class MetafilePlayer;
+class PrintSettings;
+}  // namespace printing
+
+using printing::PrintingContextLinux;
+
+// Needs to be freed on the UI thread to clean up its member variables.
+class CefPrintDialogLinux : public printing::PrintDialogGtkInterface,
+                            public base::RefCountedThreadSafe<
+                                CefPrintDialogLinux,
+                                content::BrowserThread::DeleteOnUIThread> {
+ public:
+  // Creates and returns a print dialog.
+  static printing::PrintDialogGtkInterface* CreatePrintDialog(
+      PrintingContextLinux* context);
+
+  // Returns the paper size in device units.
+  static gfx::Size GetPdfPaperSize(printing::PrintingContextLinux* context);
+
+  // Notify the client when printing has started.
+  static void OnPrintStart(int render_process_id, int render_routing_id);
+
+  // PrintDialogGtkInterface implementation.
+  void UseDefaultSettings() override;
+  void UpdateSettings(
+      std::unique_ptr<printing::PrintSettings> settings) override;
+  void ShowDialog(
+      gfx::NativeView parent_view,
+      bool has_selection,
+      PrintingContextLinux::PrintSettingsCallback callback) override;
+  void PrintDocument(const printing::MetafilePlayer& metafile,
+                     const base::string16& document_name) override;
+  void AddRefToDialog() override;
+  void ReleaseDialog() override;
+
+ private:
+  friend class base::DeleteHelper<CefPrintDialogLinux>;
+  friend class base::RefCountedThreadSafe<
+      CefPrintDialogLinux,
+      content::BrowserThread::DeleteOnUIThread>;
+  friend struct content::BrowserThread::DeleteOnThread<
+      content::BrowserThread::UI>;
+  friend class CefPrintDialogCallbackImpl;
+  friend class CefPrintJobCallbackImpl;
+
+  explicit CefPrintDialogLinux(PrintingContextLinux* context);
+  ~CefPrintDialogLinux() override;
+
+  void SetHandler();
+  void ReleaseHandler();
+
+  bool UpdateSettings(std::unique_ptr<printing::PrintSettings> settings,
+                      bool get_defaults);
+
+  // Prints document named |document_name|.
+  void SendDocumentToPrinter(const base::string16& document_name);
+
+  // Handles print dialog response.
+  void OnPrintContinue(CefRefPtr<CefPrintSettings> settings);
+  void OnPrintCancel();
+
+  // Handles print job response.
+  void OnJobCompleted();
+
+  CefRefPtr<CefPrintHandler> handler_;
+
+  // Printing dialog callback.
+  PrintingContextLinux::PrintSettingsCallback callback_;
+  PrintingContextLinux* context_;
+  CefRefPtr<CefBrowserHostImpl> browser_;
+
+  base::FilePath path_to_pdf_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefPrintDialogLinux);
+};
+
+#endif  // LIBCEF_BROWSER_PRINTING_PRINT_DIALOG_LINUX_H_
diff --git a/src/libcef/browser/printing/print_view_manager.cc b/src/libcef/browser/printing/print_view_manager.cc
new file mode 100644
index 0000000..396d1b4
--- /dev/null
+++ b/src/libcef/browser/printing/print_view_manager.cc
@@ -0,0 +1,419 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/printing/print_view_manager.h"
+
+#include "include/internal/cef_types_wrappers.h"
+#include "libcef/browser/browser_info.h"
+#include "libcef/browser/browser_info_manager.h"
+#include "libcef/browser/download_manager_delegate.h"
+#include "libcef/browser/extensions/extension_web_contents_observer.h"
+
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/browser/printing/print_preview_dialog_controller.h"
+#include "chrome/browser/printing/print_preview_message_handler.h"
+#include "chrome/browser/printing/print_view_manager.h"
+#include "chrome/browser/printing/printer_query.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "components/printing/common/print.mojom.h"
+#include "components/printing/common/print_messages.h"
+#include "content/browser/download/download_manager_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "printing/metafile_skia.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+
+#include "libcef/browser/thread_util.h"
+
+using content::BrowserThread;
+
+namespace printing {
+
+namespace {
+
+const int PREVIEW_UIID = 12345678;
+
+// Convert CefPdfPrintSettings into base::DictionaryValue.
+void FillInDictionaryFromPdfPrintSettings(
+    const CefPdfPrintSettings& pdf_settings,
+    int request_id,
+    base::DictionaryValue& print_settings) {
+  // Fixed settings.
+  print_settings.SetIntKey(kSettingPrinterType, kPdfPrinter);
+  print_settings.SetInteger(kSettingColor, GRAY);
+  print_settings.SetInteger(kSettingDuplexMode, SIMPLEX);
+  print_settings.SetInteger(kSettingCopies, 1);
+  print_settings.SetBoolean(kSettingCollate, false);
+  print_settings.SetString(kSettingDeviceName, "");
+  print_settings.SetBoolean(kSettingRasterizePdf, false);
+  print_settings.SetBoolean(kSettingPreviewModifiable, false);
+  print_settings.SetInteger(kSettingDpiHorizontal, 0);
+  print_settings.SetInteger(kSettingDpiVertical, 0);
+  print_settings.SetInteger(kSettingPagesPerSheet, 1);
+
+  // User defined settings.
+  print_settings.SetBoolean(kSettingLandscape, !!pdf_settings.landscape);
+  print_settings.SetBoolean(kSettingShouldPrintSelectionOnly,
+                            !!pdf_settings.selection_only);
+  print_settings.SetBoolean(kSettingShouldPrintBackgrounds,
+                            !!pdf_settings.backgrounds_enabled);
+  print_settings.SetBoolean(kSettingHeaderFooterEnabled,
+                            !!pdf_settings.header_footer_enabled);
+  print_settings.SetInteger(kSettingScaleFactor, pdf_settings.scale_factor > 0
+                                                     ? pdf_settings.scale_factor
+                                                     : 100);
+
+  if (pdf_settings.header_footer_enabled) {
+    print_settings.SetString(
+        kSettingHeaderFooterTitle,
+        CefString(&pdf_settings.header_footer_title).ToString16());
+    print_settings.SetString(
+        kSettingHeaderFooterURL,
+        CefString(&pdf_settings.header_footer_url).ToString16());
+  }
+
+  if (pdf_settings.page_width > 0 && pdf_settings.page_height > 0) {
+    std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+    dict->SetInteger(kSettingMediaSizeWidthMicrons, pdf_settings.page_width);
+    dict->SetInteger(kSettingMediaSizeHeightMicrons, pdf_settings.page_height);
+    print_settings.Set(kSettingMediaSize, std::move(dict));
+  }
+
+  int margin_type = DEFAULT_MARGINS;
+  switch (pdf_settings.margin_type) {
+    case PDF_PRINT_MARGIN_NONE:
+      margin_type = NO_MARGINS;
+      break;
+    case PDF_PRINT_MARGIN_MINIMUM:
+      margin_type = PRINTABLE_AREA_MARGINS;
+      break;
+    case PDF_PRINT_MARGIN_CUSTOM:
+      margin_type = CUSTOM_MARGINS;
+      break;
+    default:
+      break;
+  }
+
+  print_settings.SetInteger(kSettingMarginsType, margin_type);
+  if (margin_type == CUSTOM_MARGINS) {
+    std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+    dict->SetInteger(kSettingMarginTop, pdf_settings.margin_top);
+    dict->SetInteger(kSettingMarginRight, pdf_settings.margin_right);
+    dict->SetInteger(kSettingMarginBottom, pdf_settings.margin_bottom);
+    dict->SetInteger(kSettingMarginLeft, pdf_settings.margin_left);
+    print_settings.Set(kSettingMarginsCustom, std::move(dict));
+  }
+
+  // Service settings.
+  print_settings.SetInteger(kPreviewUIID, PREVIEW_UIID);
+  print_settings.SetInteger(kPreviewRequestID, request_id);
+  print_settings.SetBoolean(kIsFirstRequest, request_id != 0);
+}
+
+void StopWorker(int document_cookie) {
+  if (document_cookie <= 0)
+    return;
+  scoped_refptr<PrintQueriesQueue> queue =
+      g_browser_process->print_job_manager()->queue();
+  std::unique_ptr<PrinterQuery> printer_query =
+      queue->PopPrinterQuery(document_cookie);
+  if (printer_query.get()) {
+    base::PostTask(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&PrinterQuery::StopWorker, std::move(printer_query)));
+  }
+}
+
+// Write the PDF file to disk.
+void SavePdfFile(scoped_refptr<base::RefCountedSharedMemoryMapping> data,
+                 const base::FilePath& path,
+                 const CefPrintViewManager::PdfPrintCallback& callback) {
+  CEF_REQUIRE_BLOCKING();
+  DCHECK_GT(data->size(), 0U);
+
+  MetafileSkia metafile;
+  metafile.InitFromData(*data);
+
+  base::File file(path,
+                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  bool ok = file.IsValid() && metafile.SaveTo(&file);
+
+  if (!callback.is_null()) {
+    base::PostTask(FROM_HERE, {BrowserThread::UI}, base::Bind(callback, ok));
+  }
+}
+
+}  // namespace
+
+struct CefPrintViewManager::PdfPrintState {
+  content::RenderFrameHost* printing_rfh_ = nullptr;
+  base::FilePath output_path_;
+  base::DictionaryValue settings_;
+  PdfPrintCallback callback_;
+};
+
+CefPrintViewManager::CefPrintViewManager(content::WebContents* web_contents)
+    : WebContentsObserver(web_contents) {
+  PrintViewManager::CreateForWebContents(web_contents);
+  PrintPreviewMessageHandler::CreateForWebContents(web_contents);
+}
+
+CefPrintViewManager::~CefPrintViewManager() {
+  TerminatePdfPrintJob();
+}
+
+bool CefPrintViewManager::PrintToPDF(content::RenderFrameHost* rfh,
+                                     const base::FilePath& path,
+                                     const CefPdfPrintSettings& settings,
+                                     const PdfPrintCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // Don't start print again while printing is currently in progress.
+  if (pdf_print_state_)
+    return false;
+
+  // Don't print interstitials or crashed tabs.
+  if (!web_contents() || web_contents()->ShowingInterstitialPage() ||
+      web_contents()->IsCrashed()) {
+    return false;
+  }
+
+  pdf_print_state_.reset(new PdfPrintState);
+  pdf_print_state_->printing_rfh_ = rfh;
+  pdf_print_state_->output_path_ = path;
+  pdf_print_state_->callback_ = callback;
+
+  FillInDictionaryFromPdfPrintSettings(settings, ++next_pdf_request_id_,
+                                       pdf_print_state_->settings_);
+
+  mojo::AssociatedRemote<printing::mojom::PrintRenderFrame>
+      print_render_frame_remote;
+  rfh->GetRemoteAssociatedInterfaces()->GetInterface(
+      &print_render_frame_remote);
+  print_render_frame_remote->InitiatePrintPreview({},
+                                                  !!settings.selection_only);
+
+  return true;
+}
+
+bool CefPrintViewManager::PrintPreviewNow(content::RenderFrameHost* rfh,
+                                          bool has_selection) {
+  return PrintViewManager::FromWebContents(web_contents())
+      ->PrintPreviewNow(rfh, has_selection);
+}
+
+void CefPrintViewManager::RenderFrameDeleted(
+    content::RenderFrameHost* render_frame_host) {
+  if (pdf_print_state_ &&
+      render_frame_host == pdf_print_state_->printing_rfh_) {
+    TerminatePdfPrintJob();
+  }
+}
+
+void CefPrintViewManager::NavigationStopped() {
+  TerminatePdfPrintJob();
+}
+
+void CefPrintViewManager::RenderProcessGone(base::TerminationStatus status) {
+  TerminatePdfPrintJob();
+}
+
+bool CefPrintViewManager::OnMessageReceived(
+    const IPC::Message& message,
+    content::RenderFrameHost* render_frame_host) {
+  bool handled = true;
+  if (!pdf_print_state_) {
+    IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(CefPrintViewManager, message,
+                                     render_frame_host)
+      IPC_MESSAGE_HANDLER(PrintHostMsg_RequestPrintPreview,
+                          OnRequestPrintPreview)
+      IPC_MESSAGE_HANDLER(PrintHostMsg_ShowScriptedPrintPreview,
+                          OnShowScriptedPrintPreview)
+      IPC_MESSAGE_UNHANDLED(handled = false)
+    IPC_END_MESSAGE_MAP()
+    return false;
+  }
+
+  IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(CefPrintViewManager, message,
+                                   render_frame_host)
+    IPC_MESSAGE_HANDLER(PrintHostMsg_DidShowPrintDialog,
+                        OnDidShowPrintDialog_PrintToPdf)
+    IPC_MESSAGE_HANDLER(PrintHostMsg_RequestPrintPreview,
+                        OnRequestPrintPreview_PrintToPdf)
+    IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting,
+                        OnMetafileReadyForPrinting_PrintToPdf)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  return handled;
+}
+void CefPrintViewManager::OnRequestPrintPreview(
+    content::RenderFrameHost* rfh,
+    const PrintHostMsg_RequestPrintPreview_Params&) {
+  InitializePrintPreview(rfh->GetFrameTreeNodeId());
+}
+
+void CefPrintViewManager::OnShowScriptedPrintPreview(
+    content::RenderFrameHost* rfh,
+    bool source_is_modifiable) {
+  InitializePrintPreview(rfh->GetFrameTreeNodeId());
+}
+
+void CefPrintViewManager::OnDidShowPrintDialog_PrintToPdf(
+    content::RenderFrameHost* rfh) {}
+
+void CefPrintViewManager::OnRequestPrintPreview_PrintToPdf(
+    content::RenderFrameHost* rfh,
+    const PrintHostMsg_RequestPrintPreview_Params&) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!pdf_print_state_)
+    return;
+
+  DCHECK_EQ(pdf_print_state_->printing_rfh_, rfh);
+
+  mojo::AssociatedRemote<printing::mojom::PrintRenderFrame>
+      print_render_frame_remote;
+  rfh->GetRemoteAssociatedInterfaces()->GetInterface(
+      &print_render_frame_remote);
+  print_render_frame_remote->PrintPreview(pdf_print_state_->settings_.Clone());
+}
+
+void CefPrintViewManager::OnMetafileReadyForPrinting_PrintToPdf(
+    content::RenderFrameHost* rfh,
+    const PrintHostMsg_DidPreviewDocument_Params& params,
+    const PrintHostMsg_PreviewIds& ids) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  StopWorker(params.document_cookie);
+
+  if (!pdf_print_state_)
+    return;
+
+  DCHECK_EQ(pdf_print_state_->printing_rfh_, rfh);
+
+  mojo::AssociatedRemote<printing::mojom::PrintRenderFrame>
+      print_render_frame_remote;
+  rfh->GetRemoteAssociatedInterfaces()->GetInterface(
+      &print_render_frame_remote);
+  print_render_frame_remote->OnPrintPreviewDialogClosed();
+
+  auto shared_buf = base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(
+      params.content.metafile_data_region);
+  if (!shared_buf) {
+    TerminatePdfPrintJob();
+    return;
+  }
+
+  const base::FilePath output_path = pdf_print_state_->output_path_;
+  const PdfPrintCallback print_callback = pdf_print_state_->callback_;
+
+  // Reset state information.
+  pdf_print_state_.reset();
+
+  // Save the PDF file to disk and then execute the callback.
+  CEF_POST_USER_VISIBLE_TASK(
+      base::Bind(&SavePdfFile, shared_buf, output_path, print_callback));
+}
+
+void CefPrintViewManager::TerminatePdfPrintJob() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!pdf_print_state_)
+    return;
+
+  if (!pdf_print_state_->callback_.is_null()) {
+    // Execute the callback.
+    base::PostTask(FROM_HERE, {BrowserThread::UI},
+                   base::Bind(pdf_print_state_->callback_, false));
+  }
+
+  // Reset state information.
+  pdf_print_state_.reset();
+}
+
+void CefPrintViewManager::InitializePrintPreview(int frame_tree_node_id) {
+  PrintPreviewDialogController* dialog_controller =
+      PrintPreviewDialogController::GetInstance();
+  if (!dialog_controller)
+    return;
+
+  dialog_controller->PrintPreview(web_contents());
+
+  content::WebContents* preview_contents =
+      dialog_controller->GetPrintPreviewForContents(web_contents());
+
+  extensions::CefExtensionWebContentsObserver::CreateForWebContents(
+      preview_contents);
+
+  PrintPreviewHelper::CreateForWebContents(preview_contents);
+  PrintPreviewHelper::FromWebContents(preview_contents)
+      ->Initialize(frame_tree_node_id);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(CefPrintViewManager)
+
+// CefPrintViewManager::PrintPreviewHelper
+
+CefPrintViewManager::PrintPreviewHelper::PrintPreviewHelper(
+    content::WebContents* contents)
+    : content::WebContentsObserver(contents) {}
+
+void CefPrintViewManager::PrintPreviewHelper::Initialize(
+    int parent_frame_tree_node_id) {
+  DCHECK_EQ(parent_frame_tree_node_id_,
+            content::RenderFrameHost::kNoFrameTreeNodeId);
+  DCHECK_NE(parent_frame_tree_node_id,
+            content::RenderFrameHost::kNoFrameTreeNodeId);
+  parent_frame_tree_node_id_ = parent_frame_tree_node_id;
+
+  auto context = web_contents()->GetBrowserContext();
+  auto manager = content::BrowserContext::GetDownloadManager(context);
+
+  if (!context->GetDownloadManagerDelegate()) {
+    manager->SetDelegate(new CefDownloadManagerDelegate(manager));
+  }
+
+  auto browser_info =
+      CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameTreeNode(
+          parent_frame_tree_node_id_);
+  DCHECK(browser_info);
+  if (!browser_info)
+    return;
+
+  // Associate guest state information with the owner browser.
+  browser_info->MaybeCreateFrame(web_contents()->GetMainFrame(),
+                                 true /* is_guest_view */);
+}
+
+void CefPrintViewManager::PrintPreviewHelper::WebContentsDestroyed() {
+  auto browser_info =
+      CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameTreeNode(
+          parent_frame_tree_node_id_);
+  DCHECK(browser_info);
+  if (!browser_info)
+    return;
+
+  // Disassociate guest state information with the owner browser.
+  browser_info->RemoveFrame(web_contents()->GetMainFrame());
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(CefPrintViewManager::PrintPreviewHelper)
+
+}  // namespace printing
diff --git a/src/libcef/browser/printing/print_view_manager.h b/src/libcef/browser/printing/print_view_manager.h
new file mode 100644
index 0000000..e341103
--- /dev/null
+++ b/src/libcef/browser/printing/print_view_manager.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H_
+#define CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H_
+
+#include "include/internal/cef_types_wrappers.h"
+
+#include "base/macros.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class RenderFrameHost;
+class RenderProcessHost;
+class WebContentsObserver;
+}  // namespace content
+
+class CefBrowserInfo;
+
+struct PrintHostMsg_DidPreviewDocument_Params;
+struct PrintHostMsg_PreviewIds;
+struct PrintHostMsg_RequestPrintPreview_Params;
+
+namespace printing {
+
+// Manages the print commands for a WebContents.
+class CefPrintViewManager
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<CefPrintViewManager> {
+ public:
+  ~CefPrintViewManager() override;
+
+  // Callback executed on PDF printing completion.
+  typedef base::Callback<void(bool /*ok*/)> PdfPrintCallback;
+
+  // Print the current document to a PDF file. Execute |callback| on completion.
+  bool PrintToPDF(content::RenderFrameHost* rfh,
+                  const base::FilePath& path,
+                  const CefPdfPrintSettings& settings,
+                  const PdfPrintCallback& callback);
+
+  // Call to Chrome's PrintViewManager.
+  bool PrintPreviewNow(content::RenderFrameHost* rfh, bool has_selection);
+
+  // content::WebContentsObserver implementation.
+  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
+  void NavigationStopped() override;
+  void RenderProcessGone(base::TerminationStatus status) override;
+  bool OnMessageReceived(const IPC::Message& message,
+                         content::RenderFrameHost* render_frame_host) override;
+
+  // Used to track the lifespan of the print preview WebContents.
+  class PrintPreviewHelper
+      : public content::WebContentsObserver,
+        public content::WebContentsUserData<PrintPreviewHelper> {
+   public:
+    void Initialize(int parent_frame_tree_node_id);
+    void WebContentsDestroyed() override;
+
+   private:
+    friend class content::WebContentsUserData<PrintPreviewHelper>;
+
+    explicit PrintPreviewHelper(content::WebContents* contents);
+
+    int parent_frame_tree_node_id_ = -1;
+
+    WEB_CONTENTS_USER_DATA_KEY_DECL();
+    DISALLOW_COPY_AND_ASSIGN(PrintPreviewHelper);
+  };
+
+ private:
+  explicit CefPrintViewManager(content::WebContents* web_contents);
+  friend class content::WebContentsUserData<CefPrintViewManager>;
+
+  // IPC Message handlers.
+  void OnRequestPrintPreview(content::RenderFrameHost* rfh,
+                             const PrintHostMsg_RequestPrintPreview_Params&);
+  void OnShowScriptedPrintPreview(content::RenderFrameHost* rfh,
+                                  bool source_is_modifiable);
+  void OnRequestPrintPreview_PrintToPdf(
+      content::RenderFrameHost* rfh,
+      const PrintHostMsg_RequestPrintPreview_Params&);
+  void OnDidShowPrintDialog_PrintToPdf(content::RenderFrameHost* rfh);
+  void OnMetafileReadyForPrinting_PrintToPdf(
+      content::RenderFrameHost* rfh,
+      const PrintHostMsg_DidPreviewDocument_Params& params,
+      const PrintHostMsg_PreviewIds& ids);
+  void InitializePrintPreview(int frame_tree_node_id);
+  void TerminatePdfPrintJob();
+
+  // Used for printing to PDF. Only accessed on the browser process UI thread.
+  int next_pdf_request_id_ = content::RenderFrameHost::kNoFrameTreeNodeId;
+  struct PdfPrintState;
+  std::unique_ptr<PdfPrintState> pdf_print_state_;
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+  DISALLOW_COPY_AND_ASSIGN(CefPrintViewManager);
+};
+
+}  // namespace printing
+
+#endif  // CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H_
\ No newline at end of file
diff --git a/src/libcef/browser/printing/printing_message_filter.cc b/src/libcef/browser/printing/printing_message_filter.cc
new file mode 100644
index 0000000..cfe0e2d
--- /dev/null
+++ b/src/libcef/browser/printing/printing_message_filter.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/printing/printing_message_filter.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/browser/printing/printer_query.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
+#include "components/printing/browser/print_manager_utils.h"
+#include "components/printing/common/print_messages.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/child_process_host.h"
+
+#if defined(OS_LINUX)
+#include "libcef/browser/printing/print_dialog_linux.h"
+#endif
+
+using content::BrowserThread;
+
+namespace printing {
+
+namespace {
+
+class CefPrintingMessageFilterShutdownNotifierFactory
+    : public BrowserContextKeyedServiceShutdownNotifierFactory {
+ public:
+  static CefPrintingMessageFilterShutdownNotifierFactory* GetInstance();
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<
+      CefPrintingMessageFilterShutdownNotifierFactory>;
+
+  CefPrintingMessageFilterShutdownNotifierFactory()
+      : BrowserContextKeyedServiceShutdownNotifierFactory(
+            "CefPrintingMessageFilter") {}
+
+  ~CefPrintingMessageFilterShutdownNotifierFactory() override {}
+
+  DISALLOW_COPY_AND_ASSIGN(CefPrintingMessageFilterShutdownNotifierFactory);
+};
+
+base::LazyInstance<CefPrintingMessageFilterShutdownNotifierFactory>::Leaky
+    g_printing_message_filter_shutdown_notifier_factory =
+        LAZY_INSTANCE_INITIALIZER;
+
+// static
+CefPrintingMessageFilterShutdownNotifierFactory*
+CefPrintingMessageFilterShutdownNotifierFactory::GetInstance() {
+  return g_printing_message_filter_shutdown_notifier_factory.Pointer();
+}
+
+}  // namespace
+
+CefPrintingMessageFilter::CefPrintingMessageFilter(int render_process_id,
+                                                   Profile* profile)
+    : content::BrowserMessageFilter(PrintMsgStart),
+      render_process_id_(render_process_id),
+      queue_(g_browser_process->print_job_manager()->queue()) {
+  DCHECK(queue_.get());
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  printing_shutdown_notifier_ =
+      CefPrintingMessageFilterShutdownNotifierFactory::GetInstance()
+          ->Get(profile)
+          ->Subscribe(base::Bind(&CefPrintingMessageFilter::ShutdownOnUIThread,
+                                 base::Unretained(this)));
+  is_printing_enabled_.Init(prefs::kPrintingEnabled, profile->GetPrefs());
+  is_printing_enabled_.MoveToSequence(
+      base::CreateSingleThreadTaskRunner({BrowserThread::IO}));
+}
+
+void CefPrintingMessageFilter::EnsureShutdownNotifierFactoryBuilt() {
+  CefPrintingMessageFilterShutdownNotifierFactory::GetInstance();
+}
+
+CefPrintingMessageFilter::~CefPrintingMessageFilter() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+void CefPrintingMessageFilter::ShutdownOnUIThread() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  is_printing_enabled_.Destroy();
+  printing_shutdown_notifier_.reset();
+}
+
+void CefPrintingMessageFilter::OnDestruct() const {
+  BrowserThread::DeleteOnUIThread::Destruct(this);
+}
+
+bool CefPrintingMessageFilter::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(CefPrintingMessageFilter, message)
+    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings,
+                                    OnGetDefaultPrintSettings)
+    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint)
+    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings,
+                                    OnUpdatePrintSettings)
+    IPC_MESSAGE_HANDLER(PrintHostMsg_CheckForCancel, OnCheckForCancel)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void CefPrintingMessageFilter::OnGetDefaultPrintSettings(
+    IPC::Message* reply_msg) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+#if defined(OS_LINUX)
+  // Send notification to the client.
+  CefPrintDialogLinux::OnPrintStart(render_process_id_,
+                                    reply_msg->routing_id());
+#endif
+
+  std::unique_ptr<PrinterQuery> printer_query;
+  if (!is_printing_enabled_.GetValue()) {
+    // Reply with NULL query.
+    OnGetDefaultPrintSettingsReply(std::move(printer_query), reply_msg);
+    return;
+  }
+  printer_query = queue_->PopPrinterQuery(0);
+  if (!printer_query.get()) {
+    printer_query =
+        queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id());
+  }
+
+  // Loads default settings. This is asynchronous, only the IPC message sender
+  // will hang until the settings are retrieved.
+  auto* printer_query_ptr = printer_query.get();
+  printer_query_ptr->GetSettings(
+      PrinterQuery::GetSettingsAskParam::DEFAULTS, 0, false, DEFAULT_MARGINS,
+      false, false,
+      base::BindOnce(&CefPrintingMessageFilter::OnGetDefaultPrintSettingsReply,
+                     this, std::move(printer_query), reply_msg));
+}
+
+void CefPrintingMessageFilter::OnGetDefaultPrintSettingsReply(
+    std::unique_ptr<PrinterQuery> printer_query,
+    IPC::Message* reply_msg) {
+  PrintMsg_Print_Params params;
+  if (!printer_query.get() ||
+      printer_query->last_status() != PrintingContext::OK) {
+    params.Reset();
+  } else {
+    RenderParamsFromPrintSettings(printer_query->settings(), &params);
+    params.document_cookie = printer_query->cookie();
+  }
+  PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params);
+  Send(reply_msg);
+  // If printing was enabled.
+  if (printer_query.get()) {
+    // If user hasn't cancelled.
+    if (printer_query->cookie() && printer_query->settings().dpi()) {
+      queue_->QueuePrinterQuery(std::move(printer_query));
+    } else {
+      printer_query->StopWorker();
+    }
+  }
+}
+
+void CefPrintingMessageFilter::OnScriptedPrint(
+    const PrintHostMsg_ScriptedPrint_Params& params,
+    IPC::Message* reply_msg) {
+  std::unique_ptr<PrinterQuery> printer_query =
+      queue_->PopPrinterQuery(params.cookie);
+  if (!printer_query.get()) {
+    printer_query =
+        queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id());
+  }
+  printer_query->GetSettings(
+      PrinterQuery::GetSettingsAskParam::ASK_USER, params.expected_pages_count,
+      params.has_selection, params.margin_type, params.is_scripted,
+      params.is_modifiable,
+      base::BindOnce(&CefPrintingMessageFilter::OnScriptedPrintReply, this,
+                     std::move(printer_query), reply_msg));
+}
+
+void CefPrintingMessageFilter::OnScriptedPrintReply(
+    std::unique_ptr<PrinterQuery> printer_query,
+    IPC::Message* reply_msg) {
+  PrintMsg_PrintPages_Params params;
+  if (printer_query->last_status() != PrintingContext::OK ||
+      !printer_query->settings().dpi()) {
+    params.Reset();
+  } else {
+    RenderParamsFromPrintSettings(printer_query->settings(), &params.params);
+    params.params.document_cookie = printer_query->cookie();
+    params.pages = PageRange::GetPages(printer_query->settings().ranges());
+  }
+  PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params);
+  Send(reply_msg);
+  if (!params.params.dpi.IsEmpty() && params.params.document_cookie) {
+    queue_->QueuePrinterQuery(std::move(printer_query));
+  } else {
+    printer_query->StopWorker();
+  }
+}
+
+void CefPrintingMessageFilter::OnUpdatePrintSettings(int document_cookie,
+                                                     base::Value job_settings,
+                                                     IPC::Message* reply_msg) {
+  std::unique_ptr<PrinterQuery> printer_query;
+  if (!is_printing_enabled_.GetValue()) {
+    // Reply with NULL query.
+    OnUpdatePrintSettingsReply(std::move(printer_query), reply_msg);
+    return;
+  }
+  printer_query = queue_->PopPrinterQuery(document_cookie);
+  if (!printer_query.get()) {
+    printer_query = queue_->CreatePrinterQuery(
+        content::ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
+  }
+  printer_query->SetSettings(
+      std::move(job_settings),
+      base::BindOnce(&CefPrintingMessageFilter::OnUpdatePrintSettingsReply,
+                     this, std::move(printer_query), reply_msg));
+}
+
+void CefPrintingMessageFilter::OnUpdatePrintSettingsReply(
+    std::unique_ptr<PrinterQuery> printer_query,
+    IPC::Message* reply_msg) {
+  PrintMsg_PrintPages_Params params;
+  if (!printer_query.get() ||
+      printer_query->last_status() != PrintingContext::OK) {
+    params.Reset();
+  } else {
+    RenderParamsFromPrintSettings(printer_query->settings(), &params.params);
+    params.params.document_cookie = printer_query->cookie();
+    params.pages = PageRange::GetPages(printer_query->settings().ranges());
+  }
+  bool canceled = printer_query.get() &&
+                  (printer_query->last_status() == PrintingContext::CANCEL);
+  PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params,
+                                                     canceled);
+  Send(reply_msg);
+  // If user hasn't cancelled.
+  if (printer_query.get()) {
+    if (printer_query->cookie() && printer_query->settings().dpi()) {
+      queue_->QueuePrinterQuery(std::move(printer_query));
+    } else {
+      printer_query->StopWorker();
+    }
+  }
+}
+
+void CefPrintingMessageFilter::OnCheckForCancel(
+    const PrintHostMsg_PreviewIds& ids,
+    bool* cancel) {
+  *cancel = false;
+}
+
+}  // namespace printing
diff --git a/src/libcef/browser/printing/printing_message_filter.h b/src/libcef/browser/printing/printing_message_filter.h
new file mode 100644
index 0000000..d6d889b
--- /dev/null
+++ b/src/libcef/browser/printing/printing_message_filter.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
+#define CEF_LIBCEF_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
+#include "components/prefs/pref_member.h"
+#include "content/public/browser/browser_message_filter.h"
+#include "printing/buildflags/buildflags.h"
+
+struct PrintHostMsg_PreviewIds;
+struct PrintHostMsg_ScriptedPrint_Params;
+class Profile;
+
+namespace printing {
+
+class PrintQueriesQueue;
+class PrinterQuery;
+
+// This class filters out incoming printing related IPC messages for the
+// renderer process on the IPC thread.
+class CefPrintingMessageFilter : public content::BrowserMessageFilter {
+ public:
+  CefPrintingMessageFilter(int render_process_id, Profile* profile);
+
+  static void EnsureShutdownNotifierFactoryBuilt();
+
+  // content::BrowserMessageFilter methods.
+  void OnDestruct() const override;
+  bool OnMessageReceived(const IPC::Message& message) override;
+
+ private:
+  friend class base::DeleteHelper<CefPrintingMessageFilter>;
+  friend class content::BrowserThread;
+
+  ~CefPrintingMessageFilter() override;
+
+  void ShutdownOnUIThread();
+
+  // Get the default print setting.
+  void OnGetDefaultPrintSettings(IPC::Message* reply_msg);
+  void OnGetDefaultPrintSettingsReply(
+      std::unique_ptr<PrinterQuery> printer_query,
+      IPC::Message* reply_msg);
+
+  // The renderer host have to show to the user the print dialog and returns
+  // the selected print settings. The task is handled by the print worker
+  // thread and the UI thread. The reply occurs on the IO thread.
+  void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& params,
+                       IPC::Message* reply_msg);
+  void OnScriptedPrintReply(std::unique_ptr<PrinterQuery> printer_query,
+                            IPC::Message* reply_msg);
+
+  // Modify the current print settings based on |job_settings|. The task is
+  // handled by the print worker thread and the UI thread. The reply occurs on
+  // the IO thread.
+  void OnUpdatePrintSettings(int document_cookie,
+                             base::Value job_settings,
+                             IPC::Message* reply_msg);
+  void OnUpdatePrintSettingsReply(std::unique_ptr<PrinterQuery> printer_query,
+                                  IPC::Message* reply_msg);
+
+  // Check to see if print preview has been cancelled.
+  void OnCheckForCancel(const PrintHostMsg_PreviewIds& ids, bool* cancel);
+
+  std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
+      printing_shutdown_notifier_;
+
+  BooleanPrefMember is_printing_enabled_;
+
+  const int render_process_id_;
+
+  scoped_refptr<PrintQueriesQueue> queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefPrintingMessageFilter);
+};
+
+}  // namespace printing
+
+#endif  // CEF_LIBCEF_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
diff --git a/src/libcef/browser/process_util_impl.cc b/src/libcef/browser/process_util_impl.cc
new file mode 100644
index 0000000..fb42a95
--- /dev/null
+++ b/src/libcef/browser/process_util_impl.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_process_util.h"
+#include "libcef/common/command_line_impl.h"
+
+#include "base/logging.h"
+#include "base/process/launch.h"
+#include "content/public/browser/child_process_launcher_utils.h"
+
+bool CefLaunchProcess(CefRefPtr<CefCommandLine> command_line) {
+  if (!command_line.get()) {
+    NOTREACHED() << "invalid parameter";
+    return false;
+  }
+
+  if (!content::CurrentlyOnProcessLauncherTaskRunner()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  CefCommandLineImpl* impl =
+      static_cast<CefCommandLineImpl*>(command_line.get());
+
+  CefValueController::AutoLock lock_scope(impl->controller());
+
+  base::LaunchOptions options;
+  return base::LaunchProcess(impl->command_line(), options).IsValid();
+}
diff --git a/src/libcef/browser/request_context_handler_map.cc b/src/libcef/browser/request_context_handler_map.cc
new file mode 100644
index 0000000..9109898
--- /dev/null
+++ b/src/libcef/browser/request_context_handler_map.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/request_context_handler_map.h"
+
+CefRequestContextHandlerMap::CefRequestContextHandlerMap() = default;
+CefRequestContextHandlerMap::~CefRequestContextHandlerMap() = default;
+
+void CefRequestContextHandlerMap::AddHandler(
+    int render_process_id,
+    int render_frame_id,
+    int frame_tree_node_id,
+    CefRefPtr<CefRequestContextHandler> handler) {
+  DCHECK_GE(render_process_id, 0);
+  DCHECK_GE(render_frame_id, 0);
+  DCHECK_GE(frame_tree_node_id, 0);
+  DCHECK(handler);
+
+  render_id_handler_map_.insert(std::make_pair(
+      std::make_pair(render_process_id, render_frame_id), handler));
+  node_id_handler_map_.insert(std::make_pair(frame_tree_node_id, handler));
+}
+
+void CefRequestContextHandlerMap::RemoveHandler(int render_process_id,
+                                                int render_frame_id,
+                                                int frame_tree_node_id) {
+  DCHECK_GE(render_process_id, 0);
+  DCHECK_GE(render_frame_id, 0);
+  DCHECK_GE(frame_tree_node_id, 0);
+
+  auto it1 = render_id_handler_map_.find(
+      std::make_pair(render_process_id, render_frame_id));
+  if (it1 != render_id_handler_map_.end())
+    render_id_handler_map_.erase(it1);
+
+  auto it2 = node_id_handler_map_.find(frame_tree_node_id);
+  if (it2 != node_id_handler_map_.end())
+    node_id_handler_map_.erase(it2);
+}
+
+CefRefPtr<CefRequestContextHandler> CefRequestContextHandlerMap::GetHandler(
+    int render_process_id,
+    int render_frame_id,
+    int frame_tree_node_id,
+    bool require_frame_match) const {
+  if (render_process_id >= 0 && render_frame_id >= 0) {
+    const auto it1 = render_id_handler_map_.find(
+        std::make_pair(render_process_id, render_frame_id));
+    if (it1 != render_id_handler_map_.end())
+      return it1->second;
+  }
+
+  if (frame_tree_node_id >= 0) {
+    const auto it2 = node_id_handler_map_.find(frame_tree_node_id);
+    if (it2 != node_id_handler_map_.end())
+      return it2->second;
+  }
+
+  if (render_process_id >= 0 && !require_frame_match) {
+    // Choose an arbitrary handler for the same process.
+    for (auto& kv : render_id_handler_map_) {
+      if (kv.first.first == render_process_id)
+        return kv.second;
+    }
+  }
+
+  return nullptr;
+}
diff --git a/src/libcef/browser/request_context_handler_map.h b/src/libcef/browser/request_context_handler_map.h
new file mode 100644
index 0000000..51487d5
--- /dev/null
+++ b/src/libcef/browser/request_context_handler_map.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_REQUEST_CONTEXT_HANDLER_MAP_
+#define CEF_LIBCEF_BROWSER_REQUEST_CONTEXT_HANDLER_MAP_
+#pragma once
+
+#include <map>
+
+#include "include/cef_request_context.h"
+#include "include/cef_request_context_handler.h"
+
+#include "base/macros.h"
+
+// Tracks CefRequestContextHandler associations on a single thread.
+class CefRequestContextHandlerMap {
+ public:
+  CefRequestContextHandlerMap();
+  ~CefRequestContextHandlerMap();
+
+  // Keep track of handlers associated with specific frames. This information
+  // originates from frame create/delete notifications in CefBrowserHostImpl or
+  // CefMimeHandlerViewGuestDelegate which are forwarded via
+  // CefRequestContextImpl and CefBrowserContext.
+  void AddHandler(int render_process_id,
+                  int render_frame_id,
+                  int frame_tree_node_id,
+                  CefRefPtr<CefRequestContextHandler> handler);
+  void RemoveHandler(int render_process_id,
+                     int render_frame_id,
+                     int frame_tree_node_id);
+
+  // Returns the handler that matches the specified IDs. Pass -1 for unknown
+  // values. If |require_frame_match| is true only exact matches will be
+  // returned. If |require_frame_match| is false, and there is not an exact
+  // match, then the first handler for the same |render_process_id| will be
+  // returned.
+  CefRefPtr<CefRequestContextHandler> GetHandler(
+      int render_process_id,
+      int render_frame_id,
+      int frame_tree_node_id,
+      bool require_frame_match) const;
+
+ private:
+  // Map of (render_process_id, render_frame_id) to handler.
+  typedef std::map<std::pair<int, int>, CefRefPtr<CefRequestContextHandler>>
+      RenderIdHandlerMap;
+  RenderIdHandlerMap render_id_handler_map_;
+
+  // Map of frame_tree_node_id to handler. Keeping this map is necessary
+  // because, when navigating the main frame, a new (pre-commit) network request
+  // will be created before the RenderFrameHost. Consequently we can't rely
+  // on valid render IDs. See https://crbug.com/776884 for background.
+  typedef std::map<int, CefRefPtr<CefRequestContextHandler>> NodeIdHandlerMap;
+  NodeIdHandlerMap node_id_handler_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefRequestContextHandlerMap);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_REQUEST_CONTEXT_HANDLER_MAP_
diff --git a/src/libcef/browser/request_context_impl.cc b/src/libcef/browser/request_context_impl.cc
new file mode 100644
index 0000000..cace18d
--- /dev/null
+++ b/src/libcef/browser/request_context_impl.cc
@@ -0,0 +1,806 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/request_context_impl.h"
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/extensions/extension_system.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/common/task_runner_impl.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/ssl_host_state_delegate.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
+using content::BrowserThread;
+
+namespace {
+
+base::AtomicSequenceNumber g_next_id;
+
+const char* GetTypeString(base::Value::Type type) {
+  switch (type) {
+    case base::Value::Type::NONE:
+      return "NULL";
+    case base::Value::Type::BOOLEAN:
+      return "BOOLEAN";
+    case base::Value::Type::INTEGER:
+      return "INTEGER";
+    case base::Value::Type::DOUBLE:
+      return "DOUBLE";
+    case base::Value::Type::STRING:
+      return "STRING";
+    case base::Value::Type::BINARY:
+      return "BINARY";
+    case base::Value::Type::DICTIONARY:
+      return "DICTIONARY";
+    case base::Value::Type::LIST:
+      return "LIST";
+    case base::Value::Type::DEAD:
+      return "DEAD";
+  }
+
+  NOTREACHED();
+  return "UNKNOWN";
+}
+
+// Helper for HostResolver::Resolve.
+struct ResolveHostHelperOld {
+  explicit ResolveHostHelperOld(CefRefPtr<CefResolveCallback> callback)
+      : callback_(callback) {}
+
+  void OnResolveCompleted(int result) {
+    std::vector<CefString> resolved_ips;
+    base::Optional<net::AddressList> maybe_address_list =
+        request_->GetAddressResults();
+    if (maybe_address_list) {
+      net::AddressList::const_iterator iter = maybe_address_list->begin();
+      for (; iter != maybe_address_list->end(); ++iter)
+        resolved_ips.push_back(iter->ToStringWithoutPort());
+    }
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(&CefResolveCallback::OnResolveCompleted, callback_,
+                   static_cast<cef_errorcode_t>(result), resolved_ips));
+
+    delete this;
+  }
+
+  CefRefPtr<CefResolveCallback> callback_;
+  std::unique_ptr<net::HostResolver::ResolveHostRequest> request_;
+};
+
+class ResolveHostHelper : public network::ResolveHostClientBase {
+ public:
+  explicit ResolveHostHelper(CefRefPtr<CefResolveCallback> callback)
+      : callback_(callback), receiver_(this) {}
+
+  void Start(CefBrowserContext* browser_context, const CefString& origin) {
+    CEF_REQUIRE_UIT();
+
+    browser_context->GetNetworkContext()->CreateHostResolver(
+        base::nullopt, host_resolver_.BindNewPipeAndPassReceiver());
+
+    host_resolver_.set_disconnect_handler(base::BindOnce(
+        &ResolveHostHelper::OnComplete, base::Unretained(this), net::ERR_FAILED,
+        net::ResolveErrorInfo(net::ERR_FAILED), base::nullopt));
+
+    host_resolver_->ResolveHost(
+        net::HostPortPair::FromURL(GURL(origin.ToString())),
+        net::NetworkIsolationKey::Todo(), nullptr,
+        receiver_.BindNewPipeAndPassRemote());
+  }
+
+ private:
+  void OnComplete(
+      int32_t result,
+      const ::net::ResolveErrorInfo& resolve_error_info,
+      const base::Optional<net::AddressList>& resolved_addresses) override {
+    CEF_REQUIRE_UIT();
+
+    host_resolver_.reset();
+    receiver_.reset();
+
+    std::vector<CefString> resolved_ips;
+
+    if (result == net::OK) {
+      DCHECK(resolved_addresses && !resolved_addresses->empty());
+      for (const auto& value : resolved_addresses.value()) {
+        resolved_ips.push_back(value.ToStringWithoutPort());
+      }
+    }
+
+    callback_->OnResolveCompleted(static_cast<cef_errorcode_t>(result),
+                                  resolved_ips);
+    delete this;
+  }
+
+  CefRefPtr<CefResolveCallback> callback_;
+
+  mojo::Remote<network::mojom::HostResolver> host_resolver_;
+  mojo::Receiver<network::mojom::ResolveHostClient> receiver_;
+
+  DISALLOW_COPY_AND_ASSIGN(ResolveHostHelper);
+};
+
+}  // namespace
+
+// CefBrowserContext
+
+// static
+CefRefPtr<CefRequestContext> CefRequestContext::GetGlobalContext() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return nullptr;
+  }
+
+  CefRequestContextImpl::Config config;
+  config.is_global = true;
+  return CefRequestContextImpl::GetOrCreateRequestContext(config);
+}
+
+// static
+CefRefPtr<CefRequestContext> CefRequestContext::CreateContext(
+    const CefRequestContextSettings& settings,
+    CefRefPtr<CefRequestContextHandler> handler) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return nullptr;
+  }
+
+  CefRequestContextImpl::Config config;
+  config.settings = settings;
+  config.handler = handler;
+  config.unique_id = g_next_id.GetNext();
+  return CefRequestContextImpl::GetOrCreateRequestContext(config);
+}
+
+// static
+CefRefPtr<CefRequestContext> CefRequestContext::CreateContext(
+    CefRefPtr<CefRequestContext> other,
+    CefRefPtr<CefRequestContextHandler> handler) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return nullptr;
+  }
+
+  if (!other.get())
+    return nullptr;
+
+  CefRequestContextImpl::Config config;
+  config.other = static_cast<CefRequestContextImpl*>(other.get());
+  config.handler = handler;
+  config.unique_id = g_next_id.GetNext();
+  return CefRequestContextImpl::GetOrCreateRequestContext(config);
+}
+
+// CefRequestContextImpl
+
+CefRequestContextImpl::~CefRequestContextImpl() {
+  CEF_REQUIRE_UIT();
+
+  if (browser_context_) {
+    // May result in |browser_context_| being deleted if no other
+    // CefRequestContextImpl are referencing it.
+    browser_context_->RemoveCefRequestContext(this);
+  }
+}
+
+// static
+CefRefPtr<CefRequestContextImpl>
+CefRequestContextImpl::CreateGlobalRequestContext(
+    const CefRequestContextSettings& settings) {
+  // Create and initialize the global context immediately.
+  Config config;
+  config.is_global = true;
+  config.settings = settings;
+  CefRefPtr<CefRequestContextImpl> impl = new CefRequestContextImpl(config);
+  impl->Initialize();
+  return impl;
+}
+
+// static
+CefRefPtr<CefRequestContextImpl>
+CefRequestContextImpl::GetOrCreateForRequestContext(
+    CefRefPtr<CefRequestContext> request_context) {
+  if (request_context.get()) {
+    // Use the context from the provided CefRequestContext.
+    return static_cast<CefRequestContextImpl*>(request_context.get());
+  }
+
+  // Use the global context.
+  Config config;
+  config.is_global = true;
+  return CefRequestContextImpl::GetOrCreateRequestContext(config);
+}
+
+CefBrowserContext* CefRequestContextImpl::GetBrowserContext() {
+  EnsureBrowserContext();
+  return browser_context();
+}
+
+void CefRequestContextImpl::GetBrowserContext(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    const BrowserContextCallback& callback) {
+  if (!task_runner.get())
+    task_runner = CefTaskRunnerImpl::GetCurrentTaskRunner();
+  GetBrowserContextOnUIThread(task_runner, callback);
+}
+
+bool CefRequestContextImpl::IsSame(CefRefPtr<CefRequestContext> other) {
+  CefRequestContextImpl* other_impl =
+      static_cast<CefRequestContextImpl*>(other.get());
+  if (!other_impl)
+    return false;
+
+  // Compare whether both are the global context.
+  if (config_.is_global && other_impl->config_.is_global)
+    return true;
+
+  // Compare CefBrowserContext pointers if one has been associated.
+  if (browser_context() && other_impl->browser_context()) {
+    return (browser_context() == other_impl->browser_context());
+  } else if (browser_context() || other_impl->browser_context()) {
+    return false;
+  }
+
+  // Otherwise compare unique IDs.
+  return (config_.unique_id == other_impl->config_.unique_id);
+}
+
+bool CefRequestContextImpl::IsSharingWith(CefRefPtr<CefRequestContext> other) {
+  CefRequestContextImpl* other_impl =
+      static_cast<CefRequestContextImpl*>(other.get());
+  if (!other_impl)
+    return false;
+
+  if (IsSame(other))
+    return true;
+
+  CefRefPtr<CefRequestContext> pending_other = config_.other;
+  if (pending_other.get()) {
+    // This object is not initialized but we know what context this object will
+    // share with. Compare to that other context instead.
+    return pending_other->IsSharingWith(other);
+  }
+
+  pending_other = other_impl->config_.other;
+  if (pending_other.get()) {
+    // The other object is not initialized but we know what context that object
+    // will share with. Compare to that other context instead.
+    return pending_other->IsSharingWith(this);
+  }
+
+  // This or the other object is not initialized. Compare the cache path values.
+  // If both are non-empty and the same then they'll share the same storage.
+  if (config_.settings.cache_path.length > 0 &&
+      other_impl->config_.settings.cache_path.length > 0) {
+    return (
+        base::FilePath(CefString(&config_.settings.cache_path)) ==
+        base::FilePath(CefString(&other_impl->config_.settings.cache_path)));
+  }
+
+  return false;
+}
+
+bool CefRequestContextImpl::IsGlobal() {
+  return config_.is_global;
+}
+
+CefRefPtr<CefRequestContextHandler> CefRequestContextImpl::GetHandler() {
+  return config_.handler;
+}
+
+CefString CefRequestContextImpl::GetCachePath() {
+  return CefString(&config_.settings.cache_path);
+}
+
+CefRefPtr<CefCookieManager> CefRequestContextImpl::GetCookieManager(
+    CefRefPtr<CefCompletionCallback> callback) {
+  CefRefPtr<CefCookieManagerImpl> cookie_manager = new CefCookieManagerImpl();
+  InitializeCookieManagerOnUIThread(cookie_manager, callback);
+  return cookie_manager.get();
+}
+
+bool CefRequestContextImpl::RegisterSchemeHandlerFactory(
+    const CefString& scheme_name,
+    const CefString& domain_name,
+    CefRefPtr<CefSchemeHandlerFactory> factory) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(
+                      base::IgnoreResult(
+                          &CefRequestContextImpl::RegisterSchemeHandlerFactory),
+                      this, scheme_name, domain_name, factory));
+    return true;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  browser_context()->RegisterSchemeHandlerFactory(scheme_name, domain_name,
+                                                  factory);
+  return true;
+}
+
+bool CefRequestContextImpl::ClearSchemeHandlerFactories() {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::BindOnce(base::IgnoreResult(
+                           &CefRequestContextImpl::ClearSchemeHandlerFactories),
+                       this));
+    return true;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  browser_context()->ClearSchemeHandlerFactories();
+  return true;
+}
+
+void CefRequestContextImpl::PurgePluginListCache(bool reload_pages) {
+  GetBrowserContext(
+      base::CreateSingleThreadTaskRunner({BrowserThread::UI}),
+      base::Bind(&CefRequestContextImpl::PurgePluginListCacheInternal, this,
+                 reload_pages));
+}
+
+bool CefRequestContextImpl::HasPreference(const CefString& name) {
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  PrefService* pref_service = browser_context()->GetPrefs();
+  return (pref_service->FindPreference(name) != nullptr);
+}
+
+CefRefPtr<CefValue> CefRequestContextImpl::GetPreference(
+    const CefString& name) {
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  PrefService* pref_service = browser_context()->GetPrefs();
+  const PrefService::Preference* pref = pref_service->FindPreference(name);
+  if (!pref)
+    return nullptr;
+  return new CefValueImpl(pref->GetValue()->DeepCopy());
+}
+
+CefRefPtr<CefDictionaryValue> CefRequestContextImpl::GetAllPreferences(
+    bool include_defaults) {
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  PrefService* pref_service = browser_context()->GetPrefs();
+
+  std::unique_ptr<base::DictionaryValue> values =
+      pref_service->GetPreferenceValues(include_defaults
+                                            ? PrefService::INCLUDE_DEFAULTS
+                                            : PrefService::EXCLUDE_DEFAULTS);
+
+  // CefDictionaryValueImpl takes ownership of |values|.
+  return new CefDictionaryValueImpl(values.release(), true, false);
+}
+
+bool CefRequestContextImpl::CanSetPreference(const CefString& name) {
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  PrefService* pref_service = browser_context()->GetPrefs();
+  const PrefService::Preference* pref = pref_service->FindPreference(name);
+  return (pref && pref->IsUserModifiable());
+}
+
+bool CefRequestContextImpl::SetPreference(const CefString& name,
+                                          CefRefPtr<CefValue> value,
+                                          CefString& error) {
+  // Verify that this method is being called on the UI thread.
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  PrefService* pref_service = browser_context()->GetPrefs();
+
+  // The below validation logic should match PrefService::SetUserPrefValue.
+
+  const PrefService::Preference* pref = pref_service->FindPreference(name);
+  if (!pref) {
+    error = "Trying to modify an unregistered preference";
+    return false;
+  }
+
+  if (!pref->IsUserModifiable()) {
+    error = "Trying to modify a preference that is not user modifiable";
+    return false;
+  }
+
+  if (!value.get()) {
+    // Reset the preference to its default value.
+    pref_service->ClearPref(name);
+    return true;
+  }
+
+  if (!value->IsValid()) {
+    error = "A valid value is required";
+    return false;
+  }
+
+  CefValueImpl* impl = static_cast<CefValueImpl*>(value.get());
+
+  CefValueImpl::ScopedLockedValue scoped_locked_value(impl);
+  base::Value* impl_value = impl->GetValueUnsafe();
+
+  if (pref->GetType() != impl_value->type()) {
+    error = base::StringPrintf(
+        "Trying to set a preference of type %s to value of type %s",
+        GetTypeString(pref->GetType()), GetTypeString(impl_value->type()));
+    return false;
+  }
+
+  // PrefService will make a DeepCopy of |impl_value|.
+  pref_service->Set(name, *impl_value);
+  return true;
+}
+
+void CefRequestContextImpl::ClearCertificateExceptions(
+    CefRefPtr<CefCompletionCallback> callback) {
+  GetBrowserContext(
+      base::CreateSingleThreadTaskRunner({BrowserThread::UI}),
+      base::Bind(&CefRequestContextImpl::ClearCertificateExceptionsInternal,
+                 this, callback));
+}
+
+void CefRequestContextImpl::ClearHttpAuthCredentials(
+    CefRefPtr<CefCompletionCallback> callback) {
+  GetBrowserContext(
+      base::CreateSingleThreadTaskRunner({BrowserThread::UI}),
+      base::Bind(&CefRequestContextImpl::ClearHttpAuthCredentialsInternal, this,
+                 callback));
+}
+
+void CefRequestContextImpl::CloseAllConnections(
+    CefRefPtr<CefCompletionCallback> callback) {
+  GetBrowserContext(
+      base::CreateSingleThreadTaskRunner({BrowserThread::UI}),
+      base::Bind(&CefRequestContextImpl::CloseAllConnectionsInternal, this,
+                 callback));
+}
+
+void CefRequestContextImpl::ResolveHost(
+    const CefString& origin,
+    CefRefPtr<CefResolveCallback> callback) {
+  GetBrowserContext(base::CreateSingleThreadTaskRunner({BrowserThread::UI}),
+                    base::Bind(&CefRequestContextImpl::ResolveHostInternal,
+                               this, origin, callback));
+}
+
+void CefRequestContextImpl::LoadExtension(
+    const CefString& root_directory,
+    CefRefPtr<CefDictionaryValue> manifest,
+    CefRefPtr<CefExtensionHandler> handler) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefRequestContextImpl::LoadExtension, this,
+                                 root_directory, manifest, handler));
+    return;
+  }
+
+  if (!extensions::ExtensionsEnabled()) {
+    if (handler)
+      handler->OnExtensionLoadFailed(ERR_ABORTED);
+    return;
+  }
+
+  if (manifest && manifest->GetSize() > 0) {
+    CefDictionaryValueImpl* value_impl =
+        static_cast<CefDictionaryValueImpl*>(manifest.get());
+    GetBrowserContext()->extension_system()->LoadExtension(
+        base::WrapUnique(value_impl->CopyValue()), root_directory,
+        false /* builtin */, this, handler);
+  } else {
+    GetBrowserContext()->extension_system()->LoadExtension(
+        root_directory, false /* builtin */, this, handler);
+  }
+}
+
+bool CefRequestContextImpl::DidLoadExtension(const CefString& extension_id) {
+  CefRefPtr<CefExtension> extension = GetExtension(extension_id);
+  // GetLoaderContext() will return NULL for internal extensions.
+  return extension && IsSame(extension->GetLoaderContext());
+}
+
+bool CefRequestContextImpl::HasExtension(const CefString& extension_id) {
+  return !!GetExtension(extension_id);
+}
+
+bool CefRequestContextImpl::GetExtensions(
+    std::vector<CefString>& extension_ids) {
+  extension_ids.clear();
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  if (!extensions::ExtensionsEnabled())
+    return false;
+
+  extensions::CefExtensionSystem::ExtensionMap extension_map =
+      GetBrowserContext()->extension_system()->GetExtensions();
+  extensions::CefExtensionSystem::ExtensionMap::const_iterator it =
+      extension_map.begin();
+  for (; it != extension_map.end(); ++it)
+    extension_ids.push_back(it->second->GetIdentifier());
+
+  return true;
+}
+
+CefRefPtr<CefExtension> CefRequestContextImpl::GetExtension(
+    const CefString& extension_id) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  if (!extensions::ExtensionsEnabled())
+    return nullptr;
+
+  return GetBrowserContext()->extension_system()->GetExtension(extension_id);
+}
+
+CefRefPtr<CefMediaRouter> CefRequestContextImpl::GetMediaRouter() {
+  CefRefPtr<CefMediaRouterImpl> media_router = new CefMediaRouterImpl();
+  InitializeMediaRouterOnUIThread(media_router);
+  return media_router.get();
+}
+
+void CefRequestContextImpl::OnRenderFrameCreated(int render_process_id,
+                                                 int render_frame_id,
+                                                 int frame_tree_node_id,
+                                                 bool is_main_frame,
+                                                 bool is_guest_view) {
+  browser_context_->OnRenderFrameCreated(this, render_process_id,
+                                         render_frame_id, frame_tree_node_id,
+                                         is_main_frame, is_guest_view);
+}
+
+void CefRequestContextImpl::OnRenderFrameDeleted(int render_process_id,
+                                                 int render_frame_id,
+                                                 int frame_tree_node_id,
+                                                 bool is_main_frame,
+                                                 bool is_guest_view) {
+  browser_context_->OnRenderFrameDeleted(this, render_process_id,
+                                         render_frame_id, frame_tree_node_id,
+                                         is_main_frame, is_guest_view);
+}
+
+// static
+CefRefPtr<CefRequestContextImpl>
+CefRequestContextImpl::GetOrCreateRequestContext(const Config& config) {
+  if (config.is_global ||
+      (config.other && config.other->IsGlobal() && !config.handler)) {
+    // Return the singleton global context.
+    return CefContentBrowserClient::Get()->request_context();
+  }
+
+  // The new context will be initialized later by EnsureBrowserContext().
+  return new CefRequestContextImpl(config);
+}
+
+CefRequestContextImpl::CefRequestContextImpl(
+    const CefRequestContextImpl::Config& config)
+    : config_(config) {}
+
+void CefRequestContextImpl::Initialize() {
+  CEF_REQUIRE_UIT();
+
+  DCHECK(!browser_context_);
+
+  if (config_.other) {
+    // Share storage with |config_.other|.
+    browser_context_ =
+        CefBrowserContext::GetForContext(config_.other->GetBrowserContext());
+    DCHECK(browser_context_);
+  }
+
+  if (!browser_context_) {
+    if (!config_.is_global) {
+      // User-specified settings need to be normalized.
+      CefContext::Get()->NormalizeRequestContextSettings(&config_.settings);
+    }
+
+    const base::FilePath& cache_path =
+        base::FilePath(CefString(&config_.settings.cache_path));
+    if (!cache_path.empty()) {
+      // Check if a CefBrowserContext is already globally registered for
+      // the specified cache path. If so then use it.
+      browser_context_ = CefBrowserContext::GetForCachePath(cache_path);
+    }
+  }
+
+  if (!browser_context_) {
+    // Create a new CefBrowserContext instance. If the cache path is non-
+    // empty then this new instance will become the globally registered
+    // CefBrowserContext for that path. Otherwise, this new instance will
+    // be a completely isolated "incognito mode" context.
+    browser_context_ = new CefBrowserContext(config_.settings);
+    browser_context_->Initialize();
+  } else {
+    // Share the same settings as the existing context.
+    config_.settings = browser_context_->GetSettings();
+  }
+
+  // We'll disassociate from |browser_context_| on destruction.
+  browser_context_->AddCefRequestContext(this);
+
+  if (config_.other) {
+    // Clear the reference to |config_.other| after setting
+    // |request_context_getter_|. This is the reverse order of checks in
+    // IsSharedWith().
+    config_.other = nullptr;
+  }
+
+  if (config_.handler)
+    config_.handler->OnRequestContextInitialized(this);
+}
+
+void CefRequestContextImpl::EnsureBrowserContext() {
+  CEF_REQUIRE_UIT();
+  if (!browser_context())
+    Initialize();
+  DCHECK(browser_context());
+}
+
+void CefRequestContextImpl::GetBrowserContextOnUIThread(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    const BrowserContextCallback& callback) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT, base::Bind(&CefRequestContextImpl::GetBrowserContextOnUIThread,
+                            this, task_runner, callback));
+    return;
+  }
+
+  // Make sure the browser context exists.
+  EnsureBrowserContext();
+
+  if (task_runner->BelongsToCurrentThread()) {
+    // Execute the callback immediately.
+    callback.Run(browser_context());
+  } else {
+    // Execute the callback on the target thread.
+    task_runner->PostTask(FROM_HERE, base::Bind(callback, browser_context()));
+  }
+}
+
+void CefRequestContextImpl::PurgePluginListCacheInternal(
+    bool reload_pages,
+    CefBrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+  browser_context->ClearPluginLoadDecision(-1);
+  content::PluginService::GetInstance()->PurgePluginListCache(browser_context,
+                                                              false);
+}
+
+void CefRequestContextImpl::ClearCertificateExceptionsInternal(
+    CefRefPtr<CefCompletionCallback> callback,
+    CefBrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+
+  content::SSLHostStateDelegate* ssl_delegate =
+      browser_context->GetSSLHostStateDelegate();
+  if (ssl_delegate)
+    ssl_delegate->Clear(base::Callback<bool(const std::string&)>());
+
+  if (callback) {
+    CEF_POST_TASK(CEF_UIT,
+                  base::Bind(&CefCompletionCallback::OnComplete, callback));
+  }
+}
+
+void CefRequestContextImpl::ClearHttpAuthCredentialsInternal(
+    CefRefPtr<CefCompletionCallback> callback,
+    CefBrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+
+  browser_context->GetNetworkContext()->ClearHttpAuthCache(
+      base::Time(), base::Bind(&CefCompletionCallback::OnComplete, callback));
+}
+
+void CefRequestContextImpl::CloseAllConnectionsInternal(
+    CefRefPtr<CefCompletionCallback> callback,
+    CefBrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+
+  browser_context->GetNetworkContext()->CloseAllConnections(
+      base::Bind(&CefCompletionCallback::OnComplete, callback));
+}
+
+void CefRequestContextImpl::ResolveHostInternal(
+    const CefString& origin,
+    CefRefPtr<CefResolveCallback> callback,
+    CefBrowserContext* browser_context) {
+  CEF_REQUIRE_UIT();
+
+  // |helper| will be deleted in ResolveHostHelper::OnComplete().
+  ResolveHostHelper* helper = new ResolveHostHelper(callback);
+  helper->Start(browser_context, origin);
+}
+
+void CefRequestContextImpl::InitializeCookieManagerOnUIThread(
+    CefRefPtr<CefCookieManagerImpl> cookie_manager,
+    CefRefPtr<CefCompletionCallback> callback) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(&CefRequestContextImpl::InitializeCookieManagerOnUIThread,
+                   this, cookie_manager, callback));
+    return;
+  }
+
+  auto browser_context = GetBrowserContext();
+  cookie_manager->Initialize(browser_context->getter(), callback);
+}
+
+void CefRequestContextImpl::InitializeMediaRouterOnUIThread(
+    CefRefPtr<CefMediaRouterImpl> media_router) {
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(&CefRequestContextImpl::InitializeMediaRouterOnUIThread,
+                   this, media_router));
+    return;
+  }
+
+  auto browser_context = GetBrowserContext();
+  media_router->Initialize(browser_context->getter());
+}
+
+CefBrowserContext* CefRequestContextImpl::browser_context() const {
+  return browser_context_;
+}
diff --git a/src/libcef/browser/request_context_impl.h b/src/libcef/browser/request_context_impl.h
new file mode 100644
index 0000000..a802b26
--- /dev/null
+++ b/src/libcef/browser/request_context_impl.h
@@ -0,0 +1,170 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_REQUEST_CONTEXT_IMPL_H_
+#define CEF_LIBCEF_REQUEST_CONTEXT_IMPL_H_
+#pragma once
+
+#include "include/cef_request_context.h"
+#include "libcef/browser/browser_context.h"
+#include "libcef/browser/media_router/media_router_impl.h"
+#include "libcef/browser/net_service/cookie_manager_impl.h"
+#include "libcef/browser/thread_util.h"
+
+class CefBrowserContext;
+
+// Implementation of the CefRequestContext interface. All methods are thread-
+// safe unless otherwise indicated. Will be deleted on the UI thread.
+class CefRequestContextImpl : public CefRequestContext {
+ public:
+  ~CefRequestContextImpl() override;
+
+  // Creates the singleton global RequestContext. Called from
+  // CefBrowserMainParts::PreMainMessageLoopRun.
+  static CefRefPtr<CefRequestContextImpl> CreateGlobalRequestContext(
+      const CefRequestContextSettings& settings);
+
+  // Returns a CefRequestContextImpl for the specified |request_context|.
+  // Will return the global context if |request_context| is NULL.
+  static CefRefPtr<CefRequestContextImpl> GetOrCreateForRequestContext(
+      CefRefPtr<CefRequestContext> request_context);
+
+  // Returns the browser context object. Can only be called on the UI thread.
+  CefBrowserContext* GetBrowserContext();
+
+  // Executes |callback| either synchronously or asynchronously with the browser
+  // context object when it's available. If |task_runner| is NULL the callback
+  // will be executed on the originating thread. The resulting context object
+  // can only be accessed on the UI thread.
+  typedef base::Callback<void(CefBrowserContext*)> BrowserContextCallback;
+  void GetBrowserContext(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      const BrowserContextCallback& callback);
+
+  bool IsSame(CefRefPtr<CefRequestContext> other) override;
+  bool IsSharingWith(CefRefPtr<CefRequestContext> other) override;
+  bool IsGlobal() override;
+  CefRefPtr<CefRequestContextHandler> GetHandler() override;
+  CefString GetCachePath() override;
+  CefRefPtr<CefCookieManager> GetCookieManager(
+      CefRefPtr<CefCompletionCallback> callback) override;
+  bool RegisterSchemeHandlerFactory(
+      const CefString& scheme_name,
+      const CefString& domain_name,
+      CefRefPtr<CefSchemeHandlerFactory> factory) override;
+  bool ClearSchemeHandlerFactories() override;
+  void PurgePluginListCache(bool reload_pages) override;
+  bool HasPreference(const CefString& name) override;
+  CefRefPtr<CefValue> GetPreference(const CefString& name) override;
+  CefRefPtr<CefDictionaryValue> GetAllPreferences(
+      bool include_defaults) override;
+  bool CanSetPreference(const CefString& name) override;
+  bool SetPreference(const CefString& name,
+                     CefRefPtr<CefValue> value,
+                     CefString& error) override;
+  void ClearCertificateExceptions(
+      CefRefPtr<CefCompletionCallback> callback) override;
+  void ClearHttpAuthCredentials(
+      CefRefPtr<CefCompletionCallback> callback) override;
+  void CloseAllConnections(CefRefPtr<CefCompletionCallback> callback) override;
+  void ResolveHost(const CefString& origin,
+                   CefRefPtr<CefResolveCallback> callback) override;
+  void LoadExtension(const CefString& root_directory,
+                     CefRefPtr<CefDictionaryValue> manifest,
+                     CefRefPtr<CefExtensionHandler> handler) override;
+  bool DidLoadExtension(const CefString& extension_id) override;
+  bool HasExtension(const CefString& extension_id) override;
+  bool GetExtensions(std::vector<CefString>& extension_ids) override;
+  CefRefPtr<CefExtension> GetExtension(const CefString& extension_id) override;
+  CefRefPtr<CefMediaRouter> GetMediaRouter() override;
+
+  const CefRequestContextSettings& settings() const { return config_.settings; }
+
+  // Called from CefBrowserHostImpl::RenderFrameCreated or
+  // CefMimeHandlerViewGuestDelegate::OnGuestAttached when a render frame is
+  // created.
+  void OnRenderFrameCreated(int render_process_id,
+                            int render_frame_id,
+                            int frame_tree_node_id,
+                            bool is_main_frame,
+                            bool is_guest_view);
+
+  // Called from CefBrowserHostImpl::FrameDeleted or
+  // CefMimeHandlerViewGuestDelegate::OnGuestDetached when a render frame is
+  // deleted.
+  void OnRenderFrameDeleted(int render_process_id,
+                            int render_frame_id,
+                            int frame_tree_node_id,
+                            bool is_main_frame,
+                            bool is_guest_view);
+
+ private:
+  friend class CefRequestContext;
+
+  struct Config {
+    // True if wrapping the global context.
+    bool is_global = false;
+
+    // |settings| or |other| will be set when creating a new CefRequestContext
+    // via the API. When wrapping an existing CefBrowserContext* both will be
+    // empty and Initialize(CefBrowserContext*) will be called immediately after
+    // CefRequestContextImpl construction.
+    CefRequestContextSettings settings;
+    CefRefPtr<CefRequestContextImpl> other;
+
+    // Optionally use this handler.
+    CefRefPtr<CefRequestContextHandler> handler;
+
+    // Used to uniquely identify CefRequestContext objects before an associated
+    // CefBrowserContext has been created. Should be set when a new
+    // CefRequestContext via the API.
+    int unique_id = -1;
+  };
+
+  static CefRefPtr<CefRequestContextImpl> GetOrCreateRequestContext(
+      const Config& config);
+
+  explicit CefRequestContextImpl(const Config& config);
+
+  void Initialize();
+
+  // Make sure the browser context exists. Only called on the UI thread.
+  void EnsureBrowserContext();
+
+  void GetBrowserContextOnUIThread(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      const BrowserContextCallback& callback);
+
+  void PurgePluginListCacheInternal(bool reload_pages,
+                                    CefBrowserContext* browser_context);
+  void ClearCertificateExceptionsInternal(
+      CefRefPtr<CefCompletionCallback> callback,
+      CefBrowserContext* browser_context);
+  void ClearHttpAuthCredentialsInternal(
+      CefRefPtr<CefCompletionCallback> callback,
+      CefBrowserContext* browser_context);
+  void CloseAllConnectionsInternal(CefRefPtr<CefCompletionCallback> callback,
+                                   CefBrowserContext* browser_context);
+  void ResolveHostInternal(const CefString& origin,
+                           CefRefPtr<CefResolveCallback> callback,
+                           CefBrowserContext* browser_context);
+
+  void InitializeCookieManagerOnUIThread(
+      CefRefPtr<CefCookieManagerImpl> cookie_manager,
+      CefRefPtr<CefCompletionCallback> callback);
+  void InitializeMediaRouterOnUIThread(
+      CefRefPtr<CefMediaRouterImpl> media_router);
+
+  CefBrowserContext* browser_context() const;
+
+  // We must disassociate from this on destruction.
+  CefBrowserContext* browser_context_ = nullptr;
+
+  Config config_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefRequestContextImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefRequestContextImpl);
+};
+
+#endif  // CEF_LIBCEF_REQUEST_CONTEXT_IMPL_H_
diff --git a/src/libcef/browser/resource_context.cc b/src/libcef/browser/resource_context.cc
new file mode 100644
index 0000000..8d1b7c6
--- /dev/null
+++ b/src/libcef/browser/resource_context.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/resource_context.h"
+
+#include "libcef/browser/net/scheme_handler.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/net/scheme_registration.h"
+
+#include "base/i18n/case_conversion.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/resource_context_impl.h"
+#include "content/public/browser/browser_thread.h"
+
+CefResourceContext::CefResourceContext(bool is_off_the_record)
+    : is_off_the_record_(is_off_the_record) {
+  // Using base::Unretained() is safe because both this callback and possible
+  // deletion of |this| will execute on the IO thread, and this callback will
+  // be executed first.
+  CEF_POST_TASK(CEF_IOT, base::Bind(&CefResourceContext::InitOnIOThread,
+                                    base::Unretained(this)));
+}
+
+CefResourceContext::~CefResourceContext() {}
+
+void CefResourceContext::AddHandler(
+    int render_process_id,
+    int render_frame_id,
+    int frame_tree_node_id,
+    CefRefPtr<CefRequestContextHandler> handler) {
+  CEF_REQUIRE_IOT();
+  handler_map_.AddHandler(render_process_id, render_frame_id,
+                          frame_tree_node_id, handler);
+}
+
+void CefResourceContext::RemoveHandler(int render_process_id,
+                                       int render_frame_id,
+                                       int frame_tree_node_id) {
+  CEF_REQUIRE_IOT();
+  handler_map_.RemoveHandler(render_process_id, render_frame_id,
+                             frame_tree_node_id);
+}
+
+CefRefPtr<CefRequestContextHandler> CefResourceContext::GetHandler(
+    int render_process_id,
+    int render_frame_id,
+    int frame_tree_node_id,
+    bool require_frame_match) const {
+  CEF_REQUIRE_IOT();
+  return handler_map_.GetHandler(render_process_id, render_frame_id,
+                                 frame_tree_node_id, require_frame_match);
+}
+
+void CefResourceContext::RegisterSchemeHandlerFactory(
+    const std::string& scheme_name,
+    const std::string& domain_name,
+    CefRefPtr<CefSchemeHandlerFactory> factory) {
+  CEF_REQUIRE_IOT();
+
+  const std::string& scheme_lower = base::ToLowerASCII(scheme_name);
+  std::string domain_lower;
+
+  // Hostname is only supported for standard schemes.
+  if (scheme::IsStandardScheme(scheme_lower)) {
+    // Hostname might contain Unicode characters.
+    domain_lower =
+        base::UTF16ToUTF8(base::i18n::ToLower(base::UTF8ToUTF16(domain_name)));
+  }
+
+  const auto key = std::make_pair(scheme_lower, domain_lower);
+
+  if (factory) {
+    // Add or replace the factory.
+    scheme_handler_factory_map_[key] = factory;
+  } else {
+    // Remove the existing factory, if any.
+    auto it = scheme_handler_factory_map_.find(key);
+    if (it != scheme_handler_factory_map_.end())
+      scheme_handler_factory_map_.erase(it);
+  }
+}
+
+void CefResourceContext::ClearSchemeHandlerFactories() {
+  CEF_REQUIRE_IOT();
+
+  scheme_handler_factory_map_.clear();
+
+  // Restore the default internal handlers.
+  scheme::RegisterInternalHandlers(this);
+}
+
+CefRefPtr<CefSchemeHandlerFactory> CefResourceContext::GetSchemeHandlerFactory(
+    const GURL& url) {
+  CEF_REQUIRE_IOT();
+
+  if (scheme_handler_factory_map_.empty())
+    return nullptr;
+
+  const std::string& scheme_lower = url.scheme();
+  const std::string& domain_lower =
+      url.IsStandard() ? url.host() : std::string();
+
+  if (!domain_lower.empty()) {
+    // Sanity check.
+    DCHECK(scheme::IsStandardScheme(scheme_lower)) << scheme_lower;
+
+    // Try for a match with hostname first.
+    const auto it = scheme_handler_factory_map_.find(
+        std::make_pair(scheme_lower, domain_lower));
+    if (it != scheme_handler_factory_map_.end())
+      return it->second;
+  }
+
+  // Try for a match with no specified hostname.
+  const auto it = scheme_handler_factory_map_.find(
+      std::make_pair(scheme_lower, std::string()));
+  if (it != scheme_handler_factory_map_.end())
+    return it->second;
+
+  return nullptr;
+}
+
+void CefResourceContext::InitOnIOThread() {
+  CEF_REQUIRE_IOT();
+
+  // Add the default internal handlers.
+  scheme::RegisterInternalHandlers(this);
+}
diff --git a/src/libcef/browser/resource_context.h b/src/libcef/browser/resource_context.h
new file mode 100644
index 0000000..e00e3b2
--- /dev/null
+++ b/src/libcef/browser/resource_context.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_RESOURCE_CONTEXT_H_
+#define CEF_LIBCEF_BROWSER_RESOURCE_CONTEXT_H_
+#pragma once
+
+#include "include/cef_request_context.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+
+#include "libcef/browser/request_context_handler_map.h"
+
+#include "content/public/browser/resource_context.h"
+
+class GURL;
+
+// Acts as a bridge for resource loading. Life span is controlled by
+// CefBrowserContext. Created on the UI thread but accessed and destroyed on the
+// IO thread. Network request objects are associated with the ResourceContext
+// via ProxyURLLoaderFactory. When the ResourceContext is destroyed all
+// outstanding network request objects will be canceled.
+// See browser_context.h for an object relationship diagram.
+class CefResourceContext : public content::ResourceContext {
+ public:
+  explicit CefResourceContext(bool is_off_the_record);
+  ~CefResourceContext() override;
+
+  // See comments in CefRequestContextHandlerMap.
+  void AddHandler(int render_process_id,
+                  int render_frame_id,
+                  int frame_tree_node_id,
+                  CefRefPtr<CefRequestContextHandler> handler);
+  void RemoveHandler(int render_process_id,
+                     int render_frame_id,
+                     int frame_tree_node_id);
+  CefRefPtr<CefRequestContextHandler> GetHandler(
+      int render_process_id,
+      int render_frame_id,
+      int frame_tree_node_id,
+      bool require_frame_match) const;
+
+  // Manage scheme handler factories associated with this context.
+  void RegisterSchemeHandlerFactory(const std::string& scheme_name,
+                                    const std::string& domain_name,
+                                    CefRefPtr<CefSchemeHandlerFactory> factory);
+  void ClearSchemeHandlerFactories();
+  CefRefPtr<CefSchemeHandlerFactory> GetSchemeHandlerFactory(const GURL& url);
+
+  // State transferred from the BrowserContext for use on the IO thread.
+  bool IsOffTheRecord() const { return is_off_the_record_; }
+
+ private:
+  void InitOnIOThread();
+
+  // Only accessed on the IO thread.
+  const bool is_off_the_record_;
+
+  // Map IDs to CefRequestContextHandler objects.
+  CefRequestContextHandlerMap handler_map_;
+
+  // Map (scheme, domain) to factories.
+  typedef std::map<std::pair<std::string, std::string>,
+                   CefRefPtr<CefSchemeHandlerFactory>>
+      SchemeHandlerFactoryMap;
+  SchemeHandlerFactoryMap scheme_handler_factory_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefResourceContext);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_RESOURCE_CONTEXT_H_
diff --git a/src/libcef/browser/scheme_impl.cc b/src/libcef/browser/scheme_impl.cc
new file mode 100644
index 0000000..f5423c0
--- /dev/null
+++ b/src/libcef/browser/scheme_impl.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_scheme.h"
+#include "libcef/browser/context.h"
+
+#include "base/logging.h"
+
+bool CefRegisterSchemeHandlerFactory(
+    const CefString& scheme_name,
+    const CefString& domain_name,
+    CefRefPtr<CefSchemeHandlerFactory> factory) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return false;
+  }
+
+  return CefRequestContext::GetGlobalContext()->RegisterSchemeHandlerFactory(
+      scheme_name, domain_name, factory);
+}
+
+bool CefClearSchemeHandlerFactories() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return false;
+  }
+
+  return CefRequestContext::GetGlobalContext()->ClearSchemeHandlerFactories();
+}
diff --git a/src/libcef/browser/server_impl.cc b/src/libcef/browser/server_impl.cc
new file mode 100644
index 0000000..38c226c
--- /dev/null
+++ b/src/libcef/browser/server_impl.cc
@@ -0,0 +1,660 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/server_impl.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/task_runner_impl.h"
+
+#include "base/bind.h"
+#include "base/format_macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "net/server/http_server_request_info.h"
+#include "net/server/http_server_response_info.h"
+#include "net/socket/server_socket.h"
+#include "net/socket/tcp_server_socket.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+
+#define CEF_CURRENTLY_ON_HT() CurrentlyOnHandlerThread()
+#define CEF_REQUIRE_HT() DCHECK(CEF_CURRENTLY_ON_HT())
+
+#define CEF_REQUIRE_HT_RETURN(var)              \
+  if (!CEF_CURRENTLY_ON_HT()) {                 \
+    NOTREACHED() << "called on invalid thread"; \
+    return var;                                 \
+  }
+
+#define CEF_REQUIRE_HT_RETURN_VOID()            \
+  if (!CEF_CURRENTLY_ON_HT()) {                 \
+    NOTREACHED() << "called on invalid thread"; \
+    return;                                     \
+  }
+
+#define CEF_POST_TASK_HT(task) task_runner_->PostTask(FROM_HERE, task);
+
+namespace {
+
+const char kReferrerLowerCase[] = "referer";
+
+// Wrap a string in a unique_ptr to avoid extra copies.
+std::unique_ptr<std::string> CreateUniqueString(const void* data,
+                                                size_t data_size) {
+  std::unique_ptr<std::string> ptr;
+  if (data && data_size > 0) {
+    ptr.reset(new std::string(static_cast<const char*>(data), data_size));
+  } else {
+    ptr.reset(new std::string());
+  }
+  return ptr;
+}
+
+CefRefPtr<CefRequest> CreateRequest(const std::string& address,
+                                    const net::HttpServerRequestInfo& info,
+                                    bool is_websocket) {
+  DCHECK(!address.empty());
+  DCHECK(!info.method.empty());
+  DCHECK(!info.path.empty());
+
+  CefRefPtr<CefPostData> post_data;
+  if (!info.data.empty()) {
+    post_data = CefPostData::Create();
+    CefRefPtr<CefPostDataElement> post_element = CefPostDataElement::Create();
+    post_element->SetToBytes(info.data.size(), info.data.data());
+    post_data->AddElement(post_element);
+  }
+
+  std::string referer;
+
+  CefRequest::HeaderMap header_map;
+  if (!info.headers.empty()) {
+    net::HttpServerRequestInfo::HeadersMap::const_iterator it =
+        info.headers.begin();
+    for (; it != info.headers.end(); ++it) {
+      // Don't include Referer in the header map.
+      if (base::LowerCaseEqualsASCII(it->first, kReferrerLowerCase)) {
+        referer = it->second;
+      } else {
+        header_map.insert(std::make_pair(it->first, it->second));
+      }
+    }
+  }
+
+  CefRefPtr<CefRequestImpl> request = new CefRequestImpl();
+  request->Set((is_websocket ? "ws://" : "http://") + address + info.path,
+               info.method, post_data, header_map);
+  if (!referer.empty())
+    request->SetReferrer(referer, REFERRER_POLICY_DEFAULT);
+  request->SetReadOnly(true);
+  return request;
+}
+
+// Callback implementation for WebSocket acceptance. Always executes on the UI
+// thread so we can avoid multiple execution by clearing the |impl_| reference.
+class AcceptWebSocketCallback : public CefCallback {
+ public:
+  AcceptWebSocketCallback(CefRefPtr<CefServerImpl> impl,
+                          int connection_id,
+                          net::HttpServerRequestInfo request_info)
+      : impl_(impl),
+        connection_id_(connection_id),
+        request_info_(request_info) {}
+
+  ~AcceptWebSocketCallback() override {
+    if (impl_)
+      impl_->ContinueWebSocketRequest(connection_id_, request_info_, false);
+  }
+
+  void Continue() override {
+    if (!CEF_CURRENTLY_ON_UIT()) {
+      CEF_POST_TASK(CEF_UIT,
+                    base::BindOnce(&AcceptWebSocketCallback::Continue, this));
+      return;
+    }
+    if (!impl_)
+      return;
+    impl_->ContinueWebSocketRequest(connection_id_, request_info_, true);
+    impl_ = nullptr;
+  }
+
+  void Cancel() override {
+    if (!CEF_CURRENTLY_ON_UIT()) {
+      CEF_POST_TASK(CEF_UIT,
+                    base::BindOnce(&AcceptWebSocketCallback::Cancel, this));
+      return;
+    }
+    if (!impl_)
+      return;
+    impl_->ContinueWebSocketRequest(connection_id_, request_info_, false);
+    impl_ = nullptr;
+  }
+
+ private:
+  CefRefPtr<CefServerImpl> impl_;
+  int connection_id_;
+  net::HttpServerRequestInfo request_info_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(AcceptWebSocketCallback);
+  DISALLOW_COPY_AND_ASSIGN(AcceptWebSocketCallback);
+};
+
+}  // namespace
+
+// CefServer
+
+// static
+void CefServer::CreateServer(const CefString& address,
+                             uint16 port,
+                             int backlog,
+                             CefRefPtr<CefServerHandler> handler) {
+  CefRefPtr<CefServerImpl> server(new CefServerImpl(handler));
+  server->Start(address, port, backlog);
+}
+
+// CefServerImpl
+
+struct CefServerImpl::ConnectionInfo {
+  ConnectionInfo() : is_websocket(false), is_websocket_pending(false) {}
+
+  // True if this connection is a WebSocket connection.
+  bool is_websocket;
+  bool is_websocket_pending;
+};
+
+CefServerImpl::CefServerImpl(CefRefPtr<CefServerHandler> handler)
+    : handler_(handler) {
+  DCHECK(handler_);
+}
+
+void CefServerImpl::Start(const std::string& address,
+                          uint16 port,
+                          int backlog) {
+  DCHECK(!address.empty());
+  CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefServerImpl::StartOnUIThread, this,
+                                        address, port, backlog));
+}
+
+CefRefPtr<CefTaskRunner> CefServerImpl::GetTaskRunner() {
+  if (task_runner_)
+    return new CefTaskRunnerImpl(task_runner_);
+  return nullptr;
+}
+
+void CefServerImpl::Shutdown() {
+  CEF_POST_TASK_HT(
+      base::BindOnce(&CefServerImpl::ShutdownOnHandlerThread, this));
+}
+
+bool CefServerImpl::IsRunning() {
+  CEF_REQUIRE_HT_RETURN(false);
+  return !!server_.get();
+}
+
+CefString CefServerImpl::GetAddress() {
+  return address_;
+}
+
+bool CefServerImpl::HasConnection() {
+  CEF_REQUIRE_HT_RETURN(false);
+  return !connection_info_map_.empty();
+}
+
+bool CefServerImpl::IsValidConnection(int connection_id) {
+  CEF_REQUIRE_HT_RETURN(false);
+  return connection_info_map_.find(connection_id) != connection_info_map_.end();
+}
+
+void CefServerImpl::SendHttp200Response(int connection_id,
+                                        const CefString& content_type,
+                                        const void* data,
+                                        size_t data_size) {
+  SendHttp200ResponseInternal(connection_id, content_type,
+                              CreateUniqueString(data, data_size));
+}
+
+void CefServerImpl::SendHttp404Response(int connection_id) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp404Response, this,
+                                    connection_id));
+    return;
+  }
+
+  if (!ValidateServer())
+    return;
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  if (!info)
+    return;
+
+  if (info->is_websocket) {
+    LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
+               << connection_id;
+    return;
+  }
+
+  server_->Send404(connection_id, MISSING_TRAFFIC_ANNOTATION);
+  server_->Close(connection_id);
+}
+
+void CefServerImpl::SendHttp500Response(int connection_id,
+                                        const CefString& error_message) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp500Response, this,
+                                    connection_id, error_message));
+    return;
+  }
+
+  if (!ValidateServer())
+    return;
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  if (!info)
+    return;
+
+  if (info->is_websocket) {
+    LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
+               << connection_id;
+    return;
+  }
+
+  server_->Send500(connection_id, error_message, MISSING_TRAFFIC_ANNOTATION);
+  server_->Close(connection_id);
+}
+
+void CefServerImpl::SendHttpResponse(int connection_id,
+                                     int response_code,
+                                     const CefString& content_type,
+                                     int64 content_length,
+                                     const HeaderMap& extra_headers) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttpResponse, this,
+                                    connection_id, response_code, content_type,
+                                    content_length, extra_headers));
+    return;
+  }
+
+  if (!ValidateServer())
+    return;
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  if (!info)
+    return;
+
+  if (info->is_websocket) {
+    LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
+               << connection_id;
+    return;
+  }
+
+  net::HttpServerResponseInfo response(
+      static_cast<net::HttpStatusCode>(response_code));
+
+  HeaderMap::const_iterator it = extra_headers.begin();
+  for (; it != extra_headers.end(); ++it)
+    response.AddHeader(it->first, it->second);
+
+  response.AddHeader(net::HttpRequestHeaders::kContentType, content_type);
+  if (content_length >= 0) {
+    response.AddHeader(
+        net::HttpRequestHeaders::kContentLength,
+        base::StringPrintf("%" PRIuS, static_cast<size_t>(content_length)));
+  }
+
+  server_->SendResponse(connection_id, response, MISSING_TRAFFIC_ANNOTATION);
+  if (content_length == 0) {
+    server_->Close(connection_id);
+  }
+}
+
+void CefServerImpl::SendRawData(int connection_id,
+                                const void* data,
+                                size_t data_size) {
+  if (!data || data_size == 0)
+    return;
+  SendRawDataInternal(connection_id, CreateUniqueString(data, data_size));
+}
+
+void CefServerImpl::CloseConnection(int connection_id) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(
+        base::BindOnce(&CefServerImpl::CloseConnection, this, connection_id));
+    return;
+  }
+
+  if (ValidateServer() && GetConnectionInfo(connection_id)) {
+    server_->Close(connection_id);
+  }
+}
+
+void CefServerImpl::SendWebSocketMessage(int connection_id,
+                                         const void* data,
+                                         size_t data_size) {
+  if (!data || data_size == 0)
+    return;
+  SendWebSocketMessageInternal(connection_id,
+                               CreateUniqueString(data, data_size));
+}
+
+void CefServerImpl::ContinueWebSocketRequest(
+    int connection_id,
+    const net::HttpServerRequestInfo& request_info,
+    bool allow) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::ContinueWebSocketRequest,
+                                    this, connection_id, request_info, allow));
+    return;
+  }
+
+  if (!ValidateServer())
+    return;
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  DCHECK(info);
+  if (!info)
+    return;
+
+  DCHECK(info->is_websocket);
+  DCHECK(info->is_websocket_pending);
+  if (!info->is_websocket || !info->is_websocket_pending)
+    return;
+
+  info->is_websocket_pending = false;
+
+  if (allow) {
+    server_->AcceptWebSocket(connection_id, request_info,
+                             MISSING_TRAFFIC_ANNOTATION);
+    handler_->OnWebSocketConnected(this, connection_id);
+  } else {
+    server_->Close(connection_id);
+  }
+}
+
+void CefServerImpl::SendHttp200ResponseInternal(
+    int connection_id,
+    const CefString& content_type,
+    std::unique_ptr<std::string> data) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp200ResponseInternal,
+                                    this, connection_id, content_type,
+                                    std::move(data)));
+    return;
+  }
+
+  if (!ValidateServer())
+    return;
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  if (!info)
+    return;
+
+  if (info->is_websocket) {
+    LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
+               << connection_id;
+    return;
+  }
+
+  server_->Send200(connection_id, *data, content_type,
+                   MISSING_TRAFFIC_ANNOTATION);
+  server_->Close(connection_id);
+}
+
+void CefServerImpl::SendRawDataInternal(int connection_id,
+                                        std::unique_ptr<std::string> data) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendRawDataInternal, this,
+                                    connection_id, std::move(data)));
+    return;
+  }
+
+  if (!ValidateServer())
+    return;
+
+  if (!GetConnectionInfo(connection_id))
+    return;
+
+  server_->SendRaw(connection_id, *data, MISSING_TRAFFIC_ANNOTATION);
+}
+
+void CefServerImpl::SendWebSocketMessageInternal(
+    int connection_id,
+    std::unique_ptr<std::string> data) {
+  if (!CEF_CURRENTLY_ON_HT()) {
+    CEF_POST_TASK_HT(
+        base::BindOnce(&CefServerImpl::SendWebSocketMessageInternal, this,
+                       connection_id, std::move(data)));
+    return;
+  }
+
+  if (!ValidateServer())
+    return;
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  if (!info)
+    return;
+
+  if (!info->is_websocket || info->is_websocket_pending) {
+    LOG(ERROR) << "Invalid attempt to send WebSocket message for connection_id "
+               << connection_id;
+    return;
+  }
+
+  server_->SendOverWebSocket(connection_id, *data, MISSING_TRAFFIC_ANNOTATION);
+}
+
+void CefServerImpl::OnConnect(int connection_id) {
+  CEF_REQUIRE_HT();
+
+  CreateConnectionInfo(connection_id);
+  handler_->OnClientConnected(this, connection_id);
+}
+
+void CefServerImpl::OnHttpRequest(
+    int connection_id,
+    const net::HttpServerRequestInfo& request_info) {
+  CEF_REQUIRE_HT();
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  DCHECK(info);
+  if (!info)
+    return;
+
+  DCHECK(!info->is_websocket);
+
+  handler_->OnHttpRequest(this, connection_id, request_info.peer.ToString(),
+                          CreateRequest(address_, request_info, false));
+}
+
+void CefServerImpl::OnWebSocketRequest(
+    int connection_id,
+    const net::HttpServerRequestInfo& request_info) {
+  CEF_REQUIRE_HT();
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  DCHECK(info);
+  if (!info)
+    return;
+
+  DCHECK(!info->is_websocket);
+  info->is_websocket = true;
+  info->is_websocket_pending = true;
+
+  // Will eventually result in a call to ContinueWebSocketRequest.
+  CefRefPtr<CefCallback> callback =
+      new AcceptWebSocketCallback(this, connection_id, request_info);
+  handler_->OnWebSocketRequest(
+      this, connection_id, request_info.peer.ToString(),
+      CreateRequest(address_, request_info, true), callback);
+}
+
+void CefServerImpl::OnWebSocketMessage(int connection_id, std::string data) {
+  CEF_REQUIRE_HT();
+
+  ConnectionInfo* info = GetConnectionInfo(connection_id);
+  if (!info)
+    return;
+
+  DCHECK(info->is_websocket);
+  DCHECK(!info->is_websocket_pending);
+
+  handler_->OnWebSocketMessage(this, connection_id, data.data(), data.size());
+}
+
+void CefServerImpl::OnClose(int connection_id) {
+  CEF_REQUIRE_HT();
+
+  RemoveConnectionInfo(connection_id);
+  handler_->OnClientDisconnected(this, connection_id);
+}
+
+void CefServerImpl::StartOnUIThread(const std::string& address,
+                                    uint16 port,
+                                    int backlog) {
+  CEF_REQUIRE_UIT();
+  DCHECK(!thread_);
+
+  std::unique_ptr<base::Thread> thread(
+      new base::Thread(base::StringPrintf("%s:%d", address.c_str(), port)));
+  base::Thread::Options options;
+  options.message_pump_type = base::MessagePumpType::IO;
+  if (thread->StartWithOptions(options)) {
+    // Add a reference that will be released in ShutdownOnUIThread().
+    AddRef();
+
+    thread_ = std::move(thread);
+    task_runner_ = thread_->task_runner();
+
+    CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::StartOnHandlerThread, this,
+                                    address, port, backlog));
+  }
+}
+
+void CefServerImpl::StartOnHandlerThread(const std::string& address,
+                                         uint16 port,
+                                         int backlog) {
+  CEF_REQUIRE_HT();
+
+  std::unique_ptr<net::ServerSocket> socket(
+      new net::TCPServerSocket(nullptr, net::NetLogSource()));
+  if (socket->ListenWithAddressAndPort(address, port, backlog) == net::OK) {
+    server_.reset(new net::HttpServer(std::move(socket), this));
+
+    net::IPEndPoint address;
+    if (server_->GetLocalAddress(&address) == net::OK)
+      address_ = address.ToString();
+  }
+
+  handler_->OnServerCreated(this);
+
+  if (!server_) {
+    // Server failed to start.
+    handler_->OnServerDestroyed(this);
+
+    CEF_POST_TASK(CEF_UIT,
+                  base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this));
+  }
+}
+
+void CefServerImpl::ShutdownOnHandlerThread() {
+  CEF_REQUIRE_HT();
+
+  if (server_) {
+    // Stop the server.
+    server_.reset();
+
+    if (!connection_info_map_.empty()) {
+      // Clear |connection_info_map_| first so any calls from
+      // OnClientDisconnected will fail as expected.
+      ConnectionInfoMap temp_map;
+      temp_map.swap(connection_info_map_);
+
+      // OnClose won't be called for clients that are connected when the server
+      // shuts down, so send the disconnected notification here.
+      ConnectionInfoMap::const_iterator it = temp_map.begin();
+      for (; it != temp_map.end(); ++it) {
+        handler_->OnClientDisconnected(this, it->first);
+      }
+    }
+
+    handler_->OnServerDestroyed(this);
+  }
+
+  CEF_POST_TASK(CEF_UIT,
+                base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this));
+}
+
+void CefServerImpl::ShutdownOnUIThread() {
+  CEF_REQUIRE_UIT();
+
+  handler_ = nullptr;
+
+  if (thread_) {
+    // Stop the handler thread as a background task so the UI thread isn't
+    // blocked.
+    CEF_POST_BACKGROUND_TASK(BindOnce(
+        [](std::unique_ptr<base::Thread> thread) {
+          // Calling PlatformThread::Join() on the UI thread is otherwise
+          // disallowed.
+          base::ScopedAllowBaseSyncPrimitivesForTesting
+              scoped_allow_sync_primitives;
+          thread.reset();
+        },
+        std::move(thread_)));
+
+    // Release the reference that was added in StartupOnUIThread().
+    Release();
+  }
+}
+
+bool CefServerImpl::ValidateServer() const {
+  CEF_REQUIRE_HT();
+  if (!server_) {
+    LOG(ERROR) << "Server is not running";
+    return false;
+  }
+  return true;
+}
+
+CefServerImpl::ConnectionInfo* CefServerImpl::CreateConnectionInfo(
+    int connection_id) {
+  CEF_REQUIRE_HT();
+
+#if DCHECK_IS_ON()
+  ConnectionInfoMap::const_iterator it =
+      connection_info_map_.find(connection_id);
+  DCHECK(it == connection_info_map_.end());
+#endif
+
+  ConnectionInfo* info = new ConnectionInfo();
+  connection_info_map_.insert(
+      std::make_pair(connection_id, base::WrapUnique(info)));
+  return info;
+}
+
+CefServerImpl::ConnectionInfo* CefServerImpl::GetConnectionInfo(
+    int connection_id) const {
+  CEF_REQUIRE_HT();
+  ConnectionInfoMap::const_iterator it =
+      connection_info_map_.find(connection_id);
+  if (it != connection_info_map_.end())
+    return it->second.get();
+
+  LOG(ERROR) << "Invalid connection_id " << connection_id;
+  return nullptr;
+}
+
+void CefServerImpl::RemoveConnectionInfo(int connection_id) {
+  CEF_REQUIRE_HT();
+  ConnectionInfoMap::iterator it = connection_info_map_.find(connection_id);
+  DCHECK(it != connection_info_map_.end());
+  if (it != connection_info_map_.end())
+    connection_info_map_.erase(it);
+}
+
+bool CefServerImpl::CurrentlyOnHandlerThread() const {
+  return task_runner_ && task_runner_->BelongsToCurrentThread();
+}
diff --git a/src/libcef/browser/server_impl.h b/src/libcef/browser/server_impl.h
new file mode 100644
index 0000000..9703f59
--- /dev/null
+++ b/src/libcef/browser/server_impl.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_SERVER_IMPL_H_
+#define CEF_LIBCEF_BROWSER_SERVER_IMPL_H_
+#pragma once
+
+#include <map>
+#include <memory>
+
+#include "include/cef_server.h"
+
+#include "base/single_thread_task_runner.h"
+#include "net/server/http_server.h"
+
+namespace base {
+class Thread;
+}
+
+class CefServerImpl : public CefServer, net::HttpServer::Delegate {
+ public:
+  explicit CefServerImpl(CefRefPtr<CefServerHandler> handler);
+
+  void Start(const std::string& address, uint16 port, int backlog);
+
+  // CefServer methods:
+  CefRefPtr<CefTaskRunner> GetTaskRunner() override;
+  void Shutdown() override;
+  bool IsRunning() override;
+  CefString GetAddress() override;
+  bool HasConnection() override;
+  bool IsValidConnection(int connection_id) override;
+  void SendHttp200Response(int connection_id,
+                           const CefString& content_type,
+                           const void* data,
+                           size_t data_size) override;
+  void SendHttp404Response(int connection_id) override;
+  void SendHttp500Response(int connection_id,
+                           const CefString& error_message) override;
+  void SendHttpResponse(int connection_id,
+                        int response_code,
+                        const CefString& content_type,
+                        int64 content_length,
+                        const HeaderMap& extra_headers) override;
+  void SendRawData(int connection_id,
+                   const void* data,
+                   size_t data_size) override;
+  void CloseConnection(int connection_id) override;
+  void SendWebSocketMessage(int connection_id,
+                            const void* data,
+                            size_t data_size) override;
+
+  void ContinueWebSocketRequest(int connection_id,
+                                const net::HttpServerRequestInfo& request_info,
+                                bool allow);
+
+ private:
+  void SendHttp200ResponseInternal(int connection_id,
+                                   const CefString& content_type,
+                                   std::unique_ptr<std::string> data);
+  void SendRawDataInternal(int connection_id,
+                           std::unique_ptr<std::string> data);
+  void SendWebSocketMessageInternal(int connection_id,
+                                    std::unique_ptr<std::string> data);
+
+  // HttpServer::Delegate methods:
+  void OnConnect(int connection_id) override;
+  void OnHttpRequest(int connection_id,
+                     const net::HttpServerRequestInfo& request_info) override;
+  void OnWebSocketRequest(
+      int connection_id,
+      const net::HttpServerRequestInfo& request_info) override;
+  void OnWebSocketMessage(int connection_id, std::string data) override;
+  void OnClose(int connection_id) override;
+
+  void StartOnUIThread(const std::string& address, uint16 port, int backlog);
+  void StartOnHandlerThread(const std::string& address,
+                            uint16 port,
+                            int backlog);
+
+  void ShutdownOnHandlerThread();
+  void ShutdownOnUIThread();
+
+  bool ValidateServer() const;
+
+  struct ConnectionInfo;
+  ConnectionInfo* CreateConnectionInfo(int connection_id);
+  ConnectionInfo* GetConnectionInfo(int connection_id) const;
+  void RemoveConnectionInfo(int connection_id);
+
+  bool CurrentlyOnHandlerThread() const;
+
+  // Safe to access from any thread.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  std::string address_;
+
+  // Only accessed on the UI thread.
+  std::unique_ptr<base::Thread> thread_;
+
+  // Only accessed on the server thread.
+  CefRefPtr<CefServerHandler> handler_;
+  std::unique_ptr<net::HttpServer> server_;
+
+  // Map of connection_id to ConnectionInfo.
+  using ConnectionInfoMap = std::map<int, std::unique_ptr<ConnectionInfo>>;
+  ConnectionInfoMap connection_info_map_;
+
+  IMPLEMENT_REFCOUNTING(CefServerImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefServerImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_SERVER_IMPL_H_
diff --git a/src/libcef/browser/speech_recognition_manager_delegate.cc b/src/libcef/browser/speech_recognition_manager_delegate.cc
new file mode 100644
index 0000000..206a329
--- /dev/null
+++ b/src/libcef/browser/speech_recognition_manager_delegate.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/speech_recognition_manager_delegate.h"
+
+#include <set>
+#include <string>
+
+#include "libcef/common/cef_switches.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/task/post_task.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/speech_recognition_manager.h"
+#include "content/public/browser/speech_recognition_session_context.h"
+#include "content/public/browser/web_contents.h"
+
+using content::BrowserThread;
+using content::SpeechRecognitionManager;
+using content::WebContents;
+
+CefSpeechRecognitionManagerDelegate ::CefSpeechRecognitionManagerDelegate() {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  filter_profanities_ =
+      command_line->HasSwitch(switches::kEnableProfanityFilter);
+}
+
+CefSpeechRecognitionManagerDelegate ::~CefSpeechRecognitionManagerDelegate() {}
+
+void CefSpeechRecognitionManagerDelegate::OnRecognitionStart(int session_id) {}
+
+void CefSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {}
+
+void CefSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete(
+    int session_id) {}
+
+void CefSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) {}
+
+void CefSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {}
+
+void CefSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {}
+
+void CefSpeechRecognitionManagerDelegate::OnRecognitionResults(
+    int session_id,
+    const std::vector<blink::mojom::SpeechRecognitionResultPtr>& result) {}
+
+void CefSpeechRecognitionManagerDelegate::OnRecognitionError(
+    int session_id,
+    const blink::mojom::SpeechRecognitionError& error) {}
+
+void CefSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
+    int session_id,
+    float volume,
+    float noise_volume) {}
+
+void CefSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) {}
+
+void CefSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
+    int session_id,
+    base::OnceCallback<void(bool ask_user, bool is_allowed)> callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  const content::SpeechRecognitionSessionContext& context =
+      SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
+
+  // Make sure that initiators properly set the |render_process_id| field.
+  DCHECK_NE(context.render_process_id, 0);
+
+  base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                 base::BindOnce(std::move(callback), false, true));
+}
+
+content::SpeechRecognitionEventListener*
+CefSpeechRecognitionManagerDelegate::GetEventListener() {
+  return this;
+}
+
+bool CefSpeechRecognitionManagerDelegate::FilterProfanities(
+    int render_process_id) {
+  return filter_profanities_;
+}
diff --git a/src/libcef/browser/speech_recognition_manager_delegate.h b/src/libcef/browser/speech_recognition_manager_delegate.h
new file mode 100644
index 0000000..9327818
--- /dev/null
+++ b/src/libcef/browser/speech_recognition_manager_delegate.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
+#define CEF_LIBCEF_BROWSER_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/speech_recognition_event_listener.h"
+#include "content/public/browser/speech_recognition_manager_delegate.h"
+
+// This is CEF's implementation of the SpeechRecognitionManagerDelegate
+// interface. Based on chrome/browser/speech/
+// chrome_speech_recognition_manager_delegate.[cc|h]
+class CefSpeechRecognitionManagerDelegate
+    : public content::SpeechRecognitionManagerDelegate,
+      public content::SpeechRecognitionEventListener {
+ public:
+  CefSpeechRecognitionManagerDelegate();
+  ~CefSpeechRecognitionManagerDelegate() override;
+
+ protected:
+  // SpeechRecognitionEventListener methods.
+  void OnRecognitionStart(int session_id) override;
+  void OnAudioStart(int session_id) override;
+  void OnEnvironmentEstimationComplete(int session_id) override;
+  void OnSoundStart(int session_id) override;
+  void OnSoundEnd(int session_id) override;
+  void OnAudioEnd(int session_id) override;
+  void OnRecognitionEnd(int session_id) override;
+  void OnRecognitionResults(
+      int session_id,
+      const std::vector<blink::mojom::SpeechRecognitionResultPtr>& result)
+      override;
+  void OnRecognitionError(
+      int session_id,
+      const blink::mojom::SpeechRecognitionError& error) override;
+  void OnAudioLevelsChange(int session_id,
+                           float volume,
+                           float noise_volume) override;
+
+  // SpeechRecognitionManagerDelegate methods.
+  void CheckRecognitionIsAllowed(
+      int session_id,
+      base::OnceCallback<void(bool ask_user, bool is_allowed)> callback)
+      override;
+  content::SpeechRecognitionEventListener* GetEventListener() override;
+  bool FilterProfanities(int render_process_id) override;
+
+ private:
+  bool filter_profanities_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefSpeechRecognitionManagerDelegate);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
diff --git a/src/libcef/browser/ssl_host_state_delegate.cc b/src/libcef/browser/ssl_host_state_delegate.cc
new file mode 100644
index 0000000..a7f037a
--- /dev/null
+++ b/src/libcef/browser/ssl_host_state_delegate.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/ssl_host_state_delegate.h"
+
+#include "base/callback.h"
+#include "net/base/hash_value.h"
+
+using content::SSLHostStateDelegate;
+
+namespace internal {
+
+CertPolicy::CertPolicy() {}
+CertPolicy::~CertPolicy() {}
+
+// For an allowance, we consider a given |cert| to be a match to a saved
+// allowed cert if the |error| is an exact match to or subset of the errors
+// in the saved CertStatus.
+bool CertPolicy::Check(const net::X509Certificate& cert, int error) const {
+  net::SHA256HashValue fingerprint = cert.CalculateChainFingerprint256();
+  const auto& allowed_iter = allowed_.find(fingerprint);
+  if ((allowed_iter != allowed_.end()) && (allowed_iter->second & error) &&
+      ((allowed_iter->second & error) == error)) {
+    return true;
+  }
+  return false;
+}
+
+void CertPolicy::Allow(const net::X509Certificate& cert, int error) {
+  // If this same cert had already been saved with a different error status,
+  // this will replace it with the new error status.
+  net::SHA256HashValue fingerprint = cert.CalculateChainFingerprint256();
+  allowed_[fingerprint] = error;
+}
+
+}  // namespace internal
+
+CefSSLHostStateDelegate::CefSSLHostStateDelegate() {}
+
+CefSSLHostStateDelegate::~CefSSLHostStateDelegate() {}
+
+void CefSSLHostStateDelegate::HostRanInsecureContent(
+    const std::string& host,
+    int child_id,
+    InsecureContentType content_type) {
+  // Intentional no-op.
+}
+
+bool CefSSLHostStateDelegate::DidHostRunInsecureContent(
+    const std::string& host,
+    int child_id,
+    InsecureContentType content_type) {
+  // Intentional no-op.
+  return false;
+}
+
+void CefSSLHostStateDelegate::AllowCert(const std::string& host,
+                                        const net::X509Certificate& cert,
+                                        int error,
+                                        content::WebContents* web_contents) {
+  cert_policy_for_host_[host].Allow(cert, error);
+}
+
+void CefSSLHostStateDelegate::Clear(
+    const base::RepeatingCallback<bool(const std::string&)> host_filter) {
+  if (host_filter.is_null()) {
+    cert_policy_for_host_.clear();
+    return;
+  }
+
+  for (auto it = cert_policy_for_host_.begin();
+       it != cert_policy_for_host_.end();) {
+    auto next_it = std::next(it);
+
+    if (host_filter.Run(it->first))
+      cert_policy_for_host_.erase(it);
+
+    it = next_it;
+  }
+}
+
+SSLHostStateDelegate::CertJudgment CefSSLHostStateDelegate::QueryPolicy(
+    const std::string& host,
+    const net::X509Certificate& cert,
+    int error,
+    content::WebContents* web_contents) {
+  return cert_policy_for_host_[host].Check(cert, error)
+             ? SSLHostStateDelegate::ALLOWED
+             : SSLHostStateDelegate::DENIED;
+}
+
+void CefSSLHostStateDelegate::RevokeUserAllowExceptions(
+    const std::string& host) {
+  cert_policy_for_host_.erase(host);
+}
+
+bool CefSSLHostStateDelegate::HasAllowException(
+    const std::string& host,
+    content::WebContents* web_contents) {
+  auto policy_iterator = cert_policy_for_host_.find(host);
+  return policy_iterator != cert_policy_for_host_.end() &&
+         policy_iterator->second.HasAllowException();
+}
diff --git a/src/libcef/browser/ssl_host_state_delegate.h b/src/libcef/browser/ssl_host_state_delegate.h
new file mode 100644
index 0000000..3085c6e
--- /dev/null
+++ b/src/libcef/browser/ssl_host_state_delegate.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_SSL_HOST_STATE_DELEGATE_H_
+#define CEF_LIBCEF_BROWSER_SSL_HOST_STATE_DELEGATE_H_
+
+#include <map>
+#include <string>
+
+#include "content/public/browser/ssl_host_state_delegate.h"
+#include "net/base/hash_value.h"
+#include "net/cert/x509_certificate.h"
+
+// Implementation based on android_webview/browser/aw_ssl_host_state_delegate.h.
+
+namespace internal {
+
+// This class maintains the policy for storing actions on certificate errors.
+class CertPolicy {
+ public:
+  CertPolicy();
+  ~CertPolicy();
+  // Returns true if the user has decided to proceed through the ssl error
+  // before. For a certificate to be allowed, it must not have any
+  // *additional* errors from when it was allowed.
+  bool Check(const net::X509Certificate& cert, int error) const;
+
+  // Causes the policy to allow this certificate for a given |error|. And
+  // remember the user's choice.
+  void Allow(const net::X509Certificate& cert, int error);
+
+  // Returns true if and only if there exists a user allow exception for some
+  // certificate.
+  bool HasAllowException() const { return allowed_.size() > 0; }
+
+ private:
+  // The set of fingerprints of allowed certificates.
+  std::map<net::SHA256HashValue, int> allowed_;
+};
+
+}  // namespace internal
+
+class CefSSLHostStateDelegate : public content::SSLHostStateDelegate {
+ public:
+  CefSSLHostStateDelegate();
+  ~CefSSLHostStateDelegate() override;
+
+  // SSLHostStateDelegate methods:
+  void AllowCert(const std::string& host,
+                 const net::X509Certificate& cert,
+                 int error,
+                 content::WebContents* web_contents) override;
+  void Clear(const base::RepeatingCallback<bool(const std::string&)>
+                 host_filter) override;
+  content::SSLHostStateDelegate::CertJudgment QueryPolicy(
+      const std::string& host,
+      const net::X509Certificate& cert,
+      int error,
+      content::WebContents* web_contents) override;
+  void HostRanInsecureContent(const std::string& host,
+                              int child_id,
+                              InsecureContentType content_type) override;
+  bool DidHostRunInsecureContent(const std::string& host,
+                                 int child_id,
+                                 InsecureContentType content_type) override;
+  void RevokeUserAllowExceptions(const std::string& host) override;
+  bool HasAllowException(const std::string& host,
+                         content::WebContents* web_contents) override;
+
+ private:
+  // Certificate policies for each host.
+  std::map<std::string, internal::CertPolicy> cert_policy_for_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefSSLHostStateDelegate);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_SSL_HOST_STATE_DELEGATE_H_
diff --git a/src/libcef/browser/ssl_info_impl.cc b/src/libcef/browser/ssl_info_impl.cc
new file mode 100644
index 0000000..7c88127
--- /dev/null
+++ b/src/libcef/browser/ssl_info_impl.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/ssl_info_impl.h"
+#include "libcef/browser/x509_certificate_impl.h"
+
+#include "net/cert/cert_status_flags.h"
+
+CefSSLInfoImpl::CefSSLInfoImpl(const net::SSLInfo& value)
+    : cert_status_(CERT_STATUS_NONE) {
+  cert_status_ = static_cast<cef_cert_status_t>(value.cert_status);
+  if (value.cert.get()) {
+    cert_ = new CefX509CertificateImpl(value.cert);
+  }
+}
+
+cef_cert_status_t CefSSLInfoImpl::GetCertStatus() {
+  return cert_status_;
+}
+
+CefRefPtr<CefX509Certificate> CefSSLInfoImpl::GetX509Certificate() {
+  return cert_;
+}
+
+bool CefIsCertStatusError(cef_cert_status_t status) {
+  return net::IsCertStatusError(status);
+}
diff --git a/src/libcef/browser/ssl_info_impl.h b/src/libcef/browser/ssl_info_impl.h
new file mode 100644
index 0000000..f1a9124
--- /dev/null
+++ b/src/libcef/browser/ssl_info_impl.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_SSL_INFO_IMPL_H_
+#define CEF_LIBCEF_BROWSER_SSL_INFO_IMPL_H_
+#pragma once
+
+#include "include/cef_ssl_info.h"
+
+#include "net/ssl/ssl_info.h"
+
+// CefSSLInfo implementation
+class CefSSLInfoImpl : public CefSSLInfo {
+ public:
+  explicit CefSSLInfoImpl(const net::SSLInfo& value);
+
+  // CefSSLInfo methods.
+  cef_cert_status_t GetCertStatus() override;
+  CefRefPtr<CefX509Certificate> GetX509Certificate() override;
+
+ private:
+  cef_cert_status_t cert_status_;
+  CefRefPtr<CefX509Certificate> cert_;
+
+  IMPLEMENT_REFCOUNTING(CefSSLInfoImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefSSLInfoImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_SSL_INFO_IMPL_H_
diff --git a/src/libcef/browser/ssl_status_impl.cc b/src/libcef/browser/ssl_status_impl.cc
new file mode 100644
index 0000000..4330c70
--- /dev/null
+++ b/src/libcef/browser/ssl_status_impl.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/ssl_status_impl.h"
+
+#include "libcef/browser/x509_certificate_impl.h"
+
+#include "net/ssl/ssl_connection_status_flags.h"
+
+CefSSLStatusImpl::CefSSLStatusImpl(const content::SSLStatus& value) {
+  cert_status_ = static_cast<cef_cert_status_t>(value.cert_status);
+  content_status_ = static_cast<cef_ssl_content_status_t>(value.content_status);
+  ssl_version_ = static_cast<cef_ssl_version_t>(
+      net::SSLConnectionStatusToVersion(value.connection_status));
+  certificate_ = value.certificate;
+}
+
+bool CefSSLStatusImpl::IsSecureConnection() {
+  return !!certificate_.get();
+}
+
+cef_cert_status_t CefSSLStatusImpl::GetCertStatus() {
+  return cert_status_;
+}
+
+cef_ssl_version_t CefSSLStatusImpl::GetSSLVersion() {
+  return ssl_version_;
+}
+
+cef_ssl_content_status_t CefSSLStatusImpl::GetContentStatus() {
+  return content_status_;
+}
+
+CefRefPtr<CefX509Certificate> CefSSLStatusImpl::GetX509Certificate() {
+  if (certificate_ && !cef_certificate_)
+    cef_certificate_ = new CefX509CertificateImpl(certificate_);
+  return cef_certificate_;
+}
diff --git a/src/libcef/browser/ssl_status_impl.h b/src/libcef/browser/ssl_status_impl.h
new file mode 100644
index 0000000..cd07060
--- /dev/null
+++ b/src/libcef/browser/ssl_status_impl.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_SSL_STATUS_IMPL_H_
+#define CEF_LIBCEF_BROWSER_SSL_STATUS_IMPL_H_
+#pragma once
+
+#include "include/cef_ssl_status.h"
+
+#include "content/public/browser/ssl_status.h"
+
+// CefSSLStatus implementation
+class CefSSLStatusImpl : public CefSSLStatus {
+ public:
+  explicit CefSSLStatusImpl(const content::SSLStatus& value);
+
+  // CefSSLStatus methods.
+  bool IsSecureConnection() override;
+  cef_cert_status_t GetCertStatus() override;
+  cef_ssl_version_t GetSSLVersion() override;
+  cef_ssl_content_status_t GetContentStatus() override;
+  CefRefPtr<CefX509Certificate> GetX509Certificate() override;
+
+ private:
+  cef_cert_status_t cert_status_;
+  cef_ssl_version_t ssl_version_;
+  cef_ssl_content_status_t content_status_;
+
+  // Don't create a CefX509Certificate object until requested.
+  scoped_refptr<net::X509Certificate> certificate_;
+  CefRefPtr<CefX509Certificate> cef_certificate_;
+
+  IMPLEMENT_REFCOUNTING(CefSSLStatusImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefSSLStatusImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_SSL_STATUS_IMPL_H_
diff --git a/src/libcef/browser/stream_impl.cc b/src/libcef/browser/stream_impl.cc
new file mode 100644
index 0000000..6672047
--- /dev/null
+++ b/src/libcef/browser/stream_impl.cc
@@ -0,0 +1,306 @@
+// Copyright (c) 2008 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/stream_impl.h"
+#include <stdlib.h>
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+
+// Static functions
+
+CefRefPtr<CefStreamReader> CefStreamReader::CreateForFile(
+    const CefString& fileName) {
+  DCHECK(!fileName.empty());
+
+  // TODO(cef): Do not allow file IO on all threads (issue #1187).
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+  CefRefPtr<CefStreamReader> reader;
+  FILE* file = base::OpenFile(base::FilePath(fileName), "rb");
+  if (file)
+    reader = new CefFileReader(file, true);
+  return reader;
+}
+
+CefRefPtr<CefStreamReader> CefStreamReader::CreateForData(void* data,
+                                                          size_t size) {
+  DCHECK(data != nullptr);
+  DCHECK(size > 0);
+  CefRefPtr<CefStreamReader> reader;
+  if (data && size > 0)
+    reader = new CefBytesReader(data, size, true);
+  return reader;
+}
+
+CefRefPtr<CefStreamReader> CefStreamReader::CreateForHandler(
+    CefRefPtr<CefReadHandler> handler) {
+  DCHECK(handler.get());
+  CefRefPtr<CefStreamReader> reader;
+  if (handler.get())
+    reader = new CefHandlerReader(handler);
+  return reader;
+}
+
+CefRefPtr<CefStreamWriter> CefStreamWriter::CreateForFile(
+    const CefString& fileName) {
+  DCHECK(!fileName.empty());
+
+  // TODO(cef): Do not allow file IO on all threads (issue #1187).
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+  CefRefPtr<CefStreamWriter> writer;
+  FILE* file = base::OpenFile(base::FilePath(fileName), "wb");
+  if (file)
+    writer = new CefFileWriter(file, true);
+  return writer;
+}
+
+CefRefPtr<CefStreamWriter> CefStreamWriter::CreateForHandler(
+    CefRefPtr<CefWriteHandler> handler) {
+  DCHECK(handler.get());
+  CefRefPtr<CefStreamWriter> writer;
+  if (handler.get())
+    writer = new CefHandlerWriter(handler);
+  return writer;
+}
+
+// CefFileReader
+
+CefFileReader::CefFileReader(FILE* file, bool close)
+    : close_(close), file_(file) {}
+
+CefFileReader::~CefFileReader() {
+  base::AutoLock lock_scope(lock_);
+  if (close_)
+    base::CloseFile(file_);
+}
+
+size_t CefFileReader::Read(void* ptr, size_t size, size_t n) {
+  base::AutoLock lock_scope(lock_);
+  return fread(ptr, size, n, file_);
+}
+
+int CefFileReader::Seek(int64 offset, int whence) {
+  base::AutoLock lock_scope(lock_);
+#if defined(OS_WIN)
+  return _fseeki64(file_, offset, whence);
+#else
+  return fseek(file_, offset, whence);
+#endif
+}
+
+int64 CefFileReader::Tell() {
+  base::AutoLock lock_scope(lock_);
+#if defined(OS_WIN)
+  return _ftelli64(file_);
+#else
+  return ftell(file_);
+#endif
+}
+
+int CefFileReader::Eof() {
+  base::AutoLock lock_scope(lock_);
+  return feof(file_);
+}
+
+// CefFileWriter
+
+CefFileWriter::CefFileWriter(FILE* file, bool close)
+    : file_(file), close_(close) {}
+
+CefFileWriter::~CefFileWriter() {
+  base::AutoLock lock_scope(lock_);
+  if (close_)
+    base::CloseFile(file_);
+}
+
+size_t CefFileWriter::Write(const void* ptr, size_t size, size_t n) {
+  base::AutoLock lock_scope(lock_);
+  return (size_t)fwrite(ptr, size, n, file_);
+}
+
+int CefFileWriter::Seek(int64 offset, int whence) {
+  base::AutoLock lock_scope(lock_);
+  return fseek(file_, offset, whence);
+}
+
+int64 CefFileWriter::Tell() {
+  base::AutoLock lock_scope(lock_);
+  return ftell(file_);
+}
+
+int CefFileWriter::Flush() {
+  base::AutoLock lock_scope(lock_);
+  return fflush(file_);
+}
+
+// CefBytesReader
+
+CefBytesReader::CefBytesReader(void* data, int64 datasize, bool copy)
+    : data_(nullptr), datasize_(0), copy_(false), offset_(0) {
+  SetData(data, datasize, copy);
+}
+
+CefBytesReader::~CefBytesReader() {
+  SetData(nullptr, 0, false);
+}
+
+size_t CefBytesReader::Read(void* ptr, size_t size, size_t n) {
+  base::AutoLock lock_scope(lock_);
+  size_t s = (datasize_ - offset_) / size;
+  size_t ret = (n < s ? n : s);
+  memcpy(ptr, (reinterpret_cast<char*>(data_)) + offset_, ret * size);
+  offset_ += ret * size;
+  return ret;
+}
+
+int CefBytesReader::Seek(int64 offset, int whence) {
+  int rv = -1L;
+  base::AutoLock lock_scope(lock_);
+  switch (whence) {
+    case SEEK_CUR:
+      if (offset_ + offset > datasize_ || offset_ + offset < 0)
+        break;
+      offset_ += offset;
+      rv = 0;
+      break;
+    case SEEK_END: {
+      int64 offset_abs = std::abs(offset);
+      if (offset_abs > datasize_)
+        break;
+      offset_ = datasize_ - offset_abs;
+      rv = 0;
+      break;
+    }
+    case SEEK_SET:
+      if (offset > datasize_ || offset < 0)
+        break;
+      offset_ = offset;
+      rv = 0;
+      break;
+  }
+
+  return rv;
+}
+
+int64 CefBytesReader::Tell() {
+  base::AutoLock lock_scope(lock_);
+  return offset_;
+}
+
+int CefBytesReader::Eof() {
+  base::AutoLock lock_scope(lock_);
+  return (offset_ >= datasize_);
+}
+
+void CefBytesReader::SetData(void* data, int64 datasize, bool copy) {
+  base::AutoLock lock_scope(lock_);
+  if (copy_)
+    free(data_);
+
+  copy_ = copy;
+  offset_ = 0;
+  datasize_ = datasize;
+
+  if (copy) {
+    data_ = malloc(datasize);
+    DCHECK(data_ != nullptr);
+    if (data_)
+      memcpy(data_, data, datasize);
+  } else {
+    data_ = data;
+  }
+}
+
+// CefBytesWriter
+
+CefBytesWriter::CefBytesWriter(size_t grow)
+    : grow_(grow), datasize_(grow), offset_(0) {
+  DCHECK(grow > 0);
+  data_ = malloc(grow);
+  DCHECK(data_ != nullptr);
+}
+
+CefBytesWriter::~CefBytesWriter() {
+  base::AutoLock lock_scope(lock_);
+  if (data_)
+    free(data_);
+}
+
+size_t CefBytesWriter::Write(const void* ptr, size_t size, size_t n) {
+  base::AutoLock lock_scope(lock_);
+  size_t rv;
+  if (offset_ + static_cast<int64>(size * n) >= datasize_ &&
+      Grow(size * n) == 0) {
+    rv = 0;
+  } else {
+    memcpy(reinterpret_cast<char*>(data_) + offset_, ptr, size * n);
+    offset_ += size * n;
+    rv = n;
+  }
+
+  return rv;
+}
+
+int CefBytesWriter::Seek(int64 offset, int whence) {
+  int rv = -1L;
+  base::AutoLock lock_scope(lock_);
+  switch (whence) {
+    case SEEK_CUR:
+      if (offset_ + offset > datasize_ || offset_ + offset < 0)
+        break;
+      offset_ += offset;
+      rv = 0;
+      break;
+    case SEEK_END: {
+      int64 offset_abs = std::abs(offset);
+      if (offset_abs > datasize_)
+        break;
+      offset_ = datasize_ - offset_abs;
+      rv = 0;
+      break;
+    }
+    case SEEK_SET:
+      if (offset > datasize_ || offset < 0)
+        break;
+      offset_ = offset;
+      rv = 0;
+      break;
+  }
+
+  return rv;
+}
+
+int64 CefBytesWriter::Tell() {
+  base::AutoLock lock_scope(lock_);
+  return offset_;
+}
+
+int CefBytesWriter::Flush() {
+  return 0;
+}
+
+std::string CefBytesWriter::GetDataString() {
+  base::AutoLock lock_scope(lock_);
+  std::string str(reinterpret_cast<char*>(data_), offset_);
+  return str;
+}
+
+size_t CefBytesWriter::Grow(size_t size) {
+  base::AutoLock lock_scope(lock_);
+  size_t rv;
+  size_t s = (size > grow_ ? size : grow_);
+  void* tmp = realloc(data_, datasize_ + s);
+  DCHECK(tmp != nullptr);
+  if (tmp) {
+    data_ = tmp;
+    datasize_ += s;
+    rv = datasize_;
+  } else {
+    rv = 0;
+  }
+
+  return rv;
+}
diff --git a/src/libcef/browser/stream_impl.h b/src/libcef/browser/stream_impl.h
new file mode 100644
index 0000000..dde3d60
--- /dev/null
+++ b/src/libcef/browser/stream_impl.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_STREAM_IMPL_H_
+#define CEF_LIBCEF_BROWSER_STREAM_IMPL_H_
+#pragma once
+
+#include "include/cef_stream.h"
+
+#include <stdio.h>
+#include <string>
+
+#include "base/synchronization/lock.h"
+
+// Implementation of CefStreamReader for files.
+class CefFileReader : public CefStreamReader {
+ public:
+  CefFileReader(FILE* file, bool close);
+  ~CefFileReader() override;
+
+  size_t Read(void* ptr, size_t size, size_t n) override;
+  int Seek(int64 offset, int whence) override;
+  int64 Tell() override;
+  int Eof() override;
+  bool MayBlock() override { return true; }
+
+ protected:
+  bool close_;
+  FILE* file_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefFileReader);
+};
+
+// Implementation of CefStreamWriter for files.
+class CefFileWriter : public CefStreamWriter {
+ public:
+  CefFileWriter(FILE* file, bool close);
+  ~CefFileWriter() override;
+
+  size_t Write(const void* ptr, size_t size, size_t n) override;
+  int Seek(int64 offset, int whence) override;
+  int64 Tell() override;
+  int Flush() override;
+  bool MayBlock() override { return true; }
+
+ protected:
+  FILE* file_;
+  bool close_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefFileWriter);
+};
+
+// Implementation of CefStreamReader for byte buffers.
+class CefBytesReader : public CefStreamReader {
+ public:
+  CefBytesReader(void* data, int64 datasize, bool copy);
+  ~CefBytesReader() override;
+
+  size_t Read(void* ptr, size_t size, size_t n) override;
+  int Seek(int64 offset, int whence) override;
+  int64 Tell() override;
+  int Eof() override;
+  bool MayBlock() override { return false; }
+
+  void SetData(void* data, int64 datasize, bool copy);
+
+  void* GetData() { return data_; }
+  size_t GetDataSize() { return offset_; }
+
+ protected:
+  void* data_;
+  int64 datasize_;
+  bool copy_;
+  int64 offset_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefBytesReader);
+};
+
+// Implementation of CefStreamWriter for byte buffers.
+class CefBytesWriter : public CefStreamWriter {
+ public:
+  explicit CefBytesWriter(size_t grow);
+  ~CefBytesWriter() override;
+
+  size_t Write(const void* ptr, size_t size, size_t n) override;
+  int Seek(int64 offset, int whence) override;
+  int64 Tell() override;
+  int Flush() override;
+  bool MayBlock() override { return false; }
+
+  void* GetData() { return data_; }
+  int64 GetDataSize() { return offset_; }
+  std::string GetDataString();
+
+ protected:
+  size_t Grow(size_t size);
+
+  size_t grow_;
+  void* data_;
+  int64 datasize_;
+  int64 offset_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefBytesWriter);
+};
+
+// Implementation of CefStreamReader for handlers.
+class CefHandlerReader : public CefStreamReader {
+ public:
+  explicit CefHandlerReader(CefRefPtr<CefReadHandler> handler)
+      : handler_(handler) {}
+
+  size_t Read(void* ptr, size_t size, size_t n) override {
+    return handler_->Read(ptr, size, n);
+  }
+  int Seek(int64 offset, int whence) override {
+    return handler_->Seek(offset, whence);
+  }
+  int64 Tell() override { return handler_->Tell(); }
+  int Eof() override { return handler_->Eof(); }
+  bool MayBlock() override { return handler_->MayBlock(); }
+
+ protected:
+  CefRefPtr<CefReadHandler> handler_;
+
+  IMPLEMENT_REFCOUNTING(CefHandlerReader);
+};
+
+// Implementation of CefStreamWriter for handlers.
+class CefHandlerWriter : public CefStreamWriter {
+ public:
+  explicit CefHandlerWriter(CefRefPtr<CefWriteHandler> handler)
+      : handler_(handler) {}
+
+  size_t Write(const void* ptr, size_t size, size_t n) override {
+    return handler_->Write(ptr, size, n);
+  }
+  int Seek(int64 offset, int whence) override {
+    return handler_->Seek(offset, whence);
+  }
+  int64 Tell() override { return handler_->Tell(); }
+  int Flush() override { return handler_->Flush(); }
+  bool MayBlock() override { return handler_->MayBlock(); }
+
+ protected:
+  CefRefPtr<CefWriteHandler> handler_;
+
+  IMPLEMENT_REFCOUNTING(CefHandlerWriter);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_STREAM_IMPL_H_
diff --git a/src/libcef/browser/thread_util.h b/src/libcef/browser/thread_util.h
new file mode 100644
index 0000000..c9d718c
--- /dev/null
+++ b/src/libcef/browser/thread_util.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_THREAD_UTIL_H_
+#define CEF_LIBCEF_BROWSER_THREAD_UTIL_H_
+#pragma once
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+#define CEF_UIT content::BrowserThread::UI
+#define CEF_IOT content::BrowserThread::IO
+
+#define CEF_CURRENTLY_ON(id) content::BrowserThread::CurrentlyOn(id)
+#define CEF_CURRENTLY_ON_UIT() CEF_CURRENTLY_ON(CEF_UIT)
+#define CEF_CURRENTLY_ON_IOT() CEF_CURRENTLY_ON(CEF_IOT)
+
+#define CEF_REQUIRE(id) DCHECK(CEF_CURRENTLY_ON(id))
+#define CEF_REQUIRE_UIT() CEF_REQUIRE(CEF_UIT)
+#define CEF_REQUIRE_IOT() CEF_REQUIRE(CEF_IOT)
+
+#define CEF_REQUIRE_RETURN(id, var)             \
+  if (!CEF_CURRENTLY_ON(id)) {                  \
+    NOTREACHED() << "called on invalid thread"; \
+    return var;                                 \
+  }
+#define CEF_REQUIRE_UIT_RETURN(var) CEF_REQUIRE_RETURN(CEF_UIT, var)
+#define CEF_REQUIRE_IOT_RETURN(var) CEF_REQUIRE_RETURN(CEF_IOT, var)
+
+#define CEF_REQUIRE_RETURN_VOID(id)             \
+  if (!CEF_CURRENTLY_ON(id)) {                  \
+    NOTREACHED() << "called on invalid thread"; \
+    return;                                     \
+  }
+#define CEF_REQUIRE_UIT_RETURN_VOID() CEF_REQUIRE_RETURN_VOID(CEF_UIT)
+#define CEF_REQUIRE_IOT_RETURN_VOID() CEF_REQUIRE_RETURN_VOID(CEF_IOT)
+
+#define CEF_POST_TASK(id, task) base::PostTask(FROM_HERE, {id}, task)
+#define CEF_POST_DELAYED_TASK(id, task, delay_ms) \
+  base::PostDelayedTask(FROM_HERE, {id}, task,    \
+                        base::TimeDelta::FromMilliseconds(delay_ms))
+
+// Post a blocking task with the specified |priority|. Tasks that have not
+// started executing at shutdown will never run. However, any task that has
+// already begun executing when shutdown is invoked will be allowed to continue
+// and will block shutdown until completion.
+// Tasks posted with this method are not guaranteed to run sequentially. Use
+// base::CreateSequencedTaskRunner instead if sequence is important.
+// Sequenced runners at various priorities that always execute all pending tasks
+// before shutdown are available via CefContentBrowserClient::*_task_runner()
+// and exposed by the CEF API.
+#define CEF_POST_BLOCKING_TASK(priority, task)                          \
+  base::PostTask(                                                       \
+      FROM_HERE,                                                        \
+      {base::ThreadPool(), priority,                                    \
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, base::MayBlock()}, \
+      task)
+
+// Post a blocking task that affects UI or responsiveness of future user
+// interactions. Do not use if an immediate response to a user interaction is
+// expected.
+#define CEF_POST_USER_VISIBLE_TASK(task) \
+  CEF_POST_BLOCKING_TASK(base::TaskPriority::USER_VISIBLE, task)
+
+// Post a blocking task where the user won't notice if it takes an arbitrarily
+// long time to complete.
+#define CEF_POST_BACKGROUND_TASK(task) \
+  CEF_POST_BLOCKING_TASK(base::TaskPriority::BEST_EFFORT, task)
+
+// Assert that blocking is allowed on the current thread.
+#define CEF_REQUIRE_BLOCKING()                   \
+  base::ScopedBlockingCall scoped_blocking_call( \
+      FROM_HERE, base::BlockingType::WILL_BLOCK)
+
+// Same as IMPLEMENT_REFCOUNTING() but using the specified Destructor.
+#define IMPLEMENT_REFCOUNTING_EX(ClassName, Destructor)              \
+ public:                                                             \
+  void AddRef() const OVERRIDE { ref_count_.AddRef(); }              \
+  bool Release() const OVERRIDE {                                    \
+    if (ref_count_.Release()) {                                      \
+      Destructor::Destruct(this);                                    \
+      return true;                                                   \
+    }                                                                \
+    return false;                                                    \
+  }                                                                  \
+  bool HasOneRef() const OVERRIDE { return ref_count_.HasOneRef(); } \
+  bool HasAtLeastOneRef() const OVERRIDE {                           \
+    return ref_count_.HasAtLeastOneRef();                            \
+  }                                                                  \
+                                                                     \
+ private:                                                            \
+  CefRefCount ref_count_
+
+#define IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(ClassName) \
+  IMPLEMENT_REFCOUNTING_EX(ClassName, content::BrowserThread::DeleteOnUIThread)
+
+#define IMPLEMENT_REFCOUNTING_DELETE_ON_IOT(ClassName) \
+  IMPLEMENT_REFCOUNTING_EX(ClassName, content::BrowserThread::DeleteOnIOThread)
+
+#endif  // CEF_LIBCEF_BROWSER_THREAD_UTIL_H_
diff --git a/src/libcef/browser/trace_impl.cc b/src/libcef/browser/trace_impl.cc
new file mode 100644
index 0000000..6fdab14
--- /dev/null
+++ b/src/libcef/browser/trace_impl.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_trace.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/trace_subscriber.h"
+
+#include "base/time/time.h"
+
+bool CefBeginTracing(const CefString& categories,
+                     CefRefPtr<CefCompletionCallback> callback) {
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return false;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  CefTraceSubscriber* subscriber = CefContext::Get()->GetTraceSubscriber();
+  if (!subscriber)
+    return false;
+
+  return subscriber->BeginTracing(categories, callback);
+}
+
+bool CefEndTracing(const CefString& tracing_file,
+                   CefRefPtr<CefEndTracingCallback> callback) {
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return false;
+  }
+
+  if (!CEF_CURRENTLY_ON_UIT()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  CefTraceSubscriber* subscriber = CefContext::Get()->GetTraceSubscriber();
+  if (!subscriber)
+    return false;
+
+  return subscriber->EndTracing(base::FilePath(tracing_file), callback);
+}
+
+int64 CefNowFromSystemTraceTime() {
+  return base::TimeTicks::Now().ToInternalValue();
+}
diff --git a/src/libcef/browser/trace_subscriber.cc b/src/libcef/browser/trace_subscriber.cc
new file mode 100644
index 0000000..6c5c3c1
--- /dev/null
+++ b/src/libcef/browser/trace_subscriber.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/browser/trace_subscriber.h"
+#include "include/cef_trace.h"
+#include "libcef/browser/thread_util.h"
+
+#include "base/files/file_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "content/public/browser/tracing_controller.h"
+
+namespace {
+
+// Create the temporary file and then execute |callback| on the thread
+// represented by |message_loop_proxy|.
+void CreateTemporaryFileOnBackgroundThread(
+    scoped_refptr<base::SequencedTaskRunner> message_loop_proxy,
+    base::Callback<void(const base::FilePath&)> callback) {
+  CEF_REQUIRE_BLOCKING();
+  base::FilePath file_path;
+  if (!base::CreateTemporaryFile(&file_path))
+    LOG(ERROR) << "Failed to create temporary file.";
+  message_loop_proxy->PostTask(FROM_HERE, base::Bind(callback, file_path));
+}
+
+// Release the wrapped callback object after completion.
+class CefCompletionCallbackWrapper : public CefCompletionCallback {
+ public:
+  explicit CefCompletionCallbackWrapper(
+      CefRefPtr<CefCompletionCallback> callback)
+      : callback_(callback) {}
+
+  void OnComplete() override {
+    if (callback_) {
+      callback_->OnComplete();
+      callback_ = nullptr;
+    }
+  }
+
+ private:
+  CefRefPtr<CefCompletionCallback> callback_;
+
+  IMPLEMENT_REFCOUNTING(CefCompletionCallbackWrapper);
+  DISALLOW_COPY_AND_ASSIGN(CefCompletionCallbackWrapper);
+};
+
+}  // namespace
+
+using content::TracingController;
+
+CefTraceSubscriber::CefTraceSubscriber()
+    : collecting_trace_data_(false), weak_factory_(this) {
+  CEF_REQUIRE_UIT();
+}
+
+CefTraceSubscriber::~CefTraceSubscriber() {
+  CEF_REQUIRE_UIT();
+  if (collecting_trace_data_)
+    TracingController::GetInstance()->StopTracing(nullptr);
+}
+
+bool CefTraceSubscriber::BeginTracing(
+    const std::string& categories,
+    CefRefPtr<CefCompletionCallback> callback) {
+  CEF_REQUIRE_UIT();
+
+  if (collecting_trace_data_)
+    return false;
+
+  collecting_trace_data_ = true;
+
+  TracingController::StartTracingDoneCallback done_callback;
+  if (callback.get()) {
+    // Work around a bug introduced in http://crbug.com/542390#c22 that keeps a
+    // reference to |done_callback| after execution.
+    callback = new CefCompletionCallbackWrapper(callback);
+    done_callback =
+        base::BindOnce(&CefCompletionCallback::OnComplete, callback.get());
+  }
+
+  TracingController::GetInstance()->StartTracing(
+      base::trace_event::TraceConfig(categories, ""), std::move(done_callback));
+  return true;
+}
+
+bool CefTraceSubscriber::EndTracing(const base::FilePath& tracing_file,
+                                    CefRefPtr<CefEndTracingCallback> callback) {
+  CEF_REQUIRE_UIT();
+
+  if (!collecting_trace_data_)
+    return false;
+
+  if (!callback.get()) {
+    // Discard the trace data.
+    collecting_trace_data_ = false;
+    TracingController::GetInstance()->StopTracing(nullptr);
+    return true;
+  }
+
+  if (tracing_file.empty()) {
+    // Create a new temporary file path on the FILE thread, then continue.
+    CEF_POST_USER_VISIBLE_TASK(
+        base::Bind(CreateTemporaryFileOnBackgroundThread,
+                   base::ThreadTaskRunnerHandle::Get(),
+                   base::Bind(&CefTraceSubscriber::ContinueEndTracing,
+                              weak_factory_.GetWeakPtr(), callback)));
+    return true;
+  }
+
+  base::Closure result_callback =
+      base::Bind(&CefTraceSubscriber::OnTracingFileResult,
+                 weak_factory_.GetWeakPtr(), callback, tracing_file);
+
+  TracingController::GetInstance()->StopTracing(
+      TracingController::CreateFileEndpoint(tracing_file, result_callback));
+  return true;
+}
+
+void CefTraceSubscriber::ContinueEndTracing(
+    CefRefPtr<CefEndTracingCallback> callback,
+    const base::FilePath& tracing_file) {
+  CEF_REQUIRE_UIT();
+  if (!tracing_file.empty())
+    EndTracing(tracing_file, callback);
+}
+
+void CefTraceSubscriber::OnTracingFileResult(
+    CefRefPtr<CefEndTracingCallback> callback,
+    const base::FilePath& tracing_file) {
+  CEF_REQUIRE_UIT();
+
+  collecting_trace_data_ = false;
+
+  callback->OnEndTracingComplete(tracing_file.value());
+}
diff --git a/src/libcef/browser/trace_subscriber.h b/src/libcef/browser/trace_subscriber.h
new file mode 100644
index 0000000..007b15e
--- /dev/null
+++ b/src/libcef/browser/trace_subscriber.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_TRACE_SUBSCRIBER_H_
+#define CEF_LIBCEF_BROWSER_TRACE_SUBSCRIBER_H_
+#pragma once
+
+#include "include/cef_trace.h"
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+
+// May only be accessed on the browser process UI thread.
+class CefTraceSubscriber {
+ public:
+  CefTraceSubscriber();
+  virtual ~CefTraceSubscriber();
+
+  bool BeginTracing(const std::string& categories,
+                    CefRefPtr<CefCompletionCallback> callback);
+  bool EndTracing(const base::FilePath& tracing_file,
+                  CefRefPtr<CefEndTracingCallback> callback);
+
+ private:
+  void ContinueEndTracing(CefRefPtr<CefEndTracingCallback> callback,
+                          const base::FilePath& tracing_file);
+  void OnTracingFileResult(CefRefPtr<CefEndTracingCallback> callback,
+                           const base::FilePath& tracing_file);
+
+  bool collecting_trace_data_;
+  base::WeakPtrFactory<CefTraceSubscriber> weak_factory_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_TRACE_SUBSCRIBER_H_
diff --git a/src/libcef/browser/views/basic_label_button_impl.cc b/src/libcef/browser/views/basic_label_button_impl.cc
new file mode 100644
index 0000000..19f3de1
--- /dev/null
+++ b/src/libcef/browser/views/basic_label_button_impl.cc
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/basic_label_button_impl.h"
+
+#include "libcef/browser/views/basic_label_button_view.h"
+
+// static
+CefRefPtr<CefLabelButton> CefLabelButton::CreateLabelButton(
+    CefRefPtr<CefButtonDelegate> delegate,
+    const CefString& text) {
+  return CefBasicLabelButtonImpl::Create(delegate, text);
+}
+
+// static
+CefRefPtr<CefBasicLabelButtonImpl> CefBasicLabelButtonImpl::Create(
+    CefRefPtr<CefButtonDelegate> delegate,
+    const CefString& text) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefBasicLabelButtonImpl> label_button =
+      new CefBasicLabelButtonImpl(delegate);
+  label_button->Initialize();
+  if (!text.empty())
+    label_button->SetText(text);
+  return label_button;
+}
+
+CefBasicLabelButtonImpl::CefBasicLabelButtonImpl(
+    CefRefPtr<CefButtonDelegate> delegate)
+    : ParentClass(delegate) {}
+
+views::LabelButton* CefBasicLabelButtonImpl::CreateRootView() {
+  return new CefBasicLabelButtonView(delegate());
+}
+
+void CefBasicLabelButtonImpl::InitializeRootView() {
+  static_cast<CefBasicLabelButtonView*>(root_view())->Initialize();
+}
diff --git a/src/libcef/browser/views/basic_label_button_impl.h b/src/libcef/browser/views/basic_label_button_impl.h
new file mode 100644
index 0000000..2849649
--- /dev/null
+++ b/src/libcef/browser/views/basic_label_button_impl.h
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BASIC_LABEL_BUTTON_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BASIC_LABEL_BUTTON_IMPL_H_
+#pragma once
+
+#include "include/views/cef_button_delegate.h"
+#include "include/views/cef_label_button.h"
+
+#include "libcef/browser/views/label_button_impl.h"
+
+#include "ui/views/controls/button/label_button.h"
+
+class CefBasicLabelButtonImpl : public CefLabelButtonImpl<views::LabelButton,
+                                                          CefLabelButton,
+                                                          CefButtonDelegate> {
+ public:
+  typedef CefLabelButtonImpl<views::LabelButton,
+                             CefLabelButton,
+                             CefButtonDelegate>
+      ParentClass;
+
+  // Create a new CefLabelButton instance. |delegate| may be nullptr.
+  static CefRefPtr<CefBasicLabelButtonImpl> Create(
+      CefRefPtr<CefButtonDelegate> delegate,
+      const CefString& text);
+
+  // CefViewAdapter methods:
+  std::string GetDebugType() override { return "LabelButton"; }
+
+ private:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefBasicLabelButtonImpl(CefRefPtr<CefButtonDelegate> delegate);
+
+  // CefViewImpl methods:
+  views::LabelButton* CreateRootView() override;
+  void InitializeRootView() override;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefBasicLabelButtonImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefBasicLabelButtonImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BASIC_LABEL_BUTTON_IMPL_H_
diff --git a/src/libcef/browser/views/basic_label_button_view.cc b/src/libcef/browser/views/basic_label_button_view.cc
new file mode 100644
index 0000000..607806b
--- /dev/null
+++ b/src/libcef/browser/views/basic_label_button_view.cc
@@ -0,0 +1,9 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/basic_label_button_view.h"
+
+CefBasicLabelButtonView::CefBasicLabelButtonView(
+    CefButtonDelegate* cef_delegate)
+    : ParentClass(cef_delegate) {}
diff --git a/src/libcef/browser/views/basic_label_button_view.h b/src/libcef/browser/views/basic_label_button_view.h
new file mode 100644
index 0000000..e6fda32
--- /dev/null
+++ b/src/libcef/browser/views/basic_label_button_view.h
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BASIC_LABEL_BUTTON_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BASIC_LABEL_BUTTON_VIEW_H_
+#pragma once
+
+#include "include/views/cef_button_delegate.h"
+
+#include "libcef/browser/views/label_button_view.h"
+
+#include "ui/views/controls/button/label_button.h"
+
+// Extend views::LabelButton with a no-argument constructor as required by the
+// CefViewView template and extend views::ButtonListener as required by the
+// CefButtonView template.
+class LabelButtonEx : public views::LabelButton, public views::ButtonListener {
+ public:
+  LabelButtonEx() : views::LabelButton(this, base::string16()) {}
+};
+
+class CefBasicLabelButtonView
+    : public CefLabelButtonView<LabelButtonEx, CefButtonDelegate> {
+ public:
+  typedef CefLabelButtonView<LabelButtonEx, CefButtonDelegate> ParentClass;
+
+  // |cef_delegate| may be nullptr.
+  explicit CefBasicLabelButtonView(CefButtonDelegate* cef_delegate);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefBasicLabelButtonView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BASIC_LABEL_BUTTON_VIEW_H_
diff --git a/src/libcef/browser/views/basic_panel_impl.cc b/src/libcef/browser/views/basic_panel_impl.cc
new file mode 100644
index 0000000..e7b36b3
--- /dev/null
+++ b/src/libcef/browser/views/basic_panel_impl.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/basic_panel_impl.h"
+
+#include "libcef/browser/views/basic_panel_view.h"
+
+// static
+CefRefPtr<CefPanel> CefPanel::CreatePanel(
+    CefRefPtr<CefPanelDelegate> delegate) {
+  return CefBasicPanelImpl::Create(delegate);
+}
+
+// static
+CefRefPtr<CefBasicPanelImpl> CefBasicPanelImpl::Create(
+    CefRefPtr<CefPanelDelegate> delegate) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefBasicPanelImpl> panel = new CefBasicPanelImpl(delegate);
+  panel->Initialize();
+  return panel;
+}
+
+CefBasicPanelImpl::CefBasicPanelImpl(CefRefPtr<CefPanelDelegate> delegate)
+    : ParentClass(delegate) {}
+
+views::View* CefBasicPanelImpl::CreateRootView() {
+  return new CefBasicPanelView(delegate());
+}
+
+void CefBasicPanelImpl::InitializeRootView() {
+  static_cast<CefBasicPanelView*>(root_view())->Initialize();
+}
diff --git a/src/libcef/browser/views/basic_panel_impl.h b/src/libcef/browser/views/basic_panel_impl.h
new file mode 100644
index 0000000..209be5e
--- /dev/null
+++ b/src/libcef/browser/views/basic_panel_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BASIC_PANEL_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BASIC_PANEL_IMPL_H_
+#pragma once
+
+#include "include/views/cef_panel.h"
+#include "include/views/cef_panel_delegate.h"
+
+#include "libcef/browser/views/panel_impl.h"
+
+#include "ui/views/view.h"
+
+class CefBasicPanelImpl
+    : public CefPanelImpl<views::View, CefPanel, CefPanelDelegate> {
+ public:
+  typedef CefPanelImpl<views::View, CefPanel, CefPanelDelegate> ParentClass;
+
+  // Create a new CefPanel instance. |delegate| may be nullptr.
+  static CefRefPtr<CefBasicPanelImpl> Create(
+      CefRefPtr<CefPanelDelegate> delegate);
+
+  // CefViewAdapter methods:
+  std::string GetDebugType() override { return "Panel"; }
+
+ private:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefBasicPanelImpl(CefRefPtr<CefPanelDelegate> delegate);
+
+  // CefViewImpl methods:
+  views::View* CreateRootView() override;
+  void InitializeRootView() override;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefBasicPanelImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefBasicPanelImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BASIC_PANEL_IMPL_H_
diff --git a/src/libcef/browser/views/basic_panel_view.cc b/src/libcef/browser/views/basic_panel_view.cc
new file mode 100644
index 0000000..7df66a3
--- /dev/null
+++ b/src/libcef/browser/views/basic_panel_view.cc
@@ -0,0 +1,8 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/basic_panel_view.h"
+
+CefBasicPanelView::CefBasicPanelView(CefPanelDelegate* cef_delegate)
+    : ParentClass(cef_delegate) {}
diff --git a/src/libcef/browser/views/basic_panel_view.h b/src/libcef/browser/views/basic_panel_view.h
new file mode 100644
index 0000000..477573d
--- /dev/null
+++ b/src/libcef/browser/views/basic_panel_view.h
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BASIC_PANEL_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BASIC_PANEL_VIEW_H_
+#pragma once
+
+#include "include/views/cef_panel_delegate.h"
+
+#include "libcef/browser/views/panel_view.h"
+
+class CefBasicPanelView : public CefPanelView<views::View, CefPanelDelegate> {
+ public:
+  typedef CefPanelView<views::View, CefPanelDelegate> ParentClass;
+
+  // |cef_delegate| may be nullptr.
+  explicit CefBasicPanelView(CefPanelDelegate* cef_delegate);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefBasicPanelView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BASIC_PANEL_VIEW_H_
diff --git a/src/libcef/browser/views/box_layout_impl.cc b/src/libcef/browser/views/box_layout_impl.cc
new file mode 100644
index 0000000..42e375f
--- /dev/null
+++ b/src/libcef/browser/views/box_layout_impl.cc
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/box_layout_impl.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/view_util.h"
+
+// static
+CefRefPtr<CefBoxLayoutImpl> CefBoxLayoutImpl::Create(
+    const CefBoxLayoutSettings& settings,
+    views::View* owner_view) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefBoxLayoutImpl> impl = new CefBoxLayoutImpl(settings);
+  impl->Initialize(owner_view);
+  return impl;
+}
+
+void CefBoxLayoutImpl::SetFlexForView(CefRefPtr<CefView> view, int flex) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  DCHECK_GE(flex, 0);
+  if (flex < 0)
+    return;
+
+  DCHECK(view && view->IsValid() && view->IsAttached());
+  if (!view || !view->IsValid() || !view->IsAttached())
+    return;
+
+  views::View* view_ptr = view_util::GetFor(view);
+  DCHECK_EQ(view_ptr->parent(), owner_view());
+  if (view_ptr->parent() != owner_view())
+    return;
+
+  layout()->SetFlexForView(view_ptr, flex);
+}
+
+void CefBoxLayoutImpl::ClearFlexForView(CefRefPtr<CefView> view) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  DCHECK(view && view->IsValid() && view->IsAttached());
+  if (!view || !view->IsValid() || !view->IsAttached())
+    return;
+
+  views::View* view_ptr = view_util::GetFor(view);
+  DCHECK_EQ(view_ptr->parent(), owner_view());
+  if (view_ptr->parent() != owner_view())
+    return;
+
+  layout()->ClearFlexForView(view_ptr);
+}
+
+CefBoxLayoutImpl::CefBoxLayoutImpl(const CefBoxLayoutSettings& settings)
+    : settings_(settings) {}
+
+views::BoxLayout* CefBoxLayoutImpl::CreateLayout() {
+  views::BoxLayout* layout = new views::BoxLayout(
+      settings_.horizontal ? views::BoxLayout::Orientation::kHorizontal
+                           : views::BoxLayout::Orientation::kVertical,
+      gfx::Insets(settings_.inside_border_vertical_spacing,
+                  settings_.inside_border_horizontal_spacing),
+      settings_.between_child_spacing);
+  layout->set_main_axis_alignment(
+      static_cast<views::BoxLayout::MainAxisAlignment>(
+          settings_.main_axis_alignment));
+  layout->set_cross_axis_alignment(
+      static_cast<views::BoxLayout::CrossAxisAlignment>(
+          settings_.cross_axis_alignment));
+  layout->set_inside_border_insets(gfx::Insets(
+      settings_.inside_border_insets.top, settings_.inside_border_insets.left,
+      settings_.inside_border_insets.bottom,
+      settings_.inside_border_insets.right));
+  layout->set_minimum_cross_axis_size(settings_.minimum_cross_axis_size);
+  if (settings_.default_flex > 0)
+    layout->SetDefaultFlex(settings_.default_flex);
+  return layout;
+}
diff --git a/src/libcef/browser/views/box_layout_impl.h b/src/libcef/browser/views/box_layout_impl.h
new file mode 100644
index 0000000..c1c4d9c
--- /dev/null
+++ b/src/libcef/browser/views/box_layout_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BOX_LAYOUT_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BOX_LAYOUT_IMPL_H_
+#pragma once
+
+#include "include/views/cef_box_layout.h"
+
+#include "libcef/browser/views/layout_impl.h"
+#include "ui/views/layout/box_layout.h"
+
+class CefBoxLayoutImpl : public CefLayoutImpl<views::BoxLayout, CefBoxLayout> {
+ public:
+  // Necessary for the CEF_REQUIRE_VALID_*() macros to compile.
+  typedef CefLayoutImpl<views::BoxLayout, CefBoxLayout> ParentClass;
+
+  // Create a new CefBoxLayout insance. |owner_view| must be non-nullptr.
+  static CefRefPtr<CefBoxLayoutImpl> Create(
+      const CefBoxLayoutSettings& settings,
+      views::View* owner_view);
+
+  // CefBoxLayout methods:
+  void SetFlexForView(CefRefPtr<CefView> view, int flex) override;
+  void ClearFlexForView(CefRefPtr<CefView> view) override;
+
+  // CefLayout methods:
+  CefRefPtr<CefBoxLayout> AsBoxLayout() override { return this; }
+
+ private:
+  explicit CefBoxLayoutImpl(const CefBoxLayoutSettings& settings);
+
+  views::BoxLayout* CreateLayout() override;
+
+  CefBoxLayoutSettings settings_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefBoxLayoutImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefBoxLayoutImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BOX_LAYOUT_IMPL_H_
diff --git a/src/libcef/browser/views/browser_platform_delegate_views.cc b/src/libcef/browser/views/browser_platform_delegate_views.cc
new file mode 100644
index 0000000..48c09f7
--- /dev/null
+++ b/src/libcef/browser/views/browser_platform_delegate_views.cc
@@ -0,0 +1,300 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/browser/views/browser_platform_delegate_views.h"
+
+#include <utility>
+
+#include "include/views/cef_window.h"
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/views/browser_view_impl.h"
+#include "libcef/browser/views/menu_runner_views.h"
+
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+// Default popup window delegate implementation.
+class PopupWindowDelegate : public CefWindowDelegate {
+ public:
+  explicit PopupWindowDelegate(CefRefPtr<CefBrowserView> browser_view)
+      : browser_view_(browser_view) {}
+
+  void OnWindowCreated(CefRefPtr<CefWindow> window) override {
+    window->AddChildView(browser_view_);
+    window->Show();
+    browser_view_->RequestFocus();
+  }
+
+  void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
+    browser_view_ = nullptr;
+  }
+
+  bool CanClose(CefRefPtr<CefWindow> window) override {
+    CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
+    if (browser)
+      return browser->GetHost()->TryCloseBrowser();
+    return true;
+  }
+
+ private:
+  CefRefPtr<CefBrowserView> browser_view_;
+
+  IMPLEMENT_REFCOUNTING(PopupWindowDelegate);
+  DISALLOW_COPY_AND_ASSIGN(PopupWindowDelegate);
+};
+
+}  // namespace
+
+CefBrowserPlatformDelegateViews::CefBrowserPlatformDelegateViews(
+    std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate,
+    CefRefPtr<CefBrowserViewImpl> browser_view)
+    : native_delegate_(std::move(native_delegate)) {
+  if (browser_view)
+    SetBrowserView(browser_view);
+  native_delegate_->set_windowless_handler(this);
+}
+
+void CefBrowserPlatformDelegateViews::SetBrowserView(
+    CefRefPtr<CefBrowserViewImpl> browser_view) {
+  DCHECK(!browser_view_);
+  DCHECK(browser_view);
+  browser_view_ = browser_view;
+}
+
+void CefBrowserPlatformDelegateViews::WebContentsCreated(
+    content::WebContents* web_contents) {
+  CefBrowserPlatformDelegate::WebContentsCreated(web_contents);
+
+  browser_view_->WebContentsCreated(web_contents);
+}
+
+void CefBrowserPlatformDelegateViews::BrowserCreated(
+    CefBrowserHostImpl* browser) {
+  CefBrowserPlatformDelegate::BrowserCreated(browser);
+
+  native_delegate_->set_browser(browser);
+  browser_view_->BrowserCreated(browser, GetBoundsChangedCallback());
+}
+
+void CefBrowserPlatformDelegateViews::NotifyBrowserCreated() {
+  DCHECK(browser_view_);
+  DCHECK(browser_);
+  if (browser_view_->delegate())
+    browser_view_->delegate()->OnBrowserCreated(browser_view_, browser_);
+}
+
+void CefBrowserPlatformDelegateViews::NotifyBrowserDestroyed() {
+  DCHECK(browser_view_);
+  DCHECK(browser_);
+  if (browser_view_->delegate())
+    browser_view_->delegate()->OnBrowserDestroyed(browser_view_, browser_);
+}
+
+void CefBrowserPlatformDelegateViews::BrowserDestroyed(
+    CefBrowserHostImpl* browser) {
+  CefBrowserPlatformDelegate::BrowserDestroyed(browser);
+
+  native_delegate_->set_browser(nullptr);
+  browser_view_->BrowserDestroyed(browser);
+  browser_view_ = nullptr;
+}
+
+bool CefBrowserPlatformDelegateViews::CreateHostWindow() {
+  // Nothing to do here.
+  return true;
+}
+
+void CefBrowserPlatformDelegateViews::CloseHostWindow() {
+  views::Widget* widget = GetWindowWidget();
+  if (widget && !widget->IsClosed())
+    widget->Close();
+}
+
+CefWindowHandle CefBrowserPlatformDelegateViews::GetHostWindowHandle() const {
+  return view_util::GetWindowHandle(GetWindowWidget());
+}
+
+views::Widget* CefBrowserPlatformDelegateViews::GetWindowWidget() const {
+  if (browser_view_->root_view())
+    return browser_view_->root_view()->GetWidget();
+  return nullptr;
+}
+
+CefRefPtr<CefBrowserView> CefBrowserPlatformDelegateViews::GetBrowserView()
+    const {
+  return browser_view_.get();
+}
+
+void CefBrowserPlatformDelegateViews::PopupWebContentsCreated(
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefClient> client,
+    content::WebContents* new_web_contents,
+    CefBrowserPlatformDelegate* new_platform_delegate,
+    bool is_devtools) {
+  DCHECK(new_platform_delegate->IsViewsHosted());
+  CefBrowserPlatformDelegateViews* new_platform_delegate_impl =
+      static_cast<CefBrowserPlatformDelegateViews*>(new_platform_delegate);
+
+  CefRefPtr<CefBrowserViewDelegate> new_delegate;
+  if (browser_view_->delegate()) {
+    new_delegate = browser_view_->delegate()->GetDelegateForPopupBrowserView(
+        browser_view_.get(), settings, client, is_devtools);
+  }
+
+  // Create a new BrowserView for the popup.
+  CefRefPtr<CefBrowserViewImpl> new_browser_view =
+      CefBrowserViewImpl::CreateForPopup(settings, new_delegate);
+
+  // Associate the PlatformDelegate with the new BrowserView.
+  new_platform_delegate_impl->SetBrowserView(new_browser_view);
+}
+
+void CefBrowserPlatformDelegateViews::PopupBrowserCreated(
+    CefBrowserHostImpl* new_browser,
+    bool is_devtools) {
+  CefRefPtr<CefBrowserView> new_browser_view =
+      CefBrowserView::GetForBrowser(new_browser);
+  DCHECK(new_browser_view);
+
+  bool popup_handled = false;
+  if (browser_view_->delegate()) {
+    popup_handled = browser_view_->delegate()->OnPopupBrowserViewCreated(
+        browser_view_.get(), new_browser_view.get(), is_devtools);
+  }
+
+  if (!popup_handled) {
+    CefWindow::CreateTopLevelWindow(
+        new PopupWindowDelegate(new_browser_view.get()));
+  }
+}
+
+bool CefBrowserPlatformDelegateViews::CanUseSharedTexture() const {
+  return native_delegate_->CanUseSharedTexture();
+}
+
+bool CefBrowserPlatformDelegateViews::CanUseExternalBeginFrame() const {
+  return native_delegate_->CanUseExternalBeginFrame();
+}
+
+SkColor CefBrowserPlatformDelegateViews::GetBackgroundColor() const {
+  return native_delegate_->GetBackgroundColor();
+}
+
+void CefBrowserPlatformDelegateViews::WasResized() {
+  native_delegate_->WasResized();
+}
+
+void CefBrowserPlatformDelegateViews::SendKeyEvent(const CefKeyEvent& event) {
+  native_delegate_->SendKeyEvent(event);
+}
+
+void CefBrowserPlatformDelegateViews::SendMouseClickEvent(
+    const CefMouseEvent& event,
+    CefBrowserHost::MouseButtonType type,
+    bool mouseUp,
+    int clickCount) {
+  native_delegate_->SendMouseClickEvent(event, type, mouseUp, clickCount);
+}
+
+void CefBrowserPlatformDelegateViews::SendMouseMoveEvent(
+    const CefMouseEvent& event,
+    bool mouseLeave) {
+  native_delegate_->SendMouseMoveEvent(event, mouseLeave);
+}
+
+void CefBrowserPlatformDelegateViews::SendMouseWheelEvent(
+    const CefMouseEvent& event,
+    int deltaX,
+    int deltaY) {
+  native_delegate_->SendMouseWheelEvent(event, deltaX, deltaY);
+}
+
+void CefBrowserPlatformDelegateViews::SendTouchEvent(
+    const CefTouchEvent& event) {
+  native_delegate_->SendTouchEvent(event);
+}
+
+void CefBrowserPlatformDelegateViews::SendFocusEvent(bool setFocus) {
+  // Will result in a call to WebContents::Focus().
+  if (setFocus && browser_view_->root_view())
+    browser_view_->root_view()->RequestFocus();
+}
+
+gfx::Point CefBrowserPlatformDelegateViews::GetScreenPoint(
+    const gfx::Point& view_pt) const {
+  if (!browser_view_->root_view())
+    return view_pt;
+
+  gfx::Point screen_point = view_pt;
+  view_util::ConvertPointToScreen(browser_view_->root_view(), &screen_point,
+                                  true);
+  return screen_point;
+}
+
+void CefBrowserPlatformDelegateViews::ViewText(const std::string& text) {
+  native_delegate_->ViewText(text);
+}
+
+bool CefBrowserPlatformDelegateViews::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  // The BrowserView will handle accelerators.
+  return browser_view_->HandleKeyboardEvent(event);
+}
+
+CefEventHandle CefBrowserPlatformDelegateViews::GetEventHandle(
+    const content::NativeWebKeyboardEvent& event) const {
+  return native_delegate_->GetEventHandle(event);
+}
+
+std::unique_ptr<CefFileDialogRunner>
+CefBrowserPlatformDelegateViews::CreateFileDialogRunner() {
+  return native_delegate_->CreateFileDialogRunner();
+}
+
+std::unique_ptr<CefJavaScriptDialogRunner>
+CefBrowserPlatformDelegateViews::CreateJavaScriptDialogRunner() {
+  return native_delegate_->CreateJavaScriptDialogRunner();
+}
+
+std::unique_ptr<CefMenuRunner>
+CefBrowserPlatformDelegateViews::CreateMenuRunner() {
+  return base::WrapUnique(new CefMenuRunnerViews(browser_view_.get()));
+}
+
+bool CefBrowserPlatformDelegateViews::IsWindowless() const {
+  return false;
+}
+
+bool CefBrowserPlatformDelegateViews::IsViewsHosted() const {
+  return true;
+}
+
+gfx::Point CefBrowserPlatformDelegateViews::GetDialogPosition(
+    const gfx::Size& size) {
+  const gfx::Rect& bounds = browser_view_->root_view()->GetBoundsInScreen();
+
+  // Offset relative to the top-level content view.
+  gfx::Point offset = bounds.origin();
+  view_util::ConvertPointFromScreen(
+      browser_view_->root_view()->GetWidget()->GetRootView(), &offset, false);
+
+  return gfx::Point(offset.x() + (bounds.width() - size.width()) / 2,
+                    offset.y() + (bounds.height() - size.height()) / 2);
+}
+
+gfx::Size CefBrowserPlatformDelegateViews::GetMaximumDialogSize() {
+  return browser_view_->root_view()->GetBoundsInScreen().size();
+}
+
+CefWindowHandle CefBrowserPlatformDelegateViews::GetParentWindowHandle() const {
+  return GetHostWindowHandle();
+}
+
+gfx::Point CefBrowserPlatformDelegateViews::GetParentScreenPoint(
+    const gfx::Point& view) const {
+  return GetScreenPoint(view);
+}
diff --git a/src/libcef/browser/views/browser_platform_delegate_views.h b/src/libcef/browser/views/browser_platform_delegate_views.h
new file mode 100644
index 0000000..d106194
--- /dev/null
+++ b/src/libcef/browser/views/browser_platform_delegate_views.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_
+
+#include "libcef/browser/browser_platform_delegate.h"
+#include "libcef/browser/native/browser_platform_delegate_native.h"
+#include "libcef/browser/views/browser_view_impl.h"
+
+// Implementation of Views-based browser functionality.
+class CefBrowserPlatformDelegateViews
+    : public CefBrowserPlatformDelegate,
+      public CefBrowserPlatformDelegateNative::WindowlessHandler {
+ public:
+  // Platform-specific behaviors will be delegated to |native_delegate|.
+  // |browser_view_getter| may be initially empty for popup browsers.
+  CefBrowserPlatformDelegateViews(
+      std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate,
+      CefRefPtr<CefBrowserViewImpl> browser_view);
+
+  // CefBrowserPlatformDelegate methods:
+  void WebContentsCreated(content::WebContents* web_contents) override;
+  void BrowserCreated(CefBrowserHostImpl* browser) override;
+  void NotifyBrowserCreated() override;
+  void NotifyBrowserDestroyed() override;
+  void BrowserDestroyed(CefBrowserHostImpl* browser) override;
+  bool CreateHostWindow() override;
+  void CloseHostWindow() override;
+  CefWindowHandle GetHostWindowHandle() const override;
+  views::Widget* GetWindowWidget() const override;
+  CefRefPtr<CefBrowserView> GetBrowserView() const override;
+  void PopupWebContentsCreated(
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefClient> client,
+      content::WebContents* new_web_contents,
+      CefBrowserPlatformDelegate* new_platform_delegate,
+      bool is_devtools) override;
+  void PopupBrowserCreated(CefBrowserHostImpl* new_browser,
+                           bool is_devtools) override;
+  bool CanUseSharedTexture() const override;
+  bool CanUseExternalBeginFrame() const override;
+  SkColor GetBackgroundColor() const override;
+  void WasResized() override;
+  void SendKeyEvent(const CefKeyEvent& event) override;
+  void SendMouseClickEvent(const CefMouseEvent& event,
+                           CefBrowserHost::MouseButtonType type,
+                           bool mouseUp,
+                           int clickCount) override;
+  void SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) override;
+  void SendMouseWheelEvent(const CefMouseEvent& event,
+                           int deltaX,
+                           int deltaY) override;
+  void SendTouchEvent(const CefTouchEvent& event) override;
+  void SendFocusEvent(bool setFocus) override;
+  gfx::Point GetScreenPoint(const gfx::Point& view) const override;
+  void ViewText(const std::string& text) override;
+  bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) override;
+  CefEventHandle GetEventHandle(
+      const content::NativeWebKeyboardEvent& event) const override;
+  std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
+  std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
+      override;
+  std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;
+  bool IsWindowless() const override;
+  bool IsViewsHosted() const override;
+  gfx::Point GetDialogPosition(const gfx::Size& size) override;
+  gfx::Size GetMaximumDialogSize() override;
+
+  // CefBrowserPlatformDelegateNative::WindowlessHandler methods:
+  CefWindowHandle GetParentWindowHandle() const override;
+  gfx::Point GetParentScreenPoint(const gfx::Point& view) const override;
+
+ private:
+  void SetBrowserView(CefRefPtr<CefBrowserViewImpl> browser_view);
+
+  std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate_;
+  CefRefPtr<CefBrowserViewImpl> browser_view_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_
diff --git a/src/libcef/browser/views/browser_view_impl.cc b/src/libcef/browser/views/browser_view_impl.cc
new file mode 100644
index 0000000..deebcc3
--- /dev/null
+++ b/src/libcef/browser/views/browser_view_impl.cc
@@ -0,0 +1,243 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/browser_view_impl.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/browser_util.h"
+#include "libcef/browser/context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/window_impl.h"
+
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "ui/content_accelerators/accelerator_util.h"
+
+// static
+CefRefPtr<CefBrowserView> CefBrowserView::CreateBrowserView(
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context,
+    CefRefPtr<CefBrowserViewDelegate> delegate) {
+  return CefBrowserViewImpl::Create(client, url, settings, extra_info,
+                                    request_context, delegate);
+}
+
+// static
+CefRefPtr<CefBrowserView> CefBrowserView::GetForBrowser(
+    CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefBrowserHostImpl* browser_impl =
+      static_cast<CefBrowserHostImpl*>(browser.get());
+  if (browser_impl && browser_impl->IsViewsHosted())
+    return browser_impl->GetBrowserView();
+  return nullptr;
+}
+
+// static
+CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::Create(
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context,
+    CefRefPtr<CefBrowserViewDelegate> delegate) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefBrowserViewImpl> browser_view = new CefBrowserViewImpl(delegate);
+  browser_view->SetPendingBrowserCreateParams(client, url, settings, extra_info,
+                                              request_context);
+  browser_view->Initialize();
+  browser_view->SetDefaults(settings);
+  return browser_view;
+}
+
+// static
+CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::CreateForPopup(
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefBrowserViewDelegate> delegate) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefBrowserViewImpl> browser_view = new CefBrowserViewImpl(delegate);
+  browser_view->Initialize();
+  browser_view->SetDefaults(settings);
+  return browser_view;
+}
+
+void CefBrowserViewImpl::WebContentsCreated(
+    content::WebContents* web_contents) {
+  if (root_view())
+    root_view()->SetWebContents(web_contents);
+}
+
+void CefBrowserViewImpl::BrowserCreated(
+    CefBrowserHostImpl* browser,
+    base::RepeatingClosure on_bounds_changed) {
+  browser_ = browser;
+  on_bounds_changed_ = on_bounds_changed;
+}
+
+void CefBrowserViewImpl::BrowserDestroyed(CefBrowserHostImpl* browser) {
+  DCHECK_EQ(browser, browser_);
+  browser_ = nullptr;
+
+  if (root_view())
+    root_view()->SetWebContents(nullptr);
+}
+
+bool CefBrowserViewImpl::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  if (!root_view())
+    return false;
+
+  views::FocusManager* focus_manager = root_view()->GetFocusManager();
+  if (!focus_manager)
+    return false;
+
+  if (HandleAccelerator(event, focus_manager))
+    return true;
+
+  // Give the CefWindowDelegate a chance to handle the event.
+  CefRefPtr<CefWindow> window =
+      view_util::GetWindowFor(root_view()->GetWidget());
+  CefWindowImpl* window_impl = static_cast<CefWindowImpl*>(window.get());
+  if (window_impl) {
+    CefKeyEvent cef_event;
+    if (browser_util::GetCefKeyEvent(event, cef_event) &&
+        window_impl->OnKeyEvent(cef_event)) {
+      return true;
+    }
+  }
+
+  // Proceed with default native handling.
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
+                                                               focus_manager);
+}
+
+CefRefPtr<CefBrowser> CefBrowserViewImpl::GetBrowser() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  return browser_;
+}
+
+void CefBrowserViewImpl::SetPreferAccelerators(bool prefer_accelerators) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (root_view())
+    root_view()->set_allow_accelerators(prefer_accelerators);
+}
+
+void CefBrowserViewImpl::SetBackgroundColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::SetBackgroundColor(color);
+  if (root_view())
+    root_view()->SetResizeBackgroundColor(color);
+}
+
+void CefBrowserViewImpl::Detach() {
+  ParentClass::Detach();
+
+  // root_view() will be nullptr now.
+  DCHECK(!root_view());
+
+  if (browser_) {
+    // |browser_| will disappear when WindowDestroyed() indirectly calls
+    // BrowserDestroyed() so keep a reference.
+    CefRefPtr<CefBrowserHostImpl> browser = browser_;
+
+    // Force the browser to be destroyed.
+    browser->WindowDestroyed();
+  }
+}
+
+void CefBrowserViewImpl::GetDebugInfo(base::DictionaryValue* info,
+                                      bool include_children) {
+  ParentClass::GetDebugInfo(info, include_children);
+  if (browser_)
+    info->SetString("url", browser_->GetMainFrame()->GetURL().ToString());
+}
+
+void CefBrowserViewImpl::OnBrowserViewAdded() {
+  if (!browser_ && pending_browser_create_params_) {
+    // Top-level browsers will be created when this view is added to the views
+    // hierarchy.
+    pending_browser_create_params_->browser_view = this;
+
+    CefBrowserHostImpl::Create(*pending_browser_create_params_);
+    DCHECK(browser_);
+
+    pending_browser_create_params_.reset(nullptr);
+  }
+}
+
+void CefBrowserViewImpl::OnBoundsChanged() {
+  if (!on_bounds_changed_.is_null())
+    on_bounds_changed_.Run();
+}
+
+CefBrowserViewImpl::CefBrowserViewImpl(
+    CefRefPtr<CefBrowserViewDelegate> delegate)
+    : ParentClass(delegate) {}
+
+void CefBrowserViewImpl::SetPendingBrowserCreateParams(
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  DCHECK(!pending_browser_create_params_);
+  pending_browser_create_params_.reset(new CefBrowserHostImpl::CreateParams());
+  pending_browser_create_params_->client = client;
+  pending_browser_create_params_->url = GURL(url.ToString());
+  pending_browser_create_params_->settings = settings;
+  pending_browser_create_params_->extra_info = extra_info;
+  pending_browser_create_params_->request_context = request_context;
+}
+
+void CefBrowserViewImpl::SetDefaults(const CefBrowserSettings& settings) {
+  SetBackgroundColor(
+      CefContext::Get()->GetBackgroundColor(&settings, STATE_DISABLED));
+}
+
+CefBrowserViewView* CefBrowserViewImpl::CreateRootView() {
+  return new CefBrowserViewView(delegate(), this);
+}
+
+void CefBrowserViewImpl::InitializeRootView() {
+  static_cast<CefBrowserViewView*>(root_view())->Initialize();
+}
+
+bool CefBrowserViewImpl::HandleAccelerator(
+    const content::NativeWebKeyboardEvent& event,
+    views::FocusManager* focus_manager) {
+  // Previous calls to TranslateMessage can generate Char events as well as
+  // RawKeyDown events, even if the latter triggered an accelerator.  In these
+  // cases, we discard the Char events.
+  if (event.GetType() == blink::WebInputEvent::kChar &&
+      ignore_next_char_event_) {
+    ignore_next_char_event_ = false;
+    return true;
+  }
+
+  // It's necessary to reset this flag, because a RawKeyDown event may not
+  // always generate a Char event.
+  ignore_next_char_event_ = false;
+
+  if (event.GetType() == blink::WebInputEvent::kRawKeyDown) {
+    ui::Accelerator accelerator =
+        ui::GetAcceleratorFromNativeWebKeyboardEvent(event);
+
+    // This is tricky: we want to set ignore_next_char_event_ if
+    // ProcessAccelerator returns true. But ProcessAccelerator might delete
+    // |this| if the accelerator is a "close tab" one. So we speculatively
+    // set the flag and fix it if no event was handled.
+    ignore_next_char_event_ = true;
+
+    if (focus_manager->ProcessAccelerator(accelerator))
+      return true;
+
+    // ProcessAccelerator didn't handle the accelerator, so we know both
+    // that |this| is still valid, and that we didn't want to set the flag.
+    ignore_next_char_event_ = false;
+  }
+
+  return false;
+}
diff --git a/src/libcef/browser/views/browser_view_impl.h b/src/libcef/browser/views/browser_view_impl.h
new file mode 100644
index 0000000..f35e647
--- /dev/null
+++ b/src/libcef/browser/views/browser_view_impl.h
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_IMPL_H_
+#pragma once
+
+#include "include/cef_client.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_browser_view_delegate.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/views/browser_view_view.h"
+#include "libcef/browser/views/view_impl.h"
+
+#include "base/callback_forward.h"
+#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
+
+class CefBrowserViewImpl : public CefViewImpl<CefBrowserViewView,
+                                              CefBrowserView,
+                                              CefBrowserViewDelegate>,
+                           public CefBrowserViewView::Delegate {
+ public:
+  typedef CefViewImpl<CefBrowserViewView,
+                      CefBrowserView,
+                      CefBrowserViewDelegate>
+      ParentClass;
+
+  // Create a new CefBrowserView instance. |delegate| may be nullptr.
+  static CefRefPtr<CefBrowserViewImpl> Create(
+      CefRefPtr<CefClient> client,
+      const CefString& url,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefDictionaryValue> extra_info,
+      CefRefPtr<CefRequestContext> request_context,
+      CefRefPtr<CefBrowserViewDelegate> delegate);
+
+  // Create a new CefBrowserView instance for a popup. |delegate| may be
+  // nullptr.
+  static CefRefPtr<CefBrowserViewImpl> CreateForPopup(
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefBrowserViewDelegate> delegate);
+
+  // Called from CefBrowserPlatformDelegateViews.
+  void WebContentsCreated(content::WebContents* web_contents);
+  void BrowserCreated(CefBrowserHostImpl* browser,
+                      base::RepeatingClosure on_bounds_changed);
+  void BrowserDestroyed(CefBrowserHostImpl* browser);
+
+  // Called to handle accelerators when the event is unhandled by the web
+  // content and the browser client.
+  bool HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event);
+
+  // CefBrowserView methods:
+  CefRefPtr<CefBrowser> GetBrowser() override;
+  void SetPreferAccelerators(bool prefer_accelerators) override;
+
+  // CefView methods:
+  CefRefPtr<CefBrowserView> AsBrowserView() override { return this; }
+  void SetBackgroundColor(cef_color_t color) override;
+
+  // CefViewAdapter methods:
+  void Detach() override;
+  std::string GetDebugType() override { return "BrowserView"; }
+  void GetDebugInfo(base::DictionaryValue* info,
+                    bool include_children) override;
+
+  // CefBrowserViewView::Delegate methods:
+  void OnBrowserViewAdded() override;
+  void OnBoundsChanged() override;
+
+ private:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefBrowserViewImpl(CefRefPtr<CefBrowserViewDelegate> delegate);
+
+  void SetPendingBrowserCreateParams(
+      CefRefPtr<CefClient> client,
+      const CefString& url,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefDictionaryValue> extra_info,
+      CefRefPtr<CefRequestContext> request_context);
+
+  void SetDefaults(const CefBrowserSettings& settings);
+
+  // CefViewImpl methods:
+  CefBrowserViewView* CreateRootView() override;
+  void InitializeRootView() override;
+
+  // Logic extracted from UnhandledKeyboardEventHandler::HandleKeyboardEvent for
+  // the handling of accelerators. Returns true if the event was handled by the
+  // accelerator.
+  bool HandleAccelerator(const content::NativeWebKeyboardEvent& event,
+                         views::FocusManager* focus_manager);
+
+  std::unique_ptr<CefBrowserHostImpl::CreateParams>
+      pending_browser_create_params_;
+
+  CefRefPtr<CefBrowserHostImpl> browser_;
+
+  views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
+  bool ignore_next_char_event_ = false;
+
+  base::RepeatingClosure on_bounds_changed_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefBrowserViewImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserViewImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_IMPL_H_
diff --git a/src/libcef/browser/views/browser_view_view.cc b/src/libcef/browser/views/browser_view_view.cc
new file mode 100644
index 0000000..35bbf94
--- /dev/null
+++ b/src/libcef/browser/views/browser_view_view.cc
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/browser_view_view.h"
+
+#include "libcef/browser/views/browser_view_impl.h"
+
+CefBrowserViewView::CefBrowserViewView(CefBrowserViewDelegate* cef_delegate,
+                                       Delegate* browser_view_delegate)
+    : ParentClass(cef_delegate), browser_view_delegate_(browser_view_delegate) {
+  DCHECK(browser_view_delegate_);
+}
+
+void CefBrowserViewView::ViewHierarchyChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  ParentClass::ViewHierarchyChanged(details);
+  if (details.is_add && details.child == this) {
+    gfx::Size size = GetPreferredSize();
+    if (size.IsEmpty()) {
+      // No size was provided for this View. Size it to the parent by default
+      // or, depending on the Layout, the browser may be initially 0x0 size and
+      // will not display until the parent is next resized (resulting in a call
+      // to WebView::OnBoundsChanged). For example, this can happen when adding
+      // this View to a CefWindow with FillLayout and then calling
+      // CefWindow::Show() without first resizing the CefWindow.
+      size = details.parent->GetPreferredSize();
+      if (!size.IsEmpty())
+        SetSize(size);
+    }
+
+    browser_view_delegate_->OnBrowserViewAdded();
+  }
+}
+
+void CefBrowserViewView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+  ParentClass::OnBoundsChanged(previous_bounds);
+  browser_view_delegate_->OnBoundsChanged();
+}
diff --git a/src/libcef/browser/views/browser_view_view.h b/src/libcef/browser/views/browser_view_view.h
new file mode 100644
index 0000000..7a50d8a
--- /dev/null
+++ b/src/libcef/browser/views/browser_view_view.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_VIEW_H_
+#pragma once
+
+#include "include/views/cef_browser_view_delegate.h"
+
+#include "libcef/browser/views/view_view.h"
+
+#include "ui/views/controls/webview/webview.h"
+
+// Extend views::WebView with a no-argument constructor as required by the
+// CefViewView template.
+class WebViewEx : public views::WebView {
+ public:
+  WebViewEx() : views::WebView(nullptr) {}
+};
+
+class CefBrowserViewView
+    : public CefViewView<WebViewEx, CefBrowserViewDelegate> {
+ public:
+  typedef CefViewView<WebViewEx, CefBrowserViewDelegate> ParentClass;
+
+  class Delegate {
+   public:
+    // Called when the BrowserView has been added to a parent view.
+    virtual void OnBrowserViewAdded() = 0;
+
+    // Called when the BrowserView bounds have changed.
+    virtual void OnBoundsChanged() = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // |cef_delegate| may be nullptr.
+  // |browser_view_delegate| must be non-nullptr.
+  CefBrowserViewView(CefBrowserViewDelegate* cef_delegate,
+                     Delegate* browser_view_delegate);
+
+  // View methods:
+  void ViewHierarchyChanged(
+      const views::ViewHierarchyChangedDetails& details) override;
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
+
+ private:
+  // Not owned by this object.
+  Delegate* browser_view_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserViewView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_VIEW_H_
diff --git a/src/libcef/browser/views/button_impl.h b/src/libcef/browser/views/button_impl.h
new file mode 100644
index 0000000..76456a2
--- /dev/null
+++ b/src/libcef/browser/views/button_impl.h
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BUTTON_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BUTTON_IMPL_H_
+#pragma once
+
+#include "include/views/cef_button.h"
+#include "include/views/cef_label_button.h"
+
+#include "libcef/browser/views/view_impl.h"
+
+#include "base/logging.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/views/controls/button/button.h"
+
+// Helpers for template boiler-plate.
+#define CEF_BUTTON_IMPL_T CEF_VIEW_IMPL_T
+#define CEF_BUTTON_IMPL_A CEF_VIEW_IMPL_A
+#define CEF_BUTTON_IMPL_D CefButtonImpl<CEF_BUTTON_IMPL_A>
+
+// Template for implementing CefButton-derived classes. See comments in
+// view_impl.h for a usage overview.
+CEF_BUTTON_IMPL_T class CefButtonImpl : public CEF_VIEW_IMPL_D {
+ public:
+  typedef CEF_VIEW_IMPL_D ParentClass;
+
+  // CefButton methods. When adding new As*() methods make sure to update
+  // CefViewAdapter::GetFor() in view_adapter.cc.
+  CefRefPtr<CefLabelButton> AsLabelButton() override { return nullptr; }
+  void SetState(cef_button_state_t state) override;
+  cef_button_state_t GetState() override;
+  void SetInkDropEnabled(bool enabled) override;
+  void SetTooltipText(const CefString& tooltip_text) override;
+  void SetAccessibleName(const CefString& name) override;
+
+  // CefView methods:
+  CefRefPtr<CefButton> AsButton() override { return this; }
+  void SetFocusable(bool focusable) override;
+
+ protected:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefButtonImpl(CefRefPtr<CefViewDelegateClass> delegate)
+      : ParentClass(delegate) {}
+};
+
+CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetState(cef_button_state_t state) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  views::Button::ButtonState old_state = ParentClass::root_view()->state();
+  views::Button::ButtonState new_state =
+      static_cast<views::Button::ButtonState>(state);
+
+  if (ParentClass::root_view()->ink_drop_mode() !=
+          views::Button::InkDropMode::OFF &&
+      !ParentClass::root_view()->IsFocusable()) {
+    // Ink drop state does not get set properly on state change when the button
+    // is non-focusable.
+    views::InkDropState ink_state = views::InkDropState::HIDDEN;
+    if (new_state == views::Button::STATE_PRESSED) {
+      ink_state = views::InkDropState::ACTIVATED;
+    } else if (old_state == views::Button::STATE_PRESSED) {
+      ink_state = views::InkDropState::DEACTIVATED;
+    }
+    ParentClass::root_view()->AnimateInkDrop(ink_state, nullptr);
+  }
+
+  ParentClass::root_view()->SetState(new_state);
+}
+
+CEF_BUTTON_IMPL_T cef_button_state_t CEF_BUTTON_IMPL_D::GetState() {
+  CEF_REQUIRE_VALID_RETURN(CEF_BUTTON_STATE_NORMAL);
+  return static_cast<cef_button_state_t>(ParentClass::root_view()->state());
+}
+
+CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetInkDropEnabled(bool enabled) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetInkDropMode(
+      enabled ? views::InkDropHostView::InkDropMode::ON
+              : views::InkDropHostView::InkDropMode::OFF);
+  if (enabled) {
+    ParentClass::root_view()->set_ink_drop_base_color(
+        color_utils::BlendTowardMaxContrast(
+            ParentClass::root_view()->background()->get_color(), 0x61));
+  }
+}
+
+CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetTooltipText(
+    const CefString& tooltip_text) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetTooltipText(tooltip_text);
+}
+
+CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetAccessibleName(
+    const CefString& name) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetAccessibleName(name);
+}
+
+CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetFocusable(bool focusable) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->set_request_focus_on_press(focusable);
+  ParentClass::SetFocusable(focusable);
+}
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BUTTON_IMPL_H_
diff --git a/src/libcef/browser/views/button_view.h b/src/libcef/browser/views/button_view.h
new file mode 100644
index 0000000..6a112c6
--- /dev/null
+++ b/src/libcef/browser/views/button_view.h
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_BUTTON_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_BUTTON_VIEW_H_
+#pragma once
+
+#include "include/views/cef_button_delegate.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/view_view.h"
+
+#include "base/logging.h"
+#include "ui/views/controls/button/button.h"
+
+// Helpers for template boiler-plate.
+#define CEF_BUTTON_VIEW_T CEF_VIEW_VIEW_T
+#define CEF_BUTTON_VIEW_A CEF_VIEW_VIEW_A
+#define CEF_BUTTON_VIEW_D CefButtonView<CEF_BUTTON_VIEW_A>
+
+// Template for implementing views::Button-derived classes. The
+// views::Button-derived type passed to this template must extend
+// views::ButtonListener (for example, see LabelButtonEx from
+// basic_label_button_view.h). See comments in view_impl.h for a usage overview.
+CEF_BUTTON_VIEW_T class CefButtonView : public CEF_VIEW_VIEW_D {
+ public:
+  typedef CEF_VIEW_VIEW_D ParentClass;
+
+  // |cef_delegate| may be nullptr.
+  explicit CefButtonView(CefViewDelegateClass* cef_delegate)
+      : ParentClass(cef_delegate) {}
+
+  // Returns the CefButton associated with this view. See comments on
+  // CefViewView::GetCefView.
+  CefRefPtr<CefButton> GetCefButton() const {
+    CefRefPtr<CefButton> button = ParentClass::GetCefView()->AsButton();
+    DCHECK(button);
+    return button;
+  }
+
+  // views::Button methods:
+  void StateChanged(views::Button::ButtonState old_state) override;
+
+  // views::ButtonListener methods:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+};
+
+CEF_BUTTON_VIEW_T void CEF_BUTTON_VIEW_D::StateChanged(
+    views::Button::ButtonState old_state) {
+  ParentClass::StateChanged(old_state);
+  if (ParentClass::cef_delegate())
+    ParentClass::cef_delegate()->OnButtonStateChanged(GetCefButton());
+}
+
+CEF_BUTTON_VIEW_T void CEF_BUTTON_VIEW_D::ButtonPressed(
+    views::Button* sender,
+    const ui::Event& event) {
+  // Callback may trigger new animation state.
+  if (ParentClass::cef_delegate())
+    ParentClass::cef_delegate()->OnButtonPressed(GetCefButton());
+  if (ParentClass::ink_drop_mode() != views::Button::InkDropMode::OFF &&
+      !ParentClass::IsFocusable() &&
+      ParentClass::state() != views::Button::STATE_PRESSED) {
+    // Ink drop state does not get reset properly on click when the button is
+    // non-focusable. Reset the ink drop state here if the state has not been
+    // explicitly set to pressed by the OnButtonPressed callback calling
+    // SetState (which also sets the ink drop state).
+    ParentClass::AnimateInkDrop(views::InkDropState::HIDDEN,
+                                ui::LocatedEvent::FromIfValid(&event));
+  }
+}
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_BUTTON_VIEW_H_
diff --git a/src/libcef/browser/views/display_impl.cc b/src/libcef/browser/views/display_impl.cc
new file mode 100644
index 0000000..4f6d723
--- /dev/null
+++ b/src/libcef/browser/views/display_impl.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/views/display_impl.h"
+
+#include "libcef/browser/views/view_util.h"
+
+#include "ui/display/screen.h"
+
+// static
+CefRefPtr<CefDisplay> CefDisplay::GetPrimaryDisplay() {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  return new CefDisplayImpl(display::Screen::GetScreen()->GetPrimaryDisplay());
+}
+
+// static
+CefRefPtr<CefDisplay> CefDisplay::GetDisplayNearestPoint(
+    const CefPoint& point,
+    bool input_pixel_coords) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  return new CefDisplayImpl(view_util::GetDisplayNearestPoint(
+      gfx::Point(point.x, point.y), input_pixel_coords));
+}
+
+// static
+CefRefPtr<CefDisplay> CefDisplay::GetDisplayMatchingBounds(
+    const CefRect& bounds,
+    bool input_pixel_coords) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  return new CefDisplayImpl(view_util::GetDisplayMatchingBounds(
+      gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height),
+      input_pixel_coords));
+}
+
+// static
+size_t CefDisplay::GetDisplayCount() {
+  CEF_REQUIRE_UIT_RETURN(0U);
+  return static_cast<size_t>(display::Screen::GetScreen()->GetNumDisplays());
+}
+
+// static
+void CefDisplay::GetAllDisplays(std::vector<CefRefPtr<CefDisplay>>& displays) {
+  CEF_REQUIRE_UIT_RETURN_VOID();
+
+  displays.clear();
+
+  typedef std::vector<display::Display> DisplayVector;
+  DisplayVector vec = display::Screen::GetScreen()->GetAllDisplays();
+  for (size_t i = 0; i < vec.size(); ++i)
+    displays.push_back(new CefDisplayImpl(vec[i]));
+}
+
+CefDisplayImpl::CefDisplayImpl(const display::Display& display)
+    : display_(display) {
+  CEF_REQUIRE_UIT();
+}
+
+CefDisplayImpl::~CefDisplayImpl() {
+  CEF_REQUIRE_UIT();
+}
+
+int64 CefDisplayImpl::GetID() {
+  CEF_REQUIRE_UIT_RETURN(-1);
+  return display_.id();
+}
+
+float CefDisplayImpl::GetDeviceScaleFactor() {
+  CEF_REQUIRE_UIT_RETURN(0.0f);
+  return display_.device_scale_factor();
+}
+
+void CefDisplayImpl::ConvertPointToPixels(CefPoint& point) {
+  CEF_REQUIRE_UIT_RETURN_VOID();
+  gfx::Point gfx_point(point.x, point.y);
+  view_util::ConvertPointToPixels(&gfx_point, display_.device_scale_factor());
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+}
+
+void CefDisplayImpl::ConvertPointFromPixels(CefPoint& point) {
+  CEF_REQUIRE_UIT_RETURN_VOID();
+  gfx::Point gfx_point(point.x, point.y);
+  view_util::ConvertPointFromPixels(&gfx_point, display_.device_scale_factor());
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+}
+
+CefRect CefDisplayImpl::GetBounds() {
+  CEF_REQUIRE_UIT_RETURN(CefRect());
+  const gfx::Rect& gfx_rect = display_.bounds();
+  return CefRect(gfx_rect.x(), gfx_rect.y(), gfx_rect.width(),
+                 gfx_rect.height());
+}
+
+CefRect CefDisplayImpl::GetWorkArea() {
+  CEF_REQUIRE_UIT_RETURN(CefRect());
+  const gfx::Rect& gfx_rect = display_.work_area();
+  return CefRect(gfx_rect.x(), gfx_rect.y(), gfx_rect.width(),
+                 gfx_rect.height());
+}
+
+int CefDisplayImpl::GetRotation() {
+  CEF_REQUIRE_UIT_RETURN(0);
+  return display_.RotationAsDegree();
+}
diff --git a/src/libcef/browser/views/display_impl.h b/src/libcef/browser/views/display_impl.h
new file mode 100644
index 0000000..79562e3
--- /dev/null
+++ b/src/libcef/browser/views/display_impl.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_DISPLAY_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_DISPLAY_IMPL_H_
+#pragma once
+
+#include "include/views/cef_display.h"
+#include "libcef/browser/thread_util.h"
+
+#include "ui/display/display.h"
+
+class CefDisplayImpl : public CefDisplay {
+ public:
+  explicit CefDisplayImpl(const display::Display& display);
+  ~CefDisplayImpl() override;
+
+  // CefDisplay methods:
+  int64 GetID() override;
+  float GetDeviceScaleFactor() override;
+  void ConvertPointToPixels(CefPoint& point) override;
+  void ConvertPointFromPixels(CefPoint& point) override;
+  CefRect GetBounds() override;
+  CefRect GetWorkArea() override;
+  int GetRotation() override;
+
+  const display::Display& display() const { return display_; }
+
+ private:
+  display::Display display_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefDisplayImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefDisplayImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_DISPLAY_IMPL_H_
diff --git a/src/libcef/browser/views/fill_layout_impl.cc b/src/libcef/browser/views/fill_layout_impl.cc
new file mode 100644
index 0000000..fa7bdf9
--- /dev/null
+++ b/src/libcef/browser/views/fill_layout_impl.cc
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/fill_layout_impl.h"
+
+#include "libcef/browser/thread_util.h"
+
+// static
+CefRefPtr<CefFillLayout> CefFillLayoutImpl::Create(views::View* owner_view) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefFillLayoutImpl> impl = new CefFillLayoutImpl();
+  impl->Initialize(owner_view);
+  return impl;
+}
+
+CefFillLayoutImpl::CefFillLayoutImpl() {}
+
+views::FillLayout* CefFillLayoutImpl::CreateLayout() {
+  return new views::FillLayout();
+}
diff --git a/src/libcef/browser/views/fill_layout_impl.h b/src/libcef/browser/views/fill_layout_impl.h
new file mode 100644
index 0000000..e4b28a2
--- /dev/null
+++ b/src/libcef/browser/views/fill_layout_impl.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_FILL_LAYOUT_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_FILL_LAYOUT_IMPL_H_
+#pragma once
+
+#include "include/views/cef_fill_layout.h"
+
+#include "libcef/browser/views/layout_impl.h"
+#include "ui/views/layout/fill_layout.h"
+
+class CefFillLayoutImpl
+    : public CefLayoutImpl<views::FillLayout, CefFillLayout> {
+ public:
+  // Create a new CefFillLayout insance. |owner_view| must be non-nullptr.
+  static CefRefPtr<CefFillLayout> Create(views::View* owner_view);
+
+  // CefLayout methods:
+  CefRefPtr<CefFillLayout> AsFillLayout() override { return this; }
+
+ private:
+  CefFillLayoutImpl();
+
+  views::FillLayout* CreateLayout() override;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefFillLayoutImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefFillLayoutImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_FILL_LAYOUT_IMPL_H_
diff --git a/src/libcef/browser/views/label_button_impl.h b/src/libcef/browser/views/label_button_impl.h
new file mode 100644
index 0000000..98dc9d8
--- /dev/null
+++ b/src/libcef/browser/views/label_button_impl.h
@@ -0,0 +1,136 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_LABEL_BUTTON_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_LABEL_BUTTON_IMPL_H_
+#pragma once
+
+#include "include/views/cef_button.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_menu_button.h"
+
+#include "libcef/browser/image_impl.h"
+#include "libcef/browser/views/button_impl.h"
+
+#include "base/logging.h"
+#include "ui/views/controls/button/label_button.h"
+
+// Helpers for template boiler-plate.
+#define CEF_LABEL_BUTTON_IMPL_T CEF_BUTTON_IMPL_T
+#define CEF_LABEL_BUTTON_IMPL_A CEF_BUTTON_IMPL_A
+#define CEF_LABEL_BUTTON_IMPL_D CefLabelButtonImpl<CEF_LABEL_BUTTON_IMPL_A>
+
+// Template for implementing CefLabelButton-derived classes. See comments in
+// view_impl.h for a usage overview.
+CEF_LABEL_BUTTON_IMPL_T class CefLabelButtonImpl : public CEF_BUTTON_IMPL_D {
+ public:
+  typedef CEF_BUTTON_IMPL_D ParentClass;
+
+  // CefLabelButton methods. When adding new As*() methods make sure to update
+  // CefViewAdapter::GetFor() in view_adapter.cc.
+  void SetText(const CefString& text) override;
+  CefString GetText() override;
+  void SetImage(cef_button_state_t button_state,
+                CefRefPtr<CefImage> image) override;
+  CefRefPtr<CefImage> GetImage(cef_button_state_t button_state) override;
+  void SetTextColor(cef_button_state_t for_state, cef_color_t color) override;
+  void SetEnabledTextColors(cef_color_t color) override;
+  void SetFontList(const CefString& font_list) override;
+  void SetHorizontalAlignment(cef_horizontal_alignment_t alignment) override;
+  void SetMinimumSize(const CefSize& size) override;
+  void SetMaximumSize(const CefSize& size) override;
+
+  // CefLabelButton methods:
+  CefRefPtr<CefMenuButton> AsMenuButton() override { return nullptr; }
+
+  // CefButton methods:
+  CefRefPtr<CefLabelButton> AsLabelButton() override { return this; }
+
+  // CefViewAdapter methods:
+  void GetDebugInfo(base::DictionaryValue* info,
+                    bool include_children) override {
+    ParentClass::GetDebugInfo(info, include_children);
+    info->SetString("text", ParentClass::root_view()->GetText());
+  }
+
+ protected:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefLabelButtonImpl(CefRefPtr<CefViewDelegateClass> delegate)
+      : ParentClass(delegate) {}
+};
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetText(
+    const CefString& text) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetText(text);
+}
+
+CEF_LABEL_BUTTON_IMPL_T CefString CEF_LABEL_BUTTON_IMPL_D::GetText() {
+  CEF_REQUIRE_VALID_RETURN(CefString());
+  return ParentClass::root_view()->GetText();
+}
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetImage(
+    cef_button_state_t button_state,
+    CefRefPtr<CefImage> image) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  gfx::ImageSkia image_skia;
+  if (image)
+    image_skia = static_cast<CefImageImpl*>(image.get())->AsImageSkia();
+  ParentClass::root_view()->SetImage(
+      static_cast<views::Button::ButtonState>(button_state), image_skia);
+}
+
+CEF_LABEL_BUTTON_IMPL_T CefRefPtr<CefImage> CEF_LABEL_BUTTON_IMPL_D::GetImage(
+    cef_button_state_t button_state) {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  const gfx::ImageSkia& image_skia = ParentClass::root_view()->GetImage(
+      static_cast<views::Button::ButtonState>(button_state));
+  if (image_skia.isNull())
+    return nullptr;
+  return new CefImageImpl(image_skia);
+}
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetTextColor(
+    cef_button_state_t for_state,
+    cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetTextColor(
+      static_cast<views::Button::ButtonState>(for_state), color);
+}
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetEnabledTextColors(
+    cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetEnabledTextColors(color);
+}
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetFontList(
+    const CefString& font_list) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetFontList(gfx::FontList(font_list));
+}
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetHorizontalAlignment(
+    cef_horizontal_alignment_t alignment) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetHorizontalAlignment(
+      static_cast<gfx::HorizontalAlignment>(alignment));
+}
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetMinimumSize(
+    const CefSize& size) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetMinSize(gfx::Size(size.width, size.height));
+}
+
+CEF_LABEL_BUTTON_IMPL_T void CEF_LABEL_BUTTON_IMPL_D::SetMaximumSize(
+    const CefSize& size) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::root_view()->SetMaxSize(gfx::Size(size.width, size.height));
+}
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_LABEL_BUTTON_IMPL_H_
diff --git a/src/libcef/browser/views/label_button_view.h b/src/libcef/browser/views/label_button_view.h
new file mode 100644
index 0000000..e159724
--- /dev/null
+++ b/src/libcef/browser/views/label_button_view.h
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_LABEL_BUTTON_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_LABEL_BUTTON_VIEW_H_
+#pragma once
+
+#include "include/views/cef_label_button.h"
+#include "libcef/browser/views/button_view.h"
+
+#include "base/logging.h"
+#include "ui/gfx/font_list.h"
+
+// Helpers for template boiler-plate.
+#define CEF_LABEL_BUTTON_VIEW_T CEF_BUTTON_VIEW_T
+#define CEF_LABEL_BUTTON_VIEW_A CEF_BUTTON_VIEW_A
+#define CEF_LABEL_BUTTON_VIEW_D CefLabelButtonView<CEF_LABEL_BUTTON_VIEW_A>
+
+// Template for implementing views::View-derived classes that support adding and
+// removing children (called a Panel in CEF terminology). See comments in
+// view_impl.h for a usage overview.
+CEF_LABEL_BUTTON_VIEW_T class CefLabelButtonView : public CEF_BUTTON_VIEW_D {
+ public:
+  typedef CEF_BUTTON_VIEW_D ParentClass;
+
+  // |cef_delegate| may be nullptr.
+  explicit CefLabelButtonView(CefViewDelegateClass* cef_delegate)
+      : ParentClass(cef_delegate) {}
+
+  void Initialize() override {
+    ParentClass::Initialize();
+
+    // Use our defaults instead of the Views framework defaults.
+    ParentClass::SetFontList(gfx::FontList(view_util::kDefaultFontList));
+  }
+
+  // Returns the CefLabelButton associated with this view. See comments on
+  // CefViewView::GetCefView.
+  CefRefPtr<CefLabelButton> GetCefLabelButton() const {
+    CefRefPtr<CefLabelButton> label_button =
+        ParentClass::GetCefButton()->AsLabelButton();
+    DCHECK(label_button);
+    return label_button;
+  }
+
+  // CefViewView methods:
+  bool HasMinimumSize() const override { return true; }
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_LABEL_BUTTON_VIEW_H_
diff --git a/src/libcef/browser/views/layout_adapter.cc b/src/libcef/browser/views/layout_adapter.cc
new file mode 100644
index 0000000..6d4cc6a
--- /dev/null
+++ b/src/libcef/browser/views/layout_adapter.cc
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/layout_adapter.h"
+
+#include "libcef/browser/views/box_layout_impl.h"
+#include "libcef/browser/views/fill_layout_impl.h"
+
+// static
+CefLayoutAdapter* CefLayoutAdapter::GetFor(CefRefPtr<CefLayout> layout) {
+  CefLayoutAdapter* adapter = nullptr;
+  if (layout->AsBoxLayout()) {
+    adapter = static_cast<CefBoxLayoutImpl*>(layout->AsBoxLayout().get());
+  } else if (layout->AsFillLayout()) {
+    adapter = static_cast<CefFillLayoutImpl*>(layout->AsFillLayout().get());
+  }
+
+  DCHECK(adapter);
+  return adapter;
+}
diff --git a/src/libcef/browser/views/layout_adapter.h b/src/libcef/browser/views/layout_adapter.h
new file mode 100644
index 0000000..04a2d90
--- /dev/null
+++ b/src/libcef/browser/views/layout_adapter.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_ADAPTER_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_ADAPTER_H_
+#pragma once
+
+#include "include/views/cef_layout.h"
+
+namespace views {
+class LayoutManager;
+}
+
+// Exposes a common interface from all CefLayout implementation objects to
+// simplify the layout_util implementation. See comments in view_impl.h for a
+// usage overview.
+class CefLayoutAdapter {
+ public:
+  CefLayoutAdapter() {}
+
+  // Returns the CefLayoutAdapter for the specified |layout|.
+  static CefLayoutAdapter* GetFor(CefRefPtr<CefLayout> layout);
+
+  // Returns the underlying views::LayoutManager object. Does not transfer
+  // ownership.
+  virtual views::LayoutManager* Get() const = 0;
+
+  // Release all references to the views::LayoutManager object. This is called
+  // when the views::LayoutManager is deleted after being assigned to a
+  // views::View.
+  virtual void Detach() = 0;
+
+ protected:
+  virtual ~CefLayoutAdapter() {}
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_ADAPTER_H_
diff --git a/src/libcef/browser/views/layout_impl.h b/src/libcef/browser/views/layout_impl.h
new file mode 100644
index 0000000..9bfd8e9
--- /dev/null
+++ b/src/libcef/browser/views/layout_impl.h
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_IMPL_H_
+#pragma once
+
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_fill_layout.h"
+#include "include/views/cef_layout.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/layout_adapter.h"
+#include "libcef/browser/views/layout_util.h"
+
+#include "base/logging.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/view.h"
+
+// Base template for implementing CefLayout-derived classes. See comments in
+// view_impl.h for a usage overview.
+template <class ViewsLayoutClass, class CefLayoutClass>
+class CefLayoutImpl : public CefLayoutAdapter, public CefLayoutClass {
+ public:
+  // Returns the underlying views::LayoutManager object as the derived type.
+  // Does not transfer ownership.
+  ViewsLayoutClass* layout() const { return layout_ref_; }
+
+  // Returns the views::View that owns this object.
+  views::View* owner_view() const { return owner_view_; }
+
+  // CefLayoutAdapter methods:
+  views::LayoutManager* Get() const override { return layout(); }
+  void Detach() override {
+    owner_view_ = nullptr;
+    layout_ref_ = nullptr;
+  }
+
+  // CefLayout methods. When adding new As*() methods make sure to update
+  // CefLayoutAdapter::GetFor() in layout_adapter.cc.
+  CefRefPtr<CefBoxLayout> AsBoxLayout() override { return nullptr; }
+  CefRefPtr<CefFillLayout> AsFillLayout() override { return nullptr; }
+  bool IsValid() override {
+    CEF_REQUIRE_UIT_RETURN(false);
+    return !!layout_ref_;
+  }
+
+ protected:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  CefLayoutImpl() : layout_ref_(nullptr), owner_view_(nullptr) {}
+
+  // Initialize this object and assign ownership to |owner_view|.
+  void Initialize(views::View* owner_view) {
+    DCHECK(owner_view);
+    owner_view_ = owner_view;
+    layout_ref_ = CreateLayout();
+    DCHECK(layout_ref_);
+    owner_view->SetLayoutManager(base::WrapUnique(layout_ref_));
+    layout_util::Assign(this, owner_view);
+  }
+
+  // Create the views::LayoutManager object.
+  virtual ViewsLayoutClass* CreateLayout() = 0;
+
+ private:
+  // Unowned reference to the views::LayoutManager wrapped by this object. Will
+  // be nullptr after the views::LayoutManager is destroyed.
+  ViewsLayoutClass* layout_ref_;
+
+  // Unowned reference to the views::View that owns this object. Will be nullptr
+  // after the views::LayoutManager is destroyed.
+  views::View* owner_view_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_IMPL_H_
diff --git a/src/libcef/browser/views/layout_util.cc b/src/libcef/browser/views/layout_util.cc
new file mode 100644
index 0000000..305438d
--- /dev/null
+++ b/src/libcef/browser/views/layout_util.cc
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/layout_util.h"
+
+#include <utility>
+
+#include "libcef/browser/views/layout_adapter.h"
+
+#include "ui/views/view.h"
+
+namespace layout_util {
+
+namespace {
+
+// Manages the association between views::View and CefLayout instances.
+class UserData : public base::SupportsUserData::Data {
+ public:
+  static CefRefPtr<CefLayout> GetFor(const views::View* view) {
+    UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
+    if (data)
+      return data->layout_;
+    return nullptr;
+  }
+
+  // Assign ownership of the underlying views::LayoutManager to |owner_view|.
+  // The views::View that owns the views::LayoutManager will gain a ref-counted
+  // reference to the CefLayout and the CefLayout will keep an unowned reference
+  // to the views::LayoutManager. Destruction of the views::View will release
+  // the reference to the CefLayout.
+  static void Assign(CefRefPtr<CefLayout> cef_layout, views::View* owner_view) {
+    DCHECK(owner_view);
+    DCHECK(cef_layout->IsValid());
+
+    views::LayoutManager* layout = CefLayoutAdapter::GetFor(cef_layout)->Get();
+    DCHECK(layout);
+
+    // The CefLayout previously associated with |owner_view|, if any, will be
+    // destroyed by this call.
+    owner_view->SetUserData(UserDataKey(),
+                            base::WrapUnique(new UserData(cef_layout)));
+  }
+
+ private:
+  friend std::default_delete<UserData>;
+
+  explicit UserData(CefRefPtr<CefLayout> cef_layout) : layout_(cef_layout) {
+    DCHECK(layout_);
+  }
+
+  ~UserData() override { CefLayoutAdapter::GetFor(layout_)->Detach(); }
+
+  static void* UserDataKey() {
+    // We just need a unique constant. Use the address of a static that
+    // COMDAT folding won't touch in an optimizing linker.
+    static int data_key = 0;
+    return reinterpret_cast<void*>(&data_key);
+  }
+
+  CefRefPtr<CefLayout> layout_;
+};
+
+}  // namespace
+
+CefRefPtr<CefLayout> GetFor(const views::View* view) {
+  return UserData::GetFor(view);
+}
+
+void Assign(CefRefPtr<CefLayout> layout, views::View* owner_view) {
+  return UserData::Assign(layout, owner_view);
+}
+
+}  // namespace layout_util
diff --git a/src/libcef/browser/views/layout_util.h b/src/libcef/browser/views/layout_util.h
new file mode 100644
index 0000000..bd0555e
--- /dev/null
+++ b/src/libcef/browser/views/layout_util.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_UTIL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_UTIL_H_
+#pragma once
+
+#include "include/views/cef_layout.h"
+
+#include "ui/views/layout/layout_manager.h"
+
+namespace views {
+class View;
+}
+
+// The below functions manage the relationship between CefLayout and
+// views::LayoutManager instances. See comments in view_impl.h for a usage
+// overview.
+
+namespace layout_util {
+
+// Returns the CefLayout object associated with |owner_view|.
+CefRefPtr<CefLayout> GetFor(const views::View* owner_view);
+
+// Assign ownership of |layout| to |owner_view|. If a CefLayout is already
+// associated with |owner_view| it will be invalidated.
+void Assign(CefRefPtr<CefLayout> layout, views::View* owner_view);
+
+}  // namespace layout_util
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_LAYOUT_UTIL_H_
diff --git a/src/libcef/browser/views/menu_button_impl.cc b/src/libcef/browser/views/menu_button_impl.cc
new file mode 100644
index 0000000..6547277
--- /dev/null
+++ b/src/libcef/browser/views/menu_button_impl.cc
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/menu_button_impl.h"
+
+#include "libcef/browser/views/menu_button_view.h"
+#include "libcef/browser/views/window_impl.h"
+
+#include "ui/gfx/canvas.h"
+
+// static
+CefRefPtr<CefMenuButton> CefMenuButton::CreateMenuButton(
+    CefRefPtr<CefMenuButtonDelegate> delegate,
+    const CefString& text) {
+  return CefMenuButtonImpl::Create(delegate, text);
+}
+
+// static
+CefRefPtr<CefMenuButtonImpl> CefMenuButtonImpl::Create(
+    CefRefPtr<CefMenuButtonDelegate> delegate,
+    const CefString& text) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  DCHECK(delegate);
+  if (!delegate)
+    return nullptr;
+  CefRefPtr<CefMenuButtonImpl> menu_button = new CefMenuButtonImpl(delegate);
+  menu_button->Initialize();
+  if (!text.empty())
+    menu_button->SetText(text);
+  return menu_button;
+}
+
+void CefMenuButtonImpl::ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                                 const CefPoint& screen_point,
+                                 cef_menu_anchor_position_t anchor_position) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  CefRefPtr<CefWindow> window =
+      view_util::GetWindowFor(root_view()->GetWidget());
+  CefWindowImpl* window_impl = static_cast<CefWindowImpl*>(window.get());
+  if (window_impl) {
+    window_impl->ShowMenu(root_view(), menu_model, screen_point,
+                          anchor_position);
+  }
+}
+
+void CefMenuButtonImpl::TriggerMenu() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->Activate(nullptr);
+}
+
+void CefMenuButtonImpl::SetFocusable(bool focusable) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  static_cast<CefMenuButtonView*>(root_view())
+      ->SetDrawStringsFlags(focusable ? gfx::Canvas::SHOW_PREFIX
+                                      : gfx::Canvas::HIDE_PREFIX);
+  ParentClass::SetFocusable(focusable);
+}
+
+CefMenuButtonImpl::CefMenuButtonImpl(CefRefPtr<CefMenuButtonDelegate> delegate)
+    : ParentClass(delegate) {
+  DCHECK(delegate);
+}
+
+views::MenuButton* CefMenuButtonImpl::CreateRootView() {
+  return new CefMenuButtonView(delegate());
+}
+
+void CefMenuButtonImpl::InitializeRootView() {
+  static_cast<CefMenuButtonView*>(root_view())->Initialize();
+}
diff --git a/src/libcef/browser/views/menu_button_impl.h b/src/libcef/browser/views/menu_button_impl.h
new file mode 100644
index 0000000..682cddb
--- /dev/null
+++ b/src/libcef/browser/views/menu_button_impl.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_MENU_BUTTON_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_MENU_BUTTON_IMPL_H_
+#pragma once
+
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+
+#include "libcef/browser/menu_model_impl.h"
+#include "libcef/browser/views/label_button_impl.h"
+
+#include "ui/views/controls/button/menu_button.h"
+
+class CefMenuButtonImpl : public CefLabelButtonImpl<views::MenuButton,
+                                                    CefMenuButton,
+                                                    CefMenuButtonDelegate> {
+ public:
+  typedef CefLabelButtonImpl<views::MenuButton,
+                             CefMenuButton,
+                             CefMenuButtonDelegate>
+      ParentClass;
+
+  // Create a new CefMenuButton instance. |delegate| must not be nullptr.
+  static CefRefPtr<CefMenuButtonImpl> Create(
+      CefRefPtr<CefMenuButtonDelegate> delegate,
+      const CefString& text);
+
+  // CefMenuButton methods:
+  void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                const CefPoint& screen_point,
+                cef_menu_anchor_position_t anchor_position) override;
+  void TriggerMenu() override;
+
+  // CefLabelButton methods:
+  CefRefPtr<CefMenuButton> AsMenuButton() override { return this; }
+
+  // CefViewAdapter methods:
+  std::string GetDebugType() override { return "MenuButton"; }
+
+  // CefView methods:
+  void SetFocusable(bool focusable) override;
+
+ private:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| must not be nullptr.
+  explicit CefMenuButtonImpl(CefRefPtr<CefMenuButtonDelegate> delegate);
+
+  // CefViewImpl methods:
+  views::MenuButton* CreateRootView() override;
+  void InitializeRootView() override;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefMenuButtonImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefMenuButtonImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_MENU_BUTTON_IMPL_H_
diff --git a/src/libcef/browser/views/menu_button_view.cc b/src/libcef/browser/views/menu_button_view.cc
new file mode 100644
index 0000000..aac7ab6
--- /dev/null
+++ b/src/libcef/browser/views/menu_button_view.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/menu_button_view.h"
+
+#include "libcef/browser/thread_util.h"
+
+#include "ui/gfx/canvas.h"
+#include "ui/views/controls/button/menu_button_controller.h"
+#include "ui/views/controls/menu/menu_config.h"
+
+namespace {
+
+class ButtonPressedLock : public CefMenuButtonPressedLock {
+ public:
+  explicit ButtonPressedLock(views::MenuButton* menu_button)
+      : pressed_lock_(menu_button->button_controller()) {}
+
+ private:
+  views::MenuButtonController::PressedLock pressed_lock_;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(ButtonPressedLock);
+  DISALLOW_COPY_AND_ASSIGN(ButtonPressedLock);
+};
+
+}  // namespace
+
+CefMenuButtonView::CefMenuButtonView(CefMenuButtonDelegate* cef_delegate)
+    : ParentClass(cef_delegate) {
+  DCHECK(cef_delegate);
+}
+
+void CefMenuButtonView::Initialize() {
+  ParentClass::Initialize();
+
+  SetDrawStringsFlags(IsFocusable() ? gfx::Canvas::SHOW_PREFIX
+                                    : gfx::Canvas::HIDE_PREFIX);
+
+  // Use the same default font as MenuItemView.
+  SetFontList(views::MenuConfig::instance().font_list);
+}
+
+CefRefPtr<CefMenuButton> CefMenuButtonView::GetCefMenuButton() const {
+  CefRefPtr<CefMenuButton> menu_button = GetCefLabelButton()->AsMenuButton();
+  DCHECK(menu_button);
+  return menu_button;
+}
+
+void CefMenuButtonView::SetDrawStringsFlags(int flags) {
+  label()->SetDrawStringsFlags(flags);
+}
+
+void CefMenuButtonView::ButtonPressed(views::Button* source,
+                                      const ui::Event& event) {
+  auto position = source->GetMenuPosition();
+  cef_delegate()->OnMenuButtonPressed(
+      GetCefMenuButton(), CefPoint(position.x(), position.y()),
+      new ButtonPressedLock(static_cast<views::MenuButton*>(source)));
+}
diff --git a/src/libcef/browser/views/menu_button_view.h b/src/libcef/browser/views/menu_button_view.h
new file mode 100644
index 0000000..bd66e7e
--- /dev/null
+++ b/src/libcef/browser/views/menu_button_view.h
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_MENU_BUTTON_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_MENU_BUTTON_VIEW_H_
+#pragma once
+
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+
+#include "libcef/browser/views/label_button_view.h"
+
+#include "ui/views/controls/button/menu_button.h"
+
+// Extend views::LabelButton with a no-argument constructor as required by the
+// CefViewView template and extend views::ButtonListener as required by the
+// CefButtonView template.
+class MenuButtonEx : public views::MenuButton, public views::ButtonListener {
+ public:
+  MenuButtonEx() : views::MenuButton(base::string16(), this) {
+    // TODO(cef): MenuButton should not use ButtonListener. See
+    // http://crbug.com/585252 for details.
+    Button::listener_ = this;
+  }
+};
+
+class CefMenuButtonView
+    : public CefLabelButtonView<MenuButtonEx, CefMenuButtonDelegate> {
+ public:
+  typedef CefLabelButtonView<MenuButtonEx, CefMenuButtonDelegate> ParentClass;
+
+  // |cef_delegate| must not be nullptr.
+  explicit CefMenuButtonView(CefMenuButtonDelegate* cef_delegate);
+
+  void Initialize() override;
+
+  // Returns the CefMenuButton associated with this view. See comments on
+  // CefViewView::GetCefView.
+  CefRefPtr<CefMenuButton> GetCefMenuButton() const;
+
+  // Set the flags that control display of accelerator characters.
+  void SetDrawStringsFlags(int flags);
+
+  // views::ButtonListener methods:
+  void ButtonPressed(views::Button* source, const ui::Event& event) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefMenuButtonView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_MENU_BUTTON_VIEW_H_
diff --git a/src/libcef/browser/views/menu_runner_views.cc b/src/libcef/browser/views/menu_runner_views.cc
new file mode 100644
index 0000000..b6d976a
--- /dev/null
+++ b/src/libcef/browser/views/menu_runner_views.cc
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/menu_runner_views.h"
+
+#include "libcef/browser/browser_host_impl.h"
+#include "libcef/browser/views/browser_view_impl.h"
+
+CefMenuRunnerViews::CefMenuRunnerViews(CefBrowserViewImpl* browser_view)
+    : browser_view_(browser_view) {}
+
+bool CefMenuRunnerViews::RunContextMenu(
+    CefBrowserHostImpl* browser,
+    CefMenuModelImpl* model,
+    const content::ContextMenuParams& params) {
+  CefRefPtr<CefWindow> window = browser_view_->GetWindow();
+  if (!window)
+    return false;
+
+  CefPoint screen_point(params.x, params.y);
+  browser_view_->ConvertPointToScreen(screen_point);
+
+  window->ShowMenu(model, screen_point, CEF_MENU_ANCHOR_TOPRIGHT);
+  return true;
+}
+
+void CefMenuRunnerViews::CancelContextMenu() {
+  CefRefPtr<CefWindow> window = browser_view_->GetWindow();
+  if (window)
+    window->CancelMenu();
+}
+
+bool CefMenuRunnerViews::FormatLabel(base::string16& label) {
+  // Remove the accelerator indicator (&) from label strings.
+  const char16 replace[] = {L'&', 0};
+  return base::ReplaceChars(label, replace, base::string16(), &label);
+}
diff --git a/src/libcef/browser/views/menu_runner_views.h b/src/libcef/browser/views/menu_runner_views.h
new file mode 100644
index 0000000..9030dbb
--- /dev/null
+++ b/src/libcef/browser/views/menu_runner_views.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_MENU_RUNNER_VIEWS_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_MENU_RUNNER_VIEWS_H_
+#pragma once
+
+#include "libcef/browser/menu_runner.h"
+
+class CefBrowserViewImpl;
+
+class CefMenuRunnerViews : public CefMenuRunner {
+ public:
+  // |browser_view| is guaranteed to outlive this object.
+  explicit CefMenuRunnerViews(CefBrowserViewImpl* browser_view);
+
+  // CefMenuRunner methods.
+  bool RunContextMenu(CefBrowserHostImpl* browser,
+                      CefMenuModelImpl* model,
+                      const content::ContextMenuParams& params) override;
+  void CancelContextMenu() override;
+  bool FormatLabel(base::string16& label) override;
+
+ private:
+  CefBrowserViewImpl* browser_view_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_MENU_RUNNER_VIEWS_H_
diff --git a/src/libcef/browser/views/panel_impl.h b/src/libcef/browser/views/panel_impl.h
new file mode 100644
index 0000000..990f931
--- /dev/null
+++ b/src/libcef/browser/views/panel_impl.h
@@ -0,0 +1,211 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_PANEL_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_PANEL_IMPL_H_
+#pragma once
+
+#include "include/views/cef_fill_layout.h"
+#include "include/views/cef_layout.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_window.h"
+
+#include "libcef/browser/views/box_layout_impl.h"
+#include "libcef/browser/views/fill_layout_impl.h"
+#include "libcef/browser/views/layout_util.h"
+#include "libcef/browser/views/view_impl.h"
+
+#include "base/logging.h"
+
+// Helpers for template boiler-plate.
+#define CEF_PANEL_IMPL_T CEF_VIEW_IMPL_T
+#define CEF_PANEL_IMPL_A CEF_VIEW_IMPL_A
+#define CEF_PANEL_IMPL_D CefPanelImpl<CEF_PANEL_IMPL_A>
+
+// Template for implementing CefPanel-derived classes. See comments in
+// view_impl.h for a usage overview.
+CEF_PANEL_IMPL_T class CefPanelImpl : public CEF_VIEW_IMPL_D {
+ public:
+  typedef CEF_VIEW_IMPL_D ParentClass;
+
+  // CefPanel methods. When adding new As*() methods make sure to update
+  // CefViewAdapter::GetFor() in view_adapter.cc.
+  CefRefPtr<CefWindow> AsWindow() override { return nullptr; }
+  CefRefPtr<CefFillLayout> SetToFillLayout() override;
+  CefRefPtr<CefBoxLayout> SetToBoxLayout(
+      const CefBoxLayoutSettings& settings) override;
+  CefRefPtr<CefLayout> GetLayout() override;
+  void Layout() override;
+  void AddChildView(CefRefPtr<CefView> view) override;
+  void AddChildViewAt(CefRefPtr<CefView> view, int index) override;
+  void ReorderChildView(CefRefPtr<CefView> view, int index) override;
+  void RemoveChildView(CefRefPtr<CefView> view) override;
+  void RemoveAllChildViews() override;
+  size_t GetChildViewCount() override;
+  CefRefPtr<CefView> GetChildViewAt(int index) override;
+
+  // CefView methods:
+  CefRefPtr<CefPanel> AsPanel() override { return this; }
+
+  // CefViewAdapter methods:
+  void GetDebugInfo(base::DictionaryValue* info,
+                    bool include_children) override {
+    ParentClass::GetDebugInfo(info, include_children);
+    if (include_children) {
+      const size_t count = ParentClass::content_view()->children().size();
+      if (count > 0U) {
+        std::unique_ptr<base::ListValue> children(new base::ListValue());
+
+        for (size_t i = 0U; i < count; ++i) {
+          views::View* view = ParentClass::content_view()->children()[i];
+          CefViewAdapter* adapter = CefViewAdapter::GetFor(view);
+          if (adapter) {
+            std::unique_ptr<base::DictionaryValue> child_info(
+                new base::DictionaryValue());
+            adapter->GetDebugInfo(child_info.get(), include_children);
+            children->Append(std::move(child_info));
+          }
+        }
+
+        info->Set("children", std::move(children));
+      }
+    }
+  }
+
+ protected:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefPanelImpl(CefRefPtr<CefViewDelegateClass> delegate)
+      : ParentClass(delegate) {}
+
+  void Initialize() override {
+    ParentClass::Initialize();
+
+    // Create the default layout object.
+    SetToFillLayout();
+  }
+};
+
+CEF_PANEL_IMPL_T CefRefPtr<CefFillLayout> CEF_PANEL_IMPL_D::SetToFillLayout() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  return CefFillLayoutImpl::Create(ParentClass::content_view());
+}
+
+CEF_PANEL_IMPL_T CefRefPtr<CefBoxLayout> CEF_PANEL_IMPL_D::SetToBoxLayout(
+    const CefBoxLayoutSettings& settings) {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  return CefBoxLayoutImpl::Create(settings, ParentClass::content_view());
+}
+
+CEF_PANEL_IMPL_T CefRefPtr<CefLayout> CEF_PANEL_IMPL_D::GetLayout() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  return layout_util::GetFor(ParentClass::content_view());
+}
+
+CEF_PANEL_IMPL_T void CEF_PANEL_IMPL_D::Layout() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  return ParentClass::root_view()->Layout();
+}
+
+CEF_PANEL_IMPL_T void CEF_PANEL_IMPL_D::AddChildView(CefRefPtr<CefView> view) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  DCHECK(view.get());
+  DCHECK(view->IsValid());
+  DCHECK(!view->IsAttached());
+  if (!view.get() || !view->IsValid() || view->IsAttached())
+    return;
+
+  std::unique_ptr<views::View> view_ptr = view_util::PassOwnership(view);
+  ParentClass::content_view()->AddChildView(view_ptr.release());
+}
+
+CEF_PANEL_IMPL_T void CEF_PANEL_IMPL_D::AddChildViewAt(CefRefPtr<CefView> view,
+                                                       int index) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  DCHECK(view.get());
+  DCHECK(view->IsValid());
+  DCHECK(!view->IsAttached());
+  DCHECK_GE(index, 0);
+  DCHECK_LE(static_cast<unsigned int>(index),
+            ParentClass::content_view()->children().size());
+  if (!view.get() || !view->IsValid() || view->IsAttached() || index < 0 ||
+      (static_cast<unsigned int>(index) >
+       ParentClass::content_view()->children().size())) {
+    return;
+  }
+
+  std::unique_ptr<views::View> view_ptr = view_util::PassOwnership(view);
+  ParentClass::content_view()->AddChildViewAt(view_ptr.release(), index);
+}
+
+CEF_PANEL_IMPL_T void CEF_PANEL_IMPL_D::ReorderChildView(
+    CefRefPtr<CefView> view,
+    int index) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  DCHECK(view.get());
+  DCHECK(view->IsValid());
+  DCHECK(view->IsAttached());
+  if (!view.get() || !view->IsValid() || !view->IsAttached())
+    return;
+
+  views::View* view_ptr = view_util::GetFor(view);
+  DCHECK(view_ptr);
+  DCHECK_EQ(view_ptr->parent(), ParentClass::content_view());
+  if (!view_ptr || view_ptr->parent() != ParentClass::content_view())
+    return;
+
+  ParentClass::content_view()->ReorderChildView(view_ptr, index);
+}
+
+CEF_PANEL_IMPL_T void CEF_PANEL_IMPL_D::RemoveChildView(
+    CefRefPtr<CefView> view) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  DCHECK(view.get());
+  DCHECK(view->IsValid());
+  DCHECK(view->IsAttached());
+  if (!view.get() || !view->IsValid() || !view->IsAttached())
+    return;
+
+  views::View* view_ptr = view_util::GetFor(view);
+  DCHECK(view_ptr);
+  DCHECK_EQ(view_ptr->parent(), ParentClass::content_view());
+  if (!view_ptr || view_ptr->parent() != ParentClass::content_view())
+    return;
+
+  ParentClass::content_view()->RemoveChildView(view_ptr);
+  view_util::ResumeOwnership(view);
+}
+
+CEF_PANEL_IMPL_T void CEF_PANEL_IMPL_D::RemoveAllChildViews() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  while (!ParentClass::content_view()->children().empty()) {
+    CefRefPtr<CefView> view = view_util::GetFor(
+        ParentClass::content_view()->children().front(), false);
+    RemoveChildView(view);
+  }
+}
+
+CEF_PANEL_IMPL_T size_t CEF_PANEL_IMPL_D::GetChildViewCount() {
+  CEF_REQUIRE_VALID_RETURN(0U);
+  return ParentClass::content_view()->children().size();
+}
+
+CEF_PANEL_IMPL_T CefRefPtr<CefView> CEF_PANEL_IMPL_D::GetChildViewAt(
+    int index) {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  DCHECK_GE(index, 0);
+  DCHECK_LT(static_cast<unsigned int>(index),
+            ParentClass::content_view()->children().size());
+  if (index < 0 || (static_cast<unsigned int>(index) >=
+                    ParentClass::content_view()->children().size()))
+    return nullptr;
+
+  CefRefPtr<CefView> view =
+      view_util::GetFor(ParentClass::content_view()->children()[index], false);
+  DCHECK(view);
+  return view;
+}
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_PANEL_IMPL_H_
diff --git a/src/libcef/browser/views/panel_view.h b/src/libcef/browser/views/panel_view.h
new file mode 100644
index 0000000..13d7808
--- /dev/null
+++ b/src/libcef/browser/views/panel_view.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_PANEL_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_PANEL_VIEW_H_
+#pragma once
+
+#include "include/views/cef_panel_delegate.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/view_view.h"
+
+#include "base/logging.h"
+
+// Helpers for template boiler-plate.
+#define CEF_PANEL_VIEW_T CEF_VIEW_VIEW_T
+#define CEF_PANEL_VIEW_A CEF_VIEW_VIEW_A
+#define CEF_PANEL_VIEW_D CefPanelView<CEF_PANEL_VIEW_A>
+
+// Template for implementing views::View-derived classes that support adding and
+// removing children (called a Panel in CEF terminology). See comments in
+// view_impl.h for a usage overview.
+CEF_PANEL_VIEW_T class CefPanelView : public CEF_VIEW_VIEW_D {
+ public:
+  typedef CEF_VIEW_VIEW_D ParentClass;
+
+  // |cef_delegate| may be nullptr.
+  explicit CefPanelView(CefViewDelegateClass* cef_delegate)
+      : ParentClass(cef_delegate) {}
+
+  // Returns the CefPanel associated with this view. See comments on
+  // CefViewView::GetCefView.
+  CefRefPtr<CefPanel> GetCefPanel() const {
+    CefRefPtr<CefPanel> panel = ParentClass::GetCefView()->AsPanel();
+    DCHECK(panel);
+    return panel;
+  }
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_PANEL_VIEW_H_
diff --git a/src/libcef/browser/views/scroll_view_impl.cc b/src/libcef/browser/views/scroll_view_impl.cc
new file mode 100644
index 0000000..61d3fd0
--- /dev/null
+++ b/src/libcef/browser/views/scroll_view_impl.cc
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/scroll_view_impl.h"
+
+// static
+CefRefPtr<CefScrollView> CefScrollView::CreateScrollView(
+    CefRefPtr<CefViewDelegate> delegate) {
+  return CefScrollViewImpl::Create(delegate);
+}
+
+// static
+CefRefPtr<CefScrollViewImpl> CefScrollViewImpl::Create(
+    CefRefPtr<CefViewDelegate> delegate) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefScrollViewImpl> view = new CefScrollViewImpl(delegate);
+  view->Initialize();
+  return view;
+}
+
+void CefScrollViewImpl::SetContentView(CefRefPtr<CefView> view) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  DCHECK(view.get());
+  DCHECK(view->IsValid());
+  DCHECK(!view->IsAttached());
+  if (!view.get() || !view->IsValid() || view->IsAttached())
+    return;
+
+  root_view()->SetContents(view_util::PassOwnership(view));
+}
+
+CefRefPtr<CefView> CefScrollViewImpl::GetContentView() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  return view_util::GetFor(root_view()->contents(), false);
+}
+
+CefRect CefScrollViewImpl::GetVisibleContentRect() {
+  CEF_REQUIRE_VALID_RETURN(CefRect());
+  const gfx::Rect& rect = root_view()->GetVisibleRect();
+  return CefRect(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+bool CefScrollViewImpl::HasHorizontalScrollbar() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  const views::ScrollBar* scrollbar = root_view()->horizontal_scroll_bar();
+  return scrollbar && scrollbar->GetVisible();
+}
+
+int CefScrollViewImpl::GetHorizontalScrollbarHeight() {
+  CEF_REQUIRE_VALID_RETURN(0);
+  return root_view()->GetScrollBarLayoutHeight();
+}
+
+bool CefScrollViewImpl::HasVerticalScrollbar() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  const views::ScrollBar* scrollbar = root_view()->vertical_scroll_bar();
+  return scrollbar && scrollbar->GetVisible();
+}
+
+int CefScrollViewImpl::GetVerticalScrollbarWidth() {
+  CEF_REQUIRE_VALID_RETURN(0);
+  return root_view()->GetScrollBarLayoutWidth();
+}
+
+void CefScrollViewImpl::GetDebugInfo(base::DictionaryValue* info,
+                                     bool include_children) {
+  ParentClass::GetDebugInfo(info, include_children);
+  if (include_children) {
+    views::View* view = root_view()->contents();
+    CefViewAdapter* adapter = CefViewAdapter::GetFor(view);
+    if (adapter) {
+      std::unique_ptr<base::DictionaryValue> child_info(
+          new base::DictionaryValue());
+      adapter->GetDebugInfo(child_info.get(), include_children);
+      info->Set("content_view", std::move(child_info));
+    }
+  }
+}
+
+CefScrollViewImpl::CefScrollViewImpl(CefRefPtr<CefViewDelegate> delegate)
+    : ParentClass(delegate) {}
+
+CefScrollViewView* CefScrollViewImpl::CreateRootView() {
+  return new CefScrollViewView(delegate());
+}
+
+void CefScrollViewImpl::InitializeRootView() {
+  static_cast<CefScrollViewView*>(root_view())->Initialize();
+}
diff --git a/src/libcef/browser/views/scroll_view_impl.h b/src/libcef/browser/views/scroll_view_impl.h
new file mode 100644
index 0000000..ca27690
--- /dev/null
+++ b/src/libcef/browser/views/scroll_view_impl.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_SCROLL_VIEW_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_SCROLL_VIEW_IMPL_H_
+#pragma once
+
+#include "include/views/cef_scroll_view.h"
+#include "include/views/cef_view_delegate.h"
+
+#include "libcef/browser/views/scroll_view_view.h"
+#include "libcef/browser/views/view_impl.h"
+
+class CefScrollViewImpl
+    : public CefViewImpl<CefScrollViewView, CefScrollView, CefViewDelegate> {
+ public:
+  typedef CefViewImpl<CefScrollViewView, CefScrollView, CefViewDelegate>
+      ParentClass;
+
+  // Create a new CefScrollView instance. |delegate| may be nullptr.
+  static CefRefPtr<CefScrollViewImpl> Create(
+      CefRefPtr<CefViewDelegate> delegate);
+
+  // CefScrollView methods:
+  CefRefPtr<CefScrollView> AsScrollView() override { return this; }
+  void SetContentView(CefRefPtr<CefView> view) override;
+  CefRefPtr<CefView> GetContentView() override;
+  CefRect GetVisibleContentRect() override;
+  bool HasHorizontalScrollbar() override;
+  int GetHorizontalScrollbarHeight() override;
+  bool HasVerticalScrollbar() override;
+  int GetVerticalScrollbarWidth() override;
+
+  // CefViewAdapter methods:
+  std::string GetDebugType() override { return "ScrollView"; }
+  void GetDebugInfo(base::DictionaryValue* info,
+                    bool include_children) override;
+
+ private:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  CefScrollViewImpl(CefRefPtr<CefViewDelegate> delegate);
+
+  // CefViewImpl methods:
+  CefScrollViewView* CreateRootView() override;
+  void InitializeRootView() override;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefScrollViewImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefScrollViewImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_SCROLL_VIEW_IMPL_H_
diff --git a/src/libcef/browser/views/scroll_view_view.cc b/src/libcef/browser/views/scroll_view_view.cc
new file mode 100644
index 0000000..39ac25c
--- /dev/null
+++ b/src/libcef/browser/views/scroll_view_view.cc
@@ -0,0 +1,8 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/scroll_view_view.h"
+
+CefScrollViewView::CefScrollViewView(CefViewDelegate* cef_delegate)
+    : ParentClass(cef_delegate) {}
diff --git a/src/libcef/browser/views/scroll_view_view.h b/src/libcef/browser/views/scroll_view_view.h
new file mode 100644
index 0000000..62a7c4b
--- /dev/null
+++ b/src/libcef/browser/views/scroll_view_view.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_SCROLL_VIEW_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_SCROLL_VIEW_VIEW_H_
+#pragma once
+
+#include "include/views/cef_panel_delegate.h"
+
+#include "libcef/browser/views/view_view.h"
+
+#include "ui/views/controls/scroll_view.h"
+
+class CefScrollViewView
+    : public CefViewView<views::ScrollView, CefViewDelegate> {
+ public:
+  typedef CefViewView<views::ScrollView, CefViewDelegate> ParentClass;
+
+  // |cef_delegate| may be nullptr.
+  explicit CefScrollViewView(CefViewDelegate* cef_delegate);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefScrollViewView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_SCROLL_VIEW_VIEW_H_
diff --git a/src/libcef/browser/views/textfield_impl.cc b/src/libcef/browser/views/textfield_impl.cc
new file mode 100644
index 0000000..1437fdf
--- /dev/null
+++ b/src/libcef/browser/views/textfield_impl.cc
@@ -0,0 +1,212 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/textfield_impl.h"
+
+#include "libcef/browser/thread_util.h"
+
+// static
+CefRefPtr<CefTextfield> CefTextfield::CreateTextfield(
+    CefRefPtr<CefTextfieldDelegate> delegate) {
+  return CefTextfieldImpl::Create(delegate);
+}
+
+// static
+CefRefPtr<CefTextfieldImpl> CefTextfieldImpl::Create(
+    CefRefPtr<CefTextfieldDelegate> delegate) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefTextfieldImpl> textfield = new CefTextfieldImpl(delegate);
+  textfield->Initialize();
+  return textfield;
+}
+
+void CefTextfieldImpl::SetPasswordInput(bool password_input) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetTextInputType(password_input ? ui::TEXT_INPUT_TYPE_PASSWORD
+                                               : ui::TEXT_INPUT_TYPE_TEXT);
+}
+
+bool CefTextfieldImpl::IsPasswordInput() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return (root_view()->GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD);
+}
+
+void CefTextfieldImpl::SetReadOnly(bool read_only) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetReadOnly(read_only);
+}
+
+bool CefTextfieldImpl::IsReadOnly() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->GetReadOnly();
+}
+
+CefString CefTextfieldImpl::GetText() {
+  CEF_REQUIRE_VALID_RETURN(CefString());
+  return root_view()->GetText();
+}
+
+void CefTextfieldImpl::SetText(const CefString& text) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetText(text);
+}
+
+void CefTextfieldImpl::AppendText(const CefString& text) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->AppendText(text);
+}
+
+void CefTextfieldImpl::InsertOrReplaceText(const CefString& text) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->InsertOrReplaceText(text);
+}
+
+bool CefTextfieldImpl::HasSelection() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->HasSelection();
+}
+
+CefString CefTextfieldImpl::GetSelectedText() {
+  CEF_REQUIRE_VALID_RETURN(CefString());
+  return root_view()->GetSelectedText();
+}
+
+void CefTextfieldImpl::SelectAll(bool reversed) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SelectAll(reversed);
+}
+
+void CefTextfieldImpl::ClearSelection() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->ClearSelection();
+}
+
+CefRange CefTextfieldImpl::GetSelectedRange() {
+  CEF_REQUIRE_VALID_RETURN(CefRange());
+  const gfx::Range& range = root_view()->GetSelectedRange();
+  return CefRange(range.start(), range.end());
+}
+
+void CefTextfieldImpl::SelectRange(const CefRange& range) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetSelectedRange(gfx::Range(range.from, range.to));
+}
+
+size_t CefTextfieldImpl::GetCursorPosition() {
+  CEF_REQUIRE_VALID_RETURN(0U);
+  return root_view()->GetCursorPosition();
+}
+
+void CefTextfieldImpl::SetTextColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetTextColor(color);
+}
+
+cef_color_t CefTextfieldImpl::GetTextColor() {
+  CEF_REQUIRE_VALID_RETURN(0U);
+  return root_view()->GetTextColor();
+}
+
+void CefTextfieldImpl::SetSelectionTextColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetSelectionTextColor(color);
+}
+
+cef_color_t CefTextfieldImpl::GetSelectionTextColor() {
+  CEF_REQUIRE_VALID_RETURN(0U);
+  return root_view()->GetSelectionTextColor();
+}
+
+void CefTextfieldImpl::SetSelectionBackgroundColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetSelectionBackgroundColor(color);
+}
+
+cef_color_t CefTextfieldImpl::GetSelectionBackgroundColor() {
+  CEF_REQUIRE_VALID_RETURN(0U);
+  return root_view()->GetSelectionBackgroundColor();
+}
+
+void CefTextfieldImpl::SetFontList(const CefString& font_list) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetFontList(gfx::FontList(font_list));
+}
+
+void CefTextfieldImpl::ApplyTextColor(cef_color_t color,
+                                      const CefRange& range) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (range.from == range.to)
+    root_view()->SetColor(color);
+  else
+    root_view()->ApplyColor(color, gfx::Range(range.from, range.to));
+}
+
+void CefTextfieldImpl::ApplyTextStyle(cef_text_style_t style,
+                                      bool add,
+                                      const CefRange& range) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (range.from == range.to) {
+    root_view()->SetStyle(static_cast<gfx::TextStyle>(style), add);
+  } else {
+    root_view()->ApplyStyle(static_cast<gfx::TextStyle>(style), add,
+                            gfx::Range(range.from, range.to));
+  }
+}
+
+bool CefTextfieldImpl::IsCommandEnabled(int command_id) {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->IsCommandIdEnabled(command_id);
+}
+
+void CefTextfieldImpl::ExecuteCommand(int command_id) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (root_view()->IsCommandIdEnabled(command_id))
+    root_view()->ExecuteCommand(command_id, ui::EF_NONE);
+}
+
+void CefTextfieldImpl::ClearEditHistory() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->ClearEditHistory();
+}
+
+void CefTextfieldImpl::SetPlaceholderText(const CefString& text) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetPlaceholderText(text);
+}
+
+CefString CefTextfieldImpl::GetPlaceholderText() {
+  CEF_REQUIRE_VALID_RETURN(CefString());
+  return root_view()->GetPlaceholderText();
+}
+
+void CefTextfieldImpl::SetPlaceholderTextColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->set_placeholder_text_color(color);
+}
+
+void CefTextfieldImpl::SetBackgroundColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetBackgroundColor(color);
+}
+
+cef_color_t CefTextfieldImpl::GetBackgroundColor() {
+  CEF_REQUIRE_VALID_RETURN(0U);
+  return root_view()->GetBackgroundColor();
+}
+
+void CefTextfieldImpl::SetAccessibleName(const CefString& name) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetAccessibleName(name);
+}
+
+CefTextfieldImpl::CefTextfieldImpl(CefRefPtr<CefTextfieldDelegate> delegate)
+    : ParentClass(delegate) {}
+
+CefTextfieldView* CefTextfieldImpl::CreateRootView() {
+  return new CefTextfieldView(delegate());
+}
+
+void CefTextfieldImpl::InitializeRootView() {
+  static_cast<CefTextfieldView*>(root_view())->Initialize();
+}
diff --git a/src/libcef/browser/views/textfield_impl.h b/src/libcef/browser/views/textfield_impl.h
new file mode 100644
index 0000000..b620fc4
--- /dev/null
+++ b/src/libcef/browser/views/textfield_impl.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_TEXTFIELD_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_TEXTFIELD_IMPL_H_
+#pragma once
+
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_textfield_delegate.h"
+
+#include "libcef/browser/views/textfield_view.h"
+#include "libcef/browser/views/view_impl.h"
+
+class CefTextfieldImpl
+    : public CefViewImpl<CefTextfieldView, CefTextfield, CefTextfieldDelegate> {
+ public:
+  typedef CefViewImpl<CefTextfieldView, CefTextfield, CefTextfieldDelegate>
+      ParentClass;
+
+  // Create a new CefTextfield instance. |delegate| may be nullptr.
+  static CefRefPtr<CefTextfieldImpl> Create(
+      CefRefPtr<CefTextfieldDelegate> delegate);
+
+  // CefTextfield methods:
+  void SetPasswordInput(bool password_input) override;
+  bool IsPasswordInput() override;
+  void SetReadOnly(bool read_only) override;
+  bool IsReadOnly() override;
+  CefString GetText() override;
+  void SetText(const CefString& text) override;
+  void AppendText(const CefString& text) override;
+  void InsertOrReplaceText(const CefString& text) override;
+  bool HasSelection() override;
+  CefString GetSelectedText() override;
+  void SelectAll(bool reversed) override;
+  void ClearSelection() override;
+  CefRange GetSelectedRange() override;
+  void SelectRange(const CefRange& range) override;
+  size_t GetCursorPosition() override;
+  void SetTextColor(cef_color_t color) override;
+  cef_color_t GetTextColor() override;
+  void SetSelectionTextColor(cef_color_t color) override;
+  cef_color_t GetSelectionTextColor() override;
+  void SetSelectionBackgroundColor(cef_color_t color) override;
+  cef_color_t GetSelectionBackgroundColor() override;
+  void SetFontList(const CefString& font_list) override;
+  void ApplyTextColor(cef_color_t color, const CefRange& range) override;
+  void ApplyTextStyle(cef_text_style_t style,
+                      bool add,
+                      const CefRange& range) override;
+  bool IsCommandEnabled(int command_id) override;
+  void ExecuteCommand(int command_id) override;
+  void ClearEditHistory() override;
+  void SetPlaceholderText(const CefString& text) override;
+  CefString GetPlaceholderText() override;
+  void SetPlaceholderTextColor(cef_color_t color) override;
+  void SetAccessibleName(const CefString& name) override;
+
+  // CefView methods:
+  CefRefPtr<CefTextfield> AsTextfield() override { return this; }
+  void SetBackgroundColor(cef_color_t color) override;
+  cef_color_t GetBackgroundColor() override;
+
+  // CefViewAdapter methods:
+  std::string GetDebugType() override { return "Textfield"; }
+
+ private:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefTextfieldImpl(CefRefPtr<CefTextfieldDelegate> delegate);
+
+  // CefViewImpl methods:
+  CefTextfieldView* CreateRootView() override;
+  void InitializeRootView() override;
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefTextfieldImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefTextfieldImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_TEXTFIELD_IMPL_H_
diff --git a/src/libcef/browser/views/textfield_view.cc b/src/libcef/browser/views/textfield_view.cc
new file mode 100644
index 0000000..4029f87
--- /dev/null
+++ b/src/libcef/browser/views/textfield_view.cc
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/textfield_view.h"
+
+#include "libcef/browser/browser_util.h"
+
+CefTextfieldView::CefTextfieldView(CefTextfieldDelegate* cef_delegate)
+    : ParentClass(cef_delegate) {
+  set_controller(this);
+}
+
+void CefTextfieldView::Initialize() {
+  ParentClass::Initialize();
+
+  // Use our defaults instead of the Views framework defaults.
+  SetFontList(gfx::FontList(view_util::kDefaultFontList));
+}
+
+bool CefTextfieldView::HandleKeyEvent(views::Textfield* sender,
+                                      const ui::KeyEvent& key_event) {
+  DCHECK_EQ(sender, this);
+
+  if (!cef_delegate())
+    return false;
+
+  CefKeyEvent cef_key_event;
+  if (!browser_util::GetCefKeyEvent(key_event, cef_key_event))
+    return false;
+
+  return cef_delegate()->OnKeyEvent(GetCefTextfield(), cef_key_event);
+}
+
+void CefTextfieldView::OnAfterUserAction(views::Textfield* sender) {
+  DCHECK_EQ(sender, this);
+  if (cef_delegate())
+    cef_delegate()->OnAfterUserAction(GetCefTextfield());
+}
diff --git a/src/libcef/browser/views/textfield_view.h b/src/libcef/browser/views/textfield_view.h
new file mode 100644
index 0000000..b97187b
--- /dev/null
+++ b/src/libcef/browser/views/textfield_view.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_TEXTFIELD_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_TEXTFIELD_VIEW_H_
+#pragma once
+
+#include "include/views/cef_textfield_delegate.h"
+
+#include "include/views/cef_textfield.h"
+#include "libcef/browser/views/view_view.h"
+
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+
+class CefTextfieldView
+    : public CefViewView<views::Textfield, CefTextfieldDelegate>,
+      public views::TextfieldController {
+ public:
+  typedef CefViewView<views::Textfield, CefTextfieldDelegate> ParentClass;
+
+  // |cef_delegate| may be nullptr.
+  explicit CefTextfieldView(CefTextfieldDelegate* cef_delegate);
+
+  void Initialize() override;
+
+  // Returns the CefTextfield associated with this view. See comments on
+  // CefViewView::GetCefView.
+  CefRefPtr<CefTextfield> GetCefTextfield() const {
+    CefRefPtr<CefTextfield> textfield = GetCefView()->AsTextfield();
+    DCHECK(textfield);
+    return textfield;
+  }
+
+  // TextfieldController methods:
+  bool HandleKeyEvent(views::Textfield* sender,
+                      const ui::KeyEvent& key_event) override;
+  void OnAfterUserAction(views::Textfield* sender) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefTextfieldView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_TEXTFIELD_VIEW_H_
diff --git a/src/libcef/browser/views/view_adapter.cc b/src/libcef/browser/views/view_adapter.cc
new file mode 100644
index 0000000..8f3aa4b
--- /dev/null
+++ b/src/libcef/browser/views/view_adapter.cc
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/view_adapter.h"
+
+#include "libcef/browser/views/basic_label_button_impl.h"
+#include "libcef/browser/views/basic_panel_impl.h"
+#include "libcef/browser/views/browser_view_impl.h"
+#include "libcef/browser/views/menu_button_impl.h"
+#include "libcef/browser/views/scroll_view_impl.h"
+#include "libcef/browser/views/textfield_impl.h"
+#include "libcef/browser/views/view_util.h"
+#include "libcef/browser/views/window_impl.h"
+
+// static
+CefViewAdapter* CefViewAdapter::GetFor(CefRefPtr<CefView> view) {
+  CefViewAdapter* adapter = nullptr;
+  if (view->AsBrowserView()) {
+    adapter = static_cast<CefBrowserViewImpl*>(view->AsBrowserView().get());
+  } else if (view->AsButton()) {
+    CefRefPtr<CefButton> button = view->AsButton();
+    if (button->AsLabelButton()) {
+      CefRefPtr<CefLabelButton> label_button = button->AsLabelButton();
+      if (label_button->AsMenuButton()) {
+        adapter =
+            static_cast<CefMenuButtonImpl*>(label_button->AsMenuButton().get());
+      } else {
+        adapter = static_cast<CefBasicLabelButtonImpl*>(label_button.get());
+      }
+    }
+  } else if (view->AsPanel()) {
+    CefRefPtr<CefPanel> panel = view->AsPanel();
+    if (panel->AsWindow()) {
+      adapter = static_cast<CefWindowImpl*>(panel->AsWindow().get());
+    } else {
+      adapter = static_cast<CefBasicPanelImpl*>(panel.get());
+    }
+  } else if (view->AsScrollView()) {
+    adapter = static_cast<CefScrollViewImpl*>(view->AsScrollView().get());
+  } else if (view->AsTextfield()) {
+    adapter = static_cast<CefTextfieldImpl*>(view->AsTextfield().get());
+  }
+
+  DCHECK(adapter);
+  return adapter;
+}
+
+// static
+CefViewAdapter* CefViewAdapter::GetFor(views::View* view) {
+  CefRefPtr<CefView> cef_view = view_util::GetFor(view, false);
+  if (cef_view)
+    return GetFor(cef_view);
+  return nullptr;
+}
diff --git a/src/libcef/browser/views/view_adapter.h b/src/libcef/browser/views/view_adapter.h
new file mode 100644
index 0000000..39dbf0b
--- /dev/null
+++ b/src/libcef/browser/views/view_adapter.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_VIEW_ADAPTER_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_VIEW_ADAPTER_H_
+#pragma once
+
+#include "include/views/cef_view.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace views {
+class View;
+}
+
+// Exposes a common interface from all CefView implementation objects to
+// simplify the view_util implementation. See comments in view_impl.h for a
+// usage overview.
+class CefViewAdapter {
+ public:
+  CefViewAdapter() {}
+
+  // Returns the CefViewAdapter for the specified |view|.
+  static CefViewAdapter* GetFor(CefRefPtr<CefView> view);
+  static CefViewAdapter* GetFor(views::View* view);
+
+  // Returns the underlying views::View object. Does not transfer ownership.
+  virtual views::View* Get() const = 0;
+
+  // Pass ownership of the underlying views::View object to the caller. This
+  // object keeps an unowned reference to the views::View object. This is called
+  // when the views::View is parented to another views::View.
+  virtual std::unique_ptr<views::View> PassOwnership() = 0;
+
+  // Resume ownership of the underlying views::View object. This is called when
+  // the views::View is no longer parented to another views::View.
+  virtual void ResumeOwnership() = 0;
+
+  // Release all references to the views::View object. This is called when the
+  // views::View is deleted after being parented to another views::View.
+  virtual void Detach() = 0;
+
+  // Override this method to provide a string representation of the View type.
+  // Only implement this method in concrete classes.
+  virtual std::string GetDebugType() = 0;
+
+  // Override this method to provide debug info specific to the View type.
+  virtual void GetDebugInfo(base::DictionaryValue* info,
+                            bool include_children) = 0;
+
+ protected:
+  virtual ~CefViewAdapter() {}
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_VIEW_ADAPTER_H_
diff --git a/src/libcef/browser/views/view_impl.h b/src/libcef/browser/views/view_impl.h
new file mode 100644
index 0000000..3cd6e10
--- /dev/null
+++ b/src/libcef/browser/views/view_impl.h
@@ -0,0 +1,729 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_VIEW_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_VIEW_IMPL_H_
+#pragma once
+
+// CEF exposes views framework functionality via a hierarchy of CefView and
+// related objects. While the goal is to accurately represent views framework
+// capabilities there is not always a direct 1:1 mapping between the CEF
+// implementation and the underlying views implementation. Certain liberties
+// have been taken with the CEF API design to clarify the user experience.
+//
+// CEF implementation overview:
+//
+// CefView-derived classes (CefPanel, CefLabelButton, etc.) are implemented
+// using a specialization of the CefViewImpl template. On Initialize() the
+// CefViewImpl object creates an underlying views::View object via the
+// CreateRootView() method. The views::View objects are implemented using a
+// specialization of the CefViewView template. CefViewView extends the
+// views::View-derived class and executes CefViewDelegate-derived callbacks by
+// overriding views::View methods.
+//
+// Example 1: The CefBasicPanelImpl object created via CefPanel::CreatePanel()
+// has the following object hierarchy:
+//
+//   CefView => CefPanel =>
+//   CefViewImpl<views::View, CefPanel, CefPanelDelegate> =>
+//   CefPanelImpl<views::View, CefPanel, CefPanelDelegate> =>
+//   CefBasicPanelImpl.
+//
+// And the CefBasicPanelView object created via
+// CefBasicPanelImpl::CreateRootView() has the following object hierarchy:
+//
+//   views::View =>
+//   CefViewView<views::View, CefPanelDelegate> =>
+//   CefPanelView<views::View, CefPanelDelegate> =>
+//   CefBasicPanelView.
+//
+// Example 2: In some cases an intermediary type is required to meet CEF
+// template requirements (e.g. CefViewView requires a no-argument constructor).
+// The CefBasicLabelButtonImpl object created via
+// CefLabelButton::CreateLabelButton() has the following object hierarchy:
+//
+//   CefView => CefButton => CefLabelButton =>
+//   CefViewImpl<views::LabelButton, CefLabelButton, CefButtonDelegate> =>
+//   CefButtonImpl<views::LabelButton, CefLabelButton, CefButtonDelegate> =>
+//   CefLabelButtonImpl<views::LabelButton, CefLabelButton,
+//                      CefButtonDelegate> =>
+//   CefBasicLabelButtonImpl
+//
+// And the CefBasicLabelButtonView object created via
+// CefBasicLabelButtonImpl::CreateRootView() has the following object hierarchy:
+//
+//   views::View => views::Button => views::LabelButton =>
+//   LabelButtonEx (used to implement the required no-argument constructor) =>
+//   CefViewView<LabelButtonEx, CefButtonDelegate> =>
+//   CefButtonView<LabelButtonEx, CefButtonDelegate> =>
+//   CefLabelButtonView<LabelButtonEx, CefButtonDelegate> =>
+//   CefBasicLabelButtonView.
+//
+//
+// General design considerations:
+//
+// CefView classes are ref-counted whereas views::View classes are not. There
+// is generally a 1:1 relationship between CefView and views::View objects.
+// However, there may be intermediary views::View objects that are not exposed
+// by the CEF layer. For example:
+// - views::Widget creates views::RootView and views::ContentView child objects;
+// - views::ScrollView creates views::ScrollView::Viewport child objects.
+//
+// The views::View class exposes methods that are not applicable for a subset of
+// views implementations. For example:
+// - Calling AddChildView() on a views::LabelButton is unexpected;
+// - Adding a child to a views::ScrollView requires calling the SetContents()
+//   method instead of AddChildView().
+// To avoid user confusion CEF introduces a CefPanel type that extends CefView
+// and exposes common child management functionality. Types that allow
+// arbitrary children extend CefPanel instead of CefView.
+//
+//
+// Object ownership considerations:
+//
+// On initial creation the CefViewImpl object owns an underlying views::View
+// object (created by overriding the CreateRootView() method) and the
+// views::View object holds a non-ref-counted reference to the CefViewImpl
+// object. If a CefViewImpl is destroyed (all refs released) then the underlying
+// views::View object is deleted.
+//
+// When a views::View object is parented to another views::View (via
+// CefPanel::AddChildView or similar) the ownership semantics change. The
+// CefViewImpl swaps its owned reference for an unowned reference and the
+// views::View gains a ref-counted reference to the CefViewImpl
+// (CefView::IsAttached() now returns true).
+//
+// When a parent views::View is deleted all child views::Views in the view
+// hierarchy are also deleted (see [1] for exceptions). When this happens the
+// ref-counted CefViewImpl reference held by the views::View is released. The
+// CefViewImpl is deleted if the client kept no references, otherwise the
+// CefViewImpl is marked as invalid (CefView::IsValid() now returns false).
+//
+// When a views::View is removed from the view hierarchy (via
+// CefPanel::RemoveChildView or similar) the initial ownership state is
+// restored. The CefViewImpl regains ownership of the views::View and the
+// ref-counted CefViewImpl reference held by the views::View is released.
+//
+// The relationship between CefViewImpl and views::View objects is managed using
+// the view_util:: functions. Type conversion is facilitated using the As*()
+// methods exposed by CefView-derived classes and the CefViewAdapter interface
+// implemented by CefViewImpl. See view_util.[cc|h] for implementation details.
+//
+// Some other object types are also tied to views::View lifetime. For example,
+// CefLayout and the underling views::LayoutManager objects are owned by the
+// views::View that they're assigned to. This relationship is managed using the
+// layout_util:: functions in layout_util.[cc|h].
+//
+// [1] By default views::View objects are deleted when the parent views::View
+//     object is deleted. However, this behavior can be changed either
+//     explicitly by calling set_owned_by_client() or implicitly by using
+//     interfaces like WidgetDelegateView (where WidgetDelegate is-a View, and
+//     the View is deleted when the native Widget is destroyed). CEF
+//     implementations that utilize this behavior must take special care with
+//     object ownership management.
+//
+//
+// To implement a new CefView-derived class:
+//
+// 1. Choose a views class to expose.
+//    * We'll create a new CefFooBar class which exposes a hypothetical
+//      views::FooBar class. The views::FooBar class might look like this:
+//
+//      File ui/views/foo_bar.h:
+//
+//      namespace views {
+//
+//      // FooBar view does a task on child views.
+//      class FooBar : public View {
+//       public:
+//        FooBar();
+//
+//        // Do a task.
+//        void DoTask();
+//        // Called when the task is done.
+//        virtual void OnTaskDone();
+//
+//        // View methods:
+//        void Layout() override;  // Implements custom layout of child views.
+//      };
+//
+//      }  // namespace views
+//
+// 2. Determine the existing CefView-derived class that the new view class
+//    should extend.
+//    * Since in this example CefFooBar can have arbitrary child views we'll
+//      have it extend CefPanel.
+//
+// 3. Determine whether the new view class can use an existing delegate class
+//    (like CefPanelDelegate) or whether it needs its own delegate class.
+//    * Since CefFooBar has an OnTaskDone() callback we'll add a new
+//      CefFooBarDelegate class to expose it.
+//
+// 4. Create new header files in the cef/include/views/ directory.
+//    * Using existing files as a model, the resulting header contents might
+//      look like this:
+//
+//      File cef/include/views/cef_foo_bar.h:
+//
+//      ///
+//      // A FooBar view does a task on child views.
+//      ///
+//      /*--cef(source=library)--*/
+//      class CefFooBar : public CefPanel {
+//       public:
+//        ///
+//        // Create a new FooBar.
+//        ///
+//        /*--cef(optional_param=delegate)--*/
+//        static CefRefPtr<CefFooBar> CreateFooBar(
+//            CefRefPtr<CefFooBarDelegate> delegate);
+//
+//        ///
+//        // Do a task.
+//        ///
+//        /*--cef()--*/
+//        virtual void DoTask() =0;
+//      };
+//
+//      File cef/include/views/cef_foo_bar_delegate.h:
+//
+//      ///
+//      // Implement this interface to handle FooBar events.
+//      ///
+//      /*--cef(source=client)--*/
+//      class CefFooBarDelegate : public CefPanelDelegate {
+//       public:
+//        ///
+//        // Called when the task is done.
+//        ///
+//        /*--cef()--*/
+//        virtual void OnTaskDone(CefRefPtr<CefFooBar> foobar) {}
+//      };
+//
+// 5. Add an As*() method to the CefView-derived class.
+//    * Using existing file contents as a model, make the following changes in
+//      cef/include/views/cef_panel.h:
+//      * Forward declare the CefFooBar class.
+//      * Add a new CefPanel::AsFooBar() method:
+//
+//        ///
+//        // Returns this Panel as a FooBar or NULL if this is not a FooBar.
+//        ///
+//        /*--cef()--*/
+//        virtual CefRefPtr<CefFooBar> AsFooBar() =0;
+//
+// 6. Add a default implementation for the As*() method to the CefViewImpl-
+//    derived class.
+//    * Using existing file contents as a model, make the following changes in
+//      cef/libcef/browser/views/panel_impl.h:
+//      * Include "include/views/cef_foo_bar.h".
+//      * Add a default CefPanelImpl::AsFooBar() implementation:
+//
+//        CefRefPtr<CefFooBar> AsFooBar() override { return nullptr; }
+//
+// 7. Update the CefViewAdapter::GetFor() method implementation to call the
+//    As*() method.
+//    * Using existing file contents as a model, make the following changes in
+//      cef/libcef/browser/views/view_adapter.cc:
+//      * Include "include/views/cef_foo_bar.h".
+//      * Call the AsFooBar() method to identify the adapter object:
+//
+//        ... if (view->AsPanel()) {
+//          CefRefPtr<CefPanel> panel = view->AsPanel();
+//          if (panel->AsFooBar()) {
+//            adapter = static_cast<CefFooBarImpl*>(panel->AsFooBar().get());
+//          } else ...
+//        } else ...
+//
+// 8. Implement the CefViewView-derived class.
+//    * Using existing files as a model (for example, CefBasicPanelView), create
+//      a CefFooBarView class at cef/libcef/browser/views/foo_bar_view.[cc|h].
+//      This class:
+//      * Extends CefPanelView<views::FooBar, CefFooBarDelegate>.
+//      * Overrides the views::FooBar::OnTaskDone method to execute the
+//        CefFooBarDelegate::OnTaskDone callback:
+//
+//        void CefFooBarView::OnTaskDone() {
+//          if (cef_delegate())
+//            cef_delegate()->OnTaskDone(GetCefFooBar());
+//        }
+//
+// 9. Implement the CefViewImpl-derived class.
+//    * Use existing files as a model (for example, CefBasicPanelImpl), create a
+//      CefFooBarImpl class at cef/libcef/browser/views/foo_bar_impl.[cc|h].
+//      This class:
+//      * Extends CefPanelImpl<views::FooBar, CefFooBar, CefFooBarDelegate>.
+//      * Implements AsFooBar() to return |this|.
+//      * Implements CreateRootView() to return a new CefFooBarView instance.
+//      * Implements the CefFooBar::DoTask() method to call
+//        views::FooBar::DoTask():
+//
+//        void CefFooBarImpl::DoTask() {
+//          CEF_REQUIRE_VALID_RETURN_VOID();
+//          root_view()->DoTask();
+//        }
+//
+// 10. Implement the static method that creates the CefViewImpl-derived object
+//     instance.
+//     * Use existing files as a model (for example, CefBasicPanelImpl),
+//       implement the CefFooBar::CreateFooBar static method in
+//       cef/libcef/browser/views/foo_bar_impl.cc. This method:
+//       * Creates a new CefFooBarImpl object.
+//       * Calls Initialize() on the CefFooBarImpl object.
+//       * Returns the CefFooBarImpl object.
+//
+// 11. Add the new source files from #7 and #8 to the 'libcef_static' target in
+//     cef.gyp.
+//
+// 12. Update the CEF project files and build.
+//     * Run cef/tools/translator.[bat|sh] to update the translation layer for
+//       the new/modified classes. This tool needs to be run whenever header
+//       files in the cef/include/ directory are changed.
+//     * Run cef/cef_create_projects.[bat|sh] to update the Ninja build files.
+//     * Build CEF using Ninja.
+//
+
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_scroll_view.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_view.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/view_adapter.h"
+#include "libcef/browser/views/view_util.h"
+
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "ui/views/background.h"
+#include "ui/views/view.h"
+
+// Helpers for template boiler-plate.
+#define CEF_VIEW_IMPL_T                               \
+  template <class ViewsViewClass, class CefViewClass, \
+            class CefViewDelegateClass>
+#define CEF_VIEW_IMPL_A ViewsViewClass, CefViewClass, CefViewDelegateClass
+#define CEF_VIEW_IMPL_D CefViewImpl<CEF_VIEW_IMPL_A>
+
+// Base template for implementing CefView-derived classes. See above comments
+// for a usage overview.
+CEF_VIEW_IMPL_T class CefViewImpl : public CefViewAdapter, public CefViewClass {
+ public:
+  // Necessary for the CEF_REQUIRE_VALID_*() macros to compile.
+  typedef CEF_VIEW_IMPL_D ParentClass;
+
+  // Returns the content views::View object that should be the target of most
+  // customization actions. May be the root view or a child of the root view.
+  virtual views::View* content_view() const { return root_view(); }
+
+  // Returns the CEF delegate as the derived type which may be nullptr.
+  CefViewDelegateClass* delegate() const { return delegate_.get(); }
+
+  // Returns the root views::View object owned by this CefView.
+  ViewsViewClass* root_view() const { return root_view_ref_; }
+
+  // CefViewAdapter methods:
+  views::View* Get() const override { return root_view(); }
+  std::unique_ptr<views::View> PassOwnership() override {
+    DCHECK(root_view_);
+    return std::move(root_view_);
+  }
+  void ResumeOwnership() override {
+    DCHECK(root_view_ref_);
+    DCHECK(!root_view_);
+    root_view_.reset(root_view_ref_);
+  }
+  void Detach() override {
+    if (root_view_)
+      root_view_.reset();
+    root_view_ref_ = nullptr;
+  }
+  void GetDebugInfo(base::DictionaryValue* info,
+                    bool include_children) override {
+    info->SetString("type", GetDebugType());
+    info->SetInteger("id", root_view()->GetID());
+
+    // Use GetBounds() because some subclasses (like CefWindowImpl) override it.
+    const CefRect& bounds = GetBounds();
+    std::unique_ptr<base::DictionaryValue> bounds_value(
+        new base::DictionaryValue());
+    bounds_value->SetInteger("x", bounds.x);
+    bounds_value->SetInteger("y", bounds.y);
+    bounds_value->SetInteger("width", bounds.width);
+    bounds_value->SetInteger("height", bounds.height);
+    info->Set("bounds", std::move(bounds_value));
+  }
+
+  // CefView methods. When adding new As*() methods make sure to update
+  // CefViewAdapter::GetFor() in view_adapter.cc.
+  CefRefPtr<CefBrowserView> AsBrowserView() override { return nullptr; }
+  CefRefPtr<CefButton> AsButton() override { return nullptr; }
+  CefRefPtr<CefPanel> AsPanel() override { return nullptr; }
+  CefRefPtr<CefScrollView> AsScrollView() override { return nullptr; }
+  CefRefPtr<CefTextfield> AsTextfield() override { return nullptr; }
+  CefString GetTypeString() override;
+  CefString ToString(bool include_children) override;
+  bool IsValid() override;
+  bool IsAttached() override;
+  bool IsSame(CefRefPtr<CefView> that) override;
+  CefRefPtr<CefViewDelegate> GetDelegate() override;
+  CefRefPtr<CefWindow> GetWindow() override;
+  int GetID() override;
+  void SetID(int id) override;
+  int GetGroupID() override;
+  void SetGroupID(int group_id) override;
+  CefRefPtr<CefView> GetParentView() override;
+  CefRefPtr<CefView> GetViewForID(int id) override;
+  void SetBounds(const CefRect& bounds) override;
+  CefRect GetBounds() override;
+  CefRect GetBoundsInScreen() override;
+  void SetSize(const CefSize& size) override;
+  CefSize GetSize() override;
+  void SetPosition(const CefPoint& position) override;
+  CefPoint GetPosition() override;
+  CefSize GetPreferredSize() override;
+  void SizeToPreferredSize() override;
+  CefSize GetMinimumSize() override;
+  CefSize GetMaximumSize() override;
+  int GetHeightForWidth(int width) override;
+  void InvalidateLayout() override;
+  void SetVisible(bool visible) override;
+  bool IsVisible() override;
+  bool IsDrawn() override;
+  void SetEnabled(bool enabled) override;
+  bool IsEnabled() override;
+  void SetFocusable(bool focusable) override;
+  bool IsFocusable() override;
+  bool IsAccessibilityFocusable() override;
+  void RequestFocus() override;
+  void SetBackgroundColor(cef_color_t color) override;
+  cef_color_t GetBackgroundColor() override;
+  bool ConvertPointToScreen(CefPoint& point) override;
+  bool ConvertPointFromScreen(CefPoint& point) override;
+  bool ConvertPointToWindow(CefPoint& point) override;
+  bool ConvertPointFromWindow(CefPoint& point) override;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) override;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) override;
+
+ protected:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefViewImpl(CefRefPtr<CefViewDelegateClass> delegate)
+      : delegate_(delegate), root_view_ref_(nullptr) {}
+
+  // Initialize this object.
+  virtual void Initialize() {
+    root_view_.reset(CreateRootView());
+    DCHECK(root_view_.get());
+    root_view_ref_ = root_view_.get();
+    view_util::Register(this);
+    InitializeRootView();
+  }
+
+  // Create the root views::View object.
+  virtual ViewsViewClass* CreateRootView() = 0;
+
+  // Perform required initialization of the root_view() object created by
+  // CreateRootView(). Called after this object has been registered.
+  virtual void InitializeRootView() = 0;
+
+ private:
+  CefRefPtr<CefViewDelegateClass> delegate_;
+
+  // Owned reference to the views::View wrapped by this object. Will be nullptr
+  // before the View is created and after the View's ownership is transferred.
+  std::unique_ptr<ViewsViewClass> root_view_;
+
+  // Unowned reference to the views::View wrapped by this object. Will be
+  // nullptr before the View is created and after the View is destroyed.
+  ViewsViewClass* root_view_ref_;
+};
+
+CEF_VIEW_IMPL_T CefString CEF_VIEW_IMPL_D::GetTypeString() {
+  CEF_REQUIRE_UIT_RETURN(CefString());
+  return GetDebugType();
+}
+
+CEF_VIEW_IMPL_T CefString CEF_VIEW_IMPL_D::ToString(bool include_children) {
+  CEF_REQUIRE_UIT_RETURN(CefString());
+  std::unique_ptr<base::DictionaryValue> info(new base::DictionaryValue());
+  if (IsValid())
+    GetDebugInfo(info.get(), include_children);
+  else
+    info->SetString("type", GetDebugType());
+
+  std::string json_string;
+  base::JSONWriter::WriteWithOptions(*info, 0, &json_string);
+  return json_string;
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsValid() {
+  CEF_REQUIRE_UIT_RETURN(false);
+  return !!root_view_ref_;
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsAttached() {
+  CEF_REQUIRE_UIT_RETURN(false);
+  return !root_view_.get();
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsSame(CefRefPtr<CefView> that) {
+  CEF_REQUIRE_UIT_RETURN(false);
+  CefViewImpl* that_impl = static_cast<CefViewImpl*>(that.get());
+  if (!that_impl)
+    return false;
+  return this == that_impl;
+}
+
+CEF_VIEW_IMPL_T CefRefPtr<CefViewDelegate> CEF_VIEW_IMPL_D::GetDelegate() {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  return delegate();
+}
+
+CEF_VIEW_IMPL_T CefRefPtr<CefWindow> CEF_VIEW_IMPL_D::GetWindow() {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  if (root_view())
+    return view_util::GetWindowFor(root_view()->GetWidget());
+  return nullptr;
+}
+
+CEF_VIEW_IMPL_T int CEF_VIEW_IMPL_D::GetID() {
+  CEF_REQUIRE_VALID_RETURN(0);
+  return root_view()->GetID();
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetID(int id) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetID(id);
+}
+
+CEF_VIEW_IMPL_T int CEF_VIEW_IMPL_D::GetGroupID() {
+  CEF_REQUIRE_VALID_RETURN(0);
+  return root_view()->GetGroup();
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetGroupID(int group_id) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (root_view()->GetGroup() != -1)
+    return;
+  root_view()->SetGroup(group_id);
+}
+
+CEF_VIEW_IMPL_T CefRefPtr<CefView> CEF_VIEW_IMPL_D::GetParentView() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  views::View* view = root_view()->parent();
+  if (!view)
+    return nullptr;
+  return view_util::GetFor(view, true);
+}
+
+CEF_VIEW_IMPL_T CefRefPtr<CefView> CEF_VIEW_IMPL_D::GetViewForID(int id) {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  views::View* view = root_view()->GetViewByID(id);
+  if (!view)
+    return nullptr;
+  return view_util::GetFor(view, true);
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetBounds(const CefRect& bounds) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetBoundsRect(
+      gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
+}
+
+CEF_VIEW_IMPL_T CefRect CEF_VIEW_IMPL_D::GetBounds() {
+  CEF_REQUIRE_VALID_RETURN(CefRect());
+  const gfx::Rect& bounds = root_view()->bounds();
+  return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+}
+
+CEF_VIEW_IMPL_T CefRect CEF_VIEW_IMPL_D::GetBoundsInScreen() {
+  CEF_REQUIRE_VALID_RETURN(CefRect());
+  const gfx::Rect& bounds = root_view()->GetBoundsInScreen();
+  return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetSize(const CefSize& size) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetSize(gfx::Size(size.width, size.height));
+}
+
+CEF_VIEW_IMPL_T CefSize CEF_VIEW_IMPL_D::GetSize() {
+  CEF_REQUIRE_VALID_RETURN(CefSize());
+  // Call GetBounds() since child classes may override it.
+  const CefRect& bounds = GetBounds();
+  return CefSize(bounds.width, bounds.height);
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetPosition(const CefPoint& position) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetPosition(gfx::Point(position.x, position.y));
+}
+
+CEF_VIEW_IMPL_T CefPoint CEF_VIEW_IMPL_D::GetPosition() {
+  CEF_REQUIRE_VALID_RETURN(CefPoint());
+  // Call GetBounds() since child classes may override it.
+  const CefRect& bounds = GetBounds();
+  return CefPoint(bounds.x, bounds.y);
+}
+
+CEF_VIEW_IMPL_T CefSize CEF_VIEW_IMPL_D::GetPreferredSize() {
+  CEF_REQUIRE_VALID_RETURN(CefSize());
+  const gfx::Size& size = root_view()->GetPreferredSize();
+  return CefSize(size.width(), size.height());
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SizeToPreferredSize() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SizeToPreferredSize();
+}
+
+CEF_VIEW_IMPL_T CefSize CEF_VIEW_IMPL_D::GetMinimumSize() {
+  CEF_REQUIRE_VALID_RETURN(CefSize());
+  const gfx::Size& size = root_view()->GetMinimumSize();
+  return CefSize(size.width(), size.height());
+}
+
+CEF_VIEW_IMPL_T CefSize CEF_VIEW_IMPL_D::GetMaximumSize() {
+  CEF_REQUIRE_VALID_RETURN(CefSize());
+  const gfx::Size& size = root_view()->GetMaximumSize();
+  return CefSize(size.width(), size.height());
+}
+
+CEF_VIEW_IMPL_T int CEF_VIEW_IMPL_D::GetHeightForWidth(int width) {
+  CEF_REQUIRE_VALID_RETURN(0);
+  return root_view()->GetHeightForWidth(width);
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::InvalidateLayout() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->InvalidateLayout();
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetVisible(bool visible) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetVisible(visible);
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsVisible() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->GetVisible();
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsDrawn() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->IsDrawn();
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetEnabled(bool enabled) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetEnabled(enabled);
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsEnabled() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->GetEnabled();
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetFocusable(bool focusable) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->SetFocusBehavior(focusable ? views::View::FocusBehavior::ALWAYS
+                                          : views::View::FocusBehavior::NEVER);
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsFocusable() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->IsFocusable();
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::IsAccessibilityFocusable() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  return root_view()->IsAccessibilityFocusable();
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::RequestFocus() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  root_view()->RequestFocus();
+}
+
+CEF_VIEW_IMPL_T void CEF_VIEW_IMPL_D::SetBackgroundColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  content_view()->SetBackground(views::CreateSolidBackground(color));
+}
+
+CEF_VIEW_IMPL_T cef_color_t CEF_VIEW_IMPL_D::GetBackgroundColor() {
+  CEF_REQUIRE_VALID_RETURN(0U);
+  return content_view()->background()->get_color();
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::ConvertPointToScreen(CefPoint& point) {
+  CEF_REQUIRE_VALID_RETURN(false);
+  gfx::Point gfx_point = gfx::Point(point.x, point.y);
+  if (!view_util::ConvertPointToScreen(root_view(), &gfx_point, false))
+    return false;
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+  return true;
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::ConvertPointFromScreen(CefPoint& point) {
+  CEF_REQUIRE_VALID_RETURN(false);
+  gfx::Point gfx_point = gfx::Point(point.x, point.y);
+  if (!view_util::ConvertPointFromScreen(root_view(), &gfx_point, false))
+    return false;
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+  return true;
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::ConvertPointToWindow(CefPoint& point) {
+  CEF_REQUIRE_VALID_RETURN(false);
+  gfx::Point gfx_point = gfx::Point(point.x, point.y);
+  if (!view_util::ConvertPointToWindow(root_view(), &gfx_point))
+    return false;
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+  return true;
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::ConvertPointFromWindow(CefPoint& point) {
+  CEF_REQUIRE_VALID_RETURN(false);
+  gfx::Point gfx_point = gfx::Point(point.x, point.y);
+  if (!view_util::ConvertPointFromWindow(root_view(), &gfx_point))
+    return false;
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+  return true;
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::ConvertPointToView(
+    CefRefPtr<CefView> view,
+    CefPoint& point) {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (!root_view()->GetWidget())
+    return false;
+  views::View* target_view = view_util::GetFor(view);
+  if (!target_view || target_view->GetWidget() != root_view()->GetWidget())
+    return false;
+  gfx::Point gfx_point = gfx::Point(point.x, point.y);
+  views::View::ConvertPointToTarget(root_view(), target_view, &gfx_point);
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+  return true;
+}
+
+CEF_VIEW_IMPL_T bool CEF_VIEW_IMPL_D::ConvertPointFromView(
+    CefRefPtr<CefView> view,
+    CefPoint& point) {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (!root_view()->GetWidget())
+    return false;
+  views::View* target_view = view_util::GetFor(view);
+  if (!target_view || target_view->GetWidget() != root_view()->GetWidget())
+    return false;
+  gfx::Point gfx_point = gfx::Point(point.x, point.y);
+  views::View::ConvertPointToTarget(target_view, root_view(), &gfx_point);
+  point = CefPoint(gfx_point.x(), gfx_point.y());
+  return true;
+}
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_VIEW_IMPL_H_
diff --git a/src/libcef/browser/views/view_util.cc b/src/libcef/browser/views/view_util.cc
new file mode 100644
index 0000000..4e22f99
--- /dev/null
+++ b/src/libcef/browser/views/view_util.cc
@@ -0,0 +1,312 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/view_util.h"
+
+#include <utility>
+
+#include "libcef/browser/views/view_adapter.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/non_client_view.h"
+
+#if defined(OS_WIN)
+#include "ui/display/win/screen_win.h"
+#endif
+
+namespace view_util {
+
+namespace {
+
+// Manages the association between views::View and CefView instances.
+class UserData : public base::SupportsUserData::Data {
+ public:
+  // Create the initial association between the views::View and the CefView. The
+  // CefView owns the views::View at this stage.
+  static void Register(CefRefPtr<CefView> cef_view) {
+    DCHECK(cef_view->IsValid());
+    DCHECK(!cef_view->IsAttached());
+
+    views::View* view = CefViewAdapter::GetFor(cef_view)->Get();
+    DCHECK(view);
+
+    // The CefView should not already be registered.
+    DCHECK(!view->GetUserData(UserDataKey()));
+
+    view->SetUserData(UserDataKey(), base::WrapUnique(new UserData(cef_view)));
+  }
+
+  static CefRefPtr<CefView> GetFor(const views::View* view) {
+    DCHECK(view);
+    UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
+    if (data)
+      return data->view_ref_;
+    return nullptr;
+  }
+
+  // Transfer ownership of the views::View to the caller. The views::View will
+  // gain a ref-counted reference to the CefView and the CefView will keep an
+  // unowned reference to the views::View. Destruction of the views::View will
+  // release the ref-counted reference to the CefView.
+  static std::unique_ptr<views::View> PassOwnership(CefRefPtr<CefView> cef_view)
+      WARN_UNUSED_RESULT {
+    DCHECK(cef_view->IsValid());
+    DCHECK(!cef_view->IsAttached());
+
+    std::unique_ptr<views::View> view =
+        CefViewAdapter::GetFor(cef_view)->PassOwnership();
+    DCHECK(view);
+
+    UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
+    DCHECK(data);
+    data->TakeReference();
+
+    return view;
+  }
+
+  // The CefView resumes ownership of the views::View. The views::View no longer
+  // keeps a ref-counted reference to the CefView.
+  static void ResumeOwnership(CefRefPtr<CefView> cef_view) {
+    DCHECK(cef_view->IsValid());
+    DCHECK(cef_view->IsAttached());
+
+    CefViewAdapter* adapter = CefViewAdapter::GetFor(cef_view);
+    adapter->ResumeOwnership();
+
+    views::View* view = adapter->Get();
+    DCHECK(view);
+
+    UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
+    DCHECK(data);
+    data->ReleaseReference();
+  }
+
+ private:
+  friend std::default_delete<UserData>;
+
+  explicit UserData(CefRefPtr<CefView> cef_view) : view_ref_(cef_view.get()) {
+    DCHECK(view_ref_);
+  }
+
+  ~UserData() override {
+    if (view_) {
+      // The CefView does not own the views::View. Remove the CefView's
+      // reference to the views::View.
+      CefViewAdapter::GetFor(view_)->Detach();
+    }
+  }
+
+  void TakeReference() { view_ = view_ref_; }
+
+  void ReleaseReference() { view_ = nullptr; }
+
+  static void* UserDataKey() {
+    // We just need a unique constant. Use the address of a static that
+    // COMDAT folding won't touch in an optimizing linker.
+    static int data_key = 0;
+    return reinterpret_cast<void*>(&data_key);
+  }
+
+  CefRefPtr<CefView> view_;
+  CefView* view_ref_;
+};
+
+}  // namespace
+
+const SkColor kDefaultBackgroundColor = SkColorSetARGB(255, 255, 255, 255);
+const char kDefaultFontList[] = "Arial, Helvetica, 14px";
+
+void Register(CefRefPtr<CefView> view) {
+  UserData::Register(view);
+}
+
+CefRefPtr<CefView> GetFor(const views::View* view, bool find_known_parent) {
+  if (!view)
+    return nullptr;
+
+  if (!find_known_parent)
+    return UserData::GetFor(view);
+
+  CefRefPtr<CefView> cef_view;
+  const views::View* current_view = view;
+  do {
+    cef_view = UserData::GetFor(current_view);
+    if (cef_view)
+      break;
+    current_view = current_view->parent();
+  } while (current_view);
+
+  return cef_view;
+}
+
+views::View* GetFor(CefRefPtr<CefView> view) {
+  return CefViewAdapter::GetFor(view)->Get();
+}
+
+std::unique_ptr<views::View> PassOwnership(CefRefPtr<CefView> view) {
+  return UserData::PassOwnership(view);
+}
+
+void ResumeOwnership(CefRefPtr<CefView> view) {
+  UserData::ResumeOwnership(view);
+}
+
+CefRefPtr<CefWindow> GetWindowFor(views::Widget* widget) {
+  CefRefPtr<CefWindow> window;
+
+  if (widget) {
+    // The views::WidgetDelegate should be a CefWindowView and |content_view|
+    // should be the same CefWindowView. However, just in case the views::Widget
+    // was created by something else let's go about this the safer way.
+    views::View* content_view = widget->widget_delegate()->GetContentsView();
+    CefRefPtr<CefView> cef_view = GetFor(content_view, false);
+    if (cef_view && cef_view->AsPanel())
+      window = cef_view->AsPanel()->AsWindow();
+
+    // The Window should always exist if we created the views::Widget.
+    DCHECK(window);
+  }
+
+  return window;
+}
+
+display::Display GetDisplayNearestPoint(const gfx::Point& point,
+                                        bool input_pixel_coords) {
+  gfx::Point find_point = point;
+#if defined(OS_WIN)
+  if (input_pixel_coords) {
+    find_point = gfx::ToFlooredPoint(
+        display::win::ScreenWin::ScreenToDIPPoint(gfx::PointF(point)));
+  }
+#endif
+  return display::Screen::GetScreen()->GetDisplayNearestPoint(find_point);
+}
+
+display::Display GetDisplayMatchingBounds(const gfx::Rect& bounds,
+                                          bool input_pixel_coords) {
+  gfx::Rect find_bounds = bounds;
+#if defined(OS_WIN)
+  if (input_pixel_coords) {
+    find_bounds =
+        display::win::ScreenWin::ScreenToDIPRect(nullptr, find_bounds);
+  }
+#endif
+  return display::Screen::GetScreen()->GetDisplayMatching(find_bounds);
+}
+
+void ConvertPointFromPixels(gfx::Point* point, int device_scale_factor) {
+  *point = gfx::ToFlooredPoint(
+      gfx::ScalePoint(gfx::PointF(*point), 1.0f / device_scale_factor));
+}
+
+void ConvertPointToPixels(gfx::Point* point, int device_scale_factor) {
+  *point = gfx::ToFlooredPoint(
+      gfx::ScalePoint(gfx::PointF(*point), device_scale_factor));
+}
+
+bool ConvertPointToScreen(views::View* view,
+                          gfx::Point* point,
+                          bool output_pixel_coords) {
+  if (!view->GetWidget())
+    return false;
+
+  views::View::ConvertPointToScreen(view, point);
+
+  if (output_pixel_coords) {
+    const display::Display& display = GetDisplayNearestPoint(*point, false);
+    ConvertPointToPixels(point, display.device_scale_factor());
+  }
+
+  return true;
+}
+
+bool ConvertPointFromScreen(views::View* view,
+                            gfx::Point* point,
+                            bool input_pixel_coords) {
+  if (!view->GetWidget())
+    return false;
+
+  if (input_pixel_coords) {
+    const display::Display& display = GetDisplayNearestPoint(*point, true);
+    ConvertPointFromPixels(point, display.device_scale_factor());
+  }
+
+  views::View::ConvertPointFromScreen(view, point);
+
+  return true;
+}
+
+bool ConvertPointToWindow(views::View* view, gfx::Point* point) {
+  views::Widget* widget = view->GetWidget();
+  if (!widget)
+    return false;
+
+  views::View::ConvertPointToWidget(view, point);
+
+  if (widget->non_client_view()) {
+    views::NonClientFrameView* non_client_frame_view =
+        widget->non_client_view()->frame_view();
+    if (non_client_frame_view) {
+      // When using a custom drawn NonClientFrameView the native Window will not
+      // know the actual client bounds. Adjust the native Window bounds for the
+      // reported client bounds.
+      const gfx::Rect& client_bounds =
+          non_client_frame_view->GetBoundsForClientView();
+      *point -= client_bounds.OffsetFromOrigin();
+    }
+  }
+
+  return true;
+}
+
+bool ConvertPointFromWindow(views::View* view, gfx::Point* point) {
+  views::Widget* widget = view->GetWidget();
+  if (!widget)
+    return false;
+
+  if (widget->non_client_view()) {
+    views::NonClientFrameView* non_client_frame_view =
+        widget->non_client_view()->frame_view();
+    if (non_client_frame_view) {
+      // When using a custom drawn NonClientFrameView the native Window will not
+      // know the actual client bounds. Adjust the native Window bounds for the
+      // reported client bounds.
+      const gfx::Rect& client_bounds =
+          non_client_frame_view->GetBoundsForClientView();
+      *point += client_bounds.OffsetFromOrigin();
+    }
+  }
+
+  views::View::ConvertPointFromWidget(view, point);
+
+  return true;
+}
+
+gfx::NativeWindow GetNativeWindow(views::Widget* widget) {
+  if (widget) {
+    aura::Window* window = widget->GetNativeWindow();
+    if (window)
+      return window->GetRootWindow();
+  }
+  return nullptr;
+}
+
+CefWindowHandle GetWindowHandle(views::Widget* widget) {
+  // Same implementation as views::HWNDForView() but cross-platform.
+  if (widget) {
+    aura::Window* window = widget->GetNativeWindow();
+    if (window && window->GetRootWindow())
+      return window->GetHost()->GetAcceleratedWidget();
+  }
+  return kNullWindowHandle;
+}
+
+}  // namespace view_util
diff --git a/src/libcef/browser/views/view_util.h b/src/libcef/browser/views/view_util.h
new file mode 100644
index 0000000..6590f5b
--- /dev/null
+++ b/src/libcef/browser/views/view_util.h
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_VIEW_UTIL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_VIEW_UTIL_H_
+#pragma once
+
+#include "include/views/cef_view.h"
+#include "include/views/cef_window.h"
+
+#include "ui/views/view.h"
+
+namespace display {
+class Display;
+}
+
+namespace gfx {
+class Point;
+}
+
+namespace views {
+class Widget;
+}
+
+#define CEF_REQUIRE_VALID_RETURN(ret) \
+  if (!ParentClass::IsValid())        \
+    return ret;
+
+#define CEF_REQUIRE_VALID_RETURN_VOID() \
+  if (!ParentClass::IsValid())          \
+    return;
+
+// The below functions manage the relationship between CefView and views::View
+// instances. See comments in view_impl.h for a usage overview.
+
+namespace view_util {
+
+// Default values.
+extern const SkColor kDefaultBackgroundColor;
+extern const char kDefaultFontList[];
+
+// Called when a CefView is initialized to create the initial association
+// between the underlying views::View and |view|. The CefView owns the
+// views::View at this stage.
+void Register(CefRefPtr<CefView> view);
+
+// Returns the CefView object associated with the specified |view|. If no
+// CefView is associated with |view| and |find_known_parent| is true then this
+// function will return the closest parent views::View with an associated
+// CefView.
+CefRefPtr<CefView> GetFor(const views::View* view, bool find_known_parent);
+
+// Returns the views::View object associated with the specified |view|.
+// Ownership of the views::View object does not change.
+views::View* GetFor(CefRefPtr<CefView> view);
+
+// Returns the views::View object associated with the specified |view| and
+// passes ownership to the caller. The views::View object should then be passed
+// to another views::View via views::View or views::LayoutManager methods. The
+// views::View will keep a ref-counted reference to |view|, and |view| will keep
+// an un-owned reference to the views::View. These references will reset when
+// the views::View object is deleted or when ResumeOwnership() is called.
+std::unique_ptr<views::View> PassOwnership(CefRefPtr<CefView> view)
+    WARN_UNUSED_RESULT;
+
+// Causes |view| to resume ownership of the views::View object. Should be called
+// after removing the views::View object from its previous parent.
+void ResumeOwnership(CefRefPtr<CefView> view);
+
+// Returns the Window associated with |widget|.
+CefRefPtr<CefWindow> GetWindowFor(views::Widget* widget);
+
+// Returns the Display nearest |point|. Set |input_pixel_coords| to true if
+// |point| is in pixel coordinates instead of density independent pixels (DIP).
+display::Display GetDisplayNearestPoint(const gfx::Point& point,
+                                        bool input_pixel_coords);
+
+// Returns the Display that most closely intersects |bounds|.  Set
+// |input_pixel_coords| to true if |bounds| is in pixel coordinates instead of
+// density independent pixels (DIP).
+display::Display GetDisplayMatchingBounds(const gfx::Rect& bounds,
+                                          bool input_pixel_coords);
+
+// Convert |point| from pixel coordinates to density independent pixels (DIP)
+// using |device_scale_factor|.
+void ConvertPointFromPixels(gfx::Point* point, int device_scale_factor);
+
+// Convert |point| to pixel coordinates from density independent pixels (DIP)
+// using |device_scale_factor|.
+void ConvertPointToPixels(gfx::Point* point, int device_scale_factor);
+
+// Convert |point| from |view| to screen coordinates. If |output_pixel_coords|
+// is true then |point| will be output in pixel coordinates instead of density
+// independent pixels (DIP). Returns false if |view| does not currently belong
+// to a Widget.
+bool ConvertPointToScreen(views::View* view,
+                          gfx::Point* point,
+                          bool output_pixel_coords);
+
+// Convert |point| from screen to |view| coordinates. Set |input_pixel_coords|
+// to true when |point| is being input in pixel coordinates instead of density
+// independent pixels (DIP). Returns false if |view| does not currently belong
+// to a Widget.
+bool ConvertPointFromScreen(views::View* view,
+                            gfx::Point* point,
+                            bool input_pixel_coords);
+
+// Convert |point| from |view| to window (Widget) coordinates. Returns false if
+// |view| does not currently belong to a Widget.
+bool ConvertPointToWindow(views::View* view, gfx::Point* point);
+
+// Convert |point| from window (Widget) to |view| coordinates. Returns false if
+// |view| does not currently belong to a Widget.
+bool ConvertPointFromWindow(views::View* view, gfx::Point* point);
+
+// Returns the native window handle for |widget|. May return nullptr.
+gfx::NativeWindow GetNativeWindow(views::Widget* widget);
+
+// Returns the platform window handle for |widget|. May return nullptr.
+CefWindowHandle GetWindowHandle(views::Widget* widget);
+
+}  // namespace view_util
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_VIEW_UTIL_H_
diff --git a/src/libcef/browser/views/view_view.h b/src/libcef/browser/views/view_view.h
new file mode 100644
index 0000000..4ebce17
--- /dev/null
+++ b/src/libcef/browser/views/view_view.h
@@ -0,0 +1,211 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_VIEW_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_VIEW_VIEW_H_
+#pragma once
+
+#include "include/views/cef_view.h"
+#include "include/views/cef_view_delegate.h"
+
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/view_util.h"
+
+#include "base/logging.h"
+#include "ui/views/background.h"
+#include "ui/views/view.h"
+
+// Helpers for template boiler-plate.
+#define CEF_VIEW_VIEW_T \
+  template <class ViewsViewClass, class CefViewDelegateClass>
+#define CEF_VIEW_VIEW_A ViewsViewClass, CefViewDelegateClass
+#define CEF_VIEW_VIEW_D CefViewView<CEF_VIEW_VIEW_A>
+
+// Base template for implementing views::View-derived classes. The views::View-
+// derived type passed to this template must provide a no-argument constructor
+// (for example, see LabelButtonEx from basic_label_button_view.h). See comments
+// in view_impl.h for a usage overview.
+CEF_VIEW_VIEW_T class CefViewView : public ViewsViewClass {
+ public:
+  typedef ViewsViewClass ParentClass;
+
+  // Should be created from CreateRootView() in a CefViewImpl-derived class.
+  // Do not call complex views::View-derived methods from a CefViewView-derived
+  // constructor as they may attempt to call back into CefViewImpl before
+  // registration has been performed. |cef_delegate| may be nullptr.
+  explicit CefViewView(CefViewDelegateClass* cef_delegate)
+      : cef_delegate_(cef_delegate) {}
+
+  // Should be called from InitializeRootView() in the CefViewImpl-derived
+  // class that created this object. This method will be called after
+  // CefViewImpl registration has completed so it is safe to call complex
+  // views::View-derived methods here.
+  virtual void Initialize() {
+    // Use our defaults instead of the Views framework defaults.
+    ParentClass::SetBackground(
+        views::CreateSolidBackground(view_util::kDefaultBackgroundColor));
+  }
+
+  // Returns the CefViewDelegate-derived delegate associated with this view.
+  // May return nullptr.
+  CefViewDelegateClass* cef_delegate() const { return cef_delegate_; }
+
+  // Returns the CefView associated with this view. May return nullptr during
+  // CefViewImpl initialization. If callbacks to the CefViewImpl-derived class
+  // are required define an interface that the CefViewImpl-derived class can
+  // implement and pass as an unowned instance to this object's constructor (see
+  // for example CefWindowView).
+  CefRefPtr<CefView> GetCefView() const {
+    CefRefPtr<CefView> view = view_util::GetFor(this, false);
+    DCHECK(view);
+    return view;
+  }
+
+  // views::View methods:
+  gfx::Size CalculatePreferredSize() const override;
+  gfx::Size GetMinimumSize() const override;
+  gfx::Size GetMaximumSize() const override;
+  int GetHeightForWidth(int w) const override;
+  void Layout() override;
+  void ViewHierarchyChanged(
+      const views::ViewHierarchyChangedDetails& details) override;
+  void OnFocus() override;
+  void OnBlur() override;
+
+  // Return true if this View is expected to have a minimum size (for example,
+  // a button where the minimum size is based on the label).
+  virtual bool HasMinimumSize() const { return false; }
+
+ private:
+  void NotifyChildViewChanged(
+      const views::ViewHierarchyChangedDetails& details);
+  void NotifyParentViewChanged(
+      const views::ViewHierarchyChangedDetails& details);
+
+  // Not owned by this object.
+  CefViewDelegateClass* cef_delegate_;
+};
+
+CEF_VIEW_VIEW_T gfx::Size CEF_VIEW_VIEW_D::CalculatePreferredSize() const {
+  gfx::Size result;
+  if (cef_delegate()) {
+    CefSize cef_size = cef_delegate()->GetPreferredSize(GetCefView());
+    if (!cef_size.IsEmpty())
+      result = gfx::Size(cef_size.width, cef_size.height);
+  }
+  if (result.IsEmpty())
+    result = ParentClass::CalculatePreferredSize();
+  if (result.IsEmpty()) {
+    // Some layouts like BoxLayout expect the preferred size to be non-empty.
+    // The user may have set the size explicitly. Therefore return the current
+    // size as the preferred size.
+    result = ParentClass::size();
+  }
+  return result;
+}
+
+CEF_VIEW_VIEW_T gfx::Size CEF_VIEW_VIEW_D::GetMinimumSize() const {
+  gfx::Size result;
+  if (cef_delegate()) {
+    CefSize cef_size = cef_delegate()->GetMinimumSize(GetCefView());
+    if (!cef_size.IsEmpty())
+      result = gfx::Size(cef_size.width, cef_size.height);
+  }
+  // We don't want to call ParentClass::GetMinimumSize() in all cases because
+  // the default views::View implementation will call GetPreferredSize(). That
+  // may result in size() being returned which keeps the View from shrinking.
+  if (result.IsEmpty() && HasMinimumSize())
+    result = ParentClass::GetMinimumSize();
+  return result;
+}
+
+CEF_VIEW_VIEW_T gfx::Size CEF_VIEW_VIEW_D::GetMaximumSize() const {
+  gfx::Size result;
+  if (cef_delegate()) {
+    CefSize cef_size = cef_delegate()->GetMaximumSize(GetCefView());
+    if (!cef_size.IsEmpty())
+      result = gfx::Size(cef_size.width, cef_size.height);
+  }
+  if (result.IsEmpty())
+    result = ParentClass::GetMaximumSize();
+  return result;
+}
+
+CEF_VIEW_VIEW_T int CEF_VIEW_VIEW_D::GetHeightForWidth(int w) const {
+  int result = 0;
+  if (cef_delegate())
+    result = cef_delegate()->GetHeightForWidth(GetCefView(), w);
+  if (result == 0)
+    result = ParentClass::GetHeightForWidth(w);
+  if (result == 0) {
+    // Some layouts like FillLayout will ignore the preferred size if this view
+    // has no children. We want to use the preferred size if not otherwise
+    // specified.
+    result = ParentClass::GetPreferredSize().height();
+  }
+  return result;
+}
+
+CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::Layout() {
+  ParentClass::Layout();
+
+  // If Layout() did not provide a size then use the preferred size.
+  if (ParentClass::size().IsEmpty())
+    ParentClass::SizeToPreferredSize();
+}
+
+CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::ViewHierarchyChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  NotifyChildViewChanged(details);
+  NotifyParentViewChanged(details);
+  ParentClass::ViewHierarchyChanged(details);
+}
+
+CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::OnFocus() {
+  if (cef_delegate())
+    cef_delegate()->OnFocus(GetCefView());
+  ParentClass::OnFocus();
+}
+
+CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::OnBlur() {
+  if (cef_delegate())
+    cef_delegate()->OnBlur(GetCefView());
+  ParentClass::OnBlur();
+}
+
+CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::NotifyChildViewChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  if (!cef_delegate())
+    return;
+
+  // Only interested with the parent is |this| object and the notification is
+  // about an immediate child (notifications are also sent for grandchildren).
+  if (details.parent != this || details.child->parent() != this)
+    return;
+
+  // Only notify for children that have a known CEF root view. For example,
+  // don't notify when ScrollView adds child scroll bars.
+  CefRefPtr<CefView> child = view_util::GetFor(details.child, false);
+  if (child)
+    cef_delegate()->OnChildViewChanged(GetCefView(), details.is_add, child);
+}
+
+CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::NotifyParentViewChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  if (!cef_delegate())
+    return;
+
+  // Only interested when the child is |this| object and notification is about
+  // the immediate parent (notifications are sent for all parents).
+  if (details.child != this || details.parent != ParentClass::parent())
+    return;
+
+  // The immediate parent might be an intermediate view so find the closest
+  // known CEF root view.
+  CefRefPtr<CefView> parent = view_util::GetFor(details.parent, true);
+  DCHECK(parent);
+  cef_delegate()->OnParentViewChanged(GetCefView(), details.is_add, parent);
+}
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_VIEW_VIEW_H_
diff --git a/src/libcef/browser/views/window_impl.cc b/src/libcef/browser/views/window_impl.cc
new file mode 100644
index 0000000..eeb1fcc
--- /dev/null
+++ b/src/libcef/browser/views/window_impl.cc
@@ -0,0 +1,650 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/window_impl.h"
+
+#include "libcef/browser/browser_util.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/browser/views/display_impl.h"
+#include "libcef/browser/views/fill_layout_impl.h"
+#include "libcef/browser/views/layout_util.h"
+#include "libcef/browser/views/view_util.h"
+#include "libcef/browser/views/window_view.h"
+
+#include "ui/base/test/ui_controls.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/controls/button/menu_button.h"
+#include "ui/views/controls/menu/menu_runner.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/test/ui_controls_factory_aura.h"
+#include "ui/aura/window.h"
+#include "ui/base/test/ui_controls_aura.h"
+#if defined(OS_LINUX) && defined(USE_X11)
+#include "ui/views/test/ui_controls_factory_desktop_aurax11.h"
+#endif
+#endif
+
+#if defined(OS_WIN)
+#include "ui/display/win/screen_win.h"
+#endif
+
+namespace {
+
+// Based on chrome/test/base/interactive_ui_tests_main.cc.
+void InitializeUITesting() {
+  static bool initialized = false;
+  if (!initialized) {
+    ui_controls::EnableUIControls();
+
+#if defined(USE_AURA)
+#if defined(OS_LINUX) && defined(USE_X11)
+    ui_controls::InstallUIControlsAura(
+        views::test::CreateUIControlsDesktopAura());
+#else
+    ui_controls::InstallUIControlsAura(
+        aura::test::CreateUIControlsAura(nullptr));
+#endif
+#endif
+
+    initialized = true;
+  }
+}
+
+#if defined(USE_AURA)
+
+// This class forwards KeyEvents to the CefWindowImpl associated with a widget.
+// This allows KeyEvents to be processed after all other targets.
+// Events originating from CefBrowserView will instead be delivered via
+// CefBrowserViewImpl::HandleKeyboardEvent.
+class CefUnhandledKeyEventHandler : public ui::EventHandler {
+ public:
+  CefUnhandledKeyEventHandler(CefWindowImpl* window_impl, views::Widget* widget)
+      : window_impl_(window_impl),
+        widget_(widget),
+        window_(widget->GetNativeWindow()) {
+    DCHECK(window_);
+    window_->AddPostTargetHandler(this);
+  }
+
+  ~CefUnhandledKeyEventHandler() override {
+    window_->RemovePostTargetHandler(this);
+  }
+
+  // Implementation of ui::EventHandler:
+  void OnKeyEvent(ui::KeyEvent* event) override {
+    // Give the FocusManager a chance to handle accelerators first.
+    // Widget::OnKeyEvent would normally call this after all EventHandlers have
+    // had a shot but we don't want to wait.
+    if (widget_->GetFocusManager() &&
+        !widget_->GetFocusManager()->OnKeyEvent(*event)) {
+      event->StopPropagation();
+      return;
+    }
+
+    CefKeyEvent cef_event;
+    if (browser_util::GetCefKeyEvent(*event, cef_event) &&
+        window_impl_->OnKeyEvent(cef_event)) {
+      event->StopPropagation();
+    }
+  }
+
+ private:
+  // Members are guaranteed to outlive this object.
+  CefWindowImpl* window_impl_;
+  views::Widget* widget_;
+
+  // |window_| is the event target that is associated with this class.
+  aura::Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefUnhandledKeyEventHandler);
+};
+
+#endif  // defined(USE_AURA)
+
+}  // namespace
+
+// static
+CefRefPtr<CefWindow> CefWindow::CreateTopLevelWindow(
+    CefRefPtr<CefWindowDelegate> delegate) {
+  return CefWindowImpl::Create(delegate);
+}
+
+// static
+CefRefPtr<CefWindowImpl> CefWindowImpl::Create(
+    CefRefPtr<CefWindowDelegate> delegate) {
+  CEF_REQUIRE_UIT_RETURN(nullptr);
+  CefRefPtr<CefWindowImpl> window = new CefWindowImpl(delegate);
+  window->Initialize();
+  window->CreateWidget();
+  if (delegate)
+    delegate->OnWindowCreated(window.get());
+  return window;
+}
+
+void CefWindowImpl::Show() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_)
+    widget_->Show();
+}
+
+void CefWindowImpl::Hide() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_)
+    widget_->Hide();
+}
+
+void CefWindowImpl::CenterWindow(const CefSize& size) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_)
+    widget_->CenterWindow(gfx::Size(size.width, size.height));
+}
+
+void CefWindowImpl::Close() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && !widget_->IsClosed())
+    widget_->Close();
+}
+
+bool CefWindowImpl::IsClosed() {
+  CEF_REQUIRE_UIT_RETURN(false);
+  return destroyed_ || (widget_ && widget_->IsClosed());
+}
+
+void CefWindowImpl::Activate() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && widget_->CanActivate() && !widget_->IsActive())
+    widget_->Activate();
+}
+
+void CefWindowImpl::Deactivate() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && widget_->CanActivate() && widget_->IsActive())
+    widget_->Deactivate();
+}
+
+bool CefWindowImpl::IsActive() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (widget_)
+    return widget_->IsActive();
+  return false;
+}
+
+void CefWindowImpl::BringToTop() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_)
+    widget_->StackAtTop();
+}
+
+void CefWindowImpl::SetAlwaysOnTop(bool on_top) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && on_top != (widget_->GetZOrderLevel() ==
+                            ui::ZOrderLevel::kFloatingWindow)) {
+    widget_->SetZOrderLevel(on_top ? ui::ZOrderLevel::kFloatingWindow
+                                   : ui::ZOrderLevel::kNormal);
+  }
+}
+
+bool CefWindowImpl::IsAlwaysOnTop() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (widget_)
+    return widget_->GetZOrderLevel() == ui::ZOrderLevel::kFloatingWindow;
+  return false;
+}
+
+void CefWindowImpl::Maximize() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && !widget_->IsMaximized())
+    widget_->Maximize();
+}
+
+void CefWindowImpl::Minimize() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && !widget_->IsMinimized())
+    widget_->Minimize();
+}
+
+void CefWindowImpl::Restore() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && (widget_->IsMaximized() || widget_->IsMinimized()))
+    widget_->Restore();
+}
+
+void CefWindowImpl::SetFullscreen(bool fullscreen) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_ && fullscreen != widget_->IsFullscreen())
+    widget_->SetFullscreen(fullscreen);
+}
+
+bool CefWindowImpl::IsMaximized() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (widget_)
+    return widget_->IsMaximized();
+  return false;
+}
+
+bool CefWindowImpl::IsMinimized() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (widget_)
+    return widget_->IsMinimized();
+  return false;
+}
+
+bool CefWindowImpl::IsFullscreen() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (widget_)
+    return widget_->IsFullscreen();
+  return false;
+}
+
+void CefWindowImpl::SetTitle(const CefString& title) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (root_view())
+    root_view()->SetTitle(title);
+}
+
+CefString CefWindowImpl::GetTitle() {
+  CEF_REQUIRE_VALID_RETURN(CefString());
+  if (root_view())
+    return root_view()->title();
+  return CefString();
+}
+
+void CefWindowImpl::SetWindowIcon(CefRefPtr<CefImage> image) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (root_view())
+    root_view()->SetWindowIcon(image);
+}
+
+CefRefPtr<CefImage> CefWindowImpl::GetWindowIcon() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  if (root_view())
+    return root_view()->window_icon();
+  return nullptr;
+}
+
+void CefWindowImpl::SetWindowAppIcon(CefRefPtr<CefImage> image) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (root_view())
+    root_view()->SetWindowAppIcon(image);
+}
+
+CefRefPtr<CefImage> CefWindowImpl::GetWindowAppIcon() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  if (root_view())
+    return root_view()->window_app_icon();
+  return nullptr;
+}
+
+void CefWindowImpl::GetDebugInfo(base::DictionaryValue* info,
+                                 bool include_children) {
+  ParentClass::GetDebugInfo(info, include_children);
+  if (root_view())
+    info->SetString("title", root_view()->title());
+}
+
+void CefWindowImpl::ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                             const CefPoint& screen_point,
+                             cef_menu_anchor_position_t anchor_position) {
+  ShowMenu(nullptr, menu_model, screen_point, anchor_position);
+}
+
+void CefWindowImpl::Detach() {
+  // OnDeleteDelegate should always be called before Detach().
+  DCHECK(!widget_);
+
+  ParentClass::Detach();
+}
+
+void CefWindowImpl::SetBounds(const CefRect& bounds) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_) {
+    widget_->SetBounds(
+        gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
+  }
+}
+
+CefRect CefWindowImpl::GetBounds() {
+  CEF_REQUIRE_VALID_RETURN(CefRect());
+  gfx::Rect bounds;
+  if (widget_)
+    bounds = widget_->GetWindowBoundsInScreen();
+  return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+}
+
+CefRect CefWindowImpl::GetBoundsInScreen() {
+  return GetBounds();
+}
+
+void CefWindowImpl::SetSize(const CefSize& size) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_)
+    widget_->SetSize(gfx::Size(size.width, size.height));
+}
+
+void CefWindowImpl::SetPosition(const CefPoint& position) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_) {
+    gfx::Rect bounds = widget_->GetWindowBoundsInScreen();
+    bounds.set_origin(gfx::Point(position.x, position.y));
+    widget_->SetBounds(bounds);
+  }
+}
+
+void CefWindowImpl::SizeToPreferredSize() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (widget_) {
+    if (widget_->non_client_view())
+      widget_->SetSize(widget_->non_client_view()->GetPreferredSize());
+    else
+      widget_->SetSize(root_view()->GetPreferredSize());
+  }
+}
+
+void CefWindowImpl::SetVisible(bool visible) {
+  if (visible)
+    Show();
+  else
+    Hide();
+}
+
+bool CefWindowImpl::IsVisible() {
+  CEF_REQUIRE_VALID_RETURN(false);
+  if (widget_)
+    return widget_->IsVisible();
+  return false;
+}
+
+bool CefWindowImpl::IsDrawn() {
+  return IsVisible();
+}
+
+void CefWindowImpl::SetBackgroundColor(cef_color_t color) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  ParentClass::SetBackgroundColor(color);
+  if (widget_ && widget_->GetCompositor())
+    widget_->GetCompositor()->SetBackgroundColor(color);
+}
+
+bool CefWindowImpl::CanWidgetClose() {
+  if (delegate())
+    return delegate()->CanClose(this);
+  return true;
+}
+
+void CefWindowImpl::OnWindowClosing() {
+#if defined(USE_AURA)
+  unhandled_key_event_handler_.reset();
+#endif
+}
+
+void CefWindowImpl::OnWindowViewDeleted() {
+  CancelMenu();
+
+  destroyed_ = true;
+  widget_ = nullptr;
+
+  if (delegate())
+    delegate()->OnWindowDestroyed(this);
+
+  // Call Detach() here instead of waiting for the root View to be deleted so
+  // that any following attempts to call CefWindow methods from the delegate
+  // will fail.
+  Detach();
+}
+
+// Will only be called if CanHandleAccelerators() returns true.
+bool CefWindowImpl::AcceleratorPressed(const ui::Accelerator& accelerator) {
+  for (const auto& entry : accelerator_map_) {
+    if (entry.second == accelerator)
+      return delegate()->OnAccelerator(this, entry.first);
+  }
+  return false;
+}
+
+bool CefWindowImpl::CanHandleAccelerators() const {
+  if (delegate() && widget_)
+    return widget_->IsActive();
+  return false;
+}
+
+bool CefWindowImpl::OnKeyEvent(const CefKeyEvent& event) {
+  if (delegate())
+    return delegate()->OnKeyEvent(this, event);
+  return false;
+}
+
+void CefWindowImpl::ShowMenu(views::MenuButton* menu_button,
+                             CefRefPtr<CefMenuModel> menu_model,
+                             const CefPoint& screen_point,
+                             cef_menu_anchor_position_t anchor_position) {
+  CancelMenu();
+
+  if (!widget_)
+    return;
+
+  CefMenuModelImpl* menu_model_impl =
+      static_cast<CefMenuModelImpl*>(menu_model.get());
+  if (!menu_model_impl || !menu_model_impl->model())
+    return;
+
+  menu_model_ = menu_model_impl;
+
+  // We'll send the MenuClosed notification manually for better accuracy.
+  menu_model_->set_auto_notify_menu_closed(false);
+
+  menu_runner_.reset(
+      new views::MenuRunner(menu_model_impl->model(),
+                            menu_button ? views::MenuRunner::HAS_MNEMONICS
+                                        : views::MenuRunner::CONTEXT_MENU,
+                            base::Bind(&CefWindowImpl::MenuClosed, this)));
+
+  menu_runner_->RunMenuAt(
+      widget_, menu_button ? menu_button->button_controller() : nullptr,
+      gfx::Rect(gfx::Point(screen_point.x, screen_point.y), gfx::Size()),
+      static_cast<views::MenuAnchorPosition>(anchor_position),
+      ui::MENU_SOURCE_NONE);
+}
+
+void CefWindowImpl::MenuClosed() {
+  menu_model_->NotifyMenuClosed();
+  menu_model_ = nullptr;
+  menu_runner_.reset(nullptr);
+}
+
+void CefWindowImpl::CancelMenu() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (menu_runner_)
+    menu_runner_->Cancel();
+  DCHECK(!menu_model_);
+  DCHECK(!menu_runner_);
+}
+
+CefRefPtr<CefDisplay> CefWindowImpl::GetDisplay() {
+  CEF_REQUIRE_VALID_RETURN(nullptr);
+  if (widget_ && root_view()) {
+    const display::Display& display = root_view()->GetDisplay();
+    if (display.is_valid())
+      return new CefDisplayImpl(display);
+  }
+  return nullptr;
+}
+
+CefRect CefWindowImpl::GetClientAreaBoundsInScreen() {
+  CEF_REQUIRE_VALID_RETURN(CefRect());
+  if (widget_) {
+    gfx::Rect bounds = widget_->GetClientAreaBoundsInScreen();
+
+    views::NonClientFrameView* non_client_frame_view =
+        root_view()->GetNonClientFrameView();
+    if (non_client_frame_view) {
+      // When using a custom drawn NonClientFrameView the native Window will not
+      // know the actual client bounds. Adjust the native Window bounds for the
+      // reported client bounds.
+      const gfx::Rect& client_bounds =
+          non_client_frame_view->GetBoundsForClientView();
+      bounds.set_origin(bounds.origin() + client_bounds.OffsetFromOrigin());
+      bounds.set_size(client_bounds.size());
+    }
+
+    return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+  }
+  return CefRect();
+}
+
+void CefWindowImpl::SetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (root_view())
+    root_view()->SetDraggableRegions(regions);
+}
+
+CefWindowHandle CefWindowImpl::GetWindowHandle() {
+  CEF_REQUIRE_VALID_RETURN(kNullWindowHandle);
+  return view_util::GetWindowHandle(widget_);
+}
+
+void CefWindowImpl::SendKeyPress(int key_code, uint32 event_flags) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  InitializeUITesting();
+
+  gfx::NativeWindow native_window = view_util::GetNativeWindow(widget_);
+  if (!native_window)
+    return;
+
+  ui_controls::SendKeyPress(native_window,
+                            static_cast<ui::KeyboardCode>(key_code),
+                            !!(event_flags & EVENTFLAG_CONTROL_DOWN),
+                            !!(event_flags & EVENTFLAG_SHIFT_DOWN),
+                            !!(event_flags & EVENTFLAG_ALT_DOWN),
+                            false);  // Command key is not supported by Aura.
+}
+
+void CefWindowImpl::SendMouseMove(int screen_x, int screen_y) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  InitializeUITesting();
+
+  gfx::Point point(screen_x, screen_y);
+#if defined(OS_WIN)
+  // Windows expects pixel coordinates.
+  point = display::win::ScreenWin::DIPToScreenPoint(point);
+#endif
+
+  ui_controls::SendMouseMove(point.x(), point.y());
+}
+
+void CefWindowImpl::SendMouseEvents(cef_mouse_button_type_t button,
+                                    bool mouse_down,
+                                    bool mouse_up) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (!mouse_down && !mouse_up)
+    return;
+
+  InitializeUITesting();
+
+  ui_controls::MouseButton type = ui_controls::LEFT;
+  if (button == MBT_MIDDLE)
+    type = ui_controls::MIDDLE;
+  else if (button == MBT_RIGHT)
+    type = ui_controls::RIGHT;
+
+  int state = 0;
+  if (mouse_down)
+    state |= ui_controls::DOWN;
+  if (mouse_up)
+    state |= ui_controls::UP;
+
+  ui_controls::SendMouseEvents(type, state);
+}
+
+void CefWindowImpl::SetAccelerator(int command_id,
+                                   int key_code,
+                                   bool shift_pressed,
+                                   bool ctrl_pressed,
+                                   bool alt_pressed) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (!widget_)
+    return;
+
+  AcceleratorMap::const_iterator it = accelerator_map_.find(command_id);
+  if (it != accelerator_map_.end())
+    RemoveAccelerator(command_id);
+
+  int modifiers = 0;
+  if (shift_pressed)
+    modifiers |= ui::EF_SHIFT_DOWN;
+  if (ctrl_pressed)
+    modifiers |= ui::EF_CONTROL_DOWN;
+  if (alt_pressed)
+    modifiers |= ui::EF_ALT_DOWN;
+  ui::Accelerator accelerator(static_cast<ui::KeyboardCode>(key_code),
+                              modifiers);
+
+  accelerator_map_.insert(std::make_pair(command_id, accelerator));
+
+  views::FocusManager* focus_manager = widget_->GetFocusManager();
+  DCHECK(focus_manager);
+  focus_manager->RegisterAccelerator(
+      accelerator, ui::AcceleratorManager::kNormalPriority, this);
+}
+
+void CefWindowImpl::RemoveAccelerator(int command_id) {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (!widget_)
+    return;
+
+  AcceleratorMap::iterator it = accelerator_map_.find(command_id);
+  if (it == accelerator_map_.end())
+    return;
+
+  ui::Accelerator accelerator = it->second;
+
+  accelerator_map_.erase(it);
+
+  views::FocusManager* focus_manager = widget_->GetFocusManager();
+  DCHECK(focus_manager);
+  focus_manager->UnregisterAccelerator(accelerator, this);
+}
+
+void CefWindowImpl::RemoveAllAccelerators() {
+  CEF_REQUIRE_VALID_RETURN_VOID();
+  if (!widget_)
+    return;
+
+  accelerator_map_.clear();
+
+  views::FocusManager* focus_manager = widget_->GetFocusManager();
+  DCHECK(focus_manager);
+  focus_manager->UnregisterAccelerators(this);
+}
+
+CefWindowImpl::CefWindowImpl(CefRefPtr<CefWindowDelegate> delegate)
+    : ParentClass(delegate), widget_(nullptr), destroyed_(false) {}
+
+CefWindowView* CefWindowImpl::CreateRootView() {
+  return new CefWindowView(delegate(), this);
+}
+
+void CefWindowImpl::InitializeRootView() {
+  static_cast<CefWindowView*>(root_view())->Initialize();
+}
+
+void CefWindowImpl::CreateWidget() {
+  DCHECK(!widget_);
+
+  root_view()->CreateWidget();
+  widget_ = root_view()->GetWidget();
+  DCHECK(widget_);
+
+#if defined(USE_AURA)
+  unhandled_key_event_handler_ =
+      std::make_unique<CefUnhandledKeyEventHandler>(this, widget_);
+#endif
+
+  // The Widget and root View are owned by the native window. Therefore don't
+  // keep an owned reference.
+  std::unique_ptr<views::View> view_ptr = view_util::PassOwnership(this);
+  views::View* view = view_ptr.release();
+  ALLOW_UNUSED_LOCAL(view);
+}
diff --git a/src/libcef/browser/views/window_impl.h b/src/libcef/browser/views/window_impl.h
new file mode 100644
index 0000000..857b830
--- /dev/null
+++ b/src/libcef/browser/views/window_impl.h
@@ -0,0 +1,162 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_WINDOW_IMPL_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_WINDOW_IMPL_H_
+#pragma once
+
+#include <map>
+
+#include "include/views/cef_window.h"
+#include "include/views/cef_window_delegate.h"
+
+#include "libcef/browser/menu_model_impl.h"
+#include "libcef/browser/views/panel_impl.h"
+#include "libcef/browser/views/window_view.h"
+
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+class MenuButton;
+}
+
+class CefWindowImpl
+    : public CefPanelImpl<CefWindowView, CefWindow, CefWindowDelegate>,
+      public CefWindowView::Delegate,
+      public ui::AcceleratorTarget {
+ public:
+  typedef CefPanelImpl<CefWindowView, CefWindow, CefWindowDelegate> ParentClass;
+
+  // Create a new CefWindow instance. |delegate| may be nullptr.
+  static CefRefPtr<CefWindowImpl> Create(CefRefPtr<CefWindowDelegate> delegate);
+
+  // CefWindow methods:
+  void Show() override;
+  void Hide() override;
+  void CenterWindow(const CefSize& size) override;
+  void Close() override;
+  bool IsClosed() override;
+  void Activate() override;
+  void Deactivate() override;
+  bool IsActive() override;
+  void BringToTop() override;
+  void SetAlwaysOnTop(bool on_top) override;
+  bool IsAlwaysOnTop() override;
+  void Maximize() override;
+  void Minimize() override;
+  void Restore() override;
+  void SetFullscreen(bool fullscreen) override;
+  bool IsMaximized() override;
+  bool IsMinimized() override;
+  bool IsFullscreen() override;
+  void SetTitle(const CefString& title) override;
+  CefString GetTitle() override;
+  void SetWindowIcon(CefRefPtr<CefImage> image) override;
+  CefRefPtr<CefImage> GetWindowIcon() override;
+  void SetWindowAppIcon(CefRefPtr<CefImage> image) override;
+  CefRefPtr<CefImage> GetWindowAppIcon() override;
+  void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                const CefPoint& screen_point,
+                cef_menu_anchor_position_t anchor_position) override;
+  void CancelMenu() override;
+  CefRefPtr<CefDisplay> GetDisplay() override;
+  CefRect GetClientAreaBoundsInScreen() override;
+  void SetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) override;
+  CefWindowHandle GetWindowHandle() override;
+  void SendKeyPress(int key_code, uint32 event_flags) override;
+  void SendMouseMove(int screen_x, int screen_y) override;
+  void SendMouseEvents(cef_mouse_button_type_t button,
+                       bool mouse_down,
+                       bool mouse_up) override;
+  void SetAccelerator(int command_id,
+                      int key_code,
+                      bool shift_pressed,
+                      bool ctrl_pressed,
+                      bool alt_pressed) override;
+  void RemoveAccelerator(int command_id) override;
+  void RemoveAllAccelerators() override;
+
+  // CefViewAdapter methods:
+  void Detach() override;
+
+  // CefPanel methods:
+  CefRefPtr<CefWindow> AsWindow() override { return this; }
+
+  // CefView methods:
+  void SetBounds(const CefRect& bounds) override;
+  CefRect GetBounds() override;
+  CefRect GetBoundsInScreen() override;
+  void SetSize(const CefSize& bounds) override;
+  void SetPosition(const CefPoint& position) override;
+  void SizeToPreferredSize() override;
+  void SetVisible(bool visible) override;
+  bool IsVisible() override;
+  bool IsDrawn() override;
+  void SetBackgroundColor(cef_color_t color) override;
+
+  // CefWindowView::Delegate methods:
+  bool CanWidgetClose() override;
+  void OnWindowClosing() override;
+  void OnWindowViewDeleted() override;
+
+  // CefViewAdapter methods:
+  std::string GetDebugType() override { return "Window"; }
+  void GetDebugInfo(base::DictionaryValue* info,
+                    bool include_children) override;
+
+  // ui::AcceleratorTarget methods:
+  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+  bool CanHandleAccelerators() const override;
+
+  // Called for key events that have not been handled by other controls in the
+  // window. Returns true if the event was handled.
+  bool OnKeyEvent(const CefKeyEvent& event);
+
+  void ShowMenu(views::MenuButton* menu_button,
+                CefRefPtr<CefMenuModel> menu_model,
+                const CefPoint& screen_point,
+                cef_menu_anchor_position_t anchor_position);
+  void MenuClosed();
+
+  views::Widget* widget() const { return widget_; }
+
+ private:
+  // Create a new implementation object.
+  // Always call Initialize() after creation.
+  // |delegate| may be nullptr.
+  explicit CefWindowImpl(CefRefPtr<CefWindowDelegate> delegate);
+
+  // CefViewImpl methods:
+  CefWindowView* CreateRootView() override;
+  void InitializeRootView() override;
+
+  // Initialize the Widget.
+  void CreateWidget();
+
+  views::Widget* widget_;
+
+  // True if the window has been destroyed.
+  bool destroyed_;
+
+  // The currently active menu model and runner.
+  CefRefPtr<CefMenuModelImpl> menu_model_;
+  std::unique_ptr<views::MenuRunner> menu_runner_;
+
+  // Map of command_id to accelerator.
+  typedef std::map<int, ui::Accelerator> AcceleratorMap;
+  AcceleratorMap accelerator_map_;
+
+#if defined(USE_AURA)
+  // Native widget's handler to receive events after the event target.
+  std::unique_ptr<ui::EventHandler> unhandled_key_event_handler_;
+#endif
+
+  IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefWindowImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefWindowImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_WINDOW_IMPL_H_
diff --git a/src/libcef/browser/views/window_view.cc b/src/libcef/browser/views/window_view.cc
new file mode 100644
index 0000000..de8cc3a
--- /dev/null
+++ b/src/libcef/browser/views/window_view.cc
@@ -0,0 +1,538 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/browser/views/window_view.h"
+
+#include "libcef/browser/image_impl.h"
+#include "libcef/browser/views/window_impl.h"
+
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/aura/window.h"
+#include "ui/base/hit_test.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/native_frame_view.h"
+
+#if defined(OS_LINUX) && defined(USE_X11)
+#include <X11/Xlib.h>
+#include "ui/gfx/x/x11_types.h"
+#endif
+
+#if defined(OS_WIN)
+#include "ui/display/screen.h"
+#include "ui/views/win/hwnd_util.h"
+#endif
+
+namespace {
+
+// Specialize ClientView to handle Widget-related events.
+class ClientViewEx : public views::ClientView {
+ public:
+  ClientViewEx(views::Widget* widget,
+               views::View* contents_view,
+               CefWindowView::Delegate* window_delegate)
+      : views::ClientView(widget, contents_view),
+        window_delegate_(window_delegate) {
+    DCHECK(window_delegate_);
+  }
+
+  bool CanClose() override { return window_delegate_->CanWidgetClose(); }
+
+ private:
+  CefWindowView::Delegate* window_delegate_;  // Not owned by this object.
+
+  DISALLOW_COPY_AND_ASSIGN(ClientViewEx);
+};
+
+// Extend NativeFrameView with draggable region handling.
+class NativeFrameViewEx : public views::NativeFrameView {
+ public:
+  NativeFrameViewEx(views::Widget* widget, CefWindowView* view)
+      : views::NativeFrameView(widget), widget_(widget), view_(view) {}
+
+  gfx::Rect GetWindowBoundsForClientBounds(
+      const gfx::Rect& client_bounds) const override {
+#if defined(OS_WIN)
+    // views::GetWindowBoundsForClientBounds() expects the input Rect to be in
+    // pixel coordinates. NativeFrameView does not implement this correctly so
+    // we need to provide our own implementation. See http://crbug.com/602692.
+    gfx::Rect pixel_bounds =
+        display::Screen::GetScreen()->DIPToScreenRectInWindow(
+            view_util::GetNativeWindow(widget_), client_bounds);
+    pixel_bounds = views::GetWindowBoundsForClientBounds(
+        static_cast<View*>(const_cast<NativeFrameViewEx*>(this)), pixel_bounds);
+    return display::Screen::GetScreen()->ScreenToDIPRectInWindow(
+        view_util::GetNativeWindow(widget_), pixel_bounds);
+#else
+    // Use the default implementation.
+    return views::NativeFrameView::GetWindowBoundsForClientBounds(
+        client_bounds);
+#endif
+  }
+
+  int NonClientHitTest(const gfx::Point& point) override {
+    if (widget_->IsFullscreen())
+      return HTCLIENT;
+
+    // Test for mouse clicks that fall within the draggable region.
+    SkRegion* draggable_region = view_->draggable_region();
+    if (draggable_region && draggable_region->contains(point.x(), point.y()))
+      return HTCAPTION;
+
+    return views::NativeFrameView::NonClientHitTest(point);
+  }
+
+ private:
+  // Not owned by this object.
+  views::Widget* widget_;
+  CefWindowView* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeFrameViewEx);
+};
+
+// The area inside the frame border that can be clicked and dragged for resizing
+// the window. Only used in restored mode.
+const int kResizeBorderThickness = 4;
+
+// The distance from each window corner that triggers diagonal resizing. Only
+// used in restored mode.
+const int kResizeAreaCornerSize = 16;
+
+// Implement NonClientFrameView without the system default caption and icon but
+// with a resizable border. Based on AppWindowFrameView and CustomFrameView.
+class CaptionlessFrameView : public views::NonClientFrameView {
+ public:
+  CaptionlessFrameView(views::Widget* widget, CefWindowView* view)
+      : widget_(widget), view_(view) {}
+
+  gfx::Rect GetBoundsForClientView() const override {
+    return client_view_bounds_;
+  }
+
+  gfx::Rect GetWindowBoundsForClientBounds(
+      const gfx::Rect& client_bounds) const override {
+    return client_bounds;
+  }
+
+  int NonClientHitTest(const gfx::Point& point) override {
+    if (widget_->IsFullscreen())
+      return HTCLIENT;
+
+    // Sanity check.
+    if (!bounds().Contains(point))
+      return HTNOWHERE;
+
+    // Check the frame first, as we allow a small area overlapping the contents
+    // to be used for resize handles.
+    bool can_ever_resize = widget_->widget_delegate()
+                               ? widget_->widget_delegate()->CanResize()
+                               : false;
+    // Don't allow overlapping resize handles when the window is maximized or
+    // fullscreen, as it can't be resized in those states.
+    int resize_border_thickness = ResizeBorderThickness();
+    int frame_component = GetHTComponentForFrame(
+        point, resize_border_thickness, resize_border_thickness,
+        kResizeAreaCornerSize, kResizeAreaCornerSize, can_ever_resize);
+    if (frame_component != HTNOWHERE)
+      return frame_component;
+
+    // Test for mouse clicks that fall within the draggable region.
+    SkRegion* draggable_region = view_->draggable_region();
+    if (draggable_region && draggable_region->contains(point.x(), point.y()))
+      return HTCAPTION;
+
+    int client_component = widget_->client_view()->NonClientHitTest(point);
+    if (client_component != HTNOWHERE)
+      return client_component;
+
+    // Caption is a safe default.
+    return HTCAPTION;
+  }
+
+  void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override {
+    // Nothing to do here.
+  }
+
+  void ResetWindowControls() override {
+    // Nothing to do here.
+  }
+
+  void UpdateWindowIcon() override {
+    // Nothing to do here.
+  }
+
+  void UpdateWindowTitle() override {
+    // Nothing to do here.
+  }
+
+  void SizeConstraintsChanged() override {
+    // Nothing to do here.
+  }
+
+  void OnPaint(gfx::Canvas* canvas) override {
+    // Nothing to do here.
+  }
+
+  void Layout() override {
+    client_view_bounds_.SetRect(0, 0, width(), height());
+  }
+
+  gfx::Size CalculatePreferredSize() const override {
+    return widget_->non_client_view()
+        ->GetWindowBoundsForClientBounds(
+            gfx::Rect(widget_->client_view()->GetPreferredSize()))
+        .size();
+  }
+
+  gfx::Size GetMinimumSize() const override {
+    return widget_->non_client_view()
+        ->GetWindowBoundsForClientBounds(
+            gfx::Rect(widget_->client_view()->GetMinimumSize()))
+        .size();
+  }
+
+  gfx::Size GetMaximumSize() const override {
+    gfx::Size max_size = widget_->client_view()->GetMaximumSize();
+    gfx::Size converted_size =
+        widget_->non_client_view()
+            ->GetWindowBoundsForClientBounds(gfx::Rect(max_size))
+            .size();
+    return gfx::Size(max_size.width() == 0 ? 0 : converted_size.width(),
+                     max_size.height() == 0 ? 0 : converted_size.height());
+  }
+
+ private:
+  int ResizeBorderThickness() const {
+    return (widget_->IsMaximized() || widget_->IsFullscreen()
+                ? 0
+                : kResizeBorderThickness);
+  }
+
+  // Not owned by this object.
+  views::Widget* widget_;
+  CefWindowView* view_;
+
+  // The bounds of the client view, in this view's coordinates.
+  gfx::Rect client_view_bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(CaptionlessFrameView);
+};
+
+bool IsWindowBorderHit(int code) {
+// On Windows HTLEFT = 10 and HTBORDER = 18. Values are not ordered the same
+// in base/hit_test.h for non-Windows platforms.
+#if defined(OS_WIN)
+  return code >= HTLEFT && code <= HTBORDER;
+#else
+  return code == HTLEFT || code == HTRIGHT || code == HTTOP ||
+         code == HTTOPLEFT || code == HTTOPRIGHT || code == HTBOTTOM ||
+         code == HTBOTTOMLEFT || code == HTBOTTOMRIGHT || code == HTBORDER;
+#endif
+}
+
+}  // namespace
+
+CefWindowView::CefWindowView(CefWindowDelegate* cef_delegate,
+                             Delegate* window_delegate)
+    : ParentClass(cef_delegate),
+      window_delegate_(window_delegate),
+      is_frameless_(false) {
+  DCHECK(window_delegate_);
+}
+
+void CefWindowView::CreateWidget() {
+  DCHECK(!GetWidget());
+
+  // |widget| is owned by the NativeWidget and will be destroyed in response to
+  // a native destruction message.
+  views::Widget* widget = new views::Widget;
+
+  views::Widget::InitParams params;
+  params.delegate = this;
+  params.type = views::Widget::InitParams::TYPE_WINDOW;
+  params.bounds = gfx::Rect(CalculatePreferredSize());
+  bool can_activate = true;
+
+  if (cef_delegate()) {
+    CefRefPtr<CefWindow> cef_window = GetCefWindow();
+    is_frameless_ = cef_delegate()->IsFrameless(cef_window);
+
+    bool is_menu = false;
+    bool can_activate_menu = true;
+    CefRefPtr<CefWindow> parent_window = cef_delegate()->GetParentWindow(
+        cef_window, &is_menu, &can_activate_menu);
+    if (parent_window && !parent_window->IsSame(cef_window)) {
+      CefWindowImpl* parent_window_impl =
+          static_cast<CefWindowImpl*>(parent_window.get());
+      params.parent = view_util::GetNativeWindow(parent_window_impl->widget());
+      if (is_menu) {
+        // Don't clip the window to parent bounds.
+        params.type = views::Widget::InitParams::TYPE_MENU;
+
+        // Don't set "always on top" for the window.
+        params.z_order = ui::ZOrderLevel::kNormal;
+
+        can_activate = can_activate_menu;
+        if (can_activate_menu)
+          params.activatable = views::Widget::InitParams::ACTIVATABLE_YES;
+      }
+    }
+  }
+
+#if defined(OS_WIN)
+  if (is_frameless_) {
+    // Don't show the native window caption. Setting this value on Linux will
+    // result in window resize artifacts.
+    params.remove_standard_frame = true;
+  }
+#endif
+
+  widget->Init(std::move(params));
+
+  // |widget| should now be associated with |this|.
+  DCHECK_EQ(widget, GetWidget());
+  // |widget| must be top-level for focus handling to work correctly.
+  DCHECK(widget->is_top_level());
+
+  if (can_activate) {
+    // |widget| must be activatable for focus handling to work correctly.
+    DCHECK(widget->widget_delegate()->CanActivate());
+  }
+
+#if defined(OS_LINUX) && defined(USE_X11)
+  if (is_frameless_) {
+    ::Window window = view_util::GetWindowHandle(widget);
+    DCHECK(window);
+    ::Display* display = gfx::GetXDisplay();
+    DCHECK(display);
+
+    // Make the window borderless. From
+    // http://stackoverflow.com/questions/1904445/borderless-windows-on-linux
+    struct MwmHints {
+      unsigned long flags;
+      unsigned long functions;
+      unsigned long decorations;
+      long input_mode;
+      unsigned long status;
+    };
+    enum {
+      MWM_HINTS_FUNCTIONS = (1L << 0),
+      MWM_HINTS_DECORATIONS = (1L << 1),
+
+      MWM_FUNC_ALL = (1L << 0),
+      MWM_FUNC_RESIZE = (1L << 1),
+      MWM_FUNC_MOVE = (1L << 2),
+      MWM_FUNC_MINIMIZE = (1L << 3),
+      MWM_FUNC_MAXIMIZE = (1L << 4),
+      MWM_FUNC_CLOSE = (1L << 5)
+    };
+
+    Atom mwmHintsProperty = XInternAtom(display, "_MOTIF_WM_HINTS", 0);
+    struct MwmHints hints = {};
+    hints.flags = MWM_HINTS_DECORATIONS;
+    hints.decorations = 0;
+    XChangeProperty(display, window, mwmHintsProperty, mwmHintsProperty, 32,
+                    PropModeReplace, (unsigned char*)&hints, 5);
+  }
+#endif  // defined(OS_LINUX) && defined(USE_X11)
+}
+
+CefRefPtr<CefWindow> CefWindowView::GetCefWindow() const {
+  CefRefPtr<CefWindow> window = GetCefPanel()->AsWindow();
+  DCHECK(window);
+  return window;
+}
+
+void CefWindowView::DeleteDelegate() {
+  // Remove all child Views before deleting the Window so that notifications
+  // resolve correctly.
+  RemoveAllChildViews(true);
+
+  window_delegate_->OnWindowViewDeleted();
+
+  // Deletes |this|.
+  views::WidgetDelegateView::DeleteDelegate();
+}
+
+bool CefWindowView::CanResize() const {
+  if (!cef_delegate())
+    return true;
+  return cef_delegate()->CanResize(GetCefWindow());
+}
+
+bool CefWindowView::CanMinimize() const {
+  if (!cef_delegate())
+    return true;
+  return cef_delegate()->CanMinimize(GetCefWindow());
+}
+
+bool CefWindowView::CanMaximize() const {
+  if (!cef_delegate())
+    return true;
+  return cef_delegate()->CanMaximize(GetCefWindow());
+}
+
+base::string16 CefWindowView::GetWindowTitle() const {
+  return title_;
+}
+
+gfx::ImageSkia CefWindowView::GetWindowIcon() {
+  if (!window_icon_)
+    return ParentClass::GetWindowIcon();
+  return static_cast<CefImageImpl*>(window_icon_.get())
+      ->GetForced1xScaleRepresentation(GetDisplay().device_scale_factor());
+}
+
+gfx::ImageSkia CefWindowView::GetWindowAppIcon() {
+  if (!window_app_icon_)
+    return ParentClass::GetWindowAppIcon();
+  return static_cast<CefImageImpl*>(window_app_icon_.get())
+      ->GetForced1xScaleRepresentation(GetDisplay().device_scale_factor());
+}
+
+void CefWindowView::WindowClosing() {
+  window_delegate_->OnWindowClosing();
+}
+
+views::View* CefWindowView::GetContentsView() {
+  // |this| will be the "Contents View" hosted by the Widget via ClientView and
+  // RootView.
+  return this;
+}
+
+views::ClientView* CefWindowView::CreateClientView(views::Widget* widget) {
+  return new ClientViewEx(widget, GetContentsView(), window_delegate_);
+}
+
+views::NonClientFrameView* CefWindowView::CreateNonClientFrameView(
+    views::Widget* widget) {
+  if (is_frameless_) {
+    // Custom frame type that doesn't render a caption.
+    return new CaptionlessFrameView(widget, this);
+  } else if (widget->ShouldUseNativeFrame()) {
+    // DesktopNativeWidgetAura::CreateNonClientFrameView() returns
+    // NativeFrameView by default. Extend that type.
+    return new NativeFrameViewEx(widget, this);
+  }
+
+  // Use Chromium provided CustomFrameView. In case if we would like to
+  // customize the frame, provide own implementation.
+  return nullptr;
+}
+
+bool CefWindowView::ShouldDescendIntoChildForEventHandling(
+    gfx::NativeView child,
+    const gfx::Point& location) {
+  if (is_frameless_) {
+    // If the window is resizable it should claim mouse events that fall on the
+    // window border.
+    views::NonClientFrameView* ncfv = GetNonClientFrameView();
+    if (ncfv) {
+      int result = ncfv->NonClientHitTest(location);
+      if (IsWindowBorderHit(result))
+        return false;
+    }
+  }
+
+  // The window should claim mouse events that fall within the draggable region.
+  return !draggable_region_.get() ||
+         !draggable_region_->contains(location.x(), location.y());
+}
+
+bool CefWindowView::MaybeGetMinimumSize(gfx::Size* size) const {
+#if defined(OS_LINUX)
+  // Resize is disabled on Linux by returning the preferred size as the min/max
+  // size.
+  if (!CanResize()) {
+    *size = CalculatePreferredSize();
+    return true;
+  }
+#endif
+  return false;
+}
+
+bool CefWindowView::MaybeGetMaximumSize(gfx::Size* size) const {
+#if defined(OS_LINUX)
+  // Resize is disabled on Linux by returning the preferred size as the min/max
+  // size.
+  if (!CanResize()) {
+    *size = CalculatePreferredSize();
+    return true;
+  }
+#endif
+  return false;
+}
+
+void CefWindowView::ViewHierarchyChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  if (details.child == this) {
+    // This View's parent types (RootView, ClientView) are not exposed via the
+    // CEF API. Therefore don't send notifications about this View's parent
+    // changes.
+    return;
+  }
+
+  ParentClass::ViewHierarchyChanged(details);
+}
+
+display::Display CefWindowView::GetDisplay() const {
+  const views::Widget* widget = GetWidget();
+  if (widget) {
+    return view_util::GetDisplayMatchingBounds(
+        widget->GetWindowBoundsInScreen(), false);
+  }
+  return display::Display();
+}
+
+void CefWindowView::SetTitle(const base::string16& title) {
+  title_ = title;
+  views::Widget* widget = GetWidget();
+  if (widget)
+    widget->UpdateWindowTitle();
+}
+
+void CefWindowView::SetWindowIcon(CefRefPtr<CefImage> window_icon) {
+  if (std::max(window_icon->GetWidth(), window_icon->GetHeight()) != 16U) {
+    DLOG(ERROR) << "Window icons must be 16 DIP in size.";
+    return;
+  }
+
+  window_icon_ = window_icon;
+  views::Widget* widget = GetWidget();
+  if (widget)
+    widget->UpdateWindowIcon();
+}
+
+void CefWindowView::SetWindowAppIcon(CefRefPtr<CefImage> window_app_icon) {
+  window_app_icon_ = window_app_icon;
+  views::Widget* widget = GetWidget();
+  if (widget)
+    widget->UpdateWindowIcon();
+}
+
+void CefWindowView::SetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  if (regions.empty()) {
+    if (draggable_region_)
+      draggable_region_.reset(nullptr);
+    return;
+  }
+
+  draggable_region_.reset(new SkRegion);
+  for (const CefDraggableRegion& region : regions) {
+    draggable_region_->op(
+        {region.bounds.x, region.bounds.y,
+         region.bounds.x + region.bounds.width,
+         region.bounds.y + region.bounds.height},
+        region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
+  }
+}
+
+views::NonClientFrameView* CefWindowView::GetNonClientFrameView() const {
+  const views::Widget* widget = GetWidget();
+  if (!widget)
+    return nullptr;
+  if (!widget->non_client_view())
+    return nullptr;
+  return widget->non_client_view()->frame_view();
+}
diff --git a/src/libcef/browser/views/window_view.h b/src/libcef/browser/views/window_view.h
new file mode 100644
index 0000000..50093e1
--- /dev/null
+++ b/src/libcef/browser/views/window_view.h
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_VIEWS_WINDOW_VIEW_H_
+#define CEF_LIBCEF_BROWSER_VIEWS_WINDOW_VIEW_H_
+#pragma once
+
+#include "include/views/cef_window.h"
+#include "include/views/cef_window_delegate.h"
+
+#include "libcef/browser/views/panel_view.h"
+
+#include "ui/display/display.h"
+#include "ui/views/widget/widget_delegate.h"
+
+class SkRegion;
+
+// Manages the views-based root window. This object will be deleted
+// automatically when the associated root window is destroyed.
+class CefWindowView
+    : public CefPanelView<views::WidgetDelegateView, CefWindowDelegate> {
+ public:
+  typedef CefPanelView<views::WidgetDelegateView, CefWindowDelegate>
+      ParentClass;
+
+  class Delegate {
+   public:
+    // Returns true to signal that the Widget can be closed.
+    virtual bool CanWidgetClose() = 0;
+
+    // Called when the underlying platform window is closing.
+    virtual void OnWindowClosing() = 0;
+
+    // Called when the WindowView is about to be deleted.
+    virtual void OnWindowViewDeleted() = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // |cef_delegate| may be nullptr.
+  // |window_delegate| must be non-nullptr.
+  CefWindowView(CefWindowDelegate* cef_delegate, Delegate* window_delegate);
+
+  // Create the Widget.
+  void CreateWidget();
+
+  // Returns the CefWindow associated with this view. See comments on
+  // CefViewView::GetCefView.
+  CefRefPtr<CefWindow> GetCefWindow() const;
+
+  // views::WidgetDelegateView methods:
+  void DeleteDelegate() override;
+
+  // views::WidgetDelegate methods:
+  bool CanResize() const override;
+  bool CanMinimize() const override;
+  bool CanMaximize() const override;
+  base::string16 GetWindowTitle() const override;
+  gfx::ImageSkia GetWindowIcon() override;
+  gfx::ImageSkia GetWindowAppIcon() override;
+  void WindowClosing() override;
+  views::View* GetContentsView() override;
+  views::ClientView* CreateClientView(views::Widget* widget) override;
+  views::NonClientFrameView* CreateNonClientFrameView(
+      views::Widget* widget) override;
+  bool ShouldDescendIntoChildForEventHandling(
+      gfx::NativeView child,
+      const gfx::Point& location) override;
+  bool MaybeGetMinimumSize(gfx::Size* size) const override;
+  bool MaybeGetMaximumSize(gfx::Size* size) const override;
+
+  // views::View methods:
+  void ViewHierarchyChanged(
+      const views::ViewHierarchyChangedDetails& details) override;
+
+  // Returns the Display containing this Window.
+  display::Display GetDisplay() const;
+
+  // Set/get the window title.
+  void SetTitle(const base::string16& title);
+  base::string16 title() const { return title_; }
+
+  // Set/get the window icon. This should be a 16x16 icon suitable for use in
+  // the Windows's title bar.
+  void SetWindowIcon(CefRefPtr<CefImage> window_icon);
+  CefRefPtr<CefImage> window_icon() const { return window_icon_; }
+
+  // Set/get the window app icon. This should be a larger icon for use in the
+  // host environment app switching UI. On Windows, this is the ICON_BIG used in
+  // Alt-Tab list and Windows taskbar. The Window icon will be used by default
+  // if no Window App icon is specified.
+  void SetWindowAppIcon(CefRefPtr<CefImage> window_app_icon);
+  CefRefPtr<CefImage> window_app_icon() const { return window_app_icon_; }
+
+  // Set/get the draggable regions.
+  void SetDraggableRegions(const std::vector<CefDraggableRegion>& regions);
+  SkRegion* draggable_region() const { return draggable_region_.get(); }
+
+  // Returns the NonClientFrameView for this Window. May be nullptr.
+  views::NonClientFrameView* GetNonClientFrameView() const;
+
+ private:
+  // Not owned by this object.
+  Delegate* window_delegate_;
+
+  // True if the window is frameless. It might still be resizable and draggable.
+  bool is_frameless_;
+
+  base::string16 title_;
+  CefRefPtr<CefImage> window_icon_;
+  CefRefPtr<CefImage> window_app_icon_;
+
+  std::unique_ptr<SkRegion> draggable_region_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefWindowView);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_VIEWS_WINDOW_VIEW_H_
diff --git a/src/libcef/browser/web_contents_dialog_helper.cc b/src/libcef/browser/web_contents_dialog_helper.cc
new file mode 100644
index 0000000..ffabe5d
--- /dev/null
+++ b/src/libcef/browser/web_contents_dialog_helper.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/web_contents_dialog_helper.h"
+
+#include "libcef/browser/browser_platform_delegate.h"
+
+#include "chrome/browser/platform_util.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+
+#if defined(USE_AURA)
+#include "ui/views/widget/widget.h"
+#endif
+
+CefWebContentsDialogHelper::CefWebContentsDialogHelper(
+    content::WebContents* web_contents,
+    CefBrowserPlatformDelegate* browser_delegate)
+    : browser_delegate_(browser_delegate), weak_factory_(this) {
+  web_modal::WebContentsModalDialogManager::CreateForWebContents(web_contents);
+  web_modal::WebContentsModalDialogManager::FromWebContents(web_contents)
+      ->SetDelegate(this);
+}
+
+base::RepeatingClosure CefWebContentsDialogHelper::GetBoundsChangedCallback() {
+  return base::BindRepeating(&CefWebContentsDialogHelper::OnBoundsChanged,
+                             weak_factory_.GetWeakPtr());
+}
+
+bool CefWebContentsDialogHelper::IsWebContentsVisible(
+    content::WebContents* web_contents) {
+  return platform_util::IsVisible(web_contents->GetNativeView());
+}
+
+web_modal::WebContentsModalDialogHost*
+CefWebContentsDialogHelper::GetWebContentsModalDialogHost() {
+  return this;
+}
+
+gfx::NativeView CefWebContentsDialogHelper::GetHostView() const {
+#if defined(USE_AURA)
+  return browser_delegate_->GetWindowWidget()->GetNativeView();
+#else
+  NOTIMPLEMENTED();
+  return gfx::NativeView();
+#endif
+}
+
+gfx::Point CefWebContentsDialogHelper::GetDialogPosition(
+    const gfx::Size& size) {
+  return browser_delegate_->GetDialogPosition(size);
+}
+
+gfx::Size CefWebContentsDialogHelper::GetMaximumDialogSize() {
+  return browser_delegate_->GetMaximumDialogSize();
+}
+
+void CefWebContentsDialogHelper::AddObserver(
+    web_modal::ModalDialogHostObserver* observer) {
+  if (observer && !observer_list_.HasObserver(observer))
+    observer_list_.AddObserver(observer);
+}
+
+void CefWebContentsDialogHelper::RemoveObserver(
+    web_modal::ModalDialogHostObserver* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void CefWebContentsDialogHelper::OnBoundsChanged() {
+  for (auto& observer : observer_list_)
+    observer.OnPositionRequiresUpdate();
+}
diff --git a/src/libcef/browser/web_contents_dialog_helper.h b/src/libcef/browser/web_contents_dialog_helper.h
new file mode 100644
index 0000000..39be126
--- /dev/null
+++ b/src/libcef/browser/web_contents_dialog_helper.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_WEB_CONTENTS_DIALOG_HELPER_H_
+#define CEF_LIBCEF_BROWSER_WEB_CONTENTS_DIALOG_HELPER_H_
+#pragma once
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "components/web_modal/modal_dialog_host.h"
+#include "components/web_modal/web_contents_modal_dialog_host.h"
+#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
+
+class CefBrowserPlatformDelegate;
+
+class CefWebContentsDialogHelper
+    : public web_modal::WebContentsModalDialogManagerDelegate,
+      public web_modal::WebContentsModalDialogHost {
+ public:
+  CefWebContentsDialogHelper(content::WebContents* web_contents,
+                             CefBrowserPlatformDelegate* browser_delegate);
+
+  base::RepeatingClosure GetBoundsChangedCallback();
+
+  // web_modal::WebContentsModalDialogManagerDelegate methods:
+  bool IsWebContentsVisible(content::WebContents* web_contents) override;
+  web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
+      override;
+
+  // web_modal::WebContentsModalDialogHost methods:
+  gfx::NativeView GetHostView() const override;
+  gfx::Point GetDialogPosition(const gfx::Size& size) override;
+  gfx::Size GetMaximumDialogSize() override;
+  void AddObserver(web_modal::ModalDialogHostObserver* observer) override;
+  void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override;
+
+ private:
+  void OnBoundsChanged();
+
+  CefBrowserPlatformDelegate* const browser_delegate_;
+
+  // Used to notify WebContentsModalDialog.
+  base::ObserverList<web_modal::ModalDialogHostObserver>::Unchecked
+      observer_list_;
+
+  base::WeakPtrFactory<CefWebContentsDialogHelper> weak_factory_;
+};
+
+#endif  // CEF_LIBCEF_BROWSER_WEB_CONTENTS_DIALOG_HELPER_H_
diff --git a/src/libcef/browser/web_plugin_impl.cc b/src/libcef/browser/web_plugin_impl.cc
new file mode 100644
index 0000000..d61449a
--- /dev/null
+++ b/src/libcef/browser/web_plugin_impl.cc
@@ -0,0 +1,188 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/web_plugin_impl.h"
+
+#include "libcef/browser/context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/widevine_loader.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "content/browser/plugin_service_impl.h"
+
+namespace {
+
+void PluginsCallbackImpl(
+    CefRefPtr<CefWebPluginInfoVisitor> visitor,
+    const std::vector<content::WebPluginInfo>& all_plugins) {
+  CEF_REQUIRE_UIT();
+
+  int count = 0;
+  int total = static_cast<int>(all_plugins.size());
+
+  std::vector<content::WebPluginInfo>::const_iterator it = all_plugins.begin();
+  for (; it != all_plugins.end(); ++it, ++count) {
+    CefRefPtr<CefWebPluginInfoImpl> info(new CefWebPluginInfoImpl(*it));
+    if (!visitor->Visit(info.get(), count, total))
+      break;
+  }
+}
+
+#if !(BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)) || \
+    defined(OS_LINUX)
+
+void DeliverWidevineCdmError(const std::string& error_message,
+                             CefRefPtr<CefRegisterCdmCallback> callback) {
+  LOG(ERROR) << error_message;
+  if (callback.get()) {
+    CEF_POST_TASK(
+        CEF_UIT,
+        base::Bind(&CefRegisterCdmCallback::OnCdmRegistrationComplete,
+                   callback.get(), CEF_CDM_REGISTRATION_ERROR_NOT_SUPPORTED,
+                   error_message));
+  }
+}
+
+#endif
+
+}  // namespace
+
+// CefWebPluginInfoImpl
+
+CefWebPluginInfoImpl::CefWebPluginInfoImpl(
+    const content::WebPluginInfo& plugin_info)
+    : plugin_info_(plugin_info) {}
+
+CefString CefWebPluginInfoImpl::GetName() {
+  return plugin_info_.name;
+}
+
+CefString CefWebPluginInfoImpl::GetPath() {
+  return plugin_info_.path.value();
+}
+
+CefString CefWebPluginInfoImpl::GetVersion() {
+  return plugin_info_.version;
+}
+
+CefString CefWebPluginInfoImpl::GetDescription() {
+  return plugin_info_.desc;
+}
+
+// Global functions.
+
+void CefVisitWebPluginInfo(CefRefPtr<CefWebPluginInfoVisitor> visitor) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  if (!visitor.get()) {
+    NOTREACHED() << "invalid parameter";
+    return;
+  }
+
+  if (CEF_CURRENTLY_ON_UIT()) {
+    content::PluginServiceImpl::GetInstance()->GetPlugins(
+        base::Bind(PluginsCallbackImpl, visitor));
+  } else {
+    // Execute on the UI thread.
+    CEF_POST_TASK(CEF_UIT, base::Bind(CefVisitWebPluginInfo, visitor));
+  }
+}
+
+void CefRefreshWebPlugins() {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  // No thread affinity.
+  content::PluginServiceImpl::GetInstance()->RefreshPlugins();
+}
+
+void CefUnregisterInternalWebPlugin(const CefString& path) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  if (path.empty()) {
+    NOTREACHED() << "invalid parameter";
+    return;
+  }
+
+  // No thread affinity.
+  content::PluginServiceImpl::GetInstance()->UnregisterInternalPlugin(
+      base::FilePath(path));
+}
+
+void CefRegisterWebPluginCrash(const CefString& path) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  if (path.empty()) {
+    NOTREACHED() << "invalid parameter";
+    return;
+  }
+
+  if (CEF_CURRENTLY_ON_IOT()) {
+    content::PluginServiceImpl::GetInstance()->RegisterPluginCrash(
+        base::FilePath(path));
+  } else {
+    // Execute on the IO thread.
+    CEF_POST_TASK(CEF_IOT, base::Bind(CefRegisterWebPluginCrash, path));
+  }
+}
+
+void CefIsWebPluginUnstable(const CefString& path,
+                            CefRefPtr<CefWebPluginUnstableCallback> callback) {
+  // Verify that the context is in a valid state.
+  if (!CONTEXT_STATE_VALID()) {
+    NOTREACHED() << "context not valid";
+    return;
+  }
+
+  if (path.empty() || !callback.get()) {
+    NOTREACHED() << "invalid parameter";
+    return;
+  }
+
+  if (CEF_CURRENTLY_ON_IOT()) {
+    callback->IsUnstable(
+        path, content::PluginServiceImpl::GetInstance()->IsPluginUnstable(
+                  base::FilePath(path)));
+  } else {
+    // Execute on the IO thread.
+    CEF_POST_TASK(CEF_IOT, base::Bind(CefIsWebPluginUnstable, path, callback));
+  }
+}
+
+void CefRegisterWidevineCdm(const CefString& path,
+                            CefRefPtr<CefRegisterCdmCallback> callback) {
+#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#if defined(OS_LINUX)
+  // Enforce the requirement that CefRegisterWidevineCdm() is called before
+  // CefInitialize() on Linux. See comments in
+  // CefWidevineLoader::AddPepperPlugins for details.
+  if (CONTEXT_STATE_VALID()) {
+    DeliverWidevineCdmError(
+        "Widevine registration is not supported after context initialization",
+        callback);
+    return;
+  }
+#endif  // defined(OS_LINUX)
+
+  CefWidevineLoader::GetInstance()->LoadWidevineCdm(path, callback);
+#else
+  DeliverWidevineCdmError("Widevine registration is not supported", callback);
+#endif  // BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+}
diff --git a/src/libcef/browser/web_plugin_impl.h b/src/libcef/browser/web_plugin_impl.h
new file mode 100644
index 0000000..2e58425
--- /dev/null
+++ b/src/libcef/browser/web_plugin_impl.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_WEB_PLUGIN_IMPL_H_
+#define CEF_LIBCEF_BROWSER_WEB_PLUGIN_IMPL_H_
+#pragma once
+
+#include "content/public/common/webplugininfo.h"
+#include "include/cef_web_plugin.h"
+
+class CefWebPluginInfoImpl : public CefWebPluginInfo {
+ public:
+  explicit CefWebPluginInfoImpl(const content::WebPluginInfo& plugin_info);
+
+  CefString GetName() override;
+  CefString GetPath() override;
+  CefString GetVersion() override;
+  CefString GetDescription() override;
+
+ private:
+  content::WebPluginInfo plugin_info_;
+
+  IMPLEMENT_REFCOUNTING(CefWebPluginInfoImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_WEB_PLUGIN_IMPL_H_
diff --git a/src/libcef/browser/x509_cert_principal_impl.cc b/src/libcef/browser/x509_cert_principal_impl.cc
new file mode 100644
index 0000000..233447b
--- /dev/null
+++ b/src/libcef/browser/x509_cert_principal_impl.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/x509_cert_principal_impl.h"
+
+namespace {
+
+void TransferVector(const std::vector<std::string>& source,
+                    std::vector<CefString>& target) {
+  if (!target.empty())
+    target.clear();
+
+  if (!source.empty()) {
+    std::vector<std::string>::const_iterator it = source.begin();
+    for (; it != source.end(); ++it)
+      target.push_back(*it);
+  }
+}
+
+}  // namespace
+
+CefX509CertPrincipalImpl::CefX509CertPrincipalImpl(
+    const net::CertPrincipal& value)
+    : value_(value) {}
+
+CefString CefX509CertPrincipalImpl::GetDisplayName() {
+  return value_.GetDisplayName();
+}
+
+CefString CefX509CertPrincipalImpl::GetCommonName() {
+  return value_.common_name;
+}
+
+CefString CefX509CertPrincipalImpl::GetLocalityName() {
+  return value_.locality_name;
+}
+
+CefString CefX509CertPrincipalImpl::GetStateOrProvinceName() {
+  return value_.state_or_province_name;
+}
+
+CefString CefX509CertPrincipalImpl::GetCountryName() {
+  return value_.country_name;
+}
+
+void CefX509CertPrincipalImpl::GetStreetAddresses(
+    std::vector<CefString>& addresses) {
+  TransferVector(value_.street_addresses, addresses);
+}
+
+void CefX509CertPrincipalImpl::GetOrganizationNames(
+    std::vector<CefString>& names) {
+  TransferVector(value_.organization_names, names);
+}
+
+void CefX509CertPrincipalImpl::GetOrganizationUnitNames(
+    std::vector<CefString>& names) {
+  TransferVector(value_.organization_unit_names, names);
+}
+
+void CefX509CertPrincipalImpl::GetDomainComponents(
+    std::vector<CefString>& components) {
+  TransferVector(value_.domain_components, components);
+}
diff --git a/src/libcef/browser/x509_cert_principal_impl.h b/src/libcef/browser/x509_cert_principal_impl.h
new file mode 100644
index 0000000..20b409e
--- /dev/null
+++ b/src/libcef/browser/x509_cert_principal_impl.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_X509_CERT_PRINCIPAL_IMPL_H_
+#define CEF_LIBCEF_BROWSER_X509_CERT_PRINCIPAL_IMPL_H_
+#pragma once
+
+#include "include/cef_x509_certificate.h"
+
+#include "net/cert/x509_cert_types.h"
+
+// CefX509CertPrincipal implementation
+class CefX509CertPrincipalImpl : public CefX509CertPrincipal {
+ public:
+  explicit CefX509CertPrincipalImpl(const net::CertPrincipal& value);
+
+  // CefX509CertPrincipal methods.
+  CefString GetDisplayName() override;
+  CefString GetCommonName() override;
+  CefString GetLocalityName() override;
+  CefString GetStateOrProvinceName() override;
+  CefString GetCountryName() override;
+  void GetStreetAddresses(std::vector<CefString>& addresses) override;
+  void GetOrganizationNames(std::vector<CefString>& names) override;
+  void GetOrganizationUnitNames(std::vector<CefString>& names) override;
+  void GetDomainComponents(std::vector<CefString>& components) override;
+
+ private:
+  net::CertPrincipal value_;
+
+  IMPLEMENT_REFCOUNTING(CefX509CertPrincipalImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefX509CertPrincipalImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_X509_CERT_PRINCIPAL_IMPL_H_
diff --git a/src/libcef/browser/x509_certificate_impl.cc b/src/libcef/browser/x509_certificate_impl.cc
new file mode 100644
index 0000000..0ae4c42
--- /dev/null
+++ b/src/libcef/browser/x509_certificate_impl.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/x509_certificate_impl.h"
+
+#include "libcef/browser/x509_cert_principal_impl.h"
+#include "libcef/common/time_util.h"
+
+#include "net/cert/x509_util.h"
+#include "net/ssl/ssl_private_key.h"
+
+namespace {
+
+CefRefPtr<CefBinaryValue> EncodeCertificate(const CRYPTO_BUFFER* cert_buffer,
+                                            bool der) {
+  std::string encoded;
+  if (der) {
+    encoded =
+        std::string(net::x509_util::CryptoBufferAsStringPiece(cert_buffer));
+  } else if (!net::X509Certificate::GetPEMEncoded(cert_buffer, &encoded)) {
+    return nullptr;
+  }
+  if (encoded.empty())
+    return nullptr;
+  return CefBinaryValue::Create(encoded.c_str(), encoded.size());
+}
+
+}  // namespace
+
+CefX509CertificateImpl::CefX509CertificateImpl(
+    std::unique_ptr<net::ClientCertIdentity> identity)
+    : identity_(std::move(identity)), cert_(identity_->certificate()) {}
+
+CefX509CertificateImpl::CefX509CertificateImpl(
+    scoped_refptr<net::X509Certificate> cert)
+    : cert_(cert) {}
+
+CefRefPtr<CefX509CertPrincipal> CefX509CertificateImpl::GetSubject() {
+  if (cert_)
+    return new CefX509CertPrincipalImpl(cert_->subject());
+  return nullptr;
+}
+
+CefRefPtr<CefX509CertPrincipal> CefX509CertificateImpl::GetIssuer() {
+  if (cert_)
+    return new CefX509CertPrincipalImpl(cert_->issuer());
+  return nullptr;
+}
+
+CefRefPtr<CefBinaryValue> CefX509CertificateImpl::GetSerialNumber() {
+  if (cert_) {
+    const std::string& serial = cert_->serial_number();
+    return CefBinaryValue::Create(serial.c_str(), serial.size());
+  }
+  return nullptr;
+}
+
+CefTime CefX509CertificateImpl::GetValidStart() {
+  CefTime validity;
+  if (cert_) {
+    const base::Time& valid_time = cert_->valid_start();
+    if (!valid_time.is_null())
+      cef_time_from_basetime(valid_time, validity);
+  }
+  return validity;
+}
+
+CefTime CefX509CertificateImpl::GetValidExpiry() {
+  CefTime validity;
+  if (cert_) {
+    const base::Time& valid_time = cert_->valid_expiry();
+    if (!valid_time.is_null())
+      cef_time_from_basetime(valid_time, validity);
+  }
+  return validity;
+}
+
+CefRefPtr<CefBinaryValue> CefX509CertificateImpl::GetDEREncoded() {
+  if (cert_) {
+    const CRYPTO_BUFFER* cert_buffer = cert_->cert_buffer();
+    if (cert_buffer)
+      return EncodeCertificate(cert_buffer, true);
+  }
+  return nullptr;
+}
+
+CefRefPtr<CefBinaryValue> CefX509CertificateImpl::GetPEMEncoded() {
+  if (cert_) {
+    const CRYPTO_BUFFER* cert_buffer = cert_->cert_buffer();
+    if (cert_buffer)
+      return EncodeCertificate(cert_buffer, false);
+  }
+  return nullptr;
+}
+
+size_t CefX509CertificateImpl::GetIssuerChainSize() {
+  if (cert_)
+    return cert_->intermediate_buffers().size();
+  return 0;
+}
+
+void CefX509CertificateImpl::AcquirePrivateKey(
+    base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
+        private_key_callback) {
+  if (identity_)
+    identity_->AcquirePrivateKey(std::move(private_key_callback));
+  else
+    std::move(private_key_callback).Run(nullptr);
+}
+
+void CefX509CertificateImpl::GetEncodedIssuerChain(
+    CefX509Certificate::IssuerChainBinaryList& chain,
+    bool der) {
+  chain.clear();
+  if (cert_) {
+    for (const auto& it : cert_->intermediate_buffers()) {
+      // Add each to the chain, even if one conversion unexpectedly failed.
+      // GetIssuerChainSize depends on these being the same length.
+      chain.push_back(EncodeCertificate(it.get(), der));
+    }
+  }
+}
+
+void CefX509CertificateImpl::GetDEREncodedIssuerChain(
+    CefX509Certificate::IssuerChainBinaryList& chain) {
+  if (der_encoded_issuer_chain_.empty())
+    GetEncodedIssuerChain(der_encoded_issuer_chain_, true);
+  chain = der_encoded_issuer_chain_;
+}
+
+void CefX509CertificateImpl::GetPEMEncodedIssuerChain(
+    CefX509Certificate::IssuerChainBinaryList& chain) {
+  if (pem_encoded_issuer_chain_.empty())
+    GetEncodedIssuerChain(pem_encoded_issuer_chain_, false);
+  chain = pem_encoded_issuer_chain_;
+}
diff --git a/src/libcef/browser/x509_certificate_impl.h b/src/libcef/browser/x509_certificate_impl.h
new file mode 100644
index 0000000..5a5fc37
--- /dev/null
+++ b/src/libcef/browser/x509_certificate_impl.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_X509_CERTIFICATE_IMPL_H_
+#define CEF_LIBCEF_BROWSER_X509_CERTIFICATE_IMPL_H_
+#pragma once
+
+#include "include/cef_x509_certificate.h"
+
+#include <memory>
+
+#include "net/ssl/client_cert_identity.h"
+
+// CefX509Certificate implementation
+class CefX509CertificateImpl : public CefX509Certificate {
+ public:
+  explicit CefX509CertificateImpl(scoped_refptr<net::X509Certificate> cert);
+
+  // Used with CefContentBrowserClient::SelectClientCertificate only.
+  explicit CefX509CertificateImpl(
+      std::unique_ptr<net::ClientCertIdentity> identity);
+
+  // CefX509Certificate methods.
+  CefRefPtr<CefX509CertPrincipal> GetSubject() override;
+  CefRefPtr<CefX509CertPrincipal> GetIssuer() override;
+  CefRefPtr<CefBinaryValue> GetSerialNumber() override;
+  CefTime GetValidStart() override;
+  CefTime GetValidExpiry() override;
+  CefRefPtr<CefBinaryValue> GetDEREncoded() override;
+  CefRefPtr<CefBinaryValue> GetPEMEncoded() override;
+  size_t GetIssuerChainSize() override;
+  void GetDEREncodedIssuerChain(IssuerChainBinaryList& chain) override;
+  void GetPEMEncodedIssuerChain(IssuerChainBinaryList& chain) override;
+
+  scoped_refptr<net::X509Certificate> GetInternalCertObject() { return cert_; }
+  void AcquirePrivateKey(
+      base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
+          private_key_callback);
+
+ private:
+  void GetEncodedIssuerChain(IssuerChainBinaryList& chain, bool der);
+
+  std::unique_ptr<net::ClientCertIdentity> identity_;
+  scoped_refptr<net::X509Certificate> cert_;
+  IssuerChainBinaryList pem_encoded_issuer_chain_;
+  IssuerChainBinaryList der_encoded_issuer_chain_;
+
+  IMPLEMENT_REFCOUNTING(CefX509CertificateImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefX509CertificateImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_X509_CERTIFICATE_IMPL_H_
diff --git a/src/libcef/browser/xml_reader_impl.cc b/src/libcef/browser/xml_reader_impl.cc
new file mode 100644
index 0000000..5a18a6f
--- /dev/null
+++ b/src/libcef/browser/xml_reader_impl.cc
@@ -0,0 +1,452 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/xml_reader_impl.h"
+#include "base/logging.h"
+#include "include/cef_stream.h"
+
+// Static functions
+
+// static
+CefRefPtr<CefXmlReader> CefXmlReader::Create(CefRefPtr<CefStreamReader> stream,
+                                             EncodingType encodingType,
+                                             const CefString& URI) {
+  CefRefPtr<CefXmlReaderImpl> impl(new CefXmlReaderImpl());
+  if (!impl->Initialize(stream, encodingType, URI))
+    return nullptr;
+  return impl.get();
+}
+
+// CefXmlReaderImpl
+
+namespace {
+
+/**
+ * xmlInputReadCallback:
+ * @context:  an Input context
+ * @buffer:  the buffer to store data read
+ * @len:  the length of the buffer in bytes
+ *
+ * Callback used in the I/O Input API to read the resource
+ *
+ * Returns the number of bytes read or -1 in case of error
+ */
+int XMLCALL xml_read_callback(void* context, char* buffer, int len) {
+  CefRefPtr<CefStreamReader> reader(static_cast<CefStreamReader*>(context));
+  return reader->Read(buffer, 1, len);
+}
+
+/**
+ * xmlTextReaderErrorFunc:
+ * @arg: the user argument
+ * @msg: the message
+ * @severity: the severity of the error
+ * @locator: a locator indicating where the error occured
+ *
+ * Signature of an error callback from a reader parser
+ */
+void XMLCALL xml_error_callback(void* arg,
+                                const char* msg,
+                                xmlParserSeverities severity,
+                                xmlTextReaderLocatorPtr locator) {
+  if (!msg)
+    return;
+
+  std::string error_str(msg);
+  if (!error_str.empty() && error_str[error_str.length() - 1] == '\n')
+    error_str.resize(error_str.length() - 1);
+
+  std::stringstream ss;
+  ss << error_str << ", line " << xmlTextReaderLocatorLineNumber(locator);
+
+  LOG(INFO) << ss.str();
+
+  CefRefPtr<CefXmlReaderImpl> impl(static_cast<CefXmlReaderImpl*>(arg));
+  impl->AppendError(ss.str());
+}
+
+/**
+ * xmlStructuredErrorFunc:
+ * @userData:  user provided data for the error callback
+ * @error:  the error being raised.
+ *
+ * Signature of the function to use when there is an error and
+ * the module handles the new error reporting mechanism.
+ */
+void XMLCALL xml_structured_error_callback(void* userData, xmlErrorPtr error) {
+  if (!error->message)
+    return;
+
+  std::string error_str(error->message);
+  if (!error_str.empty() && error_str[error_str.length() - 1] == '\n')
+    error_str.resize(error_str.length() - 1);
+
+  std::stringstream ss;
+  ss << error_str << ", line " << error->line;
+
+  LOG(INFO) << ss.str();
+
+  CefRefPtr<CefXmlReaderImpl> impl(static_cast<CefXmlReaderImpl*>(userData));
+  impl->AppendError(ss.str());
+}
+
+CefString xmlCharToString(const xmlChar* xmlStr, bool free) {
+  if (!xmlStr)
+    return CefString();
+
+  const char* str = reinterpret_cast<const char*>(xmlStr);
+  CefString wstr = std::string(str);
+
+  if (free)
+    xmlFree(const_cast<xmlChar*>(xmlStr));
+
+  return wstr;
+}
+
+}  // namespace
+
+CefXmlReaderImpl::CefXmlReaderImpl()
+    : supported_thread_id_(base::PlatformThread::CurrentId()),
+      reader_(nullptr) {}
+
+CefXmlReaderImpl::~CefXmlReaderImpl() {
+  if (reader_ != nullptr) {
+    if (!VerifyContext()) {
+      // Close() is supposed to be called directly. We'll try to free the reader
+      // now on the wrong thread but there's no guarantee this call won't crash.
+      xmlFreeTextReader(reader_);
+    } else {
+      Close();
+    }
+  }
+}
+
+bool CefXmlReaderImpl::Initialize(CefRefPtr<CefStreamReader> stream,
+                                  EncodingType encodingType,
+                                  const CefString& URI) {
+  xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
+  switch (encodingType) {
+    case XML_ENCODING_UTF8:
+      enc = XML_CHAR_ENCODING_UTF8;
+      break;
+    case XML_ENCODING_UTF16LE:
+      enc = XML_CHAR_ENCODING_UTF16LE;
+      break;
+    case XML_ENCODING_UTF16BE:
+      enc = XML_CHAR_ENCODING_UTF16BE;
+      break;
+    case XML_ENCODING_ASCII:
+      enc = XML_CHAR_ENCODING_ASCII;
+      break;
+    default:
+      break;
+  }
+
+  // Create the input buffer.
+  xmlParserInputBufferPtr input_buffer = xmlAllocParserInputBuffer(enc);
+  if (!input_buffer)
+    return false;
+
+  input_buffer->context = stream.get();
+  input_buffer->readcallback = xml_read_callback;
+
+  // Create the text reader.
+  std::string uriStr = URI;
+  reader_ = xmlNewTextReader(input_buffer, uriStr.c_str());
+  if (!reader_) {
+    // Free the input buffer.
+    xmlFreeParserInputBuffer(input_buffer);
+    return false;
+  }
+
+  // Keep a reference to the stream.
+  stream_ = stream;
+
+  // Register the error callbacks.
+  xmlTextReaderSetErrorHandler(reader_, xml_error_callback, this);
+  xmlTextReaderSetStructuredErrorHandler(reader_, xml_structured_error_callback,
+                                         this);
+
+  return true;
+}
+
+bool CefXmlReaderImpl::MoveToNextNode() {
+  if (!VerifyContext())
+    return false;
+
+  return xmlTextReaderRead(reader_) == 1 ? true : false;
+}
+
+bool CefXmlReaderImpl::Close() {
+  if (!VerifyContext())
+    return false;
+
+  // The input buffer will be freed automatically.
+  xmlFreeTextReader(reader_);
+  reader_ = nullptr;
+  return true;
+}
+
+bool CefXmlReaderImpl::HasError() {
+  if (!VerifyContext())
+    return false;
+
+  return !error_buf_.str().empty();
+}
+
+CefString CefXmlReaderImpl::GetError() {
+  if (!VerifyContext())
+    return CefString();
+
+  return error_buf_.str();
+}
+
+CefXmlReader::NodeType CefXmlReaderImpl::GetType() {
+  if (!VerifyContext())
+    return XML_NODE_UNSUPPORTED;
+
+  switch (xmlTextReaderNodeType(reader_)) {
+    case XML_READER_TYPE_ELEMENT:
+      return XML_NODE_ELEMENT_START;
+    case XML_READER_TYPE_END_ELEMENT:
+      return XML_NODE_ELEMENT_END;
+    case XML_READER_TYPE_ATTRIBUTE:
+      return XML_NODE_ATTRIBUTE;
+    case XML_READER_TYPE_TEXT:
+      return XML_NODE_TEXT;
+    case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
+    case XML_READER_TYPE_WHITESPACE:
+      return XML_NODE_WHITESPACE;
+    case XML_READER_TYPE_CDATA:
+      return XML_NODE_CDATA;
+    case XML_READER_TYPE_ENTITY_REFERENCE:
+      return XML_NODE_ENTITY_REFERENCE;
+    case XML_READER_TYPE_PROCESSING_INSTRUCTION:
+      return XML_NODE_PROCESSING_INSTRUCTION;
+    case XML_READER_TYPE_COMMENT:
+      return XML_NODE_COMMENT;
+    case XML_READER_TYPE_DOCUMENT_TYPE:
+      return XML_NODE_DOCUMENT_TYPE;
+    default:
+      break;
+  }
+
+  return XML_NODE_UNSUPPORTED;
+}
+
+int CefXmlReaderImpl::GetDepth() {
+  if (!VerifyContext())
+    return -1;
+
+  return xmlTextReaderDepth(reader_);
+}
+
+CefString CefXmlReaderImpl::GetLocalName() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderConstLocalName(reader_), false);
+}
+
+CefString CefXmlReaderImpl::GetPrefix() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderConstPrefix(reader_), false);
+}
+
+CefString CefXmlReaderImpl::GetQualifiedName() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderConstName(reader_), false);
+}
+
+CefString CefXmlReaderImpl::GetNamespaceURI() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderConstNamespaceUri(reader_), false);
+}
+
+CefString CefXmlReaderImpl::GetBaseURI() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderConstBaseUri(reader_), false);
+}
+
+CefString CefXmlReaderImpl::GetXmlLang() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderConstXmlLang(reader_), false);
+}
+
+bool CefXmlReaderImpl::IsEmptyElement() {
+  if (!VerifyContext())
+    return false;
+
+  return xmlTextReaderIsEmptyElement(reader_) == 1 ? true : false;
+}
+
+bool CefXmlReaderImpl::HasValue() {
+  if (!VerifyContext())
+    return false;
+
+  if (xmlTextReaderNodeType(reader_) == XML_READER_TYPE_ENTITY_REFERENCE) {
+    // Provide special handling to return entity reference values.
+    return true;
+  } else {
+    return xmlTextReaderHasValue(reader_) == 1 ? true : false;
+  }
+}
+
+CefString CefXmlReaderImpl::GetValue() {
+  if (!VerifyContext())
+    return CefString();
+
+  if (xmlTextReaderNodeType(reader_) == XML_READER_TYPE_ENTITY_REFERENCE) {
+    // Provide special handling to return entity reference values.
+    xmlNodePtr node = xmlTextReaderCurrentNode(reader_);
+    if (node->content != nullptr)
+      return xmlCharToString(node->content, false);
+    return CefString();
+  } else {
+    return xmlCharToString(xmlTextReaderConstValue(reader_), false);
+  }
+}
+
+bool CefXmlReaderImpl::HasAttributes() {
+  if (!VerifyContext())
+    return false;
+
+  return xmlTextReaderHasAttributes(reader_) == 1 ? true : false;
+}
+
+size_t CefXmlReaderImpl::GetAttributeCount() {
+  if (!VerifyContext())
+    return 0;
+
+  return xmlTextReaderAttributeCount(reader_);
+}
+
+CefString CefXmlReaderImpl::GetAttribute(int index) {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderGetAttributeNo(reader_, index), true);
+}
+
+CefString CefXmlReaderImpl::GetAttribute(const CefString& qualifiedName) {
+  if (!VerifyContext())
+    return CefString();
+
+  std::string qualifiedNameStr = qualifiedName;
+  return xmlCharToString(
+      xmlTextReaderGetAttribute(reader_, BAD_CAST qualifiedNameStr.c_str()),
+      true);
+}
+
+CefString CefXmlReaderImpl::GetAttribute(const CefString& localName,
+                                         const CefString& namespaceURI) {
+  if (!VerifyContext())
+    return CefString();
+
+  std::string localNameStr = localName;
+  std::string namespaceURIStr = namespaceURI;
+  return xmlCharToString(
+      xmlTextReaderGetAttributeNs(reader_, BAD_CAST localNameStr.c_str(),
+                                  BAD_CAST namespaceURIStr.c_str()),
+      true);
+}
+
+CefString CefXmlReaderImpl::GetInnerXml() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderReadInnerXml(reader_), true);
+}
+
+CefString CefXmlReaderImpl::GetOuterXml() {
+  if (!VerifyContext())
+    return CefString();
+
+  return xmlCharToString(xmlTextReaderReadOuterXml(reader_), true);
+}
+
+int CefXmlReaderImpl::GetLineNumber() {
+  if (!VerifyContext())
+    return -1;
+
+  return xmlTextReaderGetParserLineNumber(reader_);
+}
+
+bool CefXmlReaderImpl::MoveToAttribute(int index) {
+  if (!VerifyContext())
+    return false;
+
+  return xmlTextReaderMoveToAttributeNo(reader_, index) == 1 ? true : false;
+}
+
+bool CefXmlReaderImpl::MoveToAttribute(const CefString& qualifiedName) {
+  if (!VerifyContext())
+    return false;
+
+  std::string qualifiedNameStr = qualifiedName;
+  return xmlTextReaderMoveToAttribute(reader_,
+                                      BAD_CAST qualifiedNameStr.c_str()) == 1
+             ? true
+             : false;
+}
+
+bool CefXmlReaderImpl::MoveToAttribute(const CefString& localName,
+                                       const CefString& namespaceURI) {
+  if (!VerifyContext())
+    return false;
+
+  std::string localNameStr = localName;
+  std::string namespaceURIStr = namespaceURI;
+  return xmlTextReaderMoveToAttributeNs(reader_, BAD_CAST localNameStr.c_str(),
+                                        BAD_CAST namespaceURIStr.c_str()) == 1
+             ? true
+             : false;
+}
+
+bool CefXmlReaderImpl::MoveToFirstAttribute() {
+  if (!VerifyContext())
+    return false;
+
+  return xmlTextReaderMoveToFirstAttribute(reader_) == 1 ? true : false;
+}
+
+bool CefXmlReaderImpl::MoveToNextAttribute() {
+  if (!VerifyContext())
+    return false;
+
+  return xmlTextReaderMoveToNextAttribute(reader_) == 1 ? true : false;
+}
+
+bool CefXmlReaderImpl::MoveToCarryingElement() {
+  if (!VerifyContext())
+    return false;
+
+  return xmlTextReaderMoveToElement(reader_) == 1 ? true : false;
+}
+
+void CefXmlReaderImpl::AppendError(const CefString& error_str) {
+  if (!error_buf_.str().empty())
+    error_buf_ << L"\n";
+  error_buf_ << error_str;
+}
+
+bool CefXmlReaderImpl::VerifyContext() {
+  if (base::PlatformThread::CurrentId() != supported_thread_id_) {
+    // This object should only be accessed from the thread that created it.
+    NOTREACHED();
+    return false;
+  }
+
+  return (reader_ != nullptr);
+}
diff --git a/src/libcef/browser/xml_reader_impl.h b/src/libcef/browser/xml_reader_impl.h
new file mode 100644
index 0000000..eafa63f
--- /dev/null
+++ b/src/libcef/browser/xml_reader_impl.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_XML_READER_IMPL_H_
+#define CEF_LIBCEF_BROWSER_XML_READER_IMPL_H_
+#pragma once
+
+#include <libxml/xmlreader.h>
+#include <sstream>
+
+#include "base/threading/platform_thread.h"
+#include "include/cef_xml_reader.h"
+
+// Implementation of CefXmlReader
+class CefXmlReaderImpl : public CefXmlReader {
+ public:
+  CefXmlReaderImpl();
+  ~CefXmlReaderImpl() override;
+
+  // Initialize the reader context.
+  bool Initialize(CefRefPtr<CefStreamReader> stream,
+                  EncodingType encodingType,
+                  const CefString& URI);
+
+  bool MoveToNextNode() override;
+  bool Close() override;
+  bool HasError() override;
+  CefString GetError() override;
+  NodeType GetType() override;
+  int GetDepth() override;
+  CefString GetLocalName() override;
+  CefString GetPrefix() override;
+  CefString GetQualifiedName() override;
+  CefString GetNamespaceURI() override;
+  CefString GetBaseURI() override;
+  CefString GetXmlLang() override;
+  bool IsEmptyElement() override;
+  bool HasValue() override;
+  CefString GetValue() override;
+  bool HasAttributes() override;
+  size_t GetAttributeCount() override;
+  CefString GetAttribute(int index) override;
+  CefString GetAttribute(const CefString& qualifiedName) override;
+  CefString GetAttribute(const CefString& localName,
+                         const CefString& namespaceURI) override;
+  CefString GetInnerXml() override;
+  CefString GetOuterXml() override;
+  int GetLineNumber() override;
+  bool MoveToAttribute(int index) override;
+  bool MoveToAttribute(const CefString& qualifiedName) override;
+  bool MoveToAttribute(const CefString& localName,
+                       const CefString& namespaceURI) override;
+  bool MoveToFirstAttribute() override;
+  bool MoveToNextAttribute() override;
+  bool MoveToCarryingElement() override;
+
+  // Add another line to the error string.
+  void AppendError(const CefString& error_str);
+
+  // Verify that the reader exists and is being accessed from the correct
+  // thread.
+  bool VerifyContext();
+
+ protected:
+  base::PlatformThreadId supported_thread_id_;
+  CefRefPtr<CefStreamReader> stream_;
+  xmlTextReaderPtr reader_;
+  std::stringstream error_buf_;
+
+  IMPLEMENT_REFCOUNTING(CefXmlReaderImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_XML_READER_IMPL_H_
diff --git a/src/libcef/browser/zip_reader_impl.cc b/src/libcef/browser/zip_reader_impl.cc
new file mode 100644
index 0000000..57bc299
--- /dev/null
+++ b/src/libcef/browser/zip_reader_impl.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/browser/zip_reader_impl.h"
+#include <time.h>
+#include "base/logging.h"
+#include "include/cef_stream.h"
+
+// Static functions
+
+// static
+CefRefPtr<CefZipReader> CefZipReader::Create(
+    CefRefPtr<CefStreamReader> stream) {
+  CefRefPtr<CefZipReaderImpl> impl(new CefZipReaderImpl());
+  if (!impl->Initialize(stream))
+    return nullptr;
+  return impl.get();
+}
+
+// CefZipReaderImpl
+
+namespace {
+
+voidpf ZCALLBACK zlib_open_callback OF((voidpf opaque,
+                                        const void* filename,
+                                        int mode)) {
+  // The stream is already implicitly open so just return the pointer.
+  return opaque;
+}
+
+uLong ZCALLBACK zlib_read_callback
+OF((voidpf opaque, voidpf stream, void* buf, uLong size)) {
+  CefRefPtr<CefStreamReader> reader(static_cast<CefStreamReader*>(opaque));
+  return reader->Read(buf, 1, size);
+}
+
+ZPOS64_T ZCALLBACK zlib_tell_callback OF((voidpf opaque, voidpf stream)) {
+  CefRefPtr<CefStreamReader> reader(static_cast<CefStreamReader*>(opaque));
+  return reader->Tell();
+}
+
+long ZCALLBACK zlib_seek_callback
+OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)) {
+  CefRefPtr<CefStreamReader> reader(static_cast<CefStreamReader*>(opaque));
+  int whence;
+  switch (origin) {
+    case ZLIB_FILEFUNC_SEEK_CUR:
+      whence = SEEK_CUR;
+      break;
+    case ZLIB_FILEFUNC_SEEK_END:
+      whence = SEEK_END;
+      break;
+    case ZLIB_FILEFUNC_SEEK_SET:
+      whence = SEEK_SET;
+      break;
+    default:
+      NOTREACHED();
+      return -1;
+  }
+  return reader->Seek(offset, whence);
+}
+
+int ZCALLBACK zlib_close_callback OF((voidpf opaque, voidpf stream)) {
+  CefRefPtr<CefStreamReader> reader(static_cast<CefStreamReader*>(opaque));
+  // Release the reference added by CefZipReaderImpl::Initialize().
+  reader->Release();
+  return 0;
+}
+
+int ZCALLBACK zlib_error_callback OF((voidpf opaque, voidpf stream)) {
+  return 0;
+}
+
+}  // namespace
+
+CefZipReaderImpl::CefZipReaderImpl()
+    : supported_thread_id_(base::PlatformThread::CurrentId()),
+      reader_(nullptr),
+      has_fileopen_(false),
+      has_fileinfo_(false),
+      filesize_(0),
+      filemodified_(0) {}
+
+CefZipReaderImpl::~CefZipReaderImpl() {
+  if (reader_ != nullptr) {
+    if (!VerifyContext()) {
+      // Close() is supposed to be called directly. We'll try to free the reader
+      // now on the wrong thread but there's no guarantee this call won't crash.
+      if (has_fileopen_)
+        unzCloseCurrentFile(reader_);
+      unzClose(reader_);
+    } else {
+      Close();
+    }
+  }
+}
+
+bool CefZipReaderImpl::Initialize(CefRefPtr<CefStreamReader> stream) {
+  zlib_filefunc64_def filefunc_def;
+  filefunc_def.zopen64_file = zlib_open_callback;
+  filefunc_def.zread_file = zlib_read_callback;
+  filefunc_def.zwrite_file = nullptr;
+  filefunc_def.ztell64_file = zlib_tell_callback;
+  filefunc_def.zseek64_file = zlib_seek_callback;
+  filefunc_def.zclose_file = zlib_close_callback;
+  filefunc_def.zerror_file = zlib_error_callback;
+  filefunc_def.opaque = stream.get();
+
+  // Add a reference that will be released by zlib_close_callback().
+  stream->AddRef();
+
+  reader_ = unzOpen2_64("", &filefunc_def);
+  return (reader_ != nullptr);
+}
+
+bool CefZipReaderImpl::MoveToFirstFile() {
+  if (!VerifyContext())
+    return false;
+
+  if (has_fileopen_)
+    CloseFile();
+
+  has_fileinfo_ = false;
+
+  return (unzGoToFirstFile(reader_) == UNZ_OK);
+}
+
+bool CefZipReaderImpl::MoveToNextFile() {
+  if (!VerifyContext())
+    return false;
+
+  if (has_fileopen_)
+    CloseFile();
+
+  has_fileinfo_ = false;
+
+  return (unzGoToNextFile(reader_) == UNZ_OK);
+}
+
+bool CefZipReaderImpl::MoveToFile(const CefString& fileName,
+                                  bool caseSensitive) {
+  if (!VerifyContext())
+    return false;
+
+  if (has_fileopen_)
+    CloseFile();
+
+  has_fileinfo_ = false;
+
+  std::string fileNameStr = fileName;
+  return (unzLocateFile(reader_, fileNameStr.c_str(),
+                        (caseSensitive ? 1 : 2)) == UNZ_OK);
+}
+
+bool CefZipReaderImpl::Close() {
+  if (!VerifyContext())
+    return false;
+
+  if (has_fileopen_)
+    CloseFile();
+
+  int result = unzClose(reader_);
+  reader_ = nullptr;
+  return (result == UNZ_OK);
+}
+
+CefString CefZipReaderImpl::GetFileName() {
+  if (!VerifyContext() || !GetFileInfo())
+    return CefString();
+
+  return filename_;
+}
+
+int64 CefZipReaderImpl::GetFileSize() {
+  if (!VerifyContext() || !GetFileInfo())
+    return -1;
+
+  return filesize_;
+}
+
+CefTime CefZipReaderImpl::GetFileLastModified() {
+  CefTime time;
+  if (!VerifyContext() || !GetFileInfo())
+    return time;
+
+  cef_time_from_timet(filemodified_, &time);
+  return time;
+}
+
+bool CefZipReaderImpl::OpenFile(const CefString& password) {
+  if (!VerifyContext())
+    return false;
+
+  if (has_fileopen_)
+    CloseFile();
+
+  bool ret;
+
+  if (password.empty()) {
+    ret = (unzOpenCurrentFile(reader_) == UNZ_OK);
+  } else {
+    std::string passwordStr = password;
+    ret = (unzOpenCurrentFilePassword(reader_, passwordStr.c_str()) == UNZ_OK);
+  }
+
+  if (ret)
+    has_fileopen_ = true;
+  return ret;
+}
+
+bool CefZipReaderImpl::CloseFile() {
+  if (!VerifyContext() || !has_fileopen_)
+    return false;
+
+  has_fileopen_ = false;
+  has_fileinfo_ = false;
+
+  return (unzCloseCurrentFile(reader_) == UNZ_OK);
+}
+
+int CefZipReaderImpl::ReadFile(void* buffer, size_t bufferSize) {
+  if (!VerifyContext() || !has_fileopen_)
+    return -1;
+
+  return unzReadCurrentFile(reader_, buffer, bufferSize);
+}
+
+int64 CefZipReaderImpl::Tell() {
+  if (!VerifyContext() || !has_fileopen_)
+    return -1;
+
+  return unztell64(reader_);
+}
+
+bool CefZipReaderImpl::Eof() {
+  if (!VerifyContext() || !has_fileopen_)
+    return true;
+
+  return (unzeof(reader_) == 1 ? true : false);
+}
+
+bool CefZipReaderImpl::GetFileInfo() {
+  if (has_fileinfo_)
+    return true;
+
+  char file_name[512] = {0};
+  unz_file_info file_info;
+  memset(&file_info, 0, sizeof(file_info));
+
+  if (unzGetCurrentFileInfo(reader_, &file_info, file_name, sizeof(file_name),
+                            NULL, 0, NULL, 0) != UNZ_OK) {
+    return false;
+  }
+
+  has_fileinfo_ = true;
+  filename_ = std::string(file_name);
+  filesize_ = file_info.uncompressed_size;
+
+  struct tm time;
+  memset(&time, 0, sizeof(time));
+  time.tm_sec = file_info.tmu_date.tm_sec;
+  time.tm_min = file_info.tmu_date.tm_min;
+  time.tm_hour = file_info.tmu_date.tm_hour;
+  time.tm_mday = file_info.tmu_date.tm_mday;
+  time.tm_mon = file_info.tmu_date.tm_mon;
+  time.tm_year = file_info.tmu_date.tm_year - 1900;  // Years since 1900.
+  filemodified_ = mktime(&time);
+  DCHECK_NE(filemodified_, (time_t)-1);
+
+  return true;
+}
+
+bool CefZipReaderImpl::VerifyContext() {
+  if (base::PlatformThread::CurrentId() != supported_thread_id_) {
+    // This object should only be accessed from the thread that created it.
+    NOTREACHED();
+    return false;
+  }
+
+  return (reader_ != nullptr);
+}
diff --git a/src/libcef/browser/zip_reader_impl.h b/src/libcef/browser/zip_reader_impl.h
new file mode 100644
index 0000000..722dbf9
--- /dev/null
+++ b/src/libcef/browser/zip_reader_impl.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_BROWSER_ZIP_READER_IMPL_H_
+#define CEF_LIBCEF_BROWSER_ZIP_READER_IMPL_H_
+#pragma once
+
+#include <sstream>
+
+#include "base/threading/platform_thread.h"
+#include "include/cef_zip_reader.h"
+#include "third_party/zlib/contrib/minizip/unzip.h"
+
+// Implementation of CefZipReader
+class CefZipReaderImpl : public CefZipReader {
+ public:
+  CefZipReaderImpl();
+  ~CefZipReaderImpl() override;
+
+  // Initialize the reader context.
+  bool Initialize(CefRefPtr<CefStreamReader> stream);
+
+  bool MoveToFirstFile() override;
+  bool MoveToNextFile() override;
+  bool MoveToFile(const CefString& fileName, bool caseSensitive) override;
+  bool Close() override;
+  CefString GetFileName() override;
+  int64 GetFileSize() override;
+  CefTime GetFileLastModified() override;
+  bool OpenFile(const CefString& password) override;
+  bool CloseFile() override;
+  int ReadFile(void* buffer, size_t bufferSize) override;
+  int64 Tell() override;
+  bool Eof() override;
+
+  bool GetFileInfo();
+
+  // Verify that the reader exists and is being accessed from the correct
+  // thread.
+  bool VerifyContext();
+
+ protected:
+  base::PlatformThreadId supported_thread_id_;
+  unzFile reader_;
+  bool has_fileopen_;
+  bool has_fileinfo_;
+  CefString filename_;
+  int64 filesize_;
+  time_t filemodified_;
+
+  IMPLEMENT_REFCOUNTING(CefZipReaderImpl);
+};
+
+#endif  // CEF_LIBCEF_BROWSER_ZIP_READER_IMPL_H_
diff --git a/src/libcef/common/base_impl.cc b/src/libcef/common/base_impl.cc
new file mode 100644
index 0000000..07ec5d7
--- /dev/null
+++ b/src/libcef/common/base_impl.cc
@@ -0,0 +1,355 @@
+// Copyright 2014 The Chromium Embedded Framework Authors. Portions copyright
+// 2011 the Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "include/base/cef_build.h"
+
+#include "include/internal/cef_logging_internal.h"
+#include "include/internal/cef_thread_internal.h"
+#include "include/internal/cef_trace_event_internal.h"
+
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+#include "base/trace_event/trace_event.h"
+
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
+namespace {
+
+constexpr const char kCategory[] = "cef.client";
+
+}  // namespace
+
+// The contents of this file are a compilation unit that is not called by other
+// functions in the the library. Consiquently MSVS will exclude it during the
+// linker stage if we don't call a stub function.
+#if defined(COMPILER_MSVC)
+#pragma optimize("", off)
+#endif
+
+void base_impl_stub() {}
+
+#if defined(COMPILER_MSVC)
+#pragma optimize("", on)
+#endif
+
+CEF_EXPORT void cef_trace_event_instant(const char* /* category */,
+                                        const char* name,
+                                        const char* arg1_name,
+                                        uint64 arg1_val,
+                                        const char* arg2_name,
+                                        uint64 arg2_val,
+                                        int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_COPY_INSTANT0(kCategory, name, TRACE_EVENT_SCOPE_THREAD);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_COPY_INSTANT1(kCategory, name, TRACE_EVENT_SCOPE_THREAD,
+                                arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_COPY_INSTANT2(kCategory, name, TRACE_EVENT_SCOPE_THREAD,
+                                arg1_name, arg1_val, arg2_name, arg2_val);
+    }
+  } else {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_INSTANT0(kCategory, name, TRACE_EVENT_SCOPE_THREAD);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_INSTANT1(kCategory, name, TRACE_EVENT_SCOPE_THREAD, arg1_name,
+                           arg1_val);
+    } else {
+      TRACE_EVENT_INSTANT2(kCategory, name, TRACE_EVENT_SCOPE_THREAD, arg1_name,
+                           arg1_val, arg2_name, arg2_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_event_begin(const char* /* category */,
+                                      const char* name,
+                                      const char* arg1_name,
+                                      uint64 arg1_val,
+                                      const char* arg2_name,
+                                      uint64 arg2_val,
+                                      int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_BEGIN_WITH_FLAGS0(kCategory, name, TRACE_EVENT_FLAG_COPY);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_BEGIN_WITH_FLAGS1(kCategory, name, TRACE_EVENT_FLAG_COPY,
+                                    arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_COPY_BEGIN2(kCategory, name, arg1_name, arg1_val, arg2_name,
+                              arg2_val);
+    }
+  } else {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_BEGIN0(kCategory, name);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_BEGIN1(kCategory, name, arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_BEGIN2(kCategory, name, arg1_name, arg1_val, arg2_name,
+                         arg2_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_event_end(const char* /* category */,
+                                    const char* name,
+                                    const char* arg1_name,
+                                    uint64 arg1_val,
+                                    const char* arg2_name,
+                                    uint64 arg2_val,
+                                    int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_END_WITH_FLAGS0(kCategory, name, TRACE_EVENT_FLAG_COPY);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_END_WITH_FLAGS1(kCategory, name, TRACE_EVENT_FLAG_COPY,
+                                  arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_COPY_END2(kCategory, name, arg1_name, arg1_val, arg2_name,
+                            arg2_val);
+    }
+  } else {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_END0(kCategory, name);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_END1(kCategory, name, arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_END2(kCategory, name, arg1_name, arg1_val, arg2_name,
+                       arg2_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_counter(const char* /* category */,
+                                  const char* name,
+                                  const char* value1_name,
+                                  uint64 value1_val,
+                                  const char* value2_name,
+                                  uint64 value2_val,
+                                  int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (value1_name == nullptr && value2_name == nullptr) {
+      TRACE_COPY_COUNTER1(kCategory, name, value1_val);
+    } else {
+      TRACE_COPY_COUNTER2(kCategory, name, value1_name, value1_val, value2_name,
+                          value2_val);
+    }
+  } else {
+    if (value1_name == nullptr && value2_name == nullptr) {
+      TRACE_COUNTER1(kCategory, name, value1_val);
+    } else {
+      TRACE_COUNTER2(kCategory, name, value1_name, value1_val, value2_name,
+                     value2_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_counter_id(const char* /* category */,
+                                     const char* name,
+                                     uint64 id,
+                                     const char* value1_name,
+                                     uint64 value1_val,
+                                     const char* value2_name,
+                                     uint64 value2_val,
+                                     int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (value1_name == nullptr && value2_name == nullptr) {
+      TRACE_COPY_COUNTER_ID1(kCategory, name, id, value1_val);
+    } else {
+      TRACE_COPY_COUNTER_ID2(kCategory, name, id, value1_name, value1_val,
+                             value2_name, value2_val);
+    }
+  } else {
+    if (value1_name == nullptr && value2_name == nullptr) {
+      TRACE_COUNTER_ID1(kCategory, name, id, value1_val);
+    } else {
+      TRACE_COUNTER_ID2(kCategory, name, id, value1_name, value1_val,
+                        value2_name, value2_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_event_async_begin(const char* /* category */,
+                                            const char* name,
+                                            uint64 id,
+                                            const char* arg1_name,
+                                            uint64 arg1_val,
+                                            const char* arg2_name,
+                                            uint64 arg2_val,
+                                            int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_COPY_ASYNC_BEGIN0(kCategory, name, id);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_COPY_ASYNC_BEGIN1(kCategory, name, id, arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_COPY_ASYNC_BEGIN2(kCategory, name, id, arg1_name, arg1_val,
+                                    arg2_name, arg2_val);
+    }
+  } else {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_ASYNC_BEGIN0(kCategory, name, id);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_ASYNC_BEGIN1(kCategory, name, id, arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_ASYNC_BEGIN2(kCategory, name, id, arg1_name, arg1_val,
+                               arg2_name, arg2_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_event_async_step_into(const char* /* category */,
+                                                const char* name,
+                                                uint64 id,
+                                                uint64 step,
+                                                const char* arg1_name,
+                                                uint64 arg1_val,
+                                                int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (arg1_name == nullptr) {
+      INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_INTO,
+                                       kCategory, name, id,
+                                       TRACE_EVENT_FLAG_COPY, "step", step);
+    } else {
+      INTERNAL_TRACE_EVENT_ADD_WITH_ID(
+          TRACE_EVENT_PHASE_ASYNC_STEP_INTO, kCategory, name, id,
+          TRACE_EVENT_FLAG_COPY, "step", step, arg1_name, arg1_val);
+    }
+  } else {
+    if (arg1_name == nullptr) {
+      TRACE_EVENT_ASYNC_STEP_INTO0(kCategory, name, id, step);
+    } else {
+      TRACE_EVENT_ASYNC_STEP_INTO1(kCategory, name, id, step, arg1_name,
+                                   arg1_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_event_async_step_past(const char* /* category */,
+                                                const char* name,
+                                                uint64 id,
+                                                uint64 step,
+                                                const char* arg1_name,
+                                                uint64 arg1_val,
+                                                int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (arg1_name == nullptr) {
+      INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_PAST,
+                                       kCategory, name, id,
+                                       TRACE_EVENT_FLAG_COPY, "step", step);
+    } else {
+      INTERNAL_TRACE_EVENT_ADD_WITH_ID(
+          TRACE_EVENT_PHASE_ASYNC_STEP_PAST, kCategory, name, id,
+          TRACE_EVENT_FLAG_COPY, "step", step, arg1_name, arg1_val);
+    }
+  } else {
+    if (arg1_name == nullptr) {
+      TRACE_EVENT_ASYNC_STEP_PAST0(kCategory, name, id, step);
+    } else {
+      TRACE_EVENT_ASYNC_STEP_PAST1(kCategory, name, id, step, arg1_name,
+                                   arg1_val);
+    }
+  }
+}
+
+CEF_EXPORT void cef_trace_event_async_end(const char* /* category */,
+                                          const char* name,
+                                          uint64 id,
+                                          const char* arg1_name,
+                                          uint64 arg1_val,
+                                          const char* arg2_name,
+                                          uint64 arg2_val,
+                                          int copy) {
+  DCHECK(name);
+  if (!name)
+    return;
+
+  if (copy) {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_COPY_ASYNC_END0(kCategory, name, id);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_COPY_ASYNC_END1(kCategory, name, id, arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_COPY_ASYNC_END2(kCategory, name, id, arg1_name, arg1_val,
+                                  arg2_name, arg2_val);
+    }
+  } else {
+    if (arg1_name == nullptr && arg2_name == nullptr) {
+      TRACE_EVENT_ASYNC_END0(kCategory, name, id);
+    } else if (arg2_name == nullptr) {
+      TRACE_EVENT_ASYNC_END1(kCategory, name, id, arg1_name, arg1_val);
+    } else {
+      TRACE_EVENT_ASYNC_END2(kCategory, name, id, arg1_name, arg1_val,
+                             arg2_name, arg2_val);
+    }
+  }
+}
+
+CEF_EXPORT int cef_get_min_log_level() {
+  return logging::GetMinLogLevel();
+}
+
+CEF_EXPORT int cef_get_vlog_level(const char* file_start, size_t N) {
+  return logging::GetVlogLevelHelper(file_start, N);
+}
+
+CEF_EXPORT void cef_log(const char* file,
+                        int line,
+                        int severity,
+                        const char* message) {
+  logging::LogMessage(file, line, severity).stream() << message;
+}
+
+CEF_EXPORT cef_platform_thread_id_t cef_get_current_platform_thread_id() {
+  return base::PlatformThread::CurrentId();
+}
+
+CEF_EXPORT cef_platform_thread_handle_t
+cef_get_current_platform_thread_handle() {
+#if defined(OS_WIN)
+  return base::PlatformThread::CurrentId();
+#else
+  return base::PlatformThread::CurrentHandle().platform_handle();
+#endif
+}
+
+void CefEnableHighDPISupport() {
+#if defined(OS_WIN)
+  base::win::EnableHighDPISupport();
+#endif
+}
diff --git a/src/libcef/common/cef_crash_report_upload_thread.cc b/src/libcef/common/cef_crash_report_upload_thread.cc
new file mode 100644
index 0000000..1c81feb
--- /dev/null
+++ b/src/libcef/common/cef_crash_report_upload_thread.cc
@@ -0,0 +1,210 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/cef_crash_report_upload_thread.h"
+
+#include "libcef/common/cef_crash_report_utils.h"
+#include "third_party/crashpad/crashpad/client/settings.h"
+
+using namespace crashpad;
+
+CefCrashReportUploadThread::CefCrashReportUploadThread(
+    CrashReportDatabase* database,
+    const std::string& url,
+    const Options& options,
+    int max_uploads)
+    : CrashReportUploadThread(database, url, options),
+      max_uploads_(max_uploads) {}
+
+CefCrashReportUploadThread::~CefCrashReportUploadThread() {}
+
+void CefCrashReportUploadThread::ProcessPendingReports() {
+  if (BackoffPending()) {
+    // Try again later.
+    return;
+  }
+
+  if (MaxUploadsEnabled()) {
+    // Retrieve all completed reports.
+    std::vector<CrashReportDatabase::Report> reports;
+    if (database_->GetCompletedReports(&reports) !=
+        CrashReportDatabase::kNoError) {
+      // The database is sick. It might be prudent to stop trying to poke it
+      // from this thread by abandoning the thread altogether. On the other
+      // hand, if the problem is transient, it might be possible to talk to it
+      // again on the next pass. For now, take the latter approach.
+      return;
+    }
+
+    const time_t now = time(nullptr);
+    const int kSeconds = 60 * 60 * 24;  // 24 hours
+
+    // Count how many reports have completed in the last 24 hours.
+    recent_upload_ct_ = 0;
+    for (const CrashReportDatabase::Report& report : reports) {
+      if (report.last_upload_attempt_time > now - kSeconds)
+        recent_upload_ct_++;
+    }
+  }
+
+  // Continue with processing pending reports.
+  CrashReportUploadThread::ProcessPendingReports();
+}
+
+void CefCrashReportUploadThread::ProcessPendingReport(
+    const CrashReportDatabase::Report& report) {
+  // Always allow upload if it's been explicitly requested by the user.
+  if (!report.upload_explicitly_requested) {
+    if (!UploadsEnabled()) {
+      // Don’t attempt an upload if there’s no URL or if uploads have been
+      // disabled in the database’s settings.
+      database_->SkipReportUpload(
+          report.uuid, Metrics::CrashSkippedReason::kUploadsDisabled);
+      return;
+    }
+
+    if (MaxUploadsExceeded()) {
+      // Don't send uploads if the rate limit has been exceeded.
+      database_->SkipReportUpload(
+          report.uuid, Metrics::CrashSkippedReason::kUploadThrottled);
+      return;
+    }
+  }
+
+  if (BackoffPending()) {
+    // Try again later.
+    return;
+  }
+
+  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;
+  CrashReportDatabase::OperationStatus status =
+      database_->GetReportForUploading(report.uuid, &upload_report);
+  switch (status) {
+    case CrashReportDatabase::kNoError:
+      break;
+
+    case CrashReportDatabase::kBusyError:
+      return;
+
+    case CrashReportDatabase::kReportNotFound:
+    case CrashReportDatabase::kFileSystemError:
+    case CrashReportDatabase::kDatabaseError:
+      // In these cases, SkipReportUpload() might not work either, but it’s best
+      // to at least try to get the report out of the way.
+      database_->SkipReportUpload(report.uuid,
+                                  Metrics::CrashSkippedReason::kDatabaseError);
+      return;
+
+    case CrashReportDatabase::kCannotRequestUpload:
+      NOTREACHED();
+      return;
+  }
+
+  std::string response_body;
+  UploadResult upload_result =
+      UploadReport(upload_report.get(), &response_body);
+  switch (upload_result) {
+    case UploadResult::kSuccess:
+      // The upload completed successfully.
+      database_->RecordUploadComplete(std::move(upload_report), response_body);
+      if (MaxUploadsEnabled())
+        recent_upload_ct_++;
+      ResetBackoff();
+      break;
+    case UploadResult::kPermanentFailure:
+      // The upload should never be retried.
+      database_->SkipReportUpload(report.uuid,
+                                  Metrics::CrashSkippedReason::kUploadFailed);
+      break;
+    case UploadResult::kRetry:
+      // The upload will be retried after a reasonable backoff delay. Since we
+      // didn't successfully upload it we won't count it against the rate limit.
+      IncreaseBackoff();
+      break;
+  }
+}
+
+CrashReportUploadThread::ParameterMap
+CefCrashReportUploadThread::FilterParameters(const ParameterMap& parameters) {
+  return crash_report_utils::FilterParameters(parameters);
+}
+
+bool CefCrashReportUploadThread::UploadsEnabled() const {
+  Settings* const settings = database_->GetSettings();
+  bool uploads_enabled;
+  return !url_.empty() && settings->GetUploadsEnabled(&uploads_enabled) &&
+         uploads_enabled;
+}
+
+bool CefCrashReportUploadThread::MaxUploadsEnabled() const {
+  return options_.rate_limit && max_uploads_ > 0;
+}
+
+bool CefCrashReportUploadThread::MaxUploadsExceeded() const {
+  return MaxUploadsEnabled() && recent_upload_ct_ >= max_uploads_;
+}
+
+bool CefCrashReportUploadThread::BackoffPending() const {
+  if (!options_.rate_limit)
+    return false;
+
+  Settings* const settings = database_->GetSettings();
+
+  time_t next_upload_time;
+  if (settings->GetNextUploadAttemptTime(&next_upload_time) &&
+      next_upload_time > 0) {
+    const time_t now = time(nullptr);
+    if (now < next_upload_time)
+      return true;
+  }
+
+  return false;
+}
+
+void CefCrashReportUploadThread::IncreaseBackoff() {
+  if (!options_.rate_limit)
+    return;
+
+  const int kHour = 60 * 60;  // 1 hour
+  const int kBackoffSchedule[] = {
+      kHour / 4,   // 15 minutes
+      kHour,       // 1 hour
+      kHour * 2,   // 2 hours
+      kHour * 4,   // 4 hours
+      kHour * 8,   // 8 hours
+      kHour * 24,  // 24 hours
+  };
+  const int kBackoffScheduleSize =
+      sizeof(kBackoffSchedule) / sizeof(kBackoffSchedule[0]);
+
+  Settings* settings = database_->GetSettings();
+
+  int backoff_step = 0;
+  if (settings->GetBackoffStep(&backoff_step) && backoff_step < 0)
+    backoff_step = 0;
+  if (++backoff_step > kBackoffScheduleSize)
+    backoff_step = kBackoffScheduleSize;
+
+  time_t next_upload_time = time(nullptr);  // now
+  next_upload_time += kBackoffSchedule[backoff_step - 1];
+
+  settings->SetBackoffStep(backoff_step);
+  settings->SetNextUploadAttemptTime(next_upload_time);
+
+  if (max_uploads_ > 1) {
+    // If the server is having trouble then we don't want to send many crash
+    // reports after the backoff expires. Reduce max uploads to 1 per 24 hours
+    // until the client is restarted.
+    max_uploads_ = 1;
+  }
+}
+
+void CefCrashReportUploadThread::ResetBackoff() {
+  if (!options_.rate_limit)
+    return;
+
+  Settings* settings = database_->GetSettings();
+  settings->SetBackoffStep(0);
+  settings->SetNextUploadAttemptTime(0);
+}
diff --git a/src/libcef/common/cef_crash_report_upload_thread.h b/src/libcef/common/cef_crash_report_upload_thread.h
new file mode 100644
index 0000000..b3641d6
--- /dev/null
+++ b/src/libcef/common/cef_crash_report_upload_thread.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
+#define CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
+
+#include "third_party/crashpad/crashpad/handler/crash_report_upload_thread.h"
+
+class CefCrashReportUploadThread : public crashpad::CrashReportUploadThread {
+ public:
+  CefCrashReportUploadThread(crashpad::CrashReportDatabase* database,
+                             const std::string& url,
+                             const Options& options,
+                             int max_uploads);
+  ~CefCrashReportUploadThread();
+
+ private:
+  void ProcessPendingReports() override;
+  void ProcessPendingReport(
+      const crashpad::CrashReportDatabase::Report& report) override;
+  ParameterMap FilterParameters(const ParameterMap& parameters) override;
+
+  bool UploadsEnabled() const;
+
+  bool MaxUploadsEnabled() const;
+  bool MaxUploadsExceeded() const;
+
+  bool BackoffPending() const;
+  void IncreaseBackoff();
+  void ResetBackoff();
+
+  int max_uploads_;
+
+  // Track the number of uploads that have completed within the last 24 hours.
+  // Only used when RateLimitEnabled() is true. Value is reset each time
+  // ProcessPendingReports() is called.
+  int recent_upload_ct_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(CefCrashReportUploadThread);
+};
+
+#endif  // CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
diff --git a/src/libcef/common/cef_crash_report_utils.cc b/src/libcef/common/cef_crash_report_utils.cc
new file mode 100644
index 0000000..7242d7b
--- /dev/null
+++ b/src/libcef/common/cef_crash_report_utils.cc
@@ -0,0 +1,57 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/cef_crash_report_utils.h"
+
+#include "base/strings/string_split.h"
+
+namespace crash_report_utils {
+
+ParameterMap FilterParameters(const ParameterMap& parameters) {
+  ParameterMap in_map = parameters;
+
+  // Extract the key map, if any. Must match the logic in
+  // CefCrashReporterClient::ReadCrashConfigFile.
+  std::string key_map;
+  for (size_t i = 0; true; ++i) {
+    const std::string& key = "K-" + std::string(1, 'A' + i);
+    ParameterMap::iterator it = in_map.find(key);
+    if (it == in_map.end())
+      break;
+    key_map += it->second;
+    in_map.erase(it);
+  }
+
+  if (key_map.empty()) {
+    // Nothing to substitute.
+    return parameters;
+  }
+
+  // Parse |key_map|.
+  base::StringPairs kv_pairs;
+  if (!base::SplitStringIntoKeyValuePairs(key_map, '=', ',', &kv_pairs)) {
+    return parameters;
+  }
+
+  ParameterMap subs;
+  for (const auto& pairs : kv_pairs) {
+    subs.insert(std::make_pair(pairs.first, pairs.second));
+  }
+
+  ParameterMap out_map;
+
+  // Perform key substitutions.
+  for (const auto& params : in_map) {
+    std::string key = params.first;
+    ParameterMap::const_iterator subs_it = subs.find(params.first);
+    if (subs_it != subs.end()) {
+      key = subs_it->second;
+    }
+    out_map.insert(std::make_pair(key, params.second));
+  }
+
+  return out_map;
+}
+
+}  // namespace crash_report_utils
diff --git a/src/libcef/common/cef_crash_report_utils.h b/src/libcef/common/cef_crash_report_utils.h
new file mode 100644
index 0000000..ef9102e
--- /dev/null
+++ b/src/libcef/common/cef_crash_report_utils.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UTILS_H_
+#define CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UTILS_H_
+
+#include <map>
+#include <string>
+
+namespace crash_report_utils {
+
+using ParameterMap = std::map<std::string, std::string>;
+
+ParameterMap FilterParameters(const ParameterMap& parameters);
+
+}  // namespace crash_report_utils
+
+#endif  // CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UTILS_H_
diff --git a/src/libcef/common/cef_message_generator.cc b/src/libcef/common/cef_message_generator.cc
new file mode 100644
index 0000000..ee49889
--- /dev/null
+++ b/src/libcef/common/cef_message_generator.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "libcef/common/cef_message_generator.h"
+
+// Generate constructors.
+#include "chrome/common/safe_browsing/ipc_protobuf_message_null_macros.h"
+#include "ipc/struct_constructor_macros.h"
+#include "libcef/common/cef_message_generator.h"
+
+// Generate param traits write methods.
+#include "chrome/common/safe_browsing/protobuf_message_write_macros.h"
+#include "ipc/param_traits_write_macros.h"
+namespace IPC {
+#include "libcef/common/cef_message_generator.h"
+}  // namespace IPC
+
+// Generate param traits read methods.
+#include "chrome/common/safe_browsing/protobuf_message_read_macros.h"
+#include "ipc/param_traits_read_macros.h"
+namespace IPC {
+#include "libcef/common/cef_message_generator.h"
+}  // namespace IPC
+
+// Generate param traits log methods.
+#include "chrome/common/safe_browsing/protobuf_message_log_macros.h"
+#include "ipc/param_traits_log_macros.h"
+namespace IPC {
+#include "libcef/common/cef_message_generator.h"
+}  // namespace IPC
diff --git a/src/libcef/common/cef_message_generator.h b/src/libcef/common/cef_message_generator.h
new file mode 100644
index 0000000..1243611
--- /dev/null
+++ b/src/libcef/common/cef_message_generator.h
@@ -0,0 +1,7 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Multiply-included file, hence no include guard.
+
+#include "libcef/common/cef_messages.h"
diff --git a/src/libcef/common/cef_messages.cc b/src/libcef/common/cef_messages.cc
new file mode 100644
index 0000000..b2ebf63
--- /dev/null
+++ b/src/libcef/common/cef_messages.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/cef_messages.h"
+
+namespace IPC {
+
+// Extracted from chrome/common/automation_messages.cc.
+
+// Only the net::UploadData ParamTraits<> definition needs this definition, so
+// keep this in the implementation file so we can forward declare UploadData in
+// the header.
+template <>
+struct ParamTraits<net::UploadElement> {
+  typedef net::UploadElement param_type;
+  static void Write(base::Pickle* m, const param_type& p) {
+    WriteParam(m, static_cast<int>(p.type()));
+    switch (p.type()) {
+      case net::UploadElement::TYPE_BYTES: {
+        m->WriteData(p.bytes(), static_cast<int>(p.bytes_length()));
+        break;
+      }
+      default: {
+        DCHECK(p.type() == net::UploadElement::TYPE_FILE);
+        WriteParam(m, p.file_path());
+        WriteParam(m, p.file_range_offset());
+        WriteParam(m, p.file_range_length());
+        WriteParam(m, p.expected_file_modification_time());
+        break;
+      }
+    }
+  }
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r) {
+    int type;
+    if (!ReadParam(m, iter, &type))
+      return false;
+    switch (type) {
+      case net::UploadElement::TYPE_BYTES: {
+        const char* data;
+        int len;
+        if (!iter->ReadData(&data, &len))
+          return false;
+        r->SetToBytes(data, len);
+        break;
+      }
+      default: {
+        DCHECK(type == net::UploadElement::TYPE_FILE);
+        base::FilePath file_path;
+        uint64_t offset, length;
+        base::Time expected_modification_time;
+        if (!ReadParam(m, iter, &file_path))
+          return false;
+        if (!ReadParam(m, iter, &offset))
+          return false;
+        if (!ReadParam(m, iter, &length))
+          return false;
+        if (!ReadParam(m, iter, &expected_modification_time))
+          return false;
+        r->SetToFilePathRange(file_path, offset, length,
+                              expected_modification_time);
+        break;
+      }
+    }
+    return true;
+  }
+  static void Log(const param_type& p, std::string* l) {
+    l->append("<net::UploadElement>");
+  }
+};
+
+void ParamTraits<scoped_refptr<net::UploadData>>::Write(base::Pickle* m,
+                                                        const param_type& p) {
+  WriteParam(m, p.get() != nullptr);
+  if (p.get()) {
+    WriteParam(m, p->elements());
+    WriteParam(m, p->identifier());
+    WriteParam(m, p->is_chunked());
+    WriteParam(m, p->last_chunk_appended());
+  }
+}
+
+bool ParamTraits<scoped_refptr<net::UploadData>>::Read(
+    const base::Pickle* m,
+    base::PickleIterator* iter,
+    param_type* r) {
+  bool has_object;
+  if (!ReadParam(m, iter, &has_object))
+    return false;
+  if (!has_object)
+    return true;
+  net::UploadData::ElementsVector elements;
+  if (!ReadParam(m, iter, &elements))
+    return false;
+  int64_t identifier;
+  if (!ReadParam(m, iter, &identifier))
+    return false;
+  bool is_chunked = false;
+  if (!ReadParam(m, iter, &is_chunked))
+    return false;
+  bool last_chunk_appended = false;
+  if (!ReadParam(m, iter, &last_chunk_appended))
+    return false;
+  *r = new net::UploadData;
+  (*r)->swap_elements(&elements);
+  (*r)->set_identifier(identifier);
+  (*r)->set_is_chunked(is_chunked);
+  (*r)->set_last_chunk_appended(last_chunk_appended);
+  return true;
+}
+
+void ParamTraits<scoped_refptr<net::UploadData>>::Log(const param_type& p,
+                                                      std::string* l) {
+  l->append("<net::UploadData>");
+}
+
+}  // namespace IPC
diff --git a/src/libcef/common/cef_messages.h b/src/libcef/common/cef_messages.h
new file mode 100644
index 0000000..6662ba1
--- /dev/null
+++ b/src/libcef/common/cef_messages.h
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// IPC messages for CEF.
+// Multiply-included message file, hence no include guard.
+
+#include <stdint.h>
+
+#include "libcef/common/net/upload_data.h"
+
+#include "base/memory/shared_memory_mapping.h"
+#include "base/values.h"
+#include "content/public/common/common_param_traits.h"
+#include "content/public/common/referrer.h"
+#include "ipc/ipc_message_macros.h"
+#include "ui/gfx/ipc/gfx_param_traits.h"
+
+// Singly-included section for enums and custom IPC traits.
+#ifndef CEF_LIBCEF_COMMON_CEF_MESSAGES_H_
+#define CEF_LIBCEF_COMMON_CEF_MESSAGES_H_
+
+namespace IPC {
+
+// Extracted from chrome/common/automation_messages.h.
+template <>
+struct ParamTraits<scoped_refptr<net::UploadData>> {
+  typedef scoped_refptr<net::UploadData> param_type;
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r);
+  static void Log(const param_type& p, std::string* l);
+};
+
+}  // namespace IPC
+
+#endif  // CEF_LIBCEF_COMMON_CEF_MESSAGES_H_
+
+// TODO(cef): Re-using the message start for extensions may be problematic in
+// the future. It would be better if ipc_message_utils.h contained a value
+// reserved for consumers of the content API.
+// See: http://crbug.com/110911
+#define IPC_MESSAGE_START ExtensionMsgStart
+
+// Common types.
+
+// Parameters structure for a request.
+IPC_STRUCT_BEGIN(Cef_Request_Params)
+  // Unique request id to match requests and responses.
+  IPC_STRUCT_MEMBER(int, request_id)
+
+  // True if the request is user-initiated instead of internal.
+  IPC_STRUCT_MEMBER(bool, user_initiated)
+
+  // True if a response is expected.
+  IPC_STRUCT_MEMBER(bool, expect_response)
+
+  // Message name.
+  IPC_STRUCT_MEMBER(std::string, name)
+
+  // List of message arguments.
+  IPC_STRUCT_MEMBER(base::ListValue, arguments)
+IPC_STRUCT_END()
+
+// Parameters structure for a response.
+IPC_STRUCT_BEGIN(Cef_Response_Params)
+  // Unique request id to match requests and responses.
+  IPC_STRUCT_MEMBER(int, request_id)
+
+  // True if a response ack is expected.
+  IPC_STRUCT_MEMBER(bool, expect_response_ack)
+
+  // True on success.
+  IPC_STRUCT_MEMBER(bool, success)
+
+  // Response or error string depending on the value of |success|.
+  IPC_STRUCT_MEMBER(std::string, response)
+IPC_STRUCT_END()
+
+// Parameters structure for a cross-origin white list entry.
+IPC_STRUCT_BEGIN(Cef_CrossOriginWhiteListEntry_Params)
+  IPC_STRUCT_MEMBER(std::string, source_origin)
+  IPC_STRUCT_MEMBER(std::string, target_protocol)
+  IPC_STRUCT_MEMBER(std::string, target_domain)
+  IPC_STRUCT_MEMBER(bool, allow_target_subdomains)
+IPC_STRUCT_END()
+
+// Parameters structure for a draggable region.
+IPC_STRUCT_BEGIN(Cef_DraggableRegion_Params)
+  IPC_STRUCT_MEMBER(gfx::Rect, bounds)
+  IPC_STRUCT_MEMBER(bool, draggable)
+IPC_STRUCT_END()
+
+// Messages sent from the browser to the renderer.
+
+// Parameters for a resource request.
+IPC_STRUCT_BEGIN(CefMsg_LoadRequest_Params)
+  // The request method: GET, POST, etc.
+  IPC_STRUCT_MEMBER(std::string, method)
+
+  // The requested URL.
+  IPC_STRUCT_MEMBER(GURL, url)
+
+  // The URL to send in the "Referer" header field. Can be empty if there is
+  // no referrer.
+  IPC_STRUCT_MEMBER(GURL, referrer)
+  // One of the cef_referrer_policy_t values.
+  IPC_STRUCT_MEMBER(int, referrer_policy)
+
+  // Usually the URL of the document in the top-level window, which may be
+  // checked by the third-party cookie blocking policy. Leaving it empty may
+  // lead to undesired cookie blocking. Third-party cookie blocking can be
+  // bypassed by setting site_for_cookies = url, but this should ideally
+  // only be done if there really is no way to determine the correct value.
+  IPC_STRUCT_MEMBER(net::SiteForCookies, site_for_cookies)
+
+  // Additional HTTP request headers.
+  IPC_STRUCT_MEMBER(std::string, headers)
+
+  // net::URLRequest load flags (0 by default).
+  IPC_STRUCT_MEMBER(int, load_flags)
+
+  // Optional upload data (may be null).
+  IPC_STRUCT_MEMBER(scoped_refptr<net::UploadData>, upload_data)
+IPC_STRUCT_END()
+
+// Tell the renderer to load a request.
+IPC_MESSAGE_ROUTED1(CefMsg_LoadRequest, CefMsg_LoadRequest_Params)
+
+// Sent when the browser has a request for the renderer. The renderer may
+// respond with a CefHostMsg_Response.
+IPC_MESSAGE_ROUTED1(CefMsg_Request, Cef_Request_Params)
+
+// Optional message sent in response to a CefHostMsg_Request.
+IPC_MESSAGE_ROUTED1(CefMsg_Response, Cef_Response_Params)
+
+// Optional Ack message sent to the browser to notify that a CefHostMsg_Response
+// has been processed.
+IPC_MESSAGE_ROUTED1(CefMsg_ResponseAck, int /* request_id */)
+
+// Tells the renderer that loading has stopped.
+IPC_MESSAGE_ROUTED0(CefMsg_DidStopLoading)
+
+// Tells the render frame to load all blocked plugins with the given identifier.
+// Based on ChromeViewMsg_LoadBlockedPlugins.
+IPC_MESSAGE_ROUTED1(CefViewMsg_LoadBlockedPlugins, std::string /* identifier */)
+
+// Sent to child processes to add or remove a cross-origin whitelist entry.
+IPC_MESSAGE_CONTROL2(CefProcessMsg_ModifyCrossOriginWhitelistEntry,
+                     bool /* add */,
+                     Cef_CrossOriginWhiteListEntry_Params /* params */)
+
+// Sent to child processes to clear the cross-origin whitelist.
+IPC_MESSAGE_CONTROL0(CefProcessMsg_ClearCrossOriginWhitelist)
+
+// Messages sent from the renderer to the browser.
+
+// Parameters for a newly created render thread.
+IPC_STRUCT_BEGIN(CefProcessHostMsg_GetNewRenderThreadInfo_Params)
+  IPC_STRUCT_MEMBER(std::vector<Cef_CrossOriginWhiteListEntry_Params>,
+                    cross_origin_whitelist_entries)
+
+  IPC_STRUCT_MEMBER(base::ListValue, extra_info)
+IPC_STRUCT_END()
+
+// Retrieve information about a newly created render thread.
+IPC_SYNC_MESSAGE_CONTROL0_1(
+    CefProcessHostMsg_GetNewRenderThreadInfo,
+    CefProcessHostMsg_GetNewRenderThreadInfo_Params /* params*/)
+
+// Parameters for a newly created browser window.
+IPC_STRUCT_BEGIN(CefProcessHostMsg_GetNewBrowserInfo_Params)
+  IPC_STRUCT_MEMBER(int, browser_id)
+  IPC_STRUCT_MEMBER(bool, is_popup)
+  IPC_STRUCT_MEMBER(bool, is_windowless)
+  IPC_STRUCT_MEMBER(bool, is_guest_view)
+  IPC_STRUCT_MEMBER(base::DictionaryValue, extra_info)
+IPC_STRUCT_END()
+
+// Retrieve information about a newly created browser.
+IPC_SYNC_MESSAGE_CONTROL1_1(
+    CefProcessHostMsg_GetNewBrowserInfo,
+    int /* render_frame_routing_id */,
+    CefProcessHostMsg_GetNewBrowserInfo_Params /* params*/)
+
+// Sent by the renderer when the frame can begin receiving messages.
+IPC_MESSAGE_ROUTED0(CefHostMsg_FrameAttached)
+
+// Sent when a frame has finished loading. Based on ViewHostMsg_DidFinishLoad.
+IPC_MESSAGE_ROUTED2(CefHostMsg_DidFinishLoad,
+                    GURL /* validated_url */,
+                    int /* http_status_code */)
+
+// Sent when the renderer has a request for the browser. The browser may respond
+// with a CefMsg_Response.
+IPC_MESSAGE_ROUTED1(CefHostMsg_Request, Cef_Request_Params)
+
+// Optional message sent in response to a CefMsg_Request.
+IPC_MESSAGE_ROUTED1(CefHostMsg_Response, Cef_Response_Params)
+
+// Optional Ack message sent to the browser to notify that a CefMsg_Response
+// has been processed.
+IPC_MESSAGE_ROUTED1(CefHostMsg_ResponseAck, int /* request_id */)
+
+// Sent by the renderer when the draggable regions are updated.
+IPC_MESSAGE_ROUTED1(CefHostMsg_UpdateDraggableRegions,
+                    std::vector<Cef_DraggableRegion_Params> /* regions */)
diff --git a/src/libcef/common/cef_switches.cc b/src/libcef/common/cef_switches.cc
new file mode 100644
index 0000000..1e56b3c
--- /dev/null
+++ b/src/libcef/common/cef_switches.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/common/cef_switches.h"
+
+namespace switches {
+
+// Severity of messages to log.
+const char kLogSeverity[] = "log-severity";
+const char kLogSeverity_Verbose[] = "verbose";
+const char kLogSeverity_Info[] = "info";
+const char kLogSeverity_Warning[] = "warning";
+const char kLogSeverity_Error[] = "error";
+const char kLogSeverity_Fatal[] = "fatal";
+const char kLogSeverity_Disable[] = "disable";
+
+// Path to resources directory.
+const char kResourcesDirPath[] = "resources-dir-path";
+
+// Path to locales directory.
+const char kLocalesDirPath[] = "locales-dir-path";
+
+// Path to locales directory.
+const char kDisablePackLoading[] = "disable-pack-loading";
+
+// Stack size for uncaught exceptions.
+const char kUncaughtExceptionStackSize[] = "uncaught-exception-stack-size";
+
+// Default encoding.
+const char kDefaultEncoding[] = "default-encoding";
+
+// Disable JavaScript.
+const char kDisableJavascript[] = "disable-javascript";
+
+// Disable closing of windows via JavaScript.
+const char kDisableJavascriptCloseWindows[] =
+    "disable-javascript-close-windows";
+
+// Disable clipboard access via JavaScript.
+const char kDisableJavascriptAccessClipboard[] =
+    "disable-javascript-access-clipboard";
+
+// Disable DOM paste via JavaScript execCommand("paste").
+const char kDisableJavascriptDomPaste[] = "disable-javascript-dom-paste";
+
+// Allow universal access from file URLs.
+const char kAllowUniversalAccessFromFileUrls[] =
+    "allow-universal-access-from-files";
+
+// Disable loading of images from the network. A cached image will still be
+// rendered if requested.
+const char kDisableImageLoading[] = "disable-image-loading";
+
+// Shrink stand-alone images to fit.
+const char kImageShrinkStandaloneToFit[] = "image-shrink-standalone-to-fit";
+
+// Disable resizing of text areas.
+const char kDisableTextAreaResize[] = "disable-text-area-resize";
+
+// Disable using the tab key to advance focus to links.
+const char kDisableTabToLinks[] = "disable-tab-to-links";
+
+// Disable plugins.
+const char kDisablePlugins[] = "disable-plugins";
+
+// Persist session cookies.
+const char kPersistSessionCookies[] = "persist-session-cookies";
+
+// Persist user preferences.
+const char kPersistUserPreferences[] = "persist-user-preferences";
+
+// Enable media (WebRTC audio/video) streaming.
+const char kEnableMediaStream[] = "enable-media-stream";
+
+// Enable speech input (x-webkit-speech).
+const char kEnableSpeechInput[] = "enable-speech-input";
+
+// Enable the speech input profanity filter.
+const char kEnableProfanityFilter[] = "enable-profanity-filter";
+
+// Disable spell checking.
+const char kDisableSpellChecking[] = "disable-spell-checking";
+
+// Enable the remote spelling service.
+const char kEnableSpellingService[] = "enable-spelling-service";
+
+// Override the default spellchecking language which comes from locales.pak.
+const char kOverrideSpellCheckLang[] = "override-spell-check-lang";
+
+// Enable detection and use of a system-wide Pepper Flash install.
+const char kEnableSystemFlash[] = "enable-system-flash";
+
+// Disable scroll bounce (rubber-banding) on OS X Lion and newer.
+const char kDisableScrollBounce[] = "disable-scroll-bounce";
+
+// Disable the PDF extension.
+const char kDisablePdfExtension[] = "disable-pdf-extension";
+
+// Path to Widevine CDM binaries.
+const char kWidevineCdmPath[] = "widevine-cdm-path";
+
+// Default plugin policy action.
+const char kPluginPolicy[] = "plugin-policy";
+// Allow the content. This is the default value.
+const char kPluginPolicy_Allow[] = "allow";
+// Allow important content and block unimportant content based on heuristics.
+// The user can manually load blocked content.
+const char kPluginPolicy_Detect[] = "detect";
+// Block the content. The user can manually load blocked content.
+const char kPluginPolicy_Block[] = "block";
+
+// Expose preferences used only by unit tests.
+const char kEnablePreferenceTesting[] = "enable-preference-testing";
+
+// Enable print preview.
+const char kEnablePrintPreview[] = "enable-print-preview";
+
+// Disable the timeout for delivering new browser info to the renderer process.
+const char kDisableNewBrowserInfoTimeout[] = "disable-new-browser-info-timeout";
+
+// File used for logging DevTools protocol messages.
+const char kDevToolsProtocolLogFile[] = "devtools-protocol-log-file";
+
+#if defined(OS_MACOSX)
+// Path to the framework directory.
+const char kFrameworkDirPath[] = "framework-dir-path";
+const char kMainBundlePath[] = "main-bundle-path";
+#endif
+
+}  // namespace switches
diff --git a/src/libcef/common/cef_switches.h b/src/libcef/common/cef_switches.h
new file mode 100644
index 0000000..2731f77
--- /dev/null
+++ b/src/libcef/common/cef_switches.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+// Defines all the "cef" command-line switches.
+
+#ifndef CEF_LIBCEF_COMMON_CEF_SWITCHES_H_
+#define CEF_LIBCEF_COMMON_CEF_SWITCHES_H_
+#pragma once
+
+#include "build/build_config.h"
+
+namespace switches {
+
+extern const char kLogSeverity[];
+extern const char kLogSeverity_Verbose[];
+extern const char kLogSeverity_Info[];
+extern const char kLogSeverity_Warning[];
+extern const char kLogSeverity_Error[];
+extern const char kLogSeverity_Fatal[];
+extern const char kLogSeverity_Disable[];
+extern const char kResourcesDirPath[];
+extern const char kLocalesDirPath[];
+extern const char kDisablePackLoading[];
+extern const char kUncaughtExceptionStackSize[];
+extern const char kDefaultEncoding[];
+extern const char kDisableJavascript[];
+extern const char kDisableJavascriptCloseWindows[];
+extern const char kDisableJavascriptAccessClipboard[];
+extern const char kDisableJavascriptDomPaste[];
+extern const char kAllowUniversalAccessFromFileUrls[];
+extern const char kDisableImageLoading[];
+extern const char kImageShrinkStandaloneToFit[];
+extern const char kDisableTextAreaResize[];
+extern const char kDisableTabToLinks[];
+extern const char kDisablePlugins[];
+extern const char kPersistSessionCookies[];
+extern const char kPersistUserPreferences[];
+extern const char kEnableMediaStream[];
+extern const char kEnableSpeechInput[];
+extern const char kEnableProfanityFilter[];
+extern const char kDisableSpellChecking[];
+extern const char kEnableSpellingService[];
+extern const char kOverrideSpellCheckLang[];
+extern const char kEnableSystemFlash[];
+extern const char kDisableScrollBounce[];
+extern const char kDisablePdfExtension[];
+extern const char kWidevineCdmPath[];
+extern const char kPluginPolicy[];
+extern const char kPluginPolicy_Allow[];
+extern const char kPluginPolicy_Detect[];
+extern const char kPluginPolicy_Block[];
+extern const char kEnablePreferenceTesting[];
+extern const char kEnablePrintPreview[];
+extern const char kDisableNewBrowserInfoTimeout[];
+extern const char kDevToolsProtocolLogFile[];
+
+#if defined(OS_MACOSX)
+extern const char kFrameworkDirPath[];
+extern const char kMainBundlePath[];
+#endif
+
+}  // namespace switches
+
+#endif  // CEF_LIBCEF_COMMON_CEF_SWITCHES_H_
diff --git a/src/libcef/common/command_line_impl.cc b/src/libcef/common/command_line_impl.cc
new file mode 100644
index 0000000..d44dbda
--- /dev/null
+++ b/src/libcef/common/command_line_impl.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/command_line_impl.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+
+CefCommandLineImpl::CefCommandLineImpl(base::CommandLine* value,
+                                       bool will_delete,
+                                       bool read_only)
+    : CefValueBase<CefCommandLine, base::CommandLine>(
+          value,
+          nullptr,
+          will_delete ? kOwnerWillDelete : kOwnerNoDelete,
+          read_only,
+          nullptr) {}
+
+bool CefCommandLineImpl::IsValid() {
+  return !detached();
+}
+
+bool CefCommandLineImpl::IsReadOnly() {
+  return read_only();
+}
+
+CefRefPtr<CefCommandLine> CefCommandLineImpl::Copy() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  return new CefCommandLineImpl(new base::CommandLine(const_value().argv()),
+                                true, false);
+}
+
+void CefCommandLineImpl::InitFromArgv(int argc, const char* const* argv) {
+#if !defined(OS_WIN)
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->InitFromArgv(argc, argv);
+#else
+  NOTREACHED() << "method not supported on this platform";
+#endif
+}
+
+void CefCommandLineImpl::InitFromString(const CefString& command_line) {
+#if defined(OS_WIN)
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  const base::string16& str16 = command_line;
+  mutable_value()->ParseFromString(str16);
+#else
+  NOTREACHED() << "method not supported on this platform";
+#endif
+}
+
+void CefCommandLineImpl::Reset() {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  base::CommandLine::StringVector argv;
+  argv.push_back(mutable_value()->GetProgram().value());
+  mutable_value()->InitFromArgv(argv);
+
+  const base::CommandLine::SwitchMap& map = mutable_value()->GetSwitches();
+  const_cast<base::CommandLine::SwitchMap*>(&map)->clear();
+}
+
+void CefCommandLineImpl::GetArgv(std::vector<CefString>& argv) {
+  CEF_VALUE_VERIFY_RETURN_VOID(false);
+  const base::CommandLine::StringVector& cmd_argv = const_value().argv();
+  base::CommandLine::StringVector::const_iterator it = cmd_argv.begin();
+  for (; it != cmd_argv.end(); ++it)
+    argv.push_back(*it);
+}
+
+CefString CefCommandLineImpl::GetCommandLineString() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetCommandLineString();
+}
+
+CefString CefCommandLineImpl::GetProgram() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetProgram().value();
+}
+
+void CefCommandLineImpl::SetProgram(const CefString& program) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->SetProgram(base::FilePath(program));
+}
+
+bool CefCommandLineImpl::HasSwitches() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return (const_value().GetSwitches().size() > 0);
+}
+
+bool CefCommandLineImpl::HasSwitch(const CefString& name) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().HasSwitch(name.ToString());
+}
+
+CefString CefCommandLineImpl::GetSwitchValue(const CefString& name) {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().GetSwitchValueNative(name.ToString());
+}
+
+void CefCommandLineImpl::GetSwitches(SwitchMap& switches) {
+  CEF_VALUE_VERIFY_RETURN_VOID(false);
+  const base::CommandLine::SwitchMap& map = const_value().GetSwitches();
+  base::CommandLine::SwitchMap::const_iterator it = map.begin();
+  for (; it != map.end(); ++it)
+    switches.insert(std::make_pair(it->first, it->second));
+}
+
+void CefCommandLineImpl::AppendSwitch(const CefString& name) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->AppendSwitch(name);
+}
+
+void CefCommandLineImpl::AppendSwitchWithValue(const CefString& name,
+                                               const CefString& value) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->AppendSwitchNative(name, value);
+}
+
+bool CefCommandLineImpl::HasArguments() {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return (const_value().GetArgs().size() > 0);
+}
+
+void CefCommandLineImpl::GetArguments(ArgumentList& arguments) {
+  CEF_VALUE_VERIFY_RETURN_VOID(false);
+  const base::CommandLine::StringVector& vec = const_value().GetArgs();
+  base::CommandLine::StringVector::const_iterator it = vec.begin();
+  for (; it != vec.end(); ++it)
+    arguments.push_back(*it);
+}
+
+void CefCommandLineImpl::AppendArgument(const CefString& argument) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->AppendArgNative(argument);
+}
+
+void CefCommandLineImpl::PrependWrapper(const CefString& wrapper) {
+  CEF_VALUE_VERIFY_RETURN_VOID(true);
+  mutable_value()->PrependWrapper(wrapper);
+}
+
+// CefCommandLine implementation.
+
+// static
+CefRefPtr<CefCommandLine> CefCommandLine::CreateCommandLine() {
+  return new CefCommandLineImpl(
+      new base::CommandLine(base::CommandLine::NO_PROGRAM), true, false);
+}
+
+// static
+CefRefPtr<CefCommandLine> CefCommandLine::GetGlobalCommandLine() {
+  // Uses a singleton reference object.
+  static CefRefPtr<CefCommandLineImpl> commandLinePtr;
+  if (!commandLinePtr.get()) {
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    if (command_line)
+      commandLinePtr = new CefCommandLineImpl(command_line, false, true);
+  }
+  return commandLinePtr.get();
+}
diff --git a/src/libcef/common/command_line_impl.h b/src/libcef/common/command_line_impl.h
new file mode 100644
index 0000000..ed4761a
--- /dev/null
+++ b/src/libcef/common/command_line_impl.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_COMMAND_LINE_IMPL_H_
+#define CEF_LIBCEF_COMMON_COMMAND_LINE_IMPL_H_
+#pragma once
+
+#include "include/cef_command_line.h"
+#include "libcef/common/value_base.h"
+
+#include "base/command_line.h"
+
+// CefCommandLine implementation
+class CefCommandLineImpl
+    : public CefValueBase<CefCommandLine, base::CommandLine> {
+ public:
+  CefCommandLineImpl(base::CommandLine* value,
+                     bool will_delete,
+                     bool read_only);
+
+  // CefCommandLine methods.
+  bool IsValid() override;
+  bool IsReadOnly() override;
+  CefRefPtr<CefCommandLine> Copy() override;
+  void InitFromArgv(int argc, const char* const* argv) override;
+  void InitFromString(const CefString& command_line) override;
+  void Reset() override;
+  void GetArgv(std::vector<CefString>& argv) override;
+  CefString GetCommandLineString() override;
+  CefString GetProgram() override;
+  void SetProgram(const CefString& program) override;
+  bool HasSwitches() override;
+  bool HasSwitch(const CefString& name) override;
+  CefString GetSwitchValue(const CefString& name) override;
+  void GetSwitches(SwitchMap& switches) override;
+  void AppendSwitch(const CefString& name) override;
+  void AppendSwitchWithValue(const CefString& name,
+                             const CefString& value) override;
+  bool HasArguments() override;
+  void GetArguments(ArgumentList& arguments) override;
+  void AppendArgument(const CefString& argument) override;
+  void PrependWrapper(const CefString& wrapper) override;
+
+  // Must hold the controller lock while using this value.
+  const base::CommandLine& command_line() { return const_value(); }
+
+  DISALLOW_COPY_AND_ASSIGN(CefCommandLineImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_COMMAND_LINE_IMPL_H_
diff --git a/src/libcef/common/content_client.cc b/src/libcef/common/content_client.cc
new file mode 100644
index 0000000..99e8535
--- /dev/null
+++ b/src/libcef/common/content_client.cc
@@ -0,0 +1,336 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/content_client.h"
+
+#include <stdint.h>
+
+#include "include/cef_stream.h"
+#include "include/cef_version.h"
+#include "libcef/browser/extensions/pdf_extension_util.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/common/net/scheme_registration.h"
+#include "libcef/common/scheme_registrar_impl.h"
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pepper_flash.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/common/content_constants.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/pepper_plugin_info.h"
+#include "ppapi/shared_impl/ppapi_permissions.h"
+#include "third_party/widevine/cdm/buildflags.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_LINUX)
+#include "libcef/common/widevine_loader.h"
+#endif
+
+namespace {
+
+CefContentClient* g_content_client = nullptr;
+
+// The following plugin-related methods are from
+// chrome/common/chrome_content_client.cc
+
+const char kPDFPluginExtension[] = "pdf";
+const char kPDFPluginDescription[] = "Portable Document Format";
+const char kPDFPluginOutOfProcessMimeType[] = "application/x-google-chrome-pdf";
+const uint32_t kPDFPluginPermissions =
+    ppapi::PERMISSION_PDF | ppapi::PERMISSION_DEV;
+
+content::PepperPluginInfo::GetInterfaceFunc g_pdf_get_interface;
+content::PepperPluginInfo::PPP_InitializeModuleFunc g_pdf_initialize_module;
+content::PepperPluginInfo::PPP_ShutdownModuleFunc g_pdf_shutdown_module;
+
+// Appends the known built-in plugins to the given vector. Some built-in
+// plugins are "internal" which means they are compiled into the Chrome binary,
+// and some are extra shared libraries distributed with the browser (these are
+// not marked internal, aside from being automatically registered, they're just
+// regular plugins).
+void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) {
+  if (extensions::PdfExtensionEnabled()) {
+    content::PepperPluginInfo pdf_info;
+    pdf_info.is_internal = true;
+    pdf_info.is_out_of_process = true;
+    pdf_info.name = extensions::pdf_extension_util::kPdfPluginName;
+    pdf_info.description = kPDFPluginDescription;
+    pdf_info.path =
+        base::FilePath::FromUTF8Unsafe(CefContentClient::kPDFPluginPath);
+    content::WebPluginMimeType pdf_mime_type(kPDFPluginOutOfProcessMimeType,
+                                             kPDFPluginExtension,
+                                             kPDFPluginDescription);
+    pdf_info.mime_types.push_back(pdf_mime_type);
+    pdf_info.internal_entry_points.get_interface = g_pdf_get_interface;
+    pdf_info.internal_entry_points.initialize_module = g_pdf_initialize_module;
+    pdf_info.internal_entry_points.shutdown_module = g_pdf_shutdown_module;
+    pdf_info.permissions = kPDFPluginPermissions;
+    plugins->push_back(pdf_info);
+  }
+}
+
+content::PepperPluginInfo CreatePepperFlashInfo(const base::FilePath& path,
+                                                const std::string& version) {
+  content::PepperPluginInfo plugin;
+
+  plugin.is_out_of_process = true;
+  plugin.name = content::kFlashPluginName;
+  plugin.path = path;
+  plugin.permissions = kPepperFlashPermissions;
+
+  std::vector<std::string> flash_version_numbers = base::SplitString(
+      version, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  if (flash_version_numbers.size() < 1)
+    flash_version_numbers.push_back("11");
+  if (flash_version_numbers.size() < 2)
+    flash_version_numbers.push_back("2");
+  if (flash_version_numbers.size() < 3)
+    flash_version_numbers.push_back("999");
+  if (flash_version_numbers.size() < 4)
+    flash_version_numbers.push_back("999");
+  // E.g., "Shockwave Flash 10.2 r154":
+  plugin.description = plugin.name + " " + flash_version_numbers[0] + "." +
+                       flash_version_numbers[1] + " r" +
+                       flash_version_numbers[2];
+  plugin.version = base::JoinString(flash_version_numbers, ".");
+  content::WebPluginMimeType swf_mime_type(content::kFlashPluginSwfMimeType,
+                                           content::kFlashPluginSwfExtension,
+                                           content::kFlashPluginSwfDescription);
+  plugin.mime_types.push_back(swf_mime_type);
+  content::WebPluginMimeType spl_mime_type(content::kFlashPluginSplMimeType,
+                                           content::kFlashPluginSplExtension,
+                                           content::kFlashPluginSplDescription);
+  plugin.mime_types.push_back(spl_mime_type);
+
+  return plugin;
+}
+
+void AddPepperFlashFromCommandLine(
+    std::vector<content::PepperPluginInfo>* plugins) {
+  const base::CommandLine::StringType flash_path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
+          switches::kPpapiFlashPath);
+  if (flash_path.empty())
+    return;
+
+  // Also get the version from the command-line. Should be something like 11.2
+  // or 11.2.123.45.
+  std::string flash_version =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kPpapiFlashVersion);
+
+  plugins->push_back(
+      CreatePepperFlashInfo(base::FilePath(flash_path), flash_version));
+}
+
+bool GetSystemPepperFlash(content::PepperPluginInfo* plugin) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+  if (!command_line->HasSwitch(switches::kEnableSystemFlash))
+    return false;
+
+  // Do not try and find System Pepper Flash if there is a specific path on
+  // the commmand-line.
+  if (command_line->HasSwitch(switches::kPpapiFlashPath))
+    return false;
+
+  base::FilePath flash_filename;
+  if (!base::PathService::Get(chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN,
+                              &flash_filename)) {
+    return false;
+  }
+
+  base::FilePath manifest_path(
+      flash_filename.DirName().AppendASCII("manifest.json"));
+
+  std::string manifest_data;
+  if (!base::ReadFileToString(manifest_path, &manifest_data))
+    return false;
+  auto json_manifest_value =
+      base::JSONReader::Read(manifest_data, base::JSON_ALLOW_TRAILING_COMMAS);
+  if (!json_manifest_value.has_value())
+    return false;
+  std::unique_ptr<base::Value> manifest_value(
+      base::Value::ToUniquePtrValue(std::move(json_manifest_value.value())));
+  if (!manifest_value.get())
+    return false;
+  base::DictionaryValue* manifest = nullptr;
+  if (!manifest_value->GetAsDictionary(&manifest))
+    return false;
+
+  base::Version version;
+  if (!CheckPepperFlashManifest(*manifest, &version))
+    return false;
+
+  *plugin = CreatePepperFlashInfo(flash_filename, version.GetString());
+  return true;
+}
+
+}  // namespace
+
+const char CefContentClient::kPDFPluginPath[] = "internal-pdf-viewer";
+
+CefContentClient::CefContentClient(CefRefPtr<CefApp> application)
+    : application_(application),
+      pack_loading_disabled_(false),
+      allow_pack_file_load_(false),
+      scheme_info_list_locked_(false),
+      resource_bundle_delegate_(this) {
+  DCHECK(!g_content_client);
+  g_content_client = this;
+}
+
+CefContentClient::~CefContentClient() {
+  g_content_client = nullptr;
+}
+
+// static
+CefContentClient* CefContentClient::Get() {
+  return g_content_client;
+}
+
+void CefContentClient::AddPepperPlugins(
+    std::vector<content::PepperPluginInfo>* plugins) {
+  ComputeBuiltInPlugins(plugins);
+  AddPepperFlashFromCommandLine(plugins);
+
+  content::PepperPluginInfo plugin;
+  if (GetSystemPepperFlash(&plugin))
+    plugins->push_back(plugin);
+}
+
+void CefContentClient::AddContentDecryptionModules(
+    std::vector<content::CdmInfo>* cdms,
+    std::vector<media::CdmHostFilePath>* cdm_host_file_paths) {
+#if defined(OS_LINUX)
+#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+  CefWidevineLoader::AddContentDecryptionModules(cdms, cdm_host_file_paths);
+#endif
+#endif
+}
+
+void CefContentClient::AddAdditionalSchemes(Schemes* schemes) {
+  DCHECK(!scheme_info_list_locked_);
+
+  if (application_.get()) {
+    CefSchemeRegistrarImpl schemeRegistrar;
+    application_->OnRegisterCustomSchemes(&schemeRegistrar);
+    schemeRegistrar.GetSchemes(schemes);
+  }
+
+  scheme::AddInternalSchemes(schemes);
+
+  scheme_info_list_locked_ = true;
+}
+
+base::string16 CefContentClient::GetLocalizedString(int message_id) {
+  base::string16 value =
+      ui::ResourceBundle::GetSharedInstance().GetLocalizedString(message_id);
+  if (value.empty())
+    LOG(ERROR) << "No localized string available for id " << message_id;
+
+  return value;
+}
+
+base::string16 CefContentClient::GetLocalizedString(
+    int message_id,
+    const base::string16& replacement) {
+  base::string16 value = l10n_util::GetStringFUTF16(message_id, replacement);
+  if (value.empty())
+    LOG(ERROR) << "No localized string available for id " << message_id;
+
+  return value;
+}
+
+base::StringPiece CefContentClient::GetDataResource(
+    int resource_id,
+    ui::ScaleFactor scale_factor) {
+  base::StringPiece value =
+      ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
+          resource_id, scale_factor);
+  if (value.empty())
+    LOG(ERROR) << "No data resource available for id " << resource_id;
+
+  return value;
+}
+
+base::RefCountedMemory* CefContentClient::GetDataResourceBytes(
+    int resource_id) {
+  base::RefCountedMemory* value =
+      ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
+          resource_id);
+  if (!value)
+    LOG(ERROR) << "No data resource bytes available for id " << resource_id;
+
+  return value;
+}
+
+gfx::Image& CefContentClient::GetNativeImageNamed(int resource_id) {
+  gfx::Image& value =
+      ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id);
+  if (value.IsEmpty())
+    LOG(ERROR) << "No native image available for id " << resource_id;
+
+  return value;
+}
+
+void CefContentClient::AddCustomScheme(const SchemeInfo& scheme_info) {
+  DCHECK(!scheme_info_list_locked_);
+  scheme_info_list_.push_back(scheme_info);
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kProcessType)) {
+    // Register as a Web-safe scheme in the browser process so that requests for
+    // the scheme from a render process will be allowed in
+    // resource_dispatcher_host_impl.cc ShouldServiceRequest.
+    content::ChildProcessSecurityPolicy* policy =
+        content::ChildProcessSecurityPolicy::GetInstance();
+    if (!policy->IsWebSafeScheme(scheme_info.scheme_name))
+      policy->RegisterWebSafeScheme(scheme_info.scheme_name);
+  }
+}
+
+const CefContentClient::SchemeInfoList* CefContentClient::GetCustomSchemes() {
+  DCHECK(scheme_info_list_locked_);
+  return &scheme_info_list_;
+}
+
+bool CefContentClient::HasCustomScheme(const std::string& scheme_name) {
+  DCHECK(scheme_info_list_locked_);
+  if (scheme_info_list_.empty())
+    return false;
+
+  SchemeInfoList::const_iterator it = scheme_info_list_.begin();
+  for (; it != scheme_info_list_.end(); ++it) {
+    if (it->scheme_name == scheme_name)
+      return true;
+  }
+
+  return false;
+}
+
+// static
+void CefContentClient::SetPDFEntryFunctions(
+    content::PepperPluginInfo::GetInterfaceFunc get_interface,
+    content::PepperPluginInfo::PPP_InitializeModuleFunc initialize_module,
+    content::PepperPluginInfo::PPP_ShutdownModuleFunc shutdown_module) {
+  g_pdf_get_interface = get_interface;
+  g_pdf_initialize_module = initialize_module;
+  g_pdf_shutdown_module = shutdown_module;
+}
diff --git a/src/libcef/common/content_client.h b/src/libcef/common/content_client.h
new file mode 100644
index 0000000..81963e1
--- /dev/null
+++ b/src/libcef/common/content_client.h
@@ -0,0 +1,126 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_CONTENT_CLIENT_H_
+#define CEF_LIBCEF_COMMON_CONTENT_CLIENT_H_
+#pragma once
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "include/cef_app.h"
+#include "libcef/common/resource_bundle_delegate.h"
+
+#include "base/compiler_specific.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/pepper_plugin_info.h"
+#include "url/url_util.h"
+
+class CefContentClient : public content::ContentClient {
+ public:
+  static const char kPDFPluginPath[];
+
+  explicit CefContentClient(CefRefPtr<CefApp> application);
+  ~CefContentClient() override;
+
+  // Returns the singleton CefContentClient instance.
+  static CefContentClient* Get();
+
+  // content::ContentClient methods.
+  void AddPepperPlugins(
+      std::vector<content::PepperPluginInfo>* plugins) override;
+  void AddContentDecryptionModules(
+      std::vector<content::CdmInfo>* cdms,
+      std::vector<media::CdmHostFilePath>* cdm_host_file_paths) override;
+  void AddAdditionalSchemes(Schemes* schemes) override;
+  base::string16 GetLocalizedString(int message_id) override;
+  base::string16 GetLocalizedString(int message_id,
+                                    const base::string16& replacement) override;
+  base::StringPiece GetDataResource(int resource_id,
+                                    ui::ScaleFactor scale_factor) override;
+  base::RefCountedMemory* GetDataResourceBytes(int resource_id) override;
+  gfx::Image& GetNativeImageNamed(int resource_id) override;
+
+  // Values are registered with all processes (url/url_util.h) and with Blink
+  // (SchemeRegistry) unless otherwise indicated.
+  struct SchemeInfo {
+    // Lower-case ASCII scheme name.
+    std::string scheme_name;
+
+    // A scheme that is subject to URL canonicalization and parsing rules as
+    // defined in the Common Internet Scheme Syntax RFC 1738 Section 3.1
+    // available at http://www.ietf.org/rfc/rfc1738.txt.
+    // This value is not registered with Blink.
+    bool is_standard;
+
+    // A scheme that will be treated the same as "file". For example, normal
+    // pages cannot link to or access URLs of this scheme.
+    bool is_local;
+
+    // A scheme that can only be displayed from other content hosted with the
+    // same scheme. For example, pages in other origins cannot create iframes or
+    // hyperlinks to URLs with the scheme. For schemes that must be accessible
+    // from other schemes set this value to false, set |is_cors_enabled| to
+    // true, and use CORS "Access-Control-Allow-Origin" headers to further
+    // restrict access.
+    // This value is registered with Blink only.
+    bool is_display_isolated;
+
+    // A scheme that will be treated the same as "https". For example, loading
+    // this scheme from other secure schemes will not trigger mixed content
+    // warnings.
+    bool is_secure;
+
+    // A scheme that can be sent CORS requests. This value should be true in
+    // most cases where |is_standard| is true.
+    bool is_cors_enabled;
+
+    // A scheme that can bypass Content-Security-Policy (CSP) checks. This value
+    // should be false in most cases where |is_standard| is true.
+    bool is_csp_bypassing;
+
+    // A scheme that can perform fetch request.
+    bool is_fetch_enabled;
+  };
+  typedef std::list<SchemeInfo> SchemeInfoList;
+
+  // Custom scheme information will be registered first with all processes
+  // (url/url_util.h) via CefContentClient::AddAdditionalSchemes which calls
+  // AddCustomScheme, and second with Blink (SchemeRegistry) via
+  // CefContentRendererClient::WebKitInitialized which calls GetCustomSchemes.
+  void AddCustomScheme(const SchemeInfo& scheme_info);
+  const SchemeInfoList* GetCustomSchemes();
+  bool HasCustomScheme(const std::string& scheme_name);
+
+  CefRefPtr<CefApp> application() const { return application_; }
+
+  void set_pack_loading_disabled(bool val) { pack_loading_disabled_ = val; }
+  bool pack_loading_disabled() const { return pack_loading_disabled_; }
+  void set_allow_pack_file_load(bool val) { allow_pack_file_load_ = val; }
+  bool allow_pack_file_load() { return allow_pack_file_load_; }
+
+  static void SetPDFEntryFunctions(
+      content::PepperPluginInfo::GetInterfaceFunc get_interface,
+      content::PepperPluginInfo::PPP_InitializeModuleFunc initialize_module,
+      content::PepperPluginInfo::PPP_ShutdownModuleFunc shutdown_module);
+
+  CefResourceBundleDelegate* GetCefResourceBundleDelegate() {
+    return &resource_bundle_delegate_;
+  }
+
+ private:
+  CefRefPtr<CefApp> application_;
+  bool pack_loading_disabled_;
+  bool allow_pack_file_load_;
+
+  // Custom schemes handled by the client.
+  SchemeInfoList scheme_info_list_;
+  bool scheme_info_list_locked_;
+
+  CefResourceBundleDelegate resource_bundle_delegate_;
+};
+
+#endif  // CEF_LIBCEF_COMMON_CONTENT_CLIENT_H_
diff --git a/src/libcef/common/crash_reporter_client.cc b/src/libcef/common/crash_reporter_client.cc
new file mode 100644
index 0000000..00d422b
--- /dev/null
+++ b/src/libcef/common/crash_reporter_client.cc
@@ -0,0 +1,797 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2016 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/common/crash_reporter_client.h"
+
+#include <utility>
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/crash/core/common/crash_key.h"
+#include "services/service_manager/embedder/switches.h"
+#include "third_party/crashpad/crashpad/client/annotation.h"
+
+#if defined(OS_MACOSX)
+#include "libcef/common/util_mac.h"
+#endif
+
+#if defined(OS_POSIX)
+// Don't use CommandLine, FilePath or PathService on Windows. FilePath has
+// dependencies outside of kernel32, which is disallowed by chrome_elf.
+// CommandLine and PathService depend on global state that will not be
+// initialized at the time the CefCrashReporterClient object is created.
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#include "content/public/common/content_switches.h"
+#include "libcef/common/cef_crash_report_utils.h"
+#endif
+
+#if defined(OS_WIN)
+#include "base/debug/leak_annotations.h"
+#include "chrome/install_static/install_util.h"
+#include "components/crash/core/app/crashpad.h"
+#endif
+
+namespace {
+
+#if defined(OS_WIN)
+typedef base::string16 PathString;
+const char kPathSep = '\\';
+#else
+typedef std::string PathString;
+#endif
+
+PathString GetCrashConfigPath() {
+#if defined(OS_WIN)
+  // Start with the path to the running executable.
+  wchar_t module_path[MAX_PATH];
+  if (GetModuleFileName(nullptr, module_path, MAX_PATH) == 0)
+    return PathString();
+
+  PathString config_path = module_path;
+
+  // Remove the executable file name.
+  PathString::size_type last_backslash =
+      config_path.rfind(kPathSep, config_path.size());
+  if (last_backslash != PathString::npos)
+    config_path.erase(last_backslash + 1);
+
+  config_path += L"crash_reporter.cfg";
+  return config_path;
+#elif defined(OS_POSIX)
+  base::FilePath config_path;
+
+#if defined(OS_MACOSX)
+  // Start with the path to the main app Resources directory. May be empty if
+  // not running in an app bundle.
+  config_path = util_mac::GetMainResourcesDirectory();
+#endif
+
+  if (config_path.empty()) {
+    // Start with the path to the running executable.
+    if (!base::PathService::Get(base::DIR_EXE, &config_path))
+      return PathString();
+  }
+
+  return config_path.Append(FILE_PATH_LITERAL("crash_reporter.cfg")).value();
+#endif  // defined(OS_POSIX)
+}
+
+#if defined(OS_WIN)
+
+// On Windows, FAT32 and NTFS both limit filenames to a maximum of 255
+// characters. On POSIX systems, the typical filename length limit is 255
+// character units. HFS+'s limit is actually 255 Unicode characters using
+// Apple's modification of Normalization Form D, but the differences aren't
+// really worth dealing with here.
+const unsigned maxFilenameLength = 255;
+
+const char kInvalidFileChars[] = "<>:\"/\\|?*";
+
+bool isInvalidFileCharacter(unsigned char c) {
+  if (c < ' ' || c == 0x7F)
+    return true;
+  for (size_t i = 0; i < sizeof(kInvalidFileChars); ++i) {
+    if (c == kInvalidFileChars[i])
+      return true;
+  }
+  return false;
+}
+
+bool isAbsolutePath(const std::string& s) {
+  // Check for local paths (beginning with "c:\") and network paths
+  // (beginning with "\\").
+  return s.length() > 2 &&
+         ((isalpha(s[0]) && s[1] == ':' && s[2] == kPathSep) ||
+          (s[0] == kPathSep && s[1] == kPathSep));
+}
+
+std::string extractAbsolutePathStart(std::string& s) {
+  if (!isAbsolutePath(s))
+    return std::string();
+
+  std::string start;
+  if (s[0] == kPathSep) {
+    // Network path.
+    start = s.substr(0, 2);
+    s = s.substr(2);
+  } else {
+    // Local path.
+    start = s.substr(0, 3);
+    s = s.substr(3);
+  }
+  return start;
+}
+
+std::string sanitizePathComponentPart(const std::string& s) {
+  if (s.empty())
+    return std::string();
+
+  std::string result;
+  result.reserve(s.length());
+  for (size_t i = 0; i < s.length(); ++i) {
+    if (!isInvalidFileCharacter(s[i]))
+      result.push_back(s[i]);
+  }
+  return result;
+}
+
+std::string sanitizePathComponent(const std::string& s) {
+  std::string name, ext;
+
+  // Separate name and extension, if any.
+  std::string::size_type pos = s.rfind('.');
+  if (pos != std::string::npos) {
+    name = s.substr(0, pos);
+    ext = s.substr(pos + 1);
+  } else {
+    name = s;
+  }
+
+  // Remove invalid characters.
+  name = sanitizePathComponentPart(name);
+  ext = sanitizePathComponentPart(ext);
+
+  // Remove a ridiculously-long extension.
+  if (ext.length() >= maxFilenameLength)
+    ext = std::string();
+
+  // Truncate an overly-long filename, reserving one character for a dot.
+  std::string::size_type max_name_len = maxFilenameLength - ext.length() - 1;
+  if (name.length() > max_name_len)
+    name = name.substr(0, max_name_len);
+
+  return ext.empty() ? name : name + "." + ext;
+}
+
+std::string sanitizePath(const std::string& s) {
+  std::string path = s;
+
+  // Extract the absolute path start component, if any (e.g. "c:\" on Windows).
+  std::string result = extractAbsolutePathStart(path);
+  result.reserve(s.length());
+
+  std::vector<std::string> parts =
+      base::SplitString(path, std::string() + kPathSep, base::KEEP_WHITESPACE,
+                        base::SPLIT_WANT_NONEMPTY);
+  for (size_t i = 0; i < parts.size(); ++i) {
+    std::string part = parts[i];
+    if (part != "." && part != "..")
+      part = sanitizePathComponent(part);
+    if (!result.empty() && result[result.length() - 1] != kPathSep)
+      result += kPathSep;
+    result += part;
+  }
+
+  return result;
+}
+
+std::string joinPath(const std::string& s1, const std::string& s2) {
+  if (s1.empty() && s2.empty())
+    return std::string();
+  if (s1.empty())
+    return s2;
+  if (s2.empty())
+    return s1;
+
+  // Don't try to join absolute paths on Windows.
+  // Skip this check on POSIX where it's more difficult to differentiate.
+  if (isAbsolutePath(s2))
+    return s2;
+
+  std::string result = s1;
+  if (result[result.size() - 1] != kPathSep)
+    result += kPathSep;
+  if (s2[0] == kPathSep)
+    result += s2.substr(1);
+  else
+    result += s2;
+  return result;
+}
+
+// This will only be non-nullptr in the chrome_elf address space.
+CefCrashReporterClient* g_crash_reporter_client = nullptr;
+
+#endif  // defined(OS_WIN)
+
+const char kKeyMapDelim = ',';
+
+std::string NormalizeCrashKey(const base::StringPiece& key) {
+  std::string str = key.as_string();
+  std::replace(str.begin(), str.end(), kKeyMapDelim, '-');
+  if (str.length() > crashpad::Annotation::kNameMaxLength) {
+    return str.substr(0, crashpad::Annotation::kNameMaxLength);
+  }
+  return str;
+}
+
+void ParseURL(const std::string& value, std::string* url) {
+  if (value.find("http://") == 0 || value.find("https://") == 0) {
+    *url = value;
+    if (url->rfind('/') <= 8) {
+      // Make sure the URL includes a path component. Otherwise, crash
+      // upload will fail on older Windows versions due to
+      // https://crbug.com/826564.
+      *url += "/";
+    }
+  }
+}
+
+bool ParseBool(const std::string& value) {
+  return base::EqualsCaseInsensitiveASCII(value, "true") || value == "1";
+}
+
+int ParseZeroBasedInt(const std::string& value) {
+  int int_val;
+  if (base::StringToInt(value, &int_val) && int_val > 0)
+    return int_val;
+  return 0;
+}
+
+}  // namespace
+
+#if defined(OS_WIN)
+
+extern "C" {
+
+// Export functions from chrome_elf that are required by crash_reporting.cc
+
+int __declspec(dllexport) __cdecl SetCrashKeyValueImpl(const char* key,
+                                                       size_t key_size,
+                                                       const char* value,
+                                                       size_t value_size) {
+  if (g_crash_reporter_client) {
+    return g_crash_reporter_client->SetCrashKeyValue(
+        base::StringPiece(key, key_size), base::StringPiece(value, value_size));
+  }
+  return 0;
+}
+
+int __declspec(dllexport) __cdecl IsCrashReportingEnabledImpl() {
+  return g_crash_reporter_client &&
+         g_crash_reporter_client->HasCrashConfigFile();
+}
+
+}  // extern "C"
+
+// The below functions were deleted from chrome/install_static/install_util.cc
+// in https://crbug.com/565446#c17.
+
+constexpr wchar_t kUserDataDirname[] = L"User Data";
+
+// Populates |result| with the default User Data directory for the current
+// user.This may be overidden by a command line option. Returns false if all
+// attempts at locating a User Data directory fail.
+bool GetDefaultUserDataDirectory(std::wstring* result,
+                                 const std::wstring& install_sub_directory) {
+  // This environment variable should be set on Windows Vista and later
+  // (https://msdn.microsoft.com/library/windows/desktop/dd378457.aspx).
+  std::wstring user_data_dir =
+      install_static::GetEnvironmentString16(L"LOCALAPPDATA");
+
+  if (user_data_dir.empty()) {
+    // LOCALAPPDATA was not set; fallback to the temporary files path.
+    DWORD size = ::GetTempPath(0, nullptr);
+    if (!size)
+      return false;
+    user_data_dir.resize(size + 1);
+    size = ::GetTempPath(size + 1, &user_data_dir[0]);
+    if (!size || size >= user_data_dir.size())
+      return false;
+    user_data_dir.resize(size);
+  }
+
+  result->swap(user_data_dir);
+  if ((*result)[result->length() - 1] != L'\\')
+    result->push_back(L'\\');
+  result->append(install_sub_directory);
+  result->push_back(L'\\');
+  result->append(kUserDataDirname);
+  return true;
+}
+
+// Populates |crash_dir| with the default crash dump location regardless of
+// whether DIR_USER_DATA or DIR_CRASH_DUMPS has been overridden.
+bool GetDefaultCrashDumpLocation(std::wstring* crash_dir,
+                                 const std::wstring& install_sub_directory) {
+  // In order to be able to start crash handling very early, we do not rely on
+  // chrome's PathService entries (for DIR_CRASH_DUMPS) being available on
+  // Windows. See https://crbug.com/564398.
+  if (!GetDefaultUserDataDirectory(crash_dir, install_sub_directory))
+    return false;
+
+  // We have to make sure the user data dir exists on first run. See
+  // http://crbug.com/591504.
+  if (!install_static::RecursiveDirectoryCreate(*crash_dir))
+    return false;
+  crash_dir->append(L"\\Crashpad");
+  return true;
+}
+
+#endif  // OS_WIN
+
+CefCrashReporterClient::CefCrashReporterClient() {}
+CefCrashReporterClient::~CefCrashReporterClient() {}
+
+// Be aware that logging is not initialized at the time this method is called.
+bool CefCrashReporterClient::ReadCrashConfigFile() {
+  if (has_crash_config_file_)
+    return true;
+
+  PathString config_path = GetCrashConfigPath();
+  if (config_path.empty())
+    return false;
+
+#if defined(OS_WIN)
+  FILE* fp = _wfopen(config_path.c_str(), L"r");
+#else
+  FILE* fp = fopen(config_path.c_str(), "r");
+#endif
+  if (!fp)
+    return false;
+
+  char line[1000];
+
+  size_t small_index = 0;
+  size_t medium_index = 0;
+  size_t large_index = 0;
+  std::string map_keys;
+
+  enum section {
+    kNoSection,
+    kConfigSection,
+    kCrashKeysSection,
+  } current_section = kNoSection;
+
+  while (fgets(line, sizeof(line) - 1, fp) != nullptr) {
+    std::string str = line;
+    base::TrimString(str, base::kWhitespaceASCII, &str);
+    if (str.empty() || str[0] == '#')
+      continue;
+
+    if (str == "[Config]") {
+      current_section = kConfigSection;
+      continue;
+    } else if (str == "[CrashKeys]") {
+      current_section = kCrashKeysSection;
+      continue;
+    } else if (str[0] == '[') {
+      current_section = kNoSection;
+      continue;
+    }
+
+    if (current_section == kNoSection)
+      continue;
+
+    size_t div = str.find('=');
+    if (div == std::string::npos)
+      continue;
+
+    std::string name_str = str.substr(0, div);
+    base::TrimString(name_str, base::kWhitespaceASCII, &name_str);
+    std::string val_str = str.substr(div + 1);
+    base::TrimString(val_str, base::kWhitespaceASCII, &val_str);
+    if (name_str.empty())
+      continue;
+
+    if (current_section == kConfigSection) {
+      if (name_str == "ServerURL") {
+        ParseURL(val_str, &server_url_);
+      } else if (name_str == "ProductName") {
+        product_name_ = val_str;
+      } else if (name_str == "ProductVersion") {
+        product_version_ = val_str;
+      } else if (name_str == "RateLimitEnabled") {
+        rate_limit_ = ParseBool(val_str);
+      } else if (name_str == "MaxUploadsPerDay") {
+        max_uploads_ = ParseZeroBasedInt(val_str);
+      } else if (name_str == "MaxDatabaseSizeInMb") {
+        max_db_size_ = ParseZeroBasedInt(val_str);
+      } else if (name_str == "MaxDatabaseAgeInDays") {
+        max_db_age_ = ParseZeroBasedInt(val_str);
+      }
+#if defined(OS_WIN)
+      else if (name_str == "ExternalHandler") {
+        if (!val_str.empty())
+          external_handler_ = sanitizePath(val_str);
+      } else if (name_str == "AppName") {
+        if (!val_str.empty()) {
+          val_str = sanitizePathComponent(val_str);
+          if (!val_str.empty())
+            app_name_ = val_str;
+        }
+      }
+#elif defined(OS_MACOSX)
+      else if (name_str == "BrowserCrashForwardingEnabled") {
+        enable_browser_crash_forwarding_ = ParseBool(val_str);
+      }
+#endif
+    } else if (current_section == kCrashKeysSection) {
+      // Skip duplicate definitions.
+      if (!crash_keys_.empty() &&
+          crash_keys_.find(name_str) != crash_keys_.end()) {
+        continue;
+      }
+
+      KeySize size;
+      size_t index;
+      char group;
+      if (val_str == "small") {
+        size = SMALL_SIZE;
+        index = small_index++;
+        group = 'S';
+      } else if (val_str == "medium") {
+        size = MEDIUM_SIZE;
+        index = medium_index++;
+        group = 'M';
+      } else if (val_str == "large") {
+        size = LARGE_SIZE;
+        index = large_index++;
+        group = 'L';
+      } else {
+        continue;
+      }
+
+      name_str = NormalizeCrashKey(name_str);
+      crash_keys_.insert(std::make_pair(name_str, std::make_pair(size, index)));
+
+      const std::string& key =
+          std::string(1, group) + "-" + std::string(1, 'A' + index);
+      if (!map_keys.empty()) {
+        map_keys.append(std::string(1, kKeyMapDelim));
+      }
+      map_keys.append(key + "=" + name_str);
+    }
+  }
+
+  fclose(fp);
+
+  if (!map_keys.empty()) {
+    // Split |map_keys| across multiple Annotations if necessary.
+    // Must match the logic in crash_report_utils::FilterParameters.
+    using IDKey =
+        crash_reporter::CrashKeyString<crashpad::Annotation::kValueMaxSize>;
+    static IDKey ids[] = {
+        {"K-A", IDKey::Tag::kArray},
+        {"K-B", IDKey::Tag::kArray},
+        {"K-C", IDKey::Tag::kArray},
+    };
+
+    // Make sure we can fit all possible name/value pairs.
+    static_assert(base::size(ids) * crashpad::Annotation::kValueMaxSize >=
+                      3 * 26 /* sizes (small, medium, large) * slots (A to Z) */
+                          * (3 + 2 /* key size ("S-A") + delim size ("=,") */
+                             + crashpad::Annotation::kNameMaxLength),
+                  "Not enough storage for key map");
+
+    size_t offset = 0;
+    for (size_t i = 0; i < base::size(ids); ++i) {
+      size_t length = std::min(map_keys.size() - offset,
+                               crashpad::Annotation::kValueMaxSize);
+      ids[i].Set(base::StringPiece(map_keys.data() + offset, length));
+      offset += length;
+      if (offset >= map_keys.size())
+        break;
+    }
+  }
+
+  // Allow override of some values via environment variables.
+  {
+    std::unique_ptr<base::Environment> env(base::Environment::Create());
+    std::string val_str;
+
+    if (env->GetVar("CEF_CRASH_REPORTER_SERVER_URL", &val_str)) {
+      ParseURL(val_str, &server_url_);
+    }
+    if (env->GetVar("CEF_CRASH_REPORTER_RATE_LIMIT_ENABLED", &val_str)) {
+      rate_limit_ = ParseBool(val_str);
+    }
+  }
+
+  has_crash_config_file_ = true;
+  return true;
+}
+
+bool CefCrashReporterClient::HasCrashConfigFile() const {
+  return has_crash_config_file_;
+}
+
+#if defined(OS_WIN)
+
+// static
+void CefCrashReporterClient::InitializeCrashReportingForProcess() {
+  if (g_crash_reporter_client)
+    return;
+
+  g_crash_reporter_client = new CefCrashReporterClient();
+  ANNOTATE_LEAKING_OBJECT_PTR(g_crash_reporter_client);
+
+  if (!g_crash_reporter_client->ReadCrashConfigFile())
+    return;
+
+  std::wstring process_type = install_static::GetSwitchValueFromCommandLine(
+      ::GetCommandLineW(), install_static::kProcessType);
+  if (process_type != install_static::kCrashpadHandler) {
+    crash_reporter::SetCrashReporterClient(g_crash_reporter_client);
+
+    // If |embedded_handler| is true then we launch another instance of the main
+    // executable as the crashpad-handler process.
+    const bool embedded_handler =
+        !g_crash_reporter_client->HasCrashExternalHandler();
+    if (embedded_handler) {
+      crash_reporter::InitializeCrashpadWithEmbeddedHandler(
+          process_type.empty(), install_static::UTF16ToUTF8(process_type),
+          std::string(), base::FilePath());
+    } else {
+      crash_reporter::InitializeCrashpad(
+          process_type.empty(), install_static::UTF16ToUTF8(process_type));
+    }
+  }
+}
+
+bool CefCrashReporterClient::GetAlternativeCrashDumpLocation(
+    base::string16* crash_dir) {
+  // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
+  // location to write breakpad crash dumps can be set.
+  *crash_dir =
+      install_static::GetEnvironmentString16(L"BREAKPAD_DUMP_LOCATION");
+  return !crash_dir->empty();
+}
+
+void CefCrashReporterClient::GetProductNameAndVersion(
+    const base::string16& exe_path,
+    base::string16* product_name,
+    base::string16* version,
+    base::string16* special_build,
+    base::string16* channel_name) {
+  *product_name = base::ASCIIToUTF16(product_name_);
+  *version = base::ASCIIToUTF16(product_version_);
+  *special_build = base::string16();
+  *channel_name = base::string16();
+}
+
+bool CefCrashReporterClient::GetCrashDumpLocation(base::string16* crash_dir) {
+  // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
+  // location to write breakpad crash dumps can be set.
+  if (GetAlternativeCrashDumpLocation(crash_dir))
+    return true;
+
+  return GetDefaultCrashDumpLocation(crash_dir, base::UTF8ToUTF16(app_name_));
+}
+
+bool CefCrashReporterClient::GetCrashMetricsLocation(
+    base::string16* metrics_dir) {
+  return GetDefaultUserDataDirectory(metrics_dir, base::UTF8ToUTF16(app_name_));
+}
+
+#elif defined(OS_POSIX)
+
+void CefCrashReporterClient::GetProductNameAndVersion(const char** product_name,
+                                                      const char** version) {
+  *product_name = product_name_.c_str();
+  *version = product_version_.c_str();
+}
+
+void CefCrashReporterClient::GetProductNameAndVersion(std::string* product_name,
+                                                      std::string* version,
+                                                      std::string* channel) {
+  *product_name = product_name_;
+  *version = product_version_;
+}
+
+#if !defined(OS_MACOSX)
+
+base::FilePath CefCrashReporterClient::GetReporterLogFilename() {
+  return base::FilePath(FILE_PATH_LITERAL("uploads.log"));
+}
+
+bool CefCrashReporterClient::EnableBreakpadForProcess(
+    const std::string& process_type) {
+  return process_type == switches::kRendererProcess ||
+         process_type == switches::kPpapiPluginProcess ||
+         process_type == service_manager::switches::kZygoteProcess ||
+         process_type == switches::kGpuProcess;
+}
+
+#endif  // !defined(OS_MACOSX)
+
+bool CefCrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
+  // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
+  // location to write breakpad crash dumps can be set.
+  std::unique_ptr<base::Environment> env(base::Environment::Create());
+  std::string alternate_crash_dump_location;
+  if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
+    base::FilePath crash_dumps_dir_path =
+        base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
+    base::PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path);
+  }
+  return base::PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir);
+}
+
+#endif  // !defined(OS_POSIX)
+
+bool CefCrashReporterClient::GetCollectStatsConsent() {
+  return true;
+}
+
+bool CefCrashReporterClient::GetCollectStatsInSample() {
+  return true;
+}
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+bool CefCrashReporterClient::ReportingIsEnforcedByPolicy(
+    bool* crashpad_enabled) {
+  *crashpad_enabled = true;
+  return true;
+}
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+bool CefCrashReporterClient::IsRunningUnattended() {
+  // Crash upload will only be enabled with Breakpad on Linux if this method
+  // returns false.
+  return false;
+}
+#endif
+
+std::string CefCrashReporterClient::GetCrashServerURL() {
+  return server_url_;
+}
+
+// See HandlerMain() in third_party/crashpad/crashpad/handler/handler_main.cc
+// for supported arguments.
+void CefCrashReporterClient::GetCrashOptionalArguments(
+    std::vector<std::string>* arguments) {
+  if (!rate_limit_)
+    arguments->push_back(std::string("--no-rate-limit"));
+
+  if (max_uploads_ > 0) {
+    arguments->push_back(std::string("--max-uploads=") +
+                         base::NumberToString(max_uploads_));
+  }
+
+  if (max_db_size_ > 0) {
+    arguments->push_back(std::string("--max-db-size=") +
+                         base::NumberToString(max_db_size_));
+  }
+
+  if (max_db_age_ > 0) {
+    arguments->push_back(std::string("--max-db-age=") +
+                         base::NumberToString(max_db_age_));
+  }
+}
+
+#if defined(OS_WIN)
+
+base::string16 CefCrashReporterClient::GetCrashExternalHandler(
+    const base::string16& exe_dir) {
+  if (external_handler_.empty())
+    return CrashReporterClient::GetCrashExternalHandler(exe_dir);
+  if (isAbsolutePath(external_handler_))
+    return base::UTF8ToUTF16(external_handler_);
+  return base::UTF8ToWide(
+      joinPath(base::UTF16ToUTF8(exe_dir), external_handler_));
+}
+
+bool CefCrashReporterClient::HasCrashExternalHandler() const {
+  return !external_handler_.empty();
+}
+
+#endif  // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+bool CefCrashReporterClient::EnableBrowserCrashForwarding() {
+  return enable_browser_crash_forwarding_;
+}
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+CefCrashReporterClient::ParameterMap CefCrashReporterClient::FilterParameters(
+    const ParameterMap& parameters) {
+  return crash_report_utils::FilterParameters(parameters);
+}
+#endif
+
+// The new Crashpad Annotation API requires that annotations be declared using
+// static storage. We work around this limitation by defining a fixed amount of
+// storage for each key size and later substituting the actual key name during
+// crash dump processing.
+
+#define IDKEY(name) \
+  { name, IDKey::Tag::kArray }
+
+#define IDKEY_ENTRIES(n)                                                     \
+  IDKEY(n "-A"), IDKEY(n "-B"), IDKEY(n "-C"), IDKEY(n "-D"), IDKEY(n "-E"), \
+      IDKEY(n "-F"), IDKEY(n "-G"), IDKEY(n "-H"), IDKEY(n "-I"),            \
+      IDKEY(n "-J"), IDKEY(n "-K"), IDKEY(n "-L"), IDKEY(n "-M"),            \
+      IDKEY(n "-N"), IDKEY(n "-O"), IDKEY(n "-P"), IDKEY(n "-Q"),            \
+      IDKEY(n "-R"), IDKEY(n "-S"), IDKEY(n "-T"), IDKEY(n "-U"),            \
+      IDKEY(n "-V"), IDKEY(n "-W"), IDKEY(n "-X"), IDKEY(n "-Y"),            \
+      IDKEY(n "-Z")
+
+#define IDKEY_FUNCTION(name, size_)                                          \
+  static_assert(size_ <= crashpad::Annotation::kValueMaxSize,                \
+                "Annotation size is too large.");                            \
+  bool Set##name##Annotation(size_t index, const base::StringPiece& value) { \
+    using IDKey = crash_reporter::CrashKeyString<size_>;                     \
+    static IDKey ids[] = {IDKEY_ENTRIES(#name)};                             \
+    if (index < base::size(ids)) {                                           \
+      if (value.empty()) {                                                   \
+        ids[index].Clear();                                                  \
+      } else {                                                               \
+        ids[index].Set(value);                                               \
+      }                                                                      \
+      return true;                                                           \
+    }                                                                        \
+    return false;                                                            \
+  }
+
+// The first argument must be kept synchronized with the logic in
+// CefCrashReporterClient::ReadCrashConfigFile and
+// crash_report_utils::FilterParameters.
+IDKEY_FUNCTION(S, 64)
+IDKEY_FUNCTION(M, 256)
+IDKEY_FUNCTION(L, 1024)
+
+bool CefCrashReporterClient::SetCrashKeyValue(const base::StringPiece& key,
+                                              const base::StringPiece& value) {
+  if (key.empty() || crash_keys_.empty())
+    return false;
+
+  KeyMap::const_iterator it = crash_keys_.find(NormalizeCrashKey(key));
+  if (it == crash_keys_.end())
+    return false;
+
+  const KeySize size = it->second.first;
+  const size_t index = it->second.second;
+
+  base::AutoLock lock_scope(crash_key_lock_);
+
+  switch (size) {
+    case SMALL_SIZE:
+      return SetSAnnotation(index, value);
+    case MEDIUM_SIZE:
+      return SetMAnnotation(index, value);
+    case LARGE_SIZE:
+      return SetLAnnotation(index, value);
+  }
+
+  return false;
+}
diff --git a/src/libcef/common/crash_reporter_client.h b/src/libcef/common/crash_reporter_client.h
new file mode 100644
index 0000000..fc4f5c2
--- /dev/null
+++ b/src/libcef/common/crash_reporter_client.h
@@ -0,0 +1,132 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2016 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_CRASH_REPORTER_CLIENT_H_
+#define CEF_LIBCEF_COMMON_CRASH_REPORTER_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+// Include this first to avoid compiler errors.
+#include "base/compiler_specific.h"
+
+#include "include/cef_version.h"
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+
+// Global object that is instantiated in each process and configures crash
+// reporting. On Windows this is created by the
+// InitializeCrashReportingForProcess() method called from chrome_elf. On
+// Linux and macOS this is created by crash_reporting::BasicStartupComplete().
+class CefCrashReporterClient : public crash_reporter::CrashReporterClient {
+ public:
+  CefCrashReporterClient();
+  ~CefCrashReporterClient() override;
+
+  // Reads the crash config file and returns true on success. Failure to read
+  // the crash config file will disable crash reporting. This method should be
+  // called immediately after the CefCrashReporterClient instance is created.
+  bool ReadCrashConfigFile();
+  bool HasCrashConfigFile() const;
+
+#if defined(OS_WIN)
+  // Called from chrome_elf (chrome_elf/crash/crash_helper.cc) to instantiate
+  // a process wide instance of CefCrashReporterClient and initialize crash
+  // reporting for the process. The instance is leaked.
+  // crash_reporting_win::InitializeCrashReportingForModule() will be called
+  // later from crash_reporting::PreSandboxStartup() to read global state into
+  // the module address space.
+  static void InitializeCrashReportingForProcess();
+
+  bool GetAlternativeCrashDumpLocation(base::string16* crash_dir) override;
+  void GetProductNameAndVersion(const base::string16& exe_path,
+                                base::string16* product_name,
+                                base::string16* version,
+                                base::string16* special_build,
+                                base::string16* channel_name) override;
+  bool GetCrashDumpLocation(base::string16* crash_dir) override;
+  bool GetCrashMetricsLocation(base::string16* metrics_dir) override;
+#elif defined(OS_POSIX)
+  void GetProductNameAndVersion(const char** product_name,
+                                const char** version) override;
+  void GetProductNameAndVersion(std::string* product_name,
+                                std::string* version,
+                                std::string* channel) override;
+#if !defined(OS_MACOSX)
+  base::FilePath GetReporterLogFilename() override;
+  bool EnableBreakpadForProcess(const std::string& process_type) override;
+#endif
+  bool GetCrashDumpLocation(base::FilePath* crash_dir) override;
+#endif  // defined(OS_POSIX)
+
+  // All of these methods must return true to enable crash report upload.
+  bool GetCollectStatsConsent() override;
+  bool GetCollectStatsInSample() override;
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  bool ReportingIsEnforcedByPolicy(bool* crashpad_enabled) override;
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+  bool IsRunningUnattended() override;
+#endif
+
+  std::string GetCrashServerURL() override;
+  void GetCrashOptionalArguments(std::vector<std::string>* arguments) override;
+
+#if defined(OS_WIN)
+  base::string16 GetCrashExternalHandler(
+      const base::string16& exe_dir) override;
+  bool HasCrashExternalHandler() const;
+#endif
+
+#if defined(OS_MACOSX)
+  bool EnableBrowserCrashForwarding() override;
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+  ParameterMap FilterParameters(const ParameterMap& parameters) override;
+#endif
+
+  // Set or clear a crash key value.
+  bool SetCrashKeyValue(const base::StringPiece& key,
+                        const base::StringPiece& value);
+
+ private:
+  bool has_crash_config_file_ = false;
+
+  enum KeySize { SMALL_SIZE, MEDIUM_SIZE, LARGE_SIZE };
+
+  // Map of crash key name to (KeySize, index).
+  // Const access to |crash_keys_| is thread-safe after initialization.
+  using KeyMap = std::map<std::string, std::pair<KeySize, size_t>>;
+  KeyMap crash_keys_;
+
+  // Modification of CrashKeyString values must be synchronized.
+  base::Lock crash_key_lock_;
+
+  std::string server_url_;
+  bool rate_limit_ = true;
+  int max_uploads_ = 5;
+  int max_db_size_ = 20;
+  int max_db_age_ = 5;
+
+  std::string product_name_ = "cef";
+  std::string product_version_ = CEF_VERSION;
+
+#if defined(OS_WIN)
+  std::string app_name_ = "CEF";
+  std::string external_handler_;
+#endif
+
+#if defined(OS_MACOSX)
+  bool enable_browser_crash_forwarding_ = false;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(CefCrashReporterClient);
+};
+
+#endif  // CEF_LIBCEF_COMMON_CRASH_REPORTER_CLIENT_H_
diff --git a/src/libcef/common/crash_reporting.cc b/src/libcef/common/crash_reporting.cc
new file mode 100644
index 0000000..0af03d4
--- /dev/null
+++ b/src/libcef/common/crash_reporting.cc
@@ -0,0 +1,261 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2016 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/common/crash_reporting.h"
+
+#include "include/cef_crash_util.h"
+#include "libcef/common/cef_switches.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug/crash_logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "chrome/common/crash_keys.h"
+#include "components/crash/core/common/crash_key.h"
+#include "components/crash/core/common/crash_keys.h"
+#include "content/public/common/content_switches.h"
+#include "services/service_manager/embedder/switches.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/foundation_util.h"
+#include "components/crash/core/app/crashpad.h"
+#include "components/crash/core/common/crash_keys.h"
+#include "content/public/common/content_paths.h"
+#endif
+
+#if defined(OS_POSIX)
+#include "base/lazy_instance.h"
+#include "libcef/common/crash_reporter_client.h"
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#include "components/crash/core/app/breakpad_linux.h"
+#include "v8/include/v8-wasm-trap-handler-posix.h"
+#endif
+
+namespace crash_reporting {
+
+namespace {
+
+#if defined(OS_WIN)
+
+const base::FilePath::CharType kChromeElfDllName[] =
+    FILE_PATH_LITERAL("chrome_elf.dll");
+
+// exported in crash_reporter_client.cc:
+//    int __declspec(dllexport) __cdecl SetCrashKeyValueImpl.
+typedef int(__cdecl* SetCrashKeyValue)(const char*,
+                                       size_t,
+                                       const char*,
+                                       size_t);
+
+//    int __declspec(dllexport) __cdecl IsCrashReportingEnabledImpl.
+typedef int(__cdecl* IsCrashReportingEnabled)();
+
+bool SetCrashKeyValueTrampoline(const base::StringPiece& key,
+                                const base::StringPiece& value) {
+  static SetCrashKeyValue set_crash_key = []() {
+    HMODULE elf_module = GetModuleHandle(kChromeElfDllName);
+    return reinterpret_cast<SetCrashKeyValue>(
+        elf_module ? GetProcAddress(elf_module, "SetCrashKeyValueImpl")
+                   : nullptr);
+  }();
+  if (set_crash_key) {
+    return !!(set_crash_key)(key.data(), key.size(), value.data(),
+                             value.size());
+  }
+  return false;
+}
+
+bool IsCrashReportingEnabledTrampoline() {
+  static IsCrashReportingEnabled is_crash_reporting_enabled = []() {
+    HMODULE elf_module = GetModuleHandle(kChromeElfDllName);
+    return reinterpret_cast<IsCrashReportingEnabled>(
+        elf_module ? GetProcAddress(elf_module, "IsCrashReportingEnabledImpl")
+                   : nullptr);
+  }();
+  if (is_crash_reporting_enabled) {
+    return !!(is_crash_reporting_enabled)();
+  }
+  return false;
+}
+
+#endif  // defined(OS_WIN)
+
+bool g_crash_reporting_enabled = false;
+
+#if defined(OS_POSIX)
+base::LazyInstance<CefCrashReporterClient>::Leaky g_crash_reporter_client =
+    LAZY_INSTANCE_INITIALIZER;
+
+void InitCrashReporter(const base::CommandLine& command_line,
+                       const std::string& process_type) {
+  CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
+  if (!crash_client->HasCrashConfigFile())
+    return;
+
+  crash_reporter::SetCrashReporterClient(crash_client);
+
+#if defined(OS_MACOSX)
+  // TODO(mark): Right now, InitializeCrashpad() needs to be called after
+  // CommandLine::Init() and configuration of chrome::DIR_CRASH_DUMPS. Ideally,
+  // Crashpad initialization could occur sooner, preferably even before the
+  // framework dylib is even loaded, to catch potential early crashes.
+  crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
+
+  if (base::mac::AmIBundled()) {
+    // Mac Chrome is packaged with a main app bundle and a helper app bundle.
+    // The main app bundle should only be used for the browser process, so it
+    // should never see a --type switch (switches::kProcessType).  Likewise,
+    // the helper should always have a --type switch.
+    //
+    // This check is done this late so there is already a call to
+    // base::mac::IsBackgroundOnlyProcess(), so there is no change in
+    // startup/initialization order.
+
+    // The helper's Info.plist marks it as a background only app.
+    if (base::mac::IsBackgroundOnlyProcess()) {
+      CHECK(command_line.HasSwitch(switches::kProcessType) &&
+            !process_type.empty())
+          << "Helper application requires --type.";
+    } else {
+      CHECK(!command_line.HasSwitch(switches::kProcessType) &&
+            process_type.empty())
+          << "Main application forbids --type, saw " << process_type;
+    }
+  }
+
+  g_crash_reporting_enabled = true;
+#else   // !defined(OS_MACOSX)
+  breakpad::SetCrashServerURL(crash_client->GetCrashServerURL());
+
+  if (process_type != service_manager::switches::kZygoteProcess) {
+    // Crash reporting for subprocesses created using the zygote will be
+    // initialized in ZygoteForked.
+    breakpad::InitCrashReporter(process_type);
+
+    g_crash_reporting_enabled = true;
+  }
+#endif  // !defined(OS_MACOSX)
+}
+#endif  // defined(OS_POSIX)
+
+// Used to exclude command-line flags from crash reporting.
+bool IsBoringCEFSwitch(const std::string& flag) {
+  if (crash_keys::IsBoringChromeSwitch(flag))
+    return true;
+
+  static const char* const kIgnoreSwitches[] = {
+      // CEF internals.
+      switches::kLogFile,
+
+      // Chromium internals.
+      "content-image-texture-target",
+      "mojo-platform-channel-handle",
+      "primordial-pipe-token",
+      "service-pipe-token",
+      "service-request-channel-token",
+  };
+
+  if (!base::StartsWith(flag, "--", base::CompareCase::SENSITIVE))
+    return false;
+
+  size_t end = flag.find("=");
+  size_t len = (end == std::string::npos) ? flag.length() - 2 : end - 2;
+  for (size_t i = 0; i < base::size(kIgnoreSwitches); ++i) {
+    if (flag.compare(2, len, kIgnoreSwitches[i]) == 0)
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+bool Enabled() {
+  return g_crash_reporting_enabled;
+}
+
+bool SetCrashKeyValue(const base::StringPiece& key,
+                      const base::StringPiece& value) {
+  if (!g_crash_reporting_enabled)
+    return false;
+
+#if defined(OS_WIN)
+  return SetCrashKeyValueTrampoline(key, value);
+#else
+  return g_crash_reporter_client.Pointer()->SetCrashKeyValue(key, value);
+#endif
+}
+
+#if defined(OS_POSIX)
+// Be aware that logging is not initialized at the time this method is called.
+void BasicStartupComplete(base::CommandLine* command_line) {
+  CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
+  if (crash_client->ReadCrashConfigFile()) {
+#if !defined(OS_MACOSX)
+    // Breakpad requires this switch.
+    command_line->AppendSwitch(switches::kEnableCrashReporter);
+
+    breakpad::SetFirstChanceExceptionHandler(v8::TryHandleWebAssemblyTrapPosix);
+#endif
+  }
+}
+#endif
+
+void PreSandboxStartup(const base::CommandLine& command_line,
+                       const std::string& process_type) {
+#if defined(OS_POSIX)
+  // Initialize crash reporting here on macOS and Linux. Crash reporting on
+  // Windows is initialized from context.cc.
+  InitCrashReporter(command_line, process_type);
+#elif defined(OS_WIN)
+  g_crash_reporting_enabled = IsCrashReportingEnabledTrampoline();
+#endif
+
+  if (g_crash_reporting_enabled) {
+    LOG(INFO) << "Crash reporting enabled for process: "
+              << (process_type.empty() ? "browser" : process_type.c_str());
+  }
+
+  crash_reporter::InitializeCrashKeys();
+
+  // After platform crash reporting have been initialized, store the command
+  // line for crash reporting.
+  crash_keys::SetSwitchesFromCommandLine(command_line, &IsBoringCEFSwitch);
+}
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
+void ZygoteForked(base::CommandLine* command_line,
+                  const std::string& process_type) {
+  CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
+  if (crash_client->HasCrashConfigFile()) {
+    // Breakpad requires this switch.
+    command_line->AppendSwitch(switches::kEnableCrashReporter);
+  }
+
+  InitCrashReporter(*command_line, process_type);
+
+  if (g_crash_reporting_enabled) {
+    LOG(INFO) << "Crash reporting enabled for process: " << process_type;
+  }
+
+  // Reset the command line for the newly spawned process.
+  crash_keys::SetSwitchesFromCommandLine(*command_line, &IsBoringCEFSwitch);
+}
+#endif
+
+}  // namespace crash_reporting
+
+bool CefCrashReportingEnabled() {
+  return crash_reporting::Enabled();
+}
+
+void CefSetCrashKeyValue(const CefString& key, const CefString& value) {
+  if (!crash_reporting::SetCrashKeyValue(key.ToString(), value.ToString())) {
+    LOG(WARNING) << "Failed to set crash key: " << key.ToString()
+                 << " with value: " << value.ToString();
+  }
+}
diff --git a/src/libcef/common/crash_reporting.h b/src/libcef/common/crash_reporting.h
new file mode 100644
index 0000000..ab0dad2
--- /dev/null
+++ b/src/libcef/common/crash_reporting.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2016 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include <string>
+
+#include "base/strings/string_piece_forward.h"
+#include "build/build_config.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace crash_reporting {
+
+// Returns true if crash reporting is enabled.
+bool Enabled();
+
+// Set or clear a crash key value.
+bool SetCrashKeyValue(const base::StringPiece& key,
+                      const base::StringPiece& value);
+
+// Functions are called from similarly named methods in CefMainDelegate.
+
+#if defined(OS_POSIX)
+void BasicStartupComplete(base::CommandLine* command_line);
+#endif
+
+void PreSandboxStartup(const base::CommandLine& command_line,
+                       const std::string& process_type);
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
+void ZygoteForked(base::CommandLine* command_line,
+                  const std::string& process_type);
+#endif
+
+}  // namespace crash_reporting
diff --git a/src/libcef/common/drag_data_impl.cc b/src/libcef/common/drag_data_impl.cc
new file mode 100644
index 0000000..1b9eeda
--- /dev/null
+++ b/src/libcef/common/drag_data_impl.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "libcef/browser/stream_impl.h"
+#include "libcef/common/drag_data_impl.h"
+
+#define CHECK_READONLY_RETURN_VOID()       \
+  if (read_only_) {                        \
+    NOTREACHED() << "object is read only"; \
+    return;                                \
+  }
+
+CefDragDataImpl::CefDragDataImpl(const content::DropData& data)
+    : data_(data), read_only_(false) {}
+
+CefDragDataImpl::CefDragDataImpl(const content::DropData& data,
+                                 CefRefPtr<CefImage> image,
+                                 const CefPoint& image_hotspot)
+    : data_(data),
+      image_(image),
+      image_hotspot_(image_hotspot),
+      read_only_(false) {}
+
+CefDragDataImpl::CefDragDataImpl() : read_only_(false) {}
+
+CefRefPtr<CefDragData> CefDragData::Create() {
+  return new CefDragDataImpl();
+}
+
+CefRefPtr<CefDragData> CefDragDataImpl::Clone() {
+  CefDragDataImpl* drag_data = nullptr;
+  {
+    base::AutoLock lock_scope(lock_);
+    drag_data = new CefDragDataImpl(data_, image_, image_hotspot_);
+  }
+  return drag_data;
+}
+
+bool CefDragDataImpl::IsReadOnly() {
+  base::AutoLock lock_scope(lock_);
+  return read_only_;
+}
+
+bool CefDragDataImpl::IsLink() {
+  base::AutoLock lock_scope(lock_);
+  return (data_.url.is_valid() &&
+          data_.file_contents_content_disposition.empty());
+}
+
+bool CefDragDataImpl::IsFragment() {
+  base::AutoLock lock_scope(lock_);
+  return (!data_.url.is_valid() &&
+          data_.file_contents_content_disposition.empty() &&
+          data_.filenames.empty());
+}
+
+bool CefDragDataImpl::IsFile() {
+  base::AutoLock lock_scope(lock_);
+  return (!data_.file_contents_content_disposition.empty() ||
+          !data_.filenames.empty());
+}
+
+CefString CefDragDataImpl::GetLinkURL() {
+  base::AutoLock lock_scope(lock_);
+  return data_.url.spec();
+}
+
+CefString CefDragDataImpl::GetLinkTitle() {
+  base::AutoLock lock_scope(lock_);
+  return data_.url_title;
+}
+
+CefString CefDragDataImpl::GetLinkMetadata() {
+  base::AutoLock lock_scope(lock_);
+  return data_.download_metadata;
+}
+
+CefString CefDragDataImpl::GetFragmentText() {
+  base::AutoLock lock_scope(lock_);
+  return data_.text.is_null() ? CefString() : CefString(data_.text.string());
+}
+
+CefString CefDragDataImpl::GetFragmentHtml() {
+  base::AutoLock lock_scope(lock_);
+  return data_.html.is_null() ? CefString() : CefString(data_.html.string());
+}
+
+CefString CefDragDataImpl::GetFragmentBaseURL() {
+  base::AutoLock lock_scope(lock_);
+  return data_.html_base_url.spec();
+}
+
+CefString CefDragDataImpl::GetFileName() {
+  base::AutoLock lock_scope(lock_);
+  base::Optional<base::FilePath> filename =
+      data_.GetSafeFilenameForImageFileContents();
+  return filename ? CefString(filename->value()) : CefString();
+}
+
+size_t CefDragDataImpl::GetFileContents(CefRefPtr<CefStreamWriter> writer) {
+  base::AutoLock lock_scope(lock_);
+  if (data_.file_contents.empty())
+    return 0;
+
+  char* data = const_cast<char*>(data_.file_contents.c_str());
+  size_t size = data_.file_contents.size();
+
+  if (!writer.get())
+    return size;
+
+  return writer->Write(data, 1, size);
+}
+
+bool CefDragDataImpl::GetFileNames(std::vector<CefString>& names) {
+  base::AutoLock lock_scope(lock_);
+  if (data_.filenames.empty())
+    return false;
+
+  std::vector<ui::FileInfo>::const_iterator it = data_.filenames.begin();
+  for (; it != data_.filenames.end(); ++it)
+    names.push_back(it->path.value());
+
+  return true;
+}
+
+void CefDragDataImpl::SetLinkURL(const CefString& url) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.url = GURL(url.ToString());
+}
+
+void CefDragDataImpl::SetLinkTitle(const CefString& title) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.url_title = title.ToString16();
+}
+
+void CefDragDataImpl::SetLinkMetadata(const CefString& data) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.download_metadata = data.ToString16();
+}
+
+void CefDragDataImpl::SetFragmentText(const CefString& text) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.text = base::NullableString16(text.ToString16(), false);
+}
+
+void CefDragDataImpl::SetFragmentHtml(const CefString& fragment) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.html = base::NullableString16(fragment.ToString16(), false);
+}
+
+void CefDragDataImpl::SetFragmentBaseURL(const CefString& fragment) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.html_base_url = GURL(fragment.ToString());
+}
+
+void CefDragDataImpl::ResetFileContents() {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.file_contents.erase();
+  data_.file_contents_source_url = GURL();
+  data_.file_contents_filename_extension.erase();
+  data_.file_contents_content_disposition.erase();
+}
+
+void CefDragDataImpl::AddFile(const CefString& path,
+                              const CefString& display_name) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  data_.filenames.push_back(
+      ui::FileInfo(base::FilePath(path), base::FilePath(display_name)));
+}
+
+void CefDragDataImpl::SetReadOnly(bool read_only) {
+  base::AutoLock lock_scope(lock_);
+  if (read_only_ == read_only)
+    return;
+
+  read_only_ = read_only;
+}
+
+CefRefPtr<CefImage> CefDragDataImpl::GetImage() {
+  base::AutoLock lock_scope(lock_);
+  return image_;
+}
+
+CefPoint CefDragDataImpl::GetImageHotspot() {
+  base::AutoLock lock_scope(lock_);
+  return image_hotspot_;
+}
+
+bool CefDragDataImpl::HasImage() {
+  base::AutoLock lock_scope(lock_);
+  return image_ ? true : false;
+}
diff --git a/src/libcef/common/drag_data_impl.h b/src/libcef/common/drag_data_impl.h
new file mode 100644
index 0000000..c44f83c
--- /dev/null
+++ b/src/libcef/common/drag_data_impl.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_DRAG_DATA_IMPL_H_
+#define CEF_LIBCEF_COMMON_DRAG_DATA_IMPL_H_
+#pragma once
+
+#include "include/cef_drag_data.h"
+#include "include/cef_image.h"
+
+#include <vector>
+
+#include "base/synchronization/lock.h"
+#include "content/public/common/drop_data.h"
+
+// Implementation of CefDragData.
+class CefDragDataImpl : public CefDragData {
+ public:
+  CefDragDataImpl();
+  explicit CefDragDataImpl(const content::DropData& data);
+  CefDragDataImpl(const content::DropData& data,
+                  CefRefPtr<CefImage> image,
+                  const CefPoint& image_hotspot);
+
+  CefRefPtr<CefDragData> Clone() override;
+  bool IsReadOnly() override;
+  bool IsLink() override;
+  bool IsFragment() override;
+  bool IsFile() override;
+  CefString GetLinkURL() override;
+  CefString GetLinkTitle() override;
+  CefString GetLinkMetadata() override;
+  CefString GetFragmentText() override;
+  CefString GetFragmentHtml() override;
+  CefString GetFragmentBaseURL() override;
+  CefString GetFileName() override;
+  size_t GetFileContents(CefRefPtr<CefStreamWriter> writer) override;
+  bool GetFileNames(std::vector<CefString>& names) override;
+  void SetLinkURL(const CefString& url) override;
+  void SetLinkTitle(const CefString& title) override;
+  void SetLinkMetadata(const CefString& data) override;
+  void SetFragmentText(const CefString& text) override;
+  void SetFragmentHtml(const CefString& fragment) override;
+  void SetFragmentBaseURL(const CefString& fragment) override;
+  void ResetFileContents() override;
+  void AddFile(const CefString& path, const CefString& display_name) override;
+  CefRefPtr<CefImage> GetImage() override;
+  CefPoint GetImageHotspot() override;
+  bool HasImage() override;
+
+  // This method is not safe. Use Lock/Unlock to get mutually exclusive access.
+  content::DropData* drop_data() { return &data_; }
+
+  void SetReadOnly(bool read_only);
+
+  base::Lock& lock() { return lock_; }
+
+ private:
+  content::DropData data_;
+  CefRefPtr<CefImage> image_;
+  CefPoint image_hotspot_;
+
+  // True if this object is read-only.
+  bool read_only_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefDragDataImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_DRAG_DATA_IMPL_H_
diff --git a/src/libcef/common/extensions/api/BUILD.gn b/src/libcef/common/extensions/api/BUILD.gn
new file mode 100644
index 0000000..bb9ebc4
--- /dev/null
+++ b/src/libcef/common/extensions/api/BUILD.gn
@@ -0,0 +1,74 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+# 2014 the Chromium Authors. All rights reserved. Use of this source code is
+# governed by a BSD-style license that can be found in the LICENSE file.
+
+import("//tools/json_schema_compiler/json_features.gni")
+#import("//tools/json_schema_compiler/json_schema_api.gni")
+
+# TODO(cef): Enable if/when CEF exposes its own Mojo APIs. See README.txt for
+# details.
+#schema_sources = [
+#  # TODO(cef): Add CEF-specific Mojo APIs here.
+#]
+#
+#uncompiled_sources = [
+#]
+#
+#root_namespace = "extensions::api::cef::%(namespace)s"
+#schema_include_rules = "//cef/libcef/browser/extensions/api:extensions::api::cef::%(namespace)s"
+#schema_dependencies = [ "//extensions/common/api" ]
+#
+#generated_json_strings("api") {
+#  sources = schema_sources
+#  schemas = true
+#  configs = [ "//build/config:precompiled_headers" ]
+#  bundle_name = "Cef"
+#
+#  deps = schema_dependencies
+#}
+#
+#function_registration("api_registration") {
+#  sources = schema_sources
+#  impl_dir = "//cef/libcef/browser/extensions/api"
+#  configs = [ "//build/config:precompiled_headers" ]
+#  bundle_name = "Cef"
+#
+#  deps = [
+#    ":api",
+#  ]
+#  deps += schema_dependencies
+#}
+
+json_features("cef_api_features") {
+  feature_type = "APIFeature"
+  method_name = "AddCEFAPIFeatures"
+  sources = [
+    "_api_features.json",
+  ]
+}
+
+json_features("cef_permission_features") {
+  feature_type = "PermissionFeature"
+  method_name = "AddCEFPermissionFeatures"
+  sources = [
+    "_permission_features.json",
+  ]
+}
+
+json_features("cef_manifest_features") {
+  feature_type = "ManifestFeature"
+  method_name = "AddCEFManifestFeatures"
+  sources = [
+    # Use the same manifest features as Chrome.
+    "//chrome/common/extensions/api/_manifest_features.json",
+  ]
+}
+
+group("extensions_features") {
+  public_deps = [
+    ":cef_api_features",
+    ":cef_manifest_features",
+    ":cef_permission_features",
+    "//extensions/common/api:extensions_features",
+  ]
+}
diff --git a/src/libcef/common/extensions/api/README.txt b/src/libcef/common/extensions/api/README.txt
new file mode 100644
index 0000000..0fbbc8d
--- /dev/null
+++ b/src/libcef/common/extensions/api/README.txt
@@ -0,0 +1,66 @@
+This directory provides API definitions for CEF. Some extensions are implemented
+using Mojo and others use an older JSON-based format.
+
+  <api> is the name of the API definition (e.g. 'alarms').
+  <class> is the name of the class implementation (e.g. 'AlarmManager').
+
+To add a new extension API implemented only in CEF ***:
+
+1. Add libcef/common/extensions/api/<api>.idl or .json file which defines the
+   API.
+2. Add <api>.idl or .json to the 'schema_sources' list in
+   libcef/common/extensions/api/BUILD.gn. Serialization code will be
+   generated based on this list in step 4.
+3. Add libcef/browser/extensions/api/<api>/<api>_api.[h|cc] class implementation
+   files and associated entries to the 'libcef_static' target in BUILD.gn.
+4. Run the cef_create_projects script and build to generate the
+   cef/libcef/common/extensions/api/<api>.h file and other serialization code
+   required by the extensions system.
+5. Add an entry in the libcef/common/extensions/api/_*_features.json files if
+   necessary [1].
+6. Add an entry in the libcef/common/extensions/api/*_manifest_overlay.json
+   files if necessary [2].
+7. Call `<class>::GetInstance();` or `<class>Factory::GetFactoryInstance();` [3]
+   from EnsureBrowserContextKeyedServiceFactoriesBuilt in
+   libcef/browser/extensions/browser_context_keyed_service_factories.cc.
+8. Call `DependsOn(<class>Factory::GetInstance());` from
+   CefExtensionSystemFactory::CefExtensionSystemFactory in
+   libcef/browser/extensions/extension_system_factory.cc if necessary [3].
+
+*** Note that CEF does not currently expose its own Mojo APIs. Related code is
+commented out in:
+  cef/BUILD.gn
+  cef/libcef/common/extensions/api/BUILD.gn
+  CefExtensionsBrowserClient::RegisterExtensionFunctions
+  CefExtensionsClient::IsAPISchemaGenerated
+  CefExtensionsClient::GetAPISchema
+
+To add a new extension API implemented in Chrome:
+
+1. Register the API in libcef/browser/extensions/chrome_api_registration.cc
+2. Perform steps 5 through 8 above.
+
+See https://www.chromium.org/developers/design-documents/mojo for more
+information.
+
+[1] A feature can optionally express requirements for where it can be accessed.
+    See the _api_features.json and _permission_features.json files for
+    additional details. For Chrome extensions this should match the definitions
+    in the chrome/common/extensions/api/_*_features.json files.
+
+[2] Service Manifest InterfaceProviderSpecs control interfaces exposed between
+    processes. Mojo interfaces exposed at the frame level are controlled by the
+    "navigation:frame" dictionary. Those exposed at the process level are
+    controlled by the "service_manager:connector" dictionary. Failure to specify
+    this correctly may result in a console error like the following:
+
+      InterfaceProviderSpec "navigation:frame" prevented service:
+      service:content_renderer from binding interface:
+      mojom::Foo exposed by: service:content_browser
+
+[3] Some Mojo APIs use singleton Factory objects that create a one-to-one
+    relationship between a service and a BrowserContext. This is used primarily
+    to control shutdown/destruction order and implementors must explicitly state
+    which services are depended on. See comments in
+    components/keyed_service/content/browser_context_keyed_service_factory.h
+    for additional details.
diff --git a/src/libcef/common/extensions/api/_api_features.json b/src/libcef/common/extensions/api/_api_features.json
new file mode 100644
index 0000000..551fe28
--- /dev/null
+++ b/src/libcef/common/extensions/api/_api_features.json
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file defines extension APIs implemented in CEF.
+// See extensions/common/features/* to understand this file, in particular
+// feature.h, simple_feature.h, and feature_provider.h.
+
+// If APIs are defined in chrome then entries must also be added in
+// libcef/browser/extensions/chrome_api_registration.cc.
+
+{
+  // From chrome/common/extensions/api/_api_features.json.
+  // Required by the PDF extension which is hosted in a guest view.
+  "contentSettings": {
+    "dependencies": ["permission:contentSettings"],
+    "contexts": ["blessed_extension"]
+  },
+  "mimeHandlerViewGuestInternal": {
+    "internal": true,
+    "contexts": "all",
+    "channel": "stable",
+    "matches": ["<all_urls>"]
+  },
+  "resourcesPrivate": [
+    {
+      "dependencies": ["permission:resourcesPrivate"],
+      "contexts": ["blessed_extension"]
+    },
+    {
+      "channel": "stable",
+      "contexts": ["webui"],
+      "matches": ["chrome://print/*"]
+    }
+  ],
+  "tabs": {
+    "channel": "stable",
+    "extension_types": ["extension", "legacy_packaged_app"],
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
+  }
+}
diff --git a/src/libcef/common/extensions/api/_permission_features.json b/src/libcef/common/extensions/api/_permission_features.json
new file mode 100644
index 0000000..54458e0
--- /dev/null
+++ b/src/libcef/common/extensions/api/_permission_features.json
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file defines permissions for extension APIs implemented by CEF.
+// See extensions/common/features/* to understand this file, in particular
+// feature.h, simple_feature.h, and feature_provider.h.
+
+// If APIs are defined in chrome then entries must also be added in
+// libcef/browser/extensions/chrome_api_registration.cc.
+
+{
+  // From chrome/common/extensions/api/_permission_features.json.
+  // Required by the PDF extension which is hosted in a guest view.
+  "contentSettings": {
+    "channel": "stable",
+    "extension_types": ["extension", "legacy_packaged_app"]
+  },
+  "resourcesPrivate": {
+    "channel": "stable",
+    "extension_types": [
+      "extension", "legacy_packaged_app", "platform_app"
+    ],
+    "location": "component"
+  },
+  "tabs": [
+    {
+      "channel": "stable",
+      "extension_types": ["extension", "legacy_packaged_app"]
+    },
+    {
+      "channel": "stable",
+      "extension_types": ["platform_app"],
+      "whitelist": [
+        "AE27D69DBE571F4B1694F05C89B710C646792231", // Published ADT.
+        // TODO(grv): clean up once Apps developer tool is published.
+        "5107DE9024C329EEA9C9A72D94C16723790C6422"  // Apps Developer Tool.
+      ]
+    }
+  ]
+}
diff --git a/src/libcef/common/extensions/chrome_generated_schemas.cc b/src/libcef/common/extensions/chrome_generated_schemas.cc
new file mode 100644
index 0000000..cf7dc86
--- /dev/null
+++ b/src/libcef/common/extensions/chrome_generated_schemas.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/extensions/chrome_generated_schemas.h"
+
+#include "libcef/browser/extensions/chrome_api_registration.h"
+
+#include "base/macros.h"
+#include "chrome/common/extensions/api/generated_schemas.h"
+
+namespace extensions {
+namespace api {
+namespace cef {
+
+// static
+base::StringPiece ChromeGeneratedSchemas::Get(const std::string& name) {
+  if (!ChromeFunctionRegistry::IsSupported(name))
+    return base::StringPiece();
+  return extensions::api::ChromeGeneratedSchemas::Get(name);
+}
+
+// static
+bool ChromeGeneratedSchemas::IsGenerated(std::string name) {
+  if (!ChromeFunctionRegistry::IsSupported(name))
+    return false;
+  return extensions::api::ChromeGeneratedSchemas::IsGenerated(name);
+}
+
+}  // namespace cef
+}  // namespace api
+}  // namespace extensions
diff --git a/src/libcef/common/extensions/chrome_generated_schemas.h b/src/libcef/common/extensions/chrome_generated_schemas.h
new file mode 100644
index 0000000..dbb6bd7
--- /dev/null
+++ b/src/libcef/common/extensions/chrome_generated_schemas.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// GENERATED FROM THE API DEFINITIONS IN
+//   chrome\common\extensions\api
+// DO NOT EDIT.
+
+#ifndef CEF_LIBCEF_COMMON_EXTENSIONS_CHROME_GENERATED_SCHEMAS_H_
+#define CEF_LIBCEF_COMMON_EXTENSIONS_CHROME_GENERATED_SCHEMAS_H_
+
+#include <map>
+#include <string>
+
+#include "base/strings/string_piece.h"
+
+namespace extensions {
+namespace api {
+namespace cef {
+
+class ChromeGeneratedSchemas {
+ public:
+  // Determines if schema named |name| is generated.
+  static bool IsGenerated(std::string name);
+
+  // Gets the API schema named |name|.
+  static base::StringPiece Get(const std::string& name);
+};
+
+}  // namespace cef
+}  // namespace api
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_COMMON_EXTENSIONS_CHROME_GENERATED_SCHEMAS_H_
diff --git a/src/libcef/common/extensions/extensions_api_provider.cc b/src/libcef/common/extensions/extensions_api_provider.cc
new file mode 100644
index 0000000..64df67c
--- /dev/null
+++ b/src/libcef/common/extensions/extensions_api_provider.cc
@@ -0,0 +1,87 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/extensions/extensions_api_provider.h"
+
+#include "libcef/common/extensions/chrome_generated_schemas.h"
+
+#include "cef/grit/cef_resources.h"
+//#include "cef/libcef/common/extensions/api/generated_schemas.h"
+#include "cef/libcef/common/extensions/api/cef_api_features.h"
+#include "cef/libcef/common/extensions/api/cef_manifest_features.h"
+#include "cef/libcef/common/extensions/api/cef_permission_features.h"
+#include "chrome/common/extensions/chrome_manifest_handlers.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
+#include "extensions/common/features/json_feature_provider_source.h"
+#include "extensions/common/permissions/permissions_info.h"
+
+namespace extensions {
+
+CefExtensionsAPIProvider::CefExtensionsAPIProvider() {}
+
+void CefExtensionsAPIProvider::AddAPIFeatures(FeatureProvider* provider) {
+  AddCEFAPIFeatures(provider);
+}
+
+void CefExtensionsAPIProvider::AddManifestFeatures(FeatureProvider* provider) {
+  AddCEFManifestFeatures(provider);
+}
+
+void CefExtensionsAPIProvider::AddPermissionFeatures(
+    FeatureProvider* provider) {
+  AddCEFPermissionFeatures(provider);
+}
+
+void CefExtensionsAPIProvider::AddBehaviorFeatures(FeatureProvider* provider) {
+  // No CEF-specific behavior features.
+}
+
+void CefExtensionsAPIProvider::AddAPIJSONSources(
+    JSONFeatureProviderSource* json_source) {
+  // Extension API features specific to CEF. See
+  // libcef/common/extensions/api/README.txt for additional details.
+  json_source->LoadJSON(IDR_CEF_EXTENSION_API_FEATURES);
+}
+
+bool CefExtensionsAPIProvider::IsAPISchemaGenerated(const std::string& name) {
+  // Schema for CEF-only APIs.
+  // TODO(cef): Enable if/when CEF exposes its own Mojo APIs. See
+  // libcef/common/extensions/api/README.txt for details.
+  // if (api::cef::CefGeneratedSchemas::IsGenerated(name))
+  //   return true;
+
+  // Chrome APIs whitelisted by CEF.
+  if (api::cef::ChromeGeneratedSchemas::IsGenerated(name))
+    return true;
+
+  return false;
+}
+
+base::StringPiece CefExtensionsAPIProvider::GetAPISchema(
+    const std::string& name) {
+  // Schema for CEF-only APIs.
+  // TODO(cef): Enable if/when CEF exposes its own Mojo APIs. See
+  // libcef/common/extensions/api/README.txt for details.
+  // if (api::cef::CefGeneratedSchemas::IsGenerated(name))
+  //   return api::cef::CefGeneratedSchemas::Get(name);
+
+  // Chrome APIs whitelisted by CEF.
+  if (api::cef::ChromeGeneratedSchemas::IsGenerated(name))
+    return api::cef::ChromeGeneratedSchemas::Get(name);
+
+  return base::StringPiece();
+}
+
+void CefExtensionsAPIProvider::RegisterPermissions(
+    PermissionsInfo* permissions_info) {
+  permissions_info->RegisterPermissions(
+      chrome_api_permissions::GetPermissionInfos(),
+      chrome_api_permissions::GetPermissionAliases());
+}
+
+void CefExtensionsAPIProvider::RegisterManifestHandlers() {
+  RegisterChromeManifestHandlers();
+}
+
+}  // namespace extensions
diff --git a/src/libcef/common/extensions/extensions_api_provider.h b/src/libcef/common/extensions/extensions_api_provider.h
new file mode 100644
index 0000000..805e18a
--- /dev/null
+++ b/src/libcef/common/extensions/extensions_api_provider.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_API_PROVIDER_H_
+#define CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_API_PROVIDER_H_
+
+#include "base/macros.h"
+#include "extensions/common/extensions_api_provider.h"
+
+namespace extensions {
+
+class CefExtensionsAPIProvider : public ExtensionsAPIProvider {
+ public:
+  CefExtensionsAPIProvider();
+
+  // ExtensionsAPIProvider:
+  void AddAPIFeatures(FeatureProvider* provider) override;
+  void AddManifestFeatures(FeatureProvider* provider) override;
+  void AddPermissionFeatures(FeatureProvider* provider) override;
+  void AddBehaviorFeatures(FeatureProvider* provider) override;
+  void AddAPIJSONSources(JSONFeatureProviderSource* json_source) override;
+  bool IsAPISchemaGenerated(const std::string& name) override;
+  base::StringPiece GetAPISchema(const std::string& name) override;
+  void RegisterPermissions(PermissionsInfo* permissions_info) override;
+  void RegisterManifestHandlers() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionsAPIProvider);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_API_PROVIDER_H_
diff --git a/src/libcef/common/extensions/extensions_client.cc b/src/libcef/common/extensions/extensions_client.cc
new file mode 100644
index 0000000..b62c0d5
--- /dev/null
+++ b/src/libcef/common/extensions/extensions_client.cc
@@ -0,0 +1,97 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/extensions/extensions_client.h"
+
+#include <utility>
+
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/extensions/extensions_api_provider.h"
+
+#include "base/logging.h"
+#include "extensions/common/core_extensions_api_provider.h"
+#include "extensions/common/extension_urls.h"
+#include "extensions/common/features/simple_feature.h"
+#include "extensions/common/permissions/permission_message_provider.h"
+#include "extensions/common/url_pattern_set.h"
+
+namespace extensions {
+
+namespace {
+
+template <class FeatureClass>
+SimpleFeature* CreateFeature() {
+  return new FeatureClass;
+}
+
+}  // namespace
+
+CefExtensionsClient::CefExtensionsClient()
+    : webstore_base_url_(extension_urls::kChromeWebstoreBaseURL),
+      webstore_update_url_(extension_urls::kChromeWebstoreUpdateURL) {
+  AddAPIProvider(std::make_unique<CoreExtensionsAPIProvider>());
+  AddAPIProvider(std::make_unique<CefExtensionsAPIProvider>());
+}
+
+CefExtensionsClient::~CefExtensionsClient() {}
+
+void CefExtensionsClient::Initialize() {}
+
+void CefExtensionsClient::InitializeWebStoreUrls(
+    base::CommandLine* command_line) {}
+
+const PermissionMessageProvider&
+CefExtensionsClient::GetPermissionMessageProvider() const {
+  return permission_message_provider_;
+}
+
+const std::string CefExtensionsClient::GetProductName() {
+  return "cef";
+}
+
+void CefExtensionsClient::FilterHostPermissions(
+    const URLPatternSet& hosts,
+    URLPatternSet* new_hosts,
+    PermissionIDSet* permissions) const {
+  NOTIMPLEMENTED();
+}
+
+void CefExtensionsClient::SetScriptingWhitelist(
+    const ScriptingWhitelist& whitelist) {
+  scripting_whitelist_ = whitelist;
+}
+
+const ExtensionsClient::ScriptingWhitelist&
+CefExtensionsClient::GetScriptingWhitelist() const {
+  // TODO(jamescook): Real whitelist.
+  return scripting_whitelist_;
+}
+
+URLPatternSet CefExtensionsClient::GetPermittedChromeSchemeHosts(
+    const Extension* extension,
+    const APIPermissionSet& api_permissions) const {
+  return URLPatternSet();
+}
+
+bool CefExtensionsClient::IsScriptableURL(const GURL& url,
+                                          std::string* error) const {
+  return true;
+}
+
+const GURL& CefExtensionsClient::GetWebstoreBaseURL() const {
+  return webstore_base_url_;
+}
+
+const GURL& CefExtensionsClient::GetWebstoreUpdateURL() const {
+  return webstore_update_url_;
+}
+
+bool CefExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const {
+  // TODO(rockot): Maybe we want to do something else here. For now we accept
+  // any URL as a blacklist URL because we don't really care.
+  return true;
+}
+
+}  // namespace extensions
diff --git a/src/libcef/common/extensions/extensions_client.h b/src/libcef/common/extensions/extensions_client.h
new file mode 100644
index 0000000..a1fd438
--- /dev/null
+++ b/src/libcef/common/extensions/extensions_client.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_CLIENT_H_
+#define CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_CLIENT_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
+#include "extensions/common/extensions_client.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+// The CEF implementation of ExtensionsClient.
+class CefExtensionsClient : public ExtensionsClient {
+ public:
+  CefExtensionsClient();
+  ~CefExtensionsClient() override;
+
+  // ExtensionsClient overrides:
+  void Initialize() override;
+  void InitializeWebStoreUrls(base::CommandLine* command_line) override;
+  const PermissionMessageProvider& GetPermissionMessageProvider()
+      const override;
+  const std::string GetProductName() override;
+  void FilterHostPermissions(const URLPatternSet& hosts,
+                             URLPatternSet* new_hosts,
+                             PermissionIDSet* permissions) const override;
+  void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) override;
+  const ScriptingWhitelist& GetScriptingWhitelist() const override;
+  URLPatternSet GetPermittedChromeSchemeHosts(
+      const Extension* extension,
+      const APIPermissionSet& api_permissions) const override;
+  bool IsScriptableURL(const GURL& url, std::string* error) const override;
+  const GURL& GetWebstoreBaseURL() const override;
+  const GURL& GetWebstoreUpdateURL() const override;
+  bool IsBlacklistUpdateURL(const GURL& url) const override;
+
+ private:
+  const ChromePermissionMessageProvider permission_message_provider_;
+
+  ScriptingWhitelist scripting_whitelist_;
+
+  // Mutable to allow caching in a const method.
+  const GURL webstore_base_url_;
+  const GURL webstore_update_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionsClient);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_CLIENT_H_
diff --git a/src/libcef/common/extensions/extensions_util.cc b/src/libcef/common/extensions/extensions_util.cc
new file mode 100644
index 0000000..74f0ad8
--- /dev/null
+++ b/src/libcef/common/extensions/extensions_util.cc
@@ -0,0 +1,45 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "libcef/common/cef_switches.h"
+
+#include "base/command_line.h"
+#include "chrome/common/chrome_switches.h"
+
+namespace extensions {
+
+bool ExtensionsEnabled() {
+  static bool enabled = !base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kDisableExtensions);
+  return enabled;
+}
+
+bool PdfExtensionEnabled() {
+  static bool enabled =
+      ExtensionsEnabled() && !base::CommandLine::ForCurrentProcess()->HasSwitch(
+                                 switches::kDisablePdfExtension);
+  return enabled;
+}
+
+bool PrintPreviewEnabled() {
+#if defined(OS_MACOSX)
+  // Not currently supported on macOS.
+  return false;
+#else
+  if (!PdfExtensionEnabled())
+    return false;
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisablePrintPreview)) {
+    return false;
+  }
+
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnablePrintPreview);
+#endif
+}
+
+}  // namespace extensions
diff --git a/src/libcef/common/extensions/extensions_util.h b/src/libcef/common/extensions/extensions_util.h
new file mode 100644
index 0000000..6bcbdde
--- /dev/null
+++ b/src/libcef/common/extensions/extensions_util.h
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_UTIL_H_
+#define CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_UTIL_H_
+
+namespace extensions {
+
+// Returns true if extensions have not been disabled via the command-line.
+bool ExtensionsEnabled();
+
+// Returns true if the PDF extension has not been disabled via the command-line.
+bool PdfExtensionEnabled();
+
+// Returns true if Print Preview has been enabled via the command-line.
+bool PrintPreviewEnabled();
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_COMMON_EXTENSIONS_EXTENSIONS_UTIL_H_
diff --git a/src/libcef/common/file_util_impl.cc b/src/libcef/common/file_util_impl.cc
new file mode 100644
index 0000000..3f97c75
--- /dev/null
+++ b/src/libcef/common/file_util_impl.cc
@@ -0,0 +1,86 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_file_util.h"
+
+#include "include/cef_task.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "third_party/zlib/google/zip.h"
+
+namespace {
+
+bool AllowFileIO() {
+  if (CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO)) {
+    NOTREACHED() << "file IO is not allowed on the current thread";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+bool CefCreateDirectory(const CefString& full_path) {
+  if (!AllowFileIO())
+    return false;
+  return base::CreateDirectory(full_path);
+}
+
+bool CefGetTempDirectory(CefString& temp_dir) {
+  if (!AllowFileIO())
+    return false;
+  base::FilePath result;
+  if (base::GetTempDir(&result)) {
+    temp_dir = result.value();
+    return true;
+  }
+  return false;
+}
+
+bool CefCreateNewTempDirectory(const CefString& prefix,
+                               CefString& new_temp_path) {
+  if (!AllowFileIO())
+    return false;
+  base::FilePath result;
+  if (base::CreateNewTempDirectory(prefix, &result)) {
+    new_temp_path = result.value();
+    return true;
+  }
+  return false;
+}
+
+bool CefCreateTempDirectoryInDirectory(const CefString& base_dir,
+                                       const CefString& prefix,
+                                       CefString& new_dir) {
+  if (!AllowFileIO())
+    return false;
+  base::FilePath result;
+  if (base::CreateTemporaryDirInDir(base_dir, prefix, &result)) {
+    new_dir = result.value();
+    return true;
+  }
+  return false;
+}
+
+bool CefDirectoryExists(const CefString& path) {
+  if (!AllowFileIO())
+    return false;
+  return base::DirectoryExists(path);
+}
+
+bool CefDeleteFile(const CefString& path, bool recursive) {
+  if (!AllowFileIO())
+    return false;
+  return base::DeleteFile(path, recursive);
+}
+
+bool CefZipDirectory(const CefString& src_dir,
+                     const CefString& dest_file,
+                     bool include_hidden_files) {
+  if (!AllowFileIO())
+    return false;
+  return zip::Zip(src_dir, dest_file, include_hidden_files);
+}
diff --git a/src/libcef/common/frame_util.cc b/src/libcef/common/frame_util.cc
new file mode 100644
index 0000000..2671329
--- /dev/null
+++ b/src/libcef/common/frame_util.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/common/frame_util.h"
+
+namespace frame_util {
+
+int64_t MakeFrameId(int32_t render_process_id, int32_t render_routing_id) {
+  return (static_cast<uint64_t>(render_process_id) << 32) |
+         static_cast<uint64_t>(render_routing_id);
+}
+
+}  // namespace frame_util
diff --git a/src/libcef/common/frame_util.h b/src/libcef/common/frame_util.h
new file mode 100644
index 0000000..7ca5d7b
--- /dev/null
+++ b/src/libcef/common/frame_util.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_FRAME_UTIL_H_
+#define CEF_LIBCEF_COMMON_FRAME_UTIL_H_
+
+#include <stdint.h>
+
+namespace frame_util {
+
+// Returns the frame ID, which is a 64-bit combination of |render_process_id|
+// and |render_routing_id|.
+int64_t MakeFrameId(int32_t render_process_id, int32_t render_routing_id);
+
+}  // namespace frame_util
+
+#endif  // CEF_LIBCEF_COMMON_FRAME_UTIL_H_
diff --git a/src/libcef/common/json_impl.cc b/src/libcef/common/json_impl.cc
new file mode 100644
index 0000000..d4cf45e
--- /dev/null
+++ b/src/libcef/common/json_impl.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_parser.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/values.h"
+
+namespace {
+
+int GetJSONReaderOptions(cef_json_parser_options_t options) {
+  int op = base::JSON_PARSE_RFC;
+  if (options & JSON_PARSER_ALLOW_TRAILING_COMMAS)
+    op |= base::JSON_ALLOW_TRAILING_COMMAS;
+  return op;
+}
+
+int GetJSONWriterOptions(cef_json_writer_options_t options) {
+  int op = 0;
+  if (options & JSON_WRITER_OMIT_BINARY_VALUES)
+    op |= base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES;
+  if (options & JSON_WRITER_OMIT_DOUBLE_TYPE_PRESERVATION)
+    op |= base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION;
+  if (options & JSON_WRITER_PRETTY_PRINT)
+    op |= base::JSONWriter::OPTIONS_PRETTY_PRINT;
+  return op;
+}
+
+}  // namespace
+
+CefRefPtr<CefValue> CefParseJSON(const CefString& json_string,
+                                 cef_json_parser_options_t options) {
+  const std::string& json = json_string.ToString();
+  return CefParseJSON(json.data(), json.size(), options);
+}
+
+CefRefPtr<CefValue> CefParseJSON(const void* json,
+                                 size_t json_size,
+                                 cef_json_parser_options_t options) {
+  if (!json || json_size == 0)
+    return nullptr;
+  base::Optional<base::Value> parse_result = base::JSONReader::Read(
+      base::StringPiece(static_cast<const char*>(json), json_size),
+      GetJSONReaderOptions(options));
+  if (parse_result) {
+    return new CefValueImpl(
+        base::Value::ToUniquePtrValue(std::move(parse_result.value()))
+            .release());
+  }
+  return nullptr;
+}
+
+CefRefPtr<CefValue> CefParseJSONAndReturnError(
+    const CefString& json_string,
+    cef_json_parser_options_t options,
+    cef_json_parser_error_t& error_code_out,
+    CefString& error_msg_out) {
+  const std::string& json = json_string.ToString();
+
+  std::string error_msg;
+  base::JSONReader::ValueWithError value_and_error =
+      base::JSONReader::ReadAndReturnValueWithError(
+          json, GetJSONReaderOptions(options));
+  if (value_and_error.value) {
+    return new CefValueImpl(
+        base::Value::ToUniquePtrValue(std::move(value_and_error.value.value()))
+            .release());
+  }
+
+  error_code_out =
+      static_cast<cef_json_parser_error_t>(value_and_error.error_code);
+  error_msg_out = value_and_error.error_message;
+  return nullptr;
+}
+
+CefString CefWriteJSON(CefRefPtr<CefValue> node,
+                       cef_json_writer_options_t options) {
+  if (!node.get() || !node->IsValid())
+    return CefString();
+
+  CefValueImpl* impl = static_cast<CefValueImpl*>(node.get());
+  CefValueImpl::ScopedLockedValue scoped_value(impl);
+
+  std::string json_string;
+  if (base::JSONWriter::WriteWithOptions(
+          *scoped_value.value(), GetJSONWriterOptions(options), &json_string)) {
+    return json_string;
+  }
+  return CefString();
+}
diff --git a/src/libcef/common/main_delegate.cc b/src/libcef/common/main_delegate.cc
new file mode 100644
index 0000000..a68020e
--- /dev/null
+++ b/src/libcef/common/main_delegate.cc
@@ -0,0 +1,985 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/main_delegate.h"
+
+#if defined(OS_LINUX)
+#include <dlfcn.h>
+#endif
+
+#include "libcef/browser/browser_message_loop.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/context.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/command_line_impl.h"
+#include "libcef/common/crash_reporting.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/renderer/content_renderer_client.h"
+
+#include "base/at_exit.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/child/pdf_child_init.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_paths_internal.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/utility/chrome_content_utility_client.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
+#include "components/viz/common/features.h"
+#include "content/browser/browser_process_sub_thread.h"
+#include "content/browser/scheduler/browser_task_executor.h"
+#include "content/public/browser/browser_main_runner.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/main_function_params.h"
+#include "extensions/common/constants.h"
+#include "ipc/ipc_buildflags.h"
+#include "net/base/features.h"
+#include "pdf/pdf_ppapi.h"
+#include "services/network/public/cpp/features.h"
+#include "services/service_manager/sandbox/switches.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/base/ui_base_paths.h"
+#include "ui/base/ui_base_switches.h"
+
+#if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
+#define IPC_MESSAGE_MACROS_LOG_ENABLED
+#include "content/public/common/content_ipc_logging.h"
+#define IPC_LOG_TABLE_ADD_ENTRY(msg_id, logger) \
+  content::RegisterIPCLogger(msg_id, logger)
+#include "libcef/common/cef_message_generator.h"
+#endif
+
+#if defined(OS_WIN)
+#include <Objbase.h>
+#include "base/win/registry.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
+#include "content/public/common/content_paths.h"
+#include "libcef/common/util_mac.h"
+#endif
+
+#if defined(OS_LINUX)
+#include "base/environment.h"
+#include "base/nix/xdg_util.h"
+#endif
+
+namespace {
+
+const char* const kNonWildcardDomainNonPortSchemes[] = {
+    extensions::kExtensionScheme};
+const size_t kNonWildcardDomainNonPortSchemesSize =
+    base::size(kNonWildcardDomainNonPortSchemes);
+
+#if defined(OS_MACOSX)
+
+base::FilePath GetResourcesFilePath() {
+  return util_mac::GetFrameworkResourcesDirectory();
+}
+
+// Use a "~/Library/Logs/<app name>_debug.log" file where <app name> is the name
+// of the running executable.
+base::FilePath GetDefaultLogFile() {
+  std::string exe_name = util_mac::GetMainProcessPath().BaseName().value();
+  return base::mac::GetUserLibraryPath()
+      .Append(FILE_PATH_LITERAL("Logs"))
+      .Append(FILE_PATH_LITERAL(exe_name + "_debug.log"));
+}
+
+void OverrideFrameworkBundlePath() {
+  base::FilePath framework_path = util_mac::GetFrameworkDirectory();
+  DCHECK(!framework_path.empty());
+
+  base::mac::SetOverrideFrameworkBundlePath(framework_path);
+}
+
+void OverrideOuterBundlePath() {
+  base::FilePath bundle_path = util_mac::GetMainBundlePath();
+  DCHECK(!bundle_path.empty());
+
+  base::mac::SetOverrideOuterBundlePath(bundle_path);
+}
+
+void OverrideBaseBundleID() {
+  std::string bundle_id = util_mac::GetMainBundleID();
+  DCHECK(!bundle_id.empty());
+
+  base::mac::SetBaseBundleID(bundle_id.c_str());
+}
+
+void OverrideChildProcessPath() {
+  base::FilePath child_process_path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+          switches::kBrowserSubprocessPath);
+
+  if (child_process_path.empty()) {
+    child_process_path = util_mac::GetChildProcessPath();
+    DCHECK(!child_process_path.empty());
+  }
+
+  // Used by ChildProcessHost::GetChildPath and PlatformCrashpadInitialization.
+  base::PathService::Override(content::CHILD_PROCESS_EXE, child_process_path);
+}
+
+#else  // !defined(OS_MACOSX)
+
+base::FilePath GetResourcesFilePath() {
+  base::FilePath pak_dir;
+  base::PathService::Get(base::DIR_ASSETS, &pak_dir);
+  return pak_dir;
+}
+
+// Use a "debug.log" file in the running executable's directory.
+base::FilePath GetDefaultLogFile() {
+  base::FilePath log_path;
+  base::PathService::Get(base::DIR_EXE, &log_path);
+  return log_path.Append(FILE_PATH_LITERAL("debug.log"));
+}
+
+#endif  // !defined(OS_MACOSX)
+
+#if defined(OS_WIN)
+
+// Gets the Flash path if installed on the system.
+bool GetSystemFlashFilename(base::FilePath* out_path) {
+  const wchar_t kPepperFlashRegistryRoot[] =
+      L"SOFTWARE\\Macromedia\\FlashPlayerPepper";
+  const wchar_t kFlashPlayerPathValueName[] = L"PlayerPath";
+
+  base::win::RegKey path_key(HKEY_LOCAL_MACHINE, kPepperFlashRegistryRoot,
+                             KEY_READ);
+  base::string16 path_str;
+  if (FAILED(path_key.ReadValue(kFlashPlayerPathValueName, &path_str)))
+    return false;
+
+  *out_path = base::FilePath(path_str);
+  return true;
+}
+
+#elif defined(OS_MACOSX)
+
+const base::FilePath::CharType kPepperFlashSystemBaseDirectory[] =
+    FILE_PATH_LITERAL("Internet Plug-Ins/PepperFlashPlayer");
+
+#endif
+
+void OverridePepperFlashSystemPluginPath() {
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  base::FilePath plugin_filename;
+#if defined(OS_WIN)
+  if (!GetSystemFlashFilename(&plugin_filename))
+    return;
+#elif defined(OS_MACOSX)
+  if (!util_mac::GetLocalLibraryDirectory(&plugin_filename))
+    return;
+  plugin_filename = plugin_filename.Append(kPepperFlashSystemBaseDirectory)
+                        .Append(chrome::kPepperFlashPluginFilename);
+#endif  // defined(OS_MACOSX)
+
+  if (!plugin_filename.empty()) {
+    base::PathService::Override(chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN,
+                                plugin_filename);
+  }
+#else  // !(defined(OS_WIN) || defined(OS_MACOSX))
+  // A system plugin is not available on other platforms.
+  return;
+#endif
+}
+
+#if defined(OS_LINUX)
+
+// Based on chrome/common/chrome_paths_linux.cc.
+// See http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+// for a spec on where config files go.  The net effect for most
+// systems is we use ~/.config/chromium/ for Chromium and
+// ~/.config/google-chrome/ for official builds.
+// (This also helps us sidestep issues with other apps grabbing ~/.chromium .)
+bool GetDefaultUserDataDirectory(base::FilePath* result) {
+  std::unique_ptr<base::Environment> env(base::Environment::Create());
+  base::FilePath config_dir(base::nix::GetXDGDirectory(
+      env.get(), base::nix::kXdgConfigHomeEnvVar, base::nix::kDotConfigDir));
+  *result = config_dir.Append(FILE_PATH_LITERAL("cef_user_data"));
+  return true;
+}
+
+#elif defined(OS_MACOSX)
+
+// Based on chrome/common/chrome_paths_mac.mm.
+bool GetDefaultUserDataDirectory(base::FilePath* result) {
+  if (!base::PathService::Get(base::DIR_APP_DATA, result))
+    return false;
+  *result = result->Append(FILE_PATH_LITERAL("CEF"));
+  *result = result->Append(FILE_PATH_LITERAL("User Data"));
+  return true;
+}
+
+#elif defined(OS_WIN)
+
+// Based on chrome/common/chrome_paths_win.cc.
+bool GetDefaultUserDataDirectory(base::FilePath* result) {
+  if (!base::PathService::Get(base::DIR_LOCAL_APP_DATA, result))
+    return false;
+  *result = result->Append(FILE_PATH_LITERAL("CEF"));
+  *result = result->Append(FILE_PATH_LITERAL("User Data"));
+  return true;
+}
+
+#endif
+
+base::FilePath GetUserDataPath() {
+  const CefSettings& settings = CefContext::Get()->settings();
+  if (settings.user_data_path.length > 0)
+    return base::FilePath(CefString(&settings.user_data_path));
+
+  base::FilePath result;
+  if (GetDefaultUserDataDirectory(&result))
+    return result;
+
+  if (base::PathService::Get(base::DIR_TEMP, &result))
+    return result;
+
+  NOTREACHED();
+  return result;
+}
+
+bool GetDefaultDownloadDirectory(base::FilePath* result) {
+  // This will return the safe download directory if necessary.
+  return chrome::GetUserDownloadsDirectory(result);
+}
+
+// From chrome/browser/download/download_prefs.cc.
+// Consider downloads 'dangerous' if they go to the home directory on Linux and
+// to the desktop on any platform.
+bool DownloadPathIsDangerous(const base::FilePath& download_path) {
+#if defined(OS_LINUX)
+  base::FilePath home_dir = base::GetHomeDir();
+  if (download_path == home_dir) {
+    return true;
+  }
+#endif
+
+  base::FilePath desktop_dir;
+  if (!base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_dir)) {
+    NOTREACHED();
+    return false;
+  }
+  return (download_path == desktop_dir);
+}
+
+bool GetDefaultDownloadSafeDirectory(base::FilePath* result) {
+  // Start with the default download directory.
+  if (!GetDefaultDownloadDirectory(result))
+    return false;
+
+  if (DownloadPathIsDangerous(*result)) {
+#if defined(OS_WIN) || defined(OS_LINUX)
+    // Explicitly switch to the safe download directory.
+    return chrome::GetUserDownloadsDirectorySafe(result);
+#else
+    // No viable alternative on macOS.
+    return false;
+#endif
+  }
+
+  return true;
+}
+
+// Returns true if |scale_factor| is supported by this platform.
+// Same as ui::ResourceBundle::IsScaleFactorSupported.
+bool IsScaleFactorSupported(ui::ScaleFactor scale_factor) {
+  const std::vector<ui::ScaleFactor>& supported_scale_factors =
+      ui::GetSupportedScaleFactors();
+  return std::find(supported_scale_factors.begin(),
+                   supported_scale_factors.end(),
+                   scale_factor) != supported_scale_factors.end();
+}
+
+#if defined(OS_LINUX)
+// Look for binary files (*.bin, *.dat, *.pak, chrome-sandbox, libGLESv2.so,
+// libEGL.so, locales/*.pak, swiftshader/*.so) next to libcef instead of the exe
+// on Linux. This is already the default on Windows.
+void OverrideAssetPath() {
+  Dl_info dl_info;
+  if (dladdr(reinterpret_cast<const void*>(&OverrideAssetPath), &dl_info)) {
+    base::FilePath path = base::FilePath(dl_info.dli_fname).DirName();
+    base::PathService::Override(base::DIR_ASSETS, path);
+  }
+}
+#endif
+
+}  // namespace
+
+// Used to run the UI on a separate thread.
+class CefUIThread : public base::PlatformThread::Delegate {
+ public:
+  explicit CefUIThread(base::OnceClosure setup_callback)
+      : setup_callback_(std::move(setup_callback)) {}
+  ~CefUIThread() override { Stop(); }
+
+  void Start() {
+    base::AutoLock lock(thread_lock_);
+    bool success = base::PlatformThread::CreateWithPriority(
+        0, this, &thread_, base::ThreadPriority::NORMAL);
+    if (!success) {
+      LOG(FATAL) << "failed to UI create thread";
+    }
+  }
+
+  void Stop() {
+    base::AutoLock lock(thread_lock_);
+
+    if (!stopping_) {
+      stopping_ = true;
+      base::PostTask(
+          FROM_HERE, {content::BrowserThread::UI},
+          base::BindOnce(&CefUIThread::ThreadQuitHelper, Unretained(this)));
+    }
+
+    // Can't join if the |thread_| is either already gone or is non-joinable.
+    if (thread_.is_null())
+      return;
+
+    base::PlatformThread::Join(thread_);
+    thread_ = base::PlatformThreadHandle();
+
+    stopping_ = false;
+  }
+
+  bool WaitUntilThreadStarted() const {
+    DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+    start_event_.Wait();
+    return true;
+  }
+
+  void InitializeBrowserRunner(
+      const content::MainFunctionParams& main_function_params) {
+    // Use our own browser process runner.
+    browser_runner_ = content::BrowserMainRunner::Create();
+
+    // Initialize browser process state. Uses the current thread's message loop.
+    int exit_code = browser_runner_->Initialize(main_function_params);
+    CHECK_EQ(exit_code, -1);
+  }
+
+ protected:
+  void ThreadMain() override {
+    base::PlatformThread::SetName("CefUIThread");
+
+#if defined(OS_WIN)
+    // Initializes the COM library on the current thread.
+    CoInitialize(nullptr);
+#endif
+
+    start_event_.Signal();
+
+    std::move(setup_callback_).Run();
+
+    base::RunLoop run_loop;
+    run_loop_ = &run_loop;
+    run_loop.Run();
+
+    browser_runner_->Shutdown();
+    browser_runner_.reset(nullptr);
+
+    content::BrowserTaskExecutor::Shutdown();
+
+    // Run exit callbacks on the UI thread to avoid sequence check failures.
+    base::AtExitManager::ProcessCallbacksNow();
+
+#if defined(OS_WIN)
+    // Closes the COM library on the current thread. CoInitialize must
+    // be balanced by a corresponding call to CoUninitialize.
+    CoUninitialize();
+#endif
+
+    run_loop_ = nullptr;
+  }
+
+  void ThreadQuitHelper() {
+    DCHECK(run_loop_);
+    run_loop_->QuitWhenIdle();
+  }
+
+  std::unique_ptr<content::BrowserMainRunner> browser_runner_;
+  base::OnceClosure setup_callback_;
+
+  bool stopping_ = false;
+
+  // The thread's handle.
+  base::PlatformThreadHandle thread_;
+  mutable base::Lock thread_lock_;  // Protects |thread_|.
+
+  base::RunLoop* run_loop_ = nullptr;
+
+  mutable base::WaitableEvent start_event_;
+
+  // This class is not thread-safe, use this to verify access from the owning
+  // sequence of the Thread.
+  base::SequenceChecker owning_sequence_checker_;
+};
+
+CefMainDelegate::CefMainDelegate(CefRefPtr<CefApp> application)
+    : content_client_(application) {
+  // Necessary so that exported functions from base_impl.cc will be included
+  // in the binary.
+  extern void base_impl_stub();
+  base_impl_stub();
+
+#if defined(OS_LINUX)
+  OverrideAssetPath();
+#endif
+}
+
+CefMainDelegate::~CefMainDelegate() {}
+
+void CefMainDelegate::PreCreateMainMessageLoop() {
+  InitMessagePumpFactoryForUI();
+}
+
+bool CefMainDelegate::BasicStartupComplete(int* exit_code) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  std::string process_type =
+      command_line->GetSwitchValueASCII(switches::kProcessType);
+
+#if defined(OS_POSIX)
+  // Read the crash configuration file. Platforms using Breakpad also add a
+  // command-line switch. On Windows this is done from chrome_elf.
+  crash_reporting::BasicStartupComplete(command_line);
+#endif
+
+  if (process_type.empty()) {
+    // In the browser process. Populate the global command-line object.
+    const CefSettings& settings = CefContext::Get()->settings();
+
+    if (settings.command_line_args_disabled) {
+      // Remove any existing command-line arguments.
+      base::CommandLine::StringVector argv;
+      argv.push_back(command_line->GetProgram().value());
+      command_line->InitFromArgv(argv);
+
+      const base::CommandLine::SwitchMap& map = command_line->GetSwitches();
+      const_cast<base::CommandLine::SwitchMap*>(&map)->clear();
+    }
+
+    bool no_sandbox = settings.no_sandbox ? true : false;
+
+    if (settings.browser_subprocess_path.length > 0) {
+      base::FilePath file_path =
+          base::FilePath(CefString(&settings.browser_subprocess_path));
+      if (!file_path.empty()) {
+        command_line->AppendSwitchPath(switches::kBrowserSubprocessPath,
+                                       file_path);
+
+#if defined(OS_WIN)
+        // The sandbox is not supported when using a separate subprocess
+        // executable on Windows.
+        no_sandbox = true;
+#endif
+      }
+    }
+
+#if defined(OS_MACOSX)
+    if (settings.framework_dir_path.length > 0) {
+      base::FilePath file_path =
+          base::FilePath(CefString(&settings.framework_dir_path));
+      if (!file_path.empty())
+        command_line->AppendSwitchPath(switches::kFrameworkDirPath, file_path);
+    }
+
+    if (settings.main_bundle_path.length > 0) {
+      base::FilePath file_path =
+          base::FilePath(CefString(&settings.main_bundle_path));
+      if (!file_path.empty())
+        command_line->AppendSwitchPath(switches::kMainBundlePath, file_path);
+    }
+#endif
+
+    if (no_sandbox)
+      command_line->AppendSwitch(service_manager::switches::kNoSandbox);
+
+    if (settings.user_agent.length > 0) {
+      command_line->AppendSwitchASCII(switches::kUserAgent,
+                                      CefString(&settings.user_agent));
+    } else if (settings.product_version.length > 0) {
+      command_line->AppendSwitchASCII(switches::kProductVersion,
+                                      CefString(&settings.product_version));
+    }
+
+    if (settings.locale.length > 0) {
+      command_line->AppendSwitchASCII(switches::kLang,
+                                      CefString(&settings.locale));
+    } else if (!command_line->HasSwitch(switches::kLang)) {
+      command_line->AppendSwitchASCII(switches::kLang, "en-US");
+    }
+
+    base::FilePath log_file;
+    bool has_log_file_cmdline = false;
+    if (settings.log_file.length > 0)
+      log_file = base::FilePath(CefString(&settings.log_file));
+    if (log_file.empty() && command_line->HasSwitch(switches::kLogFile)) {
+      log_file = command_line->GetSwitchValuePath(switches::kLogFile);
+      if (!log_file.empty())
+        has_log_file_cmdline = true;
+    }
+    if (log_file.empty())
+      log_file = GetDefaultLogFile();
+    DCHECK(!log_file.empty());
+    if (!has_log_file_cmdline)
+      command_line->AppendSwitchPath(switches::kLogFile, log_file);
+
+    if (settings.log_severity != LOGSEVERITY_DEFAULT) {
+      std::string log_severity;
+      switch (settings.log_severity) {
+        case LOGSEVERITY_VERBOSE:
+          log_severity = switches::kLogSeverity_Verbose;
+          break;
+        case LOGSEVERITY_INFO:
+          log_severity = switches::kLogSeverity_Info;
+          break;
+        case LOGSEVERITY_WARNING:
+          log_severity = switches::kLogSeverity_Warning;
+          break;
+        case LOGSEVERITY_ERROR:
+          log_severity = switches::kLogSeverity_Error;
+          break;
+        case LOGSEVERITY_FATAL:
+          log_severity = switches::kLogSeverity_Fatal;
+          break;
+        case LOGSEVERITY_DISABLE:
+          log_severity = switches::kLogSeverity_Disable;
+          break;
+        default:
+          break;
+      }
+      if (!log_severity.empty())
+        command_line->AppendSwitchASCII(switches::kLogSeverity, log_severity);
+    }
+
+    if (settings.javascript_flags.length > 0) {
+      command_line->AppendSwitchASCII(switches::kJavaScriptFlags,
+                                      CefString(&settings.javascript_flags));
+    }
+
+    if (settings.pack_loading_disabled) {
+      command_line->AppendSwitch(switches::kDisablePackLoading);
+    } else {
+      if (settings.resources_dir_path.length > 0) {
+        base::FilePath file_path =
+            base::FilePath(CefString(&settings.resources_dir_path));
+        if (!file_path.empty()) {
+          command_line->AppendSwitchPath(switches::kResourcesDirPath,
+                                         file_path);
+        }
+      }
+
+      if (settings.locales_dir_path.length > 0) {
+        base::FilePath file_path =
+            base::FilePath(CefString(&settings.locales_dir_path));
+        if (!file_path.empty())
+          command_line->AppendSwitchPath(switches::kLocalesDirPath, file_path);
+      }
+    }
+
+    if (settings.remote_debugging_port >= 1024 &&
+        settings.remote_debugging_port <= 65535) {
+      command_line->AppendSwitchASCII(
+          switches::kRemoteDebuggingPort,
+          base::NumberToString(settings.remote_debugging_port));
+    }
+
+    if (settings.uncaught_exception_stack_size > 0) {
+      command_line->AppendSwitchASCII(
+          switches::kUncaughtExceptionStackSize,
+          base::NumberToString(settings.uncaught_exception_stack_size));
+    }
+
+    std::vector<std::string> disable_features;
+
+    if (network::features::kOutOfBlinkCors.default_state ==
+        base::FEATURE_ENABLED_BY_DEFAULT) {
+      // TODO: Add support for out-of-Blink CORS (see issue #2716)
+      disable_features.push_back(network::features::kOutOfBlinkCors.name);
+    }
+
+#if defined(OS_WIN)
+    if (features::kCalculateNativeWinOcclusion.default_state ==
+        base::FEATURE_ENABLED_BY_DEFAULT) {
+      // TODO: Add support for occlusion detection in combination with native
+      // parent windows (see issue #2805).
+      disable_features.push_back(features::kCalculateNativeWinOcclusion.name);
+    }
+#endif  // defined(OS_WIN)
+
+    if (!disable_features.empty()) {
+      DCHECK(!base::FeatureList::GetInstance());
+      std::string disable_features_str =
+          command_line->GetSwitchValueASCII(switches::kDisableFeatures);
+      for (auto feature_str : disable_features) {
+        if (!disable_features_str.empty())
+          disable_features_str += ",";
+        disable_features_str += feature_str;
+      }
+      command_line->AppendSwitchASCII(switches::kDisableFeatures,
+                                      disable_features_str);
+    }
+
+    std::vector<std::string> enable_features;
+
+    if (media_router::kDialMediaRouteProvider.default_state ==
+        base::FEATURE_DISABLED_BY_DEFAULT) {
+      // Enable discovery of DIAL devices.
+      enable_features.push_back(media_router::kDialMediaRouteProvider.name);
+    }
+
+    if (media_router::kCastMediaRouteProvider.default_state ==
+        base::FEATURE_DISABLED_BY_DEFAULT) {
+      // Enable discovery of Cast devices.
+      enable_features.push_back(media_router::kCastMediaRouteProvider.name);
+    }
+
+    if (!enable_features.empty()) {
+      DCHECK(!base::FeatureList::GetInstance());
+      std::string enable_features_str =
+          command_line->GetSwitchValueASCII(switches::kEnableFeatures);
+      for (auto feature_str : enable_features) {
+        if (!enable_features_str.empty())
+          enable_features_str += ",";
+        enable_features_str += feature_str;
+      }
+      command_line->AppendSwitchASCII(switches::kEnableFeatures,
+                                      enable_features_str);
+    }
+  }
+
+  if (content_client_.application().get()) {
+    // Give the application a chance to view/modify the command line.
+    CefRefPtr<CefCommandLineImpl> commandLinePtr(
+        new CefCommandLineImpl(command_line, false, false));
+    content_client_.application()->OnBeforeCommandLineProcessing(
+        CefString(process_type), commandLinePtr.get());
+    commandLinePtr->Detach(nullptr);
+  }
+
+  // Initialize logging.
+  logging::LoggingSettings log_settings;
+
+  const base::FilePath& log_file =
+      command_line->GetSwitchValuePath(switches::kLogFile);
+  DCHECK(!log_file.empty());
+  log_settings.log_file_path = log_file.value().c_str();
+
+  log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+
+  logging::LogSeverity log_severity = logging::LOG_INFO;
+
+  std::string log_severity_str =
+      command_line->GetSwitchValueASCII(switches::kLogSeverity);
+  if (!log_severity_str.empty()) {
+    if (base::LowerCaseEqualsASCII(log_severity_str,
+                                   switches::kLogSeverity_Verbose)) {
+      log_severity = logging::LOG_VERBOSE;
+    } else if (base::LowerCaseEqualsASCII(log_severity_str,
+                                          switches::kLogSeverity_Warning)) {
+      log_severity = logging::LOG_WARNING;
+    } else if (base::LowerCaseEqualsASCII(log_severity_str,
+                                          switches::kLogSeverity_Error)) {
+      log_severity = logging::LOG_ERROR;
+    } else if (base::LowerCaseEqualsASCII(log_severity_str,
+                                          switches::kLogSeverity_Fatal)) {
+      log_severity = logging::LOG_FATAL;
+    } else if (base::LowerCaseEqualsASCII(log_severity_str,
+                                          switches::kLogSeverity_Disable)) {
+      log_severity = LOGSEVERITY_DISABLE;
+    }
+  }
+
+  if (log_severity == LOGSEVERITY_DISABLE) {
+    log_settings.logging_dest = logging::LOG_NONE;
+    // By default, ERROR and FATAL messages will always be output to stderr due
+    // to the kAlwaysPrintErrorLevel value in base/logging.cc. We change the log
+    // level here so that only FATAL messages are output.
+    logging::SetMinLogLevel(logging::LOG_FATAL);
+  } else {
+    log_settings.logging_dest = logging::LOG_TO_ALL;
+    logging::SetMinLogLevel(log_severity);
+  }
+
+  logging::InitLogging(log_settings);
+
+  ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(
+      kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize);
+
+  content::SetContentClient(&content_client_);
+
+#if defined(OS_MACOSX)
+  OverrideFrameworkBundlePath();
+  OverrideOuterBundlePath();
+  OverrideBaseBundleID();
+#endif
+
+  return false;
+}
+
+void CefMainDelegate::PreSandboxStartup() {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  const std::string& process_type =
+      command_line->GetSwitchValueASCII(switches::kProcessType);
+
+  if (process_type.empty()) {
+// Only override these paths when executing the main process.
+#if defined(OS_MACOSX)
+    OverrideChildProcessPath();
+#endif
+
+    OverridePepperFlashSystemPluginPath();
+
+    base::FilePath dir_default_download;
+    base::FilePath dir_default_download_safe;
+    if (GetDefaultDownloadDirectory(&dir_default_download)) {
+      base::PathService::Override(chrome::DIR_DEFAULT_DOWNLOADS,
+                                  dir_default_download);
+    }
+    if (GetDefaultDownloadSafeDirectory(&dir_default_download_safe)) {
+      base::PathService::Override(chrome::DIR_DEFAULT_DOWNLOADS_SAFE,
+                                  dir_default_download_safe);
+    }
+
+    const base::FilePath& user_data_path = GetUserDataPath();
+    base::PathService::Override(chrome::DIR_USER_DATA, user_data_path);
+
+    // Path used for crash dumps.
+    base::PathService::Override(chrome::DIR_CRASH_DUMPS, user_data_path);
+
+    // Path used for spell checking dictionary files.
+    base::PathService::OverrideAndCreateIfNeeded(
+        chrome::DIR_APP_DICTIONARIES,
+        user_data_path.AppendASCII("Dictionaries"),
+        false,  // May not be an absolute path.
+        true);  // Create if necessary.
+  }
+
+  if (command_line->HasSwitch(switches::kDisablePackLoading))
+    content_client_.set_pack_loading_disabled(true);
+
+  // Initialize crash reporting state for this process/module.
+  // chrome::DIR_CRASH_DUMPS must be configured before calling this function.
+  crash_reporting::PreSandboxStartup(*command_line, process_type);
+
+  InitializeResourceBundle();
+  MaybeInitializeGDI();
+}
+
+void CefMainDelegate::SandboxInitialized(const std::string& process_type) {
+  CefContentClient::SetPDFEntryFunctions(chrome_pdf::PPP_GetInterface,
+                                         chrome_pdf::PPP_InitializeModule,
+                                         chrome_pdf::PPP_ShutdownModule);
+}
+
+int CefMainDelegate::RunProcess(
+    const std::string& process_type,
+    const content::MainFunctionParams& main_function_params) {
+  if (process_type.empty()) {
+    const CefSettings& settings = CefContext::Get()->settings();
+    if (!settings.multi_threaded_message_loop) {
+      // Use our own browser process runner.
+      browser_runner_ = content::BrowserMainRunner::Create();
+
+      // Initialize browser process state. Results in a call to
+      // CefBrowserMain::PreMainMessageLoopStart() which creates the UI message
+      // loop.
+      int exit_code = browser_runner_->Initialize(main_function_params);
+      if (exit_code >= 0)
+        return exit_code;
+    } else {
+      // Running on the separate UI thread.
+      DCHECK(ui_thread_);
+      ui_thread_->InitializeBrowserRunner(main_function_params);
+    }
+
+    return 0;
+  }
+
+  return -1;
+}
+
+bool CefMainDelegate::CreateUIThread(base::OnceClosure setup_callback) {
+  DCHECK(!ui_thread_);
+
+  ui_thread_.reset(new CefUIThread(std::move(setup_callback)));
+  ui_thread_->Start();
+  ui_thread_->WaitUntilThreadStarted();
+
+  InitMessagePumpFactoryForUI();
+  return true;
+}
+
+void CefMainDelegate::ProcessExiting(const std::string& process_type) {
+  ui::ResourceBundle::CleanupSharedInstance();
+}
+
+#if defined(OS_LINUX)
+void CefMainDelegate::ZygoteForked() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const std::string& process_type =
+      command_line->GetSwitchValueASCII(switches::kProcessType);
+  // Initialize crash reporting state for the newly forked process.
+  crash_reporting::ZygoteForked(command_line, process_type);
+}
+#endif
+
+content::ContentBrowserClient* CefMainDelegate::CreateContentBrowserClient() {
+  browser_client_.reset(new CefContentBrowserClient);
+  return browser_client_.get();
+}
+
+content::ContentRendererClient* CefMainDelegate::CreateContentRendererClient() {
+  renderer_client_.reset(new CefContentRendererClient);
+  return renderer_client_.get();
+}
+
+content::ContentUtilityClient* CefMainDelegate::CreateContentUtilityClient() {
+  utility_client_.reset(new ChromeContentUtilityClient);
+  return utility_client_.get();
+}
+
+void CefMainDelegate::ShutdownBrowser() {
+  if (browser_runner_.get()) {
+    browser_runner_->Shutdown();
+    browser_runner_.reset(nullptr);
+  }
+
+  if (ui_thread_.get()) {
+    // Blocks until the thread has stopped.
+    ui_thread_->Stop();
+    ui_thread_.reset();
+  }
+}
+
+void CefMainDelegate::InitializeResourceBundle() {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  base::FilePath cef_pak_file, cef_100_percent_pak_file,
+      cef_200_percent_pak_file, cef_extensions_pak_file, devtools_pak_file,
+      locales_dir;
+
+  base::FilePath resources_dir;
+  if (command_line->HasSwitch(switches::kResourcesDirPath)) {
+    resources_dir =
+        command_line->GetSwitchValuePath(switches::kResourcesDirPath);
+  }
+  if (resources_dir.empty())
+    resources_dir = GetResourcesFilePath();
+  if (!resources_dir.empty())
+    base::PathService::Override(chrome::DIR_RESOURCES, resources_dir);
+
+  if (!content_client_.pack_loading_disabled()) {
+    if (!resources_dir.empty()) {
+      CHECK(resources_dir.IsAbsolute());
+      cef_pak_file = resources_dir.Append(FILE_PATH_LITERAL("cef.pak"));
+      cef_100_percent_pak_file =
+          resources_dir.Append(FILE_PATH_LITERAL("cef_100_percent.pak"));
+      cef_200_percent_pak_file =
+          resources_dir.Append(FILE_PATH_LITERAL("cef_200_percent.pak"));
+      cef_extensions_pak_file =
+          resources_dir.Append(FILE_PATH_LITERAL("cef_extensions.pak"));
+      devtools_pak_file =
+          resources_dir.Append(FILE_PATH_LITERAL("devtools_resources.pak"));
+    }
+
+    if (command_line->HasSwitch(switches::kLocalesDirPath))
+      locales_dir = command_line->GetSwitchValuePath(switches::kLocalesDirPath);
+
+    if (!locales_dir.empty())
+      base::PathService::Override(ui::DIR_LOCALES, locales_dir);
+  }
+
+  std::string locale = command_line->GetSwitchValueASCII(switches::kLang);
+  DCHECK(!locale.empty());
+
+  const std::string loaded_locale =
+      ui::ResourceBundle::InitSharedInstanceWithLocale(
+          locale, content_client_.GetCefResourceBundleDelegate(),
+          ui::ResourceBundle::LOAD_COMMON_RESOURCES);
+  if (!loaded_locale.empty() && g_browser_process)
+    g_browser_process->SetApplicationLocale(loaded_locale);
+
+  ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
+
+  if (!content_client_.pack_loading_disabled()) {
+    if (loaded_locale.empty())
+      LOG(ERROR) << "Could not load locale pak for " << locale;
+
+    content_client_.set_allow_pack_file_load(true);
+
+    if (base::PathExists(cef_pak_file)) {
+      resource_bundle.AddDataPackFromPath(cef_pak_file, ui::SCALE_FACTOR_NONE);
+    } else {
+      LOG(ERROR) << "Could not load cef.pak";
+    }
+
+    // On OS X and Linux/Aura always load the 1x data pack first as the 2x data
+    // pack contains both 1x and 2x images.
+    const bool load_100_percent =
+#if defined(OS_WIN)
+        IsScaleFactorSupported(ui::SCALE_FACTOR_100P);
+#else
+        true;
+#endif
+
+    if (load_100_percent) {
+      if (base::PathExists(cef_100_percent_pak_file)) {
+        resource_bundle.AddDataPackFromPath(cef_100_percent_pak_file,
+                                            ui::SCALE_FACTOR_100P);
+      } else {
+        LOG(ERROR) << "Could not load cef_100_percent.pak";
+      }
+    }
+
+    if (IsScaleFactorSupported(ui::SCALE_FACTOR_200P)) {
+      if (base::PathExists(cef_200_percent_pak_file)) {
+        resource_bundle.AddDataPackFromPath(cef_200_percent_pak_file,
+                                            ui::SCALE_FACTOR_200P);
+      } else {
+        LOG(ERROR) << "Could not load cef_200_percent.pak";
+      }
+    }
+
+    if (extensions::ExtensionsEnabled() ||
+        !command_line->HasSwitch(switches::kDisablePlugins)) {
+      if (base::PathExists(cef_extensions_pak_file)) {
+        resource_bundle.AddDataPackFromPath(cef_extensions_pak_file,
+                                            ui::SCALE_FACTOR_NONE);
+      } else {
+        LOG(ERROR) << "Could not load cef_extensions.pak";
+      }
+    }
+
+    if (base::PathExists(devtools_pak_file)) {
+      resource_bundle.AddDataPackFromPath(devtools_pak_file,
+                                          ui::SCALE_FACTOR_NONE);
+    }
+
+    content_client_.set_allow_pack_file_load(false);
+  }
+}
diff --git a/src/libcef/common/main_delegate.h b/src/libcef/common/main_delegate.h
new file mode 100644
index 0000000..fecc697
--- /dev/null
+++ b/src/libcef/common/main_delegate.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_MAIN_DELEGATE_H_
+#define CEF_LIBCEF_COMMON_MAIN_DELEGATE_H_
+#pragma once
+
+#include <string>
+
+#include "include/cef_app.h"
+#include "libcef/common/content_client.h"
+
+#include "base/compiler_specific.h"
+#include "content/public/app/content_main_delegate.h"
+
+namespace base {
+class CommandLine;
+class MessageLoop;
+class Thread;
+}  // namespace base
+
+namespace content {
+class BrowserMainRunner;
+}
+
+class CefContentBrowserClient;
+class CefContentRendererClient;
+class CefUIThread;
+class ChromeContentUtilityClient;
+
+class CefMainDelegate : public content::ContentMainDelegate {
+ public:
+  explicit CefMainDelegate(CefRefPtr<CefApp> application);
+  ~CefMainDelegate() override;
+
+  void PreCreateMainMessageLoop() override;
+  bool BasicStartupComplete(int* exit_code) override;
+  void PreSandboxStartup() override;
+  void SandboxInitialized(const std::string& process_type) override;
+  int RunProcess(
+      const std::string& process_type,
+      const content::MainFunctionParams& main_function_params) override;
+  void ProcessExiting(const std::string& process_type) override;
+#if defined(OS_LINUX)
+  void ZygoteForked() override;
+#endif
+  content::ContentBrowserClient* CreateContentBrowserClient() override;
+  content::ContentRendererClient* CreateContentRendererClient() override;
+  content::ContentUtilityClient* CreateContentUtilityClient() override;
+
+  bool CreateUIThread(base::OnceClosure setup_callback);
+
+  // Shut down the browser runner.
+  void ShutdownBrowser();
+
+  CefContentBrowserClient* browser_client() { return browser_client_.get(); }
+  CefContentClient* content_client() { return &content_client_; }
+
+ private:
+  void InitializeResourceBundle();
+
+  std::unique_ptr<content::BrowserMainRunner> browser_runner_;
+  std::unique_ptr<CefUIThread> ui_thread_;
+
+  std::unique_ptr<CefContentBrowserClient> browser_client_;
+  std::unique_ptr<CefContentRendererClient> renderer_client_;
+  std::unique_ptr<ChromeContentUtilityClient> utility_client_;
+  CefContentClient content_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefMainDelegate);
+};
+
+#endif  // CEF_LIBCEF_COMMON_MAIN_DELEGATE_H_
diff --git a/src/libcef/common/net/http_header_utils.cc b/src/libcef/common/net/http_header_utils.cc
new file mode 100644
index 0000000..6199d2f
--- /dev/null
+++ b/src/libcef/common/net/http_header_utils.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/net/http_header_utils.h"
+
+#include <algorithm>
+
+#include "base/strings/string_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+
+using net::HttpResponseHeaders;
+
+namespace HttpHeaderUtils {
+
+std::string GenerateHeaders(const HeaderMap& map) {
+  std::string headers;
+
+  for (HeaderMap::const_iterator header = map.begin(); header != map.end();
+       ++header) {
+    const CefString& key = header->first;
+    const CefString& value = header->second;
+
+    if (!key.empty()) {
+      // Delimit with "\r\n".
+      if (!headers.empty())
+        headers += "\r\n";
+
+      headers += std::string(key) + ": " + std::string(value);
+    }
+  }
+
+  return headers;
+}
+
+void ParseHeaders(const std::string& header_str, HeaderMap& map) {
+  // Parse the request header values
+  for (net::HttpUtil::HeadersIterator i(header_str.begin(), header_str.end(),
+                                        "\n\r");
+       i.GetNext();) {
+    map.insert(std::make_pair(i.name(), i.values()));
+  }
+}
+
+void MakeASCIILower(std::string* str) {
+  std::transform(str->begin(), str->end(), str->begin(), ::tolower);
+}
+
+HeaderMap::iterator FindHeaderInMap(const std::string& nameLower,
+                                    HeaderMap& map) {
+  for (auto it = map.begin(); it != map.end(); ++it) {
+    if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameLower))
+      return it;
+  }
+
+  return map.end();
+}
+
+}  // namespace HttpHeaderUtils
diff --git a/src/libcef/common/net/http_header_utils.h b/src/libcef/common/net/http_header_utils.h
new file mode 100644
index 0000000..50a1237
--- /dev/null
+++ b/src/libcef/common/net/http_header_utils.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_NET_HTTP_HEADER_UTILS_H_
+#define CEF_LIBCEF_COMMON_NET_HTTP_HEADER_UTILS_H_
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "include/cef_base.h"
+
+namespace HttpHeaderUtils {
+
+typedef std::multimap<CefString, CefString> HeaderMap;
+
+std::string GenerateHeaders(const HeaderMap& map);
+void ParseHeaders(const std::string& header_str, HeaderMap& map);
+
+// Convert |str| to lower-case.
+void MakeASCIILower(std::string* str);
+
+// Finds the first instance of |name| (already lower-case) in |map| with
+// case-insensitive comparison.
+HeaderMap::iterator FindHeaderInMap(const std::string& name, HeaderMap& map);
+
+}  // namespace HttpHeaderUtils
+
+#endif  // CEF_LIBCEF_COMMON_NET_HTTP_HEADER_UTILS_H_
diff --git a/src/libcef/common/net/net_resource_provider.cc b/src/libcef/common/net/net_resource_provider.cc
new file mode 100644
index 0000000..229439d
--- /dev/null
+++ b/src/libcef/common/net/net_resource_provider.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/net/net_resource_provider.h"
+
+#include "chrome/common/net/net_resource_provider.h"
+
+scoped_refptr<base::RefCountedMemory> NetResourceProvider(int key) {
+  // Chrome performs substitution of localized strings for directory listings.
+  scoped_refptr<base::RefCountedMemory> value = ChromeNetResourceProvider(key);
+  if (!value)
+    LOG(ERROR) << "No data resource available for id " << key;
+  return value;
+}
diff --git a/src/libcef/common/net/net_resource_provider.h b/src/libcef/common/net/net_resource_provider.h
new file mode 100644
index 0000000..8f6f6ed
--- /dev/null
+++ b/src/libcef/common/net/net_resource_provider.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_NET_RESOURCE_PROVIDER_H_
+#define CEF_LIBCEF_COMMON_NET_RESOURCE_PROVIDER_H_
+#pragma once
+
+#include "base/memory/ref_counted_memory.h"
+
+// This is called indirectly by the network layer to access resources.
+scoped_refptr<base::RefCountedMemory> NetResourceProvider(int key);
+
+#endif  // CEF_LIBCEF_COMMON_NET_RESOURCE_PROVIDER_H_
diff --git a/src/libcef/common/net/scheme_registration.cc b/src/libcef/common/net/scheme_registration.cc
new file mode 100644
index 0000000..ca1de54
--- /dev/null
+++ b/src/libcef/common/net/scheme_registration.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/net/scheme_registration.h"
+
+#include "libcef/common/content_client.h"
+
+#include "content/public/common/url_constants.h"
+#include "extensions/common/constants.h"
+#include "net/net_buildflags.h"
+#include "url/url_constants.h"
+#include "url/url_util.h"
+
+namespace scheme {
+
+void AddInternalSchemes(content::ContentClient::Schemes* schemes) {
+  // chrome: and chrome-devtools: schemes are registered in
+  // RenderThreadImpl::RegisterSchemes().
+  // Access restrictions for chrome-extension: and chrome-extension-resource:
+  // schemes will be applied in CefContentRendererClient::WillSendRequest().
+  static CefContentClient::SchemeInfo internal_schemes[] = {
+      {
+          extensions::kExtensionScheme, true, /* is_standard */
+          false,                              /* is_local */
+          false,                              /* is_display_isolated */
+          true,                               /* is_secure */
+          true,                               /* is_cors_enabled */
+          true,                               /* is_csp_bypassing */
+      },
+  };
+
+  // The |is_display_isolated| value is excluded here because it's registered
+  // with Blink only.
+  CefContentClient* client = CefContentClient::Get();
+  for (size_t i = 0; i < sizeof(internal_schemes) / sizeof(internal_schemes[0]);
+       ++i) {
+    if (internal_schemes[i].is_standard)
+      schemes->standard_schemes.push_back(internal_schemes[i].scheme_name);
+    if (internal_schemes[i].is_local)
+      schemes->local_schemes.push_back(internal_schemes[i].scheme_name);
+    if (internal_schemes[i].is_secure)
+      schemes->secure_schemes.push_back(internal_schemes[i].scheme_name);
+    if (internal_schemes[i].is_cors_enabled)
+      schemes->cors_enabled_schemes.push_back(internal_schemes[i].scheme_name);
+    if (internal_schemes[i].is_csp_bypassing)
+      schemes->csp_bypassing_schemes.push_back(internal_schemes[i].scheme_name);
+    client->AddCustomScheme(internal_schemes[i]);
+  }
+}
+
+bool IsInternalHandledScheme(const std::string& scheme) {
+  static const char* schemes[] = {
+    url::kAboutScheme,
+    url::kBlobScheme,
+    content::kChromeDevToolsScheme,
+    content::kChromeUIScheme,
+    url::kDataScheme,
+    extensions::kExtensionScheme,
+    url::kFileScheme,
+    url::kFileSystemScheme,
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+    url::kFtpScheme,
+#endif
+    url::kHttpScheme,
+    url::kHttpsScheme,
+    url::kJavaScriptScheme,
+    url::kWsScheme,
+    url::kWssScheme,
+  };
+
+  for (size_t i = 0; i < sizeof(schemes) / sizeof(schemes[0]); ++i) {
+    if (scheme == schemes[i])
+      return true;
+  }
+
+  return false;
+}
+
+bool IsStandardScheme(const std::string& scheme) {
+  url::Component scheme_comp(0, scheme.length());
+  return url::IsStandard(scheme.c_str(), scheme_comp);
+}
+
+}  // namespace scheme
diff --git a/src/libcef/common/net/scheme_registration.h b/src/libcef/common/net/scheme_registration.h
new file mode 100644
index 0000000..c8064fd
--- /dev/null
+++ b/src/libcef/common/net/scheme_registration.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_NET_SCHEME_REGISTRATION_H_
+#define CEF_LIBCEF_COMMON_NET_SCHEME_REGISTRATION_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "content/public/common/content_client.h"
+
+namespace scheme {
+
+// Add internal schemes.
+void AddInternalSchemes(content::ContentClient::Schemes* schemes);
+
+// Returns true if the specified |scheme| is handled internally.
+bool IsInternalHandledScheme(const std::string& scheme);
+
+// Returns true if the specified |scheme| is a registered standard scheme.
+bool IsStandardScheme(const std::string& scheme);
+
+}  // namespace scheme
+
+#endif  // CEF_LIBCEF_COMMON_NET_SCHEME_REGISTRATION_H_
diff --git a/src/libcef/common/net/upload_data.cc b/src/libcef/common/net/upload_data.cc
new file mode 100644
index 0000000..a3ca8ed
--- /dev/null
+++ b/src/libcef/common/net/upload_data.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cef/libcef/common/net/upload_data.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+
+namespace net {
+
+UploadData::UploadData()
+    : identifier_(0), is_chunked_(false), last_chunk_appended_(false) {}
+
+void UploadData::AppendBytes(const char* bytes, int bytes_len) {
+  DCHECK(!is_chunked_);
+  if (bytes_len > 0) {
+    elements_.push_back(std::make_unique<UploadElement>());
+    elements_.back()->SetToBytes(bytes, bytes_len);
+  }
+}
+
+void UploadData::AppendFileRange(const base::FilePath& file_path,
+                                 uint64_t offset,
+                                 uint64_t length,
+                                 const base::Time& expected_modification_time) {
+  DCHECK(!is_chunked_);
+  elements_.push_back(std::make_unique<UploadElement>());
+  elements_.back()->SetToFilePathRange(file_path, offset, length,
+                                       expected_modification_time);
+}
+
+UploadData::~UploadData() {}
+
+}  // namespace net
diff --git a/src/libcef/common/net/upload_data.h b/src/libcef/common/net/upload_data.h
new file mode 100644
index 0000000..ef6c960
--- /dev/null
+++ b/src/libcef/common/net/upload_data.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_NET_UPLOAD_DATA_H_
+#define CEF_LIBCEF_COMMON_NET_UPLOAD_DATA_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "libcef/common/net/upload_element.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/supports_user_data.h"
+#include "net/base/net_export.h"
+
+namespace base {
+class FilePath;
+class Time;
+}  // namespace base
+
+namespace net {
+
+//-----------------------------------------------------------------------------
+// A very concrete class representing the data to be uploaded as part of a
+// URLRequest.
+//
+// Until there is a more abstract class for this, this one derives from
+// SupportsUserData to allow users to stash random data by
+// key and ensure its destruction when UploadData is finally deleted.
+class UploadData : public base::RefCounted<UploadData>,
+                   public base::SupportsUserData {
+ public:
+  UploadData();
+
+  void AppendBytes(const char* bytes, int bytes_len);
+
+  void AppendFileRange(const base::FilePath& file_path,
+                       uint64_t offset,
+                       uint64_t length,
+                       const base::Time& expected_modification_time);
+
+  // Initializes the object to send chunks of upload data over time rather
+  // than all at once. Chunked data may only contain bytes, not files.
+  void set_is_chunked(bool set) { is_chunked_ = set; }
+  bool is_chunked() const { return is_chunked_; }
+
+  // set_last_chunk_appended() is only used for serialization.
+  void set_last_chunk_appended(bool set) { last_chunk_appended_ = set; }
+  bool last_chunk_appended() const { return last_chunk_appended_; }
+
+  using ElementsVector = std::vector<std::unique_ptr<UploadElement>>;
+
+  const ElementsVector& elements() const { return elements_; }
+
+  ElementsVector* elements_mutable() { return &elements_; }
+
+  void swap_elements(ElementsVector* elements) { elements_.swap(*elements); }
+
+  // Identifies a particular upload instance, which is used by the cache to
+  // formulate a cache key.  This value should be unique across browser
+  // sessions.  A value of 0 is used to indicate an unspecified identifier.
+  void set_identifier(int64_t id) { identifier_ = id; }
+  int64_t identifier() const { return identifier_; }
+
+ private:
+  friend class base::RefCounted<UploadData>;
+
+  ~UploadData() override;
+
+  ElementsVector elements_;
+  int64_t identifier_;
+  bool is_chunked_;
+  bool last_chunk_appended_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadData);
+};
+
+}  // namespace net
+
+#endif  // CEF_LIBCEF_COMMON_NET_UPLOAD_DATA_H_
diff --git a/src/libcef/common/net/upload_element.cc b/src/libcef/common/net/upload_element.cc
new file mode 100644
index 0000000..1a401bc
--- /dev/null
+++ b/src/libcef/common/net/upload_element.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/net/upload_element.h"
+
+#include "net/base/file_stream.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+UploadElement::UploadElement()
+    : type_(TYPE_BYTES),
+      bytes_start_(nullptr),
+      bytes_length_(0),
+      file_range_offset_(0),
+      file_range_length_(std::numeric_limits<uint64_t>::max()) {}
+
+UploadElement::~UploadElement() {}
+
+}  // namespace net
diff --git a/src/libcef/common/net/upload_element.h b/src/libcef/common/net/upload_element.h
new file mode 100644
index 0000000..9fb487d
--- /dev/null
+++ b/src/libcef/common/net/upload_element.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_NET_UPLOAD_ELEMENT_H_
+#define CEF_LIBCEF_COMMON_NET_UPLOAD_ELEMENT_H_
+
+#include <stdint.h>
+
+#include <limits>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// A class representing an element contained by UploadData.
+class UploadElement {
+ public:
+  enum Type {
+    TYPE_BYTES,
+    TYPE_FILE,
+  };
+
+  UploadElement();
+  ~UploadElement();
+
+  Type type() const { return type_; }
+
+  const char* bytes() const { return bytes_start_ ? bytes_start_ : &buf_[0]; }
+  uint64_t bytes_length() const { return buf_.size() + bytes_length_; }
+  const base::FilePath& file_path() const { return file_path_; }
+  uint64_t file_range_offset() const { return file_range_offset_; }
+  uint64_t file_range_length() const { return file_range_length_; }
+  // If NULL time is returned, we do not do the check.
+  const base::Time& expected_file_modification_time() const {
+    return expected_file_modification_time_;
+  }
+
+  void SetToBytes(const char* bytes, int bytes_len) {
+    type_ = TYPE_BYTES;
+    buf_.assign(bytes, bytes + bytes_len);
+  }
+
+  // This does not copy the given data and the caller should make sure
+  // the data is secured somewhere else (e.g. by attaching the data
+  // using SetUserData).
+  void SetToSharedBytes(const char* bytes, int bytes_len) {
+    type_ = TYPE_BYTES;
+    bytes_start_ = bytes;
+    bytes_length_ = bytes_len;
+  }
+
+  void SetToFilePath(const base::FilePath& path) {
+    SetToFilePathRange(path, 0, std::numeric_limits<uint64_t>::max(),
+                       base::Time());
+  }
+
+  // If expected_modification_time is NULL, we do not check for the file
+  // change. Also note that the granularity for comparison is time_t, not
+  // the full precision.
+  void SetToFilePathRange(const base::FilePath& path,
+                          uint64_t offset,
+                          uint64_t length,
+                          const base::Time& expected_modification_time) {
+    type_ = TYPE_FILE;
+    file_path_ = path;
+    file_range_offset_ = offset;
+    file_range_length_ = length;
+    expected_file_modification_time_ = expected_modification_time;
+  }
+
+ private:
+  Type type_;
+  std::vector<char> buf_;
+  const char* bytes_start_;
+  uint64_t bytes_length_;
+  base::FilePath file_path_;
+  uint64_t file_range_offset_;
+  uint64_t file_range_length_;
+  base::Time expected_file_modification_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadElement);
+};
+
+#if defined(UNIT_TEST)
+inline bool operator==(const UploadElement& a, const UploadElement& b) {
+  if (a.type() != b.type())
+    return false;
+  if (a.type() == UploadElement::TYPE_BYTES)
+    return a.bytes_length() == b.bytes_length() &&
+           memcmp(a.bytes(), b.bytes(), b.bytes_length()) == 0;
+  if (a.type() == UploadElement::TYPE_FILE) {
+    return a.file_path() == b.file_path() &&
+           a.file_range_offset() == b.file_range_offset() &&
+           a.file_range_length() == b.file_range_length() &&
+           a.expected_file_modification_time() ==
+               b.expected_file_modification_time();
+  }
+  return false;
+}
+
+inline bool operator!=(const UploadElement& a, const UploadElement& b) {
+  return !(a == b);
+}
+#endif  // defined(UNIT_TEST)
+
+}  // namespace net
+
+#endif  // CEF_LIBCEF_COMMON_NET_UPLOAD_ELEMENT_H_
diff --git a/src/libcef/common/net_service/net_service_util.cc b/src/libcef/common/net_service/net_service_util.cc
new file mode 100644
index 0000000..3f68e24
--- /dev/null
+++ b/src/libcef/common/net_service/net_service_util.cc
@@ -0,0 +1,284 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
+// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "libcef/common/net_service/net_service_util.h"
+
+#include "libcef/common/time_util.h"
+
+#include <set>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_util.h"
+#include "net/cookies/parsed_cookie.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/redirect_info.h"
+#include "net/url_request/redirect_util.h"
+#include "net/url_request/url_request.h"
+#include "services/network/public/cpp/resource_request.h"
+
+namespace net_service {
+
+namespace {
+
+// Determine the cookie domain to use for setting the specified cookie.
+// From net/cookies/cookie_store.cc.
+bool GetCookieDomain(const GURL& url,
+                     const net::ParsedCookie& pc,
+                     std::string* result) {
+  std::string domain_string;
+  if (pc.HasDomain())
+    domain_string = pc.Domain();
+  return net::cookie_util::GetCookieDomainWithString(url, domain_string,
+                                                     result);
+}
+
+cef_cookie_same_site_t MakeCefCookieSameSite(net::CookieSameSite value) {
+  switch (value) {
+    case net::CookieSameSite::UNSPECIFIED:
+      return CEF_COOKIE_SAME_SITE_UNSPECIFIED;
+    case net::CookieSameSite::NO_RESTRICTION:
+      return CEF_COOKIE_SAME_SITE_NO_RESTRICTION;
+    case net::CookieSameSite::LAX_MODE:
+      return CEF_COOKIE_SAME_SITE_LAX_MODE;
+    case net::CookieSameSite::STRICT_MODE:
+      return CEF_COOKIE_SAME_SITE_STRICT_MODE;
+  }
+}
+
+cef_cookie_priority_t MakeCefCookiePriority(net::CookiePriority value) {
+  switch (value) {
+    case net::COOKIE_PRIORITY_LOW:
+      return CEF_COOKIE_PRIORITY_LOW;
+    case net::COOKIE_PRIORITY_MEDIUM:
+      return CEF_COOKIE_PRIORITY_MEDIUM;
+    case net::COOKIE_PRIORITY_HIGH:
+      return CEF_COOKIE_PRIORITY_HIGH;
+  }
+}
+
+}  // namespace
+
+const char kHTTPLocationHeaderName[] = "Location";
+const char kHTTPSetCookieHeaderName[] = "Set-Cookie";
+
+const char kContentTypeApplicationFormURLEncoded[] =
+    "application/x-www-form-urlencoded";
+
+const char kHTTPHeaderSep[] = ": ";
+
+std::string MakeHeader(const std::string& name, const std::string& value) {
+  std::string header(name);
+  header.append(kHTTPHeaderSep);
+  header.append(value);
+  return header;
+}
+
+std::string MakeStatusLine(int status_code,
+                           const std::string& status_text,
+                           bool for_replacement) {
+  std::string status("HTTP/1.1 ");
+  status.append(base::NumberToString(status_code));
+  status.append(" ");
+
+  if (status_text.empty()) {
+    const std::string& text =
+        net::GetHttpReasonPhrase(static_cast<net::HttpStatusCode>(status_code));
+    DCHECK(!text.empty());
+    status.append(text);
+  } else {
+    status.append(status_text);
+  }
+
+  if (!for_replacement) {
+    // The HttpResponseHeaders constructor expects its input string to be
+    // terminated by two NULs.
+    status.append("\0\0", 2);
+  }
+  return status;
+}
+
+std::string MakeContentTypeValue(const std::string& mime_type,
+                                 const std::string& charset) {
+  DCHECK(!mime_type.empty());
+  std::string value = mime_type;
+  if (!charset.empty()) {
+    value.append("; charset=");
+    value.append(charset);
+  }
+  return value;
+}
+
+scoped_refptr<net::HttpResponseHeaders> MakeResponseHeaders(
+    int status_code,
+    const std::string& status_text,
+    const std::string& mime_type,
+    const std::string& charset,
+    int64_t content_length,
+    const std::multimap<std::string, std::string>& extra_headers,
+    bool allow_existing_header_override) {
+  if (status_code <= 0)
+    status_code = 200;
+
+  auto headers = WrapRefCounted(new net::HttpResponseHeaders(
+      MakeStatusLine(status_code, status_text, false)));
+
+  // Track the headers that have already been set. Perform all comparisons in
+  // lowercase.
+  std::set<std::string> set_headers_lowercase;
+  if ((status_code >= 200 && status_code < 300) &&
+      status_code != net::HTTP_NO_CONTENT &&
+      status_code != net::HTTP_RESET_CONTENT) {
+    if (!mime_type.empty()) {
+      headers->AddHeader(MakeHeader(net::HttpRequestHeaders::kContentType,
+                                    MakeContentTypeValue(mime_type, charset)));
+      set_headers_lowercase.insert(
+          base::ToLowerASCII(net::HttpRequestHeaders::kContentType));
+    }
+
+    if (content_length >= 0) {
+      headers->AddHeader(MakeHeader(net::HttpRequestHeaders::kContentLength,
+                                    base::NumberToString(content_length)));
+      set_headers_lowercase.insert(
+          base::ToLowerASCII(net::HttpRequestHeaders::kContentLength));
+    }
+  }
+
+  for (const auto& pair : extra_headers) {
+    if (!set_headers_lowercase.empty()) {
+      // Check if the header has already been set.
+      const std::string& name_lowercase = base::ToLowerASCII(pair.first);
+      if (set_headers_lowercase.find(name_lowercase) !=
+          set_headers_lowercase.end()) {
+        if (allow_existing_header_override)
+          headers->RemoveHeader(pair.first);
+        else
+          continue;
+      }
+    }
+
+    headers->AddHeader(MakeHeader(pair.first, pair.second));
+  }
+
+  return headers;
+}
+
+net::RedirectInfo MakeRedirectInfo(const network::ResourceRequest& request,
+                                   const net::HttpResponseHeaders* headers,
+                                   const GURL& new_location,
+                                   int status_code) {
+  bool insecure_scheme_was_upgraded = false;
+
+  GURL location = new_location;
+  if (status_code == 0)
+    status_code = net::HTTP_TEMPORARY_REDIRECT;
+
+  // If this a redirect to HTTP of a request that had the
+  // 'upgrade-insecure-requests' policy set, upgrade it to HTTPS.
+  if (request.upgrade_if_insecure) {
+    if (location.SchemeIs("http")) {
+      insecure_scheme_was_upgraded = true;
+      GURL::Replacements replacements;
+      replacements.SetSchemeStr("https");
+      location = location.ReplaceComponents(replacements);
+    }
+  }
+
+  net::URLRequest::FirstPartyURLPolicy first_party_url_policy =
+      request.update_first_party_url_on_redirect
+          ? net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT
+          : net::URLRequest::NEVER_CHANGE_FIRST_PARTY_URL;
+  return net::RedirectInfo::ComputeRedirectInfo(
+      request.method, request.url, request.site_for_cookies,
+      first_party_url_policy, request.referrer_policy, request.referrer.spec(),
+      status_code, location,
+      net::RedirectUtil::GetReferrerPolicyHeader(headers),
+      insecure_scheme_was_upgraded);
+}
+
+net::CookieSameSite MakeCookieSameSite(cef_cookie_same_site_t value) {
+  switch (value) {
+    case CEF_COOKIE_SAME_SITE_UNSPECIFIED:
+      return net::CookieSameSite::UNSPECIFIED;
+    case CEF_COOKIE_SAME_SITE_NO_RESTRICTION:
+      return net::CookieSameSite::NO_RESTRICTION;
+    case CEF_COOKIE_SAME_SITE_LAX_MODE:
+      return net::CookieSameSite::LAX_MODE;
+    case CEF_COOKIE_SAME_SITE_STRICT_MODE:
+      return net::CookieSameSite::STRICT_MODE;
+  }
+}
+
+net::CookiePriority MakeCookiePriority(cef_cookie_priority_t value) {
+  switch (value) {
+    case CEF_COOKIE_PRIORITY_LOW:
+      return net::COOKIE_PRIORITY_LOW;
+    case CEF_COOKIE_PRIORITY_MEDIUM:
+      return net::COOKIE_PRIORITY_MEDIUM;
+    case CEF_COOKIE_PRIORITY_HIGH:
+      return net::COOKIE_PRIORITY_HIGH;
+  }
+}
+
+bool MakeCefCookie(const net::CanonicalCookie& cc, CefCookie& cookie) {
+  CefString(&cookie.name).FromString(cc.Name());
+  CefString(&cookie.value).FromString(cc.Value());
+  CefString(&cookie.domain).FromString(cc.Domain());
+  CefString(&cookie.path).FromString(cc.Path());
+  cookie.secure = cc.IsSecure();
+  cookie.httponly = cc.IsHttpOnly();
+  cef_time_from_basetime(cc.CreationDate(), cookie.creation);
+  cef_time_from_basetime(cc.LastAccessDate(), cookie.last_access);
+  cookie.has_expires = cc.IsPersistent();
+  if (cookie.has_expires)
+    cef_time_from_basetime(cc.ExpiryDate(), cookie.expires);
+  cookie.same_site = MakeCefCookieSameSite(cc.SameSite());
+  cookie.priority = MakeCefCookiePriority(cc.Priority());
+
+  return true;
+}
+
+bool MakeCefCookie(const GURL& url,
+                   const std::string& cookie_line,
+                   CefCookie& cookie) {
+  // Parse the cookie.
+  net::ParsedCookie pc(cookie_line);
+  if (!pc.IsValid())
+    return false;
+
+  std::string cookie_domain;
+  if (!GetCookieDomain(url, pc, &cookie_domain))
+    return false;
+
+  std::string path_string;
+  if (pc.HasPath())
+    path_string = pc.Path();
+  std::string cookie_path =
+      net::CanonicalCookie::CanonPathWithString(url, path_string);
+  base::Time creation_time = base::Time::Now();
+  base::Time cookie_expires =
+      net::CanonicalCookie::CanonExpiration(pc, creation_time, creation_time);
+
+  CefString(&cookie.name).FromString(pc.Name());
+  CefString(&cookie.value).FromString(pc.Value());
+  CefString(&cookie.domain).FromString(cookie_domain);
+  CefString(&cookie.path).FromString(cookie_path);
+  cookie.secure = pc.IsSecure();
+  cookie.httponly = pc.IsHttpOnly();
+  cef_time_from_basetime(creation_time, cookie.creation);
+  cef_time_from_basetime(creation_time, cookie.last_access);
+  cookie.has_expires = !cookie_expires.is_null();
+  if (cookie.has_expires)
+    cef_time_from_basetime(cookie_expires, cookie.expires);
+  cookie.same_site = MakeCefCookieSameSite(pc.SameSite());
+  cookie.priority = MakeCefCookiePriority(pc.Priority());
+
+  return true;
+}
+
+}  // namespace net_service
\ No newline at end of file
diff --git a/src/libcef/common/net_service/net_service_util.h b/src/libcef/common/net_service/net_service_util.h
new file mode 100644
index 0000000..83e34b3
--- /dev/null
+++ b/src/libcef/common/net_service/net_service_util.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_NET_SERVICE_NET_SERVICE_UTIL_H_
+#define CEF_LIBCEF_COMMON_NET_SERVICE_NET_SERVICE_UTIL_H_
+
+#include <map>
+#include <string>
+
+#include "include/internal/cef_types_wrappers.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "net/cookies/cookie_constants.h"
+
+namespace net {
+class CanonicalCookie;
+class HttpResponseHeaders;
+struct RedirectInfo;
+}  // namespace net
+
+namespace network {
+struct ResourceRequest;
+}  // namespace network
+
+class GURL;
+
+namespace net_service {
+
+// HTTP header names.
+extern const char kHTTPLocationHeaderName[];
+extern const char kHTTPSetCookieHeaderName[];
+
+// HTTP header values.
+extern const char kContentTypeApplicationFormURLEncoded[];
+
+// Make a header name/value pair.
+std::string MakeHeader(const std::string& name, const std::string& value);
+
+// Make an HTTP response status line.
+// Set |for_replacement| to true if the result will be passed to
+// HttpResponseHeaders::ReplaceStatusLine and false if the result will
+// be passed to the HttpResponseHeaders constructor.
+std::string MakeStatusLine(int status_code,
+                           const std::string& status_text,
+                           bool for_replacement);
+
+// Make an HTTP Content-Type response header value.
+std::string MakeContentTypeValue(const std::string& mime_type,
+                                 const std::string& charset);
+
+// Make a new HttpResponseHeaders object.
+scoped_refptr<net::HttpResponseHeaders> MakeResponseHeaders(
+    int status_code,
+    const std::string& status_text,
+    const std::string& mime_type,
+    const std::string& charset,
+    int64_t content_length,
+    const std::multimap<std::string, std::string>& extra_headers,
+    bool allow_existing_header_override);
+
+// Make a RedirectInfo structure.
+net::RedirectInfo MakeRedirectInfo(const network::ResourceRequest& request,
+                                   const net::HttpResponseHeaders* headers,
+                                   const GURL& new_location,
+                                   int status_code);
+
+// Populate |cookie|. Returns true on success.
+bool MakeCefCookie(const net::CanonicalCookie& cc, CefCookie& cookie);
+bool MakeCefCookie(const GURL& url,
+                   const std::string& cookie_line,
+                   CefCookie& cookie);
+
+net::CookieSameSite MakeCookieSameSite(cef_cookie_same_site_t value);
+net::CookiePriority MakeCookiePriority(cef_cookie_priority_t value);
+
+}  // namespace net_service
+
+#endif  // CEF_LIBCEF_COMMON_NET_SERVICE_NET_SERVICE_UTIL_H_
diff --git a/src/libcef/common/parser_impl.cc b/src/libcef/common/parser_impl.cc
new file mode 100644
index 0000000..cd95b3b
--- /dev/null
+++ b/src/libcef/common/parser_impl.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include <sstream>
+
+#include "include/cef_parser.h"
+
+#include "base/base64.h"
+#include "base/threading/thread_restrictions.h"
+#include "components/url_formatter/elide_url.h"
+#include "net/base/escape.h"
+#include "net/base/mime_util.h"
+#include "url/gurl.h"
+
+bool CefParseURL(const CefString& url, CefURLParts& parts) {
+  GURL gurl(url.ToString());
+  if (!gurl.is_valid())
+    return false;
+
+  CefString(&parts.spec).FromString(gurl.spec());
+  CefString(&parts.scheme).FromString(gurl.scheme());
+  CefString(&parts.username).FromString(gurl.username());
+  CefString(&parts.password).FromString(gurl.password());
+  CefString(&parts.host).FromString(gurl.host());
+  CefString(&parts.origin).FromString(gurl.GetOrigin().spec());
+  CefString(&parts.port).FromString(gurl.port());
+  CefString(&parts.path).FromString(gurl.path());
+  CefString(&parts.query).FromString(gurl.query());
+  CefString(&parts.fragment).FromString(gurl.ref());
+
+  return true;
+}
+
+bool CefCreateURL(const CefURLParts& parts, CefString& url) {
+  std::string spec = CefString(parts.spec.str, parts.spec.length, false);
+  std::string scheme = CefString(parts.scheme.str, parts.scheme.length, false);
+  std::string username =
+      CefString(parts.username.str, parts.username.length, false);
+  std::string password =
+      CefString(parts.password.str, parts.password.length, false);
+  std::string host = CefString(parts.host.str, parts.host.length, false);
+  std::string port = CefString(parts.port.str, parts.port.length, false);
+  std::string path = CefString(parts.path.str, parts.path.length, false);
+  std::string query = CefString(parts.query.str, parts.query.length, false);
+  std::string fragment =
+      CefString(parts.fragment.str, parts.fragment.length, false);
+
+  GURL gurl;
+  if (!spec.empty()) {
+    gurl = GURL(spec);
+  } else if (!scheme.empty() && !host.empty()) {
+    std::stringstream ss;
+    ss << scheme << "://";
+    if (!username.empty()) {
+      ss << username;
+      if (!password.empty())
+        ss << ":" << password;
+      ss << "@";
+    }
+    ss << host;
+    if (!port.empty())
+      ss << ":" << port;
+    if (!path.empty())
+      ss << path;
+    if (!query.empty())
+      ss << "?" << query;
+    if (!fragment.empty())
+      ss << "#" << fragment;
+    gurl = GURL(ss.str());
+  }
+
+  if (gurl.is_valid()) {
+    url = gurl.spec();
+    return true;
+  }
+
+  return false;
+}
+
+CefString CefFormatUrlForSecurityDisplay(const CefString& origin_url) {
+  return url_formatter::FormatUrlForSecurityDisplay(
+      GURL(origin_url.ToString()));
+}
+
+CefString CefGetMimeType(const CefString& extension) {
+  // Requests should not block on the disk!  On POSIX this goes to disk.
+  // http://code.google.com/p/chromium/issues/detail?id=59849
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+  std::string mime_type;
+  net::GetMimeTypeFromExtension(extension, &mime_type);
+  return mime_type;
+}
+
+void CefGetExtensionsForMimeType(const CefString& mime_type,
+                                 std::vector<CefString>& extensions) {
+  typedef std::vector<base::FilePath::StringType> VectorType;
+  VectorType ext;
+  net::GetExtensionsForMimeType(mime_type, &ext);
+  VectorType::const_iterator it = ext.begin();
+  for (; it != ext.end(); ++it)
+    extensions.push_back(*it);
+}
+
+CefString CefBase64Encode(const void* data, size_t data_size) {
+  if (data_size == 0)
+    return CefString();
+
+  base::StringPiece input(static_cast<const char*>(data), data_size);
+  std::string output;
+  base::Base64Encode(input, &output);
+  return output;
+}
+
+CefRefPtr<CefBinaryValue> CefBase64Decode(const CefString& data) {
+  if (data.size() == 0)
+    return nullptr;
+
+  const std::string& input = data;
+  std::string output;
+  if (base::Base64Decode(input, &output))
+    return CefBinaryValue::Create(output.data(), output.size());
+  return nullptr;
+}
+
+CefString CefURIEncode(const CefString& text, bool use_plus) {
+  return net::EscapeQueryParamValue(text.ToString(), use_plus);
+}
+
+CefString CefURIDecode(const CefString& text,
+                       bool convert_to_utf8,
+                       cef_uri_unescape_rule_t unescape_rule) {
+  const net::UnescapeRule::Type type =
+      static_cast<net::UnescapeRule::Type>(unescape_rule);
+  if (convert_to_utf8)
+    return net::UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+        text.ToString(), type, nullptr);
+  else
+    return net::UnescapeURLComponent(text.ToString(), type);
+}
diff --git a/src/libcef/common/process_message_impl.cc b/src/libcef/common/process_message_impl.cc
new file mode 100644
index 0000000..ed6510c
--- /dev/null
+++ b/src/libcef/common/process_message_impl.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/process_message_impl.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/values_impl.h"
+
+#include "base/logging.h"
+
+namespace {
+
+void CopyValue(const Cef_Request_Params& source, Cef_Request_Params& target) {
+  target.name = source.name;
+  auto copy = source.arguments.CreateDeepCopy();
+  target.arguments.Swap(copy.get());
+}
+
+}  // namespace
+
+// static
+CefRefPtr<CefProcessMessage> CefProcessMessage::Create(const CefString& name) {
+  Cef_Request_Params* params = new Cef_Request_Params();
+  params->name = name;
+  return new CefProcessMessageImpl(params, true, false);
+}
+
+CefProcessMessageImpl::CefProcessMessageImpl(Cef_Request_Params* value,
+                                             bool will_delete,
+                                             bool read_only)
+    : CefValueBase<CefProcessMessage, Cef_Request_Params>(
+          value,
+          nullptr,
+          will_delete ? kOwnerWillDelete : kOwnerNoDelete,
+          read_only,
+          nullptr) {}
+
+bool CefProcessMessageImpl::CopyTo(Cef_Request_Params& target) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  CopyValue(const_value(), target);
+  return true;
+}
+
+bool CefProcessMessageImpl::IsValid() {
+  return !detached();
+}
+
+bool CefProcessMessageImpl::IsReadOnly() {
+  return read_only();
+}
+
+CefRefPtr<CefProcessMessage> CefProcessMessageImpl::Copy() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  Cef_Request_Params* params = new Cef_Request_Params();
+  CopyValue(const_value(), *params);
+  return new CefProcessMessageImpl(params, true, false);
+}
+
+CefString CefProcessMessageImpl::GetName() {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+  return const_value().name;
+}
+
+CefRefPtr<CefListValue> CefProcessMessageImpl::GetArgumentList() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  return CefListValueImpl::GetOrCreateRef(
+      const_cast<base::ListValue*>(&(const_value().arguments)),
+      const_cast<Cef_Request_Params*>(&const_value()), read_only(),
+      controller());
+}
diff --git a/src/libcef/common/process_message_impl.h b/src/libcef/common/process_message_impl.h
new file mode 100644
index 0000000..30dfc72
--- /dev/null
+++ b/src/libcef/common/process_message_impl.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_PROCESS_MESSAGE_IMPL_H_
+#define CEF_LIBCEF_COMMON_PROCESS_MESSAGE_IMPL_H_
+#pragma once
+
+#include "include/cef_process_message.h"
+#include "libcef/common/value_base.h"
+
+struct Cef_Request_Params;
+
+// CefProcessMessage implementation
+class CefProcessMessageImpl
+    : public CefValueBase<CefProcessMessage, Cef_Request_Params> {
+ public:
+  CefProcessMessageImpl(Cef_Request_Params* value,
+                        bool will_delete,
+                        bool read_only);
+
+  // Copies the underlying value to the specified |target| structure.
+  bool CopyTo(Cef_Request_Params& target);
+
+  // CefProcessMessage methods.
+  bool IsValid() override;
+  bool IsReadOnly() override;
+  CefRefPtr<CefProcessMessage> Copy() override;
+  CefString GetName() override;
+  CefRefPtr<CefListValue> GetArgumentList() override;
+
+  DISALLOW_COPY_AND_ASSIGN(CefProcessMessageImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_PROCESS_MESSAGE_IMPL_H_
diff --git a/src/libcef/common/request_impl.cc b/src/libcef/common/request_impl.cc
new file mode 100644
index 0000000..05f7c54
--- /dev/null
+++ b/src/libcef/common/request_impl.cc
@@ -0,0 +1,1405 @@
+// Copyright (c) 2008-2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "libcef/browser/navigate_params.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/net/http_header_utils.h"
+#include "libcef/common/net/upload_data.h"
+#include "libcef/common/net_service/net_service_util.h"
+#include "libcef/common/request_impl.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "components/navigation_interception/navigation_params.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
+#include "net/base/elements_upload_data_stream.h"
+#include "net/base/load_flags.h"
+#include "net/base/upload_bytes_element_reader.h"
+#include "net/base/upload_data_stream.h"
+#include "net/base/upload_element_reader.h"
+#include "net/base/upload_file_element_reader.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_util.h"
+#include "net/url_request/redirect_info.h"
+#include "services/network/public/cpp/data_element.h"
+#include "services/network/public/cpp/network_switches.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_request_body.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/web_http_body.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/web/web_security_policy.h"
+
+namespace {
+
+const char kReferrerLowerCase[] = "referer";
+const char kCacheControlLowerCase[] = "cache-control";
+const char kCacheControlDirectiveNoCacheLowerCase[] = "no-cache";
+const char kCacheControlDirectiveNoStoreLowerCase[] = "no-store";
+const char kCacheControlDirectiveOnlyIfCachedLowerCase[] = "only-if-cached";
+
+// Mask of values that configure the cache policy.
+const int kURCachePolicyMask =
+    (UR_FLAG_SKIP_CACHE | UR_FLAG_ONLY_FROM_CACHE | UR_FLAG_DISABLE_CACHE);
+
+// A subclass of net::UploadBytesElementReader that keeps the associated
+// UploadElement alive until the request completes.
+class BytesElementReader : public net::UploadBytesElementReader {
+ public:
+  explicit BytesElementReader(std::unique_ptr<net::UploadElement> element)
+      : net::UploadBytesElementReader(element->bytes(),
+                                      element->bytes_length()),
+        element_(std::move(element)) {
+    DCHECK_EQ(net::UploadElement::TYPE_BYTES, element_->type());
+  }
+
+ private:
+  std::unique_ptr<net::UploadElement> element_;
+
+  DISALLOW_COPY_AND_ASSIGN(BytesElementReader);
+};
+
+scoped_refptr<base::SequencedTaskRunner> GetFileTaskRunner() {
+  return base::CreateSequencedTaskRunner(
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+}
+
+// A subclass of net::UploadFileElementReader that keeps the associated
+// UploadElement alive until the request completes.
+class FileElementReader : public net::UploadFileElementReader {
+ public:
+  explicit FileElementReader(std::unique_ptr<net::UploadElement> element)
+      : net::UploadFileElementReader(
+            GetFileTaskRunner().get(),
+            element->file_path(),
+            element->file_range_offset(),
+            element->file_range_length(),
+            element->expected_file_modification_time()),
+        element_(std::move(element)) {
+    DCHECK_EQ(net::UploadElement::TYPE_FILE, element_->type());
+  }
+
+ private:
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  std::unique_ptr<net::UploadElement> element_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileElementReader);
+};
+
+// Returns the cef_urlrequest_flags_t policy specified by the Cache-Control
+// request header directives, if any. The directives are case-insensitive and
+// some have an optional argument. Multiple directives are comma-separated.
+// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
+// for details.
+int GetCacheControlHeaderPolicy(CefRequest::HeaderMap headerMap) {
+  std::string line;
+
+  // Extract the Cache-Control header line.
+  {
+    CefRequest::HeaderMap::const_iterator it = headerMap.begin();
+    for (; it != headerMap.end(); ++it) {
+      if (base::LowerCaseEqualsASCII(it->first.ToString(),
+                                     kCacheControlLowerCase)) {
+        line = it->second;
+        break;
+      }
+    }
+  }
+
+  int flags = 0;
+
+  if (!line.empty()) {
+    HttpHeaderUtils::MakeASCIILower(&line);
+
+    std::vector<base::StringPiece> pieces = base::SplitStringPiece(
+        line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    for (const auto& piece : pieces) {
+      if (base::LowerCaseEqualsASCII(piece,
+                                     kCacheControlDirectiveNoCacheLowerCase)) {
+        flags |= UR_FLAG_SKIP_CACHE;
+      } else if (base::LowerCaseEqualsASCII(
+                     piece, kCacheControlDirectiveOnlyIfCachedLowerCase)) {
+        flags |= UR_FLAG_ONLY_FROM_CACHE;
+      } else if (base::LowerCaseEqualsASCII(
+                     piece, kCacheControlDirectiveNoStoreLowerCase)) {
+        flags |= UR_FLAG_DISABLE_CACHE;
+      }
+    }
+  }
+
+  return flags;
+}
+
+// Convert cef_urlrequest_flags_t to blink::WebCachePolicy.
+blink::mojom::FetchCacheMode GetFetchCacheMode(int ur_flags) {
+  const bool skip_cache{ur_flags & UR_FLAG_SKIP_CACHE};
+  const bool only_from_cache{ur_flags & UR_FLAG_ONLY_FROM_CACHE};
+  const bool disable_cache{ur_flags & UR_FLAG_DISABLE_CACHE};
+  if (only_from_cache && (skip_cache || disable_cache)) {
+    // The request will always fail because only_from_cache and
+    // skip_cache/disable_cache are mutually exclusive.
+    return blink::mojom::FetchCacheMode::kUnspecifiedForceCacheMiss;
+  } else if (disable_cache) {
+    // This additionally implies the skip_cache behavior.
+    return blink::mojom::FetchCacheMode::kNoStore;
+  } else if (skip_cache) {
+    return blink::mojom::FetchCacheMode::kBypassCache;
+  } else if (only_from_cache) {
+    return blink::mojom::FetchCacheMode::kOnlyIfCached;
+  }
+  return blink::mojom::FetchCacheMode::kDefault;
+}
+
+blink::WebString FilePathStringToWebString(
+    const base::FilePath::StringType& str) {
+#if defined(OS_POSIX)
+  return blink::WebString::FromUTF8(str);
+#elif defined(OS_WIN)
+  return blink::WebString::FromUTF16(str);
+#endif
+}
+
+// Read |headers| into |map|.
+void GetHeaderMap(const net::HttpRequestHeaders& headers,
+                  CefRequest::HeaderMap& map) {
+  map.clear();
+
+  if (headers.IsEmpty())
+    return;
+
+  net::HttpRequestHeaders::Iterator it(headers);
+  while (it.GetNext()) {
+    const std::string& name = it.name();
+
+    // Do not include Referer in the header map.
+    if (!base::LowerCaseEqualsASCII(name, kReferrerLowerCase))
+      map.insert(std::make_pair(name, it.value()));
+  };
+}
+
+// Read |source| into |map|.
+void GetHeaderMap(const CefRequest::HeaderMap& source,
+                  CefRequest::HeaderMap& map) {
+  map.clear();
+
+  CefRequest::HeaderMap::const_iterator it = source.begin();
+  for (; it != source.end(); ++it) {
+    const CefString& name = it->first;
+
+    // Do not include Referer in the header map.
+    if (!base::LowerCaseEqualsASCII(name.ToString(), kReferrerLowerCase))
+      map.insert(std::make_pair(name, it->second));
+  }
+}
+
+// Type used in UploadDataStream.
+typedef std::vector<std::unique_ptr<net::UploadElementReader>>
+    UploadElementReaders;
+
+}  // namespace
+
+#define CHECK_READONLY_RETURN(val)         \
+  if (read_only_) {                        \
+    NOTREACHED() << "object is read only"; \
+    return val;                            \
+  }
+
+#define CHECK_READONLY_RETURN_VOID()       \
+  if (read_only_) {                        \
+    NOTREACHED() << "object is read only"; \
+    return;                                \
+  }
+
+#define SETBOOLFLAG(obj, flags, method, FLAG) \
+  obj.method((flags & (FLAG)) == (FLAG))
+
+// CefRequest -----------------------------------------------------------------
+
+// static
+CefRefPtr<CefRequest> CefRequest::Create() {
+  CefRefPtr<CefRequest> request(new CefRequestImpl());
+  return request;
+}
+
+// CefRequestImpl -------------------------------------------------------------
+
+CefRequestImpl::CefRequestImpl() {
+  // Verify that our enum matches Chromium's values.
+  static_assert(static_cast<int>(REFERRER_POLICY_LAST_VALUE) ==
+                    static_cast<int>(net::URLRequest::MAX_REFERRER_POLICY),
+                "enum mismatch");
+
+  base::AutoLock lock_scope(lock_);
+  Reset();
+}
+
+bool CefRequestImpl::IsReadOnly() {
+  base::AutoLock lock_scope(lock_);
+  return read_only_;
+}
+
+CefString CefRequestImpl::GetURL() {
+  base::AutoLock lock_scope(lock_);
+  return url_.spec();
+}
+
+void CefRequestImpl::SetURL(const CefString& url) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  const GURL& new_url = GURL(url.ToString());
+  if (url_ != new_url) {
+    Changed(kChangedUrl);
+    url_ = new_url;
+  }
+}
+
+CefString CefRequestImpl::GetMethod() {
+  base::AutoLock lock_scope(lock_);
+  return method_;
+}
+
+void CefRequestImpl::SetMethod(const CefString& method) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  const std::string& new_method = method;
+  if (method_ != new_method) {
+    Changed(kChangedMethod);
+    method_ = new_method;
+  }
+}
+
+void CefRequestImpl::SetReferrer(const CefString& referrer_url,
+                                 ReferrerPolicy policy) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  const auto& sanitized_referrer = content::Referrer::SanitizeForRequest(
+      url_, content::Referrer(GURL(referrer_url.ToString()),
+                              NetReferrerPolicyToBlinkReferrerPolicy(policy)));
+  const auto sanitized_policy =
+      BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
+
+  if (referrer_url_ != sanitized_referrer.url ||
+      referrer_policy_ != sanitized_policy) {
+    Changed(kChangedReferrer);
+    referrer_url_ = sanitized_referrer.url;
+    referrer_policy_ = sanitized_policy;
+  }
+}
+
+CefString CefRequestImpl::GetReferrerURL() {
+  base::AutoLock lock_scope(lock_);
+  return referrer_url_.spec();
+}
+
+CefRequestImpl::ReferrerPolicy CefRequestImpl::GetReferrerPolicy() {
+  base::AutoLock lock_scope(lock_);
+  return referrer_policy_;
+}
+
+CefRefPtr<CefPostData> CefRequestImpl::GetPostData() {
+  base::AutoLock lock_scope(lock_);
+  return postdata_;
+}
+
+void CefRequestImpl::SetPostData(CefRefPtr<CefPostData> postData) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  Changed(kChangedPostData);
+  postdata_ = postData;
+}
+
+void CefRequestImpl::GetHeaderMap(HeaderMap& headerMap) {
+  base::AutoLock lock_scope(lock_);
+  headerMap = headermap_;
+}
+
+void CefRequestImpl::SetHeaderMap(const HeaderMap& headerMap) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  Changed(kChangedHeaderMap);
+  ::GetHeaderMap(headerMap, headermap_);
+}
+
+CefString CefRequestImpl::GetHeaderByName(const CefString& name) {
+  base::AutoLock lock_scope(lock_);
+
+  std::string nameLower = name;
+  HttpHeaderUtils::MakeASCIILower(&nameLower);
+
+  auto it = HttpHeaderUtils::FindHeaderInMap(nameLower, headermap_);
+  if (it != headermap_.end())
+    return it->second;
+
+  return CefString();
+}
+
+void CefRequestImpl::SetHeaderByName(const CefString& name,
+                                     const CefString& value,
+                                     bool overwrite) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  std::string nameLower = name;
+  HttpHeaderUtils::MakeASCIILower(&nameLower);
+
+  // Do not include Referer in the header map.
+  if (nameLower == kReferrerLowerCase)
+    return;
+
+  Changed(kChangedHeaderMap);
+
+  // There may be multiple values, so remove any first.
+  for (auto it = headermap_.begin(); it != headermap_.end();) {
+    if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameLower)) {
+      if (!overwrite)
+        return;
+      it = headermap_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+
+  headermap_.insert(std::make_pair(name, value));
+}
+
+void CefRequestImpl::Set(const CefString& url,
+                         const CefString& method,
+                         CefRefPtr<CefPostData> postData,
+                         const HeaderMap& headerMap) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  const GURL& new_url = GURL(url.ToString());
+  if (url_ != new_url) {
+    Changed(kChangedUrl);
+    url_ = new_url;
+  }
+  const std::string& new_method = method;
+  if (method_ != new_method) {
+    Changed(kChangedMethod);
+    method_ = new_method;
+  }
+  if (postdata_ != postData) {
+    Changed(kChangedPostData);
+    postdata_ = postData;
+  }
+  Changed(kChangedHeaderMap);
+  ::GetHeaderMap(headerMap, headermap_);
+}
+
+int CefRequestImpl::GetFlags() {
+  base::AutoLock lock_scope(lock_);
+  return flags_;
+}
+
+void CefRequestImpl::SetFlags(int flags) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  if (flags_ != flags) {
+    Changed(kChangedFlags);
+    flags_ = flags;
+  }
+}
+
+CefString CefRequestImpl::GetFirstPartyForCookies() {
+  base::AutoLock lock_scope(lock_);
+  return site_for_cookies_.RepresentativeUrl().spec();
+}
+
+void CefRequestImpl::SetFirstPartyForCookies(const CefString& url) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  auto new_site = net::SiteForCookies::FromUrl(GURL(url.ToString()));
+  if (!new_site.IsEquivalent(site_for_cookies_)) {
+    Changed(kChangedSiteForCookies);
+    site_for_cookies_ = new_site;
+  }
+}
+
+CefRequestImpl::ResourceType CefRequestImpl::GetResourceType() {
+  base::AutoLock lock_scope(lock_);
+  return resource_type_;
+}
+
+CefRequestImpl::TransitionType CefRequestImpl::GetTransitionType() {
+  base::AutoLock lock_scope(lock_);
+  return transition_type_;
+}
+
+uint64 CefRequestImpl::GetIdentifier() {
+  base::AutoLock lock_scope(lock_);
+  return identifier_;
+}
+
+void CefRequestImpl::Set(const network::ResourceRequest* request,
+                         uint64 identifier) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  Reset();
+
+  url_ = request->url;
+  method_ = request->method;
+  identifier_ = identifier;
+
+  if (request->referrer.is_valid()) {
+    const auto& sanitized_referrer = content::Referrer::SanitizeForRequest(
+        request->url,
+        content::Referrer(
+            request->referrer,
+            NetReferrerPolicyToBlinkReferrerPolicy(
+                static_cast<cef_referrer_policy_t>(request->referrer_policy))));
+    referrer_url_ = sanitized_referrer.url;
+    referrer_policy_ =
+        BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
+  }
+
+  // Transfer request headers.
+  ::GetHeaderMap(request->headers, headermap_);
+
+  // Transfer post data, if any.
+  if (request->request_body) {
+    postdata_ = CefPostData::Create();
+    static_cast<CefPostDataImpl*>(postdata_.get())->Set(*request->request_body);
+  }
+
+  site_for_cookies_ = request->site_for_cookies;
+
+  resource_type_ = static_cast<cef_resource_type_t>(request->resource_type);
+  transition_type_ =
+      static_cast<cef_transition_type_t>(request->transition_type);
+}
+
+void CefRequestImpl::Get(network::ResourceRequest* request,
+                         bool changed_only) const {
+  base::AutoLock lock_scope(lock_);
+
+  if (ShouldSet(kChangedUrl, changed_only))
+    request->url = url_;
+
+  if (ShouldSet(kChangedMethod, changed_only))
+    request->method = method_;
+
+  if (ShouldSet(kChangedReferrer, changed_only)) {
+    request->referrer = referrer_url_;
+    request->referrer_policy =
+        static_cast<net::URLRequest::ReferrerPolicy>(referrer_policy_);
+  }
+
+  if (ShouldSet(kChangedHeaderMap, changed_only)) {
+    net::HttpRequestHeaders headers;
+    headers.AddHeadersFromString(HttpHeaderUtils::GenerateHeaders(headermap_));
+    request->headers.Swap(&headers);
+  }
+
+  if (ShouldSet(kChangedPostData, changed_only)) {
+    if (postdata_.get()) {
+      request->request_body =
+          static_cast<CefPostDataImpl*>(postdata_.get())->GetBody();
+    } else if (request->request_body) {
+      request->request_body = nullptr;
+    }
+  }
+
+  if (!site_for_cookies_.IsNull() &&
+      ShouldSet(kChangedSiteForCookies, changed_only)) {
+    request->site_for_cookies = site_for_cookies_;
+  }
+
+  if (ShouldSet(kChangedFlags, changed_only)) {
+    int flags = flags_;
+    if (!(flags & kURCachePolicyMask)) {
+      // Only consider the Cache-Control directives when a cache policy is not
+      // explicitly set on the request.
+      flags |= GetCacheControlHeaderPolicy(headermap_);
+    }
+
+    int net_flags = 0;
+
+    if (flags & UR_FLAG_SKIP_CACHE) {
+      net_flags |= net::LOAD_BYPASS_CACHE;
+    }
+    if (flags & UR_FLAG_ONLY_FROM_CACHE) {
+      net_flags |= net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION;
+    }
+    if (flags & UR_FLAG_DISABLE_CACHE) {
+      net_flags |= net::LOAD_DISABLE_CACHE;
+    }
+
+    if (!(flags & UR_FLAG_ALLOW_STORED_CREDENTIALS)) {
+      net_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA |
+                   net::LOAD_DO_NOT_SEND_COOKIES |
+                   net::LOAD_DO_NOT_SAVE_COOKIES;
+    }
+
+    request->load_flags = net_flags;
+  }
+}
+
+void CefRequestImpl::Set(const net::RedirectInfo& redirect_info) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  url_ = redirect_info.new_url;
+  method_ = redirect_info.new_method;
+  site_for_cookies_ = redirect_info.new_site_for_cookies;
+
+  const auto& sanitized_referrer = content::Referrer::SanitizeForRequest(
+      redirect_info.new_url,
+      content::Referrer(GURL(redirect_info.new_referrer),
+                        NetReferrerPolicyToBlinkReferrerPolicy(
+                            static_cast<cef_referrer_policy_t>(
+                                redirect_info.new_referrer_policy))));
+  referrer_url_ = sanitized_referrer.url;
+  referrer_policy_ =
+      BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
+}
+
+void CefRequestImpl::Set(const net::HttpRequestHeaders& headers) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  ::GetHeaderMap(headers, headermap_);
+}
+
+void CefRequestImpl::Set(
+    const navigation_interception::NavigationParams& params,
+    bool is_main_frame) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  Reset();
+
+  url_ = params.url();
+  method_ = params.is_post() ? "POST" : "GET";
+
+  const auto& sanitized_referrer =
+      content::Referrer::SanitizeForRequest(params.url(), params.referrer());
+  referrer_url_ = sanitized_referrer.url;
+  referrer_policy_ =
+      BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
+
+  resource_type_ = is_main_frame ? RT_MAIN_FRAME : RT_SUB_FRAME;
+  transition_type_ =
+      static_cast<cef_transition_type_t>(params.transition_type());
+}
+
+// static
+void CefRequestImpl::Get(const CefMsg_LoadRequest_Params& params,
+                         blink::WebURLRequest& request) {
+  request.SetUrl(params.url);
+  request.SetRequestorOrigin(blink::WebSecurityOrigin::Create(params.url));
+  if (!params.method.empty())
+    request.SetHttpMethod(blink::WebString::FromASCII(params.method));
+
+  if (params.referrer.is_valid()) {
+    const blink::WebString& referrer =
+        blink::WebSecurityPolicy::GenerateReferrerHeader(
+            NetReferrerPolicyToBlinkReferrerPolicy(
+                static_cast<cef_referrer_policy_t>(params.referrer_policy)),
+            params.url, blink::WebString::FromUTF8(params.referrer.spec()));
+    if (!referrer.IsEmpty()) {
+      request.SetReferrerString(referrer);
+      request.SetReferrerPolicy(NetReferrerPolicyToBlinkReferrerPolicy(
+          static_cast<cef_referrer_policy_t>(params.referrer_policy)));
+    }
+  }
+
+  CefRequest::HeaderMap headerMap;
+  if (!params.headers.empty()) {
+    for (net::HttpUtil::HeadersIterator i(params.headers.begin(),
+                                          params.headers.end(), "\n\r");
+         i.GetNext();) {
+      request.AddHttpHeaderField(blink::WebString::FromUTF8(i.name()),
+                                 blink::WebString::FromUTF8(i.values()));
+      headerMap.insert(std::make_pair(i.name(), i.values()));
+    }
+  }
+
+  if (params.upload_data.get()) {
+    const base::string16& method = request.HttpMethod().Utf16();
+    if (method == base::ASCIIToUTF16("GET") ||
+        method == base::ASCIIToUTF16("HEAD")) {
+      request.SetHttpMethod(blink::WebString::FromASCII("POST"));
+    }
+
+    // The comparison performed by httpHeaderField() is case insensitive.
+    if (request
+            .HttpHeaderField(blink::WebString::FromASCII(
+                net::HttpRequestHeaders::kContentType))
+            .length() == 0) {
+      request.SetHttpHeaderField(
+          blink::WebString::FromASCII(net::HttpRequestHeaders::kContentType),
+          blink::WebString::FromASCII(
+              net_service::kContentTypeApplicationFormURLEncoded));
+    }
+
+    blink::WebHTTPBody body;
+    body.Initialize();
+
+    for (const auto& element : params.upload_data->elements()) {
+      if (element->type() == net::UploadElement::TYPE_BYTES) {
+        blink::WebData data;
+        data.Assign(element->bytes(), element->bytes_length());
+        body.AppendData(data);
+      } else if (element->type() == net::UploadElement::TYPE_FILE) {
+        body.AppendFileRange(
+            FilePathStringToWebString(element->file_path().value()),
+            element->file_range_offset(), element->file_range_length(),
+            element->expected_file_modification_time());
+      } else {
+        NOTREACHED();
+      }
+    }
+
+    request.SetHttpBody(body);
+  }
+
+  if (!params.site_for_cookies.IsNull())
+    request.SetSiteForCookies(params.site_for_cookies);
+
+  int flags = params.load_flags;
+  if (!(flags & kURCachePolicyMask)) {
+    // Only consider the Cache-Control directives when a cache policy is not
+    // explicitly set on the request.
+    flags |= GetCacheControlHeaderPolicy(headerMap);
+  }
+  request.SetCacheMode(GetFetchCacheMode(flags));
+
+  SETBOOLFLAG(request, params.load_flags, SetAllowStoredCredentials,
+              UR_FLAG_ALLOW_STORED_CREDENTIALS);
+  SETBOOLFLAG(request, params.load_flags, SetReportUploadProgress,
+              UR_FLAG_REPORT_UPLOAD_PROGRESS);
+}
+
+void CefRequestImpl::Get(CefNavigateParams& params) const {
+  base::AutoLock lock_scope(lock_);
+
+  params.url = url_;
+  params.method = method_;
+
+  // Referrer policy will be applied later in the request pipeline.
+  params.referrer.url = referrer_url_;
+  params.referrer.policy =
+      NetReferrerPolicyToBlinkReferrerPolicy(referrer_policy_);
+
+  if (!headermap_.empty())
+    params.headers = HttpHeaderUtils::GenerateHeaders(headermap_);
+
+  if (postdata_) {
+    CefPostDataImpl* impl = static_cast<CefPostDataImpl*>(postdata_.get());
+    params.upload_data = new net::UploadData();
+    impl->Get(*params.upload_data.get());
+  }
+
+  params.site_for_cookies = site_for_cookies_;
+  params.load_flags = flags_;
+}
+
+void CefRequestImpl::SetReadOnly(bool read_only) {
+  base::AutoLock lock_scope(lock_);
+  if (read_only_ == read_only)
+    return;
+
+  read_only_ = read_only;
+
+  if (postdata_.get())
+    static_cast<CefPostDataImpl*>(postdata_.get())->SetReadOnly(read_only);
+}
+
+void CefRequestImpl::SetTrackChanges(bool track_changes,
+                                     bool backup_on_change) {
+  base::AutoLock lock_scope(lock_);
+  if (track_changes_ == track_changes)
+    return;
+
+  if (!track_changes && backup_on_change_)
+    backup_.reset();
+
+  track_changes_ = track_changes;
+  backup_on_change_ = track_changes ? backup_on_change : false;
+  changes_ = kChangedNone;
+
+  if (postdata_.get()) {
+    static_cast<CefPostDataImpl*>(postdata_.get())
+        ->SetTrackChanges(track_changes);
+  }
+}
+
+void CefRequestImpl::RevertChanges() {
+  base::AutoLock lock_scope(lock_);
+  DCHECK(!read_only_);
+  DCHECK(track_changes_);
+  DCHECK(backup_on_change_);
+  if (!backup_)
+    return;
+
+  // Restore the original values if a backup exists.
+  if (backup_->backups_ & kChangedUrl)
+    url_ = backup_->url_;
+  if (backup_->backups_ & kChangedMethod)
+    method_ = backup_->method_;
+  if (backup_->backups_ & kChangedReferrer) {
+    referrer_url_ = backup_->referrer_url_;
+    referrer_policy_ = backup_->referrer_policy_;
+  }
+  if (backup_->backups_ & kChangedPostData)
+    postdata_ = backup_->postdata_;
+  if (backup_->backups_ & kChangedHeaderMap) {
+    DCHECK(backup_->headermap_);
+    headermap_.swap(*backup_->headermap_);
+  }
+  if (backup_->backups_ & kChangedFlags)
+    flags_ = backup_->flags_;
+  if (backup_->backups_ & kChangedSiteForCookies)
+    site_for_cookies_ = backup_->site_for_cookies_;
+
+  backup_.reset();
+}
+
+void CefRequestImpl::DiscardChanges() {
+  base::AutoLock lock_scope(lock_);
+  DCHECK(track_changes_);
+  DCHECK(backup_on_change_);
+  backup_.reset();
+}
+
+uint8_t CefRequestImpl::GetChanges() const {
+  base::AutoLock lock_scope(lock_);
+
+  uint8_t changes = changes_;
+  if (postdata_.get() &&
+      static_cast<CefPostDataImpl*>(postdata_.get())->HasChanges()) {
+    changes |= kChangedPostData;
+  }
+  return changes;
+}
+
+// From content/child/web_url_loader_impl.cc
+// static
+network::mojom::ReferrerPolicy
+CefRequestImpl::NetReferrerPolicyToBlinkReferrerPolicy(
+    cef_referrer_policy_t net_policy) {
+  switch (net_policy) {
+    case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+      return network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade;
+    case REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
+      return network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin;
+    case REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
+      return network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin;
+    case REFERRER_POLICY_NEVER_CLEAR_REFERRER:
+      return network::mojom::ReferrerPolicy::kAlways;
+    case REFERRER_POLICY_ORIGIN:
+      return network::mojom::ReferrerPolicy::kOrigin;
+    case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN:
+      return network::mojom::ReferrerPolicy::kSameOrigin;
+    case REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+      return network::mojom::ReferrerPolicy::kStrictOrigin;
+    case REFERRER_POLICY_NO_REFERRER:
+      return network::mojom::ReferrerPolicy::kNever;
+  }
+  NOTREACHED();
+  return network::mojom::ReferrerPolicy::kDefault;
+}
+
+// static
+cef_referrer_policy_t CefRequestImpl::BlinkReferrerPolicyToNetReferrerPolicy(
+    network::mojom::ReferrerPolicy blink_policy) {
+  switch (blink_policy) {
+    case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade:
+      return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
+    case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin:
+      return REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
+    case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin:
+      return REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN;
+    case network::mojom::ReferrerPolicy::kAlways:
+      return REFERRER_POLICY_NEVER_CLEAR_REFERRER;
+    case network::mojom::ReferrerPolicy::kOrigin:
+      return REFERRER_POLICY_ORIGIN;
+    case network::mojom::ReferrerPolicy::kSameOrigin:
+      return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN;
+    case network::mojom::ReferrerPolicy::kStrictOrigin:
+      return REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
+    case network::mojom::ReferrerPolicy::kNever:
+      return REFERRER_POLICY_NO_REFERRER;
+    case network::mojom::ReferrerPolicy::kDefault:
+      return REFERRER_POLICY_DEFAULT;
+  }
+  NOTREACHED();
+  return REFERRER_POLICY_DEFAULT;
+}
+
+void CefRequestImpl::Changed(uint8_t changes) {
+  lock_.AssertAcquired();
+  if (!track_changes_)
+    return;
+
+  if (backup_on_change_) {
+    if (!backup_)
+      backup_.reset(new Backup());
+
+    // Set the backup values if not already set.
+    if ((changes & kChangedUrl) && !(backup_->backups_ & kChangedUrl)) {
+      backup_->url_ = url_;
+      backup_->backups_ |= kChangedUrl;
+    }
+    if ((changes & kChangedMethod) && !(backup_->backups_ & kChangedMethod)) {
+      backup_->method_ = method_;
+      backup_->backups_ |= kChangedMethod;
+    }
+    if ((changes & kChangedReferrer) &&
+        !(backup_->backups_ & kChangedReferrer)) {
+      backup_->referrer_url_ = referrer_url_;
+      backup_->referrer_policy_ = referrer_policy_;
+      backup_->backups_ |= kChangedReferrer;
+    }
+    if ((changes & kChangedPostData) &&
+        !(backup_->backups_ & kChangedPostData)) {
+      backup_->postdata_ = postdata_;
+      backup_->backups_ |= kChangedPostData;
+    }
+    if ((changes & kChangedHeaderMap) &&
+        !(backup_->backups_ & kChangedHeaderMap)) {
+      backup_->headermap_.reset(new HeaderMap());
+      if (!headermap_.empty()) {
+        backup_->headermap_->insert(headermap_.begin(), headermap_.end());
+      }
+      backup_->backups_ |= kChangedHeaderMap;
+    }
+    if ((changes & kChangedFlags) && !(backup_->backups_ & kChangedFlags)) {
+      backup_->flags_ = flags_;
+      backup_->backups_ |= kChangedFlags;
+    }
+    if ((changes & kChangedSiteForCookies) &&
+        !(backup_->backups_ & kChangedSiteForCookies)) {
+      backup_->site_for_cookies_ = site_for_cookies_;
+      backup_->backups_ |= kChangedSiteForCookies;
+    }
+  }
+
+  changes_ |= changes;
+}
+
+bool CefRequestImpl::ShouldSet(uint8_t changes, bool changed_only) const {
+  lock_.AssertAcquired();
+
+  // Always change if changes are not being tracked.
+  if (!track_changes_)
+    return true;
+
+  // Always change if changed-only was not requested.
+  if (!changed_only)
+    return true;
+
+  // Change if the |changes| bit flag has been set.
+  if ((changes_ & changes) == changes)
+    return true;
+
+  if ((changes & kChangedPostData) == kChangedPostData) {
+    // Change if the post data object was modified directly.
+    if (postdata_.get() &&
+        static_cast<CefPostDataImpl*>(postdata_.get())->HasChanges()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void CefRequestImpl::Reset() {
+  lock_.AssertAcquired();
+  DCHECK(!read_only_);
+
+  url_ = GURL();
+  method_ = "GET";
+  referrer_url_ = GURL();
+  referrer_policy_ = REFERRER_POLICY_DEFAULT;
+  postdata_ = nullptr;
+  headermap_.clear();
+  resource_type_ = RT_SUB_RESOURCE;
+  transition_type_ = TT_EXPLICIT;
+  identifier_ = 0U;
+  flags_ = UR_FLAG_NONE;
+  site_for_cookies_ = net::SiteForCookies();
+
+  changes_ = kChangedNone;
+}
+
+// CefPostData ----------------------------------------------------------------
+
+// static
+CefRefPtr<CefPostData> CefPostData::Create() {
+  CefRefPtr<CefPostData> postdata(new CefPostDataImpl());
+  return postdata;
+}
+
+// CefPostDataImpl ------------------------------------------------------------
+
+CefPostDataImpl::CefPostDataImpl()
+    : read_only_(false),
+      has_excluded_elements_(false),
+      track_changes_(false),
+      has_changes_(false) {}
+
+bool CefPostDataImpl::IsReadOnly() {
+  base::AutoLock lock_scope(lock_);
+  return read_only_;
+}
+
+bool CefPostDataImpl::HasExcludedElements() {
+  base::AutoLock lock_scope(lock_);
+  return has_excluded_elements_;
+}
+
+size_t CefPostDataImpl::GetElementCount() {
+  base::AutoLock lock_scope(lock_);
+  return elements_.size();
+}
+
+void CefPostDataImpl::GetElements(ElementVector& elements) {
+  base::AutoLock lock_scope(lock_);
+  elements = elements_;
+}
+
+bool CefPostDataImpl::RemoveElement(CefRefPtr<CefPostDataElement> element) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN(false);
+
+  ElementVector::iterator it = elements_.begin();
+  for (; it != elements_.end(); ++it) {
+    if (it->get() == element.get()) {
+      elements_.erase(it);
+      Changed();
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool CefPostDataImpl::AddElement(CefRefPtr<CefPostDataElement> element) {
+  bool found = false;
+
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN(false);
+
+  // check that the element isn't already in the list before adding
+  ElementVector::const_iterator it = elements_.begin();
+  for (; it != elements_.end(); ++it) {
+    if (it->get() == element.get()) {
+      found = true;
+      break;
+    }
+  }
+
+  if (!found) {
+    elements_.push_back(element);
+    Changed();
+  }
+
+  return !found;
+}
+
+void CefPostDataImpl::RemoveElements() {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  elements_.clear();
+  Changed();
+}
+
+void CefPostDataImpl::Set(const network::ResourceRequestBody& body) {
+  {
+    base::AutoLock lock_scope(lock_);
+    CHECK_READONLY_RETURN_VOID();
+  }
+
+  CefRefPtr<CefPostDataElement> postelem;
+
+  for (const auto& element : *body.elements()) {
+    postelem = CefPostDataElement::Create();
+    static_cast<CefPostDataElementImpl*>(postelem.get())->Set(element);
+    AddElement(postelem);
+  }
+}
+
+scoped_refptr<network::ResourceRequestBody> CefPostDataImpl::GetBody() const {
+  base::AutoLock lock_scope(lock_);
+
+  scoped_refptr<network::ResourceRequestBody> body =
+      new network::ResourceRequestBody();
+  for (const auto& element : elements_) {
+    static_cast<CefPostDataElementImpl*>(element.get())->Get(*body);
+  }
+  return body;
+}
+
+void CefPostDataImpl::Set(const net::UploadData& data) {
+  {
+    base::AutoLock lock_scope(lock_);
+    CHECK_READONLY_RETURN_VOID();
+  }
+
+  CefRefPtr<CefPostDataElement> postelem;
+
+  for (const auto& element : data.elements()) {
+    postelem = CefPostDataElement::Create();
+    static_cast<CefPostDataElementImpl*>(postelem.get())->Set(*element);
+    AddElement(postelem);
+  }
+}
+
+void CefPostDataImpl::Set(const net::UploadDataStream& data_stream) {
+  {
+    base::AutoLock lock_scope(lock_);
+    CHECK_READONLY_RETURN_VOID();
+  }
+
+  CefRefPtr<CefPostDataElement> postelem;
+
+  const UploadElementReaders* elements = data_stream.GetElementReaders();
+  if (elements) {
+    UploadElementReaders::const_iterator it = elements->begin();
+    for (; it != elements->end(); ++it) {
+      postelem = CefPostDataElement::Create();
+      static_cast<CefPostDataElementImpl*>(postelem.get())->Set(**it);
+      if (postelem->GetType() != PDE_TYPE_EMPTY)
+        AddElement(postelem);
+      else if (!has_excluded_elements_)
+        has_excluded_elements_ = true;
+    }
+  }
+}
+
+void CefPostDataImpl::Get(net::UploadData& data) const {
+  base::AutoLock lock_scope(lock_);
+
+  net::UploadData::ElementsVector data_elements;
+  for (const auto& element : elements_) {
+    std::unique_ptr<net::UploadElement> data_element =
+        std::make_unique<net::UploadElement>();
+    static_cast<CefPostDataElementImpl*>(element.get())
+        ->Get(*data_element.get());
+    data_elements.push_back(std::move(data_element));
+  }
+  data.swap_elements(&data_elements);
+}
+
+std::unique_ptr<net::UploadDataStream> CefPostDataImpl::Get() const {
+  base::AutoLock lock_scope(lock_);
+
+  UploadElementReaders element_readers;
+  for (const auto& element : elements_) {
+    element_readers.push_back(
+        static_cast<CefPostDataElementImpl*>(element.get())->Get());
+  }
+
+  return std::make_unique<net::ElementsUploadDataStream>(
+      std::move(element_readers), 0);
+}
+
+void CefPostDataImpl::SetReadOnly(bool read_only) {
+  base::AutoLock lock_scope(lock_);
+  if (read_only_ == read_only)
+    return;
+
+  read_only_ = read_only;
+
+  ElementVector::const_iterator it = elements_.begin();
+  for (; it != elements_.end(); ++it) {
+    static_cast<CefPostDataElementImpl*>(it->get())->SetReadOnly(read_only);
+  }
+}
+
+void CefPostDataImpl::SetTrackChanges(bool track_changes) {
+  base::AutoLock lock_scope(lock_);
+  if (track_changes_ == track_changes)
+    return;
+
+  track_changes_ = track_changes;
+  has_changes_ = false;
+
+  ElementVector::const_iterator it = elements_.begin();
+  for (; it != elements_.end(); ++it) {
+    static_cast<CefPostDataElementImpl*>(it->get())->SetTrackChanges(
+        track_changes);
+  }
+}
+
+bool CefPostDataImpl::HasChanges() const {
+  base::AutoLock lock_scope(lock_);
+  if (has_changes_)
+    return true;
+
+  ElementVector::const_iterator it = elements_.begin();
+  for (; it != elements_.end(); ++it) {
+    if (static_cast<CefPostDataElementImpl*>(it->get())->HasChanges())
+      return true;
+  }
+
+  return false;
+}
+
+void CefPostDataImpl::Changed() {
+  lock_.AssertAcquired();
+  if (track_changes_ && !has_changes_)
+    has_changes_ = true;
+}
+
+// CefPostDataElement ---------------------------------------------------------
+
+// static
+CefRefPtr<CefPostDataElement> CefPostDataElement::Create() {
+  CefRefPtr<CefPostDataElement> element(new CefPostDataElementImpl());
+  return element;
+}
+
+// CefPostDataElementImpl -----------------------------------------------------
+
+CefPostDataElementImpl::CefPostDataElementImpl()
+    : type_(PDE_TYPE_EMPTY),
+      read_only_(false),
+      track_changes_(false),
+      has_changes_(false) {
+  memset(&data_, 0, sizeof(data_));
+}
+
+CefPostDataElementImpl::~CefPostDataElementImpl() {
+  Cleanup();
+}
+
+bool CefPostDataElementImpl::IsReadOnly() {
+  base::AutoLock lock_scope(lock_);
+  return read_only_;
+}
+
+void CefPostDataElementImpl::SetToEmpty() {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  Cleanup();
+  Changed();
+}
+
+void CefPostDataElementImpl::SetToFile(const CefString& fileName) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  // Clear any data currently in the element
+  Cleanup();
+
+  // Assign the new data
+  type_ = PDE_TYPE_FILE;
+  cef_string_copy(fileName.c_str(), fileName.length(), &data_.filename);
+
+  Changed();
+}
+
+void CefPostDataElementImpl::SetToBytes(size_t size, const void* bytes) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  // Clear any data currently in the element
+  Cleanup();
+
+  // Assign the new data
+  void* data = malloc(size);
+  DCHECK(data != nullptr);
+  if (data == nullptr)
+    return;
+
+  memcpy(data, bytes, size);
+
+  type_ = PDE_TYPE_BYTES;
+  data_.bytes.bytes = data;
+  data_.bytes.size = size;
+
+  Changed();
+}
+
+CefPostDataElement::Type CefPostDataElementImpl::GetType() {
+  base::AutoLock lock_scope(lock_);
+  return type_;
+}
+
+CefString CefPostDataElementImpl::GetFile() {
+  base::AutoLock lock_scope(lock_);
+  DCHECK(type_ == PDE_TYPE_FILE);
+  CefString filename;
+  if (type_ == PDE_TYPE_FILE)
+    filename.FromString(data_.filename.str, data_.filename.length, false);
+  return filename;
+}
+
+size_t CefPostDataElementImpl::GetBytesCount() {
+  base::AutoLock lock_scope(lock_);
+  DCHECK(type_ == PDE_TYPE_BYTES);
+  size_t size = 0;
+  if (type_ == PDE_TYPE_BYTES)
+    size = data_.bytes.size;
+  return size;
+}
+
+size_t CefPostDataElementImpl::GetBytes(size_t size, void* bytes) {
+  base::AutoLock lock_scope(lock_);
+  DCHECK(type_ == PDE_TYPE_BYTES);
+  size_t rv = 0;
+  if (type_ == PDE_TYPE_BYTES) {
+    rv = (size < data_.bytes.size ? size : data_.bytes.size);
+    memcpy(bytes, data_.bytes.bytes, rv);
+  }
+  return rv;
+}
+
+void CefPostDataElementImpl::Set(const network::DataElement& element) {
+  {
+    base::AutoLock lock_scope(lock_);
+    CHECK_READONLY_RETURN_VOID();
+  }
+
+  if (element.type() == network::mojom::DataElementType::kBytes) {
+    SetToBytes(element.length(), element.bytes());
+  } else if (element.type() == network::mojom::DataElementType::kFile) {
+    SetToFile(element.path().value());
+  }
+}
+
+void CefPostDataElementImpl::Get(network::ResourceRequestBody& body) const {
+  base::AutoLock lock_scope(lock_);
+
+  if (type_ == PDE_TYPE_BYTES) {
+    body.AppendBytes(static_cast<char*>(data_.bytes.bytes), data_.bytes.size);
+  } else if (type_ == PDE_TYPE_FILE) {
+    base::FilePath path = base::FilePath(CefString(&data_.filename));
+    body.AppendFileRange(path, 0, std::numeric_limits<uint64_t>::max(),
+                         base::Time());
+  } else {
+    NOTREACHED();
+  }
+}
+
+void CefPostDataElementImpl::Set(const net::UploadElement& element) {
+  {
+    base::AutoLock lock_scope(lock_);
+    CHECK_READONLY_RETURN_VOID();
+  }
+
+  if (element.type() == net::UploadElement::TYPE_BYTES) {
+    SetToBytes(element.bytes_length(), element.bytes());
+  } else if (element.type() == net::UploadElement::TYPE_FILE) {
+    SetToFile(element.file_path().value());
+  } else {
+    NOTREACHED();
+  }
+}
+
+void CefPostDataElementImpl::Set(
+    const net::UploadElementReader& element_reader) {
+  {
+    base::AutoLock lock_scope(lock_);
+    CHECK_READONLY_RETURN_VOID();
+  }
+
+  const net::UploadBytesElementReader* bytes_reader =
+      element_reader.AsBytesReader();
+  if (bytes_reader) {
+    SetToBytes(bytes_reader->length(), bytes_reader->bytes());
+    return;
+  }
+
+  const net::UploadFileElementReader* file_reader =
+      element_reader.AsFileReader();
+  if (file_reader) {
+    SetToFile(file_reader->path().value());
+    return;
+  }
+
+  // Chunked uploads cannot currently be represented.
+  SetToEmpty();
+}
+
+void CefPostDataElementImpl::Get(net::UploadElement& element) const {
+  base::AutoLock lock_scope(lock_);
+
+  if (type_ == PDE_TYPE_BYTES) {
+    element.SetToBytes(static_cast<char*>(data_.bytes.bytes), data_.bytes.size);
+  } else if (type_ == PDE_TYPE_FILE) {
+    base::FilePath path = base::FilePath(CefString(&data_.filename));
+    element.SetToFilePath(path);
+  } else {
+    NOTREACHED();
+  }
+}
+
+std::unique_ptr<net::UploadElementReader> CefPostDataElementImpl::Get() const {
+  base::AutoLock lock_scope(lock_);
+
+  if (type_ == PDE_TYPE_BYTES) {
+    net::UploadElement* element = new net::UploadElement();
+    element->SetToBytes(static_cast<char*>(data_.bytes.bytes),
+                        data_.bytes.size);
+    return std::make_unique<BytesElementReader>(base::WrapUnique(element));
+  } else if (type_ == PDE_TYPE_FILE) {
+    net::UploadElement* element = new net::UploadElement();
+    base::FilePath path = base::FilePath(CefString(&data_.filename));
+    element->SetToFilePath(path);
+    return std::make_unique<FileElementReader>(base::WrapUnique(element));
+  } else {
+    NOTREACHED();
+    return nullptr;
+  }
+}
+
+void CefPostDataElementImpl::SetReadOnly(bool read_only) {
+  base::AutoLock lock_scope(lock_);
+  if (read_only_ == read_only)
+    return;
+
+  read_only_ = read_only;
+}
+
+void CefPostDataElementImpl::SetTrackChanges(bool track_changes) {
+  base::AutoLock lock_scope(lock_);
+  if (track_changes_ == track_changes)
+    return;
+
+  track_changes_ = track_changes;
+  has_changes_ = false;
+}
+
+bool CefPostDataElementImpl::HasChanges() const {
+  base::AutoLock lock_scope(lock_);
+  return has_changes_;
+}
+
+void CefPostDataElementImpl::Changed() {
+  lock_.AssertAcquired();
+  if (track_changes_ && !has_changes_)
+    has_changes_ = true;
+}
+
+void CefPostDataElementImpl::Cleanup() {
+  if (type_ == PDE_TYPE_EMPTY)
+    return;
+
+  if (type_ == PDE_TYPE_BYTES)
+    free(data_.bytes.bytes);
+  else if (type_ == PDE_TYPE_FILE)
+    cef_string_clear(&data_.filename);
+  type_ = PDE_TYPE_EMPTY;
+  memset(&data_, 0, sizeof(data_));
+}
diff --git a/src/libcef/common/request_impl.h b/src/libcef/common/request_impl.h
new file mode 100644
index 0000000..aa6462e
--- /dev/null
+++ b/src/libcef/common/request_impl.h
@@ -0,0 +1,299 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_REQUEST_IMPL_H_
+#define CEF_LIBCEF_COMMON_REQUEST_IMPL_H_
+#pragma once
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "include/cef_request.h"
+
+#include "base/synchronization/lock.h"
+#include "net/cookies/site_for_cookies.h"
+#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "url/gurl.h"
+
+namespace blink {
+class WebURLRequest;
+}  // namespace blink
+
+namespace navigation_interception {
+class NavigationParams;
+}
+
+namespace net {
+class HttpRequestHeaders;
+struct RedirectInfo;
+class UploadData;
+class UploadDataStream;
+class UploadElement;
+class UploadElementReader;
+}  // namespace net
+
+namespace network {
+class DataElement;
+struct ResourceRequest;
+class ResourceRequestBody;
+}  // namespace network
+
+struct CefMsg_LoadRequest_Params;
+struct CefNavigateParams;
+
+// Implementation of CefRequest
+class CefRequestImpl : public CefRequest {
+ public:
+  enum Changes {
+    kChangedNone = 0,
+    kChangedUrl = 1 << 0,
+    kChangedMethod = 1 << 1,
+    kChangedReferrer = 1 << 2,
+    kChangedPostData = 1 << 3,
+    kChangedHeaderMap = 1 << 4,
+    kChangedFlags = 1 << 5,
+    kChangedSiteForCookies = 1 << 6,
+  };
+
+  CefRequestImpl();
+
+  bool IsReadOnly() override;
+  CefString GetURL() override;
+  void SetURL(const CefString& url) override;
+  CefString GetMethod() override;
+  void SetMethod(const CefString& method) override;
+  void SetReferrer(const CefString& referrer_url,
+                   ReferrerPolicy policy) override;
+  CefString GetReferrerURL() override;
+  ReferrerPolicy GetReferrerPolicy() override;
+  CefRefPtr<CefPostData> GetPostData() override;
+  void SetPostData(CefRefPtr<CefPostData> postData) override;
+  void GetHeaderMap(HeaderMap& headerMap) override;
+  void SetHeaderMap(const HeaderMap& headerMap) override;
+  CefString GetHeaderByName(const CefString& name) override;
+  void SetHeaderByName(const CefString& name,
+                       const CefString& value,
+                       bool overwrite) override;
+  void Set(const CefString& url,
+           const CefString& method,
+           CefRefPtr<CefPostData> postData,
+           const HeaderMap& headerMap) override;
+  int GetFlags() override;
+  void SetFlags(int flags) override;
+  CefString GetFirstPartyForCookies() override;
+  void SetFirstPartyForCookies(const CefString& url) override;
+  ResourceType GetResourceType() override;
+  TransitionType GetTransitionType() override;
+  uint64 GetIdentifier() override;
+
+  // Populate this object from the ResourceRequest object.
+  void Set(const network::ResourceRequest* request, uint64 identifier);
+
+  // Populate the ResourceRequest object from this object.
+  // If |changed_only| is true then only the changed fields will be updated.
+  void Get(network::ResourceRequest* request, bool changed_only) const;
+
+  // Populate this object from the RedirectInfo object.
+  void Set(const net::RedirectInfo& redirect_info);
+
+  // Populate this object from teh HttpRequestHeaders object.
+  void Set(const net::HttpRequestHeaders& headers);
+
+  // Populate this object from the NavigationParams object.
+  // TODO(cef): Remove the |is_main_frame| argument once NavigationParams is
+  // reliable in reporting that value.
+  // Called from content_browser_client.cc NavigationOnUIThread().
+  void Set(const navigation_interception::NavigationParams& params,
+           bool is_main_frame);
+
+  // Populate the WebURLRequest object based on the contents of |params|.
+  // Called from CefBrowserImpl::LoadRequest().
+  static void Get(const CefMsg_LoadRequest_Params& params,
+                  blink::WebURLRequest& request);
+
+  // Populate the CefNavigateParams object from this object.
+  // Called from CefBrowserHostImpl::LoadRequest().
+  void Get(CefNavigateParams& params) const;
+
+  void SetReadOnly(bool read_only);
+
+  // Enable or disable tracking of changes. If |track_changes| is true the
+  // status of changes will be tracked, and retrievable via GetChanges(). If
+  // |backup_on_change| is true the original value will be backed up before the
+  // first change. The original values can later be restored by calling
+  // RevertChanges() before calling SetTrackChanges(false).
+  void SetTrackChanges(bool track_changes, bool backup_on_change = false);
+  void RevertChanges();
+  void DiscardChanges();
+  uint8_t GetChanges() const;
+
+  static network::mojom::ReferrerPolicy NetReferrerPolicyToBlinkReferrerPolicy(
+      cef_referrer_policy_t net_policy);
+  static cef_referrer_policy_t BlinkReferrerPolicyToNetReferrerPolicy(
+      network::mojom::ReferrerPolicy blink_policy);
+
+ private:
+  // Mark values as changed. Must be called before the new values are assigned.
+  void Changed(uint8_t changes);
+
+  // Used with the Set() methods that export data to other object types. Returns
+  // true if the values should be set on the export object. If |changed_only| is
+  // true then only return true if the value has been changed in combination
+  // with track changes.
+  bool ShouldSet(uint8_t changes, bool changed_only) const;
+
+  void Reset();
+
+  GURL url_;
+  std::string method_;
+  GURL referrer_url_;
+  ReferrerPolicy referrer_policy_;
+  CefRefPtr<CefPostData> postdata_;
+  HeaderMap headermap_;
+  ResourceType resource_type_;
+  TransitionType transition_type_;
+  uint64 identifier_;
+
+  // The below members are used by CefURLRequest.
+  int flags_;
+  net::SiteForCookies site_for_cookies_;
+
+  // Stores backup of values for use with track changes.
+  struct Backup {
+    // Bitmask of values that have been backed up.
+    uint8_t backups_ = kChangedNone;
+
+    GURL url_;
+    std::string method_;
+    GURL referrer_url_;
+    ReferrerPolicy referrer_policy_;
+    CefRefPtr<CefPostData> postdata_;
+    std::unique_ptr<HeaderMap> headermap_;
+    int flags_;
+    net::SiteForCookies site_for_cookies_;
+  };
+  std::unique_ptr<Backup> backup_;
+
+  // True if this object is read-only.
+  bool read_only_ = false;
+
+  // True if this object should track changes.
+  bool track_changes_ = false;
+
+  // True if original values should be backed up when |track_changes_| is true.
+  bool backup_on_change_ = false;
+
+  // Bitmask of |Changes| values which indicate which fields have changed.
+  uint8_t changes_ = kChangedNone;
+
+  mutable base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefRequestImpl);
+};
+
+// Implementation of CefPostData
+class CefPostDataImpl : public CefPostData {
+ public:
+  CefPostDataImpl();
+
+  bool IsReadOnly() override;
+  bool HasExcludedElements() override;
+  size_t GetElementCount() override;
+  void GetElements(ElementVector& elements) override;
+  bool RemoveElement(CefRefPtr<CefPostDataElement> element) override;
+  bool AddElement(CefRefPtr<CefPostDataElement> element) override;
+  void RemoveElements() override;
+
+  void Set(const network::ResourceRequestBody& body);
+  scoped_refptr<network::ResourceRequestBody> GetBody() const;
+  void Set(const net::UploadData& data);
+  void Set(const net::UploadDataStream& data_stream);
+  void Get(net::UploadData& data) const;
+  std::unique_ptr<net::UploadDataStream> Get() const;
+
+  void SetReadOnly(bool read_only);
+
+  void SetTrackChanges(bool track_changes);
+  bool HasChanges() const;
+
+ private:
+  void Changed();
+
+  ElementVector elements_;
+
+  // True if this object is read-only.
+  bool read_only_;
+
+  // True if this object has excluded elements.
+  bool has_excluded_elements_;
+
+  // True if this object should track changes.
+  bool track_changes_;
+
+  // True if this object has changes.
+  bool has_changes_;
+
+  mutable base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefPostDataImpl);
+};
+
+// Implementation of CefPostDataElement
+class CefPostDataElementImpl : public CefPostDataElement {
+ public:
+  CefPostDataElementImpl();
+  ~CefPostDataElementImpl() override;
+
+  bool IsReadOnly() override;
+  void SetToEmpty() override;
+  void SetToFile(const CefString& fileName) override;
+  void SetToBytes(size_t size, const void* bytes) override;
+  Type GetType() override;
+  CefString GetFile() override;
+  size_t GetBytesCount() override;
+  size_t GetBytes(size_t size, void* bytes) override;
+
+  void* GetBytes() { return data_.bytes.bytes; }
+
+  void Set(const network::DataElement& element);
+  void Get(network::ResourceRequestBody& body) const;
+  void Set(const net::UploadElement& element);
+  void Set(const net::UploadElementReader& element_reader);
+  void Get(net::UploadElement& element) const;
+  std::unique_ptr<net::UploadElementReader> Get() const;
+
+  void SetReadOnly(bool read_only);
+
+  void SetTrackChanges(bool track_changes);
+  bool HasChanges() const;
+
+ private:
+  void Changed();
+  void Cleanup();
+
+  Type type_;
+  union {
+    struct {
+      void* bytes;
+      size_t size;
+    } bytes;
+    cef_string_t filename;
+  } data_;
+
+  // True if this object is read-only.
+  bool read_only_;
+
+  // True if this object should track changes.
+  bool track_changes_;
+
+  // True if this object has changes.
+  bool has_changes_;
+
+  mutable base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefPostDataElementImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_REQUEST_IMPL_H_
diff --git a/src/libcef/common/resource_bundle_delegate.cc b/src/libcef/common/resource_bundle_delegate.cc
new file mode 100644
index 0000000..6746fd1
--- /dev/null
+++ b/src/libcef/common/resource_bundle_delegate.cc
@@ -0,0 +1,75 @@
+#include "libcef/common/resource_bundle_delegate.h"
+
+#include "libcef/common/content_client.h"
+
+base::FilePath CefResourceBundleDelegate::GetPathForResourcePack(
+    const base::FilePath& pack_path,
+    ui::ScaleFactor scale_factor) {
+  // Only allow the cef pack file to load.
+  if (!content_client_->pack_loading_disabled() &&
+      content_client_->allow_pack_file_load()) {
+    return pack_path;
+  }
+  return base::FilePath();
+}
+
+base::FilePath CefResourceBundleDelegate::GetPathForLocalePack(
+    const base::FilePath& pack_path,
+    const std::string& locale) {
+  if (!content_client_->pack_loading_disabled())
+    return pack_path;
+  return base::FilePath();
+}
+
+gfx::Image CefResourceBundleDelegate::GetImageNamed(int resource_id) {
+  return gfx::Image();
+}
+
+gfx::Image CefResourceBundleDelegate::GetNativeImageNamed(int resource_id) {
+  return gfx::Image();
+}
+
+base::RefCountedStaticMemory* CefResourceBundleDelegate::LoadDataResourceBytes(
+    int resource_id,
+    ui::ScaleFactor scale_factor) {
+  return nullptr;
+}
+
+bool CefResourceBundleDelegate::GetRawDataResource(int resource_id,
+                                                   ui::ScaleFactor scale_factor,
+                                                   base::StringPiece* value) {
+  if (content_client_->application().get()) {
+    CefRefPtr<CefResourceBundleHandler> handler =
+        content_client_->application()->GetResourceBundleHandler();
+    if (handler.get()) {
+      void* data = nullptr;
+      size_t data_size = 0;
+      if (scale_factor != ui::SCALE_FACTOR_NONE) {
+        if (handler->GetDataResourceForScale(
+                resource_id, static_cast<cef_scale_factor_t>(scale_factor),
+                data, data_size)) {
+          *value = base::StringPiece(static_cast<char*>(data), data_size);
+        }
+      } else if (handler->GetDataResource(resource_id, data, data_size)) {
+        *value = base::StringPiece(static_cast<char*>(data), data_size);
+      }
+    }
+  }
+
+  return (content_client_->pack_loading_disabled() || !value->empty());
+}
+
+bool CefResourceBundleDelegate::GetLocalizedString(int message_id,
+                                                   base::string16* value) {
+  if (content_client_->application().get()) {
+    CefRefPtr<CefResourceBundleHandler> handler =
+        content_client_->application()->GetResourceBundleHandler();
+    if (handler.get()) {
+      CefString cef_str;
+      if (handler->GetLocalizedString(message_id, cef_str))
+        *value = cef_str;
+    }
+  }
+
+  return (content_client_->pack_loading_disabled() || !value->empty());
+}
diff --git a/src/libcef/common/resource_bundle_delegate.h b/src/libcef/common/resource_bundle_delegate.h
new file mode 100644
index 0000000..ca6d5fd
--- /dev/null
+++ b/src/libcef/common/resource_bundle_delegate.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Embedded Framework Authors.
+// Portions copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_DELEGATE_H_
+#define CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_DELEGATE_H_
+#pragma once
+
+#include "ui/base/resource/resource_bundle.h"
+
+class CefContentClient;
+
+class CefResourceBundleDelegate : public ui::ResourceBundle::Delegate {
+ public:
+  CefResourceBundleDelegate(CefContentClient* content_client)
+      : content_client_(content_client) {}
+
+ private:
+  // ui::ResourceBundle::Delegate methods.
+  base::FilePath GetPathForResourcePack(const base::FilePath& pack_path,
+                                        ui::ScaleFactor scale_factor) override;
+  base::FilePath GetPathForLocalePack(const base::FilePath& pack_path,
+                                      const std::string& locale) override;
+  gfx::Image GetImageNamed(int resource_id) override;
+  gfx::Image GetNativeImageNamed(int resource_id) override;
+  base::RefCountedStaticMemory* LoadDataResourceBytes(
+      int resource_id,
+      ui::ScaleFactor scale_factor) override;
+  bool GetRawDataResource(int resource_id,
+                          ui::ScaleFactor scale_factor,
+                          base::StringPiece* value) override;
+  bool GetLocalizedString(int message_id, base::string16* value) override;
+
+ private:
+  CefContentClient* content_client_;
+};
+
+#endif  // CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_DELEGATE_H_
diff --git a/src/libcef/common/resource_bundle_impl.cc b/src/libcef/common/resource_bundle_impl.cc
new file mode 100644
index 0000000..2f4d20c
--- /dev/null
+++ b/src/libcef/common/resource_bundle_impl.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/common/resource_bundle_impl.h"
+
+#include "ui/base/resource/resource_bundle.h"
+
+CefResourceBundleImpl::CefResourceBundleImpl() {}
+
+CefString CefResourceBundleImpl::GetLocalizedString(int string_id) {
+  if (!ui::ResourceBundle::HasSharedInstance())
+    return CefString();
+
+  return ui::ResourceBundle::GetSharedInstance().GetLocalizedString(string_id);
+}
+
+bool CefResourceBundleImpl::GetDataResource(int resource_id,
+                                            void*& data,
+                                            size_t& data_size) {
+  return GetDataResourceForScale(resource_id, SCALE_FACTOR_NONE, data,
+                                 data_size);
+}
+
+bool CefResourceBundleImpl::GetDataResourceForScale(int resource_id,
+                                                    ScaleFactor scale_factor,
+                                                    void*& data,
+                                                    size_t& data_size) {
+  if (!ui::ResourceBundle::HasSharedInstance())
+    return false;
+
+  const base::StringPiece& result =
+      ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
+          resource_id, static_cast<ui::ScaleFactor>(scale_factor));
+  if (result.empty())
+    return false;
+
+  data = const_cast<char*>(result.data());
+  data_size = result.size();
+  return true;
+}
+
+// static
+CefRefPtr<CefResourceBundle> CefResourceBundle::GetGlobal() {
+  return new CefResourceBundleImpl();
+}
diff --git a/src/libcef/common/resource_bundle_impl.h b/src/libcef/common/resource_bundle_impl.h
new file mode 100644
index 0000000..cc02682
--- /dev/null
+++ b/src/libcef/common/resource_bundle_impl.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_IMPL_H_
+#define CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_IMPL_H_
+#pragma once
+
+#include "include/cef_resource_bundle.h"
+
+class CefResourceBundleImpl : public CefResourceBundle {
+ public:
+  CefResourceBundleImpl();
+
+  // CefResourceBundle methods.
+  CefString GetLocalizedString(int string_id) override;
+  bool GetDataResource(int resource_id,
+                       void*& data,
+                       size_t& data_size) override;
+  bool GetDataResourceForScale(int resource_id,
+                               ScaleFactor scale_factor,
+                               void*& data,
+                               size_t& data_size) override;
+
+ private:
+  IMPLEMENT_REFCOUNTING(CefResourceBundleImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefResourceBundleImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_IMPL_H_
diff --git a/src/libcef/common/response_impl.cc b/src/libcef/common/response_impl.cc
new file mode 100644
index 0000000..c55a173
--- /dev/null
+++ b/src/libcef/common/response_impl.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/response_impl.h"
+
+#include <string>
+
+#include "libcef/common/net/http_header_utils.h"
+#include "libcef/common/net_service/net_service_util.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "third_party/blink/public/platform/web_http_header_visitor.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+
+#define CHECK_READONLY_RETURN_VOID()       \
+  if (read_only_) {                        \
+    NOTREACHED() << "object is read only"; \
+    return;                                \
+  }
+
+// CefResponse ----------------------------------------------------------------
+
+// static
+CefRefPtr<CefResponse> CefResponse::Create() {
+  CefRefPtr<CefResponse> response(new CefResponseImpl());
+  return response;
+}
+
+// CefResponseImpl ------------------------------------------------------------
+
+CefResponseImpl::CefResponseImpl()
+    : error_code_(ERR_NONE), status_code_(0), read_only_(false) {}
+
+bool CefResponseImpl::IsReadOnly() {
+  base::AutoLock lock_scope(lock_);
+  return read_only_;
+}
+
+cef_errorcode_t CefResponseImpl::GetError() {
+  base::AutoLock lock_scope(lock_);
+  return error_code_;
+}
+
+void CefResponseImpl::SetError(cef_errorcode_t error) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  error_code_ = error;
+}
+
+int CefResponseImpl::GetStatus() {
+  base::AutoLock lock_scope(lock_);
+  return status_code_;
+}
+
+void CefResponseImpl::SetStatus(int status) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  status_code_ = status;
+}
+
+CefString CefResponseImpl::GetStatusText() {
+  base::AutoLock lock_scope(lock_);
+  return status_text_;
+}
+
+void CefResponseImpl::SetStatusText(const CefString& statusText) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  status_text_ = statusText;
+}
+
+CefString CefResponseImpl::GetMimeType() {
+  base::AutoLock lock_scope(lock_);
+  return mime_type_;
+}
+
+void CefResponseImpl::SetMimeType(const CefString& mimeType) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  mime_type_ = mimeType;
+}
+
+CefString CefResponseImpl::GetCharset() {
+  base::AutoLock lock_scope(lock_);
+  return charset_;
+}
+
+void CefResponseImpl::SetCharset(const CefString& charset) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  charset_ = charset;
+}
+
+CefString CefResponseImpl::GetHeaderByName(const CefString& name) {
+  base::AutoLock lock_scope(lock_);
+
+  std::string nameLower = name;
+  HttpHeaderUtils::MakeASCIILower(&nameLower);
+
+  auto it = HttpHeaderUtils::FindHeaderInMap(nameLower, header_map_);
+  if (it != header_map_.end())
+    return it->second;
+
+  return CefString();
+}
+
+void CefResponseImpl::SetHeaderByName(const CefString& name,
+                                      const CefString& value,
+                                      bool overwrite) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  std::string nameLower = name;
+  HttpHeaderUtils::MakeASCIILower(&nameLower);
+
+  // There may be multiple values, so remove any first.
+  for (auto it = header_map_.begin(); it != header_map_.end();) {
+    if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameLower)) {
+      if (!overwrite)
+        return;
+      it = header_map_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+
+  header_map_.insert(std::make_pair(name, value));
+}
+
+CefString CefResponseImpl::GetURL() {
+  base::AutoLock lock_scope(lock_);
+  return url_;
+}
+
+void CefResponseImpl::SetURL(const CefString& url) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  url_ = url;
+}
+
+void CefResponseImpl::GetHeaderMap(HeaderMap& map) {
+  base::AutoLock lock_scope(lock_);
+  map = header_map_;
+}
+
+void CefResponseImpl::SetHeaderMap(const HeaderMap& headerMap) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+  header_map_ = headerMap;
+}
+
+scoped_refptr<net::HttpResponseHeaders> CefResponseImpl::GetResponseHeaders() {
+  base::AutoLock lock_scope(lock_);
+
+  std::string mime_type = mime_type_;
+  if (mime_type.empty())
+    mime_type = "text/html";
+
+  std::multimap<std::string, std::string> extra_headers;
+  for (const auto& pair : header_map_)
+    extra_headers.insert(std::make_pair(pair.first, pair.second));
+
+  return net_service::MakeResponseHeaders(
+      status_code_, status_text_, mime_type, charset_, -1, extra_headers,
+      true /* allow_existing_header_override */);
+}
+
+void CefResponseImpl::SetResponseHeaders(
+    const net::HttpResponseHeaders& headers) {
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  header_map_.clear();
+
+  size_t iter = 0;
+  std::string name, value;
+  while (headers.EnumerateHeaderLines(&iter, &name, &value))
+    header_map_.insert(std::make_pair(name, value));
+
+  status_code_ = headers.response_code();
+  status_text_ = headers.GetStatusText();
+
+  if (headers.IsRedirect(nullptr)) {
+    // Don't report Content-Type header values for redirects.
+    mime_type_.clear();
+    charset_.clear();
+  } else {
+    std::string mime_type, charset;
+    headers.GetMimeTypeAndCharset(&mime_type, &charset);
+    mime_type_ = mime_type;
+    charset_ = charset;
+  }
+}
+
+void CefResponseImpl::Set(const blink::WebURLResponse& response) {
+  DCHECK(!response.IsNull());
+
+  base::AutoLock lock_scope(lock_);
+  CHECK_READONLY_RETURN_VOID();
+
+  blink::WebString str;
+  status_code_ = response.HttpStatusCode();
+  str = response.HttpStatusText();
+  status_text_ = str.Utf16();
+  str = response.MimeType();
+  mime_type_ = str.Utf16();
+  str = response.CurrentRequestUrl().GetString();
+  url_ = str.Utf16();
+
+  class HeaderVisitor : public blink::WebHTTPHeaderVisitor {
+   public:
+    explicit HeaderVisitor(HeaderMap* map) : map_(map) {}
+
+    void VisitHeader(const blink::WebString& name,
+                     const blink::WebString& value) override {
+      map_->insert(std::make_pair(name.Utf16(), value.Utf16()));
+    }
+
+   private:
+    HeaderMap* map_;
+  };
+
+  HeaderVisitor visitor(&header_map_);
+  response.VisitHttpHeaderFields(&visitor);
+}
+
+void CefResponseImpl::SetReadOnly(bool read_only) {
+  base::AutoLock lock_scope(lock_);
+  read_only_ = read_only;
+}
diff --git a/src/libcef/common/response_impl.h b/src/libcef/common/response_impl.h
new file mode 100644
index 0000000..5dab451
--- /dev/null
+++ b/src/libcef/common/response_impl.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_RESPONSE_IMPL_H_
+#define CEF_LIBCEF_COMMON_RESPONSE_IMPL_H_
+#pragma once
+
+#include "include/cef_response.h"
+
+#include "base/synchronization/lock.h"
+
+namespace net {
+class HttpResponseHeaders;
+}  // namespace net
+
+namespace blink {
+class WebURLResponse;
+}
+
+// Implementation of CefResponse.
+class CefResponseImpl : public CefResponse {
+ public:
+  CefResponseImpl();
+
+  // CefResponse methods.
+  bool IsReadOnly() override;
+  cef_errorcode_t GetError() override;
+  void SetError(cef_errorcode_t error) override;
+  int GetStatus() override;
+  void SetStatus(int status) override;
+  CefString GetStatusText() override;
+  void SetStatusText(const CefString& statusText) override;
+  CefString GetMimeType() override;
+  void SetMimeType(const CefString& mimeType) override;
+  CefString GetCharset() override;
+  void SetCharset(const CefString& charset) override;
+  CefString GetHeaderByName(const CefString& name) override;
+  void SetHeaderByName(const CefString& name,
+                       const CefString& value,
+                       bool overwrite) override;
+  void GetHeaderMap(HeaderMap& headerMap) override;
+  void SetHeaderMap(const HeaderMap& headerMap) override;
+  CefString GetURL() override;
+  void SetURL(const CefString& url) override;
+
+  scoped_refptr<net::HttpResponseHeaders> GetResponseHeaders();
+  void SetResponseHeaders(const net::HttpResponseHeaders& headers);
+
+  void Set(const blink::WebURLResponse& response);
+
+  void SetReadOnly(bool read_only);
+
+ protected:
+  cef_errorcode_t error_code_;
+  int status_code_;
+  CefString status_text_;
+  CefString mime_type_;
+  CefString charset_;
+  CefString url_;
+  HeaderMap header_map_;
+  bool read_only_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefResponseImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_RESPONSE_IMPL_H_
diff --git a/src/libcef/common/response_manager.cc b/src/libcef/common/response_manager.cc
new file mode 100644
index 0000000..2e97c4a
--- /dev/null
+++ b/src/libcef/common/response_manager.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/common/response_manager.h"
+#include "libcef/common/cef_messages.h"
+
+#include "base/logging.h"
+
+CefResponseManager::CefResponseManager() : next_request_id_(0) {}
+
+int CefResponseManager::GetNextRequestId() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return ++next_request_id_;
+}
+
+int CefResponseManager::RegisterHandler(CefRefPtr<Handler> handler) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  int request_id = GetNextRequestId();
+  TRACE_EVENT_ASYNC_BEGIN1("cef", "CefResponseManager::Handler", request_id,
+                           "request_id", request_id);
+  handlers_.insert(std::make_pair(request_id, handler));
+  return request_id;
+}
+
+bool CefResponseManager::RunHandler(const Cef_Response_Params& params) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_GT(params.request_id, 0);
+  HandlerMap::iterator it = handlers_.find(params.request_id);
+  if (it != handlers_.end()) {
+    TRACE_EVENT0("cef", "CefResponseManager::RunHandler");
+    it->second->OnResponse(params);
+    handlers_.erase(it);
+    TRACE_EVENT_ASYNC_END1("cef", "CefResponseManager::Handler",
+                           params.request_id, "success", 1);
+    return true;
+  }
+  TRACE_EVENT_ASYNC_END1("cef", "CefResponseManager::Handler",
+                         params.request_id, "success", 0);
+  return false;
+}
+
+void CefResponseManager::RegisterAckHandler(int request_id,
+                                            CefRefPtr<AckHandler> handler) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ack_handlers_.insert(std::make_pair(request_id, handler));
+}
+
+bool CefResponseManager::RunAckHandler(int request_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_GT(request_id, 0);
+  AckHandlerMap::iterator it = ack_handlers_.find(request_id);
+  if (it != ack_handlers_.end()) {
+    it->second->OnResponseAck();
+    ack_handlers_.erase(it);
+    return true;
+  }
+  return false;
+}
diff --git a/src/libcef/common/response_manager.h b/src/libcef/common/response_manager.h
new file mode 100644
index 0000000..3e47c76
--- /dev/null
+++ b/src/libcef/common/response_manager.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_RESPONSE_MANAGER_H_
+#define CEF_LIBCEF_COMMON_RESPONSE_MANAGER_H_
+#pragma once
+
+#include <map>
+
+#include "include/cef_base.h"
+
+#include "base/sequence_checker.h"
+
+struct Cef_Response_Params;
+
+// This class is not thread-safe.
+class CefResponseManager {
+ public:
+  // Used for handling response messages.
+  class Handler : public virtual CefBaseRefCounted {
+   public:
+    virtual void OnResponse(const Cef_Response_Params& params) = 0;
+  };
+
+  // Used for handling response ack messages.
+  class AckHandler : public virtual CefBaseRefCounted {
+   public:
+    virtual void OnResponseAck() = 0;
+  };
+
+  CefResponseManager();
+
+  // Returns the next unique request id.
+  int GetNextRequestId();
+
+  // Register a response handler and return the unique request id.
+  int RegisterHandler(CefRefPtr<Handler> handler);
+
+  // Run the response handler for the specified request id. Returns true if a
+  // handler was run.
+  bool RunHandler(const Cef_Response_Params& params);
+
+  // Register a response ack handler for the specified request id.
+  void RegisterAckHandler(int request_id, CefRefPtr<AckHandler> handler);
+
+  // Run the response ack handler for the specified request id. Returns true if
+  // a handler was run.
+  bool RunAckHandler(int request_id);
+
+ private:
+  // Used for generating unique request ids.
+  int next_request_id_;
+
+  // Map of unique request ids to Handler references.
+  typedef std::map<int, CefRefPtr<Handler>> HandlerMap;
+  HandlerMap handlers_;
+
+  // Map of unique request ids to AckHandler references.
+  typedef std::map<int, CefRefPtr<AckHandler>> AckHandlerMap;
+  AckHandlerMap ack_handlers_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+#endif  // CEF_LIBCEF_COMMON_RESPONSE_MANAGER_H_
diff --git a/src/libcef/common/scheme_registrar_impl.cc b/src/libcef/common/scheme_registrar_impl.cc
new file mode 100644
index 0000000..870d797
--- /dev/null
+++ b/src/libcef/common/scheme_registrar_impl.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/common/scheme_registrar_impl.h"
+
+#include <string>
+
+#include "libcef/common/content_client.h"
+#include "libcef/common/net/scheme_registration.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace {
+
+void AppendArray(const std::vector<std::string>& source,
+                 std::vector<std::string>* target) {
+  if (source.empty())
+    return;
+  target->insert(target->end(), source.begin(), source.end());
+}
+}  // namespace
+
+CefSchemeRegistrarImpl::CefSchemeRegistrarImpl() {}
+
+bool CefSchemeRegistrarImpl::AddCustomScheme(const CefString& scheme_name,
+                                             int options) {
+  const std::string& scheme = base::ToLowerASCII(scheme_name.ToString());
+  if (scheme::IsInternalHandledScheme(scheme) ||
+      registered_schemes_.find(scheme) != registered_schemes_.end()) {
+    return false;
+  }
+
+  registered_schemes_.insert(scheme);
+
+  const bool is_standard = options & CEF_SCHEME_OPTION_STANDARD;
+  const bool is_local = options & CEF_SCHEME_OPTION_LOCAL;
+  const bool is_display_isolated = options & CEF_SCHEME_OPTION_DISPLAY_ISOLATED;
+  const bool is_secure = options & CEF_SCHEME_OPTION_SECURE;
+  const bool is_cors_enabled = options & CEF_SCHEME_OPTION_CORS_ENABLED;
+  const bool is_csp_bypassing = options & CEF_SCHEME_OPTION_CSP_BYPASSING;
+  const bool is_fetch_enabled = options & CEF_SCHEME_OPTION_FETCH_ENABLED;
+
+  // The |is_display_isolated| value is excluded here because it's registered
+  // with Blink only.
+  if (is_standard)
+    schemes_.standard_schemes.push_back(scheme);
+  if (is_local)
+    schemes_.local_schemes.push_back(scheme);
+  if (is_secure)
+    schemes_.secure_schemes.push_back(scheme);
+  if (is_cors_enabled)
+    schemes_.cors_enabled_schemes.push_back(scheme);
+  if (is_csp_bypassing)
+    schemes_.csp_bypassing_schemes.push_back(scheme);
+
+  CefContentClient::SchemeInfo scheme_info = {
+      scheme,    is_standard,     is_local,         is_display_isolated,
+      is_secure, is_cors_enabled, is_csp_bypassing, is_fetch_enabled};
+  CefContentClient::Get()->AddCustomScheme(scheme_info);
+
+  return true;
+}
+
+void CefSchemeRegistrarImpl::GetSchemes(
+    content::ContentClient::Schemes* schemes) {
+  AppendArray(schemes_.standard_schemes, &schemes->standard_schemes);
+  AppendArray(schemes_.local_schemes, &schemes->local_schemes);
+  AppendArray(schemes_.secure_schemes, &schemes->secure_schemes);
+  AppendArray(schemes_.cors_enabled_schemes, &schemes->cors_enabled_schemes);
+  AppendArray(schemes_.csp_bypassing_schemes, &schemes->csp_bypassing_schemes);
+}
diff --git a/src/libcef/common/scheme_registrar_impl.h b/src/libcef/common/scheme_registrar_impl.h
new file mode 100644
index 0000000..ed71266
--- /dev/null
+++ b/src/libcef/common/scheme_registrar_impl.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_SCHEME_REGISTRAR_IMPL_H_
+#define CEF_LIBCEF_COMMON_SCHEME_REGISTRAR_IMPL_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "include/cef_scheme.h"
+
+#include "content/public/common/content_client.h"
+
+class CefSchemeRegistrarImpl : public CefSchemeRegistrar {
+ public:
+  CefSchemeRegistrarImpl();
+
+  // CefSchemeRegistrar methods.
+  bool AddCustomScheme(const CefString& scheme_name, int options) override;
+
+  void GetSchemes(content::ContentClient::Schemes* schemes);
+
+ private:
+  content::ContentClient::Schemes schemes_;
+  std::set<std::string> registered_schemes_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefSchemeRegistrarImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_SCHEME_REGISTRAR_IMPL_H_
diff --git a/src/libcef/common/service_manifests/cef_content_browser_overlay_manifest.cc b/src/libcef/common/service_manifests/cef_content_browser_overlay_manifest.cc
new file mode 100644
index 0000000..c254c76
--- /dev/null
+++ b/src/libcef/common/service_manifests/cef_content_browser_overlay_manifest.cc
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/common/service_manifests/cef_content_browser_overlay_manifest.h"
+
+#include "base/command_line.h"
+#include "base/no_destructor.h"
+#include "build/build_config.h"
+#include "components/metrics/public/mojom/call_stack_profile_collector.mojom.h"
+#include "extensions/buildflags/buildflags.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+
+#if defined(OS_WIN)
+#include "chrome/common/conflicts/module_event_sink_win.mojom.h"
+#endif
+
+const service_manager::Manifest& GetCefContentBrowserOverlayManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest {
+    service_manager::ManifestBuilder()
+        .ExposeCapability("gpu",
+                          service_manager::Manifest::InterfaceList<
+                              metrics::mojom::CallStackProfileCollector>())
+        .ExposeCapability("renderer",
+                          service_manager::Manifest::InterfaceList<
+#if defined(OS_WIN)
+                              mojom::ModuleEventSink,
+#endif
+                              metrics::mojom::CallStackProfileCollector>())
+        .RequireCapability("chrome_printing", "converter")
+        .Build()
+  };
+  return *manifest;
+}
diff --git a/src/libcef/common/service_manifests/cef_content_browser_overlay_manifest.h b/src/libcef/common/service_manifests/cef_content_browser_overlay_manifest.h
new file mode 100644
index 0000000..844a5e8
--- /dev/null
+++ b/src/libcef/common/service_manifests/cef_content_browser_overlay_manifest.h
@@ -0,0 +1,16 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_SERVICE_MANIFESTS_CEF_CONTENT_BROWSER_OVERLAY_MANIFEST_H_
+#define CEF_LIBCEF_COMMON_SERVICE_MANIFESTS_CEF_CONTENT_BROWSER_OVERLAY_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+// Returns the Manifest CEF amends to Content's content_browser service
+// manifest. This allows CEF to extend the capabilities exposed and/or
+// required by content_browser service instances, as well as declaring any
+// additional in- and out-of-process per-profile packaged services.
+const service_manager::Manifest& GetCefContentBrowserOverlayManifest();
+
+#endif  // CEF_LIBCEF_COMMON_SERVICE_MANIFESTS_CEF_CONTENT_BROWSER_OVERLAY_MANIFEST_H_
diff --git a/src/libcef/common/string_list_impl.cc b/src/libcef/common/string_list_impl.cc
new file mode 100644
index 0000000..ce2517f
--- /dev/null
+++ b/src/libcef/common/string_list_impl.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <vector>
+
+#include "include/internal/cef_string_list.h"
+
+#include "base/logging.h"
+
+typedef std::vector<CefString> StringList;
+
+CEF_EXPORT cef_string_list_t cef_string_list_alloc() {
+  return new StringList;
+}
+
+CEF_EXPORT size_t cef_string_list_size(cef_string_list_t list) {
+  DCHECK(list);
+  StringList* impl = reinterpret_cast<StringList*>(list);
+  return impl->size();
+}
+
+CEF_EXPORT int cef_string_list_value(cef_string_list_t list,
+                                     size_t index,
+                                     cef_string_t* value) {
+  DCHECK(list);
+  DCHECK(value);
+  StringList* impl = reinterpret_cast<StringList*>(list);
+  DCHECK_LT(index, impl->size());
+  if (index >= impl->size())
+    return false;
+  const CefString& str = (*impl)[index];
+  return cef_string_copy(str.c_str(), str.length(), value);
+}
+
+CEF_EXPORT void cef_string_list_append(cef_string_list_t list,
+                                       const cef_string_t* value) {
+  DCHECK(list);
+  StringList* impl = reinterpret_cast<StringList*>(list);
+  impl->push_back(CefString(value));
+}
+
+CEF_EXPORT void cef_string_list_clear(cef_string_list_t list) {
+  DCHECK(list);
+  StringList* impl = reinterpret_cast<StringList*>(list);
+  impl->clear();
+}
+
+CEF_EXPORT void cef_string_list_free(cef_string_list_t list) {
+  DCHECK(list);
+  StringList* impl = reinterpret_cast<StringList*>(list);
+  delete impl;
+}
+
+CEF_EXPORT cef_string_list_t cef_string_list_copy(cef_string_list_t list) {
+  DCHECK(list);
+  StringList* impl = reinterpret_cast<StringList*>(list);
+  return new StringList(*impl);
+}
diff --git a/src/libcef/common/string_map_impl.cc b/src/libcef/common/string_map_impl.cc
new file mode 100644
index 0000000..16e682f
--- /dev/null
+++ b/src/libcef/common/string_map_impl.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <map>
+
+#include "include/internal/cef_string_map.h"
+
+#include "base/logging.h"
+
+typedef std::map<CefString, CefString> StringMap;
+
+CEF_EXPORT cef_string_map_t cef_string_map_alloc() {
+  return new StringMap;
+}
+
+CEF_EXPORT size_t cef_string_map_size(cef_string_map_t map) {
+  DCHECK(map);
+  StringMap* impl = reinterpret_cast<StringMap*>(map);
+  return impl->size();
+}
+
+CEF_EXPORT int cef_string_map_find(cef_string_map_t map,
+                                   const cef_string_t* key,
+                                   cef_string_t* value) {
+  DCHECK(map);
+  DCHECK(value);
+  StringMap* impl = reinterpret_cast<StringMap*>(map);
+  StringMap::const_iterator it = impl->find(CefString(key));
+  if (it == impl->end())
+    return 0;
+
+  const CefString& val = it->second;
+  return cef_string_set(val.c_str(), val.length(), value, true);
+}
+
+CEF_EXPORT int cef_string_map_key(cef_string_map_t map,
+                                  size_t index,
+                                  cef_string_t* key) {
+  DCHECK(map);
+  DCHECK(key);
+  StringMap* impl = reinterpret_cast<StringMap*>(map);
+  DCHECK_LT(index, impl->size());
+  if (index >= impl->size())
+    return 0;
+
+  StringMap::const_iterator it = impl->begin();
+  for (size_t ct = 0; it != impl->end(); ++it, ct++) {
+    if (ct == index)
+      return cef_string_set(it->first.c_str(), it->first.length(), key, true);
+  }
+  return 0;
+}
+
+CEF_EXPORT int cef_string_map_value(cef_string_map_t map,
+                                    size_t index,
+                                    cef_string_t* value) {
+  DCHECK(map);
+  DCHECK(value);
+  StringMap* impl = reinterpret_cast<StringMap*>(map);
+  DCHECK_LT(index, impl->size());
+  if (index >= impl->size())
+    return 0;
+
+  StringMap::const_iterator it = impl->begin();
+  for (size_t ct = 0; it != impl->end(); ++it, ct++) {
+    if (ct == index) {
+      return cef_string_set(it->second.c_str(), it->second.length(), value,
+                            true);
+    }
+  }
+  return 0;
+}
+
+CEF_EXPORT int cef_string_map_append(cef_string_map_t map,
+                                     const cef_string_t* key,
+                                     const cef_string_t* value) {
+  DCHECK(map);
+  StringMap* impl = reinterpret_cast<StringMap*>(map);
+  impl->insert(std::make_pair(CefString(key), CefString(value)));
+  return 1;
+}
+
+CEF_EXPORT void cef_string_map_clear(cef_string_map_t map) {
+  DCHECK(map);
+  StringMap* impl = reinterpret_cast<StringMap*>(map);
+  impl->clear();
+}
+
+CEF_EXPORT void cef_string_map_free(cef_string_map_t map) {
+  DCHECK(map);
+  StringMap* impl = reinterpret_cast<StringMap*>(map);
+  delete impl;
+}
diff --git a/src/libcef/common/string_multimap_impl.cc b/src/libcef/common/string_multimap_impl.cc
new file mode 100644
index 0000000..e2fe6e0
--- /dev/null
+++ b/src/libcef/common/string_multimap_impl.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <map>
+
+#include "include/internal/cef_string_multimap.h"
+
+#include "base/logging.h"
+
+typedef std::multimap<CefString, CefString> StringMultimap;
+
+CEF_EXPORT cef_string_multimap_t cef_string_multimap_alloc() {
+  return new StringMultimap;
+}
+
+CEF_EXPORT size_t cef_string_multimap_size(cef_string_multimap_t map) {
+  DCHECK(map);
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  return impl->size();
+}
+
+CEF_EXPORT size_t cef_string_multimap_find_count(cef_string_multimap_t map,
+                                                 const cef_string_t* key) {
+  DCHECK(map);
+  DCHECK(key);
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  return impl->count(CefString(key));
+}
+
+CEF_EXPORT int cef_string_multimap_enumerate(cef_string_multimap_t map,
+                                             const cef_string_t* key,
+                                             size_t value_index,
+                                             cef_string_t* value) {
+  DCHECK(map);
+  DCHECK(key);
+  DCHECK(value);
+
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  CefString key_str(key);
+
+  DCHECK_LT(value_index, impl->count(key_str));
+  if (value_index >= impl->count(key_str))
+    return 0;
+
+  std::pair<StringMultimap::iterator, StringMultimap::iterator> range_it =
+      impl->equal_range(key_str);
+
+  size_t count = value_index;
+  while (count-- && range_it.first != range_it.second)
+    range_it.first++;
+
+  if (range_it.first == range_it.second)
+    return 0;
+
+  const CefString& val = range_it.first->second;
+  return cef_string_set(val.c_str(), val.length(), value, true);
+}
+
+CEF_EXPORT int cef_string_multimap_key(cef_string_multimap_t map,
+                                       size_t index,
+                                       cef_string_t* key) {
+  DCHECK(map);
+  DCHECK(key);
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  DCHECK_LT(index, impl->size());
+  if (index >= impl->size())
+    return 0;
+
+  StringMultimap::const_iterator it = impl->begin();
+  for (size_t ct = 0; it != impl->end(); ++it, ct++) {
+    if (ct == index)
+      return cef_string_set(it->first.c_str(), it->first.length(), key, true);
+  }
+  return 0;
+}
+
+CEF_EXPORT int cef_string_multimap_value(cef_string_multimap_t map,
+                                         size_t index,
+                                         cef_string_t* value) {
+  DCHECK(map);
+  DCHECK(value);
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  DCHECK_LT(index, impl->size());
+  if (index >= impl->size())
+    return 0;
+
+  StringMultimap::const_iterator it = impl->begin();
+  for (size_t ct = 0; it != impl->end(); ++it, ct++) {
+    if (ct == index) {
+      return cef_string_set(it->second.c_str(), it->second.length(), value,
+                            true);
+    }
+  }
+  return 0;
+}
+
+CEF_EXPORT int cef_string_multimap_append(cef_string_multimap_t map,
+                                          const cef_string_t* key,
+                                          const cef_string_t* value) {
+  DCHECK(map);
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  impl->insert(std::make_pair(CefString(key), CefString(value)));
+  return 1;
+}
+
+CEF_EXPORT void cef_string_multimap_clear(cef_string_multimap_t map) {
+  DCHECK(map);
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  impl->clear();
+}
+
+CEF_EXPORT void cef_string_multimap_free(cef_string_multimap_t map) {
+  DCHECK(map);
+  StringMultimap* impl = reinterpret_cast<StringMultimap*>(map);
+  delete impl;
+}
diff --git a/src/libcef/common/string_types_impl.cc b/src/libcef/common/string_types_impl.cc
new file mode 100644
index 0000000..b9b18f2
--- /dev/null
+++ b/src/libcef/common/string_types_impl.cc
@@ -0,0 +1,308 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+
+#include "include/internal/cef_string_types.h"
+
+#include "base/i18n/case_conversion.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace {
+
+void string_wide_dtor(wchar_t* str) {
+  delete[] str;
+}
+
+void string_utf8_dtor(char* str) {
+  delete[] str;
+}
+
+void string_utf16_dtor(char16* str) {
+  delete[] str;
+}
+
+// Originally from base/strings/utf_string_conversions.cc
+std::wstring ASCIIToWide(const base::StringPiece& ascii) {
+  DCHECK(base::IsStringASCII(ascii)) << ascii;
+  return std::wstring(ascii.begin(), ascii.end());
+}
+
+}  // namespace
+
+CEF_EXPORT int cef_string_wide_set(const wchar_t* src,
+                                   size_t src_len,
+                                   cef_string_wide_t* output,
+                                   int copy) {
+  cef_string_wide_clear(output);
+
+  if (copy) {
+    if (src && src_len > 0) {
+      output->str = new wchar_t[src_len + 1];
+      if (!output->str)
+        return 0;
+
+      memcpy(output->str, src, src_len * sizeof(wchar_t));
+      output->str[src_len] = 0;
+      output->length = src_len;
+      output->dtor = string_wide_dtor;
+    }
+  } else {
+    output->str = const_cast<wchar_t*>(src);
+    output->length = src_len;
+    output->dtor = nullptr;
+  }
+  return 1;
+}
+
+CEF_EXPORT int cef_string_utf8_set(const char* src,
+                                   size_t src_len,
+                                   cef_string_utf8_t* output,
+                                   int copy) {
+  cef_string_utf8_clear(output);
+  if (copy) {
+    if (src && src_len > 0) {
+      output->str = new char[src_len + 1];
+      if (!output->str)
+        return 0;
+
+      memcpy(output->str, src, src_len * sizeof(char));
+      output->str[src_len] = 0;
+      output->length = src_len;
+      output->dtor = string_utf8_dtor;
+    }
+  } else {
+    output->str = const_cast<char*>(src);
+    output->length = src_len;
+    output->dtor = nullptr;
+  }
+  return 1;
+}
+
+CEF_EXPORT int cef_string_utf16_set(const char16* src,
+                                    size_t src_len,
+                                    cef_string_utf16_t* output,
+                                    int copy) {
+  cef_string_utf16_clear(output);
+
+  if (copy) {
+    if (src && src_len > 0) {
+      output->str = new char16[src_len + 1];
+      if (!output->str)
+        return 0;
+
+      memcpy(output->str, src, src_len * sizeof(char16));
+      output->str[src_len] = 0;
+      output->length = src_len;
+      output->dtor = string_utf16_dtor;
+    }
+  } else {
+    output->str = const_cast<char16*>(src);
+    output->length = src_len;
+    output->dtor = nullptr;
+  }
+  return 1;
+}
+
+CEF_EXPORT void cef_string_wide_clear(cef_string_wide_t* str) {
+  DCHECK(str != nullptr);
+  if (str->dtor && str->str)
+    str->dtor(str->str);
+
+  str->str = nullptr;
+  str->length = 0;
+  str->dtor = nullptr;
+}
+
+CEF_EXPORT void cef_string_utf8_clear(cef_string_utf8_t* str) {
+  DCHECK(str != nullptr);
+  if (str->dtor && str->str)
+    str->dtor(str->str);
+
+  str->str = nullptr;
+  str->length = 0;
+  str->dtor = nullptr;
+}
+
+CEF_EXPORT void cef_string_utf16_clear(cef_string_utf16_t* str) {
+  DCHECK(str != nullptr);
+  if (str->dtor && str->str)
+    str->dtor(str->str);
+
+  str->str = nullptr;
+  str->length = 0;
+  str->dtor = nullptr;
+}
+
+CEF_EXPORT int cef_string_wide_cmp(const cef_string_wide_t* str1,
+                                   const cef_string_wide_t* str2) {
+  if (str1->length == 0 && str2->length == 0)
+    return 0;
+  int r = wcsncmp(str1->str, str2->str, std::min(str1->length, str2->length));
+  if (r == 0) {
+    if (str1->length > str2->length)
+      return 1;
+    else if (str1->length < str2->length)
+      return -1;
+  }
+  return r;
+}
+
+CEF_EXPORT int cef_string_utf8_cmp(const cef_string_utf8_t* str1,
+                                   const cef_string_utf8_t* str2) {
+  if (str1->length == 0 && str2->length == 0)
+    return 0;
+  int r = strncmp(str1->str, str2->str, std::min(str1->length, str2->length));
+  if (r == 0) {
+    if (str1->length > str2->length)
+      return 1;
+    else if (str1->length < str2->length)
+      return -1;
+  }
+  return r;
+}
+
+CEF_EXPORT int cef_string_utf16_cmp(const cef_string_utf16_t* str1,
+                                    const cef_string_utf16_t* str2) {
+  if (str1->length == 0 && str2->length == 0)
+    return 0;
+#if defined(WCHAR_T_IS_UTF32)
+  int r = base::c16memcmp(str1->str, str2->str,
+                          std::min(str1->length, str2->length));
+#else
+  int r = wcsncmp(str1->str, str2->str, std::min(str1->length, str2->length));
+#endif
+  if (r == 0) {
+    if (str1->length > str2->length)
+      return 1;
+    else if (str1->length < str2->length)
+      return -1;
+  }
+  return r;
+}
+
+CEF_EXPORT int cef_string_wide_to_utf8(const wchar_t* src,
+                                       size_t src_len,
+                                       cef_string_utf8_t* output) {
+  std::string str;
+  bool ret = base::WideToUTF8(src, src_len, &str);
+  if (!cef_string_utf8_set(str.c_str(), str.length(), output, true))
+    return false;
+  return ret;
+}
+
+CEF_EXPORT int cef_string_utf8_to_wide(const char* src,
+                                       size_t src_len,
+                                       cef_string_wide_t* output) {
+  std::wstring str;
+  bool ret = base::UTF8ToWide(src, src_len, &str);
+  if (!cef_string_wide_set(str.c_str(), str.length(), output, true))
+    return false;
+  return ret;
+}
+
+CEF_EXPORT int cef_string_wide_to_utf16(const wchar_t* src,
+                                        size_t src_len,
+                                        cef_string_utf16_t* output) {
+  base::string16 str;
+  bool ret = base::WideToUTF16(src, src_len, &str);
+  if (!cef_string_utf16_set(str.c_str(), str.length(), output, true))
+    return false;
+  return ret;
+}
+
+CEF_EXPORT int cef_string_utf16_to_wide(const char16* src,
+                                        size_t src_len,
+                                        cef_string_wide_t* output) {
+  std::wstring str;
+  bool ret = base::UTF16ToWide(src, src_len, &str);
+  if (!cef_string_wide_set(str.c_str(), str.length(), output, true))
+    return false;
+  return ret;
+}
+
+CEF_EXPORT int cef_string_utf8_to_utf16(const char* src,
+                                        size_t src_len,
+                                        cef_string_utf16_t* output) {
+  base::string16 str;
+  bool ret = base::UTF8ToUTF16(src, src_len, &str);
+  if (!cef_string_utf16_set(str.c_str(), str.length(), output, true))
+    return false;
+  return ret;
+}
+
+CEF_EXPORT int cef_string_utf16_to_utf8(const char16* src,
+                                        size_t src_len,
+                                        cef_string_utf8_t* output) {
+  std::string str;
+  bool ret = base::UTF16ToUTF8(src, src_len, &str);
+  if (!cef_string_utf8_set(str.c_str(), str.length(), output, true))
+    return false;
+  return ret;
+}
+
+CEF_EXPORT int cef_string_ascii_to_wide(const char* src,
+                                        size_t src_len,
+                                        cef_string_wide_t* output) {
+  const std::wstring& str = ASCIIToWide(std::string(src, src_len));
+  return cef_string_wide_set(str.c_str(), str.length(), output, true);
+}
+
+CEF_EXPORT int cef_string_ascii_to_utf16(const char* src,
+                                         size_t src_len,
+                                         cef_string_utf16_t* output) {
+  const base::string16& str = base::ASCIIToUTF16(std::string(src, src_len));
+  return cef_string_utf16_set(str.c_str(), str.length(), output, true);
+}
+
+CEF_EXPORT cef_string_userfree_wide_t cef_string_userfree_wide_alloc() {
+  cef_string_wide_t* s = new cef_string_wide_t;
+  memset(s, 0, sizeof(cef_string_wide_t));
+  return s;
+}
+
+CEF_EXPORT cef_string_userfree_utf8_t cef_string_userfree_utf8_alloc() {
+  cef_string_utf8_t* s = new cef_string_utf8_t;
+  memset(s, 0, sizeof(cef_string_utf8_t));
+  return s;
+}
+
+CEF_EXPORT cef_string_userfree_utf16_t cef_string_userfree_utf16_alloc() {
+  cef_string_utf16_t* s = new cef_string_utf16_t;
+  memset(s, 0, sizeof(cef_string_utf16_t));
+  return s;
+}
+
+CEF_EXPORT void cef_string_userfree_wide_free(cef_string_userfree_wide_t str) {
+  cef_string_wide_clear(str);
+  delete str;
+}
+
+CEF_EXPORT void cef_string_userfree_utf8_free(cef_string_userfree_utf8_t str) {
+  cef_string_utf8_clear(str);
+  delete str;
+}
+
+CEF_EXPORT void cef_string_userfree_utf16_free(
+    cef_string_userfree_utf16_t str) {
+  cef_string_utf16_clear(str);
+  delete str;
+}
+
+CEF_EXPORT int cef_string_utf16_to_lower(const char16* src,
+                                         size_t src_len,
+                                         cef_string_utf16_t* output) {
+  const base::string16& str = base::i18n::ToLower(base::string16(src, src_len));
+  return cef_string_utf16_set(str.c_str(), str.length(), output, true);
+}
+
+CEF_EXPORT int cef_string_utf16_to_upper(const char16* src,
+                                         size_t src_len,
+                                         cef_string_utf16_t* output) {
+  const base::string16& str = base::i18n::ToUpper(base::string16(src, src_len));
+  return cef_string_utf16_set(str.c_str(), str.length(), output, true);
+}
diff --git a/src/libcef/common/task_impl.cc b/src/libcef/common/task_impl.cc
new file mode 100644
index 0000000..e03744c
--- /dev/null
+++ b/src/libcef/common/task_impl.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_task.h"
+#include "libcef/common/task_runner_impl.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+
+bool CefCurrentlyOn(CefThreadId threadId) {
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      CefTaskRunnerImpl::GetTaskRunner(threadId);
+  if (task_runner.get())
+    return task_runner->RunsTasksInCurrentSequence();
+  return false;
+}
+
+bool CefPostTask(CefThreadId threadId, CefRefPtr<CefTask> task) {
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      CefTaskRunnerImpl::GetTaskRunner(threadId);
+  if (task_runner.get()) {
+    return task_runner->PostTask(FROM_HERE,
+                                 base::Bind(&CefTask::Execute, task.get()));
+  }
+  return false;
+}
+
+bool CefPostDelayedTask(CefThreadId threadId,
+                        CefRefPtr<CefTask> task,
+                        int64 delay_ms) {
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      CefTaskRunnerImpl::GetTaskRunner(threadId);
+  if (task_runner.get()) {
+    return task_runner->PostDelayedTask(
+        FROM_HERE, base::Bind(&CefTask::Execute, task.get()),
+        base::TimeDelta::FromMilliseconds(delay_ms));
+  }
+  return false;
+}
diff --git a/src/libcef/common/task_runner_impl.cc b/src/libcef/common/task_runner_impl.cc
new file mode 100644
index 0000000..1487289
--- /dev/null
+++ b/src/libcef/common/task_runner_impl.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/task_runner_impl.h"
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/common/content_client.h"
+#include "libcef/renderer/content_renderer_client.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_launcher_utils.h"
+
+using content::BrowserThread;
+
+// CefTaskRunner
+
+// static
+CefRefPtr<CefTaskRunner> CefTaskRunner::GetForCurrentThread() {
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      CefTaskRunnerImpl::GetCurrentTaskRunner();
+  if (task_runner.get())
+    return new CefTaskRunnerImpl(task_runner);
+  return nullptr;
+}
+
+// static
+CefRefPtr<CefTaskRunner> CefTaskRunner::GetForThread(CefThreadId threadId) {
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      CefTaskRunnerImpl::GetTaskRunner(threadId);
+  if (task_runner.get())
+    return new CefTaskRunnerImpl(task_runner);
+
+  LOG(WARNING) << "Invalid thread id " << threadId;
+  return nullptr;
+}
+
+// CefTaskRunnerImpl
+
+CefTaskRunnerImpl::CefTaskRunnerImpl(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : task_runner_(task_runner) {
+  DCHECK(task_runner_.get());
+}
+
+// static
+scoped_refptr<base::SingleThreadTaskRunner> CefTaskRunnerImpl::GetTaskRunner(
+    CefThreadId threadId) {
+  // Render process.
+  if (threadId == TID_RENDERER) {
+    CefContentRendererClient* client = CefContentRendererClient::Get();
+    if (client)
+      return client->render_task_runner();
+    return nullptr;
+  }
+
+  // Browser process.
+  CefContentBrowserClient* client = CefContentBrowserClient::Get();
+  if (!client)
+    return nullptr;
+
+  int id = -1;
+  switch (threadId) {
+    case TID_UI:
+      id = BrowserThread::UI;
+      break;
+    case TID_FILE_BACKGROUND:
+      return client->background_task_runner();
+    case TID_FILE_USER_VISIBLE:
+      return client->user_visible_task_runner();
+    case TID_FILE_USER_BLOCKING:
+      return client->user_blocking_task_runner();
+    case TID_PROCESS_LAUNCHER:
+      return content::GetProcessLauncherTaskRunner();
+    case TID_IO:
+      id = BrowserThread::IO;
+      break;
+    default:
+      break;
+  };
+
+  if (id >= 0 &&
+      BrowserThread::IsThreadInitialized(static_cast<BrowserThread::ID>(id))) {
+    // Specify USER_BLOCKING so that BrowserTaskExecutor::GetTaskRunner always
+    // gives us the same TaskRunner object.
+    return base::CreateSingleThreadTaskRunner(
+        {static_cast<BrowserThread::ID>(id),
+         base::TaskPriority::USER_BLOCKING});
+  }
+
+  return nullptr;
+}
+
+// static
+scoped_refptr<base::SingleThreadTaskRunner>
+CefTaskRunnerImpl::GetCurrentTaskRunner() {
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner;
+
+  // For named browser process threads return the same TaskRunner as
+  // GetTaskRunner(). Otherwise BelongsToThread() will return incorrect results.
+  BrowserThread::ID current_id;
+  if (BrowserThread::GetCurrentThreadIdentifier(&current_id) &&
+      BrowserThread::IsThreadInitialized(current_id)) {
+    // Specify USER_BLOCKING so that BrowserTaskExecutor::GetTaskRunner always
+    // gives us the same TaskRunner object.
+    task_runner = base::CreateSingleThreadTaskRunner(
+        {current_id, base::TaskPriority::USER_BLOCKING});
+  }
+
+  if (!task_runner.get()) {
+    // Check for a MessageLoopProxy. This covers all of the named browser and
+    // render process threads, plus a few extra.
+    task_runner = base::ThreadTaskRunnerHandle::Get();
+  }
+
+  if (!task_runner.get()) {
+    // Check for a WebWorker thread.
+    CefContentRendererClient* client = CefContentRendererClient::Get();
+    if (client)
+      task_runner = client->GetCurrentTaskRunner();
+  }
+
+  return task_runner;
+}
+
+bool CefTaskRunnerImpl::IsSame(CefRefPtr<CefTaskRunner> that) {
+  CefTaskRunnerImpl* impl = static_cast<CefTaskRunnerImpl*>(that.get());
+  return (impl && task_runner_ == impl->task_runner_);
+}
+
+bool CefTaskRunnerImpl::BelongsToCurrentThread() {
+  return task_runner_->RunsTasksInCurrentSequence();
+}
+
+bool CefTaskRunnerImpl::BelongsToThread(CefThreadId threadId) {
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      GetTaskRunner(threadId);
+  return (task_runner_ == task_runner);
+}
+
+bool CefTaskRunnerImpl::PostTask(CefRefPtr<CefTask> task) {
+  return task_runner_->PostTask(FROM_HERE,
+                                base::Bind(&CefTask::Execute, task.get()));
+}
+
+bool CefTaskRunnerImpl::PostDelayedTask(CefRefPtr<CefTask> task,
+                                        int64 delay_ms) {
+  return task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&CefTask::Execute, task.get()),
+      base::TimeDelta::FromMilliseconds(delay_ms));
+}
diff --git a/src/libcef/common/task_runner_impl.h b/src/libcef/common/task_runner_impl.h
new file mode 100644
index 0000000..8991718
--- /dev/null
+++ b/src/libcef/common/task_runner_impl.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_TASK_RUNNER_IMPL_H_
+#define CEF_LIBCEF_COMMON_TASK_RUNNER_IMPL_H_
+#pragma once
+
+#include "include/cef_task.h"
+
+#include "base/single_thread_task_runner.h"
+
+class CefTaskRunnerImpl : public CefTaskRunner {
+ public:
+  explicit CefTaskRunnerImpl(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  // Returns the task runner associated with |threadId|.
+  static scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+      CefThreadId threadId);
+  // Returns the current task runner.
+  static scoped_refptr<base::SingleThreadTaskRunner> GetCurrentTaskRunner();
+
+  // CefTaskRunner methods:
+  bool IsSame(CefRefPtr<CefTaskRunner> that) override;
+  bool BelongsToCurrentThread() override;
+  bool BelongsToThread(CefThreadId threadId) override;
+  bool PostTask(CefRefPtr<CefTask> task) override;
+  bool PostDelayedTask(CefRefPtr<CefTask> task, int64 delay_ms) override;
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  IMPLEMENT_REFCOUNTING(CefTaskRunnerImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefTaskRunnerImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_TASK_RUNNER_IMPL_H_
diff --git a/src/libcef/common/test/translator_test_impl.cc b/src/libcef/common/test/translator_test_impl.cc
new file mode 100644
index 0000000..1962b50
--- /dev/null
+++ b/src/libcef/common/test/translator_test_impl.cc
@@ -0,0 +1,559 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/test/cef_translator_test.h"
+
+class CefTranslatorTestRefPtrLibraryImpl
+    : public CefTranslatorTestRefPtrLibrary {
+ public:
+  explicit CefTranslatorTestRefPtrLibraryImpl(int value) : value_(value) {}
+
+  int GetValue() override { return value_; }
+
+  void SetValue(int value) override { value_ = value; }
+
+ protected:
+  int value_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefTranslatorTestRefPtrLibraryImpl);
+  IMPLEMENT_REFCOUNTING(CefTranslatorTestRefPtrLibraryImpl);
+};
+
+// static
+CefRefPtr<CefTranslatorTestRefPtrLibrary>
+CefTranslatorTestRefPtrLibrary::Create(int value) {
+  return new CefTranslatorTestRefPtrLibraryImpl(value);
+}
+
+class CefTranslatorTestRefPtrLibraryChildImpl
+    : public CefTranslatorTestRefPtrLibraryChild {
+ public:
+  CefTranslatorTestRefPtrLibraryChildImpl(int value, int other_value)
+      : value_(value), other_value_(other_value) {}
+
+  int GetValue() override { return value_; }
+
+  void SetValue(int value) override { value_ = value; }
+
+  int GetOtherValue() override { return other_value_; }
+
+  void SetOtherValue(int value) override { other_value_ = value; }
+
+ protected:
+  int value_;
+  int other_value_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefTranslatorTestRefPtrLibraryChildImpl);
+  IMPLEMENT_REFCOUNTING(CefTranslatorTestRefPtrLibraryChildImpl);
+};
+
+// static
+CefRefPtr<CefTranslatorTestRefPtrLibraryChild>
+CefTranslatorTestRefPtrLibraryChild::Create(int value, int other_value) {
+  return new CefTranslatorTestRefPtrLibraryChildImpl(value, other_value);
+}
+
+class CefTranslatorTestRefPtrLibraryChildChildImpl
+    : public CefTranslatorTestRefPtrLibraryChildChild {
+ public:
+  CefTranslatorTestRefPtrLibraryChildChildImpl(int value,
+                                               int other_value,
+                                               int other_other_value)
+      : value_(value),
+        other_value_(other_value),
+        other_other_value_(other_other_value) {}
+
+  int GetValue() override { return value_; }
+
+  void SetValue(int value) override { value_ = value; }
+
+  int GetOtherValue() override { return other_value_; }
+
+  void SetOtherValue(int value) override { other_value_ = value; }
+
+  int GetOtherOtherValue() override { return other_other_value_; }
+
+  void SetOtherOtherValue(int value) override { other_other_value_ = value; }
+
+ protected:
+  int value_;
+  int other_value_;
+  int other_other_value_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefTranslatorTestRefPtrLibraryChildChildImpl);
+  IMPLEMENT_REFCOUNTING(CefTranslatorTestRefPtrLibraryChildChildImpl);
+};
+
+// static
+CefRefPtr<CefTranslatorTestRefPtrLibraryChildChild>
+CefTranslatorTestRefPtrLibraryChildChild::Create(int value,
+                                                 int other_value,
+                                                 int other_other_value) {
+  return new CefTranslatorTestRefPtrLibraryChildChildImpl(value, other_value,
+                                                          other_other_value);
+}
+
+class CefTranslatorTestScopedLibraryImpl
+    : public CefTranslatorTestScopedLibrary {
+ public:
+  explicit CefTranslatorTestScopedLibraryImpl(int value) : value_(value) {}
+
+  int GetValue() override { return value_; }
+
+  void SetValue(int value) override { value_ = value; }
+
+ protected:
+  int value_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefTranslatorTestScopedLibraryImpl);
+};
+
+// static
+CefOwnPtr<CefTranslatorTestScopedLibrary>
+CefTranslatorTestScopedLibrary::Create(int value) {
+  return CefOwnPtr<CefTranslatorTestScopedLibrary>(
+      new CefTranslatorTestScopedLibraryImpl(value));
+}
+
+class CefTranslatorTestScopedLibraryChildImpl
+    : public CefTranslatorTestScopedLibraryChild {
+ public:
+  CefTranslatorTestScopedLibraryChildImpl(int value, int other_value)
+      : value_(value), other_value_(other_value) {}
+
+  int GetValue() override { return value_; }
+
+  void SetValue(int value) override { value_ = value; }
+
+  int GetOtherValue() override { return other_value_; }
+
+  void SetOtherValue(int value) override { other_value_ = value; }
+
+ protected:
+  int value_;
+  int other_value_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefTranslatorTestScopedLibraryChildImpl);
+};
+
+// static
+CefOwnPtr<CefTranslatorTestScopedLibraryChild>
+CefTranslatorTestScopedLibraryChild::Create(int value, int other_value) {
+  return CefOwnPtr<CefTranslatorTestScopedLibraryChild>(
+      new CefTranslatorTestScopedLibraryChildImpl(value, other_value));
+}
+
+class CefTranslatorTestScopedLibraryChildChildImpl
+    : public CefTranslatorTestScopedLibraryChildChild {
+ public:
+  CefTranslatorTestScopedLibraryChildChildImpl(int value,
+                                               int other_value,
+                                               int other_other_value)
+      : value_(value),
+        other_value_(other_value),
+        other_other_value_(other_other_value) {}
+
+  int GetValue() override { return value_; }
+
+  void SetValue(int value) override { value_ = value; }
+
+  int GetOtherValue() override { return other_value_; }
+
+  void SetOtherValue(int value) override { other_value_ = value; }
+
+  int GetOtherOtherValue() override { return other_other_value_; }
+
+  void SetOtherOtherValue(int value) override { other_other_value_ = value; }
+
+ protected:
+  int value_;
+  int other_value_;
+  int other_other_value_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefTranslatorTestScopedLibraryChildChildImpl);
+};
+
+// static
+CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>
+CefTranslatorTestScopedLibraryChildChild::Create(int value,
+                                                 int other_value,
+                                                 int other_other_value) {
+  return CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>(
+      new CefTranslatorTestScopedLibraryChildChildImpl(value, other_value,
+                                                       other_other_value));
+}
+
+class CefTranslatorTestImpl : public CefTranslatorTest {
+ public:
+  CefTranslatorTestImpl() {}
+
+  // PRIMITIVE VALUES
+
+  void GetVoid() override {}
+
+  bool GetBool() override { return TEST_BOOL_VAL; }
+
+  int GetInt() override { return TEST_INT_VAL; }
+
+  double GetDouble() override { return TEST_DOUBLE_VAL; }
+
+  long GetLong() override { return TEST_LONG_VAL; }
+
+  size_t GetSizet() override { return TEST_SIZET_VAL; }
+
+  bool SetVoid() override { return true; }
+
+  bool SetBool(bool val) override { return (val == TEST_BOOL_VAL); }
+
+  bool SetInt(int val) override { return (val == TEST_INT_VAL); }
+
+  bool SetDouble(double val) override { return (val == TEST_DOUBLE_VAL); }
+
+  bool SetLong(long val) override { return (val == TEST_LONG_VAL); }
+
+  bool SetSizet(size_t val) override { return (val == TEST_SIZET_VAL); }
+
+  // PRIMITIVE LIST VALUES
+
+  bool SetIntList(const std::vector<int>& val) override {
+    if (val.size() != 2U)
+      return false;
+    return val[0] == TEST_INT_VAL && val[1] == TEST_INT_VAL2;
+  }
+
+  bool GetIntListByRef(IntList& val) override {
+    if (val.size() != GetIntListSize())
+      return false;
+    val.clear();
+    val.push_back(TEST_INT_VAL);
+    val.push_back(TEST_INT_VAL2);
+    return true;
+  }
+
+  size_t GetIntListSize() override { return 2U; }
+
+  // STRING VALUES
+
+  CefString GetString() override { return TEST_STRING_VAL; }
+
+  bool SetString(const CefString& val) override {
+    return (val.ToString() == TEST_STRING_VAL);
+  }
+
+  void GetStringByRef(CefString& val) override { val = TEST_STRING_VAL; }
+
+  // STRING LIST VALUES
+
+  bool SetStringList(const std::vector<CefString>& val) override {
+    if (val.size() != 3U)
+      return false;
+    return val[0] == TEST_STRING_VAL && val[1] == TEST_STRING_VAL2 &&
+           val[2] == TEST_STRING_VAL3;
+  }
+
+  bool GetStringListByRef(StringList& val) override {
+    if (val.size() != 0U)
+      return false;
+    val.push_back(TEST_STRING_VAL);
+    val.push_back(TEST_STRING_VAL2);
+    val.push_back(TEST_STRING_VAL3);
+    return true;
+  }
+
+  // STRING MAP VALUES
+
+  bool SetStringMap(const StringMap& val) override {
+    if (val.size() != 3U)
+      return false;
+
+    StringMap::const_iterator it;
+
+    it = val.find(TEST_STRING_KEY);
+    if (it == val.end() || it->second != TEST_STRING_VAL)
+      return false;
+    it = val.find(TEST_STRING_KEY2);
+    if (it == val.end() || it->second != TEST_STRING_VAL2)
+      return false;
+    it = val.find(TEST_STRING_KEY3);
+    if (it == val.end() || it->second != TEST_STRING_VAL3)
+      return false;
+    return true;
+  }
+
+  bool GetStringMapByRef(std::map<CefString, CefString>& val) override {
+    if (val.size() != 0U)
+      return false;
+
+    val.insert(std::make_pair(TEST_STRING_KEY, TEST_STRING_VAL));
+    val.insert(std::make_pair(TEST_STRING_KEY2, TEST_STRING_VAL2));
+    val.insert(std::make_pair(TEST_STRING_KEY3, TEST_STRING_VAL3));
+    return true;
+  }
+
+  // STRING MULTIMAP VALUES
+
+  bool SetStringMultimap(
+      const std::multimap<CefString, CefString>& val) override {
+    if (val.size() != 3U)
+      return false;
+
+    StringMultimap::const_iterator it;
+
+    it = val.find(TEST_STRING_KEY);
+    if (it == val.end() || it->second != TEST_STRING_VAL)
+      return false;
+    it = val.find(TEST_STRING_KEY2);
+    if (it == val.end() || it->second != TEST_STRING_VAL2)
+      return false;
+    it = val.find(TEST_STRING_KEY3);
+    if (it == val.end() || it->second != TEST_STRING_VAL3)
+      return false;
+    return true;
+  }
+
+  bool GetStringMultimapByRef(StringMultimap& val) override {
+    if (val.size() != 0U)
+      return false;
+
+    val.insert(std::make_pair(TEST_STRING_KEY, TEST_STRING_VAL));
+    val.insert(std::make_pair(TEST_STRING_KEY2, TEST_STRING_VAL2));
+    val.insert(std::make_pair(TEST_STRING_KEY3, TEST_STRING_VAL3));
+    return true;
+  }
+
+  // STRUCT VALUES
+
+  CefPoint GetPoint() override { return CefPoint(TEST_X_VAL, TEST_Y_VAL); }
+
+  bool SetPoint(const CefPoint& val) override {
+    return val.x == TEST_X_VAL && val.y == TEST_Y_VAL;
+  }
+
+  void GetPointByRef(CefPoint& val) override {
+    val = CefPoint(TEST_X_VAL, TEST_Y_VAL);
+  }
+
+  // STRUCT LIST VALUES
+
+  bool SetPointList(const std::vector<CefPoint>& val) override {
+    if (val.size() != 2U)
+      return false;
+    return val[0].x == TEST_X_VAL && val[0].y == TEST_Y_VAL &&
+           val[1].x == TEST_X_VAL2 && val[1].y == TEST_Y_VAL2;
+  }
+
+  bool GetPointListByRef(PointList& val) override {
+    if (val.size() != GetPointListSize())
+      return false;
+    val.clear();
+    val.push_back(CefPoint(TEST_X_VAL, TEST_Y_VAL));
+    val.push_back(CefPoint(TEST_X_VAL2, TEST_Y_VAL2));
+    return true;
+  }
+
+  size_t GetPointListSize() override { return 2U; }
+
+  // LIBRARY-SIDE REFPTR VALUES
+
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> GetRefPtrLibrary(int val) override {
+    return new CefTranslatorTestRefPtrLibraryChildImpl(val, 0);
+  }
+
+  int SetRefPtrLibrary(CefRefPtr<CefTranslatorTestRefPtrLibrary> val) override {
+    return val->GetValue();
+  }
+
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> SetRefPtrLibraryAndReturn(
+      CefRefPtr<CefTranslatorTestRefPtrLibrary> val) override {
+    return val;
+  }
+
+  int SetChildRefPtrLibrary(
+      CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) override {
+    return val->GetValue();
+  }
+
+  CefRefPtr<CefTranslatorTestRefPtrLibrary>
+  SetChildRefPtrLibraryAndReturnParent(
+      CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) override {
+    return val;
+  }
+
+  // LIBRARY-SIDE REFPTR LIST VALUES
+
+  bool SetRefPtrLibraryList(
+      const std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>>& val,
+      int val1,
+      int val2) override {
+    if (val.size() != 2U)
+      return false;
+    return val[0]->GetValue() == val1 && val[1]->GetValue() == val2;
+  }
+
+  bool GetRefPtrLibraryListByRef(RefPtrLibraryList& val,
+                                 int val1,
+                                 int val2) override {
+    if (val.size() != GetRefPtrLibraryListSize())
+      return false;
+    val.clear();
+    val.push_back(new CefTranslatorTestRefPtrLibraryChildImpl(val1, 0));
+    val.push_back(new CefTranslatorTestRefPtrLibraryImpl(val2));
+    return true;
+  }
+
+  size_t GetRefPtrLibraryListSize() override { return 2U; }
+
+  // CLIENT-SIDE REFPTR VALUES
+
+  int SetRefPtrClient(CefRefPtr<CefTranslatorTestRefPtrClient> val) override {
+    return val->GetValue();
+  }
+
+  CefRefPtr<CefTranslatorTestRefPtrClient> SetRefPtrClientAndReturn(
+      CefRefPtr<CefTranslatorTestRefPtrClient> val) override {
+    return val;
+  }
+
+  int SetChildRefPtrClient(
+      CefRefPtr<CefTranslatorTestRefPtrClientChild> val) override {
+    return val->GetValue();
+  }
+
+  CefRefPtr<CefTranslatorTestRefPtrClient> SetChildRefPtrClientAndReturnParent(
+      CefRefPtr<CefTranslatorTestRefPtrClientChild> val) override {
+    return val;
+  }
+
+  // CLIENT-SIDE REFPTR LIST VALUES
+
+  bool SetRefPtrClientList(
+      const std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>>& val,
+      int val1,
+      int val2) override {
+    if (val.size() != 2U)
+      return false;
+    return val[0]->GetValue() == val1 && val[1]->GetValue() == val2;
+  }
+
+  bool GetRefPtrClientListByRef(
+      RefPtrClientList& val,
+      CefRefPtr<CefTranslatorTestRefPtrClient> val1,
+      CefRefPtr<CefTranslatorTestRefPtrClient> val2) override {
+    if (val.size() != GetRefPtrClientListSize())
+      return false;
+    val.clear();
+    val.push_back(val1);
+    val.push_back(val2);
+    return true;
+  }
+
+  size_t GetRefPtrClientListSize() override { return 2U; }
+
+  // LIBRARY-SIDE OWNPTR VALUES
+
+  CefOwnPtr<CefTranslatorTestScopedLibrary> GetOwnPtrLibrary(int val) override {
+    return CefOwnPtr<CefTranslatorTestScopedLibrary>(
+        new CefTranslatorTestScopedLibraryChildImpl(val, 0));
+  }
+
+  int SetOwnPtrLibrary(CefOwnPtr<CefTranslatorTestScopedLibrary> val) override {
+    return val->GetValue();
+  }
+
+  CefOwnPtr<CefTranslatorTestScopedLibrary> SetOwnPtrLibraryAndReturn(
+      CefOwnPtr<CefTranslatorTestScopedLibrary> val) override {
+    return val;
+  }
+
+  int SetChildOwnPtrLibrary(
+      CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) override {
+    return val->GetValue();
+  }
+
+  CefOwnPtr<CefTranslatorTestScopedLibrary>
+  SetChildOwnPtrLibraryAndReturnParent(
+      CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) override {
+    return CefOwnPtr<CefTranslatorTestScopedLibrary>(val.release());
+  }
+
+  // CLIENT-SIDE OWNPTR VALUES
+
+  int SetOwnPtrClient(CefOwnPtr<CefTranslatorTestScopedClient> val) override {
+    return val->GetValue();
+  }
+
+  CefOwnPtr<CefTranslatorTestScopedClient> SetOwnPtrClientAndReturn(
+      CefOwnPtr<CefTranslatorTestScopedClient> val) override {
+    return val;
+  }
+
+  int SetChildOwnPtrClient(
+      CefOwnPtr<CefTranslatorTestScopedClientChild> val) override {
+    return val->GetValue();
+  }
+
+  CefOwnPtr<CefTranslatorTestScopedClient> SetChildOwnPtrClientAndReturnParent(
+      CefOwnPtr<CefTranslatorTestScopedClientChild> val) override {
+    return CefOwnPtr<CefTranslatorTestScopedClient>(val.release());
+  }
+
+  // LIBRARY-SIDE RAWPTR VALUES
+
+  int SetRawPtrLibrary(CefRawPtr<CefTranslatorTestScopedLibrary> val) override {
+    return val->GetValue();
+  }
+
+  int SetChildRawPtrLibrary(
+      CefRawPtr<CefTranslatorTestScopedLibraryChild> val) override {
+    return val->GetValue();
+  }
+
+  // LIBRARY-SIDE RAWPTR LIST VALUES
+
+  bool SetRawPtrLibraryList(
+      const std::vector<CefRawPtr<CefTranslatorTestScopedLibrary>>& val,
+      int val1,
+      int val2) override {
+    if (val.size() != 2U)
+      return false;
+    return val[0]->GetValue() == val1 && val[1]->GetValue() == val2;
+  }
+
+  // CLIENT-SIDE RAWPTR VALUES
+
+  int SetRawPtrClient(CefRawPtr<CefTranslatorTestScopedClient> val) override {
+    return val->GetValue();
+  }
+
+  int SetChildRawPtrClient(
+      CefRawPtr<CefTranslatorTestScopedClientChild> val) override {
+    return val->GetValue();
+  }
+
+  // CLIENT-SIDE RAWPTR LIST VALUES
+
+  bool SetRawPtrClientList(
+      const std::vector<CefRawPtr<CefTranslatorTestScopedClient>>& val,
+      int val1,
+      int val2) override {
+    if (val.size() != 2U)
+      return false;
+    return val[0]->GetValue() == val1 && val[1]->GetValue() == val2;
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(CefTranslatorTestImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefTranslatorTestImpl);
+};
+
+// static
+CefRefPtr<CefTranslatorTest> CefTranslatorTest::Create() {
+  return new CefTranslatorTestImpl();
+}
diff --git a/src/libcef/common/thread_impl.cc b/src/libcef/common/thread_impl.cc
new file mode 100644
index 0000000..adcce19
--- /dev/null
+++ b/src/libcef/common/thread_impl.cc
@@ -0,0 +1,147 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/common/thread_impl.h"
+
+#include "libcef/common/task_runner_impl.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace {
+
+void StopAndDestroy(base::Thread* thread) {
+  // Calling PlatformThread::Join() on the UI thread is otherwise disallowed.
+  base::ScopedAllowBaseSyncPrimitivesForTesting scoped_allow_sync_primitives;
+
+  // Deleting |thread| will implicitly stop and join it.
+  delete thread;
+}
+
+}  // namespace
+
+// static
+CefRefPtr<CefThread> CefThread::CreateThread(
+    const CefString& display_name,
+    cef_thread_priority_t priority,
+    cef_message_loop_type_t message_loop_type,
+    bool stoppable,
+    cef_com_init_mode_t com_init_mode) {
+  if (!CefTaskRunnerImpl::GetCurrentTaskRunner()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  CefRefPtr<CefThreadImpl> thread_impl = new CefThreadImpl();
+  if (!thread_impl->Create(display_name, priority, message_loop_type, stoppable,
+                           com_init_mode)) {
+    return nullptr;
+  }
+  return thread_impl;
+}
+
+CefThreadImpl::CefThreadImpl() : thread_id_(kInvalidPlatformThreadId) {}
+
+CefThreadImpl::~CefThreadImpl() {
+  if (thread_.get()) {
+    if (!owner_task_runner_->RunsTasksInCurrentSequence()) {
+      // Delete |thread_| on the correct thread.
+      owner_task_runner_->PostTask(
+          FROM_HERE,
+          base::Bind(StopAndDestroy, base::Unretained(thread_.release())));
+    } else {
+      StopAndDestroy(thread_.release());
+    }
+  }
+}
+
+bool CefThreadImpl::Create(const CefString& display_name,
+                           cef_thread_priority_t priority,
+                           cef_message_loop_type_t message_loop_type,
+                           bool stoppable,
+                           cef_com_init_mode_t com_init_mode) {
+  owner_task_runner_ = CefTaskRunnerImpl::GetCurrentTaskRunner();
+  DCHECK(owner_task_runner_);
+  if (!owner_task_runner_)
+    return false;
+
+  thread_.reset(new base::Thread(display_name));
+
+  base::Thread::Options options;
+
+  switch (priority) {
+    case TP_BACKGROUND:
+      options.priority = base::ThreadPriority::BACKGROUND;
+      break;
+    case TP_DISPLAY:
+      options.priority = base::ThreadPriority::DISPLAY;
+      break;
+    case TP_REALTIME_AUDIO:
+      options.priority = base::ThreadPriority::REALTIME_AUDIO;
+      break;
+    default:
+      break;
+  }
+
+  switch (message_loop_type) {
+    case ML_TYPE_UI:
+      options.message_pump_type = base::MessagePumpType::UI;
+      break;
+    case ML_TYPE_IO:
+      options.message_pump_type = base::MessagePumpType::IO;
+      break;
+    default:
+      break;
+  }
+
+  options.joinable = stoppable;
+
+#if defined(OS_WIN)
+  if (com_init_mode != COM_INIT_MODE_NONE) {
+    if (com_init_mode == COM_INIT_MODE_STA)
+      options.message_pump_type = base::MessagePumpType::UI;
+    thread_->init_com_with_mta(com_init_mode == COM_INIT_MODE_MTA);
+  }
+#endif
+
+  if (!thread_->StartWithOptions(options)) {
+    thread_.reset();
+    return false;
+  }
+
+  thread_task_runner_ = new CefTaskRunnerImpl(thread_->task_runner());
+  thread_id_ = thread_->GetThreadId();
+  return true;
+}
+
+CefRefPtr<CefTaskRunner> CefThreadImpl::GetTaskRunner() {
+  return thread_task_runner_;
+}
+
+cef_platform_thread_id_t CefThreadImpl::GetPlatformThreadId() {
+  return thread_id_;
+}
+
+void CefThreadImpl::Stop() {
+  if (!owner_task_runner_)
+    return;
+  if (!owner_task_runner_->RunsTasksInCurrentSequence()) {
+    NOTREACHED() << "called on invalid thread";
+    return;
+  }
+
+  if (thread_)
+    StopAndDestroy(thread_.release());
+}
+
+bool CefThreadImpl::IsRunning() {
+  if (!owner_task_runner_)
+    return false;
+  if (!owner_task_runner_->RunsTasksInCurrentSequence()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  return thread_ && thread_->IsRunning();
+}
diff --git a/src/libcef/common/thread_impl.h b/src/libcef/common/thread_impl.h
new file mode 100644
index 0000000..34bfc9e
--- /dev/null
+++ b/src/libcef/common/thread_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_THREAD_IMPL_H_
+#define CEF_LIBCEF_COMMON_THREAD_IMPL_H_
+#pragma once
+
+#include "include/cef_thread.h"
+
+#include "base/threading/thread.h"
+
+class CefThreadImpl : public CefThread {
+ public:
+  CefThreadImpl();
+  ~CefThreadImpl();
+
+  bool Create(const CefString& display_name,
+              cef_thread_priority_t priority,
+              cef_message_loop_type_t message_loop_type,
+              bool stoppable,
+              cef_com_init_mode_t com_init_mode);
+
+  // CefThread methods:
+  CefRefPtr<CefTaskRunner> GetTaskRunner() override;
+  cef_platform_thread_id_t GetPlatformThreadId() override;
+  void Stop() override;
+  bool IsRunning() override;
+
+ private:
+  std::unique_ptr<base::Thread> thread_;
+  cef_platform_thread_id_t thread_id_;
+  CefRefPtr<CefTaskRunner> thread_task_runner_;
+
+  // TaskRunner for the owner thread.
+  scoped_refptr<base::SequencedTaskRunner> owner_task_runner_;
+
+  IMPLEMENT_REFCOUNTING(CefThreadImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefThreadImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_THREAD_IMPL_H_
diff --git a/src/libcef/common/time_impl.cc b/src/libcef/common/time_impl.cc
new file mode 100644
index 0000000..67f3163
--- /dev/null
+++ b/src/libcef/common/time_impl.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/time_util.h"
+
+#include "base/macros.h"
+
+void cef_time_to_basetime(const cef_time_t& cef_time, base::Time& time) {
+  base::Time::Exploded exploded;
+  exploded.year = cef_time.year;
+  exploded.month = cef_time.month;
+  exploded.day_of_week = cef_time.day_of_week;
+  exploded.day_of_month = cef_time.day_of_month;
+  exploded.hour = cef_time.hour;
+  exploded.minute = cef_time.minute;
+  exploded.second = cef_time.second;
+  exploded.millisecond = cef_time.millisecond;
+  ignore_result(base::Time::FromUTCExploded(exploded, &time));
+}
+
+void cef_time_from_basetime(const base::Time& time, cef_time_t& cef_time) {
+#if defined(OS_WIN)
+  int64_t t = time.ToDeltaSinceWindowsEpoch().InMicroseconds();
+  // From MSDN, FILETIME "Contains a 64-bit value representing the number of
+  // 100-nanosecond intervals since January 1, 1601 (UTC)." This value must
+  // be less than 0x8000000000000000. Otherwise, the function
+  // FileTimeToSystemTime fails.
+  if (t < 0 || static_cast<uint64_t>(t * 10) >= 0x8000000000000000)
+    return;
+#endif
+
+  base::Time::Exploded exploded;
+  time.UTCExplode(&exploded);
+  cef_time.year = exploded.year;
+  cef_time.month = exploded.month;
+  cef_time.day_of_week = exploded.day_of_week;
+  cef_time.day_of_month = exploded.day_of_month;
+  cef_time.hour = exploded.hour;
+  cef_time.minute = exploded.minute;
+  cef_time.second = exploded.second;
+  cef_time.millisecond = exploded.millisecond;
+}
+
+CEF_EXPORT int cef_time_to_timet(const cef_time_t* cef_time, time_t* time) {
+  if (!cef_time || !time)
+    return 0;
+
+  base::Time base_time;
+  cef_time_to_basetime(*cef_time, base_time);
+  *time = base_time.ToTimeT();
+  return 1;
+}
+
+CEF_EXPORT int cef_time_from_timet(time_t time, cef_time_t* cef_time) {
+  if (!cef_time)
+    return 0;
+
+  base::Time base_time = base::Time::FromTimeT(time);
+  cef_time_from_basetime(base_time, *cef_time);
+  return 1;
+}
+
+CEF_EXPORT int cef_time_to_doublet(const cef_time_t* cef_time, double* time) {
+  if (!cef_time || !time)
+    return 0;
+
+  base::Time base_time;
+  cef_time_to_basetime(*cef_time, base_time);
+  *time = base_time.ToDoubleT();
+  return 1;
+}
+
+CEF_EXPORT int cef_time_from_doublet(double time, cef_time_t* cef_time) {
+  if (!cef_time)
+    return 0;
+
+  base::Time base_time = base::Time::FromDoubleT(time);
+  cef_time_from_basetime(base_time, *cef_time);
+  return 1;
+}
+
+CEF_EXPORT int cef_time_now(cef_time_t* cef_time) {
+  if (!cef_time)
+    return 0;
+
+  base::Time base_time = base::Time::Now();
+  cef_time_from_basetime(base_time, *cef_time);
+  return 1;
+}
+
+CEF_EXPORT int cef_time_delta(const cef_time_t* cef_time1,
+                              const cef_time_t* cef_time2,
+                              long long* delta) {
+  if (!cef_time1 || !cef_time2 || !delta)
+    return 0;
+
+  base::Time base_time1, base_time2;
+  cef_time_to_basetime(*cef_time1, base_time1);
+  cef_time_to_basetime(*cef_time2, base_time2);
+
+  base::TimeDelta time_delta = base_time2 - base_time1;
+  *delta = time_delta.InMilliseconds();
+  return 1;
+}
diff --git a/src/libcef/common/time_util.h b/src/libcef/common/time_util.h
new file mode 100644
index 0000000..bbfa074
--- /dev/null
+++ b/src/libcef/common/time_util.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_TIME_UTIL_H_
+#define CEF_LIBCEF_COMMON_TIME_UTIL_H_
+#pragma once
+
+#include "base/time/time.h"
+#include "include/internal/cef_time.h"
+
+// Converts cef_time_t to/from a base::Time object.
+void cef_time_to_basetime(const cef_time_t& cef_time, base::Time& time);
+void cef_time_from_basetime(const base::Time& time, cef_time_t& cef_time);
+
+#endif  // CEF_LIBCEF_COMMON_TIME_UTIL_H_
diff --git a/src/libcef/common/tracker.cc b/src/libcef/common/tracker.cc
new file mode 100644
index 0000000..5e5c509
--- /dev/null
+++ b/src/libcef/common/tracker.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/tracker.h"
+
+// CefTrackNode implementation.
+
+CefTrackNode::CefTrackNode() : track_next_(nullptr), track_prev_(nullptr) {}
+
+CefTrackNode::~CefTrackNode() {}
+
+void CefTrackNode::InsertTrackPrev(CefTrackNode* object) {
+  if (track_prev_)
+    track_prev_->SetTrackNext(object);
+  object->SetTrackNext(this);
+  object->SetTrackPrev(track_prev_);
+  track_prev_ = object;
+}
+
+void CefTrackNode::InsertTrackNext(CefTrackNode* object) {
+  if (track_next_)
+    track_next_->SetTrackPrev(object);
+  object->SetTrackPrev(this);
+  object->SetTrackNext(track_next_);
+  track_next_ = object;
+}
+
+void CefTrackNode::RemoveTracking() {
+  if (track_next_)
+    track_next_->SetTrackPrev(track_prev_);
+  if (track_prev_)
+    track_prev_->SetTrackNext(track_next_);
+  track_next_ = nullptr;
+  track_prev_ = nullptr;
+}
+
+// CefTrackManager implementation.
+
+CefTrackManager::CefTrackManager() : object_count_(0) {}
+
+CefTrackManager::~CefTrackManager() {
+  DeleteAll();
+}
+
+void CefTrackManager::Add(CefTrackNode* object) {
+  base::AutoLock lock_scope(lock_);
+  if (!object->IsTracked()) {
+    tracker_.InsertTrackNext(object);
+    ++object_count_;
+  }
+}
+
+bool CefTrackManager::Delete(CefTrackNode* object) {
+  base::AutoLock lock_scope(lock_);
+  if (object->IsTracked()) {
+    object->RemoveTracking();
+    delete object;
+    --object_count_;
+    return true;
+  }
+  return false;
+}
+
+void CefTrackManager::DeleteAll() {
+  base::AutoLock lock_scope(lock_);
+  CefTrackNode* next;
+  do {
+    next = tracker_.GetTrackNext();
+    if (next) {
+      next->RemoveTracking();
+      delete next;
+    }
+  } while (next != nullptr);
+  object_count_ = 0;
+}
diff --git a/src/libcef/common/tracker.h b/src/libcef/common/tracker.h
new file mode 100644
index 0000000..84aad62
--- /dev/null
+++ b/src/libcef/common/tracker.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_TRACKER_H_
+#define CEF_LIBCEF_COMMON_TRACKER_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+#include "base/synchronization/lock.h"
+
+// Class extended by objects that must be tracked.  After creating a tracked
+// object you should add it to the appropriate track manager.
+class CefTrackNode {
+ public:
+  CefTrackNode();
+  virtual ~CefTrackNode();
+
+  // Returns true if the object is currently being tracked.
+  inline bool IsTracked() { return (track_prev_ || track_next_); }
+
+ private:
+  inline CefTrackNode* GetTrackPrev() { return track_prev_; }
+  inline void SetTrackPrev(CefTrackNode* base) { track_prev_ = base; }
+  inline CefTrackNode* GetTrackNext() { return track_next_; }
+  inline void SetTrackNext(CefTrackNode* base) { track_next_ = base; }
+
+  // Insert a new object into the tracking list before this object.
+  void InsertTrackPrev(CefTrackNode* object);
+
+  // Insert a new object into the tracking list after this object.
+  void InsertTrackNext(CefTrackNode* object);
+
+  // Remove this object from the tracking list.
+  void RemoveTracking();
+
+ private:
+  CefTrackNode* track_next_;
+  CefTrackNode* track_prev_;
+
+  friend class CefTrackManager;
+};
+
+// Class used to manage tracked objects.  A single instance of this class
+// should be created for each intended usage.  Any objects that have not been
+// removed by explicit calls to the Destroy() method will be removed when the
+// manager object is destroyed.  A manager object can be created as either a
+// member variable of another class or by using lazy initialization:
+// base::LazyInstance<CefTrackManager> g_singleton = LAZY_INSTANCE_INITIALIZER;
+class CefTrackManager : public CefBaseRefCounted {
+ public:
+  CefTrackManager();
+  ~CefTrackManager() override;
+
+  // Add an object to be tracked by this manager.
+  void Add(CefTrackNode* object);
+
+  // Delete an object tracked by this manager.
+  bool Delete(CefTrackNode* object);
+
+  // Delete all objects tracked by this manager.
+  void DeleteAll();
+
+  // Returns the number of objects currently being tracked.
+  inline int GetCount() { return object_count_; }
+
+ private:
+  CefTrackNode tracker_;
+  int object_count_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(CefTrackManager);
+};
+
+#endif  // CEF_LIBCEF_COMMON_TRACKER_H_
diff --git a/src/libcef/common/urlrequest_impl.cc b/src/libcef/common/urlrequest_impl.cc
new file mode 100644
index 0000000..f86b1a6
--- /dev/null
+++ b/src/libcef/common/urlrequest_impl.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_urlrequest.h"
+#include "libcef/browser/net_service/browser_urlrequest_impl.h"
+#include "libcef/common/content_client.h"
+#include "libcef/common/task_runner_impl.h"
+#include "libcef/renderer/render_urlrequest_impl.h"
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "content/public/common/content_client.h"
+
+// static
+CefRefPtr<CefURLRequest> CefURLRequest::Create(
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefURLRequestClient> client,
+    CefRefPtr<CefRequestContext> request_context) {
+  if (!request.get() || !client.get()) {
+    NOTREACHED() << "called with invalid parameters";
+    return nullptr;
+  }
+
+  if (!CefTaskRunnerImpl::GetCurrentTaskRunner()) {
+    NOTREACHED() << "called on invalid thread";
+    return nullptr;
+  }
+
+  if (CefContentClient::Get()->browser()) {
+    // In the browser process.
+    CefRefPtr<CefBrowserURLRequest> impl =
+        new CefBrowserURLRequest(nullptr, request, client, request_context);
+    if (impl->Start())
+      return impl.get();
+    return nullptr;
+  } else if (CefContentClient::Get()->renderer()) {
+    // In the render process.
+    CefRefPtr<CefRenderURLRequest> impl =
+        new CefRenderURLRequest(nullptr, request, client);
+    if (impl->Start())
+      return impl.get();
+    return nullptr;
+  } else {
+    NOTREACHED() << "called in unsupported process";
+    return nullptr;
+  }
+}
diff --git a/src/libcef/common/util_mac.h b/src/libcef/common/util_mac.h
new file mode 100644
index 0000000..1d07209
--- /dev/null
+++ b/src/libcef/common/util_mac.h
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2011 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_UTIL_MAC_H_
+#define CEF_LIBCEF_COMMON_UTIL_MAC_H_
+#pragma once
+
+#include <string>
+
+namespace base {
+class FilePath;
+}
+
+namespace util_mac {
+
+// Returns the path to the NSLibraryDirectory (e.g. "~/Library").
+bool GetLocalLibraryDirectory(base::FilePath* result);
+
+// Returns the path to the CEF framework directory inside the top-level app
+// bundle (e.g. "myapp.app/Contents/Frameworks/Chromium Embedded
+// Framework.framework"). May return an empty value if not running in an app
+// bundle.
+base::FilePath GetFrameworkDirectory();
+
+// Returns the path to the Resources directory inside the CEF framework
+// directory (e.g. "myapp.app/Contents/Frameworks/Chromium Embedded
+// Framework.framework/Resources"). May return an empty value if not running in
+// an app bundle.
+base::FilePath GetFrameworkResourcesDirectory();
+
+// Returns the path to the main (running) process executable (e.g.
+// "myapp.app/Contents/MacOS/myapp").
+base::FilePath GetMainProcessPath();
+
+// Returns the path to the top-level app bundle that contains the main process
+// executable (e.g. "myapp.app").
+base::FilePath GetMainBundlePath();
+
+// Returns the identifier for the top-level app bundle.
+std::string GetMainBundleID();
+
+// Returns the path to the Resources directory inside the top-level app bundle
+// (e.g. "myapp.app/Contents/Resources"). May return an empty value if not
+// running in an app bundle.
+base::FilePath GetMainResourcesDirectory();
+
+// Returns the path to the child process executable (e.g. "myapp.app/
+// Contents/Frameworks/myapp Helper.app/Contents/MacOS/myapp Helper"). May
+// return an empty value if not running in an app bundle.
+base::FilePath GetChildProcessPath();
+
+}  // namespace util_mac
+
+#endif  // CEF_LIBCEF_COMMON_UTIL_MAC_H_
diff --git a/src/libcef/common/util_mac.mm b/src/libcef/common/util_mac.mm
new file mode 100644
index 0000000..dae9b45
--- /dev/null
+++ b/src/libcef/common/util_mac.mm
@@ -0,0 +1,103 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2011 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/common/util_mac.h"
+
+#include "libcef/common/cef_switches.h"
+
+#include "base/base_paths.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
+#include "base/path_service.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace util_mac {
+
+namespace {
+
+// Returns the path to the Frameworks directory inside the top-level app bundle.
+base::FilePath GetFrameworksPath() {
+  base::FilePath bundle_path = GetMainBundlePath();
+  if (bundle_path.empty())
+    return base::FilePath();
+
+  return bundle_path.Append(FILE_PATH_LITERAL("Contents"))
+      .Append(FILE_PATH_LITERAL("Frameworks"));
+}
+
+}  // namespace
+
+bool GetLocalLibraryDirectory(base::FilePath* result) {
+  return base::mac::GetLocalDirectory(NSLibraryDirectory, result);
+}
+
+base::FilePath GetFrameworkDirectory() {
+  base::FilePath frameworks_path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+          switches::kFrameworkDirPath);
+  if (!frameworks_path.empty())
+    return frameworks_path;
+
+  frameworks_path = GetFrameworksPath();
+  if (frameworks_path.empty())
+    return base::FilePath();
+
+  return frameworks_path.Append(
+      FILE_PATH_LITERAL("Chromium Embedded Framework.framework"));
+}
+
+base::FilePath GetFrameworkResourcesDirectory() {
+  base::FilePath frameworks_path = GetFrameworkDirectory();
+  if (frameworks_path.empty())
+    return base::FilePath();
+
+  return frameworks_path.Append(FILE_PATH_LITERAL("Resources"));
+}
+
+base::FilePath GetMainProcessPath() {
+  base::FilePath path;
+  base::PathService::Get(base::FILE_EXE, &path);
+  DCHECK(!path.empty());
+  return path;
+}
+
+base::FilePath GetMainBundlePath() {
+  base::FilePath main_bundle_path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+          switches::kMainBundlePath);
+  if (!main_bundle_path.empty())
+    return main_bundle_path;
+
+  return base::mac::GetAppBundlePath(GetMainProcessPath());
+}
+
+std::string GetMainBundleID() {
+  NSBundle* bundle = base::mac::OuterBundle();
+  return base::SysNSStringToUTF8([bundle bundleIdentifier]);
+}
+
+base::FilePath GetMainResourcesDirectory() {
+  base::FilePath bundle_path = GetMainBundlePath();
+  if (bundle_path.empty())
+    return base::FilePath();
+
+  return bundle_path.Append(FILE_PATH_LITERAL("Contents"))
+      .Append(FILE_PATH_LITERAL("Resources"));
+}
+
+base::FilePath GetChildProcessPath() {
+  base::FilePath frameworks_path = GetFrameworksPath();
+  if (frameworks_path.empty())
+    return base::FilePath();
+
+  std::string exe_name = GetMainProcessPath().BaseName().value();
+  return frameworks_path.Append(FILE_PATH_LITERAL(exe_name + " Helper.app"))
+      .Append(FILE_PATH_LITERAL("Contents"))
+      .Append(FILE_PATH_LITERAL("MacOS"))
+      .Append(FILE_PATH_LITERAL(exe_name + " Helper"));
+}
+
+}  // namespace util_mac
diff --git a/src/libcef/common/value_base.cc b/src/libcef/common/value_base.cc
new file mode 100644
index 0000000..f8c741b
--- /dev/null
+++ b/src/libcef/common/value_base.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/value_base.h"
+
+CefValueController::CefValueController()
+    : owner_value_(nullptr), owner_object_(nullptr) {}
+
+CefValueController::~CefValueController() {
+  // Everything should already have been removed.
+  DCHECK(!owner_value_ && !owner_object_);
+  DCHECK(reference_map_.empty());
+  DCHECK(dependency_map_.empty());
+}
+
+void CefValueController::SetOwner(void* value, Object* object) {
+  DCHECK(value && object);
+
+  // Controller should already be locked.
+  DCHECK(locked());
+
+  // Owner should only be set once.
+  DCHECK(!owner_value_ && !owner_object_);
+
+  owner_value_ = value;
+  owner_object_ = object;
+}
+
+void CefValueController::AddReference(void* value, Object* object) {
+  DCHECK(value && object);
+
+  // Controller should already be locked.
+  DCHECK(locked());
+
+  // Controller should currently have an owner.
+  DCHECK(owner_value_);
+
+  // Values should only be added once.
+  DCHECK(reference_map_.find(value) == reference_map_.end());
+  DCHECK(value != owner_value_);
+
+  reference_map_.insert(std::make_pair(value, object));
+}
+
+void CefValueController::Remove(void* value, bool notify_object) {
+  DCHECK(value);
+
+  // Controller should already be locked.
+  DCHECK(locked());
+
+  // Controller should currently have an owner.
+  DCHECK(owner_value_);
+
+  if (value == owner_value_) {
+    // Should never notify when removing the owner object.
+    DCHECK(!notify_object);
+
+    owner_value_ = nullptr;
+    owner_object_ = nullptr;
+
+    // Remove all references.
+    if (reference_map_.size() > 0) {
+      ReferenceMap::iterator it = reference_map_.begin();
+      for (; it != reference_map_.end(); ++it)
+        it->second->OnControlRemoved();
+      reference_map_.clear();
+    }
+
+    // Remove all dependencies.
+    dependency_map_.clear();
+  } else {
+    ReferenceMap::iterator it = reference_map_.find(value);
+    if (it != reference_map_.end()) {
+      // Remove the reference.
+      if (notify_object)
+        it->second->OnControlRemoved();
+      reference_map_.erase(it);
+    }
+  }
+}
+
+CefValueController::Object* CefValueController::Get(void* value) {
+  DCHECK(value);
+
+  // Controller should already be locked.
+  DCHECK(locked());
+
+  if (value == owner_value_) {
+    return owner_object_;
+  } else {
+    ReferenceMap::iterator it = reference_map_.find(value);
+    if (it != reference_map_.end())
+      return it->second;
+    return nullptr;
+  }
+}
+
+void CefValueController::AddDependency(void* parent, void* child) {
+  DCHECK(parent && child && parent != child);
+
+  // Controller should already be locked.
+  DCHECK(locked());
+
+  DependencyMap::iterator it = dependency_map_.find(parent);
+  if (it == dependency_map_.end()) {
+    // New set.
+    DependencySet set;
+    set.insert(child);
+    dependency_map_.insert(std::make_pair(parent, set));
+  } else if (it->second.find(child) == it->second.end()) {
+    // Update existing set.
+    it->second.insert(child);
+  }
+}
+
+void CefValueController::RemoveDependencies(void* value) {
+  DCHECK(value);
+
+  // Controller should already be locked.
+  DCHECK(locked());
+
+  if (dependency_map_.empty())
+    return;
+
+  DependencyMap::iterator it_dependency = dependency_map_.find(value);
+  if (it_dependency == dependency_map_.end())
+    return;
+
+  // Start with the set of dependencies for the current value.
+  DependencySet remove_set = it_dependency->second;
+  dependency_map_.erase(it_dependency);
+
+  DependencySet::iterator it_value;
+  ReferenceMap::iterator it_reference;
+
+  while (remove_set.size() > 0) {
+    it_value = remove_set.begin();
+    value = *it_value;
+    remove_set.erase(it_value);
+
+    // Does the current value have dependencies?
+    it_dependency = dependency_map_.find(value);
+    if (it_dependency != dependency_map_.end()) {
+      // Append the dependency set to the remove set.
+      remove_set.insert(it_dependency->second.begin(),
+                        it_dependency->second.end());
+      dependency_map_.erase(it_dependency);
+    }
+
+    // Does the current value have a reference?
+    it_reference = reference_map_.find(value);
+    if (it_reference != reference_map_.end()) {
+      // Remove the reference.
+      it_reference->second->OnControlRemoved();
+      reference_map_.erase(it_reference);
+    }
+  }
+}
+
+void CefValueController::TakeFrom(CefValueController* other) {
+  DCHECK(other);
+
+  // Both controllers should already be locked.
+  DCHECK(locked());
+  DCHECK(other->locked());
+
+  if (!other->reference_map_.empty()) {
+    // Transfer references from the other to this.
+    ReferenceMap::iterator it = other->reference_map_.begin();
+    for (; it != other->reference_map_.end(); ++it) {
+      // References should only be added once.
+      DCHECK(reference_map_.find(it->first) == reference_map_.end());
+      reference_map_.insert(std::make_pair(it->first, it->second));
+    }
+    other->reference_map_.clear();
+  }
+
+  if (!other->dependency_map_.empty()) {
+    // Transfer dependencies from the other to this.
+    DependencyMap::iterator it_other = other->dependency_map_.begin();
+    for (; it_other != other->dependency_map_.end(); ++it_other) {
+      DependencyMap::iterator it_me = dependency_map_.find(it_other->first);
+      if (it_me == dependency_map_.end()) {
+        // All children are new.
+        dependency_map_.insert(
+            std::make_pair(it_other->first, it_other->second));
+      } else {
+        // Evaluate each child.
+        DependencySet::iterator it_other_set = it_other->second.begin();
+        for (; it_other_set != it_other->second.end(); ++it_other_set) {
+          if (it_me->second.find(*it_other_set) == it_me->second.end())
+            it_me->second.insert(*it_other_set);
+        }
+      }
+    }
+  }
+}
+
+void CefValueController::Swap(void* old_value, void* new_value) {
+  DCHECK(old_value && new_value && old_value != new_value);
+
+  // Controller should already be locked.
+  DCHECK(locked());
+
+  if (owner_value_ == old_value)
+    owner_value_ = new_value;
+
+  if (!reference_map_.empty()) {
+    ReferenceMap::iterator it = reference_map_.find(old_value);
+    if (it != reference_map_.end()) {
+      // References should only be added once.
+      DCHECK(reference_map_.find(new_value) == reference_map_.end());
+      reference_map_.insert(std::make_pair(new_value, it->second));
+      reference_map_.erase(it);
+    }
+  }
+
+  if (!dependency_map_.empty()) {
+    DependencyMap::iterator it = dependency_map_.find(old_value);
+    if (it != dependency_map_.end()) {
+      dependency_map_.insert(std::make_pair(new_value, it->second));
+      dependency_map_.erase(it);
+    }
+
+    it = dependency_map_.begin();
+    for (; it != dependency_map_.end(); ++it) {
+      DependencySet::iterator dit = it->second.find(old_value);
+      if (dit != it->second.end()) {
+        it->second.insert(new_value);
+        it->second.erase(dit);
+      }
+    }
+  }
+}
diff --git a/src/libcef/common/value_base.h b/src/libcef/common/value_base.h
new file mode 100644
index 0000000..3fd1c92
--- /dev/null
+++ b/src/libcef/common/value_base.h
@@ -0,0 +1,423 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_VALUE_BASE_H_
+#define CEF_LIBCEF_COMMON_VALUE_BASE_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include "include/cef_base.h"
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "base/threading/platform_thread.h"
+
+// Controller implementation base class.
+class CefValueController
+    : public base::RefCountedThreadSafe<CefValueController> {
+ public:
+  // Implemented by a class controlled using the access controller.
+  class Object {
+   public:
+    virtual ~Object() {}
+
+    // Called when the value has been removed.
+    virtual void OnControlRemoved() = 0;
+  };
+
+  // Encapsulates context locking and verification logic.
+  class AutoLock {
+   public:
+    explicit AutoLock(CefValueController* impl)
+        : impl_(impl), verified_(impl && impl->VerifyThread()) {
+      DCHECK(impl);
+      if (verified_)
+        impl_->lock();
+    }
+    ~AutoLock() {
+      if (verified_)
+        impl_->unlock();
+    }
+
+    inline bool verified() { return verified_; }
+
+   private:
+    scoped_refptr<CefValueController> impl_;
+    bool verified_;
+
+    DISALLOW_COPY_AND_ASSIGN(AutoLock);
+  };
+
+  CefValueController();
+
+  // Returns true if this controller is thread safe.
+  virtual bool thread_safe() = 0;
+
+  // Returns true if the current thread is allowed to access this controller.
+  virtual bool on_correct_thread() = 0;
+
+  // Lock the controller.
+  virtual void lock() = 0;
+
+  // Unlock the controller.
+  virtual void unlock() = 0;
+
+  // Returns true if the controller is locked on the current thread.
+  virtual bool locked() = 0;
+
+  // Assert that the lock has been acquired.
+  virtual void AssertLockAcquired() = 0;
+
+  // Verify that the current thread is correct for accessing the controller.
+  inline bool VerifyThread() {
+    if (!thread_safe() && !on_correct_thread()) {
+      // This object should only be accessed from the thread that created it.
+      NOTREACHED() << "object accessed from incorrect thread.";
+      return false;
+    }
+    return true;
+  }
+
+  // The controller must already be locked before calling the below methods.
+
+  // Set the owner for this controller.
+  void SetOwner(void* value, Object* object);
+
+  // Add a reference value and associated object.
+  void AddReference(void* value, Object* object);
+
+  // Remove the value. If |notify_object| is true the removed object will be
+  // notified. If |value| is the owner all reference objects will be removed.
+  // If |value| has dependencies those objects will also be removed.
+  void Remove(void* value, bool notify_object);
+
+  // Returns the object for the specified value.
+  Object* Get(void* value);
+
+  // Add a dependency between |parent| and |child|.
+  void AddDependency(void* parent, void* child);
+
+  // Recursively removes any dependent values.
+  void RemoveDependencies(void* value);
+
+  // Takes ownership of all references and dependencies currently controlled by
+  // |other|. The |other| controller must already be locked.
+  void TakeFrom(CefValueController* other);
+
+  // Replace all instances of |old_value| with |new_value|. Used in cases where
+  // move semantics may move the contents of an object without retaining the
+  // object pointer itself.
+  void Swap(void* old_value, void* new_value);
+
+ protected:
+  friend class base::RefCountedThreadSafe<CefValueController>;
+
+  virtual ~CefValueController();
+
+ private:
+  // Owner object.
+  void* owner_value_;
+  Object* owner_object_;
+
+  // Map of reference objects.
+  typedef std::map<void*, Object*> ReferenceMap;
+  ReferenceMap reference_map_;
+
+  // Map of dependency objects.
+  typedef std::set<void*> DependencySet;
+  typedef std::map<void*, DependencySet> DependencyMap;
+  DependencyMap dependency_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefValueController);
+};
+
+// Thread-safe access control implementation.
+class CefValueControllerThreadSafe : public CefValueController {
+ public:
+  explicit CefValueControllerThreadSafe() : locked_thread_id_(0) {}
+
+  // CefValueController methods.
+  bool thread_safe() override { return true; }
+  bool on_correct_thread() override { return true; }
+  void lock() override NO_THREAD_SAFETY_ANALYSIS {
+    lock_.Acquire();
+    locked_thread_id_ = base::PlatformThread::CurrentId();
+  }
+  void unlock() override NO_THREAD_SAFETY_ANALYSIS {
+    locked_thread_id_ = 0;
+    lock_.Release();
+  }
+  bool locked() override {
+    return (locked_thread_id_ == base::PlatformThread::CurrentId());
+  }
+  void AssertLockAcquired() override { lock_.AssertAcquired(); }
+
+ private:
+  base::Lock lock_;
+  base::PlatformThreadId locked_thread_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefValueControllerThreadSafe);
+};
+
+// Non-thread-safe access control implementation.
+class CefValueControllerNonThreadSafe : public CefValueController {
+ public:
+  explicit CefValueControllerNonThreadSafe()
+      : thread_id_(base::PlatformThread::CurrentId()) {}
+
+  // CefValueController methods.
+  bool thread_safe() override { return false; }
+  bool on_correct_thread() override {
+    return (thread_id_ == base::PlatformThread::CurrentId());
+  }
+  void lock() override {}
+  void unlock() override {}
+  bool locked() override { return on_correct_thread(); }
+  void AssertLockAcquired() override { DCHECK(locked()); }
+
+ private:
+  base::PlatformThreadId thread_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefValueControllerNonThreadSafe);
+};
+
+// Helper macros for verifying context.
+
+#define CEF_VALUE_VERIFY_RETURN_VOID_EX(object, modify) \
+  if (!VerifyAttached())                                \
+    return;                                             \
+  AutoLock auto_lock(object, modify);                   \
+  if (!auto_lock.verified())                            \
+    return;
+
+#define CEF_VALUE_VERIFY_RETURN_VOID(modify) \
+  CEF_VALUE_VERIFY_RETURN_VOID_EX(this, modify)
+
+#define CEF_VALUE_VERIFY_RETURN_EX(object, modify, error_val) \
+  if (!VerifyAttached())                                      \
+    return error_val;                                         \
+  AutoLock auto_lock(object, modify);                         \
+  if (!auto_lock.verified())                                  \
+    return error_val;
+
+#define CEF_VALUE_VERIFY_RETURN(modify, error_val) \
+  CEF_VALUE_VERIFY_RETURN_EX(this, modify, error_val)
+
+// Template class for implementing CEF wrappers of other types.
+template <class CefType, class ValueType>
+class CefValueBase : public CefType, public CefValueController::Object {
+ public:
+  // Specifies how the value will be used.
+  enum ValueMode {
+    // A reference to a value managed by an existing controller. These values
+    // can be safely detached but ownership should not be transferred (make a
+    // copy of the value instead).
+    kReference,
+
+    // The value has its own controller and will be deleted on destruction.
+    // These values can only be detached to another controller otherwise any
+    // references will not be properly managed.
+    kOwnerWillDelete,
+
+    // The value has its own controller and will not be deleted on destruction.
+    // This should only be used for global values or scope-limited values that
+    // will be explicitly detached.
+    kOwnerNoDelete,
+  };
+
+  // Create a new object.
+  // If |read_only| is true mutable access will not be allowed.
+  // If |parent_value| is non-NULL and the value mode is kReference a dependency
+  // will be added.
+  CefValueBase(ValueType* value,
+               void* parent_value,
+               ValueMode value_mode,
+               bool read_only,
+               CefValueController* controller)
+      : value_(value),
+        value_mode_(value_mode),
+        read_only_(read_only),
+        controller_(controller) {
+    DCHECK(value_);
+
+    // Specifying a parent value for a non-reference doesn't make sense.
+    DCHECK(!(!reference() && parent_value));
+
+    if (!reference() && !controller_.get()) {
+      // For owned values default to using a new multi-threaded controller.
+      controller_ = new CefValueControllerThreadSafe();
+      SetOwnsController();
+    }
+
+    // A controller is required.
+    DCHECK(controller_.get());
+
+    if (reference()) {
+      // Register the reference with the controller.
+      controller_->AddReference(value_, this);
+
+      // Add a dependency on the parent value.
+      if (parent_value)
+        controller_->AddDependency(parent_value, value_);
+    }
+  }
+
+  ~CefValueBase() override {
+    if (controller_.get() && value_)
+      Delete();
+  }
+
+  // True if the underlying value is referenced instead of owned.
+  inline bool reference() const { return (value_mode_ == kReference); }
+
+  // True if the underlying value will be deleted.
+  inline bool will_delete() const { return (value_mode_ == kOwnerWillDelete); }
+
+  // True if access to the underlying value is read-only.
+  inline bool read_only() const { return read_only_; }
+
+  // True if the underlying value has been detached.
+  inline bool detached() { return !controller_.get(); }
+
+  // Returns the controller.
+  inline CefValueController* controller() { return controller_.get(); }
+
+  // Deletes the underlying value.
+  void Delete() {
+    CEF_VALUE_VERIFY_RETURN_VOID(false);
+
+    // Remove the object from the controller. If this is the owner object any
+    // references will be detached.
+    controller()->Remove(value_, false);
+
+    if (will_delete()) {
+      // Remove any dependencies.
+      controller()->RemoveDependencies(value_);
+
+      // Delete the value.
+      DeleteValue(value_);
+    }
+
+    controller_ = nullptr;
+    value_ = nullptr;
+  }
+
+  // Detaches the underlying value and returns a pointer to it. If this is an
+  // owner and a |new_controller| value is specified any existing references
+  // will be passed to the new controller.
+  ValueType* Detach(CefValueController* new_controller) {
+    CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+    // A |new_controller| value is required for mode kOwnerWillDelete.
+    DCHECK(!will_delete() || new_controller);
+
+    if (new_controller && !reference()) {
+      // Pass any existing references and dependencies to the new controller.
+      // They will be removed from this controller.
+      new_controller->TakeFrom(controller());
+    }
+
+    // Remove the object from the controller. If this is the owner object any
+    // references will be detached.
+    controller()->Remove(value_, false);
+    controller_ = nullptr;
+
+    // Return the old value.
+    ValueType* old_val = value_;
+    value_ = nullptr;
+    return old_val;
+  }
+
+  // Verify that the value is attached.
+  inline bool VerifyAttached() {
+    if (detached()) {
+      // This object should not be accessed after being detached.
+      NOTREACHED() << "object accessed after being detached.";
+      return false;
+    }
+    return true;
+  }
+
+ protected:
+  // CefValueController::Object methods.
+  void OnControlRemoved() override {
+    DCHECK(controller()->locked());
+
+    // Only references should be removed in this manner.
+    DCHECK(reference());
+
+    controller_ = nullptr;
+    value_ = nullptr;
+  }
+
+  // Override to customize value deletion.
+  virtual void DeleteValue(ValueType* value) { delete value; }
+
+  // Returns a mutable reference to the value.
+  inline ValueType* mutable_value() {
+    DCHECK(value_);
+    DCHECK(!read_only_);
+    DCHECK(controller()->locked());
+    return value_;
+  }
+  // Returns a const reference to the value.
+  inline const ValueType& const_value() {
+    DCHECK(value_);
+    DCHECK(controller()->locked());
+    return *value_;
+  }
+
+  // Verify that the value can be accessed.
+  inline bool VerifyAccess(bool modify) {
+    // The controller must already be locked.
+    DCHECK(controller()->locked());
+
+    if (read_only() && modify) {
+      // This object cannot be modified.
+      NOTREACHED() << "mutation attempted on read-only object.";
+      return false;
+    }
+
+    return true;
+  }
+
+  // Used to indicate that this object owns the controller.
+  inline void SetOwnsController() {
+    CefValueController::AutoLock lock_scope(controller_.get());
+    if (lock_scope.verified())
+      controller_->SetOwner(value_, this);
+  }
+
+  // Encapsulates value locking and verification logic.
+  class AutoLock {
+   public:
+    explicit AutoLock(CefValueBase* impl, bool modify)
+        : auto_lock_(impl->controller()) {
+      verified_ = (auto_lock_.verified() && impl->VerifyAccess(modify));
+    }
+
+    inline bool verified() { return verified_; }
+
+   private:
+    CefValueController::AutoLock auto_lock_;
+    bool verified_;
+
+    DISALLOW_COPY_AND_ASSIGN(AutoLock);
+  };
+
+ private:
+  ValueType* value_;
+  ValueMode value_mode_;
+  bool read_only_;
+  scoped_refptr<CefValueController> controller_;
+
+  IMPLEMENT_REFCOUNTING(CefValueBase);
+
+  DISALLOW_COPY_AND_ASSIGN(CefValueBase);
+};
+
+#endif  // CEF_LIBCEF_COMMON_VALUE_BASE_H_
diff --git a/src/libcef/common/values_impl.cc b/src/libcef/common/values_impl.cc
new file mode 100644
index 0000000..5884116
--- /dev/null
+++ b/src/libcef/common/values_impl.cc
@@ -0,0 +1,1411 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/common/values_impl.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+
+// CefValueImpl implementation.
+
+// static
+CefRefPtr<CefValue> CefValue::Create() {
+  return new CefValueImpl(new base::Value());
+}
+
+// static
+CefRefPtr<CefValue> CefValueImpl::GetOrCreateRefOrCopy(
+    base::Value* value,
+    void* parent_value,
+    bool read_only,
+    CefValueController* controller) {
+  DCHECK(value);
+
+  if (value->is_blob()) {
+    return new CefValueImpl(
+        CefBinaryValueImpl::GetOrCreateRef(value, parent_value, controller));
+  }
+
+  if (value->is_dict()) {
+    base::DictionaryValue* dict_value =
+        static_cast<base::DictionaryValue*>(value);
+    return new CefValueImpl(CefDictionaryValueImpl::GetOrCreateRef(
+        dict_value, parent_value, read_only, controller));
+  }
+
+  if (value->is_list()) {
+    base::ListValue* list_value = static_cast<base::ListValue*>(value);
+    return new CefValueImpl(CefListValueImpl::GetOrCreateRef(
+        list_value, parent_value, read_only, controller));
+  }
+
+  return new CefValueImpl(value->DeepCopy());
+}
+
+CefValueImpl::CefValueImpl() {}
+
+CefValueImpl::CefValueImpl(base::Value* value) {
+  SetValue(value);
+}
+
+CefValueImpl::CefValueImpl(CefRefPtr<CefBinaryValue> value)
+    : binary_value_(value) {}
+
+CefValueImpl::CefValueImpl(CefRefPtr<CefDictionaryValue> value)
+    : dictionary_value_(value) {}
+
+CefValueImpl::CefValueImpl(CefRefPtr<CefListValue> value)
+    : list_value_(value) {}
+
+CefValueImpl::~CefValueImpl() {}
+
+void CefValueImpl::SetValue(base::Value* value) {
+  base::AutoLock lock_scope(lock_);
+  SetValueInternal(value);
+}
+
+base::Value* CefValueImpl::CopyOrDetachValue(
+    CefValueController* new_controller) {
+  base::AutoLock lock_scope(lock_);
+
+  if (binary_value_) {
+    return static_cast<CefBinaryValueImpl*>(binary_value_.get())
+        ->CopyOrDetachValue(new_controller);
+  }
+
+  if (dictionary_value_) {
+    return static_cast<CefDictionaryValueImpl*>(dictionary_value_.get())
+        ->CopyOrDetachValue(new_controller);
+  }
+
+  if (list_value_) {
+    return static_cast<CefListValueImpl*>(list_value_.get())
+        ->CopyOrDetachValue(new_controller);
+  }
+
+  return value_->DeepCopy();
+}
+
+void CefValueImpl::SwapValue(base::Value* new_value,
+                             void* new_parent_value,
+                             CefValueController* new_controller) {
+  base::AutoLock lock_scope(lock_);
+
+  if (binary_value_) {
+    binary_value_ = CefBinaryValueImpl::GetOrCreateRef(
+        new_value, new_parent_value, new_controller);
+  } else if (dictionary_value_) {
+    dictionary_value_ = CefDictionaryValueImpl::GetOrCreateRef(
+        static_cast<base::DictionaryValue*>(new_value), new_parent_value, false,
+        new_controller);
+  } else if (list_value_) {
+    list_value_ = CefListValueImpl::GetOrCreateRef(
+        static_cast<base::ListValue*>(new_value), new_parent_value, false,
+        new_controller);
+  }
+}
+
+bool CefValueImpl::IsValid() {
+  base::AutoLock lock_scope(lock_);
+
+  if (binary_value_)
+    return binary_value_->IsValid();
+  if (dictionary_value_)
+    return dictionary_value_->IsValid();
+  if (list_value_)
+    return list_value_->IsValid();
+
+  return (value_ != nullptr);
+}
+
+bool CefValueImpl::IsOwned() {
+  base::AutoLock lock_scope(lock_);
+
+  if (binary_value_)
+    return binary_value_->IsOwned();
+  if (dictionary_value_)
+    return dictionary_value_->IsOwned();
+  if (list_value_)
+    return list_value_->IsOwned();
+
+  return false;
+}
+
+bool CefValueImpl::IsReadOnly() {
+  base::AutoLock lock_scope(lock_);
+
+  if (binary_value_)
+    return true;
+  if (dictionary_value_)
+    return dictionary_value_->IsReadOnly();
+  if (list_value_)
+    return list_value_->IsReadOnly();
+
+  return false;
+}
+
+bool CefValueImpl::IsSame(CefRefPtr<CefValue> that) {
+  if (that.get() == this)
+    return true;
+  if (!that.get() || that->GetType() != GetType())
+    return false;
+
+  CefValueImpl* impl = static_cast<CefValueImpl*>(that.get());
+
+  base::AutoLock lock_scope(lock_);
+  base::AutoLock lock_scope2(impl->lock_);
+
+  if (binary_value_)
+    return binary_value_->IsSame(impl->binary_value_);
+  if (dictionary_value_)
+    return dictionary_value_->IsSame(impl->dictionary_value_);
+  if (list_value_)
+    return list_value_->IsSame(impl->list_value_);
+
+  // Simple types are never the same.
+  return false;
+}
+
+bool CefValueImpl::IsEqual(CefRefPtr<CefValue> that) {
+  if (that.get() == this)
+    return true;
+  if (!that.get() || that->GetType() != GetType())
+    return false;
+
+  CefValueImpl* impl = static_cast<CefValueImpl*>(that.get());
+
+  base::AutoLock lock_scope(lock_);
+  base::AutoLock lock_scope2(impl->lock_);
+
+  if (binary_value_)
+    return binary_value_->IsEqual(impl->binary_value_);
+  if (dictionary_value_)
+    return dictionary_value_->IsEqual(impl->dictionary_value_);
+  if (list_value_)
+    return list_value_->IsEqual(impl->list_value_);
+
+  if (!value_)  // Invalid types are equal.
+    return true;
+
+  return value_->Equals(impl->value_.get());
+}
+
+CefRefPtr<CefValue> CefValueImpl::Copy() {
+  base::AutoLock lock_scope(lock_);
+
+  if (binary_value_)
+    return new CefValueImpl(binary_value_->Copy());
+  if (dictionary_value_)
+    return new CefValueImpl(dictionary_value_->Copy(false));
+  if (list_value_)
+    return new CefValueImpl(list_value_->Copy());
+  if (value_)
+    return new CefValueImpl(value_->DeepCopy());
+
+  return new CefValueImpl();
+}
+
+CefValueType CefValueImpl::GetType() {
+  base::AutoLock lock_scope(lock_);
+
+  if (binary_value_)
+    return VTYPE_BINARY;
+  if (dictionary_value_)
+    return VTYPE_DICTIONARY;
+  if (list_value_)
+    return VTYPE_LIST;
+
+  if (value_) {
+    switch (value_->type()) {
+      case base::Value::Type::NONE:
+        return VTYPE_NULL;
+      case base::Value::Type::BOOLEAN:
+        return VTYPE_BOOL;
+      case base::Value::Type::INTEGER:
+        return VTYPE_INT;
+      case base::Value::Type::DOUBLE:
+        return VTYPE_DOUBLE;
+      case base::Value::Type::STRING:
+        return VTYPE_STRING;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  return VTYPE_INVALID;
+}
+
+bool CefValueImpl::GetBool() {
+  base::AutoLock lock_scope(lock_);
+
+  bool ret_value = false;
+  if (value_)
+    value_->GetAsBoolean(&ret_value);
+  return ret_value;
+}
+
+int CefValueImpl::GetInt() {
+  base::AutoLock lock_scope(lock_);
+
+  int ret_value = 0;
+  if (value_)
+    value_->GetAsInteger(&ret_value);
+  return ret_value;
+}
+
+double CefValueImpl::GetDouble() {
+  base::AutoLock lock_scope(lock_);
+
+  double ret_value = 0;
+  if (value_)
+    value_->GetAsDouble(&ret_value);
+  return ret_value;
+}
+
+CefString CefValueImpl::GetString() {
+  base::AutoLock lock_scope(lock_);
+
+  std::string ret_value;
+  if (value_)
+    value_->GetAsString(&ret_value);
+  return ret_value;
+}
+
+CefRefPtr<CefBinaryValue> CefValueImpl::GetBinary() {
+  base::AutoLock lock_scope(lock_);
+  return binary_value_;
+}
+
+CefRefPtr<CefDictionaryValue> CefValueImpl::GetDictionary() {
+  base::AutoLock lock_scope(lock_);
+  return dictionary_value_;
+}
+
+CefRefPtr<CefListValue> CefValueImpl::GetList() {
+  base::AutoLock lock_scope(lock_);
+  return list_value_;
+}
+
+bool CefValueImpl::SetNull() {
+  SetValue(new base::Value());
+  return true;
+}
+
+bool CefValueImpl::SetBool(bool value) {
+  SetValue(new base::Value(value));
+  return true;
+}
+
+bool CefValueImpl::SetInt(int value) {
+  SetValue(new base::Value(value));
+  return true;
+}
+
+bool CefValueImpl::SetDouble(double value) {
+  SetValue(new base::Value(value));
+  return true;
+}
+
+bool CefValueImpl::SetString(const CefString& value) {
+  SetValue(new base::Value(value.ToString()));
+  return true;
+}
+
+bool CefValueImpl::SetBinary(CefRefPtr<CefBinaryValue> value) {
+  base::AutoLock lock_scope(lock_);
+  SetValueInternal(nullptr);
+  binary_value_ = value;
+  return true;
+}
+
+bool CefValueImpl::SetDictionary(CefRefPtr<CefDictionaryValue> value) {
+  base::AutoLock lock_scope(lock_);
+  SetValueInternal(nullptr);
+  dictionary_value_ = value;
+  return true;
+}
+
+bool CefValueImpl::SetList(CefRefPtr<CefListValue> value) {
+  base::AutoLock lock_scope(lock_);
+  SetValueInternal(nullptr);
+  list_value_ = value;
+  return true;
+}
+
+void CefValueImpl::SetValueInternal(base::Value* value) {
+  lock_.AssertAcquired();
+
+  value_.reset(nullptr);
+  binary_value_ = nullptr;
+  dictionary_value_ = nullptr;
+  list_value_ = nullptr;
+
+  if (value) {
+    switch (value->type()) {
+      case base::Value::Type::BINARY:
+        binary_value_ = new CefBinaryValueImpl(value, true);
+        return;
+      case base::Value::Type::DICTIONARY:
+        dictionary_value_ = new CefDictionaryValueImpl(
+            static_cast<base::DictionaryValue*>(value), true, false);
+        return;
+      case base::Value::Type::LIST:
+        list_value_ = new CefListValueImpl(static_cast<base::ListValue*>(value),
+                                           true, false);
+        return;
+      default:
+        value_.reset(value);
+    }
+  }
+}
+
+CefValueController* CefValueImpl::GetValueController() const {
+  lock_.AssertAcquired();
+
+  if (binary_value_) {
+    return static_cast<CefBinaryValueImpl*>(binary_value_.get())->controller();
+  } else if (dictionary_value_) {
+    return static_cast<CefDictionaryValueImpl*>(dictionary_value_.get())
+        ->controller();
+  } else if (list_value_) {
+    return static_cast<CefListValueImpl*>(list_value_.get())->controller();
+  }
+
+  return nullptr;
+}
+
+void CefValueImpl::AcquireLock() NO_THREAD_SAFETY_ANALYSIS {
+  lock_.Acquire();
+
+  CefValueController* controller = GetValueController();
+  if (controller)
+    controller->lock();
+}
+
+void CefValueImpl::ReleaseLock() NO_THREAD_SAFETY_ANALYSIS {
+  CefValueController* controller = GetValueController();
+  if (controller) {
+    controller->AssertLockAcquired();
+    controller->unlock();
+  }
+
+  lock_.Release();
+}
+
+base::Value* CefValueImpl::GetValueUnsafe() const {
+  lock_.AssertAcquired();
+
+  if (binary_value_) {
+    return static_cast<CefBinaryValueImpl*>(binary_value_.get())
+        ->GetValueUnsafe();
+  } else if (dictionary_value_) {
+    return static_cast<CefDictionaryValueImpl*>(dictionary_value_.get())
+        ->GetValueUnsafe();
+  } else if (list_value_) {
+    return static_cast<CefListValueImpl*>(list_value_.get())->GetValueUnsafe();
+  }
+
+  return value_.get();
+}
+
+// CefBinaryValueImpl implementation.
+
+CefRefPtr<CefBinaryValue> CefBinaryValue::Create(const void* data,
+                                                 size_t data_size) {
+  DCHECK(data);
+  DCHECK_GT(data_size, (size_t)0);
+  if (!data || data_size == 0)
+    return nullptr;
+
+  return new CefBinaryValueImpl(static_cast<char*>(const_cast<void*>(data)),
+                                data_size);
+}
+
+// static
+CefRefPtr<CefBinaryValue> CefBinaryValueImpl::GetOrCreateRef(
+    base::Value* value,
+    void* parent_value,
+    CefValueController* controller) {
+  DCHECK(value);
+  DCHECK(parent_value);
+  DCHECK(controller);
+
+  CefValueController::Object* object = controller->Get(value);
+  if (object)
+    return static_cast<CefBinaryValueImpl*>(object);
+
+  return new CefBinaryValueImpl(value, parent_value,
+                                CefBinaryValueImpl::kReference, controller);
+}
+
+CefBinaryValueImpl::CefBinaryValueImpl(base::Value* value, bool will_delete)
+    : CefValueBase<CefBinaryValue, base::Value>(
+          value,
+          nullptr,
+          will_delete ? kOwnerWillDelete : kOwnerNoDelete,
+          true,
+          nullptr) {}
+
+CefBinaryValueImpl::CefBinaryValueImpl(char* data, size_t data_size)
+    : CefValueBase<CefBinaryValue, base::Value>(
+          new base::Value(std::vector<char>(data, data + data_size)),
+          nullptr,
+          kOwnerWillDelete,
+          true,
+          nullptr) {}
+
+base::Value* CefBinaryValueImpl::CopyValue() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  return const_value().DeepCopy();
+}
+
+base::Value* CefBinaryValueImpl::CopyOrDetachValue(
+    CefValueController* new_controller) {
+  base::Value* new_value;
+
+  if (!will_delete()) {
+    // Copy the value.
+    new_value = CopyValue();
+  } else {
+    // Take ownership of the value.
+    new_value = Detach(new_controller);
+  }
+
+  DCHECK(new_value);
+  return new_value;
+}
+
+bool CefBinaryValueImpl::IsSameValue(const base::Value* that) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return (&const_value() == that);
+}
+
+bool CefBinaryValueImpl::IsEqualValue(const base::Value* that) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().Equals(that);
+}
+
+base::Value* CefBinaryValueImpl::GetValueUnsafe() {
+  if (!VerifyAttached())
+    return nullptr;
+  controller()->AssertLockAcquired();
+  return const_cast<base::Value*>(&const_value());
+}
+
+bool CefBinaryValueImpl::IsValid() {
+  return !detached();
+}
+
+bool CefBinaryValueImpl::IsOwned() {
+  return !will_delete();
+}
+
+bool CefBinaryValueImpl::IsSame(CefRefPtr<CefBinaryValue> that) {
+  if (!that.get())
+    return false;
+  if (that.get() == this)
+    return true;
+
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return static_cast<CefBinaryValueImpl*>(that.get())
+      ->IsSameValue(&const_value());
+}
+
+bool CefBinaryValueImpl::IsEqual(CefRefPtr<CefBinaryValue> that) {
+  if (!that.get())
+    return false;
+  if (that.get() == this)
+    return true;
+
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return static_cast<CefBinaryValueImpl*>(that.get())
+      ->IsEqualValue(&const_value());
+}
+
+CefRefPtr<CefBinaryValue> CefBinaryValueImpl::Copy() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  return new CefBinaryValueImpl(const_value().DeepCopy(), nullptr,
+                                CefBinaryValueImpl::kOwnerWillDelete, nullptr);
+}
+
+size_t CefBinaryValueImpl::GetSize() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().GetBlob().size();
+}
+
+size_t CefBinaryValueImpl::GetData(void* buffer,
+                                   size_t buffer_size,
+                                   size_t data_offset) {
+  DCHECK(buffer);
+  DCHECK_GT(buffer_size, (size_t)0);
+  if (!buffer || buffer_size == 0)
+    return 0;
+
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+
+  size_t size = const_value().GetBlob().size();
+  DCHECK_LT(data_offset, size);
+  if (data_offset >= size)
+    return 0;
+
+  size = std::min(buffer_size, size - data_offset);
+  auto* data = const_value().GetBlob().data();
+  memcpy(buffer, data + data_offset, size);
+  return size;
+}
+
+CefBinaryValueImpl::CefBinaryValueImpl(base::Value* value,
+                                       void* parent_value,
+                                       ValueMode value_mode,
+                                       CefValueController* controller)
+    : CefValueBase<CefBinaryValue, base::Value>(value,
+                                                parent_value,
+                                                value_mode,
+                                                true,
+                                                controller) {}
+
+// CefDictionaryValueImpl implementation.
+
+// static
+CefRefPtr<CefDictionaryValue> CefDictionaryValue::Create() {
+  return new CefDictionaryValueImpl(new base::DictionaryValue(), true, false);
+}
+
+// static
+CefRefPtr<CefDictionaryValue> CefDictionaryValueImpl::GetOrCreateRef(
+    base::DictionaryValue* value,
+    void* parent_value,
+    bool read_only,
+    CefValueController* controller) {
+  CefValueController::Object* object = controller->Get(value);
+  if (object)
+    return static_cast<CefDictionaryValueImpl*>(object);
+
+  return new CefDictionaryValueImpl(value, parent_value,
+                                    CefDictionaryValueImpl::kReference,
+                                    read_only, controller);
+}
+
+CefDictionaryValueImpl::CefDictionaryValueImpl(base::DictionaryValue* value,
+                                               bool will_delete,
+                                               bool read_only)
+    : CefValueBase<CefDictionaryValue, base::DictionaryValue>(
+          value,
+          nullptr,
+          will_delete ? kOwnerWillDelete : kOwnerNoDelete,
+          read_only,
+          nullptr) {}
+
+base::DictionaryValue* CefDictionaryValueImpl::CopyValue() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  return const_value().DeepCopy();
+}
+
+base::DictionaryValue* CefDictionaryValueImpl::CopyOrDetachValue(
+    CefValueController* new_controller) {
+  base::DictionaryValue* new_value;
+
+  if (!will_delete()) {
+    // Copy the value.
+    new_value = CopyValue();
+  } else {
+    // Take ownership of the value.
+    new_value = Detach(new_controller);
+  }
+
+  DCHECK(new_value);
+  return new_value;
+}
+
+bool CefDictionaryValueImpl::IsSameValue(const base::DictionaryValue* that) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return (&const_value() == that);
+}
+
+bool CefDictionaryValueImpl::IsEqualValue(const base::DictionaryValue* that) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().Equals(that);
+}
+
+base::DictionaryValue* CefDictionaryValueImpl::GetValueUnsafe() {
+  if (!VerifyAttached())
+    return nullptr;
+  controller()->AssertLockAcquired();
+  return const_cast<base::DictionaryValue*>(&const_value());
+}
+
+bool CefDictionaryValueImpl::IsValid() {
+  return !detached();
+}
+
+bool CefDictionaryValueImpl::IsOwned() {
+  return !will_delete();
+}
+
+bool CefDictionaryValueImpl::IsReadOnly() {
+  return read_only();
+}
+
+bool CefDictionaryValueImpl::IsSame(CefRefPtr<CefDictionaryValue> that) {
+  if (!that.get())
+    return false;
+  if (that.get() == this)
+    return true;
+
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return static_cast<CefDictionaryValueImpl*>(that.get())
+      ->IsSameValue(&const_value());
+}
+
+bool CefDictionaryValueImpl::IsEqual(CefRefPtr<CefDictionaryValue> that) {
+  if (!that.get())
+    return false;
+  if (that.get() == this)
+    return true;
+
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return static_cast<CefDictionaryValueImpl*>(that.get())
+      ->IsEqualValue(&const_value());
+}
+
+CefRefPtr<CefDictionaryValue> CefDictionaryValueImpl::Copy(
+    bool exclude_empty_children) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  base::DictionaryValue* value;
+  if (exclude_empty_children) {
+    value = const_cast<base::DictionaryValue&>(const_value())
+                .DeepCopyWithoutEmptyChildren()
+                .release();
+  } else {
+    value = const_value().DeepCopy();
+  }
+
+  return new CefDictionaryValueImpl(
+      value, nullptr, CefDictionaryValueImpl::kOwnerWillDelete, false, nullptr);
+}
+
+size_t CefDictionaryValueImpl::GetSize() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().size();
+}
+
+bool CefDictionaryValueImpl::Clear() {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  // Detach any dependent values.
+  controller()->RemoveDependencies(mutable_value());
+
+  mutable_value()->Clear();
+  return true;
+}
+
+bool CefDictionaryValueImpl::HasKey(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().HasKey(base::StringPiece(key));
+}
+
+bool CefDictionaryValueImpl::GetKeys(KeyList& keys) {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+
+  for (base::DictionaryValue::Iterator i(const_value()); !i.IsAtEnd();
+       i.Advance()) {
+    keys.push_back(i.key());
+  }
+
+  return true;
+}
+
+bool CefDictionaryValueImpl::Remove(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  return RemoveInternal(key);
+}
+
+CefValueType CefDictionaryValueImpl::GetType(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, VTYPE_INVALID);
+
+  const base::Value* out_value = nullptr;
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key),
+                                            &out_value)) {
+    switch (out_value->type()) {
+      case base::Value::Type::NONE:
+        return VTYPE_NULL;
+      case base::Value::Type::BOOLEAN:
+        return VTYPE_BOOL;
+      case base::Value::Type::INTEGER:
+        return VTYPE_INT;
+      case base::Value::Type::DOUBLE:
+        return VTYPE_DOUBLE;
+      case base::Value::Type::STRING:
+        return VTYPE_STRING;
+      case base::Value::Type::BINARY:
+        return VTYPE_BINARY;
+      case base::Value::Type::DICTIONARY:
+        return VTYPE_DICTIONARY;
+      case base::Value::Type::LIST:
+        return VTYPE_LIST;
+      case base::Value::Type::DEAD:
+        return VTYPE_INVALID;
+    }
+  }
+
+  return VTYPE_INVALID;
+}
+
+CefRefPtr<CefValue> CefDictionaryValueImpl::GetValue(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key),
+                                            &out_value)) {
+    return CefValueImpl::GetOrCreateRefOrCopy(
+        const_cast<base::Value*>(out_value),
+        const_cast<base::DictionaryValue*>(&const_value()), read_only(),
+        controller());
+  }
+
+  return nullptr;
+}
+
+bool CefDictionaryValueImpl::GetBool(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+
+  const base::Value* out_value = nullptr;
+  bool ret_value = false;
+
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key), &out_value))
+    out_value->GetAsBoolean(&ret_value);
+
+  return ret_value;
+}
+
+int CefDictionaryValueImpl::GetInt(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+
+  const base::Value* out_value = nullptr;
+  int ret_value = 0;
+
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key), &out_value))
+    out_value->GetAsInteger(&ret_value);
+
+  return ret_value;
+}
+
+double CefDictionaryValueImpl::GetDouble(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+
+  const base::Value* out_value = nullptr;
+  double ret_value = 0;
+
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key), &out_value))
+    out_value->GetAsDouble(&ret_value);
+
+  return ret_value;
+}
+
+CefString CefDictionaryValueImpl::GetString(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+
+  const base::Value* out_value = nullptr;
+  std::string ret_value;
+
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key), &out_value))
+    out_value->GetAsString(&ret_value);
+
+  return ret_value;
+}
+
+CefRefPtr<CefBinaryValue> CefDictionaryValueImpl::GetBinary(
+    const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key),
+                                            &out_value) &&
+      out_value->is_blob()) {
+    base::Value* binary_value = const_cast<base::Value*>(out_value);
+    return CefBinaryValueImpl::GetOrCreateRef(
+        binary_value, const_cast<base::DictionaryValue*>(&const_value()),
+        controller());
+  }
+
+  return nullptr;
+}
+
+CefRefPtr<CefDictionaryValue> CefDictionaryValueImpl::GetDictionary(
+    const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key),
+                                            &out_value) &&
+      out_value->is_dict()) {
+    base::DictionaryValue* dict_value = static_cast<base::DictionaryValue*>(
+        const_cast<base::Value*>(out_value));
+    return CefDictionaryValueImpl::GetOrCreateRef(
+        dict_value, const_cast<base::DictionaryValue*>(&const_value()),
+        read_only(), controller());
+  }
+
+  return nullptr;
+}
+
+CefRefPtr<CefListValue> CefDictionaryValueImpl::GetList(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+
+  if (const_value().GetWithoutPathExpansion(base::StringPiece(key),
+                                            &out_value) &&
+      out_value->is_list()) {
+    base::ListValue* list_value =
+        static_cast<base::ListValue*>(const_cast<base::Value*>(out_value));
+    return CefListValueImpl::GetOrCreateRef(
+        list_value, const_cast<base::DictionaryValue*>(&const_value()),
+        read_only(), controller());
+  }
+
+  return nullptr;
+}
+
+bool CefDictionaryValueImpl::SetValue(const CefString& key,
+                                      CefRefPtr<CefValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefValueImpl* impl = static_cast<CefValueImpl*>(value.get());
+  DCHECK(impl);
+
+  base::Value* new_value = impl->CopyOrDetachValue(controller());
+  base::Value* actual_value = SetInternal(key, new_value);
+  impl->SwapValue(actual_value, mutable_value(), controller());
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetNull(const CefString& key) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(key, new base::Value());
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetBool(const CefString& key, bool value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(key, new base::Value(value));
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetInt(const CefString& key, int value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(key, new base::Value(value));
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetDouble(const CefString& key, double value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(key, new base::Value(value));
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetString(const CefString& key,
+                                       const CefString& value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(key, new base::Value(value.ToString()));
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetBinary(const CefString& key,
+                                       CefRefPtr<CefBinaryValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefBinaryValueImpl* impl = static_cast<CefBinaryValueImpl*>(value.get());
+  DCHECK(impl);
+
+  SetInternal(key, impl->CopyOrDetachValue(controller()));
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetDictionary(
+    const CefString& key,
+    CefRefPtr<CefDictionaryValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefDictionaryValueImpl* impl =
+      static_cast<CefDictionaryValueImpl*>(value.get());
+  DCHECK(impl);
+
+  SetInternal(key, impl->CopyOrDetachValue(controller()));
+  return true;
+}
+
+bool CefDictionaryValueImpl::SetList(const CefString& key,
+                                     CefRefPtr<CefListValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefListValueImpl* impl = static_cast<CefListValueImpl*>(value.get());
+  DCHECK(impl);
+
+  SetInternal(key, impl->CopyOrDetachValue(controller()));
+  return true;
+}
+
+bool CefDictionaryValueImpl::RemoveInternal(const CefString& key) {
+  std::unique_ptr<base::Value> out_value;
+  if (!mutable_value()->RemoveWithoutPathExpansion(base::StringPiece(key),
+                                                   &out_value)) {
+    return false;
+  }
+
+  // Remove the value.
+  controller()->Remove(out_value.get(), true);
+
+  // Only list and dictionary types may have dependencies.
+  if (out_value->is_list() || out_value->is_dict()) {
+    controller()->RemoveDependencies(out_value.get());
+  }
+
+  return true;
+}
+
+base::Value* CefDictionaryValueImpl::SetInternal(const CefString& key,
+                                                 base::Value* value) {
+  DCHECK(value);
+  RemoveInternal(key);
+  mutable_value()->SetWithoutPathExpansion(
+      base::StringPiece(key), base::WrapUnique<base::Value>(value));
+  return value;
+}
+
+CefDictionaryValueImpl::CefDictionaryValueImpl(base::DictionaryValue* value,
+                                               void* parent_value,
+                                               ValueMode value_mode,
+                                               bool read_only,
+                                               CefValueController* controller)
+    : CefValueBase<CefDictionaryValue, base::DictionaryValue>(value,
+                                                              parent_value,
+                                                              value_mode,
+                                                              read_only,
+                                                              controller) {}
+
+// CefListValueImpl implementation.
+
+// static
+CefRefPtr<CefListValue> CefListValue::Create() {
+  return new CefListValueImpl(new base::ListValue(), true, false);
+}
+
+// static
+CefRefPtr<CefListValue> CefListValueImpl::GetOrCreateRef(
+    base::ListValue* value,
+    void* parent_value,
+    bool read_only,
+    CefValueController* controller) {
+  CefValueController::Object* object = controller->Get(value);
+  if (object)
+    return static_cast<CefListValueImpl*>(object);
+
+  return new CefListValueImpl(value, parent_value, CefListValueImpl::kReference,
+                              read_only, controller);
+}
+
+CefListValueImpl::CefListValueImpl(base::ListValue* value,
+                                   bool will_delete,
+                                   bool read_only)
+    : CefValueBase<CefListValue, base::ListValue>(
+          value,
+          nullptr,
+          will_delete ? kOwnerWillDelete : kOwnerNoDelete,
+          read_only,
+          nullptr) {}
+
+base::ListValue* CefListValueImpl::CopyValue() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+  return const_value().DeepCopy();
+}
+
+base::ListValue* CefListValueImpl::CopyOrDetachValue(
+    CefValueController* new_controller) {
+  base::ListValue* new_value;
+
+  if (!will_delete()) {
+    // Copy the value.
+    new_value = CopyValue();
+  } else {
+    // Take ownership of the value.
+    new_value = Detach(new_controller);
+  }
+
+  DCHECK(new_value);
+  return new_value;
+}
+
+bool CefListValueImpl::IsSameValue(const base::ListValue* that) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return (&const_value() == that);
+}
+
+bool CefListValueImpl::IsEqualValue(const base::ListValue* that) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return const_value().Equals(that);
+}
+
+base::ListValue* CefListValueImpl::GetValueUnsafe() {
+  if (!VerifyAttached())
+    return nullptr;
+  controller()->AssertLockAcquired();
+  return const_cast<base::ListValue*>(&const_value());
+}
+
+bool CefListValueImpl::IsValid() {
+  return !detached();
+}
+
+bool CefListValueImpl::IsOwned() {
+  return !will_delete();
+}
+
+bool CefListValueImpl::IsReadOnly() {
+  return read_only();
+}
+
+bool CefListValueImpl::IsSame(CefRefPtr<CefListValue> that) {
+  if (!that.get())
+    return false;
+  if (that.get() == this)
+    return true;
+
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return static_cast<CefListValueImpl*>(that.get())
+      ->IsSameValue(&const_value());
+}
+
+bool CefListValueImpl::IsEqual(CefRefPtr<CefListValue> that) {
+  if (!that.get())
+    return false;
+  if (that.get() == this)
+    return true;
+
+  CEF_VALUE_VERIFY_RETURN(false, false);
+  return static_cast<CefListValueImpl*>(that.get())
+      ->IsEqualValue(&const_value());
+}
+
+CefRefPtr<CefListValue> CefListValueImpl::Copy() {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  return new CefListValueImpl(const_value().DeepCopy(), nullptr,
+                              CefListValueImpl::kOwnerWillDelete, false,
+                              nullptr);
+}
+
+bool CefListValueImpl::SetSize(size_t size) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  size_t current_size = const_value().GetSize();
+  if (size < current_size) {
+    // Clean up any values above the requested size.
+    for (size_t i = current_size - 1; i >= size; --i)
+      RemoveInternal(i);
+  } else if (size > 0) {
+    // Expand the list size.
+    mutable_value()->Set(size - 1, std::make_unique<base::Value>());
+  }
+  return true;
+}
+
+size_t CefListValueImpl::GetSize() {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+  return const_value().GetSize();
+}
+
+bool CefListValueImpl::Clear() {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  // Detach any dependent values.
+  controller()->RemoveDependencies(mutable_value());
+
+  mutable_value()->Clear();
+  return true;
+}
+
+bool CefListValueImpl::Remove(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  return RemoveInternal(index);
+}
+
+CefValueType CefListValueImpl::GetType(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, VTYPE_INVALID);
+
+  const base::Value* out_value = nullptr;
+  if (const_value().Get(index, &out_value)) {
+    switch (out_value->type()) {
+      case base::Value::Type::NONE:
+        return VTYPE_NULL;
+      case base::Value::Type::BOOLEAN:
+        return VTYPE_BOOL;
+      case base::Value::Type::INTEGER:
+        return VTYPE_INT;
+      case base::Value::Type::DOUBLE:
+        return VTYPE_DOUBLE;
+      case base::Value::Type::STRING:
+        return VTYPE_STRING;
+      case base::Value::Type::BINARY:
+        return VTYPE_BINARY;
+      case base::Value::Type::DICTIONARY:
+        return VTYPE_DICTIONARY;
+      case base::Value::Type::LIST:
+        return VTYPE_LIST;
+      case base::Value::Type::DEAD:
+        return VTYPE_INVALID;
+    }
+  }
+
+  return VTYPE_INVALID;
+}
+
+CefRefPtr<CefValue> CefListValueImpl::GetValue(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+  if (const_value().Get(index, &out_value)) {
+    return CefValueImpl::GetOrCreateRefOrCopy(
+        const_cast<base::Value*>(out_value),
+        const_cast<base::ListValue*>(&const_value()), read_only(),
+        controller());
+  }
+
+  return nullptr;
+}
+
+bool CefListValueImpl::GetBool(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, false);
+
+  const base::Value* out_value = nullptr;
+  bool ret_value = false;
+
+  if (const_value().Get(index, &out_value))
+    out_value->GetAsBoolean(&ret_value);
+
+  return ret_value;
+}
+
+int CefListValueImpl::GetInt(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+
+  const base::Value* out_value = nullptr;
+  int ret_value = 0;
+
+  if (const_value().Get(index, &out_value))
+    out_value->GetAsInteger(&ret_value);
+
+  return ret_value;
+}
+
+double CefListValueImpl::GetDouble(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, 0);
+
+  const base::Value* out_value = nullptr;
+  double ret_value = 0;
+
+  if (const_value().Get(index, &out_value))
+    out_value->GetAsDouble(&ret_value);
+
+  return ret_value;
+}
+
+CefString CefListValueImpl::GetString(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, CefString());
+
+  const base::Value* out_value = nullptr;
+  std::string ret_value;
+
+  if (const_value().Get(index, &out_value))
+    out_value->GetAsString(&ret_value);
+
+  return ret_value;
+}
+
+CefRefPtr<CefBinaryValue> CefListValueImpl::GetBinary(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+
+  if (const_value().Get(index, &out_value) && out_value->is_blob()) {
+    base::Value* binary_value = const_cast<base::Value*>(out_value);
+    return CefBinaryValueImpl::GetOrCreateRef(
+        binary_value, const_cast<base::ListValue*>(&const_value()),
+        controller());
+  }
+
+  return nullptr;
+}
+
+CefRefPtr<CefDictionaryValue> CefListValueImpl::GetDictionary(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+
+  if (const_value().Get(index, &out_value) && out_value->is_dict()) {
+    base::DictionaryValue* dict_value = static_cast<base::DictionaryValue*>(
+        const_cast<base::Value*>(out_value));
+    return CefDictionaryValueImpl::GetOrCreateRef(
+        dict_value, const_cast<base::ListValue*>(&const_value()), read_only(),
+        controller());
+  }
+
+  return nullptr;
+}
+
+CefRefPtr<CefListValue> CefListValueImpl::GetList(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(false, nullptr);
+
+  const base::Value* out_value = nullptr;
+
+  if (const_value().Get(index, &out_value) && out_value->is_list()) {
+    base::ListValue* list_value =
+        static_cast<base::ListValue*>(const_cast<base::Value*>(out_value));
+    return CefListValueImpl::GetOrCreateRef(
+        list_value, const_cast<base::ListValue*>(&const_value()), read_only(),
+        controller());
+  }
+
+  return nullptr;
+}
+
+bool CefListValueImpl::SetValue(size_t index, CefRefPtr<CefValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefValueImpl* impl = static_cast<CefValueImpl*>(value.get());
+  DCHECK(impl);
+
+  base::Value* new_value = impl->CopyOrDetachValue(controller());
+  base::Value* actual_value = SetInternal(index, new_value);
+  impl->SwapValue(actual_value, mutable_value(), controller());
+  return true;
+}
+
+bool CefListValueImpl::SetNull(size_t index) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(index, new base::Value());
+  return true;
+}
+
+bool CefListValueImpl::SetBool(size_t index, bool value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(index, new base::Value(value));
+  return true;
+}
+
+bool CefListValueImpl::SetInt(size_t index, int value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(index, new base::Value(value));
+  return true;
+}
+
+bool CefListValueImpl::SetDouble(size_t index, double value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(index, new base::Value(value));
+  return true;
+}
+
+bool CefListValueImpl::SetString(size_t index, const CefString& value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+  SetInternal(index, new base::Value(value.ToString()));
+  return true;
+}
+
+bool CefListValueImpl::SetBinary(size_t index,
+                                 CefRefPtr<CefBinaryValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefBinaryValueImpl* impl = static_cast<CefBinaryValueImpl*>(value.get());
+  DCHECK(impl);
+
+  SetInternal(index, impl->CopyOrDetachValue(controller()));
+  return true;
+}
+
+bool CefListValueImpl::SetDictionary(size_t index,
+                                     CefRefPtr<CefDictionaryValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefDictionaryValueImpl* impl =
+      static_cast<CefDictionaryValueImpl*>(value.get());
+  DCHECK(impl);
+
+  SetInternal(index, impl->CopyOrDetachValue(controller()));
+  return true;
+}
+
+bool CefListValueImpl::SetList(size_t index, CefRefPtr<CefListValue> value) {
+  CEF_VALUE_VERIFY_RETURN(true, false);
+
+  CefListValueImpl* impl = static_cast<CefListValueImpl*>(value.get());
+  DCHECK(impl);
+
+  SetInternal(index, impl->CopyOrDetachValue(controller()));
+  return true;
+}
+
+bool CefListValueImpl::RemoveInternal(size_t index) {
+  // base::Value now uses move semantics which means that Remove() will return
+  // a new base::Value object with the moved contents of the base::Value that
+  // exists in the implementation std::vector. Consequently we use Get() to
+  // retrieve the actual base::Value pointer as it exists in the std::vector.
+  const base::Value* actual_value = nullptr;
+  if (!const_value().Get(index, &actual_value))
+    return false;
+  DCHECK(actual_value);
+
+  std::unique_ptr<base::Value> out_value;
+  if (!mutable_value()->Remove(index, &out_value))
+    return false;
+
+  // Remove the value.
+  controller()->Remove(const_cast<base::Value*>(actual_value), true);
+
+  // Only list and dictionary types may have dependencies.
+  if (out_value->is_list() || out_value->is_dict()) {
+    controller()->RemoveDependencies(const_cast<base::Value*>(actual_value));
+  }
+
+  return true;
+}
+
+base::Value* CefListValueImpl::SetInternal(size_t index, base::Value* value) {
+  DCHECK(value);
+
+  if (RemoveInternal(index))
+    mutable_value()->Insert(index, base::WrapUnique(value));
+  else
+    mutable_value()->Set(index, base::WrapUnique(value));
+
+  // base::Value now uses move semantics which means that Insert()/Set() will
+  // move the contents of the passed-in base::Value instead of keeping the same
+  // object. Consequently we use Get() to retrieve the actual base::Value
+  // pointer as it exists in the std::vector.
+  const base::Value* actual_value = nullptr;
+  const_value().Get(index, &actual_value);
+  DCHECK(actual_value);
+
+  // |value| will have been deleted at this point. Update the controller to
+  // reference |actual_value| instead.
+  controller()->Swap(value, const_cast<base::Value*>(actual_value));
+
+  return const_cast<base::Value*>(actual_value);
+}
+
+CefListValueImpl::CefListValueImpl(base::ListValue* value,
+                                   void* parent_value,
+                                   ValueMode value_mode,
+                                   bool read_only,
+                                   CefValueController* controller)
+    : CefValueBase<CefListValue, base::ListValue>(value,
+                                                  parent_value,
+                                                  value_mode,
+                                                  read_only,
+                                                  controller) {}
diff --git a/src/libcef/common/values_impl.h b/src/libcef/common/values_impl.h
new file mode 100644
index 0000000..d188411
--- /dev/null
+++ b/src/libcef/common/values_impl.h
@@ -0,0 +1,334 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_VALUES_IMPL_H_
+#define CEF_LIBCEF_COMMON_VALUES_IMPL_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_values.h"
+#include "libcef/common/value_base.h"
+
+#include "base/threading/platform_thread.h"
+#include "base/values.h"
+
+// CefValue implementation
+class CefValueImpl : public CefValue {
+ public:
+  // Get or create a reference to a complex value or copy a simple value.
+  static CefRefPtr<CefValue> GetOrCreateRefOrCopy(
+      base::Value* value,
+      void* parent_value,
+      bool read_only,
+      CefValueController* controller);
+
+  CefValueImpl();
+
+  // Take ownership of |value|. Do not pass in a value owned by something else
+  // (use GetOrCreateRefOrCopy instead).
+  explicit CefValueImpl(base::Value* value);
+
+  // Keep a reference to |value|.
+  explicit CefValueImpl(CefRefPtr<CefBinaryValue> value);
+  explicit CefValueImpl(CefRefPtr<CefDictionaryValue> value);
+  explicit CefValueImpl(CefRefPtr<CefListValue> value);
+
+  ~CefValueImpl() override;
+
+  // Take ownership of |value|. Do not pass in a value owned by something else
+  // (use GetOrCreateRefOrCopy or Set*() instead).
+  void SetValue(base::Value* value);
+
+  // Copy a simple value or transfer ownership of a complex value. If ownership
+  // of the value is tranferred then this object's internal reference to the
+  // value will be updated and remain valid. base::Value now uses move semantics
+  // so we need to perform the copy and swap in two steps.
+  base::Value* CopyOrDetachValue(CefValueController* new_controller);
+  void SwapValue(base::Value* new_value,
+                 void* new_parent_value,
+                 CefValueController* new_controller);
+
+  // Returns a reference to the underlying data. Access must be protected by
+  // calling AcquireLock/ReleaseLock.
+  base::Value* GetValueUnsafe() const;
+
+  // CefValue methods.
+  bool IsValid() override;
+  bool IsOwned() override;
+  bool IsReadOnly() override;
+  bool IsSame(CefRefPtr<CefValue> that) override;
+  bool IsEqual(CefRefPtr<CefValue> that) override;
+  CefRefPtr<CefValue> Copy() override;
+  CefValueType GetType() override;
+  bool GetBool() override;
+  int GetInt() override;
+  double GetDouble() override;
+  CefString GetString() override;
+  CefRefPtr<CefBinaryValue> GetBinary() override;
+  CefRefPtr<CefDictionaryValue> GetDictionary() override;
+  CefRefPtr<CefListValue> GetList() override;
+  bool SetNull() override;
+  bool SetBool(bool value) override;
+  bool SetInt(int value) override;
+  bool SetDouble(double value) override;
+  bool SetString(const CefString& value) override;
+  bool SetBinary(CefRefPtr<CefBinaryValue> value) override;
+  bool SetDictionary(CefRefPtr<CefDictionaryValue> value) override;
+  bool SetList(CefRefPtr<CefListValue> value) override;
+
+  // Ensures exclusive access to the underlying data for the life of this scoped
+  // object.
+  class ScopedLockedValue {
+   public:
+    explicit ScopedLockedValue(CefRefPtr<CefValueImpl> impl) : impl_(impl) {
+      impl_->AcquireLock();
+    }
+    ~ScopedLockedValue() { impl_->ReleaseLock(); }
+
+    base::Value* value() const { return impl_->GetValueUnsafe(); }
+
+   private:
+    CefRefPtr<CefValueImpl> impl_;
+    DISALLOW_COPY_AND_ASSIGN(ScopedLockedValue);
+  };
+
+ private:
+  void SetValueInternal(base::Value* value);
+
+  // Returns the controller for the current value, if any.
+  CefValueController* GetValueController() const;
+
+  // Explicitly lock/unlock this object and the underlying data.
+  void AcquireLock();
+  void ReleaseLock();
+
+  // Access to all members must be protected by |lock_|.
+  base::Lock lock_;
+
+  // Simple values only.
+  std::unique_ptr<base::Value> value_;
+
+  // Complex values.
+  CefRefPtr<CefBinaryValue> binary_value_;
+  CefRefPtr<CefDictionaryValue> dictionary_value_;
+  CefRefPtr<CefListValue> list_value_;
+
+  IMPLEMENT_REFCOUNTING(CefValueImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefValueImpl);
+};
+
+// CefBinaryValue implementation
+class CefBinaryValueImpl : public CefValueBase<CefBinaryValue, base::Value> {
+ public:
+  // Get or create a reference value.
+  static CefRefPtr<CefBinaryValue> GetOrCreateRef(
+      base::Value* value,
+      void* parent_value,
+      CefValueController* controller);
+
+  // Reference an existing value (set |will_delete| to false) or take ownership
+  // of an existing value (set |will_delete| to true). When referencing an
+  // existing value you must explicitly call Detach(nullptr) when |value| is no
+  // longer valid. Use GetOrCreateRef instead of this constructor if |value| is
+  // owned by some other object and you do not plan to explicitly call
+  // Detach(nullptr).
+  CefBinaryValueImpl(base::Value* value, bool will_delete);
+
+  // The data will always be copied.
+  CefBinaryValueImpl(char* data, size_t data_size);
+
+  // Return a copy of the value.
+  base::Value* CopyValue();
+
+  // If this value is a reference then return a copy. Otherwise, detach and
+  // transfer ownership of the value.
+  base::Value* CopyOrDetachValue(CefValueController* new_controller);
+
+  bool IsSameValue(const base::Value* that);
+  bool IsEqualValue(const base::Value* that);
+
+  // Returns the underlying value. Access must be protected by calling
+  // lock/unlock on the controller.
+  base::Value* GetValueUnsafe();
+
+  // CefBinaryValue methods.
+  bool IsValid() override;
+  bool IsOwned() override;
+  bool IsSame(CefRefPtr<CefBinaryValue> that) override;
+  bool IsEqual(CefRefPtr<CefBinaryValue> that) override;
+  CefRefPtr<CefBinaryValue> Copy() override;
+  size_t GetSize() override;
+  size_t GetData(void* buffer, size_t buffer_size, size_t data_offset) override;
+
+ private:
+  // See the CefValueBase constructor for usage. Binary values are always
+  // read-only.
+  CefBinaryValueImpl(base::Value* value,
+                     void* parent_value,
+                     ValueMode value_mode,
+                     CefValueController* controller);
+
+  DISALLOW_COPY_AND_ASSIGN(CefBinaryValueImpl);
+};
+
+// CefDictionaryValue implementation
+class CefDictionaryValueImpl
+    : public CefValueBase<CefDictionaryValue, base::DictionaryValue> {
+ public:
+  // Get or create a reference value.
+  static CefRefPtr<CefDictionaryValue> GetOrCreateRef(
+      base::DictionaryValue* value,
+      void* parent_value,
+      bool read_only,
+      CefValueController* controller);
+
+  // Reference an existing value (set |will_delete| to false) or take ownership
+  // of an existing value (set |will_delete| to true). When referencing an
+  // existing value you must explicitly call Detach(nullptr) when |value| is no
+  // longer valid. Use GetOrCreateRef instead of this constructor if |value| is
+  // owned by some other object and you do not plan to explicitly call
+  // Detach(nullptr).
+  CefDictionaryValueImpl(base::DictionaryValue* value,
+                         bool will_delete,
+                         bool read_only);
+
+  // Return a copy of the value.
+  base::DictionaryValue* CopyValue();
+
+  // If this value is a reference then return a copy. Otherwise, detach and
+  // transfer ownership of the value.
+  base::DictionaryValue* CopyOrDetachValue(CefValueController* new_controller);
+
+  bool IsSameValue(const base::DictionaryValue* that);
+  bool IsEqualValue(const base::DictionaryValue* that);
+
+  // Returns the underlying value. Access must be protected by calling
+  // lock/unlock on the controller.
+  base::DictionaryValue* GetValueUnsafe();
+
+  // CefDictionaryValue methods.
+  bool IsValid() override;
+  bool IsOwned() override;
+  bool IsReadOnly() override;
+  bool IsSame(CefRefPtr<CefDictionaryValue> that) override;
+  bool IsEqual(CefRefPtr<CefDictionaryValue> that) override;
+  CefRefPtr<CefDictionaryValue> Copy(bool exclude_empty_children) override;
+  size_t GetSize() override;
+  bool Clear() override;
+  bool HasKey(const CefString& key) override;
+  bool GetKeys(KeyList& keys) override;
+  bool Remove(const CefString& key) override;
+  CefValueType GetType(const CefString& key) override;
+  CefRefPtr<CefValue> GetValue(const CefString& key) override;
+  bool GetBool(const CefString& key) override;
+  int GetInt(const CefString& key) override;
+  double GetDouble(const CefString& key) override;
+  CefString GetString(const CefString& key) override;
+  CefRefPtr<CefBinaryValue> GetBinary(const CefString& key) override;
+  CefRefPtr<CefDictionaryValue> GetDictionary(const CefString& key) override;
+  CefRefPtr<CefListValue> GetList(const CefString& key) override;
+  bool SetValue(const CefString& key, CefRefPtr<CefValue> value) override;
+  bool SetNull(const CefString& key) override;
+  bool SetBool(const CefString& key, bool value) override;
+  bool SetInt(const CefString& key, int value) override;
+  bool SetDouble(const CefString& key, double value) override;
+  bool SetString(const CefString& key, const CefString& value) override;
+  bool SetBinary(const CefString& key,
+                 CefRefPtr<CefBinaryValue> value) override;
+  bool SetDictionary(const CefString& key,
+                     CefRefPtr<CefDictionaryValue> value) override;
+  bool SetList(const CefString& key, CefRefPtr<CefListValue> value) override;
+
+ private:
+  // See the CefValueBase constructor for usage.
+  CefDictionaryValueImpl(base::DictionaryValue* value,
+                         void* parent_value,
+                         ValueMode value_mode,
+                         bool read_only,
+                         CefValueController* controller);
+
+  bool RemoveInternal(const CefString& key);
+  base::Value* SetInternal(const CefString& key, base::Value* value);
+
+  DISALLOW_COPY_AND_ASSIGN(CefDictionaryValueImpl);
+};
+
+// CefListValue implementation
+class CefListValueImpl : public CefValueBase<CefListValue, base::ListValue> {
+ public:
+  // Get or create a reference value.
+  static CefRefPtr<CefListValue> GetOrCreateRef(base::ListValue* value,
+                                                void* parent_value,
+                                                bool read_only,
+                                                CefValueController* controller);
+
+  // Reference an existing value (set |will_delete| to false) or take ownership
+  // of an existing value (set |will_delete| to true). When referencing an
+  // existing value you must explicitly call Detach(nullptr) when |value| is no
+  // longer valid. Use GetOrCreateRef instead of this constructor if |value| is
+  // owned by some other object and you do not plan to explicitly call
+  // Detach(nullptr).
+  CefListValueImpl(base::ListValue* value, bool will_delete, bool read_only);
+
+  // Return a copy of the value.
+  base::ListValue* CopyValue();
+
+  // If this value is a reference then return a copy. Otherwise, detach and
+  // transfer ownership of the value.
+  base::ListValue* CopyOrDetachValue(CefValueController* new_controller);
+
+  bool IsSameValue(const base::ListValue* that);
+  bool IsEqualValue(const base::ListValue* that);
+
+  // Returns the underlying value. Access must be protected by calling
+  // lock/unlock on the controller.
+  base::ListValue* GetValueUnsafe();
+
+  // CefListValue methods.
+  bool IsValid() override;
+  bool IsOwned() override;
+  bool IsReadOnly() override;
+  bool IsSame(CefRefPtr<CefListValue> that) override;
+  bool IsEqual(CefRefPtr<CefListValue> that) override;
+  CefRefPtr<CefListValue> Copy() override;
+  bool SetSize(size_t size) override;
+  size_t GetSize() override;
+  bool Clear() override;
+  bool Remove(size_t index) override;
+  CefValueType GetType(size_t index) override;
+  CefRefPtr<CefValue> GetValue(size_t index) override;
+  bool GetBool(size_t index) override;
+  int GetInt(size_t index) override;
+  double GetDouble(size_t index) override;
+  CefString GetString(size_t index) override;
+  CefRefPtr<CefBinaryValue> GetBinary(size_t index) override;
+  CefRefPtr<CefDictionaryValue> GetDictionary(size_t index) override;
+  CefRefPtr<CefListValue> GetList(size_t index) override;
+  bool SetValue(size_t index, CefRefPtr<CefValue> value) override;
+  bool SetNull(size_t index) override;
+  bool SetBool(size_t index, bool value) override;
+  bool SetInt(size_t index, int value) override;
+  bool SetDouble(size_t index, double value) override;
+  bool SetString(size_t index, const CefString& value) override;
+  bool SetBinary(size_t index, CefRefPtr<CefBinaryValue> value) override;
+  bool SetDictionary(size_t index,
+                     CefRefPtr<CefDictionaryValue> value) override;
+  bool SetList(size_t index, CefRefPtr<CefListValue> value) override;
+
+ private:
+  // See the CefValueBase constructor for usage.
+  CefListValueImpl(base::ListValue* value,
+                   void* parent_value,
+                   ValueMode value_mode,
+                   bool read_only,
+                   CefValueController* controller);
+
+  bool RemoveInternal(size_t index);
+  base::Value* SetInternal(size_t index, base::Value* value);
+
+  DISALLOW_COPY_AND_ASSIGN(CefListValueImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_VALUES_IMPL_H_
diff --git a/src/libcef/common/waitable_event_impl.cc b/src/libcef/common/waitable_event_impl.cc
new file mode 100644
index 0000000..81217bb
--- /dev/null
+++ b/src/libcef/common/waitable_event_impl.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/common/waitable_event_impl.h"
+
+#include "include/cef_task.h"
+
+#include "base/time/time.h"
+
+namespace {
+
+bool AllowWait() {
+  if (CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO)) {
+    NOTREACHED() << "waiting is not allowed on the current thread";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+// static
+CefRefPtr<CefWaitableEvent> CefWaitableEvent::CreateWaitableEvent(
+    bool automatic_reset,
+    bool initially_signaled) {
+  return new CefWaitableEventImpl(automatic_reset, initially_signaled);
+}
+
+CefWaitableEventImpl::CefWaitableEventImpl(bool automatic_reset,
+                                           bool initially_signaled)
+    : event_(automatic_reset ? base::WaitableEvent::ResetPolicy::AUTOMATIC
+                             : base::WaitableEvent::ResetPolicy::MANUAL,
+             initially_signaled
+                 ? base::WaitableEvent::InitialState::SIGNALED
+                 : base::WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+void CefWaitableEventImpl::Reset() {
+  event_.Reset();
+}
+
+void CefWaitableEventImpl::Signal() {
+  event_.Signal();
+}
+
+bool CefWaitableEventImpl::IsSignaled() {
+  return event_.IsSignaled();
+}
+
+void CefWaitableEventImpl::Wait() {
+  if (!AllowWait())
+    return;
+  event_.Wait();
+}
+
+bool CefWaitableEventImpl::TimedWait(int64 max_ms) {
+  if (!AllowWait())
+    return false;
+  return event_.TimedWait(base::TimeDelta::FromMilliseconds(max_ms));
+}
diff --git a/src/libcef/common/waitable_event_impl.h b/src/libcef/common/waitable_event_impl.h
new file mode 100644
index 0000000..96908c1
--- /dev/null
+++ b/src/libcef/common/waitable_event_impl.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_WAITABLE_EVENT_IMPL_H_
+#define CEF_LIBCEF_COMMON_WAITABLE_EVENT_IMPL_H_
+#pragma once
+
+#include "include/cef_waitable_event.h"
+
+#include "base/synchronization/waitable_event.h"
+
+class CefWaitableEventImpl : public CefWaitableEvent {
+ public:
+  CefWaitableEventImpl(bool automatic_reset, bool initially_signaled);
+
+  // CefWaitableEvent methods:
+  void Reset() override;
+  void Signal() override;
+  bool IsSignaled() override;
+  void Wait() override;
+  bool TimedWait(int64 max_ms) override;
+
+ private:
+  base::WaitableEvent event_;
+
+  IMPLEMENT_REFCOUNTING(CefWaitableEventImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefWaitableEventImpl);
+};
+
+#endif  // CEF_LIBCEF_COMMON_WAITABLE_EVENT_IMPL_H_
diff --git a/src/libcef/common/widevine_loader.cc b/src/libcef/common/widevine_loader.cc
new file mode 100644
index 0000000..f2d9e30
--- /dev/null
+++ b/src/libcef/common/widevine_loader.cc
@@ -0,0 +1,507 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 the Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "libcef/common/widevine_loader.h"
+
+#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
+#include "libcef/browser/context.h"
+#include "libcef/browser/thread_util.h"
+#include "libcef/common/cef_switches.h"
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/memory/ptr_util.h"
+#include "base/native_library.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/plugin_service_impl.h"
+#include "content/public/browser/cdm_registry.h"
+#include "content/public/common/cdm_info.h"
+#include "content/public/common/content_switches.h"
+#include "media/cdm/cdm_host_file.h"
+#include "media/cdm/supported_cdm_versions.h"
+#include "services/service_manager/embedder/switches.h"
+#include "services/service_manager/sandbox/switches.h"
+#include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
+
+namespace {
+
+base::LazyInstance<CefWidevineLoader>::Leaky g_widevine_loader =
+    LAZY_INSTANCE_INITIALIZER;
+
+// Based on chrome/browser/component_updater/widevine_cdm_component_installer.cc
+
+// Name of the Widevine CDM OS in the component manifest.
+const char kWidevineCdmOs[] =
+#if defined(OS_MACOSX)
+    "mac";
+#elif defined(OS_WIN)
+    "win";
+#else  // OS_LINUX, etc. TODO(viettrungluu): Separate out Chrome OS and Android?
+    "linux";
+#endif
+
+// Name of the Widevine CDM architecture in the component manifest.
+const char kWidevineCdmArch[] =
+#if defined(ARCH_CPU_X86)
+    "ia32";  // This differs from the component updater which uses "x86".
+#elif defined(ARCH_CPU_X86_64)
+    "x64";
+#else  // TODO(viettrungluu): Support an ARM check?
+    "???";
+#endif
+
+// The CDM OS and architecture.
+const char kCdmOsName[] = "os";
+const char kCdmArchName[] = "arch";
+
+//  The CDM version (e.g. "1.4.8.903").
+const char kCdmVersionName[] = "version";
+
+// The CDM manifest includes several custom values, all beginning with "x-cdm-".
+// All values are strings.
+// All values that are lists are delimited by commas. No trailing commas.
+// For example, "1,2,4".
+const char kCdmValueDelimiter[] = ",";
+// The following entries are required.
+//  Interface versions are lists of integers (e.g. "1" or "1,2,4").
+//  These are checked in this file before registering the CDM.
+//  All match the interface versions from content_decryption_module.h that the
+//  CDM supports.
+//    Matches CDM_MODULE_VERSION.
+const char kCdmModuleVersionsName[] = "x-cdm-module-versions";
+//    Matches supported ContentDecryptionModule_* version(s).
+const char kCdmInterfaceVersionsName[] = "x-cdm-interface-versions";
+//    Matches supported Host_* version(s).
+const char kCdmHostVersionsName[] = "x-cdm-host-versions";
+//  The codecs list is a list of simple codec names (e.g. "vp8,vorbis").
+//  The list is passed to other parts of Chrome.
+const char kCdmCodecsListName[] = "x-cdm-codecs";
+//  Whether persistent license is supported by the CDM: "true" or "false".
+const char kCdmPersistentLicenseSupportName[] =
+    "x-cdm-persistent-license-support";
+const char kCdmSupportedEncryptionSchemesName[] =
+    "x-cdm-supported-encryption-schemes";
+
+// The following strings are used to specify supported codecs in the
+// parameter |kCdmCodecsListName|.
+const char kCdmSupportedCodecVp8[] = "vp8";
+const char kCdmSupportedCodecVp9[] = "vp9.0";
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+const char kCdmSupportedCodecAvc1[] = "avc1";
+#endif
+
+// The following strings are used to specify supported encryption schemes in
+// the parameter |kCdmSupportedEncryptionSchemesName|.
+const char kCdmSupportedEncryptionSchemeCenc[] = "cenc";
+const char kCdmSupportedEncryptionSchemeCbcs[] = "cbcs";
+
+// Arguments passed to MakeCdmInfo.
+struct CdmInfoArgs {
+  base::FilePath path;
+  std::string version;
+  content::CdmCapability capability;
+};
+
+std::unique_ptr<base::DictionaryValue> ParseManifestFile(
+    const base::FilePath& manifest_path) {
+  CEF_REQUIRE_BLOCKING();
+
+  // Manifest file should be < 1kb. Read at most 2kb.
+  std::string manifest_contents;
+  if (!base::ReadFileToStringWithMaxSize(manifest_path, &manifest_contents,
+                                         2048)) {
+    return nullptr;
+  }
+
+  JSONStringValueDeserializer deserializer(manifest_contents);
+  std::unique_ptr<base::Value> manifest(
+      deserializer.Deserialize(nullptr, nullptr));
+
+  if (!manifest.get() || !manifest->is_dict())
+    return nullptr;
+
+  // Transfer ownership to the caller.
+  return base::WrapUnique(
+      static_cast<base::DictionaryValue*>(manifest.release()));
+}
+
+std::string GetManifestValue(const base::DictionaryValue& manifest,
+                             const std::string& key,
+                             std::string* error_message) {
+  std::stringstream ss;
+  std::string value;
+  if (!manifest.GetString(key, &value)) {
+    ss << "Manifest missing " << key;
+    *error_message = ss.str();
+  } else if (value.empty()) {
+    ss << "Manifest has empty " << key;
+    *error_message = ss.str();
+  }
+  return value;
+}
+
+typedef bool (*VersionCheckFunc)(int version);
+
+bool CheckForCompatibleVersion(const base::DictionaryValue& manifest,
+                               const std::string version_name,
+                               VersionCheckFunc version_check_func,
+                               std::string* error_message) {
+  std::string versions_string =
+      GetManifestValue(manifest, version_name, error_message);
+  if (versions_string.empty())
+    return false;
+
+  for (const base::StringPiece& ver_str :
+       base::SplitStringPiece(versions_string, kCdmValueDelimiter,
+                              base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+    int version = 0;
+    if (base::StringToInt(ver_str, &version))
+      if (version_check_func(version))
+        return true;
+  }
+
+  std::stringstream ss;
+  ss << "Manifest has no supported " << version_name << " in '"
+     << versions_string << "'";
+  *error_message = ss.str();
+  return false;
+}
+
+// Returns whether the CDM's OS/platform and module/interface/host API versions,
+// as specified in the manifest, are compatible with this Chromium binary.
+bool IsCompatibleWithChrome(const base::DictionaryValue& manifest,
+                            std::string* error_message) {
+  return GetManifestValue(manifest, kCdmOsName, error_message) ==
+             kWidevineCdmOs &&
+         GetManifestValue(manifest, kCdmArchName, error_message) ==
+             kWidevineCdmArch &&
+         CheckForCompatibleVersion(manifest, kCdmModuleVersionsName,
+                                   media::IsSupportedCdmModuleVersion,
+                                   error_message) &&
+         CheckForCompatibleVersion(manifest, kCdmInterfaceVersionsName,
+                                   media::IsSupportedCdmInterfaceVersion,
+                                   error_message) &&
+         CheckForCompatibleVersion(manifest, kCdmHostVersionsName,
+                                   media::IsSupportedCdmHostVersion,
+                                   error_message);
+}
+
+// Returns true and updates |video_codecs| if the appropriate manifest entry is
+// valid. Returns false and does not modify |video_codecs| if the manifest entry
+// is incorrectly formatted.
+bool GetCodecs(const base::DictionaryValue& manifest,
+               std::vector<media::VideoCodec>* video_codecs,
+               std::string* error_message) {
+  DCHECK(video_codecs);
+
+  const base::Value* value = manifest.FindKey(kCdmCodecsListName);
+  if (!value) {
+    std::stringstream ss;
+    ss << "Widevine CDM component manifest is missing codecs.";
+    *error_message = ss.str();
+    return true;
+  }
+
+  if (!value->is_string()) {
+    std::stringstream ss;
+    ss << "Manifest entry " << kCdmCodecsListName << " is not a string.";
+    *error_message = ss.str();
+    return false;
+  }
+
+  const std::string& codecs = value->GetString();
+  if (codecs.empty()) {
+    std::stringstream ss;
+    ss << "Widevine CDM component manifest has empty codecs list.";
+    *error_message = ss.str();
+    return true;
+  }
+
+  std::vector<media::VideoCodec> result;
+  const std::vector<base::StringPiece> supported_codecs =
+      base::SplitStringPiece(codecs, kCdmValueDelimiter, base::TRIM_WHITESPACE,
+                             base::SPLIT_WANT_NONEMPTY);
+
+  for (const auto& codec : supported_codecs) {
+    if (codec == kCdmSupportedCodecVp8)
+      result.push_back(media::VideoCodec::kCodecVP8);
+    else if (codec == kCdmSupportedCodecVp9)
+      result.push_back(media::VideoCodec::kCodecVP9);
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+    else if (codec == kCdmSupportedCodecAvc1)
+      result.push_back(media::VideoCodec::kCodecH264);
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
+  }
+
+  video_codecs->swap(result);
+  return true;
+}
+
+// Returns true and updates |encryption_schemes| if the appropriate manifest
+// entry is valid. Returns false and does not modify |encryption_schemes| if the
+// manifest entry is incorrectly formatted. It is assumed that all CDMs support
+// 'cenc', so if the manifest entry is missing, the result will indicate support
+// for 'cenc' only. Incorrect types in the manifest entry will log the error and
+// fail. Unrecognized values will be reported but otherwise ignored.
+bool GetEncryptionSchemes(
+    const base::DictionaryValue& manifest,
+    base::flat_set<media::EncryptionScheme>* encryption_schemes,
+    std::string* error_message) {
+  DCHECK(encryption_schemes);
+
+  const base::Value* value =
+      manifest.FindKey(kCdmSupportedEncryptionSchemesName);
+  if (!value) {
+    // No manifest entry found, so assume only 'cenc' supported for backwards
+    // compatibility.
+    encryption_schemes->insert(media::EncryptionScheme::kCenc);
+    return true;
+  }
+
+  if (!value->is_list()) {
+    std::stringstream ss;
+    ss << "Manifest entry " << kCdmSupportedEncryptionSchemesName
+       << " is not a list.";
+    *error_message = ss.str();
+    return false;
+  }
+
+  const base::span<const base::Value> list = value->GetList();
+  base::flat_set<media::EncryptionScheme> result;
+  for (const auto& item : list) {
+    if (!item.is_string()) {
+      std::stringstream ss;
+      ss << "Unrecognized item type in manifest entry "
+         << kCdmSupportedEncryptionSchemesName;
+      *error_message = ss.str();
+      return false;
+    }
+
+    const std::string& scheme = item.GetString();
+    if (scheme == kCdmSupportedEncryptionSchemeCenc) {
+      result.insert(media::EncryptionScheme::kCenc);
+    } else if (scheme == kCdmSupportedEncryptionSchemeCbcs) {
+      result.insert(media::EncryptionScheme::kCbcs);
+    } else {
+      std::stringstream ss;
+      ss << "Unrecognized encryption scheme " << scheme << " in manifest entry "
+         << kCdmSupportedEncryptionSchemesName;
+      *error_message = ss.str();
+    }
+  }
+
+  // As the manifest entry exists, it must specify at least one valid value.
+  if (result.empty())
+    return false;
+
+  encryption_schemes->swap(result);
+  return true;
+}
+
+// Returns true and updates |session_types| if the appropriate manifest entry is
+// valid. Returns false if the manifest entry is incorrectly formatted.
+bool GetSessionTypes(const base::DictionaryValue& manifest,
+                     base::flat_set<media::CdmSessionType>* session_types,
+                     std::string* error_message) {
+  DCHECK(session_types);
+
+  bool is_persistent_license_supported = false;
+  const base::Value* value = manifest.FindKey(kCdmPersistentLicenseSupportName);
+  if (value) {
+    if (!value->is_bool())
+      return false;
+    is_persistent_license_supported = value->GetBool();
+  }
+
+  // Temporary session is always supported.
+  session_types->insert(media::CdmSessionType::kTemporary);
+  if (is_persistent_license_supported)
+    session_types->insert(media::CdmSessionType::kPersistentLicense);
+
+  return true;
+}
+
+// Verify and load the contents of |base_path|.
+cef_cdm_registration_error_t LoadWidevineCdmInfo(
+    const base::FilePath& base_path,
+    CdmInfoArgs* args,
+    std::string* error_message) {
+  std::stringstream ss;
+
+  args->path = base_path.AppendASCII(
+      base::GetNativeLibraryName(kWidevineCdmLibraryName));
+  if (!base::PathExists(args->path)) {
+    ss << "Missing file " << args->path.value();
+    *error_message = ss.str();
+    return CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS;
+  }
+
+  base::FilePath manifest_path = base_path.AppendASCII("manifest.json");
+  if (!base::PathExists(manifest_path)) {
+    ss << "Missing manifest file " << manifest_path.value();
+    *error_message = ss.str();
+    return CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS;
+  }
+
+  std::unique_ptr<base::DictionaryValue> manifest =
+      ParseManifestFile(manifest_path);
+  if (!manifest) {
+    ss << "Failed to parse manifest file " << manifest_path.value();
+    *error_message = ss.str();
+    return CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS;
+  }
+
+  if (!IsCompatibleWithChrome(*manifest, error_message))
+    return CEF_CDM_REGISTRATION_ERROR_INCOMPATIBLE;
+
+  args->version = GetManifestValue(*manifest, kCdmVersionName, error_message);
+  if (args->version.empty())
+    return CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS;
+
+  if (!GetCodecs(*manifest, &args->capability.video_codecs, error_message) ||
+      !GetEncryptionSchemes(*manifest, &args->capability.encryption_schemes,
+                            error_message) ||
+      !GetSessionTypes(*manifest, &args->capability.session_types,
+                       error_message)) {
+    return CEF_CDM_REGISTRATION_ERROR_INCORRECT_CONTENTS;
+  }
+
+  return CEF_CDM_REGISTRATION_ERROR_NONE;
+}
+
+void DeliverWidevineCdmCallback(cef_cdm_registration_error_t result,
+                                const std::string& error_message,
+                                CefRefPtr<CefRegisterCdmCallback> callback) {
+  CEF_REQUIRE_UIT();
+
+  if (result != CEF_CDM_REGISTRATION_ERROR_NONE)
+    LOG(ERROR) << "Widevine CDM registration failed; " << error_message;
+  else if (!error_message.empty())
+    LOG(WARNING) << "Widevine CDM registration warning; " << error_message;
+
+  if (callback)
+    callback->OnCdmRegistrationComplete(result, error_message);
+}
+
+content::CdmInfo MakeCdmInfo(const CdmInfoArgs& args) {
+  return content::CdmInfo(kWidevineCdmDisplayName, kWidevineCdmGuid,
+                          base::Version(args.version), args.path,
+                          kWidevineCdmFileSystemId, args.capability,
+                          kWidevineKeySystem, false);
+}
+
+void RegisterWidevineCdmOnUIThread(std::unique_ptr<CdmInfoArgs> args,
+                                   CefRefPtr<CefRegisterCdmCallback> callback) {
+  CEF_REQUIRE_UIT();
+
+  // Register Widevine with the CdmRegistry.
+  content::CdmRegistry::GetInstance()->RegisterCdm(MakeCdmInfo(*args));
+
+  DeliverWidevineCdmCallback(CEF_CDM_REGISTRATION_ERROR_NONE, std::string(),
+                             callback);
+}
+
+void LoadWidevineCdmInfoOnBlockingThread(
+    const base::FilePath& base_path,
+    CefRefPtr<CefRegisterCdmCallback> callback) {
+  CEF_REQUIRE_BLOCKING();
+
+  std::unique_ptr<CdmInfoArgs> args = std::make_unique<CdmInfoArgs>();
+  std::string error_message;
+  cef_cdm_registration_error_t result =
+      LoadWidevineCdmInfo(base_path, args.get(), &error_message);
+  if (result != CEF_CDM_REGISTRATION_ERROR_NONE) {
+    CEF_POST_TASK(CEF_UIT, base::BindOnce(DeliverWidevineCdmCallback, result,
+                                          error_message, callback));
+    return;
+  }
+
+  // Continue execution on the UI thread.
+  CEF_POST_TASK(CEF_UIT, base::BindOnce(RegisterWidevineCdmOnUIThread,
+                                        std::move(args), callback));
+}
+
+}  // namespace
+
+// static
+CefWidevineLoader* CefWidevineLoader::GetInstance() {
+  return &g_widevine_loader.Get();
+}
+
+void CefWidevineLoader::LoadWidevineCdm(
+    const base::FilePath& path,
+    CefRefPtr<CefRegisterCdmCallback> callback) {
+  if (!CONTEXT_STATE_VALID()) {
+    // Loading will proceed from OnContextInitialized().
+    load_pending_ = true;
+    path_ = path;
+    callback_ = callback;
+    return;
+  }
+
+  CEF_POST_USER_VISIBLE_TASK(
+      base::BindOnce(LoadWidevineCdmInfoOnBlockingThread, path, callback));
+}
+
+void CefWidevineLoader::OnContextInitialized() {
+  CEF_REQUIRE_UIT();
+  if (load_pending_) {
+    load_pending_ = false;
+    LoadWidevineCdm(path_, callback_);
+    callback_ = nullptr;
+  }
+}
+
+#if defined(OS_LINUX)
+
+// static
+void CefWidevineLoader::AddContentDecryptionModules(
+    std::vector<content::CdmInfo>* cdms,
+    std::vector<media::CdmHostFilePath>* cdm_host_file_paths) {
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  // Perform early plugin registration in the zygote process when the sandbox is
+  // enabled to avoid "cannot open shared object file: Operation not permitted"
+  // errors during plugin loading. This is because the Zygote process must pre-
+  // load all plugins before initializing the sandbox.
+  if (command_line.GetSwitchValueASCII(switches::kProcessType) !=
+          service_manager::switches::kZygoteProcess ||
+      command_line.HasSwitch(service_manager::switches::kNoSandbox)) {
+    return;
+  }
+
+  // The Widevine CDM path is passed to the zygote process via
+  // CefContentBrowserClient::AppendExtraCommandLineSwitches.
+  const base::FilePath& base_path =
+      command_line.GetSwitchValuePath(switches::kWidevineCdmPath);
+  if (base_path.empty())
+    return;
+
+  // Load contents of the plugin directory synchronously. This only occurs once
+  // on zygote process startup so should not have a huge performance penalty.
+  CdmInfoArgs args;
+  std::string error_message;
+  cef_cdm_registration_error_t result =
+      LoadWidevineCdmInfo(base_path, &args, &error_message);
+  if (result != CEF_CDM_REGISTRATION_ERROR_NONE) {
+    LOG(ERROR) << "Widevine CDM registration failed; " << error_message;
+    return;
+  }
+
+  cdms->push_back(MakeCdmInfo(args));
+}
+
+#endif  // defined(OS_LINUX)
+
+CefWidevineLoader::CefWidevineLoader() {}
+
+CefWidevineLoader::~CefWidevineLoader() {}
+
+#endif  // BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
diff --git a/src/libcef/common/widevine_loader.h b/src/libcef/common/widevine_loader.h
new file mode 100644
index 0000000..fef829e
--- /dev/null
+++ b/src/libcef/common/widevine_loader.h
@@ -0,0 +1,70 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef CEF_LIBCEF_COMMON_WIDEVINE_LOADER_H_
+#define CEF_LIBCEF_COMMON_WIDEVINE_LOADER_H_
+#pragma once
+
+#include "build/build_config.h"
+#include "media/media_buildflags.h"
+#include "third_party/widevine/cdm/buildflags.h"
+
+#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
+#include <vector>
+
+#include "include/cef_web_plugin.h"
+
+#include "base/lazy_instance.h"
+
+namespace content {
+struct CdmInfo;
+}
+
+namespace media {
+struct CdmHostFilePath;
+}
+
+class CefWidevineLoader {
+ public:
+  // Returns the singleton instance of this object.
+  static CefWidevineLoader* GetInstance();
+
+  // Load the Widevine CDM. May be called before or after context creation. See
+  // comments in cef_web_plugin.h.
+  void LoadWidevineCdm(const base::FilePath& path,
+                       CefRefPtr<CefRegisterCdmCallback> callback);
+
+  // Plugin registration is triggered here if LoadWidevineCdm() was called
+  // before context creation.
+  void OnContextInitialized();
+
+#if defined(OS_LINUX)
+  // The zygote process which is used when the sandbox is enabled on Linux
+  // requires early loading of CDM modules. Other processes will receive
+  // load notification in the usual way.
+  // Called from CefContentClient::AddContentDecryptionModules.
+  static void AddContentDecryptionModules(
+      std::vector<content::CdmInfo>* cdms,
+      std::vector<media::CdmHostFilePath>* cdm_host_file_paths);
+
+  const base::FilePath& path() { return path_; }
+#endif
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<CefWidevineLoader>;
+
+  // Members are only accessed before context initialization or on the UI
+  // thread.
+  bool load_pending_ = false;
+  base::FilePath path_;
+  CefRefPtr<CefRegisterCdmCallback> callback_;
+
+  CefWidevineLoader();
+  ~CefWidevineLoader();
+};
+
+#endif  // BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
+#endif  // CEF_LIBCEF_COMMON_WIDEVINE_LOADER_H_
diff --git a/src/libcef/features/BUILD.gn b/src/libcef/features/BUILD.gn
new file mode 100644
index 0000000..01f5975
--- /dev/null
+++ b/src/libcef/features/BUILD.gn
@@ -0,0 +1,96 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/buildflag_header.gni")
+import("//cef/libcef/features/features.gni")
+
+# This file is in a separate directory so all targets in the build can refer to
+# the buildflag header to get the necessary preprocessor defines without
+# bringing in any CEF targets. Other targets can depend on this target
+# regardless of whether CEF is being built. Set the `enable_cef=false` GN arg to
+# disable the CEF changes when building Chrome.
+#
+# Example usage:
+#
+# 1. An existing GN configuration file at path/to/foo/BUILD.gn:
+#
+#    # Import the `enable_cef` arg.
+#    import("//cef/libcef/features/features.gni")
+#    ...
+#
+#    # An existing target that is modified for CEF.
+#    # The target type might instead be `component`, `source_set`, etc.
+#    static_library("foo") {
+#      sources = [ ... ]
+#
+#      deps = [
+#        # Always include the CEF features.
+#        "//cef/libcef/features",
+#        ...
+#      ]
+#
+#      if (enable_cef) {
+#        # Actions to perform when the CEF build is enabled.
+#
+#        # Optionally include CEF source files directly in this target. This
+#        # approach is required for targets that are either directly or
+#        # indirectly included in a `component` target (otherwise
+#        # `is_component_build=true` builds will fail). Keep in mind that these
+#        # files are part of this target instead of the `libcef_static` target
+#        # and therefore subject to any target-specific configuration settings
+#        # such as include paths, defines, compiler flags, etc.
+#        sources += [
+#          "//cef/libcef/browser/foo_helper.cc",
+#          "//cef/libcef/browser/foo_helper.h",
+#        ]
+#
+#        # Always include the CEF configuration.
+#        configs += [ "//cef/libcef/features:config" ]
+#     }
+#     ...
+#   }
+#
+# 2. An existing C++ source file at path/to/foo/foo.cc:
+#
+#    // Include the `BUILDFLAG(ENABLE_CEF)` definition.
+#    #include "cef/libcef/features/features.h"
+#    ...
+#
+#    #if BUILDFLAG(ENABLE_CEF)
+#    // CEF headers here...
+#    #include "cef/libcef/browser/foo_helper.h"
+#    #else
+#    // Chrome headers here...
+#    #endif
+#
+#    // An existing function that is modified for CEF.
+#    void DoFoo() {
+#    #if BUILDFLAG(ENABLE_CEF)
+#      // CEF implementation here...
+#      cef_foo_helper::DoFoo();
+#    #else
+#      // Chrome implementation here...
+#    #endif  // !BUILDFLAG(ENABLE_CEF)
+#    }
+#    ...
+#
+
+buildflag_header("features") {
+  header = "features.h"
+
+  flags = [
+    "ENABLE_CEF=$enable_cef",
+    "IS_CEF_SANDBOX_BUILD=$is_cef_sandbox_build",
+  ]
+}
+
+# Configuration for all targets that include CEF source code library-side.
+config("config") {
+  # CEF sources use includes relative to the CEF root directory.
+  include_dirs = [ "//cef" ]
+  defines = [
+    "BUILDING_CEF_SHARED",
+    "USING_CHROMIUM_INCLUDES",
+  ]
+}
diff --git a/src/libcef/features/features.gni b/src/libcef/features/features.gni
new file mode 100644
index 0000000..7d7ae49
--- /dev/null
+++ b/src/libcef/features/features.gni
@@ -0,0 +1,14 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+  enable_cef = true
+
+  # Enables base target customizations necessary for distribution of the
+  # cef_sandbox static library. This value will be set via gn_args.py for the
+  # official sandbox build configurations only. DO NOT SET THIS VALUE MANUALLY
+  # FOR OTHER CHROMIUM/CEF BUILD CONFIGURATIONS AS ITS USE MAY HAVE SIGNIFICANT
+  # PERFORMANCE AND/OR SECURITY IMPLICATIONS.
+  is_cef_sandbox_build = false
+}
diff --git a/src/libcef/renderer/blink_glue.cc b/src/libcef/renderer/blink_glue.cc
new file mode 100644
index 0000000..8f2c13e
--- /dev/null
+++ b/src/libcef/renderer/blink_glue.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/blink_glue.h"
+
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_local_frame_client.h"
+#include "third_party/blink/public/web/web_node.h"
+#include "third_party/blink/public/web/web_view_client.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/referrer_script_info.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_code_cache.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/editing/serializers/serialization.h"
+#include "third_party/blink/renderer/core/exported/web_view_impl.h"
+#include "third_party/blink/renderer/core/frame/frame_owner.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#undef LOG
+
+#include "base/logging.h"
+
+namespace blink_glue {
+
+const int64_t kInvalidFrameId = -1;
+
+bool CanGoBack(blink::WebView* view) {
+  if (!view)
+    return false;
+  blink::WebViewImpl* impl = reinterpret_cast<blink::WebViewImpl*>(view);
+  return (impl->Client()->HistoryBackListCount() > 0);
+}
+
+bool CanGoForward(blink::WebView* view) {
+  if (!view)
+    return false;
+  blink::WebViewImpl* impl = reinterpret_cast<blink::WebViewImpl*>(view);
+  return (impl->Client()->HistoryForwardListCount() > 0);
+}
+
+void GoBack(blink::WebView* view) {
+  if (!view)
+    return;
+
+  blink::WebFrame* main_frame = view->MainFrame();
+  if (main_frame && main_frame->IsWebLocalFrame()) {
+    blink::WebViewImpl* view_impl = reinterpret_cast<blink::WebViewImpl*>(view);
+    if (view_impl->Client()->HistoryBackListCount() > 0) {
+      blink::Frame* core_frame = blink::WebFrame::ToCoreFrame(*main_frame);
+      blink::To<blink::LocalFrame>(core_frame)
+          ->GetLocalFrameHostRemote()
+          .GoToEntryAtOffset(-1, true /* has_user_gesture */);
+    }
+  }
+}
+
+void GoForward(blink::WebView* view) {
+  if (!view)
+    return;
+
+  blink::WebFrame* main_frame = view->MainFrame();
+  if (main_frame && main_frame->IsWebLocalFrame()) {
+    blink::WebViewImpl* view_impl = reinterpret_cast<blink::WebViewImpl*>(view);
+    if (view_impl->Client()->HistoryForwardListCount() > 0) {
+      blink::Frame* core_frame = blink::WebFrame::ToCoreFrame(*main_frame);
+      blink::To<blink::LocalFrame>(core_frame)
+          ->GetLocalFrameHostRemote()
+          .GoToEntryAtOffset(1, true /* has_user_gesture */);
+    }
+  }
+}
+
+std::string DumpDocumentText(blink::WebLocalFrame* frame) {
+  // We use the document element's text instead of the body text here because
+  // not all documents have a body, such as XML documents.
+  blink::WebElement document_element = frame->GetDocument().DocumentElement();
+  if (document_element.IsNull())
+    return std::string();
+
+  blink::Element* web_element = document_element.Unwrap<blink::Element>();
+  return blink::WebString(web_element->innerText()).Utf8();
+}
+
+cef_dom_node_type_t GetNodeType(const blink::WebNode& node) {
+  const blink::Node* web_node = node.ConstUnwrap<blink::Node>();
+  switch (web_node->getNodeType()) {
+    case blink::Node::kElementNode:
+      return DOM_NODE_TYPE_ELEMENT;
+    case blink::Node::kAttributeNode:
+      return DOM_NODE_TYPE_ATTRIBUTE;
+    case blink::Node::kTextNode:
+      return DOM_NODE_TYPE_TEXT;
+    case blink::Node::kCdataSectionNode:
+      return DOM_NODE_TYPE_CDATA_SECTION;
+    case blink::Node::kProcessingInstructionNode:
+      return DOM_NODE_TYPE_PROCESSING_INSTRUCTIONS;
+    case blink::Node::kCommentNode:
+      return DOM_NODE_TYPE_COMMENT;
+    case blink::Node::kDocumentNode:
+      return DOM_NODE_TYPE_DOCUMENT;
+    case blink::Node::kDocumentTypeNode:
+      return DOM_NODE_TYPE_DOCUMENT_TYPE;
+    case blink::Node::kDocumentFragmentNode:
+      return DOM_NODE_TYPE_DOCUMENT_FRAGMENT;
+  }
+  return DOM_NODE_TYPE_UNSUPPORTED;
+}
+
+blink::WebString GetNodeName(const blink::WebNode& node) {
+  const blink::Node* web_node = node.ConstUnwrap<blink::Node>();
+  return web_node->nodeName();
+}
+
+blink::WebString CreateNodeMarkup(const blink::WebNode& node) {
+  const blink::Node* web_node = node.ConstUnwrap<blink::Node>();
+  return blink::CreateMarkup(web_node);
+}
+
+bool SetNodeValue(blink::WebNode& node, const blink::WebString& value) {
+  blink::Node* web_node = node.Unwrap<blink::Node>();
+  web_node->setNodeValue(value);
+  return true;
+}
+
+v8::MaybeLocal<v8::Value> CallV8Function(v8::Local<v8::Context> context,
+                                         v8::Local<v8::Function> function,
+                                         v8::Local<v8::Object> receiver,
+                                         int argc,
+                                         v8::Local<v8::Value> args[],
+                                         v8::Isolate* isolate) {
+  v8::MaybeLocal<v8::Value> func_rv;
+
+  // Execute the function call using the V8ScriptRunner so that inspector
+  // instrumentation works.
+  blink::LocalFrame* frame = blink::ToLocalFrameIfNotDetached(context);
+  DCHECK(frame);
+  if (frame &&
+      frame->GetDocument()->CanExecuteScripts(blink::kAboutToExecuteScript)) {
+    func_rv = blink::V8ScriptRunner::CallFunction(
+        function, frame->GetDocument()->ToExecutionContext(), receiver, argc,
+        args, isolate);
+  }
+
+  return func_rv;
+}
+
+bool IsTextControlElement(const blink::WebElement& element) {
+  const blink::Element* web_element = element.ConstUnwrap<blink::Element>();
+  return web_element->IsTextControl();
+}
+
+v8::MaybeLocal<v8::Value> ExecuteV8ScriptAndReturnValue(
+    const blink::WebString& source,
+    const blink::WebString& source_url,
+    int start_line,
+    v8::Local<v8::Context> context,
+    v8::Isolate* isolate,
+    v8::TryCatch& tryCatch,
+    blink::SanitizeScriptErrors sanitizeScriptErrors) {
+  // Based on ScriptController::executeScriptAndReturnValue
+  DCHECK(isolate);
+
+  if (start_line < 1)
+    start_line = 1;
+
+  const blink::ScriptSourceCode ssc = blink::ScriptSourceCode(
+      source, blink::ScriptSourceLocationType::kInternal,
+      nullptr, /* cache_handler */
+      blink::KURL(source_url),
+      WTF::TextPosition(WTF::OrdinalNumber::FromOneBasedInt(start_line),
+                        WTF::OrdinalNumber::FromZeroBasedInt(0)));
+
+  v8::MaybeLocal<v8::Value> result;
+
+  blink::LocalFrame* frame = blink::ToLocalFrameIfNotDetached(context);
+  if (!frame)
+    return result;
+
+  blink::V8CacheOptions v8CacheOptions(blink::kV8CacheOptionsDefault);
+  if (frame && frame->GetSettings())
+    v8CacheOptions = frame->GetSettings()->GetV8CacheOptions();
+
+  // Based on V8ScriptRunner::CompileAndRunInternalScript:
+  v8::ScriptCompiler::CompileOptions compile_options;
+  blink::V8CodeCache::ProduceCacheOptions produce_cache_options;
+  v8::ScriptCompiler::NoCacheReason no_cache_reason;
+  std::tie(compile_options, produce_cache_options, no_cache_reason) =
+      blink::V8CodeCache::GetCompileOptions(v8CacheOptions, ssc);
+
+  // Currently internal scripts don't have cache handlers, so we should not
+  // produce cache for them.
+  DCHECK_EQ(produce_cache_options,
+            blink::V8CodeCache::ProduceCacheOptions::kNoProduceCache);
+
+  v8::Local<v8::Script> script;
+  // Use default ReferrerScriptInfo here:
+  // - nonce: empty for internal script, and
+  // - parser_state: always "not parser inserted" for internal scripts.
+  if (!blink::V8ScriptRunner::CompileScript(
+           blink::ScriptState::From(context), ssc, sanitizeScriptErrors,
+           compile_options, no_cache_reason, blink::ReferrerScriptInfo())
+           .ToLocal(&script)) {
+    DCHECK(tryCatch.HasCaught());
+    return result;
+  }
+
+  return blink::V8ScriptRunner::RunCompiledScript(
+      isolate, script, blink::ToExecutionContext(context));
+}
+
+bool IsScriptForbidden() {
+  return blink::ScriptForbiddenScope::IsScriptForbidden();
+}
+
+void RegisterURLSchemeAsLocal(const blink::WebString& scheme) {
+  blink::SchemeRegistry::RegisterURLSchemeAsLocal(scheme);
+}
+
+void RegisterURLSchemeAsSecure(const blink::WebString& scheme) {
+  blink::SchemeRegistry::RegisterURLSchemeAsSecure(scheme);
+}
+
+void RegisterURLSchemeAsSupportingFetchAPI(const blink::WebString& scheme) {
+  blink::SchemeRegistry::RegisterURLSchemeAsSupportingFetchAPI(scheme);
+}
+
+struct CefScriptForbiddenScope::Impl {
+  blink::ScriptForbiddenScope scope_;
+};
+
+CefScriptForbiddenScope::CefScriptForbiddenScope() : impl_(new Impl()) {}
+
+CefScriptForbiddenScope::~CefScriptForbiddenScope() {}
+
+bool ResponseWasCached(const blink::WebURLResponse& response) {
+  return response.ToResourceResponse().WasCached();
+}
+
+bool HasPluginFrameOwner(blink::WebLocalFrame* frame) {
+  blink::Frame* core_frame = blink::WebFrame::ToCoreFrame(*frame);
+  return core_frame->Owner() && core_frame->Owner()->IsPlugin();
+}
+
+}  // namespace blink_glue
diff --git a/src/libcef/renderer/blink_glue.h b/src/libcef/renderer/blink_glue.h
new file mode 100644
index 0000000..21d6bc1
--- /dev/null
+++ b/src/libcef/renderer/blink_glue.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_BLINK_GLUE_H_
+#define CEF_LIBCEF_RENDERER_BLINK_GLUE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "include/internal/cef_types.h"
+
+#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+class WebElement;
+class WebLocalFrame;
+class WebNode;
+class WebString;
+class WebURLResponse;
+class WebView;
+}  // namespace blink
+
+namespace blink_glue {
+
+BLINK_EXPORT extern const int64_t kInvalidFrameId;
+
+BLINK_EXPORT bool CanGoBack(blink::WebView* view);
+BLINK_EXPORT bool CanGoForward(blink::WebView* view);
+BLINK_EXPORT void GoBack(blink::WebView* view);
+BLINK_EXPORT void GoForward(blink::WebView* view);
+
+// Returns the text of the document element.
+BLINK_EXPORT std::string DumpDocumentText(blink::WebLocalFrame* frame);
+
+// Expose additional actions on WebNode.
+BLINK_EXPORT cef_dom_node_type_t GetNodeType(const blink::WebNode& node);
+BLINK_EXPORT blink::WebString GetNodeName(const blink::WebNode& node);
+BLINK_EXPORT blink::WebString CreateNodeMarkup(const blink::WebNode& node);
+BLINK_EXPORT bool SetNodeValue(blink::WebNode& node,
+                               const blink::WebString& value);
+
+BLINK_EXPORT bool IsTextControlElement(const blink::WebElement& element);
+
+BLINK_EXPORT v8::MaybeLocal<v8::Value> CallV8Function(
+    v8::Local<v8::Context> context,
+    v8::Local<v8::Function> function,
+    v8::Local<v8::Object> receiver,
+    int argc,
+    v8::Local<v8::Value> args[],
+    v8::Isolate* isolate);
+
+BLINK_EXPORT v8::MaybeLocal<v8::Value> ExecuteV8ScriptAndReturnValue(
+    const blink::WebString& source,
+    const blink::WebString& source_url,
+    int start_line,
+    v8::Local<v8::Context> context,
+    v8::Isolate* isolate,
+    v8::TryCatch& tryCatch,
+    blink::SanitizeScriptErrors sanitizeScriptErrors);
+
+BLINK_EXPORT bool IsScriptForbidden();
+
+BLINK_EXPORT void RegisterURLSchemeAsLocal(const blink::WebString& scheme);
+BLINK_EXPORT void RegisterURLSchemeAsSecure(const blink::WebString& scheme);
+
+BLINK_EXPORT void RegisterURLSchemeAsSupportingFetchAPI(
+    const blink::WebString& scheme);
+
+// Wrapper for blink::ScriptForbiddenScope.
+class BLINK_EXPORT CefScriptForbiddenScope final {
+ public:
+  CefScriptForbiddenScope();
+  ~CefScriptForbiddenScope();
+
+ private:
+  struct Impl;
+  std::unique_ptr<Impl> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefScriptForbiddenScope);
+};
+
+BLINK_EXPORT bool ResponseWasCached(const blink::WebURLResponse& response);
+
+// Returns true if the frame owner is a plugin.
+BLINK_EXPORT bool HasPluginFrameOwner(blink::WebLocalFrame* frame);
+
+}  // namespace blink_glue
+
+#endif  // CEF_LIBCEF_RENDERER_BLINK_GLUE_H_
diff --git a/src/libcef/renderer/browser_impl.cc b/src/libcef/renderer/browser_impl.cc
new file mode 100644
index 0000000..0e67ca0
--- /dev/null
+++ b/src/libcef/renderer/browser_impl.cc
@@ -0,0 +1,401 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/browser_impl.h"
+
+#include <string>
+#include <vector>
+
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/content_client.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/content_renderer_client.h"
+#include "libcef/renderer/render_frame_util.h"
+#include "libcef/renderer/thread_util.h"
+
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/renderer/document_state.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_view.h"
+#include "content/renderer/navigation_state.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_frame.h"
+#include "third_party/blink/public/web/web_frame_content_dumper.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_security_policy.h"
+#include "third_party/blink/public/web/web_view.h"
+
+// CefBrowserImpl static methods.
+// -----------------------------------------------------------------------------
+
+// static
+CefRefPtr<CefBrowserImpl> CefBrowserImpl::GetBrowserForView(
+    content::RenderView* view) {
+  return CefContentRendererClient::Get()->GetBrowserForView(view);
+}
+
+// static
+CefRefPtr<CefBrowserImpl> CefBrowserImpl::GetBrowserForMainFrame(
+    blink::WebFrame* frame) {
+  return CefContentRendererClient::Get()->GetBrowserForMainFrame(frame);
+}
+
+// CefBrowser methods.
+// -----------------------------------------------------------------------------
+
+CefRefPtr<CefBrowserHost> CefBrowserImpl::GetHost() {
+  NOTREACHED() << "GetHost cannot be called from the render process";
+  return nullptr;
+}
+
+bool CefBrowserImpl::CanGoBack() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  return blink_glue::CanGoBack(render_view()->GetWebView());
+}
+
+void CefBrowserImpl::GoBack() {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  blink_glue::GoBack(render_view()->GetWebView());
+}
+
+bool CefBrowserImpl::CanGoForward() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  return blink_glue::CanGoForward(render_view()->GetWebView());
+}
+
+void CefBrowserImpl::GoForward() {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  blink_glue::GoForward(render_view()->GetWebView());
+}
+
+bool CefBrowserImpl::IsLoading() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  if (render_view()->GetWebView()) {
+    blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
+    if (main_frame)
+      return main_frame->ToWebLocalFrame()->IsLoading();
+  }
+  return false;
+}
+
+void CefBrowserImpl::Reload() {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (render_view()->GetWebView()) {
+    blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
+    if (main_frame && main_frame->IsWebLocalFrame()) {
+      main_frame->ToWebLocalFrame()->StartReload(
+          blink::WebFrameLoadType::kReload);
+    }
+  }
+}
+
+void CefBrowserImpl::ReloadIgnoreCache() {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (render_view()->GetWebView()) {
+    blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
+    if (main_frame && main_frame->IsWebLocalFrame()) {
+      main_frame->ToWebLocalFrame()->StartReload(
+          blink::WebFrameLoadType::kReloadBypassingCache);
+    }
+  }
+}
+
+void CefBrowserImpl::StopLoad() {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (render_view()->GetWebView()) {
+    blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
+    if (main_frame && main_frame->IsWebLocalFrame()) {
+      main_frame->ToWebLocalFrame()->StopLoading();
+    }
+  }
+}
+
+int CefBrowserImpl::GetIdentifier() {
+  CEF_REQUIRE_RT_RETURN(0);
+
+  return browser_id();
+}
+
+bool CefBrowserImpl::IsSame(CefRefPtr<CefBrowser> that) {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  CefBrowserImpl* impl = static_cast<CefBrowserImpl*>(that.get());
+  return (impl == this);
+}
+
+bool CefBrowserImpl::IsPopup() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  return is_popup();
+}
+
+bool CefBrowserImpl::HasDocument() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  if (render_view()->GetWebView()) {
+    blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
+    if (main_frame && main_frame->IsWebLocalFrame()) {
+      return !main_frame->ToWebLocalFrame()->GetDocument().IsNull();
+    }
+  }
+  return false;
+}
+
+CefRefPtr<CefFrame> CefBrowserImpl::GetMainFrame() {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  if (render_view()->GetWebView()) {
+    blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
+    if (main_frame && main_frame->IsWebLocalFrame()) {
+      return GetWebFrameImpl(main_frame->ToWebLocalFrame()).get();
+    }
+  }
+  return nullptr;
+}
+
+CefRefPtr<CefFrame> CefBrowserImpl::GetFocusedFrame() {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  if (render_view()->GetWebView() &&
+      render_view()->GetWebView()->FocusedFrame()) {
+    return GetWebFrameImpl(render_view()->GetWebView()->FocusedFrame()).get();
+  }
+  return nullptr;
+}
+
+CefRefPtr<CefFrame> CefBrowserImpl::GetFrame(int64 identifier) {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  return GetWebFrameImpl(identifier).get();
+}
+
+CefRefPtr<CefFrame> CefBrowserImpl::GetFrame(const CefString& name) {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  blink::WebView* web_view = render_view()->GetWebView();
+  if (web_view) {
+    const blink::WebString& frame_name =
+        blink::WebString::FromUTF16(name.ToString16());
+    // Search by assigned frame name (Frame::name).
+    blink::WebFrame* frame = web_view->MainFrame();
+    if (frame && frame->IsWebLocalFrame())
+      frame = frame->ToWebLocalFrame()->FindFrameByName(frame_name);
+    if (!frame) {
+      // Search by unique frame name (Frame::uniqueName).
+      const std::string& searchname = name;
+      for (blink::WebFrame* cur_frame = web_view->MainFrame(); cur_frame;
+           cur_frame = cur_frame->TraverseNext()) {
+        if (cur_frame->IsWebLocalFrame() &&
+            render_frame_util::GetName(cur_frame->ToWebLocalFrame()) ==
+                searchname) {
+          frame = cur_frame;
+          break;
+        }
+      }
+    }
+    if (frame && frame->IsWebLocalFrame())
+      return GetWebFrameImpl(frame->ToWebLocalFrame()).get();
+  }
+
+  return nullptr;
+}
+
+size_t CefBrowserImpl::GetFrameCount() {
+  CEF_REQUIRE_RT_RETURN(0);
+
+  int count = 0;
+
+  if (render_view()->GetWebView()) {
+    for (blink::WebFrame* frame = render_view()->GetWebView()->MainFrame();
+         frame; frame = frame->TraverseNext()) {
+      count++;
+    }
+  }
+
+  return count;
+}
+
+void CefBrowserImpl::GetFrameIdentifiers(std::vector<int64>& identifiers) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (identifiers.size() > 0)
+    identifiers.clear();
+
+  if (render_view()->GetWebView()) {
+    for (blink::WebFrame* frame = render_view()->GetWebView()->MainFrame();
+         frame; frame = frame->TraverseNext()) {
+      if (frame->IsWebLocalFrame())
+        identifiers.push_back(
+            render_frame_util::GetIdentifier(frame->ToWebLocalFrame()));
+    }
+  }
+}
+
+void CefBrowserImpl::GetFrameNames(std::vector<CefString>& names) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (names.size() > 0)
+    names.clear();
+
+  if (render_view()->GetWebView()) {
+    for (blink::WebFrame* frame = render_view()->GetWebView()->MainFrame();
+         frame; frame = frame->TraverseNext()) {
+      if (frame->IsWebLocalFrame())
+        names.push_back(render_frame_util::GetName(frame->ToWebLocalFrame()));
+    }
+  }
+}
+
+// CefBrowserImpl public methods.
+// -----------------------------------------------------------------------------
+
+CefBrowserImpl::CefBrowserImpl(content::RenderView* render_view,
+                               int browser_id,
+                               bool is_popup,
+                               bool is_windowless)
+    : content::RenderViewObserver(render_view),
+      browser_id_(browser_id),
+      is_popup_(is_popup),
+      is_windowless_(is_windowless) {}
+
+CefBrowserImpl::~CefBrowserImpl() {}
+
+CefRefPtr<CefFrameImpl> CefBrowserImpl::GetWebFrameImpl(
+    blink::WebLocalFrame* frame) {
+  DCHECK(frame);
+  int64_t frame_id = render_frame_util::GetIdentifier(frame);
+
+  // Frames are re-used between page loads. Only add the frame to the map once.
+  FrameMap::const_iterator it = frames_.find(frame_id);
+  if (it != frames_.end())
+    return it->second;
+
+  CefRefPtr<CefFrameImpl> framePtr(new CefFrameImpl(this, frame, frame_id));
+  frames_.insert(std::make_pair(frame_id, framePtr));
+
+  return framePtr;
+}
+
+CefRefPtr<CefFrameImpl> CefBrowserImpl::GetWebFrameImpl(int64_t frame_id) {
+  if (frame_id == blink_glue::kInvalidFrameId) {
+    if (render_view()->GetWebView()) {
+      blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
+      if (main_frame && main_frame->IsWebLocalFrame()) {
+        return GetWebFrameImpl(main_frame->ToWebLocalFrame());
+      }
+    }
+    return nullptr;
+  }
+
+  // Check if we already know about the frame.
+  FrameMap::const_iterator it = frames_.find(frame_id);
+  if (it != frames_.end())
+    return it->second;
+
+  if (render_view()->GetWebView()) {
+    // Check if the frame exists but we don't know about it yet.
+    for (blink::WebFrame* frame = render_view()->GetWebView()->MainFrame();
+         frame; frame = frame->TraverseNext()) {
+      if (frame->IsWebLocalFrame() &&
+          render_frame_util::GetIdentifier(frame->ToWebLocalFrame()) ==
+              frame_id) {
+        return GetWebFrameImpl(frame->ToWebLocalFrame());
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+void CefBrowserImpl::AddFrameObject(int64_t frame_id,
+                                    CefTrackNode* tracked_object) {
+  CefRefPtr<CefTrackManager> manager;
+
+  if (!frame_objects_.empty()) {
+    FrameObjectMap::const_iterator it = frame_objects_.find(frame_id);
+    if (it != frame_objects_.end())
+      manager = it->second;
+  }
+
+  if (!manager.get()) {
+    manager = new CefTrackManager();
+    frame_objects_.insert(std::make_pair(frame_id, manager));
+  }
+
+  manager->Add(tracked_object);
+}
+
+// RenderViewObserver methods.
+// -----------------------------------------------------------------------------
+
+void CefBrowserImpl::OnDestruct() {
+  // Notify that the browser window has been destroyed.
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
+    if (handler.get())
+      handler->OnBrowserDestroyed(this);
+  }
+
+  CefContentRendererClient::Get()->OnBrowserDestroyed(this);
+}
+
+void CefBrowserImpl::FrameDetached(int64_t frame_id) {
+  if (!frames_.empty()) {
+    // Remove the frame from the map.
+    FrameMap::iterator it = frames_.find(frame_id);
+    if (it != frames_.end()) {
+      frames_.erase(it);
+    }
+  }
+
+  if (!frame_objects_.empty()) {
+    // Remove any tracked objects associated with the frame.
+    FrameObjectMap::iterator it = frame_objects_.find(frame_id);
+    if (it != frame_objects_.end())
+      frame_objects_.erase(it);
+  }
+}
+
+void CefBrowserImpl::OnLoadingStateChange(bool isLoading) {
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
+    if (handler.get()) {
+      CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
+      if (load_handler.get()) {
+        blink::WebView* web_view = render_view()->GetWebView();
+        const bool canGoBack = blink_glue::CanGoBack(web_view);
+        const bool canGoForward = blink_glue::CanGoForward(web_view);
+
+        // Don't call OnLoadingStateChange multiple times with the same status.
+        // This can occur in cases where there are multiple highest-level
+        // LocalFrames in-process for the same browser.
+        if (last_loading_state_ &&
+            last_loading_state_->IsMatch(isLoading, canGoBack, canGoForward)) {
+          return;
+        }
+
+        load_handler->OnLoadingStateChange(this, isLoading, canGoBack,
+                                           canGoForward);
+        last_loading_state_.reset(
+            new LoadingState(isLoading, canGoBack, canGoForward));
+      }
+    }
+  }
+}
diff --git a/src/libcef/renderer/browser_impl.h b/src/libcef/renderer/browser_impl.h
new file mode 100644
index 0000000..99d343c
--- /dev/null
+++ b/src/libcef/renderer/browser_impl.h
@@ -0,0 +1,128 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_BROWSER_IMPL_H_
+#define CEF_LIBCEF_RENDERER_BROWSER_IMPL_H_
+#pragma once
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef/common/tracker.h"
+#include "libcef/renderer/frame_impl.h"
+
+#include "content/public/renderer/render_view_observer.h"
+
+namespace blink {
+class WebFrame;
+class WebNode;
+}  // namespace blink
+
+// Renderer plumbing for CEF features. There is a one-to-one relationship
+// between RenderView on the renderer side and RenderViewHost on the browser
+// side.
+//
+// RenderViewObserver: Interface for observing RenderView notifications.
+class CefBrowserImpl : public CefBrowser, public content::RenderViewObserver {
+ public:
+  // Returns the browser associated with the specified RenderView.
+  static CefRefPtr<CefBrowserImpl> GetBrowserForView(content::RenderView* view);
+  // Returns the browser associated with the specified main WebFrame.
+  static CefRefPtr<CefBrowserImpl> GetBrowserForMainFrame(
+      blink::WebFrame* frame);
+
+  // CefBrowser methods.
+  CefRefPtr<CefBrowserHost> GetHost() override;
+  bool CanGoBack() override;
+  void GoBack() override;
+  bool CanGoForward() override;
+  void GoForward() override;
+  bool IsLoading() override;
+  void Reload() override;
+  void ReloadIgnoreCache() override;
+  void StopLoad() override;
+  int GetIdentifier() override;
+  bool IsSame(CefRefPtr<CefBrowser> that) override;
+  bool IsPopup() override;
+  bool HasDocument() override;
+  CefRefPtr<CefFrame> GetMainFrame() override;
+  CefRefPtr<CefFrame> GetFocusedFrame() override;
+  CefRefPtr<CefFrame> GetFrame(int64 identifier) override;
+  CefRefPtr<CefFrame> GetFrame(const CefString& name) override;
+  size_t GetFrameCount() override;
+  void GetFrameIdentifiers(std::vector<int64>& identifiers) override;
+  void GetFrameNames(std::vector<CefString>& names) override;
+
+  CefBrowserImpl(content::RenderView* render_view,
+                 int browser_id,
+                 bool is_popup,
+                 bool is_windowless);
+  ~CefBrowserImpl() override;
+
+  // Returns the matching CefFrameImpl reference or creates a new one.
+  CefRefPtr<CefFrameImpl> GetWebFrameImpl(blink::WebLocalFrame* frame);
+  CefRefPtr<CefFrameImpl> GetWebFrameImpl(int64_t frame_id);
+
+  // Frame objects will be deleted immediately before the frame is closed.
+  void AddFrameObject(int64_t frame_id, CefTrackNode* tracked_object);
+
+  int browser_id() const { return browser_id_; }
+  bool is_popup() const { return is_popup_; }
+  bool is_windowless() const { return is_windowless_; }
+  content::RenderView* render_view() const {
+    return content::RenderViewObserver::render_view();
+  }
+
+  // RenderViewObserver methods.
+  void OnDestruct() override;
+  void FrameDetached(int64_t frame_id);
+
+  void OnLoadingStateChange(bool isLoading);
+
+ private:
+  // ID of the browser that this RenderView is associated with. During loading
+  // of cross-origin requests multiple RenderViews may be associated with the
+  // same browser ID.
+  int browser_id_;
+  bool is_popup_;
+  bool is_windowless_;
+
+  // Map of unique frame ids to CefFrameImpl references.
+  typedef std::map<int64, CefRefPtr<CefFrameImpl>> FrameMap;
+  FrameMap frames_;
+
+  // Map of unique frame ids to CefTrackManager objects that need to be cleaned
+  // up when the frame is deleted.
+  typedef std::map<int64, CefRefPtr<CefTrackManager>> FrameObjectMap;
+  FrameObjectMap frame_objects_;
+
+  struct LoadingState {
+    LoadingState(bool is_loading, bool can_go_back, bool can_go_forward)
+        : is_loading_(is_loading),
+          can_go_back_(can_go_back),
+          can_go_forward_(can_go_forward) {}
+
+    bool IsMatch(bool is_loading, bool can_go_back, bool can_go_forward) const {
+      return is_loading_ == is_loading && can_go_back_ == can_go_back &&
+             can_go_forward_ == can_go_forward;
+    }
+
+    bool is_loading_;
+    bool can_go_back_;
+    bool can_go_forward_;
+  };
+  std::unique_ptr<LoadingState> last_loading_state_;
+
+  IMPLEMENT_REFCOUNTING(CefBrowserImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserImpl);
+};
+
+#endif  // CEF_LIBCEF_RENDERER_BROWSER_IMPL_H_
diff --git a/src/libcef/renderer/content_renderer_client.cc b/src/libcef/renderer/content_renderer_client.cc
new file mode 100644
index 0000000..8115e07
--- /dev/null
+++ b/src/libcef/renderer/content_renderer_client.cc
@@ -0,0 +1,830 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/content_renderer_client.h"
+
+#include <utility>
+
+#include "base/compiler_specific.h"
+
+// Enable deprecation warnings on Windows. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wdeprecated-declarations"
+#else
+#pragma warning(push)
+#pragma warning(default : 4996)
+#endif
+#endif
+
+#include "libcef/browser/content_browser_client.h"
+#include "libcef/browser/context.h"
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/content_client.h"
+#include "libcef/common/extensions/extensions_client.h"
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/values_impl.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/browser_impl.h"
+#include "libcef/renderer/extensions/extensions_renderer_client.h"
+#include "libcef/renderer/extensions/print_render_frame_helper_delegate.h"
+#include "libcef/renderer/render_frame_observer.h"
+#include "libcef/renderer/render_thread_observer.h"
+#include "libcef/renderer/thread_util.h"
+#include "libcef/renderer/url_loader_throttle_provider_impl.h"
+#include "libcef/renderer/v8_impl.h"
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/path_service.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/renderer/browser_exposed_renderer_interfaces.h"
+#include "chrome/renderer/chrome_content_renderer_client.h"
+#include "chrome/renderer/extensions/chrome_extensions_renderer_client.h"
+#include "chrome/renderer/loadtimes_extension_bindings.h"
+#include "chrome/renderer/media/chrome_key_systems.h"
+#include "chrome/renderer/pepper/chrome_pdf_print_client.h"
+#include "chrome/renderer/pepper/pepper_helper.h"
+#include "chrome/renderer/plugins/chrome_plugin_placeholder.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "components/nacl/common/nacl_constants.h"
+#include "components/printing/renderer/print_render_frame_helper.h"
+#include "components/spellcheck/renderer/spellcheck.h"
+#include "components/spellcheck/renderer/spellcheck_provider.h"
+#include "components/visitedlink/renderer/visitedlink_reader.h"
+#include "components/web_cache/renderer/web_cache_impl.h"
+#include "content/common/frame_messages.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/child/child_thread.h"
+#include "content/public/common/content_constants.h"
+#include "content/public/common/content_paths.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/renderer/plugin_instance_throttler.h"
+#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/render_view_visitor.h"
+#include "content/renderer/render_widget.h"
+#include "extensions/common/switches.h"
+#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h"
+#include "extensions/renderer/renderer_extension_registry.h"
+#include "ipc/ipc_sync_channel.h"
+#include "media/base/media.h"
+#include "mojo/public/cpp/bindings/binder_map.h"
+#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
+#include "printing/print_settings.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/scheduler/web_renderer_process_type.h"
+#include "third_party/blink/public/platform/url_conversion.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/web/web_console_message.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_frame.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_prerenderer_client.h"
+#include "third_party/blink/public/web/web_security_policy.h"
+#include "third_party/blink/public/web/web_view.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#include "base/strings/sys_string_conversions.h"
+#endif
+
+namespace {
+
+// Stub implementation of blink::WebPrerendererClient.
+class CefPrerendererClient : public content::RenderViewObserver,
+                             public blink::WebPrerendererClient {
+ public:
+  explicit CefPrerendererClient(content::RenderView* render_view)
+      : content::RenderViewObserver(render_view) {
+    DCHECK(render_view);
+    render_view->GetWebView()->SetPrerendererClient(this);
+  }
+
+ private:
+  ~CefPrerendererClient() override {}
+
+  // RenderViewObserver methods:
+  void OnDestruct() override { delete this; }
+
+  // WebPrerendererClient methods:
+  bool IsPrefetchOnly() override { return false; }
+};
+
+bool IsStandaloneExtensionProcess() {
+  return extensions::ExtensionsEnabled() &&
+         extensions::CefExtensionsRendererClient::
+             IsStandaloneExtensionProcess();
+}
+
+}  // namespace
+
+// Placeholder object for guest views.
+class CefGuestView : public content::RenderViewObserver {
+ public:
+  CefGuestView(content::RenderView* render_view, bool is_windowless)
+      : content::RenderViewObserver(render_view),
+        is_windowless_(is_windowless) {}
+
+  bool is_windowless() const { return is_windowless_; }
+
+ private:
+  // RenderViewObserver methods.
+  void OnDestruct() override {
+    CefContentRendererClient::Get()->OnGuestViewDestroyed(this);
+  }
+
+  const bool is_windowless_;
+};
+
+CefContentRendererClient::CefContentRendererClient()
+    : main_entry_time_(base::TimeTicks::Now()),
+      devtools_agent_count_(0),
+      uncaught_exception_stack_size_(0),
+      single_process_cleanup_complete_(false) {
+  if (extensions::ExtensionsEnabled()) {
+    extensions_client_.reset(new extensions::CefExtensionsClient);
+    extensions::ExtensionsClient::Set(extensions_client_.get());
+    extensions_renderer_client_.reset(
+        new extensions::CefExtensionsRendererClient);
+    extensions::ExtensionsRendererClient::Set(
+        extensions_renderer_client_.get());
+  }
+}
+
+CefContentRendererClient::~CefContentRendererClient() {}
+
+// static
+CefContentRendererClient* CefContentRendererClient::Get() {
+  return static_cast<CefContentRendererClient*>(
+      CefContentClient::Get()->renderer());
+}
+
+CefRefPtr<CefBrowserImpl> CefContentRendererClient::GetBrowserForView(
+    content::RenderView* view) {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  BrowserMap::const_iterator it = browsers_.find(view);
+  if (it != browsers_.end())
+    return it->second;
+  return nullptr;
+}
+
+CefRefPtr<CefBrowserImpl> CefContentRendererClient::GetBrowserForMainFrame(
+    blink::WebFrame* frame) {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  BrowserMap::const_iterator it = browsers_.begin();
+  for (; it != browsers_.end(); ++it) {
+    content::RenderView* render_view = it->second->render_view();
+    if (render_view && render_view->GetWebView() &&
+        render_view->GetWebView()->MainFrame() == frame) {
+      return it->second;
+    }
+  }
+
+  return nullptr;
+}
+
+void CefContentRendererClient::OnBrowserDestroyed(CefBrowserImpl* browser) {
+  BrowserMap::iterator it = browsers_.begin();
+  for (; it != browsers_.end(); ++it) {
+    if (it->second.get() == browser) {
+      browsers_.erase(it);
+      return;
+    }
+  }
+
+  // No browser was found in the map.
+  NOTREACHED();
+}
+
+CefGuestView* CefContentRendererClient::GetGuestViewForView(
+    content::RenderView* view) {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  GuestViewMap::const_iterator it = guest_views_.find(view);
+  if (it != guest_views_.end())
+    return it->second.get();
+  return nullptr;
+}
+
+void CefContentRendererClient::OnGuestViewDestroyed(CefGuestView* guest_view) {
+  GuestViewMap::iterator it = guest_views_.begin();
+  for (; it != guest_views_.end(); ++it) {
+    if (it->second.get() == guest_view) {
+      guest_views_.erase(it);
+      return;
+    }
+  }
+
+  // No guest view was found in the map.
+  NOTREACHED();
+}
+
+blink::WebURLLoaderFactory*
+CefContentRendererClient::GetDefaultURLLoaderFactory() {
+  if (!default_url_loader_factory_) {
+    default_url_loader_factory_ =
+        blink::Platform::Current()->CreateDefaultURLLoaderFactory();
+  }
+  return default_url_loader_factory_.get();
+}
+
+void CefContentRendererClient::WebKitInitialized() {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+
+  // Create global objects associated with the default Isolate.
+  CefV8IsolateCreated();
+
+  // TODO(cef): Enable these once the implementation supports it.
+  blink::WebRuntimeFeatures::EnableNotifications(false);
+
+  const CefContentClient::SchemeInfoList* schemes =
+      CefContentClient::Get()->GetCustomSchemes();
+  if (!schemes->empty()) {
+    // Register the custom schemes. The |is_standard| value is excluded here
+    // because it's not explicitly registered with Blink.
+    CefContentClient::SchemeInfoList::const_iterator it = schemes->begin();
+    for (; it != schemes->end(); ++it) {
+      const CefContentClient::SchemeInfo& info = *it;
+      const blink::WebString& scheme =
+          blink::WebString::FromUTF8(info.scheme_name);
+      if (info.is_local)
+        blink_glue::RegisterURLSchemeAsLocal(scheme);
+      if (info.is_display_isolated)
+        blink::WebSecurityPolicy::RegisterURLSchemeAsDisplayIsolated(scheme);
+      if (info.is_secure)
+        blink_glue::RegisterURLSchemeAsSecure(scheme);
+      if (info.is_fetch_enabled)
+        blink_glue::RegisterURLSchemeAsSupportingFetchAPI(scheme);
+    }
+  }
+
+  if (!cross_origin_whitelist_entries_.empty()) {
+    // Add the cross-origin white list entries.
+    for (size_t i = 0; i < cross_origin_whitelist_entries_.size(); ++i) {
+      const Cef_CrossOriginWhiteListEntry_Params& entry =
+          cross_origin_whitelist_entries_[i];
+      GURL gurl = GURL(entry.source_origin);
+      blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
+          gurl, blink::WebString::FromUTF8(entry.target_protocol),
+          blink::WebString::FromUTF8(entry.target_domain),
+          /*destination_port=*/0,
+          entry.allow_target_subdomains
+              ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
+              : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+          network::mojom::CorsPortMatchMode::kAllowAnyPort,
+          network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
+    }
+    cross_origin_whitelist_entries_.clear();
+  }
+
+  // The number of stack trace frames to capture for uncaught exceptions.
+  if (command_line->HasSwitch(switches::kUncaughtExceptionStackSize)) {
+    int uncaught_exception_stack_size = 0;
+    base::StringToInt(command_line->GetSwitchValueASCII(
+                          switches::kUncaughtExceptionStackSize),
+                      &uncaught_exception_stack_size);
+
+    if (uncaught_exception_stack_size > 0) {
+      uncaught_exception_stack_size_ = uncaught_exception_stack_size;
+      CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
+    }
+  }
+
+  // Notify the render process handler.
+  CefRefPtr<CefApp> application = CefContentClient::Get()->application();
+  if (application.get()) {
+    CefRefPtr<CefRenderProcessHandler> handler =
+        application->GetRenderProcessHandler();
+    if (handler.get())
+      handler->OnWebKitInitialized();
+  }
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+CefContentRendererClient::GetCurrentTaskRunner() {
+  // Check if currently on the render thread.
+  if (CEF_CURRENTLY_ON_RT())
+    return render_task_runner_;
+  return nullptr;
+}
+
+void CefContentRendererClient::RunSingleProcessCleanup() {
+  DCHECK(content::RenderProcessHost::run_renderer_in_process());
+
+  // Make sure the render thread was actually started.
+  if (!render_task_runner_.get())
+    return;
+
+  if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
+    RunSingleProcessCleanupOnUIThread();
+  } else {
+    base::PostTask(
+        FROM_HERE, {content::BrowserThread::UI},
+        base::Bind(&CefContentRendererClient::RunSingleProcessCleanupOnUIThread,
+                   base::Unretained(this)));
+  }
+
+  // Wait for the render thread cleanup to complete. Spin instead of using
+  // base::WaitableEvent because calling Wait() is not allowed on the UI
+  // thread.
+  bool complete = false;
+  do {
+    {
+      base::AutoLock lock_scope(single_process_cleanup_lock_);
+      complete = single_process_cleanup_complete_;
+    }
+    if (!complete)
+      base::PlatformThread::YieldCurrentThread();
+  } while (!complete);
+}
+
+void CefContentRendererClient::RenderThreadStarted() {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+
+  render_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+  observer_ = std::make_unique<CefRenderThreadObserver>();
+  web_cache_impl_ = std::make_unique<web_cache::WebCacheImpl>();
+  visited_link_slave_ = std::make_unique<visitedlink::VisitedLinkReader>();
+
+  content::RenderThread* thread = content::RenderThread::Get();
+
+  thread->SetRendererProcessType(
+      IsStandaloneExtensionProcess()
+          ? blink::scheduler::WebRendererProcessType::kExtensionRenderer
+          : blink::scheduler::WebRendererProcessType::kRenderer);
+
+  thread->AddObserver(observer_.get());
+
+  if (!command_line->HasSwitch(switches::kDisableSpellChecking)) {
+    spellcheck_ = std::make_unique<SpellCheck>(this);
+  }
+
+  if (content::RenderProcessHost::run_renderer_in_process()) {
+    // When running in single-process mode register as a destruction observer
+    // on the render thread's MessageLoop.
+    base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
+  }
+
+#if defined(OS_MACOSX)
+  {
+    base::ScopedCFTypeRef<CFStringRef> key(
+        base::SysUTF8ToCFStringRef("NSScrollViewRubberbanding"));
+    base::ScopedCFTypeRef<CFStringRef> value;
+
+    // If the command-line switch is specified then set the value that will be
+    // checked in RenderThreadImpl::Init(). Otherwise, remove the application-
+    // level value.
+    if (command_line->HasSwitch(switches::kDisableScrollBounce))
+      value.reset(base::SysUTF8ToCFStringRef("false"));
+
+    CFPreferencesSetAppValue(key, value, kCFPreferencesCurrentApplication);
+    CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
+  }
+#endif  // defined(OS_MACOSX)
+
+  if (extensions::PdfExtensionEnabled()) {
+    pdf_print_client_.reset(new ChromePDFPrintClient());
+    pdf::PepperPDFHost::SetPrintClient(pdf_print_client_.get());
+  }
+
+  for (auto& origin_or_hostname_pattern :
+       network::SecureOriginAllowlist::GetInstance().GetCurrentAllowlist()) {
+    blink::WebSecurityPolicy::AddOriginToTrustworthySafelist(
+        blink::WebString::FromUTF8(origin_or_hostname_pattern));
+  }
+
+  if (extensions::ExtensionsEnabled())
+    extensions_renderer_client_->RenderThreadStarted();
+}
+
+void CefContentRendererClient::ExposeInterfacesToBrowser(
+    mojo::BinderMap* binders) {
+  auto task_runner = base::SequencedTaskRunnerHandle::Get();
+
+  binders->Add(base::BindRepeating(&web_cache::WebCacheImpl::BindReceiver,
+                                   base::Unretained(web_cache_impl_.get())),
+               task_runner);
+
+  binders->Add(visited_link_slave_->GetBindCallback(), task_runner);
+
+  if (spellcheck_) {
+    binders->Add(
+        base::BindRepeating(
+            [](SpellCheck* spellcheck,
+               mojo::PendingReceiver<spellcheck::mojom::SpellChecker>
+                   receiver) { spellcheck->BindReceiver(std::move(receiver)); },
+            base::Unretained(spellcheck_.get())),
+        task_runner);
+  }
+}
+
+void CefContentRendererClient::RenderThreadConnected() {
+  content::RenderThread* thread = content::RenderThread::Get();
+
+  // Retrieve the new render thread information synchronously.
+  CefProcessHostMsg_GetNewRenderThreadInfo_Params params;
+  thread->Send(new CefProcessHostMsg_GetNewRenderThreadInfo(&params));
+
+  // Cross-origin entries need to be added after WebKit is initialized.
+  cross_origin_whitelist_entries_ = params.cross_origin_whitelist_entries;
+
+  // Notify the render process handler.
+  CefRefPtr<CefApp> application = CefContentClient::Get()->application();
+  if (application.get()) {
+    CefRefPtr<CefRenderProcessHandler> handler =
+        application->GetRenderProcessHandler();
+    if (handler.get()) {
+      CefRefPtr<CefListValueImpl> listValuePtr(
+          new CefListValueImpl(&params.extra_info, false, true));
+      handler->OnRenderThreadCreated(listValuePtr.get());
+      listValuePtr->Detach(nullptr);
+    }
+  }
+
+  // Register extensions last because it will trigger WebKit initialization.
+  thread->RegisterExtension(extensions_v8::LoadTimesExtension::Get());
+
+  WebKitInitialized();
+}
+
+void CefContentRendererClient::RenderFrameCreated(
+    content::RenderFrame* render_frame) {
+  CefRenderFrameObserver* render_frame_observer =
+      new CefRenderFrameObserver(render_frame);
+  service_manager::BinderRegistry* registry = render_frame_observer->registry();
+
+  new PepperHelper(render_frame);
+
+  if (extensions::ExtensionsEnabled()) {
+    extensions_renderer_client_->RenderFrameCreated(render_frame, registry);
+
+    blink::AssociatedInterfaceRegistry* associated_interfaces =
+        render_frame_observer->associated_interfaces();
+    associated_interfaces->AddInterface(base::BindRepeating(
+        &extensions::MimeHandlerViewContainerManager::BindReceiver,
+        render_frame->GetRoutingID()));
+  }
+
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kDisableSpellChecking)) {
+    new SpellCheckProvider(render_frame, spellcheck_.get(), this);
+  }
+
+  base::Optional<bool> is_windowless;
+
+  auto browser = MaybeCreateBrowser(render_frame->GetRenderView(), render_frame,
+                                    &is_windowless);
+  if (browser) {
+    // Attach the frame to the observer for message routing purposes.
+    render_frame_observer->AttachFrame(
+        browser->GetWebFrameImpl(render_frame->GetWebFrame()).get());
+  }
+
+  if (is_windowless.has_value()) {
+    new printing::PrintRenderFrameHelper(
+        render_frame,
+        base::WrapUnique(
+            new extensions::CefPrintRenderFrameHelperDelegate(*is_windowless)));
+  }
+}
+
+void CefContentRendererClient::RenderViewCreated(
+    content::RenderView* render_view) {
+  new CefPrerendererClient(render_view);
+
+  MaybeCreateBrowser(render_view, render_view->GetMainRenderFrame(), nullptr);
+}
+
+bool CefContentRendererClient::IsPluginHandledExternally(
+    content::RenderFrame* render_frame,
+    const blink::WebElement& plugin_element,
+    const GURL& original_url,
+    const std::string& mime_type) {
+  if (!extensions::ExtensionsEnabled())
+    return false;
+
+  DCHECK(plugin_element.HasHTMLTagName("object") ||
+         plugin_element.HasHTMLTagName("embed"));
+  // Blink will next try to load a WebPlugin which would end up in
+  // OverrideCreatePlugin, sending another IPC only to find out the plugin is
+  // not supported. Here it suffices to return false but there should perhaps be
+  // a more unified approach to avoid sending the IPC twice.
+  chrome::mojom::PluginInfoPtr plugin_info = chrome::mojom::PluginInfo::New();
+  ChromeContentRendererClient::GetPluginInfoHost()->GetPluginInfo(
+      render_frame->GetRoutingID(), original_url,
+      render_frame->GetWebFrame()->Top()->GetSecurityOrigin(), mime_type,
+      &plugin_info);
+  // TODO(ekaramad): Not continuing here due to a disallowed status should take
+  // us to CreatePlugin. See if more in depths investigation of |status| is
+  // necessary here (see https://crbug.com/965747). For now, returning false
+  // should take us to CreatePlugin after HTMLPlugInElement which is called
+  // through HTMLPlugInElement::LoadPlugin code path.
+  if (plugin_info->status != chrome::mojom::PluginStatus::kAllowed &&
+      plugin_info->status !=
+          chrome::mojom::PluginStatus::kPlayImportantContent) {
+    // We could get here when a MimeHandlerView is loaded inside a <webview>
+    // which is using permissions API (see WebViewPluginTests).
+    ChromeExtensionsRendererClient::DidBlockMimeHandlerViewForDisallowedPlugin(
+        plugin_element);
+    return false;
+  }
+  return ChromeExtensionsRendererClient::MaybeCreateMimeHandlerView(
+      plugin_element, original_url, plugin_info->actual_mime_type,
+      plugin_info->plugin);
+}
+
+bool CefContentRendererClient::OverrideCreatePlugin(
+    content::RenderFrame* render_frame,
+    const blink::WebPluginParams& params,
+    blink::WebPlugin** plugin) {
+  std::string orig_mime_type = params.mime_type.Utf8();
+  if (extensions::ExtensionsEnabled() &&
+      !extensions_renderer_client_->OverrideCreatePlugin(render_frame,
+                                                         params)) {
+    return false;
+  }
+
+  GURL url(params.url);
+  chrome::mojom::PluginInfoPtr plugin_info = chrome::mojom::PluginInfo::New();
+  ChromeContentRendererClient::GetPluginInfoHost()->GetPluginInfo(
+      render_frame->GetRoutingID(), url,
+      render_frame->GetWebFrame()->Top()->GetSecurityOrigin(), orig_mime_type,
+      &plugin_info);
+  *plugin = ChromeContentRendererClient::CreatePlugin(render_frame, params,
+                                                      *plugin_info);
+  return true;
+}
+
+void CefContentRendererClient::WillSendRequest(
+    blink::WebLocalFrame* frame,
+    ui::PageTransition transition_type,
+    const blink::WebURL& url,
+    const net::SiteForCookies& site_for_cookies,
+    const url::Origin* initiator_origin,
+    GURL* new_url,
+    bool* attach_same_site_cookies) {
+  if (extensions::ExtensionsEnabled()) {
+    extensions_renderer_client_->WillSendRequest(
+        frame, transition_type, url, site_for_cookies, initiator_origin,
+        new_url, attach_same_site_cookies);
+    if (!new_url->is_empty())
+      return;
+  }
+}
+
+uint64_t CefContentRendererClient::VisitedLinkHash(const char* canonical_url,
+                                                   size_t length) {
+  return visited_link_slave_->ComputeURLFingerprint(canonical_url, length);
+}
+
+bool CefContentRendererClient::IsLinkVisited(uint64_t link_hash) {
+  return visited_link_slave_->IsVisited(link_hash);
+}
+
+bool CefContentRendererClient::IsOriginIsolatedPepperPlugin(
+    const base::FilePath& plugin_path) {
+  return plugin_path ==
+         base::FilePath::FromUTF8Unsafe(CefContentClient::kPDFPluginPath);
+}
+
+content::BrowserPluginDelegate*
+CefContentRendererClient::CreateBrowserPluginDelegate(
+    content::RenderFrame* render_frame,
+    const content::WebPluginInfo& info,
+    const std::string& mime_type,
+    const GURL& original_url) {
+  DCHECK(extensions::ExtensionsEnabled());
+  return extensions::CefExtensionsRendererClient::CreateBrowserPluginDelegate(
+      render_frame, info, mime_type, original_url);
+}
+
+void CefContentRendererClient::AddSupportedKeySystems(
+    std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems) {
+  AddChromeKeySystems(key_systems);
+}
+
+void CefContentRendererClient::RunScriptsAtDocumentStart(
+    content::RenderFrame* render_frame) {
+  if (extensions::ExtensionsEnabled())
+    extensions_renderer_client_->RunScriptsAtDocumentStart(render_frame);
+}
+
+void CefContentRendererClient::RunScriptsAtDocumentEnd(
+    content::RenderFrame* render_frame) {
+  if (extensions::ExtensionsEnabled())
+    extensions_renderer_client_->RunScriptsAtDocumentEnd(render_frame);
+}
+
+void CefContentRendererClient::RunScriptsAtDocumentIdle(
+    content::RenderFrame* render_frame) {
+  if (extensions::ExtensionsEnabled())
+    extensions_renderer_client_->RunScriptsAtDocumentIdle(render_frame);
+}
+
+void CefContentRendererClient::DevToolsAgentAttached() {
+  // WebWorkers may be creating agents on a different thread.
+  if (!render_task_runner_->BelongsToCurrentThread()) {
+    render_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CefContentRendererClient::DevToolsAgentAttached,
+                       base::Unretained(this)));
+    return;
+  }
+
+  ++devtools_agent_count_;
+}
+
+void CefContentRendererClient::DevToolsAgentDetached() {
+  // WebWorkers may be creating agents on a different thread.
+  if (!render_task_runner_->BelongsToCurrentThread()) {
+    render_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CefContentRendererClient::DevToolsAgentDetached,
+                       base::Unretained(this)));
+    return;
+  }
+
+  --devtools_agent_count_;
+  if (devtools_agent_count_ == 0 && uncaught_exception_stack_size_ > 0) {
+    // When the last DevToolsAgent is detached the stack size is set to 0.
+    // Restore the user-specified stack size here.
+    CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
+  }
+}
+
+std::unique_ptr<content::URLLoaderThrottleProvider>
+CefContentRendererClient::CreateURLLoaderThrottleProvider(
+    content::URLLoaderThrottleProviderType provider_type) {
+  return std::make_unique<CefURLLoaderThrottleProviderImpl>(provider_type);
+}
+
+bool CefContentRendererClient::RequiresWebComponentsV0(const GURL& url) {
+  // TODO(1025782): For now, file:// URLs are allowed to access Web Components
+  // v0 features. This will be removed once origin trials support file:// URLs
+  // for this purpose.
+  return url.SchemeIs(content::kChromeUIScheme) || url.SchemeIs("file");
+}
+
+void CefContentRendererClient::GetInterface(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  // TODO(crbug.com/977637): Get rid of the use of this implementation of
+  // |service_manager::LocalInterfaceProvider|. This was done only to avoid
+  // churning spellcheck code while eliminating the "chrome" and
+  // "chrome_renderer" services. Spellcheck is (and should remain) the only
+  // consumer of this implementation.
+  content::RenderThread::Get()->BindHostReceiver(
+      mojo::GenericPendingReceiver(interface_name, std::move(interface_pipe)));
+}
+
+void CefContentRendererClient::WillDestroyCurrentMessageLoop() {
+  base::AutoLock lock_scope(single_process_cleanup_lock_);
+  single_process_cleanup_complete_ = true;
+}
+
+CefRefPtr<CefBrowserImpl> CefContentRendererClient::MaybeCreateBrowser(
+    content::RenderView* render_view,
+    content::RenderFrame* render_frame,
+    base::Optional<bool>* is_windowless) {
+  if (!render_view || !render_frame)
+    return nullptr;
+
+  // Don't create another browser or guest view object if one already exists for
+  // the view.
+  auto browser = GetBrowserForView(render_view);
+  if (browser) {
+    if (is_windowless) {
+      *is_windowless = browser->is_windowless();
+    }
+    return browser;
+  }
+
+  auto guest_view = GetGuestViewForView(render_view);
+  if (guest_view) {
+    if (is_windowless) {
+      *is_windowless = guest_view->is_windowless();
+    }
+    return nullptr;
+  }
+
+  const int render_frame_routing_id = render_frame->GetRoutingID();
+
+  // Retrieve the browser information synchronously. This will also register
+  // the routing ids with the browser info object in the browser process.
+  CefProcessHostMsg_GetNewBrowserInfo_Params params;
+  content::RenderThread::Get()->Send(new CefProcessHostMsg_GetNewBrowserInfo(
+      render_frame_routing_id, &params));
+
+  if (is_windowless) {
+    *is_windowless = params.is_windowless;
+  }
+
+  if (params.browser_id == 0) {
+    // The popup may have been canceled during creation.
+    return nullptr;
+  }
+
+  if (params.is_guest_view || params.browser_id < 0) {
+    // Don't create a CefBrowser for guest views, or if the new browser info
+    // response has timed out.
+    guest_views_.insert(std::make_pair(
+        render_view,
+        std::make_unique<CefGuestView>(render_view, params.is_windowless)));
+    return nullptr;
+  }
+
+#if defined(OS_MACOSX)
+  // FIXME: It would be better if this API would be a callback from the
+  // WebKit layer, or if it would be exposed as an WebView instance method; the
+  // current implementation uses a static variable, and WebKit needs to be
+  // patched in order to make it work for each WebView instance
+  render_view->GetWebView()->SetUseExternalPopupMenusThisInstance(
+      !params.is_windowless);
+#endif
+
+  browser = new CefBrowserImpl(render_view, params.browser_id, params.is_popup,
+                               params.is_windowless);
+  browsers_.insert(std::make_pair(render_view, browser));
+
+  // Notify the render process handler.
+  CefRefPtr<CefApp> application = CefContentClient::Get()->application();
+  if (application.get()) {
+    CefRefPtr<CefRenderProcessHandler> handler =
+        application->GetRenderProcessHandler();
+    if (handler.get()) {
+      CefRefPtr<CefDictionaryValueImpl> dictValuePtr(
+          new CefDictionaryValueImpl(&params.extra_info, false, true));
+      handler->OnBrowserCreated(browser.get(), dictValuePtr.get());
+      dictValuePtr->Detach(nullptr);
+    }
+  }
+
+  return browser;
+}
+
+void CefContentRendererClient::RunSingleProcessCleanupOnUIThread() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  // Clean up the single existing RenderProcessHost.
+  content::RenderProcessHost* host = nullptr;
+  content::RenderProcessHost::iterator iterator(
+      content::RenderProcessHost::AllHostsIterator());
+  if (!iterator.IsAtEnd()) {
+    host = iterator.GetCurrentValue();
+    host->Cleanup();
+    iterator.Advance();
+    DCHECK(iterator.IsAtEnd());
+  }
+  DCHECK(host);
+
+  // Clear the run_renderer_in_process() flag to avoid a DCHECK in the
+  // RenderProcessHost destructor.
+  content::RenderProcessHost::SetRunRendererInProcess(false);
+
+  // Deletion of the RenderProcessHost object will stop the render thread and
+  // result in a call to WillDestroyCurrentMessageLoop.
+  // Cleanup() will cause deletion to be posted as a task on the UI thread but
+  // this task will only execute when running in multi-threaded message loop
+  // mode (because otherwise the UI message loop has already stopped). Therefore
+  // we need to explicitly delete the object when not running in this mode.
+  if (!CefContext::Get()->settings().multi_threaded_message_loop)
+    delete host;
+}
+
+// Enable deprecation warnings on Windows. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic pop
+#else
+#pragma warning(pop)
+#endif
+#endif
diff --git a/src/libcef/renderer/content_renderer_client.h b/src/libcef/renderer/content_renderer_client.h
new file mode 100644
index 0000000..19e83d6
--- /dev/null
+++ b/src/libcef/renderer/content_renderer_client.h
@@ -0,0 +1,206 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_CONTENT_RENDERER_CLIENT_H_
+#define CEF_LIBCEF_RENDERER_CONTENT_RENDERER_CLIENT_H_
+#pragma once
+
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "libcef/renderer/browser_impl.h"
+
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/optional.h"
+#include "base/sequenced_task_runner.h"
+#include "chrome/common/plugin.mojom.h"
+#include "content/public/renderer/content_renderer_client.h"
+#include "content/public/renderer/render_thread.h"
+#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
+#include "services/service_manager/public/cpp/local_interface_provider.h"
+
+namespace blink {
+class WebURLLoaderFactory;
+}
+
+namespace extensions {
+class CefExtensionsRendererClient;
+class Dispatcher;
+class DispatcherDelegate;
+class ExtensionsClient;
+class ExtensionsGuestViewContainerDispatcher;
+class ExtensionsRendererClient;
+class ResourceRequestPolicy;
+}  // namespace extensions
+
+namespace visitedlink {
+class VisitedLinkReader;
+}
+
+namespace web_cache {
+class WebCacheImpl;
+}
+
+class CefGuestView;
+class CefRenderThreadObserver;
+struct Cef_CrossOriginWhiteListEntry_Params;
+class ChromePDFPrintClient;
+class SpellCheck;
+
+class CefContentRendererClient
+    : public content::ContentRendererClient,
+      public service_manager::LocalInterfaceProvider,
+      public base::MessageLoopCurrent::DestructionObserver {
+ public:
+  CefContentRendererClient();
+  ~CefContentRendererClient() override;
+
+  // Returns the singleton CefContentRendererClient instance.
+  static CefContentRendererClient* Get();
+
+  // Returns the browser associated with the specified RenderView.
+  CefRefPtr<CefBrowserImpl> GetBrowserForView(content::RenderView* view);
+
+  // Returns the browser associated with the specified main WebFrame.
+  CefRefPtr<CefBrowserImpl> GetBrowserForMainFrame(blink::WebFrame* frame);
+
+  // Called from CefBrowserImpl::OnDestruct().
+  void OnBrowserDestroyed(CefBrowserImpl* browser);
+
+  // Returns the guest view associated with the specified RenderView if any.
+  CefGuestView* GetGuestViewForView(content::RenderView* view);
+
+  // Called from CefGuestView::OnDestruct().
+  void OnGuestViewDestroyed(CefGuestView* guest_view);
+
+  // Render thread task runner.
+  base::SingleThreadTaskRunner* render_task_runner() const {
+    return render_task_runner_.get();
+  }
+
+  int uncaught_exception_stack_size() const {
+    return uncaught_exception_stack_size_;
+  }
+
+  // Returns a factory that only supports unintercepted http(s) and blob
+  // requests. Used by CefRenderURLRequest.
+  blink::WebURLLoaderFactory* GetDefaultURLLoaderFactory();
+
+  void WebKitInitialized();
+
+  // Returns the task runner for the current thread. Returns NULL if the current
+  // thread is not the main render process thread.
+  scoped_refptr<base::SingleThreadTaskRunner> GetCurrentTaskRunner();
+
+  // Perform cleanup work that needs to occur before shutdown when running in
+  // single-process mode. Blocks until cleanup is complete.
+  void RunSingleProcessCleanup();
+
+  // ContentRendererClient implementation.
+  void RenderThreadStarted() override;
+  void ExposeInterfacesToBrowser(mojo::BinderMap* binders) override;
+  void RenderThreadConnected() override;
+  void RenderFrameCreated(content::RenderFrame* render_frame) override;
+  void RenderViewCreated(content::RenderView* render_view) override;
+  bool IsPluginHandledExternally(content::RenderFrame* render_frame,
+                                 const blink::WebElement& plugin_element,
+                                 const GURL& original_url,
+                                 const std::string& mime_type) override;
+  bool OverrideCreatePlugin(content::RenderFrame* render_frame,
+                            const blink::WebPluginParams& params,
+                            blink::WebPlugin** plugin) override;
+  void WillSendRequest(blink::WebLocalFrame* frame,
+                       ui::PageTransition transition_type,
+                       const blink::WebURL& url,
+                       const net::SiteForCookies& site_for_cookies,
+                       const url::Origin* initiator_origin,
+                       GURL* new_url,
+                       bool* attach_same_site_cookies) override;
+  uint64_t VisitedLinkHash(const char* canonical_url, size_t length) override;
+  bool IsLinkVisited(uint64_t link_hash) override;
+  bool IsOriginIsolatedPepperPlugin(const base::FilePath& plugin_path) override;
+  content::BrowserPluginDelegate* CreateBrowserPluginDelegate(
+      content::RenderFrame* render_frame,
+      const content::WebPluginInfo& info,
+      const std::string& mime_type,
+      const GURL& original_url) override;
+  void AddSupportedKeySystems(
+      std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems)
+      override;
+  void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override;
+  void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
+  void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) override;
+  void DevToolsAgentAttached() override;
+  void DevToolsAgentDetached() override;
+  std::unique_ptr<content::URLLoaderThrottleProvider>
+  CreateURLLoaderThrottleProvider(
+      content::URLLoaderThrottleProviderType provider_type) override;
+  bool RequiresWebComponentsV0(const GURL& url) override;
+
+  // service_manager::LocalInterfaceProvider implementation.
+  void GetInterface(const std::string& name,
+                    mojo::ScopedMessagePipeHandle request_handle) override;
+
+  // MessageLoopCurrent::DestructionObserver implementation.
+  void WillDestroyCurrentMessageLoop() override;
+
+ private:
+  // Maybe create a new browser object, return the existing one, or return
+  // nullptr for guest views.
+  CefRefPtr<CefBrowserImpl> MaybeCreateBrowser(
+      content::RenderView* render_view,
+      content::RenderFrame* render_frame,
+      base::Optional<bool>* is_windowless);
+
+  // Perform cleanup work for single-process mode.
+  void RunSingleProcessCleanupOnUIThread();
+
+  // Time at which this object was created. This is very close to the time at
+  // which the RendererMain function was entered.
+  base::TimeTicks main_entry_time_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> render_task_runner_;
+  std::unique_ptr<CefRenderThreadObserver> observer_;
+  std::unique_ptr<web_cache::WebCacheImpl> web_cache_impl_;
+  std::unique_ptr<SpellCheck> spellcheck_;
+  std::unique_ptr<visitedlink::VisitedLinkReader> visited_link_slave_;
+
+  std::unique_ptr<blink::WebURLLoaderFactory> default_url_loader_factory_;
+
+  // Map of RenderView pointers to CefBrowserImpl references.
+  typedef std::map<content::RenderView*, CefRefPtr<CefBrowserImpl>> BrowserMap;
+  BrowserMap browsers_;
+
+  // Map of RenderView poiners to CefGuestView implementations.
+  typedef std::map<content::RenderView*, std::unique_ptr<CefGuestView>>
+      GuestViewMap;
+  GuestViewMap guest_views_;
+
+  // Cross-origin white list entries that need to be registered with WebKit.
+  typedef std::vector<Cef_CrossOriginWhiteListEntry_Params> CrossOriginList;
+  CrossOriginList cross_origin_whitelist_entries_;
+
+  std::unique_ptr<ChromePDFPrintClient> pdf_print_client_;
+
+  std::unique_ptr<extensions::ExtensionsClient> extensions_client_;
+  std::unique_ptr<extensions::CefExtensionsRendererClient>
+      extensions_renderer_client_;
+
+  int devtools_agent_count_;
+  int uncaught_exception_stack_size_;
+
+  // Used in single-process mode to test when cleanup is complete.
+  // Access must be protected by |single_process_cleanup_lock_|.
+  bool single_process_cleanup_complete_;
+  base::Lock single_process_cleanup_lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefContentRendererClient);
+};
+
+#endif  // CEF_LIBCEF_RENDERER_CONTENT_RENDERER_CLIENT_H_
diff --git a/src/libcef/renderer/dom_document_impl.cc b/src/libcef/renderer/dom_document_impl.cc
new file mode 100644
index 0000000..4685cff
--- /dev/null
+++ b/src/libcef/renderer/dom_document_impl.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/renderer/dom_document_impl.h"
+#include "libcef/renderer/dom_node_impl.h"
+#include "libcef/renderer/thread_util.h"
+
+#include "base/logging.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_node.h"
+#include "third_party/blink/public/web/web_range.h"
+
+using blink::WebDocument;
+using blink::WebElement;
+using blink::WebLocalFrame;
+using blink::WebNode;
+using blink::WebRange;
+using blink::WebString;
+using blink::WebURL;
+
+CefDOMDocumentImpl::CefDOMDocumentImpl(CefBrowserImpl* browser,
+                                       WebLocalFrame* frame)
+    : browser_(browser), frame_(frame) {
+  const WebDocument& document = frame_->GetDocument();
+  DCHECK(!document.IsNull());
+}
+
+CefDOMDocumentImpl::~CefDOMDocumentImpl() {
+  CEF_REQUIRE_RT();
+
+  // Verify that the Detach() method has been called.
+  DCHECK(frame_ == nullptr);
+}
+
+CefDOMDocumentImpl::Type CefDOMDocumentImpl::GetType() {
+  if (!VerifyContext())
+    return DOM_DOCUMENT_TYPE_UNKNOWN;
+
+  const WebDocument& document = frame_->GetDocument();
+  if (document.IsHTMLDocument())
+    return DOM_DOCUMENT_TYPE_HTML;
+  if (document.IsXHTMLDocument())
+    return DOM_DOCUMENT_TYPE_XHTML;
+  if (document.IsPluginDocument())
+    return DOM_DOCUMENT_TYPE_PLUGIN;
+  return DOM_DOCUMENT_TYPE_UNKNOWN;
+}
+
+CefRefPtr<CefDOMNode> CefDOMDocumentImpl::GetDocument() {
+  const WebDocument& document = frame_->GetDocument();
+  return GetOrCreateNode(document.GetDocument());
+}
+
+CefRefPtr<CefDOMNode> CefDOMDocumentImpl::GetBody() {
+  const WebDocument& document = frame_->GetDocument();
+  return GetOrCreateNode(document.Body());
+}
+
+CefRefPtr<CefDOMNode> CefDOMDocumentImpl::GetHead() {
+  WebDocument document = frame_->GetDocument();
+  return GetOrCreateNode(document.Head());
+}
+
+CefString CefDOMDocumentImpl::GetTitle() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  const WebDocument& document = frame_->GetDocument();
+  const WebString& title = document.Title();
+  if (!title.IsNull())
+    str = title.Utf16();
+
+  return str;
+}
+
+CefRefPtr<CefDOMNode> CefDOMDocumentImpl::GetElementById(const CefString& id) {
+  const WebDocument& document = frame_->GetDocument();
+  return GetOrCreateNode(
+      document.GetElementById(WebString::FromUTF16(id.ToString16())));
+}
+
+CefRefPtr<CefDOMNode> CefDOMDocumentImpl::GetFocusedNode() {
+  const WebDocument& document = frame_->GetDocument();
+  return GetOrCreateNode(document.FocusedElement());
+}
+
+bool CefDOMDocumentImpl::HasSelection() {
+  if (!VerifyContext())
+    return false;
+
+  return frame_->HasSelection();
+}
+
+int CefDOMDocumentImpl::GetSelectionStartOffset() {
+  if (!VerifyContext())
+    return 0;
+
+  if (!frame_->HasSelection())
+    return 0;
+
+  const WebRange& range = frame_->SelectionRange();
+  if (range.IsNull())
+    return 0;
+
+  return range.StartOffset();
+}
+
+int CefDOMDocumentImpl::GetSelectionEndOffset() {
+  if (!VerifyContext())
+    return 0;
+
+  if (!frame_->HasSelection())
+    return 0;
+
+  const WebRange& range = frame_->SelectionRange();
+  if (range.IsNull())
+    return 0;
+
+  return range.EndOffset();
+}
+
+CefString CefDOMDocumentImpl::GetSelectionAsMarkup() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  if (!frame_->HasSelection())
+    return str;
+
+  const WebString& markup = frame_->SelectionAsMarkup();
+  if (!markup.IsNull())
+    str = markup.Utf16();
+
+  return str;
+}
+
+CefString CefDOMDocumentImpl::GetSelectionAsText() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  if (!frame_->HasSelection())
+    return str;
+
+  const WebString& text = frame_->SelectionAsText();
+  if (!text.IsNull())
+    str = text.Utf16();
+
+  return str;
+}
+
+CefString CefDOMDocumentImpl::GetBaseURL() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  const WebDocument& document = frame_->GetDocument();
+  const WebURL& url = document.BaseURL();
+  if (!url.IsNull()) {
+    GURL gurl = url;
+    str = gurl.spec();
+  }
+
+  return str;
+}
+
+CefString CefDOMDocumentImpl::GetCompleteURL(const CefString& partialURL) {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  const WebDocument& document = frame_->GetDocument();
+  const WebURL& url =
+      document.CompleteURL(WebString::FromUTF16(partialURL.ToString16()));
+  if (!url.IsNull()) {
+    GURL gurl = url;
+    str = gurl.spec();
+  }
+
+  return str;
+}
+
+CefRefPtr<CefDOMNode> CefDOMDocumentImpl::GetOrCreateNode(
+    const blink::WebNode& node) {
+  if (!VerifyContext())
+    return nullptr;
+
+  // Nodes may potentially be null.
+  if (node.IsNull())
+    return nullptr;
+
+  if (!node_map_.empty()) {
+    // Locate the existing node, if any.
+    NodeMap::const_iterator it = node_map_.find(node);
+    if (it != node_map_.end())
+      return it->second;
+  }
+
+  // Create the new node object.
+  CefRefPtr<CefDOMNode> nodeImpl(new CefDOMNodeImpl(this, node));
+  node_map_.insert(std::make_pair(node, nodeImpl.get()));
+  return nodeImpl;
+}
+
+void CefDOMDocumentImpl::RemoveNode(const blink::WebNode& node) {
+  if (!VerifyContext())
+    return;
+
+  if (!node_map_.empty()) {
+    NodeMap::iterator it = node_map_.find(node);
+    if (it != node_map_.end())
+      node_map_.erase(it);
+  }
+}
+
+void CefDOMDocumentImpl::Detach() {
+  if (!VerifyContext())
+    return;
+
+  // If you hit this assert it means that you are keeping references to node
+  // objects beyond the valid scope.
+  DCHECK(node_map_.empty());
+
+  // If you hit this assert it means that you are keeping references to this
+  // document object beyond the valid scope.
+  DCHECK(HasOneRef());
+
+  if (!node_map_.empty()) {
+    NodeMap::const_iterator it = node_map_.begin();
+    for (; it != node_map_.end(); ++it)
+      static_cast<CefDOMNodeImpl*>(it->second)->Detach();
+    node_map_.clear();
+  }
+
+  frame_ = nullptr;
+}
+
+bool CefDOMDocumentImpl::VerifyContext() {
+  if (!CEF_CURRENTLY_ON_RT() || frame_ == nullptr) {
+    NOTREACHED();
+    return false;
+  }
+  return true;
+}
diff --git a/src/libcef/renderer/dom_document_impl.h b/src/libcef/renderer/dom_document_impl.h
new file mode 100644
index 0000000..85e50ee
--- /dev/null
+++ b/src/libcef/renderer/dom_document_impl.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DOM_DOCUMENT_IMPL_H_
+#define CEF_LIBCEF_DOM_DOCUMENT_IMPL_H_
+#pragma once
+
+#include <map>
+#include "include/cef_dom.h"
+
+namespace blink {
+class WebLocalFrame;
+class WebNode;
+}  // namespace blink
+
+class CefBrowserImpl;
+
+class CefDOMDocumentImpl : public CefDOMDocument {
+ public:
+  CefDOMDocumentImpl(CefBrowserImpl* browser, blink::WebLocalFrame* frame);
+  ~CefDOMDocumentImpl() override;
+
+  // CefDOMDocument methods.
+  Type GetType() override;
+  CefRefPtr<CefDOMNode> GetDocument() override;
+  CefRefPtr<CefDOMNode> GetBody() override;
+  CefRefPtr<CefDOMNode> GetHead() override;
+  CefString GetTitle() override;
+  CefRefPtr<CefDOMNode> GetElementById(const CefString& id) override;
+  CefRefPtr<CefDOMNode> GetFocusedNode() override;
+  bool HasSelection() override;
+  int GetSelectionStartOffset() override;
+  int GetSelectionEndOffset() override;
+  CefString GetSelectionAsMarkup() override;
+  CefString GetSelectionAsText() override;
+  CefString GetBaseURL() override;
+  CefString GetCompleteURL(const CefString& partialURL) override;
+
+  CefBrowserImpl* GetBrowser() { return browser_; }
+  blink::WebLocalFrame* GetFrame() { return frame_; }
+
+  // The document maintains a map of all existing node objects.
+  CefRefPtr<CefDOMNode> GetOrCreateNode(const blink::WebNode& node);
+  void RemoveNode(const blink::WebNode& node);
+
+  // Must be called before the object is destroyed.
+  void Detach();
+
+  // Verify that the object exists and is being accessed on the UI thread.
+  bool VerifyContext();
+
+ protected:
+  CefBrowserImpl* browser_;
+  blink::WebLocalFrame* frame_;
+
+  typedef std::map<blink::WebNode, CefDOMNode*> NodeMap;
+  NodeMap node_map_;
+
+  IMPLEMENT_REFCOUNTING(CefDOMDocumentImpl);
+};
+
+#endif  // CEF_LIBCEF_DOM_DOCUMENT_IMPL_H_
diff --git a/src/libcef/renderer/dom_node_impl.cc b/src/libcef/renderer/dom_node_impl.cc
new file mode 100644
index 0000000..5cf1e8b
--- /dev/null
+++ b/src/libcef/renderer/dom_node_impl.cc
@@ -0,0 +1,416 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef/renderer/dom_node_impl.h"
+
+#include "libcef/common/tracker.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/browser_impl.h"
+#include "libcef/renderer/dom_document_impl.h"
+#include "libcef/renderer/thread_util.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_dom_event.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_form_control_element.h"
+#include "third_party/blink/public/web/web_input_element.h"
+#include "third_party/blink/public/web/web_node.h"
+#include "third_party/blink/public/web/web_select_element.h"
+
+using blink::WebDocument;
+using blink::WebDOMEvent;
+using blink::WebElement;
+using blink::WebFormControlElement;
+using blink::WebInputElement;
+using blink::WebNode;
+using blink::WebSelectElement;
+using blink::WebString;
+
+CefDOMNodeImpl::CefDOMNodeImpl(CefRefPtr<CefDOMDocumentImpl> document,
+                               const blink::WebNode& node)
+    : document_(document), node_(node) {}
+
+CefDOMNodeImpl::~CefDOMNodeImpl() {
+  CEF_REQUIRE_RT();
+
+  if (document_.get() && !node_.IsNull()) {
+    // Remove the node from the document.
+    document_->RemoveNode(node_);
+  }
+}
+
+CefDOMNodeImpl::Type CefDOMNodeImpl::GetType() {
+  if (!VerifyContext())
+    return DOM_NODE_TYPE_UNSUPPORTED;
+
+  return blink_glue::GetNodeType(node_);
+}
+
+bool CefDOMNodeImpl::IsText() {
+  if (!VerifyContext())
+    return false;
+
+  return node_.IsTextNode();
+}
+
+bool CefDOMNodeImpl::IsElement() {
+  if (!VerifyContext())
+    return false;
+
+  return node_.IsElementNode();
+}
+
+// Logic copied from RenderViewImpl::IsEditableNode.
+bool CefDOMNodeImpl::IsEditable() {
+  if (!VerifyContext())
+    return false;
+
+  if (node_.IsContentEditable())
+    return true;
+
+  if (node_.IsElementNode()) {
+    const WebElement& element = node_.ToConst<WebElement>();
+    if (blink_glue::IsTextControlElement(element))
+      return true;
+
+    // Also return true if it has an ARIA role of 'textbox'.
+    for (unsigned i = 0; i < element.AttributeCount(); ++i) {
+      if (base::LowerCaseEqualsASCII(element.AttributeLocalName(i).Utf8(),
+                                     "role")) {
+        if (base::LowerCaseEqualsASCII(element.AttributeValue(i).Utf8(),
+                                       "textbox")) {
+          return true;
+        }
+        break;
+      }
+    }
+  }
+
+  return false;
+}
+
+bool CefDOMNodeImpl::IsFormControlElement() {
+  if (!VerifyContext())
+    return false;
+
+  if (node_.IsElementNode()) {
+    const WebElement& element = node_.ToConst<WebElement>();
+    return element.IsFormControlElement();
+  }
+
+  return false;
+}
+
+CefString CefDOMNodeImpl::GetFormControlElementType() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  if (node_.IsElementNode()) {
+    const WebElement& element = node_.ToConst<WebElement>();
+    if (element.IsFormControlElement()) {
+      // Retrieve the type from the form control element.
+      const WebFormControlElement& formElement =
+          node_.ToConst<WebFormControlElement>();
+
+      const base::string16& form_control_type =
+          formElement.FormControlType().Utf16();
+      str = form_control_type;
+    }
+  }
+
+  return str;
+}
+
+bool CefDOMNodeImpl::IsSame(CefRefPtr<CefDOMNode> that) {
+  if (!VerifyContext())
+    return false;
+
+  CefDOMNodeImpl* impl = static_cast<CefDOMNodeImpl*>(that.get());
+  if (!impl || !impl->VerifyContext())
+    return false;
+
+  return node_.Equals(impl->node_);
+}
+
+CefString CefDOMNodeImpl::GetName() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  const WebString& name = blink_glue::GetNodeName(node_);
+  if (!name.IsNull())
+    str = name.Utf16();
+
+  return str;
+}
+
+CefString CefDOMNodeImpl::GetValue() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  if (node_.IsElementNode()) {
+    const WebElement& element = node_.ToConst<WebElement>();
+    if (element.IsFormControlElement()) {
+      // Retrieve the value from the form control element.
+      const WebFormControlElement& formElement =
+          node_.ToConst<WebFormControlElement>();
+
+      base::string16 value;
+      const base::string16& form_control_type =
+          formElement.FormControlType().Utf16();
+      if (form_control_type == base::ASCIIToUTF16("text")) {
+        const WebInputElement& input_element =
+            formElement.ToConst<WebInputElement>();
+        value = input_element.Value().Utf16();
+      } else if (form_control_type == base::ASCIIToUTF16("select-one")) {
+        const WebSelectElement& select_element =
+            formElement.ToConst<WebSelectElement>();
+        value = select_element.Value().Utf16();
+      }
+
+      base::TrimWhitespace(value, base::TRIM_LEADING, &value);
+      str = value;
+    }
+  }
+
+  if (str.empty()) {
+    const WebString& value = node_.NodeValue();
+    if (!value.IsNull())
+      str = value.Utf16();
+  }
+
+  return str;
+}
+
+bool CefDOMNodeImpl::SetValue(const CefString& value) {
+  if (!VerifyContext())
+    return false;
+
+  if (node_.IsElementNode())
+    return false;
+
+  return blink_glue::SetNodeValue(node_,
+                                  WebString::FromUTF16(value.ToString16()));
+}
+
+CefString CefDOMNodeImpl::GetAsMarkup() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  const WebString& markup = blink_glue::CreateNodeMarkup(node_);
+  if (!markup.IsNull())
+    str = markup.Utf16();
+
+  return str;
+}
+
+CefRefPtr<CefDOMDocument> CefDOMNodeImpl::GetDocument() {
+  if (!VerifyContext())
+    return nullptr;
+
+  return document_.get();
+}
+
+CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetParent() {
+  if (!VerifyContext())
+    return nullptr;
+
+  return document_->GetOrCreateNode(node_.ParentNode());
+}
+
+CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetPreviousSibling() {
+  if (!VerifyContext())
+    return nullptr;
+
+  return document_->GetOrCreateNode(node_.PreviousSibling());
+}
+
+CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetNextSibling() {
+  if (!VerifyContext())
+    return nullptr;
+
+  return document_->GetOrCreateNode(node_.NextSibling());
+}
+
+bool CefDOMNodeImpl::HasChildren() {
+  if (!VerifyContext())
+    return false;
+
+  return !node_.FirstChild().IsNull();
+}
+
+CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetFirstChild() {
+  if (!VerifyContext())
+    return nullptr;
+
+  return document_->GetOrCreateNode(node_.FirstChild());
+}
+
+CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetLastChild() {
+  if (!VerifyContext())
+    return nullptr;
+
+  return document_->GetOrCreateNode(node_.LastChild());
+}
+
+CefString CefDOMNodeImpl::GetElementTagName() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return str;
+  }
+
+  const WebElement& element = node_.ToConst<blink::WebElement>();
+  const WebString& tagname = element.TagName();
+  if (!tagname.IsNull())
+    str = tagname.Utf16();
+
+  return str;
+}
+
+bool CefDOMNodeImpl::HasElementAttributes() {
+  if (!VerifyContext())
+    return false;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return false;
+  }
+
+  const WebElement& element = node_.ToConst<blink::WebElement>();
+  return (element.AttributeCount() > 0);
+}
+
+bool CefDOMNodeImpl::HasElementAttribute(const CefString& attrName) {
+  if (!VerifyContext())
+    return false;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return false;
+  }
+
+  const WebElement& element = node_.ToConst<blink::WebElement>();
+  return element.HasAttribute(WebString::FromUTF16(attrName.ToString16()));
+}
+
+CefString CefDOMNodeImpl::GetElementAttribute(const CefString& attrName) {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return str;
+  }
+
+  const WebElement& element = node_.ToConst<blink::WebElement>();
+  const WebString& attr =
+      element.GetAttribute(WebString::FromUTF16(attrName.ToString16()));
+  if (!attr.IsNull())
+    str = attr.Utf16();
+
+  return str;
+}
+
+void CefDOMNodeImpl::GetElementAttributes(AttributeMap& attrMap) {
+  if (!VerifyContext())
+    return;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return;
+  }
+
+  const WebElement& element = node_.ToConst<blink::WebElement>();
+  unsigned int len = element.AttributeCount();
+  if (len == 0)
+    return;
+
+  for (unsigned int i = 0; i < len; ++i) {
+    base::string16 name = element.AttributeLocalName(i).Utf16();
+    base::string16 value = element.AttributeValue(i).Utf16();
+    attrMap.insert(std::make_pair(name, value));
+  }
+}
+
+bool CefDOMNodeImpl::SetElementAttribute(const CefString& attrName,
+                                         const CefString& value) {
+  if (!VerifyContext())
+    return false;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return false;
+  }
+
+  WebElement element = node_.To<blink::WebElement>();
+  element.SetAttribute(WebString::FromUTF16(attrName.ToString16()),
+                       WebString::FromUTF16(value.ToString16()));
+  return true;
+}
+
+CefString CefDOMNodeImpl::GetElementInnerText() {
+  CefString str;
+  if (!VerifyContext())
+    return str;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return str;
+  }
+
+  WebElement element = node_.To<blink::WebElement>();
+  const WebString& text = element.TextContent();
+  if (!text.IsNull())
+    str = text.Utf16();
+
+  return str;
+}
+
+CefRect CefDOMNodeImpl::GetElementBounds() {
+  CefRect rect;
+  if (!VerifyContext())
+    return rect;
+
+  if (!node_.IsElementNode()) {
+    NOTREACHED();
+    return rect;
+  }
+
+  WebElement element = node_.To<blink::WebElement>();
+  blink::WebRect rc = element.BoundsInViewport();
+  rect.Set(rc.x, rc.y, rc.width, rc.height);
+
+  return rect;
+}
+
+void CefDOMNodeImpl::Detach() {
+  document_ = nullptr;
+  node_.Assign(WebNode());
+}
+
+bool CefDOMNodeImpl::VerifyContext() {
+  if (!document_.get()) {
+    NOTREACHED();
+    return false;
+  }
+  if (!document_->VerifyContext())
+    return false;
+  if (node_.IsNull()) {
+    NOTREACHED();
+    return false;
+  }
+  return true;
+}
diff --git a/src/libcef/renderer/dom_node_impl.h b/src/libcef/renderer/dom_node_impl.h
new file mode 100644
index 0000000..e736477
--- /dev/null
+++ b/src/libcef/renderer/dom_node_impl.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DOM_NODE_IMPL_H_
+#define CEF_LIBCEF_DOM_NODE_IMPL_H_
+#pragma once
+
+#include "include/cef_dom.h"
+#include "third_party/blink/public/web/web_node.h"
+
+class CefDOMDocumentImpl;
+
+class CefDOMNodeImpl : public CefDOMNode {
+ public:
+  CefDOMNodeImpl(CefRefPtr<CefDOMDocumentImpl> document,
+                 const blink::WebNode& node);
+  ~CefDOMNodeImpl() override;
+
+  // CefDOMNode methods.
+  Type GetType() override;
+  bool IsText() override;
+  bool IsElement() override;
+  bool IsEditable() override;
+  bool IsFormControlElement() override;
+  CefString GetFormControlElementType() override;
+  bool IsSame(CefRefPtr<CefDOMNode> that) override;
+  CefString GetName() override;
+  CefString GetValue() override;
+  bool SetValue(const CefString& value) override;
+  CefString GetAsMarkup() override;
+  CefRefPtr<CefDOMDocument> GetDocument() override;
+  CefRefPtr<CefDOMNode> GetParent() override;
+  CefRefPtr<CefDOMNode> GetPreviousSibling() override;
+  CefRefPtr<CefDOMNode> GetNextSibling() override;
+  bool HasChildren() override;
+  CefRefPtr<CefDOMNode> GetFirstChild() override;
+  CefRefPtr<CefDOMNode> GetLastChild() override;
+  CefString GetElementTagName() override;
+  bool HasElementAttributes() override;
+  bool HasElementAttribute(const CefString& attrName) override;
+  CefString GetElementAttribute(const CefString& attrName) override;
+  void GetElementAttributes(AttributeMap& attrMap) override;
+  bool SetElementAttribute(const CefString& attrName,
+                           const CefString& value) override;
+  CefString GetElementInnerText() override;
+  CefRect GetElementBounds() override;
+
+  // Will be called from CefDOMDocumentImpl::Detach().
+  void Detach();
+
+  // Verify that the object exists and is being accessed on the UI thread.
+  bool VerifyContext();
+
+ protected:
+  CefRefPtr<CefDOMDocumentImpl> document_;
+  blink::WebNode node_;
+
+  IMPLEMENT_REFCOUNTING(CefDOMNodeImpl);
+};
+
+#endif  // CEF_LIBCEF_DOM_NODE_IMPL_H_
diff --git a/src/libcef/renderer/extensions/extensions_dispatcher_delegate.cc b/src/libcef/renderer/extensions/extensions_dispatcher_delegate.cc
new file mode 100644
index 0000000..51d3751
--- /dev/null
+++ b/src/libcef/renderer/extensions/extensions_dispatcher_delegate.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/extensions/extensions_dispatcher_delegate.h"
+
+#include "base/feature_list.h"
+#include "chrome/grit/renderer_resources.h"
+#include "extensions/common/extension_features.h"
+#include "extensions/renderer/resource_bundle_source_map.h"
+
+namespace extensions {
+
+CefExtensionsDispatcherDelegate::CefExtensionsDispatcherDelegate() {}
+
+CefExtensionsDispatcherDelegate::~CefExtensionsDispatcherDelegate() {}
+
+void CefExtensionsDispatcherDelegate::PopulateSourceMap(
+    extensions::ResourceBundleSourceMap* source_map) {}
+
+}  // namespace extensions
diff --git a/src/libcef/renderer/extensions/extensions_dispatcher_delegate.h b/src/libcef/renderer/extensions/extensions_dispatcher_delegate.h
new file mode 100644
index 0000000..3730f8f
--- /dev/null
+++ b/src/libcef/renderer/extensions/extensions_dispatcher_delegate.h
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_EXTENSIONS_EXTENSIONS_DISPATCHER_DELEGATE_H_
+#define CEF_LIBCEF_RENDERER_EXTENSIONS_EXTENSIONS_DISPATCHER_DELEGATE_H_
+
+#include "base/macros.h"
+#include "extensions/renderer/dispatcher_delegate.h"
+
+namespace extensions {
+
+class CefExtensionsDispatcherDelegate : public DispatcherDelegate {
+ public:
+  CefExtensionsDispatcherDelegate();
+  ~CefExtensionsDispatcherDelegate() override;
+
+  void PopulateSourceMap(
+      extensions::ResourceBundleSourceMap* source_map) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionsDispatcherDelegate);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_RENDERER_EXTENSIONS_EXTENSIONS_DISPATCHER_DELEGATE_H_
diff --git a/src/libcef/renderer/extensions/extensions_renderer_client.cc b/src/libcef/renderer/extensions/extensions_renderer_client.cc
new file mode 100644
index 0000000..a0c16f2
--- /dev/null
+++ b/src/libcef/renderer/extensions/extensions_renderer_client.cc
@@ -0,0 +1,194 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/extensions/extensions_renderer_client.h"
+
+#include "libcef/common/cef_messages.h"
+#include "libcef/renderer/extensions/extensions_dispatcher_delegate.h"
+#include "libcef/renderer/render_thread_observer.h"
+
+#include "base/command_line.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/renderer/extensions/extension_process_policy.h"
+#include "chrome/renderer/extensions/resource_request_policy.h"
+#include "content/public/common/content_constants.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/switches.h"
+#include "extensions/renderer/dispatcher.h"
+#include "extensions/renderer/extension_frame_helper.h"
+#include "extensions/renderer/extensions_render_frame_observer.h"
+#include "extensions/renderer/guest_view/extensions_guest_view_container.h"
+#include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h"
+#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
+#include "extensions/renderer/renderer_extension_registry.h"
+#include "extensions/renderer/script_context.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_plugin_params.h"
+
+namespace extensions {
+
+namespace {
+
+void IsGuestViewApiAvailableToScriptContext(
+    bool* api_is_available,
+    extensions::ScriptContext* context) {
+  if (context->GetAvailability("guestViewInternal").is_available()) {
+    *api_is_available = true;
+  }
+}
+
+}  // namespace
+
+CefExtensionsRendererClient::CefExtensionsRendererClient() {}
+
+CefExtensionsRendererClient::~CefExtensionsRendererClient() {}
+
+bool CefExtensionsRendererClient::IsIncognitoProcess() const {
+  return CefRenderThreadObserver::is_incognito_process();
+}
+
+int CefExtensionsRendererClient::GetLowestIsolatedWorldId() const {
+  // CEF doesn't need to reserve world IDs for anything other than extensions,
+  // so we always return 1. Note that 0 is reserved for the global world.
+  return 1;
+}
+
+extensions::Dispatcher* CefExtensionsRendererClient::GetDispatcher() {
+  return extension_dispatcher_.get();
+}
+
+void CefExtensionsRendererClient::OnExtensionLoaded(
+    const extensions::Extension& extension) {
+  resource_request_policy_->OnExtensionLoaded(extension);
+}
+
+void CefExtensionsRendererClient::OnExtensionUnloaded(
+    const extensions::ExtensionId& extension_id) {
+  resource_request_policy_->OnExtensionUnloaded(extension_id);
+}
+
+bool CefExtensionsRendererClient::ExtensionAPIEnabledForServiceWorkerScript(
+    const GURL& scope,
+    const GURL& script_url) const {
+  // TODO(extensions): Implement to support background sevice worker scripts
+  // in extensions
+  return false;
+}
+
+void CefExtensionsRendererClient::RenderThreadStarted() {
+  content::RenderThread* thread = content::RenderThread::Get();
+
+  extension_dispatcher_.reset(new extensions::Dispatcher(
+      std::make_unique<extensions::CefExtensionsDispatcherDelegate>()));
+  extension_dispatcher_->OnRenderThreadStarted(thread);
+  resource_request_policy_.reset(
+      new extensions::ResourceRequestPolicy(extension_dispatcher_.get()));
+  guest_view_container_dispatcher_.reset(
+      new extensions::ExtensionsGuestViewContainerDispatcher());
+
+  thread->AddObserver(extension_dispatcher_.get());
+  thread->AddObserver(guest_view_container_dispatcher_.get());
+}
+
+void CefExtensionsRendererClient::RenderFrameCreated(
+    content::RenderFrame* render_frame,
+    service_manager::BinderRegistry* registry) {
+  new extensions::ExtensionsRenderFrameObserver(render_frame, registry);
+  new extensions::ExtensionFrameHelper(render_frame,
+                                       extension_dispatcher_.get());
+  extension_dispatcher_->OnRenderFrameCreated(render_frame);
+}
+
+bool CefExtensionsRendererClient::OverrideCreatePlugin(
+    content::RenderFrame* render_frame,
+    const blink::WebPluginParams& params) {
+  if (params.mime_type.Utf8() != content::kBrowserPluginMimeType)
+    return true;
+
+  bool guest_view_api_available = false;
+  extension_dispatcher_->script_context_set_iterator()->ForEach(
+      render_frame, base::Bind(&IsGuestViewApiAvailableToScriptContext,
+                               &guest_view_api_available));
+  return !guest_view_api_available;
+}
+
+void CefExtensionsRendererClient::WillSendRequest(
+    blink::WebLocalFrame* frame,
+    ui::PageTransition transition_type,
+    const blink::WebURL& url,
+    const net::SiteForCookies& site_for_cookies,
+    const url::Origin* initiator_origin,
+    GURL* new_url,
+    bool* attach_same_site_cookies) {
+  if (initiator_origin &&
+      initiator_origin->scheme() == extensions::kExtensionScheme) {
+    const extensions::RendererExtensionRegistry* extension_registry =
+        extensions::RendererExtensionRegistry::Get();
+    const Extension* extension =
+        extension_registry->GetByID(initiator_origin->host());
+    if (extension) {
+      int tab_id = extensions::ExtensionFrameHelper::Get(
+                       content::RenderFrame::FromWebFrame(frame))
+                       ->tab_id();
+      GURL request_url(url);
+      if (extension->permissions_data()->GetPageAccess(request_url, tab_id,
+                                                       nullptr) ==
+              extensions::PermissionsData::PageAccess::kAllowed ||
+          extension->permissions_data()->GetContentScriptAccess(
+              request_url, tab_id, nullptr) ==
+              extensions::PermissionsData::PageAccess::kAllowed) {
+        *attach_same_site_cookies = true;
+      }
+    }
+  }
+
+  // Check whether the request should be allowed. If not allowed, we reset the
+  // URL to something invalid to prevent the request and cause an error.
+  if (url.ProtocolIs(extensions::kExtensionScheme) &&
+      !resource_request_policy_->CanRequestResource(GURL(url), frame,
+                                                    transition_type)) {
+    *new_url = GURL(chrome::kExtensionInvalidRequestURL);
+  }
+}
+
+void CefExtensionsRendererClient::RunScriptsAtDocumentStart(
+    content::RenderFrame* render_frame) {
+  extension_dispatcher_->RunScriptsAtDocumentStart(render_frame);
+}
+
+void CefExtensionsRendererClient::RunScriptsAtDocumentEnd(
+    content::RenderFrame* render_frame) {
+  extension_dispatcher_->RunScriptsAtDocumentEnd(render_frame);
+}
+
+void CefExtensionsRendererClient::RunScriptsAtDocumentIdle(
+    content::RenderFrame* render_frame) {
+  extension_dispatcher_->RunScriptsAtDocumentIdle(render_frame);
+}
+
+// static
+bool CefExtensionsRendererClient::IsStandaloneExtensionProcess() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      extensions::switches::kExtensionProcess);
+}
+
+// static
+content::BrowserPluginDelegate*
+CefExtensionsRendererClient::CreateBrowserPluginDelegate(
+    content::RenderFrame* render_frame,
+    const content::WebPluginInfo& info,
+    const std::string& mime_type,
+    const GURL& original_url) {
+  if (mime_type == content::kBrowserPluginMimeType)
+    return new extensions::ExtensionsGuestViewContainer(render_frame);
+  return new extensions::MimeHandlerViewContainer(render_frame, info, mime_type,
+                                                  original_url);
+}
+
+}  // namespace extensions
diff --git a/src/libcef/renderer/extensions/extensions_renderer_client.h b/src/libcef/renderer/extensions/extensions_renderer_client.h
new file mode 100644
index 0000000..b82c3a9
--- /dev/null
+++ b/src/libcef/renderer/extensions/extensions_renderer_client.h
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_EXTENSIONS_EXTENSIONS_RENDERER_CLIENT_H_
+#define CEF_LIBCEF_RENDERER_EXTENSIONS_EXTENSIONS_RENDERER_CLIENT_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "extensions/renderer/extensions_renderer_client.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "ui/base/page_transition_types.h"
+
+class GURL;
+
+namespace blink {
+class WebFrame;
+class WebLocalFrame;
+struct WebPluginParams;
+class WebURL;
+}  // namespace blink
+
+namespace net {
+class SiteForCookies;
+}
+
+namespace content {
+class BrowserPluginDelegate;
+class RenderFrame;
+struct WebPluginInfo;
+}  // namespace content
+
+namespace url {
+class Origin;
+}
+
+namespace extensions {
+
+class Dispatcher;
+class DispatcherDelegate;
+class ExtensionsGuestViewContainerDispatcher;
+class ResourceRequestPolicy;
+
+class CefExtensionsRendererClient : public ExtensionsRendererClient {
+ public:
+  CefExtensionsRendererClient();
+  ~CefExtensionsRendererClient() override;
+
+  // ExtensionsRendererClient implementation.
+  bool IsIncognitoProcess() const override;
+  int GetLowestIsolatedWorldId() const override;
+  extensions::Dispatcher* GetDispatcher() override;
+  void OnExtensionLoaded(const extensions::Extension& extension) override;
+  void OnExtensionUnloaded(
+      const extensions::ExtensionId& extension_id) override;
+  bool ExtensionAPIEnabledForServiceWorkerScript(
+      const GURL& scope,
+      const GURL& script_url) const override;
+
+  // See CefContentRendererClient methods with the same names.
+  void RenderThreadStarted();
+  void RenderFrameCreated(content::RenderFrame* render_frame,
+                          service_manager::BinderRegistry* registry);
+  bool OverrideCreatePlugin(content::RenderFrame* render_frame,
+                            const blink::WebPluginParams& params);
+  void WillSendRequest(blink::WebLocalFrame* frame,
+                       ui::PageTransition transition_type,
+                       const blink::WebURL& url,
+                       const net::SiteForCookies& site_for_cookies,
+                       const url::Origin* initiator_origin,
+                       GURL* new_url,
+                       bool* attach_same_site_cookies);
+  void RunScriptsAtDocumentStart(content::RenderFrame* render_frame);
+  void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame);
+  void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame);
+
+  static bool IsStandaloneExtensionProcess();
+  static content::BrowserPluginDelegate* CreateBrowserPluginDelegate(
+      content::RenderFrame* render_frame,
+      const content::WebPluginInfo& info,
+      const std::string& mime_type,
+      const GURL& original_url);
+
+ private:
+  std::unique_ptr<extensions::Dispatcher> extension_dispatcher_;
+  std::unique_ptr<extensions::ExtensionsGuestViewContainerDispatcher>
+      guest_view_container_dispatcher_;
+  std::unique_ptr<extensions::ResourceRequestPolicy> resource_request_policy_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefExtensionsRendererClient);
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_RENDERER_EXTENSIONS_EXTENSIONS_RENDERER_CLIENT_H_
diff --git a/src/libcef/renderer/extensions/print_render_frame_helper_delegate.cc b/src/libcef/renderer/extensions/print_render_frame_helper_delegate.cc
new file mode 100644
index 0000000..40434fb
--- /dev/null
+++ b/src/libcef/renderer/extensions/print_render_frame_helper_delegate.cc
@@ -0,0 +1,73 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/extensions/print_render_frame_helper_delegate.h"
+
+#include <vector>
+
+#include "libcef/common/extensions/extensions_util.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/renderer/render_frame.h"
+#include "extensions/common/constants.h"
+#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace extensions {
+
+CefPrintRenderFrameHelperDelegate::CefPrintRenderFrameHelperDelegate(
+    bool is_windowless)
+    : is_windowless_(is_windowless) {}
+
+CefPrintRenderFrameHelperDelegate::~CefPrintRenderFrameHelperDelegate() {}
+
+// Return the PDF object element if |frame| is the out of process PDF extension.
+blink::WebElement CefPrintRenderFrameHelperDelegate::GetPdfElement(
+    blink::WebLocalFrame* frame) {
+  GURL url = frame->GetDocument().Url();
+  bool inside_print_preview = url.GetOrigin() == chrome::kChromeUIPrintURL;
+  bool inside_pdf_extension =
+      url.SchemeIs(extensions::kExtensionScheme) &&
+      url.host_piece() == extension_misc::kPdfExtensionId;
+  if (inside_print_preview || inside_pdf_extension) {
+    // <object> with id="plugin" is created in
+    // chrome/browser/resources/pdf/pdf.js.
+    auto plugin_element = frame->GetDocument().GetElementById("plugin");
+    if (!plugin_element.IsNull()) {
+      return plugin_element;
+    }
+    NOTREACHED();
+  }
+  return blink::WebElement();
+}
+
+bool CefPrintRenderFrameHelperDelegate::IsPrintPreviewEnabled() {
+  return !is_windowless_ && PrintPreviewEnabled();
+}
+
+bool CefPrintRenderFrameHelperDelegate::OverridePrint(
+    blink::WebLocalFrame* frame) {
+  auto* post_message_support =
+      extensions::PostMessageSupport::FromWebLocalFrame(frame);
+  if (post_message_support) {
+    // This message is handled in chrome/browser/resources/pdf/pdf.js and
+    // instructs the PDF plugin to print. This is to make window.print() on a
+    // PDF plugin document correctly print the PDF. See
+    // https://crbug.com/448720.
+    base::DictionaryValue message;
+    message.SetString("type", "print");
+    post_message_support->PostMessageFromValue(message);
+    return true;
+  }
+  return false;
+}
+
+}  // namespace extensions
diff --git a/src/libcef/renderer/extensions/print_render_frame_helper_delegate.h b/src/libcef/renderer/extensions/print_render_frame_helper_delegate.h
new file mode 100644
index 0000000..9ecc29d
--- /dev/null
+++ b/src/libcef/renderer/extensions/print_render_frame_helper_delegate.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_EXTENSIONS_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_
+#define CEF_LIBCEF_RENDERER_EXTENSIONS_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_
+
+#include "components/printing/renderer/print_render_frame_helper.h"
+
+namespace extensions {
+
+class CefPrintRenderFrameHelperDelegate
+    : public printing::PrintRenderFrameHelper::Delegate {
+ public:
+  explicit CefPrintRenderFrameHelperDelegate(bool is_windowless);
+  ~CefPrintRenderFrameHelperDelegate() override;
+
+  blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) override;
+  bool IsPrintPreviewEnabled() override;
+  bool OverridePrint(blink::WebLocalFrame* frame) override;
+
+ private:
+  bool is_windowless_;
+};
+
+}  // namespace extensions
+
+#endif  // CEF_LIBCEF_RENDERER_EXTENSIONS_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_
diff --git a/src/libcef/renderer/frame_impl.cc b/src/libcef/renderer/frame_impl.cc
new file mode 100644
index 0000000..648721d
--- /dev/null
+++ b/src/libcef/renderer/frame_impl.cc
@@ -0,0 +1,526 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/renderer/frame_impl.h"
+
+#include "base/compiler_specific.h"
+
+// Enable deprecation warnings on Windows. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wdeprecated-declarations"
+#else
+#pragma warning(push)
+#pragma warning(default : 4996)
+#endif
+#endif
+
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/content_client.h"
+#include "libcef/common/net/http_header_utils.h"
+#include "libcef/common/process_message_impl.h"
+#include "libcef/common/request_impl.h"
+#include "libcef/common/response_manager.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/browser_impl.h"
+#include "libcef/renderer/dom_document_impl.h"
+#include "libcef/renderer/render_frame_util.h"
+#include "libcef/renderer/render_urlrequest_impl.h"
+#include "libcef/renderer/thread_util.h"
+#include "libcef/renderer/v8_impl.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/renderer/render_view.h"
+#include "content/renderer/render_frame_impl.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_document_loader.h"
+#include "third_party/blink/public/web/web_frame_content_dumper.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_navigation_control.h"
+#include "third_party/blink/public/web/web_script_source.h"
+#include "third_party/blink/public/web/web_view.h"
+
+CefFrameImpl::CefFrameImpl(CefBrowserImpl* browser,
+                           blink::WebLocalFrame* frame,
+                           int64_t frame_id)
+    : browser_(browser),
+      frame_(frame),
+      frame_id_(frame_id),
+      response_manager_(new CefResponseManager) {}
+
+CefFrameImpl::~CefFrameImpl() {}
+
+bool CefFrameImpl::IsValid() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  return (frame_ != nullptr);
+}
+
+void CefFrameImpl::Undo() {
+  ExecuteCommand("Undo");
+}
+
+void CefFrameImpl::Redo() {
+  ExecuteCommand("Redo");
+}
+
+void CefFrameImpl::Cut() {
+  ExecuteCommand("Cut");
+}
+
+void CefFrameImpl::Copy() {
+  ExecuteCommand("Copy");
+}
+
+void CefFrameImpl::Paste() {
+  ExecuteCommand("Paste");
+}
+
+void CefFrameImpl::Delete() {
+  ExecuteCommand("Delete");
+}
+
+void CefFrameImpl::SelectAll() {
+  ExecuteCommand("SelectAll");
+}
+
+void CefFrameImpl::ViewSource() {
+  NOTREACHED() << "ViewSource cannot be called from the renderer process";
+}
+
+void CefFrameImpl::GetSource(CefRefPtr<CefStringVisitor> visitor) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+  if (frame_) {
+    const CefString& content =
+        std::string(blink::WebFrameContentDumper::DumpAsMarkup(frame_).Utf8());
+    visitor->Visit(content);
+  }
+}
+
+void CefFrameImpl::GetText(CefRefPtr<CefStringVisitor> visitor) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+  if (frame_) {
+    const CefString& content = blink_glue::DumpDocumentText(frame_);
+    visitor->Visit(content);
+  }
+}
+
+void CefFrameImpl::LoadRequest(CefRefPtr<CefRequest> request) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (!frame_)
+    return;
+
+  CefMsg_LoadRequest_Params params;
+  params.url = GURL(std::string(request->GetURL()));
+  params.method = request->GetMethod();
+  params.site_for_cookies = net::SiteForCookies::FromUrl(
+      GURL(request->GetFirstPartyForCookies().ToString()));
+
+  CefRequest::HeaderMap headerMap;
+  request->GetHeaderMap(headerMap);
+  if (!headerMap.empty())
+    params.headers = HttpHeaderUtils::GenerateHeaders(headerMap);
+
+  CefRefPtr<CefPostData> postData = request->GetPostData();
+  if (postData.get()) {
+    CefPostDataImpl* impl = static_cast<CefPostDataImpl*>(postData.get());
+    params.upload_data = new net::UploadData();
+    impl->Get(*params.upload_data.get());
+  }
+
+  params.load_flags = request->GetFlags();
+
+  OnLoadRequest(params);
+}
+
+void CefFrameImpl::LoadURL(const CefString& url) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (!frame_)
+    return;
+
+  CefMsg_LoadRequest_Params params;
+  params.url = GURL(url.ToString());
+  params.method = "GET";
+
+  OnLoadRequest(params);
+}
+
+void CefFrameImpl::ExecuteJavaScript(const CefString& jsCode,
+                                     const CefString& scriptUrl,
+                                     int startLine) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (jsCode.empty())
+    return;
+  if (startLine < 1)
+    startLine = 1;
+
+  if (frame_) {
+    GURL gurl = GURL(scriptUrl.ToString());
+    frame_->ExecuteScript(blink::WebScriptSource(
+        blink::WebString::FromUTF16(jsCode.ToString16()), gurl, startLine));
+  }
+}
+
+bool CefFrameImpl::IsMain() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  if (frame_)
+    return (frame_->Parent() == nullptr);
+  return false;
+}
+
+bool CefFrameImpl::IsFocused() {
+  CEF_REQUIRE_RT_RETURN(false);
+
+  if (frame_ && frame_->View())
+    return (frame_->View()->FocusedFrame() == frame_);
+  return false;
+}
+
+CefString CefFrameImpl::GetName() {
+  CefString name;
+  CEF_REQUIRE_RT_RETURN(name);
+
+  if (frame_)
+    name = render_frame_util::GetName(frame_);
+  return name;
+}
+
+int64 CefFrameImpl::GetIdentifier() {
+  CEF_REQUIRE_RT_RETURN(0);
+
+  return frame_id_;
+}
+
+CefRefPtr<CefFrame> CefFrameImpl::GetParent() {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  if (frame_) {
+    blink::WebFrame* parent = frame_->Parent();
+    if (parent && parent->IsWebLocalFrame())
+      return browser_->GetWebFrameImpl(parent->ToWebLocalFrame()).get();
+  }
+
+  return nullptr;
+}
+
+CefString CefFrameImpl::GetURL() {
+  CefString url;
+  CEF_REQUIRE_RT_RETURN(url);
+
+  if (frame_) {
+    GURL gurl = frame_->GetDocument().Url();
+    url = gurl.spec();
+  }
+  return url;
+}
+
+CefRefPtr<CefBrowser> CefFrameImpl::GetBrowser() {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  return browser_;
+}
+
+CefRefPtr<CefV8Context> CefFrameImpl::GetV8Context() {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  if (frame_) {
+    v8::Isolate* isolate = blink::MainThreadIsolate();
+    v8::HandleScope handle_scope(isolate);
+    return new CefV8ContextImpl(isolate, frame_->MainWorldScriptContext());
+  } else {
+    return nullptr;
+  }
+}
+
+void CefFrameImpl::VisitDOM(CefRefPtr<CefDOMVisitor> visitor) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+
+  if (!frame_)
+    return;
+
+  // Create a CefDOMDocumentImpl object that is valid only for the scope of this
+  // method.
+  CefRefPtr<CefDOMDocumentImpl> documentImpl;
+  const blink::WebDocument& document = frame_->GetDocument();
+  if (!document.IsNull())
+    documentImpl = new CefDOMDocumentImpl(browser_, frame_);
+
+  visitor->Visit(documentImpl.get());
+
+  if (documentImpl.get())
+    documentImpl->Detach();
+}
+
+CefRefPtr<CefURLRequest> CefFrameImpl::CreateURLRequest(
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefURLRequestClient> client) {
+  CEF_REQUIRE_RT_RETURN(nullptr);
+
+  if (!request || !client || !frame_)
+    return nullptr;
+
+  CefRefPtr<CefRenderURLRequest> impl =
+      new CefRenderURLRequest(this, request, client);
+  if (impl->Start())
+    return impl.get();
+  return nullptr;
+}
+
+void CefFrameImpl::SendProcessMessage(CefProcessId target_process,
+                                      CefRefPtr<CefProcessMessage> message) {
+  Cef_Request_Params params;
+  CefProcessMessageImpl* impl =
+      static_cast<CefProcessMessageImpl*>(message.get());
+  if (impl->CopyTo(params)) {
+    SendProcessMessage(target_process, params.name, &params.arguments, true);
+  }
+}
+
+blink::WebURLLoaderFactory* CefFrameImpl::GetURLLoaderFactory() {
+  CEF_REQUIRE_RT();
+  if (!url_loader_factory_ && frame_) {
+    auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_);
+    if (render_frame) {
+      url_loader_factory_ = render_frame->CreateURLLoaderFactory();
+    }
+  }
+  return url_loader_factory_.get();
+}
+
+void CefFrameImpl::OnAttached() {
+  Send(new CefHostMsg_FrameAttached(MSG_ROUTING_NONE));
+}
+
+bool CefFrameImpl::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(CefFrameImpl, message)
+    IPC_MESSAGE_HANDLER(CefMsg_Request, OnRequest)
+    IPC_MESSAGE_HANDLER(CefMsg_Response, OnResponse)
+    IPC_MESSAGE_HANDLER(CefMsg_ResponseAck, OnResponseAck)
+    IPC_MESSAGE_HANDLER(CefMsg_LoadRequest, OnLoadRequest)
+    IPC_MESSAGE_HANDLER(CefMsg_DidStopLoading, OnDidStopLoading)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void CefFrameImpl::OnDidFinishLoad() {
+  // Ignore notifications from the embedded frame hosting a mime-type plugin.
+  // We'll eventually receive a notification from the owner frame.
+  if (blink_glue::HasPluginFrameOwner(frame_))
+    return;
+
+  blink::WebDocumentLoader* dl = frame_->GetDocumentLoader();
+  const int http_status_code = dl->GetResponse().HttpStatusCode();
+  Send(new CefHostMsg_DidFinishLoad(MSG_ROUTING_NONE, dl->GetUrl(),
+                                    http_status_code));
+
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app) {
+    CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
+    if (handler) {
+      CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
+      if (load_handler) {
+        load_handler->OnLoadEnd(browser_, this, http_status_code);
+      }
+    }
+  }
+}
+
+void CefFrameImpl::OnDraggableRegionsChanged() {
+  blink::WebVector<blink::WebDraggableRegion> webregions =
+      frame_->GetDocument().DraggableRegions();
+  std::vector<Cef_DraggableRegion_Params> regions;
+  for (size_t i = 0; i < webregions.size(); ++i) {
+    Cef_DraggableRegion_Params region;
+    auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_);
+    render_frame->ConvertViewportToWindow(&webregions[i].bounds);
+    region.bounds = webregions[i].bounds;
+    region.draggable = webregions[i].draggable;
+    regions.push_back(region);
+  }
+  Send(new CefHostMsg_UpdateDraggableRegions(MSG_ROUTING_NONE, regions));
+}
+
+void CefFrameImpl::OnDetached() {
+  // The browser may hold the last reference to |this|. Take a reference here to
+  // keep |this| alive until after this method returns.
+  CefRefPtr<CefFrameImpl> self = this;
+
+  browser_->FrameDetached(frame_id_);
+
+  browser_ = nullptr;
+  frame_ = nullptr;
+  url_loader_factory_.reset();
+  response_manager_.reset();
+}
+
+void CefFrameImpl::ExecuteCommand(const std::string& command) {
+  CEF_REQUIRE_RT_RETURN_VOID();
+  if (frame_)
+    frame_->ExecuteCommand(blink::WebString::FromUTF8(command));
+}
+
+void CefFrameImpl::SendProcessMessage(CefProcessId target_process,
+                                      const std::string& name,
+                                      base::ListValue* arguments,
+                                      bool user_initiated) {
+  DCHECK_EQ(PID_BROWSER, target_process);
+  DCHECK(!name.empty());
+
+  if (!frame_)
+    return;
+
+  Cef_Request_Params params;
+  params.name = name;
+  if (arguments)
+    params.arguments.Swap(arguments);
+  params.user_initiated = user_initiated;
+  params.request_id = -1;
+  params.expect_response = false;
+
+  Send(new CefHostMsg_Request(MSG_ROUTING_NONE, params));
+}
+
+void CefFrameImpl::Send(IPC::Message* message) {
+  if (!frame_) {
+    delete message;
+    return;
+  }
+
+  auto render_frame = content::RenderFrame::FromWebFrame(frame_);
+  message->set_routing_id(render_frame->GetRoutingID());
+  render_frame->Send(message);
+}
+
+void CefFrameImpl::OnRequest(const Cef_Request_Params& params) {
+  DCHECK(browser_);
+  DCHECK(frame_);
+
+  bool success = false;
+  std::string response;
+  bool expect_response_ack = false;
+
+  TRACE_EVENT2("cef", "CefBrowserImpl::OnRequest", "request_id",
+               params.request_id, "expect_response",
+               params.expect_response ? 1 : 0);
+
+  if (params.user_initiated) {
+    // Give the user a chance to handle the request.
+    CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+    if (app.get()) {
+      CefRefPtr<CefRenderProcessHandler> handler =
+          app->GetRenderProcessHandler();
+      if (handler.get()) {
+        CefRefPtr<CefProcessMessageImpl> message(new CefProcessMessageImpl(
+            const_cast<Cef_Request_Params*>(&params), false, true));
+        success = handler->OnProcessMessageReceived(browser_, this, PID_BROWSER,
+                                                    message.get());
+        message->Detach(nullptr);
+      }
+    }
+  } else if (params.name == "execute-code") {
+    // Execute code.
+    DCHECK_EQ(params.arguments.GetSize(), (size_t)4);
+
+    bool is_javascript = false;
+    std::string code, script_url;
+    int script_start_line = 0;
+
+    params.arguments.GetBoolean(0, &is_javascript);
+    params.arguments.GetString(1, &code);
+    DCHECK(!code.empty());
+    params.arguments.GetString(2, &script_url);
+    params.arguments.GetInteger(3, &script_start_line);
+    DCHECK_GE(script_start_line, 0);
+
+    if (is_javascript) {
+      frame_->ExecuteScript(
+          blink::WebScriptSource(blink::WebString::FromUTF8(code),
+                                 GURL(script_url), script_start_line));
+      success = true;
+    } else {
+      // TODO(cef): implement support for CSS code.
+      NOTIMPLEMENTED();
+    }
+  } else if (params.name == "execute-command") {
+    // Execute command.
+    DCHECK_EQ(params.arguments.GetSize(), (size_t)1);
+
+    std::string command;
+
+    params.arguments.GetString(0, &command);
+    DCHECK(!command.empty());
+
+    if (base::LowerCaseEqualsASCII(command, "getsource")) {
+      response = blink::WebFrameContentDumper::DumpAsMarkup(frame_).Utf8();
+      success = true;
+    } else if (base::LowerCaseEqualsASCII(command, "gettext")) {
+      response = blink_glue::DumpDocumentText(frame_);
+      success = true;
+    } else if (frame_->ExecuteCommand(blink::WebString::FromUTF8(command))) {
+      success = true;
+    }
+  } else {
+    // Invalid request.
+    NOTREACHED();
+  }
+
+  if (params.expect_response) {
+    DCHECK_GE(params.request_id, 0);
+
+    // Send a response to the browser.
+    Cef_Response_Params response_params;
+    response_params.request_id = params.request_id;
+    response_params.success = success;
+    response_params.response = response;
+    response_params.expect_response_ack = expect_response_ack;
+    Send(new CefHostMsg_Response(MSG_ROUTING_NONE, response_params));
+  }
+}
+
+void CefFrameImpl::OnResponse(const Cef_Response_Params& params) {
+  response_manager_->RunHandler(params);
+  if (params.expect_response_ack)
+    Send(new CefHostMsg_ResponseAck(MSG_ROUTING_NONE, params.request_id));
+}
+
+void CefFrameImpl::OnResponseAck(int request_id) {
+  response_manager_->RunAckHandler(request_id);
+}
+
+void CefFrameImpl::OnDidStopLoading() {
+  // We should only receive this notification for the highest-level LocalFrame
+  // in this frame's in-process subtree. If there are multiple of these for the
+  // same browser then the other occurrences will be discarded in
+  // OnLoadingStateChange.
+  browser_->OnLoadingStateChange(false);
+}
+
+void CefFrameImpl::OnLoadRequest(const CefMsg_LoadRequest_Params& params) {
+  DCHECK(frame_);
+
+  blink::WebURLRequest request;
+  CefRequestImpl::Get(params, request);
+
+  frame_->StartNavigation(request);
+}
+
+// Enable deprecation warnings on Windows. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic pop
+#else
+#pragma warning(pop)
+#endif
+#endif
diff --git a/src/libcef/renderer/frame_impl.h b/src/libcef/renderer/frame_impl.h
new file mode 100644
index 0000000..3859beb
--- /dev/null
+++ b/src/libcef/renderer/frame_impl.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_FRAME_IMPL_H_
+#define CEF_LIBCEF_RENDERER_FRAME_IMPL_H_
+#pragma once
+
+#include <string>
+#include "include/cef_frame.h"
+#include "include/cef_v8.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace blink {
+class WebLocalFrame;
+class WebURLLoaderFactory;
+}  // namespace blink
+
+namespace IPC {
+class Message;
+}
+
+class GURL;
+
+class CefBrowserImpl;
+class CefResponseManager;
+struct CefMsg_LoadRequest_Params;
+struct Cef_Request_Params;
+struct Cef_Response_Params;
+
+// Implementation of CefFrame. CefFrameImpl objects are owned by the
+// CefBrowerImpl and will be detached when the browser is notified that the
+// associated renderer WebFrame will close.
+class CefFrameImpl : public CefFrame {
+ public:
+  CefFrameImpl(CefBrowserImpl* browser,
+               blink::WebLocalFrame* frame,
+               int64_t frame_id);
+  ~CefFrameImpl() override;
+
+  // CefFrame implementation.
+  bool IsValid() override;
+  void Undo() override;
+  void Redo() override;
+  void Cut() override;
+  void Copy() override;
+  void Paste() override;
+  void Delete() override;
+  void SelectAll() override;
+  void ViewSource() override;
+  void GetSource(CefRefPtr<CefStringVisitor> visitor) override;
+  void GetText(CefRefPtr<CefStringVisitor> visitor) override;
+  void LoadRequest(CefRefPtr<CefRequest> request) override;
+  void LoadURL(const CefString& url) override;
+  void ExecuteJavaScript(const CefString& jsCode,
+                         const CefString& scriptUrl,
+                         int startLine) override;
+  bool IsMain() override;
+  bool IsFocused() override;
+  CefString GetName() override;
+  int64 GetIdentifier() override;
+  CefRefPtr<CefFrame> GetParent() override;
+  CefString GetURL() override;
+  CefRefPtr<CefBrowser> GetBrowser() override;
+  CefRefPtr<CefV8Context> GetV8Context() override;
+  void VisitDOM(CefRefPtr<CefDOMVisitor> visitor) override;
+  CefRefPtr<CefURLRequest> CreateURLRequest(
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefURLRequestClient> client) override;
+  void SendProcessMessage(CefProcessId target_process,
+                          CefRefPtr<CefProcessMessage> message) override;
+
+  // Used by CefRenderURLRequest.
+  blink::WebURLLoaderFactory* GetURLLoaderFactory();
+
+  // Forwarded from CefRenderFrameObserver.
+  void OnAttached();
+  bool OnMessageReceived(const IPC::Message& message);
+  void OnDidFinishLoad();
+  void OnDraggableRegionsChanged();
+  void OnDetached();
+
+  blink::WebLocalFrame* web_frame() const { return frame_; }
+
+ private:
+  void ExecuteCommand(const std::string& command);
+
+  // Avoids unnecessary string type conversions.
+  void SendProcessMessage(CefProcessId target_process,
+                          const std::string& name,
+                          base::ListValue* arguments,
+                          bool user_initiated);
+
+  // Send a message to the RenderFrame associated with this frame.
+  void Send(IPC::Message* message);
+
+  // OnMessageReceived message handlers.
+  void OnRequest(const Cef_Request_Params& params);
+  void OnResponse(const Cef_Response_Params& params);
+  void OnResponseAck(int request_id);
+  void OnDidStopLoading();
+  void OnLoadRequest(const CefMsg_LoadRequest_Params& params);
+
+  CefBrowserImpl* browser_;
+  blink::WebLocalFrame* frame_;
+  const int64 frame_id_;
+
+  std::unique_ptr<blink::WebURLLoaderFactory> url_loader_factory_;
+
+  // Manages response registrations.
+  std::unique_ptr<CefResponseManager> response_manager_;
+
+  IMPLEMENT_REFCOUNTING(CefFrameImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefFrameImpl);
+};
+
+#endif  // CEF_LIBCEF_RENDERER_FRAME_IMPL_H_
diff --git a/src/libcef/renderer/render_frame_observer.cc b/src/libcef/renderer/render_frame_observer.cc
new file mode 100644
index 0000000..f3bf062
--- /dev/null
+++ b/src/libcef/renderer/render_frame_observer.cc
@@ -0,0 +1,258 @@
+// Copyright 2014 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "base/compiler_specific.h"
+
+// Enable deprecation warnings on Windows. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wdeprecated-declarations"
+#else
+#pragma warning(push)
+#pragma warning(default : 4996)
+#endif
+#endif
+
+#include "libcef/renderer/render_frame_observer.h"
+
+#include "libcef/common/content_client.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/browser_impl.h"
+#include "libcef/renderer/dom_document_impl.h"
+#include "libcef/renderer/v8_impl.h"
+
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_view.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_node.h"
+
+CefRenderFrameObserver::CefRenderFrameObserver(
+    content::RenderFrame* render_frame)
+    : content::RenderFrameObserver(render_frame) {}
+
+CefRenderFrameObserver::~CefRenderFrameObserver() {}
+
+void CefRenderFrameObserver::OnInterfaceRequestForFrame(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle* interface_pipe) {
+  registry_.TryBindInterface(interface_name, interface_pipe);
+}
+
+bool CefRenderFrameObserver::OnAssociatedInterfaceRequestForFrame(
+    const std::string& interface_name,
+    mojo::ScopedInterfaceEndpointHandle* handle) {
+  return associated_interfaces_.TryBindInterface(interface_name, handle);
+}
+
+void CefRenderFrameObserver::DidCommitProvisionalLoad(
+    bool is_same_document_navigation,
+    ui::PageTransition transition) {
+  if (!frame_)
+    return;
+
+  if (frame_->GetParent() == nullptr) {
+    blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+    CefRefPtr<CefBrowserImpl> browserPtr =
+        CefBrowserImpl::GetBrowserForMainFrame(frame->Top());
+    browserPtr->OnLoadingStateChange(true);
+  }
+  OnLoadStart();
+}
+
+void CefRenderFrameObserver::DidFailProvisionalLoad() {
+  if (frame_) {
+    OnLoadError();
+  }
+}
+
+void CefRenderFrameObserver::DidFinishLoad() {
+  if (frame_) {
+    frame_->OnDidFinishLoad();
+  }
+}
+
+void CefRenderFrameObserver::FrameDetached() {
+  if (frame_) {
+    frame_->OnDetached();
+    frame_ = nullptr;
+  }
+}
+
+void CefRenderFrameObserver::FocusedElementChanged(
+    const blink::WebElement& element) {
+  if (!frame_)
+    return;
+
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  CefRefPtr<CefBrowserImpl> browserPtr =
+      CefBrowserImpl::GetBrowserForMainFrame(frame->Top());
+  if (!browserPtr)
+    return;
+
+  CefRefPtr<CefRenderProcessHandler> handler;
+  CefRefPtr<CefApp> application = CefContentClient::Get()->application();
+  if (application)
+    handler = application->GetRenderProcessHandler();
+  if (!handler)
+    return;
+
+  CefRefPtr<CefFrameImpl> framePtr = browserPtr->GetWebFrameImpl(frame);
+
+  if (element.IsNull()) {
+    handler->OnFocusedNodeChanged(browserPtr.get(), framePtr.get(), nullptr);
+    return;
+  }
+
+  if (element.GetDocument().IsNull())
+    return;
+
+  CefRefPtr<CefDOMDocumentImpl> documentImpl =
+      new CefDOMDocumentImpl(browserPtr.get(), frame);
+  handler->OnFocusedNodeChanged(browserPtr.get(), framePtr.get(),
+                                documentImpl->GetOrCreateNode(element));
+  documentImpl->Detach();
+}
+
+void CefRenderFrameObserver::DraggableRegionsChanged() {
+  if (frame_) {
+    frame_->OnDraggableRegionsChanged();
+  }
+}
+
+void CefRenderFrameObserver::DidCreateScriptContext(
+    v8::Handle<v8::Context> context,
+    int world_id) {
+  if (!frame_)
+    return;
+
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  CefRefPtr<CefBrowserImpl> browserPtr =
+      CefBrowserImpl::GetBrowserForMainFrame(frame->Top());
+  if (!browserPtr)
+    return;
+
+  CefRefPtr<CefRenderProcessHandler> handler;
+  CefRefPtr<CefApp> application = CefContentClient::Get()->application();
+  if (application)
+    handler = application->GetRenderProcessHandler();
+  if (!handler)
+    return;
+
+  CefRefPtr<CefFrameImpl> framePtr = browserPtr->GetWebFrameImpl(frame);
+
+  v8::Isolate* isolate = blink::MainThreadIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Context::Scope scope(context);
+  v8::MicrotasksScope microtasks_scope(isolate,
+                                       v8::MicrotasksScope::kRunMicrotasks);
+
+  CefRefPtr<CefV8Context> contextPtr(new CefV8ContextImpl(isolate, context));
+
+  handler->OnContextCreated(browserPtr.get(), framePtr.get(), contextPtr);
+}
+
+void CefRenderFrameObserver::WillReleaseScriptContext(
+    v8::Handle<v8::Context> context,
+    int world_id) {
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  CefRefPtr<CefBrowserImpl> browserPtr =
+      CefBrowserImpl::GetBrowserForMainFrame(frame->Top());
+  if (browserPtr) {
+    CefRefPtr<CefApp> application = CefContentClient::Get()->application();
+    if (application) {
+      CefRefPtr<CefRenderProcessHandler> handler =
+          application->GetRenderProcessHandler();
+      if (handler) {
+        CefRefPtr<CefFrameImpl> framePtr = browserPtr->GetWebFrameImpl(frame);
+
+        v8::Isolate* isolate = blink::MainThreadIsolate();
+        v8::HandleScope handle_scope(isolate);
+
+        // The released context should not be used for script execution.
+        // Depending on how the context is released this may or may not already
+        // be set.
+        blink_glue::CefScriptForbiddenScope forbidScript;
+
+        CefRefPtr<CefV8Context> contextPtr(
+            new CefV8ContextImpl(isolate, context));
+
+        handler->OnContextReleased(browserPtr.get(), framePtr.get(),
+                                   contextPtr);
+      }
+    }
+  }
+
+  CefV8ReleaseContext(context);
+}
+
+void CefRenderFrameObserver::OnDestruct() {
+  delete this;
+}
+
+bool CefRenderFrameObserver::OnMessageReceived(const IPC::Message& message) {
+  if (frame_) {
+    return frame_->OnMessageReceived(message);
+  }
+  return false;
+}
+
+void CefRenderFrameObserver::AttachFrame(CefFrameImpl* frame) {
+  DCHECK(frame);
+  DCHECK(!frame_);
+  frame_ = frame;
+  frame_->OnAttached();
+}
+
+void CefRenderFrameObserver::OnLoadStart() {
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
+    if (handler.get()) {
+      CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
+      if (load_handler.get()) {
+        blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+        CefRefPtr<CefBrowserImpl> browserPtr =
+            CefBrowserImpl::GetBrowserForMainFrame(frame->Top());
+        load_handler->OnLoadStart(browserPtr.get(), frame_, TT_EXPLICIT);
+      }
+    }
+  }
+}
+
+void CefRenderFrameObserver::OnLoadError() {
+  CefRefPtr<CefApp> app = CefContentClient::Get()->application();
+  if (app.get()) {
+    CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
+    if (handler.get()) {
+      CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
+      // Error codes were removed from DidFailProvisionalLoad() so we now always
+      // pass the same value.
+      if (load_handler.get()) {
+        const cef_errorcode_t errorCode =
+            static_cast<cef_errorcode_t>(net::ERR_ABORTED);
+        const std::string& errorText = net::ErrorToString(errorCode);
+        const GURL failedUrl(frame_->GetURL().c_str());
+        blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+        CefRefPtr<CefBrowserImpl> browserPtr =
+            CefBrowserImpl::GetBrowserForMainFrame(frame->Top());
+        load_handler->OnLoadError(browserPtr.get(), frame_, errorCode,
+                                  errorText, failedUrl.spec());
+      }
+    }
+  }
+}
+
+// Enable deprecation warnings on Windows. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic pop
+#else
+#pragma warning(pop)
+#endif
+#endif
diff --git a/src/libcef/renderer/render_frame_observer.h b/src/libcef/renderer/render_frame_observer.h
new file mode 100644
index 0000000..d395e4b
--- /dev/null
+++ b/src/libcef/renderer/render_frame_observer.h
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#ifndef LIBCEF_RENDERER_RENDER_FRAME_OBSERVER_H_
+#define LIBCEF_RENDERER_RENDER_FRAME_OBSERVER_H_
+
+#include "content/public/renderer/render_frame_observer.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+
+namespace content {
+class RenderFrame;
+class RenderView;
+}  // namespace content
+
+class CefFrameImpl;
+
+class CefRenderFrameObserver : public content::RenderFrameObserver {
+ public:
+  explicit CefRenderFrameObserver(content::RenderFrame* render_frame);
+  ~CefRenderFrameObserver() override;
+
+  // RenderFrameObserver methods:
+  void OnInterfaceRequestForFrame(
+      const std::string& interface_name,
+      mojo::ScopedMessagePipeHandle* interface_pipe) override;
+  bool OnAssociatedInterfaceRequestForFrame(
+      const std::string& interface_name,
+      mojo::ScopedInterfaceEndpointHandle* handle) override;
+  void DidCommitProvisionalLoad(bool is_same_document_navigation,
+                                ui::PageTransition transition) override;
+  void DidFailProvisionalLoad() override;
+  void DidFinishLoad() override;
+  void FrameDetached() override;
+  void FocusedElementChanged(const blink::WebElement& element) override;
+  void DraggableRegionsChanged() override;
+  void DidCreateScriptContext(v8::Handle<v8::Context> context,
+                              int world_id) override;
+  void WillReleaseScriptContext(v8::Handle<v8::Context> context,
+                                int world_id) override;
+  void OnDestruct() override;
+  bool OnMessageReceived(const IPC::Message& message) override;
+
+  service_manager::BinderRegistry* registry() { return &registry_; }
+  blink::AssociatedInterfaceRegistry* associated_interfaces() {
+    return &associated_interfaces_;
+  }
+
+  void AttachFrame(CefFrameImpl* frame);
+
+ private:
+  void OnLoadStart();
+  void OnLoadError();
+
+  service_manager::BinderRegistry registry_;
+  blink::AssociatedInterfaceRegistry associated_interfaces_;
+  CefFrameImpl* frame_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(CefRenderFrameObserver);
+};
+
+#endif  // LIBCEF_RENDERER_RENDER_FRAME_OBSERVER_H_
diff --git a/src/libcef/renderer/render_frame_util.cc b/src/libcef/renderer/render_frame_util.cc
new file mode 100644
index 0000000..9cb0098
--- /dev/null
+++ b/src/libcef/renderer/render_frame_util.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/render_frame_util.h"
+
+#include "libcef/common/frame_util.h"
+#include "libcef/renderer/blink_glue.h"
+
+#include "base/logging.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
+#include "content/renderer/render_frame_impl.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace render_frame_util {
+
+int64_t GetIdentifier(blink::WebLocalFrame* frame) {
+  // Each WebFrame will have an associated RenderFrame. The RenderFrame
+  // routing IDs are unique within a given renderer process.
+  content::RenderFrame* render_frame =
+      content::RenderFrame::FromWebFrame(frame);
+  return frame_util::MakeFrameId(content::RenderThread::Get()->GetClientId(),
+                                 render_frame->GetRoutingID());
+}
+
+std::string GetName(blink::WebLocalFrame* frame) {
+  DCHECK(frame);
+  // Return the assigned name if it is non-empty. This represents the name
+  // property on the frame DOM element. If the assigned name is empty, revert to
+  // the internal unique name. This matches the logic in
+  // CefFrameHostImpl::RefreshAttributes.
+  if (frame->AssignedName().length() > 0) {
+    return frame->AssignedName().Utf8();
+  }
+  content::RenderFrameImpl* render_frame =
+      content::RenderFrameImpl::FromWebFrame(frame);
+  DCHECK(render_frame);
+  if (render_frame)
+    return render_frame->unique_name();
+  return std::string();
+}
+
+}  // namespace render_frame_util
diff --git a/src/libcef/renderer/render_frame_util.h b/src/libcef/renderer/render_frame_util.h
new file mode 100644
index 0000000..ee451e5
--- /dev/null
+++ b/src/libcef/renderer/render_frame_util.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_RENDER_FRAME_UTIL_H_
+#define CEF_LIBCEF_RENDERER_RENDER_FRAME_UTIL_H_
+
+#include <stdint.h>
+
+#include <string>
+
+namespace blink {
+class WebLocalFrame;
+}
+
+namespace render_frame_util {
+
+int64_t GetIdentifier(blink::WebLocalFrame* frame);
+std::string GetName(blink::WebLocalFrame* frame);
+
+}  // namespace render_frame_util
+
+#endif  // CEF_LIBCEF_RENDERER_RENDER_FRAME_UTIL_H_
diff --git a/src/libcef/renderer/render_thread_observer.cc b/src/libcef/renderer/render_thread_observer.cc
new file mode 100644
index 0000000..e4c3bdf
--- /dev/null
+++ b/src/libcef/renderer/render_thread_observer.cc
@@ -0,0 +1,114 @@
+/// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/render_thread_observer.h"
+
+#include "libcef/common/cef_messages.h"
+#include "libcef/common/net/net_resource_provider.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/content_renderer_client.h"
+
+#include "components/visitedlink/renderer/visitedlink_reader.h"
+#include "content/public/child/child_thread.h"
+#include "content/public/renderer/render_thread.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "net/base/net_module.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/web/web_security_policy.h"
+
+namespace {
+
+chrome::mojom::DynamicParams* GetDynamicConfigParams() {
+  static base::NoDestructor<chrome::mojom::DynamicParams> dynamic_params;
+  return dynamic_params.get();
+}
+
+}  // namespace
+
+bool CefRenderThreadObserver::is_incognito_process_ = false;
+
+CefRenderThreadObserver::CefRenderThreadObserver() {
+  net::NetModule::SetResourceProvider(NetResourceProvider);
+}
+
+CefRenderThreadObserver::~CefRenderThreadObserver() {}
+
+// static
+const chrome::mojom::DynamicParams&
+CefRenderThreadObserver::GetDynamicParams() {
+  return *GetDynamicConfigParams();
+}
+
+bool CefRenderThreadObserver::OnControlMessageReceived(
+    const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(CefRenderThreadObserver, message)
+    IPC_MESSAGE_HANDLER(CefProcessMsg_ModifyCrossOriginWhitelistEntry,
+                        OnModifyCrossOriginWhitelistEntry)
+    IPC_MESSAGE_HANDLER(CefProcessMsg_ClearCrossOriginWhitelist,
+                        OnClearCrossOriginWhitelist)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void CefRenderThreadObserver::RegisterMojoInterfaces(
+    blink::AssociatedInterfaceRegistry* associated_interfaces) {
+  associated_interfaces->AddInterface(base::Bind(
+      &CefRenderThreadObserver::OnRendererConfigurationAssociatedRequest,
+      base::Unretained(this)));
+}
+
+void CefRenderThreadObserver::UnregisterMojoInterfaces(
+    blink::AssociatedInterfaceRegistry* associated_interfaces) {
+  associated_interfaces->RemoveInterface(
+      chrome::mojom::RendererConfiguration::Name_);
+}
+
+void CefRenderThreadObserver::SetInitialConfiguration(
+    bool is_incognito_process,
+    mojo::PendingReceiver<chrome::mojom::ChromeOSListener> chromeos_listener) {
+  is_incognito_process_ = is_incognito_process;
+}
+
+void CefRenderThreadObserver::SetConfiguration(
+    chrome::mojom::DynamicParamsPtr params) {
+  *GetDynamicConfigParams() = std::move(*params);
+}
+
+void CefRenderThreadObserver::SetContentSettingRules(
+    const RendererContentSettingRules& rules) {}
+
+void CefRenderThreadObserver::OnRendererConfigurationAssociatedRequest(
+    mojo::PendingAssociatedReceiver<chrome::mojom::RendererConfiguration>
+        receiver) {
+  renderer_configuration_receivers_.Add(this, std::move(receiver));
+}
+
+void CefRenderThreadObserver::OnModifyCrossOriginWhitelistEntry(
+    bool add,
+    const Cef_CrossOriginWhiteListEntry_Params& params) {
+  GURL gurl = GURL(params.source_origin);
+  if (add) {
+    blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
+        gurl, blink::WebString::FromUTF8(params.target_protocol),
+        blink::WebString::FromUTF8(params.target_domain),
+        /*destination_port=*/0,
+        params.allow_target_subdomains
+            ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
+            : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+        network::mojom::CorsPortMatchMode::kAllowAnyPort,
+        network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
+  } else {
+    blink::WebSecurityPolicy::ClearOriginAccessListForOrigin(gurl);
+  }
+}
+
+void CefRenderThreadObserver::OnClearCrossOriginWhitelist() {
+  blink::WebSecurityPolicy::ClearOriginAccessList();
+}
diff --git a/src/libcef/renderer/render_thread_observer.h b/src/libcef/renderer/render_thread_observer.h
new file mode 100644
index 0000000..2044fc8
--- /dev/null
+++ b/src/libcef/renderer/render_thread_observer.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_RENDER_THREAD_OBSERVER_H_
+#define CEF_LIBCEF_RENDERER_RENDER_THREAD_OBSERVER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "chrome/common/renderer_configuration.mojom.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "content/public/renderer/render_thread_observer.h"
+#include "mojo/public/cpp/bindings/associated_binding_set.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+
+struct Cef_CrossOriginWhiteListEntry_Params;
+
+// This class sends and receives control messages in the renderer process.
+class CefRenderThreadObserver : public content::RenderThreadObserver,
+                                public chrome::mojom::RendererConfiguration {
+ public:
+  CefRenderThreadObserver();
+  ~CefRenderThreadObserver() override;
+
+  static bool is_incognito_process() { return is_incognito_process_; }
+
+  // Return the dynamic parameters - those that may change while the
+  // render process is running.
+  static const chrome::mojom::DynamicParams& GetDynamicParams();
+
+ private:
+  // content::RenderThreadObserver:
+  bool OnControlMessageReceived(const IPC::Message& message) override;
+  void RegisterMojoInterfaces(
+      blink::AssociatedInterfaceRegistry* associated_interfaces) override;
+  void UnregisterMojoInterfaces(
+      blink::AssociatedInterfaceRegistry* associated_interfaces) override;
+
+  // chrome::mojom::RendererConfiguration:
+  void SetInitialConfiguration(
+      bool is_incognito_process,
+      mojo::PendingReceiver<chrome::mojom::ChromeOSListener> chromeos_listener)
+      override;
+  void SetConfiguration(chrome::mojom::DynamicParamsPtr params) override;
+  void SetContentSettingRules(
+      const RendererContentSettingRules& rules) override;
+
+  void OnRendererConfigurationAssociatedRequest(
+      mojo::PendingAssociatedReceiver<chrome::mojom::RendererConfiguration>
+          receiver);
+
+  // Message handlers called on the render thread.
+  void OnModifyCrossOriginWhitelistEntry(
+      bool add,
+      const Cef_CrossOriginWhiteListEntry_Params& params);
+  void OnClearCrossOriginWhitelist();
+
+  static bool is_incognito_process_;
+
+  mojo::AssociatedReceiverSet<chrome::mojom::RendererConfiguration>
+      renderer_configuration_receivers_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefRenderThreadObserver);
+};
+
+#endif  // CEF_LIBCEF_RENDERER_RENDER_THREAD_OBSERVER_H_
diff --git a/src/libcef/renderer/render_urlrequest_impl.cc b/src/libcef/renderer/render_urlrequest_impl.cc
new file mode 100644
index 0000000..4c5d25b
--- /dev/null
+++ b/src/libcef/renderer/render_urlrequest_impl.cc
@@ -0,0 +1,509 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "libcef/renderer/render_urlrequest_impl.h"
+
+#include <stdint.h>
+
+#include "libcef/common/request_impl.h"
+#include "libcef/common/response_impl.h"
+#include "libcef/common/task_runner_impl.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/content_renderer_client.h"
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "net/base/request_priority.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_client.h"
+#include "third_party/blink/public/platform/web_url_loader_factory.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+
+using blink::WebString;
+using blink::WebURL;
+using blink::WebURLError;
+using blink::WebURLLoader;
+using blink::WebURLRequest;
+using blink::WebURLResponse;
+
+namespace {
+
+class CefWebURLLoaderClient : public blink::WebURLLoaderClient {
+ public:
+  CefWebURLLoaderClient(CefRenderURLRequest::Context* context,
+                        int request_flags);
+  ~CefWebURLLoaderClient() override;
+
+  // blink::WebURLLoaderClient methods.
+  void DidSendData(uint64_t bytes_sent,
+                   uint64_t total_bytes_to_be_sent) override;
+  void DidReceiveResponse(const WebURLResponse& response) override;
+  void DidReceiveData(const char* data, int dataLength) override;
+  void DidFinishLoading(base::TimeTicks finish_time,
+                        int64_t total_encoded_data_length,
+                        int64_t total_encoded_body_length,
+                        int64_t total_decoded_body_length,
+                        bool should_report_corb_blocking) override;
+  void DidFail(const WebURLError&,
+               int64_t total_encoded_data_length,
+               int64_t total_encoded_body_length,
+               int64_t total_decoded_body_length) override;
+  void DidStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle response_body) override;
+  bool WillFollowRedirect(const WebURL& new_url,
+                          const net::SiteForCookies& new_site_for_cookies,
+                          const WebString& new_referrer,
+                          network::mojom::ReferrerPolicy new_referrer_policy,
+                          const WebString& new_method,
+                          const WebURLResponse& passed_redirect_response,
+                          bool& report_raw_headers) override;
+
+ protected:
+  // The context_ pointer will outlive this object.
+  CefRenderURLRequest::Context* context_;
+  int request_flags_;
+};
+
+}  // namespace
+
+// CefRenderURLRequest::Context -----------------------------------------------
+
+class CefRenderURLRequest::Context
+    : public base::RefCountedThreadSafe<CefRenderURLRequest::Context> {
+ public:
+  Context(CefRefPtr<CefRenderURLRequest> url_request,
+          CefRefPtr<CefFrame> frame,
+          CefRefPtr<CefRequest> request,
+          CefRefPtr<CefURLRequestClient> client)
+      : url_request_(url_request),
+        frame_(frame),
+        request_(request),
+        client_(client),
+        task_runner_(CefTaskRunnerImpl::GetCurrentTaskRunner()),
+        status_(UR_IO_PENDING),
+        error_code_(ERR_NONE),
+        body_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
+        response_was_cached_(false),
+        upload_data_size_(0),
+        got_upload_progress_complete_(false),
+        download_data_received_(0),
+        download_data_total_(-1) {
+    // Mark the request as read-only.
+    static_cast<CefRequestImpl*>(request_.get())->SetReadOnly(true);
+  }
+
+  inline bool CalledOnValidThread() {
+    return task_runner_->RunsTasksInCurrentSequence();
+  }
+
+  bool Start() {
+    DCHECK(CalledOnValidThread());
+
+    GURL url = GURL(request_->GetURL().ToString());
+    if (!url.is_valid())
+      return false;
+
+    url_client_.reset(new CefWebURLLoaderClient(this, request_->GetFlags()));
+
+    std::unique_ptr<network::ResourceRequest> resource_request =
+        std::make_unique<network::ResourceRequest>();
+    static_cast<CefRequestImpl*>(request_.get())
+        ->Get(resource_request.get(), false);
+    resource_request->priority = net::MEDIUM;
+
+    // Behave the same as a subresource load.
+    resource_request->fetch_request_context_type =
+        static_cast<int>(blink::mojom::RequestContextType::SUBRESOURCE);
+    resource_request->resource_type =
+        static_cast<int>(blink::mojom::ResourceType::kSubResource);
+
+    // Need load timing info for WebURLLoaderImpl::PopulateURLResponse to
+    // properly set cached status.
+    resource_request->enable_load_timing = true;
+
+    // Set the origin to match the request. The requirement for an origin is
+    // DCHECK'd in ResourceDispatcherHostImpl::ContinuePendingBeginRequest.
+    resource_request->request_initiator = url::Origin::Create(url);
+
+    if (request_->GetFlags() & UR_FLAG_ALLOW_STORED_CREDENTIALS) {
+      // Include SameSite cookies.
+      resource_request->attach_same_site_cookies = true;
+      resource_request->site_for_cookies =
+          net::SiteForCookies::FromOrigin(*resource_request->request_initiator);
+    }
+
+    if (resource_request->request_body) {
+      const auto& elements = *resource_request->request_body->elements();
+      if (elements.size() > 0) {
+        const auto& element = elements[0];
+        if (element.type() == network::mojom::DataElementType::kBytes) {
+          upload_data_size_ = element.length() - element.offset();
+        }
+      }
+    }
+
+    blink::WebURLLoaderFactory* factory = nullptr;
+    if (frame_) {
+      // This factory supports all requests.
+      factory = static_cast<CefFrameImpl*>(frame_.get())->GetURLLoaderFactory();
+    }
+    if (!factory) {
+      // This factory only supports unintercepted http(s) and blob requests.
+      factory = CefContentRendererClient::Get()->GetDefaultURLLoaderFactory();
+    }
+
+    loader_ = factory->CreateURLLoader(
+        blink::WebURLRequest(),
+        blink::scheduler::WebResourceLoadingTaskRunnerHandle::
+            CreateUnprioritized(task_runner_.get()));
+    loader_->LoadAsynchronously(
+        std::move(resource_request), nullptr /* extra_data */,
+        0 /* requestor_id */, false /* download_to_network_cache_only */,
+        false /* no_mime_sniffing */, url_client_.get());
+    return true;
+  }
+
+  void Cancel() {
+    DCHECK(CalledOnValidThread());
+
+    // The request may already be complete.
+    if (!loader_.get() || status_ != UR_IO_PENDING)
+      return;
+
+    status_ = UR_CANCELED;
+    error_code_ = ERR_ABORTED;
+
+    // Will result in a call to OnError().
+    loader_->Cancel();
+  }
+
+  void OnStopRedirect(const WebURL& redirect_url,
+                      const WebURLResponse& response) {
+    DCHECK(CalledOnValidThread());
+
+    response_was_cached_ = blink_glue::ResponseWasCached(response);
+    response_ = CefResponse::Create();
+    CefResponseImpl* responseImpl =
+        static_cast<CefResponseImpl*>(response_.get());
+
+    // In case of StopOnRedirect we only set these fields. Everything else is
+    // left blank. This also replicates the behaviour of the browser urlrequest
+    // fetcher.
+    responseImpl->SetStatus(response.HttpStatusCode());
+    responseImpl->SetURL(redirect_url.GetString().Utf16());
+    responseImpl->SetReadOnly(true);
+
+    status_ = UR_CANCELED;
+    error_code_ = ERR_ABORTED;
+
+    OnComplete();
+  }
+
+  void OnResponse(const WebURLResponse& response) {
+    DCHECK(CalledOnValidThread());
+
+    response_was_cached_ = blink_glue::ResponseWasCached(response);
+    response_ = CefResponse::Create();
+    CefResponseImpl* responseImpl =
+        static_cast<CefResponseImpl*>(response_.get());
+    responseImpl->Set(response);
+    responseImpl->SetReadOnly(true);
+
+    download_data_total_ = response.ExpectedContentLength();
+  }
+
+  void OnError(const WebURLError& error) {
+    DCHECK(CalledOnValidThread());
+
+    if (status_ == UR_IO_PENDING) {
+      status_ = UR_FAILED;
+      error_code_ = static_cast<cef_errorcode_t>(error.reason());
+    }
+
+    OnComplete();
+  }
+
+  void OnComplete() {
+    DCHECK(CalledOnValidThread());
+
+    if (body_handle_.is_valid()) {
+      return;
+    }
+
+    if (status_ == UR_IO_PENDING) {
+      status_ = UR_SUCCESS;
+      NotifyUploadProgressIfNecessary();
+    }
+
+    if (loader_.get())
+      loader_.reset(nullptr);
+
+    DCHECK(url_request_.get());
+    client_->OnRequestComplete(url_request_.get());
+
+    // This may result in the Context object being deleted.
+    url_request_ = nullptr;
+  }
+
+  void OnBodyReadable(MojoResult, const mojo::HandleSignalsState&) {
+    const void* buffer = nullptr;
+    uint32_t read_bytes = 0;
+    MojoResult result = body_handle_->BeginReadData(&buffer, &read_bytes,
+                                                    MOJO_READ_DATA_FLAG_NONE);
+    if (result == MOJO_RESULT_SHOULD_WAIT) {
+      body_watcher_.ArmOrNotify();
+      return;
+    }
+
+    if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+      // Whole body has been read.
+      body_handle_.reset();
+      body_watcher_.Cancel();
+      OnComplete();
+      return;
+    }
+
+    if (result != MOJO_RESULT_OK) {
+      // Something went wrong.
+      body_handle_.reset();
+      body_watcher_.Cancel();
+      OnComplete();
+      return;
+    }
+
+    download_data_received_ += read_bytes;
+
+    client_->OnDownloadProgress(url_request_.get(), download_data_received_,
+                                download_data_total_);
+
+    if (!(request_->GetFlags() & UR_FLAG_NO_DOWNLOAD_DATA)) {
+      client_->OnDownloadData(url_request_.get(), buffer, read_bytes);
+    }
+
+    body_handle_->EndReadData(read_bytes);
+    body_watcher_.ArmOrNotify();
+  }
+
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle response_body) {
+    DCHECK(CalledOnValidThread());
+    DCHECK(response_body);
+    DCHECK(!body_handle_);
+    body_handle_ = std::move(response_body);
+
+    body_watcher_.Watch(
+        body_handle_.get(),
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+        MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+        base::BindRepeating(&CefRenderURLRequest::Context::OnBodyReadable,
+                            base::Unretained(this)));
+    body_watcher_.ArmOrNotify();
+  }
+
+  void OnDownloadProgress(int64_t current) {
+    DCHECK(CalledOnValidThread());
+    DCHECK(url_request_.get());
+
+    NotifyUploadProgressIfNecessary();
+
+    download_data_received_ += current;
+    client_->OnDownloadProgress(url_request_.get(), download_data_received_,
+                                download_data_total_);
+  }
+
+  void OnDownloadData(const char* data, int dataLength) {
+    DCHECK(CalledOnValidThread());
+    DCHECK(url_request_.get());
+    client_->OnDownloadData(url_request_.get(), data, dataLength);
+  }
+
+  void OnUploadProgress(int64_t current, int64_t total) {
+    DCHECK(CalledOnValidThread());
+    DCHECK(url_request_.get());
+    if (current == total)
+      got_upload_progress_complete_ = true;
+    client_->OnUploadProgress(url_request_.get(), current, total);
+  }
+
+  CefRefPtr<CefRequest> request() const { return request_; }
+  CefRefPtr<CefURLRequestClient> client() const { return client_; }
+  CefURLRequest::Status status() const { return status_; }
+  CefURLRequest::ErrorCode error_code() const { return error_code_; }
+  CefRefPtr<CefResponse> response() const { return response_; }
+  bool response_was_cached() const { return response_was_cached_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<CefRenderURLRequest::Context>;
+
+  virtual ~Context() {}
+
+  void NotifyUploadProgressIfNecessary() {
+    if (!got_upload_progress_complete_ && upload_data_size_ > 0) {
+      // Upload notifications are sent using a timer and may not occur if the
+      // request completes too quickly. We therefore send the notification here
+      // if necessary.
+      url_client_->DidSendData(upload_data_size_, upload_data_size_);
+      got_upload_progress_complete_ = true;
+    }
+  }
+
+  // Members only accessed on the initialization thread.
+  CefRefPtr<CefRenderURLRequest> url_request_;
+  CefRefPtr<CefFrame> frame_;
+  CefRefPtr<CefRequest> request_;
+  CefRefPtr<CefURLRequestClient> client_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  CefURLRequest::Status status_;
+  CefURLRequest::ErrorCode error_code_;
+  CefRefPtr<CefResponse> response_;
+  mojo::ScopedDataPipeConsumerHandle body_handle_;
+  mojo::SimpleWatcher body_watcher_;
+  bool response_was_cached_;
+  std::unique_ptr<blink::WebURLLoader> loader_;
+  std::unique_ptr<CefWebURLLoaderClient> url_client_;
+  int64_t upload_data_size_;
+  bool got_upload_progress_complete_;
+  int64_t download_data_received_;
+  int64_t download_data_total_;
+};
+
+// CefWebURLLoaderClient --------------------------------------------------
+
+namespace {
+
+CefWebURLLoaderClient::CefWebURLLoaderClient(
+    CefRenderURLRequest::Context* context,
+    int request_flags)
+    : context_(context), request_flags_(request_flags) {}
+
+CefWebURLLoaderClient::~CefWebURLLoaderClient() {}
+
+void CefWebURLLoaderClient::DidSendData(uint64_t bytes_sent,
+                                        uint64_t total_bytes_to_be_sent) {
+  if (request_flags_ & UR_FLAG_REPORT_UPLOAD_PROGRESS)
+    context_->OnUploadProgress(bytes_sent, total_bytes_to_be_sent);
+}
+
+void CefWebURLLoaderClient::DidReceiveResponse(const WebURLResponse& response) {
+  context_->OnResponse(response);
+}
+
+void CefWebURLLoaderClient::DidReceiveData(const char* data, int dataLength) {
+  context_->OnDownloadProgress(dataLength);
+
+  if (!(request_flags_ & UR_FLAG_NO_DOWNLOAD_DATA))
+    context_->OnDownloadData(data, dataLength);
+}
+
+void CefWebURLLoaderClient::DidFinishLoading(base::TimeTicks finish_time,
+                                             int64_t total_encoded_data_length,
+                                             int64_t total_encoded_body_length,
+                                             int64_t total_decoded_body_length,
+                                             bool should_report_corb_blocking) {
+  context_->OnComplete();
+}
+
+void CefWebURLLoaderClient::DidFail(const WebURLError& error,
+                                    int64_t total_encoded_data_length,
+                                    int64_t total_encoded_body_length,
+                                    int64_t total_decoded_body_length) {
+  context_->OnError(error);
+}
+
+void CefWebURLLoaderClient::DidStartLoadingResponseBody(
+    mojo::ScopedDataPipeConsumerHandle response_body) {
+  context_->OnStartLoadingResponseBody(std::move(response_body));
+}
+
+bool CefWebURLLoaderClient::WillFollowRedirect(
+    const WebURL& new_url,
+    const net::SiteForCookies& new_site_for_cookies,
+    const WebString& new_referrer,
+    network::mojom::ReferrerPolicy new_referrer_policy,
+    const WebString& new_method,
+    const WebURLResponse& passed_redirect_response,
+    bool& report_raw_headers) {
+  if (request_flags_ & UR_FLAG_STOP_ON_REDIRECT) {
+    context_->OnStopRedirect(new_url, passed_redirect_response);
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+// CefRenderURLRequest --------------------------------------------------------
+
+CefRenderURLRequest::CefRenderURLRequest(
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefURLRequestClient> client) {
+  context_ = new Context(this, frame, request, client);
+}
+
+CefRenderURLRequest::~CefRenderURLRequest() {}
+
+bool CefRenderURLRequest::Start() {
+  if (!VerifyContext())
+    return false;
+  return context_->Start();
+}
+
+CefRefPtr<CefRequest> CefRenderURLRequest::GetRequest() {
+  if (!VerifyContext())
+    return nullptr;
+  return context_->request();
+}
+
+CefRefPtr<CefURLRequestClient> CefRenderURLRequest::GetClient() {
+  if (!VerifyContext())
+    return nullptr;
+  return context_->client();
+}
+
+CefURLRequest::Status CefRenderURLRequest::GetRequestStatus() {
+  if (!VerifyContext())
+    return UR_UNKNOWN;
+  return context_->status();
+}
+
+CefURLRequest::ErrorCode CefRenderURLRequest::GetRequestError() {
+  if (!VerifyContext())
+    return ERR_NONE;
+  return context_->error_code();
+}
+
+CefRefPtr<CefResponse> CefRenderURLRequest::GetResponse() {
+  if (!VerifyContext())
+    return nullptr;
+  return context_->response();
+}
+
+bool CefRenderURLRequest::ResponseWasCached() {
+  if (!VerifyContext())
+    return false;
+  return context_->response_was_cached();
+}
+
+void CefRenderURLRequest::Cancel() {
+  if (!VerifyContext())
+    return;
+  return context_->Cancel();
+}
+
+bool CefRenderURLRequest::VerifyContext() {
+  DCHECK(context_.get());
+  if (!context_->CalledOnValidThread()) {
+    NOTREACHED() << "called on invalid thread";
+    return false;
+  }
+
+  return true;
+}
diff --git a/src/libcef/renderer/render_urlrequest_impl.h b/src/libcef/renderer/render_urlrequest_impl.h
new file mode 100644
index 0000000..a705864
--- /dev/null
+++ b/src/libcef/renderer/render_urlrequest_impl.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_RENDER_URLREQUEST_IMPL_H_
+#define CEF_LIBCEF_RENDERER_RENDER_URLREQUEST_IMPL_H_
+
+#include "include/cef_frame.h"
+#include "include/cef_urlrequest.h"
+
+#include "base/memory/ref_counted.h"
+
+class CefRenderURLRequest : public CefURLRequest {
+ public:
+  class Context;
+
+  // If |frame| is nullptr the default URLLoaderFactory will be used. That
+  // factory only supports http(s) and blob requests that cannot be
+  // intercepted in the browser process.
+  CefRenderURLRequest(CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefURLRequestClient> client);
+  ~CefRenderURLRequest() override;
+
+  bool Start();
+
+  // CefURLRequest methods.
+  CefRefPtr<CefRequest> GetRequest() override;
+  CefRefPtr<CefURLRequestClient> GetClient() override;
+  Status GetRequestStatus() override;
+  ErrorCode GetRequestError() override;
+  CefRefPtr<CefResponse> GetResponse() override;
+  bool ResponseWasCached() override;
+  void Cancel() override;
+
+ private:
+  bool VerifyContext();
+
+  scoped_refptr<Context> context_;
+
+  IMPLEMENT_REFCOUNTING(CefRenderURLRequest);
+};
+
+#endif  // CEF_LIBCEF_RENDERER_RENDER_URLREQUEST_IMPL_H_
diff --git a/src/libcef/renderer/thread_util.h b/src/libcef/renderer/thread_util.h
new file mode 100644
index 0000000..7087b5e
--- /dev/null
+++ b/src/libcef/renderer/thread_util.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_THREAD_UTIL_H_
+#define CEF_LIBCEF_RENDERER_THREAD_UTIL_H_
+#pragma once
+
+#include "libcef/renderer/content_renderer_client.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "content/public/renderer/render_thread.h"
+
+#define CEF_CURRENTLY_ON_RT() (!!content::RenderThread::Get())
+
+#define CEF_REQUIRE_RT() DCHECK(CEF_CURRENTLY_ON_RT())
+
+#define CEF_REQUIRE_RT_RETURN(var)              \
+  if (!CEF_CURRENTLY_ON_RT()) {                 \
+    NOTREACHED() << "called on invalid thread"; \
+    return var;                                 \
+  }
+
+#define CEF_REQUIRE_RT_RETURN_VOID()            \
+  if (!CEF_CURRENTLY_ON_RT()) {                 \
+    NOTREACHED() << "called on invalid thread"; \
+    return;                                     \
+  }
+
+#define CEF_RENDER_LOOP() \
+  (CefContentRendererClient::Get()->render_task_runner())
+
+#define CEF_POST_TASK_RT(task) CEF_RENDER_LOOP()->PostTask(FROM_HERE, task)
+#define CEF_POST_DELAYED_TASK_RT(task, delay_ms) \
+  CEF_RENDER_LOOP()->PostDelayedTask(            \
+      FROM_HERE, task, base::TimeDelta::FromMilliseconds(delay_ms))
+
+// Use this template in conjuction with RefCountedThreadSafe when you want to
+// ensure that an object is deleted on the render thread.
+struct CefDeleteOnRenderThread {
+  template <typename T>
+  static void Destruct(const T* x) {
+    if (CEF_CURRENTLY_ON_RT()) {
+      delete x;
+    } else {
+      if (!CEF_RENDER_LOOP()->DeleteSoon(FROM_HERE, x)) {
+#if defined(UNIT_TEST)
+        // Only logged under unit testing because leaks at shutdown
+        // are acceptable under normal circumstances.
+        LOG(ERROR) << "DeleteSoon failed on thread " << thread;
+#endif  // UNIT_TEST
+      }
+    }
+  }
+};
+
+#endif  // CEF_LIBCEF_RENDERER_THREAD_UTIL_H_
diff --git a/src/libcef/renderer/url_loader_throttle_provider_impl.cc b/src/libcef/renderer/url_loader_throttle_provider_impl.cc
new file mode 100644
index 0000000..61f6f8d
--- /dev/null
+++ b/src/libcef/renderer/url_loader_throttle_provider_impl.cc
@@ -0,0 +1,85 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libcef/renderer/url_loader_throttle_provider_impl.h"
+
+#include "libcef/common/extensions/extensions_util.h"
+#include "libcef/renderer/render_thread_observer.h"
+
+#include <utility>
+
+#include "base/feature_list.h"
+#include "chrome/common/google_url_loader_throttle.h"
+#include "content/public/common/content_features.h"
+#include "content/public/renderer/render_frame.h"
+#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
+#include "services/network/public/cpp/features.h"
+#include "third_party/blink/public/common/loader/resource_type_util.h"
+#include "third_party/blink/public/platform/web_url.h"
+
+CefURLLoaderThrottleProviderImpl::CefURLLoaderThrottleProviderImpl(
+    content::URLLoaderThrottleProviderType type)
+    : type_(type) {
+  DETACH_FROM_THREAD(thread_checker_);
+}
+
+CefURLLoaderThrottleProviderImpl::~CefURLLoaderThrottleProviderImpl() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+CefURLLoaderThrottleProviderImpl::CefURLLoaderThrottleProviderImpl(
+    const CefURLLoaderThrottleProviderImpl& other)
+    : type_(other.type_) {
+  DETACH_FROM_THREAD(thread_checker_);
+}
+
+std::unique_ptr<content::URLLoaderThrottleProvider>
+CefURLLoaderThrottleProviderImpl::Clone() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return base::WrapUnique(new CefURLLoaderThrottleProviderImpl(*this));
+}
+
+std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+CefURLLoaderThrottleProviderImpl::CreateThrottles(
+    int render_frame_id,
+    const blink::WebURLRequest& request) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
+
+  const network::mojom::RequestDestination request_destination =
+      request.GetRequestDestination();
+
+  // Some throttles have already been added in the browser for frame resources.
+  // Don't add them for frame requests.
+  bool is_frame_resource =
+      blink::IsRequestDestinationFrame(request_destination);
+
+  DCHECK(!is_frame_resource ||
+         type_ == content::URLLoaderThrottleProviderType::kFrame);
+
+  if (extensions::ExtensionsEnabled() &&
+      type_ == content::URLLoaderThrottleProviderType::kFrame &&
+      request_destination == network::mojom::RequestDestination::kObject) {
+    content::RenderFrame* render_frame =
+        content::RenderFrame::FromRoutingID(render_frame_id);
+    auto mime_handlers =
+        extensions::MimeHandlerViewContainer::FromRenderFrame(render_frame);
+    GURL gurl(request.Url());
+    for (auto* handler : mime_handlers) {
+      auto throttle = handler->MaybeCreatePluginThrottle(gurl);
+      if (throttle) {
+        throttles.push_back(std::move(throttle));
+        break;
+      }
+    }
+  }
+
+  throttles.push_back(std::make_unique<GoogleURLLoaderThrottle>(
+      CefRenderThreadObserver::GetDynamicParams()));
+
+  return throttles;
+}
+
+void CefURLLoaderThrottleProviderImpl::SetOnline(bool is_online) {}
diff --git a/src/libcef/renderer/url_loader_throttle_provider_impl.h b/src/libcef/renderer/url_loader_throttle_provider_impl.h
new file mode 100644
index 0000000..1fe2ca0
--- /dev/null
+++ b/src/libcef/renderer/url_loader_throttle_provider_impl.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_URL_LOADER_THROTTLE_PROVIDER_IMPL_H_
+#define CEF_LIBCEF_RENDERER_URL_LOADER_THROTTLE_PROVIDER_IMPL_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/threading/thread_checker.h"
+#include "content/public/renderer/url_loader_throttle_provider.h"
+
+// Instances must be constructed on the render thread, and then used and
+// destructed on a single thread, which can be different from the render thread.
+class CefURLLoaderThrottleProviderImpl
+    : public content::URLLoaderThrottleProvider {
+ public:
+  explicit CefURLLoaderThrottleProviderImpl(
+      content::URLLoaderThrottleProviderType type);
+
+  ~CefURLLoaderThrottleProviderImpl() override;
+
+  // content::URLLoaderThrottleProvider implementation.
+  std::unique_ptr<content::URLLoaderThrottleProvider> Clone() override;
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>> CreateThrottles(
+      int render_frame_id,
+      const blink::WebURLRequest& request) override;
+  void SetOnline(bool is_online) override;
+
+ private:
+  // This copy constructor works in conjunction with Clone(), not intended for
+  // general use.
+  CefURLLoaderThrottleProviderImpl(
+      const CefURLLoaderThrottleProviderImpl& other);
+
+  content::URLLoaderThrottleProviderType type_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_ASSIGN(CefURLLoaderThrottleProviderImpl);
+};
+
+#endif  // CEF_LIBCEF_RENDERER_URL_LOADER_THROTTLE_PROVIDER_IMPL_H_
diff --git a/src/libcef/renderer/v8_impl.cc b/src/libcef/renderer/v8_impl.cc
new file mode 100644
index 0000000..63070b7
--- /dev/null
+++ b/src/libcef/renderer/v8_impl.cc
@@ -0,0 +1,2560 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+// Otherwise there will be compile errors in wtf/MathExtras.h.
+#define _USE_MATH_DEFINES
+
+#include <map>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+
+// Enable deprecation warnings for MSVC and Clang. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wdeprecated-declarations"
+#else
+#pragma warning(push)
+#pragma warning(default : 4996)
+#endif
+#endif
+
+#include "libcef/renderer/v8_impl.h"
+
+#include "libcef/common/cef_switches.h"
+#include "libcef/common/content_client.h"
+#include "libcef/common/task_runner_impl.h"
+#include "libcef/common/tracker.h"
+#include "libcef/renderer/blink_glue.h"
+#include "libcef/renderer/browser_impl.h"
+#include "libcef/renderer/render_frame_util.h"
+#include "libcef/renderer/thread_util.h"
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_local.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_frame.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "url/gurl.h"
+
+namespace {
+
+static const char kCefTrackObject[] = "Cef::TrackObject";
+
+void MessageListenerCallbackImpl(v8::Handle<v8::Message> message,
+                                 v8::Handle<v8::Value> data);
+
+// The following *Private functions are convenience wrappers for methods on
+// v8::Object with the corresponding names.
+// Based on extensions/renderer/object_backed_native_handler.cc.
+
+void SetPrivate(v8::Local<v8::Context> context,
+                v8::Local<v8::Object> obj,
+                const char* key,
+                v8::Local<v8::Value> value) {
+  v8::Isolate* isolate = context->GetIsolate();
+  obj->SetPrivate(context,
+                  v8::Private::ForApi(
+                      isolate, v8::String::NewFromUtf8(
+                                   isolate, key, v8::NewStringType::kNormal)
+                                   .ToLocalChecked()),
+                  value)
+      .FromJust();
+}
+
+bool GetPrivate(v8::Local<v8::Context> context,
+                v8::Local<v8::Object> obj,
+                const char* key,
+                v8::Local<v8::Value>* result) {
+  v8::Isolate* isolate = context->GetIsolate();
+  return obj
+      ->GetPrivate(context,
+                   v8::Private::ForApi(
+                       isolate, v8::String::NewFromUtf8(
+                                    isolate, key, v8::NewStringType::kNormal)
+                                    .ToLocalChecked()))
+      .ToLocal(result);
+}
+
+// Manages memory and state information associated with a single Isolate.
+class CefV8IsolateManager {
+ public:
+  CefV8IsolateManager()
+      : isolate_(v8::Isolate::GetCurrent()),
+        task_runner_(CefContentRendererClient::Get()->GetCurrentTaskRunner()),
+        message_listener_registered_(false),
+        worker_id_(0) {
+    DCHECK(isolate_);
+    DCHECK(task_runner_.get());
+  }
+  ~CefV8IsolateManager() {
+    DCHECK_EQ(isolate_, v8::Isolate::GetCurrent());
+    DCHECK(context_map_.empty());
+  }
+
+  scoped_refptr<CefV8ContextState> GetContextState(
+      v8::Local<v8::Context> context) {
+    DCHECK_EQ(isolate_, v8::Isolate::GetCurrent());
+    DCHECK(context.IsEmpty() || isolate_ == context->GetIsolate());
+
+    if (context.IsEmpty()) {
+      if (isolate_->InContext())
+        context = isolate_->GetCurrentContext();
+      else
+        return scoped_refptr<CefV8ContextState>();
+    }
+
+    int hash = context->Global()->GetIdentityHash();
+    ContextMap::const_iterator it = context_map_.find(hash);
+    if (it != context_map_.end())
+      return it->second;
+
+    scoped_refptr<CefV8ContextState> state = new CefV8ContextState();
+    context_map_.insert(std::make_pair(hash, state));
+
+    return state;
+  }
+
+  void ReleaseContext(v8::Local<v8::Context> context) {
+    DCHECK_EQ(isolate_, v8::Isolate::GetCurrent());
+    DCHECK_EQ(isolate_, context->GetIsolate());
+
+    int hash = context->Global()->GetIdentityHash();
+    ContextMap::iterator it = context_map_.find(hash);
+    if (it != context_map_.end()) {
+      it->second->Detach();
+      context_map_.erase(it);
+    }
+  }
+
+  void AddGlobalTrackObject(CefTrackNode* object) {
+    DCHECK_EQ(isolate_, v8::Isolate::GetCurrent());
+    global_manager_.Add(object);
+  }
+
+  void DeleteGlobalTrackObject(CefTrackNode* object) {
+    DCHECK_EQ(isolate_, v8::Isolate::GetCurrent());
+    global_manager_.Delete(object);
+  }
+
+  void SetUncaughtExceptionStackSize(int stack_size) {
+    if (stack_size <= 0)
+      return;
+
+    if (!message_listener_registered_) {
+      isolate_->AddMessageListener(&MessageListenerCallbackImpl);
+      message_listener_registered_ = true;
+    }
+
+    isolate_->SetCaptureStackTraceForUncaughtExceptions(
+        true, stack_size, v8::StackTrace::kDetailed);
+  }
+
+  void SetWorkerAttributes(int worker_id, const GURL& worker_url) {
+    worker_id_ = worker_id;
+    worker_url_ = worker_url;
+  }
+
+  v8::Isolate* isolate() const { return isolate_; }
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
+    return task_runner_;
+  }
+
+  int worker_id() const { return worker_id_; }
+
+  const GURL& worker_url() const { return worker_url_; }
+
+ private:
+  v8::Isolate* isolate_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  typedef std::map<int, scoped_refptr<CefV8ContextState>> ContextMap;
+  ContextMap context_map_;
+
+  // Used for globally tracked objects that are not associated with a particular
+  // context.
+  CefTrackManager global_manager_;
+
+  // True if the message listener has been registered.
+  bool message_listener_registered_;
+
+  // Attributes associated with WebWorker threads.
+  int worker_id_;
+  GURL worker_url_;
+};
+
+// Chromium uses the default Isolate for the main render process thread and a
+// new Isolate for each WebWorker thread. Continue this pattern by tracking
+// Isolate information on a per-thread basis. This implementation will need to
+// be re-worked (perhaps using a map keyed on v8::Isolate::GetCurrent()) if
+// in the future Chromium begins using the same Isolate across multiple threads.
+class CefV8StateManager {
+ public:
+  CefV8StateManager() {}
+
+  void CreateIsolateManager() {
+    DCHECK(!current_tls_.Get());
+    current_tls_.Set(new CefV8IsolateManager());
+  }
+
+  void DestroyIsolateManager() {
+    DCHECK(current_tls_.Get());
+    delete current_tls_.Get();
+    current_tls_.Set(nullptr);
+  }
+
+  CefV8IsolateManager* GetIsolateManager() {
+    CefV8IsolateManager* manager = current_tls_.Get();
+    DCHECK(manager);
+    return manager;
+  }
+
+ private:
+  base::ThreadLocalPointer<CefV8IsolateManager> current_tls_;
+};
+
+base::LazyInstance<CefV8StateManager>::Leaky g_v8_state =
+    LAZY_INSTANCE_INITIALIZER;
+
+CefV8IsolateManager* GetIsolateManager() {
+  return g_v8_state.Pointer()->GetIsolateManager();
+}
+
+class V8TrackObject : public CefTrackNode {
+ public:
+  explicit V8TrackObject(v8::Isolate* isolate)
+      : isolate_(isolate), external_memory_(0) {
+    DCHECK(isolate_);
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        static_cast<int>(sizeof(V8TrackObject)));
+  }
+  ~V8TrackObject() {
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        -static_cast<int>(sizeof(V8TrackObject)) - external_memory_);
+  }
+
+  inline int GetExternallyAllocatedMemory() { return external_memory_; }
+
+  int AdjustExternallyAllocatedMemory(int change_in_bytes) {
+    int new_value = external_memory_ + change_in_bytes;
+    if (new_value < 0) {
+      NOTREACHED() << "External memory usage cannot be less than 0 bytes";
+      change_in_bytes = -(external_memory_);
+      new_value = 0;
+    }
+
+    if (change_in_bytes != 0)
+      isolate_->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
+    external_memory_ = new_value;
+
+    return new_value;
+  }
+
+  inline void SetAccessor(CefRefPtr<CefV8Accessor> accessor) {
+    accessor_ = accessor;
+  }
+
+  inline CefRefPtr<CefV8Accessor> GetAccessor() { return accessor_; }
+
+  inline void SetInterceptor(CefRefPtr<CefV8Interceptor> interceptor) {
+    interceptor_ = interceptor;
+  }
+
+  inline CefRefPtr<CefV8Interceptor> GetInterceptor() { return interceptor_; }
+
+  inline void SetHandler(CefRefPtr<CefV8Handler> handler) {
+    handler_ = handler;
+  }
+
+  inline CefRefPtr<CefV8Handler> GetHandler() { return handler_; }
+
+  inline void SetUserData(CefRefPtr<CefBaseRefCounted> user_data) {
+    user_data_ = user_data;
+  }
+
+  inline CefRefPtr<CefBaseRefCounted> GetUserData() { return user_data_; }
+
+  // Attach this track object to the specified V8 object.
+  void AttachTo(v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
+    SetPrivate(context, object, kCefTrackObject,
+               v8::External::New(isolate_, this));
+  }
+
+  // Retrieve the track object for the specified V8 object.
+  static V8TrackObject* Unwrap(v8::Local<v8::Context> context,
+                               v8::Local<v8::Object> object) {
+    v8::Local<v8::Value> value;
+    if (GetPrivate(context, object, kCefTrackObject, &value))
+      return static_cast<V8TrackObject*>(v8::External::Cast(*value)->Value());
+
+    return nullptr;
+  }
+
+ private:
+  v8::Isolate* isolate_;
+  CefRefPtr<CefV8Accessor> accessor_;
+  CefRefPtr<CefV8Interceptor> interceptor_;
+  CefRefPtr<CefV8Handler> handler_;
+  CefRefPtr<CefBaseRefCounted> user_data_;
+  int external_memory_;
+};
+
+class V8TrackString : public CefTrackNode {
+ public:
+  explicit V8TrackString(const std::string& str) : string_(str) {}
+  const char* GetString() { return string_.c_str(); }
+
+ private:
+  std::string string_;
+};
+
+class V8TrackArrayBuffer : public CefTrackNode {
+ public:
+  explicit V8TrackArrayBuffer(
+      v8::Isolate* isolate,
+      void* buffer,
+      CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback)
+      : isolate_(isolate),
+        buffer_(buffer),
+        release_callback_(release_callback) {
+    DCHECK(isolate_);
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        static_cast<int>(sizeof(V8TrackArrayBuffer)));
+  }
+
+  ~V8TrackArrayBuffer() {
+    if (buffer_ != nullptr) {
+      release_callback_->ReleaseBuffer(buffer_);
+    }
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        -static_cast<int>(sizeof(V8TrackArrayBuffer)));
+  }
+
+  CefRefPtr<CefV8ArrayBufferReleaseCallback> GetReleaseCallback() {
+    return release_callback_;
+  }
+
+  void Detach() { buffer_ = nullptr; }
+
+  // Attach this track object to the specified V8 object.
+  void AttachTo(v8::Local<v8::Context> context,
+                v8::Local<v8::ArrayBuffer> arrayBuffer) {
+    SetPrivate(context, arrayBuffer, kCefTrackObject,
+               v8::External::New(isolate_, this));
+  }
+
+  // Retrieve the track object for the specified V8 object.
+  static V8TrackArrayBuffer* Unwrap(v8::Local<v8::Context> context,
+                                    v8::Local<v8::Object> object) {
+    v8::Local<v8::Value> value;
+    if (GetPrivate(context, object, kCefTrackObject, &value))
+      return static_cast<V8TrackArrayBuffer*>(
+          v8::External::Cast(*value)->Value());
+
+    return nullptr;
+  }
+
+ private:
+  v8::Isolate* isolate_;
+  void* buffer_;
+  CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback_;
+};
+
+// Object wrapped in a v8::External and passed as the Data argument to
+// v8::FunctionTemplate::New.
+class V8FunctionData {
+ public:
+  static v8::Local<v8::External> Create(v8::Isolate* isolate,
+                                        const CefString& function_name,
+                                        CefRefPtr<CefV8Handler> handler) {
+    // |data| will be deleted if/when the returned v8::External is GC'd.
+    V8FunctionData* data = new V8FunctionData(isolate, function_name, handler);
+    return data->CreateExternal();
+  }
+
+  static V8FunctionData* Unwrap(v8::Local<v8::Value> data) {
+    DCHECK(data->IsExternal());
+    return static_cast<V8FunctionData*>(v8::External::Cast(*data)->Value());
+  }
+
+  CefString function_name() const { return function_name_; }
+
+  CefV8Handler* handler() const {
+    if (!handler_)
+      return nullptr;
+    return handler_.get();
+  }
+
+ private:
+  V8FunctionData(v8::Isolate* isolate,
+                 const CefString& function_name,
+                 CefRefPtr<CefV8Handler> handler)
+      : isolate_(isolate), function_name_(function_name), handler_(handler) {
+    DCHECK(isolate_);
+    DCHECK(handler_);
+  }
+
+  ~V8FunctionData() {
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        -static_cast<int>(sizeof(V8FunctionData)));
+    handler_ = nullptr;
+    function_name_ = "FreedFunction";
+  }
+
+  v8::Local<v8::External> CreateExternal() {
+    v8::Local<v8::External> external = v8::External::New(isolate_, this);
+
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        static_cast<int>(sizeof(V8FunctionData)));
+
+    handle_.Reset(isolate_, external);
+    handle_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter);
+
+    return external;
+  }
+
+  static void FirstWeakCallback(
+      const v8::WeakCallbackInfo<V8FunctionData>& data) {
+    V8FunctionData* wrapper = data.GetParameter();
+    wrapper->handle_.Reset();
+    data.SetSecondPassCallback(SecondWeakCallback);
+  }
+
+  static void SecondWeakCallback(
+      const v8::WeakCallbackInfo<V8FunctionData>& data) {
+    V8FunctionData* wrapper = data.GetParameter();
+    delete wrapper;
+  }
+
+  v8::Isolate* isolate_;
+  CefString function_name_;
+  CefRefPtr<CefV8Handler> handler_;
+  v8::Persistent<v8::External> handle_;
+};
+
+// Convert a CefString to a V8::String.
+v8::Local<v8::String> GetV8String(v8::Isolate* isolate, const CefString& str) {
+#if defined(CEF_STRING_TYPE_UTF16)
+  // Already a UTF16 string.
+  return v8::String::NewFromTwoByte(
+             isolate,
+             reinterpret_cast<uint16_t*>(
+                 const_cast<CefString::char_type*>(str.c_str())),
+             v8::NewStringType::kNormal, str.length())
+      .ToLocalChecked();
+#elif defined(CEF_STRING_TYPE_UTF8)
+  // Already a UTF8 string.
+  return v8::String::NewFromUtf8(isolate, const_cast<char*>(str.c_str()),
+                                 v8::NewStringType::kNormal, str.length())
+      .ToLocalChecked();
+#else
+  // Convert the string to UTF8.
+  std::string tmpStr = str;
+  return v8::String::NewFromUtf8(isolate, tmpStr.c_str(),
+                                 v8::NewStringType::kNormal, tmpStr.length())
+      .ToLocalChecked();
+#endif
+}
+
+#if defined(CEF_STRING_TYPE_UTF16)
+void v8impl_string_dtor(char16* str) {
+  delete[] str;
+}
+#elif defined(CEF_STRING_TYPE_UTF8)
+void v8impl_string_dtor(char* str) {
+  delete[] str;
+}
+#endif
+
+// Convert a v8::String to CefString.
+void GetCefString(v8::Isolate* isolate,
+                  v8::Local<v8::String> str,
+                  CefString& out) {
+  if (str.IsEmpty())
+    return;
+
+#if defined(CEF_STRING_TYPE_WIDE)
+  // Allocate enough space for a worst-case conversion.
+  int len = str->Utf8Length();
+  if (len == 0)
+    return;
+  char* buf = new char[len + 1];
+  str->WriteUtf8(isolate, buf, len + 1);
+
+  // Perform conversion to the wide type.
+  cef_string_t* retws = out.GetWritableStruct();
+  cef_string_utf8_to_wide(buf, len, retws);
+
+  delete[] buf;
+#else  // !defined(CEF_STRING_TYPE_WIDE)
+#if defined(CEF_STRING_TYPE_UTF16)
+  int len = str->Length();
+  if (len == 0)
+    return;
+  char16* buf = new char16[len + 1];
+  str->Write(isolate, reinterpret_cast<uint16_t*>(buf), 0, len + 1);
+#else
+  // Allocate enough space for a worst-case conversion.
+  int len = str->Utf8Length();
+  if (len == 0)
+    return;
+  char* buf = new char[len + 1];
+  str->WriteUtf8(isolate, buf, len + 1);
+#endif
+
+  // Don't perform an extra string copy.
+  out.clear();
+  cef_string_t* retws = out.GetWritableStruct();
+  retws->str = buf;
+  retws->length = len;
+  retws->dtor = v8impl_string_dtor;
+#endif  // !defined(CEF_STRING_TYPE_WIDE)
+}
+
+// V8 function callback.
+void FunctionCallbackImpl(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+  V8FunctionData* data = V8FunctionData::Unwrap(info.Data());
+  if (!data->handler()) {
+    // handler has gone away, bail!
+    info.GetReturnValue().SetUndefined();
+    return;
+  }
+  CefV8ValueList params;
+  for (int i = 0; i < info.Length(); i++)
+    params.push_back(new CefV8ValueImpl(isolate, context, info[i]));
+
+  CefRefPtr<CefV8Value> object =
+      new CefV8ValueImpl(isolate, context, info.This());
+  CefRefPtr<CefV8Value> retval;
+  CefString exception;
+
+  if (data->handler()->Execute(data->function_name(), object, params, retval,
+                               exception)) {
+    if (!exception.empty()) {
+      info.GetReturnValue().Set(isolate->ThrowException(
+          v8::Exception::Error(GetV8String(isolate, exception))));
+      return;
+    } else {
+      CefV8ValueImpl* rv = static_cast<CefV8ValueImpl*>(retval.get());
+      if (rv && rv->IsValid()) {
+        info.GetReturnValue().Set(rv->GetV8Value(true));
+        return;
+      }
+    }
+  }
+
+  info.GetReturnValue().SetUndefined();
+}
+
+// V8 Accessor callbacks
+void AccessorNameGetterCallbackImpl(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+  v8::Local<v8::Object> obj = info.This();
+
+  CefRefPtr<CefV8Accessor> accessorPtr;
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    accessorPtr = tracker->GetAccessor();
+
+  if (accessorPtr.get()) {
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj);
+    CefString name, exception;
+    GetCefString(isolate, v8::Local<v8::String>::Cast(property), name);
+    if (accessorPtr->Get(name, object, retval, exception)) {
+      if (!exception.empty()) {
+        info.GetReturnValue().Set(isolate->ThrowException(
+            v8::Exception::Error(GetV8String(isolate, exception))));
+        return;
+      } else {
+        CefV8ValueImpl* rv = static_cast<CefV8ValueImpl*>(retval.get());
+        if (rv && rv->IsValid()) {
+          info.GetReturnValue().Set(rv->GetV8Value(true));
+          return;
+        }
+      }
+    }
+  }
+
+  return info.GetReturnValue().SetUndefined();
+}
+
+void AccessorNameSetterCallbackImpl(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<void>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+  v8::Local<v8::Object> obj = info.This();
+
+  CefRefPtr<CefV8Accessor> accessorPtr;
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    accessorPtr = tracker->GetAccessor();
+
+  if (accessorPtr.get()) {
+    CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj);
+    CefRefPtr<CefV8Value> cefValue =
+        new CefV8ValueImpl(isolate, context, value);
+    CefString name, exception;
+    GetCefString(isolate, v8::Local<v8::String>::Cast(property), name);
+    accessorPtr->Set(name, object, cefValue, exception);
+    if (!exception.empty()) {
+      isolate->ThrowException(
+          v8::Exception::Error(GetV8String(isolate, exception)));
+      return;
+    }
+  }
+}
+
+// Two helper functions for V8 Interceptor callbacks.
+CefString PropertyToIndex(v8::Isolate* isolate, v8::Local<v8::Name> property) {
+  CefString name;
+  GetCefString(isolate, property.As<v8::String>(), name);
+  return name;
+}
+
+int PropertyToIndex(v8::Isolate* isolate, uint32_t index) {
+  return static_cast<int>(index);
+}
+
+// V8 Interceptor callbacks.
+// T == v8::Local<v8::Name> for named property handlers and
+// T == uint32_t for indexed property handlers
+template <typename T>
+void InterceptorGetterCallbackImpl(
+    T property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+  v8::Handle<v8::Object> obj = info.This();
+  CefRefPtr<CefV8Interceptor> interceptorPtr;
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    interceptorPtr = tracker->GetInterceptor();
+  if (!interceptorPtr.get())
+    return;
+
+  CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj);
+  CefRefPtr<CefV8Value> retval;
+  CefString exception;
+  interceptorPtr->Get(PropertyToIndex(isolate, property), object, retval,
+                      exception);
+  if (!exception.empty()) {
+    info.GetReturnValue().Set(isolate->ThrowException(
+        v8::Exception::Error(GetV8String(isolate, exception))));
+  } else {
+    CefV8ValueImpl* retval_impl = static_cast<CefV8ValueImpl*>(retval.get());
+    if (retval_impl && retval_impl->IsValid()) {
+      info.GetReturnValue().Set(retval_impl->GetV8Value(true));
+    }
+  }
+}
+
+template <typename T>
+void InterceptorSetterCallbackImpl(
+    T property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  v8::Handle<v8::Object> obj = info.This();
+  CefRefPtr<CefV8Interceptor> interceptorPtr;
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    interceptorPtr = tracker->GetInterceptor();
+
+  if (!interceptorPtr.get())
+    return;
+  CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj);
+  CefRefPtr<CefV8Value> cefValue = new CefV8ValueImpl(isolate, context, value);
+  CefString exception;
+  interceptorPtr->Set(PropertyToIndex(isolate, property), object, cefValue,
+                      exception);
+  if (!exception.empty()) {
+    isolate->ThrowException(
+        v8::Exception::Error(GetV8String(isolate, exception)));
+  }
+}
+
+// V8 extension registration.
+
+class ExtensionWrapper : public v8::Extension {
+ public:
+  ExtensionWrapper(const char* extension_name,
+                   const char* javascript_code,
+                   CefV8Handler* handler)
+      : v8::Extension(extension_name, javascript_code), handler_(handler) {}
+
+  v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
+      v8::Isolate* isolate,
+      v8::Handle<v8::String> name) override {
+    if (!handler_)
+      return v8::Local<v8::FunctionTemplate>();
+
+    CefString func_name;
+    GetCefString(isolate, name, func_name);
+
+    v8::Local<v8::External> function_data =
+        V8FunctionData::Create(isolate, func_name, handler_);
+
+    return v8::FunctionTemplate::New(isolate, FunctionCallbackImpl,
+                                     function_data);
+  }
+
+ private:
+  CefV8Handler* handler_;
+};
+
+class CefV8ExceptionImpl : public CefV8Exception {
+ public:
+  CefV8ExceptionImpl(v8::Local<v8::Context> context,
+                     v8::Local<v8::Message> message)
+      : line_number_(0),
+        start_position_(0),
+        end_position_(0),
+        start_column_(0),
+        end_column_(0) {
+    if (message.IsEmpty())
+      return;
+
+    v8::Isolate* isolate = context->GetIsolate();
+    GetCefString(isolate, message->Get(), message_);
+    v8::MaybeLocal<v8::String> source_line = message->GetSourceLine(context);
+    if (!source_line.IsEmpty())
+      GetCefString(isolate, source_line.ToLocalChecked(), source_line_);
+
+    if (!message->GetScriptResourceName().IsEmpty()) {
+      GetCefString(
+          isolate,
+          message->GetScriptResourceName()->ToString(context).ToLocalChecked(),
+          script_);
+    }
+
+    v8::Maybe<int> line_number = message->GetLineNumber(context);
+    if (!line_number.IsNothing())
+      line_number_ = line_number.ToChecked();
+    start_position_ = message->GetStartPosition();
+    end_position_ = message->GetEndPosition();
+    start_column_ = message->GetStartColumn(context).FromJust();
+    end_column_ = message->GetEndColumn(context).FromJust();
+  }
+
+  CefString GetMessage() override { return message_; }
+  CefString GetSourceLine() override { return source_line_; }
+  CefString GetScriptResourceName() override { return script_; }
+  int GetLineNumber() override { return line_number_; }
+  int GetStartPosition() override { return start_position_; }
+  int GetEndPosition() override { return end_position_; }
+  int GetStartColumn() override { return start_column_; }
+  int GetEndColumn() override { return end_column_; }
+
+ protected:
+  CefString message_;
+  CefString source_line_;
+  CefString script_;
+  int line_number_;
+  int start_position_;
+  int end_position_;
+  int start_column_;
+  int end_column_;
+
+  IMPLEMENT_REFCOUNTING(CefV8ExceptionImpl);
+};
+
+void MessageListenerCallbackImpl(v8::Handle<v8::Message> message,
+                                 v8::Handle<v8::Value> data) {
+  CefRefPtr<CefApp> application = CefContentClient::Get()->application();
+  if (!application.get())
+    return;
+
+  CefRefPtr<CefRenderProcessHandler> handler =
+      application->GetRenderProcessHandler();
+  if (!handler.get())
+    return;
+
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+  v8::Local<v8::StackTrace> v8Stack = message->GetStackTrace();
+  CefRefPtr<CefV8StackTrace> stackTrace =
+      new CefV8StackTraceImpl(isolate, v8Stack);
+
+  CefRefPtr<CefV8Exception> exception = new CefV8ExceptionImpl(
+      static_cast<CefV8ContextImpl*>(context.get())->GetV8Context(), message);
+
+  CefRefPtr<CefBrowser> browser = context->GetBrowser();
+  if (browser) {
+    handler->OnUncaughtException(browser, context->GetFrame(), context,
+                                 exception, stackTrace);
+  }
+}
+
+}  // namespace
+
+// Global functions.
+
+void CefV8IsolateCreated() {
+  g_v8_state.Pointer()->CreateIsolateManager();
+}
+
+void CefV8IsolateDestroyed() {
+  g_v8_state.Pointer()->DestroyIsolateManager();
+}
+
+void CefV8ReleaseContext(v8::Local<v8::Context> context) {
+  GetIsolateManager()->ReleaseContext(context);
+}
+
+void CefV8SetUncaughtExceptionStackSize(int stack_size) {
+  GetIsolateManager()->SetUncaughtExceptionStackSize(stack_size);
+}
+
+void CefV8SetWorkerAttributes(int worker_id, const GURL& worker_url) {
+  GetIsolateManager()->SetWorkerAttributes(worker_id, worker_url);
+}
+
+bool CefRegisterExtension(const CefString& extension_name,
+                          const CefString& javascript_code,
+                          CefRefPtr<CefV8Handler> handler) {
+  // Verify that this method was called on the correct thread.
+  CEF_REQUIRE_RT_RETURN(false);
+
+  CefV8IsolateManager* isolate_manager = GetIsolateManager();
+
+  V8TrackString* name = new V8TrackString(extension_name);
+  isolate_manager->AddGlobalTrackObject(name);
+  V8TrackString* code = new V8TrackString(javascript_code);
+  isolate_manager->AddGlobalTrackObject(code);
+
+  if (handler.get()) {
+    // The reference will be released when the process exits.
+    V8TrackObject* object = new V8TrackObject(isolate_manager->isolate());
+    object->SetHandler(handler);
+    isolate_manager->AddGlobalTrackObject(object);
+  }
+
+  std::unique_ptr<v8::Extension> wrapper(new ExtensionWrapper(
+      name->GetString(), code->GetString(), handler.get()));
+
+  content::RenderThread::Get()->RegisterExtension(std::move(wrapper));
+  return true;
+}
+
+// Helper macros
+
+#define CEF_V8_HAS_ISOLATE() (!!GetIsolateManager())
+#define CEF_V8_REQUIRE_ISOLATE_RETURN(var)     \
+  if (!CEF_V8_HAS_ISOLATE()) {                 \
+    NOTREACHED() << "V8 isolate is not valid"; \
+    return var;                                \
+  }
+
+#define CEF_V8_CURRENTLY_ON_MLT() \
+  (!handle_.get() || handle_->BelongsToCurrentThread())
+#define CEF_V8_REQUIRE_MLT_RETURN(var)            \
+  CEF_V8_REQUIRE_ISOLATE_RETURN(var);             \
+  if (!CEF_V8_CURRENTLY_ON_MLT()) {               \
+    NOTREACHED() << "called on incorrect thread"; \
+    return var;                                   \
+  }
+
+#define CEF_V8_HANDLE_IS_VALID() (handle_.get() && handle_->IsValid())
+#define CEF_V8_REQUIRE_VALID_HANDLE_RETURN(ret) \
+  CEF_V8_REQUIRE_MLT_RETURN(ret);               \
+  if (!CEF_V8_HANDLE_IS_VALID()) {              \
+    NOTREACHED() << "V8 handle is not valid";   \
+    return ret;                                 \
+  }
+
+#define CEF_V8_IS_VALID()                               \
+  (CEF_V8_HAS_ISOLATE() && CEF_V8_CURRENTLY_ON_MLT() && \
+   CEF_V8_HANDLE_IS_VALID())
+
+#define CEF_V8_REQUIRE_OBJECT_RETURN(ret)        \
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(ret);       \
+  if (type_ != TYPE_OBJECT) {                    \
+    NOTREACHED() << "V8 value is not an object"; \
+    return ret;                                  \
+  }
+
+// CefV8HandleBase
+
+CefV8HandleBase::~CefV8HandleBase() {
+  DCHECK(BelongsToCurrentThread());
+}
+
+bool CefV8HandleBase::BelongsToCurrentThread() const {
+  return task_runner_->RunsTasksInCurrentSequence();
+}
+
+CefV8HandleBase::CefV8HandleBase(v8::Isolate* isolate,
+                                 v8::Local<v8::Context> context)
+    : isolate_(isolate) {
+  DCHECK(isolate_);
+
+  CefV8IsolateManager* manager = GetIsolateManager();
+  DCHECK(manager);
+  DCHECK_EQ(isolate_, manager->isolate());
+
+  task_runner_ = manager->task_runner();
+  context_state_ = manager->GetContextState(context);
+}
+
+// CefV8Context
+
+// static
+CefRefPtr<CefV8Context> CefV8Context::GetCurrentContext() {
+  CefRefPtr<CefV8Context> context;
+  CEF_V8_REQUIRE_ISOLATE_RETURN(context);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  if (isolate->InContext()) {
+    v8::HandleScope handle_scope(isolate);
+    context = new CefV8ContextImpl(isolate, isolate->GetCurrentContext());
+  }
+  return context;
+}
+
+// static
+CefRefPtr<CefV8Context> CefV8Context::GetEnteredContext() {
+  CefRefPtr<CefV8Context> context;
+  CEF_V8_REQUIRE_ISOLATE_RETURN(context);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  if (isolate->InContext()) {
+    v8::HandleScope handle_scope(isolate);
+    context =
+        new CefV8ContextImpl(isolate, isolate->GetEnteredOrMicrotaskContext());
+  }
+  return context;
+}
+
+// static
+bool CefV8Context::InContext() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  return isolate->InContext();
+}
+
+// CefV8ContextImpl
+
+CefV8ContextImpl::CefV8ContextImpl(v8::Isolate* isolate,
+                                   v8::Local<v8::Context> context)
+    : handle_(new Handle(isolate, context, context)), enter_count_(0) {}
+
+CefV8ContextImpl::~CefV8ContextImpl() {
+  DLOG_ASSERT(0 == enter_count_);
+}
+
+CefRefPtr<CefTaskRunner> CefV8ContextImpl::GetTaskRunner() {
+  return new CefTaskRunnerImpl(handle_->task_runner());
+}
+
+bool CefV8ContextImpl::IsValid() {
+  return CEF_V8_IS_VALID();
+}
+
+CefRefPtr<CefBrowser> CefV8ContextImpl::GetBrowser() {
+  CefRefPtr<CefBrowser> browser;
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(browser);
+
+  blink::WebLocalFrame* webframe = GetWebFrame();
+  if (webframe)
+    browser = CefBrowserImpl::GetBrowserForMainFrame(webframe->Top());
+
+  return browser;
+}
+
+CefRefPtr<CefFrame> CefV8ContextImpl::GetFrame() {
+  CefRefPtr<CefFrame> frame;
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(frame);
+
+  blink::WebLocalFrame* webframe = GetWebFrame();
+  if (webframe) {
+    CefRefPtr<CefBrowserImpl> browser =
+        CefBrowserImpl::GetBrowserForMainFrame(webframe->Top());
+    if (browser) {
+      frame = browser->GetFrame(render_frame_util::GetIdentifier(webframe));
+    }
+  }
+
+  return frame;
+}
+
+CefRefPtr<CefV8Value> CefV8ContextImpl::GetGlobal() {
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(nullptr);
+
+  if (blink_glue::IsScriptForbidden())
+    return nullptr;
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context = GetV8Context();
+  v8::Context::Scope context_scope(context);
+  return new CefV8ValueImpl(isolate, context, context->Global());
+}
+
+bool CefV8ContextImpl::Enter() {
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false);
+
+  if (blink_glue::IsScriptForbidden())
+    return false;
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  if (!microtasks_scope_) {
+    // Increment the MicrotasksScope recursion level.
+    microtasks_scope_.reset(
+        new v8::MicrotasksScope(isolate, v8::MicrotasksScope::kRunMicrotasks));
+  }
+
+  ++enter_count_;
+  handle_->GetNewV8Handle()->Enter();
+
+  return true;
+}
+
+bool CefV8ContextImpl::Exit() {
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false);
+
+  if (blink_glue::IsScriptForbidden())
+    return false;
+
+  if (enter_count_ <= 0) {
+    LOG(ERROR) << "Call to CefV8Context::Exit() without matching call to "
+                  "CefV8Context::Enter()";
+    return false;
+  }
+
+  v8::HandleScope handle_scope(handle_->isolate());
+
+  handle_->GetNewV8Handle()->Exit();
+
+  if (--enter_count_ == 0) {
+    // Decrement the MicrotasksScope recursion level.
+    microtasks_scope_.reset(nullptr);
+  }
+
+  return true;
+}
+
+bool CefV8ContextImpl::IsSame(CefRefPtr<CefV8Context> that) {
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false);
+
+  CefV8ContextImpl* impl = static_cast<CefV8ContextImpl*>(that.get());
+  if (!impl || !impl->IsValid())
+    return false;
+
+  return (handle_->GetPersistentV8Handle() ==
+          impl->handle_->GetPersistentV8Handle());
+}
+
+bool CefV8ContextImpl::Eval(const CefString& code,
+                            const CefString& script_url,
+                            int start_line,
+                            CefRefPtr<CefV8Value>& retval,
+                            CefRefPtr<CefV8Exception>& exception) {
+  retval = nullptr;
+  exception = nullptr;
+
+  CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false);
+
+  if (blink_glue::IsScriptForbidden())
+    return false;
+
+  if (code.empty()) {
+    NOTREACHED() << "invalid input parameter";
+    return false;
+  }
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context = GetV8Context();
+  v8::Context::Scope context_scope(context);
+
+  const blink::WebString& source =
+      blink::WebString::FromUTF16(code.ToString16());
+  const blink::WebString& source_url =
+      blink::WebString::FromUTF16(script_url.ToString16());
+
+  v8::TryCatch try_catch(isolate);
+  try_catch.SetVerbose(true);
+
+  v8::MaybeLocal<v8::Value> func_rv = blink_glue::ExecuteV8ScriptAndReturnValue(
+      source, source_url, start_line, context, isolate, try_catch,
+      blink::SanitizeScriptErrors::kSanitize);
+
+  if (try_catch.HasCaught()) {
+    exception = new CefV8ExceptionImpl(context, try_catch.Message());
+    return false;
+  } else if (!func_rv.IsEmpty()) {
+    retval = new CefV8ValueImpl(isolate, context, func_rv.ToLocalChecked());
+    return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+v8::Local<v8::Context> CefV8ContextImpl::GetV8Context() {
+  return handle_->GetNewV8Handle();
+}
+
+blink::WebLocalFrame* CefV8ContextImpl::GetWebFrame() {
+  CEF_REQUIRE_RT();
+
+  if (blink_glue::IsScriptForbidden())
+    return nullptr;
+
+  v8::HandleScope handle_scope(handle_->isolate());
+  v8::Local<v8::Context> context = GetV8Context();
+  v8::Context::Scope context_scope(context);
+  return blink::WebLocalFrame::FrameForContext(context);
+}
+
+// CefV8ValueImpl::Handle
+
+CefV8ValueImpl::Handle::Handle(v8::Isolate* isolate,
+                               v8::Local<v8::Context> context,
+                               handleType v,
+                               CefTrackNode* tracker)
+    : CefV8HandleBase(isolate, context),
+      handle_(isolate, v),
+      tracker_(tracker),
+      should_persist_(false),
+      is_set_weak_(false) {}
+
+CefV8ValueImpl::Handle::~Handle() {
+  DCHECK(BelongsToCurrentThread());
+
+  if (tracker_) {
+    if (is_set_weak_) {
+      if (context_state_.get()) {
+        // If the associated context is still valid then delete |tracker_|.
+        // Otherwise, |tracker_| will already have been deleted.
+        if (context_state_->IsValid())
+          context_state_->DeleteTrackObject(tracker_);
+      } else {
+        GetIsolateManager()->DeleteGlobalTrackObject(tracker_);
+      }
+    } else {
+      delete tracker_;
+    }
+  }
+
+  if (is_set_weak_) {
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        -static_cast<int>(sizeof(Handle)));
+  } else {
+    // SetWeak was not called so reset now.
+    handle_.Reset();
+  }
+}
+
+CefV8ValueImpl::Handle::handleType CefV8ValueImpl::Handle::GetNewV8Handle(
+    bool should_persist) {
+  DCHECK(IsValid());
+  if (should_persist && !should_persist_)
+    should_persist_ = true;
+  return handleType::New(isolate(), handle_);
+}
+
+CefV8ValueImpl::Handle::persistentType&
+CefV8ValueImpl::Handle::GetPersistentV8Handle() {
+  return handle_;
+}
+
+void CefV8ValueImpl::Handle::SetWeakIfNecessary() {
+  if (!BelongsToCurrentThread()) {
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&CefV8ValueImpl::Handle::SetWeakIfNecessary, this));
+    return;
+  }
+
+  // Persist the handle (call SetWeak) if:
+  // A. The handle has been passed into a V8 function or used as a return value
+  //    from a V8 callback, and
+  // B. The associated context, if any, is still valid.
+  if (should_persist_ && (!context_state_.get() || context_state_->IsValid())) {
+    is_set_weak_ = true;
+
+    if (tracker_) {
+      if (context_state_.get()) {
+        // |tracker_| will be deleted when:
+        // A. The associated context is released, or
+        // B. SecondWeakCallback is called for the weak handle.
+        DCHECK(context_state_->IsValid());
+        context_state_->AddTrackObject(tracker_);
+      } else {
+        // |tracker_| will be deleted when:
+        // A. The process shuts down, or
+        // B. SecondWeakCallback is called for the weak handle.
+        GetIsolateManager()->AddGlobalTrackObject(tracker_);
+      }
+    }
+
+    isolate_->AdjustAmountOfExternalAllocatedMemory(
+        static_cast<int>(sizeof(Handle)));
+
+    // The added reference will be released in SecondWeakCallback.
+    AddRef();
+    handle_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter);
+  }
+}
+
+// static
+void CefV8ValueImpl::Handle::FirstWeakCallback(
+    const v8::WeakCallbackInfo<Handle>& data) {
+  Handle* wrapper = data.GetParameter();
+  wrapper->handle_.Reset();
+  data.SetSecondPassCallback(SecondWeakCallback);
+}
+
+// static
+void CefV8ValueImpl::Handle::SecondWeakCallback(
+    const v8::WeakCallbackInfo<Handle>& data) {
+  Handle* wrapper = data.GetParameter();
+  wrapper->Release();
+}
+
+// CefV8Value
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateUndefined() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitUndefined();
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateNull() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitNull();
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateBool(bool value) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitBool(value);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateInt(int32 value) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitInt(value);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateUInt(uint32 value) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitUInt(value);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateDouble(double value) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitDouble(value);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateDate(const CefTime& value) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitDate(value);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateString(const CefString& value) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  CefString str(value);
+  impl->InitString(str);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateObject(
+    CefRefPtr<CefV8Accessor> accessor,
+    CefRefPtr<CefV8Interceptor> interceptor) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  // Create the new V8 object. If an interceptor is passed, create object from
+  // template and set property handlers.
+  v8::Local<v8::Object> obj;
+  if (interceptor.get()) {
+    v8::Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate);
+    tmpl->SetHandler(v8::NamedPropertyHandlerConfiguration(
+        InterceptorGetterCallbackImpl<v8::Local<v8::Name>>,
+        InterceptorSetterCallbackImpl<v8::Local<v8::Name>>, nullptr, nullptr,
+        nullptr, v8::Local<v8::Value>(),
+        v8::PropertyHandlerFlags::kOnlyInterceptStrings));
+
+    tmpl->SetIndexedPropertyHandler(InterceptorGetterCallbackImpl<uint32_t>,
+                                    InterceptorSetterCallbackImpl<uint32_t>);
+
+    v8::MaybeLocal<v8::Object> maybe_object = tmpl->NewInstance(context);
+    if (!maybe_object.ToLocal<v8::Object>(&obj)) {
+      NOTREACHED() << "Failed to create V8 Object with interceptor";
+      return nullptr;
+    }
+  } else {
+    obj = v8::Object::New(isolate);
+  }
+
+  // Create a tracker object that will cause the user data and/or accessor
+  // and/or interceptor reference to be released when the V8 object is
+  // destroyed.
+  V8TrackObject* tracker = new V8TrackObject(isolate);
+  tracker->SetAccessor(accessor);
+  tracker->SetInterceptor(interceptor);
+
+  // Attach the tracker object.
+  tracker->AttachTo(context, obj);
+
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitObject(obj, tracker);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateArray(int length) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  // Create a tracker object that will cause the user data reference to be
+  // released when the V8 object is destroyed.
+  V8TrackObject* tracker = new V8TrackObject(isolate);
+
+  // Create the new V8 array.
+  v8::Local<v8::Array> arr = v8::Array::New(isolate, length);
+
+  // Attach the tracker object.
+  tracker->AttachTo(context, arr);
+
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitObject(arr, tracker);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer(
+    void* buffer,
+    size_t length,
+    CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  // Create a tracker object that will cause the user data reference to be
+  // released when the V8 object is destroyed.
+  V8TrackArrayBuffer* tracker =
+      new V8TrackArrayBuffer(isolate, buffer, release_callback);
+  v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, buffer, length);
+
+  // Attach the tracker object.
+  tracker->AttachTo(context, ab);
+
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitObject(ab, tracker);
+  return impl.get();
+}
+
+// static
+CefRefPtr<CefV8Value> CefV8Value::CreateFunction(
+    const CefString& name,
+    CefRefPtr<CefV8Handler> handler) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+
+  if (!handler.get()) {
+    NOTREACHED() << "invalid parameter";
+    return nullptr;
+  }
+
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  v8::Local<v8::External> function_data =
+      V8FunctionData::Create(isolate, name, handler);
+
+  // Create a new V8 function template.
+  v8::Local<v8::FunctionTemplate> tmpl =
+      v8::FunctionTemplate::New(isolate, FunctionCallbackImpl, function_data);
+
+  // Retrieve the function object and set the name.
+  v8::MaybeLocal<v8::Function> maybe_func = tmpl->GetFunction(context);
+  v8::Local<v8::Function> func;
+  if (!maybe_func.ToLocal(&func)) {
+    NOTREACHED() << "failed to create V8 function";
+    return nullptr;
+  }
+
+  func->SetName(GetV8String(isolate, name));
+
+  // Create a tracker object that will cause the user data and/or handler
+  // reference to be released when the V8 object is destroyed.
+  V8TrackObject* tracker = new V8TrackObject(isolate);
+  tracker->SetHandler(handler);
+
+  // Attach the tracker object.
+  tracker->AttachTo(context, func);
+
+  // Create the CefV8ValueImpl and provide a tracker object that will cause
+  // the handler reference to be released when the V8 object is destroyed.
+  CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
+  impl->InitObject(func, tracker);
+  return impl.get();
+}
+
+// CefV8ValueImpl
+
+CefV8ValueImpl::CefV8ValueImpl(v8::Isolate* isolate)
+    : isolate_(isolate), type_(TYPE_INVALID), rethrow_exceptions_(false) {
+  DCHECK(isolate_);
+}
+
+CefV8ValueImpl::CefV8ValueImpl(v8::Isolate* isolate,
+                               v8::Local<v8::Context> context,
+                               v8::Local<v8::Value> value)
+    : isolate_(isolate), type_(TYPE_INVALID), rethrow_exceptions_(false) {
+  DCHECK(isolate_);
+  InitFromV8Value(context, value);
+}
+
+CefV8ValueImpl::~CefV8ValueImpl() {
+  if (type_ == TYPE_STRING)
+    cef_string_clear(&string_value_);
+  if (handle_.get())
+    handle_->SetWeakIfNecessary();
+}
+
+void CefV8ValueImpl::InitFromV8Value(v8::Local<v8::Context> context,
+                                     v8::Local<v8::Value> value) {
+  if (value->IsUndefined()) {
+    InitUndefined();
+  } else if (value->IsNull()) {
+    InitNull();
+  } else if (value->IsTrue()) {
+    InitBool(true);
+  } else if (value->IsFalse()) {
+    InitBool(false);
+  } else if (value->IsBoolean()) {
+    InitBool(value->ToBoolean(context->GetIsolate())->Value());
+  } else if (value->IsInt32()) {
+    InitInt(value->ToInt32(context).ToLocalChecked()->Value());
+  } else if (value->IsUint32()) {
+    InitUInt(value->ToUint32(context).ToLocalChecked()->Value());
+  } else if (value->IsNumber()) {
+    InitDouble(value->ToNumber(context).ToLocalChecked()->Value());
+  } else if (value->IsDate()) {
+    // Convert from milliseconds to seconds.
+    InitDate(
+        CefTime(value->ToNumber(context).ToLocalChecked()->Value() / 1000));
+  } else if (value->IsString()) {
+    CefString rv;
+    GetCefString(context->GetIsolate(),
+                 value->ToString(context).ToLocalChecked(), rv);
+    InitString(rv);
+  } else if (value->IsObject()) {
+    InitObject(value, nullptr);
+  }
+}
+
+void CefV8ValueImpl::InitUndefined() {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_UNDEFINED;
+}
+
+void CefV8ValueImpl::InitNull() {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_NULL;
+}
+
+void CefV8ValueImpl::InitBool(bool value) {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_BOOL;
+  bool_value_ = value;
+}
+
+void CefV8ValueImpl::InitInt(int32 value) {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_INT;
+  int_value_ = value;
+}
+
+void CefV8ValueImpl::InitUInt(uint32 value) {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_UINT;
+  uint_value_ = value;
+}
+
+void CefV8ValueImpl::InitDouble(double value) {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_DOUBLE;
+  double_value_ = value;
+}
+
+void CefV8ValueImpl::InitDate(const CefTime& value) {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_DATE;
+  date_value_ = value;
+}
+
+void CefV8ValueImpl::InitString(CefString& value) {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_STRING;
+  // Take ownership of the underling string value.
+  const cef_string_t* str = value.GetStruct();
+  if (str) {
+    string_value_ = *str;
+    cef_string_t* writable_struct = value.GetWritableStruct();
+    writable_struct->str = nullptr;
+    writable_struct->length = 0;
+  } else {
+    string_value_.str = nullptr;
+    string_value_.length = 0;
+  }
+}
+
+void CefV8ValueImpl::InitObject(v8::Local<v8::Value> value,
+                                CefTrackNode* tracker) {
+  DCHECK_EQ(type_, TYPE_INVALID);
+  type_ = TYPE_OBJECT;
+  handle_ = new Handle(isolate_, v8::Local<v8::Context>(), value, tracker);
+}
+
+v8::Local<v8::Value> CefV8ValueImpl::GetV8Value(bool should_persist) {
+  switch (type_) {
+    case TYPE_UNDEFINED:
+      return v8::Undefined(isolate_);
+    case TYPE_NULL:
+      return v8::Null(isolate_);
+    case TYPE_BOOL:
+      return v8::Boolean::New(isolate_, bool_value_);
+    case TYPE_INT:
+      return v8::Int32::New(isolate_, int_value_);
+    case TYPE_UINT:
+      return v8::Uint32::New(isolate_, uint_value_);
+    case TYPE_DOUBLE:
+      return v8::Number::New(isolate_, double_value_);
+    case TYPE_DATE:
+      // Convert from seconds to milliseconds.
+      return v8::Date::New(isolate_->GetCurrentContext(),
+                           CefTime(date_value_).GetDoubleT() * 1000)
+          .ToLocalChecked();
+    case TYPE_STRING:
+      return GetV8String(isolate_, CefString(&string_value_));
+    case TYPE_OBJECT:
+      return handle_->GetNewV8Handle(should_persist);
+    default:
+      break;
+  }
+
+  NOTREACHED() << "Invalid type for CefV8ValueImpl";
+  return v8::Local<v8::Value>();
+}
+
+bool CefV8ValueImpl::IsValid() {
+  if (!CEF_V8_HAS_ISOLATE() || type_ == TYPE_INVALID ||
+      (type_ == TYPE_OBJECT &&
+       (!handle_->BelongsToCurrentThread() || !handle_->IsValid()))) {
+    return false;
+  }
+  return true;
+}
+
+bool CefV8ValueImpl::IsUndefined() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_UNDEFINED);
+}
+
+bool CefV8ValueImpl::IsNull() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_NULL);
+}
+
+bool CefV8ValueImpl::IsBool() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_BOOL);
+}
+
+bool CefV8ValueImpl::IsInt() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_INT || type_ == TYPE_UINT);
+}
+
+bool CefV8ValueImpl::IsUInt() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_INT || type_ == TYPE_UINT);
+}
+
+bool CefV8ValueImpl::IsDouble() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_INT || type_ == TYPE_UINT || type_ == TYPE_DOUBLE);
+}
+
+bool CefV8ValueImpl::IsDate() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_DATE);
+}
+
+bool CefV8ValueImpl::IsString() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_STRING);
+}
+
+bool CefV8ValueImpl::IsObject() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  return (type_ == TYPE_OBJECT);
+}
+
+bool CefV8ValueImpl::IsArray() {
+  CEF_V8_REQUIRE_MLT_RETURN(false);
+  if (type_ == TYPE_OBJECT) {
+    v8::HandleScope handle_scope(handle_->isolate());
+    return handle_->GetNewV8Handle(false)->IsArray();
+  } else {
+    return false;
+  }
+}
+
+bool CefV8ValueImpl::IsArrayBuffer() {
+  CEF_V8_REQUIRE_MLT_RETURN(false);
+  if (type_ == TYPE_OBJECT) {
+    v8::HandleScope handle_scope(handle_->isolate());
+    return handle_->GetNewV8Handle(false)->IsArrayBuffer();
+  } else {
+    return false;
+  }
+}
+
+bool CefV8ValueImpl::IsFunction() {
+  CEF_V8_REQUIRE_MLT_RETURN(false);
+  if (type_ == TYPE_OBJECT) {
+    v8::HandleScope handle_scope(handle_->isolate());
+    return handle_->GetNewV8Handle(false)->IsFunction();
+  } else {
+    return false;
+  }
+}
+
+bool CefV8ValueImpl::IsSame(CefRefPtr<CefV8Value> that) {
+  CEF_V8_REQUIRE_MLT_RETURN(false);
+
+  CefV8ValueImpl* thatValue = static_cast<CefV8ValueImpl*>(that.get());
+  if (!thatValue || !thatValue->IsValid() || type_ != thatValue->type_)
+    return false;
+
+  switch (type_) {
+    case TYPE_UNDEFINED:
+    case TYPE_NULL:
+      return true;
+    case TYPE_BOOL:
+      return (bool_value_ == thatValue->bool_value_);
+    case TYPE_INT:
+      return (int_value_ == thatValue->int_value_);
+    case TYPE_UINT:
+      return (uint_value_ == thatValue->uint_value_);
+    case TYPE_DOUBLE:
+      return (double_value_ == thatValue->double_value_);
+    case TYPE_DATE:
+      return (CefTime(date_value_).GetTimeT() ==
+              CefTime(thatValue->date_value_).GetTimeT());
+    case TYPE_STRING:
+      return (CefString(&string_value_) ==
+              CefString(&thatValue->string_value_));
+    case TYPE_OBJECT: {
+      return (handle_->GetPersistentV8Handle() ==
+              thatValue->handle_->GetPersistentV8Handle());
+    }
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool CefV8ValueImpl::GetBoolValue() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(false);
+  if (type_ == TYPE_BOOL)
+    return bool_value_;
+  return false;
+}
+
+int32 CefV8ValueImpl::GetIntValue() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(0);
+  if (type_ == TYPE_INT || type_ == TYPE_UINT)
+    return int_value_;
+  return 0;
+}
+
+uint32 CefV8ValueImpl::GetUIntValue() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(0);
+  if (type_ == TYPE_INT || type_ == TYPE_UINT)
+    return uint_value_;
+  return 0;
+}
+
+double CefV8ValueImpl::GetDoubleValue() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(0.);
+  if (type_ == TYPE_DOUBLE)
+    return double_value_;
+  else if (type_ == TYPE_INT)
+    return int_value_;
+  else if (type_ == TYPE_UINT)
+    return uint_value_;
+  return 0.;
+}
+
+CefTime CefV8ValueImpl::GetDateValue() {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(CefTime(0.));
+  if (type_ == TYPE_DATE)
+    return date_value_;
+  return CefTime(0.);
+}
+
+CefString CefV8ValueImpl::GetStringValue() {
+  CefString rv;
+  CEF_V8_REQUIRE_ISOLATE_RETURN(rv);
+  if (type_ == TYPE_STRING)
+    rv = CefString(&string_value_);
+  return rv;
+}
+
+bool CefV8ValueImpl::IsUserCreated() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  return (tracker != nullptr);
+}
+
+bool CefV8ValueImpl::HasException() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  return (last_exception_.get() != nullptr);
+}
+
+CefRefPtr<CefV8Exception> CefV8ValueImpl::GetException() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(nullptr);
+
+  return last_exception_;
+}
+
+bool CefV8ValueImpl::ClearException() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  last_exception_ = nullptr;
+  return true;
+}
+
+bool CefV8ValueImpl::WillRethrowExceptions() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  return rethrow_exceptions_;
+}
+
+bool CefV8ValueImpl::SetRethrowExceptions(bool rethrow) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  rethrow_exceptions_ = rethrow;
+  return true;
+}
+
+bool CefV8ValueImpl::HasValue(const CefString& key) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+  return obj->Has(context, GetV8String(isolate, key)).FromJust();
+}
+
+bool CefV8ValueImpl::HasValue(int index) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  if (index < 0) {
+    NOTREACHED() << "invalid input parameter";
+    return false;
+  }
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+  return obj->Has(context, index).FromJust();
+}
+
+bool CefV8ValueImpl::DeleteValue(const CefString& key) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  v8::TryCatch try_catch(isolate);
+  try_catch.SetVerbose(true);
+  v8::Maybe<bool> del = obj->Delete(context, GetV8String(isolate, key));
+  return (!HasCaught(context, try_catch) && del.FromJust());
+}
+
+bool CefV8ValueImpl::DeleteValue(int index) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  if (index < 0) {
+    NOTREACHED() << "invalid input parameter";
+    return false;
+  }
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  v8::TryCatch try_catch(isolate);
+  try_catch.SetVerbose(true);
+  v8::Maybe<bool> del = obj->Delete(context, index);
+  return (!HasCaught(context, try_catch) && del.FromJust());
+}
+
+CefRefPtr<CefV8Value> CefV8ValueImpl::GetValue(const CefString& key) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(nullptr);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  v8::TryCatch try_catch(isolate);
+  try_catch.SetVerbose(true);
+  v8::MaybeLocal<v8::Value> ret_value =
+      obj->Get(context, GetV8String(isolate, key));
+  if (!HasCaught(context, try_catch) && !ret_value.IsEmpty()) {
+    return new CefV8ValueImpl(isolate, context, ret_value.ToLocalChecked());
+  }
+  return nullptr;
+}
+
+CefRefPtr<CefV8Value> CefV8ValueImpl::GetValue(int index) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(nullptr);
+
+  if (index < 0) {
+    NOTREACHED() << "invalid input parameter";
+    return nullptr;
+  }
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  v8::TryCatch try_catch(isolate);
+  try_catch.SetVerbose(true);
+  v8::MaybeLocal<v8::Value> ret_value =
+      obj->Get(context, v8::Number::New(isolate, index));
+  if (!HasCaught(context, try_catch) && !ret_value.IsEmpty()) {
+    return new CefV8ValueImpl(isolate, context, ret_value.ToLocalChecked());
+  }
+  return nullptr;
+}
+
+bool CefV8ValueImpl::SetValue(const CefString& key,
+                              CefRefPtr<CefV8Value> value,
+                              PropertyAttribute attribute) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  CefV8ValueImpl* impl = static_cast<CefV8ValueImpl*>(value.get());
+  if (impl && impl->IsValid()) {
+    v8::Isolate* isolate = handle_->isolate();
+    v8::HandleScope handle_scope(isolate);
+
+    v8::Local<v8::Context> context = isolate->GetCurrentContext();
+    if (context.IsEmpty()) {
+      NOTREACHED() << "not currently in a V8 context";
+      return false;
+    }
+
+    v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+    v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+    v8::TryCatch try_catch(isolate);
+    try_catch.SetVerbose(true);
+    // TODO(cef): This usage may not exactly match the previous implementation.
+    // Set will trigger interceptors and/or accessors whereas DefineOwnProperty
+    // will not. It might be better to split this functionality into separate
+    // methods.
+    if (attribute == V8_PROPERTY_ATTRIBUTE_NONE) {
+      v8::Maybe<bool> set =
+          obj->Set(context, GetV8String(isolate, key), impl->GetV8Value(true));
+      return (!HasCaught(context, try_catch) && set.FromJust());
+    } else {
+      v8::Maybe<bool> set = obj->DefineOwnProperty(
+          context, GetV8String(isolate, key), impl->GetV8Value(true),
+          static_cast<v8::PropertyAttribute>(attribute));
+      return (!HasCaught(context, try_catch) && set.FromJust());
+    }
+  } else {
+    NOTREACHED() << "invalid input parameter";
+    return false;
+  }
+}
+
+bool CefV8ValueImpl::SetValue(int index, CefRefPtr<CefV8Value> value) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  if (index < 0) {
+    NOTREACHED() << "invalid input parameter";
+    return false;
+  }
+
+  CefV8ValueImpl* impl = static_cast<CefV8ValueImpl*>(value.get());
+  if (impl && impl->IsValid()) {
+    v8::Isolate* isolate = handle_->isolate();
+    v8::HandleScope handle_scope(isolate);
+
+    v8::Local<v8::Context> context = isolate->GetCurrentContext();
+    if (context.IsEmpty()) {
+      NOTREACHED() << "not currently in a V8 context";
+      return false;
+    }
+
+    v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+    v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+    v8::TryCatch try_catch(isolate);
+    try_catch.SetVerbose(true);
+    v8::Maybe<bool> set = obj->Set(context, index, impl->GetV8Value(true));
+    return (!HasCaught(context, try_catch) && set.FromJust());
+  } else {
+    NOTREACHED() << "invalid input parameter";
+    return false;
+  }
+}
+
+bool CefV8ValueImpl::SetValue(const CefString& key,
+                              AccessControl settings,
+                              PropertyAttribute attribute) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  CefRefPtr<CefV8Accessor> accessorPtr;
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    accessorPtr = tracker->GetAccessor();
+
+  // Verify that an accessor exists for this object.
+  if (!accessorPtr.get())
+    return false;
+
+  v8::AccessorNameGetterCallback getter = AccessorNameGetterCallbackImpl;
+  v8::AccessorNameSetterCallback setter =
+      (attribute & V8_PROPERTY_ATTRIBUTE_READONLY)
+          ? nullptr
+          : AccessorNameSetterCallbackImpl;
+
+  v8::TryCatch try_catch(isolate);
+  try_catch.SetVerbose(true);
+  v8::Maybe<bool> set =
+      obj->SetAccessor(context, GetV8String(isolate, key), getter, setter, obj,
+                       static_cast<v8::AccessControl>(settings),
+                       static_cast<v8::PropertyAttribute>(attribute));
+  return (!HasCaught(context, try_catch) && set.FromJust());
+}
+
+bool CefV8ValueImpl::GetKeys(std::vector<CefString>& keys) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+  v8::Local<v8::Array> arr_keys =
+      obj->GetPropertyNames(context).ToLocalChecked();
+
+  uint32_t len = arr_keys->Length();
+  for (uint32_t i = 0; i < len; ++i) {
+    v8::Local<v8::Value> value =
+        arr_keys->Get(context, v8::Integer::New(isolate, i)).ToLocalChecked();
+    CefString str;
+    GetCefString(isolate, value->ToString(context).ToLocalChecked(), str);
+    keys.push_back(str);
+  }
+  return true;
+}
+
+bool CefV8ValueImpl::SetUserData(CefRefPtr<CefBaseRefCounted> user_data) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(false);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker) {
+    tracker->SetUserData(user_data);
+    return true;
+  }
+
+  return false;
+}
+
+CefRefPtr<CefBaseRefCounted> CefV8ValueImpl::GetUserData() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(nullptr);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    return tracker->GetUserData();
+
+  return nullptr;
+}
+
+int CefV8ValueImpl::GetExternallyAllocatedMemory() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(0);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return 0;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    return tracker->GetExternallyAllocatedMemory();
+
+  return 0;
+}
+
+int CefV8ValueImpl::AdjustExternallyAllocatedMemory(int change_in_bytes) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(0);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return 0;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    return tracker->AdjustExternallyAllocatedMemory(change_in_bytes);
+
+  return 0;
+}
+
+int CefV8ValueImpl::GetArrayLength() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(0);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return 0;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  if (!value->IsArray()) {
+    NOTREACHED() << "V8 value is not an array";
+    return 0;
+  }
+
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+  v8::Local<v8::Array> arr = v8::Local<v8::Array>::Cast(obj);
+  return arr->Length();
+}
+
+CefRefPtr<CefV8ArrayBufferReleaseCallback>
+CefV8ValueImpl::GetArrayBufferReleaseCallback() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(nullptr);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  if (!value->IsArrayBuffer()) {
+    NOTREACHED() << "V8 value is not an array buffer";
+    return nullptr;
+  }
+
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+
+  V8TrackArrayBuffer* tracker = V8TrackArrayBuffer::Unwrap(context, obj);
+  if (tracker)
+    return tracker->GetReleaseCallback();
+
+  return nullptr;
+}
+
+bool CefV8ValueImpl::NeuterArrayBuffer() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(0);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return false;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  if (!value->IsArrayBuffer()) {
+    NOTREACHED() << "V8 value is not an array buffer";
+    return false;
+  }
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+  v8::Local<v8::ArrayBuffer> arr = v8::Local<v8::ArrayBuffer>::Cast(obj);
+  if (!arr->IsDetachable()) {
+    return false;
+  }
+  arr->Detach();
+  V8TrackArrayBuffer* tracker = V8TrackArrayBuffer::Unwrap(context, obj);
+  tracker->Detach();
+
+  return true;
+}
+
+CefString CefV8ValueImpl::GetFunctionName() {
+  CefString rv;
+  CEF_V8_REQUIRE_OBJECT_RETURN(rv);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return rv;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  if (!value->IsFunction()) {
+    NOTREACHED() << "V8 value is not a function";
+    return rv;
+  }
+
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+  v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(obj);
+  GetCefString(handle_->isolate(),
+               v8::Handle<v8::String>::Cast(func->GetName()), rv);
+  return rv;
+}
+
+CefRefPtr<CefV8Handler> CefV8ValueImpl::GetFunctionHandler() {
+  CEF_V8_REQUIRE_OBJECT_RETURN(nullptr);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  if (context.IsEmpty()) {
+    NOTREACHED() << "not currently in a V8 context";
+    return nullptr;
+  }
+
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  if (!value->IsFunction()) {
+    NOTREACHED() << "V8 value is not a function";
+    return nullptr;
+  }
+
+  v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
+  V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj);
+  if (tracker)
+    return tracker->GetHandler();
+
+  return nullptr;
+}
+
+CefRefPtr<CefV8Value> CefV8ValueImpl::ExecuteFunction(
+    CefRefPtr<CefV8Value> object,
+    const CefV8ValueList& arguments) {
+  // An empty context value defaults to the current context.
+  CefRefPtr<CefV8Context> context;
+  return ExecuteFunctionWithContext(context, object, arguments);
+}
+
+CefRefPtr<CefV8Value> CefV8ValueImpl::ExecuteFunctionWithContext(
+    CefRefPtr<CefV8Context> context,
+    CefRefPtr<CefV8Value> object,
+    const CefV8ValueList& arguments) {
+  CEF_V8_REQUIRE_OBJECT_RETURN(nullptr);
+
+  v8::Isolate* isolate = handle_->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
+  if (!value->IsFunction()) {
+    NOTREACHED() << "V8 value is not a function";
+    return nullptr;
+  }
+
+  if (context.get() && !context->IsValid()) {
+    NOTREACHED() << "invalid V8 context parameter";
+    return nullptr;
+  }
+  if (object.get() && (!object->IsValid() || !object->IsObject())) {
+    NOTREACHED() << "invalid V8 object parameter";
+    return nullptr;
+  }
+
+  int argc = arguments.size();
+  if (argc > 0) {
+    for (int i = 0; i < argc; ++i) {
+      if (!arguments[i].get() || !arguments[i]->IsValid()) {
+        NOTREACHED() << "invalid V8 arguments parameter";
+        return nullptr;
+      }
+    }
+  }
+
+  v8::Local<v8::Context> context_local;
+  if (context.get()) {
+    CefV8ContextImpl* context_impl =
+        static_cast<CefV8ContextImpl*>(context.get());
+    context_local = context_impl->GetV8Context();
+  } else {
+    context_local = isolate->GetCurrentContext();
+  }
+
+  v8::Context::Scope context_scope(context_local);
+
+  v8::Local<v8::Object> obj = value->ToObject(context_local).ToLocalChecked();
+  v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(obj);
+  v8::Local<v8::Object> recv;
+
+  // Default to the global object if no object was provided.
+  if (object.get()) {
+    CefV8ValueImpl* recv_impl = static_cast<CefV8ValueImpl*>(object.get());
+    recv = v8::Local<v8::Object>::Cast(recv_impl->GetV8Value(true));
+  } else {
+    recv = context_local->Global();
+  }
+
+  v8::Local<v8::Value>* argv = nullptr;
+  if (argc > 0) {
+    argv = new v8::Local<v8::Value>[argc];
+    for (int i = 0; i < argc; ++i) {
+      argv[i] =
+          static_cast<CefV8ValueImpl*>(arguments[i].get())->GetV8Value(true);
+    }
+  }
+
+  CefRefPtr<CefV8Value> retval;
+
+  {
+    v8::TryCatch try_catch(isolate);
+    try_catch.SetVerbose(true);
+
+    v8::MaybeLocal<v8::Value> func_rv = blink_glue::CallV8Function(
+        context_local, func, recv, argc, argv, handle_->isolate());
+
+    if (!HasCaught(context_local, try_catch) && !func_rv.IsEmpty()) {
+      retval =
+          new CefV8ValueImpl(isolate, context_local, func_rv.ToLocalChecked());
+    }
+  }
+
+  if (argv)
+    delete[] argv;
+
+  return retval;
+}
+
+bool CefV8ValueImpl::HasCaught(v8::Local<v8::Context> context,
+                               v8::TryCatch& try_catch) {
+  if (try_catch.HasCaught()) {
+    last_exception_ = new CefV8ExceptionImpl(context, try_catch.Message());
+    if (rethrow_exceptions_)
+      try_catch.ReThrow();
+    return true;
+  } else {
+    if (last_exception_.get())
+      last_exception_ = nullptr;
+    return false;
+  }
+}
+
+// CefV8StackTrace
+
+// static
+CefRefPtr<CefV8StackTrace> CefV8StackTrace::GetCurrent(int frame_limit) {
+  CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
+
+  v8::Isolate* isolate = GetIsolateManager()->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
+      isolate, frame_limit, v8::StackTrace::kDetailed);
+  if (stackTrace.IsEmpty())
+    return nullptr;
+  return new CefV8StackTraceImpl(isolate, stackTrace);
+}
+
+// CefV8StackTraceImpl
+
+CefV8StackTraceImpl::CefV8StackTraceImpl(v8::Isolate* isolate,
+                                         v8::Local<v8::StackTrace> handle) {
+  if (!handle.IsEmpty()) {
+    int frame_count = handle->GetFrameCount();
+    if (frame_count > 0) {
+      frames_.reserve(frame_count);
+      for (int i = 0; i < frame_count; ++i)
+        frames_.push_back(
+            new CefV8StackFrameImpl(isolate, handle->GetFrame(isolate, i)));
+    }
+  }
+}
+
+CefV8StackTraceImpl::~CefV8StackTraceImpl() {}
+
+bool CefV8StackTraceImpl::IsValid() {
+  return true;
+}
+
+int CefV8StackTraceImpl::GetFrameCount() {
+  return frames_.size();
+}
+
+CefRefPtr<CefV8StackFrame> CefV8StackTraceImpl::GetFrame(int index) {
+  if (index < 0 || index >= static_cast<int>(frames_.size()))
+    return nullptr;
+  return frames_[index];
+}
+
+// CefV8StackFrameImpl
+
+CefV8StackFrameImpl::CefV8StackFrameImpl(v8::Isolate* isolate,
+                                         v8::Local<v8::StackFrame> handle)
+    : line_number_(0), column_(0), is_eval_(false), is_constructor_(false) {
+  if (handle.IsEmpty())
+    return;
+  GetCefString(isolate, handle->GetScriptName(), script_name_);
+  GetCefString(isolate, handle->GetScriptNameOrSourceURL(),
+               script_name_or_source_url_);
+  GetCefString(isolate, handle->GetFunctionName(), function_name_);
+  line_number_ = handle->GetLineNumber();
+  column_ = handle->GetColumn();
+  is_eval_ = handle->IsEval();
+  is_constructor_ = handle->IsConstructor();
+}
+
+CefV8StackFrameImpl::~CefV8StackFrameImpl() {}
+
+bool CefV8StackFrameImpl::IsValid() {
+  return true;
+}
+
+CefString CefV8StackFrameImpl::GetScriptName() {
+  return script_name_;
+}
+
+CefString CefV8StackFrameImpl::GetScriptNameOrSourceURL() {
+  return script_name_or_source_url_;
+}
+
+CefString CefV8StackFrameImpl::GetFunctionName() {
+  return function_name_;
+}
+
+int CefV8StackFrameImpl::GetLineNumber() {
+  return line_number_;
+}
+
+int CefV8StackFrameImpl::GetColumn() {
+  return column_;
+}
+
+bool CefV8StackFrameImpl::IsEval() {
+  return is_eval_;
+}
+
+bool CefV8StackFrameImpl::IsConstructor() {
+  return is_constructor_;
+}
+
+// Enable deprecation warnings on Windows. See http://crbug.com/585142.
+#if defined(OS_WIN)
+#if defined(__clang__)
+#pragma GCC diagnostic pop
+#else
+#pragma warning(pop)
+#endif
+#endif
diff --git a/src/libcef/renderer/v8_impl.h b/src/libcef/renderer/v8_impl.h
new file mode 100644
index 0000000..bc37af3
--- /dev/null
+++ b/src/libcef/renderer/v8_impl.h
@@ -0,0 +1,397 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_RENDERER_V8_IMPL_H_
+#define CEF_LIBCEF_RENDERER_V8_IMPL_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_v8.h"
+#include "libcef/common/tracker.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "v8/include/v8.h"
+
+class CefTrackNode;
+class GURL;
+
+namespace blink {
+class WebLocalFrame;
+}
+
+// Call after a V8 Isolate has been created and entered for the first time.
+void CefV8IsolateCreated();
+
+// Call before a V8 Isolate is exited and destroyed.
+void CefV8IsolateDestroyed();
+
+// Call to detach all handles associated with the specified context.
+void CefV8ReleaseContext(v8::Local<v8::Context> context);
+
+// Set the stack size for uncaught exceptions.
+void CefV8SetUncaughtExceptionStackSize(int stack_size);
+
+// Set attributes associated with a WebWorker thread.
+void CefV8SetWorkerAttributes(int worker_id, const GURL& worker_url);
+
+// Used to detach handles when the associated context is released.
+class CefV8ContextState : public base::RefCounted<CefV8ContextState> {
+ public:
+  CefV8ContextState() : valid_(true) {}
+
+  bool IsValid() { return valid_; }
+  void Detach() {
+    DCHECK(valid_);
+    valid_ = false;
+    track_manager_.DeleteAll();
+  }
+
+  void AddTrackObject(CefTrackNode* object) {
+    DCHECK(valid_);
+    track_manager_.Add(object);
+  }
+
+  void DeleteTrackObject(CefTrackNode* object) {
+    DCHECK(valid_);
+    track_manager_.Delete(object);
+  }
+
+ private:
+  friend class base::RefCounted<CefV8ContextState>;
+
+  ~CefV8ContextState() {}
+
+  bool valid_;
+  CefTrackManager track_manager_;
+};
+
+// Use this template in conjuction with RefCountedThreadSafe to ensure that a
+// V8 object is deleted on the correct thread.
+struct CefV8DeleteOnMessageLoopThread {
+  template <typename T>
+  static void Destruct(const T* x) {
+    if (x->task_runner()->RunsTasksInCurrentSequence()) {
+      delete x;
+    } else {
+      if (!x->task_runner()->DeleteSoon(FROM_HERE, x)) {
+#if defined(UNIT_TEST)
+        // Only logged under unit testing because leaks at shutdown
+        // are acceptable under normal circumstances.
+        LOG(ERROR) << "DeleteSoon failed on thread " << thread;
+#endif  // UNIT_TEST
+      }
+    }
+  }
+};
+
+// Base class for V8 Handle types.
+class CefV8HandleBase
+    : public base::RefCountedThreadSafe<CefV8HandleBase,
+                                        CefV8DeleteOnMessageLoopThread> {
+ public:
+  // Returns true if there is no underlying context or if the underlying context
+  // is valid.
+  bool IsValid() const {
+    return (!context_state_.get() || context_state_->IsValid());
+  }
+
+  bool BelongsToCurrentThread() const;
+
+  v8::Isolate* isolate() const { return isolate_; }
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
+    return task_runner_;
+  }
+
+ protected:
+  friend class base::DeleteHelper<CefV8HandleBase>;
+  friend class base::RefCountedThreadSafe<CefV8HandleBase,
+                                          CefV8DeleteOnMessageLoopThread>;
+  friend struct CefV8DeleteOnMessageLoopThread;
+
+  // |context| is the context that owns this handle. If empty the current
+  // context will be used.
+  CefV8HandleBase(v8::Isolate* isolate, v8::Local<v8::Context> context);
+  virtual ~CefV8HandleBase();
+
+ protected:
+  v8::Isolate* isolate_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<CefV8ContextState> context_state_;
+};
+
+// Template for V8 Handle types. This class is used to ensure that V8 objects
+// are only released on the render thread.
+template <typename v8class>
+class CefV8Handle : public CefV8HandleBase {
+ public:
+  typedef v8::Local<v8class> handleType;
+  typedef v8::Persistent<v8class> persistentType;
+
+  CefV8Handle(v8::Isolate* isolate,
+              v8::Local<v8::Context> context,
+              handleType v)
+      : CefV8HandleBase(isolate, context), handle_(isolate, v) {}
+
+  handleType GetNewV8Handle() {
+    DCHECK(IsValid());
+    return handleType::New(isolate(), handle_);
+  }
+
+  persistentType& GetPersistentV8Handle() { return handle_; }
+
+ protected:
+  ~CefV8Handle() override { handle_.Reset(); }
+
+  persistentType handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefV8Handle);
+};
+
+// Specialization for v8::Value with empty implementation to avoid incorrect
+// usage.
+template <>
+class CefV8Handle<v8::Value> {};
+
+class CefV8ContextImpl : public CefV8Context {
+ public:
+  CefV8ContextImpl(v8::Isolate* isolate, v8::Local<v8::Context> context);
+  ~CefV8ContextImpl() override;
+
+  CefRefPtr<CefTaskRunner> GetTaskRunner() override;
+  bool IsValid() override;
+  CefRefPtr<CefBrowser> GetBrowser() override;
+  CefRefPtr<CefFrame> GetFrame() override;
+  CefRefPtr<CefV8Value> GetGlobal() override;
+  bool Enter() override;
+  bool Exit() override;
+  bool IsSame(CefRefPtr<CefV8Context> that) override;
+  bool Eval(const CefString& code,
+            const CefString& script_url,
+            int start_line,
+            CefRefPtr<CefV8Value>& retval,
+            CefRefPtr<CefV8Exception>& exception) override;
+
+  v8::Local<v8::Context> GetV8Context();
+  blink::WebLocalFrame* GetWebFrame();
+
+ private:
+  typedef CefV8Handle<v8::Context> Handle;
+  scoped_refptr<Handle> handle_;
+
+  int enter_count_;
+  std::unique_ptr<v8::MicrotasksScope> microtasks_scope_;
+
+  IMPLEMENT_REFCOUNTING(CefV8ContextImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefV8ContextImpl);
+};
+
+class CefV8ValueImpl : public CefV8Value {
+ public:
+  explicit CefV8ValueImpl(v8::Isolate* isolate);
+  CefV8ValueImpl(v8::Isolate* isolate,
+                 v8::Local<v8::Context> context,
+                 v8::Local<v8::Value> value);
+  ~CefV8ValueImpl() override;
+
+  // Used for initializing the CefV8ValueImpl. Should be called a single time
+  // after the CefV8ValueImpl is created.
+  void InitFromV8Value(v8::Local<v8::Context> context,
+                       v8::Local<v8::Value> value);
+  void InitUndefined();
+  void InitNull();
+  void InitBool(bool value);
+  void InitInt(int32 value);
+  void InitUInt(uint32 value);
+  void InitDouble(double value);
+  void InitDate(const CefTime& value);
+  void InitString(CefString& value);
+  void InitObject(v8::Local<v8::Value> value, CefTrackNode* tracker);
+
+  // Creates a new V8 value for the underlying value or returns the existing
+  // object handle.
+  v8::Local<v8::Value> GetV8Value(bool should_persist);
+
+  bool IsValid() override;
+  bool IsUndefined() override;
+  bool IsNull() override;
+  bool IsBool() override;
+  bool IsInt() override;
+  bool IsUInt() override;
+  bool IsDouble() override;
+  bool IsDate() override;
+  bool IsString() override;
+  bool IsObject() override;
+  bool IsArray() override;
+  bool IsArrayBuffer() override;
+  bool IsFunction() override;
+  bool IsSame(CefRefPtr<CefV8Value> value) override;
+  bool GetBoolValue() override;
+  int32 GetIntValue() override;
+  uint32 GetUIntValue() override;
+  double GetDoubleValue() override;
+  CefTime GetDateValue() override;
+  CefString GetStringValue() override;
+  bool IsUserCreated() override;
+  bool HasException() override;
+  CefRefPtr<CefV8Exception> GetException() override;
+  bool ClearException() override;
+  bool WillRethrowExceptions() override;
+  bool SetRethrowExceptions(bool rethrow) override;
+  bool HasValue(const CefString& key) override;
+  bool HasValue(int index) override;
+  bool DeleteValue(const CefString& key) override;
+  bool DeleteValue(int index) override;
+  CefRefPtr<CefV8Value> GetValue(const CefString& key) override;
+  CefRefPtr<CefV8Value> GetValue(int index) override;
+  bool SetValue(const CefString& key,
+                CefRefPtr<CefV8Value> value,
+                PropertyAttribute attribute) override;
+  bool SetValue(int index, CefRefPtr<CefV8Value> value) override;
+  bool SetValue(const CefString& key,
+                AccessControl settings,
+                PropertyAttribute attribute) override;
+  bool GetKeys(std::vector<CefString>& keys) override;
+  bool SetUserData(CefRefPtr<CefBaseRefCounted> user_data) override;
+  CefRefPtr<CefBaseRefCounted> GetUserData() override;
+  int GetExternallyAllocatedMemory() override;
+  int AdjustExternallyAllocatedMemory(int change_in_bytes) override;
+  int GetArrayLength() override;
+  CefRefPtr<CefV8ArrayBufferReleaseCallback> GetArrayBufferReleaseCallback()
+      override;
+  bool NeuterArrayBuffer() override;
+  CefString GetFunctionName() override;
+  CefRefPtr<CefV8Handler> GetFunctionHandler() override;
+  CefRefPtr<CefV8Value> ExecuteFunction(
+      CefRefPtr<CefV8Value> object,
+      const CefV8ValueList& arguments) override;
+  CefRefPtr<CefV8Value> ExecuteFunctionWithContext(
+      CefRefPtr<CefV8Context> context,
+      CefRefPtr<CefV8Value> object,
+      const CefV8ValueList& arguments) override;
+
+ private:
+  // Test for and record any exception.
+  bool HasCaught(v8::Local<v8::Context> context, v8::TryCatch& try_catch);
+
+  class Handle : public CefV8HandleBase {
+   public:
+    typedef v8::Local<v8::Value> handleType;
+    typedef v8::Persistent<v8::Value> persistentType;
+
+    Handle(v8::Isolate* isolate,
+           v8::Local<v8::Context> context,
+           handleType v,
+           CefTrackNode* tracker);
+
+    handleType GetNewV8Handle(bool should_persist);
+
+    persistentType& GetPersistentV8Handle();
+
+    void SetWeakIfNecessary();
+
+   private:
+    ~Handle() override;
+
+   private:
+    // Callbacks for weak persistent reference destruction.
+    static void FirstWeakCallback(const v8::WeakCallbackInfo<Handle>& data);
+    static void SecondWeakCallback(const v8::WeakCallbackInfo<Handle>& data);
+
+    persistentType handle_;
+
+    // For Object and Function types, we need to hold on to a reference to their
+    // internal data or function handler objects that are reference counted.
+    CefTrackNode* tracker_;
+
+    // True if the handle needs to persist due to it being passed into V8.
+    bool should_persist_;
+
+    // True if the handle has been set as weak.
+    bool is_set_weak_;
+
+    DISALLOW_COPY_AND_ASSIGN(Handle);
+  };
+
+  v8::Isolate* isolate_;
+
+  enum {
+    TYPE_INVALID = 0,
+    TYPE_UNDEFINED,
+    TYPE_NULL,
+    TYPE_BOOL,
+    TYPE_INT,
+    TYPE_UINT,
+    TYPE_DOUBLE,
+    TYPE_DATE,
+    TYPE_STRING,
+    TYPE_OBJECT,
+  } type_;
+
+  union {
+    bool bool_value_;
+    int32 int_value_;
+    uint32 uint_value_;
+    double double_value_;
+    cef_time_t date_value_;
+    cef_string_t string_value_;
+  };
+
+  // Used with Object, Function and Array types.
+  scoped_refptr<Handle> handle_;
+
+  CefRefPtr<CefV8Exception> last_exception_;
+  bool rethrow_exceptions_;
+
+  IMPLEMENT_REFCOUNTING(CefV8ValueImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefV8ValueImpl);
+};
+
+class CefV8StackTraceImpl : public CefV8StackTrace {
+ public:
+  CefV8StackTraceImpl(v8::Isolate* isolate, v8::Local<v8::StackTrace> handle);
+  ~CefV8StackTraceImpl() override;
+
+  bool IsValid() override;
+  int GetFrameCount() override;
+  CefRefPtr<CefV8StackFrame> GetFrame(int index) override;
+
+ private:
+  std::vector<CefRefPtr<CefV8StackFrame>> frames_;
+
+  IMPLEMENT_REFCOUNTING(CefV8StackTraceImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefV8StackTraceImpl);
+};
+
+class CefV8StackFrameImpl : public CefV8StackFrame {
+ public:
+  CefV8StackFrameImpl(v8::Isolate* isolate, v8::Local<v8::StackFrame> handle);
+  ~CefV8StackFrameImpl() override;
+
+  bool IsValid() override;
+  CefString GetScriptName() override;
+  CefString GetScriptNameOrSourceURL() override;
+  CefString GetFunctionName() override;
+  int GetLineNumber() override;
+  int GetColumn() override;
+  bool IsEval() override;
+  bool IsConstructor() override;
+
+ private:
+  CefString script_name_;
+  CefString script_name_or_source_url_;
+  CefString function_name_;
+  int line_number_;
+  int column_;
+  bool is_eval_;
+  bool is_constructor_;
+
+  IMPLEMENT_REFCOUNTING(CefV8StackFrameImpl);
+  DISALLOW_COPY_AND_ASSIGN(CefV8StackFrameImpl);
+};
+
+#endif  // CEF_LIBCEF_RENDERER_V8_IMPL_H_
diff --git a/src/libcef/resources/about_version.html b/src/libcef/resources/about_version.html
new file mode 100644
index 0000000..8739575
--- /dev/null
+++ b/src/libcef/resources/about_version.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+
+<!--
+about:version template page
+-->
+
+<html id="t" i18n-values="dir:textdirection;">
+  <head>
+    <title>About Version</title>
+
+    <style>/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+body {
+  background-color: white;
+  color: black;
+  font-family: Helvetica,Arial,sans-serif;
+  margin: 0;
+}
+
+#outer {
+  margin-left: auto;
+  margin-right: auto;
+  margin-top: 10px;
+  width: 820px;
+}
+
+#inner {
+  padding-top: 10px;
+  width: 550px;
+}
+
+.label {
+  -webkit-padding-end: 5px;
+  font-size: 0.9em;
+  font-weight: bold;
+  text-align: end;
+  white-space: nowrap;
+}
+
+.label:after {
+  content: ':';
+}
+
+#logo {
+  float: right;
+  margin-left: 40px;
+  text-align: right;
+  width: 200px;
+}
+
+#company {
+  font-size: 0.7em;
+  text-align: right;
+}
+
+#copyright {
+  font-size: 0.7em;
+  text-align: right;
+}
+
+.value {
+  font-family: monospace;
+  max-width: 430px;
+  padding-left: 5px;
+}
+</style>
+
+  </head>
+
+  <body>
+    <div id="outer">
+      <div id="logo">
+        <div id="company">Chromium Embedded Framework (CEF)</div>
+        <div id="copyright">Copyright &copy; $$YEAR$$ The Chromium Embedded Framework Authors.<br/>All rights reserved.<br/><a href="chrome://license">license</a> | <a href="chrome://credits">credits</a></div>
+      </div>
+      <table id="inner" cellpadding="0" cellspacing="0" border="0">
+        <tr>
+          <td class="label" valign="top">CEF</td>
+          <td class="value">$$CEF$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">Chromium</td>
+          <td class="value">$$CHROMIUM$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">OS</td>
+          <td class="value">$$OS$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">WebKit</td>
+          <td class="value">$$WEBKIT$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">JavaScript</td>
+          <td class="value">$$JAVASCRIPT$$</td>
+        </tr>
+          <tr><td class="label" valign="top">Flash</td>
+          <td class="value" id="flash">$$FLASH$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">User Agent</td>
+          <td class="value">$$USERAGENT$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">Command Line</td>
+          <td class="value">$$COMMANDLINE$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">Module Path</td>
+          <td class="value">$$MODULEPATH$$</td>
+        </tr>
+        <tr>
+          <td class="label" valign="top">Cache Path</td>
+          <td class="value">$$CACHEPATH$$</td>
+        </tr>
+      </table>
+    </div>
+  </body>
+
+</html>
+
diff --git a/src/libcef/resources/cef_resources.grd b/src/libcef/resources/cef_resources.grd
new file mode 100644
index 0000000..39b102e
--- /dev/null
+++ b/src/libcef/resources/cef_resources.grd
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1">
+  <outputs>
+    <output filename="grit/cef_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="cef_resources.pak" type="data_package" />
+  </outputs>
+  <translations />
+  <release seq="1">
+    <includes>
+      <include name="IDR_CEF_DEVTOOLS_DISCOVERY_PAGE" file="devtools_discovery_page.html" type="BINDATA" />
+      <include name="IDR_CEF_LICENSE_TXT" file="..\..\LICENSE.txt" type="BINDATA" />
+      <include name="IDR_CEF_VERSION_HTML" file="about_version.html" type="BINDATA" />
+
+      <!-- Extension features supported by CEF. -->
+      <include name="IDR_CEF_EXTENSION_API_FEATURES" file="..\common\extensions\api\_api_features.json" type="BINDATA" />
+    </includes>
+  </release>
+</grit>
diff --git a/src/libcef/resources/cef_strings.grd b/src/libcef/resources/cef_strings.grd
new file mode 100644
index 0000000..3bd509c
--- /dev/null
+++ b/src/libcef/resources/cef_strings.grd
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This file contains definitions of resources that will be translated for
+each locale. Specifically, these are UI strings that are used by CEF that
+need to be translated for each locale.-->
+<grit base_dir="." latest_public_release="0" current_release="1"
+      source_lang_id="en" enc_check="möl">
+  <outputs>
+    <!-- Note that all output references are relative to the output directory
+    which is specified at build time. -->
+    <output filename="grit/cef_strings.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+
+    <output filename="cef_strings_am.pak" type="data_package" lang="am" />
+    <output filename="cef_strings_ar.pak" type="data_package" lang="ar" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_ast.pak" type="data_package" lang="ast" />
+    </if>
+    <output filename="cef_strings_bg.pak" type="data_package" lang="bg" />
+    <output filename="cef_strings_bn.pak" type="data_package" lang="bn" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_bs.pak" type="data_package" lang="bs" />
+    </if>
+    <output filename="cef_strings_ca.pak" type="data_package" lang="ca" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_ca@valencia.pak" type="data_package" lang="ca@valencia" />
+    </if>
+    <output filename="cef_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="cef_strings_da.pak" type="data_package" lang="da" />
+    <output filename="cef_strings_de.pak" type="data_package" lang="de" />
+    <output filename="cef_strings_el.pak" type="data_package" lang="el" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_en-AU.pak" type="data_package" lang="en-AU" />
+    </if>
+    <output filename="cef_strings_en-GB.pak" type="data_package" lang="en-GB" />
+    <output filename="cef_strings_en-US.pak" type="data_package" lang="en" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_eo.pak" type="data_package" lang="eo" />
+    </if>
+    <output filename="cef_strings_es.pak" type="data_package" lang="es" />
+    <output filename="cef_strings_es-419.pak" type="data_package" lang="es-419" />
+    <output filename="cef_strings_et.pak" type="data_package" lang="et" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_eu.pak" type="data_package" lang="eu" />
+    </if>
+    <output filename="cef_strings_fa.pak" type="data_package" lang="fa" />
+    <output filename="cef_strings_fake-bidi.pak" type="data_package" lang="fake-bidi" />
+    <output filename="cef_strings_fi.pak" type="data_package" lang="fi" />
+    <output filename="cef_strings_fil.pak" type="data_package" lang="fil" />
+    <output filename="cef_strings_fr.pak" type="data_package" lang="fr" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_gl.pak" type="data_package" lang="gl" />
+    </if>
+    <output filename="cef_strings_gu.pak" type="data_package" lang="gu" />
+    <output filename="cef_strings_he.pak" type="data_package" lang="he" />
+    <output filename="cef_strings_hi.pak" type="data_package" lang="hi" />
+    <output filename="cef_strings_hr.pak" type="data_package" lang="hr" />
+    <output filename="cef_strings_hu.pak" type="data_package" lang="hu" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_hy.pak" type="data_package" lang="hy" />
+      <output filename="cef_strings_ia.pak" type="data_package" lang="ia" />
+    </if>
+    <output filename="cef_strings_id.pak" type="data_package" lang="id" />
+    <output filename="cef_strings_it.pak" type="data_package" lang="it" />
+    <output filename="cef_strings_ja.pak" type="data_package" lang="ja" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_ka.pak" type="data_package" lang="ka" />
+    </if>
+    <output filename="cef_strings_kn.pak" type="data_package" lang="kn" />
+    <output filename="cef_strings_ko.pak" type="data_package" lang="ko" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_ku.pak" type="data_package" lang="ku" />
+      <output filename="cef_strings_kw.pak" type="data_package" lang="kw" />
+    </if>
+    <output filename="cef_strings_lt.pak" type="data_package" lang="lt" />
+    <output filename="cef_strings_lv.pak" type="data_package" lang="lv" />
+    <output filename="cef_strings_ml.pak" type="data_package" lang="ml" />
+    <output filename="cef_strings_mr.pak" type="data_package" lang="mr" />
+    <output filename="cef_strings_ms.pak" type="data_package" lang="ms" />
+    <output filename="cef_strings_nl.pak" type="data_package" lang="nl" />
+    <!-- The translation console uses 'no' for Norwegian Bokmål. It should
+         be 'nb'. -->
+    <output filename="cef_strings_nb.pak" type="data_package" lang="no" />
+    <output filename="cef_strings_pl.pak" type="data_package" lang="pl" />
+    <output filename="cef_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
+    <output filename="cef_strings_pt-PT.pak" type="data_package" lang="pt-PT" />
+    <output filename="cef_strings_ro.pak" type="data_package" lang="ro" />
+    <output filename="cef_strings_ru.pak" type="data_package" lang="ru" />
+    <output filename="cef_strings_sk.pak" type="data_package" lang="sk" />
+    <output filename="cef_strings_sl.pak" type="data_package" lang="sl" />
+    <output filename="cef_strings_sr.pak" type="data_package" lang="sr" />
+    <output filename="cef_strings_sv.pak" type="data_package" lang="sv" />
+    <output filename="cef_strings_sw.pak" type="data_package" lang="sw" />
+    <output filename="cef_strings_ta.pak" type="data_package" lang="ta" />
+    <output filename="cef_strings_te.pak" type="data_package" lang="te" />
+    <output filename="cef_strings_th.pak" type="data_package" lang="th" />
+    <output filename="cef_strings_tr.pak" type="data_package" lang="tr" />
+    <if expr="pp_ifdef('use_third_party_translations')">
+      <output filename="cef_strings_ug.pak" type="data_package" lang="ug" />
+    </if>
+    <output filename="cef_strings_uk.pak" type="data_package" lang="uk" />
+    <output filename="cef_strings_vi.pak" type="data_package" lang="vi" />
+    <output filename="cef_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
+    <output filename="cef_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+  </outputs>
+  <release seq="1" allow_pseudo="false">
+    <messages fallback_to_english="true">
+      <!-- TODO add all of your "string table" messages here.  Remember to
+      change nontranslateable parts of the messages into placeholders (using the
+      <ph> element).  You can also use the 'grit add' tool to help you identify
+      nontranslateable parts and create placeholders for them. -->
+
+      <!-- Other values come from chrome/app/generated_resources.grd -->
+      <message name="IDS_TEXT_FILES" desc="The description of the text file extensions in the select file dialog.">
+        Text Files
+      </message>
+      <message name="IDS_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS" desc="The name of the No Spelling Suggestions display in the content area context menu">
+        &amp;No spelling suggestions
+      </message>
+    </messages>
+  </release>
+</grit>
diff --git a/src/libcef/resources/devtools_discovery_page.html b/src/libcef/resources/devtools_discovery_page.html
new file mode 100644
index 0000000..87933d1
--- /dev/null
+++ b/src/libcef/resources/devtools_discovery_page.html
@@ -0,0 +1,54 @@
+<html>
+<head>
+<title>CEF remote debugging</title>
+<style>
+</style>
+
+<script>
+function onLoad() {
+  var tabs_list_request = new XMLHttpRequest();
+  tabs_list_request.open("GET", "/json/list?t=" + new Date().getTime(), true);
+  tabs_list_request.onreadystatechange = onReady;
+  tabs_list_request.send();
+}
+
+function onReady() {
+  if(this.readyState == 4 && this.status == 200) {
+    if(this.response != null)
+      var responseJSON = JSON.parse(this.response);
+      for (var i = 0; i < responseJSON.length; ++i)
+        appendItem(responseJSON[i]);
+  }
+}
+
+function appendItem(item_object) {
+  var frontend_ref;
+  if (item_object.devtoolsFrontendUrl) {
+    frontend_ref = document.createElement("a");
+    frontend_ref.href = item_object.devtoolsFrontendUrl;
+    frontend_ref.title = item_object.title;
+  } else {
+    frontend_ref = document.createElement("div");
+    frontend_ref.title = "The tab already has active debugging session";
+  }
+
+  var text = document.createElement("div");
+  if (item_object.title)
+    text.innerText = item_object.title;
+  else
+    text.innerText = "(untitled tab)";
+  text.style.cssText = "background-image:url(" + item_object.faviconUrl + ")";
+  frontend_ref.appendChild(text);
+
+  var item = document.createElement("p");
+  item.appendChild(frontend_ref);
+
+  document.getElementById("items").appendChild(item);
+}
+</script>
+</head>
+<body onload='onLoad()'>
+  <div id='caption'>Inspectable WebContents</div>
+  <div id='items'></div>
+</body>
+</html>
diff --git a/src/libcef/resources/framework-Info.plist b/src/libcef/resources/framework-Info.plist
new file mode 100644
index 0000000..21f1d20
--- /dev/null
+++ b/src/libcef/resources/framework-Info.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

+<plist version="1.0">

+<dict>

+	<key>CFBundleDevelopmentRegion</key>

+	<string>en</string>

+	<key>CFBundleExecutable</key>

+	<string>${EXECUTABLE_NAME}</string>

+	<key>CFBundleIdentifier</key>

+	<string>org.chromium.ContentShell.framework</string>

+	<key>CFBundleInfoDictionaryVersion</key>

+	<string>6.0</string>

+	<key>CFBundlePackageType</key>

+	<string>FMWK</string>

+	<key>CFBundleSignature</key>

+	<string>????</string>

+</dict>

+</plist>

diff --git a/src/libcef_dll/CMakeLists.txt.in b/src/libcef_dll/CMakeLists.txt.in
new file mode 100644
index 0000000..a218553
--- /dev/null
+++ b/src/libcef_dll/CMakeLists.txt.in
@@ -0,0 +1,50 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+# Append platform specific sources to a list of sources.
+macro(LIBCEF_APPEND_PLATFORM_SOURCES name_of_list)
+  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin" AND ${name_of_list}_MACOSX)
+    list(APPEND ${name_of_list} ${${name_of_list}_MACOSX})
+  endif()
+  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND ${name_of_list}_LINUX)
+    list(APPEND ${name_of_list} ${${name_of_list}_LINUX})
+  endif()
+  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" AND ${name_of_list}_WINDOWS)
+    list(APPEND ${name_of_list} ${${name_of_list}_WINDOWS})
+  endif()
+endmacro()
+
+set(CEF_TARGET libcef_dll_wrapper)
+
+{{
+  'prefix': 'libcef',
+  'library': '${CEF_TARGET}',
+  'append_macro': 'LIBCEF_APPEND_PLATFORM_SOURCES',
+  'includes': [
+    'includes_common',
+    'includes_common_capi',
+    'autogen_cpp_includes',
+    'includes_capi',
+    'autogen_capi_includes',
+    'includes_wrapper',
+    'includes_wrapper_mac:MACOSX',
+    'includes_win:WINDOWS',
+    'includes_win_capi:WINDOWS',
+    'includes_mac:MACOSX',
+    'includes_mac_capi:MACOSX',
+    'includes_linux:LINUX',
+    'includes_linux_capi:LINUX',
+    'libcef_dll_wrapper_sources_base',
+    'libcef_dll_wrapper_sources_common',
+    'libcef_dll_wrapper_sources_mac:MACOSX',
+    'autogen_client_side',
+  ],
+}}
+SET_LIBRARY_TARGET_PROPERTIES(${CEF_TARGET})
+
+# Creating the CEF wrapper library. Do not define this for dependent targets.
+target_compile_definitions(${CEF_TARGET} PRIVATE -DWRAPPING_CEF_SHARED)
+
+# Remove the default "lib" prefix from the resulting library.
+set_target_properties(${CEF_TARGET} PROPERTIES PREFIX "")
diff --git a/src/libcef_dll/base/cef_atomicops_x86_gcc.cc b/src/libcef_dll/base/cef_atomicops_x86_gcc.cc
new file mode 100644
index 0000000..f3dff5e
--- /dev/null
+++ b/src/libcef_dll/base/cef_atomicops_x86_gcc.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This module gets enough CPU information to optimize the
+// atomicops module on x86.
+
+#include <stdint.h>
+#include <string.h>
+
+#include "include/base/cef_atomicops.h"
+
+// This file only makes sense with atomicops_internals_x86_gcc.h -- it
+// depends on structs that are defined in that file.  If atomicops.h
+// doesn't sub-include that file, then we aren't needed, and shouldn't
+// try to do anything.
+#ifdef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_GCC_H_
+
+// Inline cpuid instruction.  In PIC compilations, %ebx contains the address
+// of the global offset table.  To avoid breaking such executables, this code
+// must preserve that register's value across cpuid instructions.
+#if defined(__i386__)
+#define cpuid(a, b, c, d, inp)             \
+  asm("mov %%ebx, %%edi\n"                 \
+      "cpuid\n"                            \
+      "xchg %%edi, %%ebx\n"                \
+      : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \
+      : "a"(inp))
+#elif defined(__x86_64__)
+#define cpuid(a, b, c, d, inp)             \
+  asm("mov %%rbx, %%rdi\n"                 \
+      "cpuid\n"                            \
+      "xchg %%rdi, %%rbx\n"                \
+      : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \
+      : "a"(inp))
+#endif
+
+#if defined(cpuid)  // initialize the struct only on x86
+
+// Set the flags so that code will run correctly and conservatively, so even
+// if we haven't been initialized yet, we're probably single threaded, and our
+// default values should hopefully be pretty safe.
+struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = {
+    false,  // bug can't exist before process spawns multiple threads
+};
+
+namespace {
+
+// Initialize the AtomicOps_Internalx86CPUFeatures struct.
+void AtomicOps_Internalx86CPUFeaturesInit() {
+  uint32_t eax;
+  uint32_t ebx;
+  uint32_t ecx;
+  uint32_t edx;
+
+  // Get vendor string (issue CPUID with eax = 0)
+  cpuid(eax, ebx, ecx, edx, 0);
+  char vendor[13];
+  memcpy(vendor, &ebx, 4);
+  memcpy(vendor + 4, &edx, 4);
+  memcpy(vendor + 8, &ecx, 4);
+  vendor[12] = 0;
+
+  // get feature flags in ecx/edx, and family/model in eax
+  cpuid(eax, ebx, ecx, edx, 1);
+
+  int family = (eax >> 8) & 0xf;  // family and model fields
+  int model = (eax >> 4) & 0xf;
+  if (family == 0xf) {  // use extended family and model fields
+    family += (eax >> 20) & 0xff;
+    model += ((eax >> 16) & 0xf) << 4;
+  }
+
+  // Opteron Rev E has a bug in which on very rare occasions a locked
+  // instruction doesn't act as a read-acquire barrier if followed by a
+  // non-locked read-modify-write instruction.  Rev F has this bug in
+  // pre-release versions, but not in versions released to customers,
+  // so we test only for Rev E, which is family 15, model 32..63 inclusive.
+  if (strcmp(vendor, "AuthenticAMD") == 0 &&  // AMD
+      family == 15 && 32 <= model && model <= 63) {
+    AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = true;
+  } else {
+    AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false;
+  }
+}
+
+class AtomicOpsx86Initializer {
+ public:
+  AtomicOpsx86Initializer() { AtomicOps_Internalx86CPUFeaturesInit(); }
+};
+
+// A global to get use initialized on startup via static initialization :/
+AtomicOpsx86Initializer g_initer;
+
+}  // namespace
+
+#endif  // if x86
+
+#endif  // ifdef CEF_INCLUDE_BASE_CEF_ATOMICOPS_INTERNALS_X86_GCC_H_
diff --git a/src/libcef_dll/base/cef_bind_helpers.cc b/src/libcef_dll/base/cef_bind_helpers.cc
new file mode 100644
index 0000000..73f48fc
--- /dev/null
+++ b/src/libcef_dll/base/cef_bind_helpers.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/cef_bind_helpers.h"
+
+#include "include/base/cef_callback.h"
+
+namespace base {
+
+void DoNothing() {}
+
+}  // namespace base
diff --git a/src/libcef_dll/base/cef_callback_helpers.cc b/src/libcef_dll/base/cef_callback_helpers.cc
new file mode 100644
index 0000000..624e509
--- /dev/null
+++ b/src/libcef_dll/base/cef_callback_helpers.cc
@@ -0,0 +1,40 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/cef_callback_helpers.h"
+
+#include "include/base/cef_callback.h"
+
+namespace base {
+
+ScopedClosureRunner::ScopedClosureRunner() {}
+
+ScopedClosureRunner::ScopedClosureRunner(const Closure& closure)
+    : closure_(closure) {}
+
+ScopedClosureRunner::~ScopedClosureRunner() {
+  if (!closure_.is_null())
+    closure_.Run();
+}
+
+void ScopedClosureRunner::Reset() {
+  Closure old_closure = Release();
+  if (!old_closure.is_null())
+    old_closure.Run();
+}
+
+void ScopedClosureRunner::Reset(const Closure& closure) {
+  Closure old_closure = Release();
+  closure_ = closure;
+  if (!old_closure.is_null())
+    old_closure.Run();
+}
+
+Closure ScopedClosureRunner::Release() {
+  Closure result = closure_;
+  closure_.Reset();
+  return result;
+}
+
+}  // namespace base
diff --git a/src/libcef_dll/base/cef_callback_internal.cc b/src/libcef_dll/base/cef_callback_internal.cc
new file mode 100644
index 0000000..dead93f
--- /dev/null
+++ b/src/libcef_dll/base/cef_callback_internal.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/internal/cef_callback_internal.h"
+
+#include "include/base/cef_logging.h"
+
+namespace base {
+namespace cef_internal {
+
+void BindStateBase::AddRef() {
+  AtomicRefCountInc(&ref_count_);
+}
+
+void BindStateBase::Release() {
+  if (!AtomicRefCountDec(&ref_count_))
+    destructor_(this);
+}
+
+void CallbackBase::Reset() {
+  polymorphic_invoke_ = NULL;
+  // NULL the bind_state_ last, since it may be holding the last ref to whatever
+  // object owns us, and we may be deleted after that.
+  bind_state_ = NULL;
+}
+
+bool CallbackBase::Equals(const CallbackBase& other) const {
+  return bind_state_.get() == other.bind_state_.get() &&
+         polymorphic_invoke_ == other.polymorphic_invoke_;
+}
+
+CallbackBase::CallbackBase(BindStateBase* bind_state)
+    : bind_state_(bind_state), polymorphic_invoke_(NULL) {
+  DCHECK(!bind_state_.get() || bind_state_->ref_count_ == 1);
+}
+
+CallbackBase::~CallbackBase() {}
+
+}  // namespace cef_internal
+}  // namespace base
diff --git a/src/libcef_dll/base/cef_lock.cc b/src/libcef_dll/base/cef_lock.cc
new file mode 100644
index 0000000..ea2ba3c
--- /dev/null
+++ b/src/libcef_dll/base/cef_lock.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is used for debugging assertion support.  The Lock class
+// is functionally a wrapper around the LockImpl class, so the only
+// real intelligence in the class is in the debugging logic.
+
+#include "include/base/cef_lock.h"
+#include "include/base/cef_logging.h"
+
+#if DCHECK_IS_ON()
+
+namespace base {
+namespace cef_internal {
+
+Lock::Lock() : lock_() {}
+
+Lock::~Lock() {
+  DCHECK(owning_thread_ref_.is_null());
+}
+
+void Lock::AssertAcquired() const {
+  DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef());
+}
+
+void Lock::CheckHeldAndUnmark() {
+  DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef());
+  owning_thread_ref_ = PlatformThreadRef();
+}
+
+void Lock::CheckUnheldAndMark() {
+  // Hitting this DCHECK means that your code is trying to re-enter a lock that
+  // is already held. The Chromium Lock implementation is not reentrant.
+  // See "Why can the holder of a Lock not reacquire it?" at
+  // http://www.chromium.org/developers/lock-and-condition-variable for more
+  // information.
+  DCHECK(owning_thread_ref_.is_null());
+  owning_thread_ref_ = PlatformThread::CurrentRef();
+}
+
+}  // namespace cef_internal
+}  // namespace base
+
+#endif  // DCHECK_IS_ON()
diff --git a/src/libcef_dll/base/cef_lock_impl.cc b/src/libcef_dll/base/cef_lock_impl.cc
new file mode 100644
index 0000000..4afe75f
--- /dev/null
+++ b/src/libcef_dll/base/cef_lock_impl.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/internal/cef_lock_impl.h"
+
+#if defined(OS_WIN)
+
+namespace base {
+namespace cef_internal {
+
+LockImpl::LockImpl() {
+  // The second parameter is the spin count, for short-held locks it avoid the
+  // contending thread from going to sleep which helps performance greatly.
+  ::InitializeCriticalSectionAndSpinCount(&native_handle_, 2000);
+}
+
+LockImpl::~LockImpl() {
+  ::DeleteCriticalSection(&native_handle_);
+}
+
+bool LockImpl::Try() {
+  if (::TryEnterCriticalSection(&native_handle_) != FALSE) {
+    return true;
+  }
+  return false;
+}
+
+void LockImpl::Lock() {
+  ::EnterCriticalSection(&native_handle_);
+}
+
+void LockImpl::Unlock() {
+  ::LeaveCriticalSection(&native_handle_);
+}
+
+}  // namespace cef_internal
+}  // namespace base
+
+#elif defined(OS_POSIX)
+
+#include <errno.h>
+#include <string.h>
+
+#include "include/base/cef_logging.h"
+
+namespace base {
+namespace cef_internal {
+
+LockImpl::LockImpl() {
+#if DCHECK_IS_ON()
+  // In debug, setup attributes for lock error checking.
+  pthread_mutexattr_t mta;
+  int rv = pthread_mutexattr_init(&mta);
+  DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+  rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK);
+  DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+  rv = pthread_mutex_init(&native_handle_, &mta);
+  DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+  rv = pthread_mutexattr_destroy(&mta);
+  DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+#else
+  // In release, go with the default lock attributes.
+  pthread_mutex_init(&native_handle_, NULL);
+#endif
+}
+
+LockImpl::~LockImpl() {
+  int rv = pthread_mutex_destroy(&native_handle_);
+  DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+}
+
+bool LockImpl::Try() {
+  int rv = pthread_mutex_trylock(&native_handle_);
+  DCHECK(rv == 0 || rv == EBUSY) << ". " << strerror(rv);
+  return rv == 0;
+}
+
+void LockImpl::Lock() {
+  int rv = pthread_mutex_lock(&native_handle_);
+  DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+}
+
+void LockImpl::Unlock() {
+  int rv = pthread_mutex_unlock(&native_handle_);
+  DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+}
+
+}  // namespace cef_internal
+}  // namespace base
+
+#endif  // defined(OS_POSIX)
diff --git a/src/libcef_dll/base/cef_logging.cc b/src/libcef_dll/base/cef_logging.cc
new file mode 100644
index 0000000..3560291
--- /dev/null
+++ b/src/libcef_dll/base/cef_logging.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/cef_logging.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <algorithm>
+#include <sstream>
+#elif defined(OS_POSIX)
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#endif
+
+#include "include/internal/cef_string_types.h"
+
+namespace cef {
+namespace logging {
+
+namespace {
+
+#if defined(OS_POSIX)
+// From base/posix/safe_strerror.cc
+
+#if defined(__GLIBC__) || defined(OS_NACL)
+#define USE_HISTORICAL_STRERRO_R 1
+#else
+#define USE_HISTORICAL_STRERRO_R 0
+#endif
+
+#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
+// GCC will complain about the unused second wrap function unless we tell it
+// that we meant for them to be potentially unused, which is exactly what this
+// attribute is for.
+#define POSSIBLY_UNUSED __attribute__((unused))
+#else
+#define POSSIBLY_UNUSED
+#endif
+
+#if USE_HISTORICAL_STRERRO_R
+// glibc has two strerror_r functions: a historical GNU-specific one that
+// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
+// that returns int. This wraps the GNU-specific one.
+static void POSSIBLY_UNUSED
+wrap_posix_strerror_r(char* (*strerror_r_ptr)(int, char*, size_t),
+                      int err,
+                      char* buf,
+                      size_t len) {
+  // GNU version.
+  char* rc = (*strerror_r_ptr)(err, buf, len);
+  if (rc != buf) {
+    // glibc did not use buf and returned a static string instead. Copy it
+    // into buf.
+    buf[0] = '\0';
+    strncat(buf, rc, len - 1);
+  }
+  // The GNU version never fails. Unknown errors get an "unknown error" message.
+  // The result is always null terminated.
+}
+#endif  // USE_HISTORICAL_STRERRO_R
+
+// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
+// does not define the behaviour for some of the edge cases, so we wrap it to
+// guarantee that they are handled. This is compiled on all POSIX platforms, but
+// it will only be used on Linux if the POSIX strerror_r implementation is
+// being used (see below).
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(int (*strerror_r_ptr)(int,
+                                                                        char*,
+                                                                        size_t),
+                                                  int err,
+                                                  char* buf,
+                                                  size_t len) {
+  int old_errno = errno;
+  // Have to cast since otherwise we get an error if this is the GNU version
+  // (but in such a scenario this function is never called). Sadly we can't use
+  // C++-style casts because the appropriate one is reinterpret_cast but it's
+  // considered illegal to reinterpret_cast a type to itself, so we get an
+  // error in the opposite case.
+  int result = (*strerror_r_ptr)(err, buf, len);
+  if (result == 0) {
+    // POSIX is vague about whether the string will be terminated, although
+    // it indirectly implies that typically ERANGE will be returned, instead
+    // of truncating the string. We play it safe by always terminating the
+    // string explicitly.
+    buf[len - 1] = '\0';
+  } else {
+    // Error. POSIX is vague about whether the return value is itself a system
+    // error code or something else. On Linux currently it is -1 and errno is
+    // set. On BSD-derived systems it is a system error and errno is unchanged.
+    // We try and detect which case it is so as to put as much useful info as
+    // we can into our message.
+    int strerror_error;  // The error encountered in strerror
+    int new_errno = errno;
+    if (new_errno != old_errno) {
+      // errno was changed, so probably the return value is just -1 or something
+      // else that doesn't provide any info, and errno is the error.
+      strerror_error = new_errno;
+    } else {
+      // Either the error from strerror_r was the same as the previous value, or
+      // errno wasn't used. Assume the latter.
+      strerror_error = result;
+    }
+    // snprintf truncates and always null-terminates.
+    snprintf(buf, len, "Error %d while retrieving error %d", strerror_error,
+             err);
+  }
+  errno = old_errno;
+}
+
+void safe_strerror_r(int err, char* buf, size_t len) {
+  if (buf == NULL || len <= 0) {
+    return;
+  }
+  // If using glibc (i.e., Linux), the compiler will automatically select the
+  // appropriate overloaded function based on the function type of strerror_r.
+  // The other one will be elided from the translation unit since both are
+  // static.
+  wrap_posix_strerror_r(&strerror_r, err, buf, len);
+}
+
+std::string safe_strerror(int err) {
+  const int buffer_size = 256;
+  char buf[buffer_size];
+  safe_strerror_r(err, buf, sizeof(buf));
+  return std::string(buf);
+}
+#endif  // defined(OS_POSIX)
+
+}  // namespace
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Explicit instantiations for commonly used comparisons.
+template std::string* MakeCheckOpString<int, int>(const int&,
+                                                  const int&,
+                                                  const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+    const unsigned long&,
+    const unsigned long&,
+    const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+    const unsigned long&,
+    const unsigned int&,
+    const char* names);
+template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+    const unsigned int&,
+    const unsigned long&,
+    const char* names);
+template std::string* MakeCheckOpString<std::string, std::string>(
+    const std::string&,
+    const std::string&,
+    const char* name);
+#endif
+
+#if defined(OS_WIN)
+LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) {}
+
+LogMessage::SaveLastError::~SaveLastError() {
+  ::SetLastError(last_error_);
+}
+#endif  // defined(OS_WIN)
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+    : severity_(severity), file_(file), line_(line) {}
+
+LogMessage::LogMessage(const char* file, int line, std::string* result)
+    : severity_(LOG_FATAL), file_(file), line_(line) {
+  stream_ << "Check failed: " << *result;
+  delete result;
+}
+
+LogMessage::LogMessage(const char* file,
+                       int line,
+                       LogSeverity severity,
+                       std::string* result)
+    : severity_(severity), file_(file), line_(line) {
+  stream_ << "Check failed: " << *result;
+  delete result;
+}
+
+LogMessage::~LogMessage() {
+  std::string str_newline(stream_.str());
+  cef_log(file_, line_, severity_, str_newline.c_str());
+}
+
+#if defined(OS_WIN)
+// This has already been defined in the header, but defining it again as DWORD
+// ensures that the type used in the header is equivalent to DWORD. If not,
+// the redefinition is a compile error.
+typedef DWORD SystemErrorCode;
+#endif
+
+SystemErrorCode GetLastSystemErrorCode() {
+#if defined(OS_WIN)
+  return ::GetLastError();
+#elif defined(OS_POSIX)
+  return errno;
+#else
+#error Not implemented
+#endif
+}
+
+#if defined(OS_WIN)
+std::string SystemErrorCodeToString(SystemErrorCode error_code) {
+  const int error_message_buffer_size = 256;
+  char msgbuf[error_message_buffer_size];
+  DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+  DWORD len = FormatMessageA(flags, NULL, error_code, 0, msgbuf,
+                             arraysize(msgbuf), NULL);
+  std::stringstream ss;
+  if (len) {
+    std::string s(msgbuf);
+    // Messages returned by system end with line breaks.
+    s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
+    ss << s << " (0x" << std::hex << error_code << ")";
+  } else {
+    ss << "Error (0x" << std::hex << GetLastError()
+       << ") while retrieving error. (0x" << error_code << ")";
+  }
+  return ss.str();
+}
+#elif defined(OS_POSIX)
+std::string SystemErrorCodeToString(SystemErrorCode error_code) {
+  return safe_strerror(error_code);
+}
+#else
+#error Not implemented
+#endif
+
+#if defined(OS_WIN)
+Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
+                                           int line,
+                                           LogSeverity severity,
+                                           SystemErrorCode err)
+    : err_(err), log_message_(file, line, severity) {}
+
+Win32ErrorLogMessage::~Win32ErrorLogMessage() {
+  stream() << ": " << SystemErrorCodeToString(err_);
+}
+#elif defined(OS_POSIX)
+ErrnoLogMessage::ErrnoLogMessage(const char* file,
+                                 int line,
+                                 LogSeverity severity,
+                                 SystemErrorCode err)
+    : err_(err), log_message_(file, line, severity) {}
+
+ErrnoLogMessage::~ErrnoLogMessage() {
+  stream() << ": " << SystemErrorCodeToString(err_);
+}
+#endif  // OS_WIN
+
+}  // namespace logging
+}  // namespace cef
+
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
+  std::wstring tmp_str(wstr);
+  if (!tmp_str.empty()) {
+    cef_string_utf8_t str = {0};
+    cef_string_wide_to_utf8(wstr, tmp_str.size(), &str);
+    out << str.str;
+    cef_string_utf8_clear(&str);
+  }
+  return out;
+}
diff --git a/src/libcef_dll/base/cef_ref_counted.cc b/src/libcef_dll/base/cef_ref_counted.cc
new file mode 100644
index 0000000..fdd22b1
--- /dev/null
+++ b/src/libcef_dll/base/cef_ref_counted.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/cef_ref_counted.h"
+
+namespace base {
+
+namespace cef_subtle {
+
+bool RefCountedThreadSafeBase::HasOneRef() const {
+  return AtomicRefCountIsOne(
+      &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_);
+}
+
+bool RefCountedThreadSafeBase::HasAtLeastOneRef() const {
+  return !AtomicRefCountIsZero(
+      &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_);
+}
+
+RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) {
+#if DCHECK_IS_ON()
+  in_dtor_ = false;
+#endif
+}
+
+RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
+#if DCHECK_IS_ON()
+  DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without "
+                      "calling Release()";
+#endif
+}
+
+void RefCountedThreadSafeBase::AddRef() const {
+#if DCHECK_IS_ON()
+  DCHECK(!in_dtor_);
+#endif
+  AtomicRefCountInc(&ref_count_);
+}
+
+bool RefCountedThreadSafeBase::Release() const {
+#if DCHECK_IS_ON()
+  DCHECK(!in_dtor_);
+  DCHECK(!AtomicRefCountIsZero(&ref_count_));
+#endif
+  if (!AtomicRefCountDec(&ref_count_)) {
+#if DCHECK_IS_ON()
+    in_dtor_ = true;
+#endif
+    return true;
+  }
+  return false;
+}
+
+}  // namespace cef_subtle
+
+}  // namespace base
diff --git a/src/libcef_dll/base/cef_string16.cc b/src/libcef_dll/base/cef_string16.cc
new file mode 100644
index 0000000..16dea8c
--- /dev/null
+++ b/src/libcef_dll/base/cef_string16.cc
@@ -0,0 +1,96 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/cef_string16.h"
+
+#if defined(OS_POSIX)
+#if defined(WCHAR_T_IS_UTF16)
+
+#error This file should not be used on 2-byte wchar_t systems
+// If this winds up being needed on 2-byte wchar_t systems, either the
+// definitions below can be used, or the host system's wide character
+// functions like wmemcmp can be wrapped.
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+#include <cstring>
+#include <ostream>
+
+#include "include/internal/cef_string_types.h"
+
+namespace cef {
+namespace base {
+
+int c16memcmp(const char16* s1, const char16* s2, size_t n) {
+  // We cannot call memcmp because that changes the semantics.
+  while (n-- > 0) {
+    if (*s1 != *s2) {
+      // We cannot use (*s1 - *s2) because char16 is unsigned.
+      return ((*s1 < *s2) ? -1 : 1);
+    }
+    ++s1;
+    ++s2;
+  }
+  return 0;
+}
+
+size_t c16len(const char16* s) {
+  const char16* s_orig = s;
+  while (*s) {
+    ++s;
+  }
+  return s - s_orig;
+}
+
+const char16* c16memchr(const char16* s, char16 c, size_t n) {
+  while (n-- > 0) {
+    if (*s == c) {
+      return s;
+    }
+    ++s;
+  }
+  return 0;
+}
+
+char16* c16memmove(char16* s1, const char16* s2, size_t n) {
+  return static_cast<char16*>(memmove(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memcpy(char16* s1, const char16* s2, size_t n) {
+  return static_cast<char16*>(memcpy(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memset(char16* s, char16 c, size_t n) {
+  char16* s_orig = s;
+  while (n-- > 0) {
+    *s = c;
+    ++s;
+  }
+  return s_orig;
+}
+
+}  // namespace base
+}  // namespace cef
+
+namespace base {
+
+std::ostream& operator<<(std::ostream& out, const string16& str) {
+  cef_string_utf8_t cef_str = {0};
+  cef_string_utf16_to_utf8(str.c_str(), str.size(), &cef_str);
+  out << cef_str.str;
+  cef_string_utf8_clear(&cef_str);
+  return out;
+}
+
+void PrintTo(const string16& str, std::ostream* out) {
+  *out << str;
+}
+
+}  // namespace base
+
+template class std::basic_string<cef::base::char16,
+                                 cef::base::string16_char_traits>;
+
+#endif  // WCHAR_T_IS_UTF32
+#endif  // OS_POSIX
diff --git a/src/libcef_dll/base/cef_thread_checker_impl.cc b/src/libcef_dll/base/cef_thread_checker_impl.cc
new file mode 100644
index 0000000..4f5a9b0
--- /dev/null
+++ b/src/libcef_dll/base/cef_thread_checker_impl.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/internal/cef_thread_checker_impl.h"
+
+namespace base {
+namespace cef_internal {
+
+ThreadCheckerImpl::ThreadCheckerImpl() : valid_thread_id_() {
+  EnsureThreadIdAssigned();
+}
+
+ThreadCheckerImpl::~ThreadCheckerImpl() {}
+
+bool ThreadCheckerImpl::CalledOnValidThread() const {
+  EnsureThreadIdAssigned();
+  AutoLock auto_lock(lock_);
+  return valid_thread_id_ == PlatformThread::CurrentRef();
+}
+
+void ThreadCheckerImpl::DetachFromThread() {
+  AutoLock auto_lock(lock_);
+  valid_thread_id_ = PlatformThreadRef();
+}
+
+void ThreadCheckerImpl::EnsureThreadIdAssigned() const {
+  AutoLock auto_lock(lock_);
+  if (valid_thread_id_.is_null()) {
+    valid_thread_id_ = PlatformThread::CurrentRef();
+  }
+}
+
+}  // namespace cef_internal
+}  // namespace base
diff --git a/src/libcef_dll/base/cef_weak_ptr.cc b/src/libcef_dll/base/cef_weak_ptr.cc
new file mode 100644
index 0000000..3e52831
--- /dev/null
+++ b/src/libcef_dll/base/cef_weak_ptr.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "include/base/cef_weak_ptr.h"
+
+namespace base {
+namespace cef_internal {
+
+WeakReference::Flag::Flag() : is_valid_(true) {
+  // Flags only become bound when checked for validity, or invalidated,
+  // so that we can check that later validity/invalidation operations on
+  // the same Flag take place on the same thread.
+  thread_checker_.DetachFromThread();
+}
+
+void WeakReference::Flag::Invalidate() {
+  // The flag being invalidated with a single ref implies that there are no
+  // weak pointers in existence. Allow deletion on other thread in this case.
+  DCHECK(thread_checker_.CalledOnValidThread() || HasOneRef())
+      << "WeakPtrs must be invalidated on the same thread.";
+  is_valid_ = false;
+}
+
+bool WeakReference::Flag::IsValid() const {
+  DCHECK(thread_checker_.CalledOnValidThread())
+      << "WeakPtrs must be checked on the same thread.";
+  return is_valid_;
+}
+
+WeakReference::Flag::~Flag() {}
+
+WeakReference::WeakReference() {}
+
+WeakReference::WeakReference(const Flag* flag) : flag_(flag) {}
+
+WeakReference::~WeakReference() {}
+
+bool WeakReference::is_valid() const {
+  return flag_.get() && flag_->IsValid();
+}
+
+WeakReferenceOwner::WeakReferenceOwner() {}
+
+WeakReferenceOwner::~WeakReferenceOwner() {
+  Invalidate();
+}
+
+WeakReference WeakReferenceOwner::GetRef() const {
+  // If we hold the last reference to the Flag then create a new one.
+  if (!HasRefs())
+    flag_ = new WeakReference::Flag();
+
+  return WeakReference(flag_.get());
+}
+
+void WeakReferenceOwner::Invalidate() {
+  if (flag_.get()) {
+    flag_->Invalidate();
+    flag_ = NULL;
+  }
+}
+
+WeakPtrBase::WeakPtrBase() {}
+
+WeakPtrBase::~WeakPtrBase() {}
+
+WeakPtrBase::WeakPtrBase(const WeakReference& ref) : ref_(ref) {}
+
+}  // namespace cef_internal
+}  // namespace base
diff --git a/src/libcef_dll/cpptoc/accessibility_handler_cpptoc.cc b/src/libcef_dll/cpptoc/accessibility_handler_cpptoc.cc
new file mode 100644
index 0000000..609884a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/accessibility_handler_cpptoc.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fd840fce008edc7699ad649e5f114ec7dc0259e1$
+//
+
+#include "libcef_dll/cpptoc/accessibility_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK accessibility_handler_on_accessibility_tree_change(
+    struct _cef_accessibility_handler_t* self,
+    struct _cef_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value);
+  if (!value)
+    return;
+
+  // Execute
+  CefAccessibilityHandlerCppToC::Get(self)->OnAccessibilityTreeChange(
+      CefValueCToCpp::Wrap(value));
+}
+
+void CEF_CALLBACK accessibility_handler_on_accessibility_location_change(
+    struct _cef_accessibility_handler_t* self,
+    struct _cef_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value);
+  if (!value)
+    return;
+
+  // Execute
+  CefAccessibilityHandlerCppToC::Get(self)->OnAccessibilityLocationChange(
+      CefValueCToCpp::Wrap(value));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAccessibilityHandlerCppToC::CefAccessibilityHandlerCppToC() {
+  GetStruct()->on_accessibility_tree_change =
+      accessibility_handler_on_accessibility_tree_change;
+  GetStruct()->on_accessibility_location_change =
+      accessibility_handler_on_accessibility_location_change;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAccessibilityHandlerCppToC::~CefAccessibilityHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefAccessibilityHandler> CefCppToCRefCounted<
+    CefAccessibilityHandlerCppToC,
+    CefAccessibilityHandler,
+    cef_accessibility_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                cef_accessibility_handler_t*
+                                                    s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefAccessibilityHandlerCppToC,
+                                   CefAccessibilityHandler,
+                                   cef_accessibility_handler_t>::kWrapperType =
+    WT_ACCESSIBILITY_HANDLER;
diff --git a/src/libcef_dll/cpptoc/accessibility_handler_cpptoc.h b/src/libcef_dll/cpptoc/accessibility_handler_cpptoc.h
new file mode 100644
index 0000000..c0d41e1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/accessibility_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5bf6a0415807090dfca56e879aa010846a24bcf5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_ACCESSIBILITY_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_ACCESSIBILITY_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_accessibility_handler_capi.h"
+#include "include/cef_accessibility_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefAccessibilityHandlerCppToC
+    : public CefCppToCRefCounted<CefAccessibilityHandlerCppToC,
+                                 CefAccessibilityHandler,
+                                 cef_accessibility_handler_t> {
+ public:
+  CefAccessibilityHandlerCppToC();
+  virtual ~CefAccessibilityHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_ACCESSIBILITY_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/app_cpptoc.cc b/src/libcef_dll/cpptoc/app_cpptoc.cc
new file mode 100644
index 0000000..b6b9c77
--- /dev/null
+++ b/src/libcef_dll/cpptoc/app_cpptoc.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=44e223e0d74d87cfc4aeee6bfad7e6582390a0ad$
+//
+
+#include "libcef_dll/cpptoc/app_cpptoc.h"
+#include "libcef_dll/cpptoc/browser_process_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/render_process_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/resource_bundle_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/command_line_ctocpp.h"
+#include "libcef_dll/ctocpp/scheme_registrar_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK app_on_before_command_line_processing(
+    struct _cef_app_t* self,
+    const cef_string_t* process_type,
+    struct _cef_command_line_t* command_line) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: command_line; type: refptr_diff
+  DCHECK(command_line);
+  if (!command_line)
+    return;
+  // Unverified params: process_type
+
+  // Execute
+  CefAppCppToC::Get(self)->OnBeforeCommandLineProcessing(
+      CefString(process_type), CefCommandLineCToCpp::Wrap(command_line));
+}
+
+void CEF_CALLBACK
+app_on_register_custom_schemes(struct _cef_app_t* self,
+                               struct _cef_scheme_registrar_t* registrar) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: registrar; type: rawptr_diff
+  DCHECK(registrar);
+  if (!registrar)
+    return;
+
+  // Translate param: registrar; type: rawptr_diff
+  CefOwnPtr<CefSchemeRegistrar> registrarPtr(
+      CefSchemeRegistrarCToCpp::Wrap(registrar));
+
+  // Execute
+  CefAppCppToC::Get(self)->OnRegisterCustomSchemes(registrarPtr.get());
+}
+
+struct _cef_resource_bundle_handler_t* CEF_CALLBACK
+app_get_resource_bundle_handler(struct _cef_app_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefResourceBundleHandler> _retval =
+      CefAppCppToC::Get(self)->GetResourceBundleHandler();
+
+  // Return type: refptr_same
+  return CefResourceBundleHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_browser_process_handler_t* CEF_CALLBACK
+app_get_browser_process_handler(struct _cef_app_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserProcessHandler> _retval =
+      CefAppCppToC::Get(self)->GetBrowserProcessHandler();
+
+  // Return type: refptr_same
+  return CefBrowserProcessHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_render_process_handler_t* CEF_CALLBACK
+app_get_render_process_handler(struct _cef_app_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRenderProcessHandler> _retval =
+      CefAppCppToC::Get(self)->GetRenderProcessHandler();
+
+  // Return type: refptr_same
+  return CefRenderProcessHandlerCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAppCppToC::CefAppCppToC() {
+  GetStruct()->on_before_command_line_processing =
+      app_on_before_command_line_processing;
+  GetStruct()->on_register_custom_schemes = app_on_register_custom_schemes;
+  GetStruct()->get_resource_bundle_handler = app_get_resource_bundle_handler;
+  GetStruct()->get_browser_process_handler = app_get_browser_process_handler;
+  GetStruct()->get_render_process_handler = app_get_render_process_handler;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAppCppToC::~CefAppCppToC() {}
+
+template <>
+CefRefPtr<CefApp>
+CefCppToCRefCounted<CefAppCppToC, CefApp, cef_app_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_app_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefAppCppToC, CefApp, cef_app_t>::kWrapperType = WT_APP;
diff --git a/src/libcef_dll/cpptoc/app_cpptoc.h b/src/libcef_dll/cpptoc/app_cpptoc.h
new file mode 100644
index 0000000..43ddd7f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/app_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0326da69d7ade92ca68cef231ded42b149406dee$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_APP_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_APP_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_app_capi.h"
+#include "include/cef_app.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefAppCppToC
+    : public CefCppToCRefCounted<CefAppCppToC, CefApp, cef_app_t> {
+ public:
+  CefAppCppToC();
+  virtual ~CefAppCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_APP_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/audio_handler_cpptoc.cc b/src/libcef_dll/cpptoc/audio_handler_cpptoc.cc
new file mode 100644
index 0000000..fc8dc3f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/audio_handler_cpptoc.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4568fbb1f264fe9a900784aa3040c406a919ad37$
+//
+
+#include "libcef_dll/cpptoc/audio_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+audio_handler_get_audio_parameters(struct _cef_audio_handler_t* self,
+                                   struct _cef_browser_t* browser,
+                                   cef_audio_parameters_t* params) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: params; type: simple_byref
+  DCHECK(params);
+  if (!params)
+    return 0;
+
+  // Translate param: params; type: simple_byref
+  CefAudioParameters paramsVal = params ? *params : CefAudioParameters();
+
+  // Execute
+  bool _retval = CefAudioHandlerCppToC::Get(self)->GetAudioParameters(
+      CefBrowserCToCpp::Wrap(browser), paramsVal);
+
+  // Restore param: params; type: simple_byref
+  if (params)
+    *params = paramsVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+audio_handler_on_audio_stream_started(struct _cef_audio_handler_t* self,
+                                      struct _cef_browser_t* browser,
+                                      const cef_audio_parameters_t* params,
+                                      int channels) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: params; type: simple_byref_const
+  DCHECK(params);
+  if (!params)
+    return;
+
+  // Translate param: params; type: simple_byref_const
+  CefAudioParameters paramsVal = params ? *params : CefAudioParameters();
+
+  // Execute
+  CefAudioHandlerCppToC::Get(self)->OnAudioStreamStarted(
+      CefBrowserCToCpp::Wrap(browser), paramsVal, channels);
+}
+
+void CEF_CALLBACK
+audio_handler_on_audio_stream_packet(struct _cef_audio_handler_t* self,
+                                     struct _cef_browser_t* browser,
+                                     const float** data,
+                                     int frames,
+                                     int64 pts) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  CefAudioHandlerCppToC::Get(self)->OnAudioStreamPacket(
+      CefBrowserCToCpp::Wrap(browser), data, frames, pts);
+}
+
+void CEF_CALLBACK
+audio_handler_on_audio_stream_stopped(struct _cef_audio_handler_t* self,
+                                      struct _cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefAudioHandlerCppToC::Get(self)->OnAudioStreamStopped(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+void CEF_CALLBACK
+audio_handler_on_audio_stream_error(struct _cef_audio_handler_t* self,
+                                    struct _cef_browser_t* browser,
+                                    const cef_string_t* message) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: message; type: string_byref_const
+  DCHECK(message);
+  if (!message)
+    return;
+
+  // Execute
+  CefAudioHandlerCppToC::Get(self)->OnAudioStreamError(
+      CefBrowserCToCpp::Wrap(browser), CefString(message));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAudioHandlerCppToC::CefAudioHandlerCppToC() {
+  GetStruct()->get_audio_parameters = audio_handler_get_audio_parameters;
+  GetStruct()->on_audio_stream_started = audio_handler_on_audio_stream_started;
+  GetStruct()->on_audio_stream_packet = audio_handler_on_audio_stream_packet;
+  GetStruct()->on_audio_stream_stopped = audio_handler_on_audio_stream_stopped;
+  GetStruct()->on_audio_stream_error = audio_handler_on_audio_stream_error;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAudioHandlerCppToC::~CefAudioHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefAudioHandler> CefCppToCRefCounted<
+    CefAudioHandlerCppToC,
+    CefAudioHandler,
+    cef_audio_handler_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_audio_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefAudioHandlerCppToC,
+                                   CefAudioHandler,
+                                   cef_audio_handler_t>::kWrapperType =
+    WT_AUDIO_HANDLER;
diff --git a/src/libcef_dll/cpptoc/audio_handler_cpptoc.h b/src/libcef_dll/cpptoc/audio_handler_cpptoc.h
new file mode 100644
index 0000000..b4778c8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/audio_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=27689a3c353f267fb650ec5b7dc095e0a6be8b13$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_AUDIO_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_AUDIO_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_audio_handler_capi.h"
+#include "include/cef_audio_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefAudioHandlerCppToC : public CefCppToCRefCounted<CefAudioHandlerCppToC,
+                                                         CefAudioHandler,
+                                                         cef_audio_handler_t> {
+ public:
+  CefAudioHandlerCppToC();
+  virtual ~CefAudioHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_AUDIO_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/auth_callback_cpptoc.cc b/src/libcef_dll/cpptoc/auth_callback_cpptoc.cc
new file mode 100644
index 0000000..784d851
--- /dev/null
+++ b/src/libcef_dll/cpptoc/auth_callback_cpptoc.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=921314be850e42ffee64ca025993a732949bf123$
+//
+
+#include "libcef_dll/cpptoc/auth_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK auth_callback_cont(struct _cef_auth_callback_t* self,
+                                     const cef_string_t* username,
+                                     const cef_string_t* password) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: username, password
+
+  // Execute
+  CefAuthCallbackCppToC::Get(self)->Continue(CefString(username),
+                                             CefString(password));
+}
+
+void CEF_CALLBACK auth_callback_cancel(struct _cef_auth_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefAuthCallbackCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAuthCallbackCppToC::CefAuthCallbackCppToC() {
+  GetStruct()->cont = auth_callback_cont;
+  GetStruct()->cancel = auth_callback_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAuthCallbackCppToC::~CefAuthCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefAuthCallback> CefCppToCRefCounted<
+    CefAuthCallbackCppToC,
+    CefAuthCallback,
+    cef_auth_callback_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_auth_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefAuthCallbackCppToC,
+                                   CefAuthCallback,
+                                   cef_auth_callback_t>::kWrapperType =
+    WT_AUTH_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/auth_callback_cpptoc.h b/src/libcef_dll/cpptoc/auth_callback_cpptoc.h
new file mode 100644
index 0000000..860ee3c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/auth_callback_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=20fac721ac760ab05005f456bb3f08d85aef1d80$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_AUTH_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_AUTH_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_auth_callback_capi.h"
+#include "include/cef_auth_callback.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefAuthCallbackCppToC : public CefCppToCRefCounted<CefAuthCallbackCppToC,
+                                                         CefAuthCallback,
+                                                         cef_auth_callback_t> {
+ public:
+  CefAuthCallbackCppToC();
+  virtual ~CefAuthCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_AUTH_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/base_ref_counted_cpptoc.cc b/src/libcef_dll/cpptoc/base_ref_counted_cpptoc.cc
new file mode 100644
index 0000000..78d7e95
--- /dev/null
+++ b/src/libcef_dll/cpptoc/base_ref_counted_cpptoc.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef_dll/cpptoc/base_ref_counted_cpptoc.h"
+
+CefBaseRefCountedCppToC::CefBaseRefCountedCppToC() {}
+
+template <>
+CefRefPtr<CefBaseRefCounted> CefCppToCRefCounted<
+    CefBaseRefCountedCppToC,
+    CefBaseRefCounted,
+    cef_base_ref_counted_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_base_ref_counted_t* s) {
+  NOTREACHED();
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefBaseRefCountedCppToC,
+                                   CefBaseRefCounted,
+                                   cef_base_ref_counted_t>::kWrapperType =
+    WT_BASE_REF_COUNTED;
diff --git a/src/libcef_dll/cpptoc/base_ref_counted_cpptoc.h b/src/libcef_dll/cpptoc/base_ref_counted_cpptoc.h
new file mode 100644
index 0000000..a8e1eb1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/base_ref_counted_cpptoc.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_BASE_REF_COUNTED_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_BASE_REF_COUNTED_CPPTOC_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+// Wrap a C++ class with a C structure.
+class CefBaseRefCountedCppToC
+    : public CefCppToCRefCounted<CefBaseRefCountedCppToC,
+                                 CefBaseRefCounted,
+                                 cef_base_ref_counted_t> {
+ public:
+  CefBaseRefCountedCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_BASE_REF_COUNTED_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/base_scoped_cpptoc.cc b/src/libcef_dll/cpptoc/base_scoped_cpptoc.cc
new file mode 100644
index 0000000..02387ca
--- /dev/null
+++ b/src/libcef_dll/cpptoc/base_scoped_cpptoc.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef_dll/cpptoc/base_scoped_cpptoc.h"
+
+CefBaseScopedCppToC::CefBaseScopedCppToC() {}
+
+template <>
+CefOwnPtr<CefBaseScoped>
+CefCppToCScoped<CefBaseScopedCppToC, CefBaseScoped, cef_base_scoped_t>::
+    UnwrapDerivedOwn(CefWrapperType type, cef_base_scoped_t* s) {
+  NOTREACHED();
+  return CefOwnPtr<CefBaseScoped>();
+}
+
+template <>
+CefRawPtr<CefBaseScoped>
+CefCppToCScoped<CefBaseScopedCppToC, CefBaseScoped, cef_base_scoped_t>::
+    UnwrapDerivedRaw(CefWrapperType type, cef_base_scoped_t* s) {
+  NOTREACHED();
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCScoped<CefBaseScopedCppToC,
+                               CefBaseScoped,
+                               cef_base_scoped_t>::kWrapperType =
+    WT_BASE_SCOPED;
diff --git a/src/libcef_dll/cpptoc/base_scoped_cpptoc.h b/src/libcef_dll/cpptoc/base_scoped_cpptoc.h
new file mode 100644
index 0000000..63a6688
--- /dev/null
+++ b/src/libcef_dll/cpptoc/base_scoped_cpptoc.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_BASE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_BASE_CPPTOC_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/cpptoc/cpptoc_scoped.h"
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+// Wrap a C++ class with a C structure.
+class CefBaseScopedCppToC : public CefCppToCScoped<CefBaseScopedCppToC,
+                                                   CefBaseScoped,
+                                                   cef_base_scoped_t> {
+ public:
+  CefBaseScopedCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_BASE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/before_download_callback_cpptoc.cc b/src/libcef_dll/cpptoc/before_download_callback_cpptoc.cc
new file mode 100644
index 0000000..93c6095
--- /dev/null
+++ b/src/libcef_dll/cpptoc/before_download_callback_cpptoc.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e353aad49690f92eaa2771a7d4293d8c41a75d5c$
+//
+
+#include "libcef_dll/cpptoc/before_download_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+before_download_callback_cont(struct _cef_before_download_callback_t* self,
+                              const cef_string_t* download_path,
+                              int show_dialog) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: download_path
+
+  // Execute
+  CefBeforeDownloadCallbackCppToC::Get(self)->Continue(
+      CefString(download_path), show_dialog ? true : false);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBeforeDownloadCallbackCppToC::CefBeforeDownloadCallbackCppToC() {
+  GetStruct()->cont = before_download_callback_cont;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBeforeDownloadCallbackCppToC::~CefBeforeDownloadCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefBeforeDownloadCallback>
+CefCppToCRefCounted<CefBeforeDownloadCallbackCppToC,
+                    CefBeforeDownloadCallback,
+                    cef_before_download_callback_t>::
+    UnwrapDerived(CefWrapperType type, cef_before_download_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefBeforeDownloadCallbackCppToC,
+                        CefBeforeDownloadCallback,
+                        cef_before_download_callback_t>::kWrapperType =
+        WT_BEFORE_DOWNLOAD_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/before_download_callback_cpptoc.h b/src/libcef_dll/cpptoc/before_download_callback_cpptoc.h
new file mode 100644
index 0000000..bdeac01
--- /dev/null
+++ b/src/libcef_dll/cpptoc/before_download_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0193ffd9be77a8b1af71f28e0a0ba12a88a2be91$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_BEFORE_DOWNLOAD_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_BEFORE_DOWNLOAD_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_download_handler_capi.h"
+#include "include/cef_download_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefBeforeDownloadCallbackCppToC
+    : public CefCppToCRefCounted<CefBeforeDownloadCallbackCppToC,
+                                 CefBeforeDownloadCallback,
+                                 cef_before_download_callback_t> {
+ public:
+  CefBeforeDownloadCallbackCppToC();
+  virtual ~CefBeforeDownloadCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_BEFORE_DOWNLOAD_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/binary_value_cpptoc.cc b/src/libcef_dll/cpptoc/binary_value_cpptoc.cc
new file mode 100644
index 0000000..276aefd
--- /dev/null
+++ b/src/libcef_dll/cpptoc/binary_value_cpptoc.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d83e342a1d94a14e39b1eba174e0b8892fd52728$
+//
+
+#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_binary_value_t* cef_binary_value_create(const void* data,
+                                                       size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval = CefBinaryValue::Create(data, data_size);
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK binary_value_is_valid(struct _cef_binary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBinaryValueCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK binary_value_is_owned(struct _cef_binary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBinaryValueCppToC::Get(self)->IsOwned();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK binary_value_is_same(struct _cef_binary_value_t* self,
+                                      struct _cef_binary_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefBinaryValueCppToC::Get(self)->IsSame(
+      CefBinaryValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK binary_value_is_equal(struct _cef_binary_value_t* self,
+                                       struct _cef_binary_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefBinaryValueCppToC::Get(self)->IsEqual(
+      CefBinaryValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_binary_value_t* CEF_CALLBACK
+binary_value_copy(struct _cef_binary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval = CefBinaryValueCppToC::Get(self)->Copy();
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+size_t CEF_CALLBACK binary_value_get_size(struct _cef_binary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefBinaryValueCppToC::Get(self)->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+size_t CEF_CALLBACK binary_value_get_data(struct _cef_binary_value_t* self,
+                                          void* buffer,
+                                          size_t buffer_size,
+                                          size_t data_offset) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return 0;
+
+  // Execute
+  size_t _retval = CefBinaryValueCppToC::Get(self)->GetData(buffer, buffer_size,
+                                                            data_offset);
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBinaryValueCppToC::CefBinaryValueCppToC() {
+  GetStruct()->is_valid = binary_value_is_valid;
+  GetStruct()->is_owned = binary_value_is_owned;
+  GetStruct()->is_same = binary_value_is_same;
+  GetStruct()->is_equal = binary_value_is_equal;
+  GetStruct()->copy = binary_value_copy;
+  GetStruct()->get_size = binary_value_get_size;
+  GetStruct()->get_data = binary_value_get_data;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBinaryValueCppToC::~CefBinaryValueCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefBinaryValue>
+CefCppToCRefCounted<CefBinaryValueCppToC, CefBinaryValue, cef_binary_value_t>::
+    UnwrapDerived(CefWrapperType type, cef_binary_value_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefBinaryValueCppToC,
+                                   CefBinaryValue,
+                                   cef_binary_value_t>::kWrapperType =
+    WT_BINARY_VALUE;
diff --git a/src/libcef_dll/cpptoc/binary_value_cpptoc.h b/src/libcef_dll/cpptoc/binary_value_cpptoc.h
new file mode 100644
index 0000000..9cff59c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/binary_value_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fccbd66b11f9f798fd1330b586d6172478a0cf81$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_BINARY_VALUE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_BINARY_VALUE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefBinaryValueCppToC : public CefCppToCRefCounted<CefBinaryValueCppToC,
+                                                        CefBinaryValue,
+                                                        cef_binary_value_t> {
+ public:
+  CefBinaryValueCppToC();
+  virtual ~CefBinaryValueCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_BINARY_VALUE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/browser_cpptoc.cc b/src/libcef_dll/cpptoc/browser_cpptoc.cc
new file mode 100644
index 0000000..f036cc5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/browser_cpptoc.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=48f8372a4b5104ce459b4aedf625c3cfd221d017$
+//
+
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include <algorithm>
+#include "libcef_dll/cpptoc/browser_host_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+struct _cef_browser_host_t* CEF_CALLBACK
+browser_get_host(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserHost> _retval = CefBrowserCppToC::Get(self)->GetHost();
+
+  // Return type: refptr_same
+  return CefBrowserHostCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK browser_can_go_back(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserCppToC::Get(self)->CanGoBack();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_go_back(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserCppToC::Get(self)->GoBack();
+}
+
+int CEF_CALLBACK browser_can_go_forward(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserCppToC::Get(self)->CanGoForward();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_go_forward(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserCppToC::Get(self)->GoForward();
+}
+
+int CEF_CALLBACK browser_is_loading(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserCppToC::Get(self)->IsLoading();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_reload(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserCppToC::Get(self)->Reload();
+}
+
+void CEF_CALLBACK browser_reload_ignore_cache(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserCppToC::Get(self)->ReloadIgnoreCache();
+}
+
+void CEF_CALLBACK browser_stop_load(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserCppToC::Get(self)->StopLoad();
+}
+
+int CEF_CALLBACK browser_get_identifier(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefBrowserCppToC::Get(self)->GetIdentifier();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK browser_is_same(struct _cef_browser_t* self,
+                                 struct _cef_browser_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserCppToC::Get(self)->IsSame(CefBrowserCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_is_popup(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserCppToC::Get(self)->IsPopup();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_has_document(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserCppToC::Get(self)->HasDocument();
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_frame_t* CEF_CALLBACK
+browser_get_main_frame(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFrame> _retval = CefBrowserCppToC::Get(self)->GetMainFrame();
+
+  // Return type: refptr_same
+  return CefFrameCppToC::Wrap(_retval);
+}
+
+struct _cef_frame_t* CEF_CALLBACK
+browser_get_focused_frame(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFrame> _retval = CefBrowserCppToC::Get(self)->GetFocusedFrame();
+
+  // Return type: refptr_same
+  return CefFrameCppToC::Wrap(_retval);
+}
+
+struct _cef_frame_t* CEF_CALLBACK
+browser_get_frame_byident(struct _cef_browser_t* self, int64 identifier) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFrame> _retval =
+      CefBrowserCppToC::Get(self)->GetFrame(identifier);
+
+  // Return type: refptr_same
+  return CefFrameCppToC::Wrap(_retval);
+}
+
+struct _cef_frame_t* CEF_CALLBACK browser_get_frame(struct _cef_browser_t* self,
+                                                    const cef_string_t* name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Unverified params: name
+
+  // Execute
+  CefRefPtr<CefFrame> _retval =
+      CefBrowserCppToC::Get(self)->GetFrame(CefString(name));
+
+  // Return type: refptr_same
+  return CefFrameCppToC::Wrap(_retval);
+}
+
+size_t CEF_CALLBACK browser_get_frame_count(struct _cef_browser_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefBrowserCppToC::Get(self)->GetFrameCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK browser_get_frame_identifiers(struct _cef_browser_t* self,
+                                                size_t* identifiersCount,
+                                                int64* identifiers) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: identifiers; type: simple_vec_byref
+  DCHECK(identifiersCount && (*identifiersCount == 0 || identifiers));
+  if (!identifiersCount || (*identifiersCount > 0 && !identifiers))
+    return;
+
+  // Translate param: identifiers; type: simple_vec_byref
+  std::vector<int64> identifiersList;
+  if (identifiersCount && *identifiersCount > 0 && identifiers) {
+    for (size_t i = 0; i < *identifiersCount; ++i) {
+      identifiersList.push_back(identifiers[i]);
+    }
+  }
+
+  // Execute
+  CefBrowserCppToC::Get(self)->GetFrameIdentifiers(identifiersList);
+
+  // Restore param: identifiers; type: simple_vec_byref
+  if (identifiersCount && identifiers) {
+    *identifiersCount = std::min(identifiersList.size(), *identifiersCount);
+    if (*identifiersCount > 0) {
+      for (size_t i = 0; i < *identifiersCount; ++i) {
+        identifiers[i] = identifiersList[i];
+      }
+    }
+  }
+}
+
+void CEF_CALLBACK browser_get_frame_names(struct _cef_browser_t* self,
+                                          cef_string_list_t names) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: names; type: string_vec_byref
+  DCHECK(names);
+  if (!names)
+    return;
+
+  // Translate param: names; type: string_vec_byref
+  std::vector<CefString> namesList;
+  transfer_string_list_contents(names, namesList);
+
+  // Execute
+  CefBrowserCppToC::Get(self)->GetFrameNames(namesList);
+
+  // Restore param: names; type: string_vec_byref
+  cef_string_list_clear(names);
+  transfer_string_list_contents(namesList, names);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserCppToC::CefBrowserCppToC() {
+  GetStruct()->get_host = browser_get_host;
+  GetStruct()->can_go_back = browser_can_go_back;
+  GetStruct()->go_back = browser_go_back;
+  GetStruct()->can_go_forward = browser_can_go_forward;
+  GetStruct()->go_forward = browser_go_forward;
+  GetStruct()->is_loading = browser_is_loading;
+  GetStruct()->reload = browser_reload;
+  GetStruct()->reload_ignore_cache = browser_reload_ignore_cache;
+  GetStruct()->stop_load = browser_stop_load;
+  GetStruct()->get_identifier = browser_get_identifier;
+  GetStruct()->is_same = browser_is_same;
+  GetStruct()->is_popup = browser_is_popup;
+  GetStruct()->has_document = browser_has_document;
+  GetStruct()->get_main_frame = browser_get_main_frame;
+  GetStruct()->get_focused_frame = browser_get_focused_frame;
+  GetStruct()->get_frame_byident = browser_get_frame_byident;
+  GetStruct()->get_frame = browser_get_frame;
+  GetStruct()->get_frame_count = browser_get_frame_count;
+  GetStruct()->get_frame_identifiers = browser_get_frame_identifiers;
+  GetStruct()->get_frame_names = browser_get_frame_names;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserCppToC::~CefBrowserCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefBrowser>
+CefCppToCRefCounted<CefBrowserCppToC, CefBrowser, cef_browser_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_browser_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefBrowserCppToC,
+                                   CefBrowser,
+                                   cef_browser_t>::kWrapperType = WT_BROWSER;
diff --git a/src/libcef_dll/cpptoc/browser_cpptoc.h b/src/libcef_dll/cpptoc/browser_cpptoc.h
new file mode 100644
index 0000000..3e20603
--- /dev/null
+++ b/src/libcef_dll/cpptoc/browser_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fe511e2e092abc4f91a953606be5872a7395a732$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_BROWSER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_BROWSER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefBrowserCppToC
+    : public CefCppToCRefCounted<CefBrowserCppToC, CefBrowser, cef_browser_t> {
+ public:
+  CefBrowserCppToC();
+  virtual ~CefBrowserCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_BROWSER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/browser_host_cpptoc.cc b/src/libcef_dll/cpptoc/browser_host_cpptoc.cc
new file mode 100644
index 0000000..124782d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/browser_host_cpptoc.cc
@@ -0,0 +1,1438 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=516b55b7ea53e2de2b096e85ba0eb83f2a2693f3$
+//
+
+#include "libcef_dll/cpptoc/browser_host_cpptoc.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/drag_data_cpptoc.h"
+#include "libcef_dll/cpptoc/extension_cpptoc.h"
+#include "libcef_dll/cpptoc/navigation_entry_cpptoc.h"
+#include "libcef_dll/cpptoc/registration_cpptoc.h"
+#include "libcef_dll/cpptoc/request_context_cpptoc.h"
+#include "libcef_dll/ctocpp/client_ctocpp.h"
+#include "libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h"
+#include "libcef_dll/ctocpp/download_image_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h"
+#include "libcef_dll/ctocpp/pdf_print_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT int cef_browser_host_create_browser(
+    const cef_window_info_t* windowInfo,
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: windowInfo; type: struct_byref_const
+  DCHECK(windowInfo);
+  if (!windowInfo)
+    return 0;
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return 0;
+  // Unverified params: client, url, extra_info, request_context
+
+  // Translate param: windowInfo; type: struct_byref_const
+  CefWindowInfo windowInfoObj;
+  if (windowInfo)
+    windowInfoObj.Set(*windowInfo, false);
+  // Translate param: settings; type: struct_byref_const
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  bool _retval = CefBrowserHost::CreateBrowser(
+      windowInfoObj, CefClientCToCpp::Wrap(client), CefString(url), settingsObj,
+      CefDictionaryValueCppToC::Unwrap(extra_info),
+      CefRequestContextCppToC::Unwrap(request_context));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT cef_browser_t* cef_browser_host_create_browser_sync(
+    const cef_window_info_t* windowInfo,
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: windowInfo; type: struct_byref_const
+  DCHECK(windowInfo);
+  if (!windowInfo)
+    return NULL;
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return NULL;
+  // Unverified params: client, url, extra_info, request_context
+
+  // Translate param: windowInfo; type: struct_byref_const
+  CefWindowInfo windowInfoObj;
+  if (windowInfo)
+    windowInfoObj.Set(*windowInfo, false);
+  // Translate param: settings; type: struct_byref_const
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  CefRefPtr<CefBrowser> _retval = CefBrowserHost::CreateBrowserSync(
+      windowInfoObj, CefClientCToCpp::Wrap(client), CefString(url), settingsObj,
+      CefDictionaryValueCppToC::Unwrap(extra_info),
+      CefRequestContextCppToC::Unwrap(request_context));
+
+  // Return type: refptr_same
+  return CefBrowserCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_browser_t* CEF_CALLBACK
+browser_host_get_browser(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowser> _retval = CefBrowserHostCppToC::Get(self)->GetBrowser();
+
+  // Return type: refptr_same
+  return CefBrowserCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK browser_host_close_browser(struct _cef_browser_host_t* self,
+                                             int force_close) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->CloseBrowser(force_close ? true : false);
+}
+
+int CEF_CALLBACK
+browser_host_try_close_browser(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->TryCloseBrowser();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_host_set_focus(struct _cef_browser_host_t* self,
+                                         int focus) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SetFocus(focus ? true : false);
+}
+
+cef_window_handle_t CEF_CALLBACK
+browser_host_get_window_handle(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return kNullWindowHandle;
+
+  // Execute
+  cef_window_handle_t _retval =
+      CefBrowserHostCppToC::Get(self)->GetWindowHandle();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_window_handle_t CEF_CALLBACK
+browser_host_get_opener_window_handle(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return kNullWindowHandle;
+
+  // Execute
+  cef_window_handle_t _retval =
+      CefBrowserHostCppToC::Get(self)->GetOpenerWindowHandle();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK browser_host_has_view(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->HasView();
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_client_t* CEF_CALLBACK
+browser_host_get_client(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefClient> _retval = CefBrowserHostCppToC::Get(self)->GetClient();
+
+  // Return type: refptr_diff
+  return CefClientCToCpp::Unwrap(_retval);
+}
+
+struct _cef_request_context_t* CEF_CALLBACK
+browser_host_get_request_context(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRequestContext> _retval =
+      CefBrowserHostCppToC::Get(self)->GetRequestContext();
+
+  // Return type: refptr_same
+  return CefRequestContextCppToC::Wrap(_retval);
+}
+
+double CEF_CALLBACK
+browser_host_get_zoom_level(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  double _retval = CefBrowserHostCppToC::Get(self)->GetZoomLevel();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK browser_host_set_zoom_level(struct _cef_browser_host_t* self,
+                                              double zoomLevel) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SetZoomLevel(zoomLevel);
+}
+
+void CEF_CALLBACK
+browser_host_run_file_dialog(struct _cef_browser_host_t* self,
+                             cef_file_dialog_mode_t mode,
+                             const cef_string_t* title,
+                             const cef_string_t* default_file_path,
+                             cef_string_list_t accept_filters,
+                             int selected_accept_filter,
+                             cef_run_file_dialog_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+  // Unverified params: title, default_file_path, accept_filters
+
+  // Translate param: accept_filters; type: string_vec_byref_const
+  std::vector<CefString> accept_filtersList;
+  transfer_string_list_contents(accept_filters, accept_filtersList);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->RunFileDialog(
+      mode, CefString(title), CefString(default_file_path), accept_filtersList,
+      selected_accept_filter, CefRunFileDialogCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK browser_host_start_download(struct _cef_browser_host_t* self,
+                                              const cef_string_t* url) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->StartDownload(CefString(url));
+}
+
+void CEF_CALLBACK
+browser_host_download_image(struct _cef_browser_host_t* self,
+                            const cef_string_t* image_url,
+                            int is_favicon,
+                            uint32 max_image_size,
+                            int bypass_cache,
+                            cef_download_image_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: image_url; type: string_byref_const
+  DCHECK(image_url);
+  if (!image_url)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->DownloadImage(
+      CefString(image_url), is_favicon ? true : false, max_image_size,
+      bypass_cache ? true : false,
+      CefDownloadImageCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK browser_host_print(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->Print();
+}
+
+void CEF_CALLBACK
+browser_host_print_to_pdf(struct _cef_browser_host_t* self,
+                          const cef_string_t* path,
+                          const struct _cef_pdf_print_settings_t* settings,
+                          cef_pdf_print_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return;
+  // Unverified params: callback
+
+  // Translate param: settings; type: struct_byref_const
+  CefPdfPrintSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->PrintToPDF(
+      CefString(path), settingsObj, CefPdfPrintCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK browser_host_find(struct _cef_browser_host_t* self,
+                                    int identifier,
+                                    const cef_string_t* searchText,
+                                    int forward,
+                                    int matchCase,
+                                    int findNext) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: searchText; type: string_byref_const
+  DCHECK(searchText);
+  if (!searchText)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->Find(
+      identifier, CefString(searchText), forward ? true : false,
+      matchCase ? true : false, findNext ? true : false);
+}
+
+void CEF_CALLBACK browser_host_stop_finding(struct _cef_browser_host_t* self,
+                                            int clearSelection) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->StopFinding(clearSelection ? true : false);
+}
+
+void CEF_CALLBACK
+browser_host_show_dev_tools(struct _cef_browser_host_t* self,
+                            const cef_window_info_t* windowInfo,
+                            struct _cef_client_t* client,
+                            const struct _cef_browser_settings_t* settings,
+                            const cef_point_t* inspect_element_at) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: windowInfo, client, settings, inspect_element_at
+
+  // Translate param: windowInfo; type: struct_byref_const
+  CefWindowInfo windowInfoObj;
+  if (windowInfo)
+    windowInfoObj.Set(*windowInfo, false);
+  // Translate param: settings; type: struct_byref_const
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+  // Translate param: inspect_element_at; type: simple_byref_const
+  CefPoint inspect_element_atVal =
+      inspect_element_at ? *inspect_element_at : CefPoint();
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->ShowDevTools(
+      windowInfoObj, CefClientCToCpp::Wrap(client), settingsObj,
+      inspect_element_atVal);
+}
+
+void CEF_CALLBACK
+browser_host_close_dev_tools(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->CloseDevTools();
+}
+
+int CEF_CALLBACK browser_host_has_dev_tools(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->HasDevTools();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+browser_host_send_dev_tools_message(struct _cef_browser_host_t* self,
+                                    const void* message,
+                                    size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->SendDevToolsMessage(
+      message, message_size);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+browser_host_execute_dev_tools_method(struct _cef_browser_host_t* self,
+                                      int message_id,
+                                      const cef_string_t* method,
+                                      struct _cef_dictionary_value_t* params) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: method; type: string_byref_const
+  DCHECK(method);
+  if (!method)
+    return 0;
+  // Unverified params: params
+
+  // Execute
+  int _retval = CefBrowserHostCppToC::Get(self)->ExecuteDevToolsMethod(
+      message_id, CefString(method), CefDictionaryValueCppToC::Unwrap(params));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_registration_t* CEF_CALLBACK
+browser_host_add_dev_tools_message_observer(
+    struct _cef_browser_host_t* self,
+    struct _cef_dev_tools_message_observer_t* observer) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: observer; type: refptr_diff
+  DCHECK(observer);
+  if (!observer)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRegistration> _retval =
+      CefBrowserHostCppToC::Get(self)->AddDevToolsMessageObserver(
+          CefDevToolsMessageObserverCToCpp::Wrap(observer));
+
+  // Return type: refptr_same
+  return CefRegistrationCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK
+browser_host_get_navigation_entries(struct _cef_browser_host_t* self,
+                                    cef_navigation_entry_visitor_t* visitor,
+                                    int current_only) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor);
+  if (!visitor)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->GetNavigationEntries(
+      CefNavigationEntryVisitorCToCpp::Wrap(visitor),
+      current_only ? true : false);
+}
+
+void CEF_CALLBACK
+browser_host_set_mouse_cursor_change_disabled(struct _cef_browser_host_t* self,
+                                              int disabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SetMouseCursorChangeDisabled(
+      disabled ? true : false);
+}
+
+int CEF_CALLBACK
+browser_host_is_mouse_cursor_change_disabled(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->IsMouseCursorChangeDisabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+browser_host_replace_misspelling(struct _cef_browser_host_t* self,
+                                 const cef_string_t* word) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: word; type: string_byref_const
+  DCHECK(word);
+  if (!word)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->ReplaceMisspelling(CefString(word));
+}
+
+void CEF_CALLBACK
+browser_host_add_word_to_dictionary(struct _cef_browser_host_t* self,
+                                    const cef_string_t* word) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: word; type: string_byref_const
+  DCHECK(word);
+  if (!word)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->AddWordToDictionary(CefString(word));
+}
+
+int CEF_CALLBACK
+browser_host_is_window_rendering_disabled(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->IsWindowRenderingDisabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_host_was_resized(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->WasResized();
+}
+
+void CEF_CALLBACK browser_host_was_hidden(struct _cef_browser_host_t* self,
+                                          int hidden) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->WasHidden(hidden ? true : false);
+}
+
+void CEF_CALLBACK
+browser_host_notify_screen_info_changed(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->NotifyScreenInfoChanged();
+}
+
+void CEF_CALLBACK browser_host_invalidate(struct _cef_browser_host_t* self,
+                                          cef_paint_element_type_t type) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->Invalidate(type);
+}
+
+void CEF_CALLBACK
+browser_host_send_external_begin_frame(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendExternalBeginFrame();
+}
+
+void CEF_CALLBACK
+browser_host_send_key_event(struct _cef_browser_host_t* self,
+                            const struct _cef_key_event_t* event) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefKeyEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendKeyEvent(eventObj);
+}
+
+void CEF_CALLBACK
+browser_host_send_mouse_click_event(struct _cef_browser_host_t* self,
+                                    const struct _cef_mouse_event_t* event,
+                                    cef_mouse_button_type_t type,
+                                    int mouseUp,
+                                    int clickCount) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefMouseEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendMouseClickEvent(
+      eventObj, type, mouseUp ? true : false, clickCount);
+}
+
+void CEF_CALLBACK
+browser_host_send_mouse_move_event(struct _cef_browser_host_t* self,
+                                   const struct _cef_mouse_event_t* event,
+                                   int mouseLeave) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefMouseEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendMouseMoveEvent(
+      eventObj, mouseLeave ? true : false);
+}
+
+void CEF_CALLBACK
+browser_host_send_mouse_wheel_event(struct _cef_browser_host_t* self,
+                                    const struct _cef_mouse_event_t* event,
+                                    int deltaX,
+                                    int deltaY) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefMouseEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendMouseWheelEvent(eventObj, deltaX,
+                                                       deltaY);
+}
+
+void CEF_CALLBACK
+browser_host_send_touch_event(struct _cef_browser_host_t* self,
+                              const struct _cef_touch_event_t* event) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefTouchEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendTouchEvent(eventObj);
+}
+
+void CEF_CALLBACK
+browser_host_send_focus_event(struct _cef_browser_host_t* self, int setFocus) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendFocusEvent(setFocus ? true : false);
+}
+
+void CEF_CALLBACK
+browser_host_send_capture_lost_event(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SendCaptureLostEvent();
+}
+
+void CEF_CALLBACK
+browser_host_notify_move_or_resize_started(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->NotifyMoveOrResizeStarted();
+}
+
+int CEF_CALLBACK
+browser_host_get_windowless_frame_rate(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefBrowserHostCppToC::Get(self)->GetWindowlessFrameRate();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+browser_host_set_windowless_frame_rate(struct _cef_browser_host_t* self,
+                                       int frame_rate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SetWindowlessFrameRate(frame_rate);
+}
+
+void CEF_CALLBACK
+browser_host_ime_set_composition(struct _cef_browser_host_t* self,
+                                 const cef_string_t* text,
+                                 size_t underlinesCount,
+                                 cef_composition_underline_t const* underlines,
+                                 const cef_range_t* replacement_range,
+                                 const cef_range_t* selection_range) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: replacement_range; type: simple_byref_const
+  DCHECK(replacement_range);
+  if (!replacement_range)
+    return;
+  // Verify param: selection_range; type: simple_byref_const
+  DCHECK(selection_range);
+  if (!selection_range)
+    return;
+  // Unverified params: text, underlines
+
+  // Translate param: underlines; type: simple_vec_byref_const
+  std::vector<CefCompositionUnderline> underlinesList;
+  if (underlinesCount > 0) {
+    for (size_t i = 0; i < underlinesCount; ++i) {
+      CefCompositionUnderline underlinesVal = underlines[i];
+      underlinesList.push_back(underlinesVal);
+    }
+  }
+  // Translate param: replacement_range; type: simple_byref_const
+  CefRange replacement_rangeVal =
+      replacement_range ? *replacement_range : CefRange();
+  // Translate param: selection_range; type: simple_byref_const
+  CefRange selection_rangeVal = selection_range ? *selection_range : CefRange();
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->ImeSetComposition(
+      CefString(text), underlinesList, replacement_rangeVal,
+      selection_rangeVal);
+}
+
+void CEF_CALLBACK
+browser_host_ime_commit_text(struct _cef_browser_host_t* self,
+                             const cef_string_t* text,
+                             const cef_range_t* replacement_range,
+                             int relative_cursor_pos) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: replacement_range; type: simple_byref_const
+  DCHECK(replacement_range);
+  if (!replacement_range)
+    return;
+  // Unverified params: text
+
+  // Translate param: replacement_range; type: simple_byref_const
+  CefRange replacement_rangeVal =
+      replacement_range ? *replacement_range : CefRange();
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->ImeCommitText(
+      CefString(text), replacement_rangeVal, relative_cursor_pos);
+}
+
+void CEF_CALLBACK
+browser_host_ime_finish_composing_text(struct _cef_browser_host_t* self,
+                                       int keep_selection) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->ImeFinishComposingText(
+      keep_selection ? true : false);
+}
+
+void CEF_CALLBACK
+browser_host_ime_cancel_composition(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->ImeCancelComposition();
+}
+
+void CEF_CALLBACK
+browser_host_drag_target_drag_enter(struct _cef_browser_host_t* self,
+                                    struct _cef_drag_data_t* drag_data,
+                                    const struct _cef_mouse_event_t* event,
+                                    cef_drag_operations_mask_t allowed_ops) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: drag_data; type: refptr_same
+  DCHECK(drag_data);
+  if (!drag_data)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefMouseEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->DragTargetDragEnter(
+      CefDragDataCppToC::Unwrap(drag_data), eventObj, allowed_ops);
+}
+
+void CEF_CALLBACK
+browser_host_drag_target_drag_over(struct _cef_browser_host_t* self,
+                                   const struct _cef_mouse_event_t* event,
+                                   cef_drag_operations_mask_t allowed_ops) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefMouseEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->DragTargetDragOver(eventObj, allowed_ops);
+}
+
+void CEF_CALLBACK
+browser_host_drag_target_drag_leave(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->DragTargetDragLeave();
+}
+
+void CEF_CALLBACK
+browser_host_drag_target_drop(struct _cef_browser_host_t* self,
+                              const struct _cef_mouse_event_t* event) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return;
+
+  // Translate param: event; type: struct_byref_const
+  CefMouseEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->DragTargetDrop(eventObj);
+}
+
+void CEF_CALLBACK
+browser_host_drag_source_ended_at(struct _cef_browser_host_t* self,
+                                  int x,
+                                  int y,
+                                  cef_drag_operations_mask_t op) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->DragSourceEndedAt(x, y, op);
+}
+
+void CEF_CALLBACK
+browser_host_drag_source_system_drag_ended(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->DragSourceSystemDragEnded();
+}
+
+struct _cef_navigation_entry_t* CEF_CALLBACK
+browser_host_get_visible_navigation_entry(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefNavigationEntry> _retval =
+      CefBrowserHostCppToC::Get(self)->GetVisibleNavigationEntry();
+
+  // Return type: refptr_same
+  return CefNavigationEntryCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK
+browser_host_set_accessibility_state(struct _cef_browser_host_t* self,
+                                     cef_state_t accessibility_state) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SetAccessibilityState(accessibility_state);
+}
+
+void CEF_CALLBACK
+browser_host_set_auto_resize_enabled(struct _cef_browser_host_t* self,
+                                     int enabled,
+                                     const cef_size_t* min_size,
+                                     const cef_size_t* max_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: min_size; type: simple_byref_const
+  DCHECK(min_size);
+  if (!min_size)
+    return;
+  // Verify param: max_size; type: simple_byref_const
+  DCHECK(max_size);
+  if (!max_size)
+    return;
+
+  // Translate param: min_size; type: simple_byref_const
+  CefSize min_sizeVal = min_size ? *min_size : CefSize();
+  // Translate param: max_size; type: simple_byref_const
+  CefSize max_sizeVal = max_size ? *max_size : CefSize();
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SetAutoResizeEnabled(
+      enabled ? true : false, min_sizeVal, max_sizeVal);
+}
+
+struct _cef_extension_t* CEF_CALLBACK
+browser_host_get_extension(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefExtension> _retval =
+      CefBrowserHostCppToC::Get(self)->GetExtension();
+
+  // Return type: refptr_same
+  return CefExtensionCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK
+browser_host_is_background_host(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->IsBackgroundHost();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_host_set_audio_muted(struct _cef_browser_host_t* self,
+                                               int mute) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserHostCppToC::Get(self)->SetAudioMuted(mute ? true : false);
+}
+
+int CEF_CALLBACK browser_host_is_audio_muted(struct _cef_browser_host_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefBrowserHostCppToC::Get(self)->IsAudioMuted();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserHostCppToC::CefBrowserHostCppToC() {
+  GetStruct()->get_browser = browser_host_get_browser;
+  GetStruct()->close_browser = browser_host_close_browser;
+  GetStruct()->try_close_browser = browser_host_try_close_browser;
+  GetStruct()->set_focus = browser_host_set_focus;
+  GetStruct()->get_window_handle = browser_host_get_window_handle;
+  GetStruct()->get_opener_window_handle = browser_host_get_opener_window_handle;
+  GetStruct()->has_view = browser_host_has_view;
+  GetStruct()->get_client = browser_host_get_client;
+  GetStruct()->get_request_context = browser_host_get_request_context;
+  GetStruct()->get_zoom_level = browser_host_get_zoom_level;
+  GetStruct()->set_zoom_level = browser_host_set_zoom_level;
+  GetStruct()->run_file_dialog = browser_host_run_file_dialog;
+  GetStruct()->start_download = browser_host_start_download;
+  GetStruct()->download_image = browser_host_download_image;
+  GetStruct()->print = browser_host_print;
+  GetStruct()->print_to_pdf = browser_host_print_to_pdf;
+  GetStruct()->find = browser_host_find;
+  GetStruct()->stop_finding = browser_host_stop_finding;
+  GetStruct()->show_dev_tools = browser_host_show_dev_tools;
+  GetStruct()->close_dev_tools = browser_host_close_dev_tools;
+  GetStruct()->has_dev_tools = browser_host_has_dev_tools;
+  GetStruct()->send_dev_tools_message = browser_host_send_dev_tools_message;
+  GetStruct()->execute_dev_tools_method = browser_host_execute_dev_tools_method;
+  GetStruct()->add_dev_tools_message_observer =
+      browser_host_add_dev_tools_message_observer;
+  GetStruct()->get_navigation_entries = browser_host_get_navigation_entries;
+  GetStruct()->set_mouse_cursor_change_disabled =
+      browser_host_set_mouse_cursor_change_disabled;
+  GetStruct()->is_mouse_cursor_change_disabled =
+      browser_host_is_mouse_cursor_change_disabled;
+  GetStruct()->replace_misspelling = browser_host_replace_misspelling;
+  GetStruct()->add_word_to_dictionary = browser_host_add_word_to_dictionary;
+  GetStruct()->is_window_rendering_disabled =
+      browser_host_is_window_rendering_disabled;
+  GetStruct()->was_resized = browser_host_was_resized;
+  GetStruct()->was_hidden = browser_host_was_hidden;
+  GetStruct()->notify_screen_info_changed =
+      browser_host_notify_screen_info_changed;
+  GetStruct()->invalidate = browser_host_invalidate;
+  GetStruct()->send_external_begin_frame =
+      browser_host_send_external_begin_frame;
+  GetStruct()->send_key_event = browser_host_send_key_event;
+  GetStruct()->send_mouse_click_event = browser_host_send_mouse_click_event;
+  GetStruct()->send_mouse_move_event = browser_host_send_mouse_move_event;
+  GetStruct()->send_mouse_wheel_event = browser_host_send_mouse_wheel_event;
+  GetStruct()->send_touch_event = browser_host_send_touch_event;
+  GetStruct()->send_focus_event = browser_host_send_focus_event;
+  GetStruct()->send_capture_lost_event = browser_host_send_capture_lost_event;
+  GetStruct()->notify_move_or_resize_started =
+      browser_host_notify_move_or_resize_started;
+  GetStruct()->get_windowless_frame_rate =
+      browser_host_get_windowless_frame_rate;
+  GetStruct()->set_windowless_frame_rate =
+      browser_host_set_windowless_frame_rate;
+  GetStruct()->ime_set_composition = browser_host_ime_set_composition;
+  GetStruct()->ime_commit_text = browser_host_ime_commit_text;
+  GetStruct()->ime_finish_composing_text =
+      browser_host_ime_finish_composing_text;
+  GetStruct()->ime_cancel_composition = browser_host_ime_cancel_composition;
+  GetStruct()->drag_target_drag_enter = browser_host_drag_target_drag_enter;
+  GetStruct()->drag_target_drag_over = browser_host_drag_target_drag_over;
+  GetStruct()->drag_target_drag_leave = browser_host_drag_target_drag_leave;
+  GetStruct()->drag_target_drop = browser_host_drag_target_drop;
+  GetStruct()->drag_source_ended_at = browser_host_drag_source_ended_at;
+  GetStruct()->drag_source_system_drag_ended =
+      browser_host_drag_source_system_drag_ended;
+  GetStruct()->get_visible_navigation_entry =
+      browser_host_get_visible_navigation_entry;
+  GetStruct()->set_accessibility_state = browser_host_set_accessibility_state;
+  GetStruct()->set_auto_resize_enabled = browser_host_set_auto_resize_enabled;
+  GetStruct()->get_extension = browser_host_get_extension;
+  GetStruct()->is_background_host = browser_host_is_background_host;
+  GetStruct()->set_audio_muted = browser_host_set_audio_muted;
+  GetStruct()->is_audio_muted = browser_host_is_audio_muted;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserHostCppToC::~CefBrowserHostCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefBrowserHost>
+CefCppToCRefCounted<CefBrowserHostCppToC, CefBrowserHost, cef_browser_host_t>::
+    UnwrapDerived(CefWrapperType type, cef_browser_host_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefBrowserHostCppToC,
+                                   CefBrowserHost,
+                                   cef_browser_host_t>::kWrapperType =
+    WT_BROWSER_HOST;
diff --git a/src/libcef_dll/cpptoc/browser_host_cpptoc.h b/src/libcef_dll/cpptoc/browser_host_cpptoc.h
new file mode 100644
index 0000000..d901588
--- /dev/null
+++ b/src/libcef_dll/cpptoc/browser_host_cpptoc.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=749d0e8cba77daca947e37b9f868778c2cb6f011$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_BROWSER_HOST_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_BROWSER_HOST_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefBrowserHostCppToC : public CefCppToCRefCounted<CefBrowserHostCppToC,
+                                                        CefBrowserHost,
+                                                        cef_browser_host_t> {
+ public:
+  CefBrowserHostCppToC();
+  virtual ~CefBrowserHostCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_BROWSER_HOST_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/browser_process_handler_cpptoc.cc b/src/libcef_dll/cpptoc/browser_process_handler_cpptoc.cc
new file mode 100644
index 0000000..226adc9
--- /dev/null
+++ b/src/libcef_dll/cpptoc/browser_process_handler_cpptoc.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6c70366a25d8ad81d0adf85e2a867906f90ee695$
+//
+
+#include "libcef_dll/cpptoc/browser_process_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/print_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/command_line_ctocpp.h"
+#include "libcef_dll/ctocpp/list_value_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK browser_process_handler_on_context_initialized(
+    struct _cef_browser_process_handler_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserProcessHandlerCppToC::Get(self)->OnContextInitialized();
+}
+
+void CEF_CALLBACK browser_process_handler_on_before_child_process_launch(
+    struct _cef_browser_process_handler_t* self,
+    struct _cef_command_line_t* command_line) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: command_line; type: refptr_diff
+  DCHECK(command_line);
+  if (!command_line)
+    return;
+
+  // Execute
+  CefBrowserProcessHandlerCppToC::Get(self)->OnBeforeChildProcessLaunch(
+      CefCommandLineCToCpp::Wrap(command_line));
+}
+
+void CEF_CALLBACK browser_process_handler_on_render_process_thread_created(
+    struct _cef_browser_process_handler_t* self,
+    struct _cef_list_value_t* extra_info) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: extra_info; type: refptr_diff
+  DCHECK(extra_info);
+  if (!extra_info)
+    return;
+
+  // Execute
+  CefBrowserProcessHandlerCppToC::Get(self)->OnRenderProcessThreadCreated(
+      CefListValueCToCpp::Wrap(extra_info));
+}
+
+struct _cef_print_handler_t* CEF_CALLBACK
+browser_process_handler_get_print_handler(
+    struct _cef_browser_process_handler_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPrintHandler> _retval =
+      CefBrowserProcessHandlerCppToC::Get(self)->GetPrintHandler();
+
+  // Return type: refptr_same
+  return CefPrintHandlerCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK browser_process_handler_on_schedule_message_pump_work(
+    struct _cef_browser_process_handler_t* self,
+    int64 delay_ms) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserProcessHandlerCppToC::Get(self)->OnScheduleMessagePumpWork(
+      delay_ms);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserProcessHandlerCppToC::CefBrowserProcessHandlerCppToC() {
+  GetStruct()->on_context_initialized =
+      browser_process_handler_on_context_initialized;
+  GetStruct()->on_before_child_process_launch =
+      browser_process_handler_on_before_child_process_launch;
+  GetStruct()->on_render_process_thread_created =
+      browser_process_handler_on_render_process_thread_created;
+  GetStruct()->get_print_handler = browser_process_handler_get_print_handler;
+  GetStruct()->on_schedule_message_pump_work =
+      browser_process_handler_on_schedule_message_pump_work;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserProcessHandlerCppToC::~CefBrowserProcessHandlerCppToC() {}
+
+template <>
+CefRefPtr<CefBrowserProcessHandler> CefCppToCRefCounted<
+    CefBrowserProcessHandlerCppToC,
+    CefBrowserProcessHandler,
+    cef_browser_process_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                  cef_browser_process_handler_t*
+                                                      s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefBrowserProcessHandlerCppToC,
+                        CefBrowserProcessHandler,
+                        cef_browser_process_handler_t>::kWrapperType =
+        WT_BROWSER_PROCESS_HANDLER;
diff --git a/src/libcef_dll/cpptoc/browser_process_handler_cpptoc.h b/src/libcef_dll/cpptoc/browser_process_handler_cpptoc.h
new file mode 100644
index 0000000..bfc8b05
--- /dev/null
+++ b/src/libcef_dll/cpptoc/browser_process_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7664d494a2e87495b34430b814474f3e4031c9a5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_BROWSER_PROCESS_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_BROWSER_PROCESS_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_process_handler_capi.h"
+#include "include/cef_browser_process_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBrowserProcessHandlerCppToC
+    : public CefCppToCRefCounted<CefBrowserProcessHandlerCppToC,
+                                 CefBrowserProcessHandler,
+                                 cef_browser_process_handler_t> {
+ public:
+  CefBrowserProcessHandlerCppToC();
+  virtual ~CefBrowserProcessHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_BROWSER_PROCESS_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/callback_cpptoc.cc b/src/libcef_dll/cpptoc/callback_cpptoc.cc
new file mode 100644
index 0000000..54bd00c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/callback_cpptoc.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=23c54bbef1c4d4e13b46f746df1d4123d2378548$
+//
+
+#include "libcef_dll/cpptoc/callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK callback_cont(struct _cef_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefCallbackCppToC::Get(self)->Continue();
+}
+
+void CEF_CALLBACK callback_cancel(struct _cef_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefCallbackCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCallbackCppToC::CefCallbackCppToC() {
+  GetStruct()->cont = callback_cont;
+  GetStruct()->cancel = callback_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCallbackCppToC::~CefCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefCallback>
+CefCppToCRefCounted<CefCallbackCppToC, CefCallback, cef_callback_t>::
+    UnwrapDerived(CefWrapperType type, cef_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefCallbackCppToC,
+                                   CefCallback,
+                                   cef_callback_t>::kWrapperType = WT_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/callback_cpptoc.h b/src/libcef_dll/cpptoc/callback_cpptoc.h
new file mode 100644
index 0000000..c978585
--- /dev/null
+++ b/src/libcef_dll/cpptoc/callback_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3be29effc92de5236544395b9b513cf5e74ae194$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_callback_capi.h"
+#include "include/cef_callback.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefCallbackCppToC : public CefCppToCRefCounted<CefCallbackCppToC,
+                                                     CefCallback,
+                                                     cef_callback_t> {
+ public:
+  CefCallbackCppToC();
+  virtual ~CefCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/client_cpptoc.cc b/src/libcef_dll/cpptoc/client_cpptoc.cc
new file mode 100644
index 0000000..9e54e6a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/client_cpptoc.cc
@@ -0,0 +1,332 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=04cee2c6a1910d7084c556f1bde99ba971b354d2$
+//
+
+#include "libcef_dll/cpptoc/client_cpptoc.h"
+#include "libcef_dll/cpptoc/audio_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/context_menu_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/dialog_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/display_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/download_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/drag_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/find_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/focus_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/jsdialog_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/keyboard_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/life_span_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/load_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/render_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/request_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/process_message_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_audio_handler_t* CEF_CALLBACK
+client_get_audio_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefAudioHandler> _retval =
+      CefClientCppToC::Get(self)->GetAudioHandler();
+
+  // Return type: refptr_same
+  return CefAudioHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_context_menu_handler_t* CEF_CALLBACK
+client_get_context_menu_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefContextMenuHandler> _retval =
+      CefClientCppToC::Get(self)->GetContextMenuHandler();
+
+  // Return type: refptr_same
+  return CefContextMenuHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_dialog_handler_t* CEF_CALLBACK
+client_get_dialog_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDialogHandler> _retval =
+      CefClientCppToC::Get(self)->GetDialogHandler();
+
+  // Return type: refptr_same
+  return CefDialogHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_display_handler_t* CEF_CALLBACK
+client_get_display_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDisplayHandler> _retval =
+      CefClientCppToC::Get(self)->GetDisplayHandler();
+
+  // Return type: refptr_same
+  return CefDisplayHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_download_handler_t* CEF_CALLBACK
+client_get_download_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDownloadHandler> _retval =
+      CefClientCppToC::Get(self)->GetDownloadHandler();
+
+  // Return type: refptr_same
+  return CefDownloadHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_drag_handler_t* CEF_CALLBACK
+client_get_drag_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDragHandler> _retval =
+      CefClientCppToC::Get(self)->GetDragHandler();
+
+  // Return type: refptr_same
+  return CefDragHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_find_handler_t* CEF_CALLBACK
+client_get_find_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFindHandler> _retval =
+      CefClientCppToC::Get(self)->GetFindHandler();
+
+  // Return type: refptr_same
+  return CefFindHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_focus_handler_t* CEF_CALLBACK
+client_get_focus_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFocusHandler> _retval =
+      CefClientCppToC::Get(self)->GetFocusHandler();
+
+  // Return type: refptr_same
+  return CefFocusHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_jsdialog_handler_t* CEF_CALLBACK
+client_get_jsdialog_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefJSDialogHandler> _retval =
+      CefClientCppToC::Get(self)->GetJSDialogHandler();
+
+  // Return type: refptr_same
+  return CefJSDialogHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_keyboard_handler_t* CEF_CALLBACK
+client_get_keyboard_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefKeyboardHandler> _retval =
+      CefClientCppToC::Get(self)->GetKeyboardHandler();
+
+  // Return type: refptr_same
+  return CefKeyboardHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_life_span_handler_t* CEF_CALLBACK
+client_get_life_span_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLifeSpanHandler> _retval =
+      CefClientCppToC::Get(self)->GetLifeSpanHandler();
+
+  // Return type: refptr_same
+  return CefLifeSpanHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_load_handler_t* CEF_CALLBACK
+client_get_load_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLoadHandler> _retval =
+      CefClientCppToC::Get(self)->GetLoadHandler();
+
+  // Return type: refptr_same
+  return CefLoadHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_render_handler_t* CEF_CALLBACK
+client_get_render_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRenderHandler> _retval =
+      CefClientCppToC::Get(self)->GetRenderHandler();
+
+  // Return type: refptr_same
+  return CefRenderHandlerCppToC::Wrap(_retval);
+}
+
+struct _cef_request_handler_t* CEF_CALLBACK
+client_get_request_handler(struct _cef_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRequestHandler> _retval =
+      CefClientCppToC::Get(self)->GetRequestHandler();
+
+  // Return type: refptr_same
+  return CefRequestHandlerCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK
+client_on_process_message_received(struct _cef_client_t* self,
+                                   cef_browser_t* browser,
+                                   struct _cef_frame_t* frame,
+                                   cef_process_id_t source_process,
+                                   struct _cef_process_message_t* message) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return 0;
+  // Verify param: message; type: refptr_diff
+  DCHECK(message);
+  if (!message)
+    return 0;
+
+  // Execute
+  bool _retval = CefClientCppToC::Get(self)->OnProcessMessageReceived(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      source_process, CefProcessMessageCToCpp::Wrap(message));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefClientCppToC::CefClientCppToC() {
+  GetStruct()->get_audio_handler = client_get_audio_handler;
+  GetStruct()->get_context_menu_handler = client_get_context_menu_handler;
+  GetStruct()->get_dialog_handler = client_get_dialog_handler;
+  GetStruct()->get_display_handler = client_get_display_handler;
+  GetStruct()->get_download_handler = client_get_download_handler;
+  GetStruct()->get_drag_handler = client_get_drag_handler;
+  GetStruct()->get_find_handler = client_get_find_handler;
+  GetStruct()->get_focus_handler = client_get_focus_handler;
+  GetStruct()->get_jsdialog_handler = client_get_jsdialog_handler;
+  GetStruct()->get_keyboard_handler = client_get_keyboard_handler;
+  GetStruct()->get_life_span_handler = client_get_life_span_handler;
+  GetStruct()->get_load_handler = client_get_load_handler;
+  GetStruct()->get_render_handler = client_get_render_handler;
+  GetStruct()->get_request_handler = client_get_request_handler;
+  GetStruct()->on_process_message_received = client_on_process_message_received;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefClientCppToC::~CefClientCppToC() {}
+
+template <>
+CefRefPtr<CefClient>
+CefCppToCRefCounted<CefClientCppToC, CefClient, cef_client_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_client_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefClientCppToC, CefClient, cef_client_t>::
+    kWrapperType = WT_CLIENT;
diff --git a/src/libcef_dll/cpptoc/client_cpptoc.h b/src/libcef_dll/cpptoc/client_cpptoc.h
new file mode 100644
index 0000000..788f65d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/client_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6e9a6091b190b39ce9f39b4464d19f96bf5e52a6$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_CLIENT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_CLIENT_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_client.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefClientCppToC
+    : public CefCppToCRefCounted<CefClientCppToC, CefClient, cef_client_t> {
+ public:
+  CefClientCppToC();
+  virtual ~CefClientCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_CLIENT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/command_line_cpptoc.cc b/src/libcef_dll/cpptoc/command_line_cpptoc.cc
new file mode 100644
index 0000000..067b9d1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/command_line_cpptoc.cc
@@ -0,0 +1,432 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c2e91da671aba98135e71145f6b27f92b892d425$
+//
+
+#include "libcef_dll/cpptoc/command_line_cpptoc.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_command_line_t* cef_command_line_create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefCommandLine> _retval = CefCommandLine::CreateCommandLine();
+
+  // Return type: refptr_same
+  return CefCommandLineCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_command_line_t* cef_command_line_get_global() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefCommandLine> _retval = CefCommandLine::GetGlobalCommandLine();
+
+  // Return type: refptr_same
+  return CefCommandLineCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK command_line_is_valid(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefCommandLineCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK command_line_is_read_only(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefCommandLineCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_command_line_t* CEF_CALLBACK
+command_line_copy(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefCommandLine> _retval = CefCommandLineCppToC::Get(self)->Copy();
+
+  // Return type: refptr_same
+  return CefCommandLineCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK command_line_init_from_argv(struct _cef_command_line_t* self,
+                                              int argc,
+                                              const char* const* argv) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: argv; type: simple_byaddr
+  DCHECK(argv);
+  if (!argv)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->InitFromArgv(argc, argv);
+}
+
+void CEF_CALLBACK
+command_line_init_from_string(struct _cef_command_line_t* self,
+                              const cef_string_t* command_line) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: command_line; type: string_byref_const
+  DCHECK(command_line);
+  if (!command_line)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->InitFromString(CefString(command_line));
+}
+
+void CEF_CALLBACK command_line_reset(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->Reset();
+}
+
+void CEF_CALLBACK command_line_get_argv(struct _cef_command_line_t* self,
+                                        cef_string_list_t argv) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: argv; type: string_vec_byref
+  DCHECK(argv);
+  if (!argv)
+    return;
+
+  // Translate param: argv; type: string_vec_byref
+  std::vector<CefString> argvList;
+  transfer_string_list_contents(argv, argvList);
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->GetArgv(argvList);
+
+  // Restore param: argv; type: string_vec_byref
+  cef_string_list_clear(argv);
+  transfer_string_list_contents(argvList, argv);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+command_line_get_command_line_string(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefCommandLineCppToC::Get(self)->GetCommandLineString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+command_line_get_program(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefCommandLineCppToC::Get(self)->GetProgram();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK command_line_set_program(struct _cef_command_line_t* self,
+                                           const cef_string_t* program) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: program; type: string_byref_const
+  DCHECK(program);
+  if (!program)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->SetProgram(CefString(program));
+}
+
+int CEF_CALLBACK command_line_has_switches(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefCommandLineCppToC::Get(self)->HasSwitches();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK command_line_has_switch(struct _cef_command_line_t* self,
+                                         const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+
+  // Execute
+  bool _retval = CefCommandLineCppToC::Get(self)->HasSwitch(CefString(name));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+command_line_get_switch_value(struct _cef_command_line_t* self,
+                              const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefCommandLineCppToC::Get(self)->GetSwitchValue(CefString(name));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK command_line_get_switches(struct _cef_command_line_t* self,
+                                            cef_string_map_t switches) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: switches; type: string_map_single_byref
+  DCHECK(switches);
+  if (!switches)
+    return;
+
+  // Translate param: switches; type: string_map_single_byref
+  std::map<CefString, CefString> switchesMap;
+  transfer_string_map_contents(switches, switchesMap);
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->GetSwitches(switchesMap);
+
+  // Restore param: switches; type: string_map_single_byref
+  cef_string_map_clear(switches);
+  transfer_string_map_contents(switchesMap, switches);
+}
+
+void CEF_CALLBACK command_line_append_switch(struct _cef_command_line_t* self,
+                                             const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->AppendSwitch(CefString(name));
+}
+
+void CEF_CALLBACK
+command_line_append_switch_with_value(struct _cef_command_line_t* self,
+                                      const cef_string_t* name,
+                                      const cef_string_t* value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+  // Verify param: value; type: string_byref_const
+  DCHECK(value);
+  if (!value)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->AppendSwitchWithValue(CefString(name),
+                                                         CefString(value));
+}
+
+int CEF_CALLBACK command_line_has_arguments(struct _cef_command_line_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefCommandLineCppToC::Get(self)->HasArguments();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK command_line_get_arguments(struct _cef_command_line_t* self,
+                                             cef_string_list_t arguments) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: arguments; type: string_vec_byref
+  DCHECK(arguments);
+  if (!arguments)
+    return;
+
+  // Translate param: arguments; type: string_vec_byref
+  std::vector<CefString> argumentsList;
+  transfer_string_list_contents(arguments, argumentsList);
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->GetArguments(argumentsList);
+
+  // Restore param: arguments; type: string_vec_byref
+  cef_string_list_clear(arguments);
+  transfer_string_list_contents(argumentsList, arguments);
+}
+
+void CEF_CALLBACK command_line_append_argument(struct _cef_command_line_t* self,
+                                               const cef_string_t* argument) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: argument; type: string_byref_const
+  DCHECK(argument);
+  if (!argument)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->AppendArgument(CefString(argument));
+}
+
+void CEF_CALLBACK command_line_prepend_wrapper(struct _cef_command_line_t* self,
+                                               const cef_string_t* wrapper) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: wrapper; type: string_byref_const
+  DCHECK(wrapper);
+  if (!wrapper)
+    return;
+
+  // Execute
+  CefCommandLineCppToC::Get(self)->PrependWrapper(CefString(wrapper));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCommandLineCppToC::CefCommandLineCppToC() {
+  GetStruct()->is_valid = command_line_is_valid;
+  GetStruct()->is_read_only = command_line_is_read_only;
+  GetStruct()->copy = command_line_copy;
+  GetStruct()->init_from_argv = command_line_init_from_argv;
+  GetStruct()->init_from_string = command_line_init_from_string;
+  GetStruct()->reset = command_line_reset;
+  GetStruct()->get_argv = command_line_get_argv;
+  GetStruct()->get_command_line_string = command_line_get_command_line_string;
+  GetStruct()->get_program = command_line_get_program;
+  GetStruct()->set_program = command_line_set_program;
+  GetStruct()->has_switches = command_line_has_switches;
+  GetStruct()->has_switch = command_line_has_switch;
+  GetStruct()->get_switch_value = command_line_get_switch_value;
+  GetStruct()->get_switches = command_line_get_switches;
+  GetStruct()->append_switch = command_line_append_switch;
+  GetStruct()->append_switch_with_value = command_line_append_switch_with_value;
+  GetStruct()->has_arguments = command_line_has_arguments;
+  GetStruct()->get_arguments = command_line_get_arguments;
+  GetStruct()->append_argument = command_line_append_argument;
+  GetStruct()->prepend_wrapper = command_line_prepend_wrapper;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCommandLineCppToC::~CefCommandLineCppToC() {}
+
+template <>
+CefRefPtr<CefCommandLine>
+CefCppToCRefCounted<CefCommandLineCppToC, CefCommandLine, cef_command_line_t>::
+    UnwrapDerived(CefWrapperType type, cef_command_line_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefCommandLineCppToC,
+                                   CefCommandLine,
+                                   cef_command_line_t>::kWrapperType =
+    WT_COMMAND_LINE;
diff --git a/src/libcef_dll/cpptoc/command_line_cpptoc.h b/src/libcef_dll/cpptoc/command_line_cpptoc.h
new file mode 100644
index 0000000..78da59e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/command_line_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1af599620e55743a48e67a53254ff6f3006e5842$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_COMMAND_LINE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_COMMAND_LINE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_command_line_capi.h"
+#include "include/cef_command_line.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefCommandLineCppToC : public CefCppToCRefCounted<CefCommandLineCppToC,
+                                                        CefCommandLine,
+                                                        cef_command_line_t> {
+ public:
+  CefCommandLineCppToC();
+  virtual ~CefCommandLineCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_COMMAND_LINE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/completion_callback_cpptoc.cc b/src/libcef_dll/cpptoc/completion_callback_cpptoc.cc
new file mode 100644
index 0000000..d5cd187
--- /dev/null
+++ b/src/libcef_dll/cpptoc/completion_callback_cpptoc.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ce5faec2604b151f3872baae02247038c41dfbbd$
+//
+
+#include "libcef_dll/cpptoc/completion_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+completion_callback_on_complete(struct _cef_completion_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefCompletionCallbackCppToC::Get(self)->OnComplete();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCompletionCallbackCppToC::CefCompletionCallbackCppToC() {
+  GetStruct()->on_complete = completion_callback_on_complete;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCompletionCallbackCppToC::~CefCompletionCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefCompletionCallback> CefCppToCRefCounted<
+    CefCompletionCallbackCppToC,
+    CefCompletionCallback,
+    cef_completion_callback_t>::UnwrapDerived(CefWrapperType type,
+                                              cef_completion_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefCompletionCallbackCppToC,
+                                   CefCompletionCallback,
+                                   cef_completion_callback_t>::kWrapperType =
+    WT_COMPLETION_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/completion_callback_cpptoc.h b/src/libcef_dll/cpptoc/completion_callback_cpptoc.h
new file mode 100644
index 0000000..5fa78fc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/completion_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e28df65801eb92f405294a952d0de78ff1eddb12$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_COMPLETION_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_COMPLETION_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_callback_capi.h"
+#include "include/cef_callback.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefCompletionCallbackCppToC
+    : public CefCppToCRefCounted<CefCompletionCallbackCppToC,
+                                 CefCompletionCallback,
+                                 cef_completion_callback_t> {
+ public:
+  CefCompletionCallbackCppToC();
+  virtual ~CefCompletionCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_COMPLETION_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/context_menu_handler_cpptoc.cc b/src/libcef_dll/cpptoc/context_menu_handler_cpptoc.cc
new file mode 100644
index 0000000..bbafc39
--- /dev/null
+++ b/src/libcef_dll/cpptoc/context_menu_handler_cpptoc.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9616dc7c84524da3f8549ce399bff1ff4c624ebc$
+//
+
+#include "libcef_dll/cpptoc/context_menu_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/context_menu_params_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/menu_model_ctocpp.h"
+#include "libcef_dll/ctocpp/run_context_menu_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK context_menu_handler_on_before_context_menu(
+    struct _cef_context_menu_handler_t* self,
+    cef_browser_t* browser,
+    struct _cef_frame_t* frame,
+    struct _cef_context_menu_params_t* params,
+    struct _cef_menu_model_t* model) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Verify param: params; type: refptr_diff
+  DCHECK(params);
+  if (!params)
+    return;
+  // Verify param: model; type: refptr_diff
+  DCHECK(model);
+  if (!model)
+    return;
+
+  // Execute
+  CefContextMenuHandlerCppToC::Get(self)->OnBeforeContextMenu(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefContextMenuParamsCToCpp::Wrap(params),
+      CefMenuModelCToCpp::Wrap(model));
+}
+
+int CEF_CALLBACK context_menu_handler_run_context_menu(
+    struct _cef_context_menu_handler_t* self,
+    cef_browser_t* browser,
+    struct _cef_frame_t* frame,
+    struct _cef_context_menu_params_t* params,
+    struct _cef_menu_model_t* model,
+    cef_run_context_menu_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return 0;
+  // Verify param: params; type: refptr_diff
+  DCHECK(params);
+  if (!params)
+    return 0;
+  // Verify param: model; type: refptr_diff
+  DCHECK(model);
+  if (!model)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Execute
+  bool _retval = CefContextMenuHandlerCppToC::Get(self)->RunContextMenu(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefContextMenuParamsCToCpp::Wrap(params), CefMenuModelCToCpp::Wrap(model),
+      CefRunContextMenuCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK context_menu_handler_on_context_menu_command(
+    struct _cef_context_menu_handler_t* self,
+    cef_browser_t* browser,
+    struct _cef_frame_t* frame,
+    struct _cef_context_menu_params_t* params,
+    int command_id,
+    cef_event_flags_t event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return 0;
+  // Verify param: params; type: refptr_diff
+  DCHECK(params);
+  if (!params)
+    return 0;
+
+  // Execute
+  bool _retval = CefContextMenuHandlerCppToC::Get(self)->OnContextMenuCommand(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefContextMenuParamsCToCpp::Wrap(params), command_id, event_flags);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK context_menu_handler_on_context_menu_dismissed(
+    struct _cef_context_menu_handler_t* self,
+    cef_browser_t* browser,
+    struct _cef_frame_t* frame) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+
+  // Execute
+  CefContextMenuHandlerCppToC::Get(self)->OnContextMenuDismissed(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefContextMenuHandlerCppToC::CefContextMenuHandlerCppToC() {
+  GetStruct()->on_before_context_menu =
+      context_menu_handler_on_before_context_menu;
+  GetStruct()->run_context_menu = context_menu_handler_run_context_menu;
+  GetStruct()->on_context_menu_command =
+      context_menu_handler_on_context_menu_command;
+  GetStruct()->on_context_menu_dismissed =
+      context_menu_handler_on_context_menu_dismissed;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefContextMenuHandlerCppToC::~CefContextMenuHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefContextMenuHandler> CefCppToCRefCounted<
+    CefContextMenuHandlerCppToC,
+    CefContextMenuHandler,
+    cef_context_menu_handler_t>::UnwrapDerived(CefWrapperType type,
+                                               cef_context_menu_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefContextMenuHandlerCppToC,
+                                   CefContextMenuHandler,
+                                   cef_context_menu_handler_t>::kWrapperType =
+    WT_CONTEXT_MENU_HANDLER;
diff --git a/src/libcef_dll/cpptoc/context_menu_handler_cpptoc.h b/src/libcef_dll/cpptoc/context_menu_handler_cpptoc.h
new file mode 100644
index 0000000..ade011b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/context_menu_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0b171771131fc299f0ee38b5ff7fb2cd427aa2ea$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_CONTEXT_MENU_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_CONTEXT_MENU_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_context_menu_handler_capi.h"
+#include "include/cef_context_menu_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefContextMenuHandlerCppToC
+    : public CefCppToCRefCounted<CefContextMenuHandlerCppToC,
+                                 CefContextMenuHandler,
+                                 cef_context_menu_handler_t> {
+ public:
+  CefContextMenuHandlerCppToC();
+  virtual ~CefContextMenuHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_CONTEXT_MENU_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/context_menu_params_cpptoc.cc b/src/libcef_dll/cpptoc/context_menu_params_cpptoc.cc
new file mode 100644
index 0000000..bca60db
--- /dev/null
+++ b/src/libcef_dll/cpptoc/context_menu_params_cpptoc.cc
@@ -0,0 +1,455 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b6e2bf6704ad9ca5dc40c6dd7674f575d0dc1e67$
+//
+
+#include "libcef_dll/cpptoc/context_menu_params_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+context_menu_params_get_xcoord(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefContextMenuParamsCppToC::Get(self)->GetXCoord();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+context_menu_params_get_ycoord(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefContextMenuParamsCppToC::Get(self)->GetYCoord();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_context_menu_type_flags_t CEF_CALLBACK
+context_menu_params_get_type_flags(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CM_TYPEFLAG_NONE;
+
+  // Execute
+  cef_context_menu_type_flags_t _retval =
+      CefContextMenuParamsCppToC::Get(self)->GetTypeFlags();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+context_menu_params_get_link_url(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefContextMenuParamsCppToC::Get(self)->GetLinkUrl();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK context_menu_params_get_unfiltered_link_url(
+    struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefContextMenuParamsCppToC::Get(self)->GetUnfilteredLinkUrl();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+context_menu_params_get_source_url(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefContextMenuParamsCppToC::Get(self)->GetSourceUrl();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK context_menu_params_has_image_contents(
+    struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefContextMenuParamsCppToC::Get(self)->HasImageContents();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+context_menu_params_get_title_text(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefContextMenuParamsCppToC::Get(self)->GetTitleText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+context_menu_params_get_page_url(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefContextMenuParamsCppToC::Get(self)->GetPageUrl();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+context_menu_params_get_frame_url(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefContextMenuParamsCppToC::Get(self)->GetFrameUrl();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+context_menu_params_get_frame_charset(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefContextMenuParamsCppToC::Get(self)->GetFrameCharset();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_context_menu_media_type_t CEF_CALLBACK
+context_menu_params_get_media_type(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CM_MEDIATYPE_NONE;
+
+  // Execute
+  cef_context_menu_media_type_t _retval =
+      CefContextMenuParamsCppToC::Get(self)->GetMediaType();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_context_menu_media_state_flags_t CEF_CALLBACK
+context_menu_params_get_media_state_flags(
+    struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CM_MEDIAFLAG_NONE;
+
+  // Execute
+  cef_context_menu_media_state_flags_t _retval =
+      CefContextMenuParamsCppToC::Get(self)->GetMediaStateFlags();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK context_menu_params_get_selection_text(
+    struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefContextMenuParamsCppToC::Get(self)->GetSelectionText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK context_menu_params_get_misspelled_word(
+    struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefContextMenuParamsCppToC::Get(self)->GetMisspelledWord();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK context_menu_params_get_dictionary_suggestions(
+    struct _cef_context_menu_params_t* self,
+    cef_string_list_t suggestions) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: suggestions; type: string_vec_byref
+  DCHECK(suggestions);
+  if (!suggestions)
+    return 0;
+
+  // Translate param: suggestions; type: string_vec_byref
+  std::vector<CefString> suggestionsList;
+  transfer_string_list_contents(suggestions, suggestionsList);
+
+  // Execute
+  bool _retval =
+      CefContextMenuParamsCppToC::Get(self)->GetDictionarySuggestions(
+          suggestionsList);
+
+  // Restore param: suggestions; type: string_vec_byref
+  cef_string_list_clear(suggestions);
+  transfer_string_list_contents(suggestionsList, suggestions);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+context_menu_params_is_editable(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefContextMenuParamsCppToC::Get(self)->IsEditable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK context_menu_params_is_spell_check_enabled(
+    struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefContextMenuParamsCppToC::Get(self)->IsSpellCheckEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_context_menu_edit_state_flags_t CEF_CALLBACK
+context_menu_params_get_edit_state_flags(
+    struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CM_EDITFLAG_NONE;
+
+  // Execute
+  cef_context_menu_edit_state_flags_t _retval =
+      CefContextMenuParamsCppToC::Get(self)->GetEditStateFlags();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+context_menu_params_is_custom_menu(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefContextMenuParamsCppToC::Get(self)->IsCustomMenu();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+context_menu_params_is_pepper_menu(struct _cef_context_menu_params_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefContextMenuParamsCppToC::Get(self)->IsPepperMenu();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefContextMenuParamsCppToC::CefContextMenuParamsCppToC() {
+  GetStruct()->get_xcoord = context_menu_params_get_xcoord;
+  GetStruct()->get_ycoord = context_menu_params_get_ycoord;
+  GetStruct()->get_type_flags = context_menu_params_get_type_flags;
+  GetStruct()->get_link_url = context_menu_params_get_link_url;
+  GetStruct()->get_unfiltered_link_url =
+      context_menu_params_get_unfiltered_link_url;
+  GetStruct()->get_source_url = context_menu_params_get_source_url;
+  GetStruct()->has_image_contents = context_menu_params_has_image_contents;
+  GetStruct()->get_title_text = context_menu_params_get_title_text;
+  GetStruct()->get_page_url = context_menu_params_get_page_url;
+  GetStruct()->get_frame_url = context_menu_params_get_frame_url;
+  GetStruct()->get_frame_charset = context_menu_params_get_frame_charset;
+  GetStruct()->get_media_type = context_menu_params_get_media_type;
+  GetStruct()->get_media_state_flags =
+      context_menu_params_get_media_state_flags;
+  GetStruct()->get_selection_text = context_menu_params_get_selection_text;
+  GetStruct()->get_misspelled_word = context_menu_params_get_misspelled_word;
+  GetStruct()->get_dictionary_suggestions =
+      context_menu_params_get_dictionary_suggestions;
+  GetStruct()->is_editable = context_menu_params_is_editable;
+  GetStruct()->is_spell_check_enabled =
+      context_menu_params_is_spell_check_enabled;
+  GetStruct()->get_edit_state_flags = context_menu_params_get_edit_state_flags;
+  GetStruct()->is_custom_menu = context_menu_params_is_custom_menu;
+  GetStruct()->is_pepper_menu = context_menu_params_is_pepper_menu;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefContextMenuParamsCppToC::~CefContextMenuParamsCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefContextMenuParams> CefCppToCRefCounted<
+    CefContextMenuParamsCppToC,
+    CefContextMenuParams,
+    cef_context_menu_params_t>::UnwrapDerived(CefWrapperType type,
+                                              cef_context_menu_params_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefContextMenuParamsCppToC,
+                                   CefContextMenuParams,
+                                   cef_context_menu_params_t>::kWrapperType =
+    WT_CONTEXT_MENU_PARAMS;
diff --git a/src/libcef_dll/cpptoc/context_menu_params_cpptoc.h b/src/libcef_dll/cpptoc/context_menu_params_cpptoc.h
new file mode 100644
index 0000000..ca22e21
--- /dev/null
+++ b/src/libcef_dll/cpptoc/context_menu_params_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b1ca103dfbfa17ce040d0d178163d102951aaa61$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_CONTEXT_MENU_PARAMS_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_CONTEXT_MENU_PARAMS_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_context_menu_handler_capi.h"
+#include "include/cef_context_menu_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefContextMenuParamsCppToC
+    : public CefCppToCRefCounted<CefContextMenuParamsCppToC,
+                                 CefContextMenuParams,
+                                 cef_context_menu_params_t> {
+ public:
+  CefContextMenuParamsCppToC();
+  virtual ~CefContextMenuParamsCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_CONTEXT_MENU_PARAMS_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/cookie_access_filter_cpptoc.cc b/src/libcef_dll/cpptoc/cookie_access_filter_cpptoc.cc
new file mode 100644
index 0000000..ab91716
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cookie_access_filter_cpptoc.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=669e20095016a2e40a0925b9715fc0b15229ff32$
+//
+
+#include "libcef_dll/cpptoc/cookie_access_filter_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/response_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+cookie_access_filter_can_send_cookie(struct _cef_cookie_access_filter_t* self,
+                                     cef_browser_t* browser,
+                                     cef_frame_t* frame,
+                                     cef_request_t* request,
+                                     const struct _cef_cookie_t* cookie) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return 0;
+  // Verify param: cookie; type: struct_byref_const
+  DCHECK(cookie);
+  if (!cookie)
+    return 0;
+  // Unverified params: browser, frame
+
+  // Translate param: cookie; type: struct_byref_const
+  CefCookie cookieObj;
+  if (cookie)
+    cookieObj.Set(*cookie, false);
+
+  // Execute
+  bool _retval = CefCookieAccessFilterCppToC::Get(self)->CanSendCookie(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefRequestCToCpp::Wrap(request), cookieObj);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+cookie_access_filter_can_save_cookie(struct _cef_cookie_access_filter_t* self,
+                                     cef_browser_t* browser,
+                                     cef_frame_t* frame,
+                                     cef_request_t* request,
+                                     struct _cef_response_t* response,
+                                     const struct _cef_cookie_t* cookie) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return 0;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response);
+  if (!response)
+    return 0;
+  // Verify param: cookie; type: struct_byref_const
+  DCHECK(cookie);
+  if (!cookie)
+    return 0;
+  // Unverified params: browser, frame
+
+  // Translate param: cookie; type: struct_byref_const
+  CefCookie cookieObj;
+  if (cookie)
+    cookieObj.Set(*cookie, false);
+
+  // Execute
+  bool _retval = CefCookieAccessFilterCppToC::Get(self)->CanSaveCookie(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefRequestCToCpp::Wrap(request), CefResponseCToCpp::Wrap(response),
+      cookieObj);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCookieAccessFilterCppToC::CefCookieAccessFilterCppToC() {
+  GetStruct()->can_send_cookie = cookie_access_filter_can_send_cookie;
+  GetStruct()->can_save_cookie = cookie_access_filter_can_save_cookie;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCookieAccessFilterCppToC::~CefCookieAccessFilterCppToC() {}
+
+template <>
+CefRefPtr<CefCookieAccessFilter> CefCppToCRefCounted<
+    CefCookieAccessFilterCppToC,
+    CefCookieAccessFilter,
+    cef_cookie_access_filter_t>::UnwrapDerived(CefWrapperType type,
+                                               cef_cookie_access_filter_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefCookieAccessFilterCppToC,
+                                   CefCookieAccessFilter,
+                                   cef_cookie_access_filter_t>::kWrapperType =
+    WT_COOKIE_ACCESS_FILTER;
diff --git a/src/libcef_dll/cpptoc/cookie_access_filter_cpptoc.h b/src/libcef_dll/cpptoc/cookie_access_filter_cpptoc.h
new file mode 100644
index 0000000..302f394
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cookie_access_filter_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c1edc5ee4581ea6437f316432cc6746e4213eeb2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_COOKIE_ACCESS_FILTER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_COOKIE_ACCESS_FILTER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_resource_request_handler_capi.h"
+#include "include/cef_resource_request_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefCookieAccessFilterCppToC
+    : public CefCppToCRefCounted<CefCookieAccessFilterCppToC,
+                                 CefCookieAccessFilter,
+                                 cef_cookie_access_filter_t> {
+ public:
+  CefCookieAccessFilterCppToC();
+  virtual ~CefCookieAccessFilterCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_COOKIE_ACCESS_FILTER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/cookie_manager_cpptoc.cc b/src/libcef_dll/cpptoc/cookie_manager_cpptoc.cc
new file mode 100644
index 0000000..6f59b4f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cookie_manager_cpptoc.cc
@@ -0,0 +1,220 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1c3e3acd8548a82404f33b728223893809a95ee8$
+//
+
+#include "libcef_dll/cpptoc/cookie_manager_cpptoc.h"
+#include "libcef_dll/ctocpp/completion_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/cookie_visitor_ctocpp.h"
+#include "libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/set_cookie_callback_ctocpp.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_cookie_manager_t* cef_cookie_manager_get_global_manager(
+    cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Execute
+  CefRefPtr<CefCookieManager> _retval = CefCookieManager::GetGlobalManager(
+      CefCompletionCallbackCToCpp::Wrap(callback));
+
+  // Return type: refptr_same
+  return CefCookieManagerCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+cookie_manager_set_supported_schemes(struct _cef_cookie_manager_t* self,
+                                     cef_string_list_t schemes,
+                                     int include_defaults,
+                                     cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: schemes; type: string_vec_byref_const
+  DCHECK(schemes);
+  if (!schemes)
+    return;
+  // Unverified params: callback
+
+  // Translate param: schemes; type: string_vec_byref_const
+  std::vector<CefString> schemesList;
+  transfer_string_list_contents(schemes, schemesList);
+
+  // Execute
+  CefCookieManagerCppToC::Get(self)->SetSupportedSchemes(
+      schemesList, include_defaults ? true : false,
+      CefCompletionCallbackCToCpp::Wrap(callback));
+}
+
+int CEF_CALLBACK
+cookie_manager_visit_all_cookies(struct _cef_cookie_manager_t* self,
+                                 struct _cef_cookie_visitor_t* visitor) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor);
+  if (!visitor)
+    return 0;
+
+  // Execute
+  bool _retval = CefCookieManagerCppToC::Get(self)->VisitAllCookies(
+      CefCookieVisitorCToCpp::Wrap(visitor));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+cookie_manager_visit_url_cookies(struct _cef_cookie_manager_t* self,
+                                 const cef_string_t* url,
+                                 int includeHttpOnly,
+                                 struct _cef_cookie_visitor_t* visitor) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return 0;
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor);
+  if (!visitor)
+    return 0;
+
+  // Execute
+  bool _retval = CefCookieManagerCppToC::Get(self)->VisitUrlCookies(
+      CefString(url), includeHttpOnly ? true : false,
+      CefCookieVisitorCToCpp::Wrap(visitor));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+cookie_manager_set_cookie(struct _cef_cookie_manager_t* self,
+                          const cef_string_t* url,
+                          const struct _cef_cookie_t* cookie,
+                          struct _cef_set_cookie_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return 0;
+  // Verify param: cookie; type: struct_byref_const
+  DCHECK(cookie);
+  if (!cookie)
+    return 0;
+  // Unverified params: callback
+
+  // Translate param: cookie; type: struct_byref_const
+  CefCookie cookieObj;
+  if (cookie)
+    cookieObj.Set(*cookie, false);
+
+  // Execute
+  bool _retval = CefCookieManagerCppToC::Get(self)->SetCookie(
+      CefString(url), cookieObj, CefSetCookieCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+cookie_manager_delete_cookies(struct _cef_cookie_manager_t* self,
+                              const cef_string_t* url,
+                              const cef_string_t* cookie_name,
+                              struct _cef_delete_cookies_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: url, cookie_name, callback
+
+  // Execute
+  bool _retval = CefCookieManagerCppToC::Get(self)->DeleteCookies(
+      CefString(url), CefString(cookie_name),
+      CefDeleteCookiesCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+cookie_manager_flush_store(struct _cef_cookie_manager_t* self,
+                           cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: callback
+
+  // Execute
+  bool _retval = CefCookieManagerCppToC::Get(self)->FlushStore(
+      CefCompletionCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCookieManagerCppToC::CefCookieManagerCppToC() {
+  GetStruct()->set_supported_schemes = cookie_manager_set_supported_schemes;
+  GetStruct()->visit_all_cookies = cookie_manager_visit_all_cookies;
+  GetStruct()->visit_url_cookies = cookie_manager_visit_url_cookies;
+  GetStruct()->set_cookie = cookie_manager_set_cookie;
+  GetStruct()->delete_cookies = cookie_manager_delete_cookies;
+  GetStruct()->flush_store = cookie_manager_flush_store;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCookieManagerCppToC::~CefCookieManagerCppToC() {}
+
+template <>
+CefRefPtr<CefCookieManager> CefCppToCRefCounted<
+    CefCookieManagerCppToC,
+    CefCookieManager,
+    cef_cookie_manager_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_cookie_manager_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefCookieManagerCppToC,
+                                   CefCookieManager,
+                                   cef_cookie_manager_t>::kWrapperType =
+    WT_COOKIE_MANAGER;
diff --git a/src/libcef_dll/cpptoc/cookie_manager_cpptoc.h b/src/libcef_dll/cpptoc/cookie_manager_cpptoc.h
new file mode 100644
index 0000000..69e733a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cookie_manager_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=25c5eda0e5265f5905b144e5b23951fe7d11cf80$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_COOKIE_MANAGER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_COOKIE_MANAGER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefCookieManagerCppToC
+    : public CefCppToCRefCounted<CefCookieManagerCppToC,
+                                 CefCookieManager,
+                                 cef_cookie_manager_t> {
+ public:
+  CefCookieManagerCppToC();
+  virtual ~CefCookieManagerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_COOKIE_MANAGER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/cookie_visitor_cpptoc.cc b/src/libcef_dll/cpptoc/cookie_visitor_cpptoc.cc
new file mode 100644
index 0000000..247e4fc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cookie_visitor_cpptoc.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ac8389b701fc37f7aa4dbccff339d8a88b2a7741$
+//
+
+#include "libcef_dll/cpptoc/cookie_visitor_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK cookie_visitor_visit(struct _cef_cookie_visitor_t* self,
+                                      const struct _cef_cookie_t* cookie,
+                                      int count,
+                                      int total,
+                                      int* deleteCookie) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: cookie; type: struct_byref_const
+  DCHECK(cookie);
+  if (!cookie)
+    return 0;
+  // Verify param: deleteCookie; type: bool_byref
+  DCHECK(deleteCookie);
+  if (!deleteCookie)
+    return 0;
+
+  // Translate param: cookie; type: struct_byref_const
+  CefCookie cookieObj;
+  if (cookie)
+    cookieObj.Set(*cookie, false);
+  // Translate param: deleteCookie; type: bool_byref
+  bool deleteCookieBool = (deleteCookie && *deleteCookie) ? true : false;
+
+  // Execute
+  bool _retval = CefCookieVisitorCppToC::Get(self)->Visit(
+      cookieObj, count, total, deleteCookieBool);
+
+  // Restore param: deleteCookie; type: bool_byref
+  if (deleteCookie)
+    *deleteCookie = deleteCookieBool ? true : false;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCookieVisitorCppToC::CefCookieVisitorCppToC() {
+  GetStruct()->visit = cookie_visitor_visit;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCookieVisitorCppToC::~CefCookieVisitorCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefCookieVisitor> CefCppToCRefCounted<
+    CefCookieVisitorCppToC,
+    CefCookieVisitor,
+    cef_cookie_visitor_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_cookie_visitor_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefCookieVisitorCppToC,
+                                   CefCookieVisitor,
+                                   cef_cookie_visitor_t>::kWrapperType =
+    WT_COOKIE_VISITOR;
diff --git a/src/libcef_dll/cpptoc/cookie_visitor_cpptoc.h b/src/libcef_dll/cpptoc/cookie_visitor_cpptoc.h
new file mode 100644
index 0000000..e56f6e1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cookie_visitor_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=379f307a22ad62e86672608e048c255734b3f94a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_COOKIE_VISITOR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_COOKIE_VISITOR_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefCookieVisitorCppToC
+    : public CefCppToCRefCounted<CefCookieVisitorCppToC,
+                                 CefCookieVisitor,
+                                 cef_cookie_visitor_t> {
+ public:
+  CefCookieVisitorCppToC();
+  virtual ~CefCookieVisitorCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_COOKIE_VISITOR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/cpptoc_ref_counted.h b/src/libcef_dll/cpptoc/cpptoc_ref_counted.h
new file mode 100644
index 0000000..dd90a79
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cpptoc_ref_counted.h
@@ -0,0 +1,203 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_CPPTOC_REF_COUNTED_H_
+#define CEF_LIBCEF_DLL_CPPTOC_CPPTOC_REF_COUNTED_H_
+#pragma once
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/wrapper_types.h"
+
+// Wrap a C++ class with a C structure.  This is used when the class
+// implementation exists on this side of the DLL boundary but will have methods
+// called from the other side of the DLL boundary.
+template <class ClassName, class BaseName, class StructName>
+class CefCppToCRefCounted : public CefBaseRefCounted {
+ public:
+  // Create a new wrapper instance and associated structure reference for
+  // passing an object instance the other side.
+  static StructName* Wrap(CefRefPtr<BaseName> c) {
+    if (!c.get())
+      return nullptr;
+
+    // Wrap our object with the CefCppToCRefCounted class.
+    ClassName* wrapper = new ClassName();
+    wrapper->wrapper_struct_.object_ = c.get();
+    // Add a reference to our wrapper object that will be released once our
+    // structure arrives on the other side.
+    wrapper->AddRef();
+    // Return the structure pointer that can now be passed to the other side.
+    return wrapper->GetStruct();
+  }
+
+  // Retrieve the underlying object instance for a structure reference passed
+  // back from the other side.
+  static CefRefPtr<BaseName> Unwrap(StructName* s) {
+    if (!s)
+      return nullptr;
+
+    // Cast our structure to the wrapper structure type.
+    WrapperStruct* wrapperStruct = GetWrapperStruct(s);
+
+    // If the type does not match this object then we need to unwrap as the
+    // derived type.
+    if (wrapperStruct->type_ != kWrapperType)
+      return UnwrapDerived(wrapperStruct->type_, s);
+
+    // Add the underlying object instance to a smart pointer.
+    CefRefPtr<BaseName> objectPtr(wrapperStruct->object_);
+    // Release the reference to our wrapper object that was added before the
+    // structure was passed back to us.
+    wrapperStruct->wrapper_->Release();
+    // Return the underlying object instance.
+    return objectPtr;
+  }
+
+  // Retrieve the underlying object instance from our own structure reference
+  // when the reference is passed as the required first parameter of a C API
+  // function call. No explicit reference counting is done in this case.
+  static CefRefPtr<BaseName> Get(StructName* s) {
+    DCHECK(s);
+    WrapperStruct* wrapperStruct = GetWrapperStruct(s);
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+    return wrapperStruct->object_;
+  }
+
+  // If returning the structure across the DLL boundary you should call
+  // AddRef() on this CefCppToCRefCounted object. On the other side of the DLL
+  // boundary, call UnderlyingRelease() on the wrapping CefCToCpp object.
+  StructName* GetStruct() { return &wrapper_struct_.struct_; }
+
+  // CefBaseRefCounted methods increment/decrement reference counts on both this
+  // object and the underlying wrapper class.
+  void AddRef() const {
+    UnderlyingAddRef();
+    ref_count_.AddRef();
+  }
+  bool Release() const {
+    UnderlyingRelease();
+    if (ref_count_.Release()) {
+      delete this;
+      return true;
+    }
+    return false;
+  }
+  bool HasOneRef() const { return UnderlyingHasOneRef(); }
+  bool HasAtLeastOneRef() const { return UnderlyingHasAtLeastOneRef(); }
+
+ protected:
+  CefCppToCRefCounted() {
+    wrapper_struct_.type_ = kWrapperType;
+    wrapper_struct_.wrapper_ = this;
+    memset(GetStruct(), 0, sizeof(StructName));
+
+    cef_base_ref_counted_t* base =
+        reinterpret_cast<cef_base_ref_counted_t*>(GetStruct());
+    base->size = sizeof(StructName);
+    base->add_ref = struct_add_ref;
+    base->release = struct_release;
+    base->has_one_ref = struct_has_one_ref;
+    base->has_at_least_one_ref = struct_has_at_least_one_ref;
+  }
+
+  virtual ~CefCppToCRefCounted() {}
+
+ private:
+  // Used to associate this wrapper object, the underlying object instance and
+  // the structure that will be passed to the other side.
+  struct WrapperStruct {
+    CefWrapperType type_;
+    BaseName* object_;
+    CefCppToCRefCounted<ClassName, BaseName, StructName>* wrapper_;
+    StructName struct_;
+  };
+
+  static WrapperStruct* GetWrapperStruct(StructName* s) {
+    // Offset using the WrapperStruct size instead of individual member sizes
+    // to avoid problems due to platform/compiler differences in structure
+    // padding.
+    return reinterpret_cast<WrapperStruct*>(
+        reinterpret_cast<char*>(s) -
+        (sizeof(WrapperStruct) - sizeof(StructName)));
+  }
+
+  // Unwrap as the derived type.
+  static CefRefPtr<BaseName> UnwrapDerived(CefWrapperType type, StructName* s);
+
+  // Increment/decrement reference counts on only the underlying class.
+  void UnderlyingAddRef() const { wrapper_struct_.object_->AddRef(); }
+  void UnderlyingRelease() const { wrapper_struct_.object_->Release(); }
+  bool UnderlyingHasOneRef() const {
+    return wrapper_struct_.object_->HasOneRef();
+  }
+  bool UnderlyingHasAtLeastOneRef() const {
+    return wrapper_struct_.object_->HasAtLeastOneRef();
+  }
+
+  static void CEF_CALLBACK struct_add_ref(cef_base_ref_counted_t* base) {
+    DCHECK(base);
+    if (!base)
+      return;
+
+    WrapperStruct* wrapperStruct =
+        GetWrapperStruct(reinterpret_cast<StructName*>(base));
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+
+    wrapperStruct->wrapper_->AddRef();
+  }
+
+  static int CEF_CALLBACK struct_release(cef_base_ref_counted_t* base) {
+    DCHECK(base);
+    if (!base)
+      return 0;
+
+    WrapperStruct* wrapperStruct =
+        GetWrapperStruct(reinterpret_cast<StructName*>(base));
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+
+    return wrapperStruct->wrapper_->Release();
+  }
+
+  static int CEF_CALLBACK struct_has_one_ref(cef_base_ref_counted_t* base) {
+    DCHECK(base);
+    if (!base)
+      return 0;
+
+    WrapperStruct* wrapperStruct =
+        GetWrapperStruct(reinterpret_cast<StructName*>(base));
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+
+    return wrapperStruct->wrapper_->HasOneRef();
+  }
+
+  static int CEF_CALLBACK
+  struct_has_at_least_one_ref(cef_base_ref_counted_t* base) {
+    DCHECK(base);
+    if (!base)
+      return 0;
+
+    WrapperStruct* wrapperStruct =
+        GetWrapperStruct(reinterpret_cast<StructName*>(base));
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+
+    return wrapperStruct->wrapper_->HasAtLeastOneRef();
+  }
+
+  WrapperStruct wrapper_struct_;
+  CefRefCount ref_count_;
+
+  static CefWrapperType kWrapperType;
+
+  DISALLOW_COPY_AND_ASSIGN(CefCppToCRefCounted);
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_CPPTOC_REF_COUNTED_H_
diff --git a/src/libcef_dll/cpptoc/cpptoc_scoped.h b/src/libcef_dll/cpptoc/cpptoc_scoped.h
new file mode 100644
index 0000000..b552404
--- /dev/null
+++ b/src/libcef_dll/cpptoc/cpptoc_scoped.h
@@ -0,0 +1,228 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_CPPTOC_SCOPED_H_
+#define CEF_LIBCEF_DLL_CPPTOC_CPPTOC_SCOPED_H_
+#pragma once
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/ptr_util.h"
+#include "libcef_dll/wrapper_types.h"
+
+// Wrap a C++ class with a C structure. This is used when the class
+// implementation exists on this side of the DLL boundary but will have methods
+// called from the other side of the DLL boundary.
+template <class ClassName, class BaseName, class StructName>
+class CefCppToCScoped : public CefBaseScoped {
+ public:
+  // Create a new wrapper instance and associated structure reference for
+  // passing an object instance the other side. The wrapper object will be
+  // deleted when |del| is called on the associated structure. The wrapped
+  // object will be deleted when the wrapper object is deleted. For example:
+  //
+  // void MyMethod(CefOwnPtr<MyType> obj) {
+  //   my_method(MyTypeCppToC::WrapOwn(obj));
+  // }
+  //
+  // void my_method(my_type_t* struct) {
+  //   // Delete the MyTypeCppToC wrapper and the owned MyType object.
+  //   struct->del(struct);
+  // }
+  static StructName* WrapOwn(CefOwnPtr<BaseName> c) {
+    if (!c)
+      return nullptr;
+
+    // Wrap our object with the CefCppToC class.
+    ClassName* wrapper = new ClassName();
+    wrapper->Initialize(c.release(), true);
+
+    // Return the structure pointer that can now be passed to the other side.
+    return wrapper->GetStruct();
+  }
+
+  // Create a new wrapper instance and associated structure reference for
+  // passing an object instance to the other side. The wrapper object is owned
+  // by the caller. The wrapped object is unowned and must outlive the wrapper.
+  // For example:
+  //
+  // void MyMethod(MyType* obj) {
+  //   CefOwnPtr<MyTypeCppToC> MyTypeWrapper = MyTypeCppToC::WrapRaw(obj);
+  //   my_method(MyTypeWrapper->GetStruct());
+  //   // MyTypeWrapper is deleted when MyMethod() goes out of scope.
+  // }
+  //
+  // void my_method(my_type_t* struct) {
+  //   // Access |struct| here but you can't delete it.
+  // }
+  static CefOwnPtr<ClassName> WrapRaw(CefRawPtr<BaseName> c) {
+    if (!c)
+      return CefOwnPtr<ClassName>();
+
+    // Wrap our object with the CefCppToC class.
+    ClassName* wrapper = new ClassName();
+    wrapper->Initialize(c, false);
+
+    // Return the owned wrapper object.
+    return CefOwnPtr<ClassName>(wrapper);
+  }
+
+  // Retrieve the underlying object instance for a structure reference passed
+  // back from the other side. The caller takes ownership of the object. For
+  // example:
+  //
+  // void my_method(my_type_t* struct) {
+  //   CefOwnPtr<MyType> MyTypePtr = MyTypeCppToC::UnwrapOwn(struct);
+  //   // |struct| has been deleted and should no longer be accessed.
+  // }
+  static CefOwnPtr<BaseName> UnwrapOwn(StructName* s) {
+    if (!s)
+      return CefOwnPtr<BaseName>();
+
+    // Cast our structure to the wrapper structure type.
+    WrapperStruct* wrapperStruct = GetWrapperStruct(s);
+
+    // If the type does not match this object then we need to unwrap as the
+    // derived type.
+    if (wrapperStruct->type_ != kWrapperType)
+      return UnwrapDerivedOwn(wrapperStruct->type_, s);
+
+    // We should own the underlying object currently.
+    DCHECK(wrapperStruct->wrapper_->owned_);
+    DCHECK(wrapperStruct->object_);
+
+    // We're giving up ownership of the underlying object. Clear the pointer so
+    // it doesn't get deleted.
+    BaseName* object = wrapperStruct->object_;
+    wrapperStruct->object_ = nullptr;
+
+    delete wrapperStruct->wrapper_;
+
+    // Return the underlying object instance.
+    return CefOwnPtr<BaseName>(object);
+  }
+
+  // Retrieve the underlying object instance for a structure reference passed
+  // back from the other side. Ownership does not change. For example:
+  //
+  // void my_method(my_type_t* struct) {
+  //   CefRawPtr<MyType> MyTypePtr = MyTypeCppToC::UnwrapRaw(struct);
+  //   // |struct| is still valid.
+  // }
+  static CefRawPtr<BaseName> UnwrapRaw(StructName* s) {
+    if (!s)
+      return nullptr;
+
+    // Cast our structure to the wrapper structure type.
+    WrapperStruct* wrapperStruct = GetWrapperStruct(s);
+
+    // If the type does not match this object then we need to unwrap as the
+    // derived type.
+    if (wrapperStruct->type_ != kWrapperType)
+      return UnwrapDerivedRaw(wrapperStruct->type_, s);
+
+    // Return the underlying object instance.
+    return wrapperStruct->object_;
+  }
+
+  // Retrieve the same side wrapper associated with the structure. Ownership
+  // does not change.
+  static ClassName* GetWrapper(StructName* s) {
+    DCHECK(s);
+    WrapperStruct* wrapperStruct = GetWrapperStruct(s);
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+    return static_cast<ClassName*>(wrapperStruct->wrapper_);
+  }
+
+  // Retrieve the underlying object instance from our own structure reference
+  // when the reference is passed as the required first parameter of a C API
+  // function call. Ownership of the object does not change.
+  static BaseName* Get(StructName* s) {
+    DCHECK(s);
+    WrapperStruct* wrapperStruct = GetWrapperStruct(s);
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+    return wrapperStruct->object_;
+  }
+
+  // If returning the structure across the DLL boundary you should call
+  // AddRef() on this CefCppToC object.  On the other side of the DLL boundary,
+  // call UnderlyingRelease() on the wrapping CefCToCpp object.
+  StructName* GetStruct() { return &wrapper_struct_.struct_; }
+
+ protected:
+  CefCppToCScoped() {
+    wrapper_struct_.type_ = kWrapperType;
+    wrapper_struct_.wrapper_ = this;
+    memset(GetStruct(), 0, sizeof(StructName));
+  }
+
+  virtual ~CefCppToCScoped() {
+    // Only delete the underlying object if we own it.
+    if (owned_ && wrapper_struct_.object_)
+      delete wrapper_struct_.object_;
+  }
+
+ private:
+  // Used to associate this wrapper object, the underlying object instance and
+  // the structure that will be passed to the other side.
+  struct WrapperStruct {
+    CefWrapperType type_;
+    BaseName* object_;
+    CefCppToCScoped<ClassName, BaseName, StructName>* wrapper_;
+    StructName struct_;
+  };
+
+  void Initialize(BaseName* obj, bool owned) {
+    wrapper_struct_.object_ = obj;
+    owned_ = owned;
+
+    cef_base_scoped_t* base = reinterpret_cast<cef_base_scoped_t*>(GetStruct());
+    base->size = sizeof(StructName);
+    if (owned)
+      base->del = struct_del;
+  }
+
+  static WrapperStruct* GetWrapperStruct(StructName* s) {
+    // Offset using the WrapperStruct size instead of individual member sizes
+    // to avoid problems due to platform/compiler differences in structure
+    // padding.
+    return reinterpret_cast<WrapperStruct*>(
+        reinterpret_cast<char*>(s) -
+        (sizeof(WrapperStruct) - sizeof(StructName)));
+  }
+
+  // Unwrap as the derived type.
+  static CefOwnPtr<BaseName> UnwrapDerivedOwn(CefWrapperType type,
+                                              StructName* s);
+  static CefRawPtr<BaseName> UnwrapDerivedRaw(CefWrapperType type,
+                                              StructName* s);
+
+  static void CEF_CALLBACK struct_del(cef_base_scoped_t* base) {
+    DCHECK(base);
+    if (!base)
+      return;
+
+    WrapperStruct* wrapperStruct =
+        GetWrapperStruct(reinterpret_cast<StructName*>(base));
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+
+    // Should only be deleting wrappers that own the underlying object.
+    DCHECK(wrapperStruct->wrapper_->owned_);
+    delete wrapperStruct->wrapper_;
+  }
+
+  WrapperStruct wrapper_struct_;
+  bool owned_;
+
+  static CefWrapperType kWrapperType;
+
+  DISALLOW_COPY_AND_ASSIGN(CefCppToCScoped);
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_CPPTOC_SCOPED_H_
diff --git a/src/libcef_dll/cpptoc/delete_cookies_callback_cpptoc.cc b/src/libcef_dll/cpptoc/delete_cookies_callback_cpptoc.cc
new file mode 100644
index 0000000..9b54837
--- /dev/null
+++ b/src/libcef_dll/cpptoc/delete_cookies_callback_cpptoc.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3c8aba8fdaad1ff7034735a44c5cb811b89384c9$
+//
+
+#include "libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+delete_cookies_callback_on_complete(struct _cef_delete_cookies_callback_t* self,
+                                    int num_deleted) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefDeleteCookiesCallbackCppToC::Get(self)->OnComplete(num_deleted);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDeleteCookiesCallbackCppToC::CefDeleteCookiesCallbackCppToC() {
+  GetStruct()->on_complete = delete_cookies_callback_on_complete;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDeleteCookiesCallbackCppToC::~CefDeleteCookiesCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDeleteCookiesCallback> CefCppToCRefCounted<
+    CefDeleteCookiesCallbackCppToC,
+    CefDeleteCookiesCallback,
+    cef_delete_cookies_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                  cef_delete_cookies_callback_t*
+                                                      s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefDeleteCookiesCallbackCppToC,
+                        CefDeleteCookiesCallback,
+                        cef_delete_cookies_callback_t>::kWrapperType =
+        WT_DELETE_COOKIES_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h b/src/libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h
new file mode 100644
index 0000000..5a2cbfb
--- /dev/null
+++ b/src/libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0a9aa85a1bc30f434486b029e85a2e4f775fa875$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DELETE_COOKIES_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DELETE_COOKIES_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDeleteCookiesCallbackCppToC
+    : public CefCppToCRefCounted<CefDeleteCookiesCallbackCppToC,
+                                 CefDeleteCookiesCallback,
+                                 cef_delete_cookies_callback_t> {
+ public:
+  CefDeleteCookiesCallbackCppToC();
+  virtual ~CefDeleteCookiesCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DELETE_COOKIES_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.cc b/src/libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.cc
new file mode 100644
index 0000000..600796a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c4a7b4d679f1f2ed47fc31df3e2099962d5cb9d6$
+//
+
+#include "libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK dev_tools_message_observer_on_dev_tools_message(
+    struct _cef_dev_tools_message_observer_t* self,
+    cef_browser_t* browser,
+    const void* message,
+    size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return 0;
+
+  // Execute
+  bool _retval = CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsMessage(
+      CefBrowserCToCpp::Wrap(browser), message, message_size);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_method_result(
+    struct _cef_dev_tools_message_observer_t* self,
+    cef_browser_t* browser,
+    int message_id,
+    int success,
+    const void* result,
+    size_t result_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Unverified params: result
+
+  // Execute
+  CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsMethodResult(
+      CefBrowserCToCpp::Wrap(browser), message_id, success ? true : false,
+      result, result_size);
+}
+
+void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_event(
+    struct _cef_dev_tools_message_observer_t* self,
+    cef_browser_t* browser,
+    const cef_string_t* method,
+    const void* params,
+    size_t params_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: method; type: string_byref_const
+  DCHECK(method);
+  if (!method)
+    return;
+  // Unverified params: params
+
+  // Execute
+  CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsEvent(
+      CefBrowserCToCpp::Wrap(browser), CefString(method), params, params_size);
+}
+
+void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_agent_attached(
+    struct _cef_dev_tools_message_observer_t* self,
+    cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsAgentAttached(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_agent_detached(
+    struct _cef_dev_tools_message_observer_t* self,
+    cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsAgentDetached(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDevToolsMessageObserverCppToC::CefDevToolsMessageObserverCppToC() {
+  GetStruct()->on_dev_tools_message =
+      dev_tools_message_observer_on_dev_tools_message;
+  GetStruct()->on_dev_tools_method_result =
+      dev_tools_message_observer_on_dev_tools_method_result;
+  GetStruct()->on_dev_tools_event =
+      dev_tools_message_observer_on_dev_tools_event;
+  GetStruct()->on_dev_tools_agent_attached =
+      dev_tools_message_observer_on_dev_tools_agent_attached;
+  GetStruct()->on_dev_tools_agent_detached =
+      dev_tools_message_observer_on_dev_tools_agent_detached;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDevToolsMessageObserverCppToC::~CefDevToolsMessageObserverCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDevToolsMessageObserver>
+CefCppToCRefCounted<CefDevToolsMessageObserverCppToC,
+                    CefDevToolsMessageObserver,
+                    cef_dev_tools_message_observer_t>::
+    UnwrapDerived(CefWrapperType type, cef_dev_tools_message_observer_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefDevToolsMessageObserverCppToC,
+                        CefDevToolsMessageObserver,
+                        cef_dev_tools_message_observer_t>::kWrapperType =
+        WT_DEV_TOOLS_MESSAGE_OBSERVER;
diff --git a/src/libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h b/src/libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h
new file mode 100644
index 0000000..9d39fd1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d48f93f3817dab7daf0e30a6fa94b1a173356383$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DEV_TOOLS_MESSAGE_OBSERVER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DEV_TOOLS_MESSAGE_OBSERVER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_devtools_message_observer_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_devtools_message_observer.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDevToolsMessageObserverCppToC
+    : public CefCppToCRefCounted<CefDevToolsMessageObserverCppToC,
+                                 CefDevToolsMessageObserver,
+                                 cef_dev_tools_message_observer_t> {
+ public:
+  CefDevToolsMessageObserverCppToC();
+  virtual ~CefDevToolsMessageObserverCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DEV_TOOLS_MESSAGE_OBSERVER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/dialog_handler_cpptoc.cc b/src/libcef_dll/cpptoc/dialog_handler_cpptoc.cc
new file mode 100644
index 0000000..9837428
--- /dev/null
+++ b/src/libcef_dll/cpptoc/dialog_handler_cpptoc.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=67f9af743f81c6fa7e4f1cdf7e626115fc83333f$
+//
+
+#include "libcef_dll/cpptoc/dialog_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/file_dialog_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+dialog_handler_on_file_dialog(struct _cef_dialog_handler_t* self,
+                              cef_browser_t* browser,
+                              cef_file_dialog_mode_t mode,
+                              const cef_string_t* title,
+                              const cef_string_t* default_file_path,
+                              cef_string_list_t accept_filters,
+                              int selected_accept_filter,
+                              cef_file_dialog_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+  // Unverified params: title, default_file_path, accept_filters
+
+  // Translate param: accept_filters; type: string_vec_byref_const
+  std::vector<CefString> accept_filtersList;
+  transfer_string_list_contents(accept_filters, accept_filtersList);
+
+  // Execute
+  bool _retval = CefDialogHandlerCppToC::Get(self)->OnFileDialog(
+      CefBrowserCToCpp::Wrap(browser), mode, CefString(title),
+      CefString(default_file_path), accept_filtersList, selected_accept_filter,
+      CefFileDialogCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDialogHandlerCppToC::CefDialogHandlerCppToC() {
+  GetStruct()->on_file_dialog = dialog_handler_on_file_dialog;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDialogHandlerCppToC::~CefDialogHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDialogHandler> CefCppToCRefCounted<
+    CefDialogHandlerCppToC,
+    CefDialogHandler,
+    cef_dialog_handler_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_dialog_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDialogHandlerCppToC,
+                                   CefDialogHandler,
+                                   cef_dialog_handler_t>::kWrapperType =
+    WT_DIALOG_HANDLER;
diff --git a/src/libcef_dll/cpptoc/dialog_handler_cpptoc.h b/src/libcef_dll/cpptoc/dialog_handler_cpptoc.h
new file mode 100644
index 0000000..ae4f430
--- /dev/null
+++ b/src/libcef_dll/cpptoc/dialog_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8d7433f549cc246d02da12b5aec26e040ceecb79$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DIALOG_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DIALOG_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_dialog_handler_capi.h"
+#include "include/cef_dialog_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDialogHandlerCppToC
+    : public CefCppToCRefCounted<CefDialogHandlerCppToC,
+                                 CefDialogHandler,
+                                 cef_dialog_handler_t> {
+ public:
+  CefDialogHandlerCppToC();
+  virtual ~CefDialogHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DIALOG_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/dictionary_value_cpptoc.cc b/src/libcef_dll/cpptoc/dictionary_value_cpptoc.cc
new file mode 100644
index 0000000..e5fd825
--- /dev/null
+++ b/src/libcef_dll/cpptoc/dictionary_value_cpptoc.cc
@@ -0,0 +1,747 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ad265f41a490c88e7b4fd64022fcf78eddb8cc35$
+//
+
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/list_value_cpptoc.h"
+#include "libcef_dll/cpptoc/value_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_dictionary_value_t* cef_dictionary_value_create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefDictionaryValue> _retval = CefDictionaryValue::Create();
+
+  // Return type: refptr_same
+  return CefDictionaryValueCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+dictionary_value_is_valid(struct _cef_dictionary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_is_owned(struct _cef_dictionary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->IsOwned();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_is_read_only(struct _cef_dictionary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_is_same(struct _cef_dictionary_value_t* self,
+                         struct _cef_dictionary_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->IsSame(
+      CefDictionaryValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_is_equal(struct _cef_dictionary_value_t* self,
+                          struct _cef_dictionary_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->IsEqual(
+      CefDictionaryValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_dictionary_value_t* CEF_CALLBACK
+dictionary_value_copy(struct _cef_dictionary_value_t* self,
+                      int exclude_empty_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDictionaryValue> _retval =
+      CefDictionaryValueCppToC::Get(self)->Copy(exclude_empty_children ? true
+                                                                       : false);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCppToC::Wrap(_retval);
+}
+
+size_t CEF_CALLBACK
+dictionary_value_get_size(struct _cef_dictionary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefDictionaryValueCppToC::Get(self)->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_clear(struct _cef_dictionary_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->Clear();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_has_key(struct _cef_dictionary_value_t* self,
+                                          const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->HasKey(CefString(key));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_get_keys(struct _cef_dictionary_value_t* self,
+                                           cef_string_list_t keys) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: keys; type: string_vec_byref
+  DCHECK(keys);
+  if (!keys)
+    return 0;
+
+  // Translate param: keys; type: string_vec_byref
+  std::vector<CefString> keysList;
+  transfer_string_list_contents(keys, keysList);
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->GetKeys(keysList);
+
+  // Restore param: keys; type: string_vec_byref
+  cef_string_list_clear(keys);
+  transfer_string_list_contents(keysList, keys);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_remove(struct _cef_dictionary_value_t* self,
+                                         const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->Remove(CefString(key));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_value_type_t CEF_CALLBACK
+dictionary_value_get_type(struct _cef_dictionary_value_t* self,
+                          const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return VTYPE_INVALID;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return VTYPE_INVALID;
+
+  // Execute
+  cef_value_type_t _retval =
+      CefDictionaryValueCppToC::Get(self)->GetType(CefString(key));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_value_t* CEF_CALLBACK
+dictionary_value_get_value(struct _cef_dictionary_value_t* self,
+                           const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefValue> _retval =
+      CefDictionaryValueCppToC::Get(self)->GetValue(CefString(key));
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK dictionary_value_get_bool(struct _cef_dictionary_value_t* self,
+                                           const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->GetBool(CefString(key));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_get_int(struct _cef_dictionary_value_t* self,
+                                          const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  int _retval = CefDictionaryValueCppToC::Get(self)->GetInt(CefString(key));
+
+  // Return type: simple
+  return _retval;
+}
+
+double CEF_CALLBACK
+dictionary_value_get_double(struct _cef_dictionary_value_t* self,
+                            const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  double _retval =
+      CefDictionaryValueCppToC::Get(self)->GetDouble(CefString(key));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+dictionary_value_get_string(struct _cef_dictionary_value_t* self,
+                            const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefDictionaryValueCppToC::Get(self)->GetString(CefString(key));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_binary_value_t* CEF_CALLBACK
+dictionary_value_get_binary(struct _cef_dictionary_value_t* self,
+                            const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval =
+      CefDictionaryValueCppToC::Get(self)->GetBinary(CefString(key));
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+struct _cef_dictionary_value_t* CEF_CALLBACK
+dictionary_value_get_dictionary(struct _cef_dictionary_value_t* self,
+                                const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDictionaryValue> _retval =
+      CefDictionaryValueCppToC::Get(self)->GetDictionary(CefString(key));
+
+  // Return type: refptr_same
+  return CefDictionaryValueCppToC::Wrap(_retval);
+}
+
+struct _cef_list_value_t* CEF_CALLBACK
+dictionary_value_get_list(struct _cef_dictionary_value_t* self,
+                          const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefListValue> _retval =
+      CefDictionaryValueCppToC::Get(self)->GetList(CefString(key));
+
+  // Return type: refptr_same
+  return CefListValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK
+dictionary_value_set_value(struct _cef_dictionary_value_t* self,
+                           const cef_string_t* key,
+                           cef_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->SetValue(
+      CefString(key), CefValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_set_null(struct _cef_dictionary_value_t* self,
+                                           const cef_string_t* key) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->SetNull(CefString(key));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_set_bool(struct _cef_dictionary_value_t* self,
+                                           const cef_string_t* key,
+                                           int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->SetBool(
+      CefString(key), value ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_set_int(struct _cef_dictionary_value_t* self,
+                                          const cef_string_t* key,
+                                          int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefDictionaryValueCppToC::Get(self)->SetInt(CefString(key), value);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_set_double(struct _cef_dictionary_value_t* self,
+                            const cef_string_t* key,
+                            double value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefDictionaryValueCppToC::Get(self)->SetDouble(CefString(key), value);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_set_string(struct _cef_dictionary_value_t* self,
+                            const cef_string_t* key,
+                            const cef_string_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+  // Unverified params: value
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->SetString(
+      CefString(key), CefString(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_set_binary(struct _cef_dictionary_value_t* self,
+                            const cef_string_t* key,
+                            cef_binary_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->SetBinary(
+      CefString(key), CefBinaryValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+dictionary_value_set_dictionary(struct _cef_dictionary_value_t* self,
+                                const cef_string_t* key,
+                                struct _cef_dictionary_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->SetDictionary(
+      CefString(key), CefDictionaryValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK dictionary_value_set_list(struct _cef_dictionary_value_t* self,
+                                           const cef_string_t* key,
+                                           struct _cef_list_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefDictionaryValueCppToC::Get(self)->SetList(
+      CefString(key), CefListValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDictionaryValueCppToC::CefDictionaryValueCppToC() {
+  GetStruct()->is_valid = dictionary_value_is_valid;
+  GetStruct()->is_owned = dictionary_value_is_owned;
+  GetStruct()->is_read_only = dictionary_value_is_read_only;
+  GetStruct()->is_same = dictionary_value_is_same;
+  GetStruct()->is_equal = dictionary_value_is_equal;
+  GetStruct()->copy = dictionary_value_copy;
+  GetStruct()->get_size = dictionary_value_get_size;
+  GetStruct()->clear = dictionary_value_clear;
+  GetStruct()->has_key = dictionary_value_has_key;
+  GetStruct()->get_keys = dictionary_value_get_keys;
+  GetStruct()->remove = dictionary_value_remove;
+  GetStruct()->get_type = dictionary_value_get_type;
+  GetStruct()->get_value = dictionary_value_get_value;
+  GetStruct()->get_bool = dictionary_value_get_bool;
+  GetStruct()->get_int = dictionary_value_get_int;
+  GetStruct()->get_double = dictionary_value_get_double;
+  GetStruct()->get_string = dictionary_value_get_string;
+  GetStruct()->get_binary = dictionary_value_get_binary;
+  GetStruct()->get_dictionary = dictionary_value_get_dictionary;
+  GetStruct()->get_list = dictionary_value_get_list;
+  GetStruct()->set_value = dictionary_value_set_value;
+  GetStruct()->set_null = dictionary_value_set_null;
+  GetStruct()->set_bool = dictionary_value_set_bool;
+  GetStruct()->set_int = dictionary_value_set_int;
+  GetStruct()->set_double = dictionary_value_set_double;
+  GetStruct()->set_string = dictionary_value_set_string;
+  GetStruct()->set_binary = dictionary_value_set_binary;
+  GetStruct()->set_dictionary = dictionary_value_set_dictionary;
+  GetStruct()->set_list = dictionary_value_set_list;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDictionaryValueCppToC::~CefDictionaryValueCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDictionaryValue> CefCppToCRefCounted<
+    CefDictionaryValueCppToC,
+    CefDictionaryValue,
+    cef_dictionary_value_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_dictionary_value_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDictionaryValueCppToC,
+                                   CefDictionaryValue,
+                                   cef_dictionary_value_t>::kWrapperType =
+    WT_DICTIONARY_VALUE;
diff --git a/src/libcef_dll/cpptoc/dictionary_value_cpptoc.h b/src/libcef_dll/cpptoc/dictionary_value_cpptoc.h
new file mode 100644
index 0000000..4fe5e78
--- /dev/null
+++ b/src/libcef_dll/cpptoc/dictionary_value_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=083bd5ba100fb82c10769e8e073b9d931914fb03$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DICTIONARY_VALUE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DICTIONARY_VALUE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefDictionaryValueCppToC
+    : public CefCppToCRefCounted<CefDictionaryValueCppToC,
+                                 CefDictionaryValue,
+                                 cef_dictionary_value_t> {
+ public:
+  CefDictionaryValueCppToC();
+  virtual ~CefDictionaryValueCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DICTIONARY_VALUE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/display_handler_cpptoc.cc b/src/libcef_dll/cpptoc/display_handler_cpptoc.cc
new file mode 100644
index 0000000..469c359
--- /dev/null
+++ b/src/libcef_dll/cpptoc/display_handler_cpptoc.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1e329026cfad131337e794e5501367604f62fdb6$
+//
+
+#include "libcef_dll/cpptoc/display_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+display_handler_on_address_change(struct _cef_display_handler_t* self,
+                                  cef_browser_t* browser,
+                                  struct _cef_frame_t* frame,
+                                  const cef_string_t* url) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return;
+
+  // Execute
+  CefDisplayHandlerCppToC::Get(self)->OnAddressChange(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefString(url));
+}
+
+void CEF_CALLBACK
+display_handler_on_title_change(struct _cef_display_handler_t* self,
+                                cef_browser_t* browser,
+                                const cef_string_t* title) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Unverified params: title
+
+  // Execute
+  CefDisplayHandlerCppToC::Get(self)->OnTitleChange(
+      CefBrowserCToCpp::Wrap(browser), CefString(title));
+}
+
+void CEF_CALLBACK
+display_handler_on_favicon_urlchange(struct _cef_display_handler_t* self,
+                                     cef_browser_t* browser,
+                                     cef_string_list_t icon_urls) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Unverified params: icon_urls
+
+  // Translate param: icon_urls; type: string_vec_byref_const
+  std::vector<CefString> icon_urlsList;
+  transfer_string_list_contents(icon_urls, icon_urlsList);
+
+  // Execute
+  CefDisplayHandlerCppToC::Get(self)->OnFaviconURLChange(
+      CefBrowserCToCpp::Wrap(browser), icon_urlsList);
+}
+
+void CEF_CALLBACK
+display_handler_on_fullscreen_mode_change(struct _cef_display_handler_t* self,
+                                          cef_browser_t* browser,
+                                          int fullscreen) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefDisplayHandlerCppToC::Get(self)->OnFullscreenModeChange(
+      CefBrowserCToCpp::Wrap(browser), fullscreen ? true : false);
+}
+
+int CEF_CALLBACK display_handler_on_tooltip(struct _cef_display_handler_t* self,
+                                            cef_browser_t* browser,
+                                            cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Unverified params: text
+
+  // Translate param: text; type: string_byref
+  CefString textStr(text);
+
+  // Execute
+  bool _retval = CefDisplayHandlerCppToC::Get(self)->OnTooltip(
+      CefBrowserCToCpp::Wrap(browser), textStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+display_handler_on_status_message(struct _cef_display_handler_t* self,
+                                  cef_browser_t* browser,
+                                  const cef_string_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Unverified params: value
+
+  // Execute
+  CefDisplayHandlerCppToC::Get(self)->OnStatusMessage(
+      CefBrowserCToCpp::Wrap(browser), CefString(value));
+}
+
+int CEF_CALLBACK
+display_handler_on_console_message(struct _cef_display_handler_t* self,
+                                   cef_browser_t* browser,
+                                   cef_log_severity_t level,
+                                   const cef_string_t* message,
+                                   const cef_string_t* source,
+                                   int line) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Unverified params: message, source
+
+  // Execute
+  bool _retval = CefDisplayHandlerCppToC::Get(self)->OnConsoleMessage(
+      CefBrowserCToCpp::Wrap(browser), level, CefString(message),
+      CefString(source), line);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+display_handler_on_auto_resize(struct _cef_display_handler_t* self,
+                               cef_browser_t* browser,
+                               const cef_size_t* new_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: new_size; type: simple_byref_const
+  DCHECK(new_size);
+  if (!new_size)
+    return 0;
+
+  // Translate param: new_size; type: simple_byref_const
+  CefSize new_sizeVal = new_size ? *new_size : CefSize();
+
+  // Execute
+  bool _retval = CefDisplayHandlerCppToC::Get(self)->OnAutoResize(
+      CefBrowserCToCpp::Wrap(browser), new_sizeVal);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+display_handler_on_loading_progress_change(struct _cef_display_handler_t* self,
+                                           cef_browser_t* browser,
+                                           double progress) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefDisplayHandlerCppToC::Get(self)->OnLoadingProgressChange(
+      CefBrowserCToCpp::Wrap(browser), progress);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDisplayHandlerCppToC::CefDisplayHandlerCppToC() {
+  GetStruct()->on_address_change = display_handler_on_address_change;
+  GetStruct()->on_title_change = display_handler_on_title_change;
+  GetStruct()->on_favicon_urlchange = display_handler_on_favicon_urlchange;
+  GetStruct()->on_fullscreen_mode_change =
+      display_handler_on_fullscreen_mode_change;
+  GetStruct()->on_tooltip = display_handler_on_tooltip;
+  GetStruct()->on_status_message = display_handler_on_status_message;
+  GetStruct()->on_console_message = display_handler_on_console_message;
+  GetStruct()->on_auto_resize = display_handler_on_auto_resize;
+  GetStruct()->on_loading_progress_change =
+      display_handler_on_loading_progress_change;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDisplayHandlerCppToC::~CefDisplayHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDisplayHandler> CefCppToCRefCounted<
+    CefDisplayHandlerCppToC,
+    CefDisplayHandler,
+    cef_display_handler_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_display_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDisplayHandlerCppToC,
+                                   CefDisplayHandler,
+                                   cef_display_handler_t>::kWrapperType =
+    WT_DISPLAY_HANDLER;
diff --git a/src/libcef_dll/cpptoc/display_handler_cpptoc.h b/src/libcef_dll/cpptoc/display_handler_cpptoc.h
new file mode 100644
index 0000000..10d7d50
--- /dev/null
+++ b/src/libcef_dll/cpptoc/display_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0ee7315137ad11a49a7bad20aee747ba3910517e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DISPLAY_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DISPLAY_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_display_handler_capi.h"
+#include "include/cef_display_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDisplayHandlerCppToC
+    : public CefCppToCRefCounted<CefDisplayHandlerCppToC,
+                                 CefDisplayHandler,
+                                 cef_display_handler_t> {
+ public:
+  CefDisplayHandlerCppToC();
+  virtual ~CefDisplayHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DISPLAY_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/domdocument_cpptoc.cc b/src/libcef_dll/cpptoc/domdocument_cpptoc.cc
new file mode 100644
index 0000000..5e451fe
--- /dev/null
+++ b/src/libcef_dll/cpptoc/domdocument_cpptoc.cc
@@ -0,0 +1,314 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=312e73a1e276ba266640055115c6b8f78920fcbb$
+//
+
+#include "libcef_dll/cpptoc/domdocument_cpptoc.h"
+#include "libcef_dll/cpptoc/domnode_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_dom_document_type_t CEF_CALLBACK
+domdocument_get_type(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return DOM_DOCUMENT_TYPE_UNKNOWN;
+
+  // Execute
+  cef_dom_document_type_t _retval = CefDOMDocumentCppToC::Get(self)->GetType();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domdocument_get_document(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval =
+      CefDOMDocumentCppToC::Get(self)->GetDocument();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domdocument_get_body(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval = CefDOMDocumentCppToC::Get(self)->GetBody();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domdocument_get_head(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval = CefDOMDocumentCppToC::Get(self)->GetHead();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domdocument_get_title(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMDocumentCppToC::Get(self)->GetTitle();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domdocument_get_element_by_id(struct _cef_domdocument_t* self,
+                              const cef_string_t* id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: id; type: string_byref_const
+  DCHECK(id);
+  if (!id)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval =
+      CefDOMDocumentCppToC::Get(self)->GetElementById(CefString(id));
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domdocument_get_focused_node(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval =
+      CefDOMDocumentCppToC::Get(self)->GetFocusedNode();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK domdocument_has_selection(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMDocumentCppToC::Get(self)->HasSelection();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+domdocument_get_selection_start_offset(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefDOMDocumentCppToC::Get(self)->GetSelectionStartOffset();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+domdocument_get_selection_end_offset(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefDOMDocumentCppToC::Get(self)->GetSelectionEndOffset();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domdocument_get_selection_as_markup(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMDocumentCppToC::Get(self)->GetSelectionAsMarkup();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domdocument_get_selection_as_text(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMDocumentCppToC::Get(self)->GetSelectionAsText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domdocument_get_base_url(struct _cef_domdocument_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMDocumentCppToC::Get(self)->GetBaseURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domdocument_get_complete_url(struct _cef_domdocument_t* self,
+                             const cef_string_t* partialURL) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: partialURL; type: string_byref_const
+  DCHECK(partialURL);
+  if (!partialURL)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefDOMDocumentCppToC::Get(self)->GetCompleteURL(CefString(partialURL));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDOMDocumentCppToC::CefDOMDocumentCppToC() {
+  GetStruct()->get_type = domdocument_get_type;
+  GetStruct()->get_document = domdocument_get_document;
+  GetStruct()->get_body = domdocument_get_body;
+  GetStruct()->get_head = domdocument_get_head;
+  GetStruct()->get_title = domdocument_get_title;
+  GetStruct()->get_element_by_id = domdocument_get_element_by_id;
+  GetStruct()->get_focused_node = domdocument_get_focused_node;
+  GetStruct()->has_selection = domdocument_has_selection;
+  GetStruct()->get_selection_start_offset =
+      domdocument_get_selection_start_offset;
+  GetStruct()->get_selection_end_offset = domdocument_get_selection_end_offset;
+  GetStruct()->get_selection_as_markup = domdocument_get_selection_as_markup;
+  GetStruct()->get_selection_as_text = domdocument_get_selection_as_text;
+  GetStruct()->get_base_url = domdocument_get_base_url;
+  GetStruct()->get_complete_url = domdocument_get_complete_url;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDOMDocumentCppToC::~CefDOMDocumentCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDOMDocument>
+CefCppToCRefCounted<CefDOMDocumentCppToC, CefDOMDocument, cef_domdocument_t>::
+    UnwrapDerived(CefWrapperType type, cef_domdocument_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDOMDocumentCppToC,
+                                   CefDOMDocument,
+                                   cef_domdocument_t>::kWrapperType =
+    WT_DOMDOCUMENT;
diff --git a/src/libcef_dll/cpptoc/domdocument_cpptoc.h b/src/libcef_dll/cpptoc/domdocument_cpptoc.h
new file mode 100644
index 0000000..1265756
--- /dev/null
+++ b/src/libcef_dll/cpptoc/domdocument_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f3031620487ad950d4d71c15cb9ee37ee2857000$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DOMDOCUMENT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DOMDOCUMENT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_dom_capi.h"
+#include "include/cef_dom.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefDOMDocumentCppToC : public CefCppToCRefCounted<CefDOMDocumentCppToC,
+                                                        CefDOMDocument,
+                                                        cef_domdocument_t> {
+ public:
+  CefDOMDocumentCppToC();
+  virtual ~CefDOMDocumentCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DOMDOCUMENT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/domnode_cpptoc.cc b/src/libcef_dll/cpptoc/domnode_cpptoc.cc
new file mode 100644
index 0000000..c1f334b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/domnode_cpptoc.cc
@@ -0,0 +1,552 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7931740b8c5860b0397dadc0208eb03098faa765$
+//
+
+#include "libcef_dll/cpptoc/domnode_cpptoc.h"
+#include "libcef_dll/cpptoc/domdocument_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_dom_node_type_t CEF_CALLBACK domnode_get_type(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return DOM_NODE_TYPE_UNSUPPORTED;
+
+  // Execute
+  cef_dom_node_type_t _retval = CefDOMNodeCppToC::Get(self)->GetType();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK domnode_is_text(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->IsText();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK domnode_is_element(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->IsElement();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK domnode_is_editable(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->IsEditable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK domnode_is_form_control_element(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->IsFormControlElement();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domnode_get_form_control_element_type(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMNodeCppToC::Get(self)->GetFormControlElementType();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK domnode_is_same(struct _cef_domnode_t* self,
+                                 struct _cef_domnode_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefDOMNodeCppToC::Get(self)->IsSame(CefDOMNodeCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domnode_get_name(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMNodeCppToC::Get(self)->GetName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domnode_get_value(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMNodeCppToC::Get(self)->GetValue();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK domnode_set_value(struct _cef_domnode_t* self,
+                                   const cef_string_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: string_byref_const
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->SetValue(CefString(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domnode_get_as_markup(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMNodeCppToC::Get(self)->GetAsMarkup();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_domdocument_t* CEF_CALLBACK
+domnode_get_document(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMDocument> _retval =
+      CefDOMNodeCppToC::Get(self)->GetDocument();
+
+  // Return type: refptr_same
+  return CefDOMDocumentCppToC::Wrap(_retval);
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domnode_get_parent(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval = CefDOMNodeCppToC::Get(self)->GetParent();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domnode_get_previous_sibling(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval =
+      CefDOMNodeCppToC::Get(self)->GetPreviousSibling();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domnode_get_next_sibling(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval = CefDOMNodeCppToC::Get(self)->GetNextSibling();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK domnode_has_children(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->HasChildren();
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domnode_get_first_child(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval = CefDOMNodeCppToC::Get(self)->GetFirstChild();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+struct _cef_domnode_t* CEF_CALLBACK
+domnode_get_last_child(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDOMNode> _retval = CefDOMNodeCppToC::Get(self)->GetLastChild();
+
+  // Return type: refptr_same
+  return CefDOMNodeCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domnode_get_element_tag_name(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMNodeCppToC::Get(self)->GetElementTagName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK domnode_has_element_attributes(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->HasElementAttributes();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK domnode_has_element_attribute(struct _cef_domnode_t* self,
+                                               const cef_string_t* attrName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: attrName; type: string_byref_const
+  DCHECK(attrName);
+  if (!attrName)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefDOMNodeCppToC::Get(self)->HasElementAttribute(CefString(attrName));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domnode_get_element_attribute(struct _cef_domnode_t* self,
+                              const cef_string_t* attrName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: attrName; type: string_byref_const
+  DCHECK(attrName);
+  if (!attrName)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefDOMNodeCppToC::Get(self)->GetElementAttribute(CefString(attrName));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK domnode_get_element_attributes(struct _cef_domnode_t* self,
+                                                 cef_string_map_t attrMap) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: attrMap; type: string_map_single_byref
+  DCHECK(attrMap);
+  if (!attrMap)
+    return;
+
+  // Translate param: attrMap; type: string_map_single_byref
+  std::map<CefString, CefString> attrMapMap;
+  transfer_string_map_contents(attrMap, attrMapMap);
+
+  // Execute
+  CefDOMNodeCppToC::Get(self)->GetElementAttributes(attrMapMap);
+
+  // Restore param: attrMap; type: string_map_single_byref
+  cef_string_map_clear(attrMap);
+  transfer_string_map_contents(attrMapMap, attrMap);
+}
+
+int CEF_CALLBACK domnode_set_element_attribute(struct _cef_domnode_t* self,
+                                               const cef_string_t* attrName,
+                                               const cef_string_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: attrName; type: string_byref_const
+  DCHECK(attrName);
+  if (!attrName)
+    return 0;
+  // Verify param: value; type: string_byref_const
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefDOMNodeCppToC::Get(self)->SetElementAttribute(
+      CefString(attrName), CefString(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+domnode_get_element_inner_text(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDOMNodeCppToC::Get(self)->GetElementInnerText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_rect_t CEF_CALLBACK
+domnode_get_element_bounds(struct _cef_domnode_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval = CefDOMNodeCppToC::Get(self)->GetElementBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDOMNodeCppToC::CefDOMNodeCppToC() {
+  GetStruct()->get_type = domnode_get_type;
+  GetStruct()->is_text = domnode_is_text;
+  GetStruct()->is_element = domnode_is_element;
+  GetStruct()->is_editable = domnode_is_editable;
+  GetStruct()->is_form_control_element = domnode_is_form_control_element;
+  GetStruct()->get_form_control_element_type =
+      domnode_get_form_control_element_type;
+  GetStruct()->is_same = domnode_is_same;
+  GetStruct()->get_name = domnode_get_name;
+  GetStruct()->get_value = domnode_get_value;
+  GetStruct()->set_value = domnode_set_value;
+  GetStruct()->get_as_markup = domnode_get_as_markup;
+  GetStruct()->get_document = domnode_get_document;
+  GetStruct()->get_parent = domnode_get_parent;
+  GetStruct()->get_previous_sibling = domnode_get_previous_sibling;
+  GetStruct()->get_next_sibling = domnode_get_next_sibling;
+  GetStruct()->has_children = domnode_has_children;
+  GetStruct()->get_first_child = domnode_get_first_child;
+  GetStruct()->get_last_child = domnode_get_last_child;
+  GetStruct()->get_element_tag_name = domnode_get_element_tag_name;
+  GetStruct()->has_element_attributes = domnode_has_element_attributes;
+  GetStruct()->has_element_attribute = domnode_has_element_attribute;
+  GetStruct()->get_element_attribute = domnode_get_element_attribute;
+  GetStruct()->get_element_attributes = domnode_get_element_attributes;
+  GetStruct()->set_element_attribute = domnode_set_element_attribute;
+  GetStruct()->get_element_inner_text = domnode_get_element_inner_text;
+  GetStruct()->get_element_bounds = domnode_get_element_bounds;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDOMNodeCppToC::~CefDOMNodeCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDOMNode>
+CefCppToCRefCounted<CefDOMNodeCppToC, CefDOMNode, cef_domnode_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_domnode_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDOMNodeCppToC,
+                                   CefDOMNode,
+                                   cef_domnode_t>::kWrapperType = WT_DOMNODE;
diff --git a/src/libcef_dll/cpptoc/domnode_cpptoc.h b/src/libcef_dll/cpptoc/domnode_cpptoc.h
new file mode 100644
index 0000000..080cdc4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/domnode_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2465c7db31ad903b30220188e139b5e2af213e11$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DOMNODE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DOMNODE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_dom_capi.h"
+#include "include/cef_dom.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefDOMNodeCppToC
+    : public CefCppToCRefCounted<CefDOMNodeCppToC, CefDOMNode, cef_domnode_t> {
+ public:
+  CefDOMNodeCppToC();
+  virtual ~CefDOMNodeCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DOMNODE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/domvisitor_cpptoc.cc b/src/libcef_dll/cpptoc/domvisitor_cpptoc.cc
new file mode 100644
index 0000000..acfd227
--- /dev/null
+++ b/src/libcef_dll/cpptoc/domvisitor_cpptoc.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d5685ce2cd2731697bf1bd0993b3754374a1a739$
+//
+
+#include "libcef_dll/cpptoc/domvisitor_cpptoc.h"
+#include "libcef_dll/ctocpp/domdocument_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK domvisitor_visit(struct _cef_domvisitor_t* self,
+                                   struct _cef_domdocument_t* document) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: document; type: refptr_diff
+  DCHECK(document);
+  if (!document)
+    return;
+
+  // Execute
+  CefDOMVisitorCppToC::Get(self)->Visit(CefDOMDocumentCToCpp::Wrap(document));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDOMVisitorCppToC::CefDOMVisitorCppToC() {
+  GetStruct()->visit = domvisitor_visit;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDOMVisitorCppToC::~CefDOMVisitorCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDOMVisitor>
+CefCppToCRefCounted<CefDOMVisitorCppToC, CefDOMVisitor, cef_domvisitor_t>::
+    UnwrapDerived(CefWrapperType type, cef_domvisitor_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDOMVisitorCppToC,
+                                   CefDOMVisitor,
+                                   cef_domvisitor_t>::kWrapperType =
+    WT_DOMVISITOR;
diff --git a/src/libcef_dll/cpptoc/domvisitor_cpptoc.h b/src/libcef_dll/cpptoc/domvisitor_cpptoc.h
new file mode 100644
index 0000000..03ee1bd
--- /dev/null
+++ b/src/libcef_dll/cpptoc/domvisitor_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=18ce927902e19740f0be8707b51ca4bb2296e5a5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DOMVISITOR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DOMVISITOR_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_dom_capi.h"
+#include "include/cef_dom.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDOMVisitorCppToC : public CefCppToCRefCounted<CefDOMVisitorCppToC,
+                                                       CefDOMVisitor,
+                                                       cef_domvisitor_t> {
+ public:
+  CefDOMVisitorCppToC();
+  virtual ~CefDOMVisitorCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DOMVISITOR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/download_handler_cpptoc.cc b/src/libcef_dll/cpptoc/download_handler_cpptoc.cc
new file mode 100644
index 0000000..4c191dc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_handler_cpptoc.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8ea9a11f03f3a303839fd15a0fd55fc2e0b9cd1c$
+//
+
+#include "libcef_dll/cpptoc/download_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/before_download_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/download_item_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/download_item_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+download_handler_on_before_download(struct _cef_download_handler_t* self,
+                                    cef_browser_t* browser,
+                                    struct _cef_download_item_t* download_item,
+                                    const cef_string_t* suggested_name,
+                                    cef_before_download_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: download_item; type: refptr_diff
+  DCHECK(download_item);
+  if (!download_item)
+    return;
+  // Verify param: suggested_name; type: string_byref_const
+  DCHECK(suggested_name);
+  if (!suggested_name)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefDownloadHandlerCppToC::Get(self)->OnBeforeDownload(
+      CefBrowserCToCpp::Wrap(browser),
+      CefDownloadItemCToCpp::Wrap(download_item), CefString(suggested_name),
+      CefBeforeDownloadCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK
+download_handler_on_download_updated(struct _cef_download_handler_t* self,
+                                     cef_browser_t* browser,
+                                     struct _cef_download_item_t* download_item,
+                                     cef_download_item_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: download_item; type: refptr_diff
+  DCHECK(download_item);
+  if (!download_item)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefDownloadHandlerCppToC::Get(self)->OnDownloadUpdated(
+      CefBrowserCToCpp::Wrap(browser),
+      CefDownloadItemCToCpp::Wrap(download_item),
+      CefDownloadItemCallbackCToCpp::Wrap(callback));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadHandlerCppToC::CefDownloadHandlerCppToC() {
+  GetStruct()->on_before_download = download_handler_on_before_download;
+  GetStruct()->on_download_updated = download_handler_on_download_updated;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadHandlerCppToC::~CefDownloadHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDownloadHandler> CefCppToCRefCounted<
+    CefDownloadHandlerCppToC,
+    CefDownloadHandler,
+    cef_download_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_download_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDownloadHandlerCppToC,
+                                   CefDownloadHandler,
+                                   cef_download_handler_t>::kWrapperType =
+    WT_DOWNLOAD_HANDLER;
diff --git a/src/libcef_dll/cpptoc/download_handler_cpptoc.h b/src/libcef_dll/cpptoc/download_handler_cpptoc.h
new file mode 100644
index 0000000..756f245
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=eba4baa6d1dfe09c59314d597d52fa3edd467ee3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_download_handler_capi.h"
+#include "include/cef_download_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDownloadHandlerCppToC
+    : public CefCppToCRefCounted<CefDownloadHandlerCppToC,
+                                 CefDownloadHandler,
+                                 cef_download_handler_t> {
+ public:
+  CefDownloadHandlerCppToC();
+  virtual ~CefDownloadHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/download_image_callback_cpptoc.cc b/src/libcef_dll/cpptoc/download_image_callback_cpptoc.cc
new file mode 100644
index 0000000..fa74575
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_image_callback_cpptoc.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c6a390757ebc1cef06464e09bf30a9c19a4e9687$
+//
+
+#include "libcef_dll/cpptoc/download_image_callback_cpptoc.h"
+#include "libcef_dll/ctocpp/image_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK download_image_callback_on_download_image_finished(
+    struct _cef_download_image_callback_t* self,
+    const cef_string_t* image_url,
+    int http_status_code,
+    struct _cef_image_t* image) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: image_url; type: string_byref_const
+  DCHECK(image_url);
+  if (!image_url)
+    return;
+  // Unverified params: image
+
+  // Execute
+  CefDownloadImageCallbackCppToC::Get(self)->OnDownloadImageFinished(
+      CefString(image_url), http_status_code, CefImageCToCpp::Wrap(image));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadImageCallbackCppToC::CefDownloadImageCallbackCppToC() {
+  GetStruct()->on_download_image_finished =
+      download_image_callback_on_download_image_finished;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadImageCallbackCppToC::~CefDownloadImageCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDownloadImageCallback> CefCppToCRefCounted<
+    CefDownloadImageCallbackCppToC,
+    CefDownloadImageCallback,
+    cef_download_image_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                  cef_download_image_callback_t*
+                                                      s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefDownloadImageCallbackCppToC,
+                        CefDownloadImageCallback,
+                        cef_download_image_callback_t>::kWrapperType =
+        WT_DOWNLOAD_IMAGE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/download_image_callback_cpptoc.h b/src/libcef_dll/cpptoc/download_image_callback_cpptoc.h
new file mode 100644
index 0000000..8294678
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_image_callback_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7571f82b08c4cf0abea130e73571449a0a85d656$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_IMAGE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_IMAGE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDownloadImageCallbackCppToC
+    : public CefCppToCRefCounted<CefDownloadImageCallbackCppToC,
+                                 CefDownloadImageCallback,
+                                 cef_download_image_callback_t> {
+ public:
+  CefDownloadImageCallbackCppToC();
+  virtual ~CefDownloadImageCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_IMAGE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/download_item_callback_cpptoc.cc b/src/libcef_dll/cpptoc/download_item_callback_cpptoc.cc
new file mode 100644
index 0000000..d4f5088
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_item_callback_cpptoc.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ee4e0abc682de9b25e143d2c685d46d2bb10f33d$
+//
+
+#include "libcef_dll/cpptoc/download_item_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+download_item_callback_cancel(struct _cef_download_item_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefDownloadItemCallbackCppToC::Get(self)->Cancel();
+}
+
+void CEF_CALLBACK
+download_item_callback_pause(struct _cef_download_item_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefDownloadItemCallbackCppToC::Get(self)->Pause();
+}
+
+void CEF_CALLBACK
+download_item_callback_resume(struct _cef_download_item_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefDownloadItemCallbackCppToC::Get(self)->Resume();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCallbackCppToC::CefDownloadItemCallbackCppToC() {
+  GetStruct()->cancel = download_item_callback_cancel;
+  GetStruct()->pause = download_item_callback_pause;
+  GetStruct()->resume = download_item_callback_resume;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCallbackCppToC::~CefDownloadItemCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDownloadItemCallback> CefCppToCRefCounted<
+    CefDownloadItemCallbackCppToC,
+    CefDownloadItemCallback,
+    cef_download_item_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                 cef_download_item_callback_t*
+                                                     s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDownloadItemCallbackCppToC,
+                                   CefDownloadItemCallback,
+                                   cef_download_item_callback_t>::kWrapperType =
+    WT_DOWNLOAD_ITEM_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/download_item_callback_cpptoc.h b/src/libcef_dll/cpptoc/download_item_callback_cpptoc.h
new file mode 100644
index 0000000..5f662c2
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_item_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9262bc6379b8626f3fa0c7dcda4520bde8376880$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_ITEM_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_ITEM_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_download_handler_capi.h"
+#include "include/cef_download_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefDownloadItemCallbackCppToC
+    : public CefCppToCRefCounted<CefDownloadItemCallbackCppToC,
+                                 CefDownloadItemCallback,
+                                 cef_download_item_callback_t> {
+ public:
+  CefDownloadItemCallbackCppToC();
+  virtual ~CefDownloadItemCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_ITEM_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/download_item_cpptoc.cc b/src/libcef_dll/cpptoc/download_item_cpptoc.cc
new file mode 100644
index 0000000..1fe98af
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_item_cpptoc.cc
@@ -0,0 +1,351 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=afaa0b2d6c7abf246c66ba853be937b6905074f1$
+//
+
+#include "libcef_dll/cpptoc/download_item_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK download_item_is_valid(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDownloadItemCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+download_item_is_in_progress(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDownloadItemCppToC::Get(self)->IsInProgress();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK download_item_is_complete(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDownloadItemCppToC::Get(self)->IsComplete();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK download_item_is_canceled(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDownloadItemCppToC::Get(self)->IsCanceled();
+
+  // Return type: bool
+  return _retval;
+}
+
+int64 CEF_CALLBACK
+download_item_get_current_speed(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefDownloadItemCppToC::Get(self)->GetCurrentSpeed();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+download_item_get_percent_complete(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefDownloadItemCppToC::Get(self)->GetPercentComplete();
+
+  // Return type: simple
+  return _retval;
+}
+
+int64 CEF_CALLBACK
+download_item_get_total_bytes(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefDownloadItemCppToC::Get(self)->GetTotalBytes();
+
+  // Return type: simple
+  return _retval;
+}
+
+int64 CEF_CALLBACK
+download_item_get_received_bytes(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefDownloadItemCppToC::Get(self)->GetReceivedBytes();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_time_t CEF_CALLBACK
+download_item_get_start_time(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefTime();
+
+  // Execute
+  cef_time_t _retval = CefDownloadItemCppToC::Get(self)->GetStartTime();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_time_t CEF_CALLBACK
+download_item_get_end_time(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefTime();
+
+  // Execute
+  cef_time_t _retval = CefDownloadItemCppToC::Get(self)->GetEndTime();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+download_item_get_full_path(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDownloadItemCppToC::Get(self)->GetFullPath();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+uint32 CEF_CALLBACK download_item_get_id(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  uint32 _retval = CefDownloadItemCppToC::Get(self)->GetId();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+download_item_get_url(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDownloadItemCppToC::Get(self)->GetURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+download_item_get_original_url(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDownloadItemCppToC::Get(self)->GetOriginalUrl();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+download_item_get_suggested_file_name(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDownloadItemCppToC::Get(self)->GetSuggestedFileName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+download_item_get_content_disposition(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDownloadItemCppToC::Get(self)->GetContentDisposition();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+download_item_get_mime_type(struct _cef_download_item_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDownloadItemCppToC::Get(self)->GetMimeType();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCppToC::CefDownloadItemCppToC() {
+  GetStruct()->is_valid = download_item_is_valid;
+  GetStruct()->is_in_progress = download_item_is_in_progress;
+  GetStruct()->is_complete = download_item_is_complete;
+  GetStruct()->is_canceled = download_item_is_canceled;
+  GetStruct()->get_current_speed = download_item_get_current_speed;
+  GetStruct()->get_percent_complete = download_item_get_percent_complete;
+  GetStruct()->get_total_bytes = download_item_get_total_bytes;
+  GetStruct()->get_received_bytes = download_item_get_received_bytes;
+  GetStruct()->get_start_time = download_item_get_start_time;
+  GetStruct()->get_end_time = download_item_get_end_time;
+  GetStruct()->get_full_path = download_item_get_full_path;
+  GetStruct()->get_id = download_item_get_id;
+  GetStruct()->get_url = download_item_get_url;
+  GetStruct()->get_original_url = download_item_get_original_url;
+  GetStruct()->get_suggested_file_name = download_item_get_suggested_file_name;
+  GetStruct()->get_content_disposition = download_item_get_content_disposition;
+  GetStruct()->get_mime_type = download_item_get_mime_type;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCppToC::~CefDownloadItemCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDownloadItem> CefCppToCRefCounted<
+    CefDownloadItemCppToC,
+    CefDownloadItem,
+    cef_download_item_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_download_item_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDownloadItemCppToC,
+                                   CefDownloadItem,
+                                   cef_download_item_t>::kWrapperType =
+    WT_DOWNLOAD_ITEM;
diff --git a/src/libcef_dll/cpptoc/download_item_cpptoc.h b/src/libcef_dll/cpptoc/download_item_cpptoc.h
new file mode 100644
index 0000000..596af38
--- /dev/null
+++ b/src/libcef_dll/cpptoc/download_item_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=528851326b066ec08ba504200046937f3f90e4c3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_ITEM_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_ITEM_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_download_item_capi.h"
+#include "include/cef_download_item.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefDownloadItemCppToC : public CefCppToCRefCounted<CefDownloadItemCppToC,
+                                                         CefDownloadItem,
+                                                         cef_download_item_t> {
+ public:
+  CefDownloadItemCppToC();
+  virtual ~CefDownloadItemCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DOWNLOAD_ITEM_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/drag_data_cpptoc.cc b/src/libcef_dll/cpptoc/drag_data_cpptoc.cc
new file mode 100644
index 0000000..3adeda7
--- /dev/null
+++ b/src/libcef_dll/cpptoc/drag_data_cpptoc.cc
@@ -0,0 +1,513 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ce7b68a16458e0d2c6c31e49a5f9896f8f3f1c8b$
+//
+
+#include "libcef_dll/cpptoc/drag_data_cpptoc.h"
+#include "libcef_dll/cpptoc/image_cpptoc.h"
+#include "libcef_dll/cpptoc/stream_writer_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_drag_data_t* cef_drag_data_create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefDragData> _retval = CefDragData::Create();
+
+  // Return type: refptr_same
+  return CefDragDataCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+struct _cef_drag_data_t* CEF_CALLBACK
+drag_data_clone(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDragData> _retval = CefDragDataCppToC::Get(self)->Clone();
+
+  // Return type: refptr_same
+  return CefDragDataCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK drag_data_is_read_only(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDragDataCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK drag_data_is_link(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDragDataCppToC::Get(self)->IsLink();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK drag_data_is_fragment(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDragDataCppToC::Get(self)->IsFragment();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK drag_data_is_file(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDragDataCppToC::Get(self)->IsFile();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+drag_data_get_link_url(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDragDataCppToC::Get(self)->GetLinkURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+drag_data_get_link_title(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDragDataCppToC::Get(self)->GetLinkTitle();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+drag_data_get_link_metadata(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDragDataCppToC::Get(self)->GetLinkMetadata();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+drag_data_get_fragment_text(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDragDataCppToC::Get(self)->GetFragmentText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+drag_data_get_fragment_html(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDragDataCppToC::Get(self)->GetFragmentHtml();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+drag_data_get_fragment_base_url(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDragDataCppToC::Get(self)->GetFragmentBaseURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+drag_data_get_file_name(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefDragDataCppToC::Get(self)->GetFileName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+size_t CEF_CALLBACK
+drag_data_get_file_contents(struct _cef_drag_data_t* self,
+                            struct _cef_stream_writer_t* writer) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: writer
+
+  // Execute
+  size_t _retval = CefDragDataCppToC::Get(self)->GetFileContents(
+      CefStreamWriterCppToC::Unwrap(writer));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK drag_data_get_file_names(struct _cef_drag_data_t* self,
+                                          cef_string_list_t names) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: names; type: string_vec_byref
+  DCHECK(names);
+  if (!names)
+    return 0;
+
+  // Translate param: names; type: string_vec_byref
+  std::vector<CefString> namesList;
+  transfer_string_list_contents(names, namesList);
+
+  // Execute
+  bool _retval = CefDragDataCppToC::Get(self)->GetFileNames(namesList);
+
+  // Restore param: names; type: string_vec_byref
+  cef_string_list_clear(names);
+  transfer_string_list_contents(namesList, names);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK drag_data_set_link_url(struct _cef_drag_data_t* self,
+                                         const cef_string_t* url) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: url
+
+  // Execute
+  CefDragDataCppToC::Get(self)->SetLinkURL(CefString(url));
+}
+
+void CEF_CALLBACK drag_data_set_link_title(struct _cef_drag_data_t* self,
+                                           const cef_string_t* title) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: title
+
+  // Execute
+  CefDragDataCppToC::Get(self)->SetLinkTitle(CefString(title));
+}
+
+void CEF_CALLBACK drag_data_set_link_metadata(struct _cef_drag_data_t* self,
+                                              const cef_string_t* data) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: data
+
+  // Execute
+  CefDragDataCppToC::Get(self)->SetLinkMetadata(CefString(data));
+}
+
+void CEF_CALLBACK drag_data_set_fragment_text(struct _cef_drag_data_t* self,
+                                              const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: text
+
+  // Execute
+  CefDragDataCppToC::Get(self)->SetFragmentText(CefString(text));
+}
+
+void CEF_CALLBACK drag_data_set_fragment_html(struct _cef_drag_data_t* self,
+                                              const cef_string_t* html) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: html
+
+  // Execute
+  CefDragDataCppToC::Get(self)->SetFragmentHtml(CefString(html));
+}
+
+void CEF_CALLBACK
+drag_data_set_fragment_base_url(struct _cef_drag_data_t* self,
+                                const cef_string_t* base_url) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: base_url
+
+  // Execute
+  CefDragDataCppToC::Get(self)->SetFragmentBaseURL(CefString(base_url));
+}
+
+void CEF_CALLBACK drag_data_reset_file_contents(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefDragDataCppToC::Get(self)->ResetFileContents();
+}
+
+void CEF_CALLBACK drag_data_add_file(struct _cef_drag_data_t* self,
+                                     const cef_string_t* path,
+                                     const cef_string_t* display_name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+  // Unverified params: display_name
+
+  // Execute
+  CefDragDataCppToC::Get(self)->AddFile(CefString(path),
+                                        CefString(display_name));
+}
+
+struct _cef_image_t* CEF_CALLBACK
+drag_data_get_image(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefImage> _retval = CefDragDataCppToC::Get(self)->GetImage();
+
+  // Return type: refptr_same
+  return CefImageCppToC::Wrap(_retval);
+}
+
+cef_point_t CEF_CALLBACK
+drag_data_get_image_hotspot(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval = CefDragDataCppToC::Get(self)->GetImageHotspot();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK drag_data_has_image(struct _cef_drag_data_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefDragDataCppToC::Get(self)->HasImage();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDragDataCppToC::CefDragDataCppToC() {
+  GetStruct()->clone = drag_data_clone;
+  GetStruct()->is_read_only = drag_data_is_read_only;
+  GetStruct()->is_link = drag_data_is_link;
+  GetStruct()->is_fragment = drag_data_is_fragment;
+  GetStruct()->is_file = drag_data_is_file;
+  GetStruct()->get_link_url = drag_data_get_link_url;
+  GetStruct()->get_link_title = drag_data_get_link_title;
+  GetStruct()->get_link_metadata = drag_data_get_link_metadata;
+  GetStruct()->get_fragment_text = drag_data_get_fragment_text;
+  GetStruct()->get_fragment_html = drag_data_get_fragment_html;
+  GetStruct()->get_fragment_base_url = drag_data_get_fragment_base_url;
+  GetStruct()->get_file_name = drag_data_get_file_name;
+  GetStruct()->get_file_contents = drag_data_get_file_contents;
+  GetStruct()->get_file_names = drag_data_get_file_names;
+  GetStruct()->set_link_url = drag_data_set_link_url;
+  GetStruct()->set_link_title = drag_data_set_link_title;
+  GetStruct()->set_link_metadata = drag_data_set_link_metadata;
+  GetStruct()->set_fragment_text = drag_data_set_fragment_text;
+  GetStruct()->set_fragment_html = drag_data_set_fragment_html;
+  GetStruct()->set_fragment_base_url = drag_data_set_fragment_base_url;
+  GetStruct()->reset_file_contents = drag_data_reset_file_contents;
+  GetStruct()->add_file = drag_data_add_file;
+  GetStruct()->get_image = drag_data_get_image;
+  GetStruct()->get_image_hotspot = drag_data_get_image_hotspot;
+  GetStruct()->has_image = drag_data_has_image;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDragDataCppToC::~CefDragDataCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDragData>
+CefCppToCRefCounted<CefDragDataCppToC, CefDragData, cef_drag_data_t>::
+    UnwrapDerived(CefWrapperType type, cef_drag_data_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDragDataCppToC,
+                                   CefDragData,
+                                   cef_drag_data_t>::kWrapperType =
+    WT_DRAG_DATA;
diff --git a/src/libcef_dll/cpptoc/drag_data_cpptoc.h b/src/libcef_dll/cpptoc/drag_data_cpptoc.h
new file mode 100644
index 0000000..5fae231
--- /dev/null
+++ b/src/libcef_dll/cpptoc/drag_data_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ef1484f9c3402407bb30e660f30223baa14ca978$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DRAG_DATA_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DRAG_DATA_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_drag_data_capi.h"
+#include "include/cef_drag_data.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefDragDataCppToC : public CefCppToCRefCounted<CefDragDataCppToC,
+                                                     CefDragData,
+                                                     cef_drag_data_t> {
+ public:
+  CefDragDataCppToC();
+  virtual ~CefDragDataCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DRAG_DATA_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/drag_handler_cpptoc.cc b/src/libcef_dll/cpptoc/drag_handler_cpptoc.cc
new file mode 100644
index 0000000..cfb5296
--- /dev/null
+++ b/src/libcef_dll/cpptoc/drag_handler_cpptoc.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0eb91a630975532db7316d0452b43190f01a2a8c$
+//
+
+#include "libcef_dll/cpptoc/drag_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/drag_data_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK drag_handler_on_drag_enter(struct _cef_drag_handler_t* self,
+                                            cef_browser_t* browser,
+                                            cef_drag_data_t* dragData,
+                                            cef_drag_operations_mask_t mask) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: dragData; type: refptr_diff
+  DCHECK(dragData);
+  if (!dragData)
+    return 0;
+
+  // Execute
+  bool _retval = CefDragHandlerCppToC::Get(self)->OnDragEnter(
+      CefBrowserCToCpp::Wrap(browser), CefDragDataCToCpp::Wrap(dragData), mask);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK drag_handler_on_draggable_regions_changed(
+    struct _cef_drag_handler_t* self,
+    cef_browser_t* browser,
+    struct _cef_frame_t* frame,
+    size_t regionsCount,
+    cef_draggable_region_t const* regions) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Verify param: regions; type: simple_vec_byref_const
+  DCHECK(regionsCount == 0 || regions);
+  if (regionsCount > 0 && !regions)
+    return;
+
+  // Translate param: regions; type: simple_vec_byref_const
+  std::vector<CefDraggableRegion> regionsList;
+  if (regionsCount > 0) {
+    for (size_t i = 0; i < regionsCount; ++i) {
+      CefDraggableRegion regionsVal = regions[i];
+      regionsList.push_back(regionsVal);
+    }
+  }
+
+  // Execute
+  CefDragHandlerCppToC::Get(self)->OnDraggableRegionsChanged(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      regionsList);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDragHandlerCppToC::CefDragHandlerCppToC() {
+  GetStruct()->on_drag_enter = drag_handler_on_drag_enter;
+  GetStruct()->on_draggable_regions_changed =
+      drag_handler_on_draggable_regions_changed;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDragHandlerCppToC::~CefDragHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDragHandler>
+CefCppToCRefCounted<CefDragHandlerCppToC, CefDragHandler, cef_drag_handler_t>::
+    UnwrapDerived(CefWrapperType type, cef_drag_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDragHandlerCppToC,
+                                   CefDragHandler,
+                                   cef_drag_handler_t>::kWrapperType =
+    WT_DRAG_HANDLER;
diff --git a/src/libcef_dll/cpptoc/drag_handler_cpptoc.h b/src/libcef_dll/cpptoc/drag_handler_cpptoc.h
new file mode 100644
index 0000000..3f055fe
--- /dev/null
+++ b/src/libcef_dll/cpptoc/drag_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=39f1e6f246b89d6fd6fdabaf5b3281d5c89cd745$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_DRAG_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_DRAG_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_drag_handler_capi.h"
+#include "include/cef_drag_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDragHandlerCppToC : public CefCppToCRefCounted<CefDragHandlerCppToC,
+                                                        CefDragHandler,
+                                                        cef_drag_handler_t> {
+ public:
+  CefDragHandlerCppToC();
+  virtual ~CefDragHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_DRAG_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/end_tracing_callback_cpptoc.cc b/src/libcef_dll/cpptoc/end_tracing_callback_cpptoc.cc
new file mode 100644
index 0000000..65e87b8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/end_tracing_callback_cpptoc.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=335d8035333e24d5fd8ab4a89279bca09b2629b0$
+//
+
+#include "libcef_dll/cpptoc/end_tracing_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK end_tracing_callback_on_end_tracing_complete(
+    struct _cef_end_tracing_callback_t* self,
+    const cef_string_t* tracing_file) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: tracing_file; type: string_byref_const
+  DCHECK(tracing_file);
+  if (!tracing_file)
+    return;
+
+  // Execute
+  CefEndTracingCallbackCppToC::Get(self)->OnEndTracingComplete(
+      CefString(tracing_file));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefEndTracingCallbackCppToC::CefEndTracingCallbackCppToC() {
+  GetStruct()->on_end_tracing_complete =
+      end_tracing_callback_on_end_tracing_complete;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefEndTracingCallbackCppToC::~CefEndTracingCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefEndTracingCallback> CefCppToCRefCounted<
+    CefEndTracingCallbackCppToC,
+    CefEndTracingCallback,
+    cef_end_tracing_callback_t>::UnwrapDerived(CefWrapperType type,
+                                               cef_end_tracing_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefEndTracingCallbackCppToC,
+                                   CefEndTracingCallback,
+                                   cef_end_tracing_callback_t>::kWrapperType =
+    WT_END_TRACING_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/end_tracing_callback_cpptoc.h b/src/libcef_dll/cpptoc/end_tracing_callback_cpptoc.h
new file mode 100644
index 0000000..33e7a87
--- /dev/null
+++ b/src/libcef_dll/cpptoc/end_tracing_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1651e2c4c6f69e8617844128e3c13a33698e5994$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_END_TRACING_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_END_TRACING_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_trace_capi.h"
+#include "include/cef_trace.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefEndTracingCallbackCppToC
+    : public CefCppToCRefCounted<CefEndTracingCallbackCppToC,
+                                 CefEndTracingCallback,
+                                 cef_end_tracing_callback_t> {
+ public:
+  CefEndTracingCallbackCppToC();
+  virtual ~CefEndTracingCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_END_TRACING_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/extension_cpptoc.cc b/src/libcef_dll/cpptoc/extension_cpptoc.cc
new file mode 100644
index 0000000..b9e6f01
--- /dev/null
+++ b/src/libcef_dll/cpptoc/extension_cpptoc.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0ba733877b885661d95e5767f05a4ce943982bbc$
+//
+
+#include "libcef_dll/cpptoc/extension_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/request_context_cpptoc.h"
+#include "libcef_dll/ctocpp/extension_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+extension_get_identifier(struct _cef_extension_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefExtensionCppToC::Get(self)->GetIdentifier();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+extension_get_path(struct _cef_extension_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefExtensionCppToC::Get(self)->GetPath();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+struct _cef_dictionary_value_t* CEF_CALLBACK
+extension_get_manifest(struct _cef_extension_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDictionaryValue> _retval =
+      CefExtensionCppToC::Get(self)->GetManifest();
+
+  // Return type: refptr_same
+  return CefDictionaryValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK extension_is_same(struct _cef_extension_t* self,
+                                   struct _cef_extension_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefExtensionCppToC::Get(self)->IsSame(CefExtensionCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_extension_handler_t* CEF_CALLBACK
+extension_get_handler(struct _cef_extension_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefExtensionHandler> _retval =
+      CefExtensionCppToC::Get(self)->GetHandler();
+
+  // Return type: refptr_diff
+  return CefExtensionHandlerCToCpp::Unwrap(_retval);
+}
+
+struct _cef_request_context_t* CEF_CALLBACK
+extension_get_loader_context(struct _cef_extension_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRequestContext> _retval =
+      CefExtensionCppToC::Get(self)->GetLoaderContext();
+
+  // Return type: refptr_same
+  return CefRequestContextCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK extension_is_loaded(struct _cef_extension_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefExtensionCppToC::Get(self)->IsLoaded();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK extension_unload(struct _cef_extension_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefExtensionCppToC::Get(self)->Unload();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefExtensionCppToC::CefExtensionCppToC() {
+  GetStruct()->get_identifier = extension_get_identifier;
+  GetStruct()->get_path = extension_get_path;
+  GetStruct()->get_manifest = extension_get_manifest;
+  GetStruct()->is_same = extension_is_same;
+  GetStruct()->get_handler = extension_get_handler;
+  GetStruct()->get_loader_context = extension_get_loader_context;
+  GetStruct()->is_loaded = extension_is_loaded;
+  GetStruct()->unload = extension_unload;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefExtensionCppToC::~CefExtensionCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefExtension>
+CefCppToCRefCounted<CefExtensionCppToC, CefExtension, cef_extension_t>::
+    UnwrapDerived(CefWrapperType type, cef_extension_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefExtensionCppToC,
+                                   CefExtension,
+                                   cef_extension_t>::kWrapperType =
+    WT_EXTENSION;
diff --git a/src/libcef_dll/cpptoc/extension_cpptoc.h b/src/libcef_dll/cpptoc/extension_cpptoc.h
new file mode 100644
index 0000000..c4ad833
--- /dev/null
+++ b/src/libcef_dll/cpptoc/extension_cpptoc.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4482d892bdd649dbd6d103afbc76f9718c6cc163$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_EXTENSION_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_EXTENSION_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_extension_capi.h"
+#include "include/capi/cef_extension_handler_capi.h"
+#include "include/capi/cef_request_context_capi.h"
+#include "include/cef_extension.h"
+#include "include/cef_extension_handler.h"
+#include "include/cef_request_context.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefExtensionCppToC : public CefCppToCRefCounted<CefExtensionCppToC,
+                                                      CefExtension,
+                                                      cef_extension_t> {
+ public:
+  CefExtensionCppToC();
+  virtual ~CefExtensionCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_EXTENSION_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/extension_handler_cpptoc.cc b/src/libcef_dll/cpptoc/extension_handler_cpptoc.cc
new file mode 100644
index 0000000..a338dce
--- /dev/null
+++ b/src/libcef_dll/cpptoc/extension_handler_cpptoc.cc
@@ -0,0 +1,376 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ccd5999e0c5b68720a67e185c2ae1cf46da77963$
+//
+
+#include "libcef_dll/cpptoc/extension_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/client_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/extension_ctocpp.h"
+#include "libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK extension_handler_on_extension_load_failed(
+    struct _cef_extension_handler_t* self,
+    cef_errorcode_t result) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefExtensionHandlerCppToC::Get(self)->OnExtensionLoadFailed(result);
+}
+
+void CEF_CALLBACK
+extension_handler_on_extension_loaded(struct _cef_extension_handler_t* self,
+                                      cef_extension_t* extension) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension);
+  if (!extension)
+    return;
+
+  // Execute
+  CefExtensionHandlerCppToC::Get(self)->OnExtensionLoaded(
+      CefExtensionCToCpp::Wrap(extension));
+}
+
+void CEF_CALLBACK
+extension_handler_on_extension_unloaded(struct _cef_extension_handler_t* self,
+                                        cef_extension_t* extension) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension);
+  if (!extension)
+    return;
+
+  // Execute
+  CefExtensionHandlerCppToC::Get(self)->OnExtensionUnloaded(
+      CefExtensionCToCpp::Wrap(extension));
+}
+
+int CEF_CALLBACK extension_handler_on_before_background_browser(
+    struct _cef_extension_handler_t* self,
+    cef_extension_t* extension,
+    const cef_string_t* url,
+    cef_client_t** client,
+    struct _cef_browser_settings_t* settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension);
+  if (!extension)
+    return 0;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return 0;
+  // Verify param: client; type: refptr_same_byref
+  DCHECK(client);
+  if (!client)
+    return 0;
+  // Verify param: settings; type: struct_byref
+  DCHECK(settings);
+  if (!settings)
+    return 0;
+
+  // Translate param: client; type: refptr_same_byref
+  CefRefPtr<CefClient> clientPtr;
+  if (client && *client)
+    clientPtr = CefClientCppToC::Unwrap(*client);
+  CefClient* clientOrig = clientPtr.get();
+  // Translate param: settings; type: struct_byref
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.AttachTo(*settings);
+
+  // Execute
+  bool _retval =
+      CefExtensionHandlerCppToC::Get(self)->OnBeforeBackgroundBrowser(
+          CefExtensionCToCpp::Wrap(extension), CefString(url), clientPtr,
+          settingsObj);
+
+  // Restore param: client; type: refptr_same_byref
+  if (client) {
+    if (clientPtr.get()) {
+      if (clientPtr.get() != clientOrig) {
+        *client = CefClientCppToC::Wrap(clientPtr);
+      }
+    } else {
+      *client = nullptr;
+    }
+  }
+  // Restore param: settings; type: struct_byref
+  if (settings)
+    settingsObj.DetachTo(*settings);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+extension_handler_on_before_browser(struct _cef_extension_handler_t* self,
+                                    cef_extension_t* extension,
+                                    cef_browser_t* browser,
+                                    cef_browser_t* active_browser,
+                                    int index,
+                                    const cef_string_t* url,
+                                    int active,
+                                    cef_window_info_t* windowInfo,
+                                    cef_client_t** client,
+                                    struct _cef_browser_settings_t* settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension);
+  if (!extension)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: active_browser; type: refptr_diff
+  DCHECK(active_browser);
+  if (!active_browser)
+    return 0;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return 0;
+  // Verify param: windowInfo; type: struct_byref
+  DCHECK(windowInfo);
+  if (!windowInfo)
+    return 0;
+  // Verify param: client; type: refptr_same_byref
+  DCHECK(client);
+  if (!client)
+    return 0;
+  // Verify param: settings; type: struct_byref
+  DCHECK(settings);
+  if (!settings)
+    return 0;
+
+  // Translate param: windowInfo; type: struct_byref
+  CefWindowInfo windowInfoObj;
+  if (windowInfo)
+    windowInfoObj.AttachTo(*windowInfo);
+  // Translate param: client; type: refptr_same_byref
+  CefRefPtr<CefClient> clientPtr;
+  if (client && *client)
+    clientPtr = CefClientCppToC::Unwrap(*client);
+  CefClient* clientOrig = clientPtr.get();
+  // Translate param: settings; type: struct_byref
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.AttachTo(*settings);
+
+  // Execute
+  bool _retval = CefExtensionHandlerCppToC::Get(self)->OnBeforeBrowser(
+      CefExtensionCToCpp::Wrap(extension), CefBrowserCToCpp::Wrap(browser),
+      CefBrowserCToCpp::Wrap(active_browser), index, CefString(url),
+      active ? true : false, windowInfoObj, clientPtr, settingsObj);
+
+  // Restore param: windowInfo; type: struct_byref
+  if (windowInfo)
+    windowInfoObj.DetachTo(*windowInfo);
+  // Restore param: client; type: refptr_same_byref
+  if (client) {
+    if (clientPtr.get()) {
+      if (clientPtr.get() != clientOrig) {
+        *client = CefClientCppToC::Wrap(clientPtr);
+      }
+    } else {
+      *client = nullptr;
+    }
+  }
+  // Restore param: settings; type: struct_byref
+  if (settings)
+    settingsObj.DetachTo(*settings);
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_browser_t* CEF_CALLBACK
+extension_handler_get_active_browser(struct _cef_extension_handler_t* self,
+                                     cef_extension_t* extension,
+                                     cef_browser_t* browser,
+                                     int include_incognito) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension);
+  if (!extension)
+    return NULL;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowser> _retval =
+      CefExtensionHandlerCppToC::Get(self)->GetActiveBrowser(
+          CefExtensionCToCpp::Wrap(extension), CefBrowserCToCpp::Wrap(browser),
+          include_incognito ? true : false);
+
+  // Return type: refptr_diff
+  return CefBrowserCToCpp::Unwrap(_retval);
+}
+
+int CEF_CALLBACK
+extension_handler_can_access_browser(struct _cef_extension_handler_t* self,
+                                     cef_extension_t* extension,
+                                     cef_browser_t* browser,
+                                     int include_incognito,
+                                     cef_browser_t* target_browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension);
+  if (!extension)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: target_browser; type: refptr_diff
+  DCHECK(target_browser);
+  if (!target_browser)
+    return 0;
+
+  // Execute
+  bool _retval = CefExtensionHandlerCppToC::Get(self)->CanAccessBrowser(
+      CefExtensionCToCpp::Wrap(extension), CefBrowserCToCpp::Wrap(browser),
+      include_incognito ? true : false, CefBrowserCToCpp::Wrap(target_browser));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK extension_handler_get_extension_resource(
+    struct _cef_extension_handler_t* self,
+    cef_extension_t* extension,
+    cef_browser_t* browser,
+    const cef_string_t* file,
+    cef_get_extension_resource_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension);
+  if (!extension)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: file; type: string_byref_const
+  DCHECK(file);
+  if (!file)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Execute
+  bool _retval = CefExtensionHandlerCppToC::Get(self)->GetExtensionResource(
+      CefExtensionCToCpp::Wrap(extension), CefBrowserCToCpp::Wrap(browser),
+      CefString(file), CefGetExtensionResourceCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefExtensionHandlerCppToC::CefExtensionHandlerCppToC() {
+  GetStruct()->on_extension_load_failed =
+      extension_handler_on_extension_load_failed;
+  GetStruct()->on_extension_loaded = extension_handler_on_extension_loaded;
+  GetStruct()->on_extension_unloaded = extension_handler_on_extension_unloaded;
+  GetStruct()->on_before_background_browser =
+      extension_handler_on_before_background_browser;
+  GetStruct()->on_before_browser = extension_handler_on_before_browser;
+  GetStruct()->get_active_browser = extension_handler_get_active_browser;
+  GetStruct()->can_access_browser = extension_handler_can_access_browser;
+  GetStruct()->get_extension_resource =
+      extension_handler_get_extension_resource;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefExtensionHandlerCppToC::~CefExtensionHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefExtensionHandler> CefCppToCRefCounted<
+    CefExtensionHandlerCppToC,
+    CefExtensionHandler,
+    cef_extension_handler_t>::UnwrapDerived(CefWrapperType type,
+                                            cef_extension_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefExtensionHandlerCppToC,
+                                   CefExtensionHandler,
+                                   cef_extension_handler_t>::kWrapperType =
+    WT_EXTENSION_HANDLER;
diff --git a/src/libcef_dll/cpptoc/extension_handler_cpptoc.h b/src/libcef_dll/cpptoc/extension_handler_cpptoc.h
new file mode 100644
index 0000000..02e6752
--- /dev/null
+++ b/src/libcef_dll/cpptoc/extension_handler_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2672174eeafe66d56aa010b327da95531750be53$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_EXTENSION_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_EXTENSION_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/capi/cef_extension_handler_capi.h"
+#include "include/cef_client.h"
+#include "include/cef_extension_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefExtensionHandlerCppToC
+    : public CefCppToCRefCounted<CefExtensionHandlerCppToC,
+                                 CefExtensionHandler,
+                                 cef_extension_handler_t> {
+ public:
+  CefExtensionHandlerCppToC();
+  virtual ~CefExtensionHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_EXTENSION_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/file_dialog_callback_cpptoc.cc b/src/libcef_dll/cpptoc/file_dialog_callback_cpptoc.cc
new file mode 100644
index 0000000..a547568
--- /dev/null
+++ b/src/libcef_dll/cpptoc/file_dialog_callback_cpptoc.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0d74adcfb0f820227588ba350859821b842ea3b3$
+//
+
+#include "libcef_dll/cpptoc/file_dialog_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+file_dialog_callback_cont(struct _cef_file_dialog_callback_t* self,
+                          int selected_accept_filter,
+                          cef_string_list_t file_paths) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return;
+  // Unverified params: file_paths
+
+  // Translate param: file_paths; type: string_vec_byref_const
+  std::vector<CefString> file_pathsList;
+  transfer_string_list_contents(file_paths, file_pathsList);
+
+  // Execute
+  CefFileDialogCallbackCppToC::Get(self)->Continue(selected_accept_filter,
+                                                   file_pathsList);
+}
+
+void CEF_CALLBACK
+file_dialog_callback_cancel(struct _cef_file_dialog_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFileDialogCallbackCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFileDialogCallbackCppToC::CefFileDialogCallbackCppToC() {
+  GetStruct()->cont = file_dialog_callback_cont;
+  GetStruct()->cancel = file_dialog_callback_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFileDialogCallbackCppToC::~CefFileDialogCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefFileDialogCallback> CefCppToCRefCounted<
+    CefFileDialogCallbackCppToC,
+    CefFileDialogCallback,
+    cef_file_dialog_callback_t>::UnwrapDerived(CefWrapperType type,
+                                               cef_file_dialog_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefFileDialogCallbackCppToC,
+                                   CefFileDialogCallback,
+                                   cef_file_dialog_callback_t>::kWrapperType =
+    WT_FILE_DIALOG_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/file_dialog_callback_cpptoc.h b/src/libcef_dll/cpptoc/file_dialog_callback_cpptoc.h
new file mode 100644
index 0000000..dd2df12
--- /dev/null
+++ b/src/libcef_dll/cpptoc/file_dialog_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ca547272f7d8af961ac13db6906ffee9c654f4df$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_FILE_DIALOG_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_FILE_DIALOG_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_dialog_handler_capi.h"
+#include "include/cef_dialog_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefFileDialogCallbackCppToC
+    : public CefCppToCRefCounted<CefFileDialogCallbackCppToC,
+                                 CefFileDialogCallback,
+                                 cef_file_dialog_callback_t> {
+ public:
+  CefFileDialogCallbackCppToC();
+  virtual ~CefFileDialogCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_FILE_DIALOG_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/find_handler_cpptoc.cc b/src/libcef_dll/cpptoc/find_handler_cpptoc.cc
new file mode 100644
index 0000000..8f0634a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/find_handler_cpptoc.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f7bc88a19079bb8133f3fabd7f8d0170c75f9a08$
+//
+
+#include "libcef_dll/cpptoc/find_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK find_handler_on_find_result(struct _cef_find_handler_t* self,
+                                              cef_browser_t* browser,
+                                              int identifier,
+                                              int count,
+                                              const cef_rect_t* selectionRect,
+                                              int activeMatchOrdinal,
+                                              int finalUpdate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: selectionRect; type: simple_byref_const
+  DCHECK(selectionRect);
+  if (!selectionRect)
+    return;
+
+  // Translate param: selectionRect; type: simple_byref_const
+  CefRect selectionRectVal = selectionRect ? *selectionRect : CefRect();
+
+  // Execute
+  CefFindHandlerCppToC::Get(self)->OnFindResult(
+      CefBrowserCToCpp::Wrap(browser), identifier, count, selectionRectVal,
+      activeMatchOrdinal, finalUpdate ? true : false);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFindHandlerCppToC::CefFindHandlerCppToC() {
+  GetStruct()->on_find_result = find_handler_on_find_result;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFindHandlerCppToC::~CefFindHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefFindHandler>
+CefCppToCRefCounted<CefFindHandlerCppToC, CefFindHandler, cef_find_handler_t>::
+    UnwrapDerived(CefWrapperType type, cef_find_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefFindHandlerCppToC,
+                                   CefFindHandler,
+                                   cef_find_handler_t>::kWrapperType =
+    WT_FIND_HANDLER;
diff --git a/src/libcef_dll/cpptoc/find_handler_cpptoc.h b/src/libcef_dll/cpptoc/find_handler_cpptoc.h
new file mode 100644
index 0000000..78fefd9
--- /dev/null
+++ b/src/libcef_dll/cpptoc/find_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0a946a06bef0d92627ff304ef13e75fddbaffc46$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_FIND_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_FIND_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_find_handler_capi.h"
+#include "include/cef_find_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefFindHandlerCppToC : public CefCppToCRefCounted<CefFindHandlerCppToC,
+                                                        CefFindHandler,
+                                                        cef_find_handler_t> {
+ public:
+  CefFindHandlerCppToC();
+  virtual ~CefFindHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_FIND_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/focus_handler_cpptoc.cc b/src/libcef_dll/cpptoc/focus_handler_cpptoc.cc
new file mode 100644
index 0000000..b3e4083
--- /dev/null
+++ b/src/libcef_dll/cpptoc/focus_handler_cpptoc.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f506b931a2f5de1d742319863e8d12e7f097d945$
+//
+
+#include "libcef_dll/cpptoc/focus_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK focus_handler_on_take_focus(struct _cef_focus_handler_t* self,
+                                              cef_browser_t* browser,
+                                              int next) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefFocusHandlerCppToC::Get(self)->OnTakeFocus(CefBrowserCToCpp::Wrap(browser),
+                                                next ? true : false);
+}
+
+int CEF_CALLBACK focus_handler_on_set_focus(struct _cef_focus_handler_t* self,
+                                            cef_browser_t* browser,
+                                            cef_focus_source_t source) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+
+  // Execute
+  bool _retval = CefFocusHandlerCppToC::Get(self)->OnSetFocus(
+      CefBrowserCToCpp::Wrap(browser), source);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK focus_handler_on_got_focus(struct _cef_focus_handler_t* self,
+                                             cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefFocusHandlerCppToC::Get(self)->OnGotFocus(CefBrowserCToCpp::Wrap(browser));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFocusHandlerCppToC::CefFocusHandlerCppToC() {
+  GetStruct()->on_take_focus = focus_handler_on_take_focus;
+  GetStruct()->on_set_focus = focus_handler_on_set_focus;
+  GetStruct()->on_got_focus = focus_handler_on_got_focus;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFocusHandlerCppToC::~CefFocusHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefFocusHandler> CefCppToCRefCounted<
+    CefFocusHandlerCppToC,
+    CefFocusHandler,
+    cef_focus_handler_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_focus_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefFocusHandlerCppToC,
+                                   CefFocusHandler,
+                                   cef_focus_handler_t>::kWrapperType =
+    WT_FOCUS_HANDLER;
diff --git a/src/libcef_dll/cpptoc/focus_handler_cpptoc.h b/src/libcef_dll/cpptoc/focus_handler_cpptoc.h
new file mode 100644
index 0000000..baad3c0
--- /dev/null
+++ b/src/libcef_dll/cpptoc/focus_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=dc0aff48844d6f1d2d006b55a9059ffb6bd10e32$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_FOCUS_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_FOCUS_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_focus_handler_capi.h"
+#include "include/cef_focus_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefFocusHandlerCppToC : public CefCppToCRefCounted<CefFocusHandlerCppToC,
+                                                         CefFocusHandler,
+                                                         cef_focus_handler_t> {
+ public:
+  CefFocusHandlerCppToC();
+  virtual ~CefFocusHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_FOCUS_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/frame_cpptoc.cc b/src/libcef_dll/cpptoc/frame_cpptoc.cc
new file mode 100644
index 0000000..4f903f8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/frame_cpptoc.cc
@@ -0,0 +1,492 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=11321a717870d88eef5252908ef9816af632664e$
+//
+
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/process_message_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/urlrequest_cpptoc.h"
+#include "libcef_dll/cpptoc/v8context_cpptoc.h"
+#include "libcef_dll/ctocpp/domvisitor_ctocpp.h"
+#include "libcef_dll/ctocpp/string_visitor_ctocpp.h"
+#include "libcef_dll/ctocpp/urlrequest_client_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK frame_is_valid(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefFrameCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK frame_undo(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->Undo();
+}
+
+void CEF_CALLBACK frame_redo(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->Redo();
+}
+
+void CEF_CALLBACK frame_cut(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->Cut();
+}
+
+void CEF_CALLBACK frame_copy(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->Copy();
+}
+
+void CEF_CALLBACK frame_paste(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->Paste();
+}
+
+void CEF_CALLBACK frame_del(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->Delete();
+}
+
+void CEF_CALLBACK frame_select_all(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->SelectAll();
+}
+
+void CEF_CALLBACK frame_view_source(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->ViewSource();
+}
+
+void CEF_CALLBACK frame_get_source(struct _cef_frame_t* self,
+                                   struct _cef_string_visitor_t* visitor) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor);
+  if (!visitor)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->GetSource(CefStringVisitorCToCpp::Wrap(visitor));
+}
+
+void CEF_CALLBACK frame_get_text(struct _cef_frame_t* self,
+                                 struct _cef_string_visitor_t* visitor) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor);
+  if (!visitor)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->GetText(CefStringVisitorCToCpp::Wrap(visitor));
+}
+
+void CEF_CALLBACK frame_load_request(struct _cef_frame_t* self,
+                                     struct _cef_request_t* request) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_same
+  DCHECK(request);
+  if (!request)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->LoadRequest(CefRequestCppToC::Unwrap(request));
+}
+
+void CEF_CALLBACK frame_load_url(struct _cef_frame_t* self,
+                                 const cef_string_t* url) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->LoadURL(CefString(url));
+}
+
+void CEF_CALLBACK frame_execute_java_script(struct _cef_frame_t* self,
+                                            const cef_string_t* code,
+                                            const cef_string_t* script_url,
+                                            int start_line) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: code; type: string_byref_const
+  DCHECK(code);
+  if (!code)
+    return;
+  // Unverified params: script_url
+
+  // Execute
+  CefFrameCppToC::Get(self)->ExecuteJavaScript(
+      CefString(code), CefString(script_url), start_line);
+}
+
+int CEF_CALLBACK frame_is_main(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefFrameCppToC::Get(self)->IsMain();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK frame_is_focused(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefFrameCppToC::Get(self)->IsFocused();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK frame_get_name(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefFrameCppToC::Get(self)->GetName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int64 CEF_CALLBACK frame_get_identifier(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefFrameCppToC::Get(self)->GetIdentifier();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_frame_t* CEF_CALLBACK frame_get_parent(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFrame> _retval = CefFrameCppToC::Get(self)->GetParent();
+
+  // Return type: refptr_same
+  return CefFrameCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK frame_get_url(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefFrameCppToC::Get(self)->GetURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_browser_t* CEF_CALLBACK frame_get_browser(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowser> _retval = CefFrameCppToC::Get(self)->GetBrowser();
+
+  // Return type: refptr_same
+  return CefBrowserCppToC::Wrap(_retval);
+}
+
+struct _cef_v8context_t* CEF_CALLBACK
+frame_get_v8context(struct _cef_frame_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8Context> _retval = CefFrameCppToC::Get(self)->GetV8Context();
+
+  // Return type: refptr_same
+  return CefV8ContextCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK frame_visit_dom(struct _cef_frame_t* self,
+                                  cef_domvisitor_t* visitor) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor);
+  if (!visitor)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->VisitDOM(CefDOMVisitorCToCpp::Wrap(visitor));
+}
+
+struct _cef_urlrequest_t* CEF_CALLBACK
+frame_create_urlrequest(struct _cef_frame_t* self,
+                        struct _cef_request_t* request,
+                        struct _cef_urlrequest_client_t* client) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: request; type: refptr_same
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Verify param: client; type: refptr_diff
+  DCHECK(client);
+  if (!client)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefURLRequest> _retval =
+      CefFrameCppToC::Get(self)->CreateURLRequest(
+          CefRequestCppToC::Unwrap(request),
+          CefURLRequestClientCToCpp::Wrap(client));
+
+  // Return type: refptr_same
+  return CefURLRequestCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK
+frame_send_process_message(struct _cef_frame_t* self,
+                           cef_process_id_t target_process,
+                           struct _cef_process_message_t* message) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: message; type: refptr_same
+  DCHECK(message);
+  if (!message)
+    return;
+
+  // Execute
+  CefFrameCppToC::Get(self)->SendProcessMessage(
+      target_process, CefProcessMessageCppToC::Unwrap(message));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFrameCppToC::CefFrameCppToC() {
+  GetStruct()->is_valid = frame_is_valid;
+  GetStruct()->undo = frame_undo;
+  GetStruct()->redo = frame_redo;
+  GetStruct()->cut = frame_cut;
+  GetStruct()->copy = frame_copy;
+  GetStruct()->paste = frame_paste;
+  GetStruct()->del = frame_del;
+  GetStruct()->select_all = frame_select_all;
+  GetStruct()->view_source = frame_view_source;
+  GetStruct()->get_source = frame_get_source;
+  GetStruct()->get_text = frame_get_text;
+  GetStruct()->load_request = frame_load_request;
+  GetStruct()->load_url = frame_load_url;
+  GetStruct()->execute_java_script = frame_execute_java_script;
+  GetStruct()->is_main = frame_is_main;
+  GetStruct()->is_focused = frame_is_focused;
+  GetStruct()->get_name = frame_get_name;
+  GetStruct()->get_identifier = frame_get_identifier;
+  GetStruct()->get_parent = frame_get_parent;
+  GetStruct()->get_url = frame_get_url;
+  GetStruct()->get_browser = frame_get_browser;
+  GetStruct()->get_v8context = frame_get_v8context;
+  GetStruct()->visit_dom = frame_visit_dom;
+  GetStruct()->create_urlrequest = frame_create_urlrequest;
+  GetStruct()->send_process_message = frame_send_process_message;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFrameCppToC::~CefFrameCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefFrame>
+CefCppToCRefCounted<CefFrameCppToC, CefFrame, cef_frame_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_frame_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefFrameCppToC, CefFrame, cef_frame_t>::kWrapperType =
+        WT_FRAME;
diff --git a/src/libcef_dll/cpptoc/frame_cpptoc.h b/src/libcef_dll/cpptoc/frame_cpptoc.h
new file mode 100644
index 0000000..9709d2b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/frame_cpptoc.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0e8ff8890e2d71c776ce964b3811a555c4a23527$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_FRAME_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_FRAME_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_urlrequest_capi.h"
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_urlrequest.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefFrameCppToC
+    : public CefCppToCRefCounted<CefFrameCppToC, CefFrame, cef_frame_t> {
+ public:
+  CefFrameCppToC();
+  virtual ~CefFrameCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_FRAME_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.cc b/src/libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.cc
new file mode 100644
index 0000000..8c11f6c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e3b6f2b73933086fdbe54f3073e1182f4f0cd7fb$
+//
+
+#include "libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/stream_reader_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK get_extension_resource_callback_cont(
+    struct _cef_get_extension_resource_callback_t* self,
+    struct _cef_stream_reader_t* stream) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: stream
+
+  // Execute
+  CefGetExtensionResourceCallbackCppToC::Get(self)->Continue(
+      CefStreamReaderCppToC::Unwrap(stream));
+}
+
+void CEF_CALLBACK get_extension_resource_callback_cancel(
+    struct _cef_get_extension_resource_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefGetExtensionResourceCallbackCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefGetExtensionResourceCallbackCppToC::CefGetExtensionResourceCallbackCppToC() {
+  GetStruct()->cont = get_extension_resource_callback_cont;
+  GetStruct()->cancel = get_extension_resource_callback_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefGetExtensionResourceCallbackCppToC::
+    ~CefGetExtensionResourceCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefGetExtensionResourceCallback>
+CefCppToCRefCounted<CefGetExtensionResourceCallbackCppToC,
+                    CefGetExtensionResourceCallback,
+                    cef_get_extension_resource_callback_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_get_extension_resource_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefGetExtensionResourceCallbackCppToC,
+                        CefGetExtensionResourceCallback,
+                        cef_get_extension_resource_callback_t>::kWrapperType =
+        WT_GET_EXTENSION_RESOURCE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.h b/src/libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.h
new file mode 100644
index 0000000..8430d6f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c17b85008c84465dd7c687148dd37b540bb3f9e5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_GET_EXTENSION_RESOURCE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_GET_EXTENSION_RESOURCE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/capi/cef_extension_handler_capi.h"
+#include "include/cef_client.h"
+#include "include/cef_extension_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefGetExtensionResourceCallbackCppToC
+    : public CefCppToCRefCounted<CefGetExtensionResourceCallbackCppToC,
+                                 CefGetExtensionResourceCallback,
+                                 cef_get_extension_resource_callback_t> {
+ public:
+  CefGetExtensionResourceCallbackCppToC();
+  virtual ~CefGetExtensionResourceCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_GET_EXTENSION_RESOURCE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/image_cpptoc.cc b/src/libcef_dll/cpptoc/image_cpptoc.cc
new file mode 100644
index 0000000..c28e1c6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/image_cpptoc.cc
@@ -0,0 +1,434 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5477e0f2a6661422b3e2d719e97f8e76ce0a631c$
+//
+
+#include "libcef_dll/cpptoc/image_cpptoc.h"
+#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_image_t* cef_image_create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefImage> _retval = CefImage::CreateImage();
+
+  // Return type: refptr_same
+  return CefImageCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK image_is_empty(struct _cef_image_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefImageCppToC::Get(self)->IsEmpty();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK image_is_same(struct _cef_image_t* self,
+                               struct _cef_image_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefImageCppToC::Get(self)->IsSame(CefImageCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK image_add_bitmap(struct _cef_image_t* self,
+                                  float scale_factor,
+                                  int pixel_width,
+                                  int pixel_height,
+                                  cef_color_type_t color_type,
+                                  cef_alpha_type_t alpha_type,
+                                  const void* pixel_data,
+                                  size_t pixel_data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: pixel_data; type: simple_byaddr
+  DCHECK(pixel_data);
+  if (!pixel_data)
+    return 0;
+
+  // Execute
+  bool _retval = CefImageCppToC::Get(self)->AddBitmap(
+      scale_factor, pixel_width, pixel_height, color_type, alpha_type,
+      pixel_data, pixel_data_size);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK image_add_png(struct _cef_image_t* self,
+                               float scale_factor,
+                               const void* png_data,
+                               size_t png_data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: png_data; type: simple_byaddr
+  DCHECK(png_data);
+  if (!png_data)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefImageCppToC::Get(self)->AddPNG(scale_factor, png_data, png_data_size);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK image_add_jpeg(struct _cef_image_t* self,
+                                float scale_factor,
+                                const void* jpeg_data,
+                                size_t jpeg_data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: jpeg_data; type: simple_byaddr
+  DCHECK(jpeg_data);
+  if (!jpeg_data)
+    return 0;
+
+  // Execute
+  bool _retval = CefImageCppToC::Get(self)->AddJPEG(scale_factor, jpeg_data,
+                                                    jpeg_data_size);
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK image_get_width(struct _cef_image_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefImageCppToC::Get(self)->GetWidth();
+
+  // Return type: simple
+  return _retval;
+}
+
+size_t CEF_CALLBACK image_get_height(struct _cef_image_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefImageCppToC::Get(self)->GetHeight();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK image_has_representation(struct _cef_image_t* self,
+                                          float scale_factor) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefImageCppToC::Get(self)->HasRepresentation(scale_factor);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK image_remove_representation(struct _cef_image_t* self,
+                                             float scale_factor) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefImageCppToC::Get(self)->RemoveRepresentation(scale_factor);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK image_get_representation_info(struct _cef_image_t* self,
+                                               float scale_factor,
+                                               float* actual_scale_factor,
+                                               int* pixel_width,
+                                               int* pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: actual_scale_factor; type: simple_byref
+  DCHECK(actual_scale_factor);
+  if (!actual_scale_factor)
+    return 0;
+  // Verify param: pixel_width; type: simple_byref
+  DCHECK(pixel_width);
+  if (!pixel_width)
+    return 0;
+  // Verify param: pixel_height; type: simple_byref
+  DCHECK(pixel_height);
+  if (!pixel_height)
+    return 0;
+
+  // Translate param: actual_scale_factor; type: simple_byref
+  float actual_scale_factorVal = actual_scale_factor ? *actual_scale_factor : 0;
+  // Translate param: pixel_width; type: simple_byref
+  int pixel_widthVal = pixel_width ? *pixel_width : 0;
+  // Translate param: pixel_height; type: simple_byref
+  int pixel_heightVal = pixel_height ? *pixel_height : 0;
+
+  // Execute
+  bool _retval = CefImageCppToC::Get(self)->GetRepresentationInfo(
+      scale_factor, actual_scale_factorVal, pixel_widthVal, pixel_heightVal);
+
+  // Restore param: actual_scale_factor; type: simple_byref
+  if (actual_scale_factor)
+    *actual_scale_factor = actual_scale_factorVal;
+  // Restore param: pixel_width; type: simple_byref
+  if (pixel_width)
+    *pixel_width = pixel_widthVal;
+  // Restore param: pixel_height; type: simple_byref
+  if (pixel_height)
+    *pixel_height = pixel_heightVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_binary_value_t* CEF_CALLBACK
+image_get_as_bitmap(struct _cef_image_t* self,
+                    float scale_factor,
+                    cef_color_type_t color_type,
+                    cef_alpha_type_t alpha_type,
+                    int* pixel_width,
+                    int* pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: pixel_width; type: simple_byref
+  DCHECK(pixel_width);
+  if (!pixel_width)
+    return NULL;
+  // Verify param: pixel_height; type: simple_byref
+  DCHECK(pixel_height);
+  if (!pixel_height)
+    return NULL;
+
+  // Translate param: pixel_width; type: simple_byref
+  int pixel_widthVal = pixel_width ? *pixel_width : 0;
+  // Translate param: pixel_height; type: simple_byref
+  int pixel_heightVal = pixel_height ? *pixel_height : 0;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval = CefImageCppToC::Get(self)->GetAsBitmap(
+      scale_factor, color_type, alpha_type, pixel_widthVal, pixel_heightVal);
+
+  // Restore param: pixel_width; type: simple_byref
+  if (pixel_width)
+    *pixel_width = pixel_widthVal;
+  // Restore param: pixel_height; type: simple_byref
+  if (pixel_height)
+    *pixel_height = pixel_heightVal;
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+struct _cef_binary_value_t* CEF_CALLBACK
+image_get_as_png(struct _cef_image_t* self,
+                 float scale_factor,
+                 int with_transparency,
+                 int* pixel_width,
+                 int* pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: pixel_width; type: simple_byref
+  DCHECK(pixel_width);
+  if (!pixel_width)
+    return NULL;
+  // Verify param: pixel_height; type: simple_byref
+  DCHECK(pixel_height);
+  if (!pixel_height)
+    return NULL;
+
+  // Translate param: pixel_width; type: simple_byref
+  int pixel_widthVal = pixel_width ? *pixel_width : 0;
+  // Translate param: pixel_height; type: simple_byref
+  int pixel_heightVal = pixel_height ? *pixel_height : 0;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval = CefImageCppToC::Get(self)->GetAsPNG(
+      scale_factor, with_transparency ? true : false, pixel_widthVal,
+      pixel_heightVal);
+
+  // Restore param: pixel_width; type: simple_byref
+  if (pixel_width)
+    *pixel_width = pixel_widthVal;
+  // Restore param: pixel_height; type: simple_byref
+  if (pixel_height)
+    *pixel_height = pixel_heightVal;
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+struct _cef_binary_value_t* CEF_CALLBACK
+image_get_as_jpeg(struct _cef_image_t* self,
+                  float scale_factor,
+                  int quality,
+                  int* pixel_width,
+                  int* pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: pixel_width; type: simple_byref
+  DCHECK(pixel_width);
+  if (!pixel_width)
+    return NULL;
+  // Verify param: pixel_height; type: simple_byref
+  DCHECK(pixel_height);
+  if (!pixel_height)
+    return NULL;
+
+  // Translate param: pixel_width; type: simple_byref
+  int pixel_widthVal = pixel_width ? *pixel_width : 0;
+  // Translate param: pixel_height; type: simple_byref
+  int pixel_heightVal = pixel_height ? *pixel_height : 0;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval = CefImageCppToC::Get(self)->GetAsJPEG(
+      scale_factor, quality, pixel_widthVal, pixel_heightVal);
+
+  // Restore param: pixel_width; type: simple_byref
+  if (pixel_width)
+    *pixel_width = pixel_widthVal;
+  // Restore param: pixel_height; type: simple_byref
+  if (pixel_height)
+    *pixel_height = pixel_heightVal;
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefImageCppToC::CefImageCppToC() {
+  GetStruct()->is_empty = image_is_empty;
+  GetStruct()->is_same = image_is_same;
+  GetStruct()->add_bitmap = image_add_bitmap;
+  GetStruct()->add_png = image_add_png;
+  GetStruct()->add_jpeg = image_add_jpeg;
+  GetStruct()->get_width = image_get_width;
+  GetStruct()->get_height = image_get_height;
+  GetStruct()->has_representation = image_has_representation;
+  GetStruct()->remove_representation = image_remove_representation;
+  GetStruct()->get_representation_info = image_get_representation_info;
+  GetStruct()->get_as_bitmap = image_get_as_bitmap;
+  GetStruct()->get_as_png = image_get_as_png;
+  GetStruct()->get_as_jpeg = image_get_as_jpeg;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefImageCppToC::~CefImageCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefImage>
+CefCppToCRefCounted<CefImageCppToC, CefImage, cef_image_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_image_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefImageCppToC, CefImage, cef_image_t>::kWrapperType =
+        WT_IMAGE;
diff --git a/src/libcef_dll/cpptoc/image_cpptoc.h b/src/libcef_dll/cpptoc/image_cpptoc.h
new file mode 100644
index 0000000..e462543
--- /dev/null
+++ b/src/libcef_dll/cpptoc/image_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3424ee809a8269adb20112e55ac448e00f1c2e13$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_IMAGE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_IMAGE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_image_capi.h"
+#include "include/cef_image.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefImageCppToC
+    : public CefCppToCRefCounted<CefImageCppToC, CefImage, cef_image_t> {
+ public:
+  CefImageCppToC();
+  virtual ~CefImageCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_IMAGE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/jsdialog_callback_cpptoc.cc b/src/libcef_dll/cpptoc/jsdialog_callback_cpptoc.cc
new file mode 100644
index 0000000..9837cda
--- /dev/null
+++ b/src/libcef_dll/cpptoc/jsdialog_callback_cpptoc.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=58e277efebc83d236217f73e080a39e603323ef0$
+//
+
+#include "libcef_dll/cpptoc/jsdialog_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK jsdialog_callback_cont(struct _cef_jsdialog_callback_t* self,
+                                         int success,
+                                         const cef_string_t* user_input) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: user_input
+
+  // Execute
+  CefJSDialogCallbackCppToC::Get(self)->Continue(success ? true : false,
+                                                 CefString(user_input));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefJSDialogCallbackCppToC::CefJSDialogCallbackCppToC() {
+  GetStruct()->cont = jsdialog_callback_cont;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefJSDialogCallbackCppToC::~CefJSDialogCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefJSDialogCallback> CefCppToCRefCounted<
+    CefJSDialogCallbackCppToC,
+    CefJSDialogCallback,
+    cef_jsdialog_callback_t>::UnwrapDerived(CefWrapperType type,
+                                            cef_jsdialog_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefJSDialogCallbackCppToC,
+                                   CefJSDialogCallback,
+                                   cef_jsdialog_callback_t>::kWrapperType =
+    WT_JSDIALOG_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/jsdialog_callback_cpptoc.h b/src/libcef_dll/cpptoc/jsdialog_callback_cpptoc.h
new file mode 100644
index 0000000..d424a98
--- /dev/null
+++ b/src/libcef_dll/cpptoc/jsdialog_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9ca357bc7ca56079e4136481bcefba7fe8925b04$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_JSDIALOG_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_JSDIALOG_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_jsdialog_handler_capi.h"
+#include "include/cef_jsdialog_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefJSDialogCallbackCppToC
+    : public CefCppToCRefCounted<CefJSDialogCallbackCppToC,
+                                 CefJSDialogCallback,
+                                 cef_jsdialog_callback_t> {
+ public:
+  CefJSDialogCallbackCppToC();
+  virtual ~CefJSDialogCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_JSDIALOG_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/jsdialog_handler_cpptoc.cc b/src/libcef_dll/cpptoc/jsdialog_handler_cpptoc.cc
new file mode 100644
index 0000000..98daa48
--- /dev/null
+++ b/src/libcef_dll/cpptoc/jsdialog_handler_cpptoc.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7ddba7a186923cfd813d205ac5a239459f1c7532$
+//
+
+#include "libcef_dll/cpptoc/jsdialog_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/jsdialog_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+jsdialog_handler_on_jsdialog(struct _cef_jsdialog_handler_t* self,
+                             cef_browser_t* browser,
+                             const cef_string_t* origin_url,
+                             cef_jsdialog_type_t dialog_type,
+                             const cef_string_t* message_text,
+                             const cef_string_t* default_prompt_text,
+                             cef_jsdialog_callback_t* callback,
+                             int* suppress_message) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+  // Verify param: suppress_message; type: bool_byref
+  DCHECK(suppress_message);
+  if (!suppress_message)
+    return 0;
+  // Unverified params: origin_url, message_text, default_prompt_text
+
+  // Translate param: suppress_message; type: bool_byref
+  bool suppress_messageBool =
+      (suppress_message && *suppress_message) ? true : false;
+
+  // Execute
+  bool _retval = CefJSDialogHandlerCppToC::Get(self)->OnJSDialog(
+      CefBrowserCToCpp::Wrap(browser), CefString(origin_url), dialog_type,
+      CefString(message_text), CefString(default_prompt_text),
+      CefJSDialogCallbackCToCpp::Wrap(callback), suppress_messageBool);
+
+  // Restore param: suppress_message; type: bool_byref
+  if (suppress_message)
+    *suppress_message = suppress_messageBool ? true : false;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+jsdialog_handler_on_before_unload_dialog(struct _cef_jsdialog_handler_t* self,
+                                         cef_browser_t* browser,
+                                         const cef_string_t* message_text,
+                                         int is_reload,
+                                         cef_jsdialog_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+  // Unverified params: message_text
+
+  // Execute
+  bool _retval = CefJSDialogHandlerCppToC::Get(self)->OnBeforeUnloadDialog(
+      CefBrowserCToCpp::Wrap(browser), CefString(message_text),
+      is_reload ? true : false, CefJSDialogCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+jsdialog_handler_on_reset_dialog_state(struct _cef_jsdialog_handler_t* self,
+                                       cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefJSDialogHandlerCppToC::Get(self)->OnResetDialogState(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+void CEF_CALLBACK
+jsdialog_handler_on_dialog_closed(struct _cef_jsdialog_handler_t* self,
+                                  cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefJSDialogHandlerCppToC::Get(self)->OnDialogClosed(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefJSDialogHandlerCppToC::CefJSDialogHandlerCppToC() {
+  GetStruct()->on_jsdialog = jsdialog_handler_on_jsdialog;
+  GetStruct()->on_before_unload_dialog =
+      jsdialog_handler_on_before_unload_dialog;
+  GetStruct()->on_reset_dialog_state = jsdialog_handler_on_reset_dialog_state;
+  GetStruct()->on_dialog_closed = jsdialog_handler_on_dialog_closed;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefJSDialogHandlerCppToC::~CefJSDialogHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefJSDialogHandler> CefCppToCRefCounted<
+    CefJSDialogHandlerCppToC,
+    CefJSDialogHandler,
+    cef_jsdialog_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_jsdialog_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefJSDialogHandlerCppToC,
+                                   CefJSDialogHandler,
+                                   cef_jsdialog_handler_t>::kWrapperType =
+    WT_JSDIALOG_HANDLER;
diff --git a/src/libcef_dll/cpptoc/jsdialog_handler_cpptoc.h b/src/libcef_dll/cpptoc/jsdialog_handler_cpptoc.h
new file mode 100644
index 0000000..f7f87c3
--- /dev/null
+++ b/src/libcef_dll/cpptoc/jsdialog_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=82f825e1d4f428e30d33559bc48d0e4d5e978ff5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_JSDIALOG_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_JSDIALOG_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_jsdialog_handler_capi.h"
+#include "include/cef_jsdialog_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefJSDialogHandlerCppToC
+    : public CefCppToCRefCounted<CefJSDialogHandlerCppToC,
+                                 CefJSDialogHandler,
+                                 cef_jsdialog_handler_t> {
+ public:
+  CefJSDialogHandlerCppToC();
+  virtual ~CefJSDialogHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_JSDIALOG_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/keyboard_handler_cpptoc.cc b/src/libcef_dll/cpptoc/keyboard_handler_cpptoc.cc
new file mode 100644
index 0000000..426c947
--- /dev/null
+++ b/src/libcef_dll/cpptoc/keyboard_handler_cpptoc.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8ce97d9d7c49e82243e0c2dc0b31b54b234f49e5$
+//
+
+#include "libcef_dll/cpptoc/keyboard_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+keyboard_handler_on_pre_key_event(struct _cef_keyboard_handler_t* self,
+                                  cef_browser_t* browser,
+                                  const struct _cef_key_event_t* event,
+                                  cef_event_handle_t os_event,
+                                  int* is_keyboard_shortcut) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return 0;
+  // Verify param: is_keyboard_shortcut; type: bool_byaddr
+  DCHECK(is_keyboard_shortcut);
+  if (!is_keyboard_shortcut)
+    return 0;
+
+  // Translate param: event; type: struct_byref_const
+  CefKeyEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+  // Translate param: is_keyboard_shortcut; type: bool_byaddr
+  bool is_keyboard_shortcutBool =
+      (is_keyboard_shortcut && *is_keyboard_shortcut) ? true : false;
+
+  // Execute
+  bool _retval = CefKeyboardHandlerCppToC::Get(self)->OnPreKeyEvent(
+      CefBrowserCToCpp::Wrap(browser), eventObj, os_event,
+      &is_keyboard_shortcutBool);
+
+  // Restore param: is_keyboard_shortcut; type: bool_byaddr
+  if (is_keyboard_shortcut)
+    *is_keyboard_shortcut = is_keyboard_shortcutBool ? true : false;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+keyboard_handler_on_key_event(struct _cef_keyboard_handler_t* self,
+                              cef_browser_t* browser,
+                              const struct _cef_key_event_t* event,
+                              cef_event_handle_t os_event) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return 0;
+
+  // Translate param: event; type: struct_byref_const
+  CefKeyEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  bool _retval = CefKeyboardHandlerCppToC::Get(self)->OnKeyEvent(
+      CefBrowserCToCpp::Wrap(browser), eventObj, os_event);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefKeyboardHandlerCppToC::CefKeyboardHandlerCppToC() {
+  GetStruct()->on_pre_key_event = keyboard_handler_on_pre_key_event;
+  GetStruct()->on_key_event = keyboard_handler_on_key_event;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefKeyboardHandlerCppToC::~CefKeyboardHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefKeyboardHandler> CefCppToCRefCounted<
+    CefKeyboardHandlerCppToC,
+    CefKeyboardHandler,
+    cef_keyboard_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_keyboard_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefKeyboardHandlerCppToC,
+                                   CefKeyboardHandler,
+                                   cef_keyboard_handler_t>::kWrapperType =
+    WT_KEYBOARD_HANDLER;
diff --git a/src/libcef_dll/cpptoc/keyboard_handler_cpptoc.h b/src/libcef_dll/cpptoc/keyboard_handler_cpptoc.h
new file mode 100644
index 0000000..07a04ae
--- /dev/null
+++ b/src/libcef_dll/cpptoc/keyboard_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b9790a5aeff5475ecdcc42559e8a84b3e351b38a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_KEYBOARD_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_KEYBOARD_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_keyboard_handler_capi.h"
+#include "include/cef_keyboard_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefKeyboardHandlerCppToC
+    : public CefCppToCRefCounted<CefKeyboardHandlerCppToC,
+                                 CefKeyboardHandler,
+                                 cef_keyboard_handler_t> {
+ public:
+  CefKeyboardHandlerCppToC();
+  virtual ~CefKeyboardHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_KEYBOARD_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/life_span_handler_cpptoc.cc b/src/libcef_dll/cpptoc/life_span_handler_cpptoc.cc
new file mode 100644
index 0000000..644a3db
--- /dev/null
+++ b/src/libcef_dll/cpptoc/life_span_handler_cpptoc.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3cbbd379676d77a0aadd503ba89a8fbef2f355a3$
+//
+
+#include "libcef_dll/cpptoc/life_span_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/client_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK life_span_handler_on_before_popup(
+    struct _cef_life_span_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    const cef_string_t* target_url,
+    const cef_string_t* target_frame_name,
+    cef_window_open_disposition_t target_disposition,
+    int user_gesture,
+    const struct _cef_popup_features_t* popupFeatures,
+    cef_window_info_t* windowInfo,
+    cef_client_t** client,
+    struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t** extra_info,
+    int* no_javascript_access) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return 0;
+  // Verify param: popupFeatures; type: struct_byref_const
+  DCHECK(popupFeatures);
+  if (!popupFeatures)
+    return 0;
+  // Verify param: windowInfo; type: struct_byref
+  DCHECK(windowInfo);
+  if (!windowInfo)
+    return 0;
+  // Verify param: client; type: refptr_same_byref
+  DCHECK(client);
+  if (!client)
+    return 0;
+  // Verify param: settings; type: struct_byref
+  DCHECK(settings);
+  if (!settings)
+    return 0;
+  // Verify param: extra_info; type: refptr_diff_byref
+  DCHECK(extra_info);
+  if (!extra_info)
+    return 0;
+  // Verify param: no_javascript_access; type: bool_byaddr
+  DCHECK(no_javascript_access);
+  if (!no_javascript_access)
+    return 0;
+  // Unverified params: target_url, target_frame_name
+
+  // Translate param: popupFeatures; type: struct_byref_const
+  CefPopupFeatures popupFeaturesObj;
+  if (popupFeatures)
+    popupFeaturesObj.Set(*popupFeatures, false);
+  // Translate param: windowInfo; type: struct_byref
+  CefWindowInfo windowInfoObj;
+  if (windowInfo)
+    windowInfoObj.AttachTo(*windowInfo);
+  // Translate param: client; type: refptr_same_byref
+  CefRefPtr<CefClient> clientPtr;
+  if (client && *client)
+    clientPtr = CefClientCppToC::Unwrap(*client);
+  CefClient* clientOrig = clientPtr.get();
+  // Translate param: settings; type: struct_byref
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.AttachTo(*settings);
+  // Translate param: extra_info; type: refptr_diff_byref
+  CefRefPtr<CefDictionaryValue> extra_infoPtr;
+  if (extra_info && *extra_info)
+    extra_infoPtr = CefDictionaryValueCToCpp::Wrap(*extra_info);
+  CefDictionaryValue* extra_infoOrig = extra_infoPtr.get();
+  // Translate param: no_javascript_access; type: bool_byaddr
+  bool no_javascript_accessBool =
+      (no_javascript_access && *no_javascript_access) ? true : false;
+
+  // Execute
+  bool _retval = CefLifeSpanHandlerCppToC::Get(self)->OnBeforePopup(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefString(target_url), CefString(target_frame_name), target_disposition,
+      user_gesture ? true : false, popupFeaturesObj, windowInfoObj, clientPtr,
+      settingsObj, extra_infoPtr, &no_javascript_accessBool);
+
+  // Restore param: windowInfo; type: struct_byref
+  if (windowInfo)
+    windowInfoObj.DetachTo(*windowInfo);
+  // Restore param: client; type: refptr_same_byref
+  if (client) {
+    if (clientPtr.get()) {
+      if (clientPtr.get() != clientOrig) {
+        *client = CefClientCppToC::Wrap(clientPtr);
+      }
+    } else {
+      *client = nullptr;
+    }
+  }
+  // Restore param: settings; type: struct_byref
+  if (settings)
+    settingsObj.DetachTo(*settings);
+  // Restore param: extra_info; type: refptr_diff_byref
+  if (extra_info) {
+    if (extra_infoPtr.get()) {
+      if (extra_infoPtr.get() != extra_infoOrig) {
+        *extra_info = CefDictionaryValueCToCpp::Unwrap(extra_infoPtr);
+      }
+    } else {
+      *extra_info = nullptr;
+    }
+  }
+  // Restore param: no_javascript_access; type: bool_byaddr
+  if (no_javascript_access)
+    *no_javascript_access = no_javascript_accessBool ? true : false;
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+life_span_handler_on_after_created(struct _cef_life_span_handler_t* self,
+                                   cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefLifeSpanHandlerCppToC::Get(self)->OnAfterCreated(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+int CEF_CALLBACK
+life_span_handler_do_close(struct _cef_life_span_handler_t* self,
+                           cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+
+  // Execute
+  bool _retval = CefLifeSpanHandlerCppToC::Get(self)->DoClose(
+      CefBrowserCToCpp::Wrap(browser));
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+life_span_handler_on_before_close(struct _cef_life_span_handler_t* self,
+                                  cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefLifeSpanHandlerCppToC::Get(self)->OnBeforeClose(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLifeSpanHandlerCppToC::CefLifeSpanHandlerCppToC() {
+  GetStruct()->on_before_popup = life_span_handler_on_before_popup;
+  GetStruct()->on_after_created = life_span_handler_on_after_created;
+  GetStruct()->do_close = life_span_handler_do_close;
+  GetStruct()->on_before_close = life_span_handler_on_before_close;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLifeSpanHandlerCppToC::~CefLifeSpanHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefLifeSpanHandler> CefCppToCRefCounted<
+    CefLifeSpanHandlerCppToC,
+    CefLifeSpanHandler,
+    cef_life_span_handler_t>::UnwrapDerived(CefWrapperType type,
+                                            cef_life_span_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefLifeSpanHandlerCppToC,
+                                   CefLifeSpanHandler,
+                                   cef_life_span_handler_t>::kWrapperType =
+    WT_LIFE_SPAN_HANDLER;
diff --git a/src/libcef_dll/cpptoc/life_span_handler_cpptoc.h b/src/libcef_dll/cpptoc/life_span_handler_cpptoc.h
new file mode 100644
index 0000000..4e624b0
--- /dev/null
+++ b/src/libcef_dll/cpptoc/life_span_handler_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e37f0c26fd35ee2f0563f8376f6b5c8dbba9d571$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_LIFE_SPAN_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_LIFE_SPAN_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/capi/cef_life_span_handler_capi.h"
+#include "include/cef_client.h"
+#include "include/cef_life_span_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefLifeSpanHandlerCppToC
+    : public CefCppToCRefCounted<CefLifeSpanHandlerCppToC,
+                                 CefLifeSpanHandler,
+                                 cef_life_span_handler_t> {
+ public:
+  CefLifeSpanHandlerCppToC();
+  virtual ~CefLifeSpanHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_LIFE_SPAN_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/list_value_cpptoc.cc b/src/libcef_dll/cpptoc/list_value_cpptoc.cc
new file mode 100644
index 0000000..c96f2fb
--- /dev/null
+++ b/src/libcef_dll/cpptoc/list_value_cpptoc.cc
@@ -0,0 +1,607 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fb9b9acf2ce3e3a2ba72b2a20e886eb8530b97b4$
+//
+
+#include "libcef_dll/cpptoc/list_value_cpptoc.h"
+#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/value_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_list_value_t* cef_list_value_create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefListValue> _retval = CefListValue::Create();
+
+  // Return type: refptr_same
+  return CefListValueCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK list_value_is_valid(struct _cef_list_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_is_owned(struct _cef_list_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->IsOwned();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_is_read_only(struct _cef_list_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_is_same(struct _cef_list_value_t* self,
+                                    struct _cef_list_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefListValueCppToC::Get(self)->IsSame(CefListValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_is_equal(struct _cef_list_value_t* self,
+                                     struct _cef_list_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefListValueCppToC::Get(self)->IsEqual(CefListValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_list_value_t* CEF_CALLBACK
+list_value_copy(struct _cef_list_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefListValue> _retval = CefListValueCppToC::Get(self)->Copy();
+
+  // Return type: refptr_same
+  return CefListValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK list_value_set_size(struct _cef_list_value_t* self,
+                                     size_t size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetSize(size);
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK list_value_get_size(struct _cef_list_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefListValueCppToC::Get(self)->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_clear(struct _cef_list_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->Clear();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_remove(struct _cef_list_value_t* self,
+                                   size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->Remove(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_value_type_t CEF_CALLBACK
+list_value_get_type(struct _cef_list_value_t* self, size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return VTYPE_INVALID;
+
+  // Execute
+  cef_value_type_t _retval = CefListValueCppToC::Get(self)->GetType(index);
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_value_t* CEF_CALLBACK list_value_get_value(struct _cef_list_value_t* self,
+                                               size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefValue> _retval = CefListValueCppToC::Get(self)->GetValue(index);
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK list_value_get_bool(struct _cef_list_value_t* self,
+                                     size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->GetBool(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_get_int(struct _cef_list_value_t* self,
+                                    size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefListValueCppToC::Get(self)->GetInt(index);
+
+  // Return type: simple
+  return _retval;
+}
+
+double CEF_CALLBACK list_value_get_double(struct _cef_list_value_t* self,
+                                          size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  double _retval = CefListValueCppToC::Get(self)->GetDouble(index);
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+list_value_get_string(struct _cef_list_value_t* self, size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefListValueCppToC::Get(self)->GetString(index);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_binary_value_t* CEF_CALLBACK
+list_value_get_binary(struct _cef_list_value_t* self, size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval =
+      CefListValueCppToC::Get(self)->GetBinary(index);
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+cef_dictionary_value_t* CEF_CALLBACK
+list_value_get_dictionary(struct _cef_list_value_t* self, size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDictionaryValue> _retval =
+      CefListValueCppToC::Get(self)->GetDictionary(index);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCppToC::Wrap(_retval);
+}
+
+struct _cef_list_value_t* CEF_CALLBACK
+list_value_get_list(struct _cef_list_value_t* self, size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefListValue> _retval =
+      CefListValueCppToC::Get(self)->GetList(index);
+
+  // Return type: refptr_same
+  return CefListValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK list_value_set_value(struct _cef_list_value_t* self,
+                                      size_t index,
+                                      cef_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetValue(
+      index, CefValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_null(struct _cef_list_value_t* self,
+                                     size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetNull(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_bool(struct _cef_list_value_t* self,
+                                     size_t index,
+                                     int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefListValueCppToC::Get(self)->SetBool(index, value ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_int(struct _cef_list_value_t* self,
+                                    size_t index,
+                                    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetInt(index, value);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_double(struct _cef_list_value_t* self,
+                                       size_t index,
+                                       double value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetDouble(index, value);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_string(struct _cef_list_value_t* self,
+                                       size_t index,
+                                       const cef_string_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: value
+
+  // Execute
+  bool _retval =
+      CefListValueCppToC::Get(self)->SetString(index, CefString(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_binary(struct _cef_list_value_t* self,
+                                       size_t index,
+                                       cef_binary_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetBinary(
+      index, CefBinaryValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_dictionary(struct _cef_list_value_t* self,
+                                           size_t index,
+                                           cef_dictionary_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetDictionary(
+      index, CefDictionaryValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK list_value_set_list(struct _cef_list_value_t* self,
+                                     size_t index,
+                                     struct _cef_list_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefListValueCppToC::Get(self)->SetList(
+      index, CefListValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefListValueCppToC::CefListValueCppToC() {
+  GetStruct()->is_valid = list_value_is_valid;
+  GetStruct()->is_owned = list_value_is_owned;
+  GetStruct()->is_read_only = list_value_is_read_only;
+  GetStruct()->is_same = list_value_is_same;
+  GetStruct()->is_equal = list_value_is_equal;
+  GetStruct()->copy = list_value_copy;
+  GetStruct()->set_size = list_value_set_size;
+  GetStruct()->get_size = list_value_get_size;
+  GetStruct()->clear = list_value_clear;
+  GetStruct()->remove = list_value_remove;
+  GetStruct()->get_type = list_value_get_type;
+  GetStruct()->get_value = list_value_get_value;
+  GetStruct()->get_bool = list_value_get_bool;
+  GetStruct()->get_int = list_value_get_int;
+  GetStruct()->get_double = list_value_get_double;
+  GetStruct()->get_string = list_value_get_string;
+  GetStruct()->get_binary = list_value_get_binary;
+  GetStruct()->get_dictionary = list_value_get_dictionary;
+  GetStruct()->get_list = list_value_get_list;
+  GetStruct()->set_value = list_value_set_value;
+  GetStruct()->set_null = list_value_set_null;
+  GetStruct()->set_bool = list_value_set_bool;
+  GetStruct()->set_int = list_value_set_int;
+  GetStruct()->set_double = list_value_set_double;
+  GetStruct()->set_string = list_value_set_string;
+  GetStruct()->set_binary = list_value_set_binary;
+  GetStruct()->set_dictionary = list_value_set_dictionary;
+  GetStruct()->set_list = list_value_set_list;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefListValueCppToC::~CefListValueCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefListValue>
+CefCppToCRefCounted<CefListValueCppToC, CefListValue, cef_list_value_t>::
+    UnwrapDerived(CefWrapperType type, cef_list_value_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefListValueCppToC,
+                                   CefListValue,
+                                   cef_list_value_t>::kWrapperType =
+    WT_LIST_VALUE;
diff --git a/src/libcef_dll/cpptoc/list_value_cpptoc.h b/src/libcef_dll/cpptoc/list_value_cpptoc.h
new file mode 100644
index 0000000..ca62e49
--- /dev/null
+++ b/src/libcef_dll/cpptoc/list_value_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=62f8954aaa96e74dabf98f75695dd8e1e92ebc75$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_LIST_VALUE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_LIST_VALUE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefListValueCppToC : public CefCppToCRefCounted<CefListValueCppToC,
+                                                      CefListValue,
+                                                      cef_list_value_t> {
+ public:
+  CefListValueCppToC();
+  virtual ~CefListValueCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_LIST_VALUE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/load_handler_cpptoc.cc b/src/libcef_dll/cpptoc/load_handler_cpptoc.cc
new file mode 100644
index 0000000..bf663a6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/load_handler_cpptoc.cc
@@ -0,0 +1,163 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e404a832b0a8a2d9ddc086c7f4763a4513d8a730$
+//
+
+#include "libcef_dll/cpptoc/load_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+load_handler_on_loading_state_change(struct _cef_load_handler_t* self,
+                                     cef_browser_t* browser,
+                                     int isLoading,
+                                     int canGoBack,
+                                     int canGoForward) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefLoadHandlerCppToC::Get(self)->OnLoadingStateChange(
+      CefBrowserCToCpp::Wrap(browser), isLoading ? true : false,
+      canGoBack ? true : false, canGoForward ? true : false);
+}
+
+void CEF_CALLBACK
+load_handler_on_load_start(struct _cef_load_handler_t* self,
+                           cef_browser_t* browser,
+                           cef_frame_t* frame,
+                           cef_transition_type_t transition_type) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+
+  // Execute
+  CefLoadHandlerCppToC::Get(self)->OnLoadStart(CefBrowserCToCpp::Wrap(browser),
+                                               CefFrameCToCpp::Wrap(frame),
+                                               transition_type);
+}
+
+void CEF_CALLBACK load_handler_on_load_end(struct _cef_load_handler_t* self,
+                                           cef_browser_t* browser,
+                                           cef_frame_t* frame,
+                                           int httpStatusCode) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+
+  // Execute
+  CefLoadHandlerCppToC::Get(self)->OnLoadEnd(CefBrowserCToCpp::Wrap(browser),
+                                             CefFrameCToCpp::Wrap(frame),
+                                             httpStatusCode);
+}
+
+void CEF_CALLBACK load_handler_on_load_error(struct _cef_load_handler_t* self,
+                                             cef_browser_t* browser,
+                                             cef_frame_t* frame,
+                                             cef_errorcode_t errorCode,
+                                             const cef_string_t* errorText,
+                                             const cef_string_t* failedUrl) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Verify param: failedUrl; type: string_byref_const
+  DCHECK(failedUrl);
+  if (!failedUrl)
+    return;
+  // Unverified params: errorText
+
+  // Execute
+  CefLoadHandlerCppToC::Get(self)->OnLoadError(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame), errorCode,
+      CefString(errorText), CefString(failedUrl));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLoadHandlerCppToC::CefLoadHandlerCppToC() {
+  GetStruct()->on_loading_state_change = load_handler_on_loading_state_change;
+  GetStruct()->on_load_start = load_handler_on_load_start;
+  GetStruct()->on_load_end = load_handler_on_load_end;
+  GetStruct()->on_load_error = load_handler_on_load_error;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLoadHandlerCppToC::~CefLoadHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefLoadHandler>
+CefCppToCRefCounted<CefLoadHandlerCppToC, CefLoadHandler, cef_load_handler_t>::
+    UnwrapDerived(CefWrapperType type, cef_load_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefLoadHandlerCppToC,
+                                   CefLoadHandler,
+                                   cef_load_handler_t>::kWrapperType =
+    WT_LOAD_HANDLER;
diff --git a/src/libcef_dll/cpptoc/load_handler_cpptoc.h b/src/libcef_dll/cpptoc/load_handler_cpptoc.h
new file mode 100644
index 0000000..b9b474c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/load_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8f54b9d21cbb223077027f755dd276d7589a32e4$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_LOAD_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_LOAD_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_load_handler_capi.h"
+#include "include/cef_load_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefLoadHandlerCppToC : public CefCppToCRefCounted<CefLoadHandlerCppToC,
+                                                        CefLoadHandler,
+                                                        cef_load_handler_t> {
+ public:
+  CefLoadHandlerCppToC();
+  virtual ~CefLoadHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_LOAD_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/media_observer_cpptoc.cc b/src/libcef_dll/cpptoc/media_observer_cpptoc.cc
new file mode 100644
index 0000000..5026161
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_observer_cpptoc.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=35f0e881b5a98c1da73789ecb72254fbe254a350$
+//
+
+#include "libcef_dll/cpptoc/media_observer_cpptoc.h"
+#include "libcef_dll/ctocpp/media_route_ctocpp.h"
+#include "libcef_dll/ctocpp/media_sink_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+media_observer_on_sinks(struct _cef_media_observer_t* self,
+                        size_t sinksCount,
+                        struct _cef_media_sink_t* const* sinks) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: sinks; type: refptr_vec_diff_byref_const
+  DCHECK(sinksCount == 0 || sinks);
+  if (sinksCount > 0 && !sinks)
+    return;
+
+  // Translate param: sinks; type: refptr_vec_diff_byref_const
+  std::vector<CefRefPtr<CefMediaSink>> sinksList;
+  if (sinksCount > 0) {
+    for (size_t i = 0; i < sinksCount; ++i) {
+      CefRefPtr<CefMediaSink> sinksVal = CefMediaSinkCToCpp::Wrap(sinks[i]);
+      sinksList.push_back(sinksVal);
+    }
+  }
+
+  // Execute
+  CefMediaObserverCppToC::Get(self)->OnSinks(sinksList);
+}
+
+void CEF_CALLBACK
+media_observer_on_routes(struct _cef_media_observer_t* self,
+                         size_t routesCount,
+                         struct _cef_media_route_t* const* routes) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: routes; type: refptr_vec_diff_byref_const
+  DCHECK(routesCount == 0 || routes);
+  if (routesCount > 0 && !routes)
+    return;
+
+  // Translate param: routes; type: refptr_vec_diff_byref_const
+  std::vector<CefRefPtr<CefMediaRoute>> routesList;
+  if (routesCount > 0) {
+    for (size_t i = 0; i < routesCount; ++i) {
+      CefRefPtr<CefMediaRoute> routesVal = CefMediaRouteCToCpp::Wrap(routes[i]);
+      routesList.push_back(routesVal);
+    }
+  }
+
+  // Execute
+  CefMediaObserverCppToC::Get(self)->OnRoutes(routesList);
+}
+
+void CEF_CALLBACK media_observer_on_route_state_changed(
+    struct _cef_media_observer_t* self,
+    struct _cef_media_route_t* route,
+    cef_media_route_connection_state_t state) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: route; type: refptr_diff
+  DCHECK(route);
+  if (!route)
+    return;
+
+  // Execute
+  CefMediaObserverCppToC::Get(self)->OnRouteStateChanged(
+      CefMediaRouteCToCpp::Wrap(route), state);
+}
+
+void CEF_CALLBACK
+media_observer_on_route_message_received(struct _cef_media_observer_t* self,
+                                         struct _cef_media_route_t* route,
+                                         const void* message,
+                                         size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: route; type: refptr_diff
+  DCHECK(route);
+  if (!route)
+    return;
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return;
+
+  // Execute
+  CefMediaObserverCppToC::Get(self)->OnRouteMessageReceived(
+      CefMediaRouteCToCpp::Wrap(route), message, message_size);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaObserverCppToC::CefMediaObserverCppToC() {
+  GetStruct()->on_sinks = media_observer_on_sinks;
+  GetStruct()->on_routes = media_observer_on_routes;
+  GetStruct()->on_route_state_changed = media_observer_on_route_state_changed;
+  GetStruct()->on_route_message_received =
+      media_observer_on_route_message_received;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaObserverCppToC::~CefMediaObserverCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMediaObserver> CefCppToCRefCounted<
+    CefMediaObserverCppToC,
+    CefMediaObserver,
+    cef_media_observer_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_media_observer_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMediaObserverCppToC,
+                                   CefMediaObserver,
+                                   cef_media_observer_t>::kWrapperType =
+    WT_MEDIA_OBSERVER;
diff --git a/src/libcef_dll/cpptoc/media_observer_cpptoc.h b/src/libcef_dll/cpptoc/media_observer_cpptoc.h
new file mode 100644
index 0000000..77745a0
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_observer_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=14ef1b6f920a375e585174e4057c66221f3e1c05$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_OBSERVER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_OBSERVER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMediaObserverCppToC
+    : public CefCppToCRefCounted<CefMediaObserverCppToC,
+                                 CefMediaObserver,
+                                 cef_media_observer_t> {
+ public:
+  CefMediaObserverCppToC();
+  virtual ~CefMediaObserverCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MEDIA_OBSERVER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/media_route_cpptoc.cc b/src/libcef_dll/cpptoc/media_route_cpptoc.cc
new file mode 100644
index 0000000..a0f3624
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_route_cpptoc.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=47181b936b29037dd4ff5169d7175ac538f5be56$
+//
+
+#include "libcef_dll/cpptoc/media_route_cpptoc.h"
+#include "libcef_dll/cpptoc/media_sink_cpptoc.h"
+#include "libcef_dll/cpptoc/media_source_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+media_route_get_id(struct _cef_media_route_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefMediaRouteCppToC::Get(self)->GetId();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+struct _cef_media_source_t* CEF_CALLBACK
+media_route_get_source(struct _cef_media_route_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMediaSource> _retval =
+      CefMediaRouteCppToC::Get(self)->GetSource();
+
+  // Return type: refptr_same
+  return CefMediaSourceCppToC::Wrap(_retval);
+}
+
+struct _cef_media_sink_t* CEF_CALLBACK
+media_route_get_sink(struct _cef_media_route_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMediaSink> _retval = CefMediaRouteCppToC::Get(self)->GetSink();
+
+  // Return type: refptr_same
+  return CefMediaSinkCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK
+media_route_send_route_message(struct _cef_media_route_t* self,
+                               const void* message,
+                               size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return;
+
+  // Execute
+  CefMediaRouteCppToC::Get(self)->SendRouteMessage(message, message_size);
+}
+
+void CEF_CALLBACK media_route_terminate(struct _cef_media_route_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMediaRouteCppToC::Get(self)->Terminate();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCppToC::CefMediaRouteCppToC() {
+  GetStruct()->get_id = media_route_get_id;
+  GetStruct()->get_source = media_route_get_source;
+  GetStruct()->get_sink = media_route_get_sink;
+  GetStruct()->send_route_message = media_route_send_route_message;
+  GetStruct()->terminate = media_route_terminate;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCppToC::~CefMediaRouteCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMediaRoute>
+CefCppToCRefCounted<CefMediaRouteCppToC, CefMediaRoute, cef_media_route_t>::
+    UnwrapDerived(CefWrapperType type, cef_media_route_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMediaRouteCppToC,
+                                   CefMediaRoute,
+                                   cef_media_route_t>::kWrapperType =
+    WT_MEDIA_ROUTE;
diff --git a/src/libcef_dll/cpptoc/media_route_cpptoc.h b/src/libcef_dll/cpptoc/media_route_cpptoc.h
new file mode 100644
index 0000000..a30c27d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_route_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=26fe0f9bd956318d0e6c7e0216024b5b839429c2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefMediaRouteCppToC : public CefCppToCRefCounted<CefMediaRouteCppToC,
+                                                       CefMediaRoute,
+                                                       cef_media_route_t> {
+ public:
+  CefMediaRouteCppToC();
+  virtual ~CefMediaRouteCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/media_route_create_callback_cpptoc.cc b/src/libcef_dll/cpptoc/media_route_create_callback_cpptoc.cc
new file mode 100644
index 0000000..bef28ee
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_route_create_callback_cpptoc.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a27462c4bcc0ef4856230997718f95edae5c1ef1$
+//
+
+#include "libcef_dll/cpptoc/media_route_create_callback_cpptoc.h"
+#include "libcef_dll/ctocpp/media_route_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK media_route_create_callback_on_media_route_create_finished(
+    struct _cef_media_route_create_callback_t* self,
+    cef_media_route_create_result_t result,
+    const cef_string_t* error,
+    cef_media_route_t* route) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: error, route
+
+  // Execute
+  CefMediaRouteCreateCallbackCppToC::Get(self)->OnMediaRouteCreateFinished(
+      result, CefString(error), CefMediaRouteCToCpp::Wrap(route));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCreateCallbackCppToC::CefMediaRouteCreateCallbackCppToC() {
+  GetStruct()->on_media_route_create_finished =
+      media_route_create_callback_on_media_route_create_finished;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCreateCallbackCppToC::~CefMediaRouteCreateCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMediaRouteCreateCallback>
+CefCppToCRefCounted<CefMediaRouteCreateCallbackCppToC,
+                    CefMediaRouteCreateCallback,
+                    cef_media_route_create_callback_t>::
+    UnwrapDerived(CefWrapperType type, cef_media_route_create_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefMediaRouteCreateCallbackCppToC,
+                        CefMediaRouteCreateCallback,
+                        cef_media_route_create_callback_t>::kWrapperType =
+        WT_MEDIA_ROUTE_CREATE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/media_route_create_callback_cpptoc.h b/src/libcef_dll/cpptoc/media_route_create_callback_cpptoc.h
new file mode 100644
index 0000000..019d215
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_route_create_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8085b195054f43f67d4ee21d8b69b9e0ee8e2ac3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTE_CREATE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTE_CREATE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMediaRouteCreateCallbackCppToC
+    : public CefCppToCRefCounted<CefMediaRouteCreateCallbackCppToC,
+                                 CefMediaRouteCreateCallback,
+                                 cef_media_route_create_callback_t> {
+ public:
+  CefMediaRouteCreateCallbackCppToC();
+  virtual ~CefMediaRouteCreateCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTE_CREATE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/media_router_cpptoc.cc b/src/libcef_dll/cpptoc/media_router_cpptoc.cc
new file mode 100644
index 0000000..9bad391
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_router_cpptoc.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6b52fceffec99c0f274b5031266c20da5480d0c1$
+//
+
+#include "libcef_dll/cpptoc/media_router_cpptoc.h"
+#include "libcef_dll/cpptoc/media_sink_cpptoc.h"
+#include "libcef_dll/cpptoc/media_source_cpptoc.h"
+#include "libcef_dll/cpptoc/registration_cpptoc.h"
+#include "libcef_dll/ctocpp/media_observer_ctocpp.h"
+#include "libcef_dll/ctocpp/media_route_create_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_media_router_t* cef_media_router_get_global() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefMediaRouter> _retval = CefMediaRouter::GetGlobalMediaRouter();
+
+  // Return type: refptr_same
+  return CefMediaRouterCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+struct _cef_registration_t* CEF_CALLBACK
+media_router_add_observer(struct _cef_media_router_t* self,
+                          struct _cef_media_observer_t* observer) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: observer; type: refptr_diff
+  DCHECK(observer);
+  if (!observer)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRegistration> _retval =
+      CefMediaRouterCppToC::Get(self)->AddObserver(
+          CefMediaObserverCToCpp::Wrap(observer));
+
+  // Return type: refptr_same
+  return CefRegistrationCppToC::Wrap(_retval);
+}
+
+struct _cef_media_source_t* CEF_CALLBACK
+media_router_get_source(struct _cef_media_router_t* self,
+                        const cef_string_t* urn) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: urn; type: string_byref_const
+  DCHECK(urn);
+  if (!urn)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMediaSource> _retval =
+      CefMediaRouterCppToC::Get(self)->GetSource(CefString(urn));
+
+  // Return type: refptr_same
+  return CefMediaSourceCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK
+media_router_notify_current_sinks(struct _cef_media_router_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMediaRouterCppToC::Get(self)->NotifyCurrentSinks();
+}
+
+void CEF_CALLBACK
+media_router_create_route(struct _cef_media_router_t* self,
+                          struct _cef_media_source_t* source,
+                          struct _cef_media_sink_t* sink,
+                          struct _cef_media_route_create_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: source; type: refptr_same
+  DCHECK(source);
+  if (!source)
+    return;
+  // Verify param: sink; type: refptr_same
+  DCHECK(sink);
+  if (!sink)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefMediaRouterCppToC::Get(self)->CreateRoute(
+      CefMediaSourceCppToC::Unwrap(source), CefMediaSinkCppToC::Unwrap(sink),
+      CefMediaRouteCreateCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK
+media_router_notify_current_routes(struct _cef_media_router_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMediaRouterCppToC::Get(self)->NotifyCurrentRoutes();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaRouterCppToC::CefMediaRouterCppToC() {
+  GetStruct()->add_observer = media_router_add_observer;
+  GetStruct()->get_source = media_router_get_source;
+  GetStruct()->notify_current_sinks = media_router_notify_current_sinks;
+  GetStruct()->create_route = media_router_create_route;
+  GetStruct()->notify_current_routes = media_router_notify_current_routes;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaRouterCppToC::~CefMediaRouterCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMediaRouter>
+CefCppToCRefCounted<CefMediaRouterCppToC, CefMediaRouter, cef_media_router_t>::
+    UnwrapDerived(CefWrapperType type, cef_media_router_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMediaRouterCppToC,
+                                   CefMediaRouter,
+                                   cef_media_router_t>::kWrapperType =
+    WT_MEDIA_ROUTER;
diff --git a/src/libcef_dll/cpptoc/media_router_cpptoc.h b/src/libcef_dll/cpptoc/media_router_cpptoc.h
new file mode 100644
index 0000000..e8aaab5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_router_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cea747c7202b95684b7208a312da818ddb094c0a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefMediaRouterCppToC : public CefCppToCRefCounted<CefMediaRouterCppToC,
+                                                        CefMediaRouter,
+                                                        cef_media_router_t> {
+ public:
+  CefMediaRouterCppToC();
+  virtual ~CefMediaRouterCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MEDIA_ROUTER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/media_sink_cpptoc.cc b/src/libcef_dll/cpptoc/media_sink_cpptoc.cc
new file mode 100644
index 0000000..2a6258f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_sink_cpptoc.cc
@@ -0,0 +1,218 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f9893b2a7c5cd519d32d06a022e503340aa66bc3$
+//
+
+#include "libcef_dll/cpptoc/media_sink_cpptoc.h"
+#include "libcef_dll/cpptoc/media_source_cpptoc.h"
+#include "libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+media_sink_get_id(struct _cef_media_sink_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefMediaSinkCppToC::Get(self)->GetId();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK media_sink_is_valid(struct _cef_media_sink_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMediaSinkCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+media_sink_get_name(struct _cef_media_sink_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefMediaSinkCppToC::Get(self)->GetName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+media_sink_get_description(struct _cef_media_sink_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefMediaSinkCppToC::Get(self)->GetDescription();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_media_sink_icon_type_t CEF_CALLBACK
+media_sink_get_icon_type(struct _cef_media_sink_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CEF_MSIT_GENERIC;
+
+  // Execute
+  cef_media_sink_icon_type_t _retval =
+      CefMediaSinkCppToC::Get(self)->GetIconType();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK media_sink_get_device_info(
+    struct _cef_media_sink_t* self,
+    struct _cef_media_sink_device_info_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefMediaSinkCppToC::Get(self)->GetDeviceInfo(
+      CefMediaSinkDeviceInfoCallbackCToCpp::Wrap(callback));
+}
+
+int CEF_CALLBACK media_sink_is_cast_sink(struct _cef_media_sink_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMediaSinkCppToC::Get(self)->IsCastSink();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK media_sink_is_dial_sink(struct _cef_media_sink_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMediaSinkCppToC::Get(self)->IsDialSink();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+media_sink_is_compatible_with(struct _cef_media_sink_t* self,
+                              struct _cef_media_source_t* source) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: source; type: refptr_same
+  DCHECK(source);
+  if (!source)
+    return 0;
+
+  // Execute
+  bool _retval = CefMediaSinkCppToC::Get(self)->IsCompatibleWith(
+      CefMediaSourceCppToC::Unwrap(source));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaSinkCppToC::CefMediaSinkCppToC() {
+  GetStruct()->get_id = media_sink_get_id;
+  GetStruct()->is_valid = media_sink_is_valid;
+  GetStruct()->get_name = media_sink_get_name;
+  GetStruct()->get_description = media_sink_get_description;
+  GetStruct()->get_icon_type = media_sink_get_icon_type;
+  GetStruct()->get_device_info = media_sink_get_device_info;
+  GetStruct()->is_cast_sink = media_sink_is_cast_sink;
+  GetStruct()->is_dial_sink = media_sink_is_dial_sink;
+  GetStruct()->is_compatible_with = media_sink_is_compatible_with;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaSinkCppToC::~CefMediaSinkCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMediaSink>
+CefCppToCRefCounted<CefMediaSinkCppToC, CefMediaSink, cef_media_sink_t>::
+    UnwrapDerived(CefWrapperType type, cef_media_sink_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMediaSinkCppToC,
+                                   CefMediaSink,
+                                   cef_media_sink_t>::kWrapperType =
+    WT_MEDIA_SINK;
diff --git a/src/libcef_dll/cpptoc/media_sink_cpptoc.h b/src/libcef_dll/cpptoc/media_sink_cpptoc.h
new file mode 100644
index 0000000..315e520
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_sink_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=409478c735898bd4e13315183e185a80ae570b74$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_SINK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_SINK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefMediaSinkCppToC : public CefCppToCRefCounted<CefMediaSinkCppToC,
+                                                      CefMediaSink,
+                                                      cef_media_sink_t> {
+ public:
+  CefMediaSinkCppToC();
+  virtual ~CefMediaSinkCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MEDIA_SINK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.cc b/src/libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.cc
new file mode 100644
index 0000000..33ca0b7
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c22023ad9e7d7d80d72729dbebdf03f0b60f4dc9$
+//
+
+#include "libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK media_sink_device_info_callback_on_media_sink_device_info(
+    struct _cef_media_sink_device_info_callback_t* self,
+    const struct _cef_media_sink_device_info_t* device_info) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: device_info; type: struct_byref_const
+  DCHECK(device_info);
+  if (!device_info)
+    return;
+
+  // Translate param: device_info; type: struct_byref_const
+  CefMediaSinkDeviceInfo device_infoObj;
+  if (device_info)
+    device_infoObj.Set(*device_info, false);
+
+  // Execute
+  CefMediaSinkDeviceInfoCallbackCppToC::Get(self)->OnMediaSinkDeviceInfo(
+      device_infoObj);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaSinkDeviceInfoCallbackCppToC::CefMediaSinkDeviceInfoCallbackCppToC() {
+  GetStruct()->on_media_sink_device_info =
+      media_sink_device_info_callback_on_media_sink_device_info;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaSinkDeviceInfoCallbackCppToC::~CefMediaSinkDeviceInfoCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMediaSinkDeviceInfoCallback>
+CefCppToCRefCounted<CefMediaSinkDeviceInfoCallbackCppToC,
+                    CefMediaSinkDeviceInfoCallback,
+                    cef_media_sink_device_info_callback_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_media_sink_device_info_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefMediaSinkDeviceInfoCallbackCppToC,
+                        CefMediaSinkDeviceInfoCallback,
+                        cef_media_sink_device_info_callback_t>::kWrapperType =
+        WT_MEDIA_SINK_DEVICE_INFO_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.h b/src/libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.h
new file mode 100644
index 0000000..87c5d5c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5313c2346d5db18dca956f2dac98b73e049a4995$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_SINK_DEVICE_INFO_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_SINK_DEVICE_INFO_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMediaSinkDeviceInfoCallbackCppToC
+    : public CefCppToCRefCounted<CefMediaSinkDeviceInfoCallbackCppToC,
+                                 CefMediaSinkDeviceInfoCallback,
+                                 cef_media_sink_device_info_callback_t> {
+ public:
+  CefMediaSinkDeviceInfoCallbackCppToC();
+  virtual ~CefMediaSinkDeviceInfoCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MEDIA_SINK_DEVICE_INFO_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/media_source_cpptoc.cc b/src/libcef_dll/cpptoc/media_source_cpptoc.cc
new file mode 100644
index 0000000..09b9394
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_source_cpptoc.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ddd51b3c8020de1b10b00eef06d745a498168323$
+//
+
+#include "libcef_dll/cpptoc/media_source_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+media_source_get_id(struct _cef_media_source_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefMediaSourceCppToC::Get(self)->GetId();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK media_source_is_valid(struct _cef_media_source_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMediaSourceCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK media_source_is_cast_source(struct _cef_media_source_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMediaSourceCppToC::Get(self)->IsCastSource();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK media_source_is_dial_source(struct _cef_media_source_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMediaSourceCppToC::Get(self)->IsDialSource();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaSourceCppToC::CefMediaSourceCppToC() {
+  GetStruct()->get_id = media_source_get_id;
+  GetStruct()->is_valid = media_source_is_valid;
+  GetStruct()->is_cast_source = media_source_is_cast_source;
+  GetStruct()->is_dial_source = media_source_is_dial_source;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaSourceCppToC::~CefMediaSourceCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMediaSource>
+CefCppToCRefCounted<CefMediaSourceCppToC, CefMediaSource, cef_media_source_t>::
+    UnwrapDerived(CefWrapperType type, cef_media_source_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMediaSourceCppToC,
+                                   CefMediaSource,
+                                   cef_media_source_t>::kWrapperType =
+    WT_MEDIA_SOURCE;
diff --git a/src/libcef_dll/cpptoc/media_source_cpptoc.h b/src/libcef_dll/cpptoc/media_source_cpptoc.h
new file mode 100644
index 0000000..e8810bc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/media_source_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=aceafab724145242461fc9f5e7c76a365bb6d54c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_SOURCE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_SOURCE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefMediaSourceCppToC : public CefCppToCRefCounted<CefMediaSourceCppToC,
+                                                        CefMediaSource,
+                                                        cef_media_source_t> {
+ public:
+  CefMediaSourceCppToC();
+  virtual ~CefMediaSourceCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MEDIA_SOURCE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/menu_model_cpptoc.cc b/src/libcef_dll/cpptoc/menu_model_cpptoc.cc
new file mode 100644
index 0000000..be37782
--- /dev/null
+++ b/src/libcef_dll/cpptoc/menu_model_cpptoc.cc
@@ -0,0 +1,1302 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=eb78e3b37b654734b8b28449b713cc3eb3b9070d$
+//
+
+#include "libcef_dll/cpptoc/menu_model_cpptoc.h"
+#include "libcef_dll/ctocpp/menu_model_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_menu_model_t* cef_menu_model_create(
+    struct _cef_menu_model_delegate_t* delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: delegate; type: refptr_diff
+  DCHECK(delegate);
+  if (!delegate)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMenuModel> _retval =
+      CefMenuModel::CreateMenuModel(CefMenuModelDelegateCToCpp::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefMenuModelCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK menu_model_is_sub_menu(struct _cef_menu_model_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->IsSubMenu();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_clear(struct _cef_menu_model_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->Clear();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_count(struct _cef_menu_model_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefMenuModelCppToC::Get(self)->GetCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_add_separator(struct _cef_menu_model_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->AddSeparator();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_add_item(struct _cef_menu_model_t* self,
+                                     int command_id,
+                                     const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->AddItem(command_id, CefString(label));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_add_check_item(struct _cef_menu_model_t* self,
+                                           int command_id,
+                                           const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->AddCheckItem(command_id, CefString(label));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_add_radio_item(struct _cef_menu_model_t* self,
+                                           int command_id,
+                                           const cef_string_t* label,
+                                           int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->AddRadioItem(
+      command_id, CefString(label), group_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_menu_model_t* CEF_CALLBACK
+menu_model_add_sub_menu(struct _cef_menu_model_t* self,
+                        int command_id,
+                        const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMenuModel> _retval =
+      CefMenuModelCppToC::Get(self)->AddSubMenu(command_id, CefString(label));
+
+  // Return type: refptr_same
+  return CefMenuModelCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK menu_model_insert_separator_at(struct _cef_menu_model_t* self,
+                                                int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->InsertSeparatorAt(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_insert_item_at(struct _cef_menu_model_t* self,
+                                           int index,
+                                           int command_id,
+                                           const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->InsertItemAt(index, command_id,
+                                                             CefString(label));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_insert_check_item_at(struct _cef_menu_model_t* self,
+                                                 int index,
+                                                 int command_id,
+                                                 const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->InsertCheckItemAt(
+      index, command_id, CefString(label));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_insert_radio_item_at(struct _cef_menu_model_t* self,
+                                                 int index,
+                                                 int command_id,
+                                                 const cef_string_t* label,
+                                                 int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->InsertRadioItemAt(
+      index, command_id, CefString(label), group_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_menu_model_t* CEF_CALLBACK
+menu_model_insert_sub_menu_at(struct _cef_menu_model_t* self,
+                              int index,
+                              int command_id,
+                              const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMenuModel> _retval =
+      CefMenuModelCppToC::Get(self)->InsertSubMenuAt(index, command_id,
+                                                     CefString(label));
+
+  // Return type: refptr_same
+  return CefMenuModelCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK menu_model_remove(struct _cef_menu_model_t* self,
+                                   int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->Remove(command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_remove_at(struct _cef_menu_model_t* self,
+                                      int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->RemoveAt(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_index_of(struct _cef_menu_model_t* self,
+                                         int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefMenuModelCppToC::Get(self)->GetIndexOf(command_id);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_command_id_at(struct _cef_menu_model_t* self,
+                                              int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefMenuModelCppToC::Get(self)->GetCommandIdAt(index);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_command_id_at(struct _cef_menu_model_t* self,
+                                              int index,
+                                              int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->SetCommandIdAt(index, command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+menu_model_get_label(struct _cef_menu_model_t* self, int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefMenuModelCppToC::Get(self)->GetLabel(command_id);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+menu_model_get_label_at(struct _cef_menu_model_t* self, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefMenuModelCppToC::Get(self)->GetLabelAt(index);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK menu_model_set_label(struct _cef_menu_model_t* self,
+                                      int command_id,
+                                      const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->SetLabel(command_id, CefString(label));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_label_at(struct _cef_menu_model_t* self,
+                                         int index,
+                                         const cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: label; type: string_byref_const
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->SetLabelAt(index, CefString(label));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_menu_item_type_t CEF_CALLBACK
+menu_model_get_type(struct _cef_menu_model_t* self, int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return MENUITEMTYPE_NONE;
+
+  // Execute
+  cef_menu_item_type_t _retval =
+      CefMenuModelCppToC::Get(self)->GetType(command_id);
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_menu_item_type_t CEF_CALLBACK
+menu_model_get_type_at(struct _cef_menu_model_t* self, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return MENUITEMTYPE_NONE;
+
+  // Execute
+  cef_menu_item_type_t _retval =
+      CefMenuModelCppToC::Get(self)->GetTypeAt(index);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_group_id(struct _cef_menu_model_t* self,
+                                         int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefMenuModelCppToC::Get(self)->GetGroupId(command_id);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_group_id_at(struct _cef_menu_model_t* self,
+                                            int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefMenuModelCppToC::Get(self)->GetGroupIdAt(index);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_group_id(struct _cef_menu_model_t* self,
+                                         int command_id,
+                                         int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->SetGroupId(command_id, group_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_group_id_at(struct _cef_menu_model_t* self,
+                                            int index,
+                                            int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetGroupIdAt(index, group_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_menu_model_t* CEF_CALLBACK
+menu_model_get_sub_menu(struct _cef_menu_model_t* self, int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMenuModel> _retval =
+      CefMenuModelCppToC::Get(self)->GetSubMenu(command_id);
+
+  // Return type: refptr_same
+  return CefMenuModelCppToC::Wrap(_retval);
+}
+
+struct _cef_menu_model_t* CEF_CALLBACK
+menu_model_get_sub_menu_at(struct _cef_menu_model_t* self, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMenuModel> _retval =
+      CefMenuModelCppToC::Get(self)->GetSubMenuAt(index);
+
+  // Return type: refptr_same
+  return CefMenuModelCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK menu_model_is_visible(struct _cef_menu_model_t* self,
+                                       int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->IsVisible(command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_is_visible_at(struct _cef_menu_model_t* self,
+                                          int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->IsVisibleAt(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_visible(struct _cef_menu_model_t* self,
+                                        int command_id,
+                                        int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetVisible(
+      command_id, visible ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_visible_at(struct _cef_menu_model_t* self,
+                                           int index,
+                                           int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetVisibleAt(
+      index, visible ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_is_enabled(struct _cef_menu_model_t* self,
+                                       int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->IsEnabled(command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_is_enabled_at(struct _cef_menu_model_t* self,
+                                          int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->IsEnabledAt(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_enabled(struct _cef_menu_model_t* self,
+                                        int command_id,
+                                        int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetEnabled(
+      command_id, enabled ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_enabled_at(struct _cef_menu_model_t* self,
+                                           int index,
+                                           int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetEnabledAt(
+      index, enabled ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_is_checked(struct _cef_menu_model_t* self,
+                                       int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->IsChecked(command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_is_checked_at(struct _cef_menu_model_t* self,
+                                          int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->IsCheckedAt(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_checked(struct _cef_menu_model_t* self,
+                                        int command_id,
+                                        int checked) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetChecked(
+      command_id, checked ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_checked_at(struct _cef_menu_model_t* self,
+                                           int index,
+                                           int checked) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetCheckedAt(
+      index, checked ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_has_accelerator(struct _cef_menu_model_t* self,
+                                            int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->HasAccelerator(command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_has_accelerator_at(struct _cef_menu_model_t* self,
+                                               int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->HasAcceleratorAt(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_accelerator(struct _cef_menu_model_t* self,
+                                            int command_id,
+                                            int key_code,
+                                            int shift_pressed,
+                                            int ctrl_pressed,
+                                            int alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetAccelerator(
+      command_id, key_code, shift_pressed ? true : false,
+      ctrl_pressed ? true : false, alt_pressed ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_accelerator_at(struct _cef_menu_model_t* self,
+                                               int index,
+                                               int key_code,
+                                               int shift_pressed,
+                                               int ctrl_pressed,
+                                               int alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetAcceleratorAt(
+      index, key_code, shift_pressed ? true : false,
+      ctrl_pressed ? true : false, alt_pressed ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_remove_accelerator(struct _cef_menu_model_t* self,
+                                               int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->RemoveAccelerator(command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+menu_model_remove_accelerator_at(struct _cef_menu_model_t* self, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->RemoveAcceleratorAt(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_accelerator(struct _cef_menu_model_t* self,
+                                            int command_id,
+                                            int* key_code,
+                                            int* shift_pressed,
+                                            int* ctrl_pressed,
+                                            int* alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key_code; type: simple_byref
+  DCHECK(key_code);
+  if (!key_code)
+    return 0;
+  // Verify param: shift_pressed; type: bool_byref
+  DCHECK(shift_pressed);
+  if (!shift_pressed)
+    return 0;
+  // Verify param: ctrl_pressed; type: bool_byref
+  DCHECK(ctrl_pressed);
+  if (!ctrl_pressed)
+    return 0;
+  // Verify param: alt_pressed; type: bool_byref
+  DCHECK(alt_pressed);
+  if (!alt_pressed)
+    return 0;
+
+  // Translate param: key_code; type: simple_byref
+  int key_codeVal = key_code ? *key_code : 0;
+  // Translate param: shift_pressed; type: bool_byref
+  bool shift_pressedBool = (shift_pressed && *shift_pressed) ? true : false;
+  // Translate param: ctrl_pressed; type: bool_byref
+  bool ctrl_pressedBool = (ctrl_pressed && *ctrl_pressed) ? true : false;
+  // Translate param: alt_pressed; type: bool_byref
+  bool alt_pressedBool = (alt_pressed && *alt_pressed) ? true : false;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->GetAccelerator(
+      command_id, key_codeVal, shift_pressedBool, ctrl_pressedBool,
+      alt_pressedBool);
+
+  // Restore param: key_code; type: simple_byref
+  if (key_code)
+    *key_code = key_codeVal;
+  // Restore param: shift_pressed; type: bool_byref
+  if (shift_pressed)
+    *shift_pressed = shift_pressedBool ? true : false;
+  // Restore param: ctrl_pressed; type: bool_byref
+  if (ctrl_pressed)
+    *ctrl_pressed = ctrl_pressedBool ? true : false;
+  // Restore param: alt_pressed; type: bool_byref
+  if (alt_pressed)
+    *alt_pressed = alt_pressedBool ? true : false;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_accelerator_at(struct _cef_menu_model_t* self,
+                                               int index,
+                                               int* key_code,
+                                               int* shift_pressed,
+                                               int* ctrl_pressed,
+                                               int* alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: key_code; type: simple_byref
+  DCHECK(key_code);
+  if (!key_code)
+    return 0;
+  // Verify param: shift_pressed; type: bool_byref
+  DCHECK(shift_pressed);
+  if (!shift_pressed)
+    return 0;
+  // Verify param: ctrl_pressed; type: bool_byref
+  DCHECK(ctrl_pressed);
+  if (!ctrl_pressed)
+    return 0;
+  // Verify param: alt_pressed; type: bool_byref
+  DCHECK(alt_pressed);
+  if (!alt_pressed)
+    return 0;
+
+  // Translate param: key_code; type: simple_byref
+  int key_codeVal = key_code ? *key_code : 0;
+  // Translate param: shift_pressed; type: bool_byref
+  bool shift_pressedBool = (shift_pressed && *shift_pressed) ? true : false;
+  // Translate param: ctrl_pressed; type: bool_byref
+  bool ctrl_pressedBool = (ctrl_pressed && *ctrl_pressed) ? true : false;
+  // Translate param: alt_pressed; type: bool_byref
+  bool alt_pressedBool = (alt_pressed && *alt_pressed) ? true : false;
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->GetAcceleratorAt(
+      index, key_codeVal, shift_pressedBool, ctrl_pressedBool, alt_pressedBool);
+
+  // Restore param: key_code; type: simple_byref
+  if (key_code)
+    *key_code = key_codeVal;
+  // Restore param: shift_pressed; type: bool_byref
+  if (shift_pressed)
+    *shift_pressed = shift_pressedBool ? true : false;
+  // Restore param: ctrl_pressed; type: bool_byref
+  if (ctrl_pressed)
+    *ctrl_pressed = ctrl_pressedBool ? true : false;
+  // Restore param: alt_pressed; type: bool_byref
+  if (alt_pressed)
+    *alt_pressed = alt_pressedBool ? true : false;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_color(struct _cef_menu_model_t* self,
+                                      int command_id,
+                                      cef_menu_color_type_t color_type,
+                                      cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->SetColor(command_id, color_type, color);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_color_at(struct _cef_menu_model_t* self,
+                                         int index,
+                                         cef_menu_color_type_t color_type,
+                                         cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->SetColorAt(index, color_type, color);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_color(struct _cef_menu_model_t* self,
+                                      int command_id,
+                                      cef_menu_color_type_t color_type,
+                                      cef_color_t* color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: color; type: simple_byref
+  DCHECK(color);
+  if (!color)
+    return 0;
+
+  // Translate param: color; type: simple_byref
+  cef_color_t colorVal = color ? *color : 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->GetColor(command_id, color_type, colorVal);
+
+  // Restore param: color; type: simple_byref
+  if (color)
+    *color = colorVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_get_color_at(struct _cef_menu_model_t* self,
+                                         int index,
+                                         cef_menu_color_type_t color_type,
+                                         cef_color_t* color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: color; type: simple_byref
+  DCHECK(color);
+  if (!color)
+    return 0;
+
+  // Translate param: color; type: simple_byref
+  cef_color_t colorVal = color ? *color : 0;
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->GetColorAt(index, color_type, colorVal);
+
+  // Restore param: color; type: simple_byref
+  if (color)
+    *color = colorVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_font_list(struct _cef_menu_model_t* self,
+                                          int command_id,
+                                          const cef_string_t* font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: font_list
+
+  // Execute
+  bool _retval = CefMenuModelCppToC::Get(self)->SetFontList(
+      command_id, CefString(font_list));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_model_set_font_list_at(struct _cef_menu_model_t* self,
+                                             int index,
+                                             const cef_string_t* font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: font_list
+
+  // Execute
+  bool _retval =
+      CefMenuModelCppToC::Get(self)->SetFontListAt(index, CefString(font_list));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuModelCppToC::CefMenuModelCppToC() {
+  GetStruct()->is_sub_menu = menu_model_is_sub_menu;
+  GetStruct()->clear = menu_model_clear;
+  GetStruct()->get_count = menu_model_get_count;
+  GetStruct()->add_separator = menu_model_add_separator;
+  GetStruct()->add_item = menu_model_add_item;
+  GetStruct()->add_check_item = menu_model_add_check_item;
+  GetStruct()->add_radio_item = menu_model_add_radio_item;
+  GetStruct()->add_sub_menu = menu_model_add_sub_menu;
+  GetStruct()->insert_separator_at = menu_model_insert_separator_at;
+  GetStruct()->insert_item_at = menu_model_insert_item_at;
+  GetStruct()->insert_check_item_at = menu_model_insert_check_item_at;
+  GetStruct()->insert_radio_item_at = menu_model_insert_radio_item_at;
+  GetStruct()->insert_sub_menu_at = menu_model_insert_sub_menu_at;
+  GetStruct()->remove = menu_model_remove;
+  GetStruct()->remove_at = menu_model_remove_at;
+  GetStruct()->get_index_of = menu_model_get_index_of;
+  GetStruct()->get_command_id_at = menu_model_get_command_id_at;
+  GetStruct()->set_command_id_at = menu_model_set_command_id_at;
+  GetStruct()->get_label = menu_model_get_label;
+  GetStruct()->get_label_at = menu_model_get_label_at;
+  GetStruct()->set_label = menu_model_set_label;
+  GetStruct()->set_label_at = menu_model_set_label_at;
+  GetStruct()->get_type = menu_model_get_type;
+  GetStruct()->get_type_at = menu_model_get_type_at;
+  GetStruct()->get_group_id = menu_model_get_group_id;
+  GetStruct()->get_group_id_at = menu_model_get_group_id_at;
+  GetStruct()->set_group_id = menu_model_set_group_id;
+  GetStruct()->set_group_id_at = menu_model_set_group_id_at;
+  GetStruct()->get_sub_menu = menu_model_get_sub_menu;
+  GetStruct()->get_sub_menu_at = menu_model_get_sub_menu_at;
+  GetStruct()->is_visible = menu_model_is_visible;
+  GetStruct()->is_visible_at = menu_model_is_visible_at;
+  GetStruct()->set_visible = menu_model_set_visible;
+  GetStruct()->set_visible_at = menu_model_set_visible_at;
+  GetStruct()->is_enabled = menu_model_is_enabled;
+  GetStruct()->is_enabled_at = menu_model_is_enabled_at;
+  GetStruct()->set_enabled = menu_model_set_enabled;
+  GetStruct()->set_enabled_at = menu_model_set_enabled_at;
+  GetStruct()->is_checked = menu_model_is_checked;
+  GetStruct()->is_checked_at = menu_model_is_checked_at;
+  GetStruct()->set_checked = menu_model_set_checked;
+  GetStruct()->set_checked_at = menu_model_set_checked_at;
+  GetStruct()->has_accelerator = menu_model_has_accelerator;
+  GetStruct()->has_accelerator_at = menu_model_has_accelerator_at;
+  GetStruct()->set_accelerator = menu_model_set_accelerator;
+  GetStruct()->set_accelerator_at = menu_model_set_accelerator_at;
+  GetStruct()->remove_accelerator = menu_model_remove_accelerator;
+  GetStruct()->remove_accelerator_at = menu_model_remove_accelerator_at;
+  GetStruct()->get_accelerator = menu_model_get_accelerator;
+  GetStruct()->get_accelerator_at = menu_model_get_accelerator_at;
+  GetStruct()->set_color = menu_model_set_color;
+  GetStruct()->set_color_at = menu_model_set_color_at;
+  GetStruct()->get_color = menu_model_get_color;
+  GetStruct()->get_color_at = menu_model_get_color_at;
+  GetStruct()->set_font_list = menu_model_set_font_list;
+  GetStruct()->set_font_list_at = menu_model_set_font_list_at;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuModelCppToC::~CefMenuModelCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMenuModel>
+CefCppToCRefCounted<CefMenuModelCppToC, CefMenuModel, cef_menu_model_t>::
+    UnwrapDerived(CefWrapperType type, cef_menu_model_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMenuModelCppToC,
+                                   CefMenuModel,
+                                   cef_menu_model_t>::kWrapperType =
+    WT_MENU_MODEL;
diff --git a/src/libcef_dll/cpptoc/menu_model_cpptoc.h b/src/libcef_dll/cpptoc/menu_model_cpptoc.h
new file mode 100644
index 0000000..0abf1bf
--- /dev/null
+++ b/src/libcef_dll/cpptoc/menu_model_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=12c4868cce94b5e3773ffcd539ba33558f65fd7f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MENU_MODEL_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MENU_MODEL_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_menu_model_capi.h"
+#include "include/cef_menu_model.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefMenuModelCppToC : public CefCppToCRefCounted<CefMenuModelCppToC,
+                                                      CefMenuModel,
+                                                      cef_menu_model_t> {
+ public:
+  CefMenuModelCppToC();
+  virtual ~CefMenuModelCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MENU_MODEL_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/menu_model_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/menu_model_delegate_cpptoc.cc
new file mode 100644
index 0000000..ef0cb47
--- /dev/null
+++ b/src/libcef_dll/cpptoc/menu_model_delegate_cpptoc.cc
@@ -0,0 +1,222 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=65dec86d6113bdacec295b3b46c6f35ed299f42a$
+//
+
+#include "libcef_dll/cpptoc/menu_model_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/menu_model_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+menu_model_delegate_execute_command(struct _cef_menu_model_delegate_t* self,
+                                    cef_menu_model_t* menu_model,
+                                    int command_id,
+                                    cef_event_flags_t event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+
+  // Execute
+  CefMenuModelDelegateCppToC::Get(self)->ExecuteCommand(
+      CefMenuModelCToCpp::Wrap(menu_model), command_id, event_flags);
+}
+
+void CEF_CALLBACK
+menu_model_delegate_mouse_outside_menu(struct _cef_menu_model_delegate_t* self,
+                                       cef_menu_model_t* menu_model,
+                                       const cef_point_t* screen_point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+  // Verify param: screen_point; type: simple_byref_const
+  DCHECK(screen_point);
+  if (!screen_point)
+    return;
+
+  // Translate param: screen_point; type: simple_byref_const
+  CefPoint screen_pointVal = screen_point ? *screen_point : CefPoint();
+
+  // Execute
+  CefMenuModelDelegateCppToC::Get(self)->MouseOutsideMenu(
+      CefMenuModelCToCpp::Wrap(menu_model), screen_pointVal);
+}
+
+void CEF_CALLBACK menu_model_delegate_unhandled_open_submenu(
+    struct _cef_menu_model_delegate_t* self,
+    cef_menu_model_t* menu_model,
+    int is_rtl) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+
+  // Execute
+  CefMenuModelDelegateCppToC::Get(self)->UnhandledOpenSubmenu(
+      CefMenuModelCToCpp::Wrap(menu_model), is_rtl ? true : false);
+}
+
+void CEF_CALLBACK menu_model_delegate_unhandled_close_submenu(
+    struct _cef_menu_model_delegate_t* self,
+    cef_menu_model_t* menu_model,
+    int is_rtl) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+
+  // Execute
+  CefMenuModelDelegateCppToC::Get(self)->UnhandledCloseSubmenu(
+      CefMenuModelCToCpp::Wrap(menu_model), is_rtl ? true : false);
+}
+
+void CEF_CALLBACK
+menu_model_delegate_menu_will_show(struct _cef_menu_model_delegate_t* self,
+                                   cef_menu_model_t* menu_model) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+
+  // Execute
+  CefMenuModelDelegateCppToC::Get(self)->MenuWillShow(
+      CefMenuModelCToCpp::Wrap(menu_model));
+}
+
+void CEF_CALLBACK
+menu_model_delegate_menu_closed(struct _cef_menu_model_delegate_t* self,
+                                cef_menu_model_t* menu_model) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+
+  // Execute
+  CefMenuModelDelegateCppToC::Get(self)->MenuClosed(
+      CefMenuModelCToCpp::Wrap(menu_model));
+}
+
+int CEF_CALLBACK
+menu_model_delegate_format_label(struct _cef_menu_model_delegate_t* self,
+                                 cef_menu_model_t* menu_model,
+                                 cef_string_t* label) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model);
+  if (!menu_model)
+    return 0;
+  // Verify param: label; type: string_byref
+  DCHECK(label);
+  if (!label)
+    return 0;
+
+  // Translate param: label; type: string_byref
+  CefString labelStr(label);
+
+  // Execute
+  bool _retval = CefMenuModelDelegateCppToC::Get(self)->FormatLabel(
+      CefMenuModelCToCpp::Wrap(menu_model), labelStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuModelDelegateCppToC::CefMenuModelDelegateCppToC() {
+  GetStruct()->execute_command = menu_model_delegate_execute_command;
+  GetStruct()->mouse_outside_menu = menu_model_delegate_mouse_outside_menu;
+  GetStruct()->unhandled_open_submenu =
+      menu_model_delegate_unhandled_open_submenu;
+  GetStruct()->unhandled_close_submenu =
+      menu_model_delegate_unhandled_close_submenu;
+  GetStruct()->menu_will_show = menu_model_delegate_menu_will_show;
+  GetStruct()->menu_closed = menu_model_delegate_menu_closed;
+  GetStruct()->format_label = menu_model_delegate_format_label;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuModelDelegateCppToC::~CefMenuModelDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMenuModelDelegate> CefCppToCRefCounted<
+    CefMenuModelDelegateCppToC,
+    CefMenuModelDelegate,
+    cef_menu_model_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                              cef_menu_model_delegate_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMenuModelDelegateCppToC,
+                                   CefMenuModelDelegate,
+                                   cef_menu_model_delegate_t>::kWrapperType =
+    WT_MENU_MODEL_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/menu_model_delegate_cpptoc.h b/src/libcef_dll/cpptoc/menu_model_delegate_cpptoc.h
new file mode 100644
index 0000000..6bec7b3
--- /dev/null
+++ b/src/libcef_dll/cpptoc/menu_model_delegate_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=45a9a9cb0e55a5878d0342c313d5e734945b0f13$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_MENU_MODEL_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_MENU_MODEL_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_menu_model_capi.h"
+#include "include/capi/cef_menu_model_delegate_capi.h"
+#include "include/cef_menu_model.h"
+#include "include/cef_menu_model_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMenuModelDelegateCppToC
+    : public CefCppToCRefCounted<CefMenuModelDelegateCppToC,
+                                 CefMenuModelDelegate,
+                                 cef_menu_model_delegate_t> {
+ public:
+  CefMenuModelDelegateCppToC();
+  virtual ~CefMenuModelDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_MENU_MODEL_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/navigation_entry_cpptoc.cc b/src/libcef_dll/cpptoc/navigation_entry_cpptoc.cc
new file mode 100644
index 0000000..a97a4ba
--- /dev/null
+++ b/src/libcef_dll/cpptoc/navigation_entry_cpptoc.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cdcd1b3b9458442c480e69c5f7cee2ed48587e8b$
+//
+
+#include "libcef_dll/cpptoc/navigation_entry_cpptoc.h"
+#include "libcef_dll/cpptoc/sslstatus_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+navigation_entry_is_valid(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefNavigationEntryCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+navigation_entry_get_url(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefNavigationEntryCppToC::Get(self)->GetURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+navigation_entry_get_display_url(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefNavigationEntryCppToC::Get(self)->GetDisplayURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+navigation_entry_get_original_url(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefNavigationEntryCppToC::Get(self)->GetOriginalURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+navigation_entry_get_title(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefNavigationEntryCppToC::Get(self)->GetTitle();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_transition_type_t CEF_CALLBACK
+navigation_entry_get_transition_type(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return TT_EXPLICIT;
+
+  // Execute
+  cef_transition_type_t _retval =
+      CefNavigationEntryCppToC::Get(self)->GetTransitionType();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+navigation_entry_has_post_data(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefNavigationEntryCppToC::Get(self)->HasPostData();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_time_t CEF_CALLBACK
+navigation_entry_get_completion_time(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefTime();
+
+  // Execute
+  cef_time_t _retval = CefNavigationEntryCppToC::Get(self)->GetCompletionTime();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+navigation_entry_get_http_status_code(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefNavigationEntryCppToC::Get(self)->GetHttpStatusCode();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_sslstatus_t* CEF_CALLBACK
+navigation_entry_get_sslstatus(struct _cef_navigation_entry_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefSSLStatus> _retval =
+      CefNavigationEntryCppToC::Get(self)->GetSSLStatus();
+
+  // Return type: refptr_same
+  return CefSSLStatusCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryCppToC::CefNavigationEntryCppToC() {
+  GetStruct()->is_valid = navigation_entry_is_valid;
+  GetStruct()->get_url = navigation_entry_get_url;
+  GetStruct()->get_display_url = navigation_entry_get_display_url;
+  GetStruct()->get_original_url = navigation_entry_get_original_url;
+  GetStruct()->get_title = navigation_entry_get_title;
+  GetStruct()->get_transition_type = navigation_entry_get_transition_type;
+  GetStruct()->has_post_data = navigation_entry_has_post_data;
+  GetStruct()->get_completion_time = navigation_entry_get_completion_time;
+  GetStruct()->get_http_status_code = navigation_entry_get_http_status_code;
+  GetStruct()->get_sslstatus = navigation_entry_get_sslstatus;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryCppToC::~CefNavigationEntryCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefNavigationEntry> CefCppToCRefCounted<
+    CefNavigationEntryCppToC,
+    CefNavigationEntry,
+    cef_navigation_entry_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_navigation_entry_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefNavigationEntryCppToC,
+                                   CefNavigationEntry,
+                                   cef_navigation_entry_t>::kWrapperType =
+    WT_NAVIGATION_ENTRY;
diff --git a/src/libcef_dll/cpptoc/navigation_entry_cpptoc.h b/src/libcef_dll/cpptoc/navigation_entry_cpptoc.h
new file mode 100644
index 0000000..20d8519
--- /dev/null
+++ b/src/libcef_dll/cpptoc/navigation_entry_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9482954c3bc3d6261594f7610d27f4c368c84f8e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_NAVIGATION_ENTRY_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_NAVIGATION_ENTRY_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_navigation_entry_capi.h"
+#include "include/cef_navigation_entry.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefNavigationEntryCppToC
+    : public CefCppToCRefCounted<CefNavigationEntryCppToC,
+                                 CefNavigationEntry,
+                                 cef_navigation_entry_t> {
+ public:
+  CefNavigationEntryCppToC();
+  virtual ~CefNavigationEntryCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_NAVIGATION_ENTRY_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.cc b/src/libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.cc
new file mode 100644
index 0000000..04754e4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=59b1875dfe400b08651488a293a1381a236dce05$
+//
+
+#include "libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h"
+#include "libcef_dll/ctocpp/navigation_entry_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+navigation_entry_visitor_visit(struct _cef_navigation_entry_visitor_t* self,
+                               struct _cef_navigation_entry_t* entry,
+                               int current,
+                               int index,
+                               int total) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: entry; type: refptr_diff
+  DCHECK(entry);
+  if (!entry)
+    return 0;
+
+  // Execute
+  bool _retval = CefNavigationEntryVisitorCppToC::Get(self)->Visit(
+      CefNavigationEntryCToCpp::Wrap(entry), current ? true : false, index,
+      total);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryVisitorCppToC::CefNavigationEntryVisitorCppToC() {
+  GetStruct()->visit = navigation_entry_visitor_visit;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryVisitorCppToC::~CefNavigationEntryVisitorCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefNavigationEntryVisitor>
+CefCppToCRefCounted<CefNavigationEntryVisitorCppToC,
+                    CefNavigationEntryVisitor,
+                    cef_navigation_entry_visitor_t>::
+    UnwrapDerived(CefWrapperType type, cef_navigation_entry_visitor_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefNavigationEntryVisitorCppToC,
+                        CefNavigationEntryVisitor,
+                        cef_navigation_entry_visitor_t>::kWrapperType =
+        WT_NAVIGATION_ENTRY_VISITOR;
diff --git a/src/libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h b/src/libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h
new file mode 100644
index 0000000..ff5218b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3ddb0546cdf9464eb96fc255ec1a5d2449d93825$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_NAVIGATION_ENTRY_VISITOR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_NAVIGATION_ENTRY_VISITOR_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefNavigationEntryVisitorCppToC
+    : public CefCppToCRefCounted<CefNavigationEntryVisitorCppToC,
+                                 CefNavigationEntryVisitor,
+                                 cef_navigation_entry_visitor_t> {
+ public:
+  CefNavigationEntryVisitorCppToC();
+  virtual ~CefNavigationEntryVisitorCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_NAVIGATION_ENTRY_VISITOR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/pdf_print_callback_cpptoc.cc b/src/libcef_dll/cpptoc/pdf_print_callback_cpptoc.cc
new file mode 100644
index 0000000..56e92bc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/pdf_print_callback_cpptoc.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1b0468f3f28054e3ce6304fb341b5747289c24b7$
+//
+
+#include "libcef_dll/cpptoc/pdf_print_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+pdf_print_callback_on_pdf_print_finished(struct _cef_pdf_print_callback_t* self,
+                                         const cef_string_t* path,
+                                         int ok) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+
+  // Execute
+  CefPdfPrintCallbackCppToC::Get(self)->OnPdfPrintFinished(CefString(path),
+                                                           ok ? true : false);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPdfPrintCallbackCppToC::CefPdfPrintCallbackCppToC() {
+  GetStruct()->on_pdf_print_finished = pdf_print_callback_on_pdf_print_finished;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPdfPrintCallbackCppToC::~CefPdfPrintCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefPdfPrintCallback> CefCppToCRefCounted<
+    CefPdfPrintCallbackCppToC,
+    CefPdfPrintCallback,
+    cef_pdf_print_callback_t>::UnwrapDerived(CefWrapperType type,
+                                             cef_pdf_print_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPdfPrintCallbackCppToC,
+                                   CefPdfPrintCallback,
+                                   cef_pdf_print_callback_t>::kWrapperType =
+    WT_PDF_PRINT_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/pdf_print_callback_cpptoc.h b/src/libcef_dll/cpptoc/pdf_print_callback_cpptoc.h
new file mode 100644
index 0000000..3633df6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/pdf_print_callback_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ab53cbd4d37273668814d44914434a097fb95aa0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_PDF_PRINT_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_PDF_PRINT_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPdfPrintCallbackCppToC
+    : public CefCppToCRefCounted<CefPdfPrintCallbackCppToC,
+                                 CefPdfPrintCallback,
+                                 cef_pdf_print_callback_t> {
+ public:
+  CefPdfPrintCallbackCppToC();
+  virtual ~CefPdfPrintCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_PDF_PRINT_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/post_data_cpptoc.cc b/src/libcef_dll/cpptoc/post_data_cpptoc.cc
new file mode 100644
index 0000000..20ef306
--- /dev/null
+++ b/src/libcef_dll/cpptoc/post_data_cpptoc.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8ef0b53920c747cb67c9ddca0082b9c36307200b$
+//
+
+#include "libcef_dll/cpptoc/post_data_cpptoc.h"
+#include <algorithm>
+#include "libcef_dll/cpptoc/post_data_element_cpptoc.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_post_data_t* cef_post_data_create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefPostData> _retval = CefPostData::Create();
+
+  // Return type: refptr_same
+  return CefPostDataCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK post_data_is_read_only(struct _cef_post_data_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPostDataCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+post_data_has_excluded_elements(struct _cef_post_data_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPostDataCppToC::Get(self)->HasExcludedElements();
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK post_data_get_element_count(struct _cef_post_data_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefPostDataCppToC::Get(self)->GetElementCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+post_data_get_elements(struct _cef_post_data_t* self,
+                       size_t* elementsCount,
+                       struct _cef_post_data_element_t** elements) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: elements; type: refptr_vec_same_byref
+  DCHECK(elementsCount && (*elementsCount == 0 || elements));
+  if (!elementsCount || (*elementsCount > 0 && !elements))
+    return;
+
+  // Translate param: elements; type: refptr_vec_same_byref
+  std::vector<CefRefPtr<CefPostDataElement>> elementsList;
+  if (elementsCount && *elementsCount > 0 && elements) {
+    for (size_t i = 0; i < *elementsCount; ++i) {
+      elementsList.push_back(CefPostDataElementCppToC::Unwrap(elements[i]));
+    }
+  }
+
+  // Execute
+  CefPostDataCppToC::Get(self)->GetElements(elementsList);
+
+  // Restore param: elements; type: refptr_vec_same_byref
+  if (elementsCount && elements) {
+    *elementsCount = std::min(elementsList.size(), *elementsCount);
+    if (*elementsCount > 0) {
+      for (size_t i = 0; i < *elementsCount; ++i) {
+        elements[i] = CefPostDataElementCppToC::Wrap(elementsList[i]);
+      }
+    }
+  }
+}
+
+int CEF_CALLBACK
+post_data_remove_element(struct _cef_post_data_t* self,
+                         struct _cef_post_data_element_t* element) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: element; type: refptr_same
+  DCHECK(element);
+  if (!element)
+    return 0;
+
+  // Execute
+  bool _retval = CefPostDataCppToC::Get(self)->RemoveElement(
+      CefPostDataElementCppToC::Unwrap(element));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+post_data_add_element(struct _cef_post_data_t* self,
+                      struct _cef_post_data_element_t* element) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: element; type: refptr_same
+  DCHECK(element);
+  if (!element)
+    return 0;
+
+  // Execute
+  bool _retval = CefPostDataCppToC::Get(self)->AddElement(
+      CefPostDataElementCppToC::Unwrap(element));
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK post_data_remove_elements(struct _cef_post_data_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPostDataCppToC::Get(self)->RemoveElements();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPostDataCppToC::CefPostDataCppToC() {
+  GetStruct()->is_read_only = post_data_is_read_only;
+  GetStruct()->has_excluded_elements = post_data_has_excluded_elements;
+  GetStruct()->get_element_count = post_data_get_element_count;
+  GetStruct()->get_elements = post_data_get_elements;
+  GetStruct()->remove_element = post_data_remove_element;
+  GetStruct()->add_element = post_data_add_element;
+  GetStruct()->remove_elements = post_data_remove_elements;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPostDataCppToC::~CefPostDataCppToC() {}
+
+template <>
+CefRefPtr<CefPostData>
+CefCppToCRefCounted<CefPostDataCppToC, CefPostData, cef_post_data_t>::
+    UnwrapDerived(CefWrapperType type, cef_post_data_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPostDataCppToC,
+                                   CefPostData,
+                                   cef_post_data_t>::kWrapperType =
+    WT_POST_DATA;
diff --git a/src/libcef_dll/cpptoc/post_data_cpptoc.h b/src/libcef_dll/cpptoc/post_data_cpptoc.h
new file mode 100644
index 0000000..78a09c0
--- /dev/null
+++ b/src/libcef_dll/cpptoc/post_data_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bda55d6e344a4164b2e95a350d57e8224851c092$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_POST_DATA_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_POST_DATA_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_capi.h"
+#include "include/cef_request.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefPostDataCppToC : public CefCppToCRefCounted<CefPostDataCppToC,
+                                                     CefPostData,
+                                                     cef_post_data_t> {
+ public:
+  CefPostDataCppToC();
+  virtual ~CefPostDataCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_POST_DATA_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/post_data_element_cpptoc.cc b/src/libcef_dll/cpptoc/post_data_element_cpptoc.cc
new file mode 100644
index 0000000..c9c3473
--- /dev/null
+++ b/src/libcef_dll/cpptoc/post_data_element_cpptoc.cc
@@ -0,0 +1,195 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3d0ca8d8f17478eebc8e50181ed0c10a7c28df4f$
+//
+
+#include "libcef_dll/cpptoc/post_data_element_cpptoc.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_post_data_element_t* cef_post_data_element_create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefPostDataElement> _retval = CefPostDataElement::Create();
+
+  // Return type: refptr_same
+  return CefPostDataElementCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+post_data_element_is_read_only(struct _cef_post_data_element_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPostDataElementCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+post_data_element_set_to_empty(struct _cef_post_data_element_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPostDataElementCppToC::Get(self)->SetToEmpty();
+}
+
+void CEF_CALLBACK
+post_data_element_set_to_file(struct _cef_post_data_element_t* self,
+                              const cef_string_t* fileName) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(fileName);
+  if (!fileName)
+    return;
+
+  // Execute
+  CefPostDataElementCppToC::Get(self)->SetToFile(CefString(fileName));
+}
+
+void CEF_CALLBACK
+post_data_element_set_to_bytes(struct _cef_post_data_element_t* self,
+                               size_t size,
+                               const void* bytes) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bytes; type: simple_byaddr
+  DCHECK(bytes);
+  if (!bytes)
+    return;
+
+  // Execute
+  CefPostDataElementCppToC::Get(self)->SetToBytes(size, bytes);
+}
+
+cef_postdataelement_type_t CEF_CALLBACK
+post_data_element_get_type(struct _cef_post_data_element_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return PDE_TYPE_EMPTY;
+
+  // Execute
+  cef_postdataelement_type_t _retval =
+      CefPostDataElementCppToC::Get(self)->GetType();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+post_data_element_get_file(struct _cef_post_data_element_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefPostDataElementCppToC::Get(self)->GetFile();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+size_t CEF_CALLBACK
+post_data_element_get_bytes_count(struct _cef_post_data_element_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefPostDataElementCppToC::Get(self)->GetBytesCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+size_t CEF_CALLBACK
+post_data_element_get_bytes(struct _cef_post_data_element_t* self,
+                            size_t size,
+                            void* bytes) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: bytes; type: simple_byaddr
+  DCHECK(bytes);
+  if (!bytes)
+    return 0;
+
+  // Execute
+  size_t _retval = CefPostDataElementCppToC::Get(self)->GetBytes(size, bytes);
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPostDataElementCppToC::CefPostDataElementCppToC() {
+  GetStruct()->is_read_only = post_data_element_is_read_only;
+  GetStruct()->set_to_empty = post_data_element_set_to_empty;
+  GetStruct()->set_to_file = post_data_element_set_to_file;
+  GetStruct()->set_to_bytes = post_data_element_set_to_bytes;
+  GetStruct()->get_type = post_data_element_get_type;
+  GetStruct()->get_file = post_data_element_get_file;
+  GetStruct()->get_bytes_count = post_data_element_get_bytes_count;
+  GetStruct()->get_bytes = post_data_element_get_bytes;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPostDataElementCppToC::~CefPostDataElementCppToC() {}
+
+template <>
+CefRefPtr<CefPostDataElement> CefCppToCRefCounted<
+    CefPostDataElementCppToC,
+    CefPostDataElement,
+    cef_post_data_element_t>::UnwrapDerived(CefWrapperType type,
+                                            cef_post_data_element_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPostDataElementCppToC,
+                                   CefPostDataElement,
+                                   cef_post_data_element_t>::kWrapperType =
+    WT_POST_DATA_ELEMENT;
diff --git a/src/libcef_dll/cpptoc/post_data_element_cpptoc.h b/src/libcef_dll/cpptoc/post_data_element_cpptoc.h
new file mode 100644
index 0000000..21f89ae
--- /dev/null
+++ b/src/libcef_dll/cpptoc/post_data_element_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e186563dbf1f9cc2cb3a5c464477a4367ceb759a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_POST_DATA_ELEMENT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_POST_DATA_ELEMENT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_capi.h"
+#include "include/cef_request.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefPostDataElementCppToC
+    : public CefCppToCRefCounted<CefPostDataElementCppToC,
+                                 CefPostDataElement,
+                                 cef_post_data_element_t> {
+ public:
+  CefPostDataElementCppToC();
+  virtual ~CefPostDataElementCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_POST_DATA_ELEMENT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/print_dialog_callback_cpptoc.cc b/src/libcef_dll/cpptoc/print_dialog_callback_cpptoc.cc
new file mode 100644
index 0000000..ed0c6ef
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_dialog_callback_cpptoc.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=dd4497ea885d54be767940af4148f3c7c9f22298$
+//
+
+#include "libcef_dll/cpptoc/print_dialog_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/print_settings_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+print_dialog_callback_cont(struct _cef_print_dialog_callback_t* self,
+                           struct _cef_print_settings_t* settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: settings; type: refptr_same
+  DCHECK(settings);
+  if (!settings)
+    return;
+
+  // Execute
+  CefPrintDialogCallbackCppToC::Get(self)->Continue(
+      CefPrintSettingsCppToC::Unwrap(settings));
+}
+
+void CEF_CALLBACK
+print_dialog_callback_cancel(struct _cef_print_dialog_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintDialogCallbackCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintDialogCallbackCppToC::CefPrintDialogCallbackCppToC() {
+  GetStruct()->cont = print_dialog_callback_cont;
+  GetStruct()->cancel = print_dialog_callback_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintDialogCallbackCppToC::~CefPrintDialogCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefPrintDialogCallback> CefCppToCRefCounted<
+    CefPrintDialogCallbackCppToC,
+    CefPrintDialogCallback,
+    cef_print_dialog_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                cef_print_dialog_callback_t*
+                                                    s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPrintDialogCallbackCppToC,
+                                   CefPrintDialogCallback,
+                                   cef_print_dialog_callback_t>::kWrapperType =
+    WT_PRINT_DIALOG_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/print_dialog_callback_cpptoc.h b/src/libcef_dll/cpptoc/print_dialog_callback_cpptoc.h
new file mode 100644
index 0000000..c225dba
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_dialog_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cb1d9f76304854b4beeac4ca95c8e66f988d6736$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_PRINT_DIALOG_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_PRINT_DIALOG_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_print_handler_capi.h"
+#include "include/cef_print_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefPrintDialogCallbackCppToC
+    : public CefCppToCRefCounted<CefPrintDialogCallbackCppToC,
+                                 CefPrintDialogCallback,
+                                 cef_print_dialog_callback_t> {
+ public:
+  CefPrintDialogCallbackCppToC();
+  virtual ~CefPrintDialogCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_PRINT_DIALOG_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/print_handler_cpptoc.cc b/src/libcef_dll/cpptoc/print_handler_cpptoc.cc
new file mode 100644
index 0000000..c1ae9a6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_handler_cpptoc.cc
@@ -0,0 +1,214 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=565613dbb99435d203b41dbb8717334527a4acb7$
+//
+
+#include "libcef_dll/cpptoc/print_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/print_dialog_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/print_job_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/print_settings_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+print_handler_on_print_start(struct _cef_print_handler_t* self,
+                             cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefPrintHandlerCppToC::Get(self)->OnPrintStart(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+void CEF_CALLBACK
+print_handler_on_print_settings(struct _cef_print_handler_t* self,
+                                cef_browser_t* browser,
+                                struct _cef_print_settings_t* settings,
+                                int get_defaults) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: settings; type: refptr_diff
+  DCHECK(settings);
+  if (!settings)
+    return;
+
+  // Execute
+  CefPrintHandlerCppToC::Get(self)->OnPrintSettings(
+      CefBrowserCToCpp::Wrap(browser), CefPrintSettingsCToCpp::Wrap(settings),
+      get_defaults ? true : false);
+}
+
+int CEF_CALLBACK
+print_handler_on_print_dialog(struct _cef_print_handler_t* self,
+                              cef_browser_t* browser,
+                              int has_selection,
+                              cef_print_dialog_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Execute
+  bool _retval = CefPrintHandlerCppToC::Get(self)->OnPrintDialog(
+      CefBrowserCToCpp::Wrap(browser), has_selection ? true : false,
+      CefPrintDialogCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+print_handler_on_print_job(struct _cef_print_handler_t* self,
+                           cef_browser_t* browser,
+                           const cef_string_t* document_name,
+                           const cef_string_t* pdf_file_path,
+                           cef_print_job_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: document_name; type: string_byref_const
+  DCHECK(document_name);
+  if (!document_name)
+    return 0;
+  // Verify param: pdf_file_path; type: string_byref_const
+  DCHECK(pdf_file_path);
+  if (!pdf_file_path)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Execute
+  bool _retval = CefPrintHandlerCppToC::Get(self)->OnPrintJob(
+      CefBrowserCToCpp::Wrap(browser), CefString(document_name),
+      CefString(pdf_file_path), CefPrintJobCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+print_handler_on_print_reset(struct _cef_print_handler_t* self,
+                             cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefPrintHandlerCppToC::Get(self)->OnPrintReset(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+cef_size_t CEF_CALLBACK
+print_handler_get_pdf_paper_size(struct _cef_print_handler_t* self,
+                                 int device_units_per_inch) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefPrintHandlerCppToC::Get(self)->GetPdfPaperSize(device_units_per_inch);
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintHandlerCppToC::CefPrintHandlerCppToC() {
+  GetStruct()->on_print_start = print_handler_on_print_start;
+  GetStruct()->on_print_settings = print_handler_on_print_settings;
+  GetStruct()->on_print_dialog = print_handler_on_print_dialog;
+  GetStruct()->on_print_job = print_handler_on_print_job;
+  GetStruct()->on_print_reset = print_handler_on_print_reset;
+  GetStruct()->get_pdf_paper_size = print_handler_get_pdf_paper_size;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintHandlerCppToC::~CefPrintHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefPrintHandler> CefCppToCRefCounted<
+    CefPrintHandlerCppToC,
+    CefPrintHandler,
+    cef_print_handler_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_print_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPrintHandlerCppToC,
+                                   CefPrintHandler,
+                                   cef_print_handler_t>::kWrapperType =
+    WT_PRINT_HANDLER;
diff --git a/src/libcef_dll/cpptoc/print_handler_cpptoc.h b/src/libcef_dll/cpptoc/print_handler_cpptoc.h
new file mode 100644
index 0000000..e334ba4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1d17820ee8a5c67aea024f69f7c651949bf5e9aa$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_PRINT_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_PRINT_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_print_handler_capi.h"
+#include "include/cef_print_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPrintHandlerCppToC : public CefCppToCRefCounted<CefPrintHandlerCppToC,
+                                                         CefPrintHandler,
+                                                         cef_print_handler_t> {
+ public:
+  CefPrintHandlerCppToC();
+  virtual ~CefPrintHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_PRINT_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/print_job_callback_cpptoc.cc b/src/libcef_dll/cpptoc/print_job_callback_cpptoc.cc
new file mode 100644
index 0000000..04cabb9
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_job_callback_cpptoc.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f79bd331e5b7091c916778dd4d850da4b9bbbc4d$
+//
+
+#include "libcef_dll/cpptoc/print_job_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+print_job_callback_cont(struct _cef_print_job_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintJobCallbackCppToC::Get(self)->Continue();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintJobCallbackCppToC::CefPrintJobCallbackCppToC() {
+  GetStruct()->cont = print_job_callback_cont;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintJobCallbackCppToC::~CefPrintJobCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefPrintJobCallback> CefCppToCRefCounted<
+    CefPrintJobCallbackCppToC,
+    CefPrintJobCallback,
+    cef_print_job_callback_t>::UnwrapDerived(CefWrapperType type,
+                                             cef_print_job_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPrintJobCallbackCppToC,
+                                   CefPrintJobCallback,
+                                   cef_print_job_callback_t>::kWrapperType =
+    WT_PRINT_JOB_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/print_job_callback_cpptoc.h b/src/libcef_dll/cpptoc/print_job_callback_cpptoc.h
new file mode 100644
index 0000000..680257a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_job_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=574630f06356876279d93d2eafb2b10bcc2f1b06$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_PRINT_JOB_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_PRINT_JOB_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_print_handler_capi.h"
+#include "include/cef_print_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefPrintJobCallbackCppToC
+    : public CefCppToCRefCounted<CefPrintJobCallbackCppToC,
+                                 CefPrintJobCallback,
+                                 cef_print_job_callback_t> {
+ public:
+  CefPrintJobCallbackCppToC();
+  virtual ~CefPrintJobCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_PRINT_JOB_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/print_settings_cpptoc.cc b/src/libcef_dll/cpptoc/print_settings_cpptoc.cc
new file mode 100644
index 0000000..2675930
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_settings_cpptoc.cc
@@ -0,0 +1,493 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a4cc31347e4f06a79f9d308cee502bb18be09996$
+//
+
+#include "libcef_dll/cpptoc/print_settings_cpptoc.h"
+#include <algorithm>
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_print_settings_t* cef_print_settings_create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefPrintSettings> _retval = CefPrintSettings::Create();
+
+  // Return type: refptr_same
+  return CefPrintSettingsCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK print_settings_is_valid(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPrintSettingsCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+print_settings_is_read_only(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPrintSettingsCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+print_settings_set_orientation(struct _cef_print_settings_t* self,
+                               int landscape) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetOrientation(landscape ? true : false);
+}
+
+int CEF_CALLBACK
+print_settings_is_landscape(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPrintSettingsCppToC::Get(self)->IsLandscape();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK print_settings_set_printer_printable_area(
+    struct _cef_print_settings_t* self,
+    const cef_size_t* physical_size_device_units,
+    const cef_rect_t* printable_area_device_units,
+    int landscape_needs_flip) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: physical_size_device_units; type: simple_byref_const
+  DCHECK(physical_size_device_units);
+  if (!physical_size_device_units)
+    return;
+  // Verify param: printable_area_device_units; type: simple_byref_const
+  DCHECK(printable_area_device_units);
+  if (!printable_area_device_units)
+    return;
+
+  // Translate param: physical_size_device_units; type: simple_byref_const
+  CefSize physical_size_device_unitsVal =
+      physical_size_device_units ? *physical_size_device_units : CefSize();
+  // Translate param: printable_area_device_units; type: simple_byref_const
+  CefRect printable_area_device_unitsVal =
+      printable_area_device_units ? *printable_area_device_units : CefRect();
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetPrinterPrintableArea(
+      physical_size_device_unitsVal, printable_area_device_unitsVal,
+      landscape_needs_flip ? true : false);
+}
+
+void CEF_CALLBACK
+print_settings_set_device_name(struct _cef_print_settings_t* self,
+                               const cef_string_t* name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: name
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetDeviceName(CefString(name));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+print_settings_get_device_name(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefPrintSettingsCppToC::Get(self)->GetDeviceName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK print_settings_set_dpi(struct _cef_print_settings_t* self,
+                                         int dpi) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetDPI(dpi);
+}
+
+int CEF_CALLBACK print_settings_get_dpi(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefPrintSettingsCppToC::Get(self)->GetDPI();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+print_settings_set_page_ranges(struct _cef_print_settings_t* self,
+                               size_t rangesCount,
+                               cef_range_t const* ranges) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: ranges; type: simple_vec_byref_const
+  DCHECK(rangesCount == 0 || ranges);
+  if (rangesCount > 0 && !ranges)
+    return;
+
+  // Translate param: ranges; type: simple_vec_byref_const
+  std::vector<CefRange> rangesList;
+  if (rangesCount > 0) {
+    for (size_t i = 0; i < rangesCount; ++i) {
+      CefRange rangesVal = ranges[i];
+      rangesList.push_back(rangesVal);
+    }
+  }
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetPageRanges(rangesList);
+}
+
+size_t CEF_CALLBACK
+print_settings_get_page_ranges_count(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefPrintSettingsCppToC::Get(self)->GetPageRangesCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+print_settings_get_page_ranges(struct _cef_print_settings_t* self,
+                               size_t* rangesCount,
+                               cef_range_t* ranges) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: ranges; type: simple_vec_byref
+  DCHECK(rangesCount && (*rangesCount == 0 || ranges));
+  if (!rangesCount || (*rangesCount > 0 && !ranges))
+    return;
+
+  // Translate param: ranges; type: simple_vec_byref
+  std::vector<CefRange> rangesList;
+  if (rangesCount && *rangesCount > 0 && ranges) {
+    for (size_t i = 0; i < *rangesCount; ++i) {
+      rangesList.push_back(ranges[i]);
+    }
+  }
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->GetPageRanges(rangesList);
+
+  // Restore param: ranges; type: simple_vec_byref
+  if (rangesCount && ranges) {
+    *rangesCount = std::min(rangesList.size(), *rangesCount);
+    if (*rangesCount > 0) {
+      for (size_t i = 0; i < *rangesCount; ++i) {
+        ranges[i] = rangesList[i];
+      }
+    }
+  }
+}
+
+void CEF_CALLBACK
+print_settings_set_selection_only(struct _cef_print_settings_t* self,
+                                  int selection_only) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetSelectionOnly(selection_only ? true
+                                                                     : false);
+}
+
+int CEF_CALLBACK
+print_settings_is_selection_only(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPrintSettingsCppToC::Get(self)->IsSelectionOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK print_settings_set_collate(struct _cef_print_settings_t* self,
+                                             int collate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetCollate(collate ? true : false);
+}
+
+int CEF_CALLBACK
+print_settings_will_collate(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPrintSettingsCppToC::Get(self)->WillCollate();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+print_settings_set_color_model(struct _cef_print_settings_t* self,
+                               cef_color_model_t model) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetColorModel(model);
+}
+
+cef_color_model_t CEF_CALLBACK
+print_settings_get_color_model(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return COLOR_MODEL_UNKNOWN;
+
+  // Execute
+  cef_color_model_t _retval =
+      CefPrintSettingsCppToC::Get(self)->GetColorModel();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK print_settings_set_copies(struct _cef_print_settings_t* self,
+                                            int copies) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetCopies(copies);
+}
+
+int CEF_CALLBACK print_settings_get_copies(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefPrintSettingsCppToC::Get(self)->GetCopies();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+print_settings_set_duplex_mode(struct _cef_print_settings_t* self,
+                               cef_duplex_mode_t mode) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPrintSettingsCppToC::Get(self)->SetDuplexMode(mode);
+}
+
+cef_duplex_mode_t CEF_CALLBACK
+print_settings_get_duplex_mode(struct _cef_print_settings_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return DUPLEX_MODE_UNKNOWN;
+
+  // Execute
+  cef_duplex_mode_t _retval =
+      CefPrintSettingsCppToC::Get(self)->GetDuplexMode();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintSettingsCppToC::CefPrintSettingsCppToC() {
+  GetStruct()->is_valid = print_settings_is_valid;
+  GetStruct()->is_read_only = print_settings_is_read_only;
+  GetStruct()->set_orientation = print_settings_set_orientation;
+  GetStruct()->is_landscape = print_settings_is_landscape;
+  GetStruct()->set_printer_printable_area =
+      print_settings_set_printer_printable_area;
+  GetStruct()->set_device_name = print_settings_set_device_name;
+  GetStruct()->get_device_name = print_settings_get_device_name;
+  GetStruct()->set_dpi = print_settings_set_dpi;
+  GetStruct()->get_dpi = print_settings_get_dpi;
+  GetStruct()->set_page_ranges = print_settings_set_page_ranges;
+  GetStruct()->get_page_ranges_count = print_settings_get_page_ranges_count;
+  GetStruct()->get_page_ranges = print_settings_get_page_ranges;
+  GetStruct()->set_selection_only = print_settings_set_selection_only;
+  GetStruct()->is_selection_only = print_settings_is_selection_only;
+  GetStruct()->set_collate = print_settings_set_collate;
+  GetStruct()->will_collate = print_settings_will_collate;
+  GetStruct()->set_color_model = print_settings_set_color_model;
+  GetStruct()->get_color_model = print_settings_get_color_model;
+  GetStruct()->set_copies = print_settings_set_copies;
+  GetStruct()->get_copies = print_settings_get_copies;
+  GetStruct()->set_duplex_mode = print_settings_set_duplex_mode;
+  GetStruct()->get_duplex_mode = print_settings_get_duplex_mode;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintSettingsCppToC::~CefPrintSettingsCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefPrintSettings> CefCppToCRefCounted<
+    CefPrintSettingsCppToC,
+    CefPrintSettings,
+    cef_print_settings_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_print_settings_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPrintSettingsCppToC,
+                                   CefPrintSettings,
+                                   cef_print_settings_t>::kWrapperType =
+    WT_PRINT_SETTINGS;
diff --git a/src/libcef_dll/cpptoc/print_settings_cpptoc.h b/src/libcef_dll/cpptoc/print_settings_cpptoc.h
new file mode 100644
index 0000000..9a289a2
--- /dev/null
+++ b/src/libcef_dll/cpptoc/print_settings_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=30197dfacc1edef6d11a3a0151141749b3a4e36e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_PRINT_SETTINGS_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_PRINT_SETTINGS_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_print_settings_capi.h"
+#include "include/cef_print_settings.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefPrintSettingsCppToC
+    : public CefCppToCRefCounted<CefPrintSettingsCppToC,
+                                 CefPrintSettings,
+                                 cef_print_settings_t> {
+ public:
+  CefPrintSettingsCppToC();
+  virtual ~CefPrintSettingsCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_PRINT_SETTINGS_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/process_message_cpptoc.cc b/src/libcef_dll/cpptoc/process_message_cpptoc.cc
new file mode 100644
index 0000000..22cff8d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/process_message_cpptoc.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7cb7bbf83d9ae4cd85a4493036150f4c47c8b879$
+//
+
+#include "libcef_dll/cpptoc/process_message_cpptoc.h"
+#include "libcef_dll/cpptoc/list_value_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_process_message_t* cef_process_message_create(
+    const cef_string_t* name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefProcessMessage> _retval =
+      CefProcessMessage::Create(CefString(name));
+
+  // Return type: refptr_same
+  return CefProcessMessageCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK process_message_is_valid(struct _cef_process_message_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefProcessMessageCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+process_message_is_read_only(struct _cef_process_message_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefProcessMessageCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_process_message_t* CEF_CALLBACK
+process_message_copy(struct _cef_process_message_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefProcessMessage> _retval =
+      CefProcessMessageCppToC::Get(self)->Copy();
+
+  // Return type: refptr_same
+  return CefProcessMessageCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+process_message_get_name(struct _cef_process_message_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefProcessMessageCppToC::Get(self)->GetName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+struct _cef_list_value_t* CEF_CALLBACK
+process_message_get_argument_list(struct _cef_process_message_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefListValue> _retval =
+      CefProcessMessageCppToC::Get(self)->GetArgumentList();
+
+  // Return type: refptr_same
+  return CefListValueCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefProcessMessageCppToC::CefProcessMessageCppToC() {
+  GetStruct()->is_valid = process_message_is_valid;
+  GetStruct()->is_read_only = process_message_is_read_only;
+  GetStruct()->copy = process_message_copy;
+  GetStruct()->get_name = process_message_get_name;
+  GetStruct()->get_argument_list = process_message_get_argument_list;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefProcessMessageCppToC::~CefProcessMessageCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefProcessMessage> CefCppToCRefCounted<
+    CefProcessMessageCppToC,
+    CefProcessMessage,
+    cef_process_message_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_process_message_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefProcessMessageCppToC,
+                                   CefProcessMessage,
+                                   cef_process_message_t>::kWrapperType =
+    WT_PROCESS_MESSAGE;
diff --git a/src/libcef_dll/cpptoc/process_message_cpptoc.h b/src/libcef_dll/cpptoc/process_message_cpptoc.h
new file mode 100644
index 0000000..614b569
--- /dev/null
+++ b/src/libcef_dll/cpptoc/process_message_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=593f843c0c82f66bba1e16943dd8a381ddbedd4f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_PROCESS_MESSAGE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_PROCESS_MESSAGE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_process_message_capi.h"
+#include "include/cef_process_message.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefProcessMessageCppToC
+    : public CefCppToCRefCounted<CefProcessMessageCppToC,
+                                 CefProcessMessage,
+                                 cef_process_message_t> {
+ public:
+  CefProcessMessageCppToC();
+  virtual ~CefProcessMessageCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_PROCESS_MESSAGE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/read_handler_cpptoc.cc b/src/libcef_dll/cpptoc/read_handler_cpptoc.cc
new file mode 100644
index 0000000..e431ec4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/read_handler_cpptoc.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=17dd4be0a0f3c4ff17da7837b6d01094d690243d$
+//
+
+#include "libcef_dll/cpptoc/read_handler_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+size_t CEF_CALLBACK read_handler_read(struct _cef_read_handler_t* self,
+                                      void* ptr,
+                                      size_t size,
+                                      size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = CefReadHandlerCppToC::Get(self)->Read(ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK read_handler_seek(struct _cef_read_handler_t* self,
+                                   int64 offset,
+                                   int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefReadHandlerCppToC::Get(self)->Seek(offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+int64 CEF_CALLBACK read_handler_tell(struct _cef_read_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefReadHandlerCppToC::Get(self)->Tell();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK read_handler_eof(struct _cef_read_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefReadHandlerCppToC::Get(self)->Eof();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK read_handler_may_block(struct _cef_read_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefReadHandlerCppToC::Get(self)->MayBlock();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefReadHandlerCppToC::CefReadHandlerCppToC() {
+  GetStruct()->read = read_handler_read;
+  GetStruct()->seek = read_handler_seek;
+  GetStruct()->tell = read_handler_tell;
+  GetStruct()->eof = read_handler_eof;
+  GetStruct()->may_block = read_handler_may_block;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefReadHandlerCppToC::~CefReadHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefReadHandler>
+CefCppToCRefCounted<CefReadHandlerCppToC, CefReadHandler, cef_read_handler_t>::
+    UnwrapDerived(CefWrapperType type, cef_read_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefReadHandlerCppToC,
+                                   CefReadHandler,
+                                   cef_read_handler_t>::kWrapperType =
+    WT_READ_HANDLER;
diff --git a/src/libcef_dll/cpptoc/read_handler_cpptoc.h b/src/libcef_dll/cpptoc/read_handler_cpptoc.h
new file mode 100644
index 0000000..54d49ba
--- /dev/null
+++ b/src/libcef_dll/cpptoc/read_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ad93c34f2e152573b9ff385a8c4d5f115fc89f9b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_READ_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_READ_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefReadHandlerCppToC : public CefCppToCRefCounted<CefReadHandlerCppToC,
+                                                        CefReadHandler,
+                                                        cef_read_handler_t> {
+ public:
+  CefReadHandlerCppToC();
+  virtual ~CefReadHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_READ_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/register_cdm_callback_cpptoc.cc b/src/libcef_dll/cpptoc/register_cdm_callback_cpptoc.cc
new file mode 100644
index 0000000..d3db64f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/register_cdm_callback_cpptoc.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4ec6b7d3ac59768907b1096d463f76a6caa45c1e$
+//
+
+#include "libcef_dll/cpptoc/register_cdm_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK register_cdm_callback_on_cdm_registration_complete(
+    struct _cef_register_cdm_callback_t* self,
+    cef_cdm_registration_error_t result,
+    const cef_string_t* error_message) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: error_message
+
+  // Execute
+  CefRegisterCdmCallbackCppToC::Get(self)->OnCdmRegistrationComplete(
+      result, CefString(error_message));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRegisterCdmCallbackCppToC::CefRegisterCdmCallbackCppToC() {
+  GetStruct()->on_cdm_registration_complete =
+      register_cdm_callback_on_cdm_registration_complete;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRegisterCdmCallbackCppToC::~CefRegisterCdmCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefRegisterCdmCallback> CefCppToCRefCounted<
+    CefRegisterCdmCallbackCppToC,
+    CefRegisterCdmCallback,
+    cef_register_cdm_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                cef_register_cdm_callback_t*
+                                                    s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRegisterCdmCallbackCppToC,
+                                   CefRegisterCdmCallback,
+                                   cef_register_cdm_callback_t>::kWrapperType =
+    WT_REGISTER_CDM_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/register_cdm_callback_cpptoc.h b/src/libcef_dll/cpptoc/register_cdm_callback_cpptoc.h
new file mode 100644
index 0000000..290c9c3
--- /dev/null
+++ b/src/libcef_dll/cpptoc/register_cdm_callback_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c27e3384b7800e52335f9c4be4a14ef68ee9ce7d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_REGISTER_CDM_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_REGISTER_CDM_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRegisterCdmCallbackCppToC
+    : public CefCppToCRefCounted<CefRegisterCdmCallbackCppToC,
+                                 CefRegisterCdmCallback,
+                                 cef_register_cdm_callback_t> {
+ public:
+  CefRegisterCdmCallbackCppToC();
+  virtual ~CefRegisterCdmCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_REGISTER_CDM_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/registration_cpptoc.cc b/src/libcef_dll/cpptoc/registration_cpptoc.cc
new file mode 100644
index 0000000..d620215
--- /dev/null
+++ b/src/libcef_dll/cpptoc/registration_cpptoc.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=37c8c576c5bccd43f6124095e4ad55e88bc1a185$
+//
+
+#include "libcef_dll/cpptoc/registration_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRegistrationCppToC::CefRegistrationCppToC() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRegistrationCppToC::~CefRegistrationCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefRegistration>
+CefCppToCRefCounted<CefRegistrationCppToC,
+                    CefRegistration,
+                    cef_registration_t>::UnwrapDerived(CefWrapperType type,
+                                                       cef_registration_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRegistrationCppToC,
+                                   CefRegistration,
+                                   cef_registration_t>::kWrapperType =
+    WT_REGISTRATION;
diff --git a/src/libcef_dll/cpptoc/registration_cpptoc.h b/src/libcef_dll/cpptoc/registration_cpptoc.h
new file mode 100644
index 0000000..3e1eb52
--- /dev/null
+++ b/src/libcef_dll/cpptoc/registration_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e27d69c1509e74c7f5d69f5829c27fa65e47fc2d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_REGISTRATION_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_REGISTRATION_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_registration_capi.h"
+#include "include/cef_registration.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefRegistrationCppToC : public CefCppToCRefCounted<CefRegistrationCppToC,
+                                                         CefRegistration,
+                                                         cef_registration_t> {
+ public:
+  CefRegistrationCppToC();
+  virtual ~CefRegistrationCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_REGISTRATION_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/render_handler_cpptoc.cc b/src/libcef_dll/cpptoc/render_handler_cpptoc.cc
new file mode 100644
index 0000000..0ab491e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/render_handler_cpptoc.cc
@@ -0,0 +1,572 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2e22f210ff06337ac41e71a00b9dc6edce08e6d8$
+//
+
+#include "libcef_dll/cpptoc/render_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/accessibility_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/drag_data_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_accessibility_handler_t* CEF_CALLBACK
+render_handler_get_accessibility_handler(struct _cef_render_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefAccessibilityHandler> _retval =
+      CefRenderHandlerCppToC::Get(self)->GetAccessibilityHandler();
+
+  // Return type: refptr_same
+  return CefAccessibilityHandlerCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK
+render_handler_get_root_screen_rect(struct _cef_render_handler_t* self,
+                                    cef_browser_t* browser,
+                                    cef_rect_t* rect) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: rect; type: simple_byref
+  DCHECK(rect);
+  if (!rect)
+    return 0;
+
+  // Translate param: rect; type: simple_byref
+  CefRect rectVal = rect ? *rect : CefRect();
+
+  // Execute
+  bool _retval = CefRenderHandlerCppToC::Get(self)->GetRootScreenRect(
+      CefBrowserCToCpp::Wrap(browser), rectVal);
+
+  // Restore param: rect; type: simple_byref
+  if (rect)
+    *rect = rectVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+render_handler_get_view_rect(struct _cef_render_handler_t* self,
+                             cef_browser_t* browser,
+                             cef_rect_t* rect) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: rect; type: simple_byref
+  DCHECK(rect);
+  if (!rect)
+    return;
+
+  // Translate param: rect; type: simple_byref
+  CefRect rectVal = rect ? *rect : CefRect();
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->GetViewRect(
+      CefBrowserCToCpp::Wrap(browser), rectVal);
+
+  // Restore param: rect; type: simple_byref
+  if (rect)
+    *rect = rectVal;
+}
+
+int CEF_CALLBACK
+render_handler_get_screen_point(struct _cef_render_handler_t* self,
+                                cef_browser_t* browser,
+                                int viewX,
+                                int viewY,
+                                int* screenX,
+                                int* screenY) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: screenX; type: simple_byref
+  DCHECK(screenX);
+  if (!screenX)
+    return 0;
+  // Verify param: screenY; type: simple_byref
+  DCHECK(screenY);
+  if (!screenY)
+    return 0;
+
+  // Translate param: screenX; type: simple_byref
+  int screenXVal = screenX ? *screenX : 0;
+  // Translate param: screenY; type: simple_byref
+  int screenYVal = screenY ? *screenY : 0;
+
+  // Execute
+  bool _retval = CefRenderHandlerCppToC::Get(self)->GetScreenPoint(
+      CefBrowserCToCpp::Wrap(browser), viewX, viewY, screenXVal, screenYVal);
+
+  // Restore param: screenX; type: simple_byref
+  if (screenX)
+    *screenX = screenXVal;
+  // Restore param: screenY; type: simple_byref
+  if (screenY)
+    *screenY = screenYVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+render_handler_get_screen_info(struct _cef_render_handler_t* self,
+                               cef_browser_t* browser,
+                               struct _cef_screen_info_t* screen_info) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: screen_info; type: struct_byref
+  DCHECK(screen_info);
+  if (!screen_info)
+    return 0;
+
+  // Translate param: screen_info; type: struct_byref
+  CefScreenInfo screen_infoObj;
+  if (screen_info)
+    screen_infoObj.AttachTo(*screen_info);
+
+  // Execute
+  bool _retval = CefRenderHandlerCppToC::Get(self)->GetScreenInfo(
+      CefBrowserCToCpp::Wrap(browser), screen_infoObj);
+
+  // Restore param: screen_info; type: struct_byref
+  if (screen_info)
+    screen_infoObj.DetachTo(*screen_info);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+render_handler_on_popup_show(struct _cef_render_handler_t* self,
+                             cef_browser_t* browser,
+                             int show) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnPopupShow(
+      CefBrowserCToCpp::Wrap(browser), show ? true : false);
+}
+
+void CEF_CALLBACK
+render_handler_on_popup_size(struct _cef_render_handler_t* self,
+                             cef_browser_t* browser,
+                             const cef_rect_t* rect) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: rect; type: simple_byref_const
+  DCHECK(rect);
+  if (!rect)
+    return;
+
+  // Translate param: rect; type: simple_byref_const
+  CefRect rectVal = rect ? *rect : CefRect();
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnPopupSize(
+      CefBrowserCToCpp::Wrap(browser), rectVal);
+}
+
+void CEF_CALLBACK render_handler_on_paint(struct _cef_render_handler_t* self,
+                                          cef_browser_t* browser,
+                                          cef_paint_element_type_t type,
+                                          size_t dirtyRectsCount,
+                                          cef_rect_t const* dirtyRects,
+                                          const void* buffer,
+                                          int width,
+                                          int height) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: dirtyRects; type: simple_vec_byref_const
+  DCHECK(dirtyRectsCount == 0 || dirtyRects);
+  if (dirtyRectsCount > 0 && !dirtyRects)
+    return;
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return;
+
+  // Translate param: dirtyRects; type: simple_vec_byref_const
+  std::vector<CefRect> dirtyRectsList;
+  if (dirtyRectsCount > 0) {
+    for (size_t i = 0; i < dirtyRectsCount; ++i) {
+      CefRect dirtyRectsVal = dirtyRects[i];
+      dirtyRectsList.push_back(dirtyRectsVal);
+    }
+  }
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnPaint(CefBrowserCToCpp::Wrap(browser),
+                                             type, dirtyRectsList, buffer,
+                                             width, height);
+}
+
+void CEF_CALLBACK
+render_handler_on_accelerated_paint(struct _cef_render_handler_t* self,
+                                    cef_browser_t* browser,
+                                    cef_paint_element_type_t type,
+                                    size_t dirtyRectsCount,
+                                    cef_rect_t const* dirtyRects,
+                                    void* shared_handle) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: dirtyRects; type: simple_vec_byref_const
+  DCHECK(dirtyRectsCount == 0 || dirtyRects);
+  if (dirtyRectsCount > 0 && !dirtyRects)
+    return;
+  // Verify param: shared_handle; type: simple_byaddr
+  DCHECK(shared_handle);
+  if (!shared_handle)
+    return;
+
+  // Translate param: dirtyRects; type: simple_vec_byref_const
+  std::vector<CefRect> dirtyRectsList;
+  if (dirtyRectsCount > 0) {
+    for (size_t i = 0; i < dirtyRectsCount; ++i) {
+      CefRect dirtyRectsVal = dirtyRects[i];
+      dirtyRectsList.push_back(dirtyRectsVal);
+    }
+  }
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnAcceleratedPaint(
+      CefBrowserCToCpp::Wrap(browser), type, dirtyRectsList, shared_handle);
+}
+
+void CEF_CALLBACK render_handler_on_cursor_change(
+    struct _cef_render_handler_t* self,
+    cef_browser_t* browser,
+    cef_cursor_handle_t cursor,
+    cef_cursor_type_t type,
+    const struct _cef_cursor_info_t* custom_cursor_info) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: custom_cursor_info; type: struct_byref_const
+  DCHECK(custom_cursor_info);
+  if (!custom_cursor_info)
+    return;
+
+  // Translate param: custom_cursor_info; type: struct_byref_const
+  CefCursorInfo custom_cursor_infoObj;
+  if (custom_cursor_info)
+    custom_cursor_infoObj.Set(*custom_cursor_info, false);
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnCursorChange(
+      CefBrowserCToCpp::Wrap(browser), cursor, type, custom_cursor_infoObj);
+}
+
+int CEF_CALLBACK
+render_handler_start_dragging(struct _cef_render_handler_t* self,
+                              cef_browser_t* browser,
+                              cef_drag_data_t* drag_data,
+                              cef_drag_operations_mask_t allowed_ops,
+                              int x,
+                              int y) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: drag_data; type: refptr_diff
+  DCHECK(drag_data);
+  if (!drag_data)
+    return 0;
+
+  // Execute
+  bool _retval = CefRenderHandlerCppToC::Get(self)->StartDragging(
+      CefBrowserCToCpp::Wrap(browser), CefDragDataCToCpp::Wrap(drag_data),
+      allowed_ops, x, y);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+render_handler_update_drag_cursor(struct _cef_render_handler_t* self,
+                                  cef_browser_t* browser,
+                                  cef_drag_operations_mask_t operation) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->UpdateDragCursor(
+      CefBrowserCToCpp::Wrap(browser), operation);
+}
+
+void CEF_CALLBACK
+render_handler_on_scroll_offset_changed(struct _cef_render_handler_t* self,
+                                        cef_browser_t* browser,
+                                        double x,
+                                        double y) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnScrollOffsetChanged(
+      CefBrowserCToCpp::Wrap(browser), x, y);
+}
+
+void CEF_CALLBACK render_handler_on_ime_composition_range_changed(
+    struct _cef_render_handler_t* self,
+    cef_browser_t* browser,
+    const cef_range_t* selected_range,
+    size_t character_boundsCount,
+    cef_rect_t const* character_bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: selected_range; type: simple_byref_const
+  DCHECK(selected_range);
+  if (!selected_range)
+    return;
+  // Verify param: character_bounds; type: simple_vec_byref_const
+  DCHECK(character_boundsCount == 0 || character_bounds);
+  if (character_boundsCount > 0 && !character_bounds)
+    return;
+
+  // Translate param: selected_range; type: simple_byref_const
+  CefRange selected_rangeVal = selected_range ? *selected_range : CefRange();
+  // Translate param: character_bounds; type: simple_vec_byref_const
+  std::vector<CefRect> character_boundsList;
+  if (character_boundsCount > 0) {
+    for (size_t i = 0; i < character_boundsCount; ++i) {
+      CefRect character_boundsVal = character_bounds[i];
+      character_boundsList.push_back(character_boundsVal);
+    }
+  }
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnImeCompositionRangeChanged(
+      CefBrowserCToCpp::Wrap(browser), selected_rangeVal, character_boundsList);
+}
+
+void CEF_CALLBACK
+render_handler_on_text_selection_changed(struct _cef_render_handler_t* self,
+                                         cef_browser_t* browser,
+                                         const cef_string_t* selected_text,
+                                         const cef_range_t* selected_range) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Unverified params: selected_text, selected_range
+
+  // Translate param: selected_range; type: simple_byref_const
+  CefRange selected_rangeVal = selected_range ? *selected_range : CefRange();
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnTextSelectionChanged(
+      CefBrowserCToCpp::Wrap(browser), CefString(selected_text),
+      selected_rangeVal);
+}
+
+void CEF_CALLBACK
+render_handler_on_virtual_keyboard_requested(struct _cef_render_handler_t* self,
+                                             cef_browser_t* browser,
+                                             cef_text_input_mode_t input_mode) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRenderHandlerCppToC::Get(self)->OnVirtualKeyboardRequested(
+      CefBrowserCToCpp::Wrap(browser), input_mode);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRenderHandlerCppToC::CefRenderHandlerCppToC() {
+  GetStruct()->get_accessibility_handler =
+      render_handler_get_accessibility_handler;
+  GetStruct()->get_root_screen_rect = render_handler_get_root_screen_rect;
+  GetStruct()->get_view_rect = render_handler_get_view_rect;
+  GetStruct()->get_screen_point = render_handler_get_screen_point;
+  GetStruct()->get_screen_info = render_handler_get_screen_info;
+  GetStruct()->on_popup_show = render_handler_on_popup_show;
+  GetStruct()->on_popup_size = render_handler_on_popup_size;
+  GetStruct()->on_paint = render_handler_on_paint;
+  GetStruct()->on_accelerated_paint = render_handler_on_accelerated_paint;
+  GetStruct()->on_cursor_change = render_handler_on_cursor_change;
+  GetStruct()->start_dragging = render_handler_start_dragging;
+  GetStruct()->update_drag_cursor = render_handler_update_drag_cursor;
+  GetStruct()->on_scroll_offset_changed =
+      render_handler_on_scroll_offset_changed;
+  GetStruct()->on_ime_composition_range_changed =
+      render_handler_on_ime_composition_range_changed;
+  GetStruct()->on_text_selection_changed =
+      render_handler_on_text_selection_changed;
+  GetStruct()->on_virtual_keyboard_requested =
+      render_handler_on_virtual_keyboard_requested;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRenderHandlerCppToC::~CefRenderHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefRenderHandler> CefCppToCRefCounted<
+    CefRenderHandlerCppToC,
+    CefRenderHandler,
+    cef_render_handler_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_render_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRenderHandlerCppToC,
+                                   CefRenderHandler,
+                                   cef_render_handler_t>::kWrapperType =
+    WT_RENDER_HANDLER;
diff --git a/src/libcef_dll/cpptoc/render_handler_cpptoc.h b/src/libcef_dll/cpptoc/render_handler_cpptoc.h
new file mode 100644
index 0000000..a4cadcc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/render_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4078c8aa17effd6bbe1deeab9d0f996a9f5f3a60$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RENDER_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RENDER_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_render_handler_capi.h"
+#include "include/cef_render_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRenderHandlerCppToC
+    : public CefCppToCRefCounted<CefRenderHandlerCppToC,
+                                 CefRenderHandler,
+                                 cef_render_handler_t> {
+ public:
+  CefRenderHandlerCppToC();
+  virtual ~CefRenderHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RENDER_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/render_process_handler_cpptoc.cc b/src/libcef_dll/cpptoc/render_process_handler_cpptoc.cc
new file mode 100644
index 0000000..39c64f4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/render_process_handler_cpptoc.cc
@@ -0,0 +1,315 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=41d141e97c1a248bdf7834b583bb417333b55955$
+//
+
+#include "libcef_dll/cpptoc/render_process_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/load_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/domnode_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/list_value_ctocpp.h"
+#include "libcef_dll/ctocpp/process_message_ctocpp.h"
+#include "libcef_dll/ctocpp/v8context_ctocpp.h"
+#include "libcef_dll/ctocpp/v8exception_ctocpp.h"
+#include "libcef_dll/ctocpp/v8stack_trace_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK render_process_handler_on_render_thread_created(
+    struct _cef_render_process_handler_t* self,
+    struct _cef_list_value_t* extra_info) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: extra_info; type: refptr_diff
+  DCHECK(extra_info);
+  if (!extra_info)
+    return;
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnRenderThreadCreated(
+      CefListValueCToCpp::Wrap(extra_info));
+}
+
+void CEF_CALLBACK render_process_handler_on_web_kit_initialized(
+    struct _cef_render_process_handler_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnWebKitInitialized();
+}
+
+void CEF_CALLBACK render_process_handler_on_browser_created(
+    struct _cef_render_process_handler_t* self,
+    cef_browser_t* browser,
+    struct _cef_dictionary_value_t* extra_info) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: extra_info; type: refptr_diff
+  DCHECK(extra_info);
+  if (!extra_info)
+    return;
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnBrowserCreated(
+      CefBrowserCToCpp::Wrap(browser),
+      CefDictionaryValueCToCpp::Wrap(extra_info));
+}
+
+void CEF_CALLBACK render_process_handler_on_browser_destroyed(
+    struct _cef_render_process_handler_t* self,
+    cef_browser_t* browser) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnBrowserDestroyed(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+cef_load_handler_t* CEF_CALLBACK render_process_handler_get_load_handler(
+    struct _cef_render_process_handler_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLoadHandler> _retval =
+      CefRenderProcessHandlerCppToC::Get(self)->GetLoadHandler();
+
+  // Return type: refptr_same
+  return CefLoadHandlerCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK render_process_handler_on_context_created(
+    struct _cef_render_process_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    struct _cef_v8context_t* context) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Verify param: context; type: refptr_diff
+  DCHECK(context);
+  if (!context)
+    return;
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnContextCreated(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefV8ContextCToCpp::Wrap(context));
+}
+
+void CEF_CALLBACK render_process_handler_on_context_released(
+    struct _cef_render_process_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    struct _cef_v8context_t* context) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Verify param: context; type: refptr_diff
+  DCHECK(context);
+  if (!context)
+    return;
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnContextReleased(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefV8ContextCToCpp::Wrap(context));
+}
+
+void CEF_CALLBACK render_process_handler_on_uncaught_exception(
+    struct _cef_render_process_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    struct _cef_v8context_t* context,
+    struct _cef_v8exception_t* exception,
+    struct _cef_v8stack_trace_t* stackTrace) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Verify param: context; type: refptr_diff
+  DCHECK(context);
+  if (!context)
+    return;
+  // Verify param: exception; type: refptr_diff
+  DCHECK(exception);
+  if (!exception)
+    return;
+  // Verify param: stackTrace; type: refptr_diff
+  DCHECK(stackTrace);
+  if (!stackTrace)
+    return;
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnUncaughtException(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefV8ContextCToCpp::Wrap(context), CefV8ExceptionCToCpp::Wrap(exception),
+      CefV8StackTraceCToCpp::Wrap(stackTrace));
+}
+
+void CEF_CALLBACK render_process_handler_on_focused_node_changed(
+    struct _cef_render_process_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_domnode_t* node) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Unverified params: frame, node
+
+  // Execute
+  CefRenderProcessHandlerCppToC::Get(self)->OnFocusedNodeChanged(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefDOMNodeCToCpp::Wrap(node));
+}
+
+int CEF_CALLBACK render_process_handler_on_process_message_received(
+    struct _cef_render_process_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_process_id_t source_process,
+    cef_process_message_t* message) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return 0;
+  // Verify param: message; type: refptr_diff
+  DCHECK(message);
+  if (!message)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefRenderProcessHandlerCppToC::Get(self)->OnProcessMessageReceived(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          source_process, CefProcessMessageCToCpp::Wrap(message));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRenderProcessHandlerCppToC::CefRenderProcessHandlerCppToC() {
+  GetStruct()->on_render_thread_created =
+      render_process_handler_on_render_thread_created;
+  GetStruct()->on_web_kit_initialized =
+      render_process_handler_on_web_kit_initialized;
+  GetStruct()->on_browser_created = render_process_handler_on_browser_created;
+  GetStruct()->on_browser_destroyed =
+      render_process_handler_on_browser_destroyed;
+  GetStruct()->get_load_handler = render_process_handler_get_load_handler;
+  GetStruct()->on_context_created = render_process_handler_on_context_created;
+  GetStruct()->on_context_released = render_process_handler_on_context_released;
+  GetStruct()->on_uncaught_exception =
+      render_process_handler_on_uncaught_exception;
+  GetStruct()->on_focused_node_changed =
+      render_process_handler_on_focused_node_changed;
+  GetStruct()->on_process_message_received =
+      render_process_handler_on_process_message_received;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRenderProcessHandlerCppToC::~CefRenderProcessHandlerCppToC() {}
+
+template <>
+CefRefPtr<CefRenderProcessHandler> CefCppToCRefCounted<
+    CefRenderProcessHandlerCppToC,
+    CefRenderProcessHandler,
+    cef_render_process_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                 cef_render_process_handler_t*
+                                                     s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRenderProcessHandlerCppToC,
+                                   CefRenderProcessHandler,
+                                   cef_render_process_handler_t>::kWrapperType =
+    WT_RENDER_PROCESS_HANDLER;
diff --git a/src/libcef_dll/cpptoc/render_process_handler_cpptoc.h b/src/libcef_dll/cpptoc/render_process_handler_cpptoc.h
new file mode 100644
index 0000000..ebafce5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/render_process_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bb8a36e35b6f5dae03f31cac87b6090796277dbd$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RENDER_PROCESS_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RENDER_PROCESS_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_render_process_handler_capi.h"
+#include "include/cef_render_process_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRenderProcessHandlerCppToC
+    : public CefCppToCRefCounted<CefRenderProcessHandlerCppToC,
+                                 CefRenderProcessHandler,
+                                 cef_render_process_handler_t> {
+ public:
+  CefRenderProcessHandlerCppToC();
+  virtual ~CefRenderProcessHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RENDER_PROCESS_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/request_callback_cpptoc.cc b/src/libcef_dll/cpptoc/request_callback_cpptoc.cc
new file mode 100644
index 0000000..c0bcbc4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_callback_cpptoc.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6564dd6b1da89671525b39541e0a7e45415c8bed$
+//
+
+#include "libcef_dll/cpptoc/request_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK request_callback_cont(struct _cef_request_callback_t* self,
+                                        int allow) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefRequestCallbackCppToC::Get(self)->Continue(allow ? true : false);
+}
+
+void CEF_CALLBACK
+request_callback_cancel(struct _cef_request_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefRequestCallbackCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestCallbackCppToC::CefRequestCallbackCppToC() {
+  GetStruct()->cont = request_callback_cont;
+  GetStruct()->cancel = request_callback_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestCallbackCppToC::~CefRequestCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefRequestCallback> CefCppToCRefCounted<
+    CefRequestCallbackCppToC,
+    CefRequestCallback,
+    cef_request_callback_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_request_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRequestCallbackCppToC,
+                                   CefRequestCallback,
+                                   cef_request_callback_t>::kWrapperType =
+    WT_REQUEST_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/request_callback_cpptoc.h b/src/libcef_dll/cpptoc/request_callback_cpptoc.h
new file mode 100644
index 0000000..a7000e8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a2f22e0169bde2d05e6d85e1e769cb03a03a7c12$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_REQUEST_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_REQUEST_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_callback_capi.h"
+#include "include/cef_request_callback.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefRequestCallbackCppToC
+    : public CefCppToCRefCounted<CefRequestCallbackCppToC,
+                                 CefRequestCallback,
+                                 cef_request_callback_t> {
+ public:
+  CefRequestCallbackCppToC();
+  virtual ~CefRequestCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_REQUEST_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/request_context_cpptoc.cc b/src/libcef_dll/cpptoc/request_context_cpptoc.cc
new file mode 100644
index 0000000..c252e42
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_context_cpptoc.cc
@@ -0,0 +1,614 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5168000f75f7911dce0bffcf47341e4ee1c2a275$
+//
+
+#include "libcef_dll/cpptoc/request_context_cpptoc.h"
+#include "libcef_dll/cpptoc/cookie_manager_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/extension_cpptoc.h"
+#include "libcef_dll/cpptoc/media_router_cpptoc.h"
+#include "libcef_dll/cpptoc/value_cpptoc.h"
+#include "libcef_dll/ctocpp/completion_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/extension_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/request_context_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/resolve_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_request_context_t* cef_request_context_get_global_context() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefRequestContext> _retval = CefRequestContext::GetGlobalContext();
+
+  // Return type: refptr_same
+  return CefRequestContextCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_request_context_t* cef_request_context_create_context(
+    const struct _cef_request_context_settings_t* settings,
+    struct _cef_request_context_handler_t* handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return NULL;
+  // Unverified params: handler
+
+  // Translate param: settings; type: struct_byref_const
+  CefRequestContextSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  CefRefPtr<CefRequestContext> _retval = CefRequestContext::CreateContext(
+      settingsObj, CefRequestContextHandlerCToCpp::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefRequestContextCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_request_context_t* cef_create_context_shared(
+    cef_request_context_t* other,
+    struct _cef_request_context_handler_t* handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: other; type: refptr_same
+  DCHECK(other);
+  if (!other)
+    return NULL;
+  // Unverified params: handler
+
+  // Execute
+  CefRefPtr<CefRequestContext> _retval = CefRequestContext::CreateContext(
+      CefRequestContextCppToC::Unwrap(other),
+      CefRequestContextHandlerCToCpp::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefRequestContextCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK request_context_is_same(struct _cef_request_context_t* self,
+                                         struct _cef_request_context_t* other) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: other; type: refptr_same
+  DCHECK(other);
+  if (!other)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestContextCppToC::Get(self)->IsSame(
+      CefRequestContextCppToC::Unwrap(other));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+request_context_is_sharing_with(struct _cef_request_context_t* self,
+                                struct _cef_request_context_t* other) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: other; type: refptr_same
+  DCHECK(other);
+  if (!other)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestContextCppToC::Get(self)->IsSharingWith(
+      CefRequestContextCppToC::Unwrap(other));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+request_context_is_global(struct _cef_request_context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestContextCppToC::Get(self)->IsGlobal();
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_request_context_handler_t* CEF_CALLBACK
+request_context_get_handler(struct _cef_request_context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRequestContextHandler> _retval =
+      CefRequestContextCppToC::Get(self)->GetHandler();
+
+  // Return type: refptr_diff
+  return CefRequestContextHandlerCToCpp::Unwrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+request_context_get_cache_path(struct _cef_request_context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefRequestContextCppToC::Get(self)->GetCachePath();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_cookie_manager_t* CEF_CALLBACK
+request_context_get_cookie_manager(struct _cef_request_context_t* self,
+                                   cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Unverified params: callback
+
+  // Execute
+  CefRefPtr<CefCookieManager> _retval =
+      CefRequestContextCppToC::Get(self)->GetCookieManager(
+          CefCompletionCallbackCToCpp::Wrap(callback));
+
+  // Return type: refptr_same
+  return CefCookieManagerCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK request_context_register_scheme_handler_factory(
+    struct _cef_request_context_t* self,
+    const cef_string_t* scheme_name,
+    const cef_string_t* domain_name,
+    struct _cef_scheme_handler_factory_t* factory) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(scheme_name);
+  if (!scheme_name)
+    return 0;
+  // Unverified params: domain_name, factory
+
+  // Execute
+  bool _retval =
+      CefRequestContextCppToC::Get(self)->RegisterSchemeHandlerFactory(
+          CefString(scheme_name), CefString(domain_name),
+          CefSchemeHandlerFactoryCToCpp::Wrap(factory));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK request_context_clear_scheme_handler_factories(
+    struct _cef_request_context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefRequestContextCppToC::Get(self)->ClearSchemeHandlerFactories();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+request_context_purge_plugin_list_cache(struct _cef_request_context_t* self,
+                                        int reload_pages) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefRequestContextCppToC::Get(self)->PurgePluginListCache(
+      reload_pages ? true : false);
+}
+
+int CEF_CALLBACK
+request_context_has_preference(struct _cef_request_context_t* self,
+                               const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefRequestContextCppToC::Get(self)->HasPreference(CefString(name));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_value_t* CEF_CALLBACK
+request_context_get_preference(struct _cef_request_context_t* self,
+                               const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefValue> _retval =
+      CefRequestContextCppToC::Get(self)->GetPreference(CefString(name));
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+struct _cef_dictionary_value_t* CEF_CALLBACK
+request_context_get_all_preferences(struct _cef_request_context_t* self,
+                                    int include_defaults) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDictionaryValue> _retval =
+      CefRequestContextCppToC::Get(self)->GetAllPreferences(
+          include_defaults ? true : false);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK
+request_context_can_set_preference(struct _cef_request_context_t* self,
+                                   const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefRequestContextCppToC::Get(self)->CanSetPreference(CefString(name));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+request_context_set_preference(struct _cef_request_context_t* self,
+                               const cef_string_t* name,
+                               struct _cef_value_t* value,
+                               cef_string_t* error) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+  // Verify param: error; type: string_byref
+  DCHECK(error);
+  if (!error)
+    return 0;
+  // Unverified params: value
+
+  // Translate param: error; type: string_byref
+  CefString errorStr(error);
+
+  // Execute
+  bool _retval = CefRequestContextCppToC::Get(self)->SetPreference(
+      CefString(name), CefValueCppToC::Unwrap(value), errorStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK request_context_clear_certificate_exceptions(
+    struct _cef_request_context_t* self,
+    cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: callback
+
+  // Execute
+  CefRequestContextCppToC::Get(self)->ClearCertificateExceptions(
+      CefCompletionCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK request_context_clear_http_auth_credentials(
+    struct _cef_request_context_t* self,
+    cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: callback
+
+  // Execute
+  CefRequestContextCppToC::Get(self)->ClearHttpAuthCredentials(
+      CefCompletionCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK
+request_context_close_all_connections(struct _cef_request_context_t* self,
+                                      cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: callback
+
+  // Execute
+  CefRequestContextCppToC::Get(self)->CloseAllConnections(
+      CefCompletionCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK
+request_context_resolve_host(struct _cef_request_context_t* self,
+                             const cef_string_t* origin,
+                             cef_resolve_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: origin; type: string_byref_const
+  DCHECK(origin);
+  if (!origin)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefRequestContextCppToC::Get(self)->ResolveHost(
+      CefString(origin), CefResolveCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK
+request_context_load_extension(struct _cef_request_context_t* self,
+                               const cef_string_t* root_directory,
+                               struct _cef_dictionary_value_t* manifest,
+                               cef_extension_handler_t* handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: root_directory; type: string_byref_const
+  DCHECK(root_directory);
+  if (!root_directory)
+    return;
+  // Unverified params: manifest, handler
+
+  // Execute
+  CefRequestContextCppToC::Get(self)->LoadExtension(
+      CefString(root_directory), CefDictionaryValueCppToC::Unwrap(manifest),
+      CefExtensionHandlerCToCpp::Wrap(handler));
+}
+
+int CEF_CALLBACK
+request_context_did_load_extension(struct _cef_request_context_t* self,
+                                   const cef_string_t* extension_id) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: extension_id; type: string_byref_const
+  DCHECK(extension_id);
+  if (!extension_id)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestContextCppToC::Get(self)->DidLoadExtension(
+      CefString(extension_id));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+request_context_has_extension(struct _cef_request_context_t* self,
+                              const cef_string_t* extension_id) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: extension_id; type: string_byref_const
+  DCHECK(extension_id);
+  if (!extension_id)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefRequestContextCppToC::Get(self)->HasExtension(CefString(extension_id));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+request_context_get_extensions(struct _cef_request_context_t* self,
+                               cef_string_list_t extension_ids) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: extension_ids; type: string_vec_byref
+  DCHECK(extension_ids);
+  if (!extension_ids)
+    return 0;
+
+  // Translate param: extension_ids; type: string_vec_byref
+  std::vector<CefString> extension_idsList;
+  transfer_string_list_contents(extension_ids, extension_idsList);
+
+  // Execute
+  bool _retval =
+      CefRequestContextCppToC::Get(self)->GetExtensions(extension_idsList);
+
+  // Restore param: extension_ids; type: string_vec_byref
+  cef_string_list_clear(extension_ids);
+  transfer_string_list_contents(extension_idsList, extension_ids);
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_extension_t* CEF_CALLBACK
+request_context_get_extension(struct _cef_request_context_t* self,
+                              const cef_string_t* extension_id) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: extension_id; type: string_byref_const
+  DCHECK(extension_id);
+  if (!extension_id)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefExtension> _retval =
+      CefRequestContextCppToC::Get(self)->GetExtension(CefString(extension_id));
+
+  // Return type: refptr_same
+  return CefExtensionCppToC::Wrap(_retval);
+}
+
+cef_media_router_t* CEF_CALLBACK
+request_context_get_media_router(struct _cef_request_context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMediaRouter> _retval =
+      CefRequestContextCppToC::Get(self)->GetMediaRouter();
+
+  // Return type: refptr_same
+  return CefMediaRouterCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestContextCppToC::CefRequestContextCppToC() {
+  GetStruct()->is_same = request_context_is_same;
+  GetStruct()->is_sharing_with = request_context_is_sharing_with;
+  GetStruct()->is_global = request_context_is_global;
+  GetStruct()->get_handler = request_context_get_handler;
+  GetStruct()->get_cache_path = request_context_get_cache_path;
+  GetStruct()->get_cookie_manager = request_context_get_cookie_manager;
+  GetStruct()->register_scheme_handler_factory =
+      request_context_register_scheme_handler_factory;
+  GetStruct()->clear_scheme_handler_factories =
+      request_context_clear_scheme_handler_factories;
+  GetStruct()->purge_plugin_list_cache =
+      request_context_purge_plugin_list_cache;
+  GetStruct()->has_preference = request_context_has_preference;
+  GetStruct()->get_preference = request_context_get_preference;
+  GetStruct()->get_all_preferences = request_context_get_all_preferences;
+  GetStruct()->can_set_preference = request_context_can_set_preference;
+  GetStruct()->set_preference = request_context_set_preference;
+  GetStruct()->clear_certificate_exceptions =
+      request_context_clear_certificate_exceptions;
+  GetStruct()->clear_http_auth_credentials =
+      request_context_clear_http_auth_credentials;
+  GetStruct()->close_all_connections = request_context_close_all_connections;
+  GetStruct()->resolve_host = request_context_resolve_host;
+  GetStruct()->load_extension = request_context_load_extension;
+  GetStruct()->did_load_extension = request_context_did_load_extension;
+  GetStruct()->has_extension = request_context_has_extension;
+  GetStruct()->get_extensions = request_context_get_extensions;
+  GetStruct()->get_extension = request_context_get_extension;
+  GetStruct()->get_media_router = request_context_get_media_router;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestContextCppToC::~CefRequestContextCppToC() {}
+
+template <>
+CefRefPtr<CefRequestContext> CefCppToCRefCounted<
+    CefRequestContextCppToC,
+    CefRequestContext,
+    cef_request_context_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_request_context_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRequestContextCppToC,
+                                   CefRequestContext,
+                                   cef_request_context_t>::kWrapperType =
+    WT_REQUEST_CONTEXT;
diff --git a/src/libcef_dll/cpptoc/request_context_cpptoc.h b/src/libcef_dll/cpptoc/request_context_cpptoc.h
new file mode 100644
index 0000000..451b6f8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_context_cpptoc.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ea7cba74131f394dddd0c33df1d6788436fd8581$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_REQUEST_CONTEXT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_REQUEST_CONTEXT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_context_capi.h"
+#include "include/capi/cef_request_context_handler_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_request_context.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefRequestContextCppToC
+    : public CefCppToCRefCounted<CefRequestContextCppToC,
+                                 CefRequestContext,
+                                 cef_request_context_t> {
+ public:
+  CefRequestContextCppToC();
+  virtual ~CefRequestContextCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_REQUEST_CONTEXT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/request_context_handler_cpptoc.cc b/src/libcef_dll/cpptoc/request_context_handler_cpptoc.cc
new file mode 100644
index 0000000..3812397
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_context_handler_cpptoc.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f7c9de5f1d996c73233fe89a3a53bb9a8f92da11$
+//
+
+#include "libcef_dll/cpptoc/request_context_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/resource_request_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/request_context_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/web_plugin_info_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK request_context_handler_on_request_context_initialized(
+    struct _cef_request_context_handler_t* self,
+    cef_request_context_t* request_context) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request_context; type: refptr_diff
+  DCHECK(request_context);
+  if (!request_context)
+    return;
+
+  // Execute
+  CefRequestContextHandlerCppToC::Get(self)->OnRequestContextInitialized(
+      CefRequestContextCToCpp::Wrap(request_context));
+}
+
+int CEF_CALLBACK request_context_handler_on_before_plugin_load(
+    struct _cef_request_context_handler_t* self,
+    const cef_string_t* mime_type,
+    const cef_string_t* plugin_url,
+    int is_main_frame,
+    const cef_string_t* top_origin_url,
+    struct _cef_web_plugin_info_t* plugin_info,
+    cef_plugin_policy_t* plugin_policy) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: mime_type; type: string_byref_const
+  DCHECK(mime_type);
+  if (!mime_type)
+    return 0;
+  // Verify param: plugin_info; type: refptr_diff
+  DCHECK(plugin_info);
+  if (!plugin_info)
+    return 0;
+  // Verify param: plugin_policy; type: simple_byaddr
+  DCHECK(plugin_policy);
+  if (!plugin_policy)
+    return 0;
+  // Unverified params: plugin_url, top_origin_url
+
+  // Execute
+  bool _retval = CefRequestContextHandlerCppToC::Get(self)->OnBeforePluginLoad(
+      CefString(mime_type), CefString(plugin_url), is_main_frame ? true : false,
+      CefString(top_origin_url), CefWebPluginInfoCToCpp::Wrap(plugin_info),
+      plugin_policy);
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_resource_request_handler_t* CEF_CALLBACK
+request_context_handler_get_resource_request_handler(
+    struct _cef_request_context_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    int is_navigation,
+    int is_download,
+    const cef_string_t* request_initiator,
+    int* disable_default_handling) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Verify param: disable_default_handling; type: bool_byref
+  DCHECK(disable_default_handling);
+  if (!disable_default_handling)
+    return NULL;
+  // Unverified params: browser, frame, request_initiator
+
+  // Translate param: disable_default_handling; type: bool_byref
+  bool disable_default_handlingBool =
+      (disable_default_handling && *disable_default_handling) ? true : false;
+
+  // Execute
+  CefRefPtr<CefResourceRequestHandler> _retval =
+      CefRequestContextHandlerCppToC::Get(self)->GetResourceRequestHandler(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          CefRequestCToCpp::Wrap(request), is_navigation ? true : false,
+          is_download ? true : false, CefString(request_initiator),
+          disable_default_handlingBool);
+
+  // Restore param: disable_default_handling; type: bool_byref
+  if (disable_default_handling)
+    *disable_default_handling = disable_default_handlingBool ? true : false;
+
+  // Return type: refptr_same
+  return CefResourceRequestHandlerCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestContextHandlerCppToC::CefRequestContextHandlerCppToC() {
+  GetStruct()->on_request_context_initialized =
+      request_context_handler_on_request_context_initialized;
+  GetStruct()->on_before_plugin_load =
+      request_context_handler_on_before_plugin_load;
+  GetStruct()->get_resource_request_handler =
+      request_context_handler_get_resource_request_handler;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestContextHandlerCppToC::~CefRequestContextHandlerCppToC() {}
+
+template <>
+CefRefPtr<CefRequestContextHandler> CefCppToCRefCounted<
+    CefRequestContextHandlerCppToC,
+    CefRequestContextHandler,
+    cef_request_context_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                  cef_request_context_handler_t*
+                                                      s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefRequestContextHandlerCppToC,
+                        CefRequestContextHandler,
+                        cef_request_context_handler_t>::kWrapperType =
+        WT_REQUEST_CONTEXT_HANDLER;
diff --git a/src/libcef_dll/cpptoc/request_context_handler_cpptoc.h b/src/libcef_dll/cpptoc/request_context_handler_cpptoc.h
new file mode 100644
index 0000000..1f386dc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_context_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=16255caf8417becd716db31c87e58c2bbffce4c8$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_REQUEST_CONTEXT_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_REQUEST_CONTEXT_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_context_handler_capi.h"
+#include "include/cef_request_context_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRequestContextHandlerCppToC
+    : public CefCppToCRefCounted<CefRequestContextHandlerCppToC,
+                                 CefRequestContextHandler,
+                                 cef_request_context_handler_t> {
+ public:
+  CefRequestContextHandlerCppToC();
+  virtual ~CefRequestContextHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_REQUEST_CONTEXT_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/request_cpptoc.cc b/src/libcef_dll/cpptoc/request_cpptoc.cc
new file mode 100644
index 0000000..db08249
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_cpptoc.cc
@@ -0,0 +1,452 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=50c2166b6a1c9699b480e7bcc1f0da7e4d5b938e$
+//
+
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/post_data_cpptoc.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_request_t* cef_request_create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefRequest> _retval = CefRequest::Create();
+
+  // Return type: refptr_same
+  return CefRequestCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK request_is_read_only(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+request_get_url(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefRequestCppToC::Get(self)->GetURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK request_set_url(struct _cef_request_t* self,
+                                  const cef_string_t* url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return;
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetURL(CefString(url));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+request_get_method(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefRequestCppToC::Get(self)->GetMethod();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK request_set_method(struct _cef_request_t* self,
+                                     const cef_string_t* method) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: method; type: string_byref_const
+  DCHECK(method);
+  if (!method)
+    return;
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetMethod(CefString(method));
+}
+
+void CEF_CALLBACK request_set_referrer(struct _cef_request_t* self,
+                                       const cef_string_t* referrer_url,
+                                       cef_referrer_policy_t policy) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: referrer_url
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetReferrer(CefString(referrer_url), policy);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+request_get_referrer_url(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefRequestCppToC::Get(self)->GetReferrerURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_referrer_policy_t CEF_CALLBACK
+request_get_referrer_policy(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return REFERRER_POLICY_DEFAULT;
+
+  // Execute
+  cef_referrer_policy_t _retval =
+      CefRequestCppToC::Get(self)->GetReferrerPolicy();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_post_data_t* CEF_CALLBACK
+request_get_post_data(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPostData> _retval = CefRequestCppToC::Get(self)->GetPostData();
+
+  // Return type: refptr_same
+  return CefPostDataCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK request_set_post_data(struct _cef_request_t* self,
+                                        struct _cef_post_data_t* postData) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: postData; type: refptr_same
+  DCHECK(postData);
+  if (!postData)
+    return;
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetPostData(CefPostDataCppToC::Unwrap(postData));
+}
+
+void CEF_CALLBACK request_get_header_map(struct _cef_request_t* self,
+                                         cef_string_multimap_t headerMap) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: headerMap; type: string_map_multi_byref
+  DCHECK(headerMap);
+  if (!headerMap)
+    return;
+
+  // Translate param: headerMap; type: string_map_multi_byref
+  std::multimap<CefString, CefString> headerMapMultimap;
+  transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  CefRequestCppToC::Get(self)->GetHeaderMap(headerMapMultimap);
+
+  // Restore param: headerMap; type: string_map_multi_byref
+  cef_string_multimap_clear(headerMap);
+  transfer_string_multimap_contents(headerMapMultimap, headerMap);
+}
+
+void CEF_CALLBACK request_set_header_map(struct _cef_request_t* self,
+                                         cef_string_multimap_t headerMap) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: headerMap; type: string_map_multi_byref_const
+  DCHECK(headerMap);
+  if (!headerMap)
+    return;
+
+  // Translate param: headerMap; type: string_map_multi_byref_const
+  std::multimap<CefString, CefString> headerMapMultimap;
+  transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetHeaderMap(headerMapMultimap);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+request_get_header_by_name(struct _cef_request_t* self,
+                           const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefRequestCppToC::Get(self)->GetHeaderByName(CefString(name));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK request_set_header_by_name(struct _cef_request_t* self,
+                                             const cef_string_t* name,
+                                             const cef_string_t* value,
+                                             int overwrite) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+  // Unverified params: value
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetHeaderByName(
+      CefString(name), CefString(value), overwrite ? true : false);
+}
+
+void CEF_CALLBACK request_set(struct _cef_request_t* self,
+                              const cef_string_t* url,
+                              const cef_string_t* method,
+                              struct _cef_post_data_t* postData,
+                              cef_string_multimap_t headerMap) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return;
+  // Verify param: method; type: string_byref_const
+  DCHECK(method);
+  if (!method)
+    return;
+  // Verify param: headerMap; type: string_map_multi_byref_const
+  DCHECK(headerMap);
+  if (!headerMap)
+    return;
+  // Unverified params: postData
+
+  // Translate param: headerMap; type: string_map_multi_byref_const
+  std::multimap<CefString, CefString> headerMapMultimap;
+  transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  CefRequestCppToC::Get(self)->Set(CefString(url), CefString(method),
+                                   CefPostDataCppToC::Unwrap(postData),
+                                   headerMapMultimap);
+}
+
+int CEF_CALLBACK request_get_flags(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return UR_FLAG_NONE;
+
+  // Execute
+  int _retval = CefRequestCppToC::Get(self)->GetFlags();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK request_set_flags(struct _cef_request_t* self, int flags) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetFlags(flags);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+request_get_first_party_for_cookies(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefRequestCppToC::Get(self)->GetFirstPartyForCookies();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK
+request_set_first_party_for_cookies(struct _cef_request_t* self,
+                                    const cef_string_t* url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: url
+
+  // Execute
+  CefRequestCppToC::Get(self)->SetFirstPartyForCookies(CefString(url));
+}
+
+cef_resource_type_t CEF_CALLBACK
+request_get_resource_type(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return RT_SUB_RESOURCE;
+
+  // Execute
+  cef_resource_type_t _retval = CefRequestCppToC::Get(self)->GetResourceType();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_transition_type_t CEF_CALLBACK
+request_get_transition_type(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return TT_EXPLICIT;
+
+  // Execute
+  cef_transition_type_t _retval =
+      CefRequestCppToC::Get(self)->GetTransitionType();
+
+  // Return type: simple
+  return _retval;
+}
+
+uint64 CEF_CALLBACK request_get_identifier(struct _cef_request_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  uint64 _retval = CefRequestCppToC::Get(self)->GetIdentifier();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestCppToC::CefRequestCppToC() {
+  GetStruct()->is_read_only = request_is_read_only;
+  GetStruct()->get_url = request_get_url;
+  GetStruct()->set_url = request_set_url;
+  GetStruct()->get_method = request_get_method;
+  GetStruct()->set_method = request_set_method;
+  GetStruct()->set_referrer = request_set_referrer;
+  GetStruct()->get_referrer_url = request_get_referrer_url;
+  GetStruct()->get_referrer_policy = request_get_referrer_policy;
+  GetStruct()->get_post_data = request_get_post_data;
+  GetStruct()->set_post_data = request_set_post_data;
+  GetStruct()->get_header_map = request_get_header_map;
+  GetStruct()->set_header_map = request_set_header_map;
+  GetStruct()->get_header_by_name = request_get_header_by_name;
+  GetStruct()->set_header_by_name = request_set_header_by_name;
+  GetStruct()->set = request_set;
+  GetStruct()->get_flags = request_get_flags;
+  GetStruct()->set_flags = request_set_flags;
+  GetStruct()->get_first_party_for_cookies =
+      request_get_first_party_for_cookies;
+  GetStruct()->set_first_party_for_cookies =
+      request_set_first_party_for_cookies;
+  GetStruct()->get_resource_type = request_get_resource_type;
+  GetStruct()->get_transition_type = request_get_transition_type;
+  GetStruct()->get_identifier = request_get_identifier;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestCppToC::~CefRequestCppToC() {}
+
+template <>
+CefRefPtr<CefRequest>
+CefCppToCRefCounted<CefRequestCppToC, CefRequest, cef_request_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_request_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRequestCppToC,
+                                   CefRequest,
+                                   cef_request_t>::kWrapperType = WT_REQUEST;
diff --git a/src/libcef_dll/cpptoc/request_cpptoc.h b/src/libcef_dll/cpptoc/request_cpptoc.h
new file mode 100644
index 0000000..478f22e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ee87fe2283f86a6801ca7650b8883ba2c2d4598f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_REQUEST_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_REQUEST_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_capi.h"
+#include "include/cef_request.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefRequestCppToC
+    : public CefCppToCRefCounted<CefRequestCppToC, CefRequest, cef_request_t> {
+ public:
+  CefRequestCppToC();
+  virtual ~CefRequestCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_REQUEST_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/request_handler_cpptoc.cc b/src/libcef_dll/cpptoc/request_handler_cpptoc.cc
new file mode 100644
index 0000000..bef261d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_handler_cpptoc.cc
@@ -0,0 +1,461 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=716501855da0203723eb18bfb9e518c7b155f110$
+//
+
+#include "libcef_dll/cpptoc/request_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/resource_request_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/auth_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/request_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/sslinfo_ctocpp.h"
+#include "libcef_dll/ctocpp/x509certificate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+request_handler_on_before_browse(struct _cef_request_handler_t* self,
+                                 cef_browser_t* browser,
+                                 cef_frame_t* frame,
+                                 cef_request_t* request,
+                                 int user_gesture,
+                                 int is_redirect) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return 0;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestHandlerCppToC::Get(self)->OnBeforeBrowse(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefRequestCToCpp::Wrap(request), user_gesture ? true : false,
+      is_redirect ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK request_handler_on_open_urlfrom_tab(
+    struct _cef_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    const cef_string_t* target_url,
+    cef_window_open_disposition_t target_disposition,
+    int user_gesture) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return 0;
+  // Verify param: target_url; type: string_byref_const
+  DCHECK(target_url);
+  if (!target_url)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestHandlerCppToC::Get(self)->OnOpenURLFromTab(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefString(target_url), target_disposition, user_gesture ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_resource_request_handler_t* CEF_CALLBACK
+request_handler_get_resource_request_handler(
+    struct _cef_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    int is_navigation,
+    int is_download,
+    const cef_string_t* request_initiator,
+    int* disable_default_handling) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return NULL;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame);
+  if (!frame)
+    return NULL;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Verify param: disable_default_handling; type: bool_byref
+  DCHECK(disable_default_handling);
+  if (!disable_default_handling)
+    return NULL;
+  // Unverified params: request_initiator
+
+  // Translate param: disable_default_handling; type: bool_byref
+  bool disable_default_handlingBool =
+      (disable_default_handling && *disable_default_handling) ? true : false;
+
+  // Execute
+  CefRefPtr<CefResourceRequestHandler> _retval =
+      CefRequestHandlerCppToC::Get(self)->GetResourceRequestHandler(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          CefRequestCToCpp::Wrap(request), is_navigation ? true : false,
+          is_download ? true : false, CefString(request_initiator),
+          disable_default_handlingBool);
+
+  // Restore param: disable_default_handling; type: bool_byref
+  if (disable_default_handling)
+    *disable_default_handling = disable_default_handlingBool ? true : false;
+
+  // Return type: refptr_same
+  return CefResourceRequestHandlerCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK
+request_handler_get_auth_credentials(struct _cef_request_handler_t* self,
+                                     cef_browser_t* browser,
+                                     const cef_string_t* origin_url,
+                                     int isProxy,
+                                     const cef_string_t* host,
+                                     int port,
+                                     const cef_string_t* realm,
+                                     const cef_string_t* scheme,
+                                     cef_auth_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: origin_url; type: string_byref_const
+  DCHECK(origin_url);
+  if (!origin_url)
+    return 0;
+  // Verify param: host; type: string_byref_const
+  DCHECK(host);
+  if (!host)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+  // Unverified params: realm, scheme
+
+  // Execute
+  bool _retval = CefRequestHandlerCppToC::Get(self)->GetAuthCredentials(
+      CefBrowserCToCpp::Wrap(browser), CefString(origin_url),
+      isProxy ? true : false, CefString(host), port, CefString(realm),
+      CefString(scheme), CefAuthCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+request_handler_on_quota_request(struct _cef_request_handler_t* self,
+                                 cef_browser_t* browser,
+                                 const cef_string_t* origin_url,
+                                 int64 new_size,
+                                 cef_request_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: origin_url; type: string_byref_const
+  DCHECK(origin_url);
+  if (!origin_url)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestHandlerCppToC::Get(self)->OnQuotaRequest(
+      CefBrowserCToCpp::Wrap(browser), CefString(origin_url), new_size,
+      CefRequestCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+request_handler_on_certificate_error(struct _cef_request_handler_t* self,
+                                     cef_browser_t* browser,
+                                     cef_errorcode_t cert_error,
+                                     const cef_string_t* request_url,
+                                     struct _cef_sslinfo_t* ssl_info,
+                                     cef_request_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: request_url; type: string_byref_const
+  DCHECK(request_url);
+  if (!request_url)
+    return 0;
+  // Verify param: ssl_info; type: refptr_diff
+  DCHECK(ssl_info);
+  if (!ssl_info)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Execute
+  bool _retval = CefRequestHandlerCppToC::Get(self)->OnCertificateError(
+      CefBrowserCToCpp::Wrap(browser), cert_error, CefString(request_url),
+      CefSSLInfoCToCpp::Wrap(ssl_info),
+      CefRequestCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK request_handler_on_select_client_certificate(
+    struct _cef_request_handler_t* self,
+    cef_browser_t* browser,
+    int isProxy,
+    const cef_string_t* host,
+    int port,
+    size_t certificatesCount,
+    struct _cef_x509certificate_t* const* certificates,
+    cef_select_client_certificate_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return 0;
+  // Verify param: host; type: string_byref_const
+  DCHECK(host);
+  if (!host)
+    return 0;
+  // Verify param: certificates; type: refptr_vec_diff_byref_const
+  DCHECK(certificatesCount == 0 || certificates);
+  if (certificatesCount > 0 && !certificates)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Translate param: certificates; type: refptr_vec_diff_byref_const
+  std::vector<CefRefPtr<CefX509Certificate>> certificatesList;
+  if (certificatesCount > 0) {
+    for (size_t i = 0; i < certificatesCount; ++i) {
+      CefRefPtr<CefX509Certificate> certificatesVal =
+          CefX509CertificateCToCpp::Wrap(certificates[i]);
+      certificatesList.push_back(certificatesVal);
+    }
+  }
+
+  // Execute
+  bool _retval = CefRequestHandlerCppToC::Get(self)->OnSelectClientCertificate(
+      CefBrowserCToCpp::Wrap(browser), isProxy ? true : false, CefString(host),
+      port, certificatesList,
+      CefSelectClientCertificateCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+request_handler_on_plugin_crashed(struct _cef_request_handler_t* self,
+                                  cef_browser_t* browser,
+                                  const cef_string_t* plugin_path) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+  // Verify param: plugin_path; type: string_byref_const
+  DCHECK(plugin_path);
+  if (!plugin_path)
+    return;
+
+  // Execute
+  CefRequestHandlerCppToC::Get(self)->OnPluginCrashed(
+      CefBrowserCToCpp::Wrap(browser), CefString(plugin_path));
+}
+
+void CEF_CALLBACK
+request_handler_on_render_view_ready(struct _cef_request_handler_t* self,
+                                     cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRequestHandlerCppToC::Get(self)->OnRenderViewReady(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+void CEF_CALLBACK request_handler_on_render_process_terminated(
+    struct _cef_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_termination_status_t status) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRequestHandlerCppToC::Get(self)->OnRenderProcessTerminated(
+      CefBrowserCToCpp::Wrap(browser), status);
+}
+
+void CEF_CALLBACK request_handler_on_document_available_in_main_frame(
+    struct _cef_request_handler_t* self,
+    cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefRequestHandlerCppToC::Get(self)->OnDocumentAvailableInMainFrame(
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestHandlerCppToC::CefRequestHandlerCppToC() {
+  GetStruct()->on_before_browse = request_handler_on_before_browse;
+  GetStruct()->on_open_urlfrom_tab = request_handler_on_open_urlfrom_tab;
+  GetStruct()->get_resource_request_handler =
+      request_handler_get_resource_request_handler;
+  GetStruct()->get_auth_credentials = request_handler_get_auth_credentials;
+  GetStruct()->on_quota_request = request_handler_on_quota_request;
+  GetStruct()->on_certificate_error = request_handler_on_certificate_error;
+  GetStruct()->on_select_client_certificate =
+      request_handler_on_select_client_certificate;
+  GetStruct()->on_plugin_crashed = request_handler_on_plugin_crashed;
+  GetStruct()->on_render_view_ready = request_handler_on_render_view_ready;
+  GetStruct()->on_render_process_terminated =
+      request_handler_on_render_process_terminated;
+  GetStruct()->on_document_available_in_main_frame =
+      request_handler_on_document_available_in_main_frame;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestHandlerCppToC::~CefRequestHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefRequestHandler> CefCppToCRefCounted<
+    CefRequestHandlerCppToC,
+    CefRequestHandler,
+    cef_request_handler_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_request_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefRequestHandlerCppToC,
+                                   CefRequestHandler,
+                                   cef_request_handler_t>::kWrapperType =
+    WT_REQUEST_HANDLER;
diff --git a/src/libcef_dll/cpptoc/request_handler_cpptoc.h b/src/libcef_dll/cpptoc/request_handler_cpptoc.h
new file mode 100644
index 0000000..4d7c1df
--- /dev/null
+++ b/src/libcef_dll/cpptoc/request_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bccd3bcc8f2fbd0aea2f41b08c54a3c99e3d218f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_REQUEST_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_REQUEST_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_handler_capi.h"
+#include "include/cef_request_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRequestHandlerCppToC
+    : public CefCppToCRefCounted<CefRequestHandlerCppToC,
+                                 CefRequestHandler,
+                                 cef_request_handler_t> {
+ public:
+  CefRequestHandlerCppToC();
+  virtual ~CefRequestHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_REQUEST_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/resolve_callback_cpptoc.cc b/src/libcef_dll/cpptoc/resolve_callback_cpptoc.cc
new file mode 100644
index 0000000..5cf8dd1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resolve_callback_cpptoc.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f7af7bfb68cb621f8aa6686bc0895854d33f9b06$
+//
+
+#include "libcef_dll/cpptoc/resolve_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+resolve_callback_on_resolve_completed(struct _cef_resolve_callback_t* self,
+                                      cef_errorcode_t result,
+                                      cef_string_list_t resolved_ips) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: resolved_ips
+
+  // Translate param: resolved_ips; type: string_vec_byref_const
+  std::vector<CefString> resolved_ipsList;
+  transfer_string_list_contents(resolved_ips, resolved_ipsList);
+
+  // Execute
+  CefResolveCallbackCppToC::Get(self)->OnResolveCompleted(result,
+                                                          resolved_ipsList);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResolveCallbackCppToC::CefResolveCallbackCppToC() {
+  GetStruct()->on_resolve_completed = resolve_callback_on_resolve_completed;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResolveCallbackCppToC::~CefResolveCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefResolveCallback> CefCppToCRefCounted<
+    CefResolveCallbackCppToC,
+    CefResolveCallback,
+    cef_resolve_callback_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_resolve_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefResolveCallbackCppToC,
+                                   CefResolveCallback,
+                                   cef_resolve_callback_t>::kWrapperType =
+    WT_RESOLVE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/resolve_callback_cpptoc.h b/src/libcef_dll/cpptoc/resolve_callback_cpptoc.h
new file mode 100644
index 0000000..f576011
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resolve_callback_cpptoc.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c1c4d90577819019fc7e3667b34101e1a36b8db7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESOLVE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESOLVE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_context_capi.h"
+#include "include/capi/cef_request_context_handler_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_request_context.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResolveCallbackCppToC
+    : public CefCppToCRefCounted<CefResolveCallbackCppToC,
+                                 CefResolveCallback,
+                                 cef_resolve_callback_t> {
+ public:
+  CefResolveCallbackCppToC();
+  virtual ~CefResolveCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESOLVE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/resource_bundle_cpptoc.cc b/src/libcef_dll/cpptoc/resource_bundle_cpptoc.cc
new file mode 100644
index 0000000..3a9235c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_bundle_cpptoc.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ba0a12367019906d32dae965d7d1b5245d02b442$
+//
+
+#include "libcef_dll/cpptoc/resource_bundle_cpptoc.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_resource_bundle_t* cef_resource_bundle_get_global() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefResourceBundle> _retval = CefResourceBundle::GetGlobal();
+
+  // Return type: refptr_same
+  return CefResourceBundleCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+resource_bundle_get_localized_string(struct _cef_resource_bundle_t* self,
+                                     int string_id) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefResourceBundleCppToC::Get(self)->GetLocalizedString(string_id);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK
+resource_bundle_get_data_resource(struct _cef_resource_bundle_t* self,
+                                  int resource_id,
+                                  void** data,
+                                  size_t* data_size) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: data; type: simple_byref
+  DCHECK(data);
+  if (!data)
+    return 0;
+  // Verify param: data_size; type: simple_byref
+  DCHECK(data_size);
+  if (!data_size)
+    return 0;
+
+  // Translate param: data; type: simple_byref
+  void* dataVal = data ? *data : NULL;
+  // Translate param: data_size; type: simple_byref
+  size_t data_sizeVal = data_size ? *data_size : 0;
+
+  // Execute
+  bool _retval = CefResourceBundleCppToC::Get(self)->GetDataResource(
+      resource_id, dataVal, data_sizeVal);
+
+  // Restore param: data; type: simple_byref
+  if (data)
+    *data = dataVal;
+  // Restore param: data_size; type: simple_byref
+  if (data_size)
+    *data_size = data_sizeVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+resource_bundle_get_data_resource_for_scale(struct _cef_resource_bundle_t* self,
+                                            int resource_id,
+                                            cef_scale_factor_t scale_factor,
+                                            void** data,
+                                            size_t* data_size) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: data; type: simple_byref
+  DCHECK(data);
+  if (!data)
+    return 0;
+  // Verify param: data_size; type: simple_byref
+  DCHECK(data_size);
+  if (!data_size)
+    return 0;
+
+  // Translate param: data; type: simple_byref
+  void* dataVal = data ? *data : NULL;
+  // Translate param: data_size; type: simple_byref
+  size_t data_sizeVal = data_size ? *data_size : 0;
+
+  // Execute
+  bool _retval = CefResourceBundleCppToC::Get(self)->GetDataResourceForScale(
+      resource_id, scale_factor, dataVal, data_sizeVal);
+
+  // Restore param: data; type: simple_byref
+  if (data)
+    *data = dataVal;
+  // Restore param: data_size; type: simple_byref
+  if (data_size)
+    *data_size = data_sizeVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceBundleCppToC::CefResourceBundleCppToC() {
+  GetStruct()->get_localized_string = resource_bundle_get_localized_string;
+  GetStruct()->get_data_resource = resource_bundle_get_data_resource;
+  GetStruct()->get_data_resource_for_scale =
+      resource_bundle_get_data_resource_for_scale;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceBundleCppToC::~CefResourceBundleCppToC() {}
+
+template <>
+CefRefPtr<CefResourceBundle> CefCppToCRefCounted<
+    CefResourceBundleCppToC,
+    CefResourceBundle,
+    cef_resource_bundle_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_resource_bundle_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefResourceBundleCppToC,
+                                   CefResourceBundle,
+                                   cef_resource_bundle_t>::kWrapperType =
+    WT_RESOURCE_BUNDLE;
diff --git a/src/libcef_dll/cpptoc/resource_bundle_cpptoc.h b/src/libcef_dll/cpptoc/resource_bundle_cpptoc.h
new file mode 100644
index 0000000..2339c97
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_bundle_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8164a4e23b7fc82cdcf231d0393b197077926d68$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESOURCE_BUNDLE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESOURCE_BUNDLE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_resource_bundle_capi.h"
+#include "include/cef_resource_bundle.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefResourceBundleCppToC
+    : public CefCppToCRefCounted<CefResourceBundleCppToC,
+                                 CefResourceBundle,
+                                 cef_resource_bundle_t> {
+ public:
+  CefResourceBundleCppToC();
+  virtual ~CefResourceBundleCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESOURCE_BUNDLE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/resource_bundle_handler_cpptoc.cc b/src/libcef_dll/cpptoc/resource_bundle_handler_cpptoc.cc
new file mode 100644
index 0000000..1211fa4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_bundle_handler_cpptoc.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b041280729bcbe060d591caccd43deeb208d02b2$
+//
+
+#include "libcef_dll/cpptoc/resource_bundle_handler_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK resource_bundle_handler_get_localized_string(
+    struct _cef_resource_bundle_handler_t* self,
+    int string_id,
+    cef_string_t* string) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: string; type: string_byref
+  DCHECK(string);
+  if (!string)
+    return 0;
+
+  // Translate param: string; type: string_byref
+  CefString stringStr(string);
+
+  // Execute
+  bool _retval = CefResourceBundleHandlerCppToC::Get(self)->GetLocalizedString(
+      string_id, stringStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK resource_bundle_handler_get_data_resource(
+    struct _cef_resource_bundle_handler_t* self,
+    int resource_id,
+    void** data,
+    size_t* data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: data; type: simple_byref
+  DCHECK(data);
+  if (!data)
+    return 0;
+  // Verify param: data_size; type: simple_byref
+  DCHECK(data_size);
+  if (!data_size)
+    return 0;
+
+  // Translate param: data; type: simple_byref
+  void* dataVal = data ? *data : NULL;
+  // Translate param: data_size; type: simple_byref
+  size_t data_sizeVal = data_size ? *data_size : 0;
+
+  // Execute
+  bool _retval = CefResourceBundleHandlerCppToC::Get(self)->GetDataResource(
+      resource_id, dataVal, data_sizeVal);
+
+  // Restore param: data; type: simple_byref
+  if (data)
+    *data = dataVal;
+  // Restore param: data_size; type: simple_byref
+  if (data_size)
+    *data_size = data_sizeVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK resource_bundle_handler_get_data_resource_for_scale(
+    struct _cef_resource_bundle_handler_t* self,
+    int resource_id,
+    cef_scale_factor_t scale_factor,
+    void** data,
+    size_t* data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: data; type: simple_byref
+  DCHECK(data);
+  if (!data)
+    return 0;
+  // Verify param: data_size; type: simple_byref
+  DCHECK(data_size);
+  if (!data_size)
+    return 0;
+
+  // Translate param: data; type: simple_byref
+  void* dataVal = data ? *data : NULL;
+  // Translate param: data_size; type: simple_byref
+  size_t data_sizeVal = data_size ? *data_size : 0;
+
+  // Execute
+  bool _retval =
+      CefResourceBundleHandlerCppToC::Get(self)->GetDataResourceForScale(
+          resource_id, scale_factor, dataVal, data_sizeVal);
+
+  // Restore param: data; type: simple_byref
+  if (data)
+    *data = dataVal;
+  // Restore param: data_size; type: simple_byref
+  if (data_size)
+    *data_size = data_sizeVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceBundleHandlerCppToC::CefResourceBundleHandlerCppToC() {
+  GetStruct()->get_localized_string =
+      resource_bundle_handler_get_localized_string;
+  GetStruct()->get_data_resource = resource_bundle_handler_get_data_resource;
+  GetStruct()->get_data_resource_for_scale =
+      resource_bundle_handler_get_data_resource_for_scale;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceBundleHandlerCppToC::~CefResourceBundleHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefResourceBundleHandler> CefCppToCRefCounted<
+    CefResourceBundleHandlerCppToC,
+    CefResourceBundleHandler,
+    cef_resource_bundle_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                  cef_resource_bundle_handler_t*
+                                                      s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefResourceBundleHandlerCppToC,
+                        CefResourceBundleHandler,
+                        cef_resource_bundle_handler_t>::kWrapperType =
+        WT_RESOURCE_BUNDLE_HANDLER;
diff --git a/src/libcef_dll/cpptoc/resource_bundle_handler_cpptoc.h b/src/libcef_dll/cpptoc/resource_bundle_handler_cpptoc.h
new file mode 100644
index 0000000..e83a405
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_bundle_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=14d8ddea86b253b3963da1e2fd9ddb17a1306451$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESOURCE_BUNDLE_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESOURCE_BUNDLE_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_resource_bundle_handler_capi.h"
+#include "include/cef_resource_bundle_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResourceBundleHandlerCppToC
+    : public CefCppToCRefCounted<CefResourceBundleHandlerCppToC,
+                                 CefResourceBundleHandler,
+                                 cef_resource_bundle_handler_t> {
+ public:
+  CefResourceBundleHandlerCppToC();
+  virtual ~CefResourceBundleHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESOURCE_BUNDLE_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/resource_handler_cpptoc.cc b/src/libcef_dll/cpptoc/resource_handler_cpptoc.cc
new file mode 100644
index 0000000..d515ba5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_handler_cpptoc.cc
@@ -0,0 +1,301 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4f0405f57b7364001a3ecf56c8d6502f7eaf818a$
+//
+
+#include "libcef_dll/cpptoc/resource_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/callback_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/resource_read_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/resource_skip_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/response_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK resource_handler_open(struct _cef_resource_handler_t* self,
+                                       cef_request_t* request,
+                                       int* handle_request,
+                                       cef_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return 0;
+  // Verify param: handle_request; type: bool_byref
+  DCHECK(handle_request);
+  if (!handle_request)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Translate param: handle_request; type: bool_byref
+  bool handle_requestBool = (handle_request && *handle_request) ? true : false;
+
+  // Execute
+  bool _retval = CefResourceHandlerCppToC::Get(self)->Open(
+      CefRequestCToCpp::Wrap(request), handle_requestBool,
+      CefCallbackCToCpp::Wrap(callback));
+
+  // Restore param: handle_request; type: bool_byref
+  if (handle_request)
+    *handle_request = handle_requestBool ? true : false;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+resource_handler_process_request(struct _cef_resource_handler_t* self,
+                                 cef_request_t* request,
+                                 cef_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Execute
+  bool _retval = CefResourceHandlerCppToC::Get(self)->ProcessRequest(
+      CefRequestCToCpp::Wrap(request), CefCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+resource_handler_get_response_headers(struct _cef_resource_handler_t* self,
+                                      struct _cef_response_t* response,
+                                      int64* response_length,
+                                      cef_string_t* redirectUrl) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response);
+  if (!response)
+    return;
+  // Verify param: response_length; type: simple_byref
+  DCHECK(response_length);
+  if (!response_length)
+    return;
+  // Verify param: redirectUrl; type: string_byref
+  DCHECK(redirectUrl);
+  if (!redirectUrl)
+    return;
+
+  // Translate param: response_length; type: simple_byref
+  int64 response_lengthVal = response_length ? *response_length : 0;
+  // Translate param: redirectUrl; type: string_byref
+  CefString redirectUrlStr(redirectUrl);
+
+  // Execute
+  CefResourceHandlerCppToC::Get(self)->GetResponseHeaders(
+      CefResponseCToCpp::Wrap(response), response_lengthVal, redirectUrlStr);
+
+  // Restore param: response_length; type: simple_byref
+  if (response_length)
+    *response_length = response_lengthVal;
+}
+
+int CEF_CALLBACK resource_handler_skip(struct _cef_resource_handler_t* self,
+                                       int64 bytes_to_skip,
+                                       int64* bytes_skipped,
+                                       cef_resource_skip_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: bytes_skipped; type: simple_byref
+  DCHECK(bytes_skipped);
+  if (!bytes_skipped)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Translate param: bytes_skipped; type: simple_byref
+  int64 bytes_skippedVal = bytes_skipped ? *bytes_skipped : 0;
+
+  // Execute
+  bool _retval = CefResourceHandlerCppToC::Get(self)->Skip(
+      bytes_to_skip, bytes_skippedVal,
+      CefResourceSkipCallbackCToCpp::Wrap(callback));
+
+  // Restore param: bytes_skipped; type: simple_byref
+  if (bytes_skipped)
+    *bytes_skipped = bytes_skippedVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK resource_handler_read(struct _cef_resource_handler_t* self,
+                                       void* data_out,
+                                       int bytes_to_read,
+                                       int* bytes_read,
+                                       cef_resource_read_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: data_out; type: simple_byaddr
+  DCHECK(data_out);
+  if (!data_out)
+    return 0;
+  // Verify param: bytes_read; type: simple_byref
+  DCHECK(bytes_read);
+  if (!bytes_read)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Translate param: bytes_read; type: simple_byref
+  int bytes_readVal = bytes_read ? *bytes_read : 0;
+
+  // Execute
+  bool _retval = CefResourceHandlerCppToC::Get(self)->Read(
+      data_out, bytes_to_read, bytes_readVal,
+      CefResourceReadCallbackCToCpp::Wrap(callback));
+
+  // Restore param: bytes_read; type: simple_byref
+  if (bytes_read)
+    *bytes_read = bytes_readVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+resource_handler_read_response(struct _cef_resource_handler_t* self,
+                               void* data_out,
+                               int bytes_to_read,
+                               int* bytes_read,
+                               cef_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: data_out; type: simple_byaddr
+  DCHECK(data_out);
+  if (!data_out)
+    return 0;
+  // Verify param: bytes_read; type: simple_byref
+  DCHECK(bytes_read);
+  if (!bytes_read)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+
+  // Translate param: bytes_read; type: simple_byref
+  int bytes_readVal = bytes_read ? *bytes_read : 0;
+
+  // Execute
+  bool _retval = CefResourceHandlerCppToC::Get(self)->ReadResponse(
+      data_out, bytes_to_read, bytes_readVal,
+      CefCallbackCToCpp::Wrap(callback));
+
+  // Restore param: bytes_read; type: simple_byref
+  if (bytes_read)
+    *bytes_read = bytes_readVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+resource_handler_cancel(struct _cef_resource_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefResourceHandlerCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceHandlerCppToC::CefResourceHandlerCppToC() {
+  GetStruct()->open = resource_handler_open;
+  GetStruct()->process_request = resource_handler_process_request;
+  GetStruct()->get_response_headers = resource_handler_get_response_headers;
+  GetStruct()->skip = resource_handler_skip;
+  GetStruct()->read = resource_handler_read;
+  GetStruct()->read_response = resource_handler_read_response;
+  GetStruct()->cancel = resource_handler_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceHandlerCppToC::~CefResourceHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefResourceHandler> CefCppToCRefCounted<
+    CefResourceHandlerCppToC,
+    CefResourceHandler,
+    cef_resource_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           cef_resource_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefResourceHandlerCppToC,
+                                   CefResourceHandler,
+                                   cef_resource_handler_t>::kWrapperType =
+    WT_RESOURCE_HANDLER;
diff --git a/src/libcef_dll/cpptoc/resource_handler_cpptoc.h b/src/libcef_dll/cpptoc/resource_handler_cpptoc.h
new file mode 100644
index 0000000..ce1750a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c2767ff40514f4b4901656da7e7b8fe004eecc1c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESOURCE_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESOURCE_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/cef_resource_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResourceHandlerCppToC
+    : public CefCppToCRefCounted<CefResourceHandlerCppToC,
+                                 CefResourceHandler,
+                                 cef_resource_handler_t> {
+ public:
+  CefResourceHandlerCppToC();
+  virtual ~CefResourceHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESOURCE_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/resource_read_callback_cpptoc.cc b/src/libcef_dll/cpptoc/resource_read_callback_cpptoc.cc
new file mode 100644
index 0000000..e1f1c1c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_read_callback_cpptoc.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3e7417f66df20a3329577fceaceb329f835290e4$
+//
+
+#include "libcef_dll/cpptoc/resource_read_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+resource_read_callback_cont(struct _cef_resource_read_callback_t* self,
+                            int bytes_read) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefResourceReadCallbackCppToC::Get(self)->Continue(bytes_read);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceReadCallbackCppToC::CefResourceReadCallbackCppToC() {
+  GetStruct()->cont = resource_read_callback_cont;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceReadCallbackCppToC::~CefResourceReadCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefResourceReadCallback> CefCppToCRefCounted<
+    CefResourceReadCallbackCppToC,
+    CefResourceReadCallback,
+    cef_resource_read_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                 cef_resource_read_callback_t*
+                                                     s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefResourceReadCallbackCppToC,
+                                   CefResourceReadCallback,
+                                   cef_resource_read_callback_t>::kWrapperType =
+    WT_RESOURCE_READ_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/resource_read_callback_cpptoc.h b/src/libcef_dll/cpptoc/resource_read_callback_cpptoc.h
new file mode 100644
index 0000000..51c38b4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_read_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=35d620ce2f1cb16c858f37cf8c5c518bc36b5aea$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESOURCE_READ_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESOURCE_READ_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/cef_resource_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefResourceReadCallbackCppToC
+    : public CefCppToCRefCounted<CefResourceReadCallbackCppToC,
+                                 CefResourceReadCallback,
+                                 cef_resource_read_callback_t> {
+ public:
+  CefResourceReadCallbackCppToC();
+  virtual ~CefResourceReadCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESOURCE_READ_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/resource_request_handler_cpptoc.cc b/src/libcef_dll/cpptoc/resource_request_handler_cpptoc.cc
new file mode 100644
index 0000000..09a7d55
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_request_handler_cpptoc.cc
@@ -0,0 +1,321 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b21e07c41e050dd666e5e25a3b9041e67d1fd299$
+//
+
+#include "libcef_dll/cpptoc/resource_request_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/cookie_access_filter_cpptoc.h"
+#include "libcef_dll/cpptoc/resource_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/response_filter_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/request_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/response_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+struct _cef_cookie_access_filter_t* CEF_CALLBACK
+resource_request_handler_get_cookie_access_filter(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Unverified params: browser, frame
+
+  // Execute
+  CefRefPtr<CefCookieAccessFilter> _retval =
+      CefResourceRequestHandlerCppToC::Get(self)->GetCookieAccessFilter(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          CefRequestCToCpp::Wrap(request));
+
+  // Return type: refptr_same
+  return CefCookieAccessFilterCppToC::Wrap(_retval);
+}
+
+cef_return_value_t CEF_CALLBACK
+resource_request_handler_on_before_resource_load(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    cef_request_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return RV_CONTINUE;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return RV_CONTINUE;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return RV_CONTINUE;
+  // Unverified params: browser, frame
+
+  // Execute
+  cef_return_value_t _retval =
+      CefResourceRequestHandlerCppToC::Get(self)->OnBeforeResourceLoad(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          CefRequestCToCpp::Wrap(request),
+          CefRequestCallbackCToCpp::Wrap(callback));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_resource_handler_t* CEF_CALLBACK
+resource_request_handler_get_resource_handler(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Unverified params: browser, frame
+
+  // Execute
+  CefRefPtr<CefResourceHandler> _retval =
+      CefResourceRequestHandlerCppToC::Get(self)->GetResourceHandler(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          CefRequestCToCpp::Wrap(request));
+
+  // Return type: refptr_same
+  return CefResourceHandlerCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK resource_request_handler_on_resource_redirect(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    struct _cef_response_t* response,
+    cef_string_t* new_url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response);
+  if (!response)
+    return;
+  // Verify param: new_url; type: string_byref
+  DCHECK(new_url);
+  if (!new_url)
+    return;
+  // Unverified params: browser, frame
+
+  // Translate param: new_url; type: string_byref
+  CefString new_urlStr(new_url);
+
+  // Execute
+  CefResourceRequestHandlerCppToC::Get(self)->OnResourceRedirect(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefRequestCToCpp::Wrap(request), CefResponseCToCpp::Wrap(response),
+      new_urlStr);
+}
+
+int CEF_CALLBACK resource_request_handler_on_resource_response(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    struct _cef_response_t* response) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return 0;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response);
+  if (!response)
+    return 0;
+  // Unverified params: browser, frame
+
+  // Execute
+  bool _retval = CefResourceRequestHandlerCppToC::Get(self)->OnResourceResponse(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefRequestCToCpp::Wrap(request), CefResponseCToCpp::Wrap(response));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_response_filter_t* CEF_CALLBACK
+resource_request_handler_get_resource_response_filter(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    struct _cef_response_t* response) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response);
+  if (!response)
+    return NULL;
+  // Unverified params: browser, frame
+
+  // Execute
+  CefRefPtr<CefResponseFilter> _retval =
+      CefResourceRequestHandlerCppToC::Get(self)->GetResourceResponseFilter(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          CefRequestCToCpp::Wrap(request), CefResponseCToCpp::Wrap(response));
+
+  // Return type: refptr_same
+  return CefResponseFilterCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK resource_request_handler_on_resource_load_complete(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    struct _cef_response_t* response,
+    cef_urlrequest_status_t status,
+    int64 received_content_length) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response);
+  if (!response)
+    return;
+  // Unverified params: browser, frame
+
+  // Execute
+  CefResourceRequestHandlerCppToC::Get(self)->OnResourceLoadComplete(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefRequestCToCpp::Wrap(request), CefResponseCToCpp::Wrap(response),
+      status, received_content_length);
+}
+
+void CEF_CALLBACK resource_request_handler_on_protocol_execution(
+    struct _cef_resource_request_handler_t* self,
+    cef_browser_t* browser,
+    cef_frame_t* frame,
+    cef_request_t* request,
+    int* allow_os_execution) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+  // Verify param: allow_os_execution; type: bool_byref
+  DCHECK(allow_os_execution);
+  if (!allow_os_execution)
+    return;
+  // Unverified params: browser, frame
+
+  // Translate param: allow_os_execution; type: bool_byref
+  bool allow_os_executionBool =
+      (allow_os_execution && *allow_os_execution) ? true : false;
+
+  // Execute
+  CefResourceRequestHandlerCppToC::Get(self)->OnProtocolExecution(
+      CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+      CefRequestCToCpp::Wrap(request), allow_os_executionBool);
+
+  // Restore param: allow_os_execution; type: bool_byref
+  if (allow_os_execution)
+    *allow_os_execution = allow_os_executionBool ? true : false;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceRequestHandlerCppToC::CefResourceRequestHandlerCppToC() {
+  GetStruct()->get_cookie_access_filter =
+      resource_request_handler_get_cookie_access_filter;
+  GetStruct()->on_before_resource_load =
+      resource_request_handler_on_before_resource_load;
+  GetStruct()->get_resource_handler =
+      resource_request_handler_get_resource_handler;
+  GetStruct()->on_resource_redirect =
+      resource_request_handler_on_resource_redirect;
+  GetStruct()->on_resource_response =
+      resource_request_handler_on_resource_response;
+  GetStruct()->get_resource_response_filter =
+      resource_request_handler_get_resource_response_filter;
+  GetStruct()->on_resource_load_complete =
+      resource_request_handler_on_resource_load_complete;
+  GetStruct()->on_protocol_execution =
+      resource_request_handler_on_protocol_execution;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceRequestHandlerCppToC::~CefResourceRequestHandlerCppToC() {}
+
+template <>
+CefRefPtr<CefResourceRequestHandler>
+CefCppToCRefCounted<CefResourceRequestHandlerCppToC,
+                    CefResourceRequestHandler,
+                    cef_resource_request_handler_t>::
+    UnwrapDerived(CefWrapperType type, cef_resource_request_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefResourceRequestHandlerCppToC,
+                        CefResourceRequestHandler,
+                        cef_resource_request_handler_t>::kWrapperType =
+        WT_RESOURCE_REQUEST_HANDLER;
diff --git a/src/libcef_dll/cpptoc/resource_request_handler_cpptoc.h b/src/libcef_dll/cpptoc/resource_request_handler_cpptoc.h
new file mode 100644
index 0000000..2a7f3cc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_request_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6279d2c726b5418595c1cdd7c4557de590af60a0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESOURCE_REQUEST_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESOURCE_REQUEST_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_resource_request_handler_capi.h"
+#include "include/cef_resource_request_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResourceRequestHandlerCppToC
+    : public CefCppToCRefCounted<CefResourceRequestHandlerCppToC,
+                                 CefResourceRequestHandler,
+                                 cef_resource_request_handler_t> {
+ public:
+  CefResourceRequestHandlerCppToC();
+  virtual ~CefResourceRequestHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESOURCE_REQUEST_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/resource_skip_callback_cpptoc.cc b/src/libcef_dll/cpptoc/resource_skip_callback_cpptoc.cc
new file mode 100644
index 0000000..906583e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_skip_callback_cpptoc.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1cb83ab583c93ae5b46cdd75a1a053d806a2d388$
+//
+
+#include "libcef_dll/cpptoc/resource_skip_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+resource_skip_callback_cont(struct _cef_resource_skip_callback_t* self,
+                            int64 bytes_skipped) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefResourceSkipCallbackCppToC::Get(self)->Continue(bytes_skipped);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceSkipCallbackCppToC::CefResourceSkipCallbackCppToC() {
+  GetStruct()->cont = resource_skip_callback_cont;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceSkipCallbackCppToC::~CefResourceSkipCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefResourceSkipCallback> CefCppToCRefCounted<
+    CefResourceSkipCallbackCppToC,
+    CefResourceSkipCallback,
+    cef_resource_skip_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                 cef_resource_skip_callback_t*
+                                                     s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefResourceSkipCallbackCppToC,
+                                   CefResourceSkipCallback,
+                                   cef_resource_skip_callback_t>::kWrapperType =
+    WT_RESOURCE_SKIP_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/resource_skip_callback_cpptoc.h b/src/libcef_dll/cpptoc/resource_skip_callback_cpptoc.h
new file mode 100644
index 0000000..3d7bde6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/resource_skip_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cd59d4e2414727eda67fe7994eba8737ddb9c336$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESOURCE_SKIP_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESOURCE_SKIP_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/cef_resource_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefResourceSkipCallbackCppToC
+    : public CefCppToCRefCounted<CefResourceSkipCallbackCppToC,
+                                 CefResourceSkipCallback,
+                                 cef_resource_skip_callback_t> {
+ public:
+  CefResourceSkipCallbackCppToC();
+  virtual ~CefResourceSkipCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESOURCE_SKIP_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/response_cpptoc.cc b/src/libcef_dll/cpptoc/response_cpptoc.cc
new file mode 100644
index 0000000..8d0f6ae
--- /dev/null
+++ b/src/libcef_dll/cpptoc/response_cpptoc.cc
@@ -0,0 +1,336 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=becfb6e4e0d47746f10295fa4843c9baebc786b5$
+//
+
+#include "libcef_dll/cpptoc/response_cpptoc.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_response_t* cef_response_create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefResponse> _retval = CefResponse::Create();
+
+  // Return type: refptr_same
+  return CefResponseCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK response_is_read_only(struct _cef_response_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefResponseCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_errorcode_t CEF_CALLBACK response_get_error(struct _cef_response_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return ERR_NONE;
+
+  // Execute
+  cef_errorcode_t _retval = CefResponseCppToC::Get(self)->GetError();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK response_set_error(struct _cef_response_t* self,
+                                     cef_errorcode_t error) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetError(error);
+}
+
+int CEF_CALLBACK response_get_status(struct _cef_response_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefResponseCppToC::Get(self)->GetStatus();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK response_set_status(struct _cef_response_t* self,
+                                      int status) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetStatus(status);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+response_get_status_text(struct _cef_response_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefResponseCppToC::Get(self)->GetStatusText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK response_set_status_text(struct _cef_response_t* self,
+                                           const cef_string_t* statusText) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: statusText
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetStatusText(CefString(statusText));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+response_get_mime_type(struct _cef_response_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefResponseCppToC::Get(self)->GetMimeType();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK response_set_mime_type(struct _cef_response_t* self,
+                                         const cef_string_t* mimeType) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: mimeType
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetMimeType(CefString(mimeType));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+response_get_charset(struct _cef_response_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefResponseCppToC::Get(self)->GetCharset();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK response_set_charset(struct _cef_response_t* self,
+                                       const cef_string_t* charset) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: charset
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetCharset(CefString(charset));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+response_get_header_by_name(struct _cef_response_t* self,
+                            const cef_string_t* name) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefResponseCppToC::Get(self)->GetHeaderByName(CefString(name));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK response_set_header_by_name(struct _cef_response_t* self,
+                                              const cef_string_t* name,
+                                              const cef_string_t* value,
+                                              int overwrite) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+  // Unverified params: value
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetHeaderByName(
+      CefString(name), CefString(value), overwrite ? true : false);
+}
+
+void CEF_CALLBACK response_get_header_map(struct _cef_response_t* self,
+                                          cef_string_multimap_t headerMap) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: headerMap; type: string_map_multi_byref
+  DCHECK(headerMap);
+  if (!headerMap)
+    return;
+
+  // Translate param: headerMap; type: string_map_multi_byref
+  std::multimap<CefString, CefString> headerMapMultimap;
+  transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  CefResponseCppToC::Get(self)->GetHeaderMap(headerMapMultimap);
+
+  // Restore param: headerMap; type: string_map_multi_byref
+  cef_string_multimap_clear(headerMap);
+  transfer_string_multimap_contents(headerMapMultimap, headerMap);
+}
+
+void CEF_CALLBACK response_set_header_map(struct _cef_response_t* self,
+                                          cef_string_multimap_t headerMap) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: headerMap; type: string_map_multi_byref_const
+  DCHECK(headerMap);
+  if (!headerMap)
+    return;
+
+  // Translate param: headerMap; type: string_map_multi_byref_const
+  std::multimap<CefString, CefString> headerMapMultimap;
+  transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetHeaderMap(headerMapMultimap);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+response_get_url(struct _cef_response_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefResponseCppToC::Get(self)->GetURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK response_set_url(struct _cef_response_t* self,
+                                   const cef_string_t* url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: url
+
+  // Execute
+  CefResponseCppToC::Get(self)->SetURL(CefString(url));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResponseCppToC::CefResponseCppToC() {
+  GetStruct()->is_read_only = response_is_read_only;
+  GetStruct()->get_error = response_get_error;
+  GetStruct()->set_error = response_set_error;
+  GetStruct()->get_status = response_get_status;
+  GetStruct()->set_status = response_set_status;
+  GetStruct()->get_status_text = response_get_status_text;
+  GetStruct()->set_status_text = response_set_status_text;
+  GetStruct()->get_mime_type = response_get_mime_type;
+  GetStruct()->set_mime_type = response_set_mime_type;
+  GetStruct()->get_charset = response_get_charset;
+  GetStruct()->set_charset = response_set_charset;
+  GetStruct()->get_header_by_name = response_get_header_by_name;
+  GetStruct()->set_header_by_name = response_set_header_by_name;
+  GetStruct()->get_header_map = response_get_header_map;
+  GetStruct()->set_header_map = response_set_header_map;
+  GetStruct()->get_url = response_get_url;
+  GetStruct()->set_url = response_set_url;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResponseCppToC::~CefResponseCppToC() {}
+
+template <>
+CefRefPtr<CefResponse>
+CefCppToCRefCounted<CefResponseCppToC, CefResponse, cef_response_t>::
+    UnwrapDerived(CefWrapperType type, cef_response_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefResponseCppToC,
+                                   CefResponse,
+                                   cef_response_t>::kWrapperType = WT_RESPONSE;
diff --git a/src/libcef_dll/cpptoc/response_cpptoc.h b/src/libcef_dll/cpptoc/response_cpptoc.h
new file mode 100644
index 0000000..74f393b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/response_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=77aefe2ee459cef3a90edcedcaf25980c97c91eb$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESPONSE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESPONSE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_response_capi.h"
+#include "include/cef_response.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefResponseCppToC : public CefCppToCRefCounted<CefResponseCppToC,
+                                                     CefResponse,
+                                                     cef_response_t> {
+ public:
+  CefResponseCppToC();
+  virtual ~CefResponseCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESPONSE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/response_filter_cpptoc.cc b/src/libcef_dll/cpptoc/response_filter_cpptoc.cc
new file mode 100644
index 0000000..7359546
--- /dev/null
+++ b/src/libcef_dll/cpptoc/response_filter_cpptoc.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=464d0d568a1b3712452c0f8406057bd598440f20$
+//
+
+#include "libcef_dll/cpptoc/response_filter_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+response_filter_init_filter(struct _cef_response_filter_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefResponseFilterCppToC::Get(self)->InitFilter();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_response_filter_status_t CEF_CALLBACK
+response_filter_filter(struct _cef_response_filter_t* self,
+                       void* data_in,
+                       size_t data_in_size,
+                       size_t* data_in_read,
+                       void* data_out,
+                       size_t data_out_size,
+                       size_t* data_out_written) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return RESPONSE_FILTER_ERROR;
+  // Verify param: data_in_read; type: simple_byref
+  DCHECK(data_in_read);
+  if (!data_in_read)
+    return RESPONSE_FILTER_ERROR;
+  // Verify param: data_out; type: simple_byaddr
+  DCHECK(data_out);
+  if (!data_out)
+    return RESPONSE_FILTER_ERROR;
+  // Verify param: data_out_written; type: simple_byref
+  DCHECK(data_out_written);
+  if (!data_out_written)
+    return RESPONSE_FILTER_ERROR;
+  // Unverified params: data_in
+
+  // Translate param: data_in_read; type: simple_byref
+  size_t data_in_readVal = data_in_read ? *data_in_read : 0;
+  // Translate param: data_out_written; type: simple_byref
+  size_t data_out_writtenVal = data_out_written ? *data_out_written : 0;
+
+  // Execute
+  cef_response_filter_status_t _retval =
+      CefResponseFilterCppToC::Get(self)->Filter(
+          data_in, data_in_size, data_in_readVal, data_out, data_out_size,
+          data_out_writtenVal);
+
+  // Restore param: data_in_read; type: simple_byref
+  if (data_in_read)
+    *data_in_read = data_in_readVal;
+  // Restore param: data_out_written; type: simple_byref
+  if (data_out_written)
+    *data_out_written = data_out_writtenVal;
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResponseFilterCppToC::CefResponseFilterCppToC() {
+  GetStruct()->init_filter = response_filter_init_filter;
+  GetStruct()->filter = response_filter_filter;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResponseFilterCppToC::~CefResponseFilterCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefResponseFilter> CefCppToCRefCounted<
+    CefResponseFilterCppToC,
+    CefResponseFilter,
+    cef_response_filter_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_response_filter_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefResponseFilterCppToC,
+                                   CefResponseFilter,
+                                   cef_response_filter_t>::kWrapperType =
+    WT_RESPONSE_FILTER;
diff --git a/src/libcef_dll/cpptoc/response_filter_cpptoc.h b/src/libcef_dll/cpptoc/response_filter_cpptoc.h
new file mode 100644
index 0000000..1b77e5c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/response_filter_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1cdc6d51716626972e346cc2eaf03d396067ea8a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RESPONSE_FILTER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RESPONSE_FILTER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_response_filter_capi.h"
+#include "include/cef_response_filter.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResponseFilterCppToC
+    : public CefCppToCRefCounted<CefResponseFilterCppToC,
+                                 CefResponseFilter,
+                                 cef_response_filter_t> {
+ public:
+  CefResponseFilterCppToC();
+  virtual ~CefResponseFilterCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RESPONSE_FILTER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/run_context_menu_callback_cpptoc.cc b/src/libcef_dll/cpptoc/run_context_menu_callback_cpptoc.cc
new file mode 100644
index 0000000..591f757
--- /dev/null
+++ b/src/libcef_dll/cpptoc/run_context_menu_callback_cpptoc.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7445defe1636c34ac7fc05e2acb02330cf71ef6e$
+//
+
+#include "libcef_dll/cpptoc/run_context_menu_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+run_context_menu_callback_cont(struct _cef_run_context_menu_callback_t* self,
+                               int command_id,
+                               cef_event_flags_t event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefRunContextMenuCallbackCppToC::Get(self)->Continue(command_id, event_flags);
+}
+
+void CEF_CALLBACK run_context_menu_callback_cancel(
+    struct _cef_run_context_menu_callback_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefRunContextMenuCallbackCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRunContextMenuCallbackCppToC::CefRunContextMenuCallbackCppToC() {
+  GetStruct()->cont = run_context_menu_callback_cont;
+  GetStruct()->cancel = run_context_menu_callback_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRunContextMenuCallbackCppToC::~CefRunContextMenuCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefRunContextMenuCallback>
+CefCppToCRefCounted<CefRunContextMenuCallbackCppToC,
+                    CefRunContextMenuCallback,
+                    cef_run_context_menu_callback_t>::
+    UnwrapDerived(CefWrapperType type, cef_run_context_menu_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefRunContextMenuCallbackCppToC,
+                        CefRunContextMenuCallback,
+                        cef_run_context_menu_callback_t>::kWrapperType =
+        WT_RUN_CONTEXT_MENU_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/run_context_menu_callback_cpptoc.h b/src/libcef_dll/cpptoc/run_context_menu_callback_cpptoc.h
new file mode 100644
index 0000000..b5329a6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/run_context_menu_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0bdb34b3f23060c3bdc9f81f20bd1a31fe843713$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RUN_CONTEXT_MENU_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RUN_CONTEXT_MENU_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_context_menu_handler_capi.h"
+#include "include/cef_context_menu_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefRunContextMenuCallbackCppToC
+    : public CefCppToCRefCounted<CefRunContextMenuCallbackCppToC,
+                                 CefRunContextMenuCallback,
+                                 cef_run_context_menu_callback_t> {
+ public:
+  CefRunContextMenuCallbackCppToC();
+  virtual ~CefRunContextMenuCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RUN_CONTEXT_MENU_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.cc b/src/libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.cc
new file mode 100644
index 0000000..0166fa3
--- /dev/null
+++ b/src/libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d2364091da1298fe5b7d1e168cc21016128660bf$
+//
+
+#include "libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK run_file_dialog_callback_on_file_dialog_dismissed(
+    struct _cef_run_file_dialog_callback_t* self,
+    int selected_accept_filter,
+    cef_string_list_t file_paths) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return;
+  // Unverified params: file_paths
+
+  // Translate param: file_paths; type: string_vec_byref_const
+  std::vector<CefString> file_pathsList;
+  transfer_string_list_contents(file_paths, file_pathsList);
+
+  // Execute
+  CefRunFileDialogCallbackCppToC::Get(self)->OnFileDialogDismissed(
+      selected_accept_filter, file_pathsList);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRunFileDialogCallbackCppToC::CefRunFileDialogCallbackCppToC() {
+  GetStruct()->on_file_dialog_dismissed =
+      run_file_dialog_callback_on_file_dialog_dismissed;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRunFileDialogCallbackCppToC::~CefRunFileDialogCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefRunFileDialogCallback>
+CefCppToCRefCounted<CefRunFileDialogCallbackCppToC,
+                    CefRunFileDialogCallback,
+                    cef_run_file_dialog_callback_t>::
+    UnwrapDerived(CefWrapperType type, cef_run_file_dialog_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefRunFileDialogCallbackCppToC,
+                        CefRunFileDialogCallback,
+                        cef_run_file_dialog_callback_t>::kWrapperType =
+        WT_RUN_FILE_DIALOG_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h b/src/libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h
new file mode 100644
index 0000000..93ca495
--- /dev/null
+++ b/src/libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f683c2febb92bc08f9e5f82cc0ec35c334026ca2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_RUN_FILE_DIALOG_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_RUN_FILE_DIALOG_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRunFileDialogCallbackCppToC
+    : public CefCppToCRefCounted<CefRunFileDialogCallbackCppToC,
+                                 CefRunFileDialogCallback,
+                                 cef_run_file_dialog_callback_t> {
+ public:
+  CefRunFileDialogCallbackCppToC();
+  virtual ~CefRunFileDialogCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_RUN_FILE_DIALOG_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc b/src/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc
new file mode 100644
index 0000000..9944830
--- /dev/null
+++ b/src/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2303bd78ed1cf87d4093a46ff6b0830e0f7e5090$
+//
+
+#include "libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h"
+#include "libcef_dll/cpptoc/resource_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_resource_handler_t* CEF_CALLBACK
+scheme_handler_factory_create(struct _cef_scheme_handler_factory_t* self,
+                              cef_browser_t* browser,
+                              cef_frame_t* frame,
+                              const cef_string_t* scheme_name,
+                              cef_request_t* request) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(scheme_name);
+  if (!scheme_name)
+    return NULL;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Unverified params: browser, frame
+
+  // Execute
+  CefRefPtr<CefResourceHandler> _retval =
+      CefSchemeHandlerFactoryCppToC::Get(self)->Create(
+          CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
+          CefString(scheme_name), CefRequestCToCpp::Wrap(request));
+
+  // Return type: refptr_same
+  return CefResourceHandlerCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSchemeHandlerFactoryCppToC::CefSchemeHandlerFactoryCppToC() {
+  GetStruct()->create = scheme_handler_factory_create;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSchemeHandlerFactoryCppToC::~CefSchemeHandlerFactoryCppToC() {}
+
+template <>
+CefRefPtr<CefSchemeHandlerFactory> CefCppToCRefCounted<
+    CefSchemeHandlerFactoryCppToC,
+    CefSchemeHandlerFactory,
+    cef_scheme_handler_factory_t>::UnwrapDerived(CefWrapperType type,
+                                                 cef_scheme_handler_factory_t*
+                                                     s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefSchemeHandlerFactoryCppToC,
+                                   CefSchemeHandlerFactory,
+                                   cef_scheme_handler_factory_t>::kWrapperType =
+    WT_SCHEME_HANDLER_FACTORY;
diff --git a/src/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h b/src/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h
new file mode 100644
index 0000000..5b07e2d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=63f4989c0dad8b36a2fc9152e657173e42af698e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SCHEME_HANDLER_FACTORY_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SCHEME_HANDLER_FACTORY_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefSchemeHandlerFactoryCppToC
+    : public CefCppToCRefCounted<CefSchemeHandlerFactoryCppToC,
+                                 CefSchemeHandlerFactory,
+                                 cef_scheme_handler_factory_t> {
+ public:
+  CefSchemeHandlerFactoryCppToC();
+  virtual ~CefSchemeHandlerFactoryCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SCHEME_HANDLER_FACTORY_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/scheme_registrar_cpptoc.cc b/src/libcef_dll/cpptoc/scheme_registrar_cpptoc.cc
new file mode 100644
index 0000000..aefb1d9
--- /dev/null
+++ b/src/libcef_dll/cpptoc/scheme_registrar_cpptoc.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=64e26329466a4e6f497b648abbcb60bb5619b033$
+//
+
+#include "libcef_dll/cpptoc/scheme_registrar_cpptoc.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+scheme_registrar_add_custom_scheme(struct _cef_scheme_registrar_t* self,
+                                   const cef_string_t* scheme_name,
+                                   int options) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(scheme_name);
+  if (!scheme_name)
+    return 0;
+
+  // Execute
+  bool _retval = CefSchemeRegistrarCppToC::Get(self)->AddCustomScheme(
+      CefString(scheme_name), options);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSchemeRegistrarCppToC::CefSchemeRegistrarCppToC() {
+  GetStruct()->add_custom_scheme = scheme_registrar_add_custom_scheme;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSchemeRegistrarCppToC::~CefSchemeRegistrarCppToC() {}
+
+template <>
+CefOwnPtr<CefSchemeRegistrar> CefCppToCScoped<
+    CefSchemeRegistrarCppToC,
+    CefSchemeRegistrar,
+    cef_scheme_registrar_t>::UnwrapDerivedOwn(CefWrapperType type,
+                                              cef_scheme_registrar_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return CefOwnPtr<CefSchemeRegistrar>();
+}
+
+template <>
+CefRawPtr<CefSchemeRegistrar> CefCppToCScoped<
+    CefSchemeRegistrarCppToC,
+    CefSchemeRegistrar,
+    cef_scheme_registrar_t>::UnwrapDerivedRaw(CefWrapperType type,
+                                              cef_scheme_registrar_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCScoped<CefSchemeRegistrarCppToC,
+                               CefSchemeRegistrar,
+                               cef_scheme_registrar_t>::kWrapperType =
+    WT_SCHEME_REGISTRAR;
diff --git a/src/libcef_dll/cpptoc/scheme_registrar_cpptoc.h b/src/libcef_dll/cpptoc/scheme_registrar_cpptoc.h
new file mode 100644
index 0000000..9a77c40
--- /dev/null
+++ b/src/libcef_dll/cpptoc/scheme_registrar_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4e6d84fb9e3dd4b4f232686ebe01b12ab96daa9f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SCHEME_REGISTRAR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SCHEME_REGISTRAR_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/cpptoc/cpptoc_scoped.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefSchemeRegistrarCppToC
+    : public CefCppToCScoped<CefSchemeRegistrarCppToC,
+                             CefSchemeRegistrar,
+                             cef_scheme_registrar_t> {
+ public:
+  CefSchemeRegistrarCppToC();
+  virtual ~CefSchemeRegistrarCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SCHEME_REGISTRAR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.cc b/src/libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.cc
new file mode 100644
index 0000000..3b315ae
--- /dev/null
+++ b/src/libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cc2fc42f673c2a27a73e5237f1c9533bf550aa33$
+//
+
+#include "libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/x509certificate_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK select_client_certificate_callback_select(
+    struct _cef_select_client_certificate_callback_t* self,
+    struct _cef_x509certificate_t* cert) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: cert
+
+  // Execute
+  CefSelectClientCertificateCallbackCppToC::Get(self)->Select(
+      CefX509CertificateCppToC::Unwrap(cert));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSelectClientCertificateCallbackCppToC::
+    CefSelectClientCertificateCallbackCppToC() {
+  GetStruct()->select = select_client_certificate_callback_select;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSelectClientCertificateCallbackCppToC::
+    ~CefSelectClientCertificateCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefSelectClientCertificateCallback>
+CefCppToCRefCounted<CefSelectClientCertificateCallbackCppToC,
+                    CefSelectClientCertificateCallback,
+                    cef_select_client_certificate_callback_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_select_client_certificate_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<
+    CefSelectClientCertificateCallbackCppToC,
+    CefSelectClientCertificateCallback,
+    cef_select_client_certificate_callback_t>::kWrapperType =
+    WT_SELECT_CLIENT_CERTIFICATE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h b/src/libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h
new file mode 100644
index 0000000..e35a6d6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=48bb19280dc30b73274f95d7e144ceb7439f859a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SELECT_CLIENT_CERTIFICATE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SELECT_CLIENT_CERTIFICATE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_handler_capi.h"
+#include "include/cef_request_handler.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefSelectClientCertificateCallbackCppToC
+    : public CefCppToCRefCounted<CefSelectClientCertificateCallbackCppToC,
+                                 CefSelectClientCertificateCallback,
+                                 cef_select_client_certificate_callback_t> {
+ public:
+  CefSelectClientCertificateCallbackCppToC();
+  virtual ~CefSelectClientCertificateCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SELECT_CLIENT_CERTIFICATE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/server_cpptoc.cc b/src/libcef_dll/cpptoc/server_cpptoc.cc
new file mode 100644
index 0000000..fe98b85
--- /dev/null
+++ b/src/libcef_dll/cpptoc/server_cpptoc.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0a1970879ddc0bd40f5a564542ab8b11414a6b0a$
+//
+
+#include "libcef_dll/cpptoc/server_cpptoc.h"
+#include "libcef_dll/cpptoc/task_runner_cpptoc.h"
+#include "libcef_dll/ctocpp/server_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT void cef_server_create(const cef_string_t* address,
+                                  uint16 port,
+                                  int backlog,
+                                  struct _cef_server_handler_t* handler) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: address; type: string_byref_const
+  DCHECK(address);
+  if (!address)
+    return;
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler);
+  if (!handler)
+    return;
+
+  // Execute
+  CefServer::CreateServer(CefString(address), port, backlog,
+                          CefServerHandlerCToCpp::Wrap(handler));
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+struct _cef_task_runner_t* CEF_CALLBACK
+server_get_task_runner(struct _cef_server_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTaskRunner> _retval =
+      CefServerCppToC::Get(self)->GetTaskRunner();
+
+  // Return type: refptr_same
+  return CefTaskRunnerCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK server_shutdown(struct _cef_server_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefServerCppToC::Get(self)->Shutdown();
+}
+
+int CEF_CALLBACK server_is_running(struct _cef_server_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefServerCppToC::Get(self)->IsRunning();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+server_get_address(struct _cef_server_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefServerCppToC::Get(self)->GetAddress();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK server_has_connection(struct _cef_server_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefServerCppToC::Get(self)->HasConnection();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK server_is_valid_connection(struct _cef_server_t* self,
+                                            int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefServerCppToC::Get(self)->IsValidConnection(connection_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK server_send_http200response(struct _cef_server_t* self,
+                                              int connection_id,
+                                              const cef_string_t* content_type,
+                                              const void* data,
+                                              size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: content_type; type: string_byref_const
+  DCHECK(content_type);
+  if (!content_type)
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  CefServerCppToC::Get(self)->SendHttp200Response(
+      connection_id, CefString(content_type), data, data_size);
+}
+
+void CEF_CALLBACK server_send_http404response(struct _cef_server_t* self,
+                                              int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefServerCppToC::Get(self)->SendHttp404Response(connection_id);
+}
+
+void CEF_CALLBACK
+server_send_http500response(struct _cef_server_t* self,
+                            int connection_id,
+                            const cef_string_t* error_message) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: error_message; type: string_byref_const
+  DCHECK(error_message);
+  if (!error_message)
+    return;
+
+  // Execute
+  CefServerCppToC::Get(self)->SendHttp500Response(connection_id,
+                                                  CefString(error_message));
+}
+
+void CEF_CALLBACK
+server_send_http_response(struct _cef_server_t* self,
+                          int connection_id,
+                          int response_code,
+                          const cef_string_t* content_type,
+                          int64 content_length,
+                          cef_string_multimap_t extra_headers) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: content_type; type: string_byref_const
+  DCHECK(content_type);
+  if (!content_type)
+    return;
+  // Unverified params: extra_headers
+
+  // Translate param: extra_headers; type: string_map_multi_byref_const
+  std::multimap<CefString, CefString> extra_headersMultimap;
+  transfer_string_multimap_contents(extra_headers, extra_headersMultimap);
+
+  // Execute
+  CefServerCppToC::Get(self)->SendHttpResponse(
+      connection_id, response_code, CefString(content_type), content_length,
+      extra_headersMultimap);
+}
+
+void CEF_CALLBACK server_send_raw_data(struct _cef_server_t* self,
+                                       int connection_id,
+                                       const void* data,
+                                       size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  CefServerCppToC::Get(self)->SendRawData(connection_id, data, data_size);
+}
+
+void CEF_CALLBACK server_close_connection(struct _cef_server_t* self,
+                                          int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefServerCppToC::Get(self)->CloseConnection(connection_id);
+}
+
+void CEF_CALLBACK server_send_web_socket_message(struct _cef_server_t* self,
+                                                 int connection_id,
+                                                 const void* data,
+                                                 size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  CefServerCppToC::Get(self)->SendWebSocketMessage(connection_id, data,
+                                                   data_size);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefServerCppToC::CefServerCppToC() {
+  GetStruct()->get_task_runner = server_get_task_runner;
+  GetStruct()->shutdown = server_shutdown;
+  GetStruct()->is_running = server_is_running;
+  GetStruct()->get_address = server_get_address;
+  GetStruct()->has_connection = server_has_connection;
+  GetStruct()->is_valid_connection = server_is_valid_connection;
+  GetStruct()->send_http200response = server_send_http200response;
+  GetStruct()->send_http404response = server_send_http404response;
+  GetStruct()->send_http500response = server_send_http500response;
+  GetStruct()->send_http_response = server_send_http_response;
+  GetStruct()->send_raw_data = server_send_raw_data;
+  GetStruct()->close_connection = server_close_connection;
+  GetStruct()->send_web_socket_message = server_send_web_socket_message;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefServerCppToC::~CefServerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefServer>
+CefCppToCRefCounted<CefServerCppToC, CefServer, cef_server_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_server_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefServerCppToC, CefServer, cef_server_t>::
+    kWrapperType = WT_SERVER;
diff --git a/src/libcef_dll/cpptoc/server_cpptoc.h b/src/libcef_dll/cpptoc/server_cpptoc.h
new file mode 100644
index 0000000..4891bc5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/server_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=69a46f4d4a439a917ffcd68951e7cae2a8bb61c3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SERVER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SERVER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_server_capi.h"
+#include "include/cef_server.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefServerCppToC
+    : public CefCppToCRefCounted<CefServerCppToC, CefServer, cef_server_t> {
+ public:
+  CefServerCppToC();
+  virtual ~CefServerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SERVER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/server_handler_cpptoc.cc b/src/libcef_dll/cpptoc/server_handler_cpptoc.cc
new file mode 100644
index 0000000..d3442b7
--- /dev/null
+++ b/src/libcef_dll/cpptoc/server_handler_cpptoc.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=82bf8ff61f6a36118817a1130aae9656cc3032e9$
+//
+
+#include "libcef_dll/cpptoc/server_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/callback_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/server_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+server_handler_on_server_created(struct _cef_server_handler_t* self,
+                                 cef_server_t* server) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnServerCreated(
+      CefServerCToCpp::Wrap(server));
+}
+
+void CEF_CALLBACK
+server_handler_on_server_destroyed(struct _cef_server_handler_t* self,
+                                   cef_server_t* server) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnServerDestroyed(
+      CefServerCToCpp::Wrap(server));
+}
+
+void CEF_CALLBACK
+server_handler_on_client_connected(struct _cef_server_handler_t* self,
+                                   cef_server_t* server,
+                                   int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnClientConnected(
+      CefServerCToCpp::Wrap(server), connection_id);
+}
+
+void CEF_CALLBACK
+server_handler_on_client_disconnected(struct _cef_server_handler_t* self,
+                                      cef_server_t* server,
+                                      int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnClientDisconnected(
+      CefServerCToCpp::Wrap(server), connection_id);
+}
+
+void CEF_CALLBACK
+server_handler_on_http_request(struct _cef_server_handler_t* self,
+                               cef_server_t* server,
+                               int connection_id,
+                               const cef_string_t* client_address,
+                               cef_request_t* request) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+  // Verify param: client_address; type: string_byref_const
+  DCHECK(client_address);
+  if (!client_address)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnHttpRequest(
+      CefServerCToCpp::Wrap(server), connection_id, CefString(client_address),
+      CefRequestCToCpp::Wrap(request));
+}
+
+void CEF_CALLBACK
+server_handler_on_web_socket_request(struct _cef_server_handler_t* self,
+                                     cef_server_t* server,
+                                     int connection_id,
+                                     const cef_string_t* client_address,
+                                     cef_request_t* request,
+                                     cef_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+  // Verify param: client_address; type: string_byref_const
+  DCHECK(client_address);
+  if (!client_address)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnWebSocketRequest(
+      CefServerCToCpp::Wrap(server), connection_id, CefString(client_address),
+      CefRequestCToCpp::Wrap(request), CefCallbackCToCpp::Wrap(callback));
+}
+
+void CEF_CALLBACK
+server_handler_on_web_socket_connected(struct _cef_server_handler_t* self,
+                                       cef_server_t* server,
+                                       int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnWebSocketConnected(
+      CefServerCToCpp::Wrap(server), connection_id);
+}
+
+void CEF_CALLBACK
+server_handler_on_web_socket_message(struct _cef_server_handler_t* self,
+                                     cef_server_t* server,
+                                     int connection_id,
+                                     const void* data,
+                                     size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: server; type: refptr_diff
+  DCHECK(server);
+  if (!server)
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  CefServerHandlerCppToC::Get(self)->OnWebSocketMessage(
+      CefServerCToCpp::Wrap(server), connection_id, data, data_size);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefServerHandlerCppToC::CefServerHandlerCppToC() {
+  GetStruct()->on_server_created = server_handler_on_server_created;
+  GetStruct()->on_server_destroyed = server_handler_on_server_destroyed;
+  GetStruct()->on_client_connected = server_handler_on_client_connected;
+  GetStruct()->on_client_disconnected = server_handler_on_client_disconnected;
+  GetStruct()->on_http_request = server_handler_on_http_request;
+  GetStruct()->on_web_socket_request = server_handler_on_web_socket_request;
+  GetStruct()->on_web_socket_connected = server_handler_on_web_socket_connected;
+  GetStruct()->on_web_socket_message = server_handler_on_web_socket_message;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefServerHandlerCppToC::~CefServerHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefServerHandler> CefCppToCRefCounted<
+    CefServerHandlerCppToC,
+    CefServerHandler,
+    cef_server_handler_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_server_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefServerHandlerCppToC,
+                                   CefServerHandler,
+                                   cef_server_handler_t>::kWrapperType =
+    WT_SERVER_HANDLER;
diff --git a/src/libcef_dll/cpptoc/server_handler_cpptoc.h b/src/libcef_dll/cpptoc/server_handler_cpptoc.h
new file mode 100644
index 0000000..1702f46
--- /dev/null
+++ b/src/libcef_dll/cpptoc/server_handler_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7af0f6f99a4070d688fe113a1efcac821bc92e55$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SERVER_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SERVER_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_server_capi.h"
+#include "include/cef_server.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefServerHandlerCppToC
+    : public CefCppToCRefCounted<CefServerHandlerCppToC,
+                                 CefServerHandler,
+                                 cef_server_handler_t> {
+ public:
+  CefServerHandlerCppToC();
+  virtual ~CefServerHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SERVER_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/set_cookie_callback_cpptoc.cc b/src/libcef_dll/cpptoc/set_cookie_callback_cpptoc.cc
new file mode 100644
index 0000000..01cf68a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/set_cookie_callback_cpptoc.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=890a15d49f22e1050caff41e10b78e588b96e189$
+//
+
+#include "libcef_dll/cpptoc/set_cookie_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+set_cookie_callback_on_complete(struct _cef_set_cookie_callback_t* self,
+                                int success) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefSetCookieCallbackCppToC::Get(self)->OnComplete(success ? true : false);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSetCookieCallbackCppToC::CefSetCookieCallbackCppToC() {
+  GetStruct()->on_complete = set_cookie_callback_on_complete;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSetCookieCallbackCppToC::~CefSetCookieCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefSetCookieCallback> CefCppToCRefCounted<
+    CefSetCookieCallbackCppToC,
+    CefSetCookieCallback,
+    cef_set_cookie_callback_t>::UnwrapDerived(CefWrapperType type,
+                                              cef_set_cookie_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefSetCookieCallbackCppToC,
+                                   CefSetCookieCallback,
+                                   cef_set_cookie_callback_t>::kWrapperType =
+    WT_SET_COOKIE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/set_cookie_callback_cpptoc.h b/src/libcef_dll/cpptoc/set_cookie_callback_cpptoc.h
new file mode 100644
index 0000000..e4ba0eb
--- /dev/null
+++ b/src/libcef_dll/cpptoc/set_cookie_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=256712a2f53f4b334c36736169646befb12f49bb$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SET_COOKIE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SET_COOKIE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefSetCookieCallbackCppToC
+    : public CefCppToCRefCounted<CefSetCookieCallbackCppToC,
+                                 CefSetCookieCallback,
+                                 cef_set_cookie_callback_t> {
+ public:
+  CefSetCookieCallbackCppToC();
+  virtual ~CefSetCookieCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SET_COOKIE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/sslinfo_cpptoc.cc b/src/libcef_dll/cpptoc/sslinfo_cpptoc.cc
new file mode 100644
index 0000000..05cb3a1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/sslinfo_cpptoc.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=898e5c53161716e6fb4d14a28ca19ab66c7342b8$
+//
+
+#include "libcef_dll/cpptoc/sslinfo_cpptoc.h"
+#include "libcef_dll/cpptoc/x509certificate_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_cert_status_t CEF_CALLBACK
+sslinfo_get_cert_status(struct _cef_sslinfo_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CERT_STATUS_NONE;
+
+  // Execute
+  cef_cert_status_t _retval = CefSSLInfoCppToC::Get(self)->GetCertStatus();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_x509certificate_t* CEF_CALLBACK
+sslinfo_get_x509certificate(struct _cef_sslinfo_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefX509Certificate> _retval =
+      CefSSLInfoCppToC::Get(self)->GetX509Certificate();
+
+  // Return type: refptr_same
+  return CefX509CertificateCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSSLInfoCppToC::CefSSLInfoCppToC() {
+  GetStruct()->get_cert_status = sslinfo_get_cert_status;
+  GetStruct()->get_x509certificate = sslinfo_get_x509certificate;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSSLInfoCppToC::~CefSSLInfoCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefSSLInfo>
+CefCppToCRefCounted<CefSSLInfoCppToC, CefSSLInfo, cef_sslinfo_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_sslinfo_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefSSLInfoCppToC,
+                                   CefSSLInfo,
+                                   cef_sslinfo_t>::kWrapperType = WT_SSLINFO;
diff --git a/src/libcef_dll/cpptoc/sslinfo_cpptoc.h b/src/libcef_dll/cpptoc/sslinfo_cpptoc.h
new file mode 100644
index 0000000..286f014
--- /dev/null
+++ b/src/libcef_dll/cpptoc/sslinfo_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d2788a51d643ea4e96de0c6c884ebd48dff448a8$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SSLINFO_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SSLINFO_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_ssl_info_capi.h"
+#include "include/cef_ssl_info.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefSSLInfoCppToC
+    : public CefCppToCRefCounted<CefSSLInfoCppToC, CefSSLInfo, cef_sslinfo_t> {
+ public:
+  CefSSLInfoCppToC();
+  virtual ~CefSSLInfoCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SSLINFO_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/sslstatus_cpptoc.cc b/src/libcef_dll/cpptoc/sslstatus_cpptoc.cc
new file mode 100644
index 0000000..2145bf2
--- /dev/null
+++ b/src/libcef_dll/cpptoc/sslstatus_cpptoc.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cb03f04718426ea7b69d19191eeb0a23e3b62829$
+//
+
+#include "libcef_dll/cpptoc/sslstatus_cpptoc.h"
+#include "libcef_dll/cpptoc/x509certificate_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK sslstatus_is_secure_connection(struct _cef_sslstatus_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefSSLStatusCppToC::Get(self)->IsSecureConnection();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_cert_status_t CEF_CALLBACK
+sslstatus_get_cert_status(struct _cef_sslstatus_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CERT_STATUS_NONE;
+
+  // Execute
+  cef_cert_status_t _retval = CefSSLStatusCppToC::Get(self)->GetCertStatus();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_ssl_version_t CEF_CALLBACK
+sslstatus_get_sslversion(struct _cef_sslstatus_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return SSL_CONNECTION_VERSION_UNKNOWN;
+
+  // Execute
+  cef_ssl_version_t _retval = CefSSLStatusCppToC::Get(self)->GetSSLVersion();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_ssl_content_status_t CEF_CALLBACK
+sslstatus_get_content_status(struct _cef_sslstatus_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return SSL_CONTENT_NORMAL_CONTENT;
+
+  // Execute
+  cef_ssl_content_status_t _retval =
+      CefSSLStatusCppToC::Get(self)->GetContentStatus();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_x509certificate_t* CEF_CALLBACK
+sslstatus_get_x509certificate(struct _cef_sslstatus_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefX509Certificate> _retval =
+      CefSSLStatusCppToC::Get(self)->GetX509Certificate();
+
+  // Return type: refptr_same
+  return CefX509CertificateCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSSLStatusCppToC::CefSSLStatusCppToC() {
+  GetStruct()->is_secure_connection = sslstatus_is_secure_connection;
+  GetStruct()->get_cert_status = sslstatus_get_cert_status;
+  GetStruct()->get_sslversion = sslstatus_get_sslversion;
+  GetStruct()->get_content_status = sslstatus_get_content_status;
+  GetStruct()->get_x509certificate = sslstatus_get_x509certificate;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSSLStatusCppToC::~CefSSLStatusCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefSSLStatus>
+CefCppToCRefCounted<CefSSLStatusCppToC, CefSSLStatus, cef_sslstatus_t>::
+    UnwrapDerived(CefWrapperType type, cef_sslstatus_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefSSLStatusCppToC,
+                                   CefSSLStatus,
+                                   cef_sslstatus_t>::kWrapperType =
+    WT_SSLSTATUS;
diff --git a/src/libcef_dll/cpptoc/sslstatus_cpptoc.h b/src/libcef_dll/cpptoc/sslstatus_cpptoc.h
new file mode 100644
index 0000000..dcd611f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/sslstatus_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1cbc6274827a813be840e38be550b538fe2c2eee$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_SSLSTATUS_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_SSLSTATUS_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_ssl_status_capi.h"
+#include "include/cef_ssl_status.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefSSLStatusCppToC : public CefCppToCRefCounted<CefSSLStatusCppToC,
+                                                      CefSSLStatus,
+                                                      cef_sslstatus_t> {
+ public:
+  CefSSLStatusCppToC();
+  virtual ~CefSSLStatusCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_SSLSTATUS_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/stream_reader_cpptoc.cc b/src/libcef_dll/cpptoc/stream_reader_cpptoc.cc
new file mode 100644
index 0000000..b77a3dd
--- /dev/null
+++ b/src/libcef_dll/cpptoc/stream_reader_cpptoc.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=02a72f4e6ace34f8ddeec8f2b9055cc304a5dfcd$
+//
+
+#include "libcef_dll/cpptoc/stream_reader_cpptoc.h"
+#include "libcef_dll/ctocpp/read_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_stream_reader_t* cef_stream_reader_create_for_file(
+    const cef_string_t* fileName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(fileName);
+  if (!fileName)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefStreamReader> _retval =
+      CefStreamReader::CreateForFile(CefString(fileName));
+
+  // Return type: refptr_same
+  return CefStreamReaderCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_stream_reader_t* cef_stream_reader_create_for_data(void* data,
+                                                                  size_t size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefStreamReader> _retval =
+      CefStreamReader::CreateForData(data, size);
+
+  // Return type: refptr_same
+  return CefStreamReaderCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_stream_reader_t* cef_stream_reader_create_for_handler(
+    cef_read_handler_t* handler) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler);
+  if (!handler)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefStreamReader> _retval =
+      CefStreamReader::CreateForHandler(CefReadHandlerCToCpp::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefStreamReaderCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+size_t CEF_CALLBACK stream_reader_read(struct _cef_stream_reader_t* self,
+                                       void* ptr,
+                                       size_t size,
+                                       size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = CefStreamReaderCppToC::Get(self)->Read(ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK stream_reader_seek(struct _cef_stream_reader_t* self,
+                                    int64 offset,
+                                    int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefStreamReaderCppToC::Get(self)->Seek(offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+int64 CEF_CALLBACK stream_reader_tell(struct _cef_stream_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefStreamReaderCppToC::Get(self)->Tell();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK stream_reader_eof(struct _cef_stream_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefStreamReaderCppToC::Get(self)->Eof();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK stream_reader_may_block(struct _cef_stream_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefStreamReaderCppToC::Get(self)->MayBlock();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefStreamReaderCppToC::CefStreamReaderCppToC() {
+  GetStruct()->read = stream_reader_read;
+  GetStruct()->seek = stream_reader_seek;
+  GetStruct()->tell = stream_reader_tell;
+  GetStruct()->eof = stream_reader_eof;
+  GetStruct()->may_block = stream_reader_may_block;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefStreamReaderCppToC::~CefStreamReaderCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefStreamReader> CefCppToCRefCounted<
+    CefStreamReaderCppToC,
+    CefStreamReader,
+    cef_stream_reader_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_stream_reader_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefStreamReaderCppToC,
+                                   CefStreamReader,
+                                   cef_stream_reader_t>::kWrapperType =
+    WT_STREAM_READER;
diff --git a/src/libcef_dll/cpptoc/stream_reader_cpptoc.h b/src/libcef_dll/cpptoc/stream_reader_cpptoc.h
new file mode 100644
index 0000000..43920cf
--- /dev/null
+++ b/src/libcef_dll/cpptoc/stream_reader_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9268eb237a0f114dfeb98d0e9cbd144914f38a9b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_STREAM_READER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_STREAM_READER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefStreamReaderCppToC : public CefCppToCRefCounted<CefStreamReaderCppToC,
+                                                         CefStreamReader,
+                                                         cef_stream_reader_t> {
+ public:
+  CefStreamReaderCppToC();
+  virtual ~CefStreamReaderCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_STREAM_READER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/stream_writer_cpptoc.cc b/src/libcef_dll/cpptoc/stream_writer_cpptoc.cc
new file mode 100644
index 0000000..f436027
--- /dev/null
+++ b/src/libcef_dll/cpptoc/stream_writer_cpptoc.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=43e882cb3dde29f9602b3feff5fe868a9f1245a9$
+//
+
+#include "libcef_dll/cpptoc/stream_writer_cpptoc.h"
+#include "libcef_dll/ctocpp/write_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_stream_writer_t* cef_stream_writer_create_for_file(
+    const cef_string_t* fileName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(fileName);
+  if (!fileName)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefStreamWriter> _retval =
+      CefStreamWriter::CreateForFile(CefString(fileName));
+
+  // Return type: refptr_same
+  return CefStreamWriterCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_stream_writer_t* cef_stream_writer_create_for_handler(
+    cef_write_handler_t* handler) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler);
+  if (!handler)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefStreamWriter> _retval =
+      CefStreamWriter::CreateForHandler(CefWriteHandlerCToCpp::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefStreamWriterCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+size_t CEF_CALLBACK stream_writer_write(struct _cef_stream_writer_t* self,
+                                        const void* ptr,
+                                        size_t size,
+                                        size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = CefStreamWriterCppToC::Get(self)->Write(ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK stream_writer_seek(struct _cef_stream_writer_t* self,
+                                    int64 offset,
+                                    int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefStreamWriterCppToC::Get(self)->Seek(offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+int64 CEF_CALLBACK stream_writer_tell(struct _cef_stream_writer_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefStreamWriterCppToC::Get(self)->Tell();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK stream_writer_flush(struct _cef_stream_writer_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefStreamWriterCppToC::Get(self)->Flush();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK stream_writer_may_block(struct _cef_stream_writer_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefStreamWriterCppToC::Get(self)->MayBlock();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefStreamWriterCppToC::CefStreamWriterCppToC() {
+  GetStruct()->write = stream_writer_write;
+  GetStruct()->seek = stream_writer_seek;
+  GetStruct()->tell = stream_writer_tell;
+  GetStruct()->flush = stream_writer_flush;
+  GetStruct()->may_block = stream_writer_may_block;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefStreamWriterCppToC::~CefStreamWriterCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefStreamWriter> CefCppToCRefCounted<
+    CefStreamWriterCppToC,
+    CefStreamWriter,
+    cef_stream_writer_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_stream_writer_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefStreamWriterCppToC,
+                                   CefStreamWriter,
+                                   cef_stream_writer_t>::kWrapperType =
+    WT_STREAM_WRITER;
diff --git a/src/libcef_dll/cpptoc/stream_writer_cpptoc.h b/src/libcef_dll/cpptoc/stream_writer_cpptoc.h
new file mode 100644
index 0000000..c7f607d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/stream_writer_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1e8cc9fc4bad94fa2d42eb1fef1e5445f59c1033$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_STREAM_WRITER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_STREAM_WRITER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefStreamWriterCppToC : public CefCppToCRefCounted<CefStreamWriterCppToC,
+                                                         CefStreamWriter,
+                                                         cef_stream_writer_t> {
+ public:
+  CefStreamWriterCppToC();
+  virtual ~CefStreamWriterCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_STREAM_WRITER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/string_visitor_cpptoc.cc b/src/libcef_dll/cpptoc/string_visitor_cpptoc.cc
new file mode 100644
index 0000000..b469696
--- /dev/null
+++ b/src/libcef_dll/cpptoc/string_visitor_cpptoc.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=094143a0f6278a87c471719eff0ce99adb7dfca4$
+//
+
+#include "libcef_dll/cpptoc/string_visitor_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK string_visitor_visit(struct _cef_string_visitor_t* self,
+                                       const cef_string_t* string) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: string
+
+  // Execute
+  CefStringVisitorCppToC::Get(self)->Visit(CefString(string));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefStringVisitorCppToC::CefStringVisitorCppToC() {
+  GetStruct()->visit = string_visitor_visit;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefStringVisitorCppToC::~CefStringVisitorCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefStringVisitor> CefCppToCRefCounted<
+    CefStringVisitorCppToC,
+    CefStringVisitor,
+    cef_string_visitor_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_string_visitor_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefStringVisitorCppToC,
+                                   CefStringVisitor,
+                                   cef_string_visitor_t>::kWrapperType =
+    WT_STRING_VISITOR;
diff --git a/src/libcef_dll/cpptoc/string_visitor_cpptoc.h b/src/libcef_dll/cpptoc/string_visitor_cpptoc.h
new file mode 100644
index 0000000..df8234b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/string_visitor_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1d097de9a493606b05869d9e2961b1c231ff7e31$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_STRING_VISITOR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_STRING_VISITOR_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_string_visitor_capi.h"
+#include "include/cef_string_visitor.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefStringVisitorCppToC
+    : public CefCppToCRefCounted<CefStringVisitorCppToC,
+                                 CefStringVisitor,
+                                 cef_string_visitor_t> {
+ public:
+  CefStringVisitorCppToC();
+  virtual ~CefStringVisitorCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_STRING_VISITOR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/task_cpptoc.cc b/src/libcef_dll/cpptoc/task_cpptoc.cc
new file mode 100644
index 0000000..dcdf58f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/task_cpptoc.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d8c8f08bf2cc720f3eb386cc31a23cff25eb12f1$
+//
+
+#include "libcef_dll/cpptoc/task_cpptoc.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK task_execute(struct _cef_task_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTaskCppToC::Get(self)->Execute();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTaskCppToC::CefTaskCppToC() {
+  GetStruct()->execute = task_execute;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTaskCppToC::~CefTaskCppToC() {}
+
+template <>
+CefRefPtr<CefTask>
+CefCppToCRefCounted<CefTaskCppToC, CefTask, cef_task_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_task_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefTaskCppToC, CefTask, cef_task_t>::kWrapperType =
+        WT_TASK;
diff --git a/src/libcef_dll/cpptoc/task_cpptoc.h b/src/libcef_dll/cpptoc/task_cpptoc.h
new file mode 100644
index 0000000..f1b042d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/task_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=474b1264fa7822a0ab6f8cc8a2a1a0d3b4fa4039$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TASK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TASK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_task_capi.h"
+#include "include/cef_task.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTaskCppToC
+    : public CefCppToCRefCounted<CefTaskCppToC, CefTask, cef_task_t> {
+ public:
+  CefTaskCppToC();
+  virtual ~CefTaskCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TASK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/task_runner_cpptoc.cc b/src/libcef_dll/cpptoc/task_runner_cpptoc.cc
new file mode 100644
index 0000000..388d3f8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/task_runner_cpptoc.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8653934432648c7d636bc39d14a16a536352241c$
+//
+
+#include "libcef_dll/cpptoc/task_runner_cpptoc.h"
+#include "libcef_dll/ctocpp/task_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_task_runner_t* cef_task_runner_get_for_current_thread() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefTaskRunner> _retval = CefTaskRunner::GetForCurrentThread();
+
+  // Return type: refptr_same
+  return CefTaskRunnerCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_task_runner_t* cef_task_runner_get_for_thread(
+    cef_thread_id_t threadId) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefTaskRunner> _retval = CefTaskRunner::GetForThread(threadId);
+
+  // Return type: refptr_same
+  return CefTaskRunnerCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK task_runner_is_same(struct _cef_task_runner_t* self,
+                                     struct _cef_task_runner_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTaskRunnerCppToC::Get(self)->IsSame(CefTaskRunnerCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+task_runner_belongs_to_current_thread(struct _cef_task_runner_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTaskRunnerCppToC::Get(self)->BelongsToCurrentThread();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK task_runner_belongs_to_thread(struct _cef_task_runner_t* self,
+                                               cef_thread_id_t threadId) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTaskRunnerCppToC::Get(self)->BelongsToThread(threadId);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK task_runner_post_task(struct _cef_task_runner_t* self,
+                                       cef_task_t* task) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: task; type: refptr_diff
+  DCHECK(task);
+  if (!task)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTaskRunnerCppToC::Get(self)->PostTask(CefTaskCToCpp::Wrap(task));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK task_runner_post_delayed_task(struct _cef_task_runner_t* self,
+                                               cef_task_t* task,
+                                               int64 delay_ms) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: task; type: refptr_diff
+  DCHECK(task);
+  if (!task)
+    return 0;
+
+  // Execute
+  bool _retval = CefTaskRunnerCppToC::Get(self)->PostDelayedTask(
+      CefTaskCToCpp::Wrap(task), delay_ms);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTaskRunnerCppToC::CefTaskRunnerCppToC() {
+  GetStruct()->is_same = task_runner_is_same;
+  GetStruct()->belongs_to_current_thread =
+      task_runner_belongs_to_current_thread;
+  GetStruct()->belongs_to_thread = task_runner_belongs_to_thread;
+  GetStruct()->post_task = task_runner_post_task;
+  GetStruct()->post_delayed_task = task_runner_post_delayed_task;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTaskRunnerCppToC::~CefTaskRunnerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTaskRunner>
+CefCppToCRefCounted<CefTaskRunnerCppToC, CefTaskRunner, cef_task_runner_t>::
+    UnwrapDerived(CefWrapperType type, cef_task_runner_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefTaskRunnerCppToC,
+                                   CefTaskRunner,
+                                   cef_task_runner_t>::kWrapperType =
+    WT_TASK_RUNNER;
diff --git a/src/libcef_dll/cpptoc/task_runner_cpptoc.h b/src/libcef_dll/cpptoc/task_runner_cpptoc.h
new file mode 100644
index 0000000..5bcb05e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/task_runner_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cca577fe44a13eff1a329d3c2525e02b49742fc8$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TASK_RUNNER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TASK_RUNNER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_task_capi.h"
+#include "include/cef_task.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTaskRunnerCppToC : public CefCppToCRefCounted<CefTaskRunnerCppToC,
+                                                       CefTaskRunner,
+                                                       cef_task_runner_t> {
+ public:
+  CefTaskRunnerCppToC();
+  virtual ~CefTaskRunnerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TASK_RUNNER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_cpptoc.cc
new file mode 100644
index 0000000..b29eb09
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_cpptoc.cc
@@ -0,0 +1,1655 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ef822c70266564f220ec8911a3b05be5cdc18cb3$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_cpptoc.h"
+#include <algorithm>
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_translator_test_t* cef_translator_test_create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefTranslatorTest> _retval = CefTranslatorTest::Create();
+
+  // Return type: refptr_same
+  return CefTranslatorTestCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+translator_test_get_void(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestCppToC::Get(self)->GetVoid();
+}
+
+int CEF_CALLBACK translator_test_get_bool(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->GetBool();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_get_int(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->GetInt();
+
+  // Return type: simple
+  return _retval;
+}
+
+double CEF_CALLBACK
+translator_test_get_double(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  double _retval = CefTranslatorTestCppToC::Get(self)->GetDouble();
+
+  // Return type: simple
+  return _retval;
+}
+
+long CEF_CALLBACK
+translator_test_get_long(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  long _retval = CefTranslatorTestCppToC::Get(self)->GetLong();
+
+  // Return type: simple
+  return _retval;
+}
+
+size_t CEF_CALLBACK
+translator_test_get_sizet(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefTranslatorTestCppToC::Get(self)->GetSizet();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_void(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetVoid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_bool(struct _cef_translator_test_t* self,
+                                          int val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTranslatorTestCppToC::Get(self)->SetBool(val ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_int(struct _cef_translator_test_t* self,
+                                         int val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetInt(val);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_double(struct _cef_translator_test_t* self,
+                                            double val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetDouble(val);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_long(struct _cef_translator_test_t* self,
+                                          long val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetLong(val);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_sizet(struct _cef_translator_test_t* self,
+                                           size_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetSizet(val);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_set_int_list(struct _cef_translator_test_t* self,
+                             size_t valCount,
+                             int const* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: simple_vec_byref_const
+  DCHECK(valCount == 0 || val);
+  if (valCount > 0 && !val)
+    return 0;
+
+  // Translate param: val; type: simple_vec_byref_const
+  std::vector<int> valList;
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      int valVal = val[i];
+      valList.push_back(valVal);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetIntList(valList);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_get_int_list_by_ref(struct _cef_translator_test_t* self,
+                                    size_t* valCount,
+                                    int* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: simple_vec_byref
+  DCHECK(valCount && (*valCount == 0 || val));
+  if (!valCount || (*valCount > 0 && !val))
+    return 0;
+
+  // Translate param: val; type: simple_vec_byref
+  std::vector<int> valList;
+  if (valCount && *valCount > 0 && val) {
+    for (size_t i = 0; i < *valCount; ++i) {
+      valList.push_back(val[i]);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->GetIntListByRef(valList);
+
+  // Restore param: val; type: simple_vec_byref
+  if (valCount && val) {
+    *valCount = std::min(valList.size(), *valCount);
+    if (*valCount > 0) {
+      for (size_t i = 0; i < *valCount; ++i) {
+        val[i] = valList[i];
+      }
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK
+translator_test_get_int_list_size(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefTranslatorTestCppToC::Get(self)->GetIntListSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+translator_test_get_string(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefTranslatorTestCppToC::Get(self)->GetString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK translator_test_set_string(struct _cef_translator_test_t* self,
+                                            const cef_string_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: string_byref_const
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetString(CefString(val));
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+translator_test_get_string_by_ref(struct _cef_translator_test_t* self,
+                                  cef_string_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: val; type: string_byref
+  DCHECK(val);
+  if (!val)
+    return;
+
+  // Translate param: val; type: string_byref
+  CefString valStr(val);
+
+  // Execute
+  CefTranslatorTestCppToC::Get(self)->GetStringByRef(valStr);
+}
+
+int CEF_CALLBACK
+translator_test_set_string_list(struct _cef_translator_test_t* self,
+                                cef_string_list_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: string_vec_byref_const
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: string_vec_byref_const
+  std::vector<CefString> valList;
+  transfer_string_list_contents(val, valList);
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetStringList(valList);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_get_string_list_by_ref(struct _cef_translator_test_t* self,
+                                       cef_string_list_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: string_vec_byref
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: string_vec_byref
+  std::vector<CefString> valList;
+  transfer_string_list_contents(val, valList);
+
+  // Execute
+  bool _retval =
+      CefTranslatorTestCppToC::Get(self)->GetStringListByRef(valList);
+
+  // Restore param: val; type: string_vec_byref
+  cef_string_list_clear(val);
+  transfer_string_list_contents(valList, val);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_set_string_map(struct _cef_translator_test_t* self,
+                               cef_string_map_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: string_map_single_byref_const
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: string_map_single_byref_const
+  std::map<CefString, CefString> valMap;
+  transfer_string_map_contents(val, valMap);
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetStringMap(valMap);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_get_string_map_by_ref(struct _cef_translator_test_t* self,
+                                      cef_string_map_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: string_map_single_byref
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: string_map_single_byref
+  std::map<CefString, CefString> valMap;
+  transfer_string_map_contents(val, valMap);
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->GetStringMapByRef(valMap);
+
+  // Restore param: val; type: string_map_single_byref
+  cef_string_map_clear(val);
+  transfer_string_map_contents(valMap, val);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_set_string_multimap(struct _cef_translator_test_t* self,
+                                    cef_string_multimap_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: string_map_multi_byref_const
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: string_map_multi_byref_const
+  std::multimap<CefString, CefString> valMultimap;
+  transfer_string_multimap_contents(val, valMultimap);
+
+  // Execute
+  bool _retval =
+      CefTranslatorTestCppToC::Get(self)->SetStringMultimap(valMultimap);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_get_string_multimap_by_ref(struct _cef_translator_test_t* self,
+                                           cef_string_multimap_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: string_map_multi_byref
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: string_map_multi_byref
+  std::multimap<CefString, CefString> valMultimap;
+  transfer_string_multimap_contents(val, valMultimap);
+
+  // Execute
+  bool _retval =
+      CefTranslatorTestCppToC::Get(self)->GetStringMultimapByRef(valMultimap);
+
+  // Restore param: val; type: string_map_multi_byref
+  cef_string_multimap_clear(val);
+  transfer_string_multimap_contents(valMultimap, val);
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_point_t CEF_CALLBACK
+translator_test_get_point(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval = CefTranslatorTestCppToC::Get(self)->GetPoint();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_point(struct _cef_translator_test_t* self,
+                                           const cef_point_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: simple_byref_const
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: simple_byref_const
+  CefPoint valVal = val ? *val : CefPoint();
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetPoint(valVal);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+translator_test_get_point_by_ref(struct _cef_translator_test_t* self,
+                                 cef_point_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: val; type: simple_byref
+  DCHECK(val);
+  if (!val)
+    return;
+
+  // Translate param: val; type: simple_byref
+  CefPoint valVal = val ? *val : CefPoint();
+
+  // Execute
+  CefTranslatorTestCppToC::Get(self)->GetPointByRef(valVal);
+
+  // Restore param: val; type: simple_byref
+  if (val)
+    *val = valVal;
+}
+
+int CEF_CALLBACK
+translator_test_set_point_list(struct _cef_translator_test_t* self,
+                               size_t valCount,
+                               cef_point_t const* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: simple_vec_byref_const
+  DCHECK(valCount == 0 || val);
+  if (valCount > 0 && !val)
+    return 0;
+
+  // Translate param: val; type: simple_vec_byref_const
+  std::vector<CefPoint> valList;
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      CefPoint valVal = val[i];
+      valList.push_back(valVal);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetPointList(valList);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+translator_test_get_point_list_by_ref(struct _cef_translator_test_t* self,
+                                      size_t* valCount,
+                                      cef_point_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: simple_vec_byref
+  DCHECK(valCount && (*valCount == 0 || val));
+  if (!valCount || (*valCount > 0 && !val))
+    return 0;
+
+  // Translate param: val; type: simple_vec_byref
+  std::vector<CefPoint> valList;
+  if (valCount && *valCount > 0 && val) {
+    for (size_t i = 0; i < *valCount; ++i) {
+      valList.push_back(val[i]);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->GetPointListByRef(valList);
+
+  // Restore param: val; type: simple_vec_byref
+  if (valCount && val) {
+    *valCount = std::min(valList.size(), *valCount);
+    if (*valCount > 0) {
+      for (size_t i = 0; i < *valCount; ++i) {
+        val[i] = valList[i];
+      }
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK
+translator_test_get_point_list_size(struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefTranslatorTestCppToC::Get(self)->GetPointListSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_ref_ptr_library_t* CEF_CALLBACK
+translator_test_get_ref_ptr_library(struct _cef_translator_test_t* self,
+                                    int val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> _retval =
+      CefTranslatorTestCppToC::Get(self)->GetRefPtrLibrary(val);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK translator_test_set_ref_ptr_library(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_library_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetRefPtrLibrary(
+      CefTranslatorTestRefPtrLibraryCppToC::Unwrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_ref_ptr_library_t* CEF_CALLBACK
+translator_test_set_ref_ptr_library_and_return(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_library_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: refptr_same
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetRefPtrLibraryAndReturn(
+          CefTranslatorTestRefPtrLibraryCppToC::Unwrap(val));
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK translator_test_set_child_ref_ptr_library(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_library_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetChildRefPtrLibrary(
+      CefTranslatorTestRefPtrLibraryChildCppToC::Unwrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_ref_ptr_library_t* CEF_CALLBACK
+translator_test_set_child_ref_ptr_library_and_return_parent(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_library_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: refptr_same
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetChildRefPtrLibraryAndReturnParent(
+          CefTranslatorTestRefPtrLibraryChildCppToC::Unwrap(val));
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK translator_test_set_ref_ptr_library_list(
+    struct _cef_translator_test_t* self,
+    size_t valCount,
+    struct _cef_translator_test_ref_ptr_library_t* const* val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_vec_same_byref_const
+  DCHECK(valCount == 0 || val);
+  if (valCount > 0 && !val)
+    return 0;
+
+  // Translate param: val; type: refptr_vec_same_byref_const
+  std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>> valList;
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      CefRefPtr<CefTranslatorTestRefPtrLibrary> valVal =
+          CefTranslatorTestRefPtrLibraryCppToC::Unwrap(val[i]);
+      valList.push_back(valVal);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetRefPtrLibraryList(
+      valList, val1, val2);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_get_ref_ptr_library_list_by_ref(
+    struct _cef_translator_test_t* self,
+    size_t* valCount,
+    struct _cef_translator_test_ref_ptr_library_t** val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_vec_same_byref
+  DCHECK(valCount && (*valCount == 0 || val));
+  if (!valCount || (*valCount > 0 && !val))
+    return 0;
+
+  // Translate param: val; type: refptr_vec_same_byref
+  std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>> valList;
+  if (valCount && *valCount > 0 && val) {
+    for (size_t i = 0; i < *valCount; ++i) {
+      valList.push_back(CefTranslatorTestRefPtrLibraryCppToC::Unwrap(val[i]));
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->GetRefPtrLibraryListByRef(
+      valList, val1, val2);
+
+  // Restore param: val; type: refptr_vec_same_byref
+  if (valCount && val) {
+    *valCount = std::min(valList.size(), *valCount);
+    if (*valCount > 0) {
+      for (size_t i = 0; i < *valCount; ++i) {
+        val[i] = CefTranslatorTestRefPtrLibraryCppToC::Wrap(valList[i]);
+      }
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK translator_test_get_ref_ptr_library_list_size(
+    struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval =
+      CefTranslatorTestCppToC::Get(self)->GetRefPtrLibraryListSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_ref_ptr_client(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_client_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetRefPtrClient(
+      CefTranslatorTestRefPtrClientCToCpp::Wrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_ref_ptr_client_t* CEF_CALLBACK
+translator_test_set_ref_ptr_client_and_return(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_client_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: refptr_diff
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrClient> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetRefPtrClientAndReturn(
+          CefTranslatorTestRefPtrClientCToCpp::Wrap(val));
+
+  // Return type: refptr_diff
+  return CefTranslatorTestRefPtrClientCToCpp::Unwrap(_retval);
+}
+
+int CEF_CALLBACK translator_test_set_child_ref_ptr_client(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_client_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetChildRefPtrClient(
+      CefTranslatorTestRefPtrClientChildCToCpp::Wrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_ref_ptr_client_t* CEF_CALLBACK
+translator_test_set_child_ref_ptr_client_and_return_parent(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_ref_ptr_client_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: refptr_diff
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrClient> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetChildRefPtrClientAndReturnParent(
+          CefTranslatorTestRefPtrClientChildCToCpp::Wrap(val));
+
+  // Return type: refptr_diff
+  return CefTranslatorTestRefPtrClientCToCpp::Unwrap(_retval);
+}
+
+int CEF_CALLBACK translator_test_set_ref_ptr_client_list(
+    struct _cef_translator_test_t* self,
+    size_t valCount,
+    struct _cef_translator_test_ref_ptr_client_t* const* val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_vec_diff_byref_const
+  DCHECK(valCount == 0 || val);
+  if (valCount > 0 && !val)
+    return 0;
+
+  // Translate param: val; type: refptr_vec_diff_byref_const
+  std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>> valList;
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      CefRefPtr<CefTranslatorTestRefPtrClient> valVal =
+          CefTranslatorTestRefPtrClientCToCpp::Wrap(val[i]);
+      valList.push_back(valVal);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetRefPtrClientList(
+      valList, val1, val2);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_get_ref_ptr_client_list_by_ref(
+    struct _cef_translator_test_t* self,
+    size_t* valCount,
+    struct _cef_translator_test_ref_ptr_client_t** val,
+    struct _cef_translator_test_ref_ptr_client_t* val1,
+    struct _cef_translator_test_ref_ptr_client_t* val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: refptr_vec_diff_byref
+  DCHECK(valCount && (*valCount == 0 || val));
+  if (!valCount || (*valCount > 0 && !val))
+    return 0;
+  // Verify param: val1; type: refptr_diff
+  DCHECK(val1);
+  if (!val1)
+    return 0;
+  // Verify param: val2; type: refptr_diff
+  DCHECK(val2);
+  if (!val2)
+    return 0;
+
+  // Translate param: val; type: refptr_vec_diff_byref
+  std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>> valList;
+  if (valCount && *valCount > 0 && val) {
+    for (size_t i = 0; i < *valCount; ++i) {
+      valList.push_back(CefTranslatorTestRefPtrClientCToCpp::Wrap(val[i]));
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->GetRefPtrClientListByRef(
+      valList, CefTranslatorTestRefPtrClientCToCpp::Wrap(val1),
+      CefTranslatorTestRefPtrClientCToCpp::Wrap(val2));
+
+  // Restore param: val; type: refptr_vec_diff_byref
+  if (valCount && val) {
+    *valCount = std::min(valList.size(), *valCount);
+    if (*valCount > 0) {
+      for (size_t i = 0; i < *valCount; ++i) {
+        val[i] = CefTranslatorTestRefPtrClientCToCpp::Unwrap(valList[i]);
+      }
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK translator_test_get_ref_ptr_client_list_size(
+    struct _cef_translator_test_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval =
+      CefTranslatorTestCppToC::Get(self)->GetRefPtrClientListSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_scoped_library_t* CEF_CALLBACK
+translator_test_get_own_ptr_library(struct _cef_translator_test_t* self,
+                                    int val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedLibrary> _retval =
+      CefTranslatorTestCppToC::Get(self)->GetOwnPtrLibrary(val);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCppToC::WrapOwn(OWN_PASS(_retval));
+}
+
+int CEF_CALLBACK translator_test_set_own_ptr_library(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_library_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: ownptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetOwnPtrLibrary(
+      CefTranslatorTestScopedLibraryCppToC::UnwrapOwn(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_scoped_library_t* CEF_CALLBACK
+translator_test_set_own_ptr_library_and_return(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_library_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: ownptr_same
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedLibrary> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetOwnPtrLibraryAndReturn(
+          CefTranslatorTestScopedLibraryCppToC::UnwrapOwn(val));
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCppToC::WrapOwn(OWN_PASS(_retval));
+}
+
+int CEF_CALLBACK translator_test_set_child_own_ptr_library(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_library_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: ownptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetChildOwnPtrLibrary(
+      CefTranslatorTestScopedLibraryChildCppToC::UnwrapOwn(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_scoped_library_t* CEF_CALLBACK
+translator_test_set_child_own_ptr_library_and_return_parent(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_library_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: ownptr_same
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedLibrary> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetChildOwnPtrLibraryAndReturnParent(
+          CefTranslatorTestScopedLibraryChildCppToC::UnwrapOwn(val));
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCppToC::WrapOwn(OWN_PASS(_retval));
+}
+
+int CEF_CALLBACK translator_test_set_own_ptr_client(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_client_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: ownptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClient> valPtr(
+      CefTranslatorTestScopedClientCToCpp::Wrap(val));
+
+  // Execute
+  int _retval =
+      CefTranslatorTestCppToC::Get(self)->SetOwnPtrClient(OWN_PASS(valPtr));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_scoped_client_t* CEF_CALLBACK
+translator_test_set_own_ptr_client_and_return(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_client_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Translate param: val; type: ownptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClient> valPtr(
+      CefTranslatorTestScopedClientCToCpp::Wrap(val));
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedClient> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetOwnPtrClientAndReturn(
+          OWN_PASS(valPtr));
+
+  // Return type: ownptr_diff
+  return CefTranslatorTestScopedClientCToCpp::UnwrapOwn(OWN_PASS(_retval));
+}
+
+int CEF_CALLBACK translator_test_set_child_own_ptr_client(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_client_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: ownptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClientChild> valPtr(
+      CefTranslatorTestScopedClientChildCToCpp::Wrap(val));
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetChildOwnPtrClient(
+      OWN_PASS(valPtr));
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_translator_test_scoped_client_t* CEF_CALLBACK
+translator_test_set_child_own_ptr_client_and_return_parent(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_client_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val);
+  if (!val)
+    return NULL;
+
+  // Translate param: val; type: ownptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClientChild> valPtr(
+      CefTranslatorTestScopedClientChildCToCpp::Wrap(val));
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedClient> _retval =
+      CefTranslatorTestCppToC::Get(self)->SetChildOwnPtrClientAndReturnParent(
+          OWN_PASS(valPtr));
+
+  // Return type: ownptr_diff
+  return CefTranslatorTestScopedClientCToCpp::UnwrapOwn(OWN_PASS(_retval));
+}
+
+int CEF_CALLBACK translator_test_set_raw_ptr_library(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_library_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: rawptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetRawPtrLibrary(
+      CefTranslatorTestScopedLibraryCppToC::UnwrapRaw(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_child_raw_ptr_library(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_library_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: rawptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestCppToC::Get(self)->SetChildRawPtrLibrary(
+      CefTranslatorTestScopedLibraryChildCppToC::UnwrapRaw(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_raw_ptr_library_list(
+    struct _cef_translator_test_t* self,
+    size_t valCount,
+    struct _cef_translator_test_scoped_library_t* const* val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: rawptr_vec_same_byref_const
+  DCHECK(valCount == 0 || val);
+  if (valCount > 0 && !val)
+    return 0;
+
+  // Translate param: val; type: rawptr_vec_same_byref_const
+  std::vector<CefRawPtr<CefTranslatorTestScopedLibrary>> valList;
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      CefRawPtr<CefTranslatorTestScopedLibrary> valVal =
+          CefTranslatorTestScopedLibraryCppToC::UnwrapRaw(val[i]);
+      valList.push_back(valVal);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetRawPtrLibraryList(
+      valList, val1, val2);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_raw_ptr_client(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_client_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: rawptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: rawptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClient> valPtr(
+      CefTranslatorTestScopedClientCToCpp::Wrap(val));
+
+  // Execute
+  int _retval =
+      CefTranslatorTestCppToC::Get(self)->SetRawPtrClient(valPtr.get());
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_child_raw_ptr_client(
+    struct _cef_translator_test_t* self,
+    struct _cef_translator_test_scoped_client_child_t* val) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: rawptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: rawptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClientChild> valPtr(
+      CefTranslatorTestScopedClientChildCToCpp::Wrap(val));
+
+  // Execute
+  int _retval =
+      CefTranslatorTestCppToC::Get(self)->SetChildRawPtrClient(valPtr.get());
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_set_raw_ptr_client_list(
+    struct _cef_translator_test_t* self,
+    size_t valCount,
+    struct _cef_translator_test_scoped_client_t* const* val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: val; type: rawptr_vec_diff_byref_const
+  DCHECK(valCount == 0 || val);
+  if (valCount > 0 && !val)
+    return 0;
+
+  // Translate param: val; type: rawptr_vec_diff_byref_const
+  std::vector<CefRawPtr<CefTranslatorTestScopedClient>> valList;
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      CefRawPtr<CefTranslatorTestScopedClient> valVal =
+          CefTranslatorTestScopedClientCToCpp::Wrap(val[i]).release();
+      valList.push_back(valVal);
+    }
+  }
+
+  // Execute
+  bool _retval = CefTranslatorTestCppToC::Get(self)->SetRawPtrClientList(
+      valList, val1, val2);
+
+  // Restore param: val; type: rawptr_vec_diff_byref_const
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      delete valList[i];
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestCppToC::CefTranslatorTestCppToC() {
+  GetStruct()->get_void = translator_test_get_void;
+  GetStruct()->get_bool = translator_test_get_bool;
+  GetStruct()->get_int = translator_test_get_int;
+  GetStruct()->get_double = translator_test_get_double;
+  GetStruct()->get_long = translator_test_get_long;
+  GetStruct()->get_sizet = translator_test_get_sizet;
+  GetStruct()->set_void = translator_test_set_void;
+  GetStruct()->set_bool = translator_test_set_bool;
+  GetStruct()->set_int = translator_test_set_int;
+  GetStruct()->set_double = translator_test_set_double;
+  GetStruct()->set_long = translator_test_set_long;
+  GetStruct()->set_sizet = translator_test_set_sizet;
+  GetStruct()->set_int_list = translator_test_set_int_list;
+  GetStruct()->get_int_list_by_ref = translator_test_get_int_list_by_ref;
+  GetStruct()->get_int_list_size = translator_test_get_int_list_size;
+  GetStruct()->get_string = translator_test_get_string;
+  GetStruct()->set_string = translator_test_set_string;
+  GetStruct()->get_string_by_ref = translator_test_get_string_by_ref;
+  GetStruct()->set_string_list = translator_test_set_string_list;
+  GetStruct()->get_string_list_by_ref = translator_test_get_string_list_by_ref;
+  GetStruct()->set_string_map = translator_test_set_string_map;
+  GetStruct()->get_string_map_by_ref = translator_test_get_string_map_by_ref;
+  GetStruct()->set_string_multimap = translator_test_set_string_multimap;
+  GetStruct()->get_string_multimap_by_ref =
+      translator_test_get_string_multimap_by_ref;
+  GetStruct()->get_point = translator_test_get_point;
+  GetStruct()->set_point = translator_test_set_point;
+  GetStruct()->get_point_by_ref = translator_test_get_point_by_ref;
+  GetStruct()->set_point_list = translator_test_set_point_list;
+  GetStruct()->get_point_list_by_ref = translator_test_get_point_list_by_ref;
+  GetStruct()->get_point_list_size = translator_test_get_point_list_size;
+  GetStruct()->get_ref_ptr_library = translator_test_get_ref_ptr_library;
+  GetStruct()->set_ref_ptr_library = translator_test_set_ref_ptr_library;
+  GetStruct()->set_ref_ptr_library_and_return =
+      translator_test_set_ref_ptr_library_and_return;
+  GetStruct()->set_child_ref_ptr_library =
+      translator_test_set_child_ref_ptr_library;
+  GetStruct()->set_child_ref_ptr_library_and_return_parent =
+      translator_test_set_child_ref_ptr_library_and_return_parent;
+  GetStruct()->set_ref_ptr_library_list =
+      translator_test_set_ref_ptr_library_list;
+  GetStruct()->get_ref_ptr_library_list_by_ref =
+      translator_test_get_ref_ptr_library_list_by_ref;
+  GetStruct()->get_ref_ptr_library_list_size =
+      translator_test_get_ref_ptr_library_list_size;
+  GetStruct()->set_ref_ptr_client = translator_test_set_ref_ptr_client;
+  GetStruct()->set_ref_ptr_client_and_return =
+      translator_test_set_ref_ptr_client_and_return;
+  GetStruct()->set_child_ref_ptr_client =
+      translator_test_set_child_ref_ptr_client;
+  GetStruct()->set_child_ref_ptr_client_and_return_parent =
+      translator_test_set_child_ref_ptr_client_and_return_parent;
+  GetStruct()->set_ref_ptr_client_list =
+      translator_test_set_ref_ptr_client_list;
+  GetStruct()->get_ref_ptr_client_list_by_ref =
+      translator_test_get_ref_ptr_client_list_by_ref;
+  GetStruct()->get_ref_ptr_client_list_size =
+      translator_test_get_ref_ptr_client_list_size;
+  GetStruct()->get_own_ptr_library = translator_test_get_own_ptr_library;
+  GetStruct()->set_own_ptr_library = translator_test_set_own_ptr_library;
+  GetStruct()->set_own_ptr_library_and_return =
+      translator_test_set_own_ptr_library_and_return;
+  GetStruct()->set_child_own_ptr_library =
+      translator_test_set_child_own_ptr_library;
+  GetStruct()->set_child_own_ptr_library_and_return_parent =
+      translator_test_set_child_own_ptr_library_and_return_parent;
+  GetStruct()->set_own_ptr_client = translator_test_set_own_ptr_client;
+  GetStruct()->set_own_ptr_client_and_return =
+      translator_test_set_own_ptr_client_and_return;
+  GetStruct()->set_child_own_ptr_client =
+      translator_test_set_child_own_ptr_client;
+  GetStruct()->set_child_own_ptr_client_and_return_parent =
+      translator_test_set_child_own_ptr_client_and_return_parent;
+  GetStruct()->set_raw_ptr_library = translator_test_set_raw_ptr_library;
+  GetStruct()->set_child_raw_ptr_library =
+      translator_test_set_child_raw_ptr_library;
+  GetStruct()->set_raw_ptr_library_list =
+      translator_test_set_raw_ptr_library_list;
+  GetStruct()->set_raw_ptr_client = translator_test_set_raw_ptr_client;
+  GetStruct()->set_child_raw_ptr_client =
+      translator_test_set_child_raw_ptr_client;
+  GetStruct()->set_raw_ptr_client_list =
+      translator_test_set_raw_ptr_client_list;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestCppToC::~CefTranslatorTestCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTranslatorTest> CefCppToCRefCounted<
+    CefTranslatorTestCppToC,
+    CefTranslatorTest,
+    cef_translator_test_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_translator_test_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefTranslatorTestCppToC,
+                                   CefTranslatorTest,
+                                   cef_translator_test_t>::kWrapperType =
+    WT_TRANSLATOR_TEST;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_cpptoc.h
new file mode 100644
index 0000000..71cd0ea
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=553eca04097ec4d280e35f92cea280bf0a05b3f7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestCppToC
+    : public CefCppToCRefCounted<CefTranslatorTestCppToC,
+                                 CefTranslatorTest,
+                                 cef_translator_test_t> {
+ public:
+  CefTranslatorTestCppToC();
+  virtual ~CefTranslatorTestCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.cc
new file mode 100644
index 0000000..00a5a27
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bfd3c7ee0176b3e62e5c196e415a2bd62409e9b8$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_ref_ptr_client_child_get_other_value(
+    struct _cef_translator_test_ref_ptr_client_child_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestRefPtrClientChildCppToC::Get(self)->GetOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_ref_ptr_client_child_get_value(
+    struct _cef_translator_test_ref_ptr_client_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestRefPtrClientChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_ref_ptr_client_child_t*>(self))
+          ->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientChildCppToC::
+    CefTranslatorTestRefPtrClientChildCppToC() {
+  GetStruct()->get_other_value =
+      translator_test_ref_ptr_client_child_get_other_value;
+  GetStruct()->base.get_value = translator_test_ref_ptr_client_child_get_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientChildCppToC::
+    ~CefTranslatorTestRefPtrClientChildCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTranslatorTestRefPtrClientChild>
+CefCppToCRefCounted<CefTranslatorTestRefPtrClientChildCppToC,
+                    CefTranslatorTestRefPtrClientChild,
+                    cef_translator_test_ref_ptr_client_child_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_translator_test_ref_ptr_client_child_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<
+    CefTranslatorTestRefPtrClientChildCppToC,
+    CefTranslatorTestRefPtrClientChild,
+    cef_translator_test_ref_ptr_client_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.h
new file mode 100644
index 0000000..c891da7
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9003c2082a7c6a97b494e2e077d3d631a5a323ec$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestRefPtrClientChildCppToC
+    : public CefCppToCRefCounted<CefTranslatorTestRefPtrClientChildCppToC,
+                                 CefTranslatorTestRefPtrClientChild,
+                                 cef_translator_test_ref_ptr_client_child_t> {
+ public:
+  CefTranslatorTestRefPtrClientChildCppToC();
+  virtual ~CefTranslatorTestRefPtrClientChildCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.cc
new file mode 100644
index 0000000..884be99
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=83b45b70a01ab7ae99b015e0017446f623e484b6$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_ref_ptr_client_get_value(
+    struct _cef_translator_test_ref_ptr_client_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestRefPtrClientCppToC::Get(self)->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientCppToC::CefTranslatorTestRefPtrClientCppToC() {
+  GetStruct()->get_value = translator_test_ref_ptr_client_get_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientCppToC::~CefTranslatorTestRefPtrClientCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTranslatorTestRefPtrClient>
+CefCppToCRefCounted<CefTranslatorTestRefPtrClientCppToC,
+                    CefTranslatorTestRefPtrClient,
+                    cef_translator_test_ref_ptr_client_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_translator_test_ref_ptr_client_t* s) {
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD) {
+    return CefTranslatorTestRefPtrClientChildCppToC::Unwrap(
+        reinterpret_cast<cef_translator_test_ref_ptr_client_child_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefTranslatorTestRefPtrClientCppToC,
+                        CefTranslatorTestRefPtrClient,
+                        cef_translator_test_ref_ptr_client_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_REF_PTR_CLIENT;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.h
new file mode 100644
index 0000000..a195262
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=99c3402ebfd13194ae9f4be79db0e157fceebebc$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestRefPtrClientCppToC
+    : public CefCppToCRefCounted<CefTranslatorTestRefPtrClientCppToC,
+                                 CefTranslatorTestRefPtrClient,
+                                 cef_translator_test_ref_ptr_client_t> {
+ public:
+  CefTranslatorTestRefPtrClientCppToC();
+  virtual ~CefTranslatorTestRefPtrClientCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.cc
new file mode 100644
index 0000000..4069921
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.cc
@@ -0,0 +1,198 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=33192fd0739040880706fdd31b99e4bc9a191c22$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_translator_test_ref_ptr_library_child_child_t*
+cef_translator_test_ref_ptr_library_child_child_create(int value,
+                                                       int other_value,
+                                                       int other_other_value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrLibraryChildChild> _retval =
+      CefTranslatorTestRefPtrLibraryChildChild::Create(value, other_value,
+                                                       other_other_value);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryChildChildCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+translator_test_ref_ptr_library_child_child_get_other_other_value(
+    struct _cef_translator_test_ref_ptr_library_child_child_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestRefPtrLibraryChildChildCppToC::Get(self)
+                    ->GetOtherOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+translator_test_ref_ptr_library_child_child_set_other_other_value(
+    struct _cef_translator_test_ref_ptr_library_child_child_t* self,
+    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestRefPtrLibraryChildChildCppToC::Get(self)->SetOtherOtherValue(
+      value);
+}
+
+int CEF_CALLBACK translator_test_ref_ptr_library_child_child_get_other_value(
+    struct _cef_translator_test_ref_ptr_library_child_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestRefPtrLibraryChildChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_ref_ptr_library_child_child_t*>(
+              self))
+          ->GetOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_ref_ptr_library_child_child_set_other_value(
+    struct _cef_translator_test_ref_ptr_library_child_t* self,
+    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestRefPtrLibraryChildChildCppToC::Get(
+      reinterpret_cast<cef_translator_test_ref_ptr_library_child_child_t*>(
+          self))
+      ->SetOtherValue(value);
+}
+
+int CEF_CALLBACK translator_test_ref_ptr_library_child_child_get_value(
+    struct _cef_translator_test_ref_ptr_library_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestRefPtrLibraryChildChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_ref_ptr_library_child_child_t*>(
+              self))
+          ->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_ref_ptr_library_child_child_set_value(
+    struct _cef_translator_test_ref_ptr_library_t* self,
+    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestRefPtrLibraryChildChildCppToC::Get(
+      reinterpret_cast<cef_translator_test_ref_ptr_library_child_child_t*>(
+          self))
+      ->SetValue(value);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildChildCppToC::
+    CefTranslatorTestRefPtrLibraryChildChildCppToC() {
+  GetStruct()->get_other_other_value =
+      translator_test_ref_ptr_library_child_child_get_other_other_value;
+  GetStruct()->set_other_other_value =
+      translator_test_ref_ptr_library_child_child_set_other_other_value;
+  GetStruct()->base.get_other_value =
+      translator_test_ref_ptr_library_child_child_get_other_value;
+  GetStruct()->base.set_other_value =
+      translator_test_ref_ptr_library_child_child_set_other_value;
+  GetStruct()->base.base.get_value =
+      translator_test_ref_ptr_library_child_child_get_value;
+  GetStruct()->base.base.set_value =
+      translator_test_ref_ptr_library_child_child_set_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildChildCppToC::
+    ~CefTranslatorTestRefPtrLibraryChildChildCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTranslatorTestRefPtrLibraryChildChild>
+CefCppToCRefCounted<CefTranslatorTestRefPtrLibraryChildChildCppToC,
+                    CefTranslatorTestRefPtrLibraryChildChild,
+                    cef_translator_test_ref_ptr_library_child_child_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_translator_test_ref_ptr_library_child_child_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<
+    CefTranslatorTestRefPtrLibraryChildChildCppToC,
+    CefTranslatorTestRefPtrLibraryChildChild,
+    cef_translator_test_ref_ptr_library_child_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.h
new file mode 100644
index 0000000..50b37bb
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=50d8ca28ea29e5d2aa0a0eac08b3dc3582f7ff82$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestRefPtrLibraryChildChildCppToC
+    : public CefCppToCRefCounted<
+          CefTranslatorTestRefPtrLibraryChildChildCppToC,
+          CefTranslatorTestRefPtrLibraryChildChild,
+          cef_translator_test_ref_ptr_library_child_child_t> {
+ public:
+  CefTranslatorTestRefPtrLibraryChildChildCppToC();
+  virtual ~CefTranslatorTestRefPtrLibraryChildChildCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.cc
new file mode 100644
index 0000000..045926e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=82b0faf6b4c952550427001b82efaa6565a417e5$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_translator_test_ref_ptr_library_child_t*
+cef_translator_test_ref_ptr_library_child_create(int value, int other_value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrLibraryChild> _retval =
+      CefTranslatorTestRefPtrLibraryChild::Create(value, other_value);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryChildCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_ref_ptr_library_child_get_other_value(
+    struct _cef_translator_test_ref_ptr_library_child_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestRefPtrLibraryChildCppToC::Get(self)->GetOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_ref_ptr_library_child_set_other_value(
+    struct _cef_translator_test_ref_ptr_library_child_t* self,
+    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestRefPtrLibraryChildCppToC::Get(self)->SetOtherValue(value);
+}
+
+int CEF_CALLBACK translator_test_ref_ptr_library_child_get_value(
+    struct _cef_translator_test_ref_ptr_library_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestRefPtrLibraryChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_ref_ptr_library_child_t*>(self))
+          ->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_ref_ptr_library_child_set_value(
+    struct _cef_translator_test_ref_ptr_library_t* self,
+    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestRefPtrLibraryChildCppToC::Get(
+      reinterpret_cast<cef_translator_test_ref_ptr_library_child_t*>(self))
+      ->SetValue(value);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildCppToC::
+    CefTranslatorTestRefPtrLibraryChildCppToC() {
+  GetStruct()->get_other_value =
+      translator_test_ref_ptr_library_child_get_other_value;
+  GetStruct()->set_other_value =
+      translator_test_ref_ptr_library_child_set_other_value;
+  GetStruct()->base.get_value = translator_test_ref_ptr_library_child_get_value;
+  GetStruct()->base.set_value = translator_test_ref_ptr_library_child_set_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildCppToC::
+    ~CefTranslatorTestRefPtrLibraryChildCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTranslatorTestRefPtrLibraryChild>
+CefCppToCRefCounted<CefTranslatorTestRefPtrLibraryChildCppToC,
+                    CefTranslatorTestRefPtrLibraryChild,
+                    cef_translator_test_ref_ptr_library_child_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_translator_test_ref_ptr_library_child_t* s) {
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD) {
+    return CefTranslatorTestRefPtrLibraryChildChildCppToC::Unwrap(
+        reinterpret_cast<cef_translator_test_ref_ptr_library_child_child_t*>(
+            s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<
+    CefTranslatorTestRefPtrLibraryChildCppToC,
+    CefTranslatorTestRefPtrLibraryChild,
+    cef_translator_test_ref_ptr_library_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.h
new file mode 100644
index 0000000..74acb4a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f001fb9941662962bd6c9ab33ad9003ad0ee9426$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestRefPtrLibraryChildCppToC
+    : public CefCppToCRefCounted<CefTranslatorTestRefPtrLibraryChildCppToC,
+                                 CefTranslatorTestRefPtrLibraryChild,
+                                 cef_translator_test_ref_ptr_library_child_t> {
+ public:
+  CefTranslatorTestRefPtrLibraryChildCppToC();
+  virtual ~CefTranslatorTestRefPtrLibraryChildCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.cc
new file mode 100644
index 0000000..aad36d8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1a3544234150172b70642cde285bfb7cba255a28$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_library_child_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_translator_test_ref_ptr_library_t*
+cef_translator_test_ref_ptr_library_create(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> _retval =
+      CefTranslatorTestRefPtrLibrary::Create(value);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_ref_ptr_library_get_value(
+    struct _cef_translator_test_ref_ptr_library_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestRefPtrLibraryCppToC::Get(self)->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_ref_ptr_library_set_value(
+    struct _cef_translator_test_ref_ptr_library_t* self,
+    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestRefPtrLibraryCppToC::Get(self)->SetValue(value);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryCppToC::CefTranslatorTestRefPtrLibraryCppToC() {
+  GetStruct()->get_value = translator_test_ref_ptr_library_get_value;
+  GetStruct()->set_value = translator_test_ref_ptr_library_set_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryCppToC::~CefTranslatorTestRefPtrLibraryCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTranslatorTestRefPtrLibrary>
+CefCppToCRefCounted<CefTranslatorTestRefPtrLibraryCppToC,
+                    CefTranslatorTestRefPtrLibrary,
+                    cef_translator_test_ref_ptr_library_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_translator_test_ref_ptr_library_t* s) {
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD) {
+    return CefTranslatorTestRefPtrLibraryChildCppToC::Unwrap(
+        reinterpret_cast<cef_translator_test_ref_ptr_library_child_t*>(s));
+  }
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD) {
+    return CefTranslatorTestRefPtrLibraryChildChildCppToC::Unwrap(
+        reinterpret_cast<cef_translator_test_ref_ptr_library_child_child_t*>(
+            s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefTranslatorTestRefPtrLibraryCppToC,
+                        CefTranslatorTestRefPtrLibrary,
+                        cef_translator_test_ref_ptr_library_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_REF_PTR_LIBRARY;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.h
new file mode 100644
index 0000000..d01f510
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_ref_ptr_library_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c99baff2907c8d42a842732da7364c3750409690$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestRefPtrLibraryCppToC
+    : public CefCppToCRefCounted<CefTranslatorTestRefPtrLibraryCppToC,
+                                 CefTranslatorTestRefPtrLibrary,
+                                 cef_translator_test_ref_ptr_library_t> {
+ public:
+  CefTranslatorTestRefPtrLibraryCppToC();
+  virtual ~CefTranslatorTestRefPtrLibraryCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.cc
new file mode 100644
index 0000000..fff8153
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ac547adab31b6d47cfe874aded1f5d26a749b524$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_scoped_client_child_get_other_value(
+    struct _cef_translator_test_scoped_client_child_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestScopedClientChildCppToC::Get(self)->GetOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK translator_test_scoped_client_child_get_value(
+    struct _cef_translator_test_scoped_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestScopedClientChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_scoped_client_child_t*>(self))
+          ->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientChildCppToC::
+    CefTranslatorTestScopedClientChildCppToC() {
+  GetStruct()->get_other_value =
+      translator_test_scoped_client_child_get_other_value;
+  GetStruct()->base.get_value = translator_test_scoped_client_child_get_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientChildCppToC::
+    ~CefTranslatorTestScopedClientChildCppToC() {}
+
+template <>
+CefOwnPtr<CefTranslatorTestScopedClientChild>
+CefCppToCScoped<CefTranslatorTestScopedClientChildCppToC,
+                CefTranslatorTestScopedClientChild,
+                cef_translator_test_scoped_client_child_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     cef_translator_test_scoped_client_child_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return CefOwnPtr<CefTranslatorTestScopedClientChild>();
+}
+
+template <>
+CefRawPtr<CefTranslatorTestScopedClientChild>
+CefCppToCScoped<CefTranslatorTestScopedClientChildCppToC,
+                CefTranslatorTestScopedClientChild,
+                cef_translator_test_scoped_client_child_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     cef_translator_test_scoped_client_child_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCScoped<CefTranslatorTestScopedClientChildCppToC,
+                    CefTranslatorTestScopedClientChild,
+                    cef_translator_test_scoped_client_child_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.h
new file mode 100644
index 0000000..67783f5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=93db4735756895c8b3bfcb15fab1186bc2494a99$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_scoped.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestScopedClientChildCppToC
+    : public CefCppToCScoped<CefTranslatorTestScopedClientChildCppToC,
+                             CefTranslatorTestScopedClientChild,
+                             cef_translator_test_scoped_client_child_t> {
+ public:
+  CefTranslatorTestScopedClientChildCppToC();
+  virtual ~CefTranslatorTestScopedClientChildCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.cc
new file mode 100644
index 0000000..469fa3a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5058f826ad475b0930ddd77fd4029e050c27fca2$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_scoped_client_get_value(
+    struct _cef_translator_test_scoped_client_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestScopedClientCppToC::Get(self)->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientCppToC::CefTranslatorTestScopedClientCppToC() {
+  GetStruct()->get_value = translator_test_scoped_client_get_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientCppToC::~CefTranslatorTestScopedClientCppToC() {}
+
+template <>
+CefOwnPtr<CefTranslatorTestScopedClient>
+CefCppToCScoped<CefTranslatorTestScopedClientCppToC,
+                CefTranslatorTestScopedClient,
+                cef_translator_test_scoped_client_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     cef_translator_test_scoped_client_t* s) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD) {
+    return OWN_RETURN_AS(
+        CefTranslatorTestScopedClientChildCppToC::UnwrapOwn(
+            reinterpret_cast<cef_translator_test_scoped_client_child_t*>(s)),
+        CefTranslatorTestScopedClient);
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return CefOwnPtr<CefTranslatorTestScopedClient>();
+}
+
+template <>
+CefRawPtr<CefTranslatorTestScopedClient>
+CefCppToCScoped<CefTranslatorTestScopedClientCppToC,
+                CefTranslatorTestScopedClient,
+                cef_translator_test_scoped_client_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     cef_translator_test_scoped_client_t* s) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD) {
+    return CefTranslatorTestScopedClientChildCppToC::UnwrapRaw(
+        reinterpret_cast<cef_translator_test_scoped_client_child_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCScoped<CefTranslatorTestScopedClientCppToC,
+                    CefTranslatorTestScopedClient,
+                    cef_translator_test_scoped_client_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_CLIENT;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.h
new file mode 100644
index 0000000..547ba7e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1e85ddaeea70900cf89e907d8b247917c69ce693$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_scoped.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestScopedClientCppToC
+    : public CefCppToCScoped<CefTranslatorTestScopedClientCppToC,
+                             CefTranslatorTestScopedClient,
+                             cef_translator_test_scoped_client_t> {
+ public:
+  CefTranslatorTestScopedClientCppToC();
+  virtual ~CefTranslatorTestScopedClientCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.cc
new file mode 100644
index 0000000..4f29c88
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c833c2d3e1fe4bcae98906f85e6f5751c17c267a$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_translator_test_scoped_library_child_child_t*
+cef_translator_test_scoped_library_child_child_create(int value,
+                                                      int other_value,
+                                                      int other_other_value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedLibraryChildChild> _retval =
+      CefTranslatorTestScopedLibraryChildChild::Create(value, other_value,
+                                                       other_other_value);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryChildChildCppToC::WrapOwn(
+      OWN_PASS(_retval));
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+translator_test_scoped_library_child_child_get_other_other_value(
+    struct _cef_translator_test_scoped_library_child_child_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestScopedLibraryChildChildCppToC::Get(self)
+                    ->GetOtherOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+translator_test_scoped_library_child_child_set_other_other_value(
+    struct _cef_translator_test_scoped_library_child_child_t* self,
+    int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestScopedLibraryChildChildCppToC::Get(self)->SetOtherOtherValue(
+      value);
+}
+
+int CEF_CALLBACK translator_test_scoped_library_child_child_get_other_value(
+    struct _cef_translator_test_scoped_library_child_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestScopedLibraryChildChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(
+              self))
+          ->GetOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_scoped_library_child_child_set_other_value(
+    struct _cef_translator_test_scoped_library_child_t* self,
+    int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestScopedLibraryChildChildCppToC::Get(
+      reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(self))
+      ->SetOtherValue(value);
+}
+
+int CEF_CALLBACK translator_test_scoped_library_child_child_get_value(
+    struct _cef_translator_test_scoped_library_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestScopedLibraryChildChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(
+              self))
+          ->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_scoped_library_child_child_set_value(
+    struct _cef_translator_test_scoped_library_t* self,
+    int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestScopedLibraryChildChildCppToC::Get(
+      reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(self))
+      ->SetValue(value);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildChildCppToC::
+    CefTranslatorTestScopedLibraryChildChildCppToC() {
+  GetStruct()->get_other_other_value =
+      translator_test_scoped_library_child_child_get_other_other_value;
+  GetStruct()->set_other_other_value =
+      translator_test_scoped_library_child_child_set_other_other_value;
+  GetStruct()->base.get_other_value =
+      translator_test_scoped_library_child_child_get_other_value;
+  GetStruct()->base.set_other_value =
+      translator_test_scoped_library_child_child_set_other_value;
+  GetStruct()->base.base.get_value =
+      translator_test_scoped_library_child_child_get_value;
+  GetStruct()->base.base.set_value =
+      translator_test_scoped_library_child_child_set_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildChildCppToC::
+    ~CefTranslatorTestScopedLibraryChildChildCppToC() {}
+
+template <>
+CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>
+CefCppToCScoped<CefTranslatorTestScopedLibraryChildChildCppToC,
+                CefTranslatorTestScopedLibraryChildChild,
+                cef_translator_test_scoped_library_child_child_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     cef_translator_test_scoped_library_child_child_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>();
+}
+
+template <>
+CefRawPtr<CefTranslatorTestScopedLibraryChildChild>
+CefCppToCScoped<CefTranslatorTestScopedLibraryChildChildCppToC,
+                CefTranslatorTestScopedLibraryChildChild,
+                cef_translator_test_scoped_library_child_child_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     cef_translator_test_scoped_library_child_child_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCScoped<
+    CefTranslatorTestScopedLibraryChildChildCppToC,
+    CefTranslatorTestScopedLibraryChildChild,
+    cef_translator_test_scoped_library_child_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.h
new file mode 100644
index 0000000..aa55900
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=dbfcd01f7e1c8da03770348614b25330f43915eb$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_scoped.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestScopedLibraryChildChildCppToC
+    : public CefCppToCScoped<CefTranslatorTestScopedLibraryChildChildCppToC,
+                             CefTranslatorTestScopedLibraryChildChild,
+                             cef_translator_test_scoped_library_child_child_t> {
+ public:
+  CefTranslatorTestScopedLibraryChildChildCppToC();
+  virtual ~CefTranslatorTestScopedLibraryChildChildCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.cc
new file mode 100644
index 0000000..c139dba
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=71958dc400edbcd7bbdb409c7293c0b23b2a7e59$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_translator_test_scoped_library_child_t*
+cef_translator_test_scoped_library_child_create(int value, int other_value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedLibraryChild> _retval =
+      CefTranslatorTestScopedLibraryChild::Create(value, other_value);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryChildCppToC::WrapOwn(OWN_PASS(_retval));
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_scoped_library_child_get_other_value(
+    struct _cef_translator_test_scoped_library_child_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestScopedLibraryChildCppToC::Get(self)->GetOtherValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_scoped_library_child_set_other_value(
+    struct _cef_translator_test_scoped_library_child_t* self,
+    int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestScopedLibraryChildCppToC::Get(self)->SetOtherValue(value);
+}
+
+int CEF_CALLBACK translator_test_scoped_library_child_get_value(
+    struct _cef_translator_test_scoped_library_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTranslatorTestScopedLibraryChildCppToC::Get(
+          reinterpret_cast<cef_translator_test_scoped_library_child_t*>(self))
+          ->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_scoped_library_child_set_value(
+    struct _cef_translator_test_scoped_library_t* self,
+    int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestScopedLibraryChildCppToC::Get(
+      reinterpret_cast<cef_translator_test_scoped_library_child_t*>(self))
+      ->SetValue(value);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildCppToC::
+    CefTranslatorTestScopedLibraryChildCppToC() {
+  GetStruct()->get_other_value =
+      translator_test_scoped_library_child_get_other_value;
+  GetStruct()->set_other_value =
+      translator_test_scoped_library_child_set_other_value;
+  GetStruct()->base.get_value = translator_test_scoped_library_child_get_value;
+  GetStruct()->base.set_value = translator_test_scoped_library_child_set_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildCppToC::
+    ~CefTranslatorTestScopedLibraryChildCppToC() {}
+
+template <>
+CefOwnPtr<CefTranslatorTestScopedLibraryChild>
+CefCppToCScoped<CefTranslatorTestScopedLibraryChildCppToC,
+                CefTranslatorTestScopedLibraryChild,
+                cef_translator_test_scoped_library_child_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     cef_translator_test_scoped_library_child_t* s) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return OWN_RETURN_AS(
+        CefTranslatorTestScopedLibraryChildChildCppToC::UnwrapOwn(
+            reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(
+                s)),
+        CefTranslatorTestScopedLibraryChild);
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return CefOwnPtr<CefTranslatorTestScopedLibraryChild>();
+}
+
+template <>
+CefRawPtr<CefTranslatorTestScopedLibraryChild>
+CefCppToCScoped<CefTranslatorTestScopedLibraryChildCppToC,
+                CefTranslatorTestScopedLibraryChild,
+                cef_translator_test_scoped_library_child_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     cef_translator_test_scoped_library_child_t* s) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return CefTranslatorTestScopedLibraryChildChildCppToC::UnwrapRaw(
+        reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCScoped<CefTranslatorTestScopedLibraryChildCppToC,
+                    CefTranslatorTestScopedLibraryChild,
+                    cef_translator_test_scoped_library_child_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.h
new file mode 100644
index 0000000..c04b32f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0bf426bddd50da6814967740ea3af864c9f10f8d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_scoped.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestScopedLibraryChildCppToC
+    : public CefCppToCScoped<CefTranslatorTestScopedLibraryChildCppToC,
+                             CefTranslatorTestScopedLibraryChild,
+                             cef_translator_test_scoped_library_child_t> {
+ public:
+  CefTranslatorTestScopedLibraryChildCppToC();
+  virtual ~CefTranslatorTestScopedLibraryChildCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.cc b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.cc
new file mode 100644
index 0000000..3e64e82
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=aaf55c2bc6deb407e8c2822726250c3a8dd06a37$
+//
+
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_child_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_library_child_cpptoc.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_translator_test_scoped_library_t*
+cef_translator_test_scoped_library_create(int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefOwnPtr<CefTranslatorTestScopedLibrary> _retval =
+      CefTranslatorTestScopedLibrary::Create(value);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCppToC::WrapOwn(OWN_PASS(_retval));
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK translator_test_scoped_library_get_value(
+    struct _cef_translator_test_scoped_library_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefTranslatorTestScopedLibraryCppToC::Get(self)->GetValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK translator_test_scoped_library_set_value(
+    struct _cef_translator_test_scoped_library_t* self,
+    int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTranslatorTestScopedLibraryCppToC::Get(self)->SetValue(value);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryCppToC::CefTranslatorTestScopedLibraryCppToC() {
+  GetStruct()->get_value = translator_test_scoped_library_get_value;
+  GetStruct()->set_value = translator_test_scoped_library_set_value;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryCppToC::~CefTranslatorTestScopedLibraryCppToC() {}
+
+template <>
+CefOwnPtr<CefTranslatorTestScopedLibrary>
+CefCppToCScoped<CefTranslatorTestScopedLibraryCppToC,
+                CefTranslatorTestScopedLibrary,
+                cef_translator_test_scoped_library_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     cef_translator_test_scoped_library_t* s) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD) {
+    return OWN_RETURN_AS(
+        CefTranslatorTestScopedLibraryChildCppToC::UnwrapOwn(
+            reinterpret_cast<cef_translator_test_scoped_library_child_t*>(s)),
+        CefTranslatorTestScopedLibrary);
+  }
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return OWN_RETURN_AS(
+        CefTranslatorTestScopedLibraryChildChildCppToC::UnwrapOwn(
+            reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(
+                s)),
+        CefTranslatorTestScopedLibrary);
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return CefOwnPtr<CefTranslatorTestScopedLibrary>();
+}
+
+template <>
+CefRawPtr<CefTranslatorTestScopedLibrary>
+CefCppToCScoped<CefTranslatorTestScopedLibraryCppToC,
+                CefTranslatorTestScopedLibrary,
+                cef_translator_test_scoped_library_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     cef_translator_test_scoped_library_t* s) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD) {
+    return CefTranslatorTestScopedLibraryChildCppToC::UnwrapRaw(
+        reinterpret_cast<cef_translator_test_scoped_library_child_t*>(s));
+  }
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return CefTranslatorTestScopedLibraryChildChildCppToC::UnwrapRaw(
+        reinterpret_cast<cef_translator_test_scoped_library_child_child_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCScoped<CefTranslatorTestScopedLibraryCppToC,
+                    CefTranslatorTestScopedLibrary,
+                    cef_translator_test_scoped_library_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_LIBRARY;
diff --git a/src/libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.h b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.h
new file mode 100644
index 0000000..5aed9dd
--- /dev/null
+++ b/src/libcef_dll/cpptoc/test/translator_test_scoped_library_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=167132fbef27711ddc4e15003e6ec9af08cb4e9c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/cpptoc/cpptoc_scoped.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestScopedLibraryCppToC
+    : public CefCppToCScoped<CefTranslatorTestScopedLibraryCppToC,
+                             CefTranslatorTestScopedLibrary,
+                             cef_translator_test_scoped_library_t> {
+ public:
+  CefTranslatorTestScopedLibraryCppToC();
+  virtual ~CefTranslatorTestScopedLibraryCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/thread_cpptoc.cc b/src/libcef_dll/cpptoc/thread_cpptoc.cc
new file mode 100644
index 0000000..749b573
--- /dev/null
+++ b/src/libcef_dll/cpptoc/thread_cpptoc.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f417535f2fb1f11f13efb5b5dfedf87f1062b70e$
+//
+
+#include "libcef_dll/cpptoc/thread_cpptoc.h"
+#include "libcef_dll/cpptoc/task_runner_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_thread_t* cef_thread_create(
+    const cef_string_t* display_name,
+    cef_thread_priority_t priority,
+    cef_message_loop_type_t message_loop_type,
+    int stoppable,
+    cef_com_init_mode_t com_init_mode) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: display_name
+
+  // Execute
+  CefRefPtr<CefThread> _retval = CefThread::CreateThread(
+      CefString(display_name), priority, message_loop_type,
+      stoppable ? true : false, com_init_mode);
+
+  // Return type: refptr_same
+  return CefThreadCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_task_runner_t* CEF_CALLBACK
+thread_get_task_runner(struct _cef_thread_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTaskRunner> _retval =
+      CefThreadCppToC::Get(self)->GetTaskRunner();
+
+  // Return type: refptr_same
+  return CefTaskRunnerCppToC::Wrap(_retval);
+}
+
+cef_platform_thread_id_t CEF_CALLBACK
+thread_get_platform_thread_id(struct _cef_thread_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return kInvalidPlatformThreadId;
+
+  // Execute
+  cef_platform_thread_id_t _retval =
+      CefThreadCppToC::Get(self)->GetPlatformThreadId();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK thread_stop(struct _cef_thread_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefThreadCppToC::Get(self)->Stop();
+}
+
+int CEF_CALLBACK thread_is_running(struct _cef_thread_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefThreadCppToC::Get(self)->IsRunning();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefThreadCppToC::CefThreadCppToC() {
+  GetStruct()->get_task_runner = thread_get_task_runner;
+  GetStruct()->get_platform_thread_id = thread_get_platform_thread_id;
+  GetStruct()->stop = thread_stop;
+  GetStruct()->is_running = thread_is_running;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefThreadCppToC::~CefThreadCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefThread>
+CefCppToCRefCounted<CefThreadCppToC, CefThread, cef_thread_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_thread_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefThreadCppToC, CefThread, cef_thread_t>::
+    kWrapperType = WT_THREAD;
diff --git a/src/libcef_dll/cpptoc/thread_cpptoc.h b/src/libcef_dll/cpptoc/thread_cpptoc.h
new file mode 100644
index 0000000..2a644d3
--- /dev/null
+++ b/src/libcef_dll/cpptoc/thread_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8a39bcb93a93bb8b7c8e8794847e82b3a4c13053$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_THREAD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_THREAD_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_thread_capi.h"
+#include "include/cef_thread.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefThreadCppToC
+    : public CefCppToCRefCounted<CefThreadCppToC, CefThread, cef_thread_t> {
+ public:
+  CefThreadCppToC();
+  virtual ~CefThreadCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_THREAD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/urlrequest_client_cpptoc.cc b/src/libcef_dll/cpptoc/urlrequest_client_cpptoc.cc
new file mode 100644
index 0000000..12954be
--- /dev/null
+++ b/src/libcef_dll/cpptoc/urlrequest_client_cpptoc.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ba49a4bef9c1ae2b5380fe7ef72313d0434e08a1$
+//
+
+#include "libcef_dll/cpptoc/urlrequest_client_cpptoc.h"
+#include "libcef_dll/ctocpp/auth_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/urlrequest_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+urlrequest_client_on_request_complete(struct _cef_urlrequest_client_t* self,
+                                      cef_urlrequest_t* request) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+
+  // Execute
+  CefURLRequestClientCppToC::Get(self)->OnRequestComplete(
+      CefURLRequestCToCpp::Wrap(request));
+}
+
+void CEF_CALLBACK
+urlrequest_client_on_upload_progress(struct _cef_urlrequest_client_t* self,
+                                     cef_urlrequest_t* request,
+                                     int64 current,
+                                     int64 total) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+
+  // Execute
+  CefURLRequestClientCppToC::Get(self)->OnUploadProgress(
+      CefURLRequestCToCpp::Wrap(request), current, total);
+}
+
+void CEF_CALLBACK
+urlrequest_client_on_download_progress(struct _cef_urlrequest_client_t* self,
+                                       cef_urlrequest_t* request,
+                                       int64 current,
+                                       int64 total) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+
+  // Execute
+  CefURLRequestClientCppToC::Get(self)->OnDownloadProgress(
+      CefURLRequestCToCpp::Wrap(request), current, total);
+}
+
+void CEF_CALLBACK
+urlrequest_client_on_download_data(struct _cef_urlrequest_client_t* self,
+                                   cef_urlrequest_t* request,
+                                   const void* data,
+                                   size_t data_length) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request);
+  if (!request)
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  CefURLRequestClientCppToC::Get(self)->OnDownloadData(
+      CefURLRequestCToCpp::Wrap(request), data, data_length);
+}
+
+int CEF_CALLBACK
+urlrequest_client_get_auth_credentials(struct _cef_urlrequest_client_t* self,
+                                       int isProxy,
+                                       const cef_string_t* host,
+                                       int port,
+                                       const cef_string_t* realm,
+                                       const cef_string_t* scheme,
+                                       cef_auth_callback_t* callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: host; type: string_byref_const
+  DCHECK(host);
+  if (!host)
+    return 0;
+  // Verify param: scheme; type: string_byref_const
+  DCHECK(scheme);
+  if (!scheme)
+    return 0;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return 0;
+  // Unverified params: realm
+
+  // Execute
+  bool _retval = CefURLRequestClientCppToC::Get(self)->GetAuthCredentials(
+      isProxy ? true : false, CefString(host), port, CefString(realm),
+      CefString(scheme), CefAuthCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefURLRequestClientCppToC::CefURLRequestClientCppToC() {
+  GetStruct()->on_request_complete = urlrequest_client_on_request_complete;
+  GetStruct()->on_upload_progress = urlrequest_client_on_upload_progress;
+  GetStruct()->on_download_progress = urlrequest_client_on_download_progress;
+  GetStruct()->on_download_data = urlrequest_client_on_download_data;
+  GetStruct()->get_auth_credentials = urlrequest_client_get_auth_credentials;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefURLRequestClientCppToC::~CefURLRequestClientCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefURLRequestClient> CefCppToCRefCounted<
+    CefURLRequestClientCppToC,
+    CefURLRequestClient,
+    cef_urlrequest_client_t>::UnwrapDerived(CefWrapperType type,
+                                            cef_urlrequest_client_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefURLRequestClientCppToC,
+                                   CefURLRequestClient,
+                                   cef_urlrequest_client_t>::kWrapperType =
+    WT_URLREQUEST_CLIENT;
diff --git a/src/libcef_dll/cpptoc/urlrequest_client_cpptoc.h b/src/libcef_dll/cpptoc/urlrequest_client_cpptoc.h
new file mode 100644
index 0000000..02c93de
--- /dev/null
+++ b/src/libcef_dll/cpptoc/urlrequest_client_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d78449c8011aa41c46a61bbf177ef09263f28b6c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_URLREQUEST_CLIENT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_URLREQUEST_CLIENT_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_urlrequest_capi.h"
+#include "include/cef_urlrequest.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefURLRequestClientCppToC
+    : public CefCppToCRefCounted<CefURLRequestClientCppToC,
+                                 CefURLRequestClient,
+                                 cef_urlrequest_client_t> {
+ public:
+  CefURLRequestClientCppToC();
+  virtual ~CefURLRequestClientCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_URLREQUEST_CLIENT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/urlrequest_cpptoc.cc b/src/libcef_dll/cpptoc/urlrequest_cpptoc.cc
new file mode 100644
index 0000000..c352aff
--- /dev/null
+++ b/src/libcef_dll/cpptoc/urlrequest_cpptoc.cc
@@ -0,0 +1,206 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=81e91e1d45ee86a325f375002d82241a381fde91$
+//
+
+#include "libcef_dll/cpptoc/urlrequest_cpptoc.h"
+#include "libcef_dll/cpptoc/request_context_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/response_cpptoc.h"
+#include "libcef_dll/ctocpp/urlrequest_client_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_urlrequest_t* cef_urlrequest_create(
+    cef_request_t* request,
+    struct _cef_urlrequest_client_t* client,
+    cef_request_context_t* request_context) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_same
+  DCHECK(request);
+  if (!request)
+    return NULL;
+  // Verify param: client; type: refptr_diff
+  DCHECK(client);
+  if (!client)
+    return NULL;
+  // Unverified params: request_context
+
+  // Execute
+  CefRefPtr<CefURLRequest> _retval =
+      CefURLRequest::Create(CefRequestCppToC::Unwrap(request),
+                            CefURLRequestClientCToCpp::Wrap(client),
+                            CefRequestContextCppToC::Unwrap(request_context));
+
+  // Return type: refptr_same
+  return CefURLRequestCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_request_t* CEF_CALLBACK
+urlrequest_get_request(struct _cef_urlrequest_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefRequest> _retval = CefURLRequestCppToC::Get(self)->GetRequest();
+
+  // Return type: refptr_same
+  return CefRequestCppToC::Wrap(_retval);
+}
+
+struct _cef_urlrequest_client_t* CEF_CALLBACK
+urlrequest_get_client(struct _cef_urlrequest_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefURLRequestClient> _retval =
+      CefURLRequestCppToC::Get(self)->GetClient();
+
+  // Return type: refptr_diff
+  return CefURLRequestClientCToCpp::Unwrap(_retval);
+}
+
+cef_urlrequest_status_t CEF_CALLBACK
+urlrequest_get_request_status(struct _cef_urlrequest_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return UR_UNKNOWN;
+
+  // Execute
+  cef_urlrequest_status_t _retval =
+      CefURLRequestCppToC::Get(self)->GetRequestStatus();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_errorcode_t CEF_CALLBACK
+urlrequest_get_request_error(struct _cef_urlrequest_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return ERR_NONE;
+
+  // Execute
+  cef_errorcode_t _retval = CefURLRequestCppToC::Get(self)->GetRequestError();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_response_t* CEF_CALLBACK
+urlrequest_get_response(struct _cef_urlrequest_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefResponse> _retval =
+      CefURLRequestCppToC::Get(self)->GetResponse();
+
+  // Return type: refptr_same
+  return CefResponseCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK
+urlrequest_response_was_cached(struct _cef_urlrequest_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefURLRequestCppToC::Get(self)->ResponseWasCached();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK urlrequest_cancel(struct _cef_urlrequest_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefURLRequestCppToC::Get(self)->Cancel();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefURLRequestCppToC::CefURLRequestCppToC() {
+  GetStruct()->get_request = urlrequest_get_request;
+  GetStruct()->get_client = urlrequest_get_client;
+  GetStruct()->get_request_status = urlrequest_get_request_status;
+  GetStruct()->get_request_error = urlrequest_get_request_error;
+  GetStruct()->get_response = urlrequest_get_response;
+  GetStruct()->response_was_cached = urlrequest_response_was_cached;
+  GetStruct()->cancel = urlrequest_cancel;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefURLRequestCppToC::~CefURLRequestCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefURLRequest>
+CefCppToCRefCounted<CefURLRequestCppToC, CefURLRequest, cef_urlrequest_t>::
+    UnwrapDerived(CefWrapperType type, cef_urlrequest_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefURLRequestCppToC,
+                                   CefURLRequest,
+                                   cef_urlrequest_t>::kWrapperType =
+    WT_URLREQUEST;
diff --git a/src/libcef_dll/cpptoc/urlrequest_cpptoc.h b/src/libcef_dll/cpptoc/urlrequest_cpptoc.h
new file mode 100644
index 0000000..d6fba5d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/urlrequest_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0cb270bce0e74831fb24a5aafbde34e460464e63$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_URLREQUEST_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_URLREQUEST_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_urlrequest_capi.h"
+#include "include/cef_urlrequest.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefURLRequestCppToC : public CefCppToCRefCounted<CefURLRequestCppToC,
+                                                       CefURLRequest,
+                                                       cef_urlrequest_t> {
+ public:
+  CefURLRequestCppToC();
+  virtual ~CefURLRequestCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_URLREQUEST_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8accessor_cpptoc.cc b/src/libcef_dll/cpptoc/v8accessor_cpptoc.cc
new file mode 100644
index 0000000..6528463
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8accessor_cpptoc.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=21cc15ad9b1f176e4c399cc74fcc459b32d38fee$
+//
+
+#include "libcef_dll/cpptoc/v8accessor_cpptoc.h"
+#include "libcef_dll/ctocpp/v8value_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK v8accessor_get(struct _cef_v8accessor_t* self,
+                                const cef_string_t* name,
+                                struct _cef_v8value_t* object,
+                                struct _cef_v8value_t** retval,
+                                cef_string_t* exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object);
+  if (!object)
+    return 0;
+  // Verify param: retval; type: refptr_diff_byref
+  DCHECK(retval);
+  if (!retval)
+    return 0;
+  // Verify param: exception; type: string_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+
+  // Translate param: retval; type: refptr_diff_byref
+  CefRefPtr<CefV8Value> retvalPtr;
+  if (retval && *retval)
+    retvalPtr = CefV8ValueCToCpp::Wrap(*retval);
+  CefV8Value* retvalOrig = retvalPtr.get();
+  // Translate param: exception; type: string_byref
+  CefString exceptionStr(exception);
+
+  // Execute
+  bool _retval = CefV8AccessorCppToC::Get(self)->Get(
+      CefString(name), CefV8ValueCToCpp::Wrap(object), retvalPtr, exceptionStr);
+
+  // Restore param: retval; type: refptr_diff_byref
+  if (retval) {
+    if (retvalPtr.get()) {
+      if (retvalPtr.get() != retvalOrig) {
+        *retval = CefV8ValueCToCpp::Unwrap(retvalPtr);
+      }
+    } else {
+      *retval = nullptr;
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8accessor_set(struct _cef_v8accessor_t* self,
+                                const cef_string_t* name,
+                                struct _cef_v8value_t* object,
+                                struct _cef_v8value_t* value,
+                                cef_string_t* exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object);
+  if (!object)
+    return 0;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value);
+  if (!value)
+    return 0;
+  // Verify param: exception; type: string_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+
+  // Translate param: exception; type: string_byref
+  CefString exceptionStr(exception);
+
+  // Execute
+  bool _retval = CefV8AccessorCppToC::Get(self)->Set(
+      CefString(name), CefV8ValueCToCpp::Wrap(object),
+      CefV8ValueCToCpp::Wrap(value), exceptionStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8AccessorCppToC::CefV8AccessorCppToC() {
+  GetStruct()->get = v8accessor_get;
+  GetStruct()->set = v8accessor_set;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8AccessorCppToC::~CefV8AccessorCppToC() {}
+
+template <>
+CefRefPtr<CefV8Accessor>
+CefCppToCRefCounted<CefV8AccessorCppToC, CefV8Accessor, cef_v8accessor_t>::
+    UnwrapDerived(CefWrapperType type, cef_v8accessor_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8AccessorCppToC,
+                                   CefV8Accessor,
+                                   cef_v8accessor_t>::kWrapperType =
+    WT_V8ACCESSOR;
diff --git a/src/libcef_dll/cpptoc/v8accessor_cpptoc.h b/src/libcef_dll/cpptoc/v8accessor_cpptoc.h
new file mode 100644
index 0000000..9cbc10a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8accessor_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e399801d4c1cf8c9dc9ca7800f26127b9006c207$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8ACCESSOR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8ACCESSOR_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8AccessorCppToC : public CefCppToCRefCounted<CefV8AccessorCppToC,
+                                                       CefV8Accessor,
+                                                       cef_v8accessor_t> {
+ public:
+  CefV8AccessorCppToC();
+  virtual ~CefV8AccessorCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8ACCESSOR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.cc b/src/libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.cc
new file mode 100644
index 0000000..dd31275
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4c83d1414180cb0e03443f76eb4a49472c532a86$
+//
+
+#include "libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK v8array_buffer_release_callback_release_buffer(
+    struct _cef_v8array_buffer_release_callback_t* self,
+    void* buffer) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return;
+
+  // Execute
+  CefV8ArrayBufferReleaseCallbackCppToC::Get(self)->ReleaseBuffer(buffer);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ArrayBufferReleaseCallbackCppToC::CefV8ArrayBufferReleaseCallbackCppToC() {
+  GetStruct()->release_buffer = v8array_buffer_release_callback_release_buffer;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ArrayBufferReleaseCallbackCppToC::
+    ~CefV8ArrayBufferReleaseCallbackCppToC() {}
+
+template <>
+CefRefPtr<CefV8ArrayBufferReleaseCallback>
+CefCppToCRefCounted<CefV8ArrayBufferReleaseCallbackCppToC,
+                    CefV8ArrayBufferReleaseCallback,
+                    cef_v8array_buffer_release_callback_t>::
+    UnwrapDerived(CefWrapperType type,
+                  cef_v8array_buffer_release_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefV8ArrayBufferReleaseCallbackCppToC,
+                        CefV8ArrayBufferReleaseCallback,
+                        cef_v8array_buffer_release_callback_t>::kWrapperType =
+        WT_V8ARRAY_BUFFER_RELEASE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.h b/src/libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.h
new file mode 100644
index 0000000..4243dfb
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=13f0f100ae8f6fc66b8a387e35d78ad3bbf74b2f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8ARRAY_BUFFER_RELEASE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8ARRAY_BUFFER_RELEASE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8ArrayBufferReleaseCallbackCppToC
+    : public CefCppToCRefCounted<CefV8ArrayBufferReleaseCallbackCppToC,
+                                 CefV8ArrayBufferReleaseCallback,
+                                 cef_v8array_buffer_release_callback_t> {
+ public:
+  CefV8ArrayBufferReleaseCallbackCppToC();
+  virtual ~CefV8ArrayBufferReleaseCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8ARRAY_BUFFER_RELEASE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8context_cpptoc.cc b/src/libcef_dll/cpptoc/v8context_cpptoc.cc
new file mode 100644
index 0000000..5c49980
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8context_cpptoc.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=982d6b3a8201bbd3d32070c3ca522d849bc2d1f0$
+//
+
+#include "libcef_dll/cpptoc/v8context_cpptoc.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/task_runner_cpptoc.h"
+#include "libcef_dll/cpptoc/v8exception_cpptoc.h"
+#include "libcef_dll/cpptoc/v8value_cpptoc.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_v8context_t* cef_v8context_get_current_context() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Context> _retval = CefV8Context::GetCurrentContext();
+
+  // Return type: refptr_same
+  return CefV8ContextCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8context_t* cef_v8context_get_entered_context() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Context> _retval = CefV8Context::GetEnteredContext();
+
+  // Return type: refptr_same
+  return CefV8ContextCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT int cef_v8context_in_context() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  bool _retval = CefV8Context::InContext();
+
+  // Return type: bool
+  return _retval;
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_task_runner_t* CEF_CALLBACK
+v8context_get_task_runner(struct _cef_v8context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTaskRunner> _retval =
+      CefV8ContextCppToC::Get(self)->GetTaskRunner();
+
+  // Return type: refptr_same
+  return CefTaskRunnerCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK v8context_is_valid(struct _cef_v8context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ContextCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_browser_t* CEF_CALLBACK
+v8context_get_browser(struct _cef_v8context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowser> _retval = CefV8ContextCppToC::Get(self)->GetBrowser();
+
+  // Return type: refptr_same
+  return CefBrowserCppToC::Wrap(_retval);
+}
+
+cef_frame_t* CEF_CALLBACK v8context_get_frame(struct _cef_v8context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFrame> _retval = CefV8ContextCppToC::Get(self)->GetFrame();
+
+  // Return type: refptr_same
+  return CefFrameCppToC::Wrap(_retval);
+}
+
+struct _cef_v8value_t* CEF_CALLBACK
+v8context_get_global(struct _cef_v8context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8ContextCppToC::Get(self)->GetGlobal();
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK v8context_enter(struct _cef_v8context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ContextCppToC::Get(self)->Enter();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8context_exit(struct _cef_v8context_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ContextCppToC::Get(self)->Exit();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8context_is_same(struct _cef_v8context_t* self,
+                                   struct _cef_v8context_t* that) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefV8ContextCppToC::Get(self)->IsSame(CefV8ContextCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self,
+                                const cef_string_t* code,
+                                const cef_string_t* script_url,
+                                int start_line,
+                                struct _cef_v8value_t** retval,
+                                struct _cef_v8exception_t** exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: code; type: string_byref_const
+  DCHECK(code);
+  if (!code)
+    return 0;
+  // Verify param: retval; type: refptr_same_byref
+  DCHECK(retval);
+  if (!retval)
+    return 0;
+  // Verify param: exception; type: refptr_same_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+  // Unverified params: script_url
+
+  // Translate param: retval; type: refptr_same_byref
+  CefRefPtr<CefV8Value> retvalPtr;
+  if (retval && *retval)
+    retvalPtr = CefV8ValueCppToC::Unwrap(*retval);
+  CefV8Value* retvalOrig = retvalPtr.get();
+  // Translate param: exception; type: refptr_same_byref
+  CefRefPtr<CefV8Exception> exceptionPtr;
+  if (exception && *exception)
+    exceptionPtr = CefV8ExceptionCppToC::Unwrap(*exception);
+  CefV8Exception* exceptionOrig = exceptionPtr.get();
+
+  // Execute
+  bool _retval = CefV8ContextCppToC::Get(self)->Eval(
+      CefString(code), CefString(script_url), start_line, retvalPtr,
+      exceptionPtr);
+
+  // Restore param: retval; type: refptr_same_byref
+  if (retval) {
+    if (retvalPtr.get()) {
+      if (retvalPtr.get() != retvalOrig) {
+        *retval = CefV8ValueCppToC::Wrap(retvalPtr);
+      }
+    } else {
+      *retval = nullptr;
+    }
+  }
+  // Restore param: exception; type: refptr_same_byref
+  if (exception) {
+    if (exceptionPtr.get()) {
+      if (exceptionPtr.get() != exceptionOrig) {
+        *exception = CefV8ExceptionCppToC::Wrap(exceptionPtr);
+      }
+    } else {
+      *exception = nullptr;
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ContextCppToC::CefV8ContextCppToC() {
+  GetStruct()->get_task_runner = v8context_get_task_runner;
+  GetStruct()->is_valid = v8context_is_valid;
+  GetStruct()->get_browser = v8context_get_browser;
+  GetStruct()->get_frame = v8context_get_frame;
+  GetStruct()->get_global = v8context_get_global;
+  GetStruct()->enter = v8context_enter;
+  GetStruct()->exit = v8context_exit;
+  GetStruct()->is_same = v8context_is_same;
+  GetStruct()->eval = v8context_eval;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ContextCppToC::~CefV8ContextCppToC() {}
+
+template <>
+CefRefPtr<CefV8Context>
+CefCppToCRefCounted<CefV8ContextCppToC, CefV8Context, cef_v8context_t>::
+    UnwrapDerived(CefWrapperType type, cef_v8context_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8ContextCppToC,
+                                   CefV8Context,
+                                   cef_v8context_t>::kWrapperType =
+    WT_V8CONTEXT;
diff --git a/src/libcef_dll/cpptoc/v8context_cpptoc.h b/src/libcef_dll/cpptoc/v8context_cpptoc.h
new file mode 100644
index 0000000..20c6ece
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8context_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f381dc42f5cfceb2de87f58518dfeb2a8163a502$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8CONTEXT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8CONTEXT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8ContextCppToC : public CefCppToCRefCounted<CefV8ContextCppToC,
+                                                      CefV8Context,
+                                                      cef_v8context_t> {
+ public:
+  CefV8ContextCppToC();
+  virtual ~CefV8ContextCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8CONTEXT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8exception_cpptoc.cc b/src/libcef_dll/cpptoc/v8exception_cpptoc.cc
new file mode 100644
index 0000000..92c9f5c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8exception_cpptoc.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=52a9308e9fe89367da969b258b0fc1aa7e315737$
+//
+
+#include "libcef_dll/cpptoc/v8exception_cpptoc.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+v8exception_get_message(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefV8ExceptionCppToC::Get(self)->GetMessage();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+v8exception_get_source_line(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefV8ExceptionCppToC::Get(self)->GetSourceLine();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+v8exception_get_script_resource_name(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefV8ExceptionCppToC::Get(self)->GetScriptResourceName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK v8exception_get_line_number(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ExceptionCppToC::Get(self)->GetLineNumber();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+v8exception_get_start_position(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ExceptionCppToC::Get(self)->GetStartPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK v8exception_get_end_position(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ExceptionCppToC::Get(self)->GetEndPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK v8exception_get_start_column(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ExceptionCppToC::Get(self)->GetStartColumn();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK v8exception_get_end_column(struct _cef_v8exception_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ExceptionCppToC::Get(self)->GetEndColumn();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ExceptionCppToC::CefV8ExceptionCppToC() {
+  GetStruct()->get_message = v8exception_get_message;
+  GetStruct()->get_source_line = v8exception_get_source_line;
+  GetStruct()->get_script_resource_name = v8exception_get_script_resource_name;
+  GetStruct()->get_line_number = v8exception_get_line_number;
+  GetStruct()->get_start_position = v8exception_get_start_position;
+  GetStruct()->get_end_position = v8exception_get_end_position;
+  GetStruct()->get_start_column = v8exception_get_start_column;
+  GetStruct()->get_end_column = v8exception_get_end_column;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ExceptionCppToC::~CefV8ExceptionCppToC() {}
+
+template <>
+CefRefPtr<CefV8Exception>
+CefCppToCRefCounted<CefV8ExceptionCppToC, CefV8Exception, cef_v8exception_t>::
+    UnwrapDerived(CefWrapperType type, cef_v8exception_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8ExceptionCppToC,
+                                   CefV8Exception,
+                                   cef_v8exception_t>::kWrapperType =
+    WT_V8EXCEPTION;
diff --git a/src/libcef_dll/cpptoc/v8exception_cpptoc.h b/src/libcef_dll/cpptoc/v8exception_cpptoc.h
new file mode 100644
index 0000000..b0e1a37
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8exception_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4c02f299865ed4f85feaad75dafa55bd8f571cc1$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8EXCEPTION_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8EXCEPTION_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8ExceptionCppToC : public CefCppToCRefCounted<CefV8ExceptionCppToC,
+                                                        CefV8Exception,
+                                                        cef_v8exception_t> {
+ public:
+  CefV8ExceptionCppToC();
+  virtual ~CefV8ExceptionCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8EXCEPTION_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8handler_cpptoc.cc b/src/libcef_dll/cpptoc/v8handler_cpptoc.cc
new file mode 100644
index 0000000..f1162a3
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8handler_cpptoc.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e0f75f572dbb0e9c838dbc88a5bfdd4d73a8e880$
+//
+
+#include "libcef_dll/cpptoc/v8handler_cpptoc.h"
+#include "libcef_dll/ctocpp/v8value_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK v8handler_execute(struct _cef_v8handler_t* self,
+                                   const cef_string_t* name,
+                                   struct _cef_v8value_t* object,
+                                   size_t argumentsCount,
+                                   struct _cef_v8value_t* const* arguments,
+                                   struct _cef_v8value_t** retval,
+                                   cef_string_t* exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object);
+  if (!object)
+    return 0;
+  // Verify param: arguments; type: refptr_vec_diff_byref_const
+  DCHECK(argumentsCount == 0 || arguments);
+  if (argumentsCount > 0 && !arguments)
+    return 0;
+  // Verify param: retval; type: refptr_diff_byref
+  DCHECK(retval);
+  if (!retval)
+    return 0;
+  // Verify param: exception; type: string_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+
+  // Translate param: arguments; type: refptr_vec_diff_byref_const
+  std::vector<CefRefPtr<CefV8Value>> argumentsList;
+  if (argumentsCount > 0) {
+    for (size_t i = 0; i < argumentsCount; ++i) {
+      CefRefPtr<CefV8Value> argumentsVal = CefV8ValueCToCpp::Wrap(arguments[i]);
+      argumentsList.push_back(argumentsVal);
+    }
+  }
+  // Translate param: retval; type: refptr_diff_byref
+  CefRefPtr<CefV8Value> retvalPtr;
+  if (retval && *retval)
+    retvalPtr = CefV8ValueCToCpp::Wrap(*retval);
+  CefV8Value* retvalOrig = retvalPtr.get();
+  // Translate param: exception; type: string_byref
+  CefString exceptionStr(exception);
+
+  // Execute
+  bool _retval = CefV8HandlerCppToC::Get(self)->Execute(
+      CefString(name), CefV8ValueCToCpp::Wrap(object), argumentsList, retvalPtr,
+      exceptionStr);
+
+  // Restore param: retval; type: refptr_diff_byref
+  if (retval) {
+    if (retvalPtr.get()) {
+      if (retvalPtr.get() != retvalOrig) {
+        *retval = CefV8ValueCToCpp::Unwrap(retvalPtr);
+      }
+    } else {
+      *retval = nullptr;
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8HandlerCppToC::CefV8HandlerCppToC() {
+  GetStruct()->execute = v8handler_execute;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8HandlerCppToC::~CefV8HandlerCppToC() {}
+
+template <>
+CefRefPtr<CefV8Handler>
+CefCppToCRefCounted<CefV8HandlerCppToC, CefV8Handler, cef_v8handler_t>::
+    UnwrapDerived(CefWrapperType type, cef_v8handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8HandlerCppToC,
+                                   CefV8Handler,
+                                   cef_v8handler_t>::kWrapperType =
+    WT_V8HANDLER;
diff --git a/src/libcef_dll/cpptoc/v8handler_cpptoc.h b/src/libcef_dll/cpptoc/v8handler_cpptoc.h
new file mode 100644
index 0000000..bdebd3a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2ecd1ec476ad4f5fb9367ef5b49dab8d9c25fd7d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8HandlerCppToC : public CefCppToCRefCounted<CefV8HandlerCppToC,
+                                                      CefV8Handler,
+                                                      cef_v8handler_t> {
+ public:
+  CefV8HandlerCppToC();
+  virtual ~CefV8HandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8interceptor_cpptoc.cc b/src/libcef_dll/cpptoc/v8interceptor_cpptoc.cc
new file mode 100644
index 0000000..1abd989
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8interceptor_cpptoc.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b44f4ce34e379b323220e4fda22e7ef4138b483e$
+//
+
+#include "libcef_dll/cpptoc/v8interceptor_cpptoc.h"
+#include "libcef_dll/ctocpp/v8value_ctocpp.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK v8interceptor_get_byname(struct _cef_v8interceptor_t* self,
+                                          const cef_string_t* name,
+                                          struct _cef_v8value_t* object,
+                                          struct _cef_v8value_t** retval,
+                                          cef_string_t* exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object);
+  if (!object)
+    return 0;
+  // Verify param: retval; type: refptr_diff_byref
+  DCHECK(retval);
+  if (!retval)
+    return 0;
+  // Verify param: exception; type: string_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+
+  // Translate param: retval; type: refptr_diff_byref
+  CefRefPtr<CefV8Value> retvalPtr;
+  if (retval && *retval)
+    retvalPtr = CefV8ValueCToCpp::Wrap(*retval);
+  CefV8Value* retvalOrig = retvalPtr.get();
+  // Translate param: exception; type: string_byref
+  CefString exceptionStr(exception);
+
+  // Execute
+  bool _retval = CefV8InterceptorCppToC::Get(self)->Get(
+      CefString(name), CefV8ValueCToCpp::Wrap(object), retvalPtr, exceptionStr);
+
+  // Restore param: retval; type: refptr_diff_byref
+  if (retval) {
+    if (retvalPtr.get()) {
+      if (retvalPtr.get() != retvalOrig) {
+        *retval = CefV8ValueCToCpp::Unwrap(retvalPtr);
+      }
+    } else {
+      *retval = nullptr;
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8interceptor_get_byindex(struct _cef_v8interceptor_t* self,
+                                           int index,
+                                           struct _cef_v8value_t* object,
+                                           struct _cef_v8value_t** retval,
+                                           cef_string_t* exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return 0;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object);
+  if (!object)
+    return 0;
+  // Verify param: retval; type: refptr_diff_byref
+  DCHECK(retval);
+  if (!retval)
+    return 0;
+  // Verify param: exception; type: string_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+
+  // Translate param: retval; type: refptr_diff_byref
+  CefRefPtr<CefV8Value> retvalPtr;
+  if (retval && *retval)
+    retvalPtr = CefV8ValueCToCpp::Wrap(*retval);
+  CefV8Value* retvalOrig = retvalPtr.get();
+  // Translate param: exception; type: string_byref
+  CefString exceptionStr(exception);
+
+  // Execute
+  bool _retval = CefV8InterceptorCppToC::Get(self)->Get(
+      index, CefV8ValueCToCpp::Wrap(object), retvalPtr, exceptionStr);
+
+  // Restore param: retval; type: refptr_diff_byref
+  if (retval) {
+    if (retvalPtr.get()) {
+      if (retvalPtr.get() != retvalOrig) {
+        *retval = CefV8ValueCToCpp::Unwrap(retvalPtr);
+      }
+    } else {
+      *retval = nullptr;
+    }
+  }
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8interceptor_set_byname(struct _cef_v8interceptor_t* self,
+                                          const cef_string_t* name,
+                                          struct _cef_v8value_t* object,
+                                          struct _cef_v8value_t* value,
+                                          cef_string_t* exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return 0;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object);
+  if (!object)
+    return 0;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value);
+  if (!value)
+    return 0;
+  // Verify param: exception; type: string_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+
+  // Translate param: exception; type: string_byref
+  CefString exceptionStr(exception);
+
+  // Execute
+  bool _retval = CefV8InterceptorCppToC::Get(self)->Set(
+      CefString(name), CefV8ValueCToCpp::Wrap(object),
+      CefV8ValueCToCpp::Wrap(value), exceptionStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8interceptor_set_byindex(struct _cef_v8interceptor_t* self,
+                                           int index,
+                                           struct _cef_v8value_t* object,
+                                           struct _cef_v8value_t* value,
+                                           cef_string_t* exception) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return 0;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object);
+  if (!object)
+    return 0;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value);
+  if (!value)
+    return 0;
+  // Verify param: exception; type: string_byref
+  DCHECK(exception);
+  if (!exception)
+    return 0;
+
+  // Translate param: exception; type: string_byref
+  CefString exceptionStr(exception);
+
+  // Execute
+  bool _retval = CefV8InterceptorCppToC::Get(self)->Set(
+      index, CefV8ValueCToCpp::Wrap(object), CefV8ValueCToCpp::Wrap(value),
+      exceptionStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8InterceptorCppToC::CefV8InterceptorCppToC() {
+  GetStruct()->get_byname = v8interceptor_get_byname;
+  GetStruct()->get_byindex = v8interceptor_get_byindex;
+  GetStruct()->set_byname = v8interceptor_set_byname;
+  GetStruct()->set_byindex = v8interceptor_set_byindex;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8InterceptorCppToC::~CefV8InterceptorCppToC() {}
+
+template <>
+CefRefPtr<CefV8Interceptor> CefCppToCRefCounted<
+    CefV8InterceptorCppToC,
+    CefV8Interceptor,
+    cef_v8interceptor_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_v8interceptor_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8InterceptorCppToC,
+                                   CefV8Interceptor,
+                                   cef_v8interceptor_t>::kWrapperType =
+    WT_V8INTERCEPTOR;
diff --git a/src/libcef_dll/cpptoc/v8interceptor_cpptoc.h b/src/libcef_dll/cpptoc/v8interceptor_cpptoc.h
new file mode 100644
index 0000000..8146da4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8interceptor_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2b68dd0f39073afa0d7ae83dbf9c4aa7e7772823$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8INTERCEPTOR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8INTERCEPTOR_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8InterceptorCppToC
+    : public CefCppToCRefCounted<CefV8InterceptorCppToC,
+                                 CefV8Interceptor,
+                                 cef_v8interceptor_t> {
+ public:
+  CefV8InterceptorCppToC();
+  virtual ~CefV8InterceptorCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8INTERCEPTOR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8stack_frame_cpptoc.cc b/src/libcef_dll/cpptoc/v8stack_frame_cpptoc.cc
new file mode 100644
index 0000000..7bd243b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8stack_frame_cpptoc.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=95cf5f4a9561fd25c98ef424df8a9d6047c24cc8$
+//
+
+#include "libcef_dll/cpptoc/v8stack_frame_cpptoc.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK v8stack_frame_is_valid(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8StackFrameCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+v8stack_frame_get_script_name(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefV8StackFrameCppToC::Get(self)->GetScriptName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+v8stack_frame_get_script_name_or_source_url(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefV8StackFrameCppToC::Get(self)->GetScriptNameOrSourceURL();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+v8stack_frame_get_function_name(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefV8StackFrameCppToC::Get(self)->GetFunctionName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK
+v8stack_frame_get_line_number(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8StackFrameCppToC::Get(self)->GetLineNumber();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK v8stack_frame_get_column(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8StackFrameCppToC::Get(self)->GetColumn();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK v8stack_frame_is_eval(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8StackFrameCppToC::Get(self)->IsEval();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+v8stack_frame_is_constructor(struct _cef_v8stack_frame_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8StackFrameCppToC::Get(self)->IsConstructor();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8StackFrameCppToC::CefV8StackFrameCppToC() {
+  GetStruct()->is_valid = v8stack_frame_is_valid;
+  GetStruct()->get_script_name = v8stack_frame_get_script_name;
+  GetStruct()->get_script_name_or_source_url =
+      v8stack_frame_get_script_name_or_source_url;
+  GetStruct()->get_function_name = v8stack_frame_get_function_name;
+  GetStruct()->get_line_number = v8stack_frame_get_line_number;
+  GetStruct()->get_column = v8stack_frame_get_column;
+  GetStruct()->is_eval = v8stack_frame_is_eval;
+  GetStruct()->is_constructor = v8stack_frame_is_constructor;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8StackFrameCppToC::~CefV8StackFrameCppToC() {}
+
+template <>
+CefRefPtr<CefV8StackFrame> CefCppToCRefCounted<
+    CefV8StackFrameCppToC,
+    CefV8StackFrame,
+    cef_v8stack_frame_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_v8stack_frame_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8StackFrameCppToC,
+                                   CefV8StackFrame,
+                                   cef_v8stack_frame_t>::kWrapperType =
+    WT_V8STACK_FRAME;
diff --git a/src/libcef_dll/cpptoc/v8stack_frame_cpptoc.h b/src/libcef_dll/cpptoc/v8stack_frame_cpptoc.h
new file mode 100644
index 0000000..8bf914c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8stack_frame_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=928142686ea53b977fe4b1f4447d249e38cf913b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8STACK_FRAME_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8STACK_FRAME_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8StackFrameCppToC : public CefCppToCRefCounted<CefV8StackFrameCppToC,
+                                                         CefV8StackFrame,
+                                                         cef_v8stack_frame_t> {
+ public:
+  CefV8StackFrameCppToC();
+  virtual ~CefV8StackFrameCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8STACK_FRAME_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8stack_trace_cpptoc.cc b/src/libcef_dll/cpptoc/v8stack_trace_cpptoc.cc
new file mode 100644
index 0000000..c936252
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8stack_trace_cpptoc.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=39ca29830cc55c7ebbb4786e811d20fa675156af$
+//
+
+#include "libcef_dll/cpptoc/v8stack_trace_cpptoc.h"
+#include "libcef_dll/cpptoc/v8stack_frame_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_v8stack_trace_t* cef_v8stack_trace_get_current(int frame_limit) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8StackTrace> _retval = CefV8StackTrace::GetCurrent(frame_limit);
+
+  // Return type: refptr_same
+  return CefV8StackTraceCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK v8stack_trace_is_valid(struct _cef_v8stack_trace_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8StackTraceCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+v8stack_trace_get_frame_count(struct _cef_v8stack_trace_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8StackTraceCppToC::Get(self)->GetFrameCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_v8stack_frame_t* CEF_CALLBACK
+v8stack_trace_get_frame(struct _cef_v8stack_trace_t* self, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8StackFrame> _retval =
+      CefV8StackTraceCppToC::Get(self)->GetFrame(index);
+
+  // Return type: refptr_same
+  return CefV8StackFrameCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8StackTraceCppToC::CefV8StackTraceCppToC() {
+  GetStruct()->is_valid = v8stack_trace_is_valid;
+  GetStruct()->get_frame_count = v8stack_trace_get_frame_count;
+  GetStruct()->get_frame = v8stack_trace_get_frame;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8StackTraceCppToC::~CefV8StackTraceCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefV8StackTrace> CefCppToCRefCounted<
+    CefV8StackTraceCppToC,
+    CefV8StackTrace,
+    cef_v8stack_trace_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_v8stack_trace_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8StackTraceCppToC,
+                                   CefV8StackTrace,
+                                   cef_v8stack_trace_t>::kWrapperType =
+    WT_V8STACK_TRACE;
diff --git a/src/libcef_dll/cpptoc/v8stack_trace_cpptoc.h b/src/libcef_dll/cpptoc/v8stack_trace_cpptoc.h
new file mode 100644
index 0000000..d79e429
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8stack_trace_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d75ffc77a6f3128512fefcc7471f8dd151fa36f2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8STACK_TRACE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8STACK_TRACE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8StackTraceCppToC : public CefCppToCRefCounted<CefV8StackTraceCppToC,
+                                                         CefV8StackTrace,
+                                                         cef_v8stack_trace_t> {
+ public:
+  CefV8StackTraceCppToC();
+  virtual ~CefV8StackTraceCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8STACK_TRACE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/v8value_cpptoc.cc b/src/libcef_dll/cpptoc/v8value_cpptoc.cc
new file mode 100644
index 0000000..2dd49d2
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8value_cpptoc.cc
@@ -0,0 +1,1058 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5a6ed12d3af81274817888347d60b580e8f5de10$
+//
+
+#include "libcef_dll/cpptoc/v8value_cpptoc.h"
+#include "libcef_dll/cpptoc/v8context_cpptoc.h"
+#include "libcef_dll/cpptoc/v8exception_cpptoc.h"
+#include "libcef_dll/ctocpp/base_ref_counted_ctocpp.h"
+#include "libcef_dll/ctocpp/v8accessor_ctocpp.h"
+#include "libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/v8handler_ctocpp.h"
+#include "libcef_dll/ctocpp/v8interceptor_ctocpp.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_undefined() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateUndefined();
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_null() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateNull();
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_bool(int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateBool(value ? true : false);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_int(int32 value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateInt(value);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_uint(uint32 value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateUInt(value);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_double(double value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateDouble(value);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_date(const cef_time_t* date) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: date; type: simple_byref_const
+  DCHECK(date);
+  if (!date)
+    return NULL;
+
+  // Translate param: date; type: simple_byref_const
+  CefTime dateVal = date ? *date : CefTime();
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateDate(dateVal);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_string(const cef_string_t* value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: value
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateString(CefString(value));
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_object(
+    cef_v8accessor_t* accessor,
+    cef_v8interceptor_t* interceptor) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: accessor, interceptor
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval =
+      CefV8Value::CreateObject(CefV8AccessorCToCpp::Wrap(accessor),
+                               CefV8InterceptorCToCpp::Wrap(interceptor));
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_array(int length) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateArray(length);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer(
+    void* buffer,
+    size_t length,
+    cef_v8array_buffer_release_callback_t* release_callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return NULL;
+  // Verify param: release_callback; type: refptr_diff
+  DCHECK(release_callback);
+  if (!release_callback)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateArrayBuffer(
+      buffer, length,
+      CefV8ArrayBufferReleaseCallbackCToCpp::Wrap(release_callback));
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_v8value_t* cef_v8value_create_function(
+    const cef_string_t* name,
+    cef_v8handler_t* handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return NULL;
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler);
+  if (!handler)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8Value::CreateFunction(
+      CefString(name), CefV8HandlerCToCpp::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK v8value_is_valid(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_undefined(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsUndefined();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_null(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsNull();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_bool(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsBool();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_int(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsInt();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_uint(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsUInt();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_double(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsDouble();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_date(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsDate();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_string(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsString();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_object(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsObject();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_array(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsArray();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_array_buffer(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsArrayBuffer();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_function(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsFunction();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_is_same(struct _cef_v8value_t* self,
+                                 struct _cef_v8value_t* that) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefV8ValueCppToC::Get(self)->IsSame(CefV8ValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_get_bool_value(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->GetBoolValue();
+
+  // Return type: bool
+  return _retval;
+}
+
+int32 CEF_CALLBACK v8value_get_int_value(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int32 _retval = CefV8ValueCppToC::Get(self)->GetIntValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+uint32 CEF_CALLBACK v8value_get_uint_value(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  uint32 _retval = CefV8ValueCppToC::Get(self)->GetUIntValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+double CEF_CALLBACK v8value_get_double_value(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  double _retval = CefV8ValueCppToC::Get(self)->GetDoubleValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_time_t CEF_CALLBACK v8value_get_date_value(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefTime();
+
+  // Execute
+  cef_time_t _retval = CefV8ValueCppToC::Get(self)->GetDateValue();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+v8value_get_string_value(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefV8ValueCppToC::Get(self)->GetStringValue();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK v8value_is_user_created(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->IsUserCreated();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_has_exception(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->HasException();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_v8exception_t* CEF_CALLBACK
+v8value_get_exception(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8Exception> _retval =
+      CefV8ValueCppToC::Get(self)->GetException();
+
+  // Return type: refptr_same
+  return CefV8ExceptionCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK v8value_clear_exception(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->ClearException();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_will_rethrow_exceptions(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->WillRethrowExceptions();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_set_rethrow_exceptions(struct _cef_v8value_t* self,
+                                                int rethrow) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefV8ValueCppToC::Get(self)->SetRethrowExceptions(rethrow ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_has_value_bykey(struct _cef_v8value_t* self,
+                                         const cef_string_t* key) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: key
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->HasValue(CefString(key));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_has_value_byindex(struct _cef_v8value_t* self,
+                                           int index) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->HasValue(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_delete_value_bykey(struct _cef_v8value_t* self,
+                                            const cef_string_t* key) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: key
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->DeleteValue(CefString(key));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_delete_value_byindex(struct _cef_v8value_t* self,
+                                              int index) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->DeleteValue(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_v8value_t* CEF_CALLBACK
+v8value_get_value_bykey(struct _cef_v8value_t* self, const cef_string_t* key) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Unverified params: key
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval =
+      CefV8ValueCppToC::Get(self)->GetValue(CefString(key));
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+struct _cef_v8value_t* CEF_CALLBACK
+v8value_get_value_byindex(struct _cef_v8value_t* self, int index) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8ValueCppToC::Get(self)->GetValue(index);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK v8value_set_value_bykey(struct _cef_v8value_t* self,
+                                         const cef_string_t* key,
+                                         struct _cef_v8value_t* value,
+                                         cef_v8_propertyattribute_t attribute) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+  // Unverified params: key
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->SetValue(
+      CefString(key), CefV8ValueCppToC::Unwrap(value), attribute);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_set_value_byindex(struct _cef_v8value_t* self,
+                                           int index,
+                                           struct _cef_v8value_t* value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->SetValue(
+      index, CefV8ValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+v8value_set_value_byaccessor(struct _cef_v8value_t* self,
+                             const cef_string_t* key,
+                             cef_v8_accesscontrol_t settings,
+                             cef_v8_propertyattribute_t attribute) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: key
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->SetValue(CefString(key), settings,
+                                                       attribute);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_get_keys(struct _cef_v8value_t* self,
+                                  cef_string_list_t keys) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: keys; type: string_vec_byref
+  DCHECK(keys);
+  if (!keys)
+    return 0;
+
+  // Translate param: keys; type: string_vec_byref
+  std::vector<CefString> keysList;
+  transfer_string_list_contents(keys, keysList);
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->GetKeys(keysList);
+
+  // Restore param: keys; type: string_vec_byref
+  cef_string_list_clear(keys);
+  transfer_string_list_contents(keysList, keys);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_set_user_data(struct _cef_v8value_t* self,
+                                       cef_base_ref_counted_t* user_data) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: user_data
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->SetUserData(
+      CefBaseRefCountedCToCpp::Wrap(user_data));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_base_ref_counted_t* CEF_CALLBACK
+v8value_get_user_data(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBaseRefCounted> _retval =
+      CefV8ValueCppToC::Get(self)->GetUserData();
+
+  // Return type: refptr_diff
+  return CefBaseRefCountedCToCpp::Unwrap(_retval);
+}
+
+int CEF_CALLBACK
+v8value_get_externally_allocated_memory(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ValueCppToC::Get(self)->GetExternallyAllocatedMemory();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+v8value_adjust_externally_allocated_memory(struct _cef_v8value_t* self,
+                                           int change_in_bytes) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ValueCppToC::Get(self)->AdjustExternallyAllocatedMemory(
+      change_in_bytes);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK v8value_get_array_length(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefV8ValueCppToC::Get(self)->GetArrayLength();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_v8array_buffer_release_callback_t* CEF_CALLBACK
+v8value_get_array_buffer_release_callback(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8ArrayBufferReleaseCallback> _retval =
+      CefV8ValueCppToC::Get(self)->GetArrayBufferReleaseCallback();
+
+  // Return type: refptr_diff
+  return CefV8ArrayBufferReleaseCallbackCToCpp::Unwrap(_retval);
+}
+
+int CEF_CALLBACK v8value_neuter_array_buffer(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefV8ValueCppToC::Get(self)->NeuterArrayBuffer();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+v8value_get_function_name(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefV8ValueCppToC::Get(self)->GetFunctionName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_v8handler_t* CEF_CALLBACK
+v8value_get_function_handler(struct _cef_v8value_t* self) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefV8Handler> _retval =
+      CefV8ValueCppToC::Get(self)->GetFunctionHandler();
+
+  // Return type: refptr_diff
+  return CefV8HandlerCToCpp::Unwrap(_retval);
+}
+
+struct _cef_v8value_t* CEF_CALLBACK
+v8value_execute_function(struct _cef_v8value_t* self,
+                         struct _cef_v8value_t* object,
+                         size_t argumentsCount,
+                         struct _cef_v8value_t* const* arguments) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: arguments; type: refptr_vec_same_byref_const
+  DCHECK(argumentsCount == 0 || arguments);
+  if (argumentsCount > 0 && !arguments)
+    return NULL;
+  // Unverified params: object
+
+  // Translate param: arguments; type: refptr_vec_same_byref_const
+  std::vector<CefRefPtr<CefV8Value>> argumentsList;
+  if (argumentsCount > 0) {
+    for (size_t i = 0; i < argumentsCount; ++i) {
+      CefRefPtr<CefV8Value> argumentsVal =
+          CefV8ValueCppToC::Unwrap(arguments[i]);
+      argumentsList.push_back(argumentsVal);
+    }
+  }
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval = CefV8ValueCppToC::Get(self)->ExecuteFunction(
+      CefV8ValueCppToC::Unwrap(object), argumentsList);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+struct _cef_v8value_t* CEF_CALLBACK
+v8value_execute_function_with_context(struct _cef_v8value_t* self,
+                                      cef_v8context_t* context,
+                                      struct _cef_v8value_t* object,
+                                      size_t argumentsCount,
+                                      struct _cef_v8value_t* const* arguments) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: context; type: refptr_same
+  DCHECK(context);
+  if (!context)
+    return NULL;
+  // Verify param: arguments; type: refptr_vec_same_byref_const
+  DCHECK(argumentsCount == 0 || arguments);
+  if (argumentsCount > 0 && !arguments)
+    return NULL;
+  // Unverified params: object
+
+  // Translate param: arguments; type: refptr_vec_same_byref_const
+  std::vector<CefRefPtr<CefV8Value>> argumentsList;
+  if (argumentsCount > 0) {
+    for (size_t i = 0; i < argumentsCount; ++i) {
+      CefRefPtr<CefV8Value> argumentsVal =
+          CefV8ValueCppToC::Unwrap(arguments[i]);
+      argumentsList.push_back(argumentsVal);
+    }
+  }
+
+  // Execute
+  CefRefPtr<CefV8Value> _retval =
+      CefV8ValueCppToC::Get(self)->ExecuteFunctionWithContext(
+          CefV8ContextCppToC::Unwrap(context), CefV8ValueCppToC::Unwrap(object),
+          argumentsList);
+
+  // Return type: refptr_same
+  return CefV8ValueCppToC::Wrap(_retval);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ValueCppToC::CefV8ValueCppToC() {
+  GetStruct()->is_valid = v8value_is_valid;
+  GetStruct()->is_undefined = v8value_is_undefined;
+  GetStruct()->is_null = v8value_is_null;
+  GetStruct()->is_bool = v8value_is_bool;
+  GetStruct()->is_int = v8value_is_int;
+  GetStruct()->is_uint = v8value_is_uint;
+  GetStruct()->is_double = v8value_is_double;
+  GetStruct()->is_date = v8value_is_date;
+  GetStruct()->is_string = v8value_is_string;
+  GetStruct()->is_object = v8value_is_object;
+  GetStruct()->is_array = v8value_is_array;
+  GetStruct()->is_array_buffer = v8value_is_array_buffer;
+  GetStruct()->is_function = v8value_is_function;
+  GetStruct()->is_same = v8value_is_same;
+  GetStruct()->get_bool_value = v8value_get_bool_value;
+  GetStruct()->get_int_value = v8value_get_int_value;
+  GetStruct()->get_uint_value = v8value_get_uint_value;
+  GetStruct()->get_double_value = v8value_get_double_value;
+  GetStruct()->get_date_value = v8value_get_date_value;
+  GetStruct()->get_string_value = v8value_get_string_value;
+  GetStruct()->is_user_created = v8value_is_user_created;
+  GetStruct()->has_exception = v8value_has_exception;
+  GetStruct()->get_exception = v8value_get_exception;
+  GetStruct()->clear_exception = v8value_clear_exception;
+  GetStruct()->will_rethrow_exceptions = v8value_will_rethrow_exceptions;
+  GetStruct()->set_rethrow_exceptions = v8value_set_rethrow_exceptions;
+  GetStruct()->has_value_bykey = v8value_has_value_bykey;
+  GetStruct()->has_value_byindex = v8value_has_value_byindex;
+  GetStruct()->delete_value_bykey = v8value_delete_value_bykey;
+  GetStruct()->delete_value_byindex = v8value_delete_value_byindex;
+  GetStruct()->get_value_bykey = v8value_get_value_bykey;
+  GetStruct()->get_value_byindex = v8value_get_value_byindex;
+  GetStruct()->set_value_bykey = v8value_set_value_bykey;
+  GetStruct()->set_value_byindex = v8value_set_value_byindex;
+  GetStruct()->set_value_byaccessor = v8value_set_value_byaccessor;
+  GetStruct()->get_keys = v8value_get_keys;
+  GetStruct()->set_user_data = v8value_set_user_data;
+  GetStruct()->get_user_data = v8value_get_user_data;
+  GetStruct()->get_externally_allocated_memory =
+      v8value_get_externally_allocated_memory;
+  GetStruct()->adjust_externally_allocated_memory =
+      v8value_adjust_externally_allocated_memory;
+  GetStruct()->get_array_length = v8value_get_array_length;
+  GetStruct()->get_array_buffer_release_callback =
+      v8value_get_array_buffer_release_callback;
+  GetStruct()->neuter_array_buffer = v8value_neuter_array_buffer;
+  GetStruct()->get_function_name = v8value_get_function_name;
+  GetStruct()->get_function_handler = v8value_get_function_handler;
+  GetStruct()->execute_function = v8value_execute_function;
+  GetStruct()->execute_function_with_context =
+      v8value_execute_function_with_context;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ValueCppToC::~CefV8ValueCppToC() {}
+
+template <>
+CefRefPtr<CefV8Value>
+CefCppToCRefCounted<CefV8ValueCppToC, CefV8Value, cef_v8value_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_v8value_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefV8ValueCppToC,
+                                   CefV8Value,
+                                   cef_v8value_t>::kWrapperType = WT_V8VALUE;
diff --git a/src/libcef_dll/cpptoc/v8value_cpptoc.h b/src/libcef_dll/cpptoc/v8value_cpptoc.h
new file mode 100644
index 0000000..4e9faba
--- /dev/null
+++ b/src/libcef_dll/cpptoc/v8value_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=50b66fea70f0b25aec23a1c24edeac8ae6b9915c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_V8VALUE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_V8VALUE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8ValueCppToC
+    : public CefCppToCRefCounted<CefV8ValueCppToC, CefV8Value, cef_v8value_t> {
+ public:
+  CefV8ValueCppToC();
+  virtual ~CefV8ValueCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_V8VALUE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/value_cpptoc.cc b/src/libcef_dll/cpptoc/value_cpptoc.cc
new file mode 100644
index 0000000..b0f53a6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/value_cpptoc.cc
@@ -0,0 +1,474 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4acbaa4608fbac62442f6f6cf921e7c51c930539$
+//
+
+#include "libcef_dll/cpptoc/value_cpptoc.h"
+#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/list_value_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_value_t* cef_value_create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefValue> _retval = CefValue::Create();
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK value_is_valid(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_is_owned(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->IsOwned();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_is_read_only(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_is_same(struct _cef_value_t* self,
+                               struct _cef_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefValueCppToC::Get(self)->IsSame(CefValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_is_equal(struct _cef_value_t* self,
+                                struct _cef_value_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefValueCppToC::Get(self)->IsEqual(CefValueCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_value_t* CEF_CALLBACK value_copy(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefValue> _retval = CefValueCppToC::Get(self)->Copy();
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+cef_value_type_t CEF_CALLBACK value_get_type(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return VTYPE_INVALID;
+
+  // Execute
+  cef_value_type_t _retval = CefValueCppToC::Get(self)->GetType();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK value_get_bool(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->GetBool();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_get_int(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefValueCppToC::Get(self)->GetInt();
+
+  // Return type: simple
+  return _retval;
+}
+
+double CEF_CALLBACK value_get_double(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  double _retval = CefValueCppToC::Get(self)->GetDouble();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK value_get_string(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefValueCppToC::Get(self)->GetString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+struct _cef_binary_value_t* CEF_CALLBACK
+value_get_binary(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval = CefValueCppToC::Get(self)->GetBinary();
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+struct _cef_dictionary_value_t* CEF_CALLBACK
+value_get_dictionary(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDictionaryValue> _retval =
+      CefValueCppToC::Get(self)->GetDictionary();
+
+  // Return type: refptr_same
+  return CefDictionaryValueCppToC::Wrap(_retval);
+}
+
+struct _cef_list_value_t* CEF_CALLBACK
+value_get_list(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefListValue> _retval = CefValueCppToC::Get(self)->GetList();
+
+  // Return type: refptr_same
+  return CefListValueCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK value_set_null(struct _cef_value_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->SetNull();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_set_bool(struct _cef_value_t* self, int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->SetBool(value ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_set_int(struct _cef_value_t* self, int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->SetInt(value);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_set_double(struct _cef_value_t* self, double value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->SetDouble(value);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_set_string(struct _cef_value_t* self,
+                                  const cef_string_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: value
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->SetString(CefString(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_set_binary(struct _cef_value_t* self,
+                                  struct _cef_binary_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefValueCppToC::Get(self)->SetBinary(CefBinaryValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_set_dictionary(struct _cef_value_t* self,
+                                      struct _cef_dictionary_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval = CefValueCppToC::Get(self)->SetDictionary(
+      CefDictionaryValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK value_set_list(struct _cef_value_t* self,
+                                struct _cef_list_value_t* value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: value; type: refptr_same
+  DCHECK(value);
+  if (!value)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefValueCppToC::Get(self)->SetList(CefListValueCppToC::Unwrap(value));
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefValueCppToC::CefValueCppToC() {
+  GetStruct()->is_valid = value_is_valid;
+  GetStruct()->is_owned = value_is_owned;
+  GetStruct()->is_read_only = value_is_read_only;
+  GetStruct()->is_same = value_is_same;
+  GetStruct()->is_equal = value_is_equal;
+  GetStruct()->copy = value_copy;
+  GetStruct()->get_type = value_get_type;
+  GetStruct()->get_bool = value_get_bool;
+  GetStruct()->get_int = value_get_int;
+  GetStruct()->get_double = value_get_double;
+  GetStruct()->get_string = value_get_string;
+  GetStruct()->get_binary = value_get_binary;
+  GetStruct()->get_dictionary = value_get_dictionary;
+  GetStruct()->get_list = value_get_list;
+  GetStruct()->set_null = value_set_null;
+  GetStruct()->set_bool = value_set_bool;
+  GetStruct()->set_int = value_set_int;
+  GetStruct()->set_double = value_set_double;
+  GetStruct()->set_string = value_set_string;
+  GetStruct()->set_binary = value_set_binary;
+  GetStruct()->set_dictionary = value_set_dictionary;
+  GetStruct()->set_list = value_set_list;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefValueCppToC::~CefValueCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefValue>
+CefCppToCRefCounted<CefValueCppToC, CefValue, cef_value_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_value_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefValueCppToC, CefValue, cef_value_t>::kWrapperType =
+        WT_VALUE;
diff --git a/src/libcef_dll/cpptoc/value_cpptoc.h b/src/libcef_dll/cpptoc/value_cpptoc.h
new file mode 100644
index 0000000..72948d2
--- /dev/null
+++ b/src/libcef_dll/cpptoc/value_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=85450d06a35f819a703ee947b2b932f2dc13e4ca$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VALUE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VALUE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefValueCppToC
+    : public CefCppToCRefCounted<CefValueCppToC, CefValue, cef_value_t> {
+ public:
+  CefValueCppToC();
+  virtual ~CefValueCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VALUE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/box_layout_cpptoc.cc b/src/libcef_dll/cpptoc/views/box_layout_cpptoc.cc
new file mode 100644
index 0000000..8ee48ea
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/box_layout_cpptoc.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6a8359ec33d2f2e18551fe0b1abf91df68c4417f$
+//
+
+#include "libcef_dll/cpptoc/views/box_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/fill_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK box_layout_set_flex_for_view(struct _cef_box_layout_t* self,
+                                               struct _cef_view_t* view,
+                                               int flex) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefBoxLayoutCppToC::Get(self)->SetFlexForView(CefViewCppToC::Unwrap(view),
+                                                flex);
+}
+
+void CEF_CALLBACK box_layout_clear_flex_for_view(struct _cef_box_layout_t* self,
+                                                 struct _cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefBoxLayoutCppToC::Get(self)->ClearFlexForView(CefViewCppToC::Unwrap(view));
+}
+
+cef_box_layout_t* CEF_CALLBACK
+box_layout_as_box_layout(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBoxLayout> _retval =
+      CefBoxLayoutCppToC::Get(reinterpret_cast<cef_box_layout_t*>(self))
+          ->AsBoxLayout();
+
+  // Return type: refptr_same
+  return CefBoxLayoutCppToC::Wrap(_retval);
+}
+
+cef_fill_layout_t* CEF_CALLBACK
+box_layout_as_fill_layout(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFillLayout> _retval =
+      CefBoxLayoutCppToC::Get(reinterpret_cast<cef_box_layout_t*>(self))
+          ->AsFillLayout();
+
+  // Return type: refptr_same
+  return CefFillLayoutCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK box_layout_is_valid(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBoxLayoutCppToC::Get(reinterpret_cast<cef_box_layout_t*>(self))
+          ->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBoxLayoutCppToC::CefBoxLayoutCppToC() {
+  GetStruct()->set_flex_for_view = box_layout_set_flex_for_view;
+  GetStruct()->clear_flex_for_view = box_layout_clear_flex_for_view;
+  GetStruct()->base.as_box_layout = box_layout_as_box_layout;
+  GetStruct()->base.as_fill_layout = box_layout_as_fill_layout;
+  GetStruct()->base.is_valid = box_layout_is_valid;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBoxLayoutCppToC::~CefBoxLayoutCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefBoxLayout>
+CefCppToCRefCounted<CefBoxLayoutCppToC, CefBoxLayout, cef_box_layout_t>::
+    UnwrapDerived(CefWrapperType type, cef_box_layout_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefBoxLayoutCppToC,
+                                   CefBoxLayout,
+                                   cef_box_layout_t>::kWrapperType =
+    WT_BOX_LAYOUT;
diff --git a/src/libcef_dll/cpptoc/views/box_layout_cpptoc.h b/src/libcef_dll/cpptoc/views/box_layout_cpptoc.h
new file mode 100644
index 0000000..fedff1c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/box_layout_cpptoc.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9e9add48dabc81d6b8eb58131db0018443f04dda$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_BOX_LAYOUT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_BOX_LAYOUT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_box_layout_capi.h"
+#include "include/capi/views/cef_view_capi.h"
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_view.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefBoxLayoutCppToC : public CefCppToCRefCounted<CefBoxLayoutCppToC,
+                                                      CefBoxLayout,
+                                                      cef_box_layout_t> {
+ public:
+  CefBoxLayoutCppToC();
+  virtual ~CefBoxLayoutCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_BOX_LAYOUT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/browser_view_cpptoc.cc b/src/libcef_dll/cpptoc/views/browser_view_cpptoc.cc
new file mode 100644
index 0000000..99f0508
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/browser_view_cpptoc.cc
@@ -0,0 +1,1154 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=41288fac368c771f9c673f231f6f632f6c6ec255$
+//
+
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/request_context_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/client_ctocpp.h"
+#include "libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_browser_view_t* cef_browser_view_create(
+    cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    cef_dictionary_value_t* extra_info,
+    cef_request_context_t* request_context,
+    cef_browser_view_delegate_t* delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return NULL;
+  // Unverified params: client, url, extra_info, request_context, delegate
+
+  // Translate param: settings; type: struct_byref_const
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval = CefBrowserView::CreateBrowserView(
+      CefClientCToCpp::Wrap(client), CefString(url), settingsObj,
+      CefDictionaryValueCppToC::Unwrap(extra_info),
+      CefRequestContextCppToC::Unwrap(request_context),
+      CefBrowserViewDelegateCToCpp::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_browser_view_t* cef_browser_view_get_for_browser(
+    cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_same
+  DCHECK(browser);
+  if (!browser)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefBrowserView::GetForBrowser(CefBrowserCppToC::Unwrap(browser));
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_browser_t* CEF_CALLBACK
+browser_view_get_browser(struct _cef_browser_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowser> _retval = CefBrowserViewCppToC::Get(self)->GetBrowser();
+
+  // Return type: refptr_same
+  return CefBrowserCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK
+browser_view_set_prefer_accelerators(struct _cef_browser_view_t* self,
+                                     int prefer_accelerators) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(self)->SetPreferAccelerators(
+      prefer_accelerators ? true : false);
+}
+
+cef_browser_view_t* CEF_CALLBACK
+browser_view_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK browser_view_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK browser_view_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK
+browser_view_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK
+browser_view_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+browser_view_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+browser_view_to_string(struct _cef_view_t* self, int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK browser_view_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_is_same(struct _cef_view_t* self,
+                                      struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+browser_view_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK
+browser_view_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK browser_view_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetID(id);
+}
+
+int CEF_CALLBACK browser_view_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_set_group_id(struct _cef_view_t* self,
+                                            int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+browser_view_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+browser_view_get_view_for_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK browser_view_set_bounds(struct _cef_view_t* self,
+                                          const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK browser_view_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK
+browser_view_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_set_size(struct _cef_view_t* self,
+                                        const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK browser_view_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_set_position(struct _cef_view_t* self,
+                                            const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK browser_view_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+browser_view_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+browser_view_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK
+browser_view_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+browser_view_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_get_height_for_width(struct _cef_view_t* self,
+                                                   int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->InvalidateLayout();
+}
+
+void CEF_CALLBACK browser_view_set_visible(struct _cef_view_t* self,
+                                           int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK browser_view_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_set_enabled(struct _cef_view_t* self,
+                                           int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK browser_view_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_set_focusable(struct _cef_view_t* self,
+                                             int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK browser_view_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+browser_view_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK browser_view_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->RequestFocus();
+}
+
+void CEF_CALLBACK browser_view_set_background_color(struct _cef_view_t* self,
+                                                    cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+browser_view_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_convert_point_to_screen(struct _cef_view_t* self,
+                                                      cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+browser_view_convert_point_from_screen(struct _cef_view_t* self,
+                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_convert_point_to_window(struct _cef_view_t* self,
+                                                      cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+browser_view_convert_point_from_window(struct _cef_view_t* self,
+                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_convert_point_to_view(struct _cef_view_t* self,
+                                                    struct _cef_view_t* view,
+                                                    cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK browser_view_convert_point_from_view(struct _cef_view_t* self,
+                                                      struct _cef_view_t* view,
+                                                      cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefBrowserViewCppToC::Get(reinterpret_cast<cef_browser_view_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserViewCppToC::CefBrowserViewCppToC() {
+  GetStruct()->get_browser = browser_view_get_browser;
+  GetStruct()->set_prefer_accelerators = browser_view_set_prefer_accelerators;
+  GetStruct()->base.as_browser_view = browser_view_as_browser_view;
+  GetStruct()->base.as_button = browser_view_as_button;
+  GetStruct()->base.as_panel = browser_view_as_panel;
+  GetStruct()->base.as_scroll_view = browser_view_as_scroll_view;
+  GetStruct()->base.as_textfield = browser_view_as_textfield;
+  GetStruct()->base.get_type_string = browser_view_get_type_string;
+  GetStruct()->base.to_string = browser_view_to_string;
+  GetStruct()->base.is_valid = browser_view_is_valid;
+  GetStruct()->base.is_attached = browser_view_is_attached;
+  GetStruct()->base.is_same = browser_view_is_same;
+  GetStruct()->base.get_delegate = browser_view_get_delegate;
+  GetStruct()->base.get_window = browser_view_get_window;
+  GetStruct()->base.get_id = browser_view_get_id;
+  GetStruct()->base.set_id = browser_view_set_id;
+  GetStruct()->base.get_group_id = browser_view_get_group_id;
+  GetStruct()->base.set_group_id = browser_view_set_group_id;
+  GetStruct()->base.get_parent_view = browser_view_get_parent_view;
+  GetStruct()->base.get_view_for_id = browser_view_get_view_for_id;
+  GetStruct()->base.set_bounds = browser_view_set_bounds;
+  GetStruct()->base.get_bounds = browser_view_get_bounds;
+  GetStruct()->base.get_bounds_in_screen = browser_view_get_bounds_in_screen;
+  GetStruct()->base.set_size = browser_view_set_size;
+  GetStruct()->base.get_size = browser_view_get_size;
+  GetStruct()->base.set_position = browser_view_set_position;
+  GetStruct()->base.get_position = browser_view_get_position;
+  GetStruct()->base.get_preferred_size = browser_view_get_preferred_size;
+  GetStruct()->base.size_to_preferred_size =
+      browser_view_size_to_preferred_size;
+  GetStruct()->base.get_minimum_size = browser_view_get_minimum_size;
+  GetStruct()->base.get_maximum_size = browser_view_get_maximum_size;
+  GetStruct()->base.get_height_for_width = browser_view_get_height_for_width;
+  GetStruct()->base.invalidate_layout = browser_view_invalidate_layout;
+  GetStruct()->base.set_visible = browser_view_set_visible;
+  GetStruct()->base.is_visible = browser_view_is_visible;
+  GetStruct()->base.is_drawn = browser_view_is_drawn;
+  GetStruct()->base.set_enabled = browser_view_set_enabled;
+  GetStruct()->base.is_enabled = browser_view_is_enabled;
+  GetStruct()->base.set_focusable = browser_view_set_focusable;
+  GetStruct()->base.is_focusable = browser_view_is_focusable;
+  GetStruct()->base.is_accessibility_focusable =
+      browser_view_is_accessibility_focusable;
+  GetStruct()->base.request_focus = browser_view_request_focus;
+  GetStruct()->base.set_background_color = browser_view_set_background_color;
+  GetStruct()->base.get_background_color = browser_view_get_background_color;
+  GetStruct()->base.convert_point_to_screen =
+      browser_view_convert_point_to_screen;
+  GetStruct()->base.convert_point_from_screen =
+      browser_view_convert_point_from_screen;
+  GetStruct()->base.convert_point_to_window =
+      browser_view_convert_point_to_window;
+  GetStruct()->base.convert_point_from_window =
+      browser_view_convert_point_from_window;
+  GetStruct()->base.convert_point_to_view = browser_view_convert_point_to_view;
+  GetStruct()->base.convert_point_from_view =
+      browser_view_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserViewCppToC::~CefBrowserViewCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefBrowserView>
+CefCppToCRefCounted<CefBrowserViewCppToC, CefBrowserView, cef_browser_view_t>::
+    UnwrapDerived(CefWrapperType type, cef_browser_view_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefBrowserViewCppToC,
+                                   CefBrowserView,
+                                   cef_browser_view_t>::kWrapperType =
+    WT_BROWSER_VIEW;
diff --git a/src/libcef_dll/cpptoc/views/browser_view_cpptoc.h b/src/libcef_dll/cpptoc/views/browser_view_cpptoc.h
new file mode 100644
index 0000000..49fb304
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/browser_view_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c6c62820ebb45bb8970a35a6b5d2d90dda6f18f2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_BROWSER_VIEW_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_BROWSER_VIEW_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_browser_view_capi.h"
+#include "include/views/cef_browser_view.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefBrowserViewCppToC : public CefCppToCRefCounted<CefBrowserViewCppToC,
+                                                        CefBrowserView,
+                                                        cef_browser_view_t> {
+ public:
+  CefBrowserViewCppToC();
+  virtual ~CefBrowserViewCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_BROWSER_VIEW_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.cc
new file mode 100644
index 0000000..4e4ec1b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.cc
@@ -0,0 +1,394 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c9ea7a1dae87105f7abfbd2e95747bc3380a9dc6$
+//
+
+#include "libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/client_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK browser_view_delegate_on_browser_created(
+    struct _cef_browser_view_delegate_t* self,
+    cef_browser_view_t* browser_view,
+    cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view);
+  if (!browser_view)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefBrowserViewDelegateCppToC::Get(self)->OnBrowserCreated(
+      CefBrowserViewCToCpp::Wrap(browser_view),
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+void CEF_CALLBACK browser_view_delegate_on_browser_destroyed(
+    struct _cef_browser_view_delegate_t* self,
+    cef_browser_view_t* browser_view,
+    cef_browser_t* browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view);
+  if (!browser_view)
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser);
+  if (!browser)
+    return;
+
+  // Execute
+  CefBrowserViewDelegateCppToC::Get(self)->OnBrowserDestroyed(
+      CefBrowserViewCToCpp::Wrap(browser_view),
+      CefBrowserCToCpp::Wrap(browser));
+}
+
+cef_browser_view_delegate_t* CEF_CALLBACK
+browser_view_delegate_get_delegate_for_popup_browser_view(
+    struct _cef_browser_view_delegate_t* self,
+    cef_browser_view_t* browser_view,
+    const struct _cef_browser_settings_t* settings,
+    cef_client_t* client,
+    int is_devtools) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view);
+  if (!browser_view)
+    return NULL;
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return NULL;
+  // Verify param: client; type: refptr_same
+  DCHECK(client);
+  if (!client)
+    return NULL;
+
+  // Translate param: settings; type: struct_byref_const
+  CefBrowserSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  CefRefPtr<CefBrowserViewDelegate> _retval =
+      CefBrowserViewDelegateCppToC::Get(self)->GetDelegateForPopupBrowserView(
+          CefBrowserViewCToCpp::Wrap(browser_view), settingsObj,
+          CefClientCppToC::Unwrap(client), is_devtools ? true : false);
+
+  // Return type: refptr_same
+  return CefBrowserViewDelegateCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK browser_view_delegate_on_popup_browser_view_created(
+    struct _cef_browser_view_delegate_t* self,
+    cef_browser_view_t* browser_view,
+    cef_browser_view_t* popup_browser_view,
+    int is_devtools) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view);
+  if (!browser_view)
+    return 0;
+  // Verify param: popup_browser_view; type: refptr_diff
+  DCHECK(popup_browser_view);
+  if (!popup_browser_view)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefBrowserViewDelegateCppToC::Get(self)->OnPopupBrowserViewCreated(
+          CefBrowserViewCToCpp::Wrap(browser_view),
+          CefBrowserViewCToCpp::Wrap(popup_browser_view),
+          is_devtools ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+browser_view_delegate_get_preferred_size(struct _cef_view_delegate_t* self,
+                                         cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefBrowserViewDelegateCppToC::Get(
+                           reinterpret_cast<cef_browser_view_delegate_t*>(self))
+                           ->GetPreferredSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+browser_view_delegate_get_minimum_size(struct _cef_view_delegate_t* self,
+                                       cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefBrowserViewDelegateCppToC::Get(
+                           reinterpret_cast<cef_browser_view_delegate_t*>(self))
+                           ->GetMinimumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+browser_view_delegate_get_maximum_size(struct _cef_view_delegate_t* self,
+                                       cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefBrowserViewDelegateCppToC::Get(
+                           reinterpret_cast<cef_browser_view_delegate_t*>(self))
+                           ->GetMaximumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+browser_view_delegate_get_height_for_width(struct _cef_view_delegate_t* self,
+                                           cef_view_t* view,
+                                           int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return 0;
+
+  // Execute
+  int _retval = CefBrowserViewDelegateCppToC::Get(
+                    reinterpret_cast<cef_browser_view_delegate_t*>(self))
+                    ->GetHeightForWidth(CefViewCToCpp::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+browser_view_delegate_on_parent_view_changed(struct _cef_view_delegate_t* self,
+                                             cef_view_t* view,
+                                             int added,
+                                             cef_view_t* parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent);
+  if (!parent)
+    return;
+
+  // Execute
+  CefBrowserViewDelegateCppToC::Get(
+      reinterpret_cast<cef_browser_view_delegate_t*>(self))
+      ->OnParentViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                            CefViewCToCpp::Wrap(parent));
+}
+
+void CEF_CALLBACK
+browser_view_delegate_on_child_view_changed(struct _cef_view_delegate_t* self,
+                                            cef_view_t* view,
+                                            int added,
+                                            cef_view_t* child) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child);
+  if (!child)
+    return;
+
+  // Execute
+  CefBrowserViewDelegateCppToC::Get(
+      reinterpret_cast<cef_browser_view_delegate_t*>(self))
+      ->OnChildViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                           CefViewCToCpp::Wrap(child));
+}
+
+void CEF_CALLBACK
+browser_view_delegate_on_focus(struct _cef_view_delegate_t* self,
+                               cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefBrowserViewDelegateCppToC::Get(
+      reinterpret_cast<cef_browser_view_delegate_t*>(self))
+      ->OnFocus(CefViewCToCpp::Wrap(view));
+}
+
+void CEF_CALLBACK
+browser_view_delegate_on_blur(struct _cef_view_delegate_t* self,
+                              cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefBrowserViewDelegateCppToC::Get(
+      reinterpret_cast<cef_browser_view_delegate_t*>(self))
+      ->OnBlur(CefViewCToCpp::Wrap(view));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserViewDelegateCppToC::CefBrowserViewDelegateCppToC() {
+  GetStruct()->on_browser_created = browser_view_delegate_on_browser_created;
+  GetStruct()->on_browser_destroyed =
+      browser_view_delegate_on_browser_destroyed;
+  GetStruct()->get_delegate_for_popup_browser_view =
+      browser_view_delegate_get_delegate_for_popup_browser_view;
+  GetStruct()->on_popup_browser_view_created =
+      browser_view_delegate_on_popup_browser_view_created;
+  GetStruct()->base.get_preferred_size =
+      browser_view_delegate_get_preferred_size;
+  GetStruct()->base.get_minimum_size = browser_view_delegate_get_minimum_size;
+  GetStruct()->base.get_maximum_size = browser_view_delegate_get_maximum_size;
+  GetStruct()->base.get_height_for_width =
+      browser_view_delegate_get_height_for_width;
+  GetStruct()->base.on_parent_view_changed =
+      browser_view_delegate_on_parent_view_changed;
+  GetStruct()->base.on_child_view_changed =
+      browser_view_delegate_on_child_view_changed;
+  GetStruct()->base.on_focus = browser_view_delegate_on_focus;
+  GetStruct()->base.on_blur = browser_view_delegate_on_blur;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserViewDelegateCppToC::~CefBrowserViewDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefBrowserViewDelegate> CefCppToCRefCounted<
+    CefBrowserViewDelegateCppToC,
+    CefBrowserViewDelegate,
+    cef_browser_view_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                                cef_browser_view_delegate_t*
+                                                    s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefBrowserViewDelegateCppToC,
+                                   CefBrowserViewDelegate,
+                                   cef_browser_view_delegate_t>::kWrapperType =
+    WT_BROWSER_VIEW_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h b/src/libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h
new file mode 100644
index 0000000..03de933
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=01f1b9ba8c2505b5a8bd532d6039c1b4ce803eb7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_BROWSER_VIEW_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_BROWSER_VIEW_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/views/cef_browser_view_capi.h"
+#include "include/capi/views/cef_browser_view_delegate_capi.h"
+#include "include/cef_browser.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_browser_view_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBrowserViewDelegateCppToC
+    : public CefCppToCRefCounted<CefBrowserViewDelegateCppToC,
+                                 CefBrowserViewDelegate,
+                                 cef_browser_view_delegate_t> {
+ public:
+  CefBrowserViewDelegateCppToC();
+  virtual ~CefBrowserViewDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_BROWSER_VIEW_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/button_cpptoc.cc b/src/libcef_dll/cpptoc/views/button_cpptoc.cc
new file mode 100644
index 0000000..5cd2ef1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/button_cpptoc.cc
@@ -0,0 +1,1131 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9dde9e370abff08479bf0e7f81b59356c7646f41$
+//
+
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/label_button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/menu_button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_label_button_t* CEF_CALLBACK
+button_as_label_button(struct _cef_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLabelButton> _retval =
+      CefButtonCppToC::Get(self)->AsLabelButton();
+
+  // Return type: refptr_same
+  return CefLabelButtonCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK button_set_state(struct _cef_button_t* self,
+                                   cef_button_state_t state) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(self)->SetState(state);
+}
+
+cef_button_state_t CEF_CALLBACK button_get_state(struct _cef_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CEF_BUTTON_STATE_NORMAL;
+
+  // Execute
+  cef_button_state_t _retval = CefButtonCppToC::Get(self)->GetState();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK button_set_ink_drop_enabled(struct _cef_button_t* self,
+                                              int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(self)->SetInkDropEnabled(enabled ? true : false);
+}
+
+void CEF_CALLBACK button_set_tooltip_text(struct _cef_button_t* self,
+                                          const cef_string_t* tooltip_text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: tooltip_text; type: string_byref_const
+  DCHECK(tooltip_text);
+  if (!tooltip_text)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(self)->SetTooltipText(CefString(tooltip_text));
+}
+
+void CEF_CALLBACK button_set_accessible_name(struct _cef_button_t* self,
+                                             const cef_string_t* name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(self)->SetAccessibleName(CefString(name));
+}
+
+cef_browser_view_t* CEF_CALLBACK
+button_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK button_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK button_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK
+button_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK button_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+button_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK button_to_string(struct _cef_view_t* self,
+                                                    int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK button_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_is_same(struct _cef_view_t* self,
+                                struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                     ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+button_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK button_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK button_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK button_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->SetID(id);
+}
+
+int CEF_CALLBACK button_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK button_set_group_id(struct _cef_view_t* self, int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+button_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+button_get_view_for_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK button_set_bounds(struct _cef_view_t* self,
+                                    const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK button_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK button_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK button_set_size(struct _cef_view_t* self,
+                                  const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK button_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK button_set_position(struct _cef_view_t* self,
+                                      const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK button_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK button_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK button_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK button_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK button_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK button_get_height_for_width(struct _cef_view_t* self,
+                                             int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                    ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK button_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->InvalidateLayout();
+}
+
+void CEF_CALLBACK button_set_visible(struct _cef_view_t* self, int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK button_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK button_set_enabled(struct _cef_view_t* self, int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK button_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK button_set_focusable(struct _cef_view_t* self,
+                                       int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK button_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                     ->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                     ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK button_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))->RequestFocus();
+}
+
+void CEF_CALLBACK button_set_background_color(struct _cef_view_t* self,
+                                              cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK button_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK button_convert_point_to_screen(struct _cef_view_t* self,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                     ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_convert_point_from_screen(struct _cef_view_t* self,
+                                                  cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                     ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_convert_point_to_window(struct _cef_view_t* self,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                     ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_convert_point_from_window(struct _cef_view_t* self,
+                                                  cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+                     ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_convert_point_to_view(struct _cef_view_t* self,
+                                              struct _cef_view_t* view,
+                                              cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK button_convert_point_from_view(struct _cef_view_t* self,
+                                                struct _cef_view_t* view,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefButtonCppToC::Get(reinterpret_cast<cef_button_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefButtonCppToC::CefButtonCppToC() {
+  GetStruct()->as_label_button = button_as_label_button;
+  GetStruct()->set_state = button_set_state;
+  GetStruct()->get_state = button_get_state;
+  GetStruct()->set_ink_drop_enabled = button_set_ink_drop_enabled;
+  GetStruct()->set_tooltip_text = button_set_tooltip_text;
+  GetStruct()->set_accessible_name = button_set_accessible_name;
+  GetStruct()->base.as_browser_view = button_as_browser_view;
+  GetStruct()->base.as_button = button_as_button;
+  GetStruct()->base.as_panel = button_as_panel;
+  GetStruct()->base.as_scroll_view = button_as_scroll_view;
+  GetStruct()->base.as_textfield = button_as_textfield;
+  GetStruct()->base.get_type_string = button_get_type_string;
+  GetStruct()->base.to_string = button_to_string;
+  GetStruct()->base.is_valid = button_is_valid;
+  GetStruct()->base.is_attached = button_is_attached;
+  GetStruct()->base.is_same = button_is_same;
+  GetStruct()->base.get_delegate = button_get_delegate;
+  GetStruct()->base.get_window = button_get_window;
+  GetStruct()->base.get_id = button_get_id;
+  GetStruct()->base.set_id = button_set_id;
+  GetStruct()->base.get_group_id = button_get_group_id;
+  GetStruct()->base.set_group_id = button_set_group_id;
+  GetStruct()->base.get_parent_view = button_get_parent_view;
+  GetStruct()->base.get_view_for_id = button_get_view_for_id;
+  GetStruct()->base.set_bounds = button_set_bounds;
+  GetStruct()->base.get_bounds = button_get_bounds;
+  GetStruct()->base.get_bounds_in_screen = button_get_bounds_in_screen;
+  GetStruct()->base.set_size = button_set_size;
+  GetStruct()->base.get_size = button_get_size;
+  GetStruct()->base.set_position = button_set_position;
+  GetStruct()->base.get_position = button_get_position;
+  GetStruct()->base.get_preferred_size = button_get_preferred_size;
+  GetStruct()->base.size_to_preferred_size = button_size_to_preferred_size;
+  GetStruct()->base.get_minimum_size = button_get_minimum_size;
+  GetStruct()->base.get_maximum_size = button_get_maximum_size;
+  GetStruct()->base.get_height_for_width = button_get_height_for_width;
+  GetStruct()->base.invalidate_layout = button_invalidate_layout;
+  GetStruct()->base.set_visible = button_set_visible;
+  GetStruct()->base.is_visible = button_is_visible;
+  GetStruct()->base.is_drawn = button_is_drawn;
+  GetStruct()->base.set_enabled = button_set_enabled;
+  GetStruct()->base.is_enabled = button_is_enabled;
+  GetStruct()->base.set_focusable = button_set_focusable;
+  GetStruct()->base.is_focusable = button_is_focusable;
+  GetStruct()->base.is_accessibility_focusable =
+      button_is_accessibility_focusable;
+  GetStruct()->base.request_focus = button_request_focus;
+  GetStruct()->base.set_background_color = button_set_background_color;
+  GetStruct()->base.get_background_color = button_get_background_color;
+  GetStruct()->base.convert_point_to_screen = button_convert_point_to_screen;
+  GetStruct()->base.convert_point_from_screen =
+      button_convert_point_from_screen;
+  GetStruct()->base.convert_point_to_window = button_convert_point_to_window;
+  GetStruct()->base.convert_point_from_window =
+      button_convert_point_from_window;
+  GetStruct()->base.convert_point_to_view = button_convert_point_to_view;
+  GetStruct()->base.convert_point_from_view = button_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefButtonCppToC::~CefButtonCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefButton>
+CefCppToCRefCounted<CefButtonCppToC, CefButton, cef_button_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_button_t* s) {
+  if (type == WT_LABEL_BUTTON) {
+    return CefLabelButtonCppToC::Unwrap(
+        reinterpret_cast<cef_label_button_t*>(s));
+  }
+  if (type == WT_MENU_BUTTON) {
+    return CefMenuButtonCppToC::Unwrap(reinterpret_cast<cef_menu_button_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefButtonCppToC, CefButton, cef_button_t>::
+    kWrapperType = WT_BUTTON;
diff --git a/src/libcef_dll/cpptoc/views/button_cpptoc.h b/src/libcef_dll/cpptoc/views/button_cpptoc.h
new file mode 100644
index 0000000..1d37862
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/button_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b94f64a13280f6f469c408f2348ed59d906bcd12$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_BUTTON_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_BUTTON_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_button_capi.h"
+#include "include/capi/views/cef_label_button_capi.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_label_button.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefButtonCppToC
+    : public CefCppToCRefCounted<CefButtonCppToC, CefButton, cef_button_t> {
+ public:
+  CefButtonCppToC();
+  virtual ~CefButtonCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_BUTTON_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/button_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/views/button_delegate_cpptoc.cc
new file mode 100644
index 0000000..0b343f1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/button_delegate_cpptoc.cc
@@ -0,0 +1,298 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=54ea52665f0558439e3c7f885962d75ce3992a0d$
+//
+
+#include "libcef_dll/cpptoc/views/button_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+button_delegate_on_button_pressed(struct _cef_button_delegate_t* self,
+                                  cef_button_t* button) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: button; type: refptr_diff
+  DCHECK(button);
+  if (!button)
+    return;
+
+  // Execute
+  CefButtonDelegateCppToC::Get(self)->OnButtonPressed(
+      CefButtonCToCpp::Wrap(button));
+}
+
+void CEF_CALLBACK
+button_delegate_on_button_state_changed(struct _cef_button_delegate_t* self,
+                                        cef_button_t* button) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: button; type: refptr_diff
+  DCHECK(button);
+  if (!button)
+    return;
+
+  // Execute
+  CefButtonDelegateCppToC::Get(self)->OnButtonStateChanged(
+      CefButtonCToCpp::Wrap(button));
+}
+
+cef_size_t CEF_CALLBACK
+button_delegate_get_preferred_size(struct _cef_view_delegate_t* self,
+                                   cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefButtonDelegateCppToC::Get(
+                           reinterpret_cast<cef_button_delegate_t*>(self))
+                           ->GetPreferredSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+button_delegate_get_minimum_size(struct _cef_view_delegate_t* self,
+                                 cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefButtonDelegateCppToC::Get(
+                           reinterpret_cast<cef_button_delegate_t*>(self))
+                           ->GetMinimumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+button_delegate_get_maximum_size(struct _cef_view_delegate_t* self,
+                                 cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefButtonDelegateCppToC::Get(
+                           reinterpret_cast<cef_button_delegate_t*>(self))
+                           ->GetMaximumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+button_delegate_get_height_for_width(struct _cef_view_delegate_t* self,
+                                     cef_view_t* view,
+                                     int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return 0;
+
+  // Execute
+  int _retval = CefButtonDelegateCppToC::Get(
+                    reinterpret_cast<cef_button_delegate_t*>(self))
+                    ->GetHeightForWidth(CefViewCToCpp::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+button_delegate_on_parent_view_changed(struct _cef_view_delegate_t* self,
+                                       cef_view_t* view,
+                                       int added,
+                                       cef_view_t* parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent);
+  if (!parent)
+    return;
+
+  // Execute
+  CefButtonDelegateCppToC::Get(reinterpret_cast<cef_button_delegate_t*>(self))
+      ->OnParentViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                            CefViewCToCpp::Wrap(parent));
+}
+
+void CEF_CALLBACK
+button_delegate_on_child_view_changed(struct _cef_view_delegate_t* self,
+                                      cef_view_t* view,
+                                      int added,
+                                      cef_view_t* child) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child);
+  if (!child)
+    return;
+
+  // Execute
+  CefButtonDelegateCppToC::Get(reinterpret_cast<cef_button_delegate_t*>(self))
+      ->OnChildViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                           CefViewCToCpp::Wrap(child));
+}
+
+void CEF_CALLBACK button_delegate_on_focus(struct _cef_view_delegate_t* self,
+                                           cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefButtonDelegateCppToC::Get(reinterpret_cast<cef_button_delegate_t*>(self))
+      ->OnFocus(CefViewCToCpp::Wrap(view));
+}
+
+void CEF_CALLBACK button_delegate_on_blur(struct _cef_view_delegate_t* self,
+                                          cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefButtonDelegateCppToC::Get(reinterpret_cast<cef_button_delegate_t*>(self))
+      ->OnBlur(CefViewCToCpp::Wrap(view));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefButtonDelegateCppToC::CefButtonDelegateCppToC() {
+  GetStruct()->on_button_pressed = button_delegate_on_button_pressed;
+  GetStruct()->on_button_state_changed =
+      button_delegate_on_button_state_changed;
+  GetStruct()->base.get_preferred_size = button_delegate_get_preferred_size;
+  GetStruct()->base.get_minimum_size = button_delegate_get_minimum_size;
+  GetStruct()->base.get_maximum_size = button_delegate_get_maximum_size;
+  GetStruct()->base.get_height_for_width = button_delegate_get_height_for_width;
+  GetStruct()->base.on_parent_view_changed =
+      button_delegate_on_parent_view_changed;
+  GetStruct()->base.on_child_view_changed =
+      button_delegate_on_child_view_changed;
+  GetStruct()->base.on_focus = button_delegate_on_focus;
+  GetStruct()->base.on_blur = button_delegate_on_blur;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefButtonDelegateCppToC::~CefButtonDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefButtonDelegate> CefCppToCRefCounted<
+    CefButtonDelegateCppToC,
+    CefButtonDelegate,
+    cef_button_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_button_delegate_t* s) {
+  if (type == WT_MENU_BUTTON_DELEGATE) {
+    return CefMenuButtonDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_menu_button_delegate_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefButtonDelegateCppToC,
+                                   CefButtonDelegate,
+                                   cef_button_delegate_t>::kWrapperType =
+    WT_BUTTON_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/views/button_delegate_cpptoc.h b/src/libcef_dll/cpptoc/views/button_delegate_cpptoc.h
new file mode 100644
index 0000000..b4da9c6
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/button_delegate_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2d19c78b51c5a0d9606a4e6c18153e37300fbf9f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_BUTTON_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_BUTTON_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_button_capi.h"
+#include "include/capi/views/cef_button_delegate_capi.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_button_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefButtonDelegateCppToC
+    : public CefCppToCRefCounted<CefButtonDelegateCppToC,
+                                 CefButtonDelegate,
+                                 cef_button_delegate_t> {
+ public:
+  CefButtonDelegateCppToC();
+  virtual ~CefButtonDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_BUTTON_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/display_cpptoc.cc b/src/libcef_dll/cpptoc/views/display_cpptoc.cc
new file mode 100644
index 0000000..59bc325
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/display_cpptoc.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d4f5ea07778b1ad3a08a1826dced47de771f9b02$
+//
+
+#include "libcef_dll/cpptoc/views/display_cpptoc.h"
+#include <algorithm>
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_display_t* cef_display_get_primary() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefDisplay> _retval = CefDisplay::GetPrimaryDisplay();
+
+  // Return type: refptr_same
+  return CefDisplayCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_display_t* cef_display_get_nearest_point(
+    const cef_point_t* point,
+    int input_pixel_coords) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: point; type: simple_byref_const
+  DCHECK(point);
+  if (!point)
+    return NULL;
+
+  // Translate param: point; type: simple_byref_const
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  CefRefPtr<CefDisplay> _retval = CefDisplay::GetDisplayNearestPoint(
+      pointVal, input_pixel_coords ? true : false);
+
+  // Return type: refptr_same
+  return CefDisplayCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_display_t* cef_display_get_matching_bounds(
+    const cef_rect_t* bounds,
+    int input_pixel_coords) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return NULL;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefRefPtr<CefDisplay> _retval = CefDisplay::GetDisplayMatchingBounds(
+      boundsVal, input_pixel_coords ? true : false);
+
+  // Return type: refptr_same
+  return CefDisplayCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT size_t cef_display_get_count() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = CefDisplay::GetDisplayCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+CEF_EXPORT void cef_display_get_alls(size_t* displaysCount,
+                                     cef_display_t** displays) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: displays; type: refptr_vec_same_byref
+  DCHECK(displaysCount && (*displaysCount == 0 || displays));
+  if (!displaysCount || (*displaysCount > 0 && !displays))
+    return;
+
+  // Translate param: displays; type: refptr_vec_same_byref
+  std::vector<CefRefPtr<CefDisplay>> displaysList;
+  if (displaysCount && *displaysCount > 0 && displays) {
+    for (size_t i = 0; i < *displaysCount; ++i) {
+      displaysList.push_back(CefDisplayCppToC::Unwrap(displays[i]));
+    }
+  }
+
+  // Execute
+  CefDisplay::GetAllDisplays(displaysList);
+
+  // Restore param: displays; type: refptr_vec_same_byref
+  if (displaysCount && displays) {
+    *displaysCount = std::min(displaysList.size(), *displaysCount);
+    if (*displaysCount > 0) {
+      for (size_t i = 0; i < *displaysCount; ++i) {
+        displays[i] = CefDisplayCppToC::Wrap(displaysList[i]);
+      }
+    }
+  }
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int64 CEF_CALLBACK display_get_id(struct _cef_display_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefDisplayCppToC::Get(self)->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+float CEF_CALLBACK
+display_get_device_scale_factor(struct _cef_display_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  float _retval = CefDisplayCppToC::Get(self)->GetDeviceScaleFactor();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK display_convert_point_to_pixels(struct _cef_display_t* self,
+                                                  cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  CefDisplayCppToC::Get(self)->ConvertPointToPixels(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+}
+
+void CEF_CALLBACK display_convert_point_from_pixels(struct _cef_display_t* self,
+                                                    cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  CefDisplayCppToC::Get(self)->ConvertPointFromPixels(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+}
+
+cef_rect_t CEF_CALLBACK display_get_bounds(struct _cef_display_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval = CefDisplayCppToC::Get(self)->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK display_get_work_area(struct _cef_display_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval = CefDisplayCppToC::Get(self)->GetWorkArea();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK display_get_rotation(struct _cef_display_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefDisplayCppToC::Get(self)->GetRotation();
+
+  // Return type: simple
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDisplayCppToC::CefDisplayCppToC() {
+  GetStruct()->get_id = display_get_id;
+  GetStruct()->get_device_scale_factor = display_get_device_scale_factor;
+  GetStruct()->convert_point_to_pixels = display_convert_point_to_pixels;
+  GetStruct()->convert_point_from_pixels = display_convert_point_from_pixels;
+  GetStruct()->get_bounds = display_get_bounds;
+  GetStruct()->get_work_area = display_get_work_area;
+  GetStruct()->get_rotation = display_get_rotation;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDisplayCppToC::~CefDisplayCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefDisplay>
+CefCppToCRefCounted<CefDisplayCppToC, CefDisplay, cef_display_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_display_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefDisplayCppToC,
+                                   CefDisplay,
+                                   cef_display_t>::kWrapperType = WT_DISPLAY;
diff --git a/src/libcef_dll/cpptoc/views/display_cpptoc.h b/src/libcef_dll/cpptoc/views/display_cpptoc.h
new file mode 100644
index 0000000..ec5a038
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/display_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=79e090cec779d0e737ebbf4bd303d5e70b4c9b5f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_DISPLAY_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_DISPLAY_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_display_capi.h"
+#include "include/views/cef_display.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefDisplayCppToC
+    : public CefCppToCRefCounted<CefDisplayCppToC, CefDisplay, cef_display_t> {
+ public:
+  CefDisplayCppToC();
+  virtual ~CefDisplayCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_DISPLAY_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/fill_layout_cpptoc.cc b/src/libcef_dll/cpptoc/views/fill_layout_cpptoc.cc
new file mode 100644
index 0000000..b120278
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/fill_layout_cpptoc.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cb375aa1dd0f29b5c43133bf6763bb2a0bb920bd$
+//
+
+#include "libcef_dll/cpptoc/views/fill_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/box_layout_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_box_layout_t* CEF_CALLBACK
+fill_layout_as_box_layout(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBoxLayout> _retval =
+      CefFillLayoutCppToC::Get(reinterpret_cast<cef_fill_layout_t*>(self))
+          ->AsBoxLayout();
+
+  // Return type: refptr_same
+  return CefBoxLayoutCppToC::Wrap(_retval);
+}
+
+cef_fill_layout_t* CEF_CALLBACK
+fill_layout_as_fill_layout(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFillLayout> _retval =
+      CefFillLayoutCppToC::Get(reinterpret_cast<cef_fill_layout_t*>(self))
+          ->AsFillLayout();
+
+  // Return type: refptr_same
+  return CefFillLayoutCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK fill_layout_is_valid(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefFillLayoutCppToC::Get(reinterpret_cast<cef_fill_layout_t*>(self))
+          ->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFillLayoutCppToC::CefFillLayoutCppToC() {
+  GetStruct()->base.as_box_layout = fill_layout_as_box_layout;
+  GetStruct()->base.as_fill_layout = fill_layout_as_fill_layout;
+  GetStruct()->base.is_valid = fill_layout_is_valid;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFillLayoutCppToC::~CefFillLayoutCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefFillLayout>
+CefCppToCRefCounted<CefFillLayoutCppToC, CefFillLayout, cef_fill_layout_t>::
+    UnwrapDerived(CefWrapperType type, cef_fill_layout_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefFillLayoutCppToC,
+                                   CefFillLayout,
+                                   cef_fill_layout_t>::kWrapperType =
+    WT_FILL_LAYOUT;
diff --git a/src/libcef_dll/cpptoc/views/fill_layout_cpptoc.h b/src/libcef_dll/cpptoc/views/fill_layout_cpptoc.h
new file mode 100644
index 0000000..a4ba76e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/fill_layout_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f9e4f2b10d60816bbd13c2545527e0619201b0ea$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_FILL_LAYOUT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_FILL_LAYOUT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_fill_layout_capi.h"
+#include "include/views/cef_fill_layout.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefFillLayoutCppToC : public CefCppToCRefCounted<CefFillLayoutCppToC,
+                                                       CefFillLayout,
+                                                       cef_fill_layout_t> {
+ public:
+  CefFillLayoutCppToC();
+  virtual ~CefFillLayoutCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_FILL_LAYOUT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/label_button_cpptoc.cc b/src/libcef_dll/cpptoc/views/label_button_cpptoc.cc
new file mode 100644
index 0000000..7dc8013
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/label_button_cpptoc.cc
@@ -0,0 +1,1415 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5d0c0d1982ef193450f5b331f6ad378ddbd8dbcc$
+//
+
+#include "libcef_dll/cpptoc/views/label_button_cpptoc.h"
+#include "libcef_dll/cpptoc/image_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/menu_button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/views/button_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_label_button_t* cef_label_button_create(
+    cef_button_delegate_t* delegate,
+    const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: delegate; type: refptr_diff
+  DCHECK(delegate);
+  if (!delegate)
+    return NULL;
+  // Unverified params: text
+
+  // Execute
+  CefRefPtr<CefLabelButton> _retval = CefLabelButton::CreateLabelButton(
+      CefButtonDelegateCToCpp::Wrap(delegate), CefString(text));
+
+  // Return type: refptr_same
+  return CefLabelButtonCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_menu_button_t* CEF_CALLBACK
+label_button_as_menu_button(struct _cef_label_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMenuButton> _retval =
+      CefLabelButtonCppToC::Get(self)->AsMenuButton();
+
+  // Return type: refptr_same
+  return CefMenuButtonCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK label_button_set_text(struct _cef_label_button_t* self,
+                                        const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetText(CefString(text));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+label_button_get_text(struct _cef_label_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefLabelButtonCppToC::Get(self)->GetText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK label_button_set_image(struct _cef_label_button_t* self,
+                                         cef_button_state_t button_state,
+                                         cef_image_t* image) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: image
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetImage(button_state,
+                                            CefImageCppToC::Unwrap(image));
+}
+
+cef_image_t* CEF_CALLBACK
+label_button_get_image(struct _cef_label_button_t* self,
+                       cef_button_state_t button_state) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefImage> _retval =
+      CefLabelButtonCppToC::Get(self)->GetImage(button_state);
+
+  // Return type: refptr_same
+  return CefImageCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK label_button_set_text_color(struct _cef_label_button_t* self,
+                                              cef_button_state_t for_state,
+                                              cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetTextColor(for_state, color);
+}
+
+void CEF_CALLBACK
+label_button_set_enabled_text_colors(struct _cef_label_button_t* self,
+                                     cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetEnabledTextColors(color);
+}
+
+void CEF_CALLBACK label_button_set_font_list(struct _cef_label_button_t* self,
+                                             const cef_string_t* font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: font_list; type: string_byref_const
+  DCHECK(font_list);
+  if (!font_list)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetFontList(CefString(font_list));
+}
+
+void CEF_CALLBACK
+label_button_set_horizontal_alignment(struct _cef_label_button_t* self,
+                                      cef_horizontal_alignment_t alignment) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetHorizontalAlignment(alignment);
+}
+
+void CEF_CALLBACK
+label_button_set_minimum_size(struct _cef_label_button_t* self,
+                              const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetMinimumSize(sizeVal);
+}
+
+void CEF_CALLBACK
+label_button_set_maximum_size(struct _cef_label_button_t* self,
+                              const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefLabelButtonCppToC::Get(self)->SetMaximumSize(sizeVal);
+}
+
+cef_label_button_t* CEF_CALLBACK
+label_button_as_label_button(struct _cef_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLabelButton> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->AsLabelButton();
+
+  // Return type: refptr_same
+  return CefLabelButtonCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK label_button_set_state(struct _cef_button_t* self,
+                                         cef_button_state_t state) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetState(state);
+}
+
+cef_button_state_t CEF_CALLBACK
+label_button_get_state(struct _cef_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CEF_BUTTON_STATE_NORMAL;
+
+  // Execute
+  cef_button_state_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetState();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_set_ink_drop_enabled(struct _cef_button_t* self,
+                                                    int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetInkDropEnabled(enabled ? true : false);
+}
+
+void CEF_CALLBACK
+label_button_set_tooltip_text(struct _cef_button_t* self,
+                              const cef_string_t* tooltip_text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: tooltip_text; type: string_byref_const
+  DCHECK(tooltip_text);
+  if (!tooltip_text)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetTooltipText(CefString(tooltip_text));
+}
+
+void CEF_CALLBACK label_button_set_accessible_name(struct _cef_button_t* self,
+                                                   const cef_string_t* name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetAccessibleName(CefString(name));
+}
+
+cef_browser_view_t* CEF_CALLBACK
+label_button_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK label_button_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK label_button_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK
+label_button_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK
+label_button_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+label_button_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+label_button_to_string(struct _cef_view_t* self, int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK label_button_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_is_same(struct _cef_view_t* self,
+                                      struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+label_button_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK
+label_button_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK label_button_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetID(id);
+}
+
+int CEF_CALLBACK label_button_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_set_group_id(struct _cef_view_t* self,
+                                            int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+label_button_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+label_button_get_view_for_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK label_button_set_bounds(struct _cef_view_t* self,
+                                          const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK label_button_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK
+label_button_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_set_size(struct _cef_view_t* self,
+                                        const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK label_button_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_set_position(struct _cef_view_t* self,
+                                            const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK label_button_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+label_button_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+label_button_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK
+label_button_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+label_button_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_get_height_for_width(struct _cef_view_t* self,
+                                                   int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->InvalidateLayout();
+}
+
+void CEF_CALLBACK label_button_set_visible(struct _cef_view_t* self,
+                                           int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK label_button_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_set_enabled(struct _cef_view_t* self,
+                                           int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK label_button_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_set_focusable(struct _cef_view_t* self,
+                                             int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK label_button_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+label_button_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK label_button_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->RequestFocus();
+}
+
+void CEF_CALLBACK label_button_set_background_color(struct _cef_view_t* self,
+                                                    cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+label_button_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_convert_point_to_screen(struct _cef_view_t* self,
+                                                      cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+label_button_convert_point_from_screen(struct _cef_view_t* self,
+                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_convert_point_to_window(struct _cef_view_t* self,
+                                                      cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+label_button_convert_point_from_window(struct _cef_view_t* self,
+                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_convert_point_to_view(struct _cef_view_t* self,
+                                                    struct _cef_view_t* view,
+                                                    cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK label_button_convert_point_from_view(struct _cef_view_t* self,
+                                                      struct _cef_view_t* view,
+                                                      cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefLabelButtonCppToC::Get(reinterpret_cast<cef_label_button_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLabelButtonCppToC::CefLabelButtonCppToC() {
+  GetStruct()->as_menu_button = label_button_as_menu_button;
+  GetStruct()->set_text = label_button_set_text;
+  GetStruct()->get_text = label_button_get_text;
+  GetStruct()->set_image = label_button_set_image;
+  GetStruct()->get_image = label_button_get_image;
+  GetStruct()->set_text_color = label_button_set_text_color;
+  GetStruct()->set_enabled_text_colors = label_button_set_enabled_text_colors;
+  GetStruct()->set_font_list = label_button_set_font_list;
+  GetStruct()->set_horizontal_alignment = label_button_set_horizontal_alignment;
+  GetStruct()->set_minimum_size = label_button_set_minimum_size;
+  GetStruct()->set_maximum_size = label_button_set_maximum_size;
+  GetStruct()->base.as_label_button = label_button_as_label_button;
+  GetStruct()->base.set_state = label_button_set_state;
+  GetStruct()->base.get_state = label_button_get_state;
+  GetStruct()->base.set_ink_drop_enabled = label_button_set_ink_drop_enabled;
+  GetStruct()->base.set_tooltip_text = label_button_set_tooltip_text;
+  GetStruct()->base.set_accessible_name = label_button_set_accessible_name;
+  GetStruct()->base.base.as_browser_view = label_button_as_browser_view;
+  GetStruct()->base.base.as_button = label_button_as_button;
+  GetStruct()->base.base.as_panel = label_button_as_panel;
+  GetStruct()->base.base.as_scroll_view = label_button_as_scroll_view;
+  GetStruct()->base.base.as_textfield = label_button_as_textfield;
+  GetStruct()->base.base.get_type_string = label_button_get_type_string;
+  GetStruct()->base.base.to_string = label_button_to_string;
+  GetStruct()->base.base.is_valid = label_button_is_valid;
+  GetStruct()->base.base.is_attached = label_button_is_attached;
+  GetStruct()->base.base.is_same = label_button_is_same;
+  GetStruct()->base.base.get_delegate = label_button_get_delegate;
+  GetStruct()->base.base.get_window = label_button_get_window;
+  GetStruct()->base.base.get_id = label_button_get_id;
+  GetStruct()->base.base.set_id = label_button_set_id;
+  GetStruct()->base.base.get_group_id = label_button_get_group_id;
+  GetStruct()->base.base.set_group_id = label_button_set_group_id;
+  GetStruct()->base.base.get_parent_view = label_button_get_parent_view;
+  GetStruct()->base.base.get_view_for_id = label_button_get_view_for_id;
+  GetStruct()->base.base.set_bounds = label_button_set_bounds;
+  GetStruct()->base.base.get_bounds = label_button_get_bounds;
+  GetStruct()->base.base.get_bounds_in_screen =
+      label_button_get_bounds_in_screen;
+  GetStruct()->base.base.set_size = label_button_set_size;
+  GetStruct()->base.base.get_size = label_button_get_size;
+  GetStruct()->base.base.set_position = label_button_set_position;
+  GetStruct()->base.base.get_position = label_button_get_position;
+  GetStruct()->base.base.get_preferred_size = label_button_get_preferred_size;
+  GetStruct()->base.base.size_to_preferred_size =
+      label_button_size_to_preferred_size;
+  GetStruct()->base.base.get_minimum_size = label_button_get_minimum_size;
+  GetStruct()->base.base.get_maximum_size = label_button_get_maximum_size;
+  GetStruct()->base.base.get_height_for_width =
+      label_button_get_height_for_width;
+  GetStruct()->base.base.invalidate_layout = label_button_invalidate_layout;
+  GetStruct()->base.base.set_visible = label_button_set_visible;
+  GetStruct()->base.base.is_visible = label_button_is_visible;
+  GetStruct()->base.base.is_drawn = label_button_is_drawn;
+  GetStruct()->base.base.set_enabled = label_button_set_enabled;
+  GetStruct()->base.base.is_enabled = label_button_is_enabled;
+  GetStruct()->base.base.set_focusable = label_button_set_focusable;
+  GetStruct()->base.base.is_focusable = label_button_is_focusable;
+  GetStruct()->base.base.is_accessibility_focusable =
+      label_button_is_accessibility_focusable;
+  GetStruct()->base.base.request_focus = label_button_request_focus;
+  GetStruct()->base.base.set_background_color =
+      label_button_set_background_color;
+  GetStruct()->base.base.get_background_color =
+      label_button_get_background_color;
+  GetStruct()->base.base.convert_point_to_screen =
+      label_button_convert_point_to_screen;
+  GetStruct()->base.base.convert_point_from_screen =
+      label_button_convert_point_from_screen;
+  GetStruct()->base.base.convert_point_to_window =
+      label_button_convert_point_to_window;
+  GetStruct()->base.base.convert_point_from_window =
+      label_button_convert_point_from_window;
+  GetStruct()->base.base.convert_point_to_view =
+      label_button_convert_point_to_view;
+  GetStruct()->base.base.convert_point_from_view =
+      label_button_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLabelButtonCppToC::~CefLabelButtonCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefLabelButton>
+CefCppToCRefCounted<CefLabelButtonCppToC, CefLabelButton, cef_label_button_t>::
+    UnwrapDerived(CefWrapperType type, cef_label_button_t* s) {
+  if (type == WT_MENU_BUTTON) {
+    return CefMenuButtonCppToC::Unwrap(reinterpret_cast<cef_menu_button_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefLabelButtonCppToC,
+                                   CefLabelButton,
+                                   cef_label_button_t>::kWrapperType =
+    WT_LABEL_BUTTON;
diff --git a/src/libcef_dll/cpptoc/views/label_button_cpptoc.h b/src/libcef_dll/cpptoc/views/label_button_cpptoc.h
new file mode 100644
index 0000000..e625878
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/label_button_cpptoc.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2e89e27310c9cf4b1663f74dd642382c9e1112f2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_LABEL_BUTTON_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_LABEL_BUTTON_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_label_button_capi.h"
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_menu_button.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefLabelButtonCppToC : public CefCppToCRefCounted<CefLabelButtonCppToC,
+                                                        CefLabelButton,
+                                                        cef_label_button_t> {
+ public:
+  CefLabelButtonCppToC();
+  virtual ~CefLabelButtonCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_LABEL_BUTTON_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/layout_cpptoc.cc b/src/libcef_dll/cpptoc/views/layout_cpptoc.cc
new file mode 100644
index 0000000..0fbc92f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/layout_cpptoc.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b690e5907fc196ab782442abcc218bf296537625$
+//
+
+#include "libcef_dll/cpptoc/views/layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/box_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/fill_layout_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_box_layout_t* CEF_CALLBACK
+layout_as_box_layout(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBoxLayout> _retval = CefLayoutCppToC::Get(self)->AsBoxLayout();
+
+  // Return type: refptr_same
+  return CefBoxLayoutCppToC::Wrap(_retval);
+}
+
+cef_fill_layout_t* CEF_CALLBACK
+layout_as_fill_layout(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFillLayout> _retval = CefLayoutCppToC::Get(self)->AsFillLayout();
+
+  // Return type: refptr_same
+  return CefFillLayoutCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK layout_is_valid(struct _cef_layout_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefLayoutCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLayoutCppToC::CefLayoutCppToC() {
+  GetStruct()->as_box_layout = layout_as_box_layout;
+  GetStruct()->as_fill_layout = layout_as_fill_layout;
+  GetStruct()->is_valid = layout_is_valid;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLayoutCppToC::~CefLayoutCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefLayout>
+CefCppToCRefCounted<CefLayoutCppToC, CefLayout, cef_layout_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_layout_t* s) {
+  if (type == WT_BOX_LAYOUT) {
+    return CefBoxLayoutCppToC::Unwrap(reinterpret_cast<cef_box_layout_t*>(s));
+  }
+  if (type == WT_FILL_LAYOUT) {
+    return CefFillLayoutCppToC::Unwrap(reinterpret_cast<cef_fill_layout_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefLayoutCppToC, CefLayout, cef_layout_t>::
+    kWrapperType = WT_LAYOUT;
diff --git a/src/libcef_dll/cpptoc/views/layout_cpptoc.h b/src/libcef_dll/cpptoc/views/layout_cpptoc.h
new file mode 100644
index 0000000..329de4b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/layout_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d8ff67a7549ec8e5cea4e089a9942acaed9cb466$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_LAYOUT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_LAYOUT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_box_layout_capi.h"
+#include "include/capi/views/cef_fill_layout_capi.h"
+#include "include/capi/views/cef_layout_capi.h"
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_fill_layout.h"
+#include "include/views/cef_layout.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefLayoutCppToC
+    : public CefCppToCRefCounted<CefLayoutCppToC, CefLayout, cef_layout_t> {
+ public:
+  CefLayoutCppToC();
+  virtual ~CefLayoutCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_LAYOUT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/menu_button_cpptoc.cc b/src/libcef_dll/cpptoc/views/menu_button_cpptoc.cc
new file mode 100644
index 0000000..b4418fd
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/menu_button_cpptoc.cc
@@ -0,0 +1,1465 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=47415544c8e36043a2a115453055bf98b888ecf1$
+//
+
+#include "libcef_dll/cpptoc/views/menu_button_cpptoc.h"
+#include "libcef_dll/cpptoc/image_cpptoc.h"
+#include "libcef_dll/cpptoc/menu_model_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/label_button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_menu_button_t* cef_menu_button_create(
+    cef_menu_button_delegate_t* delegate,
+    const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: delegate; type: refptr_diff
+  DCHECK(delegate);
+  if (!delegate)
+    return NULL;
+  // Unverified params: text
+
+  // Execute
+  CefRefPtr<CefMenuButton> _retval = CefMenuButton::CreateMenuButton(
+      CefMenuButtonDelegateCToCpp::Wrap(delegate), CefString(text));
+
+  // Return type: refptr_same
+  return CefMenuButtonCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+menu_button_show_menu(struct _cef_menu_button_t* self,
+                      cef_menu_model_t* menu_model,
+                      const cef_point_t* screen_point,
+                      cef_menu_anchor_position_t anchor_position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_same
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+  // Verify param: screen_point; type: simple_byref_const
+  DCHECK(screen_point);
+  if (!screen_point)
+    return;
+
+  // Translate param: screen_point; type: simple_byref_const
+  CefPoint screen_pointVal = screen_point ? *screen_point : CefPoint();
+
+  // Execute
+  CefMenuButtonCppToC::Get(self)->ShowMenu(
+      CefMenuModelCppToC::Unwrap(menu_model), screen_pointVal, anchor_position);
+}
+
+void CEF_CALLBACK menu_button_trigger_menu(struct _cef_menu_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(self)->TriggerMenu();
+}
+
+cef_menu_button_t* CEF_CALLBACK
+menu_button_as_menu_button(struct _cef_label_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefMenuButton> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->AsMenuButton();
+
+  // Return type: refptr_same
+  return CefMenuButtonCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK menu_button_set_text(struct _cef_label_button_t* self,
+                                       const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetText(CefString(text));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+menu_button_get_text(struct _cef_label_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK menu_button_set_image(struct _cef_label_button_t* self,
+                                        cef_button_state_t button_state,
+                                        cef_image_t* image) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: image
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetImage(button_state, CefImageCppToC::Unwrap(image));
+}
+
+cef_image_t* CEF_CALLBACK
+menu_button_get_image(struct _cef_label_button_t* self,
+                      cef_button_state_t button_state) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefImage> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetImage(button_state);
+
+  // Return type: refptr_same
+  return CefImageCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK menu_button_set_text_color(struct _cef_label_button_t* self,
+                                             cef_button_state_t for_state,
+                                             cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetTextColor(for_state, color);
+}
+
+void CEF_CALLBACK
+menu_button_set_enabled_text_colors(struct _cef_label_button_t* self,
+                                    cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetEnabledTextColors(color);
+}
+
+void CEF_CALLBACK menu_button_set_font_list(struct _cef_label_button_t* self,
+                                            const cef_string_t* font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: font_list; type: string_byref_const
+  DCHECK(font_list);
+  if (!font_list)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetFontList(CefString(font_list));
+}
+
+void CEF_CALLBACK
+menu_button_set_horizontal_alignment(struct _cef_label_button_t* self,
+                                     cef_horizontal_alignment_t alignment) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetHorizontalAlignment(alignment);
+}
+
+void CEF_CALLBACK menu_button_set_minimum_size(struct _cef_label_button_t* self,
+                                               const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetMinimumSize(sizeVal);
+}
+
+void CEF_CALLBACK menu_button_set_maximum_size(struct _cef_label_button_t* self,
+                                               const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetMaximumSize(sizeVal);
+}
+
+cef_label_button_t* CEF_CALLBACK
+menu_button_as_label_button(struct _cef_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLabelButton> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->AsLabelButton();
+
+  // Return type: refptr_same
+  return CefLabelButtonCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK menu_button_set_state(struct _cef_button_t* self,
+                                        cef_button_state_t state) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetState(state);
+}
+
+cef_button_state_t CEF_CALLBACK
+menu_button_get_state(struct _cef_button_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CEF_BUTTON_STATE_NORMAL;
+
+  // Execute
+  cef_button_state_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetState();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_set_ink_drop_enabled(struct _cef_button_t* self,
+                                                   int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetInkDropEnabled(enabled ? true : false);
+}
+
+void CEF_CALLBACK
+menu_button_set_tooltip_text(struct _cef_button_t* self,
+                             const cef_string_t* tooltip_text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: tooltip_text; type: string_byref_const
+  DCHECK(tooltip_text);
+  if (!tooltip_text)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetTooltipText(CefString(tooltip_text));
+}
+
+void CEF_CALLBACK menu_button_set_accessible_name(struct _cef_button_t* self,
+                                                  const cef_string_t* name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetAccessibleName(CefString(name));
+}
+
+cef_browser_view_t* CEF_CALLBACK
+menu_button_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK menu_button_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK menu_button_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK
+menu_button_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK
+menu_button_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+menu_button_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+menu_button_to_string(struct _cef_view_t* self, int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK menu_button_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_is_same(struct _cef_view_t* self,
+                                     struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+menu_button_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK
+menu_button_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK menu_button_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetID(id);
+}
+
+int CEF_CALLBACK menu_button_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_set_group_id(struct _cef_view_t* self,
+                                           int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+menu_button_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+menu_button_get_view_for_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK menu_button_set_bounds(struct _cef_view_t* self,
+                                         const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK menu_button_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK
+menu_button_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_set_size(struct _cef_view_t* self,
+                                       const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK menu_button_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_set_position(struct _cef_view_t* self,
+                                           const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK menu_button_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+menu_button_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK menu_button_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK menu_button_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_get_height_for_width(struct _cef_view_t* self,
+                                                  int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->InvalidateLayout();
+}
+
+void CEF_CALLBACK menu_button_set_visible(struct _cef_view_t* self,
+                                          int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK menu_button_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_set_enabled(struct _cef_view_t* self,
+                                          int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK menu_button_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_set_focusable(struct _cef_view_t* self,
+                                            int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK menu_button_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+menu_button_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK menu_button_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->RequestFocus();
+}
+
+void CEF_CALLBACK menu_button_set_background_color(struct _cef_view_t* self,
+                                                   cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+menu_button_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_convert_point_to_screen(struct _cef_view_t* self,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_convert_point_from_screen(struct _cef_view_t* self,
+                                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_convert_point_to_window(struct _cef_view_t* self,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_convert_point_from_window(struct _cef_view_t* self,
+                                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_convert_point_to_view(struct _cef_view_t* self,
+                                                   struct _cef_view_t* view,
+                                                   cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK menu_button_convert_point_from_view(struct _cef_view_t* self,
+                                                     struct _cef_view_t* view,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefMenuButtonCppToC::Get(reinterpret_cast<cef_menu_button_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuButtonCppToC::CefMenuButtonCppToC() {
+  GetStruct()->show_menu = menu_button_show_menu;
+  GetStruct()->trigger_menu = menu_button_trigger_menu;
+  GetStruct()->base.as_menu_button = menu_button_as_menu_button;
+  GetStruct()->base.set_text = menu_button_set_text;
+  GetStruct()->base.get_text = menu_button_get_text;
+  GetStruct()->base.set_image = menu_button_set_image;
+  GetStruct()->base.get_image = menu_button_get_image;
+  GetStruct()->base.set_text_color = menu_button_set_text_color;
+  GetStruct()->base.set_enabled_text_colors =
+      menu_button_set_enabled_text_colors;
+  GetStruct()->base.set_font_list = menu_button_set_font_list;
+  GetStruct()->base.set_horizontal_alignment =
+      menu_button_set_horizontal_alignment;
+  GetStruct()->base.set_minimum_size = menu_button_set_minimum_size;
+  GetStruct()->base.set_maximum_size = menu_button_set_maximum_size;
+  GetStruct()->base.base.as_label_button = menu_button_as_label_button;
+  GetStruct()->base.base.set_state = menu_button_set_state;
+  GetStruct()->base.base.get_state = menu_button_get_state;
+  GetStruct()->base.base.set_ink_drop_enabled =
+      menu_button_set_ink_drop_enabled;
+  GetStruct()->base.base.set_tooltip_text = menu_button_set_tooltip_text;
+  GetStruct()->base.base.set_accessible_name = menu_button_set_accessible_name;
+  GetStruct()->base.base.base.as_browser_view = menu_button_as_browser_view;
+  GetStruct()->base.base.base.as_button = menu_button_as_button;
+  GetStruct()->base.base.base.as_panel = menu_button_as_panel;
+  GetStruct()->base.base.base.as_scroll_view = menu_button_as_scroll_view;
+  GetStruct()->base.base.base.as_textfield = menu_button_as_textfield;
+  GetStruct()->base.base.base.get_type_string = menu_button_get_type_string;
+  GetStruct()->base.base.base.to_string = menu_button_to_string;
+  GetStruct()->base.base.base.is_valid = menu_button_is_valid;
+  GetStruct()->base.base.base.is_attached = menu_button_is_attached;
+  GetStruct()->base.base.base.is_same = menu_button_is_same;
+  GetStruct()->base.base.base.get_delegate = menu_button_get_delegate;
+  GetStruct()->base.base.base.get_window = menu_button_get_window;
+  GetStruct()->base.base.base.get_id = menu_button_get_id;
+  GetStruct()->base.base.base.set_id = menu_button_set_id;
+  GetStruct()->base.base.base.get_group_id = menu_button_get_group_id;
+  GetStruct()->base.base.base.set_group_id = menu_button_set_group_id;
+  GetStruct()->base.base.base.get_parent_view = menu_button_get_parent_view;
+  GetStruct()->base.base.base.get_view_for_id = menu_button_get_view_for_id;
+  GetStruct()->base.base.base.set_bounds = menu_button_set_bounds;
+  GetStruct()->base.base.base.get_bounds = menu_button_get_bounds;
+  GetStruct()->base.base.base.get_bounds_in_screen =
+      menu_button_get_bounds_in_screen;
+  GetStruct()->base.base.base.set_size = menu_button_set_size;
+  GetStruct()->base.base.base.get_size = menu_button_get_size;
+  GetStruct()->base.base.base.set_position = menu_button_set_position;
+  GetStruct()->base.base.base.get_position = menu_button_get_position;
+  GetStruct()->base.base.base.get_preferred_size =
+      menu_button_get_preferred_size;
+  GetStruct()->base.base.base.size_to_preferred_size =
+      menu_button_size_to_preferred_size;
+  GetStruct()->base.base.base.get_minimum_size = menu_button_get_minimum_size;
+  GetStruct()->base.base.base.get_maximum_size = menu_button_get_maximum_size;
+  GetStruct()->base.base.base.get_height_for_width =
+      menu_button_get_height_for_width;
+  GetStruct()->base.base.base.invalidate_layout = menu_button_invalidate_layout;
+  GetStruct()->base.base.base.set_visible = menu_button_set_visible;
+  GetStruct()->base.base.base.is_visible = menu_button_is_visible;
+  GetStruct()->base.base.base.is_drawn = menu_button_is_drawn;
+  GetStruct()->base.base.base.set_enabled = menu_button_set_enabled;
+  GetStruct()->base.base.base.is_enabled = menu_button_is_enabled;
+  GetStruct()->base.base.base.set_focusable = menu_button_set_focusable;
+  GetStruct()->base.base.base.is_focusable = menu_button_is_focusable;
+  GetStruct()->base.base.base.is_accessibility_focusable =
+      menu_button_is_accessibility_focusable;
+  GetStruct()->base.base.base.request_focus = menu_button_request_focus;
+  GetStruct()->base.base.base.set_background_color =
+      menu_button_set_background_color;
+  GetStruct()->base.base.base.get_background_color =
+      menu_button_get_background_color;
+  GetStruct()->base.base.base.convert_point_to_screen =
+      menu_button_convert_point_to_screen;
+  GetStruct()->base.base.base.convert_point_from_screen =
+      menu_button_convert_point_from_screen;
+  GetStruct()->base.base.base.convert_point_to_window =
+      menu_button_convert_point_to_window;
+  GetStruct()->base.base.base.convert_point_from_window =
+      menu_button_convert_point_from_window;
+  GetStruct()->base.base.base.convert_point_to_view =
+      menu_button_convert_point_to_view;
+  GetStruct()->base.base.base.convert_point_from_view =
+      menu_button_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuButtonCppToC::~CefMenuButtonCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMenuButton>
+CefCppToCRefCounted<CefMenuButtonCppToC, CefMenuButton, cef_menu_button_t>::
+    UnwrapDerived(CefWrapperType type, cef_menu_button_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMenuButtonCppToC,
+                                   CefMenuButton,
+                                   cef_menu_button_t>::kWrapperType =
+    WT_MENU_BUTTON;
diff --git a/src/libcef_dll/cpptoc/views/menu_button_cpptoc.h b/src/libcef_dll/cpptoc/views/menu_button_cpptoc.h
new file mode 100644
index 0000000..fb655ac
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/menu_button_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9759a92b82addfcf2c75b3f8ae7d9164c9ffb338$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/views/cef_menu_button.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefMenuButtonCppToC : public CefCppToCRefCounted<CefMenuButtonCppToC,
+                                                       CefMenuButton,
+                                                       cef_menu_button_t> {
+ public:
+  CefMenuButtonCppToC();
+  virtual ~CefMenuButtonCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.cc
new file mode 100644
index 0000000..b70103f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.cc
@@ -0,0 +1,343 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b434cff14ab679afd18ea45fa06e37cdfe063270$
+//
+
+#include "libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/menu_button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK menu_button_delegate_on_menu_button_pressed(
+    struct _cef_menu_button_delegate_t* self,
+    cef_menu_button_t* menu_button,
+    const cef_point_t* screen_point,
+    cef_menu_button_pressed_lock_t* button_pressed_lock) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_button; type: refptr_diff
+  DCHECK(menu_button);
+  if (!menu_button)
+    return;
+  // Verify param: screen_point; type: simple_byref_const
+  DCHECK(screen_point);
+  if (!screen_point)
+    return;
+  // Verify param: button_pressed_lock; type: refptr_diff
+  DCHECK(button_pressed_lock);
+  if (!button_pressed_lock)
+    return;
+
+  // Translate param: screen_point; type: simple_byref_const
+  CefPoint screen_pointVal = screen_point ? *screen_point : CefPoint();
+
+  // Execute
+  CefMenuButtonDelegateCppToC::Get(self)->OnMenuButtonPressed(
+      CefMenuButtonCToCpp::Wrap(menu_button), screen_pointVal,
+      CefMenuButtonPressedLockCToCpp::Wrap(button_pressed_lock));
+}
+
+void CEF_CALLBACK
+menu_button_delegate_on_button_pressed(struct _cef_button_delegate_t* self,
+                                       cef_button_t* button) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: button; type: refptr_diff
+  DCHECK(button);
+  if (!button)
+    return;
+
+  // Execute
+  CefMenuButtonDelegateCppToC::Get(
+      reinterpret_cast<cef_menu_button_delegate_t*>(self))
+      ->OnButtonPressed(CefButtonCToCpp::Wrap(button));
+}
+
+void CEF_CALLBACK menu_button_delegate_on_button_state_changed(
+    struct _cef_button_delegate_t* self,
+    cef_button_t* button) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: button; type: refptr_diff
+  DCHECK(button);
+  if (!button)
+    return;
+
+  // Execute
+  CefMenuButtonDelegateCppToC::Get(
+      reinterpret_cast<cef_menu_button_delegate_t*>(self))
+      ->OnButtonStateChanged(CefButtonCToCpp::Wrap(button));
+}
+
+cef_size_t CEF_CALLBACK
+menu_button_delegate_get_preferred_size(struct _cef_view_delegate_t* self,
+                                        cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefMenuButtonDelegateCppToC::Get(
+                           reinterpret_cast<cef_menu_button_delegate_t*>(self))
+                           ->GetPreferredSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+menu_button_delegate_get_minimum_size(struct _cef_view_delegate_t* self,
+                                      cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefMenuButtonDelegateCppToC::Get(
+                           reinterpret_cast<cef_menu_button_delegate_t*>(self))
+                           ->GetMinimumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+menu_button_delegate_get_maximum_size(struct _cef_view_delegate_t* self,
+                                      cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefMenuButtonDelegateCppToC::Get(
+                           reinterpret_cast<cef_menu_button_delegate_t*>(self))
+                           ->GetMaximumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+menu_button_delegate_get_height_for_width(struct _cef_view_delegate_t* self,
+                                          cef_view_t* view,
+                                          int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return 0;
+
+  // Execute
+  int _retval = CefMenuButtonDelegateCppToC::Get(
+                    reinterpret_cast<cef_menu_button_delegate_t*>(self))
+                    ->GetHeightForWidth(CefViewCToCpp::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+menu_button_delegate_on_parent_view_changed(struct _cef_view_delegate_t* self,
+                                            cef_view_t* view,
+                                            int added,
+                                            cef_view_t* parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent);
+  if (!parent)
+    return;
+
+  // Execute
+  CefMenuButtonDelegateCppToC::Get(
+      reinterpret_cast<cef_menu_button_delegate_t*>(self))
+      ->OnParentViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                            CefViewCToCpp::Wrap(parent));
+}
+
+void CEF_CALLBACK
+menu_button_delegate_on_child_view_changed(struct _cef_view_delegate_t* self,
+                                           cef_view_t* view,
+                                           int added,
+                                           cef_view_t* child) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child);
+  if (!child)
+    return;
+
+  // Execute
+  CefMenuButtonDelegateCppToC::Get(
+      reinterpret_cast<cef_menu_button_delegate_t*>(self))
+      ->OnChildViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                           CefViewCToCpp::Wrap(child));
+}
+
+void CEF_CALLBACK
+menu_button_delegate_on_focus(struct _cef_view_delegate_t* self,
+                              cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefMenuButtonDelegateCppToC::Get(
+      reinterpret_cast<cef_menu_button_delegate_t*>(self))
+      ->OnFocus(CefViewCToCpp::Wrap(view));
+}
+
+void CEF_CALLBACK
+menu_button_delegate_on_blur(struct _cef_view_delegate_t* self,
+                             cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefMenuButtonDelegateCppToC::Get(
+      reinterpret_cast<cef_menu_button_delegate_t*>(self))
+      ->OnBlur(CefViewCToCpp::Wrap(view));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuButtonDelegateCppToC::CefMenuButtonDelegateCppToC() {
+  GetStruct()->on_menu_button_pressed =
+      menu_button_delegate_on_menu_button_pressed;
+  GetStruct()->base.on_button_pressed = menu_button_delegate_on_button_pressed;
+  GetStruct()->base.on_button_state_changed =
+      menu_button_delegate_on_button_state_changed;
+  GetStruct()->base.base.get_preferred_size =
+      menu_button_delegate_get_preferred_size;
+  GetStruct()->base.base.get_minimum_size =
+      menu_button_delegate_get_minimum_size;
+  GetStruct()->base.base.get_maximum_size =
+      menu_button_delegate_get_maximum_size;
+  GetStruct()->base.base.get_height_for_width =
+      menu_button_delegate_get_height_for_width;
+  GetStruct()->base.base.on_parent_view_changed =
+      menu_button_delegate_on_parent_view_changed;
+  GetStruct()->base.base.on_child_view_changed =
+      menu_button_delegate_on_child_view_changed;
+  GetStruct()->base.base.on_focus = menu_button_delegate_on_focus;
+  GetStruct()->base.base.on_blur = menu_button_delegate_on_blur;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuButtonDelegateCppToC::~CefMenuButtonDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMenuButtonDelegate> CefCppToCRefCounted<
+    CefMenuButtonDelegateCppToC,
+    CefMenuButtonDelegate,
+    cef_menu_button_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                               cef_menu_button_delegate_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefMenuButtonDelegateCppToC,
+                                   CefMenuButtonDelegate,
+                                   cef_menu_button_delegate_t>::kWrapperType =
+    WT_MENU_BUTTON_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h b/src/libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h
new file mode 100644
index 0000000..a0b19b9
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f66bb8cf66c62d799895d08ba65743b1b3a8df4f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/capi/views/cef_menu_button_delegate_capi.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMenuButtonDelegateCppToC
+    : public CefCppToCRefCounted<CefMenuButtonDelegateCppToC,
+                                 CefMenuButtonDelegate,
+                                 cef_menu_button_delegate_t> {
+ public:
+  CefMenuButtonDelegateCppToC();
+  virtual ~CefMenuButtonDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.cc b/src/libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.cc
new file mode 100644
index 0000000..f5c9fff
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=85de615d0f69853565613b907ca8ed785996726f$
+//
+
+#include "libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuButtonPressedLockCppToC::CefMenuButtonPressedLockCppToC() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuButtonPressedLockCppToC::~CefMenuButtonPressedLockCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefMenuButtonPressedLock>
+CefCppToCRefCounted<CefMenuButtonPressedLockCppToC,
+                    CefMenuButtonPressedLock,
+                    cef_menu_button_pressed_lock_t>::
+    UnwrapDerived(CefWrapperType type, cef_menu_button_pressed_lock_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefMenuButtonPressedLockCppToC,
+                        CefMenuButtonPressedLock,
+                        cef_menu_button_pressed_lock_t>::kWrapperType =
+        WT_MENU_BUTTON_PRESSED_LOCK;
diff --git a/src/libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.h b/src/libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.h
new file mode 100644
index 0000000..a2a3aab
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9dad26affe3481306f8d876256dcf09b06dea18b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_PRESSED_LOCK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_PRESSED_LOCK_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/capi/views/cef_menu_button_delegate_capi.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefMenuButtonPressedLockCppToC
+    : public CefCppToCRefCounted<CefMenuButtonPressedLockCppToC,
+                                 CefMenuButtonPressedLock,
+                                 cef_menu_button_pressed_lock_t> {
+ public:
+  CefMenuButtonPressedLockCppToC();
+  virtual ~CefMenuButtonPressedLockCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_MENU_BUTTON_PRESSED_LOCK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/panel_cpptoc.cc b/src/libcef_dll/cpptoc/views/panel_cpptoc.cc
new file mode 100644
index 0000000..8c8b168
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/panel_cpptoc.cc
@@ -0,0 +1,1260 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=16383327a932a7da5991ac84d9689cea25318bdc$
+//
+
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/box_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/fill_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/views/panel_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_panel_t* cef_panel_create(cef_panel_delegate_t* delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefPanel::CreatePanel(CefPanelDelegateCToCpp::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+struct _cef_window_t* CEF_CALLBACK panel_as_window(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval = CefPanelCppToC::Get(self)->AsWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+cef_fill_layout_t* CEF_CALLBACK
+panel_set_to_fill_layout(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFillLayout> _retval =
+      CefPanelCppToC::Get(self)->SetToFillLayout();
+
+  // Return type: refptr_same
+  return CefFillLayoutCppToC::Wrap(_retval);
+}
+
+cef_box_layout_t* CEF_CALLBACK
+panel_set_to_box_layout(struct _cef_panel_t* self,
+                        const struct _cef_box_layout_settings_t* settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return NULL;
+
+  // Translate param: settings; type: struct_byref_const
+  CefBoxLayoutSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  CefRefPtr<CefBoxLayout> _retval =
+      CefPanelCppToC::Get(self)->SetToBoxLayout(settingsObj);
+
+  // Return type: refptr_same
+  return CefBoxLayoutCppToC::Wrap(_retval);
+}
+
+cef_layout_t* CEF_CALLBACK panel_get_layout(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLayout> _retval = CefPanelCppToC::Get(self)->GetLayout();
+
+  // Return type: refptr_same
+  return CefLayoutCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK panel_layout(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(self)->Layout();
+}
+
+void CEF_CALLBACK panel_add_child_view(struct _cef_panel_t* self,
+                                       struct _cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(self)->AddChildView(CefViewCppToC::Unwrap(view));
+}
+
+void CEF_CALLBACK panel_add_child_view_at(struct _cef_panel_t* self,
+                                          struct _cef_view_t* view,
+                                          int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(self)->AddChildViewAt(CefViewCppToC::Unwrap(view), index);
+}
+
+void CEF_CALLBACK panel_reorder_child_view(struct _cef_panel_t* self,
+                                           struct _cef_view_t* view,
+                                           int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(self)->ReorderChildView(CefViewCppToC::Unwrap(view),
+                                              index);
+}
+
+void CEF_CALLBACK panel_remove_child_view(struct _cef_panel_t* self,
+                                          struct _cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(self)->RemoveChildView(CefViewCppToC::Unwrap(view));
+}
+
+void CEF_CALLBACK panel_remove_all_child_views(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(self)->RemoveAllChildViews();
+}
+
+size_t CEF_CALLBACK panel_get_child_view_count(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefPanelCppToC::Get(self)->GetChildViewCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+struct _cef_view_t* CEF_CALLBACK
+panel_get_child_view_at(struct _cef_panel_t* self, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval = CefPanelCppToC::Get(self)->GetChildViewAt(index);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+cef_browser_view_t* CEF_CALLBACK
+panel_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK panel_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK panel_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK panel_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK panel_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+panel_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK panel_to_string(struct _cef_view_t* self,
+                                                   int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK panel_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_is_same(struct _cef_view_t* self,
+                               struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                     ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+panel_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK panel_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK panel_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK panel_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->SetID(id);
+}
+
+int CEF_CALLBACK panel_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK panel_set_group_id(struct _cef_view_t* self, int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+panel_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK panel_get_view_for_id(struct _cef_view_t* self,
+                                                       int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK panel_set_bounds(struct _cef_view_t* self,
+                                   const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK panel_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK panel_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                           ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK panel_set_size(struct _cef_view_t* self,
+                                 const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK panel_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK panel_set_position(struct _cef_view_t* self,
+                                     const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK panel_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK panel_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                           ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK panel_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK panel_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                           ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK panel_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                           ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK panel_get_height_for_width(struct _cef_view_t* self,
+                                            int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                    ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK panel_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->InvalidateLayout();
+}
+
+void CEF_CALLBACK panel_set_visible(struct _cef_view_t* self, int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK panel_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK panel_set_enabled(struct _cef_view_t* self, int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK panel_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK panel_set_focusable(struct _cef_view_t* self, int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK panel_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                     ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK panel_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))->RequestFocus();
+}
+
+void CEF_CALLBACK panel_set_background_color(struct _cef_view_t* self,
+                                             cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK panel_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK panel_convert_point_to_screen(struct _cef_view_t* self,
+                                               cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                     ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_convert_point_from_screen(struct _cef_view_t* self,
+                                                 cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                     ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_convert_point_to_window(struct _cef_view_t* self,
+                                               cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                     ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_convert_point_from_window(struct _cef_view_t* self,
+                                                 cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+                     ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_convert_point_to_view(struct _cef_view_t* self,
+                                             struct _cef_view_t* view,
+                                             cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK panel_convert_point_from_view(struct _cef_view_t* self,
+                                               struct _cef_view_t* view,
+                                               cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefPanelCppToC::Get(reinterpret_cast<cef_panel_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPanelCppToC::CefPanelCppToC() {
+  GetStruct()->as_window = panel_as_window;
+  GetStruct()->set_to_fill_layout = panel_set_to_fill_layout;
+  GetStruct()->set_to_box_layout = panel_set_to_box_layout;
+  GetStruct()->get_layout = panel_get_layout;
+  GetStruct()->layout = panel_layout;
+  GetStruct()->add_child_view = panel_add_child_view;
+  GetStruct()->add_child_view_at = panel_add_child_view_at;
+  GetStruct()->reorder_child_view = panel_reorder_child_view;
+  GetStruct()->remove_child_view = panel_remove_child_view;
+  GetStruct()->remove_all_child_views = panel_remove_all_child_views;
+  GetStruct()->get_child_view_count = panel_get_child_view_count;
+  GetStruct()->get_child_view_at = panel_get_child_view_at;
+  GetStruct()->base.as_browser_view = panel_as_browser_view;
+  GetStruct()->base.as_button = panel_as_button;
+  GetStruct()->base.as_panel = panel_as_panel;
+  GetStruct()->base.as_scroll_view = panel_as_scroll_view;
+  GetStruct()->base.as_textfield = panel_as_textfield;
+  GetStruct()->base.get_type_string = panel_get_type_string;
+  GetStruct()->base.to_string = panel_to_string;
+  GetStruct()->base.is_valid = panel_is_valid;
+  GetStruct()->base.is_attached = panel_is_attached;
+  GetStruct()->base.is_same = panel_is_same;
+  GetStruct()->base.get_delegate = panel_get_delegate;
+  GetStruct()->base.get_window = panel_get_window;
+  GetStruct()->base.get_id = panel_get_id;
+  GetStruct()->base.set_id = panel_set_id;
+  GetStruct()->base.get_group_id = panel_get_group_id;
+  GetStruct()->base.set_group_id = panel_set_group_id;
+  GetStruct()->base.get_parent_view = panel_get_parent_view;
+  GetStruct()->base.get_view_for_id = panel_get_view_for_id;
+  GetStruct()->base.set_bounds = panel_set_bounds;
+  GetStruct()->base.get_bounds = panel_get_bounds;
+  GetStruct()->base.get_bounds_in_screen = panel_get_bounds_in_screen;
+  GetStruct()->base.set_size = panel_set_size;
+  GetStruct()->base.get_size = panel_get_size;
+  GetStruct()->base.set_position = panel_set_position;
+  GetStruct()->base.get_position = panel_get_position;
+  GetStruct()->base.get_preferred_size = panel_get_preferred_size;
+  GetStruct()->base.size_to_preferred_size = panel_size_to_preferred_size;
+  GetStruct()->base.get_minimum_size = panel_get_minimum_size;
+  GetStruct()->base.get_maximum_size = panel_get_maximum_size;
+  GetStruct()->base.get_height_for_width = panel_get_height_for_width;
+  GetStruct()->base.invalidate_layout = panel_invalidate_layout;
+  GetStruct()->base.set_visible = panel_set_visible;
+  GetStruct()->base.is_visible = panel_is_visible;
+  GetStruct()->base.is_drawn = panel_is_drawn;
+  GetStruct()->base.set_enabled = panel_set_enabled;
+  GetStruct()->base.is_enabled = panel_is_enabled;
+  GetStruct()->base.set_focusable = panel_set_focusable;
+  GetStruct()->base.is_focusable = panel_is_focusable;
+  GetStruct()->base.is_accessibility_focusable =
+      panel_is_accessibility_focusable;
+  GetStruct()->base.request_focus = panel_request_focus;
+  GetStruct()->base.set_background_color = panel_set_background_color;
+  GetStruct()->base.get_background_color = panel_get_background_color;
+  GetStruct()->base.convert_point_to_screen = panel_convert_point_to_screen;
+  GetStruct()->base.convert_point_from_screen = panel_convert_point_from_screen;
+  GetStruct()->base.convert_point_to_window = panel_convert_point_to_window;
+  GetStruct()->base.convert_point_from_window = panel_convert_point_from_window;
+  GetStruct()->base.convert_point_to_view = panel_convert_point_to_view;
+  GetStruct()->base.convert_point_from_view = panel_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPanelCppToC::~CefPanelCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefPanel>
+CefCppToCRefCounted<CefPanelCppToC, CefPanel, cef_panel_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_panel_t* s) {
+  if (type == WT_WINDOW) {
+    return CefWindowCppToC::Unwrap(reinterpret_cast<cef_window_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefPanelCppToC, CefPanel, cef_panel_t>::kWrapperType =
+        WT_PANEL;
diff --git a/src/libcef_dll/cpptoc/views/panel_cpptoc.h b/src/libcef_dll/cpptoc/views/panel_cpptoc.h
new file mode 100644
index 0000000..ee902a4
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/panel_cpptoc.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bde3be5e373d61a9d40368e78757fd22fa01fdd7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_PANEL_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_PANEL_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_box_layout_capi.h"
+#include "include/capi/views/cef_fill_layout_capi.h"
+#include "include/capi/views/cef_layout_capi.h"
+#include "include/capi/views/cef_panel_capi.h"
+#include "include/capi/views/cef_window_capi.h"
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_fill_layout.h"
+#include "include/views/cef_layout.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_window.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefPanelCppToC
+    : public CefCppToCRefCounted<CefPanelCppToC, CefPanel, cef_panel_t> {
+ public:
+  CefPanelCppToC();
+  virtual ~CefPanelCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_PANEL_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/panel_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/views/panel_delegate_cpptoc.cc
new file mode 100644
index 0000000..82fb2b1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/panel_delegate_cpptoc.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=69b5f96bfef5ed4e368ff868fc8968d38a1159bb$
+//
+
+#include "libcef_dll/cpptoc/views/panel_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_size_t CEF_CALLBACK
+panel_delegate_get_preferred_size(struct _cef_view_delegate_t* self,
+                                  cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+          ->GetPreferredSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+panel_delegate_get_minimum_size(struct _cef_view_delegate_t* self,
+                                cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+          ->GetMinimumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+panel_delegate_get_maximum_size(struct _cef_view_delegate_t* self,
+                                cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+          ->GetMaximumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+panel_delegate_get_height_for_width(struct _cef_view_delegate_t* self,
+                                    cef_view_t* view,
+                                    int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+          ->GetHeightForWidth(CefViewCToCpp::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+panel_delegate_on_parent_view_changed(struct _cef_view_delegate_t* self,
+                                      cef_view_t* view,
+                                      int added,
+                                      cef_view_t* parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent);
+  if (!parent)
+    return;
+
+  // Execute
+  CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+      ->OnParentViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                            CefViewCToCpp::Wrap(parent));
+}
+
+void CEF_CALLBACK
+panel_delegate_on_child_view_changed(struct _cef_view_delegate_t* self,
+                                     cef_view_t* view,
+                                     int added,
+                                     cef_view_t* child) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child);
+  if (!child)
+    return;
+
+  // Execute
+  CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+      ->OnChildViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                           CefViewCToCpp::Wrap(child));
+}
+
+void CEF_CALLBACK panel_delegate_on_focus(struct _cef_view_delegate_t* self,
+                                          cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+      ->OnFocus(CefViewCToCpp::Wrap(view));
+}
+
+void CEF_CALLBACK panel_delegate_on_blur(struct _cef_view_delegate_t* self,
+                                         cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefPanelDelegateCppToC::Get(reinterpret_cast<cef_panel_delegate_t*>(self))
+      ->OnBlur(CefViewCToCpp::Wrap(view));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPanelDelegateCppToC::CefPanelDelegateCppToC() {
+  GetStruct()->base.get_preferred_size = panel_delegate_get_preferred_size;
+  GetStruct()->base.get_minimum_size = panel_delegate_get_minimum_size;
+  GetStruct()->base.get_maximum_size = panel_delegate_get_maximum_size;
+  GetStruct()->base.get_height_for_width = panel_delegate_get_height_for_width;
+  GetStruct()->base.on_parent_view_changed =
+      panel_delegate_on_parent_view_changed;
+  GetStruct()->base.on_child_view_changed =
+      panel_delegate_on_child_view_changed;
+  GetStruct()->base.on_focus = panel_delegate_on_focus;
+  GetStruct()->base.on_blur = panel_delegate_on_blur;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPanelDelegateCppToC::~CefPanelDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefPanelDelegate> CefCppToCRefCounted<
+    CefPanelDelegateCppToC,
+    CefPanelDelegate,
+    cef_panel_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_panel_delegate_t* s) {
+  if (type == WT_WINDOW_DELEGATE) {
+    return CefWindowDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_window_delegate_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefPanelDelegateCppToC,
+                                   CefPanelDelegate,
+                                   cef_panel_delegate_t>::kWrapperType =
+    WT_PANEL_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/views/panel_delegate_cpptoc.h b/src/libcef_dll/cpptoc/views/panel_delegate_cpptoc.h
new file mode 100644
index 0000000..fa721f1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/panel_delegate_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4b4762a13ba74d480900cd013195c1aeaf85b1ea$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_PANEL_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_PANEL_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_panel_delegate_capi.h"
+#include "include/views/cef_panel_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPanelDelegateCppToC
+    : public CefCppToCRefCounted<CefPanelDelegateCppToC,
+                                 CefPanelDelegate,
+                                 cef_panel_delegate_t> {
+ public:
+  CefPanelDelegateCppToC();
+  virtual ~CefPanelDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_PANEL_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/scroll_view_cpptoc.cc b/src/libcef_dll/cpptoc/views/scroll_view_cpptoc.cc
new file mode 100644
index 0000000..38910e5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/scroll_view_cpptoc.cc
@@ -0,0 +1,1201 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d2be86a1a89f55279682893e5a98366dd9c8f668$
+//
+
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_scroll_view_t* cef_scroll_view_create(
+    struct _cef_view_delegate_t* delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefScrollView::CreateScrollView(CefViewDelegateCToCpp::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK scroll_view_set_content_view(struct _cef_scroll_view_t* self,
+                                               struct _cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(self)->SetContentView(CefViewCppToC::Unwrap(view));
+}
+
+struct _cef_view_t* CEF_CALLBACK
+scroll_view_get_content_view(struct _cef_scroll_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval = CefScrollViewCppToC::Get(self)->GetContentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+cef_rect_t CEF_CALLBACK
+scroll_view_get_visible_content_rect(struct _cef_scroll_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval = CefScrollViewCppToC::Get(self)->GetVisibleContentRect();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+scroll_view_has_horizontal_scrollbar(struct _cef_scroll_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefScrollViewCppToC::Get(self)->HasHorizontalScrollbar();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+scroll_view_get_horizontal_scrollbar_height(struct _cef_scroll_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefScrollViewCppToC::Get(self)->GetHorizontalScrollbarHeight();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+scroll_view_has_vertical_scrollbar(struct _cef_scroll_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefScrollViewCppToC::Get(self)->HasVerticalScrollbar();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+scroll_view_get_vertical_scrollbar_width(struct _cef_scroll_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefScrollViewCppToC::Get(self)->GetVerticalScrollbarWidth();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_browser_view_t* CEF_CALLBACK
+scroll_view_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK scroll_view_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK scroll_view_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK
+scroll_view_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK
+scroll_view_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+scroll_view_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+scroll_view_to_string(struct _cef_view_t* self, int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK scroll_view_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_is_same(struct _cef_view_t* self,
+                                     struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+scroll_view_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK
+scroll_view_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK scroll_view_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetID(id);
+}
+
+int CEF_CALLBACK scroll_view_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_set_group_id(struct _cef_view_t* self,
+                                           int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+scroll_view_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+scroll_view_get_view_for_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK scroll_view_set_bounds(struct _cef_view_t* self,
+                                         const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK scroll_view_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK
+scroll_view_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_set_size(struct _cef_view_t* self,
+                                       const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK scroll_view_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_set_position(struct _cef_view_t* self,
+                                           const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK scroll_view_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+scroll_view_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK scroll_view_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK scroll_view_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_get_height_for_width(struct _cef_view_t* self,
+                                                  int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->InvalidateLayout();
+}
+
+void CEF_CALLBACK scroll_view_set_visible(struct _cef_view_t* self,
+                                          int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK scroll_view_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_set_enabled(struct _cef_view_t* self,
+                                          int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK scroll_view_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_set_focusable(struct _cef_view_t* self,
+                                            int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK scroll_view_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+scroll_view_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK scroll_view_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->RequestFocus();
+}
+
+void CEF_CALLBACK scroll_view_set_background_color(struct _cef_view_t* self,
+                                                   cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+scroll_view_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_convert_point_to_screen(struct _cef_view_t* self,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_convert_point_from_screen(struct _cef_view_t* self,
+                                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_convert_point_to_window(struct _cef_view_t* self,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_convert_point_from_window(struct _cef_view_t* self,
+                                                       cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_convert_point_to_view(struct _cef_view_t* self,
+                                                   struct _cef_view_t* view,
+                                                   cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK scroll_view_convert_point_from_view(struct _cef_view_t* self,
+                                                     struct _cef_view_t* view,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefScrollViewCppToC::Get(reinterpret_cast<cef_scroll_view_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefScrollViewCppToC::CefScrollViewCppToC() {
+  GetStruct()->set_content_view = scroll_view_set_content_view;
+  GetStruct()->get_content_view = scroll_view_get_content_view;
+  GetStruct()->get_visible_content_rect = scroll_view_get_visible_content_rect;
+  GetStruct()->has_horizontal_scrollbar = scroll_view_has_horizontal_scrollbar;
+  GetStruct()->get_horizontal_scrollbar_height =
+      scroll_view_get_horizontal_scrollbar_height;
+  GetStruct()->has_vertical_scrollbar = scroll_view_has_vertical_scrollbar;
+  GetStruct()->get_vertical_scrollbar_width =
+      scroll_view_get_vertical_scrollbar_width;
+  GetStruct()->base.as_browser_view = scroll_view_as_browser_view;
+  GetStruct()->base.as_button = scroll_view_as_button;
+  GetStruct()->base.as_panel = scroll_view_as_panel;
+  GetStruct()->base.as_scroll_view = scroll_view_as_scroll_view;
+  GetStruct()->base.as_textfield = scroll_view_as_textfield;
+  GetStruct()->base.get_type_string = scroll_view_get_type_string;
+  GetStruct()->base.to_string = scroll_view_to_string;
+  GetStruct()->base.is_valid = scroll_view_is_valid;
+  GetStruct()->base.is_attached = scroll_view_is_attached;
+  GetStruct()->base.is_same = scroll_view_is_same;
+  GetStruct()->base.get_delegate = scroll_view_get_delegate;
+  GetStruct()->base.get_window = scroll_view_get_window;
+  GetStruct()->base.get_id = scroll_view_get_id;
+  GetStruct()->base.set_id = scroll_view_set_id;
+  GetStruct()->base.get_group_id = scroll_view_get_group_id;
+  GetStruct()->base.set_group_id = scroll_view_set_group_id;
+  GetStruct()->base.get_parent_view = scroll_view_get_parent_view;
+  GetStruct()->base.get_view_for_id = scroll_view_get_view_for_id;
+  GetStruct()->base.set_bounds = scroll_view_set_bounds;
+  GetStruct()->base.get_bounds = scroll_view_get_bounds;
+  GetStruct()->base.get_bounds_in_screen = scroll_view_get_bounds_in_screen;
+  GetStruct()->base.set_size = scroll_view_set_size;
+  GetStruct()->base.get_size = scroll_view_get_size;
+  GetStruct()->base.set_position = scroll_view_set_position;
+  GetStruct()->base.get_position = scroll_view_get_position;
+  GetStruct()->base.get_preferred_size = scroll_view_get_preferred_size;
+  GetStruct()->base.size_to_preferred_size = scroll_view_size_to_preferred_size;
+  GetStruct()->base.get_minimum_size = scroll_view_get_minimum_size;
+  GetStruct()->base.get_maximum_size = scroll_view_get_maximum_size;
+  GetStruct()->base.get_height_for_width = scroll_view_get_height_for_width;
+  GetStruct()->base.invalidate_layout = scroll_view_invalidate_layout;
+  GetStruct()->base.set_visible = scroll_view_set_visible;
+  GetStruct()->base.is_visible = scroll_view_is_visible;
+  GetStruct()->base.is_drawn = scroll_view_is_drawn;
+  GetStruct()->base.set_enabled = scroll_view_set_enabled;
+  GetStruct()->base.is_enabled = scroll_view_is_enabled;
+  GetStruct()->base.set_focusable = scroll_view_set_focusable;
+  GetStruct()->base.is_focusable = scroll_view_is_focusable;
+  GetStruct()->base.is_accessibility_focusable =
+      scroll_view_is_accessibility_focusable;
+  GetStruct()->base.request_focus = scroll_view_request_focus;
+  GetStruct()->base.set_background_color = scroll_view_set_background_color;
+  GetStruct()->base.get_background_color = scroll_view_get_background_color;
+  GetStruct()->base.convert_point_to_screen =
+      scroll_view_convert_point_to_screen;
+  GetStruct()->base.convert_point_from_screen =
+      scroll_view_convert_point_from_screen;
+  GetStruct()->base.convert_point_to_window =
+      scroll_view_convert_point_to_window;
+  GetStruct()->base.convert_point_from_window =
+      scroll_view_convert_point_from_window;
+  GetStruct()->base.convert_point_to_view = scroll_view_convert_point_to_view;
+  GetStruct()->base.convert_point_from_view =
+      scroll_view_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefScrollViewCppToC::~CefScrollViewCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefScrollView>
+CefCppToCRefCounted<CefScrollViewCppToC, CefScrollView, cef_scroll_view_t>::
+    UnwrapDerived(CefWrapperType type, cef_scroll_view_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefScrollViewCppToC,
+                                   CefScrollView,
+                                   cef_scroll_view_t>::kWrapperType =
+    WT_SCROLL_VIEW;
diff --git a/src/libcef_dll/cpptoc/views/scroll_view_cpptoc.h b/src/libcef_dll/cpptoc/views/scroll_view_cpptoc.h
new file mode 100644
index 0000000..ba7a9b8
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/scroll_view_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fb8f1a2be6ab62c96c876e3bf07bcbb6df3a34b0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_SCROLL_VIEW_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_SCROLL_VIEW_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_scroll_view_capi.h"
+#include "include/views/cef_scroll_view.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefScrollViewCppToC : public CefCppToCRefCounted<CefScrollViewCppToC,
+                                                       CefScrollView,
+                                                       cef_scroll_view_t> {
+ public:
+  CefScrollViewCppToC();
+  virtual ~CefScrollViewCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_SCROLL_VIEW_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/textfield_cpptoc.cc b/src/libcef_dll/cpptoc/views/textfield_cpptoc.cc
new file mode 100644
index 0000000..ff4b01c
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/textfield_cpptoc.cc
@@ -0,0 +1,1619 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9ee756d7e93888230541b494da5559d736260b5c$
+//
+
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_textfield_t* cef_textfield_create(
+    cef_textfield_delegate_t* delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefTextfield::CreateTextfield(CefTextfieldDelegateCToCpp::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK textfield_set_password_input(struct _cef_textfield_t* self,
+                                               int password_input) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetPasswordInput(password_input ? true
+                                                                 : false);
+}
+
+int CEF_CALLBACK textfield_is_password_input(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTextfieldCppToC::Get(self)->IsPasswordInput();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_read_only(struct _cef_textfield_t* self,
+                                          int read_only) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetReadOnly(read_only ? true : false);
+}
+
+int CEF_CALLBACK textfield_is_read_only(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTextfieldCppToC::Get(self)->IsReadOnly();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+textfield_get_text(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefTextfieldCppToC::Get(self)->GetText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK textfield_set_text(struct _cef_textfield_t* self,
+                                     const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetText(CefString(text));
+}
+
+void CEF_CALLBACK textfield_append_text(struct _cef_textfield_t* self,
+                                        const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->AppendText(CefString(text));
+}
+
+void CEF_CALLBACK
+textfield_insert_or_replace_text(struct _cef_textfield_t* self,
+                                 const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->InsertOrReplaceText(CefString(text));
+}
+
+int CEF_CALLBACK textfield_has_selection(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTextfieldCppToC::Get(self)->HasSelection();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+textfield_get_selected_text(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefTextfieldCppToC::Get(self)->GetSelectedText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK textfield_select_all(struct _cef_textfield_t* self,
+                                       int reversed) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SelectAll(reversed ? true : false);
+}
+
+void CEF_CALLBACK textfield_clear_selection(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->ClearSelection();
+}
+
+cef_range_t CEF_CALLBACK
+textfield_get_selected_range(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRange();
+
+  // Execute
+  cef_range_t _retval = CefTextfieldCppToC::Get(self)->GetSelectedRange();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_select_range(struct _cef_textfield_t* self,
+                                         const cef_range_t* range) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: range; type: simple_byref_const
+  DCHECK(range);
+  if (!range)
+    return;
+
+  // Translate param: range; type: simple_byref_const
+  CefRange rangeVal = range ? *range : CefRange();
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SelectRange(rangeVal);
+}
+
+size_t CEF_CALLBACK
+textfield_get_cursor_position(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefTextfieldCppToC::Get(self)->GetCursorPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_text_color(struct _cef_textfield_t* self,
+                                           cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetTextColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+textfield_get_text_color(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval = CefTextfieldCppToC::Get(self)->GetTextColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+textfield_set_selection_text_color(struct _cef_textfield_t* self,
+                                   cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetSelectionTextColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+textfield_get_selection_text_color(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval = CefTextfieldCppToC::Get(self)->GetSelectionTextColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+textfield_set_selection_background_color(struct _cef_textfield_t* self,
+                                         cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetSelectionBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+textfield_get_selection_background_color(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefTextfieldCppToC::Get(self)->GetSelectionBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_font_list(struct _cef_textfield_t* self,
+                                          const cef_string_t* font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: font_list; type: string_byref_const
+  DCHECK(font_list);
+  if (!font_list)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetFontList(CefString(font_list));
+}
+
+void CEF_CALLBACK textfield_apply_text_color(struct _cef_textfield_t* self,
+                                             cef_color_t color,
+                                             const cef_range_t* range) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: range; type: simple_byref_const
+  DCHECK(range);
+  if (!range)
+    return;
+
+  // Translate param: range; type: simple_byref_const
+  CefRange rangeVal = range ? *range : CefRange();
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->ApplyTextColor(color, rangeVal);
+}
+
+void CEF_CALLBACK textfield_apply_text_style(struct _cef_textfield_t* self,
+                                             cef_text_style_t style,
+                                             int add,
+                                             const cef_range_t* range) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: range; type: simple_byref_const
+  DCHECK(range);
+  if (!range)
+    return;
+
+  // Translate param: range; type: simple_byref_const
+  CefRange rangeVal = range ? *range : CefRange();
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->ApplyTextStyle(style, add ? true : false,
+                                                rangeVal);
+}
+
+int CEF_CALLBACK textfield_is_command_enabled(struct _cef_textfield_t* self,
+                                              int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefTextfieldCppToC::Get(self)->IsCommandEnabled(command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_execute_command(struct _cef_textfield_t* self,
+                                            int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->ExecuteCommand(command_id);
+}
+
+void CEF_CALLBACK textfield_clear_edit_history(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->ClearEditHistory();
+}
+
+void CEF_CALLBACK textfield_set_placeholder_text(struct _cef_textfield_t* self,
+                                                 const cef_string_t* text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetPlaceholderText(CefString(text));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+textfield_get_placeholder_text(struct _cef_textfield_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefTextfieldCppToC::Get(self)->GetPlaceholderText();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK
+textfield_set_placeholder_text_color(struct _cef_textfield_t* self,
+                                     cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetPlaceholderTextColor(color);
+}
+
+void CEF_CALLBACK textfield_set_accessible_name(struct _cef_textfield_t* self,
+                                                const cef_string_t* name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: name; type: string_byref_const
+  DCHECK(name);
+  if (!name)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(self)->SetAccessibleName(CefString(name));
+}
+
+cef_browser_view_t* CEF_CALLBACK
+textfield_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK textfield_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK textfield_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK
+textfield_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK textfield_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+textfield_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK textfield_to_string(struct _cef_view_t* self,
+                                                       int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK textfield_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_is_same(struct _cef_view_t* self,
+                                   struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+textfield_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK
+textfield_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK textfield_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))->SetID(id);
+}
+
+int CEF_CALLBACK textfield_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_group_id(struct _cef_view_t* self,
+                                         int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+textfield_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+textfield_get_view_for_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK textfield_set_bounds(struct _cef_view_t* self,
+                                       const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK textfield_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK
+textfield_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_size(struct _cef_view_t* self,
+                                     const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK textfield_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_position(struct _cef_view_t* self,
+                                         const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK textfield_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK textfield_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK textfield_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK textfield_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_get_height_for_width(struct _cef_view_t* self,
+                                                int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->InvalidateLayout();
+}
+
+void CEF_CALLBACK textfield_set_visible(struct _cef_view_t* self, int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK textfield_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_enabled(struct _cef_view_t* self, int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK textfield_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_set_focusable(struct _cef_view_t* self,
+                                          int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK textfield_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+textfield_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK textfield_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->RequestFocus();
+}
+
+void CEF_CALLBACK textfield_set_background_color(struct _cef_view_t* self,
+                                                 cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK
+textfield_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_convert_point_to_screen(struct _cef_view_t* self,
+                                                   cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_convert_point_from_screen(struct _cef_view_t* self,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_convert_point_to_window(struct _cef_view_t* self,
+                                                   cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_convert_point_from_window(struct _cef_view_t* self,
+                                                     cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_convert_point_to_view(struct _cef_view_t* self,
+                                                 struct _cef_view_t* view,
+                                                 cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK textfield_convert_point_from_view(struct _cef_view_t* self,
+                                                   struct _cef_view_t* view,
+                                                   cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefTextfieldCppToC::Get(reinterpret_cast<cef_textfield_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTextfieldCppToC::CefTextfieldCppToC() {
+  GetStruct()->set_password_input = textfield_set_password_input;
+  GetStruct()->is_password_input = textfield_is_password_input;
+  GetStruct()->set_read_only = textfield_set_read_only;
+  GetStruct()->is_read_only = textfield_is_read_only;
+  GetStruct()->get_text = textfield_get_text;
+  GetStruct()->set_text = textfield_set_text;
+  GetStruct()->append_text = textfield_append_text;
+  GetStruct()->insert_or_replace_text = textfield_insert_or_replace_text;
+  GetStruct()->has_selection = textfield_has_selection;
+  GetStruct()->get_selected_text = textfield_get_selected_text;
+  GetStruct()->select_all = textfield_select_all;
+  GetStruct()->clear_selection = textfield_clear_selection;
+  GetStruct()->get_selected_range = textfield_get_selected_range;
+  GetStruct()->select_range = textfield_select_range;
+  GetStruct()->get_cursor_position = textfield_get_cursor_position;
+  GetStruct()->set_text_color = textfield_set_text_color;
+  GetStruct()->get_text_color = textfield_get_text_color;
+  GetStruct()->set_selection_text_color = textfield_set_selection_text_color;
+  GetStruct()->get_selection_text_color = textfield_get_selection_text_color;
+  GetStruct()->set_selection_background_color =
+      textfield_set_selection_background_color;
+  GetStruct()->get_selection_background_color =
+      textfield_get_selection_background_color;
+  GetStruct()->set_font_list = textfield_set_font_list;
+  GetStruct()->apply_text_color = textfield_apply_text_color;
+  GetStruct()->apply_text_style = textfield_apply_text_style;
+  GetStruct()->is_command_enabled = textfield_is_command_enabled;
+  GetStruct()->execute_command = textfield_execute_command;
+  GetStruct()->clear_edit_history = textfield_clear_edit_history;
+  GetStruct()->set_placeholder_text = textfield_set_placeholder_text;
+  GetStruct()->get_placeholder_text = textfield_get_placeholder_text;
+  GetStruct()->set_placeholder_text_color =
+      textfield_set_placeholder_text_color;
+  GetStruct()->set_accessible_name = textfield_set_accessible_name;
+  GetStruct()->base.as_browser_view = textfield_as_browser_view;
+  GetStruct()->base.as_button = textfield_as_button;
+  GetStruct()->base.as_panel = textfield_as_panel;
+  GetStruct()->base.as_scroll_view = textfield_as_scroll_view;
+  GetStruct()->base.as_textfield = textfield_as_textfield;
+  GetStruct()->base.get_type_string = textfield_get_type_string;
+  GetStruct()->base.to_string = textfield_to_string;
+  GetStruct()->base.is_valid = textfield_is_valid;
+  GetStruct()->base.is_attached = textfield_is_attached;
+  GetStruct()->base.is_same = textfield_is_same;
+  GetStruct()->base.get_delegate = textfield_get_delegate;
+  GetStruct()->base.get_window = textfield_get_window;
+  GetStruct()->base.get_id = textfield_get_id;
+  GetStruct()->base.set_id = textfield_set_id;
+  GetStruct()->base.get_group_id = textfield_get_group_id;
+  GetStruct()->base.set_group_id = textfield_set_group_id;
+  GetStruct()->base.get_parent_view = textfield_get_parent_view;
+  GetStruct()->base.get_view_for_id = textfield_get_view_for_id;
+  GetStruct()->base.set_bounds = textfield_set_bounds;
+  GetStruct()->base.get_bounds = textfield_get_bounds;
+  GetStruct()->base.get_bounds_in_screen = textfield_get_bounds_in_screen;
+  GetStruct()->base.set_size = textfield_set_size;
+  GetStruct()->base.get_size = textfield_get_size;
+  GetStruct()->base.set_position = textfield_set_position;
+  GetStruct()->base.get_position = textfield_get_position;
+  GetStruct()->base.get_preferred_size = textfield_get_preferred_size;
+  GetStruct()->base.size_to_preferred_size = textfield_size_to_preferred_size;
+  GetStruct()->base.get_minimum_size = textfield_get_minimum_size;
+  GetStruct()->base.get_maximum_size = textfield_get_maximum_size;
+  GetStruct()->base.get_height_for_width = textfield_get_height_for_width;
+  GetStruct()->base.invalidate_layout = textfield_invalidate_layout;
+  GetStruct()->base.set_visible = textfield_set_visible;
+  GetStruct()->base.is_visible = textfield_is_visible;
+  GetStruct()->base.is_drawn = textfield_is_drawn;
+  GetStruct()->base.set_enabled = textfield_set_enabled;
+  GetStruct()->base.is_enabled = textfield_is_enabled;
+  GetStruct()->base.set_focusable = textfield_set_focusable;
+  GetStruct()->base.is_focusable = textfield_is_focusable;
+  GetStruct()->base.is_accessibility_focusable =
+      textfield_is_accessibility_focusable;
+  GetStruct()->base.request_focus = textfield_request_focus;
+  GetStruct()->base.set_background_color = textfield_set_background_color;
+  GetStruct()->base.get_background_color = textfield_get_background_color;
+  GetStruct()->base.convert_point_to_screen = textfield_convert_point_to_screen;
+  GetStruct()->base.convert_point_from_screen =
+      textfield_convert_point_from_screen;
+  GetStruct()->base.convert_point_to_window = textfield_convert_point_to_window;
+  GetStruct()->base.convert_point_from_window =
+      textfield_convert_point_from_window;
+  GetStruct()->base.convert_point_to_view = textfield_convert_point_to_view;
+  GetStruct()->base.convert_point_from_view = textfield_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTextfieldCppToC::~CefTextfieldCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTextfield>
+CefCppToCRefCounted<CefTextfieldCppToC, CefTextfield, cef_textfield_t>::
+    UnwrapDerived(CefWrapperType type, cef_textfield_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefTextfieldCppToC,
+                                   CefTextfield,
+                                   cef_textfield_t>::kWrapperType =
+    WT_TEXTFIELD;
diff --git a/src/libcef_dll/cpptoc/views/textfield_cpptoc.h b/src/libcef_dll/cpptoc/views/textfield_cpptoc.h
new file mode 100644
index 0000000..3c2e9dc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/textfield_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=93c32090811a83e148622725bdc21914c665f72e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_TEXTFIELD_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_TEXTFIELD_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_textfield_capi.h"
+#include "include/views/cef_textfield.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefTextfieldCppToC : public CefCppToCRefCounted<CefTextfieldCppToC,
+                                                      CefTextfield,
+                                                      cef_textfield_t> {
+ public:
+  CefTextfieldCppToC();
+  virtual ~CefTextfieldCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_TEXTFIELD_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/textfield_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/views/textfield_delegate_cpptoc.cc
new file mode 100644
index 0000000..da860f7
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/textfield_delegate_cpptoc.cc
@@ -0,0 +1,310 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5c2d33803cefd442d3995d06f2fb0e8adc26aa4d$
+//
+
+#include "libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+textfield_delegate_on_key_event(struct _cef_textfield_delegate_t* self,
+                                cef_textfield_t* textfield,
+                                const struct _cef_key_event_t* event) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: textfield; type: refptr_diff
+  DCHECK(textfield);
+  if (!textfield)
+    return 0;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return 0;
+
+  // Translate param: event; type: struct_byref_const
+  CefKeyEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  bool _retval = CefTextfieldDelegateCppToC::Get(self)->OnKeyEvent(
+      CefTextfieldCToCpp::Wrap(textfield), eventObj);
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK
+textfield_delegate_on_after_user_action(struct _cef_textfield_delegate_t* self,
+                                        cef_textfield_t* textfield) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: textfield; type: refptr_diff
+  DCHECK(textfield);
+  if (!textfield)
+    return;
+
+  // Execute
+  CefTextfieldDelegateCppToC::Get(self)->OnAfterUserAction(
+      CefTextfieldCToCpp::Wrap(textfield));
+}
+
+cef_size_t CEF_CALLBACK
+textfield_delegate_get_preferred_size(struct _cef_view_delegate_t* self,
+                                      cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefTextfieldDelegateCppToC::Get(
+                           reinterpret_cast<cef_textfield_delegate_t*>(self))
+                           ->GetPreferredSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+textfield_delegate_get_minimum_size(struct _cef_view_delegate_t* self,
+                                    cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefTextfieldDelegateCppToC::Get(
+                           reinterpret_cast<cef_textfield_delegate_t*>(self))
+                           ->GetMinimumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+textfield_delegate_get_maximum_size(struct _cef_view_delegate_t* self,
+                                    cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefTextfieldDelegateCppToC::Get(
+                           reinterpret_cast<cef_textfield_delegate_t*>(self))
+                           ->GetMaximumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+textfield_delegate_get_height_for_width(struct _cef_view_delegate_t* self,
+                                        cef_view_t* view,
+                                        int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return 0;
+
+  // Execute
+  int _retval = CefTextfieldDelegateCppToC::Get(
+                    reinterpret_cast<cef_textfield_delegate_t*>(self))
+                    ->GetHeightForWidth(CefViewCToCpp::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+textfield_delegate_on_parent_view_changed(struct _cef_view_delegate_t* self,
+                                          cef_view_t* view,
+                                          int added,
+                                          cef_view_t* parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent);
+  if (!parent)
+    return;
+
+  // Execute
+  CefTextfieldDelegateCppToC::Get(
+      reinterpret_cast<cef_textfield_delegate_t*>(self))
+      ->OnParentViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                            CefViewCToCpp::Wrap(parent));
+}
+
+void CEF_CALLBACK
+textfield_delegate_on_child_view_changed(struct _cef_view_delegate_t* self,
+                                         cef_view_t* view,
+                                         int added,
+                                         cef_view_t* child) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child);
+  if (!child)
+    return;
+
+  // Execute
+  CefTextfieldDelegateCppToC::Get(
+      reinterpret_cast<cef_textfield_delegate_t*>(self))
+      ->OnChildViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                           CefViewCToCpp::Wrap(child));
+}
+
+void CEF_CALLBACK textfield_delegate_on_focus(struct _cef_view_delegate_t* self,
+                                              cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefTextfieldDelegateCppToC::Get(
+      reinterpret_cast<cef_textfield_delegate_t*>(self))
+      ->OnFocus(CefViewCToCpp::Wrap(view));
+}
+
+void CEF_CALLBACK textfield_delegate_on_blur(struct _cef_view_delegate_t* self,
+                                             cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefTextfieldDelegateCppToC::Get(
+      reinterpret_cast<cef_textfield_delegate_t*>(self))
+      ->OnBlur(CefViewCToCpp::Wrap(view));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTextfieldDelegateCppToC::CefTextfieldDelegateCppToC() {
+  GetStruct()->on_key_event = textfield_delegate_on_key_event;
+  GetStruct()->on_after_user_action = textfield_delegate_on_after_user_action;
+  GetStruct()->base.get_preferred_size = textfield_delegate_get_preferred_size;
+  GetStruct()->base.get_minimum_size = textfield_delegate_get_minimum_size;
+  GetStruct()->base.get_maximum_size = textfield_delegate_get_maximum_size;
+  GetStruct()->base.get_height_for_width =
+      textfield_delegate_get_height_for_width;
+  GetStruct()->base.on_parent_view_changed =
+      textfield_delegate_on_parent_view_changed;
+  GetStruct()->base.on_child_view_changed =
+      textfield_delegate_on_child_view_changed;
+  GetStruct()->base.on_focus = textfield_delegate_on_focus;
+  GetStruct()->base.on_blur = textfield_delegate_on_blur;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTextfieldDelegateCppToC::~CefTextfieldDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefTextfieldDelegate> CefCppToCRefCounted<
+    CefTextfieldDelegateCppToC,
+    CefTextfieldDelegate,
+    cef_textfield_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                             cef_textfield_delegate_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefTextfieldDelegateCppToC,
+                                   CefTextfieldDelegate,
+                                   cef_textfield_delegate_t>::kWrapperType =
+    WT_TEXTFIELD_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h b/src/libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h
new file mode 100644
index 0000000..4d2513a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=846e39b812ceed2719b128433890e0706bf9f439$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_TEXTFIELD_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_TEXTFIELD_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_textfield_capi.h"
+#include "include/capi/views/cef_textfield_delegate_capi.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_textfield_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTextfieldDelegateCppToC
+    : public CefCppToCRefCounted<CefTextfieldDelegateCppToC,
+                                 CefTextfieldDelegate,
+                                 cef_textfield_delegate_t> {
+ public:
+  CefTextfieldDelegateCppToC();
+  virtual ~CefTextfieldDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_TEXTFIELD_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/view_cpptoc.cc b/src/libcef_dll/cpptoc/views/view_cpptoc.cc
new file mode 100644
index 0000000..f61483b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/view_cpptoc.cc
@@ -0,0 +1,984 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cdddd584b0d4f91a93f0f635023d7d21040b7ade$
+//
+
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/label_button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/menu_button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_browser_view_t* CEF_CALLBACK
+view_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval = CefViewCppToC::Get(self)->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK view_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval = CefViewCppToC::Get(self)->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK view_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval = CefViewCppToC::Get(self)->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK view_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval = CefViewCppToC::Get(self)->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK view_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval = CefViewCppToC::Get(self)->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+view_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefViewCppToC::Get(self)->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK view_to_string(struct _cef_view_t* self,
+                                                  int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefViewCppToC::Get(self)->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK view_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_is_same(struct _cef_view_t* self,
+                              struct _cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+struct _cef_view_delegate_t* CEF_CALLBACK
+view_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval = CefViewCppToC::Get(self)->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK view_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval = CefViewCppToC::Get(self)->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK view_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefViewCppToC::Get(self)->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK view_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->SetID(id);
+}
+
+int CEF_CALLBACK view_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefViewCppToC::Get(self)->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK view_set_group_id(struct _cef_view_t* self, int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->SetGroupID(group_id);
+}
+
+struct _cef_view_t* CEF_CALLBACK
+view_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval = CefViewCppToC::Get(self)->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+struct _cef_view_t* CEF_CALLBACK view_get_view_for_id(struct _cef_view_t* self,
+                                                      int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval = CefViewCppToC::Get(self)->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK view_set_bounds(struct _cef_view_t* self,
+                                  const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefViewCppToC::Get(self)->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK view_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval = CefViewCppToC::Get(self)->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK view_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval = CefViewCppToC::Get(self)->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK view_set_size(struct _cef_view_t* self,
+                                const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefViewCppToC::Get(self)->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK view_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefViewCppToC::Get(self)->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK view_set_position(struct _cef_view_t* self,
+                                    const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefViewCppToC::Get(self)->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK view_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval = CefViewCppToC::Get(self)->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK view_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefViewCppToC::Get(self)->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK view_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK view_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefViewCppToC::Get(self)->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK view_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefViewCppToC::Get(self)->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK view_get_height_for_width(struct _cef_view_t* self,
+                                           int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefViewCppToC::Get(self)->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK view_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->InvalidateLayout();
+}
+
+void CEF_CALLBACK view_set_visible(struct _cef_view_t* self, int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK view_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK view_set_enabled(struct _cef_view_t* self, int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK view_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK view_set_focusable(struct _cef_view_t* self, int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK view_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK view_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->RequestFocus();
+}
+
+void CEF_CALLBACK view_set_background_color(struct _cef_view_t* self,
+                                            cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefViewCppToC::Get(self)->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK view_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval = CefViewCppToC::Get(self)->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK view_convert_point_to_screen(struct _cef_view_t* self,
+                                              cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_convert_point_from_screen(struct _cef_view_t* self,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_convert_point_to_window(struct _cef_view_t* self,
+                                              cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_convert_point_from_window(struct _cef_view_t* self,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_convert_point_to_view(struct _cef_view_t* self,
+                                            struct _cef_view_t* view,
+                                            cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->ConvertPointToView(
+      CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK view_convert_point_from_view(struct _cef_view_t* self,
+                                              struct _cef_view_t* view,
+                                              cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefViewCppToC::Get(self)->ConvertPointFromView(
+      CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefViewCppToC::CefViewCppToC() {
+  GetStruct()->as_browser_view = view_as_browser_view;
+  GetStruct()->as_button = view_as_button;
+  GetStruct()->as_panel = view_as_panel;
+  GetStruct()->as_scroll_view = view_as_scroll_view;
+  GetStruct()->as_textfield = view_as_textfield;
+  GetStruct()->get_type_string = view_get_type_string;
+  GetStruct()->to_string = view_to_string;
+  GetStruct()->is_valid = view_is_valid;
+  GetStruct()->is_attached = view_is_attached;
+  GetStruct()->is_same = view_is_same;
+  GetStruct()->get_delegate = view_get_delegate;
+  GetStruct()->get_window = view_get_window;
+  GetStruct()->get_id = view_get_id;
+  GetStruct()->set_id = view_set_id;
+  GetStruct()->get_group_id = view_get_group_id;
+  GetStruct()->set_group_id = view_set_group_id;
+  GetStruct()->get_parent_view = view_get_parent_view;
+  GetStruct()->get_view_for_id = view_get_view_for_id;
+  GetStruct()->set_bounds = view_set_bounds;
+  GetStruct()->get_bounds = view_get_bounds;
+  GetStruct()->get_bounds_in_screen = view_get_bounds_in_screen;
+  GetStruct()->set_size = view_set_size;
+  GetStruct()->get_size = view_get_size;
+  GetStruct()->set_position = view_set_position;
+  GetStruct()->get_position = view_get_position;
+  GetStruct()->get_preferred_size = view_get_preferred_size;
+  GetStruct()->size_to_preferred_size = view_size_to_preferred_size;
+  GetStruct()->get_minimum_size = view_get_minimum_size;
+  GetStruct()->get_maximum_size = view_get_maximum_size;
+  GetStruct()->get_height_for_width = view_get_height_for_width;
+  GetStruct()->invalidate_layout = view_invalidate_layout;
+  GetStruct()->set_visible = view_set_visible;
+  GetStruct()->is_visible = view_is_visible;
+  GetStruct()->is_drawn = view_is_drawn;
+  GetStruct()->set_enabled = view_set_enabled;
+  GetStruct()->is_enabled = view_is_enabled;
+  GetStruct()->set_focusable = view_set_focusable;
+  GetStruct()->is_focusable = view_is_focusable;
+  GetStruct()->is_accessibility_focusable = view_is_accessibility_focusable;
+  GetStruct()->request_focus = view_request_focus;
+  GetStruct()->set_background_color = view_set_background_color;
+  GetStruct()->get_background_color = view_get_background_color;
+  GetStruct()->convert_point_to_screen = view_convert_point_to_screen;
+  GetStruct()->convert_point_from_screen = view_convert_point_from_screen;
+  GetStruct()->convert_point_to_window = view_convert_point_to_window;
+  GetStruct()->convert_point_from_window = view_convert_point_from_window;
+  GetStruct()->convert_point_to_view = view_convert_point_to_view;
+  GetStruct()->convert_point_from_view = view_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefViewCppToC::~CefViewCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefView>
+CefCppToCRefCounted<CefViewCppToC, CefView, cef_view_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_view_t* s) {
+  if (type == WT_BROWSER_VIEW) {
+    return CefBrowserViewCppToC::Unwrap(
+        reinterpret_cast<cef_browser_view_t*>(s));
+  }
+  if (type == WT_BUTTON) {
+    return CefButtonCppToC::Unwrap(reinterpret_cast<cef_button_t*>(s));
+  }
+  if (type == WT_LABEL_BUTTON) {
+    return CefLabelButtonCppToC::Unwrap(
+        reinterpret_cast<cef_label_button_t*>(s));
+  }
+  if (type == WT_MENU_BUTTON) {
+    return CefMenuButtonCppToC::Unwrap(reinterpret_cast<cef_menu_button_t*>(s));
+  }
+  if (type == WT_PANEL) {
+    return CefPanelCppToC::Unwrap(reinterpret_cast<cef_panel_t*>(s));
+  }
+  if (type == WT_SCROLL_VIEW) {
+    return CefScrollViewCppToC::Unwrap(reinterpret_cast<cef_scroll_view_t*>(s));
+  }
+  if (type == WT_TEXTFIELD) {
+    return CefTextfieldCppToC::Unwrap(reinterpret_cast<cef_textfield_t*>(s));
+  }
+  if (type == WT_WINDOW) {
+    return CefWindowCppToC::Unwrap(reinterpret_cast<cef_window_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefViewCppToC, CefView, cef_view_t>::kWrapperType =
+        WT_VIEW;
diff --git a/src/libcef_dll/cpptoc/views/view_cpptoc.h b/src/libcef_dll/cpptoc/views/view_cpptoc.h
new file mode 100644
index 0000000..097fff5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/view_cpptoc.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8154a3c9ac11598bdc5e473d0b4f6660e041f31b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_VIEW_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_VIEW_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_browser_view_capi.h"
+#include "include/capi/views/cef_button_capi.h"
+#include "include/capi/views/cef_panel_capi.h"
+#include "include/capi/views/cef_scroll_view_capi.h"
+#include "include/capi/views/cef_textfield_capi.h"
+#include "include/capi/views/cef_view_capi.h"
+#include "include/capi/views/cef_window_capi.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_scroll_view.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_view.h"
+#include "include/views/cef_window.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefViewCppToC
+    : public CefCppToCRefCounted<CefViewCppToC, CefView, cef_view_t> {
+ public:
+  CefViewCppToC();
+  virtual ~CefViewCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_VIEW_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/view_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/views/view_delegate_cpptoc.cc
new file mode 100644
index 0000000..64f6f03
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/view_delegate_cpptoc.cc
@@ -0,0 +1,271 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f29ee71b497bc4f8ec55026e1e017d3d32dbc493$
+//
+
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_size_t CEF_CALLBACK
+view_delegate_get_preferred_size(struct _cef_view_delegate_t* self,
+                                 cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefViewDelegateCppToC::Get(self)->GetPreferredSize(
+      CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+view_delegate_get_minimum_size(struct _cef_view_delegate_t* self,
+                               cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefViewDelegateCppToC::Get(self)->GetMinimumSize(
+      CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+view_delegate_get_maximum_size(struct _cef_view_delegate_t* self,
+                               cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefViewDelegateCppToC::Get(self)->GetMaximumSize(
+      CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+view_delegate_get_height_for_width(struct _cef_view_delegate_t* self,
+                                   cef_view_t* view,
+                                   int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return 0;
+
+  // Execute
+  int _retval = CefViewDelegateCppToC::Get(self)->GetHeightForWidth(
+      CefViewCToCpp::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+view_delegate_on_parent_view_changed(struct _cef_view_delegate_t* self,
+                                     cef_view_t* view,
+                                     int added,
+                                     cef_view_t* parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent);
+  if (!parent)
+    return;
+
+  // Execute
+  CefViewDelegateCppToC::Get(self)->OnParentViewChanged(
+      CefViewCToCpp::Wrap(view), added ? true : false,
+      CefViewCToCpp::Wrap(parent));
+}
+
+void CEF_CALLBACK
+view_delegate_on_child_view_changed(struct _cef_view_delegate_t* self,
+                                    cef_view_t* view,
+                                    int added,
+                                    cef_view_t* child) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child);
+  if (!child)
+    return;
+
+  // Execute
+  CefViewDelegateCppToC::Get(self)->OnChildViewChanged(
+      CefViewCToCpp::Wrap(view), added ? true : false,
+      CefViewCToCpp::Wrap(child));
+}
+
+void CEF_CALLBACK view_delegate_on_focus(struct _cef_view_delegate_t* self,
+                                         cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefViewDelegateCppToC::Get(self)->OnFocus(CefViewCToCpp::Wrap(view));
+}
+
+void CEF_CALLBACK view_delegate_on_blur(struct _cef_view_delegate_t* self,
+                                        cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefViewDelegateCppToC::Get(self)->OnBlur(CefViewCToCpp::Wrap(view));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefViewDelegateCppToC::CefViewDelegateCppToC() {
+  GetStruct()->get_preferred_size = view_delegate_get_preferred_size;
+  GetStruct()->get_minimum_size = view_delegate_get_minimum_size;
+  GetStruct()->get_maximum_size = view_delegate_get_maximum_size;
+  GetStruct()->get_height_for_width = view_delegate_get_height_for_width;
+  GetStruct()->on_parent_view_changed = view_delegate_on_parent_view_changed;
+  GetStruct()->on_child_view_changed = view_delegate_on_child_view_changed;
+  GetStruct()->on_focus = view_delegate_on_focus;
+  GetStruct()->on_blur = view_delegate_on_blur;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefViewDelegateCppToC::~CefViewDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefViewDelegate> CefCppToCRefCounted<
+    CefViewDelegateCppToC,
+    CefViewDelegate,
+    cef_view_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_view_delegate_t* s) {
+  if (type == WT_BROWSER_VIEW_DELEGATE) {
+    return CefBrowserViewDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_browser_view_delegate_t*>(s));
+  }
+  if (type == WT_BUTTON_DELEGATE) {
+    return CefButtonDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_button_delegate_t*>(s));
+  }
+  if (type == WT_MENU_BUTTON_DELEGATE) {
+    return CefMenuButtonDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_menu_button_delegate_t*>(s));
+  }
+  if (type == WT_PANEL_DELEGATE) {
+    return CefPanelDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_panel_delegate_t*>(s));
+  }
+  if (type == WT_TEXTFIELD_DELEGATE) {
+    return CefTextfieldDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_textfield_delegate_t*>(s));
+  }
+  if (type == WT_WINDOW_DELEGATE) {
+    return CefWindowDelegateCppToC::Unwrap(
+        reinterpret_cast<cef_window_delegate_t*>(s));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefViewDelegateCppToC,
+                                   CefViewDelegate,
+                                   cef_view_delegate_t>::kWrapperType =
+    WT_VIEW_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/views/view_delegate_cpptoc.h b/src/libcef_dll/cpptoc/views/view_delegate_cpptoc.h
new file mode 100644
index 0000000..e5f630d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/view_delegate_cpptoc.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bea8e33cf1ccef0c18bdb548042f18c659528567$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_VIEW_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_VIEW_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_view_capi.h"
+#include "include/capi/views/cef_view_delegate_capi.h"
+#include "include/views/cef_view.h"
+#include "include/views/cef_view_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefViewDelegateCppToC : public CefCppToCRefCounted<CefViewDelegateCppToC,
+                                                         CefViewDelegate,
+                                                         cef_view_delegate_t> {
+ public:
+  CefViewDelegateCppToC();
+  virtual ~CefViewDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_VIEW_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/window_cpptoc.cc b/src/libcef_dll/cpptoc/views/window_cpptoc.cc
new file mode 100644
index 0000000..3e953dd
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/window_cpptoc.cc
@@ -0,0 +1,1899 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9543a25fe57ff1139ac9a7fb9df7f6cf272381e1$
+//
+
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/cpptoc/image_cpptoc.h"
+#include "libcef_dll/cpptoc/menu_model_cpptoc.h"
+#include "libcef_dll/cpptoc/views/box_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/display_cpptoc.h"
+#include "libcef_dll/cpptoc/views/fill_layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/layout_cpptoc.h"
+#include "libcef_dll/cpptoc/views/panel_cpptoc.h"
+#include "libcef_dll/cpptoc/views/scroll_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_window_t* cef_window_create_top_level(
+    struct _cef_window_delegate_t* delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefWindow::CreateTopLevelWindow(CefWindowDelegateCToCpp::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK window_show(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Show();
+}
+
+void CEF_CALLBACK window_hide(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Hide();
+}
+
+void CEF_CALLBACK window_center_window(struct _cef_window_t* self,
+                                       const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefWindowCppToC::Get(self)->CenterWindow(sizeVal);
+}
+
+void CEF_CALLBACK window_close(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Close();
+}
+
+int CEF_CALLBACK window_is_closed(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(self)->IsClosed();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK window_activate(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Activate();
+}
+
+void CEF_CALLBACK window_deactivate(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Deactivate();
+}
+
+int CEF_CALLBACK window_is_active(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(self)->IsActive();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK window_bring_to_top(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->BringToTop();
+}
+
+void CEF_CALLBACK window_set_always_on_top(struct _cef_window_t* self,
+                                           int on_top) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SetAlwaysOnTop(on_top ? true : false);
+}
+
+int CEF_CALLBACK window_is_always_on_top(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(self)->IsAlwaysOnTop();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK window_maximize(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Maximize();
+}
+
+void CEF_CALLBACK window_minimize(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Minimize();
+}
+
+void CEF_CALLBACK window_restore(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->Restore();
+}
+
+void CEF_CALLBACK window_set_fullscreen(struct _cef_window_t* self,
+                                        int fullscreen) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SetFullscreen(fullscreen ? true : false);
+}
+
+int CEF_CALLBACK window_is_maximized(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(self)->IsMaximized();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_is_minimized(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(self)->IsMinimized();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_is_fullscreen(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(self)->IsFullscreen();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK window_set_title(struct _cef_window_t* self,
+                                   const cef_string_t* title) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: title
+
+  // Execute
+  CefWindowCppToC::Get(self)->SetTitle(CefString(title));
+}
+
+cef_string_userfree_t CEF_CALLBACK
+window_get_title(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefWindowCppToC::Get(self)->GetTitle();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK window_set_window_icon(struct _cef_window_t* self,
+                                         cef_image_t* image) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: image; type: refptr_same
+  DCHECK(image);
+  if (!image)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SetWindowIcon(CefImageCppToC::Unwrap(image));
+}
+
+cef_image_t* CEF_CALLBACK window_get_window_icon(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefImage> _retval = CefWindowCppToC::Get(self)->GetWindowIcon();
+
+  // Return type: refptr_same
+  return CefImageCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK window_set_window_app_icon(struct _cef_window_t* self,
+                                             cef_image_t* image) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: image; type: refptr_same
+  DCHECK(image);
+  if (!image)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SetWindowAppIcon(CefImageCppToC::Unwrap(image));
+}
+
+cef_image_t* CEF_CALLBACK
+window_get_window_app_icon(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefImage> _retval = CefWindowCppToC::Get(self)->GetWindowAppIcon();
+
+  // Return type: refptr_same
+  return CefImageCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK window_show_menu(struct _cef_window_t* self,
+                                   cef_menu_model_t* menu_model,
+                                   const cef_point_t* screen_point,
+                                   cef_menu_anchor_position_t anchor_position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: menu_model; type: refptr_same
+  DCHECK(menu_model);
+  if (!menu_model)
+    return;
+  // Verify param: screen_point; type: simple_byref_const
+  DCHECK(screen_point);
+  if (!screen_point)
+    return;
+
+  // Translate param: screen_point; type: simple_byref_const
+  CefPoint screen_pointVal = screen_point ? *screen_point : CefPoint();
+
+  // Execute
+  CefWindowCppToC::Get(self)->ShowMenu(CefMenuModelCppToC::Unwrap(menu_model),
+                                       screen_pointVal, anchor_position);
+}
+
+void CEF_CALLBACK window_cancel_menu(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->CancelMenu();
+}
+
+cef_display_t* CEF_CALLBACK window_get_display(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefDisplay> _retval = CefWindowCppToC::Get(self)->GetDisplay();
+
+  // Return type: refptr_same
+  return CefDisplayCppToC::Wrap(_retval);
+}
+
+cef_rect_t CEF_CALLBACK
+window_get_client_area_bounds_in_screen(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefWindowCppToC::Get(self)->GetClientAreaBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+window_set_draggable_regions(struct _cef_window_t* self,
+                             size_t regionsCount,
+                             cef_draggable_region_t const* regions) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Unverified params: regions
+
+  // Translate param: regions; type: simple_vec_byref_const
+  std::vector<CefDraggableRegion> regionsList;
+  if (regionsCount > 0) {
+    for (size_t i = 0; i < regionsCount; ++i) {
+      CefDraggableRegion regionsVal = regions[i];
+      regionsList.push_back(regionsVal);
+    }
+  }
+
+  // Execute
+  CefWindowCppToC::Get(self)->SetDraggableRegions(regionsList);
+}
+
+cef_window_handle_t CEF_CALLBACK
+window_get_window_handle(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return kNullWindowHandle;
+
+  // Execute
+  cef_window_handle_t _retval = CefWindowCppToC::Get(self)->GetWindowHandle();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK window_send_key_press(struct _cef_window_t* self,
+                                        int key_code,
+                                        uint32 event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SendKeyPress(key_code, event_flags);
+}
+
+void CEF_CALLBACK window_send_mouse_move(struct _cef_window_t* self,
+                                         int screen_x,
+                                         int screen_y) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SendMouseMove(screen_x, screen_y);
+}
+
+void CEF_CALLBACK window_send_mouse_events(struct _cef_window_t* self,
+                                           cef_mouse_button_type_t button,
+                                           int mouse_down,
+                                           int mouse_up) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SendMouseEvents(button, mouse_down ? true : false,
+                                              mouse_up ? true : false);
+}
+
+void CEF_CALLBACK window_set_accelerator(struct _cef_window_t* self,
+                                         int command_id,
+                                         int key_code,
+                                         int shift_pressed,
+                                         int ctrl_pressed,
+                                         int alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->SetAccelerator(
+      command_id, key_code, shift_pressed ? true : false,
+      ctrl_pressed ? true : false, alt_pressed ? true : false);
+}
+
+void CEF_CALLBACK window_remove_accelerator(struct _cef_window_t* self,
+                                            int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->RemoveAccelerator(command_id);
+}
+
+void CEF_CALLBACK window_remove_all_accelerators(struct _cef_window_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(self)->RemoveAllAccelerators();
+}
+
+struct _cef_window_t* CEF_CALLBACK window_as_window(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->AsWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+cef_fill_layout_t* CEF_CALLBACK
+window_set_to_fill_layout(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefFillLayout> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->SetToFillLayout();
+
+  // Return type: refptr_same
+  return CefFillLayoutCppToC::Wrap(_retval);
+}
+
+cef_box_layout_t* CEF_CALLBACK
+window_set_to_box_layout(struct _cef_panel_t* self,
+                         const struct _cef_box_layout_settings_t* settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return NULL;
+
+  // Translate param: settings; type: struct_byref_const
+  CefBoxLayoutSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  CefRefPtr<CefBoxLayout> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->SetToBoxLayout(settingsObj);
+
+  // Return type: refptr_same
+  return CefBoxLayoutCppToC::Wrap(_retval);
+}
+
+cef_layout_t* CEF_CALLBACK window_get_layout(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefLayout> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->GetLayout();
+
+  // Return type: refptr_same
+  return CefLayoutCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK window_layout(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->Layout();
+}
+
+void CEF_CALLBACK window_add_child_view(struct _cef_panel_t* self,
+                                        cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->AddChildView(CefViewCppToC::Unwrap(view));
+}
+
+void CEF_CALLBACK window_add_child_view_at(struct _cef_panel_t* self,
+                                           cef_view_t* view,
+                                           int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->AddChildViewAt(CefViewCppToC::Unwrap(view), index);
+}
+
+void CEF_CALLBACK window_reorder_child_view(struct _cef_panel_t* self,
+                                            cef_view_t* view,
+                                            int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->ReorderChildView(CefViewCppToC::Unwrap(view), index);
+}
+
+void CEF_CALLBACK window_remove_child_view(struct _cef_panel_t* self,
+                                           cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->RemoveChildView(CefViewCppToC::Unwrap(view));
+}
+
+void CEF_CALLBACK window_remove_all_child_views(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->RemoveAllChildViews();
+}
+
+size_t CEF_CALLBACK window_get_child_view_count(struct _cef_panel_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                       ->GetChildViewCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_view_t* CEF_CALLBACK window_get_child_view_at(struct _cef_panel_t* self,
+                                                  int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetChildViewAt(index);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+cef_browser_view_t* CEF_CALLBACK
+window_as_browser_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBrowserView> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->AsBrowserView();
+
+  // Return type: refptr_same
+  return CefBrowserViewCppToC::Wrap(_retval);
+}
+
+cef_button_t* CEF_CALLBACK window_as_button(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefButton> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->AsButton();
+
+  // Return type: refptr_same
+  return CefButtonCppToC::Wrap(_retval);
+}
+
+cef_panel_t* CEF_CALLBACK window_as_panel(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefPanel> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->AsPanel();
+
+  // Return type: refptr_same
+  return CefPanelCppToC::Wrap(_retval);
+}
+
+cef_scroll_view_t* CEF_CALLBACK
+window_as_scroll_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefScrollView> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->AsScrollView();
+
+  // Return type: refptr_same
+  return CefScrollViewCppToC::Wrap(_retval);
+}
+
+cef_textfield_t* CEF_CALLBACK window_as_textfield(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefTextfield> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->AsTextfield();
+
+  // Return type: refptr_same
+  return CefTextfieldCppToC::Wrap(_retval);
+}
+
+cef_string_userfree_t CEF_CALLBACK
+window_get_type_string(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetTypeString();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK window_to_string(struct _cef_view_t* self,
+                                                    int include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->ToString(include_children ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK window_is_valid(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->IsValid();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_is_attached(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->IsAttached();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_is_same(struct _cef_view_t* self, cef_view_t* that) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: that; type: refptr_same
+  DCHECK(that);
+  if (!that)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                     ->IsSame(CefViewCppToC::Unwrap(that));
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_view_delegate_t* CEF_CALLBACK
+window_get_delegate(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefViewDelegate> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetDelegate();
+
+  // Return type: refptr_diff
+  return CefViewDelegateCToCpp::Unwrap(_retval);
+}
+
+struct _cef_window_t* CEF_CALLBACK window_get_window(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->GetWindow();
+
+  // Return type: refptr_same
+  return CefWindowCppToC::Wrap(_retval);
+}
+
+int CEF_CALLBACK window_get_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->GetID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK window_set_id(struct _cef_view_t* self, int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->SetID(id);
+}
+
+int CEF_CALLBACK window_get_group_id(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->GetGroupID();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK window_set_group_id(struct _cef_view_t* self, int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SetGroupID(group_id);
+}
+
+cef_view_t* CEF_CALLBACK window_get_parent_view(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetParentView();
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+cef_view_t* CEF_CALLBACK window_get_view_for_id(struct _cef_view_t* self,
+                                                int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefView> _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetViewForID(id);
+
+  // Return type: refptr_same
+  return CefViewCppToC::Wrap(_retval);
+}
+
+void CEF_CALLBACK window_set_bounds(struct _cef_view_t* self,
+                                    const cef_rect_t* bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: bounds; type: simple_byref_const
+  DCHECK(bounds);
+  if (!bounds)
+    return;
+
+  // Translate param: bounds; type: simple_byref_const
+  CefRect boundsVal = bounds ? *bounds : CefRect();
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SetBounds(boundsVal);
+}
+
+cef_rect_t CEF_CALLBACK window_get_bounds(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->GetBounds();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_rect_t CEF_CALLBACK window_get_bounds_in_screen(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefRect();
+
+  // Execute
+  cef_rect_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetBoundsInScreen();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK window_set_size(struct _cef_view_t* self,
+                                  const cef_size_t* size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: size; type: simple_byref_const
+  DCHECK(size);
+  if (!size)
+    return;
+
+  // Translate param: size; type: simple_byref_const
+  CefSize sizeVal = size ? *size : CefSize();
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->SetSize(sizeVal);
+}
+
+cef_size_t CEF_CALLBACK window_get_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->GetSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK window_set_position(struct _cef_view_t* self,
+                                      const cef_point_t* position) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: position; type: simple_byref_const
+  DCHECK(position);
+  if (!position)
+    return;
+
+  // Translate param: position; type: simple_byref_const
+  CefPoint positionVal = position ? *position : CefPoint();
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SetPosition(positionVal);
+}
+
+cef_point_t CEF_CALLBACK window_get_position(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefPoint();
+
+  // Execute
+  cef_point_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetPosition();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK window_get_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetPreferredSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK window_size_to_preferred_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SizeToPreferredSize();
+}
+
+cef_size_t CEF_CALLBACK window_get_minimum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetMinimumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK window_get_maximum_size(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetMaximumSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK window_get_height_for_width(struct _cef_view_t* self,
+                                             int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                    ->GetHeightForWidth(width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK window_invalidate_layout(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->InvalidateLayout();
+}
+
+void CEF_CALLBACK window_set_visible(struct _cef_view_t* self, int visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SetVisible(visible ? true : false);
+}
+
+int CEF_CALLBACK window_is_visible(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->IsVisible();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_is_drawn(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->IsDrawn();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK window_set_enabled(struct _cef_view_t* self, int enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SetEnabled(enabled ? true : false);
+}
+
+int CEF_CALLBACK window_is_enabled(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->IsEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK window_set_focusable(struct _cef_view_t* self,
+                                       int focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SetFocusable(focusable ? true : false);
+}
+
+int CEF_CALLBACK window_is_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                     ->IsFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_is_accessibility_focusable(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                     ->IsAccessibilityFocusable();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK window_request_focus(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))->RequestFocus();
+}
+
+void CEF_CALLBACK window_set_background_color(struct _cef_view_t* self,
+                                              cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+      ->SetBackgroundColor(color);
+}
+
+cef_color_t CEF_CALLBACK window_get_background_color(struct _cef_view_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  cef_color_t _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->GetBackgroundColor();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK window_convert_point_to_screen(struct _cef_view_t* self,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                     ->ConvertPointToScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_convert_point_from_screen(struct _cef_view_t* self,
+                                                  cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                     ->ConvertPointFromScreen(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_convert_point_to_window(struct _cef_view_t* self,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                     ->ConvertPointToWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_convert_point_from_window(struct _cef_view_t* self,
+                                                  cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval = CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+                     ->ConvertPointFromWindow(pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_convert_point_to_view(struct _cef_view_t* self,
+                                              cef_view_t* view,
+                                              cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->ConvertPointToView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_convert_point_from_view(struct _cef_view_t* self,
+                                                cef_view_t* view,
+                                                cef_point_t* point) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_same
+  DCHECK(view);
+  if (!view)
+    return 0;
+  // Verify param: point; type: simple_byref
+  DCHECK(point);
+  if (!point)
+    return 0;
+
+  // Translate param: point; type: simple_byref
+  CefPoint pointVal = point ? *point : CefPoint();
+
+  // Execute
+  bool _retval =
+      CefWindowCppToC::Get(reinterpret_cast<cef_window_t*>(self))
+          ->ConvertPointFromView(CefViewCppToC::Unwrap(view), pointVal);
+
+  // Restore param: point; type: simple_byref
+  if (point)
+    *point = pointVal;
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWindowCppToC::CefWindowCppToC() {
+  GetStruct()->show = window_show;
+  GetStruct()->hide = window_hide;
+  GetStruct()->center_window = window_center_window;
+  GetStruct()->close = window_close;
+  GetStruct()->is_closed = window_is_closed;
+  GetStruct()->activate = window_activate;
+  GetStruct()->deactivate = window_deactivate;
+  GetStruct()->is_active = window_is_active;
+  GetStruct()->bring_to_top = window_bring_to_top;
+  GetStruct()->set_always_on_top = window_set_always_on_top;
+  GetStruct()->is_always_on_top = window_is_always_on_top;
+  GetStruct()->maximize = window_maximize;
+  GetStruct()->minimize = window_minimize;
+  GetStruct()->restore = window_restore;
+  GetStruct()->set_fullscreen = window_set_fullscreen;
+  GetStruct()->is_maximized = window_is_maximized;
+  GetStruct()->is_minimized = window_is_minimized;
+  GetStruct()->is_fullscreen = window_is_fullscreen;
+  GetStruct()->set_title = window_set_title;
+  GetStruct()->get_title = window_get_title;
+  GetStruct()->set_window_icon = window_set_window_icon;
+  GetStruct()->get_window_icon = window_get_window_icon;
+  GetStruct()->set_window_app_icon = window_set_window_app_icon;
+  GetStruct()->get_window_app_icon = window_get_window_app_icon;
+  GetStruct()->show_menu = window_show_menu;
+  GetStruct()->cancel_menu = window_cancel_menu;
+  GetStruct()->get_display = window_get_display;
+  GetStruct()->get_client_area_bounds_in_screen =
+      window_get_client_area_bounds_in_screen;
+  GetStruct()->set_draggable_regions = window_set_draggable_regions;
+  GetStruct()->get_window_handle = window_get_window_handle;
+  GetStruct()->send_key_press = window_send_key_press;
+  GetStruct()->send_mouse_move = window_send_mouse_move;
+  GetStruct()->send_mouse_events = window_send_mouse_events;
+  GetStruct()->set_accelerator = window_set_accelerator;
+  GetStruct()->remove_accelerator = window_remove_accelerator;
+  GetStruct()->remove_all_accelerators = window_remove_all_accelerators;
+  GetStruct()->base.as_window = window_as_window;
+  GetStruct()->base.set_to_fill_layout = window_set_to_fill_layout;
+  GetStruct()->base.set_to_box_layout = window_set_to_box_layout;
+  GetStruct()->base.get_layout = window_get_layout;
+  GetStruct()->base.layout = window_layout;
+  GetStruct()->base.add_child_view = window_add_child_view;
+  GetStruct()->base.add_child_view_at = window_add_child_view_at;
+  GetStruct()->base.reorder_child_view = window_reorder_child_view;
+  GetStruct()->base.remove_child_view = window_remove_child_view;
+  GetStruct()->base.remove_all_child_views = window_remove_all_child_views;
+  GetStruct()->base.get_child_view_count = window_get_child_view_count;
+  GetStruct()->base.get_child_view_at = window_get_child_view_at;
+  GetStruct()->base.base.as_browser_view = window_as_browser_view;
+  GetStruct()->base.base.as_button = window_as_button;
+  GetStruct()->base.base.as_panel = window_as_panel;
+  GetStruct()->base.base.as_scroll_view = window_as_scroll_view;
+  GetStruct()->base.base.as_textfield = window_as_textfield;
+  GetStruct()->base.base.get_type_string = window_get_type_string;
+  GetStruct()->base.base.to_string = window_to_string;
+  GetStruct()->base.base.is_valid = window_is_valid;
+  GetStruct()->base.base.is_attached = window_is_attached;
+  GetStruct()->base.base.is_same = window_is_same;
+  GetStruct()->base.base.get_delegate = window_get_delegate;
+  GetStruct()->base.base.get_window = window_get_window;
+  GetStruct()->base.base.get_id = window_get_id;
+  GetStruct()->base.base.set_id = window_set_id;
+  GetStruct()->base.base.get_group_id = window_get_group_id;
+  GetStruct()->base.base.set_group_id = window_set_group_id;
+  GetStruct()->base.base.get_parent_view = window_get_parent_view;
+  GetStruct()->base.base.get_view_for_id = window_get_view_for_id;
+  GetStruct()->base.base.set_bounds = window_set_bounds;
+  GetStruct()->base.base.get_bounds = window_get_bounds;
+  GetStruct()->base.base.get_bounds_in_screen = window_get_bounds_in_screen;
+  GetStruct()->base.base.set_size = window_set_size;
+  GetStruct()->base.base.get_size = window_get_size;
+  GetStruct()->base.base.set_position = window_set_position;
+  GetStruct()->base.base.get_position = window_get_position;
+  GetStruct()->base.base.get_preferred_size = window_get_preferred_size;
+  GetStruct()->base.base.size_to_preferred_size = window_size_to_preferred_size;
+  GetStruct()->base.base.get_minimum_size = window_get_minimum_size;
+  GetStruct()->base.base.get_maximum_size = window_get_maximum_size;
+  GetStruct()->base.base.get_height_for_width = window_get_height_for_width;
+  GetStruct()->base.base.invalidate_layout = window_invalidate_layout;
+  GetStruct()->base.base.set_visible = window_set_visible;
+  GetStruct()->base.base.is_visible = window_is_visible;
+  GetStruct()->base.base.is_drawn = window_is_drawn;
+  GetStruct()->base.base.set_enabled = window_set_enabled;
+  GetStruct()->base.base.is_enabled = window_is_enabled;
+  GetStruct()->base.base.set_focusable = window_set_focusable;
+  GetStruct()->base.base.is_focusable = window_is_focusable;
+  GetStruct()->base.base.is_accessibility_focusable =
+      window_is_accessibility_focusable;
+  GetStruct()->base.base.request_focus = window_request_focus;
+  GetStruct()->base.base.set_background_color = window_set_background_color;
+  GetStruct()->base.base.get_background_color = window_get_background_color;
+  GetStruct()->base.base.convert_point_to_screen =
+      window_convert_point_to_screen;
+  GetStruct()->base.base.convert_point_from_screen =
+      window_convert_point_from_screen;
+  GetStruct()->base.base.convert_point_to_window =
+      window_convert_point_to_window;
+  GetStruct()->base.base.convert_point_from_window =
+      window_convert_point_from_window;
+  GetStruct()->base.base.convert_point_to_view = window_convert_point_to_view;
+  GetStruct()->base.base.convert_point_from_view =
+      window_convert_point_from_view;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWindowCppToC::~CefWindowCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefWindow>
+CefCppToCRefCounted<CefWindowCppToC, CefWindow, cef_window_t>::UnwrapDerived(
+    CefWrapperType type,
+    cef_window_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefWindowCppToC, CefWindow, cef_window_t>::
+    kWrapperType = WT_WINDOW;
diff --git a/src/libcef_dll/cpptoc/views/window_cpptoc.h b/src/libcef_dll/cpptoc/views/window_cpptoc.h
new file mode 100644
index 0000000..800f64a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/window_cpptoc.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fabf09f314badc143b51db3cdd2a590e0f23da13$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_WINDOW_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_WINDOW_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_window_capi.h"
+#include "include/views/cef_window.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefWindowCppToC
+    : public CefCppToCRefCounted<CefWindowCppToC, CefWindow, cef_window_t> {
+ public:
+  CefWindowCppToC();
+  virtual ~CefWindowCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_WINDOW_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/views/window_delegate_cpptoc.cc b/src/libcef_dll/cpptoc/views/window_delegate_cpptoc.cc
new file mode 100644
index 0000000..59b706d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/window_delegate_cpptoc.cc
@@ -0,0 +1,519 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=52b31a63bbe83937a9f5dc557accfc1e84af6d84$
+//
+
+#include "libcef_dll/cpptoc/views/window_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK
+window_delegate_on_window_created(struct _cef_window_delegate_t* self,
+                                  cef_window_t* window) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return;
+
+  // Execute
+  CefWindowDelegateCppToC::Get(self)->OnWindowCreated(
+      CefWindowCToCpp::Wrap(window));
+}
+
+void CEF_CALLBACK
+window_delegate_on_window_destroyed(struct _cef_window_delegate_t* self,
+                                    cef_window_t* window) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return;
+
+  // Execute
+  CefWindowDelegateCppToC::Get(self)->OnWindowDestroyed(
+      CefWindowCToCpp::Wrap(window));
+}
+
+cef_window_t* CEF_CALLBACK
+window_delegate_get_parent_window(struct _cef_window_delegate_t* self,
+                                  cef_window_t* window,
+                                  int* is_menu,
+                                  int* can_activate_menu) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return NULL;
+  // Verify param: is_menu; type: bool_byaddr
+  DCHECK(is_menu);
+  if (!is_menu)
+    return NULL;
+  // Verify param: can_activate_menu; type: bool_byaddr
+  DCHECK(can_activate_menu);
+  if (!can_activate_menu)
+    return NULL;
+
+  // Translate param: is_menu; type: bool_byaddr
+  bool is_menuBool = (is_menu && *is_menu) ? true : false;
+  // Translate param: can_activate_menu; type: bool_byaddr
+  bool can_activate_menuBool =
+      (can_activate_menu && *can_activate_menu) ? true : false;
+
+  // Execute
+  CefRefPtr<CefWindow> _retval =
+      CefWindowDelegateCppToC::Get(self)->GetParentWindow(
+          CefWindowCToCpp::Wrap(window), &is_menuBool, &can_activate_menuBool);
+
+  // Restore param: is_menu; type: bool_byaddr
+  if (is_menu)
+    *is_menu = is_menuBool ? true : false;
+  // Restore param: can_activate_menu; type: bool_byaddr
+  if (can_activate_menu)
+    *can_activate_menu = can_activate_menuBool ? true : false;
+
+  // Return type: refptr_diff
+  return CefWindowCToCpp::Unwrap(_retval);
+}
+
+int CEF_CALLBACK
+window_delegate_is_frameless(struct _cef_window_delegate_t* self,
+                             cef_window_t* window) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowDelegateCppToC::Get(self)->IsFrameless(
+      CefWindowCToCpp::Wrap(window));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_delegate_can_resize(struct _cef_window_delegate_t* self,
+                                            cef_window_t* window) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowDelegateCppToC::Get(self)->CanResize(
+      CefWindowCToCpp::Wrap(window));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+window_delegate_can_maximize(struct _cef_window_delegate_t* self,
+                             cef_window_t* window) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowDelegateCppToC::Get(self)->CanMaximize(
+      CefWindowCToCpp::Wrap(window));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+window_delegate_can_minimize(struct _cef_window_delegate_t* self,
+                             cef_window_t* window) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowDelegateCppToC::Get(self)->CanMinimize(
+      CefWindowCToCpp::Wrap(window));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK window_delegate_can_close(struct _cef_window_delegate_t* self,
+                                           cef_window_t* window) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowDelegateCppToC::Get(self)->CanClose(
+      CefWindowCToCpp::Wrap(window));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+window_delegate_on_accelerator(struct _cef_window_delegate_t* self,
+                               cef_window_t* window,
+                               int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return 0;
+
+  // Execute
+  bool _retval = CefWindowDelegateCppToC::Get(self)->OnAccelerator(
+      CefWindowCToCpp::Wrap(window), command_id);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+window_delegate_on_key_event(struct _cef_window_delegate_t* self,
+                             cef_window_t* window,
+                             const struct _cef_key_event_t* event) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: window; type: refptr_diff
+  DCHECK(window);
+  if (!window)
+    return 0;
+  // Verify param: event; type: struct_byref_const
+  DCHECK(event);
+  if (!event)
+    return 0;
+
+  // Translate param: event; type: struct_byref_const
+  CefKeyEvent eventObj;
+  if (event)
+    eventObj.Set(*event, false);
+
+  // Execute
+  bool _retval = CefWindowDelegateCppToC::Get(self)->OnKeyEvent(
+      CefWindowCToCpp::Wrap(window), eventObj);
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+window_delegate_get_preferred_size(struct _cef_view_delegate_t* self,
+                                   cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefWindowDelegateCppToC::Get(
+                           reinterpret_cast<cef_window_delegate_t*>(self))
+                           ->GetPreferredSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+window_delegate_get_minimum_size(struct _cef_view_delegate_t* self,
+                                 cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefWindowDelegateCppToC::Get(
+                           reinterpret_cast<cef_window_delegate_t*>(self))
+                           ->GetMinimumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_size_t CEF_CALLBACK
+window_delegate_get_maximum_size(struct _cef_view_delegate_t* self,
+                                 cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefSize();
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval = CefWindowDelegateCppToC::Get(
+                           reinterpret_cast<cef_window_delegate_t*>(self))
+                           ->GetMaximumSize(CefViewCToCpp::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+window_delegate_get_height_for_width(struct _cef_view_delegate_t* self,
+                                     cef_view_t* view,
+                                     int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return 0;
+
+  // Execute
+  int _retval = CefWindowDelegateCppToC::Get(
+                    reinterpret_cast<cef_window_delegate_t*>(self))
+                    ->GetHeightForWidth(CefViewCToCpp::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+window_delegate_on_parent_view_changed(struct _cef_view_delegate_t* self,
+                                       cef_view_t* view,
+                                       int added,
+                                       cef_view_t* parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent);
+  if (!parent)
+    return;
+
+  // Execute
+  CefWindowDelegateCppToC::Get(reinterpret_cast<cef_window_delegate_t*>(self))
+      ->OnParentViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                            CefViewCToCpp::Wrap(parent));
+}
+
+void CEF_CALLBACK
+window_delegate_on_child_view_changed(struct _cef_view_delegate_t* self,
+                                      cef_view_t* view,
+                                      int added,
+                                      cef_view_t* child) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child);
+  if (!child)
+    return;
+
+  // Execute
+  CefWindowDelegateCppToC::Get(reinterpret_cast<cef_window_delegate_t*>(self))
+      ->OnChildViewChanged(CefViewCToCpp::Wrap(view), added ? true : false,
+                           CefViewCToCpp::Wrap(child));
+}
+
+void CEF_CALLBACK window_delegate_on_focus(struct _cef_view_delegate_t* self,
+                                           cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefWindowDelegateCppToC::Get(reinterpret_cast<cef_window_delegate_t*>(self))
+      ->OnFocus(CefViewCToCpp::Wrap(view));
+}
+
+void CEF_CALLBACK window_delegate_on_blur(struct _cef_view_delegate_t* self,
+                                          cef_view_t* view) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: view; type: refptr_diff
+  DCHECK(view);
+  if (!view)
+    return;
+
+  // Execute
+  CefWindowDelegateCppToC::Get(reinterpret_cast<cef_window_delegate_t*>(self))
+      ->OnBlur(CefViewCToCpp::Wrap(view));
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWindowDelegateCppToC::CefWindowDelegateCppToC() {
+  GetStruct()->on_window_created = window_delegate_on_window_created;
+  GetStruct()->on_window_destroyed = window_delegate_on_window_destroyed;
+  GetStruct()->get_parent_window = window_delegate_get_parent_window;
+  GetStruct()->is_frameless = window_delegate_is_frameless;
+  GetStruct()->can_resize = window_delegate_can_resize;
+  GetStruct()->can_maximize = window_delegate_can_maximize;
+  GetStruct()->can_minimize = window_delegate_can_minimize;
+  GetStruct()->can_close = window_delegate_can_close;
+  GetStruct()->on_accelerator = window_delegate_on_accelerator;
+  GetStruct()->on_key_event = window_delegate_on_key_event;
+  GetStruct()->base.base.get_preferred_size =
+      window_delegate_get_preferred_size;
+  GetStruct()->base.base.get_minimum_size = window_delegate_get_minimum_size;
+  GetStruct()->base.base.get_maximum_size = window_delegate_get_maximum_size;
+  GetStruct()->base.base.get_height_for_width =
+      window_delegate_get_height_for_width;
+  GetStruct()->base.base.on_parent_view_changed =
+      window_delegate_on_parent_view_changed;
+  GetStruct()->base.base.on_child_view_changed =
+      window_delegate_on_child_view_changed;
+  GetStruct()->base.base.on_focus = window_delegate_on_focus;
+  GetStruct()->base.base.on_blur = window_delegate_on_blur;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWindowDelegateCppToC::~CefWindowDelegateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefWindowDelegate> CefCppToCRefCounted<
+    CefWindowDelegateCppToC,
+    CefWindowDelegate,
+    cef_window_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_window_delegate_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefWindowDelegateCppToC,
+                                   CefWindowDelegate,
+                                   cef_window_delegate_t>::kWrapperType =
+    WT_WINDOW_DELEGATE;
diff --git a/src/libcef_dll/cpptoc/views/window_delegate_cpptoc.h b/src/libcef_dll/cpptoc/views/window_delegate_cpptoc.h
new file mode 100644
index 0000000..17b1076
--- /dev/null
+++ b/src/libcef_dll/cpptoc/views/window_delegate_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b88ec555034d3c63e80d13471f7da921ea8ef70a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_VIEWS_WINDOW_DELEGATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_VIEWS_WINDOW_DELEGATE_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_window_capi.h"
+#include "include/capi/views/cef_window_delegate_capi.h"
+#include "include/views/cef_window.h"
+#include "include/views/cef_window_delegate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefWindowDelegateCppToC
+    : public CefCppToCRefCounted<CefWindowDelegateCppToC,
+                                 CefWindowDelegate,
+                                 cef_window_delegate_t> {
+ public:
+  CefWindowDelegateCppToC();
+  virtual ~CefWindowDelegateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_VIEWS_WINDOW_DELEGATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/waitable_event_cpptoc.cc b/src/libcef_dll/cpptoc/waitable_event_cpptoc.cc
new file mode 100644
index 0000000..07ace17
--- /dev/null
+++ b/src/libcef_dll/cpptoc/waitable_event_cpptoc.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9251c1a72b2ece747624e43953dbd476e68a62a2$
+//
+
+#include "libcef_dll/cpptoc/waitable_event_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_waitable_event_t* cef_waitable_event_create(
+    int automatic_reset,
+    int initially_signaled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefPtr<CefWaitableEvent> _retval = CefWaitableEvent::CreateWaitableEvent(
+      automatic_reset ? true : false, initially_signaled ? true : false);
+
+  // Return type: refptr_same
+  return CefWaitableEventCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK waitable_event_reset(struct _cef_waitable_event_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWaitableEventCppToC::Get(self)->Reset();
+}
+
+void CEF_CALLBACK waitable_event_signal(struct _cef_waitable_event_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWaitableEventCppToC::Get(self)->Signal();
+}
+
+int CEF_CALLBACK
+waitable_event_is_signaled(struct _cef_waitable_event_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWaitableEventCppToC::Get(self)->IsSignaled();
+
+  // Return type: bool
+  return _retval;
+}
+
+void CEF_CALLBACK waitable_event_wait(struct _cef_waitable_event_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+
+  // Execute
+  CefWaitableEventCppToC::Get(self)->Wait();
+}
+
+int CEF_CALLBACK waitable_event_timed_wait(struct _cef_waitable_event_t* self,
+                                           int64 max_ms) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWaitableEventCppToC::Get(self)->TimedWait(max_ms);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWaitableEventCppToC::CefWaitableEventCppToC() {
+  GetStruct()->reset = waitable_event_reset;
+  GetStruct()->signal = waitable_event_signal;
+  GetStruct()->is_signaled = waitable_event_is_signaled;
+  GetStruct()->wait = waitable_event_wait;
+  GetStruct()->timed_wait = waitable_event_timed_wait;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWaitableEventCppToC::~CefWaitableEventCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefWaitableEvent> CefCppToCRefCounted<
+    CefWaitableEventCppToC,
+    CefWaitableEvent,
+    cef_waitable_event_t>::UnwrapDerived(CefWrapperType type,
+                                         cef_waitable_event_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefWaitableEventCppToC,
+                                   CefWaitableEvent,
+                                   cef_waitable_event_t>::kWrapperType =
+    WT_WAITABLE_EVENT;
diff --git a/src/libcef_dll/cpptoc/waitable_event_cpptoc.h b/src/libcef_dll/cpptoc/waitable_event_cpptoc.h
new file mode 100644
index 0000000..60cb2fe
--- /dev/null
+++ b/src/libcef_dll/cpptoc/waitable_event_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d9c0c0cbb9298c485ae33ebcd51449bcc9d2ad68$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_WAITABLE_EVENT_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_WAITABLE_EVENT_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_waitable_event_capi.h"
+#include "include/cef_waitable_event.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefWaitableEventCppToC
+    : public CefCppToCRefCounted<CefWaitableEventCppToC,
+                                 CefWaitableEvent,
+                                 cef_waitable_event_t> {
+ public:
+  CefWaitableEventCppToC();
+  virtual ~CefWaitableEventCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_WAITABLE_EVENT_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/web_plugin_info_cpptoc.cc b/src/libcef_dll/cpptoc/web_plugin_info_cpptoc.cc
new file mode 100644
index 0000000..bb993eb
--- /dev/null
+++ b/src/libcef_dll/cpptoc/web_plugin_info_cpptoc.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=17553c276ccdfd3d27c552d8ae7bedfe7a458f9a$
+//
+
+#include "libcef_dll/cpptoc/web_plugin_info_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+web_plugin_info_get_name(struct _cef_web_plugin_info_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefWebPluginInfoCppToC::Get(self)->GetName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+web_plugin_info_get_path(struct _cef_web_plugin_info_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefWebPluginInfoCppToC::Get(self)->GetPath();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+web_plugin_info_get_version(struct _cef_web_plugin_info_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefWebPluginInfoCppToC::Get(self)->GetVersion();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+web_plugin_info_get_description(struct _cef_web_plugin_info_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefWebPluginInfoCppToC::Get(self)->GetDescription();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoCppToC::CefWebPluginInfoCppToC() {
+  GetStruct()->get_name = web_plugin_info_get_name;
+  GetStruct()->get_path = web_plugin_info_get_path;
+  GetStruct()->get_version = web_plugin_info_get_version;
+  GetStruct()->get_description = web_plugin_info_get_description;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoCppToC::~CefWebPluginInfoCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefWebPluginInfo> CefCppToCRefCounted<
+    CefWebPluginInfoCppToC,
+    CefWebPluginInfo,
+    cef_web_plugin_info_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_web_plugin_info_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefWebPluginInfoCppToC,
+                                   CefWebPluginInfo,
+                                   cef_web_plugin_info_t>::kWrapperType =
+    WT_WEB_PLUGIN_INFO;
diff --git a/src/libcef_dll/cpptoc/web_plugin_info_cpptoc.h b/src/libcef_dll/cpptoc/web_plugin_info_cpptoc.h
new file mode 100644
index 0000000..59844d5
--- /dev/null
+++ b/src/libcef_dll/cpptoc/web_plugin_info_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fce7bf500cfdd2e1e21655477a167272e4f89310$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_INFO_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_INFO_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefWebPluginInfoCppToC
+    : public CefCppToCRefCounted<CefWebPluginInfoCppToC,
+                                 CefWebPluginInfo,
+                                 cef_web_plugin_info_t> {
+ public:
+  CefWebPluginInfoCppToC();
+  virtual ~CefWebPluginInfoCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_INFO_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.cc b/src/libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.cc
new file mode 100644
index 0000000..117f343
--- /dev/null
+++ b/src/libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9bf14398237548c7290ae748ac62833f3d0f1830$
+//
+
+#include "libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.h"
+#include "libcef_dll/ctocpp/web_plugin_info_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK
+web_plugin_info_visitor_visit(struct _cef_web_plugin_info_visitor_t* self,
+                              cef_web_plugin_info_t* info,
+                              int count,
+                              int total) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: info; type: refptr_diff
+  DCHECK(info);
+  if (!info)
+    return 0;
+
+  // Execute
+  bool _retval = CefWebPluginInfoVisitorCppToC::Get(self)->Visit(
+      CefWebPluginInfoCToCpp::Wrap(info), count, total);
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoVisitorCppToC::CefWebPluginInfoVisitorCppToC() {
+  GetStruct()->visit = web_plugin_info_visitor_visit;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoVisitorCppToC::~CefWebPluginInfoVisitorCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefWebPluginInfoVisitor> CefCppToCRefCounted<
+    CefWebPluginInfoVisitorCppToC,
+    CefWebPluginInfoVisitor,
+    cef_web_plugin_info_visitor_t>::UnwrapDerived(CefWrapperType type,
+                                                  cef_web_plugin_info_visitor_t*
+                                                      s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefWebPluginInfoVisitorCppToC,
+                        CefWebPluginInfoVisitor,
+                        cef_web_plugin_info_visitor_t>::kWrapperType =
+        WT_WEB_PLUGIN_INFO_VISITOR;
diff --git a/src/libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.h b/src/libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.h
new file mode 100644
index 0000000..0a7767e
--- /dev/null
+++ b/src/libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=45695bea6b9ce6484f1321fd84bf11521d732b2c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_INFO_VISITOR_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_INFO_VISITOR_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefWebPluginInfoVisitorCppToC
+    : public CefCppToCRefCounted<CefWebPluginInfoVisitorCppToC,
+                                 CefWebPluginInfoVisitor,
+                                 cef_web_plugin_info_visitor_t> {
+ public:
+  CefWebPluginInfoVisitorCppToC();
+  virtual ~CefWebPluginInfoVisitorCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_INFO_VISITOR_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.cc b/src/libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.cc
new file mode 100644
index 0000000..f62517b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a0757524464550a80541e4a57f1a8d285098fcdd$
+//
+
+#include "libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+void CEF_CALLBACK web_plugin_unstable_callback_is_unstable(
+    struct _cef_web_plugin_unstable_callback_t* self,
+    const cef_string_t* path,
+    int unstable) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+
+  // Execute
+  CefWebPluginUnstableCallbackCppToC::Get(self)->IsUnstable(
+      CefString(path), unstable ? true : false);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWebPluginUnstableCallbackCppToC::CefWebPluginUnstableCallbackCppToC() {
+  GetStruct()->is_unstable = web_plugin_unstable_callback_is_unstable;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWebPluginUnstableCallbackCppToC::~CefWebPluginUnstableCallbackCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefWebPluginUnstableCallback>
+CefCppToCRefCounted<CefWebPluginUnstableCallbackCppToC,
+                    CefWebPluginUnstableCallback,
+                    cef_web_plugin_unstable_callback_t>::
+    UnwrapDerived(CefWrapperType type, cef_web_plugin_unstable_callback_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCppToCRefCounted<CefWebPluginUnstableCallbackCppToC,
+                        CefWebPluginUnstableCallback,
+                        cef_web_plugin_unstable_callback_t>::kWrapperType =
+        WT_WEB_PLUGIN_UNSTABLE_CALLBACK;
diff --git a/src/libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.h b/src/libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.h
new file mode 100644
index 0000000..44007f0
--- /dev/null
+++ b/src/libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3cd5a5c45b499f6bd65776affa5bab00d7994678$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_UNSTABLE_CALLBACK_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_UNSTABLE_CALLBACK_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefWebPluginUnstableCallbackCppToC
+    : public CefCppToCRefCounted<CefWebPluginUnstableCallbackCppToC,
+                                 CefWebPluginUnstableCallback,
+                                 cef_web_plugin_unstable_callback_t> {
+ public:
+  CefWebPluginUnstableCallbackCppToC();
+  virtual ~CefWebPluginUnstableCallbackCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_WEB_PLUGIN_UNSTABLE_CALLBACK_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/write_handler_cpptoc.cc b/src/libcef_dll/cpptoc/write_handler_cpptoc.cc
new file mode 100644
index 0000000..d268b33
--- /dev/null
+++ b/src/libcef_dll/cpptoc/write_handler_cpptoc.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6a7b209717d88fb7868b7a098b9a569920f6b255$
+//
+
+#include "libcef_dll/cpptoc/write_handler_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+size_t CEF_CALLBACK write_handler_write(struct _cef_write_handler_t* self,
+                                        const void* ptr,
+                                        size_t size,
+                                        size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = CefWriteHandlerCppToC::Get(self)->Write(ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK write_handler_seek(struct _cef_write_handler_t* self,
+                                    int64 offset,
+                                    int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefWriteHandlerCppToC::Get(self)->Seek(offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+int64 CEF_CALLBACK write_handler_tell(struct _cef_write_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefWriteHandlerCppToC::Get(self)->Tell();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK write_handler_flush(struct _cef_write_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefWriteHandlerCppToC::Get(self)->Flush();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK write_handler_may_block(struct _cef_write_handler_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefWriteHandlerCppToC::Get(self)->MayBlock();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWriteHandlerCppToC::CefWriteHandlerCppToC() {
+  GetStruct()->write = write_handler_write;
+  GetStruct()->seek = write_handler_seek;
+  GetStruct()->tell = write_handler_tell;
+  GetStruct()->flush = write_handler_flush;
+  GetStruct()->may_block = write_handler_may_block;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWriteHandlerCppToC::~CefWriteHandlerCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefWriteHandler> CefCppToCRefCounted<
+    CefWriteHandlerCppToC,
+    CefWriteHandler,
+    cef_write_handler_t>::UnwrapDerived(CefWrapperType type,
+                                        cef_write_handler_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefWriteHandlerCppToC,
+                                   CefWriteHandler,
+                                   cef_write_handler_t>::kWrapperType =
+    WT_WRITE_HANDLER;
diff --git a/src/libcef_dll/cpptoc/write_handler_cpptoc.h b/src/libcef_dll/cpptoc/write_handler_cpptoc.h
new file mode 100644
index 0000000..4f1754b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/write_handler_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5b10bd732535800c090c3000f95a2e7c8bdfe773$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_WRITE_HANDLER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_WRITE_HANDLER_CPPTOC_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed wrapper-side only.
+class CefWriteHandlerCppToC : public CefCppToCRefCounted<CefWriteHandlerCppToC,
+                                                         CefWriteHandler,
+                                                         cef_write_handler_t> {
+ public:
+  CefWriteHandlerCppToC();
+  virtual ~CefWriteHandlerCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_WRITE_HANDLER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/x509cert_principal_cpptoc.cc b/src/libcef_dll/cpptoc/x509cert_principal_cpptoc.cc
new file mode 100644
index 0000000..409292a
--- /dev/null
+++ b/src/libcef_dll/cpptoc/x509cert_principal_cpptoc.cc
@@ -0,0 +1,257 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8780097b60810a7f952f0a2169ebc925cb63b5e1$
+//
+
+#include "libcef_dll/cpptoc/x509cert_principal_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_string_userfree_t CEF_CALLBACK
+x509cert_principal_get_display_name(struct _cef_x509cert_principal_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefX509CertPrincipalCppToC::Get(self)->GetDisplayName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+x509cert_principal_get_common_name(struct _cef_x509cert_principal_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefX509CertPrincipalCppToC::Get(self)->GetCommonName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+x509cert_principal_get_locality_name(struct _cef_x509cert_principal_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefX509CertPrincipalCppToC::Get(self)->GetLocalityName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+x509cert_principal_get_state_or_province_name(
+    struct _cef_x509cert_principal_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefX509CertPrincipalCppToC::Get(self)->GetStateOrProvinceName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+x509cert_principal_get_country_name(struct _cef_x509cert_principal_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefX509CertPrincipalCppToC::Get(self)->GetCountryName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+void CEF_CALLBACK
+x509cert_principal_get_street_addresses(struct _cef_x509cert_principal_t* self,
+                                        cef_string_list_t addresses) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: addresses; type: string_vec_byref
+  DCHECK(addresses);
+  if (!addresses)
+    return;
+
+  // Translate param: addresses; type: string_vec_byref
+  std::vector<CefString> addressesList;
+  transfer_string_list_contents(addresses, addressesList);
+
+  // Execute
+  CefX509CertPrincipalCppToC::Get(self)->GetStreetAddresses(addressesList);
+
+  // Restore param: addresses; type: string_vec_byref
+  cef_string_list_clear(addresses);
+  transfer_string_list_contents(addressesList, addresses);
+}
+
+void CEF_CALLBACK x509cert_principal_get_organization_names(
+    struct _cef_x509cert_principal_t* self,
+    cef_string_list_t names) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: names; type: string_vec_byref
+  DCHECK(names);
+  if (!names)
+    return;
+
+  // Translate param: names; type: string_vec_byref
+  std::vector<CefString> namesList;
+  transfer_string_list_contents(names, namesList);
+
+  // Execute
+  CefX509CertPrincipalCppToC::Get(self)->GetOrganizationNames(namesList);
+
+  // Restore param: names; type: string_vec_byref
+  cef_string_list_clear(names);
+  transfer_string_list_contents(namesList, names);
+}
+
+void CEF_CALLBACK x509cert_principal_get_organization_unit_names(
+    struct _cef_x509cert_principal_t* self,
+    cef_string_list_t names) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: names; type: string_vec_byref
+  DCHECK(names);
+  if (!names)
+    return;
+
+  // Translate param: names; type: string_vec_byref
+  std::vector<CefString> namesList;
+  transfer_string_list_contents(names, namesList);
+
+  // Execute
+  CefX509CertPrincipalCppToC::Get(self)->GetOrganizationUnitNames(namesList);
+
+  // Restore param: names; type: string_vec_byref
+  cef_string_list_clear(names);
+  transfer_string_list_contents(namesList, names);
+}
+
+void CEF_CALLBACK
+x509cert_principal_get_domain_components(struct _cef_x509cert_principal_t* self,
+                                         cef_string_list_t components) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: components; type: string_vec_byref
+  DCHECK(components);
+  if (!components)
+    return;
+
+  // Translate param: components; type: string_vec_byref
+  std::vector<CefString> componentsList;
+  transfer_string_list_contents(components, componentsList);
+
+  // Execute
+  CefX509CertPrincipalCppToC::Get(self)->GetDomainComponents(componentsList);
+
+  // Restore param: components; type: string_vec_byref
+  cef_string_list_clear(components);
+  transfer_string_list_contents(componentsList, components);
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefX509CertPrincipalCppToC::CefX509CertPrincipalCppToC() {
+  GetStruct()->get_display_name = x509cert_principal_get_display_name;
+  GetStruct()->get_common_name = x509cert_principal_get_common_name;
+  GetStruct()->get_locality_name = x509cert_principal_get_locality_name;
+  GetStruct()->get_state_or_province_name =
+      x509cert_principal_get_state_or_province_name;
+  GetStruct()->get_country_name = x509cert_principal_get_country_name;
+  GetStruct()->get_street_addresses = x509cert_principal_get_street_addresses;
+  GetStruct()->get_organization_names =
+      x509cert_principal_get_organization_names;
+  GetStruct()->get_organization_unit_names =
+      x509cert_principal_get_organization_unit_names;
+  GetStruct()->get_domain_components = x509cert_principal_get_domain_components;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefX509CertPrincipalCppToC::~CefX509CertPrincipalCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefX509CertPrincipal> CefCppToCRefCounted<
+    CefX509CertPrincipalCppToC,
+    CefX509CertPrincipal,
+    cef_x509cert_principal_t>::UnwrapDerived(CefWrapperType type,
+                                             cef_x509cert_principal_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefX509CertPrincipalCppToC,
+                                   CefX509CertPrincipal,
+                                   cef_x509cert_principal_t>::kWrapperType =
+    WT_X509CERT_PRINCIPAL;
diff --git a/src/libcef_dll/cpptoc/x509cert_principal_cpptoc.h b/src/libcef_dll/cpptoc/x509cert_principal_cpptoc.h
new file mode 100644
index 0000000..6600abc
--- /dev/null
+++ b/src/libcef_dll/cpptoc/x509cert_principal_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1479667eef11356f361b39fb04156231adcb551d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_X509CERT_PRINCIPAL_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_X509CERT_PRINCIPAL_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_x509_certificate_capi.h"
+#include "include/cef_x509_certificate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefX509CertPrincipalCppToC
+    : public CefCppToCRefCounted<CefX509CertPrincipalCppToC,
+                                 CefX509CertPrincipal,
+                                 cef_x509cert_principal_t> {
+ public:
+  CefX509CertPrincipalCppToC();
+  virtual ~CefX509CertPrincipalCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_X509CERT_PRINCIPAL_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/x509certificate_cpptoc.cc b/src/libcef_dll/cpptoc/x509certificate_cpptoc.cc
new file mode 100644
index 0000000..5a7e026
--- /dev/null
+++ b/src/libcef_dll/cpptoc/x509certificate_cpptoc.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b65d54f4da2586c066c6027fc3f3dbfdde465841$
+//
+
+#include "libcef_dll/cpptoc/x509certificate_cpptoc.h"
+#include <algorithm>
+#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/x509cert_principal_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+cef_x509cert_principal_t* CEF_CALLBACK
+x509certificate_get_subject(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefX509CertPrincipal> _retval =
+      CefX509CertificateCppToC::Get(self)->GetSubject();
+
+  // Return type: refptr_same
+  return CefX509CertPrincipalCppToC::Wrap(_retval);
+}
+
+cef_x509cert_principal_t* CEF_CALLBACK
+x509certificate_get_issuer(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefX509CertPrincipal> _retval =
+      CefX509CertificateCppToC::Get(self)->GetIssuer();
+
+  // Return type: refptr_same
+  return CefX509CertPrincipalCppToC::Wrap(_retval);
+}
+
+cef_binary_value_t* CEF_CALLBACK
+x509certificate_get_serial_number(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval =
+      CefX509CertificateCppToC::Get(self)->GetSerialNumber();
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+cef_time_t CEF_CALLBACK
+x509certificate_get_valid_start(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefTime();
+
+  // Execute
+  cef_time_t _retval = CefX509CertificateCppToC::Get(self)->GetValidStart();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_time_t CEF_CALLBACK
+x509certificate_get_valid_expiry(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefTime();
+
+  // Execute
+  cef_time_t _retval = CefX509CertificateCppToC::Get(self)->GetValidExpiry();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_binary_value_t* CEF_CALLBACK
+x509certificate_get_derencoded(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval =
+      CefX509CertificateCppToC::Get(self)->GetDEREncoded();
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+cef_binary_value_t* CEF_CALLBACK
+x509certificate_get_pemencoded(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval =
+      CefX509CertificateCppToC::Get(self)->GetPEMEncoded();
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+size_t CEF_CALLBACK
+x509certificate_get_issuer_chain_size(struct _cef_x509certificate_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefX509CertificateCppToC::Get(self)->GetIssuerChainSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+void CEF_CALLBACK
+x509certificate_get_derencoded_issuer_chain(struct _cef_x509certificate_t* self,
+                                            size_t* chainCount,
+                                            cef_binary_value_t** chain) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: chain; type: refptr_vec_same_byref
+  DCHECK(chainCount && (*chainCount == 0 || chain));
+  if (!chainCount || (*chainCount > 0 && !chain))
+    return;
+
+  // Translate param: chain; type: refptr_vec_same_byref
+  std::vector<CefRefPtr<CefBinaryValue>> chainList;
+  if (chainCount && *chainCount > 0 && chain) {
+    for (size_t i = 0; i < *chainCount; ++i) {
+      chainList.push_back(CefBinaryValueCppToC::Unwrap(chain[i]));
+    }
+  }
+
+  // Execute
+  CefX509CertificateCppToC::Get(self)->GetDEREncodedIssuerChain(chainList);
+
+  // Restore param: chain; type: refptr_vec_same_byref
+  if (chainCount && chain) {
+    *chainCount = std::min(chainList.size(), *chainCount);
+    if (*chainCount > 0) {
+      for (size_t i = 0; i < *chainCount; ++i) {
+        chain[i] = CefBinaryValueCppToC::Wrap(chainList[i]);
+      }
+    }
+  }
+}
+
+void CEF_CALLBACK
+x509certificate_get_pemencoded_issuer_chain(struct _cef_x509certificate_t* self,
+                                            size_t* chainCount,
+                                            cef_binary_value_t** chain) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return;
+  // Verify param: chain; type: refptr_vec_same_byref
+  DCHECK(chainCount && (*chainCount == 0 || chain));
+  if (!chainCount || (*chainCount > 0 && !chain))
+    return;
+
+  // Translate param: chain; type: refptr_vec_same_byref
+  std::vector<CefRefPtr<CefBinaryValue>> chainList;
+  if (chainCount && *chainCount > 0 && chain) {
+    for (size_t i = 0; i < *chainCount; ++i) {
+      chainList.push_back(CefBinaryValueCppToC::Unwrap(chain[i]));
+    }
+  }
+
+  // Execute
+  CefX509CertificateCppToC::Get(self)->GetPEMEncodedIssuerChain(chainList);
+
+  // Restore param: chain; type: refptr_vec_same_byref
+  if (chainCount && chain) {
+    *chainCount = std::min(chainList.size(), *chainCount);
+    if (*chainCount > 0) {
+      for (size_t i = 0; i < *chainCount; ++i) {
+        chain[i] = CefBinaryValueCppToC::Wrap(chainList[i]);
+      }
+    }
+  }
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefX509CertificateCppToC::CefX509CertificateCppToC() {
+  GetStruct()->get_subject = x509certificate_get_subject;
+  GetStruct()->get_issuer = x509certificate_get_issuer;
+  GetStruct()->get_serial_number = x509certificate_get_serial_number;
+  GetStruct()->get_valid_start = x509certificate_get_valid_start;
+  GetStruct()->get_valid_expiry = x509certificate_get_valid_expiry;
+  GetStruct()->get_derencoded = x509certificate_get_derencoded;
+  GetStruct()->get_pemencoded = x509certificate_get_pemencoded;
+  GetStruct()->get_issuer_chain_size = x509certificate_get_issuer_chain_size;
+  GetStruct()->get_derencoded_issuer_chain =
+      x509certificate_get_derencoded_issuer_chain;
+  GetStruct()->get_pemencoded_issuer_chain =
+      x509certificate_get_pemencoded_issuer_chain;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefX509CertificateCppToC::~CefX509CertificateCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefX509Certificate> CefCppToCRefCounted<
+    CefX509CertificateCppToC,
+    CefX509Certificate,
+    cef_x509certificate_t>::UnwrapDerived(CefWrapperType type,
+                                          cef_x509certificate_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefX509CertificateCppToC,
+                                   CefX509Certificate,
+                                   cef_x509certificate_t>::kWrapperType =
+    WT_X509CERTIFICATE;
diff --git a/src/libcef_dll/cpptoc/x509certificate_cpptoc.h b/src/libcef_dll/cpptoc/x509certificate_cpptoc.h
new file mode 100644
index 0000000..1d0000f
--- /dev/null
+++ b/src/libcef_dll/cpptoc/x509certificate_cpptoc.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1bde7dcc51e94b88d49b7e277708bd4230272ee0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_X509CERTIFICATE_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_X509CERTIFICATE_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_x509_certificate_capi.h"
+#include "include/cef_x509_certificate.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefX509CertificateCppToC
+    : public CefCppToCRefCounted<CefX509CertificateCppToC,
+                                 CefX509Certificate,
+                                 cef_x509certificate_t> {
+ public:
+  CefX509CertificateCppToC();
+  virtual ~CefX509CertificateCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_X509CERTIFICATE_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/xml_reader_cpptoc.cc b/src/libcef_dll/cpptoc/xml_reader_cpptoc.cc
new file mode 100644
index 0000000..cb241b1
--- /dev/null
+++ b/src/libcef_dll/cpptoc/xml_reader_cpptoc.cc
@@ -0,0 +1,632 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ba90249fc88623c9e1dc7f4b0d5a2c17e6f9b664$
+//
+
+#include "libcef_dll/cpptoc/xml_reader_cpptoc.h"
+#include "libcef_dll/cpptoc/stream_reader_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_xml_reader_t* cef_xml_reader_create(
+    cef_stream_reader_t* stream,
+    cef_xml_encoding_type_t encodingType,
+    const cef_string_t* URI) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: stream; type: refptr_same
+  DCHECK(stream);
+  if (!stream)
+    return NULL;
+  // Verify param: URI; type: string_byref_const
+  DCHECK(URI);
+  if (!URI)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefXmlReader> _retval = CefXmlReader::Create(
+      CefStreamReaderCppToC::Unwrap(stream), encodingType, CefString(URI));
+
+  // Return type: refptr_same
+  return CefXmlReaderCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK xml_reader_move_to_next_node(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->MoveToNextNode();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK xml_reader_close(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->Close();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK xml_reader_has_error(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->HasError();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_error(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetError();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_xml_node_type_t CEF_CALLBACK
+xml_reader_get_type(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return XML_NODE_UNSUPPORTED;
+
+  // Execute
+  cef_xml_node_type_t _retval = CefXmlReaderCppToC::Get(self)->GetType();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK xml_reader_get_depth(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefXmlReaderCppToC::Get(self)->GetDepth();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_local_name(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetLocalName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_prefix(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetPrefix();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_qualified_name(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetQualifiedName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_namespace_uri(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetNamespaceURI();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_base_uri(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetBaseURI();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_xml_lang(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetXmlLang();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK xml_reader_is_empty_element(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->IsEmptyElement();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK xml_reader_has_value(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->HasValue();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_value(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetValue();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK xml_reader_has_attributes(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->HasAttributes();
+
+  // Return type: bool
+  return _retval;
+}
+
+size_t CEF_CALLBACK
+xml_reader_get_attribute_count(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  size_t _retval = CefXmlReaderCppToC::Get(self)->GetAttributeCount();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_attribute_byindex(struct _cef_xml_reader_t* self, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetAttribute(index);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_attribute_byqname(struct _cef_xml_reader_t* self,
+                                 const cef_string_t* qualifiedName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: qualifiedName; type: string_byref_const
+  DCHECK(qualifiedName);
+  if (!qualifiedName)
+    return NULL;
+
+  // Execute
+  CefString _retval =
+      CefXmlReaderCppToC::Get(self)->GetAttribute(CefString(qualifiedName));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_attribute_bylname(struct _cef_xml_reader_t* self,
+                                 const cef_string_t* localName,
+                                 const cef_string_t* namespaceURI) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+  // Verify param: localName; type: string_byref_const
+  DCHECK(localName);
+  if (!localName)
+    return NULL;
+  // Verify param: namespaceURI; type: string_byref_const
+  DCHECK(namespaceURI);
+  if (!namespaceURI)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetAttribute(
+      CefString(localName), CefString(namespaceURI));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_inner_xml(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetInnerXml();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+cef_string_userfree_t CEF_CALLBACK
+xml_reader_get_outer_xml(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefXmlReaderCppToC::Get(self)->GetOuterXml();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int CEF_CALLBACK xml_reader_get_line_number(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int _retval = CefXmlReaderCppToC::Get(self)->GetLineNumber();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK
+xml_reader_move_to_attribute_byindex(struct _cef_xml_reader_t* self,
+                                     int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->MoveToAttribute(index);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+xml_reader_move_to_attribute_byqname(struct _cef_xml_reader_t* self,
+                                     const cef_string_t* qualifiedName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: qualifiedName; type: string_byref_const
+  DCHECK(qualifiedName);
+  if (!qualifiedName)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefXmlReaderCppToC::Get(self)->MoveToAttribute(CefString(qualifiedName));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+xml_reader_move_to_attribute_bylname(struct _cef_xml_reader_t* self,
+                                     const cef_string_t* localName,
+                                     const cef_string_t* namespaceURI) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: localName; type: string_byref_const
+  DCHECK(localName);
+  if (!localName)
+    return 0;
+  // Verify param: namespaceURI; type: string_byref_const
+  DCHECK(namespaceURI);
+  if (!namespaceURI)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->MoveToAttribute(
+      CefString(localName), CefString(namespaceURI));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+xml_reader_move_to_first_attribute(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->MoveToFirstAttribute();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+xml_reader_move_to_next_attribute(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->MoveToNextAttribute();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK
+xml_reader_move_to_carrying_element(struct _cef_xml_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefXmlReaderCppToC::Get(self)->MoveToCarryingElement();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefXmlReaderCppToC::CefXmlReaderCppToC() {
+  GetStruct()->move_to_next_node = xml_reader_move_to_next_node;
+  GetStruct()->close = xml_reader_close;
+  GetStruct()->has_error = xml_reader_has_error;
+  GetStruct()->get_error = xml_reader_get_error;
+  GetStruct()->get_type = xml_reader_get_type;
+  GetStruct()->get_depth = xml_reader_get_depth;
+  GetStruct()->get_local_name = xml_reader_get_local_name;
+  GetStruct()->get_prefix = xml_reader_get_prefix;
+  GetStruct()->get_qualified_name = xml_reader_get_qualified_name;
+  GetStruct()->get_namespace_uri = xml_reader_get_namespace_uri;
+  GetStruct()->get_base_uri = xml_reader_get_base_uri;
+  GetStruct()->get_xml_lang = xml_reader_get_xml_lang;
+  GetStruct()->is_empty_element = xml_reader_is_empty_element;
+  GetStruct()->has_value = xml_reader_has_value;
+  GetStruct()->get_value = xml_reader_get_value;
+  GetStruct()->has_attributes = xml_reader_has_attributes;
+  GetStruct()->get_attribute_count = xml_reader_get_attribute_count;
+  GetStruct()->get_attribute_byindex = xml_reader_get_attribute_byindex;
+  GetStruct()->get_attribute_byqname = xml_reader_get_attribute_byqname;
+  GetStruct()->get_attribute_bylname = xml_reader_get_attribute_bylname;
+  GetStruct()->get_inner_xml = xml_reader_get_inner_xml;
+  GetStruct()->get_outer_xml = xml_reader_get_outer_xml;
+  GetStruct()->get_line_number = xml_reader_get_line_number;
+  GetStruct()->move_to_attribute_byindex = xml_reader_move_to_attribute_byindex;
+  GetStruct()->move_to_attribute_byqname = xml_reader_move_to_attribute_byqname;
+  GetStruct()->move_to_attribute_bylname = xml_reader_move_to_attribute_bylname;
+  GetStruct()->move_to_first_attribute = xml_reader_move_to_first_attribute;
+  GetStruct()->move_to_next_attribute = xml_reader_move_to_next_attribute;
+  GetStruct()->move_to_carrying_element = xml_reader_move_to_carrying_element;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefXmlReaderCppToC::~CefXmlReaderCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefXmlReader>
+CefCppToCRefCounted<CefXmlReaderCppToC, CefXmlReader, cef_xml_reader_t>::
+    UnwrapDerived(CefWrapperType type, cef_xml_reader_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefXmlReaderCppToC,
+                                   CefXmlReader,
+                                   cef_xml_reader_t>::kWrapperType =
+    WT_XML_READER;
diff --git a/src/libcef_dll/cpptoc/xml_reader_cpptoc.h b/src/libcef_dll/cpptoc/xml_reader_cpptoc.h
new file mode 100644
index 0000000..ba72e4b
--- /dev/null
+++ b/src/libcef_dll/cpptoc/xml_reader_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c0156e6e184a530e6be658669a46f918ca301ab5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_XML_READER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_XML_READER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_xml_reader_capi.h"
+#include "include/cef_xml_reader.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefXmlReaderCppToC : public CefCppToCRefCounted<CefXmlReaderCppToC,
+                                                      CefXmlReader,
+                                                      cef_xml_reader_t> {
+ public:
+  CefXmlReaderCppToC();
+  virtual ~CefXmlReaderCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_XML_READER_CPPTOC_H_
diff --git a/src/libcef_dll/cpptoc/zip_reader_cpptoc.cc b/src/libcef_dll/cpptoc/zip_reader_cpptoc.cc
new file mode 100644
index 0000000..55a7a98
--- /dev/null
+++ b/src/libcef_dll/cpptoc/zip_reader_cpptoc.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e5b0f5920957b5db2ff2caff30f5547d4dd3e95f$
+//
+
+#include "libcef_dll/cpptoc/zip_reader_cpptoc.h"
+#include "libcef_dll/cpptoc/stream_reader_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT cef_zip_reader_t* cef_zip_reader_create(
+    cef_stream_reader_t* stream) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: stream; type: refptr_same
+  DCHECK(stream);
+  if (!stream)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefZipReader> _retval =
+      CefZipReader::Create(CefStreamReaderCppToC::Unwrap(stream));
+
+  // Return type: refptr_same
+  return CefZipReaderCppToC::Wrap(_retval);
+}
+
+namespace {
+
+// MEMBER FUNCTIONS - Body may be edited by hand.
+
+int CEF_CALLBACK zip_reader_move_to_first_file(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefZipReaderCppToC::Get(self)->MoveToFirstFile();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK zip_reader_move_to_next_file(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefZipReaderCppToC::Get(self)->MoveToNextFile();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK zip_reader_move_to_file(struct _cef_zip_reader_t* self,
+                                         const cef_string_t* fileName,
+                                         int caseSensitive) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(fileName);
+  if (!fileName)
+    return 0;
+
+  // Execute
+  bool _retval = CefZipReaderCppToC::Get(self)->MoveToFile(
+      CefString(fileName), caseSensitive ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK zip_reader_close(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefZipReaderCppToC::Get(self)->Close();
+
+  // Return type: bool
+  return _retval;
+}
+
+cef_string_userfree_t CEF_CALLBACK
+zip_reader_get_file_name(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefZipReaderCppToC::Get(self)->GetFileName();
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+int64 CEF_CALLBACK zip_reader_get_file_size(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefZipReaderCppToC::Get(self)->GetFileSize();
+
+  // Return type: simple
+  return _retval;
+}
+
+cef_time_t CEF_CALLBACK
+zip_reader_get_file_last_modified(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return CefTime();
+
+  // Execute
+  cef_time_t _retval = CefZipReaderCppToC::Get(self)->GetFileLastModified();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK zip_reader_open_file(struct _cef_zip_reader_t* self,
+                                      const cef_string_t* password) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Unverified params: password
+
+  // Execute
+  bool _retval = CefZipReaderCppToC::Get(self)->OpenFile(CefString(password));
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK zip_reader_close_file(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefZipReaderCppToC::Get(self)->CloseFile();
+
+  // Return type: bool
+  return _retval;
+}
+
+int CEF_CALLBACK zip_reader_read_file(struct _cef_zip_reader_t* self,
+                                      void* buffer,
+                                      size_t bufferSize) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return 0;
+
+  // Execute
+  int _retval = CefZipReaderCppToC::Get(self)->ReadFile(buffer, bufferSize);
+
+  // Return type: simple
+  return _retval;
+}
+
+int64 CEF_CALLBACK zip_reader_tell(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  int64 _retval = CefZipReaderCppToC::Get(self)->Tell();
+
+  // Return type: simple
+  return _retval;
+}
+
+int CEF_CALLBACK zip_reader_eof(struct _cef_zip_reader_t* self) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  DCHECK(self);
+  if (!self)
+    return 0;
+
+  // Execute
+  bool _retval = CefZipReaderCppToC::Get(self)->Eof();
+
+  // Return type: bool
+  return _retval;
+}
+
+}  // namespace
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefZipReaderCppToC::CefZipReaderCppToC() {
+  GetStruct()->move_to_first_file = zip_reader_move_to_first_file;
+  GetStruct()->move_to_next_file = zip_reader_move_to_next_file;
+  GetStruct()->move_to_file = zip_reader_move_to_file;
+  GetStruct()->close = zip_reader_close;
+  GetStruct()->get_file_name = zip_reader_get_file_name;
+  GetStruct()->get_file_size = zip_reader_get_file_size;
+  GetStruct()->get_file_last_modified = zip_reader_get_file_last_modified;
+  GetStruct()->open_file = zip_reader_open_file;
+  GetStruct()->close_file = zip_reader_close_file;
+  GetStruct()->read_file = zip_reader_read_file;
+  GetStruct()->tell = zip_reader_tell;
+  GetStruct()->eof = zip_reader_eof;
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefZipReaderCppToC::~CefZipReaderCppToC() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+CefRefPtr<CefZipReader>
+CefCppToCRefCounted<CefZipReaderCppToC, CefZipReader, cef_zip_reader_t>::
+    UnwrapDerived(CefWrapperType type, cef_zip_reader_t* s) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCppToCRefCounted<CefZipReaderCppToC,
+                                   CefZipReader,
+                                   cef_zip_reader_t>::kWrapperType =
+    WT_ZIP_READER;
diff --git a/src/libcef_dll/cpptoc/zip_reader_cpptoc.h b/src/libcef_dll/cpptoc/zip_reader_cpptoc.h
new file mode 100644
index 0000000..554840d
--- /dev/null
+++ b/src/libcef_dll/cpptoc/zip_reader_cpptoc.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=07d189631f14e4b6af97b3bd01394dae866efade$
+//
+
+#ifndef CEF_LIBCEF_DLL_CPPTOC_ZIP_READER_CPPTOC_H_
+#define CEF_LIBCEF_DLL_CPPTOC_ZIP_READER_CPPTOC_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_zip_reader_capi.h"
+#include "include/cef_zip_reader.h"
+#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
+
+// Wrap a C++ class with a C structure.
+// This class may be instantiated and accessed DLL-side only.
+class CefZipReaderCppToC : public CefCppToCRefCounted<CefZipReaderCppToC,
+                                                      CefZipReader,
+                                                      cef_zip_reader_t> {
+ public:
+  CefZipReaderCppToC();
+  virtual ~CefZipReaderCppToC();
+};
+
+#endif  // CEF_LIBCEF_DLL_CPPTOC_ZIP_READER_CPPTOC_H_
diff --git a/src/libcef_dll/ctocpp/accessibility_handler_ctocpp.cc b/src/libcef_dll/ctocpp/accessibility_handler_ctocpp.cc
new file mode 100644
index 0000000..3327abf
--- /dev/null
+++ b/src/libcef_dll/ctocpp/accessibility_handler_ctocpp.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8bd602ae38dd8210a27c6fc0671c97d79268633c$
+//
+
+#include "libcef_dll/ctocpp/accessibility_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/value_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefAccessibilityHandlerCToCpp::OnAccessibilityTreeChange(
+    CefRefPtr<CefValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_accessibility_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_accessibility_tree_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_diff
+  DCHECK(value.get());
+  if (!value.get())
+    return;
+
+  // Execute
+  _struct->on_accessibility_tree_change(_struct, CefValueCppToC::Wrap(value));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefAccessibilityHandlerCToCpp::OnAccessibilityLocationChange(
+    CefRefPtr<CefValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_accessibility_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_accessibility_location_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_diff
+  DCHECK(value.get());
+  if (!value.get())
+    return;
+
+  // Execute
+  _struct->on_accessibility_location_change(_struct,
+                                            CefValueCppToC::Wrap(value));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAccessibilityHandlerCToCpp::CefAccessibilityHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAccessibilityHandlerCToCpp::~CefAccessibilityHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_accessibility_handler_t* CefCToCppRefCounted<
+    CefAccessibilityHandlerCToCpp,
+    CefAccessibilityHandler,
+    cef_accessibility_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                CefAccessibilityHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefAccessibilityHandlerCToCpp,
+                                   CefAccessibilityHandler,
+                                   cef_accessibility_handler_t>::kWrapperType =
+    WT_ACCESSIBILITY_HANDLER;
diff --git a/src/libcef_dll/ctocpp/accessibility_handler_ctocpp.h b/src/libcef_dll/ctocpp/accessibility_handler_ctocpp.h
new file mode 100644
index 0000000..c4544f6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/accessibility_handler_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=80cb1a6def2d011b59ebd72953d404bff04ba31c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_ACCESSIBILITY_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_ACCESSIBILITY_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_accessibility_handler_capi.h"
+#include "include/cef_accessibility_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefAccessibilityHandlerCToCpp
+    : public CefCToCppRefCounted<CefAccessibilityHandlerCToCpp,
+                                 CefAccessibilityHandler,
+                                 cef_accessibility_handler_t> {
+ public:
+  CefAccessibilityHandlerCToCpp();
+  virtual ~CefAccessibilityHandlerCToCpp();
+
+  // CefAccessibilityHandler methods.
+  void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) override;
+  void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_ACCESSIBILITY_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/app_ctocpp.cc b/src/libcef_dll/ctocpp/app_ctocpp.cc
new file mode 100644
index 0000000..4f90c06
--- /dev/null
+++ b/src/libcef_dll/ctocpp/app_ctocpp.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4f70dc8d5391cb5254f038ec2b3b56804b447087$
+//
+
+#include "libcef_dll/ctocpp/app_ctocpp.h"
+#include "libcef_dll/cpptoc/command_line_cpptoc.h"
+#include "libcef_dll/cpptoc/scheme_registrar_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_process_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/render_process_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/resource_bundle_handler_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefAppCToCpp::OnBeforeCommandLineProcessing(
+    const CefString& process_type,
+    CefRefPtr<CefCommandLine> command_line) {
+  cef_app_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_command_line_processing))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: command_line; type: refptr_diff
+  DCHECK(command_line.get());
+  if (!command_line.get())
+    return;
+  // Unverified params: process_type
+
+  // Execute
+  _struct->on_before_command_line_processing(
+      _struct, process_type.GetStruct(),
+      CefCommandLineCppToC::Wrap(command_line));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefAppCToCpp::OnRegisterCustomSchemes(
+    CefRawPtr<CefSchemeRegistrar> registrar) {
+  cef_app_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_register_custom_schemes))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: registrar; type: rawptr_diff
+  DCHECK(registrar);
+  if (!registrar)
+    return;
+
+  // Translate param: registrar; type: rawptr_diff
+  CefOwnPtr<CefSchemeRegistrarCppToC> registrarPtr(
+      CefSchemeRegistrarCppToC::WrapRaw(registrar));
+
+  // Execute
+  _struct->on_register_custom_schemes(_struct, registrarPtr->GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResourceBundleHandler> CefAppCToCpp::GetResourceBundleHandler() {
+  cef_app_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_resource_bundle_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_resource_bundle_handler_t* _retval =
+      _struct->get_resource_bundle_handler(_struct);
+
+  // Return type: refptr_same
+  return CefResourceBundleHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserProcessHandler> CefAppCToCpp::GetBrowserProcessHandler() {
+  cef_app_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_browser_process_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_process_handler_t* _retval =
+      _struct->get_browser_process_handler(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserProcessHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRenderProcessHandler> CefAppCToCpp::GetRenderProcessHandler() {
+  cef_app_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_render_process_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_render_process_handler_t* _retval =
+      _struct->get_render_process_handler(_struct);
+
+  // Return type: refptr_same
+  return CefRenderProcessHandlerCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAppCToCpp::CefAppCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAppCToCpp::~CefAppCToCpp() {}
+
+template <>
+cef_app_t* CefCToCppRefCounted<CefAppCToCpp, CefApp, cef_app_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefApp* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefAppCToCpp, CefApp, cef_app_t>::kWrapperType = WT_APP;
diff --git a/src/libcef_dll/ctocpp/app_ctocpp.h b/src/libcef_dll/ctocpp/app_ctocpp.h
new file mode 100644
index 0000000..125afd4
--- /dev/null
+++ b/src/libcef_dll/ctocpp/app_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5d865c197ea6caabc9c68b40e65d211499e462c5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_APP_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_APP_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_app_capi.h"
+#include "include/cef_app.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefAppCToCpp
+    : public CefCToCppRefCounted<CefAppCToCpp, CefApp, cef_app_t> {
+ public:
+  CefAppCToCpp();
+  virtual ~CefAppCToCpp();
+
+  // CefApp methods.
+  void OnBeforeCommandLineProcessing(
+      const CefString& process_type,
+      CefRefPtr<CefCommandLine> command_line) override;
+  void OnRegisterCustomSchemes(
+      CefRawPtr<CefSchemeRegistrar> registrar) override;
+  CefRefPtr<CefResourceBundleHandler> GetResourceBundleHandler() override;
+  CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override;
+  CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_APP_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/audio_handler_ctocpp.cc b/src/libcef_dll/ctocpp/audio_handler_ctocpp.cc
new file mode 100644
index 0000000..74d23aa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/audio_handler_ctocpp.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d7f2fb8ad6fe2bd0ab928b09ab596b12e9049ddf$
+//
+
+#include "libcef_dll/ctocpp/audio_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefAudioHandlerCToCpp::GetAudioParameters(CefRefPtr<CefBrowser> browser,
+                                               CefAudioParameters& params) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_audio_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_audio_parameters))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->get_audio_parameters(
+      _struct, CefBrowserCppToC::Wrap(browser), &params);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefAudioHandlerCToCpp::OnAudioStreamStarted(
+    CefRefPtr<CefBrowser> browser,
+    const CefAudioParameters& params,
+    int channels) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_audio_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_audio_stream_started))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_audio_stream_started(_struct, CefBrowserCppToC::Wrap(browser),
+                                   &params, channels);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefAudioHandlerCToCpp::OnAudioStreamPacket(CefRefPtr<CefBrowser> browser,
+                                                const float** data,
+                                                int frames,
+                                                int64 pts) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_audio_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_audio_stream_packet))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  _struct->on_audio_stream_packet(_struct, CefBrowserCppToC::Wrap(browser),
+                                  data, frames, pts);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefAudioHandlerCToCpp::OnAudioStreamStopped(
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_audio_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_audio_stream_stopped))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_audio_stream_stopped(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefAudioHandlerCToCpp::OnAudioStreamError(CefRefPtr<CefBrowser> browser,
+                                               const CefString& message) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_audio_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_audio_stream_error))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: message; type: string_byref_const
+  DCHECK(!message.empty());
+  if (message.empty())
+    return;
+
+  // Execute
+  _struct->on_audio_stream_error(_struct, CefBrowserCppToC::Wrap(browser),
+                                 message.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAudioHandlerCToCpp::CefAudioHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAudioHandlerCToCpp::~CefAudioHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_audio_handler_t*
+CefCToCppRefCounted<CefAudioHandlerCToCpp,
+                    CefAudioHandler,
+                    cef_audio_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefAudioHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefAudioHandlerCToCpp,
+                                   CefAudioHandler,
+                                   cef_audio_handler_t>::kWrapperType =
+    WT_AUDIO_HANDLER;
diff --git a/src/libcef_dll/ctocpp/audio_handler_ctocpp.h b/src/libcef_dll/ctocpp/audio_handler_ctocpp.h
new file mode 100644
index 0000000..16a0a76
--- /dev/null
+++ b/src/libcef_dll/ctocpp/audio_handler_ctocpp.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e5517ccac966337ef7dc576a24eedfe1154b2813$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_AUDIO_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_AUDIO_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_audio_handler_capi.h"
+#include "include/cef_audio_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefAudioHandlerCToCpp : public CefCToCppRefCounted<CefAudioHandlerCToCpp,
+                                                         CefAudioHandler,
+                                                         cef_audio_handler_t> {
+ public:
+  CefAudioHandlerCToCpp();
+  virtual ~CefAudioHandlerCToCpp();
+
+  // CefAudioHandler methods.
+  bool GetAudioParameters(CefRefPtr<CefBrowser> browser,
+                          CefAudioParameters& params) override;
+  void OnAudioStreamStarted(CefRefPtr<CefBrowser> browser,
+                            const CefAudioParameters& params,
+                            int channels) override;
+  void OnAudioStreamPacket(CefRefPtr<CefBrowser> browser,
+                           const float** data,
+                           int frames,
+                           int64 pts) override;
+  void OnAudioStreamStopped(CefRefPtr<CefBrowser> browser) override;
+  void OnAudioStreamError(CefRefPtr<CefBrowser> browser,
+                          const CefString& message) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_AUDIO_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/auth_callback_ctocpp.cc b/src/libcef_dll/ctocpp/auth_callback_ctocpp.cc
new file mode 100644
index 0000000..a9a1711
--- /dev/null
+++ b/src/libcef_dll/ctocpp/auth_callback_ctocpp.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=54b2a86796e3c6936aa4475004d620677666a796$
+//
+
+#include "libcef_dll/ctocpp/auth_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefAuthCallbackCToCpp::Continue(const CefString& username,
+                                     const CefString& password) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_auth_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: username, password
+
+  // Execute
+  _struct->cont(_struct, username.GetStruct(), password.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") void CefAuthCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_auth_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefAuthCallbackCToCpp::CefAuthCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefAuthCallbackCToCpp::~CefAuthCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_auth_callback_t*
+CefCToCppRefCounted<CefAuthCallbackCToCpp,
+                    CefAuthCallback,
+                    cef_auth_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefAuthCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefAuthCallbackCToCpp,
+                                   CefAuthCallback,
+                                   cef_auth_callback_t>::kWrapperType =
+    WT_AUTH_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/auth_callback_ctocpp.h b/src/libcef_dll/ctocpp/auth_callback_ctocpp.h
new file mode 100644
index 0000000..e5c973b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/auth_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=199cea821f0726c069942b34ea25eeb4fc99ea25$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_AUTH_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_AUTH_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_auth_callback_capi.h"
+#include "include/cef_auth_callback.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefAuthCallbackCToCpp : public CefCToCppRefCounted<CefAuthCallbackCToCpp,
+                                                         CefAuthCallback,
+                                                         cef_auth_callback_t> {
+ public:
+  CefAuthCallbackCToCpp();
+  virtual ~CefAuthCallbackCToCpp();
+
+  // CefAuthCallback methods.
+  void Continue(const CefString& username, const CefString& password) OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_AUTH_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/base_ref_counted_ctocpp.cc b/src/libcef_dll/ctocpp/base_ref_counted_ctocpp.cc
new file mode 100644
index 0000000..4b14e3f
--- /dev/null
+++ b/src/libcef_dll/ctocpp/base_ref_counted_ctocpp.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef_dll/ctocpp/base_ref_counted_ctocpp.h"
+
+CefBaseRefCountedCToCpp::CefBaseRefCountedCToCpp() {}
+
+template <>
+cef_base_ref_counted_t* CefCToCppRefCounted<
+    CefBaseRefCountedCToCpp,
+    CefBaseRefCounted,
+    cef_base_ref_counted_t>::UnwrapDerived(CefWrapperType type,
+                                           CefBaseRefCounted* c) {
+  NOTREACHED();
+  return NULL;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefBaseRefCountedCToCpp,
+                                   CefBaseRefCounted,
+                                   cef_base_ref_counted_t>::kWrapperType =
+    WT_BASE_REF_COUNTED;
diff --git a/src/libcef_dll/ctocpp/base_ref_counted_ctocpp.h b/src/libcef_dll/ctocpp/base_ref_counted_ctocpp.h
new file mode 100644
index 0000000..dfff39e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/base_ref_counted_ctocpp.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_BASE_REF_COUNTED_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_BASE_REF_COUNTED_CTOCPP_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+// Wrap a C structure with a C++ class.
+class CefBaseRefCountedCToCpp
+    : public CefCToCppRefCounted<CefBaseRefCountedCToCpp,
+                                 CefBaseRefCounted,
+                                 cef_base_ref_counted_t> {
+ public:
+  CefBaseRefCountedCToCpp();
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_BASE_REF_COUNTED_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/base_scoped_ctocpp.cc b/src/libcef_dll/ctocpp/base_scoped_ctocpp.cc
new file mode 100644
index 0000000..40accb5
--- /dev/null
+++ b/src/libcef_dll/ctocpp/base_scoped_ctocpp.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef_dll/ctocpp/base_scoped_ctocpp.h"
+
+CefBaseScopedCToCpp::CefBaseScopedCToCpp() {}
+
+template <>
+cef_base_scoped_t*
+CefCToCppScoped<CefBaseScopedCToCpp, CefBaseScoped, cef_base_scoped_t>::
+    UnwrapDerivedOwn(CefWrapperType type, CefOwnPtr<CefBaseScoped> c) {
+  NOTREACHED();
+  return NULL;
+}
+
+template <>
+cef_base_scoped_t*
+CefCToCppScoped<CefBaseScopedCToCpp, CefBaseScoped, cef_base_scoped_t>::
+    UnwrapDerivedRaw(CefWrapperType type, CefRawPtr<CefBaseScoped> c) {
+  NOTREACHED();
+  return NULL;
+}
+
+template <>
+CefWrapperType CefCToCppScoped<CefBaseScopedCToCpp,
+                               CefBaseScoped,
+                               cef_base_scoped_t>::kWrapperType =
+    WT_BASE_SCOPED;
diff --git a/src/libcef_dll/ctocpp/base_scoped_ctocpp.h b/src/libcef_dll/ctocpp/base_scoped_ctocpp.h
new file mode 100644
index 0000000..6e7c085
--- /dev/null
+++ b/src/libcef_dll/ctocpp/base_scoped_ctocpp.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_BASE_SCOPED_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_BASE_SCOPED_CTOCPP_H_
+#pragma once
+
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/ctocpp/ctocpp_scoped.h"
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+// Wrap a C structure with a C++ class.
+class CefBaseScopedCToCpp : public CefCToCppScoped<CefBaseScopedCToCpp,
+                                                   CefBaseScoped,
+                                                   cef_base_scoped_t> {
+ public:
+  CefBaseScopedCToCpp();
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_BASE_SCOPED_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/before_download_callback_ctocpp.cc b/src/libcef_dll/ctocpp/before_download_callback_ctocpp.cc
new file mode 100644
index 0000000..9dc1a71
--- /dev/null
+++ b/src/libcef_dll/ctocpp/before_download_callback_ctocpp.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=be4b247da2987252e1471149aade76c4d4a34394$
+//
+
+#include "libcef_dll/ctocpp/before_download_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefBeforeDownloadCallbackCToCpp::Continue(const CefString& download_path,
+                                               bool show_dialog) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_before_download_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: download_path
+
+  // Execute
+  _struct->cont(_struct, download_path.GetStruct(), show_dialog);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBeforeDownloadCallbackCToCpp::CefBeforeDownloadCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBeforeDownloadCallbackCToCpp::~CefBeforeDownloadCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_before_download_callback_t* CefCToCppRefCounted<
+    CefBeforeDownloadCallbackCToCpp,
+    CefBeforeDownloadCallback,
+    cef_before_download_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                   CefBeforeDownloadCallback*
+                                                       c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefBeforeDownloadCallbackCToCpp,
+                        CefBeforeDownloadCallback,
+                        cef_before_download_callback_t>::kWrapperType =
+        WT_BEFORE_DOWNLOAD_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/before_download_callback_ctocpp.h b/src/libcef_dll/ctocpp/before_download_callback_ctocpp.h
new file mode 100644
index 0000000..ec68a09
--- /dev/null
+++ b/src/libcef_dll/ctocpp/before_download_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6f17e4bcee192e06ece3358a1fb99edebd088936$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_BEFORE_DOWNLOAD_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_BEFORE_DOWNLOAD_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_download_handler_capi.h"
+#include "include/cef_download_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBeforeDownloadCallbackCToCpp
+    : public CefCToCppRefCounted<CefBeforeDownloadCallbackCToCpp,
+                                 CefBeforeDownloadCallback,
+                                 cef_before_download_callback_t> {
+ public:
+  CefBeforeDownloadCallbackCToCpp();
+  virtual ~CefBeforeDownloadCallbackCToCpp();
+
+  // CefBeforeDownloadCallback methods.
+  void Continue(const CefString& download_path, bool show_dialog) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_BEFORE_DOWNLOAD_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/binary_value_ctocpp.cc b/src/libcef_dll/ctocpp/binary_value_ctocpp.cc
new file mode 100644
index 0000000..96a878c
--- /dev/null
+++ b/src/libcef_dll/ctocpp/binary_value_ctocpp.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=352b1570cd74cce646f914bf25fc97a749f15898$
+//
+
+#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefBinaryValue::Create(const void* data,
+                                                 size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return nullptr;
+
+  // Execute
+  cef_binary_value_t* _retval = cef_binary_value_create(data, data_size);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefBinaryValueCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_binary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBinaryValueCToCpp::IsOwned() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_binary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_owned))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_owned(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBinaryValueCToCpp::IsSame(CefRefPtr<CefBinaryValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_binary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefBinaryValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBinaryValueCToCpp::IsEqual(CefRefPtr<CefBinaryValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_binary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_equal))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_equal(_struct, CefBinaryValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefBinaryValueCToCpp::Copy() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_binary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, copy))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->copy(_struct);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefBinaryValueCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_binary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+size_t CefBinaryValueCToCpp::GetData(void* buffer,
+                                     size_t buffer_size,
+                                     size_t data_offset) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_binary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_data))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return 0;
+
+  // Execute
+  size_t _retval = _struct->get_data(_struct, buffer, buffer_size, data_offset);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBinaryValueCToCpp::CefBinaryValueCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBinaryValueCToCpp::~CefBinaryValueCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_binary_value_t*
+CefCToCppRefCounted<CefBinaryValueCToCpp, CefBinaryValue, cef_binary_value_t>::
+    UnwrapDerived(CefWrapperType type, CefBinaryValue* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefBinaryValueCToCpp,
+                                   CefBinaryValue,
+                                   cef_binary_value_t>::kWrapperType =
+    WT_BINARY_VALUE;
diff --git a/src/libcef_dll/ctocpp/binary_value_ctocpp.h b/src/libcef_dll/ctocpp/binary_value_ctocpp.h
new file mode 100644
index 0000000..ef84ff0
--- /dev/null
+++ b/src/libcef_dll/ctocpp/binary_value_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=eca472fd8d1ebb66dba55157e6522c72fb72203c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_BINARY_VALUE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_BINARY_VALUE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBinaryValueCToCpp : public CefCToCppRefCounted<CefBinaryValueCToCpp,
+                                                        CefBinaryValue,
+                                                        cef_binary_value_t> {
+ public:
+  CefBinaryValueCToCpp();
+  virtual ~CefBinaryValueCToCpp();
+
+  // CefBinaryValue methods.
+  bool IsValid() OVERRIDE;
+  bool IsOwned() OVERRIDE;
+  bool IsSame(CefRefPtr<CefBinaryValue> that) OVERRIDE;
+  bool IsEqual(CefRefPtr<CefBinaryValue> that) OVERRIDE;
+  CefRefPtr<CefBinaryValue> Copy() OVERRIDE;
+  size_t GetSize() OVERRIDE;
+  size_t GetData(void* buffer, size_t buffer_size, size_t data_offset) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_BINARY_VALUE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/browser_ctocpp.cc b/src/libcef_dll/ctocpp/browser_ctocpp.cc
new file mode 100644
index 0000000..3c9584a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/browser_ctocpp.cc
@@ -0,0 +1,397 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9d16a2ca7a348181df0b026b3013d362170f7085$
+//
+
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include <algorithm>
+#include "libcef_dll/ctocpp/browser_host_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefBrowserHost> CefBrowserCToCpp::GetHost() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_host))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_host_t* _retval = _struct->get_host(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserHostCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserCToCpp::CanGoBack() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_go_back))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->can_go_back(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserCToCpp::GoBack() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, go_back))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->go_back(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserCToCpp::CanGoForward() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_go_forward))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->can_go_forward(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserCToCpp::GoForward() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, go_forward))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->go_forward(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserCToCpp::IsLoading() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_loading))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_loading(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserCToCpp::Reload() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, reload))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->reload(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserCToCpp::ReloadIgnoreCache() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, reload_ignore_cache))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->reload_ignore_cache(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserCToCpp::StopLoad() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, stop_load))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->stop_load(_struct);
+}
+
+NO_SANITIZE("cfi-icall") int CefBrowserCToCpp::GetIdentifier() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_identifier))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_identifier(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserCToCpp::IsSame(CefRefPtr<CefBrowser> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefBrowserCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserCToCpp::IsPopup() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_popup))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_popup(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserCToCpp::HasDocument() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_document))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_document(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefFrame> CefBrowserCToCpp::GetMainFrame() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_main_frame))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_frame_t* _retval = _struct->get_main_frame(_struct);
+
+  // Return type: refptr_same
+  return CefFrameCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFrame> CefBrowserCToCpp::GetFocusedFrame() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_focused_frame))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_frame_t* _retval = _struct->get_focused_frame(_struct);
+
+  // Return type: refptr_same
+  return CefFrameCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFrame> CefBrowserCToCpp::GetFrame(int64 identifier) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame_byident))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_frame_t* _retval = _struct->get_frame_byident(_struct, identifier);
+
+  // Return type: refptr_same
+  return CefFrameCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFrame> CefBrowserCToCpp::GetFrame(const CefString& name) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: name
+
+  // Execute
+  cef_frame_t* _retval = _struct->get_frame(_struct, name.GetStruct());
+
+  // Return type: refptr_same
+  return CefFrameCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefBrowserCToCpp::GetFrameCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_frame_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserCToCpp::GetFrameIdentifiers(std::vector<int64>& identifiers) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame_identifiers))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: identifiers; type: simple_vec_byref
+  size_t identifiersSize = identifiers.size();
+  size_t identifiersCount = std::max(GetFrameCount(), identifiersSize);
+  int64* identifiersList = NULL;
+  if (identifiersCount > 0) {
+    identifiersList = new int64[identifiersCount];
+    DCHECK(identifiersList);
+    if (identifiersList) {
+      memset(identifiersList, 0, sizeof(int64) * identifiersCount);
+    }
+    if (identifiersList && identifiersSize > 0) {
+      for (size_t i = 0; i < identifiersSize; ++i) {
+        identifiersList[i] = identifiers[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->get_frame_identifiers(_struct, &identifiersCount, identifiersList);
+
+  // Restore param:identifiers; type: simple_vec_byref
+  identifiers.clear();
+  if (identifiersCount > 0 && identifiersList) {
+    for (size_t i = 0; i < identifiersCount; ++i) {
+      identifiers.push_back(identifiersList[i]);
+    }
+    delete[] identifiersList;
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserCToCpp::GetFrameNames(std::vector<CefString>& names) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame_names))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: names; type: string_vec_byref
+  cef_string_list_t namesList = cef_string_list_alloc();
+  DCHECK(namesList);
+  if (namesList)
+    transfer_string_list_contents(names, namesList);
+
+  // Execute
+  _struct->get_frame_names(_struct, namesList);
+
+  // Restore param:names; type: string_vec_byref
+  if (namesList) {
+    names.clear();
+    transfer_string_list_contents(namesList, names);
+    cef_string_list_free(namesList);
+  }
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserCToCpp::CefBrowserCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserCToCpp::~CefBrowserCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_browser_t*
+CefCToCppRefCounted<CefBrowserCToCpp, CefBrowser, cef_browser_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefBrowser* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefBrowserCToCpp,
+                                   CefBrowser,
+                                   cef_browser_t>::kWrapperType = WT_BROWSER;
diff --git a/src/libcef_dll/ctocpp/browser_ctocpp.h b/src/libcef_dll/ctocpp/browser_ctocpp.h
new file mode 100644
index 0000000..0cae983
--- /dev/null
+++ b/src/libcef_dll/ctocpp/browser_ctocpp.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=422ef21aa832c006ef67641e44667c88dc5a6b1b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_BROWSER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBrowserCToCpp
+    : public CefCToCppRefCounted<CefBrowserCToCpp, CefBrowser, cef_browser_t> {
+ public:
+  CefBrowserCToCpp();
+  virtual ~CefBrowserCToCpp();
+
+  // CefBrowser methods.
+  CefRefPtr<CefBrowserHost> GetHost() OVERRIDE;
+  bool CanGoBack() OVERRIDE;
+  void GoBack() OVERRIDE;
+  bool CanGoForward() OVERRIDE;
+  void GoForward() OVERRIDE;
+  bool IsLoading() OVERRIDE;
+  void Reload() OVERRIDE;
+  void ReloadIgnoreCache() OVERRIDE;
+  void StopLoad() OVERRIDE;
+  int GetIdentifier() OVERRIDE;
+  bool IsSame(CefRefPtr<CefBrowser> that) OVERRIDE;
+  bool IsPopup() OVERRIDE;
+  bool HasDocument() OVERRIDE;
+  CefRefPtr<CefFrame> GetMainFrame() OVERRIDE;
+  CefRefPtr<CefFrame> GetFocusedFrame() OVERRIDE;
+  CefRefPtr<CefFrame> GetFrame(int64 identifier) OVERRIDE;
+  CefRefPtr<CefFrame> GetFrame(const CefString& name) OVERRIDE;
+  size_t GetFrameCount() OVERRIDE;
+  void GetFrameIdentifiers(std::vector<int64>& identifiers) OVERRIDE;
+  void GetFrameNames(std::vector<CefString>& names) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_BROWSER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/browser_host_ctocpp.cc b/src/libcef_dll/ctocpp/browser_host_ctocpp.cc
new file mode 100644
index 0000000..68b407e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/browser_host_ctocpp.cc
@@ -0,0 +1,1182 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a362e11e85ce68488bbb0f5232b01f53cffeec1d$
+//
+
+#include "libcef_dll/ctocpp/browser_host_ctocpp.h"
+#include "libcef_dll/cpptoc/client_cpptoc.h"
+#include "libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h"
+#include "libcef_dll/cpptoc/download_image_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h"
+#include "libcef_dll/cpptoc/pdf_print_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/drag_data_ctocpp.h"
+#include "libcef_dll/ctocpp/extension_ctocpp.h"
+#include "libcef_dll/ctocpp/navigation_entry_ctocpp.h"
+#include "libcef_dll/ctocpp/registration_ctocpp.h"
+#include "libcef_dll/ctocpp/request_context_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserHost::CreateBrowser(
+    const CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: client, url, extra_info, request_context
+
+  // Execute
+  int _retval = cef_browser_host_create_browser(
+      &windowInfo, CefClientCppToC::Wrap(client), url.GetStruct(), &settings,
+      CefDictionaryValueCToCpp::Unwrap(extra_info),
+      CefRequestContextCToCpp::Unwrap(request_context));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowser> CefBrowserHost::CreateBrowserSync(
+    const CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: client, url, extra_info, request_context
+
+  // Execute
+  cef_browser_t* _retval = cef_browser_host_create_browser_sync(
+      &windowInfo, CefClientCppToC::Wrap(client), url.GetStruct(), &settings,
+      CefDictionaryValueCToCpp::Unwrap(extra_info),
+      CefRequestContextCToCpp::Unwrap(request_context));
+
+  // Return type: refptr_same
+  return CefBrowserCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowser> CefBrowserHostCToCpp::GetBrowser() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_browser))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_t* _retval = _struct->get_browser(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::CloseBrowser(bool force_close) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close_browser))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->close_browser(_struct, force_close);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::TryCloseBrowser() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, try_close_browser))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->try_close_browser(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::SetFocus(bool focus) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focus(_struct, focus);
+}
+
+NO_SANITIZE("cfi-icall")
+CefWindowHandle CefBrowserHostCToCpp::GetWindowHandle() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_window_handle))
+    return kNullWindowHandle;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_handle_t _retval = _struct->get_window_handle(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefWindowHandle CefBrowserHostCToCpp::GetOpenerWindowHandle() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_opener_window_handle))
+    return kNullWindowHandle;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_handle_t _retval = _struct->get_opener_window_handle(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::HasView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_view(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefClient> CefBrowserHostCToCpp::GetClient() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_client))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_client_t* _retval = _struct->get_client(_struct);
+
+  // Return type: refptr_diff
+  return CefClientCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequestContext> CefBrowserHostCToCpp::GetRequestContext() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_request_context))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_request_context_t* _retval = _struct->get_request_context(_struct);
+
+  // Return type: refptr_same
+  return CefRequestContextCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") double CefBrowserHostCToCpp::GetZoomLevel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_zoom_level))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  double _retval = _struct->get_zoom_level(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SetZoomLevel(double zoomLevel) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_zoom_level))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_zoom_level(_struct, zoomLevel);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::RunFileDialog(
+    FileDialogMode mode,
+    const CefString& title,
+    const CefString& default_file_path,
+    const std::vector<CefString>& accept_filters,
+    int selected_accept_filter,
+    CefRefPtr<CefRunFileDialogCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, run_file_dialog))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+  // Unverified params: title, default_file_path, accept_filters
+
+  // Translate param: accept_filters; type: string_vec_byref_const
+  cef_string_list_t accept_filtersList = cef_string_list_alloc();
+  DCHECK(accept_filtersList);
+  if (accept_filtersList)
+    transfer_string_list_contents(accept_filters, accept_filtersList);
+
+  // Execute
+  _struct->run_file_dialog(_struct, mode, title.GetStruct(),
+                           default_file_path.GetStruct(), accept_filtersList,
+                           selected_accept_filter,
+                           CefRunFileDialogCallbackCppToC::Wrap(callback));
+
+  // Restore param:accept_filters; type: string_vec_byref_const
+  if (accept_filtersList)
+    cef_string_list_free(accept_filtersList);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::StartDownload(const CefString& url) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, start_download))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return;
+
+  // Execute
+  _struct->start_download(_struct, url.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::DownloadImage(
+    const CefString& image_url,
+    bool is_favicon,
+    uint32 max_image_size,
+    bool bypass_cache,
+    CefRefPtr<CefDownloadImageCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, download_image))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: image_url; type: string_byref_const
+  DCHECK(!image_url.empty());
+  if (image_url.empty())
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  _struct->download_image(_struct, image_url.GetStruct(), is_favicon,
+                          max_image_size, bypass_cache,
+                          CefDownloadImageCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::Print() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, print))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->print(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::PrintToPDF(const CefString& path,
+                                      const CefPdfPrintSettings& settings,
+                                      CefRefPtr<CefPdfPrintCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, print_to_pdf))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+  // Unverified params: callback
+
+  // Execute
+  _struct->print_to_pdf(_struct, path.GetStruct(), &settings,
+                        CefPdfPrintCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::Find(int identifier,
+                                const CefString& searchText,
+                                bool forward,
+                                bool matchCase,
+                                bool findNext) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, find))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: searchText; type: string_byref_const
+  DCHECK(!searchText.empty());
+  if (searchText.empty())
+    return;
+
+  // Execute
+  _struct->find(_struct, identifier, searchText.GetStruct(), forward, matchCase,
+                findNext);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::StopFinding(bool clearSelection) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, stop_finding))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->stop_finding(_struct, clearSelection);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::ShowDevTools(const CefWindowInfo& windowInfo,
+                                        CefRefPtr<CefClient> client,
+                                        const CefBrowserSettings& settings,
+                                        const CefPoint& inspect_element_at) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, show_dev_tools))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: windowInfo, client, settings, inspect_element_at
+
+  // Execute
+  _struct->show_dev_tools(_struct, &windowInfo, CefClientCppToC::Wrap(client),
+                          &settings, &inspect_element_at);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::CloseDevTools() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close_dev_tools))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->close_dev_tools(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::HasDevTools() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_dev_tools))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_dev_tools(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserHostCToCpp::SendDevToolsMessage(const void* message,
+                                               size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_dev_tools_message))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return false;
+
+  // Execute
+  int _retval = _struct->send_dev_tools_message(_struct, message, message_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefBrowserHostCToCpp::ExecuteDevToolsMethod(
+    int message_id,
+    const CefString& method,
+    CefRefPtr<CefDictionaryValue> params) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute_dev_tools_method))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: method; type: string_byref_const
+  DCHECK(!method.empty());
+  if (method.empty())
+    return 0;
+  // Unverified params: params
+
+  // Execute
+  int _retval = _struct->execute_dev_tools_method(
+      _struct, message_id, method.GetStruct(),
+      CefDictionaryValueCToCpp::Unwrap(params));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRegistration> CefBrowserHostCToCpp::AddDevToolsMessageObserver(
+    CefRefPtr<CefDevToolsMessageObserver> observer) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_dev_tools_message_observer))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: observer; type: refptr_diff
+  DCHECK(observer.get());
+  if (!observer.get())
+    return nullptr;
+
+  // Execute
+  cef_registration_t* _retval = _struct->add_dev_tools_message_observer(
+      _struct, CefDevToolsMessageObserverCppToC::Wrap(observer));
+
+  // Return type: refptr_same
+  return CefRegistrationCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::GetNavigationEntries(
+    CefRefPtr<CefNavigationEntryVisitor> visitor,
+    bool current_only) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_navigation_entries))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return;
+
+  // Execute
+  _struct->get_navigation_entries(
+      _struct, CefNavigationEntryVisitorCppToC::Wrap(visitor), current_only);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SetMouseCursorChangeDisabled(bool disabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_mouse_cursor_change_disabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_mouse_cursor_change_disabled(_struct, disabled);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserHostCToCpp::IsMouseCursorChangeDisabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_mouse_cursor_change_disabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_mouse_cursor_change_disabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::ReplaceMisspelling(const CefString& word) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, replace_misspelling))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: word; type: string_byref_const
+  DCHECK(!word.empty());
+  if (word.empty())
+    return;
+
+  // Execute
+  _struct->replace_misspelling(_struct, word.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::AddWordToDictionary(const CefString& word) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_word_to_dictionary))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: word; type: string_byref_const
+  DCHECK(!word.empty());
+  if (word.empty())
+    return;
+
+  // Execute
+  _struct->add_word_to_dictionary(_struct, word.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserHostCToCpp::IsWindowRenderingDisabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_window_rendering_disabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_window_rendering_disabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::WasResized() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, was_resized))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->was_resized(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::WasHidden(bool hidden) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, was_hidden))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->was_hidden(_struct, hidden);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::NotifyScreenInfoChanged() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, notify_screen_info_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->notify_screen_info_changed(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::Invalidate(PaintElementType type) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, invalidate))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate(_struct, type);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::SendExternalBeginFrame() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_external_begin_frame))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_external_begin_frame(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SendKeyEvent(const CefKeyEvent& event) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_key_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_key_event(_struct, &event);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SendMouseClickEvent(const CefMouseEvent& event,
+                                               MouseButtonType type,
+                                               bool mouseUp,
+                                               int clickCount) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_mouse_click_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_mouse_click_event(_struct, &event, type, mouseUp, clickCount);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SendMouseMoveEvent(const CefMouseEvent& event,
+                                              bool mouseLeave) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_mouse_move_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_mouse_move_event(_struct, &event, mouseLeave);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SendMouseWheelEvent(const CefMouseEvent& event,
+                                               int deltaX,
+                                               int deltaY) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_mouse_wheel_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_mouse_wheel_event(_struct, &event, deltaX, deltaY);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SendTouchEvent(const CefTouchEvent& event) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_touch_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_touch_event(_struct, &event);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SendFocusEvent(bool setFocus) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_focus_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_focus_event(_struct, setFocus);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::SendCaptureLostEvent() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_capture_lost_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_capture_lost_event(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::NotifyMoveOrResizeStarted() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, notify_move_or_resize_started))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->notify_move_or_resize_started(_struct);
+}
+
+NO_SANITIZE("cfi-icall") int CefBrowserHostCToCpp::GetWindowlessFrameRate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_windowless_frame_rate))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_windowless_frame_rate(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SetWindowlessFrameRate(int frame_rate) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_windowless_frame_rate))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_windowless_frame_rate(_struct, frame_rate);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::ImeSetComposition(
+    const CefString& text,
+    const std::vector<CefCompositionUnderline>& underlines,
+    const CefRange& replacement_range,
+    const CefRange& selection_range) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, ime_set_composition))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: text, underlines
+
+  // Translate param: underlines; type: simple_vec_byref_const
+  const size_t underlinesCount = underlines.size();
+  cef_composition_underline_t* underlinesList = NULL;
+  if (underlinesCount > 0) {
+    underlinesList = new cef_composition_underline_t[underlinesCount];
+    DCHECK(underlinesList);
+    if (underlinesList) {
+      for (size_t i = 0; i < underlinesCount; ++i) {
+        underlinesList[i] = underlines[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->ime_set_composition(_struct, text.GetStruct(), underlinesCount,
+                               underlinesList, &replacement_range,
+                               &selection_range);
+
+  // Restore param:underlines; type: simple_vec_byref_const
+  if (underlinesList)
+    delete[] underlinesList;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::ImeCommitText(const CefString& text,
+                                         const CefRange& replacement_range,
+                                         int relative_cursor_pos) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, ime_commit_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: text
+
+  // Execute
+  _struct->ime_commit_text(_struct, text.GetStruct(), &replacement_range,
+                           relative_cursor_pos);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::ImeFinishComposingText(bool keep_selection) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, ime_finish_composing_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->ime_finish_composing_text(_struct, keep_selection);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::ImeCancelComposition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, ime_cancel_composition))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->ime_cancel_composition(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
+                                               const CefMouseEvent& event,
+                                               DragOperationsMask allowed_ops) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, drag_target_drag_enter))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: drag_data; type: refptr_same
+  DCHECK(drag_data.get());
+  if (!drag_data.get())
+    return;
+
+  // Execute
+  _struct->drag_target_drag_enter(_struct, CefDragDataCToCpp::Unwrap(drag_data),
+                                  &event, allowed_ops);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::DragTargetDragOver(const CefMouseEvent& event,
+                                              DragOperationsMask allowed_ops) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, drag_target_drag_over))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->drag_target_drag_over(_struct, &event, allowed_ops);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::DragTargetDragLeave() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, drag_target_drag_leave))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->drag_target_drag_leave(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::DragTargetDrop(const CefMouseEvent& event) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, drag_target_drop))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->drag_target_drop(_struct, &event);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::DragSourceEndedAt(int x,
+                                             int y,
+                                             DragOperationsMask op) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, drag_source_ended_at))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->drag_source_ended_at(_struct, x, y, op);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::DragSourceSystemDragEnded() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, drag_source_system_drag_ended))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->drag_source_system_drag_ended(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefNavigationEntry>
+CefBrowserHostCToCpp::GetVisibleNavigationEntry() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_visible_navigation_entry))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_navigation_entry_t* _retval =
+      _struct->get_visible_navigation_entry(_struct);
+
+  // Return type: refptr_same
+  return CefNavigationEntryCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SetAccessibilityState(
+    cef_state_t accessibility_state) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_accessibility_state))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_accessibility_state(_struct, accessibility_state);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserHostCToCpp::SetAutoResizeEnabled(bool enabled,
+                                                const CefSize& min_size,
+                                                const CefSize& max_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_auto_resize_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_auto_resize_enabled(_struct, enabled, &min_size, &max_size);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefExtension> CefBrowserHostCToCpp::GetExtension() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_extension))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_extension_t* _retval = _struct->get_extension(_struct);
+
+  // Return type: refptr_same
+  return CefExtensionCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::IsBackgroundHost() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_background_host))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_background_host(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::SetAudioMuted(bool mute) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_audio_muted))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_audio_muted(_struct, mute);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::IsAudioMuted() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_host_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_audio_muted))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_audio_muted(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserHostCToCpp::CefBrowserHostCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserHostCToCpp::~CefBrowserHostCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_browser_host_t*
+CefCToCppRefCounted<CefBrowserHostCToCpp, CefBrowserHost, cef_browser_host_t>::
+    UnwrapDerived(CefWrapperType type, CefBrowserHost* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefBrowserHostCToCpp,
+                                   CefBrowserHost,
+                                   cef_browser_host_t>::kWrapperType =
+    WT_BROWSER_HOST;
diff --git a/src/libcef_dll/ctocpp/browser_host_ctocpp.h b/src/libcef_dll/ctocpp/browser_host_ctocpp.h
new file mode 100644
index 0000000..4987e5a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/browser_host_ctocpp.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c9abd1293472afbac964aac4cd7dd4cac9dd8e58$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBrowserHostCToCpp : public CefCToCppRefCounted<CefBrowserHostCToCpp,
+                                                        CefBrowserHost,
+                                                        cef_browser_host_t> {
+ public:
+  CefBrowserHostCToCpp();
+  virtual ~CefBrowserHostCToCpp();
+
+  // CefBrowserHost methods.
+  CefRefPtr<CefBrowser> GetBrowser() OVERRIDE;
+  void CloseBrowser(bool force_close) OVERRIDE;
+  bool TryCloseBrowser() OVERRIDE;
+  void SetFocus(bool focus) OVERRIDE;
+  CefWindowHandle GetWindowHandle() OVERRIDE;
+  CefWindowHandle GetOpenerWindowHandle() OVERRIDE;
+  bool HasView() OVERRIDE;
+  CefRefPtr<CefClient> GetClient() OVERRIDE;
+  CefRefPtr<CefRequestContext> GetRequestContext() OVERRIDE;
+  double GetZoomLevel() OVERRIDE;
+  void SetZoomLevel(double zoomLevel) OVERRIDE;
+  void RunFileDialog(FileDialogMode mode,
+                     const CefString& title,
+                     const CefString& default_file_path,
+                     const std::vector<CefString>& accept_filters,
+                     int selected_accept_filter,
+                     CefRefPtr<CefRunFileDialogCallback> callback) OVERRIDE;
+  void StartDownload(const CefString& url) OVERRIDE;
+  void DownloadImage(const CefString& image_url,
+                     bool is_favicon,
+                     uint32 max_image_size,
+                     bool bypass_cache,
+                     CefRefPtr<CefDownloadImageCallback> callback) OVERRIDE;
+  void Print() OVERRIDE;
+  void PrintToPDF(const CefString& path,
+                  const CefPdfPrintSettings& settings,
+                  CefRefPtr<CefPdfPrintCallback> callback) OVERRIDE;
+  void Find(int identifier,
+            const CefString& searchText,
+            bool forward,
+            bool matchCase,
+            bool findNext) OVERRIDE;
+  void StopFinding(bool clearSelection) OVERRIDE;
+  void ShowDevTools(const CefWindowInfo& windowInfo,
+                    CefRefPtr<CefClient> client,
+                    const CefBrowserSettings& settings,
+                    const CefPoint& inspect_element_at) OVERRIDE;
+  void CloseDevTools() OVERRIDE;
+  bool HasDevTools() OVERRIDE;
+  bool SendDevToolsMessage(const void* message, size_t message_size) OVERRIDE;
+  int ExecuteDevToolsMethod(int message_id,
+                            const CefString& method,
+                            CefRefPtr<CefDictionaryValue> params) OVERRIDE;
+  CefRefPtr<CefRegistration> AddDevToolsMessageObserver(
+      CefRefPtr<CefDevToolsMessageObserver> observer) OVERRIDE;
+  void GetNavigationEntries(CefRefPtr<CefNavigationEntryVisitor> visitor,
+                            bool current_only) OVERRIDE;
+  void SetMouseCursorChangeDisabled(bool disabled) OVERRIDE;
+  bool IsMouseCursorChangeDisabled() OVERRIDE;
+  void ReplaceMisspelling(const CefString& word) OVERRIDE;
+  void AddWordToDictionary(const CefString& word) OVERRIDE;
+  bool IsWindowRenderingDisabled() OVERRIDE;
+  void WasResized() OVERRIDE;
+  void WasHidden(bool hidden) OVERRIDE;
+  void NotifyScreenInfoChanged() OVERRIDE;
+  void Invalidate(PaintElementType type) OVERRIDE;
+  void SendExternalBeginFrame() OVERRIDE;
+  void SendKeyEvent(const CefKeyEvent& event) OVERRIDE;
+  void SendMouseClickEvent(const CefMouseEvent& event,
+                           MouseButtonType type,
+                           bool mouseUp,
+                           int clickCount) OVERRIDE;
+  void SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) OVERRIDE;
+  void SendMouseWheelEvent(const CefMouseEvent& event,
+                           int deltaX,
+                           int deltaY) OVERRIDE;
+  void SendTouchEvent(const CefTouchEvent& event) OVERRIDE;
+  void SendFocusEvent(bool setFocus) OVERRIDE;
+  void SendCaptureLostEvent() OVERRIDE;
+  void NotifyMoveOrResizeStarted() OVERRIDE;
+  int GetWindowlessFrameRate() OVERRIDE;
+  void SetWindowlessFrameRate(int frame_rate) OVERRIDE;
+  void ImeSetComposition(const CefString& text,
+                         const std::vector<CefCompositionUnderline>& underlines,
+                         const CefRange& replacement_range,
+                         const CefRange& selection_range) OVERRIDE;
+  void ImeCommitText(const CefString& text,
+                     const CefRange& replacement_range,
+                     int relative_cursor_pos) OVERRIDE;
+  void ImeFinishComposingText(bool keep_selection) OVERRIDE;
+  void ImeCancelComposition() OVERRIDE;
+  void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
+                           const CefMouseEvent& event,
+                           DragOperationsMask allowed_ops) OVERRIDE;
+  void DragTargetDragOver(const CefMouseEvent& event,
+                          DragOperationsMask allowed_ops) OVERRIDE;
+  void DragTargetDragLeave() OVERRIDE;
+  void DragTargetDrop(const CefMouseEvent& event) OVERRIDE;
+  void DragSourceEndedAt(int x, int y, DragOperationsMask op) OVERRIDE;
+  void DragSourceSystemDragEnded() OVERRIDE;
+  CefRefPtr<CefNavigationEntry> GetVisibleNavigationEntry() OVERRIDE;
+  void SetAccessibilityState(cef_state_t accessibility_state) OVERRIDE;
+  void SetAutoResizeEnabled(bool enabled,
+                            const CefSize& min_size,
+                            const CefSize& max_size) OVERRIDE;
+  CefRefPtr<CefExtension> GetExtension() OVERRIDE;
+  bool IsBackgroundHost() OVERRIDE;
+  void SetAudioMuted(bool mute) OVERRIDE;
+  bool IsAudioMuted() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/browser_process_handler_ctocpp.cc b/src/libcef_dll/ctocpp/browser_process_handler_ctocpp.cc
new file mode 100644
index 0000000..e69b60c
--- /dev/null
+++ b/src/libcef_dll/ctocpp/browser_process_handler_ctocpp.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=622d56aec0a5d6485a586bd6d993df7af4fa2d62$
+//
+
+#include "libcef_dll/ctocpp/browser_process_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/command_line_cpptoc.h"
+#include "libcef_dll/cpptoc/list_value_cpptoc.h"
+#include "libcef_dll/ctocpp/print_handler_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserProcessHandlerCToCpp::OnContextInitialized() {
+  cef_browser_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_context_initialized))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_context_initialized(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserProcessHandlerCToCpp::OnBeforeChildProcessLaunch(
+    CefRefPtr<CefCommandLine> command_line) {
+  cef_browser_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_child_process_launch))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: command_line; type: refptr_diff
+  DCHECK(command_line.get());
+  if (!command_line.get())
+    return;
+
+  // Execute
+  _struct->on_before_child_process_launch(
+      _struct, CefCommandLineCppToC::Wrap(command_line));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserProcessHandlerCToCpp::OnRenderProcessThreadCreated(
+    CefRefPtr<CefListValue> extra_info) {
+  cef_browser_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_render_process_thread_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extra_info; type: refptr_diff
+  DCHECK(extra_info.get());
+  if (!extra_info.get())
+    return;
+
+  // Execute
+  _struct->on_render_process_thread_created(
+      _struct, CefListValueCppToC::Wrap(extra_info));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefPrintHandler> CefBrowserProcessHandlerCToCpp::GetPrintHandler() {
+  cef_browser_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_print_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_print_handler_t* _retval = _struct->get_print_handler(_struct);
+
+  // Return type: refptr_same
+  return CefPrintHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserProcessHandlerCToCpp::OnScheduleMessagePumpWork(int64 delay_ms) {
+  cef_browser_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_schedule_message_pump_work))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_schedule_message_pump_work(_struct, delay_ms);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserProcessHandlerCToCpp::CefBrowserProcessHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserProcessHandlerCToCpp::~CefBrowserProcessHandlerCToCpp() {}
+
+template <>
+cef_browser_process_handler_t* CefCToCppRefCounted<
+    CefBrowserProcessHandlerCToCpp,
+    CefBrowserProcessHandler,
+    cef_browser_process_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                  CefBrowserProcessHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefBrowserProcessHandlerCToCpp,
+                        CefBrowserProcessHandler,
+                        cef_browser_process_handler_t>::kWrapperType =
+        WT_BROWSER_PROCESS_HANDLER;
diff --git a/src/libcef_dll/ctocpp/browser_process_handler_ctocpp.h b/src/libcef_dll/ctocpp/browser_process_handler_ctocpp.h
new file mode 100644
index 0000000..344ce82
--- /dev/null
+++ b/src/libcef_dll/ctocpp/browser_process_handler_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d07e79689f98b41fd7a0fb8c45dcae12a945cfe4$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_PROCESS_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_BROWSER_PROCESS_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_process_handler_capi.h"
+#include "include/cef_browser_process_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefBrowserProcessHandlerCToCpp
+    : public CefCToCppRefCounted<CefBrowserProcessHandlerCToCpp,
+                                 CefBrowserProcessHandler,
+                                 cef_browser_process_handler_t> {
+ public:
+  CefBrowserProcessHandlerCToCpp();
+  virtual ~CefBrowserProcessHandlerCToCpp();
+
+  // CefBrowserProcessHandler methods.
+  void OnContextInitialized() override;
+  void OnBeforeChildProcessLaunch(
+      CefRefPtr<CefCommandLine> command_line) override;
+  void OnRenderProcessThreadCreated(
+      CefRefPtr<CefListValue> extra_info) override;
+  CefRefPtr<CefPrintHandler> GetPrintHandler() override;
+  void OnScheduleMessagePumpWork(int64 delay_ms) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_BROWSER_PROCESS_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/callback_ctocpp.cc b/src/libcef_dll/ctocpp/callback_ctocpp.cc
new file mode 100644
index 0000000..3a19009
--- /dev/null
+++ b/src/libcef_dll/ctocpp/callback_ctocpp.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6d697bd19634711e28262d9df97887fd1abf47d6$
+//
+
+#include "libcef_dll/ctocpp/callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefCallbackCToCpp::Continue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cont(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCallbackCToCpp::CefCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCallbackCToCpp::~CefCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_callback_t*
+CefCToCppRefCounted<CefCallbackCToCpp, CefCallback, cef_callback_t>::
+    UnwrapDerived(CefWrapperType type, CefCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefCallbackCToCpp,
+                                   CefCallback,
+                                   cef_callback_t>::kWrapperType = WT_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/callback_ctocpp.h b/src/libcef_dll/ctocpp/callback_ctocpp.h
new file mode 100644
index 0000000..0f72d08
--- /dev/null
+++ b/src/libcef_dll/ctocpp/callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ff9e0075f199c0dc641059bd34b4366657de3125$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_callback_capi.h"
+#include "include/cef_callback.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefCallbackCToCpp : public CefCToCppRefCounted<CefCallbackCToCpp,
+                                                     CefCallback,
+                                                     cef_callback_t> {
+ public:
+  CefCallbackCToCpp();
+  virtual ~CefCallbackCToCpp();
+
+  // CefCallback methods.
+  void Continue() OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/client_ctocpp.cc b/src/libcef_dll/ctocpp/client_ctocpp.cc
new file mode 100644
index 0000000..3176ebd
--- /dev/null
+++ b/src/libcef_dll/ctocpp/client_ctocpp.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0e4556cf21b4d75aefbfa90963c6b5c9aba33bad$
+//
+
+#include "libcef_dll/ctocpp/client_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/process_message_cpptoc.h"
+#include "libcef_dll/ctocpp/audio_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/context_menu_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/dialog_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/display_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/download_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/drag_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/find_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/focus_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/jsdialog_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/keyboard_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/life_span_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/load_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/render_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/request_handler_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefAudioHandler> CefClientCToCpp::GetAudioHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_audio_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_audio_handler_t* _retval = _struct->get_audio_handler(_struct);
+
+  // Return type: refptr_same
+  return CefAudioHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefContextMenuHandler> CefClientCToCpp::GetContextMenuHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_context_menu_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_context_menu_handler_t* _retval =
+      _struct->get_context_menu_handler(_struct);
+
+  // Return type: refptr_same
+  return CefContextMenuHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDialogHandler> CefClientCToCpp::GetDialogHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_dialog_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dialog_handler_t* _retval = _struct->get_dialog_handler(_struct);
+
+  // Return type: refptr_same
+  return CefDialogHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDisplayHandler> CefClientCToCpp::GetDisplayHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_display_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_display_handler_t* _retval = _struct->get_display_handler(_struct);
+
+  // Return type: refptr_same
+  return CefDisplayHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDownloadHandler> CefClientCToCpp::GetDownloadHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_download_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_download_handler_t* _retval = _struct->get_download_handler(_struct);
+
+  // Return type: refptr_same
+  return CefDownloadHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDragHandler> CefClientCToCpp::GetDragHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_drag_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_drag_handler_t* _retval = _struct->get_drag_handler(_struct);
+
+  // Return type: refptr_same
+  return CefDragHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFindHandler> CefClientCToCpp::GetFindHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_find_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_find_handler_t* _retval = _struct->get_find_handler(_struct);
+
+  // Return type: refptr_same
+  return CefFindHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFocusHandler> CefClientCToCpp::GetFocusHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_focus_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_focus_handler_t* _retval = _struct->get_focus_handler(_struct);
+
+  // Return type: refptr_same
+  return CefFocusHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefJSDialogHandler> CefClientCToCpp::GetJSDialogHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_jsdialog_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_jsdialog_handler_t* _retval = _struct->get_jsdialog_handler(_struct);
+
+  // Return type: refptr_same
+  return CefJSDialogHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefKeyboardHandler> CefClientCToCpp::GetKeyboardHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_keyboard_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_keyboard_handler_t* _retval = _struct->get_keyboard_handler(_struct);
+
+  // Return type: refptr_same
+  return CefKeyboardHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLifeSpanHandler> CefClientCToCpp::GetLifeSpanHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_life_span_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_life_span_handler_t* _retval = _struct->get_life_span_handler(_struct);
+
+  // Return type: refptr_same
+  return CefLifeSpanHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLoadHandler> CefClientCToCpp::GetLoadHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_load_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_load_handler_t* _retval = _struct->get_load_handler(_struct);
+
+  // Return type: refptr_same
+  return CefLoadHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRenderHandler> CefClientCToCpp::GetRenderHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_render_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_render_handler_t* _retval = _struct->get_render_handler(_struct);
+
+  // Return type: refptr_same
+  return CefRenderHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequestHandler> CefClientCToCpp::GetRequestHandler() {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_request_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_request_handler_t* _retval = _struct->get_request_handler(_struct);
+
+  // Return type: refptr_same
+  return CefRequestHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefClientCToCpp::OnProcessMessageReceived(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefProcessId source_process,
+    CefRefPtr<CefProcessMessage> message) {
+  cef_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_process_message_received))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return false;
+  // Verify param: message; type: refptr_diff
+  DCHECK(message.get());
+  if (!message.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_process_message_received(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      source_process, CefProcessMessageCppToC::Wrap(message));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefClientCToCpp::CefClientCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefClientCToCpp::~CefClientCToCpp() {}
+
+template <>
+cef_client_t*
+CefCToCppRefCounted<CefClientCToCpp, CefClient, cef_client_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefClient* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefClientCToCpp, CefClient, cef_client_t>::
+    kWrapperType = WT_CLIENT;
diff --git a/src/libcef_dll/ctocpp/client_ctocpp.h b/src/libcef_dll/ctocpp/client_ctocpp.h
new file mode 100644
index 0000000..d42e9f2
--- /dev/null
+++ b/src/libcef_dll/ctocpp/client_ctocpp.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f751ee624cc3b8570cba8caa051c51bb7aeccaa7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_CLIENT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_CLIENT_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_client.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefClientCToCpp
+    : public CefCToCppRefCounted<CefClientCToCpp, CefClient, cef_client_t> {
+ public:
+  CefClientCToCpp();
+  virtual ~CefClientCToCpp();
+
+  // CefClient methods.
+  CefRefPtr<CefAudioHandler> GetAudioHandler() override;
+  CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override;
+  CefRefPtr<CefDialogHandler> GetDialogHandler() override;
+  CefRefPtr<CefDisplayHandler> GetDisplayHandler() override;
+  CefRefPtr<CefDownloadHandler> GetDownloadHandler() override;
+  CefRefPtr<CefDragHandler> GetDragHandler() override;
+  CefRefPtr<CefFindHandler> GetFindHandler() override;
+  CefRefPtr<CefFocusHandler> GetFocusHandler() override;
+  CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() override;
+  CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() override;
+  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;
+  CefRefPtr<CefLoadHandler> GetLoadHandler() override;
+  CefRefPtr<CefRenderHandler> GetRenderHandler() override;
+  CefRefPtr<CefRequestHandler> GetRequestHandler() override;
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_CLIENT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/command_line_ctocpp.cc b/src/libcef_dll/ctocpp/command_line_ctocpp.cc
new file mode 100644
index 0000000..9ecbc2e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/command_line_ctocpp.cc
@@ -0,0 +1,437 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7d5995f534719bf6466eb174f50027cc57db98ef$
+//
+
+#include "libcef_dll/ctocpp/command_line_ctocpp.h"
+#include "include/cef_api_hash.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefCommandLine> CefCommandLine::CreateCommandLine() {
+  const char* api_hash = cef_api_hash(0);
+  if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) {
+    // The libcef API hash does not match the current header API hash.
+    NOTREACHED();
+    return nullptr;
+  }
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_command_line_t* _retval = cef_command_line_create();
+
+  // Return type: refptr_same
+  return CefCommandLineCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefCommandLine> CefCommandLine::GetGlobalCommandLine() {
+  const char* api_hash = cef_api_hash(0);
+  if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) {
+    // The libcef API hash does not match the current header API hash.
+    NOTREACHED();
+    return nullptr;
+  }
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_command_line_t* _retval = cef_command_line_get_global();
+
+  // Return type: refptr_same
+  return CefCommandLineCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefCommandLineCToCpp::IsValid() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefCommandLineCToCpp::IsReadOnly() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefCommandLine> CefCommandLineCToCpp::Copy() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, copy))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_command_line_t* _retval = _struct->copy(_struct);
+
+  // Return type: refptr_same
+  return CefCommandLineCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::InitFromArgv(int argc, const char* const* argv) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, init_from_argv))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: argv; type: simple_byaddr
+  DCHECK(argv);
+  if (!argv)
+    return;
+
+  // Execute
+  _struct->init_from_argv(_struct, argc, argv);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::InitFromString(const CefString& command_line) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, init_from_string))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: command_line; type: string_byref_const
+  DCHECK(!command_line.empty());
+  if (command_line.empty())
+    return;
+
+  // Execute
+  _struct->init_from_string(_struct, command_line.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") void CefCommandLineCToCpp::Reset() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, reset))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->reset(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::GetArgv(std::vector<CefString>& argv) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_argv))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: argv; type: string_vec_byref
+  cef_string_list_t argvList = cef_string_list_alloc();
+  DCHECK(argvList);
+  if (argvList)
+    transfer_string_list_contents(argv, argvList);
+
+  // Execute
+  _struct->get_argv(_struct, argvList);
+
+  // Restore param:argv; type: string_vec_byref
+  if (argvList) {
+    argv.clear();
+    transfer_string_list_contents(argvList, argv);
+    cef_string_list_free(argvList);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefCommandLineCToCpp::GetCommandLineString() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_command_line_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_command_line_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefCommandLineCToCpp::GetProgram() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_program))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_program(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::SetProgram(const CefString& program) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_program))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: program; type: string_byref_const
+  DCHECK(!program.empty());
+  if (program.empty())
+    return;
+
+  // Execute
+  _struct->set_program(_struct, program.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") bool CefCommandLineCToCpp::HasSwitches() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_switches))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_switches(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefCommandLineCToCpp::HasSwitch(const CefString& name) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_switch))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->has_switch(_struct, name.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefCommandLineCToCpp::GetSwitchValue(const CefString& name) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_switch_value))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_switch_value(_struct, name.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::GetSwitches(SwitchMap& switches) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_switches))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: switches; type: string_map_single_byref
+  cef_string_map_t switchesMap = cef_string_map_alloc();
+  DCHECK(switchesMap);
+  if (switchesMap)
+    transfer_string_map_contents(switches, switchesMap);
+
+  // Execute
+  _struct->get_switches(_struct, switchesMap);
+
+  // Restore param:switches; type: string_map_single_byref
+  if (switchesMap) {
+    switches.clear();
+    transfer_string_map_contents(switchesMap, switches);
+    cef_string_map_free(switchesMap);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::AppendSwitch(const CefString& name) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, append_switch))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+
+  // Execute
+  _struct->append_switch(_struct, name.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::AppendSwitchWithValue(const CefString& name,
+                                                 const CefString& value) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, append_switch_with_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+  // Verify param: value; type: string_byref_const
+  DCHECK(!value.empty());
+  if (value.empty())
+    return;
+
+  // Execute
+  _struct->append_switch_with_value(_struct, name.GetStruct(),
+                                    value.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") bool CefCommandLineCToCpp::HasArguments() {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_arguments))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_arguments(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::GetArguments(ArgumentList& arguments) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_arguments))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: arguments; type: string_vec_byref
+  cef_string_list_t argumentsList = cef_string_list_alloc();
+  DCHECK(argumentsList);
+  if (argumentsList)
+    transfer_string_list_contents(arguments, argumentsList);
+
+  // Execute
+  _struct->get_arguments(_struct, argumentsList);
+
+  // Restore param:arguments; type: string_vec_byref
+  if (argumentsList) {
+    arguments.clear();
+    transfer_string_list_contents(argumentsList, arguments);
+    cef_string_list_free(argumentsList);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::AppendArgument(const CefString& argument) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, append_argument))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: argument; type: string_byref_const
+  DCHECK(!argument.empty());
+  if (argument.empty())
+    return;
+
+  // Execute
+  _struct->append_argument(_struct, argument.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefCommandLineCToCpp::PrependWrapper(const CefString& wrapper) {
+  cef_command_line_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, prepend_wrapper))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: wrapper; type: string_byref_const
+  DCHECK(!wrapper.empty());
+  if (wrapper.empty())
+    return;
+
+  // Execute
+  _struct->prepend_wrapper(_struct, wrapper.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCommandLineCToCpp::CefCommandLineCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCommandLineCToCpp::~CefCommandLineCToCpp() {}
+
+template <>
+cef_command_line_t*
+CefCToCppRefCounted<CefCommandLineCToCpp, CefCommandLine, cef_command_line_t>::
+    UnwrapDerived(CefWrapperType type, CefCommandLine* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefCommandLineCToCpp,
+                                   CefCommandLine,
+                                   cef_command_line_t>::kWrapperType =
+    WT_COMMAND_LINE;
diff --git a/src/libcef_dll/ctocpp/command_line_ctocpp.h b/src/libcef_dll/ctocpp/command_line_ctocpp.h
new file mode 100644
index 0000000..080eaf2
--- /dev/null
+++ b/src/libcef_dll/ctocpp/command_line_ctocpp.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=de597fbd256308a25adcd6ac0d27b1293ce6a088$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_COMMAND_LINE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_COMMAND_LINE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_command_line_capi.h"
+#include "include/cef_command_line.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefCommandLineCToCpp : public CefCToCppRefCounted<CefCommandLineCToCpp,
+                                                        CefCommandLine,
+                                                        cef_command_line_t> {
+ public:
+  CefCommandLineCToCpp();
+  virtual ~CefCommandLineCToCpp();
+
+  // CefCommandLine methods.
+  bool IsValid() OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  CefRefPtr<CefCommandLine> Copy() OVERRIDE;
+  void InitFromArgv(int argc, const char* const* argv) OVERRIDE;
+  void InitFromString(const CefString& command_line) OVERRIDE;
+  void Reset() OVERRIDE;
+  void GetArgv(std::vector<CefString>& argv) OVERRIDE;
+  CefString GetCommandLineString() OVERRIDE;
+  CefString GetProgram() OVERRIDE;
+  void SetProgram(const CefString& program) OVERRIDE;
+  bool HasSwitches() OVERRIDE;
+  bool HasSwitch(const CefString& name) OVERRIDE;
+  CefString GetSwitchValue(const CefString& name) OVERRIDE;
+  void GetSwitches(SwitchMap& switches) OVERRIDE;
+  void AppendSwitch(const CefString& name) OVERRIDE;
+  void AppendSwitchWithValue(const CefString& name,
+                             const CefString& value) OVERRIDE;
+  bool HasArguments() OVERRIDE;
+  void GetArguments(ArgumentList& arguments) OVERRIDE;
+  void AppendArgument(const CefString& argument) OVERRIDE;
+  void PrependWrapper(const CefString& wrapper) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_COMMAND_LINE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/completion_callback_ctocpp.cc b/src/libcef_dll/ctocpp/completion_callback_ctocpp.cc
new file mode 100644
index 0000000..4d9c8fa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/completion_callback_ctocpp.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2193f30ba657212718712739c7b48dd9b84b7bb8$
+//
+
+#include "libcef_dll/ctocpp/completion_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefCompletionCallbackCToCpp::OnComplete() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_completion_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_complete))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_complete(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCompletionCallbackCToCpp::CefCompletionCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCompletionCallbackCToCpp::~CefCompletionCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_completion_callback_t* CefCToCppRefCounted<
+    CefCompletionCallbackCToCpp,
+    CefCompletionCallback,
+    cef_completion_callback_t>::UnwrapDerived(CefWrapperType type,
+                                              CefCompletionCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefCompletionCallbackCToCpp,
+                                   CefCompletionCallback,
+                                   cef_completion_callback_t>::kWrapperType =
+    WT_COMPLETION_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/completion_callback_ctocpp.h b/src/libcef_dll/ctocpp/completion_callback_ctocpp.h
new file mode 100644
index 0000000..c15136d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/completion_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8c963d9ea85918f6942e95c24dce6769a7f881c5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_COMPLETION_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_COMPLETION_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_callback_capi.h"
+#include "include/cef_callback.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefCompletionCallbackCToCpp
+    : public CefCToCppRefCounted<CefCompletionCallbackCToCpp,
+                                 CefCompletionCallback,
+                                 cef_completion_callback_t> {
+ public:
+  CefCompletionCallbackCToCpp();
+  virtual ~CefCompletionCallbackCToCpp();
+
+  // CefCompletionCallback methods.
+  void OnComplete() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_COMPLETION_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/context_menu_handler_ctocpp.cc b/src/libcef_dll/ctocpp/context_menu_handler_ctocpp.cc
new file mode 100644
index 0000000..a4a0799
--- /dev/null
+++ b/src/libcef_dll/ctocpp/context_menu_handler_ctocpp.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d0eec19bee7cbc38c676ef6ca936483cc93bb7fc$
+//
+
+#include "libcef_dll/ctocpp/context_menu_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/context_menu_params_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/menu_model_cpptoc.h"
+#include "libcef_dll/cpptoc/run_context_menu_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefContextMenuHandlerCToCpp::OnBeforeContextMenu(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefContextMenuParams> params,
+    CefRefPtr<CefMenuModel> model) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_context_menu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+  // Verify param: params; type: refptr_diff
+  DCHECK(params.get());
+  if (!params.get())
+    return;
+  // Verify param: model; type: refptr_diff
+  DCHECK(model.get());
+  if (!model.get())
+    return;
+
+  // Execute
+  _struct->on_before_context_menu(_struct, CefBrowserCppToC::Wrap(browser),
+                                  CefFrameCppToC::Wrap(frame),
+                                  CefContextMenuParamsCppToC::Wrap(params),
+                                  CefMenuModelCppToC::Wrap(model));
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefContextMenuHandlerCToCpp::RunContextMenu(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefContextMenuParams> params,
+    CefRefPtr<CefMenuModel> model,
+    CefRefPtr<CefRunContextMenuCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, run_context_menu))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return false;
+  // Verify param: params; type: refptr_diff
+  DCHECK(params.get());
+  if (!params.get())
+    return false;
+  // Verify param: model; type: refptr_diff
+  DCHECK(model.get());
+  if (!model.get())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->run_context_menu(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefContextMenuParamsCppToC::Wrap(params), CefMenuModelCppToC::Wrap(model),
+      CefRunContextMenuCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefContextMenuHandlerCToCpp::OnContextMenuCommand(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefContextMenuParams> params,
+    int command_id,
+    EventFlags event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_context_menu_command))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return false;
+  // Verify param: params; type: refptr_diff
+  DCHECK(params.get());
+  if (!params.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_context_menu_command(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefContextMenuParamsCppToC::Wrap(params), command_id, event_flags);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefContextMenuHandlerCToCpp::OnContextMenuDismissed(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_context_menu_dismissed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+
+  // Execute
+  _struct->on_context_menu_dismissed(_struct, CefBrowserCppToC::Wrap(browser),
+                                     CefFrameCppToC::Wrap(frame));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefContextMenuHandlerCToCpp::CefContextMenuHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefContextMenuHandlerCToCpp::~CefContextMenuHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_context_menu_handler_t* CefCToCppRefCounted<
+    CefContextMenuHandlerCToCpp,
+    CefContextMenuHandler,
+    cef_context_menu_handler_t>::UnwrapDerived(CefWrapperType type,
+                                               CefContextMenuHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefContextMenuHandlerCToCpp,
+                                   CefContextMenuHandler,
+                                   cef_context_menu_handler_t>::kWrapperType =
+    WT_CONTEXT_MENU_HANDLER;
diff --git a/src/libcef_dll/ctocpp/context_menu_handler_ctocpp.h b/src/libcef_dll/ctocpp/context_menu_handler_ctocpp.h
new file mode 100644
index 0000000..6129ea7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/context_menu_handler_ctocpp.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=11042b06316a1448f4f597941d059fc12bf5d7ca$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_CONTEXT_MENU_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_CONTEXT_MENU_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_context_menu_handler_capi.h"
+#include "include/cef_context_menu_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefContextMenuHandlerCToCpp
+    : public CefCToCppRefCounted<CefContextMenuHandlerCToCpp,
+                                 CefContextMenuHandler,
+                                 cef_context_menu_handler_t> {
+ public:
+  CefContextMenuHandlerCToCpp();
+  virtual ~CefContextMenuHandlerCToCpp();
+
+  // CefContextMenuHandler methods.
+  void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefContextMenuParams> params,
+                           CefRefPtr<CefMenuModel> model) override;
+  bool RunContextMenu(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefContextMenuParams> params,
+                      CefRefPtr<CefMenuModel> model,
+                      CefRefPtr<CefRunContextMenuCallback> callback) override;
+  bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefContextMenuParams> params,
+                            int command_id,
+                            EventFlags event_flags) override;
+  void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_CONTEXT_MENU_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/context_menu_params_ctocpp.cc b/src/libcef_dll/ctocpp/context_menu_params_ctocpp.cc
new file mode 100644
index 0000000..407702a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/context_menu_params_ctocpp.cc
@@ -0,0 +1,427 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7a5343c03a9b84f8831be8b9c7e5d2f35c62983c$
+//
+
+#include "libcef_dll/ctocpp/context_menu_params_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") int CefContextMenuParamsCToCpp::GetXCoord() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_xcoord))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_xcoord(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefContextMenuParamsCToCpp::GetYCoord() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_ycoord))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_ycoord(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefContextMenuParams::TypeFlags CefContextMenuParamsCToCpp::GetTypeFlags() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type_flags))
+    return CM_TYPEFLAG_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_context_menu_type_flags_t _retval = _struct->get_type_flags(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefContextMenuParamsCToCpp::GetLinkUrl() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_link_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_link_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefContextMenuParamsCToCpp::GetUnfilteredLinkUrl() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_unfiltered_link_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_unfiltered_link_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefContextMenuParamsCToCpp::GetSourceUrl() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_source_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_source_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefContextMenuParamsCToCpp::HasImageContents() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_image_contents))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_image_contents(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefContextMenuParamsCToCpp::GetTitleText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_title_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_title_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefContextMenuParamsCToCpp::GetPageUrl() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_page_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_page_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefContextMenuParamsCToCpp::GetFrameUrl() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_frame_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefContextMenuParamsCToCpp::GetFrameCharset() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame_charset))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_frame_charset(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefContextMenuParams::MediaType CefContextMenuParamsCToCpp::GetMediaType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_media_type))
+    return CM_MEDIATYPE_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_context_menu_media_type_t _retval = _struct->get_media_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefContextMenuParams::MediaStateFlags
+CefContextMenuParamsCToCpp::GetMediaStateFlags() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_media_state_flags))
+    return CM_MEDIAFLAG_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_context_menu_media_state_flags_t _retval =
+      _struct->get_media_state_flags(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefContextMenuParamsCToCpp::GetSelectionText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selection_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_selection_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefContextMenuParamsCToCpp::GetMisspelledWord() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_misspelled_word))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_misspelled_word(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefContextMenuParamsCToCpp::GetDictionarySuggestions(
+    std::vector<CefString>& suggestions) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_dictionary_suggestions))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: suggestions; type: string_vec_byref
+  cef_string_list_t suggestionsList = cef_string_list_alloc();
+  DCHECK(suggestionsList);
+  if (suggestionsList)
+    transfer_string_list_contents(suggestions, suggestionsList);
+
+  // Execute
+  int _retval = _struct->get_dictionary_suggestions(_struct, suggestionsList);
+
+  // Restore param:suggestions; type: string_vec_byref
+  if (suggestionsList) {
+    suggestions.clear();
+    transfer_string_list_contents(suggestionsList, suggestions);
+    cef_string_list_free(suggestionsList);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefContextMenuParamsCToCpp::IsEditable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_editable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_editable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefContextMenuParamsCToCpp::IsSpellCheckEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_spell_check_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_spell_check_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefContextMenuParams::EditStateFlags
+CefContextMenuParamsCToCpp::GetEditStateFlags() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_edit_state_flags))
+    return CM_EDITFLAG_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_context_menu_edit_state_flags_t _retval =
+      _struct->get_edit_state_flags(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefContextMenuParamsCToCpp::IsCustomMenu() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_custom_menu))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_custom_menu(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefContextMenuParamsCToCpp::IsPepperMenu() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_context_menu_params_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_pepper_menu))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_pepper_menu(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefContextMenuParamsCToCpp::CefContextMenuParamsCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefContextMenuParamsCToCpp::~CefContextMenuParamsCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_context_menu_params_t* CefCToCppRefCounted<
+    CefContextMenuParamsCToCpp,
+    CefContextMenuParams,
+    cef_context_menu_params_t>::UnwrapDerived(CefWrapperType type,
+                                              CefContextMenuParams* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefContextMenuParamsCToCpp,
+                                   CefContextMenuParams,
+                                   cef_context_menu_params_t>::kWrapperType =
+    WT_CONTEXT_MENU_PARAMS;
diff --git a/src/libcef_dll/ctocpp/context_menu_params_ctocpp.h b/src/libcef_dll/ctocpp/context_menu_params_ctocpp.h
new file mode 100644
index 0000000..dca52b9
--- /dev/null
+++ b/src/libcef_dll/ctocpp/context_menu_params_ctocpp.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4f6f41aa30704aeb2d3dbb6b54d430ea3392bf29$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_CONTEXT_MENU_PARAMS_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_CONTEXT_MENU_PARAMS_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_context_menu_handler_capi.h"
+#include "include/cef_context_menu_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefContextMenuParamsCToCpp
+    : public CefCToCppRefCounted<CefContextMenuParamsCToCpp,
+                                 CefContextMenuParams,
+                                 cef_context_menu_params_t> {
+ public:
+  CefContextMenuParamsCToCpp();
+  virtual ~CefContextMenuParamsCToCpp();
+
+  // CefContextMenuParams methods.
+  int GetXCoord() OVERRIDE;
+  int GetYCoord() OVERRIDE;
+  TypeFlags GetTypeFlags() OVERRIDE;
+  CefString GetLinkUrl() OVERRIDE;
+  CefString GetUnfilteredLinkUrl() OVERRIDE;
+  CefString GetSourceUrl() OVERRIDE;
+  bool HasImageContents() OVERRIDE;
+  CefString GetTitleText() OVERRIDE;
+  CefString GetPageUrl() OVERRIDE;
+  CefString GetFrameUrl() OVERRIDE;
+  CefString GetFrameCharset() OVERRIDE;
+  MediaType GetMediaType() OVERRIDE;
+  MediaStateFlags GetMediaStateFlags() OVERRIDE;
+  CefString GetSelectionText() OVERRIDE;
+  CefString GetMisspelledWord() OVERRIDE;
+  bool GetDictionarySuggestions(std::vector<CefString>& suggestions) OVERRIDE;
+  bool IsEditable() OVERRIDE;
+  bool IsSpellCheckEnabled() OVERRIDE;
+  EditStateFlags GetEditStateFlags() OVERRIDE;
+  bool IsCustomMenu() OVERRIDE;
+  bool IsPepperMenu() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_CONTEXT_MENU_PARAMS_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/cookie_access_filter_ctocpp.cc b/src/libcef_dll/ctocpp/cookie_access_filter_ctocpp.cc
new file mode 100644
index 0000000..be1c709
--- /dev/null
+++ b/src/libcef_dll/ctocpp/cookie_access_filter_ctocpp.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=18ad9a46ee3ff980745b796013de06379659f611$
+//
+
+#include "libcef_dll/ctocpp/cookie_access_filter_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/response_cpptoc.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieAccessFilterCToCpp::CanSendCookie(CefRefPtr<CefBrowser> browser,
+                                                CefRefPtr<CefFrame> frame,
+                                                CefRefPtr<CefRequest> request,
+                                                const CefCookie& cookie) {
+  cef_cookie_access_filter_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_send_cookie))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return false;
+  // Unverified params: browser, frame
+
+  // Execute
+  int _retval = _struct->can_send_cookie(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), &cookie);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieAccessFilterCToCpp::CanSaveCookie(CefRefPtr<CefBrowser> browser,
+                                                CefRefPtr<CefFrame> frame,
+                                                CefRefPtr<CefRequest> request,
+                                                CefRefPtr<CefResponse> response,
+                                                const CefCookie& cookie) {
+  cef_cookie_access_filter_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_save_cookie))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return false;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response.get());
+  if (!response.get())
+    return false;
+  // Unverified params: browser, frame
+
+  // Execute
+  int _retval = _struct->can_save_cookie(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), CefResponseCppToC::Wrap(response),
+      &cookie);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCookieAccessFilterCToCpp::CefCookieAccessFilterCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCookieAccessFilterCToCpp::~CefCookieAccessFilterCToCpp() {}
+
+template <>
+cef_cookie_access_filter_t* CefCToCppRefCounted<
+    CefCookieAccessFilterCToCpp,
+    CefCookieAccessFilter,
+    cef_cookie_access_filter_t>::UnwrapDerived(CefWrapperType type,
+                                               CefCookieAccessFilter* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefCookieAccessFilterCToCpp,
+                                   CefCookieAccessFilter,
+                                   cef_cookie_access_filter_t>::kWrapperType =
+    WT_COOKIE_ACCESS_FILTER;
diff --git a/src/libcef_dll/ctocpp/cookie_access_filter_ctocpp.h b/src/libcef_dll/ctocpp/cookie_access_filter_ctocpp.h
new file mode 100644
index 0000000..3f815a2
--- /dev/null
+++ b/src/libcef_dll/ctocpp/cookie_access_filter_ctocpp.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=00521e32f472f47ba28a758e51d522f3f50f4db3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_COOKIE_ACCESS_FILTER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_COOKIE_ACCESS_FILTER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_resource_request_handler_capi.h"
+#include "include/cef_resource_request_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefCookieAccessFilterCToCpp
+    : public CefCToCppRefCounted<CefCookieAccessFilterCToCpp,
+                                 CefCookieAccessFilter,
+                                 cef_cookie_access_filter_t> {
+ public:
+  CefCookieAccessFilterCToCpp();
+  virtual ~CefCookieAccessFilterCToCpp();
+
+  // CefCookieAccessFilter methods.
+  bool CanSendCookie(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     CefRefPtr<CefRequest> request,
+                     const CefCookie& cookie) override;
+  bool CanSaveCookie(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     CefRefPtr<CefRequest> request,
+                     CefRefPtr<CefResponse> response,
+                     const CefCookie& cookie) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_COOKIE_ACCESS_FILTER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/cookie_manager_ctocpp.cc b/src/libcef_dll/ctocpp/cookie_manager_ctocpp.cc
new file mode 100644
index 0000000..1b7cd53
--- /dev/null
+++ b/src/libcef_dll/ctocpp/cookie_manager_ctocpp.cc
@@ -0,0 +1,208 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ce618780672b92c6a5f4468b24bdf6c15abe7a82$
+//
+
+#include "libcef_dll/ctocpp/cookie_manager_ctocpp.h"
+#include "libcef_dll/cpptoc/completion_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/cookie_visitor_cpptoc.h"
+#include "libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/set_cookie_callback_cpptoc.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefCookieManager> CefCookieManager::GetGlobalManager(
+    CefRefPtr<CefCompletionCallback> callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Execute
+  cef_cookie_manager_t* _retval = cef_cookie_manager_get_global_manager(
+      CefCompletionCallbackCppToC::Wrap(callback));
+
+  // Return type: refptr_same
+  return CefCookieManagerCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefCookieManagerCToCpp::SetSupportedSchemes(
+    const std::vector<CefString>& schemes,
+    bool include_defaults,
+    CefRefPtr<CefCompletionCallback> callback) {
+  cef_cookie_manager_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_supported_schemes))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Translate param: schemes; type: string_vec_byref_const
+  cef_string_list_t schemesList = cef_string_list_alloc();
+  DCHECK(schemesList);
+  if (schemesList)
+    transfer_string_list_contents(schemes, schemesList);
+
+  // Execute
+  _struct->set_supported_schemes(_struct, schemesList, include_defaults,
+                                 CefCompletionCallbackCppToC::Wrap(callback));
+
+  // Restore param:schemes; type: string_vec_byref_const
+  if (schemesList)
+    cef_string_list_free(schemesList);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieManagerCToCpp::VisitAllCookies(
+    CefRefPtr<CefCookieVisitor> visitor) {
+  cef_cookie_manager_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit_all_cookies))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->visit_all_cookies(
+      _struct, CefCookieVisitorCppToC::Wrap(visitor));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieManagerCToCpp::VisitUrlCookies(
+    const CefString& url,
+    bool includeHttpOnly,
+    CefRefPtr<CefCookieVisitor> visitor) {
+  cef_cookie_manager_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit_url_cookies))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return false;
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->visit_url_cookies(_struct, url.GetStruct(), includeHttpOnly,
+                                 CefCookieVisitorCppToC::Wrap(visitor));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieManagerCToCpp::SetCookie(
+    const CefString& url,
+    const CefCookie& cookie,
+    CefRefPtr<CefSetCookieCallback> callback) {
+  cef_cookie_manager_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_cookie))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return false;
+  // Unverified params: callback
+
+  // Execute
+  int _retval = _struct->set_cookie(_struct, url.GetStruct(), &cookie,
+                                    CefSetCookieCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieManagerCToCpp::DeleteCookies(
+    const CefString& url,
+    const CefString& cookie_name,
+    CefRefPtr<CefDeleteCookiesCallback> callback) {
+  cef_cookie_manager_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, delete_cookies))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: url, cookie_name, callback
+
+  // Execute
+  int _retval =
+      _struct->delete_cookies(_struct, url.GetStruct(), cookie_name.GetStruct(),
+                              CefDeleteCookiesCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieManagerCToCpp::FlushStore(
+    CefRefPtr<CefCompletionCallback> callback) {
+  cef_cookie_manager_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, flush_store))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Execute
+  int _retval = _struct->flush_store(
+      _struct, CefCompletionCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCookieManagerCToCpp::CefCookieManagerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCookieManagerCToCpp::~CefCookieManagerCToCpp() {}
+
+template <>
+cef_cookie_manager_t*
+CefCToCppRefCounted<CefCookieManagerCToCpp,
+                    CefCookieManager,
+                    cef_cookie_manager_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefCookieManager* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefCookieManagerCToCpp,
+                                   CefCookieManager,
+                                   cef_cookie_manager_t>::kWrapperType =
+    WT_COOKIE_MANAGER;
diff --git a/src/libcef_dll/ctocpp/cookie_manager_ctocpp.h b/src/libcef_dll/ctocpp/cookie_manager_ctocpp.h
new file mode 100644
index 0000000..cea787f
--- /dev/null
+++ b/src/libcef_dll/ctocpp/cookie_manager_ctocpp.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cc4a6a350ccd9d68869efae79a4d5717169e2ff8$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_COOKIE_MANAGER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_COOKIE_MANAGER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefCookieManagerCToCpp
+    : public CefCToCppRefCounted<CefCookieManagerCToCpp,
+                                 CefCookieManager,
+                                 cef_cookie_manager_t> {
+ public:
+  CefCookieManagerCToCpp();
+  virtual ~CefCookieManagerCToCpp();
+
+  // CefCookieManager methods.
+  void SetSupportedSchemes(const std::vector<CefString>& schemes,
+                           bool include_defaults,
+                           CefRefPtr<CefCompletionCallback> callback) OVERRIDE;
+  bool VisitAllCookies(CefRefPtr<CefCookieVisitor> visitor) OVERRIDE;
+  bool VisitUrlCookies(const CefString& url,
+                       bool includeHttpOnly,
+                       CefRefPtr<CefCookieVisitor> visitor) OVERRIDE;
+  bool SetCookie(const CefString& url,
+                 const CefCookie& cookie,
+                 CefRefPtr<CefSetCookieCallback> callback) OVERRIDE;
+  bool DeleteCookies(const CefString& url,
+                     const CefString& cookie_name,
+                     CefRefPtr<CefDeleteCookiesCallback> callback) OVERRIDE;
+  bool FlushStore(CefRefPtr<CefCompletionCallback> callback) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_COOKIE_MANAGER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/cookie_visitor_ctocpp.cc b/src/libcef_dll/ctocpp/cookie_visitor_ctocpp.cc
new file mode 100644
index 0000000..41bb066
--- /dev/null
+++ b/src/libcef_dll/ctocpp/cookie_visitor_ctocpp.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2fb5cbc3fb182cdc5548139ca7256916148984d7$
+//
+
+#include "libcef_dll/ctocpp/cookie_visitor_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefCookieVisitorCToCpp::Visit(const CefCookie& cookie,
+                                   int count,
+                                   int total,
+                                   bool& deleteCookie) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_cookie_visitor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: deleteCookie; type: bool_byref
+  int deleteCookieInt = deleteCookie;
+
+  // Execute
+  int _retval =
+      _struct->visit(_struct, &cookie, count, total, &deleteCookieInt);
+
+  // Restore param:deleteCookie; type: bool_byref
+  deleteCookie = deleteCookieInt ? true : false;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefCookieVisitorCToCpp::CefCookieVisitorCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefCookieVisitorCToCpp::~CefCookieVisitorCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_cookie_visitor_t*
+CefCToCppRefCounted<CefCookieVisitorCToCpp,
+                    CefCookieVisitor,
+                    cef_cookie_visitor_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefCookieVisitor* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefCookieVisitorCToCpp,
+                                   CefCookieVisitor,
+                                   cef_cookie_visitor_t>::kWrapperType =
+    WT_COOKIE_VISITOR;
diff --git a/src/libcef_dll/ctocpp/cookie_visitor_ctocpp.h b/src/libcef_dll/ctocpp/cookie_visitor_ctocpp.h
new file mode 100644
index 0000000..d49a42a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/cookie_visitor_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6eb302aca9a6178b9418310105104f079830f4a0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_COOKIE_VISITOR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_COOKIE_VISITOR_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefCookieVisitorCToCpp
+    : public CefCToCppRefCounted<CefCookieVisitorCToCpp,
+                                 CefCookieVisitor,
+                                 cef_cookie_visitor_t> {
+ public:
+  CefCookieVisitorCToCpp();
+  virtual ~CefCookieVisitorCToCpp();
+
+  // CefCookieVisitor methods.
+  bool Visit(const CefCookie& cookie,
+             int count,
+             int total,
+             bool& deleteCookie) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_COOKIE_VISITOR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/ctocpp_ref_counted.h b/src/libcef_dll/ctocpp/ctocpp_ref_counted.h
new file mode 100644
index 0000000..91260f5
--- /dev/null
+++ b/src/libcef_dll/ctocpp/ctocpp_ref_counted.h
@@ -0,0 +1,175 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_CTOCPP_REF_COUNTED_H_
+#define CEF_LIBCEF_DLL_CTOCPP_CTOCPP_REF_COUNTED_H_
+#pragma once
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/wrapper_types.h"
+
+// Wrap a C structure with a C++ class. This is used when the implementation
+// exists on the other side of the DLL boundary but will have methods called on
+// this side of the DLL boundary.
+template <class ClassName, class BaseName, class StructName>
+class CefCToCppRefCounted : public BaseName {
+ public:
+  // Create a new wrapper instance for a structure reference received from the
+  // other side.
+  static CefRefPtr<BaseName> Wrap(StructName* s);
+
+  // Retrieve the underlying structure reference from a wrapper instance for
+  // return back to the other side.
+  static StructName* Unwrap(CefRefPtr<BaseName> c);
+
+  // CefBaseRefCounted methods increment/decrement reference counts on both this
+  // object and the underlying wrapped structure.
+  void AddRef() const {
+    UnderlyingAddRef();
+    ref_count_.AddRef();
+  }
+  bool Release() const;
+  bool HasOneRef() const { return UnderlyingHasOneRef(); }
+  bool HasAtLeastOneRef() const { return UnderlyingHasAtLeastOneRef(); }
+
+ protected:
+  CefCToCppRefCounted() {}
+  virtual ~CefCToCppRefCounted() {}
+
+  // If returning the structure across the DLL boundary use Unwrap() instead.
+  StructName* GetStruct() const {
+    WrapperStruct* wrapperStruct = GetWrapperStruct(this);
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+    return wrapperStruct->struct_;
+  }
+
+ private:
+  // Used to associate this wrapper object and the structure reference received
+  // from the other side.
+  struct WrapperStruct;
+
+  static WrapperStruct* GetWrapperStruct(const BaseName* obj);
+
+  // Unwrap as the derived type.
+  static StructName* UnwrapDerived(CefWrapperType type, BaseName* c);
+
+  // Increment/decrement reference counts on only the underlying class.
+  NO_SANITIZE("cfi-icall")
+  void UnderlyingAddRef() const {
+    cef_base_ref_counted_t* base =
+        reinterpret_cast<cef_base_ref_counted_t*>(GetStruct());
+    if (base->add_ref)
+      base->add_ref(base);
+  }
+
+  NO_SANITIZE("cfi-icall")
+  bool UnderlyingRelease() const {
+    cef_base_ref_counted_t* base =
+        reinterpret_cast<cef_base_ref_counted_t*>(GetStruct());
+    if (!base->release)
+      return false;
+    return base->release(base) ? true : false;
+  }
+
+  NO_SANITIZE("cfi-icall")
+  bool UnderlyingHasOneRef() const {
+    cef_base_ref_counted_t* base =
+        reinterpret_cast<cef_base_ref_counted_t*>(GetStruct());
+    if (!base->has_one_ref)
+      return false;
+    return base->has_one_ref(base) ? true : false;
+  }
+
+  NO_SANITIZE("cfi-icall")
+  bool UnderlyingHasAtLeastOneRef() const {
+    cef_base_ref_counted_t* base =
+        reinterpret_cast<cef_base_ref_counted_t*>(GetStruct());
+    if (!base->has_one_ref)
+      return false;
+    return base->has_at_least_one_ref(base) ? true : false;
+  }
+
+  CefRefCount ref_count_;
+
+  static CefWrapperType kWrapperType;
+
+  DISALLOW_COPY_AND_ASSIGN(CefCToCppRefCounted);
+};
+
+template <class ClassName, class BaseName, class StructName>
+struct CefCToCppRefCounted<ClassName, BaseName, StructName>::WrapperStruct {
+  CefWrapperType type_;
+  StructName* struct_;
+  ClassName wrapper_;
+};
+
+template <class ClassName, class BaseName, class StructName>
+CefRefPtr<BaseName> CefCToCppRefCounted<ClassName, BaseName, StructName>::Wrap(
+    StructName* s) {
+  if (!s)
+    return nullptr;
+
+  // Wrap their structure with the CefCToCppRefCounted object.
+  WrapperStruct* wrapperStruct = new WrapperStruct;
+  wrapperStruct->type_ = kWrapperType;
+  wrapperStruct->struct_ = s;
+
+  // Put the wrapper object in a smart pointer.
+  CefRefPtr<BaseName> wrapperPtr(&wrapperStruct->wrapper_);
+  // Release the reference that was added to the CefCppToC wrapper object on
+  // the other side before their structure was passed to us.
+  wrapperStruct->wrapper_.UnderlyingRelease();
+  // Return the smart pointer.
+  return wrapperPtr;
+}
+
+template <class ClassName, class BaseName, class StructName>
+StructName* CefCToCppRefCounted<ClassName, BaseName, StructName>::Unwrap(
+    CefRefPtr<BaseName> c) {
+  if (!c.get())
+    return nullptr;
+
+  WrapperStruct* wrapperStruct = GetWrapperStruct(c.get());
+
+  // If the type does not match this object then we need to unwrap as the
+  // derived type.
+  if (wrapperStruct->type_ != kWrapperType)
+    return UnwrapDerived(wrapperStruct->type_, c.get());
+
+  // Add a reference to the CefCppToC wrapper object on the other side that
+  // will be released once the structure is received.
+  wrapperStruct->wrapper_.UnderlyingAddRef();
+  // Return their original structure.
+  return wrapperStruct->struct_;
+}
+
+template <class ClassName, class BaseName, class StructName>
+bool CefCToCppRefCounted<ClassName, BaseName, StructName>::Release() const {
+  UnderlyingRelease();
+  if (ref_count_.Release()) {
+    WrapperStruct* wrapperStruct = GetWrapperStruct(this);
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+    delete wrapperStruct;
+    return true;
+  }
+  return false;
+}
+
+template <class ClassName, class BaseName, class StructName>
+typename CefCToCppRefCounted<ClassName, BaseName, StructName>::WrapperStruct*
+CefCToCppRefCounted<ClassName, BaseName, StructName>::GetWrapperStruct(
+    const BaseName* obj) {
+  // Offset using the WrapperStruct size instead of individual member sizes to
+  // avoid problems due to platform/compiler differences in structure padding.
+  return reinterpret_cast<WrapperStruct*>(
+      reinterpret_cast<char*>(const_cast<BaseName*>(obj)) -
+      (sizeof(WrapperStruct) - sizeof(ClassName)));
+}
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_CTOCPP_REF_COUNTED_H_
diff --git a/src/libcef_dll/ctocpp/ctocpp_scoped.h b/src/libcef_dll/ctocpp/ctocpp_scoped.h
new file mode 100644
index 0000000..5ceddcb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/ctocpp_scoped.h
@@ -0,0 +1,200 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_CTOCPP_SCOPED_H_
+#define CEF_LIBCEF_DLL_CTOCPP_CTOCPP_SCOPED_H_
+#pragma once
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/capi/cef_base_capi.h"
+#include "include/cef_base.h"
+#include "libcef_dll/ptr_util.h"
+#include "libcef_dll/wrapper_types.h"
+
+// Wrap a C structure with a C++ class. This is used when the implementation
+// exists on the other side of the DLL boundary but will have methods called on
+// this side of the DLL boundary.
+template <class ClassName, class BaseName, class StructName>
+class CefCToCppScoped : public BaseName {
+ public:
+  // Create a new wrapper instance for a structure reference received from the
+  // other side. The caller owns the CToCpp wrapper instance but not necessarily
+  // the underling object on the CppToC side (depends if s->del is non-NULL).
+  // The returned wrapper object can be used as either a scoped argument or to
+  // pass ownership. For example:
+  //
+  // void my_method(my_type1_t* struct1, my_type2_t* struct2) {
+  //  // Passes ownership to MyMethod1().
+  //  MyMethod1(MyType1CToCpp::Wrap(struct1));
+  //
+  //  // Passes reference to MyMethod2().
+  //  CefOwnPtr<MyType1> obj2 = MyType2CToCpp::Wrap(struct2);
+  //  MyMethod2(obj2.get());
+  //  // |obj2| is deleted when my_method() goes out of scope.
+  // }
+  //
+  // void MyMethod1(CefOwnPtr<MyType1> obj1) {
+  //   // |obj1| is deleted when MyMethod1() goes out of scope.
+  // }
+  //
+  // void MyMethod2(CefRawPtr<MyType2> obj2) {
+  // }
+  static CefOwnPtr<BaseName> Wrap(StructName* s);
+
+  // Retrieve the underlying structure reference from a wrapper instance for
+  // return back to the other side. Ownership will be passed back to the other
+  // side and the wrapper will be deleted. For example:
+  //
+  // void MyMethod(CefOwnPtr<MyType> obj) {
+  //   // Ownership of the underlying MyType object is passed to my_method().
+  //   my_method(MyTypeCToCpp::UnwrapOwn(obj.Pass()));
+  //   // |obj| is now NULL.
+  // }
+  static StructName* UnwrapOwn(CefOwnPtr<BaseName> c);
+
+  // Retrieve the underlying structure reference from a wrapper instance for
+  // return back to the other side. Ownership does not change. For example:
+  //
+  // void MyMethod(CefRawPtr<MyType> obj) {
+  //   // A reference is passed to my_method(). Ownership does not change.
+  //   my_method2(MyTypeCToCpp::UnwrapRaw(obj));
+  // }
+  static StructName* UnwrapRaw(CefRawPtr<BaseName> c);
+
+  // Override delete operator to properly delete the WrapperStruct.
+  // ~CefCToCppScoped will be called first followed by this method.
+  static void operator delete(void* ptr);
+
+ protected:
+  CefCToCppScoped() {}
+  virtual ~CefCToCppScoped() {}
+
+  // If returning the structure across the DLL boundary use Unwrap() instead.
+  StructName* GetStruct() const {
+    WrapperStruct* wrapperStruct = GetWrapperStruct(this);
+    // Verify that the wrapper offset was calculated correctly.
+    DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+    return wrapperStruct->struct_;
+  }
+
+ private:
+  // Used to associate this wrapper object and the structure reference received
+  // from the other side.
+  struct WrapperStruct;
+
+  static WrapperStruct* GetWrapperStruct(const BaseName* obj);
+
+  // Unwrap as the derived type.
+  static StructName* UnwrapDerivedOwn(CefWrapperType type,
+                                      CefOwnPtr<BaseName> c);
+  static StructName* UnwrapDerivedRaw(CefWrapperType type,
+                                      CefRawPtr<BaseName> c);
+
+  static CefWrapperType kWrapperType;
+
+  DISALLOW_COPY_AND_ASSIGN(CefCToCppScoped);
+};
+
+template <class ClassName, class BaseName, class StructName>
+struct CefCToCppScoped<ClassName, BaseName, StructName>::WrapperStruct {
+  CefWrapperType type_;
+  StructName* struct_;
+  ClassName wrapper_;
+};
+
+template <class ClassName, class BaseName, class StructName>
+CefOwnPtr<BaseName> CefCToCppScoped<ClassName, BaseName, StructName>::Wrap(
+    StructName* s) {
+  if (!s)
+    return CefOwnPtr<BaseName>();
+
+  // Wrap their structure with the CefCToCpp object.
+  WrapperStruct* wrapperStruct = new WrapperStruct;
+  wrapperStruct->type_ = kWrapperType;
+  wrapperStruct->struct_ = s;
+
+  return CefOwnPtr<BaseName>(&wrapperStruct->wrapper_);
+}
+
+template <class ClassName, class BaseName, class StructName>
+StructName* CefCToCppScoped<ClassName, BaseName, StructName>::UnwrapOwn(
+    CefOwnPtr<BaseName> c) {
+  if (!c.get())
+    return nullptr;
+
+  WrapperStruct* wrapperStruct = GetWrapperStruct(c.get());
+
+  // If the type does not match this object then we need to unwrap as the
+  // derived type.
+  if (wrapperStruct->type_ != kWrapperType)
+    return UnwrapDerivedOwn(wrapperStruct->type_, OWN_PASS(c));
+
+  StructName* orig_struct = wrapperStruct->struct_;
+
+#if DCHECK_IS_ON()
+  // We should own the object currently.
+  cef_base_scoped_t* base = reinterpret_cast<cef_base_scoped_t*>(orig_struct);
+  DCHECK(base && base->del);
+#endif
+
+  // Don't delete the original object when the wrapper is deleted.
+  wrapperStruct->struct_ = nullptr;
+
+  // Return the original structure.
+  return orig_struct;
+  // The wrapper |c| is deleted when this method goes out of scope.
+}
+
+template <class ClassName, class BaseName, class StructName>
+StructName* CefCToCppScoped<ClassName, BaseName, StructName>::UnwrapRaw(
+    CefRawPtr<BaseName> c) {
+  if (!c)
+    return nullptr;
+
+  WrapperStruct* wrapperStruct = GetWrapperStruct(c);
+
+  // If the type does not match this object then we need to unwrap as the
+  // derived type.
+  if (wrapperStruct->type_ != kWrapperType)
+    return UnwrapDerivedRaw(wrapperStruct->type_, c);
+
+  // Return the original structure.
+  return wrapperStruct->struct_;
+}
+
+template <class ClassName, class BaseName, class StructName>
+NO_SANITIZE("cfi-icall")
+void CefCToCppScoped<ClassName, BaseName, StructName>::operator delete(
+    void* ptr) {
+  WrapperStruct* wrapperStruct = GetWrapperStruct(static_cast<BaseName*>(ptr));
+  // Verify that the wrapper offset was calculated correctly.
+  DCHECK_EQ(kWrapperType, wrapperStruct->type_);
+
+  // May be NULL if UnwrapOwn() was called.
+  cef_base_scoped_t* base =
+      reinterpret_cast<cef_base_scoped_t*>(wrapperStruct->struct_);
+
+  // If we own the object (base->del != NULL) then notify the other side that
+  // the object has been deleted.
+  if (base && base->del)
+    base->del(base);
+
+  // Delete the wrapper structure without executing ~CefCToCppScoped() an
+  // additional time.
+  ::operator delete(wrapperStruct);
+}
+
+template <class ClassName, class BaseName, class StructName>
+typename CefCToCppScoped<ClassName, BaseName, StructName>::WrapperStruct*
+CefCToCppScoped<ClassName, BaseName, StructName>::GetWrapperStruct(
+    const BaseName* obj) {
+  // Offset using the WrapperStruct size instead of individual member sizes to
+  // avoid problems due to platform/compiler differences in structure padding.
+  return reinterpret_cast<WrapperStruct*>(
+      reinterpret_cast<char*>(const_cast<BaseName*>(obj)) -
+      (sizeof(WrapperStruct) - sizeof(ClassName)));
+}
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_CTOCPP_SCOPED_H_
diff --git a/src/libcef_dll/ctocpp/delete_cookies_callback_ctocpp.cc b/src/libcef_dll/ctocpp/delete_cookies_callback_ctocpp.cc
new file mode 100644
index 0000000..fc7c5a8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/delete_cookies_callback_ctocpp.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c9c0bf488733d065951d9c989e622fedb67536a0$
+//
+
+#include "libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefDeleteCookiesCallbackCToCpp::OnComplete(int num_deleted) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_delete_cookies_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_complete))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_complete(_struct, num_deleted);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDeleteCookiesCallbackCToCpp::CefDeleteCookiesCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDeleteCookiesCallbackCToCpp::~CefDeleteCookiesCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_delete_cookies_callback_t* CefCToCppRefCounted<
+    CefDeleteCookiesCallbackCToCpp,
+    CefDeleteCookiesCallback,
+    cef_delete_cookies_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                  CefDeleteCookiesCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefDeleteCookiesCallbackCToCpp,
+                        CefDeleteCookiesCallback,
+                        cef_delete_cookies_callback_t>::kWrapperType =
+        WT_DELETE_COOKIES_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h b/src/libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h
new file mode 100644
index 0000000..2544804
--- /dev/null
+++ b/src/libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5548d59ccea5a28af2db242115214b3226b9fb03$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DELETE_COOKIES_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DELETE_COOKIES_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDeleteCookiesCallbackCToCpp
+    : public CefCToCppRefCounted<CefDeleteCookiesCallbackCToCpp,
+                                 CefDeleteCookiesCallback,
+                                 cef_delete_cookies_callback_t> {
+ public:
+  CefDeleteCookiesCallbackCToCpp();
+  virtual ~CefDeleteCookiesCallbackCToCpp();
+
+  // CefDeleteCookiesCallback methods.
+  void OnComplete(int num_deleted) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DELETE_COOKIES_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.cc b/src/libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.cc
new file mode 100644
index 0000000..c3d6210
--- /dev/null
+++ b/src/libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e8d1eaf4b8cdfd79a260de33fb3be05a2948c81e$
+//
+
+#include "libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefDevToolsMessageObserverCToCpp::OnDevToolsMessage(
+    CefRefPtr<CefBrowser> browser,
+    const void* message,
+    size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dev_tools_message_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_dev_tools_message))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return false;
+
+  // Execute
+  int _retval = _struct->on_dev_tools_message(
+      _struct, CefBrowserCppToC::Wrap(browser), message, message_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDevToolsMessageObserverCToCpp::OnDevToolsMethodResult(
+    CefRefPtr<CefBrowser> browser,
+    int message_id,
+    bool success,
+    const void* result,
+    size_t result_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dev_tools_message_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_dev_tools_method_result))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Unverified params: result
+
+  // Execute
+  _struct->on_dev_tools_method_result(_struct, CefBrowserCppToC::Wrap(browser),
+                                      message_id, success, result, result_size);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDevToolsMessageObserverCToCpp::OnDevToolsEvent(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& method,
+    const void* params,
+    size_t params_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dev_tools_message_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_dev_tools_event))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: method; type: string_byref_const
+  DCHECK(!method.empty());
+  if (method.empty())
+    return;
+  // Unverified params: params
+
+  // Execute
+  _struct->on_dev_tools_event(_struct, CefBrowserCppToC::Wrap(browser),
+                              method.GetStruct(), params, params_size);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDevToolsMessageObserverCToCpp::OnDevToolsAgentAttached(
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dev_tools_message_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_dev_tools_agent_attached))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_dev_tools_agent_attached(_struct,
+                                       CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDevToolsMessageObserverCToCpp::OnDevToolsAgentDetached(
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dev_tools_message_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_dev_tools_agent_detached))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_dev_tools_agent_detached(_struct,
+                                       CefBrowserCppToC::Wrap(browser));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDevToolsMessageObserverCToCpp::CefDevToolsMessageObserverCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDevToolsMessageObserverCToCpp::~CefDevToolsMessageObserverCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_dev_tools_message_observer_t* CefCToCppRefCounted<
+    CefDevToolsMessageObserverCToCpp,
+    CefDevToolsMessageObserver,
+    cef_dev_tools_message_observer_t>::UnwrapDerived(CefWrapperType type,
+                                                     CefDevToolsMessageObserver*
+                                                         c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefDevToolsMessageObserverCToCpp,
+                        CefDevToolsMessageObserver,
+                        cef_dev_tools_message_observer_t>::kWrapperType =
+        WT_DEV_TOOLS_MESSAGE_OBSERVER;
diff --git a/src/libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h b/src/libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h
new file mode 100644
index 0000000..d793fa5
--- /dev/null
+++ b/src/libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=26b1ef9d351b0ce4a7ca0d61c53a8374ba21168b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DEV_TOOLS_MESSAGE_OBSERVER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DEV_TOOLS_MESSAGE_OBSERVER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_devtools_message_observer_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_devtools_message_observer.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDevToolsMessageObserverCToCpp
+    : public CefCToCppRefCounted<CefDevToolsMessageObserverCToCpp,
+                                 CefDevToolsMessageObserver,
+                                 cef_dev_tools_message_observer_t> {
+ public:
+  CefDevToolsMessageObserverCToCpp();
+  virtual ~CefDevToolsMessageObserverCToCpp();
+
+  // CefDevToolsMessageObserver methods.
+  bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser,
+                         const void* message,
+                         size_t message_size) override;
+  void OnDevToolsMethodResult(CefRefPtr<CefBrowser> browser,
+                              int message_id,
+                              bool success,
+                              const void* result,
+                              size_t result_size) override;
+  void OnDevToolsEvent(CefRefPtr<CefBrowser> browser,
+                       const CefString& method,
+                       const void* params,
+                       size_t params_size) override;
+  void OnDevToolsAgentAttached(CefRefPtr<CefBrowser> browser) override;
+  void OnDevToolsAgentDetached(CefRefPtr<CefBrowser> browser) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DEV_TOOLS_MESSAGE_OBSERVER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/dialog_handler_ctocpp.cc b/src/libcef_dll/ctocpp/dialog_handler_ctocpp.cc
new file mode 100644
index 0000000..facff52
--- /dev/null
+++ b/src/libcef_dll/ctocpp/dialog_handler_ctocpp.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ad47bb8d870deaf5d51606f6c2bce0105eccdd4b$
+//
+
+#include "libcef_dll/ctocpp/dialog_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/file_dialog_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefDialogHandlerCToCpp::OnFileDialog(
+    CefRefPtr<CefBrowser> browser,
+    FileDialogMode mode,
+    const CefString& title,
+    const CefString& default_file_path,
+    const std::vector<CefString>& accept_filters,
+    int selected_accept_filter,
+    CefRefPtr<CefFileDialogCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dialog_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_file_dialog))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+  // Unverified params: title, default_file_path, accept_filters
+
+  // Translate param: accept_filters; type: string_vec_byref_const
+  cef_string_list_t accept_filtersList = cef_string_list_alloc();
+  DCHECK(accept_filtersList);
+  if (accept_filtersList)
+    transfer_string_list_contents(accept_filters, accept_filtersList);
+
+  // Execute
+  int _retval = _struct->on_file_dialog(
+      _struct, CefBrowserCppToC::Wrap(browser), mode, title.GetStruct(),
+      default_file_path.GetStruct(), accept_filtersList, selected_accept_filter,
+      CefFileDialogCallbackCppToC::Wrap(callback));
+
+  // Restore param:accept_filters; type: string_vec_byref_const
+  if (accept_filtersList)
+    cef_string_list_free(accept_filtersList);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDialogHandlerCToCpp::CefDialogHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDialogHandlerCToCpp::~CefDialogHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_dialog_handler_t*
+CefCToCppRefCounted<CefDialogHandlerCToCpp,
+                    CefDialogHandler,
+                    cef_dialog_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefDialogHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDialogHandlerCToCpp,
+                                   CefDialogHandler,
+                                   cef_dialog_handler_t>::kWrapperType =
+    WT_DIALOG_HANDLER;
diff --git a/src/libcef_dll/ctocpp/dialog_handler_ctocpp.h b/src/libcef_dll/ctocpp/dialog_handler_ctocpp.h
new file mode 100644
index 0000000..668f432
--- /dev/null
+++ b/src/libcef_dll/ctocpp/dialog_handler_ctocpp.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=85f21f598361819e2a463b94edd5e31dcc5b2648$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DIALOG_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DIALOG_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_dialog_handler_capi.h"
+#include "include/cef_dialog_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDialogHandlerCToCpp
+    : public CefCToCppRefCounted<CefDialogHandlerCToCpp,
+                                 CefDialogHandler,
+                                 cef_dialog_handler_t> {
+ public:
+  CefDialogHandlerCToCpp();
+  virtual ~CefDialogHandlerCToCpp();
+
+  // CefDialogHandler methods.
+  bool OnFileDialog(CefRefPtr<CefBrowser> browser,
+                    FileDialogMode mode,
+                    const CefString& title,
+                    const CefString& default_file_path,
+                    const std::vector<CefString>& accept_filters,
+                    int selected_accept_filter,
+                    CefRefPtr<CefFileDialogCallback> callback) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DIALOG_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/dictionary_value_ctocpp.cc b/src/libcef_dll/ctocpp/dictionary_value_ctocpp.cc
new file mode 100644
index 0000000..9259984
--- /dev/null
+++ b/src/libcef_dll/ctocpp/dictionary_value_ctocpp.cc
@@ -0,0 +1,711 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e7dcf32d390886ac5690380fbf034c8daadec891$
+//
+
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/list_value_ctocpp.h"
+#include "libcef_dll/ctocpp/value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDictionaryValue> CefDictionaryValue::Create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dictionary_value_t* _retval = cef_dictionary_value_create();
+
+  // Return type: refptr_same
+  return CefDictionaryValueCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefDictionaryValueCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDictionaryValueCToCpp::IsOwned() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_owned))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_owned(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDictionaryValueCToCpp::IsReadOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::IsSame(CefRefPtr<CefDictionaryValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->is_same(_struct, CefDictionaryValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::IsEqual(CefRefPtr<CefDictionaryValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_equal))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->is_equal(_struct, CefDictionaryValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDictionaryValue> CefDictionaryValueCToCpp::Copy(
+    bool exclude_empty_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, copy))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dictionary_value_t* _retval =
+      _struct->copy(_struct, exclude_empty_children);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefDictionaryValueCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDictionaryValueCToCpp::Clear() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->clear(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::HasKey(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_key))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->has_key(_struct, key.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDictionaryValueCToCpp::GetKeys(KeyList& keys) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_keys))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: keys; type: string_vec_byref
+  cef_string_list_t keysList = cef_string_list_alloc();
+  DCHECK(keysList);
+  if (keysList)
+    transfer_string_list_contents(keys, keysList);
+
+  // Execute
+  int _retval = _struct->get_keys(_struct, keysList);
+
+  // Restore param:keys; type: string_vec_byref
+  if (keysList) {
+    keys.clear();
+    transfer_string_list_contents(keysList, keys);
+    cef_string_list_free(keysList);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::Remove(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->remove(_struct, key.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefValueType CefDictionaryValueCToCpp::GetType(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return VTYPE_INVALID;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return VTYPE_INVALID;
+
+  // Execute
+  cef_value_type_t _retval = _struct->get_type(_struct, key.GetStruct());
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefValue> CefDictionaryValueCToCpp::GetValue(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return nullptr;
+
+  // Execute
+  cef_value_t* _retval = _struct->get_value(_struct, key.GetStruct());
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::GetBool(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->get_bool(_struct, key.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefDictionaryValueCToCpp::GetInt(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_int))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return 0;
+
+  // Execute
+  int _retval = _struct->get_int(_struct, key.GetStruct());
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+double CefDictionaryValueCToCpp::GetDouble(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_double))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return 0;
+
+  // Execute
+  double _retval = _struct->get_double(_struct, key.GetStruct());
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefDictionaryValueCToCpp::GetString(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_string(_struct, key.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefDictionaryValueCToCpp::GetBinary(
+    const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_binary))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return nullptr;
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_binary(_struct, key.GetStruct());
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDictionaryValue> CefDictionaryValueCToCpp::GetDictionary(
+    const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_dictionary))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return nullptr;
+
+  // Execute
+  cef_dictionary_value_t* _retval =
+      _struct->get_dictionary(_struct, key.GetStruct());
+
+  // Return type: refptr_same
+  return CefDictionaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefListValue> CefDictionaryValueCToCpp::GetList(
+    const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_list))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return nullptr;
+
+  // Execute
+  cef_list_value_t* _retval = _struct->get_list(_struct, key.GetStruct());
+
+  // Return type: refptr_same
+  return CefListValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetValue(const CefString& key,
+                                        CefRefPtr<CefValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_value(_struct, key.GetStruct(),
+                                   CefValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetNull(const CefString& key) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_null))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_null(_struct, key.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetBool(const CefString& key, bool value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_bool(_struct, key.GetStruct(), value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetInt(const CefString& key, int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_int))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_int(_struct, key.GetStruct(), value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetDouble(const CefString& key, double value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_double))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_double(_struct, key.GetStruct(), value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetString(const CefString& key,
+                                         const CefString& value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_string))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+  // Unverified params: value
+
+  // Execute
+  int _retval =
+      _struct->set_string(_struct, key.GetStruct(), value.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetBinary(const CefString& key,
+                                         CefRefPtr<CefBinaryValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_binary))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_binary(_struct, key.GetStruct(),
+                                    CefBinaryValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetDictionary(
+    const CefString& key,
+    CefRefPtr<CefDictionaryValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_dictionary))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_dictionary(
+      _struct, key.GetStruct(), CefDictionaryValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDictionaryValueCToCpp::SetList(const CefString& key,
+                                       CefRefPtr<CefListValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_dictionary_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return false;
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_list(_struct, key.GetStruct(),
+                                  CefListValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDictionaryValueCToCpp::CefDictionaryValueCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDictionaryValueCToCpp::~CefDictionaryValueCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_dictionary_value_t* CefCToCppRefCounted<
+    CefDictionaryValueCToCpp,
+    CefDictionaryValue,
+    cef_dictionary_value_t>::UnwrapDerived(CefWrapperType type,
+                                           CefDictionaryValue* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDictionaryValueCToCpp,
+                                   CefDictionaryValue,
+                                   cef_dictionary_value_t>::kWrapperType =
+    WT_DICTIONARY_VALUE;
diff --git a/src/libcef_dll/ctocpp/dictionary_value_ctocpp.h b/src/libcef_dll/ctocpp/dictionary_value_ctocpp.h
new file mode 100644
index 0000000..b1db791
--- /dev/null
+++ b/src/libcef_dll/ctocpp/dictionary_value_ctocpp.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3bb1b095b2bb4e2030635f4ef3000eb8949dfbe1$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DICTIONARY_VALUE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DICTIONARY_VALUE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDictionaryValueCToCpp
+    : public CefCToCppRefCounted<CefDictionaryValueCToCpp,
+                                 CefDictionaryValue,
+                                 cef_dictionary_value_t> {
+ public:
+  CefDictionaryValueCToCpp();
+  virtual ~CefDictionaryValueCToCpp();
+
+  // CefDictionaryValue methods.
+  bool IsValid() OVERRIDE;
+  bool IsOwned() OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  bool IsSame(CefRefPtr<CefDictionaryValue> that) OVERRIDE;
+  bool IsEqual(CefRefPtr<CefDictionaryValue> that) OVERRIDE;
+  CefRefPtr<CefDictionaryValue> Copy(bool exclude_empty_children) OVERRIDE;
+  size_t GetSize() OVERRIDE;
+  bool Clear() OVERRIDE;
+  bool HasKey(const CefString& key) OVERRIDE;
+  bool GetKeys(KeyList& keys) OVERRIDE;
+  bool Remove(const CefString& key) OVERRIDE;
+  CefValueType GetType(const CefString& key) OVERRIDE;
+  CefRefPtr<CefValue> GetValue(const CefString& key) OVERRIDE;
+  bool GetBool(const CefString& key) OVERRIDE;
+  int GetInt(const CefString& key) OVERRIDE;
+  double GetDouble(const CefString& key) OVERRIDE;
+  CefString GetString(const CefString& key) OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetBinary(const CefString& key) OVERRIDE;
+  CefRefPtr<CefDictionaryValue> GetDictionary(const CefString& key) OVERRIDE;
+  CefRefPtr<CefListValue> GetList(const CefString& key) OVERRIDE;
+  bool SetValue(const CefString& key, CefRefPtr<CefValue> value) OVERRIDE;
+  bool SetNull(const CefString& key) OVERRIDE;
+  bool SetBool(const CefString& key, bool value) OVERRIDE;
+  bool SetInt(const CefString& key, int value) OVERRIDE;
+  bool SetDouble(const CefString& key, double value) OVERRIDE;
+  bool SetString(const CefString& key, const CefString& value) OVERRIDE;
+  bool SetBinary(const CefString& key,
+                 CefRefPtr<CefBinaryValue> value) OVERRIDE;
+  bool SetDictionary(const CefString& key,
+                     CefRefPtr<CefDictionaryValue> value) OVERRIDE;
+  bool SetList(const CefString& key, CefRefPtr<CefListValue> value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DICTIONARY_VALUE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/display_handler_ctocpp.cc b/src/libcef_dll/ctocpp/display_handler_ctocpp.cc
new file mode 100644
index 0000000..7139bb8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/display_handler_ctocpp.cc
@@ -0,0 +1,276 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=11c971f10c02ae341f62e70dca05528f78c8d1a2$
+//
+
+#include "libcef_dll/ctocpp/display_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayHandlerCToCpp::OnAddressChange(CefRefPtr<CefBrowser> browser,
+                                              CefRefPtr<CefFrame> frame,
+                                              const CefString& url) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_address_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return;
+
+  // Execute
+  _struct->on_address_change(_struct, CefBrowserCppToC::Wrap(browser),
+                             CefFrameCppToC::Wrap(frame), url.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayHandlerCToCpp::OnTitleChange(CefRefPtr<CefBrowser> browser,
+                                            const CefString& title) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_title_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Unverified params: title
+
+  // Execute
+  _struct->on_title_change(_struct, CefBrowserCppToC::Wrap(browser),
+                           title.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayHandlerCToCpp::OnFaviconURLChange(
+    CefRefPtr<CefBrowser> browser,
+    const std::vector<CefString>& icon_urls) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_favicon_urlchange))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Unverified params: icon_urls
+
+  // Translate param: icon_urls; type: string_vec_byref_const
+  cef_string_list_t icon_urlsList = cef_string_list_alloc();
+  DCHECK(icon_urlsList);
+  if (icon_urlsList)
+    transfer_string_list_contents(icon_urls, icon_urlsList);
+
+  // Execute
+  _struct->on_favicon_urlchange(_struct, CefBrowserCppToC::Wrap(browser),
+                                icon_urlsList);
+
+  // Restore param:icon_urls; type: string_vec_byref_const
+  if (icon_urlsList)
+    cef_string_list_free(icon_urlsList);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayHandlerCToCpp::OnFullscreenModeChange(
+    CefRefPtr<CefBrowser> browser,
+    bool fullscreen) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_fullscreen_mode_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_fullscreen_mode_change(_struct, CefBrowserCppToC::Wrap(browser),
+                                     fullscreen);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDisplayHandlerCToCpp::OnTooltip(CefRefPtr<CefBrowser> browser,
+                                        CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_tooltip))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Unverified params: text
+
+  // Execute
+  int _retval = _struct->on_tooltip(_struct, CefBrowserCppToC::Wrap(browser),
+                                    text.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayHandlerCToCpp::OnStatusMessage(CefRefPtr<CefBrowser> browser,
+                                              const CefString& value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_status_message))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Unverified params: value
+
+  // Execute
+  _struct->on_status_message(_struct, CefBrowserCppToC::Wrap(browser),
+                             value.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDisplayHandlerCToCpp::OnConsoleMessage(CefRefPtr<CefBrowser> browser,
+                                               cef_log_severity_t level,
+                                               const CefString& message,
+                                               const CefString& source,
+                                               int line) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_console_message))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Unverified params: message, source
+
+  // Execute
+  int _retval = _struct->on_console_message(
+      _struct, CefBrowserCppToC::Wrap(browser), level, message.GetStruct(),
+      source.GetStruct(), line);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDisplayHandlerCToCpp::OnAutoResize(CefRefPtr<CefBrowser> browser,
+                                           const CefSize& new_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_auto_resize))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_auto_resize(
+      _struct, CefBrowserCppToC::Wrap(browser), &new_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayHandlerCToCpp::OnLoadingProgressChange(
+    CefRefPtr<CefBrowser> browser,
+    double progress) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_loading_progress_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_loading_progress_change(_struct, CefBrowserCppToC::Wrap(browser),
+                                      progress);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDisplayHandlerCToCpp::CefDisplayHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDisplayHandlerCToCpp::~CefDisplayHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_display_handler_t* CefCToCppRefCounted<
+    CefDisplayHandlerCToCpp,
+    CefDisplayHandler,
+    cef_display_handler_t>::UnwrapDerived(CefWrapperType type,
+                                          CefDisplayHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDisplayHandlerCToCpp,
+                                   CefDisplayHandler,
+                                   cef_display_handler_t>::kWrapperType =
+    WT_DISPLAY_HANDLER;
diff --git a/src/libcef_dll/ctocpp/display_handler_ctocpp.h b/src/libcef_dll/ctocpp/display_handler_ctocpp.h
new file mode 100644
index 0000000..7bdd7a0
--- /dev/null
+++ b/src/libcef_dll/ctocpp/display_handler_ctocpp.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2a5e7a2afb4d183e4688ef7aec8d12d2bc1d5212$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DISPLAY_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DISPLAY_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_display_handler_capi.h"
+#include "include/cef_display_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDisplayHandlerCToCpp
+    : public CefCToCppRefCounted<CefDisplayHandlerCToCpp,
+                                 CefDisplayHandler,
+                                 cef_display_handler_t> {
+ public:
+  CefDisplayHandlerCToCpp();
+  virtual ~CefDisplayHandlerCToCpp();
+
+  // CefDisplayHandler methods.
+  void OnAddressChange(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       const CefString& url) override;
+  void OnTitleChange(CefRefPtr<CefBrowser> browser,
+                     const CefString& title) override;
+  void OnFaviconURLChange(CefRefPtr<CefBrowser> browser,
+                          const std::vector<CefString>& icon_urls) override;
+  void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
+                              bool fullscreen) override;
+  bool OnTooltip(CefRefPtr<CefBrowser> browser, CefString& text) override;
+  void OnStatusMessage(CefRefPtr<CefBrowser> browser,
+                       const CefString& value) override;
+  bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
+                        cef_log_severity_t level,
+                        const CefString& message,
+                        const CefString& source,
+                        int line) override;
+  bool OnAutoResize(CefRefPtr<CefBrowser> browser,
+                    const CefSize& new_size) override;
+  void OnLoadingProgressChange(CefRefPtr<CefBrowser> browser,
+                               double progress) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DISPLAY_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/domdocument_ctocpp.cc b/src/libcef_dll/ctocpp/domdocument_ctocpp.cc
new file mode 100644
index 0000000..e1b9ce8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/domdocument_ctocpp.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6cb38f54146095f4335340ef3183115c4142c30b$
+//
+
+#include "libcef_dll/ctocpp/domdocument_ctocpp.h"
+#include "libcef_dll/ctocpp/domnode_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefDOMDocument::Type CefDOMDocumentCToCpp::GetType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return DOM_DOCUMENT_TYPE_UNKNOWN;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dom_document_type_t _retval = _struct->get_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMNode> CefDOMDocumentCToCpp::GetDocument() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_document))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_document(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDOMNode> CefDOMDocumentCToCpp::GetBody() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_body))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_body(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDOMNode> CefDOMDocumentCToCpp::GetHead() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_head))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_head(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMDocumentCToCpp::GetTitle() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_title))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_title(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMNode> CefDOMDocumentCToCpp::GetElementById(
+    const CefString& id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_element_by_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: id; type: string_byref_const
+  DCHECK(!id.empty());
+  if (id.empty())
+    return nullptr;
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_element_by_id(_struct, id.GetStruct());
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMNode> CefDOMDocumentCToCpp::GetFocusedNode() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_focused_node))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_focused_node(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefDOMDocumentCToCpp::HasSelection() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_selection))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_selection(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefDOMDocumentCToCpp::GetSelectionStartOffset() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selection_start_offset))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_selection_start_offset(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefDOMDocumentCToCpp::GetSelectionEndOffset() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selection_end_offset))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_selection_end_offset(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefDOMDocumentCToCpp::GetSelectionAsMarkup() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selection_as_markup))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_selection_as_markup(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMDocumentCToCpp::GetSelectionAsText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selection_as_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_selection_as_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMDocumentCToCpp::GetBaseURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_base_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_base_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefDOMDocumentCToCpp::GetCompleteURL(const CefString& partialURL) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domdocument_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_complete_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: partialURL; type: string_byref_const
+  DCHECK(!partialURL.empty());
+  if (partialURL.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_complete_url(_struct, partialURL.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDOMDocumentCToCpp::CefDOMDocumentCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDOMDocumentCToCpp::~CefDOMDocumentCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_domdocument_t*
+CefCToCppRefCounted<CefDOMDocumentCToCpp, CefDOMDocument, cef_domdocument_t>::
+    UnwrapDerived(CefWrapperType type, CefDOMDocument* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDOMDocumentCToCpp,
+                                   CefDOMDocument,
+                                   cef_domdocument_t>::kWrapperType =
+    WT_DOMDOCUMENT;
diff --git a/src/libcef_dll/ctocpp/domdocument_ctocpp.h b/src/libcef_dll/ctocpp/domdocument_ctocpp.h
new file mode 100644
index 0000000..4c97a95
--- /dev/null
+++ b/src/libcef_dll/ctocpp/domdocument_ctocpp.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=77af57c3b139249d9bf00529d2baa5f231e3ea23$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DOMDOCUMENT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DOMDOCUMENT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_dom_capi.h"
+#include "include/cef_dom.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDOMDocumentCToCpp : public CefCToCppRefCounted<CefDOMDocumentCToCpp,
+                                                        CefDOMDocument,
+                                                        cef_domdocument_t> {
+ public:
+  CefDOMDocumentCToCpp();
+  virtual ~CefDOMDocumentCToCpp();
+
+  // CefDOMDocument methods.
+  Type GetType() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetDocument() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetBody() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetHead() OVERRIDE;
+  CefString GetTitle() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetElementById(const CefString& id) OVERRIDE;
+  CefRefPtr<CefDOMNode> GetFocusedNode() OVERRIDE;
+  bool HasSelection() OVERRIDE;
+  int GetSelectionStartOffset() OVERRIDE;
+  int GetSelectionEndOffset() OVERRIDE;
+  CefString GetSelectionAsMarkup() OVERRIDE;
+  CefString GetSelectionAsText() OVERRIDE;
+  CefString GetBaseURL() OVERRIDE;
+  CefString GetCompleteURL(const CefString& partialURL) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DOMDOCUMENT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/domnode_ctocpp.cc b/src/libcef_dll/ctocpp/domnode_ctocpp.cc
new file mode 100644
index 0000000..a7a2261
--- /dev/null
+++ b/src/libcef_dll/ctocpp/domnode_ctocpp.cc
@@ -0,0 +1,529 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3f18ade9d60d3b383161ac49e65f61eb62887774$
+//
+
+#include "libcef_dll/ctocpp/domnode_ctocpp.h"
+#include "libcef_dll/ctocpp/domdocument_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefDOMNode::Type CefDOMNodeCToCpp::GetType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return DOM_NODE_TYPE_UNSUPPORTED;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dom_node_type_t _retval = _struct->get_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDOMNodeCToCpp::IsText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_text))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_text(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDOMNodeCToCpp::IsElement() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_element))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_element(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDOMNodeCToCpp::IsEditable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_editable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_editable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDOMNodeCToCpp::IsFormControlElement() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_form_control_element))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_form_control_element(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefDOMNodeCToCpp::GetFormControlElementType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_form_control_element_type))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_form_control_element_type(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDOMNodeCToCpp::IsSame(CefRefPtr<CefDOMNode> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefDOMNodeCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMNodeCToCpp::GetName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMNodeCToCpp::GetValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_value(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDOMNodeCToCpp::SetValue(const CefString& value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: string_byref_const
+  DCHECK(!value.empty());
+  if (value.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_value(_struct, value.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMNodeCToCpp::GetAsMarkup() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_as_markup))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_as_markup(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMDocument> CefDOMNodeCToCpp::GetDocument() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_document))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domdocument_t* _retval = _struct->get_document(_struct);
+
+  // Return type: refptr_same
+  return CefDOMDocumentCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDOMNode> CefDOMNodeCToCpp::GetParent() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_parent))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_parent(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMNode> CefDOMNodeCToCpp::GetPreviousSibling() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_previous_sibling))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_previous_sibling(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMNode> CefDOMNodeCToCpp::GetNextSibling() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_next_sibling))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_next_sibling(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefDOMNodeCToCpp::HasChildren() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_children))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_children(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMNode> CefDOMNodeCToCpp::GetFirstChild() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_first_child))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_first_child(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDOMNode> CefDOMNodeCToCpp::GetLastChild() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_last_child))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_domnode_t* _retval = _struct->get_last_child(_struct);
+
+  // Return type: refptr_same
+  return CefDOMNodeCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMNodeCToCpp::GetElementTagName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_element_tag_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_element_tag_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDOMNodeCToCpp::HasElementAttributes() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_element_attributes))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_element_attributes(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDOMNodeCToCpp::HasElementAttribute(const CefString& attrName) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_element_attribute))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: attrName; type: string_byref_const
+  DCHECK(!attrName.empty());
+  if (attrName.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->has_element_attribute(_struct, attrName.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefDOMNodeCToCpp::GetElementAttribute(const CefString& attrName) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_element_attribute))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: attrName; type: string_byref_const
+  DCHECK(!attrName.empty());
+  if (attrName.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_element_attribute(_struct, attrName.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDOMNodeCToCpp::GetElementAttributes(AttributeMap& attrMap) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_element_attributes))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: attrMap; type: string_map_single_byref
+  cef_string_map_t attrMapMap = cef_string_map_alloc();
+  DCHECK(attrMapMap);
+  if (attrMapMap)
+    transfer_string_map_contents(attrMap, attrMapMap);
+
+  // Execute
+  _struct->get_element_attributes(_struct, attrMapMap);
+
+  // Restore param:attrMap; type: string_map_single_byref
+  if (attrMapMap) {
+    attrMap.clear();
+    transfer_string_map_contents(attrMapMap, attrMap);
+    cef_string_map_free(attrMapMap);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDOMNodeCToCpp::SetElementAttribute(const CefString& attrName,
+                                           const CefString& value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_element_attribute))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: attrName; type: string_byref_const
+  DCHECK(!attrName.empty());
+  if (attrName.empty())
+    return false;
+  // Verify param: value; type: string_byref_const
+  DCHECK(!value.empty());
+  if (value.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_element_attribute(_struct, attrName.GetStruct(),
+                                               value.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDOMNodeCToCpp::GetElementInnerText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_element_inner_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_element_inner_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefDOMNodeCToCpp::GetElementBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domnode_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_element_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_element_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDOMNodeCToCpp::CefDOMNodeCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDOMNodeCToCpp::~CefDOMNodeCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_domnode_t*
+CefCToCppRefCounted<CefDOMNodeCToCpp, CefDOMNode, cef_domnode_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefDOMNode* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDOMNodeCToCpp,
+                                   CefDOMNode,
+                                   cef_domnode_t>::kWrapperType = WT_DOMNODE;
diff --git a/src/libcef_dll/ctocpp/domnode_ctocpp.h b/src/libcef_dll/ctocpp/domnode_ctocpp.h
new file mode 100644
index 0000000..f240618
--- /dev/null
+++ b/src/libcef_dll/ctocpp/domnode_ctocpp.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3336f2ea70c4ca369801bc8a459aff5ed10f4605$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DOMNODE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DOMNODE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_dom_capi.h"
+#include "include/cef_dom.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDOMNodeCToCpp
+    : public CefCToCppRefCounted<CefDOMNodeCToCpp, CefDOMNode, cef_domnode_t> {
+ public:
+  CefDOMNodeCToCpp();
+  virtual ~CefDOMNodeCToCpp();
+
+  // CefDOMNode methods.
+  Type GetType() OVERRIDE;
+  bool IsText() OVERRIDE;
+  bool IsElement() OVERRIDE;
+  bool IsEditable() OVERRIDE;
+  bool IsFormControlElement() OVERRIDE;
+  CefString GetFormControlElementType() OVERRIDE;
+  bool IsSame(CefRefPtr<CefDOMNode> that) OVERRIDE;
+  CefString GetName() OVERRIDE;
+  CefString GetValue() OVERRIDE;
+  bool SetValue(const CefString& value) OVERRIDE;
+  CefString GetAsMarkup() OVERRIDE;
+  CefRefPtr<CefDOMDocument> GetDocument() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetParent() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetPreviousSibling() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetNextSibling() OVERRIDE;
+  bool HasChildren() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetFirstChild() OVERRIDE;
+  CefRefPtr<CefDOMNode> GetLastChild() OVERRIDE;
+  CefString GetElementTagName() OVERRIDE;
+  bool HasElementAttributes() OVERRIDE;
+  bool HasElementAttribute(const CefString& attrName) OVERRIDE;
+  CefString GetElementAttribute(const CefString& attrName) OVERRIDE;
+  void GetElementAttributes(AttributeMap& attrMap) OVERRIDE;
+  bool SetElementAttribute(const CefString& attrName,
+                           const CefString& value) OVERRIDE;
+  CefString GetElementInnerText() OVERRIDE;
+  CefRect GetElementBounds() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DOMNODE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/domvisitor_ctocpp.cc b/src/libcef_dll/ctocpp/domvisitor_ctocpp.cc
new file mode 100644
index 0000000..f17b5f3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/domvisitor_ctocpp.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e34ff7db1264daf93af8cb95913daa3750ada3b0$
+//
+
+#include "libcef_dll/ctocpp/domvisitor_ctocpp.h"
+#include "libcef_dll/cpptoc/domdocument_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefDOMVisitorCToCpp::Visit(CefRefPtr<CefDOMDocument> document) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_domvisitor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: document; type: refptr_diff
+  DCHECK(document.get());
+  if (!document.get())
+    return;
+
+  // Execute
+  _struct->visit(_struct, CefDOMDocumentCppToC::Wrap(document));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDOMVisitorCToCpp::CefDOMVisitorCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDOMVisitorCToCpp::~CefDOMVisitorCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_domvisitor_t*
+CefCToCppRefCounted<CefDOMVisitorCToCpp, CefDOMVisitor, cef_domvisitor_t>::
+    UnwrapDerived(CefWrapperType type, CefDOMVisitor* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDOMVisitorCToCpp,
+                                   CefDOMVisitor,
+                                   cef_domvisitor_t>::kWrapperType =
+    WT_DOMVISITOR;
diff --git a/src/libcef_dll/ctocpp/domvisitor_ctocpp.h b/src/libcef_dll/ctocpp/domvisitor_ctocpp.h
new file mode 100644
index 0000000..88f779b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/domvisitor_ctocpp.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c8f5a5d42188269bd9732f28485d36284b1cb45b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DOMVISITOR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DOMVISITOR_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_dom_capi.h"
+#include "include/cef_dom.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDOMVisitorCToCpp : public CefCToCppRefCounted<CefDOMVisitorCToCpp,
+                                                       CefDOMVisitor,
+                                                       cef_domvisitor_t> {
+ public:
+  CefDOMVisitorCToCpp();
+  virtual ~CefDOMVisitorCToCpp();
+
+  // CefDOMVisitor methods.
+  void Visit(CefRefPtr<CefDOMDocument> document) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DOMVISITOR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/download_handler_ctocpp.cc b/src/libcef_dll/ctocpp/download_handler_ctocpp.cc
new file mode 100644
index 0000000..9a433ea
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_handler_ctocpp.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=667fe6d2620ca95df5e503f6332cf3e2342032ff$
+//
+
+#include "libcef_dll/ctocpp/download_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/before_download_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/download_item_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/download_item_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefDownloadHandlerCToCpp::OnBeforeDownload(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDownloadItem> download_item,
+    const CefString& suggested_name,
+    CefRefPtr<CefBeforeDownloadCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_download))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: download_item; type: refptr_diff
+  DCHECK(download_item.get());
+  if (!download_item.get())
+    return;
+  // Verify param: suggested_name; type: string_byref_const
+  DCHECK(!suggested_name.empty());
+  if (suggested_name.empty())
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  _struct->on_before_download(_struct, CefBrowserCppToC::Wrap(browser),
+                              CefDownloadItemCppToC::Wrap(download_item),
+                              suggested_name.GetStruct(),
+                              CefBeforeDownloadCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDownloadHandlerCToCpp::OnDownloadUpdated(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDownloadItem> download_item,
+    CefRefPtr<CefDownloadItemCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_download_updated))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: download_item; type: refptr_diff
+  DCHECK(download_item.get());
+  if (!download_item.get())
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  _struct->on_download_updated(_struct, CefBrowserCppToC::Wrap(browser),
+                               CefDownloadItemCppToC::Wrap(download_item),
+                               CefDownloadItemCallbackCppToC::Wrap(callback));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadHandlerCToCpp::CefDownloadHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadHandlerCToCpp::~CefDownloadHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_download_handler_t* CefCToCppRefCounted<
+    CefDownloadHandlerCToCpp,
+    CefDownloadHandler,
+    cef_download_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           CefDownloadHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDownloadHandlerCToCpp,
+                                   CefDownloadHandler,
+                                   cef_download_handler_t>::kWrapperType =
+    WT_DOWNLOAD_HANDLER;
diff --git a/src/libcef_dll/ctocpp/download_handler_ctocpp.h b/src/libcef_dll/ctocpp/download_handler_ctocpp.h
new file mode 100644
index 0000000..b394739
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_handler_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=52a2a7f06afb8a82f469cb96b3c49aa914a2a87f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_download_handler_capi.h"
+#include "include/cef_download_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDownloadHandlerCToCpp
+    : public CefCToCppRefCounted<CefDownloadHandlerCToCpp,
+                                 CefDownloadHandler,
+                                 cef_download_handler_t> {
+ public:
+  CefDownloadHandlerCToCpp();
+  virtual ~CefDownloadHandlerCToCpp();
+
+  // CefDownloadHandler methods.
+  void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDownloadItem> download_item,
+                        const CefString& suggested_name,
+                        CefRefPtr<CefBeforeDownloadCallback> callback) override;
+  void OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefDownloadItem> download_item,
+                         CefRefPtr<CefDownloadItemCallback> callback) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/download_image_callback_ctocpp.cc b/src/libcef_dll/ctocpp/download_image_callback_ctocpp.cc
new file mode 100644
index 0000000..0ca242e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_image_callback_ctocpp.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ec0d6a1abada255681087caf7a9db1bba5e90a7b$
+//
+
+#include "libcef_dll/ctocpp/download_image_callback_ctocpp.h"
+#include "libcef_dll/cpptoc/image_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefDownloadImageCallbackCToCpp::OnDownloadImageFinished(
+    const CefString& image_url,
+    int http_status_code,
+    CefRefPtr<CefImage> image) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_image_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_download_image_finished))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: image_url; type: string_byref_const
+  DCHECK(!image_url.empty());
+  if (image_url.empty())
+    return;
+  // Unverified params: image
+
+  // Execute
+  _struct->on_download_image_finished(_struct, image_url.GetStruct(),
+                                      http_status_code,
+                                      CefImageCppToC::Wrap(image));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadImageCallbackCToCpp::CefDownloadImageCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadImageCallbackCToCpp::~CefDownloadImageCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_download_image_callback_t* CefCToCppRefCounted<
+    CefDownloadImageCallbackCToCpp,
+    CefDownloadImageCallback,
+    cef_download_image_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                  CefDownloadImageCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefDownloadImageCallbackCToCpp,
+                        CefDownloadImageCallback,
+                        cef_download_image_callback_t>::kWrapperType =
+        WT_DOWNLOAD_IMAGE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/download_image_callback_ctocpp.h b/src/libcef_dll/ctocpp/download_image_callback_ctocpp.h
new file mode 100644
index 0000000..bd28afa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_image_callback_ctocpp.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b98f56b5265b6a1be28ee7c13112dc3e03e93469$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_IMAGE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_IMAGE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDownloadImageCallbackCToCpp
+    : public CefCToCppRefCounted<CefDownloadImageCallbackCToCpp,
+                                 CefDownloadImageCallback,
+                                 cef_download_image_callback_t> {
+ public:
+  CefDownloadImageCallbackCToCpp();
+  virtual ~CefDownloadImageCallbackCToCpp();
+
+  // CefDownloadImageCallback methods.
+  void OnDownloadImageFinished(const CefString& image_url,
+                               int http_status_code,
+                               CefRefPtr<CefImage> image) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_IMAGE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/download_item_callback_ctocpp.cc b/src/libcef_dll/ctocpp/download_item_callback_ctocpp.cc
new file mode 100644
index 0000000..0360f62
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_item_callback_ctocpp.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bf55af2dad90c3fe81e337640709f31005ba8a8e$
+//
+
+#include "libcef_dll/ctocpp/download_item_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefDownloadItemCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefDownloadItemCallbackCToCpp::Pause() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, pause))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->pause(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefDownloadItemCallbackCToCpp::Resume() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, resume))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->resume(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCallbackCToCpp::CefDownloadItemCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCallbackCToCpp::~CefDownloadItemCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_download_item_callback_t* CefCToCppRefCounted<
+    CefDownloadItemCallbackCToCpp,
+    CefDownloadItemCallback,
+    cef_download_item_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                 CefDownloadItemCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDownloadItemCallbackCToCpp,
+                                   CefDownloadItemCallback,
+                                   cef_download_item_callback_t>::kWrapperType =
+    WT_DOWNLOAD_ITEM_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/download_item_callback_ctocpp.h b/src/libcef_dll/ctocpp/download_item_callback_ctocpp.h
new file mode 100644
index 0000000..258a4d7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_item_callback_ctocpp.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ba5b388ff5ba71224499779e8410b8354e05363c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_ITEM_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_ITEM_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_download_handler_capi.h"
+#include "include/cef_download_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDownloadItemCallbackCToCpp
+    : public CefCToCppRefCounted<CefDownloadItemCallbackCToCpp,
+                                 CefDownloadItemCallback,
+                                 cef_download_item_callback_t> {
+ public:
+  CefDownloadItemCallbackCToCpp();
+  virtual ~CefDownloadItemCallbackCToCpp();
+
+  // CefDownloadItemCallback methods.
+  void Cancel() OVERRIDE;
+  void Pause() OVERRIDE;
+  void Resume() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_ITEM_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/download_item_ctocpp.cc b/src/libcef_dll/ctocpp/download_item_ctocpp.cc
new file mode 100644
index 0000000..c29b480
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_item_ctocpp.cc
@@ -0,0 +1,330 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2df7e7e66fe8562a16d2d3f1b9068eac73eb444c$
+//
+
+#include "libcef_dll/ctocpp/download_item_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefDownloadItemCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDownloadItemCToCpp::IsInProgress() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_in_progress))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_in_progress(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDownloadItemCToCpp::IsComplete() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_complete))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_complete(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDownloadItemCToCpp::IsCanceled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_canceled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_canceled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefDownloadItemCToCpp::GetCurrentSpeed() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_current_speed))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->get_current_speed(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefDownloadItemCToCpp::GetPercentComplete() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_percent_complete))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_percent_complete(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefDownloadItemCToCpp::GetTotalBytes() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_total_bytes))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->get_total_bytes(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefDownloadItemCToCpp::GetReceivedBytes() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_received_bytes))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->get_received_bytes(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefTime CefDownloadItemCToCpp::GetStartTime() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_start_time))
+    return CefTime();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_time_t _retval = _struct->get_start_time(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefTime CefDownloadItemCToCpp::GetEndTime() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_end_time))
+    return CefTime();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_time_t _retval = _struct->get_end_time(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDownloadItemCToCpp::GetFullPath() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_full_path))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_full_path(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") uint32 CefDownloadItemCToCpp::GetId() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  uint32 _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDownloadItemCToCpp::GetURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDownloadItemCToCpp::GetOriginalUrl() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_original_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_original_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefDownloadItemCToCpp::GetSuggestedFileName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_suggested_file_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_suggested_file_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefDownloadItemCToCpp::GetContentDisposition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_content_disposition))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_content_disposition(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDownloadItemCToCpp::GetMimeType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_download_item_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_mime_type))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_mime_type(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCToCpp::CefDownloadItemCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDownloadItemCToCpp::~CefDownloadItemCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_download_item_t*
+CefCToCppRefCounted<CefDownloadItemCToCpp,
+                    CefDownloadItem,
+                    cef_download_item_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefDownloadItem* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDownloadItemCToCpp,
+                                   CefDownloadItem,
+                                   cef_download_item_t>::kWrapperType =
+    WT_DOWNLOAD_ITEM;
diff --git a/src/libcef_dll/ctocpp/download_item_ctocpp.h b/src/libcef_dll/ctocpp/download_item_ctocpp.h
new file mode 100644
index 0000000..9c9a3fa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/download_item_ctocpp.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a7cd44cf21351fde788e0756af8d3b9ddb72669a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_ITEM_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_ITEM_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_download_item_capi.h"
+#include "include/cef_download_item.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDownloadItemCToCpp : public CefCToCppRefCounted<CefDownloadItemCToCpp,
+                                                         CefDownloadItem,
+                                                         cef_download_item_t> {
+ public:
+  CefDownloadItemCToCpp();
+  virtual ~CefDownloadItemCToCpp();
+
+  // CefDownloadItem methods.
+  bool IsValid() OVERRIDE;
+  bool IsInProgress() OVERRIDE;
+  bool IsComplete() OVERRIDE;
+  bool IsCanceled() OVERRIDE;
+  int64 GetCurrentSpeed() OVERRIDE;
+  int GetPercentComplete() OVERRIDE;
+  int64 GetTotalBytes() OVERRIDE;
+  int64 GetReceivedBytes() OVERRIDE;
+  CefTime GetStartTime() OVERRIDE;
+  CefTime GetEndTime() OVERRIDE;
+  CefString GetFullPath() OVERRIDE;
+  uint32 GetId() OVERRIDE;
+  CefString GetURL() OVERRIDE;
+  CefString GetOriginalUrl() OVERRIDE;
+  CefString GetSuggestedFileName() OVERRIDE;
+  CefString GetContentDisposition() OVERRIDE;
+  CefString GetMimeType() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DOWNLOAD_ITEM_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/drag_data_ctocpp.cc b/src/libcef_dll/ctocpp/drag_data_ctocpp.cc
new file mode 100644
index 0000000..3078d21
--- /dev/null
+++ b/src/libcef_dll/ctocpp/drag_data_ctocpp.cc
@@ -0,0 +1,493 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3c09e3dd7fb76abe189934ca434a74af29ac9954$
+//
+
+#include "libcef_dll/ctocpp/drag_data_ctocpp.h"
+#include "libcef_dll/ctocpp/image_ctocpp.h"
+#include "libcef_dll/ctocpp/stream_writer_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDragData> CefDragData::Create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_drag_data_t* _retval = cef_drag_data_create();
+
+  // Return type: refptr_same
+  return CefDragDataCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDragData> CefDragDataCToCpp::Clone() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clone))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_drag_data_t* _retval = _struct->clone(_struct);
+
+  // Return type: refptr_same
+  return CefDragDataCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefDragDataCToCpp::IsReadOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDragDataCToCpp::IsLink() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_link))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_link(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDragDataCToCpp::IsFragment() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_fragment))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_fragment(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDragDataCToCpp::IsFile() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_file))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_file(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDragDataCToCpp::GetLinkURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_link_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_link_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDragDataCToCpp::GetLinkTitle() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_link_title))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_link_title(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDragDataCToCpp::GetLinkMetadata() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_link_metadata))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_link_metadata(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDragDataCToCpp::GetFragmentText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_fragment_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_fragment_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDragDataCToCpp::GetFragmentHtml() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_fragment_html))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_fragment_html(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDragDataCToCpp::GetFragmentBaseURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_fragment_base_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_fragment_base_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefDragDataCToCpp::GetFileName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_file_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_file_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+size_t CefDragDataCToCpp::GetFileContents(CefRefPtr<CefStreamWriter> writer) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_file_contents))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: writer
+
+  // Execute
+  size_t _retval = _struct->get_file_contents(
+      _struct, CefStreamWriterCToCpp::Unwrap(writer));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefDragDataCToCpp::GetFileNames(std::vector<CefString>& names) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_file_names))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: names; type: string_vec_byref
+  cef_string_list_t namesList = cef_string_list_alloc();
+  DCHECK(namesList);
+  if (namesList)
+    transfer_string_list_contents(names, namesList);
+
+  // Execute
+  int _retval = _struct->get_file_names(_struct, namesList);
+
+  // Restore param:names; type: string_vec_byref
+  if (namesList) {
+    names.clear();
+    transfer_string_list_contents(namesList, names);
+    cef_string_list_free(namesList);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragDataCToCpp::SetLinkURL(const CefString& url) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_link_url))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: url
+
+  // Execute
+  _struct->set_link_url(_struct, url.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragDataCToCpp::SetLinkTitle(const CefString& title) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_link_title))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: title
+
+  // Execute
+  _struct->set_link_title(_struct, title.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragDataCToCpp::SetLinkMetadata(const CefString& data) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_link_metadata))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: data
+
+  // Execute
+  _struct->set_link_metadata(_struct, data.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragDataCToCpp::SetFragmentText(const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_fragment_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: text
+
+  // Execute
+  _struct->set_fragment_text(_struct, text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragDataCToCpp::SetFragmentHtml(const CefString& html) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_fragment_html))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: html
+
+  // Execute
+  _struct->set_fragment_html(_struct, html.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragDataCToCpp::SetFragmentBaseURL(const CefString& base_url) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_fragment_base_url))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: base_url
+
+  // Execute
+  _struct->set_fragment_base_url(_struct, base_url.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") void CefDragDataCToCpp::ResetFileContents() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, reset_file_contents))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->reset_file_contents(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragDataCToCpp::AddFile(const CefString& path,
+                                const CefString& display_name) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_file))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+  // Unverified params: display_name
+
+  // Execute
+  _struct->add_file(_struct, path.GetStruct(), display_name.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefImage> CefDragDataCToCpp::GetImage() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_image))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_image_t* _retval = _struct->get_image(_struct);
+
+  // Return type: refptr_same
+  return CefImageCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefDragDataCToCpp::GetImageHotspot() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_image_hotspot))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_image_hotspot(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefDragDataCToCpp::HasImage() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_image))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_image(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDragDataCToCpp::CefDragDataCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDragDataCToCpp::~CefDragDataCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_drag_data_t*
+CefCToCppRefCounted<CefDragDataCToCpp, CefDragData, cef_drag_data_t>::
+    UnwrapDerived(CefWrapperType type, CefDragData* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDragDataCToCpp,
+                                   CefDragData,
+                                   cef_drag_data_t>::kWrapperType =
+    WT_DRAG_DATA;
diff --git a/src/libcef_dll/ctocpp/drag_data_ctocpp.h b/src/libcef_dll/ctocpp/drag_data_ctocpp.h
new file mode 100644
index 0000000..954404a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/drag_data_ctocpp.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fde29293045566e5af81f49fa8a3cce687a4a79f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DRAG_DATA_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DRAG_DATA_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_drag_data_capi.h"
+#include "include/cef_drag_data.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDragDataCToCpp : public CefCToCppRefCounted<CefDragDataCToCpp,
+                                                     CefDragData,
+                                                     cef_drag_data_t> {
+ public:
+  CefDragDataCToCpp();
+  virtual ~CefDragDataCToCpp();
+
+  // CefDragData methods.
+  CefRefPtr<CefDragData> Clone() OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  bool IsLink() OVERRIDE;
+  bool IsFragment() OVERRIDE;
+  bool IsFile() OVERRIDE;
+  CefString GetLinkURL() OVERRIDE;
+  CefString GetLinkTitle() OVERRIDE;
+  CefString GetLinkMetadata() OVERRIDE;
+  CefString GetFragmentText() OVERRIDE;
+  CefString GetFragmentHtml() OVERRIDE;
+  CefString GetFragmentBaseURL() OVERRIDE;
+  CefString GetFileName() OVERRIDE;
+  size_t GetFileContents(CefRefPtr<CefStreamWriter> writer) OVERRIDE;
+  bool GetFileNames(std::vector<CefString>& names) OVERRIDE;
+  void SetLinkURL(const CefString& url) OVERRIDE;
+  void SetLinkTitle(const CefString& title) OVERRIDE;
+  void SetLinkMetadata(const CefString& data) OVERRIDE;
+  void SetFragmentText(const CefString& text) OVERRIDE;
+  void SetFragmentHtml(const CefString& html) OVERRIDE;
+  void SetFragmentBaseURL(const CefString& base_url) OVERRIDE;
+  void ResetFileContents() OVERRIDE;
+  void AddFile(const CefString& path, const CefString& display_name) OVERRIDE;
+  CefRefPtr<CefImage> GetImage() OVERRIDE;
+  CefPoint GetImageHotspot() OVERRIDE;
+  bool HasImage() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DRAG_DATA_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/drag_handler_ctocpp.cc b/src/libcef_dll/ctocpp/drag_handler_ctocpp.cc
new file mode 100644
index 0000000..bc72af3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/drag_handler_ctocpp.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1aa70a0c640336bc3569f151bfba80d675b4c07e$
+//
+
+#include "libcef_dll/ctocpp/drag_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/drag_data_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefDragHandlerCToCpp::OnDragEnter(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefDragData> dragData,
+                                       DragOperationsMask mask) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_drag_enter))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: dragData; type: refptr_diff
+  DCHECK(dragData.get());
+  if (!dragData.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_drag_enter(_struct, CefBrowserCppToC::Wrap(browser),
+                                       CefDragDataCppToC::Wrap(dragData), mask);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDragHandlerCToCpp::OnDraggableRegionsChanged(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    const std::vector<CefDraggableRegion>& regions) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_drag_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_draggable_regions_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+
+  // Translate param: regions; type: simple_vec_byref_const
+  const size_t regionsCount = regions.size();
+  cef_draggable_region_t* regionsList = NULL;
+  if (regionsCount > 0) {
+    regionsList = new cef_draggable_region_t[regionsCount];
+    DCHECK(regionsList);
+    if (regionsList) {
+      for (size_t i = 0; i < regionsCount; ++i) {
+        regionsList[i] = regions[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->on_draggable_regions_changed(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      regionsCount, regionsList);
+
+  // Restore param:regions; type: simple_vec_byref_const
+  if (regionsList)
+    delete[] regionsList;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDragHandlerCToCpp::CefDragHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDragHandlerCToCpp::~CefDragHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_drag_handler_t*
+CefCToCppRefCounted<CefDragHandlerCToCpp, CefDragHandler, cef_drag_handler_t>::
+    UnwrapDerived(CefWrapperType type, CefDragHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDragHandlerCToCpp,
+                                   CefDragHandler,
+                                   cef_drag_handler_t>::kWrapperType =
+    WT_DRAG_HANDLER;
diff --git a/src/libcef_dll/ctocpp/drag_handler_ctocpp.h b/src/libcef_dll/ctocpp/drag_handler_ctocpp.h
new file mode 100644
index 0000000..a6ea567
--- /dev/null
+++ b/src/libcef_dll/ctocpp/drag_handler_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e0f301338abe21b6d6ad6d38f6497d87fa3d9c91$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_DRAG_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_DRAG_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_drag_handler_capi.h"
+#include "include/cef_drag_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefDragHandlerCToCpp : public CefCToCppRefCounted<CefDragHandlerCToCpp,
+                                                        CefDragHandler,
+                                                        cef_drag_handler_t> {
+ public:
+  CefDragHandlerCToCpp();
+  virtual ~CefDragHandlerCToCpp();
+
+  // CefDragHandler methods.
+  bool OnDragEnter(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefDragData> dragData,
+                   DragOperationsMask mask) override;
+  void OnDraggableRegionsChanged(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const std::vector<CefDraggableRegion>& regions) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_DRAG_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/end_tracing_callback_ctocpp.cc b/src/libcef_dll/ctocpp/end_tracing_callback_ctocpp.cc
new file mode 100644
index 0000000..8f6f9cd
--- /dev/null
+++ b/src/libcef_dll/ctocpp/end_tracing_callback_ctocpp.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9a3a21ab271832d967d4212b1fa9362691eb3bdc$
+//
+
+#include "libcef_dll/ctocpp/end_tracing_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefEndTracingCallbackCToCpp::OnEndTracingComplete(
+    const CefString& tracing_file) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_end_tracing_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_end_tracing_complete))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: tracing_file; type: string_byref_const
+  DCHECK(!tracing_file.empty());
+  if (tracing_file.empty())
+    return;
+
+  // Execute
+  _struct->on_end_tracing_complete(_struct, tracing_file.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefEndTracingCallbackCToCpp::CefEndTracingCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefEndTracingCallbackCToCpp::~CefEndTracingCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_end_tracing_callback_t* CefCToCppRefCounted<
+    CefEndTracingCallbackCToCpp,
+    CefEndTracingCallback,
+    cef_end_tracing_callback_t>::UnwrapDerived(CefWrapperType type,
+                                               CefEndTracingCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefEndTracingCallbackCToCpp,
+                                   CefEndTracingCallback,
+                                   cef_end_tracing_callback_t>::kWrapperType =
+    WT_END_TRACING_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/end_tracing_callback_ctocpp.h b/src/libcef_dll/ctocpp/end_tracing_callback_ctocpp.h
new file mode 100644
index 0000000..39a622a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/end_tracing_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=78fcf71ee6c9b5eb813286bb6b9ab3474c6ee1b4$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_END_TRACING_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_END_TRACING_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_trace_capi.h"
+#include "include/cef_trace.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefEndTracingCallbackCToCpp
+    : public CefCToCppRefCounted<CefEndTracingCallbackCToCpp,
+                                 CefEndTracingCallback,
+                                 cef_end_tracing_callback_t> {
+ public:
+  CefEndTracingCallbackCToCpp();
+  virtual ~CefEndTracingCallbackCToCpp();
+
+  // CefEndTracingCallback methods.
+  void OnEndTracingComplete(const CefString& tracing_file) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_END_TRACING_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/extension_ctocpp.cc b/src/libcef_dll/ctocpp/extension_ctocpp.cc
new file mode 100644
index 0000000..ad70e9f
--- /dev/null
+++ b/src/libcef_dll/ctocpp/extension_ctocpp.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c276b1767d8f7930dd3be2c2409efda2159428b0$
+//
+
+#include "libcef_dll/ctocpp/extension_ctocpp.h"
+#include "libcef_dll/cpptoc/extension_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/request_context_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefString CefExtensionCToCpp::GetIdentifier() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_identifier))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_identifier(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefExtensionCToCpp::GetPath() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_path))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_path(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDictionaryValue> CefExtensionCToCpp::GetManifest() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_manifest))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dictionary_value_t* _retval = _struct->get_manifest(_struct);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefExtensionCToCpp::IsSame(CefRefPtr<CefExtension> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefExtensionCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefExtensionHandler> CefExtensionCToCpp::GetHandler() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_extension_handler_t* _retval = _struct->get_handler(_struct);
+
+  // Return type: refptr_diff
+  return CefExtensionHandlerCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequestContext> CefExtensionCToCpp::GetLoaderContext() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_loader_context))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_request_context_t* _retval = _struct->get_loader_context(_struct);
+
+  // Return type: refptr_same
+  return CefRequestContextCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefExtensionCToCpp::IsLoaded() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_loaded))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_loaded(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefExtensionCToCpp::Unload() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, unload))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->unload(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefExtensionCToCpp::CefExtensionCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefExtensionCToCpp::~CefExtensionCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_extension_t*
+CefCToCppRefCounted<CefExtensionCToCpp, CefExtension, cef_extension_t>::
+    UnwrapDerived(CefWrapperType type, CefExtension* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefExtensionCToCpp,
+                                   CefExtension,
+                                   cef_extension_t>::kWrapperType =
+    WT_EXTENSION;
diff --git a/src/libcef_dll/ctocpp/extension_ctocpp.h b/src/libcef_dll/ctocpp/extension_ctocpp.h
new file mode 100644
index 0000000..8f18906
--- /dev/null
+++ b/src/libcef_dll/ctocpp/extension_ctocpp.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3c31d21941156e368e6628298123a212591447cf$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_EXTENSION_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_EXTENSION_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_extension_capi.h"
+#include "include/capi/cef_extension_handler_capi.h"
+#include "include/capi/cef_request_context_capi.h"
+#include "include/cef_extension.h"
+#include "include/cef_extension_handler.h"
+#include "include/cef_request_context.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefExtensionCToCpp : public CefCToCppRefCounted<CefExtensionCToCpp,
+                                                      CefExtension,
+                                                      cef_extension_t> {
+ public:
+  CefExtensionCToCpp();
+  virtual ~CefExtensionCToCpp();
+
+  // CefExtension methods.
+  CefString GetIdentifier() OVERRIDE;
+  CefString GetPath() OVERRIDE;
+  CefRefPtr<CefDictionaryValue> GetManifest() OVERRIDE;
+  bool IsSame(CefRefPtr<CefExtension> that) OVERRIDE;
+  CefRefPtr<CefExtensionHandler> GetHandler() OVERRIDE;
+  CefRefPtr<CefRequestContext> GetLoaderContext() OVERRIDE;
+  bool IsLoaded() OVERRIDE;
+  void Unload() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_EXTENSION_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/extension_handler_ctocpp.cc b/src/libcef_dll/ctocpp/extension_handler_ctocpp.cc
new file mode 100644
index 0000000..c712339
--- /dev/null
+++ b/src/libcef_dll/ctocpp/extension_handler_ctocpp.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e2150893079724b7435ca143122b17096be5a8f5$
+//
+
+#include "libcef_dll/ctocpp/extension_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/extension_cpptoc.h"
+#include "libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.h"
+#include "libcef_dll/ctocpp/client_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefExtensionHandlerCToCpp::OnExtensionLoadFailed(cef_errorcode_t result) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_extension_load_failed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_extension_load_failed(_struct, result);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefExtensionHandlerCToCpp::OnExtensionLoaded(
+    CefRefPtr<CefExtension> extension) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_extension_loaded))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension.get());
+  if (!extension.get())
+    return;
+
+  // Execute
+  _struct->on_extension_loaded(_struct, CefExtensionCppToC::Wrap(extension));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefExtensionHandlerCToCpp::OnExtensionUnloaded(
+    CefRefPtr<CefExtension> extension) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_extension_unloaded))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension.get());
+  if (!extension.get())
+    return;
+
+  // Execute
+  _struct->on_extension_unloaded(_struct, CefExtensionCppToC::Wrap(extension));
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefExtensionHandlerCToCpp::OnBeforeBackgroundBrowser(
+    CefRefPtr<CefExtension> extension,
+    const CefString& url,
+    CefRefPtr<CefClient>& client,
+    CefBrowserSettings& settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_background_browser))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension.get());
+  if (!extension.get())
+    return false;
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return false;
+
+  // Translate param: client; type: refptr_same_byref
+  cef_client_t* clientStruct = NULL;
+  if (client.get())
+    clientStruct = CefClientCToCpp::Unwrap(client);
+  cef_client_t* clientOrig = clientStruct;
+
+  // Execute
+  int _retval = _struct->on_before_background_browser(
+      _struct, CefExtensionCppToC::Wrap(extension), url.GetStruct(),
+      &clientStruct, &settings);
+
+  // Restore param:client; type: refptr_same_byref
+  if (clientStruct) {
+    if (clientStruct != clientOrig) {
+      client = CefClientCToCpp::Wrap(clientStruct);
+    }
+  } else {
+    client = nullptr;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefExtensionHandlerCToCpp::OnBeforeBrowser(
+    CefRefPtr<CefExtension> extension,
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefBrowser> active_browser,
+    int index,
+    const CefString& url,
+    bool active,
+    CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient>& client,
+    CefBrowserSettings& settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_browser))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension.get());
+  if (!extension.get())
+    return false;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: active_browser; type: refptr_diff
+  DCHECK(active_browser.get());
+  if (!active_browser.get())
+    return false;
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return false;
+
+  // Translate param: client; type: refptr_same_byref
+  cef_client_t* clientStruct = NULL;
+  if (client.get())
+    clientStruct = CefClientCToCpp::Unwrap(client);
+  cef_client_t* clientOrig = clientStruct;
+
+  // Execute
+  int _retval = _struct->on_before_browser(
+      _struct, CefExtensionCppToC::Wrap(extension),
+      CefBrowserCppToC::Wrap(browser), CefBrowserCppToC::Wrap(active_browser),
+      index, url.GetStruct(), active, &windowInfo, &clientStruct, &settings);
+
+  // Restore param:client; type: refptr_same_byref
+  if (clientStruct) {
+    if (clientStruct != clientOrig) {
+      client = CefClientCToCpp::Wrap(clientStruct);
+    }
+  } else {
+    client = nullptr;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowser> CefExtensionHandlerCToCpp::GetActiveBrowser(
+    CefRefPtr<CefExtension> extension,
+    CefRefPtr<CefBrowser> browser,
+    bool include_incognito) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_active_browser))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension.get());
+  if (!extension.get())
+    return nullptr;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return nullptr;
+
+  // Execute
+  cef_browser_t* _retval = _struct->get_active_browser(
+      _struct, CefExtensionCppToC::Wrap(extension),
+      CefBrowserCppToC::Wrap(browser), include_incognito);
+
+  // Return type: refptr_diff
+  return CefBrowserCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefExtensionHandlerCToCpp::CanAccessBrowser(
+    CefRefPtr<CefExtension> extension,
+    CefRefPtr<CefBrowser> browser,
+    bool include_incognito,
+    CefRefPtr<CefBrowser> target_browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_access_browser))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension.get());
+  if (!extension.get())
+    return false;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: target_browser; type: refptr_diff
+  DCHECK(target_browser.get());
+  if (!target_browser.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->can_access_browser(
+      _struct, CefExtensionCppToC::Wrap(extension),
+      CefBrowserCppToC::Wrap(browser), include_incognito,
+      CefBrowserCppToC::Wrap(target_browser));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefExtensionHandlerCToCpp::GetExtensionResource(
+    CefRefPtr<CefExtension> extension,
+    CefRefPtr<CefBrowser> browser,
+    const CefString& file,
+    CefRefPtr<CefGetExtensionResourceCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_extension_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_extension_resource))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: refptr_diff
+  DCHECK(extension.get());
+  if (!extension.get())
+    return false;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: file; type: string_byref_const
+  DCHECK(!file.empty());
+  if (file.empty())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->get_extension_resource(
+      _struct, CefExtensionCppToC::Wrap(extension),
+      CefBrowserCppToC::Wrap(browser), file.GetStruct(),
+      CefGetExtensionResourceCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefExtensionHandlerCToCpp::CefExtensionHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefExtensionHandlerCToCpp::~CefExtensionHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_extension_handler_t* CefCToCppRefCounted<
+    CefExtensionHandlerCToCpp,
+    CefExtensionHandler,
+    cef_extension_handler_t>::UnwrapDerived(CefWrapperType type,
+                                            CefExtensionHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefExtensionHandlerCToCpp,
+                                   CefExtensionHandler,
+                                   cef_extension_handler_t>::kWrapperType =
+    WT_EXTENSION_HANDLER;
diff --git a/src/libcef_dll/ctocpp/extension_handler_ctocpp.h b/src/libcef_dll/ctocpp/extension_handler_ctocpp.h
new file mode 100644
index 0000000..dac16a7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/extension_handler_ctocpp.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fab935d5efc290c7dd7d663609ba152df6e1be81$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_EXTENSION_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_EXTENSION_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/capi/cef_extension_handler_capi.h"
+#include "include/cef_client.h"
+#include "include/cef_extension_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefExtensionHandlerCToCpp
+    : public CefCToCppRefCounted<CefExtensionHandlerCToCpp,
+                                 CefExtensionHandler,
+                                 cef_extension_handler_t> {
+ public:
+  CefExtensionHandlerCToCpp();
+  virtual ~CefExtensionHandlerCToCpp();
+
+  // CefExtensionHandler methods.
+  void OnExtensionLoadFailed(cef_errorcode_t result) override;
+  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override;
+  void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override;
+  bool OnBeforeBackgroundBrowser(CefRefPtr<CefExtension> extension,
+                                 const CefString& url,
+                                 CefRefPtr<CefClient>& client,
+                                 CefBrowserSettings& settings) override;
+  bool OnBeforeBrowser(CefRefPtr<CefExtension> extension,
+                       CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefBrowser> active_browser,
+                       int index,
+                       const CefString& url,
+                       bool active,
+                       CefWindowInfo& windowInfo,
+                       CefRefPtr<CefClient>& client,
+                       CefBrowserSettings& settings) override;
+  CefRefPtr<CefBrowser> GetActiveBrowser(CefRefPtr<CefExtension> extension,
+                                         CefRefPtr<CefBrowser> browser,
+                                         bool include_incognito) override;
+  bool CanAccessBrowser(CefRefPtr<CefExtension> extension,
+                        CefRefPtr<CefBrowser> browser,
+                        bool include_incognito,
+                        CefRefPtr<CefBrowser> target_browser) override;
+  bool GetExtensionResource(
+      CefRefPtr<CefExtension> extension,
+      CefRefPtr<CefBrowser> browser,
+      const CefString& file,
+      CefRefPtr<CefGetExtensionResourceCallback> callback) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_EXTENSION_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/file_dialog_callback_ctocpp.cc b/src/libcef_dll/ctocpp/file_dialog_callback_ctocpp.cc
new file mode 100644
index 0000000..53c49ba
--- /dev/null
+++ b/src/libcef_dll/ctocpp/file_dialog_callback_ctocpp.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=19a439c707a5ccfa865eabb3a2a3ec8150104ba5$
+//
+
+#include "libcef_dll/ctocpp/file_dialog_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefFileDialogCallbackCToCpp::Continue(
+    int selected_accept_filter,
+    const std::vector<CefString>& file_paths) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_file_dialog_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return;
+  // Unverified params: file_paths
+
+  // Translate param: file_paths; type: string_vec_byref_const
+  cef_string_list_t file_pathsList = cef_string_list_alloc();
+  DCHECK(file_pathsList);
+  if (file_pathsList)
+    transfer_string_list_contents(file_paths, file_pathsList);
+
+  // Execute
+  _struct->cont(_struct, selected_accept_filter, file_pathsList);
+
+  // Restore param:file_paths; type: string_vec_byref_const
+  if (file_pathsList)
+    cef_string_list_free(file_pathsList);
+}
+
+NO_SANITIZE("cfi-icall") void CefFileDialogCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_file_dialog_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFileDialogCallbackCToCpp::CefFileDialogCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFileDialogCallbackCToCpp::~CefFileDialogCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_file_dialog_callback_t* CefCToCppRefCounted<
+    CefFileDialogCallbackCToCpp,
+    CefFileDialogCallback,
+    cef_file_dialog_callback_t>::UnwrapDerived(CefWrapperType type,
+                                               CefFileDialogCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefFileDialogCallbackCToCpp,
+                                   CefFileDialogCallback,
+                                   cef_file_dialog_callback_t>::kWrapperType =
+    WT_FILE_DIALOG_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/file_dialog_callback_ctocpp.h b/src/libcef_dll/ctocpp/file_dialog_callback_ctocpp.h
new file mode 100644
index 0000000..1f1292e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/file_dialog_callback_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6dcb54aa208a5e974b7b0d7d8da229960cb3da55$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_FILE_DIALOG_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_FILE_DIALOG_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_dialog_handler_capi.h"
+#include "include/cef_dialog_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefFileDialogCallbackCToCpp
+    : public CefCToCppRefCounted<CefFileDialogCallbackCToCpp,
+                                 CefFileDialogCallback,
+                                 cef_file_dialog_callback_t> {
+ public:
+  CefFileDialogCallbackCToCpp();
+  virtual ~CefFileDialogCallbackCToCpp();
+
+  // CefFileDialogCallback methods.
+  void Continue(int selected_accept_filter,
+                const std::vector<CefString>& file_paths) OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_FILE_DIALOG_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/find_handler_ctocpp.cc b/src/libcef_dll/ctocpp/find_handler_ctocpp.cc
new file mode 100644
index 0000000..a35094d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/find_handler_ctocpp.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4899d7b77e91c0a5c46dad615f2e1b32f8077c40$
+//
+
+#include "libcef_dll/ctocpp/find_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefFindHandlerCToCpp::OnFindResult(CefRefPtr<CefBrowser> browser,
+                                        int identifier,
+                                        int count,
+                                        const CefRect& selectionRect,
+                                        int activeMatchOrdinal,
+                                        bool finalUpdate) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_find_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_find_result))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_find_result(_struct, CefBrowserCppToC::Wrap(browser), identifier,
+                          count, &selectionRect, activeMatchOrdinal,
+                          finalUpdate);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFindHandlerCToCpp::CefFindHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFindHandlerCToCpp::~CefFindHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_find_handler_t*
+CefCToCppRefCounted<CefFindHandlerCToCpp, CefFindHandler, cef_find_handler_t>::
+    UnwrapDerived(CefWrapperType type, CefFindHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefFindHandlerCToCpp,
+                                   CefFindHandler,
+                                   cef_find_handler_t>::kWrapperType =
+    WT_FIND_HANDLER;
diff --git a/src/libcef_dll/ctocpp/find_handler_ctocpp.h b/src/libcef_dll/ctocpp/find_handler_ctocpp.h
new file mode 100644
index 0000000..2668638
--- /dev/null
+++ b/src/libcef_dll/ctocpp/find_handler_ctocpp.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5af48b5e0fa05de0b6b7bc31e2f4b1b2fa4c045b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_FIND_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_FIND_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_find_handler_capi.h"
+#include "include/cef_find_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefFindHandlerCToCpp : public CefCToCppRefCounted<CefFindHandlerCToCpp,
+                                                        CefFindHandler,
+                                                        cef_find_handler_t> {
+ public:
+  CefFindHandlerCToCpp();
+  virtual ~CefFindHandlerCToCpp();
+
+  // CefFindHandler methods.
+  void OnFindResult(CefRefPtr<CefBrowser> browser,
+                    int identifier,
+                    int count,
+                    const CefRect& selectionRect,
+                    int activeMatchOrdinal,
+                    bool finalUpdate) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_FIND_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/focus_handler_ctocpp.cc b/src/libcef_dll/ctocpp/focus_handler_ctocpp.cc
new file mode 100644
index 0000000..4a9575b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/focus_handler_ctocpp.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=14a73b2c762047ce55f3e40837050b5f5e950bac$
+//
+
+#include "libcef_dll/ctocpp/focus_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefFocusHandlerCToCpp::OnTakeFocus(CefRefPtr<CefBrowser> browser,
+                                        bool next) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_focus_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_take_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_take_focus(_struct, CefBrowserCppToC::Wrap(browser), next);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefFocusHandlerCToCpp::OnSetFocus(CefRefPtr<CefBrowser> browser,
+                                       FocusSource source) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_focus_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_set_focus))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->on_set_focus(_struct, CefBrowserCppToC::Wrap(browser), source);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefFocusHandlerCToCpp::OnGotFocus(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_focus_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_got_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_got_focus(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFocusHandlerCToCpp::CefFocusHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFocusHandlerCToCpp::~CefFocusHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_focus_handler_t*
+CefCToCppRefCounted<CefFocusHandlerCToCpp,
+                    CefFocusHandler,
+                    cef_focus_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefFocusHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefFocusHandlerCToCpp,
+                                   CefFocusHandler,
+                                   cef_focus_handler_t>::kWrapperType =
+    WT_FOCUS_HANDLER;
diff --git a/src/libcef_dll/ctocpp/focus_handler_ctocpp.h b/src/libcef_dll/ctocpp/focus_handler_ctocpp.h
new file mode 100644
index 0000000..66b4a29
--- /dev/null
+++ b/src/libcef_dll/ctocpp/focus_handler_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=31490e9d66718e2b1763e8016a6b4d39a1ad2947$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_FOCUS_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_FOCUS_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_focus_handler_capi.h"
+#include "include/cef_focus_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefFocusHandlerCToCpp : public CefCToCppRefCounted<CefFocusHandlerCToCpp,
+                                                         CefFocusHandler,
+                                                         cef_focus_handler_t> {
+ public:
+  CefFocusHandlerCToCpp();
+  virtual ~CefFocusHandlerCToCpp();
+
+  // CefFocusHandler methods.
+  void OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) override;
+  bool OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source) override;
+  void OnGotFocus(CefRefPtr<CefBrowser> browser) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_FOCUS_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/frame_ctocpp.cc b/src/libcef_dll/ctocpp/frame_ctocpp.cc
new file mode 100644
index 0000000..d197647
--- /dev/null
+++ b/src/libcef_dll/ctocpp/frame_ctocpp.cc
@@ -0,0 +1,471 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=180295a757845728ac0b6b2fd778e7a16574ab56$
+//
+
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/cpptoc/domvisitor_cpptoc.h"
+#include "libcef_dll/cpptoc/string_visitor_cpptoc.h"
+#include "libcef_dll/cpptoc/urlrequest_client_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/process_message_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/urlrequest_ctocpp.h"
+#include "libcef_dll/ctocpp/v8context_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefFrameCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::Undo() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, undo))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->undo(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::Redo() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, redo))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->redo(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::Cut() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cut))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cut(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::Copy() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, copy))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->copy(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::Paste() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, paste))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->paste(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::Delete() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, del))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->del(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::SelectAll() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, select_all))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->select_all(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::ViewSource() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, view_source))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->view_source(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefFrameCToCpp::GetSource(CefRefPtr<CefStringVisitor> visitor) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_source))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return;
+
+  // Execute
+  _struct->get_source(_struct, CefStringVisitorCppToC::Wrap(visitor));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefFrameCToCpp::GetText(CefRefPtr<CefStringVisitor> visitor) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return;
+
+  // Execute
+  _struct->get_text(_struct, CefStringVisitorCppToC::Wrap(visitor));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefFrameCToCpp::LoadRequest(CefRefPtr<CefRequest> request) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, load_request))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_same
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+
+  // Execute
+  _struct->load_request(_struct, CefRequestCToCpp::Unwrap(request));
+}
+
+NO_SANITIZE("cfi-icall") void CefFrameCToCpp::LoadURL(const CefString& url) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, load_url))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return;
+
+  // Execute
+  _struct->load_url(_struct, url.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefFrameCToCpp::ExecuteJavaScript(const CefString& code,
+                                       const CefString& script_url,
+                                       int start_line) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute_java_script))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: code; type: string_byref_const
+  DCHECK(!code.empty());
+  if (code.empty())
+    return;
+  // Unverified params: script_url
+
+  // Execute
+  _struct->execute_java_script(_struct, code.GetStruct(),
+                               script_url.GetStruct(), start_line);
+}
+
+NO_SANITIZE("cfi-icall") bool CefFrameCToCpp::IsMain() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_main))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_main(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefFrameCToCpp::IsFocused() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_focused))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focused(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefFrameCToCpp::GetName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefFrameCToCpp::GetIdentifier() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_identifier))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->get_identifier(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefFrame> CefFrameCToCpp::GetParent() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_parent))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_frame_t* _retval = _struct->get_parent(_struct);
+
+  // Return type: refptr_same
+  return CefFrameCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefFrameCToCpp::GetURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefBrowser> CefFrameCToCpp::GetBrowser() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_browser))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_t* _retval = _struct->get_browser(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Context> CefFrameCToCpp::GetV8Context() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_v8context))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8context_t* _retval = _struct->get_v8context(_struct);
+
+  // Return type: refptr_same
+  return CefV8ContextCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefFrameCToCpp::VisitDOM(CefRefPtr<CefDOMVisitor> visitor) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit_dom))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return;
+
+  // Execute
+  _struct->visit_dom(_struct, CefDOMVisitorCppToC::Wrap(visitor));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefURLRequest> CefFrameCToCpp::CreateURLRequest(
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefURLRequestClient> client) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, create_urlrequest))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_same
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Verify param: client; type: refptr_diff
+  DCHECK(client.get());
+  if (!client.get())
+    return nullptr;
+
+  // Execute
+  cef_urlrequest_t* _retval =
+      _struct->create_urlrequest(_struct, CefRequestCToCpp::Unwrap(request),
+                                 CefURLRequestClientCppToC::Wrap(client));
+
+  // Return type: refptr_same
+  return CefURLRequestCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefFrameCToCpp::SendProcessMessage(CefProcessId target_process,
+                                        CefRefPtr<CefProcessMessage> message) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_process_message))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: message; type: refptr_same
+  DCHECK(message.get());
+  if (!message.get())
+    return;
+
+  // Execute
+  _struct->send_process_message(_struct, target_process,
+                                CefProcessMessageCToCpp::Unwrap(message));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFrameCToCpp::CefFrameCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFrameCToCpp::~CefFrameCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_frame_t*
+CefCToCppRefCounted<CefFrameCToCpp, CefFrame, cef_frame_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefFrame* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefFrameCToCpp, CefFrame, cef_frame_t>::kWrapperType =
+        WT_FRAME;
diff --git a/src/libcef_dll/ctocpp/frame_ctocpp.h b/src/libcef_dll/ctocpp/frame_ctocpp.h
new file mode 100644
index 0000000..a423b69
--- /dev/null
+++ b/src/libcef_dll/ctocpp/frame_ctocpp.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ddb09e01a1a6f2e928a2a3c414bef889adb7dc65$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_FRAME_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_FRAME_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_frame_capi.h"
+#include "include/capi/cef_urlrequest_capi.h"
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_urlrequest.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefFrameCToCpp
+    : public CefCToCppRefCounted<CefFrameCToCpp, CefFrame, cef_frame_t> {
+ public:
+  CefFrameCToCpp();
+  virtual ~CefFrameCToCpp();
+
+  // CefFrame methods.
+  bool IsValid() OVERRIDE;
+  void Undo() OVERRIDE;
+  void Redo() OVERRIDE;
+  void Cut() OVERRIDE;
+  void Copy() OVERRIDE;
+  void Paste() OVERRIDE;
+  void Delete() OVERRIDE;
+  void SelectAll() OVERRIDE;
+  void ViewSource() OVERRIDE;
+  void GetSource(CefRefPtr<CefStringVisitor> visitor) OVERRIDE;
+  void GetText(CefRefPtr<CefStringVisitor> visitor) OVERRIDE;
+  void LoadRequest(CefRefPtr<CefRequest> request) OVERRIDE;
+  void LoadURL(const CefString& url) OVERRIDE;
+  void ExecuteJavaScript(const CefString& code,
+                         const CefString& script_url,
+                         int start_line) OVERRIDE;
+  bool IsMain() OVERRIDE;
+  bool IsFocused() OVERRIDE;
+  CefString GetName() OVERRIDE;
+  int64 GetIdentifier() OVERRIDE;
+  CefRefPtr<CefFrame> GetParent() OVERRIDE;
+  CefString GetURL() OVERRIDE;
+  CefRefPtr<CefBrowser> GetBrowser() OVERRIDE;
+  CefRefPtr<CefV8Context> GetV8Context() OVERRIDE;
+  void VisitDOM(CefRefPtr<CefDOMVisitor> visitor) OVERRIDE;
+  CefRefPtr<CefURLRequest> CreateURLRequest(
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefURLRequestClient> client) OVERRIDE;
+  void SendProcessMessage(CefProcessId target_process,
+                          CefRefPtr<CefProcessMessage> message) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_FRAME_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.cc b/src/libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.cc
new file mode 100644
index 0000000..fa79122
--- /dev/null
+++ b/src/libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c36d12cf40153d3c86ee21e31a3b29a0f01146fc$
+//
+
+#include "libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/stream_reader_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefGetExtensionResourceCallbackCToCpp::Continue(
+    CefRefPtr<CefStreamReader> stream) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_get_extension_resource_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: stream
+
+  // Execute
+  _struct->cont(_struct, CefStreamReaderCToCpp::Unwrap(stream));
+}
+
+NO_SANITIZE("cfi-icall") void CefGetExtensionResourceCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_get_extension_resource_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefGetExtensionResourceCallbackCToCpp::CefGetExtensionResourceCallbackCToCpp() {
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefGetExtensionResourceCallbackCToCpp::
+    ~CefGetExtensionResourceCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_get_extension_resource_callback_t*
+CefCToCppRefCounted<CefGetExtensionResourceCallbackCToCpp,
+                    CefGetExtensionResourceCallback,
+                    cef_get_extension_resource_callback_t>::
+    UnwrapDerived(CefWrapperType type, CefGetExtensionResourceCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefGetExtensionResourceCallbackCToCpp,
+                        CefGetExtensionResourceCallback,
+                        cef_get_extension_resource_callback_t>::kWrapperType =
+        WT_GET_EXTENSION_RESOURCE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.h b/src/libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.h
new file mode 100644
index 0000000..7d07e0f
--- /dev/null
+++ b/src/libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7eea721db8dde8bc9d2b4d47b63b07ab41f91591$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_GET_EXTENSION_RESOURCE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_GET_EXTENSION_RESOURCE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/capi/cef_extension_handler_capi.h"
+#include "include/cef_client.h"
+#include "include/cef_extension_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefGetExtensionResourceCallbackCToCpp
+    : public CefCToCppRefCounted<CefGetExtensionResourceCallbackCToCpp,
+                                 CefGetExtensionResourceCallback,
+                                 cef_get_extension_resource_callback_t> {
+ public:
+  CefGetExtensionResourceCallbackCToCpp();
+  virtual ~CefGetExtensionResourceCallbackCToCpp();
+
+  // CefGetExtensionResourceCallback methods.
+  void Continue(CefRefPtr<CefStreamReader> stream) OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_GET_EXTENSION_RESOURCE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/image_ctocpp.cc b/src/libcef_dll/ctocpp/image_ctocpp.cc
new file mode 100644
index 0000000..c578170
--- /dev/null
+++ b/src/libcef_dll/ctocpp/image_ctocpp.cc
@@ -0,0 +1,327 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bf467f48bb553cc7308bce0fd74d3dcedaf8b8ad$
+//
+
+#include "libcef_dll/ctocpp/image_ctocpp.h"
+#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefImage> CefImage::CreateImage() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_image_t* _retval = cef_image_create();
+
+  // Return type: refptr_same
+  return CefImageCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefImageCToCpp::IsEmpty() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_empty))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_empty(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefImageCToCpp::IsSame(CefRefPtr<CefImage> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefImageCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefImageCToCpp::AddBitmap(float scale_factor,
+                               int pixel_width,
+                               int pixel_height,
+                               cef_color_type_t color_type,
+                               cef_alpha_type_t alpha_type,
+                               const void* pixel_data,
+                               size_t pixel_data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_bitmap))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: pixel_data; type: simple_byaddr
+  DCHECK(pixel_data);
+  if (!pixel_data)
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->add_bitmap(_struct, scale_factor, pixel_width, pixel_height,
+                          color_type, alpha_type, pixel_data, pixel_data_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefImageCToCpp::AddPNG(float scale_factor,
+                            const void* png_data,
+                            size_t png_data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_png))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: png_data; type: simple_byaddr
+  DCHECK(png_data);
+  if (!png_data)
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->add_png(_struct, scale_factor, png_data, png_data_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefImageCToCpp::AddJPEG(float scale_factor,
+                             const void* jpeg_data,
+                             size_t jpeg_data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_jpeg))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: jpeg_data; type: simple_byaddr
+  DCHECK(jpeg_data);
+  if (!jpeg_data)
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->add_jpeg(_struct, scale_factor, jpeg_data, jpeg_data_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefImageCToCpp::GetWidth() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_width(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefImageCToCpp::GetHeight() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_height))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_height(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefImageCToCpp::HasRepresentation(float scale_factor) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_representation))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_representation(_struct, scale_factor);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefImageCToCpp::RemoveRepresentation(float scale_factor) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_representation))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->remove_representation(_struct, scale_factor);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefImageCToCpp::GetRepresentationInfo(float scale_factor,
+                                           float& actual_scale_factor,
+                                           int& pixel_width,
+                                           int& pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_representation_info))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_representation_info(
+      _struct, scale_factor, &actual_scale_factor, &pixel_width, &pixel_height);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefImageCToCpp::GetAsBitmap(
+    float scale_factor,
+    cef_color_type_t color_type,
+    cef_alpha_type_t alpha_type,
+    int& pixel_width,
+    int& pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_as_bitmap))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval =
+      _struct->get_as_bitmap(_struct, scale_factor, color_type, alpha_type,
+                             &pixel_width, &pixel_height);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefImageCToCpp::GetAsPNG(float scale_factor,
+                                                   bool with_transparency,
+                                                   int& pixel_width,
+                                                   int& pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_as_png))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_as_png(
+      _struct, scale_factor, with_transparency, &pixel_width, &pixel_height);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefImageCToCpp::GetAsJPEG(float scale_factor,
+                                                    int quality,
+                                                    int& pixel_width,
+                                                    int& pixel_height) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_image_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_as_jpeg))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_as_jpeg(
+      _struct, scale_factor, quality, &pixel_width, &pixel_height);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefImageCToCpp::CefImageCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefImageCToCpp::~CefImageCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_image_t*
+CefCToCppRefCounted<CefImageCToCpp, CefImage, cef_image_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefImage* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefImageCToCpp, CefImage, cef_image_t>::kWrapperType =
+        WT_IMAGE;
diff --git a/src/libcef_dll/ctocpp/image_ctocpp.h b/src/libcef_dll/ctocpp/image_ctocpp.h
new file mode 100644
index 0000000..c2e3586
--- /dev/null
+++ b/src/libcef_dll/ctocpp/image_ctocpp.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=63b29975a77c78b9961af9eb88ddc67dd79e32a4$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_IMAGE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_IMAGE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_image_capi.h"
+#include "include/cef_image.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefImageCToCpp
+    : public CefCToCppRefCounted<CefImageCToCpp, CefImage, cef_image_t> {
+ public:
+  CefImageCToCpp();
+  virtual ~CefImageCToCpp();
+
+  // CefImage methods.
+  bool IsEmpty() OVERRIDE;
+  bool IsSame(CefRefPtr<CefImage> that) OVERRIDE;
+  bool AddBitmap(float scale_factor,
+                 int pixel_width,
+                 int pixel_height,
+                 cef_color_type_t color_type,
+                 cef_alpha_type_t alpha_type,
+                 const void* pixel_data,
+                 size_t pixel_data_size) OVERRIDE;
+  bool AddPNG(float scale_factor,
+              const void* png_data,
+              size_t png_data_size) OVERRIDE;
+  bool AddJPEG(float scale_factor,
+               const void* jpeg_data,
+               size_t jpeg_data_size) OVERRIDE;
+  size_t GetWidth() OVERRIDE;
+  size_t GetHeight() OVERRIDE;
+  bool HasRepresentation(float scale_factor) OVERRIDE;
+  bool RemoveRepresentation(float scale_factor) OVERRIDE;
+  bool GetRepresentationInfo(float scale_factor,
+                             float& actual_scale_factor,
+                             int& pixel_width,
+                             int& pixel_height) OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetAsBitmap(float scale_factor,
+                                        cef_color_type_t color_type,
+                                        cef_alpha_type_t alpha_type,
+                                        int& pixel_width,
+                                        int& pixel_height) OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetAsPNG(float scale_factor,
+                                     bool with_transparency,
+                                     int& pixel_width,
+                                     int& pixel_height) OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetAsJPEG(float scale_factor,
+                                      int quality,
+                                      int& pixel_width,
+                                      int& pixel_height) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_IMAGE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/jsdialog_callback_ctocpp.cc b/src/libcef_dll/ctocpp/jsdialog_callback_ctocpp.cc
new file mode 100644
index 0000000..2eef937
--- /dev/null
+++ b/src/libcef_dll/ctocpp/jsdialog_callback_ctocpp.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=07dbe8f9680d767646cafcdaa5e790003d059cf5$
+//
+
+#include "libcef_dll/ctocpp/jsdialog_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefJSDialogCallbackCToCpp::Continue(bool success,
+                                         const CefString& user_input) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_jsdialog_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: user_input
+
+  // Execute
+  _struct->cont(_struct, success, user_input.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefJSDialogCallbackCToCpp::CefJSDialogCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefJSDialogCallbackCToCpp::~CefJSDialogCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_jsdialog_callback_t* CefCToCppRefCounted<
+    CefJSDialogCallbackCToCpp,
+    CefJSDialogCallback,
+    cef_jsdialog_callback_t>::UnwrapDerived(CefWrapperType type,
+                                            CefJSDialogCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefJSDialogCallbackCToCpp,
+                                   CefJSDialogCallback,
+                                   cef_jsdialog_callback_t>::kWrapperType =
+    WT_JSDIALOG_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/jsdialog_callback_ctocpp.h b/src/libcef_dll/ctocpp/jsdialog_callback_ctocpp.h
new file mode 100644
index 0000000..b08bf7e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/jsdialog_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8fd698821e3a1a621a19a0e650d5c31eb90013fb$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_JSDIALOG_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_JSDIALOG_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_jsdialog_handler_capi.h"
+#include "include/cef_jsdialog_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefJSDialogCallbackCToCpp
+    : public CefCToCppRefCounted<CefJSDialogCallbackCToCpp,
+                                 CefJSDialogCallback,
+                                 cef_jsdialog_callback_t> {
+ public:
+  CefJSDialogCallbackCToCpp();
+  virtual ~CefJSDialogCallbackCToCpp();
+
+  // CefJSDialogCallback methods.
+  void Continue(bool success, const CefString& user_input) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_JSDIALOG_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/jsdialog_handler_ctocpp.cc b/src/libcef_dll/ctocpp/jsdialog_handler_ctocpp.cc
new file mode 100644
index 0000000..6d28bd1
--- /dev/null
+++ b/src/libcef_dll/ctocpp/jsdialog_handler_ctocpp.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c3d58fe1274c19d04eb2ef801747b1f2012b86ce$
+//
+
+#include "libcef_dll/ctocpp/jsdialog_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/jsdialog_callback_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefJSDialogHandlerCToCpp::OnJSDialog(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& origin_url,
+    JSDialogType dialog_type,
+    const CefString& message_text,
+    const CefString& default_prompt_text,
+    CefRefPtr<CefJSDialogCallback> callback,
+    bool& suppress_message) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_jsdialog_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_jsdialog))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+  // Unverified params: origin_url, message_text, default_prompt_text
+
+  // Translate param: suppress_message; type: bool_byref
+  int suppress_messageInt = suppress_message;
+
+  // Execute
+  int _retval = _struct->on_jsdialog(
+      _struct, CefBrowserCppToC::Wrap(browser), origin_url.GetStruct(),
+      dialog_type, message_text.GetStruct(), default_prompt_text.GetStruct(),
+      CefJSDialogCallbackCppToC::Wrap(callback), &suppress_messageInt);
+
+  // Restore param:suppress_message; type: bool_byref
+  suppress_message = suppress_messageInt ? true : false;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefJSDialogHandlerCToCpp::OnBeforeUnloadDialog(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& message_text,
+    bool is_reload,
+    CefRefPtr<CefJSDialogCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_jsdialog_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_unload_dialog))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+  // Unverified params: message_text
+
+  // Execute
+  int _retval = _struct->on_before_unload_dialog(
+      _struct, CefBrowserCppToC::Wrap(browser), message_text.GetStruct(),
+      is_reload, CefJSDialogCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefJSDialogHandlerCToCpp::OnResetDialogState(
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_jsdialog_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_reset_dialog_state))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_reset_dialog_state(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefJSDialogHandlerCToCpp::OnDialogClosed(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_jsdialog_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_dialog_closed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_dialog_closed(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefJSDialogHandlerCToCpp::CefJSDialogHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefJSDialogHandlerCToCpp::~CefJSDialogHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_jsdialog_handler_t* CefCToCppRefCounted<
+    CefJSDialogHandlerCToCpp,
+    CefJSDialogHandler,
+    cef_jsdialog_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           CefJSDialogHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefJSDialogHandlerCToCpp,
+                                   CefJSDialogHandler,
+                                   cef_jsdialog_handler_t>::kWrapperType =
+    WT_JSDIALOG_HANDLER;
diff --git a/src/libcef_dll/ctocpp/jsdialog_handler_ctocpp.h b/src/libcef_dll/ctocpp/jsdialog_handler_ctocpp.h
new file mode 100644
index 0000000..2707864
--- /dev/null
+++ b/src/libcef_dll/ctocpp/jsdialog_handler_ctocpp.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b24b389259c2ddfb2881760280a94759f4d3b636$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_JSDIALOG_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_JSDIALOG_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_jsdialog_handler_capi.h"
+#include "include/cef_jsdialog_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefJSDialogHandlerCToCpp
+    : public CefCToCppRefCounted<CefJSDialogHandlerCToCpp,
+                                 CefJSDialogHandler,
+                                 cef_jsdialog_handler_t> {
+ public:
+  CefJSDialogHandlerCToCpp();
+  virtual ~CefJSDialogHandlerCToCpp();
+
+  // CefJSDialogHandler methods.
+  bool OnJSDialog(CefRefPtr<CefBrowser> browser,
+                  const CefString& origin_url,
+                  JSDialogType dialog_type,
+                  const CefString& message_text,
+                  const CefString& default_prompt_text,
+                  CefRefPtr<CefJSDialogCallback> callback,
+                  bool& suppress_message) override;
+  bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
+                            const CefString& message_text,
+                            bool is_reload,
+                            CefRefPtr<CefJSDialogCallback> callback) override;
+  void OnResetDialogState(CefRefPtr<CefBrowser> browser) override;
+  void OnDialogClosed(CefRefPtr<CefBrowser> browser) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_JSDIALOG_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/keyboard_handler_ctocpp.cc b/src/libcef_dll/ctocpp/keyboard_handler_ctocpp.cc
new file mode 100644
index 0000000..b81a87b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/keyboard_handler_ctocpp.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=853cd02f42c843f486d936058a0f012d6099834c$
+//
+
+#include "libcef_dll/ctocpp/keyboard_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefKeyboardHandlerCToCpp::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
+                                             const CefKeyEvent& event,
+                                             CefEventHandle os_event,
+                                             bool* is_keyboard_shortcut) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_keyboard_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_pre_key_event))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: is_keyboard_shortcut; type: bool_byaddr
+  DCHECK(is_keyboard_shortcut);
+  if (!is_keyboard_shortcut)
+    return false;
+
+  // Translate param: is_keyboard_shortcut; type: bool_byaddr
+  int is_keyboard_shortcutInt =
+      is_keyboard_shortcut ? *is_keyboard_shortcut : 0;
+
+  // Execute
+  int _retval =
+      _struct->on_pre_key_event(_struct, CefBrowserCppToC::Wrap(browser),
+                                &event, os_event, &is_keyboard_shortcutInt);
+
+  // Restore param:is_keyboard_shortcut; type: bool_byaddr
+  if (is_keyboard_shortcut)
+    *is_keyboard_shortcut = is_keyboard_shortcutInt ? true : false;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefKeyboardHandlerCToCpp::OnKeyEvent(CefRefPtr<CefBrowser> browser,
+                                          const CefKeyEvent& event,
+                                          CefEventHandle os_event) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_keyboard_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_key_event))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_key_event(_struct, CefBrowserCppToC::Wrap(browser),
+                                      &event, os_event);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefKeyboardHandlerCToCpp::CefKeyboardHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefKeyboardHandlerCToCpp::~CefKeyboardHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_keyboard_handler_t* CefCToCppRefCounted<
+    CefKeyboardHandlerCToCpp,
+    CefKeyboardHandler,
+    cef_keyboard_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           CefKeyboardHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefKeyboardHandlerCToCpp,
+                                   CefKeyboardHandler,
+                                   cef_keyboard_handler_t>::kWrapperType =
+    WT_KEYBOARD_HANDLER;
diff --git a/src/libcef_dll/ctocpp/keyboard_handler_ctocpp.h b/src/libcef_dll/ctocpp/keyboard_handler_ctocpp.h
new file mode 100644
index 0000000..c5cfae6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/keyboard_handler_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=813bbfd94f49935fcce61d26ed9351b6e89f2a58$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_KEYBOARD_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_KEYBOARD_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_keyboard_handler_capi.h"
+#include "include/cef_keyboard_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefKeyboardHandlerCToCpp
+    : public CefCToCppRefCounted<CefKeyboardHandlerCToCpp,
+                                 CefKeyboardHandler,
+                                 cef_keyboard_handler_t> {
+ public:
+  CefKeyboardHandlerCToCpp();
+  virtual ~CefKeyboardHandlerCToCpp();
+
+  // CefKeyboardHandler methods.
+  bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
+                     const CefKeyEvent& event,
+                     CefEventHandle os_event,
+                     bool* is_keyboard_shortcut) override;
+  bool OnKeyEvent(CefRefPtr<CefBrowser> browser,
+                  const CefKeyEvent& event,
+                  CefEventHandle os_event) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_KEYBOARD_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/life_span_handler_ctocpp.cc b/src/libcef_dll/ctocpp/life_span_handler_ctocpp.cc
new file mode 100644
index 0000000..4afdd9b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/life_span_handler_ctocpp.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=243cd9194d5b67b3ba35ae8b3f7c58588c0ccebb$
+//
+
+#include "libcef_dll/ctocpp/life_span_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/ctocpp/client_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefLifeSpanHandlerCToCpp::OnBeforePopup(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    const CefString& target_url,
+    const CefString& target_frame_name,
+    WindowOpenDisposition target_disposition,
+    bool user_gesture,
+    const CefPopupFeatures& popupFeatures,
+    CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient>& client,
+    CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue>& extra_info,
+    bool* no_javascript_access) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_life_span_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_popup))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return false;
+  // Verify param: no_javascript_access; type: bool_byaddr
+  DCHECK(no_javascript_access);
+  if (!no_javascript_access)
+    return false;
+  // Unverified params: target_url, target_frame_name
+
+  // Translate param: client; type: refptr_same_byref
+  cef_client_t* clientStruct = NULL;
+  if (client.get())
+    clientStruct = CefClientCToCpp::Unwrap(client);
+  cef_client_t* clientOrig = clientStruct;
+  // Translate param: extra_info; type: refptr_diff_byref
+  cef_dictionary_value_t* extra_infoStruct = NULL;
+  if (extra_info.get())
+    extra_infoStruct = CefDictionaryValueCppToC::Wrap(extra_info);
+  cef_dictionary_value_t* extra_infoOrig = extra_infoStruct;
+  // Translate param: no_javascript_access; type: bool_byaddr
+  int no_javascript_accessInt =
+      no_javascript_access ? *no_javascript_access : 0;
+
+  // Execute
+  int _retval = _struct->on_before_popup(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      target_url.GetStruct(), target_frame_name.GetStruct(), target_disposition,
+      user_gesture, &popupFeatures, &windowInfo, &clientStruct, &settings,
+      &extra_infoStruct, &no_javascript_accessInt);
+
+  // Restore param:client; type: refptr_same_byref
+  if (clientStruct) {
+    if (clientStruct != clientOrig) {
+      client = CefClientCToCpp::Wrap(clientStruct);
+    }
+  } else {
+    client = nullptr;
+  }
+  // Restore param:extra_info; type: refptr_diff_byref
+  if (extra_infoStruct) {
+    if (extra_infoStruct != extra_infoOrig) {
+      extra_info = CefDictionaryValueCppToC::Unwrap(extra_infoStruct);
+    }
+  } else {
+    extra_info = nullptr;
+  }
+  // Restore param:no_javascript_access; type: bool_byaddr
+  if (no_javascript_access)
+    *no_javascript_access = no_javascript_accessInt ? true : false;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLifeSpanHandlerCToCpp::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_life_span_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_after_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_after_created(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLifeSpanHandlerCToCpp::DoClose(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_life_span_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, do_close))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->do_close(_struct, CefBrowserCppToC::Wrap(browser));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLifeSpanHandlerCToCpp::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_life_span_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_close))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_before_close(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLifeSpanHandlerCToCpp::CefLifeSpanHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLifeSpanHandlerCToCpp::~CefLifeSpanHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_life_span_handler_t* CefCToCppRefCounted<
+    CefLifeSpanHandlerCToCpp,
+    CefLifeSpanHandler,
+    cef_life_span_handler_t>::UnwrapDerived(CefWrapperType type,
+                                            CefLifeSpanHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefLifeSpanHandlerCToCpp,
+                                   CefLifeSpanHandler,
+                                   cef_life_span_handler_t>::kWrapperType =
+    WT_LIFE_SPAN_HANDLER;
diff --git a/src/libcef_dll/ctocpp/life_span_handler_ctocpp.h b/src/libcef_dll/ctocpp/life_span_handler_ctocpp.h
new file mode 100644
index 0000000..dcf1e18
--- /dev/null
+++ b/src/libcef_dll/ctocpp/life_span_handler_ctocpp.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2163444626efca350f6377da7fbce6e2d2291ca4$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_LIFE_SPAN_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_LIFE_SPAN_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_client_capi.h"
+#include "include/capi/cef_life_span_handler_capi.h"
+#include "include/cef_client.h"
+#include "include/cef_life_span_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefLifeSpanHandlerCToCpp
+    : public CefCToCppRefCounted<CefLifeSpanHandlerCToCpp,
+                                 CefLifeSpanHandler,
+                                 cef_life_span_handler_t> {
+ public:
+  CefLifeSpanHandlerCToCpp();
+  virtual ~CefLifeSpanHandlerCToCpp();
+
+  // CefLifeSpanHandler methods.
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     WindowOpenDisposition target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override;
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
+  bool DoClose(CefRefPtr<CefBrowser> browser) override;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_LIFE_SPAN_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/list_value_ctocpp.cc b/src/libcef_dll/ctocpp/list_value_ctocpp.cc
new file mode 100644
index 0000000..1c3e9d1
--- /dev/null
+++ b/src/libcef_dll/ctocpp/list_value_ctocpp.cc
@@ -0,0 +1,562 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b4d3794c5760e7d21e00accfc8e3c260e5bd3e1d$
+//
+
+#include "libcef_dll/ctocpp/list_value_ctocpp.h"
+#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefListValue> CefListValue::Create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_list_value_t* _retval = cef_list_value_create();
+
+  // Return type: refptr_same
+  return CefListValueCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::IsOwned() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_owned))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_owned(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::IsReadOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::IsSame(CefRefPtr<CefListValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefListValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::IsEqual(CefRefPtr<CefListValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_equal))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_equal(_struct, CefListValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefListValue> CefListValueCToCpp::Copy() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, copy))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_list_value_t* _retval = _struct->copy(_struct);
+
+  // Return type: refptr_same
+  return CefListValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::SetSize(size_t size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_size(_struct, size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefListValueCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::Clear() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->clear(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::Remove(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->remove(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefValueType CefListValueCToCpp::GetType(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return VTYPE_INVALID;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_value_type_t _retval = _struct->get_type(_struct, index);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefValue> CefListValueCToCpp::GetValue(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_value_t* _retval = _struct->get_value(_struct, index);
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::GetBool(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_bool(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefListValueCToCpp::GetInt(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_int))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_int(_struct, index);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") double CefListValueCToCpp::GetDouble(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_double))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  double _retval = _struct->get_double(_struct, index);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefListValueCToCpp::GetString(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_string(_struct, index);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefListValueCToCpp::GetBinary(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_binary))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_binary(_struct, index);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDictionaryValue> CefListValueCToCpp::GetDictionary(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_dictionary))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dictionary_value_t* _retval = _struct->get_dictionary(_struct, index);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefListValue> CefListValueCToCpp::GetList(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_list))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_list_value_t* _retval = _struct->get_list(_struct, index);
+
+  // Return type: refptr_same
+  return CefListValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetValue(size_t index, CefRefPtr<CefValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->set_value(_struct, index, CefValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefListValueCToCpp::SetNull(size_t index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_null))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_null(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetBool(size_t index, bool value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_bool(_struct, index, value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetInt(size_t index, int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_int))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_int(_struct, index, value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetDouble(size_t index, double value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_double))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_double(_struct, index, value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetString(size_t index, const CefString& value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_string))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: value
+
+  // Execute
+  int _retval = _struct->set_string(_struct, index, value.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetBinary(size_t index,
+                                   CefRefPtr<CefBinaryValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_binary))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->set_binary(_struct, index, CefBinaryValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetDictionary(size_t index,
+                                       CefRefPtr<CefDictionaryValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_dictionary))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_dictionary(
+      _struct, index, CefDictionaryValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefListValueCToCpp::SetList(size_t index, CefRefPtr<CefListValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_list_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->set_list(_struct, index, CefListValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefListValueCToCpp::CefListValueCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefListValueCToCpp::~CefListValueCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_list_value_t*
+CefCToCppRefCounted<CefListValueCToCpp, CefListValue, cef_list_value_t>::
+    UnwrapDerived(CefWrapperType type, CefListValue* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefListValueCToCpp,
+                                   CefListValue,
+                                   cef_list_value_t>::kWrapperType =
+    WT_LIST_VALUE;
diff --git a/src/libcef_dll/ctocpp/list_value_ctocpp.h b/src/libcef_dll/ctocpp/list_value_ctocpp.h
new file mode 100644
index 0000000..e505aa3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/list_value_ctocpp.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4e5f157e3be26a5111877cfd083be887bfc0de45$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_LIST_VALUE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_LIST_VALUE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefListValueCToCpp : public CefCToCppRefCounted<CefListValueCToCpp,
+                                                      CefListValue,
+                                                      cef_list_value_t> {
+ public:
+  CefListValueCToCpp();
+  virtual ~CefListValueCToCpp();
+
+  // CefListValue methods.
+  bool IsValid() OVERRIDE;
+  bool IsOwned() OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  bool IsSame(CefRefPtr<CefListValue> that) OVERRIDE;
+  bool IsEqual(CefRefPtr<CefListValue> that) OVERRIDE;
+  CefRefPtr<CefListValue> Copy() OVERRIDE;
+  bool SetSize(size_t size) OVERRIDE;
+  size_t GetSize() OVERRIDE;
+  bool Clear() OVERRIDE;
+  bool Remove(size_t index) OVERRIDE;
+  CefValueType GetType(size_t index) OVERRIDE;
+  CefRefPtr<CefValue> GetValue(size_t index) OVERRIDE;
+  bool GetBool(size_t index) OVERRIDE;
+  int GetInt(size_t index) OVERRIDE;
+  double GetDouble(size_t index) OVERRIDE;
+  CefString GetString(size_t index) OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetBinary(size_t index) OVERRIDE;
+  CefRefPtr<CefDictionaryValue> GetDictionary(size_t index) OVERRIDE;
+  CefRefPtr<CefListValue> GetList(size_t index) OVERRIDE;
+  bool SetValue(size_t index, CefRefPtr<CefValue> value) OVERRIDE;
+  bool SetNull(size_t index) OVERRIDE;
+  bool SetBool(size_t index, bool value) OVERRIDE;
+  bool SetInt(size_t index, int value) OVERRIDE;
+  bool SetDouble(size_t index, double value) OVERRIDE;
+  bool SetString(size_t index, const CefString& value) OVERRIDE;
+  bool SetBinary(size_t index, CefRefPtr<CefBinaryValue> value) OVERRIDE;
+  bool SetDictionary(size_t index,
+                     CefRefPtr<CefDictionaryValue> value) OVERRIDE;
+  bool SetList(size_t index, CefRefPtr<CefListValue> value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_LIST_VALUE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/load_handler_ctocpp.cc b/src/libcef_dll/ctocpp/load_handler_ctocpp.cc
new file mode 100644
index 0000000..f96f9d5
--- /dev/null
+++ b/src/libcef_dll/ctocpp/load_handler_ctocpp.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=07a706c04c5b002b645d0c898249343d7e7ae442$
+//
+
+#include "libcef_dll/ctocpp/load_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefLoadHandlerCToCpp::OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                                                bool isLoading,
+                                                bool canGoBack,
+                                                bool canGoForward) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_load_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_loading_state_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_loading_state_change(_struct, CefBrowserCppToC::Wrap(browser),
+                                   isLoading, canGoBack, canGoForward);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLoadHandlerCToCpp::OnLoadStart(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       TransitionType transition_type) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_load_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_load_start))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+
+  // Execute
+  _struct->on_load_start(_struct, CefBrowserCppToC::Wrap(browser),
+                         CefFrameCppToC::Wrap(frame), transition_type);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLoadHandlerCToCpp::OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                                     CefRefPtr<CefFrame> frame,
+                                     int httpStatusCode) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_load_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_load_end))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+
+  // Execute
+  _struct->on_load_end(_struct, CefBrowserCppToC::Wrap(browser),
+                       CefFrameCppToC::Wrap(frame), httpStatusCode);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLoadHandlerCToCpp::OnLoadError(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       ErrorCode errorCode,
+                                       const CefString& errorText,
+                                       const CefString& failedUrl) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_load_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_load_error))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+  // Verify param: failedUrl; type: string_byref_const
+  DCHECK(!failedUrl.empty());
+  if (failedUrl.empty())
+    return;
+  // Unverified params: errorText
+
+  // Execute
+  _struct->on_load_error(_struct, CefBrowserCppToC::Wrap(browser),
+                         CefFrameCppToC::Wrap(frame), errorCode,
+                         errorText.GetStruct(), failedUrl.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLoadHandlerCToCpp::CefLoadHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLoadHandlerCToCpp::~CefLoadHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_load_handler_t*
+CefCToCppRefCounted<CefLoadHandlerCToCpp, CefLoadHandler, cef_load_handler_t>::
+    UnwrapDerived(CefWrapperType type, CefLoadHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefLoadHandlerCToCpp,
+                                   CefLoadHandler,
+                                   cef_load_handler_t>::kWrapperType =
+    WT_LOAD_HANDLER;
diff --git a/src/libcef_dll/ctocpp/load_handler_ctocpp.h b/src/libcef_dll/ctocpp/load_handler_ctocpp.h
new file mode 100644
index 0000000..6f6aa2e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/load_handler_ctocpp.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=22460d6f937eddf1ec07dcea2325bba205101d47$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_LOAD_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_LOAD_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_load_handler_capi.h"
+#include "include/cef_load_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefLoadHandlerCToCpp : public CefCToCppRefCounted<CefLoadHandlerCToCpp,
+                                                        CefLoadHandler,
+                                                        cef_load_handler_t> {
+ public:
+  CefLoadHandlerCToCpp();
+  virtual ~CefLoadHandlerCToCpp();
+
+  // CefLoadHandler methods.
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override;
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override;
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override;
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_LOAD_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/media_observer_ctocpp.cc b/src/libcef_dll/ctocpp/media_observer_ctocpp.cc
new file mode 100644
index 0000000..1d43b8d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_observer_ctocpp.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6eed652a4bd13cf685980f7ea0b838d73e6071ba$
+//
+
+#include "libcef_dll/ctocpp/media_observer_ctocpp.h"
+#include "libcef_dll/cpptoc/media_route_cpptoc.h"
+#include "libcef_dll/cpptoc/media_sink_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefMediaObserverCToCpp::OnSinks(
+    const std::vector<CefRefPtr<CefMediaSink>>& sinks) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_sinks))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: sinks; type: refptr_vec_diff_byref_const
+  const size_t sinksCount = sinks.size();
+  cef_media_sink_t** sinksList = NULL;
+  if (sinksCount > 0) {
+    sinksList = new cef_media_sink_t*[sinksCount];
+    DCHECK(sinksList);
+    if (sinksList) {
+      for (size_t i = 0; i < sinksCount; ++i) {
+        sinksList[i] = CefMediaSinkCppToC::Wrap(sinks[i]);
+      }
+    }
+  }
+
+  // Execute
+  _struct->on_sinks(_struct, sinksCount, sinksList);
+
+  // Restore param:sinks; type: refptr_vec_diff_byref_const
+  if (sinksList)
+    delete[] sinksList;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMediaObserverCToCpp::OnRoutes(
+    const std::vector<CefRefPtr<CefMediaRoute>>& routes) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_routes))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: routes; type: refptr_vec_diff_byref_const
+  const size_t routesCount = routes.size();
+  cef_media_route_t** routesList = NULL;
+  if (routesCount > 0) {
+    routesList = new cef_media_route_t*[routesCount];
+    DCHECK(routesList);
+    if (routesList) {
+      for (size_t i = 0; i < routesCount; ++i) {
+        routesList[i] = CefMediaRouteCppToC::Wrap(routes[i]);
+      }
+    }
+  }
+
+  // Execute
+  _struct->on_routes(_struct, routesCount, routesList);
+
+  // Restore param:routes; type: refptr_vec_diff_byref_const
+  if (routesList)
+    delete[] routesList;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMediaObserverCToCpp::OnRouteStateChanged(CefRefPtr<CefMediaRoute> route,
+                                                 ConnectionState state) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_route_state_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: route; type: refptr_diff
+  DCHECK(route.get());
+  if (!route.get())
+    return;
+
+  // Execute
+  _struct->on_route_state_changed(_struct, CefMediaRouteCppToC::Wrap(route),
+                                  state);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMediaObserverCToCpp::OnRouteMessageReceived(
+    CefRefPtr<CefMediaRoute> route,
+    const void* message,
+    size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_observer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_route_message_received))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: route; type: refptr_diff
+  DCHECK(route.get());
+  if (!route.get())
+    return;
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return;
+
+  // Execute
+  _struct->on_route_message_received(_struct, CefMediaRouteCppToC::Wrap(route),
+                                     message, message_size);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaObserverCToCpp::CefMediaObserverCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaObserverCToCpp::~CefMediaObserverCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_media_observer_t*
+CefCToCppRefCounted<CefMediaObserverCToCpp,
+                    CefMediaObserver,
+                    cef_media_observer_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefMediaObserver* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMediaObserverCToCpp,
+                                   CefMediaObserver,
+                                   cef_media_observer_t>::kWrapperType =
+    WT_MEDIA_OBSERVER;
diff --git a/src/libcef_dll/ctocpp/media_observer_ctocpp.h b/src/libcef_dll/ctocpp/media_observer_ctocpp.h
new file mode 100644
index 0000000..e314c49
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_observer_ctocpp.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4c2e71870e5f8a31052431a5242f91392607502d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_OBSERVER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_OBSERVER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefMediaObserverCToCpp
+    : public CefCToCppRefCounted<CefMediaObserverCToCpp,
+                                 CefMediaObserver,
+                                 cef_media_observer_t> {
+ public:
+  CefMediaObserverCToCpp();
+  virtual ~CefMediaObserverCToCpp();
+
+  // CefMediaObserver methods.
+  void OnSinks(const std::vector<CefRefPtr<CefMediaSink>>& sinks) override;
+  void OnRoutes(const std::vector<CefRefPtr<CefMediaRoute>>& routes) override;
+  void OnRouteStateChanged(CefRefPtr<CefMediaRoute> route,
+                           ConnectionState state) override;
+  void OnRouteMessageReceived(CefRefPtr<CefMediaRoute> route,
+                              const void* message,
+                              size_t message_size) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MEDIA_OBSERVER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/media_route_create_callback_ctocpp.cc b/src/libcef_dll/ctocpp/media_route_create_callback_ctocpp.cc
new file mode 100644
index 0000000..22c00e5
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_route_create_callback_ctocpp.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=93dd08de00931b1c00ec0307cc70496455936b0c$
+//
+
+#include "libcef_dll/ctocpp/media_route_create_callback_ctocpp.h"
+#include "libcef_dll/cpptoc/media_route_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefMediaRouteCreateCallbackCToCpp::OnMediaRouteCreateFinished(
+    RouteCreateResult result,
+    const CefString& error,
+    CefRefPtr<CefMediaRoute> route) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_route_create_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_media_route_create_finished))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: error, route
+
+  // Execute
+  _struct->on_media_route_create_finished(_struct, result, error.GetStruct(),
+                                          CefMediaRouteCppToC::Wrap(route));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCreateCallbackCToCpp::CefMediaRouteCreateCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCreateCallbackCToCpp::~CefMediaRouteCreateCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_media_route_create_callback_t*
+CefCToCppRefCounted<CefMediaRouteCreateCallbackCToCpp,
+                    CefMediaRouteCreateCallback,
+                    cef_media_route_create_callback_t>::
+    UnwrapDerived(CefWrapperType type, CefMediaRouteCreateCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefMediaRouteCreateCallbackCToCpp,
+                        CefMediaRouteCreateCallback,
+                        cef_media_route_create_callback_t>::kWrapperType =
+        WT_MEDIA_ROUTE_CREATE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/media_route_create_callback_ctocpp.h b/src/libcef_dll/ctocpp/media_route_create_callback_ctocpp.h
new file mode 100644
index 0000000..0d92b94
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_route_create_callback_ctocpp.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ac5657bccd7830fde1d3dddfad69d87dff2ae684$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTE_CREATE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTE_CREATE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefMediaRouteCreateCallbackCToCpp
+    : public CefCToCppRefCounted<CefMediaRouteCreateCallbackCToCpp,
+                                 CefMediaRouteCreateCallback,
+                                 cef_media_route_create_callback_t> {
+ public:
+  CefMediaRouteCreateCallbackCToCpp();
+  virtual ~CefMediaRouteCreateCallbackCToCpp();
+
+  // CefMediaRouteCreateCallback methods.
+  void OnMediaRouteCreateFinished(RouteCreateResult result,
+                                  const CefString& error,
+                                  CefRefPtr<CefMediaRoute> route) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTE_CREATE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/media_route_ctocpp.cc b/src/libcef_dll/ctocpp/media_route_ctocpp.cc
new file mode 100644
index 0000000..b9f17fa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_route_ctocpp.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7fe34c4b79d10a88bfcf5ec46a54cf52d3657e87$
+//
+
+#include "libcef_dll/ctocpp/media_route_ctocpp.h"
+#include "libcef_dll/ctocpp/media_sink_ctocpp.h"
+#include "libcef_dll/ctocpp/media_source_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefString CefMediaRouteCToCpp::GetId() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_route_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_id(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMediaSource> CefMediaRouteCToCpp::GetSource() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_route_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_source))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_media_source_t* _retval = _struct->get_source(_struct);
+
+  // Return type: refptr_same
+  return CefMediaSourceCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMediaSink> CefMediaRouteCToCpp::GetSink() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_route_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_sink))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_media_sink_t* _retval = _struct->get_sink(_struct);
+
+  // Return type: refptr_same
+  return CefMediaSinkCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMediaRouteCToCpp::SendRouteMessage(const void* message,
+                                           size_t message_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_route_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_route_message))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: message; type: simple_byaddr
+  DCHECK(message);
+  if (!message)
+    return;
+
+  // Execute
+  _struct->send_route_message(_struct, message, message_size);
+}
+
+NO_SANITIZE("cfi-icall") void CefMediaRouteCToCpp::Terminate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_route_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, terminate))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->terminate(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCToCpp::CefMediaRouteCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaRouteCToCpp::~CefMediaRouteCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_media_route_t*
+CefCToCppRefCounted<CefMediaRouteCToCpp, CefMediaRoute, cef_media_route_t>::
+    UnwrapDerived(CefWrapperType type, CefMediaRoute* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMediaRouteCToCpp,
+                                   CefMediaRoute,
+                                   cef_media_route_t>::kWrapperType =
+    WT_MEDIA_ROUTE;
diff --git a/src/libcef_dll/ctocpp/media_route_ctocpp.h b/src/libcef_dll/ctocpp/media_route_ctocpp.h
new file mode 100644
index 0000000..c1f68b6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_route_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cb792f909646d1ccd3644877ce2393080465471a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMediaRouteCToCpp : public CefCToCppRefCounted<CefMediaRouteCToCpp,
+                                                       CefMediaRoute,
+                                                       cef_media_route_t> {
+ public:
+  CefMediaRouteCToCpp();
+  virtual ~CefMediaRouteCToCpp();
+
+  // CefMediaRoute methods.
+  CefString GetId() OVERRIDE;
+  CefRefPtr<CefMediaSource> GetSource() OVERRIDE;
+  CefRefPtr<CefMediaSink> GetSink() OVERRIDE;
+  void SendRouteMessage(const void* message, size_t message_size) OVERRIDE;
+  void Terminate() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/media_router_ctocpp.cc b/src/libcef_dll/ctocpp/media_router_ctocpp.cc
new file mode 100644
index 0000000..d90f7de
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_router_ctocpp.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4b8ab6373fcb0a84a818c2c5ef8982600346f34b$
+//
+
+#include "libcef_dll/ctocpp/media_router_ctocpp.h"
+#include "libcef_dll/cpptoc/media_observer_cpptoc.h"
+#include "libcef_dll/cpptoc/media_route_create_callback_cpptoc.h"
+#include "libcef_dll/ctocpp/media_sink_ctocpp.h"
+#include "libcef_dll/ctocpp/media_source_ctocpp.h"
+#include "libcef_dll/ctocpp/registration_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMediaRouter> CefMediaRouter::GetGlobalMediaRouter() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_media_router_t* _retval = cef_media_router_get_global();
+
+  // Return type: refptr_same
+  return CefMediaRouterCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRegistration> CefMediaRouterCToCpp::AddObserver(
+    CefRefPtr<CefMediaObserver> observer) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_router_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_observer))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: observer; type: refptr_diff
+  DCHECK(observer.get());
+  if (!observer.get())
+    return nullptr;
+
+  // Execute
+  cef_registration_t* _retval =
+      _struct->add_observer(_struct, CefMediaObserverCppToC::Wrap(observer));
+
+  // Return type: refptr_same
+  return CefRegistrationCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMediaSource> CefMediaRouterCToCpp::GetSource(
+    const CefString& urn) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_router_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_source))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: urn; type: string_byref_const
+  DCHECK(!urn.empty());
+  if (urn.empty())
+    return nullptr;
+
+  // Execute
+  cef_media_source_t* _retval = _struct->get_source(_struct, urn.GetStruct());
+
+  // Return type: refptr_same
+  return CefMediaSourceCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") void CefMediaRouterCToCpp::NotifyCurrentSinks() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_router_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, notify_current_sinks))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->notify_current_sinks(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMediaRouterCToCpp::CreateRoute(
+    CefRefPtr<CefMediaSource> source,
+    CefRefPtr<CefMediaSink> sink,
+    CefRefPtr<CefMediaRouteCreateCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_router_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, create_route))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: source; type: refptr_same
+  DCHECK(source.get());
+  if (!source.get())
+    return;
+  // Verify param: sink; type: refptr_same
+  DCHECK(sink.get());
+  if (!sink.get())
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  _struct->create_route(_struct, CefMediaSourceCToCpp::Unwrap(source),
+                        CefMediaSinkCToCpp::Unwrap(sink),
+                        CefMediaRouteCreateCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall") void CefMediaRouterCToCpp::NotifyCurrentRoutes() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_router_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, notify_current_routes))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->notify_current_routes(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaRouterCToCpp::CefMediaRouterCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaRouterCToCpp::~CefMediaRouterCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_media_router_t*
+CefCToCppRefCounted<CefMediaRouterCToCpp, CefMediaRouter, cef_media_router_t>::
+    UnwrapDerived(CefWrapperType type, CefMediaRouter* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMediaRouterCToCpp,
+                                   CefMediaRouter,
+                                   cef_media_router_t>::kWrapperType =
+    WT_MEDIA_ROUTER;
diff --git a/src/libcef_dll/ctocpp/media_router_ctocpp.h b/src/libcef_dll/ctocpp/media_router_ctocpp.h
new file mode 100644
index 0000000..7a022bb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_router_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e3d62de09a699415a839cadda52cca2582d90063$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMediaRouterCToCpp : public CefCToCppRefCounted<CefMediaRouterCToCpp,
+                                                        CefMediaRouter,
+                                                        cef_media_router_t> {
+ public:
+  CefMediaRouterCToCpp();
+  virtual ~CefMediaRouterCToCpp();
+
+  // CefMediaRouter methods.
+  CefRefPtr<CefRegistration> AddObserver(
+      CefRefPtr<CefMediaObserver> observer) OVERRIDE;
+  CefRefPtr<CefMediaSource> GetSource(const CefString& urn) OVERRIDE;
+  void NotifyCurrentSinks() OVERRIDE;
+  void CreateRoute(CefRefPtr<CefMediaSource> source,
+                   CefRefPtr<CefMediaSink> sink,
+                   CefRefPtr<CefMediaRouteCreateCallback> callback) OVERRIDE;
+  void NotifyCurrentRoutes() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MEDIA_ROUTER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/media_sink_ctocpp.cc b/src/libcef_dll/ctocpp/media_sink_ctocpp.cc
new file mode 100644
index 0000000..402dcde
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_sink_ctocpp.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8aa27e34aea83e91b04d21764f3ab985b04bbf9b$
+//
+
+#include "libcef_dll/ctocpp/media_sink_ctocpp.h"
+#include "libcef_dll/cpptoc/media_sink_device_info_callback_cpptoc.h"
+#include "libcef_dll/ctocpp/media_source_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefString CefMediaSinkCToCpp::GetId() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_id(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMediaSinkCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefMediaSinkCToCpp::GetName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefMediaSinkCToCpp::GetDescription() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_description))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_description(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefMediaSink::IconType CefMediaSinkCToCpp::GetIconType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_icon_type))
+    return CEF_MSIT_GENERIC;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_media_sink_icon_type_t _retval = _struct->get_icon_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMediaSinkCToCpp::GetDeviceInfo(
+    CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_device_info))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  _struct->get_device_info(
+      _struct, CefMediaSinkDeviceInfoCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall") bool CefMediaSinkCToCpp::IsCastSink() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_cast_sink))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_cast_sink(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMediaSinkCToCpp::IsDialSink() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_dial_sink))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_dial_sink(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMediaSinkCToCpp::IsCompatibleWith(CefRefPtr<CefMediaSource> source) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_compatible_with))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: source; type: refptr_same
+  DCHECK(source.get());
+  if (!source.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_compatible_with(
+      _struct, CefMediaSourceCToCpp::Unwrap(source));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaSinkCToCpp::CefMediaSinkCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaSinkCToCpp::~CefMediaSinkCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_media_sink_t*
+CefCToCppRefCounted<CefMediaSinkCToCpp, CefMediaSink, cef_media_sink_t>::
+    UnwrapDerived(CefWrapperType type, CefMediaSink* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMediaSinkCToCpp,
+                                   CefMediaSink,
+                                   cef_media_sink_t>::kWrapperType =
+    WT_MEDIA_SINK;
diff --git a/src/libcef_dll/ctocpp/media_sink_ctocpp.h b/src/libcef_dll/ctocpp/media_sink_ctocpp.h
new file mode 100644
index 0000000..4370410
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_sink_ctocpp.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=81437a10eeb422cf962af49b782275dd24bea500$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_SINK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_SINK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMediaSinkCToCpp : public CefCToCppRefCounted<CefMediaSinkCToCpp,
+                                                      CefMediaSink,
+                                                      cef_media_sink_t> {
+ public:
+  CefMediaSinkCToCpp();
+  virtual ~CefMediaSinkCToCpp();
+
+  // CefMediaSink methods.
+  CefString GetId() OVERRIDE;
+  bool IsValid() OVERRIDE;
+  CefString GetName() OVERRIDE;
+  CefString GetDescription() OVERRIDE;
+  IconType GetIconType() OVERRIDE;
+  void GetDeviceInfo(
+      CefRefPtr<CefMediaSinkDeviceInfoCallback> callback) OVERRIDE;
+  bool IsCastSink() OVERRIDE;
+  bool IsDialSink() OVERRIDE;
+  bool IsCompatibleWith(CefRefPtr<CefMediaSource> source) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MEDIA_SINK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.cc b/src/libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.cc
new file mode 100644
index 0000000..fdd5822
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c56362ea79609562dacc4bf8303f2bd920ddeada$
+//
+
+#include "libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefMediaSinkDeviceInfoCallbackCToCpp::OnMediaSinkDeviceInfo(
+    const CefMediaSinkDeviceInfo& device_info) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_sink_device_info_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_media_sink_device_info))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_media_sink_device_info(_struct, &device_info);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaSinkDeviceInfoCallbackCToCpp::CefMediaSinkDeviceInfoCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaSinkDeviceInfoCallbackCToCpp::~CefMediaSinkDeviceInfoCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_media_sink_device_info_callback_t*
+CefCToCppRefCounted<CefMediaSinkDeviceInfoCallbackCToCpp,
+                    CefMediaSinkDeviceInfoCallback,
+                    cef_media_sink_device_info_callback_t>::
+    UnwrapDerived(CefWrapperType type, CefMediaSinkDeviceInfoCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefMediaSinkDeviceInfoCallbackCToCpp,
+                        CefMediaSinkDeviceInfoCallback,
+                        cef_media_sink_device_info_callback_t>::kWrapperType =
+        WT_MEDIA_SINK_DEVICE_INFO_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.h b/src/libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.h
new file mode 100644
index 0000000..c5cfef8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_sink_device_info_callback_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e48d0b8482168d355e04460ea6ea5b7662dbe4fd$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_SINK_DEVICE_INFO_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_SINK_DEVICE_INFO_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefMediaSinkDeviceInfoCallbackCToCpp
+    : public CefCToCppRefCounted<CefMediaSinkDeviceInfoCallbackCToCpp,
+                                 CefMediaSinkDeviceInfoCallback,
+                                 cef_media_sink_device_info_callback_t> {
+ public:
+  CefMediaSinkDeviceInfoCallbackCToCpp();
+  virtual ~CefMediaSinkDeviceInfoCallbackCToCpp();
+
+  // CefMediaSinkDeviceInfoCallback methods.
+  void OnMediaSinkDeviceInfo(
+      const CefMediaSinkDeviceInfo& device_info) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MEDIA_SINK_DEVICE_INFO_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/media_source_ctocpp.cc b/src/libcef_dll/ctocpp/media_source_ctocpp.cc
new file mode 100644
index 0000000..25acc58
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_source_ctocpp.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2b4f20db4eccd1429c748f39db19852cd1644b4a$
+//
+
+#include "libcef_dll/ctocpp/media_source_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefString CefMediaSourceCToCpp::GetId() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_source_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_id(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMediaSourceCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_source_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMediaSourceCToCpp::IsCastSource() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_source_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_cast_source))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_cast_source(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMediaSourceCToCpp::IsDialSource() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_media_source_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_dial_source))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_dial_source(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMediaSourceCToCpp::CefMediaSourceCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMediaSourceCToCpp::~CefMediaSourceCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_media_source_t*
+CefCToCppRefCounted<CefMediaSourceCToCpp, CefMediaSource, cef_media_source_t>::
+    UnwrapDerived(CefWrapperType type, CefMediaSource* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMediaSourceCToCpp,
+                                   CefMediaSource,
+                                   cef_media_source_t>::kWrapperType =
+    WT_MEDIA_SOURCE;
diff --git a/src/libcef_dll/ctocpp/media_source_ctocpp.h b/src/libcef_dll/ctocpp/media_source_ctocpp.h
new file mode 100644
index 0000000..fd54e19
--- /dev/null
+++ b/src/libcef_dll/ctocpp/media_source_ctocpp.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=73aeb0f58611c0d7a9fa13f1d9268912e0fba0bd$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_SOURCE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_SOURCE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_media_router_capi.h"
+#include "include/cef_media_router.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMediaSourceCToCpp : public CefCToCppRefCounted<CefMediaSourceCToCpp,
+                                                        CefMediaSource,
+                                                        cef_media_source_t> {
+ public:
+  CefMediaSourceCToCpp();
+  virtual ~CefMediaSourceCToCpp();
+
+  // CefMediaSource methods.
+  CefString GetId() OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsCastSource() OVERRIDE;
+  bool IsDialSource() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MEDIA_SOURCE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/menu_model_ctocpp.cc b/src/libcef_dll/ctocpp/menu_model_ctocpp.cc
new file mode 100644
index 0000000..a280bac
--- /dev/null
+++ b/src/libcef_dll/ctocpp/menu_model_ctocpp.cc
@@ -0,0 +1,1136 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fb562ef19ca1a0c2094548aae3f6a213d08480f8$
+//
+
+#include "libcef_dll/ctocpp/menu_model_ctocpp.h"
+#include "libcef_dll/cpptoc/menu_model_delegate_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuModel> CefMenuModel::CreateMenuModel(
+    CefRefPtr<CefMenuModelDelegate> delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: delegate; type: refptr_diff
+  DCHECK(delegate.get());
+  if (!delegate.get())
+    return nullptr;
+
+  // Execute
+  cef_menu_model_t* _retval =
+      cef_menu_model_create(CefMenuModelDelegateCppToC::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefMenuModelCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::IsSubMenu() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_sub_menu))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_sub_menu(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::Clear() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->clear(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuModelCToCpp::GetCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::AddSeparator() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_separator))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->add_separator(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::AddItem(int command_id, const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_item))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->add_item(_struct, command_id, label.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::AddCheckItem(int command_id, const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_check_item))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->add_check_item(_struct, command_id, label.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::AddRadioItem(int command_id,
+                                      const CefString& label,
+                                      int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_radio_item))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->add_radio_item(_struct, command_id, label.GetStruct(), group_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuModel> CefMenuModelCToCpp::AddSubMenu(int command_id,
+                                                       const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_sub_menu))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return nullptr;
+
+  // Execute
+  cef_menu_model_t* _retval =
+      _struct->add_sub_menu(_struct, command_id, label.GetStruct());
+
+  // Return type: refptr_same
+  return CefMenuModelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::InsertSeparatorAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, insert_separator_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->insert_separator_at(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::InsertItemAt(int index,
+                                      int command_id,
+                                      const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, insert_item_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->insert_item_at(_struct, index, command_id, label.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::InsertCheckItemAt(int index,
+                                           int command_id,
+                                           const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, insert_check_item_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->insert_check_item_at(_struct, index, command_id,
+                                              label.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::InsertRadioItemAt(int index,
+                                           int command_id,
+                                           const CefString& label,
+                                           int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, insert_radio_item_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->insert_radio_item_at(_struct, index, command_id,
+                                              label.GetStruct(), group_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuModel> CefMenuModelCToCpp::InsertSubMenuAt(
+    int index,
+    int command_id,
+    const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, insert_sub_menu_at))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return nullptr;
+
+  // Execute
+  cef_menu_model_t* _retval = _struct->insert_sub_menu_at(
+      _struct, index, command_id, label.GetStruct());
+
+  // Return type: refptr_same
+  return CefMenuModelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::Remove(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->remove(_struct, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::RemoveAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->remove_at(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuModelCToCpp::GetIndexOf(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_index_of))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_index_of(_struct, command_id);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuModelCToCpp::GetCommandIdAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_command_id_at))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_command_id_at(_struct, index);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetCommandIdAt(int index, int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_command_id_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_command_id_at(_struct, index, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefMenuModelCToCpp::GetLabel(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_label))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_label(_struct, command_id);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefMenuModelCToCpp::GetLabelAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_label_at))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_label_at(_struct, index);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetLabel(int command_id, const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_label))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_label(_struct, command_id, label.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetLabelAt(int index, const CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_label_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: label; type: string_byref_const
+  DCHECK(!label.empty());
+  if (label.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_label_at(_struct, index, label.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefMenuModel::MenuItemType CefMenuModelCToCpp::GetType(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return MENUITEMTYPE_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_menu_item_type_t _retval = _struct->get_type(_struct, command_id);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefMenuModel::MenuItemType CefMenuModelCToCpp::GetTypeAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type_at))
+    return MENUITEMTYPE_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_menu_item_type_t _retval = _struct->get_type_at(_struct, index);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuModelCToCpp::GetGroupId(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct, command_id);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuModelCToCpp::GetGroupIdAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_group_id_at))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id_at(_struct, index);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetGroupId(int command_id, int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_group_id(_struct, command_id, group_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetGroupIdAt(int index, int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_group_id_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_group_id_at(_struct, index, group_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuModel> CefMenuModelCToCpp::GetSubMenu(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_sub_menu))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_menu_model_t* _retval = _struct->get_sub_menu(_struct, command_id);
+
+  // Return type: refptr_same
+  return CefMenuModelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuModel> CefMenuModelCToCpp::GetSubMenuAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_sub_menu_at))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_menu_model_t* _retval = _struct->get_sub_menu_at(_struct, index);
+
+  // Return type: refptr_same
+  return CefMenuModelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::IsVisible(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::IsVisibleAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_visible_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible_at(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetVisible(int command_id, bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_visible(_struct, command_id, visible);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetVisibleAt(int index, bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_visible_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_visible_at(_struct, index, visible);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::IsEnabled(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::IsEnabledAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_enabled_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled_at(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetEnabled(int command_id, bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_enabled(_struct, command_id, enabled);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetEnabledAt(int index, bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_enabled_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_enabled_at(_struct, index, enabled);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::IsChecked(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_checked))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_checked(_struct, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::IsCheckedAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_checked_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_checked_at(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetChecked(int command_id, bool checked) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_checked))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_checked(_struct, command_id, checked);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetCheckedAt(int index, bool checked) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_checked_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_checked_at(_struct, index, checked);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::HasAccelerator(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_accelerator))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_accelerator(_struct, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuModelCToCpp::HasAcceleratorAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_accelerator_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_accelerator_at(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetAccelerator(int command_id,
+                                        int key_code,
+                                        bool shift_pressed,
+                                        bool ctrl_pressed,
+                                        bool alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_accelerator))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_accelerator(
+      _struct, command_id, key_code, shift_pressed, ctrl_pressed, alt_pressed);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetAcceleratorAt(int index,
+                                          int key_code,
+                                          bool shift_pressed,
+                                          bool ctrl_pressed,
+                                          bool alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_accelerator_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_accelerator_at(
+      _struct, index, key_code, shift_pressed, ctrl_pressed, alt_pressed);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::RemoveAccelerator(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_accelerator))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->remove_accelerator(_struct, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::RemoveAcceleratorAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_accelerator_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->remove_accelerator_at(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::GetAccelerator(int command_id,
+                                        int& key_code,
+                                        bool& shift_pressed,
+                                        bool& ctrl_pressed,
+                                        bool& alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_accelerator))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: shift_pressed; type: bool_byref
+  int shift_pressedInt = shift_pressed;
+  // Translate param: ctrl_pressed; type: bool_byref
+  int ctrl_pressedInt = ctrl_pressed;
+  // Translate param: alt_pressed; type: bool_byref
+  int alt_pressedInt = alt_pressed;
+
+  // Execute
+  int _retval = _struct->get_accelerator(_struct, command_id, &key_code,
+                                         &shift_pressedInt, &ctrl_pressedInt,
+                                         &alt_pressedInt);
+
+  // Restore param:shift_pressed; type: bool_byref
+  shift_pressed = shift_pressedInt ? true : false;
+  // Restore param:ctrl_pressed; type: bool_byref
+  ctrl_pressed = ctrl_pressedInt ? true : false;
+  // Restore param:alt_pressed; type: bool_byref
+  alt_pressed = alt_pressedInt ? true : false;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::GetAcceleratorAt(int index,
+                                          int& key_code,
+                                          bool& shift_pressed,
+                                          bool& ctrl_pressed,
+                                          bool& alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_accelerator_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: shift_pressed; type: bool_byref
+  int shift_pressedInt = shift_pressed;
+  // Translate param: ctrl_pressed; type: bool_byref
+  int ctrl_pressedInt = ctrl_pressed;
+  // Translate param: alt_pressed; type: bool_byref
+  int alt_pressedInt = alt_pressed;
+
+  // Execute
+  int _retval =
+      _struct->get_accelerator_at(_struct, index, &key_code, &shift_pressedInt,
+                                  &ctrl_pressedInt, &alt_pressedInt);
+
+  // Restore param:shift_pressed; type: bool_byref
+  shift_pressed = shift_pressedInt ? true : false;
+  // Restore param:ctrl_pressed; type: bool_byref
+  ctrl_pressed = ctrl_pressedInt ? true : false;
+  // Restore param:alt_pressed; type: bool_byref
+  alt_pressed = alt_pressedInt ? true : false;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetColor(int command_id,
+                                  cef_menu_color_type_t color_type,
+                                  cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_color))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_color(_struct, command_id, color_type, color);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetColorAt(int index,
+                                    cef_menu_color_type_t color_type,
+                                    cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_color_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_color_at(_struct, index, color_type, color);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::GetColor(int command_id,
+                                  cef_menu_color_type_t color_type,
+                                  cef_color_t& color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_color))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_color(_struct, command_id, color_type, &color);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::GetColorAt(int index,
+                                    cef_menu_color_type_t color_type,
+                                    cef_color_t& color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_color_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_color_at(_struct, index, color_type, &color);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetFontList(int command_id,
+                                     const CefString& font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_font_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: font_list
+
+  // Execute
+  int _retval =
+      _struct->set_font_list(_struct, command_id, font_list.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelCToCpp::SetFontListAt(int index, const CefString& font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_font_list_at))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: font_list
+
+  // Execute
+  int _retval =
+      _struct->set_font_list_at(_struct, index, font_list.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuModelCToCpp::CefMenuModelCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuModelCToCpp::~CefMenuModelCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_menu_model_t*
+CefCToCppRefCounted<CefMenuModelCToCpp, CefMenuModel, cef_menu_model_t>::
+    UnwrapDerived(CefWrapperType type, CefMenuModel* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMenuModelCToCpp,
+                                   CefMenuModel,
+                                   cef_menu_model_t>::kWrapperType =
+    WT_MENU_MODEL;
diff --git a/src/libcef_dll/ctocpp/menu_model_ctocpp.h b/src/libcef_dll/ctocpp/menu_model_ctocpp.h
new file mode 100644
index 0000000..4081530
--- /dev/null
+++ b/src/libcef_dll/ctocpp/menu_model_ctocpp.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=81606be28dd5d07eaab14a00570a6fd5fed7d8b8$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MENU_MODEL_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MENU_MODEL_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_menu_model_capi.h"
+#include "include/cef_menu_model.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMenuModelCToCpp : public CefCToCppRefCounted<CefMenuModelCToCpp,
+                                                      CefMenuModel,
+                                                      cef_menu_model_t> {
+ public:
+  CefMenuModelCToCpp();
+  virtual ~CefMenuModelCToCpp();
+
+  // CefMenuModel methods.
+  bool IsSubMenu() OVERRIDE;
+  bool Clear() OVERRIDE;
+  int GetCount() OVERRIDE;
+  bool AddSeparator() OVERRIDE;
+  bool AddItem(int command_id, const CefString& label) OVERRIDE;
+  bool AddCheckItem(int command_id, const CefString& label) OVERRIDE;
+  bool AddRadioItem(int command_id,
+                    const CefString& label,
+                    int group_id) OVERRIDE;
+  CefRefPtr<CefMenuModel> AddSubMenu(int command_id,
+                                     const CefString& label) OVERRIDE;
+  bool InsertSeparatorAt(int index) OVERRIDE;
+  bool InsertItemAt(int index, int command_id, const CefString& label) OVERRIDE;
+  bool InsertCheckItemAt(int index,
+                         int command_id,
+                         const CefString& label) OVERRIDE;
+  bool InsertRadioItemAt(int index,
+                         int command_id,
+                         const CefString& label,
+                         int group_id) OVERRIDE;
+  CefRefPtr<CefMenuModel> InsertSubMenuAt(int index,
+                                          int command_id,
+                                          const CefString& label) OVERRIDE;
+  bool Remove(int command_id) OVERRIDE;
+  bool RemoveAt(int index) OVERRIDE;
+  int GetIndexOf(int command_id) OVERRIDE;
+  int GetCommandIdAt(int index) OVERRIDE;
+  bool SetCommandIdAt(int index, int command_id) OVERRIDE;
+  CefString GetLabel(int command_id) OVERRIDE;
+  CefString GetLabelAt(int index) OVERRIDE;
+  bool SetLabel(int command_id, const CefString& label) OVERRIDE;
+  bool SetLabelAt(int index, const CefString& label) OVERRIDE;
+  MenuItemType GetType(int command_id) OVERRIDE;
+  MenuItemType GetTypeAt(int index) OVERRIDE;
+  int GetGroupId(int command_id) OVERRIDE;
+  int GetGroupIdAt(int index) OVERRIDE;
+  bool SetGroupId(int command_id, int group_id) OVERRIDE;
+  bool SetGroupIdAt(int index, int group_id) OVERRIDE;
+  CefRefPtr<CefMenuModel> GetSubMenu(int command_id) OVERRIDE;
+  CefRefPtr<CefMenuModel> GetSubMenuAt(int index) OVERRIDE;
+  bool IsVisible(int command_id) OVERRIDE;
+  bool IsVisibleAt(int index) OVERRIDE;
+  bool SetVisible(int command_id, bool visible) OVERRIDE;
+  bool SetVisibleAt(int index, bool visible) OVERRIDE;
+  bool IsEnabled(int command_id) OVERRIDE;
+  bool IsEnabledAt(int index) OVERRIDE;
+  bool SetEnabled(int command_id, bool enabled) OVERRIDE;
+  bool SetEnabledAt(int index, bool enabled) OVERRIDE;
+  bool IsChecked(int command_id) OVERRIDE;
+  bool IsCheckedAt(int index) OVERRIDE;
+  bool SetChecked(int command_id, bool checked) OVERRIDE;
+  bool SetCheckedAt(int index, bool checked) OVERRIDE;
+  bool HasAccelerator(int command_id) OVERRIDE;
+  bool HasAcceleratorAt(int index) OVERRIDE;
+  bool SetAccelerator(int command_id,
+                      int key_code,
+                      bool shift_pressed,
+                      bool ctrl_pressed,
+                      bool alt_pressed) OVERRIDE;
+  bool SetAcceleratorAt(int index,
+                        int key_code,
+                        bool shift_pressed,
+                        bool ctrl_pressed,
+                        bool alt_pressed) OVERRIDE;
+  bool RemoveAccelerator(int command_id) OVERRIDE;
+  bool RemoveAcceleratorAt(int index) OVERRIDE;
+  bool GetAccelerator(int command_id,
+                      int& key_code,
+                      bool& shift_pressed,
+                      bool& ctrl_pressed,
+                      bool& alt_pressed) OVERRIDE;
+  bool GetAcceleratorAt(int index,
+                        int& key_code,
+                        bool& shift_pressed,
+                        bool& ctrl_pressed,
+                        bool& alt_pressed) OVERRIDE;
+  bool SetColor(int command_id,
+                cef_menu_color_type_t color_type,
+                cef_color_t color) OVERRIDE;
+  bool SetColorAt(int index,
+                  cef_menu_color_type_t color_type,
+                  cef_color_t color) OVERRIDE;
+  bool GetColor(int command_id,
+                cef_menu_color_type_t color_type,
+                cef_color_t& color) OVERRIDE;
+  bool GetColorAt(int index,
+                  cef_menu_color_type_t color_type,
+                  cef_color_t& color) OVERRIDE;
+  bool SetFontList(int command_id, const CefString& font_list) OVERRIDE;
+  bool SetFontListAt(int index, const CefString& font_list) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MENU_MODEL_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/menu_model_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/menu_model_delegate_ctocpp.cc
new file mode 100644
index 0000000..92e8fb4
--- /dev/null
+++ b/src/libcef_dll/ctocpp/menu_model_delegate_ctocpp.cc
@@ -0,0 +1,198 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c423c0e433e50b85f28dea833aa19e12404f3139$
+//
+
+#include "libcef_dll/ctocpp/menu_model_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/menu_model_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefMenuModelDelegateCToCpp::ExecuteCommand(
+    CefRefPtr<CefMenuModel> menu_model,
+    int command_id,
+    cef_event_flags_t event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute_command))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->execute_command(_struct, CefMenuModelCppToC::Wrap(menu_model),
+                           command_id, event_flags);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuModelDelegateCToCpp::MouseOutsideMenu(
+    CefRefPtr<CefMenuModel> menu_model,
+    const CefPoint& screen_point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, mouse_outside_menu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->mouse_outside_menu(_struct, CefMenuModelCppToC::Wrap(menu_model),
+                              &screen_point);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuModelDelegateCToCpp::UnhandledOpenSubmenu(
+    CefRefPtr<CefMenuModel> menu_model,
+    bool is_rtl) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, unhandled_open_submenu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->unhandled_open_submenu(_struct, CefMenuModelCppToC::Wrap(menu_model),
+                                  is_rtl);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuModelDelegateCToCpp::UnhandledCloseSubmenu(
+    CefRefPtr<CefMenuModel> menu_model,
+    bool is_rtl) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, unhandled_close_submenu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->unhandled_close_submenu(
+      _struct, CefMenuModelCppToC::Wrap(menu_model), is_rtl);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuModelDelegateCToCpp::MenuWillShow(
+    CefRefPtr<CefMenuModel> menu_model) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, menu_will_show))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->menu_will_show(_struct, CefMenuModelCppToC::Wrap(menu_model));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuModelDelegateCToCpp::MenuClosed(
+    CefRefPtr<CefMenuModel> menu_model) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, menu_closed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->menu_closed(_struct, CefMenuModelCppToC::Wrap(menu_model));
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuModelDelegateCToCpp::FormatLabel(CefRefPtr<CefMenuModel> menu_model,
+                                             CefString& label) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_model_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, format_label))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_diff
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->format_label(
+      _struct, CefMenuModelCppToC::Wrap(menu_model), label.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuModelDelegateCToCpp::CefMenuModelDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuModelDelegateCToCpp::~CefMenuModelDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_menu_model_delegate_t* CefCToCppRefCounted<
+    CefMenuModelDelegateCToCpp,
+    CefMenuModelDelegate,
+    cef_menu_model_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                              CefMenuModelDelegate* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMenuModelDelegateCToCpp,
+                                   CefMenuModelDelegate,
+                                   cef_menu_model_delegate_t>::kWrapperType =
+    WT_MENU_MODEL_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/menu_model_delegate_ctocpp.h b/src/libcef_dll/ctocpp/menu_model_delegate_ctocpp.h
new file mode 100644
index 0000000..4984072
--- /dev/null
+++ b/src/libcef_dll/ctocpp/menu_model_delegate_ctocpp.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6c64e0f39bd2f6bba5cf6ba3a7d70f494e5b5d42$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_MENU_MODEL_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_MENU_MODEL_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_menu_model_capi.h"
+#include "include/capi/cef_menu_model_delegate_capi.h"
+#include "include/cef_menu_model.h"
+#include "include/cef_menu_model_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefMenuModelDelegateCToCpp
+    : public CefCToCppRefCounted<CefMenuModelDelegateCToCpp,
+                                 CefMenuModelDelegate,
+                                 cef_menu_model_delegate_t> {
+ public:
+  CefMenuModelDelegateCToCpp();
+  virtual ~CefMenuModelDelegateCToCpp();
+
+  // CefMenuModelDelegate methods.
+  void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                      int command_id,
+                      cef_event_flags_t event_flags) override;
+  void MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
+                        const CefPoint& screen_point) override;
+  void UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                            bool is_rtl) override;
+  void UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                             bool is_rtl) override;
+  void MenuWillShow(CefRefPtr<CefMenuModel> menu_model) override;
+  void MenuClosed(CefRefPtr<CefMenuModel> menu_model) override;
+  bool FormatLabel(CefRefPtr<CefMenuModel> menu_model,
+                   CefString& label) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_MENU_MODEL_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/navigation_entry_ctocpp.cc b/src/libcef_dll/ctocpp/navigation_entry_ctocpp.cc
new file mode 100644
index 0000000..ee443c2
--- /dev/null
+++ b/src/libcef_dll/ctocpp/navigation_entry_ctocpp.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c6f680dcbbea79f362a1d7686d7af902be9f0f9b$
+//
+
+#include "libcef_dll/ctocpp/navigation_entry_ctocpp.h"
+#include "libcef_dll/ctocpp/sslstatus_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefNavigationEntryCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefNavigationEntryCToCpp::GetURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefNavigationEntryCToCpp::GetDisplayURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_display_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_display_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefNavigationEntryCToCpp::GetOriginalURL() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_original_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_original_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefNavigationEntryCToCpp::GetTitle() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_title))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_title(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefNavigationEntry::TransitionType
+CefNavigationEntryCToCpp::GetTransitionType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_transition_type))
+    return TT_EXPLICIT;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_transition_type_t _retval = _struct->get_transition_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefNavigationEntryCToCpp::HasPostData() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_post_data))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_post_data(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefTime CefNavigationEntryCToCpp::GetCompletionTime() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_completion_time))
+    return CefTime();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_time_t _retval = _struct->get_completion_time(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefNavigationEntryCToCpp::GetHttpStatusCode() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_http_status_code))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_http_status_code(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefSSLStatus> CefNavigationEntryCToCpp::GetSSLStatus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_sslstatus))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_sslstatus_t* _retval = _struct->get_sslstatus(_struct);
+
+  // Return type: refptr_same
+  return CefSSLStatusCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryCToCpp::CefNavigationEntryCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryCToCpp::~CefNavigationEntryCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_navigation_entry_t* CefCToCppRefCounted<
+    CefNavigationEntryCToCpp,
+    CefNavigationEntry,
+    cef_navigation_entry_t>::UnwrapDerived(CefWrapperType type,
+                                           CefNavigationEntry* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefNavigationEntryCToCpp,
+                                   CefNavigationEntry,
+                                   cef_navigation_entry_t>::kWrapperType =
+    WT_NAVIGATION_ENTRY;
diff --git a/src/libcef_dll/ctocpp/navigation_entry_ctocpp.h b/src/libcef_dll/ctocpp/navigation_entry_ctocpp.h
new file mode 100644
index 0000000..02ee0d3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/navigation_entry_ctocpp.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7276d243be0acdd65e29503571afcc55d6eb00fd$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_NAVIGATION_ENTRY_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_NAVIGATION_ENTRY_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_navigation_entry_capi.h"
+#include "include/cef_navigation_entry.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefNavigationEntryCToCpp
+    : public CefCToCppRefCounted<CefNavigationEntryCToCpp,
+                                 CefNavigationEntry,
+                                 cef_navigation_entry_t> {
+ public:
+  CefNavigationEntryCToCpp();
+  virtual ~CefNavigationEntryCToCpp();
+
+  // CefNavigationEntry methods.
+  bool IsValid() OVERRIDE;
+  CefString GetURL() OVERRIDE;
+  CefString GetDisplayURL() OVERRIDE;
+  CefString GetOriginalURL() OVERRIDE;
+  CefString GetTitle() OVERRIDE;
+  TransitionType GetTransitionType() OVERRIDE;
+  bool HasPostData() OVERRIDE;
+  CefTime GetCompletionTime() OVERRIDE;
+  int GetHttpStatusCode() OVERRIDE;
+  CefRefPtr<CefSSLStatus> GetSSLStatus() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_NAVIGATION_ENTRY_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.cc b/src/libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.cc
new file mode 100644
index 0000000..9191785
--- /dev/null
+++ b/src/libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e927c676714bc7ebebb03468d963881da643eb50$
+//
+
+#include "libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h"
+#include "libcef_dll/cpptoc/navigation_entry_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefNavigationEntryVisitorCToCpp::Visit(CefRefPtr<CefNavigationEntry> entry,
+                                            bool current,
+                                            int index,
+                                            int total) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_navigation_entry_visitor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: entry; type: refptr_diff
+  DCHECK(entry.get());
+  if (!entry.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->visit(_struct, CefNavigationEntryCppToC::Wrap(entry),
+                               current, index, total);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryVisitorCToCpp::CefNavigationEntryVisitorCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefNavigationEntryVisitorCToCpp::~CefNavigationEntryVisitorCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_navigation_entry_visitor_t* CefCToCppRefCounted<
+    CefNavigationEntryVisitorCToCpp,
+    CefNavigationEntryVisitor,
+    cef_navigation_entry_visitor_t>::UnwrapDerived(CefWrapperType type,
+                                                   CefNavigationEntryVisitor*
+                                                       c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefNavigationEntryVisitorCToCpp,
+                        CefNavigationEntryVisitor,
+                        cef_navigation_entry_visitor_t>::kWrapperType =
+        WT_NAVIGATION_ENTRY_VISITOR;
diff --git a/src/libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h b/src/libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h
new file mode 100644
index 0000000..0858945
--- /dev/null
+++ b/src/libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d4079700fe4abeacdcaf8dd40493df062736f785$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_NAVIGATION_ENTRY_VISITOR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_NAVIGATION_ENTRY_VISITOR_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefNavigationEntryVisitorCToCpp
+    : public CefCToCppRefCounted<CefNavigationEntryVisitorCToCpp,
+                                 CefNavigationEntryVisitor,
+                                 cef_navigation_entry_visitor_t> {
+ public:
+  CefNavigationEntryVisitorCToCpp();
+  virtual ~CefNavigationEntryVisitorCToCpp();
+
+  // CefNavigationEntryVisitor methods.
+  bool Visit(CefRefPtr<CefNavigationEntry> entry,
+             bool current,
+             int index,
+             int total) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_NAVIGATION_ENTRY_VISITOR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/pdf_print_callback_ctocpp.cc b/src/libcef_dll/ctocpp/pdf_print_callback_ctocpp.cc
new file mode 100644
index 0000000..eb59895
--- /dev/null
+++ b/src/libcef_dll/ctocpp/pdf_print_callback_ctocpp.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0b9a4766622f15290ae7328c08f1b28eb2d2275d$
+//
+
+#include "libcef_dll/ctocpp/pdf_print_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefPdfPrintCallbackCToCpp::OnPdfPrintFinished(const CefString& path,
+                                                   bool ok) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_pdf_print_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_pdf_print_finished))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+
+  // Execute
+  _struct->on_pdf_print_finished(_struct, path.GetStruct(), ok);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPdfPrintCallbackCToCpp::CefPdfPrintCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPdfPrintCallbackCToCpp::~CefPdfPrintCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_pdf_print_callback_t* CefCToCppRefCounted<
+    CefPdfPrintCallbackCToCpp,
+    CefPdfPrintCallback,
+    cef_pdf_print_callback_t>::UnwrapDerived(CefWrapperType type,
+                                             CefPdfPrintCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPdfPrintCallbackCToCpp,
+                                   CefPdfPrintCallback,
+                                   cef_pdf_print_callback_t>::kWrapperType =
+    WT_PDF_PRINT_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/pdf_print_callback_ctocpp.h b/src/libcef_dll/ctocpp/pdf_print_callback_ctocpp.h
new file mode 100644
index 0000000..b48c70b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/pdf_print_callback_ctocpp.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6e5e0048a9472dd5ac056b81abaf08c0d58dead2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_PDF_PRINT_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_PDF_PRINT_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefPdfPrintCallbackCToCpp
+    : public CefCToCppRefCounted<CefPdfPrintCallbackCToCpp,
+                                 CefPdfPrintCallback,
+                                 cef_pdf_print_callback_t> {
+ public:
+  CefPdfPrintCallbackCToCpp();
+  virtual ~CefPdfPrintCallbackCToCpp();
+
+  // CefPdfPrintCallback methods.
+  void OnPdfPrintFinished(const CefString& path, bool ok) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_PDF_PRINT_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/post_data_ctocpp.cc b/src/libcef_dll/ctocpp/post_data_ctocpp.cc
new file mode 100644
index 0000000..73b7fcd
--- /dev/null
+++ b/src/libcef_dll/ctocpp/post_data_ctocpp.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=895c319a8cdcb9d38eeeb55e98cb9bdb44a51b90$
+//
+
+#include "libcef_dll/ctocpp/post_data_ctocpp.h"
+#include <algorithm>
+#include "libcef_dll/ctocpp/post_data_element_ctocpp.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPostData> CefPostData::Create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_post_data_t* _retval = cef_post_data_create();
+
+  // Return type: refptr_same
+  return CefPostDataCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefPostDataCToCpp::IsReadOnly() {
+  cef_post_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefPostDataCToCpp::HasExcludedElements() {
+  cef_post_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_excluded_elements))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_excluded_elements(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefPostDataCToCpp::GetElementCount() {
+  cef_post_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_element_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_element_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPostDataCToCpp::GetElements(ElementVector& elements) {
+  cef_post_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_elements))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: elements; type: refptr_vec_same_byref
+  size_t elementsSize = elements.size();
+  size_t elementsCount = std::max(GetElementCount(), elementsSize);
+  cef_post_data_element_t** elementsList = NULL;
+  if (elementsCount > 0) {
+    elementsList = new cef_post_data_element_t*[elementsCount];
+    DCHECK(elementsList);
+    if (elementsList) {
+      memset(elementsList, 0, sizeof(cef_post_data_element_t*) * elementsCount);
+    }
+    if (elementsList && elementsSize > 0) {
+      for (size_t i = 0; i < elementsSize; ++i) {
+        elementsList[i] = CefPostDataElementCToCpp::Unwrap(elements[i]);
+      }
+    }
+  }
+
+  // Execute
+  _struct->get_elements(_struct, &elementsCount, elementsList);
+
+  // Restore param:elements; type: refptr_vec_same_byref
+  elements.clear();
+  if (elementsCount > 0 && elementsList) {
+    for (size_t i = 0; i < elementsCount; ++i) {
+      elements.push_back(CefPostDataElementCToCpp::Wrap(elementsList[i]));
+    }
+    delete[] elementsList;
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPostDataCToCpp::RemoveElement(CefRefPtr<CefPostDataElement> element) {
+  cef_post_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_element))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: element; type: refptr_same
+  DCHECK(element.get());
+  if (!element.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->remove_element(
+      _struct, CefPostDataElementCToCpp::Unwrap(element));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPostDataCToCpp::AddElement(CefRefPtr<CefPostDataElement> element) {
+  cef_post_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_element))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: element; type: refptr_same
+  DCHECK(element.get());
+  if (!element.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->add_element(_struct, CefPostDataElementCToCpp::Unwrap(element));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefPostDataCToCpp::RemoveElements() {
+  cef_post_data_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_elements))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->remove_elements(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPostDataCToCpp::CefPostDataCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPostDataCToCpp::~CefPostDataCToCpp() {}
+
+template <>
+cef_post_data_t*
+CefCToCppRefCounted<CefPostDataCToCpp, CefPostData, cef_post_data_t>::
+    UnwrapDerived(CefWrapperType type, CefPostData* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPostDataCToCpp,
+                                   CefPostData,
+                                   cef_post_data_t>::kWrapperType =
+    WT_POST_DATA;
diff --git a/src/libcef_dll/ctocpp/post_data_ctocpp.h b/src/libcef_dll/ctocpp/post_data_ctocpp.h
new file mode 100644
index 0000000..469f3ed
--- /dev/null
+++ b/src/libcef_dll/ctocpp/post_data_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0710e94a6132f440de0c46ce73ca1d4a36af2857$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_POST_DATA_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_POST_DATA_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_capi.h"
+#include "include/cef_request.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPostDataCToCpp : public CefCToCppRefCounted<CefPostDataCToCpp,
+                                                     CefPostData,
+                                                     cef_post_data_t> {
+ public:
+  CefPostDataCToCpp();
+  virtual ~CefPostDataCToCpp();
+
+  // CefPostData methods.
+  bool IsReadOnly() OVERRIDE;
+  bool HasExcludedElements() OVERRIDE;
+  size_t GetElementCount() OVERRIDE;
+  void GetElements(ElementVector& elements) OVERRIDE;
+  bool RemoveElement(CefRefPtr<CefPostDataElement> element) OVERRIDE;
+  bool AddElement(CefRefPtr<CefPostDataElement> element) OVERRIDE;
+  void RemoveElements() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_POST_DATA_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/post_data_element_ctocpp.cc b/src/libcef_dll/ctocpp/post_data_element_ctocpp.cc
new file mode 100644
index 0000000..24c73c8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/post_data_element_ctocpp.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=89f51160ee669abdfee47c627c7b7fec0b034b2b$
+//
+
+#include "libcef_dll/ctocpp/post_data_element_ctocpp.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefPostDataElement> CefPostDataElement::Create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_post_data_element_t* _retval = cef_post_data_element_create();
+
+  // Return type: refptr_same
+  return CefPostDataElementCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefPostDataElementCToCpp::IsReadOnly() {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefPostDataElementCToCpp::SetToEmpty() {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_to_empty))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_to_empty(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPostDataElementCToCpp::SetToFile(const CefString& fileName) {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_to_file))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(!fileName.empty());
+  if (fileName.empty())
+    return;
+
+  // Execute
+  _struct->set_to_file(_struct, fileName.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPostDataElementCToCpp::SetToBytes(size_t size, const void* bytes) {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_to_bytes))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: bytes; type: simple_byaddr
+  DCHECK(bytes);
+  if (!bytes)
+    return;
+
+  // Execute
+  _struct->set_to_bytes(_struct, size, bytes);
+}
+
+NO_SANITIZE("cfi-icall")
+CefPostDataElement::Type CefPostDataElementCToCpp::GetType() {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return PDE_TYPE_EMPTY;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_postdataelement_type_t _retval = _struct->get_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefPostDataElementCToCpp::GetFile() {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_file))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_file(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefPostDataElementCToCpp::GetBytesCount() {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bytes_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_bytes_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+size_t CefPostDataElementCToCpp::GetBytes(size_t size, void* bytes) {
+  cef_post_data_element_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bytes))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: bytes; type: simple_byaddr
+  DCHECK(bytes);
+  if (!bytes)
+    return 0;
+
+  // Execute
+  size_t _retval = _struct->get_bytes(_struct, size, bytes);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPostDataElementCToCpp::CefPostDataElementCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPostDataElementCToCpp::~CefPostDataElementCToCpp() {}
+
+template <>
+cef_post_data_element_t* CefCToCppRefCounted<
+    CefPostDataElementCToCpp,
+    CefPostDataElement,
+    cef_post_data_element_t>::UnwrapDerived(CefWrapperType type,
+                                            CefPostDataElement* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPostDataElementCToCpp,
+                                   CefPostDataElement,
+                                   cef_post_data_element_t>::kWrapperType =
+    WT_POST_DATA_ELEMENT;
diff --git a/src/libcef_dll/ctocpp/post_data_element_ctocpp.h b/src/libcef_dll/ctocpp/post_data_element_ctocpp.h
new file mode 100644
index 0000000..c49cf53
--- /dev/null
+++ b/src/libcef_dll/ctocpp/post_data_element_ctocpp.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ee4fd5dcea725326c5885cf30c4b0bc5dbd76a01$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_POST_DATA_ELEMENT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_POST_DATA_ELEMENT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_capi.h"
+#include "include/cef_request.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPostDataElementCToCpp
+    : public CefCToCppRefCounted<CefPostDataElementCToCpp,
+                                 CefPostDataElement,
+                                 cef_post_data_element_t> {
+ public:
+  CefPostDataElementCToCpp();
+  virtual ~CefPostDataElementCToCpp();
+
+  // CefPostDataElement methods.
+  bool IsReadOnly() OVERRIDE;
+  void SetToEmpty() OVERRIDE;
+  void SetToFile(const CefString& fileName) OVERRIDE;
+  void SetToBytes(size_t size, const void* bytes) OVERRIDE;
+  Type GetType() OVERRIDE;
+  CefString GetFile() OVERRIDE;
+  size_t GetBytesCount() OVERRIDE;
+  size_t GetBytes(size_t size, void* bytes) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_POST_DATA_ELEMENT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/print_dialog_callback_ctocpp.cc b/src/libcef_dll/ctocpp/print_dialog_callback_ctocpp.cc
new file mode 100644
index 0000000..0dd8ca5
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_dialog_callback_ctocpp.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c013c03a8fd2436c90587a96bb4960869888667a$
+//
+
+#include "libcef_dll/ctocpp/print_dialog_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/print_settings_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefPrintDialogCallbackCToCpp::Continue(
+    CefRefPtr<CefPrintSettings> settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_dialog_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: settings; type: refptr_same
+  DCHECK(settings.get());
+  if (!settings.get())
+    return;
+
+  // Execute
+  _struct->cont(_struct, CefPrintSettingsCToCpp::Unwrap(settings));
+}
+
+NO_SANITIZE("cfi-icall") void CefPrintDialogCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_dialog_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintDialogCallbackCToCpp::CefPrintDialogCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintDialogCallbackCToCpp::~CefPrintDialogCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_print_dialog_callback_t* CefCToCppRefCounted<
+    CefPrintDialogCallbackCToCpp,
+    CefPrintDialogCallback,
+    cef_print_dialog_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                CefPrintDialogCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPrintDialogCallbackCToCpp,
+                                   CefPrintDialogCallback,
+                                   cef_print_dialog_callback_t>::kWrapperType =
+    WT_PRINT_DIALOG_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/print_dialog_callback_ctocpp.h b/src/libcef_dll/ctocpp/print_dialog_callback_ctocpp.h
new file mode 100644
index 0000000..74a09b9
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_dialog_callback_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3df7f45bbfe22ab9ff61f15dc9234c5a47ec72b9$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_PRINT_DIALOG_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_PRINT_DIALOG_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_print_handler_capi.h"
+#include "include/cef_print_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPrintDialogCallbackCToCpp
+    : public CefCToCppRefCounted<CefPrintDialogCallbackCToCpp,
+                                 CefPrintDialogCallback,
+                                 cef_print_dialog_callback_t> {
+ public:
+  CefPrintDialogCallbackCToCpp();
+  virtual ~CefPrintDialogCallbackCToCpp();
+
+  // CefPrintDialogCallback methods.
+  void Continue(CefRefPtr<CefPrintSettings> settings) OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_PRINT_DIALOG_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/print_handler_ctocpp.cc b/src/libcef_dll/ctocpp/print_handler_ctocpp.cc
new file mode 100644
index 0000000..1fe5155
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_handler_ctocpp.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b9fc8704be8fced9d4fe3ac048aaa716e1b14d0b$
+//
+
+#include "libcef_dll/ctocpp/print_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/print_dialog_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/print_job_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/print_settings_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefPrintHandlerCToCpp::OnPrintStart(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_print_start))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_print_start(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintHandlerCToCpp::OnPrintSettings(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefPrintSettings> settings,
+    bool get_defaults) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_print_settings))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: settings; type: refptr_diff
+  DCHECK(settings.get());
+  if (!settings.get())
+    return;
+
+  // Execute
+  _struct->on_print_settings(_struct, CefBrowserCppToC::Wrap(browser),
+                             CefPrintSettingsCppToC::Wrap(settings),
+                             get_defaults);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPrintHandlerCToCpp::OnPrintDialog(
+    CefRefPtr<CefBrowser> browser,
+    bool has_selection,
+    CefRefPtr<CefPrintDialogCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_print_dialog))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_print_dialog(
+      _struct, CefBrowserCppToC::Wrap(browser), has_selection,
+      CefPrintDialogCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPrintHandlerCToCpp::OnPrintJob(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& document_name,
+    const CefString& pdf_file_path,
+    CefRefPtr<CefPrintJobCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_print_job))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: document_name; type: string_byref_const
+  DCHECK(!document_name.empty());
+  if (document_name.empty())
+    return false;
+  // Verify param: pdf_file_path; type: string_byref_const
+  DCHECK(!pdf_file_path.empty());
+  if (pdf_file_path.empty())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_print_job(
+      _struct, CefBrowserCppToC::Wrap(browser), document_name.GetStruct(),
+      pdf_file_path.GetStruct(), CefPrintJobCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintHandlerCToCpp::OnPrintReset(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_print_reset))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_print_reset(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefPrintHandlerCToCpp::GetPdfPaperSize(int device_units_per_inch) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_pdf_paper_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_pdf_paper_size(_struct, device_units_per_inch);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintHandlerCToCpp::CefPrintHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintHandlerCToCpp::~CefPrintHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_print_handler_t*
+CefCToCppRefCounted<CefPrintHandlerCToCpp,
+                    CefPrintHandler,
+                    cef_print_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefPrintHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPrintHandlerCToCpp,
+                                   CefPrintHandler,
+                                   cef_print_handler_t>::kWrapperType =
+    WT_PRINT_HANDLER;
diff --git a/src/libcef_dll/ctocpp/print_handler_ctocpp.h b/src/libcef_dll/ctocpp/print_handler_ctocpp.h
new file mode 100644
index 0000000..99acc36
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_handler_ctocpp.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9cc2a681b89d2958e4537a0e22c3b710702a69f6$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_PRINT_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_PRINT_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_print_handler_capi.h"
+#include "include/cef_print_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefPrintHandlerCToCpp : public CefCToCppRefCounted<CefPrintHandlerCToCpp,
+                                                         CefPrintHandler,
+                                                         cef_print_handler_t> {
+ public:
+  CefPrintHandlerCToCpp();
+  virtual ~CefPrintHandlerCToCpp();
+
+  // CefPrintHandler methods.
+  void OnPrintStart(CefRefPtr<CefBrowser> browser) override;
+  void OnPrintSettings(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefPrintSettings> settings,
+                       bool get_defaults) override;
+  bool OnPrintDialog(CefRefPtr<CefBrowser> browser,
+                     bool has_selection,
+                     CefRefPtr<CefPrintDialogCallback> callback) override;
+  bool OnPrintJob(CefRefPtr<CefBrowser> browser,
+                  const CefString& document_name,
+                  const CefString& pdf_file_path,
+                  CefRefPtr<CefPrintJobCallback> callback) override;
+  void OnPrintReset(CefRefPtr<CefBrowser> browser) override;
+  CefSize GetPdfPaperSize(int device_units_per_inch) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_PRINT_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/print_job_callback_ctocpp.cc b/src/libcef_dll/ctocpp/print_job_callback_ctocpp.cc
new file mode 100644
index 0000000..d5b3943
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_job_callback_ctocpp.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f378864057814460c2cee92c0a31e1e2333220ff$
+//
+
+#include "libcef_dll/ctocpp/print_job_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefPrintJobCallbackCToCpp::Continue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_job_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cont(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintJobCallbackCToCpp::CefPrintJobCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintJobCallbackCToCpp::~CefPrintJobCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_print_job_callback_t* CefCToCppRefCounted<
+    CefPrintJobCallbackCToCpp,
+    CefPrintJobCallback,
+    cef_print_job_callback_t>::UnwrapDerived(CefWrapperType type,
+                                             CefPrintJobCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPrintJobCallbackCToCpp,
+                                   CefPrintJobCallback,
+                                   cef_print_job_callback_t>::kWrapperType =
+    WT_PRINT_JOB_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/print_job_callback_ctocpp.h b/src/libcef_dll/ctocpp/print_job_callback_ctocpp.h
new file mode 100644
index 0000000..fb51a00
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_job_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a47d4676a9231ef14492dfc8e61643c583df7f68$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_PRINT_JOB_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_PRINT_JOB_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_print_handler_capi.h"
+#include "include/cef_print_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPrintJobCallbackCToCpp
+    : public CefCToCppRefCounted<CefPrintJobCallbackCToCpp,
+                                 CefPrintJobCallback,
+                                 cef_print_job_callback_t> {
+ public:
+  CefPrintJobCallbackCToCpp();
+  virtual ~CefPrintJobCallbackCToCpp();
+
+  // CefPrintJobCallback methods.
+  void Continue() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_PRINT_JOB_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/print_settings_ctocpp.cc b/src/libcef_dll/ctocpp/print_settings_ctocpp.cc
new file mode 100644
index 0000000..4584c63
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_settings_ctocpp.cc
@@ -0,0 +1,441 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bb2d141ebf3f887910043a250bf18f95b87d5fc3$
+//
+
+#include "libcef_dll/ctocpp/print_settings_ctocpp.h"
+#include <algorithm>
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefPrintSettings> CefPrintSettings::Create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_print_settings_t* _retval = cef_print_settings_create();
+
+  // Return type: refptr_same
+  return CefPrintSettingsCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefPrintSettingsCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefPrintSettingsCToCpp::IsReadOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::SetOrientation(bool landscape) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_orientation))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_orientation(_struct, landscape);
+}
+
+NO_SANITIZE("cfi-icall") bool CefPrintSettingsCToCpp::IsLandscape() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_landscape))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_landscape(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::SetPrinterPrintableArea(
+    const CefSize& physical_size_device_units,
+    const CefRect& printable_area_device_units,
+    bool landscape_needs_flip) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_printer_printable_area))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_printer_printable_area(_struct, &physical_size_device_units,
+                                      &printable_area_device_units,
+                                      landscape_needs_flip);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::SetDeviceName(const CefString& name) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_device_name))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: name
+
+  // Execute
+  _struct->set_device_name(_struct, name.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefPrintSettingsCToCpp::GetDeviceName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_device_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_device_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") void CefPrintSettingsCToCpp::SetDPI(int dpi) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_dpi))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_dpi(_struct, dpi);
+}
+
+NO_SANITIZE("cfi-icall") int CefPrintSettingsCToCpp::GetDPI() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_dpi))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_dpi(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::SetPageRanges(const PageRangeList& ranges) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_page_ranges))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: ranges; type: simple_vec_byref_const
+  const size_t rangesCount = ranges.size();
+  cef_range_t* rangesList = NULL;
+  if (rangesCount > 0) {
+    rangesList = new cef_range_t[rangesCount];
+    DCHECK(rangesList);
+    if (rangesList) {
+      for (size_t i = 0; i < rangesCount; ++i) {
+        rangesList[i] = ranges[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->set_page_ranges(_struct, rangesCount, rangesList);
+
+  // Restore param:ranges; type: simple_vec_byref_const
+  if (rangesList)
+    delete[] rangesList;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefPrintSettingsCToCpp::GetPageRangesCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_page_ranges_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_page_ranges_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::GetPageRanges(PageRangeList& ranges) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_page_ranges))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: ranges; type: simple_vec_byref
+  size_t rangesSize = ranges.size();
+  size_t rangesCount = std::max(GetPageRangesCount(), rangesSize);
+  cef_range_t* rangesList = NULL;
+  if (rangesCount > 0) {
+    rangesList = new cef_range_t[rangesCount];
+    DCHECK(rangesList);
+    if (rangesList) {
+      memset(rangesList, 0, sizeof(cef_range_t) * rangesCount);
+    }
+    if (rangesList && rangesSize > 0) {
+      for (size_t i = 0; i < rangesSize; ++i) {
+        rangesList[i] = ranges[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->get_page_ranges(_struct, &rangesCount, rangesList);
+
+  // Restore param:ranges; type: simple_vec_byref
+  ranges.clear();
+  if (rangesCount > 0 && rangesList) {
+    for (size_t i = 0; i < rangesCount; ++i) {
+      ranges.push_back(rangesList[i]);
+    }
+    delete[] rangesList;
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::SetSelectionOnly(bool selection_only) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_selection_only))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_selection_only(_struct, selection_only);
+}
+
+NO_SANITIZE("cfi-icall") bool CefPrintSettingsCToCpp::IsSelectionOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_selection_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_selection_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefPrintSettingsCToCpp::SetCollate(bool collate) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_collate))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_collate(_struct, collate);
+}
+
+NO_SANITIZE("cfi-icall") bool CefPrintSettingsCToCpp::WillCollate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, will_collate))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->will_collate(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::SetColorModel(ColorModel model) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_color_model))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_color_model(_struct, model);
+}
+
+NO_SANITIZE("cfi-icall")
+CefPrintSettings::ColorModel CefPrintSettingsCToCpp::GetColorModel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_color_model))
+    return COLOR_MODEL_UNKNOWN;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_model_t _retval = _struct->get_color_model(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefPrintSettingsCToCpp::SetCopies(int copies) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_copies))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_copies(_struct, copies);
+}
+
+NO_SANITIZE("cfi-icall") int CefPrintSettingsCToCpp::GetCopies() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_copies))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_copies(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPrintSettingsCToCpp::SetDuplexMode(DuplexMode mode) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_duplex_mode))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_duplex_mode(_struct, mode);
+}
+
+NO_SANITIZE("cfi-icall")
+CefPrintSettings::DuplexMode CefPrintSettingsCToCpp::GetDuplexMode() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_print_settings_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_duplex_mode))
+    return DUPLEX_MODE_UNKNOWN;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_duplex_mode_t _retval = _struct->get_duplex_mode(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPrintSettingsCToCpp::CefPrintSettingsCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPrintSettingsCToCpp::~CefPrintSettingsCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_print_settings_t*
+CefCToCppRefCounted<CefPrintSettingsCToCpp,
+                    CefPrintSettings,
+                    cef_print_settings_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefPrintSettings* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPrintSettingsCToCpp,
+                                   CefPrintSettings,
+                                   cef_print_settings_t>::kWrapperType =
+    WT_PRINT_SETTINGS;
diff --git a/src/libcef_dll/ctocpp/print_settings_ctocpp.h b/src/libcef_dll/ctocpp/print_settings_ctocpp.h
new file mode 100644
index 0000000..6a811cd
--- /dev/null
+++ b/src/libcef_dll/ctocpp/print_settings_ctocpp.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9fd8b11b774211cbeb1a8921f64d472b062afee7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_PRINT_SETTINGS_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_PRINT_SETTINGS_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_print_settings_capi.h"
+#include "include/cef_print_settings.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPrintSettingsCToCpp
+    : public CefCToCppRefCounted<CefPrintSettingsCToCpp,
+                                 CefPrintSettings,
+                                 cef_print_settings_t> {
+ public:
+  CefPrintSettingsCToCpp();
+  virtual ~CefPrintSettingsCToCpp();
+
+  // CefPrintSettings methods.
+  bool IsValid() OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  void SetOrientation(bool landscape) OVERRIDE;
+  bool IsLandscape() OVERRIDE;
+  void SetPrinterPrintableArea(const CefSize& physical_size_device_units,
+                               const CefRect& printable_area_device_units,
+                               bool landscape_needs_flip) OVERRIDE;
+  void SetDeviceName(const CefString& name) OVERRIDE;
+  CefString GetDeviceName() OVERRIDE;
+  void SetDPI(int dpi) OVERRIDE;
+  int GetDPI() OVERRIDE;
+  void SetPageRanges(const PageRangeList& ranges) OVERRIDE;
+  size_t GetPageRangesCount() OVERRIDE;
+  void GetPageRanges(PageRangeList& ranges) OVERRIDE;
+  void SetSelectionOnly(bool selection_only) OVERRIDE;
+  bool IsSelectionOnly() OVERRIDE;
+  void SetCollate(bool collate) OVERRIDE;
+  bool WillCollate() OVERRIDE;
+  void SetColorModel(ColorModel model) OVERRIDE;
+  ColorModel GetColorModel() OVERRIDE;
+  void SetCopies(int copies) OVERRIDE;
+  int GetCopies() OVERRIDE;
+  void SetDuplexMode(DuplexMode mode) OVERRIDE;
+  DuplexMode GetDuplexMode() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_PRINT_SETTINGS_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/process_message_ctocpp.cc b/src/libcef_dll/ctocpp/process_message_ctocpp.cc
new file mode 100644
index 0000000..7963d96
--- /dev/null
+++ b/src/libcef_dll/ctocpp/process_message_ctocpp.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=74d0487f35e81f0d220f947f0b203b04b193929f$
+//
+
+#include "libcef_dll/ctocpp/process_message_ctocpp.h"
+#include "libcef_dll/ctocpp/list_value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefProcessMessage> CefProcessMessage::Create(const CefString& name) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return nullptr;
+
+  // Execute
+  cef_process_message_t* _retval = cef_process_message_create(name.GetStruct());
+
+  // Return type: refptr_same
+  return CefProcessMessageCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefProcessMessageCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_process_message_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefProcessMessageCToCpp::IsReadOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_process_message_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefProcessMessage> CefProcessMessageCToCpp::Copy() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_process_message_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, copy))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_process_message_t* _retval = _struct->copy(_struct);
+
+  // Return type: refptr_same
+  return CefProcessMessageCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefProcessMessageCToCpp::GetName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_process_message_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefListValue> CefProcessMessageCToCpp::GetArgumentList() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_process_message_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_argument_list))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_list_value_t* _retval = _struct->get_argument_list(_struct);
+
+  // Return type: refptr_same
+  return CefListValueCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefProcessMessageCToCpp::CefProcessMessageCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefProcessMessageCToCpp::~CefProcessMessageCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_process_message_t* CefCToCppRefCounted<
+    CefProcessMessageCToCpp,
+    CefProcessMessage,
+    cef_process_message_t>::UnwrapDerived(CefWrapperType type,
+                                          CefProcessMessage* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefProcessMessageCToCpp,
+                                   CefProcessMessage,
+                                   cef_process_message_t>::kWrapperType =
+    WT_PROCESS_MESSAGE;
diff --git a/src/libcef_dll/ctocpp/process_message_ctocpp.h b/src/libcef_dll/ctocpp/process_message_ctocpp.h
new file mode 100644
index 0000000..1f364d8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/process_message_ctocpp.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6364b8b5300ddb055b789acf13367f24272feb51$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_PROCESS_MESSAGE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_PROCESS_MESSAGE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_process_message_capi.h"
+#include "include/cef_process_message.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefProcessMessageCToCpp
+    : public CefCToCppRefCounted<CefProcessMessageCToCpp,
+                                 CefProcessMessage,
+                                 cef_process_message_t> {
+ public:
+  CefProcessMessageCToCpp();
+  virtual ~CefProcessMessageCToCpp();
+
+  // CefProcessMessage methods.
+  bool IsValid() OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  CefRefPtr<CefProcessMessage> Copy() OVERRIDE;
+  CefString GetName() OVERRIDE;
+  CefRefPtr<CefListValue> GetArgumentList() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_PROCESS_MESSAGE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/read_handler_ctocpp.cc b/src/libcef_dll/ctocpp/read_handler_ctocpp.cc
new file mode 100644
index 0000000..e270abf
--- /dev/null
+++ b/src/libcef_dll/ctocpp/read_handler_ctocpp.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a43a5f260d313c39d52223998140fa07657bc318$
+//
+
+#include "libcef_dll/ctocpp/read_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+size_t CefReadHandlerCToCpp::Read(void* ptr, size_t size, size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_read_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, read))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = _struct->read(_struct, ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefReadHandlerCToCpp::Seek(int64 offset, int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_read_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, seek))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->seek(_struct, offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefReadHandlerCToCpp::Tell() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_read_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, tell))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->tell(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefReadHandlerCToCpp::Eof() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_read_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, eof))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->eof(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefReadHandlerCToCpp::MayBlock() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_read_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, may_block))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->may_block(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefReadHandlerCToCpp::CefReadHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefReadHandlerCToCpp::~CefReadHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_read_handler_t*
+CefCToCppRefCounted<CefReadHandlerCToCpp, CefReadHandler, cef_read_handler_t>::
+    UnwrapDerived(CefWrapperType type, CefReadHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefReadHandlerCToCpp,
+                                   CefReadHandler,
+                                   cef_read_handler_t>::kWrapperType =
+    WT_READ_HANDLER;
diff --git a/src/libcef_dll/ctocpp/read_handler_ctocpp.h b/src/libcef_dll/ctocpp/read_handler_ctocpp.h
new file mode 100644
index 0000000..07996fc
--- /dev/null
+++ b/src/libcef_dll/ctocpp/read_handler_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b95ddcc2d32bf697b5480554b45f92dbe48beb6c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_READ_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_READ_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefReadHandlerCToCpp : public CefCToCppRefCounted<CefReadHandlerCToCpp,
+                                                        CefReadHandler,
+                                                        cef_read_handler_t> {
+ public:
+  CefReadHandlerCToCpp();
+  virtual ~CefReadHandlerCToCpp();
+
+  // CefReadHandler methods.
+  size_t Read(void* ptr, size_t size, size_t n) override;
+  int Seek(int64 offset, int whence) override;
+  int64 Tell() override;
+  int Eof() override;
+  bool MayBlock() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_READ_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/register_cdm_callback_ctocpp.cc b/src/libcef_dll/ctocpp/register_cdm_callback_ctocpp.cc
new file mode 100644
index 0000000..45dc9a4
--- /dev/null
+++ b/src/libcef_dll/ctocpp/register_cdm_callback_ctocpp.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cb7f7a8bc8e7b414eceba18bd4164b857d21ff34$
+//
+
+#include "libcef_dll/ctocpp/register_cdm_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefRegisterCdmCallbackCToCpp::OnCdmRegistrationComplete(
+    cef_cdm_registration_error_t result,
+    const CefString& error_message) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_register_cdm_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_cdm_registration_complete))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: error_message
+
+  // Execute
+  _struct->on_cdm_registration_complete(_struct, result,
+                                        error_message.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRegisterCdmCallbackCToCpp::CefRegisterCdmCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRegisterCdmCallbackCToCpp::~CefRegisterCdmCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_register_cdm_callback_t* CefCToCppRefCounted<
+    CefRegisterCdmCallbackCToCpp,
+    CefRegisterCdmCallback,
+    cef_register_cdm_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                CefRegisterCdmCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRegisterCdmCallbackCToCpp,
+                                   CefRegisterCdmCallback,
+                                   cef_register_cdm_callback_t>::kWrapperType =
+    WT_REGISTER_CDM_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/register_cdm_callback_ctocpp.h b/src/libcef_dll/ctocpp/register_cdm_callback_ctocpp.h
new file mode 100644
index 0000000..f21ad13
--- /dev/null
+++ b/src/libcef_dll/ctocpp/register_cdm_callback_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bc37eb2fa694426b48707fdbfc216aadd21c4d6d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_REGISTER_CDM_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_REGISTER_CDM_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefRegisterCdmCallbackCToCpp
+    : public CefCToCppRefCounted<CefRegisterCdmCallbackCToCpp,
+                                 CefRegisterCdmCallback,
+                                 cef_register_cdm_callback_t> {
+ public:
+  CefRegisterCdmCallbackCToCpp();
+  virtual ~CefRegisterCdmCallbackCToCpp();
+
+  // CefRegisterCdmCallback methods.
+  void OnCdmRegistrationComplete(cef_cdm_registration_error_t result,
+                                 const CefString& error_message) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_REGISTER_CDM_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/registration_ctocpp.cc b/src/libcef_dll/ctocpp/registration_ctocpp.cc
new file mode 100644
index 0000000..ad14288
--- /dev/null
+++ b/src/libcef_dll/ctocpp/registration_ctocpp.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=89f0f0c0ae2485adbf8140a419969901e5766b51$
+//
+
+#include "libcef_dll/ctocpp/registration_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRegistrationCToCpp::CefRegistrationCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRegistrationCToCpp::~CefRegistrationCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_registration_t*
+CefCToCppRefCounted<CefRegistrationCToCpp,
+                    CefRegistration,
+                    cef_registration_t>::UnwrapDerived(CefWrapperType type,
+                                                       CefRegistration* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRegistrationCToCpp,
+                                   CefRegistration,
+                                   cef_registration_t>::kWrapperType =
+    WT_REGISTRATION;
diff --git a/src/libcef_dll/ctocpp/registration_ctocpp.h b/src/libcef_dll/ctocpp/registration_ctocpp.h
new file mode 100644
index 0000000..9485a68
--- /dev/null
+++ b/src/libcef_dll/ctocpp/registration_ctocpp.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6d458b0a8e42e3f3347d5101f8d3f1f88264ecc7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_REGISTRATION_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_REGISTRATION_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_registration_capi.h"
+#include "include/cef_registration.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRegistrationCToCpp : public CefCToCppRefCounted<CefRegistrationCToCpp,
+                                                         CefRegistration,
+                                                         cef_registration_t> {
+ public:
+  CefRegistrationCToCpp();
+  virtual ~CefRegistrationCToCpp();
+
+  // CefRegistration methods.
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_REGISTRATION_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/render_handler_ctocpp.cc b/src/libcef_dll/ctocpp/render_handler_ctocpp.cc
new file mode 100644
index 0000000..171f78d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/render_handler_ctocpp.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f592f3806c9f87ee6c691b9f072cee4d6bb4650e$
+//
+
+#include "libcef_dll/ctocpp/render_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/drag_data_cpptoc.h"
+#include "libcef_dll/ctocpp/accessibility_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefAccessibilityHandler>
+CefRenderHandlerCToCpp::GetAccessibilityHandler() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_accessibility_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_accessibility_handler_t* _retval =
+      _struct->get_accessibility_handler(_struct);
+
+  // Return type: refptr_same
+  return CefAccessibilityHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRenderHandlerCToCpp::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                               CefRect& rect) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_root_screen_rect))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->get_root_screen_rect(
+      _struct, CefBrowserCppToC::Wrap(browser), &rect);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::GetViewRect(CefRefPtr<CefBrowser> browser,
+                                         CefRect& rect) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_view_rect))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->get_view_rect(_struct, CefBrowserCppToC::Wrap(browser), &rect);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRenderHandlerCToCpp::GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                            int viewX,
+                                            int viewY,
+                                            int& screenX,
+                                            int& screenY) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_screen_point))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->get_screen_point(_struct, CefBrowserCppToC::Wrap(browser), viewX,
+                                viewY, &screenX, &screenY);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRenderHandlerCToCpp::GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                                           CefScreenInfo& screen_info) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_screen_info))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->get_screen_info(
+      _struct, CefBrowserCppToC::Wrap(browser), &screen_info);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnPopupShow(CefRefPtr<CefBrowser> browser,
+                                         bool show) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_popup_show))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_popup_show(_struct, CefBrowserCppToC::Wrap(browser), show);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                                         const CefRect& rect) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_popup_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_popup_size(_struct, CefBrowserCppToC::Wrap(browser), &rect);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnPaint(CefRefPtr<CefBrowser> browser,
+                                     PaintElementType type,
+                                     const RectList& dirtyRects,
+                                     const void* buffer,
+                                     int width,
+                                     int height) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_paint))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return;
+
+  // Translate param: dirtyRects; type: simple_vec_byref_const
+  const size_t dirtyRectsCount = dirtyRects.size();
+  cef_rect_t* dirtyRectsList = NULL;
+  if (dirtyRectsCount > 0) {
+    dirtyRectsList = new cef_rect_t[dirtyRectsCount];
+    DCHECK(dirtyRectsList);
+    if (dirtyRectsList) {
+      for (size_t i = 0; i < dirtyRectsCount; ++i) {
+        dirtyRectsList[i] = dirtyRects[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->on_paint(_struct, CefBrowserCppToC::Wrap(browser), type,
+                    dirtyRectsCount, dirtyRectsList, buffer, width, height);
+
+  // Restore param:dirtyRects; type: simple_vec_byref_const
+  if (dirtyRectsList)
+    delete[] dirtyRectsList;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                                                PaintElementType type,
+                                                const RectList& dirtyRects,
+                                                void* shared_handle) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_accelerated_paint))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: shared_handle; type: simple_byaddr
+  DCHECK(shared_handle);
+  if (!shared_handle)
+    return;
+
+  // Translate param: dirtyRects; type: simple_vec_byref_const
+  const size_t dirtyRectsCount = dirtyRects.size();
+  cef_rect_t* dirtyRectsList = NULL;
+  if (dirtyRectsCount > 0) {
+    dirtyRectsList = new cef_rect_t[dirtyRectsCount];
+    DCHECK(dirtyRectsList);
+    if (dirtyRectsList) {
+      for (size_t i = 0; i < dirtyRectsCount; ++i) {
+        dirtyRectsList[i] = dirtyRects[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->on_accelerated_paint(_struct, CefBrowserCppToC::Wrap(browser), type,
+                                dirtyRectsCount, dirtyRectsList, shared_handle);
+
+  // Restore param:dirtyRects; type: simple_vec_byref_const
+  if (dirtyRectsList)
+    delete[] dirtyRectsList;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnCursorChange(
+    CefRefPtr<CefBrowser> browser,
+    CefCursorHandle cursor,
+    CursorType type,
+    const CefCursorInfo& custom_cursor_info) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_cursor_change))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_cursor_change(_struct, CefBrowserCppToC::Wrap(browser), cursor,
+                            type, &custom_cursor_info);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRenderHandlerCToCpp::StartDragging(CefRefPtr<CefBrowser> browser,
+                                           CefRefPtr<CefDragData> drag_data,
+                                           DragOperationsMask allowed_ops,
+                                           int x,
+                                           int y) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, start_dragging))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: drag_data; type: refptr_diff
+  DCHECK(drag_data.get());
+  if (!drag_data.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->start_dragging(
+      _struct, CefBrowserCppToC::Wrap(browser),
+      CefDragDataCppToC::Wrap(drag_data), allowed_ops, x, y);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                                              DragOperation operation) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, update_drag_cursor))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->update_drag_cursor(_struct, CefBrowserCppToC::Wrap(browser),
+                              operation);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnScrollOffsetChanged(
+    CefRefPtr<CefBrowser> browser,
+    double x,
+    double y) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_scroll_offset_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_scroll_offset_changed(_struct, CefBrowserCppToC::Wrap(browser), x,
+                                    y);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnImeCompositionRangeChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefRange& selected_range,
+    const RectList& character_bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_ime_composition_range_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Translate param: character_bounds; type: simple_vec_byref_const
+  const size_t character_boundsCount = character_bounds.size();
+  cef_rect_t* character_boundsList = NULL;
+  if (character_boundsCount > 0) {
+    character_boundsList = new cef_rect_t[character_boundsCount];
+    DCHECK(character_boundsList);
+    if (character_boundsList) {
+      for (size_t i = 0; i < character_boundsCount; ++i) {
+        character_boundsList[i] = character_bounds[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->on_ime_composition_range_changed(
+      _struct, CefBrowserCppToC::Wrap(browser), &selected_range,
+      character_boundsCount, character_boundsList);
+
+  // Restore param:character_bounds; type: simple_vec_byref_const
+  if (character_boundsList)
+    delete[] character_boundsList;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnTextSelectionChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& selected_text,
+    const CefRange& selected_range) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_text_selection_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Unverified params: selected_text, selected_range
+
+  // Execute
+  _struct->on_text_selection_changed(_struct, CefBrowserCppToC::Wrap(browser),
+                                     selected_text.GetStruct(),
+                                     &selected_range);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderHandlerCToCpp::OnVirtualKeyboardRequested(
+    CefRefPtr<CefBrowser> browser,
+    TextInputMode input_mode) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_render_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_virtual_keyboard_requested))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_virtual_keyboard_requested(
+      _struct, CefBrowserCppToC::Wrap(browser), input_mode);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRenderHandlerCToCpp::CefRenderHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRenderHandlerCToCpp::~CefRenderHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_render_handler_t*
+CefCToCppRefCounted<CefRenderHandlerCToCpp,
+                    CefRenderHandler,
+                    cef_render_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefRenderHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRenderHandlerCToCpp,
+                                   CefRenderHandler,
+                                   cef_render_handler_t>::kWrapperType =
+    WT_RENDER_HANDLER;
diff --git a/src/libcef_dll/ctocpp/render_handler_ctocpp.h b/src/libcef_dll/ctocpp/render_handler_ctocpp.h
new file mode 100644
index 0000000..d0e1247
--- /dev/null
+++ b/src/libcef_dll/ctocpp/render_handler_ctocpp.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ed8882abe62f08d4746a646307ef74d4987780c4$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RENDER_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RENDER_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_render_handler_capi.h"
+#include "include/cef_render_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefRenderHandlerCToCpp
+    : public CefCToCppRefCounted<CefRenderHandlerCToCpp,
+                                 CefRenderHandler,
+                                 cef_render_handler_t> {
+ public:
+  CefRenderHandlerCToCpp();
+  virtual ~CefRenderHandlerCToCpp();
+
+  // CefRenderHandler methods.
+  CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() override;
+  bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override;
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override;
+  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                      int viewX,
+                      int viewY,
+                      int& screenX,
+                      int& screenY) override;
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) override;
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) override;
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) override;
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               PaintElementType type,
+               const RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) override;
+  void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                          PaintElementType type,
+                          const RectList& dirtyRects,
+                          void* shared_handle) override;
+  void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                      CefCursorHandle cursor,
+                      CursorType type,
+                      const CefCursorInfo& custom_cursor_info) override;
+  bool StartDragging(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefDragData> drag_data,
+                     DragOperationsMask allowed_ops,
+                     int x,
+                     int y) override;
+  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                        DragOperation operation) override;
+  void OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser,
+                             double x,
+                             double y) override;
+  void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
+                                    const CefRange& selected_range,
+                                    const RectList& character_bounds) override;
+  void OnTextSelectionChanged(CefRefPtr<CefBrowser> browser,
+                              const CefString& selected_text,
+                              const CefRange& selected_range) override;
+  void OnVirtualKeyboardRequested(CefRefPtr<CefBrowser> browser,
+                                  TextInputMode input_mode) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RENDER_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/render_process_handler_ctocpp.cc b/src/libcef_dll/ctocpp/render_process_handler_ctocpp.cc
new file mode 100644
index 0000000..dce8cb0
--- /dev/null
+++ b/src/libcef_dll/ctocpp/render_process_handler_ctocpp.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=084106863bdaf595427b0d121362e207b15c66bf$
+//
+
+#include "libcef_dll/ctocpp/render_process_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/domnode_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/list_value_cpptoc.h"
+#include "libcef_dll/cpptoc/process_message_cpptoc.h"
+#include "libcef_dll/cpptoc/v8context_cpptoc.h"
+#include "libcef_dll/cpptoc/v8exception_cpptoc.h"
+#include "libcef_dll/cpptoc/v8stack_trace_cpptoc.h"
+#include "libcef_dll/ctocpp/load_handler_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnRenderThreadCreated(
+    CefRefPtr<CefListValue> extra_info) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_render_thread_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extra_info; type: refptr_diff
+  DCHECK(extra_info.get());
+  if (!extra_info.get())
+    return;
+
+  // Execute
+  _struct->on_render_thread_created(_struct,
+                                    CefListValueCppToC::Wrap(extra_info));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnWebKitInitialized() {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_web_kit_initialized))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_web_kit_initialized(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnBrowserCreated(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDictionaryValue> extra_info) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_browser_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: extra_info; type: refptr_diff
+  DCHECK(extra_info.get());
+  if (!extra_info.get())
+    return;
+
+  // Execute
+  _struct->on_browser_created(_struct, CefBrowserCppToC::Wrap(browser),
+                              CefDictionaryValueCppToC::Wrap(extra_info));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnBrowserDestroyed(
+    CefRefPtr<CefBrowser> browser) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_browser_destroyed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_browser_destroyed(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLoadHandler> CefRenderProcessHandlerCToCpp::GetLoadHandler() {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_load_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_load_handler_t* _retval = _struct->get_load_handler(_struct);
+
+  // Return type: refptr_same
+  return CefLoadHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnContextCreated(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefV8Context> context) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_context_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+  // Verify param: context; type: refptr_diff
+  DCHECK(context.get());
+  if (!context.get())
+    return;
+
+  // Execute
+  _struct->on_context_created(_struct, CefBrowserCppToC::Wrap(browser),
+                              CefFrameCppToC::Wrap(frame),
+                              CefV8ContextCppToC::Wrap(context));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnContextReleased(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefV8Context> context) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_context_released))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+  // Verify param: context; type: refptr_diff
+  DCHECK(context.get());
+  if (!context.get())
+    return;
+
+  // Execute
+  _struct->on_context_released(_struct, CefBrowserCppToC::Wrap(browser),
+                               CefFrameCppToC::Wrap(frame),
+                               CefV8ContextCppToC::Wrap(context));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnUncaughtException(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefV8Context> context,
+    CefRefPtr<CefV8Exception> exception,
+    CefRefPtr<CefV8StackTrace> stackTrace) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_uncaught_exception))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+  // Verify param: context; type: refptr_diff
+  DCHECK(context.get());
+  if (!context.get())
+    return;
+  // Verify param: exception; type: refptr_diff
+  DCHECK(exception.get());
+  if (!exception.get())
+    return;
+  // Verify param: stackTrace; type: refptr_diff
+  DCHECK(stackTrace.get());
+  if (!stackTrace.get())
+    return;
+
+  // Execute
+  _struct->on_uncaught_exception(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefV8ContextCppToC::Wrap(context), CefV8ExceptionCppToC::Wrap(exception),
+      CefV8StackTraceCppToC::Wrap(stackTrace));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRenderProcessHandlerCToCpp::OnFocusedNodeChanged(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefDOMNode> node) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_focused_node_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Unverified params: frame, node
+
+  // Execute
+  _struct->on_focused_node_changed(_struct, CefBrowserCppToC::Wrap(browser),
+                                   CefFrameCppToC::Wrap(frame),
+                                   CefDOMNodeCppToC::Wrap(node));
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRenderProcessHandlerCToCpp::OnProcessMessageReceived(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefProcessId source_process,
+    CefRefPtr<CefProcessMessage> message) {
+  cef_render_process_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_process_message_received))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return false;
+  // Verify param: message; type: refptr_diff
+  DCHECK(message.get());
+  if (!message.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_process_message_received(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      source_process, CefProcessMessageCppToC::Wrap(message));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRenderProcessHandlerCToCpp::CefRenderProcessHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRenderProcessHandlerCToCpp::~CefRenderProcessHandlerCToCpp() {}
+
+template <>
+cef_render_process_handler_t* CefCToCppRefCounted<
+    CefRenderProcessHandlerCToCpp,
+    CefRenderProcessHandler,
+    cef_render_process_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                 CefRenderProcessHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRenderProcessHandlerCToCpp,
+                                   CefRenderProcessHandler,
+                                   cef_render_process_handler_t>::kWrapperType =
+    WT_RENDER_PROCESS_HANDLER;
diff --git a/src/libcef_dll/ctocpp/render_process_handler_ctocpp.h b/src/libcef_dll/ctocpp/render_process_handler_ctocpp.h
new file mode 100644
index 0000000..82ea35e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/render_process_handler_ctocpp.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=37559903bacc38b0c955e1471f0d0dbeb79b11a8$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RENDER_PROCESS_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RENDER_PROCESS_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_render_process_handler_capi.h"
+#include "include/cef_render_process_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefRenderProcessHandlerCToCpp
+    : public CefCToCppRefCounted<CefRenderProcessHandlerCToCpp,
+                                 CefRenderProcessHandler,
+                                 cef_render_process_handler_t> {
+ public:
+  CefRenderProcessHandlerCToCpp();
+  virtual ~CefRenderProcessHandlerCToCpp();
+
+  // CefRenderProcessHandler methods.
+  void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) override;
+  void OnWebKitInitialized() override;
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override;
+  void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) override;
+  CefRefPtr<CefLoadHandler> GetLoadHandler() override;
+  void OnContextCreated(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefV8Context> context) override;
+  void OnContextReleased(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefV8Context> context) override;
+  void OnUncaughtException(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefV8Context> context,
+                           CefRefPtr<CefV8Exception> exception,
+                           CefRefPtr<CefV8StackTrace> stackTrace) override;
+  void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefDOMNode> node) override;
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RENDER_PROCESS_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/request_callback_ctocpp.cc b/src/libcef_dll/ctocpp/request_callback_ctocpp.cc
new file mode 100644
index 0000000..eb81c8c
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_callback_ctocpp.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b1c7af22f4bb4b7da664dc6bb7bd057efda70222$
+//
+
+#include "libcef_dll/ctocpp/request_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefRequestCallbackCToCpp::Continue(bool allow) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cont(_struct, allow);
+}
+
+NO_SANITIZE("cfi-icall") void CefRequestCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestCallbackCToCpp::CefRequestCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestCallbackCToCpp::~CefRequestCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_request_callback_t* CefCToCppRefCounted<
+    CefRequestCallbackCToCpp,
+    CefRequestCallback,
+    cef_request_callback_t>::UnwrapDerived(CefWrapperType type,
+                                           CefRequestCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRequestCallbackCToCpp,
+                                   CefRequestCallback,
+                                   cef_request_callback_t>::kWrapperType =
+    WT_REQUEST_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/request_callback_ctocpp.h b/src/libcef_dll/ctocpp/request_callback_ctocpp.h
new file mode 100644
index 0000000..c6c6175
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_callback_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=11018a46ec73650d65701ec3464f7eb438b2e6c2$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_REQUEST_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_callback_capi.h"
+#include "include/cef_request_callback.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRequestCallbackCToCpp
+    : public CefCToCppRefCounted<CefRequestCallbackCToCpp,
+                                 CefRequestCallback,
+                                 cef_request_callback_t> {
+ public:
+  CefRequestCallbackCToCpp();
+  virtual ~CefRequestCallbackCToCpp();
+
+  // CefRequestCallback methods.
+  void Continue(bool allow) OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_REQUEST_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/request_context_ctocpp.cc b/src/libcef_dll/ctocpp/request_context_ctocpp.cc
new file mode 100644
index 0000000..b3a9ac4
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_context_ctocpp.cc
@@ -0,0 +1,566 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a29d8f9e49143e42a0ae0204c7d439c76b3c371c$
+//
+
+#include "libcef_dll/ctocpp/request_context_ctocpp.h"
+#include "libcef_dll/cpptoc/completion_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/extension_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/request_context_handler_cpptoc.h"
+#include "libcef_dll/cpptoc/resolve_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h"
+#include "libcef_dll/ctocpp/cookie_manager_ctocpp.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/extension_ctocpp.h"
+#include "libcef_dll/ctocpp/media_router_ctocpp.h"
+#include "libcef_dll/ctocpp/value_ctocpp.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequestContext> CefRequestContext::GetGlobalContext() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_request_context_t* _retval = cef_request_context_get_global_context();
+
+  // Return type: refptr_same
+  return CefRequestContextCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequestContext> CefRequestContext::CreateContext(
+    const CefRequestContextSettings& settings,
+    CefRefPtr<CefRequestContextHandler> handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: handler
+
+  // Execute
+  cef_request_context_t* _retval = cef_request_context_create_context(
+      &settings, CefRequestContextHandlerCppToC::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefRequestContextCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequestContext> CefRequestContext::CreateContext(
+    CefRefPtr<CefRequestContext> other,
+    CefRefPtr<CefRequestContextHandler> handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: other; type: refptr_same
+  DCHECK(other.get());
+  if (!other.get())
+    return nullptr;
+  // Unverified params: handler
+
+  // Execute
+  cef_request_context_t* _retval =
+      cef_create_context_shared(CefRequestContextCToCpp::Unwrap(other),
+                                CefRequestContextHandlerCppToC::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefRequestContextCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::IsSame(CefRefPtr<CefRequestContext> other) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: other; type: refptr_same
+  DCHECK(other.get());
+  if (!other.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->is_same(_struct, CefRequestContextCToCpp::Unwrap(other));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::IsSharingWith(
+    CefRefPtr<CefRequestContext> other) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_sharing_with))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: other; type: refptr_same
+  DCHECK(other.get());
+  if (!other.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->is_sharing_with(_struct, CefRequestContextCToCpp::Unwrap(other));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefRequestContextCToCpp::IsGlobal() {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_global))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_global(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequestContextHandler> CefRequestContextCToCpp::GetHandler() {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_request_context_handler_t* _retval = _struct->get_handler(_struct);
+
+  // Return type: refptr_diff
+  return CefRequestContextHandlerCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefRequestContextCToCpp::GetCachePath() {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_cache_path))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_cache_path(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefCookieManager> CefRequestContextCToCpp::GetCookieManager(
+    CefRefPtr<CefCompletionCallback> callback) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_cookie_manager))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Execute
+  cef_cookie_manager_t* _retval = _struct->get_cookie_manager(
+      _struct, CefCompletionCallbackCppToC::Wrap(callback));
+
+  // Return type: refptr_same
+  return CefCookieManagerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::RegisterSchemeHandlerFactory(
+    const CefString& scheme_name,
+    const CefString& domain_name,
+    CefRefPtr<CefSchemeHandlerFactory> factory) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, register_scheme_handler_factory))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(!scheme_name.empty());
+  if (scheme_name.empty())
+    return false;
+  // Unverified params: domain_name, factory
+
+  // Execute
+  int _retval = _struct->register_scheme_handler_factory(
+      _struct, scheme_name.GetStruct(), domain_name.GetStruct(),
+      CefSchemeHandlerFactoryCppToC::Wrap(factory));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::ClearSchemeHandlerFactories() {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear_scheme_handler_factories))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->clear_scheme_handler_factories(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestContextCToCpp::PurgePluginListCache(bool reload_pages) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, purge_plugin_list_cache))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->purge_plugin_list_cache(_struct, reload_pages);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::HasPreference(const CefString& name) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_preference))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->has_preference(_struct, name.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefValue> CefRequestContextCToCpp::GetPreference(
+    const CefString& name) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_preference))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return nullptr;
+
+  // Execute
+  cef_value_t* _retval = _struct->get_preference(_struct, name.GetStruct());
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDictionaryValue> CefRequestContextCToCpp::GetAllPreferences(
+    bool include_defaults) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_all_preferences))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dictionary_value_t* _retval =
+      _struct->get_all_preferences(_struct, include_defaults);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::CanSetPreference(const CefString& name) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_set_preference))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->can_set_preference(_struct, name.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::SetPreference(const CefString& name,
+                                            CefRefPtr<CefValue> value,
+                                            CefString& error) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_preference))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+  // Unverified params: value
+
+  // Execute
+  int _retval = _struct->set_preference(_struct, name.GetStruct(),
+                                        CefValueCToCpp::Unwrap(value),
+                                        error.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestContextCToCpp::ClearCertificateExceptions(
+    CefRefPtr<CefCompletionCallback> callback) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear_certificate_exceptions))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Execute
+  _struct->clear_certificate_exceptions(
+      _struct, CefCompletionCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestContextCToCpp::ClearHttpAuthCredentials(
+    CefRefPtr<CefCompletionCallback> callback) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear_http_auth_credentials))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Execute
+  _struct->clear_http_auth_credentials(
+      _struct, CefCompletionCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestContextCToCpp::CloseAllConnections(
+    CefRefPtr<CefCompletionCallback> callback) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close_all_connections))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: callback
+
+  // Execute
+  _struct->close_all_connections(_struct,
+                                 CefCompletionCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestContextCToCpp::ResolveHost(
+    const CefString& origin,
+    CefRefPtr<CefResolveCallback> callback) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, resolve_host))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: origin; type: string_byref_const
+  DCHECK(!origin.empty());
+  if (origin.empty())
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  _struct->resolve_host(_struct, origin.GetStruct(),
+                        CefResolveCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestContextCToCpp::LoadExtension(
+    const CefString& root_directory,
+    CefRefPtr<CefDictionaryValue> manifest,
+    CefRefPtr<CefExtensionHandler> handler) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, load_extension))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: root_directory; type: string_byref_const
+  DCHECK(!root_directory.empty());
+  if (root_directory.empty())
+    return;
+  // Unverified params: manifest, handler
+
+  // Execute
+  _struct->load_extension(_struct, root_directory.GetStruct(),
+                          CefDictionaryValueCToCpp::Unwrap(manifest),
+                          CefExtensionHandlerCppToC::Wrap(handler));
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::DidLoadExtension(const CefString& extension_id) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, did_load_extension))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension_id; type: string_byref_const
+  DCHECK(!extension_id.empty());
+  if (extension_id.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->did_load_extension(_struct, extension_id.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::HasExtension(const CefString& extension_id) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_extension))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension_id; type: string_byref_const
+  DCHECK(!extension_id.empty());
+  if (extension_id.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->has_extension(_struct, extension_id.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextCToCpp::GetExtensions(
+    std::vector<CefString>& extension_ids) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_extensions))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: extension_ids; type: string_vec_byref
+  cef_string_list_t extension_idsList = cef_string_list_alloc();
+  DCHECK(extension_idsList);
+  if (extension_idsList)
+    transfer_string_list_contents(extension_ids, extension_idsList);
+
+  // Execute
+  int _retval = _struct->get_extensions(_struct, extension_idsList);
+
+  // Restore param:extension_ids; type: string_vec_byref
+  if (extension_idsList) {
+    extension_ids.clear();
+    transfer_string_list_contents(extension_idsList, extension_ids);
+    cef_string_list_free(extension_idsList);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefExtension> CefRequestContextCToCpp::GetExtension(
+    const CefString& extension_id) {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_extension))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension_id; type: string_byref_const
+  DCHECK(!extension_id.empty());
+  if (extension_id.empty())
+    return nullptr;
+
+  // Execute
+  cef_extension_t* _retval =
+      _struct->get_extension(_struct, extension_id.GetStruct());
+
+  // Return type: refptr_same
+  return CefExtensionCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMediaRouter> CefRequestContextCToCpp::GetMediaRouter() {
+  cef_request_context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_media_router))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_media_router_t* _retval = _struct->get_media_router(_struct);
+
+  // Return type: refptr_same
+  return CefMediaRouterCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestContextCToCpp::CefRequestContextCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestContextCToCpp::~CefRequestContextCToCpp() {}
+
+template <>
+cef_request_context_t* CefCToCppRefCounted<
+    CefRequestContextCToCpp,
+    CefRequestContext,
+    cef_request_context_t>::UnwrapDerived(CefWrapperType type,
+                                          CefRequestContext* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRequestContextCToCpp,
+                                   CefRequestContext,
+                                   cef_request_context_t>::kWrapperType =
+    WT_REQUEST_CONTEXT;
diff --git a/src/libcef_dll/ctocpp/request_context_ctocpp.h b/src/libcef_dll/ctocpp/request_context_ctocpp.h
new file mode 100644
index 0000000..f5d9f6e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_context_ctocpp.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7841eec57c44171080b5d958ca03760cbe23f22b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_CONTEXT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_REQUEST_CONTEXT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_request_context_capi.h"
+#include "include/capi/cef_request_context_handler_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_request_context.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRequestContextCToCpp
+    : public CefCToCppRefCounted<CefRequestContextCToCpp,
+                                 CefRequestContext,
+                                 cef_request_context_t> {
+ public:
+  CefRequestContextCToCpp();
+  virtual ~CefRequestContextCToCpp();
+
+  // CefRequestContext methods.
+  bool IsSame(CefRefPtr<CefRequestContext> other) OVERRIDE;
+  bool IsSharingWith(CefRefPtr<CefRequestContext> other) OVERRIDE;
+  bool IsGlobal() OVERRIDE;
+  CefRefPtr<CefRequestContextHandler> GetHandler() OVERRIDE;
+  CefString GetCachePath() OVERRIDE;
+  CefRefPtr<CefCookieManager> GetCookieManager(
+      CefRefPtr<CefCompletionCallback> callback) OVERRIDE;
+  bool RegisterSchemeHandlerFactory(
+      const CefString& scheme_name,
+      const CefString& domain_name,
+      CefRefPtr<CefSchemeHandlerFactory> factory) OVERRIDE;
+  bool ClearSchemeHandlerFactories() OVERRIDE;
+  void PurgePluginListCache(bool reload_pages) OVERRIDE;
+  bool HasPreference(const CefString& name) OVERRIDE;
+  CefRefPtr<CefValue> GetPreference(const CefString& name) OVERRIDE;
+  CefRefPtr<CefDictionaryValue> GetAllPreferences(
+      bool include_defaults) OVERRIDE;
+  bool CanSetPreference(const CefString& name) OVERRIDE;
+  bool SetPreference(const CefString& name,
+                     CefRefPtr<CefValue> value,
+                     CefString& error) OVERRIDE;
+  void ClearCertificateExceptions(
+      CefRefPtr<CefCompletionCallback> callback) OVERRIDE;
+  void ClearHttpAuthCredentials(
+      CefRefPtr<CefCompletionCallback> callback) OVERRIDE;
+  void CloseAllConnections(CefRefPtr<CefCompletionCallback> callback) OVERRIDE;
+  void ResolveHost(const CefString& origin,
+                   CefRefPtr<CefResolveCallback> callback) OVERRIDE;
+  void LoadExtension(const CefString& root_directory,
+                     CefRefPtr<CefDictionaryValue> manifest,
+                     CefRefPtr<CefExtensionHandler> handler) OVERRIDE;
+  bool DidLoadExtension(const CefString& extension_id) OVERRIDE;
+  bool HasExtension(const CefString& extension_id) OVERRIDE;
+  bool GetExtensions(std::vector<CefString>& extension_ids) OVERRIDE;
+  CefRefPtr<CefExtension> GetExtension(const CefString& extension_id) OVERRIDE;
+  CefRefPtr<CefMediaRouter> GetMediaRouter() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_REQUEST_CONTEXT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/request_context_handler_ctocpp.cc b/src/libcef_dll/ctocpp/request_context_handler_ctocpp.cc
new file mode 100644
index 0000000..3e24ead
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_context_handler_ctocpp.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=afc36702ea14f7e553999aad62155a4bd189b3b4$
+//
+
+#include "libcef_dll/ctocpp/request_context_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/request_context_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/web_plugin_info_cpptoc.h"
+#include "libcef_dll/ctocpp/resource_request_handler_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefRequestContextHandlerCToCpp::OnRequestContextInitialized(
+    CefRefPtr<CefRequestContext> request_context) {
+  cef_request_context_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_request_context_initialized))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request_context; type: refptr_diff
+  DCHECK(request_context.get());
+  if (!request_context.get())
+    return;
+
+  // Execute
+  _struct->on_request_context_initialized(
+      _struct, CefRequestContextCppToC::Wrap(request_context));
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestContextHandlerCToCpp::OnBeforePluginLoad(
+    const CefString& mime_type,
+    const CefString& plugin_url,
+    bool is_main_frame,
+    const CefString& top_origin_url,
+    CefRefPtr<CefWebPluginInfo> plugin_info,
+    PluginPolicy* plugin_policy) {
+  cef_request_context_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_plugin_load))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: mime_type; type: string_byref_const
+  DCHECK(!mime_type.empty());
+  if (mime_type.empty())
+    return false;
+  // Verify param: plugin_info; type: refptr_diff
+  DCHECK(plugin_info.get());
+  if (!plugin_info.get())
+    return false;
+  // Verify param: plugin_policy; type: simple_byaddr
+  DCHECK(plugin_policy);
+  if (!plugin_policy)
+    return false;
+  // Unverified params: plugin_url, top_origin_url
+
+  // Execute
+  int _retval = _struct->on_before_plugin_load(
+      _struct, mime_type.GetStruct(), plugin_url.GetStruct(), is_main_frame,
+      top_origin_url.GetStruct(), CefWebPluginInfoCppToC::Wrap(plugin_info),
+      plugin_policy);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResourceRequestHandler>
+CefRequestContextHandlerCToCpp::GetResourceRequestHandler(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    bool is_navigation,
+    bool is_download,
+    const CefString& request_initiator,
+    bool& disable_default_handling) {
+  cef_request_context_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_resource_request_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Unverified params: browser, frame, request_initiator
+
+  // Translate param: disable_default_handling; type: bool_byref
+  int disable_default_handlingInt = disable_default_handling;
+
+  // Execute
+  cef_resource_request_handler_t* _retval =
+      _struct->get_resource_request_handler(
+          _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+          CefRequestCppToC::Wrap(request), is_navigation, is_download,
+          request_initiator.GetStruct(), &disable_default_handlingInt);
+
+  // Restore param:disable_default_handling; type: bool_byref
+  disable_default_handling = disable_default_handlingInt ? true : false;
+
+  // Return type: refptr_same
+  return CefResourceRequestHandlerCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestContextHandlerCToCpp::CefRequestContextHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestContextHandlerCToCpp::~CefRequestContextHandlerCToCpp() {}
+
+template <>
+cef_request_context_handler_t* CefCToCppRefCounted<
+    CefRequestContextHandlerCToCpp,
+    CefRequestContextHandler,
+    cef_request_context_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                  CefRequestContextHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefRequestContextHandlerCToCpp,
+                        CefRequestContextHandler,
+                        cef_request_context_handler_t>::kWrapperType =
+        WT_REQUEST_CONTEXT_HANDLER;
diff --git a/src/libcef_dll/ctocpp/request_context_handler_ctocpp.h b/src/libcef_dll/ctocpp/request_context_handler_ctocpp.h
new file mode 100644
index 0000000..50dcccf
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_context_handler_ctocpp.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c73c0a4833d4fd80e27c48d6a00f961f3d93084f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_CONTEXT_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_REQUEST_CONTEXT_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_context_handler_capi.h"
+#include "include/cef_request_context_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefRequestContextHandlerCToCpp
+    : public CefCToCppRefCounted<CefRequestContextHandlerCToCpp,
+                                 CefRequestContextHandler,
+                                 cef_request_context_handler_t> {
+ public:
+  CefRequestContextHandlerCToCpp();
+  virtual ~CefRequestContextHandlerCToCpp();
+
+  // CefRequestContextHandler methods.
+  void OnRequestContextInitialized(
+      CefRefPtr<CefRequestContext> request_context) override;
+  bool OnBeforePluginLoad(const CefString& mime_type,
+                          const CefString& plugin_url,
+                          bool is_main_frame,
+                          const CefString& top_origin_url,
+                          CefRefPtr<CefWebPluginInfo> plugin_info,
+                          PluginPolicy* plugin_policy) override;
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_REQUEST_CONTEXT_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/request_ctocpp.cc b/src/libcef_dll/ctocpp/request_ctocpp.cc
new file mode 100644
index 0000000..3279406
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_ctocpp.cc
@@ -0,0 +1,436 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=68730b3bdc2f7d7fb673a3321fa6e4b17f8a4011$
+//
+
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/post_data_ctocpp.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefRequest> CefRequest::Create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_request_t* _retval = cef_request_create();
+
+  // Return type: refptr_same
+  return CefRequestCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefRequestCToCpp::IsReadOnly() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefRequestCToCpp::GetURL() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") void CefRequestCToCpp::SetURL(const CefString& url) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_url))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return;
+
+  // Execute
+  _struct->set_url(_struct, url.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefRequestCToCpp::GetMethod() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_method))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_method(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::SetMethod(const CefString& method) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_method))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: method; type: string_byref_const
+  DCHECK(!method.empty());
+  if (method.empty())
+    return;
+
+  // Execute
+  _struct->set_method(_struct, method.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::SetReferrer(const CefString& referrer_url,
+                                   ReferrerPolicy policy) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_referrer))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: referrer_url
+
+  // Execute
+  _struct->set_referrer(_struct, referrer_url.GetStruct(), policy);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefRequestCToCpp::GetReferrerURL() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_referrer_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_referrer_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRequest::ReferrerPolicy CefRequestCToCpp::GetReferrerPolicy() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_referrer_policy))
+    return REFERRER_POLICY_DEFAULT;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_referrer_policy_t _retval = _struct->get_referrer_policy(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefPostData> CefRequestCToCpp::GetPostData() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_post_data))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_post_data_t* _retval = _struct->get_post_data(_struct);
+
+  // Return type: refptr_same
+  return CefPostDataCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::SetPostData(CefRefPtr<CefPostData> postData) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_post_data))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: postData; type: refptr_same
+  DCHECK(postData.get());
+  if (!postData.get())
+    return;
+
+  // Execute
+  _struct->set_post_data(_struct, CefPostDataCToCpp::Unwrap(postData));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::GetHeaderMap(HeaderMap& headerMap) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_header_map))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: headerMap; type: string_map_multi_byref
+  cef_string_multimap_t headerMapMultimap = cef_string_multimap_alloc();
+  DCHECK(headerMapMultimap);
+  if (headerMapMultimap)
+    transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  _struct->get_header_map(_struct, headerMapMultimap);
+
+  // Restore param:headerMap; type: string_map_multi_byref
+  if (headerMapMultimap) {
+    headerMap.clear();
+    transfer_string_multimap_contents(headerMapMultimap, headerMap);
+    cef_string_multimap_free(headerMapMultimap);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::SetHeaderMap(const HeaderMap& headerMap) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_header_map))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: headerMap; type: string_map_multi_byref_const
+  cef_string_multimap_t headerMapMultimap = cef_string_multimap_alloc();
+  DCHECK(headerMapMultimap);
+  if (headerMapMultimap)
+    transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  _struct->set_header_map(_struct, headerMapMultimap);
+
+  // Restore param:headerMap; type: string_map_multi_byref_const
+  if (headerMapMultimap)
+    cef_string_multimap_free(headerMapMultimap);
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefRequestCToCpp::GetHeaderByName(const CefString& name) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_header_by_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_header_by_name(_struct, name.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::SetHeaderByName(const CefString& name,
+                                       const CefString& value,
+                                       bool overwrite) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_header_by_name))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+  // Unverified params: value
+
+  // Execute
+  _struct->set_header_by_name(_struct, name.GetStruct(), value.GetStruct(),
+                              overwrite);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::Set(const CefString& url,
+                           const CefString& method,
+                           CefRefPtr<CefPostData> postData,
+                           const HeaderMap& headerMap) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return;
+  // Verify param: method; type: string_byref_const
+  DCHECK(!method.empty());
+  if (method.empty())
+    return;
+  // Unverified params: postData
+
+  // Translate param: headerMap; type: string_map_multi_byref_const
+  cef_string_multimap_t headerMapMultimap = cef_string_multimap_alloc();
+  DCHECK(headerMapMultimap);
+  if (headerMapMultimap)
+    transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  _struct->set(_struct, url.GetStruct(), method.GetStruct(),
+               CefPostDataCToCpp::Unwrap(postData), headerMapMultimap);
+
+  // Restore param:headerMap; type: string_map_multi_byref_const
+  if (headerMapMultimap)
+    cef_string_multimap_free(headerMapMultimap);
+}
+
+NO_SANITIZE("cfi-icall") int CefRequestCToCpp::GetFlags() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_flags))
+    return UR_FLAG_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_flags(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefRequestCToCpp::SetFlags(int flags) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_flags))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_flags(_struct, flags);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefRequestCToCpp::GetFirstPartyForCookies() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_first_party_for_cookies))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_first_party_for_cookies(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestCToCpp::SetFirstPartyForCookies(const CefString& url) {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_first_party_for_cookies))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: url
+
+  // Execute
+  _struct->set_first_party_for_cookies(_struct, url.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CefRequest::ResourceType CefRequestCToCpp::GetResourceType() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_resource_type))
+    return RT_SUB_RESOURCE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_resource_type_t _retval = _struct->get_resource_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRequest::TransitionType CefRequestCToCpp::GetTransitionType() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_transition_type))
+    return TT_EXPLICIT;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_transition_type_t _retval = _struct->get_transition_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") uint64 CefRequestCToCpp::GetIdentifier() {
+  cef_request_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_identifier))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  uint64 _retval = _struct->get_identifier(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestCToCpp::CefRequestCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestCToCpp::~CefRequestCToCpp() {}
+
+template <>
+cef_request_t*
+CefCToCppRefCounted<CefRequestCToCpp, CefRequest, cef_request_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefRequest* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRequestCToCpp,
+                                   CefRequest,
+                                   cef_request_t>::kWrapperType = WT_REQUEST;
diff --git a/src/libcef_dll/ctocpp/request_ctocpp.h b/src/libcef_dll/ctocpp/request_ctocpp.h
new file mode 100644
index 0000000..7cf586e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_ctocpp.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=08dbc905e15058879ec55a1921031eb04460801e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_REQUEST_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_capi.h"
+#include "include/cef_request.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRequestCToCpp
+    : public CefCToCppRefCounted<CefRequestCToCpp, CefRequest, cef_request_t> {
+ public:
+  CefRequestCToCpp();
+  virtual ~CefRequestCToCpp();
+
+  // CefRequest methods.
+  bool IsReadOnly() OVERRIDE;
+  CefString GetURL() OVERRIDE;
+  void SetURL(const CefString& url) OVERRIDE;
+  CefString GetMethod() OVERRIDE;
+  void SetMethod(const CefString& method) OVERRIDE;
+  void SetReferrer(const CefString& referrer_url,
+                   ReferrerPolicy policy) OVERRIDE;
+  CefString GetReferrerURL() OVERRIDE;
+  ReferrerPolicy GetReferrerPolicy() OVERRIDE;
+  CefRefPtr<CefPostData> GetPostData() OVERRIDE;
+  void SetPostData(CefRefPtr<CefPostData> postData) OVERRIDE;
+  void GetHeaderMap(HeaderMap& headerMap) OVERRIDE;
+  void SetHeaderMap(const HeaderMap& headerMap) OVERRIDE;
+  CefString GetHeaderByName(const CefString& name) OVERRIDE;
+  void SetHeaderByName(const CefString& name,
+                       const CefString& value,
+                       bool overwrite) OVERRIDE;
+  void Set(const CefString& url,
+           const CefString& method,
+           CefRefPtr<CefPostData> postData,
+           const HeaderMap& headerMap) OVERRIDE;
+  int GetFlags() OVERRIDE;
+  void SetFlags(int flags) OVERRIDE;
+  CefString GetFirstPartyForCookies() OVERRIDE;
+  void SetFirstPartyForCookies(const CefString& url) OVERRIDE;
+  ResourceType GetResourceType() OVERRIDE;
+  TransitionType GetTransitionType() OVERRIDE;
+  uint64 GetIdentifier() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_REQUEST_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/request_handler_ctocpp.cc b/src/libcef_dll/ctocpp/request_handler_ctocpp.cc
new file mode 100644
index 0000000..0260f23
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_handler_ctocpp.cc
@@ -0,0 +1,442 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fdcb73f79369fe74f65f75d54f08aaec0f0e4dbf$
+//
+
+#include "libcef_dll/ctocpp/request_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/auth_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/request_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/sslinfo_cpptoc.h"
+#include "libcef_dll/cpptoc/x509certificate_cpptoc.h"
+#include "libcef_dll/ctocpp/resource_request_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestHandlerCToCpp::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                                             CefRefPtr<CefFrame> frame,
+                                             CefRefPtr<CefRequest> request,
+                                             bool user_gesture,
+                                             bool is_redirect) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_browse))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return false;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_before_browse(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), user_gesture, is_redirect);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestHandlerCToCpp::OnOpenURLFromTab(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    const CefString& target_url,
+    WindowOpenDisposition target_disposition,
+    bool user_gesture) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_open_urlfrom_tab))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return false;
+  // Verify param: target_url; type: string_byref_const
+  DCHECK(!target_url.empty());
+  if (target_url.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_open_urlfrom_tab(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      target_url.GetStruct(), target_disposition, user_gesture);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResourceRequestHandler>
+CefRequestHandlerCToCpp::GetResourceRequestHandler(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    bool is_navigation,
+    bool is_download,
+    const CefString& request_initiator,
+    bool& disable_default_handling) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_resource_request_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return nullptr;
+  // Verify param: frame; type: refptr_diff
+  DCHECK(frame.get());
+  if (!frame.get())
+    return nullptr;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Unverified params: request_initiator
+
+  // Translate param: disable_default_handling; type: bool_byref
+  int disable_default_handlingInt = disable_default_handling;
+
+  // Execute
+  cef_resource_request_handler_t* _retval =
+      _struct->get_resource_request_handler(
+          _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+          CefRequestCppToC::Wrap(request), is_navigation, is_download,
+          request_initiator.GetStruct(), &disable_default_handlingInt);
+
+  // Restore param:disable_default_handling; type: bool_byref
+  disable_default_handling = disable_default_handlingInt ? true : false;
+
+  // Return type: refptr_same
+  return CefResourceRequestHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestHandlerCToCpp::GetAuthCredentials(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& origin_url,
+    bool isProxy,
+    const CefString& host,
+    int port,
+    const CefString& realm,
+    const CefString& scheme,
+    CefRefPtr<CefAuthCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_auth_credentials))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: origin_url; type: string_byref_const
+  DCHECK(!origin_url.empty());
+  if (origin_url.empty())
+    return false;
+  // Verify param: host; type: string_byref_const
+  DCHECK(!host.empty());
+  if (host.empty())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+  // Unverified params: realm, scheme
+
+  // Execute
+  int _retval = _struct->get_auth_credentials(
+      _struct, CefBrowserCppToC::Wrap(browser), origin_url.GetStruct(), isProxy,
+      host.GetStruct(), port, realm.GetStruct(), scheme.GetStruct(),
+      CefAuthCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestHandlerCToCpp::OnQuotaRequest(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& origin_url,
+    int64 new_size,
+    CefRefPtr<CefRequestCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_quota_request))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: origin_url; type: string_byref_const
+  DCHECK(!origin_url.empty());
+  if (origin_url.empty())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_quota_request(
+      _struct, CefBrowserCppToC::Wrap(browser), origin_url.GetStruct(),
+      new_size, CefRequestCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestHandlerCToCpp::OnCertificateError(
+    CefRefPtr<CefBrowser> browser,
+    cef_errorcode_t cert_error,
+    const CefString& request_url,
+    CefRefPtr<CefSSLInfo> ssl_info,
+    CefRefPtr<CefRequestCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_certificate_error))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: request_url; type: string_byref_const
+  DCHECK(!request_url.empty());
+  if (request_url.empty())
+    return false;
+  // Verify param: ssl_info; type: refptr_diff
+  DCHECK(ssl_info.get());
+  if (!ssl_info.get())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_certificate_error(
+      _struct, CefBrowserCppToC::Wrap(browser), cert_error,
+      request_url.GetStruct(), CefSSLInfoCppToC::Wrap(ssl_info),
+      CefRequestCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefRequestHandlerCToCpp::OnSelectClientCertificate(
+    CefRefPtr<CefBrowser> browser,
+    bool isProxy,
+    const CefString& host,
+    int port,
+    const X509CertificateList& certificates,
+    CefRefPtr<CefSelectClientCertificateCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_select_client_certificate))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return false;
+  // Verify param: host; type: string_byref_const
+  DCHECK(!host.empty());
+  if (host.empty())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Translate param: certificates; type: refptr_vec_diff_byref_const
+  const size_t certificatesCount = certificates.size();
+  cef_x509certificate_t** certificatesList = NULL;
+  if (certificatesCount > 0) {
+    certificatesList = new cef_x509certificate_t*[certificatesCount];
+    DCHECK(certificatesList);
+    if (certificatesList) {
+      for (size_t i = 0; i < certificatesCount; ++i) {
+        certificatesList[i] = CefX509CertificateCppToC::Wrap(certificates[i]);
+      }
+    }
+  }
+
+  // Execute
+  int _retval = _struct->on_select_client_certificate(
+      _struct, CefBrowserCppToC::Wrap(browser), isProxy, host.GetStruct(), port,
+      certificatesCount, certificatesList,
+      CefSelectClientCertificateCallbackCppToC::Wrap(callback));
+
+  // Restore param:certificates; type: refptr_vec_diff_byref_const
+  if (certificatesList)
+    delete[] certificatesList;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestHandlerCToCpp::OnPluginCrashed(CefRefPtr<CefBrowser> browser,
+                                              const CefString& plugin_path) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_plugin_crashed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+  // Verify param: plugin_path; type: string_byref_const
+  DCHECK(!plugin_path.empty());
+  if (plugin_path.empty())
+    return;
+
+  // Execute
+  _struct->on_plugin_crashed(_struct, CefBrowserCppToC::Wrap(browser),
+                             plugin_path.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestHandlerCToCpp::OnRenderViewReady(CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_render_view_ready))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_render_view_ready(_struct, CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestHandlerCToCpp::OnRenderProcessTerminated(
+    CefRefPtr<CefBrowser> browser,
+    TerminationStatus status) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_render_process_terminated))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_render_process_terminated(
+      _struct, CefBrowserCppToC::Wrap(browser), status);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefRequestHandlerCToCpp::OnDocumentAvailableInMainFrame(
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_document_available_in_main_frame))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_document_available_in_main_frame(_struct,
+                                               CefBrowserCppToC::Wrap(browser));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRequestHandlerCToCpp::CefRequestHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRequestHandlerCToCpp::~CefRequestHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_request_handler_t* CefCToCppRefCounted<
+    CefRequestHandlerCToCpp,
+    CefRequestHandler,
+    cef_request_handler_t>::UnwrapDerived(CefWrapperType type,
+                                          CefRequestHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefRequestHandlerCToCpp,
+                                   CefRequestHandler,
+                                   cef_request_handler_t>::kWrapperType =
+    WT_REQUEST_HANDLER;
diff --git a/src/libcef_dll/ctocpp/request_handler_ctocpp.h b/src/libcef_dll/ctocpp/request_handler_ctocpp.h
new file mode 100644
index 0000000..8122cb6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/request_handler_ctocpp.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=be2bb80e8816f0eaad34382f05e2bec59dfe7fa7$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_request_handler_capi.h"
+#include "include/cef_request_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefRequestHandlerCToCpp
+    : public CefCToCppRefCounted<CefRequestHandlerCToCpp,
+                                 CefRequestHandler,
+                                 cef_request_handler_t> {
+ public:
+  CefRequestHandlerCToCpp();
+  virtual ~CefRequestHandlerCToCpp();
+
+  // CefRequestHandler methods.
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override;
+  bool OnOpenURLFromTab(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        const CefString& target_url,
+                        WindowOpenDisposition target_disposition,
+                        bool user_gesture) override;
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override;
+  bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
+                          const CefString& origin_url,
+                          bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) override;
+  bool OnQuotaRequest(CefRefPtr<CefBrowser> browser,
+                      const CefString& origin_url,
+                      int64 new_size,
+                      CefRefPtr<CefRequestCallback> callback) override;
+  bool OnCertificateError(CefRefPtr<CefBrowser> browser,
+                          cef_errorcode_t cert_error,
+                          const CefString& request_url,
+                          CefRefPtr<CefSSLInfo> ssl_info,
+                          CefRefPtr<CefRequestCallback> callback) override;
+  bool OnSelectClientCertificate(
+      CefRefPtr<CefBrowser> browser,
+      bool isProxy,
+      const CefString& host,
+      int port,
+      const X509CertificateList& certificates,
+      CefRefPtr<CefSelectClientCertificateCallback> callback) override;
+  void OnPluginCrashed(CefRefPtr<CefBrowser> browser,
+                       const CefString& plugin_path) override;
+  void OnRenderViewReady(CefRefPtr<CefBrowser> browser) override;
+  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                 TerminationStatus status) override;
+  void OnDocumentAvailableInMainFrame(CefRefPtr<CefBrowser> browser) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/resolve_callback_ctocpp.cc b/src/libcef_dll/ctocpp/resolve_callback_ctocpp.cc
new file mode 100644
index 0000000..2c09456
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resolve_callback_ctocpp.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=37265d073717bef3508ba6c42f944e7b27c7b1dd$
+//
+
+#include "libcef_dll/ctocpp/resolve_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefResolveCallbackCToCpp::OnResolveCompleted(
+    cef_errorcode_t result,
+    const std::vector<CefString>& resolved_ips) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resolve_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_resolve_completed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: resolved_ips
+
+  // Translate param: resolved_ips; type: string_vec_byref_const
+  cef_string_list_t resolved_ipsList = cef_string_list_alloc();
+  DCHECK(resolved_ipsList);
+  if (resolved_ipsList)
+    transfer_string_list_contents(resolved_ips, resolved_ipsList);
+
+  // Execute
+  _struct->on_resolve_completed(_struct, result, resolved_ipsList);
+
+  // Restore param:resolved_ips; type: string_vec_byref_const
+  if (resolved_ipsList)
+    cef_string_list_free(resolved_ipsList);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResolveCallbackCToCpp::CefResolveCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResolveCallbackCToCpp::~CefResolveCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_resolve_callback_t* CefCToCppRefCounted<
+    CefResolveCallbackCToCpp,
+    CefResolveCallback,
+    cef_resolve_callback_t>::UnwrapDerived(CefWrapperType type,
+                                           CefResolveCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefResolveCallbackCToCpp,
+                                   CefResolveCallback,
+                                   cef_resolve_callback_t>::kWrapperType =
+    WT_RESOLVE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/resolve_callback_ctocpp.h b/src/libcef_dll/ctocpp/resolve_callback_ctocpp.h
new file mode 100644
index 0000000..f54953e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resolve_callback_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=619ab895c105edfa7802d25fcdc6f5e8bb969295$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESOLVE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESOLVE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_request_context_capi.h"
+#include "include/capi/cef_request_context_handler_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_request_context.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefResolveCallbackCToCpp
+    : public CefCToCppRefCounted<CefResolveCallbackCToCpp,
+                                 CefResolveCallback,
+                                 cef_resolve_callback_t> {
+ public:
+  CefResolveCallbackCToCpp();
+  virtual ~CefResolveCallbackCToCpp();
+
+  // CefResolveCallback methods.
+  void OnResolveCompleted(cef_errorcode_t result,
+                          const std::vector<CefString>& resolved_ips) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESOLVE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/resource_bundle_ctocpp.cc b/src/libcef_dll/ctocpp/resource_bundle_ctocpp.cc
new file mode 100644
index 0000000..c4746fb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_bundle_ctocpp.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=026dabca1d46cfbe911d71ede9d5bb79cb7c553d$
+//
+
+#include "libcef_dll/ctocpp/resource_bundle_ctocpp.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResourceBundle> CefResourceBundle::GetGlobal() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_resource_bundle_t* _retval = cef_resource_bundle_get_global();
+
+  // Return type: refptr_same
+  return CefResourceBundleCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefString CefResourceBundleCToCpp::GetLocalizedString(int string_id) {
+  cef_resource_bundle_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_localized_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_localized_string(_struct, string_id);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceBundleCToCpp::GetDataResource(int resource_id,
+                                              void*& data,
+                                              size_t& data_size) {
+  cef_resource_bundle_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_data_resource))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval =
+      _struct->get_data_resource(_struct, resource_id, &data, &data_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceBundleCToCpp::GetDataResourceForScale(int resource_id,
+                                                      ScaleFactor scale_factor,
+                                                      void*& data,
+                                                      size_t& data_size) {
+  cef_resource_bundle_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_data_resource_for_scale))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_data_resource_for_scale(
+      _struct, resource_id, scale_factor, &data, &data_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceBundleCToCpp::CefResourceBundleCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceBundleCToCpp::~CefResourceBundleCToCpp() {}
+
+template <>
+cef_resource_bundle_t* CefCToCppRefCounted<
+    CefResourceBundleCToCpp,
+    CefResourceBundle,
+    cef_resource_bundle_t>::UnwrapDerived(CefWrapperType type,
+                                          CefResourceBundle* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefResourceBundleCToCpp,
+                                   CefResourceBundle,
+                                   cef_resource_bundle_t>::kWrapperType =
+    WT_RESOURCE_BUNDLE;
diff --git a/src/libcef_dll/ctocpp/resource_bundle_ctocpp.h b/src/libcef_dll/ctocpp/resource_bundle_ctocpp.h
new file mode 100644
index 0000000..a99cc2e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_bundle_ctocpp.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c0d4e71c707c9d63a00ca1a497aaede3e429e970$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESOURCE_BUNDLE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESOURCE_BUNDLE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_resource_bundle_capi.h"
+#include "include/cef_resource_bundle.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResourceBundleCToCpp
+    : public CefCToCppRefCounted<CefResourceBundleCToCpp,
+                                 CefResourceBundle,
+                                 cef_resource_bundle_t> {
+ public:
+  CefResourceBundleCToCpp();
+  virtual ~CefResourceBundleCToCpp();
+
+  // CefResourceBundle methods.
+  CefString GetLocalizedString(int string_id) OVERRIDE;
+  bool GetDataResource(int resource_id,
+                       void*& data,
+                       size_t& data_size) OVERRIDE;
+  bool GetDataResourceForScale(int resource_id,
+                               ScaleFactor scale_factor,
+                               void*& data,
+                               size_t& data_size) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESOURCE_BUNDLE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/resource_bundle_handler_ctocpp.cc b/src/libcef_dll/ctocpp/resource_bundle_handler_ctocpp.cc
new file mode 100644
index 0000000..3e6536b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_bundle_handler_ctocpp.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e0a6432bb54e21c91fb9a82c708b04b36822c067$
+//
+
+#include "libcef_dll/ctocpp/resource_bundle_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceBundleHandlerCToCpp::GetLocalizedString(int string_id,
+                                                        CefString& string) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_bundle_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_localized_string))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_localized_string(_struct, string_id,
+                                              string.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceBundleHandlerCToCpp::GetDataResource(int resource_id,
+                                                     void*& data,
+                                                     size_t& data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_bundle_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_data_resource))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval =
+      _struct->get_data_resource(_struct, resource_id, &data, &data_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceBundleHandlerCToCpp::GetDataResourceForScale(
+    int resource_id,
+    ScaleFactor scale_factor,
+    void*& data,
+    size_t& data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_bundle_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_data_resource_for_scale))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_data_resource_for_scale(
+      _struct, resource_id, scale_factor, &data, &data_size);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceBundleHandlerCToCpp::CefResourceBundleHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceBundleHandlerCToCpp::~CefResourceBundleHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_resource_bundle_handler_t* CefCToCppRefCounted<
+    CefResourceBundleHandlerCToCpp,
+    CefResourceBundleHandler,
+    cef_resource_bundle_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                  CefResourceBundleHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefResourceBundleHandlerCToCpp,
+                        CefResourceBundleHandler,
+                        cef_resource_bundle_handler_t>::kWrapperType =
+        WT_RESOURCE_BUNDLE_HANDLER;
diff --git a/src/libcef_dll/ctocpp/resource_bundle_handler_ctocpp.h b/src/libcef_dll/ctocpp/resource_bundle_handler_ctocpp.h
new file mode 100644
index 0000000..7d49d11
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_bundle_handler_ctocpp.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0785b3534e482f12660e57106820da346885aad0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESOURCE_BUNDLE_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESOURCE_BUNDLE_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_resource_bundle_handler_capi.h"
+#include "include/cef_resource_bundle_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefResourceBundleHandlerCToCpp
+    : public CefCToCppRefCounted<CefResourceBundleHandlerCToCpp,
+                                 CefResourceBundleHandler,
+                                 cef_resource_bundle_handler_t> {
+ public:
+  CefResourceBundleHandlerCToCpp();
+  virtual ~CefResourceBundleHandlerCToCpp();
+
+  // CefResourceBundleHandler methods.
+  bool GetLocalizedString(int string_id, CefString& string) override;
+  bool GetDataResource(int resource_id,
+                       void*& data,
+                       size_t& data_size) override;
+  bool GetDataResourceForScale(int resource_id,
+                               ScaleFactor scale_factor,
+                               void*& data,
+                               size_t& data_size) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESOURCE_BUNDLE_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/resource_handler_ctocpp.cc b/src/libcef_dll/ctocpp/resource_handler_ctocpp.cc
new file mode 100644
index 0000000..5646f86
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_handler_ctocpp.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e26de314e30f8de5b6f082d0dda6e63b8e720368$
+//
+
+#include "libcef_dll/ctocpp/resource_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/callback_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/resource_read_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/resource_skip_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/response_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceHandlerCToCpp::Open(CefRefPtr<CefRequest> request,
+                                    bool& handle_request,
+                                    CefRefPtr<CefCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, open))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Translate param: handle_request; type: bool_byref
+  int handle_requestInt = handle_request;
+
+  // Execute
+  int _retval =
+      _struct->open(_struct, CefRequestCppToC::Wrap(request),
+                    &handle_requestInt, CefCallbackCppToC::Wrap(callback));
+
+  // Restore param:handle_request; type: bool_byref
+  handle_request = handle_requestInt ? true : false;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceHandlerCToCpp::ProcessRequest(CefRefPtr<CefRequest> request,
+                                              CefRefPtr<CefCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, process_request))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->process_request(_struct, CefRequestCppToC::Wrap(request),
+                               CefCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResourceHandlerCToCpp::GetResponseHeaders(
+    CefRefPtr<CefResponse> response,
+    int64& response_length,
+    CefString& redirectUrl) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_response_headers))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: response; type: refptr_diff
+  DCHECK(response.get());
+  if (!response.get())
+    return;
+
+  // Execute
+  _struct->get_response_headers(_struct, CefResponseCppToC::Wrap(response),
+                                &response_length,
+                                redirectUrl.GetWritableStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceHandlerCToCpp::Skip(
+    int64 bytes_to_skip,
+    int64& bytes_skipped,
+    CefRefPtr<CefResourceSkipCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, skip))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->skip(_struct, bytes_to_skip, &bytes_skipped,
+                              CefResourceSkipCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceHandlerCToCpp::Read(
+    void* data_out,
+    int bytes_to_read,
+    int& bytes_read,
+    CefRefPtr<CefResourceReadCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, read))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data_out; type: simple_byaddr
+  DCHECK(data_out);
+  if (!data_out)
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->read(_struct, data_out, bytes_to_read, &bytes_read,
+                              CefResourceReadCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceHandlerCToCpp::ReadResponse(void* data_out,
+                                            int bytes_to_read,
+                                            int& bytes_read,
+                                            CefRefPtr<CefCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, read_response))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data_out; type: simple_byaddr
+  DCHECK(data_out);
+  if (!data_out)
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->read_response(_struct, data_out, bytes_to_read, &bytes_read,
+                             CefCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefResourceHandlerCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceHandlerCToCpp::CefResourceHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceHandlerCToCpp::~CefResourceHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_resource_handler_t* CefCToCppRefCounted<
+    CefResourceHandlerCToCpp,
+    CefResourceHandler,
+    cef_resource_handler_t>::UnwrapDerived(CefWrapperType type,
+                                           CefResourceHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefResourceHandlerCToCpp,
+                                   CefResourceHandler,
+                                   cef_resource_handler_t>::kWrapperType =
+    WT_RESOURCE_HANDLER;
diff --git a/src/libcef_dll/ctocpp/resource_handler_ctocpp.h b/src/libcef_dll/ctocpp/resource_handler_ctocpp.h
new file mode 100644
index 0000000..6ed8af6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_handler_ctocpp.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9a0955b4412d7b25b7c232de5c9fc882ee19d636$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESOURCE_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESOURCE_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/cef_resource_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefResourceHandlerCToCpp
+    : public CefCToCppRefCounted<CefResourceHandlerCToCpp,
+                                 CefResourceHandler,
+                                 cef_resource_handler_t> {
+ public:
+  CefResourceHandlerCToCpp();
+  virtual ~CefResourceHandlerCToCpp();
+
+  // CefResourceHandler methods.
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override;
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override;
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override;
+  bool Skip(int64 bytes_to_skip,
+            int64& bytes_skipped,
+            CefRefPtr<CefResourceSkipCallback> callback) override;
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override;
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override;
+  void Cancel() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESOURCE_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/resource_read_callback_ctocpp.cc b/src/libcef_dll/ctocpp/resource_read_callback_ctocpp.cc
new file mode 100644
index 0000000..cc899a0
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_read_callback_ctocpp.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=125f79d2fcbf968b7fd2d872b03a711274583eb1$
+//
+
+#include "libcef_dll/ctocpp/resource_read_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefResourceReadCallbackCToCpp::Continue(int bytes_read) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_read_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cont(_struct, bytes_read);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceReadCallbackCToCpp::CefResourceReadCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceReadCallbackCToCpp::~CefResourceReadCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_resource_read_callback_t* CefCToCppRefCounted<
+    CefResourceReadCallbackCToCpp,
+    CefResourceReadCallback,
+    cef_resource_read_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                 CefResourceReadCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefResourceReadCallbackCToCpp,
+                                   CefResourceReadCallback,
+                                   cef_resource_read_callback_t>::kWrapperType =
+    WT_RESOURCE_READ_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/resource_read_callback_ctocpp.h b/src/libcef_dll/ctocpp/resource_read_callback_ctocpp.h
new file mode 100644
index 0000000..57af0ff
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_read_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=623833bb4eb1b7acbd3b3d7f119760178ae137e1$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESOURCE_READ_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESOURCE_READ_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/cef_resource_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResourceReadCallbackCToCpp
+    : public CefCToCppRefCounted<CefResourceReadCallbackCToCpp,
+                                 CefResourceReadCallback,
+                                 cef_resource_read_callback_t> {
+ public:
+  CefResourceReadCallbackCToCpp();
+  virtual ~CefResourceReadCallbackCToCpp();
+
+  // CefResourceReadCallback methods.
+  void Continue(int bytes_read) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESOURCE_READ_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/resource_request_handler_ctocpp.cc b/src/libcef_dll/ctocpp/resource_request_handler_ctocpp.cc
new file mode 100644
index 0000000..66634fd
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_request_handler_ctocpp.cc
@@ -0,0 +1,292 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4b688dcc74e4949b8e9a236c16530d77323d2b32$
+//
+
+#include "libcef_dll/ctocpp/resource_request_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/request_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/response_cpptoc.h"
+#include "libcef_dll/ctocpp/cookie_access_filter_ctocpp.h"
+#include "libcef_dll/ctocpp/resource_handler_ctocpp.h"
+#include "libcef_dll/ctocpp/response_filter_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefCookieAccessFilter>
+CefResourceRequestHandlerCToCpp::GetCookieAccessFilter(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_cookie_access_filter))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Unverified params: browser, frame
+
+  // Execute
+  cef_cookie_access_filter_t* _retval = _struct->get_cookie_access_filter(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request));
+
+  // Return type: refptr_same
+  return CefCookieAccessFilterCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefResourceRequestHandler::ReturnValue
+CefResourceRequestHandlerCToCpp::OnBeforeResourceLoad(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefRequestCallback> callback) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_before_resource_load))
+    return RV_CONTINUE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return RV_CONTINUE;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return RV_CONTINUE;
+  // Unverified params: browser, frame
+
+  // Execute
+  cef_return_value_t _retval = _struct->on_before_resource_load(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request),
+      CefRequestCallbackCppToC::Wrap(callback));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResourceHandler>
+CefResourceRequestHandlerCToCpp::GetResourceHandler(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_resource_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Unverified params: browser, frame
+
+  // Execute
+  cef_resource_handler_t* _retval = _struct->get_resource_handler(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request));
+
+  // Return type: refptr_same
+  return CefResourceHandlerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResourceRequestHandlerCToCpp::OnResourceRedirect(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response,
+    CefString& new_url) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_resource_redirect))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response.get());
+  if (!response.get())
+    return;
+  // Unverified params: browser, frame
+
+  // Execute
+  _struct->on_resource_redirect(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), CefResponseCppToC::Wrap(response),
+      new_url.GetWritableStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefResourceRequestHandlerCToCpp::OnResourceResponse(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_resource_response))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return false;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response.get());
+  if (!response.get())
+    return false;
+  // Unverified params: browser, frame
+
+  // Execute
+  int _retval = _struct->on_resource_response(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), CefResponseCppToC::Wrap(response));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResponseFilter>
+CefResourceRequestHandlerCToCpp::GetResourceResponseFilter(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_resource_response_filter))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response.get());
+  if (!response.get())
+    return nullptr;
+  // Unverified params: browser, frame
+
+  // Execute
+  cef_response_filter_t* _retval = _struct->get_resource_response_filter(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), CefResponseCppToC::Wrap(response));
+
+  // Return type: refptr_same
+  return CefResponseFilterCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResourceRequestHandlerCToCpp::OnResourceLoadComplete(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response,
+    URLRequestStatus status,
+    int64 received_content_length) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_resource_load_complete))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+  // Verify param: response; type: refptr_diff
+  DCHECK(response.get());
+  if (!response.get())
+    return;
+  // Unverified params: browser, frame
+
+  // Execute
+  _struct->on_resource_load_complete(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), CefResponseCppToC::Wrap(response),
+      status, received_content_length);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResourceRequestHandlerCToCpp::OnProtocolExecution(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    bool& allow_os_execution) {
+  cef_resource_request_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_protocol_execution))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+  // Unverified params: browser, frame
+
+  // Translate param: allow_os_execution; type: bool_byref
+  int allow_os_executionInt = allow_os_execution;
+
+  // Execute
+  _struct->on_protocol_execution(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      CefRequestCppToC::Wrap(request), &allow_os_executionInt);
+
+  // Restore param:allow_os_execution; type: bool_byref
+  allow_os_execution = allow_os_executionInt ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceRequestHandlerCToCpp::CefResourceRequestHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceRequestHandlerCToCpp::~CefResourceRequestHandlerCToCpp() {}
+
+template <>
+cef_resource_request_handler_t* CefCToCppRefCounted<
+    CefResourceRequestHandlerCToCpp,
+    CefResourceRequestHandler,
+    cef_resource_request_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                   CefResourceRequestHandler*
+                                                       c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefResourceRequestHandlerCToCpp,
+                        CefResourceRequestHandler,
+                        cef_resource_request_handler_t>::kWrapperType =
+        WT_RESOURCE_REQUEST_HANDLER;
diff --git a/src/libcef_dll/ctocpp/resource_request_handler_ctocpp.h b/src/libcef_dll/ctocpp/resource_request_handler_ctocpp.h
new file mode 100644
index 0000000..fe00e89
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_request_handler_ctocpp.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6ab9b4e37284582d67a4a6e63ab7e1d331b1442f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESOURCE_REQUEST_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESOURCE_REQUEST_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_resource_request_handler_capi.h"
+#include "include/cef_resource_request_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefResourceRequestHandlerCToCpp
+    : public CefCToCppRefCounted<CefResourceRequestHandlerCToCpp,
+                                 CefResourceRequestHandler,
+                                 cef_resource_request_handler_t> {
+ public:
+  CefResourceRequestHandlerCToCpp();
+  virtual ~CefResourceRequestHandlerCToCpp();
+
+  // CefResourceRequestHandler methods.
+  CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override;
+  ReturnValue OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override;
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override;
+  void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response,
+                          CefString& new_url) override;
+  bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response) override;
+  CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefResponse> response) override;
+  void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefRequest> request,
+                              CefRefPtr<CefResponse> response,
+                              URLRequestStatus status,
+                              int64 received_content_length) override;
+  void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefRequest> request,
+                           bool& allow_os_execution) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESOURCE_REQUEST_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/resource_skip_callback_ctocpp.cc b/src/libcef_dll/ctocpp/resource_skip_callback_ctocpp.cc
new file mode 100644
index 0000000..3402923
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_skip_callback_ctocpp.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2233b3c4dd73427daaf72910a7944ce0ecb63e46$
+//
+
+#include "libcef_dll/ctocpp/resource_skip_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefResourceSkipCallbackCToCpp::Continue(int64 bytes_skipped) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_resource_skip_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cont(_struct, bytes_skipped);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResourceSkipCallbackCToCpp::CefResourceSkipCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResourceSkipCallbackCToCpp::~CefResourceSkipCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_resource_skip_callback_t* CefCToCppRefCounted<
+    CefResourceSkipCallbackCToCpp,
+    CefResourceSkipCallback,
+    cef_resource_skip_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                 CefResourceSkipCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefResourceSkipCallbackCToCpp,
+                                   CefResourceSkipCallback,
+                                   cef_resource_skip_callback_t>::kWrapperType =
+    WT_RESOURCE_SKIP_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/resource_skip_callback_ctocpp.h b/src/libcef_dll/ctocpp/resource_skip_callback_ctocpp.h
new file mode 100644
index 0000000..bb11b0b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/resource_skip_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3b921fa2ac60c6a97a544223275bbc02ce26b899$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESOURCE_SKIP_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESOURCE_SKIP_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_resource_handler_capi.h"
+#include "include/cef_resource_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResourceSkipCallbackCToCpp
+    : public CefCToCppRefCounted<CefResourceSkipCallbackCToCpp,
+                                 CefResourceSkipCallback,
+                                 cef_resource_skip_callback_t> {
+ public:
+  CefResourceSkipCallbackCToCpp();
+  virtual ~CefResourceSkipCallbackCToCpp();
+
+  // CefResourceSkipCallback methods.
+  void Continue(int64 bytes_skipped) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESOURCE_SKIP_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/response_ctocpp.cc b/src/libcef_dll/ctocpp/response_ctocpp.cc
new file mode 100644
index 0000000..b7779ea
--- /dev/null
+++ b/src/libcef_dll/ctocpp/response_ctocpp.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=343ac0d93557b9f3a6e9e2500a4e2ab36b632312$
+//
+
+#include "libcef_dll/ctocpp/response_ctocpp.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefResponse> CefResponse::Create() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_response_t* _retval = cef_response_create();
+
+  // Return type: refptr_same
+  return CefResponseCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefResponseCToCpp::IsReadOnly() {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") cef_errorcode_t CefResponseCToCpp::GetError() {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_error))
+    return ERR_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_errorcode_t _retval = _struct->get_error(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResponseCToCpp::SetError(cef_errorcode_t error) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_error))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_error(_struct, error);
+}
+
+NO_SANITIZE("cfi-icall") int CefResponseCToCpp::GetStatus() {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_status))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_status(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefResponseCToCpp::SetStatus(int status) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_status))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_status(_struct, status);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefResponseCToCpp::GetStatusText() {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_status_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_status_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResponseCToCpp::SetStatusText(const CefString& statusText) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_status_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: statusText
+
+  // Execute
+  _struct->set_status_text(_struct, statusText.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefResponseCToCpp::GetMimeType() {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_mime_type))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_mime_type(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResponseCToCpp::SetMimeType(const CefString& mimeType) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_mime_type))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: mimeType
+
+  // Execute
+  _struct->set_mime_type(_struct, mimeType.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefResponseCToCpp::GetCharset() {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_charset))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_charset(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResponseCToCpp::SetCharset(const CefString& charset) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_charset))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: charset
+
+  // Execute
+  _struct->set_charset(_struct, charset.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefResponseCToCpp::GetHeaderByName(const CefString& name) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_header_by_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_header_by_name(_struct, name.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResponseCToCpp::SetHeaderByName(const CefString& name,
+                                        const CefString& value,
+                                        bool overwrite) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_header_by_name))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+  // Unverified params: value
+
+  // Execute
+  _struct->set_header_by_name(_struct, name.GetStruct(), value.GetStruct(),
+                              overwrite);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResponseCToCpp::GetHeaderMap(HeaderMap& headerMap) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_header_map))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: headerMap; type: string_map_multi_byref
+  cef_string_multimap_t headerMapMultimap = cef_string_multimap_alloc();
+  DCHECK(headerMapMultimap);
+  if (headerMapMultimap)
+    transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  _struct->get_header_map(_struct, headerMapMultimap);
+
+  // Restore param:headerMap; type: string_map_multi_byref
+  if (headerMapMultimap) {
+    headerMap.clear();
+    transfer_string_multimap_contents(headerMapMultimap, headerMap);
+    cef_string_multimap_free(headerMapMultimap);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefResponseCToCpp::SetHeaderMap(const HeaderMap& headerMap) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_header_map))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: headerMap; type: string_map_multi_byref_const
+  cef_string_multimap_t headerMapMultimap = cef_string_multimap_alloc();
+  DCHECK(headerMapMultimap);
+  if (headerMapMultimap)
+    transfer_string_multimap_contents(headerMap, headerMapMultimap);
+
+  // Execute
+  _struct->set_header_map(_struct, headerMapMultimap);
+
+  // Restore param:headerMap; type: string_map_multi_byref_const
+  if (headerMapMultimap)
+    cef_string_multimap_free(headerMapMultimap);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefResponseCToCpp::GetURL() {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") void CefResponseCToCpp::SetURL(const CefString& url) {
+  cef_response_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_url))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: url
+
+  // Execute
+  _struct->set_url(_struct, url.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResponseCToCpp::CefResponseCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResponseCToCpp::~CefResponseCToCpp() {}
+
+template <>
+cef_response_t*
+CefCToCppRefCounted<CefResponseCToCpp, CefResponse, cef_response_t>::
+    UnwrapDerived(CefWrapperType type, CefResponse* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefResponseCToCpp,
+                                   CefResponse,
+                                   cef_response_t>::kWrapperType = WT_RESPONSE;
diff --git a/src/libcef_dll/ctocpp/response_ctocpp.h b/src/libcef_dll/ctocpp/response_ctocpp.h
new file mode 100644
index 0000000..c3a179b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/response_ctocpp.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=59826865807e14e451ec90e7188f48da3748b325$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESPONSE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESPONSE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_response_capi.h"
+#include "include/cef_response.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefResponseCToCpp : public CefCToCppRefCounted<CefResponseCToCpp,
+                                                     CefResponse,
+                                                     cef_response_t> {
+ public:
+  CefResponseCToCpp();
+  virtual ~CefResponseCToCpp();
+
+  // CefResponse methods.
+  bool IsReadOnly() OVERRIDE;
+  cef_errorcode_t GetError() OVERRIDE;
+  void SetError(cef_errorcode_t error) OVERRIDE;
+  int GetStatus() OVERRIDE;
+  void SetStatus(int status) OVERRIDE;
+  CefString GetStatusText() OVERRIDE;
+  void SetStatusText(const CefString& statusText) OVERRIDE;
+  CefString GetMimeType() OVERRIDE;
+  void SetMimeType(const CefString& mimeType) OVERRIDE;
+  CefString GetCharset() OVERRIDE;
+  void SetCharset(const CefString& charset) OVERRIDE;
+  CefString GetHeaderByName(const CefString& name) OVERRIDE;
+  void SetHeaderByName(const CefString& name,
+                       const CefString& value,
+                       bool overwrite) OVERRIDE;
+  void GetHeaderMap(HeaderMap& headerMap) OVERRIDE;
+  void SetHeaderMap(const HeaderMap& headerMap) OVERRIDE;
+  CefString GetURL() OVERRIDE;
+  void SetURL(const CefString& url) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESPONSE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/response_filter_ctocpp.cc b/src/libcef_dll/ctocpp/response_filter_ctocpp.cc
new file mode 100644
index 0000000..9bcc589
--- /dev/null
+++ b/src/libcef_dll/ctocpp/response_filter_ctocpp.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f317870833b17779de0a9cb0f0516aface361613$
+//
+
+#include "libcef_dll/ctocpp/response_filter_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefResponseFilterCToCpp::InitFilter() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_response_filter_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, init_filter))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->init_filter(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefResponseFilter::FilterStatus CefResponseFilterCToCpp::Filter(
+    void* data_in,
+    size_t data_in_size,
+    size_t& data_in_read,
+    void* data_out,
+    size_t data_out_size,
+    size_t& data_out_written) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_response_filter_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, filter))
+    return RESPONSE_FILTER_ERROR;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data_out; type: simple_byaddr
+  DCHECK(data_out);
+  if (!data_out)
+    return RESPONSE_FILTER_ERROR;
+  // Unverified params: data_in
+
+  // Execute
+  cef_response_filter_status_t _retval =
+      _struct->filter(_struct, data_in, data_in_size, &data_in_read, data_out,
+                      data_out_size, &data_out_written);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefResponseFilterCToCpp::CefResponseFilterCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefResponseFilterCToCpp::~CefResponseFilterCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_response_filter_t* CefCToCppRefCounted<
+    CefResponseFilterCToCpp,
+    CefResponseFilter,
+    cef_response_filter_t>::UnwrapDerived(CefWrapperType type,
+                                          CefResponseFilter* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefResponseFilterCToCpp,
+                                   CefResponseFilter,
+                                   cef_response_filter_t>::kWrapperType =
+    WT_RESPONSE_FILTER;
diff --git a/src/libcef_dll/ctocpp/response_filter_ctocpp.h b/src/libcef_dll/ctocpp/response_filter_ctocpp.h
new file mode 100644
index 0000000..35d4eb9
--- /dev/null
+++ b/src/libcef_dll/ctocpp/response_filter_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=33af4ee0e319f8a1585b9b331101cacf51aa686f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RESPONSE_FILTER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RESPONSE_FILTER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_response_filter_capi.h"
+#include "include/cef_response_filter.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefResponseFilterCToCpp
+    : public CefCToCppRefCounted<CefResponseFilterCToCpp,
+                                 CefResponseFilter,
+                                 cef_response_filter_t> {
+ public:
+  CefResponseFilterCToCpp();
+  virtual ~CefResponseFilterCToCpp();
+
+  // CefResponseFilter methods.
+  bool InitFilter() override;
+  FilterStatus Filter(void* data_in,
+                      size_t data_in_size,
+                      size_t& data_in_read,
+                      void* data_out,
+                      size_t data_out_size,
+                      size_t& data_out_written) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RESPONSE_FILTER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/run_context_menu_callback_ctocpp.cc b/src/libcef_dll/ctocpp/run_context_menu_callback_ctocpp.cc
new file mode 100644
index 0000000..995cba4
--- /dev/null
+++ b/src/libcef_dll/ctocpp/run_context_menu_callback_ctocpp.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2f1eac0f40c023b5257161efd4b4b1911f1ab8b6$
+//
+
+#include "libcef_dll/ctocpp/run_context_menu_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefRunContextMenuCallbackCToCpp::Continue(int command_id,
+                                               EventFlags event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_run_context_menu_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cont))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cont(_struct, command_id, event_flags);
+}
+
+NO_SANITIZE("cfi-icall") void CefRunContextMenuCallbackCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_run_context_menu_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRunContextMenuCallbackCToCpp::CefRunContextMenuCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRunContextMenuCallbackCToCpp::~CefRunContextMenuCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_run_context_menu_callback_t* CefCToCppRefCounted<
+    CefRunContextMenuCallbackCToCpp,
+    CefRunContextMenuCallback,
+    cef_run_context_menu_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                    CefRunContextMenuCallback*
+                                                        c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefRunContextMenuCallbackCToCpp,
+                        CefRunContextMenuCallback,
+                        cef_run_context_menu_callback_t>::kWrapperType =
+        WT_RUN_CONTEXT_MENU_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/run_context_menu_callback_ctocpp.h b/src/libcef_dll/ctocpp/run_context_menu_callback_ctocpp.h
new file mode 100644
index 0000000..33759d3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/run_context_menu_callback_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2a475530e05300e2326247f9bdabeb4cfdf7217e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RUN_CONTEXT_MENU_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RUN_CONTEXT_MENU_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_context_menu_handler_capi.h"
+#include "include/cef_context_menu_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefRunContextMenuCallbackCToCpp
+    : public CefCToCppRefCounted<CefRunContextMenuCallbackCToCpp,
+                                 CefRunContextMenuCallback,
+                                 cef_run_context_menu_callback_t> {
+ public:
+  CefRunContextMenuCallbackCToCpp();
+  virtual ~CefRunContextMenuCallbackCToCpp();
+
+  // CefRunContextMenuCallback methods.
+  void Continue(int command_id, EventFlags event_flags) OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RUN_CONTEXT_MENU_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.cc b/src/libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.cc
new file mode 100644
index 0000000..32fb4fb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0065d3160cbd59f9fe8f5272acbca0a90a5eda81$
+//
+
+#include "libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefRunFileDialogCallbackCToCpp::OnFileDialogDismissed(
+    int selected_accept_filter,
+    const std::vector<CefString>& file_paths) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_run_file_dialog_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_file_dialog_dismissed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: selected_accept_filter; type: simple_byval
+  DCHECK_GE(selected_accept_filter, 0);
+  if (selected_accept_filter < 0)
+    return;
+  // Unverified params: file_paths
+
+  // Translate param: file_paths; type: string_vec_byref_const
+  cef_string_list_t file_pathsList = cef_string_list_alloc();
+  DCHECK(file_pathsList);
+  if (file_pathsList)
+    transfer_string_list_contents(file_paths, file_pathsList);
+
+  // Execute
+  _struct->on_file_dialog_dismissed(_struct, selected_accept_filter,
+                                    file_pathsList);
+
+  // Restore param:file_paths; type: string_vec_byref_const
+  if (file_pathsList)
+    cef_string_list_free(file_pathsList);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefRunFileDialogCallbackCToCpp::CefRunFileDialogCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefRunFileDialogCallbackCToCpp::~CefRunFileDialogCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_run_file_dialog_callback_t* CefCToCppRefCounted<
+    CefRunFileDialogCallbackCToCpp,
+    CefRunFileDialogCallback,
+    cef_run_file_dialog_callback_t>::UnwrapDerived(CefWrapperType type,
+                                                   CefRunFileDialogCallback*
+                                                       c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefRunFileDialogCallbackCToCpp,
+                        CefRunFileDialogCallback,
+                        cef_run_file_dialog_callback_t>::kWrapperType =
+        WT_RUN_FILE_DIALOG_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h b/src/libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h
new file mode 100644
index 0000000..4c8b517
--- /dev/null
+++ b/src/libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0783d7d71c2e1a69fdaf9a76e72a2c12d49e8363$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_RUN_FILE_DIALOG_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_RUN_FILE_DIALOG_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_client_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefRunFileDialogCallbackCToCpp
+    : public CefCToCppRefCounted<CefRunFileDialogCallbackCToCpp,
+                                 CefRunFileDialogCallback,
+                                 cef_run_file_dialog_callback_t> {
+ public:
+  CefRunFileDialogCallbackCToCpp();
+  virtual ~CefRunFileDialogCallbackCToCpp();
+
+  // CefRunFileDialogCallback methods.
+  void OnFileDialogDismissed(int selected_accept_filter,
+                             const std::vector<CefString>& file_paths) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_RUN_FILE_DIALOG_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc b/src/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc
new file mode 100644
index 0000000..014a77c
--- /dev/null
+++ b/src/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ba596df840e36426b87e0744fcf55e179a915c5e$
+//
+
+#include "libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/ctocpp/resource_handler_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResourceHandler> CefSchemeHandlerFactoryCToCpp::Create(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    const CefString& scheme_name,
+    CefRefPtr<CefRequest> request) {
+  cef_scheme_handler_factory_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, create))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(!scheme_name.empty());
+  if (scheme_name.empty())
+    return nullptr;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Unverified params: browser, frame
+
+  // Execute
+  cef_resource_handler_t* _retval = _struct->create(
+      _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
+      scheme_name.GetStruct(), CefRequestCppToC::Wrap(request));
+
+  // Return type: refptr_same
+  return CefResourceHandlerCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSchemeHandlerFactoryCToCpp::CefSchemeHandlerFactoryCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSchemeHandlerFactoryCToCpp::~CefSchemeHandlerFactoryCToCpp() {}
+
+template <>
+cef_scheme_handler_factory_t* CefCToCppRefCounted<
+    CefSchemeHandlerFactoryCToCpp,
+    CefSchemeHandlerFactory,
+    cef_scheme_handler_factory_t>::UnwrapDerived(CefWrapperType type,
+                                                 CefSchemeHandlerFactory* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefSchemeHandlerFactoryCToCpp,
+                                   CefSchemeHandlerFactory,
+                                   cef_scheme_handler_factory_t>::kWrapperType =
+    WT_SCHEME_HANDLER_FACTORY;
diff --git a/src/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h b/src/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h
new file mode 100644
index 0000000..cf51b1b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ce60f732df5ba8d6bf345f0451ca75a8a634e4e8$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SCHEME_HANDLER_FACTORY_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SCHEME_HANDLER_FACTORY_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefSchemeHandlerFactoryCToCpp
+    : public CefCToCppRefCounted<CefSchemeHandlerFactoryCToCpp,
+                                 CefSchemeHandlerFactory,
+                                 cef_scheme_handler_factory_t> {
+ public:
+  CefSchemeHandlerFactoryCToCpp();
+  virtual ~CefSchemeHandlerFactoryCToCpp();
+
+  // CefSchemeHandlerFactory methods.
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SCHEME_HANDLER_FACTORY_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/scheme_registrar_ctocpp.cc b/src/libcef_dll/ctocpp/scheme_registrar_ctocpp.cc
new file mode 100644
index 0000000..ef41462
--- /dev/null
+++ b/src/libcef_dll/ctocpp/scheme_registrar_ctocpp.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=96867bd89ff762e10642ac8c037b187a03dab85e$
+//
+
+#include "libcef_dll/ctocpp/scheme_registrar_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefSchemeRegistrarCToCpp::AddCustomScheme(const CefString& scheme_name,
+                                               int options) {
+  cef_scheme_registrar_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_custom_scheme))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(!scheme_name.empty());
+  if (scheme_name.empty())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->add_custom_scheme(_struct, scheme_name.GetStruct(), options);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSchemeRegistrarCToCpp::CefSchemeRegistrarCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSchemeRegistrarCToCpp::~CefSchemeRegistrarCToCpp() {}
+
+template <>
+cef_scheme_registrar_t* CefCToCppScoped<
+    CefSchemeRegistrarCToCpp,
+    CefSchemeRegistrar,
+    cef_scheme_registrar_t>::UnwrapDerivedOwn(CefWrapperType type,
+                                              CefOwnPtr<CefSchemeRegistrar> c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+cef_scheme_registrar_t* CefCToCppScoped<
+    CefSchemeRegistrarCToCpp,
+    CefSchemeRegistrar,
+    cef_scheme_registrar_t>::UnwrapDerivedRaw(CefWrapperType type,
+                                              CefRawPtr<CefSchemeRegistrar> c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppScoped<CefSchemeRegistrarCToCpp,
+                               CefSchemeRegistrar,
+                               cef_scheme_registrar_t>::kWrapperType =
+    WT_SCHEME_REGISTRAR;
diff --git a/src/libcef_dll/ctocpp/scheme_registrar_ctocpp.h b/src/libcef_dll/ctocpp/scheme_registrar_ctocpp.h
new file mode 100644
index 0000000..e27a93a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/scheme_registrar_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5b599c1296d8643dd104d2ecb65528d85b6be6d5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SCHEME_REGISTRAR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SCHEME_REGISTRAR_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_scheme_capi.h"
+#include "include/cef_scheme.h"
+#include "libcef_dll/ctocpp/ctocpp_scoped.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefSchemeRegistrarCToCpp
+    : public CefCToCppScoped<CefSchemeRegistrarCToCpp,
+                             CefSchemeRegistrar,
+                             cef_scheme_registrar_t> {
+ public:
+  CefSchemeRegistrarCToCpp();
+  virtual ~CefSchemeRegistrarCToCpp();
+
+  // CefSchemeRegistrar methods.
+  bool AddCustomScheme(const CefString& scheme_name, int options) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SCHEME_REGISTRAR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.cc b/src/libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.cc
new file mode 100644
index 0000000..3b63ea8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9074ca50ffc28d04a31a322d86e8b6118782a724$
+//
+
+#include "libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/x509certificate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefSelectClientCertificateCallbackCToCpp::Select(
+    CefRefPtr<CefX509Certificate> cert) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_select_client_certificate_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, select))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: cert
+
+  // Execute
+  _struct->select(_struct, CefX509CertificateCToCpp::Unwrap(cert));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSelectClientCertificateCallbackCToCpp::
+    CefSelectClientCertificateCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSelectClientCertificateCallbackCToCpp::
+    ~CefSelectClientCertificateCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_select_client_certificate_callback_t*
+CefCToCppRefCounted<CefSelectClientCertificateCallbackCToCpp,
+                    CefSelectClientCertificateCallback,
+                    cef_select_client_certificate_callback_t>::
+    UnwrapDerived(CefWrapperType type, CefSelectClientCertificateCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<
+    CefSelectClientCertificateCallbackCToCpp,
+    CefSelectClientCertificateCallback,
+    cef_select_client_certificate_callback_t>::kWrapperType =
+    WT_SELECT_CLIENT_CERTIFICATE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h b/src/libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h
new file mode 100644
index 0000000..d23e0a3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c1ae778f52e24fdb61ed52ca4b79f32b8a336f8f$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SELECT_CLIENT_CERTIFICATE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SELECT_CLIENT_CERTIFICATE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_request_handler_capi.h"
+#include "include/cef_request_handler.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefSelectClientCertificateCallbackCToCpp
+    : public CefCToCppRefCounted<CefSelectClientCertificateCallbackCToCpp,
+                                 CefSelectClientCertificateCallback,
+                                 cef_select_client_certificate_callback_t> {
+ public:
+  CefSelectClientCertificateCallbackCToCpp();
+  virtual ~CefSelectClientCertificateCallbackCToCpp();
+
+  // CefSelectClientCertificateCallback methods.
+  void Select(CefRefPtr<CefX509Certificate> cert) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SELECT_CLIENT_CERTIFICATE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/server_ctocpp.cc b/src/libcef_dll/ctocpp/server_ctocpp.cc
new file mode 100644
index 0000000..cee398b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/server_ctocpp.cc
@@ -0,0 +1,320 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e33f5d5355018c27df765228ce01ea4d21ac1663$
+//
+
+#include "libcef_dll/ctocpp/server_ctocpp.h"
+#include "libcef_dll/cpptoc/server_handler_cpptoc.h"
+#include "libcef_dll/ctocpp/task_runner_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefServer::CreateServer(const CefString& address,
+                             uint16 port,
+                             int backlog,
+                             CefRefPtr<CefServerHandler> handler) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: address; type: string_byref_const
+  DCHECK(!address.empty());
+  if (address.empty())
+    return;
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler.get());
+  if (!handler.get())
+    return;
+
+  // Execute
+  cef_server_create(address.GetStruct(), port, backlog,
+                    CefServerHandlerCppToC::Wrap(handler));
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTaskRunner> CefServerCToCpp::GetTaskRunner() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_task_runner))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_task_runner_t* _retval = _struct->get_task_runner(_struct);
+
+  // Return type: refptr_same
+  return CefTaskRunnerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") void CefServerCToCpp::Shutdown() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, shutdown))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->shutdown(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefServerCToCpp::IsRunning() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_running))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_running(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefServerCToCpp::GetAddress() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_address))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_address(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefServerCToCpp::HasConnection() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_connection))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_connection(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefServerCToCpp::IsValidConnection(int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid_connection))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid_connection(_struct, connection_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerCToCpp::SendHttp200Response(int connection_id,
+                                          const CefString& content_type,
+                                          const void* data,
+                                          size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_http200response))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: content_type; type: string_byref_const
+  DCHECK(!content_type.empty());
+  if (content_type.empty())
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  _struct->send_http200response(_struct, connection_id,
+                                content_type.GetStruct(), data, data_size);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerCToCpp::SendHttp404Response(int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_http404response))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_http404response(_struct, connection_id);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerCToCpp::SendHttp500Response(int connection_id,
+                                          const CefString& error_message) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_http500response))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: error_message; type: string_byref_const
+  DCHECK(!error_message.empty());
+  if (error_message.empty())
+    return;
+
+  // Execute
+  _struct->send_http500response(_struct, connection_id,
+                                error_message.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerCToCpp::SendHttpResponse(int connection_id,
+                                       int response_code,
+                                       const CefString& content_type,
+                                       int64 content_length,
+                                       const HeaderMap& extra_headers) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_http_response))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: content_type; type: string_byref_const
+  DCHECK(!content_type.empty());
+  if (content_type.empty())
+    return;
+  // Unverified params: extra_headers
+
+  // Translate param: extra_headers; type: string_map_multi_byref_const
+  cef_string_multimap_t extra_headersMultimap = cef_string_multimap_alloc();
+  DCHECK(extra_headersMultimap);
+  if (extra_headersMultimap)
+    transfer_string_multimap_contents(extra_headers, extra_headersMultimap);
+
+  // Execute
+  _struct->send_http_response(_struct, connection_id, response_code,
+                              content_type.GetStruct(), content_length,
+                              extra_headersMultimap);
+
+  // Restore param:extra_headers; type: string_map_multi_byref_const
+  if (extra_headersMultimap)
+    cef_string_multimap_free(extra_headersMultimap);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerCToCpp::SendRawData(int connection_id,
+                                  const void* data,
+                                  size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_raw_data))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  _struct->send_raw_data(_struct, connection_id, data, data_size);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerCToCpp::CloseConnection(int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close_connection))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->close_connection(_struct, connection_id);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerCToCpp::SendWebSocketMessage(int connection_id,
+                                           const void* data,
+                                           size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_web_socket_message))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  _struct->send_web_socket_message(_struct, connection_id, data, data_size);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefServerCToCpp::CefServerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefServerCToCpp::~CefServerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_server_t*
+CefCToCppRefCounted<CefServerCToCpp, CefServer, cef_server_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefServer* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefServerCToCpp, CefServer, cef_server_t>::
+    kWrapperType = WT_SERVER;
diff --git a/src/libcef_dll/ctocpp/server_ctocpp.h b/src/libcef_dll/ctocpp/server_ctocpp.h
new file mode 100644
index 0000000..f95cd0e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/server_ctocpp.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9cb2173041cc6127fcc511cb011dde29bb0995c3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SERVER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SERVER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_server_capi.h"
+#include "include/cef_server.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefServerCToCpp
+    : public CefCToCppRefCounted<CefServerCToCpp, CefServer, cef_server_t> {
+ public:
+  CefServerCToCpp();
+  virtual ~CefServerCToCpp();
+
+  // CefServer methods.
+  CefRefPtr<CefTaskRunner> GetTaskRunner() OVERRIDE;
+  void Shutdown() OVERRIDE;
+  bool IsRunning() OVERRIDE;
+  CefString GetAddress() OVERRIDE;
+  bool HasConnection() OVERRIDE;
+  bool IsValidConnection(int connection_id) OVERRIDE;
+  void SendHttp200Response(int connection_id,
+                           const CefString& content_type,
+                           const void* data,
+                           size_t data_size) OVERRIDE;
+  void SendHttp404Response(int connection_id) OVERRIDE;
+  void SendHttp500Response(int connection_id,
+                           const CefString& error_message) OVERRIDE;
+  void SendHttpResponse(int connection_id,
+                        int response_code,
+                        const CefString& content_type,
+                        int64 content_length,
+                        const HeaderMap& extra_headers) OVERRIDE;
+  void SendRawData(int connection_id,
+                   const void* data,
+                   size_t data_size) OVERRIDE;
+  void CloseConnection(int connection_id) OVERRIDE;
+  void SendWebSocketMessage(int connection_id,
+                            const void* data,
+                            size_t data_size) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SERVER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/server_handler_ctocpp.cc b/src/libcef_dll/ctocpp/server_handler_ctocpp.cc
new file mode 100644
index 0000000..8c5c45d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/server_handler_ctocpp.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=eda7a02f86a6f75b5c2befa5cc3c02bf290a6782$
+//
+
+#include "libcef_dll/ctocpp/server_handler_ctocpp.h"
+#include "libcef_dll/cpptoc/callback_cpptoc.h"
+#include "libcef_dll/cpptoc/request_cpptoc.h"
+#include "libcef_dll/cpptoc/server_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnServerCreated(CefRefPtr<CefServer> server) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_server_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+
+  // Execute
+  _struct->on_server_created(_struct, CefServerCppToC::Wrap(server));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnServerDestroyed(CefRefPtr<CefServer> server) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_server_destroyed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+
+  // Execute
+  _struct->on_server_destroyed(_struct, CefServerCppToC::Wrap(server));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnClientConnected(CefRefPtr<CefServer> server,
+                                               int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_client_connected))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+
+  // Execute
+  _struct->on_client_connected(_struct, CefServerCppToC::Wrap(server),
+                               connection_id);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnClientDisconnected(CefRefPtr<CefServer> server,
+                                                  int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_client_disconnected))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+
+  // Execute
+  _struct->on_client_disconnected(_struct, CefServerCppToC::Wrap(server),
+                                  connection_id);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnHttpRequest(CefRefPtr<CefServer> server,
+                                           int connection_id,
+                                           const CefString& client_address,
+                                           CefRefPtr<CefRequest> request) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_http_request))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+  // Verify param: client_address; type: string_byref_const
+  DCHECK(!client_address.empty());
+  if (client_address.empty())
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+
+  // Execute
+  _struct->on_http_request(_struct, CefServerCppToC::Wrap(server),
+                           connection_id, client_address.GetStruct(),
+                           CefRequestCppToC::Wrap(request));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnWebSocketRequest(
+    CefRefPtr<CefServer> server,
+    int connection_id,
+    const CefString& client_address,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_web_socket_request))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+  // Verify param: client_address; type: string_byref_const
+  DCHECK(!client_address.empty());
+  if (client_address.empty())
+    return;
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  _struct->on_web_socket_request(_struct, CefServerCppToC::Wrap(server),
+                                 connection_id, client_address.GetStruct(),
+                                 CefRequestCppToC::Wrap(request),
+                                 CefCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnWebSocketConnected(CefRefPtr<CefServer> server,
+                                                  int connection_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_web_socket_connected))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+
+  // Execute
+  _struct->on_web_socket_connected(_struct, CefServerCppToC::Wrap(server),
+                                   connection_id);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefServerHandlerCToCpp::OnWebSocketMessage(CefRefPtr<CefServer> server,
+                                                int connection_id,
+                                                const void* data,
+                                                size_t data_size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_server_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_web_socket_message))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: server; type: refptr_diff
+  DCHECK(server.get());
+  if (!server.get())
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  _struct->on_web_socket_message(_struct, CefServerCppToC::Wrap(server),
+                                 connection_id, data, data_size);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefServerHandlerCToCpp::CefServerHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefServerHandlerCToCpp::~CefServerHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_server_handler_t*
+CefCToCppRefCounted<CefServerHandlerCToCpp,
+                    CefServerHandler,
+                    cef_server_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefServerHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefServerHandlerCToCpp,
+                                   CefServerHandler,
+                                   cef_server_handler_t>::kWrapperType =
+    WT_SERVER_HANDLER;
diff --git a/src/libcef_dll/ctocpp/server_handler_ctocpp.h b/src/libcef_dll/ctocpp/server_handler_ctocpp.h
new file mode 100644
index 0000000..e381cbc
--- /dev/null
+++ b/src/libcef_dll/ctocpp/server_handler_ctocpp.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=30d2dee5c66e3a98cd788f74a5856d068dc68e58$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SERVER_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SERVER_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_server_capi.h"
+#include "include/cef_server.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefServerHandlerCToCpp
+    : public CefCToCppRefCounted<CefServerHandlerCToCpp,
+                                 CefServerHandler,
+                                 cef_server_handler_t> {
+ public:
+  CefServerHandlerCToCpp();
+  virtual ~CefServerHandlerCToCpp();
+
+  // CefServerHandler methods.
+  void OnServerCreated(CefRefPtr<CefServer> server) override;
+  void OnServerDestroyed(CefRefPtr<CefServer> server) override;
+  void OnClientConnected(CefRefPtr<CefServer> server,
+                         int connection_id) override;
+  void OnClientDisconnected(CefRefPtr<CefServer> server,
+                            int connection_id) override;
+  void OnHttpRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const CefString& client_address,
+                     CefRefPtr<CefRequest> request) override;
+  void OnWebSocketRequest(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const CefString& client_address,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefCallback> callback) override;
+  void OnWebSocketConnected(CefRefPtr<CefServer> server,
+                            int connection_id) override;
+  void OnWebSocketMessage(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const void* data,
+                          size_t data_size) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SERVER_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/set_cookie_callback_ctocpp.cc b/src/libcef_dll/ctocpp/set_cookie_callback_ctocpp.cc
new file mode 100644
index 0000000..1380a25
--- /dev/null
+++ b/src/libcef_dll/ctocpp/set_cookie_callback_ctocpp.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a00d1d8d0e2a5a7a63b8f9669301186ffc2fc664$
+//
+
+#include "libcef_dll/ctocpp/set_cookie_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefSetCookieCallbackCToCpp::OnComplete(bool success) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_set_cookie_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_complete))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->on_complete(_struct, success);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSetCookieCallbackCToCpp::CefSetCookieCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSetCookieCallbackCToCpp::~CefSetCookieCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_set_cookie_callback_t* CefCToCppRefCounted<
+    CefSetCookieCallbackCToCpp,
+    CefSetCookieCallback,
+    cef_set_cookie_callback_t>::UnwrapDerived(CefWrapperType type,
+                                              CefSetCookieCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefSetCookieCallbackCToCpp,
+                                   CefSetCookieCallback,
+                                   cef_set_cookie_callback_t>::kWrapperType =
+    WT_SET_COOKIE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/set_cookie_callback_ctocpp.h b/src/libcef_dll/ctocpp/set_cookie_callback_ctocpp.h
new file mode 100644
index 0000000..34e2ec7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/set_cookie_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=18bb4e458cc3c1027baa21090f35c5a29018705d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SET_COOKIE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SET_COOKIE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_cookie_capi.h"
+#include "include/cef_cookie.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefSetCookieCallbackCToCpp
+    : public CefCToCppRefCounted<CefSetCookieCallbackCToCpp,
+                                 CefSetCookieCallback,
+                                 cef_set_cookie_callback_t> {
+ public:
+  CefSetCookieCallbackCToCpp();
+  virtual ~CefSetCookieCallbackCToCpp();
+
+  // CefSetCookieCallback methods.
+  void OnComplete(bool success) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SET_COOKIE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/sslinfo_ctocpp.cc b/src/libcef_dll/ctocpp/sslinfo_ctocpp.cc
new file mode 100644
index 0000000..37cca7a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/sslinfo_ctocpp.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=bcc26f8afe20b67daeb6feb8578f195649b2bc59$
+//
+
+#include "libcef_dll/ctocpp/sslinfo_ctocpp.h"
+#include "libcef_dll/ctocpp/x509certificate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") cef_cert_status_t CefSSLInfoCToCpp::GetCertStatus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_sslinfo_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_cert_status))
+    return CERT_STATUS_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_cert_status_t _retval = _struct->get_cert_status(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefX509Certificate> CefSSLInfoCToCpp::GetX509Certificate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_sslinfo_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_x509certificate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_x509certificate_t* _retval = _struct->get_x509certificate(_struct);
+
+  // Return type: refptr_same
+  return CefX509CertificateCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSSLInfoCToCpp::CefSSLInfoCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSSLInfoCToCpp::~CefSSLInfoCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_sslinfo_t*
+CefCToCppRefCounted<CefSSLInfoCToCpp, CefSSLInfo, cef_sslinfo_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefSSLInfo* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefSSLInfoCToCpp,
+                                   CefSSLInfo,
+                                   cef_sslinfo_t>::kWrapperType = WT_SSLINFO;
diff --git a/src/libcef_dll/ctocpp/sslinfo_ctocpp.h b/src/libcef_dll/ctocpp/sslinfo_ctocpp.h
new file mode 100644
index 0000000..22114e7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/sslinfo_ctocpp.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=27ba3a4f6fa0c2dd2d9dd88a6e21eab436610405$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SSLINFO_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SSLINFO_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_ssl_info_capi.h"
+#include "include/cef_ssl_info.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefSSLInfoCToCpp
+    : public CefCToCppRefCounted<CefSSLInfoCToCpp, CefSSLInfo, cef_sslinfo_t> {
+ public:
+  CefSSLInfoCToCpp();
+  virtual ~CefSSLInfoCToCpp();
+
+  // CefSSLInfo methods.
+  cef_cert_status_t GetCertStatus() OVERRIDE;
+  CefRefPtr<CefX509Certificate> GetX509Certificate() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SSLINFO_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/sslstatus_ctocpp.cc b/src/libcef_dll/ctocpp/sslstatus_ctocpp.cc
new file mode 100644
index 0000000..0dcbaa8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/sslstatus_ctocpp.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=61b177e6dc50166afe90999fbc9771d4f69254c5$
+//
+
+#include "libcef_dll/ctocpp/sslstatus_ctocpp.h"
+#include "libcef_dll/ctocpp/x509certificate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefSSLStatusCToCpp::IsSecureConnection() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_sslstatus_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_secure_connection))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_secure_connection(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") cef_cert_status_t CefSSLStatusCToCpp::GetCertStatus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_sslstatus_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_cert_status))
+    return CERT_STATUS_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_cert_status_t _retval = _struct->get_cert_status(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") cef_ssl_version_t CefSSLStatusCToCpp::GetSSLVersion() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_sslstatus_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_sslversion))
+    return SSL_CONNECTION_VERSION_UNKNOWN;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_ssl_version_t _retval = _struct->get_sslversion(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+cef_ssl_content_status_t CefSSLStatusCToCpp::GetContentStatus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_sslstatus_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_content_status))
+    return SSL_CONTENT_NORMAL_CONTENT;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_ssl_content_status_t _retval = _struct->get_content_status(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefX509Certificate> CefSSLStatusCToCpp::GetX509Certificate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_sslstatus_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_x509certificate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_x509certificate_t* _retval = _struct->get_x509certificate(_struct);
+
+  // Return type: refptr_same
+  return CefX509CertificateCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefSSLStatusCToCpp::CefSSLStatusCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefSSLStatusCToCpp::~CefSSLStatusCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_sslstatus_t*
+CefCToCppRefCounted<CefSSLStatusCToCpp, CefSSLStatus, cef_sslstatus_t>::
+    UnwrapDerived(CefWrapperType type, CefSSLStatus* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefSSLStatusCToCpp,
+                                   CefSSLStatus,
+                                   cef_sslstatus_t>::kWrapperType =
+    WT_SSLSTATUS;
diff --git a/src/libcef_dll/ctocpp/sslstatus_ctocpp.h b/src/libcef_dll/ctocpp/sslstatus_ctocpp.h
new file mode 100644
index 0000000..258f34d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/sslstatus_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4021a70f4b22fa6af86310906d9d14ccc1cec9da$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_SSLSTATUS_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_SSLSTATUS_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_ssl_status_capi.h"
+#include "include/cef_ssl_status.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefSSLStatusCToCpp : public CefCToCppRefCounted<CefSSLStatusCToCpp,
+                                                      CefSSLStatus,
+                                                      cef_sslstatus_t> {
+ public:
+  CefSSLStatusCToCpp();
+  virtual ~CefSSLStatusCToCpp();
+
+  // CefSSLStatus methods.
+  bool IsSecureConnection() OVERRIDE;
+  cef_cert_status_t GetCertStatus() OVERRIDE;
+  cef_ssl_version_t GetSSLVersion() OVERRIDE;
+  cef_ssl_content_status_t GetContentStatus() OVERRIDE;
+  CefRefPtr<CefX509Certificate> GetX509Certificate() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_SSLSTATUS_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/stream_reader_ctocpp.cc b/src/libcef_dll/ctocpp/stream_reader_ctocpp.cc
new file mode 100644
index 0000000..e92e1bb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/stream_reader_ctocpp.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=82b24259bb9ee73f15743e8b9970f5eeb7e47e7e$
+//
+
+#include "libcef_dll/ctocpp/stream_reader_ctocpp.h"
+#include "libcef_dll/cpptoc/read_handler_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefStreamReader> CefStreamReader::CreateForFile(
+    const CefString& fileName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(!fileName.empty());
+  if (fileName.empty())
+    return nullptr;
+
+  // Execute
+  cef_stream_reader_t* _retval =
+      cef_stream_reader_create_for_file(fileName.GetStruct());
+
+  // Return type: refptr_same
+  return CefStreamReaderCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefStreamReader> CefStreamReader::CreateForData(void* data,
+                                                          size_t size) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return nullptr;
+
+  // Execute
+  cef_stream_reader_t* _retval = cef_stream_reader_create_for_data(data, size);
+
+  // Return type: refptr_same
+  return CefStreamReaderCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefStreamReader> CefStreamReader::CreateForHandler(
+    CefRefPtr<CefReadHandler> handler) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler.get());
+  if (!handler.get())
+    return nullptr;
+
+  // Execute
+  cef_stream_reader_t* _retval =
+      cef_stream_reader_create_for_handler(CefReadHandlerCppToC::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefStreamReaderCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+size_t CefStreamReaderCToCpp::Read(void* ptr, size_t size, size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, read))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = _struct->read(_struct, ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefStreamReaderCToCpp::Seek(int64 offset, int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, seek))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->seek(_struct, offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefStreamReaderCToCpp::Tell() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, tell))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->tell(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefStreamReaderCToCpp::Eof() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, eof))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->eof(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefStreamReaderCToCpp::MayBlock() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, may_block))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->may_block(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefStreamReaderCToCpp::CefStreamReaderCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefStreamReaderCToCpp::~CefStreamReaderCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_stream_reader_t*
+CefCToCppRefCounted<CefStreamReaderCToCpp,
+                    CefStreamReader,
+                    cef_stream_reader_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefStreamReader* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefStreamReaderCToCpp,
+                                   CefStreamReader,
+                                   cef_stream_reader_t>::kWrapperType =
+    WT_STREAM_READER;
diff --git a/src/libcef_dll/ctocpp/stream_reader_ctocpp.h b/src/libcef_dll/ctocpp/stream_reader_ctocpp.h
new file mode 100644
index 0000000..9bf9123
--- /dev/null
+++ b/src/libcef_dll/ctocpp/stream_reader_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fda3483ae3ca295184f60189c0c47c1e88994700$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_STREAM_READER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_STREAM_READER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefStreamReaderCToCpp : public CefCToCppRefCounted<CefStreamReaderCToCpp,
+                                                         CefStreamReader,
+                                                         cef_stream_reader_t> {
+ public:
+  CefStreamReaderCToCpp();
+  virtual ~CefStreamReaderCToCpp();
+
+  // CefStreamReader methods.
+  size_t Read(void* ptr, size_t size, size_t n) OVERRIDE;
+  int Seek(int64 offset, int whence) OVERRIDE;
+  int64 Tell() OVERRIDE;
+  int Eof() OVERRIDE;
+  bool MayBlock() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_STREAM_READER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/stream_writer_ctocpp.cc b/src/libcef_dll/ctocpp/stream_writer_ctocpp.cc
new file mode 100644
index 0000000..b664a56
--- /dev/null
+++ b/src/libcef_dll/ctocpp/stream_writer_ctocpp.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1db7fdb06286f5c56a73b837e1f2bc30ae5ae536$
+//
+
+#include "libcef_dll/ctocpp/stream_writer_ctocpp.h"
+#include "libcef_dll/cpptoc/write_handler_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefStreamWriter> CefStreamWriter::CreateForFile(
+    const CefString& fileName) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(!fileName.empty());
+  if (fileName.empty())
+    return nullptr;
+
+  // Execute
+  cef_stream_writer_t* _retval =
+      cef_stream_writer_create_for_file(fileName.GetStruct());
+
+  // Return type: refptr_same
+  return CefStreamWriterCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefStreamWriter> CefStreamWriter::CreateForHandler(
+    CefRefPtr<CefWriteHandler> handler) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler.get());
+  if (!handler.get())
+    return nullptr;
+
+  // Execute
+  cef_stream_writer_t* _retval = cef_stream_writer_create_for_handler(
+      CefWriteHandlerCppToC::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefStreamWriterCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+size_t CefStreamWriterCToCpp::Write(const void* ptr, size_t size, size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_writer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, write))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = _struct->write(_struct, ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefStreamWriterCToCpp::Seek(int64 offset, int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_writer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, seek))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->seek(_struct, offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefStreamWriterCToCpp::Tell() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_writer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, tell))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->tell(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefStreamWriterCToCpp::Flush() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_writer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, flush))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->flush(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefStreamWriterCToCpp::MayBlock() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_stream_writer_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, may_block))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->may_block(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefStreamWriterCToCpp::CefStreamWriterCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefStreamWriterCToCpp::~CefStreamWriterCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_stream_writer_t*
+CefCToCppRefCounted<CefStreamWriterCToCpp,
+                    CefStreamWriter,
+                    cef_stream_writer_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefStreamWriter* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefStreamWriterCToCpp,
+                                   CefStreamWriter,
+                                   cef_stream_writer_t>::kWrapperType =
+    WT_STREAM_WRITER;
diff --git a/src/libcef_dll/ctocpp/stream_writer_ctocpp.h b/src/libcef_dll/ctocpp/stream_writer_ctocpp.h
new file mode 100644
index 0000000..096a1b0
--- /dev/null
+++ b/src/libcef_dll/ctocpp/stream_writer_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=aaca7be8cdc8d2b3142e505dd38d1785de58b1cb$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_STREAM_WRITER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_STREAM_WRITER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefStreamWriterCToCpp : public CefCToCppRefCounted<CefStreamWriterCToCpp,
+                                                         CefStreamWriter,
+                                                         cef_stream_writer_t> {
+ public:
+  CefStreamWriterCToCpp();
+  virtual ~CefStreamWriterCToCpp();
+
+  // CefStreamWriter methods.
+  size_t Write(const void* ptr, size_t size, size_t n) OVERRIDE;
+  int Seek(int64 offset, int whence) OVERRIDE;
+  int64 Tell() OVERRIDE;
+  int Flush() OVERRIDE;
+  bool MayBlock() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_STREAM_WRITER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/string_visitor_ctocpp.cc b/src/libcef_dll/ctocpp/string_visitor_ctocpp.cc
new file mode 100644
index 0000000..243d413
--- /dev/null
+++ b/src/libcef_dll/ctocpp/string_visitor_ctocpp.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b4199e85d500f9c22ce48472b0dc14eb581f8d84$
+//
+
+#include "libcef_dll/ctocpp/string_visitor_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefStringVisitorCToCpp::Visit(const CefString& string) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_string_visitor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: string
+
+  // Execute
+  _struct->visit(_struct, string.GetStruct());
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefStringVisitorCToCpp::CefStringVisitorCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefStringVisitorCToCpp::~CefStringVisitorCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_string_visitor_t*
+CefCToCppRefCounted<CefStringVisitorCToCpp,
+                    CefStringVisitor,
+                    cef_string_visitor_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefStringVisitor* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefStringVisitorCToCpp,
+                                   CefStringVisitor,
+                                   cef_string_visitor_t>::kWrapperType =
+    WT_STRING_VISITOR;
diff --git a/src/libcef_dll/ctocpp/string_visitor_ctocpp.h b/src/libcef_dll/ctocpp/string_visitor_ctocpp.h
new file mode 100644
index 0000000..366c654
--- /dev/null
+++ b/src/libcef_dll/ctocpp/string_visitor_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=69a583ccea07a3e2b3bd5f61446a22b3fdcac125$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_STRING_VISITOR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_STRING_VISITOR_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_string_visitor_capi.h"
+#include "include/cef_string_visitor.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefStringVisitorCToCpp
+    : public CefCToCppRefCounted<CefStringVisitorCToCpp,
+                                 CefStringVisitor,
+                                 cef_string_visitor_t> {
+ public:
+  CefStringVisitorCToCpp();
+  virtual ~CefStringVisitorCToCpp();
+
+  // CefStringVisitor methods.
+  void Visit(const CefString& string) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_STRING_VISITOR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/task_ctocpp.cc b/src/libcef_dll/ctocpp/task_ctocpp.cc
new file mode 100644
index 0000000..a5feb30
--- /dev/null
+++ b/src/libcef_dll/ctocpp/task_ctocpp.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3a86994454947714f641ccc5c318b4afc9841cca$
+//
+
+#include "libcef_dll/ctocpp/task_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefTaskCToCpp::Execute() {
+  cef_task_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->execute(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTaskCToCpp::CefTaskCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTaskCToCpp::~CefTaskCToCpp() {}
+
+template <>
+cef_task_t*
+CefCToCppRefCounted<CefTaskCToCpp, CefTask, cef_task_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefTask* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefTaskCToCpp, CefTask, cef_task_t>::kWrapperType =
+        WT_TASK;
diff --git a/src/libcef_dll/ctocpp/task_ctocpp.h b/src/libcef_dll/ctocpp/task_ctocpp.h
new file mode 100644
index 0000000..be99274
--- /dev/null
+++ b/src/libcef_dll/ctocpp/task_ctocpp.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=70354f833d79fc211f5e9aaef273fbd638fe57f3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TASK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TASK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_task_capi.h"
+#include "include/cef_task.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefTaskCToCpp
+    : public CefCToCppRefCounted<CefTaskCToCpp, CefTask, cef_task_t> {
+ public:
+  CefTaskCToCpp();
+  virtual ~CefTaskCToCpp();
+
+  // CefTask methods.
+  void Execute() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TASK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/task_runner_ctocpp.cc b/src/libcef_dll/ctocpp/task_runner_ctocpp.cc
new file mode 100644
index 0000000..4980169
--- /dev/null
+++ b/src/libcef_dll/ctocpp/task_runner_ctocpp.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3d4ea30711c0e3c716585f14b4122cdd57eba499$
+//
+
+#include "libcef_dll/ctocpp/task_runner_ctocpp.h"
+#include "libcef_dll/cpptoc/task_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTaskRunner> CefTaskRunner::GetForCurrentThread() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_task_runner_t* _retval = cef_task_runner_get_for_current_thread();
+
+  // Return type: refptr_same
+  return CefTaskRunnerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTaskRunner> CefTaskRunner::GetForThread(CefThreadId threadId) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_task_runner_t* _retval = cef_task_runner_get_for_thread(threadId);
+
+  // Return type: refptr_same
+  return CefTaskRunnerCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefTaskRunnerCToCpp::IsSame(CefRefPtr<CefTaskRunner> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_task_runner_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefTaskRunnerCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTaskRunnerCToCpp::BelongsToCurrentThread() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_task_runner_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, belongs_to_current_thread))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->belongs_to_current_thread(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTaskRunnerCToCpp::BelongsToThread(CefThreadId threadId) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_task_runner_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, belongs_to_thread))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->belongs_to_thread(_struct, threadId);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTaskRunnerCToCpp::PostTask(CefRefPtr<CefTask> task) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_task_runner_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, post_task))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: task; type: refptr_diff
+  DCHECK(task.get());
+  if (!task.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->post_task(_struct, CefTaskCppToC::Wrap(task));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTaskRunnerCToCpp::PostDelayedTask(CefRefPtr<CefTask> task,
+                                          int64 delay_ms) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_task_runner_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, post_delayed_task))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: task; type: refptr_diff
+  DCHECK(task.get());
+  if (!task.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->post_delayed_task(_struct, CefTaskCppToC::Wrap(task), delay_ms);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTaskRunnerCToCpp::CefTaskRunnerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTaskRunnerCToCpp::~CefTaskRunnerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_task_runner_t*
+CefCToCppRefCounted<CefTaskRunnerCToCpp, CefTaskRunner, cef_task_runner_t>::
+    UnwrapDerived(CefWrapperType type, CefTaskRunner* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefTaskRunnerCToCpp,
+                                   CefTaskRunner,
+                                   cef_task_runner_t>::kWrapperType =
+    WT_TASK_RUNNER;
diff --git a/src/libcef_dll/ctocpp/task_runner_ctocpp.h b/src/libcef_dll/ctocpp/task_runner_ctocpp.h
new file mode 100644
index 0000000..baa257c
--- /dev/null
+++ b/src/libcef_dll/ctocpp/task_runner_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cfd8f2fd4562fa69006b6983bf495e48b19cc4f5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TASK_RUNNER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TASK_RUNNER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_task_capi.h"
+#include "include/cef_task.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTaskRunnerCToCpp : public CefCToCppRefCounted<CefTaskRunnerCToCpp,
+                                                       CefTaskRunner,
+                                                       cef_task_runner_t> {
+ public:
+  CefTaskRunnerCToCpp();
+  virtual ~CefTaskRunnerCToCpp();
+
+  // CefTaskRunner methods.
+  bool IsSame(CefRefPtr<CefTaskRunner> that) OVERRIDE;
+  bool BelongsToCurrentThread() OVERRIDE;
+  bool BelongsToThread(CefThreadId threadId) OVERRIDE;
+  bool PostTask(CefRefPtr<CefTask> task) OVERRIDE;
+  bool PostDelayedTask(CefRefPtr<CefTask> task, int64 delay_ms) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TASK_RUNNER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_ctocpp.cc
new file mode 100644
index 0000000..3638913
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ctocpp.cc
@@ -0,0 +1,1562 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4ccc62b0449038044535576aed814a925db93432$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_ctocpp.h"
+#include <algorithm>
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_client_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_ref_ptr_client_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_client_child_cpptoc.h"
+#include "libcef_dll/cpptoc/test/translator_test_scoped_client_cpptoc.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTest> CefTranslatorTest::Create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_t* _retval = cef_translator_test_create();
+
+  // Return type: refptr_same
+  return CefTranslatorTestCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefTranslatorTestCToCpp::GetVoid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_void))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->get_void(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefTranslatorTestCToCpp::GetBool() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_bool(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefTranslatorTestCToCpp::GetInt() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_int))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_int(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") double CefTranslatorTestCToCpp::GetDouble() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_double))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  double _retval = _struct->get_double(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") long CefTranslatorTestCToCpp::GetLong() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_long))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  long _retval = _struct->get_long(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefTranslatorTestCToCpp::GetSizet() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_sizet))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_sizet(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTranslatorTestCToCpp::SetVoid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_void))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_void(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTranslatorTestCToCpp::SetBool(bool val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_bool(_struct, val);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTranslatorTestCToCpp::SetInt(int val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_int))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_int(_struct, val);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTranslatorTestCToCpp::SetDouble(double val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_double))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_double(_struct, val);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTranslatorTestCToCpp::SetLong(long val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_long))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_long(_struct, val);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTranslatorTestCToCpp::SetSizet(size_t val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_sizet))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_sizet(_struct, val);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetIntList(const std::vector<int>& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_int_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: simple_vec_byref_const
+  const size_t valCount = val.size();
+  int* valList = NULL;
+  if (valCount > 0) {
+    valList = new int[valCount];
+    DCHECK(valList);
+    if (valList) {
+      for (size_t i = 0; i < valCount; ++i) {
+        valList[i] = val[i];
+      }
+    }
+  }
+
+  // Execute
+  int _retval = _struct->set_int_list(_struct, valCount, valList);
+
+  // Restore param:val; type: simple_vec_byref_const
+  if (valList)
+    delete[] valList;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::GetIntListByRef(IntList& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_int_list_by_ref))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: simple_vec_byref
+  size_t valSize = val.size();
+  size_t valCount = std::max(GetIntListSize(), valSize);
+  int* valList = NULL;
+  if (valCount > 0) {
+    valList = new int[valCount];
+    DCHECK(valList);
+    if (valList) {
+      memset(valList, 0, sizeof(int) * valCount);
+    }
+    if (valList && valSize > 0) {
+      for (size_t i = 0; i < valSize; ++i) {
+        valList[i] = val[i];
+      }
+    }
+  }
+
+  // Execute
+  int _retval = _struct->get_int_list_by_ref(_struct, &valCount, valList);
+
+  // Restore param:val; type: simple_vec_byref
+  val.clear();
+  if (valCount > 0 && valList) {
+    for (size_t i = 0; i < valCount; ++i) {
+      val.push_back(valList[i]);
+    }
+    delete[] valList;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefTranslatorTestCToCpp::GetIntListSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_int_list_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_int_list_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefTranslatorTestCToCpp::GetString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetString(const CefString& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_string))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: string_byref_const
+  DCHECK(!val.empty());
+  if (val.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_string(_struct, val.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestCToCpp::GetStringByRef(CefString& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string_by_ref))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->get_string_by_ref(_struct, val.GetWritableStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetStringList(const std::vector<CefString>& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_string_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: string_vec_byref_const
+  cef_string_list_t valList = cef_string_list_alloc();
+  DCHECK(valList);
+  if (valList)
+    transfer_string_list_contents(val, valList);
+
+  // Execute
+  int _retval = _struct->set_string_list(_struct, valList);
+
+  // Restore param:val; type: string_vec_byref_const
+  if (valList)
+    cef_string_list_free(valList);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::GetStringListByRef(StringList& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string_list_by_ref))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: string_vec_byref
+  cef_string_list_t valList = cef_string_list_alloc();
+  DCHECK(valList);
+  if (valList)
+    transfer_string_list_contents(val, valList);
+
+  // Execute
+  int _retval = _struct->get_string_list_by_ref(_struct, valList);
+
+  // Restore param:val; type: string_vec_byref
+  if (valList) {
+    val.clear();
+    transfer_string_list_contents(valList, val);
+    cef_string_list_free(valList);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetStringMap(const StringMap& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_string_map))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: string_map_single_byref_const
+  cef_string_map_t valMap = cef_string_map_alloc();
+  DCHECK(valMap);
+  if (valMap)
+    transfer_string_map_contents(val, valMap);
+
+  // Execute
+  int _retval = _struct->set_string_map(_struct, valMap);
+
+  // Restore param:val; type: string_map_single_byref_const
+  if (valMap)
+    cef_string_map_free(valMap);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::GetStringMapByRef(
+    std::map<CefString, CefString>& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string_map_by_ref))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: string_map_single_byref
+  cef_string_map_t valMap = cef_string_map_alloc();
+  DCHECK(valMap);
+  if (valMap)
+    transfer_string_map_contents(val, valMap);
+
+  // Execute
+  int _retval = _struct->get_string_map_by_ref(_struct, valMap);
+
+  // Restore param:val; type: string_map_single_byref
+  if (valMap) {
+    val.clear();
+    transfer_string_map_contents(valMap, val);
+    cef_string_map_free(valMap);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetStringMultimap(
+    const std::multimap<CefString, CefString>& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_string_multimap))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: string_map_multi_byref_const
+  cef_string_multimap_t valMultimap = cef_string_multimap_alloc();
+  DCHECK(valMultimap);
+  if (valMultimap)
+    transfer_string_multimap_contents(val, valMultimap);
+
+  // Execute
+  int _retval = _struct->set_string_multimap(_struct, valMultimap);
+
+  // Restore param:val; type: string_map_multi_byref_const
+  if (valMultimap)
+    cef_string_multimap_free(valMultimap);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::GetStringMultimapByRef(StringMultimap& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string_multimap_by_ref))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: string_map_multi_byref
+  cef_string_multimap_t valMultimap = cef_string_multimap_alloc();
+  DCHECK(valMultimap);
+  if (valMultimap)
+    transfer_string_multimap_contents(val, valMultimap);
+
+  // Execute
+  int _retval = _struct->get_string_multimap_by_ref(_struct, valMultimap);
+
+  // Restore param:val; type: string_map_multi_byref
+  if (valMultimap) {
+    val.clear();
+    transfer_string_multimap_contents(valMultimap, val);
+    cef_string_multimap_free(valMultimap);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefTranslatorTestCToCpp::GetPoint() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_point))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_point(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetPoint(const CefPoint& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_point))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_point(_struct, &val);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestCToCpp::GetPointByRef(CefPoint& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_point_by_ref))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->get_point_by_ref(_struct, &val);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetPointList(const std::vector<CefPoint>& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_point_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: simple_vec_byref_const
+  const size_t valCount = val.size();
+  cef_point_t* valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_point_t[valCount];
+    DCHECK(valList);
+    if (valList) {
+      for (size_t i = 0; i < valCount; ++i) {
+        valList[i] = val[i];
+      }
+    }
+  }
+
+  // Execute
+  int _retval = _struct->set_point_list(_struct, valCount, valList);
+
+  // Restore param:val; type: simple_vec_byref_const
+  if (valList)
+    delete[] valList;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::GetPointListByRef(PointList& val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_point_list_by_ref))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: simple_vec_byref
+  size_t valSize = val.size();
+  size_t valCount = std::max(GetPointListSize(), valSize);
+  cef_point_t* valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_point_t[valCount];
+    DCHECK(valList);
+    if (valList) {
+      memset(valList, 0, sizeof(cef_point_t) * valCount);
+    }
+    if (valList && valSize > 0) {
+      for (size_t i = 0; i < valSize; ++i) {
+        valList[i] = val[i];
+      }
+    }
+  }
+
+  // Execute
+  int _retval = _struct->get_point_list_by_ref(_struct, &valCount, valList);
+
+  // Restore param:val; type: simple_vec_byref
+  val.clear();
+  if (valCount > 0 && valList) {
+    for (size_t i = 0; i < valCount; ++i) {
+      val.push_back(valList[i]);
+    }
+    delete[] valList;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefTranslatorTestCToCpp::GetPointListSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_point_list_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_point_list_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrLibrary>
+CefTranslatorTestCToCpp::GetRefPtrLibrary(int val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_ref_ptr_library))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_ref_ptr_library_t* _retval =
+      _struct->get_ref_ptr_library(_struct, val);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetRefPtrLibrary(
+    CefRefPtr<CefTranslatorTestRefPtrLibrary> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_ref_ptr_library))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_ref_ptr_library(
+      _struct, CefTranslatorTestRefPtrLibraryCToCpp::Unwrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrLibrary>
+CefTranslatorTestCToCpp::SetRefPtrLibraryAndReturn(
+    CefRefPtr<CefTranslatorTestRefPtrLibrary> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_ref_ptr_library_and_return))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return nullptr;
+
+  // Execute
+  cef_translator_test_ref_ptr_library_t* _retval =
+      _struct->set_ref_ptr_library_and_return(
+          _struct, CefTranslatorTestRefPtrLibraryCToCpp::Unwrap(val));
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetChildRefPtrLibrary(
+    CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_ref_ptr_library))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_child_ref_ptr_library(
+      _struct, CefTranslatorTestRefPtrLibraryChildCToCpp::Unwrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrLibrary>
+CefTranslatorTestCToCpp::SetChildRefPtrLibraryAndReturnParent(
+    CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_ref_ptr_library_and_return_parent))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return nullptr;
+
+  // Execute
+  cef_translator_test_ref_ptr_library_t* _retval =
+      _struct->set_child_ref_ptr_library_and_return_parent(
+          _struct, CefTranslatorTestRefPtrLibraryChildCToCpp::Unwrap(val));
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetRefPtrLibraryList(
+    const std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>>& val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_ref_ptr_library_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: refptr_vec_same_byref_const
+  const size_t valCount = val.size();
+  cef_translator_test_ref_ptr_library_t** valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_translator_test_ref_ptr_library_t*[valCount];
+    DCHECK(valList);
+    if (valList) {
+      for (size_t i = 0; i < valCount; ++i) {
+        valList[i] = CefTranslatorTestRefPtrLibraryCToCpp::Unwrap(val[i]);
+      }
+    }
+  }
+
+  // Execute
+  int _retval =
+      _struct->set_ref_ptr_library_list(_struct, valCount, valList, val1, val2);
+
+  // Restore param:val; type: refptr_vec_same_byref_const
+  if (valList)
+    delete[] valList;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::GetRefPtrLibraryListByRef(RefPtrLibraryList& val,
+                                                        int val1,
+                                                        int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_ref_ptr_library_list_by_ref))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: refptr_vec_same_byref
+  size_t valSize = val.size();
+  size_t valCount = std::max(GetRefPtrLibraryListSize(), valSize);
+  cef_translator_test_ref_ptr_library_t** valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_translator_test_ref_ptr_library_t*[valCount];
+    DCHECK(valList);
+    if (valList) {
+      memset(valList, 0,
+             sizeof(cef_translator_test_ref_ptr_library_t*) * valCount);
+    }
+    if (valList && valSize > 0) {
+      for (size_t i = 0; i < valSize; ++i) {
+        valList[i] = CefTranslatorTestRefPtrLibraryCToCpp::Unwrap(val[i]);
+      }
+    }
+  }
+
+  // Execute
+  int _retval = _struct->get_ref_ptr_library_list_by_ref(_struct, &valCount,
+                                                         valList, val1, val2);
+
+  // Restore param:val; type: refptr_vec_same_byref
+  val.clear();
+  if (valCount > 0 && valList) {
+    for (size_t i = 0; i < valCount; ++i) {
+      val.push_back(CefTranslatorTestRefPtrLibraryCToCpp::Wrap(valList[i]));
+    }
+    delete[] valList;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+size_t CefTranslatorTestCToCpp::GetRefPtrLibraryListSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_ref_ptr_library_list_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_ref_ptr_library_list_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetRefPtrClient(
+    CefRefPtr<CefTranslatorTestRefPtrClient> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_ref_ptr_client))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_ref_ptr_client(
+      _struct, CefTranslatorTestRefPtrClientCppToC::Wrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrClient>
+CefTranslatorTestCToCpp::SetRefPtrClientAndReturn(
+    CefRefPtr<CefTranslatorTestRefPtrClient> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_ref_ptr_client_and_return))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return nullptr;
+
+  // Execute
+  cef_translator_test_ref_ptr_client_t* _retval =
+      _struct->set_ref_ptr_client_and_return(
+          _struct, CefTranslatorTestRefPtrClientCppToC::Wrap(val));
+
+  // Return type: refptr_diff
+  return CefTranslatorTestRefPtrClientCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetChildRefPtrClient(
+    CefRefPtr<CefTranslatorTestRefPtrClientChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_ref_ptr_client))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_child_ref_ptr_client(
+      _struct, CefTranslatorTestRefPtrClientChildCppToC::Wrap(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrClient>
+CefTranslatorTestCToCpp::SetChildRefPtrClientAndReturnParent(
+    CefRefPtr<CefTranslatorTestRefPtrClientChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_ref_ptr_client_and_return_parent))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: refptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return nullptr;
+
+  // Execute
+  cef_translator_test_ref_ptr_client_t* _retval =
+      _struct->set_child_ref_ptr_client_and_return_parent(
+          _struct, CefTranslatorTestRefPtrClientChildCppToC::Wrap(val));
+
+  // Return type: refptr_diff
+  return CefTranslatorTestRefPtrClientCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetRefPtrClientList(
+    const std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>>& val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_ref_ptr_client_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: refptr_vec_diff_byref_const
+  const size_t valCount = val.size();
+  cef_translator_test_ref_ptr_client_t** valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_translator_test_ref_ptr_client_t*[valCount];
+    DCHECK(valList);
+    if (valList) {
+      for (size_t i = 0; i < valCount; ++i) {
+        valList[i] = CefTranslatorTestRefPtrClientCppToC::Wrap(val[i]);
+      }
+    }
+  }
+
+  // Execute
+  int _retval =
+      _struct->set_ref_ptr_client_list(_struct, valCount, valList, val1, val2);
+
+  // Restore param:val; type: refptr_vec_diff_byref_const
+  if (valList)
+    delete[] valList;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::GetRefPtrClientListByRef(
+    RefPtrClientList& val,
+    CefRefPtr<CefTranslatorTestRefPtrClient> val1,
+    CefRefPtr<CefTranslatorTestRefPtrClient> val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_ref_ptr_client_list_by_ref))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val1; type: refptr_diff
+  DCHECK(val1.get());
+  if (!val1.get())
+    return false;
+  // Verify param: val2; type: refptr_diff
+  DCHECK(val2.get());
+  if (!val2.get())
+    return false;
+
+  // Translate param: val; type: refptr_vec_diff_byref
+  size_t valSize = val.size();
+  size_t valCount = std::max(GetRefPtrLibraryListSize(), valSize);
+  cef_translator_test_ref_ptr_client_t** valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_translator_test_ref_ptr_client_t*[valCount];
+    DCHECK(valList);
+    if (valList) {
+      memset(valList, 0,
+             sizeof(cef_translator_test_ref_ptr_client_t*) * valCount);
+    }
+    if (valList && valSize > 0) {
+      for (size_t i = 0; i < valSize; ++i) {
+        valList[i] = CefTranslatorTestRefPtrClientCppToC::Wrap(val[i]);
+      }
+    }
+  }
+
+  // Execute
+  int _retval = _struct->get_ref_ptr_client_list_by_ref(
+      _struct, &valCount, valList,
+      CefTranslatorTestRefPtrClientCppToC::Wrap(val1),
+      CefTranslatorTestRefPtrClientCppToC::Wrap(val2));
+
+  // Restore param:val; type: refptr_vec_diff_byref
+  val.clear();
+  if (valCount > 0 && valList) {
+    for (size_t i = 0; i < valCount; ++i) {
+      val.push_back(CefTranslatorTestRefPtrClientCppToC::Unwrap(valList[i]));
+    }
+    delete[] valList;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+size_t CefTranslatorTestCToCpp::GetRefPtrClientListSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_ref_ptr_client_list_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_ref_ptr_client_list_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedLibrary>
+CefTranslatorTestCToCpp::GetOwnPtrLibrary(int val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_own_ptr_library))
+    return CefOwnPtr<CefTranslatorTestScopedLibrary>();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_scoped_library_t* _retval =
+      _struct->get_own_ptr_library(_struct, val);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetOwnPtrLibrary(
+    CefOwnPtr<CefTranslatorTestScopedLibrary> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_own_ptr_library))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_own_ptr_library(
+      _struct, CefTranslatorTestScopedLibraryCToCpp::UnwrapOwn(OWN_PASS(val)));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedLibrary>
+CefTranslatorTestCToCpp::SetOwnPtrLibraryAndReturn(
+    CefOwnPtr<CefTranslatorTestScopedLibrary> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_own_ptr_library_and_return))
+    return CefOwnPtr<CefTranslatorTestScopedLibrary>();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return CefOwnPtr<CefTranslatorTestScopedLibrary>();
+
+  // Execute
+  cef_translator_test_scoped_library_t* _retval =
+      _struct->set_own_ptr_library_and_return(
+          _struct,
+          CefTranslatorTestScopedLibraryCToCpp::UnwrapOwn(OWN_PASS(val)));
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetChildOwnPtrLibrary(
+    CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_own_ptr_library))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_child_own_ptr_library(
+      _struct,
+      CefTranslatorTestScopedLibraryChildCToCpp::UnwrapOwn(OWN_PASS(val)));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedLibrary>
+CefTranslatorTestCToCpp::SetChildOwnPtrLibraryAndReturnParent(
+    CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_own_ptr_library_and_return_parent))
+    return CefOwnPtr<CefTranslatorTestScopedLibrary>();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_same
+  DCHECK(val.get());
+  if (!val.get())
+    return CefOwnPtr<CefTranslatorTestScopedLibrary>();
+
+  // Execute
+  cef_translator_test_scoped_library_t* _retval =
+      _struct->set_child_own_ptr_library_and_return_parent(
+          _struct,
+          CefTranslatorTestScopedLibraryChildCToCpp::UnwrapOwn(OWN_PASS(val)));
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetOwnPtrClient(
+    CefOwnPtr<CefTranslatorTestScopedClient> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_own_ptr_client))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_own_ptr_client(
+      _struct, CefTranslatorTestScopedClientCppToC::WrapOwn(OWN_PASS(val)));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedClient>
+CefTranslatorTestCToCpp::SetOwnPtrClientAndReturn(
+    CefOwnPtr<CefTranslatorTestScopedClient> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_own_ptr_client_and_return))
+    return CefOwnPtr<CefTranslatorTestScopedClient>();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return CefOwnPtr<CefTranslatorTestScopedClient>();
+
+  // Execute
+  cef_translator_test_scoped_client_t* _retval =
+      _struct->set_own_ptr_client_and_return(
+          _struct, CefTranslatorTestScopedClientCppToC::WrapOwn(OWN_PASS(val)));
+
+  // Return type: ownptr_diff
+  return CefTranslatorTestScopedClientCppToC::UnwrapOwn(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetChildOwnPtrClient(
+    CefOwnPtr<CefTranslatorTestScopedClientChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_own_ptr_client))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_child_own_ptr_client(
+      _struct,
+      CefTranslatorTestScopedClientChildCppToC::WrapOwn(OWN_PASS(val)));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedClient>
+CefTranslatorTestCToCpp::SetChildOwnPtrClientAndReturnParent(
+    CefOwnPtr<CefTranslatorTestScopedClientChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_own_ptr_client_and_return_parent))
+    return CefOwnPtr<CefTranslatorTestScopedClient>();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: ownptr_diff
+  DCHECK(val.get());
+  if (!val.get())
+    return CefOwnPtr<CefTranslatorTestScopedClient>();
+
+  // Execute
+  cef_translator_test_scoped_client_t* _retval =
+      _struct->set_child_own_ptr_client_and_return_parent(
+          _struct,
+          CefTranslatorTestScopedClientChildCppToC::WrapOwn(OWN_PASS(val)));
+
+  // Return type: ownptr_diff
+  return CefTranslatorTestScopedClientCppToC::UnwrapOwn(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetRawPtrLibrary(
+    CefRawPtr<CefTranslatorTestScopedLibrary> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_raw_ptr_library))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: rawptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_raw_ptr_library(
+      _struct, CefTranslatorTestScopedLibraryCToCpp::UnwrapRaw(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetChildRawPtrLibrary(
+    CefRawPtr<CefTranslatorTestScopedLibraryChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_raw_ptr_library))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: rawptr_same
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Execute
+  int _retval = _struct->set_child_raw_ptr_library(
+      _struct, CefTranslatorTestScopedLibraryChildCToCpp::UnwrapRaw(val));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetRawPtrLibraryList(
+    const std::vector<CefRawPtr<CefTranslatorTestScopedLibrary>>& val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_raw_ptr_library_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: rawptr_vec_same_byref_const
+  const size_t valCount = val.size();
+  cef_translator_test_scoped_library_t** valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_translator_test_scoped_library_t*[valCount];
+    DCHECK(valList);
+    if (valList) {
+      for (size_t i = 0; i < valCount; ++i) {
+        valList[i] = CefTranslatorTestScopedLibraryCToCpp::UnwrapRaw(val[i]);
+      }
+    }
+  }
+
+  // Execute
+  int _retval =
+      _struct->set_raw_ptr_library_list(_struct, valCount, valList, val1, val2);
+
+  // Restore param:val; type: rawptr_vec_same_byref_const
+  if (valList)
+    delete[] valList;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetRawPtrClient(
+    CefRawPtr<CefTranslatorTestScopedClient> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_raw_ptr_client))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: rawptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: rawptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClientCppToC> valPtr(
+      CefTranslatorTestScopedClientCppToC::WrapRaw(val));
+
+  // Execute
+  int _retval = _struct->set_raw_ptr_client(_struct, valPtr->GetStruct());
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestCToCpp::SetChildRawPtrClient(
+    CefRawPtr<CefTranslatorTestScopedClientChild> val) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_child_raw_ptr_client))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: val; type: rawptr_diff
+  DCHECK(val);
+  if (!val)
+    return 0;
+
+  // Translate param: val; type: rawptr_diff
+  CefOwnPtr<CefTranslatorTestScopedClientChildCppToC> valPtr(
+      CefTranslatorTestScopedClientChildCppToC::WrapRaw(val));
+
+  // Execute
+  int _retval = _struct->set_child_raw_ptr_client(_struct, valPtr->GetStruct());
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTranslatorTestCToCpp::SetRawPtrClientList(
+    const std::vector<CefRawPtr<CefTranslatorTestScopedClient>>& val,
+    int val1,
+    int val2) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_raw_ptr_client_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: val; type: rawptr_vec_diff_byref_const
+  const size_t valCount = val.size();
+  cef_translator_test_scoped_client_t** valList = NULL;
+  if (valCount > 0) {
+    valList = new cef_translator_test_scoped_client_t*[valCount];
+    DCHECK(valList);
+    if (valList) {
+      for (size_t i = 0; i < valCount; ++i) {
+        valList[i] = CefTranslatorTestScopedClientCppToC::WrapRaw(val[i])
+                         .release()
+                         ->GetStruct();
+      }
+    }
+  }
+
+  // Execute
+  int _retval =
+      _struct->set_raw_ptr_client_list(_struct, valCount, valList, val1, val2);
+
+  // Restore param:val; type: rawptr_vec_diff_byref_const
+  if (valCount > 0) {
+    for (size_t i = 0; i < valCount; ++i) {
+      delete CefTranslatorTestScopedClientCppToC::GetWrapper(valList[i]);
+    }
+  }
+  if (valList)
+    delete[] valList;
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestCToCpp::CefTranslatorTestCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestCToCpp::~CefTranslatorTestCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_translator_test_t* CefCToCppRefCounted<
+    CefTranslatorTestCToCpp,
+    CefTranslatorTest,
+    cef_translator_test_t>::UnwrapDerived(CefWrapperType type,
+                                          CefTranslatorTest* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefTranslatorTestCToCpp,
+                                   CefTranslatorTest,
+                                   cef_translator_test_t>::kWrapperType =
+    WT_TRANSLATOR_TEST;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_ctocpp.h
new file mode 100644
index 0000000..93db13b
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ctocpp.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e7302b507c09d89d0b5735136ed6e47867e56a00$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <map>
+#include <vector>
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestCToCpp
+    : public CefCToCppRefCounted<CefTranslatorTestCToCpp,
+                                 CefTranslatorTest,
+                                 cef_translator_test_t> {
+ public:
+  CefTranslatorTestCToCpp();
+  virtual ~CefTranslatorTestCToCpp();
+
+  // CefTranslatorTest methods.
+  void GetVoid() OVERRIDE;
+  bool GetBool() OVERRIDE;
+  int GetInt() OVERRIDE;
+  double GetDouble() OVERRIDE;
+  long GetLong() OVERRIDE;
+  size_t GetSizet() OVERRIDE;
+  bool SetVoid() OVERRIDE;
+  bool SetBool(bool val) OVERRIDE;
+  bool SetInt(int val) OVERRIDE;
+  bool SetDouble(double val) OVERRIDE;
+  bool SetLong(long val) OVERRIDE;
+  bool SetSizet(size_t val) OVERRIDE;
+  bool SetIntList(const std::vector<int>& val) OVERRIDE;
+  bool GetIntListByRef(IntList& val) OVERRIDE;
+  size_t GetIntListSize() OVERRIDE;
+  CefString GetString() OVERRIDE;
+  bool SetString(const CefString& val) OVERRIDE;
+  void GetStringByRef(CefString& val) OVERRIDE;
+  bool SetStringList(const std::vector<CefString>& val) OVERRIDE;
+  bool GetStringListByRef(StringList& val) OVERRIDE;
+  bool SetStringMap(const StringMap& val) OVERRIDE;
+  bool GetStringMapByRef(std::map<CefString, CefString>& val) OVERRIDE;
+  bool SetStringMultimap(
+      const std::multimap<CefString, CefString>& val) OVERRIDE;
+  bool GetStringMultimapByRef(StringMultimap& val) OVERRIDE;
+  CefPoint GetPoint() OVERRIDE;
+  bool SetPoint(const CefPoint& val) OVERRIDE;
+  void GetPointByRef(CefPoint& val) OVERRIDE;
+  bool SetPointList(const std::vector<CefPoint>& val) OVERRIDE;
+  bool GetPointListByRef(PointList& val) OVERRIDE;
+  size_t GetPointListSize() OVERRIDE;
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> GetRefPtrLibrary(int val) OVERRIDE;
+  int SetRefPtrLibrary(CefRefPtr<CefTranslatorTestRefPtrLibrary> val) OVERRIDE;
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> SetRefPtrLibraryAndReturn(
+      CefRefPtr<CefTranslatorTestRefPtrLibrary> val) OVERRIDE;
+  int SetChildRefPtrLibrary(
+      CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) OVERRIDE;
+  CefRefPtr<CefTranslatorTestRefPtrLibrary>
+  SetChildRefPtrLibraryAndReturnParent(
+      CefRefPtr<CefTranslatorTestRefPtrLibraryChild> val) OVERRIDE;
+  bool SetRefPtrLibraryList(
+      const std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>>& val,
+      int val1,
+      int val2) OVERRIDE;
+  bool GetRefPtrLibraryListByRef(RefPtrLibraryList& val,
+                                 int val1,
+                                 int val2) OVERRIDE;
+  size_t GetRefPtrLibraryListSize() OVERRIDE;
+  int SetRefPtrClient(CefRefPtr<CefTranslatorTestRefPtrClient> val) OVERRIDE;
+  CefRefPtr<CefTranslatorTestRefPtrClient> SetRefPtrClientAndReturn(
+      CefRefPtr<CefTranslatorTestRefPtrClient> val) OVERRIDE;
+  int SetChildRefPtrClient(
+      CefRefPtr<CefTranslatorTestRefPtrClientChild> val) OVERRIDE;
+  CefRefPtr<CefTranslatorTestRefPtrClient> SetChildRefPtrClientAndReturnParent(
+      CefRefPtr<CefTranslatorTestRefPtrClientChild> val) OVERRIDE;
+  bool SetRefPtrClientList(
+      const std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>>& val,
+      int val1,
+      int val2) OVERRIDE;
+  bool GetRefPtrClientListByRef(
+      RefPtrClientList& val,
+      CefRefPtr<CefTranslatorTestRefPtrClient> val1,
+      CefRefPtr<CefTranslatorTestRefPtrClient> val2) OVERRIDE;
+  size_t GetRefPtrClientListSize() OVERRIDE;
+  CefOwnPtr<CefTranslatorTestScopedLibrary> GetOwnPtrLibrary(int val) OVERRIDE;
+  int SetOwnPtrLibrary(CefOwnPtr<CefTranslatorTestScopedLibrary> val) OVERRIDE;
+  CefOwnPtr<CefTranslatorTestScopedLibrary> SetOwnPtrLibraryAndReturn(
+      CefOwnPtr<CefTranslatorTestScopedLibrary> val) OVERRIDE;
+  int SetChildOwnPtrLibrary(
+      CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) OVERRIDE;
+  CefOwnPtr<CefTranslatorTestScopedLibrary>
+  SetChildOwnPtrLibraryAndReturnParent(
+      CefOwnPtr<CefTranslatorTestScopedLibraryChild> val) OVERRIDE;
+  int SetOwnPtrClient(CefOwnPtr<CefTranslatorTestScopedClient> val) OVERRIDE;
+  CefOwnPtr<CefTranslatorTestScopedClient> SetOwnPtrClientAndReturn(
+      CefOwnPtr<CefTranslatorTestScopedClient> val) OVERRIDE;
+  int SetChildOwnPtrClient(
+      CefOwnPtr<CefTranslatorTestScopedClientChild> val) OVERRIDE;
+  CefOwnPtr<CefTranslatorTestScopedClient> SetChildOwnPtrClientAndReturnParent(
+      CefOwnPtr<CefTranslatorTestScopedClientChild> val) OVERRIDE;
+  int SetRawPtrLibrary(CefRawPtr<CefTranslatorTestScopedLibrary> val) OVERRIDE;
+  int SetChildRawPtrLibrary(
+      CefRawPtr<CefTranslatorTestScopedLibraryChild> val) OVERRIDE;
+  bool SetRawPtrLibraryList(
+      const std::vector<CefRawPtr<CefTranslatorTestScopedLibrary>>& val,
+      int val1,
+      int val2) OVERRIDE;
+  int SetRawPtrClient(CefRawPtr<CefTranslatorTestScopedClient> val) OVERRIDE;
+  int SetChildRawPtrClient(
+      CefRawPtr<CefTranslatorTestScopedClientChild> val) OVERRIDE;
+  bool SetRawPtrClientList(
+      const std::vector<CefRawPtr<CefTranslatorTestScopedClient>>& val,
+      int val1,
+      int val2) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.cc
new file mode 100644
index 0000000..411ccbf
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2453173215db3e380519cc01ccf4e6716728a3e6$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestRefPtrClientChildCToCpp::GetOtherValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_client_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestRefPtrClientChildCToCpp::GetValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_client_t* _struct =
+      reinterpret_cast<cef_translator_test_ref_ptr_client_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientChildCToCpp::
+    CefTranslatorTestRefPtrClientChildCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientChildCToCpp::
+    ~CefTranslatorTestRefPtrClientChildCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_translator_test_ref_ptr_client_child_t*
+CefCToCppRefCounted<CefTranslatorTestRefPtrClientChildCToCpp,
+                    CefTranslatorTestRefPtrClientChild,
+                    cef_translator_test_ref_ptr_client_child_t>::
+    UnwrapDerived(CefWrapperType type, CefTranslatorTestRefPtrClientChild* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<
+    CefTranslatorTestRefPtrClientChildCToCpp,
+    CefTranslatorTestRefPtrClientChild,
+    cef_translator_test_ref_ptr_client_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.h
new file mode 100644
index 0000000..ef7166e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a9a72db9d83fecf02678a6da859a4dc475914d28$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestRefPtrClientChildCToCpp
+    : public CefCToCppRefCounted<CefTranslatorTestRefPtrClientChildCToCpp,
+                                 CefTranslatorTestRefPtrClientChild,
+                                 cef_translator_test_ref_ptr_client_child_t> {
+ public:
+  CefTranslatorTestRefPtrClientChildCToCpp();
+  virtual ~CefTranslatorTestRefPtrClientChildCToCpp();
+
+  // CefTranslatorTestRefPtrClientChild methods.
+  int GetOtherValue() override;
+
+  // CefTranslatorTestRefPtrClient methods.
+  int GetValue() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.cc
new file mode 100644
index 0000000..0043cce
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a92bb99010dc728a39413e46e68a19cd2ac2906b$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_client_child_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") int CefTranslatorTestRefPtrClientCToCpp::GetValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientCToCpp::CefTranslatorTestRefPtrClientCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrClientCToCpp::~CefTranslatorTestRefPtrClientCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_translator_test_ref_ptr_client_t*
+CefCToCppRefCounted<CefTranslatorTestRefPtrClientCToCpp,
+                    CefTranslatorTestRefPtrClient,
+                    cef_translator_test_ref_ptr_client_t>::
+    UnwrapDerived(CefWrapperType type, CefTranslatorTestRefPtrClient* c) {
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD) {
+    return reinterpret_cast<cef_translator_test_ref_ptr_client_t*>(
+        CefTranslatorTestRefPtrClientChildCToCpp::Unwrap(
+            reinterpret_cast<CefTranslatorTestRefPtrClientChild*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefTranslatorTestRefPtrClientCToCpp,
+                        CefTranslatorTestRefPtrClient,
+                        cef_translator_test_ref_ptr_client_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_REF_PTR_CLIENT;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.h
new file mode 100644
index 0000000..3dae892
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_client_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=89be8d7884c7bee2df32fe683210d8728729bfee$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestRefPtrClientCToCpp
+    : public CefCToCppRefCounted<CefTranslatorTestRefPtrClientCToCpp,
+                                 CefTranslatorTestRefPtrClient,
+                                 cef_translator_test_ref_ptr_client_t> {
+ public:
+  CefTranslatorTestRefPtrClientCToCpp();
+  virtual ~CefTranslatorTestRefPtrClientCToCpp();
+
+  // CefTranslatorTestRefPtrClient methods.
+  int GetValue() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_CLIENT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.cc
new file mode 100644
index 0000000..ae1ec4e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=393e66b4a61fb024e637495a6015f571ae9f2934$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrLibraryChildChild>
+CefTranslatorTestRefPtrLibraryChildChild::Create(int value,
+                                                 int other_value,
+                                                 int other_other_value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_ref_ptr_library_child_child_t* _retval =
+      cef_translator_test_ref_ptr_library_child_child_create(value, other_value,
+                                                             other_other_value);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryChildChildCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestRefPtrLibraryChildChildCToCpp::GetOtherOtherValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_child_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_other_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestRefPtrLibraryChildChildCToCpp::SetOtherOtherValue(
+    int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_child_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_other_other_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_other_other_value(_struct, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestRefPtrLibraryChildChildCToCpp::GetOtherValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_child_t* _struct =
+      reinterpret_cast<cef_translator_test_ref_ptr_library_child_t*>(
+          GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestRefPtrLibraryChildChildCToCpp::SetOtherValue(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_child_t* _struct =
+      reinterpret_cast<cef_translator_test_ref_ptr_library_child_t*>(
+          GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_other_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_other_value(_struct, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestRefPtrLibraryChildChildCToCpp::GetValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_t* _struct =
+      reinterpret_cast<cef_translator_test_ref_ptr_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestRefPtrLibraryChildChildCToCpp::SetValue(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_t* _struct =
+      reinterpret_cast<cef_translator_test_ref_ptr_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_value(_struct, value);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildChildCToCpp::
+    CefTranslatorTestRefPtrLibraryChildChildCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildChildCToCpp::
+    ~CefTranslatorTestRefPtrLibraryChildChildCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_translator_test_ref_ptr_library_child_child_t*
+CefCToCppRefCounted<CefTranslatorTestRefPtrLibraryChildChildCToCpp,
+                    CefTranslatorTestRefPtrLibraryChildChild,
+                    cef_translator_test_ref_ptr_library_child_child_t>::
+    UnwrapDerived(CefWrapperType type,
+                  CefTranslatorTestRefPtrLibraryChildChild* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<
+    CefTranslatorTestRefPtrLibraryChildChildCToCpp,
+    CefTranslatorTestRefPtrLibraryChildChild,
+    cef_translator_test_ref_ptr_library_child_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.h
new file mode 100644
index 0000000..ded3e56
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8e4db948f3b947ffc17aa0b020307f5b05584fda$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestRefPtrLibraryChildChildCToCpp
+    : public CefCToCppRefCounted<
+          CefTranslatorTestRefPtrLibraryChildChildCToCpp,
+          CefTranslatorTestRefPtrLibraryChildChild,
+          cef_translator_test_ref_ptr_library_child_child_t> {
+ public:
+  CefTranslatorTestRefPtrLibraryChildChildCToCpp();
+  virtual ~CefTranslatorTestRefPtrLibraryChildChildCToCpp();
+
+  // CefTranslatorTestRefPtrLibraryChildChild methods.
+  int GetOtherOtherValue() OVERRIDE;
+  void SetOtherOtherValue(int value) OVERRIDE;
+
+  // CefTranslatorTestRefPtrLibraryChild methods.
+  int GetOtherValue() OVERRIDE;
+  void SetOtherValue(int value) OVERRIDE;
+
+  // CefTranslatorTestRefPtrLibrary methods.
+  int GetValue() OVERRIDE;
+  void SetValue(int value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.cc
new file mode 100644
index 0000000..204ef36
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=752d19a05cc2fd993f6a501894bd06aebfee72f2$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrLibraryChild>
+CefTranslatorTestRefPtrLibraryChild::Create(int value, int other_value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_ref_ptr_library_child_t* _retval =
+      cef_translator_test_ref_ptr_library_child_create(value, other_value);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryChildCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestRefPtrLibraryChildCToCpp::GetOtherValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestRefPtrLibraryChildCToCpp::SetOtherValue(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_other_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_other_value(_struct, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestRefPtrLibraryChildCToCpp::GetValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_t* _struct =
+      reinterpret_cast<cef_translator_test_ref_ptr_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestRefPtrLibraryChildCToCpp::SetValue(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_t* _struct =
+      reinterpret_cast<cef_translator_test_ref_ptr_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_value(_struct, value);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildCToCpp::
+    CefTranslatorTestRefPtrLibraryChildCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryChildCToCpp::
+    ~CefTranslatorTestRefPtrLibraryChildCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_translator_test_ref_ptr_library_child_t*
+CefCToCppRefCounted<CefTranslatorTestRefPtrLibraryChildCToCpp,
+                    CefTranslatorTestRefPtrLibraryChild,
+                    cef_translator_test_ref_ptr_library_child_t>::
+    UnwrapDerived(CefWrapperType type, CefTranslatorTestRefPtrLibraryChild* c) {
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD) {
+    return reinterpret_cast<cef_translator_test_ref_ptr_library_child_t*>(
+        CefTranslatorTestRefPtrLibraryChildChildCToCpp::Unwrap(
+            reinterpret_cast<CefTranslatorTestRefPtrLibraryChildChild*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<
+    CefTranslatorTestRefPtrLibraryChildCToCpp,
+    CefTranslatorTestRefPtrLibraryChild,
+    cef_translator_test_ref_ptr_library_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.h
new file mode 100644
index 0000000..461435d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=940f6bd84685ec544f18320104afda0062b7ad51$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestRefPtrLibraryChildCToCpp
+    : public CefCToCppRefCounted<CefTranslatorTestRefPtrLibraryChildCToCpp,
+                                 CefTranslatorTestRefPtrLibraryChild,
+                                 cef_translator_test_ref_ptr_library_child_t> {
+ public:
+  CefTranslatorTestRefPtrLibraryChildCToCpp();
+  virtual ~CefTranslatorTestRefPtrLibraryChildCToCpp();
+
+  // CefTranslatorTestRefPtrLibraryChild methods.
+  int GetOtherValue() OVERRIDE;
+  void SetOtherValue(int value) OVERRIDE;
+
+  // CefTranslatorTestRefPtrLibrary methods.
+  int GetValue() OVERRIDE;
+  void SetValue(int value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.cc
new file mode 100644
index 0000000..3e1809a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8bb13d683dd6c3dd98a0990de4b3e2396e9f9a1e$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_ref_ptr_library_child_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTranslatorTestRefPtrLibrary>
+CefTranslatorTestRefPtrLibrary::Create(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_ref_ptr_library_t* _retval =
+      cef_translator_test_ref_ptr_library_create(value);
+
+  // Return type: refptr_same
+  return CefTranslatorTestRefPtrLibraryCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") int CefTranslatorTestRefPtrLibraryCToCpp::GetValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestRefPtrLibraryCToCpp::SetValue(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_translator_test_ref_ptr_library_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_value(_struct, value);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryCToCpp::CefTranslatorTestRefPtrLibraryCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestRefPtrLibraryCToCpp::~CefTranslatorTestRefPtrLibraryCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_translator_test_ref_ptr_library_t*
+CefCToCppRefCounted<CefTranslatorTestRefPtrLibraryCToCpp,
+                    CefTranslatorTestRefPtrLibrary,
+                    cef_translator_test_ref_ptr_library_t>::
+    UnwrapDerived(CefWrapperType type, CefTranslatorTestRefPtrLibrary* c) {
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD) {
+    return reinterpret_cast<cef_translator_test_ref_ptr_library_t*>(
+        CefTranslatorTestRefPtrLibraryChildCToCpp::Unwrap(
+            reinterpret_cast<CefTranslatorTestRefPtrLibraryChild*>(c)));
+  }
+  if (type == WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD) {
+    return reinterpret_cast<cef_translator_test_ref_ptr_library_t*>(
+        CefTranslatorTestRefPtrLibraryChildChildCToCpp::Unwrap(
+            reinterpret_cast<CefTranslatorTestRefPtrLibraryChildChild*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefTranslatorTestRefPtrLibraryCToCpp,
+                        CefTranslatorTestRefPtrLibrary,
+                        cef_translator_test_ref_ptr_library_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_REF_PTR_LIBRARY;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.h
new file mode 100644
index 0000000..9e5c285
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_ref_ptr_library_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=26f49fae992d673accdd8d31dfc2c763a75087cb$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestRefPtrLibraryCToCpp
+    : public CefCToCppRefCounted<CefTranslatorTestRefPtrLibraryCToCpp,
+                                 CefTranslatorTestRefPtrLibrary,
+                                 cef_translator_test_ref_ptr_library_t> {
+ public:
+  CefTranslatorTestRefPtrLibraryCToCpp();
+  virtual ~CefTranslatorTestRefPtrLibraryCToCpp();
+
+  // CefTranslatorTestRefPtrLibrary methods.
+  int GetValue() OVERRIDE;
+  void SetValue(int value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_REF_PTR_LIBRARY_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.cc
new file mode 100644
index 0000000..53062a8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d9e908faae3ca204ade3cceff3471419b234dddb$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestScopedClientChildCToCpp::GetOtherValue() {
+  cef_translator_test_scoped_client_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestScopedClientChildCToCpp::GetValue() {
+  cef_translator_test_scoped_client_t* _struct =
+      reinterpret_cast<cef_translator_test_scoped_client_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientChildCToCpp::
+    CefTranslatorTestScopedClientChildCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientChildCToCpp::
+    ~CefTranslatorTestScopedClientChildCToCpp() {}
+
+template <>
+cef_translator_test_scoped_client_child_t*
+CefCToCppScoped<CefTranslatorTestScopedClientChildCToCpp,
+                CefTranslatorTestScopedClientChild,
+                cef_translator_test_scoped_client_child_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     CefOwnPtr<CefTranslatorTestScopedClientChild> c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+cef_translator_test_scoped_client_child_t*
+CefCToCppScoped<CefTranslatorTestScopedClientChildCToCpp,
+                CefTranslatorTestScopedClientChild,
+                cef_translator_test_scoped_client_child_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     CefRawPtr<CefTranslatorTestScopedClientChild> c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppScoped<CefTranslatorTestScopedClientChildCToCpp,
+                    CefTranslatorTestScopedClientChild,
+                    cef_translator_test_scoped_client_child_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.h
new file mode 100644
index 0000000..2f33b51
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=883e6a51cedcbe20292e6da6dd97709068e032e3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_scoped.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestScopedClientChildCToCpp
+    : public CefCToCppScoped<CefTranslatorTestScopedClientChildCToCpp,
+                             CefTranslatorTestScopedClientChild,
+                             cef_translator_test_scoped_client_child_t> {
+ public:
+  CefTranslatorTestScopedClientChildCToCpp();
+  virtual ~CefTranslatorTestScopedClientChildCToCpp();
+
+  // CefTranslatorTestScopedClientChild methods.
+  int GetOtherValue() override;
+
+  // CefTranslatorTestScopedClient methods.
+  int GetValue() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.cc
new file mode 100644
index 0000000..fb57aa7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5c7f3edee8fa09ae414ccc7cacfaaa9713cdbf2c$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_client_child_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") int CefTranslatorTestScopedClientCToCpp::GetValue() {
+  cef_translator_test_scoped_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientCToCpp::CefTranslatorTestScopedClientCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedClientCToCpp::~CefTranslatorTestScopedClientCToCpp() {}
+
+template <>
+cef_translator_test_scoped_client_t*
+CefCToCppScoped<CefTranslatorTestScopedClientCToCpp,
+                CefTranslatorTestScopedClient,
+                cef_translator_test_scoped_client_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     CefOwnPtr<CefTranslatorTestScopedClient> c) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_client_t*>(
+        CefTranslatorTestScopedClientChildCToCpp::UnwrapOwn(
+            CefOwnPtr<CefTranslatorTestScopedClientChild>(
+                reinterpret_cast<CefTranslatorTestScopedClientChild*>(
+                    c.release()))));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+cef_translator_test_scoped_client_t*
+CefCToCppScoped<CefTranslatorTestScopedClientCToCpp,
+                CefTranslatorTestScopedClient,
+                cef_translator_test_scoped_client_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     CefRawPtr<CefTranslatorTestScopedClient> c) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_client_t*>(
+        CefTranslatorTestScopedClientChildCToCpp::UnwrapRaw(
+            CefRawPtr<CefTranslatorTestScopedClientChild>(
+                reinterpret_cast<CefTranslatorTestScopedClientChild*>(
+                    CEF_RAW_PTR_GET(c)))));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppScoped<CefTranslatorTestScopedClientCToCpp,
+                    CefTranslatorTestScopedClient,
+                    cef_translator_test_scoped_client_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_CLIENT;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.h
new file mode 100644
index 0000000..c2dd242
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_client_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=38ae3f615e92571aab894b0a7e4844a2ee69f20b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_scoped.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefTranslatorTestScopedClientCToCpp
+    : public CefCToCppScoped<CefTranslatorTestScopedClientCToCpp,
+                             CefTranslatorTestScopedClient,
+                             cef_translator_test_scoped_client_t> {
+ public:
+  CefTranslatorTestScopedClientCToCpp();
+  virtual ~CefTranslatorTestScopedClientCToCpp();
+
+  // CefTranslatorTestScopedClient methods.
+  int GetValue() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_CLIENT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.cc
new file mode 100644
index 0000000..653ed7a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=fc692610bce3f501fc3cbcf316da081d29a03cd9$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>
+CefTranslatorTestScopedLibraryChildChild::Create(int value,
+                                                 int other_value,
+                                                 int other_other_value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_scoped_library_child_child_t* _retval =
+      cef_translator_test_scoped_library_child_child_create(value, other_value,
+                                                            other_other_value);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryChildChildCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestScopedLibraryChildChildCToCpp::GetOtherOtherValue() {
+  cef_translator_test_scoped_library_child_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_other_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestScopedLibraryChildChildCToCpp::SetOtherOtherValue(
+    int value) {
+  cef_translator_test_scoped_library_child_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_other_other_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_other_other_value(_struct, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestScopedLibraryChildChildCToCpp::GetOtherValue() {
+  cef_translator_test_scoped_library_child_t* _struct =
+      reinterpret_cast<cef_translator_test_scoped_library_child_t*>(
+          GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestScopedLibraryChildChildCToCpp::SetOtherValue(int value) {
+  cef_translator_test_scoped_library_child_t* _struct =
+      reinterpret_cast<cef_translator_test_scoped_library_child_t*>(
+          GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_other_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_other_value(_struct, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestScopedLibraryChildChildCToCpp::GetValue() {
+  cef_translator_test_scoped_library_t* _struct =
+      reinterpret_cast<cef_translator_test_scoped_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestScopedLibraryChildChildCToCpp::SetValue(int value) {
+  cef_translator_test_scoped_library_t* _struct =
+      reinterpret_cast<cef_translator_test_scoped_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_value(_struct, value);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildChildCToCpp::
+    CefTranslatorTestScopedLibraryChildChildCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildChildCToCpp::
+    ~CefTranslatorTestScopedLibraryChildChildCToCpp() {}
+
+template <>
+cef_translator_test_scoped_library_child_child_t*
+CefCToCppScoped<CefTranslatorTestScopedLibraryChildChildCToCpp,
+                CefTranslatorTestScopedLibraryChildChild,
+                cef_translator_test_scoped_library_child_child_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     CefOwnPtr<CefTranslatorTestScopedLibraryChildChild> c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+cef_translator_test_scoped_library_child_child_t*
+CefCToCppScoped<CefTranslatorTestScopedLibraryChildChildCToCpp,
+                CefTranslatorTestScopedLibraryChildChild,
+                cef_translator_test_scoped_library_child_child_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     CefRawPtr<CefTranslatorTestScopedLibraryChildChild> c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppScoped<
+    CefTranslatorTestScopedLibraryChildChildCToCpp,
+    CefTranslatorTestScopedLibraryChildChild,
+    cef_translator_test_scoped_library_child_child_t>::kWrapperType =
+    WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.h
new file mode 100644
index 0000000..232ad75
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1470375f5966c1b47a38265ce12172b5d05f3254$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_scoped.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestScopedLibraryChildChildCToCpp
+    : public CefCToCppScoped<CefTranslatorTestScopedLibraryChildChildCToCpp,
+                             CefTranslatorTestScopedLibraryChildChild,
+                             cef_translator_test_scoped_library_child_child_t> {
+ public:
+  CefTranslatorTestScopedLibraryChildChildCToCpp();
+  virtual ~CefTranslatorTestScopedLibraryChildChildCToCpp();
+
+  // CefTranslatorTestScopedLibraryChildChild methods.
+  int GetOtherOtherValue() OVERRIDE;
+  void SetOtherOtherValue(int value) OVERRIDE;
+
+  // CefTranslatorTestScopedLibraryChild methods.
+  int GetOtherValue() OVERRIDE;
+  void SetOtherValue(int value) OVERRIDE;
+
+  // CefTranslatorTestScopedLibrary methods.
+  int GetValue() OVERRIDE;
+  void SetValue(int value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.cc
new file mode 100644
index 0000000..3225b86
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e76720f37e225d0c2f30938ec59b1e73e61b9017$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedLibraryChild>
+CefTranslatorTestScopedLibraryChild::Create(int value, int other_value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_scoped_library_child_t* _retval =
+      cef_translator_test_scoped_library_child_create(value, other_value);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryChildCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestScopedLibraryChildCToCpp::GetOtherValue() {
+  cef_translator_test_scoped_library_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_other_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_other_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestScopedLibraryChildCToCpp::SetOtherValue(int value) {
+  cef_translator_test_scoped_library_child_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_other_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_other_value(_struct, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTranslatorTestScopedLibraryChildCToCpp::GetValue() {
+  cef_translator_test_scoped_library_t* _struct =
+      reinterpret_cast<cef_translator_test_scoped_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestScopedLibraryChildCToCpp::SetValue(int value) {
+  cef_translator_test_scoped_library_t* _struct =
+      reinterpret_cast<cef_translator_test_scoped_library_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_value(_struct, value);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildCToCpp::
+    CefTranslatorTestScopedLibraryChildCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryChildCToCpp::
+    ~CefTranslatorTestScopedLibraryChildCToCpp() {}
+
+template <>
+cef_translator_test_scoped_library_child_t*
+CefCToCppScoped<CefTranslatorTestScopedLibraryChildCToCpp,
+                CefTranslatorTestScopedLibraryChild,
+                cef_translator_test_scoped_library_child_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     CefOwnPtr<CefTranslatorTestScopedLibraryChild> c) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_library_child_t*>(
+        CefTranslatorTestScopedLibraryChildChildCToCpp::UnwrapOwn(
+            CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>(
+                reinterpret_cast<CefTranslatorTestScopedLibraryChildChild*>(
+                    c.release()))));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+cef_translator_test_scoped_library_child_t*
+CefCToCppScoped<CefTranslatorTestScopedLibraryChildCToCpp,
+                CefTranslatorTestScopedLibraryChild,
+                cef_translator_test_scoped_library_child_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     CefRawPtr<CefTranslatorTestScopedLibraryChild> c) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_library_child_t*>(
+        CefTranslatorTestScopedLibraryChildChildCToCpp::UnwrapRaw(
+            CefRawPtr<CefTranslatorTestScopedLibraryChildChild>(
+                reinterpret_cast<CefTranslatorTestScopedLibraryChildChild*>(
+                    CEF_RAW_PTR_GET(c)))));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppScoped<CefTranslatorTestScopedLibraryChildCToCpp,
+                    CefTranslatorTestScopedLibraryChild,
+                    cef_translator_test_scoped_library_child_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.h
new file mode 100644
index 0000000..a45890d
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0018f0e4d7a63bb8ab959a9f98268d696894dd7d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_scoped.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestScopedLibraryChildCToCpp
+    : public CefCToCppScoped<CefTranslatorTestScopedLibraryChildCToCpp,
+                             CefTranslatorTestScopedLibraryChild,
+                             cef_translator_test_scoped_library_child_t> {
+ public:
+  CefTranslatorTestScopedLibraryChildCToCpp();
+  virtual ~CefTranslatorTestScopedLibraryChildCToCpp();
+
+  // CefTranslatorTestScopedLibraryChild methods.
+  int GetOtherValue() OVERRIDE;
+  void SetOtherValue(int value) OVERRIDE;
+
+  // CefTranslatorTestScopedLibrary methods.
+  int GetValue() OVERRIDE;
+  void SetValue(int value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.cc b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.cc
new file mode 100644
index 0000000..5673197
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f7a516322c3b9b51a545f7b826dbfb509a7194e7$
+//
+
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_child_child_ctocpp.h"
+#include "libcef_dll/ctocpp/test/translator_test_scoped_library_child_ctocpp.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefOwnPtr<CefTranslatorTestScopedLibrary>
+CefTranslatorTestScopedLibrary::Create(int value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_translator_test_scoped_library_t* _retval =
+      cef_translator_test_scoped_library_create(value);
+
+  // Return type: ownptr_same
+  return CefTranslatorTestScopedLibraryCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") int CefTranslatorTestScopedLibraryCToCpp::GetValue() {
+  cef_translator_test_scoped_library_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTranslatorTestScopedLibraryCToCpp::SetValue(int value) {
+  cef_translator_test_scoped_library_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_value(_struct, value);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryCToCpp::CefTranslatorTestScopedLibraryCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTranslatorTestScopedLibraryCToCpp::~CefTranslatorTestScopedLibraryCToCpp() {}
+
+template <>
+cef_translator_test_scoped_library_t*
+CefCToCppScoped<CefTranslatorTestScopedLibraryCToCpp,
+                CefTranslatorTestScopedLibrary,
+                cef_translator_test_scoped_library_t>::
+    UnwrapDerivedOwn(CefWrapperType type,
+                     CefOwnPtr<CefTranslatorTestScopedLibrary> c) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_library_t*>(
+        CefTranslatorTestScopedLibraryChildCToCpp::UnwrapOwn(
+            CefOwnPtr<CefTranslatorTestScopedLibraryChild>(
+                reinterpret_cast<CefTranslatorTestScopedLibraryChild*>(
+                    c.release()))));
+  }
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_library_t*>(
+        CefTranslatorTestScopedLibraryChildChildCToCpp::UnwrapOwn(
+            CefOwnPtr<CefTranslatorTestScopedLibraryChildChild>(
+                reinterpret_cast<CefTranslatorTestScopedLibraryChildChild*>(
+                    c.release()))));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+cef_translator_test_scoped_library_t*
+CefCToCppScoped<CefTranslatorTestScopedLibraryCToCpp,
+                CefTranslatorTestScopedLibrary,
+                cef_translator_test_scoped_library_t>::
+    UnwrapDerivedRaw(CefWrapperType type,
+                     CefRawPtr<CefTranslatorTestScopedLibrary> c) {
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_library_t*>(
+        CefTranslatorTestScopedLibraryChildCToCpp::UnwrapRaw(
+            CefRawPtr<CefTranslatorTestScopedLibraryChild>(
+                reinterpret_cast<CefTranslatorTestScopedLibraryChild*>(
+                    CEF_RAW_PTR_GET(c)))));
+  }
+  if (type == WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD) {
+    return reinterpret_cast<cef_translator_test_scoped_library_t*>(
+        CefTranslatorTestScopedLibraryChildChildCToCpp::UnwrapRaw(
+            CefRawPtr<CefTranslatorTestScopedLibraryChildChild>(
+                reinterpret_cast<CefTranslatorTestScopedLibraryChildChild*>(
+                    CEF_RAW_PTR_GET(c)))));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppScoped<CefTranslatorTestScopedLibraryCToCpp,
+                    CefTranslatorTestScopedLibrary,
+                    cef_translator_test_scoped_library_t>::kWrapperType =
+        WT_TRANSLATOR_TEST_SCOPED_LIBRARY;
diff --git a/src/libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.h b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.h
new file mode 100644
index 0000000..11600e0
--- /dev/null
+++ b/src/libcef_dll/ctocpp/test/translator_test_scoped_library_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=35349ead1a95bca88b7076a79e1c8ad7be025d08$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/test/cef_translator_test.h"
+#include "libcef_dll/ctocpp/ctocpp_scoped.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTranslatorTestScopedLibraryCToCpp
+    : public CefCToCppScoped<CefTranslatorTestScopedLibraryCToCpp,
+                             CefTranslatorTestScopedLibrary,
+                             cef_translator_test_scoped_library_t> {
+ public:
+  CefTranslatorTestScopedLibraryCToCpp();
+  virtual ~CefTranslatorTestScopedLibraryCToCpp();
+
+  // CefTranslatorTestScopedLibrary methods.
+  int GetValue() OVERRIDE;
+  void SetValue(int value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_TEST_TRANSLATOR_TEST_SCOPED_LIBRARY_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/thread_ctocpp.cc b/src/libcef_dll/ctocpp/thread_ctocpp.cc
new file mode 100644
index 0000000..26cf529
--- /dev/null
+++ b/src/libcef_dll/ctocpp/thread_ctocpp.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=10047fa557a7b02daa67063a01f1f19c45d17120$
+//
+
+#include "libcef_dll/ctocpp/thread_ctocpp.h"
+#include "libcef_dll/ctocpp/task_runner_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefThread> CefThread::CreateThread(
+    const CefString& display_name,
+    cef_thread_priority_t priority,
+    cef_message_loop_type_t message_loop_type,
+    bool stoppable,
+    cef_com_init_mode_t com_init_mode) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: display_name
+
+  // Execute
+  cef_thread_t* _retval =
+      cef_thread_create(display_name.GetStruct(), priority, message_loop_type,
+                        stoppable, com_init_mode);
+
+  // Return type: refptr_same
+  return CefThreadCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTaskRunner> CefThreadCToCpp::GetTaskRunner() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_thread_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_task_runner))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_task_runner_t* _retval = _struct->get_task_runner(_struct);
+
+  // Return type: refptr_same
+  return CefTaskRunnerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_platform_thread_id_t CefThreadCToCpp::GetPlatformThreadId() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_thread_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_platform_thread_id))
+    return kInvalidPlatformThreadId;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_platform_thread_id_t _retval = _struct->get_platform_thread_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefThreadCToCpp::Stop() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_thread_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, stop))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->stop(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefThreadCToCpp::IsRunning() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_thread_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_running))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_running(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefThreadCToCpp::CefThreadCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefThreadCToCpp::~CefThreadCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_thread_t*
+CefCToCppRefCounted<CefThreadCToCpp, CefThread, cef_thread_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefThread* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefThreadCToCpp, CefThread, cef_thread_t>::
+    kWrapperType = WT_THREAD;
diff --git a/src/libcef_dll/ctocpp/thread_ctocpp.h b/src/libcef_dll/ctocpp/thread_ctocpp.h
new file mode 100644
index 0000000..880cc7e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/thread_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cfe482fc913cfa603b98d58e4579e5af667b0ce5$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_THREAD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_THREAD_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_thread_capi.h"
+#include "include/cef_thread.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefThreadCToCpp
+    : public CefCToCppRefCounted<CefThreadCToCpp, CefThread, cef_thread_t> {
+ public:
+  CefThreadCToCpp();
+  virtual ~CefThreadCToCpp();
+
+  // CefThread methods.
+  CefRefPtr<CefTaskRunner> GetTaskRunner() OVERRIDE;
+  cef_platform_thread_id_t GetPlatformThreadId() OVERRIDE;
+  void Stop() OVERRIDE;
+  bool IsRunning() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_THREAD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/urlrequest_client_ctocpp.cc b/src/libcef_dll/ctocpp/urlrequest_client_ctocpp.cc
new file mode 100644
index 0000000..fbc7676
--- /dev/null
+++ b/src/libcef_dll/ctocpp/urlrequest_client_ctocpp.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b08f8ba4d2ee2aaf4ee5f805d9cd6d0159a84969$
+//
+
+#include "libcef_dll/ctocpp/urlrequest_client_ctocpp.h"
+#include "libcef_dll/cpptoc/auth_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/urlrequest_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefURLRequestClientCToCpp::OnRequestComplete(
+    CefRefPtr<CefURLRequest> request) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_request_complete))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+
+  // Execute
+  _struct->on_request_complete(_struct, CefURLRequestCppToC::Wrap(request));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefURLRequestClientCToCpp::OnUploadProgress(
+    CefRefPtr<CefURLRequest> request,
+    int64 current,
+    int64 total) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_upload_progress))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+
+  // Execute
+  _struct->on_upload_progress(_struct, CefURLRequestCppToC::Wrap(request),
+                              current, total);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefURLRequestClientCToCpp::OnDownloadProgress(
+    CefRefPtr<CefURLRequest> request,
+    int64 current,
+    int64 total) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_download_progress))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+
+  // Execute
+  _struct->on_download_progress(_struct, CefURLRequestCppToC::Wrap(request),
+                                current, total);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefURLRequestClientCToCpp::OnDownloadData(CefRefPtr<CefURLRequest> request,
+                                               const void* data,
+                                               size_t data_length) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_download_data))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_diff
+  DCHECK(request.get());
+  if (!request.get())
+    return;
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return;
+
+  // Execute
+  _struct->on_download_data(_struct, CefURLRequestCppToC::Wrap(request), data,
+                            data_length);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefURLRequestClientCToCpp::GetAuthCredentials(
+    bool isProxy,
+    const CefString& host,
+    int port,
+    const CefString& realm,
+    const CefString& scheme,
+    CefRefPtr<CefAuthCallback> callback) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_client_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_auth_credentials))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: host; type: string_byref_const
+  DCHECK(!host.empty());
+  if (host.empty())
+    return false;
+  // Verify param: scheme; type: string_byref_const
+  DCHECK(!scheme.empty());
+  if (scheme.empty())
+    return false;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return false;
+  // Unverified params: realm
+
+  // Execute
+  int _retval = _struct->get_auth_credentials(
+      _struct, isProxy, host.GetStruct(), port, realm.GetStruct(),
+      scheme.GetStruct(), CefAuthCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefURLRequestClientCToCpp::CefURLRequestClientCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefURLRequestClientCToCpp::~CefURLRequestClientCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_urlrequest_client_t* CefCToCppRefCounted<
+    CefURLRequestClientCToCpp,
+    CefURLRequestClient,
+    cef_urlrequest_client_t>::UnwrapDerived(CefWrapperType type,
+                                            CefURLRequestClient* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefURLRequestClientCToCpp,
+                                   CefURLRequestClient,
+                                   cef_urlrequest_client_t>::kWrapperType =
+    WT_URLREQUEST_CLIENT;
diff --git a/src/libcef_dll/ctocpp/urlrequest_client_ctocpp.h b/src/libcef_dll/ctocpp/urlrequest_client_ctocpp.h
new file mode 100644
index 0000000..41f7902
--- /dev/null
+++ b/src/libcef_dll/ctocpp/urlrequest_client_ctocpp.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2a531ac29630c829e393105fcd54e12263e82193$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_URLREQUEST_CLIENT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_URLREQUEST_CLIENT_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_urlrequest_capi.h"
+#include "include/cef_urlrequest.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefURLRequestClientCToCpp
+    : public CefCToCppRefCounted<CefURLRequestClientCToCpp,
+                                 CefURLRequestClient,
+                                 cef_urlrequest_client_t> {
+ public:
+  CefURLRequestClientCToCpp();
+  virtual ~CefURLRequestClientCToCpp();
+
+  // CefURLRequestClient methods.
+  void OnRequestComplete(CefRefPtr<CefURLRequest> request) override;
+  void OnUploadProgress(CefRefPtr<CefURLRequest> request,
+                        int64 current,
+                        int64 total) override;
+  void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
+                          int64 current,
+                          int64 total) override;
+  void OnDownloadData(CefRefPtr<CefURLRequest> request,
+                      const void* data,
+                      size_t data_length) override;
+  bool GetAuthCredentials(bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_URLREQUEST_CLIENT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/urlrequest_ctocpp.cc b/src/libcef_dll/ctocpp/urlrequest_ctocpp.cc
new file mode 100644
index 0000000..d25fb82
--- /dev/null
+++ b/src/libcef_dll/ctocpp/urlrequest_ctocpp.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=59e8bd7a950727fdcd6234401df65ca60ea33ba2$
+//
+
+#include "libcef_dll/ctocpp/urlrequest_ctocpp.h"
+#include "libcef_dll/cpptoc/urlrequest_client_cpptoc.h"
+#include "libcef_dll/ctocpp/request_context_ctocpp.h"
+#include "libcef_dll/ctocpp/request_ctocpp.h"
+#include "libcef_dll/ctocpp/response_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefURLRequest> CefURLRequest::Create(
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefURLRequestClient> client,
+    CefRefPtr<CefRequestContext> request_context) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: request; type: refptr_same
+  DCHECK(request.get());
+  if (!request.get())
+    return nullptr;
+  // Verify param: client; type: refptr_diff
+  DCHECK(client.get());
+  if (!client.get())
+    return nullptr;
+  // Unverified params: request_context
+
+  // Execute
+  cef_urlrequest_t* _retval =
+      cef_urlrequest_create(CefRequestCToCpp::Unwrap(request),
+                            CefURLRequestClientCppToC::Wrap(client),
+                            CefRequestContextCToCpp::Unwrap(request_context));
+
+  // Return type: refptr_same
+  return CefURLRequestCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefRequest> CefURLRequestCToCpp::GetRequest() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_request))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_request_t* _retval = _struct->get_request(_struct);
+
+  // Return type: refptr_same
+  return CefRequestCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefURLRequestClient> CefURLRequestCToCpp::GetClient() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_client))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_urlrequest_client_t* _retval = _struct->get_client(_struct);
+
+  // Return type: refptr_diff
+  return CefURLRequestClientCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefURLRequest::Status CefURLRequestCToCpp::GetRequestStatus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_request_status))
+    return UR_UNKNOWN;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_urlrequest_status_t _retval = _struct->get_request_status(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefURLRequest::ErrorCode CefURLRequestCToCpp::GetRequestError() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_request_error))
+    return ERR_NONE;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_errorcode_t _retval = _struct->get_request_error(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefResponse> CefURLRequestCToCpp::GetResponse() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_response))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_response_t* _retval = _struct->get_response(_struct);
+
+  // Return type: refptr_same
+  return CefResponseCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefURLRequestCToCpp::ResponseWasCached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, response_was_cached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->response_was_cached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefURLRequestCToCpp::Cancel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_urlrequest_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel(_struct);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefURLRequestCToCpp::CefURLRequestCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefURLRequestCToCpp::~CefURLRequestCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_urlrequest_t*
+CefCToCppRefCounted<CefURLRequestCToCpp, CefURLRequest, cef_urlrequest_t>::
+    UnwrapDerived(CefWrapperType type, CefURLRequest* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefURLRequestCToCpp,
+                                   CefURLRequest,
+                                   cef_urlrequest_t>::kWrapperType =
+    WT_URLREQUEST;
diff --git a/src/libcef_dll/ctocpp/urlrequest_ctocpp.h b/src/libcef_dll/ctocpp/urlrequest_ctocpp.h
new file mode 100644
index 0000000..9f1a053
--- /dev/null
+++ b/src/libcef_dll/ctocpp/urlrequest_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3ab70d366cf2fe9f3c1bf296445a16225f18a20e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_URLREQUEST_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_URLREQUEST_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_urlrequest_capi.h"
+#include "include/cef_urlrequest.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefURLRequestCToCpp : public CefCToCppRefCounted<CefURLRequestCToCpp,
+                                                       CefURLRequest,
+                                                       cef_urlrequest_t> {
+ public:
+  CefURLRequestCToCpp();
+  virtual ~CefURLRequestCToCpp();
+
+  // CefURLRequest methods.
+  CefRefPtr<CefRequest> GetRequest() OVERRIDE;
+  CefRefPtr<CefURLRequestClient> GetClient() OVERRIDE;
+  Status GetRequestStatus() OVERRIDE;
+  ErrorCode GetRequestError() OVERRIDE;
+  CefRefPtr<CefResponse> GetResponse() OVERRIDE;
+  bool ResponseWasCached() OVERRIDE;
+  void Cancel() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_URLREQUEST_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8accessor_ctocpp.cc b/src/libcef_dll/ctocpp/v8accessor_ctocpp.cc
new file mode 100644
index 0000000..87a9f02
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8accessor_ctocpp.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=ba0cb1e58376451d6ab1a9bddf36629a79d10b95$
+//
+
+#include "libcef_dll/ctocpp/v8accessor_ctocpp.h"
+#include "libcef_dll/cpptoc/v8value_cpptoc.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefV8AccessorCToCpp::Get(const CefString& name,
+                              const CefRefPtr<CefV8Value> object,
+                              CefRefPtr<CefV8Value>& retval,
+                              CefString& exception) {
+  cef_v8accessor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object.get());
+  if (!object.get())
+    return false;
+
+  // Translate param: retval; type: refptr_diff_byref
+  cef_v8value_t* retvalStruct = NULL;
+  if (retval.get())
+    retvalStruct = CefV8ValueCppToC::Wrap(retval);
+  cef_v8value_t* retvalOrig = retvalStruct;
+
+  // Execute
+  int _retval =
+      _struct->get(_struct, name.GetStruct(), CefV8ValueCppToC::Wrap(object),
+                   &retvalStruct, exception.GetWritableStruct());
+
+  // Restore param:retval; type: refptr_diff_byref
+  if (retvalStruct) {
+    if (retvalStruct != retvalOrig) {
+      retval = CefV8ValueCppToC::Unwrap(retvalStruct);
+    }
+  } else {
+    retval = nullptr;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8AccessorCToCpp::Set(const CefString& name,
+                              const CefRefPtr<CefV8Value> object,
+                              const CefRefPtr<CefV8Value> value,
+                              CefString& exception) {
+  cef_v8accessor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object.get());
+  if (!object.get())
+    return false;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set(
+      _struct, name.GetStruct(), CefV8ValueCppToC::Wrap(object),
+      CefV8ValueCppToC::Wrap(value), exception.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8AccessorCToCpp::CefV8AccessorCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8AccessorCToCpp::~CefV8AccessorCToCpp() {}
+
+template <>
+cef_v8accessor_t*
+CefCToCppRefCounted<CefV8AccessorCToCpp, CefV8Accessor, cef_v8accessor_t>::
+    UnwrapDerived(CefWrapperType type, CefV8Accessor* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8AccessorCToCpp,
+                                   CefV8Accessor,
+                                   cef_v8accessor_t>::kWrapperType =
+    WT_V8ACCESSOR;
diff --git a/src/libcef_dll/ctocpp/v8accessor_ctocpp.h b/src/libcef_dll/ctocpp/v8accessor_ctocpp.h
new file mode 100644
index 0000000..b30175e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8accessor_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8e7a47e0cc8cea5d9aa39e243657107ae51b1b93$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8ACCESSOR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8ACCESSOR_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8AccessorCToCpp : public CefCToCppRefCounted<CefV8AccessorCToCpp,
+                                                       CefV8Accessor,
+                                                       cef_v8accessor_t> {
+ public:
+  CefV8AccessorCToCpp();
+  virtual ~CefV8AccessorCToCpp();
+
+  // CefV8Accessor methods.
+  bool Get(const CefString& name,
+           const CefRefPtr<CefV8Value> object,
+           CefRefPtr<CefV8Value>& retval,
+           CefString& exception) override;
+  bool Set(const CefString& name,
+           const CefRefPtr<CefV8Value> object,
+           const CefRefPtr<CefV8Value> value,
+           CefString& exception) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8ACCESSOR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.cc b/src/libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.cc
new file mode 100644
index 0000000..a37b7b3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a9e8a08fe2206b4dd9a9f0655b149465f81c14c5$
+//
+
+#include "libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefV8ArrayBufferReleaseCallbackCToCpp::ReleaseBuffer(void* buffer) {
+  cef_v8array_buffer_release_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, release_buffer))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return;
+
+  // Execute
+  _struct->release_buffer(_struct, buffer);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ArrayBufferReleaseCallbackCToCpp::CefV8ArrayBufferReleaseCallbackCToCpp() {
+}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ArrayBufferReleaseCallbackCToCpp::
+    ~CefV8ArrayBufferReleaseCallbackCToCpp() {}
+
+template <>
+cef_v8array_buffer_release_callback_t*
+CefCToCppRefCounted<CefV8ArrayBufferReleaseCallbackCToCpp,
+                    CefV8ArrayBufferReleaseCallback,
+                    cef_v8array_buffer_release_callback_t>::
+    UnwrapDerived(CefWrapperType type, CefV8ArrayBufferReleaseCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefV8ArrayBufferReleaseCallbackCToCpp,
+                        CefV8ArrayBufferReleaseCallback,
+                        cef_v8array_buffer_release_callback_t>::kWrapperType =
+        WT_V8ARRAY_BUFFER_RELEASE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.h b/src/libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.h
new file mode 100644
index 0000000..4c16715
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a8516449a68fa70c51ae471bf754d73879929d33$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8ARRAY_BUFFER_RELEASE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8ARRAY_BUFFER_RELEASE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8ArrayBufferReleaseCallbackCToCpp
+    : public CefCToCppRefCounted<CefV8ArrayBufferReleaseCallbackCToCpp,
+                                 CefV8ArrayBufferReleaseCallback,
+                                 cef_v8array_buffer_release_callback_t> {
+ public:
+  CefV8ArrayBufferReleaseCallbackCToCpp();
+  virtual ~CefV8ArrayBufferReleaseCallbackCToCpp();
+
+  // CefV8ArrayBufferReleaseCallback methods.
+  void ReleaseBuffer(void* buffer) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8ARRAY_BUFFER_RELEASE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8context_ctocpp.cc b/src/libcef_dll/ctocpp/v8context_ctocpp.cc
new file mode 100644
index 0000000..048387c
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8context_ctocpp.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=17bb7b970832cbd12edb0378b9529a22a7d0ee03$
+//
+
+#include "libcef_dll/ctocpp/v8context_ctocpp.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/task_runner_ctocpp.h"
+#include "libcef_dll/ctocpp/v8exception_ctocpp.h"
+#include "libcef_dll/ctocpp/v8value_ctocpp.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Context> CefV8Context::GetCurrentContext() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8context_t* _retval = cef_v8context_get_current_context();
+
+  // Return type: refptr_same
+  return CefV8ContextCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Context> CefV8Context::GetEnteredContext() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8context_t* _retval = cef_v8context_get_entered_context();
+
+  // Return type: refptr_same
+  return CefV8ContextCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8Context::InContext() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_v8context_in_context();
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTaskRunner> CefV8ContextCToCpp::GetTaskRunner() {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_task_runner))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_task_runner_t* _retval = _struct->get_task_runner(_struct);
+
+  // Return type: refptr_same
+  return CefTaskRunnerCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ContextCToCpp::IsValid() {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowser> CefV8ContextCToCpp::GetBrowser() {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_browser))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_t* _retval = _struct->get_browser(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefFrame> CefV8ContextCToCpp::GetFrame() {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_frame_t* _retval = _struct->get_frame(_struct);
+
+  // Return type: refptr_same
+  return CefFrameCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefV8Value> CefV8ContextCToCpp::GetGlobal() {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_global))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = _struct->get_global(_struct);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ContextCToCpp::Enter() {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, enter))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->enter(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ContextCToCpp::Exit() {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, exit))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->exit(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ContextCToCpp::IsSame(CefRefPtr<CefV8Context> that) {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefV8ContextCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ContextCToCpp::Eval(const CefString& code,
+                              const CefString& script_url,
+                              int start_line,
+                              CefRefPtr<CefV8Value>& retval,
+                              CefRefPtr<CefV8Exception>& exception) {
+  cef_v8context_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, eval))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: code; type: string_byref_const
+  DCHECK(!code.empty());
+  if (code.empty())
+    return false;
+  // Unverified params: script_url
+
+  // Translate param: retval; type: refptr_same_byref
+  cef_v8value_t* retvalStruct = NULL;
+  if (retval.get())
+    retvalStruct = CefV8ValueCToCpp::Unwrap(retval);
+  cef_v8value_t* retvalOrig = retvalStruct;
+  // Translate param: exception; type: refptr_same_byref
+  cef_v8exception_t* exceptionStruct = NULL;
+  if (exception.get())
+    exceptionStruct = CefV8ExceptionCToCpp::Unwrap(exception);
+  cef_v8exception_t* exceptionOrig = exceptionStruct;
+
+  // Execute
+  int _retval = _struct->eval(_struct, code.GetStruct(), script_url.GetStruct(),
+                              start_line, &retvalStruct, &exceptionStruct);
+
+  // Restore param:retval; type: refptr_same_byref
+  if (retvalStruct) {
+    if (retvalStruct != retvalOrig) {
+      retval = CefV8ValueCToCpp::Wrap(retvalStruct);
+    }
+  } else {
+    retval = nullptr;
+  }
+  // Restore param:exception; type: refptr_same_byref
+  if (exceptionStruct) {
+    if (exceptionStruct != exceptionOrig) {
+      exception = CefV8ExceptionCToCpp::Wrap(exceptionStruct);
+    }
+  } else {
+    exception = nullptr;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ContextCToCpp::CefV8ContextCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ContextCToCpp::~CefV8ContextCToCpp() {}
+
+template <>
+cef_v8context_t*
+CefCToCppRefCounted<CefV8ContextCToCpp, CefV8Context, cef_v8context_t>::
+    UnwrapDerived(CefWrapperType type, CefV8Context* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8ContextCToCpp,
+                                   CefV8Context,
+                                   cef_v8context_t>::kWrapperType =
+    WT_V8CONTEXT;
diff --git a/src/libcef_dll/ctocpp/v8context_ctocpp.h b/src/libcef_dll/ctocpp/v8context_ctocpp.h
new file mode 100644
index 0000000..79141fb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8context_ctocpp.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6beb66276c6d884570c8f8903fd67d39a9e6f986$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8CONTEXT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8CONTEXT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8ContextCToCpp : public CefCToCppRefCounted<CefV8ContextCToCpp,
+                                                      CefV8Context,
+                                                      cef_v8context_t> {
+ public:
+  CefV8ContextCToCpp();
+  virtual ~CefV8ContextCToCpp();
+
+  // CefV8Context methods.
+  CefRefPtr<CefTaskRunner> GetTaskRunner() OVERRIDE;
+  bool IsValid() OVERRIDE;
+  CefRefPtr<CefBrowser> GetBrowser() OVERRIDE;
+  CefRefPtr<CefFrame> GetFrame() OVERRIDE;
+  CefRefPtr<CefV8Value> GetGlobal() OVERRIDE;
+  bool Enter() OVERRIDE;
+  bool Exit() OVERRIDE;
+  bool IsSame(CefRefPtr<CefV8Context> that) OVERRIDE;
+  bool Eval(const CefString& code,
+            const CefString& script_url,
+            int start_line,
+            CefRefPtr<CefV8Value>& retval,
+            CefRefPtr<CefV8Exception>& exception) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8CONTEXT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8exception_ctocpp.cc b/src/libcef_dll/ctocpp/v8exception_ctocpp.cc
new file mode 100644
index 0000000..6f18a3c
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8exception_ctocpp.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0a09365e00f932df7d9beba64b1aa772b1a06998$
+//
+
+#include "libcef_dll/ctocpp/v8exception_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefString CefV8ExceptionCToCpp::GetMessage() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_message))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_message(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefV8ExceptionCToCpp::GetSourceLine() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_source_line))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_source_line(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefV8ExceptionCToCpp::GetScriptResourceName() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_script_resource_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_script_resource_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8ExceptionCToCpp::GetLineNumber() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_line_number))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_line_number(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8ExceptionCToCpp::GetStartPosition() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_start_position))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_start_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8ExceptionCToCpp::GetEndPosition() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_end_position))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_end_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8ExceptionCToCpp::GetStartColumn() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_start_column))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_start_column(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8ExceptionCToCpp::GetEndColumn() {
+  cef_v8exception_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_end_column))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_end_column(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ExceptionCToCpp::CefV8ExceptionCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ExceptionCToCpp::~CefV8ExceptionCToCpp() {}
+
+template <>
+cef_v8exception_t*
+CefCToCppRefCounted<CefV8ExceptionCToCpp, CefV8Exception, cef_v8exception_t>::
+    UnwrapDerived(CefWrapperType type, CefV8Exception* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8ExceptionCToCpp,
+                                   CefV8Exception,
+                                   cef_v8exception_t>::kWrapperType =
+    WT_V8EXCEPTION;
diff --git a/src/libcef_dll/ctocpp/v8exception_ctocpp.h b/src/libcef_dll/ctocpp/v8exception_ctocpp.h
new file mode 100644
index 0000000..4c79567
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8exception_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d5fbe63e64a74c8217af3d551f65a85ded5214fd$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8EXCEPTION_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8EXCEPTION_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8ExceptionCToCpp : public CefCToCppRefCounted<CefV8ExceptionCToCpp,
+                                                        CefV8Exception,
+                                                        cef_v8exception_t> {
+ public:
+  CefV8ExceptionCToCpp();
+  virtual ~CefV8ExceptionCToCpp();
+
+  // CefV8Exception methods.
+  CefString GetMessage() OVERRIDE;
+  CefString GetSourceLine() OVERRIDE;
+  CefString GetScriptResourceName() OVERRIDE;
+  int GetLineNumber() OVERRIDE;
+  int GetStartPosition() OVERRIDE;
+  int GetEndPosition() OVERRIDE;
+  int GetStartColumn() OVERRIDE;
+  int GetEndColumn() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8EXCEPTION_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8handler_ctocpp.cc b/src/libcef_dll/ctocpp/v8handler_ctocpp.cc
new file mode 100644
index 0000000..7566f77
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8handler_ctocpp.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=271072f761428ca4b308b0ee5418148f11126c27$
+//
+
+#include "libcef_dll/ctocpp/v8handler_ctocpp.h"
+#include "libcef_dll/cpptoc/v8value_cpptoc.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefV8HandlerCToCpp::Execute(const CefString& name,
+                                 CefRefPtr<CefV8Value> object,
+                                 const CefV8ValueList& arguments,
+                                 CefRefPtr<CefV8Value>& retval,
+                                 CefString& exception) {
+  cef_v8handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object.get());
+  if (!object.get())
+    return false;
+
+  // Translate param: arguments; type: refptr_vec_diff_byref_const
+  const size_t argumentsCount = arguments.size();
+  cef_v8value_t** argumentsList = NULL;
+  if (argumentsCount > 0) {
+    argumentsList = new cef_v8value_t*[argumentsCount];
+    DCHECK(argumentsList);
+    if (argumentsList) {
+      for (size_t i = 0; i < argumentsCount; ++i) {
+        argumentsList[i] = CefV8ValueCppToC::Wrap(arguments[i]);
+      }
+    }
+  }
+  // Translate param: retval; type: refptr_diff_byref
+  cef_v8value_t* retvalStruct = NULL;
+  if (retval.get())
+    retvalStruct = CefV8ValueCppToC::Wrap(retval);
+  cef_v8value_t* retvalOrig = retvalStruct;
+
+  // Execute
+  int _retval = _struct->execute(
+      _struct, name.GetStruct(), CefV8ValueCppToC::Wrap(object), argumentsCount,
+      argumentsList, &retvalStruct, exception.GetWritableStruct());
+
+  // Restore param:arguments; type: refptr_vec_diff_byref_const
+  if (argumentsList)
+    delete[] argumentsList;
+  // Restore param:retval; type: refptr_diff_byref
+  if (retvalStruct) {
+    if (retvalStruct != retvalOrig) {
+      retval = CefV8ValueCppToC::Unwrap(retvalStruct);
+    }
+  } else {
+    retval = nullptr;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8HandlerCToCpp::CefV8HandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8HandlerCToCpp::~CefV8HandlerCToCpp() {}
+
+template <>
+cef_v8handler_t*
+CefCToCppRefCounted<CefV8HandlerCToCpp, CefV8Handler, cef_v8handler_t>::
+    UnwrapDerived(CefWrapperType type, CefV8Handler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8HandlerCToCpp,
+                                   CefV8Handler,
+                                   cef_v8handler_t>::kWrapperType =
+    WT_V8HANDLER;
diff --git a/src/libcef_dll/ctocpp/v8handler_ctocpp.h b/src/libcef_dll/ctocpp/v8handler_ctocpp.h
new file mode 100644
index 0000000..fea2184
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8handler_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=49e06b49baadb1a40c4b5602d9b97c969614c31e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8HandlerCToCpp : public CefCToCppRefCounted<CefV8HandlerCToCpp,
+                                                      CefV8Handler,
+                                                      cef_v8handler_t> {
+ public:
+  CefV8HandlerCToCpp();
+  virtual ~CefV8HandlerCToCpp();
+
+  // CefV8Handler methods.
+  bool Execute(const CefString& name,
+               CefRefPtr<CefV8Value> object,
+               const CefV8ValueList& arguments,
+               CefRefPtr<CefV8Value>& retval,
+               CefString& exception) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8interceptor_ctocpp.cc b/src/libcef_dll/ctocpp/v8interceptor_ctocpp.cc
new file mode 100644
index 0000000..8d130cb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8interceptor_ctocpp.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=03d79fc410f4c1f268f34abf0e13483fe4c62172$
+//
+
+#include "libcef_dll/ctocpp/v8interceptor_ctocpp.h"
+#include "libcef_dll/cpptoc/v8value_cpptoc.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefV8InterceptorCToCpp::Get(const CefString& name,
+                                 const CefRefPtr<CefV8Value> object,
+                                 CefRefPtr<CefV8Value>& retval,
+                                 CefString& exception) {
+  cef_v8interceptor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_byname))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object.get());
+  if (!object.get())
+    return false;
+
+  // Translate param: retval; type: refptr_diff_byref
+  cef_v8value_t* retvalStruct = NULL;
+  if (retval.get())
+    retvalStruct = CefV8ValueCppToC::Wrap(retval);
+  cef_v8value_t* retvalOrig = retvalStruct;
+
+  // Execute
+  int _retval = _struct->get_byname(
+      _struct, name.GetStruct(), CefV8ValueCppToC::Wrap(object), &retvalStruct,
+      exception.GetWritableStruct());
+
+  // Restore param:retval; type: refptr_diff_byref
+  if (retvalStruct) {
+    if (retvalStruct != retvalOrig) {
+      retval = CefV8ValueCppToC::Unwrap(retvalStruct);
+    }
+  } else {
+    retval = nullptr;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8InterceptorCToCpp::Get(int index,
+                                 const CefRefPtr<CefV8Value> object,
+                                 CefRefPtr<CefV8Value>& retval,
+                                 CefString& exception) {
+  cef_v8interceptor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_byindex))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return false;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object.get());
+  if (!object.get())
+    return false;
+
+  // Translate param: retval; type: refptr_diff_byref
+  cef_v8value_t* retvalStruct = NULL;
+  if (retval.get())
+    retvalStruct = CefV8ValueCppToC::Wrap(retval);
+  cef_v8value_t* retvalOrig = retvalStruct;
+
+  // Execute
+  int _retval =
+      _struct->get_byindex(_struct, index, CefV8ValueCppToC::Wrap(object),
+                           &retvalStruct, exception.GetWritableStruct());
+
+  // Restore param:retval; type: refptr_diff_byref
+  if (retvalStruct) {
+    if (retvalStruct != retvalOrig) {
+      retval = CefV8ValueCppToC::Unwrap(retvalStruct);
+    }
+  } else {
+    retval = nullptr;
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8InterceptorCToCpp::Set(const CefString& name,
+                                 const CefRefPtr<CefV8Value> object,
+                                 const CefRefPtr<CefV8Value> value,
+                                 CefString& exception) {
+  cef_v8interceptor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_byname))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object.get());
+  if (!object.get())
+    return false;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_byname(
+      _struct, name.GetStruct(), CefV8ValueCppToC::Wrap(object),
+      CefV8ValueCppToC::Wrap(value), exception.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8InterceptorCToCpp::Set(int index,
+                                 const CefRefPtr<CefV8Value> object,
+                                 const CefRefPtr<CefV8Value> value,
+                                 CefString& exception) {
+  cef_v8interceptor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_byindex))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return false;
+  // Verify param: object; type: refptr_diff
+  DCHECK(object.get());
+  if (!object.get())
+    return false;
+  // Verify param: value; type: refptr_diff
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_byindex(
+      _struct, index, CefV8ValueCppToC::Wrap(object),
+      CefV8ValueCppToC::Wrap(value), exception.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8InterceptorCToCpp::CefV8InterceptorCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8InterceptorCToCpp::~CefV8InterceptorCToCpp() {}
+
+template <>
+cef_v8interceptor_t*
+CefCToCppRefCounted<CefV8InterceptorCToCpp,
+                    CefV8Interceptor,
+                    cef_v8interceptor_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefV8Interceptor* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8InterceptorCToCpp,
+                                   CefV8Interceptor,
+                                   cef_v8interceptor_t>::kWrapperType =
+    WT_V8INTERCEPTOR;
diff --git a/src/libcef_dll/ctocpp/v8interceptor_ctocpp.h b/src/libcef_dll/ctocpp/v8interceptor_ctocpp.h
new file mode 100644
index 0000000..992ea73
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8interceptor_ctocpp.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=78ea48543b579b38c382062d0912e4aac70e0603$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8INTERCEPTOR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8INTERCEPTOR_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefV8InterceptorCToCpp
+    : public CefCToCppRefCounted<CefV8InterceptorCToCpp,
+                                 CefV8Interceptor,
+                                 cef_v8interceptor_t> {
+ public:
+  CefV8InterceptorCToCpp();
+  virtual ~CefV8InterceptorCToCpp();
+
+  // CefV8Interceptor methods.
+  bool Get(const CefString& name,
+           const CefRefPtr<CefV8Value> object,
+           CefRefPtr<CefV8Value>& retval,
+           CefString& exception) override;
+  bool Get(int index,
+           const CefRefPtr<CefV8Value> object,
+           CefRefPtr<CefV8Value>& retval,
+           CefString& exception) override;
+  bool Set(const CefString& name,
+           const CefRefPtr<CefV8Value> object,
+           const CefRefPtr<CefV8Value> value,
+           CefString& exception) override;
+  bool Set(int index,
+           const CefRefPtr<CefV8Value> object,
+           const CefRefPtr<CefV8Value> value,
+           CefString& exception) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8INTERCEPTOR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8stack_frame_ctocpp.cc b/src/libcef_dll/ctocpp/v8stack_frame_ctocpp.cc
new file mode 100644
index 0000000..d1a0357
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8stack_frame_ctocpp.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=251396f9d749a62b14644a314fbeb5e31505420d$
+//
+
+#include "libcef_dll/ctocpp/v8stack_frame_ctocpp.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefV8StackFrameCToCpp::IsValid() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefV8StackFrameCToCpp::GetScriptName() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_script_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_script_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefV8StackFrameCToCpp::GetScriptNameOrSourceURL() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_script_name_or_source_url))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_script_name_or_source_url(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefV8StackFrameCToCpp::GetFunctionName() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_function_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_function_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8StackFrameCToCpp::GetLineNumber() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_line_number))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_line_number(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8StackFrameCToCpp::GetColumn() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_column))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_column(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8StackFrameCToCpp::IsEval() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_eval))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_eval(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8StackFrameCToCpp::IsConstructor() {
+  cef_v8stack_frame_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_constructor))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_constructor(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8StackFrameCToCpp::CefV8StackFrameCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8StackFrameCToCpp::~CefV8StackFrameCToCpp() {}
+
+template <>
+cef_v8stack_frame_t*
+CefCToCppRefCounted<CefV8StackFrameCToCpp,
+                    CefV8StackFrame,
+                    cef_v8stack_frame_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefV8StackFrame* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8StackFrameCToCpp,
+                                   CefV8StackFrame,
+                                   cef_v8stack_frame_t>::kWrapperType =
+    WT_V8STACK_FRAME;
diff --git a/src/libcef_dll/ctocpp/v8stack_frame_ctocpp.h b/src/libcef_dll/ctocpp/v8stack_frame_ctocpp.h
new file mode 100644
index 0000000..1956de5
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8stack_frame_ctocpp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a3706e4e68205837e899ac8ad5ee03094576d31d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8STACK_FRAME_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8STACK_FRAME_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8StackFrameCToCpp : public CefCToCppRefCounted<CefV8StackFrameCToCpp,
+                                                         CefV8StackFrame,
+                                                         cef_v8stack_frame_t> {
+ public:
+  CefV8StackFrameCToCpp();
+  virtual ~CefV8StackFrameCToCpp();
+
+  // CefV8StackFrame methods.
+  bool IsValid() OVERRIDE;
+  CefString GetScriptName() OVERRIDE;
+  CefString GetScriptNameOrSourceURL() OVERRIDE;
+  CefString GetFunctionName() OVERRIDE;
+  int GetLineNumber() OVERRIDE;
+  int GetColumn() OVERRIDE;
+  bool IsEval() OVERRIDE;
+  bool IsConstructor() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8STACK_FRAME_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8stack_trace_ctocpp.cc b/src/libcef_dll/ctocpp/v8stack_trace_ctocpp.cc
new file mode 100644
index 0000000..c400835
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8stack_trace_ctocpp.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e8c1ba613fd96ac7872f84e15bf560350f957ec4$
+//
+
+#include "libcef_dll/ctocpp/v8stack_trace_ctocpp.h"
+#include "libcef_dll/ctocpp/v8stack_frame_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8StackTrace> CefV8StackTrace::GetCurrent(int frame_limit) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8stack_trace_t* _retval = cef_v8stack_trace_get_current(frame_limit);
+
+  // Return type: refptr_same
+  return CefV8StackTraceCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefV8StackTraceCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_v8stack_trace_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8StackTraceCToCpp::GetFrameCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_v8stack_trace_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_frame_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8StackFrame> CefV8StackTraceCToCpp::GetFrame(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_v8stack_trace_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_frame))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8stack_frame_t* _retval = _struct->get_frame(_struct, index);
+
+  // Return type: refptr_same
+  return CefV8StackFrameCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8StackTraceCToCpp::CefV8StackTraceCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8StackTraceCToCpp::~CefV8StackTraceCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_v8stack_trace_t*
+CefCToCppRefCounted<CefV8StackTraceCToCpp,
+                    CefV8StackTrace,
+                    cef_v8stack_trace_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefV8StackTrace* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8StackTraceCToCpp,
+                                   CefV8StackTrace,
+                                   cef_v8stack_trace_t>::kWrapperType =
+    WT_V8STACK_TRACE;
diff --git a/src/libcef_dll/ctocpp/v8stack_trace_ctocpp.h b/src/libcef_dll/ctocpp/v8stack_trace_ctocpp.h
new file mode 100644
index 0000000..2670c51
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8stack_trace_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6f3af4b8ac1a836e8ce76a60e86ad39fa79dbbc0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8STACK_TRACE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8STACK_TRACE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8StackTraceCToCpp : public CefCToCppRefCounted<CefV8StackTraceCToCpp,
+                                                         CefV8StackTrace,
+                                                         cef_v8stack_trace_t> {
+ public:
+  CefV8StackTraceCToCpp();
+  virtual ~CefV8StackTraceCToCpp();
+
+  // CefV8StackTrace methods.
+  bool IsValid() OVERRIDE;
+  int GetFrameCount() OVERRIDE;
+  CefRefPtr<CefV8StackFrame> GetFrame(int index) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8STACK_TRACE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/v8value_ctocpp.cc b/src/libcef_dll/ctocpp/v8value_ctocpp.cc
new file mode 100644
index 0000000..b8dc379
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8value_ctocpp.cc
@@ -0,0 +1,1011 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=d50470b45303a24f4c2589ad8d629f3d273ac23f$
+//
+
+#include "libcef_dll/ctocpp/v8value_ctocpp.h"
+#include "libcef_dll/cpptoc/base_ref_counted_cpptoc.h"
+#include "libcef_dll/cpptoc/v8accessor_cpptoc.h"
+#include "libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/v8handler_cpptoc.h"
+#include "libcef_dll/cpptoc/v8interceptor_cpptoc.h"
+#include "libcef_dll/ctocpp/v8context_ctocpp.h"
+#include "libcef_dll/ctocpp/v8exception_ctocpp.h"
+#include "libcef_dll/transfer_util.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefV8Value> CefV8Value::CreateUndefined() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_undefined();
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefV8Value> CefV8Value::CreateNull() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_null();
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateBool(bool value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_bool(value);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateInt(int32 value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_int(value);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateUInt(uint32 value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_uint(value);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateDouble(double value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_double(value);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateDate(const CefTime& date) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_date(&date);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateString(const CefString& value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: value
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_string(value.GetStruct());
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateObject(
+    CefRefPtr<CefV8Accessor> accessor,
+    CefRefPtr<CefV8Interceptor> interceptor) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: accessor, interceptor
+
+  // Execute
+  cef_v8value_t* _retval =
+      cef_v8value_create_object(CefV8AccessorCppToC::Wrap(accessor),
+                                CefV8InterceptorCppToC::Wrap(interceptor));
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateArray(int length) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_array(length);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer(
+    void* buffer,
+    size_t length,
+    CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return nullptr;
+  // Verify param: release_callback; type: refptr_diff
+  DCHECK(release_callback.get());
+  if (!release_callback.get())
+    return nullptr;
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_array_buffer(
+      buffer, length,
+      CefV8ArrayBufferReleaseCallbackCppToC::Wrap(release_callback));
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8Value::CreateFunction(
+    const CefString& name,
+    CefRefPtr<CefV8Handler> handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return nullptr;
+  // Verify param: handler; type: refptr_diff
+  DCHECK(handler.get());
+  if (!handler.get())
+    return nullptr;
+
+  // Execute
+  cef_v8value_t* _retval = cef_v8value_create_function(
+      name.GetStruct(), CefV8HandlerCppToC::Wrap(handler));
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsValid() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsUndefined() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_undefined))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_undefined(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsNull() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_null))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_null(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsBool() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_bool(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsInt() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_int))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_int(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsUInt() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_uint))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_uint(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsDouble() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_double))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_double(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsDate() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_date))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_date(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsString() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_string))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_string(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsObject() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_object))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_object(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsArray() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_array))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_array(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsArrayBuffer() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_array_buffer))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_array_buffer(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsFunction() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_function))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_function(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::IsSame(CefRefPtr<CefV8Value> that) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefV8ValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::GetBoolValue() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bool_value))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_bool_value(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int32 CefV8ValueCToCpp::GetIntValue() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_int_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int32 _retval = _struct->get_int_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") uint32 CefV8ValueCToCpp::GetUIntValue() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_uint_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  uint32 _retval = _struct->get_uint_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") double CefV8ValueCToCpp::GetDoubleValue() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_double_value))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  double _retval = _struct->get_double_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefTime CefV8ValueCToCpp::GetDateValue() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_date_value))
+    return CefTime();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_time_t _retval = _struct->get_date_value(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefV8ValueCToCpp::GetStringValue() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string_value))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_string_value(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsUserCreated() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_user_created))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_user_created(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::HasException() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_exception))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_exception(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Exception> CefV8ValueCToCpp::GetException() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_exception))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8exception_t* _retval = _struct->get_exception(_struct);
+
+  // Return type: refptr_same
+  return CefV8ExceptionCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::ClearException() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear_exception))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->clear_exception(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::WillRethrowExceptions() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, will_rethrow_exceptions))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->will_rethrow_exceptions(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::SetRethrowExceptions(bool rethrow) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_rethrow_exceptions))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_rethrow_exceptions(_struct, rethrow);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::HasValue(const CefString& key) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_value_bykey))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: key
+
+  // Execute
+  int _retval = _struct->has_value_bykey(_struct, key.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::HasValue(int index) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_value_byindex))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return false;
+
+  // Execute
+  int _retval = _struct->has_value_byindex(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::DeleteValue(const CefString& key) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, delete_value_bykey))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: key
+
+  // Execute
+  int _retval = _struct->delete_value_bykey(_struct, key.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::DeleteValue(int index) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, delete_value_byindex))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return false;
+
+  // Execute
+  int _retval = _struct->delete_value_byindex(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8ValueCToCpp::GetValue(const CefString& key) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value_bykey))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: key
+
+  // Execute
+  cef_v8value_t* _retval = _struct->get_value_bykey(_struct, key.GetStruct());
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8ValueCToCpp::GetValue(int index) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value_byindex))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return nullptr;
+
+  // Execute
+  cef_v8value_t* _retval = _struct->get_value_byindex(_struct, index);
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::SetValue(const CefString& key,
+                                CefRefPtr<CefV8Value> value,
+                                PropertyAttribute attribute) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value_bykey))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+  // Unverified params: key
+
+  // Execute
+  int _retval = _struct->set_value_bykey(
+      _struct, key.GetStruct(), CefV8ValueCToCpp::Unwrap(value), attribute);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::SetValue(int index, CefRefPtr<CefV8Value> value) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value_byindex))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return false;
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_value_byindex(_struct, index,
+                                           CefV8ValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::SetValue(const CefString& key,
+                                AccessControl settings,
+                                PropertyAttribute attribute) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_value_byaccessor))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: key
+
+  // Execute
+  int _retval = _struct->set_value_byaccessor(_struct, key.GetStruct(),
+                                              settings, attribute);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::GetKeys(std::vector<CefString>& keys) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_keys))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: keys; type: string_vec_byref
+  cef_string_list_t keysList = cef_string_list_alloc();
+  DCHECK(keysList);
+  if (keysList)
+    transfer_string_list_contents(keys, keysList);
+
+  // Execute
+  int _retval = _struct->get_keys(_struct, keysList);
+
+  // Restore param:keys; type: string_vec_byref
+  if (keysList) {
+    keys.clear();
+    transfer_string_list_contents(keysList, keys);
+    cef_string_list_free(keysList);
+  }
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefV8ValueCToCpp::SetUserData(CefRefPtr<CefBaseRefCounted> user_data) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_user_data))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: user_data
+
+  // Execute
+  int _retval =
+      _struct->set_user_data(_struct, CefBaseRefCountedCppToC::Wrap(user_data));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBaseRefCounted> CefV8ValueCToCpp::GetUserData() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_user_data))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_base_ref_counted_t* _retval = _struct->get_user_data(_struct);
+
+  // Return type: refptr_diff
+  return CefBaseRefCountedCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefV8ValueCToCpp::GetExternallyAllocatedMemory() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_externally_allocated_memory))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_externally_allocated_memory(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefV8ValueCToCpp::AdjustExternallyAllocatedMemory(int change_in_bytes) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, adjust_externally_allocated_memory))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval =
+      _struct->adjust_externally_allocated_memory(_struct, change_in_bytes);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefV8ValueCToCpp::GetArrayLength() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_array_length))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_array_length(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8ArrayBufferReleaseCallback>
+CefV8ValueCToCpp::GetArrayBufferReleaseCallback() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_array_buffer_release_callback))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8array_buffer_release_callback_t* _retval =
+      _struct->get_array_buffer_release_callback(_struct);
+
+  // Return type: refptr_diff
+  return CefV8ArrayBufferReleaseCallbackCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::NeuterArrayBuffer() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, neuter_array_buffer))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->neuter_array_buffer(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefV8ValueCToCpp::GetFunctionName() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_function_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_function_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Handler> CefV8ValueCToCpp::GetFunctionHandler() {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_function_handler))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_v8handler_t* _retval = _struct->get_function_handler(_struct);
+
+  // Return type: refptr_diff
+  return CefV8HandlerCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8ValueCToCpp::ExecuteFunction(
+    CefRefPtr<CefV8Value> object,
+    const CefV8ValueList& arguments) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute_function))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: object
+
+  // Translate param: arguments; type: refptr_vec_same_byref_const
+  const size_t argumentsCount = arguments.size();
+  cef_v8value_t** argumentsList = NULL;
+  if (argumentsCount > 0) {
+    argumentsList = new cef_v8value_t*[argumentsCount];
+    DCHECK(argumentsList);
+    if (argumentsList) {
+      for (size_t i = 0; i < argumentsCount; ++i) {
+        argumentsList[i] = CefV8ValueCToCpp::Unwrap(arguments[i]);
+      }
+    }
+  }
+
+  // Execute
+  cef_v8value_t* _retval = _struct->execute_function(
+      _struct, CefV8ValueCToCpp::Unwrap(object), argumentsCount, argumentsList);
+
+  // Restore param:arguments; type: refptr_vec_same_byref_const
+  if (argumentsList)
+    delete[] argumentsList;
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefV8Value> CefV8ValueCToCpp::ExecuteFunctionWithContext(
+    CefRefPtr<CefV8Context> context,
+    CefRefPtr<CefV8Value> object,
+    const CefV8ValueList& arguments) {
+  cef_v8value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute_function_with_context))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: context; type: refptr_same
+  DCHECK(context.get());
+  if (!context.get())
+    return nullptr;
+  // Unverified params: object
+
+  // Translate param: arguments; type: refptr_vec_same_byref_const
+  const size_t argumentsCount = arguments.size();
+  cef_v8value_t** argumentsList = NULL;
+  if (argumentsCount > 0) {
+    argumentsList = new cef_v8value_t*[argumentsCount];
+    DCHECK(argumentsList);
+    if (argumentsList) {
+      for (size_t i = 0; i < argumentsCount; ++i) {
+        argumentsList[i] = CefV8ValueCToCpp::Unwrap(arguments[i]);
+      }
+    }
+  }
+
+  // Execute
+  cef_v8value_t* _retval = _struct->execute_function_with_context(
+      _struct, CefV8ContextCToCpp::Unwrap(context),
+      CefV8ValueCToCpp::Unwrap(object), argumentsCount, argumentsList);
+
+  // Restore param:arguments; type: refptr_vec_same_byref_const
+  if (argumentsList)
+    delete[] argumentsList;
+
+  // Return type: refptr_same
+  return CefV8ValueCToCpp::Wrap(_retval);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefV8ValueCToCpp::CefV8ValueCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefV8ValueCToCpp::~CefV8ValueCToCpp() {}
+
+template <>
+cef_v8value_t*
+CefCToCppRefCounted<CefV8ValueCToCpp, CefV8Value, cef_v8value_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefV8Value* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefV8ValueCToCpp,
+                                   CefV8Value,
+                                   cef_v8value_t>::kWrapperType = WT_V8VALUE;
diff --git a/src/libcef_dll/ctocpp/v8value_ctocpp.h b/src/libcef_dll/ctocpp/v8value_ctocpp.h
new file mode 100644
index 0000000..7ad13d7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/v8value_ctocpp.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1173914ff14a10ab522cdaa4eb99a2774fabfb21$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_V8VALUE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_V8VALUE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_v8_capi.h"
+#include "include/cef_v8.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefV8ValueCToCpp
+    : public CefCToCppRefCounted<CefV8ValueCToCpp, CefV8Value, cef_v8value_t> {
+ public:
+  CefV8ValueCToCpp();
+  virtual ~CefV8ValueCToCpp();
+
+  // CefV8Value methods.
+  bool IsValid() OVERRIDE;
+  bool IsUndefined() OVERRIDE;
+  bool IsNull() OVERRIDE;
+  bool IsBool() OVERRIDE;
+  bool IsInt() OVERRIDE;
+  bool IsUInt() OVERRIDE;
+  bool IsDouble() OVERRIDE;
+  bool IsDate() OVERRIDE;
+  bool IsString() OVERRIDE;
+  bool IsObject() OVERRIDE;
+  bool IsArray() OVERRIDE;
+  bool IsArrayBuffer() OVERRIDE;
+  bool IsFunction() OVERRIDE;
+  bool IsSame(CefRefPtr<CefV8Value> that) OVERRIDE;
+  bool GetBoolValue() OVERRIDE;
+  int32 GetIntValue() OVERRIDE;
+  uint32 GetUIntValue() OVERRIDE;
+  double GetDoubleValue() OVERRIDE;
+  CefTime GetDateValue() OVERRIDE;
+  CefString GetStringValue() OVERRIDE;
+  bool IsUserCreated() OVERRIDE;
+  bool HasException() OVERRIDE;
+  CefRefPtr<CefV8Exception> GetException() OVERRIDE;
+  bool ClearException() OVERRIDE;
+  bool WillRethrowExceptions() OVERRIDE;
+  bool SetRethrowExceptions(bool rethrow) OVERRIDE;
+  bool HasValue(const CefString& key) OVERRIDE;
+  bool HasValue(int index) OVERRIDE;
+  bool DeleteValue(const CefString& key) OVERRIDE;
+  bool DeleteValue(int index) OVERRIDE;
+  CefRefPtr<CefV8Value> GetValue(const CefString& key) OVERRIDE;
+  CefRefPtr<CefV8Value> GetValue(int index) OVERRIDE;
+  bool SetValue(const CefString& key,
+                CefRefPtr<CefV8Value> value,
+                PropertyAttribute attribute) OVERRIDE;
+  bool SetValue(int index, CefRefPtr<CefV8Value> value) OVERRIDE;
+  bool SetValue(const CefString& key,
+                AccessControl settings,
+                PropertyAttribute attribute) OVERRIDE;
+  bool GetKeys(std::vector<CefString>& keys) OVERRIDE;
+  bool SetUserData(CefRefPtr<CefBaseRefCounted> user_data) OVERRIDE;
+  CefRefPtr<CefBaseRefCounted> GetUserData() OVERRIDE;
+  int GetExternallyAllocatedMemory() OVERRIDE;
+  int AdjustExternallyAllocatedMemory(int change_in_bytes) OVERRIDE;
+  int GetArrayLength() OVERRIDE;
+  CefRefPtr<CefV8ArrayBufferReleaseCallback> GetArrayBufferReleaseCallback()
+      OVERRIDE;
+  bool NeuterArrayBuffer() OVERRIDE;
+  CefString GetFunctionName() OVERRIDE;
+  CefRefPtr<CefV8Handler> GetFunctionHandler() OVERRIDE;
+  CefRefPtr<CefV8Value> ExecuteFunction(
+      CefRefPtr<CefV8Value> object,
+      const CefV8ValueList& arguments) OVERRIDE;
+  CefRefPtr<CefV8Value> ExecuteFunctionWithContext(
+      CefRefPtr<CefV8Context> context,
+      CefRefPtr<CefV8Value> object,
+      const CefV8ValueList& arguments) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_V8VALUE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/value_ctocpp.cc b/src/libcef_dll/ctocpp/value_ctocpp.cc
new file mode 100644
index 0000000..b03a989
--- /dev/null
+++ b/src/libcef_dll/ctocpp/value_ctocpp.cc
@@ -0,0 +1,448 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7711f5c2b432b11bf4ab1d194c09a1cb7a8fcd05$
+//
+
+#include "libcef_dll/ctocpp/value_ctocpp.h"
+#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/list_value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefValue> CefValue::Create() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_value_t* _retval = cef_value_create();
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::IsOwned() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_owned))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_owned(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::IsReadOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::IsSame(CefRefPtr<CefValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefValueCToCpp::IsEqual(CefRefPtr<CefValue> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_equal))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_equal(_struct, CefValueCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefValue> CefValueCToCpp::Copy() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, copy))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_value_t* _retval = _struct->copy(_struct);
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefValueType CefValueCToCpp::GetType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return VTYPE_INVALID;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_value_type_t _retval = _struct->get_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::GetBool() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_bool(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefValueCToCpp::GetInt() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_int))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_int(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") double CefValueCToCpp::GetDouble() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_double))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  double _retval = _struct->get_double(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefValueCToCpp::GetString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefBinaryValue> CefValueCToCpp::GetBinary() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_binary))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_binary(_struct);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDictionaryValue> CefValueCToCpp::GetDictionary() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_dictionary))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_dictionary_value_t* _retval = _struct->get_dictionary(_struct);
+
+  // Return type: refptr_same
+  return CefDictionaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefListValue> CefValueCToCpp::GetList() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_list))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_list_value_t* _retval = _struct->get_list(_struct);
+
+  // Return type: refptr_same
+  return CefListValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::SetNull() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_null))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_null(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::SetBool(bool value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_bool))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_bool(_struct, value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::SetInt(int value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_int))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_int(_struct, value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefValueCToCpp::SetDouble(double value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_double))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->set_double(_struct, value);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefValueCToCpp::SetString(const CefString& value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_string))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: value
+
+  // Execute
+  int _retval = _struct->set_string(_struct, value.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefValueCToCpp::SetBinary(CefRefPtr<CefBinaryValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_binary))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->set_binary(_struct, CefBinaryValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefValueCToCpp::SetDictionary(CefRefPtr<CefDictionaryValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_dictionary))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->set_dictionary(_struct, CefDictionaryValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefValueCToCpp::SetList(CefRefPtr<CefListValue> value) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_value_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_list))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: value; type: refptr_same
+  DCHECK(value.get());
+  if (!value.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->set_list(_struct, CefListValueCToCpp::Unwrap(value));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefValueCToCpp::CefValueCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefValueCToCpp::~CefValueCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_value_t*
+CefCToCppRefCounted<CefValueCToCpp, CefValue, cef_value_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefValue* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefValueCToCpp, CefValue, cef_value_t>::kWrapperType =
+        WT_VALUE;
diff --git a/src/libcef_dll/ctocpp/value_ctocpp.h b/src/libcef_dll/ctocpp/value_ctocpp.h
new file mode 100644
index 0000000..6c757ef
--- /dev/null
+++ b/src/libcef_dll/ctocpp/value_ctocpp.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=f72dd0d0687b5b7626b651a298a5ed017686e649$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VALUE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VALUE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_values_capi.h"
+#include "include/cef_values.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefValueCToCpp
+    : public CefCToCppRefCounted<CefValueCToCpp, CefValue, cef_value_t> {
+ public:
+  CefValueCToCpp();
+  virtual ~CefValueCToCpp();
+
+  // CefValue methods.
+  bool IsValid() OVERRIDE;
+  bool IsOwned() OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  bool IsSame(CefRefPtr<CefValue> that) OVERRIDE;
+  bool IsEqual(CefRefPtr<CefValue> that) OVERRIDE;
+  CefRefPtr<CefValue> Copy() OVERRIDE;
+  CefValueType GetType() OVERRIDE;
+  bool GetBool() OVERRIDE;
+  int GetInt() OVERRIDE;
+  double GetDouble() OVERRIDE;
+  CefString GetString() OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetBinary() OVERRIDE;
+  CefRefPtr<CefDictionaryValue> GetDictionary() OVERRIDE;
+  CefRefPtr<CefListValue> GetList() OVERRIDE;
+  bool SetNull() OVERRIDE;
+  bool SetBool(bool value) OVERRIDE;
+  bool SetInt(int value) OVERRIDE;
+  bool SetDouble(double value) OVERRIDE;
+  bool SetString(const CefString& value) OVERRIDE;
+  bool SetBinary(CefRefPtr<CefBinaryValue> value) OVERRIDE;
+  bool SetDictionary(CefRefPtr<CefDictionaryValue> value) OVERRIDE;
+  bool SetList(CefRefPtr<CefListValue> value) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VALUE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/box_layout_ctocpp.cc b/src/libcef_dll/ctocpp/views/box_layout_ctocpp.cc
new file mode 100644
index 0000000..9e046c9
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/box_layout_ctocpp.cc
@@ -0,0 +1,132 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0345e3e77f8c55057284809bbfa45747ae75e777$
+//
+
+#include "libcef_dll/ctocpp/views/box_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/fill_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefBoxLayoutCToCpp::SetFlexForView(CefRefPtr<CefView> view, int flex) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_box_layout_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_flex_for_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->set_flex_for_view(_struct, CefViewCToCpp::Unwrap(view), flex);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBoxLayoutCToCpp::ClearFlexForView(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_box_layout_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear_flex_for_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->clear_flex_for_view(_struct, CefViewCToCpp::Unwrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBoxLayout> CefBoxLayoutCToCpp::AsBoxLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = reinterpret_cast<cef_layout_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_box_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_box_layout_t* _retval = _struct->as_box_layout(_struct);
+
+  // Return type: refptr_same
+  return CefBoxLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFillLayout> CefBoxLayoutCToCpp::AsFillLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = reinterpret_cast<cef_layout_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_fill_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_fill_layout_t* _retval = _struct->as_fill_layout(_struct);
+
+  // Return type: refptr_same
+  return CefFillLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBoxLayoutCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = reinterpret_cast<cef_layout_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBoxLayoutCToCpp::CefBoxLayoutCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBoxLayoutCToCpp::~CefBoxLayoutCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_box_layout_t*
+CefCToCppRefCounted<CefBoxLayoutCToCpp, CefBoxLayout, cef_box_layout_t>::
+    UnwrapDerived(CefWrapperType type, CefBoxLayout* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefBoxLayoutCToCpp,
+                                   CefBoxLayout,
+                                   cef_box_layout_t>::kWrapperType =
+    WT_BOX_LAYOUT;
diff --git a/src/libcef_dll/ctocpp/views/box_layout_ctocpp.h b/src/libcef_dll/ctocpp/views/box_layout_ctocpp.h
new file mode 100644
index 0000000..a3f0875
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/box_layout_ctocpp.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=62ff39b6f227b2d35a4324a02c5ece9128a14c7c$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_BOX_LAYOUT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_BOX_LAYOUT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_box_layout_capi.h"
+#include "include/capi/views/cef_view_capi.h"
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_view.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBoxLayoutCToCpp : public CefCToCppRefCounted<CefBoxLayoutCToCpp,
+                                                      CefBoxLayout,
+                                                      cef_box_layout_t> {
+ public:
+  CefBoxLayoutCToCpp();
+  virtual ~CefBoxLayoutCToCpp();
+
+  // CefBoxLayout methods.
+  void SetFlexForView(CefRefPtr<CefView> view, int flex) OVERRIDE;
+  void ClearFlexForView(CefRefPtr<CefView> view) OVERRIDE;
+
+  // CefLayout methods.
+  CefRefPtr<CefBoxLayout> AsBoxLayout() OVERRIDE;
+  CefRefPtr<CefFillLayout> AsFillLayout() OVERRIDE;
+  bool IsValid() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_BOX_LAYOUT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/browser_view_ctocpp.cc b/src/libcef_dll/ctocpp/views/browser_view_ctocpp.cc
new file mode 100644
index 0000000..7ee8785
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/browser_view_ctocpp.cc
@@ -0,0 +1,909 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=cefedca66680117ea721d46e4fbbaa47fac607aa$
+//
+
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/cpptoc/client_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/browser_ctocpp.h"
+#include "libcef_dll/ctocpp/dictionary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/request_context_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefBrowserView::CreateBrowserView(
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context,
+    CefRefPtr<CefBrowserViewDelegate> delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: client, url, extra_info, request_context, delegate
+
+  // Execute
+  cef_browser_view_t* _retval = cef_browser_view_create(
+      CefClientCppToC::Wrap(client), url.GetStruct(), &settings,
+      CefDictionaryValueCToCpp::Unwrap(extra_info),
+      CefRequestContextCToCpp::Unwrap(request_context),
+      CefBrowserViewDelegateCppToC::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefBrowserView::GetForBrowser(
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser; type: refptr_same
+  DCHECK(browser.get());
+  if (!browser.get())
+    return nullptr;
+
+  // Execute
+  cef_browser_view_t* _retval =
+      cef_browser_view_get_for_browser(CefBrowserCToCpp::Unwrap(browser));
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowser> CefBrowserViewCToCpp::GetBrowser() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_browser))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_t* _retval = _struct->get_browser(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewCToCpp::SetPreferAccelerators(bool prefer_accelerators) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_prefer_accelerators))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_prefer_accelerators(_struct, prefer_accelerators);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefBrowserViewCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefBrowserViewCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefBrowserViewCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefBrowserViewCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefBrowserViewCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefBrowserViewCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefBrowserViewCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserViewCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserViewCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefBrowserViewCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefWindow> CefBrowserViewCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefBrowserViewCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserViewCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefBrowserViewCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserViewCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefBrowserViewCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefBrowserViewCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefBrowserViewCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefBrowserViewCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefBrowserViewCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefBrowserViewCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefBrowserViewCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserViewCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefBrowserViewCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefBrowserViewCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefBrowserViewCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserViewCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserViewCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserViewCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserViewCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserViewCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserViewCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserViewCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefBrowserViewCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefBrowserViewCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_color_t CefBrowserViewCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                              CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                                CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserViewCToCpp::CefBrowserViewCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserViewCToCpp::~CefBrowserViewCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_browser_view_t*
+CefCToCppRefCounted<CefBrowserViewCToCpp, CefBrowserView, cef_browser_view_t>::
+    UnwrapDerived(CefWrapperType type, CefBrowserView* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefBrowserViewCToCpp,
+                                   CefBrowserView,
+                                   cef_browser_view_t>::kWrapperType =
+    WT_BROWSER_VIEW;
diff --git a/src/libcef_dll/ctocpp/views/browser_view_ctocpp.h b/src/libcef_dll/ctocpp/views/browser_view_ctocpp.h
new file mode 100644
index 0000000..3c45bba
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/browser_view_ctocpp.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9a09044f3f15be6f0c98a57d1d10839e89daab23$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_BROWSER_VIEW_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_BROWSER_VIEW_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_browser_view_capi.h"
+#include "include/views/cef_browser_view.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefBrowserViewCToCpp : public CefCToCppRefCounted<CefBrowserViewCToCpp,
+                                                        CefBrowserView,
+                                                        cef_browser_view_t> {
+ public:
+  CefBrowserViewCToCpp();
+  virtual ~CefBrowserViewCToCpp();
+
+  // CefBrowserView methods.
+  CefRefPtr<CefBrowser> GetBrowser() OVERRIDE;
+  void SetPreferAccelerators(bool prefer_accelerators) OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_BROWSER_VIEW_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.cc
new file mode 100644
index 0000000..52a43d1
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.cc
@@ -0,0 +1,360 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=31420c63af54d0862f106caad051e85ed7a6772d$
+//
+
+#include "libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/browser_cpptoc.h"
+#include "libcef_dll/cpptoc/views/browser_view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/ctocpp/client_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewDelegateCToCpp::OnBrowserCreated(
+    CefRefPtr<CefBrowserView> browser_view,
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_browser_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view.get());
+  if (!browser_view.get())
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_browser_created(_struct, CefBrowserViewCppToC::Wrap(browser_view),
+                              CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewDelegateCToCpp::OnBrowserDestroyed(
+    CefRefPtr<CefBrowserView> browser_view,
+    CefRefPtr<CefBrowser> browser) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_browser_destroyed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view.get());
+  if (!browser_view.get())
+    return;
+  // Verify param: browser; type: refptr_diff
+  DCHECK(browser.get());
+  if (!browser.get())
+    return;
+
+  // Execute
+  _struct->on_browser_destroyed(_struct,
+                                CefBrowserViewCppToC::Wrap(browser_view),
+                                CefBrowserCppToC::Wrap(browser));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserViewDelegate>
+CefBrowserViewDelegateCToCpp::GetDelegateForPopupBrowserView(
+    CefRefPtr<CefBrowserView> browser_view,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefClient> client,
+    bool is_devtools) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_delegate_for_popup_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view.get());
+  if (!browser_view.get())
+    return nullptr;
+  // Verify param: client; type: refptr_same
+  DCHECK(client.get());
+  if (!client.get())
+    return nullptr;
+
+  // Execute
+  cef_browser_view_delegate_t* _retval =
+      _struct->get_delegate_for_popup_browser_view(
+          _struct, CefBrowserViewCppToC::Wrap(browser_view), &settings,
+          CefClientCToCpp::Unwrap(client), is_devtools);
+
+  // Return type: refptr_same
+  return CefBrowserViewDelegateCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefBrowserViewDelegateCToCpp::OnPopupBrowserViewCreated(
+    CefRefPtr<CefBrowserView> browser_view,
+    CefRefPtr<CefBrowserView> popup_browser_view,
+    bool is_devtools) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_browser_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_popup_browser_view_created))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: browser_view; type: refptr_diff
+  DCHECK(browser_view.get());
+  if (!browser_view.get())
+    return false;
+  // Verify param: popup_browser_view; type: refptr_diff
+  DCHECK(popup_browser_view.get());
+  if (!popup_browser_view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_popup_browser_view_created(
+      _struct, CefBrowserViewCppToC::Wrap(browser_view),
+      CefBrowserViewCppToC::Wrap(popup_browser_view), is_devtools);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefBrowserViewDelegateCToCpp::GetPreferredSize(
+    CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_preferred_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefBrowserViewDelegateCToCpp::GetMinimumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_minimum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefBrowserViewDelegateCToCpp::GetMaximumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_maximum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefBrowserViewDelegateCToCpp::GetHeightForWidth(CefRefPtr<CefView> view,
+                                                    int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return 0;
+
+  // Execute
+  int _retval =
+      _struct->get_height_for_width(_struct, CefViewCppToC::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewDelegateCToCpp::OnParentViewChanged(
+    CefRefPtr<CefView> view,
+    bool added,
+    CefRefPtr<CefView> parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_parent_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent.get());
+  if (!parent.get())
+    return;
+
+  // Execute
+  _struct->on_parent_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                  CefViewCppToC::Wrap(parent));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewDelegateCToCpp::OnChildViewChanged(
+    CefRefPtr<CefView> view,
+    bool added,
+    CefRefPtr<CefView> child) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_child_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child.get());
+  if (!child.get())
+    return;
+
+  // Execute
+  _struct->on_child_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                 CefViewCppToC::Wrap(child));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewDelegateCToCpp::OnFocus(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_focus(_struct, CefViewCppToC::Wrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefBrowserViewDelegateCToCpp::OnBlur(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_blur))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_blur(_struct, CefViewCppToC::Wrap(view));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefBrowserViewDelegateCToCpp::CefBrowserViewDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefBrowserViewDelegateCToCpp::~CefBrowserViewDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_browser_view_delegate_t* CefCToCppRefCounted<
+    CefBrowserViewDelegateCToCpp,
+    CefBrowserViewDelegate,
+    cef_browser_view_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                                CefBrowserViewDelegate* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefBrowserViewDelegateCToCpp,
+                                   CefBrowserViewDelegate,
+                                   cef_browser_view_delegate_t>::kWrapperType =
+    WT_BROWSER_VIEW_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h b/src/libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h
new file mode 100644
index 0000000..b2a1831
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=085e93a68068064e7d6d96b6f12123434c4ee37a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_BROWSER_VIEW_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_BROWSER_VIEW_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/views/cef_browser_view_capi.h"
+#include "include/capi/views/cef_browser_view_delegate_capi.h"
+#include "include/cef_browser.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_browser_view_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefBrowserViewDelegateCToCpp
+    : public CefCToCppRefCounted<CefBrowserViewDelegateCToCpp,
+                                 CefBrowserViewDelegate,
+                                 cef_browser_view_delegate_t> {
+ public:
+  CefBrowserViewDelegateCToCpp();
+  virtual ~CefBrowserViewDelegateCToCpp();
+
+  // CefBrowserViewDelegate methods.
+  void OnBrowserCreated(CefRefPtr<CefBrowserView> browser_view,
+                        CefRefPtr<CefBrowser> browser) override;
+  void OnBrowserDestroyed(CefRefPtr<CefBrowserView> browser_view,
+                          CefRefPtr<CefBrowser> browser) override;
+  CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
+      CefRefPtr<CefBrowserView> browser_view,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefClient> client,
+      bool is_devtools) override;
+  bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
+                                 CefRefPtr<CefBrowserView> popup_browser_view,
+                                 bool is_devtools) override;
+
+  // CefViewDelegate methods.
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override;
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override;
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override;
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override;
+  void OnFocus(CefRefPtr<CefView> view) override;
+  void OnBlur(CefRefPtr<CefView> view) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_BROWSER_VIEW_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/button_ctocpp.cc b/src/libcef_dll/ctocpp/views/button_ctocpp.cc
new file mode 100644
index 0000000..506a94a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/button_ctocpp.cc
@@ -0,0 +1,926 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3fb1a282c85f146a38d8694f5f7bf1914fe16d38$
+//
+
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/label_button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/menu_button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLabelButton> CefButtonCToCpp::AsLabelButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_label_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_label_button_t* _retval = _struct->as_label_button(_struct);
+
+  // Return type: refptr_same
+  return CefLabelButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonCToCpp::SetState(cef_button_state_t state) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_state))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_state(_struct, state);
+}
+
+NO_SANITIZE("cfi-icall") cef_button_state_t CefButtonCToCpp::GetState() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_state))
+    return CEF_BUTTON_STATE_NORMAL;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_state_t _retval = _struct->get_state(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SetInkDropEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_ink_drop_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_ink_drop_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonCToCpp::SetTooltipText(const CefString& tooltip_text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_tooltip_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: tooltip_text; type: string_byref_const
+  DCHECK(!tooltip_text.empty());
+  if (tooltip_text.empty())
+    return;
+
+  // Execute
+  _struct->set_tooltip_text(_struct, tooltip_text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonCToCpp::SetAccessibleName(const CefString& name) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_accessible_name))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+
+  // Execute
+  _struct->set_accessible_name(_struct, name.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefButtonCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefButtonCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefButtonCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefButtonCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefButtonCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefButtonCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefButtonCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefButtonCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefButtonCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefButtonCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefButtonCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefView> CefButtonCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefButtonCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefButtonCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefButtonCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefButtonCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefButtonCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefButtonCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefButtonCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefButtonCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefButtonCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefButtonCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefButtonCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefButtonCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefButtonCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefButtonCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefButtonCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefButtonCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefButtonCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                         CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefButtonCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                           CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefButtonCToCpp::CefButtonCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefButtonCToCpp::~CefButtonCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_button_t*
+CefCToCppRefCounted<CefButtonCToCpp, CefButton, cef_button_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefButton* c) {
+  if (type == WT_LABEL_BUTTON) {
+    return reinterpret_cast<cef_button_t*>(
+        CefLabelButtonCToCpp::Unwrap(reinterpret_cast<CefLabelButton*>(c)));
+  }
+  if (type == WT_MENU_BUTTON) {
+    return reinterpret_cast<cef_button_t*>(
+        CefMenuButtonCToCpp::Unwrap(reinterpret_cast<CefMenuButton*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefButtonCToCpp, CefButton, cef_button_t>::
+    kWrapperType = WT_BUTTON;
diff --git a/src/libcef_dll/ctocpp/views/button_ctocpp.h b/src/libcef_dll/ctocpp/views/button_ctocpp.h
new file mode 100644
index 0000000..192d2c8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/button_ctocpp.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=54c17da1bcad99263468117e4ee4aa96174b91c3$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_BUTTON_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_BUTTON_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_button_capi.h"
+#include "include/capi/views/cef_label_button_capi.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_label_button.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefButtonCToCpp
+    : public CefCToCppRefCounted<CefButtonCToCpp, CefButton, cef_button_t> {
+ public:
+  CefButtonCToCpp();
+  virtual ~CefButtonCToCpp();
+
+  // CefButton methods.
+  CefRefPtr<CefLabelButton> AsLabelButton() OVERRIDE;
+  void SetState(cef_button_state_t state) OVERRIDE;
+  cef_button_state_t GetState() OVERRIDE;
+  void SetInkDropEnabled(bool enabled) OVERRIDE;
+  void SetTooltipText(const CefString& tooltip_text) OVERRIDE;
+  void SetAccessibleName(const CefString& name) OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_BUTTON_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/button_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/views/button_delegate_ctocpp.cc
new file mode 100644
index 0000000..c440bd3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/button_delegate_ctocpp.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=17852d5b4f41fd2e6d5e3b33b61d014bc4131dc3$
+//
+
+#include "libcef_dll/ctocpp/views/button_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefButtonDelegateCToCpp::OnButtonPressed(CefRefPtr<CefButton> button) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_button_pressed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: button; type: refptr_diff
+  DCHECK(button.get());
+  if (!button.get())
+    return;
+
+  // Execute
+  _struct->on_button_pressed(_struct, CefButtonCppToC::Wrap(button));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonDelegateCToCpp::OnButtonStateChanged(
+    CefRefPtr<CefButton> button) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_button_state_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: button; type: refptr_diff
+  DCHECK(button.get());
+  if (!button.get())
+    return;
+
+  // Execute
+  _struct->on_button_state_changed(_struct, CefButtonCppToC::Wrap(button));
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefButtonDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_preferred_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefButtonDelegateCToCpp::GetMinimumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_minimum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefButtonDelegateCToCpp::GetMaximumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_maximum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefButtonDelegateCToCpp::GetHeightForWidth(CefRefPtr<CefView> view,
+                                               int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return 0;
+
+  // Execute
+  int _retval =
+      _struct->get_height_for_width(_struct, CefViewCppToC::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonDelegateCToCpp::OnParentViewChanged(CefRefPtr<CefView> view,
+                                                  bool added,
+                                                  CefRefPtr<CefView> parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_parent_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent.get());
+  if (!parent.get())
+    return;
+
+  // Execute
+  _struct->on_parent_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                  CefViewCppToC::Wrap(parent));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonDelegateCToCpp::OnChildViewChanged(CefRefPtr<CefView> view,
+                                                 bool added,
+                                                 CefRefPtr<CefView> child) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_child_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child.get());
+  if (!child.get())
+    return;
+
+  // Execute
+  _struct->on_child_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                 CefViewCppToC::Wrap(child));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonDelegateCToCpp::OnFocus(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_focus(_struct, CefViewCppToC::Wrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefButtonDelegateCToCpp::OnBlur(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_blur))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_blur(_struct, CefViewCppToC::Wrap(view));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefButtonDelegateCToCpp::CefButtonDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefButtonDelegateCToCpp::~CefButtonDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_button_delegate_t* CefCToCppRefCounted<
+    CefButtonDelegateCToCpp,
+    CefButtonDelegate,
+    cef_button_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                          CefButtonDelegate* c) {
+  if (type == WT_MENU_BUTTON_DELEGATE) {
+    return reinterpret_cast<cef_button_delegate_t*>(
+        CefMenuButtonDelegateCToCpp::Unwrap(
+            reinterpret_cast<CefMenuButtonDelegate*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefButtonDelegateCToCpp,
+                                   CefButtonDelegate,
+                                   cef_button_delegate_t>::kWrapperType =
+    WT_BUTTON_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/views/button_delegate_ctocpp.h b/src/libcef_dll/ctocpp/views/button_delegate_ctocpp.h
new file mode 100644
index 0000000..3c0e004
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/button_delegate_ctocpp.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6e52653a50e0930437a93fa1f9ac01b8c3b39c02$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_BUTTON_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_BUTTON_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_button_capi.h"
+#include "include/capi/views/cef_button_delegate_capi.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_button_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefButtonDelegateCToCpp
+    : public CefCToCppRefCounted<CefButtonDelegateCToCpp,
+                                 CefButtonDelegate,
+                                 cef_button_delegate_t> {
+ public:
+  CefButtonDelegateCToCpp();
+  virtual ~CefButtonDelegateCToCpp();
+
+  // CefButtonDelegate methods.
+  void OnButtonPressed(CefRefPtr<CefButton> button) override;
+  void OnButtonStateChanged(CefRefPtr<CefButton> button) override;
+
+  // CefViewDelegate methods.
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override;
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override;
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override;
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override;
+  void OnFocus(CefRefPtr<CefView> view) override;
+  void OnBlur(CefRefPtr<CefView> view) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_BUTTON_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/display_ctocpp.cc b/src/libcef_dll/ctocpp/views/display_ctocpp.cc
new file mode 100644
index 0000000..5f09dcc
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/display_ctocpp.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=50521680b4ef13badbac42fa1a5e28d5ddf46194$
+//
+
+#include "libcef_dll/ctocpp/views/display_ctocpp.h"
+#include <algorithm>
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDisplay> CefDisplay::GetPrimaryDisplay() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_display_t* _retval = cef_display_get_primary();
+
+  // Return type: refptr_same
+  return CefDisplayCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDisplay> CefDisplay::GetDisplayNearestPoint(
+    const CefPoint& point,
+    bool input_pixel_coords) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_display_t* _retval =
+      cef_display_get_nearest_point(&point, input_pixel_coords);
+
+  // Return type: refptr_same
+  return CefDisplayCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDisplay> CefDisplay::GetDisplayMatchingBounds(
+    const CefRect& bounds,
+    bool input_pixel_coords) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_display_t* _retval =
+      cef_display_get_matching_bounds(&bounds, input_pixel_coords);
+
+  // Return type: refptr_same
+  return CefDisplayCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefDisplay::GetDisplayCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = cef_display_get_count();
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplay::GetAllDisplays(std::vector<CefRefPtr<CefDisplay>>& displays) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: displays; type: refptr_vec_same_byref
+  size_t displaysSize = displays.size();
+  size_t displaysCount = std::max(GetDisplayCount(), displaysSize);
+  cef_display_t** displaysList = NULL;
+  if (displaysCount > 0) {
+    displaysList = new cef_display_t*[displaysCount];
+    DCHECK(displaysList);
+    if (displaysList) {
+      memset(displaysList, 0, sizeof(cef_display_t*) * displaysCount);
+    }
+    if (displaysList && displaysSize > 0) {
+      for (size_t i = 0; i < displaysSize; ++i) {
+        displaysList[i] = CefDisplayCToCpp::Unwrap(displays[i]);
+      }
+    }
+  }
+
+  // Execute
+  cef_display_get_alls(&displaysCount, displaysList);
+
+  // Restore param:displays; type: refptr_vec_same_byref
+  displays.clear();
+  if (displaysCount > 0 && displaysList) {
+    for (size_t i = 0; i < displaysCount; ++i) {
+      displays.push_back(CefDisplayCToCpp::Wrap(displaysList[i]));
+    }
+    delete[] displaysList;
+  }
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") int64 CefDisplayCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") float CefDisplayCToCpp::GetDeviceScaleFactor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_device_scale_factor))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  float _retval = _struct->get_device_scale_factor(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayCToCpp::ConvertPointToPixels(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_pixels))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->convert_point_to_pixels(_struct, &point);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplayCToCpp::ConvertPointFromPixels(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_pixels))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->convert_point_from_pixels(_struct, &point);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefDisplayCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefDisplayCToCpp::GetWorkArea() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_work_area))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_work_area(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefDisplayCToCpp::GetRotation() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_display_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_rotation))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_rotation(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefDisplayCToCpp::CefDisplayCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefDisplayCToCpp::~CefDisplayCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_display_t*
+CefCToCppRefCounted<CefDisplayCToCpp, CefDisplay, cef_display_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefDisplay* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefDisplayCToCpp,
+                                   CefDisplay,
+                                   cef_display_t>::kWrapperType = WT_DISPLAY;
diff --git a/src/libcef_dll/ctocpp/views/display_ctocpp.h b/src/libcef_dll/ctocpp/views/display_ctocpp.h
new file mode 100644
index 0000000..6e60795
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/display_ctocpp.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8414b880b6fbb3ea8dba03250a387fa2f525edf6$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_DISPLAY_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_DISPLAY_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_display_capi.h"
+#include "include/views/cef_display.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefDisplayCToCpp
+    : public CefCToCppRefCounted<CefDisplayCToCpp, CefDisplay, cef_display_t> {
+ public:
+  CefDisplayCToCpp();
+  virtual ~CefDisplayCToCpp();
+
+  // CefDisplay methods.
+  int64 GetID() OVERRIDE;
+  float GetDeviceScaleFactor() OVERRIDE;
+  void ConvertPointToPixels(CefPoint& point) OVERRIDE;
+  void ConvertPointFromPixels(CefPoint& point) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetWorkArea() OVERRIDE;
+  int GetRotation() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_DISPLAY_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/fill_layout_ctocpp.cc b/src/libcef_dll/ctocpp/views/fill_layout_ctocpp.cc
new file mode 100644
index 0000000..d85016a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/fill_layout_ctocpp.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c23b9c61162d6f4e69a04dca457d014a272ca4cf$
+//
+
+#include "libcef_dll/ctocpp/views/fill_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/box_layout_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBoxLayout> CefFillLayoutCToCpp::AsBoxLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = reinterpret_cast<cef_layout_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_box_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_box_layout_t* _retval = _struct->as_box_layout(_struct);
+
+  // Return type: refptr_same
+  return CefBoxLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFillLayout> CefFillLayoutCToCpp::AsFillLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = reinterpret_cast<cef_layout_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_fill_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_fill_layout_t* _retval = _struct->as_fill_layout(_struct);
+
+  // Return type: refptr_same
+  return CefFillLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefFillLayoutCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = reinterpret_cast<cef_layout_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefFillLayoutCToCpp::CefFillLayoutCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefFillLayoutCToCpp::~CefFillLayoutCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_fill_layout_t*
+CefCToCppRefCounted<CefFillLayoutCToCpp, CefFillLayout, cef_fill_layout_t>::
+    UnwrapDerived(CefWrapperType type, CefFillLayout* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefFillLayoutCToCpp,
+                                   CefFillLayout,
+                                   cef_fill_layout_t>::kWrapperType =
+    WT_FILL_LAYOUT;
diff --git a/src/libcef_dll/ctocpp/views/fill_layout_ctocpp.h b/src/libcef_dll/ctocpp/views/fill_layout_ctocpp.h
new file mode 100644
index 0000000..39461c0
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/fill_layout_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8eb4ca176ea5985fcbe291dc5e05b65dade7bb77$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_FILL_LAYOUT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_FILL_LAYOUT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_fill_layout_capi.h"
+#include "include/views/cef_fill_layout.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefFillLayoutCToCpp : public CefCToCppRefCounted<CefFillLayoutCToCpp,
+                                                       CefFillLayout,
+                                                       cef_fill_layout_t> {
+ public:
+  CefFillLayoutCToCpp();
+  virtual ~CefFillLayoutCToCpp();
+
+  // CefFillLayout methods.
+
+  // CefLayout methods.
+  CefRefPtr<CefBoxLayout> AsBoxLayout() OVERRIDE;
+  CefRefPtr<CefFillLayout> AsFillLayout() OVERRIDE;
+  bool IsValid() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_FILL_LAYOUT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/label_button_ctocpp.cc b/src/libcef_dll/ctocpp/views/label_button_ctocpp.cc
new file mode 100644
index 0000000..7dc4bd3
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/label_button_ctocpp.cc
@@ -0,0 +1,1137 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6682fd73dd4a88b7082ea6032a9a169f27295cc8$
+//
+
+#include "libcef_dll/ctocpp/views/label_button_ctocpp.h"
+#include "libcef_dll/cpptoc/views/button_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/image_ctocpp.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/menu_button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLabelButton> CefLabelButton::CreateLabelButton(
+    CefRefPtr<CefButtonDelegate> delegate,
+    const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: delegate; type: refptr_diff
+  DCHECK(delegate.get());
+  if (!delegate.get())
+    return nullptr;
+  // Unverified params: text
+
+  // Execute
+  cef_label_button_t* _retval = cef_label_button_create(
+      CefButtonDelegateCppToC::Wrap(delegate), text.GetStruct());
+
+  // Return type: refptr_same
+  return CefLabelButtonCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuButton> CefLabelButtonCToCpp::AsMenuButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_menu_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_menu_button_t* _retval = _struct->as_menu_button(_struct);
+
+  // Return type: refptr_same
+  return CefMenuButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetText(const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return;
+
+  // Execute
+  _struct->set_text(_struct, text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefLabelButtonCToCpp::GetText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetImage(cef_button_state_t button_state,
+                                    CefRefPtr<CefImage> image) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_image))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: image
+
+  // Execute
+  _struct->set_image(_struct, button_state, CefImageCToCpp::Unwrap(image));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefImage> CefLabelButtonCToCpp::GetImage(
+    cef_button_state_t button_state) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_image))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_image_t* _retval = _struct->get_image(_struct, button_state);
+
+  // Return type: refptr_same
+  return CefImageCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetTextColor(cef_button_state_t for_state,
+                                        cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_text_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_text_color(_struct, for_state, color);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetEnabledTextColors(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_enabled_text_colors))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled_text_colors(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetFontList(const CefString& font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_font_list))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: font_list; type: string_byref_const
+  DCHECK(!font_list.empty());
+  if (font_list.empty())
+    return;
+
+  // Execute
+  _struct->set_font_list(_struct, font_list.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetHorizontalAlignment(
+    cef_horizontal_alignment_t alignment) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_horizontal_alignment))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_horizontal_alignment(_struct, alignment);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetMinimumSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_minimum_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_minimum_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetMaximumSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_maximum_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_maximum_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLabelButton> CefLabelButtonCToCpp::AsLabelButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_label_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_label_button_t* _retval = _struct->as_label_button(_struct);
+
+  // Return type: refptr_same
+  return CefLabelButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetState(cef_button_state_t state) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_state))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_state(_struct, state);
+}
+
+NO_SANITIZE("cfi-icall") cef_button_state_t CefLabelButtonCToCpp::GetState() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_state))
+    return CEF_BUTTON_STATE_NORMAL;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_state_t _retval = _struct->get_state(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetInkDropEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_ink_drop_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_ink_drop_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetTooltipText(const CefString& tooltip_text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_tooltip_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: tooltip_text; type: string_byref_const
+  DCHECK(!tooltip_text.empty());
+  if (tooltip_text.empty())
+    return;
+
+  // Execute
+  _struct->set_tooltip_text(_struct, tooltip_text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetAccessibleName(const CefString& name) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_accessible_name))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+
+  // Execute
+  _struct->set_accessible_name(_struct, name.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefLabelButtonCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefLabelButtonCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefLabelButtonCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefLabelButtonCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefLabelButtonCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefLabelButtonCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefLabelButtonCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefLabelButtonCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefLabelButtonCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLabelButtonCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefLabelButtonCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefWindow> CefLabelButtonCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefLabelButtonCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefLabelButtonCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefLabelButtonCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefLabelButtonCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefLabelButtonCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefLabelButtonCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefLabelButtonCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefLabelButtonCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefLabelButtonCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefLabelButtonCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefLabelButtonCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefLabelButtonCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefLabelButtonCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefLabelButtonCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefLabelButtonCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefLabelButtonCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefLabelButtonCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefLabelButtonCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefLabelButtonCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefLabelButtonCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefLabelButtonCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefLabelButtonCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefLabelButtonCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefLabelButtonCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefLabelButtonCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_color_t CefLabelButtonCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLabelButtonCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLabelButtonCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLabelButtonCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLabelButtonCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLabelButtonCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                              CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefLabelButtonCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                                CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLabelButtonCToCpp::CefLabelButtonCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLabelButtonCToCpp::~CefLabelButtonCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_label_button_t*
+CefCToCppRefCounted<CefLabelButtonCToCpp, CefLabelButton, cef_label_button_t>::
+    UnwrapDerived(CefWrapperType type, CefLabelButton* c) {
+  if (type == WT_MENU_BUTTON) {
+    return reinterpret_cast<cef_label_button_t*>(
+        CefMenuButtonCToCpp::Unwrap(reinterpret_cast<CefMenuButton*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefLabelButtonCToCpp,
+                                   CefLabelButton,
+                                   cef_label_button_t>::kWrapperType =
+    WT_LABEL_BUTTON;
diff --git a/src/libcef_dll/ctocpp/views/label_button_ctocpp.h b/src/libcef_dll/ctocpp/views/label_button_ctocpp.h
new file mode 100644
index 0000000..a46a9a1
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/label_button_ctocpp.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=34cf1281025c4db90d4a11237a68799276bd74d6$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_LABEL_BUTTON_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_LABEL_BUTTON_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_label_button_capi.h"
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_menu_button.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefLabelButtonCToCpp : public CefCToCppRefCounted<CefLabelButtonCToCpp,
+                                                        CefLabelButton,
+                                                        cef_label_button_t> {
+ public:
+  CefLabelButtonCToCpp();
+  virtual ~CefLabelButtonCToCpp();
+
+  // CefLabelButton methods.
+  CefRefPtr<CefMenuButton> AsMenuButton() OVERRIDE;
+  void SetText(const CefString& text) OVERRIDE;
+  CefString GetText() OVERRIDE;
+  void SetImage(cef_button_state_t button_state,
+                CefRefPtr<CefImage> image) OVERRIDE;
+  CefRefPtr<CefImage> GetImage(cef_button_state_t button_state) OVERRIDE;
+  void SetTextColor(cef_button_state_t for_state, cef_color_t color) OVERRIDE;
+  void SetEnabledTextColors(cef_color_t color) OVERRIDE;
+  void SetFontList(const CefString& font_list) OVERRIDE;
+  void SetHorizontalAlignment(cef_horizontal_alignment_t alignment) OVERRIDE;
+  void SetMinimumSize(const CefSize& size) OVERRIDE;
+  void SetMaximumSize(const CefSize& size) OVERRIDE;
+
+  // CefButton methods.
+  CefRefPtr<CefLabelButton> AsLabelButton() OVERRIDE;
+  void SetState(cef_button_state_t state) OVERRIDE;
+  cef_button_state_t GetState() OVERRIDE;
+  void SetInkDropEnabled(bool enabled) OVERRIDE;
+  void SetTooltipText(const CefString& tooltip_text) OVERRIDE;
+  void SetAccessibleName(const CefString& name) OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_LABEL_BUTTON_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/layout_ctocpp.cc b/src/libcef_dll/ctocpp/views/layout_ctocpp.cc
new file mode 100644
index 0000000..0de8c3a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/layout_ctocpp.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0acbb84fac664d4f43b0d32586dcdec0b81cd082$
+//
+
+#include "libcef_dll/ctocpp/views/layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/box_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/fill_layout_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBoxLayout> CefLayoutCToCpp::AsBoxLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_box_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_box_layout_t* _retval = _struct->as_box_layout(_struct);
+
+  // Return type: refptr_same
+  return CefBoxLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFillLayout> CefLayoutCToCpp::AsFillLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_fill_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_fill_layout_t* _retval = _struct->as_fill_layout(_struct);
+
+  // Return type: refptr_same
+  return CefFillLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") bool CefLayoutCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_layout_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefLayoutCToCpp::CefLayoutCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefLayoutCToCpp::~CefLayoutCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_layout_t*
+CefCToCppRefCounted<CefLayoutCToCpp, CefLayout, cef_layout_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefLayout* c) {
+  if (type == WT_BOX_LAYOUT) {
+    return reinterpret_cast<cef_layout_t*>(
+        CefBoxLayoutCToCpp::Unwrap(reinterpret_cast<CefBoxLayout*>(c)));
+  }
+  if (type == WT_FILL_LAYOUT) {
+    return reinterpret_cast<cef_layout_t*>(
+        CefFillLayoutCToCpp::Unwrap(reinterpret_cast<CefFillLayout*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefLayoutCToCpp, CefLayout, cef_layout_t>::
+    kWrapperType = WT_LAYOUT;
diff --git a/src/libcef_dll/ctocpp/views/layout_ctocpp.h b/src/libcef_dll/ctocpp/views/layout_ctocpp.h
new file mode 100644
index 0000000..b09181f
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/layout_ctocpp.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0cc6a8abd2da2a8b8aa42a5f515b86696b390711$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_LAYOUT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_LAYOUT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_box_layout_capi.h"
+#include "include/capi/views/cef_fill_layout_capi.h"
+#include "include/capi/views/cef_layout_capi.h"
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_fill_layout.h"
+#include "include/views/cef_layout.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefLayoutCToCpp
+    : public CefCToCppRefCounted<CefLayoutCToCpp, CefLayout, cef_layout_t> {
+ public:
+  CefLayoutCToCpp();
+  virtual ~CefLayoutCToCpp();
+
+  // CefLayout methods.
+  CefRefPtr<CefBoxLayout> AsBoxLayout() OVERRIDE;
+  CefRefPtr<CefFillLayout> AsFillLayout() OVERRIDE;
+  bool IsValid() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_LAYOUT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/menu_button_ctocpp.cc b/src/libcef_dll/ctocpp/views/menu_button_ctocpp.cc
new file mode 100644
index 0000000..146ed18
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/menu_button_ctocpp.cc
@@ -0,0 +1,1177 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2ae5da63dbe303051882b451b59db219782bdd87$
+//
+
+#include "libcef_dll/ctocpp/views/menu_button_ctocpp.h"
+#include "libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/image_ctocpp.h"
+#include "libcef_dll/ctocpp/menu_model_ctocpp.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/label_button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuButton> CefMenuButton::CreateMenuButton(
+    CefRefPtr<CefMenuButtonDelegate> delegate,
+    const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: delegate; type: refptr_diff
+  DCHECK(delegate.get());
+  if (!delegate.get())
+    return nullptr;
+  // Unverified params: text
+
+  // Execute
+  cef_menu_button_t* _retval = cef_menu_button_create(
+      CefMenuButtonDelegateCppToC::Wrap(delegate), text.GetStruct());
+
+  // Return type: refptr_same
+  return CefMenuButtonCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                                   const CefPoint& screen_point,
+                                   cef_menu_anchor_position_t anchor_position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, show_menu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_same
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->show_menu(_struct, CefMenuModelCToCpp::Unwrap(menu_model),
+                     &screen_point, anchor_position);
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::TriggerMenu() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_button_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, trigger_menu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->trigger_menu(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuButton> CefMenuButtonCToCpp::AsMenuButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_menu_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_menu_button_t* _retval = _struct->as_menu_button(_struct);
+
+  // Return type: refptr_same
+  return CefMenuButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetText(const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return;
+
+  // Execute
+  _struct->set_text(_struct, text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefMenuButtonCToCpp::GetText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetImage(cef_button_state_t button_state,
+                                   CefRefPtr<CefImage> image) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_image))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: image
+
+  // Execute
+  _struct->set_image(_struct, button_state, CefImageCToCpp::Unwrap(image));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefImage> CefMenuButtonCToCpp::GetImage(
+    cef_button_state_t button_state) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_image))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_image_t* _retval = _struct->get_image(_struct, button_state);
+
+  // Return type: refptr_same
+  return CefImageCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetTextColor(cef_button_state_t for_state,
+                                       cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_text_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_text_color(_struct, for_state, color);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetEnabledTextColors(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled_text_colors))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled_text_colors(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetFontList(const CefString& font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_font_list))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: font_list; type: string_byref_const
+  DCHECK(!font_list.empty());
+  if (font_list.empty())
+    return;
+
+  // Execute
+  _struct->set_font_list(_struct, font_list.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetHorizontalAlignment(
+    cef_horizontal_alignment_t alignment) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_horizontal_alignment))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_horizontal_alignment(_struct, alignment);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetMinimumSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_minimum_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_minimum_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetMaximumSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_label_button_t* _struct =
+      reinterpret_cast<cef_label_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_maximum_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_maximum_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLabelButton> CefMenuButtonCToCpp::AsLabelButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_label_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_label_button_t* _retval = _struct->as_label_button(_struct);
+
+  // Return type: refptr_same
+  return CefLabelButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetState(cef_button_state_t state) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_state))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_state(_struct, state);
+}
+
+NO_SANITIZE("cfi-icall") cef_button_state_t CefMenuButtonCToCpp::GetState() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_state))
+    return CEF_BUTTON_STATE_NORMAL;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_state_t _retval = _struct->get_state(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetInkDropEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_ink_drop_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_ink_drop_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetTooltipText(const CefString& tooltip_text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_tooltip_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: tooltip_text; type: string_byref_const
+  DCHECK(!tooltip_text.empty());
+  if (tooltip_text.empty())
+    return;
+
+  // Execute
+  _struct->set_tooltip_text(_struct, tooltip_text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetAccessibleName(const CefString& name) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_t* _struct = reinterpret_cast<cef_button_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_accessible_name))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+
+  // Execute
+  _struct->set_accessible_name(_struct, name.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefMenuButtonCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefMenuButtonCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefMenuButtonCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefMenuButtonCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefMenuButtonCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefMenuButtonCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefMenuButtonCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuButtonCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuButtonCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuButtonCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefMenuButtonCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefMenuButtonCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuButtonCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuButtonCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefMenuButtonCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefMenuButtonCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefMenuButtonCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefMenuButtonCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefMenuButtonCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefMenuButtonCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefMenuButtonCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefMenuButtonCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefMenuButtonCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefMenuButtonCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuButtonCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuButtonCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuButtonCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuButtonCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefMenuButtonCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefMenuButtonCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefMenuButtonCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuButtonCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuButtonCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuButtonCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuButtonCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuButtonCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                             CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefMenuButtonCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                               CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuButtonCToCpp::CefMenuButtonCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuButtonCToCpp::~CefMenuButtonCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_menu_button_t*
+CefCToCppRefCounted<CefMenuButtonCToCpp, CefMenuButton, cef_menu_button_t>::
+    UnwrapDerived(CefWrapperType type, CefMenuButton* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMenuButtonCToCpp,
+                                   CefMenuButton,
+                                   cef_menu_button_t>::kWrapperType =
+    WT_MENU_BUTTON;
diff --git a/src/libcef_dll/ctocpp/views/menu_button_ctocpp.h b/src/libcef_dll/ctocpp/views/menu_button_ctocpp.h
new file mode 100644
index 0000000..fbc5e3f
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/menu_button_ctocpp.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7e2978dea2942c4ceeeb9d57609ee3fb0fa5bc4a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/views/cef_menu_button.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMenuButtonCToCpp : public CefCToCppRefCounted<CefMenuButtonCToCpp,
+                                                       CefMenuButton,
+                                                       cef_menu_button_t> {
+ public:
+  CefMenuButtonCToCpp();
+  virtual ~CefMenuButtonCToCpp();
+
+  // CefMenuButton methods.
+  void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                const CefPoint& screen_point,
+                cef_menu_anchor_position_t anchor_position) OVERRIDE;
+  void TriggerMenu() OVERRIDE;
+
+  // CefLabelButton methods.
+  CefRefPtr<CefMenuButton> AsMenuButton() OVERRIDE;
+  void SetText(const CefString& text) OVERRIDE;
+  CefString GetText() OVERRIDE;
+  void SetImage(cef_button_state_t button_state,
+                CefRefPtr<CefImage> image) OVERRIDE;
+  CefRefPtr<CefImage> GetImage(cef_button_state_t button_state) OVERRIDE;
+  void SetTextColor(cef_button_state_t for_state, cef_color_t color) OVERRIDE;
+  void SetEnabledTextColors(cef_color_t color) OVERRIDE;
+  void SetFontList(const CefString& font_list) OVERRIDE;
+  void SetHorizontalAlignment(cef_horizontal_alignment_t alignment) OVERRIDE;
+  void SetMinimumSize(const CefSize& size) OVERRIDE;
+  void SetMaximumSize(const CefSize& size) OVERRIDE;
+
+  // CefButton methods.
+  CefRefPtr<CefLabelButton> AsLabelButton() OVERRIDE;
+  void SetState(cef_button_state_t state) OVERRIDE;
+  cef_button_state_t GetState() OVERRIDE;
+  void SetInkDropEnabled(bool enabled) OVERRIDE;
+  void SetTooltipText(const CefString& tooltip_text) OVERRIDE;
+  void SetAccessibleName(const CefString& name) OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.cc
new file mode 100644
index 0000000..de3a3e2
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.cc
@@ -0,0 +1,309 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e22af57f1e6505996368140760d9a249bf9189e9$
+//
+
+#include "libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/views/button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/menu_button_cpptoc.h"
+#include "libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonDelegateCToCpp::OnMenuButtonPressed(
+    CefRefPtr<CefMenuButton> menu_button,
+    const CefPoint& screen_point,
+    CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_menu_button_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_menu_button_pressed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_button; type: refptr_diff
+  DCHECK(menu_button.get());
+  if (!menu_button.get())
+    return;
+  // Verify param: button_pressed_lock; type: refptr_diff
+  DCHECK(button_pressed_lock.get());
+  if (!button_pressed_lock.get())
+    return;
+
+  // Execute
+  _struct->on_menu_button_pressed(
+      _struct, CefMenuButtonCppToC::Wrap(menu_button), &screen_point,
+      CefMenuButtonPressedLockCppToC::Wrap(button_pressed_lock));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonDelegateCToCpp::OnButtonPressed(CefRefPtr<CefButton> button) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_delegate_t* _struct =
+      reinterpret_cast<cef_button_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_button_pressed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: button; type: refptr_diff
+  DCHECK(button.get());
+  if (!button.get())
+    return;
+
+  // Execute
+  _struct->on_button_pressed(_struct, CefButtonCppToC::Wrap(button));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonDelegateCToCpp::OnButtonStateChanged(
+    CefRefPtr<CefButton> button) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_button_delegate_t* _struct =
+      reinterpret_cast<cef_button_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_button_state_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: button; type: refptr_diff
+  DCHECK(button.get());
+  if (!button.get())
+    return;
+
+  // Execute
+  _struct->on_button_state_changed(_struct, CefButtonCppToC::Wrap(button));
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefMenuButtonDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_preferred_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefMenuButtonDelegateCToCpp::GetMinimumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_minimum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefMenuButtonDelegateCToCpp::GetMaximumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_maximum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefMenuButtonDelegateCToCpp::GetHeightForWidth(CefRefPtr<CefView> view,
+                                                   int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return 0;
+
+  // Execute
+  int _retval =
+      _struct->get_height_for_width(_struct, CefViewCppToC::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonDelegateCToCpp::OnParentViewChanged(
+    CefRefPtr<CefView> view,
+    bool added,
+    CefRefPtr<CefView> parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_parent_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent.get());
+  if (!parent.get())
+    return;
+
+  // Execute
+  _struct->on_parent_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                  CefViewCppToC::Wrap(parent));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonDelegateCToCpp::OnChildViewChanged(CefRefPtr<CefView> view,
+                                                     bool added,
+                                                     CefRefPtr<CefView> child) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_child_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child.get());
+  if (!child.get())
+    return;
+
+  // Execute
+  _struct->on_child_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                 CefViewCppToC::Wrap(child));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonDelegateCToCpp::OnFocus(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_focus(_struct, CefViewCppToC::Wrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefMenuButtonDelegateCToCpp::OnBlur(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_blur))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_blur(_struct, CefViewCppToC::Wrap(view));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuButtonDelegateCToCpp::CefMenuButtonDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuButtonDelegateCToCpp::~CefMenuButtonDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_menu_button_delegate_t* CefCToCppRefCounted<
+    CefMenuButtonDelegateCToCpp,
+    CefMenuButtonDelegate,
+    cef_menu_button_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                               CefMenuButtonDelegate* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefMenuButtonDelegateCToCpp,
+                                   CefMenuButtonDelegate,
+                                   cef_menu_button_delegate_t>::kWrapperType =
+    WT_MENU_BUTTON_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h b/src/libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h
new file mode 100644
index 0000000..c49cb47
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=524d6cee4ca930e3ea4db6f08ab72e3d4578d19a$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/capi/views/cef_menu_button_delegate_capi.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefMenuButtonDelegateCToCpp
+    : public CefCToCppRefCounted<CefMenuButtonDelegateCToCpp,
+                                 CefMenuButtonDelegate,
+                                 cef_menu_button_delegate_t> {
+ public:
+  CefMenuButtonDelegateCToCpp();
+  virtual ~CefMenuButtonDelegateCToCpp();
+
+  // CefMenuButtonDelegate methods.
+  void OnMenuButtonPressed(
+      CefRefPtr<CefMenuButton> menu_button,
+      const CefPoint& screen_point,
+      CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) override;
+
+  // CefButtonDelegate methods.
+  void OnButtonPressed(CefRefPtr<CefButton> button) override;
+  void OnButtonStateChanged(CefRefPtr<CefButton> button) override;
+
+  // CefViewDelegate methods.
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override;
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override;
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override;
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override;
+  void OnFocus(CefRefPtr<CefView> view) override;
+  void OnBlur(CefRefPtr<CefView> view) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.cc b/src/libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.cc
new file mode 100644
index 0000000..b36bbfa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=34a0389265fe422b1def87bb4c94115411c77c08$
+//
+
+#include "libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefMenuButtonPressedLockCToCpp::CefMenuButtonPressedLockCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefMenuButtonPressedLockCToCpp::~CefMenuButtonPressedLockCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_menu_button_pressed_lock_t* CefCToCppRefCounted<
+    CefMenuButtonPressedLockCToCpp,
+    CefMenuButtonPressedLock,
+    cef_menu_button_pressed_lock_t>::UnwrapDerived(CefWrapperType type,
+                                                   CefMenuButtonPressedLock*
+                                                       c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefMenuButtonPressedLockCToCpp,
+                        CefMenuButtonPressedLock,
+                        cef_menu_button_pressed_lock_t>::kWrapperType =
+        WT_MENU_BUTTON_PRESSED_LOCK;
diff --git a/src/libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.h b/src/libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.h
new file mode 100644
index 0000000..ef8e36a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1727eb9fe6e45fbd1d5479fde119a58485af1022$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_PRESSED_LOCK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_PRESSED_LOCK_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/capi/views/cef_menu_button_delegate_capi.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefMenuButtonPressedLockCToCpp
+    : public CefCToCppRefCounted<CefMenuButtonPressedLockCToCpp,
+                                 CefMenuButtonPressedLock,
+                                 cef_menu_button_pressed_lock_t> {
+ public:
+  CefMenuButtonPressedLockCToCpp();
+  virtual ~CefMenuButtonPressedLockCToCpp();
+
+  // CefMenuButtonPressedLock methods.
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_MENU_BUTTON_PRESSED_LOCK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/panel_ctocpp.cc b/src/libcef_dll/ctocpp/views/panel_ctocpp.cc
new file mode 100644
index 0000000..e5e4003
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/panel_ctocpp.cc
@@ -0,0 +1,1055 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=9821af1835fd4289a117b3dba1b893b5bf83dd55$
+//
+
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/cpptoc/views/panel_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/box_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/fill_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefPanel> CefPanel::CreatePanel(
+    CefRefPtr<CefPanelDelegate> delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  cef_panel_t* _retval =
+      cef_panel_create(CefPanelDelegateCppToC::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefPanelCToCpp::AsWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->as_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFillLayout> CefPanelCToCpp::SetToFillLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_to_fill_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_fill_layout_t* _retval = _struct->set_to_fill_layout(_struct);
+
+  // Return type: refptr_same
+  return CefFillLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBoxLayout> CefPanelCToCpp::SetToBoxLayout(
+    const CefBoxLayoutSettings& settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_to_box_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_box_layout_t* _retval = _struct->set_to_box_layout(_struct, &settings);
+
+  // Return type: refptr_same
+  return CefBoxLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefLayout> CefPanelCToCpp::GetLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_layout_t* _retval = _struct->get_layout(_struct);
+
+  // Return type: refptr_same
+  return CefLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::Layout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelCToCpp::AddChildView(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_child_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->add_child_view(_struct, CefViewCToCpp::Unwrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelCToCpp::AddChildViewAt(CefRefPtr<CefView> view, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, add_child_view_at))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return;
+
+  // Execute
+  _struct->add_child_view_at(_struct, CefViewCToCpp::Unwrap(view), index);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelCToCpp::ReorderChildView(CefRefPtr<CefView> view, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, reorder_child_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->reorder_child_view(_struct, CefViewCToCpp::Unwrap(view), index);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelCToCpp::RemoveChildView(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_child_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->remove_child_view(_struct, CefViewCToCpp::Unwrap(view));
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::RemoveAllChildViews() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_all_child_views))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->remove_all_child_views(_struct);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefPanelCToCpp::GetChildViewCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_child_view_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_child_view_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefPanelCToCpp::GetChildViewAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_child_view_at))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return nullptr;
+
+  // Execute
+  cef_view_t* _retval = _struct->get_child_view_at(_struct, index);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefPanelCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefPanelCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefPanelCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefPanelCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefTextfield> CefPanelCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefPanelCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefPanelCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefPanelCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefPanelCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefPanelCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefPanelCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefView> CefPanelCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefPanelCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefPanelCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefPanelCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefPanelCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefPanelCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefPanelCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefPanelCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefPanelCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefPanelCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefPanelCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefPanelCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefPanelCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPanelCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPanelCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPanelCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPanelCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPanelCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                        CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefPanelCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                          CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPanelCToCpp::CefPanelCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPanelCToCpp::~CefPanelCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_panel_t*
+CefCToCppRefCounted<CefPanelCToCpp, CefPanel, cef_panel_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefPanel* c) {
+  if (type == WT_WINDOW) {
+    return reinterpret_cast<cef_panel_t*>(
+        CefWindowCToCpp::Unwrap(reinterpret_cast<CefWindow*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefPanelCToCpp, CefPanel, cef_panel_t>::kWrapperType =
+        WT_PANEL;
diff --git a/src/libcef_dll/ctocpp/views/panel_ctocpp.h b/src/libcef_dll/ctocpp/views/panel_ctocpp.h
new file mode 100644
index 0000000..5e37994
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/panel_ctocpp.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a2ad3c9f6314f8c05b6d807ec887c1aa67780cb6$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_PANEL_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_PANEL_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_box_layout_capi.h"
+#include "include/capi/views/cef_fill_layout_capi.h"
+#include "include/capi/views/cef_layout_capi.h"
+#include "include/capi/views/cef_panel_capi.h"
+#include "include/capi/views/cef_window_capi.h"
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_fill_layout.h"
+#include "include/views/cef_layout.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_window.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefPanelCToCpp
+    : public CefCToCppRefCounted<CefPanelCToCpp, CefPanel, cef_panel_t> {
+ public:
+  CefPanelCToCpp();
+  virtual ~CefPanelCToCpp();
+
+  // CefPanel methods.
+  CefRefPtr<CefWindow> AsWindow() OVERRIDE;
+  CefRefPtr<CefFillLayout> SetToFillLayout() OVERRIDE;
+  CefRefPtr<CefBoxLayout> SetToBoxLayout(
+      const CefBoxLayoutSettings& settings) OVERRIDE;
+  CefRefPtr<CefLayout> GetLayout() OVERRIDE;
+  void Layout() OVERRIDE;
+  void AddChildView(CefRefPtr<CefView> view) OVERRIDE;
+  void AddChildViewAt(CefRefPtr<CefView> view, int index) OVERRIDE;
+  void ReorderChildView(CefRefPtr<CefView> view, int index) OVERRIDE;
+  void RemoveChildView(CefRefPtr<CefView> view) OVERRIDE;
+  void RemoveAllChildViews() OVERRIDE;
+  size_t GetChildViewCount() OVERRIDE;
+  CefRefPtr<CefView> GetChildViewAt(int index) OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_PANEL_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/panel_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/views/panel_delegate_ctocpp.cc
new file mode 100644
index 0000000..9e904ce
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/panel_delegate_ctocpp.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c6d5a6d79d6e0ccb4e35b2f4bda92b2b99ba70bf$
+//
+
+#include "libcef_dll/ctocpp/views/panel_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/ctocpp/views/window_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefSize CefPanelDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_preferred_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefPanelDelegateCToCpp::GetMinimumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_minimum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefPanelDelegateCToCpp::GetMaximumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_maximum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefPanelDelegateCToCpp::GetHeightForWidth(CefRefPtr<CefView> view,
+                                              int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return 0;
+
+  // Execute
+  int _retval =
+      _struct->get_height_for_width(_struct, CefViewCppToC::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelDelegateCToCpp::OnParentViewChanged(CefRefPtr<CefView> view,
+                                                 bool added,
+                                                 CefRefPtr<CefView> parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_parent_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent.get());
+  if (!parent.get())
+    return;
+
+  // Execute
+  _struct->on_parent_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                  CefViewCppToC::Wrap(parent));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelDelegateCToCpp::OnChildViewChanged(CefRefPtr<CefView> view,
+                                                bool added,
+                                                CefRefPtr<CefView> child) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_child_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child.get());
+  if (!child.get())
+    return;
+
+  // Execute
+  _struct->on_child_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                 CefViewCppToC::Wrap(child));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelDelegateCToCpp::OnFocus(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_focus(_struct, CefViewCppToC::Wrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefPanelDelegateCToCpp::OnBlur(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_blur))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_blur(_struct, CefViewCppToC::Wrap(view));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefPanelDelegateCToCpp::CefPanelDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefPanelDelegateCToCpp::~CefPanelDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_panel_delegate_t*
+CefCToCppRefCounted<CefPanelDelegateCToCpp,
+                    CefPanelDelegate,
+                    cef_panel_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefPanelDelegate* c) {
+  if (type == WT_WINDOW_DELEGATE) {
+    return reinterpret_cast<cef_panel_delegate_t*>(
+        CefWindowDelegateCToCpp::Unwrap(
+            reinterpret_cast<CefWindowDelegate*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefPanelDelegateCToCpp,
+                                   CefPanelDelegate,
+                                   cef_panel_delegate_t>::kWrapperType =
+    WT_PANEL_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/views/panel_delegate_ctocpp.h b/src/libcef_dll/ctocpp/views/panel_delegate_ctocpp.h
new file mode 100644
index 0000000..4c8748e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/panel_delegate_ctocpp.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e5532f84397754cb9c2da1beeea8107ea5ab258b$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_PANEL_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_PANEL_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_panel_delegate_capi.h"
+#include "include/views/cef_panel_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefPanelDelegateCToCpp
+    : public CefCToCppRefCounted<CefPanelDelegateCToCpp,
+                                 CefPanelDelegate,
+                                 cef_panel_delegate_t> {
+ public:
+  CefPanelDelegateCToCpp();
+  virtual ~CefPanelDelegateCToCpp();
+
+  // CefPanelDelegate methods.
+
+  // CefViewDelegate methods.
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override;
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override;
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override;
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override;
+  void OnFocus(CefRefPtr<CefView> view) override;
+  void OnBlur(CefRefPtr<CefView> view) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_PANEL_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/scroll_view_ctocpp.cc b/src/libcef_dll/ctocpp/views/scroll_view_ctocpp.cc
new file mode 100644
index 0000000..9233777
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/scroll_view_ctocpp.cc
@@ -0,0 +1,959 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=1f55cd5b15e391c8e20faf3d060c46d36ead0a96$
+//
+
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefScrollView::CreateScrollView(
+    CefRefPtr<CefViewDelegate> delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  cef_scroll_view_t* _retval =
+      cef_scroll_view_create(CefViewDelegateCppToC::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefScrollViewCToCpp::SetContentView(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_scroll_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_content_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->set_content_view(_struct, CefViewCToCpp::Unwrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefScrollViewCToCpp::GetContentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_scroll_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_content_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_content_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefScrollViewCToCpp::GetVisibleContentRect() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_scroll_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_visible_content_rect))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_visible_content_rect(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::HasHorizontalScrollbar() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_scroll_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_horizontal_scrollbar))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_horizontal_scrollbar(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefScrollViewCToCpp::GetHorizontalScrollbarHeight() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_scroll_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_horizontal_scrollbar_height))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_horizontal_scrollbar_height(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::HasVerticalScrollbar() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_scroll_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_vertical_scrollbar))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_vertical_scrollbar(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") int CefScrollViewCToCpp::GetVerticalScrollbarWidth() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_scroll_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_vertical_scrollbar_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_vertical_scrollbar_width(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefScrollViewCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefScrollViewCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefScrollViewCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefScrollViewCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefScrollViewCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefScrollViewCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefScrollViewCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefScrollViewCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefScrollViewCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefScrollViewCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefScrollViewCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefScrollViewCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefScrollViewCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefScrollViewCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefScrollViewCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefScrollViewCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefScrollViewCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefScrollViewCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefScrollViewCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefScrollViewCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefScrollViewCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefScrollViewCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefScrollViewCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefScrollViewCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefScrollViewCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefScrollViewCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefScrollViewCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefScrollViewCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefScrollViewCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefScrollViewCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefScrollViewCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefScrollViewCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefScrollViewCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefScrollViewCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefScrollViewCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefScrollViewCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefScrollViewCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefScrollViewCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefScrollViewCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefScrollViewCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefScrollViewCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                             CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefScrollViewCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                               CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefScrollViewCToCpp::CefScrollViewCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefScrollViewCToCpp::~CefScrollViewCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_scroll_view_t*
+CefCToCppRefCounted<CefScrollViewCToCpp, CefScrollView, cef_scroll_view_t>::
+    UnwrapDerived(CefWrapperType type, CefScrollView* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefScrollViewCToCpp,
+                                   CefScrollView,
+                                   cef_scroll_view_t>::kWrapperType =
+    WT_SCROLL_VIEW;
diff --git a/src/libcef_dll/ctocpp/views/scroll_view_ctocpp.h b/src/libcef_dll/ctocpp/views/scroll_view_ctocpp.h
new file mode 100644
index 0000000..258f965
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/scroll_view_ctocpp.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=4e98f8d65f5844c90f1b9fc2a7c6ecde10a11d84$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_SCROLL_VIEW_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_SCROLL_VIEW_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_scroll_view_capi.h"
+#include "include/views/cef_scroll_view.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefScrollViewCToCpp : public CefCToCppRefCounted<CefScrollViewCToCpp,
+                                                       CefScrollView,
+                                                       cef_scroll_view_t> {
+ public:
+  CefScrollViewCToCpp();
+  virtual ~CefScrollViewCToCpp();
+
+  // CefScrollView methods.
+  void SetContentView(CefRefPtr<CefView> view) OVERRIDE;
+  CefRefPtr<CefView> GetContentView() OVERRIDE;
+  CefRect GetVisibleContentRect() OVERRIDE;
+  bool HasHorizontalScrollbar() OVERRIDE;
+  int GetHorizontalScrollbarHeight() OVERRIDE;
+  bool HasVerticalScrollbar() OVERRIDE;
+  int GetVerticalScrollbarWidth() OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_SCROLL_VIEW_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/textfield_ctocpp.cc b/src/libcef_dll/ctocpp/views/textfield_ctocpp.cc
new file mode 100644
index 0000000..a45edd6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/textfield_ctocpp.cc
@@ -0,0 +1,1337 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0f677bbeb351b9d71e00c622971d4593c99b99d5$
+//
+
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefTextfield::CreateTextfield(
+    CefRefPtr<CefTextfieldDelegate> delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  cef_textfield_t* _retval =
+      cef_textfield_create(CefTextfieldDelegateCppToC::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetPasswordInput(bool password_input) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_password_input))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_password_input(_struct, password_input);
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsPasswordInput() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_password_input))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_password_input(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SetReadOnly(bool read_only) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_read_only))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_read_only(_struct, read_only);
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsReadOnly() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_read_only))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_read_only(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefTextfieldCToCpp::GetText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetText(const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return;
+
+  // Execute
+  _struct->set_text(_struct, text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::AppendText(const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, append_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return;
+
+  // Execute
+  _struct->append_text(_struct, text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::InsertOrReplaceText(const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, insert_or_replace_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return;
+
+  // Execute
+  _struct->insert_or_replace_text(_struct, text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::HasSelection() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_selection))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_selection(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefTextfieldCToCpp::GetSelectedText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selected_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_selected_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SelectAll(bool reversed) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, select_all))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->select_all(_struct, reversed);
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::ClearSelection() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear_selection))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->clear_selection(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefRange CefTextfieldCToCpp::GetSelectedRange() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selected_range))
+    return CefRange();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_range_t _retval = _struct->get_selected_range(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SelectRange(const CefRange& range) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, select_range))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->select_range(_struct, &range);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefTextfieldCToCpp::GetCursorPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_cursor_position))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_cursor_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetTextColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_text_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_text_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefTextfieldCToCpp::GetTextColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_text_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_text_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetSelectionTextColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_selection_text_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_selection_text_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_color_t CefTextfieldCToCpp::GetSelectionTextColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selection_text_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_selection_text_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetSelectionBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_selection_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_selection_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_color_t CefTextfieldCToCpp::GetSelectionBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_selection_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_selection_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetFontList(const CefString& font_list) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_font_list))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: font_list; type: string_byref_const
+  DCHECK(!font_list.empty());
+  if (font_list.empty())
+    return;
+
+  // Execute
+  _struct->set_font_list(_struct, font_list.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::ApplyTextColor(cef_color_t color,
+                                        const CefRange& range) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, apply_text_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->apply_text_color(_struct, color, &range);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::ApplyTextStyle(cef_text_style_t style,
+                                        bool add,
+                                        const CefRange& range) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, apply_text_style))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->apply_text_style(_struct, style, add, &range);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::IsCommandEnabled(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_command_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_command_enabled(_struct, command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::ExecuteCommand(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, execute_command))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->execute_command(_struct, command_id);
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::ClearEditHistory() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, clear_edit_history))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->clear_edit_history(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetPlaceholderText(const CefString& text) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_placeholder_text))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return;
+
+  // Execute
+  _struct->set_placeholder_text(_struct, text.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefTextfieldCToCpp::GetPlaceholderText() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_placeholder_text))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_placeholder_text(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetPlaceholderTextColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_placeholder_text_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_placeholder_text_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetAccessibleName(const CefString& name) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_accessible_name))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: name; type: string_byref_const
+  DCHECK(!name.empty());
+  if (name.empty())
+    return;
+
+  // Execute
+  _struct->set_accessible_name(_struct, name.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefTextfieldCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefTextfieldCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefTextfieldCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefTextfieldCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefTextfieldCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefTextfieldCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefTextfieldCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefTextfieldCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefTextfieldCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefTextfieldCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefTextfieldCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefTextfieldCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefTextfieldCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefTextfieldCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefTextfieldCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefTextfieldCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefTextfieldCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefTextfieldCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefTextfieldCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefTextfieldCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefTextfieldCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefTextfieldCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefTextfieldCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefTextfieldCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                            CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                              CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTextfieldCToCpp::CefTextfieldCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTextfieldCToCpp::~CefTextfieldCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_textfield_t*
+CefCToCppRefCounted<CefTextfieldCToCpp, CefTextfield, cef_textfield_t>::
+    UnwrapDerived(CefWrapperType type, CefTextfield* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefTextfieldCToCpp,
+                                   CefTextfield,
+                                   cef_textfield_t>::kWrapperType =
+    WT_TEXTFIELD;
diff --git a/src/libcef_dll/ctocpp/views/textfield_ctocpp.h b/src/libcef_dll/ctocpp/views/textfield_ctocpp.h
new file mode 100644
index 0000000..b132013
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/textfield_ctocpp.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8c3c6f34acb740232874335312c7a28699f32806$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_TEXTFIELD_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_TEXTFIELD_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_textfield_capi.h"
+#include "include/views/cef_textfield.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefTextfieldCToCpp : public CefCToCppRefCounted<CefTextfieldCToCpp,
+                                                      CefTextfield,
+                                                      cef_textfield_t> {
+ public:
+  CefTextfieldCToCpp();
+  virtual ~CefTextfieldCToCpp();
+
+  // CefTextfield methods.
+  void SetPasswordInput(bool password_input) OVERRIDE;
+  bool IsPasswordInput() OVERRIDE;
+  void SetReadOnly(bool read_only) OVERRIDE;
+  bool IsReadOnly() OVERRIDE;
+  CefString GetText() OVERRIDE;
+  void SetText(const CefString& text) OVERRIDE;
+  void AppendText(const CefString& text) OVERRIDE;
+  void InsertOrReplaceText(const CefString& text) OVERRIDE;
+  bool HasSelection() OVERRIDE;
+  CefString GetSelectedText() OVERRIDE;
+  void SelectAll(bool reversed) OVERRIDE;
+  void ClearSelection() OVERRIDE;
+  CefRange GetSelectedRange() OVERRIDE;
+  void SelectRange(const CefRange& range) OVERRIDE;
+  size_t GetCursorPosition() OVERRIDE;
+  void SetTextColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetTextColor() OVERRIDE;
+  void SetSelectionTextColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetSelectionTextColor() OVERRIDE;
+  void SetSelectionBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetSelectionBackgroundColor() OVERRIDE;
+  void SetFontList(const CefString& font_list) OVERRIDE;
+  void ApplyTextColor(cef_color_t color, const CefRange& range) OVERRIDE;
+  void ApplyTextStyle(cef_text_style_t style,
+                      bool add,
+                      const CefRange& range) OVERRIDE;
+  bool IsCommandEnabled(int command_id) OVERRIDE;
+  void ExecuteCommand(int command_id) OVERRIDE;
+  void ClearEditHistory() OVERRIDE;
+  void SetPlaceholderText(const CefString& text) OVERRIDE;
+  CefString GetPlaceholderText() OVERRIDE;
+  void SetPlaceholderTextColor(cef_color_t color) OVERRIDE;
+  void SetAccessibleName(const CefString& name) OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_TEXTFIELD_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/textfield_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/views/textfield_delegate_ctocpp.cc
new file mode 100644
index 0000000..b95da92
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/textfield_delegate_ctocpp.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b2179a4404a247783c97e3926fc2cf19fb1f46c6$
+//
+
+#include "libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/views/textfield_cpptoc.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefTextfieldDelegateCToCpp::OnKeyEvent(CefRefPtr<CefTextfield> textfield,
+                                            const CefKeyEvent& event) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_key_event))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: textfield; type: refptr_diff
+  DCHECK(textfield.get());
+  if (!textfield.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_key_event(
+      _struct, CefTextfieldCppToC::Wrap(textfield), &event);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldDelegateCToCpp::OnAfterUserAction(
+    CefRefPtr<CefTextfield> textfield) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_textfield_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_after_user_action))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: textfield; type: refptr_diff
+  DCHECK(textfield.get());
+  if (!textfield.get())
+    return;
+
+  // Execute
+  _struct->on_after_user_action(_struct, CefTextfieldCppToC::Wrap(textfield));
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefTextfieldDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_preferred_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefTextfieldDelegateCToCpp::GetMinimumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_minimum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefTextfieldDelegateCToCpp::GetMaximumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_maximum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefTextfieldDelegateCToCpp::GetHeightForWidth(CefRefPtr<CefView> view,
+                                                  int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return 0;
+
+  // Execute
+  int _retval =
+      _struct->get_height_for_width(_struct, CefViewCppToC::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldDelegateCToCpp::OnParentViewChanged(
+    CefRefPtr<CefView> view,
+    bool added,
+    CefRefPtr<CefView> parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_parent_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent.get());
+  if (!parent.get())
+    return;
+
+  // Execute
+  _struct->on_parent_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                  CefViewCppToC::Wrap(parent));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldDelegateCToCpp::OnChildViewChanged(CefRefPtr<CefView> view,
+                                                    bool added,
+                                                    CefRefPtr<CefView> child) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_child_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child.get());
+  if (!child.get())
+    return;
+
+  // Execute
+  _struct->on_child_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                 CefViewCppToC::Wrap(child));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldDelegateCToCpp::OnFocus(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_focus(_struct, CefViewCppToC::Wrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefTextfieldDelegateCToCpp::OnBlur(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_blur))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_blur(_struct, CefViewCppToC::Wrap(view));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefTextfieldDelegateCToCpp::CefTextfieldDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefTextfieldDelegateCToCpp::~CefTextfieldDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_textfield_delegate_t* CefCToCppRefCounted<
+    CefTextfieldDelegateCToCpp,
+    CefTextfieldDelegate,
+    cef_textfield_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                             CefTextfieldDelegate* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefTextfieldDelegateCToCpp,
+                                   CefTextfieldDelegate,
+                                   cef_textfield_delegate_t>::kWrapperType =
+    WT_TEXTFIELD_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h b/src/libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h
new file mode 100644
index 0000000..cb9b26e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5945bc681b8f5e31bebe8a22d6f870ed714f0446$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_TEXTFIELD_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_TEXTFIELD_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_textfield_capi.h"
+#include "include/capi/views/cef_textfield_delegate_capi.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_textfield_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefTextfieldDelegateCToCpp
+    : public CefCToCppRefCounted<CefTextfieldDelegateCToCpp,
+                                 CefTextfieldDelegate,
+                                 cef_textfield_delegate_t> {
+ public:
+  CefTextfieldDelegateCToCpp();
+  virtual ~CefTextfieldDelegateCToCpp();
+
+  // CefTextfieldDelegate methods.
+  bool OnKeyEvent(CefRefPtr<CefTextfield> textfield,
+                  const CefKeyEvent& event) override;
+  void OnAfterUserAction(CefRefPtr<CefTextfield> textfield) override;
+
+  // CefViewDelegate methods.
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override;
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override;
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override;
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override;
+  void OnFocus(CefRefPtr<CefView> view) override;
+  void OnBlur(CefRefPtr<CefView> view) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_TEXTFIELD_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/view_ctocpp.cc b/src/libcef_dll/ctocpp/views/view_ctocpp.cc
new file mode 100644
index 0000000..a8ba2c8
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/view_ctocpp.cc
@@ -0,0 +1,851 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2866cdd50d8546d7a9b7e4294aacab628933fc4b$
+//
+
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/label_button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/menu_button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefViewCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefViewCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefViewCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefViewCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefTextfield> CefViewCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefViewCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefViewCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefViewCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefViewCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefViewCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefViewCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefView> CefViewCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefViewCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefViewCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefViewCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefViewCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefViewCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefViewCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefViewCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefViewCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefViewCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefViewCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefViewCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefViewCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefViewCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefViewCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefViewCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefViewCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefViewCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefViewCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefViewCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                       CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefViewCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                         CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefViewCToCpp::CefViewCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefViewCToCpp::~CefViewCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_view_t*
+CefCToCppRefCounted<CefViewCToCpp, CefView, cef_view_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefView* c) {
+  if (type == WT_BROWSER_VIEW) {
+    return reinterpret_cast<cef_view_t*>(
+        CefBrowserViewCToCpp::Unwrap(reinterpret_cast<CefBrowserView*>(c)));
+  }
+  if (type == WT_BUTTON) {
+    return reinterpret_cast<cef_view_t*>(
+        CefButtonCToCpp::Unwrap(reinterpret_cast<CefButton*>(c)));
+  }
+  if (type == WT_LABEL_BUTTON) {
+    return reinterpret_cast<cef_view_t*>(
+        CefLabelButtonCToCpp::Unwrap(reinterpret_cast<CefLabelButton*>(c)));
+  }
+  if (type == WT_MENU_BUTTON) {
+    return reinterpret_cast<cef_view_t*>(
+        CefMenuButtonCToCpp::Unwrap(reinterpret_cast<CefMenuButton*>(c)));
+  }
+  if (type == WT_PANEL) {
+    return reinterpret_cast<cef_view_t*>(
+        CefPanelCToCpp::Unwrap(reinterpret_cast<CefPanel*>(c)));
+  }
+  if (type == WT_SCROLL_VIEW) {
+    return reinterpret_cast<cef_view_t*>(
+        CefScrollViewCToCpp::Unwrap(reinterpret_cast<CefScrollView*>(c)));
+  }
+  if (type == WT_TEXTFIELD) {
+    return reinterpret_cast<cef_view_t*>(
+        CefTextfieldCToCpp::Unwrap(reinterpret_cast<CefTextfield*>(c)));
+  }
+  if (type == WT_WINDOW) {
+    return reinterpret_cast<cef_view_t*>(
+        CefWindowCToCpp::Unwrap(reinterpret_cast<CefWindow*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefViewCToCpp, CefView, cef_view_t>::kWrapperType =
+        WT_VIEW;
diff --git a/src/libcef_dll/ctocpp/views/view_ctocpp.h b/src/libcef_dll/ctocpp/views/view_ctocpp.h
new file mode 100644
index 0000000..38e0c2a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/view_ctocpp.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=993cdec1a5c9c78f9c11eed22cd692139fb00d92$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_VIEW_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_VIEW_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/views/cef_browser_view_capi.h"
+#include "include/capi/views/cef_button_capi.h"
+#include "include/capi/views/cef_panel_capi.h"
+#include "include/capi/views/cef_scroll_view_capi.h"
+#include "include/capi/views/cef_textfield_capi.h"
+#include "include/capi/views/cef_view_capi.h"
+#include "include/capi/views/cef_window_capi.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_scroll_view.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_view.h"
+#include "include/views/cef_window.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefViewCToCpp
+    : public CefCToCppRefCounted<CefViewCToCpp, CefView, cef_view_t> {
+ public:
+  CefViewCToCpp();
+  virtual ~CefViewCToCpp();
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_VIEW_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/view_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/views/view_delegate_ctocpp.cc
new file mode 100644
index 0000000..021aa8e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/view_delegate_ctocpp.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7303b34e865e03fae0c070d9df8157f9b343fe7c$
+//
+
+#include "libcef_dll/ctocpp/views/view_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h"
+#include "libcef_dll/ctocpp/views/window_delegate_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefSize CefViewDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_preferred_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefViewDelegateCToCpp::GetMinimumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_minimum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefViewDelegateCToCpp::GetMaximumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_maximum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefViewDelegateCToCpp::GetHeightForWidth(CefRefPtr<CefView> view,
+                                             int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return 0;
+
+  // Execute
+  int _retval =
+      _struct->get_height_for_width(_struct, CefViewCppToC::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefViewDelegateCToCpp::OnParentViewChanged(CefRefPtr<CefView> view,
+                                                bool added,
+                                                CefRefPtr<CefView> parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_parent_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent.get());
+  if (!parent.get())
+    return;
+
+  // Execute
+  _struct->on_parent_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                  CefViewCppToC::Wrap(parent));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefViewDelegateCToCpp::OnChildViewChanged(CefRefPtr<CefView> view,
+                                               bool added,
+                                               CefRefPtr<CefView> child) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_child_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child.get());
+  if (!child.get())
+    return;
+
+  // Execute
+  _struct->on_child_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                 CefViewCppToC::Wrap(child));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefViewDelegateCToCpp::OnFocus(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_focus(_struct, CefViewCppToC::Wrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefViewDelegateCToCpp::OnBlur(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_blur))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_blur(_struct, CefViewCppToC::Wrap(view));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefViewDelegateCToCpp::CefViewDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefViewDelegateCToCpp::~CefViewDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_view_delegate_t*
+CefCToCppRefCounted<CefViewDelegateCToCpp,
+                    CefViewDelegate,
+                    cef_view_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefViewDelegate* c) {
+  if (type == WT_BROWSER_VIEW_DELEGATE) {
+    return reinterpret_cast<cef_view_delegate_t*>(
+        CefBrowserViewDelegateCToCpp::Unwrap(
+            reinterpret_cast<CefBrowserViewDelegate*>(c)));
+  }
+  if (type == WT_BUTTON_DELEGATE) {
+    return reinterpret_cast<cef_view_delegate_t*>(
+        CefButtonDelegateCToCpp::Unwrap(
+            reinterpret_cast<CefButtonDelegate*>(c)));
+  }
+  if (type == WT_MENU_BUTTON_DELEGATE) {
+    return reinterpret_cast<cef_view_delegate_t*>(
+        CefMenuButtonDelegateCToCpp::Unwrap(
+            reinterpret_cast<CefMenuButtonDelegate*>(c)));
+  }
+  if (type == WT_PANEL_DELEGATE) {
+    return reinterpret_cast<cef_view_delegate_t*>(
+        CefPanelDelegateCToCpp::Unwrap(reinterpret_cast<CefPanelDelegate*>(c)));
+  }
+  if (type == WT_TEXTFIELD_DELEGATE) {
+    return reinterpret_cast<cef_view_delegate_t*>(
+        CefTextfieldDelegateCToCpp::Unwrap(
+            reinterpret_cast<CefTextfieldDelegate*>(c)));
+  }
+  if (type == WT_WINDOW_DELEGATE) {
+    return reinterpret_cast<cef_view_delegate_t*>(
+        CefWindowDelegateCToCpp::Unwrap(
+            reinterpret_cast<CefWindowDelegate*>(c)));
+  }
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefViewDelegateCToCpp,
+                                   CefViewDelegate,
+                                   cef_view_delegate_t>::kWrapperType =
+    WT_VIEW_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/views/view_delegate_ctocpp.h b/src/libcef_dll/ctocpp/views/view_delegate_ctocpp.h
new file mode 100644
index 0000000..d7a87ce
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/view_delegate_ctocpp.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=50285c0d5d83ec0de6bb0542a2c2b32517217b4e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_VIEW_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_VIEW_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_view_capi.h"
+#include "include/capi/views/cef_view_delegate_capi.h"
+#include "include/views/cef_view.h"
+#include "include/views/cef_view_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefViewDelegateCToCpp : public CefCToCppRefCounted<CefViewDelegateCToCpp,
+                                                         CefViewDelegate,
+                                                         cef_view_delegate_t> {
+ public:
+  CefViewDelegateCToCpp();
+  virtual ~CefViewDelegateCToCpp();
+
+  // CefViewDelegate methods.
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override;
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override;
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override;
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override;
+  void OnFocus(CefRefPtr<CefView> view) override;
+  void OnBlur(CefRefPtr<CefView> view) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_VIEW_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/window_ctocpp.cc b/src/libcef_dll/ctocpp/views/window_ctocpp.cc
new file mode 100644
index 0000000..79bd058
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/window_ctocpp.cc
@@ -0,0 +1,1621 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=50c86d019c16b2efb58ea99ca3f763eaaf297397$
+//
+
+#include "libcef_dll/ctocpp/views/window_ctocpp.h"
+#include "libcef_dll/cpptoc/views/view_delegate_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_delegate_cpptoc.h"
+#include "libcef_dll/ctocpp/image_ctocpp.h"
+#include "libcef_dll/ctocpp/menu_model_ctocpp.h"
+#include "libcef_dll/ctocpp/views/box_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/browser_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/button_ctocpp.h"
+#include "libcef_dll/ctocpp/views/display_ctocpp.h"
+#include "libcef_dll/ctocpp/views/fill_layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/layout_ctocpp.h"
+#include "libcef_dll/ctocpp/views/panel_ctocpp.h"
+#include "libcef_dll/ctocpp/views/scroll_view_ctocpp.h"
+#include "libcef_dll/ctocpp/views/textfield_ctocpp.h"
+#include "libcef_dll/ctocpp/views/view_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefWindow> CefWindow::CreateTopLevelWindow(
+    CefRefPtr<CefWindowDelegate> delegate) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: delegate
+
+  // Execute
+  cef_window_t* _retval =
+      cef_window_create_top_level(CefWindowDelegateCppToC::Wrap(delegate));
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Show() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, show))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->show(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Hide() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, hide))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->hide(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::CenterWindow(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, center_window))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->center_window(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Close() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->close(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsClosed() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_closed))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_closed(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Activate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, activate))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->activate(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Deactivate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, deactivate))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->deactivate(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsActive() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_active))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_active(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::BringToTop() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, bring_to_top))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->bring_to_top(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetAlwaysOnTop(bool on_top) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_always_on_top))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_always_on_top(_struct, on_top);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsAlwaysOnTop() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_always_on_top))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_always_on_top(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Maximize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, maximize))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->maximize(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Minimize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, minimize))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->minimize(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Restore() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, restore))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->restore(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetFullscreen(bool fullscreen) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_fullscreen))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_fullscreen(_struct, fullscreen);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsMaximized() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_maximized))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_maximized(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsMinimized() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_minimized))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_minimized(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsFullscreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_fullscreen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_fullscreen(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetTitle(const CefString& title) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_title))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: title
+
+  // Execute
+  _struct->set_title(_struct, title.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall") CefString CefWindowCToCpp::GetTitle() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_title))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_title(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetWindowIcon(CefRefPtr<CefImage> image) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_window_icon))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: image; type: refptr_same
+  DCHECK(image.get());
+  if (!image.get())
+    return;
+
+  // Execute
+  _struct->set_window_icon(_struct, CefImageCToCpp::Unwrap(image));
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefImage> CefWindowCToCpp::GetWindowIcon() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_window_icon))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_image_t* _retval = _struct->get_window_icon(_struct);
+
+  // Return type: refptr_same
+  return CefImageCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetWindowAppIcon(CefRefPtr<CefImage> image) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_window_app_icon))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: image; type: refptr_same
+  DCHECK(image.get());
+  if (!image.get())
+    return;
+
+  // Execute
+  _struct->set_window_app_icon(_struct, CefImageCToCpp::Unwrap(image));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefImage> CefWindowCToCpp::GetWindowAppIcon() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_window_app_icon))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_image_t* _retval = _struct->get_window_app_icon(_struct);
+
+  // Return type: refptr_same
+  return CefImageCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                               const CefPoint& screen_point,
+                               cef_menu_anchor_position_t anchor_position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, show_menu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: menu_model; type: refptr_same
+  DCHECK(menu_model.get());
+  if (!menu_model.get())
+    return;
+
+  // Execute
+  _struct->show_menu(_struct, CefMenuModelCToCpp::Unwrap(menu_model),
+                     &screen_point, anchor_position);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::CancelMenu() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, cancel_menu))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->cancel_menu(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDisplay> CefWindowCToCpp::GetDisplay() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_display))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_display_t* _retval = _struct->get_display(_struct);
+
+  // Return type: refptr_same
+  return CefDisplayCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRect CefWindowCToCpp::GetClientAreaBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_client_area_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_client_area_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_draggable_regions))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: regions
+
+  // Translate param: regions; type: simple_vec_byref_const
+  const size_t regionsCount = regions.size();
+  cef_draggable_region_t* regionsList = NULL;
+  if (regionsCount > 0) {
+    regionsList = new cef_draggable_region_t[regionsCount];
+    DCHECK(regionsList);
+    if (regionsList) {
+      for (size_t i = 0; i < regionsCount; ++i) {
+        regionsList[i] = regions[i];
+      }
+    }
+  }
+
+  // Execute
+  _struct->set_draggable_regions(_struct, regionsCount, regionsList);
+
+  // Restore param:regions; type: simple_vec_byref_const
+  if (regionsList)
+    delete[] regionsList;
+}
+
+NO_SANITIZE("cfi-icall") CefWindowHandle CefWindowCToCpp::GetWindowHandle() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_window_handle))
+    return kNullWindowHandle;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_handle_t _retval = _struct->get_window_handle(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SendKeyPress(int key_code, uint32 event_flags) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_key_press))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_key_press(_struct, key_code, event_flags);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SendMouseMove(int screen_x, int screen_y) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_mouse_move))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_mouse_move(_struct, screen_x, screen_y);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SendMouseEvents(cef_mouse_button_type_t button,
+                                      bool mouse_down,
+                                      bool mouse_up) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, send_mouse_events))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->send_mouse_events(_struct, button, mouse_down, mouse_up);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetAccelerator(int command_id,
+                                     int key_code,
+                                     bool shift_pressed,
+                                     bool ctrl_pressed,
+                                     bool alt_pressed) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, set_accelerator))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_accelerator(_struct, command_id, key_code, shift_pressed,
+                           ctrl_pressed, alt_pressed);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::RemoveAccelerator(int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_accelerator))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->remove_accelerator(_struct, command_id);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::RemoveAllAccelerators() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, remove_all_accelerators))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->remove_all_accelerators(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefWindowCToCpp::AsWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->as_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefFillLayout> CefWindowCToCpp::SetToFillLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_to_fill_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_fill_layout_t* _retval = _struct->set_to_fill_layout(_struct);
+
+  // Return type: refptr_same
+  return CefFillLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBoxLayout> CefWindowCToCpp::SetToBoxLayout(
+    const CefBoxLayoutSettings& settings) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_to_box_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_box_layout_t* _retval = _struct->set_to_box_layout(_struct, &settings);
+
+  // Return type: refptr_same
+  return CefBoxLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefLayout> CefWindowCToCpp::GetLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_layout))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_layout_t* _retval = _struct->get_layout(_struct);
+
+  // Return type: refptr_same
+  return CefLayoutCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::Layout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::AddChildView(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, add_child_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->add_child_view(_struct, CefViewCToCpp::Unwrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::AddChildViewAt(CefRefPtr<CefView> view, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, add_child_view_at))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return;
+
+  // Execute
+  _struct->add_child_view_at(_struct, CefViewCToCpp::Unwrap(view), index);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::ReorderChildView(CefRefPtr<CefView> view, int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, reorder_child_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->reorder_child_view(_struct, CefViewCToCpp::Unwrap(view), index);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::RemoveChildView(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, remove_child_view))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->remove_child_view(_struct, CefViewCToCpp::Unwrap(view));
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::RemoveAllChildViews() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, remove_all_child_views))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->remove_all_child_views(_struct);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefWindowCToCpp::GetChildViewCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_child_view_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_child_view_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefWindowCToCpp::GetChildViewAt(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_panel_t* _struct = reinterpret_cast<cef_panel_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_child_view_at))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return nullptr;
+
+  // Execute
+  cef_view_t* _retval = _struct->get_child_view_at(_struct, index);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefWindowCToCpp::AsBrowserView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_browser_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_browser_view_t* _retval = _struct->as_browser_view(_struct);
+
+  // Return type: refptr_same
+  return CefBrowserViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefButton> CefWindowCToCpp::AsButton() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_button))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_button_t* _retval = _struct->as_button(_struct);
+
+  // Return type: refptr_same
+  return CefButtonCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefPanel> CefWindowCToCpp::AsPanel() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_panel))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_panel_t* _retval = _struct->as_panel(_struct);
+
+  // Return type: refptr_same
+  return CefPanelCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefWindowCToCpp::AsScrollView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_scroll_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_scroll_view_t* _retval = _struct->as_scroll_view(_struct);
+
+  // Return type: refptr_same
+  return CefScrollViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefWindowCToCpp::AsTextfield() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, as_textfield))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_textfield_t* _retval = _struct->as_textfield(_struct);
+
+  // Return type: refptr_same
+  return CefTextfieldCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefString CefWindowCToCpp::GetTypeString() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_type_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_type_string(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefWindowCToCpp::ToString(bool include_children) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, to_string))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->to_string(_struct, include_children);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsValid() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_valid))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_valid(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsAttached() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_attached))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_attached(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsSame(CefRefPtr<CefView> that) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_same))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: that; type: refptr_same
+  DCHECK(that.get());
+  if (!that.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_same(_struct, CefViewCToCpp::Unwrap(that));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefViewDelegate> CefWindowCToCpp::GetDelegate() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_delegate))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_delegate_t* _retval = _struct->get_delegate(_struct);
+
+  // Return type: refptr_diff
+  return CefViewDelegateCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefWindow> CefWindowCToCpp::GetWindow() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_window_t* _retval = _struct->get_window(_struct);
+
+  // Return type: refptr_same
+  return CefWindowCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") int CefWindowCToCpp::GetID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_id(_struct, id);
+}
+
+NO_SANITIZE("cfi-icall") int CefWindowCToCpp::GetGroupID() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_group_id))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_group_id(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetGroupID(int group_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_group_id))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_group_id(_struct, group_id);
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefView> CefWindowCToCpp::GetParentView() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_parent_view))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_parent_view(_struct);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefView> CefWindowCToCpp::GetViewForID(int id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_view_for_id))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_view_t* _retval = _struct->get_view_for_id(_struct, id);
+
+  // Return type: refptr_same
+  return CefViewCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetBounds(const CefRect& bounds) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_bounds))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_bounds(_struct, &bounds);
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefWindowCToCpp::GetBounds() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefRect CefWindowCToCpp::GetBoundsInScreen() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_bounds_in_screen))
+    return CefRect();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_rect_t _retval = _struct->get_bounds_in_screen(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetSize(const CefSize& size) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_size(_struct, &size);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefWindowCToCpp::GetSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetPosition(const CefPoint& position) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_position))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_position(_struct, &position);
+}
+
+NO_SANITIZE("cfi-icall") CefPoint CefWindowCToCpp::GetPosition() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_position))
+    return CefPoint();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_point_t _retval = _struct->get_position(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefWindowCToCpp::GetPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_preferred_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SizeToPreferredSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, size_to_preferred_size))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->size_to_preferred_size(_struct);
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefWindowCToCpp::GetMinimumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_minimum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefSize CefWindowCToCpp::GetMaximumSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_size_t _retval = _struct->get_maximum_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefWindowCToCpp::GetHeightForWidth(int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_height_for_width(_struct, width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::InvalidateLayout() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, invalidate_layout))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->invalidate_layout(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetVisible(bool visible) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_visible))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_visible(_struct, visible);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsVisible() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_visible))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_visible(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsDrawn() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_drawn))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_drawn(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetEnabled(bool enabled) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_enabled))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_enabled(_struct, enabled);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsEnabled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_enabled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_enabled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::SetFocusable(bool focusable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_focusable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_focusable(_struct, focusable);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWindowCToCpp::IsAccessibilityFocusable() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, is_accessibility_focusable))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_accessibility_focusable(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefWindowCToCpp::RequestFocus() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, request_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->request_focus(_struct);
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowCToCpp::SetBackgroundColor(cef_color_t color) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, set_background_color))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->set_background_color(_struct, color);
+}
+
+NO_SANITIZE("cfi-icall") cef_color_t CefWindowCToCpp::GetBackgroundColor() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_background_color))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_color_t _retval = _struct->get_background_color(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowCToCpp::ConvertPointToScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowCToCpp::ConvertPointFromScreen(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_screen))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_screen(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowCToCpp::ConvertPointToWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_to_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowCToCpp::ConvertPointFromWindow(CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_window))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->convert_point_from_window(_struct, &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowCToCpp::ConvertPointToView(CefRefPtr<CefView> view,
+                                         CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_to_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_to_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowCToCpp::ConvertPointFromView(CefRefPtr<CefView> view,
+                                           CefPoint& point) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_t* _struct = reinterpret_cast<cef_view_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, convert_point_from_view))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_same
+  DCHECK(view.get());
+  if (!view.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->convert_point_from_view(
+      _struct, CefViewCToCpp::Unwrap(view), &point);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWindowCToCpp::CefWindowCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWindowCToCpp::~CefWindowCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_window_t*
+CefCToCppRefCounted<CefWindowCToCpp, CefWindow, cef_window_t>::UnwrapDerived(
+    CefWrapperType type,
+    CefWindow* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefWindowCToCpp, CefWindow, cef_window_t>::
+    kWrapperType = WT_WINDOW;
diff --git a/src/libcef_dll/ctocpp/views/window_ctocpp.h b/src/libcef_dll/ctocpp/views/window_ctocpp.h
new file mode 100644
index 0000000..6d0ec71
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/window_ctocpp.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=2206227226ce95ce062425eda932fa88af151b35$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_WINDOW_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_WINDOW_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/views/cef_window_capi.h"
+#include "include/views/cef_window.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefWindowCToCpp
+    : public CefCToCppRefCounted<CefWindowCToCpp, CefWindow, cef_window_t> {
+ public:
+  CefWindowCToCpp();
+  virtual ~CefWindowCToCpp();
+
+  // CefWindow methods.
+  void Show() OVERRIDE;
+  void Hide() OVERRIDE;
+  void CenterWindow(const CefSize& size) OVERRIDE;
+  void Close() OVERRIDE;
+  bool IsClosed() OVERRIDE;
+  void Activate() OVERRIDE;
+  void Deactivate() OVERRIDE;
+  bool IsActive() OVERRIDE;
+  void BringToTop() OVERRIDE;
+  void SetAlwaysOnTop(bool on_top) OVERRIDE;
+  bool IsAlwaysOnTop() OVERRIDE;
+  void Maximize() OVERRIDE;
+  void Minimize() OVERRIDE;
+  void Restore() OVERRIDE;
+  void SetFullscreen(bool fullscreen) OVERRIDE;
+  bool IsMaximized() OVERRIDE;
+  bool IsMinimized() OVERRIDE;
+  bool IsFullscreen() OVERRIDE;
+  void SetTitle(const CefString& title) OVERRIDE;
+  CefString GetTitle() OVERRIDE;
+  void SetWindowIcon(CefRefPtr<CefImage> image) OVERRIDE;
+  CefRefPtr<CefImage> GetWindowIcon() OVERRIDE;
+  void SetWindowAppIcon(CefRefPtr<CefImage> image) OVERRIDE;
+  CefRefPtr<CefImage> GetWindowAppIcon() OVERRIDE;
+  void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
+                const CefPoint& screen_point,
+                cef_menu_anchor_position_t anchor_position) OVERRIDE;
+  void CancelMenu() OVERRIDE;
+  CefRefPtr<CefDisplay> GetDisplay() OVERRIDE;
+  CefRect GetClientAreaBoundsInScreen() OVERRIDE;
+  void SetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) OVERRIDE;
+  CefWindowHandle GetWindowHandle() OVERRIDE;
+  void SendKeyPress(int key_code, uint32 event_flags) OVERRIDE;
+  void SendMouseMove(int screen_x, int screen_y) OVERRIDE;
+  void SendMouseEvents(cef_mouse_button_type_t button,
+                       bool mouse_down,
+                       bool mouse_up) OVERRIDE;
+  void SetAccelerator(int command_id,
+                      int key_code,
+                      bool shift_pressed,
+                      bool ctrl_pressed,
+                      bool alt_pressed) OVERRIDE;
+  void RemoveAccelerator(int command_id) OVERRIDE;
+  void RemoveAllAccelerators() OVERRIDE;
+
+  // CefPanel methods.
+  CefRefPtr<CefWindow> AsWindow() OVERRIDE;
+  CefRefPtr<CefFillLayout> SetToFillLayout() OVERRIDE;
+  CefRefPtr<CefBoxLayout> SetToBoxLayout(
+      const CefBoxLayoutSettings& settings) OVERRIDE;
+  CefRefPtr<CefLayout> GetLayout() OVERRIDE;
+  void Layout() OVERRIDE;
+  void AddChildView(CefRefPtr<CefView> view) OVERRIDE;
+  void AddChildViewAt(CefRefPtr<CefView> view, int index) OVERRIDE;
+  void ReorderChildView(CefRefPtr<CefView> view, int index) OVERRIDE;
+  void RemoveChildView(CefRefPtr<CefView> view) OVERRIDE;
+  void RemoveAllChildViews() OVERRIDE;
+  size_t GetChildViewCount() OVERRIDE;
+  CefRefPtr<CefView> GetChildViewAt(int index) OVERRIDE;
+
+  // CefView methods.
+  CefRefPtr<CefBrowserView> AsBrowserView() OVERRIDE;
+  CefRefPtr<CefButton> AsButton() OVERRIDE;
+  CefRefPtr<CefPanel> AsPanel() OVERRIDE;
+  CefRefPtr<CefScrollView> AsScrollView() OVERRIDE;
+  CefRefPtr<CefTextfield> AsTextfield() OVERRIDE;
+  CefString GetTypeString() OVERRIDE;
+  CefString ToString(bool include_children) OVERRIDE;
+  bool IsValid() OVERRIDE;
+  bool IsAttached() OVERRIDE;
+  bool IsSame(CefRefPtr<CefView> that) OVERRIDE;
+  CefRefPtr<CefViewDelegate> GetDelegate() OVERRIDE;
+  CefRefPtr<CefWindow> GetWindow() OVERRIDE;
+  int GetID() OVERRIDE;
+  void SetID(int id) OVERRIDE;
+  int GetGroupID() OVERRIDE;
+  void SetGroupID(int group_id) OVERRIDE;
+  CefRefPtr<CefView> GetParentView() OVERRIDE;
+  CefRefPtr<CefView> GetViewForID(int id) OVERRIDE;
+  void SetBounds(const CefRect& bounds) OVERRIDE;
+  CefRect GetBounds() OVERRIDE;
+  CefRect GetBoundsInScreen() OVERRIDE;
+  void SetSize(const CefSize& size) OVERRIDE;
+  CefSize GetSize() OVERRIDE;
+  void SetPosition(const CefPoint& position) OVERRIDE;
+  CefPoint GetPosition() OVERRIDE;
+  CefSize GetPreferredSize() OVERRIDE;
+  void SizeToPreferredSize() OVERRIDE;
+  CefSize GetMinimumSize() OVERRIDE;
+  CefSize GetMaximumSize() OVERRIDE;
+  int GetHeightForWidth(int width) OVERRIDE;
+  void InvalidateLayout() OVERRIDE;
+  void SetVisible(bool visible) OVERRIDE;
+  bool IsVisible() OVERRIDE;
+  bool IsDrawn() OVERRIDE;
+  void SetEnabled(bool enabled) OVERRIDE;
+  bool IsEnabled() OVERRIDE;
+  void SetFocusable(bool focusable) OVERRIDE;
+  bool IsFocusable() OVERRIDE;
+  bool IsAccessibilityFocusable() OVERRIDE;
+  void RequestFocus() OVERRIDE;
+  void SetBackgroundColor(cef_color_t color) OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool ConvertPointToScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromScreen(CefPoint& point) OVERRIDE;
+  bool ConvertPointToWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointFromWindow(CefPoint& point) OVERRIDE;
+  bool ConvertPointToView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+  bool ConvertPointFromView(CefRefPtr<CefView> view, CefPoint& point) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_WINDOW_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/views/window_delegate_ctocpp.cc b/src/libcef_dll/ctocpp/views/window_delegate_ctocpp.cc
new file mode 100644
index 0000000..833ced7
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/window_delegate_ctocpp.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=42f9fdcc49577f6d052e4f357138efe4443a72d9$
+//
+
+#include "libcef_dll/ctocpp/views/window_delegate_ctocpp.h"
+#include "libcef_dll/cpptoc/views/view_cpptoc.h"
+#include "libcef_dll/cpptoc/views/window_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefWindowDelegateCToCpp::OnWindowCreated(CefRefPtr<CefWindow> window) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_window_created))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return;
+
+  // Execute
+  _struct->on_window_created(_struct, CefWindowCppToC::Wrap(window));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowDelegateCToCpp::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_window_destroyed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return;
+
+  // Execute
+  _struct->on_window_destroyed(_struct, CefWindowCppToC::Wrap(window));
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefWindow> CefWindowDelegateCToCpp::GetParentWindow(
+    CefRefPtr<CefWindow> window,
+    bool* is_menu,
+    bool* can_activate_menu) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_parent_window))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return nullptr;
+  // Verify param: is_menu; type: bool_byaddr
+  DCHECK(is_menu);
+  if (!is_menu)
+    return nullptr;
+  // Verify param: can_activate_menu; type: bool_byaddr
+  DCHECK(can_activate_menu);
+  if (!can_activate_menu)
+    return nullptr;
+
+  // Translate param: is_menu; type: bool_byaddr
+  int is_menuInt = is_menu ? *is_menu : 0;
+  // Translate param: can_activate_menu; type: bool_byaddr
+  int can_activate_menuInt = can_activate_menu ? *can_activate_menu : 0;
+
+  // Execute
+  cef_window_t* _retval =
+      _struct->get_parent_window(_struct, CefWindowCppToC::Wrap(window),
+                                 &is_menuInt, &can_activate_menuInt);
+
+  // Restore param:is_menu; type: bool_byaddr
+  if (is_menu)
+    *is_menu = is_menuInt ? true : false;
+  // Restore param:can_activate_menu; type: bool_byaddr
+  if (can_activate_menu)
+    *can_activate_menu = can_activate_menuInt ? true : false;
+
+  // Return type: refptr_diff
+  return CefWindowCppToC::Unwrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowDelegateCToCpp::IsFrameless(CefRefPtr<CefWindow> window) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_frameless))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->is_frameless(_struct, CefWindowCppToC::Wrap(window));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowDelegateCToCpp::CanResize(CefRefPtr<CefWindow> window) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_resize))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->can_resize(_struct, CefWindowCppToC::Wrap(window));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowDelegateCToCpp::CanMaximize(CefRefPtr<CefWindow> window) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_maximize))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->can_maximize(_struct, CefWindowCppToC::Wrap(window));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowDelegateCToCpp::CanMinimize(CefRefPtr<CefWindow> window) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_minimize))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->can_minimize(_struct, CefWindowCppToC::Wrap(window));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowDelegateCToCpp::CanClose(CefRefPtr<CefWindow> window) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, can_close))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->can_close(_struct, CefWindowCppToC::Wrap(window));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowDelegateCToCpp::OnAccelerator(CefRefPtr<CefWindow> window,
+                                            int command_id) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_accelerator))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return false;
+
+  // Execute
+  int _retval = _struct->on_accelerator(_struct, CefWindowCppToC::Wrap(window),
+                                        command_id);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefWindowDelegateCToCpp::OnKeyEvent(CefRefPtr<CefWindow> window,
+                                         const CefKeyEvent& event) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_window_delegate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, on_key_event))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: window; type: refptr_diff
+  DCHECK(window.get());
+  if (!window.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->on_key_event(_struct, CefWindowCppToC::Wrap(window), &event);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefWindowDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_preferred_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_preferred_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefWindowDelegateCToCpp::GetMinimumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_minimum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_minimum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefSize CefWindowDelegateCToCpp::GetMaximumSize(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_maximum_size))
+    return CefSize();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return CefSize();
+
+  // Execute
+  cef_size_t _retval =
+      _struct->get_maximum_size(_struct, CefViewCppToC::Wrap(view));
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefWindowDelegateCToCpp::GetHeightForWidth(CefRefPtr<CefView> view,
+                                               int width) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, get_height_for_width))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return 0;
+
+  // Execute
+  int _retval =
+      _struct->get_height_for_width(_struct, CefViewCppToC::Wrap(view), width);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowDelegateCToCpp::OnParentViewChanged(CefRefPtr<CefView> view,
+                                                  bool added,
+                                                  CefRefPtr<CefView> parent) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_parent_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: parent; type: refptr_diff
+  DCHECK(parent.get());
+  if (!parent.get())
+    return;
+
+  // Execute
+  _struct->on_parent_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                  CefViewCppToC::Wrap(parent));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowDelegateCToCpp::OnChildViewChanged(CefRefPtr<CefView> view,
+                                                 bool added,
+                                                 CefRefPtr<CefView> child) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_child_view_changed))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+  // Verify param: child; type: refptr_diff
+  DCHECK(child.get());
+  if (!child.get())
+    return;
+
+  // Execute
+  _struct->on_child_view_changed(_struct, CefViewCppToC::Wrap(view), added,
+                                 CefViewCppToC::Wrap(child));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowDelegateCToCpp::OnFocus(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_focus))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_focus(_struct, CefViewCppToC::Wrap(view));
+}
+
+NO_SANITIZE("cfi-icall")
+void CefWindowDelegateCToCpp::OnBlur(CefRefPtr<CefView> view) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_view_delegate_t* _struct =
+      reinterpret_cast<cef_view_delegate_t*>(GetStruct());
+  if (CEF_MEMBER_MISSING(_struct, on_blur))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: view; type: refptr_diff
+  DCHECK(view.get());
+  if (!view.get())
+    return;
+
+  // Execute
+  _struct->on_blur(_struct, CefViewCppToC::Wrap(view));
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWindowDelegateCToCpp::CefWindowDelegateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWindowDelegateCToCpp::~CefWindowDelegateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_window_delegate_t* CefCToCppRefCounted<
+    CefWindowDelegateCToCpp,
+    CefWindowDelegate,
+    cef_window_delegate_t>::UnwrapDerived(CefWrapperType type,
+                                          CefWindowDelegate* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefWindowDelegateCToCpp,
+                                   CefWindowDelegate,
+                                   cef_window_delegate_t>::kWrapperType =
+    WT_WINDOW_DELEGATE;
diff --git a/src/libcef_dll/ctocpp/views/window_delegate_ctocpp.h b/src/libcef_dll/ctocpp/views/window_delegate_ctocpp.h
new file mode 100644
index 0000000..6ed7b39
--- /dev/null
+++ b/src/libcef_dll/ctocpp/views/window_delegate_ctocpp.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=e1c4def5dc9c50b90a1a57bb7769f18763827b25$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_WINDOW_DELEGATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_VIEWS_WINDOW_DELEGATE_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/views/cef_window_capi.h"
+#include "include/capi/views/cef_window_delegate_capi.h"
+#include "include/views/cef_window.h"
+#include "include/views/cef_window_delegate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefWindowDelegateCToCpp
+    : public CefCToCppRefCounted<CefWindowDelegateCToCpp,
+                                 CefWindowDelegate,
+                                 cef_window_delegate_t> {
+ public:
+  CefWindowDelegateCToCpp();
+  virtual ~CefWindowDelegateCToCpp();
+
+  // CefWindowDelegate methods.
+  void OnWindowCreated(CefRefPtr<CefWindow> window) override;
+  void OnWindowDestroyed(CefRefPtr<CefWindow> window) override;
+  CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
+                                       bool* is_menu,
+                                       bool* can_activate_menu) override;
+  bool IsFrameless(CefRefPtr<CefWindow> window) override;
+  bool CanResize(CefRefPtr<CefWindow> window) override;
+  bool CanMaximize(CefRefPtr<CefWindow> window) override;
+  bool CanMinimize(CefRefPtr<CefWindow> window) override;
+  bool CanClose(CefRefPtr<CefWindow> window) override;
+  bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) override;
+  bool OnKeyEvent(CefRefPtr<CefWindow> window,
+                  const CefKeyEvent& event) override;
+
+  // CefPanelDelegate methods.
+
+  // CefViewDelegate methods.
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override;
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override;
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override;
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override;
+  void OnFocus(CefRefPtr<CefView> view) override;
+  void OnBlur(CefRefPtr<CefView> view) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_VIEWS_WINDOW_DELEGATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/waitable_event_ctocpp.cc b/src/libcef_dll/ctocpp/waitable_event_ctocpp.cc
new file mode 100644
index 0000000..3c98086
--- /dev/null
+++ b/src/libcef_dll/ctocpp/waitable_event_ctocpp.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=3a22edd7a71ffaa44bb7215dcb7c7c0df8dd6a23$
+//
+
+#include "libcef_dll/ctocpp/waitable_event_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefWaitableEvent> CefWaitableEvent::CreateWaitableEvent(
+    bool automatic_reset,
+    bool initially_signaled) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_waitable_event_t* _retval =
+      cef_waitable_event_create(automatic_reset, initially_signaled);
+
+  // Return type: refptr_same
+  return CefWaitableEventCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") void CefWaitableEventCToCpp::Reset() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_waitable_event_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, reset))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->reset(_struct);
+}
+
+NO_SANITIZE("cfi-icall") void CefWaitableEventCToCpp::Signal() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_waitable_event_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, signal))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->signal(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWaitableEventCToCpp::IsSignaled() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_waitable_event_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_signaled))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_signaled(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") void CefWaitableEventCToCpp::Wait() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_waitable_event_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, wait))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  _struct->wait(_struct);
+}
+
+NO_SANITIZE("cfi-icall") bool CefWaitableEventCToCpp::TimedWait(int64 max_ms) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_waitable_event_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, timed_wait))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->timed_wait(_struct, max_ms);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWaitableEventCToCpp::CefWaitableEventCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWaitableEventCToCpp::~CefWaitableEventCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_waitable_event_t*
+CefCToCppRefCounted<CefWaitableEventCToCpp,
+                    CefWaitableEvent,
+                    cef_waitable_event_t>::UnwrapDerived(CefWrapperType type,
+                                                         CefWaitableEvent* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefWaitableEventCToCpp,
+                                   CefWaitableEvent,
+                                   cef_waitable_event_t>::kWrapperType =
+    WT_WAITABLE_EVENT;
diff --git a/src/libcef_dll/ctocpp/waitable_event_ctocpp.h b/src/libcef_dll/ctocpp/waitable_event_ctocpp.h
new file mode 100644
index 0000000..a735e61
--- /dev/null
+++ b/src/libcef_dll/ctocpp/waitable_event_ctocpp.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=42d88a6d0562330e918279cb11c29ab6b5a848bf$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_WAITABLE_EVENT_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_WAITABLE_EVENT_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_waitable_event_capi.h"
+#include "include/cef_waitable_event.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefWaitableEventCToCpp
+    : public CefCToCppRefCounted<CefWaitableEventCToCpp,
+                                 CefWaitableEvent,
+                                 cef_waitable_event_t> {
+ public:
+  CefWaitableEventCToCpp();
+  virtual ~CefWaitableEventCToCpp();
+
+  // CefWaitableEvent methods.
+  void Reset() OVERRIDE;
+  void Signal() OVERRIDE;
+  bool IsSignaled() OVERRIDE;
+  void Wait() OVERRIDE;
+  bool TimedWait(int64 max_ms) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_WAITABLE_EVENT_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/web_plugin_info_ctocpp.cc b/src/libcef_dll/ctocpp/web_plugin_info_ctocpp.cc
new file mode 100644
index 0000000..4c0da73
--- /dev/null
+++ b/src/libcef_dll/ctocpp/web_plugin_info_ctocpp.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=8893d13d3250068c66c1a4bb82bf7c9345e95f27$
+//
+
+#include "libcef_dll/ctocpp/web_plugin_info_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") CefString CefWebPluginInfoCToCpp::GetName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_web_plugin_info_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefWebPluginInfoCToCpp::GetPath() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_web_plugin_info_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_path))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_path(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefWebPluginInfoCToCpp::GetVersion() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_web_plugin_info_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_version))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_version(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefWebPluginInfoCToCpp::GetDescription() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_web_plugin_info_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_description))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_description(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoCToCpp::CefWebPluginInfoCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoCToCpp::~CefWebPluginInfoCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_web_plugin_info_t*
+CefCToCppRefCounted<CefWebPluginInfoCToCpp,
+                    CefWebPluginInfo,
+                    cef_web_plugin_info_t>::UnwrapDerived(CefWrapperType type,
+                                                          CefWebPluginInfo* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefWebPluginInfoCToCpp,
+                                   CefWebPluginInfo,
+                                   cef_web_plugin_info_t>::kWrapperType =
+    WT_WEB_PLUGIN_INFO;
diff --git a/src/libcef_dll/ctocpp/web_plugin_info_ctocpp.h b/src/libcef_dll/ctocpp/web_plugin_info_ctocpp.h
new file mode 100644
index 0000000..a39069a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/web_plugin_info_ctocpp.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=0485ac1dcee7fa6707d64e312369c1eb95c1c96d$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_INFO_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_INFO_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefWebPluginInfoCToCpp
+    : public CefCToCppRefCounted<CefWebPluginInfoCToCpp,
+                                 CefWebPluginInfo,
+                                 cef_web_plugin_info_t> {
+ public:
+  CefWebPluginInfoCToCpp();
+  virtual ~CefWebPluginInfoCToCpp();
+
+  // CefWebPluginInfo methods.
+  CefString GetName() OVERRIDE;
+  CefString GetPath() OVERRIDE;
+  CefString GetVersion() OVERRIDE;
+  CefString GetDescription() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_INFO_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.cc b/src/libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.cc
new file mode 100644
index 0000000..14add4e
--- /dev/null
+++ b/src/libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=aa1d37e0ac4dbe70fc8e0739676ef968c8ffa765$
+//
+
+#include "libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.h"
+#include "libcef_dll/cpptoc/web_plugin_info_cpptoc.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+bool CefWebPluginInfoVisitorCToCpp::Visit(CefRefPtr<CefWebPluginInfo> info,
+                                          int count,
+                                          int total) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_web_plugin_info_visitor_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, visit))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: info; type: refptr_diff
+  DCHECK(info.get());
+  if (!info.get())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->visit(_struct, CefWebPluginInfoCppToC::Wrap(info), count, total);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoVisitorCToCpp::CefWebPluginInfoVisitorCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWebPluginInfoVisitorCToCpp::~CefWebPluginInfoVisitorCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_web_plugin_info_visitor_t* CefCToCppRefCounted<
+    CefWebPluginInfoVisitorCToCpp,
+    CefWebPluginInfoVisitor,
+    cef_web_plugin_info_visitor_t>::UnwrapDerived(CefWrapperType type,
+                                                  CefWebPluginInfoVisitor* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefWebPluginInfoVisitorCToCpp,
+                        CefWebPluginInfoVisitor,
+                        cef_web_plugin_info_visitor_t>::kWrapperType =
+        WT_WEB_PLUGIN_INFO_VISITOR;
diff --git a/src/libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.h b/src/libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.h
new file mode 100644
index 0000000..f9384e6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=b53a2be3ea825d25ee3071adc3084491649d12df$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_INFO_VISITOR_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_INFO_VISITOR_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefWebPluginInfoVisitorCToCpp
+    : public CefCToCppRefCounted<CefWebPluginInfoVisitorCToCpp,
+                                 CefWebPluginInfoVisitor,
+                                 cef_web_plugin_info_visitor_t> {
+ public:
+  CefWebPluginInfoVisitorCToCpp();
+  virtual ~CefWebPluginInfoVisitorCToCpp();
+
+  // CefWebPluginInfoVisitor methods.
+  bool Visit(CefRefPtr<CefWebPluginInfo> info, int count, int total) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_INFO_VISITOR_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.cc b/src/libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.cc
new file mode 100644
index 0000000..f5550aa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7d4c6744f017df70be4318f1f61b140b4ee40ed5$
+//
+
+#include "libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+void CefWebPluginUnstableCallbackCToCpp::IsUnstable(const CefString& path,
+                                                    bool unstable) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_web_plugin_unstable_callback_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_unstable))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+
+  // Execute
+  _struct->is_unstable(_struct, path.GetStruct(), unstable);
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWebPluginUnstableCallbackCToCpp::CefWebPluginUnstableCallbackCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWebPluginUnstableCallbackCToCpp::~CefWebPluginUnstableCallbackCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_web_plugin_unstable_callback_t*
+CefCToCppRefCounted<CefWebPluginUnstableCallbackCToCpp,
+                    CefWebPluginUnstableCallback,
+                    cef_web_plugin_unstable_callback_t>::
+    UnwrapDerived(CefWrapperType type, CefWebPluginUnstableCallback* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType
+    CefCToCppRefCounted<CefWebPluginUnstableCallbackCToCpp,
+                        CefWebPluginUnstableCallback,
+                        cef_web_plugin_unstable_callback_t>::kWrapperType =
+        WT_WEB_PLUGIN_UNSTABLE_CALLBACK;
diff --git a/src/libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.h b/src/libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.h
new file mode 100644
index 0000000..48714ea
--- /dev/null
+++ b/src/libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=03d71fb933226f515bd852f98c1293b076bc9ec0$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_UNSTABLE_CALLBACK_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_UNSTABLE_CALLBACK_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/cef_browser.h"
+#include "include/cef_web_plugin.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefWebPluginUnstableCallbackCToCpp
+    : public CefCToCppRefCounted<CefWebPluginUnstableCallbackCToCpp,
+                                 CefWebPluginUnstableCallback,
+                                 cef_web_plugin_unstable_callback_t> {
+ public:
+  CefWebPluginUnstableCallbackCToCpp();
+  virtual ~CefWebPluginUnstableCallbackCToCpp();
+
+  // CefWebPluginUnstableCallback methods.
+  void IsUnstable(const CefString& path, bool unstable) override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_WEB_PLUGIN_UNSTABLE_CALLBACK_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/write_handler_ctocpp.cc b/src/libcef_dll/ctocpp/write_handler_ctocpp.cc
new file mode 100644
index 0000000..93da3fa
--- /dev/null
+++ b/src/libcef_dll/ctocpp/write_handler_ctocpp.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=de27cf9a29354fb3f0fd37141f1a4fe45ff73c88$
+//
+
+#include "libcef_dll/ctocpp/write_handler_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+size_t CefWriteHandlerCToCpp::Write(const void* ptr, size_t size, size_t n) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_write_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, write))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: ptr; type: simple_byaddr
+  DCHECK(ptr);
+  if (!ptr)
+    return 0;
+
+  // Execute
+  size_t _retval = _struct->write(_struct, ptr, size, n);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefWriteHandlerCToCpp::Seek(int64 offset, int whence) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_write_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, seek))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->seek(_struct, offset, whence);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefWriteHandlerCToCpp::Tell() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_write_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, tell))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->tell(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefWriteHandlerCToCpp::Flush() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_write_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, flush))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->flush(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefWriteHandlerCToCpp::MayBlock() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_write_handler_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, may_block))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->may_block(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefWriteHandlerCToCpp::CefWriteHandlerCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefWriteHandlerCToCpp::~CefWriteHandlerCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_write_handler_t*
+CefCToCppRefCounted<CefWriteHandlerCToCpp,
+                    CefWriteHandler,
+                    cef_write_handler_t>::UnwrapDerived(CefWrapperType type,
+                                                        CefWriteHandler* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefWriteHandlerCToCpp,
+                                   CefWriteHandler,
+                                   cef_write_handler_t>::kWrapperType =
+    WT_WRITE_HANDLER;
diff --git a/src/libcef_dll/ctocpp/write_handler_ctocpp.h b/src/libcef_dll/ctocpp/write_handler_ctocpp.h
new file mode 100644
index 0000000..21e2429
--- /dev/null
+++ b/src/libcef_dll/ctocpp/write_handler_ctocpp.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=5dd7e1db0aefed2cc2bd78dac1cd77aabe76d064$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_WRITE_HANDLER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_WRITE_HANDLER_CTOCPP_H_
+#pragma once
+
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+
+#include "include/capi/cef_stream_capi.h"
+#include "include/cef_stream.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed DLL-side only.
+class CefWriteHandlerCToCpp : public CefCToCppRefCounted<CefWriteHandlerCToCpp,
+                                                         CefWriteHandler,
+                                                         cef_write_handler_t> {
+ public:
+  CefWriteHandlerCToCpp();
+  virtual ~CefWriteHandlerCToCpp();
+
+  // CefWriteHandler methods.
+  size_t Write(const void* ptr, size_t size, size_t n) override;
+  int Seek(int64 offset, int whence) override;
+  int64 Tell() override;
+  int Flush() override;
+  bool MayBlock() override;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_WRITE_HANDLER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/x509cert_principal_ctocpp.cc b/src/libcef_dll/ctocpp/x509cert_principal_ctocpp.cc
new file mode 100644
index 0000000..2e90645
--- /dev/null
+++ b/src/libcef_dll/ctocpp/x509cert_principal_ctocpp.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=66b1884e86ab45e63bbe4f902da5934d61520d45$
+//
+
+#include "libcef_dll/ctocpp/x509cert_principal_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefString CefX509CertPrincipalCToCpp::GetDisplayName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_display_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_display_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefX509CertPrincipalCToCpp::GetCommonName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_common_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_common_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefX509CertPrincipalCToCpp::GetLocalityName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_locality_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_locality_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefX509CertPrincipalCToCpp::GetStateOrProvinceName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_state_or_province_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_state_or_province_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefX509CertPrincipalCToCpp::GetCountryName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_country_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_country_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefX509CertPrincipalCToCpp::GetStreetAddresses(
+    std::vector<CefString>& addresses) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_street_addresses))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: addresses; type: string_vec_byref
+  cef_string_list_t addressesList = cef_string_list_alloc();
+  DCHECK(addressesList);
+  if (addressesList)
+    transfer_string_list_contents(addresses, addressesList);
+
+  // Execute
+  _struct->get_street_addresses(_struct, addressesList);
+
+  // Restore param:addresses; type: string_vec_byref
+  if (addressesList) {
+    addresses.clear();
+    transfer_string_list_contents(addressesList, addresses);
+    cef_string_list_free(addressesList);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefX509CertPrincipalCToCpp::GetOrganizationNames(
+    std::vector<CefString>& names) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_organization_names))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: names; type: string_vec_byref
+  cef_string_list_t namesList = cef_string_list_alloc();
+  DCHECK(namesList);
+  if (namesList)
+    transfer_string_list_contents(names, namesList);
+
+  // Execute
+  _struct->get_organization_names(_struct, namesList);
+
+  // Restore param:names; type: string_vec_byref
+  if (namesList) {
+    names.clear();
+    transfer_string_list_contents(namesList, names);
+    cef_string_list_free(namesList);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefX509CertPrincipalCToCpp::GetOrganizationUnitNames(
+    std::vector<CefString>& names) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_organization_unit_names))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: names; type: string_vec_byref
+  cef_string_list_t namesList = cef_string_list_alloc();
+  DCHECK(namesList);
+  if (namesList)
+    transfer_string_list_contents(names, namesList);
+
+  // Execute
+  _struct->get_organization_unit_names(_struct, namesList);
+
+  // Restore param:names; type: string_vec_byref
+  if (namesList) {
+    names.clear();
+    transfer_string_list_contents(namesList, names);
+    cef_string_list_free(namesList);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefX509CertPrincipalCToCpp::GetDomainComponents(
+    std::vector<CefString>& components) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509cert_principal_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_domain_components))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: components; type: string_vec_byref
+  cef_string_list_t componentsList = cef_string_list_alloc();
+  DCHECK(componentsList);
+  if (componentsList)
+    transfer_string_list_contents(components, componentsList);
+
+  // Execute
+  _struct->get_domain_components(_struct, componentsList);
+
+  // Restore param:components; type: string_vec_byref
+  if (componentsList) {
+    components.clear();
+    transfer_string_list_contents(componentsList, components);
+    cef_string_list_free(componentsList);
+  }
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefX509CertPrincipalCToCpp::CefX509CertPrincipalCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefX509CertPrincipalCToCpp::~CefX509CertPrincipalCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_x509cert_principal_t* CefCToCppRefCounted<
+    CefX509CertPrincipalCToCpp,
+    CefX509CertPrincipal,
+    cef_x509cert_principal_t>::UnwrapDerived(CefWrapperType type,
+                                             CefX509CertPrincipal* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefX509CertPrincipalCToCpp,
+                                   CefX509CertPrincipal,
+                                   cef_x509cert_principal_t>::kWrapperType =
+    WT_X509CERT_PRINCIPAL;
diff --git a/src/libcef_dll/ctocpp/x509cert_principal_ctocpp.h b/src/libcef_dll/ctocpp/x509cert_principal_ctocpp.h
new file mode 100644
index 0000000..fb6fd3a
--- /dev/null
+++ b/src/libcef_dll/ctocpp/x509cert_principal_ctocpp.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=eb6a2fc4970c9511728a65bb944720cc44c76568$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_X509CERT_PRINCIPAL_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_X509CERT_PRINCIPAL_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include <vector>
+#include "include/capi/cef_x509_certificate_capi.h"
+#include "include/cef_x509_certificate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefX509CertPrincipalCToCpp
+    : public CefCToCppRefCounted<CefX509CertPrincipalCToCpp,
+                                 CefX509CertPrincipal,
+                                 cef_x509cert_principal_t> {
+ public:
+  CefX509CertPrincipalCToCpp();
+  virtual ~CefX509CertPrincipalCToCpp();
+
+  // CefX509CertPrincipal methods.
+  CefString GetDisplayName() OVERRIDE;
+  CefString GetCommonName() OVERRIDE;
+  CefString GetLocalityName() OVERRIDE;
+  CefString GetStateOrProvinceName() OVERRIDE;
+  CefString GetCountryName() OVERRIDE;
+  void GetStreetAddresses(std::vector<CefString>& addresses) OVERRIDE;
+  void GetOrganizationNames(std::vector<CefString>& names) OVERRIDE;
+  void GetOrganizationUnitNames(std::vector<CefString>& names) OVERRIDE;
+  void GetDomainComponents(std::vector<CefString>& components) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_X509CERT_PRINCIPAL_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/x509certificate_ctocpp.cc b/src/libcef_dll/ctocpp/x509certificate_ctocpp.cc
new file mode 100644
index 0000000..6f98f86
--- /dev/null
+++ b/src/libcef_dll/ctocpp/x509certificate_ctocpp.cc
@@ -0,0 +1,262 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c47209a99213feca0178f75623f6c7d1b6985f06$
+//
+
+#include "libcef_dll/ctocpp/x509certificate_ctocpp.h"
+#include <algorithm>
+#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/x509cert_principal_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefX509CertPrincipal> CefX509CertificateCToCpp::GetSubject() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_subject))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_x509cert_principal_t* _retval = _struct->get_subject(_struct);
+
+  // Return type: refptr_same
+  return CefX509CertPrincipalCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefX509CertPrincipal> CefX509CertificateCToCpp::GetIssuer() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_issuer))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_x509cert_principal_t* _retval = _struct->get_issuer(_struct);
+
+  // Return type: refptr_same
+  return CefX509CertPrincipalCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefX509CertificateCToCpp::GetSerialNumber() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_serial_number))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_serial_number(_struct);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") CefTime CefX509CertificateCToCpp::GetValidStart() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_valid_start))
+    return CefTime();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_time_t _retval = _struct->get_valid_start(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefTime CefX509CertificateCToCpp::GetValidExpiry() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_valid_expiry))
+    return CefTime();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_time_t _retval = _struct->get_valid_expiry(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefX509CertificateCToCpp::GetDEREncoded() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_derencoded))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_derencoded(_struct);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBinaryValue> CefX509CertificateCToCpp::GetPEMEncoded() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_pemencoded))
+    return nullptr;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_binary_value_t* _retval = _struct->get_pemencoded(_struct);
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall") size_t CefX509CertificateCToCpp::GetIssuerChainSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_issuer_chain_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_issuer_chain_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefX509CertificateCToCpp::GetDEREncodedIssuerChain(
+    IssuerChainBinaryList& chain) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_derencoded_issuer_chain))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: chain; type: refptr_vec_same_byref
+  size_t chainSize = chain.size();
+  size_t chainCount = std::max(GetIssuerChainSize(), chainSize);
+  cef_binary_value_t** chainList = NULL;
+  if (chainCount > 0) {
+    chainList = new cef_binary_value_t*[chainCount];
+    DCHECK(chainList);
+    if (chainList) {
+      memset(chainList, 0, sizeof(cef_binary_value_t*) * chainCount);
+    }
+    if (chainList && chainSize > 0) {
+      for (size_t i = 0; i < chainSize; ++i) {
+        chainList[i] = CefBinaryValueCToCpp::Unwrap(chain[i]);
+      }
+    }
+  }
+
+  // Execute
+  _struct->get_derencoded_issuer_chain(_struct, &chainCount, chainList);
+
+  // Restore param:chain; type: refptr_vec_same_byref
+  chain.clear();
+  if (chainCount > 0 && chainList) {
+    for (size_t i = 0; i < chainCount; ++i) {
+      chain.push_back(CefBinaryValueCToCpp::Wrap(chainList[i]));
+    }
+    delete[] chainList;
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+void CefX509CertificateCToCpp::GetPEMEncodedIssuerChain(
+    IssuerChainBinaryList& chain) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_x509certificate_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_pemencoded_issuer_chain))
+    return;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Translate param: chain; type: refptr_vec_same_byref
+  size_t chainSize = chain.size();
+  size_t chainCount = std::max(GetIssuerChainSize(), chainSize);
+  cef_binary_value_t** chainList = NULL;
+  if (chainCount > 0) {
+    chainList = new cef_binary_value_t*[chainCount];
+    DCHECK(chainList);
+    if (chainList) {
+      memset(chainList, 0, sizeof(cef_binary_value_t*) * chainCount);
+    }
+    if (chainList && chainSize > 0) {
+      for (size_t i = 0; i < chainSize; ++i) {
+        chainList[i] = CefBinaryValueCToCpp::Unwrap(chain[i]);
+      }
+    }
+  }
+
+  // Execute
+  _struct->get_pemencoded_issuer_chain(_struct, &chainCount, chainList);
+
+  // Restore param:chain; type: refptr_vec_same_byref
+  chain.clear();
+  if (chainCount > 0 && chainList) {
+    for (size_t i = 0; i < chainCount; ++i) {
+      chain.push_back(CefBinaryValueCToCpp::Wrap(chainList[i]));
+    }
+    delete[] chainList;
+  }
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefX509CertificateCToCpp::CefX509CertificateCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefX509CertificateCToCpp::~CefX509CertificateCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_x509certificate_t* CefCToCppRefCounted<
+    CefX509CertificateCToCpp,
+    CefX509Certificate,
+    cef_x509certificate_t>::UnwrapDerived(CefWrapperType type,
+                                          CefX509Certificate* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefX509CertificateCToCpp,
+                                   CefX509Certificate,
+                                   cef_x509certificate_t>::kWrapperType =
+    WT_X509CERTIFICATE;
diff --git a/src/libcef_dll/ctocpp/x509certificate_ctocpp.h b/src/libcef_dll/ctocpp/x509certificate_ctocpp.h
new file mode 100644
index 0000000..1905ebb
--- /dev/null
+++ b/src/libcef_dll/ctocpp/x509certificate_ctocpp.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=76b2c3c6e5bcffc3f737f8f2ae127d9190aef91e$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_X509CERTIFICATE_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_X509CERTIFICATE_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_x509_certificate_capi.h"
+#include "include/cef_x509_certificate.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefX509CertificateCToCpp
+    : public CefCToCppRefCounted<CefX509CertificateCToCpp,
+                                 CefX509Certificate,
+                                 cef_x509certificate_t> {
+ public:
+  CefX509CertificateCToCpp();
+  virtual ~CefX509CertificateCToCpp();
+
+  // CefX509Certificate methods.
+  CefRefPtr<CefX509CertPrincipal> GetSubject() OVERRIDE;
+  CefRefPtr<CefX509CertPrincipal> GetIssuer() OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetSerialNumber() OVERRIDE;
+  CefTime GetValidStart() OVERRIDE;
+  CefTime GetValidExpiry() OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetDEREncoded() OVERRIDE;
+  CefRefPtr<CefBinaryValue> GetPEMEncoded() OVERRIDE;
+  size_t GetIssuerChainSize() OVERRIDE;
+  void GetDEREncodedIssuerChain(IssuerChainBinaryList& chain) OVERRIDE;
+  void GetPEMEncodedIssuerChain(IssuerChainBinaryList& chain) OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_X509CERTIFICATE_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/xml_reader_ctocpp.cc b/src/libcef_dll/ctocpp/xml_reader_ctocpp.cc
new file mode 100644
index 0000000..83943b4
--- /dev/null
+++ b/src/libcef_dll/ctocpp/xml_reader_ctocpp.cc
@@ -0,0 +1,609 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=480fc8b34176c75410ff4073adea11b2397e7d3e$
+//
+
+#include "libcef_dll/ctocpp/xml_reader_ctocpp.h"
+#include "libcef_dll/ctocpp/stream_reader_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefXmlReader> CefXmlReader::Create(CefRefPtr<CefStreamReader> stream,
+                                             EncodingType encodingType,
+                                             const CefString& URI) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: stream; type: refptr_same
+  DCHECK(stream.get());
+  if (!stream.get())
+    return nullptr;
+  // Verify param: URI; type: string_byref_const
+  DCHECK(!URI.empty());
+  if (URI.empty())
+    return nullptr;
+
+  // Execute
+  cef_xml_reader_t* _retval = cef_xml_reader_create(
+      CefStreamReaderCToCpp::Unwrap(stream), encodingType, URI.GetStruct());
+
+  // Return type: refptr_same
+  return CefXmlReaderCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::MoveToNextNode() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_next_node))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->move_to_next_node(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::Close() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->close(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::HasError() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_error))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_error(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetError() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_error))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_error(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefXmlReader::NodeType CefXmlReaderCToCpp::GetType() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_type))
+    return XML_NODE_UNSUPPORTED;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_xml_node_type_t _retval = _struct->get_type(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int CefXmlReaderCToCpp::GetDepth() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_depth))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_depth(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetLocalName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_local_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_local_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetPrefix() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_prefix))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_prefix(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetQualifiedName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_qualified_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_qualified_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetNamespaceURI() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_namespace_uri))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_namespace_uri(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetBaseURI() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_base_uri))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_base_uri(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetXmlLang() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_xml_lang))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_xml_lang(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::IsEmptyElement() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, is_empty_element))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->is_empty_element(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::HasValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_value))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_value(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetValue() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_value))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_value(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::HasAttributes() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, has_attributes))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->has_attributes(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefXmlReaderCToCpp::GetAttributeCount() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_attribute_count))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  size_t _retval = _struct->get_attribute_count(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetAttribute(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_attribute_byindex))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_attribute_byindex(_struct, index);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefXmlReaderCToCpp::GetAttribute(const CefString& qualifiedName) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_attribute_byqname))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: qualifiedName; type: string_byref_const
+  DCHECK(!qualifiedName.empty());
+  if (qualifiedName.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      _struct->get_attribute_byqname(_struct, qualifiedName.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefString CefXmlReaderCToCpp::GetAttribute(const CefString& localName,
+                                           const CefString& namespaceURI) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_attribute_bylname))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: localName; type: string_byref_const
+  DCHECK(!localName.empty());
+  if (localName.empty())
+    return CefString();
+  // Verify param: namespaceURI; type: string_byref_const
+  DCHECK(!namespaceURI.empty());
+  if (namespaceURI.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_attribute_bylname(
+      _struct, localName.GetStruct(), namespaceURI.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetInnerXml() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_inner_xml))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_inner_xml(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefXmlReaderCToCpp::GetOuterXml() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_outer_xml))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_outer_xml(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") int CefXmlReaderCToCpp::GetLineNumber() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_line_number))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->get_line_number(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::MoveToAttribute(int index) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_attribute_byindex))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: index; type: simple_byval
+  DCHECK_GE(index, 0);
+  if (index < 0)
+    return false;
+
+  // Execute
+  int _retval = _struct->move_to_attribute_byindex(_struct, index);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefXmlReaderCToCpp::MoveToAttribute(const CefString& qualifiedName) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_attribute_byqname))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: qualifiedName; type: string_byref_const
+  DCHECK(!qualifiedName.empty());
+  if (qualifiedName.empty())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->move_to_attribute_byqname(_struct, qualifiedName.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefXmlReaderCToCpp::MoveToAttribute(const CefString& localName,
+                                         const CefString& namespaceURI) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_attribute_bylname))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: localName; type: string_byref_const
+  DCHECK(!localName.empty());
+  if (localName.empty())
+    return false;
+  // Verify param: namespaceURI; type: string_byref_const
+  DCHECK(!namespaceURI.empty());
+  if (namespaceURI.empty())
+    return false;
+
+  // Execute
+  int _retval = _struct->move_to_attribute_bylname(
+      _struct, localName.GetStruct(), namespaceURI.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::MoveToFirstAttribute() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_first_attribute))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->move_to_first_attribute(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::MoveToNextAttribute() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_next_attribute))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->move_to_next_attribute(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefXmlReaderCToCpp::MoveToCarryingElement() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_xml_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_carrying_element))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->move_to_carrying_element(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefXmlReaderCToCpp::CefXmlReaderCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefXmlReaderCToCpp::~CefXmlReaderCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_xml_reader_t*
+CefCToCppRefCounted<CefXmlReaderCToCpp, CefXmlReader, cef_xml_reader_t>::
+    UnwrapDerived(CefWrapperType type, CefXmlReader* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefXmlReaderCToCpp,
+                                   CefXmlReader,
+                                   cef_xml_reader_t>::kWrapperType =
+    WT_XML_READER;
diff --git a/src/libcef_dll/ctocpp/xml_reader_ctocpp.h b/src/libcef_dll/ctocpp/xml_reader_ctocpp.h
new file mode 100644
index 0000000..3b98826
--- /dev/null
+++ b/src/libcef_dll/ctocpp/xml_reader_ctocpp.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7313546898a9aecdbaf6eeda54f792c10803eb84$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_XML_READER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_XML_READER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_xml_reader_capi.h"
+#include "include/cef_xml_reader.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefXmlReaderCToCpp : public CefCToCppRefCounted<CefXmlReaderCToCpp,
+                                                      CefXmlReader,
+                                                      cef_xml_reader_t> {
+ public:
+  CefXmlReaderCToCpp();
+  virtual ~CefXmlReaderCToCpp();
+
+  // CefXmlReader methods.
+  bool MoveToNextNode() OVERRIDE;
+  bool Close() OVERRIDE;
+  bool HasError() OVERRIDE;
+  CefString GetError() OVERRIDE;
+  NodeType GetType() OVERRIDE;
+  int GetDepth() OVERRIDE;
+  CefString GetLocalName() OVERRIDE;
+  CefString GetPrefix() OVERRIDE;
+  CefString GetQualifiedName() OVERRIDE;
+  CefString GetNamespaceURI() OVERRIDE;
+  CefString GetBaseURI() OVERRIDE;
+  CefString GetXmlLang() OVERRIDE;
+  bool IsEmptyElement() OVERRIDE;
+  bool HasValue() OVERRIDE;
+  CefString GetValue() OVERRIDE;
+  bool HasAttributes() OVERRIDE;
+  size_t GetAttributeCount() OVERRIDE;
+  CefString GetAttribute(int index) OVERRIDE;
+  CefString GetAttribute(const CefString& qualifiedName) OVERRIDE;
+  CefString GetAttribute(const CefString& localName,
+                         const CefString& namespaceURI) OVERRIDE;
+  CefString GetInnerXml() OVERRIDE;
+  CefString GetOuterXml() OVERRIDE;
+  int GetLineNumber() OVERRIDE;
+  bool MoveToAttribute(int index) OVERRIDE;
+  bool MoveToAttribute(const CefString& qualifiedName) OVERRIDE;
+  bool MoveToAttribute(const CefString& localName,
+                       const CefString& namespaceURI) OVERRIDE;
+  bool MoveToFirstAttribute() OVERRIDE;
+  bool MoveToNextAttribute() OVERRIDE;
+  bool MoveToCarryingElement() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_XML_READER_CTOCPP_H_
diff --git a/src/libcef_dll/ctocpp/zip_reader_ctocpp.cc b/src/libcef_dll/ctocpp/zip_reader_ctocpp.cc
new file mode 100644
index 0000000..c46f7a6
--- /dev/null
+++ b/src/libcef_dll/ctocpp/zip_reader_ctocpp.cc
@@ -0,0 +1,276 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=078dbecccef4097257adf7962e33168411375928$
+//
+
+#include "libcef_dll/ctocpp/zip_reader_ctocpp.h"
+#include "libcef_dll/ctocpp/stream_reader_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+
+// STATIC METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefZipReader> CefZipReader::Create(
+    CefRefPtr<CefStreamReader> stream) {
+  shutdown_checker::AssertNotShutdown();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: stream; type: refptr_same
+  DCHECK(stream.get());
+  if (!stream.get())
+    return nullptr;
+
+  // Execute
+  cef_zip_reader_t* _retval =
+      cef_zip_reader_create(CefStreamReaderCToCpp::Unwrap(stream));
+
+  // Return type: refptr_same
+  return CefZipReaderCToCpp::Wrap(_retval);
+}
+
+// VIRTUAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall") bool CefZipReaderCToCpp::MoveToFirstFile() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_first_file))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->move_to_first_file(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefZipReaderCToCpp::MoveToNextFile() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_next_file))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->move_to_next_file(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefZipReaderCToCpp::MoveToFile(const CefString& fileName,
+                                    bool caseSensitive) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, move_to_file))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: fileName; type: string_byref_const
+  DCHECK(!fileName.empty());
+  if (fileName.empty())
+    return false;
+
+  // Execute
+  int _retval =
+      _struct->move_to_file(_struct, fileName.GetStruct(), caseSensitive);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefZipReaderCToCpp::Close() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->close(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CefString CefZipReaderCToCpp::GetFileName() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_file_name))
+    return CefString();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_string_userfree_t _retval = _struct->get_file_name(_struct);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefZipReaderCToCpp::GetFileSize() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_file_size))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->get_file_size(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") CefTime CefZipReaderCToCpp::GetFileLastModified() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, get_file_last_modified))
+    return CefTime();
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_time_t _retval = _struct->get_file_last_modified(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+bool CefZipReaderCToCpp::OpenFile(const CefString& password) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, open_file))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: password
+
+  // Execute
+  int _retval = _struct->open_file(_struct, password.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") bool CefZipReaderCToCpp::CloseFile() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, close_file))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->close_file(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+int CefZipReaderCToCpp::ReadFile(void* buffer, size_t bufferSize) {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, read_file))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: buffer; type: simple_byaddr
+  DCHECK(buffer);
+  if (!buffer)
+    return 0;
+
+  // Execute
+  int _retval = _struct->read_file(_struct, buffer, bufferSize);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") int64 CefZipReaderCToCpp::Tell() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, tell))
+    return 0;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = _struct->tell(_struct);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall") bool CefZipReaderCToCpp::Eof() {
+  shutdown_checker::AssertNotShutdown();
+
+  cef_zip_reader_t* _struct = GetStruct();
+  if (CEF_MEMBER_MISSING(_struct, eof))
+    return false;
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = _struct->eof(_struct);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+// CONSTRUCTOR - Do not edit by hand.
+
+CefZipReaderCToCpp::CefZipReaderCToCpp() {}
+
+// DESTRUCTOR - Do not edit by hand.
+
+CefZipReaderCToCpp::~CefZipReaderCToCpp() {
+  shutdown_checker::AssertNotShutdown();
+}
+
+template <>
+cef_zip_reader_t*
+CefCToCppRefCounted<CefZipReaderCToCpp, CefZipReader, cef_zip_reader_t>::
+    UnwrapDerived(CefWrapperType type, CefZipReader* c) {
+  NOTREACHED() << "Unexpected class type: " << type;
+  return nullptr;
+}
+
+template <>
+CefWrapperType CefCToCppRefCounted<CefZipReaderCToCpp,
+                                   CefZipReader,
+                                   cef_zip_reader_t>::kWrapperType =
+    WT_ZIP_READER;
diff --git a/src/libcef_dll/ctocpp/zip_reader_ctocpp.h b/src/libcef_dll/ctocpp/zip_reader_ctocpp.h
new file mode 100644
index 0000000..f960387
--- /dev/null
+++ b/src/libcef_dll/ctocpp/zip_reader_ctocpp.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=a2db9b88080c167747d75e9c5a68c0757d077861$
+//
+
+#ifndef CEF_LIBCEF_DLL_CTOCPP_ZIP_READER_CTOCPP_H_
+#define CEF_LIBCEF_DLL_CTOCPP_ZIP_READER_CTOCPP_H_
+#pragma once
+
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+
+#include "include/capi/cef_zip_reader_capi.h"
+#include "include/cef_zip_reader.h"
+#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
+
+// Wrap a C structure with a C++ class.
+// This class may be instantiated and accessed wrapper-side only.
+class CefZipReaderCToCpp : public CefCToCppRefCounted<CefZipReaderCToCpp,
+                                                      CefZipReader,
+                                                      cef_zip_reader_t> {
+ public:
+  CefZipReaderCToCpp();
+  virtual ~CefZipReaderCToCpp();
+
+  // CefZipReader methods.
+  bool MoveToFirstFile() OVERRIDE;
+  bool MoveToNextFile() OVERRIDE;
+  bool MoveToFile(const CefString& fileName, bool caseSensitive) OVERRIDE;
+  bool Close() OVERRIDE;
+  CefString GetFileName() OVERRIDE;
+  int64 GetFileSize() OVERRIDE;
+  CefTime GetFileLastModified() OVERRIDE;
+  bool OpenFile(const CefString& password) OVERRIDE;
+  bool CloseFile() OVERRIDE;
+  int ReadFile(void* buffer, size_t bufferSize) OVERRIDE;
+  int64 Tell() OVERRIDE;
+  bool Eof() OVERRIDE;
+};
+
+#endif  // CEF_LIBCEF_DLL_CTOCPP_ZIP_READER_CTOCPP_H_
diff --git a/src/libcef_dll/libcef.dll.manifest b/src/libcef_dll/libcef.dll.manifest
new file mode 100644
index 0000000..76c6380
--- /dev/null
+++ b/src/libcef_dll/libcef.dll.manifest
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

+  <dependency>

+    <dependentAssembly>

+      <assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>

+    </dependentAssembly>

+  </dependency>

+</assembly>

diff --git a/src/libcef_dll/libcef.lst b/src/libcef_dll/libcef.lst
new file mode 100644
index 0000000..e236ea9
--- /dev/null
+++ b/src/libcef_dll/libcef.lst
@@ -0,0 +1,9 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  /* Only export necessary symbols from libcef.so. */
+  global: cef_*;
+  local: *;
+};
diff --git a/src/libcef_dll/libcef_dll.cc b/src/libcef_dll/libcef_dll.cc
new file mode 100644
index 0000000..90df59a
--- /dev/null
+++ b/src/libcef_dll/libcef_dll.cc
@@ -0,0 +1,937 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c42cd0225d8e4286df471fcc622f9bbf9ed977d3$
+//
+
+#include "include/capi/cef_app_capi.h"
+#include "include/capi/cef_crash_util_capi.h"
+#include "include/capi/cef_file_util_capi.h"
+#include "include/capi/cef_origin_whitelist_capi.h"
+#include "include/capi/cef_parser_capi.h"
+#include "include/capi/cef_path_util_capi.h"
+#include "include/capi/cef_process_util_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+#include "include/capi/cef_ssl_info_capi.h"
+#include "include/capi/cef_task_capi.h"
+#include "include/capi/cef_trace_capi.h"
+#include "include/capi/cef_v8_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/capi/test/cef_test_helpers_capi.h"
+#include "include/cef_app.h"
+#include "include/cef_crash_util.h"
+#include "include/cef_file_util.h"
+#include "include/cef_origin_whitelist.h"
+#include "include/cef_parser.h"
+#include "include/cef_path_util.h"
+#include "include/cef_process_util.h"
+#include "include/cef_scheme.h"
+#include "include/cef_ssl_info.h"
+#include "include/cef_task.h"
+#include "include/cef_trace.h"
+#include "include/cef_v8.h"
+#include "include/cef_web_plugin.h"
+#include "include/test/cef_test_helpers.h"
+#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
+#include "libcef_dll/cpptoc/command_line_cpptoc.h"
+#include "libcef_dll/cpptoc/frame_cpptoc.h"
+#include "libcef_dll/cpptoc/value_cpptoc.h"
+#include "libcef_dll/ctocpp/app_ctocpp.h"
+#include "libcef_dll/ctocpp/completion_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/end_tracing_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/register_cdm_callback_ctocpp.h"
+#include "libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h"
+#include "libcef_dll/ctocpp/task_ctocpp.h"
+#include "libcef_dll/ctocpp/v8handler_ctocpp.h"
+#include "libcef_dll/ctocpp/web_plugin_info_visitor_ctocpp.h"
+#include "libcef_dll/ctocpp/web_plugin_unstable_callback_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// GLOBAL FUNCTIONS - Body may be edited by hand.
+
+CEF_EXPORT int cef_execute_process(const struct _cef_main_args_t* args,
+                                   struct _cef_app_t* application,
+                                   void* windows_sandbox_info) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: args; type: struct_byref_const
+  DCHECK(args);
+  if (!args)
+    return 0;
+  // Unverified params: application, windows_sandbox_info
+
+  // Translate param: args; type: struct_byref_const
+  CefMainArgs argsObj;
+  if (args)
+    argsObj.Set(*args, false);
+
+  // Execute
+  int _retval = CefExecuteProcess(argsObj, CefAppCToCpp::Wrap(application),
+                                  windows_sandbox_info);
+
+  // Return type: simple
+  return _retval;
+}
+
+CEF_EXPORT int cef_initialize(const struct _cef_main_args_t* args,
+                              const struct _cef_settings_t* settings,
+                              struct _cef_app_t* application,
+                              void* windows_sandbox_info) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: args; type: struct_byref_const
+  DCHECK(args);
+  if (!args)
+    return 0;
+  // Verify param: settings; type: struct_byref_const
+  DCHECK(settings);
+  if (!settings)
+    return 0;
+  // Unverified params: application, windows_sandbox_info
+
+  // Translate param: args; type: struct_byref_const
+  CefMainArgs argsObj;
+  if (args)
+    argsObj.Set(*args, false);
+  // Translate param: settings; type: struct_byref_const
+  CefSettings settingsObj;
+  if (settings)
+    settingsObj.Set(*settings, false);
+
+  // Execute
+  bool _retval =
+      CefInitialize(argsObj, settingsObj, CefAppCToCpp::Wrap(application),
+                    windows_sandbox_info);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT void cef_shutdown() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+#if DCHECK_IS_ON()
+  shutdown_checker::SetIsShutdown();
+#endif
+
+  // Execute
+  CefShutdown();
+}
+
+CEF_EXPORT void cef_do_message_loop_work() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefDoMessageLoopWork();
+}
+
+CEF_EXPORT void cef_run_message_loop() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRunMessageLoop();
+}
+
+CEF_EXPORT void cef_quit_message_loop() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefQuitMessageLoop();
+}
+
+CEF_EXPORT void cef_set_osmodal_loop(int osModalLoop) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefSetOSModalLoop(osModalLoop ? true : false);
+}
+
+CEF_EXPORT void cef_enable_highdpi_support() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefEnableHighDPISupport();
+}
+
+CEF_EXPORT int cef_crash_reporting_enabled() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  bool _retval = CefCrashReportingEnabled();
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT void cef_set_crash_key_value(const cef_string_t* key,
+                                        const cef_string_t* value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(key);
+  if (!key)
+    return;
+  // Unverified params: value
+
+  // Execute
+  CefSetCrashKeyValue(CefString(key), CefString(value));
+}
+
+CEF_EXPORT int cef_create_directory(const cef_string_t* full_path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: full_path; type: string_byref_const
+  DCHECK(full_path);
+  if (!full_path)
+    return 0;
+
+  // Execute
+  bool _retval = CefCreateDirectory(CefString(full_path));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_get_temp_directory(cef_string_t* temp_dir) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: temp_dir; type: string_byref
+  DCHECK(temp_dir);
+  if (!temp_dir)
+    return 0;
+
+  // Translate param: temp_dir; type: string_byref
+  CefString temp_dirStr(temp_dir);
+
+  // Execute
+  bool _retval = CefGetTempDirectory(temp_dirStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_create_new_temp_directory(const cef_string_t* prefix,
+                                             cef_string_t* new_temp_path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: new_temp_path; type: string_byref
+  DCHECK(new_temp_path);
+  if (!new_temp_path)
+    return 0;
+  // Unverified params: prefix
+
+  // Translate param: new_temp_path; type: string_byref
+  CefString new_temp_pathStr(new_temp_path);
+
+  // Execute
+  bool _retval = CefCreateNewTempDirectory(CefString(prefix), new_temp_pathStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_create_temp_directory_in_directory(
+    const cef_string_t* base_dir,
+    const cef_string_t* prefix,
+    cef_string_t* new_dir) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: base_dir; type: string_byref_const
+  DCHECK(base_dir);
+  if (!base_dir)
+    return 0;
+  // Verify param: new_dir; type: string_byref
+  DCHECK(new_dir);
+  if (!new_dir)
+    return 0;
+  // Unverified params: prefix
+
+  // Translate param: new_dir; type: string_byref
+  CefString new_dirStr(new_dir);
+
+  // Execute
+  bool _retval = CefCreateTempDirectoryInDirectory(
+      CefString(base_dir), CefString(prefix), new_dirStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_directory_exists(const cef_string_t* path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return 0;
+
+  // Execute
+  bool _retval = CefDirectoryExists(CefString(path));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_delete_file(const cef_string_t* path, int recursive) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return 0;
+
+  // Execute
+  bool _retval = CefDeleteFile(CefString(path), recursive ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_zip_directory(const cef_string_t* src_dir,
+                                 const cef_string_t* dest_file,
+                                 int include_hidden_files) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: src_dir; type: string_byref_const
+  DCHECK(src_dir);
+  if (!src_dir)
+    return 0;
+  // Verify param: dest_file; type: string_byref_const
+  DCHECK(dest_file);
+  if (!dest_file)
+    return 0;
+
+  // Execute
+  bool _retval = CefZipDirectory(CefString(src_dir), CefString(dest_file),
+                                 include_hidden_files ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT void cef_load_crlsets_file(const cef_string_t* path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+
+  // Execute
+  CefLoadCRLSetsFile(CefString(path));
+}
+
+CEF_EXPORT int cef_add_cross_origin_whitelist_entry(
+    const cef_string_t* source_origin,
+    const cef_string_t* target_protocol,
+    const cef_string_t* target_domain,
+    int allow_target_subdomains) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: source_origin; type: string_byref_const
+  DCHECK(source_origin);
+  if (!source_origin)
+    return 0;
+  // Verify param: target_protocol; type: string_byref_const
+  DCHECK(target_protocol);
+  if (!target_protocol)
+    return 0;
+  // Unverified params: target_domain
+
+  // Execute
+  bool _retval = CefAddCrossOriginWhitelistEntry(
+      CefString(source_origin), CefString(target_protocol),
+      CefString(target_domain), allow_target_subdomains ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_remove_cross_origin_whitelist_entry(
+    const cef_string_t* source_origin,
+    const cef_string_t* target_protocol,
+    const cef_string_t* target_domain,
+    int allow_target_subdomains) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: source_origin; type: string_byref_const
+  DCHECK(source_origin);
+  if (!source_origin)
+    return 0;
+  // Verify param: target_protocol; type: string_byref_const
+  DCHECK(target_protocol);
+  if (!target_protocol)
+    return 0;
+  // Unverified params: target_domain
+
+  // Execute
+  bool _retval = CefRemoveCrossOriginWhitelistEntry(
+      CefString(source_origin), CefString(target_protocol),
+      CefString(target_domain), allow_target_subdomains ? true : false);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_clear_cross_origin_whitelist() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  bool _retval = CefClearCrossOriginWhitelist();
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_parse_url(const cef_string_t* url,
+                             struct _cef_urlparts_t* parts) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(url);
+  if (!url)
+    return 0;
+  // Verify param: parts; type: struct_byref
+  DCHECK(parts);
+  if (!parts)
+    return 0;
+
+  // Translate param: parts; type: struct_byref
+  CefURLParts partsObj;
+  if (parts)
+    partsObj.AttachTo(*parts);
+
+  // Execute
+  bool _retval = CefParseURL(CefString(url), partsObj);
+
+  // Restore param: parts; type: struct_byref
+  if (parts)
+    partsObj.DetachTo(*parts);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_create_url(const struct _cef_urlparts_t* parts,
+                              cef_string_t* url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: parts; type: struct_byref_const
+  DCHECK(parts);
+  if (!parts)
+    return 0;
+  // Verify param: url; type: string_byref
+  DCHECK(url);
+  if (!url)
+    return 0;
+
+  // Translate param: parts; type: struct_byref_const
+  CefURLParts partsObj;
+  if (parts)
+    partsObj.Set(*parts, false);
+  // Translate param: url; type: string_byref
+  CefString urlStr(url);
+
+  // Execute
+  bool _retval = CefCreateURL(partsObj, urlStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT cef_string_userfree_t
+cef_format_url_for_security_display(const cef_string_t* origin_url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: origin_url; type: string_byref_const
+  DCHECK(origin_url);
+  if (!origin_url)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefFormatUrlForSecurityDisplay(CefString(origin_url));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+CEF_EXPORT cef_string_userfree_t
+cef_get_mime_type(const cef_string_t* extension) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: string_byref_const
+  DCHECK(extension);
+  if (!extension)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefGetMimeType(CefString(extension));
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+CEF_EXPORT void cef_get_extensions_for_mime_type(const cef_string_t* mime_type,
+                                                 cef_string_list_t extensions) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: mime_type; type: string_byref_const
+  DCHECK(mime_type);
+  if (!mime_type)
+    return;
+  // Verify param: extensions; type: string_vec_byref
+  DCHECK(extensions);
+  if (!extensions)
+    return;
+
+  // Translate param: extensions; type: string_vec_byref
+  std::vector<CefString> extensionsList;
+  transfer_string_list_contents(extensions, extensionsList);
+
+  // Execute
+  CefGetExtensionsForMimeType(CefString(mime_type), extensionsList);
+
+  // Restore param: extensions; type: string_vec_byref
+  cef_string_list_clear(extensions);
+  transfer_string_list_contents(extensionsList, extensions);
+}
+
+CEF_EXPORT cef_string_userfree_t cef_base64encode(const void* data,
+                                                  size_t data_size) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefBase64Encode(data, data_size);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+CEF_EXPORT struct _cef_binary_value_t* cef_base64decode(
+    const cef_string_t* data) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: string_byref_const
+  DCHECK(data);
+  if (!data)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefBinaryValue> _retval = CefBase64Decode(CefString(data));
+
+  // Return type: refptr_same
+  return CefBinaryValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_string_userfree_t cef_uriencode(const cef_string_t* text,
+                                               int use_plus) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefURIEncode(CefString(text), use_plus ? true : false);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+CEF_EXPORT cef_string_userfree_t
+cef_uridecode(const cef_string_t* text,
+              int convert_to_utf8,
+              cef_uri_unescape_rule_t unescape_rule) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(text);
+  if (!text)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefURIDecode(
+      CefString(text), convert_to_utf8 ? true : false, unescape_rule);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+CEF_EXPORT struct _cef_value_t* cef_parse_json(
+    const cef_string_t* json_string,
+    cef_json_parser_options_t options) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: json_string; type: string_byref_const
+  DCHECK(json_string);
+  if (!json_string)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefValue> _retval = CefParseJSON(CefString(json_string), options);
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT struct _cef_value_t* cef_parse_json_buffer(
+    const void* json,
+    size_t json_size,
+    cef_json_parser_options_t options) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: json; type: simple_byaddr
+  DCHECK(json);
+  if (!json)
+    return NULL;
+
+  // Execute
+  CefRefPtr<CefValue> _retval = CefParseJSON(json, json_size, options);
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT struct _cef_value_t* cef_parse_jsonand_return_error(
+    const cef_string_t* json_string,
+    cef_json_parser_options_t options,
+    cef_json_parser_error_t* error_code_out,
+    cef_string_t* error_msg_out) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: json_string; type: string_byref_const
+  DCHECK(json_string);
+  if (!json_string)
+    return NULL;
+  // Verify param: error_code_out; type: simple_byref
+  DCHECK(error_code_out);
+  if (!error_code_out)
+    return NULL;
+  // Verify param: error_msg_out; type: string_byref
+  DCHECK(error_msg_out);
+  if (!error_msg_out)
+    return NULL;
+
+  // Translate param: error_code_out; type: simple_byref
+  cef_json_parser_error_t error_code_outVal =
+      error_code_out ? *error_code_out : JSON_NO_ERROR;
+  // Translate param: error_msg_out; type: string_byref
+  CefString error_msg_outStr(error_msg_out);
+
+  // Execute
+  CefRefPtr<CefValue> _retval = CefParseJSONAndReturnError(
+      CefString(json_string), options, error_code_outVal, error_msg_outStr);
+
+  // Restore param: error_code_out; type: simple_byref
+  if (error_code_out)
+    *error_code_out = error_code_outVal;
+
+  // Return type: refptr_same
+  return CefValueCppToC::Wrap(_retval);
+}
+
+CEF_EXPORT cef_string_userfree_t
+cef_write_json(struct _cef_value_t* node, cef_json_writer_options_t options) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: node; type: refptr_same
+  DCHECK(node);
+  if (!node)
+    return NULL;
+
+  // Execute
+  CefString _retval = CefWriteJSON(CefValueCppToC::Unwrap(node), options);
+
+  // Return type: string
+  return _retval.DetachToUserFree();
+}
+
+CEF_EXPORT int cef_get_path(cef_path_key_t key, cef_string_t* path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref
+  DCHECK(path);
+  if (!path)
+    return 0;
+
+  // Translate param: path; type: string_byref
+  CefString pathStr(path);
+
+  // Execute
+  bool _retval = CefGetPath(key, pathStr);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_launch_process(struct _cef_command_line_t* command_line) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: command_line; type: refptr_same
+  DCHECK(command_line);
+  if (!command_line)
+    return 0;
+
+  // Execute
+  bool _retval = CefLaunchProcess(CefCommandLineCppToC::Unwrap(command_line));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_register_scheme_handler_factory(
+    const cef_string_t* scheme_name,
+    const cef_string_t* domain_name,
+    struct _cef_scheme_handler_factory_t* factory) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(scheme_name);
+  if (!scheme_name)
+    return 0;
+  // Unverified params: domain_name, factory
+
+  // Execute
+  bool _retval = CefRegisterSchemeHandlerFactory(
+      CefString(scheme_name), CefString(domain_name),
+      CefSchemeHandlerFactoryCToCpp::Wrap(factory));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_clear_scheme_handler_factories() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  bool _retval = CefClearSchemeHandlerFactories();
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_is_cert_status_error(cef_cert_status_t status) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  bool _retval = CefIsCertStatusError(status);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_currently_on(cef_thread_id_t threadId) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  bool _retval = CefCurrentlyOn(threadId);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_post_task(cef_thread_id_t threadId,
+                             struct _cef_task_t* task) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: task; type: refptr_diff
+  DCHECK(task);
+  if (!task)
+    return 0;
+
+  // Execute
+  bool _retval = CefPostTask(threadId, CefTaskCToCpp::Wrap(task));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_post_delayed_task(cef_thread_id_t threadId,
+                                     struct _cef_task_t* task,
+                                     int64 delay_ms) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: task; type: refptr_diff
+  DCHECK(task);
+  if (!task)
+    return 0;
+
+  // Execute
+  bool _retval =
+      CefPostDelayedTask(threadId, CefTaskCToCpp::Wrap(task), delay_ms);
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_begin_tracing(const cef_string_t* categories,
+                                 struct _cef_completion_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: categories, callback
+
+  // Execute
+  bool _retval = CefBeginTracing(CefString(categories),
+                                 CefCompletionCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int cef_end_tracing(const cef_string_t* tracing_file,
+                               struct _cef_end_tracing_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: tracing_file, callback
+
+  // Execute
+  bool _retval = CefEndTracing(CefString(tracing_file),
+                               CefEndTracingCallbackCToCpp::Wrap(callback));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT int64 cef_now_from_system_trace_time() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = CefNowFromSystemTraceTime();
+
+  // Return type: simple
+  return _retval;
+}
+
+CEF_EXPORT int cef_register_extension(const cef_string_t* extension_name,
+                                      const cef_string_t* javascript_code,
+                                      struct _cef_v8handler_t* handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension_name; type: string_byref_const
+  DCHECK(extension_name);
+  if (!extension_name)
+    return 0;
+  // Verify param: javascript_code; type: string_byref_const
+  DCHECK(javascript_code);
+  if (!javascript_code)
+    return 0;
+  // Unverified params: handler
+
+  // Execute
+  bool _retval = CefRegisterExtension(CefString(extension_name),
+                                      CefString(javascript_code),
+                                      CefV8HandlerCToCpp::Wrap(handler));
+
+  // Return type: bool
+  return _retval;
+}
+
+CEF_EXPORT void cef_visit_web_plugin_info(
+    struct _cef_web_plugin_info_visitor_t* visitor) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor);
+  if (!visitor)
+    return;
+
+  // Execute
+  CefVisitWebPluginInfo(CefWebPluginInfoVisitorCToCpp::Wrap(visitor));
+}
+
+CEF_EXPORT void cef_refresh_web_plugins() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  CefRefreshWebPlugins();
+}
+
+CEF_EXPORT void cef_unregister_internal_web_plugin(const cef_string_t* path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+
+  // Execute
+  CefUnregisterInternalWebPlugin(CefString(path));
+}
+
+CEF_EXPORT void cef_register_web_plugin_crash(const cef_string_t* path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+
+  // Execute
+  CefRegisterWebPluginCrash(CefString(path));
+}
+
+CEF_EXPORT void cef_is_web_plugin_unstable(
+    const cef_string_t* path,
+    struct _cef_web_plugin_unstable_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback);
+  if (!callback)
+    return;
+
+  // Execute
+  CefIsWebPluginUnstable(CefString(path),
+                         CefWebPluginUnstableCallbackCToCpp::Wrap(callback));
+}
+
+CEF_EXPORT void cef_register_widevine_cdm(
+    const cef_string_t* path,
+    struct _cef_register_cdm_callback_t* callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(path);
+  if (!path)
+    return;
+  // Unverified params: callback
+
+  // Execute
+  CefRegisterWidevineCdm(CefString(path),
+                         CefRegisterCdmCallbackCToCpp::Wrap(callback));
+}
+
+CEF_EXPORT void cef_execute_java_script_with_user_gesture_for_tests(
+    struct _cef_frame_t* frame,
+    const cef_string_t* javascript) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: frame; type: refptr_same
+  DCHECK(frame);
+  if (!frame)
+    return;
+  // Unverified params: javascript
+
+  // Execute
+  CefExecuteJavaScriptWithUserGestureForTests(CefFrameCppToC::Unwrap(frame),
+                                              CefString(javascript));
+}
diff --git a/src/libcef_dll/libcef_dll.rc b/src/libcef_dll/libcef_dll.rc
new file mode 100644
index 0000000..f01d627
--- /dev/null
+++ b/src/libcef_dll/libcef_dll.rc
@@ -0,0 +1,143 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#define APSTUDIO_HIDDEN_SYMBOLS

+#include "windows.h"

+#include "include/cef_version.h"

+#undef APSTUDIO_HIDDEN_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"

+    "#include ""windows.h""\r\n"

+    "#include ""include/version.h""\r\n"

+    "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Dialog

+//

+

+IDD_ALERT DIALOGEX 0, 0, 241, 76

+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU

+CAPTION "JavaScript Alert"

+FONT 8, "MS Shell Dlg", 400, 0, 0x1

+BEGIN

+    DEFPUSHBUTTON   "OK",IDOK,184,55,50,14

+    LTEXT           "",IDC_DIALOGTEXT,16,17,210,30

+END

+

+IDD_CONFIRM DIALOGEX 0, 0, 241, 76

+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU

+CAPTION "JavaScript Confirm"

+FONT 8, "MS Shell Dlg", 400, 0, 0x1

+BEGIN

+    PUSHBUTTON      "Cancel",IDCANCEL,184,55,50,14

+    DEFPUSHBUTTON   "OK",IDOK,131,55,50,14

+    LTEXT           "",IDC_DIALOGTEXT,16,17,210,30

+END

+

+IDD_PROMPT DIALOGEX 0, 0, 241, 76

+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU

+CAPTION "JavaScript Prompt"

+FONT 8, "MS Shell Dlg", 400, 0, 0x1

+BEGIN

+    DEFPUSHBUTTON   "OK",IDOK,131,55,50,14

+    LTEXT           "",IDC_DIALOGTEXT,16,17,210,18

+    PUSHBUTTON      "Cancel",IDCANCEL,184,55,50,14

+    EDITTEXT        IDC_PROMPTEDIT,15,33,210,14,ES_AUTOHSCROLL

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0

+ PRODUCTVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0

+ FILEFLAGSMASK 0x17L

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x2L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904b0"

+        BEGIN

+            VALUE "FileDescription", "Chromium Embedded Framework (CEF) Dynamic Link Library"

+            VALUE "FileVersion", CEF_VERSION

+            VALUE "InternalName", "libcef"

+            VALUE "LegalCopyright", "Copyright (C) " MAKE_STRING(COPYRIGHT_YEAR) " The Chromium Embedded Framework Authors"

+            VALUE "OriginalFilename", "libcef.dll"

+            VALUE "ProductName", "Chromium Embedded Framework (CEF) Dynamic Link Library"

+            VALUE "ProductVersion", CEF_VERSION

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1200

+    END

+END

+

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/src/libcef_dll/libcef_dll2.cc b/src/libcef_dll/libcef_dll2.cc
new file mode 100644
index 0000000..a240a1d
--- /dev/null
+++ b/src/libcef_dll/libcef_dll2.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+
+#include <cstddef>
+#include "include/cef_api_hash.h"
+#include "include/cef_version.h"
+
+CEF_EXPORT int cef_version_info(int entry) {
+  switch (entry) {
+    case 0:
+      return CEF_VERSION_MAJOR;
+    case 1:
+      return CEF_VERSION_MINOR;
+    case 2:
+      return CEF_VERSION_PATCH;
+    case 3:
+      return CEF_COMMIT_NUMBER;
+    case 4:
+      return CHROME_VERSION_MAJOR;
+    case 5:
+      return CHROME_VERSION_MINOR;
+    case 6:
+      return CHROME_VERSION_BUILD;
+    case 7:
+      return CHROME_VERSION_PATCH;
+    default:
+      return 0;
+  }
+}
+
+CEF_EXPORT const char* cef_api_hash(int entry) {
+  switch (entry) {
+    case 0:
+      return CEF_API_HASH_PLATFORM;
+    case 1:
+      return CEF_API_HASH_UNIVERSAL;
+    case 2:
+      return CEF_COMMIT_HASH;
+    default:
+      return NULL;
+  }
+}
diff --git a/src/libcef_dll/ptr_util.h b/src/libcef_dll/ptr_util.h
new file mode 100644
index 0000000..9692322
--- /dev/null
+++ b/src/libcef_dll/ptr_util.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_PTR_UTIL_H_
+#define CEF_LIBCEF_DLL_PTR_UTIL_H_
+#pragma once
+
+// Helpers for CefOwnPtr<>.
+#if defined(USING_CHROMIUM_INCLUDES)
+#define OWN_PASS(p) std::move(p)
+#define OWN_RETURN_AS(p, t) (p)
+#else
+#define OWN_PASS(p) (p).Pass()
+#define OWN_RETURN_AS(p, t) (p).PassAs<t>()
+#endif
+
+#endif  // CEF_LIBCEF_DLL_PTR_UTIL_H_
diff --git a/src/libcef_dll/resource.h b/src/libcef_dll/resource.h
new file mode 100644
index 0000000..a4a5889
--- /dev/null
+++ b/src/libcef_dll/resource.h
@@ -0,0 +1,25 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by libcef_dll.rc
+//
+
+// Avoid files associated with MacOS
+#define _X86_
+
+#define IDD_ALERT 130
+#define IDD_CONFIRM 131
+#define IDD_PROMPT 132
+#define IDC_PROMPTEDIT 1000
+#define IDC_DIALOGTEXT 1001
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 130
+#define _APS_NEXT_COMMAND_VALUE 32000
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/src/libcef_dll/sandbox/sandbox_mac.mm b/src/libcef_dll/sandbox/sandbox_mac.mm
new file mode 100644
index 0000000..cfdcae7
--- /dev/null
+++ b/src/libcef_dll/sandbox/sandbox_mac.mm
@@ -0,0 +1,65 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. Portions Copyright
+// 2018 the Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include <mach-o/dyld.h>
+#include <stdio.h>
+
+#include <memory>
+
+#include "sandbox/mac/seatbelt_exec.h"
+
+#include "include/cef_sandbox_mac.h"
+
+void* cef_sandbox_initialize(int argc, char** argv) {
+  uint32_t exec_path_size = 0;
+  int rv = _NSGetExecutablePath(NULL, &exec_path_size);
+  if (rv != -1) {
+    return NULL;
+  }
+
+  std::unique_ptr<char[]> exec_path(new char[exec_path_size]);
+  rv = _NSGetExecutablePath(exec_path.get(), &exec_path_size);
+  if (rv != 0) {
+    return NULL;
+  }
+
+  sandbox::SeatbeltExecServer::CreateFromArgumentsResult seatbelt =
+      sandbox::SeatbeltExecServer::CreateFromArguments(exec_path.get(), argc,
+                                                       argv);
+  if (seatbelt.sandbox_required) {
+    if (!seatbelt.server) {
+      fprintf(stderr, "Failed to create the seatbelt sandbox server.\n");
+      return NULL;
+    }
+    if (!seatbelt.server->InitializeSandbox()) {
+      fprintf(stderr, "Failed to initialize the sandbox.\n");
+      return NULL;
+    }
+  }
+
+  auto* copy = new sandbox::SeatbeltExecServer::CreateFromArgumentsResult();
+  copy->sandbox_required = seatbelt.sandbox_required;
+  copy->server.swap(seatbelt.server);
+  return copy;
+}
+
+void cef_sandbox_destroy(void* sandbox_context) {
+  delete static_cast<sandbox::SeatbeltExecServer::CreateFromArgumentsResult*>(
+      sandbox_context);
+}
+
+CefScopedSandboxContext::CefScopedSandboxContext() : sandbox_context_(NULL) {}
+
+CefScopedSandboxContext::~CefScopedSandboxContext() {
+  if (sandbox_context_) {
+    cef_sandbox_destroy(sandbox_context_);
+  }
+}
+
+bool CefScopedSandboxContext::Initialize(int argc, char** argv) {
+  if (sandbox_context_)
+    return false;
+  sandbox_context_ = cef_sandbox_initialize(argc, argv);
+  return !!sandbox_context_;
+}
diff --git a/src/libcef_dll/sandbox/sandbox_win.cc b/src/libcef_dll/sandbox/sandbox_win.cc
new file mode 100644
index 0000000..f8cdcc9
--- /dev/null
+++ b/src/libcef_dll/sandbox/sandbox_win.cc
@@ -0,0 +1,39 @@
+// Copyright 2013 The Chromium Embedded Framework Authors. Portions Copyright
+// 2011 the Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/sandbox_factory.h"
+
+#include "include/cef_sandbox_win.h"
+
+namespace {
+
+// From content/app/startup_helper_win.cc:
+void InitializeSandboxInfo(sandbox::SandboxInterfaceInfo* info) {
+  info->broker_services = sandbox::SandboxFactory::GetBrokerServices();
+  if (!info->broker_services) {
+    info->target_services = sandbox::SandboxFactory::GetTargetServices();
+  } else {
+    // Ensure the proper mitigations are enforced for the browser process.
+    sandbox::ApplyProcessMitigationsToCurrentProcess(
+        sandbox::MITIGATION_DEP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+        sandbox::MITIGATION_HARDEN_TOKEN_IL_POLICY);
+    // Note: these mitigations are "post-startup".  Some mitigations that need
+    // to be enabled sooner (e.g. MITIGATION_EXTENSION_POINT_DISABLE) are done
+    // so in Chrome_ELF.
+  }
+}
+
+}  // namespace
+
+void* cef_sandbox_info_create() {
+  sandbox::SandboxInterfaceInfo* info = new sandbox::SandboxInterfaceInfo();
+  memset(info, 0, sizeof(sandbox::SandboxInterfaceInfo));
+  InitializeSandboxInfo(info);
+  return info;
+}
+
+void cef_sandbox_info_destroy(void* sandbox_info) {
+  delete static_cast<sandbox::SandboxInterfaceInfo*>(sandbox_info);
+}
diff --git a/src/libcef_dll/shutdown_checker.cc b/src/libcef_dll/shutdown_checker.cc
new file mode 100644
index 0000000..f369d03
--- /dev/null
+++ b/src/libcef_dll/shutdown_checker.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef_dll/shutdown_checker.h"
+
+// For compatibility with older client compiler versions only use std::atomic
+// on the library side.
+#if defined(BUILDING_CEF_SHARED)
+#include <atomic>
+#else
+#include "include/base/cef_atomic_ref_count.h"
+#endif
+
+#include "include/base/cef_logging.h"
+
+namespace shutdown_checker {
+
+#if DCHECK_IS_ON()
+
+namespace {
+
+#if defined(BUILDING_CEF_SHARED)
+
+std::atomic_bool g_cef_shutdown{false};
+
+bool IsCefShutdown() {
+  return g_cef_shutdown.load();
+}
+
+void SetCefShutdown() {
+  g_cef_shutdown.store(true);
+}
+
+#else  // !defined(BUILDING_CEF_SHARED)
+
+base::AtomicRefCount g_cef_shutdown ATOMIC_DECLARATION;
+
+bool IsCefShutdown() {
+  return !base::AtomicRefCountIsZero(&g_cef_shutdown);
+}
+
+void SetCefShutdown() {
+  base::AtomicRefCountInc(&g_cef_shutdown);
+}
+
+#endif  // !defined(BUILDING_CEF_SHARED)
+
+}  // namespace
+
+void AssertNotShutdown() {
+  DCHECK(!IsCefShutdown())
+      << "Object reference incorrectly held at CefShutdown";
+}
+
+void SetIsShutdown() {
+  DCHECK(!IsCefShutdown());
+  SetCefShutdown();
+}
+
+#else  // !DCHECK_IS_ON()
+
+void AssertNotShutdown() {}
+
+#endif  // !DCHECK_IS_ON()
+
+}  // namespace shutdown_checker
diff --git a/src/libcef_dll/shutdown_checker.h b/src/libcef_dll/shutdown_checker.h
new file mode 100644
index 0000000..9d44788
--- /dev/null
+++ b/src/libcef_dll/shutdown_checker.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_SHUTDOWN_CHECKER_H_
+#define CEF_LIBCEF_DLL_SHUTDOWN_CHECKER_H_
+#pragma once
+
+namespace shutdown_checker {
+
+// Check that CEF objects are not held at CefShutdown.
+void AssertNotShutdown();
+
+// Called from libcef_dll.cc and libcef_dll_wrapper.cc.
+void SetIsShutdown();
+
+}  // namespace shutdown_checker
+
+#endif  // CEF_LIBCEF_DLL_SHUTDOWN_CHECKER_H_
diff --git a/src/libcef_dll/transfer_util.cc b/src/libcef_dll/transfer_util.cc
new file mode 100644
index 0000000..eb6a79c
--- /dev/null
+++ b/src/libcef_dll/transfer_util.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "libcef_dll/transfer_util.h"
+
+void transfer_string_list_contents(cef_string_list_t fromList,
+                                   StringList& toList) {
+  size_t size = cef_string_list_size(fromList);
+  CefString value;
+
+  for (size_t i = 0; i < size; i++) {
+    cef_string_list_value(fromList, i, value.GetWritableStruct());
+    toList.push_back(value);
+  }
+}
+
+void transfer_string_list_contents(const StringList& fromList,
+                                   cef_string_list_t toList) {
+  size_t size = fromList.size();
+  for (size_t i = 0; i < size; ++i)
+    cef_string_list_append(toList, fromList[i].GetStruct());
+}
+
+void transfer_string_map_contents(cef_string_map_t fromMap, StringMap& toMap) {
+  size_t size = cef_string_map_size(fromMap);
+  CefString key, value;
+
+  for (size_t i = 0; i < size; ++i) {
+    cef_string_map_key(fromMap, i, key.GetWritableStruct());
+    cef_string_map_value(fromMap, i, value.GetWritableStruct());
+
+    toMap.insert(std::make_pair(key, value));
+  }
+}
+
+void transfer_string_map_contents(const StringMap& fromMap,
+                                  cef_string_map_t toMap) {
+  StringMap::const_iterator it = fromMap.begin();
+  for (; it != fromMap.end(); ++it)
+    cef_string_map_append(toMap, it->first.GetStruct(), it->second.GetStruct());
+}
+
+void transfer_string_multimap_contents(cef_string_multimap_t fromMap,
+                                       StringMultimap& toMap) {
+  size_t size = cef_string_multimap_size(fromMap);
+  CefString key, value;
+
+  for (size_t i = 0; i < size; ++i) {
+    cef_string_multimap_key(fromMap, i, key.GetWritableStruct());
+    cef_string_multimap_value(fromMap, i, value.GetWritableStruct());
+
+    toMap.insert(std::make_pair(key, value));
+  }
+}
+
+void transfer_string_multimap_contents(const StringMultimap& fromMap,
+                                       cef_string_multimap_t toMap) {
+  StringMultimap::const_iterator it = fromMap.begin();
+  for (; it != fromMap.end(); ++it) {
+    cef_string_multimap_append(toMap, it->first.GetStruct(),
+                               it->second.GetStruct());
+  }
+}
diff --git a/src/libcef_dll/transfer_util.h b/src/libcef_dll/transfer_util.h
new file mode 100644
index 0000000..f2c3e81
--- /dev/null
+++ b/src/libcef_dll/transfer_util.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_TRANSFER_UTIL_H_
+#define CEF_LIBCEF_DLL_TRANSFER_UTIL_H_
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "include/internal/cef_string_list.h"
+#include "include/internal/cef_string_map.h"
+#include "include/internal/cef_string_multimap.h"
+
+// Copy contents from one list type to another.
+typedef std::vector<CefString> StringList;
+void transfer_string_list_contents(cef_string_list_t fromList,
+                                   StringList& toList);
+void transfer_string_list_contents(const StringList& fromList,
+                                   cef_string_list_t toList);
+
+// Copy contents from one map type to another.
+typedef std::map<CefString, CefString> StringMap;
+void transfer_string_map_contents(cef_string_map_t fromMap, StringMap& toMap);
+void transfer_string_map_contents(const StringMap& fromMap,
+                                  cef_string_map_t toMap);
+
+// Copy contents from one map type to another.
+typedef std::multimap<CefString, CefString> StringMultimap;
+void transfer_string_multimap_contents(cef_string_multimap_t fromMap,
+                                       StringMultimap& toMap);
+void transfer_string_multimap_contents(const StringMultimap& fromMap,
+                                       cef_string_multimap_t toMap);
+
+#endif  // CEF_LIBCEF_DLL_TRANSFER_UTIL_H_
diff --git a/src/libcef_dll/views_stub.cc b/src/libcef_dll/views_stub.cc
new file mode 100644
index 0000000..f78b3b6
--- /dev/null
+++ b/src/libcef_dll/views_stub.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=c5439c177bd70bf3d8d52e0381c08d6276453183$
+//
+
+#include "base/logging.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_display.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_scroll_view.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_window.h"
+
+// STATIC STUB METHODS - Do not edit by hand.
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefBrowserView::CreateBrowserView(
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context,
+    CefRefPtr<CefBrowserViewDelegate> delegate) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefBrowserView> CefBrowserView::GetForBrowser(
+    CefRefPtr<CefBrowser> browser) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall") CefRefPtr<CefDisplay> CefDisplay::GetPrimaryDisplay() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDisplay> CefDisplay::GetDisplayNearestPoint(
+    const CefPoint& point,
+    bool input_pixel_coords) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefDisplay> CefDisplay::GetDisplayMatchingBounds(
+    const CefRect& bounds,
+    bool input_pixel_coords) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall") size_t CefDisplay::GetDisplayCount() {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+NO_SANITIZE("cfi-icall")
+void CefDisplay::GetAllDisplays(std::vector<CefRefPtr<CefDisplay>>& displays) {
+  NOTIMPLEMENTED();
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefLabelButton> CefLabelButton::CreateLabelButton(
+    CefRefPtr<CefButtonDelegate> delegate,
+    const CefString& text) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefMenuButton> CefMenuButton::CreateMenuButton(
+    CefRefPtr<CefMenuButtonDelegate> delegate,
+    const CefString& text) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefPanel> CefPanel::CreatePanel(
+    CefRefPtr<CefPanelDelegate> delegate) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefScrollView> CefScrollView::CreateScrollView(
+    CefRefPtr<CefViewDelegate> delegate) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefTextfield> CefTextfield::CreateTextfield(
+    CefRefPtr<CefTextfieldDelegate> delegate) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+NO_SANITIZE("cfi-icall")
+CefRefPtr<CefWindow> CefWindow::CreateTopLevelWindow(
+    CefRefPtr<CefWindowDelegate> delegate) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
diff --git a/src/libcef_dll/wrapper/cef_browser_info_map.h b/src/libcef_dll/wrapper/cef_browser_info_map.h
new file mode 100644
index 0000000..f62c307
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_browser_info_map.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_LIBCEF_DLL_WRAPPER_CEF_BROWSER_INFO_MAP_H_
+#define CEF_LIBCEF_DLL_WRAPPER_CEF_BROWSER_INFO_MAP_H_
+#pragma once
+
+#include <map>
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+
+// Default traits for CefBrowserInfoMap. Override to provide different object
+// destruction behavior.
+template <typename ObjectType>
+struct DefaultCefBrowserInfoMapTraits {
+  static void Destruct(ObjectType info) { delete info; }
+};
+
+// Maps an arbitrary IdType to an arbitrary ObjectType on a per-browser basis.
+template <typename IdType,
+          typename ObjectType,
+          typename Traits = DefaultCefBrowserInfoMapTraits<ObjectType>>
+class CefBrowserInfoMap {
+ public:
+  // Implement this interface to visit and optionally delete objects in the map.
+  class Visitor {
+   public:
+    typedef IdType InfoIdType;
+    typedef ObjectType InfoObjectType;
+
+    // Called once for each info object. Set |remove| to true to remove the
+    // object from the map. It is safe to destruct removed objects in this
+    // callback. Return true to continue iterating or false to stop iterating.
+    virtual bool OnNextInfo(int browser_id,
+                            InfoIdType info_id,
+                            InfoObjectType info,
+                            bool* remove) = 0;
+
+   protected:
+    virtual ~Visitor() {}
+  };
+
+  CefBrowserInfoMap() {}
+
+  ~CefBrowserInfoMap() { clear(); }
+
+  // Add an object associated with the specified ID values.
+  void Add(int browser_id, IdType info_id, ObjectType info) {
+    InfoMap* info_map = nullptr;
+    typename BrowserInfoMap::const_iterator it_browser =
+        browser_info_map_.find(browser_id);
+    if (it_browser == browser_info_map_.end()) {
+      // No InfoMap exists for the browser ID so create it.
+      info_map = new InfoMap;
+      browser_info_map_.insert(std::make_pair(browser_id, info_map));
+    } else {
+      info_map = it_browser->second;
+      // The specified ID should not already exist in the map.
+      DCHECK(info_map->find(info_id) == info_map->end());
+    }
+
+    info_map->insert(std::make_pair(info_id, info));
+  }
+
+  // Find the object with the specified ID values. |visitor| can optionally be
+  // used to evaluate or remove the object at the same time. If the object is
+  // removed using the Visitor the caller is responsible for destroying it.
+  ObjectType Find(int browser_id, IdType info_id, Visitor* vistor) {
+    if (browser_info_map_.empty())
+      return ObjectType();
+
+    typename BrowserInfoMap::iterator it_browser =
+        browser_info_map_.find(browser_id);
+    if (it_browser == browser_info_map_.end())
+      return ObjectType();
+
+    InfoMap* info_map = it_browser->second;
+    typename InfoMap::iterator it_info = info_map->find(info_id);
+    if (it_info == info_map->end())
+      return ObjectType();
+
+    ObjectType info = it_info->second;
+
+    bool remove = false;
+    if (vistor)
+      vistor->OnNextInfo(browser_id, it_info->first, info, &remove);
+    if (remove) {
+      info_map->erase(it_info);
+
+      if (info_map->empty()) {
+        // No more entries in the InfoMap so remove it.
+        browser_info_map_.erase(it_browser);
+        delete info_map;
+      }
+    }
+
+    return info;
+  }
+
+  // Find all objects. If any objects are removed using the Visitor the caller
+  // is responsible for destroying them.
+  void FindAll(Visitor* visitor) {
+    DCHECK(visitor);
+
+    if (browser_info_map_.empty())
+      return;
+
+    bool remove, keepgoing = true;
+
+    typename BrowserInfoMap::iterator it_browser = browser_info_map_.begin();
+    while (it_browser != browser_info_map_.end()) {
+      InfoMap* info_map = it_browser->second;
+
+      typename InfoMap::iterator it_info = info_map->begin();
+      while (it_info != info_map->end()) {
+        remove = false;
+        keepgoing = visitor->OnNextInfo(it_browser->first, it_info->first,
+                                        it_info->second, &remove);
+
+        if (remove)
+          info_map->erase(it_info++);
+        else
+          ++it_info;
+
+        if (!keepgoing)
+          break;
+      }
+
+      if (info_map->empty()) {
+        // No more entries in the InfoMap so remove it.
+        browser_info_map_.erase(it_browser++);
+        delete info_map;
+      } else {
+        ++it_browser;
+      }
+
+      if (!keepgoing)
+        break;
+    }
+  }
+
+  // Find all objects associated with the specified browser. If any objects are
+  // removed using the Visitor the caller is responsible for destroying them.
+  void FindAll(int browser_id, Visitor* visitor) {
+    DCHECK(visitor);
+
+    if (browser_info_map_.empty())
+      return;
+
+    typename BrowserInfoMap::iterator it_browser =
+        browser_info_map_.find(browser_id);
+    if (it_browser == browser_info_map_.end())
+      return;
+
+    InfoMap* info_map = it_browser->second;
+    bool remove, keepgoing;
+
+    typename InfoMap::iterator it_info = info_map->begin();
+    while (it_info != info_map->end()) {
+      remove = false;
+      keepgoing = visitor->OnNextInfo(browser_id, it_info->first,
+                                      it_info->second, &remove);
+
+      if (remove)
+        info_map->erase(it_info++);
+      else
+        ++it_info;
+
+      if (!keepgoing)
+        break;
+    }
+
+    if (info_map->empty()) {
+      // No more entries in the InfoMap so remove it.
+      browser_info_map_.erase(it_browser);
+      delete info_map;
+    }
+  }
+
+  // Returns true if the map is empty.
+  bool empty() const { return browser_info_map_.empty(); }
+
+  // Returns the number of objects in the map.
+  size_t size() const {
+    if (browser_info_map_.empty())
+      return 0;
+
+    size_t size = 0;
+    typename BrowserInfoMap::const_iterator it_browser =
+        browser_info_map_.begin();
+    for (; it_browser != browser_info_map_.end(); ++it_browser)
+      size += it_browser->second->size();
+    return size;
+  }
+
+  // Returns the number of objects in the map that are associated with the
+  // specified browser.
+  size_t size(int browser_id) const {
+    if (browser_info_map_.empty())
+      return 0;
+
+    typename BrowserInfoMap::const_iterator it_browser =
+        browser_info_map_.find(browser_id);
+    if (it_browser != browser_info_map_.end())
+      return it_browser->second->size();
+
+    return 0;
+  }
+
+  // Remove all objects from the map. The objects will be destructed.
+  void clear() {
+    if (browser_info_map_.empty())
+      return;
+
+    typename BrowserInfoMap::const_iterator it_browser =
+        browser_info_map_.begin();
+    for (; it_browser != browser_info_map_.end(); ++it_browser) {
+      InfoMap* info_map = it_browser->second;
+      typename InfoMap::const_iterator it_info = info_map->begin();
+      for (; it_info != info_map->end(); ++it_info)
+        Traits::Destruct(it_info->second);
+      delete info_map;
+    }
+    browser_info_map_.clear();
+  }
+
+  // Remove all objects from the map that are associated with the specified
+  // browser. The objects will be destructed.
+  void clear(int browser_id) {
+    if (browser_info_map_.empty())
+      return;
+
+    typename BrowserInfoMap::iterator it_browser =
+        browser_info_map_.find(browser_id);
+    if (it_browser == browser_info_map_.end())
+      return;
+
+    InfoMap* info_map = it_browser->second;
+    typename InfoMap::const_iterator it_info = info_map->begin();
+    for (; it_info != info_map->end(); ++it_info)
+      Traits::Destruct(it_info->second);
+
+    browser_info_map_.erase(it_browser);
+    delete info_map;
+  }
+
+ private:
+  // Map IdType to ObjectType instance.
+  typedef std::map<IdType, ObjectType> InfoMap;
+  // Map browser ID to InfoMap instance.
+  typedef std::map<int, InfoMap*> BrowserInfoMap;
+
+  BrowserInfoMap browser_info_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefBrowserInfoMap);
+};
+
+#endif  // CEF_LIBCEF_DLL_WRAPPER_CEF_BROWSER_INFO_MAP_H_
diff --git a/src/libcef_dll/wrapper/cef_byte_read_handler.cc b/src/libcef_dll/wrapper/cef_byte_read_handler.cc
new file mode 100644
index 0000000..dbb5181
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_byte_read_handler.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_byte_read_handler.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+
+CefByteReadHandler::CefByteReadHandler(const unsigned char* bytes,
+                                       size_t size,
+                                       CefRefPtr<CefBaseRefCounted> source)
+    : bytes_(bytes), size_(size), offset_(0), source_(source) {}
+
+size_t CefByteReadHandler::Read(void* ptr, size_t size, size_t n) {
+  base::AutoLock lock_scope(lock_);
+  size_t s = static_cast<size_t>(size_ - offset_) / size;
+  size_t ret = std::min(n, s);
+  memcpy(ptr, bytes_ + offset_, ret * size);
+  offset_ += ret * size;
+  return ret;
+}
+
+int CefByteReadHandler::Seek(int64 offset, int whence) {
+  int rv = -1L;
+  base::AutoLock lock_scope(lock_);
+  switch (whence) {
+    case SEEK_CUR:
+      if (offset_ + offset > size_ || offset_ + offset < 0)
+        break;
+      offset_ += offset;
+      rv = 0;
+      break;
+    case SEEK_END: {
+#if defined(OS_WIN)
+      int64 offset_abs = _abs64(offset);
+#else
+      int64 offset_abs = std::abs(offset);
+#endif
+      if (offset_abs > size_)
+        break;
+      offset_ = size_ - offset_abs;
+      rv = 0;
+      break;
+    }
+    case SEEK_SET:
+      if (offset > size_ || offset < 0)
+        break;
+      offset_ = offset;
+      rv = 0;
+      break;
+  }
+
+  return rv;
+}
+
+int64 CefByteReadHandler::Tell() {
+  base::AutoLock lock_scope(lock_);
+  return offset_;
+}
+
+int CefByteReadHandler::Eof() {
+  base::AutoLock lock_scope(lock_);
+  return (offset_ >= size_);
+}
diff --git a/src/libcef_dll/wrapper/cef_closure_task.cc b/src/libcef_dll/wrapper/cef_closure_task.cc
new file mode 100644
index 0000000..0fc7fec
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_closure_task.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_closure_task.h"
+#include "include/base/cef_callback.h"
+
+namespace {
+
+class CefClosureTask : public CefTask {
+ public:
+  explicit CefClosureTask(const base::Closure& closure) : closure_(closure) {}
+
+  // CefTask method
+  virtual void Execute() OVERRIDE {
+    closure_.Run();
+    closure_.Reset();
+  }
+
+ private:
+  base::Closure closure_;
+
+  IMPLEMENT_REFCOUNTING(CefClosureTask);
+  DISALLOW_COPY_AND_ASSIGN(CefClosureTask);
+};
+
+}  // namespace
+
+CefRefPtr<CefTask> CefCreateClosureTask(const base::Closure& closure) {
+  return new CefClosureTask(closure);
+}
+
+bool CefPostTask(CefThreadId threadId, const base::Closure& closure) {
+  return CefPostTask(threadId, new CefClosureTask(closure));
+}
+
+bool CefPostDelayedTask(CefThreadId threadId,
+                        const base::Closure& closure,
+                        int64 delay_ms) {
+  return CefPostDelayedTask(threadId, new CefClosureTask(closure), delay_ms);
+}
diff --git a/src/libcef_dll/wrapper/cef_library_loader_mac.mm b/src/libcef_dll/wrapper/cef_library_loader_mac.mm
new file mode 100644
index 0000000..046f46b
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_library_loader_mac.mm
@@ -0,0 +1,77 @@
+// Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_library_loader.h"
+
+#include <libgen.h>
+#include <mach-o/dyld.h>
+#include <stdio.h>
+
+#include <memory>
+#include <sstream>
+
+namespace {
+
+const char kFrameworkPath[] =
+    "Chromium Embedded Framework.framework/Chromium Embedded Framework";
+const char kPathFromHelperExe[] = "../../..";
+const char kPathFromMainExe[] = "../Frameworks";
+
+std::string GetFrameworkPath(bool helper) {
+  uint32_t exec_path_size = 0;
+  int rv = _NSGetExecutablePath(NULL, &exec_path_size);
+  if (rv != -1) {
+    return std::string();
+  }
+
+  std::unique_ptr<char[]> exec_path(new char[exec_path_size]);
+  rv = _NSGetExecutablePath(exec_path.get(), &exec_path_size);
+  if (rv != 0) {
+    return std::string();
+  }
+
+  // Get the directory path of the executable.
+  const char* parent_dir = dirname(exec_path.get());
+  if (!parent_dir) {
+    return std::string();
+  }
+
+  // Append the relative path to the framework.
+  std::stringstream ss;
+  ss << parent_dir << "/" << (helper ? kPathFromHelperExe : kPathFromMainExe)
+     << "/" << kFrameworkPath;
+  return ss.str();
+}
+
+}  // namespace
+
+CefScopedLibraryLoader::CefScopedLibraryLoader() : loaded_(false) {}
+
+bool CefScopedLibraryLoader::Load(bool helper) {
+  if (loaded_) {
+    return false;
+  }
+
+  const std::string& framework_path = GetFrameworkPath(helper);
+  if (framework_path.empty()) {
+    fprintf(stderr, "App does not have the expected bundle structure.\n");
+    return false;
+  }
+
+  // Load the CEF framework library.
+  if (!cef_load_library(framework_path.c_str())) {
+    fprintf(stderr, "Failed to load the CEF framework.\n");
+    return false;
+  }
+
+  loaded_ = true;
+  return true;
+}
+
+CefScopedLibraryLoader::~CefScopedLibraryLoader() {
+  if (loaded_) {
+    // Unload the CEF framework library.
+    cef_unload_library();
+  }
+}
diff --git a/src/libcef_dll/wrapper/cef_message_router.cc b/src/libcef_dll/wrapper/cef_message_router.cc
new file mode 100644
index 0000000..d521280
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_message_router.cc
@@ -0,0 +1,1129 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_message_router.h"
+
+#include <map>
+#include <set>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_macros.h"
+#include "include/cef_task.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+#include "libcef_dll/wrapper/cef_browser_info_map.h"
+
+namespace {
+
+// ID value reserved for internal use.
+const int kReservedId = 0;
+
+// Appended to the JS function name for related IPC messages.
+const char kMessageSuffix[] = "Msg";
+
+// JS object member argument names for cefQuery.
+const char kMemberRequest[] = "request";
+const char kMemberOnSuccess[] = "onSuccess";
+const char kMemberOnFailure[] = "onFailure";
+const char kMemberPersistent[] = "persistent";
+
+// Default error information when a query is canceled.
+const int kCanceledErrorCode = -1;
+const char kCanceledErrorMessage[] = "The query has been canceled";
+
+// Validate configuration settings.
+bool ValidateConfig(CefMessageRouterConfig& config) {
+  // Must specify function names.
+  if (config.js_cancel_function.empty() || config.js_query_function.empty()) {
+    return false;
+  }
+
+  return true;
+}
+
+// Helper template for generated ID values.
+template <typename T>
+class IdGenerator {
+ public:
+  IdGenerator() : next_id_(kReservedId) {}
+
+  T GetNextId() {
+    T id = ++next_id_;
+    if (id == kReservedId)  // In case the integer value wraps.
+      id = ++next_id_;
+    return id;
+  }
+
+ private:
+  T next_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(IdGenerator);
+};
+
+// Browser-side router implementation.
+class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
+ public:
+  // Implementation of the Callback interface.
+  class CallbackImpl : public CefMessageRouterBrowserSide::Callback {
+   public:
+    CallbackImpl(CefRefPtr<CefMessageRouterBrowserSideImpl> router,
+                 int browser_id,
+                 int64 query_id,
+                 bool persistent)
+        : router_(router),
+          browser_id_(browser_id),
+          query_id_(query_id),
+          persistent_(persistent) {}
+    virtual ~CallbackImpl() {
+      // Hitting this DCHECK means that you didn't call Success or Failure
+      // on the Callback after returning true from Handler::OnQuery. You must
+      // call Failure to terminate persistent queries.
+      DCHECK(!router_);
+    }
+
+    void Success(const CefString& response) OVERRIDE {
+      if (!CefCurrentlyOn(TID_UI)) {
+        // Must execute on the UI thread to access member variables.
+        CefPostTask(TID_UI, base::Bind(&CallbackImpl::Success, this, response));
+        return;
+      }
+
+      if (router_) {
+        CefPostTask(
+            TID_UI,
+            base::Bind(&CefMessageRouterBrowserSideImpl::OnCallbackSuccess,
+                       router_.get(), browser_id_, query_id_, response));
+
+        if (!persistent_) {
+          // Non-persistent callbacks are only good for a single use.
+          router_ = nullptr;
+        }
+      }
+    }
+
+    void Failure(int error_code, const CefString& error_message) OVERRIDE {
+      if (!CefCurrentlyOn(TID_UI)) {
+        // Must execute on the UI thread to access member variables.
+        CefPostTask(TID_UI, base::Bind(&CallbackImpl::Failure, this, error_code,
+                                       error_message));
+        return;
+      }
+
+      if (router_) {
+        CefPostTask(
+            TID_UI,
+            base::Bind(&CefMessageRouterBrowserSideImpl::OnCallbackFailure,
+                       router_.get(), browser_id_, query_id_, error_code,
+                       error_message));
+
+        // Failure always invalidates the callback.
+        router_ = nullptr;
+      }
+    }
+
+    void Detach() {
+      CEF_REQUIRE_UI_THREAD();
+      router_ = nullptr;
+    }
+
+   private:
+    CefRefPtr<CefMessageRouterBrowserSideImpl> router_;
+    const int browser_id_;
+    const int64 query_id_;
+    const bool persistent_;
+
+    IMPLEMENT_REFCOUNTING(CallbackImpl);
+  };
+
+  explicit CefMessageRouterBrowserSideImpl(const CefMessageRouterConfig& config)
+      : config_(config),
+        query_message_name_(config.js_query_function.ToString() +
+                            kMessageSuffix),
+        cancel_message_name_(config.js_cancel_function.ToString() +
+                             kMessageSuffix) {}
+
+  virtual ~CefMessageRouterBrowserSideImpl() {
+    // There should be no pending queries when the router is deleted.
+    DCHECK(browser_query_info_map_.empty());
+  }
+
+  bool AddHandler(Handler* handler, bool first) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    if (handler_set_.find(handler) == handler_set_.end()) {
+      handler_set_.insert(first ? handler_set_.begin() : handler_set_.end(),
+                          handler);
+      return true;
+    }
+    return false;
+  }
+
+  bool RemoveHandler(Handler* handler) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    if (handler_set_.erase(handler) > 0) {
+      CancelPendingFor(nullptr, handler, true);
+      return true;
+    }
+    return false;
+  }
+
+  void CancelPending(CefRefPtr<CefBrowser> browser, Handler* handler) OVERRIDE {
+    CancelPendingFor(browser, handler, true);
+  }
+
+  int GetPendingCount(CefRefPtr<CefBrowser> browser,
+                      Handler* handler) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    if (browser_query_info_map_.empty())
+      return 0;
+
+    if (handler) {
+      // Need to iterate over each QueryInfo object to test the handler.
+      class Visitor : public BrowserQueryInfoMap::Visitor {
+       public:
+        explicit Visitor(Handler* handler) : handler_(handler), count_(0) {}
+
+        bool OnNextInfo(int browser_id,
+                        InfoIdType info_id,
+                        InfoObjectType info,
+                        bool* remove) OVERRIDE {
+          if (info->handler == handler_)
+            count_++;
+          return true;
+        }
+
+        int count() const { return count_; }
+
+       private:
+        Handler* handler_;
+        int count_;
+      };
+
+      Visitor visitor(handler);
+
+      if (browser.get()) {
+        // Count queries associated with the specified browser.
+        browser_query_info_map_.FindAll(browser->GetIdentifier(), &visitor);
+      } else {
+        // Count all queries for all browsers.
+        browser_query_info_map_.FindAll(&visitor);
+      }
+
+      return visitor.count();
+    } else if (browser.get()) {
+      return static_cast<int>(
+          browser_query_info_map_.size(browser->GetIdentifier()));
+    } else {
+      return static_cast<int>(browser_query_info_map_.size());
+    }
+
+    return 0;
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE {
+    CancelPendingFor(browser, nullptr, false);
+  }
+
+  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser) OVERRIDE {
+    CancelPendingFor(browser, nullptr, false);
+  }
+
+  void OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame) OVERRIDE {
+    if (frame->IsMain())
+      CancelPendingFor(browser, nullptr, false);
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    const std::string& message_name = message->GetName();
+    if (message_name == query_message_name_) {
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      DCHECK_EQ(args->GetSize(), 4U);
+
+      const int context_id = args->GetInt(0);
+      const int request_id = args->GetInt(1);
+      const CefString& request = args->GetString(2);
+      const bool persistent = args->GetBool(3);
+
+      if (handler_set_.empty()) {
+        // No handlers so cancel the query.
+        CancelUnhandledQuery(browser, frame, context_id, request_id);
+        return true;
+      }
+
+      const int browser_id = browser->GetIdentifier();
+      const int64 query_id = query_id_generator_.GetNextId();
+
+      CefRefPtr<CallbackImpl> callback(
+          new CallbackImpl(this, browser_id, query_id, persistent));
+
+      // Make a copy of the handler list in case the user adds or removes a
+      // handler while we're iterating.
+      HandlerSet handler_set = handler_set_;
+
+      bool handled = false;
+      HandlerSet::const_iterator it_handler = handler_set.begin();
+      for (; it_handler != handler_set.end(); ++it_handler) {
+        handled = (*it_handler)
+                      ->OnQuery(browser, frame, query_id, request, persistent,
+                                callback.get());
+        if (handled)
+          break;
+      }
+
+      // If the query isn't handled nothing should be keeping a reference to
+      // the callback.
+      DCHECK(handled || callback->HasOneRef());
+
+      if (handled) {
+        // Persist the query information until the callback executes.
+        // It's safe to do this here because the callback will execute
+        // asynchronously.
+        QueryInfo* info = new QueryInfo;
+        info->browser = browser;
+        info->frame = frame;
+        info->context_id = context_id;
+        info->request_id = request_id;
+        info->persistent = persistent;
+        info->callback = callback;
+        info->handler = *(it_handler);
+        browser_query_info_map_.Add(browser_id, query_id, info);
+      } else {
+        // Invalidate the callback.
+        callback->Detach();
+
+        // No one chose to handle the query so cancel it.
+        CancelUnhandledQuery(browser, frame, context_id, request_id);
+      }
+
+      return true;
+    } else if (message_name == cancel_message_name_) {
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      DCHECK_EQ(args->GetSize(), 2U);
+
+      const int browser_id = browser->GetIdentifier();
+      const int context_id = args->GetInt(0);
+      const int request_id = args->GetInt(1);
+
+      CancelPendingRequest(browser_id, context_id, request_id);
+      return true;
+    }
+
+    return false;
+  }
+
+ private:
+  // Structure representing a pending query.
+  struct QueryInfo {
+    // Browser and frame originated the query.
+    CefRefPtr<CefBrowser> browser;
+    CefRefPtr<CefFrame> frame;
+
+    // IDs that uniquely identify the query in the renderer process. These
+    // values are opaque to the browser process but must be returned with the
+    // response.
+    int context_id;
+    int request_id;
+
+    // True if the query is persistent.
+    bool persistent;
+
+    // Callback associated with the query that must be detached when the query
+    // is canceled.
+    CefRefPtr<CallbackImpl> callback;
+
+    // Handler that should be notified if the query is automatically canceled.
+    Handler* handler;
+  };
+
+  // Retrieve a QueryInfo object from the map based on the browser-side query
+  // ID. If |always_remove| is true then the QueryInfo object will always be
+  // removed from the map. Othewise, the QueryInfo object will only be removed
+  // if the query is non-persistent. If |removed| is true the caller is
+  // responsible for deleting the returned QueryInfo object.
+  QueryInfo* GetQueryInfo(int browser_id,
+                          int64 query_id,
+                          bool always_remove,
+                          bool* removed) {
+    class Visitor : public BrowserQueryInfoMap::Visitor {
+     public:
+      explicit Visitor(bool always_remove)
+          : always_remove_(always_remove), removed_(false) {}
+
+      bool OnNextInfo(int browser_id,
+                      InfoIdType info_id,
+                      InfoObjectType info,
+                      bool* remove) OVERRIDE {
+        *remove = removed_ = (always_remove_ || !info->persistent);
+        return true;
+      }
+
+      bool removed() const { return removed_; }
+
+     private:
+      const bool always_remove_;
+      bool removed_;
+    };
+
+    Visitor visitor(always_remove);
+    QueryInfo* info =
+        browser_query_info_map_.Find(browser_id, query_id, &visitor);
+    if (info)
+      *removed = visitor.removed();
+    return info;
+  }
+
+  // Called by CallbackImpl on success.
+  void OnCallbackSuccess(int browser_id,
+                         int64 query_id,
+                         const CefString& response) {
+    CEF_REQUIRE_UI_THREAD();
+
+    bool removed;
+    QueryInfo* info = GetQueryInfo(browser_id, query_id, false, &removed);
+    if (info) {
+      SendQuerySuccess(info, response);
+      if (removed)
+        delete info;
+    }
+  }
+
+  // Called by CallbackImpl on failure.
+  void OnCallbackFailure(int browser_id,
+                         int64 query_id,
+                         int error_code,
+                         const CefString& error_message) {
+    CEF_REQUIRE_UI_THREAD();
+
+    bool removed;
+    QueryInfo* info = GetQueryInfo(browser_id, query_id, true, &removed);
+    if (info) {
+      SendQueryFailure(info, error_code, error_message);
+      DCHECK(removed);
+      delete info;
+    }
+  }
+
+  void SendQuerySuccess(QueryInfo* info, const CefString& response) {
+    SendQuerySuccess(info->browser, info->frame, info->context_id,
+                     info->request_id, response);
+  }
+
+  void SendQuerySuccess(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        int context_id,
+                        int request_id,
+                        const CefString& response) {
+    CefRefPtr<CefProcessMessage> message =
+        CefProcessMessage::Create(query_message_name_);
+    CefRefPtr<CefListValue> args = message->GetArgumentList();
+    args->SetInt(0, context_id);
+    args->SetInt(1, request_id);
+    args->SetBool(2, true);  // Indicates a success result.
+    args->SetString(3, response);
+    frame->SendProcessMessage(PID_RENDERER, message);
+  }
+
+  void SendQueryFailure(QueryInfo* info,
+                        int error_code,
+                        const CefString& error_message) {
+    SendQueryFailure(info->browser, info->frame, info->context_id,
+                     info->request_id, error_code, error_message);
+  }
+
+  void SendQueryFailure(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        int context_id,
+                        int request_id,
+                        int error_code,
+                        const CefString& error_message) {
+    CefRefPtr<CefProcessMessage> message =
+        CefProcessMessage::Create(query_message_name_);
+    CefRefPtr<CefListValue> args = message->GetArgumentList();
+    args->SetInt(0, context_id);
+    args->SetInt(1, request_id);
+    args->SetBool(2, false);  // Indicates a failure result.
+    args->SetInt(3, error_code);
+    args->SetString(4, error_message);
+    frame->SendProcessMessage(PID_RENDERER, message);
+  }
+
+  // Cancel a query that has not been sent to a handler.
+  void CancelUnhandledQuery(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            int context_id,
+                            int request_id) {
+    SendQueryFailure(browser, frame, context_id, request_id, kCanceledErrorCode,
+                     kCanceledErrorMessage);
+  }
+
+  // Cancel a query that has already been sent to a handler.
+  void CancelQuery(int64 query_id, QueryInfo* info, bool notify_renderer) {
+    if (notify_renderer)
+      SendQueryFailure(info, kCanceledErrorCode, kCanceledErrorMessage);
+
+    info->handler->OnQueryCanceled(info->browser, info->frame, query_id);
+
+    // Invalidate the callback.
+    info->callback->Detach();
+  }
+
+  // Cancel all pending queries associated with either |browser| or |handler|.
+  // If both |browser| and |handler| are NULL all pending queries will be
+  // canceled. Set |notify_renderer| to true if the renderer should be notified.
+  void CancelPendingFor(CefRefPtr<CefBrowser> browser,
+                        Handler* handler,
+                        bool notify_renderer) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      // Must execute on the UI thread.
+      CefPostTask(TID_UI,
+                  base::Bind(&CefMessageRouterBrowserSideImpl::CancelPendingFor,
+                             this, browser, handler, notify_renderer));
+      return;
+    }
+
+    if (browser_query_info_map_.empty())
+      return;
+
+    class Visitor : public BrowserQueryInfoMap::Visitor {
+     public:
+      Visitor(CefMessageRouterBrowserSideImpl* router,
+              Handler* handler,
+              bool notify_renderer)
+          : router_(router),
+            handler_(handler),
+            notify_renderer_(notify_renderer) {}
+
+      bool OnNextInfo(int browser_id,
+                      InfoIdType info_id,
+                      InfoObjectType info,
+                      bool* remove) OVERRIDE {
+        if (!handler_ || info->handler == handler_) {
+          *remove = true;
+          router_->CancelQuery(info_id, info, notify_renderer_);
+          delete info;
+        }
+        return true;
+      }
+
+     private:
+      CefMessageRouterBrowserSideImpl* router_;
+      Handler* handler_;
+      const bool notify_renderer_;
+    };
+
+    Visitor visitor(this, handler, notify_renderer);
+
+    if (browser.get()) {
+      // Cancel all queries associated with the specified browser.
+      browser_query_info_map_.FindAll(browser->GetIdentifier(), &visitor);
+    } else {
+      // Cancel all queries for all browsers.
+      browser_query_info_map_.FindAll(&visitor);
+    }
+  }
+
+  // Cancel a query based on the renderer-side IDs. If |request_id| is
+  // kReservedId all requests associated with |context_id| will be canceled.
+  void CancelPendingRequest(int browser_id, int context_id, int request_id) {
+    class Visitor : public BrowserQueryInfoMap::Visitor {
+     public:
+      Visitor(CefMessageRouterBrowserSideImpl* router,
+              int context_id,
+              int request_id)
+          : router_(router), context_id_(context_id), request_id_(request_id) {}
+
+      bool OnNextInfo(int browser_id,
+                      InfoIdType info_id,
+                      InfoObjectType info,
+                      bool* remove) OVERRIDE {
+        if (info->context_id == context_id_ &&
+            (request_id_ == kReservedId || info->request_id == request_id_)) {
+          *remove = true;
+          router_->CancelQuery(info_id, info, false);
+          delete info;
+
+          // Stop iterating if only canceling a single request.
+          return (request_id_ == kReservedId);
+        }
+        return true;
+      }
+
+     private:
+      CefMessageRouterBrowserSideImpl* router_;
+      const int context_id_;
+      const int request_id_;
+    };
+
+    Visitor visitor(this, context_id, request_id);
+    browser_query_info_map_.FindAll(browser_id, &visitor);
+  }
+
+  const CefMessageRouterConfig config_;
+  const std::string query_message_name_;
+  const std::string cancel_message_name_;
+
+  IdGenerator<int64> query_id_generator_;
+
+  // Set of currently registered handlers. An entry is added when a handler is
+  // registered and removed when a handler is unregistered.
+  typedef std::set<Handler*> HandlerSet;
+  HandlerSet handler_set_;
+
+  // Map of query ID to QueryInfo instance. An entry is added when a Handler
+  // indicates that it will handle the query and removed when either the query
+  // is completed via the Callback, the query is explicitly canceled from the
+  // renderer process, or the associated context is (or will be) released.
+  typedef CefBrowserInfoMap<int64, QueryInfo*> BrowserQueryInfoMap;
+  BrowserQueryInfoMap browser_query_info_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefMessageRouterBrowserSideImpl);
+};
+
+// Renderer-side router implementation.
+class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
+ public:
+  class V8HandlerImpl : public CefV8Handler {
+   public:
+    V8HandlerImpl(CefRefPtr<CefMessageRouterRendererSideImpl> router,
+                  const CefMessageRouterConfig& config)
+        : router_(router), config_(config), context_id_(kReservedId) {}
+
+    bool Execute(const CefString& name,
+                 CefRefPtr<CefV8Value> object,
+                 const CefV8ValueList& arguments,
+                 CefRefPtr<CefV8Value>& retval,
+                 CefString& exception) OVERRIDE {
+      if (name == config_.js_query_function) {
+        if (arguments.size() != 1 || !arguments[0]->IsObject()) {
+          exception = "Invalid arguments; expecting a single object";
+          return true;
+        }
+
+        CefRefPtr<CefV8Value> arg = arguments[0];
+
+        CefRefPtr<CefV8Value> requestVal = arg->GetValue(kMemberRequest);
+        if (!requestVal.get() || !requestVal->IsString()) {
+          exception = "Invalid arguments; object member '" +
+                      std::string(kMemberRequest) +
+                      "' is required and must have type string";
+          return true;
+        }
+
+        CefRefPtr<CefV8Value> successVal = nullptr;
+        if (arg->HasValue(kMemberOnSuccess)) {
+          successVal = arg->GetValue(kMemberOnSuccess);
+          if (!successVal->IsFunction()) {
+            exception = "Invalid arguments; object member '" +
+                        std::string(kMemberOnSuccess) +
+                        "' must have type function";
+            return true;
+          }
+        }
+
+        CefRefPtr<CefV8Value> failureVal = nullptr;
+        if (arg->HasValue(kMemberOnFailure)) {
+          failureVal = arg->GetValue(kMemberOnFailure);
+          if (!failureVal->IsFunction()) {
+            exception = "Invalid arguments; object member '" +
+                        std::string(kMemberOnFailure) +
+                        "' must have type function";
+            return true;
+          }
+        }
+
+        CefRefPtr<CefV8Value> persistentVal = nullptr;
+        if (arg->HasValue(kMemberPersistent)) {
+          persistentVal = arg->GetValue(kMemberPersistent);
+          if (!persistentVal->IsBool()) {
+            exception = "Invalid arguments; object member '" +
+                        std::string(kMemberPersistent) +
+                        "' must have type boolean";
+            return true;
+          }
+        }
+
+        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+        const int context_id = GetIDForContext(context);
+        const bool persistent =
+            (persistentVal.get() && persistentVal->GetBoolValue());
+
+        const int request_id = router_->SendQuery(
+            context->GetBrowser(), context->GetFrame(), context_id,
+            requestVal->GetStringValue(), persistent, successVal, failureVal);
+        retval = CefV8Value::CreateInt(request_id);
+        return true;
+      } else if (name == config_.js_cancel_function) {
+        if (arguments.size() != 1 || !arguments[0]->IsInt()) {
+          exception = "Invalid arguments; expecting a single integer";
+          return true;
+        }
+
+        bool result = false;
+        const int request_id = arguments[0]->GetIntValue();
+        if (request_id != kReservedId) {
+          CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+          const int context_id = GetIDForContext(context);
+          result =
+              router_->SendCancel(context->GetBrowser(), context->GetFrame(),
+                                  context_id, request_id);
+        }
+        retval = CefV8Value::CreateBool(result);
+        return true;
+      }
+
+      return false;
+    }
+
+   private:
+    // Don't create the context ID until it's actually needed.
+    int GetIDForContext(CefRefPtr<CefV8Context> context) {
+      if (context_id_ == kReservedId)
+        context_id_ = router_->CreateIDForContext(context);
+      return context_id_;
+    }
+
+    CefRefPtr<CefMessageRouterRendererSideImpl> router_;
+    const CefMessageRouterConfig config_;
+    int context_id_;
+
+    IMPLEMENT_REFCOUNTING(V8HandlerImpl);
+  };
+
+  explicit CefMessageRouterRendererSideImpl(
+      const CefMessageRouterConfig& config)
+      : config_(config),
+        query_message_name_(config.js_query_function.ToString() +
+                            kMessageSuffix),
+        cancel_message_name_(config.js_cancel_function.ToString() +
+                             kMessageSuffix) {}
+
+  virtual ~CefMessageRouterRendererSideImpl() {}
+
+  int GetPendingCount(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefV8Context> context) OVERRIDE {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    if (browser_request_info_map_.empty())
+      return 0;
+
+    if (context.get()) {
+      const int context_id = GetIDForContext(context, false);
+      if (context_id == kReservedId)
+        return 0;  // Nothing associated with the specified context.
+
+      // Need to iterate over each RequestInfo object to test the context.
+      class Visitor : public BrowserRequestInfoMap::Visitor {
+       public:
+        explicit Visitor(int context_id) : context_id_(context_id), count_(0) {}
+
+        bool OnNextInfo(int browser_id,
+                        InfoIdType info_id,
+                        InfoObjectType info,
+                        bool* remove) OVERRIDE {
+          if (info_id.first == context_id_)
+            count_++;
+          return true;
+        }
+
+        int count() const { return count_; }
+
+       private:
+        int context_id_;
+        int count_;
+      };
+
+      Visitor visitor(context_id);
+
+      if (browser.get()) {
+        // Count requests associated with the specified browser.
+        browser_request_info_map_.FindAll(browser->GetIdentifier(), &visitor);
+      } else {
+        // Count all requests for all browsers.
+        browser_request_info_map_.FindAll(&visitor);
+      }
+
+      return visitor.count();
+    } else if (browser.get()) {
+      return static_cast<int>(
+          browser_request_info_map_.size(browser->GetIdentifier()));
+    } else {
+      return static_cast<int>(browser_request_info_map_.size());
+    }
+
+    return 0;
+  }
+
+  void OnContextCreated(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefV8Context> context) OVERRIDE {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    // Register function handlers with the 'window' object.
+    CefRefPtr<CefV8Value> window = context->GetGlobal();
+
+    CefRefPtr<V8HandlerImpl> handler = new V8HandlerImpl(this, config_);
+    CefV8Value::PropertyAttribute attributes =
+        static_cast<CefV8Value::PropertyAttribute>(
+            V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM |
+            V8_PROPERTY_ATTRIBUTE_DONTDELETE);
+
+    // Add the query function.
+    CefRefPtr<CefV8Value> query_func =
+        CefV8Value::CreateFunction(config_.js_query_function, handler.get());
+    window->SetValue(config_.js_query_function, query_func, attributes);
+
+    // Add the cancel function.
+    CefRefPtr<CefV8Value> cancel_func =
+        CefV8Value::CreateFunction(config_.js_cancel_function, handler.get());
+    window->SetValue(config_.js_cancel_function, cancel_func, attributes);
+  }
+
+  void OnContextReleased(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefV8Context> context) OVERRIDE {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    // Get the context ID and remove the context from the map.
+    const int context_id = GetIDForContext(context, true);
+    if (context_id != kReservedId) {
+      // Cancel all pending requests for the context.
+      SendCancel(browser, frame, context_id, kReservedId);
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) OVERRIDE {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    const std::string& message_name = message->GetName();
+    if (message_name == query_message_name_) {
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      DCHECK_GT(args->GetSize(), 3U);
+
+      const int context_id = args->GetInt(0);
+      const int request_id = args->GetInt(1);
+      bool is_success = args->GetBool(2);
+
+      if (is_success) {
+        DCHECK_EQ(args->GetSize(), 4U);
+        const CefString& response = args->GetString(3);
+        CefPostTask(
+            TID_RENDERER,
+            base::Bind(
+                &CefMessageRouterRendererSideImpl::ExecuteSuccessCallback, this,
+                browser->GetIdentifier(), context_id, request_id, response));
+      } else {
+        DCHECK_EQ(args->GetSize(), 5U);
+        int error_code = args->GetInt(3);
+        const CefString& error_message = args->GetString(4);
+        CefPostTask(
+            TID_RENDERER,
+            base::Bind(
+                &CefMessageRouterRendererSideImpl::ExecuteFailureCallback, this,
+                browser->GetIdentifier(), context_id, request_id, error_code,
+                error_message));
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+
+ private:
+  // Structure representing a pending request.
+  struct RequestInfo {
+    // True if the request is persistent.
+    bool persistent;
+
+    // Success callback function. May be NULL.
+    CefRefPtr<CefV8Value> success_callback;
+
+    // Failure callback function. May be NULL.
+    CefRefPtr<CefV8Value> failure_callback;
+  };
+
+  // Retrieve a RequestInfo object from the map based on the renderer-side
+  // IDs. If |always_remove| is true then the RequestInfo object will always be
+  // removed from the map. Othewise, the RequestInfo object will only be removed
+  // if the query is non-persistent. If |removed| is true the caller is
+  // responsible for deleting the returned QueryInfo object.
+  RequestInfo* GetRequestInfo(int browser_id,
+                              int context_id,
+                              int request_id,
+                              bool always_remove,
+                              bool* removed) {
+    class Visitor : public BrowserRequestInfoMap::Visitor {
+     public:
+      explicit Visitor(bool always_remove)
+          : always_remove_(always_remove), removed_(false) {}
+
+      bool OnNextInfo(int browser_id,
+                      InfoIdType info_id,
+                      InfoObjectType info,
+                      bool* remove) OVERRIDE {
+        *remove = removed_ = (always_remove_ || !info->persistent);
+        return true;
+      }
+
+      bool removed() const { return removed_; }
+
+     private:
+      const bool always_remove_;
+      bool removed_;
+    };
+
+    Visitor visitor(always_remove);
+    RequestInfo* info = browser_request_info_map_.Find(
+        browser_id, std::make_pair(context_id, request_id), &visitor);
+    if (info)
+      *removed = visitor.removed();
+    return info;
+  }
+
+  // Returns the new request ID.
+  int SendQuery(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                int context_id,
+                const CefString& request,
+                bool persistent,
+                CefRefPtr<CefV8Value> success_callback,
+                CefRefPtr<CefV8Value> failure_callback) {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    const int request_id = request_id_generator_.GetNextId();
+
+    RequestInfo* info = new RequestInfo;
+    info->persistent = persistent;
+    info->success_callback = success_callback;
+    info->failure_callback = failure_callback;
+    browser_request_info_map_.Add(browser->GetIdentifier(),
+                                  std::make_pair(context_id, request_id), info);
+
+    CefRefPtr<CefProcessMessage> message =
+        CefProcessMessage::Create(query_message_name_);
+
+    CefRefPtr<CefListValue> args = message->GetArgumentList();
+    args->SetInt(0, context_id);
+    args->SetInt(1, request_id);
+    args->SetString(2, request);
+    args->SetBool(3, persistent);
+
+    frame->SendProcessMessage(PID_BROWSER, message);
+
+    return request_id;
+  }
+
+  // If |request_id| is kReservedId all requests associated with |context_id|
+  // will be canceled, otherwise only the specified |request_id| will be
+  // canceled. Returns true if any request was canceled.
+  bool SendCancel(CefRefPtr<CefBrowser> browser,
+                  CefRefPtr<CefFrame> frame,
+                  int context_id,
+                  int request_id) {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    const int browser_id = browser->GetIdentifier();
+
+    int cancel_count = 0;
+    if (request_id != kReservedId) {
+      // Cancel a single request.
+      bool removed;
+      RequestInfo* info =
+          GetRequestInfo(browser_id, context_id, request_id, true, &removed);
+      if (info) {
+        DCHECK(removed);
+        delete info;
+        cancel_count = 1;
+      }
+    } else {
+      // Cancel all requests with the specified context ID.
+      class Visitor : public BrowserRequestInfoMap::Visitor {
+       public:
+        explicit Visitor(int context_id)
+            : context_id_(context_id), cancel_count_(0) {}
+
+        bool OnNextInfo(int browser_id,
+                        InfoIdType info_id,
+                        InfoObjectType info,
+                        bool* remove) OVERRIDE {
+          if (info_id.first == context_id_) {
+            *remove = true;
+            delete info;
+            cancel_count_++;
+          }
+          return true;
+        }
+
+        int cancel_count() const { return cancel_count_; }
+
+       private:
+        const int context_id_;
+        int cancel_count_;
+      };
+
+      Visitor visitor(context_id);
+      browser_request_info_map_.FindAll(browser_id, &visitor);
+      cancel_count = visitor.cancel_count();
+    }
+
+    if (cancel_count > 0) {
+      CefRefPtr<CefProcessMessage> message =
+          CefProcessMessage::Create(cancel_message_name_);
+
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      args->SetInt(0, context_id);
+      args->SetInt(1, request_id);
+
+      frame->SendProcessMessage(PID_BROWSER, message);
+      return true;
+    }
+
+    return false;
+  }
+
+  // Execute the onSuccess JavaScript callback.
+  void ExecuteSuccessCallback(int browser_id,
+                              int context_id,
+                              int request_id,
+                              const CefString& response) {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    bool removed;
+    RequestInfo* info =
+        GetRequestInfo(browser_id, context_id, request_id, false, &removed);
+    if (!info)
+      return;
+
+    CefRefPtr<CefV8Context> context = GetContextByID(context_id);
+    if (context && info->success_callback) {
+      CefV8ValueList args;
+      args.push_back(CefV8Value::CreateString(response));
+      info->success_callback->ExecuteFunctionWithContext(context, nullptr,
+                                                         args);
+    }
+
+    if (removed)
+      delete info;
+  }
+
+  // Execute the onFailure JavaScript callback.
+  void ExecuteFailureCallback(int browser_id,
+                              int context_id,
+                              int request_id,
+                              int error_code,
+                              const CefString& error_message) {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    bool removed;
+    RequestInfo* info =
+        GetRequestInfo(browser_id, context_id, request_id, true, &removed);
+    if (!info)
+      return;
+
+    CefRefPtr<CefV8Context> context = GetContextByID(context_id);
+    if (context && info->failure_callback) {
+      CefV8ValueList args;
+      args.push_back(CefV8Value::CreateInt(error_code));
+      args.push_back(CefV8Value::CreateString(error_message));
+      info->failure_callback->ExecuteFunctionWithContext(context, nullptr,
+                                                         args);
+    }
+
+    DCHECK(removed);
+    delete info;
+  }
+
+  int CreateIDForContext(CefRefPtr<CefV8Context> context) {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    // The context should not already have an associated ID.
+    DCHECK_EQ(GetIDForContext(context, false), kReservedId);
+
+    const int context_id = context_id_generator_.GetNextId();
+    context_map_.insert(std::make_pair(context_id, context));
+    return context_id;
+  }
+
+  // Retrieves the existing ID value associated with the specified |context|.
+  // If |remove| is true the context will also be removed from the map.
+  int GetIDForContext(CefRefPtr<CefV8Context> context, bool remove) {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    ContextMap::iterator it = context_map_.begin();
+    for (; it != context_map_.end(); ++it) {
+      if (it->second->IsSame(context)) {
+        int context_id = it->first;
+        if (remove)
+          context_map_.erase(it);
+        return context_id;
+      }
+    }
+
+    return kReservedId;
+  }
+
+  CefRefPtr<CefV8Context> GetContextByID(int context_id) {
+    CEF_REQUIRE_RENDERER_THREAD();
+
+    ContextMap::const_iterator it = context_map_.find(context_id);
+    if (it != context_map_.end())
+      return it->second;
+    return nullptr;
+  }
+
+  const CefMessageRouterConfig config_;
+  const std::string query_message_name_;
+  const std::string cancel_message_name_;
+
+  IdGenerator<int> context_id_generator_;
+  IdGenerator<int> request_id_generator_;
+
+  // Map of (request ID, context ID) to RequestInfo for pending queries. An
+  // entry is added when a request is initiated via the bound function and
+  // removed when either the request completes, is canceled via the bound
+  // function, or the associated context is released.
+  typedef CefBrowserInfoMap<std::pair<int, int>, RequestInfo*>
+      BrowserRequestInfoMap;
+  BrowserRequestInfoMap browser_request_info_map_;
+
+  // Map of context ID to CefV8Context for existing contexts. An entry is added
+  // when a bound function is executed for the first time in the context and
+  // removed when the context is released.
+  typedef std::map<int, CefRefPtr<CefV8Context>> ContextMap;
+  ContextMap context_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefMessageRouterRendererSideImpl);
+};
+
+}  // namespace
+
+CefMessageRouterConfig::CefMessageRouterConfig()
+    : js_query_function("cefQuery"), js_cancel_function("cefQueryCancel") {}
+
+// static
+CefRefPtr<CefMessageRouterBrowserSide> CefMessageRouterBrowserSide::Create(
+    const CefMessageRouterConfig& config) {
+  CefMessageRouterConfig validated_config = config;
+  if (!ValidateConfig(validated_config))
+    return nullptr;
+  return new CefMessageRouterBrowserSideImpl(validated_config);
+}
+
+// static
+CefRefPtr<CefMessageRouterRendererSide> CefMessageRouterRendererSide::Create(
+    const CefMessageRouterConfig& config) {
+  CefMessageRouterConfig validated_config = config;
+  if (!ValidateConfig(validated_config))
+    return nullptr;
+  return new CefMessageRouterRendererSideImpl(validated_config);
+}
diff --git a/src/libcef_dll/wrapper/cef_resource_manager.cc b/src/libcef_dll/wrapper/cef_resource_manager.cc
new file mode 100644
index 0000000..0f5c647
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_resource_manager.cc
@@ -0,0 +1,748 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_resource_manager.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "include/base/cef_macros.h"
+#include "include/base/cef_weak_ptr.h"
+#include "include/cef_parser.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "include/wrapper/cef_zip_archive.h"
+
+namespace {
+
+#if defined(OS_WIN)
+#define PATH_SEP '\\'
+#else
+#define PATH_SEP '/'
+#endif
+
+// Returns |url| without the query or fragment components, if any.
+std::string GetUrlWithoutQueryOrFragment(const std::string& url) {
+  // Find the first instance of '?' or '#'.
+  const size_t pos = std::min(url.find('?'), url.find('#'));
+  if (pos != std::string::npos)
+    return url.substr(0, pos);
+
+  return url;
+}
+
+// Determine the mime type based on the |url| file extension.
+std::string GetMimeType(const std::string& url) {
+  std::string mime_type;
+  const std::string& url_without_query = GetUrlWithoutQueryOrFragment(url);
+  size_t sep = url_without_query.find_last_of(".");
+  if (sep != std::string::npos) {
+    mime_type = CefGetMimeType(url_without_query.substr(sep + 1));
+    if (!mime_type.empty())
+      return mime_type;
+  }
+  return "text/html";
+}
+
+// Default no-op filter.
+std::string GetFilteredUrl(const std::string& url) {
+  return url;
+}
+
+// Provider of fixed contents.
+class ContentProvider : public CefResourceManager::Provider {
+ public:
+  ContentProvider(const std::string& url,
+                  const std::string& content,
+                  const std::string& mime_type)
+      : url_(url), content_(content), mime_type_(mime_type) {
+    DCHECK(!url.empty());
+    DCHECK(!content.empty());
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+
+    const std::string& url = request->url();
+    if (url != url_) {
+      // Not handled by this provider.
+      return false;
+    }
+
+    CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+        static_cast<void*>(const_cast<char*>(content_.data())),
+        content_.length());
+
+    // Determine the mime type a single time if it isn't already set.
+    if (mime_type_.empty())
+      mime_type_ = request->mime_type_resolver().Run(url);
+
+    request->Continue(new CefStreamResourceHandler(mime_type_, stream));
+    return true;
+  }
+
+ private:
+  std::string url_;
+  std::string content_;
+  std::string mime_type_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentProvider);
+};
+
+// Provider of contents loaded from a directory on the file system.
+class DirectoryProvider : public CefResourceManager::Provider {
+ public:
+  DirectoryProvider(const std::string& url_path,
+                    const std::string& directory_path)
+      : url_path_(url_path), directory_path_(directory_path) {
+    DCHECK(!url_path_.empty());
+    DCHECK(!directory_path_.empty());
+
+    // Normalize the path values.
+    if (url_path_[url_path_.size() - 1] != '/')
+      url_path_ += '/';
+    if (directory_path_[directory_path_.size() - 1] != PATH_SEP)
+      directory_path_ += PATH_SEP;
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+
+    const std::string& url = request->url();
+    if (url.find(url_path_) != 0U) {
+      return false;
+    }
+
+    const std::string& file_path = GetFilePath(url);
+
+    // Open |file_path| on the FILE thread.
+    CefPostTask(TID_FILE, base::Bind(&DirectoryProvider::OpenOnFileThread,
+                                     file_path, request));
+
+    return true;
+  }
+
+ private:
+  std::string GetFilePath(const std::string& url) {
+    std::string path_part = url.substr(url_path_.length());
+#if defined(OS_WIN)
+    std::replace(path_part.begin(), path_part.end(), '/', '\\');
+#endif
+    return directory_path_ + path_part;
+  }
+
+  static void OpenOnFileThread(
+      const std::string& file_path,
+      scoped_refptr<CefResourceManager::Request> request) {
+    CEF_REQUIRE_FILE_THREAD();
+
+    CefRefPtr<CefStreamReader> stream =
+        CefStreamReader::CreateForFile(file_path);
+
+    // Continue loading on the IO thread.
+    CefPostTask(TID_IO, base::Bind(&DirectoryProvider::ContinueOpenOnIOThread,
+                                   request, stream));
+  }
+
+  static void ContinueOpenOnIOThread(
+      scoped_refptr<CefResourceManager::Request> request,
+      CefRefPtr<CefStreamReader> stream) {
+    CEF_REQUIRE_IO_THREAD();
+
+    CefRefPtr<CefStreamResourceHandler> handler;
+    if (stream.get()) {
+      handler = new CefStreamResourceHandler(
+          request->mime_type_resolver().Run(request->url()), stream);
+    }
+    request->Continue(handler);
+  }
+
+  std::string url_path_;
+  std::string directory_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(DirectoryProvider);
+};
+
+// Provider of contents loaded from an archive file.
+class ArchiveProvider : public CefResourceManager::Provider {
+ public:
+  ArchiveProvider(const std::string& url_path,
+                  const std::string& archive_path,
+                  const std::string& password)
+      : url_path_(url_path),
+        archive_path_(archive_path),
+        password_(password),
+        archive_load_started_(false),
+        archive_load_ended_(false),
+        ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+    DCHECK(!url_path_.empty());
+    DCHECK(!archive_path_.empty());
+
+    // Normalize the path values.
+    if (url_path_[url_path_.size() - 1] != '/')
+      url_path_ += '/';
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+
+    const std::string& url = request->url();
+    if (url.find(url_path_) != 0U) {
+      // Not handled by this provider.
+      return false;
+    }
+
+    if (!archive_load_started_) {
+      // Initiate archive loading and queue the pending request.
+      archive_load_started_ = true;
+      pending_requests_.push_back(request);
+
+      // Load the archive file on the FILE thread.
+      CefPostTask(TID_FILE, base::Bind(&ArchiveProvider::LoadOnFileThread,
+                                       weak_ptr_factory_.GetWeakPtr(),
+                                       archive_path_, password_));
+      return true;
+    }
+
+    if (archive_load_started_ && !archive_load_ended_) {
+      // The archive load has already started. Queue the pending request.
+      pending_requests_.push_back(request);
+      return true;
+    }
+
+    // Archive loading is done.
+    return ContinueRequest(request);
+  }
+
+ private:
+  static void LoadOnFileThread(base::WeakPtr<ArchiveProvider> ptr,
+                               const std::string& archive_path,
+                               const std::string& password) {
+    CEF_REQUIRE_FILE_THREAD();
+
+    CefRefPtr<CefZipArchive> archive;
+
+    CefRefPtr<CefStreamReader> stream =
+        CefStreamReader::CreateForFile(archive_path);
+    if (stream.get()) {
+      archive = new CefZipArchive;
+      if (archive->Load(stream, password, true) == 0) {
+        DLOG(WARNING) << "Empty archive file: " << archive_path;
+        archive = nullptr;
+      }
+    } else {
+      DLOG(WARNING) << "Failed to load archive file: " << archive_path;
+    }
+
+    CefPostTask(TID_IO,
+                base::Bind(&ArchiveProvider::ContinueOnIOThread, ptr, archive));
+  }
+
+  void ContinueOnIOThread(CefRefPtr<CefZipArchive> archive) {
+    CEF_REQUIRE_IO_THREAD();
+
+    archive_load_ended_ = true;
+    archive_ = archive;
+
+    if (!pending_requests_.empty()) {
+      // Continue all pending requests.
+      PendingRequests::const_iterator it = pending_requests_.begin();
+      for (; it != pending_requests_.end(); ++it)
+        ContinueRequest(*it);
+      pending_requests_.clear();
+    }
+  }
+
+  bool ContinueRequest(scoped_refptr<CefResourceManager::Request> request) {
+    CefRefPtr<CefResourceHandler> handler;
+
+    // |archive_| will be NULL if the archive file failed to load or was empty.
+    if (archive_.get()) {
+      const std::string& url = request->url();
+      const std::string& relative_path = url.substr(url_path_.length());
+      CefRefPtr<CefZipArchive::File> file = archive_->GetFile(relative_path);
+      if (file.get()) {
+        handler = new CefStreamResourceHandler(
+            request->mime_type_resolver().Run(url), file->GetStreamReader());
+      }
+    }
+
+    if (!handler.get())
+      return false;
+
+    request->Continue(handler);
+    return true;
+  }
+
+  std::string url_path_;
+  std::string archive_path_;
+  std::string password_;
+
+  bool archive_load_started_;
+  bool archive_load_ended_;
+  CefRefPtr<CefZipArchive> archive_;
+
+  // List of requests that are pending while the archive is being loaded.
+  typedef std::vector<scoped_refptr<CefResourceManager::Request>>
+      PendingRequests;
+  PendingRequests pending_requests_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<ArchiveProvider> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArchiveProvider);
+};
+
+}  // namespace
+
+// CefResourceManager::ProviderEntry implementation.
+
+struct CefResourceManager::ProviderEntry {
+  ProviderEntry(Provider* provider, int order, const std::string& identifier)
+      : provider_(provider),
+        order_(order),
+        identifier_(identifier),
+        deletion_pending_(false) {}
+
+  scoped_ptr<Provider> provider_;
+  int order_;
+  std::string identifier_;
+
+  // List of pending requests currently associated with this provider.
+  RequestList pending_requests_;
+
+  // True if deletion of this provider is pending.
+  bool deletion_pending_;
+};
+
+// CefResourceManager::RequestState implementation.
+
+CefResourceManager::RequestState::~RequestState() {
+  // Always execute the callback.
+  if (callback_.get())
+    callback_->Continue(true);
+}
+
+// CefResourceManager::Request implementation.
+
+void CefResourceManager::Request::Continue(
+    CefRefPtr<CefResourceHandler> handler) {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::Continue, this,
+                                   handler));
+    return;
+  }
+
+  if (!state_.get())
+    return;
+
+  // Disassociate |state_| immediately so that Provider::OnRequestCanceled is
+  // not called unexpectedly if Provider::OnRequest calls this method and then
+  // calls CefResourceManager::Remove*.
+  CefPostTask(TID_IO,
+              base::Bind(&CefResourceManager::Request::ContinueOnIOThread,
+                         base::Passed(&state_), handler));
+}
+
+void CefResourceManager::Request::Stop() {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::Stop, this));
+    return;
+  }
+
+  if (!state_.get())
+    return;
+
+  // Disassociate |state_| immediately so that Provider::OnRequestCanceled is
+  // not called unexpectedly if Provider::OnRequest calls this method and then
+  // calls CefResourceManager::Remove*.
+  CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::StopOnIOThread,
+                                 base::Passed(&state_)));
+}
+
+CefResourceManager::Request::Request(scoped_ptr<RequestState> state)
+    : state_(state.Pass()), params_(state_->params_) {
+  CEF_REQUIRE_IO_THREAD();
+
+  ProviderEntry* entry = *(state_->current_entry_pos_);
+  // Should not be on a deleted entry.
+  DCHECK(!entry->deletion_pending_);
+
+  // Add this request to the entry's pending request list.
+  entry->pending_requests_.push_back(this);
+  state_->current_request_pos_ = --entry->pending_requests_.end();
+}
+
+// Detaches and returns |state_| if the provider indicates that it will not
+// handle the request. Note that |state_| may already be NULL if OnRequest
+// executes a callback before returning, in which case execution will continue
+// asynchronously in any case.
+scoped_ptr<CefResourceManager::RequestState>
+CefResourceManager::Request::SendRequest() {
+  CEF_REQUIRE_IO_THREAD();
+  Provider* provider = (*state_->current_entry_pos_)->provider_.get();
+  if (!provider->OnRequest(this))
+    return state_.Pass();
+  return scoped_ptr<RequestState>();
+}
+
+bool CefResourceManager::Request::HasState() {
+  CEF_REQUIRE_IO_THREAD();
+  return (state_.get() != nullptr);
+}
+
+// static
+void CefResourceManager::Request::ContinueOnIOThread(
+    scoped_ptr<RequestState> state,
+    CefRefPtr<CefResourceHandler> handler) {
+  CEF_REQUIRE_IO_THREAD();
+  // The manager may already have been deleted.
+  base::WeakPtr<CefResourceManager> manager = state->manager_;
+  if (manager)
+    manager->ContinueRequest(state.Pass(), handler);
+}
+
+// static
+void CefResourceManager::Request::StopOnIOThread(
+    scoped_ptr<RequestState> state) {
+  CEF_REQUIRE_IO_THREAD();
+  // The manager may already have been deleted.
+  base::WeakPtr<CefResourceManager> manager = state->manager_;
+  if (manager)
+    manager->StopRequest(state.Pass());
+}
+
+// CefResourceManager implementation.
+
+CefResourceManager::CefResourceManager()
+    : url_filter_(base::Bind(GetFilteredUrl)),
+      mime_type_resolver_(base::Bind(GetMimeType)) {}
+
+CefResourceManager::~CefResourceManager() {
+  CEF_REQUIRE_IO_THREAD();
+  RemoveAllProviders();
+
+  // Delete all entryies now. Requests may still be pending but they will not
+  // call back into this manager due to the use of WeakPtr.
+  if (!providers_.empty()) {
+    ProviderEntryList::iterator it = providers_.begin();
+    for (; it != providers_.end(); ++it)
+      delete *it;
+    providers_.clear();
+  }
+}
+
+void CefResourceManager::AddContentProvider(const std::string& url,
+                                            const std::string& content,
+                                            const std::string& mime_type,
+                                            int order,
+                                            const std::string& identifier) {
+  AddProvider(new ContentProvider(url, content, mime_type), order, identifier);
+}
+
+void CefResourceManager::AddDirectoryProvider(const std::string& url_path,
+                                              const std::string& directory_path,
+                                              int order,
+                                              const std::string& identifier) {
+  AddProvider(new DirectoryProvider(url_path, directory_path), order,
+              identifier);
+}
+
+void CefResourceManager::AddArchiveProvider(const std::string& url_path,
+                                            const std::string& archive_path,
+                                            const std::string& password,
+                                            int order,
+                                            const std::string& identifier) {
+  AddProvider(new ArchiveProvider(url_path, archive_path, password), order,
+              identifier);
+}
+
+void CefResourceManager::AddProvider(Provider* provider,
+                                     int order,
+                                     const std::string& identifier) {
+  DCHECK(provider);
+  if (!provider)
+    return;
+
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO, base::Bind(&CefResourceManager::AddProvider, this,
+                                   provider, order, identifier));
+    return;
+  }
+
+  scoped_ptr<ProviderEntry> new_entry(
+      new ProviderEntry(provider, order, identifier));
+
+  if (providers_.empty()) {
+    providers_.push_back(new_entry.release());
+    return;
+  }
+
+  // Insert before the first entry with a higher |order| value.
+  ProviderEntryList::iterator it = providers_.begin();
+  for (; it != providers_.end(); ++it) {
+    if ((*it)->order_ > order)
+      break;
+  }
+
+  providers_.insert(it, new_entry.release());
+}
+
+void CefResourceManager::RemoveProviders(const std::string& identifier) {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO, base::Bind(&CefResourceManager::RemoveProviders, this,
+                                   identifier));
+    return;
+  }
+
+  if (providers_.empty())
+    return;
+
+  ProviderEntryList::iterator it = providers_.begin();
+  while (it != providers_.end()) {
+    if ((*it)->identifier_ == identifier)
+      DeleteProvider(it, false);
+    else
+      ++it;
+  }
+}
+
+void CefResourceManager::RemoveAllProviders() {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO,
+                base::Bind(&CefResourceManager::RemoveAllProviders, this));
+    return;
+  }
+
+  if (providers_.empty())
+    return;
+
+  ProviderEntryList::iterator it = providers_.begin();
+  while (it != providers_.end())
+    DeleteProvider(it, true);
+}
+
+void CefResourceManager::SetMimeTypeResolver(const MimeTypeResolver& resolver) {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO, base::Bind(&CefResourceManager::SetMimeTypeResolver,
+                                   this, resolver));
+    return;
+  }
+
+  if (!resolver.is_null())
+    mime_type_resolver_ = resolver;
+  else
+    mime_type_resolver_ = base::Bind(GetMimeType);
+}
+
+void CefResourceManager::SetUrlFilter(const UrlFilter& filter) {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO,
+                base::Bind(&CefResourceManager::SetUrlFilter, this, filter));
+    return;
+  }
+
+  if (!filter.is_null())
+    url_filter_ = filter;
+  else
+    url_filter_ = base::Bind(GetFilteredUrl);
+}
+
+cef_return_value_t CefResourceManager::OnBeforeResourceLoad(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefRequestCallback> callback) {
+  CEF_REQUIRE_IO_THREAD();
+
+  // Find the first provider that is not pending deletion.
+  ProviderEntryList::iterator current_entry_pos = providers_.begin();
+  GetNextValidProvider(current_entry_pos);
+
+  if (current_entry_pos == providers_.end()) {
+    // No providers so continue the request immediately.
+    return RV_CONTINUE;
+  }
+
+  scoped_ptr<RequestState> state(new RequestState);
+
+  if (!weak_ptr_factory_.get()) {
+    // WeakPtrFactory instances need to be created and destroyed on the same
+    // thread. This object performs most of its work on the IO thread and will
+    // be destroyed on the IO thread so, now that we're on the IO thread,
+    // properly initialize the WeakPtrFactory.
+    weak_ptr_factory_.reset(new base::WeakPtrFactory<CefResourceManager>(this));
+  }
+
+  state->manager_ = weak_ptr_factory_->GetWeakPtr();
+  state->callback_ = callback;
+
+  state->params_.url_ =
+      GetUrlWithoutQueryOrFragment(url_filter_.Run(request->GetURL()));
+  state->params_.browser_ = browser;
+  state->params_.frame_ = frame;
+  state->params_.request_ = request;
+  state->params_.url_filter_ = url_filter_;
+  state->params_.mime_type_resolver_ = mime_type_resolver_;
+
+  state->current_entry_pos_ = current_entry_pos;
+
+  // If the request is potentially handled we need to continue asynchronously.
+  return SendRequest(state.Pass()) ? RV_CONTINUE_ASYNC : RV_CONTINUE;
+}
+
+CefRefPtr<CefResourceHandler> CefResourceManager::GetResourceHandler(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request) {
+  CEF_REQUIRE_IO_THREAD();
+
+  if (pending_handlers_.empty())
+    return nullptr;
+
+  CefRefPtr<CefResourceHandler> handler;
+
+  PendingHandlersMap::iterator it =
+      pending_handlers_.find(request->GetIdentifier());
+  if (it != pending_handlers_.end()) {
+    handler = it->second;
+    pending_handlers_.erase(it);
+  }
+
+  return handler;
+}
+
+// Send the request to providers in order until one potentially handles it or we
+// run out of providers. Returns true if the request is potentially handled.
+bool CefResourceManager::SendRequest(scoped_ptr<RequestState> state) {
+  bool potentially_handled = false;
+
+  do {
+    // Should not be on the last provider entry.
+    DCHECK(state->current_entry_pos_ != providers_.end());
+    scoped_refptr<Request> request = new Request(state.Pass());
+
+    // Give the provider an opportunity to handle the request.
+    state = request->SendRequest();
+    if (state.get()) {
+      // The provider will not handle the request. Move to the next provider if
+      // any.
+      if (!IncrementProvider(state.get()))
+        StopRequest(state.Pass());
+    } else {
+      potentially_handled = true;
+    }
+  } while (state.get());
+
+  return potentially_handled;
+}
+
+void CefResourceManager::ContinueRequest(
+    scoped_ptr<RequestState> state,
+    CefRefPtr<CefResourceHandler> handler) {
+  CEF_REQUIRE_IO_THREAD();
+
+  if (handler.get()) {
+    // The request has been handled. Associate the request ID with the handler.
+    pending_handlers_.insert(
+        std::make_pair(state->params_.request_->GetIdentifier(), handler));
+    StopRequest(state.Pass());
+  } else {
+    // Move to the next provider if any.
+    if (IncrementProvider(state.get()))
+      SendRequest(state.Pass());
+    else
+      StopRequest(state.Pass());
+  }
+}
+
+void CefResourceManager::StopRequest(scoped_ptr<RequestState> state) {
+  CEF_REQUIRE_IO_THREAD();
+
+  // Detach from the current provider.
+  DetachRequestFromProvider(state.get());
+
+  // Delete the state object and execute the callback.
+  state.reset();
+}
+
+// Move state to the next provider if any and return true if there are more
+// providers.
+bool CefResourceManager::IncrementProvider(RequestState* state) {
+  // Identify the next provider.
+  ProviderEntryList::iterator next_entry_pos = state->current_entry_pos_;
+  GetNextValidProvider(++next_entry_pos);
+
+  // Detach from the current provider.
+  DetachRequestFromProvider(state);
+
+  if (next_entry_pos != providers_.end()) {
+    // Update the state to reference the new provider entry.
+    state->current_entry_pos_ = next_entry_pos;
+    return true;
+  }
+
+  return false;
+}
+
+// The new provider, if any, should be determined before calling this method.
+void CefResourceManager::DetachRequestFromProvider(RequestState* state) {
+  if (state->current_entry_pos_ != providers_.end()) {
+    // Remove the association from the current provider entry.
+    ProviderEntryList::iterator current_entry_pos = state->current_entry_pos_;
+    ProviderEntry* current_entry = *(current_entry_pos);
+    current_entry->pending_requests_.erase(state->current_request_pos_);
+
+    if (current_entry->deletion_pending_ &&
+        current_entry->pending_requests_.empty()) {
+      // Delete the current provider entry now.
+      providers_.erase(current_entry_pos);
+      delete current_entry;
+    }
+
+    // Set to the end for error checking purposes.
+    state->current_entry_pos_ = providers_.end();
+  }
+}
+
+// Move to the next provider that is not pending deletion.
+void CefResourceManager::GetNextValidProvider(
+    ProviderEntryList::iterator& iterator) {
+  while (iterator != providers_.end() && (*iterator)->deletion_pending_) {
+    ++iterator;
+  }
+}
+
+void CefResourceManager::DeleteProvider(ProviderEntryList::iterator& iterator,
+                                        bool stop) {
+  CEF_REQUIRE_IO_THREAD();
+
+  ProviderEntry* current_entry = *(iterator);
+
+  if (current_entry->deletion_pending_)
+    return;
+
+  if (!current_entry->pending_requests_.empty()) {
+    // Don't delete the provider entry until all pending requests have cleared.
+    current_entry->deletion_pending_ = true;
+
+    // Continue pending requests immediately.
+    RequestList::iterator it = current_entry->pending_requests_.begin();
+    for (; it != current_entry->pending_requests_.end(); ++it) {
+      const scoped_refptr<Request>& request = *it;
+      if (request->HasState()) {
+        if (stop)
+          request->Stop();
+        else
+          request->Continue(nullptr);
+        current_entry->provider_->OnRequestCanceled(request);
+      }
+    }
+
+    ++iterator;
+  } else {
+    // Delete the provider entry now.
+    iterator = providers_.erase(iterator);
+    delete current_entry;
+  }
+}
diff --git a/src/libcef_dll/wrapper/cef_scoped_temp_dir.cc b/src/libcef_dll/wrapper/cef_scoped_temp_dir.cc
new file mode 100644
index 0000000..9269aba
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_scoped_temp_dir.cc
@@ -0,0 +1,86 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2011 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "include/wrapper/cef_scoped_temp_dir.h"
+
+#include "include/base/cef_logging.h"
+#include "include/cef_file_util.h"
+
+CefScopedTempDir::CefScopedTempDir() {}
+
+CefScopedTempDir::~CefScopedTempDir() {
+  if (!path_.empty() && !Delete())
+    DLOG(WARNING) << "Could not delete temp dir in dtor.";
+}
+
+bool CefScopedTempDir::CreateUniqueTempDir() {
+  if (!path_.empty())
+    return false;
+
+  // This "scoped_dir" prefix is only used on Windows and serves as a template
+  // for the unique name.
+  if (!CefCreateNewTempDirectory("scoped_dir", path_))
+    return false;
+
+  return true;
+}
+
+bool CefScopedTempDir::CreateUniqueTempDirUnderPath(
+    const CefString& base_path) {
+  if (!path_.empty())
+    return false;
+
+  // If |base_path| does not exist, create it.
+  if (!CefCreateDirectory(base_path))
+    return false;
+
+  // Create a new, uniquely named directory under |base_path|.
+  if (!CefCreateTempDirectoryInDirectory(base_path, "scoped_dir_", path_))
+    return false;
+
+  return true;
+}
+
+bool CefScopedTempDir::Set(const CefString& path) {
+  if (!path_.empty())
+    return false;
+
+  if (!CefDirectoryExists(path) && !CefCreateDirectory(path))
+    return false;
+
+  path_ = path;
+  return true;
+}
+
+bool CefScopedTempDir::Delete() {
+  if (path_.empty())
+    return false;
+
+  bool ret = CefDeleteFile(path_, true);
+  if (ret) {
+    // We only clear the path if deleted the directory.
+    path_.clear();
+  }
+
+  return ret;
+}
+
+CefString CefScopedTempDir::Take() {
+  CefString ret = path_;
+  path_.clear();
+  return ret;
+}
+
+const CefString& CefScopedTempDir::GetPath() const {
+  DCHECK(!path_.empty()) << "Did you call CreateUniqueTempDir* before?";
+  return path_;
+}
+
+bool CefScopedTempDir::IsEmpty() const {
+  return path_.empty();
+}
+
+bool CefScopedTempDir::IsValid() const {
+  return !path_.empty() && CefDirectoryExists(path_);
+}
diff --git a/src/libcef_dll/wrapper/cef_stream_resource_handler.cc b/src/libcef_dll/wrapper/cef_stream_resource_handler.cc
new file mode 100644
index 0000000..ad5a34f
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_stream_resource_handler.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_stream_resource_handler.h"
+
+#include <algorithm>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_task.h"
+#include "include/wrapper/cef_helpers.h"
+
+CefStreamResourceHandler::CefStreamResourceHandler(
+    const CefString& mime_type,
+    CefRefPtr<CefStreamReader> stream)
+    : status_code_(200),
+      status_text_("OK"),
+      mime_type_(mime_type),
+      stream_(stream) {
+  DCHECK(!mime_type_.empty());
+  DCHECK(stream_.get());
+}
+
+CefStreamResourceHandler::CefStreamResourceHandler(
+    int status_code,
+    const CefString& status_text,
+    const CefString& mime_type,
+    CefResponse::HeaderMap header_map,
+    CefRefPtr<CefStreamReader> stream)
+    : status_code_(status_code),
+      status_text_(status_text),
+      mime_type_(mime_type),
+      header_map_(header_map),
+      stream_(stream) {
+  DCHECK(!mime_type_.empty());
+  DCHECK(stream_.get());
+}
+
+bool CefStreamResourceHandler::Open(CefRefPtr<CefRequest> request,
+                                    bool& handle_request,
+                                    CefRefPtr<CefCallback> callback) {
+  DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
+
+  // Continue the request immediately.
+  handle_request = true;
+  return true;
+}
+
+void CefStreamResourceHandler::GetResponseHeaders(
+    CefRefPtr<CefResponse> response,
+    int64& response_length,
+    CefString& redirectUrl) {
+  CEF_REQUIRE_IO_THREAD();
+
+  response->SetStatus(status_code_);
+  response->SetStatusText(status_text_);
+  response->SetMimeType(mime_type_);
+
+  if (!header_map_.empty())
+    response->SetHeaderMap(header_map_);
+
+  response_length = -1;
+}
+
+bool CefStreamResourceHandler::Read(
+    void* data_out,
+    int bytes_to_read,
+    int& bytes_read,
+    CefRefPtr<CefResourceReadCallback> callback) {
+  DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
+  DCHECK_GT(bytes_to_read, 0);
+
+  // Read until the buffer is full or until Read() returns 0 to indicate no
+  // more data.
+  bytes_read = 0;
+  int read = 0;
+  do {
+    read = static_cast<int>(
+        stream_->Read(static_cast<char*>(data_out) + bytes_read, 1,
+                      bytes_to_read - bytes_read));
+    bytes_read += read;
+  } while (read != 0 && bytes_read < bytes_to_read);
+
+  return (bytes_read > 0);
+}
+
+void CefStreamResourceHandler::Cancel() {}
diff --git a/src/libcef_dll/wrapper/cef_xml_object.cc b/src/libcef_dll/wrapper/cef_xml_object.cc
new file mode 100644
index 0000000..d6fc439
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_xml_object.cc
@@ -0,0 +1,451 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_xml_object.h"
+
+#include <sstream>
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/cef_stream.h"
+
+namespace {
+
+class CefXmlObjectLoader {
+ public:
+  explicit CefXmlObjectLoader(CefRefPtr<CefXmlObject> root_object)
+      : root_object_(root_object) {}
+
+  bool Load(CefRefPtr<CefStreamReader> stream,
+            CefXmlReader::EncodingType encodingType,
+            const CefString& URI) {
+    CefRefPtr<CefXmlReader> reader(
+        CefXmlReader::Create(stream, encodingType, URI));
+    if (!reader.get())
+      return false;
+
+    bool ret = reader->MoveToNextNode();
+    if (ret) {
+      CefRefPtr<CefXmlObject> cur_object(root_object_), new_object;
+      CefXmlObject::ObjectVector queue;
+      int cur_depth, value_depth = -1;
+      CefXmlReader::NodeType cur_type;
+      std::stringstream cur_value;
+      bool last_has_ns = false;
+
+      queue.push_back(root_object_);
+
+      do {
+        cur_depth = reader->GetDepth();
+        if (value_depth >= 0 && cur_depth > value_depth) {
+          // The current node has already been parsed as part of a value.
+          continue;
+        }
+
+        cur_type = reader->GetType();
+        if (cur_type == XML_NODE_ELEMENT_START) {
+          if (cur_depth == value_depth) {
+            // Add to the current value.
+            cur_value << std::string(reader->GetOuterXml());
+            continue;
+          } else if (last_has_ns && reader->GetPrefix().empty()) {
+            if (!cur_object->HasChildren()) {
+              // Start a new value because the last element has a namespace and
+              // this element does not.
+              value_depth = cur_depth;
+              cur_value << std::string(reader->GetOuterXml());
+            } else {
+              // Value following a child element is not allowed.
+              std::stringstream ss;
+              ss << "Value following child element, line "
+                 << reader->GetLineNumber();
+              load_error_ = ss.str();
+              ret = false;
+              break;
+            }
+          } else {
+            // Start a new element.
+            new_object = new CefXmlObject(reader->GetQualifiedName());
+            cur_object->AddChild(new_object);
+            last_has_ns = !reader->GetPrefix().empty();
+
+            if (!reader->IsEmptyElement()) {
+              // The new element potentially has a value and/or children, so
+              // set the current object and add the object to the queue.
+              cur_object = new_object;
+              queue.push_back(cur_object);
+            }
+
+            if (reader->HasAttributes() && reader->MoveToFirstAttribute()) {
+              // Read all object attributes.
+              do {
+                new_object->SetAttributeValue(reader->GetQualifiedName(),
+                                              reader->GetValue());
+              } while (reader->MoveToNextAttribute());
+              reader->MoveToCarryingElement();
+            }
+          }
+        } else if (cur_type == XML_NODE_ELEMENT_END) {
+          if (cur_depth == value_depth) {
+            // Ending an element that is already in the value.
+            continue;
+          } else if (cur_depth < value_depth) {
+            // Done with parsing the value portion of the current element.
+            cur_object->SetValue(cur_value.str());
+            cur_value.str("");
+            value_depth = -1;
+          }
+
+          // Pop the current element from the queue.
+          queue.pop_back();
+
+          if (queue.empty() ||
+              cur_object->GetName() != reader->GetQualifiedName()) {
+            // Open tag without close tag or close tag without open tag should
+            // never occur (the parser catches this error).
+            NOTREACHED();
+            std::stringstream ss;
+            ss << "Mismatched end tag for "
+               << std::string(cur_object->GetName()) << ", line "
+               << reader->GetLineNumber();
+            load_error_ = ss.str();
+            ret = false;
+            break;
+          }
+
+          // Set the current object to the previous object in the queue.
+          cur_object = queue.back().get();
+        } else if (cur_type == XML_NODE_TEXT || cur_type == XML_NODE_CDATA ||
+                   cur_type == XML_NODE_ENTITY_REFERENCE) {
+          if (cur_depth == value_depth) {
+            // Add to the current value.
+            cur_value << std::string(reader->GetValue());
+          } else if (!cur_object->HasChildren()) {
+            // Start a new value.
+            value_depth = cur_depth;
+            cur_value << std::string(reader->GetValue());
+          } else {
+            // Value following a child element is not allowed.
+            std::stringstream ss;
+            ss << "Value following child element, line "
+               << reader->GetLineNumber();
+            load_error_ = ss.str();
+            ret = false;
+            break;
+          }
+        }
+      } while (reader->MoveToNextNode());
+    }
+
+    if (reader->HasError()) {
+      load_error_ = reader->GetError();
+      return false;
+    }
+
+    return ret;
+  }
+
+  CefString GetLoadError() { return load_error_; }
+
+ private:
+  CefString load_error_;
+  CefRefPtr<CefXmlObject> root_object_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefXmlObjectLoader);
+};
+
+}  // namespace
+
+CefXmlObject::CefXmlObject(const CefString& name)
+    : name_(name), parent_(nullptr) {}
+
+CefXmlObject::~CefXmlObject() {}
+
+bool CefXmlObject::Load(CefRefPtr<CefStreamReader> stream,
+                        CefXmlReader::EncodingType encodingType,
+                        const CefString& URI,
+                        CefString* loadError) {
+  Clear();
+
+  CefXmlObjectLoader loader(this);
+  if (!loader.Load(stream, encodingType, URI)) {
+    if (loadError)
+      *loadError = loader.GetLoadError();
+    return false;
+  }
+  return true;
+}
+
+void CefXmlObject::Set(CefRefPtr<CefXmlObject> object) {
+  DCHECK(object.get());
+
+  Clear();
+
+  name_ = object->GetName();
+  Append(object, true);
+}
+
+void CefXmlObject::Append(CefRefPtr<CefXmlObject> object,
+                          bool overwriteAttributes) {
+  DCHECK(object.get());
+
+  if (object->HasChildren()) {
+    ObjectVector children;
+    object->GetChildren(children);
+    ObjectVector::const_iterator it = children.begin();
+    for (; it != children.end(); ++it)
+      AddChild((*it)->Duplicate());
+  }
+
+  if (object->HasAttributes()) {
+    AttributeMap attributes;
+    object->GetAttributes(attributes);
+    AttributeMap::const_iterator it = attributes.begin();
+    for (; it != attributes.end(); ++it) {
+      if (overwriteAttributes || !HasAttribute(it->first))
+        SetAttributeValue(it->first, it->second);
+    }
+  }
+}
+
+CefRefPtr<CefXmlObject> CefXmlObject::Duplicate() {
+  CefRefPtr<CefXmlObject> new_obj;
+  {
+    base::AutoLock lock_scope(lock_);
+    new_obj = new CefXmlObject(name_);
+    new_obj->Append(this, true);
+  }
+  return new_obj;
+}
+
+void CefXmlObject::Clear() {
+  ClearChildren();
+  ClearAttributes();
+}
+
+CefString CefXmlObject::GetName() {
+  CefString name;
+  {
+    base::AutoLock lock_scope(lock_);
+    name = name_;
+  }
+  return name;
+}
+
+bool CefXmlObject::SetName(const CefString& name) {
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+
+  base::AutoLock lock_scope(lock_);
+  name_ = name;
+  return true;
+}
+
+bool CefXmlObject::HasParent() {
+  base::AutoLock lock_scope(lock_);
+  return (parent_ != nullptr);
+}
+
+CefRefPtr<CefXmlObject> CefXmlObject::GetParent() {
+  CefRefPtr<CefXmlObject> parent;
+  {
+    base::AutoLock lock_scope(lock_);
+    parent = parent_;
+  }
+  return parent;
+}
+
+bool CefXmlObject::HasValue() {
+  base::AutoLock lock_scope(lock_);
+  return !value_.empty();
+}
+
+CefString CefXmlObject::GetValue() {
+  CefString value;
+  {
+    base::AutoLock lock_scope(lock_);
+    value = value_;
+  }
+  return value;
+}
+
+bool CefXmlObject::SetValue(const CefString& value) {
+  base::AutoLock lock_scope(lock_);
+  DCHECK(children_.empty());
+  if (!children_.empty())
+    return false;
+  value_ = value;
+  return true;
+}
+
+bool CefXmlObject::HasAttributes() {
+  base::AutoLock lock_scope(lock_);
+  return !attributes_.empty();
+}
+
+size_t CefXmlObject::GetAttributeCount() {
+  base::AutoLock lock_scope(lock_);
+  return attributes_.size();
+}
+
+bool CefXmlObject::HasAttribute(const CefString& name) {
+  if (name.empty())
+    return false;
+
+  base::AutoLock lock_scope(lock_);
+  AttributeMap::const_iterator it = attributes_.find(name);
+  return (it != attributes_.end());
+}
+
+CefString CefXmlObject::GetAttributeValue(const CefString& name) {
+  DCHECK(!name.empty());
+  CefString value;
+  if (!name.empty()) {
+    base::AutoLock lock_scope(lock_);
+    AttributeMap::const_iterator it = attributes_.find(name);
+    if (it != attributes_.end())
+      value = it->second;
+  }
+  return value;
+}
+
+bool CefXmlObject::SetAttributeValue(const CefString& name,
+                                     const CefString& value) {
+  DCHECK(!name.empty());
+  if (name.empty())
+    return false;
+
+  base::AutoLock lock_scope(lock_);
+  AttributeMap::iterator it = attributes_.find(name);
+  if (it != attributes_.end()) {
+    it->second = value;
+  } else {
+    attributes_.insert(std::make_pair(name, value));
+  }
+  return true;
+}
+
+size_t CefXmlObject::GetAttributes(AttributeMap& attributes) {
+  base::AutoLock lock_scope(lock_);
+  attributes = attributes_;
+  return attributes_.size();
+}
+
+void CefXmlObject::ClearAttributes() {
+  base::AutoLock lock_scope(lock_);
+  attributes_.clear();
+}
+
+bool CefXmlObject::HasChildren() {
+  base::AutoLock lock_scope(lock_);
+  return !children_.empty();
+}
+
+size_t CefXmlObject::GetChildCount() {
+  base::AutoLock lock_scope(lock_);
+  return children_.size();
+}
+
+bool CefXmlObject::HasChild(CefRefPtr<CefXmlObject> child) {
+  DCHECK(child.get());
+
+  base::AutoLock lock_scope(lock_);
+  ObjectVector::const_iterator it = children_.begin();
+  for (; it != children_.end(); ++it) {
+    if ((*it).get() == child.get())
+      return true;
+  }
+  return false;
+}
+
+bool CefXmlObject::AddChild(CefRefPtr<CefXmlObject> child) {
+  DCHECK(child.get());
+  if (!child.get())
+    return false;
+
+  CefRefPtr<CefXmlObject> parent = child->GetParent();
+  DCHECK(!parent);
+  if (parent)
+    return false;
+
+  base::AutoLock lock_scope(lock_);
+
+  children_.push_back(child);
+  child->SetParent(this);
+  return true;
+}
+
+bool CefXmlObject::RemoveChild(CefRefPtr<CefXmlObject> child) {
+  DCHECK(child.get());
+
+  base::AutoLock lock_scope(lock_);
+  ObjectVector::iterator it = children_.begin();
+  for (; it != children_.end(); ++it) {
+    if ((*it).get() == child.get()) {
+      children_.erase(it);
+      child->SetParent(nullptr);
+      return true;
+    }
+  }
+  return false;
+}
+
+size_t CefXmlObject::GetChildren(ObjectVector& children) {
+  base::AutoLock lock_scope(lock_);
+  children = children_;
+  return children_.size();
+}
+
+void CefXmlObject::ClearChildren() {
+  base::AutoLock lock_scope(lock_);
+  ObjectVector::iterator it = children_.begin();
+  for (; it != children_.end(); ++it)
+    (*it)->SetParent(nullptr);
+  children_.clear();
+}
+
+CefRefPtr<CefXmlObject> CefXmlObject::FindChild(const CefString& name) {
+  DCHECK(!name.empty());
+  if (name.empty())
+    return nullptr;
+
+  base::AutoLock lock_scope(lock_);
+  ObjectVector::const_iterator it = children_.begin();
+  for (; it != children_.end(); ++it) {
+    if ((*it)->GetName() == name)
+      return (*it);
+  }
+  return nullptr;
+}
+
+size_t CefXmlObject::FindChildren(const CefString& name,
+                                  ObjectVector& children) {
+  DCHECK(!name.empty());
+  if (name.empty())
+    return 0;
+
+  size_t ct = 0;
+
+  base::AutoLock lock_scope(lock_);
+  ObjectVector::const_iterator it = children_.begin();
+  for (; it != children_.end(); ++it) {
+    if ((*it)->GetName() == name) {
+      children.push_back(*it);
+      ct++;
+    }
+  }
+  return ct;
+}
+
+void CefXmlObject::SetParent(CefXmlObject* parent) {
+  base::AutoLock lock_scope(lock_);
+  if (parent) {
+    DCHECK(parent_ == nullptr);
+    parent_ = parent;
+  } else {
+    DCHECK(parent_ != nullptr);
+    parent_ = nullptr;
+  }
+}
diff --git a/src/libcef_dll/wrapper/cef_zip_archive.cc b/src/libcef_dll/wrapper/cef_zip_archive.cc
new file mode 100644
index 0000000..5120d99
--- /dev/null
+++ b/src/libcef_dll/wrapper/cef_zip_archive.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_zip_archive.h"
+
+#include <algorithm>
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_stream.h"
+#include "include/cef_zip_reader.h"
+#include "include/wrapper/cef_byte_read_handler.h"
+
+#if defined(OS_LINUX)
+#include <wctype.h>
+#endif
+
+namespace {
+
+// Convert |str| to lowercase in a Unicode-friendly manner.
+CefString ToLower(const CefString& str) {
+  std::wstring wstr = str;
+  std::transform(wstr.begin(), wstr.end(), wstr.begin(), towlower);
+  return wstr;
+}
+
+class CefZipFile : public CefZipArchive::File {
+ public:
+  CefZipFile() : data_size_(0) {}
+
+  bool Initialize(size_t data_size) {
+    data_.reset(new unsigned char[data_size]);
+    if (data_) {
+      data_size_ = data_size;
+      return true;
+    } else {
+      DLOG(ERROR) << "Failed to allocate " << data_size << " bytes of memory";
+      data_size_ = 0;
+      return false;
+    }
+  }
+
+  virtual const unsigned char* GetData() const OVERRIDE { return data_.get(); }
+
+  virtual size_t GetDataSize() const OVERRIDE { return data_size_; }
+
+  virtual CefRefPtr<CefStreamReader> GetStreamReader() const OVERRIDE {
+    CefRefPtr<CefReadHandler> handler(new CefByteReadHandler(
+        data_.get(), data_size_, const_cast<CefZipFile*>(this)));
+    return CefStreamReader::CreateForHandler(handler);
+  }
+
+  unsigned char* data() { return data_.get(); }
+
+ private:
+  size_t data_size_;
+  scoped_ptr<unsigned char[]> data_;
+
+  IMPLEMENT_REFCOUNTING(CefZipFile);
+  DISALLOW_COPY_AND_ASSIGN(CefZipFile);
+};
+
+}  // namespace
+
+// CefZipArchive implementation
+
+CefZipArchive::CefZipArchive() {}
+
+CefZipArchive::~CefZipArchive() {}
+
+size_t CefZipArchive::Load(CefRefPtr<CefStreamReader> stream,
+                           const CefString& password,
+                           bool overwriteExisting) {
+  base::AutoLock lock_scope(lock_);
+
+  CefRefPtr<CefZipReader> reader(CefZipReader::Create(stream));
+  if (!reader.get())
+    return 0;
+
+  if (!reader->MoveToFirstFile())
+    return 0;
+
+  FileMap::iterator it;
+  size_t count = 0;
+
+  do {
+    const size_t size = static_cast<size_t>(reader->GetFileSize());
+    if (size == 0) {
+      // Skip directories and empty files.
+      continue;
+    }
+
+    if (!reader->OpenFile(password))
+      break;
+
+    const CefString& name = ToLower(reader->GetFileName());
+
+    it = contents_.find(name);
+    if (it != contents_.end()) {
+      if (overwriteExisting)
+        contents_.erase(it);
+      else  // Skip files that already exist.
+        continue;
+    }
+
+    CefRefPtr<CefZipFile> contents = new CefZipFile();
+    if (!contents->Initialize(size))
+      continue;
+    unsigned char* data = contents->data();
+    size_t offset = 0;
+
+    // Read the file contents.
+    do {
+      offset += reader->ReadFile(data + offset, size - offset);
+    } while (offset < size && !reader->Eof());
+
+    DCHECK(offset == size);
+
+    reader->CloseFile();
+    count++;
+
+    // Add the file to the map.
+    contents_.insert(std::make_pair(name, contents.get()));
+  } while (reader->MoveToNextFile());
+
+  return count;
+}
+
+void CefZipArchive::Clear() {
+  base::AutoLock lock_scope(lock_);
+  contents_.clear();
+}
+
+size_t CefZipArchive::GetFileCount() const {
+  base::AutoLock lock_scope(lock_);
+  return contents_.size();
+}
+
+bool CefZipArchive::HasFile(const CefString& fileName) const {
+  base::AutoLock lock_scope(lock_);
+  FileMap::const_iterator it = contents_.find(ToLower(fileName));
+  return (it != contents_.end());
+}
+
+CefRefPtr<CefZipArchive::File> CefZipArchive::GetFile(
+    const CefString& fileName) const {
+  base::AutoLock lock_scope(lock_);
+  FileMap::const_iterator it = contents_.find(ToLower(fileName));
+  if (it != contents_.end())
+    return it->second;
+  return nullptr;
+}
+
+bool CefZipArchive::RemoveFile(const CefString& fileName) {
+  base::AutoLock lock_scope(lock_);
+  FileMap::iterator it = contents_.find(ToLower(fileName));
+  if (it != contents_.end()) {
+    contents_.erase(it);
+    return true;
+  }
+  return false;
+}
+
+size_t CefZipArchive::GetFiles(FileMap& map) const {
+  base::AutoLock lock_scope(lock_);
+  map = contents_;
+  return contents_.size();
+}
diff --git a/src/libcef_dll/wrapper/libcef_dll_dylib.cc b/src/libcef_dll/wrapper/libcef_dll_dylib.cc
new file mode 100644
index 0000000..58a257d
--- /dev/null
+++ b/src/libcef_dll/wrapper/libcef_dll_dylib.cc
@@ -0,0 +1,2169 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=66b734973339eb27399083e978844f7d5a6a6c44$
+//
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+#include "include/capi/cef_app_capi.h"
+#include "include/capi/cef_browser_capi.h"
+#include "include/capi/cef_command_line_capi.h"
+#include "include/capi/cef_cookie_capi.h"
+#include "include/capi/cef_crash_util_capi.h"
+#include "include/capi/cef_drag_data_capi.h"
+#include "include/capi/cef_file_util_capi.h"
+#include "include/capi/cef_image_capi.h"
+#include "include/capi/cef_media_router_capi.h"
+#include "include/capi/cef_menu_model_capi.h"
+#include "include/capi/cef_origin_whitelist_capi.h"
+#include "include/capi/cef_parser_capi.h"
+#include "include/capi/cef_path_util_capi.h"
+#include "include/capi/cef_print_settings_capi.h"
+#include "include/capi/cef_process_message_capi.h"
+#include "include/capi/cef_process_util_capi.h"
+#include "include/capi/cef_request_capi.h"
+#include "include/capi/cef_request_context_capi.h"
+#include "include/capi/cef_resource_bundle_capi.h"
+#include "include/capi/cef_response_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+#include "include/capi/cef_server_capi.h"
+#include "include/capi/cef_ssl_info_capi.h"
+#include "include/capi/cef_stream_capi.h"
+#include "include/capi/cef_task_capi.h"
+#include "include/capi/cef_thread_capi.h"
+#include "include/capi/cef_trace_capi.h"
+#include "include/capi/cef_urlrequest_capi.h"
+#include "include/capi/cef_v8_capi.h"
+#include "include/capi/cef_values_capi.h"
+#include "include/capi/cef_waitable_event_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/capi/cef_xml_reader_capi.h"
+#include "include/capi/cef_zip_reader_capi.h"
+#include "include/capi/test/cef_test_helpers_capi.h"
+#include "include/capi/test/cef_translator_test_capi.h"
+#include "include/capi/views/cef_browser_view_capi.h"
+#include "include/capi/views/cef_display_capi.h"
+#include "include/capi/views/cef_label_button_capi.h"
+#include "include/capi/views/cef_menu_button_capi.h"
+#include "include/capi/views/cef_panel_capi.h"
+#include "include/capi/views/cef_scroll_view_capi.h"
+#include "include/capi/views/cef_textfield_capi.h"
+#include "include/capi/views/cef_window_capi.h"
+#include "include/cef_api_hash.h"
+#include "include/cef_version.h"
+#include "include/internal/cef_logging_internal.h"
+#include "include/internal/cef_string_list.h"
+#include "include/internal/cef_string_map.h"
+#include "include/internal/cef_string_multimap.h"
+#include "include/internal/cef_string_types.h"
+#include "include/internal/cef_thread_internal.h"
+#include "include/internal/cef_time.h"
+#include "include/internal/cef_trace_event_internal.h"
+#include "include/wrapper/cef_library_loader.h"
+
+// GLOBAL WRAPPER FUNCTIONS - Do not edit by hand.
+
+namespace {
+
+void* g_libcef_handle = nullptr;
+
+void* libcef_get_ptr(const char* path, const char* name) {
+  void* ptr = dlsym(g_libcef_handle, name);
+  if (!ptr) {
+    fprintf(stderr, "dlsym %s: %s\n", path, dlerror());
+  }
+  return ptr;
+}
+
+typedef int (*cef_execute_process_ptr)(const struct _cef_main_args_t*,
+                                       struct _cef_app_t*,
+                                       void*);
+typedef int (*cef_initialize_ptr)(const struct _cef_main_args_t*,
+                                  const struct _cef_settings_t*,
+                                  struct _cef_app_t*,
+                                  void*);
+typedef void (*cef_shutdown_ptr)();
+typedef void (*cef_do_message_loop_work_ptr)();
+typedef void (*cef_run_message_loop_ptr)();
+typedef void (*cef_quit_message_loop_ptr)();
+typedef void (*cef_set_osmodal_loop_ptr)(int);
+typedef void (*cef_enable_highdpi_support_ptr)();
+typedef int (*cef_crash_reporting_enabled_ptr)();
+typedef void (*cef_set_crash_key_value_ptr)(const cef_string_t*,
+                                            const cef_string_t*);
+typedef int (*cef_create_directory_ptr)(const cef_string_t*);
+typedef int (*cef_get_temp_directory_ptr)(cef_string_t*);
+typedef int (*cef_create_new_temp_directory_ptr)(const cef_string_t*,
+                                                 cef_string_t*);
+typedef int (*cef_create_temp_directory_in_directory_ptr)(const cef_string_t*,
+                                                          const cef_string_t*,
+                                                          cef_string_t*);
+typedef int (*cef_directory_exists_ptr)(const cef_string_t*);
+typedef int (*cef_delete_file_ptr)(const cef_string_t*, int);
+typedef int (*cef_zip_directory_ptr)(const cef_string_t*,
+                                     const cef_string_t*,
+                                     int);
+typedef void (*cef_load_crlsets_file_ptr)(const cef_string_t*);
+typedef int (*cef_add_cross_origin_whitelist_entry_ptr)(const cef_string_t*,
+                                                        const cef_string_t*,
+                                                        const cef_string_t*,
+                                                        int);
+typedef int (*cef_remove_cross_origin_whitelist_entry_ptr)(const cef_string_t*,
+                                                           const cef_string_t*,
+                                                           const cef_string_t*,
+                                                           int);
+typedef int (*cef_clear_cross_origin_whitelist_ptr)();
+typedef int (*cef_parse_url_ptr)(const cef_string_t*, struct _cef_urlparts_t*);
+typedef int (*cef_create_url_ptr)(const struct _cef_urlparts_t*, cef_string_t*);
+typedef cef_string_userfree_t (*cef_format_url_for_security_display_ptr)(
+    const cef_string_t*);
+typedef cef_string_userfree_t (*cef_get_mime_type_ptr)(const cef_string_t*);
+typedef void (*cef_get_extensions_for_mime_type_ptr)(const cef_string_t*,
+                                                     cef_string_list_t);
+typedef cef_string_userfree_t (*cef_base64encode_ptr)(const void*, size_t);
+typedef struct _cef_binary_value_t* (*cef_base64decode_ptr)(
+    const cef_string_t*);
+typedef cef_string_userfree_t (*cef_uriencode_ptr)(const cef_string_t*, int);
+typedef cef_string_userfree_t (*cef_uridecode_ptr)(const cef_string_t*,
+                                                   int,
+                                                   cef_uri_unescape_rule_t);
+typedef struct _cef_value_t* (*cef_parse_json_ptr)(const cef_string_t*,
+                                                   cef_json_parser_options_t);
+typedef struct _cef_value_t* (
+    *cef_parse_json_buffer_ptr)(const void*, size_t, cef_json_parser_options_t);
+typedef struct _cef_value_t* (*cef_parse_jsonand_return_error_ptr)(
+    const cef_string_t*,
+    cef_json_parser_options_t,
+    cef_json_parser_error_t*,
+    cef_string_t*);
+typedef cef_string_userfree_t (*cef_write_json_ptr)(struct _cef_value_t*,
+                                                    cef_json_writer_options_t);
+typedef int (*cef_get_path_ptr)(cef_path_key_t, cef_string_t*);
+typedef int (*cef_launch_process_ptr)(struct _cef_command_line_t*);
+typedef int (*cef_register_scheme_handler_factory_ptr)(
+    const cef_string_t*,
+    const cef_string_t*,
+    struct _cef_scheme_handler_factory_t*);
+typedef int (*cef_clear_scheme_handler_factories_ptr)();
+typedef int (*cef_is_cert_status_error_ptr)(cef_cert_status_t);
+typedef int (*cef_currently_on_ptr)(cef_thread_id_t);
+typedef int (*cef_post_task_ptr)(cef_thread_id_t, struct _cef_task_t*);
+typedef int (*cef_post_delayed_task_ptr)(cef_thread_id_t,
+                                         struct _cef_task_t*,
+                                         int64);
+typedef int (*cef_begin_tracing_ptr)(const cef_string_t*,
+                                     struct _cef_completion_callback_t*);
+typedef int (*cef_end_tracing_ptr)(const cef_string_t*,
+                                   struct _cef_end_tracing_callback_t*);
+typedef int64 (*cef_now_from_system_trace_time_ptr)();
+typedef int (*cef_register_extension_ptr)(const cef_string_t*,
+                                          const cef_string_t*,
+                                          struct _cef_v8handler_t*);
+typedef void (*cef_visit_web_plugin_info_ptr)(
+    struct _cef_web_plugin_info_visitor_t*);
+typedef void (*cef_refresh_web_plugins_ptr)();
+typedef void (*cef_unregister_internal_web_plugin_ptr)(const cef_string_t*);
+typedef void (*cef_register_web_plugin_crash_ptr)(const cef_string_t*);
+typedef void (*cef_is_web_plugin_unstable_ptr)(
+    const cef_string_t*,
+    struct _cef_web_plugin_unstable_callback_t*);
+typedef void (*cef_register_widevine_cdm_ptr)(
+    const cef_string_t*,
+    struct _cef_register_cdm_callback_t*);
+typedef void (*cef_execute_java_script_with_user_gesture_for_tests_ptr)(
+    struct _cef_frame_t*,
+    const cef_string_t*);
+typedef int (*cef_browser_host_create_browser_ptr)(
+    const struct _cef_window_info_t*,
+    struct _cef_client_t*,
+    const cef_string_t*,
+    const struct _cef_browser_settings_t*,
+    struct _cef_dictionary_value_t*,
+    struct _cef_request_context_t*);
+typedef struct _cef_browser_t* (*cef_browser_host_create_browser_sync_ptr)(
+    const struct _cef_window_info_t*,
+    struct _cef_client_t*,
+    const cef_string_t*,
+    const struct _cef_browser_settings_t*,
+    struct _cef_dictionary_value_t*,
+    struct _cef_request_context_t*);
+typedef struct _cef_command_line_t* (*cef_command_line_create_ptr)();
+typedef struct _cef_command_line_t* (*cef_command_line_get_global_ptr)();
+typedef struct _cef_cookie_manager_t* (
+    *cef_cookie_manager_get_global_manager_ptr)(
+    struct _cef_completion_callback_t*);
+typedef struct _cef_drag_data_t* (*cef_drag_data_create_ptr)();
+typedef struct _cef_image_t* (*cef_image_create_ptr)();
+typedef struct _cef_media_router_t* (*cef_media_router_get_global_ptr)();
+typedef struct _cef_menu_model_t* (*cef_menu_model_create_ptr)(
+    struct _cef_menu_model_delegate_t*);
+typedef struct _cef_print_settings_t* (*cef_print_settings_create_ptr)();
+typedef struct _cef_process_message_t* (*cef_process_message_create_ptr)(
+    const cef_string_t*);
+typedef struct _cef_request_t* (*cef_request_create_ptr)();
+typedef struct _cef_post_data_t* (*cef_post_data_create_ptr)();
+typedef struct _cef_post_data_element_t* (*cef_post_data_element_create_ptr)();
+typedef struct _cef_request_context_t* (
+    *cef_request_context_get_global_context_ptr)();
+typedef struct _cef_request_context_t* (
+    *cef_request_context_create_context_ptr)(
+    const struct _cef_request_context_settings_t*,
+    struct _cef_request_context_handler_t*);
+typedef struct _cef_request_context_t* (*cef_create_context_shared_ptr)(
+    struct _cef_request_context_t*,
+    struct _cef_request_context_handler_t*);
+typedef struct _cef_resource_bundle_t* (*cef_resource_bundle_get_global_ptr)();
+typedef struct _cef_response_t* (*cef_response_create_ptr)();
+typedef void (*cef_server_create_ptr)(const cef_string_t*,
+                                      uint16,
+                                      int,
+                                      struct _cef_server_handler_t*);
+typedef struct _cef_stream_reader_t* (*cef_stream_reader_create_for_file_ptr)(
+    const cef_string_t*);
+typedef struct _cef_stream_reader_t* (
+    *cef_stream_reader_create_for_data_ptr)(void*, size_t);
+typedef struct _cef_stream_reader_t* (
+    *cef_stream_reader_create_for_handler_ptr)(struct _cef_read_handler_t*);
+typedef struct _cef_stream_writer_t* (*cef_stream_writer_create_for_file_ptr)(
+    const cef_string_t*);
+typedef struct _cef_stream_writer_t* (
+    *cef_stream_writer_create_for_handler_ptr)(struct _cef_write_handler_t*);
+typedef struct _cef_task_runner_t* (
+    *cef_task_runner_get_for_current_thread_ptr)();
+typedef struct _cef_task_runner_t* (*cef_task_runner_get_for_thread_ptr)(
+    cef_thread_id_t);
+typedef struct _cef_thread_t* (*cef_thread_create_ptr)(const cef_string_t*,
+                                                       cef_thread_priority_t,
+                                                       cef_message_loop_type_t,
+                                                       int,
+                                                       cef_com_init_mode_t);
+typedef struct _cef_urlrequest_t* (*cef_urlrequest_create_ptr)(
+    struct _cef_request_t*,
+    struct _cef_urlrequest_client_t*,
+    struct _cef_request_context_t*);
+typedef struct _cef_v8context_t* (*cef_v8context_get_current_context_ptr)();
+typedef struct _cef_v8context_t* (*cef_v8context_get_entered_context_ptr)();
+typedef int (*cef_v8context_in_context_ptr)();
+typedef struct _cef_v8value_t* (*cef_v8value_create_undefined_ptr)();
+typedef struct _cef_v8value_t* (*cef_v8value_create_null_ptr)();
+typedef struct _cef_v8value_t* (*cef_v8value_create_bool_ptr)(int);
+typedef struct _cef_v8value_t* (*cef_v8value_create_int_ptr)(int32);
+typedef struct _cef_v8value_t* (*cef_v8value_create_uint_ptr)(uint32);
+typedef struct _cef_v8value_t* (*cef_v8value_create_double_ptr)(double);
+typedef struct _cef_v8value_t* (*cef_v8value_create_date_ptr)(
+    const cef_time_t*);
+typedef struct _cef_v8value_t* (*cef_v8value_create_string_ptr)(
+    const cef_string_t*);
+typedef struct _cef_v8value_t* (*cef_v8value_create_object_ptr)(
+    struct _cef_v8accessor_t*,
+    struct _cef_v8interceptor_t*);
+typedef struct _cef_v8value_t* (*cef_v8value_create_array_ptr)(int);
+typedef struct _cef_v8value_t* (*cef_v8value_create_array_buffer_ptr)(
+    void*,
+    size_t,
+    struct _cef_v8array_buffer_release_callback_t*);
+typedef struct _cef_v8value_t* (*cef_v8value_create_function_ptr)(
+    const cef_string_t*,
+    struct _cef_v8handler_t*);
+typedef struct _cef_v8stack_trace_t* (*cef_v8stack_trace_get_current_ptr)(int);
+typedef struct _cef_value_t* (*cef_value_create_ptr)();
+typedef struct _cef_binary_value_t* (*cef_binary_value_create_ptr)(const void*,
+                                                                   size_t);
+typedef struct _cef_dictionary_value_t* (*cef_dictionary_value_create_ptr)();
+typedef struct _cef_list_value_t* (*cef_list_value_create_ptr)();
+typedef struct _cef_waitable_event_t* (*cef_waitable_event_create_ptr)(int,
+                                                                       int);
+typedef struct _cef_xml_reader_t* (*cef_xml_reader_create_ptr)(
+    struct _cef_stream_reader_t*,
+    cef_xml_encoding_type_t,
+    const cef_string_t*);
+typedef struct _cef_zip_reader_t* (*cef_zip_reader_create_ptr)(
+    struct _cef_stream_reader_t*);
+typedef struct _cef_translator_test_t* (*cef_translator_test_create_ptr)();
+typedef struct _cef_translator_test_ref_ptr_library_t* (
+    *cef_translator_test_ref_ptr_library_create_ptr)(int);
+typedef struct _cef_translator_test_ref_ptr_library_child_t* (
+    *cef_translator_test_ref_ptr_library_child_create_ptr)(int, int);
+typedef struct _cef_translator_test_ref_ptr_library_child_child_t* (
+    *cef_translator_test_ref_ptr_library_child_child_create_ptr)(int, int, int);
+typedef struct _cef_translator_test_scoped_library_t* (
+    *cef_translator_test_scoped_library_create_ptr)(int);
+typedef struct _cef_translator_test_scoped_library_child_t* (
+    *cef_translator_test_scoped_library_child_create_ptr)(int, int);
+typedef struct _cef_translator_test_scoped_library_child_child_t* (
+    *cef_translator_test_scoped_library_child_child_create_ptr)(int, int, int);
+typedef struct _cef_browser_view_t* (*cef_browser_view_create_ptr)(
+    struct _cef_client_t*,
+    const cef_string_t*,
+    const struct _cef_browser_settings_t*,
+    struct _cef_dictionary_value_t*,
+    struct _cef_request_context_t*,
+    struct _cef_browser_view_delegate_t*);
+typedef struct _cef_browser_view_t* (*cef_browser_view_get_for_browser_ptr)(
+    struct _cef_browser_t*);
+typedef struct _cef_display_t* (*cef_display_get_primary_ptr)();
+typedef struct _cef_display_t* (
+    *cef_display_get_nearest_point_ptr)(const cef_point_t*, int);
+typedef struct _cef_display_t* (
+    *cef_display_get_matching_bounds_ptr)(const cef_rect_t*, int);
+typedef size_t (*cef_display_get_count_ptr)();
+typedef void (*cef_display_get_alls_ptr)(size_t*, struct _cef_display_t**);
+typedef struct _cef_label_button_t* (*cef_label_button_create_ptr)(
+    struct _cef_button_delegate_t*,
+    const cef_string_t*);
+typedef struct _cef_menu_button_t* (*cef_menu_button_create_ptr)(
+    struct _cef_menu_button_delegate_t*,
+    const cef_string_t*);
+typedef struct _cef_panel_t* (*cef_panel_create_ptr)(
+    struct _cef_panel_delegate_t*);
+typedef struct _cef_scroll_view_t* (*cef_scroll_view_create_ptr)(
+    struct _cef_view_delegate_t*);
+typedef struct _cef_textfield_t* (*cef_textfield_create_ptr)(
+    struct _cef_textfield_delegate_t*);
+typedef struct _cef_window_t* (*cef_window_create_top_level_ptr)(
+    struct _cef_window_delegate_t*);
+typedef const char* (*cef_api_hash_ptr)(int);
+typedef int (*cef_version_info_ptr)(int);
+typedef int (*cef_get_min_log_level_ptr)();
+typedef int (*cef_get_vlog_level_ptr)(const char*, size_t);
+typedef void (*cef_log_ptr)(const char*, int, int, const char*);
+typedef cef_string_list_t (*cef_string_list_alloc_ptr)();
+typedef size_t (*cef_string_list_size_ptr)(cef_string_list_t);
+typedef int (*cef_string_list_value_ptr)(cef_string_list_t,
+                                         size_t,
+                                         cef_string_t*);
+typedef void (*cef_string_list_append_ptr)(cef_string_list_t,
+                                           const cef_string_t*);
+typedef void (*cef_string_list_clear_ptr)(cef_string_list_t);
+typedef void (*cef_string_list_free_ptr)(cef_string_list_t);
+typedef cef_string_list_t (*cef_string_list_copy_ptr)(cef_string_list_t);
+typedef cef_string_map_t (*cef_string_map_alloc_ptr)();
+typedef size_t (*cef_string_map_size_ptr)(cef_string_map_t);
+typedef int (*cef_string_map_find_ptr)(cef_string_map_t,
+                                       const cef_string_t*,
+                                       cef_string_t*);
+typedef int (*cef_string_map_key_ptr)(cef_string_map_t, size_t, cef_string_t*);
+typedef int (*cef_string_map_value_ptr)(cef_string_map_t,
+                                        size_t,
+                                        cef_string_t*);
+typedef int (*cef_string_map_append_ptr)(cef_string_map_t,
+                                         const cef_string_t*,
+                                         const cef_string_t*);
+typedef void (*cef_string_map_clear_ptr)(cef_string_map_t);
+typedef void (*cef_string_map_free_ptr)(cef_string_map_t);
+typedef cef_string_multimap_t (*cef_string_multimap_alloc_ptr)();
+typedef size_t (*cef_string_multimap_size_ptr)(cef_string_multimap_t);
+typedef size_t (*cef_string_multimap_find_count_ptr)(cef_string_multimap_t,
+                                                     const cef_string_t*);
+typedef int (*cef_string_multimap_enumerate_ptr)(cef_string_multimap_t,
+                                                 const cef_string_t*,
+                                                 size_t,
+                                                 cef_string_t*);
+typedef int (*cef_string_multimap_key_ptr)(cef_string_multimap_t,
+                                           size_t,
+                                           cef_string_t*);
+typedef int (*cef_string_multimap_value_ptr)(cef_string_multimap_t,
+                                             size_t,
+                                             cef_string_t*);
+typedef int (*cef_string_multimap_append_ptr)(cef_string_multimap_t,
+                                              const cef_string_t*,
+                                              const cef_string_t*);
+typedef void (*cef_string_multimap_clear_ptr)(cef_string_multimap_t);
+typedef void (*cef_string_multimap_free_ptr)(cef_string_multimap_t);
+typedef int (*cef_string_wide_set_ptr)(const wchar_t*,
+                                       size_t,
+                                       cef_string_wide_t*,
+                                       int);
+typedef int (*cef_string_utf8_set_ptr)(const char*,
+                                       size_t,
+                                       cef_string_utf8_t*,
+                                       int);
+typedef int (*cef_string_utf16_set_ptr)(const char16*,
+                                        size_t,
+                                        cef_string_utf16_t*,
+                                        int);
+typedef void (*cef_string_wide_clear_ptr)(cef_string_wide_t*);
+typedef void (*cef_string_utf8_clear_ptr)(cef_string_utf8_t*);
+typedef void (*cef_string_utf16_clear_ptr)(cef_string_utf16_t*);
+typedef int (*cef_string_wide_cmp_ptr)(const cef_string_wide_t*,
+                                       const cef_string_wide_t*);
+typedef int (*cef_string_utf8_cmp_ptr)(const cef_string_utf8_t*,
+                                       const cef_string_utf8_t*);
+typedef int (*cef_string_utf16_cmp_ptr)(const cef_string_utf16_t*,
+                                        const cef_string_utf16_t*);
+typedef int (*cef_string_wide_to_utf8_ptr)(const wchar_t*,
+                                           size_t,
+                                           cef_string_utf8_t*);
+typedef int (*cef_string_utf8_to_wide_ptr)(const char*,
+                                           size_t,
+                                           cef_string_wide_t*);
+typedef int (*cef_string_wide_to_utf16_ptr)(const wchar_t*,
+                                            size_t,
+                                            cef_string_utf16_t*);
+typedef int (*cef_string_utf16_to_wide_ptr)(const char16*,
+                                            size_t,
+                                            cef_string_wide_t*);
+typedef int (*cef_string_utf8_to_utf16_ptr)(const char*,
+                                            size_t,
+                                            cef_string_utf16_t*);
+typedef int (*cef_string_utf16_to_utf8_ptr)(const char16*,
+                                            size_t,
+                                            cef_string_utf8_t*);
+typedef int (*cef_string_ascii_to_wide_ptr)(const char*,
+                                            size_t,
+                                            cef_string_wide_t*);
+typedef int (*cef_string_ascii_to_utf16_ptr)(const char*,
+                                             size_t,
+                                             cef_string_utf16_t*);
+typedef cef_string_userfree_wide_t (*cef_string_userfree_wide_alloc_ptr)();
+typedef cef_string_userfree_utf8_t (*cef_string_userfree_utf8_alloc_ptr)();
+typedef cef_string_userfree_utf16_t (*cef_string_userfree_utf16_alloc_ptr)();
+typedef void (*cef_string_userfree_wide_free_ptr)(cef_string_userfree_wide_t);
+typedef void (*cef_string_userfree_utf8_free_ptr)(cef_string_userfree_utf8_t);
+typedef void (*cef_string_userfree_utf16_free_ptr)(cef_string_userfree_utf16_t);
+typedef int (*cef_string_utf16_to_lower_ptr)(const char16*,
+                                             size_t,
+                                             cef_string_utf16_t*);
+typedef int (*cef_string_utf16_to_upper_ptr)(const char16*,
+                                             size_t,
+                                             cef_string_utf16_t*);
+typedef cef_platform_thread_id_t (*cef_get_current_platform_thread_id_ptr)();
+typedef cef_platform_thread_handle_t (
+    *cef_get_current_platform_thread_handle_ptr)();
+typedef int (*cef_time_to_timet_ptr)(const cef_time_t*, time_t*);
+typedef int (*cef_time_from_timet_ptr)(time_t, cef_time_t*);
+typedef int (*cef_time_to_doublet_ptr)(const cef_time_t*, double*);
+typedef int (*cef_time_from_doublet_ptr)(double, cef_time_t*);
+typedef int (*cef_time_now_ptr)(cef_time_t*);
+typedef int (*cef_time_delta_ptr)(const cef_time_t*,
+                                  const cef_time_t*,
+                                  long long*);
+typedef void (*cef_trace_event_instant_ptr)(const char*,
+                                            const char*,
+                                            const char*,
+                                            uint64,
+                                            const char*,
+                                            uint64,
+                                            int);
+typedef void (*cef_trace_event_begin_ptr)(const char*,
+                                          const char*,
+                                          const char*,
+                                          uint64,
+                                          const char*,
+                                          uint64,
+                                          int);
+typedef void (*cef_trace_event_end_ptr)(const char*,
+                                        const char*,
+                                        const char*,
+                                        uint64,
+                                        const char*,
+                                        uint64,
+                                        int);
+typedef void (*cef_trace_counter_ptr)(const char*,
+                                      const char*,
+                                      const char*,
+                                      uint64,
+                                      const char*,
+                                      uint64,
+                                      int);
+typedef void (*cef_trace_counter_id_ptr)(const char*,
+                                         const char*,
+                                         uint64,
+                                         const char*,
+                                         uint64,
+                                         const char*,
+                                         uint64,
+                                         int);
+typedef void (*cef_trace_event_async_begin_ptr)(const char*,
+                                                const char*,
+                                                uint64,
+                                                const char*,
+                                                uint64,
+                                                const char*,
+                                                uint64,
+                                                int);
+typedef void (*cef_trace_event_async_step_into_ptr)(const char*,
+                                                    const char*,
+                                                    uint64,
+                                                    uint64,
+                                                    const char*,
+                                                    uint64,
+                                                    int);
+typedef void (*cef_trace_event_async_step_past_ptr)(const char*,
+                                                    const char*,
+                                                    uint64,
+                                                    uint64,
+                                                    const char*,
+                                                    uint64,
+                                                    int);
+typedef void (*cef_trace_event_async_end_ptr)(const char*,
+                                              const char*,
+                                              uint64,
+                                              const char*,
+                                              uint64,
+                                              const char*,
+                                              uint64,
+                                              int);
+
+struct libcef_pointers {
+  cef_execute_process_ptr cef_execute_process;
+  cef_initialize_ptr cef_initialize;
+  cef_shutdown_ptr cef_shutdown;
+  cef_do_message_loop_work_ptr cef_do_message_loop_work;
+  cef_run_message_loop_ptr cef_run_message_loop;
+  cef_quit_message_loop_ptr cef_quit_message_loop;
+  cef_set_osmodal_loop_ptr cef_set_osmodal_loop;
+  cef_enable_highdpi_support_ptr cef_enable_highdpi_support;
+  cef_crash_reporting_enabled_ptr cef_crash_reporting_enabled;
+  cef_set_crash_key_value_ptr cef_set_crash_key_value;
+  cef_create_directory_ptr cef_create_directory;
+  cef_get_temp_directory_ptr cef_get_temp_directory;
+  cef_create_new_temp_directory_ptr cef_create_new_temp_directory;
+  cef_create_temp_directory_in_directory_ptr
+      cef_create_temp_directory_in_directory;
+  cef_directory_exists_ptr cef_directory_exists;
+  cef_delete_file_ptr cef_delete_file;
+  cef_zip_directory_ptr cef_zip_directory;
+  cef_load_crlsets_file_ptr cef_load_crlsets_file;
+  cef_add_cross_origin_whitelist_entry_ptr cef_add_cross_origin_whitelist_entry;
+  cef_remove_cross_origin_whitelist_entry_ptr
+      cef_remove_cross_origin_whitelist_entry;
+  cef_clear_cross_origin_whitelist_ptr cef_clear_cross_origin_whitelist;
+  cef_parse_url_ptr cef_parse_url;
+  cef_create_url_ptr cef_create_url;
+  cef_format_url_for_security_display_ptr cef_format_url_for_security_display;
+  cef_get_mime_type_ptr cef_get_mime_type;
+  cef_get_extensions_for_mime_type_ptr cef_get_extensions_for_mime_type;
+  cef_base64encode_ptr cef_base64encode;
+  cef_base64decode_ptr cef_base64decode;
+  cef_uriencode_ptr cef_uriencode;
+  cef_uridecode_ptr cef_uridecode;
+  cef_parse_json_ptr cef_parse_json;
+  cef_parse_json_buffer_ptr cef_parse_json_buffer;
+  cef_parse_jsonand_return_error_ptr cef_parse_jsonand_return_error;
+  cef_write_json_ptr cef_write_json;
+  cef_get_path_ptr cef_get_path;
+  cef_launch_process_ptr cef_launch_process;
+  cef_register_scheme_handler_factory_ptr cef_register_scheme_handler_factory;
+  cef_clear_scheme_handler_factories_ptr cef_clear_scheme_handler_factories;
+  cef_is_cert_status_error_ptr cef_is_cert_status_error;
+  cef_currently_on_ptr cef_currently_on;
+  cef_post_task_ptr cef_post_task;
+  cef_post_delayed_task_ptr cef_post_delayed_task;
+  cef_begin_tracing_ptr cef_begin_tracing;
+  cef_end_tracing_ptr cef_end_tracing;
+  cef_now_from_system_trace_time_ptr cef_now_from_system_trace_time;
+  cef_register_extension_ptr cef_register_extension;
+  cef_visit_web_plugin_info_ptr cef_visit_web_plugin_info;
+  cef_refresh_web_plugins_ptr cef_refresh_web_plugins;
+  cef_unregister_internal_web_plugin_ptr cef_unregister_internal_web_plugin;
+  cef_register_web_plugin_crash_ptr cef_register_web_plugin_crash;
+  cef_is_web_plugin_unstable_ptr cef_is_web_plugin_unstable;
+  cef_register_widevine_cdm_ptr cef_register_widevine_cdm;
+  cef_execute_java_script_with_user_gesture_for_tests_ptr
+      cef_execute_java_script_with_user_gesture_for_tests;
+  cef_browser_host_create_browser_ptr cef_browser_host_create_browser;
+  cef_browser_host_create_browser_sync_ptr cef_browser_host_create_browser_sync;
+  cef_command_line_create_ptr cef_command_line_create;
+  cef_command_line_get_global_ptr cef_command_line_get_global;
+  cef_cookie_manager_get_global_manager_ptr
+      cef_cookie_manager_get_global_manager;
+  cef_drag_data_create_ptr cef_drag_data_create;
+  cef_image_create_ptr cef_image_create;
+  cef_media_router_get_global_ptr cef_media_router_get_global;
+  cef_menu_model_create_ptr cef_menu_model_create;
+  cef_print_settings_create_ptr cef_print_settings_create;
+  cef_process_message_create_ptr cef_process_message_create;
+  cef_request_create_ptr cef_request_create;
+  cef_post_data_create_ptr cef_post_data_create;
+  cef_post_data_element_create_ptr cef_post_data_element_create;
+  cef_request_context_get_global_context_ptr
+      cef_request_context_get_global_context;
+  cef_request_context_create_context_ptr cef_request_context_create_context;
+  cef_create_context_shared_ptr cef_create_context_shared;
+  cef_resource_bundle_get_global_ptr cef_resource_bundle_get_global;
+  cef_response_create_ptr cef_response_create;
+  cef_server_create_ptr cef_server_create;
+  cef_stream_reader_create_for_file_ptr cef_stream_reader_create_for_file;
+  cef_stream_reader_create_for_data_ptr cef_stream_reader_create_for_data;
+  cef_stream_reader_create_for_handler_ptr cef_stream_reader_create_for_handler;
+  cef_stream_writer_create_for_file_ptr cef_stream_writer_create_for_file;
+  cef_stream_writer_create_for_handler_ptr cef_stream_writer_create_for_handler;
+  cef_task_runner_get_for_current_thread_ptr
+      cef_task_runner_get_for_current_thread;
+  cef_task_runner_get_for_thread_ptr cef_task_runner_get_for_thread;
+  cef_thread_create_ptr cef_thread_create;
+  cef_urlrequest_create_ptr cef_urlrequest_create;
+  cef_v8context_get_current_context_ptr cef_v8context_get_current_context;
+  cef_v8context_get_entered_context_ptr cef_v8context_get_entered_context;
+  cef_v8context_in_context_ptr cef_v8context_in_context;
+  cef_v8value_create_undefined_ptr cef_v8value_create_undefined;
+  cef_v8value_create_null_ptr cef_v8value_create_null;
+  cef_v8value_create_bool_ptr cef_v8value_create_bool;
+  cef_v8value_create_int_ptr cef_v8value_create_int;
+  cef_v8value_create_uint_ptr cef_v8value_create_uint;
+  cef_v8value_create_double_ptr cef_v8value_create_double;
+  cef_v8value_create_date_ptr cef_v8value_create_date;
+  cef_v8value_create_string_ptr cef_v8value_create_string;
+  cef_v8value_create_object_ptr cef_v8value_create_object;
+  cef_v8value_create_array_ptr cef_v8value_create_array;
+  cef_v8value_create_array_buffer_ptr cef_v8value_create_array_buffer;
+  cef_v8value_create_function_ptr cef_v8value_create_function;
+  cef_v8stack_trace_get_current_ptr cef_v8stack_trace_get_current;
+  cef_value_create_ptr cef_value_create;
+  cef_binary_value_create_ptr cef_binary_value_create;
+  cef_dictionary_value_create_ptr cef_dictionary_value_create;
+  cef_list_value_create_ptr cef_list_value_create;
+  cef_waitable_event_create_ptr cef_waitable_event_create;
+  cef_xml_reader_create_ptr cef_xml_reader_create;
+  cef_zip_reader_create_ptr cef_zip_reader_create;
+  cef_translator_test_create_ptr cef_translator_test_create;
+  cef_translator_test_ref_ptr_library_create_ptr
+      cef_translator_test_ref_ptr_library_create;
+  cef_translator_test_ref_ptr_library_child_create_ptr
+      cef_translator_test_ref_ptr_library_child_create;
+  cef_translator_test_ref_ptr_library_child_child_create_ptr
+      cef_translator_test_ref_ptr_library_child_child_create;
+  cef_translator_test_scoped_library_create_ptr
+      cef_translator_test_scoped_library_create;
+  cef_translator_test_scoped_library_child_create_ptr
+      cef_translator_test_scoped_library_child_create;
+  cef_translator_test_scoped_library_child_child_create_ptr
+      cef_translator_test_scoped_library_child_child_create;
+  cef_browser_view_create_ptr cef_browser_view_create;
+  cef_browser_view_get_for_browser_ptr cef_browser_view_get_for_browser;
+  cef_display_get_primary_ptr cef_display_get_primary;
+  cef_display_get_nearest_point_ptr cef_display_get_nearest_point;
+  cef_display_get_matching_bounds_ptr cef_display_get_matching_bounds;
+  cef_display_get_count_ptr cef_display_get_count;
+  cef_display_get_alls_ptr cef_display_get_alls;
+  cef_label_button_create_ptr cef_label_button_create;
+  cef_menu_button_create_ptr cef_menu_button_create;
+  cef_panel_create_ptr cef_panel_create;
+  cef_scroll_view_create_ptr cef_scroll_view_create;
+  cef_textfield_create_ptr cef_textfield_create;
+  cef_window_create_top_level_ptr cef_window_create_top_level;
+  cef_api_hash_ptr cef_api_hash;
+  cef_version_info_ptr cef_version_info;
+  cef_get_min_log_level_ptr cef_get_min_log_level;
+  cef_get_vlog_level_ptr cef_get_vlog_level;
+  cef_log_ptr cef_log;
+  cef_string_list_alloc_ptr cef_string_list_alloc;
+  cef_string_list_size_ptr cef_string_list_size;
+  cef_string_list_value_ptr cef_string_list_value;
+  cef_string_list_append_ptr cef_string_list_append;
+  cef_string_list_clear_ptr cef_string_list_clear;
+  cef_string_list_free_ptr cef_string_list_free;
+  cef_string_list_copy_ptr cef_string_list_copy;
+  cef_string_map_alloc_ptr cef_string_map_alloc;
+  cef_string_map_size_ptr cef_string_map_size;
+  cef_string_map_find_ptr cef_string_map_find;
+  cef_string_map_key_ptr cef_string_map_key;
+  cef_string_map_value_ptr cef_string_map_value;
+  cef_string_map_append_ptr cef_string_map_append;
+  cef_string_map_clear_ptr cef_string_map_clear;
+  cef_string_map_free_ptr cef_string_map_free;
+  cef_string_multimap_alloc_ptr cef_string_multimap_alloc;
+  cef_string_multimap_size_ptr cef_string_multimap_size;
+  cef_string_multimap_find_count_ptr cef_string_multimap_find_count;
+  cef_string_multimap_enumerate_ptr cef_string_multimap_enumerate;
+  cef_string_multimap_key_ptr cef_string_multimap_key;
+  cef_string_multimap_value_ptr cef_string_multimap_value;
+  cef_string_multimap_append_ptr cef_string_multimap_append;
+  cef_string_multimap_clear_ptr cef_string_multimap_clear;
+  cef_string_multimap_free_ptr cef_string_multimap_free;
+  cef_string_wide_set_ptr cef_string_wide_set;
+  cef_string_utf8_set_ptr cef_string_utf8_set;
+  cef_string_utf16_set_ptr cef_string_utf16_set;
+  cef_string_wide_clear_ptr cef_string_wide_clear;
+  cef_string_utf8_clear_ptr cef_string_utf8_clear;
+  cef_string_utf16_clear_ptr cef_string_utf16_clear;
+  cef_string_wide_cmp_ptr cef_string_wide_cmp;
+  cef_string_utf8_cmp_ptr cef_string_utf8_cmp;
+  cef_string_utf16_cmp_ptr cef_string_utf16_cmp;
+  cef_string_wide_to_utf8_ptr cef_string_wide_to_utf8;
+  cef_string_utf8_to_wide_ptr cef_string_utf8_to_wide;
+  cef_string_wide_to_utf16_ptr cef_string_wide_to_utf16;
+  cef_string_utf16_to_wide_ptr cef_string_utf16_to_wide;
+  cef_string_utf8_to_utf16_ptr cef_string_utf8_to_utf16;
+  cef_string_utf16_to_utf8_ptr cef_string_utf16_to_utf8;
+  cef_string_ascii_to_wide_ptr cef_string_ascii_to_wide;
+  cef_string_ascii_to_utf16_ptr cef_string_ascii_to_utf16;
+  cef_string_userfree_wide_alloc_ptr cef_string_userfree_wide_alloc;
+  cef_string_userfree_utf8_alloc_ptr cef_string_userfree_utf8_alloc;
+  cef_string_userfree_utf16_alloc_ptr cef_string_userfree_utf16_alloc;
+  cef_string_userfree_wide_free_ptr cef_string_userfree_wide_free;
+  cef_string_userfree_utf8_free_ptr cef_string_userfree_utf8_free;
+  cef_string_userfree_utf16_free_ptr cef_string_userfree_utf16_free;
+  cef_string_utf16_to_lower_ptr cef_string_utf16_to_lower;
+  cef_string_utf16_to_upper_ptr cef_string_utf16_to_upper;
+  cef_get_current_platform_thread_id_ptr cef_get_current_platform_thread_id;
+  cef_get_current_platform_thread_handle_ptr
+      cef_get_current_platform_thread_handle;
+  cef_time_to_timet_ptr cef_time_to_timet;
+  cef_time_from_timet_ptr cef_time_from_timet;
+  cef_time_to_doublet_ptr cef_time_to_doublet;
+  cef_time_from_doublet_ptr cef_time_from_doublet;
+  cef_time_now_ptr cef_time_now;
+  cef_time_delta_ptr cef_time_delta;
+  cef_trace_event_instant_ptr cef_trace_event_instant;
+  cef_trace_event_begin_ptr cef_trace_event_begin;
+  cef_trace_event_end_ptr cef_trace_event_end;
+  cef_trace_counter_ptr cef_trace_counter;
+  cef_trace_counter_id_ptr cef_trace_counter_id;
+  cef_trace_event_async_begin_ptr cef_trace_event_async_begin;
+  cef_trace_event_async_step_into_ptr cef_trace_event_async_step_into;
+  cef_trace_event_async_step_past_ptr cef_trace_event_async_step_past;
+  cef_trace_event_async_end_ptr cef_trace_event_async_end;
+
+} g_libcef_pointers = {0};
+
+#define INIT_ENTRY(name)                                            \
+  g_libcef_pointers.name = (name##_ptr)libcef_get_ptr(path, #name); \
+  if (!g_libcef_pointers.name) {                                    \
+    return 0;                                                       \
+  }
+
+int libcef_init_pointers(const char* path) {
+  INIT_ENTRY(cef_execute_process);
+  INIT_ENTRY(cef_initialize);
+  INIT_ENTRY(cef_shutdown);
+  INIT_ENTRY(cef_do_message_loop_work);
+  INIT_ENTRY(cef_run_message_loop);
+  INIT_ENTRY(cef_quit_message_loop);
+  INIT_ENTRY(cef_set_osmodal_loop);
+  INIT_ENTRY(cef_enable_highdpi_support);
+  INIT_ENTRY(cef_crash_reporting_enabled);
+  INIT_ENTRY(cef_set_crash_key_value);
+  INIT_ENTRY(cef_create_directory);
+  INIT_ENTRY(cef_get_temp_directory);
+  INIT_ENTRY(cef_create_new_temp_directory);
+  INIT_ENTRY(cef_create_temp_directory_in_directory);
+  INIT_ENTRY(cef_directory_exists);
+  INIT_ENTRY(cef_delete_file);
+  INIT_ENTRY(cef_zip_directory);
+  INIT_ENTRY(cef_load_crlsets_file);
+  INIT_ENTRY(cef_add_cross_origin_whitelist_entry);
+  INIT_ENTRY(cef_remove_cross_origin_whitelist_entry);
+  INIT_ENTRY(cef_clear_cross_origin_whitelist);
+  INIT_ENTRY(cef_parse_url);
+  INIT_ENTRY(cef_create_url);
+  INIT_ENTRY(cef_format_url_for_security_display);
+  INIT_ENTRY(cef_get_mime_type);
+  INIT_ENTRY(cef_get_extensions_for_mime_type);
+  INIT_ENTRY(cef_base64encode);
+  INIT_ENTRY(cef_base64decode);
+  INIT_ENTRY(cef_uriencode);
+  INIT_ENTRY(cef_uridecode);
+  INIT_ENTRY(cef_parse_json);
+  INIT_ENTRY(cef_parse_json_buffer);
+  INIT_ENTRY(cef_parse_jsonand_return_error);
+  INIT_ENTRY(cef_write_json);
+  INIT_ENTRY(cef_get_path);
+  INIT_ENTRY(cef_launch_process);
+  INIT_ENTRY(cef_register_scheme_handler_factory);
+  INIT_ENTRY(cef_clear_scheme_handler_factories);
+  INIT_ENTRY(cef_is_cert_status_error);
+  INIT_ENTRY(cef_currently_on);
+  INIT_ENTRY(cef_post_task);
+  INIT_ENTRY(cef_post_delayed_task);
+  INIT_ENTRY(cef_begin_tracing);
+  INIT_ENTRY(cef_end_tracing);
+  INIT_ENTRY(cef_now_from_system_trace_time);
+  INIT_ENTRY(cef_register_extension);
+  INIT_ENTRY(cef_visit_web_plugin_info);
+  INIT_ENTRY(cef_refresh_web_plugins);
+  INIT_ENTRY(cef_unregister_internal_web_plugin);
+  INIT_ENTRY(cef_register_web_plugin_crash);
+  INIT_ENTRY(cef_is_web_plugin_unstable);
+  INIT_ENTRY(cef_register_widevine_cdm);
+  INIT_ENTRY(cef_execute_java_script_with_user_gesture_for_tests);
+  INIT_ENTRY(cef_browser_host_create_browser);
+  INIT_ENTRY(cef_browser_host_create_browser_sync);
+  INIT_ENTRY(cef_command_line_create);
+  INIT_ENTRY(cef_command_line_get_global);
+  INIT_ENTRY(cef_cookie_manager_get_global_manager);
+  INIT_ENTRY(cef_drag_data_create);
+  INIT_ENTRY(cef_image_create);
+  INIT_ENTRY(cef_media_router_get_global);
+  INIT_ENTRY(cef_menu_model_create);
+  INIT_ENTRY(cef_print_settings_create);
+  INIT_ENTRY(cef_process_message_create);
+  INIT_ENTRY(cef_request_create);
+  INIT_ENTRY(cef_post_data_create);
+  INIT_ENTRY(cef_post_data_element_create);
+  INIT_ENTRY(cef_request_context_get_global_context);
+  INIT_ENTRY(cef_request_context_create_context);
+  INIT_ENTRY(cef_create_context_shared);
+  INIT_ENTRY(cef_resource_bundle_get_global);
+  INIT_ENTRY(cef_response_create);
+  INIT_ENTRY(cef_server_create);
+  INIT_ENTRY(cef_stream_reader_create_for_file);
+  INIT_ENTRY(cef_stream_reader_create_for_data);
+  INIT_ENTRY(cef_stream_reader_create_for_handler);
+  INIT_ENTRY(cef_stream_writer_create_for_file);
+  INIT_ENTRY(cef_stream_writer_create_for_handler);
+  INIT_ENTRY(cef_task_runner_get_for_current_thread);
+  INIT_ENTRY(cef_task_runner_get_for_thread);
+  INIT_ENTRY(cef_thread_create);
+  INIT_ENTRY(cef_urlrequest_create);
+  INIT_ENTRY(cef_v8context_get_current_context);
+  INIT_ENTRY(cef_v8context_get_entered_context);
+  INIT_ENTRY(cef_v8context_in_context);
+  INIT_ENTRY(cef_v8value_create_undefined);
+  INIT_ENTRY(cef_v8value_create_null);
+  INIT_ENTRY(cef_v8value_create_bool);
+  INIT_ENTRY(cef_v8value_create_int);
+  INIT_ENTRY(cef_v8value_create_uint);
+  INIT_ENTRY(cef_v8value_create_double);
+  INIT_ENTRY(cef_v8value_create_date);
+  INIT_ENTRY(cef_v8value_create_string);
+  INIT_ENTRY(cef_v8value_create_object);
+  INIT_ENTRY(cef_v8value_create_array);
+  INIT_ENTRY(cef_v8value_create_array_buffer);
+  INIT_ENTRY(cef_v8value_create_function);
+  INIT_ENTRY(cef_v8stack_trace_get_current);
+  INIT_ENTRY(cef_value_create);
+  INIT_ENTRY(cef_binary_value_create);
+  INIT_ENTRY(cef_dictionary_value_create);
+  INIT_ENTRY(cef_list_value_create);
+  INIT_ENTRY(cef_waitable_event_create);
+  INIT_ENTRY(cef_xml_reader_create);
+  INIT_ENTRY(cef_zip_reader_create);
+  INIT_ENTRY(cef_translator_test_create);
+  INIT_ENTRY(cef_translator_test_ref_ptr_library_create);
+  INIT_ENTRY(cef_translator_test_ref_ptr_library_child_create);
+  INIT_ENTRY(cef_translator_test_ref_ptr_library_child_child_create);
+  INIT_ENTRY(cef_translator_test_scoped_library_create);
+  INIT_ENTRY(cef_translator_test_scoped_library_child_create);
+  INIT_ENTRY(cef_translator_test_scoped_library_child_child_create);
+  INIT_ENTRY(cef_browser_view_create);
+  INIT_ENTRY(cef_browser_view_get_for_browser);
+  INIT_ENTRY(cef_display_get_primary);
+  INIT_ENTRY(cef_display_get_nearest_point);
+  INIT_ENTRY(cef_display_get_matching_bounds);
+  INIT_ENTRY(cef_display_get_count);
+  INIT_ENTRY(cef_display_get_alls);
+  INIT_ENTRY(cef_label_button_create);
+  INIT_ENTRY(cef_menu_button_create);
+  INIT_ENTRY(cef_panel_create);
+  INIT_ENTRY(cef_scroll_view_create);
+  INIT_ENTRY(cef_textfield_create);
+  INIT_ENTRY(cef_window_create_top_level);
+  INIT_ENTRY(cef_api_hash);
+  INIT_ENTRY(cef_version_info);
+  INIT_ENTRY(cef_get_min_log_level);
+  INIT_ENTRY(cef_get_vlog_level);
+  INIT_ENTRY(cef_log);
+  INIT_ENTRY(cef_string_list_alloc);
+  INIT_ENTRY(cef_string_list_size);
+  INIT_ENTRY(cef_string_list_value);
+  INIT_ENTRY(cef_string_list_append);
+  INIT_ENTRY(cef_string_list_clear);
+  INIT_ENTRY(cef_string_list_free);
+  INIT_ENTRY(cef_string_list_copy);
+  INIT_ENTRY(cef_string_map_alloc);
+  INIT_ENTRY(cef_string_map_size);
+  INIT_ENTRY(cef_string_map_find);
+  INIT_ENTRY(cef_string_map_key);
+  INIT_ENTRY(cef_string_map_value);
+  INIT_ENTRY(cef_string_map_append);
+  INIT_ENTRY(cef_string_map_clear);
+  INIT_ENTRY(cef_string_map_free);
+  INIT_ENTRY(cef_string_multimap_alloc);
+  INIT_ENTRY(cef_string_multimap_size);
+  INIT_ENTRY(cef_string_multimap_find_count);
+  INIT_ENTRY(cef_string_multimap_enumerate);
+  INIT_ENTRY(cef_string_multimap_key);
+  INIT_ENTRY(cef_string_multimap_value);
+  INIT_ENTRY(cef_string_multimap_append);
+  INIT_ENTRY(cef_string_multimap_clear);
+  INIT_ENTRY(cef_string_multimap_free);
+  INIT_ENTRY(cef_string_wide_set);
+  INIT_ENTRY(cef_string_utf8_set);
+  INIT_ENTRY(cef_string_utf16_set);
+  INIT_ENTRY(cef_string_wide_clear);
+  INIT_ENTRY(cef_string_utf8_clear);
+  INIT_ENTRY(cef_string_utf16_clear);
+  INIT_ENTRY(cef_string_wide_cmp);
+  INIT_ENTRY(cef_string_utf8_cmp);
+  INIT_ENTRY(cef_string_utf16_cmp);
+  INIT_ENTRY(cef_string_wide_to_utf8);
+  INIT_ENTRY(cef_string_utf8_to_wide);
+  INIT_ENTRY(cef_string_wide_to_utf16);
+  INIT_ENTRY(cef_string_utf16_to_wide);
+  INIT_ENTRY(cef_string_utf8_to_utf16);
+  INIT_ENTRY(cef_string_utf16_to_utf8);
+  INIT_ENTRY(cef_string_ascii_to_wide);
+  INIT_ENTRY(cef_string_ascii_to_utf16);
+  INIT_ENTRY(cef_string_userfree_wide_alloc);
+  INIT_ENTRY(cef_string_userfree_utf8_alloc);
+  INIT_ENTRY(cef_string_userfree_utf16_alloc);
+  INIT_ENTRY(cef_string_userfree_wide_free);
+  INIT_ENTRY(cef_string_userfree_utf8_free);
+  INIT_ENTRY(cef_string_userfree_utf16_free);
+  INIT_ENTRY(cef_string_utf16_to_lower);
+  INIT_ENTRY(cef_string_utf16_to_upper);
+  INIT_ENTRY(cef_get_current_platform_thread_id);
+  INIT_ENTRY(cef_get_current_platform_thread_handle);
+  INIT_ENTRY(cef_time_to_timet);
+  INIT_ENTRY(cef_time_from_timet);
+  INIT_ENTRY(cef_time_to_doublet);
+  INIT_ENTRY(cef_time_from_doublet);
+  INIT_ENTRY(cef_time_now);
+  INIT_ENTRY(cef_time_delta);
+  INIT_ENTRY(cef_trace_event_instant);
+  INIT_ENTRY(cef_trace_event_begin);
+  INIT_ENTRY(cef_trace_event_end);
+  INIT_ENTRY(cef_trace_counter);
+  INIT_ENTRY(cef_trace_counter_id);
+  INIT_ENTRY(cef_trace_event_async_begin);
+  INIT_ENTRY(cef_trace_event_async_step_into);
+  INIT_ENTRY(cef_trace_event_async_step_past);
+  INIT_ENTRY(cef_trace_event_async_end);
+  return 1;
+}
+
+}  // namespace
+
+int cef_load_library(const char* path) {
+  if (g_libcef_handle)
+    return 0;
+
+  g_libcef_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL | RTLD_FIRST);
+  if (!g_libcef_handle) {
+    fprintf(stderr, "dlopen %s: %s\n", path, dlerror());
+    return 0;
+  }
+
+  if (!libcef_init_pointers(path)) {
+    cef_unload_library();
+    return 0;
+  }
+
+  return 1;
+}
+
+int cef_unload_library() {
+  int result = 0;
+  if (g_libcef_handle) {
+    result = !dlclose(g_libcef_handle);
+    if (!result) {
+      fprintf(stderr, "dlclose: %s\n", dlerror());
+    }
+    g_libcef_handle = nullptr;
+  }
+  return result;
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_execute_process(const struct _cef_main_args_t* args,
+                        struct _cef_app_t* application,
+                        void* windows_sandbox_info) {
+  return g_libcef_pointers.cef_execute_process(args, application,
+                                               windows_sandbox_info);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_initialize(const struct _cef_main_args_t* args,
+                   const struct _cef_settings_t* settings,
+                   struct _cef_app_t* application,
+                   void* windows_sandbox_info) {
+  return g_libcef_pointers.cef_initialize(args, settings, application,
+                                          windows_sandbox_info);
+}
+
+NO_SANITIZE("cfi-icall") void cef_shutdown() {
+  g_libcef_pointers.cef_shutdown();
+}
+
+NO_SANITIZE("cfi-icall") void cef_do_message_loop_work() {
+  g_libcef_pointers.cef_do_message_loop_work();
+}
+
+NO_SANITIZE("cfi-icall") void cef_run_message_loop() {
+  g_libcef_pointers.cef_run_message_loop();
+}
+
+NO_SANITIZE("cfi-icall") void cef_quit_message_loop() {
+  g_libcef_pointers.cef_quit_message_loop();
+}
+
+NO_SANITIZE("cfi-icall") void cef_set_osmodal_loop(int osModalLoop) {
+  g_libcef_pointers.cef_set_osmodal_loop(osModalLoop);
+}
+
+NO_SANITIZE("cfi-icall") void cef_enable_highdpi_support() {
+  g_libcef_pointers.cef_enable_highdpi_support();
+}
+
+NO_SANITIZE("cfi-icall") int cef_crash_reporting_enabled() {
+  return g_libcef_pointers.cef_crash_reporting_enabled();
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_set_crash_key_value(const cef_string_t* key,
+                             const cef_string_t* value) {
+  g_libcef_pointers.cef_set_crash_key_value(key, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_create_directory(const cef_string_t* full_path) {
+  return g_libcef_pointers.cef_create_directory(full_path);
+}
+
+NO_SANITIZE("cfi-icall") int cef_get_temp_directory(cef_string_t* temp_dir) {
+  return g_libcef_pointers.cef_get_temp_directory(temp_dir);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_create_new_temp_directory(const cef_string_t* prefix,
+                                  cef_string_t* new_temp_path) {
+  return g_libcef_pointers.cef_create_new_temp_directory(prefix, new_temp_path);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_create_temp_directory_in_directory(const cef_string_t* base_dir,
+                                           const cef_string_t* prefix,
+                                           cef_string_t* new_dir) {
+  return g_libcef_pointers.cef_create_temp_directory_in_directory(
+      base_dir, prefix, new_dir);
+}
+
+NO_SANITIZE("cfi-icall") int cef_directory_exists(const cef_string_t* path) {
+  return g_libcef_pointers.cef_directory_exists(path);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_delete_file(const cef_string_t* path, int recursive) {
+  return g_libcef_pointers.cef_delete_file(path, recursive);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_zip_directory(const cef_string_t* src_dir,
+                      const cef_string_t* dest_file,
+                      int include_hidden_files) {
+  return g_libcef_pointers.cef_zip_directory(src_dir, dest_file,
+                                             include_hidden_files);
+}
+
+NO_SANITIZE("cfi-icall") void cef_load_crlsets_file(const cef_string_t* path) {
+  g_libcef_pointers.cef_load_crlsets_file(path);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_add_cross_origin_whitelist_entry(const cef_string_t* source_origin,
+                                         const cef_string_t* target_protocol,
+                                         const cef_string_t* target_domain,
+                                         int allow_target_subdomains) {
+  return g_libcef_pointers.cef_add_cross_origin_whitelist_entry(
+      source_origin, target_protocol, target_domain, allow_target_subdomains);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_remove_cross_origin_whitelist_entry(const cef_string_t* source_origin,
+                                            const cef_string_t* target_protocol,
+                                            const cef_string_t* target_domain,
+                                            int allow_target_subdomains) {
+  return g_libcef_pointers.cef_remove_cross_origin_whitelist_entry(
+      source_origin, target_protocol, target_domain, allow_target_subdomains);
+}
+
+NO_SANITIZE("cfi-icall") int cef_clear_cross_origin_whitelist() {
+  return g_libcef_pointers.cef_clear_cross_origin_whitelist();
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_parse_url(const cef_string_t* url, struct _cef_urlparts_t* parts) {
+  return g_libcef_pointers.cef_parse_url(url, parts);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_create_url(const struct _cef_urlparts_t* parts, cef_string_t* url) {
+  return g_libcef_pointers.cef_create_url(parts, url);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_t cef_format_url_for_security_display(
+    const cef_string_t* origin_url) {
+  return g_libcef_pointers.cef_format_url_for_security_display(origin_url);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_t cef_get_mime_type(const cef_string_t* extension) {
+  return g_libcef_pointers.cef_get_mime_type(extension);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_get_extensions_for_mime_type(const cef_string_t* mime_type,
+                                      cef_string_list_t extensions) {
+  g_libcef_pointers.cef_get_extensions_for_mime_type(mime_type, extensions);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_t cef_base64encode(const void* data, size_t data_size) {
+  return g_libcef_pointers.cef_base64encode(data, data_size);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_binary_value_t* cef_base64decode(const cef_string_t* data) {
+  return g_libcef_pointers.cef_base64decode(data);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_t cef_uriencode(const cef_string_t* text, int use_plus) {
+  return g_libcef_pointers.cef_uriencode(text, use_plus);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_t cef_uridecode(const cef_string_t* text,
+                                    int convert_to_utf8,
+                                    cef_uri_unescape_rule_t unescape_rule) {
+  return g_libcef_pointers.cef_uridecode(text, convert_to_utf8, unescape_rule);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_value_t* cef_parse_json(const cef_string_t* json_string,
+                                    cef_json_parser_options_t options) {
+  return g_libcef_pointers.cef_parse_json(json_string, options);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_value_t* cef_parse_json_buffer(const void* json,
+                                           size_t json_size,
+                                           cef_json_parser_options_t options) {
+  return g_libcef_pointers.cef_parse_json_buffer(json, json_size, options);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_value_t* cef_parse_jsonand_return_error(
+    const cef_string_t* json_string,
+    cef_json_parser_options_t options,
+    cef_json_parser_error_t* error_code_out,
+    cef_string_t* error_msg_out) {
+  return g_libcef_pointers.cef_parse_jsonand_return_error(
+      json_string, options, error_code_out, error_msg_out);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_t cef_write_json(struct _cef_value_t* node,
+                                     cef_json_writer_options_t options) {
+  return g_libcef_pointers.cef_write_json(node, options);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_get_path(cef_path_key_t key, cef_string_t* path) {
+  return g_libcef_pointers.cef_get_path(key, path);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_launch_process(struct _cef_command_line_t* command_line) {
+  return g_libcef_pointers.cef_launch_process(command_line);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_register_scheme_handler_factory(
+    const cef_string_t* scheme_name,
+    const cef_string_t* domain_name,
+    struct _cef_scheme_handler_factory_t* factory) {
+  return g_libcef_pointers.cef_register_scheme_handler_factory(
+      scheme_name, domain_name, factory);
+}
+
+NO_SANITIZE("cfi-icall") int cef_clear_scheme_handler_factories() {
+  return g_libcef_pointers.cef_clear_scheme_handler_factories();
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_is_cert_status_error(cef_cert_status_t status) {
+  return g_libcef_pointers.cef_is_cert_status_error(status);
+}
+
+NO_SANITIZE("cfi-icall") int cef_currently_on(cef_thread_id_t threadId) {
+  return g_libcef_pointers.cef_currently_on(threadId);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_post_task(cef_thread_id_t threadId, struct _cef_task_t* task) {
+  return g_libcef_pointers.cef_post_task(threadId, task);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_post_delayed_task(cef_thread_id_t threadId,
+                          struct _cef_task_t* task,
+                          int64 delay_ms) {
+  return g_libcef_pointers.cef_post_delayed_task(threadId, task, delay_ms);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_begin_tracing(const cef_string_t* categories,
+                      struct _cef_completion_callback_t* callback) {
+  return g_libcef_pointers.cef_begin_tracing(categories, callback);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_end_tracing(const cef_string_t* tracing_file,
+                    struct _cef_end_tracing_callback_t* callback) {
+  return g_libcef_pointers.cef_end_tracing(tracing_file, callback);
+}
+
+NO_SANITIZE("cfi-icall") int64 cef_now_from_system_trace_time() {
+  return g_libcef_pointers.cef_now_from_system_trace_time();
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_register_extension(const cef_string_t* extension_name,
+                           const cef_string_t* javascript_code,
+                           struct _cef_v8handler_t* handler) {
+  return g_libcef_pointers.cef_register_extension(extension_name,
+                                                  javascript_code, handler);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_visit_web_plugin_info(struct _cef_web_plugin_info_visitor_t* visitor) {
+  g_libcef_pointers.cef_visit_web_plugin_info(visitor);
+}
+
+NO_SANITIZE("cfi-icall") void cef_refresh_web_plugins() {
+  g_libcef_pointers.cef_refresh_web_plugins();
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_unregister_internal_web_plugin(const cef_string_t* path) {
+  g_libcef_pointers.cef_unregister_internal_web_plugin(path);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_register_web_plugin_crash(const cef_string_t* path) {
+  g_libcef_pointers.cef_register_web_plugin_crash(path);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_is_web_plugin_unstable(
+    const cef_string_t* path,
+    struct _cef_web_plugin_unstable_callback_t* callback) {
+  g_libcef_pointers.cef_is_web_plugin_unstable(path, callback);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_register_widevine_cdm(const cef_string_t* path,
+                               struct _cef_register_cdm_callback_t* callback) {
+  g_libcef_pointers.cef_register_widevine_cdm(path, callback);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_execute_java_script_with_user_gesture_for_tests(
+    struct _cef_frame_t* frame,
+    const cef_string_t* javascript) {
+  g_libcef_pointers.cef_execute_java_script_with_user_gesture_for_tests(
+      frame, javascript);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_browser_host_create_browser(
+    const struct _cef_window_info_t* windowInfo,
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context) {
+  return g_libcef_pointers.cef_browser_host_create_browser(
+      windowInfo, client, url, settings, extra_info, request_context);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_browser_t* cef_browser_host_create_browser_sync(
+    const struct _cef_window_info_t* windowInfo,
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context) {
+  return g_libcef_pointers.cef_browser_host_create_browser_sync(
+      windowInfo, client, url, settings, extra_info, request_context);
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_command_line_t* cef_command_line_create() {
+  return g_libcef_pointers.cef_command_line_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_command_line_t* cef_command_line_get_global() {
+  return g_libcef_pointers.cef_command_line_get_global();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_cookie_manager_t* cef_cookie_manager_get_global_manager(
+    struct _cef_completion_callback_t* callback) {
+  return g_libcef_pointers.cef_cookie_manager_get_global_manager(callback);
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_drag_data_t* cef_drag_data_create() {
+  return g_libcef_pointers.cef_drag_data_create();
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_image_t* cef_image_create() {
+  return g_libcef_pointers.cef_image_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_media_router_t* cef_media_router_get_global() {
+  return g_libcef_pointers.cef_media_router_get_global();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_menu_model_t* cef_menu_model_create(
+    struct _cef_menu_model_delegate_t* delegate) {
+  return g_libcef_pointers.cef_menu_model_create(delegate);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_print_settings_t* cef_print_settings_create() {
+  return g_libcef_pointers.cef_print_settings_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_process_message_t* cef_process_message_create(
+    const cef_string_t* name) {
+  return g_libcef_pointers.cef_process_message_create(name);
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_request_t* cef_request_create() {
+  return g_libcef_pointers.cef_request_create();
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_post_data_t* cef_post_data_create() {
+  return g_libcef_pointers.cef_post_data_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_post_data_element_t* cef_post_data_element_create() {
+  return g_libcef_pointers.cef_post_data_element_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_request_context_t* cef_request_context_get_global_context() {
+  return g_libcef_pointers.cef_request_context_get_global_context();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_request_context_t* cef_request_context_create_context(
+    const struct _cef_request_context_settings_t* settings,
+    struct _cef_request_context_handler_t* handler) {
+  return g_libcef_pointers.cef_request_context_create_context(settings,
+                                                              handler);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_request_context_t* cef_create_context_shared(
+    struct _cef_request_context_t* other,
+    struct _cef_request_context_handler_t* handler) {
+  return g_libcef_pointers.cef_create_context_shared(other, handler);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_resource_bundle_t* cef_resource_bundle_get_global() {
+  return g_libcef_pointers.cef_resource_bundle_get_global();
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_response_t* cef_response_create() {
+  return g_libcef_pointers.cef_response_create();
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_server_create(const cef_string_t* address,
+                       uint16 port,
+                       int backlog,
+                       struct _cef_server_handler_t* handler) {
+  g_libcef_pointers.cef_server_create(address, port, backlog, handler);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_stream_reader_t* cef_stream_reader_create_for_file(
+    const cef_string_t* fileName) {
+  return g_libcef_pointers.cef_stream_reader_create_for_file(fileName);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_stream_reader_t* cef_stream_reader_create_for_data(void* data,
+                                                               size_t size) {
+  return g_libcef_pointers.cef_stream_reader_create_for_data(data, size);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_stream_reader_t* cef_stream_reader_create_for_handler(
+    struct _cef_read_handler_t* handler) {
+  return g_libcef_pointers.cef_stream_reader_create_for_handler(handler);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_stream_writer_t* cef_stream_writer_create_for_file(
+    const cef_string_t* fileName) {
+  return g_libcef_pointers.cef_stream_writer_create_for_file(fileName);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_stream_writer_t* cef_stream_writer_create_for_handler(
+    struct _cef_write_handler_t* handler) {
+  return g_libcef_pointers.cef_stream_writer_create_for_handler(handler);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_task_runner_t* cef_task_runner_get_for_current_thread() {
+  return g_libcef_pointers.cef_task_runner_get_for_current_thread();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_task_runner_t* cef_task_runner_get_for_thread(
+    cef_thread_id_t threadId) {
+  return g_libcef_pointers.cef_task_runner_get_for_thread(threadId);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_thread_t* cef_thread_create(
+    const cef_string_t* display_name,
+    cef_thread_priority_t priority,
+    cef_message_loop_type_t message_loop_type,
+    int stoppable,
+    cef_com_init_mode_t com_init_mode) {
+  return g_libcef_pointers.cef_thread_create(
+      display_name, priority, message_loop_type, stoppable, com_init_mode);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_urlrequest_t* cef_urlrequest_create(
+    struct _cef_request_t* request,
+    struct _cef_urlrequest_client_t* client,
+    struct _cef_request_context_t* request_context) {
+  return g_libcef_pointers.cef_urlrequest_create(request, client,
+                                                 request_context);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8context_t* cef_v8context_get_current_context() {
+  return g_libcef_pointers.cef_v8context_get_current_context();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8context_t* cef_v8context_get_entered_context() {
+  return g_libcef_pointers.cef_v8context_get_entered_context();
+}
+
+NO_SANITIZE("cfi-icall") int cef_v8context_in_context() {
+  return g_libcef_pointers.cef_v8context_in_context();
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_v8value_t* cef_v8value_create_undefined() {
+  return g_libcef_pointers.cef_v8value_create_undefined();
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_v8value_t* cef_v8value_create_null() {
+  return g_libcef_pointers.cef_v8value_create_null();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_bool(int value) {
+  return g_libcef_pointers.cef_v8value_create_bool(value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_int(int32 value) {
+  return g_libcef_pointers.cef_v8value_create_int(value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_uint(uint32 value) {
+  return g_libcef_pointers.cef_v8value_create_uint(value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_double(double value) {
+  return g_libcef_pointers.cef_v8value_create_double(value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_date(const cef_time_t* date) {
+  return g_libcef_pointers.cef_v8value_create_date(date);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_string(const cef_string_t* value) {
+  return g_libcef_pointers.cef_v8value_create_string(value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_object(
+    struct _cef_v8accessor_t* accessor,
+    struct _cef_v8interceptor_t* interceptor) {
+  return g_libcef_pointers.cef_v8value_create_object(accessor, interceptor);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_array(int length) {
+  return g_libcef_pointers.cef_v8value_create_array(length);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_array_buffer(
+    void* buffer,
+    size_t length,
+    struct _cef_v8array_buffer_release_callback_t* release_callback) {
+  return g_libcef_pointers.cef_v8value_create_array_buffer(buffer, length,
+                                                           release_callback);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8value_t* cef_v8value_create_function(
+    const cef_string_t* name,
+    struct _cef_v8handler_t* handler) {
+  return g_libcef_pointers.cef_v8value_create_function(name, handler);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_v8stack_trace_t* cef_v8stack_trace_get_current(int frame_limit) {
+  return g_libcef_pointers.cef_v8stack_trace_get_current(frame_limit);
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_value_t* cef_value_create() {
+  return g_libcef_pointers.cef_value_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_binary_value_t* cef_binary_value_create(const void* data,
+                                                    size_t data_size) {
+  return g_libcef_pointers.cef_binary_value_create(data, data_size);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_dictionary_value_t* cef_dictionary_value_create() {
+  return g_libcef_pointers.cef_dictionary_value_create();
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_list_value_t* cef_list_value_create() {
+  return g_libcef_pointers.cef_list_value_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_waitable_event_t* cef_waitable_event_create(
+    int automatic_reset,
+    int initially_signaled) {
+  return g_libcef_pointers.cef_waitable_event_create(automatic_reset,
+                                                     initially_signaled);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_xml_reader_t* cef_xml_reader_create(
+    struct _cef_stream_reader_t* stream,
+    cef_xml_encoding_type_t encodingType,
+    const cef_string_t* URI) {
+  return g_libcef_pointers.cef_xml_reader_create(stream, encodingType, URI);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_zip_reader_t* cef_zip_reader_create(
+    struct _cef_stream_reader_t* stream) {
+  return g_libcef_pointers.cef_zip_reader_create(stream);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_translator_test_t* cef_translator_test_create() {
+  return g_libcef_pointers.cef_translator_test_create();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_translator_test_ref_ptr_library_t*
+cef_translator_test_ref_ptr_library_create(int value) {
+  return g_libcef_pointers.cef_translator_test_ref_ptr_library_create(value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_translator_test_ref_ptr_library_child_t*
+cef_translator_test_ref_ptr_library_child_create(int value, int other_value) {
+  return g_libcef_pointers.cef_translator_test_ref_ptr_library_child_create(
+      value, other_value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_translator_test_ref_ptr_library_child_child_t*
+cef_translator_test_ref_ptr_library_child_child_create(int value,
+                                                       int other_value,
+                                                       int other_other_value) {
+  return g_libcef_pointers
+      .cef_translator_test_ref_ptr_library_child_child_create(
+          value, other_value, other_other_value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_translator_test_scoped_library_t*
+cef_translator_test_scoped_library_create(int value) {
+  return g_libcef_pointers.cef_translator_test_scoped_library_create(value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_translator_test_scoped_library_child_t*
+cef_translator_test_scoped_library_child_create(int value, int other_value) {
+  return g_libcef_pointers.cef_translator_test_scoped_library_child_create(
+      value, other_value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_translator_test_scoped_library_child_child_t*
+cef_translator_test_scoped_library_child_child_create(int value,
+                                                      int other_value,
+                                                      int other_other_value) {
+  return g_libcef_pointers
+      .cef_translator_test_scoped_library_child_child_create(value, other_value,
+                                                             other_other_value);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_browser_view_t* cef_browser_view_create(
+    struct _cef_client_t* client,
+    const cef_string_t* url,
+    const struct _cef_browser_settings_t* settings,
+    struct _cef_dictionary_value_t* extra_info,
+    struct _cef_request_context_t* request_context,
+    struct _cef_browser_view_delegate_t* delegate) {
+  return g_libcef_pointers.cef_browser_view_create(
+      client, url, settings, extra_info, request_context, delegate);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_browser_view_t* cef_browser_view_get_for_browser(
+    struct _cef_browser_t* browser) {
+  return g_libcef_pointers.cef_browser_view_get_for_browser(browser);
+}
+
+NO_SANITIZE("cfi-icall") struct _cef_display_t* cef_display_get_primary() {
+  return g_libcef_pointers.cef_display_get_primary();
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_display_t* cef_display_get_nearest_point(const cef_point_t* point,
+                                                     int input_pixel_coords) {
+  return g_libcef_pointers.cef_display_get_nearest_point(point,
+                                                         input_pixel_coords);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_display_t* cef_display_get_matching_bounds(const cef_rect_t* bounds,
+                                                       int input_pixel_coords) {
+  return g_libcef_pointers.cef_display_get_matching_bounds(bounds,
+                                                           input_pixel_coords);
+}
+
+NO_SANITIZE("cfi-icall") size_t cef_display_get_count() {
+  return g_libcef_pointers.cef_display_get_count();
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_display_get_alls(size_t* displaysCount,
+                          struct _cef_display_t** displays) {
+  g_libcef_pointers.cef_display_get_alls(displaysCount, displays);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_label_button_t* cef_label_button_create(
+    struct _cef_button_delegate_t* delegate,
+    const cef_string_t* text) {
+  return g_libcef_pointers.cef_label_button_create(delegate, text);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_menu_button_t* cef_menu_button_create(
+    struct _cef_menu_button_delegate_t* delegate,
+    const cef_string_t* text) {
+  return g_libcef_pointers.cef_menu_button_create(delegate, text);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_panel_t* cef_panel_create(struct _cef_panel_delegate_t* delegate) {
+  return g_libcef_pointers.cef_panel_create(delegate);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_scroll_view_t* cef_scroll_view_create(
+    struct _cef_view_delegate_t* delegate) {
+  return g_libcef_pointers.cef_scroll_view_create(delegate);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_textfield_t* cef_textfield_create(
+    struct _cef_textfield_delegate_t* delegate) {
+  return g_libcef_pointers.cef_textfield_create(delegate);
+}
+
+NO_SANITIZE("cfi-icall")
+struct _cef_window_t* cef_window_create_top_level(
+    struct _cef_window_delegate_t* delegate) {
+  return g_libcef_pointers.cef_window_create_top_level(delegate);
+}
+
+NO_SANITIZE("cfi-icall") const char* cef_api_hash(int entry) {
+  return g_libcef_pointers.cef_api_hash(entry);
+}
+
+NO_SANITIZE("cfi-icall") int cef_version_info(int entry) {
+  return g_libcef_pointers.cef_version_info(entry);
+}
+
+NO_SANITIZE("cfi-icall") int cef_get_min_log_level() {
+  return g_libcef_pointers.cef_get_min_log_level();
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_get_vlog_level(const char* file_start, size_t N) {
+  return g_libcef_pointers.cef_get_vlog_level(file_start, N);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_log(const char* file, int line, int severity, const char* message) {
+  g_libcef_pointers.cef_log(file, line, severity, message);
+}
+
+NO_SANITIZE("cfi-icall") cef_string_list_t cef_string_list_alloc() {
+  return g_libcef_pointers.cef_string_list_alloc();
+}
+
+NO_SANITIZE("cfi-icall") size_t cef_string_list_size(cef_string_list_t list) {
+  return g_libcef_pointers.cef_string_list_size(list);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_list_value(cef_string_list_t list,
+                          size_t index,
+                          cef_string_t* value) {
+  return g_libcef_pointers.cef_string_list_value(list, index, value);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_string_list_append(cef_string_list_t list, const cef_string_t* value) {
+  g_libcef_pointers.cef_string_list_append(list, value);
+}
+
+NO_SANITIZE("cfi-icall") void cef_string_list_clear(cef_string_list_t list) {
+  g_libcef_pointers.cef_string_list_clear(list);
+}
+
+NO_SANITIZE("cfi-icall") void cef_string_list_free(cef_string_list_t list) {
+  g_libcef_pointers.cef_string_list_free(list);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_list_t cef_string_list_copy(cef_string_list_t list) {
+  return g_libcef_pointers.cef_string_list_copy(list);
+}
+
+NO_SANITIZE("cfi-icall") cef_string_map_t cef_string_map_alloc() {
+  return g_libcef_pointers.cef_string_map_alloc();
+}
+
+NO_SANITIZE("cfi-icall") size_t cef_string_map_size(cef_string_map_t map) {
+  return g_libcef_pointers.cef_string_map_size(map);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_map_find(cef_string_map_t map,
+                        const cef_string_t* key,
+                        cef_string_t* value) {
+  return g_libcef_pointers.cef_string_map_find(map, key, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_map_key(cef_string_map_t map, size_t index, cef_string_t* key) {
+  return g_libcef_pointers.cef_string_map_key(map, index, key);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_map_value(cef_string_map_t map,
+                         size_t index,
+                         cef_string_t* value) {
+  return g_libcef_pointers.cef_string_map_value(map, index, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_map_append(cef_string_map_t map,
+                          const cef_string_t* key,
+                          const cef_string_t* value) {
+  return g_libcef_pointers.cef_string_map_append(map, key, value);
+}
+
+NO_SANITIZE("cfi-icall") void cef_string_map_clear(cef_string_map_t map) {
+  g_libcef_pointers.cef_string_map_clear(map);
+}
+
+NO_SANITIZE("cfi-icall") void cef_string_map_free(cef_string_map_t map) {
+  g_libcef_pointers.cef_string_map_free(map);
+}
+
+NO_SANITIZE("cfi-icall") cef_string_multimap_t cef_string_multimap_alloc() {
+  return g_libcef_pointers.cef_string_multimap_alloc();
+}
+
+NO_SANITIZE("cfi-icall")
+size_t cef_string_multimap_size(cef_string_multimap_t map) {
+  return g_libcef_pointers.cef_string_multimap_size(map);
+}
+
+NO_SANITIZE("cfi-icall")
+size_t cef_string_multimap_find_count(cef_string_multimap_t map,
+                                      const cef_string_t* key) {
+  return g_libcef_pointers.cef_string_multimap_find_count(map, key);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_multimap_enumerate(cef_string_multimap_t map,
+                                  const cef_string_t* key,
+                                  size_t value_index,
+                                  cef_string_t* value) {
+  return g_libcef_pointers.cef_string_multimap_enumerate(map, key, value_index,
+                                                         value);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_multimap_key(cef_string_multimap_t map,
+                            size_t index,
+                            cef_string_t* key) {
+  return g_libcef_pointers.cef_string_multimap_key(map, index, key);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_multimap_value(cef_string_multimap_t map,
+                              size_t index,
+                              cef_string_t* value) {
+  return g_libcef_pointers.cef_string_multimap_value(map, index, value);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_multimap_append(cef_string_multimap_t map,
+                               const cef_string_t* key,
+                               const cef_string_t* value) {
+  return g_libcef_pointers.cef_string_multimap_append(map, key, value);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_string_multimap_clear(cef_string_multimap_t map) {
+  g_libcef_pointers.cef_string_multimap_clear(map);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_string_multimap_free(cef_string_multimap_t map) {
+  g_libcef_pointers.cef_string_multimap_free(map);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_wide_set(const wchar_t* src,
+                        size_t src_len,
+                        cef_string_wide_t* output,
+                        int copy) {
+  return g_libcef_pointers.cef_string_wide_set(src, src_len, output, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf8_set(const char* src,
+                        size_t src_len,
+                        cef_string_utf8_t* output,
+                        int copy) {
+  return g_libcef_pointers.cef_string_utf8_set(src, src_len, output, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf16_set(const char16* src,
+                         size_t src_len,
+                         cef_string_utf16_t* output,
+                         int copy) {
+  return g_libcef_pointers.cef_string_utf16_set(src, src_len, output, copy);
+}
+
+NO_SANITIZE("cfi-icall") void cef_string_wide_clear(cef_string_wide_t* str) {
+  g_libcef_pointers.cef_string_wide_clear(str);
+}
+
+NO_SANITIZE("cfi-icall") void cef_string_utf8_clear(cef_string_utf8_t* str) {
+  g_libcef_pointers.cef_string_utf8_clear(str);
+}
+
+NO_SANITIZE("cfi-icall") void cef_string_utf16_clear(cef_string_utf16_t* str) {
+  g_libcef_pointers.cef_string_utf16_clear(str);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_wide_cmp(const cef_string_wide_t* str1,
+                        const cef_string_wide_t* str2) {
+  return g_libcef_pointers.cef_string_wide_cmp(str1, str2);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf8_cmp(const cef_string_utf8_t* str1,
+                        const cef_string_utf8_t* str2) {
+  return g_libcef_pointers.cef_string_utf8_cmp(str1, str2);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf16_cmp(const cef_string_utf16_t* str1,
+                         const cef_string_utf16_t* str2) {
+  return g_libcef_pointers.cef_string_utf16_cmp(str1, str2);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_wide_to_utf8(const wchar_t* src,
+                            size_t src_len,
+                            cef_string_utf8_t* output) {
+  return g_libcef_pointers.cef_string_wide_to_utf8(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf8_to_wide(const char* src,
+                            size_t src_len,
+                            cef_string_wide_t* output) {
+  return g_libcef_pointers.cef_string_utf8_to_wide(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_wide_to_utf16(const wchar_t* src,
+                             size_t src_len,
+                             cef_string_utf16_t* output) {
+  return g_libcef_pointers.cef_string_wide_to_utf16(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf16_to_wide(const char16* src,
+                             size_t src_len,
+                             cef_string_wide_t* output) {
+  return g_libcef_pointers.cef_string_utf16_to_wide(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf8_to_utf16(const char* src,
+                             size_t src_len,
+                             cef_string_utf16_t* output) {
+  return g_libcef_pointers.cef_string_utf8_to_utf16(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf16_to_utf8(const char16* src,
+                             size_t src_len,
+                             cef_string_utf8_t* output) {
+  return g_libcef_pointers.cef_string_utf16_to_utf8(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_ascii_to_wide(const char* src,
+                             size_t src_len,
+                             cef_string_wide_t* output) {
+  return g_libcef_pointers.cef_string_ascii_to_wide(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_ascii_to_utf16(const char* src,
+                              size_t src_len,
+                              cef_string_utf16_t* output) {
+  return g_libcef_pointers.cef_string_ascii_to_utf16(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_wide_t cef_string_userfree_wide_alloc() {
+  return g_libcef_pointers.cef_string_userfree_wide_alloc();
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_utf8_t cef_string_userfree_utf8_alloc() {
+  return g_libcef_pointers.cef_string_userfree_utf8_alloc();
+}
+
+NO_SANITIZE("cfi-icall")
+cef_string_userfree_utf16_t cef_string_userfree_utf16_alloc() {
+  return g_libcef_pointers.cef_string_userfree_utf16_alloc();
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_string_userfree_wide_free(cef_string_userfree_wide_t str) {
+  g_libcef_pointers.cef_string_userfree_wide_free(str);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_string_userfree_utf8_free(cef_string_userfree_utf8_t str) {
+  g_libcef_pointers.cef_string_userfree_utf8_free(str);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_string_userfree_utf16_free(cef_string_userfree_utf16_t str) {
+  g_libcef_pointers.cef_string_userfree_utf16_free(str);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf16_to_lower(const char16* src,
+                              size_t src_len,
+                              cef_string_utf16_t* output) {
+  return g_libcef_pointers.cef_string_utf16_to_lower(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_string_utf16_to_upper(const char16* src,
+                              size_t src_len,
+                              cef_string_utf16_t* output) {
+  return g_libcef_pointers.cef_string_utf16_to_upper(src, src_len, output);
+}
+
+NO_SANITIZE("cfi-icall")
+cef_platform_thread_id_t cef_get_current_platform_thread_id() {
+  return g_libcef_pointers.cef_get_current_platform_thread_id();
+}
+
+NO_SANITIZE("cfi-icall")
+cef_platform_thread_handle_t cef_get_current_platform_thread_handle() {
+  return g_libcef_pointers.cef_get_current_platform_thread_handle();
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_time_to_timet(const cef_time_t* cef_time, time_t* time) {
+  return g_libcef_pointers.cef_time_to_timet(cef_time, time);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_time_from_timet(time_t time, cef_time_t* cef_time) {
+  return g_libcef_pointers.cef_time_from_timet(time, cef_time);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_time_to_doublet(const cef_time_t* cef_time, double* time) {
+  return g_libcef_pointers.cef_time_to_doublet(cef_time, time);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_time_from_doublet(double time, cef_time_t* cef_time) {
+  return g_libcef_pointers.cef_time_from_doublet(time, cef_time);
+}
+
+NO_SANITIZE("cfi-icall") int cef_time_now(cef_time_t* cef_time) {
+  return g_libcef_pointers.cef_time_now(cef_time);
+}
+
+NO_SANITIZE("cfi-icall")
+int cef_time_delta(const cef_time_t* cef_time1,
+                   const cef_time_t* cef_time2,
+                   long long* delta) {
+  return g_libcef_pointers.cef_time_delta(cef_time1, cef_time2, delta);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_event_instant(const char* category,
+                             const char* name,
+                             const char* arg1_name,
+                             uint64 arg1_val,
+                             const char* arg2_name,
+                             uint64 arg2_val,
+                             int copy) {
+  g_libcef_pointers.cef_trace_event_instant(category, name, arg1_name, arg1_val,
+                                            arg2_name, arg2_val, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_event_begin(const char* category,
+                           const char* name,
+                           const char* arg1_name,
+                           uint64 arg1_val,
+                           const char* arg2_name,
+                           uint64 arg2_val,
+                           int copy) {
+  g_libcef_pointers.cef_trace_event_begin(category, name, arg1_name, arg1_val,
+                                          arg2_name, arg2_val, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_event_end(const char* category,
+                         const char* name,
+                         const char* arg1_name,
+                         uint64 arg1_val,
+                         const char* arg2_name,
+                         uint64 arg2_val,
+                         int copy) {
+  g_libcef_pointers.cef_trace_event_end(category, name, arg1_name, arg1_val,
+                                        arg2_name, arg2_val, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_counter(const char* category,
+                       const char* name,
+                       const char* value1_name,
+                       uint64 value1_val,
+                       const char* value2_name,
+                       uint64 value2_val,
+                       int copy) {
+  g_libcef_pointers.cef_trace_counter(category, name, value1_name, value1_val,
+                                      value2_name, value2_val, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_counter_id(const char* category,
+                          const char* name,
+                          uint64 id,
+                          const char* value1_name,
+                          uint64 value1_val,
+                          const char* value2_name,
+                          uint64 value2_val,
+                          int copy) {
+  g_libcef_pointers.cef_trace_counter_id(category, name, id, value1_name,
+                                         value1_val, value2_name, value2_val,
+                                         copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_event_async_begin(const char* category,
+                                 const char* name,
+                                 uint64 id,
+                                 const char* arg1_name,
+                                 uint64 arg1_val,
+                                 const char* arg2_name,
+                                 uint64 arg2_val,
+                                 int copy) {
+  g_libcef_pointers.cef_trace_event_async_begin(
+      category, name, id, arg1_name, arg1_val, arg2_name, arg2_val, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_event_async_step_into(const char* category,
+                                     const char* name,
+                                     uint64 id,
+                                     uint64 step,
+                                     const char* arg1_name,
+                                     uint64 arg1_val,
+                                     int copy) {
+  g_libcef_pointers.cef_trace_event_async_step_into(category, name, id, step,
+                                                    arg1_name, arg1_val, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_event_async_step_past(const char* category,
+                                     const char* name,
+                                     uint64 id,
+                                     uint64 step,
+                                     const char* arg1_name,
+                                     uint64 arg1_val,
+                                     int copy) {
+  g_libcef_pointers.cef_trace_event_async_step_past(category, name, id, step,
+                                                    arg1_name, arg1_val, copy);
+}
+
+NO_SANITIZE("cfi-icall")
+void cef_trace_event_async_end(const char* category,
+                               const char* name,
+                               uint64 id,
+                               const char* arg1_name,
+                               uint64 arg1_val,
+                               const char* arg2_name,
+                               uint64 arg2_val,
+                               int copy) {
+  g_libcef_pointers.cef_trace_event_async_end(
+      category, name, id, arg1_name, arg1_val, arg2_name, arg2_val, copy);
+}
diff --git a/src/libcef_dll/wrapper/libcef_dll_wrapper.cc b/src/libcef_dll/wrapper/libcef_dll_wrapper.cc
new file mode 100644
index 0000000..052e3a8
--- /dev/null
+++ b/src/libcef_dll/wrapper/libcef_dll_wrapper.cc
@@ -0,0 +1,899 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=7db8dbe24a2510d9ae0649f1569909711017c064$
+//
+
+#include "include/capi/cef_app_capi.h"
+#include "include/capi/cef_crash_util_capi.h"
+#include "include/capi/cef_file_util_capi.h"
+#include "include/capi/cef_origin_whitelist_capi.h"
+#include "include/capi/cef_parser_capi.h"
+#include "include/capi/cef_path_util_capi.h"
+#include "include/capi/cef_process_util_capi.h"
+#include "include/capi/cef_scheme_capi.h"
+#include "include/capi/cef_ssl_info_capi.h"
+#include "include/capi/cef_task_capi.h"
+#include "include/capi/cef_trace_capi.h"
+#include "include/capi/cef_v8_capi.h"
+#include "include/capi/cef_web_plugin_capi.h"
+#include "include/capi/test/cef_test_helpers_capi.h"
+#include "include/cef_api_hash.h"
+#include "include/cef_app.h"
+#include "include/cef_crash_util.h"
+#include "include/cef_file_util.h"
+#include "include/cef_origin_whitelist.h"
+#include "include/cef_parser.h"
+#include "include/cef_path_util.h"
+#include "include/cef_process_util.h"
+#include "include/cef_scheme.h"
+#include "include/cef_ssl_info.h"
+#include "include/cef_task.h"
+#include "include/cef_trace.h"
+#include "include/cef_v8.h"
+#include "include/cef_web_plugin.h"
+#include "include/test/cef_test_helpers.h"
+#include "libcef_dll/cpptoc/app_cpptoc.h"
+#include "libcef_dll/cpptoc/completion_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/end_tracing_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/register_cdm_callback_cpptoc.h"
+#include "libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h"
+#include "libcef_dll/cpptoc/task_cpptoc.h"
+#include "libcef_dll/cpptoc/v8handler_cpptoc.h"
+#include "libcef_dll/cpptoc/web_plugin_info_visitor_cpptoc.h"
+#include "libcef_dll/cpptoc/web_plugin_unstable_callback_cpptoc.h"
+#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
+#include "libcef_dll/ctocpp/command_line_ctocpp.h"
+#include "libcef_dll/ctocpp/frame_ctocpp.h"
+#include "libcef_dll/ctocpp/value_ctocpp.h"
+#include "libcef_dll/shutdown_checker.h"
+#include "libcef_dll/transfer_util.h"
+
+// Define used to facilitate parsing.
+#define CEF_GLOBAL
+
+// GLOBAL METHODS - Body may be edited by hand.
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL int CefExecuteProcess(const CefMainArgs& args,
+                                 CefRefPtr<CefApp> application,
+                                 void* windows_sandbox_info) {
+  const char* api_hash = cef_api_hash(0);
+  if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) {
+    // The libcef API hash does not match the current header API hash.
+    NOTREACHED();
+    return 0;
+  }
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: application, windows_sandbox_info
+
+  // Execute
+  int _retval = cef_execute_process(&args, CefAppCppToC::Wrap(application),
+                                    windows_sandbox_info);
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefInitialize(const CefMainArgs& args,
+                              const CefSettings& settings,
+                              CefRefPtr<CefApp> application,
+                              void* windows_sandbox_info) {
+  const char* api_hash = cef_api_hash(0);
+  if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) {
+    // The libcef API hash does not match the current header API hash.
+    NOTREACHED();
+    return false;
+  }
+
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: application, windows_sandbox_info
+
+  // Execute
+  int _retval = cef_initialize(
+      &args, &settings, CefAppCppToC::Wrap(application), windows_sandbox_info);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefShutdown() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+#if DCHECK_IS_ON()
+  shutdown_checker::SetIsShutdown();
+#endif
+
+  // Execute
+  cef_shutdown();
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefDoMessageLoopWork() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_do_message_loop_work();
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefRunMessageLoop() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_run_message_loop();
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefQuitMessageLoop() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_quit_message_loop();
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefSetOSModalLoop(bool osModalLoop) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_set_osmodal_loop(osModalLoop);
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefEnableHighDPISupport() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_enable_highdpi_support();
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL bool CefCrashReportingEnabled() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_crash_reporting_enabled();
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL
+void CefSetCrashKeyValue(const CefString& key, const CefString& value) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: key; type: string_byref_const
+  DCHECK(!key.empty());
+  if (key.empty())
+    return;
+  // Unverified params: value
+
+  // Execute
+  cef_set_crash_key_value(key.GetStruct(), value.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefCreateDirectory(const CefString& full_path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: full_path; type: string_byref_const
+  DCHECK(!full_path.empty());
+  if (full_path.empty())
+    return false;
+
+  // Execute
+  int _retval = cef_create_directory(full_path.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefGetTempDirectory(CefString& temp_dir) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_get_temp_directory(temp_dir.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefCreateNewTempDirectory(const CefString& prefix,
+                                          CefString& new_temp_path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: prefix
+
+  // Execute
+  int _retval = cef_create_new_temp_directory(
+      prefix.GetStruct(), new_temp_path.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefCreateTempDirectoryInDirectory(const CefString& base_dir,
+                                                  const CefString& prefix,
+                                                  CefString& new_dir) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: base_dir; type: string_byref_const
+  DCHECK(!base_dir.empty());
+  if (base_dir.empty())
+    return false;
+  // Unverified params: prefix
+
+  // Execute
+  int _retval = cef_create_temp_directory_in_directory(
+      base_dir.GetStruct(), prefix.GetStruct(), new_dir.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefDirectoryExists(const CefString& path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return false;
+
+  // Execute
+  int _retval = cef_directory_exists(path.GetStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefDeleteFile(const CefString& path, bool recursive) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return false;
+
+  // Execute
+  int _retval = cef_delete_file(path.GetStruct(), recursive);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefZipDirectory(const CefString& src_dir,
+                                const CefString& dest_file,
+                                bool include_hidden_files) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: src_dir; type: string_byref_const
+  DCHECK(!src_dir.empty());
+  if (src_dir.empty())
+    return false;
+  // Verify param: dest_file; type: string_byref_const
+  DCHECK(!dest_file.empty());
+  if (dest_file.empty())
+    return false;
+
+  // Execute
+  int _retval = cef_zip_directory(src_dir.GetStruct(), dest_file.GetStruct(),
+                                  include_hidden_files);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL void CefLoadCRLSetsFile(const CefString& path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+
+  // Execute
+  cef_load_crlsets_file(path.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL
+bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin,
+                                     const CefString& target_protocol,
+                                     const CefString& target_domain,
+                                     bool allow_target_subdomains) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: source_origin; type: string_byref_const
+  DCHECK(!source_origin.empty());
+  if (source_origin.empty())
+    return false;
+  // Verify param: target_protocol; type: string_byref_const
+  DCHECK(!target_protocol.empty());
+  if (target_protocol.empty())
+    return false;
+  // Unverified params: target_domain
+
+  // Execute
+  int _retval = cef_add_cross_origin_whitelist_entry(
+      source_origin.GetStruct(), target_protocol.GetStruct(),
+      target_domain.GetStruct(), allow_target_subdomains);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL
+bool CefRemoveCrossOriginWhitelistEntry(const CefString& source_origin,
+                                        const CefString& target_protocol,
+                                        const CefString& target_domain,
+                                        bool allow_target_subdomains) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: source_origin; type: string_byref_const
+  DCHECK(!source_origin.empty());
+  if (source_origin.empty())
+    return false;
+  // Verify param: target_protocol; type: string_byref_const
+  DCHECK(!target_protocol.empty());
+  if (target_protocol.empty())
+    return false;
+  // Unverified params: target_domain
+
+  // Execute
+  int _retval = cef_remove_cross_origin_whitelist_entry(
+      source_origin.GetStruct(), target_protocol.GetStruct(),
+      target_domain.GetStruct(), allow_target_subdomains);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL bool CefClearCrossOriginWhitelist() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_clear_cross_origin_whitelist();
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefParseURL(const CefString& url, CefURLParts& parts) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: url; type: string_byref_const
+  DCHECK(!url.empty());
+  if (url.empty())
+    return false;
+
+  // Execute
+  int _retval = cef_parse_url(url.GetStruct(), &parts);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefCreateURL(const CefURLParts& parts, CefString& url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_create_url(&parts, url.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefString
+CefFormatUrlForSecurityDisplay(const CefString& origin_url) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: origin_url; type: string_byref_const
+  DCHECK(!origin_url.empty());
+  if (origin_url.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      cef_format_url_for_security_display(origin_url.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefString CefGetMimeType(const CefString& extension) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension; type: string_byref_const
+  DCHECK(!extension.empty());
+  if (extension.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval = cef_get_mime_type(extension.GetStruct());
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL
+void CefGetExtensionsForMimeType(const CefString& mime_type,
+                                 std::vector<CefString>& extensions) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: mime_type; type: string_byref_const
+  DCHECK(!mime_type.empty());
+  if (mime_type.empty())
+    return;
+
+  // Translate param: extensions; type: string_vec_byref
+  cef_string_list_t extensionsList = cef_string_list_alloc();
+  DCHECK(extensionsList);
+  if (extensionsList)
+    transfer_string_list_contents(extensions, extensionsList);
+
+  // Execute
+  cef_get_extensions_for_mime_type(mime_type.GetStruct(), extensionsList);
+
+  // Restore param:extensions; type: string_vec_byref
+  if (extensionsList) {
+    extensions.clear();
+    transfer_string_list_contents(extensionsList, extensions);
+    cef_string_list_free(extensionsList);
+  }
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefString CefBase64Encode(const void* data, size_t data_size) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: simple_byaddr
+  DCHECK(data);
+  if (!data)
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval = cef_base64encode(data, data_size);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefRefPtr<CefBinaryValue> CefBase64Decode(const CefString& data) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: data; type: string_byref_const
+  DCHECK(!data.empty());
+  if (data.empty())
+    return nullptr;
+
+  // Execute
+  cef_binary_value_t* _retval = cef_base64decode(data.GetStruct());
+
+  // Return type: refptr_same
+  return CefBinaryValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefString CefURIEncode(const CefString& text, bool use_plus) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval = cef_uriencode(text.GetStruct(), use_plus);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefString CefURIDecode(const CefString& text,
+                                  bool convert_to_utf8,
+                                  cef_uri_unescape_rule_t unescape_rule) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: text; type: string_byref_const
+  DCHECK(!text.empty());
+  if (text.empty())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      cef_uridecode(text.GetStruct(), convert_to_utf8, unescape_rule);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefRefPtr<CefValue> CefParseJSON(const CefString& json_string,
+                                            cef_json_parser_options_t options) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: json_string; type: string_byref_const
+  DCHECK(!json_string.empty());
+  if (json_string.empty())
+    return nullptr;
+
+  // Execute
+  cef_value_t* _retval = cef_parse_json(json_string.GetStruct(), options);
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefRefPtr<CefValue> CefParseJSON(const void* json,
+                                            size_t json_size,
+                                            cef_json_parser_options_t options) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: json; type: simple_byaddr
+  DCHECK(json);
+  if (!json)
+    return nullptr;
+
+  // Execute
+  cef_value_t* _retval = cef_parse_json_buffer(json, json_size, options);
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefRefPtr<CefValue> CefParseJSONAndReturnError(
+    const CefString& json_string,
+    cef_json_parser_options_t options,
+    cef_json_parser_error_t& error_code_out,
+    CefString& error_msg_out) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: json_string; type: string_byref_const
+  DCHECK(!json_string.empty());
+  if (json_string.empty())
+    return nullptr;
+
+  // Execute
+  cef_value_t* _retval = cef_parse_jsonand_return_error(
+      json_string.GetStruct(), options, &error_code_out,
+      error_msg_out.GetWritableStruct());
+
+  // Return type: refptr_same
+  return CefValueCToCpp::Wrap(_retval);
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL CefString CefWriteJSON(CefRefPtr<CefValue> node,
+                                  cef_json_writer_options_t options) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: node; type: refptr_same
+  DCHECK(node.get());
+  if (!node.get())
+    return CefString();
+
+  // Execute
+  cef_string_userfree_t _retval =
+      cef_write_json(CefValueCToCpp::Unwrap(node), options);
+
+  // Return type: string
+  CefString _retvalStr;
+  _retvalStr.AttachToUserFree(_retval);
+  return _retvalStr;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefGetPath(PathKey key, CefString& path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_get_path(key, path.GetWritableStruct());
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefLaunchProcess(CefRefPtr<CefCommandLine> command_line) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: command_line; type: refptr_same
+  DCHECK(command_line.get());
+  if (!command_line.get())
+    return false;
+
+  // Execute
+  int _retval = cef_launch_process(CefCommandLineCToCpp::Unwrap(command_line));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefRegisterSchemeHandlerFactory(
+    const CefString& scheme_name,
+    const CefString& domain_name,
+    CefRefPtr<CefSchemeHandlerFactory> factory) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: scheme_name; type: string_byref_const
+  DCHECK(!scheme_name.empty());
+  if (scheme_name.empty())
+    return false;
+  // Unverified params: domain_name, factory
+
+  // Execute
+  int _retval = cef_register_scheme_handler_factory(
+      scheme_name.GetStruct(), domain_name.GetStruct(),
+      CefSchemeHandlerFactoryCppToC::Wrap(factory));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL bool CefClearSchemeHandlerFactories() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_clear_scheme_handler_factories();
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefIsCertStatusError(cef_cert_status_t status) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_is_cert_status_error(status);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL bool CefCurrentlyOn(CefThreadId threadId) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int _retval = cef_currently_on(threadId);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefPostTask(CefThreadId threadId, CefRefPtr<CefTask> task) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: task; type: refptr_diff
+  DCHECK(task.get());
+  if (!task.get())
+    return false;
+
+  // Execute
+  int _retval = cef_post_task(threadId, CefTaskCppToC::Wrap(task));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefPostDelayedTask(CefThreadId threadId,
+                                   CefRefPtr<CefTask> task,
+                                   int64 delay_ms) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: task; type: refptr_diff
+  DCHECK(task.get());
+  if (!task.get())
+    return false;
+
+  // Execute
+  int _retval =
+      cef_post_delayed_task(threadId, CefTaskCppToC::Wrap(task), delay_ms);
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefBeginTracing(const CefString& categories,
+                                CefRefPtr<CefCompletionCallback> callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: categories, callback
+
+  // Execute
+  int _retval = cef_begin_tracing(categories.GetStruct(),
+                                  CefCompletionCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefEndTracing(const CefString& tracing_file,
+                              CefRefPtr<CefEndTracingCallback> callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Unverified params: tracing_file, callback
+
+  // Execute
+  int _retval = cef_end_tracing(tracing_file.GetStruct(),
+                                CefEndTracingCallbackCppToC::Wrap(callback));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL int64 CefNowFromSystemTraceTime() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  int64 _retval = cef_now_from_system_trace_time();
+
+  // Return type: simple
+  return _retval;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL bool CefRegisterExtension(const CefString& extension_name,
+                                     const CefString& javascript_code,
+                                     CefRefPtr<CefV8Handler> handler) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: extension_name; type: string_byref_const
+  DCHECK(!extension_name.empty());
+  if (extension_name.empty())
+    return false;
+  // Verify param: javascript_code; type: string_byref_const
+  DCHECK(!javascript_code.empty());
+  if (javascript_code.empty())
+    return false;
+  // Unverified params: handler
+
+  // Execute
+  int _retval = cef_register_extension(extension_name.GetStruct(),
+                                       javascript_code.GetStruct(),
+                                       CefV8HandlerCppToC::Wrap(handler));
+
+  // Return type: bool
+  return _retval ? true : false;
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL
+void CefVisitWebPluginInfo(CefRefPtr<CefWebPluginInfoVisitor> visitor) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: visitor; type: refptr_diff
+  DCHECK(visitor.get());
+  if (!visitor.get())
+    return;
+
+  // Execute
+  cef_visit_web_plugin_info(CefWebPluginInfoVisitorCppToC::Wrap(visitor));
+}
+
+NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefRefreshWebPlugins() {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Execute
+  cef_refresh_web_plugins();
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL void CefUnregisterInternalWebPlugin(const CefString& path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+
+  // Execute
+  cef_unregister_internal_web_plugin(path.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL void CefRegisterWebPluginCrash(const CefString& path) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+
+  // Execute
+  cef_register_web_plugin_crash(path.GetStruct());
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL void CefIsWebPluginUnstable(
+    const CefString& path,
+    CefRefPtr<CefWebPluginUnstableCallback> callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+  // Verify param: callback; type: refptr_diff
+  DCHECK(callback.get());
+  if (!callback.get())
+    return;
+
+  // Execute
+  cef_is_web_plugin_unstable(
+      path.GetStruct(), CefWebPluginUnstableCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL
+void CefRegisterWidevineCdm(const CefString& path,
+                            CefRefPtr<CefRegisterCdmCallback> callback) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: path; type: string_byref_const
+  DCHECK(!path.empty());
+  if (path.empty())
+    return;
+  // Unverified params: callback
+
+  // Execute
+  cef_register_widevine_cdm(path.GetStruct(),
+                            CefRegisterCdmCallbackCppToC::Wrap(callback));
+}
+
+NO_SANITIZE("cfi-icall")
+CEF_GLOBAL void CefExecuteJavaScriptWithUserGestureForTests(
+    CefRefPtr<CefFrame> frame,
+    const CefString& javascript) {
+  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+  // Verify param: frame; type: refptr_same
+  DCHECK(frame.get());
+  if (!frame.get())
+    return;
+  // Unverified params: javascript
+
+  // Execute
+  cef_execute_java_script_with_user_gesture_for_tests(
+      CefFrameCToCpp::Unwrap(frame), javascript.GetStruct());
+}
diff --git a/src/libcef_dll/wrapper/libcef_dll_wrapper2.cc b/src/libcef_dll/wrapper/libcef_dll_wrapper2.cc
new file mode 100644
index 0000000..bdeef8a
--- /dev/null
+++ b/src/libcef_dll/wrapper/libcef_dll_wrapper2.cc
@@ -0,0 +1,3 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
diff --git a/src/libcef_dll/wrapper_types.h b/src/libcef_dll/wrapper_types.h
new file mode 100644
index 0000000..980b90d
--- /dev/null
+++ b/src/libcef_dll/wrapper_types.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=6e7cec57ce98a10f0b0668d47c9935639963c215$
+//
+
+#ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_
+#define CEF_LIBCEF_DLL_WRAPPER_TYPES_H_
+#pragma once
+
+enum CefWrapperType {
+  WT_BASE_REF_COUNTED = 1,
+  WT_BASE_SCOPED,
+  WT_ACCESSIBILITY_HANDLER,
+  WT_APP,
+  WT_AUDIO_HANDLER,
+  WT_AUTH_CALLBACK,
+  WT_BEFORE_DOWNLOAD_CALLBACK,
+  WT_BINARY_VALUE,
+  WT_BOX_LAYOUT,
+  WT_BROWSER,
+  WT_BROWSER_HOST,
+  WT_BROWSER_PROCESS_HANDLER,
+  WT_BROWSER_VIEW,
+  WT_BROWSER_VIEW_DELEGATE,
+  WT_BUTTON,
+  WT_BUTTON_DELEGATE,
+  WT_CALLBACK,
+  WT_CLIENT,
+  WT_COMMAND_LINE,
+  WT_COMPLETION_CALLBACK,
+  WT_CONTEXT_MENU_HANDLER,
+  WT_CONTEXT_MENU_PARAMS,
+  WT_COOKIE_ACCESS_FILTER,
+  WT_COOKIE_MANAGER,
+  WT_COOKIE_VISITOR,
+  WT_DOMDOCUMENT,
+  WT_DOMNODE,
+  WT_DOMVISITOR,
+  WT_DELETE_COOKIES_CALLBACK,
+  WT_DEV_TOOLS_MESSAGE_OBSERVER,
+  WT_DIALOG_HANDLER,
+  WT_DICTIONARY_VALUE,
+  WT_DISPLAY,
+  WT_DISPLAY_HANDLER,
+  WT_DOWNLOAD_HANDLER,
+  WT_DOWNLOAD_IMAGE_CALLBACK,
+  WT_DOWNLOAD_ITEM,
+  WT_DOWNLOAD_ITEM_CALLBACK,
+  WT_DRAG_DATA,
+  WT_DRAG_HANDLER,
+  WT_END_TRACING_CALLBACK,
+  WT_EXTENSION,
+  WT_EXTENSION_HANDLER,
+  WT_FILE_DIALOG_CALLBACK,
+  WT_FILL_LAYOUT,
+  WT_FIND_HANDLER,
+  WT_FOCUS_HANDLER,
+  WT_FRAME,
+  WT_GET_EXTENSION_RESOURCE_CALLBACK,
+  WT_IMAGE,
+  WT_JSDIALOG_CALLBACK,
+  WT_JSDIALOG_HANDLER,
+  WT_KEYBOARD_HANDLER,
+  WT_LABEL_BUTTON,
+  WT_LAYOUT,
+  WT_LIFE_SPAN_HANDLER,
+  WT_LIST_VALUE,
+  WT_LOAD_HANDLER,
+  WT_MEDIA_OBSERVER,
+  WT_MEDIA_ROUTE,
+  WT_MEDIA_ROUTE_CREATE_CALLBACK,
+  WT_MEDIA_ROUTER,
+  WT_MEDIA_SINK,
+  WT_MEDIA_SINK_DEVICE_INFO_CALLBACK,
+  WT_MEDIA_SOURCE,
+  WT_MENU_BUTTON,
+  WT_MENU_BUTTON_DELEGATE,
+  WT_MENU_BUTTON_PRESSED_LOCK,
+  WT_MENU_MODEL,
+  WT_MENU_MODEL_DELEGATE,
+  WT_NAVIGATION_ENTRY,
+  WT_NAVIGATION_ENTRY_VISITOR,
+  WT_PANEL,
+  WT_PANEL_DELEGATE,
+  WT_PDF_PRINT_CALLBACK,
+  WT_POST_DATA,
+  WT_POST_DATA_ELEMENT,
+  WT_PRINT_DIALOG_CALLBACK,
+  WT_PRINT_HANDLER,
+  WT_PRINT_JOB_CALLBACK,
+  WT_PRINT_SETTINGS,
+  WT_PROCESS_MESSAGE,
+  WT_READ_HANDLER,
+  WT_REGISTER_CDM_CALLBACK,
+  WT_REGISTRATION,
+  WT_RENDER_HANDLER,
+  WT_RENDER_PROCESS_HANDLER,
+  WT_REQUEST,
+  WT_REQUEST_CALLBACK,
+  WT_REQUEST_CONTEXT,
+  WT_REQUEST_CONTEXT_HANDLER,
+  WT_REQUEST_HANDLER,
+  WT_RESOLVE_CALLBACK,
+  WT_RESOURCE_BUNDLE,
+  WT_RESOURCE_BUNDLE_HANDLER,
+  WT_RESOURCE_HANDLER,
+  WT_RESOURCE_READ_CALLBACK,
+  WT_RESOURCE_REQUEST_HANDLER,
+  WT_RESOURCE_SKIP_CALLBACK,
+  WT_RESPONSE,
+  WT_RESPONSE_FILTER,
+  WT_RUN_CONTEXT_MENU_CALLBACK,
+  WT_RUN_FILE_DIALOG_CALLBACK,
+  WT_SSLINFO,
+  WT_SSLSTATUS,
+  WT_SCHEME_HANDLER_FACTORY,
+  WT_SCHEME_REGISTRAR,
+  WT_SCROLL_VIEW,
+  WT_SELECT_CLIENT_CERTIFICATE_CALLBACK,
+  WT_SERVER,
+  WT_SERVER_HANDLER,
+  WT_SET_COOKIE_CALLBACK,
+  WT_STREAM_READER,
+  WT_STREAM_WRITER,
+  WT_STRING_VISITOR,
+  WT_TASK,
+  WT_TASK_RUNNER,
+  WT_TEXTFIELD,
+  WT_TEXTFIELD_DELEGATE,
+  WT_THREAD,
+  WT_TRANSLATOR_TEST,
+  WT_TRANSLATOR_TEST_REF_PTR_CLIENT,
+  WT_TRANSLATOR_TEST_REF_PTR_CLIENT_CHILD,
+  WT_TRANSLATOR_TEST_REF_PTR_LIBRARY,
+  WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD,
+  WT_TRANSLATOR_TEST_REF_PTR_LIBRARY_CHILD_CHILD,
+  WT_TRANSLATOR_TEST_SCOPED_CLIENT,
+  WT_TRANSLATOR_TEST_SCOPED_CLIENT_CHILD,
+  WT_TRANSLATOR_TEST_SCOPED_LIBRARY,
+  WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD,
+  WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD,
+  WT_URLREQUEST,
+  WT_URLREQUEST_CLIENT,
+  WT_V8ACCESSOR,
+  WT_V8ARRAY_BUFFER_RELEASE_CALLBACK,
+  WT_V8CONTEXT,
+  WT_V8EXCEPTION,
+  WT_V8HANDLER,
+  WT_V8INTERCEPTOR,
+  WT_V8STACK_FRAME,
+  WT_V8STACK_TRACE,
+  WT_V8VALUE,
+  WT_VALUE,
+  WT_VIEW,
+  WT_VIEW_DELEGATE,
+  WT_WAITABLE_EVENT,
+  WT_WEB_PLUGIN_INFO,
+  WT_WEB_PLUGIN_INFO_VISITOR,
+  WT_WEB_PLUGIN_UNSTABLE_CALLBACK,
+  WT_WINDOW,
+  WT_WINDOW_DELEGATE,
+  WT_WRITE_HANDLER,
+  WT_X509CERT_PRINCIPAL,
+  WT_X509CERTIFICATE,
+  WT_XML_READER,
+  WT_ZIP_READER,
+
+  WT_LAST
+};
+
+#endif  // CEF_LIBCEF_DLL_WRAPPER_TYPES_H_
diff --git a/src/patch/README.txt b/src/patch/README.txt
new file mode 100644
index 0000000..7ea3ccb
--- /dev/null
+++ b/src/patch/README.txt
@@ -0,0 +1,15 @@
+There may be instances where CEF requires changes to the source code for
+Chromium, Blink or third-party projects that are either not desired by those
+projects or that have not yet been merged into the source code versions of those
+projects used by CEF. To address this situation the CEF project adds a patch
+capability as part of cef_create_projects[.bat|sh] build step. This patch
+capability works as follows:
+
+1. The CEF developer creates one or more patch files containing all required
+   code changes and places those patch files in the "patches" subdirectory.
+2. The CEF developer adds an entry for each patch file in the "patch.cfg" file.
+3. When building CEF from source code the patch files are applied by the
+   patcher.py tool via the cef_create_projects[.bat|sh] build step
+4. When updating Chromium the patch_updater.py tool is used to update all patch
+   files. See https://bitbucket.org/chromiumembedded/cef/wiki/ChromiumUpdate.md
+   for more information about the update process.
diff --git a/src/patch/patch.cfg b/src/patch/patch.cfg
new file mode 100644
index 0000000..fbe7a1c
--- /dev/null
+++ b/src/patch/patch.cfg
@@ -0,0 +1,508 @@
+# Patch configuration file. See the README.txt file in the patch directory for
+# information on how this configuration is used.
+#
+# Each dictionary entry in the "patches" map represents a single patch file.
+# Supported key/value pairs are as follows:
+#
+# - 'name'       Required. The name of the patch file without the .patch
+#                extension that will be read from the patches subdirectory.
+# - 'path'       Optional. The repository root for the patch file. Defaults to
+#                the Chromium "src" root. All patch file contents must be
+#                relative to this repository root.
+# - 'condition'  Optional. The patch file will only be applied if an environment
+#                variable with this name exists.
+#
+# Each entry should also include a comment linking to the code review or bug
+# report that the patch relates to.
+
+patches = [
+  {
+    # Necessary for grit integration.
+    'name': 'gritsettings',
+  },
+  {
+    # Necessary for GN integration.
+    #
+    # Move chrome target locales output to a chrome/ directory to avoid
+    # conflicts with the CEF configuration.
+    #
+    # Write environment.* files with the correct SDK version on Windows.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=634788
+    #
+    # Windows: Add cc_wrapper support for sccache builds.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2432
+    'name': 'gn_config',
+  },
+  {
+    # Patches that must be applied after `gclient sync --nohooks` and before
+    # `gclient runhooks`.
+    #
+    # Support custom VS toolchain on Windows.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=623342
+    'name': 'runhooks',
+  },
+  {
+    # Support component builds (GN is_component_build=true).
+    # https://bitbucket.org/chromiumembedded/cef/issues/1617
+    #
+    # Export GetUnicodeCharacterFromXKeySym and XKeySymToDomKey
+    # to fix component builds.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2587
+    'name': 'component_build',
+  },
+  {
+    # Revert change on Windows that removes MessageLoop::os_modal_loop().
+    # https://codereview.chromium.org/1992243003
+    #
+    # Revert change that removes MessageLoopForUI constructor.
+    # https://chromium-review.googlesource.com/751322
+    #
+    # Add MessageLoop::ReleasePump to fix crashes during shutdown with multi-
+    # threaded message loop mode.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2362
+    'name': 'message_loop',
+  },
+  {
+    # Add builtin trace event categories for CEF.
+    # Required due to https://crrev.com/331266377d.
+    'name': 'trace_event',
+  },
+  {
+    # Enable popups in offscreen rendering on OS X.
+    #
+    # Allow customization of the WebView background color.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2345
+    'name': 'webkit_popups',
+  },
+  {
+    # Fix export of UnderlayOpenGLHostingWindow for 64-bit OS X builds.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1051
+    'name': 'underlay_1051',
+  },
+  {
+    # Allow specification of a parent window handle for Widget creation.
+    # https://bitbucket.org/chromiumembedded/cef/issues/180
+    #
+    # Fix multiple handling of WM_MOUSEWHEEL messages on Windows.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1481
+    #
+    # Support custom RenderWidgetHostViewOSR for BrowserPluginGuest.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1565
+    #
+    # Fix focus/activation handling and keyboard input on Windows and Linux.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1677
+    # https://bitbucket.org/chromiumembedded/cef/issues/1679
+    # https://bitbucket.org/chromiumembedded/cef/issues/1700
+    #
+    # Support creation of captionless windows with resizable borders.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1749
+    #
+    # Windows: When |params.remove_standard_frame| is true remove WS_CAPTION
+    # and WS_SYSMENU styles. Otherwise Windows 10 enforces a minimum window
+    # width of ~116 units that cannot be overridden.
+    # Linux: Allow creation of activatable menu windows.
+    # Linux: Support CefWindowDelegate::CanResize restriction by specifying
+    # min/max Widget size values.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1947
+    #
+    # Support configuration of RWHVGuest device scale factor.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2078
+    'name': 'views_widget_180_1481_1565_1677_1749',
+  },
+  {
+    # Allow specification of a custom WebContentsView.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1257
+    #
+    # Support custom RenderWidgetHostViewOSR for BrowserPluginGuest.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1565
+    #
+    # Add WebContentsObserver::OnFrameFocused.
+    'name': 'web_contents_1257_1565',
+  },
+  {
+    # Support custom RenderWidgetHostViewOSR for MimeHandlerViewGuest and
+    # expose OnGuestAttached/Detached notifications.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1565
+    # https://bitbucket.org/chromiumembedded/cef/issues/2727
+    'name': 'mime_handler_view_guest_1565_2727',
+  },
+  {
+    # Allow customization of the WebView background color.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1161
+    # https://codereview.chromium.org/228603007/
+    'name': 'prefs_content_1161',
+  },
+  {
+    # Fix drag&drop of combined text and URL data on Linux/Aura.
+    # https://codereview.chromium.org/208313009
+    'name': 'ui_dragdrop_355390',
+  },
+  {
+    # Remove NOTREACHED() that is hit when loading Flash in incognito mode.
+    # https://bitbucket.org/chromiumembedded/cef/issue/1586
+    'name': 'content_pepper_flash_1586',
+  },
+  {
+    # Fix placement of IME window on Windows.
+    # https://bitbucket.org/chromiumembedded/cef/issue/1610
+    'name': 'ime_1610',
+  },
+  {
+    # Split service_manager::Main into the separate steps required by CEF.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=654986#c17
+    #
+    # Remove DCHECK on Linux when initialized CEF from a non-main thread.
+    # https://bitbucket.org/chromiumembedded/cef/issue/1639
+    'name': 'service_manager_654986',
+  },
+  {
+    # Fix missing check for defined(ENABLE_THEMES) in
+    # renderer_preferences_util.cc on Linux.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=545103
+    'name': 'renderer_preferences_util_545103',
+  },
+  {
+    # Expose the FontFamilyCache UserData key.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1501
+    'name': 'font_family_cache_1501',
+  },
+  {
+    # Modify views::View to extend SupportsUserData.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1749
+    #
+    # Don't add TestDesktopScreenX11 dependency on Linux.
+    # Reverts ui_controls_factory_desktop_aurax11.cc changes from
+    # https://codereview.chromium.org/2327623002
+    #
+    # Add InkDropHostView::ink_drop_mode method.
+    # Reverts ink_drop_host_view.h changes from
+    # https://codereview.chromium.org/2723843002
+    #
+    # Add LabelButton::SetFontList method.
+    # Reverts label_button.[cc|h] changes from
+    # https://codereview.chromium.org/2801583002
+    #
+    # Expose callbacks for mouse/keyboard events that trigger menu switching.
+    # Add accelerator display support to Label.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2102
+    'name': 'views_1749_2102',
+  },
+  {
+    # Expose ui::Compositor via BrowserCompositorMac for OSR.
+    'name': 'browser_compositor_mac',
+  },
+  {
+    # Fix chrome Widevine build on Linux.
+    #
+    # Remove incorrect assertion on Windows/macOS by cherry-picking this commit:
+    # https://chromium.googlesource.com/chromium/src/+/ec992a43e7
+    'name': 'chrome_widevine',
+  },
+  {
+    # Support CEF changes in chrome/browser.
+    'name': 'chrome_browser',
+  },
+  {
+    # Support CEF changes in chrome/renderer.
+    'name': 'chrome_renderer',
+  },
+  {
+    # Don't initialize ExtensionSystemFactory when extensions are disabled.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2359
+    'name': 'chrome_browser_content_settings',
+  },
+  {
+    # Don't initialize ExtensionSystemFactory when extensions are disabled.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2852
+    'name': 'chrome_browser_themes',
+  },
+  {
+    # Make some methods of ProfileManager virtual.
+    #
+    # Don't create IdentityManager in RendererUpdater.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1917
+    'name': 'chrome_browser_profiles',
+  },
+  {
+    # Show the CEF Save As dialog.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2613
+    #
+    # Fix fatal error: 'components/printing/common/print.mojom.h' file not found
+    # From chrome/browser/ui/browser_commands.cc via
+    # chrome/browser/printing/print_view_manager_common.h
+    'name': 'chrome_browser_net_export',
+  },
+  {
+    # Support override of the User-Agent product component when NetworkService
+    # is enabled.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2622
+    'name': 'chrome_browser_product_override',
+  },
+  {
+    # Fix Jumbo/component build dependency issue.
+    'name': 'chrome_browser_safe_browsing',
+  },
+  {
+    # Allow CEF to share Chrome plugin loading code.
+    'name': 'chrome_plugins',
+  },
+  {
+    # Don't create databases, blob_storage or VideoDecodeStats directories when
+    # cache_path is empty.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2289
+    'name': 'storage_incognito_2289',
+  },
+  {
+    # Fix plugin placeholder blocked message.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2020/
+    'name': 'webview_plugin_2020',
+  },
+  {
+    # Support WebUI by removing dependency on non-NULL IOThread* object.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2037
+    'name': 'webui_2037',
+  },
+  {
+    # Implement breakpad/crashpad customization required by CEF.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1995
+    'name': 'crashpad_1995',
+  },
+  {
+    # Support customization of crash report pruning limits.
+    # https://bugs.chromium.org/p/crashpad/issues/detail?id=142
+    #
+    # Implement better rate-limiting/retry logic.
+    # https://bugs.chromium.org/p/crashpad/issues/detail?id=23
+    'name': 'crashpad_tp_1995',
+  },
+  {
+    # Fix white flash during browser creation.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1984
+    #
+    # Windows: Fix crash during window creation.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=761389
+    'name': 'rwh_background_color_1984',
+  },
+  {
+    # Pass is_main_frame to PluginServiceFilter::IsPluginAvailable.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2015
+    #
+    # Pass the render process id to PluginServiceFilter::IsPluginAvailable.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=626728#c15
+    #
+    # Expose RFH via NavigationHandle for retrieval in DidFinishNavigation on
+    # network error.
+    # https://groups.google.com/a/chromium.org/d/msg/chromium-dev/6iAQPx_hwh8/gaTR5f1GAQAJ
+    #
+    # Add ContentRendererClient::RenderThreadConnected to fix sync IPC issue.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=728195
+    #
+    # Add ContentRendererClient::DevToolsAgent[Attached|Detached] methods.
+    #
+    # Always return the Chrome product value for DevTools.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2300
+    #
+    # Add new ContentBrowserClient::HandleExternalProtocol variant for use with
+    # the NetworkService.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2622
+    'name': 'content_2015',
+  },
+  {
+    # Pass is_main_frame to PluginServiceFilter::IsPluginAvailable.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2015
+    #
+    # Add ContentRendererClient::DevToolsAgent[Attached|Detached] methods.
+    'name': 'webkit_plugin_info_2015',
+  },
+  {
+    # Linux: Attach routing IDs to PrintingContext.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2196
+    'name': 'printing_context_2196',
+  },
+  {
+    # Linux: Fix 32-bit build fails with ld.gold: internal error in
+    # get_section_contents, at icf.cc:467
+    # https://bitbucket.org/chromiumembedded/cef/issues/2256
+    # Win: remove llvmlibthin as the combine_libs.py can't handle those
+    # https://bitbucket.org/chromiumembedded/cef/issues/2470
+    'name': 'build',
+  },
+  {
+    # Changes necessary to support for chrome extensions. Add a new
+    # ExtensionHost constructor that allows CEF to create the WebContents.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1947
+    #
+    # Don't initialize PrerenderContents object in StreamsPrivateAPI.
+    'name': 'extensions_1947',
+  },
+  {
+    # Don't enable sandbox::MITIGATION_STRICT_HANDLE_CHECKS in WinRT apps.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2274
+    'name': 'win_rt_2274',
+  },
+  {
+    # Fix DCHECK running OSRTest.DragDropUpdateCursor.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=781966
+    'name': 'webkit_pointer_event_781966',
+  },
+  {
+    # macOS: Fix undesirable switch to discrete GPU during startup.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2398
+    #
+    # macOS: Rely on symlinks to find the Libraries directory.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=757974#c23
+    'name': 'mac_gpu',
+  },
+  {
+    # macOS: Make the NativeEventProcessor protocol dependency optional.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2539
+    'name': 'mac_event_observer_2539',
+  },
+  {
+    # macOS: Fix crash when scrolling in OSR mode.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2540
+    'name': 'mac_fling_scheduler_2540',
+  },
+  {
+    # Linux: Use poll instead of select to fix crash during startup.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2466
+    'name': 'linux_poll_2466',
+  },
+  {
+    # Allow ResourceBundle creation/destruction on the main thread and usage on
+    # the UI thread.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2398
+    'name': 'resource_bundle_2512',
+  },
+  {
+    # macOS: Fix crash when showing a select popup with CefDoMessageLoopWork.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2495
+    'name': 'message_pump_mac_2495',
+  },
+  {
+    # Linux: Load binaries from DIR_ASSETS.
+    # https://bitbucket.org/chromiumembedded/cef/issues/1936
+    'name': 'linux_assets_path_1936',
+  },
+  {
+    # Enhancements to NetworkService:
+    # - Add support for calling CookieMonster::SetCookieableSchemes.
+    # - Fix cache directory structure ("C:\temp\cache\cache\Cache" should be
+    #   "C:\temp\cache\Cache").
+    # https://bitbucket.org/chromiumembedded/cef/issues/2622
+    'name': 'services_network_2622',
+  },
+  {
+    # Enhancements to NetworkService:
+    # - Remove the non-nullptr WebContents requirement from
+    #   NetworkServiceClient::OnAuthRequired.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2718
+    #
+    # Fix shutdown crash in InitNetworkContext with multi-threaded message loop.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2703
+    #
+    # Compute correct default quota when cache_path is unspecified.
+    'name': 'services_network_2718',
+  },
+  {
+    # Windows: Remove the base::Value is_standard_layout assert which will fail
+    # for the cef_sandbox build, and which is no longer required.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=646113#c173
+    # https://chromium.googlesource.com/chromium/src/+/2f28731c17
+    'name': 'win_base_msvc_sandbox',
+  },
+  {
+    # Restore WebView::SetResizeBackgroundColor() that was removed.
+    # http://crrev.com/3955c9f9eb
+    'name': 'set_resize_background_color',
+  },
+  {
+    # Restore WebUrlLoader Cancel method.
+    # https://chromium-review.googlesource.com/c/chromium/src/+/1617042
+    'name': 'web_url_loader_cancel_1617042',
+  },
+  {
+    # Avoid a shutdown crash with multi-threaded message loop caused by
+    # |g_browser_task_executor->browser_ui_thread_scheduler_| being null when
+    # BrowserTaskExecutor::Shutdown is called via CefContext::FinalizeShutdown.
+    # This crash was introduced by https://crrev.com/5f6212babf.
+    'name': 'browser_scheduler',
+  },
+  {
+    # Avoid a shutdown crash caused by PrefWatcher holding a reference to
+    # |g_browser_process->local_state()|, and the local_state being deleted
+    # before the PrefWatcher object (which is associated with a Profile).
+    # PrefWatcher::Shutdown will now be called from ChromeBrowserProcessStub::
+    # Shutdown for all Profiles before local_state deletion.
+    # This crash was introduced by https://crrev.com/7d032b378c.
+    'name': 'chrome_pref_watcher',
+  },
+  {
+    # Add support for OSR rendering with Viz.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2575
+    'name': 'viz_osr_2575',
+  },
+  {
+    # Changes for print preview support:
+    # - Don't attach unnecessary Chrome-related handlers to constrained window.
+    # - Create file dialogs using the CEF code path.
+    # - Remove unsupported print preview UI options.
+    # - macOS: Fix error: no member named 'kCloudPrinterHandler' in namespace
+    #   'printing::features',
+    # https://bitbucket.org/chromiumembedded/cef/issues/123
+    'name': 'print_preview_123',
+  },
+  {
+    # Remove cef_sandbox dependency on boringssl MD5/SHA1 functions.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2743
+    'name': 'base_sandbox_2743',
+  },
+  {
+    # Fix component build error due to ContentServiceManagerMainDelegate not
+    # being exported.
+    #
+    # Fix DiscardableSharedMemoryManager crash on shutdown with multi-threaded
+    # message loop.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2798
+    'name': 'content_app_shutdown_2798',
+  },
+  {
+    # Add RenderWidgetHostImpl::SetCompositorForFlingScheduler to fix fling
+    # scrolling in OSR mode.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2745
+    'name': 'osr_fling_2745',
+  },
+  {
+    # Windows: Build targets as C++17 to avoid export of std::is_integral
+    # templates in cef_sandbox that should be inlined.
+    # https://bitbucket.org/chromiumembedded/cef/issues/2819
+    'name': 'win_cpp17_msvc_sandbox_2819',
+  },
+  {
+    # libxml access is now limited to targets audited by the Security Team.
+    # https://chromium-review.googlesource.com/c/chromium/src/+/1884750
+    'name': 'libxml_visibility',
+  },
+  {
+    # Fix ScreenlockMonitorDeviceSource window creation error.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=1058556
+    'name': 'win_screenlock_1058556',
+  },
+  {
+    # Fix unbound AssociatedRemote error in SetBackgroundOpaque.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=1070713
+    'name': 'renderer_host_1070713',
+  },
+  {
+    # Allow the loading of non-standard non-local WebSafe custom schemes in
+    # iframes.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=1081397#c9
+    'name': 'browser_security_policy_1081397',
+  },
+  {
+    # Linux: Fix undefined symbol: ui::GtkUiDelegate::SetInstance in
+    # ChromeBrowserMainExtraPartsViewsLinux::ToolkitInitialized.
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=1085806
+    'name': 'linux_chrome_views_1085806',
+  }
+]
diff --git a/src/patch/patches/base_sandbox_2743.patch b/src/patch/patches/base_sandbox_2743.patch
new file mode 100644
index 0000000..1c39edf
--- /dev/null
+++ b/src/patch/patches/base_sandbox_2743.patch
@@ -0,0 +1,40 @@
+diff --git base/BUILD.gn base/BUILD.gn
+index 8df68ae89306..aa23704044be 100644
+--- base/BUILD.gn
++++ base/BUILD.gn
+@@ -33,6 +33,7 @@ import("//build/config/sysroot.gni")
+ import("//build/config/ui.gni")
+ import("//build/nocompile.gni")
+ import("//build/timestamp.gni")
++import("//cef/libcef/features/features.gni")
+ import("//testing/libfuzzer/fuzzer_test.gni")
+ import("//testing/test.gni")
+ import("//third_party/icu/config.gni")
+@@ -1656,7 +1657,11 @@ jumbo_component("base") {
+     "hash/md5_constexpr_internal.h",
+     "hash/sha1.h",
+   ]
+-  if (is_nacl) {
++  deps += [ "//cef/libcef/features" ]
++  if (enable_cef) {
++    configs += [ "//cef/libcef/features:config" ]
++  }
++  if (is_nacl || is_cef_sandbox_build) {
+     sources += [
+       "hash/md5_nacl.cc",
+       "hash/md5_nacl.h",
+diff --git base/hash/md5.h base/hash/md5.h
+index 8a49f08dcb04..2f03d7a6d1b3 100644
+--- base/hash/md5.h
++++ base/hash/md5.h
+@@ -10,8 +10,9 @@
+ #include "base/base_export.h"
+ #include "base/strings/string_piece.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ 
+-#if defined(OS_NACL)
++#if defined(OS_NACL) || BUILDFLAG(IS_CEF_SANDBOX_BUILD)
+ #include "base/hash/md5_nacl.h"
+ #else
+ #include "base/hash/md5_boringssl.h"
diff --git a/src/patch/patches/browser_compositor_mac.patch b/src/patch/patches/browser_compositor_mac.patch
new file mode 100644
index 0000000..6f48449
--- /dev/null
+++ b/src/patch/patches/browser_compositor_mac.patch
@@ -0,0 +1,30 @@
+diff --git content/browser/renderer_host/browser_compositor_view_mac.h content/browser/renderer_host/browser_compositor_view_mac.h
+index af7b3249d421..1483a48f9074 100644
+--- content/browser/renderer_host/browser_compositor_view_mac.h
++++ content/browser/renderer_host/browser_compositor_view_mac.h
+@@ -58,6 +58,8 @@ class CONTENT_EXPORT BrowserCompositorMac : public DelegatedFrameHostClient,
+ 
+   // These will not return nullptr until Destroy is called.
+   DelegatedFrameHost* GetDelegatedFrameHost();
++  ui::Layer* GetRootLayer() { return root_layer_.get(); }
++  ui::Compositor* GetCompositor();
+ 
+   // Force a new surface id to be allocated. Returns true if the
+   // RenderWidgetHostImpl sent the resulting surface id to the renderer.
+diff --git content/browser/renderer_host/browser_compositor_view_mac.mm content/browser/renderer_host/browser_compositor_view_mac.mm
+index ac9fdebbc87d..ebb2556ad908 100644
+--- content/browser/renderer_host/browser_compositor_view_mac.mm
++++ content/browser/renderer_host/browser_compositor_view_mac.mm
+@@ -85,6 +85,12 @@ DelegatedFrameHost* BrowserCompositorMac::GetDelegatedFrameHost() {
+   return delegated_frame_host_.get();
+ }
+ 
++ui::Compositor* BrowserCompositorMac::GetCompositor() {
++  if (recyclable_compositor_)
++    return recyclable_compositor_->compositor();
++  return nullptr;
++}
++
+ bool BrowserCompositorMac::ForceNewSurfaceId() {
+   dfh_local_surface_id_allocator_.GenerateId();
+   delegated_frame_host_->EmbedSurface(
diff --git a/src/patch/patches/browser_scheduler.patch b/src/patch/patches/browser_scheduler.patch
new file mode 100644
index 0000000..0f719d2
--- /dev/null
+++ b/src/patch/patches/browser_scheduler.patch
@@ -0,0 +1,13 @@
+diff --git content/browser/scheduler/browser_task_executor.cc content/browser/scheduler/browser_task_executor.cc
+index 332ef34e24dc..ee866ca5f3b6 100644
+--- content/browser/scheduler/browser_task_executor.cc
++++ content/browser/scheduler/browser_task_executor.cc
+@@ -244,7 +244,7 @@ void BrowserTaskExecutor::PostFeatureListSetup() {
+ 
+ // static
+ void BrowserTaskExecutor::Shutdown() {
+-  if (!g_browser_task_executor)
++  if (!g_browser_task_executor || !g_browser_task_executor->ui_thread_executor_)
+     return;
+ 
+   DCHECK(g_browser_task_executor->ui_thread_executor_);
diff --git a/src/patch/patches/browser_security_policy_1081397.patch b/src/patch/patches/browser_security_policy_1081397.patch
new file mode 100644
index 0000000..7e00f34
--- /dev/null
+++ b/src/patch/patches/browser_security_policy_1081397.patch
@@ -0,0 +1,21 @@
+diff --git content/browser/child_process_security_policy_impl.cc content/browser/child_process_security_policy_impl.cc
+index bcd42e788019..6a7a164a527a 100644
+--- content/browser/child_process_security_policy_impl.cc
++++ content/browser/child_process_security_policy_impl.cc
+@@ -1469,6 +1469,16 @@ bool ChildProcessSecurityPolicyImpl::CanAccessDataForOrigin(
+         // DeclarativeApiTest.PersistRules.
+         if (actual_process_lock.SchemeIs(url::kDataScheme))
+           return true;
++
++        // Allow other schemes that are non-standard, non-local and WebSafe.
++        if (actual_process_lock.is_valid() &&
++            !actual_process_lock.IsStandard() &&
++            !base::Contains(url::GetLocalSchemes(),
++                            actual_process_lock.scheme_piece()) &&
++            base::Contains(schemes_okay_to_request_in_any_process_,
++                           actual_process_lock.scheme_piece())) {
++          return true;
++        }
+       }
+ 
+       failure_reason = "lock_mismatch";
diff --git a/src/patch/patches/build.patch b/src/patch/patches/build.patch
new file mode 100644
index 0000000..65adb12
--- /dev/null
+++ b/src/patch/patches/build.patch
@@ -0,0 +1,22 @@
+diff --git build/config/compiler/BUILD.gn build/config/compiler/BUILD.gn
+index 4c5e6779efda..a75448436eab 100644
+--- build/config/compiler/BUILD.gn
++++ build/config/compiler/BUILD.gn
+@@ -172,7 +172,7 @@ declare_args() {
+             !use_clang_coverage && !(is_android && use_order_profiling) &&
+             (use_lld ||
+              (use_gold &&
+-              ((!is_android && linux_use_bundled_binutils) || is_chromeos ||
++              ((!is_android && linux_use_bundled_binutils && current_cpu != "x86") || is_chromeos ||
+                !(current_cpu == "x86" || current_cpu == "x64"))))
+ }
+ 
+@@ -1755,8 +1755,6 @@ config("thin_archive") {
+   # archive names to 16 characters, which is not what we want).
+   if ((is_posix && !is_nacl && !is_mac && !is_ios) || is_fuchsia) {
+     arflags = [ "-T" ]
+-  } else if (is_win && use_lld) {
+-    arflags = [ "/llvmlibthin" ]
+   }
+ }
+ 
diff --git a/src/patch/patches/chrome_browser.patch b/src/patch/patches/chrome_browser.patch
new file mode 100644
index 0000000..0353bb7
--- /dev/null
+++ b/src/patch/patches/chrome_browser.patch
@@ -0,0 +1,31 @@
+diff --git chrome/browser/BUILD.gn chrome/browser/BUILD.gn
+index b3f8ac152722..89a49bdd1e8a 100644
+--- chrome/browser/BUILD.gn
++++ chrome/browser/BUILD.gn
+@@ -11,6 +11,7 @@ import("//build/config/features.gni")
+ import("//build/config/jumbo.gni")
+ import("//build/config/linux/gtk/gtk.gni")
+ import("//build/config/ui.gni")
++import("//cef/libcef/features/features.gni")
+ import("//chrome/browser/buildflags.gni")
+ import("//chrome/browser/downgrade/buildflags.gni")
+ import("//chrome/common/features.gni")
+@@ -1988,6 +1989,7 @@ jumbo_static_library("browser") {
+     "//base/util/values:values_util",
+     "//build:branding_buildflags",
+     "//cc",
++    "//cef/libcef/features",
+     "//chrome:extra_resources",
+     "//chrome:resources",
+     "//chrome:strings",
+@@ -2314,6 +2316,10 @@ jumbo_static_library("browser") {
+     ]
+   }
+ 
++  if (enable_cef) {
++    configs += [ "//cef/libcef/features:config" ]
++  }
++
+   if (is_android) {
+     sources += [
+       "after_startup_task_utils_android.cc",
diff --git a/src/patch/patches/chrome_browser_content_settings.patch b/src/patch/patches/chrome_browser_content_settings.patch
new file mode 100644
index 0000000..be25854
--- /dev/null
+++ b/src/patch/patches/chrome_browser_content_settings.patch
@@ -0,0 +1,55 @@
+diff --git chrome/browser/content_settings/host_content_settings_map_factory.cc chrome/browser/content_settings/host_content_settings_map_factory.cc
+index 937b56a2e261..9e3877b06c59 100644
+--- chrome/browser/content_settings/host_content_settings_map_factory.cc
++++ chrome/browser/content_settings/host_content_settings_map_factory.cc
+@@ -8,6 +8,7 @@
+ 
+ #include "base/feature_list.h"
+ #include "build/buildflag.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/profiles/off_the_record_profile_impl.h"
+ #include "chrome/browser/profiles/profile.h"
+ #include "chrome/browser/search_engines/template_url_service_factory.h"
+@@ -19,6 +20,10 @@
+ #include "content/public/browser/browser_thread.h"
+ #include "extensions/buildflags/buildflags.h"
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/common/extensions/extensions_util.h"
++#endif
++
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
+ #include "chrome/browser/extensions/extension_service.h"
+ #include "extensions/browser/extension_system.h"
+@@ -47,8 +52,14 @@ HostContentSettingsMapFactory::HostContentSettingsMapFactory()
+   DependsOn(SupervisedUserSettingsServiceFactory::GetInstance());
+ #endif
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
++#if BUILDFLAG(ENABLE_CEF)
++  if (extensions::ExtensionsEnabled()) {
++#endif
+   DependsOn(
+       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
++#if BUILDFLAG(ENABLE_CEF)
++  }
++#endif
+ #endif
+ }
+ 
+@@ -89,10 +100,16 @@ scoped_refptr<RefcountedKeyedService>
+           permissions::features::kPermissionDelegation)));
+ 
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
++#if BUILDFLAG(ENABLE_CEF)
++  if (extensions::ExtensionsEnabled()) {
++#endif
+   // These must be registered before before the HostSettings are passed over to
+   // the IOThread.  Simplest to do this on construction.
+   extensions::ExtensionService::RegisterContentSettings(settings_map.get(),
+                                                         profile);
++#if BUILDFLAG(ENABLE_CEF)
++  }
++#endif
+ #endif // BUILDFLAG(ENABLE_EXTENSIONS)
+ #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+   SupervisedUserSettingsService* supervised_service =
diff --git a/src/patch/patches/chrome_browser_net_export.patch b/src/patch/patches/chrome_browser_net_export.patch
new file mode 100644
index 0000000..b62d3d9
--- /dev/null
+++ b/src/patch/patches/chrome_browser_net_export.patch
@@ -0,0 +1,253 @@
+diff --git chrome/browser/ui/BUILD.gn chrome/browser/ui/BUILD.gn
+index dfffff81bcf2..a4518ba20aaf 100644
+--- chrome/browser/ui/BUILD.gn
++++ chrome/browser/ui/BUILD.gn
+@@ -10,6 +10,7 @@ import("//build/config/features.gni")
+ import("//build/config/jumbo.gni")
+ import("//build/config/linux/gtk/gtk.gni")
+ import("//build/config/ui.gni")
++import("//cef/libcef/features/features.gni")
+ import("//chrome/browser/buildflags.gni")
+ import("//chrome/common/features.gni")
+ import("//chromeos/assistant/assistant.gni")
+@@ -362,6 +363,10 @@ jumbo_static_library("ui") {
+     "//build/config/compiler:wexit_time_destructors",
+   ]
+ 
++  if (enable_cef) {
++    configs += [ "//cef/libcef/features:config" ]
++  }
++
+   # Since browser and browser_ui actually depend on each other,
+   # we must omit the dependency from browser_ui to browser.
+   # However, this means browser_ui and browser should more or less
+@@ -383,6 +388,7 @@ jumbo_static_library("ui") {
+     "//base/allocator:buildflags",
+     "//build:branding_buildflags",
+     "//cc/paint",
++    "//cef/libcef/features",
+     "//chrome:extra_resources",
+     "//chrome:resources",
+     "//chrome:strings",
+@@ -1470,6 +1476,7 @@ jumbo_static_library("ui") {
+       "//components/keep_alive_registry",
+       "//components/network_session_configurator/common",
+       "//components/page_load_metrics/browser",
++      "//components/printing/common:mojo_interfaces",
+       "//components/profile_metrics",
+       "//components/search_provider_logos",
+       "//components/ui_metrics",
+diff --git chrome/browser/ui/webui/net_export_ui.cc chrome/browser/ui/webui/net_export_ui.cc
+index 93bfc84f49a1..eeee229e943f 100644
+--- chrome/browser/ui/webui/net_export_ui.cc
++++ chrome/browser/ui/webui/net_export_ui.cc
+@@ -21,13 +21,12 @@
+ #include "base/strings/string_util.h"
+ #include "base/strings/utf_string_conversions.h"
+ #include "base/values.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/browser_process.h"
+-#include "chrome/browser/download/download_prefs.h"
+ #include "chrome/browser/net/net_export_helper.h"
+ #include "chrome/browser/net/system_network_context_manager.h"
+ #include "chrome/browser/platform_util.h"
+ #include "chrome/browser/profiles/profile.h"
+-#include "chrome/browser/ui/chrome_select_file_policy.h"
+ #include "chrome/common/channel_info.h"
+ #include "chrome/common/url_constants.h"
+ #include "components/grit/dev_ui_components_resources.h"
+@@ -42,7 +41,14 @@
+ #include "content/public/browser/web_ui_message_handler.h"
+ #include "extensions/buildflags/buildflags.h"
+ #include "net/log/net_log_capture_mode.h"
++
++#if !BUILDFLAG(ENABLE_CEF)
++#include "chrome/browser/download/download_prefs.h"
++#include "chrome/browser/ui/chrome_select_file_policy.h"
+ #include "ui/shell_dialogs/select_file_dialog.h"
++#else
++#include "cef/libcef/browser/browser_host_impl.h"
++#endif
+ 
+ #if defined(OS_ANDROID)
+ #include "chrome/browser/android/intent_helper.h"
+@@ -68,6 +74,7 @@ content::WebUIDataSource* CreateNetExportHTMLSource() {
+   return source;
+ }
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+ void SetIfNotNull(base::DictionaryValue* dict,
+                   const base::StringPiece& path,
+                   std::unique_ptr<base::Value> in_value) {
+@@ -75,6 +82,7 @@ void SetIfNotNull(base::DictionaryValue* dict,
+     dict->Set(path, std::move(in_value));
+   }
+ }
++#endif  // !BUILDFLAG(ENABLE_CEF)
+ 
+ // This class receives javascript messages from the renderer.
+ // Note that the WebUI infrastructure runs on the UI thread, therefore all of
+@@ -82,7 +90,9 @@ void SetIfNotNull(base::DictionaryValue* dict,
+ class NetExportMessageHandler
+     : public WebUIMessageHandler,
+       public base::SupportsWeakPtr<NetExportMessageHandler>,
++#if !BUILDFLAG(ENABLE_CEF)
+       public ui::SelectFileDialog::Listener,
++#endif
+       public net_log::NetExportFileWriter::StateObserver {
+  public:
+   NetExportMessageHandler();
+@@ -98,11 +108,13 @@ class NetExportMessageHandler
+   void OnSendNetLog(const base::ListValue* list);
+   void OnShowFile(const base::ListValue* list);
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   // ui::SelectFileDialog::Listener implementation.
+   void FileSelected(const base::FilePath& path,
+                     int index,
+                     void* params) override;
+   void FileSelectionCanceled(void* params) override;
++#endif  // !BUILDFLAG(ENABLE_CEF)
+ 
+   // net_log::NetExportFileWriter::StateObserver implementation.
+   void OnNewState(const base::DictionaryValue& state) override;
+@@ -132,9 +144,16 @@ class NetExportMessageHandler
+   // renderer.
+   void NotifyUIWithState(std::unique_ptr<base::DictionaryValue> state);
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   // Opens the SelectFileDialog UI with the default path to save a
+   // NetLog file.
+   void ShowSelectFileDialog(const base::FilePath& default_path);
++#else
++  void ShowCefSaveAsDialog(content::WebContents* web_contents);
++  void SaveAsDialogDismissed(
++      int selected_accept_filter,
++      const std::vector<base::FilePath>& file_paths);
++#endif
+ 
+   // Cached pointer to SystemNetworkContextManager's NetExportFileWriter.
+   net_log::NetExportFileWriter* file_writer_;
+@@ -150,7 +169,9 @@ class NetExportMessageHandler
+   net::NetLogCaptureMode capture_mode_;
+   uint64_t max_log_file_size_;
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
++#endif
+ 
+   base::WeakPtrFactory<NetExportMessageHandler> weak_ptr_factory_{this};
+ 
+@@ -167,8 +188,10 @@ NetExportMessageHandler::NetExportMessageHandler()
+ NetExportMessageHandler::~NetExportMessageHandler() {
+   // There may be a pending file dialog, it needs to be told that the user
+   // has gone away so that it doesn't try to call back.
++#if !BUILDFLAG(ENABLE_CEF)
+   if (select_file_dialog_)
+     select_file_dialog_->ListenerDestroyed();
++#endif
+ 
+   file_writer_->StopNetLog(nullptr);
+ }
+@@ -231,6 +254,7 @@ void NetExportMessageHandler::OnStartNetLog(const base::ListValue* list) {
+   if (UsingMobileUI()) {
+     StartNetLog(base::FilePath());
+   } else {
++#if !BUILDFLAG(ENABLE_CEF)
+     base::FilePath initial_dir = last_save_dir.Pointer()->empty() ?
+         DownloadPrefs::FromBrowserContext(
+             web_ui()->GetWebContents()->GetBrowserContext())->DownloadPath() :
+@@ -238,6 +262,9 @@ void NetExportMessageHandler::OnStartNetLog(const base::ListValue* list) {
+     base::FilePath initial_path =
+         initial_dir.Append(FILE_PATH_LITERAL("chrome-net-export-log.json"));
+     ShowSelectFileDialog(initial_path);
++#else  // BUILDFLAG(ENABLE_CEF)
++    ShowCefSaveAsDialog(web_ui()->GetWebContents());
++#endif  // BUILDFLAG(ENABLE_CEF)
+   }
+ }
+ 
+@@ -247,6 +274,7 @@ void NetExportMessageHandler::OnStopNetLog(const base::ListValue* list) {
+   std::unique_ptr<base::DictionaryValue> ui_thread_polled_data(
+       new base::DictionaryValue());
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   Profile* profile = Profile::FromWebUI(web_ui());
+   SetIfNotNull(ui_thread_polled_data.get(), "prerenderInfo",
+                chrome_browser_net::GetPrerenderInfo(profile));
+@@ -256,6 +284,7 @@ void NetExportMessageHandler::OnStopNetLog(const base::ListValue* list) {
+   SetIfNotNull(ui_thread_polled_data.get(), "serviceProviders",
+                chrome_browser_net::GetWindowsServiceProviders());
+ #endif
++#endif  // !BUILDFLAG(ENABLE_CEF)
+ 
+   file_writer_->StopNetLog(std::move(ui_thread_polled_data));
+ }
+@@ -272,6 +301,7 @@ void NetExportMessageHandler::OnShowFile(const base::ListValue* list) {
+       base::Bind(&NetExportMessageHandler::ShowFileInShell, AsWeakPtr()));
+ }
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+ void NetExportMessageHandler::FileSelected(const base::FilePath& path,
+                                            int index,
+                                            void* params) {
+@@ -290,6 +320,7 @@ void NetExportMessageHandler::FileSelectionCanceled(void* params) {
+   DCHECK(select_file_dialog_);
+   select_file_dialog_ = nullptr;
+ }
++#endif  // !BUILDFLAG(ENABLE_CEF)
+ 
+ void NetExportMessageHandler::OnNewState(const base::DictionaryValue& state) {
+   NotifyUIWithState(state.CreateDeepCopy());
+@@ -354,6 +385,7 @@ void NetExportMessageHandler::NotifyUIWithState(
+                                          *state);
+ }
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+ void NetExportMessageHandler::ShowSelectFileDialog(
+     const base::FilePath& default_path) {
+   // User may have clicked more than once before the save dialog appears.
+@@ -372,6 +404,43 @@ void NetExportMessageHandler::ShowSelectFileDialog(
+       ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(), default_path,
+       &file_type_info, 0, base::FilePath::StringType(), owning_window, nullptr);
+ }
++#endif  // !BUILDFLAG(ENABLE_CEF)
++
++#if BUILDFLAG(ENABLE_CEF)
++
++void NetExportMessageHandler::ShowCefSaveAsDialog(
++    content::WebContents* web_contents) {
++  CefRefPtr<CefBrowserHostImpl> cef_browser =
++      CefBrowserHostImpl::GetBrowserForContents(web_contents);
++  if (!cef_browser)
++    return;
++
++  base::FilePath initial_dir;
++  if (!last_save_dir.Pointer()->empty())
++    initial_dir = *last_save_dir.Pointer();
++  base::FilePath initial_path =
++      initial_dir.Append(FILE_PATH_LITERAL("chrome-net-export-log.json"));
++
++  CefFileDialogRunner::FileChooserParams params;
++  params.mode = blink::mojom::FileChooserParams::Mode::kSave;
++  params.default_file_name = initial_path;
++  params.accept_types.push_back(CefString(initial_path.Extension()));
++
++  cef_browser->RunFileChooser(
++      params, base::Bind(&NetExportMessageHandler::SaveAsDialogDismissed,
++                         weak_ptr_factory_.GetWeakPtr()));
++}
++
++void NetExportMessageHandler::SaveAsDialogDismissed(
++    int selected_accept_filter,
++    const std::vector<base::FilePath>& file_paths) {
++  if (file_paths.size() == 1) {
++    *last_save_dir.Pointer() = file_paths[0].DirName();
++    StartNetLog(file_paths[0]);
++  }
++}
++
++#endif  // BUILDFLAG(ENABLE_CEF)
+ 
+ }  // namespace
+ 
diff --git a/src/patch/patches/chrome_browser_product_override.patch b/src/patch/patches/chrome_browser_product_override.patch
new file mode 100644
index 0000000..b5e728c
--- /dev/null
+++ b/src/patch/patches/chrome_browser_product_override.patch
@@ -0,0 +1,44 @@
+diff --git chrome/browser/chrome_content_browser_client.cc chrome/browser/chrome_content_browser_client.cc
+index b5deda2a1739..cfa14167cc89 100644
+--- chrome/browser/chrome_content_browser_client.cc
++++ chrome/browser/chrome_content_browser_client.cc
+@@ -996,10 +996,6 @@ void LaunchURL(const GURL& url,
+   }
+ }
+ 
+-std::string GetProduct() {
+-  return version_info::GetProductNameAndVersionForUserAgent();
+-}
+-
+ void MaybeAppendSecureOriginsAllowlistSwitch(base::CommandLine* cmdline) {
+   // |allowlist| combines pref/policy + cmdline switch in the browser process.
+   // For renderer and utility (e.g. NetworkService) processes the switch is the
+@@ -1094,6 +1090,14 @@ void MaybeRecordSameSiteCookieEngagementHistogram(
+ 
+ }  // namespace
+ 
++std::string GetProduct() {
++  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
++  if (command_line->HasSwitch(switches::kProductVersion))
++    return command_line->GetSwitchValueASCII(switches::kProductVersion);
++
++  return version_info::GetProductNameAndVersionForUserAgent();
++}
++
+ std::string GetUserAgent() {
+   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+   if (command_line->HasSwitch(switches::kUserAgent)) {
+diff --git chrome/browser/chrome_content_browser_client.h chrome/browser/chrome_content_browser_client.h
+index ae86be8afe0f..61be54670431 100644
+--- chrome/browser/chrome_content_browser_client.h
++++ chrome/browser/chrome_content_browser_client.h
+@@ -91,7 +91,8 @@ class ChromeXrIntegrationClient;
+ }
+ #endif
+ 
+-// Returns the user agent of Chrome.
++// Returns the product and user agent of Chrome.
++std::string GetProduct();
+ std::string GetUserAgent();
+ 
+ blink::UserAgentMetadata GetUserAgentMetadata();
diff --git a/src/patch/patches/chrome_browser_profiles.patch b/src/patch/patches/chrome_browser_profiles.patch
new file mode 100644
index 0000000..f35b93e
--- /dev/null
+++ b/src/patch/patches/chrome_browser_profiles.patch
@@ -0,0 +1,69 @@
+diff --git chrome/browser/profiles/profile_manager.cc chrome/browser/profiles/profile_manager.cc
+index 7a7ba226f6dc..a5c8f844344e 100644
+--- chrome/browser/profiles/profile_manager.cc
++++ chrome/browser/profiles/profile_manager.cc
+@@ -364,7 +364,7 @@ ProfileManager::ProfileManager(const base::FilePath& user_data_dir)
+   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
+                  content::NotificationService::AllSources());
+ 
+-  if (ProfileShortcutManager::IsFeatureEnabled() && !user_data_dir_.empty())
++  if (!user_data_dir_.empty() && ProfileShortcutManager::IsFeatureEnabled())
+     profile_shortcut_manager_ = ProfileShortcutManager::Create(this);
+ }
+ 
+diff --git chrome/browser/profiles/profile_manager.h chrome/browser/profiles/profile_manager.h
+index 3e1e28ea7d0e..b70f718c8e94 100644
+--- chrome/browser/profiles/profile_manager.h
++++ chrome/browser/profiles/profile_manager.h
+@@ -101,7 +101,7 @@ class ProfileManager : public content::NotificationObserver,
+   // acceptable. Returns null if creation of the new profile fails.
+   // TODO(bauerb): Migrate calls from other code to GetProfileByPath(), then
+   // make this method private.
+-  Profile* GetProfile(const base::FilePath& profile_dir);
++  virtual Profile* GetProfile(const base::FilePath& profile_dir);
+ 
+   // Returns total number of profiles available on this machine.
+   size_t GetNumberOfProfiles();
+@@ -131,7 +131,7 @@ class ProfileManager : public content::NotificationObserver,
+ 
+   // Returns true if the profile pointer is known to point to an existing
+   // profile.
+-  bool IsValidProfile(const void* profile);
++  virtual bool IsValidProfile(const void* profile);
+ 
+   // Returns the directory where the first created profile is stored,
+   // relative to the user data directory currently in use.
+@@ -140,7 +140,7 @@ class ProfileManager : public content::NotificationObserver,
+   // Get the Profile last used (the Profile to which owns the most recently
+   // focused window) with this Chrome build. If no signed profile has been
+   // stored in Local State, hand back the Default profile.
+-  Profile* GetLastUsedProfile(const base::FilePath& user_data_dir);
++  virtual Profile* GetLastUsedProfile(const base::FilePath& user_data_dir);
+ 
+   // Get the path of the last used profile, or if that's undefined, the default
+   // profile.
+diff --git chrome/browser/profiles/renderer_updater.cc chrome/browser/profiles/renderer_updater.cc
+index c1792eccd997..6c34846b7947 100644
+--- chrome/browser/profiles/renderer_updater.cc
++++ chrome/browser/profiles/renderer_updater.cc
+@@ -7,6 +7,7 @@
+ #include <utility>
+ 
+ #include "base/bind.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+ #include "chrome/browser/profiles/profile.h"
+ #include "chrome/browser/signin/identity_manager_factory.h"
+@@ -62,8 +63,12 @@ void GetGuestViewDefaultContentSettingRules(
+ 
+ RendererUpdater::RendererUpdater(Profile* profile)
+     : profile_(profile), identity_manager_observer_(this) {
++#if !BUILDFLAG(ENABLE_CEF)
+   identity_manager_ = IdentityManagerFactory::GetForProfile(profile);
+   identity_manager_observer_.Add(identity_manager_);
++#else
++  identity_manager_ = nullptr;
++#endif
+ #if defined(OS_CHROMEOS)
+   oauth2_login_manager_ =
+       chromeos::OAuth2LoginManagerFactory::GetForProfile(profile_);
diff --git a/src/patch/patches/chrome_browser_safe_browsing.patch b/src/patch/patches/chrome_browser_safe_browsing.patch
new file mode 100644
index 0000000..d8734e0
--- /dev/null
+++ b/src/patch/patches/chrome_browser_safe_browsing.patch
@@ -0,0 +1,12 @@
+diff --git chrome/browser/safe_browsing/BUILD.gn chrome/browser/safe_browsing/BUILD.gn
+index 14bef91c5370..cb1faba7c23e 100644
+--- chrome/browser/safe_browsing/BUILD.gn
++++ chrome/browser/safe_browsing/BUILD.gn
+@@ -246,6 +246,7 @@ jumbo_static_library("safe_browsing") {
+         "//chrome/common/safe_browsing:download_type_util",
+         "//chrome/services/file_util/public/cpp",
+         "//components/content_settings/core/browser",
++        "//components/gcm_driver:gcm_buildflags",
+         "//components/language/core/common",
+         "//components/prefs",
+         "//components/safe_browsing/core:webprotect_proto",
diff --git a/src/patch/patches/chrome_browser_themes.patch b/src/patch/patches/chrome_browser_themes.patch
new file mode 100644
index 0000000..ffb61d5
--- /dev/null
+++ b/src/patch/patches/chrome_browser_themes.patch
@@ -0,0 +1,37 @@
+diff --git chrome/browser/themes/theme_service_factory.cc chrome/browser/themes/theme_service_factory.cc
+index 5a0d5e736cb9..f1c9da3f73b1 100644
+--- chrome/browser/themes/theme_service_factory.cc
++++ chrome/browser/themes/theme_service_factory.cc
+@@ -7,6 +7,7 @@
+ #include "base/logging.h"
+ #include "base/no_destructor.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/extensions/extension_system_factory.h"
+ #include "chrome/browser/profiles/incognito_helpers.h"
+ #include "chrome/browser/profiles/profile.h"
+@@ -42,6 +43,10 @@ const ThemeHelper& GetThemeHelper() {
+ 
+ }  // namespace
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/common/extensions/extensions_util.h"
++#endif
++
+ // static
+ ThemeService* ThemeServiceFactory::GetForProfile(Profile* profile) {
+   return static_cast<ThemeService*>(
+@@ -71,7 +76,13 @@ ThemeServiceFactory::ThemeServiceFactory()
+           BrowserContextDependencyManager::GetInstance()) {
+   DependsOn(extensions::ExtensionRegistryFactory::GetInstance());
+   DependsOn(extensions::ExtensionPrefsFactory::GetInstance());
++#if BUILDFLAG(ENABLE_CEF)
++  if (extensions::ExtensionsEnabled()) {
++#endif
+   DependsOn(extensions::ExtensionSystemFactory::GetInstance());
++#if BUILDFLAG(ENABLE_CEF)
++  }
++#endif
+ }
+ 
+ ThemeServiceFactory::~ThemeServiceFactory() {}
diff --git a/src/patch/patches/chrome_plugins.patch b/src/patch/patches/chrome_plugins.patch
new file mode 100644
index 0000000..d930baf
--- /dev/null
+++ b/src/patch/patches/chrome_plugins.patch
@@ -0,0 +1,268 @@
+diff --git chrome/browser/plugins/plugin_info_host_impl.cc chrome/browser/plugins/plugin_info_host_impl.cc
+index d49a1df73622..9a389e4383f0 100644
+--- chrome/browser/plugins/plugin_info_host_impl.cc
++++ chrome/browser/plugins/plugin_info_host_impl.cc
+@@ -18,6 +18,7 @@
+ #include "base/task_runner_util.h"
+ #include "build/branding_buildflags.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/browser_process.h"
+ #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+ #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
+@@ -54,6 +55,11 @@
+ #include "url/gurl.h"
+ #include "url/origin.h"
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/browser/plugins/plugin_service_filter.h"
++#include "cef/libcef/common/extensions/extensions_util.h"
++#endif
++
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
+ #include "components/guest_view/browser/guest_view_base.h"
+ #include "extensions/browser/extension_registry.h"
+@@ -100,6 +106,9 @@ bool IsPluginLoadingAccessibleResourceInWebView(
+     extensions::ExtensionRegistry* extension_registry,
+     int process_id,
+     const GURL& resource) {
++  if (!extension_registry)
++    return false;
++
+   extensions::WebViewRendererState* renderer_state =
+       extensions::WebViewRendererState::GetInstance();
+   std::string partition_id;
+@@ -128,9 +137,6 @@ bool IsPluginLoadingAccessibleResourceInWebView(
+ 
+ PluginInfoHostImpl::Context::Context(int render_process_id, Profile* profile)
+     : render_process_id_(render_process_id),
+-#if BUILDFLAG(ENABLE_EXTENSIONS)
+-      extension_registry_(extensions::ExtensionRegistry::Get(profile)),
+-#endif
+       host_content_settings_map_(
+           HostContentSettingsMapFactory::GetForProfile(profile)),
+       plugin_prefs_(PluginPrefs::GetForProfile(profile)) {
+@@ -138,6 +144,13 @@ PluginInfoHostImpl::Context::Context(int render_process_id, Profile* profile)
+                                profile->GetPrefs());
+   run_all_flash_in_allow_mode_.Init(prefs::kRunAllFlashInAllowMode,
+                                     profile->GetPrefs());
++
++#if BUILDFLAG(ENABLE_EXTENSIONS)
++#if BUILDFLAG(ENABLE_CEF)
++  if (extensions::ExtensionsEnabled())
++#endif
++    extension_registry_ = extensions::ExtensionRegistry::Get(profile);
++#endif
+ }
+ 
+ PluginInfoHostImpl::Context::~Context() {}
+@@ -208,6 +221,7 @@ void PluginInfoHostImpl::PluginsLoaded(
+         plugin_metadata->identifier(), &output->status);
+   }
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   if (output->status == chrome::mojom::PluginStatus::kNotFound) {
+     // Check to see if the component updater can fetch an implementation.
+     std::unique_ptr<component_updater::ComponentInfo> cus_plugin_info =
+@@ -216,7 +230,9 @@ void PluginInfoHostImpl::PluginsLoaded(
+     ComponentPluginLookupDone(params, std::move(output), std::move(callback),
+                               std::move(plugin_metadata),
+                               std::move(cus_plugin_info));
+-  } else {
++  } else
++#endif  // !BUILDFLAG(ENABLE_CEF)
++  {
+     GetPluginInfoFinish(params, std::move(output), std::move(callback),
+                         std::move(plugin_metadata));
+   }
+@@ -229,6 +245,14 @@ void PluginInfoHostImpl::Context::DecidePluginStatus(
+     PluginMetadata::SecurityStatus security_status,
+     const std::string& plugin_identifier,
+     chrome::mojom::PluginStatus* status) const {
++#if BUILDFLAG(ENABLE_CEF)
++  // Don't override the user decision.
++  if (*status == chrome::mojom::PluginStatus::kBlocked ||
++      *status == chrome::mojom::PluginStatus::kDisabled) {
++    return;
++  }
++#endif
++
+   if (security_status == PluginMetadata::SECURITY_STATUS_FULLY_TRUSTED) {
+     *status = chrome::mojom::PluginStatus::kAllowed;
+     return;
+@@ -337,16 +361,35 @@ bool PluginInfoHostImpl::Context::FindEnabledPlugin(
+     return false;
+   }
+ 
++  const bool is_main_frame =
++      main_frame_origin.IsSameOriginWith(url::Origin::Create(url));
++
++#if BUILDFLAG(ENABLE_CEF)
++  CefPluginServiceFilter* filter = static_cast<CefPluginServiceFilter*>(
++      PluginService::GetInstance()->GetFilter());
++  DCHECK(filter);
++
++  size_t i = 0;
++  for (; i < matching_plugins.size(); ++i) {
++    if (filter->IsPluginAvailable(render_process_id_, render_frame_id,
++                                  url, is_main_frame, main_frame_origin,
++                                  &matching_plugins[i], status)) {
++      break;
++    }
++  }
++#else  // !BUILDFLAG(ENABLE_CEF)
+   content::PluginServiceFilter* filter =
+       PluginService::GetInstance()->GetFilter();
+   size_t i = 0;
+   for (; i < matching_plugins.size(); ++i) {
+     if (!filter ||
+         filter->IsPluginAvailable(render_process_id_, render_frame_id, url,
+-                                  main_frame_origin, &matching_plugins[i])) {
++                                  is_main_frame, main_frame_origin,
++                                  &matching_plugins[i])) {
+       break;
+     }
+   }
++#endif  // !BUILDFLAG(ENABLE_CEF)
+ 
+   // If we broke out of the loop, we have found an enabled plugin.
+   bool enabled = i < matching_plugins.size();
+diff --git chrome/browser/plugins/plugin_utils.cc chrome/browser/plugins/plugin_utils.cc
+index 4e64db143b8a..71322b0261a1 100644
+--- chrome/browser/plugins/plugin_utils.cc
++++ chrome/browser/plugins/plugin_utils.cc
+@@ -5,6 +5,7 @@
+ #include "chrome/browser/plugins/plugin_utils.h"
+ 
+ #include "base/values.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/profiles/profile_io_data.h"
+ #include "chrome/common/plugin_utils.h"
+ #include "components/content_settings/core/browser/host_content_settings_map.h"
+@@ -14,6 +15,11 @@
+ #include "url/gurl.h"
+ #include "url/origin.h"
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/browser/resource_context.h"
++#include "cef/libcef/common/extensions/extensions_util.h"
++#endif
++
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
+ #include "chrome/common/pref_names.h"
+ #include "components/prefs/pref_service.h"
+@@ -182,6 +188,12 @@ base::flat_map<std::string, std::string>
+ PluginUtils::GetMimeTypeToExtensionIdMap(
+     content::BrowserContext* browser_context) {
+   base::flat_map<std::string, std::string> mime_type_to_extension_id_map;
++
++#if BUILDFLAG(ENABLE_CEF)
++  if (!extensions::ExtensionsEnabled())
++    return mime_type_to_extension_id_map;
++#endif
++
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
+   Profile* profile = Profile::FromBrowserContext(browser_context);
+   std::vector<std::string> whitelist = MimeTypesHandler::GetMIMETypeWhitelist();
+diff --git chrome/common/google_url_loader_throttle.cc chrome/common/google_url_loader_throttle.cc
+index bcb97138c321..df6792bd5317 100644
+--- chrome/common/google_url_loader_throttle.cc
++++ chrome/common/google_url_loader_throttle.cc
+@@ -5,10 +5,15 @@
+ #include "chrome/common/google_url_loader_throttle.h"
+ 
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/common/net/safe_search_util.h"
+ #include "components/google/core/common/google_util.h"
+ #include "services/network/public/mojom/url_response_head.mojom.h"
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/common/extensions/extensions_util.h"
++#endif
++
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
+ #include "extensions/common/extension_urls.h"
+ #endif
+@@ -116,6 +121,11 @@ void GoogleURLLoaderThrottle::WillProcessResponse(
+     const GURL& response_url,
+     network::mojom::URLResponseHead* response_head,
+     bool* defer) {
++#if BUILDFLAG(ENABLE_CEF)
++  if (!extensions::ExtensionsEnabled())
++    return;
++#endif
++
+   // Built-in additional protection for the chrome web store origin.
+   GURL webstore_url(extension_urls::GetWebstoreLaunchURL());
+   if (response_url.SchemeIsHTTPOrHTTPS() &&
+diff --git chrome/renderer/chrome_content_renderer_client.cc chrome/renderer/chrome_content_renderer_client.cc
+index b96048aec4f7..edaa78c59fe8 100644
+--- chrome/renderer/chrome_content_renderer_client.cc
++++ chrome/renderer/chrome_content_renderer_client.cc
+@@ -813,6 +813,7 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin(
+ 
+     if ((status == chrome::mojom::PluginStatus::kUnauthorized ||
+          status == chrome::mojom::PluginStatus::kBlocked) &&
++        content_settings_agent &&
+         content_settings_agent->IsPluginTemporarilyAllowed(identifier)) {
+       status = chrome::mojom::PluginStatus::kAllowed;
+     }
+@@ -1015,7 +1016,8 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin(
+         render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
+             plugin_auth_host.BindNewEndpointAndPassReceiver());
+         plugin_auth_host->BlockedUnauthorizedPlugin(group_name, identifier);
+-        content_settings_agent->DidBlockContentType(content_type);
++        if (content_settings_agent)
++          content_settings_agent->DidBlockContentType(content_type);
+         break;
+       }
+       case chrome::mojom::PluginStatus::kBlocked: {
+@@ -1024,7 +1026,8 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin(
+             l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name));
+         placeholder->AllowLoading();
+         RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Blocked"));
+-        content_settings_agent->DidBlockContentType(content_type);
++        if (content_settings_agent)
++          content_settings_agent->DidBlockContentType(content_type);
+         break;
+       }
+       case chrome::mojom::PluginStatus::kBlockedByPolicy: {
+@@ -1034,7 +1037,8 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin(
+                                        group_name));
+         RenderThread::Get()->RecordAction(
+             UserMetricsAction("Plugin_BlockedByPolicy"));
+-        content_settings_agent->DidBlockContentType(content_type);
++        if (content_settings_agent)
++          content_settings_agent->DidBlockContentType(content_type);
+         break;
+       }
+       case chrome::mojom::PluginStatus::kBlockedNoLoading: {
+@@ -1042,7 +1046,8 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin(
+             IDR_BLOCKED_PLUGIN_HTML,
+             l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED_NO_LOADING,
+                                        group_name));
+-        content_settings_agent->DidBlockContentType(content_type);
++        if (content_settings_agent)
++          content_settings_agent->DidBlockContentType(content_type);
+         break;
+       }
+       case chrome::mojom::PluginStatus::kComponentUpdateRequired: {
+diff --git chrome/renderer/plugins/chrome_plugin_placeholder.cc chrome/renderer/plugins/chrome_plugin_placeholder.cc
+index 8a7d7402a48c..4dde943d0222 100644
+--- chrome/renderer/plugins/chrome_plugin_placeholder.cc
++++ chrome/renderer/plugins/chrome_plugin_placeholder.cc
+@@ -359,8 +359,11 @@ void ChromePluginPlaceholder::OnBlockedContent(
+ 
+   if (status ==
+       content::RenderFrame::PeripheralContentStatus::CONTENT_STATUS_TINY) {
+-    ContentSettingsAgentImpl::Get(render_frame())
+-        ->DidBlockContentType(ContentSettingsType::PLUGINS);
++      ContentSettingsAgentImpl *content_settings_agent =
++          ContentSettingsAgentImpl::Get(render_frame());
++      if (content_settings_agent) {
++        content_settings_agent->DidBlockContentType(ContentSettingsType::PLUGINS);
++      }
+   }
+ 
+   std::string message = base::StringPrintf(
diff --git a/src/patch/patches/chrome_pref_watcher.patch b/src/patch/patches/chrome_pref_watcher.patch
new file mode 100644
index 0000000..743c1d9
--- /dev/null
+++ b/src/patch/patches/chrome_pref_watcher.patch
@@ -0,0 +1,16 @@
+diff --git chrome/browser/ui/prefs/pref_watcher.h chrome/browser/ui/prefs/pref_watcher.h
+index a439b2ce1b32..21c7b97639f4 100644
+--- chrome/browser/ui/prefs/pref_watcher.h
++++ chrome/browser/ui/prefs/pref_watcher.h
+@@ -29,10 +29,10 @@ class PrefWatcher : public KeyedService {
+   void RegisterRendererPreferenceWatcher(
+       mojo::PendingRemote<blink::mojom::RendererPreferenceWatcher> watcher);
+ 
+- private:
+   // KeyedService overrides:
+   void Shutdown() override;
+ 
++ private:
+   void UpdateRendererPreferences();
+   void OnWebPrefChanged(const std::string& pref_name);
+   void OnLiveCaptionEnabledPrefChanged(const std::string& pref_name);
diff --git a/src/patch/patches/chrome_renderer.patch b/src/patch/patches/chrome_renderer.patch
new file mode 100644
index 0000000..ebeb2e6
--- /dev/null
+++ b/src/patch/patches/chrome_renderer.patch
@@ -0,0 +1,31 @@
+diff --git chrome/renderer/BUILD.gn chrome/renderer/BUILD.gn
+index 88cbad65b714..2d899f1805e2 100644
+--- chrome/renderer/BUILD.gn
++++ chrome/renderer/BUILD.gn
+@@ -5,6 +5,7 @@
+ import("//build/config/buildflags_paint_preview.gni")
+ import("//build/config/features.gni")
+ import("//build/config/jumbo.gni")
++import("//cef/libcef/features/features.gni")
+ import("//chrome/common/features.gni")
+ import("//components/nacl/features.gni")
+ import("//components/offline_pages/buildflags/features.gni")
+@@ -129,6 +130,7 @@ jumbo_static_library("renderer") {
+   defines = []
+ 
+   deps = [
++    "//cef/libcef/features",
+     "//chrome:resources",
+     "//chrome:strings",
+     "//chrome/common",
+@@ -193,6 +195,10 @@ jumbo_static_library("renderer") {
+ 
+   configs += [ "//build/config/compiler:wexit_time_destructors" ]
+ 
++  if (enable_cef) {
++    configs += [ "//cef/libcef/features:config" ]
++  }
++
+   if (enable_nacl) {
+     deps += [
+       "//components/nacl/loader",
diff --git a/src/patch/patches/chrome_widevine.patch b/src/patch/patches/chrome_widevine.patch
new file mode 100644
index 0000000..c99058e
--- /dev/null
+++ b/src/patch/patches/chrome_widevine.patch
@@ -0,0 +1,12 @@
+diff --git third_party/widevine/cdm/BUILD.gn third_party/widevine/cdm/BUILD.gn
+index 1919784f5f93..6a8d2d5140f2 100644
+--- third_party/widevine/cdm/BUILD.gn
++++ third_party/widevine/cdm/BUILD.gn
+@@ -5,6 +5,7 @@
+ import("//build/buildflag_header.gni")
+ import("//build/config/chrome_build.gni")
+ import("//build/config/features.gni")
++import("//cef/libcef/features/features.gni")
+ import("//media/cdm/library_cdm/cdm_paths.gni")
+ import("//media/media_options.gni")
+ import("//third_party/widevine/cdm/widevine.gni")
diff --git a/src/patch/patches/component_build.patch b/src/patch/patches/component_build.patch
new file mode 100644
index 0000000..3676198
--- /dev/null
+++ b/src/patch/patches/component_build.patch
@@ -0,0 +1,133 @@
+diff --git content/browser/devtools/devtools_instrumentation.h content/browser/devtools/devtools_instrumentation.h
+index 35726bdf4303..e0da57f2275b 100644
+--- content/browser/devtools/devtools_instrumentation.h
++++ content/browser/devtools/devtools_instrumentation.h
+@@ -12,6 +12,7 @@
+ #include <vector>
+ 
+ #include "base/optional.h"
++#include "content/common/content_export.h"
+ #include "content/common/navigation_params.mojom.h"
+ #include "content/public/browser/certificate_request_result_type.h"
+ #include "mojo/public/cpp/bindings/pending_receiver.h"
+@@ -54,7 +55,7 @@ void ApplyNetworkRequestOverrides(FrameTreeNode* frame_tree_node,
+                                   mojom::BeginNavigationParams* begin_params,
+                                   bool* report_raw_headers);
+ 
+-bool WillCreateURLLoaderFactory(
++CONTENT_EXPORT bool WillCreateURLLoaderFactory(
+     RenderFrameHostImpl* rfh,
+     bool is_navigation,
+     bool is_download,
+diff --git content/browser/renderer_host/input/synthetic_gesture_target_base.h content/browser/renderer_host/input/synthetic_gesture_target_base.h
+index a1f57138a24f..f6dd542ab89e 100644
+--- content/browser/renderer_host/input/synthetic_gesture_target_base.h
++++ content/browser/renderer_host/input/synthetic_gesture_target_base.h
+@@ -8,6 +8,7 @@
+ #include "base/macros.h"
+ #include "base/time/time.h"
+ #include "content/browser/renderer_host/input/synthetic_gesture_target.h"
++#include "content/common/content_export.h"
+ #include "ui/gfx/geometry/point_f.h"
+ 
+ namespace ui {
+@@ -25,7 +26,8 @@ namespace content {
+ 
+ class RenderWidgetHostImpl;
+ 
+-class SyntheticGestureTargetBase : public SyntheticGestureTarget {
++class CONTENT_EXPORT SyntheticGestureTargetBase :
++    public SyntheticGestureTarget {
+  public:
+   explicit SyntheticGestureTargetBase(RenderWidgetHostImpl* host);
+   ~SyntheticGestureTargetBase() override;
+diff --git content/common/content_switches_internal.h content/common/content_switches_internal.h
+index 886bdf0edf8f..1d714000cce5 100644
+--- content/common/content_switches_internal.h
++++ content/common/content_switches_internal.h
+@@ -15,7 +15,7 @@ class CommandLine;
+ 
+ namespace content {
+ 
+-bool IsPinchToZoomEnabled();
++CONTENT_EXPORT bool IsPinchToZoomEnabled();
+ 
+ blink::mojom::V8CacheOptions GetV8CacheOptions();
+ 
+diff --git third_party/blink/renderer/controller/BUILD.gn third_party/blink/renderer/controller/BUILD.gn
+index ea7f0d6dbdac..d586c2900861 100644
+--- third_party/blink/renderer/controller/BUILD.gn
++++ third_party/blink/renderer/controller/BUILD.gn
+@@ -25,6 +25,7 @@ jumbo_component("controller") {
+ 
+   configs += [
+     "//build/config/compiler:wexit_time_destructors",
++    "//cef/libcef/features:config",
+     "//third_party/blink/renderer:config",
+     "//third_party/blink/renderer:inside_blink",
+     "//third_party/blink/renderer:non_test_config",
+@@ -43,6 +44,8 @@ jumbo_component("controller") {
+     "dev_tools_frontend_impl.h",
+     "memory_usage_monitor.cc",
+     "memory_usage_monitor.h",
++    "//cef/libcef/renderer/blink_glue.cc",
++    "//cef/libcef/renderer/blink_glue.h",
+   ]
+ 
+   if (is_linux) {
+diff --git ui/events/keycodes/BUILD.gn ui/events/keycodes/BUILD.gn
+index 772d0ef90e03..7d6d9aaf8014 100644
+--- ui/events/keycodes/BUILD.gn
++++ ui/events/keycodes/BUILD.gn
+@@ -16,6 +16,8 @@ jumbo_source_set("xkb") {
+ 
+   public_deps = [ "//ui/base:buildflags" ]
+ 
++  defines = [ "KEYCODES_X_IMPLEMENTATION" ]
++
+   deps = [
+     "//base",
+     "//ui/events:dom_keycode_converter",
+diff --git ui/events/keycodes/keyboard_code_conversion_xkb.h ui/events/keycodes/keyboard_code_conversion_xkb.h
+index a1f9b78704fb..c7d3558251d9 100644
+--- ui/events/keycodes/keyboard_code_conversion_xkb.h
++++ ui/events/keycodes/keyboard_code_conversion_xkb.h
+@@ -9,6 +9,7 @@
+ 
+ #include "base/strings/string16.h"
+ #include "ui/events/keycodes/dom/dom_key.h"
++#include "ui/events/keycodes/keycodes_x_export.h"
+ #include "ui/events/keycodes/xkb_keysym.h"
+ 
+ namespace ui {
+@@ -24,7 +25,7 @@ DomKey NonPrintableXKeySymToDomKey(xkb_keysym_t keysym);
+ // base::char16 DeadXKeySymToCombiningCharacter(xkb_keysym_t keysym);
+ 
+ // Return the DomKey determined by the XKB layout result (keysym, character).
+-DomKey XKeySymToDomKey(xkb_keysym_t keysym, base::char16 character);
++KEYCODES_X_EXPORT DomKey XKeySymToDomKey(xkb_keysym_t keysym, base::char16 character);
+ 
+ }  // namespace ui
+ 
+diff --git ui/events/keycodes/keysym_to_unicode.h ui/events/keycodes/keysym_to_unicode.h
+index a7983d179832..2bbee48e57ac 100644
+--- ui/events/keycodes/keysym_to_unicode.h
++++ ui/events/keycodes/keysym_to_unicode.h
+@@ -5,6 +5,8 @@
+ #ifndef UI_EVENTS_KEYCODES_KEYSYM_TO_UNICODE_H_
+ #define UI_EVENTS_KEYCODES_KEYSYM_TO_UNICODE_H_
+ 
++#include "ui/events/keycodes/keycodes_x_export.h"
++
+ #include <stdint.h>
+ 
+ namespace ui {
+@@ -13,7 +15,7 @@ namespace ui {
+ // |keysym| doesn't represent a printable character, returns zero.  We don't
+ // support characters outside the Basic Plane, and this function returns zero
+ // in that case.
+-uint16_t GetUnicodeCharacterFromXKeySym(unsigned long keysym);
++KEYCODES_X_EXPORT uint16_t GetUnicodeCharacterFromXKeySym(unsigned long keysym);
+ 
+ }  // namespace ui
+ 
diff --git a/src/patch/patches/content_2015.patch b/src/patch/patches/content_2015.patch
new file mode 100644
index 0000000..f649fac
--- /dev/null
+++ b/src/patch/patches/content_2015.patch
@@ -0,0 +1,450 @@
+diff --git chrome/browser/download/download_target_determiner.cc chrome/browser/download/download_target_determiner.cc
+index 1b5b71193d02..05dad7a969d5 100644
+--- chrome/browser/download/download_target_determiner.cc
++++ chrome/browser/download/download_target_determiner.cc
+@@ -657,7 +657,7 @@ void IsHandledBySafePlugin(int render_process_id,
+   content::PluginService* plugin_service =
+       content::PluginService::GetInstance();
+   bool plugin_found = plugin_service->GetPluginInfo(
+-      render_process_id, routing_id, url, url::Origin(), mime_type, false,
++      render_process_id, routing_id, url, true, url::Origin(), mime_type, false,
+       &is_stale, &plugin_info, &actual_mime_type);
+   if (is_stale && stale_plugin_action == RETRY_IF_STALE_PLUGIN_LIST) {
+     // The GetPlugins call causes the plugin list to be refreshed. Once that's
+diff --git chrome/browser/plugins/chrome_plugin_service_filter.cc chrome/browser/plugins/chrome_plugin_service_filter.cc
+index e46003ea5e4a..503876cf7725 100644
+--- chrome/browser/plugins/chrome_plugin_service_filter.cc
++++ chrome/browser/plugins/chrome_plugin_service_filter.cc
+@@ -163,6 +163,7 @@ bool ChromePluginServiceFilter::IsPluginAvailable(
+     int render_process_id,
+     int render_frame_id,
+     const GURL& plugin_content_url,
++    bool is_main_frame,
+     const url::Origin& main_frame_origin,
+     content::WebPluginInfo* plugin) {
+   base::AutoLock auto_lock(lock_);
+diff --git chrome/browser/plugins/chrome_plugin_service_filter.h chrome/browser/plugins/chrome_plugin_service_filter.h
+index 937d3d5bc84f..ac327392dcf3 100644
+--- chrome/browser/plugins/chrome_plugin_service_filter.h
++++ chrome/browser/plugins/chrome_plugin_service_filter.h
+@@ -64,6 +64,7 @@ class ChromePluginServiceFilter : public content::PluginServiceFilter,
+   bool IsPluginAvailable(int render_process_id,
+                          int render_frame_id,
+                          const GURL& plugin_content_url,
++                         bool is_main_frame,
+                          const url::Origin& main_frame_origin,
+                          content::WebPluginInfo* plugin) override;
+ 
+diff --git chrome/browser/plugins/pdf_iframe_navigation_throttle.cc chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
+index 5b780f8bf34c..a75d0da983b4 100644
+--- chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
++++ chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
+@@ -65,7 +65,7 @@ bool IsPDFPluginEnabled(content::NavigationHandle* navigation_handle,
+ 
+   content::WebPluginInfo plugin_info;
+   return content::PluginService::GetInstance()->GetPluginInfo(
+-      process_id, routing_id, navigation_handle->GetURL(),
++      process_id, routing_id, navigation_handle->GetURL(), false,
+       web_contents->GetMainFrame()->GetLastCommittedOrigin(), kPDFMimeType,
+       false /* allow_wildcard */, is_stale, &plugin_info,
+       nullptr /* actual_mime_type */);
+diff --git chrome/browser/ui/views/frame/browser_root_view.cc chrome/browser/ui/views/frame/browser_root_view.cc
+index 2277ac1008af..db0c90677538 100644
+--- chrome/browser/ui/views/frame/browser_root_view.cc
++++ chrome/browser/ui/views/frame/browser_root_view.cc
+@@ -76,7 +76,7 @@ void OnFindURLMimeType(const GURL& url,
+ #if BUILDFLAG(ENABLE_PLUGINS)
+   content::WebPluginInfo plugin;
+   result = result || content::PluginService::GetInstance()->GetPluginInfo(
+-                         process_id, routing_id, url, url::Origin(), mime_type,
++                         process_id, routing_id, url, true, url::Origin(), mime_type,
+                          false, nullptr, &plugin, nullptr);
+ #endif
+ 
+diff --git content/browser/devtools/devtools_http_handler.cc content/browser/devtools/devtools_http_handler.cc
+index 4956b35c1278..2f9c0621f53e 100644
+--- content/browser/devtools/devtools_http_handler.cc
++++ content/browser/devtools/devtools_http_handler.cc
+@@ -571,7 +571,7 @@ void DevToolsHttpHandler::OnJsonRequest(
+     version.SetString("Protocol-Version",
+                       DevToolsAgentHost::GetProtocolVersion());
+     version.SetString("WebKit-Version", GetWebKitVersion());
+-    version.SetString("Browser", GetContentClient()->browser()->GetProduct());
++    version.SetString("Browser", GetContentClient()->browser()->GetChromeProduct());
+     version.SetString("User-Agent",
+                       GetContentClient()->browser()->GetUserAgent());
+     version.SetString("V8-Version", V8_VERSION_STRING);
+diff --git content/browser/frame_host/render_frame_message_filter.cc content/browser/frame_host/render_frame_message_filter.cc
+index f6511e22ab05..0ccf811d7b22 100644
+--- content/browser/frame_host/render_frame_message_filter.cc
++++ content/browser/frame_host/render_frame_message_filter.cc
+@@ -298,6 +298,7 @@ void RenderFrameMessageFilter::OnCreateChildFrame(
+ void RenderFrameMessageFilter::OnGetPluginInfo(
+     int render_frame_id,
+     const GURL& url,
++    bool is_main_frame,
+     const url::Origin& main_frame_origin,
+     const std::string& mime_type,
+     bool* found,
+@@ -305,8 +306,9 @@ void RenderFrameMessageFilter::OnGetPluginInfo(
+     std::string* actual_mime_type) {
+   bool allow_wildcard = true;
+   *found = plugin_service_->GetPluginInfo(
+-      render_process_id_, render_frame_id, url, main_frame_origin, mime_type,
+-      allow_wildcard, nullptr, info, actual_mime_type);
++      render_process_id_, render_frame_id, url, is_main_frame,
++      main_frame_origin, mime_type, allow_wildcard, nullptr, info,
++      actual_mime_type);
+ }
+ 
+ void RenderFrameMessageFilter::OnOpenChannelToPepperPlugin(
+diff --git content/browser/frame_host/render_frame_message_filter.h content/browser/frame_host/render_frame_message_filter.h
+index 451bfecfa4b1..b7cddcbaca66 100644
+--- content/browser/frame_host/render_frame_message_filter.h
++++ content/browser/frame_host/render_frame_message_filter.h
+@@ -83,6 +83,7 @@ class CONTENT_EXPORT RenderFrameMessageFilter : public BrowserMessageFilter {
+ #if BUILDFLAG(ENABLE_PLUGINS)
+   void OnGetPluginInfo(int render_frame_id,
+                        const GURL& url,
++                       bool is_main_frame,
+                        const url::Origin& main_frame_origin,
+                        const std::string& mime_type,
+                        bool* found,
+diff --git content/browser/loader/navigation_url_loader_impl.cc content/browser/loader/navigation_url_loader_impl.cc
+index 2be5d4400de9..14ece86df0e3 100644
+--- content/browser/loader/navigation_url_loader_impl.cc
++++ content/browser/loader/navigation_url_loader_impl.cc
+@@ -722,6 +722,13 @@ class NavigationURLLoaderImpl::URLLoaderRequestController
+             resource_request_->has_user_gesture,
+             resource_request_->request_initiator, &loader_factory);
+ 
++        if (!handled) {
++          handled = GetContentClient()->browser()->HandleExternalProtocol(
++              web_contents_getter_, frame_tree_node_id_,
++              navigation_ui_data_.get(), *resource_request_,
++              &loader_factory);
++        }
++
+         if (loader_factory) {
+           factory =
+               base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
+@@ -932,7 +939,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController
+         frame_tree_node->current_frame_host()->GetProcess()->GetID();
+     int routing_id = frame_tree_node->current_frame_host()->GetRoutingID();
+     bool has_plugin = PluginService::GetInstance()->GetPluginInfo(
+-        render_process_id, routing_id, resource_request_->url, url::Origin(),
++        render_process_id, routing_id, resource_request_->url, true, url::Origin(),
+         head->mime_type, false /* allow_wildcard */, &stale, &plugin, nullptr);
+ 
+     if (stale) {
+diff --git content/browser/plugin_service_impl.cc content/browser/plugin_service_impl.cc
+index 8f0f145f9760..49f0698c89f5 100644
+--- content/browser/plugin_service_impl.cc
++++ content/browser/plugin_service_impl.cc
+@@ -340,6 +340,7 @@ bool PluginServiceImpl::GetPluginInfoArray(
+ bool PluginServiceImpl::GetPluginInfo(int render_process_id,
+                                       int render_frame_id,
+                                       const GURL& url,
++                                      bool is_main_frame,
+                                       const url::Origin& main_frame_origin,
+                                       const std::string& mime_type,
+                                       bool allow_wildcard,
+@@ -357,7 +358,8 @@ bool PluginServiceImpl::GetPluginInfo(int render_process_id,
+   for (size_t i = 0; i < plugins.size(); ++i) {
+     if (!filter_ ||
+         filter_->IsPluginAvailable(render_process_id, render_frame_id, url,
+-                                   main_frame_origin, &plugins[i])) {
++                                   is_main_frame, main_frame_origin,
++                                   &plugins[i])) {
+       *info = plugins[i];
+       if (actual_mime_type)
+         *actual_mime_type = mime_types[i];
+diff --git content/browser/plugin_service_impl.h content/browser/plugin_service_impl.h
+index 79ac3b705511..71d73d3da158 100644
+--- content/browser/plugin_service_impl.h
++++ content/browser/plugin_service_impl.h
+@@ -54,6 +54,7 @@ class CONTENT_EXPORT PluginServiceImpl : public PluginService {
+   bool GetPluginInfo(int render_process_id,
+                      int render_frame_id,
+                      const GURL& url,
++                     bool is_main_frame,
+                      const url::Origin& main_frame_origin,
+                      const std::string& mime_type,
+                      bool allow_wildcard,
+diff --git content/browser/renderer_host/plugin_registry_impl.cc content/browser/renderer_host/plugin_registry_impl.cc
+index a6d6188fb139..7ac57de6fd55 100644
+--- content/browser/renderer_host/plugin_registry_impl.cc
++++ content/browser/renderer_host/plugin_registry_impl.cc
+@@ -29,6 +29,7 @@ void PluginRegistryImpl::Bind(
+ }
+ 
+ void PluginRegistryImpl::GetPlugins(bool refresh,
++                                    bool is_main_frame,
+                                     const url::Origin& main_frame_origin,
+                                     GetPluginsCallback callback) {
+   auto* plugin_service = PluginServiceImpl::GetInstance();
+@@ -50,10 +51,11 @@ void PluginRegistryImpl::GetPlugins(bool refresh,
+ 
+   plugin_service->GetPlugins(base::BindOnce(
+       &PluginRegistryImpl::GetPluginsComplete, weak_factory_.GetWeakPtr(),
+-      main_frame_origin, std::move(callback)));
++      is_main_frame, main_frame_origin, std::move(callback)));
+ }
+ 
+ void PluginRegistryImpl::GetPluginsComplete(
++    bool is_main_frame,
+     const url::Origin& main_frame_origin,
+     GetPluginsCallback callback,
+     const std::vector<WebPluginInfo>& all_plugins) {
+@@ -76,6 +78,7 @@ void PluginRegistryImpl::GetPluginsComplete(
+     // TODO(crbug.com/621724): Pass an url::Origin instead of a GURL.
+     if (!filter || filter->IsPluginAvailable(render_process_id_, routing_id,
+                                              main_frame_origin.GetURL(),
++                                             is_main_frame,
+                                              main_frame_origin, &plugin)) {
+       auto plugin_blink = blink::mojom::PluginInfo::New();
+       plugin_blink->name = plugin.name;
+diff --git content/browser/renderer_host/plugin_registry_impl.h content/browser/renderer_host/plugin_registry_impl.h
+index 632ae86c6fd6..55b749ec1242 100644
+--- content/browser/renderer_host/plugin_registry_impl.h
++++ content/browser/renderer_host/plugin_registry_impl.h
+@@ -24,11 +24,13 @@ class PluginRegistryImpl : public blink::mojom::PluginRegistry {
+ 
+   // blink::mojom::PluginRegistry
+   void GetPlugins(bool refresh,
++                  bool is_main_frame,
+                   const url::Origin& main_frame_origin,
+                   GetPluginsCallback callback) override;
+ 
+  private:
+-  void GetPluginsComplete(const url::Origin& main_frame_origin,
++  void GetPluginsComplete(bool is_main_frame,
++                          const url::Origin& main_frame_origin,
+                           GetPluginsCallback callback,
+                           const std::vector<WebPluginInfo>& all_plugins);
+ 
+diff --git content/common/frame_messages.h content/common/frame_messages.h
+index 118765f9323f..614830e4f406 100644
+--- content/common/frame_messages.h
++++ content/common/frame_messages.h
+@@ -687,9 +687,10 @@ IPC_MESSAGE_ROUTED1(FrameHostMsg_PepperStopsPlayback,
+ // type. If there is no matching plugin, |found| is false.
+ // |actual_mime_type| is the actual mime type supported by the
+ // found plugin.
+-IPC_SYNC_MESSAGE_CONTROL4_3(FrameHostMsg_GetPluginInfo,
++IPC_SYNC_MESSAGE_CONTROL5_3(FrameHostMsg_GetPluginInfo,
+                             int /* render_frame_id */,
+                             GURL /* url */,
++                            bool /* is_main_frame */,
+                             url::Origin /* main_frame_origin */,
+                             std::string /* mime_type */,
+                             bool /* found */,
+diff --git content/public/browser/content_browser_client.cc content/public/browser/content_browser_client.cc
+index 89f33b33f8c8..30512bab9b12 100644
+--- content/public/browser/content_browser_client.cc
++++ content/public/browser/content_browser_client.cc
+@@ -9,7 +9,7 @@
+ // declarations instead of including more headers. If that is infeasible, adjust
+ // the limit. For more info, see
+ // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/wmax_tokens.md
+-#pragma clang max_tokens_here 820000
++// #pragma clang max_tokens_here 820000
+ 
+ #include <utility>
+ 
+diff --git content/public/browser/content_browser_client.h content/public/browser/content_browser_client.h
+index 26cd6d1afcb8..5ac45c794daf 100644
+--- content/public/browser/content_browser_client.h
++++ content/public/browser/content_browser_client.h
+@@ -26,6 +26,7 @@
+ #include "content/common/content_export.h"
+ #include "content/public/browser/certificate_request_result_type.h"
+ #include "content/public/browser/generated_code_cache_settings.h"
++#include "content/public/browser/web_contents.h"
+ #include "content/public/common/page_visibility_state.h"
+ #include "content/public/common/previews_state.h"
+ #include "content/public/common/window_container_type.mojom-forward.h"
+@@ -1631,6 +1632,14 @@ class CONTENT_EXPORT ContentBrowserClient {
+       const base::Optional<url::Origin>& initiating_origin,
+       mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory);
+ 
++  // Same as above, but exposing the whole ResourceRequest object.
++  virtual bool HandleExternalProtocol(
++      WebContents::Getter web_contents_getter,
++      int frame_tree_node_id,
++      NavigationUIData* navigation_data,
++      const network::ResourceRequest& request,
++      mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) { return false; }
++
+   // Creates an OverlayWindow to be used for Picture-in-Picture. This window
+   // will house the content shown when in Picture-in-Picture mode. This will
+   // return a new OverlayWindow.
+@@ -1699,6 +1708,10 @@ class CONTENT_EXPORT ContentBrowserClient {
+   // Used as part of the user agent string.
+   virtual std::string GetProduct();
+ 
++  // Returns the Chrome-specific product string. This is used for compatibility
++  // purposes with external tools like Selenium.
++  virtual std::string GetChromeProduct() { return GetProduct(); }
++
+   // Returns the user agent.  Content may cache this value.
+   virtual std::string GetUserAgent();
+ 
+diff --git content/public/browser/plugin_service.h content/public/browser/plugin_service.h
+index dcfd3ff37186..4e822ee5f750 100644
+--- content/public/browser/plugin_service.h
++++ content/public/browser/plugin_service.h
+@@ -74,6 +74,7 @@ class CONTENT_EXPORT PluginService {
+   virtual bool GetPluginInfo(int render_process_id,
+                              int render_frame_id,
+                              const GURL& url,
++                             bool is_main_frame,
+                              const url::Origin& main_frame_origin,
+                              const std::string& mime_type,
+                              bool allow_wildcard,
+diff --git content/public/browser/plugin_service_filter.h content/public/browser/plugin_service_filter.h
+index 98c59005599e..69752184745d 100644
+--- content/public/browser/plugin_service_filter.h
++++ content/public/browser/plugin_service_filter.h
+@@ -32,6 +32,7 @@ class PluginServiceFilter {
+   virtual bool IsPluginAvailable(int render_process_id,
+                                  int render_frame_id,
+                                  const GURL& url,
++                                 bool is_main_frame,
+                                  const url::Origin& main_frame_origin,
+                                  WebPluginInfo* plugin) = 0;
+ 
+diff --git content/public/renderer/content_renderer_client.h content/public/renderer/content_renderer_client.h
+index 764556d80f1b..7267aa6c26f6 100644
+--- content/public/renderer/content_renderer_client.h
++++ content/public/renderer/content_renderer_client.h
+@@ -81,6 +81,9 @@ class CONTENT_EXPORT ContentRendererClient {
+   // binding requests from RenderProcessHost::BindReceiver().
+   virtual void ExposeInterfacesToBrowser(mojo::BinderMap* binders) {}
+ 
++  // Notifies that the RenderThread can now send sync IPC messages.
++  virtual void RenderThreadConnected() {}
++
+   // Notifies that a new RenderFrame has been created.
+   virtual void RenderFrameCreated(RenderFrame* render_frame) {}
+ 
+@@ -310,6 +313,10 @@ class CONTENT_EXPORT ContentRendererClient {
+   // This method may invalidate the frame.
+   virtual void RunScriptsAtDocumentIdle(RenderFrame* render_frame) {}
+ 
++  // Notifies that a DevTools agent has attached or detached.
++  virtual void DevToolsAgentAttached() {}
++  virtual void DevToolsAgentDetached() {}
++
+   // Allows subclasses to enable some runtime features before Blink has
+   // started.
+   virtual void SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() {}
+diff --git content/renderer/render_frame_impl.cc content/renderer/render_frame_impl.cc
+index 2ab5e79b71cc..51cd9556a884 100644
+--- content/renderer/render_frame_impl.cc
++++ content/renderer/render_frame_impl.cc
+@@ -3742,7 +3742,8 @@ blink::WebPlugin* RenderFrameImpl::CreatePlugin(
+   std::string mime_type;
+   bool found = false;
+   Send(new FrameHostMsg_GetPluginInfo(
+-      routing_id_, params.url, frame_->Top()->GetSecurityOrigin(),
++      routing_id_, params.url, frame_->Parent() == nullptr,
++      frame_->Top()->GetSecurityOrigin(),
+       params.mime_type.Utf8(), &found, &info, &mime_type));
+   if (!found)
+     return nullptr;
+diff --git content/renderer/render_thread_impl.cc content/renderer/render_thread_impl.cc
+index 76176518dfe4..b7d8dc75a23a 100644
+--- content/renderer/render_thread_impl.cc
++++ content/renderer/render_thread_impl.cc
+@@ -630,6 +630,8 @@ void RenderThreadImpl::Init() {
+       GetContentClient()->renderer()->CreateURLLoaderThrottleProvider(
+           URLLoaderThrottleProviderType::kFrame);
+ 
++  GetContentClient()->renderer()->RenderThreadConnected();
++
+   GetAssociatedInterfaceRegistry()->AddInterface(base::BindRepeating(
+       &RenderThreadImpl::OnRendererInterfaceReceiver, base::Unretained(this)));
+ 
+diff --git content/renderer/renderer_blink_platform_impl.cc content/renderer/renderer_blink_platform_impl.cc
+index 8c641e6e3a6f..d2d1a7c2b6de 100644
+--- content/renderer/renderer_blink_platform_impl.cc
++++ content/renderer/renderer_blink_platform_impl.cc
+@@ -899,6 +899,15 @@ RendererBlinkPlatformImpl::GetGpuFactories() {
+ 
+ //------------------------------------------------------------------------------
+ 
++void RendererBlinkPlatformImpl::DevToolsAgentAttached() {
++  GetContentClient()->renderer()->DevToolsAgentAttached();
++}
++void RendererBlinkPlatformImpl::DevToolsAgentDetached() {
++  GetContentClient()->renderer()->DevToolsAgentDetached();
++}
++
++//------------------------------------------------------------------------------
++
+ blink::mojom::CodeCacheHost& RendererBlinkPlatformImpl::GetCodeCacheHost() {
+   if (!code_cache_host_) {
+     code_cache_host_ = mojo::SharedRemote<blink::mojom::CodeCacheHost>(
+diff --git content/renderer/renderer_blink_platform_impl.h content/renderer/renderer_blink_platform_impl.h
+index 80490f5b8d52..8c07dc87edbb 100644
+--- content/renderer/renderer_blink_platform_impl.h
++++ content/renderer/renderer_blink_platform_impl.h
+@@ -198,6 +198,9 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl {
+ 
+   media::GpuVideoAcceleratorFactories* GetGpuFactories() override;
+ 
++  void DevToolsAgentAttached() override;
++  void DevToolsAgentDetached() override;
++
+   // Returns non-null.
+   // It is invalid to call this in an incomplete env where
+   // RenderThreadImpl::current() returns nullptr (e.g. in some tests).
+diff --git content/shell/browser/shell_plugin_service_filter.cc content/shell/browser/shell_plugin_service_filter.cc
+index 427132c6920e..4809ddaf21e1 100644
+--- content/shell/browser/shell_plugin_service_filter.cc
++++ content/shell/browser/shell_plugin_service_filter.cc
+@@ -17,6 +17,7 @@ bool ShellPluginServiceFilter::IsPluginAvailable(
+     int render_process_id,
+     int render_frame_id,
+     const GURL& url,
++    bool is_main_frame,
+     const url::Origin& main_frame_origin,
+     WebPluginInfo* plugin) {
+   return plugin->name == base::ASCIIToUTF16("Blink Test Plugin") ||
+diff --git content/shell/browser/shell_plugin_service_filter.h content/shell/browser/shell_plugin_service_filter.h
+index 337b4b0653fe..107ab4c9d8a8 100644
+--- content/shell/browser/shell_plugin_service_filter.h
++++ content/shell/browser/shell_plugin_service_filter.h
+@@ -20,6 +20,7 @@ class ShellPluginServiceFilter : public PluginServiceFilter {
+   bool IsPluginAvailable(int render_process_id,
+                          int render_frame_id,
+                          const GURL& url,
++                         bool is_main_frame,
+                          const url::Origin& main_frame_origin,
+                          WebPluginInfo* plugin) override;
+ 
+diff --git content/test/fake_plugin_service.cc content/test/fake_plugin_service.cc
+index eb280cd21c6d..b499b2536b2d 100644
+--- content/test/fake_plugin_service.cc
++++ content/test/fake_plugin_service.cc
+@@ -28,6 +28,7 @@ bool FakePluginService::GetPluginInfoArray(
+ bool FakePluginService::GetPluginInfo(int render_process_id,
+                                       int render_frame_id,
+                                       const GURL& url,
++                                      bool is_main_frame,
+                                       const url::Origin& main_frame_origin,
+                                       const std::string& mime_type,
+                                       bool allow_wildcard,
+diff --git content/test/fake_plugin_service.h content/test/fake_plugin_service.h
+index df49da7cbec0..edf526fff7f8 100644
+--- content/test/fake_plugin_service.h
++++ content/test/fake_plugin_service.h
+@@ -29,6 +29,7 @@ class FakePluginService : public PluginService {
+   bool GetPluginInfo(int render_process_id,
+                      int render_frame_id,
+                      const GURL& url,
++                     bool is_main_frame,
+                      const url::Origin& main_frame_origin,
+                      const std::string& mime_type,
+                      bool allow_wildcard,
diff --git a/src/patch/patches/content_app_shutdown_2798.patch b/src/patch/patches/content_app_shutdown_2798.patch
new file mode 100644
index 0000000..3ed48d5
--- /dev/null
+++ b/src/patch/patches/content_app_shutdown_2798.patch
@@ -0,0 +1,73 @@
+diff --git content/app/content_main_runner_impl.cc content/app/content_main_runner_impl.cc
+index bb72915a26e2..9fbafc0643e1 100644
+--- content/app/content_main_runner_impl.cc
++++ content/app/content_main_runner_impl.cc
+@@ -44,6 +44,7 @@
+ #include "base/strings/stringprintf.h"
+ #include "base/task/post_task.h"
+ #include "base/task/thread_pool/thread_pool_instance.h"
++#include "base/threading/thread_restrictions.h"
+ #include "base/trace_event/trace_event.h"
+ #include "components/discardable_memory/service/discardable_shared_memory_manager.h"
+ #include "components/download/public/common/download_task_runner.h"
+@@ -1016,6 +1017,11 @@ void ContentMainRunnerImpl::Shutdown() {
+   is_shutdown_ = true;
+ }
+ 
++void ContentMainRunnerImpl::ShutdownOnUIThread() {
++  base::ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
++  discardable_shared_memory_manager_.reset();
++}
++
+ // static
+ ContentMainRunner* ContentMainRunner::Create() {
+   return ContentMainRunnerImpl::Create();
+diff --git content/app/content_main_runner_impl.h content/app/content_main_runner_impl.h
+index 4632e35eaf65..c74d9ca750c7 100644
+--- content/app/content_main_runner_impl.h
++++ content/app/content_main_runner_impl.h
+@@ -51,6 +51,8 @@ class ContentMainRunnerImpl : public ContentMainRunner {
+   int Run(bool start_service_manager_only) override;
+   void Shutdown() override;
+ 
++  void ShutdownOnUIThread();
++
+  private:
+ #if !defined(CHROME_MULTIPLE_DLL_CHILD)
+   int RunServiceManager(MainFunctionParams& main_function_params,
+diff --git content/app/content_service_manager_main_delegate.cc content/app/content_service_manager_main_delegate.cc
+index 86d0a470a7c2..5e4b91d31e3f 100644
+--- content/app/content_service_manager_main_delegate.cc
++++ content/app/content_service_manager_main_delegate.cc
+@@ -130,4 +130,8 @@ void ContentServiceManagerMainDelegate::SetStartServiceManagerOnly(
+   start_service_manager_only_ = start_service_manager_only;
+ }
+ 
++void ContentServiceManagerMainDelegate::ShutdownOnUIThread() {
++  content_main_runner_->ShutdownOnUIThread();
++}
++
+ }  // namespace content
+diff --git content/app/content_service_manager_main_delegate.h content/app/content_service_manager_main_delegate.h
+index 0b4042aec2ae..b8643b10a02e 100644
+--- content/app/content_service_manager_main_delegate.h
++++ content/app/content_service_manager_main_delegate.h
+@@ -18,7 +18,8 @@ namespace content {
+ 
+ class ContentMainRunnerImpl;
+ 
+-class ContentServiceManagerMainDelegate : public service_manager::MainDelegate {
++class CONTENT_EXPORT ContentServiceManagerMainDelegate :
++    public service_manager::MainDelegate {
+  public:
+   explicit ContentServiceManagerMainDelegate(const ContentMainParams& params);
+   ~ContentServiceManagerMainDelegate() override;
+@@ -46,6 +47,8 @@ class ContentServiceManagerMainDelegate : public service_manager::MainDelegate {
+   // full browser.
+   void SetStartServiceManagerOnly(bool start_service_manager_only);
+ 
++  void ShutdownOnUIThread();
++
+  private:
+   ContentMainParams content_main_params_;
+   std::unique_ptr<ContentMainRunnerImpl> content_main_runner_;
diff --git a/src/patch/patches/content_pepper_flash_1586.patch b/src/patch/patches/content_pepper_flash_1586.patch
new file mode 100644
index 0000000..3a78939
--- /dev/null
+++ b/src/patch/patches/content_pepper_flash_1586.patch
@@ -0,0 +1,13 @@
+diff --git content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc
+index be208ad722fa..df9c50da18ad 100644
+--- content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc
++++ content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc
+@@ -56,7 +56,7 @@ PepperFlashFileMessageFilter::PepperFlashFileMessageFilter(
+     // will construct a bad path and could provide access to the wrong files.
+     // In this case, |plugin_data_directory_| will remain unset and
+     // |ValidateAndConvertPepperFilePath| will fail.
+-    NOTREACHED();
++    //NOTREACHED();
+   } else {
+     plugin_data_directory_ = GetDataDirName(profile_data_directory).Append(
+         base::FilePath::FromUTF8Unsafe(plugin_name));
diff --git a/src/patch/patches/crashpad_1995.patch b/src/patch/patches/crashpad_1995.patch
new file mode 100644
index 0000000..fb06747
--- /dev/null
+++ b/src/patch/patches/crashpad_1995.patch
@@ -0,0 +1,603 @@
+diff --git chrome/chrome_elf/BUILD.gn chrome/chrome_elf/BUILD.gn
+index 9b08e23e921b..49182504ae36 100644
+--- chrome/chrome_elf/BUILD.gn
++++ chrome/chrome_elf/BUILD.gn
+@@ -7,6 +7,7 @@
+ 
+ import("//build/config/compiler/compiler.gni")
+ import("//build/config/win/manifest.gni")
++import("//cef/libcef/features/features.gni")
+ import("//chrome/process_version_rc_template.gni")
+ import("//testing/test.gni")
+ 
+@@ -104,9 +105,6 @@ source_set("constants") {
+ 
+ static_library("crash") {
+   sources = [
+-    "../app/chrome_crash_reporter_client_win.cc",
+-    "../app/chrome_crash_reporter_client_win.h",
+-    "../common/chrome_result_codes.h",
+     "crash/crash_helper.cc",
+     "crash/crash_helper.h",
+   ]
+@@ -114,6 +112,7 @@ static_library("crash") {
+     ":hook_util",
+     "//base",  # This needs to go.  DEP of app, crash_keys, client.
+     "//base:base_static",  # pe_image
++    "//cef/libcef/features",
+     "//chrome/install_static:install_static_util",
+     "//components/crash/core/app",
+     "//components/crash/core/common",  # crash_keys
+@@ -121,6 +120,17 @@ static_library("crash") {
+     "//content/public/common:result_codes",
+     "//third_party/crashpad/crashpad/client",  # DumpWithoutCrash
+   ]
++
++  if (enable_cef) {
++    deps += [ "//cef:chrome_elf_set" ]
++    include_dirs = [ "//cef" ]
++  } else {
++    sources += [
++      "//chrome/app/chrome_crash_reporter_client_win.cc",
++      "//chrome/app/chrome_crash_reporter_client_win.h",
++      "//chrome/common/chrome_result_codes.h",
++    ]
++  }
+ }
+ 
+ source_set("dll_hash") {
+diff --git chrome/chrome_elf/crash/crash_helper.cc chrome/chrome_elf/crash/crash_helper.cc
+index 42a4bfcdb856..9f674625a155 100644
+--- chrome/chrome_elf/crash/crash_helper.cc
++++ chrome/chrome_elf/crash/crash_helper.cc
+@@ -11,12 +11,17 @@
+ #include <string>
+ #include <vector>
+ 
++#include "cef/libcef/features/features.h"
+ #include "chrome/app/chrome_crash_reporter_client_win.h"
+ #include "chrome/chrome_elf/hook_util/hook_util.h"
+ #include "components/crash/core/app/crashpad.h"
+ #include "components/crash/core/common/crash_keys.h"
+ #include "third_party/crashpad/crashpad/client/crashpad_client.h"
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/common/crash_reporter_client.h"
++#endif
++
+ namespace {
+ 
+ // Crash handling from elf is only enabled for the chrome.exe process.
+@@ -77,7 +82,11 @@ bool InitializeCrashReporting() {
+   g_crash_reports = new std::vector<crash_reporter::Report>;
+   g_set_unhandled_exception_filter = new elf_hook::IATHook();
+ 
++#if BUILDFLAG(ENABLE_CEF)
++  CefCrashReporterClient::InitializeCrashReportingForProcess();
++#else
+   ChromeCrashReporterClient::InitializeCrashReportingForProcess();
++#endif
+ 
+   g_crash_helper_enabled = true;
+   return true;
+diff --git chrome/common/crash_keys.cc chrome/common/crash_keys.cc
+index f9a6c6b6ae2d..3701b4423899 100644
+--- chrome/common/crash_keys.cc
++++ chrome/common/crash_keys.cc
+@@ -4,6 +4,8 @@
+ 
+ #include "chrome/common/crash_keys.h"
+ 
++#include <iterator>
++
+ #include "base/base_switches.h"
+ #include "base/command_line.h"
+ #include "base/logging.h"
+@@ -28,7 +30,7 @@
+ namespace crash_keys {
+ 
+ // Return true if we DON'T want to upload this flag to the crash server.
+-static bool IsBoringSwitch(const std::string& flag) {
++bool IsBoringChromeSwitch(const std::string& flag) {
+   static const char* const kIgnoreSwitches[] = {
+     switches::kEnableLogging,
+     switches::kFlagSwitchesBegin,
+@@ -83,7 +85,7 @@ static bool IsBoringSwitch(const std::string& flag) {
+ }
+ 
+ void SetCrashKeysFromCommandLine(const base::CommandLine& command_line) {
+-  return SetSwitchesFromCommandLine(command_line, &IsBoringSwitch);
++  return SetSwitchesFromCommandLine(command_line, &IsBoringChromeSwitch);
+ }
+ 
+ void SetActiveExtensions(const std::set<std::string>& extensions) {
+diff --git chrome/common/crash_keys.h chrome/common/crash_keys.h
+index bcf172e645a2..f879aa745adf 100644
+--- chrome/common/crash_keys.h
++++ chrome/common/crash_keys.h
+@@ -17,6 +17,10 @@ class CommandLine;
+ 
+ namespace crash_keys {
+ 
++// Returns true if the specified command-line flag should be excluded from
++// crash reporting.
++bool IsBoringChromeSwitch(const std::string& flag);
++
+ // Sets the kNumSwitches key and the set of keys named using kSwitchFormat based
+ // on the given |command_line|.
+ void SetCrashKeysFromCommandLine(const base::CommandLine& command_line);
+diff --git components/crash/core/app/breakpad_linux.cc components/crash/core/app/breakpad_linux.cc
+index 192b0a7f137f..d53b90173ae2 100644
+--- components/crash/core/app/breakpad_linux.cc
++++ components/crash/core/app/breakpad_linux.cc
+@@ -28,6 +28,7 @@
+ #include "base/base_switches.h"
+ #include "base/command_line.h"
+ #include "base/debug/dump_without_crashing.h"
++#include "base/debug/leak_annotations.h"
+ #include "base/files/file_path.h"
+ #include "base/lazy_instance.h"
+ #include "base/linux_util.h"
+@@ -104,6 +105,7 @@ namespace {
+ uint64_t g_crash_loop_before_time = 0;
+ #else
+ const char kUploadURL[] = "https://clients2.google.com/cr/report";
++const char* g_crash_server_url = kUploadURL;
+ #endif
+ 
+ bool g_is_crash_reporter_enabled = false;
+@@ -717,7 +719,7 @@ bool CrashDone(const MinidumpDescriptor& minidump,
+   info.process_type_length = 7;
+   info.distro = base::g_linux_distro;
+   info.distro_length = my_strlen(base::g_linux_distro);
+-  info.upload = upload;
++  info.upload = upload && g_crash_server_url;
+   info.process_start_time = g_process_start_time;
+   info.oom_size = base::g_oom_size;
+   info.pid = g_pid;
+@@ -1404,7 +1406,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
+     header_content_encoding,
+     header_content_type,
+     post_file,
+-    kUploadURL,
++    g_crash_server_url,
+     "--timeout=10",  // Set a timeout so we don't hang forever.
+     "--tries=1",     // Don't retry if the upload fails.
+     "-O",  // Output reply to the file descriptor path.
+@@ -1744,10 +1746,19 @@ void HandleCrashDump(const BreakpadInfo& info) {
+     GetCrashReporterClient()->GetProductNameAndVersion(&product_name, &version);
+ 
+     writer.AddBoundary();
+-    writer.AddPairString("prod", product_name);
++    writer.AddPairString("product", product_name);
++    writer.AddBoundary();
++    writer.AddPairString("version", version);
+     writer.AddBoundary();
+-    writer.AddPairString("ver", version);
++
++#if defined(ARCH_CPU_32_BITS)
++    const char* platform = "linux32";
++#elif defined(ARCH_CPU_64_BITS)
++    const char* platform = "linux64";
++#endif
++    writer.AddPairString("platform", platform);
+     writer.AddBoundary();
++
+     if (info.pid > 0) {
+       char pid_value_buf[kUint64StringSize];
+       uint64_t pid_value_len = my_uint64_len(info.pid);
+@@ -1864,6 +1875,9 @@ void HandleCrashDump(const BreakpadInfo& info) {
+         crash_reporter::internal::TransitionalCrashKeyStorage;
+     CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys);
+     const CrashKeyStorage::Entry* entry;
++
++    crash_reporter::CrashReporterClient::ParameterMap parameters;
++
+     while ((entry = crash_key_iterator.Next())) {
+       if (g_use_crash_key_white_list && !IsInWhiteList(entry->key))
+         continue;
+@@ -1876,7 +1890,13 @@ void HandleCrashDump(const BreakpadInfo& info) {
+                        ? CrashKeyStorage::value_size - 1
+                        : my_strlen(entry->value);
+ 
+-      writer.AddPairData(entry->key, key_size, entry->value, value_size);
++      parameters.insert(std::make_pair(std::string{entry->key, key_size}, std::string{entry->value, value_size}));
++    }
++    if (!parameters.empty())
++      parameters = GetCrashReporterClient()->FilterParameters(parameters);
++
++    for (const auto& param : parameters) {
++      writer.AddPairData(param.first.data(), param.first.size(), param.second.data(), param.second.size());
+       writer.AddBoundary();
+       writer.Flush();
+     }
+@@ -2088,6 +2108,17 @@ void SetChannelCrashKey(const std::string& channel) {
+   channel_key.Set(channel);
+ }
+ 
++void SetCrashServerURL(const std::string& url) {
++  if (url.empty()) {
++    g_crash_server_url = nullptr;
++  } else {
++    char* new_url = new char[url.size() + 1];
++    ANNOTATE_LEAKING_OBJECT_PTR(new_url);
++    strcpy(new_url, url.c_str());
++    g_crash_server_url = new_url;
++  }
++}
++
+ #if defined(OS_ANDROID)
+ void InitNonBrowserCrashReporterForAndroid(const std::string& process_type) {
+   SanitizationInfo sanitization_info;
+diff --git components/crash/core/app/breakpad_linux.h components/crash/core/app/breakpad_linux.h
+index 9ea80370a842..3043f7d32f33 100644
+--- components/crash/core/app/breakpad_linux.h
++++ components/crash/core/app/breakpad_linux.h
+@@ -20,6 +20,9 @@ extern void InitCrashReporter(const std::string& process_type);
+ // Sets the product/distribution channel crash key.
+ void SetChannelCrashKey(const std::string& channel);
+ 
++// Set the crash server URL.
++void SetCrashServerURL(const std::string& url);
++
+ #if defined(OS_ANDROID)
+ extern void InitCrashKeysForTesting();
+ 
+diff --git components/crash/core/app/crash_reporter_client.cc components/crash/core/app/crash_reporter_client.cc
+index e778f68af30f..d2a2a6bf1f67 100644
+--- components/crash/core/app/crash_reporter_client.cc
++++ components/crash/core/app/crash_reporter_client.cc
+@@ -88,7 +88,7 @@ int CrashReporterClient::GetResultCodeRespawnFailed() {
+ }
+ #endif
+ 
+-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
++#if defined(OS_POSIX) && !defined(OS_IOS)
+ void CrashReporterClient::GetProductNameAndVersion(const char** product_name,
+                                                    const char** version) {
+ }
+@@ -97,6 +97,7 @@ void CrashReporterClient::GetProductNameAndVersion(std::string* product_name,
+                                                    std::string* version,
+                                                    std::string* channel) {}
+ 
++#if !defined(OS_MACOSX)
+ base::FilePath CrashReporterClient::GetReporterLogFilename() {
+   return base::FilePath();
+ }
+@@ -106,6 +107,7 @@ bool CrashReporterClient::HandleCrashDump(const char* crashdump_filename,
+   return false;
+ }
+ #endif
++#endif
+ 
+ #if defined(OS_WIN)
+ bool CrashReporterClient::GetCrashDumpLocation(base::string16* crash_dir) {
+@@ -148,6 +150,32 @@ bool CrashReporterClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
+   return false;
+ }
+ 
++bool CrashReporterClient::EnableBreakpadForProcess(
++    const std::string& process_type) {
++  return false;
++}
++
++std::string CrashReporterClient::GetCrashServerURL() {
++  return std::string();
++}
++
++void CrashReporterClient::GetCrashOptionalArguments(
++    std::vector<std::string>* arguments) {
++}
++
++#if defined(OS_WIN)
++base::string16 CrashReporterClient::GetCrashExternalHandler(
++    const base::string16& exe_dir) {
++  return exe_dir + L"\\crashpad_handler.exe";
++}
++#endif
++
++#if defined(OS_MACOSX)
++bool CrashReporterClient::EnableBrowserCrashForwarding() {
++  return true;
++}
++#endif
++
+ #if defined(OS_ANDROID)
+ unsigned int CrashReporterClient::GetCrashDumpPercentage() {
+   return 100;
+@@ -200,9 +228,11 @@ bool CrashReporterClient::ShouldMonitorCrashHandlerExpensively() {
+   return false;
+ }
+ 
+-bool CrashReporterClient::EnableBreakpadForProcess(
+-    const std::string& process_type) {
+-  return false;
++#if defined(OS_POSIX) && !defined(OS_MACOSX)
++CrashReporterClient::ParameterMap
++CrashReporterClient::FilterParameters(const ParameterMap& parameters) {
++  return parameters;
+ }
++#endif
+ 
+ }  // namespace crash_reporter
+diff --git components/crash/core/app/crash_reporter_client.h components/crash/core/app/crash_reporter_client.h
+index 9cc78fc25840..f54cdbdf2fc4 100644
+--- components/crash/core/app/crash_reporter_client.h
++++ components/crash/core/app/crash_reporter_client.h
+@@ -5,7 +5,9 @@
+ #ifndef COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
+ #define COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
+ 
++#include <map>
+ #include <string>
++#include <vector>
+ 
+ #include "base/strings/string16.h"
+ #include "build/build_config.h"
+@@ -91,7 +93,7 @@ class CrashReporterClient {
+   virtual int GetResultCodeRespawnFailed();
+ #endif
+ 
+-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
++#if defined(OS_POSIX) && !defined(OS_IOS)
+   // Returns a textual description of the product type and version to include
+   // in the crash report. Neither out parameter should be set to NULL.
+   // TODO(jperaza): Remove the 2-parameter overload of this method once all
+@@ -102,6 +104,7 @@ class CrashReporterClient {
+                                         std::string* version,
+                                         std::string* channel);
+ 
++#if !defined(OS_MACOSX)
+   virtual base::FilePath GetReporterLogFilename();
+ 
+   // Custom crash minidump handler after the minidump is generated.
+@@ -111,6 +114,7 @@ class CrashReporterClient {
+   // libc nor allocate memory normally.
+   virtual bool HandleCrashDump(const char* crashdump_filename,
+                                uint64_t crash_pid);
++#endif
+ #endif
+ 
+   // The location where minidump files should be written. Returns true if
+@@ -210,6 +214,30 @@ class CrashReporterClient {
+ 
+   // Returns true if breakpad should run in the given process type.
+   virtual bool EnableBreakpadForProcess(const std::string& process_type);
++
++  // Returns the URL for submitting crash reports.
++  virtual std::string GetCrashServerURL();
++
++  // Populate |arguments| with additional optional arguments.
++  virtual void GetCrashOptionalArguments(std::vector<std::string>* arguments);
++
++#if defined(OS_WIN)
++  // Returns the absolute path to the external crash handler exe.
++  virtual base::string16 GetCrashExternalHandler(const base::string16& exe_dir);
++#endif
++
++#if defined(OS_MACOSX)
++  // Returns true if forwarding of crashes to the system crash reporter is
++  // enabled for the browser process.
++  virtual bool EnableBrowserCrashForwarding();
++#endif
++
++#if defined(OS_POSIX) && !defined(OS_MACOSX)
++  // Provides an oportunity to modify the parameters that will be sent with a
++  // crash upload.
++  using ParameterMap = std::map<std::string, std::string>;
++  virtual ParameterMap FilterParameters(const ParameterMap& parameters);
++#endif
+ };
+ 
+ }  // namespace crash_reporter
+diff --git components/crash/core/app/crashpad.cc components/crash/core/app/crashpad.cc
+index 290b4692c16f..ef91dbdc369d 100644
+--- components/crash/core/app/crashpad.cc
++++ components/crash/core/app/crashpad.cc
+@@ -151,7 +151,8 @@ void InitializeCrashpadImpl(bool initial_client,
+   // fallback. Forwarding is turned off for debug-mode builds even for the
+   // browser process, because the system's crash reporter can take a very long
+   // time to chew on symbols.
+-  if (!browser_process || is_debug_build) {
++  if (!browser_process || is_debug_build ||
++      !GetCrashReporterClient()->EnableBrowserCrashForwarding()) {
+     crashpad::CrashpadInfo::GetCrashpadInfo()
+         ->set_system_crash_reporter_forwarding(crashpad::TriState::kDisabled);
+   }
+diff --git components/crash/core/app/crashpad_mac.mm components/crash/core/app/crashpad_mac.mm
+index b579521d5586..644756cf710c 100644
+--- components/crash/core/app/crashpad_mac.mm
++++ components/crash/core/app/crashpad_mac.mm
+@@ -16,12 +16,15 @@
+ #include "base/logging.h"
+ #include "base/mac/bundle_locations.h"
+ #include "base/mac/foundation_util.h"
++#include "base/path_service.h"
+ #include "base/strings/string_number_conversions.h"
+ #include "base/strings/string_piece.h"
+ #include "base/strings/stringprintf.h"
+ #include "base/strings/sys_string_conversions.h"
+ #include "build/branding_buildflags.h"
+ #include "components/crash/core/app/crash_reporter_client.h"
++#include "components/crash/core/app/crash_switches.h"
++#include "content/public/common/content_paths.h"
+ #include "third_party/crashpad/crashpad/client/crash_report_database.h"
+ #include "third_party/crashpad/crashpad/client/crashpad_client.h"
+ #include "third_party/crashpad/crashpad/client/crashpad_info.h"
+@@ -37,12 +40,25 @@ namespace {
+ std::map<std::string, std::string> GetProcessSimpleAnnotations() {
+   static std::map<std::string, std::string> annotations = []() -> auto {
+     std::map<std::string, std::string> process_annotations;
++
+     @autoreleasepool {
+       NSBundle* outer_bundle = base::mac::OuterBundle();
+-      NSString* product = base::mac::ObjCCast<NSString>([outer_bundle
+-          objectForInfoDictionaryKey:base::mac::CFToNSCast(kCFBundleNameKey)]);
+-      process_annotations["prod"] =
+-          base::SysNSStringToUTF8(product).append("_Mac");
++
++      CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
++      const char* product_name = "";
++      const char* product_version = "";
++      crash_reporter_client->GetProductNameAndVersion(&product_name,
++                                                      &product_version);
++
++      if (strlen(product_name) == 0) {
++        NSString* product = base::mac::ObjCCast<NSString>([outer_bundle
++            objectForInfoDictionaryKey:base::mac::CFToNSCast(
++                kCFBundleNameKey)]);
++        process_annotations["product"] =
++            base::SysNSStringToUTF8(product).append("_Mac");
++      } else {
++        process_annotations["product"] = product_name;
++      }
+ 
+ #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+       // Empty means stable.
+@@ -58,12 +74,16 @@ std::map<std::string, std::string> GetProcessSimpleAnnotations() {
+         process_annotations["channel"] = "";
+       }
+ 
+-      NSString* version =
+-          base::mac::ObjCCast<NSString>([base::mac::FrameworkBundle()
+-              objectForInfoDictionaryKey:@"CFBundleShortVersionString"]);
+-      process_annotations["ver"] = base::SysNSStringToUTF8(version);
++      if (strlen(product_version) == 0) {
++        NSString* version =
++            base::mac::ObjCCast<NSString>([base::mac::FrameworkBundle()
++                objectForInfoDictionaryKey:@"CFBundleShortVersionString"]);
++        process_annotations["version"] = base::SysNSStringToUTF8(version);
++      } else {
++        process_annotations["version"] = product_version;
++      }
+ 
+-      process_annotations["plat"] = std::string("OS X");
++      process_annotations["platform"] = std::string("macos");
+     }  // @autoreleasepool
+     return process_annotations;
+   }();
+@@ -123,10 +143,10 @@ base::FilePath PlatformCrashpadInitialization(
+ 
+   if (initial_client) {
+     @autoreleasepool {
+-      base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
+-      base::FilePath handler_path =
+-          framework_bundle_path.Append("Helpers").Append(
+-              "chrome_crashpad_handler");
++      // Use the same subprocess helper exe.
++      base::FilePath handler_path;
++      base::PathService::Get(content::CHILD_PROCESS_EXE, &handler_path);
++      DCHECK(!handler_path.empty());
+ 
+       // Is there a way to recover if this fails?
+       CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
+@@ -138,7 +158,7 @@ base::FilePath PlatformCrashpadInitialization(
+       // crash server won't have symbols for any other build types.
+       std::string url = "https://clients2.google.com/cr/report";
+ #else
+-      std::string url;
++      std::string url = crash_reporter_client->GetCrashServerURL();
+ #endif
+ 
+       std::vector<std::string> arguments;
+@@ -161,6 +181,12 @@ base::FilePath PlatformCrashpadInitialization(
+             "--reset-own-crash-exception-port-to-system-default");
+       }
+ 
++      // Since we're using the same subprocess helper exe we must specify the
++      // process type.
++      arguments.push_back(std::string("--type=") + switches::kCrashpadHandler);
++
++      crash_reporter_client->GetCrashOptionalArguments(&arguments);
++
+       bool result = GetCrashpadClient().StartHandler(
+           handler_path, database_path, metrics_path, url,
+           GetProcessSimpleAnnotations(), arguments, true, false);
+diff --git components/crash/core/app/crashpad_win.cc components/crash/core/app/crashpad_win.cc
+index 669f5bea844d..734163c0aad1 100644
+--- components/crash/core/app/crashpad_win.cc
++++ components/crash/core/app/crashpad_win.cc
+@@ -36,8 +36,8 @@ void GetPlatformCrashpadAnnotations(
+   base::string16 product_name, version, special_build, channel_name;
+   crash_reporter_client->GetProductNameAndVersion(
+       exe_file, &product_name, &version, &special_build, &channel_name);
+-  (*annotations)["prod"] = base::UTF16ToUTF8(product_name);
+-  (*annotations)["ver"] = base::UTF16ToUTF8(version);
++  (*annotations)["product"] = base::UTF16ToUTF8(product_name);
++  (*annotations)["version"] = base::UTF16ToUTF8(version);
+ #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+   // Empty means stable.
+   const bool allow_empty_channel = true;
+@@ -49,9 +49,9 @@ void GetPlatformCrashpadAnnotations(
+   if (!special_build.empty())
+     (*annotations)["special"] = base::UTF16ToUTF8(special_build);
+ #if defined(ARCH_CPU_X86)
+-  (*annotations)["plat"] = std::string("Win32");
++  (*annotations)["platform"] = std::string("win32");
+ #elif defined(ARCH_CPU_X86_64)
+-  (*annotations)["plat"] = std::string("Win64");
++  (*annotations)["platform"] = std::string("win64");
+ #endif
+ }
+ 
+@@ -66,7 +66,9 @@ base::FilePath PlatformCrashpadInitialization(
+   base::FilePath metrics_path;  // Only valid in the browser process.
+ 
+   const char kPipeNameVar[] = "CHROME_CRASHPAD_PIPE_NAME";
++#if defined(GOOGLE_CHROME_BUILD)
+   const char kServerUrlVar[] = "CHROME_CRASHPAD_SERVER_URL";
++#endif
+   std::unique_ptr<base::Environment> env(base::Environment::Create());
+ 
+   CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
+@@ -87,13 +89,13 @@ base::FilePath PlatformCrashpadInitialization(
+ 
+ #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+     std::string url = "https://clients2.google.com/cr/report";
+-#else
+-    std::string url;
+-#endif
+ 
+     // Allow the crash server to be overridden for testing. If the variable
+     // isn't present in the environment then the default URL will remain.
+     env->GetVar(kServerUrlVar, &url);
++#else
++    std::string url = crash_reporter_client->GetCrashServerURL();
++#endif
+ 
+     base::FilePath exe_file(exe_path);
+     if (exe_file.empty()) {
+@@ -104,13 +106,14 @@ base::FilePath PlatformCrashpadInitialization(
+       exe_file = base::FilePath(exe_file_path);
+     }
+ 
+-    // If the handler is embedded in the binary (e.g. chrome, setup), we
+-    // reinvoke it with --type=crashpad-handler. Otherwise, we use the
+-    // standalone crashpad_handler.exe (for tests, etc.).
+     std::vector<std::string> start_arguments(initial_arguments);
++
++    // Always add --type=crashpad-handler because the value is expected by
++    // CefExecuteProcess.
++    start_arguments.push_back(
++        std::string("--type=") + switches::kCrashpadHandler);
++
+     if (embedded_handler) {
+-      start_arguments.push_back(std::string("--type=") +
+-                                switches::kCrashpadHandler);
+       if (!user_data_dir.empty()) {
+         start_arguments.push_back(std::string("--user-data-dir=") +
+                                   user_data_dir);
+@@ -121,9 +124,12 @@ base::FilePath PlatformCrashpadInitialization(
+       start_arguments.push_back("/prefetch:7");
+     } else {
+       base::FilePath exe_dir = exe_file.DirName();
+-      exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe"));
++      exe_file = base::FilePath(
++          crash_reporter_client->GetCrashExternalHandler(exe_dir.value()));
+     }
+ 
++    crash_reporter_client->GetCrashOptionalArguments(&start_arguments);
++
+     std::vector<std::string> arguments(start_arguments);
+ 
+     if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
diff --git a/src/patch/patches/crashpad_tp_1995.patch b/src/patch/patches/crashpad_tp_1995.patch
new file mode 100644
index 0000000..1ece692
--- /dev/null
+++ b/src/patch/patches/crashpad_tp_1995.patch
@@ -0,0 +1,358 @@
+diff --git third_party/crashpad/crashpad/client/prune_crash_reports.cc third_party/crashpad/crashpad/client/prune_crash_reports.cc
+index 8eed18d4944c..3e1fb7ac3ba5 100644
+--- third_party/crashpad/crashpad/client/prune_crash_reports.cc
++++ third_party/crashpad/crashpad/client/prune_crash_reports.cc
+@@ -72,13 +72,19 @@ size_t PruneCrashReportDatabase(CrashReportDatabase* database,
+ }
+ 
+ // static
+-std::unique_ptr<PruneCondition> PruneCondition::GetDefault() {
++std::unique_ptr<PruneCondition> PruneCondition::GetDefault(
++    int max_size_in_mb,
++    int max_age_in_days) {
+   // DatabaseSizePruneCondition must be the LHS so that it is always evaluated,
+   // due to the short-circuting behavior of BinaryPruneCondition.
++  if (max_size_in_mb <= 0)
++    max_size_in_mb = 128;
++  if (max_age_in_days <= 0)
++    max_age_in_days = 365;
+   return std::make_unique<BinaryPruneCondition>(
+       BinaryPruneCondition::OR,
+-      new DatabaseSizePruneCondition(1024 * 128),
+-      new AgePruneCondition(365));
++      new DatabaseSizePruneCondition(1024 * max_size_in_mb),
++      new AgePruneCondition(max_age_in_days));
+ }
+ 
+ static const time_t kSecondsInDay = 60 * 60 * 24;
+diff --git third_party/crashpad/crashpad/client/prune_crash_reports.h third_party/crashpad/crashpad/client/prune_crash_reports.h
+index 07a70980f12a..ddf7f17325fe 100644
+--- third_party/crashpad/crashpad/client/prune_crash_reports.h
++++ third_party/crashpad/crashpad/client/prune_crash_reports.h
+@@ -59,7 +59,8 @@ class PruneCondition {
+   //! of 128 MB.
+   //!
+   //! \return A PruneCondition for use with PruneCrashReportDatabase().
+-  static std::unique_ptr<PruneCondition> GetDefault();
++  static std::unique_ptr<PruneCondition> GetDefault(int max_size_in_mb,
++                                                    int max_age_in_days);
+ 
+   virtual ~PruneCondition() {}
+ 
+diff --git third_party/crashpad/crashpad/client/settings.cc third_party/crashpad/crashpad/client/settings.cc
+index db9dface43f8..e5bdfebf8403 100644
+--- third_party/crashpad/crashpad/client/settings.cc
++++ third_party/crashpad/crashpad/client/settings.cc
+@@ -86,7 +86,7 @@ void ScopedLockedFileHandleTraits::Free(FileHandle handle) {
+ 
+ struct Settings::Data {
+   static const uint32_t kSettingsMagic = 'CPds';
+-  static const uint32_t kSettingsVersion = 1;
++  static const uint32_t kSettingsVersion = 2;
+ 
+   enum Options : uint32_t {
+     kUploadsEnabled = 1 << 0,
+@@ -97,6 +97,9 @@ struct Settings::Data {
+            options(0),
+            padding_0(0),
+            last_upload_attempt_time(0),
++           next_upload_attempt_time(0),
++           backoff_step(0),
++           padding_1(0),
+            client_id() {}
+ 
+   uint32_t magic;
+@@ -104,6 +107,9 @@ struct Settings::Data {
+   uint32_t options;
+   uint32_t padding_0;
+   int64_t last_upload_attempt_time;  // time_t
++  int64_t next_upload_attempt_time;  // time_t
++  uint32_t backoff_step;
++  uint32_t padding_1;
+   UUID client_id;
+ };
+ 
+@@ -187,6 +193,56 @@ bool Settings::SetLastUploadAttemptTime(time_t time) {
+   return WriteSettings(handle.get(), settings);
+ }
+ 
++bool Settings::GetNextUploadAttemptTime(time_t* time) {
++  DCHECK(initialized_.is_valid());
++
++  Data settings;
++  if (!OpenAndReadSettings(&settings))
++    return false;
++
++  *time = InRangeCast<time_t>(settings.next_upload_attempt_time,
++                              std::numeric_limits<time_t>::max());
++  return true;
++}
++
++bool Settings::SetNextUploadAttemptTime(time_t time) {
++  DCHECK(initialized_.is_valid());
++
++  Data settings;
++  ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
++  if (!handle.is_valid())
++    return false;
++
++  settings.next_upload_attempt_time = InRangeCast<int64_t>(time, 0);
++
++  return WriteSettings(handle.get(), settings);
++}
++
++bool Settings::GetBackoffStep(int* step) {
++  DCHECK(initialized_.is_valid());
++
++  Data settings;
++  if (!OpenAndReadSettings(&settings))
++    return false;
++
++  *step = InRangeCast<int>(settings.backoff_step,
++                           std::numeric_limits<int>::max());
++  return true;
++}
++
++bool Settings::SetBackoffStep(int step) {
++  DCHECK(initialized_.is_valid());
++
++  Data settings;
++  ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
++  if (!handle.is_valid())
++    return false;
++
++  settings.backoff_step = InRangeCast<uint32_t>(step, 0);
++
++  return WriteSettings(handle.get(), settings);
++}
++
+ // static
+ Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle(
+     FileHandle file,
+diff --git third_party/crashpad/crashpad/client/settings.h third_party/crashpad/crashpad/client/settings.h
+index 5761c6b965b5..aee4e6c96033 100644
+--- third_party/crashpad/crashpad/client/settings.h
++++ third_party/crashpad/crashpad/client/settings.h
+@@ -115,6 +115,11 @@ class Settings {
+   //!     error logged.
+   bool SetLastUploadAttemptTime(time_t time);
+ 
++  bool GetNextUploadAttemptTime(time_t* time);
++  bool SetNextUploadAttemptTime(time_t time);
++  bool GetBackoffStep(int* step);
++  bool SetBackoffStep(int step);
++
+  private:
+   struct Data;
+ 
+diff --git third_party/crashpad/crashpad/handler/BUILD.gn third_party/crashpad/crashpad/handler/BUILD.gn
+index ff9f5d1f2c47..c7935c4d65a7 100644
+--- third_party/crashpad/crashpad/handler/BUILD.gn
++++ third_party/crashpad/crashpad/handler/BUILD.gn
+@@ -12,6 +12,7 @@
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ 
++import("//cef/libcef/features/features.gni")
+ import("../build/crashpad_buildconfig.gni")
+ 
+ static_library("handler") {
+@@ -74,6 +75,17 @@ static_library("handler") {
+     ]
+   }
+ 
++  if (enable_cef) {
++    sources += [
++      "//cef/libcef/common/cef_crash_report_upload_thread.cc",
++      "//cef/libcef/common/cef_crash_report_upload_thread.h",
++      "//cef/libcef/common/cef_crash_report_utils.cc",
++      "//cef/libcef/common/cef_crash_report_utils.h",
++    ]
++
++    configs += [ "//cef/libcef/features:config" ]
++  }
++
+   public_configs = [ "..:crashpad_config" ]
+ 
+   public_deps = [
+@@ -86,6 +98,7 @@ static_library("handler") {
+     "../minidump",
+     "../snapshot",
+     "../tools:tool_support",
++    "//cef/libcef/features",
+   ]
+ 
+   if (crashpad_is_win) {
+diff --git third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
+index e144bddc67d1..f3d727d0b247 100644
+--- third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
++++ third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
+@@ -262,6 +262,8 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
+   if (minidump_process_snapshot.Initialize(reader)) {
+     parameters =
+         BreakpadHTTPFormParametersFromMinidump(&minidump_process_snapshot);
++    if (!parameters.empty())
++      parameters = FilterParameters(parameters);
+   }
+ 
+   if (!reader->SeekSet(start_offset)) {
+diff --git third_party/crashpad/crashpad/handler/crash_report_upload_thread.h third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
+index 2ec1147d2620..8ff9a72e0bd7 100644
+--- third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
++++ third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
+@@ -15,6 +15,7 @@
+ #ifndef CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_
+ #define CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_
+ 
++#include <map>
+ #include <memory>
+ #include <string>
+ 
+@@ -103,7 +104,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate,
+   //! It is expected to only be called from the same thread that called Start().
+   void Stop() override;
+ 
+- private:
++ protected:
+   //! \brief The result code from UploadReport().
+   enum class UploadResult {
+     //! \brief The crash report was uploaded successfully.
+@@ -131,7 +132,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate,
+   //! object was constructed with \a watch_pending_reports, it will also scan
+   //! the crash report database for other pending reports, and process those as
+   //! well.
+-  void ProcessPendingReports();
++  virtual void ProcessPendingReports();
+ 
+   //! \brief Processes a single pending report from the database.
+   //!
+@@ -145,7 +146,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate,
+   //! remain in the “pending” state. If the upload fails and no more retries are
+   //! desired, or report upload is disabled, it will be marked as “completed” in
+   //! the database without ever having been uploaded.
+-  void ProcessPendingReport(const CrashReportDatabase::Report& report);
++  virtual void ProcessPendingReport(const CrashReportDatabase::Report& report);
+ 
+   //! \brief Attempts to upload a crash report.
+   //!
+@@ -162,6 +163,11 @@ class CrashReportUploadThread : public WorkerThread::Delegate,
+   UploadResult UploadReport(const CrashReportDatabase::UploadReport* report,
+                             std::string* response_body);
+ 
++  using ParameterMap = std::map<std::string, std::string>;
++  virtual ParameterMap FilterParameters(const ParameterMap& parameters) {
++    return parameters;
++  }
++
+   // WorkerThread::Delegate:
+   //! \brief Calls ProcessPendingReports() in response to ReportPending() having
+   //!     been called on any thread, as well as periodically on a timer.
+diff --git third_party/crashpad/crashpad/handler/handler_main.cc third_party/crashpad/crashpad/handler/handler_main.cc
+index e0a262cd1f38..2949e3ac1739 100644
+--- third_party/crashpad/crashpad/handler/handler_main.cc
++++ third_party/crashpad/crashpad/handler/handler_main.cc
+@@ -36,8 +36,10 @@
+ #include "base/scoped_generic.h"
+ #include "base/strings/string_number_conversions.h"
+ #include "base/strings/stringprintf.h"
++#include "base/strings/string_number_conversions.h"
+ #include "base/strings/utf_string_conversions.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "client/crash_report_database.h"
+ #include "client/crashpad_client.h"
+ #include "client/crashpad_info.h"
+@@ -100,6 +102,10 @@
+ #include "handler/linux/exception_handler_server.h"
+ #endif  // OS_MACOSX
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/common/cef_crash_report_upload_thread.h"
++#endif
++
+ namespace crashpad {
+ 
+ namespace {
+@@ -215,6 +221,9 @@ struct Options {
+   bool periodic_tasks;
+   bool rate_limit;
+   bool upload_gzip;
++  int max_uploads;
++  int max_database_size;
++  int max_database_age;
+ #if defined(OS_CHROMEOS)
+   bool use_cros_crash_reporter = false;
+   base::FilePath minidump_dir_for_tests;
+@@ -577,6 +586,9 @@ int HandlerMain(int argc,
+     kOptionTraceParentWithException,
+ #endif
+     kOptionURL,
++    kOptionMaxUploads,
++    kOptionMaxDatabaseSize,
++    kOptionMaxDatabaseAge,
+ #if defined(OS_CHROMEOS)
+     kOptionUseCrosCrashReporter,
+     kOptionMinidumpDirForTests,
+@@ -675,6 +687,9 @@ int HandlerMain(int argc,
+ #endif  // OS_ANDROID
+     {"help", no_argument, nullptr, kOptionHelp},
+     {"version", no_argument, nullptr, kOptionVersion},
++    {"max-uploads", required_argument, nullptr, kOptionMaxUploads},
++    {"max-db-size", required_argument, nullptr, kOptionMaxDatabaseSize},
++    {"max-db-age", required_argument, nullptr, kOptionMaxDatabaseAge},
+     {nullptr, 0, nullptr, 0},
+   };
+ 
+@@ -823,6 +838,27 @@ int HandlerMain(int argc,
+         options.url = optarg;
+         break;
+       }
++      case kOptionMaxUploads: {
++        if (base::StringToInt(optarg, &options.max_uploads)) {
++          if (options.max_uploads < 0)
++            options.max_uploads = 0;
++        }
++        break;
++      }
++      case kOptionMaxDatabaseSize: {
++        if (base::StringToInt(optarg, &options.max_database_size)) {
++          if (options.max_database_size < 0)
++            options.max_database_size = 0;
++        }
++        break;
++      }
++      case kOptionMaxDatabaseAge: {
++        if (base::StringToInt(optarg, &options.max_database_age)) {
++          if (options.max_database_age < 0)
++            options.max_database_age = 0;
++        }
++        break;
++      }
+ #if defined(OS_CHROMEOS)
+       case kOptionUseCrosCrashReporter: {
+         options.use_cros_crash_reporter = true;
+@@ -972,8 +1008,14 @@ int HandlerMain(int argc,
+     upload_thread_options.upload_gzip = options.upload_gzip;
+     upload_thread_options.watch_pending_reports = options.periodic_tasks;
+ 
++#if BUILDFLAG(ENABLE_CEF)
++    upload_thread.Reset(new CefCrashReportUploadThread(
++        database.get(), options.url, upload_thread_options,
++        options.max_uploads));
++#else
+     upload_thread.Reset(new CrashReportUploadThread(
+         database.get(), options.url, upload_thread_options));
++#endif
+     upload_thread.Get()->Start();
+   }
+ 
+@@ -1043,7 +1085,8 @@ int HandlerMain(int argc,
+   ScopedStoppable prune_thread;
+   if (options.periodic_tasks) {
+     prune_thread.Reset(new PruneCrashReportThread(
+-        database.get(), PruneCondition::GetDefault()));
++        database.get(), PruneCondition::GetDefault(options.max_database_size,
++                                                   options.max_database_age)));
+     prune_thread.Get()->Start();
+   }
+ 
diff --git a/src/patch/patches/extensions_1947.patch b/src/patch/patches/extensions_1947.patch
new file mode 100644
index 0000000..171e675
--- /dev/null
+++ b/src/patch/patches/extensions_1947.patch
@@ -0,0 +1,184 @@
+diff --git chrome/browser/extensions/api/streams_private/streams_private_api.cc chrome/browser/extensions/api/streams_private/streams_private_api.cc
+index bb4af725af9f..a1389be84f6e 100644
+--- chrome/browser/extensions/api/streams_private/streams_private_api.cc
++++ chrome/browser/extensions/api/streams_private/streams_private_api.cc
+@@ -6,6 +6,7 @@
+ 
+ #include <utility>
+ 
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/extensions/extension_tab_util.h"
+ #include "chrome/browser/prerender/prerender_contents.h"
+ #include "components/sessions/core/session_id.h"
+@@ -41,6 +42,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
+   if (!web_contents)
+     return;
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   // If the request was for a prerender, abort the prerender and do not
+   // continue. This is because plugins cancel prerender, see
+   // http://crbug.com/343590.
+@@ -50,6 +52,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
+     prerender_contents->Destroy(prerender::FINAL_STATUS_DOWNLOAD);
+     return;
+   }
++#endif  // !BUILDFLAG(ENABLE_CEF)
+ 
+   auto* browser_context = web_contents->GetBrowserContext();
+ 
+diff --git extensions/browser/extension_host.cc extensions/browser/extension_host.cc
+index 01fbe50e48a9..3977d27e813d 100644
+--- extensions/browser/extension_host.cc
++++ extensions/browser/extension_host.cc
+@@ -66,11 +66,12 @@ ExtensionHost::ExtensionHost(const Extension* extension,
+   DCHECK(host_type == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE ||
+          host_type == VIEW_TYPE_EXTENSION_DIALOG ||
+          host_type == VIEW_TYPE_EXTENSION_POPUP);
+-  host_contents_ = WebContents::Create(
+-      WebContents::CreateParams(browser_context_, site_instance)),
+-  content::WebContentsObserver::Observe(host_contents_.get());
++  host_contents_owned_ = WebContents::Create(
++      WebContents::CreateParams(browser_context_, site_instance));
++  host_contents_ = host_contents_owned_.get();
++  content::WebContentsObserver::Observe(host_contents_);
+   host_contents_->SetDelegate(this);
+-  SetViewType(host_contents_.get(), host_type);
++  SetViewType(host_contents_, host_type);
+ 
+   render_view_host_ = host_contents_->GetRenderViewHost();
+ 
+@@ -85,6 +86,48 @@ ExtensionHost::ExtensionHost(const Extension* extension,
+       dispatcher()->set_delegate(this);
+ }
+ 
++ExtensionHost::ExtensionHost(ExtensionHostDelegate* delegate,
++                             const Extension* extension,
++                             content::BrowserContext* browser_context,
++                             content::WebContents* host_contents,
++                             const GURL& url,
++                             ViewType host_type)
++    : delegate_(delegate),
++      extension_(extension),
++      extension_id_(extension->id()),
++      browser_context_(browser_context),
++      host_contents_(host_contents),
++      render_view_host_(nullptr),
++      is_render_view_creation_pending_(false),
++      has_loaded_once_(false),
++      document_element_available_(false),
++      initial_url_(url),
++      extension_host_type_(host_type) {
++  DCHECK(delegate);
++  DCHECK(browser_context);
++  DCHECK(host_contents);
++
++  // Not used for panels, see PanelHost.
++  DCHECK(host_type == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE ||
++         host_type == VIEW_TYPE_EXTENSION_DIALOG ||
++         host_type == VIEW_TYPE_EXTENSION_POPUP);
++
++  content::WebContentsObserver::Observe(host_contents_);
++  SetViewType(host_contents_, host_type);
++
++  render_view_host_ = host_contents_->GetRenderViewHost();
++
++  // Listen for when an extension is unloaded from the same profile, as it may
++  // be the same extension that this points to.
++  ExtensionRegistry::Get(browser_context_)->AddObserver(this);
++
++  // Set up web contents observers and pref observers.
++  delegate_->OnExtensionHostCreated(host_contents_);
++
++  ExtensionWebContentsObserver::GetForWebContents(host_contents_)->
++      dispatcher()->set_delegate(this);
++}
++
+ ExtensionHost::~ExtensionHost() {
+   ExtensionRegistry::Get(browser_context_)->RemoveObserver(this);
+ 
+diff --git extensions/browser/extension_host.h extensions/browser/extension_host.h
+index b5d2c7652cae..2e11775e5db3 100644
+--- extensions/browser/extension_host.h
++++ extensions/browser/extension_host.h
+@@ -52,13 +52,19 @@ class ExtensionHost : public DeferredStartRenderHost,
+   ExtensionHost(const Extension* extension,
+                 content::SiteInstance* site_instance,
+                 const GURL& url, ViewType host_type);
++  ExtensionHost(ExtensionHostDelegate* delegate,
++                const Extension* extension,
++                content::BrowserContext* browser_context,
++                content::WebContents* host_contents,
++                const GURL& url,
++                ViewType host_type);
+   ~ExtensionHost() override;
+ 
+   // This may be null if the extension has been or is being unloaded.
+   const Extension* extension() const { return extension_; }
+ 
+   const std::string& extension_id() const { return extension_id_; }
+-  content::WebContents* host_contents() const { return host_contents_.get(); }
++  content::WebContents* host_contents() const { return host_contents_; }
+   content::RenderViewHost* render_view_host() const;
+   content::RenderProcessHost* render_process_host() const;
+   bool has_loaded_once() const { return has_loaded_once_; }
+@@ -176,7 +182,8 @@ class ExtensionHost : public DeferredStartRenderHost,
+   content::BrowserContext* browser_context_;
+ 
+   // The host for our HTML content.
+-  std::unique_ptr<content::WebContents> host_contents_;
++  std::unique_ptr<content::WebContents> host_contents_owned_;
++  content::WebContents* host_contents_;
+ 
+   // A weak pointer to the current or pending RenderViewHost. We don't access
+   // this through the host_contents because we want to deal with the pending
+diff --git extensions/browser/extensions_browser_client.h extensions/browser/extensions_browser_client.h
+index c2d64706c1db..4d5c75dd035f 100644
+--- extensions/browser/extensions_browser_client.h
++++ extensions/browser/extensions_browser_client.h
+@@ -56,6 +56,7 @@ class ComponentExtensionResourceManager;
+ class Extension;
+ class ExtensionCache;
+ class ExtensionError;
++class ExtensionHost;
+ class ExtensionHostDelegate;
+ class ExtensionSet;
+ class ExtensionSystem;
+@@ -195,6 +196,14 @@ class ExtensionsBrowserClient {
+   virtual std::unique_ptr<ExtensionHostDelegate>
+   CreateExtensionHostDelegate() = 0;
+ 
++  // CEF creates a custom ExtensionHost for background pages. If the return
++  // value is true and |host| is NULL then fail the background host creation.
++  virtual bool CreateBackgroundExtensionHost(
++      const Extension* extension,
++      content::BrowserContext* browser_context,
++      const GURL& url,
++      ExtensionHost** host) { return false; }
++
+   // Returns true if the client version has updated since the last run. Called
+   // once each time the extensions system is loaded per browser_context. The
+   // implementation may wish to use the BrowserContext to record the current
+diff --git extensions/browser/process_manager.cc extensions/browser/process_manager.cc
+index bc06d55da961..64f513b54fe3 100644
+--- extensions/browser/process_manager.cc
++++ extensions/browser/process_manager.cc
+@@ -386,9 +386,16 @@ bool ProcessManager::CreateBackgroundHost(const Extension* extension,
+     return true;  // TODO(kalman): return false here? It might break things...
+ 
+   DVLOG(1) << "CreateBackgroundHost " << extension->id();
+-  ExtensionHost* host =
+-      new ExtensionHost(extension, GetSiteInstanceForURL(url).get(), url,
+-                        VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
++  ExtensionHost* host = nullptr;
++  if (ExtensionsBrowserClient::Get()->CreateBackgroundExtensionHost(
++          extension, browser_context_, url, &host) && !host) {
++    // Explicitly fail if the client can't create the host.
++    return false;
++  }
++  if (!host) {
++    host = new ExtensionHost(extension, GetSiteInstanceForURL(url).get(), url,
++                             VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
++  }
+   host->CreateRenderViewSoon();
+   OnBackgroundHostCreated(host);
+   return true;
diff --git a/src/patch/patches/font_family_cache_1501.patch b/src/patch/patches/font_family_cache_1501.patch
new file mode 100644
index 0000000..ba0ff03
--- /dev/null
+++ b/src/patch/patches/font_family_cache_1501.patch
@@ -0,0 +1,13 @@
+diff --git chrome/browser/font_family_cache.h chrome/browser/font_family_cache.h
+index a99372e6744c..ce40a8448868 100644
+--- chrome/browser/font_family_cache.h
++++ chrome/browser/font_family_cache.h
+@@ -19,6 +19,8 @@ class Profile;
+ 
+ FORWARD_DECLARE_TEST(FontFamilyCacheTest, Caching);
+ 
++extern const char kFontFamilyCacheKey[];
++
+ // Caches font family preferences associated with a PrefService. This class
+ // relies on the assumption that each concatenation of map_name + '.' + script
+ // is a unique string. It also relies on the assumption that the (const char*)
diff --git a/src/patch/patches/gn_config.patch b/src/patch/patches/gn_config.patch
new file mode 100644
index 0000000..4ef38c3
--- /dev/null
+++ b/src/patch/patches/gn_config.patch
@@ -0,0 +1,83 @@
+diff --git .gn .gn
+index c7296bb80f62..a64c7c42720a 100644
+--- .gn
++++ .gn
+@@ -642,6 +642,8 @@ exec_script_whitelist =
+ 
+       "//chrome/android/webapk/shell_apk/prepare_upload_dir/BUILD.gn",
+ 
++      "//cef/BUILD.gn",
++
+       # TODO(dgn): Layer violation but breaks the build otherwise, see
+       # https://crbug.com/474506.
+       "//clank/java/BUILD.gn",
+diff --git BUILD.gn BUILD.gn
+index ad58fc03312a..856991ba6096 100644
+--- BUILD.gn
++++ BUILD.gn
+@@ -222,6 +222,7 @@ group("gn_all") {
+ 
+   if (!is_ios && !is_fuchsia) {
+     deps += [
++      "//cef",
+       "//chrome/test:telemetry_perf_unittests",
+       "//chrome/test:unit_tests",
+       "//components:components_browsertests",
+diff --git build/config/win/visual_studio_version.gni build/config/win/visual_studio_version.gni
+index 982fbe8d3f0d..e757be4688f1 100644
+--- build/config/win/visual_studio_version.gni
++++ build/config/win/visual_studio_version.gni
+@@ -12,9 +12,8 @@ declare_args() {
+   # Currently always "2015".
+   visual_studio_version = ""
+ 
+-  # Directory of the Windows driver kit. If visual_studio_path is empty, this
+-  # will be auto-filled.
+-  wdk_path = ""
++  # Path to Visual Studio runtime libraries.
++  visual_studio_runtime_dirs = ""
+ 
+   # Full path to the Windows SDK, not including a backslash at the end.
+   # This value is the default location, override if you have a different
+@@ -28,12 +27,11 @@ if (visual_studio_path == "") {
+   visual_studio_path = toolchain_data.vs_path
+   windows_sdk_path = toolchain_data.sdk_path
+   visual_studio_version = toolchain_data.vs_version
+-  wdk_path = toolchain_data.wdk_dir
+   visual_studio_runtime_dirs = toolchain_data.runtime_dirs
+ } else {
+   assert(visual_studio_version != "",
+          "You must set the visual_studio_version if you set the path")
+-  assert(wdk_path != "",
+-         "You must set the wdk_path if you set the visual studio path")
+-  visual_studio_runtime_dirs = []
++  assert(visual_studio_runtime_dirs != "",
++         "You must set the visual_studio_runtime_dirs if you set the visual " +
++         "studio path")
+ }
+diff --git chrome/chrome_paks.gni chrome/chrome_paks.gni
+index ddfc4bd551d1..0e9157969deb 100644
+--- chrome/chrome_paks.gni
++++ chrome/chrome_paks.gni
+@@ -297,7 +297,7 @@ template("chrome_paks") {
+     }
+ 
+     input_locales = locales
+-    output_dir = "${invoker.output_dir}/locales"
++    output_dir = "${invoker.output_dir}/chrome/locales"
+ 
+     if (is_mac) {
+       output_locales = locales_as_mac_outputs
+diff --git chrome/installer/mini_installer/BUILD.gn chrome/installer/mini_installer/BUILD.gn
+index b2c99d4721dd..c8f870fa4a06 100644
+--- chrome/installer/mini_installer/BUILD.gn
++++ chrome/installer/mini_installer/BUILD.gn
+@@ -134,7 +134,7 @@ template("generate_mini_installer") {
+     inputs = [
+       "$chrome_dll_file",
+       "$root_out_dir/chrome.exe",
+-      "$root_out_dir/locales/en-US.pak",
++      "$root_out_dir/chrome/locales/en-US.pak",
+       "$root_out_dir/setup.exe",
+       "//chrome/tools/build/win/makecab.py",
+       release_file,
diff --git a/src/patch/patches/gritsettings.patch b/src/patch/patches/gritsettings.patch
new file mode 100644
index 0000000..1a91508
--- /dev/null
+++ b/src/patch/patches/gritsettings.patch
@@ -0,0 +1,18 @@
+diff --git tools/gritsettings/resource_ids.spec tools/gritsettings/resource_ids.spec
+index 730e3209ec95..39257ae244de 100644
+--- tools/gritsettings/resource_ids.spec
++++ tools/gritsettings/resource_ids.spec
+@@ -601,4 +601,13 @@
+   # Please read the header and find the right section above instead.
+ 
+   # Resource ids starting at 31000 are reserved for projects built on Chromium.
++
++  "cef/libcef/resources/cef_resources.grd": {
++    "META": {"align": 31500},
++    "includes": [31500],
++  },
++  "cef/libcef/resources/cef_strings.grd": {
++    "META": {"align": 32000},
++    "messages": [32000],
++  },
+ }
diff --git a/src/patch/patches/ime_1610.patch b/src/patch/patches/ime_1610.patch
new file mode 100644
index 0000000..8dcc653
--- /dev/null
+++ b/src/patch/patches/ime_1610.patch
@@ -0,0 +1,15 @@
+diff --git ui/base/ime/win/input_method_win_base.cc ui/base/ime/win/input_method_win_base.cc
+index 83850840d83b..3d5ffcc54f1e 100644
+--- ui/base/ime/win/input_method_win_base.cc
++++ ui/base/ime/win/input_method_win_base.cc
+@@ -263,8 +263,9 @@ bool InputMethodWinBase::IsWindowFocused(const TextInputClient* client) const {
+   // receiving keyboard input as long as it is an active window. This works well
+   // even when the |attached_window_handle| becomes active but has not received
+   // WM_FOCUS yet.
++  // With CEF |toplevel_window_handle_| may be a child window.
+   return toplevel_window_handle_ &&
+-         GetActiveWindow() == toplevel_window_handle_;
++         GetActiveWindow() == ::GetAncestor(toplevel_window_handle_, GA_ROOT);
+ }
+ 
+ LRESULT InputMethodWinBase::OnChar(HWND window_handle,
diff --git a/src/patch/patches/libxml_visibility.patch b/src/patch/patches/libxml_visibility.patch
new file mode 100644
index 0000000..dad5e02
--- /dev/null
+++ b/src/patch/patches/libxml_visibility.patch
@@ -0,0 +1,12 @@
+diff --git third_party/libxml/BUILD.gn third_party/libxml/BUILD.gn
+index 3fe4dfd2990b..64848dfd33d7 100644
+--- third_party/libxml/BUILD.gn
++++ third_party/libxml/BUILD.gn
+@@ -141,6 +141,7 @@ static_library("libxml") {
+     "//third_party/blink/renderer/*",
+     "//third_party/fontconfig",
+     "//third_party/libxslt",
++    "//cef:*",
+   ]
+   if (is_ios) {
+     foreach(tgt, ios_libxml_visibility_additions) {
diff --git a/src/patch/patches/linux_assets_path_1936.patch b/src/patch/patches/linux_assets_path_1936.patch
new file mode 100644
index 0000000..3fac280
--- /dev/null
+++ b/src/patch/patches/linux_assets_path_1936.patch
@@ -0,0 +1,48 @@
+diff --git content/browser/child_process_launcher_helper_linux.cc content/browser/child_process_launcher_helper_linux.cc
+index e53e60ef146f..9b6db4a69d41 100644
+--- content/browser/child_process_launcher_helper_linux.cc
++++ content/browser/child_process_launcher_helper_linux.cc
+@@ -164,7 +164,7 @@ void ChildProcessLauncherHelper::SetProcessPriorityOnLauncherThread(
+ base::File OpenFileToShare(const base::FilePath& path,
+                            base::MemoryMappedFile::Region* region) {
+   base::FilePath exe_dir;
+-  bool result = base::PathService::Get(base::BasePathKey::DIR_EXE, &exe_dir);
++  bool result = base::PathService::Get(base::BasePathKey::DIR_ASSETS, &exe_dir);
+   DCHECK(result);
+   base::File file(exe_dir.Append(path),
+                   base::File::FLAG_OPEN | base::File::FLAG_READ);
+diff --git sandbox/linux/suid/client/setuid_sandbox_host.cc sandbox/linux/suid/client/setuid_sandbox_host.cc
+index 0aaed76c1dda..517c3d8b5772 100644
+--- sandbox/linux/suid/client/setuid_sandbox_host.cc
++++ sandbox/linux/suid/client/setuid_sandbox_host.cc
+@@ -120,7 +120,7 @@ bool SetuidSandboxHost::IsDisabledViaEnvironment() {
+ base::FilePath SetuidSandboxHost::GetSandboxBinaryPath() {
+   base::FilePath sandbox_binary;
+   base::FilePath exe_dir;
+-  if (base::PathService::Get(base::DIR_EXE, &exe_dir)) {
++  if (base::PathService::Get(base::DIR_ASSETS, &exe_dir)) {
+     base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox");
+     if (base::PathExists(sandbox_candidate))
+       sandbox_binary = sandbox_candidate;
+diff --git ui/gl/init/gl_initializer_x11.cc ui/gl/init/gl_initializer_x11.cc
+index d9190a957ca2..50bb49eaf56c 100644
+--- ui/gl/init/gl_initializer_x11.cc
++++ ui/gl/init/gl_initializer_x11.cc
+@@ -86,7 +86,7 @@ bool InitializeStaticEGLInternal(GLImplementation implementation) {
+   if (implementation == kGLImplementationSwiftShaderGL) {
+ #if BUILDFLAG(ENABLE_SWIFTSHADER)
+     base::FilePath module_path;
+-    if (!base::PathService::Get(base::DIR_MODULE, &module_path))
++    if (!base::PathService::Get(base::DIR_ASSETS, &module_path))
+       return false;
+     module_path = module_path.Append("swiftshader/");
+ 
+@@ -97,7 +97,7 @@ bool InitializeStaticEGLInternal(GLImplementation implementation) {
+ #endif
+   } else if (implementation == kGLImplementationEGLANGLE) {
+     base::FilePath module_path;
+-    if (!base::PathService::Get(base::DIR_MODULE, &module_path))
++    if (!base::PathService::Get(base::DIR_ASSETS, &module_path))
+       return false;
+ 
+     glesv2_path = module_path.Append(kGLESv2ANGLELibraryName);
diff --git a/src/patch/patches/linux_chrome_views_1085806.patch b/src/patch/patches/linux_chrome_views_1085806.patch
new file mode 100644
index 0000000..32e2538
--- /dev/null
+++ b/src/patch/patches/linux_chrome_views_1085806.patch
@@ -0,0 +1,13 @@
+diff --git chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
+index f76fb26d4780..4cc511600b3b 100644
+--- chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
++++ chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
+@@ -48,7 +48,7 @@ ChromeBrowserMainExtraPartsViewsLinux::
+ }
+ 
+ void ChromeBrowserMainExtraPartsViewsLinux::ToolkitInitialized() {
+-#if defined(USE_X11)
++#if defined(USE_X11) && BUILDFLAG(USE_GTK)
+   // In Aura/X11, Gtk-based LinuxUI implementation is used, so we instantiate
+   // and inject the GtkUiDelegate before ChromeBrowserMainExtraPartsViewsLinux,
+   // so it can properly initialize GtkUi on its |ToolkitInitialized| override.
diff --git a/src/patch/patches/linux_poll_2466.patch b/src/patch/patches/linux_poll_2466.patch
new file mode 100644
index 0000000..1ffb92f
--- /dev/null
+++ b/src/patch/patches/linux_poll_2466.patch
@@ -0,0 +1,47 @@
+diff --git base/files/file_path_watcher_linux.cc base/files/file_path_watcher_linux.cc
+index 563e08067585..3200fa658457 100644
+--- base/files/file_path_watcher_linux.cc
++++ base/files/file_path_watcher_linux.cc
+@@ -5,6 +5,7 @@
+ #include "base/files/file_path_watcher.h"
+ 
+ #include <errno.h>
++#include <poll.h>
+ #include <stddef.h>
+ #include <string.h>
+ #include <sys/inotify.h>
+@@ -13,6 +14,7 @@
+ #include <unistd.h>
+ 
+ #include <algorithm>
++#include <array>
+ #include <fstream>
+ #include <map>
+ #include <memory>
+@@ -263,8 +265,10 @@ void InotifyReaderThreadDelegate::ThreadMain() {
+   PlatformThread::SetName("inotify_reader");
+ 
+   // Make sure the file descriptors are good for use with select().
+-  CHECK_LE(0, inotify_fd_);
+-  CHECK_GT(FD_SETSIZE, inotify_fd_);
++  std::array<pollfd, 1> fdarray
++  { {
++      { inotify_fd_, POLLIN, 0 }
++  } };
+ 
+   while (true) {
+     fd_set rfds;
+@@ -272,10 +276,9 @@ void InotifyReaderThreadDelegate::ThreadMain() {
+     FD_SET(inotify_fd_, &rfds);
+ 
+     // Wait until some inotify events are available.
+-    int select_result =
+-        HANDLE_EINTR(select(inotify_fd_ + 1, &rfds, nullptr, nullptr, nullptr));
+-    if (select_result < 0) {
+-      DPLOG(WARNING) << "select failed";
++    int poll_result = HANDLE_EINTR(poll(fdarray.data(), fdarray.size(), -1));
++    if (poll_result < 0) {
++      DPLOG(WARNING) << "poll failed";
+       return;
+     }
+ 
diff --git a/src/patch/patches/mac_event_observer_2539.patch b/src/patch/patches/mac_event_observer_2539.patch
new file mode 100644
index 0000000..a384086
--- /dev/null
+++ b/src/patch/patches/mac_event_observer_2539.patch
@@ -0,0 +1,22 @@
+diff --git content/browser/scheduler/responsiveness/native_event_observer_mac.mm content/browser/scheduler/responsiveness/native_event_observer_mac.mm
+index 7cb3238e97ed..ae800739b686 100644
+--- content/browser/scheduler/responsiveness/native_event_observer_mac.mm
++++ content/browser/scheduler/responsiveness/native_event_observer_mac.mm
+@@ -12,13 +12,15 @@ namespace content {
+ namespace responsiveness {
+ 
+ void NativeEventObserver::RegisterObserver() {
+-  DCHECK([NSApp conformsToProtocol:@protocol(NativeEventProcessor)]);
++  if (![NSApp conformsToProtocol:@protocol(NativeEventProcessor)])
++    return;
+   id<NativeEventProcessor> processor =
+       static_cast<id<NativeEventProcessor>>(NSApp);
+   [processor addNativeEventProcessorObserver:this];
+ }
+ void NativeEventObserver::DeregisterObserver() {
+-  DCHECK([NSApp conformsToProtocol:@protocol(NativeEventProcessor)]);
++  if (![NSApp conformsToProtocol:@protocol(NativeEventProcessor)])
++    return;
+   id<NativeEventProcessor> processor =
+       static_cast<id<NativeEventProcessor>>(NSApp);
+   [processor removeNativeEventProcessorObserver:this];
diff --git a/src/patch/patches/mac_fling_scheduler_2540.patch b/src/patch/patches/mac_fling_scheduler_2540.patch
new file mode 100644
index 0000000..8a7776a
--- /dev/null
+++ b/src/patch/patches/mac_fling_scheduler_2540.patch
@@ -0,0 +1,15 @@
+diff --git content/browser/renderer_host/input/fling_scheduler_mac.mm content/browser/renderer_host/input/fling_scheduler_mac.mm
+index f10c5d161dd1..92a751dd984e 100644
+--- content/browser/renderer_host/input/fling_scheduler_mac.mm
++++ content/browser/renderer_host/input/fling_scheduler_mac.mm
+@@ -26,6 +26,10 @@ ui::Compositor* FlingSchedulerMac::GetCompositor() {
+       return nullptr;
+   }
+ 
++  // For CEF this will always be false when running in OSR mode.
++  if (!view->GetNativeView())
++    return nullptr;
++
+   RenderWidgetHostViewMac* mac_view =
+       static_cast<RenderWidgetHostViewMac*>(view);
+   if (mac_view->BrowserCompositor())
diff --git a/src/patch/patches/mac_gpu.patch b/src/patch/patches/mac_gpu.patch
new file mode 100644
index 0000000..8123410
--- /dev/null
+++ b/src/patch/patches/mac_gpu.patch
@@ -0,0 +1,18 @@
+diff --git ui/gl/init/gl_initializer_mac.cc ui/gl/init/gl_initializer_mac.cc
+index 0deebafe135f..fdbd9a7ecf17 100644
+--- ui/gl/init/gl_initializer_mac.cc
++++ ui/gl/init/gl_initializer_mac.cc
+@@ -46,11 +46,8 @@ bool InitializeOneOffForSandbox() {
+   // GPU-related stuff is very slow without this, probably because
+   // the sandbox prevents loading graphics drivers or some such.
+   std::vector<CGLPixelFormatAttribute> attribs;
+-  if (GLContext::SwitchableGPUsSupported()) {
+-    // Avoid switching to the discrete GPU just for this pixel
+-    // format selection.
+-    attribs.push_back(kCGLPFAAllowOfflineRenderers);
+-  }
++  // Avoid switching to the discrete GPU just for this pixel format selection.
++  attribs.push_back(kCGLPFAAllowOfflineRenderers);
+   if (GetGLImplementation() == kGLImplementationAppleGL) {
+     attribs.push_back(kCGLPFARendererID);
+     attribs.push_back(
diff --git a/src/patch/patches/message_loop.patch b/src/patch/patches/message_loop.patch
new file mode 100644
index 0000000..dd2c938
--- /dev/null
+++ b/src/patch/patches/message_loop.patch
@@ -0,0 +1,78 @@
+diff --git base/message_loop/message_loop_current.cc base/message_loop/message_loop_current.cc
+index 7688ba3d7b0d..9acaaa0d5c7f 100644
+--- base/message_loop/message_loop_current.cc
++++ base/message_loop/message_loop_current.cc
+@@ -47,6 +47,8 @@ void MessageLoopCurrent::AddDestructionObserver(
+ 
+ void MessageLoopCurrent::RemoveDestructionObserver(
+     DestructionObserver* destruction_observer) {
++  if (!current_)
++    return;
+   DCHECK(current_->IsBoundToCurrentThread());
+   current_->RemoveDestructionObserver(destruction_observer);
+ }
+diff --git base/message_loop/message_loop_current.h base/message_loop/message_loop_current.h
+index 61b8c6fcc42d..d439b00a87bb 100644
+--- base/message_loop/message_loop_current.h
++++ base/message_loop/message_loop_current.h
+@@ -117,6 +117,12 @@ class BASE_EXPORT MessageLoopCurrent {
+   // posted tasks.
+   void SetAddQueueTimeToTasks(bool enable);
+ 
++#if defined(OS_WIN)
++  void set_os_modal_loop(bool os_modal_loop) { os_modal_loop_ = os_modal_loop; }
++
++  bool os_modal_loop() const { return os_modal_loop_; }
++#endif  // OS_WIN
++
+   // Enables or disables the recursive task processing. This happens in the case
+   // of recursive message loops. Some unwanted message loops may occur when
+   // using common controls or printer functions. By default, recursive task
+@@ -186,6 +192,13 @@ class BASE_EXPORT MessageLoopCurrent {
+   friend class web::WebTaskEnvironment;
+ 
+   sequence_manager::internal::SequenceManagerImpl* current_;
++
++#if defined(OS_WIN)
++ private:
++  // Should be set to true before calling Windows APIs like TrackPopupMenu, etc.
++  // which enter a modal message loop.
++  bool os_modal_loop_ = false;
++#endif
+ };
+ 
+ #if !defined(OS_NACL)
+diff --git base/message_loop/message_pump_win.cc base/message_loop/message_pump_win.cc
+index c50e34f6da1b..40dcc66ed82a 100644
+--- base/message_loop/message_pump_win.cc
++++ base/message_loop/message_pump_win.cc
+@@ -2,6 +2,7 @@
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+ 
++#include "base/message_loop/message_loop_current.h"
+ #include "base/message_loop/message_pump_win.h"
+ 
+ #include <algorithm>
+@@ -493,10 +494,18 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() {
+   // asynchronous to this thread!!
+ 
+   MSG msg;
+-  const bool have_message =
+-      ::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE;
++  bool have_message = false;
++  // We should not process all window messages if we are in the context of an
++  // OS modal loop, i.e. in the context of a windows API call like MessageBox.
++  // This is to ensure that these messages are peeked out by the OS modal loop.
++  if (MessageLoopCurrent::Get()->os_modal_loop()) {
++    // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
++    have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
++                   PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
++  } else {
++    have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE;
++  }
+ 
+-  // Expect no message or a message different than kMsgHaveWork.
+   DCHECK(!have_message || kMsgHaveWork != msg.message ||
+          msg.hwnd != message_window_.hwnd());
+ 
diff --git a/src/patch/patches/message_pump_mac_2495.patch b/src/patch/patches/message_pump_mac_2495.patch
new file mode 100644
index 0000000..cab8923
--- /dev/null
+++ b/src/patch/patches/message_pump_mac_2495.patch
@@ -0,0 +1,24 @@
+diff --git base/message_loop/message_pump_mac.mm base/message_loop/message_pump_mac.mm
+index bb9e6d30bedd..e5ef7077c9de 100644
+--- base/message_loop/message_pump_mac.mm
++++ base/message_loop/message_pump_mac.mm
+@@ -785,7 +785,8 @@ void MessagePumpUIApplication::Detach() {
+ #else
+ 
+ ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() {
+-  DCHECK(g_app_pump);
++  if (!g_app_pump)
++    return;
+   DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask());
+   // Pumping events in private runloop modes is known to interact badly with
+   // app modal windows like NSAlert.
+@@ -797,7 +798,8 @@ ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() {
+ }
+ 
+ ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() {
+-  DCHECK(g_app_pump);
++  if (!g_app_pump)
++    return;
+   g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask);
+   g_app_pump->SetTimerInvalidationAllowed(true);
+ }
diff --git a/src/patch/patches/mime_handler_view_guest_1565_2727.patch b/src/patch/patches/mime_handler_view_guest_1565_2727.patch
new file mode 100644
index 0000000..34fe80d
--- /dev/null
+++ b/src/patch/patches/mime_handler_view_guest_1565_2727.patch
@@ -0,0 +1,90 @@
+diff --git content/browser/web_contents/web_contents_view.h content/browser/web_contents/web_contents_view.h
+index c0cd19506973..956d80443c11 100644
+--- content/browser/web_contents/web_contents_view.h
++++ content/browser/web_contents/web_contents_view.h
+@@ -23,7 +23,7 @@ struct DropData;
+ // The WebContentsView is an interface that is implemented by the platform-
+ // dependent web contents views. The WebContents uses this interface to talk to
+ // them.
+-class WebContentsView {
++class CONTENT_EXPORT WebContentsView {
+  public:
+   virtual ~WebContentsView() {}
+ 
+diff --git extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+index 068b22336fac..a970f5be0428 100644
+--- extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
++++ extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+@@ -207,6 +207,8 @@ void MimeHandlerViewGuest::CreateWebContents(
+   WebContents::CreateParams params(browser_context(),
+                                    guest_site_instance.get());
+   params.guest_delegate = this;
++  if (delegate_)
++    delegate_->OverrideWebContentsCreateParams(&params);
+   // TODO(erikchen): Fix ownership semantics for guest views.
+   // https://crbug.com/832879.
+   std::move(callback).Run(
+@@ -217,6 +219,9 @@ void MimeHandlerViewGuest::CreateWebContents(
+ }
+ 
+ void MimeHandlerViewGuest::DidAttachToEmbedder() {
++  if (delegate_)
++    delegate_->OnGuestAttached();
++
+   web_contents()->GetController().LoadURL(
+       stream_->handler_url(), content::Referrer(),
+       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
+@@ -246,6 +251,11 @@ bool MimeHandlerViewGuest::ShouldDestroyOnDetach() const {
+   return true;
+ }
+ 
++void MimeHandlerViewGuest::WillDestroy() {
++  if (delegate_)
++    delegate_->OnGuestDetached();
++}
++
+ WebContents* MimeHandlerViewGuest::OpenURLFromTab(
+     WebContents* source,
+     const content::OpenURLParams& params) {
+diff --git extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
+index 242db17f8f3b..80e9a5663588 100644
+--- extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
++++ extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
+@@ -130,6 +130,7 @@ class MimeHandlerViewGuest
+   void EmbedderFullscreenToggled(bool entered_fullscreen) final;
+   bool ZoomPropagatesFromEmbedderToGuest() const final;
+   bool ShouldDestroyOnDetach() const final;
++  void WillDestroy() override;
+ 
+   // WebContentsDelegate implementation.
+   content::WebContents* OpenURLFromTab(
+diff --git extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h
+index 98689e261460..a1b08274f455 100644
+--- extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h
++++ extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h
+@@ -8,9 +8,9 @@
+ #include <string>
+ 
+ #include "base/macros.h"
++#include "content/public/browser/web_contents.h"
+ 
+ namespace content {
+-class WebContents;
+ struct ContextMenuParams;
+ }  // namespace content
+ 
+@@ -22,6 +22,14 @@ class MimeHandlerViewGuestDelegate {
+   MimeHandlerViewGuestDelegate() {}
+   virtual ~MimeHandlerViewGuestDelegate() {}
+ 
++  // Provides an opportunity to supply a custom view implementation.
++  virtual void OverrideWebContentsCreateParams(
++      content::WebContents::CreateParams* params) {}
++
++  // Called when a guest is attached or detached.
++  virtual void OnGuestAttached() {}
++  virtual void OnGuestDetached() {}
++
+   // Handles context menu, or returns false if unhandled.
+   virtual bool HandleContextMenu(content::WebContents* web_contents,
+                                  const content::ContextMenuParams& params);
diff --git a/src/patch/patches/osr_fling_2745.patch b/src/patch/patches/osr_fling_2745.patch
new file mode 100644
index 0000000..71f20a4
--- /dev/null
+++ b/src/patch/patches/osr_fling_2745.patch
@@ -0,0 +1,70 @@
+diff --git content/browser/renderer_host/input/fling_scheduler.cc content/browser/renderer_host/input/fling_scheduler.cc
+index c7a4f1f0c3d0..7e6e3b4e56d4 100644
+--- content/browser/renderer_host/input/fling_scheduler.cc
++++ content/browser/renderer_host/input/fling_scheduler.cc
+@@ -68,6 +68,9 @@ void FlingScheduler::ProgressFlingOnBeginFrameIfneeded(
+ }
+ 
+ ui::Compositor* FlingScheduler::GetCompositor() {
++  if (compositor_) {
++    return compositor_;
++  }
+ #if defined(USE_AURA)
+   if (host_->GetView() && host_->GetView()->GetNativeView() &&
+       host_->GetView()->GetNativeView()->GetHost() &&
+diff --git content/browser/renderer_host/input/fling_scheduler_base.h content/browser/renderer_host/input/fling_scheduler_base.h
+index cc4b13a7b9c6..84f3b9ed7cf4 100644
+--- content/browser/renderer_host/input/fling_scheduler_base.h
++++ content/browser/renderer_host/input/fling_scheduler_base.h
+@@ -7,12 +7,23 @@
+ 
+ #include "content/browser/renderer_host/input/fling_controller.h"
+ 
++namespace ui {
++class Compositor;
++}
++
+ namespace content {
+ 
+ class FlingSchedulerBase : public FlingControllerSchedulerClient {
+  public:
+   virtual void ProgressFlingOnBeginFrameIfneeded(
+       base::TimeTicks current_time) = 0;
++
++  void SetCompositor(ui::Compositor* compositor) {
++    compositor_ = compositor;
++  }
++
++protected:
++ ui::Compositor* compositor_ = nullptr;
+ };
+ 
+ }  // namespace content
+diff --git content/browser/renderer_host/render_widget_host_impl.cc content/browser/renderer_host/render_widget_host_impl.cc
+index fc06067480b0..4f313bdb1414 100644
+--- content/browser/renderer_host/render_widget_host_impl.cc
++++ content/browser/renderer_host/render_widget_host_impl.cc
+@@ -2751,6 +2751,11 @@ void RenderWidgetHostImpl::DidStartScrollingViewport() {
+     view_->set_is_currently_scrolling_viewport(true);
+ }
+ 
++void RenderWidgetHostImpl::SetCompositorForFlingScheduler(ui::Compositor* compositor)
++{
++  fling_scheduler_->SetCompositor(compositor);
++}
++
+ void RenderWidgetHostImpl::AddPendingUserActivation(
+     const WebInputEvent& event) {
+   if ((base::FeatureList::IsEnabled(
+diff --git content/browser/renderer_host/render_widget_host_impl.h content/browser/renderer_host/render_widget_host_impl.h
+index 013b2165038f..28d33768143f 100644
+--- content/browser/renderer_host/render_widget_host_impl.h
++++ content/browser/renderer_host/render_widget_host_impl.h
+@@ -731,6 +731,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl
+ 
+   void ProgressFlingIfNeeded(base::TimeTicks current_time);
+   void StopFling();
++  void SetCompositorForFlingScheduler(ui::Compositor* compositor);
+ 
+   // The RenderWidgetHostImpl will keep showing the old page (for a while) after
+   // navigation until the first frame of the new page arrives. This reduces
diff --git a/src/patch/patches/prefs_content_1161.patch b/src/patch/patches/prefs_content_1161.patch
new file mode 100644
index 0000000..81d5775
--- /dev/null
+++ b/src/patch/patches/prefs_content_1161.patch
@@ -0,0 +1,49 @@
+diff --git content/public/common/common_param_traits_macros.h content/public/common/common_param_traits_macros.h
+index 0e67ad3c7431..de0bb8e1a824 100644
+--- content/public/common/common_param_traits_macros.h
++++ content/public/common/common_param_traits_macros.h
+@@ -177,6 +177,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::WebPreferences)
+   IPC_STRUCT_TRAITS_MEMBER(main_frame_resizes_are_orientation_changes)
+   IPC_STRUCT_TRAITS_MEMBER(initialize_at_minimum_page_scale)
+   IPC_STRUCT_TRAITS_MEMBER(smart_insert_delete_enabled)
++  IPC_STRUCT_TRAITS_MEMBER(base_background_color)
+   IPC_STRUCT_TRAITS_MEMBER(cookie_enabled)
+   IPC_STRUCT_TRAITS_MEMBER(navigate_on_drag_drop)
+   IPC_STRUCT_TRAITS_MEMBER(spatial_navigation_enabled)
+diff --git content/public/common/web_preferences.cc content/public/common/web_preferences.cc
+index 13999e87c780..3494c1c762cc 100644
+--- content/public/common/web_preferences.cc
++++ content/public/common/web_preferences.cc
+@@ -165,6 +165,7 @@ WebPreferences::WebPreferences()
+       spatial_navigation_enabled(false),
+       caret_browsing_enabled(false),
+       navigate_on_drag_drop(true),
++      base_background_color(0xFFFFFFFF),  // Color::white
+       v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
+       record_whole_document(false),
+       cookie_enabled(true),
+diff --git content/public/common/web_preferences.h content/public/common/web_preferences.h
+index 6c6420ec70bf..256923855837 100644
+--- content/public/common/web_preferences.h
++++ content/public/common/web_preferences.h
+@@ -181,6 +181,7 @@ struct CONTENT_EXPORT WebPreferences {
+   bool spatial_navigation_enabled;
+   bool caret_browsing_enabled;
+   bool navigate_on_drag_drop;
++  uint32_t base_background_color;
+   blink::mojom::V8CacheOptions v8_cache_options;
+   bool record_whole_document;
+ 
+diff --git content/renderer/render_view_impl.cc content/renderer/render_view_impl.cc
+index f0cb4833442b..1f80fefaa124 100644
+--- content/renderer/render_view_impl.cc
++++ content/renderer/render_view_impl.cc
+@@ -971,6 +971,8 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs,
+ #endif
+ 
+   WebRuntimeFeatures::EnableTranslateService(prefs.translate_service_available);
++
++  web_view->SetBaseBackgroundColor(prefs.base_background_color);
+ }
+ 
+ /*static*/
diff --git a/src/patch/patches/print_preview_123.patch b/src/patch/patches/print_preview_123.patch
new file mode 100644
index 0000000..2712750
--- /dev/null
+++ b/src/patch/patches/print_preview_123.patch
@@ -0,0 +1,366 @@
+diff --git chrome/browser/download/download_prefs.cc chrome/browser/download/download_prefs.cc
+index 00d1c0fa2b00..45dcadc79fb2 100644
+--- chrome/browser/download/download_prefs.cc
++++ chrome/browser/download/download_prefs.cc
+@@ -22,6 +22,7 @@
+ #include "base/strings/sys_string_conversions.h"
+ #include "base/strings/utf_string_conversions.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/download/chrome_download_manager_delegate.h"
+ #include "chrome/browser/download/download_core_service_factory.h"
+ #include "chrome/browser/download/download_core_service_impl.h"
+@@ -53,6 +54,10 @@
+ #include "chrome/browser/ui/pdf/adobe_reader_info_win.h"
+ #endif
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/browser/browser_context.h"
++#endif
++
+ using content::BrowserContext;
+ using content::BrowserThread;
+ using content::DownloadManager;
+@@ -309,7 +314,11 @@ DownloadPrefs* DownloadPrefs::FromDownloadManager(
+ // static
+ DownloadPrefs* DownloadPrefs::FromBrowserContext(
+     content::BrowserContext* context) {
++#if !BUILDFLAG(ENABLE_CEF)
+   return FromDownloadManager(BrowserContext::GetDownloadManager(context));
++#else
++  return CefBrowserContext::GetForContext(context)->GetDownloadPrefs();
++#endif
+ }
+ 
+ bool DownloadPrefs::IsFromTrustedSource(const download::DownloadItem& item) {
+diff --git chrome/browser/printing/print_preview_dialog_controller.cc chrome/browser/printing/print_preview_dialog_controller.cc
+index 84818167bce1..cdbd3f7f6f27 100644
+--- chrome/browser/printing/print_preview_dialog_controller.cc
++++ chrome/browser/printing/print_preview_dialog_controller.cc
+@@ -16,6 +16,7 @@
+ #include "base/strings/utf_string_conversions.h"
+ #include "build/branding_buildflags.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/browser_process.h"
+ #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
+ #include "chrome/browser/printing/print_view_manager.h"
+@@ -451,8 +452,11 @@ WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
+   content::HostZoomMap::Get(preview_dialog->GetSiteInstance())
+       ->SetZoomLevelForHostAndScheme(print_url.scheme(), print_url.host(), 0);
+   PrintViewManager::CreateForWebContents(preview_dialog);
++
++#if !BUILDFLAG(ENABLE_CEF)
+   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
+       preview_dialog);
++#endif
+ 
+   // Add an entry to the map.
+   preview_dialog_map_[preview_dialog] = initiator;
+diff --git chrome/browser/resources/print_preview/ui/destination_dialog.html chrome/browser/resources/print_preview/ui/destination_dialog.html
+index 56a6a8d8ee67..5fecf7ad1780 100644
+--- chrome/browser/resources/print_preview/ui/destination_dialog.html
++++ chrome/browser/resources/print_preview/ui/destination_dialog.html
+@@ -141,9 +141,7 @@
+     </print-preview-provisional-destination-resolver>
+   </div>
+   <div slot="button-container">
+-    <cr-button on-click="onOpenSettingsPrintPage_">
+-      $i18n{manage}
+-    </cr-button>
++    <div></div>
+     <cr-button class="cancel-button" on-click="onCancelButtonClick_">
+       $i18n{cancel}
+     </cr-button>
+diff --git chrome/browser/resources/print_preview/ui/destination_select.html chrome/browser/resources/print_preview/ui/destination_select.html
+index c93947a7634c..c60cada63ef8 100644
+--- chrome/browser/resources/print_preview/ui/destination_select.html
++++ chrome/browser/resources/print_preview/ui/destination_select.html
+@@ -31,10 +31,6 @@
+           hidden$="[[pdfPrinterDisabled]]">
+     $i18n{printToPDF}
+   </option>
+-  <option value="[[getGoogleDriveDestinationKey_(activeUser)]]"
+-      hidden$="[[!driveDestinationReady]]">
+-    $i18n{printToGoogleDrive}
+-  </option>
+   <option value="noDestinations"
+           hidden$="[[!noDestinations]]" selected$="[[noDestinations]]">
+     $i18n{noDestinationsMessage}
+diff --git chrome/browser/ui/webui/constrained_web_dialog_ui.cc chrome/browser/ui/webui/constrained_web_dialog_ui.cc
+index b59728f623e3..6fd443962836 100644
+--- chrome/browser/ui/webui/constrained_web_dialog_ui.cc
++++ chrome/browser/ui/webui/constrained_web_dialog_ui.cc
+@@ -26,6 +26,8 @@
+ #include "chrome/browser/extensions/tab_helper.h"
+ #endif
+ 
++#include "cef/libcef/features/features.h"
++
+ using content::RenderFrameHost;
+ using content::WebContents;
+ using content::WebUIMessageHandler;
+@@ -55,8 +57,10 @@ class ConstrainedWebDialogDelegateUserData
+ ConstrainedWebDialogUI::ConstrainedWebDialogUI(content::WebUI* web_ui)
+     : WebUIController(web_ui) {
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
++#if !BUILDFLAG(ENABLE_CEF)
+   extensions::TabHelper::CreateForWebContents(web_ui->GetWebContents());
+ #endif
++#endif
+ }
+ 
+ ConstrainedWebDialogUI::~ConstrainedWebDialogUI() {
+diff --git chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
+index 394ce8f66012..fe0e14e27fd1 100644
+--- chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
++++ chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
+@@ -178,8 +178,10 @@ PdfPrinterHandler::PdfPrinterHandler(
+       sticky_settings_(sticky_settings) {}
+ 
+ PdfPrinterHandler::~PdfPrinterHandler() {
++#if !BUILDFLAG(ENABLE_CEF)
+   if (select_file_dialog_.get())
+     select_file_dialog_->ListenerDestroyed();
++#endif
+ }
+ 
+ void PdfPrinterHandler::Reset() {
+@@ -226,12 +228,14 @@ void PdfPrinterHandler::StartPrint(
+     return;
+   }
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   if (select_file_dialog_ &&
+       select_file_dialog_->IsRunning(
+           platform_util::GetTopLevel(preview_web_contents_->GetNativeView()))) {
+     // Dialog is already showing.
+     return;
+   }
++#endif
+ 
+   DCHECK(!print_callback_);
+   print_callback_ = std::move(callback);
+@@ -370,7 +374,11 @@ void PdfPrinterHandler::SelectFile(const base::FilePath& default_filename,
+   // If the directory is empty there is no reason to create it or use the
+   // default location.
+   if (path.empty()) {
++#if !BUILDFLAG(ENABLE_CEF)
+     OnDirectorySelected(default_filename, path);
++#else
++    ShowCefSaveAsDialog(initiator, default_filename, path);
++#endif
+     return;
+   }
+ 
+@@ -380,8 +388,14 @@ void PdfPrinterHandler::SelectFile(const base::FilePath& default_filename,
+   base::ThreadPool::PostTaskAndReplyWithResult(
+       FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+       base::BindOnce(&SelectSaveDirectory, path, default_path),
++#if !BUILDFLAG(ENABLE_CEF)
+       base::BindOnce(&PdfPrinterHandler::OnDirectorySelected,
+                      weak_ptr_factory_.GetWeakPtr(), default_filename));
++#else
++      base::BindOnce(&PdfPrinterHandler::ShowCefSaveAsDialog,
++                     weak_ptr_factory_.GetWeakPtr(), initiator,
++                     default_filename));
++#endif
+ }
+ 
+ void PdfPrinterHandler::PostPrintToPdfTask() {
+@@ -397,6 +411,7 @@ void PdfPrinterHandler::OnGotUniqueFileName(const base::FilePath& path) {
+   FileSelected(path, 0, nullptr);
+ }
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+ void PdfPrinterHandler::OnDirectorySelected(const base::FilePath& filename,
+                                             const base::FilePath& directory) {
+   base::FilePath path = directory.Append(filename);
+@@ -421,5 +436,36 @@ void PdfPrinterHandler::OnDirectorySelected(const base::FilePath& filename,
+       &file_type_info, 0, base::FilePath::StringType(),
+       platform_util::GetTopLevel(preview_web_contents_->GetNativeView()), NULL);
+ }
++#else
++void PdfPrinterHandler::ShowCefSaveAsDialog(content::WebContents* initiator,
++                                            const base::FilePath& filename,
++                                            const base::FilePath& directory) {
++  CefRefPtr<CefBrowserHostImpl> cef_browser =
++      CefBrowserHostImpl::GetBrowserForContents(initiator);
++  if (!cef_browser)
++    return;
++
++  base::FilePath path = directory.Append(filename);
++
++  CefFileDialogRunner::FileChooserParams params;
++  params.mode = blink::mojom::FileChooserParams::Mode::kSave;
++  params.default_file_name = path;
++  params.accept_types.push_back(CefString(path.Extension()));
++
++  cef_browser->RunFileChooser(
++      params, base::Bind(&PdfPrinterHandler::SaveAsDialogDismissed,
++                         weak_ptr_factory_.GetWeakPtr()));
++}
++
++void PdfPrinterHandler::SaveAsDialogDismissed(
++    int selected_accept_filter,
++    const std::vector<base::FilePath>& file_paths) {
++  if (file_paths.size() == 1) {
++    FileSelected(file_paths[0], 0, nullptr);
++  } else {
++    FileSelectionCanceled(nullptr);
++  }
++}
++#endif
+ 
+ }  // namespace printing
+diff --git chrome/browser/ui/webui/print_preview/pdf_printer_handler.h chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
+index 0881b3dd9303..4df6883d4d05 100644
+--- chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
++++ chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
+@@ -11,9 +11,14 @@
+ #include "base/memory/ref_counted.h"
+ #include "base/memory/weak_ptr.h"
+ #include "base/strings/string16.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/ui/webui/print_preview/printer_handler.h"
+ #include "ui/shell_dialogs/select_file_dialog.h"
+ 
++#if BUILDFLAG(ENABLE_CEF)
++#include "cef/libcef/browser/browser_host_impl.h"
++#endif
++
+ namespace base {
+ class FilePath;
+ class RefCountedMemory;
+@@ -76,17 +81,28 @@ class PdfPrinterHandler : public PrinterHandler,
+   // The print preview web contents. Protected so unit tests can access it.
+   content::WebContents* const preview_web_contents_;
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   // The underlying dialog object. Protected so unit tests can access it.
+   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
++#endif
+ 
+  private:
+   void PostPrintToPdfTask();
+   void OnGotUniqueFileName(const base::FilePath& path);
+ 
++#if !BUILDFLAG(ENABLE_CEF)
+   // Prompts the user to save the file. The dialog will default to saving
+   // the file with name |filename| in |directory|.
+   void OnDirectorySelected(const base::FilePath& filename,
+                            const base::FilePath& directory);
++#else
++  void ShowCefSaveAsDialog(content::WebContents* initiator,
++                           const base::FilePath& filename,
++                           const base::FilePath& directory);
++
++  void SaveAsDialogDismissed(int selected_accept_filter,
++                             const std::vector<base::FilePath>& file_paths);
++#endif
+ 
+   Profile* const profile_;
+   PrintPreviewStickySettings* const sticky_settings_;
+diff --git chrome/browser/ui/webui/print_preview/print_preview_handler.cc chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+index 113d34201f90..4a75044c2bb7 100644
+--- chrome/browser/ui/webui/print_preview/print_preview_handler.cc
++++ chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+@@ -27,6 +27,7 @@
+ #include "base/metrics/histogram_macros.h"
+ #include "base/values.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/app_mode/app_mode_utils.h"
+ #include "chrome/browser/bad_message.h"
+ #include "chrome/browser/browser_process.h"
+@@ -1146,7 +1147,7 @@ void PrintPreviewHandler::SendInitialSettings(
+     initial_settings.SetKey(kPolicies, std::move(policies));
+ 
+   if (IsCloudPrintEnabled() &&
+-      !base::FeatureList::IsEnabled(features::kCloudPrinterHandler)) {
++      !base::FeatureList::IsEnabled(::features::kCloudPrinterHandler)) {
+     initial_settings.SetStringKey(
+         kCloudPrintURL, GURL(cloud_devices::GetCloudPrintURL()).spec());
+   }
+@@ -1403,7 +1404,7 @@ PrinterHandler* PrintPreviewHandler::GetPrinterHandler(
+     }
+     return extension_printer_handler_.get();
+   }
+-#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
++#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY) && !BUILDFLAG(ENABLE_CEF)
+   if (printer_type == PrinterType::kPrivetPrinter) {
+     if (!privet_printer_handler_) {
+       privet_printer_handler_ =
+@@ -1411,6 +1412,9 @@ PrinterHandler* PrintPreviewHandler::GetPrinterHandler(
+     }
+     return privet_printer_handler_.get();
+   }
++#else  // !BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
++  if (printer_type == PrinterType::kPrivetPrinter)
++    return nullptr;
+ #endif
+   if (printer_type == PrinterType::kPdfPrinter) {
+     if (!pdf_printer_handler_) {
+@@ -1430,7 +1434,7 @@ PrinterHandler* PrintPreviewHandler::GetPrinterHandler(
+   if (printer_type == PrinterType::kCloudPrinter) {
+     // This printer handler is currently experimental. Ensure it is never
+     // created unless the flag is enabled.
+-    CHECK(base::FeatureList::IsEnabled(features::kCloudPrinterHandler));
++    CHECK(base::FeatureList::IsEnabled(::features::kCloudPrinterHandler));
+     if (!cloud_printer_handler_)
+       cloud_printer_handler_ = PrinterHandler::CreateForCloudPrinters();
+     return cloud_printer_handler_.get();
+@@ -1493,6 +1497,7 @@ void PrintPreviewHandler::OnPrintResult(const std::string& callback_id,
+ }
+ 
+ void PrintPreviewHandler::RegisterForGaiaCookieChanges() {
++#if !BUILDFLAG(ENABLE_CEF)
+   DCHECK(!identity_manager_);
+   cloud_print_enabled_ =
+       !base::Contains(printer_type_deny_list_, kCloudPrinter) &&
+@@ -1509,6 +1514,7 @@ void PrintPreviewHandler::RegisterForGaiaCookieChanges() {
+ 
+   identity_manager_ = IdentityManagerFactory::GetForProfile(profile);
+   identity_manager_->AddObserver(this);
++#endif
+ }
+ 
+ void PrintPreviewHandler::UnregisterForGaiaCookieChanges() {
+diff --git chrome/browser/ui/webui/print_preview/print_preview_ui.cc chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+index cf3c71f478da..8438a234eb8b 100644
+--- chrome/browser/ui/webui/print_preview/print_preview_ui.cc
++++ chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+@@ -24,6 +24,7 @@
+ #include "base/synchronization/lock.h"
+ #include "base/values.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/browser_process.h"
+ #include "chrome/browser/browser_process_platform_part.h"
+ #include "chrome/browser/printing/background_printing_manager.h"
+@@ -76,12 +77,16 @@ namespace printing {
+ 
+ namespace {
+ 
++#if BUILDFLAG(ENABLE_CEF)
++const char kBasicPrintShortcut[] = "";
++#else
+ #if defined(OS_MACOSX)
+ // U+0028 U+21E7 U+2318 U+0050 U+0029 in UTF8
+ const char kBasicPrintShortcut[] = "\x28\xE2\x8c\xA5\xE2\x8C\x98\x50\x29";
+ #elif !defined(OS_CHROMEOS)
+ const char kBasicPrintShortcut[] = "(Ctrl+Shift+P)";
+ #endif
++#endif
+ 
+ #if !BUILDFLAG(OPTIMIZE_WEBUI)
+ constexpr char kGeneratedPath[] =
+@@ -332,7 +337,7 @@ void AddPrintPreviewFlags(content::WebUIDataSource* source, Profile* profile) {
+   source->AddBoolean("isEnterpriseManaged", enterprise_managed);
+ 
+   bool cloud_printer_handler_enabled =
+-      base::FeatureList::IsEnabled(features::kCloudPrinterHandler);
++      base::FeatureList::IsEnabled(::features::kCloudPrinterHandler);
+   source->AddBoolean("cloudPrinterHandlerEnabled",
+                      cloud_printer_handler_enabled);
+ }
diff --git a/src/patch/patches/printing_context_2196.patch b/src/patch/patches/printing_context_2196.patch
new file mode 100644
index 0000000..99af9bc
--- /dev/null
+++ b/src/patch/patches/printing_context_2196.patch
@@ -0,0 +1,41 @@
+diff --git chrome/browser/printing/print_job_worker.cc chrome/browser/printing/print_job_worker.cc
+index 33e17f0df356..04dd4798755c 100644
+--- chrome/browser/printing/print_job_worker.cc
++++ chrome/browser/printing/print_job_worker.cc
+@@ -134,6 +134,7 @@ PrintJobWorker::PrintJobWorker(int render_process_id, int render_frame_id)
+           PrintingContext::Create(printing_context_delegate_.get())),
+       thread_("Printing_Worker") {
+   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
++  printing_context_->set_render_ids(render_process_id, render_frame_id);
+ }
+ 
+ PrintJobWorker::~PrintJobWorker() {
+diff --git printing/printing_context.h printing/printing_context.h
+index 6a5a7c90ef5b..04c0535046dc 100644
+--- printing/printing_context.h
++++ printing/printing_context.h
+@@ -131,6 +131,13 @@ class PRINTING_EXPORT PrintingContext {
+ 
+   int job_id() const { return job_id_; }
+ 
++  void set_render_ids(int render_process_id, int render_frame_id) {
++    render_process_id_ = render_process_id;
++    render_frame_id_ = render_frame_id;
++  }
++  int render_process_id() const { return render_process_id_; }
++  int render_frame_id() const { return render_frame_id_; }
++
+  protected:
+   explicit PrintingContext(Delegate* delegate);
+ 
+@@ -155,6 +162,10 @@ class PRINTING_EXPORT PrintingContext {
+   // The job id for the current job. The value is 0 if no jobs are active.
+   int job_id_;
+ 
++  // Routing IDs for the frame that owns this object.
++  int render_process_id_ = 0;
++  int render_frame_id_ = 0;
++
+  private:
+   DISALLOW_COPY_AND_ASSIGN(PrintingContext);
+ };
diff --git a/src/patch/patches/renderer_host_1070713.patch b/src/patch/patches/renderer_host_1070713.patch
new file mode 100644
index 0000000..8ad6883
--- /dev/null
+++ b/src/patch/patches/renderer_host_1070713.patch
@@ -0,0 +1,13 @@
+diff --git content/browser/renderer_host/render_view_host_impl.cc content/browser/renderer_host/render_view_host_impl.cc
+index 01a4e3dc134b..cddb71886d4f 100644
+--- content/browser/renderer_host/render_view_host_impl.cc
++++ content/browser/renderer_host/render_view_host_impl.cc
+@@ -455,6 +455,8 @@ bool RenderViewHostImpl::IsRenderViewLive() {
+ }
+ 
+ void RenderViewHostImpl::SetBackgroundOpaque(bool opaque) {
++  if (!GetWidget()->GetAssociatedFrameWidget().is_bound())
++    return;
+   GetWidget()->GetAssociatedFrameWidget()->SetBackgroundOpaque(opaque);
+ }
+ 
diff --git a/src/patch/patches/renderer_preferences_util_545103.patch b/src/patch/patches/renderer_preferences_util_545103.patch
new file mode 100644
index 0000000..8d87c5b
--- /dev/null
+++ b/src/patch/patches/renderer_preferences_util_545103.patch
@@ -0,0 +1,24 @@
+diff --git chrome/browser/renderer_preferences_util.cc chrome/browser/renderer_preferences_util.cc
+index 776c050ad31b..5107091f62b4 100644
+--- chrome/browser/renderer_preferences_util.cc
++++ chrome/browser/renderer_preferences_util.cc
+@@ -31,7 +31,8 @@
+ #include "ui/base/cocoa/defaults_utils.h"
+ #endif
+ 
+-#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
++#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS) && \
++    defined(ENABLE_THEMES)
+ #include "chrome/browser/themes/theme_service.h"
+ #include "chrome/browser/themes/theme_service_factory.h"
+ #include "ui/views/linux_ui/linux_ui.h"
+@@ -146,7 +147,8 @@ void UpdateFromSystemSettings(blink::mojom::RendererPreferences* prefs,
+     prefs->caret_blink_interval = interval;
+ #endif
+ 
+-#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
++#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS) && \
++    defined(ENABLE_THEMES)
+   views::LinuxUI* linux_ui = views::LinuxUI::instance();
+   if (linux_ui) {
+     if (ThemeServiceFactory::GetForProfile(profile)->UsingSystemTheme()) {
diff --git a/src/patch/patches/resource_bundle_2512.patch b/src/patch/patches/resource_bundle_2512.patch
new file mode 100644
index 0000000..594951a
--- /dev/null
+++ b/src/patch/patches/resource_bundle_2512.patch
@@ -0,0 +1,45 @@
+diff --git ui/base/resource/resource_bundle.cc ui/base/resource/resource_bundle.cc
+index 133540ca848f..d442344715af 100644
+--- ui/base/resource/resource_bundle.cc
++++ ui/base/resource/resource_bundle.cc
+@@ -854,6 +854,12 @@ ResourceBundle::ResourceBundle(Delegate* delegate)
+     : delegate_(delegate),
+       locale_resources_data_lock_(new base::Lock),
+       max_scale_factor_(SCALE_FACTOR_100P) {
++  // With CEF's multi-threaded mode the ResourceBundle may be created on the
++  // main thread and then accessed on the UI thread. Allow the SequenceChecker
++  // to re-bind on the UI thread when CalledOnValidSequence() is called for the
++  // first time.
++  DETACH_FROM_SEQUENCE(sequence_checker_);
++
+   mangle_localized_strings_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
+       switches::kMangleLocalizedStrings);
+ }
+@@ -863,6 +869,11 @@ ResourceBundle::~ResourceBundle() {
+   UnloadLocaleResources();
+ }
+ 
++void ResourceBundle::CleanupOnUIThread() {
++  FreeImages();
++  font_cache_.clear();
++}
++
+ // static
+ void ResourceBundle::InitSharedInstance(Delegate* delegate) {
+   DCHECK(g_shared_instance_ == nullptr) << "ResourceBundle initialized twice";
+diff --git ui/base/resource/resource_bundle.h ui/base/resource/resource_bundle.h
+index 3daa73bdc6d5..3d2f9232870d 100644
+--- ui/base/resource/resource_bundle.h
++++ ui/base/resource/resource_bundle.h
+@@ -158,6 +158,11 @@ class UI_BASE_EXPORT ResourceBundle {
+   // Return the global resource loader instance.
+   static ResourceBundle& GetSharedInstance();
+ 
++  // With CEF's multi-threaded mode the ResourceBundle may be created/destroyed
++  // on the main thread but accessed on the UI thread. Call this method on the
++  // UI thread to clean up resources before destruction.
++  void CleanupOnUIThread();
++
+   // Loads a secondary locale data pack using the given file region.
+   void LoadSecondaryLocaleDataWithPakFileRegion(
+       base::File pak_file,
diff --git a/src/patch/patches/runhooks.patch b/src/patch/patches/runhooks.patch
new file mode 100644
index 0000000..a39c51a
--- /dev/null
+++ b/src/patch/patches/runhooks.patch
@@ -0,0 +1,73 @@
+diff --git build/toolchain/win/setup_toolchain.py build/toolchain/win/setup_toolchain.py
+index 2ab240da4548..81e69abe1ecd 100644
+--- build/toolchain/win/setup_toolchain.py
++++ build/toolchain/win/setup_toolchain.py
+@@ -142,21 +142,29 @@ def _LoadToolchainEnv(cpu, sdk_dir, target_store):
+       # variable.
+       if 'VSINSTALLDIR' in os.environ:
+         del os.environ['VSINSTALLDIR']
+-      other_path = os.path.normpath(os.path.join(
++      script_path = os.path.normpath(os.path.join(
+                                         os.environ['GYP_MSVS_OVERRIDE_PATH'],
+                                         'VC/Auxiliary/Build/vcvarsall.bat'))
+-      if not os.path.exists(other_path):
+-        raise Exception('%s is missing - make sure VC++ tools are installed.' %
+-                        script_path)
+-      script_path = other_path
++    if not os.path.exists(script_path):
++      # Compiler environment variables must already be specified.
++      variables = []
++      for k in sorted(os.environ.keys()):
++        variables.append('%s=%s' % (str(k), str(os.environ[k])))
++      variables = '\n'.join(variables)
++      return _ExtractImportantEnvironment(variables)
++
+     cpu_arg = "amd64"
+     if (cpu != 'x64'):
+       # x64 is default target CPU thus any other CPU requires a target set
+       cpu_arg += '_' + cpu
+-    args = [script_path, cpu_arg]
++    args = [script_path, cpu_arg, ]
+     # Store target must come before any SDK version declaration
+     if (target_store):
+-      args.append(['store'])
++      args.append('store')
++    # Explicitly specifying the SDK version to build with to avoid accidentally
++    # building with a new and untested SDK. This should stay in sync with the
++    # packaged toolchain in build/vs_toolchain.py.
++    args.append('10.0.18362.0')
+     variables = _LoadEnvFromBat(args)
+   return _ExtractImportantEnvironment(variables)
+ 
+diff --git build/vs_toolchain.py build/vs_toolchain.py
+index 97600e73ba10..36773b159453 100755
+--- build/vs_toolchain.py
++++ build/vs_toolchain.py
+@@ -89,9 +89,16 @@ def SetEnvironmentAndGetRuntimeDllDirs():
+     runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs)
+     os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH']
+   elif sys.platform == 'win32' and not depot_tools_win_toolchain:
++    has_override_path = True
+     if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ:
++      has_override_path = False
+       os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath()
+ 
++    if has_override_path:
++      # Don't attempt to copy DLLs when using a custom toolchain.
++      # The DLLs should already be discoverable via the PATH env variable.
++      return None
++
+     # When using an installed toolchain these files aren't needed in the output
+     # directory in order to run binaries locally, but they are needed in order
+     # to create isolates or the mini_installer. Copying them to the output
+@@ -140,6 +147,10 @@ def _RegistryGetValue(key, value):
+ def GetVisualStudioVersion():
+   """Return best available version of Visual Studio.
+   """
++  # Return the explicitly requested version, if any.
++  if 'GYP_MSVS_VERSION' in os.environ:
++    return os.environ['GYP_MSVS_VERSION']
++
+   supported_versions = list(MSVS_VERSIONS.keys())
+ 
+   # VS installed in depot_tools for Googlers
diff --git a/src/patch/patches/rwh_background_color_1984.patch b/src/patch/patches/rwh_background_color_1984.patch
new file mode 100644
index 0000000..e4b3c5d
--- /dev/null
+++ b/src/patch/patches/rwh_background_color_1984.patch
@@ -0,0 +1,37 @@
+diff --git content/browser/renderer_host/render_widget_host_view_aura.cc content/browser/renderer_host/render_widget_host_view_aura.cc
+index f7f9dafb9798..a378394c2d41 100644
+--- content/browser/renderer_host/render_widget_host_view_aura.cc
++++ content/browser/renderer_host/render_widget_host_view_aura.cc
+@@ -745,10 +745,12 @@ gfx::Rect RenderWidgetHostViewAura::GetViewBounds() {
+ void RenderWidgetHostViewAura::UpdateBackgroundColor() {
+   DCHECK(GetBackgroundColor());
+ 
+-  SkColor color = *GetBackgroundColor();
+-  bool opaque = SkColorGetA(color) == SK_AlphaOPAQUE;
+-  window_->layer()->SetFillsBoundsOpaquely(opaque);
+-  window_->layer()->SetColor(color);
++  if (window_) {
++    SkColor color = *GetBackgroundColor();
++    bool opaque = SkColorGetA(color) == SK_AlphaOPAQUE;
++    window_->layer()->SetFillsBoundsOpaquely(opaque);
++    window_->layer()->SetColor(color);
++  }
+ }
+ 
+ void RenderWidgetHostViewAura::WindowTitleChanged() {
+@@ -2029,6 +2031,15 @@ void RenderWidgetHostViewAura::CreateAuraWindow(aura::client::WindowType type) {
+   // Init(), because it needs to have the layer.
+   if (frame_sink_id_.is_valid())
+     window_->SetEmbedFrameSinkId(frame_sink_id_);
++
++  // Do this after |window_| is created to avoid crashes on Win10.
++  // See https://crbug.com/761389.
++  RenderViewHost* rvh = RenderViewHost::From(host_);
++  if (rvh) {
++    // TODO(mostynb): actually use prefs.  Landing this as a separate CL
++    // first to rebaseline some unreliable layout tests.
++    ignore_result(rvh->GetWebkitPreferences());
++  }
+ }
+ 
+ void RenderWidgetHostViewAura::CreateDelegatedFrameHostClient() {
diff --git a/src/patch/patches/service_manager_654986.patch b/src/patch/patches/service_manager_654986.patch
new file mode 100644
index 0000000..9a1e5ee
--- /dev/null
+++ b/src/patch/patches/service_manager_654986.patch
@@ -0,0 +1,193 @@
+diff --git services/service_manager/embedder/main.cc services/service_manager/embedder/main.cc
+index f9c275cdd78f..247142951d99 100644
+--- services/service_manager/embedder/main.cc
++++ services/service_manager/embedder/main.cc
+@@ -244,22 +244,36 @@ int RunService(MainDelegate* delegate) {
+   return 0;
+ }
+ 
++ProcessType GetProcessType(MainDelegate* delegate,
++                           const base::CommandLine& command_line) {
++  ProcessType process_type = delegate->OverrideProcessType();
++  if (process_type == ProcessType::kDefault) {
++    const std::string& type_switch =
++        command_line.GetSwitchValueASCII(switches::kProcessType);
++    if (type_switch == switches::kProcessTypeServiceManager) {
++      process_type = ProcessType::kServiceManager;
++    } else if (type_switch == switches::kProcessTypeService) {
++      process_type = ProcessType::kService;
++    } else {
++      process_type = ProcessType::kEmbedder;
++    }
++  }
++  return process_type;
++}
++
+ }  // namespace
+ 
+ MainParams::MainParams(MainDelegate* delegate) : delegate(delegate) {}
+ 
+ MainParams::~MainParams() {}
+ 
+-int Main(const MainParams& params) {
++int MainInitialize(MainParams& params) {
+   MainDelegate* delegate = params.delegate;
+   DCHECK(delegate);
+ 
+   int exit_code = -1;
+   base::debug::GlobalActivityTracker* tracker = nullptr;
+   ProcessType process_type = delegate->OverrideProcessType();
+-#if defined(OS_MACOSX)
+-  std::unique_ptr<base::mac::ScopedNSAutoreleasePool> autorelease_pool;
+-#endif
+ 
+   // A flag to indicate whether Main() has been called before. On Android, we
+   // may re-run Main() without restarting the browser process. This flag
+@@ -345,12 +359,7 @@ int Main(const MainParams& params) {
+     MainDelegate::InitializeParams init_params;
+ 
+ #if defined(OS_MACOSX)
+-    // We need this pool for all the objects created before we get to the event
+-    // loop, but we don't want to leave them hanging around until the app quits.
+-    // Each "main" needs to flush this pool right before it goes into its main
+-    // event loop to get rid of the cruft.
+-    autorelease_pool = std::make_unique<base::mac::ScopedNSAutoreleasePool>();
+-    init_params.autorelease_pool = autorelease_pool.get();
++    init_params.autorelease_pool = params.autorelease_pool.get();
+     InitializeMac();
+ #endif
+ 
+@@ -422,18 +431,16 @@ int Main(const MainParams& params) {
+     }
+   }
+ 
++  return exit_code;
++}
++
++int MainRun(MainParams& params) {
++  MainDelegate* delegate = params.delegate;
++  DCHECK(delegate);
++
++  int exit_code = 0;
+   const auto& command_line = *base::CommandLine::ForCurrentProcess();
+-  if (process_type == ProcessType::kDefault) {
+-    std::string type_switch =
+-        command_line.GetSwitchValueASCII(switches::kProcessType);
+-    if (type_switch == switches::kProcessTypeServiceManager) {
+-      process_type = ProcessType::kServiceManager;
+-    } else if (type_switch == switches::kProcessTypeService) {
+-      process_type = ProcessType::kService;
+-    } else {
+-      process_type = ProcessType::kEmbedder;
+-    }
+-  }
++  const ProcessType process_type = GetProcessType(delegate, command_line);
+   switch (process_type) {
+     case ProcessType::kDefault:
+       NOTREACHED();
+@@ -455,6 +462,8 @@ int Main(const MainParams& params) {
+       break;
+   }
+ 
++  base::debug::GlobalActivityTracker* tracker =
++      base::debug::GlobalActivityTracker::Get();
+   if (tracker) {
+     if (exit_code == 0) {
+       tracker->SetProcessPhaseIfEnabled(
+@@ -466,13 +475,38 @@ int Main(const MainParams& params) {
+     }
+   }
+ 
++  return exit_code;
++}
++
++void MainShutdown(MainParams& params) {
++  MainDelegate* delegate = params.delegate;
++  DCHECK(delegate);
++
+ #if defined(OS_MACOSX)
+-  autorelease_pool.reset();
++  params.autorelease_pool.reset();
+ #endif
+ 
++  const ProcessType process_type =
++      GetProcessType(delegate, *base::CommandLine::ForCurrentProcess());
+   if (process_type == ProcessType::kEmbedder)
+     delegate->ShutDownEmbedderProcess();
++}
++
++int Main(MainParams& params) {
++#if defined(OS_MACOSX)
++  // We need this pool for all the objects created before we get to the event
++  // loop, but we don't want to leave them hanging around until the app quits.
++  // Each "main" needs to flush this pool right before it goes into its main
++  // event loop to get rid of the cruft.
++  params.autorelease_pool =
++      std::make_unique<base::mac::ScopedNSAutoreleasePool>();
++#endif
+ 
++  int exit_code = MainInitialize(params);
++  if (exit_code >= 0)
++    return exit_code;
++  exit_code = MainRun(params);
++  MainShutdown(params);
+   return exit_code;
+ }
+ 
+diff --git services/service_manager/embedder/main.h services/service_manager/embedder/main.h
+index 57e88aa85dfe..5ed6ec2abfda 100644
+--- services/service_manager/embedder/main.h
++++ services/service_manager/embedder/main.h
+@@ -5,9 +5,15 @@
+ #ifndef SERVICES_SERVICE_MANAGER_EMBEDDER_MAIN_H_
+ #define SERVICES_SERVICE_MANAGER_EMBEDDER_MAIN_H_
+ 
++#include <memory>
++
+ #include "base/component_export.h"
+ #include "build/build_config.h"
+ 
++#if defined(OS_MACOSX)
++#include "base/mac/scoped_nsautorelease_pool.h"
++#endif  // defined(OS_MACOSX)
++
+ namespace service_manager {
+ 
+ class MainDelegate;
+@@ -22,11 +28,22 @@ struct COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER) MainParams {
+   int argc = 0;
+   const char** argv = nullptr;
+ #endif
++
++#if defined(OS_MACOSX)
++  std::unique_ptr<base::mac::ScopedNSAutoreleasePool> autorelease_pool;
++#endif
+ };
+ 
++// Split Main() into separate stages.
++int COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER)
++    MainInitialize(MainParams& params);
++int COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER) MainRun(MainParams& params);
++void COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER)
++    MainShutdown(MainParams& params);
++
+ // Main function which should be called as early as possible by any executable
+ // embedding the service manager.
+-int COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER) Main(const MainParams& params);
++int COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER) Main(MainParams& params);
+ 
+ }  // namespace service_manager
+ 
+diff --git services/service_manager/embedder/set_process_title.cc services/service_manager/embedder/set_process_title.cc
+index 1dc53b847ef9..5432ab02a088 100644
+--- services/service_manager/embedder/set_process_title.cc
++++ services/service_manager/embedder/set_process_title.cc
+@@ -44,7 +44,7 @@ void SetProcessTitleFromCommandLine(const char** main_argv) {
+   bool have_argv0 = false;
+ 
+ #if defined(OS_LINUX)
+-  DCHECK_EQ(base::PlatformThread::CurrentId(), getpid());
++  //DCHECK_EQ(base::PlatformThread::CurrentId(), getpid());
+ 
+   if (main_argv)
+     setproctitle_init(main_argv);
diff --git a/src/patch/patches/services_network_2622.patch b/src/patch/patches/services_network_2622.patch
new file mode 100644
index 0000000..69f778f
--- /dev/null
+++ b/src/patch/patches/services_network_2622.patch
@@ -0,0 +1,196 @@
+diff --git chrome/browser/net/profile_network_context_service.cc chrome/browser/net/profile_network_context_service.cc
+index 44ce8ba980a6..1dbbf0aa2341 100644
+--- chrome/browser/net/profile_network_context_service.cc
++++ chrome/browser/net/profile_network_context_service.cc
+@@ -18,6 +18,7 @@
+ #include "base/strings/string_split.h"
+ #include "base/task/post_task.h"
+ #include "base/task/thread_pool.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/browser_process.h"
+ #include "chrome/browser/content_settings/cookie_settings_factory.h"
+ #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+@@ -681,9 +682,22 @@ ProfileNetworkContextService::CreateNetworkContextParams(
+   network_context_params->cookie_manager_params =
+       CreateCookieManagerParams(profile_, *cookie_settings_);
+ 
++  network_context_params->cookieable_schemes = profile_->GetCookieableSchemes();
++
+   // Configure on-disk storage for non-OTR profiles. OTR profiles just use
+   // default behavior (in memory storage, default sizes).
+   if (!in_memory) {
++#if BUILDFLAG(ENABLE_CEF)
++    PrefService* prefs = profile_->GetPrefs();
++    // Configure the HTTP cache path and size.
++    const base::FilePath& base_cache_path =
++        prefs->GetFilePath(prefs::kDiskCacheDir);
++    DCHECK(!base_cache_path.empty());
++    network_context_params->http_cache_path =
++        base_cache_path.Append(chrome::kCacheDirname);
++    network_context_params->http_cache_max_size =
++        prefs->GetInteger(prefs::kDiskCacheSize);
++#else
+     PrefService* local_state = g_browser_process->local_state();
+     // Configure the HTTP cache path and size.
+     base::FilePath base_cache_path;
+@@ -696,6 +710,7 @@ ProfileNetworkContextService::CreateNetworkContextParams(
+         base_cache_path.Append(chrome::kCacheDirname);
+     network_context_params->http_cache_max_size =
+         local_state->GetInteger(prefs::kDiskCacheSize);
++#endif
+ 
+     // Currently this just contains HttpServerProperties, but that will likely
+     // change.
+diff --git chrome/browser/profiles/profile.h chrome/browser/profiles/profile.h
+index ee2f1e1baf51..4d53c655d723 100644
+--- chrome/browser/profiles/profile.h
++++ chrome/browser/profiles/profile.h
+@@ -375,6 +375,11 @@ class Profile : public content::BrowserContext {
+   virtual bool ShouldRestoreOldSessionCookies();
+   virtual bool ShouldPersistSessionCookies();
+ 
++  // Returns schemes that should be cookieable, if other than the defaults.
++  virtual base::Optional<std::vector<std::string>> GetCookieableSchemes() {
++    return base::nullopt;
++  }
++
+   // Creates NetworkContext for the specified isolated app (or for the profile
+   // itself, if |relative_path| is empty).
+   virtual mojo::Remote<network::mojom::NetworkContext> CreateNetworkContext(
+diff --git net/cookies/cookie_monster.cc net/cookies/cookie_monster.cc
+index 783485c013df..bf115fd4d63a 100644
+--- net/cookies/cookie_monster.cc
++++ net/cookies/cookie_monster.cc
+@@ -476,6 +476,25 @@ void CookieMonster::SetCookieableSchemes(
+   MaybeRunCookieCallback(std::move(callback), true);
+ }
+ 
++void CookieMonster::AddCookieableSchemes(
++    const std::vector<std::string>& schemes,
++    SetCookieableSchemesCallback callback) {
++  DCHECK(thread_checker_.CalledOnValidThread());
++
++  // Calls to this method will have no effect if made after a WebView or
++  // CookieManager instance has been created.
++  if (initialized_) {
++    MaybeRunCookieCallback(std::move(callback), false);
++    return;
++  }
++
++  if (!schemes.empty()) {
++    cookieable_schemes_.insert(cookieable_schemes_.begin(), schemes.begin(),
++                               schemes.end());
++  }
++  MaybeRunCookieCallback(std::move(callback), true);
++}
++
+ // This function must be called before the CookieMonster is used.
+ void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) {
+   DCHECK(thread_checker_.CalledOnValidThread());
+diff --git net/cookies/cookie_monster.h net/cookies/cookie_monster.h
+index 431fab884a5f..eeed129b49f2 100644
+--- net/cookies/cookie_monster.h
++++ net/cookies/cookie_monster.h
+@@ -180,6 +180,8 @@ class NET_EXPORT CookieMonster : public CookieStore {
+   CookieChangeDispatcher& GetChangeDispatcher() override;
+   void SetCookieableSchemes(const std::vector<std::string>& schemes,
+                             SetCookieableSchemesCallback callback) override;
++  void AddCookieableSchemes(const std::vector<std::string>& schemes,
++                            SetCookieableSchemesCallback callback) override;
+ 
+   // Enables writing session cookies into the cookie database. If this this
+   // method is called, it must be called before first use of the instance
+diff --git net/cookies/cookie_store.h net/cookies/cookie_store.h
+index 996ad7d14dfb..5c8e43c1d29c 100644
+--- net/cookies/cookie_store.h
++++ net/cookies/cookie_store.h
+@@ -148,6 +148,11 @@ class NET_EXPORT CookieStore {
+   // Transfer ownership of a CookieAccessDelegate.
+   void SetCookieAccessDelegate(std::unique_ptr<CookieAccessDelegate> delegate);
+ 
++  // Adds to the list of cookieable schemes. Does nothing if called after first
++  // use of the instance (i.e. after the instance initialization process).
++  virtual void AddCookieableSchemes(const std::vector<std::string>& schemes,
++                                    SetCookieableSchemesCallback callback) = 0;
++
+   // Reports the estimate of dynamically allocated memory in bytes.
+   virtual void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                                const std::string& parent_absolute_name) const;
+diff --git services/network/cookie_manager.cc services/network/cookie_manager.cc
+index d1bf7a4d1f6d..772e08ee8916 100644
+--- services/network/cookie_manager.cc
++++ services/network/cookie_manager.cc
+@@ -228,14 +228,9 @@ void CookieManager::FlushCookieStore(FlushCookieStoreCallback callback) {
+ void CookieManager::AllowFileSchemeCookies(
+     bool allow,
+     AllowFileSchemeCookiesCallback callback) {
+-  std::vector<std::string> cookieable_schemes(
+-      net::CookieMonster::kDefaultCookieableSchemes,
+-      net::CookieMonster::kDefaultCookieableSchemes +
+-          net::CookieMonster::kDefaultCookieableSchemesCount);
+-  if (allow) {
+-    cookieable_schemes.push_back(url::kFileScheme);
+-  }
+-  cookie_store_->SetCookieableSchemes(cookieable_schemes, std::move(callback));
++  if (!allow)
++    return;
++  cookie_store_->AddCookieableSchemes({url::kFileScheme}, std::move(callback));
+ }
+ 
+ void CookieManager::SetForceKeepSessionState() {
+diff --git services/network/network_context.cc services/network/network_context.cc
+index 5e37a45a033a..ca44461120a8 100644
+--- services/network/network_context.cc
++++ services/network/network_context.cc
+@@ -1795,6 +1795,7 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext() {
+   }
+ 
+   scoped_refptr<SessionCleanupCookieStore> session_cleanup_cookie_store;
++  std::unique_ptr<net::CookieMonster> cookie_store;
+   if (params_->cookie_path) {
+     scoped_refptr<base::SequencedTaskRunner> client_task_runner =
+         base::ThreadTaskRunnerHandle::Get();
+@@ -1821,18 +1822,26 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext() {
+     session_cleanup_cookie_store =
+         base::MakeRefCounted<SessionCleanupCookieStore>(sqlite_store);
+ 
+-    std::unique_ptr<net::CookieMonster> cookie_store =
++    cookie_store =
+         std::make_unique<net::CookieMonster>(session_cleanup_cookie_store.get(),
+                                              net_log);
+     if (params_->persist_session_cookies)
+       cookie_store->SetPersistSessionCookies(true);
+-
+-    builder.SetCookieStore(std::move(cookie_store));
+   } else {
+     DCHECK(!params_->restore_old_session_cookies);
+     DCHECK(!params_->persist_session_cookies);
++    cookie_store =
++        std::make_unique<net::CookieMonster>(nullptr /* store */, net_log);
++  }
++
++  if (params_->cookieable_schemes.has_value()) {
++    cookie_store->SetCookieableSchemes(
++        *params_->cookieable_schemes,
++        net::CookieStore::SetCookieableSchemesCallback());
+   }
+ 
++  builder.SetCookieStore(std::move(cookie_store));
++
+   if (base::FeatureList::IsEnabled(features::kTrustTokens)) {
+     trust_token_store_ = std::make_unique<PendingTrustTokenStore>();
+ 
+diff --git services/network/public/mojom/network_context.mojom services/network/public/mojom/network_context.mojom
+index 9515fe92e0b1..e659550debd2 100644
+--- services/network/public/mojom/network_context.mojom
++++ services/network/public/mojom/network_context.mojom
+@@ -219,6 +219,9 @@ struct NetworkContextParams {
+   // cookies. Otherwise it should be false.
+   bool persist_session_cookies = false;
+ 
++  // Schemes that will be passed to CookieMonster::SetCookieableSchemes.
++  array<string>? cookieable_schemes;
++
+   // True if an HTTP cache should be used.
+   bool http_cache_enabled = true;
+   // Maximum size of the HTTP cache. 0 means to use the default size.
diff --git a/src/patch/patches/services_network_2718.patch b/src/patch/patches/services_network_2718.patch
new file mode 100644
index 0000000..3302121
--- /dev/null
+++ b/src/patch/patches/services_network_2718.patch
@@ -0,0 +1,52 @@
+diff --git content/browser/storage_partition_impl.cc content/browser/storage_partition_impl.cc
+index 66a4f7c28b4f..92cb66c1dc38 100644
+--- content/browser/storage_partition_impl.cc
++++ content/browser/storage_partition_impl.cc
+@@ -797,10 +797,6 @@ class LoginHandlerDelegate {
+     }
+ 
+     WebContents* web_contents = web_contents_getter_.Run();
+-    if (!web_contents) {
+-      OnAuthCredentials(base::nullopt);
+-      return;
+-    }
+ 
+     // WeakPtr is not strictly necessary here due to OnRequestCancelled.
+     creating_login_delegate_ = true;
+@@ -857,12 +853,6 @@ void OnAuthRequiredContinuation(
+     web_contents_getter =
+         base::BindRepeating(GetWebContents, process_id, routing_id);
+   }
+-  if (!web_contents_getter.Run()) {
+-    mojo::Remote<network::mojom::AuthChallengeResponder>
+-        auth_challenge_responder_remote(std::move(auth_challenge_responder));
+-    auth_challenge_responder_remote->OnAuthCredentials(base::nullopt);
+-    return;
+-  }
+   new LoginHandlerDelegate(std::move(auth_challenge_responder),
+                            std::move(web_contents_getter), auth_info,
+                            is_request_for_main_frame, process_id, routing_id,
+@@ -2603,15 +2593,21 @@ void StoragePartitionImpl::GetQuotaSettings(
+     return;
+   }
+ 
++  // CEF always returns false for IsOffTheRecord(), so also check the path.
++  const bool is_incognito = browser_context_->IsOffTheRecord() ||
++                            browser_context_->GetPath().empty();
+   storage::GetNominalDynamicSettings(
+-      GetPath(), browser_context_->IsOffTheRecord(),
++      GetPath(), is_incognito,
+       storage::GetDefaultDeviceInfoHelper(), std::move(callback));
+ }
+ 
+ void StoragePartitionImpl::InitNetworkContext() {
+   network_context_ = GetContentClient()->browser()->CreateNetworkContext(
+       browser_context_, is_in_memory_, relative_partition_path_);
+-  DCHECK(network_context_);
++  if (!network_context_) {
++    // May happen during shutdown.
++    return;
++  }
+ 
+   network_context_client_receiver_.reset();
+   network_context_->SetClient(
diff --git a/src/patch/patches/set_resize_background_color.patch b/src/patch/patches/set_resize_background_color.patch
new file mode 100644
index 0000000..f6d3fa7
--- /dev/null
+++ b/src/patch/patches/set_resize_background_color.patch
@@ -0,0 +1,70 @@
+diff --git ui/views/controls/native/native_view_host.cc ui/views/controls/native/native_view_host.cc
+index b29553ae8f7b..c31233db9ab2 100644
+--- ui/views/controls/native/native_view_host.cc
++++ ui/views/controls/native/native_view_host.cc
+@@ -154,7 +154,7 @@ void NativeViewHost::OnPaint(gfx::Canvas* canvas) {
+   // It would be nice if this used some approximation of the page's
+   // current background color.
+   if (native_wrapper_->HasInstalledClip())
+-    canvas->FillRect(GetLocalBounds(), SK_ColorWHITE);
++    canvas->FillRect(GetLocalBounds(), resize_background_color_);
+ }
+ 
+ void NativeViewHost::VisibilityChanged(View* starting_from, bool is_visible) {
+diff --git ui/views/controls/native/native_view_host.h ui/views/controls/native/native_view_host.h
+index f92961e9f79b..f1ff80b244ef 100644
+--- ui/views/controls/native/native_view_host.h
++++ ui/views/controls/native/native_view_host.h
+@@ -86,6 +86,12 @@ class VIEWS_EXPORT NativeViewHost : public View {
+   void set_fast_resize(bool fast_resize) { fast_resize_ = fast_resize; }
+   bool fast_resize() const { return fast_resize_; }
+ 
++  // Sets the color to paint the background during a resize that involves a
++  // clip. This is white by default.
++  void set_resize_background_color(SkColor resize_background_color) {
++    resize_background_color_ = resize_background_color;
++  }
++
+   gfx::NativeView native_view() const { return native_view_; }
+ 
+   void NativeViewDestroyed();
+@@ -132,6 +138,9 @@ class VIEWS_EXPORT NativeViewHost : public View {
+   // in the setter/accessor above.
+   bool fast_resize_ = false;
+ 
++  // Color to paint in the background while resizing.
++  SkColor resize_background_color_ = SK_ColorWHITE;
++
+   DISALLOW_COPY_AND_ASSIGN(NativeViewHost);
+ };
+ 
+diff --git ui/views/controls/webview/webview.cc ui/views/controls/webview/webview.cc
+index 7bef84b8c9ea..9b9f0a789fe9 100644
+--- ui/views/controls/webview/webview.cc
++++ ui/views/controls/webview/webview.cc
+@@ -127,6 +127,10 @@ void WebView::EnableSizingFromWebContents(const gfx::Size& min_size,
+   MaybeEnableAutoResize();
+ }
+ 
++void WebView::SetResizeBackgroundColor(SkColor resize_background_color) {
++  holder_->set_resize_background_color(resize_background_color);
++}
++
+ void WebView::SetCrashedOverlayView(View* crashed_overlay_view) {
+   if (crashed_overlay_view_ == crashed_overlay_view)
+     return;
+diff --git ui/views/controls/webview/webview.h ui/views/controls/webview/webview.h
+index 84d5cd520f7a..c7a2c7644c4f 100644
+--- ui/views/controls/webview/webview.h
++++ ui/views/controls/webview/webview.h
+@@ -82,6 +82,10 @@ class WEBVIEW_EXPORT WebView : public View,
+   void EnableSizingFromWebContents(const gfx::Size& min_size,
+                                    const gfx::Size& max_size);
+ 
++  // Set the background color to use while resizing with a clip. This is white
++  // by default.
++  void SetResizeBackgroundColor(SkColor resize_background_color);
++
+   // If provided, this View will be shown in place of the web contents
+   // when the web contents is in a crashed state. This is cleared automatically
+   // if the web contents is changed.
diff --git a/src/patch/patches/storage_incognito_2289.patch b/src/patch/patches/storage_incognito_2289.patch
new file mode 100644
index 0000000..96bfb4a
--- /dev/null
+++ b/src/patch/patches/storage_incognito_2289.patch
@@ -0,0 +1,57 @@
+diff --git content/browser/blob_storage/chrome_blob_storage_context.cc content/browser/blob_storage/chrome_blob_storage_context.cc
+index f90e9fce0675..33551a2874bc 100644
+--- content/browser/blob_storage/chrome_blob_storage_context.cc
++++ content/browser/blob_storage/chrome_blob_storage_context.cc
+@@ -117,7 +117,8 @@ ChromeBlobStorageContext* ChromeBlobStorageContext::GetFor(
+ 
+     // If we're not incognito mode, schedule all of our file tasks to enable
+     // disk on the storage context.
+-    if (!context->IsOffTheRecord() && io_thread_valid) {
++    if (!context->GetPath().empty() && !context->IsOffTheRecord() &&
++        io_thread_valid) {
+       file_task_runner = base::ThreadPool::CreateTaskRunner(
+           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+diff --git content/browser/browser_context.cc content/browser/browser_context.cc
+index 90ec29eda0e7..80cf13d79eff 100644
+--- content/browser/browser_context.cc
++++ content/browser/browser_context.cc
+@@ -59,6 +59,7 @@
+ #include "content/public/common/content_switches.h"
+ #include "content/public/common/service_manager_connection.h"
+ #include "content/public/common/service_names.mojom.h"
++#include "media/capabilities/in_memory_video_decode_stats_db_impl.h"
+ #include "media/base/media_switches.h"
+ #include "media/capabilities/in_memory_video_decode_stats_db_impl.h"
+ #include "media/capabilities/video_decode_stats_db_impl.h"
+@@ -170,7 +171,7 @@ StoragePartition* GetStoragePartitionFromConfig(
+   StoragePartitionImplMap* partition_map =
+       GetStoragePartitionMap(browser_context);
+ 
+-  if (browser_context->IsOffTheRecord())
++  if (browser_context->IsOffTheRecord() || browser_context->GetPath().empty())
+     in_memory = true;
+ 
+   return partition_map->Get(partition_domain, partition_name, in_memory,
+@@ -674,7 +675,7 @@ media::VideoDecodePerfHistory* BrowserContext::GetVideoDecodePerfHistory() {
+         kUseInMemoryDBDefault);
+ 
+     std::unique_ptr<media::VideoDecodeStatsDB> stats_db;
+-    if (use_in_memory_db) {
++    if (use_in_memory_db || GetPath().empty()) {
+       stats_db =
+           std::make_unique<media::InMemoryVideoDecodeStatsDBImpl>(nullptr);
+     } else {
+diff --git storage/browser/database/database_tracker.cc storage/browser/database/database_tracker.cc
+index 41dc6c9ac188..b125c1d2bcc8 100644
+--- storage/browser/database/database_tracker.cc
++++ storage/browser/database/database_tracker.cc
+@@ -506,7 +506,7 @@ bool DatabaseTracker::LazyInit() {
+     meta_table_.reset(new sql::MetaTable());
+ 
+     is_initialized_ =
+-        base::CreateDirectory(db_dir_) &&
++        (is_incognito_ ? true : base::CreateDirectory(db_dir_)) &&
+         (db_->is_open() ||
+          (is_incognito_ ? db_->OpenInMemory() :
+           db_->Open(kTrackerDatabaseFullPath))) &&
diff --git a/src/patch/patches/trace_event.patch b/src/patch/patches/trace_event.patch
new file mode 100644
index 0000000..524aa47
--- /dev/null
+++ b/src/patch/patches/trace_event.patch
@@ -0,0 +1,13 @@
+diff --git base/trace_event/builtin_categories.h base/trace_event/builtin_categories.h
+index ac67c35bbcb0..f1ebee76b00d 100644
+--- base/trace_event/builtin_categories.h
++++ base/trace_event/builtin_categories.h
+@@ -52,6 +52,8 @@
+   X("cc")                                                                \
+   X("cc.debug")                                                          \
+   X("cdp.perf")                                                          \
++  X("cef")                                                               \
++  X("cef.client")                                                        \
+   X("chromeos")                                                          \
+   X("cma")                                                               \
+   X("compositor")                                                        \
diff --git a/src/patch/patches/ui_dragdrop_355390.patch b/src/patch/patches/ui_dragdrop_355390.patch
new file mode 100644
index 0000000..03a2c4a
--- /dev/null
+++ b/src/patch/patches/ui_dragdrop_355390.patch
@@ -0,0 +1,14 @@
+diff --git ui/base/x/x11_os_exchange_data_provider.cc ui/base/x/x11_os_exchange_data_provider.cc
+index 41190f9f56a9..dcb869d1e2f5 100644
+--- ui/base/x/x11_os_exchange_data_provider.cc
++++ ui/base/x/x11_os_exchange_data_provider.cc
+@@ -127,7 +127,8 @@ void XOSExchangeDataProvider::SetURL(const GURL& url,
+     format_map_.Insert(gfx::GetAtom(kMimeTypeMozillaURL), mem);
+ 
+     // Set a string fallback as well.
+-    SetString(spec);
++    if (!HasString())
++      SetString(spec);
+ 
+     // Return early if this drag already contains file contents (this implies
+     // that file contents must be populated before URLs). Nautilus (and possibly
diff --git a/src/patch/patches/underlay_1051.patch b/src/patch/patches/underlay_1051.patch
new file mode 100644
index 0000000..330aaa6
--- /dev/null
+++ b/src/patch/patches/underlay_1051.patch
@@ -0,0 +1,13 @@
+diff --git ui/base/cocoa/underlay_opengl_hosting_window.h ui/base/cocoa/underlay_opengl_hosting_window.h
+index d673c31a0f31..806d1341567b 100644
+--- ui/base/cocoa/underlay_opengl_hosting_window.h
++++ ui/base/cocoa/underlay_opengl_hosting_window.h
+@@ -12,7 +12,7 @@
+ // Common base class for windows that host a OpenGL surface that renders under
+ // the window. Previously contained methods related to hole punching, now just
+ // contains common asserts.
+-UI_BASE_EXPORT
++__attribute__((visibility("default")))
+ @interface UnderlayOpenGLHostingWindow : NSWindow
+ @end
+ 
diff --git a/src/patch/patches/views_1749_2102.patch b/src/patch/patches/views_1749_2102.patch
new file mode 100644
index 0000000..cf38e9a
--- /dev/null
+++ b/src/patch/patches/views_1749_2102.patch
@@ -0,0 +1,493 @@
+diff --git ui/base/models/menu_model.h ui/base/models/menu_model.h
+index 5bcc6204c2b5..1bf18609d150 100644
+--- ui/base/models/menu_model.h
++++ ui/base/models/menu_model.h
+@@ -16,6 +16,7 @@
+ namespace gfx {
+ class FontList;
+ class Image;
++class Point;
+ struct VectorIcon;
+ }
+ 
+@@ -125,6 +126,27 @@ class UI_BASE_EXPORT MenuModel : public base::SupportsWeakPtr<MenuModel> {
+   // |event_flags| is a bit mask of ui::EventFlags.
+   virtual void ActivatedAt(int index, int event_flags);
+ 
++  // Called when the user moves the mouse outside the menu and over the owning
++  // window.
++  virtual void MouseOutsideMenu(const gfx::Point& screen_point) {}
++
++  // Called on unhandled open/close submenu keyboard commands. |is_rtl| will be
++  // true if the menu is displaying a right-to-left language.
++  virtual void UnhandledOpenSubmenu(bool is_rtl) {}
++  virtual void UnhandledCloseSubmenu(bool is_rtl) {}
++
++  // Override the text/background color of a given menu item dependent on the
++  // |index| and its |is_hovered| state. |is_minor| will be true for accelerator
++  // text. Returns true if it chooses to override the color.
++  virtual bool GetTextColor(int index,
++                            bool is_minor,
++                            bool is_hovered,
++                            SkColor* override_color) const { return false; }
++  virtual bool GetBackgroundColor(int index,
++                                  bool is_hovered,
++                                  SkColor* override_color) const
++                                  { return false; }
++
+   // Called when the menu is about to be shown.
+   virtual void MenuWillShow() {}
+ 
+diff --git ui/gfx/render_text.cc ui/gfx/render_text.cc
+index 7d5fb0c4d6e6..79e59cd87f91 100644
+--- ui/gfx/render_text.cc
++++ ui/gfx/render_text.cc
+@@ -609,6 +609,14 @@ void RenderText::SetWhitespaceElision(base::Optional<bool> whitespace_elision) {
+   }
+ }
+ 
++void RenderText::SetDrawStringsFlags(int flags) {
++  if (draw_strings_flags_ == flags)
++    return;
++  draw_strings_flags_ = flags;
++  cached_bounds_and_offset_valid_ = false;
++  OnTextAttributeChanged();
++}
++
+ void RenderText::SetDisplayRect(const Rect& r) {
+   if (r != display_rect_) {
+     display_rect_ = r;
+@@ -1973,6 +1981,19 @@ void RenderText::OnTextAttributeChanged() {
+ 
+   layout_text_up_to_date_ = false;
+ 
++  if (draw_strings_flags_ != 0) {
++    // Compute layout size with the mnemonic character underlined since it might
++    // be larger than with the underline hidden.
++    int char_pos = -1;
++    int char_span = 0;
++    layout_text_ =
++        gfx::RemoveAcceleratorChar(layout_text_, '&', &char_pos, &char_span);
++    if (char_pos != -1) {
++      gfx::Range range(char_pos, char_pos + char_span);
++      styles_[TEXT_STYLE_UNDERLINE].ApplyValue(true, range);
++    }
++  }
++
+   OnLayoutTextAttributeChanged(true);
+ }
+ 
+diff --git ui/gfx/render_text.h ui/gfx/render_text.h
+index 892b32910b77..0d3c4ef5cafa 100644
+--- ui/gfx/render_text.h
++++ ui/gfx/render_text.h
+@@ -324,6 +324,10 @@ class GFX_EXPORT RenderText {
+     return whitespace_elision_;
+   }
+ 
++  // Get or set the flags that control display of accelerator characters.
++  void SetDrawStringsFlags(int flags);
++  int draw_strings_flags() const { return draw_strings_flags_; }
++
+   const Rect& display_rect() const { return display_rect_; }
+   void SetDisplayRect(const Rect& r);
+ 
+@@ -1006,6 +1010,8 @@ class GFX_EXPORT RenderText {
+   // Tell whether or not the |layout_text_| needs an update or is up to date.
+   mutable bool layout_text_up_to_date_ = false;
+ 
++  int draw_strings_flags_ = 0;
++
+   DISALLOW_COPY_AND_ASSIGN(RenderText);
+ };
+ 
+diff --git ui/views/animation/ink_drop_host_view.h ui/views/animation/ink_drop_host_view.h
+index b711c5f08f80..d0d428057e5d 100644
+--- ui/views/animation/ink_drop_host_view.h
++++ ui/views/animation/ink_drop_host_view.h
+@@ -138,6 +138,8 @@ class VIEWS_EXPORT InkDropHostView : public View {
+   // this isn't necessary anymore.
+   virtual InkDrop* GetInkDrop();
+ 
++  InkDropMode ink_drop_mode() const { return ink_drop_mode_; }
++
+  protected:
+   // Size used for the default SquareInkDropRipple.
+   static constexpr gfx::Size kDefaultInkDropSize = gfx::Size(24, 24);
+diff --git ui/views/controls/button/label_button.cc ui/views/controls/button/label_button.cc
+index bb9105f1a18e..45ae9cdb4ab0 100644
+--- ui/views/controls/button/label_button.cc
++++ ui/views/controls/button/label_button.cc
+@@ -480,6 +480,12 @@ void LabelButton::OnThemeChanged() {
+   SchedulePaint();
+ }
+ 
++void LabelButton::SetFontList(const gfx::FontList& font_list) {
++  cached_normal_font_list_ = font_list;
++  cached_default_button_font_list_ = font_list;
++  label_->SetFontList(cached_normal_font_list_);
++}
++
+ void LabelButton::StateChanged(ButtonState old_state) {
+   const gfx::Size previous_image_size(image_->GetPreferredSize());
+   UpdateImage();
+diff --git ui/views/controls/button/label_button.h ui/views/controls/button/label_button.h
+index ac348aa7dd46..88ab098daddc 100644
+--- ui/views/controls/button/label_button.h
++++ ui/views/controls/button/label_button.h
+@@ -124,6 +124,9 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
+   ui::NativeTheme::State GetForegroundThemeState(
+       ui::NativeTheme::ExtraParams* params) const override;
+ 
++  // Sets the font list used by this button.
++  void SetFontList(const gfx::FontList& font_list);
++
+  protected:
+   ImageView* image() const { return image_; }
+   Label* label() const { return label_; }
+diff --git ui/views/controls/label.cc ui/views/controls/label.cc
+index c15c27ae75b6..325eec4939de 100644
+--- ui/views/controls/label.cc
++++ ui/views/controls/label.cc
+@@ -53,6 +53,20 @@ bool IsOpaque(SkColor color) {
+   return SkColorGetA(color) == SK_AlphaOPAQUE;
+ }
+ 
++// Strips accelerator character prefixes in |text| if needed, based on |flags|.
++// Returns a range in |text| to underline or Range::InvalidRange() if
++// underlining is not needed.
++gfx::Range StripAcceleratorChars(int flags, base::string16* text) {
++  if (flags & (gfx::Canvas::SHOW_PREFIX | gfx::Canvas::HIDE_PREFIX)) {
++    int char_pos = -1;
++    int char_span = 0;
++    *text = gfx::RemoveAcceleratorChar(*text, '&', &char_pos, &char_span);
++    if ((flags & gfx::Canvas::SHOW_PREFIX) && char_pos != -1)
++      return gfx::Range(char_pos, char_pos + char_span);
++  }
++  return gfx::Range::InvalidRange();
++}
++
+ }  // namespace
+ 
+ namespace views {
+@@ -308,6 +322,14 @@ base::string16 Label::GetTooltipText() const {
+   return tooltip_text_;
+ }
+ 
++void Label::SetDrawStringsFlags(int flags) {
++  if (draw_strings_flags_ == flags)
++    return;
++  draw_strings_flags_ = flags;
++  full_text_->SetDrawStringsFlags(draw_strings_flags_);
++  ResetLayout();
++}
++
+ void Label::SetTooltipText(const base::string16& tooltip_text) {
+   DCHECK(handles_tooltips_);
+   if (tooltip_text_ == tooltip_text)
+@@ -582,7 +604,19 @@ std::unique_ptr<gfx::RenderText> Label::CreateRenderText() const {
+   render_text->SetFontList(font_list());
+   render_text->set_shadows(GetShadows());
+   render_text->SetCursorEnabled(false);
+-  render_text->SetText(GetText());
++
++  if (draw_strings_flags_ != 0) {
++    base::string16 text_str = GetText();
++    gfx::Range range = StripAcceleratorChars(draw_strings_flags_, &text_str);
++    render_text->SetText(text_str);
++    if (range.IsValid()) {
++      render_text->SetDisplayRect(bounds());
++      render_text->ApplyStyle(gfx::TEXT_STYLE_UNDERLINE, true, range);
++    }
++  } else {
++    render_text->SetText(GetText());
++  }
++
+   const bool multiline = GetMultiLine();
+   render_text->SetMultiline(multiline);
+   render_text->SetMaxLines(multiline ? GetMaxLines() : 0);
+diff --git ui/views/controls/label.h ui/views/controls/label.h
+index 4cb533caa9c7..63eac79c878c 100644
+--- ui/views/controls/label.h
++++ ui/views/controls/label.h
+@@ -177,6 +177,10 @@ class VIEWS_EXPORT Label : public View,
+   gfx::ElideBehavior GetElideBehavior() const;
+   void SetElideBehavior(gfx::ElideBehavior elide_behavior);
+ 
++  // Get or set the flags that control display of accelerator characters.
++  void SetDrawStringsFlags(int flags);
++  int GetDrawStringsFlags() const { return draw_strings_flags_; }
++
+   // Gets/Sets the tooltip text.  Default behavior for a label (single-line) is
+   // to show the full text if it is wider than its bounds.  Calling this
+   // overrides the default behavior and lets you set a custom tooltip.  To
+@@ -413,6 +417,7 @@ class VIEWS_EXPORT Label : public View,
+   bool collapse_when_hidden_ = false;
+   int fixed_width_ = 0;
+   int max_width_ = 0;
++  int draw_strings_flags_ = 0;
+ 
+   std::unique_ptr<SelectionController> selection_controller_;
+ 
+diff --git ui/views/controls/menu/menu_controller.cc ui/views/controls/menu/menu_controller.cc
+index d8c09129ad9a..7c28530fa7dd 100644
+--- ui/views/controls/menu/menu_controller.cc
++++ ui/views/controls/menu/menu_controller.cc
+@@ -2638,8 +2638,13 @@ MenuItemView* MenuController::FindNextSelectableMenuItem(
+ 
+ void MenuController::OpenSubmenuChangeSelectionIfCan() {
+   MenuItemView* item = pending_state_.item;
+-  if (!item->HasSubmenu() || !item->GetEnabled())
++  if (!item->HasSubmenu() || !item->GetEnabled() || !item->GetParentMenuItem()) {
++    MenuItemView* submenu_item =
++        item->GetParentMenuItem() ? item->GetParentMenuItem() : item;
++    submenu_item->GetDelegate()->OnUnhandledOpenSubmenu(submenu_item,
++                                                        base::i18n::IsRTL());
+     return;
++  }
+   MenuItemView* to_select = nullptr;
+   if (!item->GetSubmenu()->GetMenuItems().empty())
+     to_select = FindInitialSelectableMenuItem(item, INCREMENT_SELECTION_DOWN);
+@@ -2658,8 +2663,10 @@ void MenuController::OpenSubmenuChangeSelectionIfCan() {
+ void MenuController::CloseSubmenu() {
+   MenuItemView* item = state_.item;
+   DCHECK(item);
+-  if (!item->GetParentMenuItem())
++  if (!item->GetParentMenuItem()) {
++    item->GetDelegate()->OnUnhandledCloseSubmenu(item, base::i18n::IsRTL());
+     return;
++  }
+   if (item->SubmenuIsShowing())
+     SetSelection(item, SELECTION_UPDATE_IMMEDIATELY);
+   else if (item->GetParentMenuItem()->GetParentMenuItem())
+diff --git ui/views/controls/menu/menu_delegate.h ui/views/controls/menu/menu_delegate.h
+index 2a03ff43f4a6..c12d68eb212c 100644
+--- ui/views/controls/menu/menu_delegate.h
++++ ui/views/controls/menu/menu_delegate.h
+@@ -81,6 +81,22 @@ class VIEWS_EXPORT MenuDelegate {
+   // parts of |style| or leave it unmodified.
+   virtual void GetLabelStyle(int id, LabelStyle* style) const;
+ 
++  // Override the text color of a given menu item dependent on the |command_id|
++  // and its |is_hovered| state. |is_minor| will be true for accelerator text.
++  // Returns true if it chooses to override the color.
++  virtual bool GetTextColor(int command_id,
++                            bool is_minor,
++                            bool is_hovered,
++                            SkColor* override_color) const { return false; }
++
++  // Override the background color of a given menu item dependent on the
++  // |command_id| and its |is_hovered| state. Returns true if it chooses to
++  // override the color.
++  virtual bool GetBackgroundColor(int command_id,
++                                  bool is_hovered,
++                                  SkColor* override_color) const
++                                  { return false; }
++
+   // The tooltip shown for the menu item. This is invoked when the user
+   // hovers over the item, and no tooltip text has been set for that item.
+   virtual base::string16 GetTooltipText(int id,
+@@ -207,6 +223,11 @@ class VIEWS_EXPORT MenuDelegate {
+                                        bool* has_mnemonics,
+                                        MenuButton** button);
+ 
++  // Called on unhandled open/close submenu keyboard commands. |is_rtl| will be
++  // true if the menu is displaying a right-to-left language.
++  virtual void OnUnhandledOpenSubmenu(MenuItemView* menu, bool is_rtl) {}
++  virtual void OnUnhandledCloseSubmenu(MenuItemView* menu, bool is_rtl) {}
++
+   // Returns the max width menus can grow to be.
+   virtual int GetMaxWidthForMenu(MenuItemView* menu);
+ 
+diff --git ui/views/controls/menu/menu_item_view.cc ui/views/controls/menu/menu_item_view.cc
+index 6f45e5ac5d1c..d8a0eee3fedd 100644
+--- ui/views/controls/menu/menu_item_view.cc
++++ ui/views/controls/menu/menu_item_view.cc
+@@ -987,6 +987,15 @@ void MenuItemView::PaintBackground(gfx::Canvas* canvas,
+     spilling_rect.set_y(spilling_rect.y() - corner_radius_);
+     spilling_rect.set_height(spilling_rect.height() + corner_radius_);
+     canvas->DrawRoundRect(spilling_rect, corner_radius_, flags);
++    return;
++  }
++
++  MenuDelegate *delegate = GetDelegate();
++  SkColor override_color;
++  if (delegate && delegate->GetBackgroundColor(GetCommand(),
++                                               render_selection,
++                                               &override_color)) {
++    canvas->DrawColor(override_color);
+   } else if (render_selection) {
+     gfx::Rect item_bounds = GetLocalBounds();
+     if (type_ == Type::kActionableSubMenu) {
+@@ -1054,6 +1063,13 @@ void MenuItemView::PaintMinorIconAndText(
+ }
+ 
+ SkColor MenuItemView::GetTextColor(bool minor, bool render_selection) const {
++  SkColor text_color;
++  const MenuDelegate *delegate = GetDelegate();
++  if (delegate && delegate->GetTextColor(GetCommand(), minor, render_selection,
++                                         &text_color)) {
++    return text_color;
++  }
++
+   style::TextContext context =
+       GetMenuController() && GetMenuController()->use_touchable_layout()
+           ? style::CONTEXT_TOUCH_MENU
+diff --git ui/views/controls/menu/menu_model_adapter.cc ui/views/controls/menu/menu_model_adapter.cc
+index 88a868cf6f98..b7c10dc5613c 100644
+--- ui/views/controls/menu/menu_model_adapter.cc
++++ ui/views/controls/menu/menu_model_adapter.cc
+@@ -230,6 +230,77 @@ bool MenuModelAdapter::IsItemChecked(int id) const {
+   return false;
+ }
+ 
++MenuItemView* MenuModelAdapter::GetSiblingMenu(MenuItemView* menu,
++                                               const gfx::Point& screen_point,
++                                               MenuAnchorPosition* anchor,
++                                               bool* has_mnemonics,
++                                               MenuButton** button) {
++  // Look up the menu model for this menu.
++  const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
++      menu_map_.find(menu);
++  if (map_iterator != menu_map_.end()) {
++    map_iterator->second->MouseOutsideMenu(screen_point);
++    return nullptr;
++  }
++
++  NOTREACHED();
++  return nullptr;
++}
++
++void MenuModelAdapter::OnUnhandledOpenSubmenu(MenuItemView* menu,
++                                              bool is_rtl) {
++  // Look up the menu model for this menu.
++  const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
++      menu_map_.find(menu);
++  if (map_iterator != menu_map_.end()) {
++    map_iterator->second->UnhandledOpenSubmenu(is_rtl);
++    return;
++  }
++
++  NOTREACHED();
++}
++
++void MenuModelAdapter::OnUnhandledCloseSubmenu(MenuItemView* menu,
++                                               bool is_rtl) {
++  // Look up the menu model for this menu.
++  const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
++      menu_map_.find(menu);
++  if (map_iterator != menu_map_.end()) {
++    map_iterator->second->UnhandledCloseSubmenu(is_rtl);
++    return;
++  }
++
++  NOTREACHED();
++}
++
++bool MenuModelAdapter::GetTextColor(int command_id,
++                                    bool is_minor,
++                                    bool is_hovered,
++                                    SkColor* override_color) const {
++  ui::MenuModel* model = menu_model_;
++  int index = 0;
++  if (ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model, &index))
++    return model->GetTextColor(index, is_minor, is_hovered, override_color);
++
++  NOTREACHED();
++  return false;
++}
++
++bool MenuModelAdapter::GetBackgroundColor(int command_id,
++                                          bool is_hovered,
++                                          SkColor* override_color) const {
++  if (command_id == -1)
++    return menu_model_->GetBackgroundColor(-1, is_hovered, override_color);
++
++  ui::MenuModel* model = menu_model_;
++  int index = 0;
++  if (ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model, &index))
++    return model->GetBackgroundColor(index, is_hovered, override_color);
++
++  NOTREACHED();
++  return false;
++}
++
+ void MenuModelAdapter::WillShowMenu(MenuItemView* menu) {
+   // Look up the menu model for this menu.
+   const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
+diff --git ui/views/controls/menu/menu_model_adapter.h ui/views/controls/menu/menu_model_adapter.h
+index 78f832fd3acf..cb030c991614 100644
+--- ui/views/controls/menu/menu_model_adapter.h
++++ ui/views/controls/menu/menu_model_adapter.h
+@@ -84,6 +84,20 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate,
+   bool IsCommandEnabled(int id) const override;
+   bool IsCommandVisible(int id) const override;
+   bool IsItemChecked(int id) const override;
++  MenuItemView* GetSiblingMenu(MenuItemView* menu,
++                               const gfx::Point& screen_point,
++                               MenuAnchorPosition* anchor,
++                               bool* has_mnemonics,
++                               MenuButton** button) override;
++  void OnUnhandledOpenSubmenu(MenuItemView* menu, bool is_rtl) override;
++  void OnUnhandledCloseSubmenu(MenuItemView* menu, bool is_rtl) override;
++  bool GetTextColor(int command_id,
++                    bool is_minor,
++                    bool is_hovered,
++                    SkColor* override_color) const override;
++  bool GetBackgroundColor(int command_id,
++                          bool is_hovered,
++                          SkColor* override_color) const override;
+   void WillShowMenu(MenuItemView* menu) override;
+   void WillHideMenu(MenuItemView* menu) override;
+   void OnMenuClosed(MenuItemView* menu) override;
+diff --git ui/views/controls/menu/menu_scroll_view_container.cc ui/views/controls/menu/menu_scroll_view_container.cc
+index 7a9d15cb2066..abfe73a68ace 100644
+--- ui/views/controls/menu/menu_scroll_view_container.cc
++++ ui/views/controls/menu/menu_scroll_view_container.cc
+@@ -189,6 +189,11 @@ MenuScrollViewContainer::MenuScrollViewContainer(SubmenuView* content_view)
+   scroll_down_button_ =
+       AddChildView(std::make_unique<MenuScrollButton>(content_view, false));
+ 
++  SkColor override_color;
++  MenuDelegate* delegate = content_view_->GetMenuItem()->GetDelegate();
++  if (delegate && delegate->GetBackgroundColor(-1, false, &override_color))
++    SetBackground(views::CreateSolidBackground(override_color));
++
+   arrow_ = BubbleBorderTypeFromAnchor(
+       content_view_->GetMenuItem()->GetMenuController()->GetAnchorPosition());
+ 
+diff --git ui/views/test/ui_controls_factory_desktop_aurax11.cc ui/views/test/ui_controls_factory_desktop_aurax11.cc
+index 82a2d7a7827f..082c614e57a9 100644
+--- ui/views/test/ui_controls_factory_desktop_aurax11.cc
++++ ui/views/test/ui_controls_factory_desktop_aurax11.cc
+@@ -139,10 +139,6 @@ class UIControlsDesktopX11 : public UIControlsAura {
+         aura::test::QueryLatestMousePositionRequestInHost(host);
+     host->ConvertPixelsToDIP(&root_current_location);
+ 
+-    auto* screen = views::test::TestDesktopScreenX11::GetInstance();
+-    DCHECK_EQ(screen, display::Screen::GetScreen());
+-    screen->set_cursor_screen_point(gfx::Point(screen_x, screen_y));
+-
+     if (root_location != root_current_location && button_down_mask == 0) {
+       // Move the cursor because EnterNotify/LeaveNotify are generated with the
+       // current mouse position as a result of XGrabPointer()
+diff --git ui/views/view.h ui/views/view.h
+index b1dfe17e2b28..87480a766437 100644
+--- ui/views/view.h
++++ ui/views/view.h
+@@ -23,6 +23,7 @@
+ #include "base/logging.h"
+ #include "base/macros.h"
+ #include "base/memory/ptr_util.h"
++#include "base/supports_user_data.h"
+ #include "build/build_config.h"
+ #include "third_party/skia/include/core/SkPath.h"
+ #include "ui/accessibility/ax_enums.mojom-forward.h"
+@@ -273,6 +274,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
+                           public ui::EventTarget,
+                           public ui::EventHandler,
+                           public ui::PropertyHandler,
++                          public base::SupportsUserData,
+                           public views::metadata::MetaDataProvider {
+  public:
+   using Views = std::vector<View*>;
diff --git a/src/patch/patches/views_widget_180_1481_1565_1677_1749.patch b/src/patch/patches/views_widget_180_1481_1565_1677_1749.patch
new file mode 100644
index 0000000..22d9b37
--- /dev/null
+++ b/src/patch/patches/views_widget_180_1481_1565_1677_1749.patch
@@ -0,0 +1,438 @@
+diff --git content/browser/renderer_host/render_widget_host_view_base.cc content/browser/renderer_host/render_widget_host_view_base.cc
+index 19c6b2e5c705..b48d5c04ae0e 100644
+--- content/browser/renderer_host/render_widget_host_view_base.cc
++++ content/browser/renderer_host/render_widget_host_view_base.cc
+@@ -568,6 +568,14 @@ float RenderWidgetHostViewBase::GetDeviceScaleFactor() {
+   return screen_info.device_scale_factor;
+ }
+ 
++void RenderWidgetHostViewBase::SetHasExternalParent(bool val) {
++  has_external_parent_ = val;
++}
++
++bool RenderWidgetHostViewBase::HasExternalParent() const {
++  return has_external_parent_;
++}
++
+ uint32_t RenderWidgetHostViewBase::RendererFrameNumber() {
+   return renderer_frame_number_;
+ }
+diff --git content/browser/renderer_host/render_widget_host_view_base.h content/browser/renderer_host/render_widget_host_view_base.h
+index 27133fd98354..2552750935af 100644
+--- content/browser/renderer_host/render_widget_host_view_base.h
++++ content/browser/renderer_host/render_widget_host_view_base.h
+@@ -65,6 +65,7 @@ class CursorManager;
+ class MouseWheelPhaseHandler;
+ class RenderWidgetHostImpl;
+ class RenderWidgetHostViewBaseObserver;
++class RenderWidgetHostViewGuest;
+ class SyntheticGestureTarget;
+ class TextInputManager;
+ class TouchSelectionControllerClientManager;
+@@ -82,6 +83,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
+   float current_device_scale_factor() const {
+     return current_device_scale_factor_;
+   }
++  void set_current_device_scale_factor(float scale_factor) {
++    current_device_scale_factor_ = scale_factor;
++  }
+ 
+   // Returns the focused RenderWidgetHost inside this |view|'s RWH.
+   RenderWidgetHostImpl* GetFocusedWidget() const;
+@@ -116,6 +120,8 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
+   void DisableAutoResize(const gfx::Size& new_size) override;
+   bool IsScrollOffsetAtTop() override;
+   float GetDeviceScaleFactor() final;
++  void SetHasExternalParent(bool val) override;
++  bool HasExternalParent() const override;
+   TouchSelectionControllerClientManager*
+   GetTouchSelectionControllerClientManager() override;
+   void SetRecordContentToVisibleTimeRequest(
+@@ -444,6 +450,12 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
+   // helps to position the full screen widget on the correct monitor.
+   virtual void InitAsFullscreen(RenderWidgetHostView* reference_host_view) = 0;
+ 
++  // Perform all the initialization steps necessary for this object to represent
++  // the platform widget owned by |guest_view| and embedded in
++  // |parent_host_view|.
++  virtual void InitAsGuest(RenderWidgetHostView* parent_host_view,
++                           RenderWidgetHostViewGuest* guest_view) {}
++
+   // Sets the cursor for this view to the one associated with the specified
+   // cursor_type.
+   virtual void UpdateCursor(const WebCursor& cursor) = 0;
+@@ -608,6 +620,10 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
+ 
+   bool is_currently_scrolling_viewport_ = false;
+ 
++  // True if the widget has a external parent view/window outside of the
++  // Chromium-controlled view/window hierarchy.
++  bool has_external_parent_ = false;
++
+  private:
+   FRIEND_TEST_ALL_PREFIXES(
+       BrowserSideFlingBrowserTest,
+diff --git content/browser/renderer_host/render_widget_host_view_event_handler.cc content/browser/renderer_host/render_widget_host_view_event_handler.cc
+index 4b2067bc7f26..3f483ff54365 100644
+--- content/browser/renderer_host/render_widget_host_view_event_handler.cc
++++ content/browser/renderer_host/render_widget_host_view_event_handler.cc
+@@ -33,6 +33,10 @@
+ #include "ui/events/keycodes/dom/dom_code.h"
+ #include "ui/touch_selection/touch_selection_controller.h"
+ 
++#if defined(OS_LINUX)
++#include "ui/aura/window_tree_host.h"
++#endif
++
+ #if defined(OS_WIN)
+ #include "content/browser/frame_host/render_frame_host_impl.h"
+ #include "ui/aura/window_tree_host.h"
+@@ -928,6 +932,14 @@ void RenderWidgetHostViewEventHandler::SetKeyboardFocus() {
+         ::SetFocus(hwnd);
+     }
+   }
++#endif
++#if defined(OS_LINUX)
++  if (host_view_->HasExternalParent() &&
++      window_ && window_->delegate()->CanFocus()) {
++    aura::WindowTreeHost* host = window_->GetHost();
++    if (host)
++      host->Show();
++  }
+ #endif
+   // TODO(wjmaclean): can host_ ever be null?
+   if (host_ && set_focus_on_mouse_down_or_key_event_) {
+diff --git content/public/browser/render_widget_host_view.h content/public/browser/render_widget_host_view.h
+index 3e5bf09f1cf7..bc8ea335d501 100644
+--- content/public/browser/render_widget_host_view.h
++++ content/public/browser/render_widget_host_view.h
+@@ -243,6 +243,14 @@ class CONTENT_EXPORT RenderWidgetHostView {
+   // This must always return the same device scale factor as GetScreenInfo.
+   virtual float GetDeviceScaleFactor() = 0;
+ 
++  // Set whether the widget has a external parent view/window outside of the
++  // Chromium-controlled view/window hierarchy.
++  virtual void SetHasExternalParent(bool val) = 0;
++
++  // Returns true if the widget has a external parent view/window outside of the
++  // Chromium-controlled view/window hierarchy.
++  virtual bool HasExternalParent() const = 0;
++
+ #if defined(OS_MACOSX)
+   // Set the view's active state (i.e., tint state of controls).
+   virtual void SetActive(bool active) = 0;
+diff --git ui/base/x/x11_window.cc ui/base/x/x11_window.cc
+index f58ab14a3a07..f8388e7ac048 100644
+--- ui/base/x/x11_window.cc
++++ ui/base/x/x11_window.cc
+@@ -255,8 +255,12 @@ void XWindow::Init(const Configuration& config) {
+   attribute_mask |= CWBorderPixel;
+   swa.border_pixel = 0;
+ 
++  gfx::AcceleratedWidget parent_widget = config.parent_widget;
++  if (parent_widget == gfx::kNullAcceleratedWidget)
++    parent_widget = x_root_window_;
++
+   bounds_in_pixels_ = SanitizeBounds(config.bounds);
+-  xwindow_ = XCreateWindow(xdisplay_, x_root_window_, bounds_in_pixels_.x(),
++  xwindow_ = XCreateWindow(xdisplay_, parent_widget, bounds_in_pixels_.x(),
+                            bounds_in_pixels_.y(), bounds_in_pixels_.width(),
+                            bounds_in_pixels_.height(),
+                            0,  // border width
+diff --git ui/base/x/x11_window.h ui/base/x/x11_window.h
+index d0f852b1d69d..7d3ea8779fd1 100644
+--- ui/base/x/x11_window.h
++++ ui/base/x/x11_window.h
+@@ -19,6 +19,7 @@
+ #include "ui/gfx/geometry/rect.h"
+ #include "ui/gfx/geometry/size.h"
+ #include "ui/gfx/geometry/size_f.h"
++#include "ui/gfx/native_widget_types.h"
+ #include "ui/gfx/x/x11.h"
+ #include "ui/gfx/x/x11_types.h"
+ 
+@@ -86,6 +87,7 @@ class COMPONENT_EXPORT(UI_BASE_X) XWindow {
+     std::string wm_class_name;
+     std::string wm_class_class;
+     std::string wm_role_name;
++    gfx::AcceleratedWidget parent_widget = gfx::kNullAcceleratedWidget;
+   };
+ 
+   XWindow();
+diff --git ui/platform_window/x11/x11_window.cc ui/platform_window/x11/x11_window.cc
+index 98b442ee756d..d3ad9f8995cc 100644
+--- ui/platform_window/x11/x11_window.cc
++++ ui/platform_window/x11/x11_window.cc
+@@ -85,6 +85,7 @@ ui::XWindow::Configuration ConvertInitPropertiesToXWindowConfig(
+   config.wm_class_class = properties.wm_class_class;
+   config.wm_role_name = properties.wm_role_name;
+   config.activatable = properties.activatable;
++  config.parent_widget = properties.parent_widget;
+   config.prefer_dark_theme = properties.prefer_dark_theme;
+   config.background_color = properties.background_color;
+   return config;
+diff --git ui/views/widget/desktop_aura/desktop_screen_win.cc ui/views/widget/desktop_aura/desktop_screen_win.cc
+index 70553b153c44..ecd99bc78373 100644
+--- ui/views/widget/desktop_aura/desktop_screen_win.cc
++++ ui/views/widget/desktop_aura/desktop_screen_win.cc
+@@ -30,6 +30,8 @@ display::Display DesktopScreenWin::GetDisplayMatching(
+ }
+ 
+ HWND DesktopScreenWin::GetHWNDFromNativeView(gfx::NativeView window) const {
++  if (!window)
++    return NULL;
+   aura::WindowTreeHost* host = window->GetHost();
+   return host ? host->GetAcceleratedWidget() : nullptr;
+ }
+diff --git ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
+index 91cfaf77bb31..f369314e02f4 100644
+--- ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
++++ ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
+@@ -348,6 +348,8 @@ void DesktopWindowTreeHostLinux::AddAdditionalInitProperties(
+   properties->wm_class_class = params.wm_class_class;
+   properties->wm_role_name = params.wm_role_name;
+ 
++  properties->parent_widget = params.parent_widget;
++
+   DCHECK(!properties->x11_extension_delegate);
+   properties->x11_extension_delegate = this;
+ }
+diff --git ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+index 6f576d95707d..dacd02f13012 100644
+--- ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
++++ ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+@@ -133,8 +133,12 @@ void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) {
+                         native_widget_delegate_);
+ 
+   HWND parent_hwnd = nullptr;
+-  if (params.parent && params.parent->GetHost())
++  if (params.parent_widget) {
++    parent_hwnd = params.parent_widget;
++    has_external_parent_ = true;
++  } else if (params.parent && params.parent->GetHost()) {
+     parent_hwnd = params.parent->GetHost()->GetAcceleratedWidget();
++  }
+ 
+   remove_standard_frame_ = params.remove_standard_frame;
+   has_non_client_view_ = Widget::RequiresNonClientView(params.type);
+@@ -909,11 +913,15 @@ void DesktopWindowTreeHostWin::HandleFrameChanged() {
+ }
+ 
+ void DesktopWindowTreeHostWin::HandleNativeFocus(HWND last_focused_window) {
+-  // TODO(beng): inform the native_widget_delegate_.
++  // See comments in CefBrowserHostImpl::PlatformSetFocus.
++  if (has_external_parent_ && CanActivate())
++    HandleActivationChanged(true);
+ }
+ 
+ void DesktopWindowTreeHostWin::HandleNativeBlur(HWND focused_window) {
+-  // TODO(beng): inform the native_widget_delegate_.
++  // See comments in CefBrowserHostImpl::PlatformSetFocus.
++  if (has_external_parent_ && CanActivate())
++    HandleActivationChanged(false);
+ }
+ 
+ bool DesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) {
+diff --git ui/views/widget/desktop_aura/desktop_window_tree_host_win.h ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+index 4b217541512c..1940da51d5c6 100644
+--- ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
++++ ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+@@ -288,6 +288,10 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
+   // True if the window should have the frame removed.
+   bool remove_standard_frame_;
+ 
++  // True if the widget has a external parent view/window outside of the
++  // Chromium-controlled view/window hierarchy.
++  bool has_external_parent_ = false;
++
+   // Owned by TooltipController, but we need to forward events to it so we keep
+   // a reference.
+   corewm::TooltipWin* tooltip_;
+diff --git ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+index 2c94941cf273..9512335908cf 100644
+--- ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
++++ ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+@@ -83,6 +83,9 @@ DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() {
+ // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation:
+ 
+ void DesktopWindowTreeHostX11::Init(const Widget::InitParams& params) {
++  if (params.parent_widget != gfx::kNullAcceleratedWidget)
++    has_external_parent_ = true;
++
+   DesktopWindowTreeHostLinux::Init(params);
+ 
+   // Set XEventDelegate to receive selection, drag&drop and raw key events.
+@@ -134,6 +137,18 @@ void DesktopWindowTreeHostX11::EndMoveLoop() {
+ ////////////////////////////////////////////////////////////////////////////////
+ // DesktopWindowTreeHostX11 implementation:
+ 
++gfx::Rect DesktopWindowTreeHostX11::GetWindowBoundsInScreen() const {
++  if (!screen_bounds_.IsEmpty())
++    return screen_bounds_;
++  return DesktopWindowTreeHostLinux::GetWindowBoundsInScreen();
++}
++
++gfx::Point DesktopWindowTreeHostX11::GetLocationOnScreenInPixels() const {
++  if (!screen_bounds_.IsEmpty())
++    return screen_bounds_.origin();
++  return DesktopWindowTreeHostLinux::GetLocationOnScreenInPixels();
++}
++
+ void DesktopWindowTreeHostX11::OnXWindowSelectionEvent(XEvent* xev) {
+   DCHECK(xev);
+   DCHECK(drag_drop_client_);
+diff --git ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+index 53da309c14d3..7a50560c8abc 100644
+--- ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
++++ ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+@@ -31,6 +31,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux,
+       DesktopNativeWidgetAura* desktop_native_widget_aura);
+   ~DesktopWindowTreeHostX11() override;
+ 
++  void set_screen_bounds(const gfx::Rect& bounds) { screen_bounds_ = bounds; }
++
++  // Returns true if the widget has a external parent view/window outside of the
++  // Chromium-controlled view/window hierarchy.
++  bool has_external_parent() const { return has_external_parent_; }
++
+  protected:
+   // Overridden from DesktopWindowTreeHost:
+   void Init(const Widget::InitParams& params) override;
+@@ -42,6 +48,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux,
+       Widget::MoveLoopSource source,
+       Widget::MoveLoopEscapeBehavior escape_behavior) override;
+   void EndMoveLoop() override;
++  gfx::Rect GetWindowBoundsInScreen() const override;
++  gfx::Point GetLocationOnScreenInPixels() const override;
+ 
+  private:
+   friend class DesktopWindowTreeHostX11HighDPITest;
+@@ -56,10 +64,20 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux,
+   // directly. See https://crbug.com/990756.
+   const ui::XWindow* GetXWindow() const;
+ 
++  // Override the screen bounds when the host is a child window.
++  gfx::Rect screen_bounds_;
++
++  // True if the widget has a external parent view/window outside of the
++  // Chromium-controlled view/window hierarchy.
++  bool has_external_parent_ = false;
++
+   DesktopDragDropClientAuraX11* drag_drop_client_ = nullptr;
+ 
+   std::unique_ptr<X11DesktopWindowMoveClient> x11_window_move_client_;
+ 
++  // True if the xwindow has already been destroyed.
++  bool xwindow_destroyed_ = false;
++
+   DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11);
+ };
+ 
+diff --git ui/views/widget/widget.cc ui/views/widget/widget.cc
+index 6472b4210a0a..390758b017be 100644
+--- ui/views/widget/widget.cc
++++ ui/views/widget/widget.cc
+@@ -289,7 +289,8 @@ void Widget::Init(InitParams params) {
+     params.name = params.delegate->GetContentsView()->GetClassName();
+ 
+   params.child |= (params.type == InitParams::TYPE_CONTROL);
+-  is_top_level_ = !params.child;
++  is_top_level_ = !params.child ||
++                  params.parent_widget != gfx::kNullAcceleratedWidget;
+ 
+   if (params.opacity == views::Widget::InitParams::WindowOpacity::kInferred &&
+       params.type != views::Widget::InitParams::TYPE_WINDOW) {
+@@ -371,7 +372,12 @@ void Widget::Init(InitParams params) {
+     }
+   } else if (delegate) {
+     SetContentsView(delegate->GetContentsView());
+-    SetInitialBoundsForFramelessWindow(bounds);
++    if (params.parent_widget != gfx::kNullAcceleratedWidget) {
++      // Set the bounds directly instead of applying an inset.
++      SetBounds(bounds);
++    } else {
++      SetInitialBoundsForFramelessWindow(bounds);
++    }
+   }
+ 
+   observer_manager_.Add(GetNativeTheme());
+@@ -1156,10 +1162,16 @@ void Widget::OnNativeWidgetDestroyed() {
+ }
+ 
+ gfx::Size Widget::GetMinimumSize() const {
++  gfx::Size size;
++  if (widget_delegate_->MaybeGetMinimumSize(&size))
++    return size;
+   return non_client_view_ ? non_client_view_->GetMinimumSize() : gfx::Size();
+ }
+ 
+ gfx::Size Widget::GetMaximumSize() const {
++  gfx::Size size;
++  if (widget_delegate_->MaybeGetMaximumSize(&size))
++    return size;
+   return non_client_view_ ? non_client_view_->GetMaximumSize() : gfx::Size();
+ }
+ 
+diff --git ui/views/widget/widget.h ui/views/widget/widget.h
+index 105fc39ed5ec..a4e227167dbd 100644
+--- ui/views/widget/widget.h
++++ ui/views/widget/widget.h
+@@ -312,6 +312,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
+     // the concept with bubble anchoring a la BubbleDialogDelegateView.
+     gfx::NativeView parent = nullptr;
+ 
++    gfx::AcceleratedWidget parent_widget = gfx::kNullAcceleratedWidget;
++
+     // Specifies the initial bounds of the Widget. Default is empty, which means
+     // the NativeWidget may specify a default size. If the parent is specified,
+     // |bounds| is in the parent's coordinate system. If the parent is not
+diff --git ui/views/widget/widget_delegate.h ui/views/widget/widget_delegate.h
+index a3ec229f11cb..714f608b7b58 100644
+--- ui/views/widget/widget_delegate.h
++++ ui/views/widget/widget_delegate.h
+@@ -202,6 +202,10 @@ class VIEWS_EXPORT WidgetDelegate {
+   // be cycled through with keyboard focus.
+   virtual void GetAccessiblePanes(std::vector<View*>* panes) {}
+ 
++  // CEF supports override of min/max size values.
++  virtual bool MaybeGetMinimumSize(gfx::Size* size) const { return false; }
++  virtual bool MaybeGetMaximumSize(gfx::Size* size) const { return false; }
++
+  protected:
+   virtual ~WidgetDelegate();
+ 
+diff --git ui/views/widget/widget_hwnd_utils.cc ui/views/widget/widget_hwnd_utils.cc
+index 40e66a212e3e..08ee8523ab15 100644
+--- ui/views/widget/widget_hwnd_utils.cc
++++ ui/views/widget/widget_hwnd_utils.cc
+@@ -67,7 +67,7 @@ void CalculateWindowStylesFromInitParams(
+       if (!widget_delegate->CanResize())
+         *style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
+       if (params.remove_standard_frame)
+-        *style &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
++        *style &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION | WS_SYSMENU);
+ 
+       if (native_widget_delegate->IsDialogBox()) {
+         *style |= DS_MODALFRAME;
+diff --git ui/views/win/hwnd_message_handler.cc ui/views/win/hwnd_message_handler.cc
+index e18afe3c8c28..3b8b21eee322 100644
+--- ui/views/win/hwnd_message_handler.cc
++++ ui/views/win/hwnd_message_handler.cc
+@@ -3005,10 +3005,13 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
+   } else if (event.type() == ui::ET_MOUSEWHEEL) {
+     ui::MouseWheelEvent mouse_wheel_event(msg);
+     // Reroute the mouse wheel to the window under the pointer if applicable.
+-    return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) ||
+-            delegate_->HandleMouseEvent(&mouse_wheel_event))
+-               ? 0
+-               : 1;
++    if (ui::RerouteMouseWheel(hwnd(), w_param, l_param) ||
++        delegate_->HandleMouseEvent(&mouse_wheel_event)) {
++      SetMsgHandled(TRUE);
++      return 0;
++    } else {
++      return 1;
++    }
+   }
+ 
+   // Suppress |ET_MOUSE_MOVED| and |ET_MOUSE_DRAGGED| events from WM_MOUSE*
diff --git a/src/patch/patches/viz_osr_2575.patch b/src/patch/patches/viz_osr_2575.patch
new file mode 100644
index 0000000..7afedd6
--- /dev/null
+++ b/src/patch/patches/viz_osr_2575.patch
@@ -0,0 +1,265 @@
+diff --git components/viz/host/host_display_client.cc components/viz/host/host_display_client.cc
+index 3547ee865c22..2215296ffadc 100644
+--- components/viz/host/host_display_client.cc
++++ components/viz/host/host_display_client.cc
+@@ -43,9 +43,14 @@ void HostDisplayClient::OnDisplayReceivedCALayerParams(
+ }
+ #endif
+ 
+-#if defined(OS_WIN)
++void HostDisplayClient::UseProxyOutputDevice(
++    UseProxyOutputDeviceCallback callback) {
++  std::move(callback).Run(false);
++}
++
+ void HostDisplayClient::CreateLayeredWindowUpdater(
+     mojo::PendingReceiver<mojom::LayeredWindowUpdater> receiver) {
++#if defined(OS_WIN)
+   if (!NeedsToUseLayerWindow(widget_)) {
+     DLOG(ERROR) << "HWND shouldn't be using a layered window";
+     return;
+@@ -53,8 +58,8 @@ void HostDisplayClient::CreateLayeredWindowUpdater(
+ 
+   layered_window_updater_ =
+       std::make_unique<LayeredWindowUpdaterImpl>(widget_, std::move(receiver));
+-}
+ #endif
++}
+ 
+ #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ void HostDisplayClient::DidCompleteSwapWithNewSize(const gfx::Size& size) {
+diff --git components/viz/host/host_display_client.h components/viz/host/host_display_client.h
+index cedf833d2358..04456e045304 100644
+--- components/viz/host/host_display_client.h
++++ components/viz/host/host_display_client.h
+@@ -31,17 +31,17 @@ class VIZ_HOST_EXPORT HostDisplayClient : public mojom::DisplayClient {
+   mojo::PendingRemote<mojom::DisplayClient> GetBoundRemote(
+       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ 
+- private:
++ protected:
+   // mojom::DisplayClient implementation:
++  void UseProxyOutputDevice(UseProxyOutputDeviceCallback callback) override;
++
+ #if defined(OS_MACOSX)
+   void OnDisplayReceivedCALayerParams(
+       const gfx::CALayerParams& ca_layer_params) override;
+ #endif
+ 
+-#if defined(OS_WIN)
+   void CreateLayeredWindowUpdater(
+       mojo::PendingReceiver<mojom::LayeredWindowUpdater> receiver) override;
+-#endif
+ 
+ #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+   void DidCompleteSwapWithNewSize(const gfx::Size& size) override;
+diff --git components/viz/host/layered_window_updater_impl.cc components/viz/host/layered_window_updater_impl.cc
+index b04f654fe820..131977a36591 100644
+--- components/viz/host/layered_window_updater_impl.cc
++++ components/viz/host/layered_window_updater_impl.cc
+@@ -44,7 +44,7 @@ void LayeredWindowUpdaterImpl::OnAllocatedSharedMemory(
+   // |region|'s handle will close when it goes out of scope.
+ }
+ 
+-void LayeredWindowUpdaterImpl::Draw(DrawCallback draw_callback) {
++void LayeredWindowUpdaterImpl::Draw(const gfx::Rect& damage_rect, DrawCallback draw_callback) {
+   TRACE_EVENT0("viz", "LayeredWindowUpdaterImpl::Draw");
+ 
+   if (!canvas_) {
+diff --git components/viz/host/layered_window_updater_impl.h components/viz/host/layered_window_updater_impl.h
+index 1026b739d283..fe562ab60ce9 100644
+--- components/viz/host/layered_window_updater_impl.h
++++ components/viz/host/layered_window_updater_impl.h
+@@ -35,7 +35,7 @@ class VIZ_HOST_EXPORT LayeredWindowUpdaterImpl
+   // mojom::LayeredWindowUpdater implementation.
+   void OnAllocatedSharedMemory(const gfx::Size& pixel_size,
+                                base::UnsafeSharedMemoryRegion region) override;
+-  void Draw(DrawCallback draw_callback) override;
++  void Draw(const gfx::Rect& damage_rect, DrawCallback draw_callback) override;
+ 
+  private:
+   const HWND hwnd_;
+diff --git components/viz/service/BUILD.gn components/viz/service/BUILD.gn
+index eb1a0ae5ca25..846c738053b7 100644
+--- components/viz/service/BUILD.gn
++++ components/viz/service/BUILD.gn
+@@ -13,7 +13,10 @@ config("viz_service_implementation") {
+ }
+ 
+ viz_component("service") {
++  never_build_jumbo = true
+   sources = [
++    "//cef/libcef/browser/osr/software_output_device_proxy.cc",
++    "//cef/libcef/browser/osr/software_output_device_proxy.h",
+     "display/bsp_tree.cc",
+     "display/bsp_tree.h",
+     "display/bsp_walk_action.cc",
+diff --git components/viz/service/display_embedder/output_surface_provider_impl.cc components/viz/service/display_embedder/output_surface_provider_impl.cc
+index b4d4b1c1c597..9ce685048ab1 100644
+--- components/viz/service/display_embedder/output_surface_provider_impl.cc
++++ components/viz/service/display_embedder/output_surface_provider_impl.cc
+@@ -13,6 +13,7 @@
+ #include "base/threading/thread_task_runner_handle.h"
+ #include "build/chromecast_buildflags.h"
+ #include "cc/base/switches.h"
++#include "cef/libcef/browser/osr/software_output_device_proxy.h"
+ #include "components/viz/common/display/renderer_settings.h"
+ #include "components/viz/common/frame_sinks/begin_frame_source.h"
+ #include "components/viz/service/display_embedder/gl_output_surface.h"
+@@ -224,6 +225,20 @@ OutputSurfaceProviderImpl::CreateSoftwareOutputDeviceForPlatform(
+   if (headless_)
+     return std::make_unique<SoftwareOutputDevice>();
+ 
++  {
++    mojo::ScopedAllowSyncCallForTesting allow_sync;
++    DCHECK(display_client);
++    bool use_proxy_output_device = false;
++    if (display_client->UseProxyOutputDevice(&use_proxy_output_device) &&
++        use_proxy_output_device) {
++      mojom::LayeredWindowUpdaterPtr layered_window_updater;
++      display_client->CreateLayeredWindowUpdater(
++          mojo::MakeRequest(&layered_window_updater));
++      return std::make_unique<SoftwareOutputDeviceProxy>(
++          std::move(layered_window_updater));
++    }
++  }
++
+ #if defined(OS_WIN)
+   return CreateSoftwareOutputDeviceWin(surface_handle, &output_device_backing_,
+                                        display_client);
+diff --git components/viz/service/display_embedder/software_output_device_win.cc components/viz/service/display_embedder/software_output_device_win.cc
+index 2bb30e5318b6..535535dd6c10 100644
+--- components/viz/service/display_embedder/software_output_device_win.cc
++++ components/viz/service/display_embedder/software_output_device_win.cc
+@@ -188,8 +188,9 @@ void SoftwareOutputDeviceWinProxy::EndPaintDelegated(
+   if (!canvas_)
+     return;
+ 
+-  layered_window_updater_->Draw(base::BindOnce(
+-      &SoftwareOutputDeviceWinProxy::DrawAck, base::Unretained(this)));
++  layered_window_updater_->Draw(
++      damage_rect, base::BindOnce(&SoftwareOutputDeviceWinProxy::DrawAck,
++                                  base::Unretained(this)));
+   waiting_on_draw_ack_ = true;
+ 
+   TRACE_EVENT_ASYNC_BEGIN0("viz", "SoftwareOutputDeviceWinProxy::Draw", this);
+diff --git content/browser/compositor/viz_process_transport_factory.cc content/browser/compositor/viz_process_transport_factory.cc
+index 8e10af98f2b0..d39fc7ec7b4c 100644
+--- content/browser/compositor/viz_process_transport_factory.cc
++++ content/browser/compositor/viz_process_transport_factory.cc
+@@ -404,8 +404,13 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel(
+   compositor_data.display_private.reset();
+   root_params->display_private =
+       compositor_data.display_private.BindNewEndpointAndPassReceiver();
+-  compositor_data.display_client =
+-      std::make_unique<HostDisplayClient>(compositor);
++  if (compositor->delegate()) {
++    compositor_data.display_client =
++        compositor->delegate()->CreateHostDisplayClient();
++  } else {
++    compositor_data.display_client =
++        std::make_unique<HostDisplayClient>(compositor);
++  }
+   root_params->display_client =
+       compositor_data.display_client->GetBoundRemote(resize_task_runner_);
+ 
+diff --git mojo/public/cpp/bindings/sync_call_restrictions.h mojo/public/cpp/bindings/sync_call_restrictions.h
+index 60e114920575..01f2ce164a3b 100644
+--- mojo/public/cpp/bindings/sync_call_restrictions.h
++++ mojo/public/cpp/bindings/sync_call_restrictions.h
+@@ -29,6 +29,7 @@ class Compositor;
+ 
+ namespace viz {
+ class HostFrameSinkManager;
++class GpuDisplayProvider;
+ }
+ 
+ namespace mojo {
+@@ -82,6 +83,8 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) SyncCallRestrictions {
+   // For preventing frame swaps of wrong size during resize on Windows.
+   // (https://crbug.com/811945)
+   friend class ui::Compositor;
++  // For query of whether to use SoftwareOutputDevice or not.
++  friend class viz::GpuDisplayProvider;
+   // END ALLOWED USAGE.
+ 
+ #if ENABLE_SYNC_CALL_RESTRICTIONS
+diff --git services/viz/privileged/mojom/compositing/display_private.mojom services/viz/privileged/mojom/compositing/display_private.mojom
+index d36f62e6ee41..e8ec62cda569 100644
+--- services/viz/privileged/mojom/compositing/display_private.mojom
++++ services/viz/privileged/mojom/compositing/display_private.mojom
+@@ -74,12 +74,14 @@ interface DisplayPrivate {
+ };
+ 
+ interface DisplayClient {
++  [Sync]
++  UseProxyOutputDevice() => (bool success);
++
+   [EnableIf=is_mac]
+   OnDisplayReceivedCALayerParams(gfx.mojom.CALayerParams ca_layer_params);
+ 
+   // Creates a LayeredWindowUpdater implementation to draw into a layered
+   // window.
+-  [EnableIf=is_win]
+   CreateLayeredWindowUpdater(pending_receiver<LayeredWindowUpdater> receiver);
+ 
+   // Notifies that a swap has occurred and provides information about the pixel
+diff --git services/viz/privileged/mojom/compositing/layered_window_updater.mojom services/viz/privileged/mojom/compositing/layered_window_updater.mojom
+index 6b7fbb6cf13d..e2af75168cb9 100644
+--- services/viz/privileged/mojom/compositing/layered_window_updater.mojom
++++ services/viz/privileged/mojom/compositing/layered_window_updater.mojom
+@@ -26,5 +26,5 @@ interface LayeredWindowUpdater {
+   // Draws to the HWND by copying pixels from shared memory. Callback must be
+   // called after draw operation is complete to signal shared memory can be
+   // modified.
+-  Draw() => ();
++  Draw(gfx.mojom.Rect damage_rect) => ();
+ };
+diff --git ui/compositor/compositor.h ui/compositor/compositor.h
+index 3e2d2304faf7..f0029d34f591 100644
+--- ui/compositor/compositor.h
++++ ui/compositor/compositor.h
+@@ -23,7 +23,9 @@
+ #include "cc/trees/layer_tree_host_single_thread_client.h"
+ #include "components/viz/common/frame_sinks/begin_frame_args.h"
+ #include "components/viz/common/surfaces/frame_sink_id.h"
++#include "components/viz/host/host_display_client.h"
+ #include "components/viz/host/host_frame_sink_client.h"
++#include "components/viz/service/display/software_output_device.h"
+ #include "mojo/public/cpp/bindings/pending_remote.h"
+ #include "services/viz/privileged/mojom/compositing/vsync_parameter_observer.mojom-forward.h"
+ #include "third_party/skia/include/core/SkColor.h"
+@@ -122,6 +124,14 @@ class COMPOSITOR_EXPORT ContextFactory {
+   virtual viz::HostFrameSinkManager* GetHostFrameSinkManager() = 0;
+ };
+ 
++class COMPOSITOR_EXPORT CompositorDelegate {
++ public:
++  virtual std::unique_ptr<viz::HostDisplayClient> CreateHostDisplayClient() = 0;
++
++ protected:
++  virtual ~CompositorDelegate() {}
++};
++
+ // Compositor object to take care of GPU painting.
+ // A Browser compositor object is responsible for generating the final
+ // displayable form of pixels comprising a single widget's contents. It draws an
+@@ -155,6 +165,9 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient,
+   // Schedules a redraw of the layer tree associated with this compositor.
+   void ScheduleDraw();
+ 
++  CompositorDelegate* delegate() const { return delegate_; }
++  void SetDelegate(CompositorDelegate* delegate) { delegate_ = delegate; }
++
+   // Sets the root of the layer tree drawn by this Compositor. The root layer
+   // must have no parent. The compositor's root layer is reset if the root layer
+   // is destroyed. NULL can be passed to reset the root layer, in which case the
+@@ -390,6 +403,8 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient,
+ 
+   std::unique_ptr<PendingBeginFrameArgs> pending_begin_frame_args_;
+ 
++  CompositorDelegate* delegate_ = nullptr;
++
+   // The root of the Layer tree drawn by this compositor.
+   Layer* root_layer_ = nullptr;
+ 
diff --git a/src/patch/patches/web_contents_1257_1565.patch b/src/patch/patches/web_contents_1257_1565.patch
new file mode 100644
index 0000000..828a98a
--- /dev/null
+++ b/src/patch/patches/web_contents_1257_1565.patch
@@ -0,0 +1,148 @@
+diff --git content/browser/web_contents/web_contents_impl.cc content/browser/web_contents/web_contents_impl.cc
+index 0fe5a498d698..9d5379624671 100644
+--- content/browser/web_contents/web_contents_impl.cc
++++ content/browser/web_contents/web_contents_impl.cc
+@@ -2069,15 +2069,22 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) {
+   std::string unique_name;
+   frame_tree_.root()->SetFrameName(params.main_frame_name, unique_name);
+ 
+-  WebContentsViewDelegate* delegate =
+-      GetContentClient()->browser()->GetWebContentsViewDelegate(this);
++  if (params.view && params.delegate_view) {
++    view_.reset(params.view);
++    render_view_host_delegate_view_ = params.delegate_view;
++  }
+ 
+-  if (browser_plugin_guest_) {
+-    view_.reset(new WebContentsViewChildFrame(
+-        this, delegate, &render_view_host_delegate_view_));
+-  } else {
+-    view_.reset(CreateWebContentsView(this, delegate,
+-                                      &render_view_host_delegate_view_));
++  if (!view_) {
++    WebContentsViewDelegate* delegate =
++        GetContentClient()->browser()->GetWebContentsViewDelegate(this);
++
++    if (browser_plugin_guest_) {
++      view_.reset(new WebContentsViewChildFrame(
++          this, delegate, &render_view_host_delegate_view_));
++    } else {
++      view_.reset(CreateWebContentsView(this, delegate,
++                                        &render_view_host_delegate_view_));
++    }
+   }
+   CHECK(render_view_host_delegate_view_);
+   CHECK(view_.get());
+@@ -2897,6 +2904,15 @@ RenderFrameHostDelegate* WebContentsImpl::CreateNewWindow(
+   // objects.
+   create_params.renderer_initiated_creation = !is_new_browsing_instance;
+ 
++  if (delegate_) {
++    delegate_->GetCustomWebContentsView(this,
++                                        params.target_url,
++                                        render_process_id,
++                                        opener->GetRoutingID(),
++                                        &create_params.view,
++                                        &create_params.delegate_view);
++  }
++
+   std::unique_ptr<WebContentsImpl> new_contents;
+   if (!is_guest) {
+     create_params.context = view_->GetNativeView();
+@@ -6370,6 +6386,9 @@ void WebContentsImpl::SetFocusedFrame(FrameTreeNode* node,
+     // This is an outermost WebContents.
+     SetAsFocusedWebContentsIfNecessary();
+   }
++
++  for (auto& observer : observers_)
++    observer.OnFrameFocused(node->current_frame_host());
+ }
+ 
+ void WebContentsImpl::DidCallFocus() {
+diff --git content/public/browser/web_contents.cc content/public/browser/web_contents.cc
+index d1d8ff84e1d2..e7cca94f8647 100644
+--- content/public/browser/web_contents.cc
++++ content/public/browser/web_contents.cc
+@@ -28,6 +28,8 @@ WebContents::CreateParams::CreateParams(BrowserContext* context,
+       renderer_initiated_creation(false),
+       desired_renderer_state(kOkayToHaveRendererProcess),
+       starting_sandbox_flags(blink::mojom::WebSandboxFlags::kNone),
++      view(nullptr),
++      delegate_view(nullptr),
+       is_never_visible(false) {}
+ 
+ WebContents::CreateParams::CreateParams(const CreateParams& other) = default;
+diff --git content/public/browser/web_contents.h content/public/browser/web_contents.h
+index b471bd1f78c6..19fb0c70ac94 100644
+--- content/public/browser/web_contents.h
++++ content/public/browser/web_contents.h
+@@ -84,9 +84,11 @@ class BrowserPluginGuestDelegate;
+ class InterstitialPage;
+ class RenderFrameHost;
+ class RenderViewHost;
++class RenderViewHostDelegateView;
+ class RenderWidgetHost;
+ class RenderWidgetHostView;
+ class WebContentsDelegate;
++class WebContentsView;
+ class WebUI;
+ struct CustomContextMenuContext;
+ struct DropData;
+@@ -214,6 +216,10 @@ class WebContents : public PageNavigator,
+     // Sandboxing flags set on the new WebContents.
+     blink::mojom::WebSandboxFlags starting_sandbox_flags;
+ 
++    // Optionally specify the view and delegate view.
++    content::WebContentsView* view;
++    content::RenderViewHostDelegateView* delegate_view;
++
+     // Value used to set the last time the WebContents was made active, this is
+     // the value that'll be returned by GetLastActiveTime(). If this is left
+     // default initialized then the value is not passed on to the WebContents
+diff --git content/public/browser/web_contents_delegate.h content/public/browser/web_contents_delegate.h
+index d10b0f458416..84e09b266d48 100644
+--- content/public/browser/web_contents_delegate.h
++++ content/public/browser/web_contents_delegate.h
+@@ -59,10 +59,12 @@ class ColorChooser;
+ class FileSelectListener;
+ class JavaScriptDialogManager;
+ class RenderFrameHost;
++class RenderViewHostDelegateView;
+ class RenderWidgetHost;
+ class SessionStorageNamespace;
+ class SiteInstance;
+ class WebContentsImpl;
++class WebContentsView;
+ struct ContextMenuParams;
+ struct DropData;
+ struct MediaPlayerWatchTime;
+@@ -327,6 +329,14 @@ class CONTENT_EXPORT WebContentsDelegate {
+       const std::string& partition_id,
+       SessionStorageNamespace* session_storage_namespace);
+ 
++  virtual void GetCustomWebContentsView(
++      WebContents* web_contents,
++      const GURL& target_url,
++      int opener_render_process_id,
++      int opener_render_frame_id,
++      content::WebContentsView** view,
++      content::RenderViewHostDelegateView** delegate_view) {}
++
+   // Notifies the delegate about the creation of a new WebContents. This
+   // typically happens when popups are created.
+   virtual void WebContentsCreated(WebContents* source_contents,
+diff --git content/public/browser/web_contents_observer.h content/public/browser/web_contents_observer.h
+index 0b9a8c72a426..1c6ca61171cd 100644
+--- content/public/browser/web_contents_observer.h
++++ content/public/browser/web_contents_observer.h
+@@ -582,6 +582,10 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener {
+   // WebContents has gained/lost focus.
+   virtual void OnFocusChangedInPage(FocusedNodeDetails* details) {}
+ 
++  // Notification that |render_frame_host| for this WebContents has gained
++  // focus.
++  virtual void OnFrameFocused(RenderFrameHost* render_frame_host) {}
++
+   // Notifies that the manifest URL for the main frame changed to
+   // |manifest_url|. This will be invoked when a document with a manifest loads
+   // or when the manifest URL changes (possibly to nothing). It is not invoked
diff --git a/src/patch/patches/web_url_loader_cancel_1617042.patch b/src/patch/patches/web_url_loader_cancel_1617042.patch
new file mode 100644
index 0000000..43f93a8
--- /dev/null
+++ b/src/patch/patches/web_url_loader_cancel_1617042.patch
@@ -0,0 +1,36 @@
+diff --git content/renderer/loader/web_url_loader_impl.h content/renderer/loader/web_url_loader_impl.h
+index 7b00faa7376d..62345e79ebac 100644
+--- content/renderer/loader/web_url_loader_impl.h
++++ content/renderer/loader/web_url_loader_impl.h
+@@ -88,6 +88,7 @@ class CONTENT_EXPORT WebURLLoaderImpl : public blink::WebURLLoader {
+       bool download_to_network_cache_only,
+       bool no_mime_sniffing,
+       blink::WebURLLoaderClient* client) override;
++  void Cancel() override;
+   void SetDefersLoading(bool value) override;
+   void DidChangePriority(blink::WebURLRequest::Priority new_priority,
+                          int intra_priority_value) override;
+@@ -98,8 +99,6 @@ class CONTENT_EXPORT WebURLLoaderImpl : public blink::WebURLLoader {
+   class RequestPeerImpl;
+   class SinkPeer;
+ 
+-  void Cancel();
+-
+   scoped_refptr<Context> context_;
+ 
+   DISALLOW_COPY_AND_ASSIGN(WebURLLoaderImpl);
+diff --git third_party/blink/public/platform/web_url_loader.h third_party/blink/public/platform/web_url_loader.h
+index 7f1b0efcdff9..24b5561d24d8 100644
+--- third_party/blink/public/platform/web_url_loader.h
++++ third_party/blink/public/platform/web_url_loader.h
+@@ -91,6 +91,10 @@ class WebURLLoader {
+       bool no_mime_sniffing,
+       WebURLLoaderClient*) = 0;
+ 
++  // Cancels an asynchronous load.  This will appear as a load error to
++  // the client.
++  virtual void Cancel() {}
++
+   // Suspends/resumes an asynchronous load.
+   virtual void SetDefersLoading(bool) = 0;
+ 
diff --git a/src/patch/patches/webkit_plugin_info_2015.patch b/src/patch/patches/webkit_plugin_info_2015.patch
new file mode 100644
index 0000000..97640bd
--- /dev/null
+++ b/src/patch/patches/webkit_plugin_info_2015.patch
@@ -0,0 +1,227 @@
+diff --git third_party/blink/public/mojom/plugins/plugin_registry.mojom third_party/blink/public/mojom/plugins/plugin_registry.mojom
+index ff7a8ed89e94..77f44956ff22 100644
+--- third_party/blink/public/mojom/plugins/plugin_registry.mojom
++++ third_party/blink/public/mojom/plugins/plugin_registry.mojom
+@@ -36,5 +36,5 @@ interface PluginRegistry {
+   //
+   // TODO(crbug.com/850278): We shouldn't rely on the renderer to tell us the main frame origin.
+   [Sync]
+-  GetPlugins(bool refresh, url.mojom.Origin main_frame_origin) => (array<PluginInfo> plugins);
++  GetPlugins(bool refresh, bool is_main_frame, url.mojom.Origin main_frame_origin) => (array<PluginInfo> plugins);
+ };
+diff --git third_party/blink/public/platform/platform.h third_party/blink/public/platform/platform.h
+index c7bc68000ad0..45798fbcf914 100644
+--- third_party/blink/public/platform/platform.h
++++ third_party/blink/public/platform/platform.h
+@@ -667,6 +667,11 @@ class BLINK_PLATFORM_EXPORT Platform {
+   // runs during Chromium's build step).
+   virtual bool IsTakingV8ContextSnapshot() { return false; }
+ 
++  // DevTools ------------------------------------------------------------
++
++  virtual void DevToolsAgentAttached() {}
++  virtual void DevToolsAgentDetached() {}
++
+  private:
+   static void InitializeCommon(Platform* platform,
+                                std::unique_ptr<Thread> main_thread);
+diff --git third_party/blink/renderer/core/dom/document_init.cc third_party/blink/renderer/core/dom/document_init.cc
+index c9c761dd34e6..75afc167a60e 100644
+--- third_party/blink/renderer/core/dom/document_init.cc
++++ third_party/blink/renderer/core/dom/document_init.cc
+@@ -200,11 +200,11 @@ DocumentInit& DocumentInit::WithTypeFrom(const String& type) {
+     if (GetFrame()->IsMainFrame()) {
+       scoped_refptr<const SecurityOrigin> origin =
+           SecurityOrigin::Create(Url());
+-      plugin_data = GetFrame()->GetPage()->GetPluginData(origin.get());
++      plugin_data = GetFrame()->GetPage()->GetPluginData(true, origin.get());
+     } else {
+       auto* top_security_origin =
+           GetFrame()->Tree().Top().GetSecurityContext()->GetSecurityOrigin();
+-      plugin_data = GetFrame()->GetPage()->GetPluginData(top_security_origin);
++      plugin_data = GetFrame()->GetPage()->GetPluginData(false, top_security_origin);
+     }
+   }
+ 
+diff --git third_party/blink/renderer/core/frame/local_frame.cc third_party/blink/renderer/core/frame/local_frame.cc
+index 3c78d0089938..2f7cb6d27eea 100644
+--- third_party/blink/renderer/core/frame/local_frame.cc
++++ third_party/blink/renderer/core/frame/local_frame.cc
+@@ -1331,7 +1331,7 @@ WebContentSettingsClient* LocalFrame::GetContentSettingsClient() {
+ PluginData* LocalFrame::GetPluginData() const {
+   if (!Loader().AllowPlugins(kNotAboutToInstantiatePlugin))
+     return nullptr;
+-  return GetPage()->GetPluginData(
++  return GetPage()->GetPluginData(IsMainFrame(),
+       Tree().Top().GetSecurityContext()->GetSecurityOrigin());
+ }
+ 
+diff --git third_party/blink/renderer/core/inspector/devtools_session.cc third_party/blink/renderer/core/inspector/devtools_session.cc
+index 4ee6a0ea5689..8b6d5b1a6e06 100644
+--- third_party/blink/renderer/core/inspector/devtools_session.cc
++++ third_party/blink/renderer/core/inspector/devtools_session.cc
+@@ -8,6 +8,7 @@
+ #include <utility>
+ #include <vector>
+ 
++#include "third_party/blink/public/platform/platform.h"
+ #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
+ #include "third_party/blink/renderer/core/frame/local_frame.h"
+ #include "third_party/blink/renderer/core/inspector/devtools_agent.h"
+@@ -142,6 +143,7 @@ DevToolsSession::DevToolsSession(
+     for (wtf_size_t i = 0; i < agents_.size(); i++)
+       agents_[i]->Restore();
+   }
++  Platform::Current()->DevToolsAgentAttached();
+ }
+ 
+ DevToolsSession::~DevToolsSession() {
+@@ -182,6 +184,7 @@ void DevToolsSession::Detach() {
+   agents_.clear();
+   v8_session_.reset();
+   agent_->client_->DebuggerTaskFinished();
++  Platform::Current()->DevToolsAgentDetached();
+ }
+ 
+ void DevToolsSession::DispatchProtocolCommand(
+diff --git third_party/blink/renderer/core/page/page.cc third_party/blink/renderer/core/page/page.cc
+index 50e76a0e0a63..e3e7dcaa5352 100644
+--- third_party/blink/renderer/core/page/page.cc
++++ third_party/blink/renderer/core/page/page.cc
+@@ -194,7 +194,8 @@ Page::Page(PageClients& page_clients)
+           MakeGarbageCollected<OverscrollController>(GetVisualViewport(),
+                                                      GetChromeClient())),
+       link_highlight_(MakeGarbageCollected<LinkHighlight>(*this)),
+-      plugin_data_(nullptr),
++      plugin_data_main_frame_(nullptr),
++      plugin_data_sub_frame_(nullptr),
+       // TODO(pdr): Initialize |validation_message_client_| lazily.
+       validation_message_client_(
+           MakeGarbageCollected<ValidationMessageClientImpl>(*this)),
+@@ -379,21 +380,41 @@ void Page::InitialStyleChanged() {
+   }
+ }
+ 
+-PluginData* Page::GetPluginData(const SecurityOrigin* main_frame_origin) {
+-  if (!plugin_data_)
+-    plugin_data_ = MakeGarbageCollected<PluginData>();
++PluginData* Page::GetPluginData(bool is_main_frame,
++                                const SecurityOrigin* main_frame_origin) {
++  if (is_main_frame) {
++    if (!plugin_data_main_frame_)
++      plugin_data_main_frame_ = MakeGarbageCollected<PluginData>();
+ 
+-  if (!plugin_data_->Origin() ||
+-      !main_frame_origin->IsSameOriginWith(plugin_data_->Origin()))
+-    plugin_data_->UpdatePluginList(main_frame_origin);
+ 
+-  return plugin_data_.Get();
++    if (!plugin_data_main_frame_->Origin() ||
++        !main_frame_origin->IsSameOriginWith(
++            plugin_data_main_frame_->Origin())) {
++      plugin_data_main_frame_->UpdatePluginList(true, main_frame_origin);
++    }
++
++    return plugin_data_main_frame_.Get();
++  } else {
++    if (!plugin_data_sub_frame_)
++      plugin_data_sub_frame_ = MakeGarbageCollected<PluginData>();
++
++    if (!plugin_data_sub_frame_->Origin() ||
++        !main_frame_origin->IsSameOriginWith(
++            plugin_data_sub_frame_->Origin())) {
++      plugin_data_sub_frame_->UpdatePluginList(false, main_frame_origin);
++    }
++
++    return plugin_data_sub_frame_.Get();
++  }
+ }
+ 
+ void Page::ResetPluginData() {
+   for (Page* page : AllPages()) {
+-    if (page->plugin_data_) {
+-      page->plugin_data_->ResetPluginData();
++    if (page->plugin_data_main_frame_ || page->plugin_data_sub_frame_) {
++      if (page->plugin_data_main_frame_)
++        page->plugin_data_main_frame_->ResetPluginData();
++      if (page->plugin_data_sub_frame_)
++        page->plugin_data_sub_frame_->ResetPluginData();
+       page->NotifyPluginsChanged();
+     }
+   }
+@@ -906,7 +927,8 @@ void Page::Trace(Visitor* visitor) {
+   visitor->Trace(link_highlight_);
+   visitor->Trace(spatial_navigation_controller_);
+   visitor->Trace(main_frame_);
+-  visitor->Trace(plugin_data_);
++  visitor->Trace(plugin_data_main_frame_);
++  visitor->Trace(plugin_data_sub_frame_);
+   visitor->Trace(validation_message_client_);
+   visitor->Trace(agent_metrics_collector_);
+   visitor->Trace(plugins_changed_observers_);
+diff --git third_party/blink/renderer/core/page/page.h third_party/blink/renderer/core/page/page.h
+index 1074204dbfb6..e00ef8e7393a 100644
+--- third_party/blink/renderer/core/page/page.h
++++ third_party/blink/renderer/core/page/page.h
+@@ -144,7 +144,8 @@ class CORE_EXPORT Page final : public GarbageCollected<Page>,
+   ViewportDescription GetViewportDescription() const;
+ 
+   // Returns the plugin data associated with |main_frame_origin|.
+-  PluginData* GetPluginData(const SecurityOrigin* main_frame_origin);
++  PluginData* GetPluginData(bool is_main_frame,
++                            const SecurityOrigin* main_frame_origin);
+ 
+   // Resets the plugin data for all pages in the renderer process and notifies
+   // PluginsChangedObservers.
+@@ -404,7 +405,8 @@ class CORE_EXPORT Page final : public GarbageCollected<Page>,
+   const Member<LinkHighlight> link_highlight_;
+   Member<SpatialNavigationController> spatial_navigation_controller_;
+ 
+-  Member<PluginData> plugin_data_;
++  Member<PluginData> plugin_data_main_frame_;
++  Member<PluginData> plugin_data_sub_frame_;
+ 
+   Member<ValidationMessageClient> validation_message_client_;
+ 
+diff --git third_party/blink/renderer/core/page/plugin_data.cc third_party/blink/renderer/core/page/plugin_data.cc
+index 9b6a945105c6..63c7c43114bc 100644
+--- third_party/blink/renderer/core/page/plugin_data.cc
++++ third_party/blink/renderer/core/page/plugin_data.cc
+@@ -91,10 +91,12 @@ void PluginData::RefreshBrowserSidePluginCache() {
+   Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
+       registry.BindNewPipeAndPassReceiver());
+   Vector<mojom::blink::PluginInfoPtr> plugins;
+-  registry->GetPlugins(true, SecurityOrigin::CreateUniqueOpaque(), &plugins);
++  registry->GetPlugins(true, true, SecurityOrigin::CreateUniqueOpaque(),
++                       &plugins);
+ }
+ 
+-void PluginData::UpdatePluginList(const SecurityOrigin* main_frame_origin) {
++void PluginData::UpdatePluginList(bool is_main_frame,
++                                  const SecurityOrigin* main_frame_origin) {
+   ResetPluginData();
+   main_frame_origin_ = main_frame_origin;
+ 
+@@ -102,7 +104,7 @@ void PluginData::UpdatePluginList(const SecurityOrigin* main_frame_origin) {
+   Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
+       registry.BindNewPipeAndPassReceiver());
+   Vector<mojom::blink::PluginInfoPtr> plugins;
+-  registry->GetPlugins(false, main_frame_origin_, &plugins);
++  registry->GetPlugins(false, is_main_frame, main_frame_origin_, &plugins);
+   for (const auto& plugin : plugins) {
+     auto* plugin_info = MakeGarbageCollected<PluginInfo>(
+         plugin->name, FilePathToWebString(plugin->filename),
+diff --git third_party/blink/renderer/core/page/plugin_data.h third_party/blink/renderer/core/page/plugin_data.h
+index d0260f9f6b1b..675ba5a7c893 100644
+--- third_party/blink/renderer/core/page/plugin_data.h
++++ third_party/blink/renderer/core/page/plugin_data.h
+@@ -97,7 +97,8 @@ class CORE_EXPORT PluginData final : public GarbageCollected<PluginData> {
+   const HeapVector<Member<PluginInfo>>& Plugins() const { return plugins_; }
+   const HeapVector<Member<MimeClassInfo>>& Mimes() const { return mimes_; }
+   const SecurityOrigin* Origin() const { return main_frame_origin_.get(); }
+-  void UpdatePluginList(const SecurityOrigin* main_frame_origin);
++  void UpdatePluginList(bool is_main_frame,
++                        const SecurityOrigin* main_frame_origin);
+   void ResetPluginData();
+ 
+   bool SupportsMimeType(const String& mime_type) const;
diff --git a/src/patch/patches/webkit_pointer_event_781966.patch b/src/patch/patches/webkit_pointer_event_781966.patch
new file mode 100644
index 0000000..476a576
--- /dev/null
+++ b/src/patch/patches/webkit_pointer_event_781966.patch
@@ -0,0 +1,13 @@
+diff --git third_party/blink/renderer/core/input/pointer_event_manager.cc third_party/blink/renderer/core/input/pointer_event_manager.cc
+index 6a68b124069d..56d4a2bcad3c 100644
+--- third_party/blink/renderer/core/input/pointer_event_manager.cc
++++ third_party/blink/renderer/core/input/pointer_event_manager.cc
+@@ -310,7 +310,7 @@ void PointerEventManager::HandlePointerInterruption(
+   for (auto pointer_event : canceled_pointer_events) {
+     // If we are sending a pointercancel we have sent the pointerevent to some
+     // target before.
+-    DCHECK(element_under_pointer_.Contains(pointer_event->pointerId()));
++    // DCHECK(element_under_pointer_.Contains(pointer_event->pointerId()));
+     Element* target =
+         element_under_pointer_.at(pointer_event->pointerId()).target;
+ 
diff --git a/src/patch/patches/webkit_popups.patch b/src/patch/patches/webkit_popups.patch
new file mode 100644
index 0000000..6734c86
--- /dev/null
+++ b/src/patch/patches/webkit_popups.patch
@@ -0,0 +1,76 @@
+diff --git third_party/blink/public/web/web_view.h third_party/blink/public/web/web_view.h
+index 406c19b10b2e..28d5336e5ab6 100644
+--- third_party/blink/public/web/web_view.h
++++ third_party/blink/public/web/web_view.h
+@@ -372,6 +372,7 @@ class WebView {
+ 
+   // Sets whether select popup menus should be rendered by the browser.
+   BLINK_EXPORT static void SetUseExternalPopupMenus(bool);
++  virtual void SetUseExternalPopupMenusThisInstance(bool) = 0;
+ 
+   // Cancels and hides the current popup (datetime, select...) if any.
+   virtual void CancelPagePopup() = 0;
+diff --git third_party/blink/renderer/core/exported/web_view_impl.cc third_party/blink/renderer/core/exported/web_view_impl.cc
+index 43fc2bb2de69..562952a05f0c 100644
+--- third_party/blink/renderer/core/exported/web_view_impl.cc
++++ third_party/blink/renderer/core/exported/web_view_impl.cc
+@@ -216,8 +216,13 @@ void WebView::SetUseExternalPopupMenus(bool use_external_popup_menus) {
+   g_should_use_external_popup_menus = use_external_popup_menus;
+ }
+ 
+-bool WebViewImpl::UseExternalPopupMenus() {
+-  return g_should_use_external_popup_menus;
++void WebViewImpl::SetUseExternalPopupMenusThisInstance(
++    bool use_external_popup_menus) {
++  should_use_external_popup_menus_ = use_external_popup_menus;
++}
++
++bool WebViewImpl::UseExternalPopupMenus() const {
++  return should_use_external_popup_menus_;
+ }
+ 
+ namespace {
+@@ -280,6 +285,7 @@ WebViewImpl::WebViewImpl(WebViewClient* client,
+       chrome_client_(MakeGarbageCollected<ChromeClientImpl>(this)),
+       minimum_zoom_level_(PageZoomFactorToZoomLevel(kMinimumPageZoomFactor)),
+       maximum_zoom_level_(PageZoomFactorToZoomLevel(kMaximumPageZoomFactor)),
++      should_use_external_popup_menus_(g_should_use_external_popup_menus),
+       does_composite_(does_composite),
+       fullscreen_controller_(std::make_unique<FullscreenController>(this)),
+       receiver_(this,
+diff --git third_party/blink/renderer/core/exported/web_view_impl.h third_party/blink/renderer/core/exported/web_view_impl.h
+index 60cfa746367a..3329a3f4a9b1 100644
+--- third_party/blink/renderer/core/exported/web_view_impl.h
++++ third_party/blink/renderer/core/exported/web_view_impl.h
+@@ -118,7 +118,8 @@ class CORE_EXPORT WebViewImpl final : public WebView,
+   static HashSet<WebViewImpl*>& AllInstances();
+   // Returns true if popup menus should be rendered by the browser, false if
+   // they should be rendered by WebKit (which is the default).
+-  static bool UseExternalPopupMenus();
++  void SetUseExternalPopupMenusThisInstance(bool) override;
++  bool UseExternalPopupMenus() const;
+ 
+   // Returns whether frames under this WebView are backed by a compositor.
+   bool does_composite() const { return does_composite_; }
+@@ -628,6 +629,8 @@ class CORE_EXPORT WebViewImpl final : public WebView,
+   float fake_page_scale_animation_page_scale_factor_ = 0.f;
+   bool fake_page_scale_animation_use_anchor_ = false;
+ 
++  bool should_use_external_popup_menus_;
++
+   float compositor_device_scale_factor_override_ = 0.f;
+   TransformationMatrix device_emulation_transform_;
+ 
+diff --git third_party/blink/renderer/core/page/chrome_client_impl.cc third_party/blink/renderer/core/page/chrome_client_impl.cc
+index 1b430137301a..ee21b1e7bf17 100644
+--- third_party/blink/renderer/core/page/chrome_client_impl.cc
++++ third_party/blink/renderer/core/page/chrome_client_impl.cc
+@@ -819,7 +819,7 @@ bool ChromeClientImpl::HasOpenedPopup() const {
+ PopupMenu* ChromeClientImpl::OpenPopupMenu(LocalFrame& frame,
+                                            HTMLSelectElement& select) {
+   NotifyPopupOpeningObservers();
+-  if (WebViewImpl::UseExternalPopupMenus())
++  if (web_view_->UseExternalPopupMenus())
+     return MakeGarbageCollected<ExternalPopupMenu>(frame, select);
+ 
+   DCHECK(RuntimeEnabledFeatures::PagePopupEnabled());
diff --git a/src/patch/patches/webui_2037.patch b/src/patch/patches/webui_2037.patch
new file mode 100644
index 0000000..3e7f317
--- /dev/null
+++ b/src/patch/patches/webui_2037.patch
@@ -0,0 +1,79 @@
+diff --git chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+index 29953fdc7c55..89dd4e61f66f 100644
+--- chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
++++ chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+@@ -21,6 +21,7 @@
+ #include "base/task/thread_pool.h"
+ #include "build/branding_buildflags.h"
+ #include "build/build_config.h"
++#include "cef/libcef/features/features.h"
+ #include "chrome/browser/browser_process.h"
+ #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
+ #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
+@@ -28,6 +29,7 @@
+ #include "chrome/browser/profiles/profile_manager.h"
+ #include "chrome/browser/sync/profile_sync_service_factory.h"
+ #include "chrome/common/channel_info.h"
++#include "chrome/common/chrome_switches.h"
+ #include "components/prefs/pref_service.h"
+ #include "components/sync/driver/about_sync_util.h"
+ #include "components/sync/driver/sync_service.h"
+@@ -274,7 +276,11 @@ void ChromeInternalLogSource::Fetch(SysLogsSourceCallback callback) {
+   response->emplace(kOsVersionTag, os_version);
+ #endif
+ 
++#if !BUILDFLAG(ENABLE_CEF)
++  // CEF should avoid loading ProfileSyncServiceFactory which depends on a lot
++  // of unnecessary Chrome-specific factories.
+   PopulateSyncLogs(response.get());
++#endif
+   PopulateExtensionInfoLogs(response.get());
+   PopulatePowerApiLogs(response.get());
+   PopulateDataReductionProxyLogs(response.get());
+@@ -364,6 +370,12 @@ void ChromeInternalLogSource::PopulateExtensionInfoLogs(
+   if (!profile)
+     return;
+ 
++  // CEF should avoid accessing ExtensionRegistry when extensions are disabled.
++  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
++      switches::kDisableExtensions)) {
++    return;
++  }
++
+   extensions::ExtensionRegistry* extension_registry =
+       extensions::ExtensionRegistry::Get(profile);
+   std::string extensions_list;
+@@ -453,6 +465,8 @@ void ChromeInternalLogSource::PopulateArcPolicyStatus(
+ #if defined(OS_WIN)
+ void ChromeInternalLogSource::PopulateUsbKeyboardDetected(
+     SystemLogsResponse* response) {
++  // The below call may result in some DLLs being loaded.
++  base::ScopedAllowBlockingForTesting allow_blocking;
+   std::string reason;
+   bool result =
+       base::win::IsKeyboardPresentOnSlate(ui::GetHiddenWindow(), &reason);
+diff --git chrome/browser/memory_details.cc chrome/browser/memory_details.cc
+index d42895d9db3f..74a6d407bbcd 100644
+--- chrome/browser/memory_details.cc
++++ chrome/browser/memory_details.cc
+@@ -18,6 +18,7 @@
+ #include "base/task/thread_pool.h"
+ #include "build/build_config.h"
+ #include "chrome/browser/profiles/profile.h"
++#include "chrome/common/chrome_switches.h"
+ #include "components/nacl/common/nacl_process_type.h"
+ #include "components/strings/grit/components_strings.h"
+ #include "content/public/browser/browser_child_process_host_iterator.h"
+@@ -250,8 +251,11 @@ void MemoryDetails::CollectChildInfoOnUIThread() {
+ 
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
+     // Determine if this is an extension process.
++    // CEF should avoid accessing ExtensionRegistry when extensions are disabled.
+     bool process_is_for_extensions = false;
+-    if (render_process_host) {
++    if (render_process_host &&
++        !base::CommandLine::ForCurrentProcess()->HasSwitch(
++            switches::kDisableExtensions)) {
+       content::BrowserContext* context =
+           render_process_host->GetBrowserContext();
+       extensions::ExtensionRegistry* extension_registry =
diff --git a/src/patch/patches/webview_plugin_2020.patch b/src/patch/patches/webview_plugin_2020.patch
new file mode 100644
index 0000000..66d888f
--- /dev/null
+++ b/src/patch/patches/webview_plugin_2020.patch
@@ -0,0 +1,13 @@
+diff --git chrome/app/generated_resources.grd chrome/app/generated_resources.grd
+index 288c442298f6..27e2608d1531 100644
+--- chrome/app/generated_resources.grd
++++ chrome/app/generated_resources.grd
+@@ -4973,7 +4973,7 @@ Keep your key file in a safe place. You will need it to create new versions of y
+         </message>
+       </if>
+       <message name="IDS_PLUGIN_BLOCKED_BY_POLICY" desc="The placeholder text for a plugin blocked by enterprise policy.">
+-        <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> is blocked by enterprise policy
++        <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> is not allowed
+       </message>
+       <message name="IDS_PLUGIN_BLOCKED_NO_LOADING" desc="The placeholder text for a blocked plugin that cannot be manually loaded by the user.">
+         <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> is blocked
diff --git a/src/patch/patches/win_base_msvc_sandbox.patch b/src/patch/patches/win_base_msvc_sandbox.patch
new file mode 100644
index 0000000..7f14997
--- /dev/null
+++ b/src/patch/patches/win_base_msvc_sandbox.patch
@@ -0,0 +1,25 @@
+diff --git base/values.cc base/values.cc
+index ecca445840a8..9cde6ece0000 100644
+--- base/values.cc
++++ base/values.cc
+@@ -24,20 +24,6 @@
+ 
+ namespace base {
+ 
+-// base::Value must be standard layout to guarantee that writing to
+-// |bool_type_| then reading |type_| is defined behaviour. See:
+-//
+-// [class.union]:
+-//   If a standard-layout union contains several standard-layout structs that
+-//   share a common initial sequence (9.2), and if an object of this
+-//   standard-layout union type contains one of the standard-layout structs,
+-//   it is permitted to inspect the common initial sequence of any of
+-//   standard-layout struct members;
+-//
+-static_assert(std::is_standard_layout<Value>::value,
+-              "base::Value should be a standard-layout C++ class in order "
+-              "to avoid undefined behaviour in its implementation!");
+-
+ static_assert(sizeof(Value::DoubleStorage) == sizeof(double),
+               "The double and DoubleStorage types should have the same size");
+ 
diff --git a/src/patch/patches/win_cpp17_msvc_sandbox_2819.patch b/src/patch/patches/win_cpp17_msvc_sandbox_2819.patch
new file mode 100644
index 0000000..b99a07a
--- /dev/null
+++ b/src/patch/patches/win_cpp17_msvc_sandbox_2819.patch
@@ -0,0 +1,28 @@
+diff --git base/third_party/double_conversion/BUILD.gn base/third_party/double_conversion/BUILD.gn
+index 0083efdcd9c8..6f647c31e28a 100644
+--- base/third_party/double_conversion/BUILD.gn
++++ base/third_party/double_conversion/BUILD.gn
+@@ -9,6 +9,11 @@ config("config") {
+     "-Wno-unused-const-variable",
+     "-Wno-unused-function",
+   ]
++
++  # Build as C++17 to avoid export of templates that should be inlined.
++  if (is_win) {
++    cflags_cc = [ "/std:c++17" ]
++  }
+ }
+ 
+ static_library("double_conversion") {
+diff --git base/win/BUILD.gn base/win/BUILD.gn
+index 33a38267a658..0d54f224d31c 100644
+--- base/win/BUILD.gn
++++ base/win/BUILD.gn
+@@ -31,4 +31,7 @@ static_library("pe_image") {
+     "pe_image.cc",
+     "pe_image.h",
+   ]
++
++  # Build as C++17 to avoid export of templates that should be inlined.
++  cflags_cc = [ "/std:c++17" ]
+ }
diff --git a/src/patch/patches/win_rt_2274.patch b/src/patch/patches/win_rt_2274.patch
new file mode 100644
index 0000000..b4a4123
--- /dev/null
+++ b/src/patch/patches/win_rt_2274.patch
@@ -0,0 +1,17 @@
+diff --git services/service_manager/sandbox/win/sandbox_win.cc services/service_manager/sandbox/win/sandbox_win.cc
+index c369dde34758..1c2dea10764d 100644
+--- services/service_manager/sandbox/win/sandbox_win.cc
++++ services/service_manager/sandbox/win/sandbox_win.cc
+@@ -939,8 +939,11 @@ sandbox::ResultCode SandboxWin::StartSandboxedProcess(
+   }
+   // TODO(wfh): Relax strict handle checks for network process until root cause
+   // for this crash can be resolved. See https://crbug.com/939590.
+-  if (sandbox_type != SandboxType::kNetwork)
++  if (!launcher_process_command_line.HasSwitch("win-rt-app") &&
++      sandbox_type != SandboxType::kNetwork) {
++    // Don't enable this mitigation in WinRT apps. See issue #2274.
+     mitigations |= sandbox::MITIGATION_STRICT_HANDLE_CHECKS;
++  }
+ 
+   result = policy->SetDelayedProcessMitigations(mitigations);
+   if (result != sandbox::SBOX_ALL_OK)
diff --git a/src/patch/patches/win_screenlock_1058556.patch b/src/patch/patches/win_screenlock_1058556.patch
new file mode 100644
index 0000000..b740048
--- /dev/null
+++ b/src/patch/patches/win_screenlock_1058556.patch
@@ -0,0 +1,13 @@
+diff --git content/browser/screenlock_monitor/screenlock_monitor_device_source_win.cc content/browser/screenlock_monitor/screenlock_monitor_device_source_win.cc
+index 1cc7bf44645f..7ec28c21ec1d 100644
+--- content/browser/screenlock_monitor/screenlock_monitor_device_source_win.cc
++++ content/browser/screenlock_monitor/screenlock_monitor_device_source_win.cc
+@@ -48,7 +48,7 @@ bool ScreenlockMonitorDeviceSource::SessionMessageWindow::OnWndProc(
+   if (message == WM_WTSSESSION_CHANGE) {
+     ProcessWTSSessionLockMessage(wparam);
+   }
+-  return true;
++  return false;
+ }
+ 
+ void ScreenlockMonitorDeviceSource::SessionMessageWindow::
diff --git a/src/tests/cefclient/CMakeLists.txt.in b/src/tests/cefclient/CMakeLists.txt.in
new file mode 100644
index 0000000..8a9924e
--- /dev/null
+++ b/src/tests/cefclient/CMakeLists.txt.in
@@ -0,0 +1,315 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+#
+# Source files.
+#
+
+# cefclient browser sources.
+{{
+  'prefix': 'cefclient_browser',
+  'set': 'CEFCLIENT_BROWSER_SRCS',
+  'includes': [
+    'shared_sources_browser',
+    'cefclient_sources_browser',
+  ],
+}}
+
+# cefclient common sources.
+{{
+  'prefix': 'cefclient_common',
+  'set': 'CEFCLIENT_COMMON_SRCS',
+  'includes': [
+    'shared_sources_common',
+    'cefclient_sources_common',
+  ],
+}}
+
+# cefclient renderer sources.
+{{
+  'prefix': 'cefclient_renderer',
+  'set': 'CEFCLIENT_RENDERER_SRCS',
+  'includes': [
+    'shared_sources_renderer',
+    'cefclient_sources_renderer',
+  ],
+}}
+
+#cefclient Linux sources
+{{
+  'prefix': 'cefclient_linux',
+  'set': 'CEFCLIENT_LINUX_SRCS',
+  'includes': [
+    'shared_sources_linux',
+    'cefclient_sources_linux',
+  ],
+}}
+
+#cefclient Mac OS X sources
+{{
+  'prefix': 'cefclient_macosx',
+  'set': 'CEFCLIENT_MACOSX_SRCS',
+  'includes': [
+    'shared_sources_mac',
+    'cefclient_sources_mac',
+  ],
+}}
+
+# cefclient Mac OS X helper sources.
+{{
+  'prefix': 'cefclient_helper',
+  'set': 'CEFCLIENT_MACOSX_HELPER_SRCS',
+  'includes': [
+    'shared_sources_mac_helper',
+  ],
+}}
+
+#cefclient Windows sources
+{{
+  'prefix': 'cefclient_windows',
+  'set': 'CEFCLIENT_WINDOWS_SRCS',
+  'includes': [
+    'shared_sources_win',
+    'cefclient_sources_win',
+  ],
+}}
+
+# cefclient resources.
+{{
+  'prefix': 'cefclient_resources',
+  'set': 'CEFCLIENT_RESOURCES_SRCS',
+  'includes': [
+    'shared_sources_resources',
+    'cefclient_bundle_resources_mac:MACOSX',
+    'cefclient_sources_resources',
+    'cefclient_sources_resources_extensions_set_page_color',
+  ],
+}}
+
+
+#
+# Shared configuration.
+#
+
+# Target executable names.
+set(CEF_TARGET "cefclient")
+if(OS_MACOSX)
+  set(CEF_HELPER_TARGET "cefclient_Helper")
+  set(CEF_HELPER_OUTPUT_NAME "cefclient Helper")
+else()
+  # Logical target used to link the libcef library.
+  ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}")
+endif()
+
+# Determine the target output directory.
+SET_CEF_TARGET_OUT_DIR()
+
+
+#
+# Linux configuration.
+#
+
+if(OS_LINUX)
+  # All sources required by the "cefclient" target. Generates an executable that
+  # is used for all processes.
+  set(CEFCLIENT_SRCS
+    ${CEFCLIENT_BROWSER_SRCS}
+    ${CEFCLIENT_COMMON_SRCS}
+    ${CEFCLIENT_RENDERER_SRCS}
+    ${CEFCLIENT_RESOURCES_SRCS}
+    ${CEFCLIENT_LINUX_SRCS}
+    )
+
+  # Find required libraries and update compiler/linker variables.
+  FIND_LINUX_LIBRARIES("gmodule-2.0 gtk+-2.0 gthread-2.0 gtk+-unix-print-2.0 gtkglext-1.0 xi")
+
+  # Executable target.
+  add_executable(${CEF_TARGET} ${CEFCLIENT_SRCS})
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
+  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})
+
+  # Set rpath so that libraries can be placed next to the executable.
+  set_target_properties(${CEF_TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN")
+  set_target_properties(${CEF_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
+  set_target_properties(${CEF_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})
+
+  # Copy CEF binary and resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
+  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
+
+  # Copy cefclient resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${CEFCLIENT_RESOURCES_SRCS}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_TARGET_OUT_DIR}/cefclient_files")
+
+  # Set SUID permissions on the chrome-sandbox target.
+  SET_LINUX_SUID_PERMISSIONS("${CEF_TARGET}" "${CEF_TARGET_OUT_DIR}/chrome-sandbox")
+endif()
+
+
+#
+# Mac OS X configuration.
+#
+
+if(OS_MACOSX)
+  option(OPTION_USE_ARC "Build with ARC (automatic Reference Counting) on macOS." ON)
+  if(OPTION_USE_ARC)
+    list(APPEND CEF_COMPILER_FLAGS
+      -fobjc-arc
+      )
+    set_target_properties(${target} PROPERTIES
+      CLANG_ENABLE_OBJC_ARC "YES"
+      )
+  endif()
+
+  # All sources required by the "cefclient" target. Generates an app bundle that
+  # is used only for the browser process.
+  set(CEFCLIENT_SRCS
+    ${CEFCLIENT_BROWSER_SRCS}
+    ${CEFCLIENT_COMMON_SRCS}
+    ${CEFCLIENT_RESOURCES_SRCS}
+    ${CEFCLIENT_MACOSX_SRCS}
+    )
+
+  # All sources required by the "cefclient Helper" target. Generates an app
+  # bundle that is used only for non-browser processes.
+  set(CEFCLIENT_HELPER_SRCS
+    ${CEFCLIENT_COMMON_SRCS}
+    ${CEFCLIENT_RENDERER_SRCS}
+    ${CEFCLIENT_MACOSX_HELPER_SRCS}
+    )
+
+  # Output path for the main app bundle.
+  set(CEF_APP "${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.app")
+
+  # Variables referenced from the main Info.plist file.
+  set(EXECUTABLE_NAME "${CEF_TARGET}")
+  set(PRODUCT_NAME "${CEF_TARGET}")
+
+  if(USE_SANDBOX)
+    # Logical target used to link the cef_sandbox library.
+    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
+  endif()
+
+  # Main app bundle target.
+  add_executable(${CEF_TARGET} MACOSX_BUNDLE ${CEFCLIENT_RESOURCES_SRCS} ${CEFCLIENT_SRCS})
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
+  target_link_libraries(${CEF_TARGET} libcef_dll_wrapper ${CEF_STANDARD_LIBS} "-framework OpenGL")
+  set_target_properties(${CEF_TARGET} PROPERTIES
+    MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/Info.plist
+    )
+
+  # Copy the CEF framework into the Frameworks directory.
+  add_custom_command(
+    TARGET ${CEF_TARGET}
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy_directory
+            "${CEF_BINARY_DIR}/Chromium Embedded Framework.framework"
+            "${CEF_APP}/Contents/Frameworks/Chromium Embedded Framework.framework"
+    VERBATIM
+    )
+
+  # Create the multiple Helper app bundle targets.
+  foreach(_suffix_list ${CEF_HELPER_APP_SUFFIXES})
+    # Convert to a list and extract the suffix values.
+    string(REPLACE ":" ";" _suffix_list ${_suffix_list})
+    list(GET _suffix_list 0 _name_suffix)
+    list(GET _suffix_list 1 _target_suffix)
+    list(GET _suffix_list 2 _plist_suffix)
+
+    # Define Helper target and output names.
+    set(_helper_target "${CEF_HELPER_TARGET}${_target_suffix}")
+    set(_helper_output_name "${CEF_HELPER_OUTPUT_NAME}${_name_suffix}")
+
+    # Create Helper-specific variants of the helper-Info.plist file. Do this
+    # manually because the configure_file command (which is executed as part of
+    # MACOSX_BUNDLE_INFO_PLIST) uses global env variables and would insert the
+    # wrong values with multiple targets.
+    set(_helper_info_plist "${CMAKE_CURRENT_BINARY_DIR}/helper-Info${_target_suffix}.plist")
+    file(READ "${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/helper-Info.plist" _plist_contents)
+    string(REPLACE "\${EXECUTABLE_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
+    string(REPLACE "\${PRODUCT_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
+    string(REPLACE "\${BUNDLE_ID_SUFFIX}" "${_plist_suffix}" _plist_contents ${_plist_contents})
+    file(WRITE ${_helper_info_plist} ${_plist_contents})
+
+    # Create Helper executable target.
+    add_executable(${_helper_target} MACOSX_BUNDLE ${CEFCLIENT_HELPER_SRCS})
+    SET_EXECUTABLE_TARGET_PROPERTIES(${_helper_target})
+    add_dependencies(${_helper_target} libcef_dll_wrapper)
+    target_link_libraries(${_helper_target} libcef_dll_wrapper ${CEF_STANDARD_LIBS})
+    set_target_properties(${_helper_target} PROPERTIES
+      MACOSX_BUNDLE_INFO_PLIST ${_helper_info_plist}
+      OUTPUT_NAME ${_helper_output_name}
+      )
+
+    if(USE_SANDBOX)
+      target_link_libraries(${_helper_target} cef_sandbox_lib)
+    endif()
+
+    # Add the Helper as a dependency of the main executable target.
+    add_dependencies(${CEF_TARGET} "${_helper_target}")
+
+    # Copy the Helper app bundle into the Frameworks directory.
+    add_custom_command(
+      TARGET ${CEF_TARGET}
+      POST_BUILD
+      COMMAND ${CMAKE_COMMAND} -E copy_directory
+              "${CEF_TARGET_OUT_DIR}/${_helper_output_name}.app"
+              "${CEF_APP}/Contents/Frameworks/${_helper_output_name}.app"
+      VERBATIM
+      )
+  endforeach()
+
+  # Manually process and copy over resource files.
+  # The Xcode generator can support this via the set_target_properties RESOURCE
+  # directive but that doesn't properly handle nested resource directories.
+  # Remove these prefixes from input file paths.
+  set(PREFIXES
+    "resources/mac/"
+    "resources/"
+    "../shared/resources/"
+    )
+  COPY_MACOSX_RESOURCES("${CEFCLIENT_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
+endif()
+
+
+#
+# Windows configuration.
+#
+
+if(OS_WINDOWS)
+  # All sources required by the "cefclient" target. Generates an executable that
+  # is used for all processes.
+  set(CEFCLIENT_SRCS
+    ${CEFCLIENT_BROWSER_SRCS}
+    ${CEFCLIENT_COMMON_SRCS}
+    ${CEFCLIENT_RENDERER_SRCS}
+    ${CEFCLIENT_RESOURCES_SRCS}
+    ${CEFCLIENT_WINDOWS_SRCS}
+    )
+
+  # Executable target.
+  add_executable(${CEF_TARGET} WIN32 ${CEFCLIENT_SRCS})
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
+  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS} d3d11.lib glu32.lib imm32.lib opengl32.lib)
+
+  if(USE_ATL)
+    # Required by VS2013 to link accessibility API functions.
+    target_link_libraries(${CEF_TARGET} oleacc.lib)
+  endif()
+
+  if(USE_SANDBOX)
+    # Logical target used to link the cef_sandbox library.
+    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
+    target_link_libraries(${CEF_TARGET} cef_sandbox_lib ${CEF_SANDBOX_STANDARD_LIBS})
+  endif()
+
+  # Add the custom manifest files to the executable.
+  ADD_WINDOWS_MANIFEST("${CMAKE_CURRENT_SOURCE_DIR}/resources/win" "${CEF_TARGET}" "exe")
+
+  # Copy CEF binary and resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
+  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
+endif()
diff --git a/src/tests/cefclient/browser/binding_test.cc b/src/tests/cefclient/browser/binding_test.cc
new file mode 100644
index 0000000..297ebb3
--- /dev/null
+++ b/src/tests/cefclient/browser/binding_test.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/binding_test.h"
+
+#include <algorithm>
+#include <string>
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace binding_test {
+
+namespace {
+
+const char kTestUrlPath[] = "/binding";
+const char kTestMessageName[] = "BindingTest";
+
+// Handle messages in the browser process.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  Handler() {}
+
+  // Called due to cefQuery execution in binding.html.
+  virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id,
+                       const CefString& request,
+                       bool persistent,
+                       CefRefPtr<Callback> callback) OVERRIDE {
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (!test_runner::IsTestURL(url, kTestUrlPath))
+      return false;
+
+    const std::string& message_name = request;
+    if (message_name.find(kTestMessageName) == 0) {
+      // Reverse the string and return.
+      std::string result = message_name.substr(sizeof(kTestMessageName));
+      std::reverse(result.begin(), result.end());
+      callback->Success(result);
+      return true;
+    }
+
+    return false;
+  }
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace binding_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/binding_test.h b/src/tests/cefclient/browser/binding_test.h
new file mode 100644
index 0000000..9692694
--- /dev/null
+++ b/src/tests/cefclient/browser/binding_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BINDING_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BINDING_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace binding_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace binding_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BINDING_TEST_H_
diff --git a/src/tests/cefclient/browser/browser_window.cc b/src/tests/cefclient/browser/browser_window.cc
new file mode 100644
index 0000000..ffe67ed
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/browser_window.h"
+
+#include "include/base/cef_bind.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+BrowserWindow::BrowserWindow(Delegate* delegate)
+    : delegate_(delegate), is_closing_(false) {
+  DCHECK(delegate_);
+}
+
+void BrowserWindow::SetDeviceScaleFactor(float device_scale_factor) {}
+
+float BrowserWindow::GetDeviceScaleFactor() const {
+  return 1.0f;
+}
+
+CefRefPtr<CefBrowser> BrowserWindow::GetBrowser() const {
+  REQUIRE_MAIN_THREAD();
+  return browser_;
+}
+
+bool BrowserWindow::IsClosing() const {
+  REQUIRE_MAIN_THREAD();
+  return is_closing_;
+}
+
+void BrowserWindow::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!browser_);
+  browser_ = browser;
+
+  delegate_->OnBrowserCreated(browser);
+}
+
+void BrowserWindow::OnBrowserClosing(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK_EQ(browser->GetIdentifier(), browser_->GetIdentifier());
+  is_closing_ = true;
+
+  delegate_->OnBrowserWindowClosing();
+}
+
+void BrowserWindow::OnBrowserClosed(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  if (browser_.get()) {
+    DCHECK_EQ(browser->GetIdentifier(), browser_->GetIdentifier());
+    browser_ = nullptr;
+  }
+
+  client_handler_->DetachDelegate();
+  client_handler_ = nullptr;
+
+  // |this| may be deleted.
+  delegate_->OnBrowserWindowDestroyed();
+}
+
+void BrowserWindow::OnSetAddress(const std::string& url) {
+  REQUIRE_MAIN_THREAD();
+  delegate_->OnSetAddress(url);
+}
+
+void BrowserWindow::OnSetTitle(const std::string& title) {
+  REQUIRE_MAIN_THREAD();
+  delegate_->OnSetTitle(title);
+}
+
+void BrowserWindow::OnSetFullscreen(bool fullscreen) {
+  REQUIRE_MAIN_THREAD();
+  delegate_->OnSetFullscreen(fullscreen);
+}
+
+void BrowserWindow::OnAutoResize(const CefSize& new_size) {
+  REQUIRE_MAIN_THREAD();
+  delegate_->OnAutoResize(new_size);
+}
+
+void BrowserWindow::OnSetLoadingState(bool isLoading,
+                                      bool canGoBack,
+                                      bool canGoForward) {
+  REQUIRE_MAIN_THREAD();
+  delegate_->OnSetLoadingState(isLoading, canGoBack, canGoForward);
+}
+
+void BrowserWindow::OnSetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  REQUIRE_MAIN_THREAD();
+  delegate_->OnSetDraggableRegions(regions);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/browser_window.h b/src/tests/cefclient/browser/browser_window.h
new file mode 100644
index 0000000..a003ede
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_H_
+#pragma once
+
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_browser.h"
+#include "tests/cefclient/browser/client_handler.h"
+#include "tests/cefclient/browser/client_types.h"
+
+namespace client {
+
+// Represents a native child window hosting a single browser instance. The
+// methods of this class must be called on the main thread unless otherwise
+// indicated.
+class BrowserWindow : public ClientHandler::Delegate {
+ public:
+  // This interface is implemented by the owner of the BrowserWindow. The
+  // methods of this class will be called on the main thread.
+  class Delegate {
+   public:
+    // Called when the browser has been created.
+    virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) = 0;
+
+    // Called when the BrowserWindow is closing.
+    virtual void OnBrowserWindowClosing() {}
+
+    // Called when the BrowserWindow has been destroyed.
+    virtual void OnBrowserWindowDestroyed() = 0;
+
+    // Set the window URL address.
+    virtual void OnSetAddress(const std::string& url) = 0;
+
+    // Set the window title.
+    virtual void OnSetTitle(const std::string& title) = 0;
+
+    // Set fullscreen mode.
+    virtual void OnSetFullscreen(bool fullscreen) = 0;
+
+    // Auto-resize contents.
+    virtual void OnAutoResize(const CefSize& new_size) = 0;
+
+    // Set the loading state.
+    virtual void OnSetLoadingState(bool isLoading,
+                                   bool canGoBack,
+                                   bool canGoForward) = 0;
+
+    // Set the draggable regions.
+    virtual void OnSetDraggableRegions(
+        const std::vector<CefDraggableRegion>& regions) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // Create a new browser and native window.
+  virtual void CreateBrowser(ClientWindowHandle parent_handle,
+                             const CefRect& rect,
+                             const CefBrowserSettings& settings,
+                             CefRefPtr<CefDictionaryValue> extra_info,
+                             CefRefPtr<CefRequestContext> request_context) = 0;
+
+  // Retrieve the configuration that will be used when creating a popup window.
+  // The popup browser will initially be parented to |temp_handle| which should
+  // be a pre-existing hidden window. The native window will be created later
+  // after the browser has been created. This method will be called on the
+  // browser process UI thread.
+  virtual void GetPopupConfig(CefWindowHandle temp_handle,
+                              CefWindowInfo& windowInfo,
+                              CefRefPtr<CefClient>& client,
+                              CefBrowserSettings& settings) = 0;
+
+  // Show the popup window with correct parent and bounds in parent coordinates.
+  virtual void ShowPopup(ClientWindowHandle parent_handle,
+                         int x,
+                         int y,
+                         size_t width,
+                         size_t height) = 0;
+
+  // Show the window.
+  virtual void Show() = 0;
+
+  // Hide the window.
+  virtual void Hide() = 0;
+
+  // Set the window bounds in parent coordinates.
+  virtual void SetBounds(int x, int y, size_t width, size_t height) = 0;
+
+  // Set focus to the window.
+  virtual void SetFocus(bool focus) = 0;
+
+  // Set the device scale factor. Only used in combination with off-screen
+  // rendering.
+  virtual void SetDeviceScaleFactor(float device_scale_factor);
+
+  // Returns the device scale factor. Only used in combination with off-screen
+  // rendering.
+  virtual float GetDeviceScaleFactor() const;
+
+  // Returns the window handle.
+  virtual ClientWindowHandle GetWindowHandle() const = 0;
+
+  // Returns the browser owned by the window.
+  CefRefPtr<CefBrowser> GetBrowser() const;
+
+  // Returns true if the browser is closing.
+  bool IsClosing() const;
+
+ protected:
+  // Allow deletion via scoped_ptr only.
+  friend struct base::DefaultDeleter<BrowserWindow>;
+
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object.
+  explicit BrowserWindow(Delegate* delegate);
+
+  // ClientHandler::Delegate methods.
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBrowserClosing(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBrowserClosed(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnSetAddress(const std::string& url) OVERRIDE;
+  void OnSetTitle(const std::string& title) OVERRIDE;
+  void OnSetFullscreen(bool fullscreen) OVERRIDE;
+  void OnAutoResize(const CefSize& new_size) OVERRIDE;
+  void OnSetLoadingState(bool isLoading,
+                         bool canGoBack,
+                         bool canGoForward) OVERRIDE;
+  void OnSetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) OVERRIDE;
+
+  Delegate* delegate_;
+  CefRefPtr<CefBrowser> browser_;
+  CefRefPtr<ClientHandler> client_handler_;
+  bool is_closing_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrowserWindow);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_H_
diff --git a/src/tests/cefclient/browser/browser_window_osr_gtk.cc b/src/tests/cefclient/browser/browser_window_osr_gtk.cc
new file mode 100644
index 0000000..242b312
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_osr_gtk.cc
@@ -0,0 +1,2172 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/browser_window_osr_gtk.h"
+
+#include <GL/gl.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkgl.h>
+
+#define XK_3270  // for XK_3270_BackTab
+#include <X11/XF86keysym.h>
+#include <X11/Xcursor/Xcursor.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/keysym.h>
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/cefclient/browser/util_gtk.h"
+#include "tests/shared/browser/geometry_util.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+namespace {
+
+// Major opcode of XInputExtension, or -1 if XInput 2.2 is not available.
+int g_xinput_extension = -1;
+
+// Static BrowserWindowOsrGtk::EventFilter needs to forward touch events
+// to correct browser, so we maintain a vector of all windows.
+std::vector<BrowserWindowOsrGtk*> g_browser_windows;
+
+bool IsTouchAvailable() {
+  return g_xinput_extension != -1;
+}
+
+int GetCefStateModifiers(guint state) {
+  int modifiers = 0;
+  if (state & GDK_SHIFT_MASK)
+    modifiers |= EVENTFLAG_SHIFT_DOWN;
+  if (state & GDK_LOCK_MASK)
+    modifiers |= EVENTFLAG_CAPS_LOCK_ON;
+  if (state & GDK_CONTROL_MASK)
+    modifiers |= EVENTFLAG_CONTROL_DOWN;
+  if (state & GDK_MOD1_MASK)
+    modifiers |= EVENTFLAG_ALT_DOWN;
+  if (state & GDK_BUTTON1_MASK)
+    modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
+  if (state & GDK_BUTTON2_MASK)
+    modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
+  if (state & GDK_BUTTON3_MASK)
+    modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
+  return modifiers;
+}
+
+int GetCefStateModifiers(XIModifierState mods, XIButtonState buttons) {
+  guint state = mods.effective;
+  if (buttons.mask_len >= 1) {
+    if (XIMaskIsSet(buttons.mask, 1))
+      state |= GDK_BUTTON1_MASK;
+    if (XIMaskIsSet(buttons.mask, 2))
+      state |= GDK_BUTTON2_MASK;
+    if (XIMaskIsSet(buttons.mask, 3))
+      state |= GDK_BUTTON3_MASK;
+  }
+
+  return GetCefStateModifiers(state);
+}
+
+// From ui/events/keycodes/keyboard_codes_posix.h.
+enum KeyboardCode {
+  VKEY_BACK = 0x08,
+  VKEY_TAB = 0x09,
+  VKEY_BACKTAB = 0x0A,
+  VKEY_CLEAR = 0x0C,
+  VKEY_RETURN = 0x0D,
+  VKEY_SHIFT = 0x10,
+  VKEY_CONTROL = 0x11,
+  VKEY_MENU = 0x12,
+  VKEY_PAUSE = 0x13,
+  VKEY_CAPITAL = 0x14,
+  VKEY_KANA = 0x15,
+  VKEY_HANGUL = 0x15,
+  VKEY_JUNJA = 0x17,
+  VKEY_FINAL = 0x18,
+  VKEY_HANJA = 0x19,
+  VKEY_KANJI = 0x19,
+  VKEY_ESCAPE = 0x1B,
+  VKEY_CONVERT = 0x1C,
+  VKEY_NONCONVERT = 0x1D,
+  VKEY_ACCEPT = 0x1E,
+  VKEY_MODECHANGE = 0x1F,
+  VKEY_SPACE = 0x20,
+  VKEY_PRIOR = 0x21,
+  VKEY_NEXT = 0x22,
+  VKEY_END = 0x23,
+  VKEY_HOME = 0x24,
+  VKEY_LEFT = 0x25,
+  VKEY_UP = 0x26,
+  VKEY_RIGHT = 0x27,
+  VKEY_DOWN = 0x28,
+  VKEY_SELECT = 0x29,
+  VKEY_PRINT = 0x2A,
+  VKEY_EXECUTE = 0x2B,
+  VKEY_SNAPSHOT = 0x2C,
+  VKEY_INSERT = 0x2D,
+  VKEY_DELETE = 0x2E,
+  VKEY_HELP = 0x2F,
+  VKEY_0 = 0x30,
+  VKEY_1 = 0x31,
+  VKEY_2 = 0x32,
+  VKEY_3 = 0x33,
+  VKEY_4 = 0x34,
+  VKEY_5 = 0x35,
+  VKEY_6 = 0x36,
+  VKEY_7 = 0x37,
+  VKEY_8 = 0x38,
+  VKEY_9 = 0x39,
+  VKEY_A = 0x41,
+  VKEY_B = 0x42,
+  VKEY_C = 0x43,
+  VKEY_D = 0x44,
+  VKEY_E = 0x45,
+  VKEY_F = 0x46,
+  VKEY_G = 0x47,
+  VKEY_H = 0x48,
+  VKEY_I = 0x49,
+  VKEY_J = 0x4A,
+  VKEY_K = 0x4B,
+  VKEY_L = 0x4C,
+  VKEY_M = 0x4D,
+  VKEY_N = 0x4E,
+  VKEY_O = 0x4F,
+  VKEY_P = 0x50,
+  VKEY_Q = 0x51,
+  VKEY_R = 0x52,
+  VKEY_S = 0x53,
+  VKEY_T = 0x54,
+  VKEY_U = 0x55,
+  VKEY_V = 0x56,
+  VKEY_W = 0x57,
+  VKEY_X = 0x58,
+  VKEY_Y = 0x59,
+  VKEY_Z = 0x5A,
+  VKEY_LWIN = 0x5B,
+  VKEY_COMMAND = VKEY_LWIN,  // Provide the Mac name for convenience.
+  VKEY_RWIN = 0x5C,
+  VKEY_APPS = 0x5D,
+  VKEY_SLEEP = 0x5F,
+  VKEY_NUMPAD0 = 0x60,
+  VKEY_NUMPAD1 = 0x61,
+  VKEY_NUMPAD2 = 0x62,
+  VKEY_NUMPAD3 = 0x63,
+  VKEY_NUMPAD4 = 0x64,
+  VKEY_NUMPAD5 = 0x65,
+  VKEY_NUMPAD6 = 0x66,
+  VKEY_NUMPAD7 = 0x67,
+  VKEY_NUMPAD8 = 0x68,
+  VKEY_NUMPAD9 = 0x69,
+  VKEY_MULTIPLY = 0x6A,
+  VKEY_ADD = 0x6B,
+  VKEY_SEPARATOR = 0x6C,
+  VKEY_SUBTRACT = 0x6D,
+  VKEY_DECIMAL = 0x6E,
+  VKEY_DIVIDE = 0x6F,
+  VKEY_F1 = 0x70,
+  VKEY_F2 = 0x71,
+  VKEY_F3 = 0x72,
+  VKEY_F4 = 0x73,
+  VKEY_F5 = 0x74,
+  VKEY_F6 = 0x75,
+  VKEY_F7 = 0x76,
+  VKEY_F8 = 0x77,
+  VKEY_F9 = 0x78,
+  VKEY_F10 = 0x79,
+  VKEY_F11 = 0x7A,
+  VKEY_F12 = 0x7B,
+  VKEY_F13 = 0x7C,
+  VKEY_F14 = 0x7D,
+  VKEY_F15 = 0x7E,
+  VKEY_F16 = 0x7F,
+  VKEY_F17 = 0x80,
+  VKEY_F18 = 0x81,
+  VKEY_F19 = 0x82,
+  VKEY_F20 = 0x83,
+  VKEY_F21 = 0x84,
+  VKEY_F22 = 0x85,
+  VKEY_F23 = 0x86,
+  VKEY_F24 = 0x87,
+  VKEY_NUMLOCK = 0x90,
+  VKEY_SCROLL = 0x91,
+  VKEY_LSHIFT = 0xA0,
+  VKEY_RSHIFT = 0xA1,
+  VKEY_LCONTROL = 0xA2,
+  VKEY_RCONTROL = 0xA3,
+  VKEY_LMENU = 0xA4,
+  VKEY_RMENU = 0xA5,
+  VKEY_BROWSER_BACK = 0xA6,
+  VKEY_BROWSER_FORWARD = 0xA7,
+  VKEY_BROWSER_REFRESH = 0xA8,
+  VKEY_BROWSER_STOP = 0xA9,
+  VKEY_BROWSER_SEARCH = 0xAA,
+  VKEY_BROWSER_FAVORITES = 0xAB,
+  VKEY_BROWSER_HOME = 0xAC,
+  VKEY_VOLUME_MUTE = 0xAD,
+  VKEY_VOLUME_DOWN = 0xAE,
+  VKEY_VOLUME_UP = 0xAF,
+  VKEY_MEDIA_NEXT_TRACK = 0xB0,
+  VKEY_MEDIA_PREV_TRACK = 0xB1,
+  VKEY_MEDIA_STOP = 0xB2,
+  VKEY_MEDIA_PLAY_PAUSE = 0xB3,
+  VKEY_MEDIA_LAUNCH_MAIL = 0xB4,
+  VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
+  VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
+  VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
+  VKEY_OEM_1 = 0xBA,
+  VKEY_OEM_PLUS = 0xBB,
+  VKEY_OEM_COMMA = 0xBC,
+  VKEY_OEM_MINUS = 0xBD,
+  VKEY_OEM_PERIOD = 0xBE,
+  VKEY_OEM_2 = 0xBF,
+  VKEY_OEM_3 = 0xC0,
+  VKEY_OEM_4 = 0xDB,
+  VKEY_OEM_5 = 0xDC,
+  VKEY_OEM_6 = 0xDD,
+  VKEY_OEM_7 = 0xDE,
+  VKEY_OEM_8 = 0xDF,
+  VKEY_OEM_102 = 0xE2,
+  VKEY_OEM_103 = 0xE3,  // GTV KEYCODE_MEDIA_REWIND
+  VKEY_OEM_104 = 0xE4,  // GTV KEYCODE_MEDIA_FAST_FORWARD
+  VKEY_PROCESSKEY = 0xE5,
+  VKEY_PACKET = 0xE7,
+  VKEY_DBE_SBCSCHAR = 0xF3,
+  VKEY_DBE_DBCSCHAR = 0xF4,
+  VKEY_ATTN = 0xF6,
+  VKEY_CRSEL = 0xF7,
+  VKEY_EXSEL = 0xF8,
+  VKEY_EREOF = 0xF9,
+  VKEY_PLAY = 0xFA,
+  VKEY_ZOOM = 0xFB,
+  VKEY_NONAME = 0xFC,
+  VKEY_PA1 = 0xFD,
+  VKEY_OEM_CLEAR = 0xFE,
+  VKEY_UNKNOWN = 0,
+
+  // POSIX specific VKEYs. Note that as of Windows SDK 7.1, 0x97-9F, 0xD8-DA,
+  // and 0xE8 are unassigned.
+  VKEY_WLAN = 0x97,
+  VKEY_POWER = 0x98,
+  VKEY_BRIGHTNESS_DOWN = 0xD8,
+  VKEY_BRIGHTNESS_UP = 0xD9,
+  VKEY_KBD_BRIGHTNESS_DOWN = 0xDA,
+  VKEY_KBD_BRIGHTNESS_UP = 0xE8,
+
+  // Windows does not have a specific key code for AltGr. We use the unused 0xE1
+  // (VK_OEM_AX) code to represent AltGr, matching the behaviour of Firefox on
+  // Linux.
+  VKEY_ALTGR = 0xE1,
+  // Windows does not have a specific key code for Compose. We use the unused
+  // 0xE6 (VK_ICO_CLEAR) code to represent Compose.
+  VKEY_COMPOSE = 0xE6,
+};
+
+// From ui/events/keycodes/keyboard_code_conversion_x.cc.
+// Gdk key codes (e.g. GDK_BackSpace) and X keysyms (e.g. XK_BackSpace) share
+// the same values.
+KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym) {
+  switch (keysym) {
+    case XK_BackSpace:
+      return VKEY_BACK;
+    case XK_Delete:
+    case XK_KP_Delete:
+      return VKEY_DELETE;
+    case XK_Tab:
+    case XK_KP_Tab:
+    case XK_ISO_Left_Tab:
+    case XK_3270_BackTab:
+      return VKEY_TAB;
+    case XK_Linefeed:
+    case XK_Return:
+    case XK_KP_Enter:
+    case XK_ISO_Enter:
+      return VKEY_RETURN;
+    case XK_Clear:
+    case XK_KP_Begin:  // NumPad 5 without Num Lock, for crosbug.com/29169.
+      return VKEY_CLEAR;
+    case XK_KP_Space:
+    case XK_space:
+      return VKEY_SPACE;
+    case XK_Home:
+    case XK_KP_Home:
+      return VKEY_HOME;
+    case XK_End:
+    case XK_KP_End:
+      return VKEY_END;
+    case XK_Page_Up:
+    case XK_KP_Page_Up:  // aka XK_KP_Prior
+      return VKEY_PRIOR;
+    case XK_Page_Down:
+    case XK_KP_Page_Down:  // aka XK_KP_Next
+      return VKEY_NEXT;
+    case XK_Left:
+    case XK_KP_Left:
+      return VKEY_LEFT;
+    case XK_Right:
+    case XK_KP_Right:
+      return VKEY_RIGHT;
+    case XK_Down:
+    case XK_KP_Down:
+      return VKEY_DOWN;
+    case XK_Up:
+    case XK_KP_Up:
+      return VKEY_UP;
+    case XK_Escape:
+      return VKEY_ESCAPE;
+    case XK_Kana_Lock:
+    case XK_Kana_Shift:
+      return VKEY_KANA;
+    case XK_Hangul:
+      return VKEY_HANGUL;
+    case XK_Hangul_Hanja:
+      return VKEY_HANJA;
+    case XK_Kanji:
+      return VKEY_KANJI;
+    case XK_Henkan:
+      return VKEY_CONVERT;
+    case XK_Muhenkan:
+      return VKEY_NONCONVERT;
+    case XK_Zenkaku_Hankaku:
+      return VKEY_DBE_DBCSCHAR;
+    case XK_A:
+    case XK_a:
+      return VKEY_A;
+    case XK_B:
+    case XK_b:
+      return VKEY_B;
+    case XK_C:
+    case XK_c:
+      return VKEY_C;
+    case XK_D:
+    case XK_d:
+      return VKEY_D;
+    case XK_E:
+    case XK_e:
+      return VKEY_E;
+    case XK_F:
+    case XK_f:
+      return VKEY_F;
+    case XK_G:
+    case XK_g:
+      return VKEY_G;
+    case XK_H:
+    case XK_h:
+      return VKEY_H;
+    case XK_I:
+    case XK_i:
+      return VKEY_I;
+    case XK_J:
+    case XK_j:
+      return VKEY_J;
+    case XK_K:
+    case XK_k:
+      return VKEY_K;
+    case XK_L:
+    case XK_l:
+      return VKEY_L;
+    case XK_M:
+    case XK_m:
+      return VKEY_M;
+    case XK_N:
+    case XK_n:
+      return VKEY_N;
+    case XK_O:
+    case XK_o:
+      return VKEY_O;
+    case XK_P:
+    case XK_p:
+      return VKEY_P;
+    case XK_Q:
+    case XK_q:
+      return VKEY_Q;
+    case XK_R:
+    case XK_r:
+      return VKEY_R;
+    case XK_S:
+    case XK_s:
+      return VKEY_S;
+    case XK_T:
+    case XK_t:
+      return VKEY_T;
+    case XK_U:
+    case XK_u:
+      return VKEY_U;
+    case XK_V:
+    case XK_v:
+      return VKEY_V;
+    case XK_W:
+    case XK_w:
+      return VKEY_W;
+    case XK_X:
+    case XK_x:
+      return VKEY_X;
+    case XK_Y:
+    case XK_y:
+      return VKEY_Y;
+    case XK_Z:
+    case XK_z:
+      return VKEY_Z;
+
+    case XK_0:
+    case XK_1:
+    case XK_2:
+    case XK_3:
+    case XK_4:
+    case XK_5:
+    case XK_6:
+    case XK_7:
+    case XK_8:
+    case XK_9:
+      return static_cast<KeyboardCode>(VKEY_0 + (keysym - XK_0));
+
+    case XK_parenright:
+      return VKEY_0;
+    case XK_exclam:
+      return VKEY_1;
+    case XK_at:
+      return VKEY_2;
+    case XK_numbersign:
+      return VKEY_3;
+    case XK_dollar:
+      return VKEY_4;
+    case XK_percent:
+      return VKEY_5;
+    case XK_asciicircum:
+      return VKEY_6;
+    case XK_ampersand:
+      return VKEY_7;
+    case XK_asterisk:
+      return VKEY_8;
+    case XK_parenleft:
+      return VKEY_9;
+
+    case XK_KP_0:
+    case XK_KP_1:
+    case XK_KP_2:
+    case XK_KP_3:
+    case XK_KP_4:
+    case XK_KP_5:
+    case XK_KP_6:
+    case XK_KP_7:
+    case XK_KP_8:
+    case XK_KP_9:
+      return static_cast<KeyboardCode>(VKEY_NUMPAD0 + (keysym - XK_KP_0));
+
+    case XK_multiply:
+    case XK_KP_Multiply:
+      return VKEY_MULTIPLY;
+    case XK_KP_Add:
+      return VKEY_ADD;
+    case XK_KP_Separator:
+      return VKEY_SEPARATOR;
+    case XK_KP_Subtract:
+      return VKEY_SUBTRACT;
+    case XK_KP_Decimal:
+      return VKEY_DECIMAL;
+    case XK_KP_Divide:
+      return VKEY_DIVIDE;
+    case XK_KP_Equal:
+    case XK_equal:
+    case XK_plus:
+      return VKEY_OEM_PLUS;
+    case XK_comma:
+    case XK_less:
+      return VKEY_OEM_COMMA;
+    case XK_minus:
+    case XK_underscore:
+      return VKEY_OEM_MINUS;
+    case XK_greater:
+    case XK_period:
+      return VKEY_OEM_PERIOD;
+    case XK_colon:
+    case XK_semicolon:
+      return VKEY_OEM_1;
+    case XK_question:
+    case XK_slash:
+      return VKEY_OEM_2;
+    case XK_asciitilde:
+    case XK_quoteleft:
+      return VKEY_OEM_3;
+    case XK_bracketleft:
+    case XK_braceleft:
+      return VKEY_OEM_4;
+    case XK_backslash:
+    case XK_bar:
+      return VKEY_OEM_5;
+    case XK_bracketright:
+    case XK_braceright:
+      return VKEY_OEM_6;
+    case XK_quoteright:
+    case XK_quotedbl:
+      return VKEY_OEM_7;
+    case XK_ISO_Level5_Shift:
+      return VKEY_OEM_8;
+    case XK_Shift_L:
+    case XK_Shift_R:
+      return VKEY_SHIFT;
+    case XK_Control_L:
+    case XK_Control_R:
+      return VKEY_CONTROL;
+    case XK_Meta_L:
+    case XK_Meta_R:
+    case XK_Alt_L:
+    case XK_Alt_R:
+      return VKEY_MENU;
+    case XK_ISO_Level3_Shift:
+      return VKEY_ALTGR;
+    case XK_Multi_key:
+      return VKEY_COMPOSE;
+    case XK_Pause:
+      return VKEY_PAUSE;
+    case XK_Caps_Lock:
+      return VKEY_CAPITAL;
+    case XK_Num_Lock:
+      return VKEY_NUMLOCK;
+    case XK_Scroll_Lock:
+      return VKEY_SCROLL;
+    case XK_Select:
+      return VKEY_SELECT;
+    case XK_Print:
+      return VKEY_PRINT;
+    case XK_Execute:
+      return VKEY_EXECUTE;
+    case XK_Insert:
+    case XK_KP_Insert:
+      return VKEY_INSERT;
+    case XK_Help:
+      return VKEY_HELP;
+    case XK_Super_L:
+      return VKEY_LWIN;
+    case XK_Super_R:
+      return VKEY_RWIN;
+    case XK_Menu:
+      return VKEY_APPS;
+    case XK_F1:
+    case XK_F2:
+    case XK_F3:
+    case XK_F4:
+    case XK_F5:
+    case XK_F6:
+    case XK_F7:
+    case XK_F8:
+    case XK_F9:
+    case XK_F10:
+    case XK_F11:
+    case XK_F12:
+    case XK_F13:
+    case XK_F14:
+    case XK_F15:
+    case XK_F16:
+    case XK_F17:
+    case XK_F18:
+    case XK_F19:
+    case XK_F20:
+    case XK_F21:
+    case XK_F22:
+    case XK_F23:
+    case XK_F24:
+      return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_F1));
+    case XK_KP_F1:
+    case XK_KP_F2:
+    case XK_KP_F3:
+    case XK_KP_F4:
+      return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_KP_F1));
+
+    case XK_guillemotleft:
+    case XK_guillemotright:
+    case XK_degree:
+    // In the case of canadian multilingual keyboard layout, VKEY_OEM_102 is
+    // assigned to ugrave key.
+    case XK_ugrave:
+    case XK_Ugrave:
+    case XK_brokenbar:
+      return VKEY_OEM_102;  // international backslash key in 102 keyboard.
+
+    // When evdev is in use, /usr/share/X11/xkb/symbols/inet maps F13-18 keys
+    // to the special XF86XK symbols to support Microsoft Ergonomic keyboards:
+    // https://bugs.freedesktop.org/show_bug.cgi?id=5783
+    // In Chrome, we map these X key symbols back to F13-18 since we don't have
+    // VKEYs for these XF86XK symbols.
+    case XF86XK_Tools:
+      return VKEY_F13;
+    case XF86XK_Launch5:
+      return VKEY_F14;
+    case XF86XK_Launch6:
+      return VKEY_F15;
+    case XF86XK_Launch7:
+      return VKEY_F16;
+    case XF86XK_Launch8:
+      return VKEY_F17;
+    case XF86XK_Launch9:
+      return VKEY_F18;
+    case XF86XK_Refresh:
+    case XF86XK_History:
+    case XF86XK_OpenURL:
+    case XF86XK_AddFavorite:
+    case XF86XK_Go:
+    case XF86XK_ZoomIn:
+    case XF86XK_ZoomOut:
+      // ui::AcceleratorGtk tries to convert the XF86XK_ keysyms on Chrome
+      // startup. It's safe to return VKEY_UNKNOWN here since ui::AcceleratorGtk
+      // also checks a Gdk keysym. http://crbug.com/109843
+      return VKEY_UNKNOWN;
+    // For supporting multimedia buttons on a USB keyboard.
+    case XF86XK_Back:
+      return VKEY_BROWSER_BACK;
+    case XF86XK_Forward:
+      return VKEY_BROWSER_FORWARD;
+    case XF86XK_Reload:
+      return VKEY_BROWSER_REFRESH;
+    case XF86XK_Stop:
+      return VKEY_BROWSER_STOP;
+    case XF86XK_Search:
+      return VKEY_BROWSER_SEARCH;
+    case XF86XK_Favorites:
+      return VKEY_BROWSER_FAVORITES;
+    case XF86XK_HomePage:
+      return VKEY_BROWSER_HOME;
+    case XF86XK_AudioMute:
+      return VKEY_VOLUME_MUTE;
+    case XF86XK_AudioLowerVolume:
+      return VKEY_VOLUME_DOWN;
+    case XF86XK_AudioRaiseVolume:
+      return VKEY_VOLUME_UP;
+    case XF86XK_AudioNext:
+      return VKEY_MEDIA_NEXT_TRACK;
+    case XF86XK_AudioPrev:
+      return VKEY_MEDIA_PREV_TRACK;
+    case XF86XK_AudioStop:
+      return VKEY_MEDIA_STOP;
+    case XF86XK_AudioPlay:
+      return VKEY_MEDIA_PLAY_PAUSE;
+    case XF86XK_Mail:
+      return VKEY_MEDIA_LAUNCH_MAIL;
+    case XF86XK_LaunchA:  // F3 on an Apple keyboard.
+      return VKEY_MEDIA_LAUNCH_APP1;
+    case XF86XK_LaunchB:  // F4 on an Apple keyboard.
+    case XF86XK_Calculator:
+      return VKEY_MEDIA_LAUNCH_APP2;
+    case XF86XK_WLAN:
+      return VKEY_WLAN;
+    case XF86XK_PowerOff:
+      return VKEY_POWER;
+    case XF86XK_MonBrightnessDown:
+      return VKEY_BRIGHTNESS_DOWN;
+    case XF86XK_MonBrightnessUp:
+      return VKEY_BRIGHTNESS_UP;
+    case XF86XK_KbdBrightnessDown:
+      return VKEY_KBD_BRIGHTNESS_DOWN;
+    case XF86XK_KbdBrightnessUp:
+      return VKEY_KBD_BRIGHTNESS_UP;
+
+      // TODO(sad): some keycodes are still missing.
+  }
+  return VKEY_UNKNOWN;
+}
+
+// From content/browser/renderer_host/input/web_input_event_util_posix.cc.
+KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) {
+  static const unsigned int kHardwareCodeToGDKKeyval[] = {
+      0,                 // 0x00:
+      0,                 // 0x01:
+      0,                 // 0x02:
+      0,                 // 0x03:
+      0,                 // 0x04:
+      0,                 // 0x05:
+      0,                 // 0x06:
+      0,                 // 0x07:
+      0,                 // 0x08:
+      0,                 // 0x09: GDK_Escape
+      GDK_1,             // 0x0A: GDK_1
+      GDK_2,             // 0x0B: GDK_2
+      GDK_3,             // 0x0C: GDK_3
+      GDK_4,             // 0x0D: GDK_4
+      GDK_5,             // 0x0E: GDK_5
+      GDK_6,             // 0x0F: GDK_6
+      GDK_7,             // 0x10: GDK_7
+      GDK_8,             // 0x11: GDK_8
+      GDK_9,             // 0x12: GDK_9
+      GDK_0,             // 0x13: GDK_0
+      GDK_minus,         // 0x14: GDK_minus
+      GDK_equal,         // 0x15: GDK_equal
+      0,                 // 0x16: GDK_BackSpace
+      0,                 // 0x17: GDK_Tab
+      GDK_q,             // 0x18: GDK_q
+      GDK_w,             // 0x19: GDK_w
+      GDK_e,             // 0x1A: GDK_e
+      GDK_r,             // 0x1B: GDK_r
+      GDK_t,             // 0x1C: GDK_t
+      GDK_y,             // 0x1D: GDK_y
+      GDK_u,             // 0x1E: GDK_u
+      GDK_i,             // 0x1F: GDK_i
+      GDK_o,             // 0x20: GDK_o
+      GDK_p,             // 0x21: GDK_p
+      GDK_bracketleft,   // 0x22: GDK_bracketleft
+      GDK_bracketright,  // 0x23: GDK_bracketright
+      0,                 // 0x24: GDK_Return
+      0,                 // 0x25: GDK_Control_L
+      GDK_a,             // 0x26: GDK_a
+      GDK_s,             // 0x27: GDK_s
+      GDK_d,             // 0x28: GDK_d
+      GDK_f,             // 0x29: GDK_f
+      GDK_g,             // 0x2A: GDK_g
+      GDK_h,             // 0x2B: GDK_h
+      GDK_j,             // 0x2C: GDK_j
+      GDK_k,             // 0x2D: GDK_k
+      GDK_l,             // 0x2E: GDK_l
+      GDK_semicolon,     // 0x2F: GDK_semicolon
+      GDK_apostrophe,    // 0x30: GDK_apostrophe
+      GDK_grave,         // 0x31: GDK_grave
+      0,                 // 0x32: GDK_Shift_L
+      GDK_backslash,     // 0x33: GDK_backslash
+      GDK_z,             // 0x34: GDK_z
+      GDK_x,             // 0x35: GDK_x
+      GDK_c,             // 0x36: GDK_c
+      GDK_v,             // 0x37: GDK_v
+      GDK_b,             // 0x38: GDK_b
+      GDK_n,             // 0x39: GDK_n
+      GDK_m,             // 0x3A: GDK_m
+      GDK_comma,         // 0x3B: GDK_comma
+      GDK_period,        // 0x3C: GDK_period
+      GDK_slash,         // 0x3D: GDK_slash
+      0,                 // 0x3E: GDK_Shift_R
+      0,                 // 0x3F:
+      0,                 // 0x40:
+      0,                 // 0x41:
+      0,                 // 0x42:
+      0,                 // 0x43:
+      0,                 // 0x44:
+      0,                 // 0x45:
+      0,                 // 0x46:
+      0,                 // 0x47:
+      0,                 // 0x48:
+      0,                 // 0x49:
+      0,                 // 0x4A:
+      0,                 // 0x4B:
+      0,                 // 0x4C:
+      0,                 // 0x4D:
+      0,                 // 0x4E:
+      0,                 // 0x4F:
+      0,                 // 0x50:
+      0,                 // 0x51:
+      0,                 // 0x52:
+      0,                 // 0x53:
+      0,                 // 0x54:
+      0,                 // 0x55:
+      0,                 // 0x56:
+      0,                 // 0x57:
+      0,                 // 0x58:
+      0,                 // 0x59:
+      0,                 // 0x5A:
+      0,                 // 0x5B:
+      0,                 // 0x5C:
+      0,                 // 0x5D:
+      0,                 // 0x5E:
+      0,                 // 0x5F:
+      0,                 // 0x60:
+      0,                 // 0x61:
+      0,                 // 0x62:
+      0,                 // 0x63:
+      0,                 // 0x64:
+      0,                 // 0x65:
+      0,                 // 0x66:
+      0,                 // 0x67:
+      0,                 // 0x68:
+      0,                 // 0x69:
+      0,                 // 0x6A:
+      0,                 // 0x6B:
+      0,                 // 0x6C:
+      0,                 // 0x6D:
+      0,                 // 0x6E:
+      0,                 // 0x6F:
+      0,                 // 0x70:
+      0,                 // 0x71:
+      0,                 // 0x72:
+      GDK_Super_L,       // 0x73: GDK_Super_L
+      GDK_Super_R,       // 0x74: GDK_Super_R
+  };
+
+  // |windows_key_code| has to include a valid virtual-key code even when we
+  // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
+  // on the Hebrew layout, |windows_key_code| should be VK_A.
+  // On the other hand, |event->keyval| value depends on the current
+  // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
+  // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
+  // KeyboardCodeFromXKeysym() call returns 0.
+  // To improve compatibilty with Windows, we use |event->hardware_keycode|
+  // for retrieving its Windows key-code for the keys when the
+  // WebCore::windows_key_codeForEvent() call returns 0.
+  // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
+  // objects cannot change because |event->hardware_keycode| doesn't change
+  // even when we change the layout options, e.g. when we swap a control
+  // key and a caps-lock key, GTK doesn't swap their
+  // |event->hardware_keycode| values but swap their |event->keyval| values.
+  KeyboardCode windows_key_code = KeyboardCodeFromXKeysym(event->keyval);
+  if (windows_key_code)
+    return windows_key_code;
+
+  if (event->hardware_keycode < arraysize(kHardwareCodeToGDKKeyval)) {
+    int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode];
+    if (keyval)
+      return KeyboardCodeFromXKeysym(keyval);
+  }
+
+  // This key is one that keyboard-layout drivers cannot change.
+  // Use |event->keyval| to retrieve its |windows_key_code| value.
+  return KeyboardCodeFromXKeysym(event->keyval);
+}
+
+// From content/browser/renderer_host/input/web_input_event_util_posix.cc.
+KeyboardCode GetWindowsKeyCodeWithoutLocation(KeyboardCode key_code) {
+  switch (key_code) {
+    case VKEY_LCONTROL:
+    case VKEY_RCONTROL:
+      return VKEY_CONTROL;
+    case VKEY_LSHIFT:
+    case VKEY_RSHIFT:
+      return VKEY_SHIFT;
+    case VKEY_LMENU:
+    case VKEY_RMENU:
+      return VKEY_MENU;
+    default:
+      return key_code;
+  }
+}
+
+// From content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
+// Gets the corresponding control character of a specified key code. See:
+// http://en.wikipedia.org/wiki/Control_characters
+// We emulate Windows behavior here.
+int GetControlCharacter(KeyboardCode windows_key_code, bool shift) {
+  if (windows_key_code >= VKEY_A && windows_key_code <= VKEY_Z) {
+    // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
+    return windows_key_code - VKEY_A + 1;
+  }
+  if (shift) {
+    // following graphics chars require shift key to input.
+    switch (windows_key_code) {
+      // ctrl-@ maps to \x00 (Null byte)
+      case VKEY_2:
+        return 0;
+      // ctrl-^ maps to \x1E (Record separator, Information separator two)
+      case VKEY_6:
+        return 0x1E;
+      // ctrl-_ maps to \x1F (Unit separator, Information separator one)
+      case VKEY_OEM_MINUS:
+        return 0x1F;
+      // Returns 0 for all other keys to avoid inputting unexpected chars.
+      default:
+        return 0;
+    }
+  } else {
+    switch (windows_key_code) {
+      // ctrl-[ maps to \x1B (Escape)
+      case VKEY_OEM_4:
+        return 0x1B;
+      // ctrl-\ maps to \x1C (File separator, Information separator four)
+      case VKEY_OEM_5:
+        return 0x1C;
+      // ctrl-] maps to \x1D (Group separator, Information separator three)
+      case VKEY_OEM_6:
+        return 0x1D;
+      // ctrl-Enter maps to \x0A (Line feed)
+      case VKEY_RETURN:
+        return 0x0A;
+      // Returns 0 for all other keys to avoid inputting unexpected chars.
+      default:
+        return 0;
+    }
+  }
+}
+
+void GetWidgetRectInScreen(GtkWidget* widget, GdkRectangle* r) {
+  gint x, y, w, h;
+  GdkRectangle extents;
+
+  GdkWindow* window = gtk_widget_get_parent_window(widget);
+
+  // Get parent's left-top screen coordinates.
+  gdk_window_get_root_origin(window, &x, &y);
+  // Get parent's width and height.
+  gdk_drawable_get_size(window, &w, &h);
+  // Get parent's extents including decorations.
+  gdk_window_get_frame_extents(window, &extents);
+
+  // X and Y calculations assume that left, right and bottom border sizes are
+  // all the same.
+  const gint border = (extents.width - w) / 2;
+  r->x = x + border + widget->allocation.x;
+  r->y = y + (extents.height - h) - border + widget->allocation.y;
+  r->width = widget->allocation.width;
+  r->height = widget->allocation.height;
+}
+
+CefBrowserHost::DragOperationsMask GetDragOperationsMask(
+    GdkDragContext* drag_context) {
+  int allowed_ops = DRAG_OPERATION_NONE;
+  GdkDragAction drag_action = gdk_drag_context_get_actions(drag_context);
+  if (drag_action & GDK_ACTION_COPY)
+    allowed_ops |= DRAG_OPERATION_COPY;
+  if (drag_action & GDK_ACTION_MOVE)
+    allowed_ops |= DRAG_OPERATION_MOVE;
+  if (drag_action & GDK_ACTION_LINK)
+    allowed_ops |= DRAG_OPERATION_LINK;
+  if (drag_action & GDK_ACTION_PRIVATE)
+    allowed_ops |= DRAG_OPERATION_PRIVATE;
+  return static_cast<CefBrowserHost::DragOperationsMask>(allowed_ops);
+}
+
+class ScopedGLContext {
+ public:
+  ScopedGLContext(GtkWidget* widget, bool swap_buffers)
+      : swap_buffers_(swap_buffers) {
+    GdkGLContext* glcontext = gtk_widget_get_gl_context(widget);
+    gldrawable_ = gtk_widget_get_gl_drawable(widget);
+    is_valid_ = gdk_gl_drawable_gl_begin(gldrawable_, glcontext);
+  }
+
+  virtual ~ScopedGLContext() {
+    if (is_valid_) {
+      gdk_gl_drawable_gl_end(gldrawable_);
+
+      if (swap_buffers_) {
+        if (gdk_gl_drawable_is_double_buffered(gldrawable_))
+          gdk_gl_drawable_swap_buffers(gldrawable_);
+        else
+          glFlush();
+      }
+    }
+  }
+
+  bool IsValid() const { return is_valid_; }
+
+ private:
+  bool swap_buffers_;
+  GdkGLDrawable* gldrawable_;
+  bool is_valid_;
+  ScopedGdkThreadsEnter scoped_gdk_threads_;
+};
+
+}  // namespace
+
+BrowserWindowOsrGtk::BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate,
+                                         const std::string& startup_url,
+                                         const OsrRendererSettings& settings)
+    : BrowserWindow(delegate),
+      xdisplay_(nullptr),
+      renderer_(settings),
+      gl_enabled_(false),
+      painting_popup_(false),
+      hidden_(false),
+      glarea_(NULL),
+      drag_trigger_event_(nullptr),
+      drag_data_(nullptr),
+      drag_operation_(DRAG_OPERATION_NONE),
+      drag_context_(nullptr),
+      drag_targets_(gtk_target_list_new(NULL, 0)),
+      drag_leave_(false),
+      drag_drop_(false),
+      device_scale_factor_(1.0f) {
+  client_handler_ = new ClientHandlerOsr(this, this, startup_url);
+  g_browser_windows.push_back(this);
+}
+
+BrowserWindowOsrGtk::~BrowserWindowOsrGtk() {
+  g_browser_windows.erase(
+      std::find(g_browser_windows.begin(), g_browser_windows.end(), this));
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  if (drag_trigger_event_) {
+    gdk_event_free(drag_trigger_event_);
+  }
+  if (drag_context_) {
+    g_object_unref(drag_context_);
+  }
+  gtk_target_list_unref(drag_targets_);
+}
+
+void BrowserWindowOsrGtk::set_xdisplay(XDisplay* xdisplay) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!xdisplay_);
+  xdisplay_ = xdisplay;
+}
+
+void BrowserWindowOsrGtk::CreateBrowser(
+    ClientWindowHandle parent_handle,
+    const CefRect& rect,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  REQUIRE_MAIN_THREAD();
+
+  // Create the native window.
+  Create(parent_handle);
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  // Retrieve the X11 Window ID for the GTK parent window.
+  GtkWidget* window =
+      gtk_widget_get_ancestor(GTK_WIDGET(parent_handle), GTK_TYPE_WINDOW);
+  ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(window));
+  DCHECK(xwindow);
+
+  CefWindowInfo window_info;
+  window_info.SetAsWindowless(xwindow);
+
+  // Create the browser asynchronously.
+  CefBrowserHost::CreateBrowser(window_info, client_handler_,
+                                client_handler_->startup_url(), settings,
+                                extra_info, request_context);
+}
+
+void BrowserWindowOsrGtk::GetPopupConfig(CefWindowHandle temp_handle,
+                                         CefWindowInfo& windowInfo,
+                                         CefRefPtr<CefClient>& client,
+                                         CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  windowInfo.SetAsWindowless(temp_handle);
+  client = client_handler_;
+}
+
+void BrowserWindowOsrGtk::ShowPopup(ClientWindowHandle parent_handle,
+                                    int x,
+                                    int y,
+                                    size_t width,
+                                    size_t height) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(browser_.get());
+
+  // Create the native window.
+  Create(parent_handle);
+
+  // Send resize notification so the compositor is assigned the correct
+  // viewport size and begins rendering.
+  browser_->GetHost()->WasResized();
+
+  Show();
+}
+
+void BrowserWindowOsrGtk::Show() {
+  REQUIRE_MAIN_THREAD();
+
+  if (hidden_) {
+    // Set the browser as visible.
+    browser_->GetHost()->WasHidden(false);
+    hidden_ = false;
+  }
+
+  // Give focus to the browser.
+  browser_->GetHost()->SendFocusEvent(true);
+}
+
+void BrowserWindowOsrGtk::Hide() {
+  REQUIRE_MAIN_THREAD();
+
+  if (!browser_)
+    return;
+
+  // Remove focus from the browser.
+  browser_->GetHost()->SendFocusEvent(false);
+
+  if (!hidden_) {
+    // Set the browser as hidden.
+    browser_->GetHost()->WasHidden(true);
+    hidden_ = true;
+  }
+}
+
+void BrowserWindowOsrGtk::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+  // Nothing to do here. GTK will take care of positioning in the container.
+}
+
+void BrowserWindowOsrGtk::SetFocus(bool focus) {
+  REQUIRE_MAIN_THREAD();
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+  if (glarea_ && focus) {
+    gtk_widget_grab_focus(glarea_);
+  }
+}
+
+void BrowserWindowOsrGtk::SetDeviceScaleFactor(float device_scale_factor) {
+  REQUIRE_MAIN_THREAD();
+  {
+    base::AutoLock lock_scope(lock_);
+    if (device_scale_factor == device_scale_factor_)
+      return;
+
+    // Apply some sanity checks.
+    if (device_scale_factor < 1.0f || device_scale_factor > 4.0f)
+      return;
+
+    device_scale_factor_ = device_scale_factor;
+  }
+
+  if (browser_) {
+    browser_->GetHost()->NotifyScreenInfoChanged();
+    browser_->GetHost()->WasResized();
+  }
+}
+
+float BrowserWindowOsrGtk::GetDeviceScaleFactor() const {
+  REQUIRE_MAIN_THREAD();
+  base::AutoLock lock_scope(lock_);
+  return device_scale_factor_;
+}
+
+ClientWindowHandle BrowserWindowOsrGtk::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+  return glarea_;
+}
+
+void BrowserWindowOsrGtk::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+}
+
+void BrowserWindowOsrGtk::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Detach |this| from the ClientHandlerOsr.
+  static_cast<ClientHandlerOsr*>(client_handler_.get())->DetachOsrDelegate();
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  UnregisterDragDrop();
+
+  // Disconnect all signal handlers that reference |this|.
+  g_signal_handlers_disconnect_matched(glarea_, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+                                       NULL, this);
+
+  DisableGL();
+}
+
+bool BrowserWindowOsrGtk::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                            CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  return false;
+}
+
+void BrowserWindowOsrGtk::GetViewRect(CefRefPtr<CefBrowser> browser,
+                                      CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+
+  rect.x = rect.y = 0;
+
+  if (!glarea_) {
+    // Never return an empty rectangle.
+    rect.width = rect.height = 1;
+    return;
+  }
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(lock_);
+    device_scale_factor = device_scale_factor_;
+  }
+
+  // The simulated screen and view rectangle are the same. This is necessary
+  // for popup menus to be located and sized inside the view.
+  rect.width = DeviceToLogical(glarea_->allocation.width, device_scale_factor);
+  if (rect.width == 0)
+    rect.width = 1;
+  rect.height =
+      DeviceToLogical(glarea_->allocation.height, device_scale_factor);
+  if (rect.height == 0)
+    rect.height = 1;
+}
+
+bool BrowserWindowOsrGtk::GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                         int viewX,
+                                         int viewY,
+                                         int& screenX,
+                                         int& screenY) {
+  CEF_REQUIRE_UI_THREAD();
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(lock_);
+    device_scale_factor = device_scale_factor_;
+  }
+
+  GdkRectangle screen_rect;
+  GetWidgetRectInScreen(glarea_, &screen_rect);
+  screenX = screen_rect.x + LogicalToDevice(viewX, device_scale_factor);
+  screenY = screen_rect.y + LogicalToDevice(viewY, device_scale_factor);
+  return true;
+}
+
+bool BrowserWindowOsrGtk::GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                                        CefScreenInfo& screen_info) {
+  CEF_REQUIRE_UI_THREAD();
+
+  CefRect view_rect;
+  GetViewRect(browser, view_rect);
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(lock_);
+    device_scale_factor = device_scale_factor_;
+  }
+
+  screen_info.device_scale_factor = device_scale_factor;
+
+  // The screen info rectangles are used by the renderer to create and position
+  // popups. Keep popups inside the view rectangle.
+  screen_info.rect = view_rect;
+  screen_info.available_rect = view_rect;
+  return true;
+}
+
+void BrowserWindowOsrGtk::OnPopupShow(CefRefPtr<CefBrowser> browser,
+                                      bool show) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!show) {
+    renderer_.ClearPopupRects();
+    browser->GetHost()->Invalidate(PET_VIEW);
+  }
+  renderer_.OnPopupShow(browser, show);
+}
+
+void BrowserWindowOsrGtk::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                                      const CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(lock_);
+    device_scale_factor = device_scale_factor_;
+  }
+
+  renderer_.OnPopupSize(browser, LogicalToDevice(rect, device_scale_factor));
+}
+
+void BrowserWindowOsrGtk::OnPaint(CefRefPtr<CefBrowser> browser,
+                                  CefRenderHandler::PaintElementType type,
+                                  const CefRenderHandler::RectList& dirtyRects,
+                                  const void* buffer,
+                                  int width,
+                                  int height) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (width <= 2 && height <= 2) {
+    // Ignore really small buffer sizes while the widget is starting up.
+    return;
+  }
+
+  if (painting_popup_) {
+    renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+    return;
+  }
+
+  if (!gl_enabled_)
+    EnableGL();
+
+  ScopedGLContext scoped_gl_context(glarea_, true);
+  if (!scoped_gl_context.IsValid())
+    return;
+
+  renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+  if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
+    painting_popup_ = true;
+    browser->GetHost()->Invalidate(PET_POPUP);
+    painting_popup_ = false;
+  }
+  renderer_.Render();
+}
+
+void BrowserWindowOsrGtk::OnCursorChange(
+    CefRefPtr<CefBrowser> browser,
+    CefCursorHandle cursor,
+    CefRenderHandler::CursorType type,
+    const CefCursorInfo& custom_cursor_info) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Retrieve the X11 display shared with Chromium.
+  CHECK(xdisplay_ != 0);
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  // Retrieve the X11 window handle for the GTK widget.
+  ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(glarea_));
+
+  // Set the cursor.
+  XDefineCursor(xdisplay_, xwindow, cursor);
+}
+
+bool BrowserWindowOsrGtk::StartDragging(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDragData> drag_data,
+    CefRenderHandler::DragOperationsMask allowed_ops,
+    int x,
+    int y) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!drag_data->HasImage()) {
+    LOG(ERROR) << "Drag image representation not available";
+    return false;
+  }
+
+  DragReset();
+  drag_data_ = drag_data;
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  // Begin drag.
+  if (drag_trigger_event_) {
+    LOG(ERROR) << "Dragging started, but last mouse event is missing";
+    DragReset();
+    return false;
+  }
+  drag_context_ = gtk_drag_begin(glarea_, drag_targets_, GDK_ACTION_COPY,
+                                 1,  // left mouse button
+                                 drag_trigger_event_);
+  if (!drag_context_) {
+    LOG(ERROR) << "GTK drag begin failed";
+    DragReset();
+    return false;
+  }
+  g_object_ref(drag_context_);
+
+  // Send drag enter event.
+  CefMouseEvent ev;
+  ev.x = x;
+  ev.y = y;
+  ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
+  browser->GetHost()->DragTargetDragEnter(drag_data, ev, allowed_ops);
+
+  return true;
+}
+
+void BrowserWindowOsrGtk::UpdateDragCursor(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::DragOperation operation) {
+  CEF_REQUIRE_UI_THREAD();
+  drag_operation_ = operation;
+}
+
+void BrowserWindowOsrGtk::OnImeCompositionRangeChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefRange& selection_range,
+    const CefRenderHandler::RectList& character_bounds) {
+  CEF_REQUIRE_UI_THREAD();
+}
+
+void BrowserWindowOsrGtk::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+}
+
+void BrowserWindowOsrGtk::UpdateAccessibilityLocation(
+    CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+}
+
+void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!glarea_);
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  glarea_ = gtk_drawing_area_new();
+  DCHECK(glarea_);
+
+  GdkGLConfig* glconfig =
+      gdk_gl_config_new_by_mode(static_cast<GdkGLConfigMode>(
+          GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
+  DCHECK(glconfig);
+
+  gtk_widget_set_gl_capability(glarea_, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
+
+  gtk_widget_set_can_focus(glarea_, TRUE);
+
+  g_signal_connect(G_OBJECT(glarea_), "size_allocate",
+                   G_CALLBACK(&BrowserWindowOsrGtk::SizeAllocation), this);
+
+  gtk_widget_set_events(
+      glarea_, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                   GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
+                   GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+                   GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
+                   GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK);
+  g_signal_connect(G_OBJECT(glarea_), "button_press_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "button_release_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "key_press_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "key_release_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "enter_notify_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "leave_notify_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "motion_notify_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "scroll_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::ScrollEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "focus_in_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
+  g_signal_connect(G_OBJECT(glarea_), "focus_out_event",
+                   G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
+
+  RegisterDragDrop();
+
+  gtk_container_add(GTK_CONTAINER(parent_handle), glarea_);
+
+  // Make the GlArea visible in the parent container.
+  gtk_widget_show_all(parent_handle);
+
+  InitializeXinput(xdisplay_);
+
+  if (IsTouchAvailable())
+    RegisterTouch();
+}
+
+// static
+gint BrowserWindowOsrGtk::SizeAllocation(GtkWidget* widget,
+                                         GtkAllocation* allocation,
+                                         BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  if (self->browser_.get()) {
+    // Results in a call to GetViewRect().
+    self->browser_->GetHost()->WasResized();
+  }
+  return TRUE;
+}
+
+// static
+gint BrowserWindowOsrGtk::ClickEvent(GtkWidget* widget,
+                                     GdkEventButton* event,
+                                     BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!self->browser_.get())
+    return TRUE;
+
+  CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
+
+  CefBrowserHost::MouseButtonType button_type = MBT_LEFT;
+  switch (event->button) {
+    case 1:
+      break;
+    case 2:
+      button_type = MBT_MIDDLE;
+      break;
+    case 3:
+      button_type = MBT_RIGHT;
+      break;
+    default:
+      // Other mouse buttons are not handled here.
+      return FALSE;
+  }
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(self->lock_);
+    device_scale_factor = self->device_scale_factor_;
+  }
+
+  CefMouseEvent mouse_event;
+  mouse_event.x = event->x;
+  mouse_event.y = event->y;
+  self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
+  DeviceToLogical(mouse_event, device_scale_factor);
+  mouse_event.modifiers = GetCefStateModifiers(event->state);
+
+  bool mouse_up = (event->type == GDK_BUTTON_RELEASE);
+  if (!mouse_up) {
+    gtk_widget_grab_focus(widget);
+  }
+
+  int click_count = 1;
+  switch (event->type) {
+    case GDK_2BUTTON_PRESS:
+      click_count = 2;
+      break;
+    case GDK_3BUTTON_PRESS:
+      click_count = 3;
+      break;
+    default:
+      break;
+  }
+
+  host->SendMouseClickEvent(mouse_event, button_type, mouse_up, click_count);
+
+  // Save mouse event that can be a possible trigger for drag.
+  if (!self->drag_context_ && button_type == MBT_LEFT) {
+    if (self->drag_trigger_event_) {
+      gdk_event_free(self->drag_trigger_event_);
+    }
+    self->drag_trigger_event_ =
+        gdk_event_copy(reinterpret_cast<GdkEvent*>(event));
+  }
+
+  return TRUE;
+}
+
+// static
+gint BrowserWindowOsrGtk::KeyEvent(GtkWidget* widget,
+                                   GdkEventKey* event,
+                                   BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!self->browser_.get())
+    return TRUE;
+
+  CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
+
+  // Based on WebKeyboardEventBuilder::Build from
+  // content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
+  CefKeyEvent key_event;
+  KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event);
+  key_event.windows_key_code =
+      GetWindowsKeyCodeWithoutLocation(windows_key_code);
+  key_event.native_key_code = event->hardware_keycode;
+
+  key_event.modifiers = GetCefStateModifiers(event->state);
+  if (event->keyval >= GDK_KP_Space && event->keyval <= GDK_KP_9)
+    key_event.modifiers |= EVENTFLAG_IS_KEY_PAD;
+  if (key_event.modifiers & EVENTFLAG_ALT_DOWN)
+    key_event.is_system_key = true;
+
+  if (windows_key_code == VKEY_RETURN) {
+    // We need to treat the enter key as a key press of character \r.  This
+    // is apparently just how webkit handles it and what it expects.
+    key_event.unmodified_character = '\r';
+  } else {
+    // FIXME: fix for non BMP chars
+    key_event.unmodified_character =
+        static_cast<int>(gdk_keyval_to_unicode(event->keyval));
+  }
+
+  // If ctrl key is pressed down, then control character shall be input.
+  if (key_event.modifiers & EVENTFLAG_CONTROL_DOWN) {
+    key_event.character = GetControlCharacter(
+        windows_key_code, key_event.modifiers & EVENTFLAG_SHIFT_DOWN);
+  } else {
+    key_event.character = key_event.unmodified_character;
+  }
+
+  if (event->type == GDK_KEY_PRESS) {
+    key_event.type = KEYEVENT_RAWKEYDOWN;
+    host->SendKeyEvent(key_event);
+    key_event.type = KEYEVENT_CHAR;
+    host->SendKeyEvent(key_event);
+  } else {
+    key_event.type = KEYEVENT_KEYUP;
+    host->SendKeyEvent(key_event);
+  }
+
+  return TRUE;
+}
+
+// static
+gint BrowserWindowOsrGtk::MoveEvent(GtkWidget* widget,
+                                    GdkEventMotion* event,
+                                    BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!self->browser_.get())
+    return TRUE;
+
+  CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
+
+  gint x, y;
+  GdkModifierType state;
+
+  if (event->is_hint) {
+    gdk_window_get_pointer(event->window, &x, &y, &state);
+  } else {
+    x = (gint)event->x;
+    y = (gint)event->y;
+    state = (GdkModifierType)event->state;
+    if (x == 0 && y == 0) {
+      // Invalid coordinates of (0,0) appear from time to time in
+      // enter-notify-event and leave-notify-event events. Sending them may
+      // cause StartDragging to never get called, so just ignore these.
+      return TRUE;
+    }
+  }
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(self->lock_);
+    device_scale_factor = self->device_scale_factor_;
+  }
+
+  CefMouseEvent mouse_event;
+  mouse_event.x = x;
+  mouse_event.y = y;
+  self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
+  DeviceToLogical(mouse_event, device_scale_factor);
+  mouse_event.modifiers = GetCefStateModifiers(state);
+
+  bool mouse_leave = (event->type == GDK_LEAVE_NOTIFY);
+  host->SendMouseMoveEvent(mouse_event, mouse_leave);
+
+  // Save mouse event that can be a possible trigger for drag.
+  if (!self->drag_context_ &&
+      (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)) {
+    if (self->drag_trigger_event_) {
+      gdk_event_free(self->drag_trigger_event_);
+    }
+    self->drag_trigger_event_ =
+        gdk_event_copy(reinterpret_cast<GdkEvent*>(event));
+  }
+
+  return TRUE;
+}
+
+// static
+gint BrowserWindowOsrGtk::ScrollEvent(GtkWidget* widget,
+                                      GdkEventScroll* event,
+                                      BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!self->browser_.get())
+    return TRUE;
+
+  CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(self->lock_);
+    device_scale_factor = self->device_scale_factor_;
+  }
+
+  CefMouseEvent mouse_event;
+  mouse_event.x = event->x;
+  mouse_event.y = event->y;
+  self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
+  DeviceToLogical(mouse_event, device_scale_factor);
+  mouse_event.modifiers = GetCefStateModifiers(event->state);
+
+  static const int scrollbarPixelsPerGtkTick = 40;
+  int deltaX = 0;
+  int deltaY = 0;
+  switch (event->direction) {
+    case GDK_SCROLL_UP:
+      deltaY = scrollbarPixelsPerGtkTick;
+      break;
+    case GDK_SCROLL_DOWN:
+      deltaY = -scrollbarPixelsPerGtkTick;
+      break;
+    case GDK_SCROLL_LEFT:
+      deltaX = scrollbarPixelsPerGtkTick;
+      break;
+    case GDK_SCROLL_RIGHT:
+      deltaX = -scrollbarPixelsPerGtkTick;
+      break;
+  }
+
+  host->SendMouseWheelEvent(mouse_event, deltaX, deltaY);
+  return TRUE;
+}
+
+// static
+gint BrowserWindowOsrGtk::FocusEvent(GtkWidget* widget,
+                                     GdkEventFocus* event,
+                                     BrowserWindowOsrGtk* self) {
+  // May be called on the main thread and the UI thread.
+  if (self->browser_.get())
+    self->browser_->GetHost()->SendFocusEvent(event->in == TRUE);
+  return TRUE;
+}
+
+void BrowserWindowOsrGtk::TouchEvent(CefXIDeviceEvent event) {
+  if (!browser_.get())
+    return;
+
+  XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(event);
+  CefTouchEvent cef_event;
+  switch (ev->evtype) {
+    case XI_TouchBegin:
+      cef_event.type = CEF_TET_PRESSED;
+      break;
+    case XI_TouchUpdate:
+      cef_event.type = CEF_TET_MOVED;
+      break;
+    case XI_TouchEnd:
+      cef_event.type = CEF_TET_RELEASED;
+      break;
+    default:
+      return;
+  }
+
+  cef_event.id = ev->detail;
+  cef_event.x = ev->event_x;
+  cef_event.y = ev->event_y;
+  cef_event.radius_x = 0;
+  cef_event.radius_y = 0;
+  cef_event.rotation_angle = 0;
+  cef_event.pressure = 0;
+  cef_event.modifiers = GetCefStateModifiers(ev->mods, ev->buttons);
+
+  browser_->GetHost()->SendTouchEvent(cef_event);
+}
+
+void BrowserWindowOsrGtk::RegisterTouch() {
+  GdkWindow* glwindow = gtk_widget_get_window(glarea_);
+  ::Window xwindow = GDK_WINDOW_XID(glwindow);
+  uint32_t bitMask = XI_TouchBeginMask | XI_TouchUpdateMask | XI_TouchEndMask;
+
+  XIEventMask mask;
+  mask.deviceid = XIAllMasterDevices;
+  mask.mask = reinterpret_cast<unsigned char*>(&bitMask);
+  mask.mask_len = sizeof(bitMask);
+  XISelectEvents(xdisplay_, xwindow, &mask, 1);
+}
+
+// static
+GdkFilterReturn BrowserWindowOsrGtk::EventFilter(GdkXEvent* gdk_xevent,
+                                                 GdkEvent* event,
+                                                 gpointer data) {
+  XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
+  if (xevent->type == GenericEvent &&
+      xevent->xgeneric.extension == g_xinput_extension) {
+    XGetEventData(xevent->xcookie.display, &xevent->xcookie);
+    XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
+
+    if (!ev)
+      return GDK_FILTER_REMOVE;
+
+    for (BrowserWindowOsrGtk* browser_window : g_browser_windows) {
+      GtkWidget* widget = browser_window->GetWindowHandle();
+      ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(widget));
+      if (xwindow == ev->event) {
+        browser_window->TouchEvent(ev);
+        break;
+      }
+    }
+
+    XFreeEventData(xevent->xcookie.display, &xevent->xcookie);
+    // Even if we didn't find a consumer for this event, we will make sure Gdk
+    // doesn't attempt to process the event, since it can't parse GenericEvents
+    return GDK_FILTER_REMOVE;
+  }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+// static
+void BrowserWindowOsrGtk::InitializeXinput(XDisplay* xdisplay) {
+  static bool initialized = false;
+  if (initialized)
+    return;
+  initialized = true;
+
+  int firstEvent, firstError;
+  if (XQueryExtension(xdisplay, "XInputExtension", &g_xinput_extension,
+                      &firstEvent, &firstError)) {
+    int major = 2, minor = 2;
+    // X Input Extension 2.2 is needed for multitouch events.
+    if (XIQueryVersion(xdisplay, &major, &minor) == Success) {
+      // Ideally we would add an event filter for each glarea_ window
+      // separately, but unfortunately GDK can't parse X GenericEvents
+      // which have the target window stored in different way compared
+      // to other X events. That is why we add this global event filter
+      // just once, and dispatch the event to correct BrowserWindowOsrGtk
+      // manually.
+      gdk_window_add_filter(nullptr, &BrowserWindowOsrGtk::EventFilter,
+                            nullptr);
+    } else {
+      g_xinput_extension = -1;
+    }
+  }
+}
+
+bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const {
+  const CefRect& rc = renderer_.popup_rect();
+  int popup_right = rc.x + rc.width;
+  int popup_bottom = rc.y + rc.height;
+  return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom);
+}
+
+int BrowserWindowOsrGtk::GetPopupXOffset() const {
+  return renderer_.original_popup_rect().x - renderer_.popup_rect().x;
+}
+
+int BrowserWindowOsrGtk::GetPopupYOffset() const {
+  return renderer_.original_popup_rect().y - renderer_.popup_rect().y;
+}
+
+void BrowserWindowOsrGtk::ApplyPopupOffset(int& x, int& y) const {
+  if (IsOverPopupWidget(x, y)) {
+    x += GetPopupXOffset();
+    y += GetPopupYOffset();
+  }
+}
+
+void BrowserWindowOsrGtk::EnableGL() {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (gl_enabled_)
+    return;
+
+  ScopedGLContext scoped_gl_context(glarea_, false);
+  if (!scoped_gl_context.IsValid())
+    return;
+
+  renderer_.Initialize();
+
+  gl_enabled_ = true;
+}
+
+void BrowserWindowOsrGtk::DisableGL() {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!gl_enabled_)
+    return;
+
+  ScopedGLContext scoped_gl_context(glarea_, false);
+  if (!scoped_gl_context.IsValid())
+    return;
+
+  renderer_.Cleanup();
+
+  gl_enabled_ = false;
+}
+
+void BrowserWindowOsrGtk::RegisterDragDrop() {
+  REQUIRE_MAIN_THREAD();
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  // Succession of CEF d&d calls:
+  // 1. DragTargetDragEnter
+  // 2. DragTargetDragOver
+  // 3. DragTargetDragLeave - optional
+  // 4. DragSourceSystemDragEnded - optional, to cancel dragging
+  // 5. DragTargetDrop
+  // 6. DragSourceEndedAt
+  // 7. DragSourceSystemDragEnded
+
+  // Succession of GTK d&d events:
+  // 1. drag-begin-event, drag-data-get
+  // 2. drag-motion
+  // 3. drag-leave
+  // 4. drag-failed
+  // 5. drag-drop, drag-data-received
+  // 6. 7. drag-end-event
+
+  // Using gtk_drag_begin in StartDragging instead of calling
+  // gtk_drag_source_set here. Doing so because when using gtk_drag_source_set
+  // then StartDragging is being called very late, about ten DragMotion events
+  // after DragBegin, and drag icon can be set only when beginning drag.
+  // Default values for drag threshold are set to 8 pixels in both GTK and
+  // Chromium, but doesn't work as expected.
+  // --OFF--
+  // gtk_drag_source_set(glarea_, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
+
+  // Source widget events.
+  g_signal_connect(G_OBJECT(glarea_), "drag_begin",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragBegin), this);
+  g_signal_connect(G_OBJECT(glarea_), "drag_data_get",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragDataGet), this);
+  g_signal_connect(G_OBJECT(glarea_), "drag_end",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragEnd), this);
+
+  // Destination widget and its events.
+  gtk_drag_dest_set(glarea_, (GtkDestDefaults)0, (GtkTargetEntry*)NULL, 0,
+                    (GdkDragAction)GDK_ACTION_COPY);
+  g_signal_connect(G_OBJECT(glarea_), "drag_motion",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragMotion), this);
+  g_signal_connect(G_OBJECT(glarea_), "drag_leave",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragLeave), this);
+  g_signal_connect(G_OBJECT(glarea_), "drag_failed",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragFailed), this);
+  g_signal_connect(G_OBJECT(glarea_), "drag_drop",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragDrop), this);
+  g_signal_connect(G_OBJECT(glarea_), "drag_data_received",
+                   G_CALLBACK(&BrowserWindowOsrGtk::DragDataReceived), this);
+}
+
+void BrowserWindowOsrGtk::UnregisterDragDrop() {
+  CEF_REQUIRE_UI_THREAD();
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+  gtk_drag_dest_unset(glarea_);
+  // Drag events are unregistered in OnBeforeClose by calling
+  // g_signal_handlers_disconnect_matched.
+}
+
+void BrowserWindowOsrGtk::DragReset() {
+  CEF_REQUIRE_UI_THREAD();
+  if (drag_trigger_event_) {
+    gdk_event_free(drag_trigger_event_);
+    drag_trigger_event_ = nullptr;
+  }
+  drag_data_ = nullptr;
+  drag_operation_ = DRAG_OPERATION_NONE;
+  if (drag_context_) {
+    g_object_unref(drag_context_);
+    drag_context_ = nullptr;
+  }
+  drag_leave_ = false;
+  drag_drop_ = false;
+}
+
+// static
+void BrowserWindowOsrGtk::DragBegin(GtkWidget* widget,
+                                    GdkDragContext* drag_context,
+                                    BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Load drag icon.
+  if (!self->drag_data_->HasImage()) {
+    LOG(ERROR) << "Failed to set drag icon, drag image not available";
+    return;
+  }
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(self->lock_);
+    device_scale_factor = self->device_scale_factor_;
+  }
+
+  int pixel_width = 0;
+  int pixel_height = 0;
+  CefRefPtr<CefBinaryValue> image_binary =
+      self->drag_data_->GetImage()->GetAsPNG(device_scale_factor, true,
+                                             pixel_width, pixel_height);
+  if (!image_binary) {
+    LOG(ERROR) << "Failed to set drag icon, drag image error";
+    return;
+  }
+
+  size_t image_size = image_binary->GetSize();
+  guint8* image_buffer = (guint8*)malloc(image_size);  // must free
+  image_binary->GetData((void*)image_buffer, image_size, 0);
+  GdkPixbufLoader* loader = nullptr;  // must unref
+  GError* error = nullptr;            // must free
+  GdkPixbuf* pixbuf = nullptr;        // owned by loader
+  gboolean success = FALSE;
+  loader = gdk_pixbuf_loader_new_with_type("png", &error);
+  if (error == nullptr && loader) {
+    success = gdk_pixbuf_loader_write(loader, image_buffer, image_size, NULL);
+    if (success) {
+      success = gdk_pixbuf_loader_close(loader, NULL);
+      if (success) {
+        pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+        if (pixbuf) {
+          CefPoint image_hotspot = self->drag_data_->GetImageHotspot();
+          int hotspot_x = image_hotspot.x;
+          int hotspot_y = image_hotspot.y;
+          gtk_drag_set_icon_pixbuf(drag_context, pixbuf, hotspot_x, hotspot_y);
+        } else {
+          LOG(ERROR) << "Failed to set drag icon, pixbuf error";
+        }
+      } else {
+        LOG(ERROR) << "Failed to set drag icon, loader close error";
+      }
+    } else {
+      LOG(ERROR) << "Failed to set drag icon, loader write error";
+    }
+  } else {
+    LOG(ERROR) << "Failed to set drag icon, loader creation error";
+  }
+  if (loader) {
+    g_object_unref(loader);  // unref
+  }
+  if (error) {
+    g_error_free(error);  // free
+  }
+  free(image_buffer);  // free
+}
+
+// static
+void BrowserWindowOsrGtk::DragDataGet(GtkWidget* widget,
+                                      GdkDragContext* drag_context,
+                                      GtkSelectionData* data,
+                                      guint info,
+                                      guint time,
+                                      BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  // No drag targets are set so this callback is never called.
+}
+
+// static
+void BrowserWindowOsrGtk::DragEnd(GtkWidget* widget,
+                                  GdkDragContext* drag_context,
+                                  BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (self->browser_) {
+    // Sometimes there is DragEnd event generated without prior DragDrop.
+    // Maybe related to drag-leave bug described in comments in DragLeave.
+    if (!self->drag_drop_) {
+      // Real coordinates not available.
+      self->browser_->GetHost()->DragSourceEndedAt(-1, -1,
+                                                   self->drag_operation_);
+    }
+    self->browser_->GetHost()->DragSourceSystemDragEnded();
+  }
+
+  self->DragReset();
+}
+
+// static
+gboolean BrowserWindowOsrGtk::DragMotion(GtkWidget* widget,
+                                         GdkDragContext* drag_context,
+                                         gint x,
+                                         gint y,
+                                         guint time,
+                                         BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  float device_scale_factor;
+  {
+    base::AutoLock lock_scope(self->lock_);
+    device_scale_factor = self->device_scale_factor_;
+  }
+
+  // MoveEvent is never called during drag & drop, so must call
+  // SendMouseMoveEvent here.
+  CefMouseEvent mouse_event;
+  mouse_event.x = x;
+  mouse_event.y = y;
+  mouse_event.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
+  self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
+  DeviceToLogical(mouse_event, device_scale_factor);
+  if (self->browser_) {
+    bool mouse_leave = self->drag_leave_;
+    self->browser_->GetHost()->SendMouseMoveEvent(mouse_event, mouse_leave);
+  }
+
+  // Mouse event.
+  CefMouseEvent ev;
+  ev.x = x;
+  ev.y = y;
+  ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
+
+  CefBrowserHost::DragOperationsMask allowed_ops =
+      GetDragOperationsMask(drag_context);
+
+  // Send drag enter event if needed.
+  if (self->drag_leave_ && self->browser_) {
+    self->browser_->GetHost()->DragTargetDragEnter(self->drag_data_, ev,
+                                                   allowed_ops);
+  }
+
+  // Send drag over event.
+  if (self->browser_) {
+    self->browser_->GetHost()->DragTargetDragOver(ev, allowed_ops);
+  }
+
+  // Update GTK drag status.
+  if (widget == self->glarea_) {
+    gdk_drag_status(drag_context, GDK_ACTION_COPY, time);
+    if (self->drag_leave_) {
+      self->drag_leave_ = false;
+    }
+    return TRUE;
+  } else {
+    LOG(WARNING) << "Invalid drag destination widget";
+    gdk_drag_status(drag_context, (GdkDragAction)0, time);
+    return FALSE;
+  }
+}
+
+// static
+void BrowserWindowOsrGtk::DragLeave(GtkWidget* widget,
+                                    GdkDragContext* drag_context,
+                                    guint time,
+                                    BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // There is no drag-enter event in GTK. The first drag-motion event
+  // after drag-leave will be a drag-enter event.
+
+  // There seems to be a bug during GTK drop, drag-leave event is generated
+  // just before drag-drop. A solution is to call DragTargetDragEnter
+  // and DragTargetDragOver in DragDrop when drag_leave_ is true.
+
+  // Send drag leave event.
+  if (self->browser_) {
+    self->browser_->GetHost()->DragTargetDragLeave();
+  }
+
+  self->drag_leave_ = true;
+}
+
+// static
+gboolean BrowserWindowOsrGtk::DragFailed(GtkWidget* widget,
+                                         GdkDragContext* drag_context,
+                                         GtkDragResult result,
+                                         BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Send drag end coordinates and system drag ended event.
+  if (self->browser_) {
+    // Real coordinates not available.
+    self->browser_->GetHost()->DragSourceEndedAt(-1, -1, self->drag_operation_);
+    self->browser_->GetHost()->DragSourceSystemDragEnded();
+  }
+
+  self->DragReset();
+  return TRUE;
+}
+
+// static
+gboolean BrowserWindowOsrGtk::DragDrop(GtkWidget* widget,
+                                       GdkDragContext* drag_context,
+                                       gint x,
+                                       gint y,
+                                       guint time,
+                                       BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Finish GTK drag.
+  gtk_drag_finish(drag_context, TRUE, FALSE, time);
+
+  // Mouse event.
+  CefMouseEvent ev;
+  ev.x = x;
+  ev.y = y;
+  ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
+
+  CefBrowserHost::DragOperationsMask allowed_ops =
+      GetDragOperationsMask(drag_context);
+
+  // Send drag enter/over events if needed (read comment in DragLeave).
+  if (self->drag_leave_ && self->browser_) {
+    self->browser_->GetHost()->DragTargetDragEnter(self->drag_data_, ev,
+                                                   allowed_ops);
+    self->browser_->GetHost()->DragTargetDragOver(ev, allowed_ops);
+  }
+
+  // Send drag drop event.
+  if (self->browser_) {
+    self->browser_->GetHost()->DragTargetDrop(ev);
+  }
+
+  // Send drag end coordinates.
+  if (self->browser_) {
+    self->browser_->GetHost()->DragSourceEndedAt(x, y, self->drag_operation_);
+  }
+
+  self->drag_drop_ = true;
+  return TRUE;
+}
+
+// static
+void BrowserWindowOsrGtk::DragDataReceived(GtkWidget* widget,
+                                           GdkDragContext* drag_context,
+                                           gint x,
+                                           gint y,
+                                           GtkSelectionData* data,
+                                           guint info,
+                                           guint time,
+                                           BrowserWindowOsrGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  // This callback is never called because DragDrop does not call
+  // gtk_drag_get_data, as only dragging inside web view is supported.
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/browser_window_osr_gtk.h b/src/tests/cefclient/browser/browser_window_osr_gtk.h
new file mode 100644
index 0000000..33083e0
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_osr_gtk.h
@@ -0,0 +1,213 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_GTK_H_
+#pragma once
+
+#include "include/base/cef_lock.h"
+
+#include "tests/cefclient/browser/browser_window.h"
+#include "tests/cefclient/browser/client_handler_osr.h"
+#include "tests/cefclient/browser/osr_renderer.h"
+
+namespace client {
+
+// Represents a native child window hosting a single off-screen browser
+// instance. The methods of this class must be called on the main thread unless
+// otherwise indicated.
+class BrowserWindowOsrGtk : public BrowserWindow,
+                            public ClientHandlerOsr::OsrDelegate {
+ public:
+  typedef void* CefXIDeviceEvent;
+
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object.
+  BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate,
+                      const std::string& startup_url,
+                      const OsrRendererSettings& settings);
+
+  // Called from RootWindowGtk::CreateRootWindow before CreateBrowser.
+  void set_xdisplay(XDisplay* xdisplay);
+
+  // BrowserWindow methods.
+  void CreateBrowser(ClientWindowHandle parent_handle,
+                     const CefRect& rect,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context) OVERRIDE;
+  void GetPopupConfig(CefWindowHandle temp_handle,
+                      CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient>& client,
+                      CefBrowserSettings& settings) OVERRIDE;
+  void ShowPopup(ClientWindowHandle parent_handle,
+                 int x,
+                 int y,
+                 size_t width,
+                 size_t height) OVERRIDE;
+  void Show() OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void SetFocus(bool focus) OVERRIDE;
+  void SetDeviceScaleFactor(float device_scale_factor) OVERRIDE;
+  float GetDeviceScaleFactor() const OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+
+  // ClientHandlerOsr::OsrDelegate methods.
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                      int viewX,
+                      int viewY,
+                      int& screenX,
+                      int& screenY) OVERRIDE;
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) OVERRIDE;
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) OVERRIDE;
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) OVERRIDE;
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) OVERRIDE;
+  void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                      CefCursorHandle cursor,
+                      CefRenderHandler::CursorType type,
+                      const CefCursorInfo& custom_cursor_info) OVERRIDE;
+  bool StartDragging(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefDragData> drag_data,
+                     CefRenderHandler::DragOperationsMask allowed_ops,
+                     int x,
+                     int y) OVERRIDE;
+  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                        CefRenderHandler::DragOperation operation) OVERRIDE;
+  void OnImeCompositionRangeChanged(
+      CefRefPtr<CefBrowser> browser,
+      const CefRange& selection_range,
+      const CefRenderHandler::RectList& character_bounds) OVERRIDE;
+  void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
+  void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) OVERRIDE;
+
+ private:
+  ~BrowserWindowOsrGtk();
+
+  // Create the GTK GlArea.
+  void Create(ClientWindowHandle parent_handle);
+
+  // Signal handlers for the GTK GlArea.
+  static gint SizeAllocation(GtkWidget* widget,
+                             GtkAllocation* allocation,
+                             BrowserWindowOsrGtk* self);
+  static gint ClickEvent(GtkWidget* widget,
+                         GdkEventButton* event,
+                         BrowserWindowOsrGtk* self);
+  static gint KeyEvent(GtkWidget* widget,
+                       GdkEventKey* event,
+                       BrowserWindowOsrGtk* self);
+  static gint MoveEvent(GtkWidget* widget,
+                        GdkEventMotion* event,
+                        BrowserWindowOsrGtk* self);
+  static gint ScrollEvent(GtkWidget* widget,
+                          GdkEventScroll* event,
+                          BrowserWindowOsrGtk* self);
+  static gint FocusEvent(GtkWidget* widget,
+                         GdkEventFocus* event,
+                         BrowserWindowOsrGtk* self);
+
+  void TouchEvent(CefXIDeviceEvent event);
+  void RegisterTouch();
+
+  bool IsOverPopupWidget(int x, int y) const;
+  int GetPopupXOffset() const;
+  int GetPopupYOffset() const;
+  void ApplyPopupOffset(int& x, int& y) const;
+
+  void EnableGL();
+  void DisableGL();
+
+  // Drag & drop
+  void RegisterDragDrop();
+  void UnregisterDragDrop();
+  void DragReset();
+  static void DragBegin(GtkWidget* widget,
+                        GdkDragContext* drag_context,
+                        BrowserWindowOsrGtk* self);
+  static void DragDataGet(GtkWidget* widget,
+                          GdkDragContext* drag_context,
+                          GtkSelectionData* data,
+                          guint info,
+                          guint time,
+                          BrowserWindowOsrGtk* self);
+  static void DragEnd(GtkWidget* widget,
+                      GdkDragContext* drag_context,
+                      BrowserWindowOsrGtk* self);
+  static gboolean DragMotion(GtkWidget* widget,
+                             GdkDragContext* drag_context,
+                             gint x,
+                             gint y,
+                             guint time,
+                             BrowserWindowOsrGtk* self);
+  static void DragLeave(GtkWidget* widget,
+                        GdkDragContext* drag_context,
+                        guint time,
+                        BrowserWindowOsrGtk* self);
+  static gboolean DragFailed(GtkWidget* widget,
+                             GdkDragContext* drag_context,
+                             GtkDragResult result,
+                             BrowserWindowOsrGtk* self);
+  static gboolean DragDrop(GtkWidget* widget,
+                           GdkDragContext* drag_context,
+                           gint x,
+                           gint y,
+                           guint time,
+                           BrowserWindowOsrGtk* self);
+  static void DragDataReceived(GtkWidget* widget,
+                               GdkDragContext* drag_context,
+                               gint x,
+                               gint y,
+                               GtkSelectionData* data,
+                               guint info,
+                               guint time,
+                               BrowserWindowOsrGtk* self);
+  static GdkFilterReturn EventFilter(GdkXEvent* gdk_xevent,
+                                     GdkEvent* event,
+                                     gpointer data);
+  static void InitializeXinput(XDisplay* xdisplay);
+
+  XDisplay* xdisplay_;
+
+  // Members only accessed on the UI thread.
+  OsrRenderer renderer_;
+  bool gl_enabled_;
+  bool painting_popup_;
+
+  // Members only accessed on the main thread.
+  bool hidden_;
+
+  // Members protected by the GDK global lock.
+  ClientWindowHandle glarea_;
+
+  // Drag & drop
+  GdkEvent* drag_trigger_event_;  // mouse event, a possible trigger for drag
+  CefRefPtr<CefDragData> drag_data_;
+  CefRenderHandler::DragOperation drag_operation_;
+  GdkDragContext* drag_context_;
+  GtkTargetList* drag_targets_;
+  bool drag_leave_;
+  bool drag_drop_;
+
+  mutable base::Lock lock_;
+
+  // Access to these members must be protected by |lock_|.
+  float device_scale_factor_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserWindowOsrGtk);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_GTK_H_
diff --git a/src/tests/cefclient/browser/browser_window_osr_mac.h b/src/tests/cefclient/browser/browser_window_osr_mac.h
new file mode 100644
index 0000000..be42213
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_osr_mac.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_MAC_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_MAC_H_
+#pragma once
+
+#include "tests/cefclient/browser/browser_window.h"
+#include "tests/cefclient/browser/client_handler_osr.h"
+#include "tests/cefclient/browser/osr_renderer.h"
+#include "tests/cefclient/browser/text_input_client_osr_mac.h"
+
+namespace client {
+
+class BrowserWindowOsrMacImpl;
+
+// Represents a native child window hosting a single off-screen browser
+// instance. The methods of this class must be called on the main thread unless
+// otherwise indicated.
+class BrowserWindowOsrMac : public BrowserWindow,
+                            public ClientHandlerOsr::OsrDelegate {
+ public:
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object.
+  BrowserWindowOsrMac(BrowserWindow::Delegate* delegate,
+                      const std::string& startup_url,
+                      const OsrRendererSettings& settings);
+  ~BrowserWindowOsrMac();
+
+  // BrowserWindow methods.
+  void CreateBrowser(ClientWindowHandle parent_handle,
+                     const CefRect& rect,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context) OVERRIDE;
+  void GetPopupConfig(CefWindowHandle temp_handle,
+                      CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient>& client,
+                      CefBrowserSettings& settings) OVERRIDE;
+  void ShowPopup(ClientWindowHandle parent_handle,
+                 int x,
+                 int y,
+                 size_t width,
+                 size_t height) OVERRIDE;
+  void Show() OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void SetFocus(bool focus) OVERRIDE;
+  void SetDeviceScaleFactor(float device_scale_factor) OVERRIDE;
+  float GetDeviceScaleFactor() const OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+
+  // ClientHandlerOsr::OsrDelegate methods.
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                      int viewX,
+                      int viewY,
+                      int& screenX,
+                      int& screenY) OVERRIDE;
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) OVERRIDE;
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) OVERRIDE;
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) OVERRIDE;
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) OVERRIDE;
+  void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                      CefCursorHandle cursor,
+                      CefRenderHandler::CursorType type,
+                      const CefCursorInfo& custom_cursor_info) OVERRIDE;
+  bool StartDragging(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefDragData> drag_data,
+                     CefRenderHandler::DragOperationsMask allowed_ops,
+                     int x,
+                     int y) OVERRIDE;
+  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                        CefRenderHandler::DragOperation operation) OVERRIDE;
+  void OnImeCompositionRangeChanged(
+      CefRefPtr<CefBrowser> browser,
+      const CefRange& selection_range,
+      const CefRenderHandler::RectList& character_bounds) OVERRIDE;
+
+  void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
+  void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) OVERRIDE;
+
+ private:
+  scoped_ptr<BrowserWindowOsrMacImpl> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserWindowOsrMac);
+
+  friend class BrowserWindowOsrMacImpl;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_MAC_H_
diff --git a/src/tests/cefclient/browser/browser_window_osr_mac.mm b/src/tests/cefclient/browser/browser_window_osr_mac.mm
new file mode 100644
index 0000000..da143c1
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_osr_mac.mm
@@ -0,0 +1,1917 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/browser_window_osr_mac.h"
+
+#include <Cocoa/Cocoa.h>
+#include <OpenGL/gl.h>
+#import <objc/runtime.h>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_parser.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/cefclient/browser/bytes_write_handler.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/osr_accessibility_helper.h"
+#include "tests/cefclient/browser/osr_accessibility_node.h"
+#include "tests/cefclient/browser/text_input_client_osr_mac.h"
+#include "tests/shared/browser/geometry_util.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+#import <AppKit/NSAccessibility.h>
+
+@interface BrowserOpenGLView
+    : NSOpenGLView <NSDraggingSource, NSDraggingDestination, NSAccessibility> {
+ @private
+  NSTrackingArea* tracking_area_;
+  client::BrowserWindowOsrMac* browser_window_;
+  client::OsrRenderer* renderer_;
+  NSPoint last_mouse_pos_;
+  NSPoint cur_mouse_pos_;
+  bool rotating_;
+
+  bool was_last_mouse_down_on_view_;
+
+  float device_scale_factor_;
+
+  // Drag and drop.
+  CefRefPtr<CefDragData> current_drag_data_;
+  NSDragOperation current_drag_op_;
+  NSDragOperation current_allowed_ops_;
+  NSPasteboard* pasteboard_;
+  NSString* fileUTI_;
+
+  // For intreacting with IME.
+  NSTextInputContext* text_input_context_osr_mac_;
+  CefTextInputClientOSRMac* text_input_client_;
+
+  // Manages Accessibility Tree
+  client::OsrAccessibilityHelper* accessibility_helper_;
+
+  // Event monitor for scroll wheel end event.
+  id endWheelMonitor_;
+}
+
+@end  // @interface BrowserOpenGLView
+
+namespace {
+
+NSString* const kCEFDragDummyPboardType = @"org.CEF.drag-dummy-type";
+NSString* const kNSURLTitlePboardType = @"public.url-name";
+
+class ScopedGLContext {
+ public:
+  ScopedGLContext(BrowserOpenGLView* view, bool swap_buffers)
+      : swap_buffers_(swap_buffers) {
+    context_ = [view openGLContext];
+    [context_ makeCurrentContext];
+  }
+  ~ScopedGLContext() {
+    [NSOpenGLContext clearCurrentContext];
+    if (swap_buffers_)
+      [context_ flushBuffer];
+  }
+
+ private:
+  NSOpenGLContext* context_;
+  const bool swap_buffers_;
+};
+
+NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
+  NSRect point_rect = NSMakeRect(point.x, point.y, 0, 0);
+  return [window convertRectToScreen:point_rect].origin;
+}
+
+}  // namespace
+
+@implementation BrowserOpenGLView
+
+- (id)initWithFrame:(NSRect)frame
+    andBrowserWindow:(client::BrowserWindowOsrMac*)browser_window
+         andRenderer:(client::OsrRenderer*)renderer {
+  NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc]
+      initWithAttributes:(NSOpenGLPixelFormatAttribute[]){
+                             NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 32,
+                             0}];
+#if !__has_feature(objc_arc)
+  [pixelFormat autorelease];
+#endif  // !__has_feature(objc_arc)
+
+  if (self = [super initWithFrame:frame pixelFormat:pixelFormat]) {
+    browser_window_ = browser_window;
+    renderer_ = renderer;
+    rotating_ = false;
+    endWheelMonitor_ = nil;
+    device_scale_factor_ = 1.0f;
+
+    tracking_area_ = [[NSTrackingArea alloc]
+        initWithRect:frame
+             options:NSTrackingMouseMoved | NSTrackingActiveInActiveApp |
+                     NSTrackingInVisibleRect
+               owner:self
+            userInfo:nil];
+    [self addTrackingArea:tracking_area_];
+
+    // enable HiDPI buffer
+    [self setWantsBestResolutionOpenGLSurface:YES];
+
+    [self resetDragDrop];
+
+    NSArray* types = [NSArray
+        arrayWithObjects:kCEFDragDummyPboardType, NSStringPboardType,
+                         NSFilenamesPboardType, NSPasteboardTypeString, nil];
+    [self registerForDraggedTypes:types];
+  }
+
+  return self;
+}
+
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter]
+      removeObserver:self
+                name:NSWindowDidChangeBackingPropertiesNotification
+              object:nil];
+#if !__has_feature(objc_arc)
+  if (text_input_context_osr_mac_) {
+    [text_input_client_ release];
+    [text_input_context_osr_mac_ release];
+  }
+  [super dealloc];
+#endif  // !__has_feature(objc_arc)
+}
+
+- (void)detach {
+  renderer_ = NULL;
+  browser_window_ = NULL;
+  if (text_input_client_)
+    [text_input_client_ detach];
+}
+
+- (CefRefPtr<CefBrowser>)getBrowser {
+  if (browser_window_)
+    return browser_window_->GetBrowser();
+  return NULL;
+}
+
+- (void)setFrame:(NSRect)frameRect {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return;
+
+  [super setFrame:frameRect];
+  browser->GetHost()->WasResized();
+}
+
+- (void)sendMouseClick:(NSEvent*)event
+                button:(CefBrowserHost::MouseButtonType)type
+                  isUp:(bool)isUp {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return;
+
+  CefMouseEvent mouseEvent;
+  [self getMouseEvent:mouseEvent forEvent:event];
+
+  // |point| is in OS X view coordinates.
+  NSPoint point = [self getClickPointForEvent:event];
+
+  // Convert to device coordinates.
+  point = [self convertPointToBackingInternal:point];
+
+  if (!isUp) {
+    was_last_mouse_down_on_view_ = ![self isOverPopupWidgetX:point.x
+                                                        andY:point.y];
+  } else if (was_last_mouse_down_on_view_ &&
+             [self isOverPopupWidgetX:point.x andY:point.y] &&
+             ([self getPopupXOffset] || [self getPopupYOffset])) {
+    return;
+  }
+
+  browser->GetHost()->SendMouseClickEvent(mouseEvent, type, isUp,
+                                          [event clickCount]);
+}
+
+- (void)mouseDown:(NSEvent*)event {
+  [self sendMouseClick:event button:MBT_LEFT isUp:false];
+}
+
+- (void)rightMouseDown:(NSEvent*)event {
+  if ([event modifierFlags] & NSShiftKeyMask) {
+    // Start rotation effect.
+    last_mouse_pos_ = cur_mouse_pos_ = [self getClickPointForEvent:event];
+    rotating_ = true;
+    return;
+  }
+
+  [self sendMouseClick:event button:MBT_RIGHT isUp:false];
+}
+
+- (void)otherMouseDown:(NSEvent*)event {
+  [self sendMouseClick:event button:MBT_MIDDLE isUp:false];
+}
+
+- (void)mouseUp:(NSEvent*)event {
+  [self sendMouseClick:event button:MBT_LEFT isUp:true];
+}
+
+- (void)rightMouseUp:(NSEvent*)event {
+  if (rotating_) {
+    // End rotation effect.
+    renderer_->SetSpin(0, 0);
+    rotating_ = false;
+    [self setNeedsDisplay:YES];
+    return;
+  }
+  [self sendMouseClick:event button:MBT_RIGHT isUp:true];
+}
+
+- (void)otherMouseUp:(NSEvent*)event {
+  [self sendMouseClick:event button:MBT_MIDDLE isUp:true];
+}
+
+- (void)mouseMoved:(NSEvent*)event {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return;
+
+  if (rotating_) {
+    // Apply rotation effect.
+    cur_mouse_pos_ = [self getClickPointForEvent:event];
+    ;
+    renderer_->IncrementSpin((cur_mouse_pos_.x - last_mouse_pos_.x),
+                             (cur_mouse_pos_.y - last_mouse_pos_.y));
+    last_mouse_pos_ = cur_mouse_pos_;
+    [self setNeedsDisplay:YES];
+    return;
+  }
+
+  CefMouseEvent mouseEvent;
+  [self getMouseEvent:mouseEvent forEvent:event];
+
+  browser->GetHost()->SendMouseMoveEvent(mouseEvent, false);
+}
+
+- (void)mouseDragged:(NSEvent*)event {
+  [self mouseMoved:event];
+}
+
+- (void)rightMouseDragged:(NSEvent*)event {
+  [self mouseMoved:event];
+}
+
+- (void)otherMouseDragged:(NSEvent*)event {
+  [self mouseMoved:event];
+}
+
+- (void)mouseEntered:(NSEvent*)event {
+  [self mouseMoved:event];
+}
+
+- (void)mouseExited:(NSEvent*)event {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return;
+
+  CefMouseEvent mouseEvent;
+  [self getMouseEvent:mouseEvent forEvent:event];
+
+  browser->GetHost()->SendMouseMoveEvent(mouseEvent, true);
+}
+
+- (void)keyDown:(NSEvent*)event {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get() || !text_input_context_osr_mac_)
+    return;
+
+  if ([event type] != NSFlagsChanged) {
+    if (text_input_client_) {
+      [text_input_client_ HandleKeyEventBeforeTextInputClient:event];
+
+      // The return value of this method seems to always be set to YES, thus we
+      // ignore it and ask the host view whether IME is active or not.
+      [text_input_context_osr_mac_ handleEvent:event];
+
+      CefKeyEvent keyEvent;
+      [self getKeyEvent:keyEvent forEvent:event];
+
+      [text_input_client_ HandleKeyEventAfterTextInputClient:keyEvent];
+    }
+  }
+
+  // Check for Caps lock and Toggle Touch Emulation
+  if (client::MainContext::Get()->TouchEventsEnabled())
+    [self toggleTouchEmulation:event];
+}
+
+// OSX does not have touch screens, so we emulate it by mapping multitouch
+// events on TrackPad to Touch Events on Screen. To ensure it does not
+// interfere with other Trackpad events, this mapping is only enabled if
+// touch-events=enabled commandline is passed and caps lock key is on.
+- (void)toggleTouchEmulation:(NSEvent*)event {
+  if ([event type] == NSFlagsChanged && [event keyCode] == 0x39) {
+    NSUInteger flags = [event modifierFlags];
+    BOOL touch_enabled = flags & NSAlphaShiftKeyMask ? YES : NO;
+    [self setAcceptsTouchEvents:touch_enabled];
+  }
+}
+
+- (cef_touch_event_type_t)getTouchPhase:(NSTouchPhase)phase {
+  cef_touch_event_type_t event_type = CEF_TET_RELEASED;
+  switch (phase) {
+    case NSTouchPhaseBegan:
+      event_type = CEF_TET_PRESSED;
+      break;
+    case NSTouchPhaseMoved:
+      event_type = CEF_TET_MOVED;
+      break;
+    case NSTouchPhaseEnded:
+      event_type = CEF_TET_RELEASED;
+      break;
+    case NSTouchPhaseCancelled:
+      event_type = CEF_TET_CANCELLED;
+      break;
+    default:
+      break;
+  }
+  return event_type;
+}
+
+// Translate NSTouch events to CefTouchEvents and send to browser.
+- (void)sendTouchEvent:(NSEvent*)event touchPhase:(NSTouchPhase)phase {
+  int modifiers = [self getModifiersForEvent:event];
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+
+  NSSet* touches = [event touchesMatchingPhase:phase inView:self];
+
+  for (NSTouch* touch in touches) {
+    // Convert NSTouch to CefTouchEvent.
+    CefTouchEvent touch_event;
+
+    // NSTouch.identity is unique during the life of the touch
+    touch_event.id = touch.identity.hash;
+    touch_event.type = [self getTouchPhase:phase];
+
+    NSPoint scaled_pos = [touch normalizedPosition];
+    NSSize view_size = [self bounds].size;
+
+    // Map point on Touch Device to View coordinates.
+    NSPoint touch_point = NSMakePoint(scaled_pos.x * view_size.width,
+                                      scaled_pos.y * view_size.height);
+
+    NSPoint contentLocal = [self convertPoint:touch_point fromView:nil];
+    NSPoint point;
+    point.x = contentLocal.x;
+    point.y = [self frame].size.height - contentLocal.y;  // Flip y.
+
+    // Convert to device coordinates.
+    point = [self convertPointToBackingInternal:point];
+
+    int device_x = point.x;
+    int device_y = point.y;
+
+    const float device_scale_factor = [self getDeviceScaleFactor];
+    // Convert to browser view coordinates.
+    touch_event.x = client::DeviceToLogical(device_x, device_scale_factor);
+    touch_event.y = client::DeviceToLogical(device_y, device_scale_factor);
+
+    touch_event.radius_x = 0;
+    touch_event.radius_y = 0;
+
+    touch_event.rotation_angle = 0;
+    touch_event.pressure = 0;
+
+    touch_event.modifiers = modifiers;
+
+    // Notify the browser of touch event.
+    browser->GetHost()->SendTouchEvent(touch_event);
+  }
+}
+
+- (void)touchesBeganWithEvent:(NSEvent*)event {
+  [self sendTouchEvent:event touchPhase:NSTouchPhaseBegan];
+}
+
+- (void)touchesMovedWithEvent:(NSEvent*)event {
+  [self sendTouchEvent:event touchPhase:NSTouchPhaseMoved];
+}
+
+- (void)touchesEndedWithEvent:(NSEvent*)event {
+  [self sendTouchEvent:event touchPhase:NSTouchPhaseEnded];
+}
+
+- (void)touchesCancelledWithEvent:(NSEvent*)event {
+  [self sendTouchEvent:event touchPhase:NSTouchPhaseCancelled];
+}
+
+- (void)keyUp:(NSEvent*)event {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return;
+
+  CefKeyEvent keyEvent;
+  [self getKeyEvent:keyEvent forEvent:event];
+
+  keyEvent.type = KEYEVENT_KEYUP;
+  browser->GetHost()->SendKeyEvent(keyEvent);
+}
+
+- (void)flagsChanged:(NSEvent*)event {
+  if ([self isKeyUpEvent:event])
+    [self keyUp:event];
+  else
+    [self keyDown:event];
+}
+
+- (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
+  if ([event phase] != NSEventPhaseEnded &&
+      [event phase] != NSEventPhaseCancelled)
+    return;
+
+  [self sendScrollWheelEvet:event];
+
+  if (endWheelMonitor_) {
+    [NSEvent removeMonitor:endWheelMonitor_];
+    endWheelMonitor_ = nil;
+  }
+}
+
+- (void)scrollWheel:(NSEvent*)event {
+  // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
+  // the event is received even when the mouse cursor is no longer over the
+  // view when the scrolling ends. Also it avoids sending duplicate scroll
+  // events to the renderer.
+  if ([event phase] == NSEventPhaseBegan && !endWheelMonitor_) {
+    endWheelMonitor_ = [NSEvent
+        addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
+                                     handler:^(NSEvent* blockEvent) {
+                                       [self shortCircuitScrollWheelEvent:
+                                                 blockEvent];
+                                       return blockEvent;
+                                     }];
+  }
+
+  [self sendScrollWheelEvet:event];
+}
+
+- (void)sendScrollWheelEvet:(NSEvent*)event {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return;
+
+  CGEventRef cgEvent = [event CGEvent];
+  DCHECK(cgEvent);
+
+  int deltaX =
+      CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2);
+  int deltaY =
+      CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1);
+
+  CefMouseEvent mouseEvent;
+  [self getMouseEvent:mouseEvent forEvent:event];
+
+  browser->GetHost()->SendMouseWheelEvent(mouseEvent, deltaX, deltaY);
+}
+
+- (BOOL)canBecomeKeyView {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  return (browser.get() != NULL);
+}
+
+- (BOOL)acceptsFirstResponder {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  return (browser.get() != NULL);
+}
+
+- (BOOL)becomeFirstResponder {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get()) {
+    browser->GetHost()->SendFocusEvent(true);
+    return [super becomeFirstResponder];
+  }
+
+  return NO;
+}
+
+- (BOOL)resignFirstResponder {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get()) {
+    browser->GetHost()->SendFocusEvent(false);
+    return [super resignFirstResponder];
+  }
+
+  return NO;
+}
+
+- (void)undo:(id)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetFocusedFrame()->Undo();
+}
+
+- (void)redo:(id)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetFocusedFrame()->Redo();
+}
+
+- (void)cut:(id)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetFocusedFrame()->Cut();
+}
+
+- (void)copy:(id)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetFocusedFrame()->Copy();
+}
+
+- (void)paste:(id)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetFocusedFrame()->Paste();
+}
+
+- (void)delete:(id)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetFocusedFrame()->Delete();
+}
+
+- (void)selectAll:(id)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetFocusedFrame()->SelectAll();
+}
+
+- (NSPoint)getClickPointForEvent:(NSEvent*)event {
+  NSPoint windowLocal = [event locationInWindow];
+  NSPoint contentLocal = [self convertPoint:windowLocal fromView:nil];
+
+  NSPoint point;
+  point.x = contentLocal.x;
+  point.y = [self frame].size.height - contentLocal.y;  // Flip y.
+  return point;
+}
+
+- (void)getKeyEvent:(CefKeyEvent&)keyEvent forEvent:(NSEvent*)event {
+  if ([event type] == NSKeyDown || [event type] == NSKeyUp) {
+    NSString* s = [event characters];
+    if ([s length] > 0)
+      keyEvent.character = [s characterAtIndex:0];
+
+    s = [event charactersIgnoringModifiers];
+    if ([s length] > 0)
+      keyEvent.unmodified_character = [s characterAtIndex:0];
+  }
+
+  if ([event type] == NSFlagsChanged) {
+    keyEvent.character = 0;
+    keyEvent.unmodified_character = 0;
+  }
+
+  keyEvent.native_key_code = [event keyCode];
+
+  keyEvent.modifiers = [self getModifiersForEvent:event];
+}
+
+- (NSTextInputContext*)inputContext {
+  if (!text_input_context_osr_mac_) {
+    text_input_client_ =
+        [[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]];
+    text_input_context_osr_mac_ =
+        [[NSTextInputContext alloc] initWithClient:text_input_client_];
+#if !__has_feature(objc_arc)
+    [text_input_client_ retain];
+    [text_input_context_osr_mac_ retain];
+#endif  // !__has_feature(objc_arc)
+  }
+
+  return text_input_context_osr_mac_;
+}
+
+- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event {
+  const float device_scale_factor = [self getDeviceScaleFactor];
+
+  // |point| is in OS X view coordinates.
+  NSPoint point = [self getClickPointForEvent:event];
+
+  // Convert to device coordinates.
+  point = [self convertPointToBackingInternal:point];
+
+  int device_x = point.x;
+  int device_y = point.y;
+  if ([self isOverPopupWidgetX:device_x andY:device_y])
+    [self applyPopupOffsetToX:device_x andY:device_y];
+
+  // Convert to browser view coordinates.
+  mouseEvent.x = client::DeviceToLogical(device_x, device_scale_factor);
+  mouseEvent.y = client::DeviceToLogical(device_y, device_scale_factor);
+
+  mouseEvent.modifiers = [self getModifiersForEvent:event];
+}
+
+- (void)getMouseEvent:(CefMouseEvent&)mouseEvent
+          forDragInfo:(id<NSDraggingInfo>)info {
+  const float device_scale_factor = [self getDeviceScaleFactor];
+
+  // |point| is in OS X view coordinates.
+  NSPoint windowPoint = [info draggingLocation];
+  NSPoint point = [self flipWindowPointToView:windowPoint];
+
+  // Convert to device coordinates.
+  point = [self convertPointToBackingInternal:point];
+
+  // Convert to browser view coordinates.
+  mouseEvent.x = client::DeviceToLogical(point.x, device_scale_factor);
+  mouseEvent.y = client::DeviceToLogical(point.y, device_scale_factor);
+
+  mouseEvent.modifiers = [NSEvent modifierFlags];
+}
+
+- (int)getModifiersForEvent:(NSEvent*)event {
+  int modifiers = 0;
+
+  if ([event modifierFlags] & NSControlKeyMask)
+    modifiers |= EVENTFLAG_CONTROL_DOWN;
+  if ([event modifierFlags] & NSShiftKeyMask)
+    modifiers |= EVENTFLAG_SHIFT_DOWN;
+  if ([event modifierFlags] & NSAlternateKeyMask)
+    modifiers |= EVENTFLAG_ALT_DOWN;
+  if ([event modifierFlags] & NSCommandKeyMask)
+    modifiers |= EVENTFLAG_COMMAND_DOWN;
+  if ([event modifierFlags] & NSAlphaShiftKeyMask)
+    modifiers |= EVENTFLAG_CAPS_LOCK_ON;
+
+  if ([event type] == NSKeyUp || [event type] == NSKeyDown ||
+      [event type] == NSFlagsChanged) {
+    // Only perform this check for key events
+    if ([self isKeyPadEvent:event])
+      modifiers |= EVENTFLAG_IS_KEY_PAD;
+  }
+
+  // OS X does not have a modifier for NumLock, so I'm not entirely sure how to
+  // set EVENTFLAG_NUM_LOCK_ON;
+  //
+  // There is no EVENTFLAG for the function key either.
+
+  // Mouse buttons
+  switch ([event type]) {
+    case NSLeftMouseDragged:
+    case NSLeftMouseDown:
+    case NSLeftMouseUp:
+      modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
+      break;
+    case NSRightMouseDragged:
+    case NSRightMouseDown:
+    case NSRightMouseUp:
+      modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
+      break;
+    case NSOtherMouseDragged:
+    case NSOtherMouseDown:
+    case NSOtherMouseUp:
+      modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
+      break;
+    default:
+      break;
+  }
+
+  return modifiers;
+}
+
+- (BOOL)isKeyUpEvent:(NSEvent*)event {
+  if ([event type] != NSFlagsChanged)
+    return [event type] == NSKeyUp;
+
+  // FIXME: This logic fails if the user presses both Shift keys at once, for
+  // example: we treat releasing one of them as keyDown.
+  switch ([event keyCode]) {
+    case 54:  // Right Command
+    case 55:  // Left Command
+      return ([event modifierFlags] & NSCommandKeyMask) == 0;
+
+    case 57:  // Capslock
+      return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0;
+
+    case 56:  // Left Shift
+    case 60:  // Right Shift
+      return ([event modifierFlags] & NSShiftKeyMask) == 0;
+
+    case 58:  // Left Alt
+    case 61:  // Right Alt
+      return ([event modifierFlags] & NSAlternateKeyMask) == 0;
+
+    case 59:  // Left Ctrl
+    case 62:  // Right Ctrl
+      return ([event modifierFlags] & NSControlKeyMask) == 0;
+
+    case 63:  // Function
+      return ([event modifierFlags] & NSFunctionKeyMask) == 0;
+  }
+  return false;
+}
+
+- (BOOL)isKeyPadEvent:(NSEvent*)event {
+  if ([event modifierFlags] & NSNumericPadKeyMask)
+    return true;
+
+  switch ([event keyCode]) {
+    case 71:  // Clear
+    case 81:  // =
+    case 75:  // /
+    case 67:  // *
+    case 78:  // -
+    case 69:  // +
+    case 76:  // Enter
+    case 65:  // .
+    case 82:  // 0
+    case 83:  // 1
+    case 84:  // 2
+    case 85:  // 3
+    case 86:  // 4
+    case 87:  // 5
+    case 88:  // 6
+    case 89:  // 7
+    case 91:  // 8
+    case 92:  // 9
+      return true;
+  }
+
+  return false;
+}
+
+- (void)windowDidChangeBackingProperties:(NSNotification*)notification {
+  // This delegate method is only called on 10.7 and later, so don't worry about
+  // other backing changes calling it on 10.6 or earlier
+  [self resetDeviceScaleFactor];
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if ([self inLiveResize] || !browser.get()) {
+    // Fill with the background color.
+    const cef_color_t background_color =
+        client::MainContext::Get()->GetBackgroundColor();
+    NSColor* color = [NSColor
+        colorWithCalibratedRed:float(CefColorGetR(background_color)) / 255.0f
+                         green:float(CefColorGetG(background_color)) / 255.0f
+                          blue:float(CefColorGetB(background_color)) / 255.0f
+                         alpha:1.f];
+    [color setFill];
+    NSRectFill(dirtyRect);
+  }
+
+  // The Invalidate below fixes flicker when resizing.
+  if ([self inLiveResize] && browser.get())
+    browser->GetHost()->Invalidate(PET_VIEW);
+}
+
+// Drag and drop
+
+- (BOOL)startDragging:(CefRefPtr<CefDragData>)drag_data
+           allowedOps:(NSDragOperation)ops
+                point:(NSPoint)position {
+  DCHECK(!pasteboard_);
+  DCHECK(!fileUTI_);
+  DCHECK(!current_drag_data_.get());
+
+  [self resetDragDrop];
+
+  current_allowed_ops_ = ops;
+  current_drag_data_ = drag_data;
+
+  [self fillPasteboard];
+
+  NSEvent* currentEvent = [[NSApplication sharedApplication] currentEvent];
+  NSWindow* window = [self window];
+  NSTimeInterval eventTime = [currentEvent timestamp];
+
+  NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
+                                          location:position
+                                     modifierFlags:NSLeftMouseDraggedMask
+                                         timestamp:eventTime
+                                      windowNumber:[window windowNumber]
+                                           context:nil
+                                       eventNumber:0
+                                        clickCount:1
+                                          pressure:1.0];
+
+  // TODO(cef): Pass a non-nil value to dragImage (see issue #1715). For now
+  // work around the "callee requires a non-null argument" error that occurs
+  // when building with the 10.11 SDK.
+  id nilArg = nil;
+  [window dragImage:nilArg
+                 at:position
+             offset:NSZeroSize
+              event:dragEvent
+         pasteboard:pasteboard_
+             source:self
+          slideBack:YES];
+  return YES;
+}
+
+- (void)setCurrentDragOp:(NSDragOperation)op {
+  current_drag_op_ = op;
+}
+
+// NSDraggingSource Protocol
+
+- (NSDragOperation)draggingSession:(NSDraggingSession*)session
+    sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
+  switch (context) {
+    case NSDraggingContextOutsideApplication:
+      return current_allowed_ops_;
+
+    case NSDraggingContextWithinApplication:
+    default:
+      return current_allowed_ops_;
+  }
+}
+
+- (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDest {
+  if (![dropDest isFileURL])
+    return nil;
+
+  if (!current_drag_data_)
+    return nil;
+
+  size_t expected_size = current_drag_data_->GetFileContents(NULL);
+  if (expected_size == 0)
+    return nil;
+
+  std::string path = [[dropDest path] UTF8String];
+  path.append("/");
+  path.append(current_drag_data_->GetFileName().ToString());
+
+  CefRefPtr<CefStreamWriter> writer = CefStreamWriter::CreateForFile(path);
+  if (!writer)
+    return nil;
+
+  if (current_drag_data_->GetFileContents(writer) != expected_size)
+    return nil;
+
+  return @[ [NSString stringWithUTF8String:path.c_str()] ];
+}
+
+- (void)draggedImage:(NSImage*)anImage
+             endedAt:(NSPoint)screenPoint
+           operation:(NSDragOperation)operation {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return;
+
+  if (operation == (NSDragOperationMove | NSDragOperationCopy))
+    operation &= ~NSDragOperationMove;
+
+  NSPoint windowPoint = [[self window] convertScreenToBase:screenPoint];
+  NSPoint pt = [self flipWindowPointToView:windowPoint];
+  CefRenderHandler::DragOperation op =
+      static_cast<CefRenderHandler::DragOperation>(operation);
+  browser->GetHost()->DragSourceEndedAt(pt.x, pt.y, op);
+  browser->GetHost()->DragSourceSystemDragEnded();
+  [self resetDragDrop];
+}
+
+// NSDraggingDestination Protocol
+
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return NSDragOperationNone;
+
+  CefRefPtr<CefDragData> drag_data;
+  if (!current_drag_data_) {
+    drag_data = CefDragData::Create();
+    [self populateDropData:drag_data fromPasteboard:[info draggingPasteboard]];
+  } else {
+    drag_data = current_drag_data_->Clone();
+    drag_data->ResetFileContents();
+  }
+
+  CefMouseEvent mouseEvent;
+  [self getMouseEvent:mouseEvent forDragInfo:info];
+
+  NSDragOperation mask = [info draggingSourceOperationMask];
+  CefBrowserHost::DragOperationsMask allowed_ops =
+      static_cast<CefBrowserHost::DragOperationsMask>(mask);
+
+  browser->GetHost()->DragTargetDragEnter(drag_data, mouseEvent, allowed_ops);
+  browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops);
+
+  current_drag_op_ = NSDragOperationCopy;
+  return current_drag_op_;
+}
+
+- (void)draggingExited:(id<NSDraggingInfo>)sender {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser.get())
+    browser->GetHost()->DragTargetDragLeave();
+}
+
+- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
+  return YES;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return NO;
+
+  CefMouseEvent mouseEvent;
+  [self getMouseEvent:mouseEvent forDragInfo:info];
+
+  browser->GetHost()->DragTargetDrop(mouseEvent);
+
+  return YES;
+}
+
+- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (!browser.get())
+    return NSDragOperationNone;
+
+  CefMouseEvent mouseEvent;
+  [self getMouseEvent:mouseEvent forDragInfo:info];
+
+  NSDragOperation mask = [info draggingSourceOperationMask];
+  CefBrowserHost::DragOperationsMask allowed_ops =
+      static_cast<CefBrowserHost::DragOperationsMask>(mask);
+
+  browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops);
+
+  return current_drag_op_;
+}
+
+// NSPasteboardOwner Protocol
+
+- (void)pasteboard:(NSPasteboard*)pboard provideDataForType:(NSString*)type {
+  if (!current_drag_data_) {
+    return;
+  }
+
+  // URL.
+  if ([type isEqualToString:NSURLPboardType]) {
+    DCHECK(current_drag_data_->IsLink());
+    NSString* strUrl =
+        [NSString stringWithUTF8String:current_drag_data_->GetLinkURL()
+                                           .ToString()
+                                           .c_str()];
+    NSURL* url = [NSURL URLWithString:strUrl];
+    [url writeToPasteboard:pboard];
+    // URL title.
+  } else if ([type isEqualToString:kNSURLTitlePboardType]) {
+    NSString* strTitle =
+        [NSString stringWithUTF8String:current_drag_data_->GetLinkTitle()
+                                           .ToString()
+                                           .c_str()];
+    [pboard setString:strTitle forType:kNSURLTitlePboardType];
+
+    // File contents.
+  } else if ([type isEqualToString:fileUTI_]) {
+    size_t size = current_drag_data_->GetFileContents(NULL);
+    DCHECK_GT(size, 0U);
+    CefRefPtr<client::BytesWriteHandler> handler =
+        new client::BytesWriteHandler(size);
+    CefRefPtr<CefStreamWriter> writer =
+        CefStreamWriter::CreateForHandler(handler.get());
+    current_drag_data_->GetFileContents(writer);
+    DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(size));
+
+    [pboard setData:[NSData dataWithBytes:handler->GetData()
+                                   length:handler->GetDataSize()]
+            forType:fileUTI_];
+
+    // Plain text.
+  } else if ([type isEqualToString:NSStringPboardType]) {
+    NSString* strTitle =
+        [NSString stringWithUTF8String:current_drag_data_->GetFragmentText()
+                                           .ToString()
+                                           .c_str()];
+    [pboard setString:strTitle forType:NSStringPboardType];
+
+  } else if ([type isEqualToString:kCEFDragDummyPboardType]) {
+    // The dummy type _was_ promised and someone decided to call the bluff.
+    [pboard setData:[NSData data] forType:kCEFDragDummyPboardType];
+  }
+}
+
+// NSAccessibility Protocol implementation.
+- (BOOL)accessibilityIsIgnored {
+  if (!accessibility_helper_)
+    return YES;
+  else
+    return NO;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute {
+  if (!accessibility_helper_)
+    return [super accessibilityAttributeValue:attribute];
+  if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+    return NSAccessibilityGroupRole;
+  } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
+    client::OsrAXNode* node = accessibility_helper_->GetRootNode();
+    std::string desc = node ? node->AxDescription() : "";
+    return [NSString stringWithUTF8String:desc.c_str()];
+  } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
+    client::OsrAXNode* node = accessibility_helper_->GetRootNode();
+    std::string desc = node ? node->AxValue() : "";
+    return [NSString stringWithUTF8String:desc.c_str()];
+  } else if ([attribute
+                 isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
+    return NSAccessibilityRoleDescriptionForUIElement(self);
+  } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+    client::OsrAXNode* node = accessibility_helper_->GetRootNode();
+    // Add Root as first Kid
+    NSMutableArray* kids = [NSMutableArray arrayWithCapacity:1];
+    NSObject* child = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
+        node->GetNativeAccessibleObject(NULL));
+    [kids addObject:child];
+    return NSAccessibilityUnignoredChildren(kids);
+  } else {
+    return [super accessibilityAttributeValue:attribute];
+  }
+}
+
+- (id)accessibilityFocusedUIElement {
+  if (accessibility_helper_) {
+    client::OsrAXNode* node = accessibility_helper_->GetFocusedNode();
+    return node ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
+                      node->GetNativeAccessibleObject(NULL))
+                : nil;
+  }
+  return nil;
+}
+
+// Utility methods.
+- (void)resetDragDrop {
+  current_drag_op_ = NSDragOperationNone;
+  current_allowed_ops_ = NSDragOperationNone;
+  current_drag_data_ = NULL;
+  if (fileUTI_) {
+#if !__has_feature(objc_arc)
+    [fileUTI_ release];
+#endif  // !__has_feature(objc_arc)
+    fileUTI_ = nil;
+  }
+  if (pasteboard_) {
+#if !__has_feature(objc_arc)
+    [pasteboard_ release];
+#endif  // !__has_feature(objc_arc)
+    pasteboard_ = nil;
+  }
+}
+
+- (void)fillPasteboard {
+  DCHECK(!pasteboard_);
+  pasteboard_ = [NSPasteboard pasteboardWithName:NSDragPboard];
+#if !__has_feature(objc_arc)
+  [pasteboard_ retain];
+#endif  // !__has_feature(objc_arc)
+
+  [pasteboard_ declareTypes:@[ kCEFDragDummyPboardType ] owner:self];
+
+  // URL (and title).
+  if (current_drag_data_->IsLink()) {
+    [pasteboard_ addTypes:@[ NSURLPboardType, kNSURLTitlePboardType ]
+                    owner:self];
+  }
+
+  // MIME type.
+  CefString mimeType;
+  size_t contents_size = current_drag_data_->GetFileContents(NULL);
+  CefString download_metadata = current_drag_data_->GetLinkMetadata();
+  CefString file_name = current_drag_data_->GetFileName();
+
+  // File.
+  if (contents_size > 0) {
+    std::string file_name = current_drag_data_->GetFileName().ToString();
+    size_t sep = file_name.find_last_of(".");
+    CefString extension = file_name.substr(sep + 1);
+
+    mimeType = CefGetMimeType(extension);
+
+    if (!mimeType.empty()) {
+      CFStringRef mimeTypeCF;
+      mimeTypeCF = CFStringCreateWithCString(kCFAllocatorDefault,
+                                             mimeType.ToString().c_str(),
+                                             kCFStringEncodingUTF8);
+      fileUTI_ = (__bridge NSString*)UTTypeCreatePreferredIdentifierForTag(
+          kUTTagClassMIMEType, mimeTypeCF, NULL);
+      CFRelease(mimeTypeCF);
+      // File (HFS) promise.
+      NSArray* fileUTIList = @[ fileUTI_ ];
+      [pasteboard_ addTypes:@[ NSFilesPromisePboardType ] owner:self];
+      [pasteboard_ setPropertyList:fileUTIList
+                           forType:NSFilesPromisePboardType];
+
+      [pasteboard_ addTypes:fileUTIList owner:self];
+    }
+  }
+
+  // Plain text.
+  if (!current_drag_data_->GetFragmentText().empty()) {
+    [pasteboard_ addTypes:@[ NSStringPboardType ] owner:self];
+  }
+}
+
+- (void)populateDropData:(CefRefPtr<CefDragData>)data
+          fromPasteboard:(NSPasteboard*)pboard {
+  DCHECK(data);
+  DCHECK(pboard);
+  DCHECK(data && !data->IsReadOnly());
+  NSArray* types = [pboard types];
+
+  // Get plain text.
+  if ([types containsObject:NSStringPboardType]) {
+    data->SetFragmentText(
+        [[pboard stringForType:NSStringPboardType] UTF8String]);
+  }
+
+  // Get files.
+  if ([types containsObject:NSFilenamesPboardType]) {
+    NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
+    if ([files isKindOfClass:[NSArray class]] && [files count]) {
+      for (NSUInteger i = 0; i < [files count]; i++) {
+        NSString* filename = [files objectAtIndex:i];
+        BOOL exists =
+            [[NSFileManager defaultManager] fileExistsAtPath:filename];
+        if (exists) {
+          data->AddFile([filename UTF8String], CefString());
+        }
+      }
+    }
+  }
+}
+
+- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint {
+  NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
+  NSRect viewFrame = [self frame];
+  viewPoint.y = viewFrame.size.height - viewPoint.y;
+  return viewPoint;
+}
+
+- (void)resetDeviceScaleFactor {
+  float device_scale_factor = 1.0f;
+  NSWindow* window = [self window];
+  if (window)
+    device_scale_factor = [window backingScaleFactor];
+  [self setDeviceScaleFactor:device_scale_factor];
+}
+
+- (void)setDeviceScaleFactor:(float)device_scale_factor {
+  if (device_scale_factor == device_scale_factor_)
+    return;
+
+  // Apply some sanity checks.
+  if (device_scale_factor < 1.0f || device_scale_factor > 4.0f)
+    return;
+
+  device_scale_factor_ = device_scale_factor;
+
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser) {
+    browser->GetHost()->NotifyScreenInfoChanged();
+    browser->GetHost()->WasResized();
+  }
+}
+
+- (float)getDeviceScaleFactor {
+  return device_scale_factor_;
+}
+
+- (void)viewDidChangeBackingProperties {
+  const CGFloat device_scale_factor = [self getDeviceScaleFactor];
+
+  if (device_scale_factor == device_scale_factor_)
+    return;
+
+  CefRefPtr<CefBrowser> browser = [self getBrowser];
+  if (browser) {
+    browser->GetHost()->NotifyScreenInfoChanged();
+    browser->GetHost()->WasResized();
+  }
+}
+
+- (bool)isOverPopupWidgetX:(int)x andY:(int)y {
+  CefRect rc = renderer_->popup_rect();
+  int popup_right = rc.x + rc.width;
+  int popup_bottom = rc.y + rc.height;
+  return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom);
+}
+
+- (int)getPopupXOffset {
+  return renderer_->original_popup_rect().x - renderer_->popup_rect().x;
+}
+
+- (int)getPopupYOffset {
+  return renderer_->original_popup_rect().y - renderer_->popup_rect().y;
+}
+
+- (void)applyPopupOffsetToX:(int&)x andY:(int&)y {
+  if ([self isOverPopupWidgetX:x andY:y]) {
+    x += [self getPopupXOffset];
+    y += [self getPopupYOffset];
+  }
+}
+
+// Convert from scaled coordinates to view coordinates.
+- (NSPoint)convertPointFromBackingInternal:(NSPoint)aPoint {
+  return [self convertPointFromBacking:aPoint];
+}
+
+// Convert from view coordinates to scaled coordinates.
+- (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint {
+  return [self convertPointToBacking:aPoint];
+}
+
+// Convert from scaled coordinates to view coordinates.
+- (NSRect)convertRectFromBackingInternal:(NSRect)aRect {
+  return [self convertRectFromBacking:aRect];
+}
+
+// Convert from view coordinates to scaled coordinates.
+- (NSRect)convertRectToBackingInternal:(NSRect)aRect {
+  return [self convertRectToBacking:aRect];
+}
+
+- (void)ChangeCompositionRange:(CefRange)range
+              character_bounds:(const CefRenderHandler::RectList&)bounds {
+  if (text_input_client_)
+    [text_input_client_ ChangeCompositionRange:range character_bounds:bounds];
+}
+
+- (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value {
+  if (!accessibility_helper_) {
+    accessibility_helper_ =
+        new client::OsrAccessibilityHelper(value, [self getBrowser]);
+  } else {
+    accessibility_helper_->UpdateAccessibilityTree(value);
+  }
+
+  if (accessibility_helper_) {
+    NSAccessibilityPostNotification(self,
+                                    NSAccessibilityValueChangedNotification);
+  }
+  return;
+}
+
+- (void)UpdateAccessibilityLocation:(CefRefPtr<CefValue>)value {
+  if (accessibility_helper_) {
+    accessibility_helper_->UpdateAccessibilityLocation(value);
+  }
+
+  if (accessibility_helper_) {
+    NSAccessibilityPostNotification(self,
+                                    NSAccessibilityValueChangedNotification);
+  }
+  return;
+}
+@end
+
+namespace client {
+
+class BrowserWindowOsrMacImpl {
+ public:
+  BrowserWindowOsrMacImpl(BrowserWindow::Delegate* delegate,
+                          const std::string& startup_url,
+                          const OsrRendererSettings& settings,
+                          BrowserWindowOsrMac& browser_window);
+  ~BrowserWindowOsrMacImpl();
+
+  // BrowserWindow methods.
+  void CreateBrowser(ClientWindowHandle parent_handle,
+                     const CefRect& rect,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context);
+  void GetPopupConfig(CefWindowHandle temp_handle,
+                      CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient>& client,
+                      CefBrowserSettings& settings);
+  void ShowPopup(ClientWindowHandle parent_handle,
+                 int x,
+                 int y,
+                 size_t width,
+                 size_t height);
+  void Show();
+  void Hide();
+  void SetBounds(int x, int y, size_t width, size_t height);
+  void SetFocus(bool focus);
+  void SetDeviceScaleFactor(float device_scale_factor);
+  float GetDeviceScaleFactor() const;
+  ClientWindowHandle GetWindowHandle() const;
+
+  // ClientHandlerOsr::OsrDelegate methods.
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser);
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser);
+  bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect);
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect);
+  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                      int viewX,
+                      int viewY,
+                      int& screenX,
+                      int& screenY);
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser, CefScreenInfo& screen_info);
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show);
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect);
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height);
+  void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                      CefCursorHandle cursor,
+                      CefRenderHandler::CursorType type,
+                      const CefCursorInfo& custom_cursor_info);
+  bool StartDragging(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefDragData> drag_data,
+                     CefRenderHandler::DragOperationsMask allowed_ops,
+                     int x,
+                     int y);
+  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                        CefRenderHandler::DragOperation operation);
+  void OnImeCompositionRangeChanged(
+      CefRefPtr<CefBrowser> browser,
+      const CefRange& selection_range,
+      const CefRenderHandler::RectList& character_bounds);
+
+  void UpdateAccessibilityTree(CefRefPtr<CefValue> value);
+  void UpdateAccessibilityLocation(CefRefPtr<CefValue> value);
+
+ private:
+  // Create the NSView.
+  void Create(ClientWindowHandle parent_handle, const CefRect& rect);
+
+  BrowserWindowOsrMac& browser_window_;
+  // The below members will only be accessed on the main thread which should be
+  // the same as the CEF UI thread.
+  OsrRenderer renderer_;
+  BrowserOpenGLView* native_browser_view_;
+  bool hidden_;
+  bool painting_popup_;
+};
+
+BrowserWindowOsrMacImpl::BrowserWindowOsrMacImpl(
+    BrowserWindow::Delegate* delegate,
+    const std::string& startup_url,
+    const OsrRendererSettings& settings,
+    BrowserWindowOsrMac& browser_window)
+    : browser_window_(browser_window),
+      renderer_(settings),
+      native_browser_view_(nil),
+      hidden_(false),
+      painting_popup_(false) {}
+
+BrowserWindowOsrMacImpl::~BrowserWindowOsrMacImpl() {
+  if (native_browser_view_) {
+    // Disassociate the view with |this|.
+    [native_browser_view_ detach];
+  }
+}
+
+void BrowserWindowOsrMacImpl::CreateBrowser(
+    ClientWindowHandle parent_handle,
+    const CefRect& rect,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  REQUIRE_MAIN_THREAD();
+
+  // Create the native NSView.
+  Create(parent_handle, rect);
+
+  CefWindowInfo window_info;
+  window_info.SetAsWindowless(
+      CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_));
+
+  // Create the browser asynchronously.
+  CefBrowserHost::CreateBrowser(window_info, browser_window_.client_handler_,
+                                browser_window_.client_handler_->startup_url(),
+                                settings, extra_info, request_context);
+}
+
+void BrowserWindowOsrMacImpl::GetPopupConfig(CefWindowHandle temp_handle,
+                                             CefWindowInfo& windowInfo,
+                                             CefRefPtr<CefClient>& client,
+                                             CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  windowInfo.SetAsWindowless(temp_handle);
+  client = browser_window_.client_handler_;
+}
+
+void BrowserWindowOsrMacImpl::ShowPopup(ClientWindowHandle parent_handle,
+                                        int x,
+                                        int y,
+                                        size_t width,
+                                        size_t height) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(browser_window_.browser_.get());
+
+  // Create the native NSView.
+  Create(parent_handle,
+         CefRect(x, y, static_cast<int>(width), static_cast<int>(height)));
+
+  // Send resize notification so the compositor is assigned the correct
+  // viewport size and begins rendering.
+  browser_window_.browser_->GetHost()->WasResized();
+
+  Show();
+}
+
+void BrowserWindowOsrMacImpl::Show() {
+  REQUIRE_MAIN_THREAD();
+
+  if (hidden_) {
+    // Set the browser as visible.
+    browser_window_.browser_->GetHost()->WasHidden(false);
+    hidden_ = false;
+  }
+
+  // Give focus to the browser.
+  browser_window_.browser_->GetHost()->SendFocusEvent(true);
+}
+
+void BrowserWindowOsrMacImpl::Hide() {
+  REQUIRE_MAIN_THREAD();
+
+  if (!browser_window_.browser_.get())
+    return;
+
+  // Remove focus from the browser.
+  browser_window_.browser_->GetHost()->SendFocusEvent(false);
+
+  if (!hidden_) {
+    // Set the browser as hidden.
+    browser_window_.browser_->GetHost()->WasHidden(true);
+    hidden_ = true;
+  }
+}
+
+void BrowserWindowOsrMacImpl::SetBounds(int x,
+                                        int y,
+                                        size_t width,
+                                        size_t height) {
+  REQUIRE_MAIN_THREAD();
+  // Nothing to do here. GTK will take care of positioning in the container.
+}
+
+void BrowserWindowOsrMacImpl::SetFocus(bool focus) {
+  REQUIRE_MAIN_THREAD();
+  if (native_browser_view_)
+    [native_browser_view_.window makeFirstResponder:native_browser_view_];
+}
+
+void BrowserWindowOsrMacImpl::SetDeviceScaleFactor(float device_scale_factor) {
+  REQUIRE_MAIN_THREAD();
+  if (native_browser_view_)
+    [native_browser_view_ setDeviceScaleFactor:device_scale_factor];
+}
+
+float BrowserWindowOsrMacImpl::GetDeviceScaleFactor() const {
+  REQUIRE_MAIN_THREAD();
+  if (native_browser_view_)
+    return [native_browser_view_ getDeviceScaleFactor];
+  return 1.0f;
+}
+
+ClientWindowHandle BrowserWindowOsrMacImpl::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+  return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_);
+}
+
+void BrowserWindowOsrMacImpl::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+}
+
+void BrowserWindowOsrMacImpl::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  // Detach |this| from the ClientHandlerOsr.
+  static_cast<ClientHandlerOsr*>(browser_window_.client_handler_.get())
+      ->DetachOsrDelegate();
+}
+
+bool BrowserWindowOsrMacImpl::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                                CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  return false;
+}
+
+void BrowserWindowOsrMacImpl::GetViewRect(CefRefPtr<CefBrowser> browser,
+                                          CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  rect.x = rect.y = 0;
+
+  if (!native_browser_view_) {
+    // Never return an empty rectangle.
+    rect.width = rect.height = 1;
+    return;
+  }
+
+  const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
+
+  // |bounds| is in OS X view coordinates.
+  NSRect bounds = native_browser_view_.bounds;
+
+  // Convert to device coordinates.
+  bounds = [native_browser_view_ convertRectToBackingInternal:bounds];
+
+  // Convert to browser view coordinates.
+  rect.width = DeviceToLogical(bounds.size.width, device_scale_factor);
+  if (rect.width == 0)
+    rect.width = 1;
+  rect.height = DeviceToLogical(bounds.size.height, device_scale_factor);
+  if (rect.height == 0)
+    rect.height = 1;
+}
+
+bool BrowserWindowOsrMacImpl::GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                             int viewX,
+                                             int viewY,
+                                             int& screenX,
+                                             int& screenY) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  if (!native_browser_view_)
+    return false;
+
+  const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
+
+  // (viewX, viewX) is in browser view coordinates.
+  // Convert to device coordinates.
+  NSPoint view_pt = NSMakePoint(LogicalToDevice(viewX, device_scale_factor),
+                                LogicalToDevice(viewY, device_scale_factor));
+
+  // Convert to OS X view coordinates.
+  view_pt = [native_browser_view_ convertPointFromBackingInternal:view_pt];
+
+  // Reverse the Y component.
+  const NSRect bounds = native_browser_view_.bounds;
+  view_pt.y = bounds.size.height - view_pt.y;
+
+  // Convert to screen coordinates.
+  NSPoint window_pt = [native_browser_view_ convertPoint:view_pt toView:nil];
+  NSPoint screen_pt =
+      ConvertPointFromWindowToScreen(native_browser_view_.window, window_pt);
+
+  screenX = screen_pt.x;
+  screenY = screen_pt.y;
+  return true;
+}
+
+bool BrowserWindowOsrMacImpl::GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                                            CefScreenInfo& screen_info) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  if (!native_browser_view_)
+    return false;
+
+  CefRect view_rect;
+  GetViewRect(browser, view_rect);
+
+  screen_info.device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
+
+  // The screen info rectangles are used by the renderer to create and position
+  // popups. Keep popups inside the view rectangle.
+  screen_info.rect = view_rect;
+  screen_info.available_rect = view_rect;
+
+  return true;
+}
+
+void BrowserWindowOsrMacImpl::OnPopupShow(CefRefPtr<CefBrowser> browser,
+                                          bool show) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  if (!native_browser_view_)
+    return;
+
+  if (!show) {
+    renderer_.ClearPopupRects();
+    browser->GetHost()->Invalidate(PET_VIEW);
+  }
+  renderer_.OnPopupShow(browser, show);
+}
+
+void BrowserWindowOsrMacImpl::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                                          const CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  if (!native_browser_view_)
+    return;
+
+  const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
+
+  // |rect| is in browser view coordinates. Convert to device coordinates.
+  CefRect device_rect = LogicalToDevice(rect, device_scale_factor);
+
+  renderer_.OnPopupSize(browser, device_rect);
+}
+
+void BrowserWindowOsrMacImpl::OnPaint(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::PaintElementType type,
+    const CefRenderHandler::RectList& dirtyRects,
+    const void* buffer,
+    int width,
+    int height) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  if (!native_browser_view_)
+    return;
+
+  if (width <= 2 && height <= 2) {
+    // Ignore really small buffer sizes while the widget is starting up.
+    return;
+  }
+
+  if (painting_popup_) {
+    renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+    return;
+  }
+
+  ScopedGLContext scoped_gl_context(native_browser_view_, true);
+
+  renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+  if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
+    painting_popup_ = true;
+    browser->GetHost()->Invalidate(PET_POPUP);
+    painting_popup_ = false;
+  }
+  renderer_.Render();
+}
+
+void BrowserWindowOsrMacImpl::OnCursorChange(
+    CefRefPtr<CefBrowser> browser,
+    CefCursorHandle cursor,
+    CefRenderHandler::CursorType type,
+    const CefCursorInfo& custom_cursor_info) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  [CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(cursor) set];
+}
+
+bool BrowserWindowOsrMacImpl::StartDragging(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDragData> drag_data,
+    CefRenderHandler::DragOperationsMask allowed_ops,
+    int x,
+    int y) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  if (!native_browser_view_)
+    return false;
+
+  static float device_scale_factor =
+      [native_browser_view_ getDeviceScaleFactor];
+
+  // |point| is in browser view coordinates.
+  NSPoint point = NSMakePoint(x, y);
+
+  // Convert to device coordinates.
+  point.x = LogicalToDevice(point.x, device_scale_factor);
+  point.y = LogicalToDevice(point.y, device_scale_factor);
+
+  // Convert to OS X view coordinates.
+  point = [native_browser_view_ convertPointFromBackingInternal:point];
+
+  return [native_browser_view_
+      startDragging:drag_data
+         allowedOps:static_cast<NSDragOperation>(allowed_ops)
+              point:point];
+}
+
+void BrowserWindowOsrMacImpl::UpdateDragCursor(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::DragOperation operation) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  if (native_browser_view_)
+    [native_browser_view_ setCurrentDragOp:operation];
+}
+
+void BrowserWindowOsrMacImpl::OnImeCompositionRangeChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefRange& selection_range,
+    const CefRenderHandler::RectList& bounds) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (native_browser_view_) {
+    [native_browser_view_ ChangeCompositionRange:selection_range
+                                character_bounds:bounds];
+  }
+}
+
+void BrowserWindowOsrMacImpl::UpdateAccessibilityTree(
+    CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (native_browser_view_) {
+    [native_browser_view_ UpdateAccessibilityTree:value];
+  }
+}
+
+void BrowserWindowOsrMacImpl::UpdateAccessibilityLocation(
+    CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (native_browser_view_) {
+    [native_browser_view_ UpdateAccessibilityLocation:value];
+  }
+}
+
+void BrowserWindowOsrMacImpl::Create(ClientWindowHandle parent_handle,
+                                     const CefRect& rect) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!native_browser_view_);
+
+  NSRect window_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height);
+  native_browser_view_ =
+      [[BrowserOpenGLView alloc] initWithFrame:window_rect
+                              andBrowserWindow:&browser_window_
+                                   andRenderer:&renderer_];
+  native_browser_view_.autoresizingMask =
+      (NSViewWidthSizable | NSViewHeightSizable);
+  native_browser_view_.autoresizesSubviews = YES;
+  [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle)
+      addSubview:native_browser_view_];
+
+  // Determine the default scale factor.
+  [native_browser_view_ resetDeviceScaleFactor];
+
+  [[NSNotificationCenter defaultCenter]
+      addObserver:native_browser_view_
+         selector:@selector(windowDidChangeBackingProperties:)
+             name:NSWindowDidChangeBackingPropertiesNotification
+           object:native_browser_view_.window];
+}
+
+BrowserWindowOsrMac::BrowserWindowOsrMac(BrowserWindow::Delegate* delegate,
+                                         const std::string& startup_url,
+                                         const OsrRendererSettings& settings)
+    : BrowserWindow(delegate) {
+  client_handler_ = new ClientHandlerOsr(this, this, startup_url);
+  impl_.reset(
+      new BrowserWindowOsrMacImpl(delegate, startup_url, settings, *this));
+}
+
+BrowserWindowOsrMac::~BrowserWindowOsrMac() {}
+
+void BrowserWindowOsrMac::CreateBrowser(
+    ClientWindowHandle parent_handle,
+    const CefRect& rect,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  impl_->CreateBrowser(parent_handle, rect, settings, extra_info,
+                       request_context);
+}
+
+void BrowserWindowOsrMac::GetPopupConfig(CefWindowHandle temp_handle,
+                                         CefWindowInfo& windowInfo,
+                                         CefRefPtr<CefClient>& client,
+                                         CefBrowserSettings& settings) {
+  impl_->GetPopupConfig(temp_handle, windowInfo, client, settings);
+}
+
+void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle,
+                                    int x,
+                                    int y,
+                                    size_t width,
+                                    size_t height) {
+  impl_->ShowPopup(parent_handle, x, y, width, height);
+}
+
+void BrowserWindowOsrMac::Show() {
+  impl_->Show();
+}
+
+void BrowserWindowOsrMac::Hide() {
+  impl_->Hide();
+}
+
+void BrowserWindowOsrMac::SetBounds(int x, int y, size_t width, size_t height) {
+  impl_->SetBounds(x, y, width, height);
+}
+
+void BrowserWindowOsrMac::SetFocus(bool focus) {
+  impl_->SetFocus(focus);
+}
+
+void BrowserWindowOsrMac::SetDeviceScaleFactor(float device_scale_factor) {
+  impl_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float BrowserWindowOsrMac::GetDeviceScaleFactor() const {
+  return impl_->GetDeviceScaleFactor();
+}
+
+ClientWindowHandle BrowserWindowOsrMac::GetWindowHandle() const {
+  return impl_->GetWindowHandle();
+}
+
+void BrowserWindowOsrMac::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  impl_->OnAfterCreated(browser);
+}
+
+void BrowserWindowOsrMac::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  impl_->OnBeforeClose(browser);
+}
+
+bool BrowserWindowOsrMac::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                            CefRect& rect) {
+  return impl_->GetRootScreenRect(browser, rect);
+}
+
+void BrowserWindowOsrMac::GetViewRect(CefRefPtr<CefBrowser> browser,
+                                      CefRect& rect) {
+  impl_->GetViewRect(browser, rect);
+}
+
+bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                         int viewX,
+                                         int viewY,
+                                         int& screenX,
+                                         int& screenY) {
+  return impl_->GetScreenPoint(browser, viewX, viewY, screenX, screenY);
+}
+
+bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                                        CefScreenInfo& screen_info) {
+  return impl_->GetScreenInfo(browser, screen_info);
+}
+
+void BrowserWindowOsrMac::OnPopupShow(CefRefPtr<CefBrowser> browser,
+                                      bool show) {
+  impl_->OnPopupShow(browser, show);
+}
+
+void BrowserWindowOsrMac::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                                      const CefRect& rect) {
+  impl_->OnPopupSize(browser, rect);
+}
+
+void BrowserWindowOsrMac::OnPaint(CefRefPtr<CefBrowser> browser,
+                                  CefRenderHandler::PaintElementType type,
+                                  const CefRenderHandler::RectList& dirtyRects,
+                                  const void* buffer,
+                                  int width,
+                                  int height) {
+  impl_->OnPaint(browser, type, dirtyRects, buffer, width, height);
+}
+
+void BrowserWindowOsrMac::OnCursorChange(
+    CefRefPtr<CefBrowser> browser,
+    CefCursorHandle cursor,
+    CefRenderHandler::CursorType type,
+    const CefCursorInfo& custom_cursor_info) {
+  impl_->OnCursorChange(browser, cursor, type, custom_cursor_info);
+}
+
+bool BrowserWindowOsrMac::StartDragging(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDragData> drag_data,
+    CefRenderHandler::DragOperationsMask allowed_ops,
+    int x,
+    int y) {
+  return impl_->StartDragging(browser, drag_data, allowed_ops, x, y);
+}
+
+void BrowserWindowOsrMac::UpdateDragCursor(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::DragOperation operation) {
+  impl_->UpdateDragCursor(browser, operation);
+}
+
+void BrowserWindowOsrMac::OnImeCompositionRangeChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefRange& selection_range,
+    const CefRenderHandler::RectList& character_bounds) {
+  impl_->OnImeCompositionRangeChanged(browser, selection_range,
+                                      character_bounds);
+}
+
+void BrowserWindowOsrMac::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
+  impl_->UpdateAccessibilityTree(value);
+}
+
+void BrowserWindowOsrMac::UpdateAccessibilityLocation(
+    CefRefPtr<CefValue> value) {
+  impl_->UpdateAccessibilityLocation(value);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/browser_window_osr_win.cc b/src/tests/cefclient/browser/browser_window_osr_win.cc
new file mode 100644
index 0000000..053c655
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_osr_win.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/browser_window_osr_win.h"
+
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+BrowserWindowOsrWin::BrowserWindowOsrWin(BrowserWindow::Delegate* delegate,
+                                         const std::string& startup_url,
+                                         const OsrRendererSettings& settings)
+    : BrowserWindow(delegate), osr_hwnd_(NULL), device_scale_factor_(0) {
+  osr_window_ = new OsrWindowWin(this, settings);
+  client_handler_ = new ClientHandlerOsr(this, osr_window_.get(), startup_url);
+}
+
+void BrowserWindowOsrWin::CreateBrowser(
+    ClientWindowHandle parent_handle,
+    const CefRect& rect,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  REQUIRE_MAIN_THREAD();
+
+  // Create the new browser and native window on the UI thread.
+  RECT wnd_rect = {rect.x, rect.y, rect.x + rect.width, rect.y + rect.height};
+  osr_window_->CreateBrowser(parent_handle, wnd_rect, client_handler_, settings,
+                             extra_info, request_context,
+                             client_handler_->startup_url());
+}
+
+void BrowserWindowOsrWin::GetPopupConfig(CefWindowHandle temp_handle,
+                                         CefWindowInfo& windowInfo,
+                                         CefRefPtr<CefClient>& client,
+                                         CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  windowInfo.SetAsWindowless(temp_handle);
+  windowInfo.shared_texture_enabled =
+      osr_window_->settings().shared_texture_enabled;
+  windowInfo.external_begin_frame_enabled =
+      osr_window_->settings().external_begin_frame_enabled;
+
+  // Don't activate the hidden browser on creation.
+  windowInfo.ex_style |= WS_EX_NOACTIVATE;
+
+  client = client_handler_;
+}
+
+void BrowserWindowOsrWin::ShowPopup(ClientWindowHandle parent_handle,
+                                    int x,
+                                    int y,
+                                    size_t width,
+                                    size_t height) {
+  REQUIRE_MAIN_THREAD();
+  if (osr_window_)
+    osr_window_->ShowPopup(parent_handle, x, y, width, height);
+}
+
+void BrowserWindowOsrWin::Show() {
+  REQUIRE_MAIN_THREAD();
+  if (osr_window_)
+    osr_window_->Show();
+}
+
+void BrowserWindowOsrWin::Hide() {
+  REQUIRE_MAIN_THREAD();
+  if (osr_window_)
+    osr_window_->Hide();
+}
+
+void BrowserWindowOsrWin::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+  if (osr_window_)
+    osr_window_->SetBounds(x, y, width, height);
+}
+
+void BrowserWindowOsrWin::SetFocus(bool focus) {
+  REQUIRE_MAIN_THREAD();
+  if (osr_window_ && focus)
+    osr_window_->SetFocus();
+}
+
+void BrowserWindowOsrWin::SetDeviceScaleFactor(float device_scale_factor) {
+  REQUIRE_MAIN_THREAD();
+  if (device_scale_factor == device_scale_factor_)
+    return;
+
+  // Apply some sanity checks.
+  if (device_scale_factor < 1.0f || device_scale_factor > 4.0f)
+    return;
+
+  device_scale_factor_ = device_scale_factor;
+  if (osr_window_)
+    osr_window_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float BrowserWindowOsrWin::GetDeviceScaleFactor() const {
+  REQUIRE_MAIN_THREAD();
+  DCHECK_GT(device_scale_factor_, 0);
+  return device_scale_factor_;
+}
+
+ClientWindowHandle BrowserWindowOsrWin::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+  return osr_hwnd_;
+}
+
+void BrowserWindowOsrWin::OnBrowserClosed(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+
+  // Release the OSR window reference. It will be deleted on the UI thread.
+  osr_window_ = nullptr;
+
+  BrowserWindow::OnBrowserClosed(browser);
+}
+
+void BrowserWindowOsrWin::OnOsrNativeWindowCreated(HWND hwnd) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!osr_hwnd_);
+  osr_hwnd_ = hwnd;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/browser_window_osr_win.h b/src/tests/cefclient/browser/browser_window_osr_win.h
new file mode 100644
index 0000000..9cf3027
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_osr_win.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_WIN_H_
+#pragma once
+
+#include "tests/cefclient/browser/browser_window.h"
+#include "tests/cefclient/browser/osr_window_win.h"
+
+namespace client {
+
+// Represents a native child window hosting a single off-screen browser
+// instance. The methods of this class must be called on the main thread unless
+// otherwise indicated.
+class BrowserWindowOsrWin : public BrowserWindow,
+                            public OsrWindowWin::Delegate {
+ public:
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object.
+  BrowserWindowOsrWin(BrowserWindow::Delegate* delegate,
+                      const std::string& startup_url,
+                      const OsrRendererSettings& settings);
+
+  // BrowserWindow methods.
+  void CreateBrowser(ClientWindowHandle parent_handle,
+                     const CefRect& rect,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context) OVERRIDE;
+  void GetPopupConfig(CefWindowHandle temp_handle,
+                      CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient>& client,
+                      CefBrowserSettings& settings) OVERRIDE;
+  void ShowPopup(ClientWindowHandle parent_handle,
+                 int x,
+                 int y,
+                 size_t width,
+                 size_t height) OVERRIDE;
+  void Show() OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void SetFocus(bool focus) OVERRIDE;
+  void SetDeviceScaleFactor(float device_scale_factor) OVERRIDE;
+  float GetDeviceScaleFactor() const OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+
+ private:
+  // ClienHandler::Delegate methods.
+  void OnBrowserClosed(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+  // OsrWindowWin::Delegate methods.
+  void OnOsrNativeWindowCreated(HWND hwnd) OVERRIDE;
+
+  // The below members are only accessed on the main thread.
+  scoped_refptr<OsrWindowWin> osr_window_;
+  HWND osr_hwnd_;
+
+  float device_scale_factor_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserWindowOsrWin);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_OSR_WIN_H_
diff --git a/src/tests/cefclient/browser/browser_window_std_gtk.cc b/src/tests/cefclient/browser/browser_window_std_gtk.cc
new file mode 100644
index 0000000..4810f52
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_std_gtk.cc
@@ -0,0 +1,188 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/browser_window_std_gtk.h"
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include <X11/Xlib.h>
+#undef Success     // Definition conflicts with cef_message_router.h
+#undef RootWindow  // Definition conflicts with root_window.h
+
+#include "include/base/cef_logging.h"
+#include "tests/cefclient/browser/client_handler_std.h"
+#include "tests/cefclient/browser/util_gtk.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+namespace {
+
+::Window GetXWindowForWidget(GtkWidget* widget) {
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  // The GTK window must be visible before we can retrieve the XID.
+  ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(widget));
+  DCHECK(xwindow);
+  return xwindow;
+}
+
+void SetXWindowVisible(XDisplay* xdisplay, ::Window xwindow, bool visible) {
+  CHECK(xdisplay != 0);
+
+  // Retrieve the atoms required by the below XChangeProperty call.
+  const char* kAtoms[] = {"_NET_WM_STATE", "ATOM", "_NET_WM_STATE_HIDDEN"};
+  Atom atoms[3];
+  int result =
+      XInternAtoms(xdisplay, const_cast<char**>(kAtoms), 3, false, atoms);
+  if (!result)
+    NOTREACHED();
+
+  if (!visible) {
+    // Set the hidden property state value.
+    scoped_ptr<Atom[]> data(new Atom[1]);
+    data[0] = atoms[2];
+
+    XChangeProperty(xdisplay, xwindow,
+                    atoms[0],  // name
+                    atoms[1],  // type
+                    32,        // size in bits of items in 'value'
+                    PropModeReplace,
+                    reinterpret_cast<const unsigned char*>(data.get()),
+                    1);  // num items
+  } else {
+    // Set an empty array of property state values.
+    XChangeProperty(xdisplay, xwindow,
+                    atoms[0],  // name
+                    atoms[1],  // type
+                    32,        // size in bits of items in 'value'
+                    PropModeReplace, NULL,
+                    0);  // num items
+  }
+}
+
+void SetXWindowBounds(XDisplay* xdisplay,
+                      ::Window xwindow,
+                      int x,
+                      int y,
+                      size_t width,
+                      size_t height) {
+  CHECK(xdisplay != 0);
+  XWindowChanges changes = {0};
+  changes.x = x;
+  changes.y = y;
+  changes.width = static_cast<int>(width);
+  changes.height = static_cast<int>(height);
+  XConfigureWindow(xdisplay, xwindow, CWX | CWY | CWHeight | CWWidth, &changes);
+}
+
+}  // namespace
+
+BrowserWindowStdGtk::BrowserWindowStdGtk(Delegate* delegate,
+                                         const std::string& startup_url)
+    : BrowserWindow(delegate), xdisplay_(nullptr) {
+  client_handler_ = new ClientHandlerStd(this, startup_url);
+}
+
+void BrowserWindowStdGtk::set_xdisplay(XDisplay* xdisplay) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!xdisplay_);
+  xdisplay_ = xdisplay;
+}
+
+void BrowserWindowStdGtk::CreateBrowser(
+    ClientWindowHandle parent_handle,
+    const CefRect& rect,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  REQUIRE_MAIN_THREAD();
+
+  CefWindowInfo window_info;
+  window_info.SetAsChild(GetXWindowForWidget(parent_handle), rect);
+
+  CefBrowserHost::CreateBrowser(window_info, client_handler_,
+                                client_handler_->startup_url(), settings,
+                                extra_info, request_context);
+}
+
+void BrowserWindowStdGtk::GetPopupConfig(CefWindowHandle temp_handle,
+                                         CefWindowInfo& windowInfo,
+                                         CefRefPtr<CefClient>& client,
+                                         CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // The window will be properly sized after the browser is created.
+  windowInfo.SetAsChild(temp_handle, CefRect());
+  client = client_handler_;
+}
+
+void BrowserWindowStdGtk::ShowPopup(ClientWindowHandle parent_handle,
+                                    int x,
+                                    int y,
+                                    size_t width,
+                                    size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_) {
+    ::Window parent_xwindow = GetXWindowForWidget(parent_handle);
+    CHECK(xdisplay_ != 0);
+    ::Window xwindow = browser_->GetHost()->GetWindowHandle();
+    DCHECK(xwindow);
+
+    XReparentWindow(xdisplay_, xwindow, parent_xwindow, x, y);
+
+    SetXWindowBounds(xdisplay_, xwindow, x, y, width, height);
+    SetXWindowVisible(xdisplay_, xwindow, true);
+  }
+}
+
+void BrowserWindowStdGtk::Show() {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_) {
+    ::Window xwindow = browser_->GetHost()->GetWindowHandle();
+    DCHECK(xwindow);
+    SetXWindowVisible(xdisplay_, xwindow, true);
+  }
+}
+
+void BrowserWindowStdGtk::Hide() {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_) {
+    ::Window xwindow = browser_->GetHost()->GetWindowHandle();
+    DCHECK(xwindow);
+    SetXWindowVisible(xdisplay_, xwindow, false);
+  }
+}
+
+void BrowserWindowStdGtk::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  if (xdisplay_ && browser_) {
+    ::Window xwindow = browser_->GetHost()->GetWindowHandle();
+    DCHECK(xwindow);
+    SetXWindowBounds(xdisplay_, xwindow, x, y, width, height);
+  }
+}
+
+void BrowserWindowStdGtk::SetFocus(bool focus) {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_)
+    browser_->GetHost()->SetFocus(focus);
+}
+
+ClientWindowHandle BrowserWindowStdGtk::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+
+  // There is no GtkWidget* representation of this object.
+  NOTREACHED();
+  return NULL;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/browser_window_std_gtk.h b/src/tests/cefclient/browser/browser_window_std_gtk.h
new file mode 100644
index 0000000..300f37f
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_std_gtk.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_GTK_H_
+#pragma once
+
+#include "tests/cefclient/browser/browser_window.h"
+
+namespace client {
+
+// Represents a native child window hosting a single windowed browser instance.
+// The methods of this class must be called on the main thread unless otherwise
+// indicated.
+class BrowserWindowStdGtk : public BrowserWindow {
+ public:
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object.
+  BrowserWindowStdGtk(Delegate* delegate, const std::string& startup_url);
+
+  // Called from RootWindowGtk::CreateRootWindow before CreateBrowser.
+  void set_xdisplay(XDisplay* xdisplay);
+
+  // BrowserWindow methods.
+  void CreateBrowser(ClientWindowHandle parent_handle,
+                     const CefRect& rect,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context) OVERRIDE;
+  void GetPopupConfig(CefWindowHandle temp_handle,
+                      CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient>& client,
+                      CefBrowserSettings& settings) OVERRIDE;
+  void ShowPopup(ClientWindowHandle parent_handle,
+                 int x,
+                 int y,
+                 size_t width,
+                 size_t height) OVERRIDE;
+  void Show() OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void SetFocus(bool focus) OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+
+ private:
+  XDisplay* xdisplay_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserWindowStdGtk);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_GTK_H_
diff --git a/src/tests/cefclient/browser/browser_window_std_mac.h b/src/tests/cefclient/browser/browser_window_std_mac.h
new file mode 100644
index 0000000..853b616
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_std_mac.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_MAC_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_MAC_H_
+#pragma once
+
+#include "tests/cefclient/browser/browser_window.h"
+
+namespace client {
+
+// Represents a native child window hosting a single windowed browser instance.
+// The methods of this class must be called on the main thread unless otherwise
+// indicated.
+class BrowserWindowStdMac : public BrowserWindow {
+ public:
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object.
+  BrowserWindowStdMac(Delegate* delegate, const std::string& startup_url);
+
+  // BrowserWindow methods.
+  void CreateBrowser(ClientWindowHandle parent_handle,
+                     const CefRect& rect,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context) OVERRIDE;
+  void GetPopupConfig(CefWindowHandle temp_handle,
+                      CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient>& client,
+                      CefBrowserSettings& settings) OVERRIDE;
+  void ShowPopup(ClientWindowHandle parent_handle,
+                 int x,
+                 int y,
+                 size_t width,
+                 size_t height) OVERRIDE;
+  void Show() OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void SetFocus(bool focus) OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrowserWindowStdMac);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_MAC_H_
diff --git a/src/tests/cefclient/browser/browser_window_std_mac.mm b/src/tests/cefclient/browser/browser_window_std_mac.mm
new file mode 100644
index 0000000..c02bb32
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_std_mac.mm
@@ -0,0 +1,94 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/browser_window_std_mac.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "include/base/cef_logging.h"
+#include "tests/cefclient/browser/client_handler_std.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+BrowserWindowStdMac::BrowserWindowStdMac(Delegate* delegate,
+                                         const std::string& startup_url)
+    : BrowserWindow(delegate) {
+  client_handler_ = new ClientHandlerStd(this, startup_url);
+}
+
+void BrowserWindowStdMac::CreateBrowser(
+    ClientWindowHandle parent_handle,
+    const CefRect& rect,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  REQUIRE_MAIN_THREAD();
+
+  CefWindowInfo window_info;
+  window_info.SetAsChild(parent_handle, rect.x, rect.y, rect.width,
+                         rect.height);
+
+  CefBrowserHost::CreateBrowser(window_info, client_handler_,
+                                client_handler_->startup_url(), settings,
+                                extra_info, request_context);
+}
+
+void BrowserWindowStdMac::GetPopupConfig(CefWindowHandle temp_handle,
+                                         CefWindowInfo& windowInfo,
+                                         CefRefPtr<CefClient>& client,
+                                         CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // The window will be properly sized after the browser is created.
+  windowInfo.SetAsChild(temp_handle, 0, 0, 0, 0);
+  client = client_handler_;
+}
+
+void BrowserWindowStdMac::ShowPopup(ClientWindowHandle parent_handle,
+                                    int x,
+                                    int y,
+                                    size_t width,
+                                    size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  NSView* browser_view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(GetWindowHandle());
+
+  // Re-parent |browser_view| to |parent_handle|.
+  [browser_view removeFromSuperview];
+  [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle) addSubview:browser_view];
+
+  NSSize size = NSMakeSize(static_cast<int>(width), static_cast<int>(height));
+  [browser_view setFrameSize:size];
+}
+
+void BrowserWindowStdMac::Show() {
+  REQUIRE_MAIN_THREAD();
+  // Nothing to do here. Chromium internally handles window show/hide.
+}
+
+void BrowserWindowStdMac::Hide() {
+  REQUIRE_MAIN_THREAD();
+  // Nothing to do here. Chromium internally handles window show/hide.
+}
+
+void BrowserWindowStdMac::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+  // Nothing to do here. Cocoa will size the browser for us.
+}
+
+void BrowserWindowStdMac::SetFocus(bool focus) {
+  REQUIRE_MAIN_THREAD();
+  // Nothing to do here. Chromium internally handles window focus assignment.
+}
+
+ClientWindowHandle BrowserWindowStdMac::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_)
+    return browser_->GetHost()->GetWindowHandle();
+  return NULL;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/browser_window_std_win.cc b/src/tests/cefclient/browser/browser_window_std_win.cc
new file mode 100644
index 0000000..f1e53d6
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_std_win.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/browser_window_std_win.h"
+
+#include "tests/cefclient/browser/client_handler_std.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+BrowserWindowStdWin::BrowserWindowStdWin(Delegate* delegate,
+                                         const std::string& startup_url)
+    : BrowserWindow(delegate) {
+  client_handler_ = new ClientHandlerStd(this, startup_url);
+}
+
+void BrowserWindowStdWin::CreateBrowser(
+    ClientWindowHandle parent_handle,
+    const CefRect& rect,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue> extra_info,
+    CefRefPtr<CefRequestContext> request_context) {
+  REQUIRE_MAIN_THREAD();
+
+  CefWindowInfo window_info;
+  RECT wnd_rect = {rect.x, rect.y, rect.x + rect.width, rect.y + rect.height};
+  window_info.SetAsChild(parent_handle, wnd_rect);
+
+  if (GetWindowLongPtr(parent_handle, GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
+    // Don't activate the browser window on creation.
+    window_info.ex_style |= WS_EX_NOACTIVATE;
+  }
+
+  CefBrowserHost::CreateBrowser(window_info, client_handler_,
+                                client_handler_->startup_url(), settings,
+                                extra_info, request_context);
+}
+
+void BrowserWindowStdWin::GetPopupConfig(CefWindowHandle temp_handle,
+                                         CefWindowInfo& windowInfo,
+                                         CefRefPtr<CefClient>& client,
+                                         CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // The window will be properly sized after the browser is created.
+  windowInfo.SetAsChild(temp_handle, RECT());
+
+  // Don't activate the hidden browser window on creation.
+  windowInfo.ex_style |= WS_EX_NOACTIVATE;
+
+  client = client_handler_;
+}
+
+void BrowserWindowStdWin::ShowPopup(ClientWindowHandle parent_handle,
+                                    int x,
+                                    int y,
+                                    size_t width,
+                                    size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  HWND hwnd = GetWindowHandle();
+  if (hwnd) {
+    SetParent(hwnd, parent_handle);
+    SetWindowPos(hwnd, NULL, x, y, static_cast<int>(width),
+                 static_cast<int>(height), SWP_NOZORDER | SWP_NOACTIVATE);
+
+    const bool no_activate =
+        GetWindowLongPtr(parent_handle, GWL_EXSTYLE) & WS_EX_NOACTIVATE;
+    ShowWindow(hwnd, no_activate ? SW_SHOWNOACTIVATE : SW_SHOW);
+  }
+}
+
+void BrowserWindowStdWin::Show() {
+  REQUIRE_MAIN_THREAD();
+
+  HWND hwnd = GetWindowHandle();
+  if (hwnd && !::IsWindowVisible(hwnd))
+    ShowWindow(hwnd, SW_SHOW);
+}
+
+void BrowserWindowStdWin::Hide() {
+  REQUIRE_MAIN_THREAD();
+
+  HWND hwnd = GetWindowHandle();
+  if (hwnd) {
+    // When the frame window is minimized set the browser window size to 0x0 to
+    // reduce resource usage.
+    SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
+                 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+  }
+}
+
+void BrowserWindowStdWin::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  HWND hwnd = GetWindowHandle();
+  if (hwnd) {
+    // Set the browser window bounds.
+    SetWindowPos(hwnd, NULL, x, y, static_cast<int>(width),
+                 static_cast<int>(height), SWP_NOZORDER);
+  }
+}
+
+void BrowserWindowStdWin::SetFocus(bool focus) {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_)
+    browser_->GetHost()->SetFocus(focus);
+}
+
+ClientWindowHandle BrowserWindowStdWin::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_)
+    return browser_->GetHost()->GetWindowHandle();
+  return NULL;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/browser_window_std_win.h b/src/tests/cefclient/browser/browser_window_std_win.h
new file mode 100644
index 0000000..8c8bef2
--- /dev/null
+++ b/src/tests/cefclient/browser/browser_window_std_win.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_WIN_H_
+#pragma once
+
+#include "tests/cefclient/browser/browser_window.h"
+
+namespace client {
+
+// Represents a native child window hosting a single windowed browser instance.
+// The methods of this class must be called on the main thread unless otherwise
+// indicated.
+class BrowserWindowStdWin : public BrowserWindow {
+ public:
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object.
+  BrowserWindowStdWin(Delegate* delegate, const std::string& startup_url);
+
+  // BrowserWindow methods.
+  void CreateBrowser(ClientWindowHandle parent_handle,
+                     const CefRect& rect,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context) OVERRIDE;
+  void GetPopupConfig(CefWindowHandle temp_handle,
+                      CefWindowInfo& windowInfo,
+                      CefRefPtr<CefClient>& client,
+                      CefBrowserSettings& settings) OVERRIDE;
+  void ShowPopup(ClientWindowHandle parent_handle,
+                 int x,
+                 int y,
+                 size_t width,
+                 size_t height) OVERRIDE;
+  void Show() OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void SetFocus(bool focus) OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrowserWindowStdWin);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BROWSER_WINDOW_STD_WIN_H_
diff --git a/src/tests/cefclient/browser/bytes_write_handler.cc b/src/tests/cefclient/browser/bytes_write_handler.cc
new file mode 100644
index 0000000..25f17fa
--- /dev/null
+++ b/src/tests/cefclient/browser/bytes_write_handler.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/bytes_write_handler.h"
+
+#include <cstdio>
+#include <cstdlib>
+
+#include "include/wrapper/cef_helpers.h"
+
+namespace client {
+
+BytesWriteHandler::BytesWriteHandler(size_t grow)
+    : grow_(grow), datasize_(grow), offset_(0) {
+  DCHECK_GT(grow, 0U);
+  data_ = malloc(grow);
+  DCHECK(data_ != nullptr);
+}
+
+BytesWriteHandler::~BytesWriteHandler() {
+  if (data_)
+    free(data_);
+}
+
+size_t BytesWriteHandler::Write(const void* ptr, size_t size, size_t n) {
+  base::AutoLock lock_scope(lock_);
+  size_t rv;
+  if (offset_ + static_cast<int64>(size * n) >= datasize_ &&
+      Grow(size * n) == 0) {
+    rv = 0;
+  } else {
+    memcpy(reinterpret_cast<char*>(data_) + offset_, ptr, size * n);
+    offset_ += size * n;
+    rv = n;
+  }
+
+  return rv;
+}
+
+int BytesWriteHandler::Seek(int64 offset, int whence) {
+  int rv = -1L;
+  base::AutoLock lock_scope(lock_);
+  switch (whence) {
+    case SEEK_CUR:
+      if (offset_ + offset > datasize_ || offset_ + offset < 0)
+        break;
+      offset_ += offset;
+      rv = 0;
+      break;
+    case SEEK_END: {
+      int64 offset_abs = std::abs(offset);
+      if (offset_abs > datasize_)
+        break;
+      offset_ = datasize_ - offset_abs;
+      rv = 0;
+      break;
+    }
+    case SEEK_SET:
+      if (offset > datasize_ || offset < 0)
+        break;
+      offset_ = offset;
+      rv = 0;
+      break;
+  }
+
+  return rv;
+}
+
+int64 BytesWriteHandler::Tell() {
+  base::AutoLock lock_scope(lock_);
+  return offset_;
+}
+
+int BytesWriteHandler::Flush() {
+  return 0;
+}
+
+size_t BytesWriteHandler::Grow(size_t size) {
+  lock_.AssertAcquired();
+  size_t rv;
+  size_t s = (size > grow_ ? size : grow_);
+  void* tmp = realloc(data_, datasize_ + s);
+  DCHECK(tmp != nullptr);
+  if (tmp) {
+    data_ = tmp;
+    datasize_ += s;
+    rv = datasize_;
+  } else {
+    rv = 0;
+  }
+
+  return rv;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/bytes_write_handler.h b/src/tests/cefclient/browser/bytes_write_handler.h
new file mode 100644
index 0000000..26f9c7b
--- /dev/null
+++ b/src/tests/cefclient/browser/bytes_write_handler.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BYTES_WRITE_HANDLER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_BYTES_WRITE_HANDLER_H_
+#pragma once
+
+#include "include/base/cef_lock.h"
+#include "include/cef_stream.h"
+
+namespace client {
+
+class BytesWriteHandler : public CefWriteHandler {
+ public:
+  explicit BytesWriteHandler(size_t grow);
+  ~BytesWriteHandler();
+
+  size_t Write(const void* ptr, size_t size, size_t n) OVERRIDE;
+  int Seek(int64 offset, int whence) OVERRIDE;
+  int64 Tell() OVERRIDE;
+  int Flush() OVERRIDE;
+  bool MayBlock() OVERRIDE { return false; }
+
+  void* GetData() { return data_; }
+  int64 GetDataSize() { return offset_; }
+
+ private:
+  size_t Grow(size_t size);
+
+  size_t grow_;
+  void* data_;
+  int64 datasize_;
+  int64 offset_;
+
+  base::Lock lock_;
+
+  IMPLEMENT_REFCOUNTING(BytesWriteHandler);
+  DISALLOW_COPY_AND_ASSIGN(BytesWriteHandler);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_BYTES_WRITE_HANDLER_H_
diff --git a/src/tests/cefclient/browser/client_app_delegates_browser.cc b/src/tests/cefclient/browser/client_app_delegates_browser.cc
new file mode 100644
index 0000000..4594818
--- /dev/null
+++ b/src/tests/cefclient/browser/client_app_delegates_browser.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/client_app_browser.h"
+
+#include "tests/cefclient/browser/client_browser.h"
+
+#if defined(OS_LINUX)
+#include "tests/cefclient/browser/print_handler_gtk.h"
+#endif
+
+namespace client {
+
+// static
+void ClientAppBrowser::CreateDelegates(DelegateSet& delegates) {
+  browser::CreateDelegates(delegates);
+}
+
+// static
+CefRefPtr<CefPrintHandler> ClientAppBrowser::CreatePrintHandler() {
+#if defined(OS_LINUX)
+  return new ClientPrintHandlerGtk();
+#else
+  return nullptr;
+#endif
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/client_browser.cc b/src/tests/cefclient/browser/client_browser.cc
new file mode 100644
index 0000000..cffb1dc
--- /dev/null
+++ b/src/tests/cefclient/browser/client_browser.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/client_browser.h"
+#include "tests/cefclient/browser/main_context.h"
+
+#include "include/cef_command_line.h"
+#include "include/cef_crash_util.h"
+#include "include/cef_file_util.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+namespace browser {
+
+namespace {
+
+class ClientBrowserDelegate : public ClientAppBrowser::Delegate {
+ public:
+  ClientBrowserDelegate() {}
+
+  void OnContextInitialized(CefRefPtr<ClientAppBrowser> app) OVERRIDE {
+    if (CefCrashReportingEnabled()) {
+      // Set some crash keys for testing purposes. Keys must be defined in the
+      // "crash_reporter.cfg" file. See cef_crash_util.h for details.
+      CefSetCrashKeyValue("testkey_small1", "value1_small_browser");
+      CefSetCrashKeyValue("testkey_small2", "value2_small_browser");
+      CefSetCrashKeyValue("testkey_medium1", "value1_medium_browser");
+      CefSetCrashKeyValue("testkey_medium2", "value2_medium_browser");
+      CefSetCrashKeyValue("testkey_large1", "value1_large_browser");
+      CefSetCrashKeyValue("testkey_large2", "value2_large_browser");
+    }
+
+    const std::string& crl_sets_path =
+        CefCommandLine::GetGlobalCommandLine()->GetSwitchValue(
+            switches::kCRLSetsPath);
+    if (!crl_sets_path.empty()) {
+      // Load the CRLSets file from the specified path.
+      CefLoadCRLSetsFile(crl_sets_path);
+    }
+  }
+
+  void OnBeforeCommandLineProcessing(
+      CefRefPtr<ClientAppBrowser> app,
+      CefRefPtr<CefCommandLine> command_line) OVERRIDE {
+    // Append Chromium command line parameters if touch events are enabled
+    if (client::MainContext::Get()->TouchEventsEnabled())
+      command_line->AppendSwitchWithValue("touch-events", "enabled");
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ClientBrowserDelegate);
+  IMPLEMENT_REFCOUNTING(ClientBrowserDelegate);
+};
+
+}  // namespace
+
+void CreateDelegates(ClientAppBrowser::DelegateSet& delegates) {
+  delegates.insert(new ClientBrowserDelegate);
+}
+
+}  // namespace browser
+}  // namespace client
diff --git a/src/tests/cefclient/browser/client_browser.h b/src/tests/cefclient/browser/client_browser.h
new file mode 100644
index 0000000..ac09e42
--- /dev/null
+++ b/src/tests/cefclient/browser/client_browser.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_BROWSER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_BROWSER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "tests/shared/browser/client_app_browser.h"
+
+namespace client {
+namespace browser {
+
+// Create the browser delegate. Called from client_app_delegates_browser.cc.
+void CreateDelegates(ClientAppBrowser::DelegateSet& delegates);
+
+}  // namespace browser
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_BROWSER_H_
diff --git a/src/tests/cefclient/browser/client_handler.cc b/src/tests/cefclient/browser/client_handler.cc
new file mode 100644
index 0000000..dc2bb5f
--- /dev/null
+++ b/src/tests/cefclient/browser/client_handler.cc
@@ -0,0 +1,1208 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/client_handler.h"
+
+#include <stdio.h>
+#include <algorithm>
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_browser.h"
+#include "include/cef_frame.h"
+#include "include/cef_parser.h"
+#include "include/cef_ssl_status.h"
+#include "include/cef_x509_certificate.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/root_window_manager.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/browser/extension_util.h"
+#include "tests/shared/browser/resource_util.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+
+#if defined(OS_WIN)
+#define NEWLINE "\r\n"
+#else
+#define NEWLINE "\n"
+#endif
+
+namespace {
+
+// Custom menu command Ids.
+enum client_menu_ids {
+  CLIENT_ID_SHOW_DEVTOOLS = MENU_ID_USER_FIRST,
+  CLIENT_ID_CLOSE_DEVTOOLS,
+  CLIENT_ID_INSPECT_ELEMENT,
+  CLIENT_ID_SHOW_SSL_INFO,
+  CLIENT_ID_OFFLINE,
+  CLIENT_ID_TESTMENU_SUBMENU,
+  CLIENT_ID_TESTMENU_CHECKITEM,
+  CLIENT_ID_TESTMENU_RADIOITEM1,
+  CLIENT_ID_TESTMENU_RADIOITEM2,
+  CLIENT_ID_TESTMENU_RADIOITEM3,
+};
+
+// Musr match the value in client_renderer.cc.
+const char kFocusedNodeChangedMessage[] = "ClientRenderer.FocusedNodeChanged";
+
+std::string GetTimeString(const CefTime& value) {
+  if (value.GetTimeT() == 0)
+    return "Unspecified";
+
+  static const char* kMonths[] = {
+      "January", "February", "March",     "April",   "May",      "June",
+      "July",    "August",   "September", "October", "November", "December"};
+  std::string month;
+  if (value.month >= 1 && value.month <= 12)
+    month = kMonths[value.month - 1];
+  else
+    month = "Invalid";
+
+  std::stringstream ss;
+  ss << month << " " << value.day_of_month << ", " << value.year << " "
+     << std::setfill('0') << std::setw(2) << value.hour << ":"
+     << std::setfill('0') << std::setw(2) << value.minute << ":"
+     << std::setfill('0') << std::setw(2) << value.second;
+  return ss.str();
+}
+
+std::string GetBinaryString(CefRefPtr<CefBinaryValue> value) {
+  if (!value.get())
+    return "&nbsp;";
+
+  // Retrieve the value.
+  const size_t size = value->GetSize();
+  std::string src;
+  src.resize(size);
+  value->GetData(const_cast<char*>(src.data()), size, 0);
+
+  // Encode the value.
+  return CefBase64Encode(src.data(), src.size());
+}
+
+#define FLAG(flag)                          \
+  if (status & flag) {                      \
+    result += std::string(#flag) + "<br/>"; \
+  }
+
+#define VALUE(val, def)       \
+  if (val == def) {           \
+    return std::string(#def); \
+  }
+
+std::string GetCertStatusString(cef_cert_status_t status) {
+  std::string result;
+
+  FLAG(CERT_STATUS_COMMON_NAME_INVALID);
+  FLAG(CERT_STATUS_DATE_INVALID);
+  FLAG(CERT_STATUS_AUTHORITY_INVALID);
+  FLAG(CERT_STATUS_NO_REVOCATION_MECHANISM);
+  FLAG(CERT_STATUS_UNABLE_TO_CHECK_REVOCATION);
+  FLAG(CERT_STATUS_REVOKED);
+  FLAG(CERT_STATUS_INVALID);
+  FLAG(CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
+  FLAG(CERT_STATUS_NON_UNIQUE_NAME);
+  FLAG(CERT_STATUS_WEAK_KEY);
+  FLAG(CERT_STATUS_PINNED_KEY_MISSING);
+  FLAG(CERT_STATUS_NAME_CONSTRAINT_VIOLATION);
+  FLAG(CERT_STATUS_VALIDITY_TOO_LONG);
+  FLAG(CERT_STATUS_IS_EV);
+  FLAG(CERT_STATUS_REV_CHECKING_ENABLED);
+  FLAG(CERT_STATUS_SHA1_SIGNATURE_PRESENT);
+  FLAG(CERT_STATUS_CT_COMPLIANCE_FAILED);
+
+  if (result.empty())
+    return "&nbsp;";
+  return result;
+}
+
+std::string GetSSLVersionString(cef_ssl_version_t version) {
+  VALUE(version, SSL_CONNECTION_VERSION_UNKNOWN);
+  VALUE(version, SSL_CONNECTION_VERSION_SSL2);
+  VALUE(version, SSL_CONNECTION_VERSION_SSL3);
+  VALUE(version, SSL_CONNECTION_VERSION_TLS1);
+  VALUE(version, SSL_CONNECTION_VERSION_TLS1_1);
+  VALUE(version, SSL_CONNECTION_VERSION_TLS1_2);
+  VALUE(version, SSL_CONNECTION_VERSION_TLS1_3);
+  VALUE(version, SSL_CONNECTION_VERSION_QUIC);
+  return std::string();
+}
+
+std::string GetContentStatusString(cef_ssl_content_status_t status) {
+  std::string result;
+
+  VALUE(status, SSL_CONTENT_NORMAL_CONTENT);
+  FLAG(SSL_CONTENT_DISPLAYED_INSECURE_CONTENT);
+  FLAG(SSL_CONTENT_RAN_INSECURE_CONTENT);
+
+  if (result.empty())
+    return "&nbsp;";
+  return result;
+}
+
+// Load a data: URI containing the error message.
+void LoadErrorPage(CefRefPtr<CefFrame> frame,
+                   const std::string& failed_url,
+                   cef_errorcode_t error_code,
+                   const std::string& other_info) {
+  std::stringstream ss;
+  ss << "<html><head><title>Page failed to load</title></head>"
+        "<body bgcolor=\"white\">"
+        "<h3>Page failed to load.</h3>"
+        "URL: <a href=\""
+     << failed_url << "\">" << failed_url
+     << "</a><br/>Error: " << test_runner::GetErrorString(error_code) << " ("
+     << error_code << ")";
+
+  if (!other_info.empty())
+    ss << "<br/>" << other_info;
+
+  ss << "</body></html>";
+  frame->LoadURL(test_runner::GetDataURI(ss.str(), "text/html"));
+}
+
+// Return HTML string with information about a certificate.
+std::string GetCertificateInformation(CefRefPtr<CefX509Certificate> cert,
+                                      cef_cert_status_t certstatus) {
+  CefRefPtr<CefX509CertPrincipal> subject = cert->GetSubject();
+  CefRefPtr<CefX509CertPrincipal> issuer = cert->GetIssuer();
+
+  // Build a table showing certificate information. Various types of invalid
+  // certificates can be tested using https://badssl.com/.
+  std::stringstream ss;
+  ss << "<h3>X.509 Certificate Information:</h3>"
+        "<table border=1><tr><th>Field</th><th>Value</th></tr>";
+
+  if (certstatus != CERT_STATUS_NONE) {
+    ss << "<tr><td>Status</td><td>" << GetCertStatusString(certstatus)
+       << "</td></tr>";
+  }
+
+  ss << "<tr><td>Subject</td><td>"
+     << (subject.get() ? subject->GetDisplayName().ToString() : "&nbsp;")
+     << "</td></tr>"
+        "<tr><td>Issuer</td><td>"
+     << (issuer.get() ? issuer->GetDisplayName().ToString() : "&nbsp;")
+     << "</td></tr>"
+        "<tr><td>Serial #*</td><td>"
+     << GetBinaryString(cert->GetSerialNumber()) << "</td></tr>"
+     << "<tr><td>Valid Start</td><td>" << GetTimeString(cert->GetValidStart())
+     << "</td></tr>"
+        "<tr><td>Valid Expiry</td><td>"
+     << GetTimeString(cert->GetValidExpiry()) << "</td></tr>";
+
+  CefX509Certificate::IssuerChainBinaryList der_chain_list;
+  CefX509Certificate::IssuerChainBinaryList pem_chain_list;
+  cert->GetDEREncodedIssuerChain(der_chain_list);
+  cert->GetPEMEncodedIssuerChain(pem_chain_list);
+  DCHECK_EQ(der_chain_list.size(), pem_chain_list.size());
+
+  der_chain_list.insert(der_chain_list.begin(), cert->GetDEREncoded());
+  pem_chain_list.insert(pem_chain_list.begin(), cert->GetPEMEncoded());
+
+  for (size_t i = 0U; i < der_chain_list.size(); ++i) {
+    ss << "<tr><td>DER Encoded*</td>"
+          "<td style=\"max-width:800px;overflow:scroll;\">"
+       << GetBinaryString(der_chain_list[i])
+       << "</td></tr>"
+          "<tr><td>PEM Encoded*</td>"
+          "<td style=\"max-width:800px;overflow:scroll;\">"
+       << GetBinaryString(pem_chain_list[i]) << "</td></tr>";
+  }
+
+  ss << "</table> * Displayed value is base64 encoded.";
+  return ss.str();
+}
+
+}  // namespace
+
+class ClientDownloadImageCallback : public CefDownloadImageCallback {
+ public:
+  explicit ClientDownloadImageCallback(CefRefPtr<ClientHandler> client_handler)
+      : client_handler_(client_handler) {}
+
+  void OnDownloadImageFinished(const CefString& image_url,
+                               int http_status_code,
+                               CefRefPtr<CefImage> image) OVERRIDE {
+    if (image)
+      client_handler_->NotifyFavicon(image);
+  }
+
+ private:
+  CefRefPtr<ClientHandler> client_handler_;
+
+  IMPLEMENT_REFCOUNTING(ClientDownloadImageCallback);
+  DISALLOW_COPY_AND_ASSIGN(ClientDownloadImageCallback);
+};
+
+ClientHandler::ClientHandler(Delegate* delegate,
+                             bool is_osr,
+                             const std::string& startup_url)
+    : is_osr_(is_osr),
+      startup_url_(startup_url),
+      download_favicon_images_(false),
+      delegate_(delegate),
+      browser_count_(0),
+      console_log_file_(MainContext::Get()->GetConsoleLogPath()),
+      first_console_message_(true),
+      focus_on_editable_field_(false),
+      initial_navigation_(true) {
+  DCHECK(!console_log_file_.empty());
+
+#if defined(OS_LINUX)
+  // Provide the GTK-based dialog implementation on Linux.
+  dialog_handler_ = new ClientDialogHandlerGtk();
+#endif
+
+  resource_manager_ = new CefResourceManager();
+  test_runner::SetupResourceManager(resource_manager_, &string_resource_map_);
+
+  // Read command line settings.
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+  mouse_cursor_change_disabled_ =
+      command_line->HasSwitch(switches::kMouseCursorChangeDisabled);
+  offline_ = command_line->HasSwitch(switches::kOffline);
+}
+
+void ClientHandler::DetachDelegate() {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&ClientHandler::DetachDelegate, this));
+    return;
+  }
+
+  DCHECK(delegate_);
+  delegate_ = nullptr;
+}
+
+bool ClientHandler::OnProcessMessageReceived(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefProcessId source_process,
+    CefRefPtr<CefProcessMessage> message) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (message_router_->OnProcessMessageReceived(browser, frame, source_process,
+                                                message)) {
+    return true;
+  }
+
+  // Check for messages from the client renderer.
+  std::string message_name = message->GetName();
+  if (message_name == kFocusedNodeChangedMessage) {
+    // A message is sent from ClientRenderDelegate to tell us whether the
+    // currently focused DOM node is editable. Use of |focus_on_editable_field_|
+    // is redundant with CefKeyEvent.focus_on_editable_field in OnPreKeyEvent
+    // but is useful for demonstration purposes.
+    focus_on_editable_field_ = message->GetArgumentList()->GetBool(0);
+    return true;
+  }
+
+  return false;
+}
+
+void ClientHandler::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
+                                        CefRefPtr<CefFrame> frame,
+                                        CefRefPtr<CefContextMenuParams> params,
+                                        CefRefPtr<CefMenuModel> model) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if ((params->GetTypeFlags() & (CM_TYPEFLAG_PAGE | CM_TYPEFLAG_FRAME)) != 0) {
+    // Add a separator if the menu already has items.
+    if (model->GetCount() > 0)
+      model->AddSeparator();
+
+    // Add DevTools items to all context menus.
+    model->AddItem(CLIENT_ID_SHOW_DEVTOOLS, "&Show DevTools");
+    model->AddItem(CLIENT_ID_CLOSE_DEVTOOLS, "Close DevTools");
+    model->AddSeparator();
+    model->AddItem(CLIENT_ID_INSPECT_ELEMENT, "Inspect Element");
+
+    if (HasSSLInformation(browser)) {
+      model->AddSeparator();
+      model->AddItem(CLIENT_ID_SHOW_SSL_INFO, "Show SSL information");
+    }
+
+    model->AddSeparator();
+    model->AddItem(CLIENT_ID_OFFLINE, "Offline mode");
+    if (offline_)
+      model->SetChecked(CLIENT_ID_OFFLINE, true);
+
+    // Test context menu features.
+    BuildTestMenu(model);
+  }
+
+  if (delegate_)
+    delegate_->OnBeforeContextMenu(model);
+}
+
+bool ClientHandler::OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
+                                         CefRefPtr<CefFrame> frame,
+                                         CefRefPtr<CefContextMenuParams> params,
+                                         int command_id,
+                                         EventFlags event_flags) {
+  CEF_REQUIRE_UI_THREAD();
+
+  switch (command_id) {
+    case CLIENT_ID_SHOW_DEVTOOLS:
+      ShowDevTools(browser, CefPoint());
+      return true;
+    case CLIENT_ID_CLOSE_DEVTOOLS:
+      CloseDevTools(browser);
+      return true;
+    case CLIENT_ID_INSPECT_ELEMENT:
+      ShowDevTools(browser, CefPoint(params->GetXCoord(), params->GetYCoord()));
+      return true;
+    case CLIENT_ID_SHOW_SSL_INFO:
+      ShowSSLInformation(browser);
+      return true;
+    case CLIENT_ID_OFFLINE:
+      offline_ = !offline_;
+      SetOfflineState(browser, offline_);
+      return true;
+    default:  // Allow default handling, if any.
+      return ExecuteTestMenu(command_id);
+  }
+}
+
+void ClientHandler::OnAddressChange(CefRefPtr<CefBrowser> browser,
+                                    CefRefPtr<CefFrame> frame,
+                                    const CefString& url) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Only update the address for the main (top-level) frame.
+  if (frame->IsMain())
+    NotifyAddress(url);
+}
+
+void ClientHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
+                                  const CefString& title) {
+  CEF_REQUIRE_UI_THREAD();
+
+  NotifyTitle(title);
+}
+
+void ClientHandler::OnFaviconURLChange(
+    CefRefPtr<CefBrowser> browser,
+    const std::vector<CefString>& icon_urls) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!icon_urls.empty() && download_favicon_images_) {
+    browser->GetHost()->DownloadImage(icon_urls[0], true, 16, false,
+                                      new ClientDownloadImageCallback(this));
+  }
+}
+
+void ClientHandler::OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
+                                           bool fullscreen) {
+  CEF_REQUIRE_UI_THREAD();
+
+  NotifyFullscreen(fullscreen);
+}
+
+bool ClientHandler::OnConsoleMessage(CefRefPtr<CefBrowser> browser,
+                                     cef_log_severity_t level,
+                                     const CefString& message,
+                                     const CefString& source,
+                                     int line) {
+  CEF_REQUIRE_UI_THREAD();
+
+  FILE* file = fopen(console_log_file_.c_str(), "a");
+  if (file) {
+    std::stringstream ss;
+    ss << "Level: ";
+    switch (level) {
+      case LOGSEVERITY_DEBUG:
+        ss << "Debug" << NEWLINE;
+        break;
+      case LOGSEVERITY_INFO:
+        ss << "Info" << NEWLINE;
+        break;
+      case LOGSEVERITY_WARNING:
+        ss << "Warn" << NEWLINE;
+        break;
+      case LOGSEVERITY_ERROR:
+        ss << "Error" << NEWLINE;
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+    ss << "Message: " << message.ToString() << NEWLINE
+       << "Source: " << source.ToString() << NEWLINE << "Line: " << line
+       << NEWLINE << "-----------------------" << NEWLINE;
+    fputs(ss.str().c_str(), file);
+    fclose(file);
+
+    if (first_console_message_) {
+      test_runner::Alert(
+          browser, "Console messages written to \"" + console_log_file_ + "\"");
+      first_console_message_ = false;
+    }
+  }
+
+  return false;
+}
+
+bool ClientHandler::OnAutoResize(CefRefPtr<CefBrowser> browser,
+                                 const CefSize& new_size) {
+  CEF_REQUIRE_UI_THREAD();
+
+  NotifyAutoResize(new_size);
+  return true;
+}
+
+void ClientHandler::OnBeforeDownload(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDownloadItem> download_item,
+    const CefString& suggested_name,
+    CefRefPtr<CefBeforeDownloadCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Continue the download and show the "Save As" dialog.
+  callback->Continue(MainContext::Get()->GetDownloadPath(suggested_name), true);
+}
+
+void ClientHandler::OnDownloadUpdated(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDownloadItem> download_item,
+    CefRefPtr<CefDownloadItemCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (download_item->IsComplete()) {
+    test_runner::Alert(browser, "File \"" +
+                                    download_item->GetFullPath().ToString() +
+                                    "\" downloaded successfully.");
+  }
+}
+
+bool ClientHandler::OnDragEnter(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefDragData> dragData,
+                                CefDragHandler::DragOperationsMask mask) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Forbid dragging of URLs and files.
+  if ((mask & DRAG_OPERATION_LINK) && !dragData->IsFragment()) {
+    test_runner::Alert(browser, "cefclient blocks dragging of URLs and files");
+    return true;
+  }
+
+  return false;
+}
+
+void ClientHandler::OnDraggableRegionsChanged(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    const std::vector<CefDraggableRegion>& regions) {
+  CEF_REQUIRE_UI_THREAD();
+
+  NotifyDraggableRegions(regions);
+}
+
+void ClientHandler::OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) {
+  CEF_REQUIRE_UI_THREAD();
+
+  NotifyTakeFocus(next);
+}
+
+bool ClientHandler::OnSetFocus(CefRefPtr<CefBrowser> browser,
+                               FocusSource source) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (initial_navigation_) {
+    CefRefPtr<CefCommandLine> command_line =
+        CefCommandLine::GetGlobalCommandLine();
+    if (command_line->HasSwitch(switches::kNoActivate)) {
+      // Don't give focus to the browser on creation.
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool ClientHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
+                                  const CefKeyEvent& event,
+                                  CefEventHandle os_event,
+                                  bool* is_keyboard_shortcut) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!event.focus_on_editable_field && event.windows_key_code == 0x20) {
+    // Special handling for the space character when an input element does not
+    // have focus. Handling the event in OnPreKeyEvent() keeps the event from
+    // being processed in the renderer. If we instead handled the event in the
+    // OnKeyEvent() method the space key would cause the window to scroll in
+    // addition to showing the alert box.
+    if (event.type == KEYEVENT_RAWKEYDOWN)
+      test_runner::Alert(browser, "You pressed the space bar!");
+    return true;
+  }
+
+  return false;
+}
+
+bool ClientHandler::OnBeforePopup(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    const CefString& target_url,
+    const CefString& target_frame_name,
+    CefLifeSpanHandler::WindowOpenDisposition target_disposition,
+    bool user_gesture,
+    const CefPopupFeatures& popupFeatures,
+    CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient>& client,
+    CefBrowserSettings& settings,
+    CefRefPtr<CefDictionaryValue>& extra_info,
+    bool* no_javascript_access) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Return true to cancel the popup window.
+  return !CreatePopupWindow(browser, false, popupFeatures, windowInfo, client,
+                            settings);
+}
+
+void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  browser_count_++;
+
+  if (!message_router_) {
+    // Create the browser-side router for query handling.
+    CefMessageRouterConfig config;
+    message_router_ = CefMessageRouterBrowserSide::Create(config);
+
+    // Register handlers with the router.
+    test_runner::CreateMessageHandlers(message_handler_set_);
+    MessageHandlerSet::const_iterator it = message_handler_set_.begin();
+    for (; it != message_handler_set_.end(); ++it)
+      message_router_->AddHandler(*(it), false);
+  }
+
+  // Disable mouse cursor change if requested via the command-line flag.
+  if (mouse_cursor_change_disabled_)
+    browser->GetHost()->SetMouseCursorChangeDisabled(true);
+
+  // Set offline mode if requested via the command-line flag.
+  if (offline_)
+    SetOfflineState(browser, true);
+
+  if (browser->GetHost()->GetExtension()) {
+    // Browsers hosting extension apps should auto-resize.
+    browser->GetHost()->SetAutoResizeEnabled(true, CefSize(20, 20),
+                                             CefSize(1000, 1000));
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    if (extension_util::IsInternalExtension(extension->GetPath())) {
+      // Register the internal handler for extension resources.
+      extension_util::AddInternalExtensionToResourceManager(extension,
+                                                            resource_manager_);
+    }
+  }
+
+  NotifyBrowserCreated(browser);
+}
+
+bool ClientHandler::DoClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  NotifyBrowserClosing(browser);
+
+  // Allow the close. For windowed browsers this will result in the OS close
+  // event being sent.
+  return false;
+}
+
+void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (--browser_count_ == 0) {
+    // Remove and delete message router handlers.
+    MessageHandlerSet::const_iterator it = message_handler_set_.begin();
+    for (; it != message_handler_set_.end(); ++it) {
+      message_router_->RemoveHandler(*(it));
+      delete *(it);
+    }
+    message_handler_set_.clear();
+    message_router_ = nullptr;
+  }
+
+  NotifyBrowserClosed(browser);
+}
+
+void ClientHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                                         bool isLoading,
+                                         bool canGoBack,
+                                         bool canGoForward) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!isLoading && initial_navigation_) {
+    initial_navigation_ = false;
+  }
+
+  NotifyLoadingState(isLoading, canGoBack, canGoForward);
+}
+
+void ClientHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                ErrorCode errorCode,
+                                const CefString& errorText,
+                                const CefString& failedUrl) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Don't display an error for downloaded files.
+  if (errorCode == ERR_ABORTED)
+    return;
+
+  // Don't display an error for external protocols that we allow the OS to
+  // handle. See OnProtocolExecution().
+  if (errorCode == ERR_UNKNOWN_URL_SCHEME) {
+    std::string urlStr = frame->GetURL();
+    if (urlStr.find("spotify:") == 0)
+      return;
+  }
+
+  // Load the error page.
+  LoadErrorPage(frame, failedUrl, errorCode, errorText);
+}
+
+bool ClientHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                                   CefRefPtr<CefFrame> frame,
+                                   CefRefPtr<CefRequest> request,
+                                   bool user_gesture,
+                                   bool is_redirect) {
+  CEF_REQUIRE_UI_THREAD();
+
+  message_router_->OnBeforeBrowse(browser, frame);
+  return false;
+}
+
+bool ClientHandler::OnOpenURLFromTab(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    const CefString& target_url,
+    CefRequestHandler::WindowOpenDisposition target_disposition,
+    bool user_gesture) {
+  if (target_disposition == WOD_NEW_BACKGROUND_TAB ||
+      target_disposition == WOD_NEW_FOREGROUND_TAB) {
+    // Handle middle-click and ctrl + left-click by opening the URL in a new
+    // browser window.
+    RootWindowConfig config;
+    config.with_controls = true;
+    config.with_osr = is_osr();
+    config.url = target_url;
+    MainContext::Get()->GetRootWindowManager()->CreateRootWindow(config);
+    return true;
+  }
+
+  // Open the URL in the current browser window.
+  return false;
+}
+
+CefRefPtr<CefResourceRequestHandler> ClientHandler::GetResourceRequestHandler(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    bool is_navigation,
+    bool is_download,
+    const CefString& request_initiator,
+    bool& disable_default_handling) {
+  CEF_REQUIRE_IO_THREAD();
+  return this;
+}
+
+bool ClientHandler::GetAuthCredentials(CefRefPtr<CefBrowser> browser,
+                                       const CefString& origin_url,
+                                       bool isProxy,
+                                       const CefString& host,
+                                       int port,
+                                       const CefString& realm,
+                                       const CefString& scheme,
+                                       CefRefPtr<CefAuthCallback> callback) {
+  CEF_REQUIRE_IO_THREAD();
+
+  // Used for testing authentication with a proxy server.
+  // For example, CCProxy on Windows.
+  if (isProxy) {
+    callback->Continue("guest", "guest");
+    return true;
+  }
+
+  // Used for testing authentication with https://jigsaw.w3.org/HTTP/.
+  if (host == "jigsaw.w3.org") {
+    callback->Continue("guest", "guest");
+    return true;
+  }
+
+  return false;
+}
+
+bool ClientHandler::OnQuotaRequest(CefRefPtr<CefBrowser> browser,
+                                   const CefString& origin_url,
+                                   int64 new_size,
+                                   CefRefPtr<CefRequestCallback> callback) {
+  CEF_REQUIRE_IO_THREAD();
+
+  static const int64 max_size = 1024 * 1024 * 20;  // 20mb.
+
+  // Grant the quota request if the size is reasonable.
+  callback->Continue(new_size <= max_size);
+  return true;
+}
+
+bool ClientHandler::OnCertificateError(CefRefPtr<CefBrowser> browser,
+                                       ErrorCode cert_error,
+                                       const CefString& request_url,
+                                       CefRefPtr<CefSSLInfo> ssl_info,
+                                       CefRefPtr<CefRequestCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (cert_error == ERR_CERT_AUTHORITY_INVALID &&
+      request_url.ToString().find("https://www.magpcss.org/") == 0U) {
+    // Allow the CEF Forum to load. It has a self-signed certificate.
+    callback->Continue(true);
+    return true;
+  }
+
+  CefRefPtr<CefX509Certificate> cert = ssl_info->GetX509Certificate();
+  if (cert.get()) {
+    // Load the error page.
+    LoadErrorPage(browser->GetMainFrame(), request_url, cert_error,
+                  GetCertificateInformation(cert, ssl_info->GetCertStatus()));
+  }
+
+  return false;  // Cancel the request.
+}
+
+bool ClientHandler::OnSelectClientCertificate(
+    CefRefPtr<CefBrowser> browser,
+    bool isProxy,
+    const CefString& host,
+    int port,
+    const X509CertificateList& certificates,
+    CefRefPtr<CefSelectClientCertificateCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+  if (!command_line->HasSwitch(switches::kSslClientCertificate)) {
+    return false;
+  }
+
+  const std::string& cert_name =
+      command_line->GetSwitchValue(switches::kSslClientCertificate);
+
+  if (cert_name.empty()) {
+    callback->Select(nullptr);
+    return true;
+  }
+
+  std::vector<CefRefPtr<CefX509Certificate>>::const_iterator it =
+      certificates.begin();
+  for (; it != certificates.end(); ++it) {
+    CefString subject((*it)->GetSubject()->GetDisplayName());
+    if (subject == cert_name) {
+      callback->Select(*it);
+      return true;
+    }
+  }
+
+  return true;
+}
+
+void ClientHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                              TerminationStatus status) {
+  CEF_REQUIRE_UI_THREAD();
+
+  message_router_->OnRenderProcessTerminated(browser);
+
+  // Don't reload if there's no start URL, or if the crash URL was specified.
+  if (startup_url_.empty() || startup_url_ == "chrome://crash")
+    return;
+
+  CefRefPtr<CefFrame> frame = browser->GetMainFrame();
+  std::string url = frame->GetURL();
+
+  // Don't reload if the termination occurred before any URL had successfully
+  // loaded.
+  if (url.empty())
+    return;
+
+  std::string start_url = startup_url_;
+
+  // Convert URLs to lowercase for easier comparison.
+  std::transform(url.begin(), url.end(), url.begin(), tolower);
+  std::transform(start_url.begin(), start_url.end(), start_url.begin(),
+                 tolower);
+
+  // Don't reload the URL that just resulted in termination.
+  if (url.find(start_url) == 0)
+    return;
+
+  frame->LoadURL(startup_url_);
+}
+
+void ClientHandler::OnDocumentAvailableInMainFrame(
+    CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Restore offline mode after main frame navigation. Otherwise, offline state
+  // (e.g. `navigator.onLine`) might be wrong in the renderer process.
+  if (offline_)
+    SetOfflineState(browser, true);
+}
+
+cef_return_value_t ClientHandler::OnBeforeResourceLoad(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefRequestCallback> callback) {
+  CEF_REQUIRE_IO_THREAD();
+
+  return resource_manager_->OnBeforeResourceLoad(browser, frame, request,
+                                                 callback);
+}
+
+CefRefPtr<CefResourceHandler> ClientHandler::GetResourceHandler(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request) {
+  CEF_REQUIRE_IO_THREAD();
+
+  return resource_manager_->GetResourceHandler(browser, frame, request);
+}
+
+CefRefPtr<CefResponseFilter> ClientHandler::GetResourceResponseFilter(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response) {
+  CEF_REQUIRE_IO_THREAD();
+
+  return test_runner::GetResourceResponseFilter(browser, frame, request,
+                                                response);
+}
+
+void ClientHandler::OnProtocolExecution(CefRefPtr<CefBrowser> browser,
+                                        CefRefPtr<CefFrame> frame,
+                                        CefRefPtr<CefRequest> request,
+                                        bool& allow_os_execution) {
+  CEF_REQUIRE_IO_THREAD();
+
+  std::string urlStr = request->GetURL();
+
+  // Allow OS execution of Spotify URIs.
+  if (urlStr.find("spotify:") == 0)
+    allow_os_execution = true;
+}
+
+int ClientHandler::GetBrowserCount() const {
+  CEF_REQUIRE_UI_THREAD();
+  return browser_count_;
+}
+
+void ClientHandler::ShowDevTools(CefRefPtr<CefBrowser> browser,
+                                 const CefPoint& inspect_element_at) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&ClientHandler::ShowDevTools, this, browser,
+                                   inspect_element_at));
+    return;
+  }
+
+  CefWindowInfo windowInfo;
+  CefRefPtr<CefClient> client;
+  CefBrowserSettings settings;
+
+  MainContext::Get()->PopulateBrowserSettings(&settings);
+
+  CefRefPtr<CefBrowserHost> host = browser->GetHost();
+
+  // Test if the DevTools browser already exists.
+  bool has_devtools = host->HasDevTools();
+  if (!has_devtools) {
+    // Create a new RootWindow for the DevTools browser that will be created
+    // by ShowDevTools().
+    has_devtools = CreatePopupWindow(browser, true, CefPopupFeatures(),
+                                     windowInfo, client, settings);
+  }
+
+  if (has_devtools) {
+    // Create the DevTools browser if it doesn't already exist.
+    // Otherwise, focus the existing DevTools browser and inspect the element
+    // at |inspect_element_at| if non-empty.
+    host->ShowDevTools(windowInfo, client, settings, inspect_element_at);
+  }
+}
+
+void ClientHandler::CloseDevTools(CefRefPtr<CefBrowser> browser) {
+  browser->GetHost()->CloseDevTools();
+}
+
+bool ClientHandler::HasSSLInformation(CefRefPtr<CefBrowser> browser) {
+  CefRefPtr<CefNavigationEntry> nav =
+      browser->GetHost()->GetVisibleNavigationEntry();
+
+  return (nav && nav->GetSSLStatus() &&
+          nav->GetSSLStatus()->IsSecureConnection());
+}
+
+void ClientHandler::ShowSSLInformation(CefRefPtr<CefBrowser> browser) {
+  std::stringstream ss;
+  CefRefPtr<CefNavigationEntry> nav =
+      browser->GetHost()->GetVisibleNavigationEntry();
+  if (!nav)
+    return;
+
+  CefRefPtr<CefSSLStatus> ssl = nav->GetSSLStatus();
+  if (!ssl)
+    return;
+
+  ss << "<html><head><title>SSL Information</title></head>"
+        "<body bgcolor=\"white\">"
+        "<h3>SSL Connection</h3>"
+     << "<table border=1><tr><th>Field</th><th>Value</th></tr>";
+
+  CefURLParts urlparts;
+  if (CefParseURL(nav->GetURL(), urlparts)) {
+    CefString port(&urlparts.port);
+    ss << "<tr><td>Server</td><td>" << CefString(&urlparts.host).ToString();
+    if (!port.empty())
+      ss << ":" << port.ToString();
+    ss << "</td></tr>";
+  }
+
+  ss << "<tr><td>SSL Version</td><td>"
+     << GetSSLVersionString(ssl->GetSSLVersion()) << "</td></tr>";
+  ss << "<tr><td>Content Status</td><td>"
+     << GetContentStatusString(ssl->GetContentStatus()) << "</td></tr>";
+
+  ss << "</table>";
+
+  CefRefPtr<CefX509Certificate> cert = ssl->GetX509Certificate();
+  if (cert.get())
+    ss << GetCertificateInformation(cert, ssl->GetCertStatus());
+
+  ss << "</body></html>";
+
+  RootWindowConfig config;
+  config.with_controls = false;
+  config.with_osr = is_osr();
+  config.url = test_runner::GetDataURI(ss.str(), "text/html");
+  MainContext::Get()->GetRootWindowManager()->CreateRootWindow(config);
+}
+
+void ClientHandler::SetStringResource(const std::string& page,
+                                      const std::string& data) {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO, base::Bind(&ClientHandler::SetStringResource, this,
+                                   page, data));
+    return;
+  }
+
+  string_resource_map_[page] = data;
+}
+
+bool ClientHandler::CreatePopupWindow(CefRefPtr<CefBrowser> browser,
+                                      bool is_devtools,
+                                      const CefPopupFeatures& popupFeatures,
+                                      CefWindowInfo& windowInfo,
+                                      CefRefPtr<CefClient>& client,
+                                      CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // The popup browser will be parented to a new native window.
+  // Don't show URL bar and navigation buttons on DevTools windows.
+  MainContext::Get()->GetRootWindowManager()->CreateRootWindowAsPopup(
+      !is_devtools, is_osr(), popupFeatures, windowInfo, client, settings);
+
+  return true;
+}
+
+void ClientHandler::NotifyBrowserCreated(CefRefPtr<CefBrowser> browser) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&ClientHandler::NotifyBrowserCreated, this, browser));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnBrowserCreated(browser);
+}
+
+void ClientHandler::NotifyBrowserClosing(CefRefPtr<CefBrowser> browser) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&ClientHandler::NotifyBrowserClosing, this, browser));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnBrowserClosing(browser);
+}
+
+void ClientHandler::NotifyBrowserClosed(CefRefPtr<CefBrowser> browser) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&ClientHandler::NotifyBrowserClosed, this, browser));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnBrowserClosed(browser);
+}
+
+void ClientHandler::NotifyAddress(const CefString& url) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&ClientHandler::NotifyAddress, this, url));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnSetAddress(url);
+}
+
+void ClientHandler::NotifyTitle(const CefString& title) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&ClientHandler::NotifyTitle, this, title));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnSetTitle(title);
+}
+
+void ClientHandler::NotifyFavicon(CefRefPtr<CefImage> image) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&ClientHandler::NotifyFavicon, this, image));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnSetFavicon(image);
+}
+
+void ClientHandler::NotifyFullscreen(bool fullscreen) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&ClientHandler::NotifyFullscreen, this, fullscreen));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnSetFullscreen(fullscreen);
+}
+
+void ClientHandler::NotifyAutoResize(const CefSize& new_size) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&ClientHandler::NotifyAutoResize, this, new_size));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnAutoResize(new_size);
+}
+
+void ClientHandler::NotifyLoadingState(bool isLoading,
+                                       bool canGoBack,
+                                       bool canGoForward) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&ClientHandler::NotifyLoadingState, this,
+                                 isLoading, canGoBack, canGoForward));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnSetLoadingState(isLoading, canGoBack, canGoForward);
+}
+
+void ClientHandler::NotifyDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&ClientHandler::NotifyDraggableRegions, this, regions));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnSetDraggableRegions(regions);
+}
+
+void ClientHandler::NotifyTakeFocus(bool next) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&ClientHandler::NotifyTakeFocus, this, next));
+    return;
+  }
+
+  if (delegate_)
+    delegate_->OnTakeFocus(next);
+}
+
+void ClientHandler::BuildTestMenu(CefRefPtr<CefMenuModel> model) {
+  if (model->GetCount() > 0)
+    model->AddSeparator();
+
+  // Build the sub menu.
+  CefRefPtr<CefMenuModel> submenu =
+      model->AddSubMenu(CLIENT_ID_TESTMENU_SUBMENU, "Context Menu Test");
+  submenu->AddCheckItem(CLIENT_ID_TESTMENU_CHECKITEM, "Check Item");
+  submenu->AddRadioItem(CLIENT_ID_TESTMENU_RADIOITEM1, "Radio Item 1", 0);
+  submenu->AddRadioItem(CLIENT_ID_TESTMENU_RADIOITEM2, "Radio Item 2", 0);
+  submenu->AddRadioItem(CLIENT_ID_TESTMENU_RADIOITEM3, "Radio Item 3", 0);
+
+  // Check the check item.
+  if (test_menu_state_.check_item)
+    submenu->SetChecked(CLIENT_ID_TESTMENU_CHECKITEM, true);
+
+  // Check the selected radio item.
+  submenu->SetChecked(
+      CLIENT_ID_TESTMENU_RADIOITEM1 + test_menu_state_.radio_item, true);
+}
+
+bool ClientHandler::ExecuteTestMenu(int command_id) {
+  if (command_id == CLIENT_ID_TESTMENU_CHECKITEM) {
+    // Toggle the check item.
+    test_menu_state_.check_item ^= 1;
+    return true;
+  } else if (command_id >= CLIENT_ID_TESTMENU_RADIOITEM1 &&
+             command_id <= CLIENT_ID_TESTMENU_RADIOITEM3) {
+    // Store the selected radio item.
+    test_menu_state_.radio_item = (command_id - CLIENT_ID_TESTMENU_RADIOITEM1);
+    return true;
+  }
+
+  // Allow default handling to proceed.
+  return false;
+}
+
+void ClientHandler::SetOfflineState(CefRefPtr<CefBrowser> browser,
+                                    bool offline) {
+  // See DevTools protocol docs for message format specification.
+  CefRefPtr<CefDictionaryValue> params = CefDictionaryValue::Create();
+  params->SetBool("offline", offline);
+  params->SetDouble("latency", 0);
+  params->SetDouble("downloadThroughput", 0);
+  params->SetDouble("uploadThroughput", 0);
+  browser->GetHost()->ExecuteDevToolsMethod(
+      /*message_id=*/0, "Network.emulateNetworkConditions", params);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/client_handler.h b/src/tests/cefclient/browser/client_handler.h
new file mode 100644
index 0000000..599ef7f
--- /dev/null
+++ b/src/tests/cefclient/browser/client_handler.h
@@ -0,0 +1,422 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "include/cef_client.h"
+#include "include/wrapper/cef_helpers.h"
+#include "include/wrapper/cef_message_router.h"
+#include "include/wrapper/cef_resource_manager.h"
+#include "tests/cefclient/browser/client_types.h"
+#include "tests/cefclient/browser/test_runner.h"
+
+#if defined(OS_LINUX)
+#include "tests/cefclient/browser/dialog_handler_gtk.h"
+#endif
+
+namespace client {
+
+class ClientDownloadImageCallback;
+
+// Client handler abstract base class. Provides common functionality shared by
+// all concrete client handler implementations.
+class ClientHandler : public CefClient,
+                      public CefContextMenuHandler,
+                      public CefDisplayHandler,
+                      public CefDownloadHandler,
+                      public CefDragHandler,
+                      public CefFocusHandler,
+                      public CefKeyboardHandler,
+                      public CefLifeSpanHandler,
+                      public CefLoadHandler,
+                      public CefRequestHandler,
+                      public CefResourceRequestHandler {
+ public:
+  // Implement this interface to receive notification of ClientHandler
+  // events. The methods of this class will be called on the main thread unless
+  // otherwise indicated.
+  class Delegate {
+   public:
+    // Called when the browser is created.
+    virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) = 0;
+
+    // Called when the browser is closing.
+    virtual void OnBrowserClosing(CefRefPtr<CefBrowser> browser) = 0;
+
+    // Called when the browser has been closed.
+    virtual void OnBrowserClosed(CefRefPtr<CefBrowser> browser) = 0;
+
+    // Set the window URL address.
+    virtual void OnSetAddress(const std::string& url) = 0;
+
+    // Set the window title.
+    virtual void OnSetTitle(const std::string& title) = 0;
+
+    // Set the Favicon image.
+    virtual void OnSetFavicon(CefRefPtr<CefImage> image) {}
+
+    // Set fullscreen mode.
+    virtual void OnSetFullscreen(bool fullscreen) = 0;
+
+    // Auto-resize contents.
+    virtual void OnAutoResize(const CefSize& new_size) = 0;
+
+    // Set the loading state.
+    virtual void OnSetLoadingState(bool isLoading,
+                                   bool canGoBack,
+                                   bool canGoForward) = 0;
+
+    // Set the draggable regions.
+    virtual void OnSetDraggableRegions(
+        const std::vector<CefDraggableRegion>& regions) = 0;
+
+    // Set focus to the next/previous control.
+    virtual void OnTakeFocus(bool next) {}
+
+    // Called on the UI thread before a context menu is displayed.
+    virtual void OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) {}
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  typedef std::set<CefMessageRouterBrowserSide::Handler*> MessageHandlerSet;
+
+  // Constructor may be called on any thread.
+  // |delegate| must outlive this object or DetachDelegate() must be called.
+  ClientHandler(Delegate* delegate,
+                bool is_osr,
+                const std::string& startup_url);
+
+  // This object may outlive the Delegate object so it's necessary for the
+  // Delegate to detach itself before destruction.
+  void DetachDelegate();
+
+  // CefClient methods
+  CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() OVERRIDE {
+    return this;
+  }
+  CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE { return this; }
+  CefRefPtr<CefDownloadHandler> GetDownloadHandler() OVERRIDE { return this; }
+  CefRefPtr<CefDragHandler> GetDragHandler() OVERRIDE { return this; }
+  CefRefPtr<CefFocusHandler> GetFocusHandler() OVERRIDE { return this; }
+  CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() OVERRIDE { return this; }
+  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE { return this; }
+  CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE { return this; }
+  CefRefPtr<CefRequestHandler> GetRequestHandler() OVERRIDE { return this; }
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) OVERRIDE;
+
+#if defined(OS_LINUX)
+  CefRefPtr<CefDialogHandler> GetDialogHandler() OVERRIDE {
+    return dialog_handler_;
+  }
+  CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() OVERRIDE {
+    return dialog_handler_;
+  }
+#endif
+
+  // CefContextMenuHandler methods
+  void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefContextMenuParams> params,
+                           CefRefPtr<CefMenuModel> model) OVERRIDE;
+  bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefContextMenuParams> params,
+                            int command_id,
+                            EventFlags event_flags) OVERRIDE;
+
+  // CefDisplayHandler methods
+  void OnAddressChange(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       const CefString& url) OVERRIDE;
+  void OnTitleChange(CefRefPtr<CefBrowser> browser,
+                     const CefString& title) OVERRIDE;
+  void OnFaviconURLChange(CefRefPtr<CefBrowser> browser,
+                          const std::vector<CefString>& icon_urls) OVERRIDE;
+  void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
+                              bool fullscreen) OVERRIDE;
+  bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
+                        cef_log_severity_t level,
+                        const CefString& message,
+                        const CefString& source,
+                        int line) OVERRIDE;
+  bool OnAutoResize(CefRefPtr<CefBrowser> browser,
+                    const CefSize& new_size) OVERRIDE;
+
+  // CefDownloadHandler methods
+  void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDownloadItem> download_item,
+                        const CefString& suggested_name,
+                        CefRefPtr<CefBeforeDownloadCallback> callback) OVERRIDE;
+  void OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefDownloadItem> download_item,
+                         CefRefPtr<CefDownloadItemCallback> callback) OVERRIDE;
+
+  // CefDragHandler methods
+  bool OnDragEnter(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefDragData> dragData,
+                   CefDragHandler::DragOperationsMask mask) OVERRIDE;
+  void OnDraggableRegionsChanged(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const std::vector<CefDraggableRegion>& regions) OVERRIDE;
+
+  // CefFocusHandler methods
+  void OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) OVERRIDE;
+  bool OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source) OVERRIDE;
+
+  // CefKeyboardHandler methods
+  bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
+                     const CefKeyEvent& event,
+                     CefEventHandle os_event,
+                     bool* is_keyboard_shortcut) OVERRIDE;
+
+  // CefLifeSpanHandler methods
+  bool OnBeforePopup(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const CefString& target_url,
+      const CefString& target_frame_name,
+      CefLifeSpanHandler::WindowOpenDisposition target_disposition,
+      bool user_gesture,
+      const CefPopupFeatures& popupFeatures,
+      CefWindowInfo& windowInfo,
+      CefRefPtr<CefClient>& client,
+      CefBrowserSettings& settings,
+      CefRefPtr<CefDictionaryValue>& extra_info,
+      bool* no_javascript_access) OVERRIDE;
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+  // CefLoadHandler methods
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) OVERRIDE;
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) OVERRIDE;
+
+  // CefRequestHandler methods
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) OVERRIDE;
+  bool OnOpenURLFromTab(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const CefString& target_url,
+      CefRequestHandler::WindowOpenDisposition target_disposition,
+      bool user_gesture) OVERRIDE;
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) OVERRIDE;
+  bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
+                          const CefString& origin_url,
+                          bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) OVERRIDE;
+  bool OnQuotaRequest(CefRefPtr<CefBrowser> browser,
+                      const CefString& origin_url,
+                      int64 new_size,
+                      CefRefPtr<CefRequestCallback> callback) OVERRIDE;
+  bool OnCertificateError(CefRefPtr<CefBrowser> browser,
+                          ErrorCode cert_error,
+                          const CefString& request_url,
+                          CefRefPtr<CefSSLInfo> ssl_info,
+                          CefRefPtr<CefRequestCallback> callback) OVERRIDE;
+  bool OnSelectClientCertificate(
+      CefRefPtr<CefBrowser> browser,
+      bool isProxy,
+      const CefString& host,
+      int port,
+      const X509CertificateList& certificates,
+      CefRefPtr<CefSelectClientCertificateCallback> callback) OVERRIDE;
+  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                 TerminationStatus status) OVERRIDE;
+  void OnDocumentAvailableInMainFrame(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+  // CefResourceRequestHandler methods
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) OVERRIDE;
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) OVERRIDE;
+  CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefResponse> response) OVERRIDE;
+  void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefRequest> request,
+                           bool& allow_os_execution) OVERRIDE;
+
+  // Returns the number of browsers currently using this handler. Can only be
+  // called on the CEF UI thread.
+  int GetBrowserCount() const;
+
+  // Show a new DevTools popup window.
+  void ShowDevTools(CefRefPtr<CefBrowser> browser,
+                    const CefPoint& inspect_element_at);
+
+  // Close the existing DevTools popup window, if any.
+  void CloseDevTools(CefRefPtr<CefBrowser> browser);
+
+  // Test if the current site has SSL information available.
+  bool HasSSLInformation(CefRefPtr<CefBrowser> browser);
+
+  // Show SSL information for the current site.
+  void ShowSSLInformation(CefRefPtr<CefBrowser> browser);
+
+  // Set a string resource for loading via StringResourceProvider.
+  void SetStringResource(const std::string& page, const std::string& data);
+
+  // Returns the Delegate.
+  Delegate* delegate() const { return delegate_; }
+
+  // Returns the startup URL.
+  std::string startup_url() const { return startup_url_; }
+
+  // Returns true if this handler uses off-screen rendering.
+  bool is_osr() const { return is_osr_; }
+
+  // Set/get whether the client should download favicon images. Only safe to
+  // call immediately after client creation or on the browser process UI thread.
+  bool download_favicon_images() const { return download_favicon_images_; }
+  void set_download_favicon_images(bool allow) {
+    download_favicon_images_ = allow;
+  }
+
+ private:
+  friend class ClientDownloadImageCallback;
+
+  // Create a new popup window using the specified information. |is_devtools|
+  // will be true if the window will be used for DevTools. Return true to
+  // proceed with popup browser creation or false to cancel the popup browser.
+  // May be called on any thead.
+  bool CreatePopupWindow(CefRefPtr<CefBrowser> browser,
+                         bool is_devtools,
+                         const CefPopupFeatures& popupFeatures,
+                         CefWindowInfo& windowInfo,
+                         CefRefPtr<CefClient>& client,
+                         CefBrowserSettings& settings);
+
+  // Execute Delegate notifications on the main thread.
+  void NotifyBrowserCreated(CefRefPtr<CefBrowser> browser);
+  void NotifyBrowserClosing(CefRefPtr<CefBrowser> browser);
+  void NotifyBrowserClosed(CefRefPtr<CefBrowser> browser);
+  void NotifyAddress(const CefString& url);
+  void NotifyTitle(const CefString& title);
+  void NotifyFavicon(CefRefPtr<CefImage> image);
+  void NotifyFullscreen(bool fullscreen);
+  void NotifyAutoResize(const CefSize& new_size);
+  void NotifyLoadingState(bool isLoading, bool canGoBack, bool canGoForward);
+  void NotifyDraggableRegions(const std::vector<CefDraggableRegion>& regions);
+  void NotifyTakeFocus(bool next);
+
+  // Test context menu creation.
+  void BuildTestMenu(CefRefPtr<CefMenuModel> model);
+  bool ExecuteTestMenu(int command_id);
+
+  void SetOfflineState(CefRefPtr<CefBrowser> browser, bool offline);
+
+  // THREAD SAFE MEMBERS
+  // The following members may be accessed from any thread.
+
+  // True if this handler uses off-screen rendering.
+  const bool is_osr_;
+
+  // The startup URL.
+  const std::string startup_url_;
+
+  // True if mouse cursor change is disabled.
+  bool mouse_cursor_change_disabled_;
+
+  // True if the browser is currently offline.
+  bool offline_;
+
+  // True if Favicon images should be downloaded.
+  bool download_favicon_images_;
+
+#if defined(OS_LINUX)
+  // Custom dialog handler for GTK.
+  CefRefPtr<ClientDialogHandlerGtk> dialog_handler_;
+#endif
+
+  // Handles the browser side of query routing. The renderer side is handled
+  // in client_renderer.cc.
+  CefRefPtr<CefMessageRouterBrowserSide> message_router_;
+
+  // Manages the registration and delivery of resources.
+  CefRefPtr<CefResourceManager> resource_manager_;
+
+  // Used to manage string resources in combination with StringResourceProvider.
+  // Only accessed on the IO thread.
+  test_runner::StringResourceMap string_resource_map_;
+
+  // MAIN THREAD MEMBERS
+  // The following members will only be accessed on the main thread. This will
+  // be the same as the CEF UI thread except when using multi-threaded message
+  // loop mode on Windows.
+
+  Delegate* delegate_;
+
+  // UI THREAD MEMBERS
+  // The following members will only be accessed on the CEF UI thread.
+
+  // Track state information for the text context menu.
+  struct TestMenuState {
+    TestMenuState() : check_item(true), radio_item(0) {}
+    bool check_item;
+    int radio_item;
+  } test_menu_state_;
+
+  // The current number of browsers using this handler.
+  int browser_count_;
+
+  // Console logging state.
+  const std::string console_log_file_;
+  bool first_console_message_;
+
+  // True if an editable field currently has focus.
+  bool focus_on_editable_field_;
+
+  // True for the initial navigation after browser creation.
+  bool initial_navigation_;
+
+  // Set of Handlers registered with the message router.
+  MessageHandlerSet message_handler_set_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientHandler);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_H_
diff --git a/src/tests/cefclient/browser/client_handler_osr.cc b/src/tests/cefclient/browser/client_handler_osr.cc
new file mode 100644
index 0000000..a5873da
--- /dev/null
+++ b/src/tests/cefclient/browser/client_handler_osr.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/client_handler_osr.h"
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+
+namespace client {
+
+ClientHandlerOsr::ClientHandlerOsr(Delegate* delegate,
+                                   OsrDelegate* osr_delegate,
+                                   const std::string& startup_url)
+    : ClientHandler(delegate, true, startup_url), osr_delegate_(osr_delegate) {
+  DCHECK(osr_delegate_);
+}
+
+void ClientHandlerOsr::DetachOsrDelegate() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&ClientHandlerOsr::DetachOsrDelegate, this));
+    return;
+  }
+
+  DCHECK(osr_delegate_);
+  osr_delegate_ = nullptr;
+}
+
+void ClientHandlerOsr::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  if (osr_delegate_)
+    osr_delegate_->OnAfterCreated(browser);
+  ClientHandler::OnAfterCreated(browser);
+}
+
+void ClientHandlerOsr::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  if (osr_delegate_)
+    osr_delegate_->OnBeforeClose(browser);
+  ClientHandler::OnBeforeClose(browser);
+}
+
+bool ClientHandlerOsr::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                         CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return false;
+  return osr_delegate_->GetRootScreenRect(browser, rect);
+}
+
+void ClientHandlerOsr::GetViewRect(CefRefPtr<CefBrowser> browser,
+                                   CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_) {
+    // Never return an empty rectangle.
+    rect.width = rect.height = 1;
+    return;
+  }
+  osr_delegate_->GetViewRect(browser, rect);
+}
+
+bool ClientHandlerOsr::GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                      int viewX,
+                                      int viewY,
+                                      int& screenX,
+                                      int& screenY) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return false;
+  return osr_delegate_->GetScreenPoint(browser, viewX, viewY, screenX, screenY);
+}
+
+bool ClientHandlerOsr::GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                                     CefScreenInfo& screen_info) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return false;
+  return osr_delegate_->GetScreenInfo(browser, screen_info);
+}
+
+void ClientHandlerOsr::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  return osr_delegate_->OnPopupShow(browser, show);
+}
+
+void ClientHandlerOsr::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                                   const CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  return osr_delegate_->OnPopupSize(browser, rect);
+}
+
+void ClientHandlerOsr::OnPaint(CefRefPtr<CefBrowser> browser,
+                               PaintElementType type,
+                               const RectList& dirtyRects,
+                               const void* buffer,
+                               int width,
+                               int height) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  osr_delegate_->OnPaint(browser, type, dirtyRects, buffer, width, height);
+}
+
+void ClientHandlerOsr::OnAcceleratedPaint(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::PaintElementType type,
+    const CefRenderHandler::RectList& dirtyRects,
+    void* share_handle) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  osr_delegate_->OnAcceleratedPaint(browser, type, dirtyRects, share_handle);
+}
+
+void ClientHandlerOsr::OnCursorChange(CefRefPtr<CefBrowser> browser,
+                                      CefCursorHandle cursor,
+                                      CursorType type,
+                                      const CefCursorInfo& custom_cursor_info) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  osr_delegate_->OnCursorChange(browser, cursor, type, custom_cursor_info);
+}
+
+bool ClientHandlerOsr::StartDragging(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDragData> drag_data,
+    CefRenderHandler::DragOperationsMask allowed_ops,
+    int x,
+    int y) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return false;
+  return osr_delegate_->StartDragging(browser, drag_data, allowed_ops, x, y);
+}
+
+void ClientHandlerOsr::UpdateDragCursor(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::DragOperation operation) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  osr_delegate_->UpdateDragCursor(browser, operation);
+}
+
+void ClientHandlerOsr::OnImeCompositionRangeChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefRange& selection_range,
+    const CefRenderHandler::RectList& character_bounds) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  osr_delegate_->OnImeCompositionRangeChanged(browser, selection_range,
+                                              character_bounds);
+}
+
+void ClientHandlerOsr::OnAccessibilityTreeChange(CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  osr_delegate_->UpdateAccessibilityTree(value);
+}
+
+void ClientHandlerOsr::OnAccessibilityLocationChange(
+    CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!osr_delegate_)
+    return;
+  osr_delegate_->UpdateAccessibilityLocation(value);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/client_handler_osr.h b/src/tests/cefclient/browser/client_handler_osr.h
new file mode 100644
index 0000000..1a2c200
--- /dev/null
+++ b/src/tests/cefclient/browser/client_handler_osr.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_OSR_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_OSR_H_
+#pragma once
+
+#include "tests/cefclient/browser/client_handler.h"
+
+namespace client {
+
+// Client handler implementation for windowless browsers. There will only ever
+// be one browser per handler instance.
+class ClientHandlerOsr : public ClientHandler,
+                         public CefAccessibilityHandler,
+                         public CefRenderHandler {
+ public:
+  // Implement this interface to receive notification of ClientHandlerOsr
+  // events. The methods of this class will be called on the CEF UI thread.
+  class OsrDelegate {
+   public:
+    // These methods match the CefLifeSpanHandler interface.
+    virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) = 0;
+    virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) = 0;
+
+    // These methods match the CefRenderHandler interface.
+    virtual bool GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                   CefRect& rect) = 0;
+    virtual void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) = 0;
+    virtual bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                int viewX,
+                                int viewY,
+                                int& screenX,
+                                int& screenY) = 0;
+    virtual bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                               CefScreenInfo& screen_info) = 0;
+    virtual void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) = 0;
+    virtual void OnPopupSize(CefRefPtr<CefBrowser> browser,
+                             const CefRect& rect) = 0;
+    virtual void OnPaint(CefRefPtr<CefBrowser> browser,
+                         CefRenderHandler::PaintElementType type,
+                         const CefRenderHandler::RectList& dirtyRects,
+                         const void* buffer,
+                         int width,
+                         int height) = 0;
+    virtual void OnAcceleratedPaint(
+        CefRefPtr<CefBrowser> browser,
+        CefRenderHandler::PaintElementType type,
+        const CefRenderHandler::RectList& dirtyRects,
+        void* share_handle) {}
+    virtual void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                                CefCursorHandle cursor,
+                                CefRenderHandler::CursorType type,
+                                const CefCursorInfo& custom_cursor_info) = 0;
+    virtual bool StartDragging(CefRefPtr<CefBrowser> browser,
+                               CefRefPtr<CefDragData> drag_data,
+                               CefRenderHandler::DragOperationsMask allowed_ops,
+                               int x,
+                               int y) = 0;
+    virtual void UpdateDragCursor(
+        CefRefPtr<CefBrowser> browser,
+        CefRenderHandler::DragOperation operation) = 0;
+    virtual void OnImeCompositionRangeChanged(
+        CefRefPtr<CefBrowser> browser,
+        const CefRange& selection_range,
+        const CefRenderHandler::RectList& character_bounds) = 0;
+
+    virtual void UpdateAccessibilityTree(CefRefPtr<CefValue> value) = 0;
+
+    virtual void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) = 0;
+
+   protected:
+    virtual ~OsrDelegate() {}
+  };
+
+  ClientHandlerOsr(Delegate* delegate,
+                   OsrDelegate* osr_delegate,
+                   const std::string& startup_url);
+
+  // This object may outlive the OsrDelegate object so it's necessary for the
+  // OsrDelegate to detach itself before destruction.
+  void DetachOsrDelegate();
+
+  // CefClient methods.
+  CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE { return this; }
+  CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() OVERRIDE {
+    return this;
+  }
+
+  // CefLifeSpanHandler methods.
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+  // CefRenderHandler methods.
+  bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                      int viewX,
+                      int viewY,
+                      int& screenX,
+                      int& screenY) OVERRIDE;
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) OVERRIDE;
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) OVERRIDE;
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) OVERRIDE;
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) OVERRIDE;
+  void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                          CefRenderHandler::PaintElementType type,
+                          const CefRenderHandler::RectList& dirtyRects,
+                          void* share_handle) OVERRIDE;
+  void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                      CefCursorHandle cursor,
+                      CursorType type,
+                      const CefCursorInfo& custom_cursor_info) OVERRIDE;
+  bool StartDragging(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefDragData> drag_data,
+                     CefRenderHandler::DragOperationsMask allowed_ops,
+                     int x,
+                     int y) OVERRIDE;
+  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                        CefRenderHandler::DragOperation operation) OVERRIDE;
+  void OnImeCompositionRangeChanged(
+      CefRefPtr<CefBrowser> browser,
+      const CefRange& selection_range,
+      const CefRenderHandler::RectList& character_bounds) OVERRIDE;
+
+  // CefAccessibilityHandler methods.
+  void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) OVERRIDE;
+  void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) OVERRIDE;
+
+ private:
+  // Only accessed on the UI thread.
+  OsrDelegate* osr_delegate_;
+
+  // Include the default reference counting implementation.
+  IMPLEMENT_REFCOUNTING(ClientHandlerOsr);
+  DISALLOW_COPY_AND_ASSIGN(ClientHandlerOsr);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_OSR_H_
diff --git a/src/tests/cefclient/browser/client_handler_std.cc b/src/tests/cefclient/browser/client_handler_std.cc
new file mode 100644
index 0000000..803028f
--- /dev/null
+++ b/src/tests/cefclient/browser/client_handler_std.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/client_handler_std.h"
+
+namespace client {
+
+ClientHandlerStd::ClientHandlerStd(Delegate* delegate,
+                                   const std::string& startup_url)
+    : ClientHandler(delegate, false, startup_url) {}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/client_handler_std.h b/src/tests/cefclient/browser/client_handler_std.h
new file mode 100644
index 0000000..5a73a3e
--- /dev/null
+++ b/src/tests/cefclient/browser/client_handler_std.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_STD_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_STD_H_
+#pragma once
+
+#include "tests/cefclient/browser/client_handler.h"
+
+namespace client {
+
+// Client handler implementation for windowed browsers. There will only ever be
+// one browser per handler instance.
+class ClientHandlerStd : public ClientHandler {
+ public:
+  ClientHandlerStd(Delegate* delegate, const std::string& startup_url);
+
+ private:
+  // Include the default reference counting implementation.
+  IMPLEMENT_REFCOUNTING(ClientHandlerStd);
+  DISALLOW_COPY_AND_ASSIGN(ClientHandlerStd);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_HANDLER_STD_H_
diff --git a/src/tests/cefclient/browser/client_types.h b/src/tests/cefclient/browser/client_types.h
new file mode 100644
index 0000000..2eefd19
--- /dev/null
+++ b/src/tests/cefclient/browser/client_types.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_TYPES_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_TYPES_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+#if defined(OS_LINUX)
+#include <gtk/gtk.h>
+
+// The Linux client uses GTK instead of the underlying platform type (X11).
+#define ClientWindowHandle GtkWidget*
+#else
+#define ClientWindowHandle CefWindowHandle
+#endif
+
+#if defined(OS_MACOSX)
+#define ClientNativeMacWindow void*
+#ifdef __OBJC__
+#define CAST_CLIENT_NATIVE_MAC_WINDOW_TO_NSWINDOW(native) \
+  (__bridge NSWindow*)native
+#define CAST_NSWINDOW_TO_CLIENT_NATIVE_MAC_WINDOW(window) (__bridge void*)window
+#endif  // __OBJC__
+#endif  // defined OS_MACOSX
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_TYPES_H_
diff --git a/src/tests/cefclient/browser/dialog_handler_gtk.cc b/src/tests/cefclient/browser/dialog_handler_gtk.cc
new file mode 100644
index 0000000..1958a52
--- /dev/null
+++ b/src/tests/cefclient/browser/dialog_handler_gtk.cc
@@ -0,0 +1,472 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/dialog_handler_gtk.h"
+
+#include <libgen.h>
+#include <sys/stat.h>
+
+#include "include/cef_browser.h"
+#include "include/cef_parser.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/root_window.h"
+#include "tests/cefclient/browser/util_gtk.h"
+
+namespace client {
+
+namespace {
+
+const char kPromptTextId[] = "cef_prompt_text";
+
+// If there's a text entry in the dialog, get the text from the first one and
+// return it.
+std::string GetPromptText(GtkDialog* dialog) {
+  GtkWidget* widget = static_cast<GtkWidget*>(
+      g_object_get_data(G_OBJECT(dialog), kPromptTextId));
+  if (widget)
+    return gtk_entry_get_text(GTK_ENTRY(widget));
+  return std::string();
+}
+
+std::string GetDescriptionFromMimeType(const std::string& mime_type) {
+  // Check for wild card mime types and return an appropriate description.
+  static const struct {
+    const char* mime_type;
+    const char* label;
+  } kWildCardMimeTypes[] = {
+      {"audio", "Audio Files"},
+      {"image", "Image Files"},
+      {"text", "Text Files"},
+      {"video", "Video Files"},
+  };
+
+  for (size_t i = 0;
+       i < sizeof(kWildCardMimeTypes) / sizeof(kWildCardMimeTypes[0]); ++i) {
+    if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
+      return std::string(kWildCardMimeTypes[i].label);
+  }
+
+  return std::string();
+}
+
+void AddFilters(GtkFileChooser* chooser,
+                const std::vector<CefString>& accept_filters,
+                bool include_all_files,
+                std::vector<GtkFileFilter*>* filters) {
+  bool has_filter = false;
+
+  for (size_t i = 0; i < accept_filters.size(); ++i) {
+    const std::string& filter = accept_filters[i];
+    if (filter.empty())
+      continue;
+
+    std::vector<std::string> extensions;
+    std::string description;
+
+    size_t sep_index = filter.find('|');
+    if (sep_index != std::string::npos) {
+      // Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
+      description = filter.substr(0, sep_index);
+
+      const std::string& exts = filter.substr(sep_index + 1);
+      size_t last = 0;
+      size_t size = exts.size();
+      for (size_t i = 0; i <= size; ++i) {
+        if (i == size || exts[i] == ';') {
+          std::string ext(exts, last, i - last);
+          if (!ext.empty() && ext[0] == '.')
+            extensions.push_back(ext);
+          last = i + 1;
+        }
+      }
+    } else if (filter[0] == '.') {
+      // Treat as an extension beginning with the '.' character.
+      extensions.push_back(filter);
+    } else {
+      // Otherwise convert mime type to one or more extensions.
+      description = GetDescriptionFromMimeType(filter);
+
+      std::vector<CefString> ext;
+      CefGetExtensionsForMimeType(filter, ext);
+      for (size_t x = 0; x < ext.size(); ++x)
+        extensions.push_back("." + ext[x].ToString());
+    }
+
+    if (extensions.empty())
+      continue;
+
+    GtkFileFilter* gtk_filter = gtk_file_filter_new();
+
+    std::string ext_str;
+    for (size_t x = 0; x < extensions.size(); ++x) {
+      const std::string& pattern = "*" + extensions[x];
+      if (x != 0)
+        ext_str += ";";
+      ext_str += pattern;
+      gtk_file_filter_add_pattern(gtk_filter, pattern.c_str());
+    }
+
+    if (description.empty())
+      description = ext_str;
+    else
+      description += " (" + ext_str + ")";
+
+    gtk_file_filter_set_name(gtk_filter, description.c_str());
+    gtk_file_chooser_add_filter(chooser, gtk_filter);
+    if (!has_filter)
+      has_filter = true;
+
+    filters->push_back(gtk_filter);
+  }
+
+  // Add the *.* filter, but only if we have added other filters (otherwise it
+  // is implied).
+  if (include_all_files && has_filter) {
+    GtkFileFilter* filter = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filter, "*");
+    gtk_file_filter_set_name(filter, "All Files (*)");
+    gtk_file_chooser_add_filter(chooser, filter);
+  }
+}
+
+GtkWindow* GetWindow(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  scoped_refptr<RootWindow> root_window =
+      RootWindow::GetForBrowser(browser->GetIdentifier());
+  if (root_window) {
+    GtkWindow* window = GTK_WINDOW(root_window->GetWindowHandle());
+    if (!window)
+      LOG(ERROR) << "No GtkWindow for browser";
+    return window;
+  }
+  return nullptr;
+}
+
+void RunCallback(base::Callback<void(GtkWindow*)> callback, GtkWindow* window) {
+  callback.Run(window);
+}
+
+}  // namespace
+
+ClientDialogHandlerGtk::ClientDialogHandlerGtk() : gtk_dialog_(nullptr) {}
+
+bool ClientDialogHandlerGtk::OnFileDialog(
+    CefRefPtr<CefBrowser> browser,
+    FileDialogMode mode,
+    const CefString& title,
+    const CefString& default_file_path,
+    const std::vector<CefString>& accept_filters,
+    int selected_accept_filter,
+    CefRefPtr<CefFileDialogCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  OnFileDialogParams params;
+  params.browser = browser;
+  params.mode = mode;
+  params.title = title;
+  params.default_file_path = default_file_path;
+  params.accept_filters = accept_filters;
+  params.selected_accept_filter = selected_accept_filter;
+  params.callback = callback;
+
+  GetWindowAndContinue(
+      browser,
+      base::Bind(&ClientDialogHandlerGtk::OnFileDialogContinue, this, params));
+  return true;
+}
+
+bool ClientDialogHandlerGtk::OnJSDialog(CefRefPtr<CefBrowser> browser,
+                                        const CefString& origin_url,
+                                        JSDialogType dialog_type,
+                                        const CefString& message_text,
+                                        const CefString& default_prompt_text,
+                                        CefRefPtr<CefJSDialogCallback> callback,
+                                        bool& suppress_message) {
+  CEF_REQUIRE_UI_THREAD();
+
+  OnJSDialogParams params;
+  params.browser = browser;
+  params.origin_url = origin_url;
+  params.dialog_type = dialog_type;
+  params.message_text = message_text;
+  params.default_prompt_text = default_prompt_text;
+  params.callback = callback;
+
+  GetWindowAndContinue(
+      browser,
+      base::Bind(&ClientDialogHandlerGtk::OnJSDialogContinue, this, params));
+  return true;
+}
+
+bool ClientDialogHandlerGtk::OnBeforeUnloadDialog(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& message_text,
+    bool is_reload,
+    CefRefPtr<CefJSDialogCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  const std::string& new_message_text =
+      message_text.ToString() + "\n\nIs it OK to leave/reload this page?";
+  bool suppress_message = false;
+
+  return OnJSDialog(browser, CefString(), JSDIALOGTYPE_CONFIRM,
+                    new_message_text, CefString(), callback, suppress_message);
+}
+
+void ClientDialogHandlerGtk::OnResetDialogState(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!gtk_dialog_)
+    return;
+
+  gtk_widget_destroy(gtk_dialog_);
+  gtk_dialog_ = nullptr;
+  js_dialog_callback_ = nullptr;
+}
+
+void ClientDialogHandlerGtk::OnFileDialogContinue(OnFileDialogParams params,
+                                                  GtkWindow* window) {
+  CEF_REQUIRE_UI_THREAD();
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  std::vector<CefString> files;
+
+  GtkFileChooserAction action;
+  const gchar* accept_button;
+
+  // Remove any modifier flags.
+  FileDialogMode mode_type =
+      static_cast<FileDialogMode>(params.mode & FILE_DIALOG_TYPE_MASK);
+
+  if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
+    action = GTK_FILE_CHOOSER_ACTION_OPEN;
+    accept_button = GTK_STOCK_OPEN;
+  } else if (mode_type == FILE_DIALOG_OPEN_FOLDER) {
+    action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
+    accept_button = GTK_STOCK_OPEN;
+  } else if (mode_type == FILE_DIALOG_SAVE) {
+    action = GTK_FILE_CHOOSER_ACTION_SAVE;
+    accept_button = GTK_STOCK_SAVE;
+  } else {
+    NOTREACHED();
+    params.callback->Cancel();
+    return;
+  }
+
+  std::string title_str;
+  if (!params.title.empty()) {
+    title_str = params.title;
+  } else {
+    switch (mode_type) {
+      case FILE_DIALOG_OPEN:
+        title_str = "Open File";
+        break;
+      case FILE_DIALOG_OPEN_MULTIPLE:
+        title_str = "Open Files";
+        break;
+      case FILE_DIALOG_OPEN_FOLDER:
+        title_str = "Open Folder";
+        break;
+      case FILE_DIALOG_SAVE:
+        title_str = "Save File";
+        break;
+      default:
+        break;
+    }
+  }
+
+  GtkWidget* dialog = gtk_file_chooser_dialog_new(
+      title_str.c_str(), GTK_WINDOW(window), action, GTK_STOCK_CANCEL,
+      GTK_RESPONSE_CANCEL, accept_button, GTK_RESPONSE_ACCEPT, NULL);
+
+  if (mode_type == FILE_DIALOG_OPEN_MULTIPLE)
+    gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
+
+  if (mode_type == FILE_DIALOG_SAVE) {
+    gtk_file_chooser_set_do_overwrite_confirmation(
+        GTK_FILE_CHOOSER(dialog),
+        !!(params.mode & FILE_DIALOG_OVERWRITEPROMPT_FLAG));
+  }
+
+  gtk_file_chooser_set_show_hidden(
+      GTK_FILE_CHOOSER(dialog), !(params.mode & FILE_DIALOG_HIDEREADONLY_FLAG));
+
+  if (!params.default_file_path.empty() && mode_type == FILE_DIALOG_SAVE) {
+    const std::string& file_path = params.default_file_path;
+    bool exists = false;
+
+    struct stat sb;
+    if (stat(file_path.c_str(), &sb) == 0 && S_ISREG(sb.st_mode)) {
+      // Use the directory and name of the existing file.
+      gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), file_path.data());
+      exists = true;
+    }
+
+    if (!exists) {
+      // Set the current file name but let the user choose the directory.
+      std::string file_name_str = file_path;
+      const char* file_name = basename(const_cast<char*>(file_name_str.data()));
+      gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), file_name);
+    }
+  }
+
+  std::vector<GtkFileFilter*> filters;
+  AddFilters(GTK_FILE_CHOOSER(dialog), params.accept_filters, true, &filters);
+  if (params.selected_accept_filter < static_cast<int>(filters.size())) {
+    gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog),
+                                filters[params.selected_accept_filter]);
+  }
+
+  bool success = false;
+
+  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+    if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_FOLDER ||
+        mode_type == FILE_DIALOG_SAVE) {
+      char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+      files.push_back(std::string(filename));
+      success = true;
+    } else if (mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
+      GSList* filenames =
+          gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+      if (filenames) {
+        for (GSList* iter = filenames; iter != nullptr;
+             iter = g_slist_next(iter)) {
+          std::string path(static_cast<char*>(iter->data));
+          g_free(iter->data);
+          files.push_back(path);
+        }
+        g_slist_free(filenames);
+        success = true;
+      }
+    }
+  }
+
+  int filter_index = params.selected_accept_filter;
+  if (success) {
+    GtkFileFilter* selected_filter =
+        gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
+    if (selected_filter != nullptr) {
+      for (size_t x = 0; x < filters.size(); ++x) {
+        if (filters[x] == selected_filter) {
+          filter_index = x;
+          break;
+        }
+      }
+    }
+  }
+
+  gtk_widget_destroy(dialog);
+
+  if (success)
+    params.callback->Continue(filter_index, files);
+  else
+    params.callback->Cancel();
+}
+
+void ClientDialogHandlerGtk::OnJSDialogContinue(OnJSDialogParams params,
+                                                GtkWindow* window) {
+  CEF_REQUIRE_UI_THREAD();
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkButtonsType buttons = GTK_BUTTONS_NONE;
+  GtkMessageType gtk_message_type = GTK_MESSAGE_OTHER;
+  std::string title;
+
+  switch (params.dialog_type) {
+    case JSDIALOGTYPE_ALERT:
+      buttons = GTK_BUTTONS_NONE;
+      gtk_message_type = GTK_MESSAGE_WARNING;
+      title = "JavaScript Alert";
+      break;
+
+    case JSDIALOGTYPE_CONFIRM:
+      buttons = GTK_BUTTONS_CANCEL;
+      gtk_message_type = GTK_MESSAGE_QUESTION;
+      title = "JavaScript Confirm";
+      break;
+
+    case JSDIALOGTYPE_PROMPT:
+      buttons = GTK_BUTTONS_CANCEL;
+      gtk_message_type = GTK_MESSAGE_QUESTION;
+      title = "JavaScript Prompt";
+      break;
+  }
+
+  js_dialog_callback_ = params.callback;
+
+  if (!params.origin_url.empty()) {
+    title += " - ";
+    title += CefFormatUrlForSecurityDisplay(params.origin_url).ToString();
+  }
+
+  gtk_dialog_ = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
+                                       gtk_message_type, buttons, "%s",
+                                       params.message_text.ToString().c_str());
+  g_signal_connect(gtk_dialog_, "delete-event",
+                   G_CALLBACK(gtk_widget_hide_on_delete), NULL);
+
+  gtk_window_set_title(GTK_WINDOW(gtk_dialog_), title.c_str());
+
+  GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_),
+                                               GTK_STOCK_OK, GTK_RESPONSE_OK);
+
+  if (params.dialog_type != JSDIALOGTYPE_PROMPT)
+    gtk_widget_grab_focus(ok_button);
+
+  if (params.dialog_type == JSDIALOGTYPE_PROMPT) {
+    GtkWidget* content_area =
+        gtk_dialog_get_content_area(GTK_DIALOG(gtk_dialog_));
+    GtkWidget* text_box = gtk_entry_new();
+    gtk_entry_set_text(GTK_ENTRY(text_box),
+                       params.default_prompt_text.ToString().c_str());
+    gtk_box_pack_start(GTK_BOX(content_area), text_box, TRUE, TRUE, 0);
+    g_object_set_data(G_OBJECT(gtk_dialog_), kPromptTextId, text_box);
+    gtk_entry_set_activates_default(GTK_ENTRY(text_box), TRUE);
+  }
+
+  gtk_dialog_set_default_response(GTK_DIALOG(gtk_dialog_), GTK_RESPONSE_OK);
+  g_signal_connect(gtk_dialog_, "response", G_CALLBACK(OnDialogResponse), this);
+  gtk_widget_show_all(GTK_WIDGET(gtk_dialog_));
+}
+
+void ClientDialogHandlerGtk::GetWindowAndContinue(
+    CefRefPtr<CefBrowser> browser,
+    base::Callback<void(GtkWindow*)> callback) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(&ClientDialogHandlerGtk::GetWindowAndContinue,
+                                 this, browser, callback));
+    return;
+  }
+
+  GtkWindow* window = GetWindow(browser);
+  if (window) {
+    CefPostTask(TID_UI, base::Bind(RunCallback, callback, window));
+  }
+}
+
+// static
+void ClientDialogHandlerGtk::OnDialogResponse(GtkDialog* dialog,
+                                              gint response_id,
+                                              ClientDialogHandlerGtk* handler) {
+  CEF_REQUIRE_UI_THREAD();
+
+  DCHECK_EQ(dialog, GTK_DIALOG(handler->gtk_dialog_));
+  switch (response_id) {
+    case GTK_RESPONSE_OK:
+      handler->js_dialog_callback_->Continue(true, GetPromptText(dialog));
+      break;
+    case GTK_RESPONSE_CANCEL:
+    case GTK_RESPONSE_DELETE_EVENT:
+      handler->js_dialog_callback_->Continue(false, CefString());
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  handler->OnResetDialogState(nullptr);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/dialog_handler_gtk.h b/src/tests/cefclient/browser/dialog_handler_gtk.h
new file mode 100644
index 0000000..c29d9f0
--- /dev/null
+++ b/src/tests/cefclient/browser/dialog_handler_gtk.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_DIALOG_HANDLER_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_DIALOG_HANDLER_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "include/base/cef_callback_forward.h"
+#include "include/cef_dialog_handler.h"
+#include "include/cef_jsdialog_handler.h"
+
+namespace client {
+
+class ClientDialogHandlerGtk : public CefDialogHandler,
+                               public CefJSDialogHandler {
+ public:
+  ClientDialogHandlerGtk();
+
+  // CefDialogHandler methods.
+  bool OnFileDialog(CefRefPtr<CefBrowser> browser,
+                    FileDialogMode mode,
+                    const CefString& title,
+                    const CefString& default_file_path,
+                    const std::vector<CefString>& accept_filters,
+                    int selected_accept_filter,
+                    CefRefPtr<CefFileDialogCallback> callback) OVERRIDE;
+
+  // CefJSDialogHandler methods.
+  bool OnJSDialog(CefRefPtr<CefBrowser> browser,
+                  const CefString& origin_url,
+                  JSDialogType dialog_type,
+                  const CefString& message_text,
+                  const CefString& default_prompt_text,
+                  CefRefPtr<CefJSDialogCallback> callback,
+                  bool& suppress_message) OVERRIDE;
+  bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
+                            const CefString& message_text,
+                            bool is_reload,
+                            CefRefPtr<CefJSDialogCallback> callback) OVERRIDE;
+  void OnResetDialogState(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+ private:
+  struct OnFileDialogParams {
+    CefRefPtr<CefBrowser> browser;
+    FileDialogMode mode;
+    CefString title;
+    CefString default_file_path;
+    std::vector<CefString> accept_filters;
+    int selected_accept_filter;
+    CefRefPtr<CefFileDialogCallback> callback;
+  };
+  void OnFileDialogContinue(OnFileDialogParams params, GtkWindow* window);
+
+  struct OnJSDialogParams {
+    CefRefPtr<CefBrowser> browser;
+    CefString origin_url;
+    JSDialogType dialog_type;
+    CefString message_text;
+    CefString default_prompt_text;
+    CefRefPtr<CefJSDialogCallback> callback;
+  };
+  void OnJSDialogContinue(OnJSDialogParams params, GtkWindow* window);
+
+  void GetWindowAndContinue(CefRefPtr<CefBrowser> browser,
+                            base::Callback<void(GtkWindow*)> callback);
+
+  static void OnDialogResponse(GtkDialog* dialog,
+                               gint response_id,
+                               ClientDialogHandlerGtk* handler);
+
+  GtkWidget* gtk_dialog_;
+  CefRefPtr<CefJSDialogCallback> js_dialog_callback_;
+
+  IMPLEMENT_REFCOUNTING(ClientDialogHandlerGtk);
+  DISALLOW_COPY_AND_ASSIGN(ClientDialogHandlerGtk);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_DIALOG_HANDLER_GTK_H_
diff --git a/src/tests/cefclient/browser/dialog_test.cc b/src/tests/cefclient/browser/dialog_test.cc
new file mode 100644
index 0000000..030cedc
--- /dev/null
+++ b/src/tests/cefclient/browser/dialog_test.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/dialog_test.h"
+
+#include <string>
+
+#include "include/cef_browser.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/browser/file_util.h"
+
+namespace client {
+namespace dialog_test {
+
+namespace {
+
+const char kTestUrlPath[] = "/dialogs";
+const char kFileOpenMessageName[] = "DialogTest.FileOpen";
+const char kFileOpenMultipleMessageName[] = "DialogTest.FileOpenMultiple";
+const char kFileOpenFolderMessageName[] = "DialogTest.FileOpenFolder";
+const char kFileSaveMessageName[] = "DialogTest.FileSave";
+
+// Store persistent dialog state information.
+class DialogState : public base::RefCountedThreadSafe<DialogState> {
+ public:
+  DialogState()
+      : mode_(FILE_DIALOG_OPEN), last_selected_filter_(0), pending_(false) {}
+
+  cef_file_dialog_mode_t mode_;
+  int last_selected_filter_;
+  CefString last_file_;
+  bool pending_;
+
+  DISALLOW_COPY_AND_ASSIGN(DialogState);
+};
+
+// Callback executed when the file dialog is dismissed.
+class DialogCallback : public CefRunFileDialogCallback {
+ public:
+  DialogCallback(
+      CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback,
+      scoped_refptr<DialogState> dialog_state)
+      : router_callback_(router_callback), dialog_state_(dialog_state) {}
+
+  virtual void OnFileDialogDismissed(
+      int last_selected_filter,
+      const std::vector<CefString>& file_paths) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    DCHECK(dialog_state_->pending_);
+
+    if (!file_paths.empty()) {
+      if (dialog_state_->mode_ != FILE_DIALOG_OPEN_FOLDER)
+        dialog_state_->last_selected_filter_ = last_selected_filter;
+
+      dialog_state_->last_file_ = file_paths[0];
+      if (dialog_state_->mode_ == FILE_DIALOG_OPEN_FOLDER) {
+        std::string last_file = dialog_state_->last_file_;
+        if (last_file[last_file.length() - 1] != file_util::kPathSep) {
+          // Add a trailing slash so we know it's a directory. Otherwise, file
+          // dialogs will think the last path component is a file name.
+          last_file += file_util::kPathSep;
+          dialog_state_->last_file_ = last_file;
+        }
+      }
+    }
+
+    // Send a message back to the render process with the list of file paths.
+    std::string response;
+    for (int i = 0; i < static_cast<int>(file_paths.size()); ++i) {
+      if (!response.empty())
+        response += "|";  // Use a delimiter disallowed in file paths.
+      response += file_paths[i];
+    }
+
+    router_callback_->Success(response);
+    router_callback_ = nullptr;
+
+    dialog_state_->pending_ = false;
+    dialog_state_ = nullptr;
+  }
+
+ private:
+  CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback_;
+  scoped_refptr<DialogState> dialog_state_;
+
+  IMPLEMENT_REFCOUNTING(DialogCallback);
+  DISALLOW_COPY_AND_ASSIGN(DialogCallback);
+};
+
+// Handle messages in the browser process.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  Handler() {}
+
+  // Called due to cefQuery execution in dialogs.html.
+  virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id,
+                       const CefString& request,
+                       bool persistent,
+                       CefRefPtr<Callback> callback) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (!test_runner::IsTestURL(url, kTestUrlPath))
+      return false;
+
+    if (!dialog_state_.get())
+      dialog_state_ = new DialogState;
+
+    // Make sure we're only running one dialog at a time.
+    DCHECK(!dialog_state_->pending_);
+
+    std::vector<CefString> accept_filters;
+    std::string title;
+
+    const std::string& message_name = request;
+    if (message_name == kFileOpenMessageName) {
+      dialog_state_->mode_ = FILE_DIALOG_OPEN;
+      title = "My Open Dialog";
+    } else if (message_name == kFileOpenMultipleMessageName) {
+      dialog_state_->mode_ = FILE_DIALOG_OPEN_MULTIPLE;
+      title = "My Open Multiple Dialog";
+    } else if (message_name == kFileOpenFolderMessageName) {
+      dialog_state_->mode_ = FILE_DIALOG_OPEN_FOLDER;
+      title = "My Open Folder Dialog";
+    } else if (message_name == kFileSaveMessageName) {
+      dialog_state_->mode_ = static_cast<cef_file_dialog_mode_t>(
+          FILE_DIALOG_SAVE | FILE_DIALOG_OVERWRITEPROMPT_FLAG);
+      title = "My Save Dialog";
+    } else {
+      NOTREACHED();
+      return true;
+    }
+
+    if (dialog_state_->mode_ != FILE_DIALOG_OPEN_FOLDER) {
+      // Build filters based on mime time.
+      accept_filters.push_back("text/*");
+
+      // Build filters based on file extension.
+      accept_filters.push_back(".log");
+      accept_filters.push_back(".patch");
+
+      // Add specific filters as-is.
+      accept_filters.push_back("Document Files|.doc;.odt");
+      accept_filters.push_back("Image Files|.png;.jpg;.gif");
+      accept_filters.push_back("PDF Files|.pdf");
+    }
+
+    dialog_state_->pending_ = true;
+
+    browser->GetHost()->RunFileDialog(
+        dialog_state_->mode_, title, dialog_state_->last_file_, accept_filters,
+        dialog_state_->last_selected_filter_,
+        new DialogCallback(callback, dialog_state_));
+
+    return true;
+  }
+
+ private:
+  scoped_refptr<DialogState> dialog_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(Handler);
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace dialog_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/dialog_test.h b/src/tests/cefclient/browser/dialog_test.h
new file mode 100644
index 0000000..2044594
--- /dev/null
+++ b/src/tests/cefclient/browser/dialog_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_DIALOG_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_DIALOG_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace dialog_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace dialog_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_DIALOG_TEST_H_
diff --git a/src/tests/cefclient/browser/drm_test.cc b/src/tests/cefclient/browser/drm_test.cc
new file mode 100644
index 0000000..75632bd
--- /dev/null
+++ b/src/tests/cefclient/browser/drm_test.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/drm_test.h"
+
+#include <algorithm>
+#include <string>
+
+#include "include/cef_parser.h"
+#include "include/cef_web_plugin.h"
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace drm_test {
+
+namespace {
+
+// Application-specific error codes.
+const int kMessageFormatError = 1;
+const int kCdmLoadError = 2;
+
+const char kTestUrlPath[] = "/drm";
+const char kWidevineCdmPathKey[] = "widevine_cdm_path";
+
+// Callback executed once CDM registration is complete.
+class CdmCallback : public CefRegisterCdmCallback {
+ public:
+  CdmCallback(CefRefPtr<CefMessageRouterBrowserSide::Callback> callback)
+      : callback_(callback) {}
+
+  void OnCdmRegistrationComplete(cef_cdm_registration_error_t result,
+                                 const CefString& error_message) OVERRIDE {
+    if (result == CEF_CDM_REGISTRATION_ERROR_NONE)
+      callback_->Success("");
+    else
+      callback_->Failure(kCdmLoadError, error_message);
+    callback_ = nullptr;
+  }
+
+ private:
+  CefRefPtr<CefMessageRouterBrowserSide::Callback> callback_;
+
+  IMPLEMENT_REFCOUNTING(CdmCallback);
+  DISALLOW_COPY_AND_ASSIGN(CdmCallback);
+};
+
+// Handle messages in the browser process.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  Handler() {}
+
+  // Called due to cefQuery execution in drm.html.
+  virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id,
+                       const CefString& request,
+                       bool persistent,
+                       CefRefPtr<Callback> callback) OVERRIDE {
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (!test_runner::IsTestURL(url, kTestUrlPath))
+      return false;
+
+    // Parse |request| as a JSON dictionary.
+    CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
+    if (!request_dict) {
+      callback->Failure(kMessageFormatError, "Incorrect message format");
+      return true;
+    }
+
+    // Verify the "widevine_cdm_path" key.
+    if (!VerifyKey(request_dict, kWidevineCdmPathKey, VTYPE_STRING, callback))
+      return true;
+
+    const std::string& widevine_cdm_path =
+        request_dict->GetString(kWidevineCdmPathKey);
+    if (widevine_cdm_path.empty()) {
+      callback->Failure(kMessageFormatError, "Empty widevine CDM path");
+      return true;
+    }
+
+    // Register the Widvine CDM.
+    CefRegisterWidevineCdm(widevine_cdm_path, new CdmCallback(callback));
+    return true;
+  }
+
+ private:
+  // Convert a JSON string to a dictionary value.
+  static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
+    CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
+    if (value.get() && value->GetType() == VTYPE_DICTIONARY)
+      return value->GetDictionary();
+    return nullptr;
+  }
+
+  // Verify that |key| exists in |dictionary| and has type |value_type|. Fails
+  // |callback| and returns false on failure.
+  static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
+                        const char* key,
+                        cef_value_type_t value_type,
+                        CefRefPtr<Callback> callback) {
+    if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
+      callback->Failure(
+          kMessageFormatError,
+          "Missing or incorrectly formatted message key: " + std::string(key));
+      return false;
+    }
+    return true;
+  }
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace drm_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/drm_test.h b/src/tests/cefclient/browser/drm_test.h
new file mode 100644
index 0000000..e8afa3f
--- /dev/null
+++ b/src/tests/cefclient/browser/drm_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_DRM_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_DRM_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace drm_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace drm_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_DRM_TEST_H_
diff --git a/src/tests/cefclient/browser/image_cache.cc b/src/tests/cefclient/browser/image_cache.cc
new file mode 100644
index 0000000..c2c4105
--- /dev/null
+++ b/src/tests/cefclient/browser/image_cache.cc
@@ -0,0 +1,306 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/image_cache.h"
+
+#include "tests/shared/browser/file_util.h"
+#include "tests/shared/browser/resource_util.h"
+
+namespace client {
+
+namespace {
+
+const char kEmptyId[] = "__empty";
+
+}  // namespace
+
+ImageCache::ImageCache() {}
+
+ImageCache::~ImageCache() {
+  CEF_REQUIRE_UI_THREAD();
+}
+
+ImageCache::ImageRep::ImageRep(const std::string& path, float scale_factor)
+    : path_(path), scale_factor_(scale_factor) {
+  DCHECK(!path_.empty());
+  DCHECK_GT(scale_factor_, 0.0f);
+}
+
+ImageCache::ImageInfo::ImageInfo(const std::string& id,
+                                 const ImageRepSet& reps,
+                                 bool internal,
+                                 bool force_reload)
+    : id_(id), reps_(reps), internal_(internal), force_reload_(force_reload) {
+#ifndef NDEBUG
+  DCHECK(!id_.empty());
+  if (id_ != kEmptyId)
+    DCHECK(!reps_.empty());
+#endif
+}
+
+// static
+ImageCache::ImageInfo ImageCache::ImageInfo::Empty() {
+  return ImageInfo(kEmptyId, ImageRepSet(), true, false);
+}
+
+// static
+ImageCache::ImageInfo ImageCache::ImageInfo::Create1x(
+    const std::string& id,
+    const std::string& path_1x,
+    bool internal) {
+  ImageRepSet reps;
+  reps.push_back(ImageRep(path_1x, 1.0f));
+  return ImageInfo(id, reps, internal, false);
+}
+
+// static
+ImageCache::ImageInfo ImageCache::ImageInfo::Create2x(
+    const std::string& id,
+    const std::string& path_1x,
+    const std::string& path_2x,
+    bool internal) {
+  ImageRepSet reps;
+  reps.push_back(ImageRep(path_1x, 1.0f));
+  reps.push_back(ImageRep(path_2x, 2.0f));
+  return ImageInfo(id, reps, internal, false);
+}
+
+// static
+ImageCache::ImageInfo ImageCache::ImageInfo::Create2x(const std::string& id) {
+  return Create2x(id, id + ".1x.png", id + ".2x.png", true);
+}
+
+struct ImageCache::ImageContent {
+  ImageContent() {}
+
+  struct RepContent {
+    RepContent(ImageType type, float scale_factor, const std::string& contents)
+        : type_(type), scale_factor_(scale_factor), contents_(contents) {}
+
+    ImageType type_;
+    float scale_factor_;
+    std::string contents_;
+  };
+  typedef std::vector<RepContent> RepContentSet;
+  RepContentSet contents_;
+
+  CefRefPtr<CefImage> image_;
+};
+
+void ImageCache::LoadImages(const ImageInfoSet& image_info,
+                            const LoadImagesCallback& callback) {
+  DCHECK(!image_info.empty());
+  DCHECK(!callback.is_null());
+
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(&ImageCache::LoadImages, this, image_info,
+                                   callback));
+    return;
+  }
+
+  ImageSet images;
+  bool missing_images = false;
+
+  ImageInfoSet::const_iterator it = image_info.begin();
+  for (; it != image_info.end(); ++it) {
+    const ImageInfo& info = *it;
+
+    if (info.id_ == kEmptyId) {
+      // Image intentionally left empty.
+      images.push_back(nullptr);
+      continue;
+    }
+
+    ImageMap::iterator it2 = image_map_.find(info.id_);
+    if (it2 != image_map_.end()) {
+      if (!info.force_reload_) {
+        // Image already exists.
+        images.push_back(it2->second);
+        continue;
+      }
+
+      // Remove the existing image from the map.
+      image_map_.erase(it2);
+    }
+
+    // Load the image.
+    images.push_back(nullptr);
+    if (!missing_images)
+      missing_images = true;
+  }
+
+  if (missing_images) {
+    CefPostTask(TID_FILE, base::Bind(&ImageCache::LoadMissing, this, image_info,
+                                     images, callback));
+  } else {
+    callback.Run(images);
+  }
+}
+
+CefRefPtr<CefImage> ImageCache::GetCachedImage(const std::string& image_id) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(!image_id.empty());
+
+  ImageMap::const_iterator it = image_map_.find(image_id);
+  if (it != image_map_.end())
+    return it->second;
+
+  return nullptr;
+}
+
+// static
+ImageCache::ImageType ImageCache::GetImageType(const std::string& path) {
+  std::string ext = file_util::GetFileExtension(path);
+  if (ext.empty())
+    return TYPE_NONE;
+
+  std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
+  if (ext == "png")
+    return TYPE_PNG;
+  if (ext == "jpg" || ext == "jpeg")
+    return TYPE_JPEG;
+
+  return TYPE_NONE;
+}
+
+void ImageCache::LoadMissing(const ImageInfoSet& image_info,
+                             const ImageSet& images,
+                             const LoadImagesCallback& callback) {
+  CEF_REQUIRE_FILE_THREAD();
+
+  DCHECK_EQ(image_info.size(), images.size());
+
+  ImageContentSet contents;
+
+  ImageInfoSet::const_iterator it1 = image_info.begin();
+  ImageSet::const_iterator it2 = images.begin();
+  for (; it1 != image_info.end() && it2 != images.end(); ++it1, ++it2) {
+    const ImageInfo& info = *it1;
+    ImageContent content;
+    if (*it2 || info.id_ == kEmptyId) {
+      // Image already exists or is intentionally empty.
+      content.image_ = *it2;
+    } else {
+      LoadImageContents(info, &content);
+    }
+    contents.push_back(content);
+  }
+
+  CefPostTask(TID_UI, base::Bind(&ImageCache::UpdateCache, this, image_info,
+                                 contents, callback));
+}
+
+// static
+bool ImageCache::LoadImageContents(const ImageInfo& info,
+                                   ImageContent* content) {
+  CEF_REQUIRE_FILE_THREAD();
+
+  ImageRepSet::const_iterator it = info.reps_.begin();
+  for (; it != info.reps_.end(); ++it) {
+    const ImageRep& rep = *it;
+    ImageType rep_type;
+    std::string rep_contents;
+    if (!LoadImageContents(rep.path_, info.internal_, &rep_type,
+                           &rep_contents)) {
+      LOG(ERROR) << "Failed to load image " << info.id_ << " from path "
+                 << rep.path_;
+      return false;
+    }
+    content->contents_.push_back(
+        ImageContent::RepContent(rep_type, rep.scale_factor_, rep_contents));
+  }
+
+  return true;
+}
+
+// static
+bool ImageCache::LoadImageContents(const std::string& path,
+                                   bool internal,
+                                   ImageType* type,
+                                   std::string* contents) {
+  CEF_REQUIRE_FILE_THREAD();
+
+  *type = GetImageType(path);
+  if (*type == TYPE_NONE)
+    return false;
+
+  if (internal) {
+    if (!LoadBinaryResource(path.c_str(), *contents))
+      return false;
+  } else if (!file_util::ReadFileToString(path, contents)) {
+    return false;
+  }
+
+  return !contents->empty();
+}
+
+void ImageCache::UpdateCache(const ImageInfoSet& image_info,
+                             const ImageContentSet& contents,
+                             const LoadImagesCallback& callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  DCHECK_EQ(image_info.size(), contents.size());
+
+  ImageSet images;
+
+  ImageInfoSet::const_iterator it1 = image_info.begin();
+  ImageContentSet::const_iterator it2 = contents.begin();
+  for (; it1 != image_info.end() && it2 != contents.end(); ++it1, ++it2) {
+    const ImageInfo& info = *it1;
+    const ImageContent& content = *it2;
+    if (content.image_ || info.id_ == kEmptyId) {
+      // Image already exists or is intentionally empty.
+      images.push_back(content.image_);
+    } else {
+      CefRefPtr<CefImage> image = CreateImage(info.id_, content);
+      images.push_back(image);
+
+      // Add the image to the map.
+      image_map_.insert(std::make_pair(info.id_, image));
+    }
+  }
+
+  callback.Run(images);
+}
+
+// static
+CefRefPtr<CefImage> ImageCache::CreateImage(const std::string& image_id,
+                                            const ImageContent& content) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Shouldn't be creating an image if one already exists.
+  DCHECK(!content.image_);
+
+  if (content.contents_.empty())
+    return nullptr;
+
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+
+  ImageContent::RepContentSet::const_iterator it = content.contents_.begin();
+  for (; it != content.contents_.end(); ++it) {
+    const ImageContent::RepContent& rep = *it;
+    if (rep.type_ == TYPE_PNG) {
+      if (!image->AddPNG(rep.scale_factor_, rep.contents_.c_str(),
+                         rep.contents_.size())) {
+        LOG(ERROR) << "Failed to create image " << image_id << " for PNG@"
+                   << rep.scale_factor_;
+        return nullptr;
+      }
+    } else if (rep.type_ == TYPE_JPEG) {
+      if (!image->AddJPEG(rep.scale_factor_, rep.contents_.c_str(),
+                          rep.contents_.size())) {
+        LOG(ERROR) << "Failed to create image " << image_id << " for JPG@"
+                   << rep.scale_factor_;
+        return nullptr;
+      }
+    } else {
+      NOTREACHED();
+      return nullptr;
+    }
+  }
+
+  return image;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/image_cache.h b/src/tests/cefclient/browser/image_cache.h
new file mode 100644
index 0000000..fa43c8d
--- /dev/null
+++ b/src/tests/cefclient/browser/image_cache.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_IMAGE_CACHE_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_IMAGE_CACHE_H_
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/cef_image.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+
+namespace client {
+
+// Simple image caching implementation.
+class ImageCache
+    : public base::RefCountedThreadSafe<ImageCache, CefDeleteOnUIThread> {
+ public:
+  ImageCache();
+
+  // Image representation at a specific scale factor.
+  struct ImageRep {
+    ImageRep(const std::string& path, float scale_factor);
+
+    // Full file system path.
+    std::string path_;
+
+    // Image scale factor (usually 1.0f or 2.0f).
+    float scale_factor_;
+  };
+  typedef std::vector<ImageRep> ImageRepSet;
+
+  // Unique image that may have multiple representations.
+  struct ImageInfo {
+    ImageInfo(const std::string& id,
+              const ImageRepSet& reps,
+              bool internal,
+              bool force_reload);
+
+    // Helper for returning an empty image.
+    static ImageInfo Empty();
+
+    // Helpers for creating common representations.
+    static ImageInfo Create1x(const std::string& id,
+                              const std::string& path_1x,
+                              bool internal);
+    static ImageInfo Create2x(const std::string& id,
+                              const std::string& path_1x,
+                              const std::string& path_2x,
+                              bool internal);
+    static ImageInfo Create2x(const std::string& id);
+
+    // Image unique ID.
+    std::string id_;
+
+    // Image representations to load.
+    ImageRepSet reps_;
+
+    // True if the image is internal (loaded via LoadBinaryResource).
+    bool internal_;
+
+    // True to force reload.
+    bool force_reload_;
+  };
+  typedef std::vector<ImageInfo> ImageInfoSet;
+
+  typedef std::vector<CefRefPtr<CefImage>> ImageSet;
+
+  typedef base::Callback<void(const ImageSet& /*images*/)> LoadImagesCallback;
+
+  // Loads the images represented by |image_info|. Executes |callback|
+  // either synchronously or asychronously on the UI thread after completion.
+  void LoadImages(const ImageInfoSet& image_info,
+                  const LoadImagesCallback& callback);
+
+  // Returns an image that has already been cached. Must be called on the
+  // UI thread.
+  CefRefPtr<CefImage> GetCachedImage(const std::string& image_id);
+
+ private:
+  // Only allow deletion via scoped_refptr.
+  friend struct CefDeleteOnThread<TID_UI>;
+  friend class base::RefCountedThreadSafe<ImageCache, CefDeleteOnUIThread>;
+
+  ~ImageCache();
+
+  enum ImageType {
+    TYPE_NONE,
+    TYPE_PNG,
+    TYPE_JPEG,
+  };
+
+  static ImageType GetImageType(const std::string& path);
+
+  struct ImageContent;
+  typedef std::vector<ImageContent> ImageContentSet;
+
+  // Load missing image contents on the FILE thread.
+  void LoadMissing(const ImageInfoSet& image_info,
+                   const ImageSet& images,
+                   const LoadImagesCallback& callback);
+  static bool LoadImageContents(const ImageInfo& info, ImageContent* content);
+  static bool LoadImageContents(const std::string& path,
+                                bool internal,
+                                ImageType* type,
+                                std::string* contents);
+
+  // Create missing CefImage representations on the UI thread.
+  void UpdateCache(const ImageInfoSet& image_info,
+                   const ImageContentSet& contents,
+                   const LoadImagesCallback& callback);
+  static CefRefPtr<CefImage> CreateImage(const std::string& image_id,
+                                         const ImageContent& content);
+
+  // Map image ID to image representation. Only accessed on the UI thread.
+  typedef std::map<std::string, CefRefPtr<CefImage>> ImageMap;
+  ImageMap image_map_;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_IMAGE_CACHE_H_
diff --git a/src/tests/cefclient/browser/main_context.cc b/src/tests/cefclient/browser/main_context.cc
new file mode 100644
index 0000000..11c455c
--- /dev/null
+++ b/src/tests/cefclient/browser/main_context.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/main_context.h"
+
+#include "include/base/cef_logging.h"
+
+namespace client {
+
+namespace {
+
+MainContext* g_main_context = nullptr;
+
+}  // namespace
+
+// static
+MainContext* MainContext::Get() {
+  DCHECK(g_main_context);
+  return g_main_context;
+}
+
+MainContext::MainContext() {
+  DCHECK(!g_main_context);
+  g_main_context = this;
+}
+
+MainContext::~MainContext() {
+  g_main_context = nullptr;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/main_context.h b/src/tests/cefclient/browser/main_context.h
new file mode 100644
index 0000000..780bf1e
--- /dev/null
+++ b/src/tests/cefclient/browser/main_context.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_MAIN_CONTEXT_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_MAIN_CONTEXT_H_
+#pragma once
+
+#include <string>
+
+#include "include/base/cef_ref_counted.h"
+#include "include/internal/cef_types_wrappers.h"
+#include "tests/cefclient/browser/osr_renderer_settings.h"
+
+namespace client {
+
+class RootWindowManager;
+
+// Used to store global context in the browser process. The methods of this
+// class are thread-safe unless otherwise indicated.
+class MainContext {
+ public:
+  // Returns the singleton instance of this object.
+  static MainContext* Get();
+
+  // Returns the full path to the console log file.
+  virtual std::string GetConsoleLogPath() = 0;
+
+  // Returns the full path to |file_name|.
+  virtual std::string GetDownloadPath(const std::string& file_name) = 0;
+
+  // Returns the app working directory including trailing path separator.
+  virtual std::string GetAppWorkingDirectory() = 0;
+
+  // Returns the main application URL.
+  virtual std::string GetMainURL() = 0;
+
+  // Returns the background color.
+  virtual cef_color_t GetBackgroundColor() = 0;
+
+  // Returns true if the Views framework will be used.
+  virtual bool UseViews() = 0;
+
+  // Returns true if windowless (off-screen) rendering will be used.
+  virtual bool UseWindowlessRendering() = 0;
+
+  // Returns true if touch events are enabled.
+  virtual bool TouchEventsEnabled() = 0;
+
+  // Populate |settings| based on command-line arguments.
+  virtual void PopulateSettings(CefSettings* settings) = 0;
+  virtual void PopulateBrowserSettings(CefBrowserSettings* settings) = 0;
+  virtual void PopulateOsrSettings(OsrRendererSettings* settings) = 0;
+
+  // Returns the object used to create/manage RootWindow instances.
+  virtual RootWindowManager* GetRootWindowManager() = 0;
+
+ protected:
+  MainContext();
+  virtual ~MainContext();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MainContext);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_MAIN_CONTEXT_H_
diff --git a/src/tests/cefclient/browser/main_context_impl.cc b/src/tests/cefclient/browser/main_context_impl.cc
new file mode 100644
index 0000000..ef293bf
--- /dev/null
+++ b/src/tests/cefclient/browser/main_context_impl.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/main_context_impl.h"
+
+#include "include/cef_parser.h"
+#include "include/cef_web_plugin.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+
+namespace {
+
+// The default URL to load in a browser window.
+const char kDefaultUrl[] = "http://www.google.com";
+
+// Returns the ARGB value for |color|.
+cef_color_t ParseColor(const std::string& color) {
+  std::string colorToLower;
+  colorToLower.resize(color.size());
+  std::transform(color.begin(), color.end(), colorToLower.begin(), ::tolower);
+
+  if (colorToLower == "black")
+    return CefColorSetARGB(255, 0, 0, 0);
+  else if (colorToLower == "blue")
+    return CefColorSetARGB(255, 0, 0, 255);
+  else if (colorToLower == "green")
+    return CefColorSetARGB(255, 0, 255, 0);
+  else if (colorToLower == "red")
+    return CefColorSetARGB(255, 255, 0, 0);
+  else if (colorToLower == "white")
+    return CefColorSetARGB(255, 255, 255, 255);
+
+  // Use the default color.
+  return 0;
+}
+
+}  // namespace
+
+MainContextImpl::MainContextImpl(CefRefPtr<CefCommandLine> command_line,
+                                 bool terminate_when_all_windows_closed)
+    : command_line_(command_line),
+      terminate_when_all_windows_closed_(terminate_when_all_windows_closed),
+      initialized_(false),
+      shutdown_(false),
+      background_color_(0),
+      browser_background_color_(0),
+      windowless_frame_rate_(0),
+      use_views_(false) {
+  DCHECK(command_line_.get());
+
+  // Set the main URL.
+  if (command_line_->HasSwitch(switches::kUrl))
+    main_url_ = command_line_->GetSwitchValue(switches::kUrl);
+  if (main_url_.empty())
+    main_url_ = kDefaultUrl;
+
+  // Whether windowless (off-screen) rendering will be used.
+  use_windowless_rendering_ =
+      command_line_->HasSwitch(switches::kOffScreenRenderingEnabled);
+
+  if (use_windowless_rendering_ &&
+      command_line_->HasSwitch(switches::kOffScreenFrameRate)) {
+    windowless_frame_rate_ =
+        atoi(command_line_->GetSwitchValue(switches::kOffScreenFrameRate)
+                 .ToString()
+                 .c_str());
+  }
+
+  // Whether transparent painting is used with windowless rendering.
+  const bool use_transparent_painting =
+      use_windowless_rendering_ &&
+      command_line_->HasSwitch(switches::kTransparentPaintingEnabled);
+
+#if defined(OS_WIN)
+  // Shared texture is only supported on Windows.
+  shared_texture_enabled_ =
+      use_windowless_rendering_ &&
+      command_line_->HasSwitch(switches::kSharedTextureEnabled);
+#endif
+
+  external_begin_frame_enabled_ =
+      use_windowless_rendering_ &&
+      command_line_->HasSwitch(switches::kExternalBeginFrameEnabled);
+
+  if (windowless_frame_rate_ <= 0) {
+// Choose a reasonable default rate based on the OSR mode.
+#if defined(OS_WIN)
+    windowless_frame_rate_ = shared_texture_enabled_ ? 60 : 30;
+#else
+    windowless_frame_rate_ = 30;
+#endif
+  }
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+  // Whether the Views framework will be used.
+  use_views_ = command_line_->HasSwitch(switches::kUseViews);
+
+  if (use_windowless_rendering_ && use_views_) {
+    LOG(ERROR)
+        << "Windowless rendering is not supported by the Views framework.";
+    use_views_ = false;
+  }
+
+  if (use_views_ && command_line->HasSwitch(switches::kHideFrame) &&
+      !command_line_->HasSwitch(switches::kUrl)) {
+    // Use the draggable regions test as the default URL for frameless windows.
+    main_url_ = "http://tests/draggable";
+  }
+#endif  // defined(OS_WIN) || defined(OS_LINUX)
+
+  if (command_line_->HasSwitch(switches::kBackgroundColor)) {
+    // Parse the background color value.
+    background_color_ =
+        ParseColor(command_line_->GetSwitchValue(switches::kBackgroundColor));
+  }
+
+  if (background_color_ == 0 && !use_views_) {
+    // Set an explicit background color.
+    background_color_ = CefColorSetARGB(255, 255, 255, 255);
+  }
+
+  // |browser_background_color_| should remain 0 to enable transparent painting.
+  if (!use_transparent_painting) {
+    browser_background_color_ = background_color_;
+  }
+
+  const std::string& cdm_path =
+      command_line_->GetSwitchValue(switches::kWidevineCdmPath);
+  if (!cdm_path.empty()) {
+    // Register the Widevine CDM at the specified path. See comments in
+    // cef_web_plugin.h for details. It's safe to call this method before
+    // CefInitialize(), and calling it before CefInitialize() is required on
+    // Linux.
+    CefRegisterWidevineCdm(cdm_path, nullptr);
+  }
+}
+
+MainContextImpl::~MainContextImpl() {
+  // The context must either not have been initialized, or it must have also
+  // been shut down.
+  DCHECK(!initialized_ || shutdown_);
+}
+
+std::string MainContextImpl::GetConsoleLogPath() {
+  return GetAppWorkingDirectory() + "console.log";
+}
+
+std::string MainContextImpl::GetMainURL() {
+  return main_url_;
+}
+
+cef_color_t MainContextImpl::GetBackgroundColor() {
+  return background_color_;
+}
+
+bool MainContextImpl::UseViews() {
+  return use_views_;
+}
+
+bool MainContextImpl::UseWindowlessRendering() {
+  return use_windowless_rendering_;
+}
+
+bool MainContextImpl::TouchEventsEnabled() {
+  return command_line_->GetSwitchValue("touch-events") == "enabled";
+}
+
+void MainContextImpl::PopulateSettings(CefSettings* settings) {
+#if defined(OS_WIN) || defined(OS_LINUX)
+  settings->multi_threaded_message_loop =
+      command_line_->HasSwitch(switches::kMultiThreadedMessageLoop);
+#endif
+
+  if (!settings->multi_threaded_message_loop) {
+    settings->external_message_pump =
+        command_line_->HasSwitch(switches::kExternalMessagePump);
+  }
+
+  CefString(&settings->cache_path) =
+      command_line_->GetSwitchValue(switches::kCachePath);
+
+  if (use_windowless_rendering_)
+    settings->windowless_rendering_enabled = true;
+
+  if (browser_background_color_ != 0)
+    settings->background_color = browser_background_color_;
+}
+
+void MainContextImpl::PopulateBrowserSettings(CefBrowserSettings* settings) {
+  settings->windowless_frame_rate = windowless_frame_rate_;
+
+  if (browser_background_color_ != 0)
+    settings->background_color = browser_background_color_;
+}
+
+void MainContextImpl::PopulateOsrSettings(OsrRendererSettings* settings) {
+  settings->show_update_rect =
+      command_line_->HasSwitch(switches::kShowUpdateRect);
+
+#if defined(OS_WIN)
+  settings->shared_texture_enabled = shared_texture_enabled_;
+#endif
+  settings->external_begin_frame_enabled = external_begin_frame_enabled_;
+  settings->begin_frame_rate = windowless_frame_rate_;
+
+  if (browser_background_color_ != 0)
+    settings->background_color = browser_background_color_;
+}
+
+RootWindowManager* MainContextImpl::GetRootWindowManager() {
+  DCHECK(InValidState());
+  return root_window_manager_.get();
+}
+
+bool MainContextImpl::Initialize(const CefMainArgs& args,
+                                 const CefSettings& settings,
+                                 CefRefPtr<CefApp> application,
+                                 void* windows_sandbox_info) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!initialized_);
+  DCHECK(!shutdown_);
+
+  if (!CefInitialize(args, settings, application, windows_sandbox_info))
+    return false;
+
+  // Need to create the RootWindowManager after calling CefInitialize because
+  // TempWindowX11 uses cef_get_xdisplay().
+  root_window_manager_.reset(
+      new RootWindowManager(terminate_when_all_windows_closed_));
+
+  initialized_ = true;
+
+  return true;
+}
+
+void MainContextImpl::Shutdown() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(initialized_);
+  DCHECK(!shutdown_);
+
+  root_window_manager_.reset();
+
+  CefShutdown();
+
+  shutdown_ = true;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/main_context_impl.h b/src/tests/cefclient/browser/main_context_impl.h
new file mode 100644
index 0000000..4c267f3
--- /dev/null
+++ b/src/tests/cefclient/browser/main_context_impl.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_MAIN_CONTEXT_IMPL_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_MAIN_CONTEXT_IMPL_H_
+#pragma once
+
+#include "include/base/cef_scoped_ptr.h"
+#include "include/base/cef_thread_checker.h"
+#include "include/cef_app.h"
+#include "include/cef_command_line.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/root_window_manager.h"
+
+namespace client {
+
+// Used to store global context in the browser process.
+class MainContextImpl : public MainContext {
+ public:
+  MainContextImpl(CefRefPtr<CefCommandLine> command_line,
+                  bool terminate_when_all_windows_closed);
+
+  // MainContext members.
+  std::string GetConsoleLogPath() OVERRIDE;
+  std::string GetDownloadPath(const std::string& file_name) OVERRIDE;
+  std::string GetAppWorkingDirectory() OVERRIDE;
+  std::string GetMainURL() OVERRIDE;
+  cef_color_t GetBackgroundColor() OVERRIDE;
+  bool UseViews() OVERRIDE;
+  bool UseWindowlessRendering() OVERRIDE;
+  bool TouchEventsEnabled() OVERRIDE;
+  void PopulateSettings(CefSettings* settings) OVERRIDE;
+  void PopulateBrowserSettings(CefBrowserSettings* settings) OVERRIDE;
+  void PopulateOsrSettings(OsrRendererSettings* settings) OVERRIDE;
+  RootWindowManager* GetRootWindowManager() OVERRIDE;
+
+  // Initialize CEF and associated main context state. This method must be
+  // called on the same thread that created this object.
+  bool Initialize(const CefMainArgs& args,
+                  const CefSettings& settings,
+                  CefRefPtr<CefApp> application,
+                  void* windows_sandbox_info);
+
+  // Shut down CEF and associated context state. This method must be called on
+  // the same thread that created this object.
+  void Shutdown();
+
+ private:
+  // Allow deletion via scoped_ptr only.
+  friend struct base::DefaultDeleter<MainContextImpl>;
+
+  ~MainContextImpl();
+
+  // Returns true if the context is in a valid state (initialized and not yet
+  // shut down).
+  bool InValidState() const { return initialized_ && !shutdown_; }
+
+  CefRefPtr<CefCommandLine> command_line_;
+  const bool terminate_when_all_windows_closed_;
+
+  // Track context state. Accessing these variables from multiple threads is
+  // safe because only a single thread will exist at the time that they're set
+  // (during context initialization and shutdown).
+  bool initialized_;
+  bool shutdown_;
+
+  std::string main_url_;
+  cef_color_t background_color_;
+  cef_color_t browser_background_color_;
+  bool use_windowless_rendering_;
+  int windowless_frame_rate_;
+  bool use_views_;
+  bool touch_events_enabled_;
+
+  scoped_ptr<RootWindowManager> root_window_manager_;
+
+#if defined(OS_WIN)
+  bool shared_texture_enabled_;
+#endif
+
+  bool external_begin_frame_enabled_;
+
+  // Used to verify that methods are called on the correct thread.
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(MainContextImpl);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_MAIN_CONTEXT_IMPL_H_
diff --git a/src/tests/cefclient/browser/main_context_impl_posix.cc b/src/tests/cefclient/browser/main_context_impl_posix.cc
new file mode 100644
index 0000000..8a13b33
--- /dev/null
+++ b/src/tests/cefclient/browser/main_context_impl_posix.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/main_context_impl.h"
+
+#include <unistd.h>
+
+namespace client {
+
+std::string MainContextImpl::GetDownloadPath(const std::string& file_name) {
+  return std::string();
+}
+
+std::string MainContextImpl::GetAppWorkingDirectory() {
+  char szWorkingDir[256];
+  if (getcwd(szWorkingDir, sizeof(szWorkingDir) - 1) == nullptr) {
+    szWorkingDir[0] = 0;
+  } else {
+    // Add trailing path separator.
+    size_t len = strlen(szWorkingDir);
+    szWorkingDir[len] = '/';
+    szWorkingDir[len + 1] = 0;
+  }
+  return szWorkingDir;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/main_context_impl_win.cc b/src/tests/cefclient/browser/main_context_impl_win.cc
new file mode 100644
index 0000000..0bbfbf2
--- /dev/null
+++ b/src/tests/cefclient/browser/main_context_impl_win.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/main_context_impl.h"
+
+#include <direct.h>
+#include <shlobj.h>
+
+namespace client {
+
+std::string MainContextImpl::GetDownloadPath(const std::string& file_name) {
+  TCHAR szFolderPath[MAX_PATH];
+  std::string path;
+
+  // Save the file in the user's "My Documents" folder.
+  if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL,
+                                0, szFolderPath))) {
+    path = CefString(szFolderPath);
+    path += "\\" + file_name;
+  }
+
+  return path;
+}
+
+std::string MainContextImpl::GetAppWorkingDirectory() {
+  char szWorkingDir[MAX_PATH + 1];
+  if (_getcwd(szWorkingDir, MAX_PATH) == nullptr) {
+    szWorkingDir[0] = 0;
+  } else {
+    // Add trailing path separator.
+    size_t len = strlen(szWorkingDir);
+    szWorkingDir[len] = '\\';
+    szWorkingDir[len + 1] = 0;
+  }
+  return szWorkingDir;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/main_message_loop_multithreaded_gtk.cc b/src/tests/cefclient/browser/main_message_loop_multithreaded_gtk.cc
new file mode 100644
index 0000000..d4a0443
--- /dev/null
+++ b/src/tests/cefclient/browser/main_message_loop_multithreaded_gtk.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/main_message_loop_multithreaded_gtk.h"
+
+#include <X11/Xlib.h>
+#include <gtk/gtkmain.h>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/wrapper/cef_closure_task.h"
+
+namespace client {
+
+namespace {
+
+base::Lock g_global_lock;
+base::PlatformThreadId g_global_lock_thread = kInvalidPlatformThreadId;
+
+void lock_enter() {
+  // The GDK lock is not reentrant, so check that we're using it correctly.
+  // See comments on ScopedGdkThreadsEnter.
+  base::PlatformThreadId current_thread = base::PlatformThread::CurrentId();
+  CHECK(current_thread != g_global_lock_thread);
+
+  g_global_lock.Acquire();
+  g_global_lock_thread = current_thread;
+}
+
+void lock_leave() {
+  g_global_lock_thread = kInvalidPlatformThreadId;
+  g_global_lock.Release();
+}
+
+// Same as g_idle_add() but specifying the GMainContext.
+guint idle_add(GMainContext* main_context,
+               GSourceFunc function,
+               gpointer data) {
+  GSource* source = g_idle_source_new();
+  g_source_set_callback(source, function, data, nullptr);
+  guint id = g_source_attach(source, main_context);
+  g_source_unref(source);
+  return id;
+}
+
+// Same as g_timeout_add() but specifying the GMainContext.
+guint timeout_add(GMainContext* main_context,
+                  guint interval,
+                  GSourceFunc function,
+                  gpointer data) {
+  GSource* source = g_timeout_source_new(interval);
+  g_source_set_callback(source, function, data, nullptr);
+  guint id = g_source_attach(source, main_context);
+  g_source_unref(source);
+  return id;
+}
+
+}  // namespace
+
+MainMessageLoopMultithreadedGtk::MainMessageLoopMultithreadedGtk()
+    : thread_id_(base::PlatformThread::CurrentId()) {
+  // Initialize Xlib support for concurrent threads. This function must be the
+  // first Xlib function a multi-threaded program calls, and it must complete
+  // before any other Xlib call is made.
+  CHECK(XInitThreads() != 0);
+
+  // Initialize GDK thread support. See comments on ScopedGdkThreadsEnter.
+  gdk_threads_set_lock_functions(lock_enter, lock_leave);
+  gdk_threads_init();
+}
+
+MainMessageLoopMultithreadedGtk::~MainMessageLoopMultithreadedGtk() {
+  DCHECK(RunsTasksOnCurrentThread());
+  DCHECK(queued_tasks_.empty());
+}
+
+int MainMessageLoopMultithreadedGtk::Run() {
+  DCHECK(RunsTasksOnCurrentThread());
+
+  // Chromium uses the default GLib context so we create our own context and
+  // make it the default for this thread.
+  main_context_ = g_main_context_new();
+  g_main_context_push_thread_default(main_context_);
+
+  main_loop_ = g_main_loop_new(main_context_, TRUE);
+
+  // Check the queue when GTK is idle, or at least every 100ms.
+  // TODO(cef): It might be more efficient to use input functions
+  // (gdk_input_add) and trigger by writing to an fd.
+  idle_add(main_context_, MainMessageLoopMultithreadedGtk::TriggerRunTasks,
+           this);
+  timeout_add(main_context_, 100,
+              MainMessageLoopMultithreadedGtk::TriggerRunTasks, this);
+
+  // Block until g_main_loop_quit().
+  g_main_loop_run(main_loop_);
+
+  // Release GLib resources.
+  g_main_loop_unref(main_loop_);
+  main_loop_ = nullptr;
+
+  g_main_context_pop_thread_default(main_context_);
+  g_main_context_unref(main_context_);
+  main_context_ = nullptr;
+
+  return 0;
+}
+
+void MainMessageLoopMultithreadedGtk::Quit() {
+  PostTask(CefCreateClosureTask(base::Bind(
+      &MainMessageLoopMultithreadedGtk::DoQuit, base::Unretained(this))));
+}
+
+void MainMessageLoopMultithreadedGtk::PostTask(CefRefPtr<CefTask> task) {
+  base::AutoLock lock_scope(lock_);
+
+  // Queue the task.
+  queued_tasks_.push(task);
+}
+
+bool MainMessageLoopMultithreadedGtk::RunsTasksOnCurrentThread() const {
+  return (thread_id_ == base::PlatformThread::CurrentId());
+}
+
+// static
+int MainMessageLoopMultithreadedGtk::TriggerRunTasks(void* self) {
+  static_cast<MainMessageLoopMultithreadedGtk*>(self)->RunTasks();
+  return G_SOURCE_CONTINUE;
+}
+
+void MainMessageLoopMultithreadedGtk::RunTasks() {
+  DCHECK(RunsTasksOnCurrentThread());
+
+  std::queue<CefRefPtr<CefTask>> tasks;
+
+  {
+    base::AutoLock lock_scope(lock_);
+    tasks.swap(queued_tasks_);
+  }
+
+  // Execute all queued tasks.
+  while (!tasks.empty()) {
+    CefRefPtr<CefTask> task = tasks.front();
+    tasks.pop();
+    task->Execute();
+  }
+}
+
+void MainMessageLoopMultithreadedGtk::DoQuit() {
+  DCHECK(RunsTasksOnCurrentThread());
+  g_main_loop_quit(main_loop_);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/main_message_loop_multithreaded_gtk.h b/src/tests/cefclient/browser/main_message_loop_multithreaded_gtk.h
new file mode 100644
index 0000000..51087fa
--- /dev/null
+++ b/src/tests/cefclient/browser/main_message_loop_multithreaded_gtk.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_MULTITHREADED_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_MULTITHREADED_GTK_H_
+#pragma once
+
+#include <queue>
+
+#include <gdk/gdk.h>
+
+#include "include/base/cef_lock.h"
+#include "include/base/cef_platform_thread.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+// Represents the main message loop in the browser process when using multi-
+// threaded message loop mode on Linux. In this mode there is no Chromium
+// message loop running on the main application thread. Instead, this
+// implementation utilizes a Glib context for running tasks.
+class MainMessageLoopMultithreadedGtk : public MainMessageLoop {
+ public:
+  MainMessageLoopMultithreadedGtk();
+  ~MainMessageLoopMultithreadedGtk();
+
+  // MainMessageLoop methods.
+  int Run() OVERRIDE;
+  void Quit() OVERRIDE;
+  void PostTask(CefRefPtr<CefTask> task) OVERRIDE;
+  bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ private:
+  static int TriggerRunTasks(void* self);
+  void RunTasks();
+  void DoQuit();
+
+  base::PlatformThreadId thread_id_;
+
+  GMainContext* main_context_;
+  GMainLoop* main_loop_;
+
+  base::Lock lock_;
+
+  // Must be protected by |lock_|.
+  std::queue<CefRefPtr<CefTask>> queued_tasks_;
+
+  DISALLOW_COPY_AND_ASSIGN(MainMessageLoopMultithreadedGtk);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_MULTITHREADED_GTK_H_
diff --git a/src/tests/cefclient/browser/main_message_loop_multithreaded_win.cc b/src/tests/cefclient/browser/main_message_loop_multithreaded_win.cc
new file mode 100644
index 0000000..39912ce
--- /dev/null
+++ b/src/tests/cefclient/browser/main_message_loop_multithreaded_win.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/main_message_loop_multithreaded_win.h"
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/cef_app.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+namespace {
+
+const wchar_t kWndClass[] = L"Client_MessageWindow";
+const wchar_t kTaskMessageName[] = L"Client_CustomTask";
+
+}  // namespace
+
+MainMessageLoopMultithreadedWin::MainMessageLoopMultithreadedWin()
+    : thread_id_(base::PlatformThread::CurrentId()),
+      task_message_id_(RegisterWindowMessage(kTaskMessageName)),
+      dialog_hwnd_(NULL),
+      message_hwnd_(NULL) {}
+
+MainMessageLoopMultithreadedWin::~MainMessageLoopMultithreadedWin() {
+  DCHECK(RunsTasksOnCurrentThread());
+  DCHECK(!message_hwnd_);
+  DCHECK(queued_tasks_.empty());
+}
+
+int MainMessageLoopMultithreadedWin::Run() {
+  DCHECK(RunsTasksOnCurrentThread());
+
+  HINSTANCE hInstance = ::GetModuleHandle(NULL);
+
+  {
+    base::AutoLock lock_scope(lock_);
+
+    // Create the hidden window for message processing.
+    message_hwnd_ = CreateMessageWindow(hInstance);
+    CHECK(message_hwnd_);
+
+    // Store a pointer to |this| in the window's user data.
+    SetUserDataPtr(message_hwnd_, this);
+
+    // Execute any tasks that are currently queued.
+    while (!queued_tasks_.empty()) {
+      PostTaskInternal(queued_tasks_.front());
+      queued_tasks_.pop();
+    }
+  }
+
+  HACCEL hAccelTable =
+      LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT));
+
+  MSG msg;
+
+  // Run the application message loop.
+  while (GetMessage(&msg, NULL, 0, 0)) {
+    // Allow processing of dialog messages.
+    if (dialog_hwnd_ && IsDialogMessage(dialog_hwnd_, &msg))
+      continue;
+
+    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
+    }
+  }
+
+  {
+    base::AutoLock lock_scope(lock_);
+
+    // Destroy the message window.
+    DestroyWindow(message_hwnd_);
+    message_hwnd_ = NULL;
+  }
+
+  return static_cast<int>(msg.wParam);
+}
+
+void MainMessageLoopMultithreadedWin::Quit() {
+  // Execute PostQuitMessage(0) on the main thread.
+  PostClosure(base::Bind(::PostQuitMessage, 0));
+}
+
+void MainMessageLoopMultithreadedWin::PostTask(CefRefPtr<CefTask> task) {
+  base::AutoLock lock_scope(lock_);
+  PostTaskInternal(task);
+}
+
+bool MainMessageLoopMultithreadedWin::RunsTasksOnCurrentThread() const {
+  return (thread_id_ == base::PlatformThread::CurrentId());
+}
+
+void MainMessageLoopMultithreadedWin::SetCurrentModelessDialog(
+    HWND hWndDialog) {
+  DCHECK(RunsTasksOnCurrentThread());
+
+#if DCHECK_IS_ON()
+  if (hWndDialog) {
+    // A new dialog reference should not be set while one is currently set.
+    DCHECK(!dialog_hwnd_);
+  }
+#endif
+  dialog_hwnd_ = hWndDialog;
+}
+
+// static
+HWND MainMessageLoopMultithreadedWin::CreateMessageWindow(HINSTANCE hInstance) {
+  WNDCLASSEX wc = {0};
+  wc.cbSize = sizeof(wc);
+  wc.lpfnWndProc = MessageWndProc;
+  wc.hInstance = hInstance;
+  wc.lpszClassName = kWndClass;
+  RegisterClassEx(&wc);
+
+  return CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance,
+                      0);
+}
+
+// static
+LRESULT CALLBACK
+MainMessageLoopMultithreadedWin::MessageWndProc(HWND hWnd,
+                                                UINT message,
+                                                WPARAM wParam,
+                                                LPARAM lParam) {
+  MainMessageLoopMultithreadedWin* self =
+      GetUserDataPtr<MainMessageLoopMultithreadedWin*>(hWnd);
+
+  if (self && message == self->task_message_id_) {
+    // Execute the task.
+    CefTask* task = reinterpret_cast<CefTask*>(wParam);
+    task->Execute();
+
+    // Release the reference added in PostTaskInternal. This will likely result
+    // in |task| being deleted.
+    task->Release();
+  } else {
+    switch (message) {
+      case WM_NCDESTROY:
+        // Clear the reference to |self|.
+        SetUserDataPtr(hWnd, NULL);
+        break;
+    }
+  }
+
+  return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+void MainMessageLoopMultithreadedWin::PostTaskInternal(
+    CefRefPtr<CefTask> task) {
+  lock_.AssertAcquired();
+
+  if (!message_hwnd_) {
+    // Queue the task until the message loop starts running.
+    queued_tasks_.push(task);
+    return;
+  }
+
+  // Add a reference that will be released in MessageWndProc.
+  task->AddRef();
+
+  // Post the task for execution by the message window.
+  PostMessage(message_hwnd_, task_message_id_,
+              reinterpret_cast<WPARAM>(task.get()), 0);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/main_message_loop_multithreaded_win.h b/src/tests/cefclient/browser/main_message_loop_multithreaded_win.h
new file mode 100644
index 0000000..7f04555
--- /dev/null
+++ b/src/tests/cefclient/browser/main_message_loop_multithreaded_win.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_MULTITHREADED_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_MULTITHREADED_WIN_H_
+#pragma once
+
+#include <windows.h>
+#include <queue>
+
+#include "include/base/cef_lock.h"
+#include "include/base/cef_platform_thread.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+// Represents the main message loop in the browser process when using multi-
+// threaded message loop mode on Windows. In this mode there is no Chromium
+// message loop running on the main application thread. Instead, this
+// implementation utilizes a hidden message window for running tasks.
+class MainMessageLoopMultithreadedWin : public MainMessageLoop {
+ public:
+  MainMessageLoopMultithreadedWin();
+  ~MainMessageLoopMultithreadedWin();
+
+  // MainMessageLoop methods.
+  int Run() OVERRIDE;
+  void Quit() OVERRIDE;
+  void PostTask(CefRefPtr<CefTask> task) OVERRIDE;
+  bool RunsTasksOnCurrentThread() const OVERRIDE;
+  void SetCurrentModelessDialog(HWND hWndDialog) OVERRIDE;
+
+ private:
+  // Create the message window.
+  static HWND CreateMessageWindow(HINSTANCE hInstance);
+
+  // Window procedure for the message window.
+  static LRESULT CALLBACK MessageWndProc(HWND hWnd,
+                                         UINT message,
+                                         WPARAM wParam,
+                                         LPARAM lParam);
+
+  void PostTaskInternal(CefRefPtr<CefTask> task);
+
+  base::PlatformThreadId thread_id_;
+  UINT task_message_id_;
+
+  // Only accessed on the main thread.
+  HWND dialog_hwnd_;
+
+  base::Lock lock_;
+
+  // Must be protected by |lock_|.
+  HWND message_hwnd_;
+  std::queue<CefRefPtr<CefTask>> queued_tasks_;
+
+  DISALLOW_COPY_AND_ASSIGN(MainMessageLoopMultithreadedWin);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_MAIN_MESSAGE_LOOP_MULTITHREADED_WIN_H_
diff --git a/src/tests/cefclient/browser/media_router_test.cc b/src/tests/cefclient/browser/media_router_test.cc
new file mode 100644
index 0000000..8fdfe1c
--- /dev/null
+++ b/src/tests/cefclient/browser/media_router_test.cc
@@ -0,0 +1,594 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/media_router_test.h"
+
+#include <string>
+#include <vector>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_media_router.h"
+#include "include/cef_parser.h"
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace media_router_test {
+
+namespace {
+
+const char kTestUrlPath[] = "/media_router";
+
+// Application-specific error codes.
+const int kMessageFormatError = 1;
+const int kRequestFailedError = 2;
+
+// Message strings.
+const char kNameKey[] = "name";
+const char kNameValueSubscribe[] = "subscribe";
+const char kNameValueCreateRoute[] = "createRoute";
+const char kNameValueTerminateRoute[] = "terminateRoute";
+const char kNameValueSendMessage[] = "sendMessage";
+const char kSourceKey[] = "source_urn";
+const char kSinkKey[] = "sink_id";
+const char kRouteKey[] = "route_id";
+const char kMessageKey[] = "message";
+const char kSuccessKey[] = "success";
+const char kPayloadKey[] = "payload";
+
+// Convert a dictionary value to a JSON string.
+CefString GetJSON(CefRefPtr<CefDictionaryValue> dictionary) {
+  CefRefPtr<CefValue> value = CefValue::Create();
+  value->SetDictionary(dictionary);
+  return CefWriteJSON(value, JSON_WRITER_DEFAULT);
+}
+
+typedef CefMessageRouterBrowserSide::Callback CallbackType;
+
+void SendSuccess(CefRefPtr<CallbackType> callback,
+                 CefRefPtr<CefDictionaryValue> result) {
+  callback->Success(GetJSON(result));
+}
+
+void SendFailure(CefRefPtr<CallbackType> callback,
+                 int error_code,
+                 const std::string& error_message) {
+  callback->Failure(error_code, error_message);
+}
+
+// Callback for CefMediaRouter::CreateRoute.
+class MediaRouteCreateCallback : public CefMediaRouteCreateCallback {
+ public:
+  explicit MediaRouteCreateCallback(CefRefPtr<CallbackType> create_callback)
+      : create_callback_(create_callback) {}
+
+  // CefMediaRouteCreateCallback method:
+  void OnMediaRouteCreateFinished(RouteCreateResult result,
+                                  const CefString& error,
+                                  CefRefPtr<CefMediaRoute> route) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    if (result == CEF_MRCR_OK) {
+      CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
+      dict->SetString(kRouteKey, route->GetId());
+      SendSuccess(create_callback_, dict);
+    } else {
+      SendFailure(create_callback_, kRequestFailedError + result, error);
+    }
+    create_callback_ = NULL;
+  }
+
+ private:
+  CefRefPtr<CallbackType> create_callback_;
+
+  IMPLEMENT_REFCOUNTING(MediaRouteCreateCallback);
+  DISALLOW_COPY_AND_ASSIGN(MediaRouteCreateCallback);
+};
+
+// Observes MediaRouter events. Only accessed on the UI thread.
+class MediaObserver : public CefMediaObserver {
+ public:
+  typedef std::vector<CefRefPtr<CefMediaRoute>> MediaRouteVector;
+  typedef std::vector<CefRefPtr<CefMediaSink>> MediaSinkVector;
+
+  MediaObserver(CefRefPtr<CefMediaRouter> media_router,
+                CefRefPtr<CallbackType> subscription_callback)
+      : media_router_(media_router),
+        subscription_callback_(subscription_callback),
+        next_sink_query_id_(0),
+        pending_sink_query_id_(-1),
+        pending_sink_callbacks_(0U) {}
+
+  ~MediaObserver() OVERRIDE { ClearSinkInfoMap(); }
+
+  bool CreateRoute(const std::string& source_urn,
+                   const std::string& sink_id,
+                   CefRefPtr<CallbackType> callback,
+                   std::string& error) {
+    CefRefPtr<CefMediaSource> source = GetSource(source_urn);
+    if (!source) {
+      error = "Invalid source: " + source_urn;
+      return false;
+    }
+
+    CefRefPtr<CefMediaSink> sink = GetSink(sink_id);
+    if (!sink) {
+      error = "Invalid sink: " + sink_id;
+      return false;
+    }
+
+    media_router_->CreateRoute(source, sink,
+                               new MediaRouteCreateCallback(callback));
+    return true;
+  }
+
+  bool TerminateRoute(const std::string& route_id, std::string& error) {
+    CefRefPtr<CefMediaRoute> route = GetRoute(route_id);
+    if (!route) {
+      error = "Invalid route: " + route_id;
+      return false;
+    }
+
+    route->Terminate();
+    return true;
+  }
+
+  bool SendRouteMessage(const std::string& route_id,
+                        const std::string& message,
+                        std::string& error) {
+    CefRefPtr<CefMediaRoute> route = GetRoute(route_id);
+    if (!route) {
+      error = "Invalid route: " + route_id;
+      return false;
+    }
+
+    route->SendRouteMessage(message.c_str(), message.size());
+    return true;
+  }
+
+ protected:
+  class DeviceInfoCallback : public CefMediaSinkDeviceInfoCallback {
+   public:
+    // Callback to be executed when the device info is available.
+    typedef base::Callback<void(const std::string& sink_id,
+                                const CefMediaSinkDeviceInfo& device_info)>
+        CallbackType;
+
+    DeviceInfoCallback(const std::string& sink_id, const CallbackType& callback)
+        : sink_id_(sink_id), callback_(callback) {}
+
+    void OnMediaSinkDeviceInfo(
+        const CefMediaSinkDeviceInfo& device_info) OVERRIDE {
+      CEF_REQUIRE_UI_THREAD();
+      callback_.Run(sink_id_, device_info);
+      callback_.Reset();
+    }
+
+   private:
+    const std::string sink_id_;
+    CallbackType callback_;
+
+    IMPLEMENT_REFCOUNTING(DeviceInfoCallback);
+    DISALLOW_COPY_AND_ASSIGN(DeviceInfoCallback);
+  };
+
+  // CefMediaObserver methods:
+  void OnSinks(const MediaSinkVector& sinks) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    ClearSinkInfoMap();
+
+    // Reset pending sink state.
+    pending_sink_callbacks_ = sinks.size();
+    pending_sink_query_id_ = ++next_sink_query_id_;
+
+    if (sinks.empty()) {
+      // No sinks, send the response immediately.
+      SendSinksResponse();
+      return;
+    }
+
+    DeviceInfoCallback::CallbackType callback = base::Bind(
+        &MediaObserver::OnSinkDeviceInfo, this, pending_sink_query_id_);
+
+    MediaSinkVector::const_iterator it = sinks.begin();
+    for (size_t idx = 0; it != sinks.end(); ++it, ++idx) {
+      CefRefPtr<CefMediaSink> sink = *it;
+      const std::string& sink_id = sink->GetId();
+      SinkInfo* info = new SinkInfo;
+      info->sink = sink;
+      sink_info_map_.insert(std::make_pair(sink_id, info));
+
+      // Request the device info asynchronously. Send the response once all
+      // callbacks have executed.
+      sink->GetDeviceInfo(new DeviceInfoCallback(sink_id, callback));
+    }
+  }
+
+  void OnRoutes(const MediaRouteVector& routes) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    route_map_.clear();
+
+    CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
+    CefRefPtr<CefListValue> routes_list = CefListValue::Create();
+    routes_list->SetSize(routes.size());
+
+    MediaRouteVector::const_iterator it = routes.begin();
+    for (size_t idx = 0; it != routes.end(); ++it, ++idx) {
+      CefRefPtr<CefMediaRoute> route = *it;
+      const std::string& route_id = route->GetId();
+      route_map_.insert(std::make_pair(route_id, route));
+
+      CefRefPtr<CefDictionaryValue> route_dict = CefDictionaryValue::Create();
+      route_dict->SetString("id", route_id);
+      route_dict->SetString(kSourceKey, route->GetSource()->GetId());
+      route_dict->SetString(kSinkKey, route->GetSink()->GetId());
+      routes_list->SetDictionary(idx, route_dict);
+    }
+
+    payload->SetList("routes_list", routes_list);
+    SendResponse("onRoutes", payload);
+  }
+
+  void OnRouteStateChanged(CefRefPtr<CefMediaRoute> route,
+                           ConnectionState state) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
+    payload->SetString(kRouteKey, route->GetId());
+    payload->SetInt("connection_state", state);
+    SendResponse("onRouteStateChanged", payload);
+  }
+
+  void OnRouteMessageReceived(CefRefPtr<CefMediaRoute> route,
+                              const void* message,
+                              size_t message_size) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    std::string message_str(static_cast<const char*>(message), message_size);
+
+    CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
+    payload->SetString(kRouteKey, route->GetId());
+    payload->SetString(kMessageKey, message_str);
+    SendResponse("onRouteMessageReceived", payload);
+  }
+
+ private:
+  CefRefPtr<CefMediaSource> GetSource(const std::string& source_urn) {
+    CefRefPtr<CefMediaSource> source = media_router_->GetSource(source_urn);
+    if (!source || !source->IsValid())
+      return NULL;
+    return source;
+  }
+
+  CefRefPtr<CefMediaSink> GetSink(const std::string& sink_id) {
+    SinkInfoMap::const_iterator it = sink_info_map_.find(sink_id);
+    if (it != sink_info_map_.end())
+      return it->second->sink;
+    return NULL;
+  }
+
+  void ClearSinkInfoMap() {
+    SinkInfoMap::const_iterator it = sink_info_map_.begin();
+    for (; it != sink_info_map_.end(); ++it) {
+      delete it->second;
+    }
+    sink_info_map_.clear();
+  }
+
+  void OnSinkDeviceInfo(int sink_query_id,
+                        const std::string& sink_id,
+                        const CefMediaSinkDeviceInfo& device_info) {
+    // Discard callbacks that arrive after a new call to OnSinks().
+    if (sink_query_id != pending_sink_query_id_)
+      return;
+
+    SinkInfoMap::const_iterator it = sink_info_map_.find(sink_id);
+    if (it != sink_info_map_.end()) {
+      it->second->device_info = device_info;
+    }
+
+    // Send the response once we've received all expected callbacks.
+    DCHECK_GT(pending_sink_callbacks_, 0U);
+    if (--pending_sink_callbacks_ == 0U) {
+      SendSinksResponse();
+    }
+  }
+
+  CefRefPtr<CefMediaRoute> GetRoute(const std::string& route_id) {
+    RouteMap::const_iterator it = route_map_.find(route_id);
+    if (it != route_map_.end())
+      return it->second;
+    return NULL;
+  }
+
+  void SendResponse(const std::string& name,
+                    CefRefPtr<CefDictionaryValue> payload) {
+    CefRefPtr<CefDictionaryValue> result = CefDictionaryValue::Create();
+    result->SetString(kNameKey, name);
+    result->SetDictionary(kPayloadKey, payload);
+    SendSuccess(subscription_callback_, result);
+  }
+
+  void SendSinksResponse() {
+    CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
+    CefRefPtr<CefListValue> sinks_list = CefListValue::Create();
+    sinks_list->SetSize(sink_info_map_.size());
+
+    SinkInfoMap::const_iterator it = sink_info_map_.begin();
+    for (size_t idx = 0; it != sink_info_map_.end(); ++it, ++idx) {
+      const SinkInfo* info = it->second;
+
+      CefRefPtr<CefDictionaryValue> sink_dict = CefDictionaryValue::Create();
+      sink_dict->SetString("id", it->first);
+      sink_dict->SetString("name", info->sink->GetName());
+      sink_dict->SetString("desc", info->sink->GetDescription());
+      sink_dict->SetInt("icon", info->sink->GetIconType());
+      sink_dict->SetString("ip_address",
+                           CefString(&info->device_info.ip_address));
+      sink_dict->SetInt("port", info->device_info.port);
+      sink_dict->SetString("model_name",
+                           CefString(&info->device_info.model_name));
+      sink_dict->SetString("type",
+                           info->sink->IsCastSink()
+                               ? "cast"
+                               : info->sink->IsDialSink() ? "dial" : "unknown");
+      sinks_list->SetDictionary(idx, sink_dict);
+    }
+
+    payload->SetList("sinks_list", sinks_list);
+    SendResponse("onSinks", payload);
+  }
+
+  CefRefPtr<CefMediaRouter> media_router_;
+  CefRefPtr<CallbackType> subscription_callback_;
+
+  struct SinkInfo {
+    CefRefPtr<CefMediaSink> sink;
+    CefMediaSinkDeviceInfo device_info;
+  };
+  typedef std::map<std::string, SinkInfo*> SinkInfoMap;
+
+  // Used to uniquely identify a call to OnSinks(), for the purpose of
+  // associating OnMediaSinkDeviceInfo() callbacks.
+  int next_sink_query_id_;
+
+  // State from the most recent call to OnSinks().
+  SinkInfoMap sink_info_map_;
+  int pending_sink_query_id_;
+  size_t pending_sink_callbacks_;
+
+  // State from the most recent call to OnRoutes().
+  typedef std::map<std::string, CefRefPtr<CefMediaRoute>> RouteMap;
+  RouteMap route_map_;
+
+  IMPLEMENT_REFCOUNTING(MediaObserver);
+  DISALLOW_COPY_AND_ASSIGN(MediaObserver);
+};
+
+// Handle messages in the browser process. Only accessed on the UI thread.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  typedef std::vector<std::string> NameVector;
+
+  Handler() { CEF_REQUIRE_UI_THREAD(); }
+
+  virtual ~Handler() {
+    SubscriptionStateMap::iterator it = subscription_state_map_.begin();
+    for (; it != subscription_state_map_.end(); ++it) {
+      delete it->second;
+    }
+  }
+
+  // Called due to cefQuery execution in media_router.html.
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (!test_runner::IsTestURL(url, kTestUrlPath))
+      return false;
+
+    // Parse |request| as a JSON dictionary.
+    CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
+    if (!request_dict) {
+      SendFailure(callback, kMessageFormatError, "Incorrect message format");
+      return true;
+    }
+
+    // Verify the "name" key.
+    if (!VerifyKey(request_dict, kNameKey, VTYPE_STRING, callback))
+      return true;
+
+    const std::string& message_name = request_dict->GetString(kNameKey);
+    if (message_name == kNameValueSubscribe) {
+      // Subscribe to notifications from the media router.
+
+      if (!persistent) {
+        SendFailure(callback, kMessageFormatError,
+                    "Subscriptions must be persistent");
+        return true;
+      }
+
+      if (!CreateSubscription(browser, query_id, callback)) {
+        SendFailure(callback, kRequestFailedError,
+                    "Browser is already subscribed");
+      }
+      return true;
+    }
+
+    // All other messages require a current subscription.
+    CefRefPtr<MediaObserver> media_observer =
+        GetMediaObserver(browser->GetIdentifier());
+    if (!media_observer) {
+      SendFailure(callback, kRequestFailedError,
+                  "Browser is not currently subscribed");
+    }
+
+    if (message_name == kNameValueCreateRoute) {
+      // Create a new route.
+
+      // Verify the "source_urn" key.
+      if (!VerifyKey(request_dict, kSourceKey, VTYPE_STRING, callback))
+        return true;
+      // Verify the "sink_id" key.
+      if (!VerifyKey(request_dict, kSinkKey, VTYPE_STRING, callback))
+        return true;
+
+      const std::string& source_urn = request_dict->GetString(kSourceKey);
+      const std::string& sink_id = request_dict->GetString(kSinkKey);
+
+      // |callback| will be executed once the route is created.
+      std::string error;
+      if (!media_observer->CreateRoute(source_urn, sink_id, callback, error)) {
+        SendFailure(callback, kRequestFailedError, error);
+      }
+      return true;
+    } else if (message_name == kNameValueTerminateRoute) {
+      // Terminate an existing route.
+
+      // Verify the "route" key.
+      if (!VerifyKey(request_dict, kRouteKey, VTYPE_STRING, callback))
+        return true;
+
+      const std::string& route_id = request_dict->GetString(kRouteKey);
+      std::string error;
+      if (!media_observer->TerminateRoute(route_id, error)) {
+        SendFailure(callback, kRequestFailedError, error);
+      } else {
+        SendSuccessACK(callback);
+      }
+      return true;
+    } else if (message_name == kNameValueSendMessage) {
+      // Send a route message.
+
+      // Verify the "route_id" key.
+      if (!VerifyKey(request_dict, kRouteKey, VTYPE_STRING, callback))
+        return true;
+      // Verify the "message" key.
+      if (!VerifyKey(request_dict, kMessageKey, VTYPE_STRING, callback))
+        return true;
+
+      const std::string& route_id = request_dict->GetString(kRouteKey);
+      const std::string& message = request_dict->GetString(kMessageKey);
+      std::string error;
+      if (!media_observer->SendRouteMessage(route_id, message, error)) {
+        SendFailure(callback, kRequestFailedError, error);
+      } else {
+        SendSuccessACK(callback);
+      }
+      return true;
+    }
+
+    return false;
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    RemoveSubscription(browser->GetIdentifier(), query_id);
+  }
+
+ private:
+  static void SendSuccessACK(CefRefPtr<Callback> callback) {
+    CefRefPtr<CefDictionaryValue> result = CefDictionaryValue::Create();
+    result->SetBool(kSuccessKey, true);
+    SendSuccess(callback, result);
+  }
+
+  // Convert a JSON string to a dictionary value.
+  static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
+    CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
+    if (value.get() && value->GetType() == VTYPE_DICTIONARY)
+      return value->GetDictionary();
+    return nullptr;
+  }
+
+  // Verify that |key| exists in |dictionary| and has type |value_type|. Fails
+  // |callback| and returns false on failure.
+  static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
+                        const char* key,
+                        cef_value_type_t value_type,
+                        CefRefPtr<Callback> callback) {
+    if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
+      SendFailure(
+          callback, kMessageFormatError,
+          "Missing or incorrectly formatted message key: " + std::string(key));
+      return false;
+    }
+    return true;
+  }
+
+  // Subscription state associated with a single browser.
+  struct SubscriptionState {
+    int64 query_id;
+    CefRefPtr<MediaObserver> observer;
+    CefRefPtr<CefRegistration> registration;
+  };
+
+  bool CreateSubscription(CefRefPtr<CefBrowser> browser,
+                          int64 query_id,
+                          CefRefPtr<Callback> callback) {
+    const int browser_id = browser->GetIdentifier();
+    if (subscription_state_map_.find(browser_id) !=
+        subscription_state_map_.end()) {
+      // An subscription already exists for this browser.
+      return false;
+    }
+
+    CefRefPtr<CefMediaRouter> media_router =
+        browser->GetHost()->GetRequestContext()->GetMediaRouter();
+
+    SubscriptionState* state = new SubscriptionState();
+    state->query_id = query_id;
+    state->observer = new MediaObserver(media_router, callback);
+    state->registration = media_router->AddObserver(state->observer);
+    subscription_state_map_.insert(std::make_pair(browser_id, state));
+
+    // Trigger sink and route callbacks.
+    media_router->NotifyCurrentSinks();
+    media_router->NotifyCurrentRoutes();
+
+    return true;
+  }
+
+  void RemoveSubscription(int browser_id, int64 query_id) {
+    SubscriptionStateMap::iterator it =
+        subscription_state_map_.find(browser_id);
+    if (it != subscription_state_map_.end() &&
+        it->second->query_id == query_id) {
+      delete it->second;
+      subscription_state_map_.erase(it);
+    }
+  }
+
+  CefRefPtr<MediaObserver> GetMediaObserver(int browser_id) {
+    SubscriptionStateMap::const_iterator it =
+        subscription_state_map_.find(browser_id);
+    if (it != subscription_state_map_.end()) {
+      return it->second->observer;
+    }
+    return NULL;
+  }
+
+  // Map of browser ID to SubscriptionState object.
+  typedef std::map<int, SubscriptionState*> SubscriptionStateMap;
+  SubscriptionStateMap subscription_state_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(Handler);
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace media_router_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/media_router_test.h b/src/tests/cefclient/browser/media_router_test.h
new file mode 100644
index 0000000..1671ff0
--- /dev/null
+++ b/src/tests/cefclient/browser/media_router_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_MEDIA_ROUTER_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_MEDIA_ROUTER_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace media_router_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace media_router_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_MEDIA_ROUTER_TEST_H_
diff --git a/src/tests/cefclient/browser/osr_accessibility_helper.cc b/src/tests/cefclient/browser/osr_accessibility_helper.cc
new file mode 100644
index 0000000..01fafc0
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_accessibility_helper.cc
@@ -0,0 +1,270 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_accessibility_helper.h"
+#include "tests/cefclient/browser/osr_accessibility_node.h"
+
+namespace client {
+
+OsrAXTree::OsrAXTree() : root_node_id_(-1) {}
+
+OsrAXNode* OsrAXTree::GetNode(int nodeId) const {
+  auto result = node_map_.find(nodeId);
+  if (result != node_map_.end()) {
+    return result->second;
+  }
+  return nullptr;
+}
+
+void OsrAXTree::EraseNode(int nodeId) {
+  node_map_.erase(nodeId);
+}
+
+void OsrAXTree::AddNode(OsrAXNode* node) {
+  node_map_[node->OsrAXNodeId()] = node;
+}
+
+void OsrAXTree::UpdateTreeData(CefRefPtr<CefDictionaryValue> value) {
+  if (value->HasKey("parent_tree_id")) {
+    parent_tree_id_ = value->GetString("parent_tree_id");
+  } else {
+    parent_tree_id_ = "";
+  }
+
+  // may also update following:
+  // doctype, title, url, mimetype
+}
+
+OsrAccessibilityHelper::OsrAccessibilityHelper(CefRefPtr<CefValue> value,
+                                               CefRefPtr<CefBrowser> browser)
+    : focused_node_id_(-1),
+      browser_(browser) {
+  UpdateAccessibilityTree(value);
+}
+
+int OsrAccessibilityHelper::CastToInt(CefRefPtr<CefValue> value) {
+  if (value->GetType() == VTYPE_STRING) {
+    const std::string& str = value->GetString();
+    return atoi(str.c_str());
+  } else {
+    return value->GetInt();
+  }
+}
+
+void OsrAccessibilityHelper::UpdateAccessibilityLocation(
+    CefRefPtr<CefValue> value) {
+  if (!value || value->GetType() != VTYPE_LIST) {
+    return;
+  }
+
+  CefRefPtr<CefListValue> locationChangeList = value->GetList();
+  size_t locationChangeCount = locationChangeList->GetSize();
+  if (locationChangeCount == 0) {
+    return;
+  }
+
+  for (size_t i = 0; i < locationChangeCount; i++) {
+    CefRefPtr<CefDictionaryValue> locationChangeDict =
+        locationChangeList->GetDictionary(i);
+    if (!locationChangeDict->HasKey("ax_tree_id") ||
+        !locationChangeDict->HasKey("new_location") ||
+        !locationChangeDict->HasKey("id")) {
+      continue;
+    }
+    CefString treeId = locationChangeDict->GetString("ax_tree_id");
+    int nodeId = CastToInt(locationChangeDict->GetValue("id"));
+
+    CefRefPtr<CefDictionaryValue> newLocationDict =
+        locationChangeDict->GetDictionary("new_location");
+    if (!newLocationDict) {
+      continue;
+    }
+
+    OsrAXNode* node = GetNode(treeId, nodeId);
+    if (!node) {
+      continue;
+    }
+    node->UpdateLocation(newLocationDict);
+  }
+}
+
+void OsrAccessibilityHelper::UpdateAccessibilityTree(
+    CefRefPtr<CefValue> value) {
+  if (!value || value->GetType() != VTYPE_DICTIONARY) {
+    return;
+  }
+
+  CefRefPtr<CefDictionaryValue> mainDict = value->GetDictionary();
+  if (!mainDict->HasKey("ax_tree_id") || !mainDict->HasKey("updates")) {
+    return;
+  }
+
+  CefString treeId = mainDict->GetString("ax_tree_id");
+  CefRefPtr<CefListValue> updatesList = mainDict->GetList("updates");
+
+  size_t updatesCount = updatesList->GetSize();
+  if (updatesCount == 0) {
+    return;
+  }
+
+  for (size_t i = 0; i < updatesCount; i++) {
+    CefRefPtr<CefDictionaryValue> updateDict = updatesList->GetDictionary(i);
+    UpdateLayout(treeId, updateDict);
+  }
+}
+
+OsrAXNode* OsrAccessibilityHelper::GetRootNode() const {
+  return GetTreeRootNode(root_tree_id_);
+}
+
+OsrAXNode* OsrAccessibilityHelper::GetFocusedNode() const {
+  auto tree = accessibility_node_map_.find(focused_tree_id_);
+  if (tree != accessibility_node_map_.end()) {
+    return tree->second.GetNode(focused_node_id_);
+  }
+
+  return nullptr;
+}
+
+OsrAXNode* OsrAccessibilityHelper::GetTreeRootNode(
+    const CefString& treeId) const {
+  auto tree = accessibility_node_map_.find(treeId);
+  if (tree != accessibility_node_map_.end()) {
+    return tree->second.GetNode(tree->second.GetRootNodeId());
+  }
+
+  return nullptr;
+}
+
+void OsrAccessibilityHelper::UpdateLayout(
+    const CefString& treeId,
+    CefRefPtr<CefDictionaryValue> update) {
+  if (!update) {
+    return;
+  }
+
+  // If a node is to be cleared
+  if (update->HasKey("node_id_to_clear")) {
+    int nodeId = CastToInt(update->GetValue("node_id_to_clear"));
+
+    // reset root node if that is to be cleared
+    auto tree = accessibility_node_map_.find(treeId);
+    if (tree != accessibility_node_map_.end()) {
+      if (tree->second.GetRootNodeId() == nodeId) {
+        root_tree_id_ = "";
+        tree->second.SetRootNodeId(-1);
+      }
+    }
+    if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
+      UpdateFocusedNode("", -1);
+    }
+    OsrAXNode* node = GetNode(treeId, nodeId);
+    DestroyNode(node);
+  }
+
+  // get tree data
+  if (update->HasKey("tree_data") && update->HasKey("has_tree_data") &&
+      update->GetBool("has_tree_data")) {
+    CefRefPtr<CefDictionaryValue> tree_data =
+        update->GetDictionary("tree_data");
+    auto& tree = accessibility_node_map_[treeId];
+    tree.UpdateTreeData(tree_data);
+    if (tree.GetParentTreeId().empty()) {
+      root_tree_id_ = treeId;
+    }
+    if (tree_data->HasKey("focus_id") && tree_data->HasKey("focused_tree_id")) {
+      UpdateFocusedNode(tree_data->GetString("focused_tree_id"),
+                        CastToInt(tree_data->GetValue("focus_id")));
+    }
+  }
+
+  // Now initialize/update the node data.
+  if (update->HasKey("nodes")) {
+    CefRefPtr<CefListValue> nodes = update->GetList("nodes");
+
+    for (size_t index = 0; index < nodes->GetSize(); index++) {
+      CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
+      if (node) {
+        auto& tree = accessibility_node_map_[treeId];
+        int nodeId = CastToInt(node->GetValue("id"));
+        OsrAXNode* axNode = tree.GetNode(nodeId);
+        // Create if it is a new one
+        if (axNode) {
+          axNode->UpdateValue(node);
+        } else {
+          axNode = OsrAXNode::CreateNode(treeId, nodeId, node, this);
+          tree.AddNode(axNode);
+        }
+      }
+    }
+  }
+
+  if (update->HasKey("root_id")) {
+    int nodeId = CastToInt(update->GetValue("root_id"));
+    OsrAXNode* node = GetNode(treeId, nodeId);
+    if (node != nullptr) {
+      auto& tree = accessibility_node_map_[treeId];
+      tree.SetRootNodeId(nodeId);
+    }
+  }
+}
+
+void OsrAccessibilityHelper::UpdateFocusedNode(const CefString& treeId,
+                                               int nodeId) {
+  if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
+    return;
+  }
+  focused_tree_id_ = treeId;
+  focused_node_id_ = nodeId;
+
+  // Now Notify Screen Reader
+  OsrAXNode* axNode = GetFocusedNode();
+  if (axNode) {
+    axNode->NotifyAccessibilityEvent("focus");
+  }
+}
+
+void OsrAccessibilityHelper::Reset() {
+  accessibility_node_map_.clear();
+  root_tree_id_ = "";
+  focused_tree_id_ = "";
+  focused_node_id_ = -1;
+}
+
+void OsrAccessibilityHelper::DestroyNode(OsrAXNode* node) {
+  if (node) {
+    CefString treeId = node->OsrAXTreeId();
+    int numChilds = node->GetChildCount();
+    if (numChilds > 0) {
+      for (int i = 0; i < numChilds; i++) {
+        OsrAXNode* childNode = node->ChildAtIndex(i);
+        if (!childNode) {
+          continue;
+        }
+        childNode->SetParent(nullptr);
+        if (childNode->OsrAXTreeId() == treeId) {
+          DestroyNode(childNode);
+        }
+      }
+    }
+    auto tree = accessibility_node_map_.find(treeId);
+    if (tree != accessibility_node_map_.end()) {
+      tree->second.EraseNode(node->OsrAXNodeId());
+    }
+
+    node->Destroy();
+  }
+}
+
+OsrAXNode* OsrAccessibilityHelper::GetNode(const CefString& treeId,
+                                           int nodeId) const {
+  auto tree = accessibility_node_map_.find(treeId);
+  if (tree != accessibility_node_map_.end()) {
+    return tree->second.GetNode(nodeId);
+  }
+
+  return nullptr;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_accessibility_helper.h b/src/tests/cefclient/browser/osr_accessibility_helper.h
new file mode 100644
index 0000000..83f271e
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_accessibility_helper.h
@@ -0,0 +1,81 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
+
+#include <map>
+
+#include "include/cef_browser.h"
+
+namespace client {
+
+class OsrAXNode;
+class OsrAccessibilityHelper;
+
+class OsrAXTree {
+ public:
+  OsrAXTree();
+  OsrAXNode* GetNode(int nodeId) const;
+  void EraseNode(int nodeId);
+  void UpdateTreeData(CefRefPtr<CefDictionaryValue> value);
+  void AddNode(OsrAXNode* node);
+  const CefString& GetParentTreeId() const { return parent_tree_id_; }
+  int GetRootNodeId() const { return root_node_id_; }
+  void SetRootNodeId(int nodeId) { root_node_id_ = nodeId; }
+
+ private:
+  CefString parent_tree_id_;
+  int root_node_id_;
+  std::map<int, OsrAXNode*> node_map_;
+};
+
+// Helper class that abstracts Renderer Accessibility tree and provides a
+// uniform interface to be consumed by IAccessible interface on Windows and
+// NSAccessibility implementation on Mac in CefClient.
+class OsrAccessibilityHelper {
+ public:
+  OsrAccessibilityHelper(CefRefPtr<CefValue> value,
+                         CefRefPtr<CefBrowser> browser);
+
+  void UpdateAccessibilityTree(CefRefPtr<CefValue> value);
+
+  void UpdateAccessibilityLocation(CefRefPtr<CefValue> value);
+
+  OsrAXNode* GetRootNode() const;
+
+  OsrAXNode* GetFocusedNode() const;
+
+  CefWindowHandle GetWindowHandle() const {
+    return browser_->GetHost()->GetWindowHandle();
+  }
+
+  CefRefPtr<CefBrowser> GetBrowser() const { return browser_; }
+
+  OsrAXNode* GetNode(const CefString& treeId, int nodeId) const;
+
+  OsrAXNode* GetTreeRootNode(const CefString& treeId) const;
+
+  static int CastToInt(CefRefPtr<CefValue> value);
+
+ private:
+  void Reset();
+
+  void UpdateLayout(const CefString& treeId,
+                    CefRefPtr<CefDictionaryValue> update);
+
+  void UpdateFocusedNode(const CefString& treeId, int nodeId);
+
+  // Destroy the node and remove from Map
+  void DestroyNode(OsrAXNode* node);
+  CefString root_tree_id_;
+  CefString focused_tree_id_;
+  int focused_node_id_;
+  CefRefPtr<CefBrowser> browser_;
+  std::map<CefString, OsrAXTree> accessibility_node_map_;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
diff --git a/src/tests/cefclient/browser/osr_accessibility_node.cc b/src/tests/cefclient/browser/osr_accessibility_node.cc
new file mode 100644
index 0000000..befa25f
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_accessibility_node.cc
@@ -0,0 +1,173 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+// Base class implementation for CEF Acccessibility node. This is subclassed and
+// used by both IAccessible/NSAccessibility protocol implementation.
+
+#include "tests/cefclient/browser/osr_accessibility_node.h"
+
+#include "tests/cefclient/browser/osr_accessibility_helper.h"
+
+namespace client {
+
+OsrAXNode::OsrAXNode(const CefString& treeId,
+                     int nodeId,
+                     CefRefPtr<CefDictionaryValue> value,
+                     OsrAccessibilityHelper* helper)
+    : tree_id_(treeId),
+      node_id_(nodeId),
+      platform_accessibility_(nullptr),
+      parent_(nullptr),
+      offset_container_id_(-1),
+      accessibility_helper_(helper) {
+  UpdateValue(value);
+}
+
+void OsrAXNode::UpdateLocation(CefRefPtr<CefDictionaryValue> value) {
+  // Update Bounds
+  if (value->HasKey("bounds")) {
+    CefRefPtr<CefDictionaryValue> loc = value->GetDictionary("bounds");
+    if (loc) {
+      location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"),
+                          loc->GetDouble("width"), loc->GetDouble("height"));
+    }
+  }
+  // Update offsets
+  if (value->HasKey("offset_container_id")) {
+    offset_container_id_ = OsrAccessibilityHelper::CastToInt(
+        value->GetValue("offset_container_id"));
+  }
+}
+
+void OsrAXNode::UpdateValue(CefRefPtr<CefDictionaryValue> value) {
+  if (value->HasKey("role"))
+    role_ = value->GetString("role");
+
+  if (value->HasKey("child_ids")) {
+    CefRefPtr<CefListValue> childs = value->GetList("child_ids");
+    // Reset child Ids
+    child_ids_.clear();
+    for (size_t idx = 0; idx < childs->GetSize(); idx++)
+      child_ids_.push_back(
+          OsrAccessibilityHelper::CastToInt(childs->GetValue(idx)));
+  }
+  // Update Location
+  if (value->HasKey("location")) {
+    CefRefPtr<CefDictionaryValue> loc = value->GetDictionary("location");
+    if (loc) {
+      location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"),
+                          loc->GetDouble("width"), loc->GetDouble("height"));
+    }
+  }
+  // Update offsets
+  if (value->HasKey("offset_container_id")) {
+    offset_container_id_ = OsrAccessibilityHelper::CastToInt(
+        value->GetValue("offset_container_id"));
+  }
+  // Update attributes
+  if (value->HasKey("attributes")) {
+    child_tree_id_ = "";
+
+    attributes_ = value->GetDictionary("attributes");
+
+    if (attributes_) {
+      scroll_.x = attributes_->HasKey("scrollX")
+                      ? OsrAccessibilityHelper::CastToInt(
+                            attributes_->GetValue("scrollX"))
+                      : 0;
+      scroll_.y = attributes_->HasKey("scrollY")
+                      ? OsrAccessibilityHelper::CastToInt(
+                            attributes_->GetValue("scrollY"))
+                      : 0;
+    }
+
+    if (attributes_ && attributes_->HasKey("childTreeId")) {
+      child_tree_id_ = attributes_->GetString("childTreeId");
+    }
+    if (attributes_ && attributes_->HasKey("name"))
+      name_ = attributes_->GetString("name");
+    if (attributes_ && attributes_->HasKey("value"))
+      value_ = attributes_->GetString("value");
+    if (attributes_ && attributes_->HasKey("description"))
+      description_ = attributes_->GetString("description");
+  }
+}
+
+CefWindowHandle OsrAXNode::GetWindowHandle() const {
+  if (accessibility_helper_)
+    return accessibility_helper_->GetWindowHandle();
+  return NULL;
+}
+
+CefRefPtr<CefBrowser> OsrAXNode::GetBrowser() const {
+  if (accessibility_helper_)
+    return accessibility_helper_->GetBrowser();
+  return nullptr;
+}
+
+void OsrAXNode::SetParent(OsrAXNode* parent) {
+  parent_ = parent;
+}
+
+CefRect OsrAXNode::AxLocation() const {
+  CefRect loc = location_;
+  loc.x -= scroll_.x;
+  loc.y -= scroll_.y;
+  OsrAXNode* offsetNode =
+      accessibility_helper_->GetNode(OsrAXTreeId(), offset_container_id_);
+  if (!offsetNode) {
+    OsrAXNode* p = parent_;
+    while (p) {
+      if (p->OsrAXTreeId() != OsrAXTreeId()) {
+        offsetNode = p;
+        break;
+      }
+      p = p->parent_;
+    }
+  }
+  // Add offset from parent Location
+  if (offsetNode) {
+    CefRect offset = offsetNode->AxLocation();
+    loc.x += offset.x;
+    loc.y += offset.y;
+  }
+  return loc;
+}
+
+int OsrAXNode::GetChildCount() const {
+  int count = static_cast<int>(child_ids_.size());
+  if (!child_tree_id_.empty()) {
+    OsrAXNode* childTreeRootNode =
+        accessibility_helper_->GetTreeRootNode(child_tree_id_);
+    if (childTreeRootNode) {
+      count++;
+    }
+  }
+  return count;
+}
+
+OsrAXNode* OsrAXNode::ChildAtIndex(int index) const {
+  int count = static_cast<int>(child_ids_.size());
+  if (index < count)
+    return accessibility_helper_->GetNode(OsrAXTreeId(), child_ids_[index]);
+  if ((index == count) && (!child_tree_id_.empty())) {
+    OsrAXNode* childTreeRootNode =
+        accessibility_helper_->GetTreeRootNode(child_tree_id_);
+    if (childTreeRootNode) {
+      return childTreeRootNode;
+    }
+  }
+
+  return nullptr;
+}
+
+// Create and return the platform specific OsrAXNode Object
+OsrAXNode* OsrAXNode::CreateNode(const CefString& treeId,
+                                 int nodeId,
+                                 CefRefPtr<CefDictionaryValue> value,
+                                 OsrAccessibilityHelper* helper) {
+  return new OsrAXNode(treeId, nodeId, value, helper);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_accessibility_node.h b/src/tests/cefclient/browser/osr_accessibility_node.h
new file mode 100644
index 0000000..ece83c0
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_accessibility_node.h
@@ -0,0 +1,123 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_NODE_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_NODE_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_browser.h"
+
+#if defined(OS_MACOSX)
+typedef void CefNativeAccessible;
+#if __OBJC__
+#if __has_feature(objc_arc)
+#define CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(accessible) \
+  (__bridge NSObject*)accessible
+#define CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(object) \
+  (__bridge CefNativeAccessible*)object
+#else  // __has_feature(objc_arc)
+#define CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(accessible) (NSObject*)accessible
+#define CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(object) \
+  (__bridge CefNativeAccessible*)object
+#endif  // __has_feature(objc_arc)
+#endif  // __OBJC__
+#elif defined(OS_WIN)
+struct IAccessible;
+typedef IAccessible CefNativeAccessible;
+#else
+#error "Unsupported platform"
+#endif
+
+namespace client {
+
+class OsrAccessibilityHelper;
+
+// OsrAXNode is the base class for implementation for the NSAccessibility
+// protocol for interacting with VoiceOver and other accessibility clients.
+class OsrAXNode {
+ public:
+  // Create and return the platform specific OsrAXNode Object.
+  static OsrAXNode* CreateNode(const CefString& treeId,
+                               int nodeId,
+                               CefRefPtr<CefDictionaryValue> value,
+                               OsrAccessibilityHelper* helper);
+
+  // Update Value.
+  void UpdateValue(CefRefPtr<CefDictionaryValue> value);
+
+  // UpdateLocation
+  void UpdateLocation(CefRefPtr<CefDictionaryValue> value);
+
+  // Fire a platform-specific notification that an event has occurred on
+  // this object.
+  void NotifyAccessibilityEvent(std::string event_type) const;
+
+  // Call Destroy rather than deleting this, because the subclass may
+  // use reference counting.
+  void Destroy();
+
+  // Return NSAccessibility Object for Mac/ IAccessible for Windows
+  CefNativeAccessible* GetNativeAccessibleObject(OsrAXNode* parent);
+
+  CefNativeAccessible* GetParentAccessibleObject() const {
+    return parent_ ? parent_->platform_accessibility_ : nullptr;
+  }
+
+  OsrAccessibilityHelper* GetAccessibilityHelper() const {
+    return accessibility_helper_;
+  }
+
+  int GetChildCount() const;
+
+  // Return the Child at the specified index
+  OsrAXNode* ChildAtIndex(int index) const;
+
+  const CefString& AxRole() const { return role_; }
+
+  const CefString& OsrAXTreeId() const { return tree_id_; }
+
+  int OsrAXNodeId() const { return node_id_; }
+
+  const CefString& AxValue() const { return value_; }
+
+  const CefString& AxName() const { return name_; }
+
+  const CefString& AxDescription() const { return description_; }
+
+  CefRect AxLocation() const;
+
+  CefWindowHandle GetWindowHandle() const;
+
+  CefRefPtr<CefBrowser> GetBrowser() const;
+
+  void SetParent(OsrAXNode* parent);
+
+ protected:
+  OsrAXNode(const CefString& treeId,
+            int nodeId,
+            CefRefPtr<CefDictionaryValue> value,
+            OsrAccessibilityHelper* helper);
+
+  CefString tree_id_;
+  int node_id_;
+  CefString child_tree_id_;
+  CefString role_;
+  CefString value_;
+  CefString name_;
+  CefString description_;
+  CefRect location_;
+  CefPoint scroll_;
+  std::vector<int> child_ids_;
+  CefNativeAccessible* platform_accessibility_;
+  OsrAXNode* parent_;
+  int offset_container_id_;
+  OsrAccessibilityHelper* accessibility_helper_;
+  CefRefPtr<CefDictionaryValue> attributes_;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_NODE_H_
diff --git a/src/tests/cefclient/browser/osr_accessibility_node_mac.mm b/src/tests/cefclient/browser/osr_accessibility_node_mac.mm
new file mode 100644
index 0000000..2fbb8da
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_accessibility_node_mac.mm
@@ -0,0 +1,504 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+// Sample implementation for the NSAccessibility protocol for interacting with
+// VoiceOver and other accessibility clients.
+
+#include "tests/cefclient/browser/osr_accessibility_node.h"
+
+#import <AppKit/NSAccessibility.h>
+#import <Cocoa/Cocoa.h>
+
+#include "tests/cefclient/browser/osr_accessibility_helper.h"
+
+namespace {
+
+NSString* AxRoleToNSAxRole(const std::string& role_string) {
+  if (role_string == "abbr")
+    return NSAccessibilityGroupRole;
+  if (role_string == "alertDialog")
+    return NSAccessibilityGroupRole;
+  if (role_string == "alert")
+    return NSAccessibilityGroupRole;
+  if (role_string == "annotation")
+    return NSAccessibilityUnknownRole;
+  if (role_string == "application")
+    return NSAccessibilityGroupRole;
+  if (role_string == "article")
+    return NSAccessibilityGroupRole;
+  if (role_string == "audio")
+    return NSAccessibilityGroupRole;
+  if (role_string == "banner")
+    return NSAccessibilityGroupRole;
+  if (role_string == "blockquote")
+    return NSAccessibilityGroupRole;
+  if (role_string == "busyIndicator")
+    return NSAccessibilityBusyIndicatorRole;
+  if (role_string == "button")
+    return NSAccessibilityButtonRole;
+  if (role_string == "buttonDropDown")
+    return NSAccessibilityButtonRole;
+  if (role_string == "canvas")
+    return NSAccessibilityImageRole;
+  if (role_string == "caption")
+    return NSAccessibilityGroupRole;
+  if (role_string == "checkBox")
+    return NSAccessibilityCheckBoxRole;
+  if (role_string == "colorWell")
+    return NSAccessibilityColorWellRole;
+  if (role_string == "column")
+    return NSAccessibilityColumnRole;
+  if (role_string == "comboBox")
+    return NSAccessibilityComboBoxRole;
+  if (role_string == "complementary")
+    return NSAccessibilityGroupRole;
+  if (role_string == "contentInfo")
+    return NSAccessibilityGroupRole;
+  if (role_string == "definition")
+    return NSAccessibilityGroupRole;
+  if (role_string == "descriptionListDetail")
+    return NSAccessibilityGroupRole;
+  if (role_string == "descriptionList")
+    return NSAccessibilityListRole;
+  if (role_string == "descriptionListTerm")
+    return NSAccessibilityGroupRole;
+  if (role_string == "details")
+    return NSAccessibilityGroupRole;
+  if (role_string == "dialog")
+    return NSAccessibilityGroupRole;
+  if (role_string == "directory")
+    return NSAccessibilityListRole;
+  if (role_string == "disclosureTriangle")
+    return NSAccessibilityDisclosureTriangleRole;
+  if (role_string == "div")
+    return NSAccessibilityGroupRole;
+  if (role_string == "document")
+    return NSAccessibilityGroupRole;
+  if (role_string == "embeddedObject")
+    return NSAccessibilityGroupRole;
+  if (role_string == "figcaption")
+    return NSAccessibilityGroupRole;
+  if (role_string == "figure")
+    return NSAccessibilityGroupRole;
+  if (role_string == "footer")
+    return NSAccessibilityGroupRole;
+  if (role_string == "form")
+    return NSAccessibilityGroupRole;
+  if (role_string == "genericContainer")
+    return NSAccessibilityGroupRole;
+  if (role_string == "grid")
+    return NSAccessibilityGroupRole;
+  if (role_string == "group")
+    return NSAccessibilityGroupRole;
+  if (role_string == "iframe")
+    return NSAccessibilityGroupRole;
+  if (role_string == "iframePresentational")
+    return NSAccessibilityGroupRole;
+  if (role_string == "ignored")
+    return NSAccessibilityUnknownRole;
+  if (role_string == "imageMapLink")
+    return NSAccessibilityLinkRole;
+  if (role_string == "imageMap")
+    return NSAccessibilityGroupRole;
+  if (role_string == "image")
+    return NSAccessibilityImageRole;
+  if (role_string == "labelText")
+    return NSAccessibilityGroupRole;
+  if (role_string == "legend")
+    return NSAccessibilityGroupRole;
+  if (role_string == "link")
+    return NSAccessibilityLinkRole;
+  if (role_string == "listBoxOption")
+    return NSAccessibilityStaticTextRole;
+  if (role_string == "listBox")
+    return NSAccessibilityListRole;
+  if (role_string == "listItem")
+    return NSAccessibilityGroupRole;
+  if (role_string == "list")
+    return NSAccessibilityListRole;
+  if (role_string == "log")
+    return NSAccessibilityGroupRole;
+  if (role_string == "main")
+    return NSAccessibilityGroupRole;
+  if (role_string == "mark")
+    return NSAccessibilityGroupRole;
+  if (role_string == "marquee")
+    return NSAccessibilityGroupRole;
+  if (role_string == "math")
+    return NSAccessibilityGroupRole;
+  if (role_string == "menu")
+    return NSAccessibilityMenuRole;
+  if (role_string == "menuBar")
+    return NSAccessibilityMenuBarRole;
+  if (role_string == "menuButton")
+    return NSAccessibilityButtonRole;
+  if (role_string == "menuItem")
+    return NSAccessibilityMenuItemRole;
+  if (role_string == "menuItemCheckBox")
+    return NSAccessibilityMenuItemRole;
+  if (role_string == "menuItemRadio")
+    return NSAccessibilityMenuItemRole;
+  if (role_string == "menuListOption")
+    return NSAccessibilityMenuItemRole;
+  if (role_string == "menuListPopup")
+    return NSAccessibilityUnknownRole;
+  if (role_string == "meter")
+    return NSAccessibilityProgressIndicatorRole;
+  if (role_string == "navigation")
+    return NSAccessibilityGroupRole;
+  if (role_string == "note")
+    return NSAccessibilityGroupRole;
+  if (role_string == "outline")
+    return NSAccessibilityOutlineRole;
+  if (role_string == "paragraph")
+    return NSAccessibilityGroupRole;
+  if (role_string == "popUpButton")
+    return NSAccessibilityPopUpButtonRole;
+  if (role_string == "pre")
+    return NSAccessibilityGroupRole;
+  if (role_string == "presentational")
+    return NSAccessibilityGroupRole;
+  if (role_string == "progressIndicator")
+    return NSAccessibilityProgressIndicatorRole;
+  if (role_string == "radioButton")
+    return NSAccessibilityRadioButtonRole;
+  if (role_string == "radioGroup")
+    return NSAccessibilityRadioGroupRole;
+  if (role_string == "region")
+    return NSAccessibilityGroupRole;
+  if (role_string == "row")
+    return NSAccessibilityRowRole;
+  if (role_string == "ruler")
+    return NSAccessibilityRulerRole;
+  if (role_string == "scrollBar")
+    return NSAccessibilityScrollBarRole;
+  if (role_string == "search")
+    return NSAccessibilityGroupRole;
+  if (role_string == "searchBox")
+    return NSAccessibilityTextFieldRole;
+  if (role_string == "slider")
+    return NSAccessibilitySliderRole;
+  if (role_string == "sliderThumb")
+    return NSAccessibilityValueIndicatorRole;
+  if (role_string == "spinButton")
+    return NSAccessibilityIncrementorRole;
+  if (role_string == "splitter")
+    return NSAccessibilitySplitterRole;
+  if (role_string == "staticText")
+    return NSAccessibilityStaticTextRole;
+  if (role_string == "status")
+    return NSAccessibilityGroupRole;
+  if (role_string == "svgRoot")
+    return NSAccessibilityGroupRole;
+  if (role_string == "switch")
+    return NSAccessibilityCheckBoxRole;
+  if (role_string == "tabGroup")
+    return NSAccessibilityTabGroupRole;
+  if (role_string == "tabList")
+    return NSAccessibilityTabGroupRole;
+  if (role_string == "tabPanel")
+    return NSAccessibilityGroupRole;
+  if (role_string == "tab")
+    return NSAccessibilityRadioButtonRole;
+  if (role_string == "tableHeaderContainer")
+    return NSAccessibilityGroupRole;
+  if (role_string == "table")
+    return NSAccessibilityTableRole;
+  if (role_string == "textField")
+    return NSAccessibilityTextFieldRole;
+  if (role_string == "time")
+    return NSAccessibilityGroupRole;
+  if (role_string == "timer")
+    return NSAccessibilityGroupRole;
+  if (role_string == "toggleButton")
+    return NSAccessibilityCheckBoxRole;
+  if (role_string == "toolbar")
+    return NSAccessibilityToolbarRole;
+  if (role_string == "treeGrid")
+    return NSAccessibilityTableRole;
+  if (role_string == "treeItem")
+    return NSAccessibilityRowRole;
+  if (role_string == "tree")
+    return NSAccessibilityOutlineRole;
+  if (role_string == "unknown")
+    return NSAccessibilityUnknownRole;
+  if (role_string == "tooltip")
+    return NSAccessibilityGroupRole;
+  if (role_string == "video")
+    return NSAccessibilityGroupRole;
+  if (role_string == "window")
+    return NSAccessibilityWindowRole;
+  return [NSString stringWithUTF8String:role_string.c_str()];
+}
+
+inline int MiddleX(const CefRect& rect) {
+  return rect.x + rect.width / 2;
+}
+
+inline int MiddleY(const CefRect& rect) {
+  return rect.y + rect.height / 2;
+}
+
+}  // namespace
+
+// OsrAXNodeObject is sample implementation for the NSAccessibility protocol
+// for interacting with VoiceOver and other accessibility clients.
+@interface OsrAXNodeObject : NSObject {
+  // OsrAXNode* proxy object
+  client::OsrAXNode* node_;
+  CefNativeAccessible* parent_;
+}
+
+- (id)init:(client::OsrAXNode*)node;
++ (OsrAXNodeObject*)elementWithNode:(client::OsrAXNode*)node;
+@end
+
+@implementation OsrAXNodeObject
+- (id)init:(client::OsrAXNode*)node {
+  node_ = node;
+  parent_ = node_->GetParentAccessibleObject();
+  if (!parent_) {
+    parent_ = node_->GetWindowHandle();
+  }
+  return self;
+}
+
++ (OsrAXNodeObject*)elementWithNode:(client::OsrAXNode*)node {
+  // We manage the release ourself
+  return [[OsrAXNodeObject alloc] init:node];
+}
+
+- (BOOL)isEqual:(id)object {
+  if ([object isKindOfClass:[OsrAXNodeObject self]]) {
+    OsrAXNodeObject* other = object;
+    return (node_ == other->node_);
+  } else {
+    return NO;
+  }
+}
+
+// Utility methods to map AX information received from renderer
+// to platform properties
+- (NSString*)axRole {
+  // Get the Role from CefAccessibilityHelper and Map to NSRole
+  return AxRoleToNSAxRole(node_->AxRole());
+}
+
+- (NSString*)axDescription {
+  std::string desc = node_->AxDescription();
+  return [NSString stringWithUTF8String:desc.c_str()];
+}
+
+- (NSString*)axName {
+  std::string desc = node_->AxName();
+  return [NSString stringWithUTF8String:desc.c_str()];
+}
+
+- (NSString*)axValue {
+  std::string desc = node_->AxValue();
+  return [NSString stringWithUTF8String:desc.c_str()];
+}
+
+- (void)doMouseClick:(cef_mouse_button_type_t)type {
+  CefRefPtr<CefBrowser> browser = node_->GetBrowser();
+  if (browser) {
+    CefMouseEvent mouse_event;
+    const CefRect& rect = node_->AxLocation();
+    mouse_event.x = MiddleX(rect);
+    mouse_event.y = MiddleY(rect);
+
+    mouse_event.modifiers = 0;
+    browser->GetHost()->SendMouseClickEvent(mouse_event, type, false, 1);
+    browser->GetHost()->SendMouseClickEvent(mouse_event, type, true, 1);
+  }
+}
+
+- (NSMutableArray*)getKids {
+  int numChilds = node_->GetChildCount();
+  if (numChilds > 0) {
+    NSMutableArray* kids = [NSMutableArray arrayWithCapacity:numChilds];
+    for (int index = 0; index < numChilds; index++) {
+      client::OsrAXNode* child = node_->ChildAtIndex(index);
+      [kids addObject:child ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
+                                  child->GetNativeAccessibleObject(node_))
+                            : nil];
+    }
+    return kids;
+  }
+  return nil;
+}
+
+- (NSPoint)position {
+  CefRect cef_rect = node_->AxLocation();
+  NSPoint origin = NSMakePoint(cef_rect.x, cef_rect.y);
+  NSSize size = NSMakeSize(cef_rect.width, cef_rect.height);
+
+  NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(node_->GetWindowHandle());
+  origin.y = NSHeight([view bounds]) - origin.y;
+  NSPoint originInWindow = [view convertPoint:origin toView:nil];
+
+  NSRect point_rect = NSMakeRect(originInWindow.x, originInWindow.y, 0, 0);
+  NSPoint originInScreen =
+      [[view window] convertRectToScreen:point_rect].origin;
+
+  originInScreen.y = originInScreen.y - size.height;
+  return originInScreen;
+}
+
+- (NSSize)size {
+  CefRect cef_rect = node_->AxLocation();
+  NSRect rect =
+      NSMakeRect(cef_rect.x, cef_rect.y, cef_rect.width, cef_rect.height);
+  NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(node_->GetWindowHandle());
+  rect = [[view window] convertRectToScreen:rect];
+  return rect.size;
+}
+
+//
+// accessibility protocol
+//
+
+// attributes
+
+- (BOOL)accessibilityIsIgnored {
+  return NO;
+}
+
+- (NSArray*)accessibilityAttributeNames {
+  static NSArray* attributes = nil;
+  if (attributes == nil) {
+    attributes = [[NSArray alloc]
+        initWithObjects:NSAccessibilityRoleAttribute,
+                        NSAccessibilityRoleDescriptionAttribute,
+                        NSAccessibilityChildrenAttribute,
+                        NSAccessibilityValueAttribute,
+                        NSAccessibilityTitleAttribute,
+                        NSAccessibilityDescriptionAttribute,
+                        NSAccessibilityFocusedAttribute,
+                        NSAccessibilityParentAttribute,
+                        NSAccessibilityWindowAttribute,
+                        NSAccessibilityTopLevelUIElementAttribute,
+                        NSAccessibilityPositionAttribute,
+                        NSAccessibilitySizeAttribute, nil];
+  }
+  return attributes;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute {
+  NSObject* typed_parent = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(parent_);
+  if (!node_)
+    return nil;
+  if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+    return [self axRole];
+  } else if ([attribute
+                 isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
+    return NSAccessibilityRoleDescription([self axRole], nil);
+  } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+    // Just check if the app thinks we're focused.
+    id focusedElement = [NSApp
+        accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
+    return [NSNumber numberWithBool:[focusedElement isEqual:self]];
+  } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
+    return NSAccessibilityUnignoredAncestor(typed_parent);
+  } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+    return NSAccessibilityUnignoredChildren([self getKids]);
+  } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
+    // We're in the same window as our parent.
+    return [typed_parent
+        accessibilityAttributeValue:NSAccessibilityWindowAttribute];
+  } else if ([attribute
+                 isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
+    // We're in the same top level element as our parent.
+    return [typed_parent
+        accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
+  } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
+    return [NSValue valueWithPoint:[self position]];
+  } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
+    return [NSValue valueWithSize:[self size]];
+  } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
+    return [self axDescription];
+  } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
+    return [self axValue];
+  } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
+    return [self axName];
+  }
+  return nil;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point {
+  return NSAccessibilityUnignoredAncestor(self);
+}
+
+- (NSArray*)accessibilityActionNames {
+  return [NSArray arrayWithObject:NSAccessibilityPressAction];
+}
+
+- (NSString*)accessibilityActionDescription:(NSString*)action {
+  return NSAccessibilityActionDescription(action);
+}
+
+- (void)accessibilityPerformAction:(NSString*)action {
+  if ([action isEqualToString:NSAccessibilityPressAction]) {
+    // Do Click on Default action
+    [self doMouseClick:MBT_LEFT];
+  } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
+    // Right click for Context Menu
+    [self doMouseClick:MBT_RIGHT];
+  }
+}
+
+- (id)accessibilityFocusedUIElement {
+  return NSAccessibilityUnignoredAncestor(self);
+}
+
+- (BOOL)accessibilityNotifiesWhenDestroyed {
+  // Indicate that BrowserAccessibilityCocoa will post a notification when it's
+  // destroyed (see -detach). This allows VoiceOver to do some internal things
+  // more efficiently.
+  return YES;
+}
+
+@end
+
+namespace client {
+
+void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {
+  NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(GetWindowHandle());
+  if (event_type == "focus") {
+    NSAccessibilityPostNotification(
+        view, NSAccessibilityFocusedUIElementChangedNotification);
+  } else if (event_type == "textChanged") {
+    NSAccessibilityPostNotification(view,
+                                    NSAccessibilityTitleChangedNotification);
+  } else if (event_type == "valueChanged") {
+    NSAccessibilityPostNotification(view,
+                                    NSAccessibilityValueChangedNotification);
+  } else if (event_type == "textSelectionChanged") {
+    NSAccessibilityPostNotification(view,
+                                    NSAccessibilityValueChangedNotification);
+  }
+}
+
+void OsrAXNode::Destroy() {
+  if (platform_accessibility_) {
+    NSAccessibilityPostNotification(
+        CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(platform_accessibility_),
+        NSAccessibilityUIElementDestroyedNotification);
+  }
+
+  delete this;
+}
+
+// Create and return NSAccessibility Implementation Object for Mac
+CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(
+    client::OsrAXNode* parent) {
+  if (!platform_accessibility_) {
+    platform_accessibility_ = CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(
+        [OsrAXNodeObject elementWithNode:this]);
+    SetParent(parent);
+  }
+  return platform_accessibility_;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_accessibility_node_win.cc b/src/tests/cefclient/browser/osr_accessibility_node_win.cc
new file mode 100644
index 0000000..a96889a
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_accessibility_node_win.cc
@@ -0,0 +1,695 @@
+// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+// This class implements our accessible proxy object that handles moving
+// data back and forth between MSAA clients and CefClient renderers.
+// Sample implementation based on ui\accessibility\ax_platform_node_win.h
+
+#include "tests/cefclient/browser/osr_accessibility_node.h"
+
+#if defined(CEF_USE_ATL)
+
+#include <atlbase.h>
+#include <oleacc.h>
+#include <string>
+
+#include "tests/cefclient/browser/osr_accessibility_helper.h"
+
+namespace client {
+
+// Return CO_E_OBJNOTCONNECTED for accessible objects thar still exists but the
+// window and/or object it references has been destroyed.
+#define DATACHECK(node) (node) ? S_OK : CO_E_OBJNOTCONNECTED
+#define VALID_CHILDID(varChild) ((varChild.vt == VT_I4))
+
+namespace {
+
+// Helper function to convert a rectangle from client coordinates to screen
+// coordinates.
+void ClientToScreen(HWND hwnd, LPRECT lpRect) {
+  if (lpRect) {
+    POINT ptTL = {lpRect->left, lpRect->top};
+    POINT ptBR = {lpRect->right, lpRect->bottom};
+    // Win32 API only provides the call for a point.
+    ClientToScreen(hwnd, &ptTL);
+    ClientToScreen(hwnd, &ptBR);
+    SetRect(lpRect, ptTL.x, ptTL.y, ptBR.x, ptBR.y);
+  }
+}
+
+// Helper function to convert to MSAARole
+int AxRoleToMSAARole(const std::string& role_string) {
+  if (role_string == "alert")
+    return ROLE_SYSTEM_ALERT;
+  if (role_string == "application")
+    return ROLE_SYSTEM_APPLICATION;
+  if (role_string == "buttonDropDown")
+    return ROLE_SYSTEM_BUTTONDROPDOWN;
+  if (role_string == "popUpButton")
+    return ROLE_SYSTEM_BUTTONMENU;
+  if (role_string == "checkBox")
+    return ROLE_SYSTEM_CHECKBUTTON;
+  if (role_string == "comboBox")
+    return ROLE_SYSTEM_COMBOBOX;
+  if (role_string == "dialog")
+    return ROLE_SYSTEM_DIALOG;
+  if (role_string == "genericContainer")
+    return ROLE_SYSTEM_GROUPING;
+  if (role_string == "group")
+    return ROLE_SYSTEM_GROUPING;
+  if (role_string == "image")
+    return ROLE_SYSTEM_GRAPHIC;
+  if (role_string == "link")
+    return ROLE_SYSTEM_LINK;
+  if (role_string == "locationBar")
+    return ROLE_SYSTEM_GROUPING;
+  if (role_string == "menuBar")
+    return ROLE_SYSTEM_MENUBAR;
+  if (role_string == "menuItem")
+    return ROLE_SYSTEM_MENUITEM;
+  if (role_string == "menuListPopup")
+    return ROLE_SYSTEM_MENUPOPUP;
+  if (role_string == "tree")
+    return ROLE_SYSTEM_OUTLINE;
+  if (role_string == "treeItem")
+    return ROLE_SYSTEM_OUTLINEITEM;
+  if (role_string == "tab")
+    return ROLE_SYSTEM_PAGETAB;
+  if (role_string == "tabList")
+    return ROLE_SYSTEM_PAGETABLIST;
+  if (role_string == "pane")
+    return ROLE_SYSTEM_PANE;
+  if (role_string == "progressIndicator")
+    return ROLE_SYSTEM_PROGRESSBAR;
+  if (role_string == "button")
+    return ROLE_SYSTEM_PUSHBUTTON;
+  if (role_string == "radioButton")
+    return ROLE_SYSTEM_RADIOBUTTON;
+  if (role_string == "scrollBar")
+    return ROLE_SYSTEM_SCROLLBAR;
+  if (role_string == "splitter")
+    return ROLE_SYSTEM_SEPARATOR;
+  if (role_string == "slider")
+    return ROLE_SYSTEM_SLIDER;
+  if (role_string == "staticText")
+    return ROLE_SYSTEM_STATICTEXT;
+  if (role_string == "textField")
+    return ROLE_SYSTEM_TEXT;
+  if (role_string == "titleBar")
+    return ROLE_SYSTEM_TITLEBAR;
+  if (role_string == "toolbar")
+    return ROLE_SYSTEM_TOOLBAR;
+  if (role_string == "webView")
+    return ROLE_SYSTEM_GROUPING;
+  if (role_string == "window")
+    return ROLE_SYSTEM_WINDOW;
+  if (role_string == "client")
+    return ROLE_SYSTEM_CLIENT;
+  // This is the default role for MSAA.
+  return ROLE_SYSTEM_CLIENT;
+}
+
+static inline int MiddleX(const CefRect& rect) {
+  return rect.x + rect.width / 2;
+}
+
+static inline int MiddleY(const CefRect& rect) {
+  return rect.y + rect.height / 2;
+}
+
+}  // namespace
+
+struct CefIAccessible : public IAccessible {
+ public:
+  // Implement IUnknown
+  STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override;
+  STDMETHODIMP_(ULONG) AddRef() override;
+  STDMETHODIMP_(ULONG) Release() override;
+
+  //
+  // IAccessible methods.
+  //
+  // Retrieves the child element or child object at a given point on the screen.
+  STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child) override;
+
+  // Performs the object's default action.
+  STDMETHODIMP accDoDefaultAction(VARIANT var_id) override;
+
+  // Retrieves the specified object's current screen location.
+  STDMETHODIMP accLocation(LONG* x_left,
+                           LONG* y_top,
+                           LONG* width,
+                           LONG* height,
+                           VARIANT var_id) override;
+
+  // Traverses to another UI element and retrieves the object.
+  STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end) override;
+
+  // Retrieves an IDispatch interface pointer for the specified child.
+  STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child) override;
+
+  // Retrieves the number of accessible children.
+  STDMETHODIMP get_accChildCount(LONG* child_count) override;
+
+  // Retrieves a string that describes the object's default action.
+  STDMETHODIMP get_accDefaultAction(VARIANT var_id,
+                                    BSTR* default_action) override;
+
+  // Retrieves the tooltip description.
+  STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override;
+
+  // Retrieves the object that has the keyboard focus.
+  STDMETHODIMP get_accFocus(VARIANT* focus_child) override;
+
+  // Retrieves the specified object's shortcut.
+  STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id,
+                                       BSTR* access_key) override;
+
+  // Retrieves the name of the specified object.
+  STDMETHODIMP get_accName(VARIANT var_id, BSTR* name) override;
+
+  // Retrieves the IDispatch interface of the object's parent.
+  STDMETHODIMP get_accParent(IDispatch** disp_parent) override;
+
+  // Retrieves information describing the role of the specified object.
+  STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override;
+
+  // Retrieves the current state of the specified object.
+  STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override;
+
+  // Gets the help string for the specified object.
+  STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override;
+
+  // Retrieve or set the string value associated with the specified object.
+  // Setting the value is not typically used by screen readers, but it's
+  // used frequently by automation software.
+  STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override;
+  STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override;
+
+  // IAccessible methods not implemented.
+  STDMETHODIMP get_accSelection(VARIANT* selected) override;
+  STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override;
+  STDMETHODIMP get_accHelpTopic(BSTR* help_file,
+                                VARIANT var_id,
+                                LONG* topic_id) override;
+  STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override;
+
+  // Implement IDispatch
+  STDMETHODIMP GetTypeInfoCount(unsigned int FAR* pctinfo) override;
+  STDMETHODIMP GetTypeInfo(unsigned int iTInfo,
+                           LCID lcid,
+                           ITypeInfo FAR* FAR* ppTInfo) override;
+  STDMETHODIMP GetIDsOfNames(REFIID riid,
+                             OLECHAR FAR* FAR* rgszNames,
+                             unsigned int cNames,
+                             LCID lcid,
+                             DISPID FAR* rgDispId) override;
+  STDMETHODIMP Invoke(DISPID dispIdMember,
+                      REFIID riid,
+                      LCID lcid,
+                      WORD wFlags,
+                      DISPPARAMS FAR* pDispParams,
+                      VARIANT FAR* pVarResult,
+                      EXCEPINFO FAR* pExcepInfo,
+                      unsigned int FAR* puArgErr) override;
+
+  CefIAccessible(OsrAXNode* node) : ref_count_(0), node_(node) {}
+
+  // Remove the node reference when OsrAXNode is destroyed, so that
+  // MSAA clients get  CO_E_OBJNOTCONNECTED
+  void MarkDestroyed() { node_ = nullptr; }
+
+ protected:
+  virtual ~CefIAccessible() {}
+
+  // Ref Count
+  ULONG ref_count_;
+  // OsrAXNode* proxy object
+  OsrAXNode* node_;
+};
+
+// Implement IUnknown
+// *********************
+
+// Handles ref counting and querying for other supported interfaces.
+// We only support, IUnknown, IDispatch and IAccessible.
+STDMETHODIMP CefIAccessible::QueryInterface(REFIID riid, void** ppvObject) {
+  if (riid == IID_IAccessible)
+    *ppvObject = static_cast<IAccessible*>(this);
+  else if (riid == IID_IDispatch)
+    *ppvObject = static_cast<IDispatch*>(this);
+  else if (riid == IID_IUnknown)
+    *ppvObject = static_cast<IUnknown*>(this);
+  else
+    *ppvObject = nullptr;
+
+  if (*ppvObject)
+    reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
+
+  return (*ppvObject) ? S_OK : E_NOINTERFACE;
+}
+
+// Increments COM objects refcount required by IUnknown for reference counting
+STDMETHODIMP_(ULONG) CefIAccessible::AddRef() {
+  return InterlockedIncrement((LONG volatile*)&ref_count_);
+}
+
+STDMETHODIMP_(ULONG) CefIAccessible::Release() {
+  ULONG ulRefCnt = InterlockedDecrement((LONG volatile*)&ref_count_);
+  if (ulRefCnt == 0) {
+    // Remove reference from OsrAXNode
+    if (node_)
+      node_->Destroy();
+    delete this;
+  }
+
+  return ulRefCnt;
+}
+
+// Implement IAccessible
+// *********************
+
+// Returns the parent IAccessible in the form of an IDispatch interface.
+STDMETHODIMP CefIAccessible::get_accParent(IDispatch** ppdispParent) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (ppdispParent) {
+      CefNativeAccessible* parent = node_->GetParentAccessibleObject();
+      if (!parent) {
+        // Find our parent window
+        HWND hWnd = ::GetParent(node_->GetWindowHandle());
+        // if we have a window attempt to get its IAccessible pointer
+        if (hWnd) {
+          AccessibleObjectFromWindow(hWnd, (DWORD)OBJID_CLIENT, IID_IAccessible,
+                                     (void**)(&parent));
+        }
+      }
+
+      if (parent)
+        parent->AddRef();
+      *ppdispParent = parent;
+      retCode = (*ppdispParent) ? S_OK : S_FALSE;
+    }
+  } else {
+    retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// Returns the number of children we have for this element.
+STDMETHODIMP CefIAccessible::get_accChildCount(long* pcountChildren) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode) && pcountChildren) {
+    // Get Child node count for this from Accessibility tree
+    *pcountChildren = node_->GetChildCount();
+  } else {
+    retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// Returns a child IAccessible object.
+STDMETHODIMP CefIAccessible::get_accChild(VARIANT varChild,
+                                          IDispatch** ppdispChild) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    int numChilds = node_->GetChildCount();
+    // Mark Leaf node if there are no child
+    if (numChilds <= 0) {
+      *ppdispChild = nullptr;
+      return S_FALSE;
+    } else {
+      if (ppdispChild && VALID_CHILDID(varChild)) {
+        if (varChild.lVal == CHILDID_SELF) {
+          *ppdispChild = this;
+        } else {
+          // Convert to 0 based index and get Child Node.
+          OsrAXNode* child = node_->ChildAtIndex(varChild.lVal - 1);
+          // Fallback to focused node
+          if (!child)
+            child = node_->GetAccessibilityHelper()->GetFocusedNode();
+
+          *ppdispChild = child->GetNativeAccessibleObject(node_);
+        }
+        if (*ppdispChild == nullptr)
+          retCode = S_FALSE;
+        else
+          (*ppdispChild)->AddRef();
+      }
+    }
+  }
+  return retCode;
+}
+
+// Check and returns the accessible name for element from accessibility tree
+STDMETHODIMP CefIAccessible::get_accName(VARIANT varChild, BSTR* pszName) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pszName && VALID_CHILDID(varChild)) {
+      std::wstring name = node_->AxName();
+      CComBSTR bstrResult(name.c_str());
+      *pszName = bstrResult.Detach();
+    }
+  } else {
+    retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// Check and returns the value for element from accessibility tree
+STDMETHODIMP CefIAccessible::get_accValue(VARIANT varChild, BSTR* pszValue) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pszValue && VALID_CHILDID(varChild)) {
+      std::wstring name = node_->AxValue();
+      CComBSTR bstrResult(name.c_str());
+      *pszValue = bstrResult.Detach();
+    }
+  } else {
+    retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// Check and returns the description for element from accessibility tree
+STDMETHODIMP CefIAccessible::get_accDescription(VARIANT varChild,
+                                                BSTR* pszDescription) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pszDescription && VALID_CHILDID(varChild)) {
+      std::wstring name = node_->AxDescription();
+      CComBSTR bstrResult(name.c_str());
+      *pszDescription = bstrResult.Detach();
+    }
+  } else {
+    retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// Check and returns the MSAA Role for element from accessibility tree
+STDMETHODIMP CefIAccessible::get_accRole(VARIANT varChild, VARIANT* pvarRole) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    // Get the accessibilty role and Map to MSAA Role
+    if (pvarRole) {
+      pvarRole->vt = VT_I4;
+      pvarRole->lVal = AxRoleToMSAARole(node_->AxRole());
+    } else {
+      retCode = E_INVALIDARG;
+    }
+  }
+  return retCode;
+}
+
+// Check and returns Accessibility State for element from accessibility tree
+STDMETHODIMP CefIAccessible::get_accState(VARIANT varChild,
+                                          VARIANT* pvarState) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pvarState) {
+      pvarState->vt = VT_I4;
+      pvarState->lVal =
+          (GetFocus() == node_->GetWindowHandle()) ? STATE_SYSTEM_FOCUSED : 0;
+      pvarState->lVal |= STATE_SYSTEM_PRESSED;
+      pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
+
+      // For child
+      if (varChild.lVal == CHILDID_SELF) {
+        DWORD dwStyle = GetWindowLong(node_->GetWindowHandle(), GWL_STYLE);
+        pvarState->lVal |=
+            ((dwStyle & WS_VISIBLE) == 0) ? STATE_SYSTEM_INVISIBLE : 0;
+        pvarState->lVal |=
+            ((dwStyle & WS_DISABLED) > 0) ? STATE_SYSTEM_UNAVAILABLE : 0;
+      }
+    } else {
+      retCode = E_INVALIDARG;
+    }
+  }
+  return retCode;
+}
+
+// Check and returns Accessibility Shortcut if any for element
+STDMETHODIMP CefIAccessible::get_accKeyboardShortcut(
+    VARIANT varChild,
+    BSTR* pszKeyboardShortcut) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pszKeyboardShortcut && VALID_CHILDID(varChild))
+      *pszKeyboardShortcut = ::SysAllocString(L"None");
+    else
+      retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// Return focused element from the accessibility tree
+STDMETHODIMP CefIAccessible::get_accFocus(VARIANT* pFocusChild) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    OsrAXNode* focusedNode = node_->GetAccessibilityHelper()->GetFocusedNode();
+    CefNativeAccessible* nativeObj = nullptr;
+    if (focusedNode)
+      nativeObj = focusedNode->GetNativeAccessibleObject(nullptr);
+
+    if (nativeObj) {
+      if (nativeObj == this) {
+        pFocusChild->vt = VT_I4;
+        pFocusChild->lVal = CHILDID_SELF;
+      } else {
+        pFocusChild->vt = VT_DISPATCH;
+        pFocusChild->pdispVal = nativeObj;
+        pFocusChild->pdispVal->AddRef();
+      }
+    } else {
+      pFocusChild->vt = VT_EMPTY;
+    }
+  }
+  return retCode;
+}
+
+// Return a selection list for multiple selection items.
+STDMETHODIMP CefIAccessible::get_accSelection(VARIANT* pvarChildren) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pvarChildren)
+      pvarChildren->vt = VT_EMPTY;
+    else
+      retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// Return a string description of the default action of our element, eg. push
+STDMETHODIMP CefIAccessible::get_accDefaultAction(VARIANT varChild,
+                                                  BSTR* pszDefaultAction) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pszDefaultAction && VALID_CHILDID(varChild))
+      *pszDefaultAction = ::SysAllocString(L"Push");
+    else
+      retCode = E_INVALIDARG;
+  }
+  return retCode;
+}
+
+// child item selectionor for an item to take focus.
+STDMETHODIMP CefIAccessible::accSelect(long flagsSelect, VARIANT varChild) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (VALID_CHILDID(varChild)) {
+      HWND hwnd = node_->GetWindowHandle();
+      // we only support SELFLAG_TAKEFOCUS.
+      if (((flagsSelect & SELFLAG_TAKEFOCUS) > 0) && (GetFocus() == hwnd)) {
+        RECT rcWnd;
+        GetClientRect(hwnd, &rcWnd);
+        InvalidateRect(hwnd, &rcWnd, FALSE);
+      } else {
+        retCode = S_FALSE;
+      }
+    } else {
+      retCode = E_INVALIDARG;
+    }
+  }
+
+  return retCode;
+}
+
+// Returns back the screen coordinates of our element or one of its childs
+STDMETHODIMP CefIAccessible::accLocation(long* pxLeft,
+                                         long* pyTop,
+                                         long* pcxWidth,
+                                         long* pcyHeight,
+                                         VARIANT varChild) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pxLeft && pyTop && pcxWidth && pcyHeight && VALID_CHILDID(varChild)) {
+      CefRect loc = node_->AxLocation();
+      RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height};
+      HWND hwnd = node_->GetWindowHandle();
+      ClientToScreen(hwnd, &rcItem);
+
+      *pxLeft = rcItem.left;
+      *pyTop = rcItem.top;
+      *pcxWidth = rcItem.right - rcItem.left;
+      *pcyHeight = rcItem.bottom - rcItem.top;
+    } else {
+      retCode = E_INVALIDARG;
+    }
+  }
+  return retCode;
+}
+
+// Allow clients to move the keyboard focus within the control
+// Deprecated
+STDMETHODIMP CefIAccessible::accNavigate(long navDir,
+                                         VARIANT varStart,
+                                         VARIANT* pvarEndUpAt) {
+  return E_NOTIMPL;
+}
+
+// Check if the coordinates provided are within our element or child items.
+STDMETHODIMP CefIAccessible::accHitTest(long xLeft,
+                                        long yTop,
+                                        VARIANT* pvarChild) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode)) {
+    if (pvarChild) {
+      pvarChild->vt = VT_EMPTY;
+
+      CefRect loc = node_->AxLocation();
+      RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height};
+      POINT pt = {xLeft, yTop};
+
+      ClientToScreen(node_->GetWindowHandle(), &rcItem);
+
+      if (PtInRect(&rcItem, pt)) {
+        pvarChild->vt = VT_I4;
+        pvarChild->lVal = 1;
+      }
+    } else {
+      retCode = E_INVALIDARG;
+    }
+  }
+
+  return retCode;
+}
+
+// Forces the default action of our element. In simplest cases, send a click.
+STDMETHODIMP CefIAccessible::accDoDefaultAction(VARIANT varChild) {
+  HRESULT retCode = DATACHECK(node_);
+  if (SUCCEEDED(retCode) && VALID_CHILDID(varChild)) {
+    // doing our default action for out button is to simply click the button.
+    CefRefPtr<CefBrowser> browser = node_->GetBrowser();
+    if (browser) {
+      CefMouseEvent mouse_event;
+      const CefRect& rect = node_->AxLocation();
+      mouse_event.x = MiddleX(rect);
+      mouse_event.y = MiddleY(rect);
+
+      mouse_event.modifiers = 0;
+      browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1);
+      browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
+    }
+  } else {
+    retCode = E_INVALIDARG;
+  }
+
+  return retCode;
+}
+
+// Set the name for an element in the accessibility tree
+STDMETHODIMP CefIAccessible::put_accName(VARIANT varChild, BSTR szName) {
+  return E_NOTIMPL;
+}
+
+// Set the value for an element in the accessibility tree
+STDMETHODIMP CefIAccessible::put_accValue(VARIANT varChild, BSTR szValue) {
+  return E_NOTIMPL;
+}
+
+// Return E_NOTIMPL as no help file/ topic
+STDMETHODIMP CefIAccessible::get_accHelp(VARIANT varChild, BSTR* pszHelp) {
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CefIAccessible::get_accHelpTopic(BSTR* pszHelpFile,
+                                              VARIANT varChild,
+                                              long* pidTopic) {
+  return E_NOTIMPL;
+}
+
+// IDispatch - We are not going to return E_NOTIMPL from IDispatch methods and
+// let Active Accessibility implement the IAccessible interface for them.
+STDMETHODIMP CefIAccessible::GetTypeInfoCount(unsigned int FAR* pctinfo) {
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CefIAccessible::GetTypeInfo(unsigned int iTInfo,
+                                         LCID lcid,
+                                         ITypeInfo FAR* FAR* ppTInfo) {
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CefIAccessible::GetIDsOfNames(REFIID riid,
+                                           OLECHAR FAR* FAR* rgszNames,
+                                           unsigned int cNames,
+                                           LCID lcid,
+                                           DISPID FAR* rgDispId) {
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CefIAccessible::Invoke(DISPID dispIdMember,
+                                    REFIID riid,
+                                    LCID lcid,
+                                    WORD wFlags,
+                                    DISPPARAMS FAR* pDispParams,
+                                    VARIANT FAR* pVarResult,
+                                    EXCEPINFO FAR* pExcepInfo,
+                                    unsigned int FAR* puArgErr) {
+  return E_NOTIMPL;
+}
+
+void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {
+  if (event_type == "focus") {
+    // Notify Screen Reader of focus change
+    ::NotifyWinEvent(EVENT_OBJECT_FOCUS, GetWindowHandle(), OBJID_CLIENT,
+                     node_id_);
+  }
+}
+
+void OsrAXNode::Destroy() {
+  CefIAccessible* ptr = static_cast<CefIAccessible*>(platform_accessibility_);
+  if (ptr)
+    ptr->MarkDestroyed();
+  platform_accessibility_ = nullptr;
+}
+
+// Create and return NSAccessibility Implementation Object for Window
+CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
+  if (!platform_accessibility_) {
+    platform_accessibility_ = new CefIAccessible(this);
+    platform_accessibility_->AddRef();
+    SetParent(parent);
+  }
+  return platform_accessibility_;
+}
+
+}  // namespace client
+
+#else  // !defined(CEF_USE_ATL)
+
+namespace client {
+
+void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {}
+
+void OsrAXNode::Destroy() {}
+
+CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
+  return nullptr;
+}
+
+}  // namespace client
+
+#endif  // !defined(CEF_USE_ATL)
diff --git a/src/tests/cefclient/browser/osr_d3d11_win.cc b/src/tests/cefclient/browser/osr_d3d11_win.cc
new file mode 100644
index 0000000..ca23d07
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_d3d11_win.cc
@@ -0,0 +1,934 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+//
+// Portions Copyright (c) 2018 Daktronics with the following MIT License:
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+
+#include "tests/cefclient/browser/osr_d3d11_win.h"
+
+#include <iomanip>  // For std::setw.
+
+#if OS_WIN && ARCH_CPU_ARM_FAMILY
+#define __prefetch(x) x
+#endif
+#include <d3dcompiler.h>
+#include <directxmath.h>
+
+#include "include/base/cef_logging.h"
+#include "include/internal/cef_string.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+namespace d3d11 {
+
+namespace {
+
+// Wrap a raw COM pointer in a shared_ptr for auto Release().
+template <class T>
+std::shared_ptr<T> to_com_ptr(T* obj) {
+  return std::shared_ptr<T>(obj, [](T* p) {
+    if (p)
+      p->Release();
+  });
+}
+
+}  // namespace
+
+struct SimpleVertex {
+  DirectX::XMFLOAT3 pos;
+  DirectX::XMFLOAT2 tex;
+};
+
+Context::Context(ID3D11DeviceContext* ctx) : ctx_(to_com_ptr(ctx)) {}
+
+void Context::flush() {
+  ctx_->Flush();
+}
+
+SwapChain::SwapChain(IDXGISwapChain* swapchain,
+                     ID3D11RenderTargetView* rtv,
+                     ID3D11SamplerState* sampler,
+                     ID3D11BlendState* blender)
+    : sampler_(to_com_ptr(sampler)),
+      blender_(to_com_ptr(blender)),
+      swapchain_(to_com_ptr(swapchain)),
+      rtv_(to_com_ptr(rtv)),
+      width_(0),
+      height_(0) {}
+
+void SwapChain::bind(const std::shared_ptr<Context>& ctx) {
+  ctx_ = ctx;
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+
+  ID3D11RenderTargetView* rtv[1] = {rtv_.get()};
+  d3d11_ctx->OMSetRenderTargets(1, rtv, nullptr);
+
+  // Set default blending state.
+  if (blender_) {
+    float factor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+    d3d11_ctx->OMSetBlendState(blender_.get(), factor, 0xffffffff);
+  }
+
+  // Set default sampler state.
+  if (sampler_) {
+    ID3D11SamplerState* samplers[1] = {sampler_.get()};
+    d3d11_ctx->PSSetSamplers(0, 1, samplers);
+  }
+}
+
+void SwapChain::unbind() {
+  ctx_.reset();
+}
+
+void SwapChain::clear(float red, float green, float blue, float alpha) {
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+  CHECK(d3d11_ctx);
+
+  FLOAT color[4] = {red, green, blue, alpha};
+  d3d11_ctx->ClearRenderTargetView(rtv_.get(), color);
+}
+
+void SwapChain::present(int sync_interval) {
+  swapchain_->Present(sync_interval, 0);
+}
+
+void SwapChain::resize(int width, int height) {
+  if (width <= 0 || height <= 0 || width == width_ || height == height_) {
+    return;
+  }
+  width_ = width;
+  height_ = height;
+
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+  CHECK(d3d11_ctx);
+
+  d3d11_ctx->OMSetRenderTargets(0, 0, 0);
+  rtv_.reset();
+
+  DXGI_SWAP_CHAIN_DESC desc;
+  swapchain_->GetDesc(&desc);
+  auto hr = swapchain_->ResizeBuffers(0, width, height, desc.BufferDesc.Format,
+                                      desc.Flags);
+  if (FAILED(hr)) {
+    LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x"
+               << height << ")";
+    return;
+  }
+
+  ID3D11Texture2D* buffer = nullptr;
+  hr = swapchain_->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&buffer);
+  if (FAILED(hr)) {
+    LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x"
+               << height << ")";
+    return;
+  }
+
+  ID3D11Device* dev = nullptr;
+  d3d11_ctx->GetDevice(&dev);
+  if (dev) {
+    D3D11_RENDER_TARGET_VIEW_DESC vdesc = {};
+    vdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+    vdesc.Texture2D.MipSlice = 0;
+    vdesc.Format = desc.BufferDesc.Format;
+
+    ID3D11RenderTargetView* view = nullptr;
+    hr = dev->CreateRenderTargetView(buffer, &vdesc, &view);
+    if (SUCCEEDED(hr)) {
+      rtv_ = to_com_ptr(view);
+      d3d11_ctx->OMSetRenderTargets(1, &view, nullptr);
+    }
+    dev->Release();
+  }
+  buffer->Release();
+
+  D3D11_VIEWPORT vp;
+  vp.Width = static_cast<float>(width);
+  vp.Height = static_cast<float>(height);
+  vp.MinDepth = D3D11_MIN_DEPTH;
+  vp.MaxDepth = D3D11_MAX_DEPTH;
+  vp.TopLeftX = 0;
+  vp.TopLeftY = 0;
+  d3d11_ctx->RSSetViewports(1, &vp);
+}
+
+Effect::Effect(ID3D11VertexShader* vsh,
+               ID3D11PixelShader* psh,
+               ID3D11InputLayout* layout)
+    : vsh_(to_com_ptr(vsh)),
+      psh_(to_com_ptr(psh)),
+      layout_(to_com_ptr(layout)) {}
+
+void Effect::bind(const std::shared_ptr<Context>& ctx) {
+  ctx_ = ctx;
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+
+  d3d11_ctx->IASetInputLayout(layout_.get());
+  d3d11_ctx->VSSetShader(vsh_.get(), nullptr, 0);
+  d3d11_ctx->PSSetShader(psh_.get(), nullptr, 0);
+}
+
+void Effect::unbind() {}
+
+Geometry::Geometry(D3D_PRIMITIVE_TOPOLOGY primitive,
+                   uint32_t vertices,
+                   uint32_t stride,
+                   ID3D11Buffer* buffer)
+    : primitive_(primitive),
+      vertices_(vertices),
+      stride_(stride),
+      buffer_(to_com_ptr(buffer)) {}
+
+void Geometry::bind(const std::shared_ptr<Context>& ctx) {
+  ctx_ = ctx;
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+
+  // TODO: Handle offset.
+  uint32_t offset = 0;
+
+  ID3D11Buffer* buffers[1] = {buffer_.get()};
+  d3d11_ctx->IASetVertexBuffers(0, 1, buffers, &stride_, &offset);
+  d3d11_ctx->IASetPrimitiveTopology(primitive_);
+}
+
+void Geometry::unbind() {}
+
+void Geometry::draw() {
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+  CHECK(d3d11_ctx);
+
+  // TODO: Handle offset.
+  d3d11_ctx->Draw(vertices_, 0);
+}
+
+Texture2D::Texture2D(ID3D11Texture2D* tex, ID3D11ShaderResourceView* srv)
+    : texture_(to_com_ptr(tex)), srv_(to_com_ptr(srv)) {
+  share_handle_ = nullptr;
+
+  IDXGIResource* res = nullptr;
+  if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIResource),
+                                         reinterpret_cast<void**>(&res)))) {
+    res->GetSharedHandle(&share_handle_);
+    res->Release();
+  }
+
+  // Are we using a keyed mutex?
+  IDXGIKeyedMutex* mutex = nullptr;
+  if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIKeyedMutex),
+                                         (void**)&mutex))) {
+    keyed_mutex_ = to_com_ptr(mutex);
+  }
+}
+
+uint32_t Texture2D::width() const {
+  D3D11_TEXTURE2D_DESC desc;
+  texture_->GetDesc(&desc);
+  return desc.Width;
+}
+
+uint32_t Texture2D::height() const {
+  D3D11_TEXTURE2D_DESC desc;
+  texture_->GetDesc(&desc);
+  return desc.Height;
+}
+
+DXGI_FORMAT Texture2D::format() const {
+  D3D11_TEXTURE2D_DESC desc;
+  texture_->GetDesc(&desc);
+  return desc.Format;
+}
+
+bool Texture2D::has_mutex() const {
+  return (keyed_mutex_.get() != nullptr);
+}
+
+bool Texture2D::lock_key(uint64_t key, uint32_t timeout_ms) {
+  if (keyed_mutex_) {
+    const auto hr = keyed_mutex_->AcquireSync(key, timeout_ms);
+    return (hr == S_OK);
+  }
+  return true;
+}
+
+void Texture2D::unlock_key(uint64_t key) {
+  if (keyed_mutex_) {
+    keyed_mutex_->ReleaseSync(key);
+  }
+}
+
+void Texture2D::bind(const std::shared_ptr<Context>& ctx) {
+  ctx_ = ctx;
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+  if (srv_) {
+    ID3D11ShaderResourceView* views[1] = {srv_.get()};
+    d3d11_ctx->PSSetShaderResources(0, 1, views);
+  }
+}
+
+void Texture2D::unbind() {}
+
+void* Texture2D::share_handle() const {
+  return share_handle_;
+}
+
+void Texture2D::copy_from(const std::shared_ptr<Texture2D>& other) {
+  ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
+  CHECK(d3d11_ctx);
+  if (other) {
+    d3d11_ctx->CopyResource(texture_.get(), other->texture_.get());
+  }
+}
+
+Device::Device(ID3D11Device* pdev, ID3D11DeviceContext* pctx)
+    : device_(to_com_ptr(pdev)), ctx_(std::make_shared<Context>(pctx)) {
+  lib_compiler_ = LoadLibrary(L"d3dcompiler_47.dll");
+}
+
+// static
+std::shared_ptr<Device> Device::create() {
+  UINT flags = 0;
+#ifdef _DEBUG
+  flags |= D3D11_CREATE_DEVICE_DEBUG;
+#endif
+
+  D3D_FEATURE_LEVEL feature_levels[] = {
+      D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
+      D3D_FEATURE_LEVEL_10_0,
+      // D3D_FEATURE_LEVEL_9_3,
+  };
+  UINT num_feature_levels = sizeof(feature_levels) / sizeof(feature_levels[0]);
+
+  ID3D11Device* pdev = nullptr;
+  ID3D11DeviceContext* pctx = nullptr;
+
+  D3D_FEATURE_LEVEL selected_level;
+  HRESULT hr = D3D11CreateDevice(
+      nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, feature_levels,
+      num_feature_levels, D3D11_SDK_VERSION, &pdev, &selected_level, &pctx);
+
+  if (hr == E_INVALIDARG) {
+    // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1
+    // so we need to retry without it.
+    hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags,
+                           &feature_levels[1], num_feature_levels - 1,
+                           D3D11_SDK_VERSION, &pdev, &selected_level, &pctx);
+  }
+
+  if (SUCCEEDED(hr)) {
+    const auto dev = std::make_shared<Device>(pdev, pctx);
+
+    LOG(INFO) << "d3d11: Selected adapter " << dev->adapter_name()
+              << " and feature level 0x" << std::setw(4) << std::hex
+              << selected_level;
+
+    return dev;
+  }
+
+  return nullptr;
+}
+
+std::string Device::adapter_name() const {
+  IDXGIDevice* dxgi_dev = nullptr;
+  auto hr = device_->QueryInterface(__uuidof(dxgi_dev), (void**)&dxgi_dev);
+  if (SUCCEEDED(hr)) {
+    IDXGIAdapter* dxgi_adapt = nullptr;
+    hr = dxgi_dev->GetAdapter(&dxgi_adapt);
+    dxgi_dev->Release();
+    if (SUCCEEDED(hr)) {
+      DXGI_ADAPTER_DESC desc;
+      hr = dxgi_adapt->GetDesc(&desc);
+      dxgi_adapt->Release();
+      if (SUCCEEDED(hr)) {
+        return CefString(desc.Description);
+      }
+    }
+  }
+
+  return "n/a";
+}
+
+std::shared_ptr<Context> Device::immedidate_context() {
+  return ctx_;
+}
+
+std::shared_ptr<SwapChain> Device::create_swapchain(HWND window,
+                                                    int width,
+                                                    int height) {
+  HRESULT hr;
+  IDXGIFactory1* dxgi_factory = nullptr;
+
+  // Default size to the window size unless specified.
+  RECT rc_bounds;
+  GetClientRect(window, &rc_bounds);
+  if (width <= 0) {
+    width = rc_bounds.right - rc_bounds.left;
+  }
+  if (height <= 0) {
+    height = rc_bounds.bottom - rc_bounds.top;
+  }
+
+  {
+    IDXGIDevice* dxgi_dev = nullptr;
+    hr = device_->QueryInterface(__uuidof(IDXGIDevice),
+                                 reinterpret_cast<void**>(&dxgi_dev));
+    if (FAILED(hr)) {
+      return nullptr;
+    }
+
+    IDXGIAdapter* adapter = nullptr;
+    hr = dxgi_dev->GetAdapter(&adapter);
+    dxgi_dev->Release();
+    if (FAILED(hr)) {
+      return nullptr;
+    }
+
+    hr = adapter->GetParent(__uuidof(IDXGIFactory1),
+                            reinterpret_cast<void**>(&dxgi_factory));
+    adapter->Release();
+  }
+
+  if (!dxgi_factory) {
+    return nullptr;
+  }
+
+  IDXGISwapChain* swapchain = nullptr;
+
+  // Create swap chain.
+  IDXGIFactory2* dxgi_factory2 = nullptr;
+  hr = dxgi_factory->QueryInterface(__uuidof(IDXGIFactory2),
+                                    reinterpret_cast<void**>(&dxgi_factory2));
+  if (dxgi_factory2) {
+    DXGI_SWAP_CHAIN_DESC1 sd;
+    ZeroMemory(&sd, sizeof(sd));
+    sd.Width = width;
+    sd.Height = height;
+    sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    sd.SampleDesc.Count = 1;
+    sd.SampleDesc.Quality = 0;
+    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    sd.BufferCount = 1;
+
+    IDXGISwapChain1* swapchain1 = nullptr;
+    hr = dxgi_factory2->CreateSwapChainForHwnd(device_.get(), window, &sd,
+                                               nullptr, nullptr, &swapchain1);
+    if (SUCCEEDED(hr)) {
+      hr = swapchain1->QueryInterface(__uuidof(IDXGISwapChain),
+                                      reinterpret_cast<void**>(&swapchain));
+      swapchain1->Release();
+    }
+
+    dxgi_factory2->Release();
+  } else {
+    // DirectX 11.0 systems.
+    DXGI_SWAP_CHAIN_DESC sd;
+    ZeroMemory(&sd, sizeof(sd));
+    sd.BufferCount = 1;
+    sd.BufferDesc.Width = width;
+    sd.BufferDesc.Height = height;
+    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    sd.BufferDesc.RefreshRate.Numerator = 60;
+    sd.BufferDesc.RefreshRate.Denominator = 1;
+    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    sd.OutputWindow = window;
+    sd.SampleDesc.Count = 1;
+    sd.SampleDesc.Quality = 0;
+    sd.Windowed = TRUE;
+
+    hr = dxgi_factory->CreateSwapChain(device_.get(), &sd, &swapchain);
+  }
+
+  // We don't handle full-screen swapchains so we block the ALT+ENTER shortcut.
+  dxgi_factory->MakeWindowAssociation(window, DXGI_MWA_NO_ALT_ENTER);
+  dxgi_factory->Release();
+
+  if (!swapchain) {
+    return nullptr;
+  }
+
+  ID3D11Texture2D* back_buffer = nullptr;
+  hr = swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D),
+                            reinterpret_cast<void**>(&back_buffer));
+  if (FAILED(hr)) {
+    swapchain->Release();
+    return nullptr;
+  }
+
+  ID3D11RenderTargetView* rtv = nullptr;
+  hr = device_->CreateRenderTargetView(back_buffer, nullptr, &rtv);
+  back_buffer->Release();
+  if (FAILED(hr)) {
+    swapchain->Release();
+    return nullptr;
+  }
+
+  const auto ctx = (ID3D11DeviceContext*)(*ctx_);
+
+  ctx->OMSetRenderTargets(1, &rtv, nullptr);
+
+  // Setup the viewport.
+  D3D11_VIEWPORT vp;
+  vp.Width = (FLOAT)width;
+  vp.Height = (FLOAT)height;
+  vp.MinDepth = 0.0f;
+  vp.MaxDepth = 1.0f;
+  vp.TopLeftX = 0;
+  vp.TopLeftY = 0;
+  ctx->RSSetViewports(1, &vp);
+
+  // Create a default sampler to use.
+  ID3D11SamplerState* sampler = nullptr;
+  {
+    D3D11_SAMPLER_DESC desc = {};
+    desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+    desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+    desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+    desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+    desc.MinLOD = 0.0f;
+    desc.MaxLOD = D3D11_FLOAT32_MAX;
+    desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+    device_->CreateSamplerState(&desc, &sampler);
+  }
+
+  // Create a default blend state to use (pre-multiplied alpha).
+  ID3D11BlendState* blender = nullptr;
+  {
+    D3D11_BLEND_DESC desc;
+    desc.AlphaToCoverageEnable = FALSE;
+    desc.IndependentBlendEnable = FALSE;
+    const auto count = sizeof(desc.RenderTarget) / sizeof(desc.RenderTarget[0]);
+    for (size_t n = 0; n < count; ++n) {
+      desc.RenderTarget[n].BlendEnable = TRUE;
+      desc.RenderTarget[n].SrcBlend = D3D11_BLEND_ONE;
+      desc.RenderTarget[n].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+      desc.RenderTarget[n].SrcBlendAlpha = D3D11_BLEND_ONE;
+      desc.RenderTarget[n].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
+      desc.RenderTarget[n].BlendOp = D3D11_BLEND_OP_ADD;
+      desc.RenderTarget[n].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+      desc.RenderTarget[n].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
+    }
+    device_->CreateBlendState(&desc, &blender);
+  }
+
+  return std::make_shared<SwapChain>(swapchain, rtv, sampler, blender);
+}
+
+std::shared_ptr<Geometry> Device::create_quad(float x,
+                                              float y,
+                                              float width,
+                                              float height,
+                                              bool flip) {
+  x = (x * 2.0f) - 1.0f;
+  y = 1.0f - (y * 2.0f);
+  width = width * 2.0f;
+  height = height * 2.0f;
+  float z = 1.0f;
+
+  SimpleVertex vertices[] = {
+
+      {DirectX::XMFLOAT3(x, y, z), DirectX::XMFLOAT2(0.0f, 0.0f)},
+      {DirectX::XMFLOAT3(x + width, y, z), DirectX::XMFLOAT2(1.0f, 0.0f)},
+      {DirectX::XMFLOAT3(x, y - height, z), DirectX::XMFLOAT2(0.0f, 1.0f)},
+      {DirectX::XMFLOAT3(x + width, y - height, z),
+       DirectX::XMFLOAT2(1.0f, 1.0f)}};
+
+  if (flip) {
+    DirectX::XMFLOAT2 tmp(vertices[2].tex);
+    vertices[2].tex = vertices[0].tex;
+    vertices[0].tex = tmp;
+
+    tmp = vertices[3].tex;
+    vertices[3].tex = vertices[1].tex;
+    vertices[1].tex = tmp;
+  }
+
+  D3D11_BUFFER_DESC desc = {};
+  desc.Usage = D3D11_USAGE_DEFAULT;
+  desc.ByteWidth = sizeof(SimpleVertex) * 4;
+  desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+  desc.CPUAccessFlags = 0;
+
+  D3D11_SUBRESOURCE_DATA srd = {};
+  srd.pSysMem = vertices;
+
+  ID3D11Buffer* buffer = nullptr;
+  const auto hr = device_->CreateBuffer(&desc, &srd, &buffer);
+  if (SUCCEEDED(hr)) {
+    return std::make_shared<Geometry>(
+        D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, 4,
+        static_cast<uint32_t>(sizeof(SimpleVertex)), buffer);
+  }
+
+  return nullptr;
+}
+
+std::shared_ptr<Texture2D> Device::open_shared_texture(void* handle) {
+  ID3D11Texture2D* tex = nullptr;
+  auto hr = device_->OpenSharedResource(handle, __uuidof(ID3D11Texture2D),
+                                        (void**)(&tex));
+  if (FAILED(hr)) {
+    return nullptr;
+  }
+
+  D3D11_TEXTURE2D_DESC td;
+  tex->GetDesc(&td);
+
+  ID3D11ShaderResourceView* srv = nullptr;
+
+  if (td.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
+    D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
+    srv_desc.Format = td.Format;
+    srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    srv_desc.Texture2D.MostDetailedMip = 0;
+    srv_desc.Texture2D.MipLevels = 1;
+
+    hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv);
+    if (FAILED(hr)) {
+      tex->Release();
+      return nullptr;
+    }
+  }
+
+  return std::make_shared<Texture2D>(tex, srv);
+}
+
+std::shared_ptr<Texture2D> Device::create_texture(int width,
+                                                  int height,
+                                                  DXGI_FORMAT format,
+                                                  const void* data,
+                                                  size_t row_stride) {
+  D3D11_TEXTURE2D_DESC td;
+  td.ArraySize = 1;
+  td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+  td.CPUAccessFlags = 0;
+  td.Format = format;
+  td.Width = width;
+  td.Height = height;
+  td.MipLevels = 1;
+  td.MiscFlags = 0;
+  td.SampleDesc.Count = 1;
+  td.SampleDesc.Quality = 0;
+  td.Usage = D3D11_USAGE_DEFAULT;
+
+  D3D11_SUBRESOURCE_DATA srd;
+  srd.pSysMem = data;
+  srd.SysMemPitch = static_cast<uint32_t>(row_stride);
+  srd.SysMemSlicePitch = 0;
+
+  ID3D11Texture2D* tex = nullptr;
+  auto hr = device_->CreateTexture2D(&td, data ? &srd : nullptr, &tex);
+  if (FAILED(hr)) {
+    return nullptr;
+  }
+
+  D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
+  srv_desc.Format = td.Format;
+  srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+  srv_desc.Texture2D.MostDetailedMip = 0;
+  srv_desc.Texture2D.MipLevels = 1;
+
+  ID3D11ShaderResourceView* srv = nullptr;
+  hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv);
+  if (FAILED(hr)) {
+    tex->Release();
+    return nullptr;
+  }
+
+  return std::make_shared<Texture2D>(tex, srv);
+}
+
+std::shared_ptr<ID3DBlob> Device::compile_shader(const std::string& source_code,
+                                                 const std::string& entry_point,
+                                                 const std::string& model) {
+  if (!lib_compiler_) {
+    return nullptr;
+  }
+
+  typedef HRESULT(WINAPI * PFN_D3DCOMPILE)(
+      LPCVOID, SIZE_T, LPCSTR, const D3D_SHADER_MACRO*, ID3DInclude*, LPCSTR,
+      LPCSTR, UINT, UINT, ID3DBlob**, ID3DBlob**);
+
+  const auto fnc_compile = reinterpret_cast<PFN_D3DCOMPILE>(
+      GetProcAddress(lib_compiler_, "D3DCompile"));
+  if (!fnc_compile) {
+    return nullptr;
+  }
+
+  DWORD flags = D3DCOMPILE_ENABLE_STRICTNESS;
+
+#if defined(NDEBUG)
+// flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
+// flags |= D3DCOMPILE_AVOID_FLOW_CONTROL;
+#else
+  flags |= D3DCOMPILE_DEBUG;
+  flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
+#endif
+
+  ID3DBlob* blob = nullptr;
+  ID3DBlob* blob_err = nullptr;
+
+  const auto psrc = source_code.c_str();
+  const auto len = source_code.size() + 1;
+
+  const auto hr =
+      fnc_compile(psrc, len, nullptr, nullptr, nullptr, entry_point.c_str(),
+                  model.c_str(), flags, 0, &blob, &blob_err);
+
+  if (FAILED(hr)) {
+    if (blob_err) {
+      // TODO: Log the error.
+      blob_err->Release();
+    }
+    return nullptr;
+  }
+
+  if (blob_err) {
+    blob_err->Release();
+  }
+
+  return std::shared_ptr<ID3DBlob>(blob, [](ID3DBlob* p) {
+    if (p)
+      p->Release();
+  });
+}
+
+std::shared_ptr<Effect> Device::create_default_effect() {
+  const auto vsh =
+      R"--(struct VS_INPUT
+{
+	float4 pos : POSITION;
+	float2 tex : TEXCOORD0;
+};
+
+struct VS_OUTPUT
+{
+	float4 pos : SV_POSITION;
+	float2 tex : TEXCOORD0;
+};
+
+VS_OUTPUT main(VS_INPUT input)
+{
+	VS_OUTPUT output;
+	output.pos = input.pos;
+	output.tex = input.tex;
+	return output;
+})--";
+
+  const auto psh =
+      R"--(Texture2D tex0 : register(t0);
+SamplerState samp0 : register(s0);
+
+struct VS_OUTPUT
+{
+	float4 pos : SV_POSITION;
+	float2 tex : TEXCOORD0;
+};
+
+float4 main(VS_OUTPUT input) : SV_Target
+{
+	return tex0.Sample(samp0, input.tex);
+})--";
+
+  return create_effect(vsh, "main", "vs_4_0", psh, "main", "ps_4_0");
+}
+
+std::shared_ptr<Effect> Device::create_effect(const std::string& vertex_code,
+                                              const std::string& vertex_entry,
+                                              const std::string& vertex_model,
+                                              const std::string& pixel_code,
+                                              const std::string& pixel_entry,
+                                              const std::string& pixel_model) {
+  const auto vs_blob = compile_shader(vertex_code, vertex_entry, vertex_model);
+
+  ID3D11VertexShader* vshdr = nullptr;
+  ID3D11InputLayout* layout = nullptr;
+
+  if (vs_blob) {
+    device_->CreateVertexShader(vs_blob->GetBufferPointer(),
+                                vs_blob->GetBufferSize(), nullptr, &vshdr);
+
+    D3D11_INPUT_ELEMENT_DESC layout_desc[] = {
+        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
+         D3D11_INPUT_PER_VERTEX_DATA, 0},
+        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
+         D3D11_INPUT_PER_VERTEX_DATA, 0},
+    };
+
+    UINT elements = ARRAYSIZE(layout_desc);
+
+    // Create the input layout.
+    device_->CreateInputLayout(layout_desc, elements,
+                               vs_blob->GetBufferPointer(),
+                               vs_blob->GetBufferSize(), &layout);
+  }
+
+  const auto ps_blob = compile_shader(pixel_code, pixel_entry, pixel_model);
+  ID3D11PixelShader* pshdr = nullptr;
+  if (ps_blob) {
+    device_->CreatePixelShader(ps_blob->GetBufferPointer(),
+                               ps_blob->GetBufferSize(), nullptr, &pshdr);
+  }
+
+  return std::make_shared<Effect>(vshdr, pshdr, layout);
+}
+
+Layer::Layer(const std::shared_ptr<Device>& device, bool flip)
+    : device_(device), flip_(flip) {
+  bounds_.x = bounds_.y = bounds_.width = bounds_.height = 0.0f;
+}
+
+Layer::~Layer() {}
+
+void Layer::attach(const std::shared_ptr<Composition>& parent) {
+  composition_ = parent;
+}
+
+std::shared_ptr<Composition> Layer::composition() const {
+  return composition_.lock();
+}
+
+Rect Layer::bounds() const {
+  return bounds_;
+}
+
+void Layer::move(float x, float y, float width, float height) {
+  bounds_.x = x;
+  bounds_.y = y;
+  bounds_.width = width;
+  bounds_.height = height;
+
+  // It's not efficient to create the quad everytime we move, but for now we're
+  // just trying to get something on-screen.
+  geometry_.reset();
+}
+
+void Layer::tick(double) {
+  // Nothing to update in the base class.
+}
+
+void Layer::render_texture(const std::shared_ptr<Context>& ctx,
+                           const std::shared_ptr<Texture2D>& texture) {
+  if (!geometry_) {
+    geometry_ = device_->create_quad(bounds_.x, bounds_.y, bounds_.width,
+                                     bounds_.height, flip_);
+  }
+
+  if (geometry_ && texture) {
+    // We need a shader.
+    if (!effect_) {
+      effect_ = device_->create_default_effect();
+    }
+
+    // Bind our states/resource to the pipeline.
+    ScopedBinder<Geometry> quad_binder(ctx, geometry_);
+    ScopedBinder<Effect> fx_binder(ctx, effect_);
+    ScopedBinder<Texture2D> tex_binder(ctx, texture);
+
+    // Draw the quad.
+    geometry_->draw();
+  }
+}
+
+Composition::Composition(const std::shared_ptr<Device>& device,
+                         int width,
+                         int height)
+    : width_(width), height_(height), vsync_(true), device_(device) {
+  fps_ = 0.0;
+  time_ = 0.0;
+  frame_ = 0;
+  fps_start_ = GetTimeNow();
+}
+
+bool Composition::is_vsync() const {
+  return vsync_;
+}
+
+double Composition::time() const {
+  return time_;
+}
+
+double Composition::fps() const {
+  return fps_;
+}
+
+void Composition::add_layer(const std::shared_ptr<Layer>& layer) {
+  if (layer) {
+    layers_.push_back(layer);
+
+    // Attach ourselves as the parent.
+    layer->attach(shared_from_this());
+  }
+}
+
+bool Composition::remove_layer(const std::shared_ptr<Layer>& layer) {
+  size_t match = 0;
+  if (layer) {
+    for (auto i = layers_.begin(); i != layers_.end();) {
+      if ((*i).get() == layer.get()) {
+        i = layers_.erase(i);
+        match++;
+      } else {
+        i++;
+      }
+    }
+  }
+  return (match > 0);
+}
+
+void Composition::resize(bool vsync, int width, int height) {
+  vsync_ = vsync;
+  width_ = width;
+  height_ = height;
+}
+
+void Composition::tick(double t) {
+  time_ = t;
+  for (const auto& layer : layers_) {
+    layer->tick(t);
+  }
+}
+
+void Composition::render(const std::shared_ptr<Context>& ctx) {
+  // Use painter's algorithm and render our layers in order (not doing any dept
+  // or 3D here).
+  for (const auto& layer : layers_) {
+    layer->render(ctx);
+  }
+
+  frame_++;
+  const auto now = GetTimeNow();
+  if ((now - fps_start_) > 1000000) {
+    fps_ = frame_ / double((now - fps_start_) / 1000000.0);
+    frame_ = 0;
+    fps_start_ = now;
+  }
+}
+
+FrameBuffer::FrameBuffer(const std::shared_ptr<Device>& device)
+    : device_(device) {}
+
+void FrameBuffer::on_paint(void* shared_handle) {
+  // Did the shared texture change?
+  if (shared_buffer_ && shared_handle != shared_buffer_->share_handle()) {
+    shared_buffer_.reset();
+  }
+
+  // Open the shared texture.
+  if (!shared_buffer_) {
+    shared_buffer_ = device_->open_shared_texture((void*)shared_handle);
+    if (!shared_buffer_) {
+      LOG(ERROR) << "d3d11: Could not open shared texture!";
+    }
+  }
+}
+
+}  // namespace d3d11
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_d3d11_win.h b/src/tests/cefclient/browser/osr_d3d11_win.h
new file mode 100644
index 0000000..f1190e3
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_d3d11_win.h
@@ -0,0 +1,330 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+//
+// Portions Copyright (c) 2018 Daktronics with the following MIT License:
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_D3D11_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_D3D11_WIN_H_
+#pragma once
+
+#include <d3d11_1.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "include/base/cef_macros.h"
+
+namespace client {
+namespace d3d11 {
+
+class Composition;
+class Context;
+class Effect;
+class Geometry;
+class SwapChain;
+class Texture2D;
+
+// Basic rect for floats.
+struct Rect {
+  float x;
+  float y;
+  float width;
+  float height;
+};
+
+template <class T>
+class ScopedBinder {
+ public:
+  ScopedBinder(const std::shared_ptr<Context>& ctx,
+               const std::shared_ptr<T>& target)
+      : target_(target) {
+    if (target_) {
+      target_->bind(ctx);
+    }
+  }
+  ~ScopedBinder() {
+    if (target_) {
+      target_->unbind();
+    }
+  }
+
+ private:
+  const std::shared_ptr<T> target_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedBinder);
+};
+
+class Context {
+ public:
+  Context(ID3D11DeviceContext*);
+
+  void flush();
+
+  operator ID3D11DeviceContext*() { return ctx_.get(); }
+
+ private:
+  const std::shared_ptr<ID3D11DeviceContext> ctx_;
+};
+
+// Encapsulate a D3D11 Device object.
+class Device {
+ public:
+  Device(ID3D11Device*, ID3D11DeviceContext*);
+
+  static std::shared_ptr<Device> create();
+
+  std::string adapter_name() const;
+
+  operator ID3D11Device*() { return device_.get(); }
+
+  std::shared_ptr<Context> immedidate_context();
+
+  std::shared_ptr<SwapChain> create_swapchain(HWND,
+                                              int width = 0,
+                                              int height = 0);
+
+  std::shared_ptr<Geometry> create_quad(float x,
+                                        float y,
+                                        float width,
+                                        float height,
+                                        bool flip = false);
+
+  std::shared_ptr<Texture2D> create_texture(int width,
+                                            int height,
+                                            DXGI_FORMAT format,
+                                            const void* data,
+                                            size_t row_stride);
+
+  std::shared_ptr<Texture2D> open_shared_texture(void*);
+
+  // Create some basic shaders so we can draw a textured-quad.
+  std::shared_ptr<Effect> create_default_effect();
+
+  std::shared_ptr<Effect> create_effect(const std::string& vertex_code,
+                                        const std::string& vertex_entry,
+                                        const std::string& vertex_model,
+                                        const std::string& pixel_code,
+                                        const std::string& pixel_entry,
+                                        const std::string& pixel_model);
+
+ private:
+  std::shared_ptr<ID3DBlob> compile_shader(const std::string& source_code,
+                                           const std::string& entry_point,
+                                           const std::string& model);
+
+  HMODULE lib_compiler_;
+
+  const std::shared_ptr<ID3D11Device> device_;
+  const std::shared_ptr<Context> ctx_;
+
+  DISALLOW_COPY_AND_ASSIGN(Device);
+};
+
+// Encapsulate a DXGI swapchain for a window.
+class SwapChain {
+ public:
+  SwapChain(IDXGISwapChain*,
+            ID3D11RenderTargetView*,
+            ID3D11SamplerState*,
+            ID3D11BlendState*);
+
+  void bind(const std::shared_ptr<Context>& ctx);
+  void unbind();
+
+  void clear(float red, float green, float blue, float alpha);
+
+  void present(int sync_interval);
+  void resize(int width, int height);
+
+  int width() const { return width_; }
+  int height() const { return height_; }
+
+ private:
+  const std::shared_ptr<ID3D11SamplerState> sampler_;
+  const std::shared_ptr<ID3D11BlendState> blender_;
+  const std::shared_ptr<IDXGISwapChain> swapchain_;
+  std::shared_ptr<ID3D11RenderTargetView> rtv_;
+  std::shared_ptr<Context> ctx_;
+  int width_;
+  int height_;
+
+  DISALLOW_COPY_AND_ASSIGN(SwapChain);
+};
+
+class Texture2D {
+ public:
+  Texture2D(ID3D11Texture2D* tex, ID3D11ShaderResourceView* srv);
+
+  void bind(std::shared_ptr<Context> const& ctx);
+  void unbind();
+
+  uint32_t width() const;
+  uint32_t height() const;
+  DXGI_FORMAT format() const;
+
+  bool has_mutex() const;
+
+  bool lock_key(uint64_t key, uint32_t timeout_ms);
+  void unlock_key(uint64_t key);
+
+  void* share_handle() const;
+
+  void copy_from(const std::shared_ptr<Texture2D>&);
+
+ private:
+  HANDLE share_handle_;
+
+  const std::shared_ptr<ID3D11Texture2D> texture_;
+  const std::shared_ptr<ID3D11ShaderResourceView> srv_;
+  std::shared_ptr<IDXGIKeyedMutex> keyed_mutex_;
+  std::shared_ptr<Context> ctx_;
+
+  DISALLOW_COPY_AND_ASSIGN(Texture2D);
+};
+
+class Effect {
+ public:
+  Effect(ID3D11VertexShader* vsh,
+         ID3D11PixelShader* psh,
+         ID3D11InputLayout* layout);
+
+  void bind(const std::shared_ptr<Context>& ctx);
+  void unbind();
+
+ private:
+  const std::shared_ptr<ID3D11VertexShader> vsh_;
+  const std::shared_ptr<ID3D11PixelShader> psh_;
+  const std::shared_ptr<ID3D11InputLayout> layout_;
+  std::shared_ptr<Context> ctx_;
+
+  DISALLOW_COPY_AND_ASSIGN(Effect);
+};
+
+class Geometry {
+ public:
+  Geometry(D3D_PRIMITIVE_TOPOLOGY primitive,
+           uint32_t vertices,
+           uint32_t stride,
+           ID3D11Buffer*);
+
+  void bind(const std::shared_ptr<Context>& ctx);
+  void unbind();
+
+  void draw();
+
+ private:
+  D3D_PRIMITIVE_TOPOLOGY primitive_;
+  uint32_t vertices_;
+  uint32_t stride_;
+  const std::shared_ptr<ID3D11Buffer> buffer_;
+  std::shared_ptr<Context> ctx_;
+
+  DISALLOW_COPY_AND_ASSIGN(Geometry);
+};
+
+// Abstraction for a 2D layer within a composition.
+class Layer {
+ public:
+  Layer(const std::shared_ptr<Device>& device, bool flip);
+  virtual ~Layer();
+
+  void attach(const std::shared_ptr<Composition>&);
+
+  // Uses normalized 0-1.0 coordinates.
+  virtual void move(float x, float y, float width, float height);
+
+  virtual void tick(double t);
+  virtual void render(const std::shared_ptr<Context>& ctx) = 0;
+
+  Rect bounds() const;
+
+  std::shared_ptr<Composition> composition() const;
+
+ protected:
+  // Helper method for derived classes to draw a textured-quad.
+  void render_texture(const std::shared_ptr<Context>& ctx,
+                      const std::shared_ptr<Texture2D>& texture);
+
+  const std::shared_ptr<Device> device_;
+  const bool flip_;
+
+  Rect bounds_;
+  std::shared_ptr<Geometry> geometry_;
+  std::shared_ptr<Effect> effect_;
+
+ private:
+  std::weak_ptr<Composition> composition_;
+
+  DISALLOW_COPY_AND_ASSIGN(Layer);
+};
+
+// A collection of layers. Will render 1-N layers to a D3D11 device.
+class Composition : public std::enable_shared_from_this<Composition> {
+ public:
+  Composition(const std::shared_ptr<Device>& device,
+              int width = 0,
+              int height = 0);
+
+  int width() const { return width_; }
+  int height() const { return height_; }
+
+  double fps() const;
+  double time() const;
+
+  bool is_vsync() const;
+
+  void tick(double);
+  void render(const std::shared_ptr<Context>&);
+
+  void add_layer(const std::shared_ptr<Layer>& layer);
+  bool remove_layer(const std::shared_ptr<Layer>& layer);
+  void resize(bool vsync, int width, int height);
+
+ private:
+  int width_;
+  int height_;
+  uint32_t frame_;
+  int64_t fps_start_;
+  double fps_;
+  double time_;
+  bool vsync_;
+
+  const std::shared_ptr<Device> device_;
+  std::vector<std::shared_ptr<Layer>> layers_;
+
+  DISALLOW_COPY_AND_ASSIGN(Composition);
+};
+
+class FrameBuffer {
+ public:
+  explicit FrameBuffer(const std::shared_ptr<Device>& device);
+
+  // Called in response to CEF's OnAcceleratedPaint notification.
+  void on_paint(void* shared_handle);
+
+  // Returns what should be considered the front buffer.
+  std::shared_ptr<Texture2D> texture() const { return shared_buffer_; }
+
+ private:
+  const std::shared_ptr<Device> device_;
+  std::shared_ptr<Texture2D> shared_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameBuffer);
+};
+
+}  // namespace d3d11
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_D3D11_WIN_H_
diff --git a/src/tests/cefclient/browser/osr_dragdrop_events.h b/src/tests/cefclient/browser/osr_dragdrop_events.h
new file mode 100644
index 0000000..db31e6b
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_dragdrop_events.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_EVENTS_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_EVENTS_H_
+#pragma once
+
+#include "include/cef_render_handler.h"
+#include "tests/cefclient/browser/client_handler.h"
+
+namespace client {
+
+class OsrDragEvents {
+ public:
+  virtual CefBrowserHost::DragOperationsMask OnDragEnter(
+      CefRefPtr<CefDragData> drag_data,
+      CefMouseEvent ev,
+      CefBrowserHost::DragOperationsMask effect) = 0;
+
+  virtual CefBrowserHost::DragOperationsMask OnDragOver(
+      CefMouseEvent ev,
+      CefBrowserHost::DragOperationsMask effect) = 0;
+
+  virtual void OnDragLeave() = 0;
+
+  virtual CefBrowserHost::DragOperationsMask OnDrop(
+      CefMouseEvent ev,
+      CefBrowserHost::DragOperationsMask effect) = 0;
+
+ protected:
+  virtual ~OsrDragEvents() {}
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_EVENTS_H_
diff --git a/src/tests/cefclient/browser/osr_dragdrop_win.cc b/src/tests/cefclient/browser/osr_dragdrop_win.cc
new file mode 100644
index 0000000..cdd95b8
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_dragdrop_win.cc
@@ -0,0 +1,656 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_dragdrop_win.h"
+
+#if defined(CEF_USE_ATL)
+
+#include <shellapi.h>
+#include <shlobj.h>
+#include <windowsx.h>
+
+#include <algorithm>
+#include <string>
+
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/bytes_write_handler.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+namespace {
+
+DWORD DragOperationToDropEffect(CefRenderHandler::DragOperation allowed_ops) {
+  DWORD effect = DROPEFFECT_NONE;
+  if (allowed_ops & DRAG_OPERATION_COPY)
+    effect |= DROPEFFECT_COPY;
+  if (allowed_ops & DRAG_OPERATION_LINK)
+    effect |= DROPEFFECT_LINK;
+  if (allowed_ops & DRAG_OPERATION_MOVE)
+    effect |= DROPEFFECT_MOVE;
+  return effect;
+}
+
+CefRenderHandler::DragOperationsMask DropEffectToDragOperation(DWORD effect) {
+  DWORD operation = DRAG_OPERATION_NONE;
+  if (effect & DROPEFFECT_COPY)
+    operation |= DRAG_OPERATION_COPY;
+  if (effect & DROPEFFECT_LINK)
+    operation |= DRAG_OPERATION_LINK;
+  if (effect & DROPEFFECT_MOVE)
+    operation |= DRAG_OPERATION_MOVE;
+  return static_cast<CefRenderHandler::DragOperationsMask>(operation);
+}
+
+CefMouseEvent ToMouseEvent(POINTL p, DWORD key_state, HWND hWnd) {
+  CefMouseEvent ev;
+  POINT screen_point = {p.x, p.y};
+  ScreenToClient(hWnd, &screen_point);
+  ev.x = screen_point.x;
+  ev.y = screen_point.y;
+  ev.modifiers = GetCefMouseModifiers(key_state);
+  return ev;
+}
+
+void GetStorageForBytes(STGMEDIUM* storage, const void* data, size_t bytes) {
+  HANDLE handle = GlobalAlloc(GPTR, static_cast<int>(bytes));
+  if (handle) {
+    memcpy(handle, data, bytes);
+  }
+
+  storage->hGlobal = handle;
+  storage->tymed = TYMED_HGLOBAL;
+  storage->pUnkForRelease = NULL;
+}
+
+template <typename T>
+void GetStorageForString(STGMEDIUM* stgmed, const std::basic_string<T>& data) {
+  GetStorageForBytes(
+      stgmed, data.c_str(),
+      (data.size() + 1) * sizeof(typename std::basic_string<T>::value_type));
+}
+
+void GetStorageForFileDescriptor(STGMEDIUM* storage,
+                                 const std::wstring& file_name) {
+  DCHECK(!file_name.empty());
+  HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
+
+  FILEGROUPDESCRIPTOR* descriptor =
+      reinterpret_cast<FILEGROUPDESCRIPTOR*>(hdata);
+  descriptor->cItems = 1;
+  descriptor->fgd[0].dwFlags = FD_LINKUI;
+  wcsncpy_s(descriptor->fgd[0].cFileName, MAX_PATH, file_name.c_str(),
+            std::min(file_name.size(), static_cast<size_t>(MAX_PATH - 1u)));
+
+  storage->tymed = TYMED_HGLOBAL;
+  storage->hGlobal = hdata;
+  storage->pUnkForRelease = NULL;
+}
+
+// Helper method for converting from text/html to MS CF_HTML.
+// Documentation for the CF_HTML format is available at
+// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
+std::string HtmlToCFHtml(const std::string& html, const std::string& base_url) {
+  if (html.empty())
+    return std::string();
+
+#define MAX_DIGITS 10
+#define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
+#define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
+#define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
+
+  static const char* header =
+      "Version:0.9\r\n"
+      "StartHTML:" NUMBER_FORMAT
+      "\r\n"
+      "EndHTML:" NUMBER_FORMAT
+      "\r\n"
+      "StartFragment:" NUMBER_FORMAT
+      "\r\n"
+      "EndFragment:" NUMBER_FORMAT "\r\n";
+  static const char* source_url_prefix = "SourceURL:";
+
+  static const char* start_markup = "<html>\r\n<body>\r\n<!--StartFragment-->";
+  static const char* end_markup = "<!--EndFragment-->\r\n</body>\r\n</html>";
+
+  // Calculate offsets
+  size_t start_html_offset =
+      strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4;
+  if (!base_url.empty()) {
+    start_html_offset +=
+        strlen(source_url_prefix) + base_url.length() + 2;  // Add 2 for \r\n.
+  }
+  size_t start_fragment_offset = start_html_offset + strlen(start_markup);
+  size_t end_fragment_offset = start_fragment_offset + html.length();
+  size_t end_html_offset = end_fragment_offset + strlen(end_markup);
+  char raw_result[1024];
+  _snprintf(raw_result, sizeof(1024), header, start_html_offset,
+            end_html_offset, start_fragment_offset, end_fragment_offset);
+  std::string result = raw_result;
+  if (!base_url.empty()) {
+    result.append(source_url_prefix);
+    result.append(base_url);
+    result.append("\r\n");
+  }
+  result.append(start_markup);
+  result.append(html);
+  result.append(end_markup);
+
+#undef MAX_DIGITS
+#undef MAKE_NUMBER_FORMAT_1
+#undef MAKE_NUMBER_FORMAT_2
+#undef NUMBER_FORMAT
+
+  return result;
+}
+
+void CFHtmlExtractMetadata(const std::string& cf_html,
+                           std::string* base_url,
+                           size_t* html_start,
+                           size_t* fragment_start,
+                           size_t* fragment_end) {
+  // Obtain base_url if present.
+  if (base_url) {
+    static std::string src_url_str("SourceURL:");
+    size_t line_start = cf_html.find(src_url_str);
+    if (line_start != std::string::npos) {
+      size_t src_end = cf_html.find("\n", line_start);
+      size_t src_start = line_start + src_url_str.length();
+      if (src_end != std::string::npos && src_start != std::string::npos) {
+        *base_url = cf_html.substr(src_start, src_end - src_start);
+      }
+    }
+  }
+
+  // Find the markup between "<!--StartFragment-->" and "<!--EndFragment-->".
+  // If the comments cannot be found, like copying from OpenOffice Writer,
+  // we simply fall back to using StartFragment/EndFragment bytecount values
+  // to determine the fragment indexes.
+  std::string cf_html_lower = cf_html;
+  size_t markup_start = cf_html_lower.find("<html", 0);
+  if (html_start) {
+    *html_start = markup_start;
+  }
+  size_t tag_start = cf_html.find("<!--StartFragment", markup_start);
+  if (tag_start == std::string::npos) {
+    static std::string start_fragment_str("StartFragment:");
+    size_t start_fragment_start = cf_html.find(start_fragment_str);
+    if (start_fragment_start != std::string::npos) {
+      *fragment_start =
+          static_cast<size_t>(atoi(cf_html.c_str() + start_fragment_start +
+                                   start_fragment_str.length()));
+    }
+
+    static std::string end_fragment_str("EndFragment:");
+    size_t end_fragment_start = cf_html.find(end_fragment_str);
+    if (end_fragment_start != std::string::npos) {
+      *fragment_end = static_cast<size_t>(atoi(
+          cf_html.c_str() + end_fragment_start + end_fragment_str.length()));
+    }
+  } else {
+    *fragment_start = cf_html.find('>', tag_start) + 1;
+    size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos);
+    *fragment_end = cf_html.rfind('<', tag_end);
+  }
+}
+
+void CFHtmlToHtml(const std::string& cf_html,
+                  std::string* html,
+                  std::string* base_url) {
+  size_t frag_start = std::string::npos;
+  size_t frag_end = std::string::npos;
+
+  CFHtmlExtractMetadata(cf_html, base_url, NULL, &frag_start, &frag_end);
+
+  if (html && frag_start != std::string::npos &&
+      frag_end != std::string::npos) {
+    *html = cf_html.substr(frag_start, frag_end - frag_start);
+  }
+}
+
+const DWORD moz_url_format = ::RegisterClipboardFormat(L"text/x-moz-url");
+const DWORD html_format = ::RegisterClipboardFormat(L"HTML Format");
+const DWORD file_desc_format = ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
+const DWORD file_contents_format =
+    ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
+
+bool DragDataToDataObject(CefRefPtr<CefDragData> drag_data,
+                          IDataObject** data_object) {
+  const int kMaxDataObjects = 10;
+  FORMATETC fmtetcs[kMaxDataObjects];
+  STGMEDIUM stgmeds[kMaxDataObjects];
+  FORMATETC fmtetc = {0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  int curr_index = 0;
+  CefString text = drag_data->GetFragmentText();
+  if (!text.empty()) {
+    fmtetc.cfFormat = CF_UNICODETEXT;
+    fmtetcs[curr_index] = fmtetc;
+    GetStorageForString(&stgmeds[curr_index], text.ToWString());
+    curr_index++;
+  }
+  if (drag_data->IsLink() && !drag_data->GetLinkURL().empty()) {
+    std::wstring x_moz_url_str = drag_data->GetLinkURL().ToWString();
+    x_moz_url_str += '\n';
+    x_moz_url_str += drag_data->GetLinkTitle().ToWString();
+    fmtetc.cfFormat = moz_url_format;
+    fmtetcs[curr_index] = fmtetc;
+    GetStorageForString(&stgmeds[curr_index], x_moz_url_str);
+    curr_index++;
+  }
+  CefString html = drag_data->GetFragmentHtml();
+  if (!html.empty()) {
+    CefString base_url = drag_data->GetFragmentBaseURL();
+    std::string cfhtml = HtmlToCFHtml(html.ToString(), base_url.ToString());
+    fmtetc.cfFormat = html_format;
+    fmtetcs[curr_index] = fmtetc;
+    GetStorageForString(&stgmeds[curr_index], cfhtml);
+    curr_index++;
+  }
+
+  size_t bufferSize = drag_data->GetFileContents(NULL);
+  if (bufferSize) {
+    CefRefPtr<BytesWriteHandler> handler = new BytesWriteHandler(bufferSize);
+    CefRefPtr<CefStreamWriter> writer =
+        CefStreamWriter::CreateForHandler(handler.get());
+    drag_data->GetFileContents(writer);
+    DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(bufferSize));
+    CefString fileName = drag_data->GetFileName();
+    GetStorageForFileDescriptor(&stgmeds[curr_index], fileName.ToWString());
+    fmtetc.cfFormat = file_desc_format;
+    fmtetcs[curr_index] = fmtetc;
+    curr_index++;
+    GetStorageForBytes(&stgmeds[curr_index], handler->GetData(),
+                       handler->GetDataSize());
+    fmtetc.cfFormat = file_contents_format;
+    fmtetcs[curr_index] = fmtetc;
+    curr_index++;
+  }
+  DCHECK_LT(curr_index, kMaxDataObjects);
+
+  CComPtr<DataObjectWin> obj =
+      DataObjectWin::Create(fmtetcs, stgmeds, curr_index);
+  (*data_object) = obj.Detach();
+  return true;
+}
+
+CefRefPtr<CefDragData> DataObjectToDragData(IDataObject* data_object) {
+  CefRefPtr<CefDragData> drag_data = CefDragData::Create();
+  IEnumFORMATETC* enumFormats = nullptr;
+  HRESULT res = data_object->EnumFormatEtc(DATADIR_GET, &enumFormats);
+  if (res != S_OK)
+    return drag_data;
+  enumFormats->Reset();
+  const int kCelt = 10;
+
+  ULONG celtFetched;
+  do {
+    celtFetched = kCelt;
+    FORMATETC rgelt[kCelt];
+    res = enumFormats->Next(kCelt, rgelt, &celtFetched);
+    for (unsigned i = 0; i < celtFetched; i++) {
+      CLIPFORMAT format = rgelt[i].cfFormat;
+      if (!(format == CF_UNICODETEXT || format == CF_TEXT ||
+            format == moz_url_format || format == html_format ||
+            format == CF_HDROP) ||
+          rgelt[i].tymed != TYMED_HGLOBAL)
+        continue;
+      STGMEDIUM medium;
+      if (data_object->GetData(&rgelt[i], &medium) == S_OK) {
+        if (!medium.hGlobal) {
+          ReleaseStgMedium(&medium);
+          continue;
+        }
+        void* hGlobal = GlobalLock(medium.hGlobal);
+        if (!hGlobal) {
+          ReleaseStgMedium(&medium);
+          continue;
+        }
+        if (format == CF_UNICODETEXT) {
+          CefString text;
+          text.FromWString((std::wstring::value_type*)hGlobal);
+          drag_data->SetFragmentText(text);
+        } else if (format == CF_TEXT) {
+          CefString text;
+          text.FromString((std::string::value_type*)hGlobal);
+          drag_data->SetFragmentText(text);
+        } else if (format == moz_url_format) {
+          std::wstring html((std::wstring::value_type*)hGlobal);
+          size_t pos = html.rfind('\n');
+          CefString url(html.substr(0, pos));
+          CefString title(html.substr(pos + 1));
+          drag_data->SetLinkURL(url);
+          drag_data->SetLinkTitle(title);
+        } else if (format == html_format) {
+          std::string cf_html((std::string::value_type*)hGlobal);
+          std::string base_url;
+          std::string html;
+          CFHtmlToHtml(cf_html, &html, &base_url);
+          drag_data->SetFragmentHtml(html);
+          drag_data->SetFragmentBaseURL(base_url);
+        }
+        if (format == CF_HDROP) {
+          HDROP hdrop = (HDROP)hGlobal;
+          const int kMaxFilenameLen = 4096;
+          const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
+          for (unsigned int x = 0; x < num_files; ++x) {
+            wchar_t filename[kMaxFilenameLen];
+            if (!DragQueryFileW(hdrop, x, filename, kMaxFilenameLen))
+              continue;
+            WCHAR* name = wcsrchr(filename, '\\');
+            drag_data->AddFile(filename, (name ? name + 1 : filename));
+          }
+        }
+        if (medium.hGlobal)
+          GlobalUnlock(medium.hGlobal);
+        if (format == CF_HDROP)
+          DragFinish((HDROP)hGlobal);
+        else
+          ReleaseStgMedium(&medium);
+      }
+    }
+  } while (res == S_OK);
+  enumFormats->Release();
+  return drag_data;
+}
+
+}  // namespace
+
+CComPtr<DropTargetWin> DropTargetWin::Create(OsrDragEvents* callback,
+                                             HWND hWnd) {
+  return CComPtr<DropTargetWin>(new DropTargetWin(callback, hWnd));
+}
+
+HRESULT DropTargetWin::DragEnter(IDataObject* data_object,
+                                 DWORD key_state,
+                                 POINTL cursor_position,
+                                 DWORD* effect) {
+  if (!callback_)
+    return E_UNEXPECTED;
+
+  CefRefPtr<CefDragData> drag_data = current_drag_data_;
+  if (!drag_data) {
+    drag_data = DataObjectToDragData(data_object);
+  }
+  CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
+  CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
+  mask = callback_->OnDragEnter(drag_data, ev, mask);
+  *effect = DragOperationToDropEffect(mask);
+  return S_OK;
+}
+
+CefBrowserHost::DragOperationsMask DropTargetWin::StartDragging(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDragData> drag_data,
+    CefRenderHandler::DragOperationsMask allowed_ops,
+    int x,
+    int y) {
+  CComPtr<IDataObject> dataObject;
+  DWORD resEffect = DROPEFFECT_NONE;
+  if (DragDataToDataObject(drag_data, &dataObject)) {
+    CComPtr<DropSourceWin> dropSource = DropSourceWin::Create();
+    DWORD effect = DragOperationToDropEffect(allowed_ops);
+    current_drag_data_ = drag_data->Clone();
+    current_drag_data_->ResetFileContents();
+    HRESULT res = DoDragDrop(dataObject, dropSource, effect, &resEffect);
+    if (res != DRAGDROP_S_DROP)
+      resEffect = DROPEFFECT_NONE;
+    current_drag_data_ = nullptr;
+  }
+  return DropEffectToDragOperation(resEffect);
+}
+
+HRESULT DropTargetWin::DragOver(DWORD key_state,
+                                POINTL cursor_position,
+                                DWORD* effect) {
+  if (!callback_)
+    return E_UNEXPECTED;
+  CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
+  CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
+  mask = callback_->OnDragOver(ev, mask);
+  *effect = DragOperationToDropEffect(mask);
+  return S_OK;
+}
+
+HRESULT DropTargetWin::DragLeave() {
+  if (!callback_)
+    return E_UNEXPECTED;
+  callback_->OnDragLeave();
+  return S_OK;
+}
+
+HRESULT DropTargetWin::Drop(IDataObject* data_object,
+                            DWORD key_state,
+                            POINTL cursor_position,
+                            DWORD* effect) {
+  if (!callback_)
+    return E_UNEXPECTED;
+  CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
+  CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
+  mask = callback_->OnDrop(ev, mask);
+  *effect = DragOperationToDropEffect(mask);
+  return S_OK;
+}
+
+CComPtr<DropSourceWin> DropSourceWin::Create() {
+  return CComPtr<DropSourceWin>(new DropSourceWin());
+}
+
+HRESULT DropSourceWin::GiveFeedback(DWORD dwEffect) {
+  return DRAGDROP_S_USEDEFAULTCURSORS;
+}
+
+HRESULT DropSourceWin::QueryContinueDrag(BOOL fEscapePressed,
+                                         DWORD grfKeyState) {
+  if (fEscapePressed) {
+    return DRAGDROP_S_CANCEL;
+  }
+
+  if (!(grfKeyState & MK_LBUTTON)) {
+    return DRAGDROP_S_DROP;
+  }
+
+  return S_OK;
+}
+
+HRESULT DragEnumFormatEtc::CreateEnumFormatEtc(
+    UINT cfmt,
+    FORMATETC* afmt,
+    IEnumFORMATETC** ppEnumFormatEtc) {
+  if (cfmt == 0 || afmt == 0 || ppEnumFormatEtc == 0)
+    return E_INVALIDARG;
+
+  *ppEnumFormatEtc = new DragEnumFormatEtc(afmt, cfmt);
+
+  return (*ppEnumFormatEtc) ? S_OK : E_OUTOFMEMORY;
+}
+
+HRESULT DragEnumFormatEtc::Next(ULONG celt,
+                                FORMATETC* pFormatEtc,
+                                ULONG* pceltFetched) {
+  ULONG copied = 0;
+
+  // copy the FORMATETC structures into the caller's buffer
+  while (m_nIndex < m_nNumFormats && copied < celt) {
+    DeepCopyFormatEtc(&pFormatEtc[copied], &m_pFormatEtc[m_nIndex]);
+    copied++;
+    m_nIndex++;
+  }
+
+  // store result
+  if (pceltFetched != 0)
+    *pceltFetched = copied;
+
+  // did we copy all that was requested?
+  return (copied == celt) ? S_OK : S_FALSE;
+}
+HRESULT DragEnumFormatEtc::Skip(ULONG celt) {
+  m_nIndex += celt;
+  return (m_nIndex <= m_nNumFormats) ? S_OK : S_FALSE;
+}
+HRESULT DragEnumFormatEtc::Reset(void) {
+  m_nIndex = 0;
+  return S_OK;
+}
+HRESULT DragEnumFormatEtc::Clone(IEnumFORMATETC** ppEnumFormatEtc) {
+  HRESULT hResult;
+
+  // make a duplicate enumerator
+  hResult = CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
+
+  if (hResult == S_OK) {
+    // manually set the index state
+    reinterpret_cast<DragEnumFormatEtc*>(*ppEnumFormatEtc)->m_nIndex = m_nIndex;
+  }
+
+  return hResult;
+}
+
+DragEnumFormatEtc::DragEnumFormatEtc(FORMATETC* pFormatEtc, int nNumFormats) {
+  AddRef();
+
+  m_nIndex = 0;
+  m_nNumFormats = nNumFormats;
+  m_pFormatEtc = new FORMATETC[nNumFormats];
+
+  // make a new copy of each FORMATETC structure
+  for (int i = 0; i < nNumFormats; i++) {
+    DeepCopyFormatEtc(&m_pFormatEtc[i], &pFormatEtc[i]);
+  }
+}
+DragEnumFormatEtc::~DragEnumFormatEtc() {
+  // first free any DVTARGETDEVICE structures
+  for (ULONG i = 0; i < m_nNumFormats; i++) {
+    if (m_pFormatEtc[i].ptd)
+      CoTaskMemFree(m_pFormatEtc[i].ptd);
+  }
+
+  // now free the main array
+  delete[] m_pFormatEtc;
+}
+
+void DragEnumFormatEtc::DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source) {
+  // copy the source FORMATETC into dest
+  *dest = *source;
+  if (source->ptd) {
+    // allocate memory for the DVTARGETDEVICE if necessary
+    dest->ptd = reinterpret_cast<DVTARGETDEVICE*>(
+        CoTaskMemAlloc(sizeof(DVTARGETDEVICE)));
+
+    // copy the contents of the source DVTARGETDEVICE into dest->ptd
+    *(dest->ptd) = *(source->ptd);
+  }
+}
+
+CComPtr<DataObjectWin> DataObjectWin::Create(FORMATETC* fmtetc,
+                                             STGMEDIUM* stgmed,
+                                             int count) {
+  return CComPtr<DataObjectWin>(new DataObjectWin(fmtetc, stgmed, count));
+}
+
+HRESULT DataObjectWin::GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pmedium) {
+  return E_NOTIMPL;
+}
+
+HRESULT DataObjectWin::QueryGetData(FORMATETC* pFormatEtc) {
+  return (LookupFormatEtc(pFormatEtc) == -1) ? DV_E_FORMATETC : S_OK;
+}
+
+HRESULT DataObjectWin::GetCanonicalFormatEtc(FORMATETC* pFormatEct,
+                                             FORMATETC* pFormatEtcOut) {
+  pFormatEtcOut->ptd = NULL;
+  return E_NOTIMPL;
+}
+
+HRESULT DataObjectWin::SetData(FORMATETC* pFormatEtc,
+                               STGMEDIUM* pMedium,
+                               BOOL fRelease) {
+  return E_NOTIMPL;
+}
+
+HRESULT DataObjectWin::DAdvise(FORMATETC* pFormatEtc,
+                               DWORD advf,
+                               IAdviseSink*,
+                               DWORD*) {
+  return E_NOTIMPL;
+}
+
+HRESULT DataObjectWin::DUnadvise(DWORD dwConnection) {
+  return E_NOTIMPL;
+}
+
+HRESULT DataObjectWin::EnumDAdvise(IEnumSTATDATA** ppEnumAdvise) {
+  return E_NOTIMPL;
+}
+
+HRESULT DataObjectWin::EnumFormatEtc(DWORD dwDirection,
+                                     IEnumFORMATETC** ppEnumFormatEtc) {
+  return DragEnumFormatEtc::CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc,
+                                                ppEnumFormatEtc);
+}
+
+HRESULT DataObjectWin::GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium) {
+  int idx;
+
+  // try to match the specified FORMATETC with one of our supported formats
+  if ((idx = LookupFormatEtc(pFormatEtc)) == -1)
+    return DV_E_FORMATETC;
+
+  // found a match - transfer data into supplied storage medium
+  pMedium->tymed = m_pFormatEtc[idx].tymed;
+  pMedium->pUnkForRelease = 0;
+
+  // copy the data into the caller's storage medium
+  switch (m_pFormatEtc[idx].tymed) {
+    case TYMED_HGLOBAL:
+      pMedium->hGlobal = DupGlobalMem(m_pStgMedium[idx].hGlobal);
+      break;
+
+    default:
+      return DV_E_FORMATETC;
+  }
+  return S_OK;
+}
+
+HGLOBAL DataObjectWin::DupGlobalMem(HGLOBAL hMem) {
+  DWORD len = GlobalSize(hMem);
+  PVOID source = GlobalLock(hMem);
+  PVOID dest = GlobalAlloc(GMEM_FIXED, len);
+
+  memcpy(dest, source, len);
+  GlobalUnlock(hMem);
+  return dest;
+}
+
+int DataObjectWin::LookupFormatEtc(FORMATETC* pFormatEtc) {
+  // check each of our formats in turn to see if one matches
+  for (int i = 0; i < m_nNumFormats; i++) {
+    if ((m_pFormatEtc[i].tymed & pFormatEtc->tymed) &&
+        m_pFormatEtc[i].cfFormat == pFormatEtc->cfFormat &&
+        m_pFormatEtc[i].dwAspect == pFormatEtc->dwAspect) {
+      // return index of stored format
+      return i;
+    }
+  }
+
+  // error, format not found
+  return -1;
+}
+
+DataObjectWin::DataObjectWin(FORMATETC* fmtetc, STGMEDIUM* stgmed, int count)
+    : ref_count_(0) {
+  m_nNumFormats = count;
+
+  m_pFormatEtc = new FORMATETC[count];
+  m_pStgMedium = new STGMEDIUM[count];
+
+  for (int i = 0; i < count; i++) {
+    m_pFormatEtc[i] = fmtetc[i];
+    m_pStgMedium[i] = stgmed[i];
+  }
+}
+
+}  // namespace client
+
+#endif  // defined(CEF_USE_ATL)
diff --git a/src/tests/cefclient/browser/osr_dragdrop_win.h b/src/tests/cefclient/browser/osr_dragdrop_win.h
new file mode 100644
index 0000000..caf9613
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_dragdrop_win.h
@@ -0,0 +1,191 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_WIN_H_
+#pragma once
+
+// When generating projects with CMake the CEF_USE_ATL value will be defined
+// automatically if using a supported Visual Studio version. Pass -DUSE_ATL=OFF
+// to the CMake command-line to disable use of ATL.
+// Uncomment this line to manually enable ATL support.
+// #define CEF_USE_ATL 1
+
+#if defined(CEF_USE_ATL)
+
+#include <atlcomcli.h>
+#include <objidl.h>
+#include <stdio.h>
+
+#include "tests/cefclient/browser/osr_dragdrop_events.h"
+
+namespace client {
+
+#define DEFAULT_QUERY_INTERFACE(__Class)                            \
+  HRESULT __stdcall QueryInterface(const IID& iid, void** object) { \
+    *object = nullptr;                                              \
+    if (IsEqualIID(iid, IID_IUnknown)) {                            \
+      IUnknown* obj = this;                                         \
+      *object = obj;                                                \
+    } else if (IsEqualIID(iid, IID_##__Class)) {                    \
+      __Class* obj = this;                                          \
+      *object = obj;                                                \
+    } else {                                                        \
+      return E_NOINTERFACE;                                         \
+    }                                                               \
+    AddRef();                                                       \
+    return S_OK;                                                    \
+  }
+#define IUNKNOWN_IMPLEMENTATION                     \
+  ULONG __stdcall AddRef() { return ++ref_count_; } \
+  ULONG __stdcall Release() {                       \
+    if (--ref_count_ == 0) {                        \
+      delete this;                                  \
+      return 0U;                                    \
+    }                                               \
+    return ref_count_;                              \
+  }                                                 \
+                                                    \
+ protected:                                         \
+  ULONG ref_count_;
+
+class DropTargetWin : public IDropTarget {
+ public:
+  static CComPtr<DropTargetWin> Create(OsrDragEvents* callback, HWND hWnd);
+
+  CefBrowserHost::DragOperationsMask StartDragging(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefDragData> drag_data,
+      CefRenderHandler::DragOperationsMask allowed_ops,
+      int x,
+      int y);
+
+  // IDropTarget implementation:
+  HRESULT __stdcall DragEnter(IDataObject* data_object,
+                              DWORD key_state,
+                              POINTL cursor_position,
+                              DWORD* effect);
+
+  HRESULT __stdcall DragOver(DWORD key_state,
+                             POINTL cursor_position,
+                             DWORD* effect);
+
+  HRESULT __stdcall DragLeave();
+
+  HRESULT __stdcall Drop(IDataObject* data_object,
+                         DWORD key_state,
+                         POINTL cursor_position,
+                         DWORD* effect);
+
+  DEFAULT_QUERY_INTERFACE(IDropTarget)
+  IUNKNOWN_IMPLEMENTATION
+
+ protected:
+  DropTargetWin(OsrDragEvents* callback, HWND hWnd)
+      : ref_count_(0), callback_(callback), hWnd_(hWnd) {}
+  virtual ~DropTargetWin() {}
+
+ private:
+  OsrDragEvents* callback_;
+  HWND hWnd_;
+
+  CefRefPtr<CefDragData> current_drag_data_;
+};
+
+class DropSourceWin : public IDropSource {
+ public:
+  static CComPtr<DropSourceWin> Create();
+
+  // IDropSource implementation:
+  HRESULT __stdcall GiveFeedback(DWORD dwEffect);
+
+  HRESULT __stdcall QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);
+
+  DEFAULT_QUERY_INTERFACE(IDropSource)
+  IUNKNOWN_IMPLEMENTATION
+
+ protected:
+  explicit DropSourceWin() : ref_count_(0) {}
+  virtual ~DropSourceWin() {}
+};
+
+class DragEnumFormatEtc : public IEnumFORMATETC {
+ public:
+  static HRESULT CreateEnumFormatEtc(UINT cfmt,
+                                     FORMATETC* afmt,
+                                     IEnumFORMATETC** ppEnumFormatEtc);
+
+  //
+  // IEnumFormatEtc members
+  //
+  HRESULT __stdcall Next(ULONG celt,
+                         FORMATETC* pFormatEtc,
+                         ULONG* pceltFetched);
+  HRESULT __stdcall Skip(ULONG celt);
+  HRESULT __stdcall Reset(void);
+  HRESULT __stdcall Clone(IEnumFORMATETC** ppEnumFormatEtc);
+
+  //
+  // Construction / Destruction
+  //
+  DragEnumFormatEtc(FORMATETC* pFormatEtc, int nNumFormats);
+  virtual ~DragEnumFormatEtc();
+
+  static void DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source);
+
+  DEFAULT_QUERY_INTERFACE(IEnumFORMATETC)
+  IUNKNOWN_IMPLEMENTATION
+
+ private:
+  ULONG m_nIndex;           // current enumerator index
+  ULONG m_nNumFormats;      // number of FORMATETC members
+  FORMATETC* m_pFormatEtc;  // array of FORMATETC objects
+};
+
+class DataObjectWin : public IDataObject {
+ public:
+  static CComPtr<DataObjectWin> Create(FORMATETC* fmtetc,
+                                       STGMEDIUM* stgmed,
+                                       int count);
+
+  // IDataObject memberS
+  HRESULT __stdcall GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pmedium);
+  HRESULT __stdcall QueryGetData(FORMATETC* pFormatEtc);
+  HRESULT __stdcall GetCanonicalFormatEtc(FORMATETC* pFormatEct,
+                                          FORMATETC* pFormatEtcOut);
+  HRESULT __stdcall SetData(FORMATETC* pFormatEtc,
+                            STGMEDIUM* pMedium,
+                            BOOL fRelease);
+  HRESULT __stdcall DAdvise(FORMATETC* pFormatEtc,
+                            DWORD advf,
+                            IAdviseSink*,
+                            DWORD*);
+  HRESULT __stdcall DUnadvise(DWORD dwConnection);
+  HRESULT __stdcall EnumDAdvise(IEnumSTATDATA** ppEnumAdvise);
+
+  HRESULT __stdcall EnumFormatEtc(DWORD dwDirection,
+                                  IEnumFORMATETC** ppEnumFormatEtc);
+  HRESULT __stdcall GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium);
+
+  DEFAULT_QUERY_INTERFACE(IDataObject)
+  IUNKNOWN_IMPLEMENTATION
+
+ protected:
+  int m_nNumFormats;
+  FORMATETC* m_pFormatEtc;
+  STGMEDIUM* m_pStgMedium;
+
+  static HGLOBAL DupGlobalMem(HGLOBAL hMem);
+
+  int LookupFormatEtc(FORMATETC* pFormatEtc);
+
+  explicit DataObjectWin(FORMATETC* fmtetc, STGMEDIUM* stgmed, int count);
+  virtual ~DataObjectWin() {}
+};
+
+}  // namespace client
+
+#endif  // defined(CEF_USE_ATL)
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_DRAGDROP_WIN_H_
diff --git a/src/tests/cefclient/browser/osr_ime_handler_win.cc b/src/tests/cefclient/browser/osr_ime_handler_win.cc
new file mode 100644
index 0000000..aac4d3d
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_ime_handler_win.cc
@@ -0,0 +1,391 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+// Implementation based on ui/base/ime/win/imm32_manager.cc from Chromium.
+
+#include <msctf.h>
+#include <windowsx.h>
+
+#include "include/base/cef_build.h"
+#include "tests/cefclient/browser/osr_ime_handler_win.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/shared/browser/geometry_util.h"
+#include "tests/shared/browser/main_message_loop.h"
+#include "tests/shared/browser/util_win.h"
+
+#define ColorUNDERLINE 0xFF000000  // Black SkColor value for underline,
+                                   // same as Blink.
+#define ColorBKCOLOR 0x00000000    // White SkColor value for background,
+                                   // same as Blink.
+
+namespace client {
+
+namespace {
+
+// Determines whether or not the given attribute represents a selection
+bool IsSelectionAttribute(char attribute) {
+  return (attribute == ATTR_TARGET_CONVERTED ||
+          attribute == ATTR_TARGET_NOTCONVERTED);
+}
+
+// Helper function for OsrImeHandlerWin::GetCompositionInfo() method,
+// to get the target range that's selected by the user in the current
+// composition string.
+void GetCompositionSelectionRange(HIMC imc,
+                                  int* target_start,
+                                  int* target_end) {
+  int attribute_size = ::ImmGetCompositionString(imc, GCS_COMPATTR, NULL, 0);
+  if (attribute_size > 0) {
+    int start = 0;
+    int end = 0;
+    std::vector<char> attribute_data(attribute_size);
+
+    ::ImmGetCompositionString(imc, GCS_COMPATTR, &attribute_data[0],
+                              attribute_size);
+    for (start = 0; start < attribute_size; ++start) {
+      if (IsSelectionAttribute(attribute_data[start]))
+        break;
+    }
+    for (end = start; end < attribute_size; ++end) {
+      if (!IsSelectionAttribute(attribute_data[end]))
+        break;
+    }
+
+    *target_start = start;
+    *target_end = end;
+  }
+}
+
+// Helper function for OsrImeHandlerWin::GetCompositionInfo() method, to get
+// underlines information of the current composition string.
+void GetCompositionUnderlines(
+    HIMC imc,
+    int target_start,
+    int target_end,
+    std::vector<CefCompositionUnderline>& underlines) {
+  int clause_size = ::ImmGetCompositionString(imc, GCS_COMPCLAUSE, NULL, 0);
+  int clause_length = clause_size / sizeof(uint32);
+  if (clause_length) {
+    std::vector<uint32> clause_data(clause_length);
+
+    ::ImmGetCompositionString(imc, GCS_COMPCLAUSE, &clause_data[0],
+                              clause_size);
+    for (int i = 0; i < clause_length - 1; ++i) {
+      cef_composition_underline_t underline;
+      underline.range.from = clause_data[i];
+      underline.range.to = clause_data[i + 1];
+      underline.color = ColorUNDERLINE;
+      underline.background_color = ColorBKCOLOR;
+      underline.thick = 0;
+
+      // Use thick underline for the target clause.
+      if (underline.range.from >= target_start &&
+          underline.range.to <= target_end) {
+        underline.thick = 1;
+      }
+      underlines.push_back(underline);
+    }
+  }
+}
+
+}  // namespace
+
+OsrImeHandlerWin::OsrImeHandlerWin(HWND hwnd)
+    : is_composing_(false),
+      input_language_id_(LANG_USER_DEFAULT),
+      system_caret_(false),
+      cursor_index_(-1),
+      hwnd_(hwnd) {
+  ime_rect_ = {-1, -1, 0, 0};
+}
+
+OsrImeHandlerWin::~OsrImeHandlerWin() {
+  DestroyImeWindow();
+}
+
+void OsrImeHandlerWin::SetInputLanguage() {
+  // Retrieve the current input language from the system's keyboard layout.
+  // Using GetKeyboardLayoutName instead of GetKeyboardLayout, because
+  // the language from GetKeyboardLayout is the language under where the
+  // keyboard layout is installed. And the language from GetKeyboardLayoutName
+  // indicates the language of the keyboard layout itself.
+  // See crbug.com/344834.
+  WCHAR keyboard_layout[KL_NAMELENGTH];
+  if (::GetKeyboardLayoutNameW(keyboard_layout)) {
+    input_language_id_ =
+        static_cast<LANGID>(_wtoi(&keyboard_layout[KL_NAMELENGTH >> 1]));
+  } else {
+    input_language_id_ = 0x0409;  // Fallback to en-US.
+  }
+}
+
+void OsrImeHandlerWin::CreateImeWindow() {
+  // Chinese/Japanese IMEs somehow ignore function calls to
+  // ::ImmSetCandidateWindow(), and use the position of the current system
+  // caret instead -::GetCaretPos().
+  // Therefore, we create a temporary system caret for Chinese IMEs and use
+  // it during this input context.
+  // Since some third-party Japanese IME also uses ::GetCaretPos() to determine
+  // their window position, we also create a caret for Japanese IMEs.
+  if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE ||
+      PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) {
+    if (!system_caret_) {
+      if (::CreateCaret(hwnd_, NULL, 1, 1))
+        system_caret_ = true;
+    }
+  }
+}
+
+void OsrImeHandlerWin::DestroyImeWindow() {
+  // Destroy the system caret if we have created for this IME input context.
+  if (system_caret_) {
+    ::DestroyCaret();
+    system_caret_ = false;
+  }
+}
+
+void OsrImeHandlerWin::MoveImeWindow() {
+  // Does nothing when the target window has no input focus.
+  if (GetFocus() != hwnd_)
+    return;
+
+  CefRect rc = ime_rect_;
+  int location = cursor_index_;
+
+  // If location is not specified fall back to the composition range start.
+  if (location == -1)
+    location = composition_range_.from;
+
+  // Offset location by the composition range start if required.
+  if (location >= composition_range_.from)
+    location -= composition_range_.from;
+
+  if (location < static_cast<int>(composition_bounds_.size()))
+    rc = composition_bounds_[location];
+  else
+    return;
+
+  HIMC imc = ::ImmGetContext(hwnd_);
+  if (imc) {
+    const int kCaretMargin = 1;
+    if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE) {
+      // Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
+      // when a user disables TSF (Text Service Framework) and CUAS (Cicero
+      // Unaware Application Support).
+      // On the other hand, when a user enables TSF and CUAS, Chinese IMEs
+      // ignore the position of the current system caret and use the
+      // parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
+      // parameter CFS_CANDIDATEPOS.
+      // Therefore, we do not only call ::ImmSetCandidateWindow() but also
+      // set the positions of the temporary system caret if it exists.
+      CANDIDATEFORM candidate_position = {
+          0, CFS_CANDIDATEPOS, {rc.x, rc.y}, {0, 0, 0, 0}};
+      ::ImmSetCandidateWindow(imc, &candidate_position);
+    }
+    if (system_caret_) {
+      switch (PRIMARYLANGID(input_language_id_)) {
+        case LANG_JAPANESE:
+          ::SetCaretPos(rc.x, rc.y + rc.height);
+          break;
+        default:
+          ::SetCaretPos(rc.x, rc.y);
+          break;
+      }
+    }
+
+    if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) {
+      // Korean IMEs require the lower-left corner of the caret to move their
+      // candidate windows.
+      rc.y += kCaretMargin;
+    }
+
+    // Japanese IMEs and Korean IMEs also use the rectangle given to
+    // ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
+    // Therefore, we also set this parameter here.
+    CANDIDATEFORM exclude_rectangle = {
+        0,
+        CFS_EXCLUDE,
+        {rc.x, rc.y},
+        {rc.x, rc.y, rc.x + rc.width, rc.y + rc.height}};
+    ::ImmSetCandidateWindow(imc, &exclude_rectangle);
+
+    ::ImmReleaseContext(hwnd_, imc);
+  }
+}
+
+void OsrImeHandlerWin::CleanupComposition() {
+  // Notify the IMM attached to the given window to complete the ongoing
+  // composition (when given window is de-activated while composing and
+  // re-activated) and reset the composition status.
+  if (is_composing_) {
+    HIMC imc = ::ImmGetContext(hwnd_);
+    if (imc) {
+      ::ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
+      ::ImmReleaseContext(hwnd_, imc);
+    }
+    ResetComposition();
+  }
+}
+
+void OsrImeHandlerWin::ResetComposition() {
+  // Reset the composition status.
+  is_composing_ = false;
+  cursor_index_ = -1;
+}
+
+void OsrImeHandlerWin::GetCompositionInfo(
+    HIMC imc,
+    LPARAM lparam,
+    CefString& composition_text,
+    std::vector<CefCompositionUnderline>& underlines,
+    int& composition_start) {
+  // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and
+  // convert them into underlines and selection range respectively.
+  underlines.clear();
+
+  int length = static_cast<int>(composition_text.length());
+
+  // Find out the range selected by the user.
+  int target_start = length;
+  int target_end = length;
+  if (lparam & GCS_COMPATTR)
+    GetCompositionSelectionRange(imc, &target_start, &target_end);
+
+  // Retrieve the selection range information. If CS_NOMOVECARET is specified
+  // it means the cursor should not be moved and we therefore place the caret at
+  // the beginning of the composition string. Otherwise we should honour the
+  // GCS_CURSORPOS value if it's available.
+  // TODO(suzhe): Due to a bug in WebKit we currently can't use selection range
+  // with composition string.
+  // See: https://bugs.webkit.org/show_bug.cgi?id=40805
+  if (!(lparam & CS_NOMOVECARET) && (lparam & GCS_CURSORPOS)) {
+    // IMM32 does not support non-zero-width selection in a composition. So
+    // always use the caret position as selection range.
+    int cursor = ::ImmGetCompositionString(imc, GCS_CURSORPOS, NULL, 0);
+    composition_start = cursor;
+  } else {
+    composition_start = 0;
+  }
+
+  // Retrieve the clause segmentations and convert them to underlines.
+  if (lparam & GCS_COMPCLAUSE)
+    GetCompositionUnderlines(imc, target_start, target_end, underlines);
+
+  // Set default underlines in case there is no clause information.
+  if (!underlines.size()) {
+    CefCompositionUnderline underline;
+    underline.color = ColorUNDERLINE;
+    underline.background_color = ColorBKCOLOR;
+    if (target_start > 0) {
+      underline.range.from = 0;
+      underline.range.to = target_start;
+      underline.thick = 0;
+      underlines.push_back(underline);
+    }
+    if (target_end > target_start) {
+      underline.range.from = target_start;
+      underline.range.to = target_end;
+      underline.thick = 1;
+      underlines.push_back(underline);
+    }
+    if (target_end < length) {
+      underline.range.from = target_end;
+      underline.range.to = length;
+      underline.thick = 0;
+      underlines.push_back(underline);
+    }
+  }
+}
+
+bool OsrImeHandlerWin::GetString(HIMC imc,
+                                 WPARAM lparam,
+                                 int type,
+                                 CefString& result) {
+  if (!(lparam & type))
+    return false;
+  LONG string_size = ::ImmGetCompositionString(imc, type, NULL, 0);
+  if (string_size <= 0)
+    return false;
+
+  // For trailing NULL - ImmGetCompositionString excludes that.
+  string_size += sizeof(WCHAR);
+
+  std::vector<wchar_t> buffer(string_size);
+  ::ImmGetCompositionString(imc, type, &buffer[0], string_size);
+  result.FromWString(&buffer[0]);
+  return true;
+}
+
+bool OsrImeHandlerWin::GetResult(LPARAM lparam, CefString& result) {
+  bool ret = false;
+  HIMC imc = ::ImmGetContext(hwnd_);
+  if (imc) {
+    ret = GetString(imc, lparam, GCS_RESULTSTR, result);
+    ::ImmReleaseContext(hwnd_, imc);
+  }
+  return ret;
+}
+
+bool OsrImeHandlerWin::GetComposition(
+    LPARAM lparam,
+    CefString& composition_text,
+    std::vector<CefCompositionUnderline>& underlines,
+    int& composition_start) {
+  bool ret = false;
+  HIMC imc = ::ImmGetContext(hwnd_);
+  if (imc) {
+    // Copy the composition string to the CompositionText object.
+    ret = GetString(imc, lparam, GCS_COMPSTR, composition_text);
+
+    if (ret) {
+      // Retrieve the composition underlines and selection range information.
+      GetCompositionInfo(imc, lparam, composition_text, underlines,
+                         composition_start);
+
+      // Mark that there is an ongoing composition.
+      is_composing_ = true;
+    }
+
+    ::ImmReleaseContext(hwnd_, imc);
+  }
+  return ret;
+}
+
+void OsrImeHandlerWin::DisableIME() {
+  CleanupComposition();
+  ::ImmAssociateContextEx(hwnd_, NULL, 0);
+}
+
+void OsrImeHandlerWin::CancelIME() {
+  if (is_composing_) {
+    HIMC imc = ::ImmGetContext(hwnd_);
+    if (imc) {
+      ::ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+      ::ImmReleaseContext(hwnd_, imc);
+    }
+    ResetComposition();
+  }
+}
+
+void OsrImeHandlerWin::EnableIME() {
+  // Load the default IME context.
+  ::ImmAssociateContextEx(hwnd_, NULL, IACE_DEFAULT);
+}
+
+void OsrImeHandlerWin::UpdateCaretPosition(int index) {
+  // Save the caret position.
+  cursor_index_ = index;
+  // Move the IME window.
+  MoveImeWindow();
+}
+
+void OsrImeHandlerWin::ChangeCompositionRange(
+    const CefRange& selection_range,
+    const std::vector<CefRect>& bounds) {
+  composition_range_ = selection_range;
+  composition_bounds_ = bounds;
+  MoveImeWindow();
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_ime_handler_win.h b/src/tests/cefclient/browser/osr_ime_handler_win.h
new file mode 100644
index 0000000..8758985
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_ime_handler_win.h
@@ -0,0 +1,115 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
+#pragma once
+
+#include <windows.h>
+#include <vector>
+
+#include "include/internal/cef_types_wrappers.h"
+
+namespace client {
+
+// Handles IME for the native parent window that hosts an off-screen browser.
+// This object is only accessed on the CEF UI thread.
+class OsrImeHandlerWin {
+ public:
+  explicit OsrImeHandlerWin(HWND hwnd);
+  virtual ~OsrImeHandlerWin();
+
+  // Retrieves whether or not there is an ongoing composition.
+  bool is_composing() const { return is_composing_; }
+
+  // Retrieves the input language from Windows and update it.
+  void SetInputLanguage();
+
+  // Creates the IME caret windows if required.
+  void CreateImeWindow();
+
+  // Destroys the IME caret windows.
+  void DestroyImeWindow();
+
+  // Cleans up the all resources attached to the given IMM32Manager object, and
+  // reset its composition status.
+  void CleanupComposition();
+
+  // Resets the composition status and cancels the ongoing composition.
+  void ResetComposition();
+
+  // Retrieves a composition result of the ongoing composition if it exists.
+  bool GetResult(LPARAM lparam, CefString& result);
+
+  // Retrieves the current composition status of the ongoing composition.
+  // Includes composition text, underline information and selection range in the
+  // composition text. IMM32 does not support char selection.
+  bool GetComposition(LPARAM lparam,
+                      CefString& composition_text,
+                      std::vector<CefCompositionUnderline>& underlines,
+                      int& composition_start);
+
+  // Enables the IME attached to the given window.
+  virtual void EnableIME();
+
+  // Disables the IME attached to the given window.
+  virtual void DisableIME();
+
+  // Cancels an ongoing composition of the IME.
+  virtual void CancelIME();
+
+  // Updates the IME caret position of the given window.
+  void UpdateCaretPosition(int index);
+
+  // Updates the composition range. |selected_range| is the range of characters
+  // that have been selected. |character_bounds| is the bounds of each character
+  // in view device coordinates.
+  void ChangeCompositionRange(const CefRange& selection_range,
+                              const std::vector<CefRect>& character_bounds);
+
+  // Updates the position of the IME windows.
+  void MoveImeWindow();
+
+ private:
+  // Retrieves the composition information.
+  void GetCompositionInfo(HIMC imm_context,
+                          LPARAM lparam,
+                          CefString& composition_text,
+                          std::vector<CefCompositionUnderline>& underlines,
+                          int& composition_start);
+
+  // Retrieves a string from the IMM.
+  bool GetString(HIMC imm_context, WPARAM lparam, int type, CefString& result);
+
+  // Represents whether or not there is an ongoing composition.
+  bool is_composing_;
+
+  // The current composition character range and its bounds.
+  std::vector<CefRect> composition_bounds_;
+
+  // The current input Language ID retrieved from Windows -
+  // used for processing language-specific operations in IME.
+  LANGID input_language_id_;
+
+  // Represents whether or not the current input context has created a system
+  // caret to set the position of its IME candidate window.
+  bool system_caret_;
+
+  // The rectangle of the input caret retrieved from a renderer process.
+  CefRect ime_rect_;
+
+  // The current cursor index in composition string.
+  int cursor_index_;
+
+  // The composition range in the string. This may be used to determine the
+  // offset in composition bounds.
+  CefRange composition_range_;
+
+  // Hwnd associated with this instance.
+  HWND hwnd_;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
diff --git a/src/tests/cefclient/browser/osr_render_handler_win.cc b/src/tests/cefclient/browser/osr_render_handler_win.cc
new file mode 100644
index 0000000..8fa9b16
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_render_handler_win.cc
@@ -0,0 +1,83 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_render_handler_win.h"
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+OsrRenderHandlerWin::OsrRenderHandlerWin(const OsrRendererSettings& settings,
+                                         HWND hwnd)
+    : settings_(settings),
+      hwnd_(hwnd),
+      begin_frame_pending_(false),
+      weak_factory_(this) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(hwnd_);
+}
+
+OsrRenderHandlerWin::~OsrRenderHandlerWin() {
+  CEF_REQUIRE_UI_THREAD();
+}
+
+void OsrRenderHandlerWin::SetBrowser(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  browser_ = browser;
+  if (browser_ && settings_.external_begin_frame_enabled) {
+    // Start the BeginFrame timer.
+    Invalidate();
+  }
+}
+
+void OsrRenderHandlerWin::Invalidate() {
+  CEF_REQUIRE_UI_THREAD();
+  if (begin_frame_pending_) {
+    // The timer is already running.
+    return;
+  }
+
+  // Trigger the BeginFrame timer.
+  CHECK_GT(settings_.begin_frame_rate, 0);
+  const float delay_us = (1.0 / double(settings_.begin_frame_rate)) * 1000000.0;
+  TriggerBeginFrame(0, delay_us);
+}
+
+void OsrRenderHandlerWin::TriggerBeginFrame(uint64_t last_time_us,
+                                            float delay_us) {
+  if (begin_frame_pending_ && !settings_.external_begin_frame_enabled) {
+    // Render immediately and then wait for the next call to Invalidate() or
+    // On[Accelerated]Paint().
+    begin_frame_pending_ = false;
+    Render();
+    return;
+  }
+
+  const auto now = GetTimeNow();
+  float offset = now - last_time_us;
+  if (offset > delay_us) {
+    offset = delay_us;
+  }
+
+  if (!begin_frame_pending_) {
+    begin_frame_pending_ = true;
+  }
+
+  // Trigger again after the necessary delay to maintain the desired frame rate.
+  CefPostDelayedTask(TID_UI,
+                     base::Bind(&OsrRenderHandlerWin::TriggerBeginFrame,
+                                weak_factory_.GetWeakPtr(), now, delay_us),
+                     int64(offset / 1000.0));
+
+  if (settings_.external_begin_frame_enabled && browser_) {
+    // We're running the BeginFrame timer. Trigger rendering via
+    // On[Accelerated]Paint().
+    browser_->GetHost()->SendExternalBeginFrame();
+  }
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_render_handler_win.h b/src/tests/cefclient/browser/osr_render_handler_win.h
new file mode 100644
index 0000000..4eee024
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_render_handler_win.h
@@ -0,0 +1,82 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_H_
+#pragma once
+
+#include "include/base/cef_weak_ptr.h"
+#include "include/cef_render_handler.h"
+#include "tests/cefclient/browser/osr_renderer_settings.h"
+
+namespace client {
+
+// Abstract base class for implementing OSR rendering with different backends on
+// Windows. Methods are only called on the UI thread.
+class OsrRenderHandlerWin {
+ public:
+  OsrRenderHandlerWin(const OsrRendererSettings& settings, HWND hwnd);
+  virtual ~OsrRenderHandlerWin();
+
+  void SetBrowser(CefRefPtr<CefBrowser> browser);
+
+  // Rotate the texture based on mouse events.
+  virtual void SetSpin(float spinX, float spinY) = 0;
+  virtual void IncrementSpin(float spinDX, float spinDY) = 0;
+
+  // Popup hit testing.
+  virtual bool IsOverPopupWidget(int x, int y) const = 0;
+  virtual int GetPopupXOffset() const = 0;
+  virtual int GetPopupYOffset() const = 0;
+
+  // CefRenderHandler callbacks.
+  virtual void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) = 0;
+  // |rect| must be in pixel coordinates.
+  virtual void OnPopupSize(CefRefPtr<CefBrowser> browser,
+                           const CefRect& rect) = 0;
+
+  // Used when not rendering with shared textures.
+  virtual void OnPaint(CefRefPtr<CefBrowser> browser,
+                       CefRenderHandler::PaintElementType type,
+                       const CefRenderHandler::RectList& dirtyRects,
+                       const void* buffer,
+                       int width,
+                       int height) = 0;
+
+  // Used when rendering with shared textures.
+  virtual void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                                  CefRenderHandler::PaintElementType type,
+                                  const CefRenderHandler::RectList& dirtyRects,
+                                  void* share_handle) = 0;
+
+  bool send_begin_frame() const {
+    return settings_.external_begin_frame_enabled;
+  }
+  HWND hwnd() const { return hwnd_; }
+
+ protected:
+  // Called to trigger the BeginFrame timer.
+  void Invalidate();
+
+  // Called by the BeginFrame timer.
+  virtual void Render() = 0;
+
+ private:
+  void TriggerBeginFrame(uint64_t last_time_us, float delay_us);
+
+  // The below members are only accessed on the UI thread.
+  const OsrRendererSettings settings_;
+  const HWND hwnd_;
+  bool begin_frame_pending_;
+  CefRefPtr<CefBrowser> browser_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<OsrRenderHandlerWin> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OsrRenderHandlerWin);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_H_
diff --git a/src/tests/cefclient/browser/osr_render_handler_win_d3d11.cc b/src/tests/cefclient/browser/osr_render_handler_win_d3d11.cc
new file mode 100644
index 0000000..67f4d90
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_render_handler_win_d3d11.cc
@@ -0,0 +1,217 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_render_handler_win_d3d11.h"
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+BrowserLayer::BrowserLayer(const std::shared_ptr<d3d11::Device>& device)
+    : d3d11::Layer(device, true /* flip */) {
+  frame_buffer_ = std::make_shared<d3d11::FrameBuffer>(device_);
+}
+
+void BrowserLayer::render(const std::shared_ptr<d3d11::Context>& ctx) {
+  // Use the base class method to draw our texture.
+  render_texture(ctx, frame_buffer_->texture());
+}
+
+void BrowserLayer::on_paint(void* share_handle) {
+  frame_buffer_->on_paint(share_handle);
+}
+
+std::pair<uint32_t, uint32_t> BrowserLayer::texture_size() const {
+  const auto texture = frame_buffer_->texture();
+  return std::make_pair(texture->width(), texture->height());
+}
+
+PopupLayer::PopupLayer(const std::shared_ptr<d3d11::Device>& device)
+    : BrowserLayer(device) {}
+
+void PopupLayer::set_bounds(const CefRect& bounds) {
+  const auto comp = composition();
+  if (!comp)
+    return;
+
+  const auto outer_width = comp->width();
+  const auto outer_height = comp->height();
+  if (outer_width == 0 || outer_height == 0)
+    return;
+
+  original_bounds_ = bounds;
+  bounds_ = bounds;
+
+  // If x or y are negative, move them to 0.
+  if (bounds_.x < 0)
+    bounds_.x = 0;
+  if (bounds_.y < 0)
+    bounds_.y = 0;
+  // If popup goes outside the view, try to reposition origin
+  if (bounds_.x + bounds_.width > outer_width)
+    bounds_.x = outer_width - bounds_.width;
+  if (bounds_.y + bounds_.height > outer_height)
+    bounds_.y = outer_height - bounds_.height;
+  // If x or y became negative, move them to 0 again.
+  if (bounds_.x < 0)
+    bounds_.x = 0;
+  if (bounds_.y < 0)
+    bounds_.y = 0;
+
+  const auto x = bounds_.x / float(outer_width);
+  const auto y = bounds_.y / float(outer_height);
+  const auto w = bounds_.width / float(outer_width);
+  const auto h = bounds_.height / float(outer_height);
+  move(x, y, w, h);
+}
+
+OsrRenderHandlerWinD3D11::OsrRenderHandlerWinD3D11(
+    const OsrRendererSettings& settings,
+    HWND hwnd)
+    : OsrRenderHandlerWin(settings, hwnd), start_time_(0) {}
+
+bool OsrRenderHandlerWinD3D11::Initialize(CefRefPtr<CefBrowser> browser,
+                                          int width,
+                                          int height) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Create a D3D11 device instance.
+  device_ = d3d11::Device::create();
+  DCHECK(device_);
+  if (!device_)
+    return false;
+
+  // Create a D3D11 swapchain for the window.
+  swap_chain_ = device_->create_swapchain(hwnd());
+  DCHECK(swap_chain_);
+  if (!swap_chain_)
+    return false;
+
+  // Create the browser layer.
+  browser_layer_ = std::make_shared<BrowserLayer>(device_);
+
+  // Set up the composition.
+  composition_ = std::make_shared<d3d11::Composition>(device_, width, height);
+  composition_->add_layer(browser_layer_);
+
+  // Size to the whole composition.
+  browser_layer_->move(0.0f, 0.0f, 1.0f, 1.0f);
+
+  start_time_ = GetTimeNow();
+
+  SetBrowser(browser);
+  return true;
+}
+
+void OsrRenderHandlerWinD3D11::SetSpin(float spinX, float spinY) {
+  CEF_REQUIRE_UI_THREAD();
+  // Spin support is not implemented.
+}
+
+void OsrRenderHandlerWinD3D11::IncrementSpin(float spinDX, float spinDY) {
+  CEF_REQUIRE_UI_THREAD();
+  // Spin support is not implemented.
+}
+
+bool OsrRenderHandlerWinD3D11::IsOverPopupWidget(int x, int y) const {
+  CEF_REQUIRE_UI_THREAD();
+  return popup_layer_ && popup_layer_->contains(x, y);
+}
+
+int OsrRenderHandlerWinD3D11::GetPopupXOffset() const {
+  CEF_REQUIRE_UI_THREAD();
+  if (popup_layer_)
+    return popup_layer_->xoffset();
+  return 0;
+}
+
+int OsrRenderHandlerWinD3D11::GetPopupYOffset() const {
+  CEF_REQUIRE_UI_THREAD();
+  if (popup_layer_)
+    return popup_layer_->yoffset();
+  return 0;
+}
+
+void OsrRenderHandlerWinD3D11::OnPopupShow(CefRefPtr<CefBrowser> browser,
+                                           bool show) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (show) {
+    DCHECK(!popup_layer_);
+
+    // Create a new layer.
+    popup_layer_ = std::make_shared<PopupLayer>(device_);
+    composition_->add_layer(popup_layer_);
+  } else {
+    DCHECK(popup_layer_);
+
+    composition_->remove_layer(popup_layer_);
+    popup_layer_ = nullptr;
+
+    Render();
+  }
+}
+
+void OsrRenderHandlerWinD3D11::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                                           const CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  popup_layer_->set_bounds(rect);
+}
+
+void OsrRenderHandlerWinD3D11::OnPaint(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::PaintElementType type,
+    const CefRenderHandler::RectList& dirtyRects,
+    const void* buffer,
+    int width,
+    int height) {
+  // Not used with this implementation.
+  NOTREACHED();
+}
+
+void OsrRenderHandlerWinD3D11::OnAcceleratedPaint(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::PaintElementType type,
+    const CefRenderHandler::RectList& dirtyRects,
+    void* share_handle) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (type == PET_POPUP) {
+    popup_layer_->on_paint(share_handle);
+  } else {
+    browser_layer_->on_paint(share_handle);
+  }
+
+  Render();
+}
+
+void OsrRenderHandlerWinD3D11::Render() {
+  // Update composition + layers based on time.
+  const auto t = (GetTimeNow() - start_time_) / 1000000.0;
+  composition_->tick(t);
+
+  auto ctx = device_->immedidate_context();
+  swap_chain_->bind(ctx);
+
+  const auto texture_size = browser_layer_->texture_size();
+
+  // Resize the composition and swap chain to match the texture if necessary.
+  composition_->resize(!send_begin_frame(), texture_size.first,
+                       texture_size.second);
+  swap_chain_->resize(texture_size.first, texture_size.second);
+
+  // Clear the render target.
+  swap_chain_->clear(0.0f, 0.0f, 1.0f, 1.0f);
+
+  // Render the scene.
+  composition_->render(ctx);
+
+  // Present to window.
+  swap_chain_->present(send_begin_frame() ? 0 : 1);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_render_handler_win_d3d11.h b/src/tests/cefclient/browser/osr_render_handler_win_d3d11.h
new file mode 100644
index 0000000..a72b1dd
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_render_handler_win_d3d11.h
@@ -0,0 +1,90 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_D3D11_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_D3D11_H_
+#pragma once
+
+#include "tests/cefclient/browser/osr_d3d11_win.h"
+#include "tests/cefclient/browser/osr_render_handler_win.h"
+#include "tests/cefclient/browser/osr_renderer_settings.h"
+
+namespace client {
+
+class BrowserLayer : public d3d11::Layer {
+ public:
+  explicit BrowserLayer(const std::shared_ptr<d3d11::Device>& device);
+
+  void render(const std::shared_ptr<d3d11::Context>& ctx) OVERRIDE;
+
+  void on_paint(void* share_handle);
+
+  // After calling on_paint() we can query the texture size.
+  std::pair<uint32_t, uint32_t> texture_size() const;
+
+ private:
+  std::shared_ptr<d3d11::FrameBuffer> frame_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserLayer);
+};
+
+class PopupLayer : public BrowserLayer {
+ public:
+  explicit PopupLayer(const std::shared_ptr<d3d11::Device>& device);
+
+  void set_bounds(const CefRect& bounds);
+
+  bool contains(int x, int y) const { return bounds_.Contains(x, y); }
+  int xoffset() const { return original_bounds_.x - bounds_.x; }
+  int yoffset() const { return original_bounds_.y - bounds_.y; }
+
+ private:
+  CefRect original_bounds_;
+  CefRect bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(PopupLayer);
+};
+
+class OsrRenderHandlerWinD3D11 : public OsrRenderHandlerWin {
+ public:
+  OsrRenderHandlerWinD3D11(const OsrRendererSettings& settings, HWND hwnd);
+
+  // Must be called immediately after object creation.
+  // May fail if D3D11 cannot be initialized.
+  bool Initialize(CefRefPtr<CefBrowser> browser, int width, int height);
+
+  void SetSpin(float spinX, float spinY) OVERRIDE;
+  void IncrementSpin(float spinDX, float spinDY) OVERRIDE;
+  bool IsOverPopupWidget(int x, int y) const OVERRIDE;
+  int GetPopupXOffset() const OVERRIDE;
+  int GetPopupYOffset() const OVERRIDE;
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) OVERRIDE;
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) OVERRIDE;
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) OVERRIDE;
+  void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                          CefRenderHandler::PaintElementType type,
+                          const CefRenderHandler::RectList& dirtyRects,
+                          void* share_handle) OVERRIDE;
+
+ private:
+  void Render() OVERRIDE;
+
+  uint64_t start_time_;
+  std::shared_ptr<d3d11::Device> device_;
+  std::shared_ptr<d3d11::SwapChain> swap_chain_;
+  std::shared_ptr<d3d11::Composition> composition_;
+  std::shared_ptr<BrowserLayer> browser_layer_;
+  std::shared_ptr<PopupLayer> popup_layer_;
+
+  DISALLOW_COPY_AND_ASSIGN(OsrRenderHandlerWinD3D11);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_D3D11_H_
diff --git a/src/tests/cefclient/browser/osr_render_handler_win_gl.cc b/src/tests/cefclient/browser/osr_render_handler_win_gl.cc
new file mode 100644
index 0000000..6bfe1ea
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_render_handler_win_gl.cc
@@ -0,0 +1,201 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_render_handler_win_gl.h"
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+namespace {
+
+// Helper that calls wglMakeCurrent.
+class ScopedGLContext {
+ public:
+  ScopedGLContext(HDC hdc, HGLRC hglrc, bool swap_buffers)
+      : hdc_(hdc), swap_buffers_(swap_buffers) {
+    BOOL result = wglMakeCurrent(hdc, hglrc);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK(result);
+  }
+  ~ScopedGLContext() {
+    BOOL result = wglMakeCurrent(NULL, NULL);
+    DCHECK(result);
+    if (swap_buffers_) {
+      result = SwapBuffers(hdc_);
+      DCHECK(result);
+    }
+  }
+
+ private:
+  const HDC hdc_;
+  const bool swap_buffers_;
+};
+
+}  // namespace
+
+OsrRenderHandlerWinGL::OsrRenderHandlerWinGL(
+    const OsrRendererSettings& settings,
+    HWND hwnd)
+    : OsrRenderHandlerWin(settings, hwnd),
+      renderer_(settings),
+      hdc_(NULL),
+      hrc_(NULL),
+      painting_popup_(false) {}
+
+void OsrRenderHandlerWinGL::Initialize(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  SetBrowser(browser);
+}
+
+OsrRenderHandlerWinGL::~OsrRenderHandlerWinGL() {
+  CEF_REQUIRE_UI_THREAD();
+  DisableGL();
+}
+
+void OsrRenderHandlerWinGL::SetSpin(float spinX, float spinY) {
+  CEF_REQUIRE_UI_THREAD();
+  renderer_.SetSpin(spinX, spinY);
+  Invalidate();
+}
+
+void OsrRenderHandlerWinGL::IncrementSpin(float spinDX, float spinDY) {
+  CEF_REQUIRE_UI_THREAD();
+  renderer_.IncrementSpin(spinDX, spinDY);
+  Invalidate();
+}
+
+bool OsrRenderHandlerWinGL::IsOverPopupWidget(int x, int y) const {
+  CEF_REQUIRE_UI_THREAD();
+  const CefRect& rc = renderer_.popup_rect();
+  int popup_right = rc.x + rc.width;
+  int popup_bottom = rc.y + rc.height;
+  return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom);
+}
+
+int OsrRenderHandlerWinGL::GetPopupXOffset() const {
+  CEF_REQUIRE_UI_THREAD();
+  return renderer_.original_popup_rect().x - renderer_.popup_rect().x;
+}
+
+int OsrRenderHandlerWinGL::GetPopupYOffset() const {
+  CEF_REQUIRE_UI_THREAD();
+  return renderer_.original_popup_rect().y - renderer_.popup_rect().y;
+}
+
+void OsrRenderHandlerWinGL::OnPopupShow(CefRefPtr<CefBrowser> browser,
+                                        bool show) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!show) {
+    renderer_.ClearPopupRects();
+    browser->GetHost()->Invalidate(PET_VIEW);
+  }
+
+  renderer_.OnPopupShow(browser, show);
+}
+
+void OsrRenderHandlerWinGL::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                                        const CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  renderer_.OnPopupSize(browser, rect);
+}
+
+void OsrRenderHandlerWinGL::OnPaint(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::PaintElementType type,
+    const CefRenderHandler::RectList& dirtyRects,
+    const void* buffer,
+    int width,
+    int height) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (painting_popup_) {
+    renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+    return;
+  }
+  if (!hdc_) {
+    EnableGL();
+  }
+
+  ScopedGLContext scoped_gl_context(hdc_, hrc_, true);
+  renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+  if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
+    painting_popup_ = true;
+    browser->GetHost()->Invalidate(PET_POPUP);
+    painting_popup_ = false;
+  }
+  renderer_.Render();
+}
+
+void OsrRenderHandlerWinGL::OnAcceleratedPaint(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::PaintElementType type,
+    const CefRenderHandler::RectList& dirtyRects,
+    void* share_handle) {
+  // Not used with this implementation.
+  NOTREACHED();
+}
+
+void OsrRenderHandlerWinGL::Render() {
+  if (!hdc_) {
+    EnableGL();
+  }
+
+  ScopedGLContext scoped_gl_context(hdc_, hrc_, true);
+  renderer_.Render();
+}
+
+void OsrRenderHandlerWinGL::EnableGL() {
+  PIXELFORMATDESCRIPTOR pfd;
+  int format;
+
+  // Get the device context.
+  hdc_ = GetDC(hwnd());
+
+  // Set the pixel format for the DC.
+  ZeroMemory(&pfd, sizeof(pfd));
+  pfd.nSize = sizeof(pfd);
+  pfd.nVersion = 1;
+  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+  pfd.iPixelType = PFD_TYPE_RGBA;
+  pfd.cColorBits = 24;
+  pfd.cDepthBits = 16;
+  pfd.iLayerType = PFD_MAIN_PLANE;
+  format = ChoosePixelFormat(hdc_, &pfd);
+  SetPixelFormat(hdc_, format, &pfd);
+
+  // Create and enable the render context.
+  hrc_ = wglCreateContext(hdc_);
+
+  ScopedGLContext scoped_gl_context(hdc_, hrc_, false);
+  renderer_.Initialize();
+}
+
+void OsrRenderHandlerWinGL::DisableGL() {
+  if (!hdc_) {
+    return;
+  }
+
+  {
+    ScopedGLContext scoped_gl_context(hdc_, hrc_, false);
+    renderer_.Cleanup();
+  }
+
+  if (IsWindow(hwnd())) {
+    // wglDeleteContext will make the context not current before deleting it.
+    BOOL result = wglDeleteContext(hrc_);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK(result);
+    ReleaseDC(hwnd(), hdc_);
+  }
+
+  hdc_ = NULL;
+  hrc_ = NULL;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_render_handler_win_gl.h b/src/tests/cefclient/browser/osr_render_handler_win_gl.h
new file mode 100644
index 0000000..606bc70
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_render_handler_win_gl.h
@@ -0,0 +1,57 @@
+// Copyright 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_GL_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_GL_H_
+#pragma once
+
+#include "tests/cefclient/browser/osr_render_handler_win.h"
+#include "tests/cefclient/browser/osr_renderer.h"
+
+namespace client {
+
+class OsrRenderHandlerWinGL : public OsrRenderHandlerWin {
+ public:
+  OsrRenderHandlerWinGL(const OsrRendererSettings& settings, HWND hwnd);
+  virtual ~OsrRenderHandlerWinGL();
+
+  // Must be called immediately after object creation.
+  void Initialize(CefRefPtr<CefBrowser> browser);
+
+  void SetSpin(float spinX, float spinY) OVERRIDE;
+  void IncrementSpin(float spinDX, float spinDY) OVERRIDE;
+  bool IsOverPopupWidget(int x, int y) const OVERRIDE;
+  int GetPopupXOffset() const OVERRIDE;
+  int GetPopupYOffset() const OVERRIDE;
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) OVERRIDE;
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) OVERRIDE;
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) OVERRIDE;
+  void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                          CefRenderHandler::PaintElementType type,
+                          const CefRenderHandler::RectList& dirtyRects,
+                          void* share_handle) OVERRIDE;
+
+ private:
+  void Render() OVERRIDE;
+
+  void EnableGL();
+  void DisableGL();
+
+  // The below members are only accessed on the UI thread.
+  OsrRenderer renderer_;
+  HDC hdc_;
+  HGLRC hrc_;
+  bool painting_popup_;
+
+  DISALLOW_COPY_AND_ASSIGN(OsrRenderHandlerWinGL);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDER_HANDLER_WIN_GL_H_
diff --git a/src/tests/cefclient/browser/osr_renderer.cc b/src/tests/cefclient/browser/osr_renderer.cc
new file mode 100644
index 0000000..de2473a
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_renderer.cc
@@ -0,0 +1,398 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_renderer.h"
+
+#if defined(OS_WIN)
+#include <gl/gl.h>
+#elif defined(OS_MACOSX)
+#include <OpenGL/gl.h>
+#elif defined(OS_LINUX)
+#include <GL/gl.h>
+#else
+#error Platform is not supported.
+#endif
+
+#include "include/base/cef_logging.h"
+#include "include/wrapper/cef_helpers.h"
+
+#ifndef GL_BGR
+#define GL_BGR 0x80E0
+#endif
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+
+// DCHECK on gl errors.
+#if DCHECK_IS_ON()
+#define VERIFY_NO_ERROR                                                      \
+  {                                                                          \
+    int _gl_error = glGetError();                                            \
+    DCHECK(_gl_error == GL_NO_ERROR) << "glGetError returned " << _gl_error; \
+  }
+#else
+#define VERIFY_NO_ERROR
+#endif
+
+namespace client {
+
+OsrRenderer::OsrRenderer(const OsrRendererSettings& settings)
+    : settings_(settings),
+      initialized_(false),
+      texture_id_(0),
+      view_width_(0),
+      view_height_(0),
+      spin_x_(0),
+      spin_y_(0) {}
+
+OsrRenderer::~OsrRenderer() {
+  Cleanup();
+}
+
+void OsrRenderer::Initialize() {
+  if (initialized_)
+    return;
+
+  glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
+  VERIFY_NO_ERROR;
+
+  if (IsTransparent()) {
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    VERIFY_NO_ERROR;
+  } else {
+    glClearColor(float(CefColorGetR(settings_.background_color)) / 255.0f,
+                 float(CefColorGetG(settings_.background_color)) / 255.0f,
+                 float(CefColorGetB(settings_.background_color)) / 255.0f,
+                 1.0f);
+    VERIFY_NO_ERROR;
+  }
+
+  // Necessary for non-power-of-2 textures to render correctly.
+  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+  VERIFY_NO_ERROR;
+
+  // Create the texture.
+  glGenTextures(1, &texture_id_);
+  VERIFY_NO_ERROR;
+  DCHECK_NE(texture_id_, 0U);
+  VERIFY_NO_ERROR;
+
+  glBindTexture(GL_TEXTURE_2D, texture_id_);
+  VERIFY_NO_ERROR;
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  VERIFY_NO_ERROR;
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  VERIFY_NO_ERROR;
+  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+  VERIFY_NO_ERROR;
+
+  initialized_ = true;
+}
+
+void OsrRenderer::Cleanup() {
+  if (texture_id_ != 0)
+    glDeleteTextures(1, &texture_id_);
+}
+
+void OsrRenderer::Render() {
+  if (view_width_ == 0 || view_height_ == 0)
+    return;
+
+  DCHECK(initialized_);
+
+  struct {
+    float tu, tv;
+    float x, y, z;
+  } static vertices[] = {{0.0f, 1.0f, -1.0f, -1.0f, 0.0f},
+                         {1.0f, 1.0f, 1.0f, -1.0f, 0.0f},
+                         {1.0f, 0.0f, 1.0f, 1.0f, 0.0f},
+                         {0.0f, 0.0f, -1.0f, 1.0f, 0.0f}};
+
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  VERIFY_NO_ERROR;
+
+  glMatrixMode(GL_MODELVIEW);
+  VERIFY_NO_ERROR;
+  glLoadIdentity();
+  VERIFY_NO_ERROR;
+
+  // Match GL units to screen coordinates.
+  glViewport(0, 0, view_width_, view_height_);
+  VERIFY_NO_ERROR;
+  glMatrixMode(GL_PROJECTION);
+  VERIFY_NO_ERROR;
+  glLoadIdentity();
+  VERIFY_NO_ERROR;
+
+  // Draw the background gradient.
+  glPushAttrib(GL_ALL_ATTRIB_BITS);
+  VERIFY_NO_ERROR;
+  // Don't check for errors until glEnd().
+  glBegin(GL_QUADS);
+  glColor4f(1.0, 0.0, 0.0, 1.0);  // red
+  glVertex2f(-1.0, -1.0);
+  glVertex2f(1.0, -1.0);
+  glColor4f(0.0, 0.0, 1.0, 1.0);  // blue
+  glVertex2f(1.0, 1.0);
+  glVertex2f(-1.0, 1.0);
+  glEnd();
+  VERIFY_NO_ERROR;
+  glPopAttrib();
+  VERIFY_NO_ERROR;
+
+  // Rotate the view based on the mouse spin.
+  if (spin_x_ != 0) {
+    glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f);
+    VERIFY_NO_ERROR;
+  }
+  if (spin_y_ != 0) {
+    glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f);
+    VERIFY_NO_ERROR;
+  }
+
+  if (IsTransparent()) {
+    // Alpha blending style. Texture values have premultiplied alpha.
+    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+    VERIFY_NO_ERROR;
+
+    // Enable alpha blending.
+    glEnable(GL_BLEND);
+    VERIFY_NO_ERROR;
+  }
+
+  // Enable 2D textures.
+  glEnable(GL_TEXTURE_2D);
+  VERIFY_NO_ERROR;
+
+  // Draw the facets with the texture.
+  DCHECK_NE(texture_id_, 0U);
+  VERIFY_NO_ERROR;
+  glBindTexture(GL_TEXTURE_2D, texture_id_);
+  VERIFY_NO_ERROR;
+  glInterleavedArrays(GL_T2F_V3F, 0, vertices);
+  VERIFY_NO_ERROR;
+  glDrawArrays(GL_QUADS, 0, 4);
+  VERIFY_NO_ERROR;
+
+  // Disable 2D textures.
+  glDisable(GL_TEXTURE_2D);
+  VERIFY_NO_ERROR;
+
+  if (IsTransparent()) {
+    // Disable alpha blending.
+    glDisable(GL_BLEND);
+    VERIFY_NO_ERROR;
+  }
+
+  // Draw a rectangle around the update region.
+  if (settings_.show_update_rect && !update_rect_.IsEmpty()) {
+    int left = update_rect_.x;
+    int right = update_rect_.x + update_rect_.width;
+    int top = update_rect_.y;
+    int bottom = update_rect_.y + update_rect_.height;
+
+#if defined(OS_LINUX)
+    // Shrink the box so that top & right sides are drawn.
+    top += 1;
+    right -= 1;
+#else
+    // Shrink the box so that left & bottom sides are drawn.
+    left += 1;
+    bottom -= 1;
+#endif
+
+    glPushAttrib(GL_ALL_ATTRIB_BITS);
+    VERIFY_NO_ERROR
+    glMatrixMode(GL_PROJECTION);
+    VERIFY_NO_ERROR;
+    glPushMatrix();
+    VERIFY_NO_ERROR;
+    glLoadIdentity();
+    VERIFY_NO_ERROR;
+    glOrtho(0, view_width_, view_height_, 0, 0, 1);
+    VERIFY_NO_ERROR;
+
+    glLineWidth(1);
+    VERIFY_NO_ERROR;
+    glColor3f(1.0f, 0.0f, 0.0f);
+    VERIFY_NO_ERROR;
+    // Don't check for errors until glEnd().
+    glBegin(GL_LINE_STRIP);
+    glVertex2i(left, top);
+    glVertex2i(right, top);
+    glVertex2i(right, bottom);
+    glVertex2i(left, bottom);
+    glVertex2i(left, top);
+    glEnd();
+    VERIFY_NO_ERROR;
+
+    glPopMatrix();
+    VERIFY_NO_ERROR;
+    glPopAttrib();
+    VERIFY_NO_ERROR;
+  }
+}
+
+void OsrRenderer::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {
+  if (!show) {
+    // Clear the popup rectangle.
+    ClearPopupRects();
+  }
+}
+
+void OsrRenderer::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                              const CefRect& rect) {
+  if (rect.width <= 0 || rect.height <= 0)
+    return;
+  original_popup_rect_ = rect;
+  popup_rect_ = GetPopupRectInWebView(original_popup_rect_);
+}
+
+CefRect OsrRenderer::GetPopupRectInWebView(const CefRect& original_rect) {
+  CefRect rc(original_rect);
+  // if x or y are negative, move them to 0.
+  if (rc.x < 0)
+    rc.x = 0;
+  if (rc.y < 0)
+    rc.y = 0;
+  // if popup goes outside the view, try to reposition origin
+  if (rc.x + rc.width > view_width_)
+    rc.x = view_width_ - rc.width;
+  if (rc.y + rc.height > view_height_)
+    rc.y = view_height_ - rc.height;
+  // if x or y became negative, move them to 0 again.
+  if (rc.x < 0)
+    rc.x = 0;
+  if (rc.y < 0)
+    rc.y = 0;
+  return rc;
+}
+
+void OsrRenderer::ClearPopupRects() {
+  popup_rect_.Set(0, 0, 0, 0);
+  original_popup_rect_.Set(0, 0, 0, 0);
+}
+
+void OsrRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
+                          CefRenderHandler::PaintElementType type,
+                          const CefRenderHandler::RectList& dirtyRects,
+                          const void* buffer,
+                          int width,
+                          int height) {
+  if (!initialized_)
+    Initialize();
+
+  if (IsTransparent()) {
+    // Enable alpha blending.
+    glEnable(GL_BLEND);
+    VERIFY_NO_ERROR;
+  }
+
+  // Enable 2D textures.
+  glEnable(GL_TEXTURE_2D);
+  VERIFY_NO_ERROR;
+
+  DCHECK_NE(texture_id_, 0U);
+  glBindTexture(GL_TEXTURE_2D, texture_id_);
+  VERIFY_NO_ERROR;
+
+  if (type == PET_VIEW) {
+    int old_width = view_width_;
+    int old_height = view_height_;
+
+    view_width_ = width;
+    view_height_ = height;
+
+    if (settings_.show_update_rect)
+      update_rect_ = dirtyRects[0];
+
+    glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_);
+    VERIFY_NO_ERROR;
+
+    if (old_width != view_width_ || old_height != view_height_ ||
+        (dirtyRects.size() == 1 &&
+         dirtyRects[0] == CefRect(0, 0, view_width_, view_height_))) {
+      // Update/resize the whole texture.
+      glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+      VERIFY_NO_ERROR;
+      glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+      VERIFY_NO_ERROR;
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width_, view_height_, 0,
+                   GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
+      VERIFY_NO_ERROR;
+    } else {
+      // Update just the dirty rectangles.
+      CefRenderHandler::RectList::const_iterator i = dirtyRects.begin();
+      for (; i != dirtyRects.end(); ++i) {
+        const CefRect& rect = *i;
+        DCHECK(rect.x + rect.width <= view_width_);
+        DCHECK(rect.y + rect.height <= view_height_);
+        glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
+        VERIFY_NO_ERROR;
+        glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y);
+        VERIFY_NO_ERROR;
+        glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width,
+                        rect.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+                        buffer);
+        VERIFY_NO_ERROR;
+      }
+    }
+  } else if (type == PET_POPUP && popup_rect_.width > 0 &&
+             popup_rect_.height > 0) {
+    int skip_pixels = 0, x = popup_rect_.x;
+    int skip_rows = 0, y = popup_rect_.y;
+    int w = width;
+    int h = height;
+
+    // Adjust the popup to fit inside the view.
+    if (x < 0) {
+      skip_pixels = -x;
+      x = 0;
+    }
+    if (y < 0) {
+      skip_rows = -y;
+      y = 0;
+    }
+    if (x + w > view_width_)
+      w -= x + w - view_width_;
+    if (y + h > view_height_)
+      h -= y + h - view_height_;
+
+    // Update the popup rectangle.
+    glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
+    VERIFY_NO_ERROR;
+    glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
+    VERIFY_NO_ERROR;
+    glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
+    VERIFY_NO_ERROR;
+    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_BGRA,
+                    GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
+    VERIFY_NO_ERROR;
+  }
+
+  // Disable 2D textures.
+  glDisable(GL_TEXTURE_2D);
+  VERIFY_NO_ERROR;
+
+  if (IsTransparent()) {
+    // Disable alpha blending.
+    glDisable(GL_BLEND);
+    VERIFY_NO_ERROR;
+  }
+}
+
+void OsrRenderer::SetSpin(float spinX, float spinY) {
+  spin_x_ = spinX;
+  spin_y_ = spinY;
+}
+
+void OsrRenderer::IncrementSpin(float spinDX, float spinDY) {
+  spin_x_ -= spinDX;
+  spin_y_ -= spinDY;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_renderer.h b/src/tests/cefclient/browser/osr_renderer.h
new file mode 100644
index 0000000..f66cc24
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_renderer.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDERER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDERER_H_
+#pragma once
+
+#include "include/cef_browser.h"
+#include "include/cef_render_handler.h"
+#include "tests/cefclient/browser/osr_renderer_settings.h"
+
+namespace client {
+
+class OsrRenderer {
+ public:
+  explicit OsrRenderer(const OsrRendererSettings& settings);
+  ~OsrRenderer();
+
+  // Initialize the OpenGL environment.
+  void Initialize();
+
+  // Clean up the OpenGL environment.
+  void Cleanup();
+
+  // Render to the screen.
+  void Render();
+
+  // Forwarded from CefRenderHandler callbacks.
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show);
+  // |rect| must be in pixel coordinates.
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect);
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height);
+
+  // Apply spin.
+  void SetSpin(float spinX, float spinY);
+  void IncrementSpin(float spinDX, float spinDY);
+
+  int GetViewWidth() const { return view_width_; }
+  int GetViewHeight() const { return view_height_; }
+
+  CefRect popup_rect() const { return popup_rect_; }
+  CefRect original_popup_rect() const { return original_popup_rect_; }
+
+  void ClearPopupRects();
+
+ private:
+  CefRect GetPopupRectInWebView(const CefRect& original_rect);
+
+  inline bool IsTransparent() const {
+    return CefColorGetA(settings_.background_color) == 0;
+  }
+
+  const OsrRendererSettings settings_;
+  bool initialized_;
+  unsigned int texture_id_;
+  int view_width_;
+  int view_height_;
+  CefRect popup_rect_;
+  CefRect original_popup_rect_;
+  float spin_x_;
+  float spin_y_;
+  CefRect update_rect_;
+
+  DISALLOW_COPY_AND_ASSIGN(OsrRenderer);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDERER_H_
diff --git a/src/tests/cefclient/browser/osr_renderer_settings.h b/src/tests/cefclient/browser/osr_renderer_settings.h
new file mode 100644
index 0000000..535b10a
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_renderer_settings.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDERER_SETTINGS_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDERER_SETTINGS_H_
+#pragma once
+
+#include "include/internal/cef_types.h"
+
+namespace client {
+
+struct OsrRendererSettings {
+  OsrRendererSettings()
+      : show_update_rect(false),
+        background_color(CefColorSetARGB(255, 255, 255, 255)),
+        shared_texture_enabled(false),
+        external_begin_frame_enabled(false),
+        begin_frame_rate(0) {}
+
+  // If true draw a border around update rectangles.
+  bool show_update_rect;
+
+  // Background color. Enables transparency if the alpha component is 0.
+  cef_color_t background_color;
+
+  // Render using shared textures. Supported on Windows only via D3D11.
+  bool shared_texture_enabled;
+
+  // Client implements a BeginFrame timer by calling
+  // CefBrowserHost::SendExternalBeginFrame at the specified frame rate.
+  bool external_begin_frame_enabled;
+  int begin_frame_rate;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_RENDERER_SETTINGS_H_
diff --git a/src/tests/cefclient/browser/osr_window_win.cc b/src/tests/cefclient/browser/osr_window_win.cc
new file mode 100644
index 0000000..66b220d
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_window_win.cc
@@ -0,0 +1,1196 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_window_win.h"
+
+#include <windowsx.h>
+#if defined(CEF_USE_ATL)
+#include <oleacc.h>
+#endif
+
+#include "include/base/cef_build.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/osr_accessibility_helper.h"
+#include "tests/cefclient/browser/osr_accessibility_node.h"
+#include "tests/cefclient/browser/osr_ime_handler_win.h"
+#include "tests/cefclient/browser/osr_render_handler_win_d3d11.h"
+#include "tests/cefclient/browser/osr_render_handler_win_gl.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/shared/browser/geometry_util.h"
+#include "tests/shared/browser/main_message_loop.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+namespace {
+
+const wchar_t kWndClass[] = L"Client_OsrWindow";
+
+// Helper funtion to check if it is Windows8 or greater.
+// https://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx
+inline BOOL IsWindows_8_Or_Newer() {
+  OSVERSIONINFOEX osvi = {0};
+  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+  osvi.dwMajorVersion = 6;
+  osvi.dwMinorVersion = 2;
+  DWORDLONG dwlConditionMask = 0;
+  VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+  VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
+  return ::VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION,
+                             dwlConditionMask);
+}
+
+// Helper function to detect mouse messages coming from emulation of touch
+// events. These should be ignored.
+bool IsMouseEventFromTouch(UINT message) {
+#define MOUSEEVENTF_FROMTOUCH 0xFF515700
+  return (message >= WM_MOUSEFIRST) && (message <= WM_MOUSELAST) &&
+         (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) ==
+             MOUSEEVENTF_FROMTOUCH;
+}
+
+class CreateBrowserHelper {
+ public:
+  CreateBrowserHelper(HWND hwnd,
+                      const RECT& rect,
+                      CefRefPtr<CefClient> handler,
+                      const std::string& url,
+                      const CefBrowserSettings& settings,
+                      CefRefPtr<CefDictionaryValue> extra_info,
+                      CefRefPtr<CefRequestContext> request_context,
+                      OsrWindowWin* osr_window_win)
+      : hwnd_(hwnd),
+        rect_(rect),
+        handler_(handler),
+        url_(url),
+        settings_(settings),
+        extra_info_(extra_info),
+        request_context_(request_context),
+        osr_window_win_(osr_window_win) {}
+
+  HWND hwnd_;
+  RECT rect_;
+  CefRefPtr<CefClient> handler_;
+  std::string url_;
+  CefBrowserSettings settings_;
+  CefRefPtr<CefDictionaryValue> extra_info_;
+  CefRefPtr<CefRequestContext> request_context_;
+  OsrWindowWin* osr_window_win_;
+};
+
+}  // namespace
+
+OsrWindowWin::OsrWindowWin(Delegate* delegate,
+                           const OsrRendererSettings& settings)
+    : delegate_(delegate),
+      settings_(settings),
+      hwnd_(NULL),
+      device_scale_factor_(0),
+      hidden_(false),
+      last_mouse_pos_(),
+      current_mouse_pos_(),
+      mouse_rotation_(false),
+      mouse_tracking_(false),
+      last_click_x_(0),
+      last_click_y_(0),
+      last_click_button_(MBT_LEFT),
+      last_click_count_(1),
+      last_click_time_(0),
+      last_mouse_down_on_view_(false) {
+  DCHECK(delegate_);
+  client_rect_ = {0};
+}
+
+OsrWindowWin::~OsrWindowWin() {
+  CEF_REQUIRE_UI_THREAD();
+  // The native window should have already been destroyed.
+  DCHECK(!hwnd_ && !render_handler_.get());
+}
+
+void CreateBrowserWithHelper(CreateBrowserHelper* helper) {
+  helper->osr_window_win_->CreateBrowser(
+      helper->hwnd_, helper->rect_, helper->handler_, helper->settings_,
+      helper->extra_info_, helper->request_context_, helper->url_);
+  delete helper;
+}
+
+void OsrWindowWin::CreateBrowser(HWND parent_hwnd,
+                                 const RECT& rect,
+                                 CefRefPtr<CefClient> handler,
+                                 const CefBrowserSettings& settings,
+                                 CefRefPtr<CefDictionaryValue> extra_info,
+                                 CefRefPtr<CefRequestContext> request_context,
+                                 const std::string& startup_url) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CreateBrowserHelper* helper =
+        new CreateBrowserHelper(parent_hwnd, rect, handler, startup_url,
+                                settings, extra_info, request_context, this);
+    CefPostTask(TID_UI, base::Bind(CreateBrowserWithHelper, helper));
+    return;
+  }
+
+  // Create the native window.
+  Create(parent_hwnd, rect);
+
+  CefWindowInfo window_info;
+  window_info.SetAsWindowless(hwnd_);
+
+  if (GetWindowLongPtr(parent_hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
+    // Don't activate the browser window on creation.
+    window_info.ex_style |= WS_EX_NOACTIVATE;
+  }
+
+  window_info.shared_texture_enabled = settings_.shared_texture_enabled;
+  window_info.external_begin_frame_enabled =
+      settings_.external_begin_frame_enabled;
+
+  // Create the browser asynchronously.
+  CefBrowserHost::CreateBrowser(window_info, handler, startup_url, settings,
+                                extra_info, request_context);
+}
+
+void OsrWindowWin::ShowPopup(HWND parent_hwnd,
+                             int x,
+                             int y,
+                             size_t width,
+                             size_t height) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::ShowPopup, this, parent_hwnd,
+                                   x, y, width, height));
+    return;
+  }
+
+  DCHECK(browser_.get());
+
+  // Create the native window.
+  const RECT rect = {x, y, x + static_cast<int>(width),
+                     y + static_cast<int>(height)};
+  Create(parent_hwnd, rect);
+
+  // Create the render handler.
+  EnsureRenderHandler();
+  render_handler_->SetBrowser(browser_);
+
+  // Send resize notification so the compositor is assigned the correct
+  // viewport size and begins rendering.
+  browser_->GetHost()->WasResized();
+
+  Show();
+}
+
+void OsrWindowWin::Show() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::Show, this));
+    return;
+  }
+
+  if (!browser_)
+    return;
+
+  // Show the native window if not currently visible.
+  if (hwnd_ && !::IsWindowVisible(hwnd_))
+    ShowWindow(hwnd_, SW_SHOW);
+
+  if (hidden_) {
+    // Set the browser as visible.
+    browser_->GetHost()->WasHidden(false);
+    hidden_ = false;
+  }
+
+  // Give focus to the browser.
+  browser_->GetHost()->SendFocusEvent(true);
+}
+
+void OsrWindowWin::Hide() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::Hide, this));
+    return;
+  }
+
+  if (!browser_)
+    return;
+
+  // Remove focus from the browser.
+  browser_->GetHost()->SendFocusEvent(false);
+
+  if (!hidden_) {
+    // Set the browser as hidden.
+    browser_->GetHost()->WasHidden(true);
+    hidden_ = true;
+  }
+}
+
+void OsrWindowWin::SetBounds(int x, int y, size_t width, size_t height) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::SetBounds, this, x, y, width,
+                                   height));
+    return;
+  }
+
+  if (hwnd_) {
+    // Set the browser window bounds.
+    ::SetWindowPos(hwnd_, NULL, x, y, static_cast<int>(width),
+                   static_cast<int>(height), SWP_NOZORDER);
+  }
+}
+
+void OsrWindowWin::SetFocus() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::SetFocus, this));
+    return;
+  }
+
+  if (hwnd_) {
+    // Give focus to the native window.
+    ::SetFocus(hwnd_);
+  }
+}
+
+void OsrWindowWin::SetDeviceScaleFactor(float device_scale_factor) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::SetDeviceScaleFactor, this,
+                                   device_scale_factor));
+    return;
+  }
+
+  if (device_scale_factor == device_scale_factor_)
+    return;
+
+  device_scale_factor_ = device_scale_factor;
+  if (browser_) {
+    browser_->GetHost()->NotifyScreenInfoChanged();
+    browser_->GetHost()->WasResized();
+  }
+}
+
+void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(!hwnd_ && !render_handler_.get());
+  DCHECK(parent_hwnd);
+  DCHECK(!::IsRectEmpty(&rect));
+
+  HINSTANCE hInst = ::GetModuleHandle(NULL);
+
+  const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
+  const HBRUSH background_brush = CreateSolidBrush(
+      RGB(CefColorGetR(background_color), CefColorGetG(background_color),
+          CefColorGetB(background_color)));
+
+  RegisterOsrClass(hInst, background_brush);
+
+  DWORD ex_style = 0;
+  if (GetWindowLongPtr(parent_hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
+    // Don't activate the browser window on creation.
+    ex_style |= WS_EX_NOACTIVATE;
+  }
+
+  // Create the native window with a border so it's easier to visually identify
+  // OSR windows.
+  hwnd_ = ::CreateWindowEx(
+      ex_style, kWndClass, 0,
+      WS_BORDER | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
+      rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
+      parent_hwnd, 0, hInst, 0);
+  CHECK(hwnd_);
+
+  client_rect_ = rect;
+
+  // Associate |this| with the window.
+  SetUserDataPtr(hwnd_, this);
+
+#if defined(CEF_USE_ATL)
+  accessibility_root_ = nullptr;
+
+  // Create/register the drag&drop handler.
+  drop_target_ = DropTargetWin::Create(this, hwnd_);
+  HRESULT register_res = RegisterDragDrop(hwnd_, drop_target_);
+  DCHECK_EQ(register_res, S_OK);
+#endif
+
+  ime_handler_.reset(new OsrImeHandlerWin(hwnd_));
+
+  // Enable Touch Events if requested
+  if (client::MainContext::Get()->TouchEventsEnabled())
+    RegisterTouchWindow(hwnd_, 0);
+
+  // Notify the window owner.
+  NotifyNativeWindowCreated(hwnd_);
+}
+
+void OsrWindowWin::Destroy() {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(hwnd_ != NULL);
+
+#if defined(CEF_USE_ATL)
+  // Revoke/delete the drag&drop handler.
+  RevokeDragDrop(hwnd_);
+  drop_target_ = nullptr;
+#endif
+
+  render_handler_.reset();
+
+  // Destroy the native window.
+  ::DestroyWindow(hwnd_);
+  ime_handler_.reset();
+  hwnd_ = NULL;
+}
+
+void OsrWindowWin::NotifyNativeWindowCreated(HWND hwnd) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&OsrWindowWin::NotifyNativeWindowCreated, this, hwnd));
+    return;
+  }
+
+  delegate_->OnOsrNativeWindowCreated(hwnd);
+}
+
+// static
+void OsrWindowWin::RegisterOsrClass(HINSTANCE hInstance,
+                                    HBRUSH background_brush) {
+  // Only register the class one time.
+  static bool class_registered = false;
+  if (class_registered)
+    return;
+  class_registered = true;
+
+  WNDCLASSEX wcex;
+
+  wcex.cbSize = sizeof(WNDCLASSEX);
+  wcex.style = CS_OWNDC;
+  wcex.lpfnWndProc = OsrWndProc;
+  wcex.cbClsExtra = 0;
+  wcex.cbWndExtra = 0;
+  wcex.hInstance = hInstance;
+  wcex.hIcon = NULL;
+  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+  wcex.hbrBackground = background_brush;
+  wcex.lpszMenuName = NULL;
+  wcex.lpszClassName = kWndClass;
+  wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+
+  RegisterClassEx(&wcex);
+}
+
+void OsrWindowWin::OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam) {
+  // We handle the IME Composition Window ourselves (but let the IME Candidates
+  // Window be handled by IME through DefWindowProc()), so clear the
+  // ISC_SHOWUICOMPOSITIONWINDOW flag:
+  lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
+  ::DefWindowProc(hwnd_, message, wParam, lParam);
+
+  // Create Caret Window if required
+  if (ime_handler_) {
+    ime_handler_->CreateImeWindow();
+    ime_handler_->MoveImeWindow();
+  }
+}
+
+void OsrWindowWin::OnIMEStartComposition() {
+  if (ime_handler_) {
+    ime_handler_->CreateImeWindow();
+    ime_handler_->MoveImeWindow();
+    ime_handler_->ResetComposition();
+  }
+}
+
+void OsrWindowWin::OnIMEComposition(UINT message,
+                                    WPARAM wParam,
+                                    LPARAM lParam) {
+  if (browser_ && ime_handler_) {
+    CefString cTextStr;
+    if (ime_handler_->GetResult(lParam, cTextStr)) {
+      // Send the text to the browser. The |replacement_range| and
+      // |relative_cursor_pos| params are not used on Windows, so provide
+      // default invalid values.
+      browser_->GetHost()->ImeCommitText(cTextStr,
+                                         CefRange(UINT32_MAX, UINT32_MAX), 0);
+      ime_handler_->ResetComposition();
+      // Continue reading the composition string - Japanese IMEs send both
+      // GCS_RESULTSTR and GCS_COMPSTR.
+    }
+
+    std::vector<CefCompositionUnderline> underlines;
+    int composition_start = 0;
+
+    if (ime_handler_->GetComposition(lParam, cTextStr, underlines,
+                                     composition_start)) {
+      // Send the composition string to the browser. The |replacement_range|
+      // param is not used on Windows, so provide a default invalid value.
+      browser_->GetHost()->ImeSetComposition(
+          cTextStr, underlines, CefRange(UINT32_MAX, UINT32_MAX),
+          CefRange(composition_start,
+                   static_cast<int>(composition_start + cTextStr.length())));
+
+      // Update the Candidate Window position. The cursor is at the end so
+      // subtract 1. This is safe because IMM32 does not support non-zero-width
+      // in a composition. Also,  negative values are safely ignored in
+      // MoveImeWindow
+      ime_handler_->UpdateCaretPosition(composition_start - 1);
+    } else {
+      OnIMECancelCompositionEvent();
+    }
+  }
+}
+
+void OsrWindowWin::OnIMECancelCompositionEvent() {
+  if (browser_ && ime_handler_) {
+    browser_->GetHost()->ImeCancelComposition();
+    ime_handler_->ResetComposition();
+    ime_handler_->DestroyImeWindow();
+  }
+}
+
+// static
+LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd,
+                                          UINT message,
+                                          WPARAM wParam,
+                                          LPARAM lParam) {
+  CEF_REQUIRE_UI_THREAD();
+
+  OsrWindowWin* self = GetUserDataPtr<OsrWindowWin*>(hWnd);
+  if (!self)
+    return DefWindowProc(hWnd, message, wParam, lParam);
+
+  // We want to handle IME events before the OS does any default handling.
+  switch (message) {
+    case WM_IME_SETCONTEXT:
+      self->OnIMESetContext(message, wParam, lParam);
+      return 0;
+    case WM_IME_STARTCOMPOSITION:
+      self->OnIMEStartComposition();
+      return 0;
+    case WM_IME_COMPOSITION:
+      self->OnIMEComposition(message, wParam, lParam);
+      return 0;
+    case WM_IME_ENDCOMPOSITION:
+      self->OnIMECancelCompositionEvent();
+      // Let WTL call::DefWindowProc() and release its resources.
+      break;
+#if defined(CEF_USE_ATL)
+    case WM_GETOBJECT: {
+      // Only the lower 32 bits of lParam are valid when checking the object id
+      // because it sometimes gets sign-extended incorrectly (but not always).
+      DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
+
+      // Accessibility readers will send an OBJID_CLIENT message.
+      if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
+        if (self->accessibility_root_) {
+          return LresultFromObject(
+              IID_IAccessible, wParam,
+              static_cast<IAccessible*>(self->accessibility_root_));
+        } else {
+          // Notify the renderer to enable accessibility.
+          if (self->browser_ && self->browser_->GetHost())
+            self->browser_->GetHost()->SetAccessibilityState(STATE_ENABLED);
+        }
+      }
+    } break;
+#endif
+    case WM_LBUTTONDOWN:
+    case WM_RBUTTONDOWN:
+    case WM_MBUTTONDOWN:
+    case WM_LBUTTONUP:
+    case WM_RBUTTONUP:
+    case WM_MBUTTONUP:
+    case WM_MOUSEMOVE:
+    case WM_MOUSELEAVE:
+    case WM_MOUSEWHEEL:
+      self->OnMouseEvent(message, wParam, lParam);
+      break;
+
+    case WM_SIZE:
+      self->OnSize();
+      break;
+
+    case WM_SETFOCUS:
+    case WM_KILLFOCUS:
+      self->OnFocus(message == WM_SETFOCUS);
+      break;
+
+    case WM_CAPTURECHANGED:
+    case WM_CANCELMODE:
+      self->OnCaptureLost();
+      break;
+
+    case WM_SYSCHAR:
+    case WM_SYSKEYDOWN:
+    case WM_SYSKEYUP:
+    case WM_KEYDOWN:
+    case WM_KEYUP:
+    case WM_CHAR:
+      self->OnKeyEvent(message, wParam, lParam);
+      break;
+
+    case WM_PAINT:
+      self->OnPaint();
+      return 0;
+
+    case WM_ERASEBKGND:
+      if (self->OnEraseBkgnd())
+        break;
+      // Don't erase the background.
+      return 0;
+
+    // If your application does not require Win7 support, please do consider
+    // using WM_POINTER* messages instead of WM_TOUCH. WM_POINTER are more
+    // intutive, complete and simpler to code.
+    // https://msdn.microsoft.com/en-us/library/hh454903(v=vs.85).aspx
+    case WM_TOUCH:
+      if (self->OnTouchEvent(message, wParam, lParam))
+        return 0;
+      break;
+
+    case WM_NCDESTROY:
+      // Clear the reference to |self|.
+      SetUserDataPtr(hWnd, NULL);
+      self->hwnd_ = NULL;
+      break;
+  }
+
+  return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+void OsrWindowWin::OnMouseEvent(UINT message, WPARAM wParam, LPARAM lParam) {
+  if (IsMouseEventFromTouch(message))
+    return;
+
+  CefRefPtr<CefBrowserHost> browser_host;
+  if (browser_)
+    browser_host = browser_->GetHost();
+
+  LONG currentTime = 0;
+  bool cancelPreviousClick = false;
+
+  if (message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN ||
+      message == WM_MBUTTONDOWN || message == WM_MOUSEMOVE ||
+      message == WM_MOUSELEAVE) {
+    currentTime = GetMessageTime();
+    int x = GET_X_LPARAM(lParam);
+    int y = GET_Y_LPARAM(lParam);
+    cancelPreviousClick =
+        (abs(last_click_x_ - x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) ||
+        (abs(last_click_y_ - y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2)) ||
+        ((currentTime - last_click_time_) > GetDoubleClickTime());
+    if (cancelPreviousClick &&
+        (message == WM_MOUSEMOVE || message == WM_MOUSELEAVE)) {
+      last_click_count_ = 1;
+      last_click_x_ = 0;
+      last_click_y_ = 0;
+      last_click_time_ = 0;
+    }
+  }
+
+  switch (message) {
+    case WM_LBUTTONDOWN:
+    case WM_RBUTTONDOWN:
+    case WM_MBUTTONDOWN: {
+      ::SetCapture(hwnd_);
+      ::SetFocus(hwnd_);
+      int x = GET_X_LPARAM(lParam);
+      int y = GET_Y_LPARAM(lParam);
+      if (wParam & MK_SHIFT) {
+        // Start rotation effect.
+        last_mouse_pos_.x = current_mouse_pos_.x = x;
+        last_mouse_pos_.y = current_mouse_pos_.y = y;
+        mouse_rotation_ = true;
+      } else {
+        CefBrowserHost::MouseButtonType btnType =
+            (message == WM_LBUTTONDOWN
+                 ? MBT_LEFT
+                 : (message == WM_RBUTTONDOWN ? MBT_RIGHT : MBT_MIDDLE));
+        if (!cancelPreviousClick && (btnType == last_click_button_)) {
+          ++last_click_count_;
+        } else {
+          last_click_count_ = 1;
+          last_click_x_ = x;
+          last_click_y_ = y;
+        }
+        last_click_time_ = currentTime;
+        last_click_button_ = btnType;
+
+        if (browser_host) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = x;
+          mouse_event.y = y;
+          last_mouse_down_on_view_ = !IsOverPopupWidget(x, y);
+          ApplyPopupOffset(mouse_event.x, mouse_event.y);
+          DeviceToLogical(mouse_event, device_scale_factor_);
+          mouse_event.modifiers = GetCefMouseModifiers(wParam);
+          browser_host->SendMouseClickEvent(mouse_event, btnType, false,
+                                            last_click_count_);
+        }
+      }
+    } break;
+
+    case WM_LBUTTONUP:
+    case WM_RBUTTONUP:
+    case WM_MBUTTONUP:
+      if (GetCapture() == hwnd_)
+        ReleaseCapture();
+      if (mouse_rotation_) {
+        // End rotation effect.
+        mouse_rotation_ = false;
+        render_handler_->SetSpin(0, 0);
+      } else {
+        int x = GET_X_LPARAM(lParam);
+        int y = GET_Y_LPARAM(lParam);
+        CefBrowserHost::MouseButtonType btnType =
+            (message == WM_LBUTTONUP
+                 ? MBT_LEFT
+                 : (message == WM_RBUTTONUP ? MBT_RIGHT : MBT_MIDDLE));
+        if (browser_host) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = x;
+          mouse_event.y = y;
+          if (last_mouse_down_on_view_ && IsOverPopupWidget(x, y) &&
+              (GetPopupXOffset() || GetPopupYOffset())) {
+            break;
+          }
+          ApplyPopupOffset(mouse_event.x, mouse_event.y);
+          DeviceToLogical(mouse_event, device_scale_factor_);
+          mouse_event.modifiers = GetCefMouseModifiers(wParam);
+          browser_host->SendMouseClickEvent(mouse_event, btnType, true,
+                                            last_click_count_);
+        }
+      }
+      break;
+
+    case WM_MOUSEMOVE: {
+      int x = GET_X_LPARAM(lParam);
+      int y = GET_Y_LPARAM(lParam);
+      if (mouse_rotation_) {
+        // Apply rotation effect.
+        current_mouse_pos_.x = x;
+        current_mouse_pos_.y = y;
+        render_handler_->IncrementSpin(
+            current_mouse_pos_.x - last_mouse_pos_.x,
+            current_mouse_pos_.y - last_mouse_pos_.y);
+        last_mouse_pos_.x = current_mouse_pos_.x;
+        last_mouse_pos_.y = current_mouse_pos_.y;
+      } else {
+        if (!mouse_tracking_) {
+          // Start tracking mouse leave. Required for the WM_MOUSELEAVE event to
+          // be generated.
+          TRACKMOUSEEVENT tme;
+          tme.cbSize = sizeof(TRACKMOUSEEVENT);
+          tme.dwFlags = TME_LEAVE;
+          tme.hwndTrack = hwnd_;
+          TrackMouseEvent(&tme);
+          mouse_tracking_ = true;
+        }
+
+        if (browser_host) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = x;
+          mouse_event.y = y;
+          ApplyPopupOffset(mouse_event.x, mouse_event.y);
+          DeviceToLogical(mouse_event, device_scale_factor_);
+          mouse_event.modifiers = GetCefMouseModifiers(wParam);
+          browser_host->SendMouseMoveEvent(mouse_event, false);
+        }
+      }
+      break;
+    }
+
+    case WM_MOUSELEAVE: {
+      if (mouse_tracking_) {
+        // Stop tracking mouse leave.
+        TRACKMOUSEEVENT tme;
+        tme.cbSize = sizeof(TRACKMOUSEEVENT);
+        tme.dwFlags = TME_LEAVE & TME_CANCEL;
+        tme.hwndTrack = hwnd_;
+        TrackMouseEvent(&tme);
+        mouse_tracking_ = false;
+      }
+
+      if (browser_host) {
+        // Determine the cursor position in screen coordinates.
+        POINT p;
+        ::GetCursorPos(&p);
+        ::ScreenToClient(hwnd_, &p);
+
+        CefMouseEvent mouse_event;
+        mouse_event.x = p.x;
+        mouse_event.y = p.y;
+        DeviceToLogical(mouse_event, device_scale_factor_);
+        mouse_event.modifiers = GetCefMouseModifiers(wParam);
+        browser_host->SendMouseMoveEvent(mouse_event, true);
+      }
+    } break;
+
+    case WM_MOUSEWHEEL:
+      if (browser_host) {
+        POINT screen_point = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
+        HWND scrolled_wnd = ::WindowFromPoint(screen_point);
+        if (scrolled_wnd != hwnd_)
+          break;
+
+        ScreenToClient(hwnd_, &screen_point);
+        int delta = GET_WHEEL_DELTA_WPARAM(wParam);
+
+        CefMouseEvent mouse_event;
+        mouse_event.x = screen_point.x;
+        mouse_event.y = screen_point.y;
+        ApplyPopupOffset(mouse_event.x, mouse_event.y);
+        DeviceToLogical(mouse_event, device_scale_factor_);
+        mouse_event.modifiers = GetCefMouseModifiers(wParam);
+        browser_host->SendMouseWheelEvent(mouse_event,
+                                          IsKeyDown(VK_SHIFT) ? delta : 0,
+                                          !IsKeyDown(VK_SHIFT) ? delta : 0);
+      }
+      break;
+  }
+}
+
+void OsrWindowWin::OnSize() {
+  // Keep |client_rect_| up to date.
+  ::GetClientRect(hwnd_, &client_rect_);
+
+  if (browser_)
+    browser_->GetHost()->WasResized();
+}
+
+void OsrWindowWin::OnFocus(bool setFocus) {
+  if (browser_)
+    browser_->GetHost()->SendFocusEvent(setFocus);
+}
+
+void OsrWindowWin::OnCaptureLost() {
+  if (mouse_rotation_)
+    return;
+
+  if (browser_)
+    browser_->GetHost()->SendCaptureLostEvent();
+}
+
+void OsrWindowWin::OnKeyEvent(UINT message, WPARAM wParam, LPARAM lParam) {
+  if (!browser_)
+    return;
+
+  CefKeyEvent event;
+  event.windows_key_code = wParam;
+  event.native_key_code = lParam;
+  event.is_system_key = message == WM_SYSCHAR || message == WM_SYSKEYDOWN ||
+                        message == WM_SYSKEYUP;
+
+  if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
+    event.type = KEYEVENT_RAWKEYDOWN;
+  else if (message == WM_KEYUP || message == WM_SYSKEYUP)
+    event.type = KEYEVENT_KEYUP;
+  else
+    event.type = KEYEVENT_CHAR;
+  event.modifiers = GetCefKeyboardModifiers(wParam, lParam);
+
+  // mimic alt-gr check behaviour from
+  // src/ui/events/win/events_win_utils.cc: GetModifiersFromKeyState
+  if ((event.type == KEYEVENT_CHAR) && IsKeyDown(VK_RMENU)) {
+    // reverse AltGr detection taken from PlatformKeyMap::UsesAltGraph
+    // instead of checking all combination for ctrl-alt, just check current char
+    HKL current_layout = ::GetKeyboardLayout(0);
+
+    // https://docs.microsoft.com/en-gb/windows/win32/api/winuser/nf-winuser-vkkeyscanexw
+    // ... high-order byte contains the shift state,
+    // which can be a combination of the following flag bits.
+    // 2 Either CTRL key is pressed.
+    // 4 Either ALT key is pressed.
+    SHORT scan_res = ::VkKeyScanExW(wParam, current_layout);
+    if (((scan_res >> 8) & 0xFF) == (2 | 4)) {  // ctrl-alt pressed
+      event.modifiers &= ~(EVENTFLAG_CONTROL_DOWN | EVENTFLAG_ALT_DOWN);
+      event.modifiers |= EVENTFLAG_ALTGR_DOWN;
+    }
+  }
+
+  browser_->GetHost()->SendKeyEvent(event);
+}
+
+void OsrWindowWin::OnPaint() {
+  // Paint nothing here. Invalidate will cause OnPaint to be called for the
+  // render handler.
+  PAINTSTRUCT ps;
+  BeginPaint(hwnd_, &ps);
+  EndPaint(hwnd_, &ps);
+
+  if (browser_)
+    browser_->GetHost()->Invalidate(PET_VIEW);
+}
+
+bool OsrWindowWin::OnEraseBkgnd() {
+  // Erase the background when the browser does not exist.
+  return (browser_ == nullptr);
+}
+
+bool OsrWindowWin::OnTouchEvent(UINT message, WPARAM wParam, LPARAM lParam) {
+  // Handle touch events on Windows.
+  int num_points = LOWORD(wParam);
+  // Chromium only supports upto 16 touch points.
+  if (num_points < 0 || num_points > 16)
+    return false;
+  std::unique_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]);
+  if (GetTouchInputInfo(reinterpret_cast<HTOUCHINPUT>(lParam), num_points,
+                        input.get(), sizeof(TOUCHINPUT))) {
+    CefTouchEvent touch_event;
+    for (int i = 0; i < num_points; ++i) {
+      POINT point;
+      point.x = TOUCH_COORD_TO_PIXEL(input[i].x);
+      point.y = TOUCH_COORD_TO_PIXEL(input[i].y);
+
+      if (!IsWindows_8_Or_Newer()) {
+        // Windows 7 sends touch events for touches in the non-client area,
+        // whereas Windows 8 does not. In order to unify the behaviour, always
+        // ignore touch events in the non-client area.
+        LPARAM l_param_ht = MAKELPARAM(point.x, point.y);
+        LRESULT hittest = SendMessage(hwnd_, WM_NCHITTEST, 0, l_param_ht);
+        if (hittest != HTCLIENT)
+          return false;
+      }
+
+      ScreenToClient(hwnd_, &point);
+      touch_event.x = DeviceToLogical(point.x, device_scale_factor_);
+      touch_event.y = DeviceToLogical(point.y, device_scale_factor_);
+
+      // Touch point identifier stays consistent in a touch contact sequence
+      touch_event.id = input[i].dwID;
+
+      if (input[i].dwFlags & TOUCHEVENTF_DOWN) {
+        touch_event.type = CEF_TET_PRESSED;
+      } else if (input[i].dwFlags & TOUCHEVENTF_MOVE) {
+        touch_event.type = CEF_TET_MOVED;
+      } else if (input[i].dwFlags & TOUCHEVENTF_UP) {
+        touch_event.type = CEF_TET_RELEASED;
+      }
+
+      touch_event.radius_x = 0;
+      touch_event.radius_y = 0;
+      touch_event.rotation_angle = 0;
+      touch_event.pressure = 0;
+      touch_event.modifiers = 0;
+
+      // Notify the browser of touch event
+      if (browser_)
+        browser_->GetHost()->SendTouchEvent(touch_event);
+    }
+    CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(lParam));
+    return true;
+  }
+
+  return false;
+}
+
+bool OsrWindowWin::IsOverPopupWidget(int x, int y) const {
+  if (!render_handler_)
+    return false;
+  return render_handler_->IsOverPopupWidget(x, y);
+}
+
+int OsrWindowWin::GetPopupXOffset() const {
+  return render_handler_->GetPopupXOffset();
+}
+
+int OsrWindowWin::GetPopupYOffset() const {
+  return render_handler_->GetPopupYOffset();
+}
+
+void OsrWindowWin::ApplyPopupOffset(int& x, int& y) const {
+  if (IsOverPopupWidget(x, y)) {
+    x += GetPopupXOffset();
+    y += GetPopupYOffset();
+  }
+}
+
+void OsrWindowWin::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(!browser_);
+  browser_ = browser;
+
+  if (hwnd_) {
+    // The native window will already exist for non-popup browsers.
+    EnsureRenderHandler();
+    render_handler_->SetBrowser(browser);
+  }
+
+  if (hwnd_) {
+    // Show the browser window. Called asynchronously so that the browser has
+    // time to create associated internal objects.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::Show, this));
+  }
+}
+
+void OsrWindowWin::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  // Detach |this| from the ClientHandlerOsr.
+  static_cast<ClientHandlerOsr*>(browser_->GetHost()->GetClient().get())
+      ->DetachOsrDelegate();
+  browser_ = nullptr;
+  render_handler_->SetBrowser(nullptr);
+  Destroy();
+}
+
+bool OsrWindowWin::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                     CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  return false;
+}
+
+void OsrWindowWin::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK_GT(device_scale_factor_, 0);
+
+  rect.x = rect.y = 0;
+  rect.width = DeviceToLogical(client_rect_.right - client_rect_.left,
+                               device_scale_factor_);
+  if (rect.width == 0)
+    rect.width = 1;
+  rect.height = DeviceToLogical(client_rect_.bottom - client_rect_.top,
+                                device_scale_factor_);
+  if (rect.height == 0)
+    rect.height = 1;
+}
+
+bool OsrWindowWin::GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                  int viewX,
+                                  int viewY,
+                                  int& screenX,
+                                  int& screenY) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK_GT(device_scale_factor_, 0);
+
+  if (!::IsWindow(hwnd_))
+    return false;
+
+  // Convert the point from view coordinates to actual screen coordinates.
+  POINT screen_pt = {LogicalToDevice(viewX, device_scale_factor_),
+                     LogicalToDevice(viewY, device_scale_factor_)};
+  ClientToScreen(hwnd_, &screen_pt);
+  screenX = screen_pt.x;
+  screenY = screen_pt.y;
+  return true;
+}
+
+bool OsrWindowWin::GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                                 CefScreenInfo& screen_info) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK_GT(device_scale_factor_, 0);
+
+  if (!::IsWindow(hwnd_))
+    return false;
+
+  CefRect view_rect;
+  GetViewRect(browser, view_rect);
+
+  screen_info.device_scale_factor = device_scale_factor_;
+
+  // The screen info rectangles are used by the renderer to create and position
+  // popups. Keep popups inside the view rectangle.
+  screen_info.rect = view_rect;
+  screen_info.available_rect = view_rect;
+  return true;
+}
+
+void OsrWindowWin::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {
+  render_handler_->OnPopupShow(browser, show);
+}
+
+void OsrWindowWin::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                               const CefRect& rect) {
+  render_handler_->OnPopupSize(browser,
+                               LogicalToDevice(rect, device_scale_factor_));
+}
+
+void OsrWindowWin::OnPaint(CefRefPtr<CefBrowser> browser,
+                           CefRenderHandler::PaintElementType type,
+                           const CefRenderHandler::RectList& dirtyRects,
+                           const void* buffer,
+                           int width,
+                           int height) {
+  EnsureRenderHandler();
+  render_handler_->OnPaint(browser, type, dirtyRects, buffer, width, height);
+}
+
+void OsrWindowWin::OnAcceleratedPaint(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::PaintElementType type,
+    const CefRenderHandler::RectList& dirtyRects,
+    void* share_handle) {
+  EnsureRenderHandler();
+  render_handler_->OnAcceleratedPaint(browser, type, dirtyRects, share_handle);
+}
+
+void OsrWindowWin::OnCursorChange(CefRefPtr<CefBrowser> browser,
+                                  CefCursorHandle cursor,
+                                  CefRenderHandler::CursorType type,
+                                  const CefCursorInfo& custom_cursor_info) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!::IsWindow(hwnd_))
+    return;
+
+  // Change the plugin window's cursor.
+  SetClassLongPtr(hwnd_, GCLP_HCURSOR,
+                  static_cast<LONG>(reinterpret_cast<LONG_PTR>(cursor)));
+  SetCursor(cursor);
+}
+
+bool OsrWindowWin::StartDragging(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDragData> drag_data,
+    CefRenderHandler::DragOperationsMask allowed_ops,
+    int x,
+    int y) {
+  CEF_REQUIRE_UI_THREAD();
+
+#if defined(CEF_USE_ATL)
+  if (!drop_target_)
+    return false;
+
+  current_drag_op_ = DRAG_OPERATION_NONE;
+  CefBrowserHost::DragOperationsMask result =
+      drop_target_->StartDragging(browser, drag_data, allowed_ops, x, y);
+  current_drag_op_ = DRAG_OPERATION_NONE;
+  POINT pt = {};
+  GetCursorPos(&pt);
+  ScreenToClient(hwnd_, &pt);
+
+  browser->GetHost()->DragSourceEndedAt(
+      DeviceToLogical(pt.x, device_scale_factor_),
+      DeviceToLogical(pt.y, device_scale_factor_), result);
+  browser->GetHost()->DragSourceSystemDragEnded();
+  return true;
+#else
+  // Cancel the drag. The dragging implementation requires ATL support.
+  return false;
+#endif
+}
+
+void OsrWindowWin::UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                                    CefRenderHandler::DragOperation operation) {
+  CEF_REQUIRE_UI_THREAD();
+
+#if defined(CEF_USE_ATL)
+  current_drag_op_ = operation;
+#endif
+}
+
+void OsrWindowWin::OnImeCompositionRangeChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefRange& selection_range,
+    const CefRenderHandler::RectList& character_bounds) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (ime_handler_) {
+    // Convert from view coordinates to device coordinates.
+    CefRenderHandler::RectList device_bounds;
+    CefRenderHandler::RectList::const_iterator it = character_bounds.begin();
+    for (; it != character_bounds.end(); ++it) {
+      device_bounds.push_back(LogicalToDevice(*it, device_scale_factor_));
+    }
+
+    ime_handler_->ChangeCompositionRange(selection_range, device_bounds);
+  }
+}
+
+void OsrWindowWin::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+
+#if defined(CEF_USE_ATL)
+  if (!accessibility_handler_) {
+    accessibility_handler_.reset(new OsrAccessibilityHelper(value, browser_));
+  } else {
+    accessibility_handler_->UpdateAccessibilityTree(value);
+  }
+
+  // Update |accessibility_root_| because UpdateAccessibilityTree may have
+  // cleared it.
+  OsrAXNode* root = accessibility_handler_->GetRootNode();
+  accessibility_root_ =
+      root ? root->GetNativeAccessibleObject(nullptr) : nullptr;
+#endif  // defined(CEF_USE_ATL)
+}
+
+void OsrWindowWin::UpdateAccessibilityLocation(CefRefPtr<CefValue> value) {
+  CEF_REQUIRE_UI_THREAD();
+
+#if defined(CEF_USE_ATL)
+  if (accessibility_handler_) {
+    accessibility_handler_->UpdateAccessibilityLocation(value);
+  }
+#endif  // defined(CEF_USE_ATL)
+}
+
+#if defined(CEF_USE_ATL)
+
+CefBrowserHost::DragOperationsMask OsrWindowWin::OnDragEnter(
+    CefRefPtr<CefDragData> drag_data,
+    CefMouseEvent ev,
+    CefBrowserHost::DragOperationsMask effect) {
+  if (browser_) {
+    DeviceToLogical(ev, device_scale_factor_);
+    browser_->GetHost()->DragTargetDragEnter(drag_data, ev, effect);
+    browser_->GetHost()->DragTargetDragOver(ev, effect);
+  }
+  return current_drag_op_;
+}
+
+CefBrowserHost::DragOperationsMask OsrWindowWin::OnDragOver(
+    CefMouseEvent ev,
+    CefBrowserHost::DragOperationsMask effect) {
+  if (browser_) {
+    DeviceToLogical(ev, device_scale_factor_);
+    browser_->GetHost()->DragTargetDragOver(ev, effect);
+  }
+  return current_drag_op_;
+}
+
+void OsrWindowWin::OnDragLeave() {
+  if (browser_)
+    browser_->GetHost()->DragTargetDragLeave();
+}
+
+CefBrowserHost::DragOperationsMask OsrWindowWin::OnDrop(
+    CefMouseEvent ev,
+    CefBrowserHost::DragOperationsMask effect) {
+  if (browser_) {
+    DeviceToLogical(ev, device_scale_factor_);
+    browser_->GetHost()->DragTargetDragOver(ev, effect);
+    browser_->GetHost()->DragTargetDrop(ev);
+  }
+  return current_drag_op_;
+}
+
+#endif  // defined(CEF_USE_ATL)
+
+void OsrWindowWin::EnsureRenderHandler() {
+  CEF_REQUIRE_UI_THREAD();
+  if (!render_handler_) {
+    if (settings_.shared_texture_enabled) {
+      // Try to initialize D3D11 rendering.
+      auto render_handler = new OsrRenderHandlerWinD3D11(settings_, hwnd_);
+      if (render_handler->Initialize(browser_,
+                                     client_rect_.right - client_rect_.left,
+                                     client_rect_.bottom - client_rect_.top)) {
+        render_handler_.reset(render_handler);
+      } else {
+        LOG(ERROR) << "Failed to initialize D3D11 rendering.";
+        delete render_handler;
+      }
+    }
+
+    // Fall back to GL rendering.
+    if (!render_handler_) {
+      auto render_handler = new OsrRenderHandlerWinGL(settings_, hwnd_);
+      render_handler->Initialize(browser_);
+      render_handler_.reset(render_handler);
+    }
+  }
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/osr_window_win.h b/src/tests/cefclient/browser/osr_window_win.h
new file mode 100644
index 0000000..0955ebc
--- /dev/null
+++ b/src/tests/cefclient/browser/osr_window_win.h
@@ -0,0 +1,215 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_WINDOW_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_WINDOW_WIN_H_
+#pragma once
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/client_handler_osr.h"
+#include "tests/cefclient/browser/osr_accessibility_node.h"
+#include "tests/cefclient/browser/osr_dragdrop_win.h"
+#include "tests/cefclient/browser/osr_render_handler_win.h"
+#include "tests/cefclient/browser/osr_renderer_settings.h"
+
+namespace client {
+
+class OsrAccessibilityHelper;
+class OsrImeHandlerWin;
+
+// Represents the native parent window for an off-screen browser. This object
+// must live on the CEF UI thread in order to handle CefRenderHandler callbacks.
+// The methods of this class are thread-safe unless otherwise indicated.
+class OsrWindowWin
+    : public base::RefCountedThreadSafe<OsrWindowWin, CefDeleteOnUIThread>,
+      public ClientHandlerOsr::OsrDelegate
+#if defined(CEF_USE_ATL)
+    ,
+      public OsrDragEvents
+#endif
+{
+ public:
+  // This interface is implemented by the owner of the OsrWindowWin. The
+  // methods of this class will be called on the main thread.
+  class Delegate {
+   public:
+    // Called after the native window has been created.
+    virtual void OnOsrNativeWindowCreated(HWND hwnd) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // |delegate| must outlive this object.
+  OsrWindowWin(Delegate* delegate, const OsrRendererSettings& settings);
+
+  // Create a new browser and native window.
+  void CreateBrowser(HWND parent_hwnd,
+                     const RECT& rect,
+                     CefRefPtr<CefClient> handler,
+                     const CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue> extra_info,
+                     CefRefPtr<CefRequestContext> request_context,
+                     const std::string& startup_url);
+
+  // Show the popup window with correct parent and bounds in parent coordinates.
+  void ShowPopup(HWND parent_hwnd, int x, int y, size_t width, size_t height);
+
+  void Show();
+  void Hide();
+  void SetBounds(int x, int y, size_t width, size_t height);
+  void SetFocus();
+  void SetDeviceScaleFactor(float device_scale_factor);
+
+  const OsrRendererSettings& settings() const { return settings_; }
+
+ private:
+  // Only allow deletion via scoped_refptr.
+  friend struct CefDeleteOnThread<TID_UI>;
+  friend class base::RefCountedThreadSafe<OsrWindowWin, CefDeleteOnUIThread>;
+
+  ~OsrWindowWin();
+
+  // Manage native window lifespan.
+  void Create(HWND parent_hwnd, const RECT& rect);
+  void Destroy();
+
+  void NotifyNativeWindowCreated(HWND hwnd);
+
+  static void RegisterOsrClass(HINSTANCE hInstance, HBRUSH background_brush);
+  static LRESULT CALLBACK OsrWndProc(HWND hWnd,
+                                     UINT message,
+                                     WPARAM wParam,
+                                     LPARAM lParam);
+
+  // WndProc message handlers.
+  void OnMouseEvent(UINT message, WPARAM wParam, LPARAM lParam);
+  void OnSize();
+  void OnFocus(bool setFocus);
+  void OnCaptureLost();
+  void OnKeyEvent(UINT message, WPARAM wParam, LPARAM lParam);
+  void OnPaint();
+  bool OnEraseBkgnd();
+  bool OnTouchEvent(UINT message, WPARAM wParam, LPARAM lParam);
+
+  void OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam);
+  void OnIMEStartComposition();
+  void OnIMEComposition(UINT message, WPARAM wParam, LPARAM lParam);
+  void OnIMECancelCompositionEvent();
+
+  // Manage popup bounds.
+  bool IsOverPopupWidget(int x, int y) const;
+  int GetPopupXOffset() const;
+  int GetPopupYOffset() const;
+  void ApplyPopupOffset(int& x, int& y) const;
+
+  // ClientHandlerOsr::OsrDelegate methods.
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
+  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                      int viewX,
+                      int viewY,
+                      int& screenX,
+                      int& screenY) OVERRIDE;
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) OVERRIDE;
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) OVERRIDE;
+  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect) OVERRIDE;
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) OVERRIDE;
+  void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
+                          CefRenderHandler::PaintElementType type,
+                          const CefRenderHandler::RectList& dirtyRects,
+                          void* share_handle) OVERRIDE;
+  void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                      CefCursorHandle cursor,
+                      CefRenderHandler::CursorType type,
+                      const CefCursorInfo& custom_cursor_info) OVERRIDE;
+  bool StartDragging(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefDragData> drag_data,
+                     CefRenderHandler::DragOperationsMask allowed_ops,
+                     int x,
+                     int y) OVERRIDE;
+  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                        CefRenderHandler::DragOperation operation) OVERRIDE;
+  void OnImeCompositionRangeChanged(
+      CefRefPtr<CefBrowser> browser,
+      const CefRange& selection_range,
+      const CefRenderHandler::RectList& character_bounds) OVERRIDE;
+
+  void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
+
+  void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) OVERRIDE;
+
+#if defined(CEF_USE_ATL)
+  // OsrDragEvents methods.
+  CefBrowserHost::DragOperationsMask OnDragEnter(
+      CefRefPtr<CefDragData> drag_data,
+      CefMouseEvent ev,
+      CefBrowserHost::DragOperationsMask effect) OVERRIDE;
+  CefBrowserHost::DragOperationsMask OnDragOver(
+      CefMouseEvent ev,
+      CefBrowserHost::DragOperationsMask effect) OVERRIDE;
+  void OnDragLeave() OVERRIDE;
+  CefBrowserHost::DragOperationsMask OnDrop(
+      CefMouseEvent ev,
+      CefBrowserHost::DragOperationsMask effect) OVERRIDE;
+#endif  // defined(CEF_USE_ATL)
+
+  void EnsureRenderHandler();
+
+  // Only accessed on the main thread.
+  Delegate* delegate_;
+
+  const OsrRendererSettings settings_;
+  HWND hwnd_;
+  scoped_ptr<OsrRenderHandlerWin> render_handler_;
+
+  // Class that encapsulates IMM32 APIs and controls IMEs attached to a window.
+  scoped_ptr<OsrImeHandlerWin> ime_handler_;
+
+  RECT client_rect_;
+  float device_scale_factor_;
+
+  CefRefPtr<CefBrowser> browser_;
+
+#if defined(CEF_USE_ATL)
+  CComPtr<DropTargetWin> drop_target_;
+  CefRenderHandler::DragOperation current_drag_op_;
+
+  // Class that abstracts the accessibility information received from the
+  // renderer.
+  scoped_ptr<OsrAccessibilityHelper> accessibility_handler_;
+  IAccessible* accessibility_root_;
+#endif
+
+  bool hidden_;
+
+  // Mouse state tracking.
+  POINT last_mouse_pos_;
+  POINT current_mouse_pos_;
+  bool mouse_rotation_;
+  bool mouse_tracking_;
+  int last_click_x_;
+  int last_click_y_;
+  CefBrowserHost::MouseButtonType last_click_button_;
+  int last_click_count_;
+  double last_click_time_;
+  bool last_mouse_down_on_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(OsrWindowWin);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_WINDOW_WIN_H_
diff --git a/src/tests/cefclient/browser/preferences_test.cc b/src/tests/cefclient/browser/preferences_test.cc
new file mode 100644
index 0000000..5659a81
--- /dev/null
+++ b/src/tests/cefclient/browser/preferences_test.cc
@@ -0,0 +1,334 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/preferences_test.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_command_line.h"
+#include "include/cef_parser.h"
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace preferences_test {
+
+namespace {
+
+const char kTestUrlPath[] = "/preferences";
+
+// Application-specific error codes.
+const int kMessageFormatError = 1;
+const int kPreferenceApplicationError = 1;
+
+// Common to all messages.
+const char kNameKey[] = "name";
+const char kNameValueGet[] = "preferences_get";
+const char kNameValueSet[] = "preferences_set";
+const char kNameValueState[] = "preferences_state";
+
+// Used with "preferences_get" messages.
+const char kIncludeDefaultsKey[] = "include_defaults";
+
+// Used with "preferences_set" messages.
+const char kPreferencesKey[] = "preferences";
+
+// Handle messages in the browser process. Only accessed on the UI thread.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  typedef std::vector<std::string> NameVector;
+
+  Handler() { CEF_REQUIRE_UI_THREAD(); }
+
+  // Called due to cefQuery execution in preferences.html.
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (!test_runner::IsTestURL(url, kTestUrlPath))
+      return false;
+
+    // Parse |request| as a JSON dictionary.
+    CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
+    if (!request_dict) {
+      callback->Failure(kMessageFormatError, "Incorrect message format");
+      return true;
+    }
+
+    // Verify the "name" key.
+    if (!VerifyKey(request_dict, kNameKey, VTYPE_STRING, callback))
+      return true;
+
+    const std::string& message_name = request_dict->GetString(kNameKey);
+    if (message_name == kNameValueGet) {
+      // JavaScript is requesting a JSON representation of the preferences tree.
+
+      // Verify the "include_defaults" key.
+      if (!VerifyKey(request_dict, kIncludeDefaultsKey, VTYPE_BOOL, callback))
+        return true;
+
+      const bool include_defaults = request_dict->GetBool(kIncludeDefaultsKey);
+
+      OnPreferencesGet(browser, include_defaults, callback);
+
+      return true;
+    } else if (message_name == kNameValueSet) {
+      // JavaScript is requesting that preferences be updated to match the
+      // specified JSON representation.
+
+      // Verify the "preferences" key.
+      if (!VerifyKey(request_dict, kPreferencesKey, VTYPE_DICTIONARY, callback))
+        return true;
+
+      CefRefPtr<CefDictionaryValue> preferences =
+          request_dict->GetDictionary(kPreferencesKey);
+
+      OnPreferencesSet(browser, preferences, callback);
+
+      return true;
+    } else if (message_name == kNameValueState) {
+      // JavaScript is requesting global state information.
+
+      OnPreferencesState(browser, callback);
+
+      return true;
+    }
+
+    return false;
+  }
+
+ private:
+  // Execute |callback| with the preferences dictionary as a JSON string.
+  static void OnPreferencesGet(CefRefPtr<CefBrowser> browser,
+                               bool include_defaults,
+                               CefRefPtr<Callback> callback) {
+    CefRefPtr<CefRequestContext> context =
+        browser->GetHost()->GetRequestContext();
+
+    // Retrieve all preference values.
+    CefRefPtr<CefDictionaryValue> prefs =
+        context->GetAllPreferences(include_defaults);
+
+    // Serialize the preferences to JSON and return to the JavaScript caller.
+    callback->Success(GetJSON(prefs));
+  }
+
+  // Set preferences based on the contents of |preferences|. Execute |callback|
+  // with a descriptive result message.
+  static void OnPreferencesSet(CefRefPtr<CefBrowser> browser,
+                               CefRefPtr<CefDictionaryValue> preferences,
+                               CefRefPtr<Callback> callback) {
+    CefRefPtr<CefRequestContext> context =
+        browser->GetHost()->GetRequestContext();
+
+    CefRefPtr<CefValue> value = CefValue::Create();
+    value->SetDictionary(preferences);
+
+    std::string error;
+    NameVector changed_names;
+
+    // Apply preferences. This may result in errors.
+    const bool success =
+        ApplyPrefs(context, std::string(), value, error, changed_names);
+
+    // Create a message that accurately represents the result.
+    std::string message;
+    if (!changed_names.empty()) {
+      std::stringstream ss;
+      ss << "Successfully changed " << changed_names.size() << " preferences; ";
+      for (size_t i = 0; i < changed_names.size(); ++i) {
+        ss << changed_names[i];
+        if (i < changed_names.size() - 1)
+          ss << ", ";
+      }
+      message = ss.str();
+    }
+
+    if (!success) {
+      DCHECK(!error.empty());
+      if (!message.empty())
+        message += "\n";
+      message += error;
+    }
+
+    if (changed_names.empty()) {
+      if (!message.empty())
+        message += "\n";
+      message += "No preferences changed.";
+    }
+
+    // Return the message to the JavaScript caller.
+    if (success)
+      callback->Success(message);
+    else
+      callback->Failure(kPreferenceApplicationError, message);
+  }
+
+  // Execute |callback| with the global state dictionary as a JSON string.
+  static void OnPreferencesState(CefRefPtr<CefBrowser> browser,
+                                 CefRefPtr<Callback> callback) {
+    CefRefPtr<CefCommandLine> command_line =
+        CefCommandLine::GetGlobalCommandLine();
+
+    CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
+
+    // If spell checking is disabled via the command-line then it cannot be
+    // enabled via preferences.
+    dict->SetBool("spellcheck_disabled",
+                  command_line->HasSwitch("disable-spell-checking"));
+
+    // If proxy settings are configured via the command-line then they cannot
+    // be modified via preferences.
+    dict->SetBool("proxy_configured",
+                  command_line->HasSwitch("no-proxy-server") ||
+                      command_line->HasSwitch("proxy-auto-detect") ||
+                      command_line->HasSwitch("proxy-pac-url") ||
+                      command_line->HasSwitch("proxy-server"));
+
+    // If allow running insecure content is enabled via the command-line then it
+    // cannot be enabled via preferences.
+    dict->SetBool("allow_running_insecure_content",
+                  command_line->HasSwitch("allow-running-insecure-content"));
+
+    // Serialize the state to JSON and return to the JavaScript caller.
+    callback->Success(GetJSON(dict));
+  }
+
+  // Convert a JSON string to a dictionary value.
+  static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
+    CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
+    if (value.get() && value->GetType() == VTYPE_DICTIONARY)
+      return value->GetDictionary();
+    return nullptr;
+  }
+
+  // Convert a dictionary value to a JSON string.
+  static CefString GetJSON(CefRefPtr<CefDictionaryValue> dictionary) {
+    CefRefPtr<CefValue> value = CefValue::Create();
+    value->SetDictionary(dictionary);
+    return CefWriteJSON(value, JSON_WRITER_DEFAULT);
+  }
+
+  // Verify that |key| exists in |dictionary| and has type |value_type|. Fails
+  // |callback| and returns false on failure.
+  static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
+                        const char* key,
+                        cef_value_type_t value_type,
+                        CefRefPtr<Callback> callback) {
+    if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
+      callback->Failure(
+          kMessageFormatError,
+          "Missing or incorrectly formatted message key: " + std::string(key));
+      return false;
+    }
+    return true;
+  }
+
+  // Apply preferences. Returns true on success. Returns false and sets |error|
+  // to a descriptive error string on failure. |changed_names| is the list of
+  // preferences that were successfully changed.
+  static bool ApplyPrefs(CefRefPtr<CefRequestContext> context,
+                         const std::string& name,
+                         CefRefPtr<CefValue> value,
+                         std::string& error,
+                         NameVector& changed_names) {
+    if (!name.empty() && context->HasPreference(name)) {
+      // The preference exists. Set the value.
+      return SetPref(context, name, value, error, changed_names);
+    }
+
+    if (value->GetType() == VTYPE_DICTIONARY) {
+      // A dictionary type value that is not an existing preference. Try to set
+      // each of the elements individually.
+      CefRefPtr<CefDictionaryValue> dict = value->GetDictionary();
+
+      CefDictionaryValue::KeyList keys;
+      dict->GetKeys(keys);
+      for (size_t i = 0; i < keys.size(); ++i) {
+        const std::string& key = keys[i];
+        const std::string& current_name = name.empty() ? key : name + "." + key;
+        if (!ApplyPrefs(context, current_name, dict->GetValue(key), error,
+                        changed_names)) {
+          return false;
+        }
+      }
+
+      return true;
+    }
+
+    error = "Trying to create an unregistered preference: " + name;
+    return false;
+  }
+
+  // Set a specific preference value. Returns true if the value is set
+  // successfully or has not changed. If the value has changed then |name| will
+  // be added to |changed_names|. Returns false and sets |error| to a
+  // descriptive error string on failure.
+  static bool SetPref(CefRefPtr<CefRequestContext> context,
+                      const std::string& name,
+                      CefRefPtr<CefValue> value,
+                      std::string& error,
+                      NameVector& changed_names) {
+    CefRefPtr<CefValue> existing_value = context->GetPreference(name);
+    DCHECK(existing_value);
+
+    if (value->GetType() == VTYPE_STRING &&
+        existing_value->GetType() != VTYPE_STRING) {
+      // Since |value| is coming from JSON all basic types will be represented
+      // as strings. Convert to the expected data type.
+      const std::string& string_val = value->GetString();
+      switch (existing_value->GetType()) {
+        case VTYPE_BOOL:
+          if (string_val == "true" || string_val == "1")
+            value->SetBool(true);
+          else if (string_val == "false" || string_val == "0")
+            value->SetBool(false);
+          break;
+        case VTYPE_INT:
+          value->SetInt(atoi(string_val.c_str()));
+          break;
+        case VTYPE_DOUBLE:
+          value->SetInt(atof(string_val.c_str()));
+          break;
+        default:
+          // Other types cannot be converted.
+          break;
+      }
+    }
+
+    // Nothing to do if the value hasn't changed.
+    if (existing_value->IsEqual(value))
+      return true;
+
+    // Attempt to set the preference.
+    CefString error_str;
+    if (!context->SetPreference(name, value, error_str)) {
+      error = error_str.ToString() + ": " + name;
+      return false;
+    }
+
+    // The preference was set successfully.
+    changed_names.push_back(name);
+    return true;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(Handler);
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace preferences_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/preferences_test.h b/src/tests/cefclient/browser/preferences_test.h
new file mode 100644
index 0000000..b11d89d
--- /dev/null
+++ b/src/tests/cefclient/browser/preferences_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_PREFERENCES_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_PREFERENCES_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace preferences_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace preferences_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_PREFERENCES_TEST_H_
diff --git a/src/tests/cefclient/browser/print_handler_gtk.cc b/src/tests/cefclient/browser/print_handler_gtk.cc
new file mode 100644
index 0000000..922c85b
--- /dev/null
+++ b/src/tests/cefclient/browser/print_handler_gtk.cc
@@ -0,0 +1,663 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tests/cefclient/browser/print_handler_gtk.h"
+
+#include <vector>
+
+#include <gtk/gtk.h>
+#include <gtk/gtkunixprint.h>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/wrapper/cef_helpers.h"
+
+#include "tests/cefclient/browser/root_window.h"
+#include "tests/cefclient/browser/util_gtk.h"
+
+namespace client {
+
+namespace {
+
+// CUPS Duplex attribute and values.
+const char kCUPSDuplex[] = "cups-Duplex";
+const char kDuplexNone[] = "None";
+const char kDuplexTumble[] = "DuplexTumble";
+const char kDuplexNoTumble[] = "DuplexNoTumble";
+
+// CUPS color mode attribute and values.
+const char kCUPSColorMode[] = "cups-ColorMode";
+const char kCUPSColorModel[] = "cups-ColorModel";
+const char kCUPSPrintoutMode[] = "cups-PrintoutMode";
+const char kCUPSProcessColorModel[] = "cups-ProcessColorModel";
+const char kBlack[] = "Black";
+const char kCMYK[] = "CMYK";
+const char kCMY_K[] = "CMY+K";
+const char kCMY[] = "CMY";
+const char kColor[] = "Color";
+const char kGray[] = "Gray";
+const char kGrayscale[] = "Grayscale";
+const char kGreyscale[] = "Greyscale";
+const char kMonochrome[] = "Monochrome";
+const char kNormal[] = "Normal";
+const char kNormalGray[] = "Normal.Gray";
+const char kRGB[] = "RGB";
+const char kRGBA[] = "RGBA";
+const char kRGB16[] = "RGB16";
+
+// Default margin settings.
+const double kTopMarginInInch = 0.25;
+const double kBottomMarginInInch = 0.56;
+const double kLeftMarginInInch = 0.25;
+const double kRightMarginInInch = 0.25;
+
+// Length of an inch in CSS's 1px unit.
+// http://dev.w3.org/csswg/css3-values/#the-px-unit
+const int kPixelsPerInch = 96;
+
+// LETTER: 8.5 x 11 inches
+const float kLetterWidthInch = 8.5f;
+const float kLetterHeightInch = 11.0f;
+
+class StickyPrintSettingGtk {
+ public:
+  StickyPrintSettingGtk() : last_used_settings_(gtk_print_settings_new()) {}
+  ~StickyPrintSettingGtk() {
+    NOTREACHED();  // The instance is intentionally leaked.
+  }
+
+  GtkPrintSettings* settings() { return last_used_settings_; }
+
+  void SetLastUsedSettings(GtkPrintSettings* settings) {
+    DCHECK(last_used_settings_);
+    g_object_unref(last_used_settings_);
+    last_used_settings_ = gtk_print_settings_copy(settings);
+  }
+
+ private:
+  GtkPrintSettings* last_used_settings_;
+
+  DISALLOW_COPY_AND_ASSIGN(StickyPrintSettingGtk);
+};
+
+// Lazily initialize the singleton instance.
+StickyPrintSettingGtk* GetLastUsedSettings() {
+  static StickyPrintSettingGtk* settings = nullptr;
+  if (!settings)
+    settings = new StickyPrintSettingGtk();
+  return settings;
+}
+
+// Helper class to track GTK printers.
+class GtkPrinterList {
+ public:
+  GtkPrinterList() : default_printer_(nullptr) {
+    gtk_enumerate_printers(SetPrinter, this, NULL, TRUE);
+  }
+
+  ~GtkPrinterList() {
+    for (std::vector<GtkPrinter*>::iterator it = printers_.begin();
+         it < printers_.end(); ++it) {
+      g_object_unref(*it);
+    }
+  }
+
+  // Can return NULL if there's no default printer. E.g. Printer on a laptop
+  // is "home_printer", but the laptop is at work.
+  GtkPrinter* default_printer() { return default_printer_; }
+
+  // Can return NULL if the printer cannot be found due to:
+  // - Printer list out of sync with printer dialog UI.
+  // - Querying for non-existant printers like 'Print to PDF'.
+  GtkPrinter* GetPrinterWithName(const std::string& name) {
+    if (name.empty())
+      return nullptr;
+
+    for (std::vector<GtkPrinter*>::iterator it = printers_.begin();
+         it < printers_.end(); ++it) {
+      if (gtk_printer_get_name(*it) == name) {
+        return *it;
+      }
+    }
+
+    return nullptr;
+  }
+
+ private:
+  // Callback function used by gtk_enumerate_printers() to get all printer.
+  static gboolean SetPrinter(GtkPrinter* printer, gpointer data) {
+    GtkPrinterList* printer_list = reinterpret_cast<GtkPrinterList*>(data);
+    if (gtk_printer_is_default(printer))
+      printer_list->default_printer_ = printer;
+
+    g_object_ref(printer);
+    printer_list->printers_.push_back(printer);
+
+    return FALSE;
+  }
+
+  std::vector<GtkPrinter*> printers_;
+  GtkPrinter* default_printer_;
+};
+
+void GetColorModelForMode(CefPrintSettings::ColorModel color_mode,
+                          std::string* color_setting_name,
+                          std::string* color_value) {
+  color_setting_name->assign(kCUPSColorModel);
+  switch (color_mode) {
+    case COLOR_MODEL_COLOR:
+      color_value->assign(kColor);
+      break;
+    case COLOR_MODEL_CMYK:
+      color_value->assign(kCMYK);
+      break;
+    case COLOR_MODEL_PRINTOUTMODE_NORMAL:
+      color_value->assign(kNormal);
+      color_setting_name->assign(kCUPSPrintoutMode);
+      break;
+    case COLOR_MODEL_PRINTOUTMODE_NORMAL_GRAY:
+      color_value->assign(kNormalGray);
+      color_setting_name->assign(kCUPSPrintoutMode);
+      break;
+    case COLOR_MODEL_RGB16:
+      color_value->assign(kRGB16);
+      break;
+    case COLOR_MODEL_RGBA:
+      color_value->assign(kRGBA);
+      break;
+    case COLOR_MODEL_RGB:
+      color_value->assign(kRGB);
+      break;
+    case COLOR_MODEL_CMY:
+      color_value->assign(kCMY);
+      break;
+    case COLOR_MODEL_CMY_K:
+      color_value->assign(kCMY_K);
+      break;
+    case COLOR_MODEL_BLACK:
+      color_value->assign(kBlack);
+      break;
+    case COLOR_MODEL_GRAY:
+      color_value->assign(kGray);
+      break;
+    case COLOR_MODEL_COLORMODE_COLOR:
+      color_setting_name->assign(kCUPSColorMode);
+      color_value->assign(kColor);
+      break;
+    case COLOR_MODEL_COLORMODE_MONOCHROME:
+      color_setting_name->assign(kCUPSColorMode);
+      color_value->assign(kMonochrome);
+      break;
+    case COLOR_MODEL_HP_COLOR_COLOR:
+      color_setting_name->assign(kColor);
+      color_value->assign(kColor);
+      break;
+    case COLOR_MODEL_HP_COLOR_BLACK:
+      color_setting_name->assign(kColor);
+      color_value->assign(kBlack);
+      break;
+    case COLOR_MODEL_PROCESSCOLORMODEL_CMYK:
+      color_setting_name->assign(kCUPSProcessColorModel);
+      color_value->assign(kCMYK);
+      break;
+    case COLOR_MODEL_PROCESSCOLORMODEL_GREYSCALE:
+      color_setting_name->assign(kCUPSProcessColorModel);
+      color_value->assign(kGreyscale);
+      break;
+    case COLOR_MODEL_PROCESSCOLORMODEL_RGB:
+      color_setting_name->assign(kCUPSProcessColorModel);
+      color_value->assign(kRGB);
+      break;
+    default:
+      color_value->assign(kGrayscale);
+      break;
+  }
+}
+
+void InitPrintSettings(GtkPrintSettings* settings,
+                       GtkPageSetup* page_setup,
+                       CefRefPtr<CefPrintSettings> print_settings) {
+  DCHECK(settings);
+  DCHECK(page_setup);
+
+  std::string device_name;
+  const gchar* name = gtk_print_settings_get_printer(settings);
+  if (name)
+    device_name = name;
+  print_settings->SetDeviceName(device_name);
+
+  CefSize physical_size_device_units;
+  CefRect printable_area_device_units;
+  int dpi = gtk_print_settings_get_resolution(settings);
+  if (dpi) {
+    // Initialize page_setup_device_units_.
+    physical_size_device_units.Set(
+        gtk_page_setup_get_paper_width(page_setup, GTK_UNIT_INCH) * dpi,
+        gtk_page_setup_get_paper_height(page_setup, GTK_UNIT_INCH) * dpi);
+    printable_area_device_units.Set(
+        gtk_page_setup_get_left_margin(page_setup, GTK_UNIT_INCH) * dpi,
+        gtk_page_setup_get_top_margin(page_setup, GTK_UNIT_INCH) * dpi,
+        gtk_page_setup_get_page_width(page_setup, GTK_UNIT_INCH) * dpi,
+        gtk_page_setup_get_page_height(page_setup, GTK_UNIT_INCH) * dpi);
+  } else {
+    // Use default values if we cannot get valid values from the print dialog.
+    dpi = kPixelsPerInch;
+    double page_width_in_pixel = kLetterWidthInch * dpi;
+    double page_height_in_pixel = kLetterHeightInch * dpi;
+    physical_size_device_units.Set(static_cast<int>(page_width_in_pixel),
+                                   static_cast<int>(page_height_in_pixel));
+    printable_area_device_units.Set(
+        static_cast<int>(kLeftMarginInInch * dpi),
+        static_cast<int>(kTopMarginInInch * dpi),
+        page_width_in_pixel - (kLeftMarginInInch + kRightMarginInInch) * dpi,
+        page_height_in_pixel - (kTopMarginInInch + kBottomMarginInInch) * dpi);
+  }
+
+  print_settings->SetDPI(dpi);
+
+  // Note: With the normal GTK print dialog, when the user selects the landscape
+  // orientation, all that does is change the paper size. Which seems to be
+  // enough to render the right output and send it to the printer.
+  // The orientation value stays as portrait and does not actually affect
+  // printing.
+  // Thus this is only useful in print preview mode, where we manually set the
+  // orientation and change the paper size ourselves.
+  GtkPageOrientation orientation = gtk_print_settings_get_orientation(settings);
+  // Set before SetPrinterPrintableArea to make it flip area if necessary.
+  print_settings->SetOrientation(orientation == GTK_PAGE_ORIENTATION_LANDSCAPE);
+  print_settings->SetPrinterPrintableArea(physical_size_device_units,
+                                          printable_area_device_units, true);
+}
+
+// Returns the GtkWindow* for the browser. Will return NULL when using the Views
+// framework.
+GtkWindow* GetWindow(CefRefPtr<CefBrowser> browser) {
+  scoped_refptr<RootWindow> root_window =
+      RootWindow::GetForBrowser(browser->GetIdentifier());
+  if (root_window)
+    return GTK_WINDOW(root_window->GetWindowHandle());
+  return nullptr;
+}
+
+void RunCallback(base::Callback<void(GtkWindow*)> callback, GtkWindow* window) {
+  callback.Run(window);
+}
+
+void GetWindowAndContinue(CefRefPtr<CefBrowser> browser,
+                          base::Callback<void(GtkWindow*)> callback) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(GetWindowAndContinue, browser, callback));
+    return;
+  }
+
+  GtkWindow* window = GetWindow(browser);
+  if (window) {
+    CefPostTask(TID_UI, base::Bind(RunCallback, callback, window));
+  }
+}
+
+}  // namespace
+
+struct ClientPrintHandlerGtk::PrintHandler {
+  PrintHandler(CefRefPtr<CefBrowser> browser)
+      : browser_(browser),
+        dialog_(nullptr),
+        gtk_settings_(nullptr),
+        page_setup_(nullptr),
+        printer_(nullptr) {}
+
+  ~PrintHandler() {
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    if (dialog_) {
+      gtk_widget_destroy(dialog_);
+      dialog_ = nullptr;
+    }
+    if (gtk_settings_) {
+      g_object_unref(gtk_settings_);
+      gtk_settings_ = nullptr;
+    }
+    if (page_setup_) {
+      g_object_unref(page_setup_);
+      page_setup_ = nullptr;
+    }
+    if (printer_) {
+      g_object_unref(printer_);
+      printer_ = nullptr;
+    }
+  }
+
+  void OnPrintSettings(CefRefPtr<CefPrintSettings> settings,
+                       bool get_defaults) {
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    if (get_defaults) {
+      DCHECK(!page_setup_);
+      DCHECK(!printer_);
+
+      // |gtk_settings_| is a new copy.
+      gtk_settings_ =
+          gtk_print_settings_copy(GetLastUsedSettings()->settings());
+      page_setup_ = gtk_page_setup_new();
+    } else {
+      if (!gtk_settings_) {
+        gtk_settings_ =
+            gtk_print_settings_copy(GetLastUsedSettings()->settings());
+      }
+
+      GtkPrinterList* printer_list = new GtkPrinterList;
+      printer_ = printer_list->GetPrinterWithName(settings->GetDeviceName());
+      if (printer_) {
+        g_object_ref(printer_);
+        gtk_print_settings_set_printer(gtk_settings_,
+                                       gtk_printer_get_name(printer_));
+        if (!page_setup_) {
+          page_setup_ = gtk_printer_get_default_page_size(printer_);
+        }
+      }
+
+      gtk_print_settings_set_n_copies(gtk_settings_, settings->GetCopies());
+      gtk_print_settings_set_collate(gtk_settings_, settings->WillCollate());
+
+      std::string color_value;
+      std::string color_setting_name;
+      GetColorModelForMode(settings->GetColorModel(), &color_setting_name,
+                           &color_value);
+      gtk_print_settings_set(gtk_settings_, color_setting_name.c_str(),
+                             color_value.c_str());
+
+      if (settings->GetDuplexMode() != DUPLEX_MODE_UNKNOWN) {
+        const char* cups_duplex_mode = nullptr;
+        switch (settings->GetDuplexMode()) {
+          case DUPLEX_MODE_LONG_EDGE:
+            cups_duplex_mode = kDuplexNoTumble;
+            break;
+          case DUPLEX_MODE_SHORT_EDGE:
+            cups_duplex_mode = kDuplexTumble;
+            break;
+          case DUPLEX_MODE_SIMPLEX:
+            cups_duplex_mode = kDuplexNone;
+            break;
+          default:  // UNKNOWN_DUPLEX_MODE
+            NOTREACHED();
+            break;
+        }
+        gtk_print_settings_set(gtk_settings_, kCUPSDuplex, cups_duplex_mode);
+      }
+
+      if (!page_setup_)
+        page_setup_ = gtk_page_setup_new();
+
+      gtk_print_settings_set_orientation(gtk_settings_,
+                                         settings->IsLandscape()
+                                             ? GTK_PAGE_ORIENTATION_LANDSCAPE
+                                             : GTK_PAGE_ORIENTATION_PORTRAIT);
+
+      delete printer_list;
+    }
+
+    InitPrintSettings(gtk_settings_, page_setup_, settings);
+  }
+
+  void OnPrintDialog(bool has_selection,
+                     CefRefPtr<CefPrintDialogCallback> callback,
+                     GtkWindow* parent) {
+    dialog_callback_ = callback;
+
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    // TODO(estade): We need a window title here.
+    dialog_ = gtk_print_unix_dialog_new(NULL, parent);
+    g_signal_connect(dialog_, "delete-event",
+                     G_CALLBACK(gtk_widget_hide_on_delete), NULL);
+
+    // Set modal so user cannot focus the same tab and press print again.
+    gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
+
+    // Since we only generate PDF, only show printers that support PDF.
+    // TODO(thestig) Add more capabilities to support?
+    GtkPrintCapabilities cap = static_cast<GtkPrintCapabilities>(
+        GTK_PRINT_CAPABILITY_GENERATE_PDF | GTK_PRINT_CAPABILITY_PAGE_SET |
+        GTK_PRINT_CAPABILITY_COPIES | GTK_PRINT_CAPABILITY_COLLATE |
+        GTK_PRINT_CAPABILITY_REVERSE);
+    gtk_print_unix_dialog_set_manual_capabilities(
+        GTK_PRINT_UNIX_DIALOG(dialog_), cap);
+    gtk_print_unix_dialog_set_embed_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_),
+                                               TRUE);
+    gtk_print_unix_dialog_set_support_selection(GTK_PRINT_UNIX_DIALOG(dialog_),
+                                                TRUE);
+    gtk_print_unix_dialog_set_has_selection(GTK_PRINT_UNIX_DIALOG(dialog_),
+                                            has_selection);
+    gtk_print_unix_dialog_set_settings(GTK_PRINT_UNIX_DIALOG(dialog_),
+                                       gtk_settings_);
+    g_signal_connect(dialog_, "response", G_CALLBACK(OnDialogResponseThunk),
+                     this);
+    gtk_widget_show(dialog_);
+  }
+
+  bool OnPrintJob(const CefString& document_name,
+                  const CefString& pdf_file_path,
+                  CefRefPtr<CefPrintJobCallback> callback) {
+    // If |printer_| is NULL then somehow the GTK printer list changed out under
+    // us. In which case, just bail out.
+    if (!printer_)
+      return false;
+
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    job_callback_ = callback;
+
+    // Save the settings for next time.
+    GetLastUsedSettings()->SetLastUsedSettings(gtk_settings_);
+
+    GtkPrintJob* print_job = gtk_print_job_new(
+        document_name.ToString().c_str(), printer_, gtk_settings_, page_setup_);
+    gtk_print_job_set_source_file(print_job, pdf_file_path.ToString().c_str(),
+                                  NULL);
+    gtk_print_job_send(print_job, OnJobCompletedThunk, this, NULL);
+
+    return true;
+  }
+
+ private:
+  void OnDialogResponse(GtkDialog* dialog, gint response_id) {
+    int num_matched_handlers = g_signal_handlers_disconnect_by_func(
+        dialog_, reinterpret_cast<gpointer>(&OnDialogResponseThunk), this);
+    DCHECK_EQ(1, num_matched_handlers);
+
+    gtk_widget_hide(dialog_);
+
+    switch (response_id) {
+      case GTK_RESPONSE_OK: {
+        if (gtk_settings_)
+          g_object_unref(gtk_settings_);
+        gtk_settings_ =
+            gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog_));
+
+        if (printer_)
+          g_object_unref(printer_);
+        printer_ = gtk_print_unix_dialog_get_selected_printer(
+            GTK_PRINT_UNIX_DIALOG(dialog_));
+        g_object_ref(printer_);
+
+        if (page_setup_)
+          g_object_unref(page_setup_);
+        page_setup_ = gtk_print_unix_dialog_get_page_setup(
+            GTK_PRINT_UNIX_DIALOG(dialog_));
+        g_object_ref(page_setup_);
+
+        // Handle page ranges.
+        CefPrintSettings::PageRangeList ranges_vector;
+        gint num_ranges;
+        bool print_selection_only = false;
+        switch (gtk_print_settings_get_print_pages(gtk_settings_)) {
+          case GTK_PRINT_PAGES_RANGES: {
+            GtkPageRange* gtk_range =
+                gtk_print_settings_get_page_ranges(gtk_settings_, &num_ranges);
+            if (gtk_range) {
+              for (int i = 0; i < num_ranges; ++i) {
+                ranges_vector.push_back(
+                    CefRange(gtk_range[i].start, gtk_range[i].end));
+              }
+              g_free(gtk_range);
+            }
+            break;
+          }
+          case GTK_PRINT_PAGES_SELECTION:
+            print_selection_only = true;
+            break;
+          case GTK_PRINT_PAGES_ALL:
+            // Leave |ranges_vector| empty to indicate print all pages.
+            break;
+          case GTK_PRINT_PAGES_CURRENT:
+          default:
+            NOTREACHED();
+            break;
+        }
+
+        CefRefPtr<CefPrintSettings> settings = CefPrintSettings::Create();
+        settings->SetPageRanges(ranges_vector);
+        settings->SetSelectionOnly(print_selection_only);
+        InitPrintSettings(gtk_settings_, page_setup_, settings);
+        dialog_callback_->Continue(settings);
+        dialog_callback_ = nullptr;
+        return;
+      }
+      case GTK_RESPONSE_DELETE_EVENT:  // Fall through.
+      case GTK_RESPONSE_CANCEL: {
+        dialog_callback_->Cancel();
+        dialog_callback_ = nullptr;
+        return;
+      }
+      case GTK_RESPONSE_APPLY:
+      default: {
+        NOTREACHED();
+      }
+    }
+  }
+
+  void OnJobCompleted(GtkPrintJob* print_job, GError* error) {
+    // Continue() will result in a call to ClientPrintHandlerGtk::OnPrintReset
+    // which deletes |this|. Execute it asnychronously so the call stack has a
+    // chance to unwind.
+    CefPostTask(TID_UI, base::Bind(&CefPrintJobCallback::Continue,
+                                   job_callback_.get()));
+    job_callback_ = nullptr;
+  }
+
+  static void OnDialogResponseThunk(GtkDialog* dialog,
+                                    gint response_id,
+                                    PrintHandler* handler) {
+    handler->OnDialogResponse(dialog, response_id);
+  }
+
+  static void OnJobCompletedThunk(GtkPrintJob* print_job,
+                                  void* handler,
+                                  GError* error) {
+    static_cast<PrintHandler*>(handler)->OnJobCompleted(print_job, error);
+  }
+
+  CefRefPtr<CefBrowser> browser_;
+
+  GtkWidget* dialog_;               // Owned.
+  GtkPrintSettings* gtk_settings_;  // Referenced.
+  GtkPageSetup* page_setup_;        // Referenced.
+  GtkPrinter* printer_;             // Referenced.
+
+  CefRefPtr<CefPrintDialogCallback> dialog_callback_;
+  CefRefPtr<CefPrintJobCallback> job_callback_;
+};
+
+ClientPrintHandlerGtk::ClientPrintHandlerGtk() {}
+
+ClientPrintHandlerGtk::~ClientPrintHandlerGtk() {
+  DCHECK(print_handler_map_.empty());
+}
+
+void ClientPrintHandlerGtk::OnPrintStart(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  const int browser_id = browser->GetIdentifier();
+
+#ifndef _NDEBUG
+  // Print handler should not already exist for the browser.
+  PrintHandlerMap::const_iterator it = print_handler_map_.find(browser_id);
+  DCHECK(it == print_handler_map_.end());
+#endif
+
+  // Create a new print handler.
+  PrintHandler* ph = new PrintHandler(browser);
+  print_handler_map_.insert(std::make_pair(browser_id, ph));
+}
+
+void ClientPrintHandlerGtk::OnPrintSettings(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefPrintSettings> settings,
+    bool get_defaults) {
+  CEF_REQUIRE_UI_THREAD();
+
+  GetPrintHandler(browser)->OnPrintSettings(settings, get_defaults);
+}
+
+bool ClientPrintHandlerGtk::OnPrintDialog(
+    CefRefPtr<CefBrowser> browser,
+    bool has_selection,
+    CefRefPtr<CefPrintDialogCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  PrintHandler* print_handler = GetPrintHandler(browser);
+  GetWindowAndContinue(browser, base::Bind(&PrintHandler::OnPrintDialog,
+                                           base::Unretained(print_handler),
+                                           has_selection, callback));
+  return true;
+}
+
+bool ClientPrintHandlerGtk::OnPrintJob(
+    CefRefPtr<CefBrowser> browser,
+    const CefString& document_name,
+    const CefString& pdf_file_path,
+    CefRefPtr<CefPrintJobCallback> callback) {
+  CEF_REQUIRE_UI_THREAD();
+
+  return GetPrintHandler(browser)->OnPrintJob(document_name, pdf_file_path,
+                                              callback);
+}
+
+void ClientPrintHandlerGtk::OnPrintReset(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Delete the print handler.
+  PrintHandlerMap::iterator it =
+      print_handler_map_.find(browser->GetIdentifier());
+  DCHECK(it != print_handler_map_.end());
+  delete it->second;
+  print_handler_map_.erase(it);
+}
+
+CefSize ClientPrintHandlerGtk::GetPdfPaperSize(int device_units_per_inch) {
+  CEF_REQUIRE_UI_THREAD();
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkPageSetup* page_setup = gtk_page_setup_new();
+
+  float width = gtk_page_setup_get_paper_width(page_setup, GTK_UNIT_INCH);
+  float height = gtk_page_setup_get_paper_height(page_setup, GTK_UNIT_INCH);
+
+  g_object_unref(page_setup);
+
+  return CefSize(width * device_units_per_inch, height * device_units_per_inch);
+}
+
+ClientPrintHandlerGtk::PrintHandler* ClientPrintHandlerGtk::GetPrintHandler(
+    CefRefPtr<CefBrowser> browser) {
+  PrintHandlerMap::const_iterator it =
+      print_handler_map_.find(browser->GetIdentifier());
+  DCHECK(it != print_handler_map_.end());
+  return it->second;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/print_handler_gtk.h b/src/tests/cefclient/browser/print_handler_gtk.h
new file mode 100644
index 0000000..857b8c7
--- /dev/null
+++ b/src/tests/cefclient/browser/print_handler_gtk.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors.
+// Portions Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_PRINT_HANDLER_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_PRINT_HANDLER_GTK_H_
+#pragma once
+
+#include <map>
+
+#include "include/cef_print_handler.h"
+
+namespace client {
+
+class ClientPrintHandlerGtk : public CefPrintHandler {
+ public:
+  ClientPrintHandlerGtk();
+  virtual ~ClientPrintHandlerGtk();
+
+  // CefPrintHandler methods.
+  void OnPrintStart(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnPrintSettings(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefPrintSettings> settings,
+                       bool get_defaults) OVERRIDE;
+  bool OnPrintDialog(CefRefPtr<CefBrowser> browser,
+                     bool has_selection,
+                     CefRefPtr<CefPrintDialogCallback> callback) OVERRIDE;
+  bool OnPrintJob(CefRefPtr<CefBrowser> browser,
+                  const CefString& document_name,
+                  const CefString& pdf_file_path,
+                  CefRefPtr<CefPrintJobCallback> callback) OVERRIDE;
+  void OnPrintReset(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  CefSize GetPdfPaperSize(int device_units_per_inch) OVERRIDE;
+
+ private:
+  // Print handler.
+  struct PrintHandler;
+  PrintHandler* GetPrintHandler(CefRefPtr<CefBrowser> browser);
+
+  // Map of browser ID to print handler.
+  typedef std::map<int, PrintHandler*> PrintHandlerMap;
+  PrintHandlerMap print_handler_map_;
+
+  IMPLEMENT_REFCOUNTING(ClientPrintHandlerGtk);
+  DISALLOW_COPY_AND_ASSIGN(ClientPrintHandlerGtk);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_PRINT_HANDLER_GTK_H_
diff --git a/src/tests/cefclient/browser/resource.h b/src/tests/cefclient/browser/resource.h
new file mode 100644
index 0000000..6003d12
--- /dev/null
+++ b/src/tests/cefclient/browser/resource.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by cefclient.rc
+//
+#define BINARY 256
+#define IDC_MYICON 2
+#define IDD_CEFCLIENT_DIALOG 102
+#define IDS_APP_TITLE 103
+#define IDD_ABOUTBOX 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDI_CEFCLIENT 107
+#define IDI_SMALL 108
+#define IDC_CEFCLIENT 109
+#define IDR_MAINFRAME 128
+#define IDC_NAV_BACK 200
+#define IDC_NAV_FORWARD 201
+#define IDC_NAV_RELOAD 202
+#define IDC_NAV_STOP 203
+#define ID_QUIT 32500
+#define ID_FIND 32501
+#define ID_TESTS_FIRST 32700
+#define ID_TESTS_GETSOURCE 32700
+#define ID_TESTS_GETTEXT 32701
+#define ID_TESTS_OTHER_TESTS 32702
+#define ID_TESTS_PLUGIN_INFO 32703
+#define ID_TESTS_WINDOW_NEW 32704
+#define ID_TESTS_WINDOW_POPUP 32705
+#define ID_TESTS_PRINT 32706
+#define ID_TESTS_REQUEST 32707
+#define ID_TESTS_TRACING_BEGIN 32708
+#define ID_TESTS_TRACING_END 32709
+#define ID_TESTS_ZOOM_IN 32710
+#define ID_TESTS_ZOOM_OUT 32711
+#define ID_TESTS_ZOOM_RESET 32712
+#define ID_TESTS_OSR_FPS 32713
+#define ID_TESTS_OSR_DSF 32714
+#define ID_TESTS_PRINT_TO_PDF 32715
+#define ID_TESTS_MUTE_AUDIO 32716
+#define ID_TESTS_UNMUTE_AUDIO 32717
+#define ID_TESTS_LAST 32717
+#define IDC_STATIC -1
+#define IDS_BINDING_HTML 1000
+#define IDS_DIALOGS_HTML 1001
+#define IDS_DRAGGABLE_HTML 1002
+#define IDS_DRM_HTML 1003
+#define IDS_LOCALSTORAGE_HTML 1004
+#define IDS_LOGO_PNG 1005
+#define IDS_MEDIA_ROUTER_HTML 1006
+#define IDS_MENU_ICON_1X_PNG 1007
+#define IDS_MENU_ICON_2X_PNG 1008
+#define IDS_OSRTEST_HTML 1009
+#define IDS_OTHER_TESTS_HTML 1010
+#define IDS_PDF_HTML 1011
+#define IDS_PDF_PDF 1012
+#define IDS_PERFORMANCE_HTML 1013
+#define IDS_PERFORMANCE2_HTML 1014
+#define IDS_PREFERENCES_HTML 1015
+#define IDS_RESPONSE_FILTER_HTML 1016
+#define IDS_SERVER_HTML 1017
+#define IDS_TRANSPARENCY_HTML 1018
+#define IDS_URLREQUEST_HTML 1019
+#define IDS_WEBSOCKET_HTML 1020
+#define IDS_WINDOW_HTML 1021
+#define IDS_WINDOW_ICON_1X_PNG 1022
+#define IDS_WINDOW_ICON_2X_PNG 1023
+#define IDS_XMLHTTPREQUEST_HTML 1024
+
+#define IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG 1030
+#define IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON 1031
+#define IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML 1032
+#define IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS 1033
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 130
+#define _APS_NEXT_COMMAND_VALUE 32774
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 111
+#endif
+#endif
diff --git a/src/tests/cefclient/browser/resource_util_linux.cc b/src/tests/cefclient/browser/resource_util_linux.cc
new file mode 100644
index 0000000..a7aa555
--- /dev/null
+++ b/src/tests/cefclient/browser/resource_util_linux.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tests/shared/browser/resource_util.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+namespace client {
+
+bool GetResourceDir(std::string& dir) {
+  char buff[1024];
+
+  // Retrieve the executable path.
+  ssize_t len = readlink("/proc/self/exe", buff, sizeof(buff) - 1);
+  if (len == -1)
+    return false;
+
+  buff[len] = 0;
+
+  // Remove the executable name from the path.
+  char* pos = strrchr(buff, '/');
+  if (!pos)
+    return false;
+
+  // Add "cefclient_files" to the path.
+  strcpy(pos + 1, "cefclient_files");
+  dir = std::string(buff);
+  return true;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/resource_util_win_idmap.cc b/src/tests/cefclient/browser/resource_util_win_idmap.cc
new file mode 100644
index 0000000..545ce49
--- /dev/null
+++ b/src/tests/cefclient/browser/resource_util_win_idmap.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <cstring>
+
+#include "tests/cefclient/browser/resource.h"
+
+namespace client {
+
+int GetResourceId(const char* resource_name) {
+  // Map of resource labels to BINARY id values.
+  static struct _resource_map {
+    const char* name;
+    int id;
+  } resource_map[] = {
+      {"binding.html", IDS_BINDING_HTML},
+      {"dialogs.html", IDS_DIALOGS_HTML},
+      {"draggable.html", IDS_DRAGGABLE_HTML},
+      {"drm.html", IDS_DRM_HTML},
+      {"extensions/set_page_color/icon.png",
+       IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG},
+      {"extensions/set_page_color/manifest.json",
+       IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON},
+      {"extensions/set_page_color/popup.html",
+       IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML},
+      {"extensions/set_page_color/popup.js",
+       IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS},
+      {"localstorage.html", IDS_LOCALSTORAGE_HTML},
+      {"logo.png", IDS_LOGO_PNG},
+      {"media_router.html", IDS_MEDIA_ROUTER_HTML},
+      {"menu_icon.1x.png", IDS_MENU_ICON_1X_PNG},
+      {"menu_icon.2x.png", IDS_MENU_ICON_2X_PNG},
+      {"osr_test.html", IDS_OSRTEST_HTML},
+      {"other_tests.html", IDS_OTHER_TESTS_HTML},
+      {"pdf.html", IDS_PDF_HTML},
+      {"pdf.pdf", IDS_PDF_PDF},
+      {"performance.html", IDS_PERFORMANCE_HTML},
+      {"performance2.html", IDS_PERFORMANCE2_HTML},
+      {"preferences.html", IDS_PREFERENCES_HTML},
+      {"response_filter.html", IDS_RESPONSE_FILTER_HTML},
+      {"server.html", IDS_SERVER_HTML},
+      {"transparency.html", IDS_TRANSPARENCY_HTML},
+      {"urlrequest.html", IDS_URLREQUEST_HTML},
+      {"websocket.html", IDS_WEBSOCKET_HTML},
+      {"window.html", IDS_WINDOW_HTML},
+      {"window_icon.1x.png", IDS_WINDOW_ICON_1X_PNG},
+      {"window_icon.2x.png", IDS_WINDOW_ICON_2X_PNG},
+      {"xmlhttprequest.html", IDS_XMLHTTPREQUEST_HTML},
+  };
+
+  for (size_t i = 0; i < sizeof(resource_map) / sizeof(_resource_map); ++i) {
+    if (!strcmp(resource_map[i].name, resource_name))
+      return resource_map[i].id;
+  }
+
+  return 0;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/response_filter_test.cc b/src/tests/cefclient/browser/response_filter_test.cc
new file mode 100644
index 0000000..1832504
--- /dev/null
+++ b/src/tests/cefclient/browser/response_filter_test.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/response_filter_test.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_command_line.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+namespace response_filter_test {
+
+namespace {
+
+const char kTestUrlPath[] = "/response_filter";
+const char kFindString[] = "REPLACE_THIS_STRING";
+const char kReplaceString[] = "This is the replaced string!";
+
+// Helper for passing params to Write().
+#define WRITE_PARAMS data_out_ptr, data_out_size, data_out_written
+
+// Filter the contents of response_filter.html by replacing all instances of
+// |kFindString| with |kReplaceString|. Pass the `--enable-filter-testing`
+// command-line flag (which shrinks the buffer size to 32 bytes) to better test
+// the logic in this implementation.
+class FindReplaceResponseFilter : public CefResponseFilter {
+ public:
+  FindReplaceResponseFilter()
+      : find_match_offset_(0U),
+        replace_overflow_size_(0U),
+        replace_count_(0U) {}
+
+  bool InitFilter() OVERRIDE {
+    const size_t find_size = sizeof(kFindString) - 1;
+    const size_t replace_size = sizeof(kReplaceString) - 1;
+
+    // Determine a reasonable amount of space for find/replace overflow. For
+    // example, the amount of space required if the search string is
+    // found/replaced 10 times (plus space for the count).
+    if (replace_size > find_size)
+      replace_overflow_size_ = (replace_size - find_size + 3) * 10;
+
+    return true;
+  }
+
+  FilterStatus Filter(void* data_in,
+                      size_t data_in_size,
+                      size_t& data_in_read,
+                      void* data_out,
+                      size_t data_out_size,
+                      size_t& data_out_written) OVERRIDE {
+    DCHECK((data_in_size == 0U && !data_in) || (data_in_size > 0U && data_in));
+    DCHECK_EQ(data_in_read, 0U);
+    DCHECK(data_out);
+    DCHECK_GT(data_out_size, 0U);
+    DCHECK_EQ(data_out_written, 0U);
+
+    // All data will be read.
+    data_in_read = data_in_size;
+
+    const size_t find_size = sizeof(kFindString) - 1;
+
+    const char* data_in_ptr = static_cast<char*>(data_in);
+    char* data_out_ptr = static_cast<char*>(data_out);
+
+    // Reset the overflow.
+    std::string old_overflow;
+    if (!overflow_.empty()) {
+      old_overflow = overflow_;
+      overflow_.clear();
+    }
+
+    const size_t likely_out_size =
+        data_in_size + replace_overflow_size_ + old_overflow.size();
+    if (data_out_size < likely_out_size) {
+      // We'll likely need to use the overflow buffer. Size it appropriately.
+      overflow_.reserve(likely_out_size - data_out_size);
+    }
+
+    if (!old_overflow.empty()) {
+      // Write the overflow from last time.
+      Write(old_overflow.c_str(), old_overflow.size(), WRITE_PARAMS);
+    }
+
+    // Evaluate each character in the input buffer. Track how many characters in
+    // a row match kFindString. If kFindString is completely matched then write
+    // kReplaceString. Otherwise, write the input characters as-is.
+    for (size_t i = 0U; i < data_in_size; ++i) {
+      if (data_in_ptr[i] == kFindString[find_match_offset_]) {
+        // Matched the next character in the find string.
+        if (++find_match_offset_ == find_size) {
+          // Complete match of the find string. Write the replace string.
+          std::stringstream ss;
+          ss << ++replace_count_ << ". " << kReplaceString;
+          const std::string& replace_str = ss.str();
+          Write(replace_str.c_str(), replace_str.size(), WRITE_PARAMS);
+
+          // Start over looking for a match.
+          find_match_offset_ = 0;
+        }
+        continue;
+      }
+
+      // Character did not match the find string.
+      if (find_match_offset_ > 0) {
+        // Write the portion of the find string that has matched so far.
+        Write(kFindString, find_match_offset_, WRITE_PARAMS);
+
+        // Start over looking for a match.
+        find_match_offset_ = 0;
+      }
+
+      // Write the current character.
+      Write(&data_in_ptr[i], 1, WRITE_PARAMS);
+    }
+
+    // If a match is currently in-progress we need more data. Otherwise, we're
+    // done.
+    return find_match_offset_ > 0 ? RESPONSE_FILTER_NEED_MORE_DATA
+                                  : RESPONSE_FILTER_DONE;
+  }
+
+ private:
+  inline void Write(const char* str,
+                    size_t str_size,
+                    char*& data_out_ptr,
+                    size_t data_out_size,
+                    size_t& data_out_written) {
+    // Number of bytes remaining in the output buffer.
+    const size_t remaining_space = data_out_size - data_out_written;
+    // Maximum number of bytes we can write into the output buffer.
+    const size_t max_write = std::min(str_size, remaining_space);
+
+    // Write the maximum portion that fits in the output buffer.
+    if (max_write == 1) {
+      // Small optimization for single character writes.
+      *data_out_ptr = str[0];
+      data_out_ptr += 1;
+      data_out_written += 1;
+    } else if (max_write > 1) {
+      memcpy(data_out_ptr, str, max_write);
+      data_out_ptr += max_write;
+      data_out_written += max_write;
+    }
+
+    if (max_write < str_size) {
+      // Need to write more bytes than will fit in the output buffer. Store the
+      // remainder in the overflow buffer.
+      overflow_ += std::string(str + max_write, str_size - max_write);
+    }
+  }
+
+  // The portion of the find string that is currently matching.
+  size_t find_match_offset_;
+
+  // The likely amount of overflow.
+  size_t replace_overflow_size_;
+
+  // Overflow from the output buffer.
+  std::string overflow_;
+
+  // Number of times the the string was found/replaced.
+  size_t replace_count_;
+
+  IMPLEMENT_REFCOUNTING(FindReplaceResponseFilter);
+};
+
+// Filter that writes out all of the contents unchanged.
+class PassThruResponseFilter : public CefResponseFilter {
+ public:
+  PassThruResponseFilter() {}
+
+  bool InitFilter() OVERRIDE { return true; }
+
+  FilterStatus Filter(void* data_in,
+                      size_t data_in_size,
+                      size_t& data_in_read,
+                      void* data_out,
+                      size_t data_out_size,
+                      size_t& data_out_written) OVERRIDE {
+    DCHECK((data_in_size == 0U && !data_in) || (data_in_size > 0U && data_in));
+    DCHECK_EQ(data_in_read, 0U);
+    DCHECK(data_out);
+    DCHECK_GT(data_out_size, 0U);
+    DCHECK_EQ(data_out_written, 0U);
+
+    // All data will be read.
+    data_in_read = data_in_size;
+
+    // Write out the contents unchanged.
+    data_out_written = std::min(data_in_read, data_out_size);
+    if (data_out_written > 0)
+      memcpy(data_out, data_in, data_out_written);
+
+    return RESPONSE_FILTER_DONE;
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(PassThruResponseFilter);
+};
+
+// Returns true if |url| starts with the value specified via the `--filter-url`
+// command-line flag.
+bool MatchesFilterURL(const std::string& url) {
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+  if (command_line->HasSwitch(switches::kFilterURL)) {
+    const std::string& filter_url =
+        command_line->GetSwitchValue(switches::kFilterURL);
+    return url.find(filter_url) == 0;
+  }
+  return false;
+}
+
+}  // namespace
+
+CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response) {
+  // Use the find/replace filter on the test URL.
+  const std::string& url = request->GetURL();
+
+  if (test_runner::IsTestURL(url, kTestUrlPath))
+    return new FindReplaceResponseFilter();
+
+  if (MatchesFilterURL(url))
+    return new PassThruResponseFilter();
+
+  return nullptr;
+}
+
+}  // namespace response_filter_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/response_filter_test.h b/src/tests/cefclient/browser/response_filter_test.h
new file mode 100644
index 0000000..ddb2320
--- /dev/null
+++ b/src/tests/cefclient/browser/response_filter_test.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_RESPONSE_FILTER_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_RESPONSE_FILTER_TEST_H_
+#pragma once
+
+#include "include/cef_browser.h"
+#include "include/cef_request.h"
+#include "include/cef_response.h"
+#include "include/cef_response_filter.h"
+
+namespace client {
+namespace response_filter_test {
+
+// Create a resource response filter. Called from test_runner.cc.
+CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response);
+
+}  // namespace response_filter_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_RESPONSE_FILTER_TEST_H_
diff --git a/src/tests/cefclient/browser/root_window.cc b/src/tests/cefclient/browser/root_window.cc
new file mode 100644
index 0000000..45ae809
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/root_window.h"
+
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/root_window_manager.h"
+
+namespace client {
+
+RootWindowConfig::RootWindowConfig()
+    : always_on_top(false),
+      with_controls(true),
+      with_osr(false),
+      with_extension(false),
+      initially_hidden(false),
+      url(MainContext::Get()->GetMainURL()) {}
+
+RootWindow::RootWindow() : delegate_(nullptr) {}
+
+RootWindow::~RootWindow() {}
+
+// static
+scoped_refptr<RootWindow> RootWindow::GetForBrowser(int browser_id) {
+  return MainContext::Get()->GetRootWindowManager()->GetWindowForBrowser(
+      browser_id);
+}
+
+void RootWindow::OnExtensionsChanged(const ExtensionSet& extensions) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(delegate_);
+  DCHECK(!WithExtension());
+
+  if (extensions.empty())
+    return;
+
+  ExtensionSet::const_iterator it = extensions.begin();
+  for (; it != extensions.end(); ++it) {
+    delegate_->CreateExtensionWindow(*it, CefRect(), nullptr, base::Closure(),
+                                     WithWindowlessRendering());
+  }
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/root_window.h b/src/tests/cefclient/browser/root_window.h
new file mode 100644
index 0000000..b73fc51
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window.h
@@ -0,0 +1,210 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "include/base/cef_callback_forward.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/cef_browser.h"
+#include "include/views/cef_window.h"
+#include "tests/cefclient/browser/client_types.h"
+#include "tests/cefclient/browser/image_cache.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+#if defined(OS_MACOSX) && __OBJC__
+@class NSWindow;
+#endif  // defined(OS_MACOSX) && __OBJC__
+
+namespace client {
+
+// Used to configure how a RootWindow is created.
+struct RootWindowConfig {
+  RootWindowConfig();
+
+  // If true the window will always display above other windows.
+  bool always_on_top;
+
+  // If true the window will show controls.
+  bool with_controls;
+
+  // If true the window will use off-screen rendering.
+  bool with_osr;
+
+  // If true the window is hosting an extension app.
+  bool with_extension;
+
+  // If true the window will be created initially hidden.
+  bool initially_hidden;
+
+  // Requested window position. If |bounds| and |source_bounds| are empty the
+  // default window size and location will be used.
+  CefRect bounds;
+
+  // Position of the UI element that triggered the window creation. If |bounds|
+  // is empty and |source_bounds| is non-empty the new window will be positioned
+  // relative to |source_bounds|. This is currently only implemented for Views-
+  // based windows when |initially_hidden| is also true.
+  CefRect source_bounds;
+
+  // Parent window. Only used for Views-based windows.
+  CefRefPtr<CefWindow> parent_window;
+
+  // Callback to be executed when the window is closed. Will be executed on the
+  // main thread. This is currently only implemented for Views-based windows.
+  base::Closure close_callback;
+
+  // Initial URL to load.
+  std::string url;
+};
+
+typedef std::set<CefRefPtr<CefExtension>> ExtensionSet;
+
+// Represents a top-level native window in the browser process. While references
+// to this object are thread-safe the methods must be called on the main thread
+// unless otherwise indicated.
+class RootWindow
+    : public base::RefCountedThreadSafe<RootWindow, DeleteOnMainThread> {
+ public:
+  // This interface is implemented by the owner of the RootWindow. The methods
+  // of this class will be called on the main thread.
+  class Delegate {
+   public:
+    // Called to retrieve the CefRequestContext for browser. Only called for
+    // non-popup browsers. May return NULL.
+    virtual CefRefPtr<CefRequestContext> GetRequestContext(
+        RootWindow* root_window) = 0;
+
+    // Returns the ImageCache.
+    virtual scoped_refptr<ImageCache> GetImageCache() = 0;
+
+    // Called to execute a test. See resource.h for |test_id| values.
+    virtual void OnTest(RootWindow* root_window, int test_id) = 0;
+
+    // Called to exit the application.
+    virtual void OnExit(RootWindow* root_window) = 0;
+
+    // Called when the RootWindow has been destroyed.
+    virtual void OnRootWindowDestroyed(RootWindow* root_window) = 0;
+
+    // Called when the RootWindow is activated (becomes the foreground window).
+    virtual void OnRootWindowActivated(RootWindow* root_window) = 0;
+
+    // Called when the browser is created for the RootWindow.
+    virtual void OnBrowserCreated(RootWindow* root_window,
+                                  CefRefPtr<CefBrowser> browser) = 0;
+
+    // Create a window for |extension|. |source_bounds| are the bounds of the
+    // UI element, like a button, that triggered the extension.
+    virtual void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
+                                       const CefRect& source_bounds,
+                                       CefRefPtr<CefWindow> parent_window,
+                                       const base::Closure& close_callback,
+                                       bool with_osr) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // Create a new RootWindow object. This method may be called on any thread.
+  // Use RootWindowManager::CreateRootWindow() or CreateRootWindowAsPopup()
+  // instead of calling this method directly. |use_views| will be true if the
+  // Views framework should be used.
+  static scoped_refptr<RootWindow> Create(bool use_views);
+
+  // Returns the RootWindow associated with the specified |browser_id|. Must be
+  // called on the main thread.
+  static scoped_refptr<RootWindow> GetForBrowser(int browser_id);
+
+#if defined(OS_MACOSX) && __OBJC__
+  // Returns the RootWindow associated with the specified |window|. Must be
+  // called on the main thread.
+  static scoped_refptr<RootWindow> GetForNSWindow(NSWindow* window);
+#endif
+
+  // Initialize as a normal window. This will create and show a native window
+  // hosting a single browser instance. This method may be called on any thread.
+  // |delegate| must be non-NULL and outlive this object.
+  // Use RootWindowManager::CreateRootWindow() instead of calling this method
+  // directly.
+  virtual void Init(RootWindow::Delegate* delegate,
+                    const RootWindowConfig& config,
+                    const CefBrowserSettings& settings) = 0;
+
+  // Initialize as a popup window. This is used to attach a new native window to
+  // a single browser instance that will be created later. The native window
+  // will be created and shown once the browser is available. This method may be
+  // called on any thread. |delegate| must be non-NULL and outlive this object.
+  // Use RootWindowManager::CreateRootWindowAsPopup() instead of calling this
+  // method directly. Called on the UI thread.
+  virtual void InitAsPopup(RootWindow::Delegate* delegate,
+                           bool with_controls,
+                           bool with_osr,
+                           const CefPopupFeatures& popupFeatures,
+                           CefWindowInfo& windowInfo,
+                           CefRefPtr<CefClient>& client,
+                           CefBrowserSettings& settings) = 0;
+
+  enum ShowMode {
+    ShowNormal,
+    ShowMinimized,
+    ShowMaximized,
+    ShowNoActivate,
+  };
+
+  // Show the window.
+  virtual void Show(ShowMode mode) = 0;
+
+  // Hide the window.
+  virtual void Hide() = 0;
+
+  // Set the window bounds in screen coordinates.
+  virtual void SetBounds(int x, int y, size_t width, size_t height) = 0;
+
+  // Close the window. If |force| is true onunload handlers will not be
+  // executed.
+  virtual void Close(bool force) = 0;
+
+  // Set the device scale factor. Only used in combination with off-screen
+  // rendering.
+  virtual void SetDeviceScaleFactor(float device_scale_factor) = 0;
+
+  // Returns the device scale factor. Only used in combination with off-screen
+  // rendering.
+  virtual float GetDeviceScaleFactor() const = 0;
+
+  // Returns the browser that this window contains, if any.
+  virtual CefRefPtr<CefBrowser> GetBrowser() const = 0;
+
+  // Returns the native handle for this window, if any.
+  virtual ClientWindowHandle GetWindowHandle() const = 0;
+
+  // Returns true if this window is using windowless rendering (osr).
+  virtual bool WithWindowlessRendering() const = 0;
+
+  // Returns true if this window is hosting an extension app.
+  virtual bool WithExtension() const = 0;
+
+  // Called when the set of loaded extensions changes. The default
+  // implementation will create a single window instance for each extension.
+  virtual void OnExtensionsChanged(const ExtensionSet& extensions);
+
+ protected:
+  // Allow deletion via scoped_refptr only.
+  friend struct DeleteOnMainThread;
+  friend class base::RefCountedThreadSafe<RootWindow, DeleteOnMainThread>;
+
+  RootWindow();
+  virtual ~RootWindow();
+
+  Delegate* delegate_;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_H_
diff --git a/src/tests/cefclient/browser/root_window_create.cc b/src/tests/cefclient/browser/root_window_create.cc
new file mode 100644
index 0000000..04e347c
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_create.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/root_window.h"
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+#include "tests/cefclient/browser/root_window_views.h"
+#endif
+
+#if defined(OS_WIN)
+#include "tests/cefclient/browser/root_window_win.h"
+#elif defined(OS_LINUX)
+#include "tests/cefclient/browser/root_window_gtk.h"
+#elif defined(OS_MACOSX)
+#include "tests/cefclient/browser/root_window_mac.h"
+#endif
+
+namespace client {
+
+// static
+scoped_refptr<RootWindow> RootWindow::Create(bool use_views) {
+  if (use_views) {
+#if defined(OS_WIN) || defined(OS_LINUX)
+    return new RootWindowViews();
+#else
+    LOG(FATAL) << "Views framework is not supported on this platform.";
+#endif
+  }
+
+#if defined(OS_WIN)
+  return new RootWindowWin();
+#elif defined(OS_LINUX)
+  return new RootWindowGtk();
+#elif defined(OS_MACOSX)
+  return new RootWindowMac();
+#endif
+
+  return nullptr;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/root_window_gtk.cc b/src/tests/cefclient/browser/root_window_gtk.cc
new file mode 100644
index 0000000..ba48589
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_gtk.cc
@@ -0,0 +1,911 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/root_window_gtk.h"
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include <X11/Xlib.h>
+#undef Success     // Definition conflicts with cef_message_router.h
+#undef RootWindow  // Definition conflicts with root_window.h
+
+#include "include/base/cef_bind.h"
+#include "include/cef_app.h"
+#include "tests/cefclient/browser/browser_window_osr_gtk.h"
+#include "tests/cefclient/browser/browser_window_std_gtk.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/cefclient/browser/temp_window.h"
+#include "tests/cefclient/browser/util_gtk.h"
+#include "tests/cefclient/browser/window_test_runner_gtk.h"
+#include "tests/shared/browser/main_message_loop.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+
+namespace {
+
+const char kMenuIdKey[] = "menu_id";
+
+bool IsWindowMaximized(GtkWindow* window) {
+  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+  gint state = gdk_window_get_state(gdk_window);
+  return (state & GDK_WINDOW_STATE_MAXIMIZED) ? true : false;
+}
+
+void MinimizeWindow(GtkWindow* window) {
+  // Unmaximize the window before minimizing so restore behaves correctly.
+  if (IsWindowMaximized(window))
+    gtk_window_unmaximize(window);
+
+  gtk_window_iconify(window);
+}
+
+void MaximizeWindow(GtkWindow* window) {
+  gtk_window_maximize(window);
+}
+
+}  // namespace
+
+RootWindowGtk::RootWindowGtk()
+    : with_controls_(false),
+      always_on_top_(false),
+      with_osr_(false),
+      with_extension_(false),
+      is_popup_(false),
+      initialized_(false),
+      window_(nullptr),
+      back_button_(nullptr),
+      forward_button_(nullptr),
+      reload_button_(nullptr),
+      stop_button_(nullptr),
+      url_entry_(nullptr),
+      toolbar_height_(0),
+      menubar_height_(0),
+      window_destroyed_(false),
+      browser_destroyed_(false),
+      force_close_(false),
+      is_closing_(false) {}
+
+RootWindowGtk::~RootWindowGtk() {
+  REQUIRE_MAIN_THREAD();
+
+  // The window and browser should already have been destroyed.
+  DCHECK(window_destroyed_);
+  DCHECK(browser_destroyed_);
+}
+
+void RootWindowGtk::Init(RootWindow::Delegate* delegate,
+                         const RootWindowConfig& config,
+                         const CefBrowserSettings& settings) {
+  DCHECK(delegate);
+  DCHECK(!initialized_);
+
+  delegate_ = delegate;
+  with_controls_ = config.with_controls;
+  always_on_top_ = config.always_on_top;
+  with_osr_ = config.with_osr;
+  with_extension_ = config.with_extension;
+  start_rect_ = config.bounds;
+
+  CreateBrowserWindow(config.url);
+
+  initialized_ = true;
+
+  // Create the native root window on the main thread.
+  if (CURRENTLY_ON_MAIN_THREAD()) {
+    CreateRootWindow(settings, config.initially_hidden);
+  } else {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::CreateRootWindow, this,
+                                 settings, config.initially_hidden));
+  }
+}
+
+void RootWindowGtk::InitAsPopup(RootWindow::Delegate* delegate,
+                                bool with_controls,
+                                bool with_osr,
+                                const CefPopupFeatures& popupFeatures,
+                                CefWindowInfo& windowInfo,
+                                CefRefPtr<CefClient>& client,
+                                CefBrowserSettings& settings) {
+  DCHECK(delegate);
+  DCHECK(!initialized_);
+
+  delegate_ = delegate;
+  with_controls_ = with_controls;
+  with_osr_ = with_osr;
+  is_popup_ = true;
+
+  if (popupFeatures.xSet)
+    start_rect_.x = popupFeatures.x;
+  if (popupFeatures.ySet)
+    start_rect_.y = popupFeatures.y;
+  if (popupFeatures.widthSet)
+    start_rect_.width = popupFeatures.width;
+  if (popupFeatures.heightSet)
+    start_rect_.height = popupFeatures.height;
+
+  CreateBrowserWindow(std::string());
+
+  initialized_ = true;
+
+  // The new popup is initially parented to a temporary window. The native root
+  // window will be created after the browser is created and the popup window
+  // will be re-parented to it at that time.
+  browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo,
+                                  client, settings);
+}
+
+void RootWindowGtk::Show(ShowMode mode) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!window_)
+    return;
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  // Show the GTK window.
+  gtk_widget_show_all(window_);
+
+  if (mode == ShowMinimized)
+    MinimizeWindow(GTK_WINDOW(window_));
+  else if (mode == ShowMaximized)
+    MaximizeWindow(GTK_WINDOW(window_));
+
+  // Flush the display to make sure the underlying X11 window gets created
+  // immediately.
+  GdkWindow* gdk_window = gtk_widget_get_window(window_);
+  GdkDisplay* display = gdk_window_get_display(gdk_window);
+  gdk_display_flush(display);
+}
+
+void RootWindowGtk::Hide() {
+  REQUIRE_MAIN_THREAD();
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  if (window_)
+    gtk_widget_hide(window_);
+}
+
+void RootWindowGtk::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!window_)
+    return;
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkWindow* window = GTK_WINDOW(window_);
+  GdkWindow* gdk_window = gtk_widget_get_window(window_);
+
+  // Make sure the window isn't minimized or maximized.
+  if (IsWindowMaximized(window))
+    gtk_window_unmaximize(window);
+  else
+    gtk_window_present(window);
+
+  gdk_window_move_resize(gdk_window, x, y, width, height);
+}
+
+void RootWindowGtk::Close(bool force) {
+  REQUIRE_MAIN_THREAD();
+
+  if (window_) {
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    if (force) {
+      NotifyForceClose();
+    }
+    gtk_widget_destroy(window_);
+  }
+}
+
+void RootWindowGtk::SetDeviceScaleFactor(float device_scale_factor) {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_ && with_osr_)
+    browser_window_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float RootWindowGtk::GetDeviceScaleFactor() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_ && with_osr_)
+    return browser_window_->GetDeviceScaleFactor();
+
+  NOTREACHED();
+  return 0.0f;
+}
+
+CefRefPtr<CefBrowser> RootWindowGtk::GetBrowser() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_)
+    return browser_window_->GetBrowser();
+  return nullptr;
+}
+
+ClientWindowHandle RootWindowGtk::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+  return window_;
+}
+
+bool RootWindowGtk::WithWindowlessRendering() const {
+  REQUIRE_MAIN_THREAD();
+  return with_osr_;
+}
+
+bool RootWindowGtk::WithExtension() const {
+  REQUIRE_MAIN_THREAD();
+  return with_extension_;
+}
+
+void RootWindowGtk::CreateBrowserWindow(const std::string& startup_url) {
+  if (with_osr_) {
+    OsrRendererSettings settings = {};
+    MainContext::Get()->PopulateOsrSettings(&settings);
+    browser_window_.reset(new BrowserWindowOsrGtk(this, startup_url, settings));
+  } else {
+    browser_window_.reset(new BrowserWindowStdGtk(this, startup_url));
+  }
+}
+
+void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
+                                     bool initially_hidden) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!window_);
+
+  // TODO(port): If no x,y position is specified the window will always appear
+  // in the upper-left corner. Maybe there's a better default place to put it?
+  int x = start_rect_.x;
+  int y = start_rect_.y;
+  int width, height;
+  if (start_rect_.IsEmpty()) {
+    // TODO(port): Also, maybe there's a better way to choose the default size.
+    width = 800;
+    height = 600;
+  } else {
+    width = start_rect_.width;
+    height = start_rect_.height;
+  }
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  CHECK(window_);
+
+  if (always_on_top_) {
+    gtk_window_set_keep_above(GTK_WINDOW(window_), TRUE);
+  }
+
+  gtk_window_set_default_size(GTK_WINDOW(window_), width, height);
+  g_signal_connect(G_OBJECT(window_), "focus-in-event",
+                   G_CALLBACK(&RootWindowGtk::WindowFocusIn), this);
+  g_signal_connect(G_OBJECT(window_), "window-state-event",
+                   G_CALLBACK(&RootWindowGtk::WindowState), this);
+  g_signal_connect(G_OBJECT(window_), "configure-event",
+                   G_CALLBACK(&RootWindowGtk::WindowConfigure), this);
+  g_signal_connect(G_OBJECT(window_), "destroy",
+                   G_CALLBACK(&RootWindowGtk::WindowDestroy), this);
+  g_signal_connect(G_OBJECT(window_), "delete_event",
+                   G_CALLBACK(&RootWindowGtk::WindowDelete), this);
+
+  const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
+  GdkColor color = {0};
+  color.red = CefColorGetR(background_color) * 65535 / 255;
+  color.green = CefColorGetG(background_color) * 65535 / 255;
+  color.blue = CefColorGetB(background_color) * 65535 / 255;
+  gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &color);
+
+  GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
+  g_signal_connect(vbox, "size-allocate",
+                   G_CALLBACK(&RootWindowGtk::VboxSizeAllocated), this);
+  gtk_container_add(GTK_CONTAINER(window_), vbox);
+
+  if (with_controls_) {
+    GtkWidget* menu_bar = CreateMenuBar();
+    g_signal_connect(menu_bar, "size-allocate",
+                     G_CALLBACK(&RootWindowGtk::MenubarSizeAllocated), this);
+
+    gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0);
+
+    GtkWidget* toolbar = gtk_toolbar_new();
+    // Turn off the labels on the toolbar buttons.
+    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
+    g_signal_connect(toolbar, "size-allocate",
+                     G_CALLBACK(&RootWindowGtk::ToolbarSizeAllocated), this);
+
+    back_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
+    g_signal_connect(back_button_, "clicked",
+                     G_CALLBACK(&RootWindowGtk::BackButtonClicked), this);
+    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */);
+
+    forward_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
+    g_signal_connect(forward_button_, "clicked",
+                     G_CALLBACK(&RootWindowGtk::ForwardButtonClicked), this);
+    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */);
+
+    reload_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH);
+    g_signal_connect(reload_button_, "clicked",
+                     G_CALLBACK(&RootWindowGtk::ReloadButtonClicked), this);
+    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */);
+
+    stop_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_STOP);
+    g_signal_connect(stop_button_, "clicked",
+                     G_CALLBACK(&RootWindowGtk::StopButtonClicked), this);
+    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */);
+
+    url_entry_ = gtk_entry_new();
+    g_signal_connect(url_entry_, "activate",
+                     G_CALLBACK(&RootWindowGtk::URLEntryActivate), this);
+    g_signal_connect(url_entry_, "button-press-event",
+                     G_CALLBACK(&RootWindowGtk::URLEntryButtonPress), this);
+
+    GtkToolItem* tool_item = gtk_tool_item_new();
+    gtk_container_add(GTK_CONTAINER(tool_item), url_entry_);
+    gtk_tool_item_set_expand(tool_item, TRUE);
+    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1);  // append
+
+    gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
+  }
+
+  // Realize (show) the GTK widget. This must be done before the browser is
+  // created because the underlying X11 Window is required. |browser_bounds_|
+  // will be set at this point based on the GTK *SizeAllocated signal callbacks.
+  Show(ShowNormal);
+
+  // Most window managers ignore requests for initial window positions (instead
+  // using a user-defined placement algorithm) and honor requests after the
+  // window has already been shown.
+  gtk_window_move(GTK_WINDOW(window_), x, y);
+
+  // Windowed browsers are parented to the X11 Window underlying the GtkWindow*
+  // and must be sized manually. The OSR GTK widget, on the other hand, can be
+  // added to the Vbox container for automatic layout-based sizing.
+  GtkWidget* parent = with_osr_ ? vbox : window_;
+
+  // Set the Display associated with the browser.
+  ::Display* xdisplay = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(window_));
+  CHECK(xdisplay);
+  if (with_osr_) {
+    static_cast<BrowserWindowOsrGtk*>(browser_window_.get())
+        ->set_xdisplay(xdisplay);
+  } else {
+    static_cast<BrowserWindowStdGtk*>(browser_window_.get())
+        ->set_xdisplay(xdisplay);
+  }
+
+  if (!is_popup_) {
+    // Create the browser window.
+    browser_window_->CreateBrowser(parent, browser_bounds_, settings, nullptr,
+                                   delegate_->GetRequestContext(this));
+  } else {
+    // With popups we already have a browser window. Parent the browser window
+    // to the root window and show it in the correct location.
+    browser_window_->ShowPopup(parent, browser_bounds_.x, browser_bounds_.y,
+                               browser_bounds_.width, browser_bounds_.height);
+  }
+}
+
+void RootWindowGtk::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+
+  // For popup browsers create the root window once the browser has been
+  // created.
+  if (is_popup_)
+    CreateRootWindow(CefBrowserSettings(), false);
+
+  delegate_->OnBrowserCreated(this, browser);
+}
+
+void RootWindowGtk::OnBrowserWindowClosing() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI,
+                base::Bind(&RootWindowGtk::OnBrowserWindowClosing, this));
+    return;
+  }
+
+  is_closing_ = true;
+}
+
+void RootWindowGtk::OnBrowserWindowDestroyed() {
+  REQUIRE_MAIN_THREAD();
+
+  browser_window_.reset();
+
+  if (!window_destroyed_) {
+    // The browser was destroyed first. This could be due to the use of
+    // off-screen rendering or execution of JavaScript window.close().
+    // Close the RootWindow.
+    Close(true);
+  }
+
+  NotifyDestroyedIfDone(false, true);
+}
+
+void RootWindowGtk::OnSetAddress(const std::string& url) {
+  REQUIRE_MAIN_THREAD();
+
+  if (url_entry_) {
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    std::string urlStr(url);
+    gtk_entry_set_text(GTK_ENTRY(url_entry_), urlStr.c_str());
+  }
+}
+
+void RootWindowGtk::OnSetTitle(const std::string& title) {
+  REQUIRE_MAIN_THREAD();
+
+  if (window_) {
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    std::string titleStr(title);
+    gtk_window_set_title(GTK_WINDOW(window_), titleStr.c_str());
+  }
+}
+
+void RootWindowGtk::OnSetFullscreen(bool fullscreen) {
+  REQUIRE_MAIN_THREAD();
+
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (browser) {
+    scoped_ptr<window_test::WindowTestRunnerGtk> test_runner(
+        new window_test::WindowTestRunnerGtk());
+    if (fullscreen)
+      test_runner->Maximize(browser);
+    else
+      test_runner->Restore(browser);
+  }
+}
+
+void RootWindowGtk::OnAutoResize(const CefSize& new_size) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!window_)
+    return;
+
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkWindow* window = GTK_WINDOW(window_);
+  GdkWindow* gdk_window = gtk_widget_get_window(window_);
+
+  // Make sure the window isn't minimized or maximized.
+  if (IsWindowMaximized(window))
+    gtk_window_unmaximize(window);
+  else
+    gtk_window_present(window);
+
+  gdk_window_resize(gdk_window, new_size.width, new_size.height);
+}
+
+void RootWindowGtk::OnSetLoadingState(bool isLoading,
+                                      bool canGoBack,
+                                      bool canGoForward) {
+  REQUIRE_MAIN_THREAD();
+
+  if (with_controls_) {
+    ScopedGdkThreadsEnter scoped_gdk_threads;
+
+    gtk_widget_set_sensitive(GTK_WIDGET(stop_button_), isLoading);
+    gtk_widget_set_sensitive(GTK_WIDGET(reload_button_), !isLoading);
+    gtk_widget_set_sensitive(GTK_WIDGET(back_button_), canGoBack);
+    gtk_widget_set_sensitive(GTK_WIDGET(forward_button_), canGoForward);
+  }
+}
+
+void RootWindowGtk::OnSetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  REQUIRE_MAIN_THREAD();
+  // TODO(cef): Implement support for draggable regions on this platform.
+}
+
+void RootWindowGtk::NotifyMoveOrResizeStarted() {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(
+        base::Bind(&RootWindowGtk::NotifyMoveOrResizeStarted, this));
+    return;
+  }
+
+  // Called when size, position or stack order changes.
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (browser.get()) {
+    // Notify the browser of move/resize events so that:
+    // - Popup windows are displayed in the correct location and dismissed
+    //   when the window moves.
+    // - Drag&drop areas are updated accordingly.
+    browser->GetHost()->NotifyMoveOrResizeStarted();
+  }
+}
+
+void RootWindowGtk::NotifySetFocus() {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifySetFocus, this));
+    return;
+  }
+
+  if (!browser_window_.get())
+    return;
+
+  browser_window_->SetFocus(true);
+  delegate_->OnRootWindowActivated(this);
+}
+
+void RootWindowGtk::NotifyVisibilityChange(bool show) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(
+        base::Bind(&RootWindowGtk::NotifyVisibilityChange, this, show));
+    return;
+  }
+
+  if (!browser_window_.get())
+    return;
+
+  if (show)
+    browser_window_->Show();
+  else
+    browser_window_->Hide();
+}
+
+void RootWindowGtk::NotifyMenuBarHeight(int height) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(
+        base::Bind(&RootWindowGtk::NotifyMenuBarHeight, this, height));
+    return;
+  }
+
+  menubar_height_ = height;
+}
+
+void RootWindowGtk::NotifyContentBounds(int x, int y, int width, int height) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyContentBounds, this, x,
+                                 y, width, height));
+    return;
+  }
+
+  // Offset browser positioning by any controls that will appear in the client
+  // area.
+  const int ux_height = toolbar_height_ + menubar_height_;
+  const int browser_x = x;
+  const int browser_y = y + ux_height;
+  const int browser_width = width;
+  const int browser_height = height - ux_height;
+
+  // Size the browser window to match the GTK widget.
+  browser_bounds_ =
+      CefRect(browser_x, browser_y, browser_width, browser_height);
+  if (browser_window_.get()) {
+    browser_window_->SetBounds(browser_x, browser_y, browser_width,
+                               browser_height);
+  }
+}
+
+void RootWindowGtk::NotifyLoadURL(const std::string& url) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyLoadURL, this, url));
+    return;
+  }
+
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (browser.get()) {
+    browser->GetMainFrame()->LoadURL(url);
+  }
+}
+
+void RootWindowGtk::NotifyButtonClicked(int id) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(
+        base::Bind(&RootWindowGtk::NotifyButtonClicked, this, id));
+    return;
+  }
+
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (!browser.get())
+    return;
+
+  switch (id) {
+    case IDC_NAV_BACK:
+      browser->GoBack();
+      break;
+    case IDC_NAV_FORWARD:
+      browser->GoForward();
+      break;
+    case IDC_NAV_RELOAD:
+      browser->Reload();
+      break;
+    case IDC_NAV_STOP:
+      browser->StopLoad();
+      break;
+    default:
+      NOTREACHED() << "id=" << id;
+  }
+}
+
+void RootWindowGtk::NotifyMenuItem(int id) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyMenuItem, this, id));
+    return;
+  }
+
+  // Run the test.
+  if (delegate_)
+    delegate_->OnTest(this, id);
+}
+
+void RootWindowGtk::NotifyForceClose() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(&RootWindowGtk::NotifyForceClose, this));
+    return;
+  }
+
+  force_close_ = true;
+}
+
+void RootWindowGtk::NotifyCloseBrowser() {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyCloseBrowser, this));
+    return;
+  }
+
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (browser) {
+    browser->GetHost()->CloseBrowser(false);
+  }
+}
+
+void RootWindowGtk::NotifyDestroyedIfDone(bool window_destroyed,
+                                          bool browser_destroyed) {
+  // Each call will to this method will set only one state flag.
+  DCHECK_EQ(1, window_destroyed + browser_destroyed);
+
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyDestroyedIfDone, this,
+                                 window_destroyed, browser_destroyed));
+    return;
+  }
+
+  if (window_destroyed)
+    window_destroyed_ = true;
+  if (browser_destroyed)
+    browser_destroyed_ = true;
+
+  // Notify once both the window and the browser have been destroyed.
+  if (window_destroyed_ && browser_destroyed_)
+    delegate_->OnRootWindowDestroyed(this);
+}
+
+// static
+gboolean RootWindowGtk::WindowFocusIn(GtkWidget* widget,
+                                      GdkEventFocus* event,
+                                      RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (event->in) {
+    self->NotifySetFocus();
+
+    // Return true for a windowed browser so that focus is not passed to GTK.
+    return self->with_osr_ ? FALSE : TRUE;
+  }
+
+  return FALSE;
+}
+
+// static
+gboolean RootWindowGtk::WindowState(GtkWidget* widget,
+                                    GdkEventWindowState* event,
+                                    RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Called when the root window is iconified or restored. Hide the browser
+  // window when the root window is iconified to reduce resource usage.
+  if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) {
+    self->NotifyVisibilityChange(
+        !(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED));
+  }
+
+  return TRUE;
+}
+
+// static
+gboolean RootWindowGtk::WindowConfigure(GtkWindow* window,
+                                        GdkEvent* event,
+                                        RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  self->NotifyMoveOrResizeStarted();
+  return FALSE;  // Don't stop this message.
+}
+
+// static
+void RootWindowGtk::WindowDestroy(GtkWidget* widget, RootWindowGtk* self) {
+  // May be called on the main thread or the UI thread.
+  self->NotifyDestroyedIfDone(true, false);
+}
+
+// static
+gboolean RootWindowGtk::WindowDelete(GtkWidget* widget,
+                                     GdkEvent* event,
+                                     RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Called to query whether the root window should be closed.
+  if (self->force_close_)
+    return FALSE;  // Allow the close.
+
+  if (!self->is_closing_) {
+    // Notify the browser window that we would like to close it. This
+    // will result in a call to ClientHandler::DoClose() if the
+    // JavaScript 'onbeforeunload' event handler allows it.
+    self->NotifyCloseBrowser();
+
+    // Cancel the close.
+    return TRUE;
+  }
+
+  // Allow the close.
+  return FALSE;
+}
+
+// static
+void RootWindowGtk::VboxSizeAllocated(GtkWidget* widget,
+                                      GtkAllocation* allocation,
+                                      RootWindowGtk* self) {
+  // May be called on the main thread and the UI thread.
+  self->NotifyContentBounds(allocation->x, allocation->y, allocation->width,
+                            allocation->height);
+}
+
+// static
+void RootWindowGtk::MenubarSizeAllocated(GtkWidget* widget,
+                                         GtkAllocation* allocation,
+                                         RootWindowGtk* self) {
+  // May be called on the main thread and the UI thread.
+  self->NotifyMenuBarHeight(allocation->height);
+}
+
+// static
+gboolean RootWindowGtk::MenuItemActivated(GtkWidget* widget,
+                                          RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Retrieve the menu ID set in AddMenuEntry.
+  int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), kMenuIdKey));
+  self->NotifyMenuItem(id);
+
+  return FALSE;  // Don't stop this message.
+}
+
+// static
+void RootWindowGtk::ToolbarSizeAllocated(GtkWidget* widget,
+                                         GtkAllocation* allocation,
+                                         RootWindowGtk* self) {
+  self->toolbar_height_ = allocation->height;
+}
+
+// static
+void RootWindowGtk::BackButtonClicked(GtkButton* button, RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  self->NotifyButtonClicked(IDC_NAV_BACK);
+}
+
+// static
+void RootWindowGtk::ForwardButtonClicked(GtkButton* button,
+                                         RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  self->NotifyButtonClicked(IDC_NAV_FORWARD);
+}
+
+// static
+void RootWindowGtk::StopButtonClicked(GtkButton* button, RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  self->NotifyButtonClicked(IDC_NAV_STOP);
+}
+
+// static
+void RootWindowGtk::ReloadButtonClicked(GtkButton* button,
+                                        RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  self->NotifyButtonClicked(IDC_NAV_RELOAD);
+}
+
+// static
+void RootWindowGtk::URLEntryActivate(GtkEntry* entry, RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+  const gchar* url = gtk_entry_get_text(entry);
+  self->NotifyLoadURL(std::string(url));
+}
+
+// static
+gboolean RootWindowGtk::URLEntryButtonPress(GtkWidget* widget,
+                                            GdkEventButton* event,
+                                            RootWindowGtk* self) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Give focus to the GTK window. This is a work-around for bad focus-related
+  // interaction between the root window managed by GTK and the browser managed
+  // by X11.
+  GtkWidget* window = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
+  GdkWindow* gdk_window = gtk_widget_get_window(window);
+  ::Display* xdisplay = GDK_WINDOW_XDISPLAY(gdk_window);
+  ::Window xwindow = GDK_WINDOW_XID(gdk_window);
+
+  // Retrieve the atoms required by the below XSendEvent call.
+  const char* kAtoms[] = {"WM_PROTOCOLS", "WM_TAKE_FOCUS"};
+  Atom atoms[2];
+  int result =
+      XInternAtoms(xdisplay, const_cast<char**>(kAtoms), 2, false, atoms);
+  if (!result)
+    NOTREACHED();
+
+  XEvent e;
+  e.type = ClientMessage;
+  e.xany.display = xdisplay;
+  e.xany.window = xwindow;
+  e.xclient.format = 32;
+  e.xclient.message_type = atoms[0];
+  e.xclient.data.l[0] = atoms[1];
+  e.xclient.data.l[1] = CurrentTime;
+  e.xclient.data.l[2] = 0;
+  e.xclient.data.l[3] = 0;
+  e.xclient.data.l[4] = 0;
+
+  XSendEvent(xdisplay, xwindow, false, 0, &e);
+
+  return FALSE;
+}
+
+GtkWidget* RootWindowGtk::CreateMenuBar() {
+  GtkWidget* menu_bar = gtk_menu_bar_new();
+
+  // Create the test menu.
+  GtkWidget* test_menu = CreateMenu(menu_bar, "Tests");
+  AddMenuEntry(test_menu, "Get Source", ID_TESTS_GETSOURCE);
+  AddMenuEntry(test_menu, "Get Text", ID_TESTS_GETTEXT);
+  AddMenuEntry(test_menu, "New Window", ID_TESTS_WINDOW_NEW);
+  AddMenuEntry(test_menu, "Popup Window", ID_TESTS_WINDOW_POPUP);
+  AddMenuEntry(test_menu, "Request", ID_TESTS_REQUEST);
+  AddMenuEntry(test_menu, "Plugin Info", ID_TESTS_PLUGIN_INFO);
+  AddMenuEntry(test_menu, "Zoom In", ID_TESTS_ZOOM_IN);
+  AddMenuEntry(test_menu, "Zoom Out", ID_TESTS_ZOOM_OUT);
+  AddMenuEntry(test_menu, "Zoom Reset", ID_TESTS_ZOOM_RESET);
+  if (with_osr_) {
+    AddMenuEntry(test_menu, "Set FPS", ID_TESTS_OSR_FPS);
+    AddMenuEntry(test_menu, "Set Scale Factor", ID_TESTS_OSR_DSF);
+  }
+  AddMenuEntry(test_menu, "Begin Tracing", ID_TESTS_TRACING_BEGIN);
+  AddMenuEntry(test_menu, "End Tracing", ID_TESTS_TRACING_END);
+  AddMenuEntry(test_menu, "Print", ID_TESTS_PRINT);
+  AddMenuEntry(test_menu, "Print to PDF", ID_TESTS_PRINT_TO_PDF);
+  AddMenuEntry(test_menu, "Mute Audio", ID_TESTS_MUTE_AUDIO);
+  AddMenuEntry(test_menu, "Unmute Audio", ID_TESTS_UNMUTE_AUDIO);
+  AddMenuEntry(test_menu, "Other Tests", ID_TESTS_OTHER_TESTS);
+
+  return menu_bar;
+}
+
+GtkWidget* RootWindowGtk::CreateMenu(GtkWidget* menu_bar, const char* text) {
+  GtkWidget* menu_widget = gtk_menu_new();
+  GtkWidget* menu_header = gtk_menu_item_new_with_label(text);
+  gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_header), menu_widget);
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_header);
+  return menu_widget;
+}
+
+GtkWidget* RootWindowGtk::AddMenuEntry(GtkWidget* menu_widget,
+                                       const char* text,
+                                       int id) {
+  GtkWidget* entry = gtk_menu_item_new_with_label(text);
+  g_signal_connect(entry, "activate",
+                   G_CALLBACK(&RootWindowGtk::MenuItemActivated), this);
+
+  // Set the menu ID that will be retrieved in MenuItemActivated.
+  g_object_set_data(G_OBJECT(entry), kMenuIdKey, GINT_TO_POINTER(id));
+
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu_widget), entry);
+  return entry;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/root_window_gtk.h b/src/tests/cefclient/browser/root_window_gtk.h
new file mode 100644
index 0000000..9982c14
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_gtk.h
@@ -0,0 +1,167 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+#include <string>
+
+#include "include/base/cef_scoped_ptr.h"
+#include "tests/cefclient/browser/browser_window.h"
+#include "tests/cefclient/browser/root_window.h"
+
+namespace client {
+
+// GTK implementation of a top-level native window in the browser process.
+// The methods of this class must be called on the main thread unless otherwise
+// indicated.
+class RootWindowGtk : public RootWindow, public BrowserWindow::Delegate {
+ public:
+  // Constructor may be called on any thread.
+  RootWindowGtk();
+  ~RootWindowGtk();
+
+  // RootWindow methods.
+  void Init(RootWindow::Delegate* delegate,
+            const RootWindowConfig& config,
+            const CefBrowserSettings& settings) OVERRIDE;
+  void InitAsPopup(RootWindow::Delegate* delegate,
+                   bool with_controls,
+                   bool with_osr,
+                   const CefPopupFeatures& popupFeatures,
+                   CefWindowInfo& windowInfo,
+                   CefRefPtr<CefClient>& client,
+                   CefBrowserSettings& settings) OVERRIDE;
+  void Show(ShowMode mode) OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void Close(bool force) OVERRIDE;
+  void SetDeviceScaleFactor(float device_scale_factor) OVERRIDE;
+  float GetDeviceScaleFactor() const OVERRIDE;
+  CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+  bool WithWindowlessRendering() const OVERRIDE;
+  bool WithExtension() const OVERRIDE;
+
+ private:
+  void CreateBrowserWindow(const std::string& startup_url);
+  void CreateRootWindow(const CefBrowserSettings& settings,
+                        bool initially_hidden);
+
+  // BrowserWindow::Delegate methods.
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBrowserWindowClosing() OVERRIDE;
+  void OnBrowserWindowDestroyed() OVERRIDE;
+  void OnSetAddress(const std::string& url) OVERRIDE;
+  void OnSetTitle(const std::string& title) OVERRIDE;
+  void OnSetFullscreen(bool fullscreen) OVERRIDE;
+  void OnAutoResize(const CefSize& new_size) OVERRIDE;
+  void OnSetLoadingState(bool isLoading,
+                         bool canGoBack,
+                         bool canGoForward) OVERRIDE;
+  void OnSetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) OVERRIDE;
+
+  void NotifyMoveOrResizeStarted();
+  void NotifySetFocus();
+  void NotifyVisibilityChange(bool show);
+  void NotifyMenuBarHeight(int height);
+  void NotifyContentBounds(int x, int y, int width, int height);
+  void NotifyLoadURL(const std::string& url);
+  void NotifyButtonClicked(int id);
+  void NotifyMenuItem(int id);
+  void NotifyForceClose();
+  void NotifyCloseBrowser();
+  void NotifyDestroyedIfDone(bool window_destroyed, bool browser_destroyed);
+
+  GtkWidget* CreateMenuBar();
+  GtkWidget* CreateMenu(GtkWidget* menu_bar, const char* text);
+  GtkWidget* AddMenuEntry(GtkWidget* menu_widget, const char* text, int id);
+
+  // Signal handlers for the top-level GTK window.
+  static gboolean WindowFocusIn(GtkWidget* widget,
+                                GdkEventFocus* event,
+                                RootWindowGtk* self);
+  static gboolean WindowState(GtkWidget* widget,
+                              GdkEventWindowState* event,
+                              RootWindowGtk* self);
+  static gboolean WindowConfigure(GtkWindow* window,
+                                  GdkEvent* event,
+                                  RootWindowGtk* self);
+  static void WindowDestroy(GtkWidget* widget, RootWindowGtk* self);
+  static gboolean WindowDelete(GtkWidget* widget,
+                               GdkEvent* event,
+                               RootWindowGtk* self);
+
+  // Signal handlers for the GTK Vbox containing all UX elements.
+  static void VboxSizeAllocated(GtkWidget* widget,
+                                GtkAllocation* allocation,
+                                RootWindowGtk* self);
+
+  // Signal handlers for the GTK menu bar.
+  static void MenubarSizeAllocated(GtkWidget* widget,
+                                   GtkAllocation* allocation,
+                                   RootWindowGtk* self);
+  static gboolean MenuItemActivated(GtkWidget* widget, RootWindowGtk* self);
+
+  // Signal handlers for the GTK toolbar.
+  static void ToolbarSizeAllocated(GtkWidget* widget,
+                                   GtkAllocation* allocation,
+                                   RootWindowGtk* self);
+  static void BackButtonClicked(GtkButton* button, RootWindowGtk* self);
+  static void ForwardButtonClicked(GtkButton* button, RootWindowGtk* self);
+  static void StopButtonClicked(GtkButton* button, RootWindowGtk* self);
+  static void ReloadButtonClicked(GtkButton* button, RootWindowGtk* self);
+
+  // Signal handlers for the GTK URL entry field.
+  static void URLEntryActivate(GtkEntry* entry, RootWindowGtk* self);
+  static gboolean URLEntryButtonPress(GtkWidget* widget,
+                                      GdkEventButton* event,
+                                      RootWindowGtk* self);
+
+  // After initialization all members are only accessed on the main thread.
+  // Members set during initialization.
+  bool with_controls_;
+  bool always_on_top_;
+  bool with_osr_;
+  bool with_extension_;
+  bool is_popup_;
+  CefRect start_rect_;
+  scoped_ptr<BrowserWindow> browser_window_;
+  bool initialized_;
+
+  // Main window.
+  GtkWidget* window_;
+
+  // Buttons.
+  GtkToolItem* back_button_;
+  GtkToolItem* forward_button_;
+  GtkToolItem* reload_button_;
+  GtkToolItem* stop_button_;
+
+  // URL text field.
+  GtkWidget* url_entry_;
+
+  // Height of UX controls that affect browser window placement.
+  int toolbar_height_;
+  int menubar_height_;
+
+  CefRect browser_bounds_;
+
+  bool window_destroyed_;
+  bool browser_destroyed_;
+
+  // Members only accessed on the UI thread because they're needed for
+  // WindowDelete.
+  bool force_close_;
+  bool is_closing_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowGtk);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_GTK_H_
diff --git a/src/tests/cefclient/browser/root_window_mac.h b/src/tests/cefclient/browser/root_window_mac.h
new file mode 100644
index 0000000..ddd76ba
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_mac.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_MAC_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_MAC_H_
+#pragma once
+
+#include <string>
+
+#include "include/base/cef_scoped_ptr.h"
+#include "tests/cefclient/browser/browser_window.h"
+#include "tests/cefclient/browser/root_window.h"
+
+namespace client {
+
+class RootWindowMacImpl;
+
+// OS X implementation of a top-level native window in the browser process.
+// The methods of this class must be called on the main thread unless otherwise
+// indicated.
+class RootWindowMac : public RootWindow, public BrowserWindow::Delegate {
+ public:
+  // Constructor may be called on any thread.
+  RootWindowMac();
+  ~RootWindowMac();
+
+  BrowserWindow* browser_window() const;
+  RootWindow::Delegate* delegate() const;
+
+  // RootWindow methods.
+  void Init(RootWindow::Delegate* delegate,
+            const RootWindowConfig& config,
+            const CefBrowserSettings& settings) OVERRIDE;
+  void InitAsPopup(RootWindow::Delegate* delegate,
+                   bool with_controls,
+                   bool with_osr,
+                   const CefPopupFeatures& popupFeatures,
+                   CefWindowInfo& windowInfo,
+                   CefRefPtr<CefClient>& client,
+                   CefBrowserSettings& settings) OVERRIDE;
+  void Show(ShowMode mode) OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void Close(bool force) OVERRIDE;
+  void SetDeviceScaleFactor(float device_scale_factor) OVERRIDE;
+  float GetDeviceScaleFactor() const OVERRIDE;
+  CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+  bool WithWindowlessRendering() const OVERRIDE;
+  bool WithExtension() const OVERRIDE;
+
+  // BrowserWindow::Delegate methods.
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBrowserWindowDestroyed() OVERRIDE;
+  void OnSetAddress(const std::string& url) OVERRIDE;
+  void OnSetTitle(const std::string& title) OVERRIDE;
+  void OnSetFullscreen(bool fullscreen) OVERRIDE;
+  void OnAutoResize(const CefSize& new_size) OVERRIDE;
+  void OnSetLoadingState(bool isLoading,
+                         bool canGoBack,
+                         bool canGoForward) OVERRIDE;
+  void OnSetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) OVERRIDE;
+
+  void OnNativeWindowClosed();
+
+ private:
+  CefRefPtr<RootWindowMacImpl> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowMac);
+
+  friend class RootWindowMacImpl;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_MAC_H_
diff --git a/src/tests/cefclient/browser/root_window_mac.mm b/src/tests/cefclient/browser/root_window_mac.mm
new file mode 100644
index 0000000..6595407
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_mac.mm
@@ -0,0 +1,929 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/root_window_mac.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_app.h"
+#include "include/cef_application_mac.h"
+#include "tests/cefclient/browser/browser_window_osr_mac.h"
+#include "tests/cefclient/browser/browser_window_std_mac.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/temp_window.h"
+#include "tests/cefclient/browser/window_test_runner_mac.h"
+#include "tests/shared/browser/main_message_loop.h"
+#include "tests/shared/common/client_switches.h"
+
+// Receives notifications from controls and the browser window. Will delete
+// itself when done.
+@interface RootWindowDelegate : NSObject <NSWindowDelegate> {
+ @private
+  NSWindow* window_;
+  client::RootWindowMac* root_window_;
+  bool force_close_;
+}
+
+@property(nonatomic, readonly) client::RootWindowMac* root_window;
+@property(nonatomic, readwrite) bool force_close;
+
+- (id)initWithWindow:(NSWindow*)window
+       andRootWindow:(client::RootWindowMac*)root_window;
+- (IBAction)goBack:(id)sender;
+- (IBAction)goForward:(id)sender;
+- (IBAction)reload:(id)sender;
+- (IBAction)stopLoading:(id)sender;
+- (IBAction)takeURLStringValueFrom:(NSTextField*)sender;
+@end  // @interface RootWindowDelegate
+
+namespace client {
+
+namespace {
+
+// Sizes for URL bar layout.
+#define BUTTON_HEIGHT 22
+#define BUTTON_WIDTH 72
+#define BUTTON_MARGIN 8
+#define URLBAR_HEIGHT 32
+
+NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
+  NSButton* button = [[NSButton alloc] initWithFrame:*rect];
+#if !__has_feature(objc_arc)
+  [button autorelease];
+#endif  // !__has_feature(objc_arc)
+  [button setTitle:title];
+  [button setBezelStyle:NSSmallSquareBezelStyle];
+  [button setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)];
+  [parent addSubview:button];
+  rect->origin.x += BUTTON_WIDTH;
+  return button;
+}
+
+NSRect GetScreenRectForWindow(NSWindow* window) {
+  NSScreen* screen = [window screen];
+  if (screen == nil)
+    screen = [NSScreen mainScreen];
+  return [screen visibleFrame];
+}
+
+}  // namespace
+
+class RootWindowMacImpl
+    : public base::RefCountedThreadSafe<RootWindowMacImpl, DeleteOnMainThread> {
+ public:
+  RootWindowMacImpl(RootWindowMac& root_window);
+  ~RootWindowMacImpl();
+
+  // Called by RootWindowDelegate after the associated NSWindow has been
+  // closed.
+  void OnNativeWindowClosed();
+
+  void CreateBrowserWindow(const std::string& startup_url);
+  void CreateRootWindow(const CefBrowserSettings& settings,
+                        bool initially_hidden);
+
+  // RootWindow methods.
+  void Init(RootWindow::Delegate* delegate,
+            const RootWindowConfig& config,
+            const CefBrowserSettings& settings);
+  void InitAsPopup(RootWindow::Delegate* delegate,
+                   bool with_controls,
+                   bool with_osr,
+                   const CefPopupFeatures& popupFeatures,
+                   CefWindowInfo& windowInfo,
+                   CefRefPtr<CefClient>& client,
+                   CefBrowserSettings& settings);
+  void Show(RootWindow::ShowMode mode);
+  void Hide();
+  void SetBounds(int x, int y, size_t width, size_t height);
+  void Close(bool force);
+  void SetDeviceScaleFactor(float device_scale_factor);
+  float GetDeviceScaleFactor() const;
+  CefRefPtr<CefBrowser> GetBrowser() const;
+  ClientWindowHandle GetWindowHandle() const;
+  bool WithWindowlessRendering() const;
+  bool WithExtension() const;
+
+  // BrowserWindow::Delegate methods.
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser);
+  void OnBrowserWindowDestroyed();
+  void OnSetAddress(const std::string& url);
+  void OnSetTitle(const std::string& title);
+  void OnSetFullscreen(bool fullscreen);
+  void OnAutoResize(const CefSize& new_size);
+  void OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward);
+  void OnSetDraggableRegions(const std::vector<CefDraggableRegion>& regions);
+
+  void NotifyDestroyedIfDone();
+
+  // After initialization all members are only accessed on the main thread.
+  // Members set during initialization.
+  RootWindowMac& root_window_;
+  bool with_controls_;
+  bool with_osr_;
+  bool with_extension_;
+  bool is_popup_;
+  CefRect start_rect_;
+  scoped_ptr<BrowserWindow> browser_window_;
+  bool initialized_;
+
+  // Main window.
+  NSWindow* window_;
+  RootWindowDelegate* window_delegate_;
+
+  // Buttons.
+  NSButton* back_button_;
+  NSButton* forward_button_;
+  NSButton* reload_button_;
+  NSButton* stop_button_;
+
+  // URL text field.
+  NSTextField* url_textfield_;
+
+  bool window_destroyed_;
+  bool browser_destroyed_;
+};
+
+RootWindowMacImpl::RootWindowMacImpl(RootWindowMac& root_window)
+    : root_window_(root_window),
+      with_controls_(false),
+      with_osr_(false),
+      is_popup_(false),
+      initialized_(false),
+      window_(nil),
+      back_button_(nil),
+      forward_button_(nil),
+      reload_button_(nil),
+      stop_button_(nil),
+      url_textfield_(nil),
+      window_destroyed_(false),
+      browser_destroyed_(false) {}
+
+RootWindowMacImpl::~RootWindowMacImpl() {
+  REQUIRE_MAIN_THREAD();
+
+  // The window and browser should already have been destroyed.
+  DCHECK(window_destroyed_);
+  DCHECK(browser_destroyed_);
+}
+
+void RootWindowMacImpl::Init(RootWindow::Delegate* delegate,
+                             const RootWindowConfig& config,
+                             const CefBrowserSettings& settings) {
+  DCHECK(!initialized_);
+
+  with_controls_ = config.with_controls;
+  with_osr_ = config.with_osr;
+  with_extension_ = config.with_extension;
+  start_rect_ = config.bounds;
+
+  CreateBrowserWindow(config.url);
+
+  initialized_ = true;
+
+  // Create the native root window on the main thread.
+  if (CURRENTLY_ON_MAIN_THREAD()) {
+    CreateRootWindow(settings, config.initially_hidden);
+  } else {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowMacImpl::CreateRootWindow, this,
+                                 settings, config.initially_hidden));
+  }
+}
+
+void RootWindowMacImpl::InitAsPopup(RootWindow::Delegate* delegate,
+                                    bool with_controls,
+                                    bool with_osr,
+                                    const CefPopupFeatures& popupFeatures,
+                                    CefWindowInfo& windowInfo,
+                                    CefRefPtr<CefClient>& client,
+                                    CefBrowserSettings& settings) {
+  DCHECK(delegate);
+  DCHECK(!initialized_);
+
+  with_controls_ = with_controls;
+  with_osr_ = with_osr;
+  is_popup_ = true;
+
+  if (popupFeatures.xSet)
+    start_rect_.x = popupFeatures.x;
+  if (popupFeatures.ySet)
+    start_rect_.y = popupFeatures.y;
+  if (popupFeatures.widthSet)
+    start_rect_.width = popupFeatures.width;
+  if (popupFeatures.heightSet)
+    start_rect_.height = popupFeatures.height;
+
+  CreateBrowserWindow(std::string());
+
+  initialized_ = true;
+
+  // The new popup is initially parented to a temporary window. The native root
+  // window will be created after the browser is created and the popup window
+  // will be re-parented to it at that time.
+  browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo,
+                                  client, settings);
+}
+
+void RootWindowMacImpl::Show(RootWindow::ShowMode mode) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!window_)
+    return;
+
+  const bool is_visible = [window_ isVisible];
+  const bool is_minimized = [window_ isMiniaturized];
+  const bool is_maximized = [window_ isZoomed];
+
+  if ((mode == RootWindow::ShowMinimized && is_minimized) ||
+      (mode == RootWindow::ShowMaximized && is_maximized) ||
+      (mode == RootWindow::ShowNormal && is_visible)) {
+    // The window is already in the desired state.
+    return;
+  }
+
+  // Undo the previous state since it's not the desired state.
+  if (is_minimized)
+    [window_ deminiaturize:nil];
+  else if (is_maximized)
+    [window_ performZoom:nil];
+
+  // Window visibility may change after (for example) deminiaturizing the
+  // window.
+  if (![window_ isVisible])
+    [window_ makeKeyAndOrderFront:nil];
+
+  if (mode == RootWindow::ShowMinimized)
+    [window_ performMiniaturize:nil];
+  else if (mode == RootWindow::ShowMaximized)
+    [window_ performZoom:nil];
+}
+
+void RootWindowMacImpl::Hide() {
+  REQUIRE_MAIN_THREAD();
+
+  if (!window_)
+    return;
+
+  // Undo miniaturization, if any, so the window will actually be hidden.
+  if ([window_ isMiniaturized])
+    [window_ deminiaturize:nil];
+
+  // Hide the window.
+  [window_ orderOut:nil];
+}
+
+void RootWindowMacImpl::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!window_)
+    return;
+
+  NSRect screen_rect = GetScreenRectForWindow(window_);
+
+  // Desired content rectangle.
+  NSRect content_rect;
+  content_rect.size.width = static_cast<int>(width);
+  content_rect.size.height =
+      static_cast<int>(height) + (with_controls_ ? URLBAR_HEIGHT : 0);
+
+  // Convert to a frame rectangle.
+  NSRect frame_rect = [window_ frameRectForContentRect:content_rect];
+  frame_rect.origin.x = x;
+  frame_rect.origin.y = screen_rect.size.height - y;
+
+  [window_ setFrame:frame_rect display:YES];
+}
+
+void RootWindowMacImpl::Close(bool force) {
+  REQUIRE_MAIN_THREAD();
+
+  if (window_) {
+    static_cast<RootWindowDelegate*>([window_ delegate]).force_close = force;
+    [window_ performClose:nil];
+    window_destroyed_ = true;
+  }
+}
+
+void RootWindowMacImpl::SetDeviceScaleFactor(float device_scale_factor) {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_ && with_osr_)
+    browser_window_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float RootWindowMacImpl::GetDeviceScaleFactor() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_ && with_osr_)
+    return browser_window_->GetDeviceScaleFactor();
+
+  NOTREACHED();
+  return 0.0f;
+}
+
+CefRefPtr<CefBrowser> RootWindowMacImpl::GetBrowser() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_)
+    return browser_window_->GetBrowser();
+  return NULL;
+}
+
+ClientWindowHandle RootWindowMacImpl::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+  return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE([window_ contentView]);
+}
+
+bool RootWindowMacImpl::WithWindowlessRendering() const {
+  REQUIRE_MAIN_THREAD();
+  return with_osr_;
+}
+
+bool RootWindowMacImpl::WithExtension() const {
+  REQUIRE_MAIN_THREAD();
+  return with_extension_;
+}
+
+void RootWindowMacImpl::OnNativeWindowClosed() {
+  window_ = nil;
+  window_destroyed_ = true;
+  NotifyDestroyedIfDone();
+}
+
+void RootWindowMacImpl::CreateBrowserWindow(const std::string& startup_url) {
+  if (with_osr_) {
+    OsrRendererSettings settings = {};
+    MainContext::Get()->PopulateOsrSettings(&settings);
+    browser_window_.reset(
+        new BrowserWindowOsrMac(&root_window_, startup_url, settings));
+  } else {
+    browser_window_.reset(new BrowserWindowStdMac(&root_window_, startup_url));
+  }
+}
+
+void RootWindowMacImpl::CreateRootWindow(const CefBrowserSettings& settings,
+                                         bool initially_hidden) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!window_);
+
+  // TODO(port): If no x,y position is specified the window will always appear
+  // in the upper-left corner. Maybe there's a better default place to put it?
+  int x = start_rect_.x;
+  int y = start_rect_.y;
+  int width, height;
+  if (start_rect_.IsEmpty()) {
+    // TODO(port): Also, maybe there's a better way to choose the default size.
+    width = 800;
+    height = 600;
+  } else {
+    width = start_rect_.width;
+    height = start_rect_.height;
+  }
+
+  // Create the main window.
+  NSRect screen_rect = [[NSScreen mainScreen] visibleFrame];
+  NSRect window_rect =
+      NSMakeRect(x, screen_rect.size.height - y, width, height);
+
+  // The CEF framework library is loaded at runtime so we need to use this
+  // mechanism for retrieving the class.
+  Class window_class = NSClassFromString(@"UnderlayOpenGLHostingWindow");
+  CHECK(window_class);
+
+  window_ = [[window_class alloc]
+      initWithContentRect:window_rect
+                styleMask:(NSTitledWindowMask | NSClosableWindowMask |
+                           NSMiniaturizableWindowMask | NSResizableWindowMask |
+                           NSUnifiedTitleAndToolbarWindowMask)
+                  backing:NSBackingStoreBuffered
+                    defer:NO];
+  [window_ setTitle:@"cefclient"];
+  // No dark mode, please
+  window_.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
+
+  // Create the delegate for control and browser window events.
+  window_delegate_ = [[RootWindowDelegate alloc] initWithWindow:window_
+                                                  andRootWindow:&root_window_];
+
+  // Rely on the window delegate to clean us up rather than immediately
+  // releasing when the window gets closed. We use the delegate to do
+  // everything from the autorelease pool so the window isn't on the stack
+  // during cleanup (ie, a window close from javascript).
+  [window_ setReleasedWhenClosed:NO];
+
+  const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
+  [window_
+      setBackgroundColor:[NSColor
+                             colorWithCalibratedRed:float(CefColorGetR(
+                                                        background_color)) /
+                                                    255.0f
+                                              green:float(CefColorGetG(
+                                                        background_color)) /
+                                                    255.0f
+                                               blue:float(CefColorGetB(
+                                                        background_color)) /
+                                                    255.0f
+                                              alpha:1.f]];
+
+  NSView* contentView = [window_ contentView];
+  NSRect contentBounds = [contentView bounds];
+
+  if (!with_osr_) {
+    // Make the content view for the window have a layer. This will make all
+    // sub-views have layers. This is necessary to ensure correct layer
+    // ordering of all child views and their layers.
+    [contentView setWantsLayer:YES];
+  }
+
+  if (with_controls_) {
+    // Create the buttons.
+    NSRect button_rect = contentBounds;
+    button_rect.origin.y = window_rect.size.height - URLBAR_HEIGHT +
+                           (URLBAR_HEIGHT - BUTTON_HEIGHT) / 2;
+    button_rect.size.height = BUTTON_HEIGHT;
+    button_rect.origin.x += BUTTON_MARGIN;
+    button_rect.size.width = BUTTON_WIDTH;
+
+    contentBounds.size.height -= URLBAR_HEIGHT;
+
+    back_button_ = MakeButton(&button_rect, @"Back", contentView);
+    [back_button_ setTarget:window_delegate_];
+    [back_button_ setAction:@selector(goBack:)];
+    [back_button_ setEnabled:NO];
+
+    forward_button_ = MakeButton(&button_rect, @"Forward", contentView);
+    [forward_button_ setTarget:window_delegate_];
+    [forward_button_ setAction:@selector(goForward:)];
+    [forward_button_ setEnabled:NO];
+
+    reload_button_ = MakeButton(&button_rect, @"Reload", contentView);
+    [reload_button_ setTarget:window_delegate_];
+    [reload_button_ setAction:@selector(reload:)];
+    [reload_button_ setEnabled:NO];
+
+    stop_button_ = MakeButton(&button_rect, @"Stop", contentView);
+    [stop_button_ setTarget:window_delegate_];
+    [stop_button_ setAction:@selector(stopLoading:)];
+    [stop_button_ setEnabled:NO];
+
+    // Create the URL text field.
+    button_rect.origin.x += BUTTON_MARGIN;
+    button_rect.size.width =
+        [contentView bounds].size.width - button_rect.origin.x - BUTTON_MARGIN;
+    url_textfield_ = [[NSTextField alloc] initWithFrame:button_rect];
+    [contentView addSubview:url_textfield_];
+    [url_textfield_
+        setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
+    [url_textfield_ setTarget:window_delegate_];
+    [url_textfield_ setAction:@selector(takeURLStringValueFrom:)];
+    [url_textfield_ setEnabled:NO];
+    [[url_textfield_ cell] setWraps:NO];
+    [[url_textfield_ cell] setScrollable:YES];
+  }
+
+  if (!is_popup_) {
+    // Create the browser window.
+    browser_window_->CreateBrowser(
+        CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(contentView),
+        CefRect(0, 0, width, height), settings, NULL,
+        root_window_.delegate_->GetRequestContext(&root_window_));
+  } else {
+    // With popups we already have a browser window. Parent the browser window
+    // to the root window and show it in the correct location.
+    browser_window_->ShowPopup(CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(contentView), 0,
+                               0, contentBounds.size.width,
+                               contentBounds.size.height);
+  }
+
+  if (!initially_hidden) {
+    // Show the window.
+    Show(RootWindow::ShowNormal);
+
+    // Size the window.
+    SetBounds(x, y, width, height);
+  }
+}
+
+void RootWindowMacImpl::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+
+  // For popup browsers create the root window once the browser has been
+  // created.
+  if (is_popup_)
+    CreateRootWindow(CefBrowserSettings(), false);
+
+  root_window_.delegate_->OnBrowserCreated(&root_window_, browser);
+}
+
+void RootWindowMacImpl::OnBrowserWindowDestroyed() {
+  REQUIRE_MAIN_THREAD();
+
+  browser_window_.reset();
+
+  if (!window_destroyed_) {
+    // The browser was destroyed first. This could be due to the use of
+    // off-screen rendering or execution of JavaScript window.close().
+    // Close the RootWindow.
+    Close(true);
+  }
+
+  browser_destroyed_ = true;
+  NotifyDestroyedIfDone();
+}
+
+void RootWindowMacImpl::OnSetAddress(const std::string& url) {
+  REQUIRE_MAIN_THREAD();
+
+  if (url_textfield_) {
+    std::string urlStr(url);
+    NSString* str = [NSString stringWithUTF8String:urlStr.c_str()];
+    [url_textfield_ setStringValue:str];
+  }
+}
+
+void RootWindowMacImpl::OnSetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  REQUIRE_MAIN_THREAD();
+  // TODO(cef): Implement support for draggable regions on this platform.
+}
+
+void RootWindowMacImpl::OnSetTitle(const std::string& title) {
+  REQUIRE_MAIN_THREAD();
+
+  if (window_) {
+    std::string titleStr(title);
+    NSString* str = [NSString stringWithUTF8String:titleStr.c_str()];
+    [window_ setTitle:str];
+  }
+}
+
+void RootWindowMacImpl::OnSetFullscreen(bool fullscreen) {
+  REQUIRE_MAIN_THREAD();
+
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (browser) {
+    scoped_ptr<window_test::WindowTestRunnerMac> test_runner(
+        new window_test::WindowTestRunnerMac());
+    if (fullscreen)
+      test_runner->Maximize(browser);
+    else
+      test_runner->Restore(browser);
+  }
+}
+
+void RootWindowMacImpl::OnAutoResize(const CefSize& new_size) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!window_)
+    return;
+
+  // Desired content rectangle.
+  NSRect content_rect;
+  content_rect.size.width = static_cast<int>(new_size.width);
+  content_rect.size.height =
+      static_cast<int>(new_size.height) + (with_controls_ ? URLBAR_HEIGHT : 0);
+
+  // Convert to a frame rectangle.
+  NSRect frame_rect = [window_ frameRectForContentRect:content_rect];
+  // Don't change the origin.
+  frame_rect.origin = window_.frame.origin;
+
+  [window_ setFrame:frame_rect display:YES];
+
+  // Make sure the window is visible.
+  Show(RootWindow::ShowNormal);
+}
+
+void RootWindowMacImpl::OnSetLoadingState(bool isLoading,
+                                          bool canGoBack,
+                                          bool canGoForward) {
+  REQUIRE_MAIN_THREAD();
+
+  if (with_controls_) {
+    [url_textfield_ setEnabled:YES];
+    [reload_button_ setEnabled:!isLoading];
+    [stop_button_ setEnabled:isLoading];
+    [back_button_ setEnabled:canGoBack];
+    [forward_button_ setEnabled:canGoForward];
+  }
+
+  // After Loading is done, check if voiceover is running and accessibility
+  // should be enabled.
+  if (!isLoading) {
+    Boolean keyExists = false;
+    // On OSX there is no API to query if VoiceOver is active or not. The value
+    // however is stored in preferences that can be queried.
+    if (CFPreferencesGetAppBooleanValue(CFSTR("voiceOverOnOffKey"),
+                                        CFSTR("com.apple.universalaccess"),
+                                        &keyExists)) {
+      GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
+    }
+  }
+}
+
+void RootWindowMacImpl::NotifyDestroyedIfDone() {
+  // Notify once both the window and the browser have been destroyed.
+  if (window_destroyed_ && browser_destroyed_)
+    root_window_.delegate_->OnRootWindowDestroyed(&root_window_);
+}
+
+RootWindowMac::RootWindowMac() {
+  impl_ = new RootWindowMacImpl(*this);
+}
+
+RootWindowMac::~RootWindowMac() {}
+
+BrowserWindow* RootWindowMac::browser_window() const {
+  return impl_->browser_window_.get();
+}
+
+RootWindow::Delegate* RootWindowMac::delegate() const {
+  return delegate_;
+}
+
+void RootWindowMac::Init(RootWindow::Delegate* delegate,
+                         const RootWindowConfig& config,
+                         const CefBrowserSettings& settings) {
+  DCHECK(delegate);
+  delegate_ = delegate;
+  impl_->Init(delegate, config, settings);
+}
+
+void RootWindowMac::InitAsPopup(RootWindow::Delegate* delegate,
+                                bool with_controls,
+                                bool with_osr,
+                                const CefPopupFeatures& popupFeatures,
+                                CefWindowInfo& windowInfo,
+                                CefRefPtr<CefClient>& client,
+                                CefBrowserSettings& settings) {
+  DCHECK(delegate);
+  delegate_ = delegate;
+  impl_->InitAsPopup(delegate, with_controls, with_osr, popupFeatures,
+                     windowInfo, client, settings);
+}
+
+void RootWindowMac::Show(ShowMode mode) {
+  impl_->Show(mode);
+}
+
+void RootWindowMac::Hide() {
+  impl_->Hide();
+}
+
+void RootWindowMac::SetBounds(int x, int y, size_t width, size_t height) {
+  impl_->SetBounds(x, y, width, height);
+}
+
+void RootWindowMac::Close(bool force) {
+  impl_->Close(force);
+}
+
+void RootWindowMac::SetDeviceScaleFactor(float device_scale_factor) {
+  impl_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float RootWindowMac::GetDeviceScaleFactor() const {
+  return impl_->GetDeviceScaleFactor();
+}
+
+CefRefPtr<CefBrowser> RootWindowMac::GetBrowser() const {
+  return impl_->GetBrowser();
+}
+
+ClientWindowHandle RootWindowMac::GetWindowHandle() const {
+  return impl_->GetWindowHandle();
+}
+
+bool RootWindowMac::WithWindowlessRendering() const {
+  return impl_->WithWindowlessRendering();
+}
+
+bool RootWindowMac::WithExtension() const {
+  return impl_->WithExtension();
+}
+
+void RootWindowMac::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
+  impl_->OnBrowserCreated(browser);
+}
+
+void RootWindowMac::OnBrowserWindowDestroyed() {
+  impl_->OnBrowserWindowDestroyed();
+}
+
+void RootWindowMac::OnSetAddress(const std::string& url) {
+  impl_->OnSetAddress(url);
+}
+
+void RootWindowMac::OnSetTitle(const std::string& title) {
+  impl_->OnSetTitle(title);
+}
+
+void RootWindowMac::OnSetFullscreen(bool fullscreen) {
+  impl_->OnSetFullscreen(fullscreen);
+}
+
+void RootWindowMac::OnAutoResize(const CefSize& new_size) {
+  impl_->OnAutoResize(new_size);
+}
+
+void RootWindowMac::OnSetLoadingState(bool isLoading,
+                                      bool canGoBack,
+                                      bool canGoForward) {
+  impl_->OnSetLoadingState(isLoading, canGoBack, canGoForward);
+}
+
+void RootWindowMac::OnSetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  impl_->OnSetDraggableRegions(regions);
+}
+
+void RootWindowMac::OnNativeWindowClosed() {
+  impl_->OnNativeWindowClosed();
+}
+
+// static
+scoped_refptr<RootWindow> RootWindow::GetForNSWindow(NSWindow* window) {
+  RootWindowDelegate* delegate =
+      static_cast<RootWindowDelegate*>([window delegate]);
+  return [delegate root_window];
+}
+
+}  // namespace client
+
+@implementation RootWindowDelegate
+
+@synthesize root_window = root_window_;
+@synthesize force_close = force_close_;
+
+- (id)initWithWindow:(NSWindow*)window
+       andRootWindow:(client::RootWindowMac*)root_window {
+  if (self = [super init]) {
+    window_ = window;
+    [window_ setDelegate:self];
+    root_window_ = root_window;
+    force_close_ = false;
+
+    // Register for application hide/unhide notifications.
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(applicationDidHide:)
+               name:NSApplicationDidHideNotification
+             object:nil];
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(applicationDidUnhide:)
+               name:NSApplicationDidUnhideNotification
+             object:nil];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+#if !__has_feature(objc_arc)
+  [super dealloc];
+#endif  // !__has_feature(objc_arc)
+}
+
+- (IBAction)goBack:(id)sender {
+  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
+  if (browser.get())
+    browser->GoBack();
+}
+
+- (IBAction)goForward:(id)sender {
+  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
+  if (browser.get())
+    browser->GoForward();
+}
+
+- (IBAction)reload:(id)sender {
+  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
+  if (browser.get())
+    browser->Reload();
+}
+
+- (IBAction)stopLoading:(id)sender {
+  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
+  if (browser.get())
+    browser->StopLoad();
+}
+
+- (IBAction)takeURLStringValueFrom:(NSTextField*)sender {
+  CefRefPtr<CefBrowser> browser = root_window_->GetBrowser();
+  if (!browser.get())
+    return;
+
+  NSString* url = [sender stringValue];
+
+  // if it doesn't already have a prefix, add http. If we can't parse it,
+  // just don't bother rather than making things worse.
+  NSURL* tempUrl = [NSURL URLWithString:url];
+  if (tempUrl && ![tempUrl scheme])
+    url = [@"http://" stringByAppendingString:url];
+
+  std::string urlStr = [url UTF8String];
+  browser->GetMainFrame()->LoadURL(urlStr);
+}
+
+// Called when we are activated (when we gain focus).
+- (void)windowDidBecomeKey:(NSNotification*)notification {
+  client::BrowserWindow* browser_window = root_window_->browser_window();
+  if (browser_window)
+    browser_window->SetFocus(true);
+  root_window_->delegate()->OnRootWindowActivated(root_window_);
+}
+
+// Called when we are deactivated (when we lose focus).
+- (void)windowDidResignKey:(NSNotification*)notification {
+  client::BrowserWindow* browser_window = root_window_->browser_window();
+  if (browser_window)
+    browser_window->SetFocus(false);
+}
+
+// Called when we have been minimized.
+- (void)windowDidMiniaturize:(NSNotification*)notification {
+  client::BrowserWindow* browser_window = root_window_->browser_window();
+  if (browser_window)
+    browser_window->Hide();
+}
+
+// Called when we have been unminimized.
+- (void)windowDidDeminiaturize:(NSNotification*)notification {
+  client::BrowserWindow* browser_window = root_window_->browser_window();
+  if (browser_window)
+    browser_window->Show();
+}
+
+// Called when the application has been hidden.
+- (void)applicationDidHide:(NSNotification*)notification {
+  // If the window is miniaturized then nothing has really changed.
+  if (![window_ isMiniaturized]) {
+    client::BrowserWindow* browser_window = root_window_->browser_window();
+    if (browser_window)
+      browser_window->Hide();
+  }
+}
+
+// Called when the application has been unhidden.
+- (void)applicationDidUnhide:(NSNotification*)notification {
+  // If the window is miniaturized then nothing has really changed.
+  if (![window_ isMiniaturized]) {
+    client::BrowserWindow* browser_window = root_window_->browser_window();
+    if (browser_window)
+      browser_window->Show();
+  }
+}
+
+// Called when the window is about to close. Perform the self-destruction
+// sequence by getting rid of the window. By returning YES, we allow the window
+// to be removed from the screen.
+- (BOOL)windowShouldClose:(NSWindow*)window {
+  if (!force_close_) {
+    client::BrowserWindow* browser_window = root_window_->browser_window();
+    if (browser_window && !browser_window->IsClosing()) {
+      CefRefPtr<CefBrowser> browser = browser_window->GetBrowser();
+      if (browser.get()) {
+        // Notify the browser window that we would like to close it. This
+        // will result in a call to ClientHandler::DoClose() if the
+        // JavaScript 'onbeforeunload' event handler allows it.
+        browser->GetHost()->CloseBrowser(false);
+
+        // Cancel the close.
+        return NO;
+      }
+    }
+  }
+
+  // Don't want any more delegate callbacks after we destroy ourselves.
+  window_.delegate = nil;
+  // Delete the window.
+#if !__has_feature(objc_arc)
+  [window autorelease];
+#endif  // !__has_feature(objc_arc)
+  window_ = nil;
+
+  // Clean ourselves up after clearing the stack of anything that might have the
+  // window on it.
+  [self cleanup];
+
+  // Allow the close.
+  return YES;
+}
+
+// Deletes itself.
+- (void)cleanup {
+  // Don't want any more delegate callbacks after we destroy ourselves.
+  window_.delegate = nil;
+  window_.contentView = [[NSView alloc] initWithFrame:NSZeroRect];
+  // Delete the window.
+#if !__has_feature(objc_arc)
+  [window_ autorelease];
+#endif  // !__has_feature(objc_arc)
+  window_ = nil;
+  root_window_->OnNativeWindowClosed();
+}
+
+@end  // @implementation RootWindowDelegate
diff --git a/src/tests/cefclient/browser/root_window_manager.cc b/src/tests/cefclient/browser/root_window_manager.cc
new file mode 100644
index 0000000..f0ad7a5
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_manager.cc
@@ -0,0 +1,459 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/root_window_manager.h"
+
+#include <sstream>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/browser/extension_util.h"
+#include "tests/shared/browser/file_util.h"
+#include "tests/shared/browser/resource_util.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+
+namespace {
+
+class ClientRequestContextHandler : public CefRequestContextHandler,
+                                    public CefExtensionHandler {
+ public:
+  ClientRequestContextHandler() {}
+
+  // CefRequestContextHandler methods:
+  bool OnBeforePluginLoad(const CefString& mime_type,
+                          const CefString& plugin_url,
+                          bool is_main_frame,
+                          const CefString& top_origin_url,
+                          CefRefPtr<CefWebPluginInfo> plugin_info,
+                          PluginPolicy* plugin_policy) OVERRIDE {
+    // Always allow the PDF plugin to load.
+    if (*plugin_policy != PLUGIN_POLICY_ALLOW &&
+        mime_type == "application/pdf") {
+      *plugin_policy = PLUGIN_POLICY_ALLOW;
+      return true;
+    }
+
+    return false;
+  }
+
+  void OnRequestContextInitialized(
+      CefRefPtr<CefRequestContext> request_context) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    CefRefPtr<CefCommandLine> command_line =
+        CefCommandLine::GetGlobalCommandLine();
+    if (command_line->HasSwitch(switches::kLoadExtension)) {
+      if (MainContext::Get()
+              ->GetRootWindowManager()
+              ->request_context_per_browser()) {
+        // The example extension loading implementation requires all browsers to
+        // share the same request context.
+        LOG(ERROR)
+            << "Cannot mix --load-extension and --request-context-per-browser";
+        return;
+      }
+
+      // Load one or more extension paths specified on the command-line and
+      // delimited with semicolon.
+      const std::string& extension_path =
+          command_line->GetSwitchValue(switches::kLoadExtension);
+      if (!extension_path.empty()) {
+        std::string part;
+        std::istringstream f(extension_path);
+        while (getline(f, part, ';')) {
+          if (!part.empty())
+            extension_util::LoadExtension(request_context, part, this);
+        }
+      }
+    }
+  }
+
+  // CefExtensionHandler methods:
+  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    MainContext::Get()->GetRootWindowManager()->AddExtension(extension);
+  }
+
+  CefRefPtr<CefBrowser> GetActiveBrowser(CefRefPtr<CefExtension> extension,
+                                         CefRefPtr<CefBrowser> browser,
+                                         bool include_incognito) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    // Return the browser for the active/foreground window.
+    CefRefPtr<CefBrowser> active_browser =
+        MainContext::Get()->GetRootWindowManager()->GetActiveBrowser();
+    if (!active_browser) {
+      LOG(WARNING)
+          << "No active browser available for extension "
+          << browser->GetHost()->GetExtension()->GetIdentifier().ToString();
+    } else {
+      // The active browser should not be hosting an extension.
+      DCHECK(!active_browser->GetHost()->GetExtension());
+    }
+    return active_browser;
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(ClientRequestContextHandler);
+  DISALLOW_COPY_AND_ASSIGN(ClientRequestContextHandler);
+};
+
+}  // namespace
+
+RootWindowManager::RootWindowManager(bool terminate_when_all_windows_closed)
+    : terminate_when_all_windows_closed_(terminate_when_all_windows_closed) {
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+  DCHECK(command_line.get());
+  request_context_per_browser_ =
+      command_line->HasSwitch(switches::kRequestContextPerBrowser);
+  request_context_shared_cache_ =
+      command_line->HasSwitch(switches::kRequestContextSharedCache);
+}
+
+RootWindowManager::~RootWindowManager() {
+  // All root windows should already have been destroyed.
+  DCHECK(root_windows_.empty());
+}
+
+scoped_refptr<RootWindow> RootWindowManager::CreateRootWindow(
+    const RootWindowConfig& config) {
+  CefBrowserSettings settings;
+  MainContext::Get()->PopulateBrowserSettings(&settings);
+
+  scoped_refptr<RootWindow> root_window =
+      RootWindow::Create(MainContext::Get()->UseViews());
+  root_window->Init(this, config, settings);
+
+  // Store a reference to the root window on the main thread.
+  OnRootWindowCreated(root_window);
+
+  return root_window;
+}
+
+scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsPopup(
+    bool with_controls,
+    bool with_osr,
+    const CefPopupFeatures& popupFeatures,
+    CefWindowInfo& windowInfo,
+    CefRefPtr<CefClient>& client,
+    CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!temp_window_) {
+    // TempWindow must be created on the UI thread.
+    temp_window_.reset(new TempWindow());
+  }
+
+  MainContext::Get()->PopulateBrowserSettings(&settings);
+
+  scoped_refptr<RootWindow> root_window =
+      RootWindow::Create(MainContext::Get()->UseViews());
+  root_window->InitAsPopup(this, with_controls, with_osr, popupFeatures,
+                           windowInfo, client, settings);
+
+  // Store a reference to the root window on the main thread.
+  OnRootWindowCreated(root_window);
+
+  return root_window;
+}
+
+scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsExtension(
+    CefRefPtr<CefExtension> extension,
+    const CefRect& source_bounds,
+    CefRefPtr<CefWindow> parent_window,
+    const base::Closure& close_callback,
+    bool with_controls,
+    bool with_osr) {
+  const std::string& extension_url = extension_util::GetExtensionURL(extension);
+  if (extension_url.empty()) {
+    NOTREACHED() << "Extension cannot be loaded directly.";
+    return nullptr;
+  }
+
+  // Create an initially hidden browser window that loads the extension URL.
+  // We'll show the window when the desired size becomes available via
+  // ClientHandler::OnAutoResize.
+  RootWindowConfig config;
+  config.with_controls = with_controls;
+  config.with_osr = with_osr;
+  config.with_extension = true;
+  config.initially_hidden = true;
+  config.source_bounds = source_bounds;
+  config.parent_window = parent_window;
+  config.close_callback = close_callback;
+  config.url = extension_url;
+  return CreateRootWindow(config);
+}
+
+bool RootWindowManager::HasRootWindowAsExtension(
+    CefRefPtr<CefExtension> extension) {
+  REQUIRE_MAIN_THREAD();
+
+  RootWindowSet::const_iterator it = root_windows_.begin();
+  for (; it != root_windows_.end(); ++it) {
+    const RootWindow* root_window = (*it);
+    if (!root_window->WithExtension())
+      continue;
+
+    CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
+    if (!browser)
+      continue;
+
+    CefRefPtr<CefExtension> browser_extension =
+        browser->GetHost()->GetExtension();
+    DCHECK(browser_extension);
+    if (browser_extension->GetIdentifier() == extension->GetIdentifier())
+      return true;
+  }
+
+  return false;
+}
+
+scoped_refptr<RootWindow> RootWindowManager::GetWindowForBrowser(
+    int browser_id) const {
+  REQUIRE_MAIN_THREAD();
+
+  RootWindowSet::const_iterator it = root_windows_.begin();
+  for (; it != root_windows_.end(); ++it) {
+    CefRefPtr<CefBrowser> browser = (*it)->GetBrowser();
+    if (browser.get() && browser->GetIdentifier() == browser_id)
+      return *it;
+  }
+  return nullptr;
+}
+
+scoped_refptr<RootWindow> RootWindowManager::GetActiveRootWindow() const {
+  REQUIRE_MAIN_THREAD();
+  return active_root_window_;
+}
+
+CefRefPtr<CefBrowser> RootWindowManager::GetActiveBrowser() const {
+  base::AutoLock lock_scope(active_browser_lock_);
+  return active_browser_;
+}
+
+void RootWindowManager::CloseAllWindows(bool force) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::CloseAllWindows,
+                                 base::Unretained(this), force));
+    return;
+  }
+
+  if (root_windows_.empty())
+    return;
+
+  // Use a copy of |root_windows_| because the original set may be modified
+  // in OnRootWindowDestroyed while iterating.
+  RootWindowSet root_windows = root_windows_;
+
+  RootWindowSet::const_iterator it = root_windows.begin();
+  for (; it != root_windows.end(); ++it)
+    (*it)->Close(force);
+}
+
+void RootWindowManager::AddExtension(CefRefPtr<CefExtension> extension) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::AddExtension,
+                                 base::Unretained(this), extension));
+    return;
+  }
+
+  // Don't track extensions that can't be loaded directly.
+  if (extension_util::GetExtensionURL(extension).empty())
+    return;
+
+  // Don't add the same extension multiple times.
+  ExtensionSet::const_iterator it = extensions_.begin();
+  for (; it != extensions_.end(); ++it) {
+    if ((*it)->GetIdentifier() == extension->GetIdentifier())
+      return;
+  }
+
+  extensions_.insert(extension);
+  NotifyExtensionsChanged();
+}
+
+void RootWindowManager::OnRootWindowCreated(
+    scoped_refptr<RootWindow> root_window) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::OnRootWindowCreated,
+                                 base::Unretained(this), root_window));
+    return;
+  }
+
+  root_windows_.insert(root_window);
+  if (!root_window->WithExtension()) {
+    root_window->OnExtensionsChanged(extensions_);
+
+    if (root_windows_.size() == 1U) {
+      // The first non-extension root window should be considered the active
+      // window.
+      OnRootWindowActivated(root_window);
+    }
+  }
+}
+
+void RootWindowManager::NotifyExtensionsChanged() {
+  REQUIRE_MAIN_THREAD();
+
+  RootWindowSet::const_iterator it = root_windows_.begin();
+  for (; it != root_windows_.end(); ++it) {
+    RootWindow* root_window = *it;
+    if (!root_window->WithExtension())
+      root_window->OnExtensionsChanged(extensions_);
+  }
+}
+
+CefRefPtr<CefRequestContext> RootWindowManager::GetRequestContext(
+    RootWindow* root_window) {
+  REQUIRE_MAIN_THREAD();
+
+  if (request_context_per_browser_) {
+    // Create a new request context for each browser.
+    CefRequestContextSettings settings;
+
+    CefRefPtr<CefCommandLine> command_line =
+        CefCommandLine::GetGlobalCommandLine();
+    if (command_line->HasSwitch(switches::kCachePath)) {
+      if (request_context_shared_cache_) {
+        // Give each browser the same cache path. The resulting context objects
+        // will share the same storage internally.
+        CefString(&settings.cache_path) =
+            command_line->GetSwitchValue(switches::kCachePath);
+      } else {
+        // Give each browser a unique cache path. This will create completely
+        // isolated context objects.
+        std::stringstream ss;
+        ss << command_line->GetSwitchValue(switches::kCachePath).ToString()
+           << file_util::kPathSep << time(NULL);
+        CefString(&settings.cache_path) = ss.str();
+      }
+    }
+
+    return CefRequestContext::CreateContext(settings,
+                                            new ClientRequestContextHandler);
+  }
+
+  // All browsers will share the global request context.
+  if (!shared_request_context_.get()) {
+    shared_request_context_ = CefRequestContext::CreateContext(
+        CefRequestContext::GetGlobalContext(), new ClientRequestContextHandler);
+  }
+  return shared_request_context_;
+}
+
+scoped_refptr<ImageCache> RootWindowManager::GetImageCache() {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!image_cache_) {
+    image_cache_ = new ImageCache;
+  }
+  return image_cache_;
+}
+
+void RootWindowManager::OnTest(RootWindow* root_window, int test_id) {
+  REQUIRE_MAIN_THREAD();
+
+  test_runner::RunTest(root_window->GetBrowser(), test_id);
+}
+
+void RootWindowManager::OnExit(RootWindow* root_window) {
+  REQUIRE_MAIN_THREAD();
+
+  CloseAllWindows(false);
+}
+
+void RootWindowManager::OnRootWindowDestroyed(RootWindow* root_window) {
+  REQUIRE_MAIN_THREAD();
+
+  RootWindowSet::iterator it = root_windows_.find(root_window);
+  DCHECK(it != root_windows_.end());
+  if (it != root_windows_.end())
+    root_windows_.erase(it);
+
+  if (root_window == active_root_window_) {
+    active_root_window_ = nullptr;
+
+    base::AutoLock lock_scope(active_browser_lock_);
+    active_browser_ = nullptr;
+  }
+
+  if (terminate_when_all_windows_closed_ && root_windows_.empty()) {
+    // All windows have closed. Clean up on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowManager::CleanupOnUIThread,
+                                   base::Unretained(this)));
+  }
+}
+
+void RootWindowManager::OnRootWindowActivated(RootWindow* root_window) {
+  REQUIRE_MAIN_THREAD();
+
+  if (root_window->WithExtension()) {
+    // We don't want extension apps to become the active RootWindow.
+    return;
+  }
+
+  if (root_window == active_root_window_)
+    return;
+
+  active_root_window_ = root_window;
+
+  {
+    base::AutoLock lock_scope(active_browser_lock_);
+    // May be NULL at this point, in which case we'll make the association in
+    // OnBrowserCreated.
+    active_browser_ = active_root_window_->GetBrowser();
+  }
+}
+
+void RootWindowManager::OnBrowserCreated(RootWindow* root_window,
+                                         CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+
+  if (root_window == active_root_window_) {
+    base::AutoLock lock_scope(active_browser_lock_);
+    active_browser_ = browser;
+  }
+}
+
+void RootWindowManager::CreateExtensionWindow(
+    CefRefPtr<CefExtension> extension,
+    const CefRect& source_bounds,
+    CefRefPtr<CefWindow> parent_window,
+    const base::Closure& close_callback,
+    bool with_osr) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!HasRootWindowAsExtension(extension)) {
+    CreateRootWindowAsExtension(extension, source_bounds, parent_window,
+                                close_callback, false, with_osr);
+  }
+}
+
+void RootWindowManager::CleanupOnUIThread() {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (temp_window_) {
+    // TempWindow must be destroyed on the UI thread.
+    temp_window_.reset(nullptr);
+  }
+
+  if (image_cache_) {
+    image_cache_ = nullptr;
+  }
+
+  // Quit the main message loop.
+  MainMessageLoop::Get()->Quit();
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/root_window_manager.h b/src/tests/cefclient/browser/root_window_manager.h
new file mode 100644
index 0000000..18cf8dd
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_manager.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_MANAGER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_MANAGER_H_
+#pragma once
+
+#include <set>
+
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_command_line.h"
+#include "include/cef_request_context_handler.h"
+#include "tests/cefclient/browser/image_cache.h"
+#include "tests/cefclient/browser/root_window.h"
+#include "tests/cefclient/browser/temp_window.h"
+
+namespace client {
+
+// Used to create/manage RootWindow instances. The methods of this class can be
+// called from any browser process thread unless otherwise indicated.
+class RootWindowManager : public RootWindow::Delegate {
+ public:
+  // If |terminate_when_all_windows_closed| is true quit the main message loop
+  // after all windows have closed.
+  explicit RootWindowManager(bool terminate_when_all_windows_closed);
+
+  // Create a new top-level native window. This method can be called from
+  // anywhere.
+  scoped_refptr<RootWindow> CreateRootWindow(const RootWindowConfig& config);
+
+  // Create a new native popup window.
+  // If |with_controls| is true the window will show controls.
+  // If |with_osr| is true the window will use off-screen rendering.
+  // This method is called from ClientHandler::CreatePopupWindow() to
+  // create a new popup or DevTools window. Must be called on the UI thread.
+  scoped_refptr<RootWindow> CreateRootWindowAsPopup(
+      bool with_controls,
+      bool with_osr,
+      const CefPopupFeatures& popupFeatures,
+      CefWindowInfo& windowInfo,
+      CefRefPtr<CefClient>& client,
+      CefBrowserSettings& settings);
+
+  // Create a new top-level native window to host |extension|.
+  // If |with_controls| is true the window will show controls.
+  // If |with_osr| is true the window will use off-screen rendering.
+  // This method can be called from anywhere.
+  scoped_refptr<RootWindow> CreateRootWindowAsExtension(
+      CefRefPtr<CefExtension> extension,
+      const CefRect& source_bounds,
+      CefRefPtr<CefWindow> parent_window,
+      const base::Closure& close_callback,
+      bool with_controls,
+      bool with_osr);
+
+  // Returns true if a window hosting |extension| currently exists. Must be
+  // called on the main thread.
+  bool HasRootWindowAsExtension(CefRefPtr<CefExtension> extension);
+
+  // Returns the RootWindow associated with the specified browser ID. Must be
+  // called on the main thread.
+  scoped_refptr<RootWindow> GetWindowForBrowser(int browser_id) const;
+
+  // Returns the currently active/foreground RootWindow. May return NULL. Must
+  // be called on the main thread.
+  scoped_refptr<RootWindow> GetActiveRootWindow() const;
+
+  // Returns the currently active/foreground browser. May return NULL. Safe to
+  // call from any thread.
+  CefRefPtr<CefBrowser> GetActiveBrowser() const;
+
+  // Close all existing windows. If |force| is true onunload handlers will not
+  // be executed.
+  void CloseAllWindows(bool force);
+
+  // Manage the set of loaded extensions. RootWindows will be notified via the
+  // OnExtensionsChanged method.
+  void AddExtension(CefRefPtr<CefExtension> extension);
+
+  bool request_context_per_browser() const {
+    return request_context_per_browser_;
+  }
+
+ private:
+  // Allow deletion via scoped_ptr only.
+  friend struct base::DefaultDeleter<RootWindowManager>;
+
+  ~RootWindowManager();
+
+  void OnRootWindowCreated(scoped_refptr<RootWindow> root_window);
+  void NotifyExtensionsChanged();
+
+  // RootWindow::Delegate methods.
+  CefRefPtr<CefRequestContext> GetRequestContext(
+      RootWindow* root_window) OVERRIDE;
+  scoped_refptr<ImageCache> GetImageCache() OVERRIDE;
+  void OnTest(RootWindow* root_window, int test_id) OVERRIDE;
+  void OnExit(RootWindow* root_window) OVERRIDE;
+  void OnRootWindowDestroyed(RootWindow* root_window) OVERRIDE;
+  void OnRootWindowActivated(RootWindow* root_window) OVERRIDE;
+  void OnBrowserCreated(RootWindow* root_window,
+                        CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
+                             const CefRect& source_bounds,
+                             CefRefPtr<CefWindow> parent_window,
+                             const base::Closure& close_callback,
+                             bool with_osr) OVERRIDE;
+
+  void CleanupOnUIThread();
+
+  const bool terminate_when_all_windows_closed_;
+  bool request_context_per_browser_;
+  bool request_context_shared_cache_;
+
+  // Existing root windows. Only accessed on the main thread.
+  typedef std::set<scoped_refptr<RootWindow>> RootWindowSet;
+  RootWindowSet root_windows_;
+
+  // The currently active/foreground RootWindow. Only accessed on the main
+  // thread.
+  scoped_refptr<RootWindow> active_root_window_;
+
+  // The currently active/foreground browser. Access is protected by
+  // |active_browser_lock_;
+  mutable base::Lock active_browser_lock_;
+  CefRefPtr<CefBrowser> active_browser_;
+
+  // Singleton window used as the temporary parent for popup browsers.
+  scoped_ptr<TempWindow> temp_window_;
+
+  CefRefPtr<CefRequestContext> shared_request_context_;
+
+  // Loaded extensions. Only accessed on the main thread.
+  ExtensionSet extensions_;
+
+  scoped_refptr<ImageCache> image_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowManager);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_MANAGER_H_
diff --git a/src/tests/cefclient/browser/root_window_views.cc b/src/tests/cefclient/browser/root_window_views.cc
new file mode 100644
index 0000000..0165a9c
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_views.cc
@@ -0,0 +1,542 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/root_window_views.h"
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_build.h"
+#include "include/cef_app.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/client_handler_std.h"
+
+namespace client {
+
+namespace {
+
+// Images that are loaded early and cached.
+static const char* kDefaultImageCache[] = {"menu_icon", "window_icon"};
+
+}  // namespace
+
+RootWindowViews::RootWindowViews()
+    : with_controls_(false),
+      always_on_top_(false),
+      with_extension_(false),
+      initially_hidden_(false),
+      is_popup_(false),
+      position_on_resize_(false),
+      initialized_(false),
+      window_destroyed_(false),
+      browser_destroyed_(false) {}
+
+RootWindowViews::~RootWindowViews() {
+  REQUIRE_MAIN_THREAD();
+}
+
+void RootWindowViews::Init(RootWindow::Delegate* delegate,
+                           const RootWindowConfig& config,
+                           const CefBrowserSettings& settings) {
+  DCHECK(delegate);
+  DCHECK(!config.with_osr);  // Windowless rendering is not supported.
+  DCHECK(!initialized_);
+
+  delegate_ = delegate;
+  with_controls_ = config.with_controls;
+  always_on_top_ = config.always_on_top;
+  with_extension_ = config.with_extension;
+  initially_hidden_ = config.initially_hidden;
+  if (initially_hidden_ && !config.source_bounds.IsEmpty()) {
+    // The window will be sized and positioned in OnAutoResize().
+    initial_bounds_ = config.source_bounds;
+    position_on_resize_ = true;
+  } else {
+    initial_bounds_ = config.bounds;
+  }
+  parent_window_ = config.parent_window;
+  close_callback_ = config.close_callback;
+
+  CreateClientHandler(config.url);
+  initialized_ = true;
+
+  // Continue initialization on the UI thread.
+  InitOnUIThread(settings, config.url, delegate_->GetRequestContext(this));
+}
+
+void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate,
+                                  bool with_controls,
+                                  bool with_osr,
+                                  const CefPopupFeatures& popupFeatures,
+                                  CefWindowInfo& windowInfo,
+                                  CefRefPtr<CefClient>& client,
+                                  CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  DCHECK(delegate);
+  DCHECK(!with_osr);  // Windowless rendering is not supported.
+  DCHECK(!initialized_);
+
+  delegate_ = delegate;
+  with_controls_ = with_controls;
+  is_popup_ = true;
+
+  if (popupFeatures.xSet)
+    initial_bounds_.x = popupFeatures.x;
+  if (popupFeatures.ySet)
+    initial_bounds_.y = popupFeatures.y;
+  if (popupFeatures.widthSet)
+    initial_bounds_.width = popupFeatures.width;
+  if (popupFeatures.heightSet)
+    initial_bounds_.height = popupFeatures.height;
+
+  CreateClientHandler(std::string());
+  initialized_ = true;
+
+  // The Window will be created in ViewsWindow::OnPopupBrowserViewCreated().
+  client = client_handler_;
+}
+
+void RootWindowViews::Show(ShowMode mode) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::Show, this, mode));
+    return;
+  }
+
+  if (!window_)
+    return;
+
+  window_->Show();
+
+  switch (mode) {
+    case ShowMinimized:
+      window_->Minimize();
+      break;
+    case ShowMaximized:
+      window_->Maximize();
+      break;
+    default:
+      break;
+  }
+}
+
+void RootWindowViews::Hide() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::Hide, this));
+    return;
+  }
+
+  if (window_)
+    window_->Hide();
+}
+
+void RootWindowViews::SetBounds(int x, int y, size_t width, size_t height) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::SetBounds, this, x, y,
+                                   width, height));
+    return;
+  }
+
+  if (window_) {
+    window_->SetBounds(
+        CefRect(x, y, static_cast<int>(width), static_cast<int>(height)));
+  }
+}
+
+void RootWindowViews::Close(bool force) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::Close, this, force));
+    return;
+  }
+
+  if (window_)
+    window_->Close(force);
+}
+
+void RootWindowViews::SetDeviceScaleFactor(float device_scale_factor) {
+  REQUIRE_MAIN_THREAD();
+  // Windowless rendering is not supported.
+  NOTREACHED();
+}
+
+float RootWindowViews::GetDeviceScaleFactor() const {
+  REQUIRE_MAIN_THREAD();
+  // Windowless rendering is not supported.
+  NOTREACHED();
+  return 0.0;
+}
+
+CefRefPtr<CefBrowser> RootWindowViews::GetBrowser() const {
+  REQUIRE_MAIN_THREAD();
+  return browser_;
+}
+
+ClientWindowHandle RootWindowViews::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+#if defined(OS_LINUX)
+  // ClientWindowHandle is a GtkWidget* on Linux and we don't have one of those.
+  return NULL;
+#else
+  if (browser_)
+    return browser_->GetHost()->GetWindowHandle();
+  return kNullWindowHandle;
+#endif
+}
+
+bool RootWindowViews::WithExtension() const {
+  DCHECK(initialized_);
+  return with_extension_;
+}
+
+bool RootWindowViews::WithControls() {
+  DCHECK(initialized_);
+  return with_controls_;
+}
+
+bool RootWindowViews::WithExtension() {
+  DCHECK(initialized_);
+  return with_extension_;
+}
+
+void RootWindowViews::OnExtensionsChanged(const ExtensionSet& extensions) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnExtensionsChanged, this,
+                                   extensions));
+    return;
+  }
+
+  if (window_) {
+    window_->OnExtensionsChanged(extensions);
+  } else {
+    // Window may not exist yet for popups.
+    pending_extensions_ = extensions;
+  }
+}
+
+bool RootWindowViews::InitiallyHidden() {
+  CEF_REQUIRE_UI_THREAD();
+  return initially_hidden_;
+}
+
+CefRefPtr<CefWindow> RootWindowViews::GetParentWindow() {
+  CEF_REQUIRE_UI_THREAD();
+  return parent_window_;
+}
+
+CefRect RootWindowViews::GetWindowBounds() {
+  CEF_REQUIRE_UI_THREAD();
+  return initial_bounds_;
+}
+
+scoped_refptr<ImageCache> RootWindowViews::GetImageCache() {
+  CEF_REQUIRE_UI_THREAD();
+  return image_cache_;
+}
+
+void RootWindowViews::OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(!window_);
+  window_ = window;
+  window_->SetAlwaysOnTop(always_on_top_);
+
+  if (!pending_extensions_.empty()) {
+    window_->OnExtensionsChanged(pending_extensions_);
+    pending_extensions_.clear();
+  }
+}
+
+void RootWindowViews::OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+  window_ = nullptr;
+
+  // Continue on the main thread.
+  MAIN_POST_CLOSURE(
+      base::Bind(&RootWindowViews::NotifyViewsWindowDestroyed, this));
+}
+
+void RootWindowViews::OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Continue on the main thread.
+  MAIN_POST_CLOSURE(
+      base::Bind(&RootWindowViews::NotifyViewsWindowActivated, this));
+}
+
+ViewsWindow::Delegate* RootWindowViews::GetDelegateForPopup(
+    CefRefPtr<CefClient> client) {
+  CEF_REQUIRE_UI_THREAD();
+  // |handler| was created in RootWindowViews::InitAsPopup().
+  ClientHandlerStd* handler = static_cast<ClientHandlerStd*>(client.get());
+  RootWindowViews* root_window =
+      static_cast<RootWindowViews*>(handler->delegate());
+
+  // Transfer some state to the child RootWindowViews.
+  root_window->image_cache_ = image_cache_;
+
+  return root_window;
+}
+
+void RootWindowViews::CreateExtensionWindow(
+    CefRefPtr<CefExtension> extension,
+    const CefRect& source_bounds,
+    CefRefPtr<CefWindow> parent_window,
+    const base::Closure& close_callback) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowViews::CreateExtensionWindow, this,
+                                 extension, source_bounds, parent_window,
+                                 close_callback));
+    return;
+  }
+
+  delegate_->CreateExtensionWindow(extension, source_bounds, parent_window,
+                                   close_callback, false);
+}
+
+void RootWindowViews::OnTest(int test_id) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowViews::OnTest, this, test_id));
+    return;
+  }
+
+  delegate_->OnTest(this, test_id);
+}
+
+void RootWindowViews::OnExit() {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowViews::OnExit, this));
+    return;
+  }
+
+  delegate_->OnExit(this);
+}
+
+void RootWindowViews::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!browser_);
+  browser_ = browser;
+  delegate_->OnBrowserCreated(this, browser);
+}
+
+void RootWindowViews::OnBrowserClosing(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  // Nothing to do here.
+}
+
+void RootWindowViews::OnBrowserClosed(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  if (browser_) {
+    DCHECK_EQ(browser->GetIdentifier(), browser_->GetIdentifier());
+    browser_ = nullptr;
+  }
+
+  client_handler_->DetachDelegate();
+  client_handler_ = nullptr;
+
+  browser_destroyed_ = true;
+  NotifyDestroyedIfDone();
+}
+
+void RootWindowViews::OnSetAddress(const std::string& url) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetAddress, this, url));
+    return;
+  }
+
+  if (window_ && with_controls_)
+    window_->SetAddress(url);
+}
+
+void RootWindowViews::OnSetTitle(const std::string& title) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetTitle, this, title));
+    return;
+  }
+
+  if (window_)
+    window_->SetTitle(title);
+}
+
+void RootWindowViews::OnSetFavicon(CefRefPtr<CefImage> image) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI,
+                base::Bind(&RootWindowViews::OnSetFavicon, this, image));
+    return;
+  }
+
+  if (window_)
+    window_->SetFavicon(image);
+}
+
+void RootWindowViews::OnSetFullscreen(bool fullscreen) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetFullscreen, this,
+                                   fullscreen));
+    return;
+  }
+
+  if (window_)
+    window_->SetFullscreen(fullscreen);
+}
+
+void RootWindowViews::OnAutoResize(const CefSize& new_size) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI,
+                base::Bind(&RootWindowViews::OnAutoResize, this, new_size));
+    return;
+  }
+
+  bool has_position = false;
+  CefPoint position;
+  if (position_on_resize_) {
+    // Position the window centered on and immediately below the source.
+    const int x_offset = (initial_bounds_.width - new_size.width) / 2;
+    position.Set(initial_bounds_.x + x_offset,
+                 initial_bounds_.y + initial_bounds_.height);
+    has_position = true;
+
+    // Don't change the window position on future resizes.
+    position_on_resize_ = false;
+  }
+
+  if (window_) {
+    window_->SetBrowserSize(new_size, has_position, position);
+    window_->Show();
+  }
+}
+
+void RootWindowViews::OnSetLoadingState(bool isLoading,
+                                        bool canGoBack,
+                                        bool canGoForward) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetLoadingState, this,
+                                   isLoading, canGoBack, canGoForward));
+    return;
+  }
+
+  if (window_) {
+    if (with_controls_)
+      window_->SetLoadingState(isLoading, canGoBack, canGoForward);
+
+    if (isLoading) {
+      // Reset to the default window icon when loading begins.
+      window_->SetFavicon(
+          delegate_->GetImageCache()->GetCachedImage("window_icon"));
+    }
+  }
+}
+
+void RootWindowViews::OnSetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetDraggableRegions,
+                                   this, regions));
+    return;
+  }
+
+  if (window_)
+    window_->SetDraggableRegions(regions);
+}
+
+void RootWindowViews::OnTakeFocus(bool next) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnTakeFocus, this, next));
+    return;
+  }
+
+  if (window_)
+    window_->TakeFocus(next);
+}
+
+void RootWindowViews::OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_)
+    window_->OnBeforeContextMenu(model);
+}
+
+void RootWindowViews::CreateClientHandler(const std::string& url) {
+  DCHECK(!client_handler_);
+
+  client_handler_ = new ClientHandlerStd(this, url);
+  client_handler_->set_download_favicon_images(true);
+}
+
+void RootWindowViews::InitOnUIThread(
+    const CefBrowserSettings& settings,
+    const std::string& startup_url,
+    CefRefPtr<CefRequestContext> request_context) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&RootWindowViews::InitOnUIThread, this,
+                                   settings, startup_url, request_context));
+    return;
+  }
+
+  image_cache_ = delegate_->GetImageCache();
+
+  // Populate the default image cache.
+  ImageCache::ImageInfoSet image_set;
+  for (size_t i = 0U; i < arraysize(kDefaultImageCache); ++i)
+    image_set.push_back(ImageCache::ImageInfo::Create2x(kDefaultImageCache[i]));
+
+  image_cache_->LoadImages(
+      image_set, base::Bind(&RootWindowViews::CreateViewsWindow, this, settings,
+                            startup_url, request_context));
+}
+
+void RootWindowViews::CreateViewsWindow(
+    const CefBrowserSettings& settings,
+    const std::string& startup_url,
+    CefRefPtr<CefRequestContext> request_context,
+    const ImageCache::ImageSet& images) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(!window_);
+
+#ifndef NDEBUG
+  // Make sure the default images loaded successfully.
+  DCHECK_EQ(images.size(), arraysize(kDefaultImageCache));
+  for (size_t i = 0U; i < arraysize(kDefaultImageCache); ++i) {
+    DCHECK(images[i]) << "Default image " << i << " failed to load";
+  }
+#endif
+
+  // Create the ViewsWindow. It will show itself after creation.
+  ViewsWindow::Create(this, client_handler_, startup_url, settings,
+                      request_context);
+}
+
+void RootWindowViews::NotifyViewsWindowDestroyed() {
+  REQUIRE_MAIN_THREAD();
+  window_destroyed_ = true;
+  NotifyDestroyedIfDone();
+}
+
+void RootWindowViews::NotifyViewsWindowActivated() {
+  REQUIRE_MAIN_THREAD();
+  delegate_->OnRootWindowActivated(this);
+}
+
+void RootWindowViews::NotifyDestroyedIfDone() {
+  // Notify once both the window and the browser have been destroyed.
+  if (window_destroyed_ && browser_destroyed_) {
+    delegate_->OnRootWindowDestroyed(this);
+    if (!close_callback_.is_null())
+      close_callback_.Run();
+  }
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/root_window_views.h b/src/tests/cefclient/browser/root_window_views.h
new file mode 100644
index 0000000..1c719c4
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_views.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_VIEWS_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_VIEWS_H_
+#pragma once
+
+#include <string>
+
+#include "include/base/cef_scoped_ptr.h"
+#include "tests/cefclient/browser/client_handler.h"
+#include "tests/cefclient/browser/root_window.h"
+#include "tests/cefclient/browser/views_window.h"
+
+namespace client {
+
+// Views framework implementation of a top-level window in the browser process.
+// The methods of this class must be called on the main thread unless otherwise
+// indicated.
+class RootWindowViews : public RootWindow,
+                        public ClientHandler::Delegate,
+                        public ViewsWindow::Delegate {
+ public:
+  // Constructor may be called on any thread.
+  RootWindowViews();
+  ~RootWindowViews();
+
+  // RootWindow methods:
+  void Init(RootWindow::Delegate* delegate,
+            const RootWindowConfig& config,
+            const CefBrowserSettings& settings) OVERRIDE;
+  void InitAsPopup(RootWindow::Delegate* delegate,
+                   bool with_controls,
+                   bool with_osr,
+                   const CefPopupFeatures& popupFeatures,
+                   CefWindowInfo& windowInfo,
+                   CefRefPtr<CefClient>& client,
+                   CefBrowserSettings& settings) OVERRIDE;
+  void Show(ShowMode mode) OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void Close(bool force) OVERRIDE;
+  void SetDeviceScaleFactor(float device_scale_factor) OVERRIDE;
+  float GetDeviceScaleFactor() const OVERRIDE;
+  CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+  bool WithWindowlessRendering() const OVERRIDE { return false; }
+  bool WithExtension() const OVERRIDE;
+  void OnExtensionsChanged(const ExtensionSet& extensions) OVERRIDE;
+
+  // ViewsWindow::Delegate methods:
+  bool WithControls() OVERRIDE;
+  bool WithExtension() OVERRIDE;
+  bool InitiallyHidden() OVERRIDE;
+  CefRefPtr<CefWindow> GetParentWindow() OVERRIDE;
+  CefRect GetWindowBounds() OVERRIDE;
+  scoped_refptr<ImageCache> GetImageCache() OVERRIDE;
+  void OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) OVERRIDE;
+  void OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) OVERRIDE;
+  void OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) OVERRIDE;
+  ViewsWindow::Delegate* GetDelegateForPopup(
+      CefRefPtr<CefClient> client) OVERRIDE;
+  void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
+                             const CefRect& source_bounds,
+                             CefRefPtr<CefWindow> parent_window,
+                             const base::Closure& close_callback) OVERRIDE;
+  void OnTest(int test_id) OVERRIDE;
+  void OnExit() OVERRIDE;
+
+ protected:
+  // ClientHandler::Delegate methods:
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBrowserClosing(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBrowserClosed(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnSetAddress(const std::string& url) OVERRIDE;
+  void OnSetTitle(const std::string& title) OVERRIDE;
+  void OnSetFavicon(CefRefPtr<CefImage> image) OVERRIDE;
+  void OnSetFullscreen(bool fullscreen) OVERRIDE;
+  void OnAutoResize(const CefSize& new_size) OVERRIDE;
+  void OnSetLoadingState(bool isLoading,
+                         bool canGoBack,
+                         bool canGoForward) OVERRIDE;
+  void OnSetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) OVERRIDE;
+  void OnTakeFocus(bool next) OVERRIDE;
+  void OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) OVERRIDE;
+
+ private:
+  void CreateClientHandler(const std::string& url);
+
+  void InitOnUIThread(const CefBrowserSettings& settings,
+                      const std::string& startup_url,
+                      CefRefPtr<CefRequestContext> request_context);
+  void CreateViewsWindow(const CefBrowserSettings& settings,
+                         const std::string& startup_url,
+                         CefRefPtr<CefRequestContext> request_context,
+                         const ImageCache::ImageSet& images);
+
+  void NotifyViewsWindowDestroyed();
+  void NotifyViewsWindowActivated();
+  void NotifyDestroyedIfDone();
+
+  // After initialization all members are only accessed on the main thread
+  // unless otherwise indicated.
+  // Members set during initialization.
+  bool with_controls_;
+  bool always_on_top_;
+  bool with_extension_;
+  bool initially_hidden_;
+  CefRefPtr<CefWindow> parent_window_;
+  bool is_popup_;
+  CefRect initial_bounds_;
+  base::Closure close_callback_;
+  bool position_on_resize_;
+  CefRefPtr<ClientHandler> client_handler_;
+
+  bool initialized_;
+  bool window_destroyed_;
+  bool browser_destroyed_;
+
+  CefRefPtr<CefBrowser> browser_;
+
+  // Only accessed on the browser process UI thread.
+  CefRefPtr<ViewsWindow> window_;
+  ExtensionSet pending_extensions_;
+  scoped_refptr<ImageCache> image_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowViews);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_VIEWS_H_
diff --git a/src/tests/cefclient/browser/root_window_win.cc b/src/tests/cefclient/browser/root_window_win.cc
new file mode 100644
index 0000000..dd208db
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_win.cc
@@ -0,0 +1,1213 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/root_window_win.h"
+
+#include <shellscalingapi.h>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_build.h"
+#include "include/cef_app.h"
+#include "tests/cefclient/browser/browser_window_osr_win.h"
+#include "tests/cefclient/browser/browser_window_std_win.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/cefclient/browser/temp_window.h"
+#include "tests/cefclient/browser/window_test_runner_win.h"
+#include "tests/shared/browser/geometry_util.h"
+#include "tests/shared/browser/main_message_loop.h"
+#include "tests/shared/browser/util_win.h"
+#include "tests/shared/common/client_switches.h"
+
+#define MAX_URL_LENGTH 255
+#define BUTTON_WIDTH 72
+#define URLBAR_HEIGHT 24
+
+namespace client {
+
+namespace {
+
+// Message handler for the About box.
+INT_PTR CALLBACK AboutWndProc(HWND hDlg,
+                              UINT message,
+                              WPARAM wParam,
+                              LPARAM lParam) {
+  UNREFERENCED_PARAMETER(lParam);
+  switch (message) {
+    case WM_INITDIALOG:
+      return TRUE;
+
+    case WM_COMMAND:
+      if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
+        EndDialog(hDlg, LOWORD(wParam));
+        return TRUE;
+      }
+      break;
+  }
+  return FALSE;
+}
+
+// Returns true if the process is per monitor DPI aware.
+bool IsProcessPerMonitorDpiAware() {
+  enum class PerMonitorDpiAware {
+    UNKNOWN = 0,
+    PER_MONITOR_DPI_UNAWARE,
+    PER_MONITOR_DPI_AWARE,
+  };
+  static PerMonitorDpiAware per_monitor_dpi_aware = PerMonitorDpiAware::UNKNOWN;
+  if (per_monitor_dpi_aware == PerMonitorDpiAware::UNKNOWN) {
+    per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_UNAWARE;
+    HMODULE shcore_dll = ::LoadLibrary(L"shcore.dll");
+    if (shcore_dll) {
+      typedef HRESULT(WINAPI * GetProcessDpiAwarenessPtr)(
+          HANDLE, PROCESS_DPI_AWARENESS*);
+      GetProcessDpiAwarenessPtr func_ptr =
+          reinterpret_cast<GetProcessDpiAwarenessPtr>(
+              ::GetProcAddress(shcore_dll, "GetProcessDpiAwareness"));
+      if (func_ptr) {
+        PROCESS_DPI_AWARENESS awareness;
+        if (SUCCEEDED(func_ptr(nullptr, &awareness)) &&
+            awareness == PROCESS_PER_MONITOR_DPI_AWARE)
+          per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
+      }
+    }
+  }
+  return per_monitor_dpi_aware == PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
+}
+
+// DPI value for 1x scale factor.
+#define DPI_1X 96.0f
+
+float GetWindowScaleFactor(HWND hwnd) {
+  if (hwnd && IsProcessPerMonitorDpiAware()) {
+    typedef UINT(WINAPI * GetDpiForWindowPtr)(HWND);
+    static GetDpiForWindowPtr func_ptr = reinterpret_cast<GetDpiForWindowPtr>(
+        GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow"));
+    if (func_ptr)
+      return static_cast<float>(func_ptr(hwnd)) / DPI_1X;
+  }
+
+  return client::GetDeviceScaleFactor();
+}
+
+int GetButtonWidth(HWND hwnd) {
+  return LogicalToDevice(BUTTON_WIDTH, GetWindowScaleFactor(hwnd));
+}
+
+int GetURLBarHeight(HWND hwnd) {
+  return LogicalToDevice(URLBAR_HEIGHT, GetWindowScaleFactor(hwnd));
+}
+
+}  // namespace
+
+RootWindowWin::RootWindowWin()
+    : with_controls_(false),
+      always_on_top_(false),
+      with_osr_(false),
+      with_extension_(false),
+      is_popup_(false),
+      start_rect_(),
+      initialized_(false),
+      hwnd_(NULL),
+      draggable_region_(NULL),
+      font_(NULL),
+      font_height_(0),
+      back_hwnd_(NULL),
+      forward_hwnd_(NULL),
+      reload_hwnd_(NULL),
+      stop_hwnd_(NULL),
+      edit_hwnd_(NULL),
+      edit_wndproc_old_(NULL),
+      find_hwnd_(NULL),
+      find_message_id_(0),
+      find_wndproc_old_(NULL),
+      find_state_(),
+      find_next_(false),
+      find_match_case_last_(false),
+      window_destroyed_(false),
+      browser_destroyed_(false),
+      called_enable_non_client_dpi_scaling_(false) {
+  find_buff_[0] = 0;
+
+  // Create a HRGN representing the draggable window area.
+  draggable_region_ = ::CreateRectRgn(0, 0, 0, 0);
+}
+
+RootWindowWin::~RootWindowWin() {
+  REQUIRE_MAIN_THREAD();
+
+  ::DeleteObject(draggable_region_);
+  ::DeleteObject(font_);
+
+  // The window and browser should already have been destroyed.
+  DCHECK(window_destroyed_);
+  DCHECK(browser_destroyed_);
+}
+
+void RootWindowWin::Init(RootWindow::Delegate* delegate,
+                         const RootWindowConfig& config,
+                         const CefBrowserSettings& settings) {
+  DCHECK(delegate);
+  DCHECK(!initialized_);
+
+  delegate_ = delegate;
+  with_controls_ = config.with_controls;
+  always_on_top_ = config.always_on_top;
+  with_osr_ = config.with_osr;
+  with_extension_ = config.with_extension;
+
+  start_rect_.left = config.bounds.x;
+  start_rect_.top = config.bounds.y;
+  start_rect_.right = config.bounds.x + config.bounds.width;
+  start_rect_.bottom = config.bounds.y + config.bounds.height;
+
+  CreateBrowserWindow(config.url);
+
+  initialized_ = true;
+
+  // Create the native root window on the main thread.
+  if (CURRENTLY_ON_MAIN_THREAD()) {
+    CreateRootWindow(settings, config.initially_hidden);
+  } else {
+    MAIN_POST_CLOSURE(base::Bind(&RootWindowWin::CreateRootWindow, this,
+                                 settings, config.initially_hidden));
+  }
+}
+
+void RootWindowWin::InitAsPopup(RootWindow::Delegate* delegate,
+                                bool with_controls,
+                                bool with_osr,
+                                const CefPopupFeatures& popupFeatures,
+                                CefWindowInfo& windowInfo,
+                                CefRefPtr<CefClient>& client,
+                                CefBrowserSettings& settings) {
+  CEF_REQUIRE_UI_THREAD();
+
+  DCHECK(delegate);
+  DCHECK(!initialized_);
+
+  delegate_ = delegate;
+  with_controls_ = with_controls;
+  with_osr_ = with_osr;
+  is_popup_ = true;
+
+  if (popupFeatures.xSet)
+    start_rect_.left = popupFeatures.x;
+  if (popupFeatures.ySet)
+    start_rect_.top = popupFeatures.y;
+  if (popupFeatures.widthSet)
+    start_rect_.right = start_rect_.left + popupFeatures.width;
+  if (popupFeatures.heightSet)
+    start_rect_.bottom = start_rect_.top + popupFeatures.height;
+
+  CreateBrowserWindow(std::string());
+
+  initialized_ = true;
+
+  // The new popup is initially parented to a temporary window. The native root
+  // window will be created after the browser is created and the popup window
+  // will be re-parented to it at that time.
+  browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo,
+                                  client, settings);
+}
+
+void RootWindowWin::Show(ShowMode mode) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!hwnd_)
+    return;
+
+  int nCmdShow = SW_SHOWNORMAL;
+  switch (mode) {
+    case ShowMinimized:
+      nCmdShow = SW_SHOWMINIMIZED;
+      break;
+    case ShowMaximized:
+      nCmdShow = SW_SHOWMAXIMIZED;
+      break;
+    case ShowNoActivate:
+      nCmdShow = SW_SHOWNOACTIVATE;
+      break;
+    default:
+      break;
+  }
+
+  ShowWindow(hwnd_, nCmdShow);
+  UpdateWindow(hwnd_);
+}
+
+void RootWindowWin::Hide() {
+  REQUIRE_MAIN_THREAD();
+
+  if (hwnd_)
+    ShowWindow(hwnd_, SW_HIDE);
+}
+
+void RootWindowWin::SetBounds(int x, int y, size_t width, size_t height) {
+  REQUIRE_MAIN_THREAD();
+
+  if (hwnd_) {
+    SetWindowPos(hwnd_, NULL, x, y, static_cast<int>(width),
+                 static_cast<int>(height), SWP_NOZORDER);
+  }
+}
+
+void RootWindowWin::Close(bool force) {
+  REQUIRE_MAIN_THREAD();
+
+  if (hwnd_) {
+    if (force)
+      DestroyWindow(hwnd_);
+    else
+      PostMessage(hwnd_, WM_CLOSE, 0, 0);
+  }
+}
+
+void RootWindowWin::SetDeviceScaleFactor(float device_scale_factor) {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_ && with_osr_)
+    browser_window_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float RootWindowWin::GetDeviceScaleFactor() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_ && with_osr_)
+    return browser_window_->GetDeviceScaleFactor();
+
+  NOTREACHED();
+  return 0.0f;
+}
+
+CefRefPtr<CefBrowser> RootWindowWin::GetBrowser() const {
+  REQUIRE_MAIN_THREAD();
+
+  if (browser_window_)
+    return browser_window_->GetBrowser();
+  return nullptr;
+}
+
+ClientWindowHandle RootWindowWin::GetWindowHandle() const {
+  REQUIRE_MAIN_THREAD();
+  return hwnd_;
+}
+
+bool RootWindowWin::WithWindowlessRendering() const {
+  REQUIRE_MAIN_THREAD();
+  return with_osr_;
+}
+
+bool RootWindowWin::WithExtension() const {
+  REQUIRE_MAIN_THREAD();
+  return with_extension_;
+}
+
+void RootWindowWin::CreateBrowserWindow(const std::string& startup_url) {
+  if (with_osr_) {
+    OsrRendererSettings settings = {};
+    MainContext::Get()->PopulateOsrSettings(&settings);
+    browser_window_.reset(new BrowserWindowOsrWin(this, startup_url, settings));
+  } else {
+    browser_window_.reset(new BrowserWindowStdWin(this, startup_url));
+  }
+}
+
+void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
+                                     bool initially_hidden) {
+  REQUIRE_MAIN_THREAD();
+  DCHECK(!hwnd_);
+
+  HINSTANCE hInstance = GetModuleHandle(NULL);
+
+  // Load strings from the resource file.
+  const std::wstring& window_title = GetResourceString(IDS_APP_TITLE);
+  const std::wstring& window_class = GetResourceString(IDC_CEFCLIENT);
+
+  const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
+  const HBRUSH background_brush = CreateSolidBrush(
+      RGB(CefColorGetR(background_color), CefColorGetG(background_color),
+          CefColorGetB(background_color)));
+
+  // Register the window class.
+  RegisterRootClass(hInstance, window_class, background_brush);
+
+  // Register the message used with the find dialog.
+  find_message_id_ = RegisterWindowMessage(FINDMSGSTRING);
+  CHECK(find_message_id_);
+
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+  const bool no_activate = command_line->HasSwitch(switches::kNoActivate);
+
+  const DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
+  DWORD dwExStyle = always_on_top_ ? WS_EX_TOPMOST : 0;
+  if (no_activate) {
+    // Don't activate the browser window on creation.
+    dwExStyle |= WS_EX_NOACTIVATE;
+  }
+
+  int x, y, width, height;
+  if (::IsRectEmpty(&start_rect_)) {
+    // Use the default window position/size.
+    x = y = width = height = CW_USEDEFAULT;
+  } else {
+    // Adjust the window size to account for window frame and controls.
+    RECT window_rect = start_rect_;
+    ::AdjustWindowRectEx(&window_rect, dwStyle, with_controls_, dwExStyle);
+
+    x = start_rect_.left;
+    y = start_rect_.top;
+    width = window_rect.right - window_rect.left;
+    height = window_rect.bottom - window_rect.top;
+  }
+
+  browser_settings_ = settings;
+
+  // Create the main window initially hidden.
+  CreateWindowEx(dwExStyle, window_class.c_str(), window_title.c_str(), dwStyle,
+                 x, y, width, height, NULL, NULL, hInstance, this);
+  CHECK(hwnd_);
+
+  if (!called_enable_non_client_dpi_scaling_ && IsProcessPerMonitorDpiAware()) {
+    // This call gets Windows to scale the non-client area when WM_DPICHANGED
+    // is fired on Windows versions < 10.0.14393.0.
+    // Derived signature; not available in headers.
+    typedef LRESULT(WINAPI * EnableChildWindowDpiMessagePtr)(HWND, BOOL);
+    static EnableChildWindowDpiMessagePtr func_ptr =
+        reinterpret_cast<EnableChildWindowDpiMessagePtr>(GetProcAddress(
+            GetModuleHandle(L"user32.dll"), "EnableChildWindowDpiMessage"));
+    if (func_ptr)
+      func_ptr(hwnd_, TRUE);
+  }
+
+  if (!initially_hidden) {
+    // Show this window.
+    Show(no_activate ? ShowNoActivate : ShowNormal);
+  }
+}
+
+// static
+void RootWindowWin::RegisterRootClass(HINSTANCE hInstance,
+                                      const std::wstring& window_class,
+                                      HBRUSH background_brush) {
+  // Only register the class one time.
+  static bool class_registered = false;
+  if (class_registered)
+    return;
+  class_registered = true;
+
+  WNDCLASSEX wcex;
+
+  wcex.cbSize = sizeof(WNDCLASSEX);
+
+  wcex.style = CS_HREDRAW | CS_VREDRAW;
+  wcex.lpfnWndProc = RootWndProc;
+  wcex.cbClsExtra = 0;
+  wcex.cbWndExtra = 0;
+  wcex.hInstance = hInstance;
+  wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CEFCLIENT));
+  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+  wcex.hbrBackground = background_brush;
+  wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CEFCLIENT);
+  wcex.lpszClassName = window_class.c_str();
+  wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+
+  RegisterClassEx(&wcex);
+}
+
+// static
+LRESULT CALLBACK RootWindowWin::EditWndProc(HWND hWnd,
+                                            UINT message,
+                                            WPARAM wParam,
+                                            LPARAM lParam) {
+  REQUIRE_MAIN_THREAD();
+
+  RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
+  DCHECK(self);
+  DCHECK(hWnd == self->edit_hwnd_);
+
+  switch (message) {
+    case WM_CHAR:
+      if (wParam == VK_RETURN) {
+        // When the user hits the enter key load the URL.
+        CefRefPtr<CefBrowser> browser = self->GetBrowser();
+        if (browser) {
+          wchar_t strPtr[MAX_URL_LENGTH + 1] = {0};
+          *((LPWORD)strPtr) = MAX_URL_LENGTH;
+          LRESULT strLen = SendMessage(hWnd, EM_GETLINE, 0, (LPARAM)strPtr);
+          if (strLen > 0) {
+            strPtr[strLen] = 0;
+            browser->GetMainFrame()->LoadURL(strPtr);
+          }
+        }
+        return 0;
+      }
+      break;
+    case WM_NCDESTROY:
+      // Clear the reference to |self|.
+      SetUserDataPtr(hWnd, NULL);
+      self->edit_hwnd_ = NULL;
+      break;
+  }
+
+  return CallWindowProc(self->edit_wndproc_old_, hWnd, message, wParam, lParam);
+}
+
+// static
+LRESULT CALLBACK RootWindowWin::FindWndProc(HWND hWnd,
+                                            UINT message,
+                                            WPARAM wParam,
+                                            LPARAM lParam) {
+  REQUIRE_MAIN_THREAD();
+
+  RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
+  DCHECK(self);
+  DCHECK(hWnd == self->find_hwnd_);
+
+  switch (message) {
+    case WM_ACTIVATE:
+      // Set this dialog as current when activated.
+      MainMessageLoop::Get()->SetCurrentModelessDialog(wParam == 0 ? NULL
+                                                                   : hWnd);
+      return FALSE;
+    case WM_NCDESTROY:
+      // Clear the reference to |self|.
+      SetUserDataPtr(hWnd, NULL);
+      self->find_hwnd_ = NULL;
+      break;
+  }
+
+  return CallWindowProc(self->find_wndproc_old_, hWnd, message, wParam, lParam);
+}
+
+// static
+LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd,
+                                            UINT message,
+                                            WPARAM wParam,
+                                            LPARAM lParam) {
+  REQUIRE_MAIN_THREAD();
+
+  RootWindowWin* self = nullptr;
+  if (message != WM_NCCREATE) {
+    self = GetUserDataPtr<RootWindowWin*>(hWnd);
+    if (!self)
+      return DefWindowProc(hWnd, message, wParam, lParam);
+    DCHECK_EQ(hWnd, self->hwnd_);
+  }
+
+  if (self && message == self->find_message_id_) {
+    // Message targeting the find dialog.
+    LPFINDREPLACE lpfr = reinterpret_cast<LPFINDREPLACE>(lParam);
+    CHECK(lpfr == &self->find_state_);
+    self->OnFindEvent();
+    return 0;
+  }
+
+  // Callback for the main window
+  switch (message) {
+    case WM_COMMAND:
+      if (self->OnCommand(LOWORD(wParam)))
+        return 0;
+      break;
+
+    case WM_GETOBJECT: {
+      // Only the lower 32 bits of lParam are valid when checking the object id
+      // because it sometimes gets sign-extended incorrectly (but not always).
+      DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
+
+      // Accessibility readers will send an OBJID_CLIENT message.
+      if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
+        if (self->GetBrowser() && self->GetBrowser()->GetHost())
+          self->GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
+      }
+    } break;
+
+    case WM_PAINT:
+      self->OnPaint();
+      return 0;
+
+    case WM_ACTIVATE:
+      self->OnActivate(LOWORD(wParam) != WA_INACTIVE);
+      // Allow DefWindowProc to set keyboard focus.
+      break;
+
+    case WM_SETFOCUS:
+      self->OnFocus();
+      return 0;
+
+    case WM_SIZE:
+      self->OnSize(wParam == SIZE_MINIMIZED);
+      break;
+
+    case WM_MOVING:
+    case WM_MOVE:
+      self->OnMove();
+      return 0;
+
+    case WM_DPICHANGED:
+      self->OnDpiChanged(wParam, lParam);
+      break;
+
+    case WM_ERASEBKGND:
+      if (self->OnEraseBkgnd())
+        break;
+      // Don't erase the background.
+      return 0;
+
+    case WM_ENTERMENULOOP:
+      if (!wParam) {
+        // Entering the menu loop for the application menu.
+        CefSetOSModalLoop(true);
+      }
+      break;
+
+    case WM_EXITMENULOOP:
+      if (!wParam) {
+        // Exiting the menu loop for the application menu.
+        CefSetOSModalLoop(false);
+      }
+      break;
+
+    case WM_CLOSE:
+      if (self->OnClose())
+        return 0;  // Cancel the close.
+      break;
+
+    case WM_NCHITTEST: {
+      LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
+      if (hit == HTCLIENT) {
+        POINTS points = MAKEPOINTS(lParam);
+        POINT point = {points.x, points.y};
+        ::ScreenToClient(hWnd, &point);
+        if (::PtInRegion(self->draggable_region_, point.x, point.y)) {
+          // If cursor is inside a draggable region return HTCAPTION to allow
+          // dragging.
+          return HTCAPTION;
+        }
+      }
+      return hit;
+    }
+
+    case WM_NCCREATE: {
+      CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
+      self = reinterpret_cast<RootWindowWin*>(cs->lpCreateParams);
+      DCHECK(self);
+      // Associate |self| with the main window.
+      SetUserDataPtr(hWnd, self);
+      self->hwnd_ = hWnd;
+
+      self->OnNCCreate(cs);
+    } break;
+
+    case WM_CREATE:
+      self->OnCreate(reinterpret_cast<CREATESTRUCT*>(lParam));
+      break;
+
+    case WM_NCDESTROY:
+      // Clear the reference to |self|.
+      SetUserDataPtr(hWnd, NULL);
+      self->hwnd_ = NULL;
+      self->OnDestroyed();
+      break;
+  }
+
+  return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+void RootWindowWin::OnPaint() {
+  PAINTSTRUCT ps;
+  BeginPaint(hwnd_, &ps);
+  EndPaint(hwnd_, &ps);
+}
+
+void RootWindowWin::OnFocus() {
+  // Selecting "Close window" from the task bar menu may send a focus
+  // notification even though the window is currently disabled (e.g. while a
+  // modal JS dialog is displayed).
+  if (browser_window_ && ::IsWindowEnabled(hwnd_))
+    browser_window_->SetFocus(true);
+}
+
+void RootWindowWin::OnActivate(bool active) {
+  if (active)
+    delegate_->OnRootWindowActivated(this);
+}
+
+void RootWindowWin::OnSize(bool minimized) {
+  if (minimized) {
+    // Notify the browser window that it was hidden and do nothing further.
+    if (browser_window_)
+      browser_window_->Hide();
+    return;
+  }
+
+  if (browser_window_)
+    browser_window_->Show();
+
+  RECT rect;
+  GetClientRect(hwnd_, &rect);
+
+  if (with_controls_ && edit_hwnd_) {
+    const int button_width = GetButtonWidth(hwnd_);
+    const int urlbar_height = GetURLBarHeight(hwnd_);
+    const int font_height = LogicalToDevice(14, GetWindowScaleFactor(hwnd_));
+
+    if (font_height != font_height_) {
+      font_height_ = font_height;
+      if (font_) {
+        DeleteObject(font_);
+      }
+
+      // Create a scaled font.
+      font_ =
+          ::CreateFont(-font_height, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
+                       DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
+                       DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
+
+      SendMessage(back_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
+                  TRUE);
+      SendMessage(forward_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
+                  TRUE);
+      SendMessage(reload_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
+                  TRUE);
+      SendMessage(stop_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
+                  TRUE);
+      SendMessage(edit_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
+                  TRUE);
+    }
+
+    // Resize the window and address bar to match the new frame size.
+    rect.top += urlbar_height;
+
+    int x_offset = rect.left;
+
+    // |browser_hwnd| may be NULL if the browser has not yet been created.
+    HWND browser_hwnd = NULL;
+    if (browser_window_)
+      browser_hwnd = browser_window_->GetWindowHandle();
+
+    // Resize all controls.
+    HDWP hdwp = BeginDeferWindowPos(browser_hwnd ? 6 : 5);
+    hdwp = DeferWindowPos(hdwp, back_hwnd_, NULL, x_offset, 0, button_width,
+                          urlbar_height, SWP_NOZORDER);
+    x_offset += button_width;
+    hdwp = DeferWindowPos(hdwp, forward_hwnd_, NULL, x_offset, 0, button_width,
+                          urlbar_height, SWP_NOZORDER);
+    x_offset += button_width;
+    hdwp = DeferWindowPos(hdwp, reload_hwnd_, NULL, x_offset, 0, button_width,
+                          urlbar_height, SWP_NOZORDER);
+    x_offset += button_width;
+    hdwp = DeferWindowPos(hdwp, stop_hwnd_, NULL, x_offset, 0, button_width,
+                          urlbar_height, SWP_NOZORDER);
+    x_offset += button_width;
+    hdwp = DeferWindowPos(hdwp, edit_hwnd_, NULL, x_offset, 0,
+                          rect.right - x_offset, urlbar_height, SWP_NOZORDER);
+
+    if (browser_hwnd) {
+      hdwp = DeferWindowPos(hdwp, browser_hwnd, NULL, rect.left, rect.top,
+                            rect.right - rect.left, rect.bottom - rect.top,
+                            SWP_NOZORDER);
+    }
+
+    BOOL result = EndDeferWindowPos(hdwp);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK(result);
+  } else if (browser_window_) {
+    // Size the browser window to the whole client area.
+    browser_window_->SetBounds(0, 0, rect.right, rect.bottom);
+  }
+}
+
+void RootWindowWin::OnMove() {
+  // Notify the browser of move events so that popup windows are displayed
+  // in the correct location and dismissed when the window moves.
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (browser)
+    browser->GetHost()->NotifyMoveOrResizeStarted();
+}
+
+void RootWindowWin::OnDpiChanged(WPARAM wParam, LPARAM lParam) {
+  if (LOWORD(wParam) != HIWORD(wParam)) {
+    NOTIMPLEMENTED() << "Received non-square scaling factors";
+    return;
+  }
+
+  if (browser_window_ && with_osr_) {
+    // Scale factor for the new display.
+    const float display_scale_factor =
+        static_cast<float>(LOWORD(wParam)) / DPI_1X;
+    browser_window_->SetDeviceScaleFactor(display_scale_factor);
+  }
+
+  // Suggested size and position of the current window scaled for the new DPI.
+  const RECT* rect = reinterpret_cast<RECT*>(lParam);
+  SetBounds(rect->left, rect->top, rect->right - rect->left,
+            rect->bottom - rect->top);
+}
+
+bool RootWindowWin::OnEraseBkgnd() {
+  // Erase the background when the browser does not exist.
+  return (GetBrowser() == nullptr);
+}
+
+bool RootWindowWin::OnCommand(UINT id) {
+  if (id >= ID_TESTS_FIRST && id <= ID_TESTS_LAST) {
+    delegate_->OnTest(this, id);
+    return true;
+  }
+
+  switch (id) {
+    case IDM_ABOUT:
+      OnAbout();
+      return true;
+    case IDM_EXIT:
+      delegate_->OnExit(this);
+      return true;
+    case ID_FIND:
+      OnFind();
+      return true;
+    case IDC_NAV_BACK:  // Back button
+      if (CefRefPtr<CefBrowser> browser = GetBrowser())
+        browser->GoBack();
+      return true;
+    case IDC_NAV_FORWARD:  // Forward button
+      if (CefRefPtr<CefBrowser> browser = GetBrowser())
+        browser->GoForward();
+      return true;
+    case IDC_NAV_RELOAD:  // Reload button
+      if (CefRefPtr<CefBrowser> browser = GetBrowser())
+        browser->Reload();
+      return true;
+    case IDC_NAV_STOP:  // Stop button
+      if (CefRefPtr<CefBrowser> browser = GetBrowser())
+        browser->StopLoad();
+      return true;
+  }
+
+  return false;
+}
+
+void RootWindowWin::OnFind() {
+  if (find_hwnd_) {
+    // Give focus to the existing find dialog.
+    ::SetFocus(find_hwnd_);
+    return;
+  }
+
+  // Configure dialog state.
+  ZeroMemory(&find_state_, sizeof(find_state_));
+  find_state_.lStructSize = sizeof(find_state_);
+  find_state_.hwndOwner = hwnd_;
+  find_state_.lpstrFindWhat = find_buff_;
+  find_state_.wFindWhatLen = sizeof(find_buff_);
+  find_state_.Flags = FR_HIDEWHOLEWORD | FR_DOWN;
+
+  // Create the dialog.
+  find_hwnd_ = FindText(&find_state_);
+
+  // Override the dialog's window procedure.
+  find_wndproc_old_ = SetWndProcPtr(find_hwnd_, FindWndProc);
+
+  // Associate |self| with the dialog.
+  SetUserDataPtr(find_hwnd_, this);
+}
+
+void RootWindowWin::OnFindEvent() {
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+
+  if (find_state_.Flags & FR_DIALOGTERM) {
+    // The find dialog box has been dismissed so invalidate the handle and
+    // reset the search results.
+    if (browser) {
+      browser->GetHost()->StopFinding(true);
+      find_what_last_.clear();
+      find_next_ = false;
+    }
+  } else if ((find_state_.Flags & FR_FINDNEXT) && browser) {
+    // Search for the requested string.
+    bool match_case = ((find_state_.Flags & FR_MATCHCASE) ? true : false);
+    const std::wstring& find_what = find_buff_;
+    if (match_case != find_match_case_last_ || find_what != find_what_last_) {
+      // The search string has changed, so reset the search results.
+      if (!find_what.empty()) {
+        browser->GetHost()->StopFinding(true);
+        find_next_ = false;
+      }
+      find_match_case_last_ = match_case;
+      find_what_last_ = find_buff_;
+    }
+
+    browser->GetHost()->Find(0, find_what,
+                             (find_state_.Flags & FR_DOWN) ? true : false,
+                             match_case, find_next_);
+    if (!find_next_)
+      find_next_ = true;
+  }
+}
+
+void RootWindowWin::OnAbout() {
+  // Show the about box.
+  DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd_,
+            AboutWndProc);
+}
+
+void RootWindowWin::OnNCCreate(LPCREATESTRUCT lpCreateStruct) {
+  if (IsProcessPerMonitorDpiAware()) {
+    // This call gets Windows to scale the non-client area when WM_DPICHANGED
+    // is fired on Windows versions >= 10.0.14393.0.
+    typedef BOOL(WINAPI * EnableNonClientDpiScalingPtr)(HWND);
+    static EnableNonClientDpiScalingPtr func_ptr =
+        reinterpret_cast<EnableNonClientDpiScalingPtr>(GetProcAddress(
+            GetModuleHandle(L"user32.dll"), "EnableNonClientDpiScaling"));
+    called_enable_non_client_dpi_scaling_ = !!(func_ptr && func_ptr(hwnd_));
+  }
+}
+
+void RootWindowWin::OnCreate(LPCREATESTRUCT lpCreateStruct) {
+  const HINSTANCE hInstance = lpCreateStruct->hInstance;
+
+  RECT rect;
+  GetClientRect(hwnd_, &rect);
+
+  if (with_controls_) {
+    // Create the child controls.
+    int x_offset = 0;
+
+    const int button_width = GetButtonWidth(hwnd_);
+    const int urlbar_height = GetURLBarHeight(hwnd_);
+
+    back_hwnd_ = CreateWindow(
+        L"BUTTON", L"Back", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
+        x_offset, 0, button_width, urlbar_height, hwnd_,
+        reinterpret_cast<HMENU>(IDC_NAV_BACK), hInstance, 0);
+    CHECK(back_hwnd_);
+    x_offset += button_width;
+
+    forward_hwnd_ =
+        CreateWindow(L"BUTTON", L"Forward",
+                     WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
+                     x_offset, 0, button_width, urlbar_height, hwnd_,
+                     reinterpret_cast<HMENU>(IDC_NAV_FORWARD), hInstance, 0);
+    CHECK(forward_hwnd_);
+    x_offset += button_width;
+
+    reload_hwnd_ =
+        CreateWindow(L"BUTTON", L"Reload",
+                     WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
+                     x_offset, 0, button_width, urlbar_height, hwnd_,
+                     reinterpret_cast<HMENU>(IDC_NAV_RELOAD), hInstance, 0);
+    CHECK(reload_hwnd_);
+    x_offset += button_width;
+
+    stop_hwnd_ = CreateWindow(
+        L"BUTTON", L"Stop", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
+        x_offset, 0, button_width, urlbar_height, hwnd_,
+        reinterpret_cast<HMENU>(IDC_NAV_STOP), hInstance, 0);
+    CHECK(stop_hwnd_);
+    x_offset += button_width;
+
+    edit_hwnd_ = CreateWindow(L"EDIT", 0,
+                              WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
+                                  ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_DISABLED,
+                              x_offset, 0, rect.right - button_width * 4,
+                              urlbar_height, hwnd_, 0, hInstance, 0);
+    CHECK(edit_hwnd_);
+
+    // Override the edit control's window procedure.
+    edit_wndproc_old_ = SetWndProcPtr(edit_hwnd_, EditWndProc);
+
+    // Associate |this| with the edit window.
+    SetUserDataPtr(edit_hwnd_, this);
+
+    rect.top += urlbar_height;
+
+    if (!with_osr_) {
+      // Remove the menu items that are only used with OSR.
+      HMENU hMenu = ::GetMenu(hwnd_);
+      if (hMenu) {
+        HMENU hTestMenu = ::GetSubMenu(hMenu, 2);
+        if (hTestMenu) {
+          ::RemoveMenu(hTestMenu, ID_TESTS_OSR_FPS, MF_BYCOMMAND);
+          ::RemoveMenu(hTestMenu, ID_TESTS_OSR_DSF, MF_BYCOMMAND);
+        }
+      }
+    }
+  } else {
+    // No controls so also remove the default menu.
+    ::SetMenu(hwnd_, NULL);
+  }
+
+  const float device_scale_factor = GetWindowScaleFactor(hwnd_);
+
+  if (with_osr_) {
+    browser_window_->SetDeviceScaleFactor(device_scale_factor);
+  }
+
+  if (!is_popup_) {
+    // Create the browser window.
+    CefRect cef_rect(rect.left, rect.top, rect.right - rect.left,
+                     rect.bottom - rect.top);
+    browser_window_->CreateBrowser(hwnd_, cef_rect, browser_settings_, nullptr,
+                                   delegate_->GetRequestContext(this));
+  } else {
+    // With popups we already have a browser window. Parent the browser window
+    // to the root window and show it in the correct location.
+    browser_window_->ShowPopup(hwnd_, rect.left, rect.top,
+                               rect.right - rect.left, rect.bottom - rect.top);
+  }
+}
+
+bool RootWindowWin::OnClose() {
+  if (browser_window_ && !browser_window_->IsClosing()) {
+    CefRefPtr<CefBrowser> browser = GetBrowser();
+    if (browser) {
+      // Notify the browser window that we would like to close it. This
+      // will result in a call to ClientHandler::DoClose() if the
+      // JavaScript 'onbeforeunload' event handler allows it.
+      browser->GetHost()->CloseBrowser(false);
+
+      // Cancel the close.
+      return true;
+    }
+  }
+
+  // Allow the close.
+  return false;
+}
+
+void RootWindowWin::OnDestroyed() {
+  window_destroyed_ = true;
+  NotifyDestroyedIfDone();
+}
+
+void RootWindowWin::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+
+  if (is_popup_) {
+    // For popup browsers create the root window once the browser has been
+    // created.
+    CreateRootWindow(CefBrowserSettings(), false);
+  } else {
+    // Make sure the browser is sized correctly.
+    OnSize(false);
+  }
+
+  delegate_->OnBrowserCreated(this, browser);
+}
+
+void RootWindowWin::OnBrowserWindowDestroyed() {
+  REQUIRE_MAIN_THREAD();
+
+  browser_window_.reset();
+
+  if (!window_destroyed_) {
+    // The browser was destroyed first. This could be due to the use of
+    // off-screen rendering or execution of JavaScript window.close().
+    // Close the RootWindow.
+    Close(true);
+  }
+
+  browser_destroyed_ = true;
+  NotifyDestroyedIfDone();
+}
+
+void RootWindowWin::OnSetAddress(const std::string& url) {
+  REQUIRE_MAIN_THREAD();
+
+  if (edit_hwnd_)
+    SetWindowText(edit_hwnd_, CefString(url).ToWString().c_str());
+}
+
+void RootWindowWin::OnSetTitle(const std::string& title) {
+  REQUIRE_MAIN_THREAD();
+
+  if (hwnd_)
+    SetWindowText(hwnd_, CefString(title).ToWString().c_str());
+}
+
+void RootWindowWin::OnSetFullscreen(bool fullscreen) {
+  REQUIRE_MAIN_THREAD();
+
+  CefRefPtr<CefBrowser> browser = GetBrowser();
+  if (browser) {
+    scoped_ptr<window_test::WindowTestRunnerWin> test_runner(
+        new window_test::WindowTestRunnerWin());
+    if (fullscreen)
+      test_runner->Maximize(browser);
+    else
+      test_runner->Restore(browser);
+  }
+}
+
+void RootWindowWin::OnAutoResize(const CefSize& new_size) {
+  REQUIRE_MAIN_THREAD();
+
+  if (!hwnd_)
+    return;
+
+  int new_width = new_size.width;
+
+  // Make the window wide enough to drag by the top menu bar.
+  if (new_width < 200)
+    new_width = 200;
+
+  const float device_scale_factor = GetWindowScaleFactor(hwnd_);
+  RECT rect = {0, 0, LogicalToDevice(new_width, device_scale_factor),
+               LogicalToDevice(new_size.height, device_scale_factor)};
+  DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
+  DWORD ex_style = GetWindowLong(hwnd_, GWL_EXSTYLE);
+  bool has_menu = !(style & WS_CHILD) && (GetMenu(hwnd_) != NULL);
+
+  // The size value is for the client area. Calculate the whole window size
+  // based on the current style.
+  AdjustWindowRectEx(&rect, style, has_menu, ex_style);
+
+  // Size the window. The left/top values may be negative.
+  // Also show the window if it's not currently visible.
+  SetWindowPos(hwnd_, NULL, 0, 0, rect.right - rect.left,
+               rect.bottom - rect.top,
+               SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+}
+
+void RootWindowWin::OnSetLoadingState(bool isLoading,
+                                      bool canGoBack,
+                                      bool canGoForward) {
+  REQUIRE_MAIN_THREAD();
+
+  if (with_controls_) {
+    EnableWindow(back_hwnd_, canGoBack);
+    EnableWindow(forward_hwnd_, canGoForward);
+    EnableWindow(reload_hwnd_, !isLoading);
+    EnableWindow(stop_hwnd_, isLoading);
+    EnableWindow(edit_hwnd_, TRUE);
+  }
+
+  if (!isLoading && GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
+    // Done with the initial navigation. Remove the WS_EX_NOACTIVATE style so
+    // that future mouse clicks inside the browser correctly activate and focus
+    // the window. For the top-level window removing this style causes Windows
+    // to display the task bar button.
+    SetWindowLongPtr(hwnd_, GWL_EXSTYLE,
+                     GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
+
+    if (browser_window_) {
+      HWND browser_hwnd = browser_window_->GetWindowHandle();
+      SetWindowLongPtr(
+          browser_hwnd, GWL_EXSTYLE,
+          GetWindowLongPtr(browser_hwnd, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
+    }
+  }
+}
+
+namespace {
+
+LPCWSTR kParentWndProc = L"CefParentWndProc";
+LPCWSTR kDraggableRegion = L"CefDraggableRegion";
+
+LRESULT CALLBACK SubclassedWindowProc(HWND hWnd,
+                                      UINT message,
+                                      WPARAM wParam,
+                                      LPARAM lParam) {
+  WNDPROC hParentWndProc =
+      reinterpret_cast<WNDPROC>(::GetPropW(hWnd, kParentWndProc));
+  HRGN hRegion = reinterpret_cast<HRGN>(::GetPropW(hWnd, kDraggableRegion));
+
+  if (message == WM_NCHITTEST) {
+    LRESULT hit = CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
+    if (hit == HTCLIENT) {
+      POINTS points = MAKEPOINTS(lParam);
+      POINT point = {points.x, points.y};
+      ::ScreenToClient(hWnd, &point);
+      if (::PtInRegion(hRegion, point.x, point.y)) {
+        // Let the parent window handle WM_NCHITTEST by returning HTTRANSPARENT
+        // in child windows.
+        return HTTRANSPARENT;
+      }
+    }
+    return hit;
+  }
+
+  return CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
+}
+
+void SubclassWindow(HWND hWnd, HRGN hRegion) {
+  HANDLE hParentWndProc = ::GetPropW(hWnd, kParentWndProc);
+  if (hParentWndProc) {
+    return;
+  }
+
+  SetLastError(0);
+  LONG_PTR hOldWndProc = SetWindowLongPtr(
+      hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
+  if (hOldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
+    return;
+  }
+
+  ::SetPropW(hWnd, kParentWndProc, reinterpret_cast<HANDLE>(hOldWndProc));
+  ::SetPropW(hWnd, kDraggableRegion, reinterpret_cast<HANDLE>(hRegion));
+}
+
+void UnSubclassWindow(HWND hWnd) {
+  LONG_PTR hParentWndProc =
+      reinterpret_cast<LONG_PTR>(::GetPropW(hWnd, kParentWndProc));
+  if (hParentWndProc) {
+    LONG_PTR hPreviousWndProc =
+        SetWindowLongPtr(hWnd, GWLP_WNDPROC, hParentWndProc);
+    ALLOW_UNUSED_LOCAL(hPreviousWndProc);
+    DCHECK_EQ(hPreviousWndProc,
+              reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
+  }
+
+  ::RemovePropW(hWnd, kParentWndProc);
+  ::RemovePropW(hWnd, kDraggableRegion);
+}
+
+BOOL CALLBACK SubclassWindowsProc(HWND hwnd, LPARAM lParam) {
+  SubclassWindow(hwnd, reinterpret_cast<HRGN>(lParam));
+  return TRUE;
+}
+
+BOOL CALLBACK UnSubclassWindowsProc(HWND hwnd, LPARAM lParam) {
+  UnSubclassWindow(hwnd);
+  return TRUE;
+}
+
+}  // namespace
+
+void RootWindowWin::OnSetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  REQUIRE_MAIN_THREAD();
+
+  // Reset draggable region.
+  ::SetRectRgn(draggable_region_, 0, 0, 0, 0);
+
+  // Determine new draggable region.
+  std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
+  for (; it != regions.end(); ++it) {
+    HRGN region = ::CreateRectRgn(it->bounds.x, it->bounds.y,
+                                  it->bounds.x + it->bounds.width,
+                                  it->bounds.y + it->bounds.height);
+    ::CombineRgn(draggable_region_, draggable_region_, region,
+                 it->draggable ? RGN_OR : RGN_DIFF);
+    ::DeleteObject(region);
+  }
+
+  // Subclass child window procedures in order to do hit-testing.
+  // This will be a no-op, if it is already subclassed.
+  if (hwnd_) {
+    WNDENUMPROC proc =
+        !regions.empty() ? SubclassWindowsProc : UnSubclassWindowsProc;
+    ::EnumChildWindows(hwnd_, proc,
+                       reinterpret_cast<LPARAM>(draggable_region_));
+  }
+}
+
+void RootWindowWin::NotifyDestroyedIfDone() {
+  // Notify once both the window and the browser have been destroyed.
+  if (window_destroyed_ && browser_destroyed_)
+    delegate_->OnRootWindowDestroyed(this);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/root_window_win.h b/src/tests/cefclient/browser/root_window_win.h
new file mode 100644
index 0000000..153c38e
--- /dev/null
+++ b/src/tests/cefclient/browser/root_window_win.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_WIN_H_
+#pragma once
+
+#include <windows.h>
+
+#include <commdlg.h>
+
+#include <string>
+
+#include "include/base/cef_scoped_ptr.h"
+#include "tests/cefclient/browser/browser_window.h"
+#include "tests/cefclient/browser/root_window.h"
+
+namespace client {
+
+// Windows implementation of a top-level native window in the browser process.
+// The methods of this class must be called on the main thread unless otherwise
+// indicated.
+class RootWindowWin : public RootWindow, public BrowserWindow::Delegate {
+ public:
+  // Constructor may be called on any thread.
+  RootWindowWin();
+  ~RootWindowWin();
+
+  // RootWindow methods.
+  void Init(RootWindow::Delegate* delegate,
+            const RootWindowConfig& config,
+            const CefBrowserSettings& settings) OVERRIDE;
+  void InitAsPopup(RootWindow::Delegate* delegate,
+                   bool with_controls,
+                   bool with_osr,
+                   const CefPopupFeatures& popupFeatures,
+                   CefWindowInfo& windowInfo,
+                   CefRefPtr<CefClient>& client,
+                   CefBrowserSettings& settings) OVERRIDE;
+  void Show(ShowMode mode) OVERRIDE;
+  void Hide() OVERRIDE;
+  void SetBounds(int x, int y, size_t width, size_t height) OVERRIDE;
+  void Close(bool force) OVERRIDE;
+  void SetDeviceScaleFactor(float device_scale_factor) OVERRIDE;
+  float GetDeviceScaleFactor() const OVERRIDE;
+  CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
+  ClientWindowHandle GetWindowHandle() const OVERRIDE;
+  bool WithWindowlessRendering() const OVERRIDE;
+  bool WithExtension() const OVERRIDE;
+
+ private:
+  void CreateBrowserWindow(const std::string& startup_url);
+  void CreateRootWindow(const CefBrowserSettings& settings,
+                        bool initially_hidden);
+
+  // Register the root window class.
+  static void RegisterRootClass(HINSTANCE hInstance,
+                                const std::wstring& window_class,
+                                HBRUSH background_brush);
+
+  // Window procedure for the edit field.
+  static LRESULT CALLBACK EditWndProc(HWND hWnd,
+                                      UINT message,
+                                      WPARAM wParam,
+                                      LPARAM lParam);
+
+  // Window procedure for the find dialog.
+  static LRESULT CALLBACK FindWndProc(HWND hWnd,
+                                      UINT message,
+                                      WPARAM wParam,
+                                      LPARAM lParam);
+
+  // Window procedure for the root window.
+  static LRESULT CALLBACK RootWndProc(HWND hWnd,
+                                      UINT message,
+                                      WPARAM wParam,
+                                      LPARAM lParam);
+
+  // Event handlers.
+  void OnPaint();
+  void OnFocus();
+  void OnActivate(bool active);
+  void OnSize(bool minimized);
+  void OnMove();
+  void OnDpiChanged(WPARAM wParam, LPARAM lParam);
+  bool OnEraseBkgnd();
+  bool OnCommand(UINT id);
+  void OnFind();
+  void OnFindEvent();
+  void OnAbout();
+  void OnNCCreate(LPCREATESTRUCT lpCreateStruct);
+  void OnCreate(LPCREATESTRUCT lpCreateStruct);
+  bool OnClose();
+  void OnDestroyed();
+
+  // BrowserWindow::Delegate methods.
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void OnBrowserWindowDestroyed() OVERRIDE;
+  void OnSetAddress(const std::string& url) OVERRIDE;
+  void OnSetTitle(const std::string& title) OVERRIDE;
+  void OnSetFullscreen(bool fullscreen) OVERRIDE;
+  void OnAutoResize(const CefSize& new_size) OVERRIDE;
+  void OnSetLoadingState(bool isLoading,
+                         bool canGoBack,
+                         bool canGoForward) OVERRIDE;
+  void OnSetDraggableRegions(
+      const std::vector<CefDraggableRegion>& regions) OVERRIDE;
+
+  void NotifyDestroyedIfDone();
+
+  // After initialization all members are only accessed on the main thread.
+  // Members set during initialization.
+  bool with_controls_;
+  bool always_on_top_;
+  bool with_osr_;
+  bool with_extension_;
+  bool is_popup_;
+  RECT start_rect_;
+  scoped_ptr<BrowserWindow> browser_window_;
+  CefBrowserSettings browser_settings_;
+  bool initialized_;
+
+  // Main window.
+  HWND hwnd_;
+
+  // Draggable region.
+  HRGN draggable_region_;
+
+  // Font for buttons and text fields.
+  HFONT font_;
+  int font_height_;
+
+  // Buttons.
+  HWND back_hwnd_;
+  HWND forward_hwnd_;
+  HWND reload_hwnd_;
+  HWND stop_hwnd_;
+
+  // URL text field.
+  HWND edit_hwnd_;
+  WNDPROC edit_wndproc_old_;
+
+  // Find dialog.
+  HWND find_hwnd_;
+  UINT find_message_id_;
+  WNDPROC find_wndproc_old_;
+
+  // Find dialog state.
+  FINDREPLACE find_state_;
+  WCHAR find_buff_[80];
+  std::wstring find_what_last_;
+  bool find_next_;
+  bool find_match_case_last_;
+
+  bool window_destroyed_;
+  bool browser_destroyed_;
+
+  bool called_enable_non_client_dpi_scaling_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowWin);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_WIN_H_
diff --git a/src/tests/cefclient/browser/scheme_test.cc b/src/tests/cefclient/browser/scheme_test.cc
new file mode 100644
index 0000000..c19806b
--- /dev/null
+++ b/src/tests/cefclient/browser/scheme_test.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/scheme_test.h"
+
+#include <algorithm>
+#include <string>
+
+#include "include/cef_browser.h"
+#include "include/cef_callback.h"
+#include "include/cef_frame.h"
+#include "include/cef_request.h"
+#include "include/cef_resource_handler.h"
+#include "include/cef_response.h"
+#include "include/cef_scheme.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/browser/resource_util.h"
+
+namespace client {
+namespace scheme_test {
+
+namespace {
+
+// Implementation of the schema handler for client:// requests.
+class ClientSchemeHandler : public CefResourceHandler {
+ public:
+  ClientSchemeHandler() : offset_(0) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) OVERRIDE {
+    DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
+
+    // The request will be continued or canceled based on the return value.
+    handle_request = true;
+
+    bool handled = false;
+
+    std::string url = request->GetURL();
+    if (strstr(url.c_str(), "handler.html") != nullptr) {
+      // Build the response html
+      data_ =
+          "<html><head><title>Client Scheme Handler</title></head>"
+          "<body bgcolor=\"white\">"
+          "This contents of this page page are served by the "
+          "ClientSchemeHandler class handling the client:// protocol."
+          "<br/>You should see an image:"
+          "<br/><img src=\"client://tests/logo.png\"><pre>";
+
+      // Output a string representation of the request
+      const std::string& dump = test_runner::DumpRequestContents(request);
+      data_.append(dump);
+
+      data_.append(
+          "</pre><br/>Try the test form:"
+          "<form method=\"POST\" action=\"handler.html\">"
+          "<input type=\"text\" name=\"field1\">"
+          "<input type=\"text\" name=\"field2\">"
+          "<input type=\"submit\">"
+          "</form></body></html>");
+
+      handled = true;
+
+      // Set the resulting mime type
+      mime_type_ = "text/html";
+    } else if (strstr(url.c_str(), "logo.png") != nullptr) {
+      // Load the response image
+      if (LoadBinaryResource("logo.png", data_)) {
+        handled = true;
+        // Set the resulting mime type
+        mime_type_ = "image/png";
+      }
+    }
+
+    return handled;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+
+    DCHECK(!data_.empty());
+
+    response->SetMimeType(mime_type_);
+    response->SetStatus(200);
+
+    // Set the resulting response length
+    response_length = data_.length();
+  }
+
+  void Cancel() OVERRIDE { CEF_REQUIRE_IO_THREAD(); }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) OVERRIDE {
+    DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
+
+    bool has_data = false;
+    bytes_read = 0;
+
+    if (offset_ < data_.length()) {
+      // Copy the next block of data into the buffer.
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(data_.length() - offset_));
+      memcpy(data_out, data_.c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+ private:
+  std::string data_;
+  std::string mime_type_;
+  size_t offset_;
+
+  IMPLEMENT_REFCOUNTING(ClientSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(ClientSchemeHandler);
+};
+
+// Implementation of the factory for for creating schema handlers.
+class ClientSchemeHandlerFactory : public CefSchemeHandlerFactory {
+ public:
+  ClientSchemeHandlerFactory() {}
+
+  // Return a new scheme handler instance to handle the request.
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+    return new ClientSchemeHandler();
+  }
+
+  IMPLEMENT_REFCOUNTING(ClientSchemeHandlerFactory);
+  DISALLOW_COPY_AND_ASSIGN(ClientSchemeHandlerFactory);
+};
+
+}  // namespace
+
+void RegisterSchemeHandlers() {
+  CefRegisterSchemeHandlerFactory("client", "tests",
+                                  new ClientSchemeHandlerFactory());
+}
+
+}  // namespace scheme_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/scheme_test.h b/src/tests/cefclient/browser/scheme_test.h
new file mode 100644
index 0000000..c48d6f6
--- /dev/null
+++ b/src/tests/cefclient/browser/scheme_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_SCHEME_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_SCHEME_TEST_H_
+#pragma once
+
+namespace client {
+namespace scheme_test {
+
+// Create and register the custom scheme handler. See
+// common/scheme_handler_common.h for registration of the custom scheme
+// name/type which must occur in all processes. Called from test_runner.cc.
+void RegisterSchemeHandlers();
+
+}  // namespace scheme_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_SCHEME_TEST_H_
diff --git a/src/tests/cefclient/browser/server_test.cc b/src/tests/cefclient/browser/server_test.cc
new file mode 100644
index 0000000..ee3b922
--- /dev/null
+++ b/src/tests/cefclient/browser/server_test.cc
@@ -0,0 +1,387 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/server_test.h"
+
+#include <algorithm>
+#include <string>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_weak_ptr.h"
+#include "include/cef_parser.h"
+#include "include/cef_server.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/shared/browser/resource_util.h"
+
+namespace client {
+namespace server_test {
+
+namespace {
+
+// Application-specific error codes.
+const int kMessageFormatError = 1;
+const int kActionStateError = 1;
+
+// JSON dictionary keys.
+const char kActionKey[] = "action";
+const char kResultKey[] = "result";
+const char kPortKey[] = "port";
+const char kStatusKey[] = "status";
+const char kMessageKey[] = "message";
+
+// Required URL for cefQuery execution.
+const char kTestUrl[] = "http://tests/server";
+
+// Server default values.
+const char kServerAddress[] = "127.0.0.1";
+const int kServerPortDefault = 8099;
+const int kServerBacklog = 10;
+const char kDefaultPath[] = "websocket.html";
+
+// Handles the HTTP/WebSocket server.
+class ServerHandler : public CefServerHandler {
+ public:
+  typedef base::Callback<void(bool /* success */)> CompleteCallback;
+
+  ServerHandler() {}
+
+  // |complete_callback| will be executed on the UI thread after completion.
+  void StartServer(int port, const CompleteCallback& complete_callback) {
+    CEF_REQUIRE_UI_THREAD();
+    DCHECK(!server_);
+    DCHECK(port >= 1025 && port <= 65535);
+    port_ = port;
+    complete_callback_ = complete_callback;
+    CefServer::CreateServer(kServerAddress, port, kServerBacklog, this);
+  }
+
+  // |complete_callback| will be executed on the UI thread after completion.
+  void StopServer(const CompleteCallback& complete_callback) {
+    CEF_REQUIRE_UI_THREAD();
+    DCHECK(server_);
+    complete_callback_ = complete_callback;
+    server_->Shutdown();
+  }
+
+  // CefServerHandler methods are called on the server thread.
+
+  void OnServerCreated(CefRefPtr<CefServer> server) override {
+    DCHECK(!server_);
+    server_ = server;
+    RunCompleteCallback(server->IsRunning());
+  }
+
+  void OnServerDestroyed(CefRefPtr<CefServer> server) override {
+    DCHECK(server_);
+    server_ = nullptr;
+    RunCompleteCallback(true);
+  }
+
+  void OnClientConnected(CefRefPtr<CefServer> server,
+                         int connection_id) override {}
+
+  void OnClientDisconnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {}
+
+  void OnHttpRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const CefString& client_address,
+                     CefRefPtr<CefRequest> request) override {
+    // Parse the request URL and retrieve the path without leading slash.
+    CefURLParts url_parts;
+    CefParseURL(request->GetURL(), url_parts);
+    std::string path = CefString(&url_parts.path);
+    if (!path.empty() && path[0] == '/')
+      path = path.substr(1);
+
+    if (path.empty())
+      path = kDefaultPath;
+
+    std::string mime_type;
+    const size_t sep = path.find_last_of(".");
+    if (sep != std::string::npos) {
+      // Determine the mime type based on the extension.
+      mime_type = CefGetMimeType(path.substr(sep + 1));
+    } else {
+      // No extension. Assume html.
+      path += ".html";
+    }
+    if (mime_type.empty())
+      mime_type = "text/html";
+
+    CefRefPtr<CefStreamReader> stream;
+    CefResponse::HeaderMap extra_headers;
+
+    if (path == "request.html") {
+      // Return the request contents.
+      stream = test_runner::GetDumpResponse(request, extra_headers);
+    }
+
+    if (!stream) {
+      // Load any resource supported by cefclient.
+      stream = GetBinaryResourceReader(path.c_str());
+    }
+
+    if (stream) {
+      SendHttpResponseStream(server, connection_id, mime_type, stream,
+                             extra_headers);
+    } else {
+      server->SendHttp404Response(connection_id);
+    }
+  }
+
+  void OnWebSocketRequest(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const CefString& client_address,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefCallback> callback) override {
+    // Always accept WebSocket connections.
+    callback->Continue();
+  }
+
+  void OnWebSocketConnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {}
+
+  void OnWebSocketMessage(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const void* data,
+                          size_t data_size) override {
+    // Echo the reverse of the message.
+    std::string message(static_cast<const char*>(data), data_size);
+    std::reverse(message.begin(), message.end());
+
+    server->SendWebSocketMessage(connection_id, message.data(), message.size());
+  }
+
+  int port() const { return port_; }
+
+ private:
+  void RunCompleteCallback(bool success) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&ServerHandler::RunCompleteCallback, this,
+                                     success));
+      return;
+    }
+
+    if (!complete_callback_.is_null()) {
+      complete_callback_.Run(success);
+      complete_callback_.Reset();
+    }
+  }
+
+  static void SendHttpResponseStream(CefRefPtr<CefServer> server,
+                                     int connection_id,
+                                     const std::string& mime_type,
+                                     CefRefPtr<CefStreamReader> stream,
+                                     CefResponse::HeaderMap extra_headers) {
+    // Determine the stream size.
+    stream->Seek(0, SEEK_END);
+    int64 content_length = stream->Tell();
+    stream->Seek(0, SEEK_SET);
+
+    // Send response headers.
+    server->SendHttpResponse(connection_id, 200, mime_type, content_length,
+                             extra_headers);
+
+    // Send stream contents.
+    char buffer[8192];
+    size_t read;
+    do {
+      read = stream->Read(buffer, 1, sizeof(buffer));
+      if (read > 0)
+        server->SendRawData(connection_id, buffer, read);
+    } while (!stream->Eof() && read != 0);
+
+    // Close the connection.
+    server->CloseConnection(connection_id);
+  }
+
+  CefRefPtr<CefServer> server_;
+
+  // The below members are only accessed on the UI thread.
+  int port_;
+  CompleteCallback complete_callback_;
+
+  IMPLEMENT_REFCOUNTING(ServerHandler);
+  DISALLOW_COPY_AND_ASSIGN(ServerHandler);
+};
+
+// Handle messages in the browser process.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  Handler() : weak_ptr_factory_(this) {}
+
+  virtual ~Handler() {
+    if (handler_) {
+      handler_->StopServer(ServerHandler::CompleteCallback());
+      handler_ = nullptr;
+    }
+  }
+
+  // Called due to cefQuery execution in server.html.
+  virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id,
+                       const CefString& request,
+                       bool persistent,
+                       CefRefPtr<Callback> callback) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (url.find(kTestUrl) != 0)
+      return false;
+
+    // Parse |request| as a JSON dictionary.
+    CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
+    if (!request_dict) {
+      callback->Failure(kMessageFormatError, "Incorrect message format");
+      return true;
+    }
+
+    if (!VerifyKey(request_dict, kActionKey, VTYPE_STRING, callback))
+      return true;
+
+    const std::string& action = request_dict->GetString(kActionKey);
+    if (action == "query") {
+      HandleQueryAction(request_dict, callback);
+    } else if (action == "start") {
+      HandleStartAction(request_dict, callback);
+    } else if (action == "stop") {
+      HandleStopAction(request_dict, callback);
+    } else {
+      callback->Failure(kMessageFormatError, "Unrecognized action: " + action);
+    }
+
+    return true;
+  }
+
+ private:
+  // Return current server status.
+  void HandleQueryAction(CefRefPtr<CefDictionaryValue> request_dict,
+                         CefRefPtr<Callback> callback) {
+    CefRefPtr<CefDictionaryValue> result_dict = CefDictionaryValue::Create();
+    if (handler_) {
+      result_dict->SetInt(kPortKey, handler_->port());
+      result_dict->SetString(kStatusKey, "running");
+    } else {
+      result_dict->SetInt(kPortKey, kServerPortDefault);
+      result_dict->SetString(kStatusKey, "stopped");
+    }
+    SendResponse(callback, true, result_dict);
+  }
+
+  // Start the server.
+  void HandleStartAction(CefRefPtr<CefDictionaryValue> request_dict,
+                         CefRefPtr<Callback> callback) {
+    if (handler_) {
+      callback->Failure(kActionStateError, "Server is currently running");
+      return;
+    }
+
+    if (!VerifyKey(request_dict, kPortKey, VTYPE_INT, callback))
+      return;
+
+    const int port = request_dict->GetInt(kPortKey);
+    if (port < 8000 || port > 65535) {
+      callback->Failure(kMessageFormatError, "Invalid port number specified");
+      return;
+    }
+
+    handler_ = new ServerHandler();
+
+    // Start the server. OnComplete will be executed upon completion.
+    handler_->StartServer(port,
+                          base::Bind(&Handler::OnStartComplete,
+                                     weak_ptr_factory_.GetWeakPtr(), callback));
+  }
+
+  // Stop the server.
+  void HandleStopAction(CefRefPtr<CefDictionaryValue> request_dict,
+                        CefRefPtr<Callback> callback) {
+    if (!handler_) {
+      callback->Failure(kActionStateError, "Server is not currently running");
+      return;
+    }
+
+    // Stop the server. OnComplete will be executed upon completion.
+    handler_->StopServer(base::Bind(&Handler::OnStopComplete,
+                                    weak_ptr_factory_.GetWeakPtr(), callback));
+
+    handler_ = nullptr;
+  }
+
+  // Server start completed.
+  void OnStartComplete(CefRefPtr<Callback> callback, bool success) {
+    CEF_REQUIRE_UI_THREAD();
+    CefRefPtr<CefDictionaryValue> result_dict = CefDictionaryValue::Create();
+    if (!success) {
+      handler_ = nullptr;
+      result_dict->SetString(kMessageKey, "Server failed to start.");
+    }
+    SendResponse(callback, success, result_dict);
+  }
+
+  // Server stop completed.
+  void OnStopComplete(CefRefPtr<Callback> callback, bool success) {
+    CEF_REQUIRE_UI_THREAD();
+    CefRefPtr<CefDictionaryValue> result_dict = CefDictionaryValue::Create();
+    if (!success) {
+      result_dict->SetString(kMessageKey, "Server failed to stop.");
+    }
+    SendResponse(callback, success, result_dict);
+  }
+
+  // Send a response in the format expected by server.html.
+  static void SendResponse(CefRefPtr<Callback> callback,
+                           bool success,
+                           CefRefPtr<CefDictionaryValue> result_dict) {
+    if (!result_dict) {
+      result_dict = CefDictionaryValue::Create();
+    }
+    result_dict->SetString(kResultKey, success ? "success" : "failure");
+    CefRefPtr<CefValue> value = CefValue::Create();
+    value->SetDictionary(result_dict);
+    const std::string& response = CefWriteJSON(value, JSON_WRITER_DEFAULT);
+    callback->Success(response);
+  }
+
+  // Convert a JSON string to a dictionary value.
+  static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
+    CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
+    if (value.get() && value->GetType() == VTYPE_DICTIONARY)
+      return value->GetDictionary();
+    return nullptr;
+  }
+
+  // Verify that |key| exists in |dictionary| and has type |value_type|. Fails
+  // |callback| and returns false on failure.
+  static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
+                        const char* key,
+                        cef_value_type_t value_type,
+                        CefRefPtr<Callback> callback) {
+    if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
+      callback->Failure(
+          kMessageFormatError,
+          "Missing or incorrectly formatted message key: " + std::string(key));
+      return false;
+    }
+    return true;
+  }
+
+  // Non-nullptr while the server is running.
+  CefRefPtr<ServerHandler> handler_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<Handler> weak_ptr_factory_;
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace server_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/server_test.h b/src/tests/cefclient/browser/server_test.h
new file mode 100644
index 0000000..ffe3172
--- /dev/null
+++ b/src/tests/cefclient/browser/server_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_SERVER_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_SERVER_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace server_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace server_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_SERVER_TEST_H_
diff --git a/src/tests/cefclient/browser/temp_window.h b/src/tests/cefclient/browser/temp_window.h
new file mode 100644
index 0000000..499c80a
--- /dev/null
+++ b/src/tests/cefclient/browser/temp_window.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_H_
+#pragma once
+
+#include "tests/cefclient/browser/client_types.h"
+
+#if defined(OS_WIN)
+#include "tests/cefclient/browser/temp_window_win.h"
+#elif defined(OS_LINUX)
+#include "tests/cefclient/browser/temp_window_x11.h"
+#elif defined(OS_MACOSX)
+#include "tests/cefclient/browser/temp_window_mac.h"
+#endif
+
+namespace client {
+
+#if defined(OS_WIN)
+typedef TempWindowWin TempWindow;
+#elif defined(OS_LINUX)
+typedef TempWindowX11 TempWindow;
+#elif defined(OS_MACOSX)
+typedef TempWindowMac TempWindow;
+#endif
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_H_
diff --git a/src/tests/cefclient/browser/temp_window_mac.h b/src/tests/cefclient/browser/temp_window_mac.h
new file mode 100644
index 0000000..c64062f
--- /dev/null
+++ b/src/tests/cefclient/browser/temp_window_mac.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_MAC_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_MAC_H_
+#pragma once
+
+#include "tests/cefclient/browser/client_types.h"
+
+namespace client {
+
+class TempWindowMacImpl;
+
+// Represents a singleton hidden window that acts as a temporary parent for
+// popup browsers. Only accessed on the UI thread.
+class TempWindowMac {
+ public:
+  // Returns the singleton window handle.
+  static CefWindowHandle GetWindowHandle();
+
+ private:
+  // A single instance will be created/owned by RootWindowManager.
+  friend class RootWindowManager;
+  // Allow deletion via scoped_ptr only.
+  friend struct base::DefaultDeleter<TempWindowMac>;
+
+  TempWindowMac();
+  ~TempWindowMac();
+
+  scoped_ptr<TempWindowMacImpl> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(TempWindowMac);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_MAC_H_
diff --git a/src/tests/cefclient/browser/temp_window_mac.mm b/src/tests/cefclient/browser/temp_window_mac.mm
new file mode 100644
index 0000000..6aff01c
--- /dev/null
+++ b/src/tests/cefclient/browser/temp_window_mac.mm
@@ -0,0 +1,59 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/temp_window_mac.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_app.h"
+
+namespace client {
+
+namespace {
+
+TempWindowMac* g_temp_window = NULL;
+
+}  // namespace
+
+class TempWindowMacImpl {
+ public:
+  TempWindowMacImpl() {
+    // Create a borderless non-visible 1x1 window.
+    window_ = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1)
+                                          styleMask:NSBorderlessWindowMask
+                                            backing:NSBackingStoreBuffered
+                                              defer:NO];
+    CHECK(window_);
+  }
+  ~TempWindowMacImpl() {
+    DCHECK(window_);
+    [window_ close];
+  }
+
+ private:
+  NSWindow* window_;
+
+  friend class TempWindowMac;
+};
+
+TempWindowMac::TempWindowMac() {
+  DCHECK(!g_temp_window);
+  impl_.reset(new TempWindowMacImpl);
+  g_temp_window = this;
+}
+
+TempWindowMac::~TempWindowMac() {
+  impl_.reset();
+  g_temp_window = NULL;
+}
+
+// static
+CefWindowHandle TempWindowMac::GetWindowHandle() {
+  DCHECK(g_temp_window);
+  return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(
+      g_temp_window->impl_->window_.contentView);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/temp_window_win.cc b/src/tests/cefclient/browser/temp_window_win.cc
new file mode 100644
index 0000000..af5cdf9
--- /dev/null
+++ b/src/tests/cefclient/browser/temp_window_win.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/temp_window_win.h"
+
+#include <windows.h>
+
+#include "include/base/cef_logging.h"
+
+namespace client {
+
+namespace {
+
+const wchar_t kWndClass[] = L"Client_TempWindow";
+
+// Create the temp window.
+HWND CreateTempWindow() {
+  HINSTANCE hInstance = ::GetModuleHandle(NULL);
+
+  WNDCLASSEX wc = {0};
+  wc.cbSize = sizeof(wc);
+  wc.lpfnWndProc = DefWindowProc;
+  wc.hInstance = hInstance;
+  wc.lpszClassName = kWndClass;
+  RegisterClassEx(&wc);
+
+  // Create a 1x1 pixel hidden window.
+  return CreateWindow(kWndClass, 0, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 0, 0,
+                      1, 1, NULL, NULL, hInstance, NULL);
+}
+
+TempWindowWin* g_temp_window = nullptr;
+
+}  // namespace
+
+TempWindowWin::TempWindowWin() : hwnd_(NULL) {
+  DCHECK(!g_temp_window);
+  g_temp_window = this;
+
+  hwnd_ = CreateTempWindow();
+  CHECK(hwnd_);
+}
+
+TempWindowWin::~TempWindowWin() {
+  g_temp_window = nullptr;
+  DCHECK(hwnd_);
+  DestroyWindow(hwnd_);
+}
+
+// static
+CefWindowHandle TempWindowWin::GetWindowHandle() {
+  DCHECK(g_temp_window);
+  return g_temp_window->hwnd_;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/temp_window_win.h b/src/tests/cefclient/browser/temp_window_win.h
new file mode 100644
index 0000000..b5f312e
--- /dev/null
+++ b/src/tests/cefclient/browser/temp_window_win.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_WIN_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+namespace client {
+
+// Represents a singleton hidden window that acts as a temporary parent for
+// popup browsers. Only accessed on the UI thread.
+class TempWindowWin {
+ public:
+  // Returns the singleton window handle.
+  static CefWindowHandle GetWindowHandle();
+
+ private:
+  // A single instance will be created/owned by RootWindowManager.
+  friend class RootWindowManager;
+  // Allow deletion via scoped_ptr only.
+  friend struct base::DefaultDeleter<TempWindowWin>;
+
+  TempWindowWin();
+  ~TempWindowWin();
+
+  CefWindowHandle hwnd_;
+
+  DISALLOW_COPY_AND_ASSIGN(TempWindowWin);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_WIN_H_
diff --git a/src/tests/cefclient/browser/temp_window_x11.cc b/src/tests/cefclient/browser/temp_window_x11.cc
new file mode 100644
index 0000000..de9f45d
--- /dev/null
+++ b/src/tests/cefclient/browser/temp_window_x11.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/temp_window_x11.h"
+
+#include <X11/Xlib.h>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_app.h"
+
+namespace client {
+
+namespace {
+
+// Create the temp window.
+::Window CreateTempWindow() {
+  ::Display* xdisplay = cef_get_xdisplay();
+  CHECK(xdisplay != 0);
+  ::Window parent_xwindow = DefaultRootWindow(xdisplay);
+
+  XSetWindowAttributes swa;
+  memset(&swa, 0, sizeof(swa));
+  swa.background_pixmap = None;
+  swa.override_redirect = false;
+  return XCreateWindow(xdisplay, parent_xwindow, 0, 0, 1, 1,  // size (1x1px)
+                       0,                                     // border width
+                       CopyFromParent,                        // depth
+                       InputOutput,
+                       CopyFromParent,  // visual
+                       CWBackPixmap | CWOverrideRedirect, &swa);
+}
+
+// Close the temp window.
+void CloseTempWindow(::Window xwindow) {
+  ::Display* xdisplay = cef_get_xdisplay();
+  CHECK(xdisplay != 0);
+  XDestroyWindow(xdisplay, xwindow);
+}
+
+TempWindowX11* g_temp_window = nullptr;
+
+}  // namespace
+
+TempWindowX11::TempWindowX11() : xwindow_(kNullWindowHandle) {
+  DCHECK(!g_temp_window);
+  g_temp_window = this;
+
+  xwindow_ = CreateTempWindow();
+  CHECK(xwindow_);
+}
+
+TempWindowX11::~TempWindowX11() {
+  g_temp_window = nullptr;
+  DCHECK(xwindow_);
+
+  CloseTempWindow(xwindow_);
+}
+
+// static
+CefWindowHandle TempWindowX11::GetWindowHandle() {
+  DCHECK(g_temp_window);
+  return g_temp_window->xwindow_;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/temp_window_x11.h b/src/tests/cefclient/browser/temp_window_x11.h
new file mode 100644
index 0000000..08e452e
--- /dev/null
+++ b/src/tests/cefclient/browser/temp_window_x11.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_X11_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_X11_H_
+#pragma once
+
+#include "include/cef_base.h"
+
+namespace client {
+
+// Represents a singleton hidden window that acts as a temporary parent for
+// popup browsers. Only accessed on the UI thread.
+class TempWindowX11 {
+ public:
+  // Returns the singleton window handle.
+  static CefWindowHandle GetWindowHandle();
+
+ private:
+  // A single instance will be created/owned by RootWindowManager.
+  friend class RootWindowManager;
+  // Allow deletion via scoped_ptr only.
+  friend struct base::DefaultDeleter<TempWindowX11>;
+
+  TempWindowX11();
+  ~TempWindowX11();
+
+  CefWindowHandle xwindow_;
+
+  DISALLOW_COPY_AND_ASSIGN(TempWindowX11);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_TEMP_WINDOW_X11_H_
diff --git a/src/tests/cefclient/browser/test_runner.cc b/src/tests/cefclient/browser/test_runner.cc
new file mode 100644
index 0000000..68805e9
--- /dev/null
+++ b/src/tests/cefclient/browser/test_runner.cc
@@ -0,0 +1,918 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/test_runner.h"
+
+#include <map>
+#include <set>
+#include <sstream>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_parser.h"
+#include "include/cef_task.h"
+#include "include/cef_trace.h"
+#include "include/cef_web_plugin.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/cefclient/browser/binding_test.h"
+#include "tests/cefclient/browser/client_handler.h"
+#include "tests/cefclient/browser/dialog_test.h"
+#include "tests/cefclient/browser/drm_test.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/media_router_test.h"
+#include "tests/cefclient/browser/preferences_test.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/cefclient/browser/response_filter_test.h"
+#include "tests/cefclient/browser/root_window_manager.h"
+#include "tests/cefclient/browser/scheme_test.h"
+#include "tests/cefclient/browser/server_test.h"
+#include "tests/cefclient/browser/urlrequest_test.h"
+#include "tests/cefclient/browser/window_test.h"
+#include "tests/shared/browser/resource_util.h"
+
+namespace client {
+namespace test_runner {
+
+namespace {
+
+const char kTestHost[] = "tests";
+const char kLocalHost[] = "localhost";
+const char kTestOrigin[] = "http://tests/";
+
+// Pages handled via StringResourceProvider.
+const char kTestGetSourcePage[] = "get_source.html";
+const char kTestGetTextPage[] = "get_text.html";
+const char kTestPluginInfoPage[] = "plugin_info.html";
+
+// Set page data and navigate the browser. Used in combination with
+// StringResourceProvider.
+void LoadStringResourcePage(CefRefPtr<CefBrowser> browser,
+                            const std::string& page,
+                            const std::string& data) {
+  CefRefPtr<CefClient> client = browser->GetHost()->GetClient();
+  ClientHandler* client_handler = static_cast<ClientHandler*>(client.get());
+  client_handler->SetStringResource(page, data);
+  browser->GetMainFrame()->LoadURL(kTestOrigin + page);
+}
+
+// Replace all instances of |from| with |to| in |str|.
+std::string StringReplace(const std::string& str,
+                          const std::string& from,
+                          const std::string& to) {
+  std::string result = str;
+  std::string::size_type pos = 0;
+  std::string::size_type from_len = from.length();
+  std::string::size_type to_len = to.length();
+  do {
+    pos = result.find(from, pos);
+    if (pos != std::string::npos) {
+      result.replace(pos, from_len, to);
+      pos += to_len;
+    }
+  } while (pos != std::string::npos);
+  return result;
+}
+
+void RunGetSourceTest(CefRefPtr<CefBrowser> browser) {
+  class Visitor : public CefStringVisitor {
+   public:
+    explicit Visitor(CefRefPtr<CefBrowser> browser) : browser_(browser) {}
+    virtual void Visit(const CefString& string) OVERRIDE {
+      std::string source = StringReplace(string, "<", "&lt;");
+      source = StringReplace(source, ">", "&gt;");
+      std::stringstream ss;
+      ss << "<html><body bgcolor=\"white\">Source:<pre>" << source
+         << "</pre></body></html>";
+      LoadStringResourcePage(browser_, kTestGetSourcePage, ss.str());
+    }
+
+   private:
+    CefRefPtr<CefBrowser> browser_;
+    IMPLEMENT_REFCOUNTING(Visitor);
+  };
+
+  browser->GetMainFrame()->GetSource(new Visitor(browser));
+}
+
+void RunGetTextTest(CefRefPtr<CefBrowser> browser) {
+  class Visitor : public CefStringVisitor {
+   public:
+    explicit Visitor(CefRefPtr<CefBrowser> browser) : browser_(browser) {}
+    virtual void Visit(const CefString& string) OVERRIDE {
+      std::string text = StringReplace(string, "<", "&lt;");
+      text = StringReplace(text, ">", "&gt;");
+      std::stringstream ss;
+      ss << "<html><body bgcolor=\"white\">Text:<pre>" << text
+         << "</pre></body></html>";
+      LoadStringResourcePage(browser_, kTestGetTextPage, ss.str());
+    }
+
+   private:
+    CefRefPtr<CefBrowser> browser_;
+    IMPLEMENT_REFCOUNTING(Visitor);
+  };
+
+  browser->GetMainFrame()->GetText(new Visitor(browser));
+}
+
+void RunRequestTest(CefRefPtr<CefBrowser> browser) {
+  // Create a new request
+  CefRefPtr<CefRequest> request(CefRequest::Create());
+
+  if (browser->GetMainFrame()->GetURL().ToString().find("http://tests/") != 0) {
+    // The LoadRequest method will fail with "bad IPC message" reason
+    // INVALID_INITIATOR_ORIGIN (213) unless you first navigate to the
+    // request origin using some other mechanism (LoadURL, link click, etc).
+    Alert(browser,
+          "Please first navigate to a http://tests/ URL. "
+          "For example, first load Tests > Other Tests.");
+    return;
+  }
+
+  // Set the request URL
+  request->SetURL("http://tests/request");
+
+  // Add post data to the request.  The correct method and content-
+  // type headers will be set by CEF.
+  CefRefPtr<CefPostDataElement> postDataElement(CefPostDataElement::Create());
+  std::string data = "arg1=val1&arg2=val2";
+  postDataElement->SetToBytes(data.length(), data.c_str());
+  CefRefPtr<CefPostData> postData(CefPostData::Create());
+  postData->AddElement(postDataElement);
+  request->SetPostData(postData);
+
+  // Add a custom header
+  CefRequest::HeaderMap headerMap;
+  headerMap.insert(std::make_pair("X-My-Header", "My Header Value"));
+  request->SetHeaderMap(headerMap);
+
+  // Load the request
+  browser->GetMainFrame()->LoadRequest(request);
+}
+
+void RunNewWindowTest(CefRefPtr<CefBrowser> browser) {
+  RootWindowConfig config;
+  config.with_controls = true;
+  config.with_osr = browser->GetHost()->IsWindowRenderingDisabled();
+  MainContext::Get()->GetRootWindowManager()->CreateRootWindow(config);
+}
+
+void RunPopupWindowTest(CefRefPtr<CefBrowser> browser) {
+  browser->GetMainFrame()->ExecuteJavaScript(
+      "window.open('http://www.google.com');", "about:blank", 0);
+}
+
+void RunPluginInfoTest(CefRefPtr<CefBrowser> browser) {
+  class Visitor : public CefWebPluginInfoVisitor {
+   public:
+    explicit Visitor(CefRefPtr<CefBrowser> browser) : browser_(browser) {
+      html_ =
+          "<html><head><title>Plugin Info Test</title></head>"
+          "<body bgcolor=\"white\">"
+          "\n<b>Installed plugins:</b>";
+    }
+    ~Visitor() {
+      html_ += "\n</body></html>";
+
+      // Load the html in the browser.
+      LoadStringResourcePage(browser_, kTestPluginInfoPage, html_);
+    }
+
+    virtual bool Visit(CefRefPtr<CefWebPluginInfo> info,
+                       int count,
+                       int total) OVERRIDE {
+      html_ += "\n<br/><br/>Name: " + info->GetName().ToString() +
+               "\n<br/>Description: " + info->GetDescription().ToString() +
+               "\n<br/>Version: " + info->GetVersion().ToString() +
+               "\n<br/>Path: " + info->GetPath().ToString();
+      return true;
+    }
+
+   private:
+    std::string html_;
+    CefRefPtr<CefBrowser> browser_;
+    IMPLEMENT_REFCOUNTING(Visitor);
+  };
+
+  CefVisitWebPluginInfo(new Visitor(browser));
+}
+
+void ModifyZoom(CefRefPtr<CefBrowser> browser, double delta) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&ModifyZoom, browser, delta));
+    return;
+  }
+
+  browser->GetHost()->SetZoomLevel(browser->GetHost()->GetZoomLevel() + delta);
+}
+
+const char kPrompt[] = "Prompt.";
+const char kPromptFPS[] = "FPS";
+const char kPromptDSF[] = "DSF";
+
+// Handles execution of prompt results.
+class PromptHandler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  PromptHandler() {}
+
+  // Called due to cefQuery execution.
+  virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id,
+                       const CefString& request,
+                       bool persistent,
+                       CefRefPtr<Callback> callback) OVERRIDE {
+    // Parse |request| which takes the form "Prompt.[type]:[value]".
+    const std::string& request_str = request;
+    if (request_str.find(kPrompt) != 0)
+      return false;
+
+    std::string type = request_str.substr(sizeof(kPrompt) - 1);
+    size_t delim = type.find(':');
+    if (delim == std::string::npos)
+      return false;
+
+    const std::string& value = type.substr(delim + 1);
+    type = type.substr(0, delim);
+
+    // Canceling the prompt dialog returns a value of "null".
+    if (value != "null") {
+      if (type == kPromptFPS)
+        SetFPS(browser, atoi(value.c_str()));
+      else if (type == kPromptDSF)
+        SetDSF(browser, static_cast<float>(atof(value.c_str())));
+    }
+
+    // Nothing is done with the response.
+    callback->Success(CefString());
+    return true;
+  }
+
+ private:
+  void SetFPS(CefRefPtr<CefBrowser> browser, int fps) {
+    if (fps <= 0) {
+      // Reset to the default value.
+      CefBrowserSettings settings;
+      MainContext::Get()->PopulateBrowserSettings(&settings);
+      fps = settings.windowless_frame_rate;
+    }
+
+    browser->GetHost()->SetWindowlessFrameRate(fps);
+  }
+
+  void SetDSF(CefRefPtr<CefBrowser> browser, float dsf) {
+    MainMessageLoop::Get()->PostClosure(
+        base::Bind(&PromptHandler::SetDSFOnMainThread, browser, dsf));
+  }
+
+  static void SetDSFOnMainThread(CefRefPtr<CefBrowser> browser, float dsf) {
+    RootWindow::GetForBrowser(browser->GetIdentifier())
+        ->SetDeviceScaleFactor(dsf);
+  }
+};
+
+void Prompt(CefRefPtr<CefBrowser> browser,
+            const std::string& type,
+            const std::string& label,
+            const std::string& default_value) {
+  // Prompt the user for a new value. Works as follows:
+  // 1. Show a prompt() dialog via JavaScript.
+  // 2. Pass the result to window.cefQuery().
+  // 3. Handle the result in PromptHandler::OnQuery.
+  const std::string& code = "window.cefQuery({'request': '" +
+                            std::string(kPrompt) + type + ":' + prompt('" +
+                            label + "', '" + default_value + "')});";
+  browser->GetMainFrame()->ExecuteJavaScript(
+      code, browser->GetMainFrame()->GetURL(), 0);
+}
+
+void PromptFPS(CefRefPtr<CefBrowser> browser) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&PromptFPS, browser));
+    return;
+  }
+
+  // Format the default value string.
+  std::stringstream ss;
+  ss << browser->GetHost()->GetWindowlessFrameRate();
+
+  Prompt(browser, kPromptFPS, "Enter FPS", ss.str());
+}
+
+void PromptDSF(CefRefPtr<CefBrowser> browser) {
+  if (!MainMessageLoop::Get()->RunsTasksOnCurrentThread()) {
+    // Execute on the main thread.
+    MainMessageLoop::Get()->PostClosure(base::Bind(&PromptDSF, browser));
+    return;
+  }
+
+  // Format the default value string.
+  std::stringstream ss;
+  ss << RootWindow::GetForBrowser(browser->GetIdentifier())
+            ->GetDeviceScaleFactor();
+
+  Prompt(browser, kPromptDSF, "Enter Device Scale Factor", ss.str());
+}
+
+void BeginTracing() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&BeginTracing));
+    return;
+  }
+
+  CefBeginTracing(CefString(), nullptr);
+}
+
+void EndTracing(CefRefPtr<CefBrowser> browser) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&EndTracing, browser));
+    return;
+  }
+
+  class Client : public CefEndTracingCallback, public CefRunFileDialogCallback {
+   public:
+    explicit Client(CefRefPtr<CefBrowser> browser) : browser_(browser) {
+      RunDialog();
+    }
+
+    void RunDialog() {
+      static const char kDefaultFileName[] = "trace.txt";
+      std::string path = MainContext::Get()->GetDownloadPath(kDefaultFileName);
+      if (path.empty())
+        path = kDefaultFileName;
+
+      // Results in a call to OnFileDialogDismissed.
+      browser_->GetHost()->RunFileDialog(
+          static_cast<cef_file_dialog_mode_t>(FILE_DIALOG_SAVE |
+                                              FILE_DIALOG_OVERWRITEPROMPT_FLAG),
+          CefString(),  // title
+          path,
+          std::vector<CefString>(),  // accept_filters
+          0,                         // selected_accept_filter
+          this);
+    }
+
+    void OnFileDialogDismissed(
+        int selected_accept_filter,
+        const std::vector<CefString>& file_paths) OVERRIDE {
+      if (!file_paths.empty()) {
+        // File selected. Results in a call to OnEndTracingComplete.
+        CefEndTracing(file_paths.front(), this);
+      } else {
+        // No file selected. Discard the trace data.
+        CefEndTracing(CefString(), nullptr);
+      }
+    }
+
+    void OnEndTracingComplete(const CefString& tracing_file) OVERRIDE {
+      Alert(browser_,
+            "File \"" + tracing_file.ToString() + "\" saved successfully.");
+    }
+
+   private:
+    CefRefPtr<CefBrowser> browser_;
+
+    IMPLEMENT_REFCOUNTING(Client);
+  };
+
+  new Client(browser);
+}
+
+void PrintToPDF(CefRefPtr<CefBrowser> browser) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&PrintToPDF, browser));
+    return;
+  }
+
+  class Client : public CefPdfPrintCallback, public CefRunFileDialogCallback {
+   public:
+    explicit Client(CefRefPtr<CefBrowser> browser) : browser_(browser) {
+      RunDialog();
+    }
+
+    void RunDialog() {
+      static const char kDefaultFileName[] = "output.pdf";
+      std::string path = MainContext::Get()->GetDownloadPath(kDefaultFileName);
+      if (path.empty())
+        path = kDefaultFileName;
+
+      std::vector<CefString> accept_filters;
+      accept_filters.push_back(".pdf");
+
+      // Results in a call to OnFileDialogDismissed.
+      browser_->GetHost()->RunFileDialog(
+          static_cast<cef_file_dialog_mode_t>(FILE_DIALOG_SAVE |
+                                              FILE_DIALOG_OVERWRITEPROMPT_FLAG),
+          CefString(),  // title
+          path, accept_filters,
+          0,  // selected_accept_filter
+          this);
+    }
+
+    void OnFileDialogDismissed(
+        int selected_accept_filter,
+        const std::vector<CefString>& file_paths) OVERRIDE {
+      if (!file_paths.empty()) {
+        CefPdfPrintSettings settings;
+
+        // Show the URL in the footer.
+        settings.header_footer_enabled = true;
+        CefString(&settings.header_footer_url) =
+            browser_->GetMainFrame()->GetURL();
+
+        // Print to the selected PDF file.
+        browser_->GetHost()->PrintToPDF(file_paths[0], settings, this);
+      }
+    }
+
+    void OnPdfPrintFinished(const CefString& path, bool ok) OVERRIDE {
+      Alert(browser_, "File \"" + path.ToString() + "\" " +
+                          (ok ? "saved successfully." : "failed to save."));
+    }
+
+   private:
+    CefRefPtr<CefBrowser> browser_;
+
+    IMPLEMENT_REFCOUNTING(Client);
+  };
+
+  new Client(browser);
+}
+
+void MuteAudio(CefRefPtr<CefBrowser> browser, bool mute) {
+  CefRefPtr<CefBrowserHost> host = browser->GetHost();
+  host->SetAudioMuted(mute);
+}
+
+void RunOtherTests(CefRefPtr<CefBrowser> browser) {
+  browser->GetMainFrame()->LoadURL("http://tests/other_tests");
+}
+
+// Provider that dumps the request contents.
+class RequestDumpResourceProvider : public CefResourceManager::Provider {
+ public:
+  explicit RequestDumpResourceProvider(const std::string& url) : url_(url) {
+    DCHECK(!url.empty());
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+
+    const std::string& url = request->url();
+    if (url != url_) {
+      // Not handled by this provider.
+      return false;
+    }
+
+    CefResponse::HeaderMap response_headers;
+    CefRefPtr<CefStreamReader> response =
+        GetDumpResponse(request->request(), response_headers);
+
+    request->Continue(new CefStreamResourceHandler(200, "OK", "text/html",
+                                                   response_headers, response));
+    return true;
+  }
+
+ private:
+  std::string url_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestDumpResourceProvider);
+};
+
+// Provider that returns string data for specific pages. Used in combination
+// with LoadStringResourcePage().
+class StringResourceProvider : public CefResourceManager::Provider {
+ public:
+  StringResourceProvider(const std::set<std::string>& pages,
+                         StringResourceMap* string_resource_map)
+      : pages_(pages), string_resource_map_(string_resource_map) {
+    DCHECK(!pages.empty());
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+
+    const std::string& url = request->url();
+    if (url.find(kTestOrigin) != 0U) {
+      // Not handled by this provider.
+      return false;
+    }
+
+    const std::string& page = url.substr(strlen(kTestOrigin));
+    if (pages_.find(page) == pages_.end()) {
+      // Not handled by this provider.
+      return false;
+    }
+
+    std::string value;
+    StringResourceMap::const_iterator it = string_resource_map_->find(page);
+    if (it != string_resource_map_->end()) {
+      value = it->second;
+    } else {
+      value = "<html><body>No data available</body></html>";
+    }
+
+    CefRefPtr<CefStreamReader> response = CefStreamReader::CreateForData(
+        static_cast<void*>(const_cast<char*>(value.c_str())), value.size());
+
+    request->Continue(new CefStreamResourceHandler(
+        200, "OK", "text/html", CefResponse::HeaderMap(), response));
+    return true;
+  }
+
+ private:
+  const std::set<std::string> pages_;
+
+  // Only accessed on the IO thread.
+  StringResourceMap* string_resource_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(StringResourceProvider);
+};
+
+// Add a file extension to |url| if none is currently specified.
+std::string RequestUrlFilter(const std::string& url) {
+  if (url.find(kTestOrigin) != 0U) {
+    // Don't filter anything outside of the test origin.
+    return url;
+  }
+
+  // Identify where the query or fragment component, if any, begins.
+  size_t suffix_pos = url.find('?');
+  if (suffix_pos == std::string::npos)
+    suffix_pos = url.find('#');
+
+  std::string url_base, url_suffix;
+  if (suffix_pos == std::string::npos) {
+    url_base = url;
+  } else {
+    url_base = url.substr(0, suffix_pos);
+    url_suffix = url.substr(suffix_pos);
+  }
+
+  // Identify the last path component.
+  size_t path_pos = url_base.rfind('/');
+  if (path_pos == std::string::npos)
+    return url;
+
+  const std::string& path_component = url_base.substr(path_pos);
+
+  // Identify if a file extension is currently specified.
+  size_t ext_pos = path_component.rfind(".");
+  if (ext_pos != std::string::npos)
+    return url;
+
+  // Rebuild the URL with a file extension.
+  return url_base + ".html" + url_suffix;
+}
+
+}  // namespace
+
+void RunTest(CefRefPtr<CefBrowser> browser, int id) {
+  if (!browser)
+    return;
+
+  switch (id) {
+    case ID_TESTS_GETSOURCE:
+      RunGetSourceTest(browser);
+      break;
+    case ID_TESTS_GETTEXT:
+      RunGetTextTest(browser);
+      break;
+    case ID_TESTS_WINDOW_NEW:
+      RunNewWindowTest(browser);
+      break;
+    case ID_TESTS_WINDOW_POPUP:
+      RunPopupWindowTest(browser);
+      break;
+    case ID_TESTS_REQUEST:
+      RunRequestTest(browser);
+      break;
+    case ID_TESTS_PLUGIN_INFO:
+      RunPluginInfoTest(browser);
+      break;
+    case ID_TESTS_ZOOM_IN:
+      ModifyZoom(browser, 0.5);
+      break;
+    case ID_TESTS_ZOOM_OUT:
+      ModifyZoom(browser, -0.5);
+      break;
+    case ID_TESTS_ZOOM_RESET:
+      browser->GetHost()->SetZoomLevel(0.0);
+      break;
+    case ID_TESTS_OSR_FPS:
+      PromptFPS(browser);
+      break;
+    case ID_TESTS_OSR_DSF:
+      PromptDSF(browser);
+      break;
+    case ID_TESTS_TRACING_BEGIN:
+      BeginTracing();
+      break;
+    case ID_TESTS_TRACING_END:
+      EndTracing(browser);
+      break;
+    case ID_TESTS_PRINT:
+      browser->GetHost()->Print();
+      break;
+    case ID_TESTS_PRINT_TO_PDF:
+      PrintToPDF(browser);
+      break;
+    case ID_TESTS_MUTE_AUDIO:
+      MuteAudio(browser, true);
+      break;
+    case ID_TESTS_UNMUTE_AUDIO:
+      MuteAudio(browser, false);
+      break;
+    case ID_TESTS_OTHER_TESTS:
+      RunOtherTests(browser);
+      break;
+  }
+}
+
+std::string DumpRequestContents(CefRefPtr<CefRequest> request) {
+  std::stringstream ss;
+
+  ss << "URL: " << std::string(request->GetURL());
+  ss << "\nMethod: " << std::string(request->GetMethod());
+
+  CefRequest::HeaderMap headerMap;
+  request->GetHeaderMap(headerMap);
+  if (headerMap.size() > 0) {
+    ss << "\nHeaders:";
+    CefRequest::HeaderMap::const_iterator it = headerMap.begin();
+    for (; it != headerMap.end(); ++it) {
+      ss << "\n\t" << std::string((*it).first) << ": "
+         << std::string((*it).second);
+    }
+  }
+
+  CefRefPtr<CefPostData> postData = request->GetPostData();
+  if (postData.get()) {
+    CefPostData::ElementVector elements;
+    postData->GetElements(elements);
+    if (elements.size() > 0) {
+      ss << "\nPost Data:";
+      CefRefPtr<CefPostDataElement> element;
+      CefPostData::ElementVector::const_iterator it = elements.begin();
+      for (; it != elements.end(); ++it) {
+        element = (*it);
+        if (element->GetType() == PDE_TYPE_BYTES) {
+          // the element is composed of bytes
+          ss << "\n\tBytes: ";
+          if (element->GetBytesCount() == 0) {
+            ss << "(empty)";
+          } else {
+            // retrieve the data.
+            size_t size = element->GetBytesCount();
+            char* bytes = new char[size];
+            element->GetBytes(size, bytes);
+            ss << std::string(bytes, size);
+            delete[] bytes;
+          }
+        } else if (element->GetType() == PDE_TYPE_FILE) {
+          ss << "\n\tFile: " << std::string(element->GetFile());
+        }
+      }
+    }
+  }
+
+  return ss.str();
+}
+
+CefRefPtr<CefStreamReader> GetDumpResponse(
+    CefRefPtr<CefRequest> request,
+    CefResponse::HeaderMap& response_headers) {
+  std::string origin;
+
+  // Extract the origin request header, if any. It will be specified for
+  // cross-origin requests.
+  {
+    CefRequest::HeaderMap requestMap;
+    request->GetHeaderMap(requestMap);
+
+    CefRequest::HeaderMap::const_iterator it = requestMap.begin();
+    for (; it != requestMap.end(); ++it) {
+      std::string key = it->first;
+      std::transform(key.begin(), key.end(), key.begin(), ::tolower);
+      if (key == "origin") {
+        origin = it->second;
+        break;
+      }
+    }
+  }
+
+  if (!origin.empty() &&
+      (origin.find("http://" + std::string(kTestHost)) == 0 ||
+       origin.find("http://" + std::string(kLocalHost)) == 0)) {
+    // Allow cross-origin XMLHttpRequests from test origins.
+    response_headers.insert(
+        std::make_pair("Access-Control-Allow-Origin", origin));
+
+    // Allow the custom header from the xmlhttprequest.html example.
+    response_headers.insert(
+        std::make_pair("Access-Control-Allow-Headers", "My-Custom-Header"));
+  }
+
+  const std::string& dump = DumpRequestContents(request);
+  std::string str =
+      "<html><body bgcolor=\"white\"><pre>" + dump + "</pre></body></html>";
+  CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+      static_cast<void*>(const_cast<char*>(str.c_str())), str.size());
+  DCHECK(stream);
+  return stream;
+}
+
+std::string GetDataURI(const std::string& data, const std::string& mime_type) {
+  return "data:" + mime_type + ";base64," +
+         CefURIEncode(CefBase64Encode(data.data(), data.size()), false)
+             .ToString();
+}
+
+std::string GetErrorString(cef_errorcode_t code) {
+// Case condition that returns |code| as a string.
+#define CASE(code) \
+  case code:       \
+    return #code
+
+  switch (code) {
+    CASE(ERR_NONE);
+    CASE(ERR_FAILED);
+    CASE(ERR_ABORTED);
+    CASE(ERR_INVALID_ARGUMENT);
+    CASE(ERR_INVALID_HANDLE);
+    CASE(ERR_FILE_NOT_FOUND);
+    CASE(ERR_TIMED_OUT);
+    CASE(ERR_FILE_TOO_BIG);
+    CASE(ERR_UNEXPECTED);
+    CASE(ERR_ACCESS_DENIED);
+    CASE(ERR_NOT_IMPLEMENTED);
+    CASE(ERR_CONNECTION_CLOSED);
+    CASE(ERR_CONNECTION_RESET);
+    CASE(ERR_CONNECTION_REFUSED);
+    CASE(ERR_CONNECTION_ABORTED);
+    CASE(ERR_CONNECTION_FAILED);
+    CASE(ERR_NAME_NOT_RESOLVED);
+    CASE(ERR_INTERNET_DISCONNECTED);
+    CASE(ERR_SSL_PROTOCOL_ERROR);
+    CASE(ERR_ADDRESS_INVALID);
+    CASE(ERR_ADDRESS_UNREACHABLE);
+    CASE(ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
+    CASE(ERR_TUNNEL_CONNECTION_FAILED);
+    CASE(ERR_NO_SSL_VERSIONS_ENABLED);
+    CASE(ERR_SSL_VERSION_OR_CIPHER_MISMATCH);
+    CASE(ERR_SSL_RENEGOTIATION_REQUESTED);
+    CASE(ERR_CERT_COMMON_NAME_INVALID);
+    CASE(ERR_CERT_DATE_INVALID);
+    CASE(ERR_CERT_AUTHORITY_INVALID);
+    CASE(ERR_CERT_CONTAINS_ERRORS);
+    CASE(ERR_CERT_NO_REVOCATION_MECHANISM);
+    CASE(ERR_CERT_UNABLE_TO_CHECK_REVOCATION);
+    CASE(ERR_CERT_REVOKED);
+    CASE(ERR_CERT_INVALID);
+    CASE(ERR_CERT_END);
+    CASE(ERR_INVALID_URL);
+    CASE(ERR_DISALLOWED_URL_SCHEME);
+    CASE(ERR_UNKNOWN_URL_SCHEME);
+    CASE(ERR_TOO_MANY_REDIRECTS);
+    CASE(ERR_UNSAFE_REDIRECT);
+    CASE(ERR_UNSAFE_PORT);
+    CASE(ERR_INVALID_RESPONSE);
+    CASE(ERR_INVALID_CHUNKED_ENCODING);
+    CASE(ERR_METHOD_NOT_SUPPORTED);
+    CASE(ERR_UNEXPECTED_PROXY_AUTH);
+    CASE(ERR_EMPTY_RESPONSE);
+    CASE(ERR_RESPONSE_HEADERS_TOO_BIG);
+    CASE(ERR_CACHE_MISS);
+    CASE(ERR_INSECURE_RESPONSE);
+    default:
+      return "UNKNOWN";
+  }
+}
+
+void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager,
+                          StringResourceMap* string_resource_map) {
+  if (!CefCurrentlyOn(TID_IO)) {
+    // Execute on the browser IO thread.
+    CefPostTask(TID_IO, base::Bind(SetupResourceManager, resource_manager,
+                                   string_resource_map));
+    return;
+  }
+
+  const std::string& test_origin = kTestOrigin;
+
+  // Add the URL filter.
+  resource_manager->SetUrlFilter(base::Bind(RequestUrlFilter));
+
+  // Add provider for resource dumps.
+  resource_manager->AddProvider(
+      new RequestDumpResourceProvider(test_origin + "request.html"), 0,
+      std::string());
+
+  // Set of supported string pages.
+  std::set<std::string> string_pages;
+  string_pages.insert(kTestGetSourcePage);
+  string_pages.insert(kTestGetTextPage);
+  string_pages.insert(kTestPluginInfoPage);
+
+  // Add provider for string resources.
+  resource_manager->AddProvider(
+      new StringResourceProvider(string_pages, string_resource_map), 0,
+      std::string());
+
+// Add provider for bundled resource files.
+#if defined(OS_WIN)
+  // Read resources from the binary.
+  resource_manager->AddProvider(
+      CreateBinaryResourceProvider(test_origin, std::string()), 100,
+      std::string());
+#elif defined(OS_POSIX)
+  // Read resources from a directory on disk.
+  std::string resource_dir;
+  if (GetResourceDir(resource_dir)) {
+    resource_manager->AddDirectoryProvider(test_origin, resource_dir, 100,
+                                           std::string());
+  }
+#endif
+}
+
+void Alert(CefRefPtr<CefBrowser> browser, const std::string& message) {
+  if (browser->GetHost()->GetExtension()) {
+    // Alerts originating from extension hosts should instead be displayed in
+    // the active browser.
+    browser = MainContext::Get()->GetRootWindowManager()->GetActiveBrowser();
+    if (!browser)
+      return;
+  }
+
+  // Escape special characters in the message.
+  std::string msg = StringReplace(message, "\\", "\\\\");
+  msg = StringReplace(msg, "'", "\\'");
+
+  // Execute a JavaScript alert().
+  CefRefPtr<CefFrame> frame = browser->GetMainFrame();
+  frame->ExecuteJavaScript("alert('" + msg + "');", frame->GetURL(), 0);
+}
+
+bool IsTestURL(const std::string& url, const std::string& path) {
+  CefURLParts parts;
+  CefParseURL(url, parts);
+
+  const std::string& url_host = CefString(&parts.host);
+  if (url_host != kTestHost && url_host != kLocalHost)
+    return false;
+
+  const std::string& url_path = CefString(&parts.path);
+  return url_path.find(path) == 0;
+}
+
+void CreateMessageHandlers(MessageHandlerSet& handlers) {
+  handlers.insert(new PromptHandler);
+
+  // Create the binding test handlers.
+  binding_test::CreateMessageHandlers(handlers);
+
+  // Create the dialog test handlers.
+  dialog_test::CreateMessageHandlers(handlers);
+
+  // Create the drm test handlers.
+  drm_test::CreateMessageHandlers(handlers);
+
+  // Create the media router test handlers.
+  media_router_test::CreateMessageHandlers(handlers);
+
+  // Create the preferences test handlers.
+  preferences_test::CreateMessageHandlers(handlers);
+
+  // Create the server test handlers.
+  server_test::CreateMessageHandlers(handlers);
+
+  // Create the urlrequest test handlers.
+  urlrequest_test::CreateMessageHandlers(handlers);
+
+  // Create the window test handlers.
+  window_test::CreateMessageHandlers(handlers);
+}
+
+void RegisterSchemeHandlers() {
+  // Register the scheme handler.
+  scheme_test::RegisterSchemeHandlers();
+}
+
+CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response) {
+  // Create the response filter.
+  return response_filter_test::GetResourceResponseFilter(browser, frame,
+                                                         request, response);
+}
+
+}  // namespace test_runner
+}  // namespace client
diff --git a/src/tests/cefclient/browser/test_runner.h b/src/tests/cefclient/browser/test_runner.h
new file mode 100644
index 0000000..ed0172d
--- /dev/null
+++ b/src/tests/cefclient/browser/test_runner.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_TEST_RUNNER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_TEST_RUNNER_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "include/cef_browser.h"
+#include "include/cef_request.h"
+#include "include/wrapper/cef_message_router.h"
+#include "include/wrapper/cef_resource_manager.h"
+
+namespace client {
+namespace test_runner {
+
+// Run a test.
+void RunTest(CefRefPtr<CefBrowser> browser, int id);
+
+// Returns the contents of the CefRequest as a string.
+std::string DumpRequestContents(CefRefPtr<CefRequest> request);
+
+// Returns the dump response as a stream. |request| is the request.
+// |response_headers| will be populated with extra response headers, if any.
+CefRefPtr<CefStreamReader> GetDumpResponse(
+    CefRefPtr<CefRequest> request,
+    CefResponse::HeaderMap& response_headers);
+
+// Returns a data: URI with the specified contents.
+std::string GetDataURI(const std::string& data, const std::string& mime_type);
+
+// Returns the string representation of the specified error code.
+std::string GetErrorString(cef_errorcode_t code);
+
+typedef std::map<std::string, std::string> StringResourceMap;
+
+// Set up the resource manager for tests.
+void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager,
+                          StringResourceMap* string_resource_map);
+
+// Show a JS alert message.
+void Alert(CefRefPtr<CefBrowser> browser, const std::string& message);
+
+// Returns true if |url| is a test URL with the specified |path|. This matches
+// both http://tests/<path> and http://localhost:xxxx/<path>.
+bool IsTestURL(const std::string& url, const std::string& path);
+
+// Create all CefMessageRouterBrowserSide::Handler objects. They will be
+// deleted when the ClientHandler is destroyed.
+typedef std::set<CefMessageRouterBrowserSide::Handler*> MessageHandlerSet;
+void CreateMessageHandlers(MessageHandlerSet& handlers);
+
+// Register scheme handlers for tests.
+void RegisterSchemeHandlers();
+
+// Create a resource response filter for tests.
+CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request,
+    CefRefPtr<CefResponse> response);
+
+}  // namespace test_runner
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_TEST_RUNNER_H_
diff --git a/src/tests/cefclient/browser/text_input_client_osr_mac.h b/src/tests/cefclient/browser/text_input_client_osr_mac.h
new file mode 100644
index 0000000..9f6cbca
--- /dev/null
+++ b/src/tests/cefclient/browser/text_input_client_osr_mac.h
@@ -0,0 +1,78 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
+#pragma once
+
+#import <Cocoa/Cocoa.h>
+#include <string>
+#include <vector>
+
+#include "include/cef_browser.h"
+#include "include/cef_render_handler.h"
+
+// Implementation for the NSTextInputClient protocol used for enabling IME on
+// mac when window rendering is disabled.
+
+@interface CefTextInputClientOSRMac : NSObject<NSTextInputClient> {
+ @private
+
+  // The range of current marked text inside the whole content of the DOM node
+  // being edited.
+  NSRange markedRange_;
+
+  // The current composition character range and its bounds.
+  CefRange composition_range_;
+  std::vector<CefRect> composition_bounds_;
+
+  // Represents the input-method attributes supported by this object.
+  NSArray* validAttributesForMarkedText_;
+
+  // Indicates if we are currently handling a key down event.
+  BOOL handlingKeyDown_;
+
+  // Indicates if there is any marked text.
+  BOOL hasMarkedText_;
+
+  // Indicates whether there was any marked text prior to handling
+  // the current key event.
+  BOOL oldHasMarkedText_;
+
+  // Indicates if unmarkText is called or not when handling a keyboard
+  // event.
+  BOOL unmarkTextCalled_;
+
+  // The selected range, cached from a message sent by the renderer.
+  NSRange selectedRange_;
+
+  // Text to be inserted which was generated by handling a key down event.
+  std::string textToBeInserted_;
+
+  // Marked text which was generated by handling a key down event.
+  CefString markedText_;
+
+  // Underline information of the |markedText_|.
+  std::vector<CefCompositionUnderline> underlines_;
+
+  // Replacement range information received from |setMarkedText:|.
+  CefRange setMarkedTextReplacementRange_;
+
+  CefRefPtr<CefBrowser> browser_;
+}
+
+@property(nonatomic, readonly) NSRange selectedRange;
+@property(nonatomic) BOOL handlingKeyDown;
+
+- (id)initWithBrowser:(CefRefPtr<CefBrowser>)browser;
+- (void)detach;
+- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent;
+- (void)HandleKeyEventAfterTextInputClient:(CefKeyEvent)keyEvent;
+- (void)ChangeCompositionRange:(CefRange)range
+              character_bounds:(const CefRenderHandler::RectList&)bounds;
+- (void)cancelComposition;
+
+@end
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
diff --git a/src/tests/cefclient/browser/text_input_client_osr_mac.mm b/src/tests/cefclient/browser/text_input_client_osr_mac.mm
new file mode 100644
index 0000000..8532708
--- /dev/null
+++ b/src/tests/cefclient/browser/text_input_client_osr_mac.mm
@@ -0,0 +1,348 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2013 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+// Implementation based on
+// content/browser/renderer_host/render_widget_host_view_mac.mm from Chromium.
+
+#include "text_input_client_osr_mac.h"
+#include "include/cef_client.h"
+
+#define ColorBLACK 0xFF000000  // Same as Blink SKColor.
+
+namespace {
+
+// TODO(suzhe): Upstream this function.
+cef_color_t CefColorFromNSColor(NSColor* color) {
+  CGFloat r, g, b, a;
+  [color getRed:&r green:&g blue:&b alpha:&a];
+
+  return std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255))
+             << 24 |
+         std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255))
+             << 16 |
+         std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255))
+             << 8 |
+         std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
+}
+
+// Extract underline information from an attributed string. Mostly copied from
+// third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
+void ExtractUnderlines(NSAttributedString* string,
+                       std::vector<CefCompositionUnderline>* underlines) {
+  int length = [[string string] length];
+  int i = 0;
+  while (i < length) {
+    NSRange range;
+    NSDictionary* attrs = [string attributesAtIndex:i
+                              longestEffectiveRange:&range
+                                            inRange:NSMakeRange(i, length - i)];
+    NSNumber* style = [attrs objectForKey:NSUnderlineStyleAttributeName];
+    if (style) {
+      cef_color_t color = ColorBLACK;
+      if (NSColor* colorAttr =
+              [attrs objectForKey:NSUnderlineColorAttributeName]) {
+        color = CefColorFromNSColor(
+            [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
+      }
+      cef_composition_underline_t line = {
+          {range.location, NSMaxRange(range)}, color, 0, [style intValue] > 1};
+      underlines->push_back(line);
+    }
+    i = range.location + range.length;
+  }
+}
+
+}  // namespace
+
+extern "C" {
+extern NSString* NSTextInputReplacementRangeAttributeName;
+}
+
+@implementation CefTextInputClientOSRMac
+
+@synthesize selectedRange = selectedRange_;
+@synthesize handlingKeyDown = handlingKeyDown_;
+
+- (id)initWithBrowser:(CefRefPtr<CefBrowser>)browser {
+  self = [super init];
+  browser_ = browser;
+  return self;
+}
+
+- (void)detach {
+  browser_ = NULL;
+}
+
+- (NSArray*)validAttributesForMarkedText {
+  if (!validAttributesForMarkedText_) {
+    validAttributesForMarkedText_ = [[NSArray alloc]
+        initWithObjects:NSUnderlineStyleAttributeName,
+                        NSUnderlineColorAttributeName,
+                        NSMarkedClauseSegmentAttributeName,
+                        NSTextInputReplacementRangeAttributeName, nil];
+  }
+  return validAttributesForMarkedText_;
+}
+
+- (NSRange)selectedRange {
+  if (selectedRange_.location == NSNotFound || selectedRange_.length == 0)
+    return NSMakeRange(NSNotFound, 0);
+  return selectedRange_;
+}
+
+- (NSRange)markedRange {
+  return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
+}
+
+- (BOOL)hasMarkedText {
+  return hasMarkedText_;
+}
+
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
+  BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
+  NSString* im_text = isAttributedString ? [aString string] : aString;
+  if (handlingKeyDown_) {
+    textToBeInserted_.append([im_text UTF8String]);
+  } else {
+    cef_range_t range = {replacementRange.location,
+                         NSMaxRange(replacementRange)};
+    browser_->GetHost()->ImeCommitText([im_text UTF8String], range, 0);
+  }
+
+  // Inserting text will delete all marked text automatically.
+  hasMarkedText_ = NO;
+}
+
+- (void)doCommandBySelector:(SEL)aSelector {
+  // An input method calls this function to dispatch an editing command to be
+  // handled by this view.
+}
+
+- (void)setMarkedText:(id)aString
+        selectedRange:(NSRange)newSelRange
+     replacementRange:(NSRange)replacementRange {
+  // An input method has updated the composition string. We send the given text
+  // and range to the browser so it can update the composition node of Blink.
+
+  BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
+  NSString* im_text = isAttributedString ? [aString string] : aString;
+  int length = [im_text length];
+
+  // |markedRange_| will get set in a callback from ImeSetComposition().
+  selectedRange_ = newSelRange;
+  markedText_ = [im_text UTF8String];
+  hasMarkedText_ = (length > 0);
+  underlines_.clear();
+
+  if (isAttributedString) {
+    ExtractUnderlines(aString, &underlines_);
+  } else {
+    // Use a thin black underline by default.
+    cef_composition_underline_t line = {{0, length}, ColorBLACK, 0, false};
+    underlines_.push_back(line);
+  }
+
+  // If we are handling a key down event then ImeSetComposition() will be
+  // called from the keyEvent: method.
+  // Input methods of Mac use setMarkedText calls with empty text to cancel an
+  // ongoing composition. Our input method backend will automatically cancel an
+  // ongoing composition when we send empty text.
+  if (handlingKeyDown_) {
+    setMarkedTextReplacementRange_ = {replacementRange.location,
+                                      NSMaxRange(replacementRange)};
+  } else if (!handlingKeyDown_) {
+    CefRange replacement_range(replacementRange.location,
+                               NSMaxRange(replacementRange));
+    CefRange selection_range(newSelRange.location, NSMaxRange(newSelRange));
+
+    browser_->GetHost()->ImeSetComposition(markedText_, underlines_,
+                                           replacement_range, selection_range);
+  }
+}
+
+- (void)unmarkText {
+  // Delete the composition node of the browser and finish an ongoing
+  // composition.
+  // It seems that, instead of calling this method, an input method will call
+  // the setMarkedText method with empty text to cancel ongoing composition.
+  // Implement this method even though we don't expect it to be called.
+  hasMarkedText_ = NO;
+  markedText_.clear();
+  underlines_.clear();
+
+  // If we are handling a key down event then ImeFinishComposingText() will be
+  // called from the keyEvent: method.
+  if (!handlingKeyDown_)
+    browser_->GetHost()->ImeFinishComposingText(false);
+  else
+    unmarkTextCalled_ = YES;
+}
+
+- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
+                                               actualRange:
+                                                   (NSRangePointer)actualRange {
+  // Modify the attributed string if required.
+  // Not implemented here as we do not want to control the IME window view.
+  return nil;
+}
+
+- (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
+                             actualRange:(NSRangePointer)actualRange {
+  NSRect rect;
+
+  NSUInteger location = theRange.location;
+
+  // If location is not specified fall back to the composition range start.
+  if (location == NSNotFound)
+    location = markedRange_.location;
+
+  // Offset location by the composition range start if required.
+  if (location >= markedRange_.location)
+    location -= markedRange_.location;
+
+  if (location < composition_bounds_.size()) {
+    const CefRect& rc = composition_bounds_[location];
+    rect = NSMakeRect(rc.x, rc.y, rc.width, rc.height);
+  }
+
+  if (actualRange)
+    *actualRange = NSMakeRange(location, theRange.length);
+
+  return rect;
+}
+
+- (NSRect)screenRectFromViewRect:(NSRect)rect {
+  NSRect screenRect;
+
+  int screenX, screenY;
+  browser_->GetHost()->GetClient()->GetRenderHandler()->GetScreenPoint(
+      browser_, rect.origin.x, rect.origin.y, screenX, screenY);
+  screenRect.origin = NSMakePoint(screenX, screenY);
+  screenRect.size = rect.size;
+
+  return screenRect;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)theRange
+                         actualRange:(NSRangePointer)actualRange {
+  NSRect rect =
+      [self firstViewRectForCharacterRange:theRange actualRange:actualRange];
+
+  // Convert into screen coordinates for return.
+  rect = [self screenRectFromViewRect:rect];
+
+  if (rect.origin.y >= rect.size.height)
+    rect.origin.y -= rect.size.height;
+  else
+    rect.origin.y = 0;
+
+  return rect;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
+  return NSNotFound;
+}
+
+- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent {
+  DCHECK([keyEvent type] == NSKeyDown);
+  // Don't call this method recursively.
+  DCHECK(!handlingKeyDown_);
+
+  oldHasMarkedText_ = hasMarkedText_;
+  handlingKeyDown_ = YES;
+
+  // These variables might be set when handling the keyboard event.
+  // Clear them here so that we can know whether they have changed afterwards.
+  textToBeInserted_.clear();
+  markedText_.clear();
+  underlines_.clear();
+  setMarkedTextReplacementRange_ = CefRange(UINT32_MAX, UINT32_MAX);
+  unmarkTextCalled_ = NO;
+}
+
+- (void)HandleKeyEventAfterTextInputClient:(CefKeyEvent)keyEvent {
+  handlingKeyDown_ = NO;
+
+  // Send keypress and/or composition related events.
+  // Note that |textToBeInserted_| is a UTF-16 string but it's fine to only
+  // handle BMP characters here as we can always insert non-BMP characters as
+  // text.
+
+  // If the text to be inserted only contains 1 character then we can just send
+  // a keypress event.
+  if (!hasMarkedText_ && !oldHasMarkedText_ &&
+      textToBeInserted_.length() <= 1) {
+    keyEvent.type = KEYEVENT_KEYDOWN;
+
+    browser_->GetHost()->SendKeyEvent(keyEvent);
+
+    // Don't send a CHAR event for non-char keys like arrows, function keys and
+    // clear.
+    if (keyEvent.modifiers & (EVENTFLAG_IS_KEY_PAD)) {
+      if (keyEvent.native_key_code == 71)
+        return;
+    }
+
+    keyEvent.type = KEYEVENT_CHAR;
+    browser_->GetHost()->SendKeyEvent(keyEvent);
+  }
+
+  // If the text to be inserted contains multiple characters then send the text
+  // to the browser using ImeCommitText().
+  BOOL textInserted = NO;
+  if (textToBeInserted_.length() >
+      ((hasMarkedText_ || oldHasMarkedText_) ? 0u : 1u)) {
+    browser_->GetHost()->ImeCommitText(textToBeInserted_,
+                                       CefRange(UINT32_MAX, UINT32_MAX), 0);
+    textToBeInserted_.clear();
+  }
+
+  // Update or cancel the composition. If some text has been inserted then we
+  // don't need to explicitly cancel the composition.
+  if (hasMarkedText_ && markedText_.length()) {
+    // Update the composition by sending marked text to the browser.
+    // |selectedRange_| is the range being selected inside the marked text.
+    browser_->GetHost()->ImeSetComposition(
+        markedText_, underlines_, setMarkedTextReplacementRange_,
+        CefRange(selectedRange_.location, NSMaxRange(selectedRange_)));
+  } else if (oldHasMarkedText_ && !hasMarkedText_ && !textInserted) {
+    // There was no marked text or inserted text. Complete or cancel the
+    // composition.
+    if (unmarkTextCalled_)
+      browser_->GetHost()->ImeFinishComposingText(false);
+    else
+      browser_->GetHost()->ImeCancelComposition();
+  }
+
+  setMarkedTextReplacementRange_ = CefRange(UINT32_MAX, UINT32_MAX);
+}
+
+- (void)ChangeCompositionRange:(CefRange)range
+              character_bounds:(const CefRenderHandler::RectList&)bounds {
+  composition_range_ = range;
+  markedRange_ = NSMakeRange(range.from, range.to - range.from);
+  composition_bounds_ = bounds;
+}
+
+- (void)cancelComposition {
+  if (!hasMarkedText_)
+    return;
+
+// Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
+// doesn't call any NSTextInput functions, such as setMarkedText or
+// insertText.
+// TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to
+// NSTextInputContext. http://www.crbug.com/479010.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  NSInputManager* currentInputManager = [NSInputManager currentInputManager];
+  [currentInputManager markedTextAbandoned:self];
+#pragma clang diagnostic pop
+
+  hasMarkedText_ = NO;
+  // Should not call [self unmarkText] here because it'll send unnecessary
+  // cancel composition messages to the browser.
+}
+
+@end
diff --git a/src/tests/cefclient/browser/urlrequest_test.cc b/src/tests/cefclient/browser/urlrequest_test.cc
new file mode 100644
index 0000000..7e39613
--- /dev/null
+++ b/src/tests/cefclient/browser/urlrequest_test.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/urlrequest_test.h"
+
+#include <string>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_callback.h"
+#include "include/base/cef_logging.h"
+#include "include/cef_urlrequest.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace urlrequest_test {
+
+namespace {
+
+const char kTestUrlPath[] = "/urlrequest";
+const char kTestMessageName[] = "URLRequestTest";
+
+// Implementation of CefURLRequestClient that stores response information. Only
+// accessed on the UI thread.
+class RequestClient : public CefURLRequestClient {
+ public:
+  // Callback to be executed on request completion.
+  typedef base::Callback<void(CefURLRequest::ErrorCode /*error_code*/,
+                              const std::string& /*download_data*/)>
+      Callback;
+
+  explicit RequestClient(const Callback& callback) : callback_(callback) {
+    CEF_REQUIRE_UI_THREAD();
+    DCHECK(!callback_.is_null());
+  }
+
+  void Detach() {
+    CEF_REQUIRE_UI_THREAD();
+    if (!callback_.is_null())
+      callback_.Reset();
+  }
+
+  void OnRequestComplete(CefRefPtr<CefURLRequest> request) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    if (!callback_.is_null()) {
+      callback_.Run(request->GetRequestError(), download_data_);
+      callback_.Reset();
+    }
+  }
+
+  void OnUploadProgress(CefRefPtr<CefURLRequest> request,
+                        int64 current,
+                        int64 total) OVERRIDE {}
+
+  void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
+                          int64 current,
+                          int64 total) OVERRIDE {}
+
+  void OnDownloadData(CefRefPtr<CefURLRequest> request,
+                      const void* data,
+                      size_t data_length) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+    download_data_ += std::string(static_cast<const char*>(data), data_length);
+  }
+
+  bool GetAuthCredentials(bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) OVERRIDE {
+    return false;
+  }
+
+ private:
+  Callback callback_;
+  std::string download_data_;
+
+  IMPLEMENT_REFCOUNTING(RequestClient);
+  DISALLOW_COPY_AND_ASSIGN(RequestClient);
+};
+
+// Handle messages in the browser process. Only accessed on the UI thread.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  Handler() { CEF_REQUIRE_UI_THREAD(); }
+
+  ~Handler() { CancelPendingRequest(); }
+
+  // Called due to cefQuery execution in urlrequest.html.
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) OVERRIDE {
+    CEF_REQUIRE_UI_THREAD();
+
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (!test_runner::IsTestURL(url, kTestUrlPath))
+      return false;
+
+    const std::string& message_name = request;
+    if (message_name.find(kTestMessageName) == 0) {
+      const std::string& load_url =
+          message_name.substr(sizeof(kTestMessageName));
+
+      CancelPendingRequest();
+
+      DCHECK(!callback_.get());
+      DCHECK(!urlrequest_.get());
+
+      callback_ = callback;
+
+      // Create a CefRequest for the specified URL.
+      CefRefPtr<CefRequest> cef_request = CefRequest::Create();
+      cef_request->SetURL(load_url);
+      cef_request->SetMethod("GET");
+
+      // Callback to be executed on request completion.
+      // It's safe to use base::Unretained() here because there is only one
+      // RequestClient pending at any given time and we explicitly detach the
+      // callback in the Handler destructor.
+      const RequestClient::Callback& request_callback =
+          base::Bind(&Handler::OnRequestComplete, base::Unretained(this));
+
+      // Create and start a new CefURLRequest associated with the frame, so
+      // that it shares authentication with ClientHandler::GetAuthCredentials.
+      urlrequest_ = frame->CreateURLRequest(
+          cef_request, new RequestClient(request_callback));
+
+      return true;
+    }
+
+    return false;
+  }
+
+ private:
+  // Cancel the currently pending URL request, if any.
+  void CancelPendingRequest() {
+    CEF_REQUIRE_UI_THREAD();
+
+    if (urlrequest_.get()) {
+      // Don't execute the callback when we explicitly cancel the request.
+      static_cast<RequestClient*>(urlrequest_->GetClient().get())->Detach();
+
+      urlrequest_->Cancel();
+      urlrequest_ = nullptr;
+    }
+
+    if (callback_.get()) {
+      // Must always execute |callback_| before deleting it.
+      callback_->Failure(ERR_ABORTED, test_runner::GetErrorString(ERR_ABORTED));
+      callback_ = nullptr;
+    }
+  }
+
+  void OnRequestComplete(CefURLRequest::ErrorCode error_code,
+                         const std::string& download_data) {
+    CEF_REQUIRE_UI_THREAD();
+
+    if (error_code == ERR_NONE)
+      callback_->Success(download_data);
+    else
+      callback_->Failure(error_code, test_runner::GetErrorString(error_code));
+
+    callback_ = nullptr;
+    urlrequest_ = nullptr;
+  }
+
+  CefRefPtr<Callback> callback_;
+  CefRefPtr<CefURLRequest> urlrequest_;
+
+  DISALLOW_COPY_AND_ASSIGN(Handler);
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace urlrequest_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/urlrequest_test.h b/src/tests/cefclient/browser/urlrequest_test.h
new file mode 100644
index 0000000..ec572e7
--- /dev/null
+++ b/src/tests/cefclient/browser/urlrequest_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_URLREQUEST_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_URLREQUEST_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace urlrequest_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace urlrequest_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_URLREQUEST_TEST_H_
diff --git a/src/tests/cefclient/browser/util_gtk.cc b/src/tests/cefclient/browser/util_gtk.cc
new file mode 100644
index 0000000..be5e30b
--- /dev/null
+++ b/src/tests/cefclient/browser/util_gtk.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/util_gtk.h"
+
+#include <gdk/gdk.h>
+
+namespace client {
+
+base::PlatformThreadId ScopedGdkThreadsEnter::locked_thread_ =
+    kInvalidPlatformThreadId;
+
+ScopedGdkThreadsEnter::ScopedGdkThreadsEnter() {
+  // The GDK lock is not reentrant, so don't try to lock again if the current
+  // thread already holds it.
+  base::PlatformThreadId current_thread = base::PlatformThread::CurrentId();
+  take_lock_ = current_thread != locked_thread_;
+
+  if (take_lock_) {
+    gdk_threads_enter();
+    locked_thread_ = current_thread;
+  }
+}
+
+ScopedGdkThreadsEnter::~ScopedGdkThreadsEnter() {
+  if (take_lock_) {
+    locked_thread_ = kInvalidPlatformThreadId;
+    gdk_threads_leave();
+  }
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/util_gtk.h b/src/tests/cefclient/browser/util_gtk.h
new file mode 100644
index 0000000..c8836fe
--- /dev/null
+++ b/src/tests/cefclient/browser/util_gtk.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_UTIL_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_UTIL_GTK_H_
+#pragma once
+
+#include "include/base/cef_macros.h"
+#include "include/base/cef_platform_thread.h"
+
+namespace client {
+
+// Scoped helper that manages the global GDK lock by calling gdk_threads_enter()
+// and gdk_threads_leave(). The lock is not reentrant so this helper implements
+// additional checking to avoid deadlocks.
+//
+// When using GTK in multi-threaded mode you must do the following:
+// 1. Call gdk_threads_init() before making any other GTK/GDK/GLib calls.
+// 2. Acquire the global lock before making any GTK/GDK calls, and release the
+//    lock afterwards. This should only be done with callbacks that do not
+//    originate from GTK signals (because those callbacks already hold the
+//    lock).
+//
+// See https://www.geany.org/manual/gtk/gtk-faq/x482.html for more information.
+class ScopedGdkThreadsEnter {
+ public:
+  ScopedGdkThreadsEnter();
+  ~ScopedGdkThreadsEnter();
+
+ private:
+  bool take_lock_;
+
+  static base::PlatformThreadId locked_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedGdkThreadsEnter);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_UTIL_GTK_H_
diff --git a/src/tests/cefclient/browser/views_menu_bar.cc b/src/tests/cefclient/browser/views_menu_bar.cc
new file mode 100644
index 0000000..a7b4b4f
--- /dev/null
+++ b/src/tests/cefclient/browser/views_menu_bar.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/views_menu_bar.h"
+
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_window.h"
+#include "tests/cefclient/browser/views_style.h"
+
+namespace client {
+
+namespace {
+
+const int kMenuBarGroupId = 100;
+
+// Convert |c| to lowercase using the current ICU locale.
+// TODO(jshin): What about Turkish locale? See http://crbug.com/81719.
+// If the mnemonic is capital I and the UI language is Turkish, lowercasing it
+// results in 'small dotless i', which is different from a 'dotted i'. Similar
+// issues may exist for az and lt locales.
+base::char16 ToLower(base::char16 c) {
+  CefStringUTF16 str16;
+  cef_string_utf16_to_lower(&c, 1, str16.GetWritableStruct());
+  return str16.length() > 0 ? str16.c_str()[0] : 0;
+}
+
+// Extract the mnemonic character from |title|. For example, if |title| is
+// "&Test" then the mnemonic character is 'T'.
+base::char16 GetMnemonic(const base::string16& title) {
+  size_t index = 0;
+  do {
+    index = title.find('&', index);
+    if (index != base::string16::npos) {
+      if (index + 1 != title.size() && title[index + 1] != '&')
+        return ToLower(title[index + 1]);
+      index++;
+    }
+  } while (index != base::string16::npos);
+  return 0;
+}
+
+}  // namespace
+
+ViewsMenuBar::ViewsMenuBar(Delegate* delegate, int menu_id_start)
+    : delegate_(delegate),
+      id_start_(menu_id_start),
+      id_next_(menu_id_start),
+      last_nav_with_keyboard_(false) {
+  DCHECK(delegate_);
+  DCHECK_GT(id_start_, 0);
+}
+
+bool ViewsMenuBar::HasMenuId(int menu_id) const {
+  return menu_id >= id_start_ && menu_id < id_next_;
+}
+
+CefRefPtr<CefPanel> ViewsMenuBar::GetMenuPanel() {
+  EnsureMenuPanel();
+  return panel_;
+}
+
+CefRefPtr<CefMenuModel> ViewsMenuBar::CreateMenuModel(const CefString& label,
+                                                      int* menu_id) {
+  EnsureMenuPanel();
+
+  // Assign the new menu ID.
+  const int new_menu_id = id_next_++;
+  if (menu_id)
+    *menu_id = new_menu_id;
+
+  // Create the new MenuModel.
+  CefRefPtr<CefMenuModel> model = CefMenuModel::CreateMenuModel(this);
+  views_style::ApplyTo(model);
+  models_.push_back(model);
+
+  // Create the new MenuButton.
+  CefRefPtr<CefMenuButton> button =
+      CefMenuButton::CreateMenuButton(this, label);
+  button->SetID(new_menu_id);
+  views_style::ApplyTo(button.get());
+  button->SetInkDropEnabled(true);
+
+  // Assign a group ID to allow focus traversal between MenuButtons using the
+  // arrow keys when the menu is not displayed.
+  button->SetGroupID(kMenuBarGroupId);
+
+  // Add the new MenuButton to the Planel.
+  panel_->AddChildView(button);
+
+  // Extract the mnemonic that triggers the menu, if any.
+  base::char16 mnemonic = GetMnemonic(label);
+  if (mnemonic != 0)
+    mnemonics_.insert(std::make_pair(mnemonic, new_menu_id));
+
+  return model;
+}
+
+CefRefPtr<CefMenuModel> ViewsMenuBar::GetMenuModel(int menu_id) const {
+  if (HasMenuId(menu_id))
+    return models_[menu_id - id_start_];
+  return nullptr;
+}
+
+void ViewsMenuBar::SetMenuFocusable(bool focusable) {
+  if (!panel_)
+    return;
+
+  for (int id = id_start_; id < id_next_; ++id)
+    panel_->GetViewForID(id)->SetFocusable(focusable);
+
+  if (focusable) {
+    // Give focus to the first MenuButton.
+    panel_->GetViewForID(id_start_)->RequestFocus();
+  }
+}
+
+bool ViewsMenuBar::OnKeyEvent(const CefKeyEvent& event) {
+  if (!panel_)
+    return false;
+
+  if (event.type != KEYEVENT_RAWKEYDOWN)
+    return false;
+
+  // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. For
+  // example Ctrl+<T> is an accelerator, but <T> only is a mnemonic.
+  if (event.modifiers & (EVENTFLAG_ALT_DOWN | EVENTFLAG_CONTROL_DOWN))
+    return false;
+
+  MnemonicMap::const_iterator it = mnemonics_.find(ToLower(event.character));
+  if (it == mnemonics_.end())
+    return false;
+
+  // Set status indicating that we navigated using the keyboard.
+  last_nav_with_keyboard_ = true;
+
+  // Show the selected menu.
+  TriggerMenuButton(panel_->GetViewForID(it->second));
+
+  return true;
+}
+
+void ViewsMenuBar::Reset() {
+  panel_ = nullptr;
+  models_.clear();
+  mnemonics_.clear();
+  id_next_ = id_start_;
+}
+
+void ViewsMenuBar::OnMenuButtonPressed(
+    CefRefPtr<CefMenuButton> menu_button,
+    const CefPoint& screen_point,
+    CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) {
+  CefRefPtr<CefMenuModel> menu_model = GetMenuModel(menu_button->GetID());
+
+  // Adjust menu position left by button width.
+  CefPoint point = screen_point;
+  point.x -= menu_button->GetBounds().width - 4;
+
+  // Keep track of the current |last_nav_with_keyboard_| status and restore it
+  // after displaying the new menu.
+  bool cur_last_nav_with_keyboard = last_nav_with_keyboard_;
+
+  // May result in the previous menu being closed, in which case MenuClosed will
+  // be called before the new menu is displayed.
+  menu_button->ShowMenu(menu_model, point, CEF_MENU_ANCHOR_TOPLEFT);
+
+  last_nav_with_keyboard_ = cur_last_nav_with_keyboard;
+}
+
+void ViewsMenuBar::ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                                  int command_id,
+                                  cef_event_flags_t event_flags) {
+  delegate_->MenuBarExecuteCommand(menu_model, command_id, event_flags);
+}
+
+void ViewsMenuBar::MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
+                                    const CefPoint& screen_point) {
+  DCHECK(panel_);
+
+  // Retrieve the Window hosting the Panel.
+  CefRefPtr<CefWindow> window = panel_->GetWindow();
+  DCHECK(window);
+
+  // Convert the point from screen to window coordinates.
+  CefPoint window_point = screen_point;
+  if (!window->ConvertPointFromScreen(window_point))
+    return;
+
+  CefRect panel_bounds = panel_->GetBounds();
+
+  if (last_nav_with_keyboard_) {
+    // The user navigated last using the keyboard. Don't change menus using
+    // mouse movements until the mouse exits and re-enters the Panel.
+    if (panel_bounds.Contains(window_point))
+      return;
+    last_nav_with_keyboard_ = false;
+  }
+
+  // Check that the point is inside the Panel.
+  if (!panel_bounds.Contains(window_point))
+    return;
+
+  const int active_menu_id = GetActiveMenuId();
+
+  // Determine which MenuButton is under the specified point.
+  for (int id = id_start_; id < id_next_; ++id) {
+    // Skip the currently active MenuButton.
+    if (id == active_menu_id)
+      continue;
+
+    CefRefPtr<CefView> button = panel_->GetViewForID(id);
+    CefRect button_bounds = button->GetBounds();
+    if (button_bounds.Contains(window_point)) {
+      // Trigger the hovered MenuButton.
+      TriggerMenuButton(button);
+      break;
+    }
+  }
+}
+
+void ViewsMenuBar::UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                                        bool is_rtl) {
+  TriggerNextMenu(is_rtl ? 1 : -1);
+}
+
+void ViewsMenuBar::UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                                         bool is_rtl) {
+  TriggerNextMenu(is_rtl ? -1 : 1);
+}
+
+void ViewsMenuBar::MenuClosed(CefRefPtr<CefMenuModel> menu_model) {
+  // Reset |last_nav_with_keyboard_| status whenever the main menu closes.
+  if (!menu_model->IsSubMenu() && last_nav_with_keyboard_)
+    last_nav_with_keyboard_ = false;
+}
+
+void ViewsMenuBar::EnsureMenuPanel() {
+  if (panel_)
+    return;
+
+  panel_ = CefPanel::CreatePanel(nullptr);
+  views_style::ApplyTo(panel_);
+
+  // Use a horizontal box layout.
+  CefBoxLayoutSettings top_panel_layout_settings;
+  top_panel_layout_settings.horizontal = true;
+  panel_->SetToBoxLayout(top_panel_layout_settings);
+}
+
+int ViewsMenuBar::GetActiveMenuId() {
+  DCHECK(panel_);
+
+  for (int id = id_start_; id < id_next_; ++id) {
+    CefRefPtr<CefButton> button = panel_->GetViewForID(id)->AsButton();
+    if (button->GetState() == CEF_BUTTON_STATE_PRESSED)
+      return id;
+  }
+
+  return -1;
+}
+
+void ViewsMenuBar::TriggerNextMenu(int offset) {
+  DCHECK(panel_);
+
+  const int active_menu_id = GetActiveMenuId();
+  const int menu_count = id_next_ - id_start_;
+  const int active_menu_index = active_menu_id - id_start_;
+
+  // Compute the modulus to avoid negative values.
+  int next_menu_index = (active_menu_index + offset) % menu_count;
+  if (next_menu_index < 0)
+    next_menu_index += menu_count;
+
+  // Cancel the existing menu. MenuClosed may be called.
+  panel_->GetWindow()->CancelMenu();
+
+  // Set status indicating that we navigated using the keyboard.
+  last_nav_with_keyboard_ = true;
+
+  // Show the new menu.
+  TriggerMenuButton(panel_->GetViewForID(id_start_ + next_menu_index));
+}
+
+void ViewsMenuBar::TriggerMenuButton(CefRefPtr<CefView> button) {
+  CefRefPtr<CefMenuButton> menu_button =
+      button->AsButton()->AsLabelButton()->AsMenuButton();
+  if (menu_button->IsFocusable())
+    menu_button->RequestFocus();
+  menu_button->TriggerMenu();
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/views_menu_bar.h b/src/tests/cefclient/browser/views_menu_bar.h
new file mode 100644
index 0000000..0bf1b2d
--- /dev/null
+++ b/src/tests/cefclient/browser/views_menu_bar.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_MENU_BAR_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_MENU_BAR_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "include/cef_menu_model.h"
+#include "include/cef_menu_model_delegate.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+#include "include/views/cef_panel.h"
+
+namespace client {
+
+// Implements a menu bar which is composed of CefMenuButtons positioned in a
+// row with automatic switching between them via mouse/keyboard. All methods
+// must be called on the browser process UI thread.
+class ViewsMenuBar : public CefMenuButtonDelegate, public CefMenuModelDelegate {
+ public:
+  // Delegate methods will be called on the browser process UI thread.
+  class Delegate {
+   public:
+    // Called when a menu command is selected.
+    virtual void MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                                       int command_id,
+                                       cef_event_flags_t event_flags) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // |delegate| must outlive this object.
+  // |menu_id_start| is the ID for the first CefMenuButton in the bar. An ID
+  // range starting with |menu_id_start| and extending for a reasonable distance
+  // should be reserved in the client for MenuBar usage.
+  ViewsMenuBar(Delegate* delegate, int menu_id_start);
+
+  // Returns true if |menu_id| exists in the menu bar.
+  bool HasMenuId(int menu_id) const;
+
+  // Returns the CefPanel that represents the menu bar.
+  CefRefPtr<CefPanel> GetMenuPanel();
+
+  // Create a new menu with the specified |label|. If |menu_id| is non-NULL it
+  // will be populated with the new menu ID.
+  CefRefPtr<CefMenuModel> CreateMenuModel(const CefString& label, int* menu_id);
+
+  // Returns the menu with the specified |menu_id|, or NULL if no such menu
+  // exists.
+  CefRefPtr<CefMenuModel> GetMenuModel(int menu_id) const;
+
+  // Assign or remove focus from the menu bar.
+  // Focus is assigned to the menu bar by ViewsWindow::OnKeyEvent when the ALT
+  // key is pressed. Focus is removed from the menu bar by ViewsWindow::OnFocus
+  // when a control not in the menu bar gains focus.
+  void SetMenuFocusable(bool focusable);
+
+  // Key events forwarded from ViewsWindow::OnKeyEvent when the menu bar has
+  // focus.
+  bool OnKeyEvent(const CefKeyEvent& event);
+
+  // Reset menu bar state.
+  void Reset();
+
+ protected:
+  // CefButtonDelegate methods:
+  void OnButtonPressed(CefRefPtr<CefButton> button) OVERRIDE {}
+
+  // CefMenuButtonDelegate methods:
+  void OnMenuButtonPressed(
+      CefRefPtr<CefMenuButton> menu_button,
+      const CefPoint& screen_point,
+      CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) OVERRIDE;
+
+  // CefMenuModelDelegate methods:
+  void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                      int command_id,
+                      cef_event_flags_t event_flags) OVERRIDE;
+  void MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
+                        const CefPoint& screen_point) OVERRIDE;
+  void UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                            bool is_rtl) OVERRIDE;
+  void UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
+                             bool is_rtl) OVERRIDE;
+  void MenuClosed(CefRefPtr<CefMenuModel> menu_model) OVERRIDE;
+
+ private:
+  // Creates the menu panel if it doesn't already exist.
+  void EnsureMenuPanel();
+
+  // Returns the ID for the currently active menu, or -1 if no menu is currently
+  // active.
+  int GetActiveMenuId();
+
+  // Triggers the menu at the specified |offset| from the currently active menu.
+  void TriggerNextMenu(int offset);
+
+  // Triggers the specified MenuButton |button|.
+  void TriggerMenuButton(CefRefPtr<CefView> button);
+
+  Delegate* delegate_;  // Not owned by this object.
+  const int id_start_;
+  int id_next_;
+  CefRefPtr<CefPanel> panel_;
+  std::vector<CefRefPtr<CefMenuModel>> models_;
+  bool last_nav_with_keyboard_;
+
+  // Map of mnemonic to MenuButton ID.
+  typedef std::map<base::char16, int> MnemonicMap;
+  MnemonicMap mnemonics_;
+
+  IMPLEMENT_REFCOUNTING(ViewsMenuBar);
+  DISALLOW_COPY_AND_ASSIGN(ViewsMenuBar);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_MENU_BAR_H_
diff --git a/src/tests/cefclient/browser/views_style.cc b/src/tests/cefclient/browser/views_style.cc
new file mode 100644
index 0000000..830b7ce
--- /dev/null
+++ b/src/tests/cefclient/browser/views_style.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/views_style.h"
+
+#include "tests/cefclient/browser/main_context.h"
+
+namespace client {
+
+namespace views_style {
+
+namespace {
+
+cef_color_t g_background_color = 0;
+cef_color_t g_background_hover_color = 0;
+cef_color_t g_text_color = 0;
+
+int GetShade(int component) {
+  return (component < 127) ? component + 75 : component - 75;
+}
+
+void MaybeInitialize() {
+  static bool initialized = false;
+  if (initialized)
+    return;
+
+  g_background_color = MainContext::Get()->GetBackgroundColor();
+  if (g_background_color != 0) {
+    // Use a slightly modified shade of the background color for hover.
+    g_background_hover_color =
+        CefColorSetARGB(255, GetShade(CefColorGetR(g_background_color)),
+                        GetShade(CefColorGetG(g_background_color)),
+                        GetShade(CefColorGetB(g_background_color)));
+
+    // Invert the background color for text.
+    g_text_color = CefColorSetARGB(255, 255 - CefColorGetR(g_background_color),
+                                   255 - CefColorGetG(g_background_color),
+                                   255 - CefColorGetB(g_background_color));
+  }
+
+  initialized = true;
+}
+
+}  // namespace
+
+bool IsSet() {
+  MaybeInitialize();
+  return g_background_color != 0;
+}
+
+void ApplyTo(CefRefPtr<CefPanel> panel) {
+  if (!IsSet())
+    return;
+
+  panel->SetBackgroundColor(g_background_color);
+}
+
+void ApplyTo(CefRefPtr<CefLabelButton> label_button) {
+  if (!IsSet())
+    return;
+
+  // All text except disabled gets the same color.
+  label_button->SetEnabledTextColors(g_text_color);
+  label_button->SetTextColor(CEF_BUTTON_STATE_DISABLED,
+                             g_background_hover_color);
+
+  label_button->SetBackgroundColor(g_background_color);
+}
+
+void ApplyTo(CefRefPtr<CefTextfield> textfield) {
+  if (!IsSet())
+    return;
+
+  textfield->SetBackgroundColor(g_background_color);
+  textfield->SetTextColor(g_text_color);
+}
+
+void ApplyTo(CefRefPtr<CefMenuModel> menu_model) {
+  if (!IsSet())
+    return;
+
+  // All text except non-hovered accelerator gets the same color.
+  menu_model->SetColorAt(-1, CEF_MENU_COLOR_TEXT, g_text_color);
+  menu_model->SetColorAt(-1, CEF_MENU_COLOR_TEXT_HOVERED, g_text_color);
+  menu_model->SetColorAt(-1, CEF_MENU_COLOR_TEXT_ACCELERATOR,
+                         g_background_hover_color);
+  menu_model->SetColorAt(-1, CEF_MENU_COLOR_TEXT_ACCELERATOR_HOVERED,
+                         g_text_color);
+
+  menu_model->SetColorAt(-1, CEF_MENU_COLOR_BACKGROUND, g_background_color);
+  menu_model->SetColorAt(-1, CEF_MENU_COLOR_BACKGROUND_HOVERED,
+                         g_background_hover_color);
+
+  // Recursively color sub-menus.
+  for (int i = 0; i < menu_model->GetCount(); ++i) {
+    if (menu_model->GetTypeAt(i) == MENUITEMTYPE_SUBMENU)
+      ApplyTo(menu_model->GetSubMenuAt(i));
+  }
+}
+
+}  // namespace views_style
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/views_style.h b/src/tests/cefclient/browser/views_style.h
new file mode 100644
index 0000000..0d32f2c
--- /dev/null
+++ b/src/tests/cefclient/browser/views_style.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_STYLE_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_STYLE_H_
+#pragma once
+
+#include "include/cef_menu_model.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_textfield.h"
+
+namespace client {
+
+namespace views_style {
+
+// Returns true if a style is set.
+bool IsSet();
+
+// Apply style to views objects.
+void ApplyTo(CefRefPtr<CefPanel> panel);
+void ApplyTo(CefRefPtr<CefLabelButton> label_button);
+void ApplyTo(CefRefPtr<CefTextfield> textfield);
+void ApplyTo(CefRefPtr<CefMenuModel> menu_model);
+
+}  // namespace views_style
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_STYLE_H_
diff --git a/src/tests/cefclient/browser/views_window.cc b/src/tests/cefclient/browser/views_window.cc
new file mode 100644
index 0000000..230e6de
--- /dev/null
+++ b/src/tests/cefclient/browser/views_window.cc
@@ -0,0 +1,952 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/views_window.h"
+
+#include <algorithm>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_build.h"
+#include "include/cef_app.h"
+#include "include/views/cef_box_layout.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/cefclient/browser/views_style.h"
+#include "tests/shared/browser/extension_util.h"
+#include "tests/shared/common/client_switches.h"
+
+#if !defined(OS_WIN)
+#define VK_ESCAPE 0x1B
+#define VK_RETURN 0x0D
+#define VK_MENU 0x12  // ALT key.
+#endif
+
+namespace client {
+
+namespace {
+
+const char kDefaultExtensionIcon[] = "window_icon";
+
+// Control IDs for Views in the top-level Window.
+enum ControlIds {
+  ID_WINDOW = 1,
+  ID_BROWSER_VIEW,
+  ID_BACK_BUTTON,
+  ID_FORWARD_BUTTON,
+  ID_STOP_BUTTON,
+  ID_RELOAD_BUTTON,
+  ID_URL_TEXTFIELD,
+  ID_MENU_BUTTON,
+
+  // Reserved range of top menu button IDs.
+  ID_TOP_MENU_FIRST,
+  ID_TOP_MENU_LAST = ID_TOP_MENU_FIRST + 10,
+
+  // Reserved range of extension button IDs.
+  ID_EXTENSION_BUTTON_FIRST,
+  ID_EXTENSION_BUTTON_LAST = ID_EXTENSION_BUTTON_FIRST + 10,
+};
+
+typedef std::vector<CefRefPtr<CefLabelButton>> LabelButtons;
+
+// Make all |buttons| the same size.
+void MakeButtonsSameSize(const LabelButtons& buttons) {
+  CefSize size;
+
+  // Determine the largest button size.
+  for (size_t i = 0U; i < buttons.size(); ++i) {
+    const CefSize& button_size = buttons[i]->GetPreferredSize();
+    if (size.width < button_size.width)
+      size.width = button_size.width;
+    if (size.height < button_size.height)
+      size.height = button_size.height;
+  }
+
+  for (size_t i = 0U; i < buttons.size(); ++i) {
+    // Set the button's minimum size.
+    buttons[i]->SetMinimumSize(size);
+
+    // Re-layout the button and all parent Views.
+    buttons[i]->InvalidateLayout();
+  }
+}
+
+void AddTestMenuItems(CefRefPtr<CefMenuModel> test_menu) {
+  test_menu->AddItem(ID_TESTS_GETSOURCE, "Get Source");
+  test_menu->AddItem(ID_TESTS_GETTEXT, "Get Text");
+  test_menu->AddItem(ID_TESTS_WINDOW_NEW, "New Window");
+  test_menu->AddItem(ID_TESTS_WINDOW_POPUP, "Popup Window");
+  test_menu->AddItem(ID_TESTS_REQUEST, "Request");
+  test_menu->AddItem(ID_TESTS_PLUGIN_INFO, "Plugin Info");
+  test_menu->AddItem(ID_TESTS_ZOOM_IN, "Zoom In");
+  test_menu->AddItem(ID_TESTS_ZOOM_OUT, "Zoom Out");
+  test_menu->AddItem(ID_TESTS_ZOOM_RESET, "Zoom Reset");
+  test_menu->AddItem(ID_TESTS_TRACING_BEGIN, "Begin Tracing");
+  test_menu->AddItem(ID_TESTS_TRACING_END, "End Tracing");
+  test_menu->AddItem(ID_TESTS_PRINT, "Print");
+  test_menu->AddItem(ID_TESTS_PRINT_TO_PDF, "Print to PDF");
+  test_menu->AddItem(ID_TESTS_MUTE_AUDIO, "Mute Audio");
+  test_menu->AddItem(ID_TESTS_UNMUTE_AUDIO, "Unmute Audio");
+  test_menu->AddItem(ID_TESTS_OTHER_TESTS, "Other Tests");
+}
+
+void AddFileMenuItems(CefRefPtr<CefMenuModel> file_menu) {
+  file_menu->AddItem(ID_QUIT, "E&xit");
+
+  // Show the accelerator shortcut text in the menu.
+  file_menu->SetAcceleratorAt(file_menu->GetCount() - 1, 'X', false, false,
+                              true);
+}
+
+}  // namespace
+
+// static
+CefRefPtr<ViewsWindow> ViewsWindow::Create(
+    Delegate* delegate,
+    CefRefPtr<CefClient> client,
+    const CefString& url,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefRequestContext> request_context) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(delegate);
+
+  // Create a new ViewsWindow.
+  CefRefPtr<ViewsWindow> views_window = new ViewsWindow(delegate, nullptr);
+
+  // Create a new BrowserView.
+  CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
+      client, url, settings, nullptr, request_context, views_window);
+
+  // Associate the BrowserView with the ViewsWindow.
+  views_window->SetBrowserView(browser_view);
+
+  // Create a new top-level Window. It will show itself after creation.
+  CefWindow::CreateTopLevelWindow(views_window);
+
+  return views_window;
+}
+
+void ViewsWindow::Show() {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_)
+    window_->Show();
+  if (browser_view_) {
+    // Give keyboard focus to the BrowserView.
+    browser_view_->RequestFocus();
+  }
+}
+
+void ViewsWindow::Hide() {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_)
+    window_->Hide();
+}
+
+void ViewsWindow::Minimize() {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_)
+    window_->Minimize();
+}
+
+void ViewsWindow::Maximize() {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_)
+    window_->Maximize();
+}
+
+void ViewsWindow::SetBounds(const CefRect& bounds) {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_)
+    window_->SetBounds(bounds);
+}
+
+void ViewsWindow::SetBrowserSize(const CefSize& size,
+                                 bool has_position,
+                                 const CefPoint& position) {
+  CEF_REQUIRE_UI_THREAD();
+  if (browser_view_)
+    browser_view_->SetSize(size);
+  if (window_) {
+    window_->SizeToPreferredSize();
+    if (has_position)
+      window_->SetPosition(position);
+  }
+}
+
+void ViewsWindow::Close(bool force) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!browser_view_)
+    return;
+
+  CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
+  if (browser) {
+    // This will result in a call to CefWindow::Close() which will then call
+    // ViewsWindow::CanClose().
+    browser->GetHost()->CloseBrowser(force);
+  }
+}
+
+void ViewsWindow::SetAddress(const std::string& url) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!window_ || !with_controls_)
+    return;
+
+  CefRefPtr<CefView> view = window_->GetViewForID(ID_URL_TEXTFIELD);
+  if (view && view->AsTextfield())
+    view->AsTextfield()->SetText(url);
+}
+
+void ViewsWindow::SetTitle(const std::string& title) {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_)
+    window_->SetTitle(title);
+}
+
+void ViewsWindow::SetFavicon(CefRefPtr<CefImage> image) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Window icons should be 16 DIP in size.
+  DCHECK_EQ(std::max(image->GetWidth(), image->GetHeight()), 16U);
+
+  if (window_)
+    window_->SetWindowIcon(image);
+}
+
+void ViewsWindow::SetFullscreen(bool fullscreen) {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_) {
+    // Hide the top controls while in full-screen mode.
+    if (with_controls_)
+      ShowTopControls(!fullscreen);
+
+    window_->SetFullscreen(fullscreen);
+  }
+}
+
+void ViewsWindow::SetAlwaysOnTop(bool on_top) {
+  CEF_REQUIRE_UI_THREAD();
+  if (window_) {
+    window_->SetAlwaysOnTop(on_top);
+  }
+}
+
+void ViewsWindow::SetLoadingState(bool isLoading,
+                                  bool canGoBack,
+                                  bool canGoForward) {
+  CEF_REQUIRE_UI_THREAD();
+  if (!window_ || !with_controls_)
+    return;
+
+  EnableView(ID_BACK_BUTTON, canGoBack);
+  EnableView(ID_FORWARD_BUTTON, canGoForward);
+  EnableView(ID_RELOAD_BUTTON, !isLoading);
+  EnableView(ID_STOP_BUTTON, isLoading);
+  EnableView(ID_URL_TEXTFIELD, true);
+}
+
+void ViewsWindow::SetDraggableRegions(
+    const std::vector<CefDraggableRegion>& regions) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!window_ || !browser_view_)
+    return;
+
+  std::vector<CefDraggableRegion> window_regions;
+
+  // Convert the regions from BrowserView to Window coordinates.
+  std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
+  for (; it != regions.end(); ++it) {
+    CefDraggableRegion region = *it;
+    CefPoint origin = CefPoint(region.bounds.x, region.bounds.y);
+    browser_view_->ConvertPointToWindow(origin);
+    region.bounds.x = origin.x;
+    region.bounds.y = origin.y;
+    window_regions.push_back(region);
+  }
+
+  window_->SetDraggableRegions(window_regions);
+}
+
+void ViewsWindow::TakeFocus(bool next) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!window_ || !with_controls_)
+    return;
+
+  // Give focus to the URL textfield.
+  window_->GetViewForID(ID_URL_TEXTFIELD)->RequestFocus();
+}
+
+void ViewsWindow::OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) {
+  CEF_REQUIRE_UI_THREAD();
+
+  views_style::ApplyTo(model);
+}
+
+void ViewsWindow::OnExtensionsChanged(const ExtensionSet& extensions) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (extensions.empty()) {
+    if (!extensions_.empty()) {
+      extensions_.clear();
+      UpdateExtensionControls();
+    }
+    return;
+  }
+
+  ImageCache::ImageInfoSet image_set;
+
+  ExtensionSet::const_iterator it = extensions.begin();
+  for (; it != extensions.end(); ++it) {
+    CefRefPtr<CefExtension> extension = *it;
+    bool internal = false;
+    const std::string& icon_path =
+        extension_util::GetExtensionIconPath(extension, &internal);
+    if (!icon_path.empty()) {
+      // Load the extension icon.
+      image_set.push_back(
+          ImageCache::ImageInfo::Create1x(icon_path, icon_path, internal));
+    } else {
+      // Get a NULL image and use the default icon.
+      image_set.push_back(ImageCache::ImageInfo::Empty());
+    }
+  }
+
+  delegate_->GetImageCache()->LoadImages(
+      image_set,
+      base::Bind(&ViewsWindow::OnExtensionIconsLoaded, this, extensions));
+}
+
+CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
+    CefRefPtr<CefBrowserView> browser_view,
+    const CefBrowserSettings& settings,
+    CefRefPtr<CefClient> client,
+    bool is_devtools) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // The popup browser client is created in CefLifeSpanHandler::OnBeforePopup()
+  // (e.g. via RootWindowViews::InitAsPopup()). The Delegate (RootWindowViews)
+  // knows the association between |client| and itself.
+  Delegate* popup_delegate = delegate_->GetDelegateForPopup(client);
+
+  // Should not be the same RootWindowViews that owns |this|.
+  DCHECK(popup_delegate && popup_delegate != delegate_);
+
+  // Create a new ViewsWindow for the popup BrowserView.
+  return new ViewsWindow(popup_delegate, nullptr);
+}
+
+bool ViewsWindow::OnPopupBrowserViewCreated(
+    CefRefPtr<CefBrowserView> browser_view,
+    CefRefPtr<CefBrowserView> popup_browser_view,
+    bool is_devtools) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Retrieve the ViewsWindow created in GetDelegateForPopupBrowserView.
+  CefRefPtr<ViewsWindow> popup_window =
+      static_cast<ViewsWindow*>(static_cast<CefBrowserViewDelegate*>(
+          popup_browser_view->GetDelegate().get()));
+
+  // Should not be the same ViewsWindow as |this|.
+  DCHECK(popup_window && popup_window != this);
+
+  // Associate the ViewsWindow with the new popup browser.
+  popup_window->SetBrowserView(popup_browser_view);
+
+  // Create a new top-level Window for the popup. It will show itself after
+  // creation.
+  CefWindow::CreateTopLevelWindow(popup_window);
+
+  // We created the Window.
+  return true;
+}
+
+void ViewsWindow::OnButtonPressed(CefRefPtr<CefButton> button) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(with_controls_);
+
+  if (!browser_view_)
+    return;
+
+  CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
+  if (!browser)
+    return;
+
+  switch (button->GetID()) {
+    case ID_BACK_BUTTON:
+      browser->GoBack();
+      break;
+    case ID_FORWARD_BUTTON:
+      browser->GoForward();
+      break;
+    case ID_STOP_BUTTON:
+      browser->StopLoad();
+      break;
+    case ID_RELOAD_BUTTON:
+      browser->Reload();
+      break;
+    case ID_MENU_BUTTON:
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+void ViewsWindow::OnMenuButtonPressed(
+    CefRefPtr<CefMenuButton> menu_button,
+    const CefPoint& screen_point,
+    CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) {
+  CEF_REQUIRE_UI_THREAD();
+
+  const int id = menu_button->GetID();
+  if (id >= ID_EXTENSION_BUTTON_FIRST && id <= ID_EXTENSION_BUTTON_LAST) {
+    const size_t extension_idx = id - ID_EXTENSION_BUTTON_FIRST;
+    if (extension_idx >= extensions_.size()) {
+      LOG(ERROR) << "Invalid extension index " << extension_idx;
+      return;
+    }
+
+    // Keep the button pressed until the extension window is closed.
+    extension_button_pressed_lock_ = button_pressed_lock;
+
+    // Create a window for the extension.
+    delegate_->CreateExtensionWindow(
+        extensions_[extension_idx].extension_, menu_button->GetBoundsInScreen(),
+        window_, base::Bind(&ViewsWindow::OnExtensionWindowClosed, this));
+    return;
+  }
+
+  DCHECK(with_controls_);
+  DCHECK_EQ(ID_MENU_BUTTON, id);
+
+  menu_button->ShowMenu(button_menu_model_, screen_point,
+                        CEF_MENU_ANCHOR_TOPRIGHT);
+}
+
+void ViewsWindow::ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                                 int command_id,
+                                 cef_event_flags_t event_flags) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(with_controls_);
+
+  if (command_id == ID_QUIT) {
+    delegate_->OnExit();
+  } else if (command_id >= ID_TESTS_FIRST && command_id <= ID_TESTS_LAST) {
+    delegate_->OnTest(command_id);
+  } else {
+    NOTREACHED();
+  }
+}
+
+bool ViewsWindow::OnKeyEvent(CefRefPtr<CefTextfield> textfield,
+                             const CefKeyEvent& event) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(with_controls_);
+  DCHECK_EQ(ID_URL_TEXTFIELD, textfield->GetID());
+
+  // Trigger when the return key is pressed.
+  if (window_ && browser_view_ && event.type == KEYEVENT_RAWKEYDOWN &&
+      event.windows_key_code == VK_RETURN) {
+    CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
+    if (browser) {
+      CefRefPtr<CefView> view = window_->GetViewForID(ID_URL_TEXTFIELD);
+      if (view && view->AsTextfield()) {
+        const CefString& url = view->AsTextfield()->GetText();
+        if (!url.empty())
+          browser->GetMainFrame()->LoadURL(url);
+      }
+    }
+
+    // We handled the event.
+    return true;
+  }
+
+  return false;
+}
+
+void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(browser_view_);
+  DCHECK(!window_);
+  DCHECK(window);
+
+  window_ = window;
+  window_->SetID(ID_WINDOW);
+
+  with_controls_ = delegate_->WithControls();
+
+  delegate_->OnViewsWindowCreated(this);
+
+  CefRect bounds = delegate_->GetWindowBounds();
+  if (bounds.IsEmpty()) {
+    // Use the default size.
+    bounds.width = 800;
+    bounds.height = 600;
+  }
+
+  if (bounds.x == 0 && bounds.y == 0) {
+    // Size the Window and center it.
+    window_->CenterWindow(CefSize(bounds.width, bounds.height));
+  } else {
+    // Set the Window bounds as specified.
+    window_->SetBounds(bounds);
+  }
+
+  // Set the background color for regions that are not obscured by other Views.
+  views_style::ApplyTo(window_.get());
+
+  if (with_controls_) {
+    // Add the BrowserView and other controls to the Window.
+    AddControls();
+
+    // Add keyboard accelerators to the Window.
+    AddAccelerators();
+  } else {
+    // Add the BrowserView as the only child of the Window.
+    window_->AddChildView(browser_view_);
+
+    if (!delegate_->WithExtension()) {
+      // Choose a reasonable minimum window size.
+      minimum_window_size_ = CefSize(100, 100);
+    }
+  }
+
+  if (!delegate_->InitiallyHidden()) {
+    // Show the Window.
+    Show();
+  }
+}
+
+void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(window_);
+
+  delegate_->OnViewsWindowDestroyed(this);
+
+  browser_view_ = nullptr;
+  button_menu_model_ = nullptr;
+  if (top_menu_bar_) {
+    top_menu_bar_->Reset();
+    top_menu_bar_ = nullptr;
+  }
+  extensions_panel_ = nullptr;
+  window_ = nullptr;
+}
+
+bool ViewsWindow::CanClose(CefRefPtr<CefWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Allow the window to close if the browser says it's OK.
+  CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
+  if (browser)
+    return browser->GetHost()->TryCloseBrowser();
+  return true;
+}
+
+CefRefPtr<CefWindow> ViewsWindow::GetParentWindow(CefRefPtr<CefWindow> window,
+                                                  bool* is_menu,
+                                                  bool* can_activate_menu) {
+  CEF_REQUIRE_UI_THREAD();
+  CefRefPtr<CefWindow> parent_window = delegate_->GetParentWindow();
+  if (parent_window) {
+    // Should be an extension window, in which case we want it to behave as a
+    // menu and allow activation.
+    DCHECK(delegate_->WithExtension());
+    *is_menu = true;
+    *can_activate_menu = true;
+  }
+  return parent_window;
+}
+
+bool ViewsWindow::IsFrameless(CefRefPtr<CefWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+  return frameless_;
+}
+
+bool ViewsWindow::CanResize(CefRefPtr<CefWindow> window) {
+  CEF_REQUIRE_UI_THREAD();
+  // Don't allow windows hosting extensions to resize.
+  return !delegate_->WithExtension();
+}
+
+bool ViewsWindow::OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (command_id == ID_QUIT) {
+    delegate_->OnExit();
+    return true;
+  }
+
+  return false;
+}
+
+bool ViewsWindow::OnKeyEvent(CefRefPtr<CefWindow> window,
+                             const CefKeyEvent& event) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!window_)
+    return false;
+
+  if (delegate_->WithExtension() && event.type == KEYEVENT_RAWKEYDOWN &&
+      event.windows_key_code == VK_ESCAPE) {
+    // Close the extension window on escape.
+    Close(false);
+    return true;
+  }
+
+  if (!with_controls_)
+    return false;
+
+  if (event.type == KEYEVENT_RAWKEYDOWN && event.windows_key_code == VK_MENU) {
+    // ALT key is pressed.
+    int last_focused_view = last_focused_view_;
+    bool menu_had_focus = menu_has_focus_;
+
+    // Toggle menu button focusable.
+    SetMenuFocusable(!menu_has_focus_);
+
+    if (menu_had_focus && last_focused_view != 0) {
+      // Restore focus to the view that was previously focused.
+      window_->GetViewForID(last_focused_view)->RequestFocus();
+    }
+
+    return true;
+  }
+
+  if (menu_has_focus_ && top_menu_bar_)
+    return top_menu_bar_->OnKeyEvent(event);
+
+  return false;
+}
+
+CefSize ViewsWindow::GetMinimumSize(CefRefPtr<CefView> view) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (view->GetID() == ID_WINDOW)
+    return minimum_window_size_;
+
+  return CefSize();
+}
+
+void ViewsWindow::OnFocus(CefRefPtr<CefView> view) {
+  CEF_REQUIRE_UI_THREAD();
+
+  const int view_id = view->GetID();
+
+  // Keep track of the non-menu view that was last focused.
+  if (last_focused_view_ != view_id &&
+      (!top_menu_bar_ || !top_menu_bar_->HasMenuId(view_id))) {
+    last_focused_view_ = view_id;
+  }
+
+  // When focus leaves the menu buttons make them unfocusable.
+  if (menu_has_focus_) {
+    if (top_menu_bar_) {
+      if (!top_menu_bar_->HasMenuId(view_id))
+        SetMenuFocusable(false);
+    } else if (view_id != ID_MENU_BUTTON) {
+      SetMenuFocusable(false);
+    }
+  }
+
+  if (view_id == ID_BROWSER_VIEW)
+    delegate_->OnViewsWindowActivated(this);
+}
+
+void ViewsWindow::OnBlur(CefRefPtr<CefView> view) {
+  CEF_REQUIRE_UI_THREAD();
+
+  const int view_id = view->GetID();
+  if (view_id == ID_BROWSER_VIEW && delegate_->WithExtension()) {
+    // Close windows hosting extensions when the browser loses focus.
+    Close(false);
+  }
+}
+
+void ViewsWindow::MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                                        int command_id,
+                                        cef_event_flags_t event_flags) {
+  ExecuteCommand(menu_model, command_id, event_flags);
+}
+
+ViewsWindow::ViewsWindow(Delegate* delegate,
+                         CefRefPtr<CefBrowserView> browser_view)
+    : delegate_(delegate),
+      with_controls_(false),
+      menu_has_focus_(false),
+      last_focused_view_(false) {
+  DCHECK(delegate_);
+  if (browser_view)
+    SetBrowserView(browser_view);
+
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+  frameless_ = command_line->HasSwitch(switches::kHideFrame) ||
+               delegate_->WithExtension();
+
+  if (!command_line->HasSwitch(switches::kHideTopMenu)) {
+    top_menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST);
+  }
+}
+
+void ViewsWindow::SetBrowserView(CefRefPtr<CefBrowserView> browser_view) {
+  DCHECK(!browser_view_);
+  DCHECK(browser_view);
+  DCHECK(browser_view->IsValid());
+  DCHECK(!browser_view->IsAttached());
+  browser_view_ = browser_view;
+  browser_view_->SetID(ID_BROWSER_VIEW);
+}
+
+void ViewsWindow::CreateMenuModel() {
+  // Create the menu button model.
+  button_menu_model_ = CefMenuModel::CreateMenuModel(this);
+  CefRefPtr<CefMenuModel> test_menu =
+      button_menu_model_->AddSubMenu(0, "&Tests");
+  views_style::ApplyTo(button_menu_model_);
+  AddTestMenuItems(test_menu);
+  AddFileMenuItems(button_menu_model_);
+
+  if (top_menu_bar_) {
+    // Add the menus to the top menu bar.
+    AddFileMenuItems(top_menu_bar_->CreateMenuModel("&File", nullptr));
+    AddTestMenuItems(top_menu_bar_->CreateMenuModel("&Tests", nullptr));
+  }
+}
+
+CefRefPtr<CefLabelButton> ViewsWindow::CreateBrowseButton(
+    const std::string& label,
+    int id) {
+  CefRefPtr<CefLabelButton> button =
+      CefLabelButton::CreateLabelButton(this, label);
+  button->SetID(id);
+  button->SetEnabled(false);    // Disabled by default.
+  button->SetFocusable(false);  // Don't give focus to the button.
+
+  return button;
+}
+
+void ViewsWindow::AddControls() {
+  // Create the MenuModel that will be displayed via the menu button.
+  CreateMenuModel();
+
+  CefRefPtr<CefPanel> top_menu_panel;
+  if (top_menu_bar_)
+    top_menu_panel = top_menu_bar_->GetMenuPanel();
+
+  // Create the browse buttons.
+  LabelButtons browse_buttons;
+  browse_buttons.push_back(CreateBrowseButton("Back", ID_BACK_BUTTON));
+  browse_buttons.push_back(CreateBrowseButton("Forward", ID_FORWARD_BUTTON));
+  browse_buttons.push_back(CreateBrowseButton("Reload", ID_RELOAD_BUTTON));
+  browse_buttons.push_back(CreateBrowseButton("Stop", ID_STOP_BUTTON));
+
+  // Create the URL textfield.
+  CefRefPtr<CefTextfield> url_textfield = CefTextfield::CreateTextfield(this);
+  url_textfield->SetID(ID_URL_TEXTFIELD);
+  url_textfield->SetEnabled(false);  // Disabled by default.
+  views_style::ApplyTo(url_textfield);
+
+  // Create the menu button.
+  CefRefPtr<CefMenuButton> menu_button =
+      CefMenuButton::CreateMenuButton(this, CefString());
+  menu_button->SetID(ID_MENU_BUTTON);
+  menu_button->SetImage(
+      CEF_BUTTON_STATE_NORMAL,
+      delegate_->GetImageCache()->GetCachedImage("menu_icon"));
+  views_style::ApplyTo(menu_button.get());
+  menu_button->SetInkDropEnabled(true);
+  // Override the default minimum size.
+  menu_button->SetMinimumSize(CefSize(0, 0));
+
+  // Create the top panel.
+  CefRefPtr<CefPanel> top_panel = CefPanel::CreatePanel(nullptr);
+
+  // Use a horizontal box layout for |top_panel|.
+  CefBoxLayoutSettings top_panel_layout_settings;
+  top_panel_layout_settings.horizontal = true;
+  CefRefPtr<CefBoxLayout> top_panel_layout =
+      top_panel->SetToBoxLayout(top_panel_layout_settings);
+
+  // Add the buttons and URL textfield to |top_panel|.
+  for (size_t i = 0U; i < browse_buttons.size(); ++i)
+    top_panel->AddChildView(browse_buttons[i]);
+  top_panel->AddChildView(url_textfield);
+
+  UpdateExtensionControls();
+  DCHECK(extensions_panel_);
+  top_panel->AddChildView(extensions_panel_);
+
+  top_panel->AddChildView(menu_button);
+  views_style::ApplyTo(top_panel);
+
+  // Allow |url_textfield| to grow and fill any remaining space.
+  top_panel_layout->SetFlexForView(url_textfield, 1);
+
+  // Use a vertical box layout for |window|.
+  CefBoxLayoutSettings window_layout_settings;
+  window_layout_settings.horizontal = false;
+  window_layout_settings.between_child_spacing = 2;
+  CefRefPtr<CefBoxLayout> window_layout =
+      window_->SetToBoxLayout(window_layout_settings);
+
+  // Add the top panel and browser view to |window|.
+  if (top_menu_panel)
+    window_->AddChildView(top_menu_panel);
+  window_->AddChildView(top_panel);
+  window_->AddChildView(browser_view_);
+
+  // Allow |browser_view_| to grow and fill any remaining space.
+  window_layout->SetFlexForView(browser_view_, 1);
+
+  // Lay out |window| so we can get the default button sizes.
+  window_->Layout();
+
+  // Make all browse buttons the same size.
+  MakeButtonsSameSize(browse_buttons);
+
+  // Lay out |window| again with the new button sizes.
+  window_->Layout();
+
+  // Minimum window width is the size of all buttons plus some extra.
+  const int min_width = browse_buttons[0]->GetBounds().width * 4 +
+                        menu_button->GetBounds().width + 100;
+  // Minimum window height is the hight of the top toolbar plus some extra.
+  int min_height = top_panel->GetBounds().height + 100;
+  if (top_menu_panel)
+    min_height += top_menu_panel->GetBounds().height;
+
+  minimum_window_size_ = CefSize(min_width, min_height);
+}
+
+void ViewsWindow::AddAccelerators() {
+  // Trigger accelerators without first forwarding to web content.
+  browser_view_->SetPreferAccelerators(true);
+
+  // Specify the accelerators to handle. OnAccelerator will be called when the
+  // accelerator is triggered.
+  window_->SetAccelerator(ID_QUIT, 'X', false, false, true);
+}
+
+void ViewsWindow::SetMenuFocusable(bool focusable) {
+  if (!window_ || !with_controls_)
+    return;
+
+  if (top_menu_bar_) {
+    top_menu_bar_->SetMenuFocusable(focusable);
+  } else {
+    window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable);
+
+    if (focusable) {
+      // Give focus to menu button.
+      window_->GetViewForID(ID_MENU_BUTTON)->RequestFocus();
+    }
+  }
+
+  menu_has_focus_ = focusable;
+}
+
+void ViewsWindow::EnableView(int id, bool enable) {
+  if (!window_)
+    return;
+  CefRefPtr<CefView> view = window_->GetViewForID(id);
+  if (view)
+    view->SetEnabled(enable);
+}
+
+void ViewsWindow::ShowTopControls(bool show) {
+  if (!window_ || !with_controls_)
+    return;
+
+  // Change the visibility of the panel that contains the buttons.
+  CefRefPtr<CefView> parent_view =
+      window_->GetViewForID(ID_BACK_BUTTON)->GetParentView();
+  if (parent_view->IsVisible() != show) {
+    parent_view->SetVisible(show);
+    parent_view->InvalidateLayout();
+  }
+}
+
+void ViewsWindow::UpdateExtensionControls() {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!window_ || !with_controls_)
+    return;
+
+  if (!extensions_panel_) {
+    extensions_panel_ = CefPanel::CreatePanel(nullptr);
+
+    // Use a horizontal box layout for |top_panel|.
+    CefBoxLayoutSettings top_panel_layout_settings;
+    top_panel_layout_settings.horizontal = true;
+    CefRefPtr<CefBoxLayout> top_panel_layout =
+        extensions_panel_->SetToBoxLayout(top_panel_layout_settings);
+  } else {
+    extensions_panel_->RemoveAllChildViews();
+  }
+
+  if (extensions_.size() >
+      ID_EXTENSION_BUTTON_LAST - ID_EXTENSION_BUTTON_FIRST) {
+    LOG(WARNING) << "Too many extensions loaded. Some will be ignored.";
+  }
+
+  ExtensionInfoSet::const_iterator it = extensions_.begin();
+  for (int id = ID_EXTENSION_BUTTON_FIRST;
+       it != extensions_.end() && id <= ID_EXTENSION_BUTTON_LAST; ++id, ++it) {
+    CefRefPtr<CefMenuButton> button =
+        CefMenuButton::CreateMenuButton(this, CefString());
+    button->SetID(id);
+    button->SetImage(CEF_BUTTON_STATE_NORMAL, (*it).image_);
+    views_style::ApplyTo(button.get());
+    button->SetInkDropEnabled(true);
+    // Override the default minimum size.
+    button->SetMinimumSize(CefSize(0, 0));
+
+    extensions_panel_->AddChildView(button);
+  }
+
+  CefRefPtr<CefView> parent_view = extensions_panel_->GetParentView();
+  if (parent_view)
+    parent_view->InvalidateLayout();
+}
+
+void ViewsWindow::OnExtensionIconsLoaded(const ExtensionSet& extensions,
+                                         const ImageCache::ImageSet& images) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&ViewsWindow::OnExtensionIconsLoaded, this,
+                                   extensions, images));
+    return;
+  }
+
+  DCHECK_EQ(extensions.size(), images.size());
+
+  extensions_.clear();
+
+  ExtensionSet::const_iterator it1 = extensions.begin();
+  ImageCache::ImageSet::const_iterator it2 = images.begin();
+  for (; it1 != extensions.end() && it2 != images.end(); ++it1, ++it2) {
+    CefRefPtr<CefImage> icon = *it2;
+    if (!icon)
+      icon = delegate_->GetImageCache()->GetCachedImage(kDefaultExtensionIcon);
+    extensions_.push_back(ExtensionInfo(*it1, icon));
+  }
+
+  UpdateExtensionControls();
+}
+
+void ViewsWindow::OnExtensionWindowClosed() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI,
+                base::Bind(&ViewsWindow::OnExtensionWindowClosed, this));
+    return;
+  }
+
+  // Restore the button state.
+  extension_button_pressed_lock_ = nullptr;
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/browser/views_window.h b/src/tests/cefclient/browser/views_window.h
new file mode 100644
index 0000000..62739ac
--- /dev/null
+++ b/src/tests/cefclient/browser/views_window.h
@@ -0,0 +1,240 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_WINDOW_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_WINDOW_H_
+#pragma once
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "include/base/cef_callback_forward.h"
+#include "include/cef_menu_model_delegate.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_browser_view_delegate.h"
+#include "include/views/cef_button_delegate.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_textfield_delegate.h"
+#include "include/views/cef_window.h"
+#include "include/views/cef_window_delegate.h"
+#include "tests/cefclient/browser/image_cache.h"
+#include "tests/cefclient/browser/views_menu_bar.h"
+
+namespace client {
+
+typedef std::set<CefRefPtr<CefExtension>> ExtensionSet;
+
+// Implements a CefWindow that hosts a single CefBrowserView and optional
+// Views-based controls. All methods must be called on the browser process UI
+// thread.
+class ViewsWindow : public CefBrowserViewDelegate,
+                    public CefMenuButtonDelegate,
+                    public CefMenuModelDelegate,
+                    public CefTextfieldDelegate,
+                    public CefWindowDelegate,
+                    public ViewsMenuBar::Delegate {
+ public:
+  // Delegate methods will be called on the browser process UI thread.
+  class Delegate {
+   public:
+    // Return true if the window should show controls.
+    virtual bool WithControls() = 0;
+
+    // Return true if the window is hosting an extension.
+    virtual bool WithExtension() = 0;
+
+    // Return true if the window should be created initially hidden.
+    virtual bool InitiallyHidden() = 0;
+
+    // Returns the parent for this window.
+    virtual CefRefPtr<CefWindow> GetParentWindow() = 0;
+
+    // Return the initial window bounds.
+    virtual CefRect GetWindowBounds() = 0;
+
+    // Returns the ImageCache.
+    virtual scoped_refptr<ImageCache> GetImageCache() = 0;
+
+    // Called when the ViewsWindow is created.
+    virtual void OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) = 0;
+
+    // Called when the ViewsWindow is destroyed. All references to |window|
+    // should be released in this callback.
+    virtual void OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) = 0;
+
+    // Called when the ViewsWindow is activated (becomes the foreground window).
+    virtual void OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) = 0;
+
+    // Return the Delegate for the popup window controlled by |client|.
+    virtual Delegate* GetDelegateForPopup(CefRefPtr<CefClient> client) = 0;
+
+    // Create a window for |extension|. |source_bounds| are the bounds of the
+    // UI element, like a button, that triggered the extension.
+    virtual void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
+                                       const CefRect& source_bounds,
+                                       CefRefPtr<CefWindow> parent_window,
+                                       const base::Closure& close_callback) = 0;
+
+    // Called to execute a test. See resource.h for |test_id| values.
+    virtual void OnTest(int test_id) = 0;
+
+    // Called to exit the application.
+    virtual void OnExit() = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  // Create a new top-level ViewsWindow hosting a browser with the specified
+  // configuration.
+  static CefRefPtr<ViewsWindow> Create(
+      Delegate* delegate,
+      CefRefPtr<CefClient> client,
+      const CefString& url,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefRequestContext> request_context);
+
+  void Show();
+  void Hide();
+  void Minimize();
+  void Maximize();
+  void SetBounds(const CefRect& bounds);
+  void SetBrowserSize(const CefSize& size,
+                      bool has_position,
+                      const CefPoint& position);
+  void Close(bool force);
+  void SetAddress(const std::string& url);
+  void SetTitle(const std::string& title);
+  void SetFavicon(CefRefPtr<CefImage> image);
+  void SetFullscreen(bool fullscreen);
+  void SetAlwaysOnTop(bool on_top);
+  void SetLoadingState(bool isLoading, bool canGoBack, bool canGoForward);
+  void SetDraggableRegions(const std::vector<CefDraggableRegion>& regions);
+  void TakeFocus(bool next);
+  void OnBeforeContextMenu(CefRefPtr<CefMenuModel> model);
+  void OnExtensionsChanged(const ExtensionSet& extensions);
+
+  // CefBrowserViewDelegate methods:
+  CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
+      CefRefPtr<CefBrowserView> browser_view,
+      const CefBrowserSettings& settings,
+      CefRefPtr<CefClient> client,
+      bool is_devtools) OVERRIDE;
+  bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
+                                 CefRefPtr<CefBrowserView> popup_browser_view,
+                                 bool is_devtools) OVERRIDE;
+
+  // CefButtonDelegate methods:
+  void OnButtonPressed(CefRefPtr<CefButton> button) OVERRIDE;
+
+  // CefMenuButtonDelegate methods:
+  void OnMenuButtonPressed(
+      CefRefPtr<CefMenuButton> menu_button,
+      const CefPoint& screen_point,
+      CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) OVERRIDE;
+
+  // CefMenuModelDelegate methods:
+  void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                      int command_id,
+                      cef_event_flags_t event_flags) OVERRIDE;
+
+  // CefTextfieldDelegate methods:
+  bool OnKeyEvent(CefRefPtr<CefTextfield> textfield,
+                  const CefKeyEvent& event) OVERRIDE;
+
+  // CefWindowDelegate methods:
+  void OnWindowCreated(CefRefPtr<CefWindow> window) OVERRIDE;
+  void OnWindowDestroyed(CefRefPtr<CefWindow> window) OVERRIDE;
+  CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
+                                       bool* is_menu,
+                                       bool* can_activate_menu) OVERRIDE;
+  bool IsFrameless(CefRefPtr<CefWindow> window) OVERRIDE;
+  bool CanResize(CefRefPtr<CefWindow> window) OVERRIDE;
+  bool CanClose(CefRefPtr<CefWindow> window) OVERRIDE;
+  bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) OVERRIDE;
+  bool OnKeyEvent(CefRefPtr<CefWindow> window,
+                  const CefKeyEvent& event) OVERRIDE;
+
+  // CefViewDelegate methods:
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) OVERRIDE;
+  void OnFocus(CefRefPtr<CefView> view) OVERRIDE;
+  void OnBlur(CefRefPtr<CefView> view) OVERRIDE;
+
+  // ViewsMenuBar::Delegate methods:
+  void MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                             int command_id,
+                             cef_event_flags_t event_flags) OVERRIDE;
+
+ private:
+  // |delegate| is guaranteed to outlive this object.
+  // |browser_view| may be NULL, in which case SetBrowserView() will be called.
+  ViewsWindow(Delegate* delegate, CefRefPtr<CefBrowserView> browser_view);
+
+  void SetBrowserView(CefRefPtr<CefBrowserView> browser_view);
+
+  // Create controls.
+  void CreateMenuModel();
+  CefRefPtr<CefLabelButton> CreateBrowseButton(const std::string& label,
+                                               int id);
+
+  // Add controls to the Window.
+  void AddControls();
+
+  // Add keyboard accelerators to the Window.
+  void AddAccelerators();
+
+  // Control whether the top menu butons are focusable.
+  void SetMenuFocusable(bool focusable);
+
+  // Enable or disable a view by |id|.
+  void EnableView(int id, bool enable);
+
+  // Show/hide top controls on the Window.
+  void ShowTopControls(bool show);
+
+  // Update extension controls on the Window.
+  void UpdateExtensionControls();
+
+  void OnExtensionIconsLoaded(const ExtensionSet& extensions,
+                              const ImageCache::ImageSet& images);
+  void OnExtensionWindowClosed();
+
+  Delegate* delegate_;  // Not owned by this object.
+  CefRefPtr<CefBrowserView> browser_view_;
+  bool frameless_;
+  bool with_controls_;
+  CefRefPtr<CefWindow> window_;
+
+  CefRefPtr<CefMenuModel> button_menu_model_;
+  CefRefPtr<ViewsMenuBar> top_menu_bar_;
+  bool menu_has_focus_;
+  int last_focused_view_;
+
+  CefSize minimum_window_size_;
+
+  // Structure representing an extension.
+  struct ExtensionInfo {
+    ExtensionInfo(CefRefPtr<CefExtension> extension, CefRefPtr<CefImage> image)
+        : extension_(extension), image_(image) {}
+
+    CefRefPtr<CefExtension> extension_;
+    CefRefPtr<CefImage> image_;
+  };
+  typedef std::vector<ExtensionInfo> ExtensionInfoSet;
+
+  ExtensionInfoSet extensions_;
+  CefRefPtr<CefPanel> extensions_panel_;
+  CefRefPtr<CefMenuButtonPressedLock> extension_button_pressed_lock_;
+
+  IMPLEMENT_REFCOUNTING(ViewsWindow);
+  DISALLOW_COPY_AND_ASSIGN(ViewsWindow);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_WINDOW_H_
diff --git a/src/tests/cefclient/browser/window_test.cc b/src/tests/cefclient/browser/window_test.cc
new file mode 100644
index 0000000..4cb0461
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/window_test.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/cefclient/browser/main_context.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/cefclient/browser/window_test_runner.h"
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+#include "tests/cefclient/browser/window_test_runner_views.h"
+#endif
+
+#if defined(OS_WIN)
+#include "tests/cefclient/browser/window_test_runner_win.h"
+#elif defined(OS_LINUX)
+#include "tests/cefclient/browser/window_test_runner_gtk.h"
+#elif defined(OS_MACOSX)
+#include "tests/cefclient/browser/window_test_runner_mac.h"
+#endif
+
+namespace client {
+namespace window_test {
+
+namespace {
+
+const char kTestUrlPath[] = "/window";
+const char kMessagePositionName[] = "WindowTest.Position";
+const char kMessageMinimizeName[] = "WindowTest.Minimize";
+const char kMessageMaximizeName[] = "WindowTest.Maximize";
+const char kMessageRestoreName[] = "WindowTest.Restore";
+
+// Create the appropriate platform test runner object.
+scoped_ptr<WindowTestRunner> CreateWindowTestRunner() {
+#if defined(OS_WIN) || defined(OS_LINUX)
+  if (MainContext::Get()->UseViews())
+    return scoped_ptr<WindowTestRunner>(new WindowTestRunnerViews());
+#endif
+
+#if defined(OS_WIN)
+  return scoped_ptr<WindowTestRunner>(new WindowTestRunnerWin());
+#elif defined(OS_LINUX)
+  return scoped_ptr<WindowTestRunner>(new WindowTestRunnerGtk());
+#elif defined(OS_MACOSX)
+  return scoped_ptr<WindowTestRunner>(new WindowTestRunnerMac());
+#else
+#error "No implementation available for your platform."
+#endif
+}
+
+// Handle messages in the browser process.
+class Handler : public CefMessageRouterBrowserSide::Handler {
+ public:
+  Handler() : runner_(CreateWindowTestRunner()) {}
+
+  // Called due to cefBroadcast execution in window.html.
+  virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id,
+                       const CefString& request,
+                       bool persistent,
+                       CefRefPtr<Callback> callback) OVERRIDE {
+    // Only handle messages from the test URL.
+    const std::string& url = frame->GetURL();
+    if (!test_runner::IsTestURL(url, kTestUrlPath))
+      return false;
+
+    const std::string& message_name = request;
+    if (message_name.find(kMessagePositionName) == 0) {
+      // Parse the comma-delimited list of integer values.
+      std::vector<int> vec;
+      const std::string& vals =
+          message_name.substr(sizeof(kMessagePositionName));
+      std::stringstream ss(vals);
+      int i;
+      while (ss >> i) {
+        vec.push_back(i);
+        if (ss.peek() == ',')
+          ss.ignore();
+      }
+
+      if (vec.size() == 4) {
+        // Execute SetPos() on the main thread.
+        runner_->SetPos(browser, vec[0], vec[1], vec[2], vec[3]);
+      }
+    } else if (message_name == kMessageMinimizeName) {
+      // Execute Minimize() on the main thread.
+      runner_->Minimize(browser);
+    } else if (message_name == kMessageMaximizeName) {
+      // Execute Maximize() on the main thread.
+      runner_->Maximize(browser);
+    } else if (message_name == kMessageRestoreName) {
+      // Execute Restore() on the main thread.
+      runner_->Restore(browser);
+    } else {
+      NOTREACHED();
+    }
+
+    callback->Success("");
+    return true;
+  }
+
+ private:
+  scoped_ptr<WindowTestRunner> runner_;
+};
+
+}  // namespace
+
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
+  handlers.insert(new Handler());
+}
+
+}  // namespace window_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/window_test.h b/src/tests/cefclient/browser/window_test.h
new file mode 100644
index 0000000..2b99bbb
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_H_
+#pragma once
+
+#include "tests/cefclient/browser/test_runner.h"
+
+namespace client {
+namespace window_test {
+
+// Create message handlers. Called from test_runner.cc.
+void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
+
+}  // namespace window_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_H_
diff --git a/src/tests/cefclient/browser/window_test_runner.cc b/src/tests/cefclient/browser/window_test_runner.cc
new file mode 100644
index 0000000..05f1ac5
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/window_test_runner.h"
+
+namespace client {
+namespace window_test {
+
+// static
+void WindowTestRunner::ModifyBounds(const CefRect& display, CefRect& window) {
+  window.x += display.x;
+  window.y += display.y;
+
+  if (window.x < display.x)
+    window.x = display.x;
+  if (window.y < display.y)
+    window.y = display.y;
+  if (window.width < 100)
+    window.width = 100;
+  else if (window.width >= display.width)
+    window.width = display.width;
+  if (window.height < 100)
+    window.height = 100;
+  else if (window.height >= display.height)
+    window.height = display.height;
+  if (window.x + window.width >= display.x + display.width)
+    window.x = display.x + display.width - window.width;
+  if (window.y + window.height >= display.y + display.height)
+    window.y = display.y + display.height - window.height;
+}
+
+}  // namespace window_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/window_test_runner.h b/src/tests/cefclient/browser/window_test_runner.h
new file mode 100644
index 0000000..26a5324
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_H_
+#pragma once
+
+#include "include/cef_browser.h"
+
+namespace client {
+namespace window_test {
+
+// Implement this interface for different platforms. Methods will be called on
+// the browser process UI thread unless otherwise indicated.
+class WindowTestRunner {
+ public:
+  virtual void SetPos(CefRefPtr<CefBrowser> browser,
+                      int x,
+                      int y,
+                      int width,
+                      int height) = 0;
+  virtual void Minimize(CefRefPtr<CefBrowser> browser) = 0;
+  virtual void Maximize(CefRefPtr<CefBrowser> browser) = 0;
+  virtual void Restore(CefRefPtr<CefBrowser> browser) = 0;
+
+  // Fit |window| inside |display|. Coordinates are relative to the upper-left
+  // corner of the display.
+  static void ModifyBounds(const CefRect& display, CefRect& window);
+
+  virtual ~WindowTestRunner() {}
+};
+
+}  // namespace window_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_H_
diff --git a/src/tests/cefclient/browser/window_test_runner_gtk.cc b/src/tests/cefclient/browser/window_test_runner_gtk.cc
new file mode 100644
index 0000000..ff79991
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_gtk.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/window_test_runner_gtk.h"
+
+#include <gtk/gtk.h>
+
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/root_window.h"
+#include "tests/cefclient/browser/util_gtk.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+namespace window_test {
+
+namespace {
+
+GtkWindow* GetWindow(CefRefPtr<CefBrowser> browser) {
+  scoped_refptr<RootWindow> root_window =
+      RootWindow::GetForBrowser(browser->GetIdentifier());
+  if (root_window) {
+    GtkWindow* window = GTK_WINDOW(root_window->GetWindowHandle());
+    if (!window)
+      LOG(ERROR) << "No GtkWindow for browser";
+    return window;
+  }
+  return nullptr;
+}
+
+bool IsMaximized(GtkWindow* window) {
+  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+  gint state = gdk_window_get_state(gdk_window);
+  return (state & GDK_WINDOW_STATE_MAXIMIZED) ? true : false;
+}
+
+void SetPosImpl(CefRefPtr<CefBrowser> browser,
+                int x,
+                int y,
+                int width,
+                int height) {
+  REQUIRE_MAIN_THREAD();
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkWindow* window = GetWindow(browser);
+  if (!window)
+    return;
+  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+
+  // Make sure the window isn't minimized or maximized.
+  if (IsMaximized(window))
+    gtk_window_unmaximize(window);
+  else
+    gtk_window_present(window);
+
+  // Retrieve information about the display that contains the window.
+  GdkScreen* screen = gdk_screen_get_default();
+  gint monitor = gdk_screen_get_monitor_at_window(screen, gdk_window);
+  GdkRectangle rect;
+  gdk_screen_get_monitor_geometry(screen, monitor, &rect);
+
+  // Make sure the window is inside the display.
+  CefRect display_rect(rect.x, rect.y, rect.width, rect.height);
+  CefRect window_rect(x, y, width, height);
+  WindowTestRunner::ModifyBounds(display_rect, window_rect);
+
+  gdk_window_move_resize(gdk_window, window_rect.x, window_rect.y,
+                         window_rect.width, window_rect.height);
+}
+
+void MinimizeImpl(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkWindow* window = GetWindow(browser);
+  if (!window)
+    return;
+
+  // Unmaximize the window before minimizing so restore behaves correctly.
+  if (IsMaximized(window))
+    gtk_window_unmaximize(window);
+
+  gtk_window_iconify(window);
+}
+
+void MaximizeImpl(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkWindow* window = GetWindow(browser);
+  if (!window)
+    return;
+  gtk_window_maximize(window);
+}
+
+void RestoreImpl(CefRefPtr<CefBrowser> browser) {
+  REQUIRE_MAIN_THREAD();
+  ScopedGdkThreadsEnter scoped_gdk_threads;
+
+  GtkWindow* window = GetWindow(browser);
+  if (!window)
+    return;
+  if (IsMaximized(window))
+    gtk_window_unmaximize(window);
+  else
+    gtk_window_present(window);
+}
+
+}  // namespace
+
+WindowTestRunnerGtk::WindowTestRunnerGtk() {}
+
+void WindowTestRunnerGtk::SetPos(CefRefPtr<CefBrowser> browser,
+                                 int x,
+                                 int y,
+                                 int width,
+                                 int height) {
+  MAIN_POST_CLOSURE(base::Bind(SetPosImpl, browser, x, y, width, height));
+}
+
+void WindowTestRunnerGtk::Minimize(CefRefPtr<CefBrowser> browser) {
+  MAIN_POST_CLOSURE(base::Bind(MinimizeImpl, browser));
+}
+
+void WindowTestRunnerGtk::Maximize(CefRefPtr<CefBrowser> browser) {
+  MAIN_POST_CLOSURE(base::Bind(MaximizeImpl, browser));
+}
+
+void WindowTestRunnerGtk::Restore(CefRefPtr<CefBrowser> browser) {
+  MAIN_POST_CLOSURE(base::Bind(RestoreImpl, browser));
+}
+
+}  // namespace window_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/window_test_runner_gtk.h b/src/tests/cefclient/browser/window_test_runner_gtk.h
new file mode 100644
index 0000000..c920fdb
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_gtk.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_GTK_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_GTK_H_
+#pragma once
+
+#include "tests/cefclient/browser/window_test_runner.h"
+
+namespace client {
+namespace window_test {
+
+// GTK platform implementation.
+class WindowTestRunnerGtk : public WindowTestRunner {
+ public:
+  WindowTestRunnerGtk();
+
+  void SetPos(CefRefPtr<CefBrowser> browser,
+              int x,
+              int y,
+              int width,
+              int height) OVERRIDE;
+  void Minimize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Maximize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Restore(CefRefPtr<CefBrowser> browser) OVERRIDE;
+};
+
+}  // namespace window_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_GTK_H_
diff --git a/src/tests/cefclient/browser/window_test_runner_mac.h b/src/tests/cefclient/browser/window_test_runner_mac.h
new file mode 100644
index 0000000..199d04a
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_mac.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_MAC_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_MAC_H_
+#pragma once
+
+#include "tests/cefclient/browser/window_test_runner.h"
+
+namespace client {
+namespace window_test {
+
+// Mac OS X platform implementation.
+class WindowTestRunnerMac : public WindowTestRunner {
+ public:
+  WindowTestRunnerMac();
+
+  void SetPos(CefRefPtr<CefBrowser> browser,
+              int x,
+              int y,
+              int width,
+              int height) OVERRIDE;
+  void Minimize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Maximize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Restore(CefRefPtr<CefBrowser> browser) OVERRIDE;
+};
+
+}  // namespace window_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_MAC_H_
diff --git a/src/tests/cefclient/browser/window_test_runner_mac.mm b/src/tests/cefclient/browser/window_test_runner_mac.mm
new file mode 100644
index 0000000..2a04d09
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_mac.mm
@@ -0,0 +1,92 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/window_test_runner_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "include/wrapper/cef_helpers.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+namespace window_test {
+
+namespace {
+
+NSWindow* GetWindow(CefRefPtr<CefBrowser> browser) {
+  NSView* view =
+      CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetHost()->GetWindowHandle());
+  return [view window];
+}
+
+}  // namespace
+
+WindowTestRunnerMac::WindowTestRunnerMac() {}
+
+void WindowTestRunnerMac::SetPos(CefRefPtr<CefBrowser> browser,
+                                 int x,
+                                 int y,
+                                 int width,
+                                 int height) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  NSWindow* window = GetWindow(browser);
+
+  // Make sure the window isn't minimized or maximized.
+  if ([window isMiniaturized])
+    [window deminiaturize:nil];
+  else if ([window isZoomed])
+    [window performZoom:nil];
+
+  // Retrieve information for the display that contains the window.
+  NSScreen* screen = [window screen];
+  if (screen == nil)
+    screen = [NSScreen mainScreen];
+  NSRect frame = [screen frame];
+  NSRect visibleFrame = [screen visibleFrame];
+
+  // Make sure the window is inside the display.
+  CefRect display_rect(
+      visibleFrame.origin.x,
+      frame.size.height - visibleFrame.size.height - visibleFrame.origin.y,
+      visibleFrame.size.width, visibleFrame.size.height);
+  CefRect window_rect(x, y, width, height);
+  ModifyBounds(display_rect, window_rect);
+
+  NSRect newRect;
+  newRect.origin.x = window_rect.x;
+  newRect.origin.y = frame.size.height - window_rect.height - window_rect.y;
+  newRect.size.width = window_rect.width;
+  newRect.size.height = window_rect.height;
+  [window setFrame:newRect display:YES];
+}
+
+void WindowTestRunnerMac::Minimize(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  [GetWindow(browser) performMiniaturize:nil];
+}
+
+void WindowTestRunnerMac::Maximize(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  [GetWindow(browser) performZoom:nil];
+}
+
+void WindowTestRunnerMac::Restore(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  REQUIRE_MAIN_THREAD();
+
+  NSWindow* window = GetWindow(browser);
+  if ([window isMiniaturized])
+    [window deminiaturize:nil];
+  else if ([window isZoomed])
+    [window performZoom:nil];
+}
+
+}  // namespace window_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/window_test_runner_views.cc b/src/tests/cefclient/browser/window_test_runner_views.cc
new file mode 100644
index 0000000..03aecf4
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_views.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/window_test_runner_views.h"
+
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_display.h"
+#include "include/views/cef_window.h"
+#include "include/wrapper/cef_helpers.h"
+
+namespace client {
+namespace window_test {
+
+namespace {
+
+CefRefPtr<CefWindow> GetWindow(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(browser->GetHost()->HasView());
+
+  CefRefPtr<CefBrowserView> browser_view =
+      CefBrowserView::GetForBrowser(browser);
+  DCHECK(browser_view.get());
+
+  CefRefPtr<CefWindow> window = browser_view->GetWindow();
+  DCHECK(window.get());
+  return window;
+}
+
+}  // namespace
+
+WindowTestRunnerViews::WindowTestRunnerViews() {}
+
+void WindowTestRunnerViews::SetPos(CefRefPtr<CefBrowser> browser,
+                                   int x,
+                                   int y,
+                                   int width,
+                                   int height) {
+  CefRefPtr<CefWindow> window = GetWindow(browser);
+
+  CefRect window_bounds(x, y, width, height);
+  ModifyBounds(window->GetDisplay()->GetWorkArea(), window_bounds);
+
+  window->SetBounds(window_bounds);
+}
+
+void WindowTestRunnerViews::Minimize(CefRefPtr<CefBrowser> browser) {
+  GetWindow(browser)->Minimize();
+}
+
+void WindowTestRunnerViews::Maximize(CefRefPtr<CefBrowser> browser) {
+  GetWindow(browser)->Maximize();
+}
+
+void WindowTestRunnerViews::Restore(CefRefPtr<CefBrowser> browser) {
+  GetWindow(browser)->Restore();
+}
+
+}  // namespace window_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/window_test_runner_views.h b/src/tests/cefclient/browser/window_test_runner_views.h
new file mode 100644
index 0000000..81334e4
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_views.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_VIEWS_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_VIEWS_H_
+#pragma once
+
+#include "tests/cefclient/browser/window_test_runner.h"
+
+namespace client {
+namespace window_test {
+
+// Views platform implementation.
+class WindowTestRunnerViews : public WindowTestRunner {
+ public:
+  WindowTestRunnerViews();
+
+  void SetPos(CefRefPtr<CefBrowser> browser,
+              int x,
+              int y,
+              int width,
+              int height) OVERRIDE;
+  void Minimize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Maximize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Restore(CefRefPtr<CefBrowser> browser) OVERRIDE;
+};
+
+}  // namespace window_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_VIEWS_H_
diff --git a/src/tests/cefclient/browser/window_test_runner_win.cc b/src/tests/cefclient/browser/window_test_runner_win.cc
new file mode 100644
index 0000000..de03c96
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_win.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/window_test_runner_win.h"
+
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+namespace window_test {
+
+namespace {
+
+HWND GetRootHwnd(CefRefPtr<CefBrowser> browser) {
+  return ::GetAncestor(browser->GetHost()->GetWindowHandle(), GA_ROOT);
+}
+
+// Toggles the current display state.
+void Toggle(HWND root_hwnd, UINT nCmdShow) {
+  // Retrieve current window placement information.
+  WINDOWPLACEMENT placement;
+  ::GetWindowPlacement(root_hwnd, &placement);
+
+  if (placement.showCmd == nCmdShow)
+    ::ShowWindow(root_hwnd, SW_RESTORE);
+  else
+    ::ShowWindow(root_hwnd, nCmdShow);
+}
+
+void SetPosImpl(CefRefPtr<CefBrowser> browser,
+                int x,
+                int y,
+                int width,
+                int height) {
+  HWND root_hwnd = GetRootHwnd(browser);
+  if (!root_hwnd)
+    return;
+
+  // Retrieve current window placement information.
+  WINDOWPLACEMENT placement;
+  ::GetWindowPlacement(root_hwnd, &placement);
+
+  // Retrieve information about the display that contains the window.
+  HMONITOR monitor =
+      MonitorFromRect(&placement.rcNormalPosition, MONITOR_DEFAULTTONEAREST);
+  MONITORINFO info;
+  info.cbSize = sizeof(info);
+  GetMonitorInfo(monitor, &info);
+
+  // Make sure the window is inside the display.
+  CefRect display_rect(info.rcWork.left, info.rcWork.top,
+                       info.rcWork.right - info.rcWork.left,
+                       info.rcWork.bottom - info.rcWork.top);
+  CefRect window_rect(x, y, width, height);
+  WindowTestRunner::ModifyBounds(display_rect, window_rect);
+
+  if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_MAXIMIZE) {
+    // The window is currently minimized or maximized. Restore it to the desired
+    // position.
+    placement.rcNormalPosition.left = window_rect.x;
+    placement.rcNormalPosition.right = window_rect.x + window_rect.width;
+    placement.rcNormalPosition.top = window_rect.y;
+    placement.rcNormalPosition.bottom = window_rect.y + window_rect.height;
+    ::SetWindowPlacement(root_hwnd, &placement);
+    ::ShowWindow(root_hwnd, SW_RESTORE);
+  } else {
+    // Set the window position.
+    ::SetWindowPos(root_hwnd, NULL, window_rect.x, window_rect.y,
+                   window_rect.width, window_rect.height, SWP_NOZORDER);
+  }
+}
+
+void MinimizeImpl(CefRefPtr<CefBrowser> browser) {
+  HWND root_hwnd = GetRootHwnd(browser);
+  if (!root_hwnd)
+    return;
+  Toggle(root_hwnd, SW_MINIMIZE);
+}
+
+void MaximizeImpl(CefRefPtr<CefBrowser> browser) {
+  HWND root_hwnd = GetRootHwnd(browser);
+  if (!root_hwnd)
+    return;
+  Toggle(root_hwnd, SW_MAXIMIZE);
+}
+
+void RestoreImpl(CefRefPtr<CefBrowser> browser) {
+  HWND root_hwnd = GetRootHwnd(browser);
+  if (!root_hwnd)
+    return;
+  ::ShowWindow(root_hwnd, SW_RESTORE);
+}
+
+}  // namespace
+
+WindowTestRunnerWin::WindowTestRunnerWin() {}
+
+void WindowTestRunnerWin::SetPos(CefRefPtr<CefBrowser> browser,
+                                 int x,
+                                 int y,
+                                 int width,
+                                 int height) {
+  if (CURRENTLY_ON_MAIN_THREAD()) {
+    SetPosImpl(browser, x, y, width, height);
+  } else {
+    // Execute on the main application thread.
+    MAIN_POST_CLOSURE(base::Bind(SetPosImpl, browser, x, y, width, height));
+  }
+}
+
+void WindowTestRunnerWin::Minimize(CefRefPtr<CefBrowser> browser) {
+  if (CURRENTLY_ON_MAIN_THREAD()) {
+    MinimizeImpl(browser);
+  } else {
+    // Execute on the main application thread.
+    MAIN_POST_CLOSURE(base::Bind(MinimizeImpl, browser));
+  }
+}
+
+void WindowTestRunnerWin::Maximize(CefRefPtr<CefBrowser> browser) {
+  if (CURRENTLY_ON_MAIN_THREAD()) {
+    MaximizeImpl(browser);
+  } else {
+    // Execute on the main application thread.
+    MAIN_POST_CLOSURE(base::Bind(MaximizeImpl, browser));
+  }
+}
+
+void WindowTestRunnerWin::Restore(CefRefPtr<CefBrowser> browser) {
+  if (CURRENTLY_ON_MAIN_THREAD()) {
+    RestoreImpl(browser);
+  } else {
+    // Execute on the main application thread.
+    MAIN_POST_CLOSURE(base::Bind(RestoreImpl, browser));
+  }
+}
+
+}  // namespace window_test
+}  // namespace client
diff --git a/src/tests/cefclient/browser/window_test_runner_win.h b/src/tests/cefclient/browser/window_test_runner_win.h
new file mode 100644
index 0000000..14ff9f7
--- /dev/null
+++ b/src/tests/cefclient/browser/window_test_runner_win.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_WIN_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_WIN_H_
+#pragma once
+
+#include "tests/cefclient/browser/window_test_runner.h"
+
+namespace client {
+namespace window_test {
+
+// Windows platform implementation. Methods are safe to call on any browser
+// process thread.
+class WindowTestRunnerWin : public WindowTestRunner {
+ public:
+  WindowTestRunnerWin();
+
+  void SetPos(CefRefPtr<CefBrowser> browser,
+              int x,
+              int y,
+              int width,
+              int height) OVERRIDE;
+  void Minimize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Maximize(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  void Restore(CefRefPtr<CefBrowser> browser) OVERRIDE;
+};
+
+}  // namespace window_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_WINDOW_TEST_RUNNER_WIN_H_
diff --git a/src/tests/cefclient/cefclient_gtk.cc b/src/tests/cefclient/cefclient_gtk.cc
new file mode 100644
index 0000000..02ae571
--- /dev/null
+++ b/src/tests/cefclient/cefclient_gtk.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <gtk/gtk.h>
+#include <gtk/gtkgl.h>
+
+#include <X11/Xlib.h>
+#undef Success     // Definition conflicts with cef_message_router.h
+#undef RootWindow  // Definition conflicts with root_window.h
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string>
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_app.h"
+#include "include/cef_command_line.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefclient/browser/main_context_impl.h"
+#include "tests/cefclient/browser/main_message_loop_multithreaded_gtk.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+#include "tests/shared/browser/main_message_loop_std.h"
+#include "tests/shared/common/client_app_other.h"
+#include "tests/shared/common/client_switches.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+namespace client {
+namespace {
+
+int XErrorHandlerImpl(Display* display, XErrorEvent* event) {
+  LOG(WARNING) << "X error received: "
+               << "type " << event->type << ", "
+               << "serial " << event->serial << ", "
+               << "error_code " << static_cast<int>(event->error_code) << ", "
+               << "request_code " << static_cast<int>(event->request_code)
+               << ", "
+               << "minor_code " << static_cast<int>(event->minor_code);
+  return 0;
+}
+
+int XIOErrorHandlerImpl(Display* display) {
+  return 0;
+}
+
+void TerminationSignalHandler(int signatl) {
+  LOG(ERROR) << "Received termination signal: " << signatl;
+  MainContext::Get()->GetRootWindowManager()->CloseAllWindows(true);
+}
+
+int RunMain(int argc, char* argv[]) {
+  // Create a copy of |argv| on Linux because Chromium mangles the value
+  // internally (see issue #620).
+  CefScopedArgArray scoped_arg_array(argc, argv);
+  char** argv_copy = scoped_arg_array.array();
+
+  CefMainArgs main_args(argc, argv);
+
+  // Parse command-line arguments.
+  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+  command_line->InitFromArgv(argc, argv);
+
+  // Create a ClientApp of the correct type.
+  CefRefPtr<CefApp> app;
+  ClientApp::ProcessType process_type = ClientApp::GetProcessType(command_line);
+  if (process_type == ClientApp::BrowserProcess) {
+    app = new ClientAppBrowser();
+  } else if (process_type == ClientApp::RendererProcess ||
+             process_type == ClientApp::ZygoteProcess) {
+    // On Linux the zygote process is used to spawn other process types. Since
+    // we don't know what type of process it will be give it the renderer
+    // client.
+    app = new ClientAppRenderer();
+  } else if (process_type == ClientApp::OtherProcess) {
+    app = new ClientAppOther();
+  }
+
+  // Execute the secondary process, if any.
+  int exit_code = CefExecuteProcess(main_args, app, nullptr);
+  if (exit_code >= 0)
+    return exit_code;
+
+  // Create the main context object.
+  scoped_ptr<MainContextImpl> context(new MainContextImpl(command_line, true));
+
+  CefSettings settings;
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
+// use of the sandbox.
+#if !defined(CEF_USE_SANDBOX)
+  settings.no_sandbox = true;
+#endif
+
+  // Populate the settings based on command line arguments.
+  context->PopulateSettings(&settings);
+
+  // Create the main message loop object.
+  scoped_ptr<MainMessageLoop> message_loop;
+  if (settings.multi_threaded_message_loop)
+    message_loop.reset(new MainMessageLoopMultithreadedGtk);
+  else if (settings.external_message_pump)
+    message_loop = MainMessageLoopExternalPump::Create();
+  else
+    message_loop.reset(new MainMessageLoopStd);
+
+  // Initialize CEF.
+  context->Initialize(main_args, settings, app, nullptr);
+
+  // The Chromium sandbox requires that there only be a single thread during
+  // initialization. Therefore initialize GTK after CEF.
+  gtk_init(&argc, &argv_copy);
+
+  // Perform gtkglext initialization required by the OSR example.
+  gtk_gl_init(&argc, &argv_copy);
+
+  // Install xlib error handlers so that the application won't be terminated
+  // on non-fatal errors. Must be done after initializing GTK.
+  XSetErrorHandler(XErrorHandlerImpl);
+  XSetIOErrorHandler(XIOErrorHandlerImpl);
+
+  // Install a signal handler so we clean up after ourselves.
+  signal(SIGINT, TerminationSignalHandler);
+  signal(SIGTERM, TerminationSignalHandler);
+
+  // Register scheme handlers.
+  test_runner::RegisterSchemeHandlers();
+
+  RootWindowConfig window_config;
+  window_config.always_on_top = command_line->HasSwitch(switches::kAlwaysOnTop);
+  window_config.with_controls =
+      !command_line->HasSwitch(switches::kHideControls);
+  window_config.with_osr = settings.windowless_rendering_enabled ? true : false;
+
+  // Create the first window.
+  context->GetRootWindowManager()->CreateRootWindow(window_config);
+
+  // Run the message loop. This will block until Quit() is called.
+  int result = message_loop->Run();
+
+  // Shut down CEF.
+  context->Shutdown();
+
+  // Release objects in reverse order of creation.
+  message_loop.reset();
+  context.reset();
+
+  return result;
+}
+
+}  // namespace
+}  // namespace client
+
+// Program entry point function.
+int main(int argc, char* argv[]) {
+  return client::RunMain(argc, argv);
+}
diff --git a/src/tests/cefclient/cefclient_mac.mm b/src/tests/cefclient/cefclient_mac.mm
new file mode 100644
index 0000000..e7bd659
--- /dev/null
+++ b/src/tests/cefclient/cefclient_mac.mm
@@ -0,0 +1,439 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+#include "include/cef_app.h"
+#import "include/cef_application_mac.h"
+#import "include/wrapper/cef_library_loader.h"
+#include "tests/cefclient/browser/main_context_impl.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/cefclient/browser/root_window.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+#include "tests/shared/browser/main_message_loop_std.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace {
+
+// Returns the top menu bar with the specified |tag|.
+NSMenuItem* GetMenuBarMenuWithTag(NSInteger tag) {
+  NSMenu* main_menu = [[NSApplication sharedApplication] mainMenu];
+  NSInteger found_index = [main_menu indexOfItemWithTag:tag];
+  if (found_index >= 0)
+    return [main_menu itemAtIndex:found_index];
+  return nil;
+}
+
+// Returns the item in |menu| that has the specified |action_selector|.
+NSMenuItem* GetMenuItemWithAction(NSMenu* menu, SEL action_selector) {
+  for (NSInteger i = 0; i < menu.numberOfItems; ++i) {
+    NSMenuItem* item = [menu itemAtIndex:i];
+    if (item.action == action_selector)
+      return item;
+  }
+  return nil;
+}
+
+}  // namespace
+
+// Receives notifications from the application. Will delete itself when done.
+@interface ClientAppDelegate : NSObject <NSApplicationDelegate> {
+ @private
+  bool with_controls_;
+  bool with_osr_;
+}
+
+- (id)initWithControls:(bool)with_controls andOsr:(bool)with_osr;
+- (void)createApplication:(id)object;
+- (void)tryToTerminateApplication:(NSApplication*)app;
+- (void)testsItemSelected:(int)command_id;
+- (IBAction)menuTestsGetText:(id)sender;
+- (IBAction)menuTestsGetSource:(id)sender;
+- (IBAction)menuTestsWindowNew:(id)sender;
+- (IBAction)menuTestsWindowPopup:(id)sender;
+- (IBAction)menuTestsRequest:(id)sender;
+- (IBAction)menuTestsPluginInfo:(id)sender;
+- (IBAction)menuTestsZoomIn:(id)sender;
+- (IBAction)menuTestsZoomOut:(id)sender;
+- (IBAction)menuTestsZoomReset:(id)sender;
+- (IBAction)menuTestsSetFPS:(id)sender;
+- (IBAction)menuTestsSetScaleFactor:(id)sender;
+- (IBAction)menuTestsTracingBegin:(id)sender;
+- (IBAction)menuTestsTracingEnd:(id)sender;
+- (IBAction)menuTestsPrint:(id)sender;
+- (IBAction)menuTestsPrintToPdf:(id)sender;
+- (IBAction)menuTestsMuteAudio:(id)sender;
+- (IBAction)menuTestsUnmuteAudio:(id)sender;
+- (IBAction)menuTestsOtherTests:(id)sender;
+- (void)enableAccessibility:(bool)bEnable;
+@end
+
+// Provide the CefAppProtocol implementation required by CEF.
+@interface ClientApplication : NSApplication <CefAppProtocol> {
+ @private
+  BOOL handlingSendEvent_;
+}
+@end
+
+@implementation ClientApplication
+
+- (BOOL)isHandlingSendEvent {
+  return handlingSendEvent_;
+}
+
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
+  handlingSendEvent_ = handlingSendEvent;
+}
+
+- (void)sendEvent:(NSEvent*)event {
+  CefScopedSendingEvent sendingEventScoper;
+  [super sendEvent:event];
+}
+
+// |-terminate:| is the entry point for orderly "quit" operations in Cocoa. This
+// includes the application menu's quit menu item and keyboard equivalent, the
+// application's dock icon menu's quit menu item, "quit" (not "force quit") in
+// the Activity Monitor, and quits triggered by user logout and system restart
+// and shutdown.
+//
+// The default |-terminate:| implementation ends the process by calling exit(),
+// and thus never leaves the main run loop. This is unsuitable for Chromium
+// since Chromium depends on leaving the main run loop to perform an orderly
+// shutdown. We support the normal |-terminate:| interface by overriding the
+// default implementation. Our implementation, which is very specific to the
+// needs of Chromium, works by asking the application delegate to terminate
+// using its |-tryToTerminateApplication:| method.
+//
+// |-tryToTerminateApplication:| differs from the standard
+// |-applicationShouldTerminate:| in that no special event loop is run in the
+// case that immediate termination is not possible (e.g., if dialog boxes
+// allowing the user to cancel have to be shown). Instead, this method tries to
+// close all browsers by calling CloseBrowser(false) via
+// ClientHandler::CloseAllBrowsers. Calling CloseBrowser will result in a call
+// to ClientHandler::DoClose and execution of |-performClose:| on the NSWindow.
+// DoClose sets a flag that is used to differentiate between new close events
+// (e.g., user clicked the window close button) and in-progress close events
+// (e.g., user approved the close window dialog). The NSWindowDelegate
+// |-windowShouldClose:| method checks this flag and either calls
+// CloseBrowser(false) in the case of a new close event or destructs the
+// NSWindow in the case of an in-progress close event.
+// ClientHandler::OnBeforeClose will be called after the CEF NSView hosted in
+// the NSWindow is dealloc'ed.
+//
+// After the final browser window has closed ClientHandler::OnBeforeClose will
+// begin actual tear-down of the application by calling CefQuitMessageLoop.
+// This ends the NSApplication event loop and execution then returns to the
+// main() function for cleanup before application termination.
+//
+// The standard |-applicationShouldTerminate:| is not supported, and code paths
+// leading to it must be redirected.
+- (void)terminate:(id)sender {
+  ClientAppDelegate* delegate = static_cast<ClientAppDelegate*>(
+      [[NSApplication sharedApplication] delegate]);
+  [delegate tryToTerminateApplication:self];
+  // Return, don't exit. The application is responsible for exiting on its own.
+}
+
+// Detect dynamically if VoiceOver is running. Like Chromium, rely upon the
+// undocumented accessibility attribute @"AXEnhancedUserInterface" which is set
+// when VoiceOver is launched and unset when VoiceOver is closed.
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
+  if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) {
+    ClientAppDelegate* delegate = static_cast<ClientAppDelegate*>(
+        [[NSApplication sharedApplication] delegate]);
+    [delegate enableAccessibility:([value intValue] == 1)];
+  }
+  return [super accessibilitySetValue:value forAttribute:attribute];
+}
+@end
+
+@implementation ClientAppDelegate
+
+- (id)initWithControls:(bool)with_controls andOsr:(bool)with_osr {
+  if (self = [super init]) {
+    with_controls_ = with_controls;
+    with_osr_ = with_osr;
+  }
+  return self;
+}
+
+// Create the application on the UI thread.
+- (void)createApplication:(id)object {
+  NSApplication* application = [NSApplication sharedApplication];
+
+  // The top menu is configured using Interface Builder (IB). To modify the menu
+  // start by loading MainMenu.xib in IB.
+  //
+  // To associate MainMenu.xib with ClientAppDelegate:
+  // 1. Select "File's Owner" from the "Placeholders" section in the left side
+  //    pane.
+  // 2. Load the "Identity inspector" tab in the top-right side pane.
+  // 3. In the "Custom Class" section set the "Class" value to
+  //    "ClientAppDelegate".
+  // 4. Pass an instance of ClientAppDelegate as the |owner| parameter to
+  //    loadNibNamed:.
+  //
+  // To create a new top menu:
+  // 1. Load the "Object library" tab in the bottom-right side pane.
+  // 2. Drag a "Submenu Menu Item" widget from the Object library to the desired
+  //    location in the menu bar shown in the center pane.
+  // 3. Select the newly created top menu by left clicking on it.
+  // 4. Load the "Attributes inspector" tab in the top-right side pane.
+  // 5. Under the "Menu Item" section set the "Tag" value to a unique integer.
+  //    This is necessary for the GetMenuBarMenuWithTag function to work
+  //    properly.
+  //
+  // To create a new menu item in a top menu:
+  // 1. Add a new receiver method in ClientAppDelegate (e.g. menuTestsDoStuff:).
+  // 2. Load the "Object library" tab in the bottom-right side pane.
+  // 3. Drag a "Menu Item" widget from the Object library to the desired
+  //    location in the menu bar shown in the center pane.
+  // 4. Double-click on the new menu item to set the label.
+  // 5. Right click on the new menu item to show the "Get Source" dialog.
+  // 6. In the "Sent Actions" section drag from the circle icon and drop on the
+  //    new receiver method in the ClientAppDelegate source code file.
+  //
+  // Load the top menu from MainMenu.xib.
+  [[NSBundle mainBundle] loadNibNamed:@"MainMenu"
+                                owner:self
+                      topLevelObjects:nil];
+
+  // Set the delegate for application events.
+  [application setDelegate:self];
+
+  if (!with_osr_) {
+    // Remove the OSR-related menu items when OSR is disabled.
+    NSMenuItem* tests_menu = GetMenuBarMenuWithTag(8);
+    if (tests_menu) {
+      NSMenuItem* set_fps_item = GetMenuItemWithAction(
+          tests_menu.submenu, @selector(menuTestsSetFPS:));
+      if (set_fps_item)
+        [tests_menu.submenu removeItem:set_fps_item];
+      NSMenuItem* set_scale_factor_item = GetMenuItemWithAction(
+          tests_menu.submenu, @selector(menuTestsSetScaleFactor:));
+      if (set_scale_factor_item)
+        [tests_menu.submenu removeItem:set_scale_factor_item];
+    }
+  }
+
+  client::RootWindowConfig window_config;
+  window_config.with_controls = with_controls_;
+  window_config.with_osr = with_osr_;
+
+  // Create the first window.
+  client::MainContext::Get()->GetRootWindowManager()->CreateRootWindow(
+      window_config);
+}
+
+- (void)tryToTerminateApplication:(NSApplication*)app {
+  client::MainContext::Get()->GetRootWindowManager()->CloseAllWindows(false);
+}
+
+- (void)orderFrontStandardAboutPanel:(id)sender {
+  [[NSApplication sharedApplication] orderFrontStandardAboutPanel:nil];
+}
+
+- (void)testsItemSelected:(int)command_id {
+  // Retrieve the active RootWindow.
+  NSWindow* key_window = [[NSApplication sharedApplication] keyWindow];
+  if (!key_window)
+    return;
+
+  scoped_refptr<client::RootWindow> root_window =
+      client::RootWindow::GetForNSWindow(key_window);
+
+  CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
+  if (browser.get())
+    client::test_runner::RunTest(browser, command_id);
+}
+
+- (IBAction)menuTestsGetText:(id)sender {
+  [self testsItemSelected:ID_TESTS_GETTEXT];
+}
+
+- (IBAction)menuTestsGetSource:(id)sender {
+  [self testsItemSelected:ID_TESTS_GETSOURCE];
+}
+
+- (IBAction)menuTestsWindowNew:(id)sender {
+  [self testsItemSelected:ID_TESTS_WINDOW_NEW];
+}
+
+- (IBAction)menuTestsWindowPopup:(id)sender {
+  [self testsItemSelected:ID_TESTS_WINDOW_POPUP];
+}
+
+- (IBAction)menuTestsRequest:(id)sender {
+  [self testsItemSelected:ID_TESTS_REQUEST];
+}
+
+- (IBAction)menuTestsPluginInfo:(id)sender {
+  [self testsItemSelected:ID_TESTS_PLUGIN_INFO];
+}
+
+- (IBAction)menuTestsZoomIn:(id)sender {
+  [self testsItemSelected:ID_TESTS_ZOOM_IN];
+}
+
+- (IBAction)menuTestsZoomOut:(id)sender {
+  [self testsItemSelected:ID_TESTS_ZOOM_OUT];
+}
+
+- (IBAction)menuTestsZoomReset:(id)sender {
+  [self testsItemSelected:ID_TESTS_ZOOM_RESET];
+}
+
+- (IBAction)menuTestsSetFPS:(id)sender {
+  [self testsItemSelected:ID_TESTS_OSR_FPS];
+}
+
+- (IBAction)menuTestsSetScaleFactor:(id)sender {
+  [self testsItemSelected:ID_TESTS_OSR_DSF];
+}
+
+- (IBAction)menuTestsTracingBegin:(id)sender {
+  [self testsItemSelected:ID_TESTS_TRACING_BEGIN];
+}
+
+- (IBAction)menuTestsTracingEnd:(id)sender {
+  [self testsItemSelected:ID_TESTS_TRACING_END];
+}
+
+- (IBAction)menuTestsPrint:(id)sender {
+  [self testsItemSelected:ID_TESTS_PRINT];
+}
+
+- (IBAction)menuTestsPrintToPdf:(id)sender {
+  [self testsItemSelected:ID_TESTS_PRINT_TO_PDF];
+}
+
+- (IBAction)menuTestsMuteAudio:(id)sender {
+  [self testsItemSelected:ID_TESTS_MUTE_AUDIO];
+}
+
+- (IBAction)menuTestsUnmuteAudio:(id)sender {
+  [self testsItemSelected:ID_TESTS_UNMUTE_AUDIO];
+}
+
+- (IBAction)menuTestsOtherTests:(id)sender {
+  [self testsItemSelected:ID_TESTS_OTHER_TESTS];
+}
+
+- (void)enableAccessibility:(bool)bEnable {
+  // Retrieve the active RootWindow.
+  NSWindow* key_window = [[NSApplication sharedApplication] keyWindow];
+  if (!key_window)
+    return;
+
+  scoped_refptr<client::RootWindow> root_window =
+      client::RootWindow::GetForNSWindow(key_window);
+
+  CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
+  if (browser.get()) {
+    browser->GetHost()->SetAccessibilityState(bEnable ? STATE_ENABLED
+                                                      : STATE_DISABLED);
+  }
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:
+    (NSApplication*)sender {
+  return NSTerminateNow;
+}
+
+@end
+
+namespace client {
+namespace {
+
+int RunMain(int argc, char* argv[]) {
+  // Load the CEF framework library at runtime instead of linking directly
+  // as required by the macOS sandbox implementation.
+  CefScopedLibraryLoader library_loader;
+  if (!library_loader.LoadInMain())
+    return 1;
+
+  int result = -1;
+
+  CefMainArgs main_args(argc, argv);
+
+  @autoreleasepool {
+    // Initialize the ClientApplication instance.
+    [ClientApplication sharedApplication];
+
+    // Parse command-line arguments.
+    CefRefPtr<CefCommandLine> command_line =
+        CefCommandLine::CreateCommandLine();
+    command_line->InitFromArgv(argc, argv);
+
+    // Create a ClientApp of the correct type.
+    CefRefPtr<CefApp> app;
+    ClientApp::ProcessType process_type =
+        ClientApp::GetProcessType(command_line);
+    if (process_type == ClientApp::BrowserProcess)
+      app = new ClientAppBrowser();
+
+    // Create the main context object.
+    scoped_ptr<MainContextImpl> context(
+        new MainContextImpl(command_line, true));
+
+    CefSettings settings;
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
+// use of the sandbox.
+#if !defined(CEF_USE_SANDBOX)
+    settings.no_sandbox = true;
+#endif
+
+    // Populate the settings based on command line arguments.
+    context->PopulateSettings(&settings);
+
+    // Create the main message loop object.
+    scoped_ptr<MainMessageLoop> message_loop;
+    if (settings.external_message_pump)
+      message_loop = MainMessageLoopExternalPump::Create();
+    else
+      message_loop.reset(new MainMessageLoopStd);
+
+    // Initialize CEF.
+    context->Initialize(main_args, settings, app, NULL);
+
+    // Register scheme handlers.
+    test_runner::RegisterSchemeHandlers();
+
+    // Create the application delegate and window.
+    ClientAppDelegate* delegate = [[ClientAppDelegate alloc]
+        initWithControls:!command_line->HasSwitch(switches::kHideControls)
+                  andOsr:settings.windowless_rendering_enabled ? true : false];
+    [delegate performSelectorOnMainThread:@selector(createApplication:)
+                               withObject:nil
+                            waitUntilDone:NO];
+
+    // Run the message loop. This will block until Quit() is called.
+    result = message_loop->Run();
+
+    // Shut down CEF.
+    context->Shutdown();
+
+    // Release objects in reverse order of creation.
+#if !__has_feature(objc_arc)
+    [delegate release];
+#endif  // !__has_feature(objc_arc)
+    delegate = nil;
+    message_loop.reset();
+    context.reset();
+  }  // @autoreleasepool
+
+  return result;
+}
+
+}  // namespace
+}  // namespace client
+
+// Entry point function for the browser process.
+int main(int argc, char* argv[]) {
+  return client::RunMain(argc, argv);
+}
diff --git a/src/tests/cefclient/cefclient_win.cc b/src/tests/cefclient/cefclient_win.cc
new file mode 100644
index 0000000..74166bb
--- /dev/null
+++ b/src/tests/cefclient/cefclient_win.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <windows.h>
+
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_command_line.h"
+#include "include/cef_sandbox_win.h"
+#include "tests/cefclient/browser/main_context_impl.h"
+#include "tests/cefclient/browser/main_message_loop_multithreaded_win.h"
+#include "tests/cefclient/browser/root_window_manager.h"
+#include "tests/cefclient/browser/test_runner.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+#include "tests/shared/browser/main_message_loop_std.h"
+#include "tests/shared/common/client_app_other.h"
+#include "tests/shared/common/client_switches.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
+// to the CMake command-line to disable use of the sandbox.
+// Uncomment this line to manually enable sandbox support.
+// #define CEF_USE_SANDBOX 1
+
+#if defined(CEF_USE_SANDBOX)
+// The cef_sandbox.lib static library may not link successfully with all VS
+// versions.
+#pragma comment(lib, "cef_sandbox.lib")
+#endif
+
+namespace client {
+namespace {
+
+int RunMain(HINSTANCE hInstance, int nCmdShow) {
+  // Enable High-DPI support on Windows 7 or newer.
+  CefEnableHighDPISupport();
+
+  CefMainArgs main_args(hInstance);
+
+  void* sandbox_info = nullptr;
+
+#if defined(CEF_USE_SANDBOX)
+  // Manage the life span of the sandbox information object. This is necessary
+  // for sandbox support on Windows. See cef_sandbox_win.h for complete details.
+  CefScopedSandboxInfo scoped_sandbox;
+  sandbox_info = scoped_sandbox.sandbox_info();
+#endif
+
+  // Parse command-line arguments.
+  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+  command_line->InitFromString(::GetCommandLineW());
+
+  // Create a ClientApp of the correct type.
+  CefRefPtr<CefApp> app;
+  ClientApp::ProcessType process_type = ClientApp::GetProcessType(command_line);
+  if (process_type == ClientApp::BrowserProcess)
+    app = new ClientAppBrowser();
+  else if (process_type == ClientApp::RendererProcess)
+    app = new ClientAppRenderer();
+  else if (process_type == ClientApp::OtherProcess)
+    app = new ClientAppOther();
+
+  // Execute the secondary process, if any.
+  int exit_code = CefExecuteProcess(main_args, app, sandbox_info);
+  if (exit_code >= 0)
+    return exit_code;
+
+  // Create the main context object.
+  scoped_ptr<MainContextImpl> context(new MainContextImpl(command_line, true));
+
+  CefSettings settings;
+
+#if !defined(CEF_USE_SANDBOX)
+  settings.no_sandbox = true;
+#endif
+
+  // Applications should specify a unique GUID here to enable trusted downloads.
+  CefString(&settings.application_client_id_for_file_scanning)
+      .FromString("9A8DE24D-B822-4C6C-8259-5A848FEA1E68");
+
+  // Populate the settings based on command line arguments.
+  context->PopulateSettings(&settings);
+
+  // Create the main message loop object.
+  scoped_ptr<MainMessageLoop> message_loop;
+  if (settings.multi_threaded_message_loop)
+    message_loop.reset(new MainMessageLoopMultithreadedWin);
+  else if (settings.external_message_pump)
+    message_loop = MainMessageLoopExternalPump::Create();
+  else
+    message_loop.reset(new MainMessageLoopStd);
+
+  // Initialize CEF.
+  context->Initialize(main_args, settings, app, sandbox_info);
+
+  // Register scheme handlers.
+  test_runner::RegisterSchemeHandlers();
+
+  RootWindowConfig window_config;
+  window_config.always_on_top = command_line->HasSwitch(switches::kAlwaysOnTop);
+  window_config.with_controls =
+      !command_line->HasSwitch(switches::kHideControls);
+  window_config.with_osr = settings.windowless_rendering_enabled ? true : false;
+
+  // Create the first window.
+  context->GetRootWindowManager()->CreateRootWindow(window_config);
+
+  // Run the message loop. This will block until Quit() is called by the
+  // RootWindowManager after all windows have been destroyed.
+  int result = message_loop->Run();
+
+  // Shut down CEF.
+  context->Shutdown();
+
+  // Release objects in reverse order of creation.
+  message_loop.reset();
+  context.reset();
+
+  return result;
+}
+
+}  // namespace
+}  // namespace client
+
+// Program entry point function.
+int APIENTRY wWinMain(HINSTANCE hInstance,
+                      HINSTANCE hPrevInstance,
+                      LPTSTR lpCmdLine,
+                      int nCmdShow) {
+  UNREFERENCED_PARAMETER(hPrevInstance);
+  UNREFERENCED_PARAMETER(lpCmdLine);
+  return client::RunMain(hInstance, nCmdShow);
+}
diff --git a/src/tests/cefclient/common/client_app_delegates_common.cc b/src/tests/cefclient/common/client_app_delegates_common.cc
new file mode 100644
index 0000000..99c3b86
--- /dev/null
+++ b/src/tests/cefclient/common/client_app_delegates_common.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/common/scheme_test_common.h"
+#include "tests/shared/common/client_app.h"
+
+namespace client {
+
+// static
+void ClientApp::RegisterCustomSchemes(
+    CefRawPtr<CefSchemeRegistrar> registrar,
+    std::vector<CefString>& cookiable_schemes) {
+  scheme_test::RegisterCustomSchemes(registrar, cookiable_schemes);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/common/scheme_test_common.cc b/src/tests/cefclient/common/scheme_test_common.cc
new file mode 100644
index 0000000..4cf337a
--- /dev/null
+++ b/src/tests/cefclient/common/scheme_test_common.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/common/scheme_test_common.h"
+
+#include "include/cef_scheme.h"
+
+namespace client {
+namespace scheme_test {
+
+void RegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar,
+                           std::vector<CefString>& cookiable_schemes) {
+  registrar->AddCustomScheme(
+      "client", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
+}
+
+}  // namespace scheme_test
+}  // namespace client
diff --git a/src/tests/cefclient/common/scheme_test_common.h b/src/tests/cefclient/common/scheme_test_common.h
new file mode 100644
index 0000000..859623d
--- /dev/null
+++ b/src/tests/cefclient/common/scheme_test_common.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_COMMON_SCHEME_TEST_COMMON_H_
+#define CEF_TESTS_CEFCLIENT_COMMON_SCHEME_TEST_COMMON_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_scheme.h"
+
+namespace client {
+namespace scheme_test {
+
+// Register the custom scheme name/type. This must be done in all processes.
+// See browser/scheme_test.h for creation/registration of the custom scheme
+// handler which only occurs in the browser process. Called from
+// client_app_delegates_common.cc.
+void RegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar,
+                           std::vector<CefString>& cookiable_schemes);
+
+}  // namespace scheme_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_COMMON_SCHEME_TEST_COMMON_H_
diff --git a/src/tests/cefclient/renderer/client_app_delegates_renderer.cc b/src/tests/cefclient/renderer/client_app_delegates_renderer.cc
new file mode 100644
index 0000000..bae180d
--- /dev/null
+++ b/src/tests/cefclient/renderer/client_app_delegates_renderer.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/renderer/client_renderer.h"
+#include "tests/cefclient/renderer/performance_test.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+namespace client {
+
+// static
+void ClientAppRenderer::CreateDelegates(DelegateSet& delegates) {
+  renderer::CreateDelegates(delegates);
+  performance_test::CreateDelegates(delegates);
+}
+
+}  // namespace client
diff --git a/src/tests/cefclient/renderer/client_renderer.cc b/src/tests/cefclient/renderer/client_renderer.cc
new file mode 100644
index 0000000..100fe2c
--- /dev/null
+++ b/src/tests/cefclient/renderer/client_renderer.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/renderer/client_renderer.h"
+
+#include <sstream>
+#include <string>
+
+#include "include/cef_crash_util.h"
+#include "include/cef_dom.h"
+#include "include/wrapper/cef_helpers.h"
+#include "include/wrapper/cef_message_router.h"
+
+namespace client {
+namespace renderer {
+
+namespace {
+
+// Must match the value in client_handler.cc.
+const char kFocusedNodeChangedMessage[] = "ClientRenderer.FocusedNodeChanged";
+
+class ClientRenderDelegate : public ClientAppRenderer::Delegate {
+ public:
+  ClientRenderDelegate() : last_node_is_editable_(false) {}
+
+  void OnRenderThreadCreated(CefRefPtr<ClientAppRenderer> app,
+                             CefRefPtr<CefListValue> extra_info) OVERRIDE {
+    if (CefCrashReportingEnabled()) {
+      // Set some crash keys for testing purposes. Keys must be defined in the
+      // "crash_reporter.cfg" file. See cef_crash_util.h for details.
+      CefSetCrashKeyValue("testkey_small1", "value1_small_renderer");
+      CefSetCrashKeyValue("testkey_small2", "value2_small_renderer");
+      CefSetCrashKeyValue("testkey_medium1", "value1_medium_renderer");
+      CefSetCrashKeyValue("testkey_medium2", "value2_medium_renderer");
+      CefSetCrashKeyValue("testkey_large1", "value1_large_renderer");
+      CefSetCrashKeyValue("testkey_large2", "value2_large_renderer");
+    }
+  }
+
+  void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) OVERRIDE {
+    // Create the renderer-side router for query handling.
+    CefMessageRouterConfig config;
+    message_router_ = CefMessageRouterRendererSide::Create(config);
+  }
+
+  void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefV8Context> context) OVERRIDE {
+    message_router_->OnContextCreated(browser, frame, context);
+  }
+
+  void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
+                         CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefV8Context> context) OVERRIDE {
+    message_router_->OnContextReleased(browser, frame, context);
+  }
+
+  void OnFocusedNodeChanged(CefRefPtr<ClientAppRenderer> app,
+                            CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefDOMNode> node) OVERRIDE {
+    bool is_editable = (node.get() && node->IsEditable());
+    if (is_editable != last_node_is_editable_) {
+      // Notify the browser of the change in focused element type.
+      last_node_is_editable_ = is_editable;
+      CefRefPtr<CefProcessMessage> message =
+          CefProcessMessage::Create(kFocusedNodeChangedMessage);
+      message->GetArgumentList()->SetBool(0, is_editable);
+      frame->SendProcessMessage(PID_BROWSER, message);
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) OVERRIDE {
+    return message_router_->OnProcessMessageReceived(browser, frame,
+                                                     source_process, message);
+  }
+
+ private:
+  bool last_node_is_editable_;
+
+  // Handles the renderer side of query routing.
+  CefRefPtr<CefMessageRouterRendererSide> message_router_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientRenderDelegate);
+  IMPLEMENT_REFCOUNTING(ClientRenderDelegate);
+};
+
+}  // namespace
+
+void CreateDelegates(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new ClientRenderDelegate);
+}
+
+}  // namespace renderer
+}  // namespace client
diff --git a/src/tests/cefclient/renderer/client_renderer.h b/src/tests/cefclient/renderer/client_renderer.h
new file mode 100644
index 0000000..0807645
--- /dev/null
+++ b/src/tests/cefclient/renderer/client_renderer.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_RENDERER_CLIENT_RENDERER_H_
+#define CEF_TESTS_CEFCLIENT_RENDERER_CLIENT_RENDERER_H_
+#pragma once
+
+#include "include/cef_base.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+namespace client {
+namespace renderer {
+
+// Create the renderer delegate. Called from client_app_delegates_renderer.cc.
+void CreateDelegates(ClientAppRenderer::DelegateSet& delegates);
+
+}  // namespace renderer
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_RENDERER_CLIENT_RENDERER_H_
diff --git a/src/tests/cefclient/renderer/performance_test.cc b/src/tests/cefclient/renderer/performance_test.cc
new file mode 100644
index 0000000..64fcaaa
--- /dev/null
+++ b/src/tests/cefclient/renderer/performance_test.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/renderer/performance_test.h"
+
+#include <algorithm>
+#include <string>
+
+#include "include/base/cef_logging.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/cefclient/renderer/performance_test_setup.h"
+
+namespace client {
+namespace performance_test {
+
+// Use more interations for a Release build.
+#if DCHECK_IS_ON()
+const int kDefaultIterations = 100000;
+#else
+const int kDefaultIterations = 10000;
+#endif
+
+namespace {
+
+const char kGetPerfTests[] = "GetPerfTests";
+const char kRunPerfTest[] = "RunPerfTest";
+const char kPerfTestReturnValue[] = "PerfTestReturnValue";
+
+class V8Handler : public CefV8Handler {
+ public:
+  V8Handler() {}
+
+  virtual bool Execute(const CefString& name,
+                       CefRefPtr<CefV8Value> object,
+                       const CefV8ValueList& arguments,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+    if (name == kRunPerfTest) {
+      if (arguments.size() == 1 && arguments[0]->IsString()) {
+        // Run the specified perf test.
+        bool found = false;
+
+        std::string test = arguments[0]->GetStringValue();
+        for (int i = 0; i < kPerfTestsCount; ++i) {
+          if (test == kPerfTests[i].name) {
+            // Execute the test.
+            int64 delta = kPerfTests[i].test(kPerfTests[i].iterations);
+
+            retval = CefV8Value::CreateInt(delta);
+            found = true;
+            break;
+          }
+        }
+
+        if (!found) {
+          std::string msg = "Unknown test: ";
+          msg.append(test);
+          exception = msg;
+        }
+      } else {
+        exception = "Invalid function parameters";
+      }
+    } else if (name == kGetPerfTests) {
+      // Retrieve the list of perf tests.
+      retval = CefV8Value::CreateArray(kPerfTestsCount);
+      for (int i = 0; i < kPerfTestsCount; ++i) {
+        CefRefPtr<CefV8Value> val = CefV8Value::CreateArray(2);
+        val->SetValue(0, CefV8Value::CreateString(kPerfTests[i].name));
+        val->SetValue(1, CefV8Value::CreateUInt(kPerfTests[i].iterations));
+        retval->SetValue(i, val);
+      }
+    } else if (name == kPerfTestReturnValue) {
+      if (arguments.size() == 0) {
+        retval = CefV8Value::CreateInt(1);
+      } else if (arguments.size() == 1 && arguments[0]->IsInt()) {
+        int32 type = arguments[0]->GetIntValue();
+        CefTime date;
+        switch (type) {
+          case 0:
+            retval = CefV8Value::CreateUndefined();
+            break;
+          case 1:
+            retval = CefV8Value::CreateNull();
+            break;
+          case 2:
+            retval = CefV8Value::CreateBool(true);
+            break;
+          case 3:
+            retval = CefV8Value::CreateInt(1);
+            break;
+          case 4:
+            retval = CefV8Value::CreateUInt(1);
+            break;
+          case 5:
+            retval = CefV8Value::CreateDouble(1.234);
+            break;
+          case 6:
+            date.Now();
+            retval = CefV8Value::CreateDate(date);
+            break;
+          case 7:
+            retval = CefV8Value::CreateString("Hello, world!");
+            break;
+          case 8:
+            retval = CefV8Value::CreateObject(nullptr, nullptr);
+            break;
+          case 9:
+            retval = CefV8Value::CreateArray(8);
+            break;
+          case 10:
+            // retval = CefV8Value::CreateFunction(...);
+            exception = "Not implemented";
+            break;
+          default:
+            exception = "Not supported";
+        }
+      }
+    }
+
+    return true;
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(V8Handler);
+};
+
+// Handle bindings in the render process.
+class RenderDelegate : public ClientAppRenderer::Delegate {
+ public:
+  RenderDelegate() {}
+
+  virtual void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefRefPtr<CefV8Context> context) OVERRIDE {
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+
+    CefRefPtr<CefV8Handler> handler = new V8Handler();
+
+    // Bind test functions.
+    object->SetValue(kGetPerfTests,
+                     CefV8Value::CreateFunction(kGetPerfTests, handler),
+                     V8_PROPERTY_ATTRIBUTE_READONLY);
+    object->SetValue(kRunPerfTest,
+                     CefV8Value::CreateFunction(kRunPerfTest, handler),
+                     V8_PROPERTY_ATTRIBUTE_READONLY);
+    object->SetValue(kPerfTestReturnValue,
+                     CefV8Value::CreateFunction(kPerfTestReturnValue, handler),
+                     V8_PROPERTY_ATTRIBUTE_READONLY);
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(RenderDelegate);
+};
+
+}  // namespace
+
+void CreateDelegates(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new RenderDelegate);
+}
+
+}  // namespace performance_test
+}  // namespace client
diff --git a/src/tests/cefclient/renderer/performance_test.h b/src/tests/cefclient/renderer/performance_test.h
new file mode 100644
index 0000000..ce016bf
--- /dev/null
+++ b/src/tests/cefclient/renderer/performance_test.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_RENDERER_PERFORMANCE_TEST_H_
+#define CEF_TESTS_CEFCLIENT_RENDERER_PERFORMANCE_TEST_H_
+#pragma once
+
+#include "tests/shared/renderer/client_app_renderer.h"
+
+namespace client {
+namespace performance_test {
+
+// Create the renderer delegate. Called from client_app_delegates_renderer.cc.
+void CreateDelegates(ClientAppRenderer::DelegateSet& delegates);
+
+}  // namespace performance_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_RENDERER_PERFORMANCE_TEST_H_
diff --git a/src/tests/cefclient/renderer/performance_test_setup.h b/src/tests/cefclient/renderer/performance_test_setup.h
new file mode 100644
index 0000000..7427a55
--- /dev/null
+++ b/src/tests/cefclient/renderer/performance_test_setup.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_RENDERER_PERFORMANCE_TEST_SETUP_H_
+#define CEF_TESTS_CEFCLIENT_RENDERER_PERFORMANCE_TEST_SETUP_H_
+#pragma once
+
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+
+namespace client {
+namespace performance_test {
+
+// Default number of iterations.
+extern const int kDefaultIterations;
+
+// Test name.
+#define PERF_TEST_NAME(name) PerfTest##name
+
+// Entry in test array.
+#define PERF_TEST_ENTRY_EX(name, iterations) \
+  { #name, PERF_TEST_NAME(name), iterations }
+#define PERF_TEST_ENTRY(name) PERF_TEST_ENTRY_EX(name, kDefaultIterations)
+
+// Test function declaration.
+#define PERF_TEST_RESULT int64
+#define PERF_TEST_PARAM_ITERATIONS iterations
+#define PERF_TEST_PARAMS int PERF_TEST_PARAM_ITERATIONS
+#define PERF_TEST_FUNC(name) \
+  PERF_TEST_RESULT PERF_TEST_NAME(name)(PERF_TEST_PARAMS)
+
+// Typedef for test pointers.
+typedef PERF_TEST_RESULT(PerfTest(PERF_TEST_PARAMS));
+
+class CefTimer {
+ public:
+  CefTimer() : running_(false) {}
+
+  bool IsRunning() { return running_; }
+
+  void Start() {
+    DCHECK(!running_);
+    running_ = true;
+    start_.Now();
+  }
+
+  void Stop() {
+    stop_.Now();
+    DCHECK(running_);
+    running_ = false;
+  }
+
+  int64 Delta() {
+    DCHECK(!running_);
+    return start_.Delta(stop_);
+  }
+
+ private:
+  bool running_;
+  CefTime start_;
+  CefTime stop_;
+
+  DISALLOW_COPY_AND_ASSIGN(CefTimer);
+};
+
+// Peform test iterations using a user-provided timing result variable.
+#define PERF_ITERATIONS_START_EX() \
+  {                                \
+    CefTimer _timer;               \
+    _timer.Start();                \
+    for (int _i = 0; _i < PERF_TEST_PARAM_ITERATIONS; ++_i) {
+#define PERF_ITERATIONS_END_EX(result) \
+  }                                    \
+  _timer.Stop();                       \
+  result = _timer.Delta();             \
+  }
+
+// Perform test iterations and return the timing result.
+#define PERF_ITERATIONS_START() \
+  int64 _result = 0;            \
+  PERF_ITERATIONS_START_EX()
+
+#define PERF_ITERATIONS_END()     \
+  PERF_ITERATIONS_END_EX(_result) \
+  return _result;
+
+// Perf test entry structure.
+struct PerfTestEntry {
+  const char* name;
+  PerfTest* test;
+  int iterations;
+};
+
+// Array of perf tests.
+extern const PerfTestEntry kPerfTests[];
+extern const int kPerfTestsCount;
+
+}  // namespace performance_test
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_RENDERER_PERFORMANCE_TEST_H_
diff --git a/src/tests/cefclient/renderer/performance_test_tests.cc b/src/tests/cefclient/renderer/performance_test_tests.cc
new file mode 100644
index 0000000..010fcf9
--- /dev/null
+++ b/src/tests/cefclient/renderer/performance_test_tests.cc
@@ -0,0 +1,392 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_v8.h"
+#include "tests/cefclient/renderer/performance_test.h"
+#include "tests/cefclient/renderer/performance_test_setup.h"
+
+namespace client {
+namespace performance_test {
+
+namespace {
+
+// Test function implementations.
+
+PERF_TEST_FUNC(V8NullCreate) {
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateNull();
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8BoolCreate) {
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateBool(true);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8IntCreate) {
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateInt(-5);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8UIntCreate) {
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateUInt(10);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8DoubleCreate) {
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateDouble(12.432);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8DateCreate) {
+  static cef_time_t time = {2012, 1, 0, 1};
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateDate(time);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8StringCreate) {
+  CefString str = "test string";
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateString(str);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ArrayCreate) {
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateArray(1);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ArraySetValue) {
+  CefRefPtr<CefV8Value> val = CefV8Value::CreateBool(true);
+  CefRefPtr<CefV8Value> array = CefV8Value::CreateArray(1);
+  array->SetValue(0, val);
+
+  PERF_ITERATIONS_START()
+  array->SetValue(0, val);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ArrayGetValue) {
+  CefRefPtr<CefV8Value> val = CefV8Value::CreateBool(true);
+  CefRefPtr<CefV8Value> array = CefV8Value::CreateArray(1);
+  array->SetValue(0, val);
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> ret = array->GetValue(0);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8FunctionCreate) {
+  class Handler : public CefV8Handler {
+   public:
+    Handler() {}
+    virtual bool Execute(const CefString& name,
+                         CefRefPtr<CefV8Value> object,
+                         const CefV8ValueList& arguments,
+                         CefRefPtr<CefV8Value>& retval,
+                         CefString& exception) OVERRIDE {
+      return false;
+    }
+    IMPLEMENT_REFCOUNTING(Handler);
+  };
+
+  CefString name = "name";
+  CefRefPtr<CefV8Handler> handler = new Handler();
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateFunction(name, handler);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8FunctionExecute) {
+  class Handler : public CefV8Handler {
+   public:
+    Handler() {}
+    virtual bool Execute(const CefString& name,
+                         CefRefPtr<CefV8Value> object,
+                         const CefV8ValueList& arguments,
+                         CefRefPtr<CefV8Value>& retval,
+                         CefString& exception) OVERRIDE {
+      return true;
+    }
+    IMPLEMENT_REFCOUNTING(Handler);
+  };
+
+  CefString name = "name";
+  CefRefPtr<CefV8Handler> handler = new Handler();
+  CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(name, handler);
+  CefRefPtr<CefV8Value> obj = CefV8Context::GetCurrentContext()->GetGlobal();
+  CefV8ValueList args;
+
+  PERF_ITERATIONS_START()
+  func->ExecuteFunction(obj, args);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8FunctionExecuteWithContext) {
+  class Handler : public CefV8Handler {
+   public:
+    Handler() {}
+    virtual bool Execute(const CefString& name,
+                         CefRefPtr<CefV8Value> object,
+                         const CefV8ValueList& arguments,
+                         CefRefPtr<CefV8Value>& retval,
+                         CefString& exception) OVERRIDE {
+      return true;
+    }
+    IMPLEMENT_REFCOUNTING(Handler);
+  };
+
+  CefString name = "name";
+  CefRefPtr<CefV8Handler> handler = new Handler();
+  CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(name, handler);
+  CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+  CefRefPtr<CefV8Value> obj = context->GetGlobal();
+  CefV8ValueList args;
+
+  PERF_ITERATIONS_START()
+  func->ExecuteFunctionWithContext(context, obj, args);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ObjectCreate) {
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateObject(nullptr, nullptr);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ObjectCreateWithAccessor) {
+  class Accessor : public CefV8Accessor {
+   public:
+    Accessor() {}
+    virtual bool Get(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    virtual bool Set(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     const CefRefPtr<CefV8Value> value,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    IMPLEMENT_REFCOUNTING(Accessor);
+  };
+
+  CefRefPtr<CefV8Accessor> accessor = new Accessor();
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateObject(accessor, nullptr);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ObjectCreateWithInterceptor) {
+  class Interceptor : public CefV8Interceptor {
+   public:
+    Interceptor() {}
+    virtual bool Get(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    virtual bool Get(int index,
+                     const CefRefPtr<CefV8Value> object,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    virtual bool Set(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     const CefRefPtr<CefV8Value> value,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    virtual bool Set(int index,
+                     const CefRefPtr<CefV8Value> object,
+                     const CefRefPtr<CefV8Value> value,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    IMPLEMENT_REFCOUNTING(Interceptor);
+  };
+
+  CefRefPtr<CefV8Interceptor> interceptor = new Interceptor();
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> value = CefV8Value::CreateObject(nullptr, interceptor);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ObjectSetValue) {
+  CefString name = "name";
+  CefRefPtr<CefV8Value> val = CefV8Value::CreateBool(true);
+  CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(nullptr, nullptr);
+  obj->SetValue(name, val, V8_PROPERTY_ATTRIBUTE_NONE);
+
+  PERF_ITERATIONS_START()
+  obj->SetValue(name, val, V8_PROPERTY_ATTRIBUTE_NONE);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ObjectGetValue) {
+  CefString name = "name";
+  CefRefPtr<CefV8Value> val = CefV8Value::CreateBool(true);
+  CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(nullptr, nullptr);
+  obj->SetValue(name, val, V8_PROPERTY_ATTRIBUTE_NONE);
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> ret = obj->GetValue(name);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ObjectSetValueWithAccessor) {
+  class Accessor : public CefV8Accessor {
+   public:
+    Accessor() {}
+    virtual bool Get(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    virtual bool Set(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     const CefRefPtr<CefV8Value> value,
+                     CefString& exception) OVERRIDE {
+      val_ = value;
+      return true;
+    }
+    CefRefPtr<CefV8Value> val_;
+    IMPLEMENT_REFCOUNTING(Accessor);
+  };
+
+  CefRefPtr<CefV8Accessor> accessor = new Accessor();
+
+  CefString name = "name";
+  CefRefPtr<CefV8Value> val = CefV8Value::CreateBool(true);
+  CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor, nullptr);
+  obj->SetValue(name, V8_ACCESS_CONTROL_DEFAULT, V8_PROPERTY_ATTRIBUTE_NONE);
+  obj->SetValue(name, val, V8_PROPERTY_ATTRIBUTE_NONE);
+
+  PERF_ITERATIONS_START()
+  obj->SetValue(name, val, V8_PROPERTY_ATTRIBUTE_NONE);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ObjectGetValueWithAccessor) {
+  class Accessor : public CefV8Accessor {
+   public:
+    Accessor() : val_(CefV8Value::CreateBool(true)) {}
+    virtual bool Get(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) OVERRIDE {
+      retval = val_;
+      return true;
+    }
+    virtual bool Set(const CefString& name,
+                     const CefRefPtr<CefV8Value> object,
+                     const CefRefPtr<CefV8Value> value,
+                     CefString& exception) OVERRIDE {
+      return true;
+    }
+    CefRefPtr<CefV8Value> val_;
+    IMPLEMENT_REFCOUNTING(Accessor);
+  };
+
+  CefRefPtr<CefV8Accessor> accessor = new Accessor();
+
+  CefString name = "name";
+  CefRefPtr<CefV8Value> val = CefV8Value::CreateBool(true);
+  CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor, nullptr);
+  obj->SetValue(name, V8_ACCESS_CONTROL_DEFAULT, V8_PROPERTY_ATTRIBUTE_NONE);
+  obj->SetValue(name, val, V8_PROPERTY_ATTRIBUTE_NONE);
+
+  PERF_ITERATIONS_START()
+  CefRefPtr<CefV8Value> ret = obj->GetValue(name);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ArrayBufferCreate) {
+  class ReleaseCallback : public CefV8ArrayBufferReleaseCallback {
+   public:
+    void ReleaseBuffer(void* buffer) override { std::free(buffer); }
+    IMPLEMENT_REFCOUNTING(ReleaseCallback);
+  };
+
+  size_t len = 1;
+  size_t byte_len = len * sizeof(float);
+  CefRefPtr<CefV8ArrayBufferReleaseCallback> callback = new ReleaseCallback();
+
+  PERF_ITERATIONS_START()
+  float* buffer = (float*)std::malloc(byte_len);
+  CefRefPtr<CefV8Value> ret =
+      CefV8Value::CreateArrayBuffer(buffer, byte_len, callback);
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ContextEnterExit) {
+  CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+
+  PERF_ITERATIONS_START()
+  context->Enter();
+  context->Exit();
+  PERF_ITERATIONS_END()
+}
+
+PERF_TEST_FUNC(V8ContextEval) {
+  CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+  CefString jsCode = "var i = 0;";
+  CefRefPtr<CefV8Value> retval;
+  CefRefPtr<CefV8Exception> exception;
+
+  PERF_ITERATIONS_START()
+  context->Eval(jsCode, CefString(), 0, retval, exception);
+  PERF_ITERATIONS_END()
+}
+
+}  // namespace
+
+// Test function entries.
+
+const PerfTestEntry kPerfTests[] = {
+    PERF_TEST_ENTRY(V8NullCreate),
+    PERF_TEST_ENTRY(V8BoolCreate),
+    PERF_TEST_ENTRY(V8IntCreate),
+    PERF_TEST_ENTRY(V8UIntCreate),
+    PERF_TEST_ENTRY(V8DoubleCreate),
+    PERF_TEST_ENTRY(V8DateCreate),
+    PERF_TEST_ENTRY(V8StringCreate),
+    PERF_TEST_ENTRY(V8ArrayCreate),
+    PERF_TEST_ENTRY(V8ArraySetValue),
+    PERF_TEST_ENTRY(V8ArrayGetValue),
+    PERF_TEST_ENTRY(V8FunctionCreate),
+    PERF_TEST_ENTRY(V8FunctionExecute),
+    PERF_TEST_ENTRY(V8FunctionExecuteWithContext),
+    PERF_TEST_ENTRY(V8ObjectCreate),
+    PERF_TEST_ENTRY(V8ObjectCreateWithAccessor),
+    PERF_TEST_ENTRY(V8ObjectCreateWithInterceptor),
+    PERF_TEST_ENTRY(V8ObjectSetValue),
+    PERF_TEST_ENTRY(V8ObjectGetValue),
+    PERF_TEST_ENTRY(V8ObjectSetValueWithAccessor),
+    PERF_TEST_ENTRY(V8ObjectGetValueWithAccessor),
+    PERF_TEST_ENTRY(V8ArrayBufferCreate),
+    PERF_TEST_ENTRY(V8ContextEnterExit),
+    PERF_TEST_ENTRY(V8ContextEval),
+};
+
+const int kPerfTestsCount = (sizeof(kPerfTests) / sizeof(kPerfTests[0]));
+
+}  // namespace performance_test
+}  // namespace client
diff --git a/src/tests/cefclient/resources/binding.html b/src/tests/cefclient/resources/binding.html
new file mode 100644
index 0000000..855e644
--- /dev/null
+++ b/src/tests/cefclient/resources/binding.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<title>Binding Test</title>
+<script language="JavaScript">
+
+function setup() {
+  if (location.hostname == 'tests' || location.hostname == 'localhost')
+    return;
+
+  alert('This page can only be run from tests or localhost.');
+
+  // Disable all elements.
+  var elements = document.getElementById("form").elements;
+  for (var i = 0, element; element = elements[i++]; ) {
+    element.disabled = true;
+  }
+}
+
+// Send a query to the browser process.
+function sendMessage() {
+  // Results in a call to the OnQuery method in binding_test.cc
+  window.cefQuery({
+    request: 'BindingTest:' + document.getElementById("message").value,
+    onSuccess: function(response) {
+      document.getElementById('result').value = 'Response: '+response;
+    },
+    onFailure: function(error_code, error_message) {}
+  });
+}
+</script>
+
+</head>
+<body bgcolor="white" onload="setup()">
+<form id="form">
+Message: <input type="text" id="message" value="My Message">
+<br/><input type="button" onclick="sendMessage();" value="Send Message">
+<br/>You should see the reverse of your message below:
+<br/><textarea rows="10" cols="40" id="result"></textarea>
+</form>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/dialogs.html b/src/tests/cefclient/resources/dialogs.html
new file mode 100644
index 0000000..fb1321c
--- /dev/null
+++ b/src/tests/cefclient/resources/dialogs.html
@@ -0,0 +1,77 @@
+<html>
+<head>
+<title>Dialog Test</title>
+<script>
+function show_alert() {
+  alert("I am an alert box!");
+}
+
+function show_confirm() {
+  var r = confirm("Press a button");
+  var msg = r ? "You pressed OK!" : "You pressed Cancel!";
+  document.getElementById('cm').innerText = msg;
+}
+
+function show_prompt() {
+  var name = prompt("Please enter your name" ,"Harry Potter");
+  if (name != null && name != "")
+    document.getElementById('pm').innerText = "Hello " + name + "!";
+}
+
+window.onbeforeunload = function() {
+  return 'This is an onbeforeunload message.';
+}
+
+function update_time() {
+  document.getElementById('time').innerText = new Date().toLocaleString();
+}
+
+function setup() {
+  update_time();
+  setInterval(update_time, 1000);
+
+  if (location.hostname != 'tests' && location.hostname != 'localhost') {
+    alert('Parts of this page can only be run from tests or localhost.');
+    return;
+  }
+
+  // Enable all elements.
+  var elements = document.getElementById("form").elements;
+  for (var i = 0, element; element = elements[i++]; ) {
+    element.disabled = false;
+  }
+}
+
+function show_file_dialog(element, test) {
+  var message = 'DialogTest.' + test;
+  var target = document.getElementById(element);
+
+  // Results in a call to the OnQuery method in dialog_test.cpp
+  window.cefQuery({
+    request: message,
+    onSuccess: function(response) {
+      target.innerText = response;
+    },
+    onFailure: function(error_code, error_message) {}
+  });
+}
+
+window.addEventListener('load', setup, false);
+</script>
+</head>
+<body bgcolor="white">
+<form id="form">
+Click a button to show the associated dialog type.
+<br/><input type="button" onclick="show_alert();" value="Show Alert">
+<br/><input type="button" onclick="show_confirm();" value="Show Confirm"> <span id="cm"></span>
+<br/><input type="button" onclick="show_prompt();" value="Show Prompt"> <span id="pm"></span>
+<br/>input type="file": <input type="file" name="pic" accept="text/*,.js,.css,image/*">
+<br/>input type="file" (directory): <input type="file" webkitdirectory  accept="text/*,.js,.css,image/*">
+<br/><input type="button" onclick="show_file_dialog('fo', 'FileOpen');" value="Show File Open" disabled="true"> <span id="fo"></span>
+<br/><input type="button" onclick="show_file_dialog('fom', 'FileOpenMultiple');" value="Show File Open Multiple" disabled="true"> <span id="fom"></span>
+<br/><input type="button" onclick="show_file_dialog('fof', 'FileOpenFolder');" value="Show File Open Folder" disabled="true"> <span id="fof"></span>
+<br/><input type="button" onclick="show_file_dialog('fs', 'FileSave');" value="Show File Save" disabled="true"> <span id="fs"></span>
+<p id="time"></p>
+</form>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/draggable.html b/src/tests/cefclient/resources/draggable.html
new file mode 100644
index 0000000..6b1ecf0
--- /dev/null
+++ b/src/tests/cefclient/resources/draggable.html
@@ -0,0 +1,34 @@
+<html>
+<head>
+<title>Draggable Regions Test</title>
+<style>
+.draggable {
+  -webkit-app-region: drag;
+  position: absolute;
+  top: 125px;
+  left: 50px;
+  width: 200px;
+  height: 200px;
+  background-color: red;
+}
+.nondraggable {
+  -webkit-app-region: no-drag;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 50px;
+  height: 50px;
+  background-color: blue;
+}
+</style>
+</head>
+<body bgcolor="white">
+  Draggable regions can be defined using the -webkit-app-region CSS property.
+  <br/>In the below example the red region is draggable and the blue sub-region is non-draggable.
+  <br/>Windows can be resized by default and closed using JavaScript <a href="#" onClick="window.close(); return false;">window.close()</a>.
+  <div class="draggable">
+    <div class="nondraggable"></div>
+  </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/tests/cefclient/resources/drm.html b/src/tests/cefclient/resources/drm.html
new file mode 100644
index 0000000..cfb1cc0
--- /dev/null
+++ b/src/tests/cefclient/resources/drm.html
@@ -0,0 +1,141 @@
+<html>
+<head>
+<title>DRM Test</title>
+<script language="JavaScript">
+
+function setup() {
+  if (location.hostname == 'tests' || location.hostname == 'localhost')
+    return;
+
+  alert('This page can only be run from tests or localhost.');
+
+  // Disable all elements.
+  var elements = document.getElementById("form").elements;
+  for (var i = 0, element; element = elements[i++]; ) {
+    element.disabled = true;
+  }
+
+  doTest();
+}
+
+// Based on DRM detection code from https://github.com/google/shaka-player
+function probeSupport() {
+  var tests = [];
+  var testKeySystems = [
+    'org.w3.clearkey',
+    'com.widevine.alpha',
+  ];
+
+  var basicVideoCapabilities = [
+    { contentType: 'video/mp4; codecs="avc1.42E01E"' },
+    { contentType: 'video/webm; codecs="vp9"' }
+  ];
+
+  var basicConfig = {
+    videoCapabilities: basicVideoCapabilities
+  };
+  var offlineConfig = {
+    videoCapabilities: basicVideoCapabilities,
+    persistentState: 'required',
+    sessionTypes: ['persistent-license']
+  };
+
+  // Try the offline config first, then fall back to the basic config.
+  var configs = [offlineConfig, basicConfig];
+
+  var support = {};
+  testKeySystems.forEach(function(keySystem) {
+    var p = navigator.requestMediaKeySystemAccess(keySystem, configs)
+        .then(function(access) {
+          // We can't (yet) trust every browser to report the session types they
+          // support in access.getConfiguration().  Therefore, we create a
+          // MediaKeys object and try to create an offline session.
+          // https://goo.gl/gtYT3z, https://goo.gl/rvnB1g, https://goo.gl/z0URJ0
+          var mediaKeys = access.createMediaKeys();
+          var persistentState = false;
+          try {
+            // This will throw if persistent licenses are not supported.
+            mediaKeys.createSession('persistent-license');
+            persistentState = true;
+          } catch (e) {}
+
+          support[keySystem] = {persistentState: persistentState};
+        }, function() {
+          support[keySystem] = null;
+        });
+    tests.push(p);
+  });
+
+  return Promise.all(tests).then(function() {
+    return support;
+  });
+}
+
+function getWidevineCdmInfo() {
+  if (!('Widevine Content Decryption Module' in navigator.plugins)) {
+    return "Widevine CDM plugin not loaded.";
+  }
+
+  var plugin = navigator.plugins['Widevine Content Decryption Module'];
+  return "Widevine CDM plugin:\n" + JSON.stringify({
+    "name": plugin.name,
+    "filename": plugin.filename,
+    "description": plugin.description,
+  }, null, '  ')
+}
+
+function printSupport(support) {
+  var output = document.getElementById('output');
+  output.textContent = getWidevineCdmInfo() + "\n\nDRM Support:\n" + support;
+}
+
+function doTest() {
+  probeSupport().then(function(support) {
+    printSupport(JSON.stringify(support, null, '  '));
+  });
+}
+
+// Send a message to the browser process.
+function sendMessage() {
+  // Create the request object.
+  var request = {};
+  request.widevine_cdm_path =
+      document.getElementById("widevine_cdm_path").value;
+
+  // Results in a call to the OnQuery method in drm_test.cc
+  window.cefQuery({
+    request: JSON.stringify(request),
+    onSuccess: function(response) {
+      alert('Widevine CDM plugin loaded successfully!');
+      // Registration succeeded so test again.
+      doTest();
+    },
+    onFailure: function(error_code, error_message) {
+      alert(error_message + ' (' + error_code + ')');
+    }
+  });
+}
+
+</script>
+
+</head>
+<body bgcolor="white" onload="setup()">
+Important notes:
+<ul>
+<li>Clearkey support is built in and should always be enabled.</li>
+<li>Widevine support requires CDM binaries that must be downloaded from Google. Contact Google <a href="https://www.widevine.com/contact.html">here</a> for details.</li>
+<li>Widevine support is enabled by calling the CefRegisterWidevineCdm() function. See comments in cef_web_plugin.h for usage.</li>
+<li>The CefRegisterWidevineCdm() function can be called during runtime on Windows and OS X to register Widevine binaries. Use the below form to test this capability.</li>
+<li>Calling CefRegisterWidevineCdm() before CefInitialize() is required on Linux.</li>
+<li>Cefclient will call CefRegisterWidevineCdm() before CefInitialize() if "--widevine-cdm-path=&lt;path&gt;" is specified on the command-line.</li>
+<li>View extended media support information <a href="https://shaka-player-demo.appspot.com/support.html">here</a>.</li>
+<li>Test DRM video playback <a href="https://shaka-player-demo.appspot.com/demo/">here</a>. Select an "asset" that includes Clearkey or Widevine in the name.</li>
+</ul>
+
+<form id="form">
+Widevine CDM Path: <input type="text" id="widevine_cdm_path" value="" size="40">
+<input type="button" onclick="sendMessage();" value="Load CDM">
+</form>
+<pre id="output"></pre>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/extensions/set_page_color/README.md b/src/tests/cefclient/resources/extensions/set_page_color/README.md
new file mode 100644
index 0000000..550bac3
--- /dev/null
+++ b/src/tests/cefclient/resources/extensions/set_page_color/README.md
@@ -0,0 +1,17 @@
+# Color Extension
+
+Demonstrates basic extension app loading and integration by using a popup to change the page color.
+
+## Usage
+
+Run `cefclient --load-extension=set_page_color`.
+
+When using the Views framework (`--use-views`) an extension icon will be added to the control bar and clicking the icon will open the extension window. When not using the Views framework an extension window will be opened automatically on application start.
+
+## Implementation
+
+Based on the [set_page_color](https://developer.chrome.com/extensions/samples#search:browser%20action%20with%20a%20popup) example extension.
+
+Calls:
+
+ * [tabs.executeScript](https://developer.chrome.com/extensions/tabs#method-executeScript)
diff --git a/src/tests/cefclient/resources/extensions/set_page_color/icon.png b/src/tests/cefclient/resources/extensions/set_page_color/icon.png
new file mode 100644
index 0000000..46819c7
--- /dev/null
+++ b/src/tests/cefclient/resources/extensions/set_page_color/icon.png
Binary files differ
diff --git a/src/tests/cefclient/resources/extensions/set_page_color/manifest.json b/src/tests/cefclient/resources/extensions/set_page_color/manifest.json
new file mode 100644
index 0000000..48f551d
--- /dev/null
+++ b/src/tests/cefclient/resources/extensions/set_page_color/manifest.json
@@ -0,0 +1,14 @@
+{
+  "name": "A browser action with a popup that changes the page color",
+  "description": "Change the current page color",
+  "version": "1.0",
+  "permissions": [
+    "tabs", "http://*/*", "https://*/*"
+  ],
+  "browser_action": {
+      "default_title": "Set this page's color.",
+      "default_icon": "icon.png",
+      "default_popup": "popup.html"
+  },
+  "manifest_version": 2
+}
diff --git a/src/tests/cefclient/resources/extensions/set_page_color/popup.html b/src/tests/cefclient/resources/extensions/set_page_color/popup.html
new file mode 100644
index 0000000..bf1b42b
--- /dev/null
+++ b/src/tests/cefclient/resources/extensions/set_page_color/popup.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Set Page Color Popup</title>
+    <style>
+    body {
+      overflow: hidden;
+      margin: 0px;
+      padding: 0px;
+      background: white;
+    }
+
+    div:first-child {
+      margin-top: 0px;
+    }
+
+    div {
+      cursor: pointer;
+      text-align: center;
+      padding: 1px 3px;
+      font-family: sans-serif;
+      font-size: 0.8em;
+      width: 100px;
+      margin-top: 1px;
+      background: #cccccc;
+    }
+    div:hover {
+      background: #aaaaaa;
+    }
+    #red {
+      border: 1px solid red;
+      color: red;
+    }
+    #blue {
+      border: 1px solid blue;
+      color: blue;
+    }
+    #green {
+      border: 1px solid green;
+      color: green;
+    }
+    #yellow {
+      border: 1px solid yellow;
+      color: yellow;
+    }
+    </style>
+    <script src="popup.js"></script>
+  </head>
+  <body>
+    <div id="red">red</div>
+    <div id="blue">blue</div>
+    <div id="green">green</div>
+    <div id="yellow">yellow</div>
+  </body>
+</html>
diff --git a/src/tests/cefclient/resources/extensions/set_page_color/popup.js b/src/tests/cefclient/resources/extensions/set_page_color/popup.js
new file mode 100644
index 0000000..6f62708
--- /dev/null
+++ b/src/tests/cefclient/resources/extensions/set_page_color/popup.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+function click(e) {
+  chrome.tabs.executeScript(null,
+      {code:"document.body.style.backgroundColor='" + e.target.id + "'"});
+}
+
+document.addEventListener('DOMContentLoaded', function () {
+  var divs = document.querySelectorAll('div');
+  for (var i = 0; i < divs.length; i++) {
+    divs[i].addEventListener('click', click);
+  }
+});
diff --git a/src/tests/cefclient/resources/localstorage.html b/src/tests/cefclient/resources/localstorage.html
new file mode 100644
index 0000000..87c6e68
--- /dev/null
+++ b/src/tests/cefclient/resources/localstorage.html
@@ -0,0 +1,24 @@
+<html>
+<body bgcolor="white">
+<script language="JavaScript">
+var val = window.localStorage.getItem('val');
+function addLine() {
+  if(val == null)
+    val = '<br/>One Line.';
+  else
+    val += '<br/>Another Line.';
+  window.localStorage.setItem('val', val);
+  document.getElementById('out').innerHTML = val;
+}
+</script>
+Click the "Add Line" button to add a line or the "Clear" button to clear.<br/>
+This data will persist across sessions if a cache path was specified.<br/>
+<input type="button" value="Add Line" onClick="addLine();"/>
+<input type="button" value="Clear" onClick="window.localStorage.removeItem('val'); window.location.reload();"/>
+<div id="out"></div>
+<script language="JavaScript">
+if(val != null)
+  document.getElementById('out').innerHTML = val;
+</script>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/logo.png b/src/tests/cefclient/resources/logo.png
new file mode 100644
index 0000000..a2a15f4
--- /dev/null
+++ b/src/tests/cefclient/resources/logo.png
Binary files differ
diff --git a/src/tests/cefclient/resources/mac/English.lproj/InfoPlist.strings b/src/tests/cefclient/resources/mac/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..fe2abe1
--- /dev/null
+++ b/src/tests/cefclient/resources/mac/English.lproj/InfoPlist.strings
@@ -0,0 +1,3 @@
+/* Localized versions of Info.plist keys */
+
+NSHumanReadableCopyright = "© Chromium Embedded Framework Authors, 2010";
diff --git a/src/tests/cefclient/resources/mac/English.lproj/MainMenu.xib b/src/tests/cefclient/resources/mac/English.lproj/MainMenu.xib
new file mode 100644
index 0000000..d54bff2
--- /dev/null
+++ b/src/tests/cefclient/resources/mac/English.lproj/MainMenu.xib
@@ -0,0 +1,433 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16B2657" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+    <dependencies>
+        <deployment version="1090" identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="ClientAppDelegate"/>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
+            <items>
+                <menuItem title="cefclient" tag="1" id="56">
+                    <menu key="submenu" title="TestShell" systemMenu="apple" id="57">
+                        <items>
+                            <menuItem title="About cefclient" id="58">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="236">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121"/>
+                            <menuItem isSeparatorItem="YES" id="143">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Services" id="131">
+                                <menu key="submenu" title="Services" systemMenu="services" id="130"/>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="144">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Hide cefclient" keyEquivalent="h" id="134">
+                                <connections>
+                                    <action selector="hide:" target="-1" id="367"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Hide Others" keyEquivalent="h" id="145">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="hideOtherApplications:" target="-1" id="368"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Show All" id="150">
+                                <connections>
+                                    <action selector="unhideAllApplications:" target="-1" id="370"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="149">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Quit cefclient" keyEquivalent="q" id="136" userLabel="1111">
+                                <connections>
+                                    <action selector="terminate:" target="-1" id="369"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="File" tag="2" id="83">
+                    <menu key="submenu" title="File" id="81">
+                        <items>
+                            <menuItem title="New" keyEquivalent="n" id="82" userLabel="9">
+                                <connections>
+                                    <action selector="newDocument:" target="-1" id="373"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open…" keyEquivalent="o" id="72">
+                                <connections>
+                                    <action selector="openDocument:" target="-1" id="374"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open Recent" id="124">
+                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
+                                    <items>
+                                        <menuItem title="Clear Menu" id="126">
+                                            <connections>
+                                                <action selector="clearRecentDocuments:" target="-1" id="127"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="79" userLabel="7">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Close" keyEquivalent="w" id="73" userLabel="1">
+                                <connections>
+                                    <action selector="performClose:" target="-1" id="193"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save" keyEquivalent="s" id="75" userLabel="3">
+                                <connections>
+                                    <action selector="saveDocument:" target="-1" id="362"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save As…" keyEquivalent="S" id="80" userLabel="8">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="saveDocumentAs:" target="-1" id="363"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Revert to Saved" id="112" userLabel="10">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="revertDocumentToSaved:" target="-1" id="364"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="74" userLabel="2">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Page Setup..." keyEquivalent="P" id="77" userLabel="5">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="runPageLayout:" target="-1" id="87"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print…" keyEquivalent="p" id="78" userLabel="6">
+                                <connections>
+                                    <action selector="print:" target="-1" id="86"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Edit" tag="3" id="217">
+                    <menu key="submenu" title="Edit" id="205">
+                        <items>
+                            <menuItem title="Undo" keyEquivalent="z" id="207">
+                                <connections>
+                                    <action selector="undo:" target="-1" id="223"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Redo" keyEquivalent="Z" id="215">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="redo:" target="-1" id="231"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="206">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Cut" keyEquivalent="x" id="199">
+                                <connections>
+                                    <action selector="cut:" target="-1" id="228"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Copy" keyEquivalent="c" id="197">
+                                <connections>
+                                    <action selector="copy:" target="-1" id="224"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste" keyEquivalent="v" id="203">
+                                <connections>
+                                    <action selector="paste:" target="-1" id="226"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Delete" id="202">
+                                <connections>
+                                    <action selector="delete:" target="-1" id="235"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Select All" keyEquivalent="a" id="198">
+                                <connections>
+                                    <action selector="selectAll:" target="-1" id="232"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="214">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Find" id="218">
+                                <menu key="submenu" title="Find" id="220">
+                                    <items>
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="241"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
+                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                        </menuItem>
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="210">
+                                            <connections>
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Spelling and Grammar" id="216">
+                                <menu key="submenu" title="Spelling and Grammar" id="200">
+                                    <items>
+                                        <menuItem title="Show Spelling…" keyEquivalent=":" id="204">
+                                            <connections>
+                                                <action selector="showGuessPanel:" target="-1" id="230"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Spelling" keyEquivalent=";" id="201">
+                                            <connections>
+                                                <action selector="checkSpelling:" target="-1" id="225"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Spelling While Typing" id="219">
+                                            <connections>
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Grammar With Spelling" id="346">
+                                            <connections>
+                                                <action selector="toggleGrammarChecking:" target="-1" id="347"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Substitutions" id="348">
+                                <menu key="submenu" title="Substitutions" id="349">
+                                    <items>
+                                        <menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
+                                            <connections>
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
+                                            <connections>
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
+                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Speech" id="211">
+                                <menu key="submenu" title="Speech" id="212">
+                                    <items>
+                                        <menuItem title="Start Speaking" id="196">
+                                            <connections>
+                                                <action selector="startSpeaking:" target="-1" id="233"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Stop Speaking" id="195">
+                                            <connections>
+                                                <action selector="stopSpeaking:" target="-1" id="227"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Format" tag="4" id="299">
+                    <menu key="submenu" title="Format" id="300">
+                        <items>
+                            <menuItem title="Show Fonts" keyEquivalent="t" id="344"/>
+                            <menuItem title="Show Colors" keyEquivalent="C" id="345">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="orderFrontColorPanel:" target="-1" id="361"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="View" tag="5" id="295">
+                    <menu key="submenu" title="View" id="296">
+                        <items>
+                            <menuItem title="Show Toolbar" keyEquivalent="t" id="297">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleToolbarShown:" target="-1" id="366"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Customize Toolbar…" id="298">
+                                <connections>
+                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Window" tag="6" id="19">
+                    <menu key="submenu" title="Window" systemMenu="window" id="24">
+                        <items>
+                            <menuItem title="Minimize" keyEquivalent="m" id="23">
+                                <connections>
+                                    <action selector="performMiniaturize:" target="-1" id="37"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom" id="239">
+                                <connections>
+                                    <action selector="performZoom:" target="-1" id="240"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="92">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Bring All to Front" id="5">
+                                <connections>
+                                    <action selector="arrangeInFront:" target="-1" id="39"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Help" tag="7" id="103" userLabel="1">
+                    <menu key="submenu" title="Help" id="106" userLabel="2">
+                        <items>
+                            <menuItem title="cefclient Help" keyEquivalent="?" id="111">
+                                <connections>
+                                    <action selector="showHelp:" target="-1" id="360"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Tests" tag="8" id="Yv2-Jq-Amk">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Tests" id="CkQ-OF-S73">
+                        <items>
+                            <menuItem title="Get Text" id="Lpo-DT-bV3">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsGetText:" target="-2" id="wHm-G4-hGW"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Get Source" id="hhS-PS-Frj">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsGetSource:" target="-2" id="zj9-5Y-5WK"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="New Window" id="I90-8O-ZQB">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsWindowNew:" target="-2" id="vBa-IJ-3KK"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Popup Window" id="a52-WG-ltY">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsWindowPopup:" target="-2" id="8GQ-Ph-2iP"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Request" id="Ymm-D1-9xh">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsRequest:" target="-2" id="bT6-It-UE3"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Plugin Info" id="DgT-RO-YAr">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsPluginInfo:" target="-2" id="xIV-r7-IGH"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom In" id="l8B-JC-657">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsZoomIn:" target="-2" id="5Eq-yz-nca"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom Out" id="XSc-wR-sjC">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsZoomOut:" target="-2" id="xv5-EK-MeY"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom Reset" id="CvI-5Y-Daf">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsZoomReset:" target="-2" id="gHk-RN-RLz"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Set FPS" id="pJQ-OF-Zof">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsSetFPS:" target="-2" id="tmx-Ro-ryG"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Set Scale Factor" id="NSu-VF-AOB">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsSetScaleFactor:" target="-2" id="S47-BI-RtO"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Begin Tracing" id="na7-bM-yBE">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsTracingBegin:" target="-2" id="kx2-uj-cIe"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="End Tracing" id="q8j-Jh-DDj">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsTracingEnd:" target="-2" id="dtx-FX-x9L"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print" id="gXe-px-Ble">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsPrint:" target="-2" id="Ovd-bh-UYy"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print to PDF" id="khT-ti-jYy">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsPrintToPdf:" target="-2" id="f8B-MA-teX"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Other Tests" id="7VD-bm-EOX">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="menuTestsOtherTests:" target="-2" id="HbC-QY-Pwf"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+            </items>
+        </menu>
+        <userDefaultsController representsSharedInstance="YES" id="389"/>
+    </objects>
+</document>
diff --git a/src/tests/cefclient/resources/mac/Info.plist b/src/tests/cefclient/resources/mac/Info.plist
new file mode 100644
index 0000000..3730aeb
--- /dev/null
+++ b/src/tests/cefclient/resources/mac/Info.plist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string>cefclient.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.cef.cefclient</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>LSEnvironment</key>
+	<dict>
+		<key>MallocNanoZone</key>
+		<string>0</string>
+	</dict>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.9.0</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+	<key>NSSupportsAutomaticGraphicsSwitching</key>
+	<true/>
+</dict>
+</plist>
diff --git a/src/tests/cefclient/resources/mac/cefclient.icns b/src/tests/cefclient/resources/mac/cefclient.icns
new file mode 100644
index 0000000..f36742d
--- /dev/null
+++ b/src/tests/cefclient/resources/mac/cefclient.icns
Binary files differ
diff --git a/src/tests/cefclient/resources/mac/helper-Info.plist b/src/tests/cefclient/resources/mac/helper-Info.plist
new file mode 100644
index 0000000..43fb556
--- /dev/null
+++ b/src/tests/cefclient/resources/mac/helper-Info.plist
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleDisplayName</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.cef.cefclient.helper${BUNDLE_ID_SUFFIX}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>LSEnvironment</key>
+	<dict>
+		<key>MallocNanoZone</key>
+		<string>0</string>
+	</dict>
+	<key>LSFileQuarantineEnabled</key>
+	<true/>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.9.0</string>
+	<key>LSUIElement</key>
+	<string>1</string>
+	<key>NSSupportsAutomaticGraphicsSwitching</key>
+	<true/>
+</dict>
+</plist>
diff --git a/src/tests/cefclient/resources/media_router.html b/src/tests/cefclient/resources/media_router.html
new file mode 100644
index 0000000..54f8b74
--- /dev/null
+++ b/src/tests/cefclient/resources/media_router.html
@@ -0,0 +1,595 @@
+<html>
+<head>
+<style>
+body {
+  font-family: Verdana, Arial;
+  font-size: 12px;
+}
+
+/* Give the same font styling to form elements. */
+input, select, textarea, button {
+  font-family: inherit;
+  font-size: inherit;
+}
+
+.content {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: 100%;
+}
+
+.description {
+  padding-bottom: 5px;
+}
+
+.description .title {
+  font-size: 120%;
+  font-weight: bold;
+}
+
+.route_controls {
+  flex: 0;
+  align-self: center;
+  text-align: right;
+  padding-bottom: 5px;
+}
+
+.route_controls .label {
+  display: inline-block;
+  vertical-align: top;
+  font-weight: bold;
+}
+
+.route_controls .control {
+  width: 500px;
+}
+
+.messages {
+  flex: 1;
+  min-height: 100px;
+  border: 1px solid gray;
+  overflow: auto;
+}
+
+.messages .message {
+  padding: 3px;
+  border-bottom: 1px solid #cccbca;
+}
+
+.messages .message .timestamp {
+  font-size: 90%;
+  font-style: italic;
+}
+
+.messages .status {
+  background-color: #d6d6d6;  /* light gray */
+}
+
+.messages .sent {
+  background-color: #c5e8fc;  /* light blue */
+}
+
+.messages .recv {
+  background-color: #fcf4e3;  /* light yellow */
+}
+
+.message_controls {
+  flex: 0;
+  text-align: right;
+  padding-top: 5px;
+}
+
+.message_controls textarea {
+  width: 100%;
+  height: 10em;
+}
+</style>
+<script language="JavaScript">
+// Application state.
+var demoMode = false;
+var currentSubscriptionId = null;
+var currentRouteId = null;
+
+// List of currently supported source protocols.
+var allowedSourceProtocols = ['cast', 'dial'];
+
+// Values from cef_media_route_connection_state_t.
+var CEF_MRCS_UNKNOWN = 0;
+var CEF_MRCS_CONNECTING = 1;
+var CEF_MRCS_CONNECTED = 2;
+var CEF_MRCS_CLOSED = 3;
+var CEF_MRCS_TERMINATED = 4;
+
+function getStateLabel(state) {
+  switch (state) {
+    case CEF_MRCS_CONNECTING: return "CONNECTING";
+    case CEF_MRCS_CONNECTED: return "CONNECTED";
+    case CEF_MRCS_CLOSED: return "CLOSED";
+    case CEF_MRCS_TERMINATED: return "TERMINATED";
+    default: break;
+  }
+  return "UNKNOWN";
+}
+
+// Values from cef_media_sink_icon_type_t.
+var CEF_MSIT_CAST = 0;
+var CEF_MSIT_CAST_AUDIO_GROUP = 1;
+var CEF_MSIT_CAST_AUDIO = 2;
+var CEF_MSIT_MEETING = 3;
+var CEF_MSIT_HANGOUT = 4;
+var CEF_MSIT_EDUCATION = 5;
+var CEF_MSIT_WIRED_DISPLAY = 6;
+var CEF_MSIT_GENERIC = 7;
+
+function getIconTypeLabel(type) {
+  switch (type) {
+    case CEF_MSIT_CAST: return "CAST";
+    case CEF_MSIT_CAST_AUDIO_GROUP: return "CAST_AUDIO_GROUP";
+    case CEF_MSIT_CAST_AUDIO: return "CAST_AUDIO";
+    case CEF_MSIT_MEETING: return "MEETING";
+    case CEF_MSIT_HANGOUT: return "HANGOUT";
+    case CEF_MSIT_EDUCATION: return "EDUCATION";
+    case CEF_MSIT_WIRED_DISPLAY: return "WIRED_DISPLAY";
+    case CEF_MSIT_GENERIC: return "GENERIC";
+    default: break;
+  }
+  return "UNKNOWN";
+}
+
+
+///
+// Manage show/hide of default text for form elements.
+///
+
+// Default messages that are shown until the user focuses on the input field.
+var defaultSourceText = 'Enter URN here and click "Create Route"';
+var defaultMessageText = 'Enter message contents here and click "Send Message"';
+
+function getDefaultText(control) {
+  if (control === 'source')
+    return defaultSourceText;
+  if (control === 'message')
+    return defaultMessageText;
+  return null;
+}
+
+function hideDefaultText(control) {
+  var element = document.getElementById(control);
+  var defaultText = getDefaultText(control);
+  if (element.value === defaultText)
+    element.value = '';
+}
+
+function showDefaultText(control) {
+  var element = document.getElementById(control);
+  var defaultText = getDefaultText(control);
+  if (element.value === '')
+    element.value = defaultText;
+}
+
+function initDefaultText() {
+  showDefaultText('source');
+  showDefaultText('message');
+}
+
+
+///
+// Retrieve current form values. Return null if validation fails.
+///
+
+function getCurrentSource() {
+  var sourceInput = document.getElementById('source');
+  var value = sourceInput.value;
+  if (value === defaultSourceText || value.length === 0 || value.indexOf(':') < 0) {
+    return null;
+  }
+
+  // Validate the URN value.
+  try {
+    var url = new URL(value);
+    if ((url.hostname.length === 0 && url.pathname.length === 0) ||
+        !allowedSourceProtocols.includes(url.protocol.slice(0, -1))) {
+      return null;
+    }
+  } catch (e) {
+    return null;
+  }
+
+  return value;
+}
+
+function getCurrentSink() {
+  var sinksSelect = document.getElementById('sink');
+  if (sinksSelect.options.length === 0)
+    return null;
+  return sinksSelect.value;
+}
+
+function getCurrentMessage() {
+  var messageInput = document.getElementById('message');
+  if (messageInput.value === defaultMessageText || messageInput.value.length === 0)
+    return null;
+  return messageInput.value;
+}
+
+
+///
+// Set disabled state of form elements.
+///
+
+function updateControls() {
+  document.getElementById('source').disabled = hasRoute();
+  document.getElementById('sink').disabled = hasRoute();
+  document.getElementById('create_route').disabled =
+      hasRoute() || getCurrentSource() === null || getCurrentSink() === null;
+  document.getElementById('terminate_route').disabled = !hasRoute();
+  document.getElementById('message').disabled = !hasRoute();
+  document.getElementById('send_message').disabled = !hasRoute() || getCurrentMessage() === null;
+}
+
+
+///
+// Manage the media sinks list.
+///
+
+/*
+Expected format for |sinks| is:
+  [
+    {
+      name: string,
+      type: string ('cast' or 'dial'),
+      id: string,
+      desc: string,
+      icon: int
+    }, ...
+  ]
+*/
+function updateSinks(sinks) {
+  var sinksSelect = document.getElementById('sink');
+
+  // Currently selected value.
+  var selectedValue = sinksSelect.options.length === 0 ? null : sinksSelect.value;
+
+  // Build a list of old (existing) values.
+  var oldValues = [];
+  for (var i = 0; i < sinksSelect.options.length; ++i) {
+    oldValues.push(sinksSelect.options[i].value);
+  }
+
+  // Build a list of new (possibly new or existing) values.
+  var newValues = [];
+  for(var i = 0; i < sinks.length; i++) {
+    newValues.push(sinks[i].id);
+  }
+
+  // Remove old values that no longer exist.
+  for (var i = sinksSelect.options.length - 1; i >= 0; --i) {
+    if (!newValues.includes(sinksSelect.options[i].value)) {
+      sinksSelect.remove(i);
+    }
+  }
+
+  // Add new values that don't already exist.
+  for(var i = 0; i < sinks.length; i++) {
+    var sink = sinks[i];
+    if (oldValues.includes(sink.id))
+      continue;
+    var opt = document.createElement('option');
+    opt.innerHTML = sink.name + ' (' + sink.model_name + ', ' + sink.type + ', ' +
+                    getIconTypeLabel(sink.icon) + ', ' + sink.ip_address + ':' + sink.port + ')';
+    opt.value = sink.id;
+    sinksSelect.appendChild(opt);
+  }
+
+  if (sinksSelect.options.length === 0) {
+    selectedValue = null;
+  } else if (!newValues.includes(selectedValue)) {
+    // The previously selected value no longer exists.
+    // Select the first value in the new list.
+    selectedValue = sinksSelect.options[0].value;
+    sinksSelect.value = selectedValue;
+  }
+
+  updateControls();
+
+  return selectedValue;
+}
+
+
+///
+// Manage the current media route.
+///
+
+function hasRoute() {
+  return currentRouteId !== null;
+}
+
+function createRoute() {
+  console.assert(!hasRoute());
+  var source = getCurrentSource();
+  console.assert(source !== null);
+  var sink = getCurrentSink();
+  console.assert(sink !== null);
+
+  if (demoMode) {
+    onRouteCreated('demo-route-id');
+    return;
+  }
+
+  sendCefQuery(
+    {name: 'createRoute', source_urn: source, sink_id: sink},
+    (message) => onRouteCreated(JSON.parse(message).route_id)
+  );
+}
+
+function onRouteCreated(route_id) {
+  currentRouteId = route_id;
+  showStatusMessage('Route ' + route_id + '\ncreated');
+  updateControls();
+}
+
+function terminateRoute() {
+  console.assert(hasRoute());
+  var source = getCurrentSource();
+  console.assert(source !== null);
+  var sink = getCurrentSink();
+  console.assert(sink !== null);
+
+  if (demoMode) {
+    onRouteTerminated();
+    return;
+  }
+
+  sendCefQuery(
+    {name: 'terminateRoute', route_id: currentRouteId},
+    (unused) => {}
+  );
+}
+
+function onRouteTerminated() {
+  showStatusMessage('Route ' + currentRouteId + '\nterminated');
+  currentRouteId = null;
+  updateControls();
+}
+
+
+///
+// Manage messages.
+///
+
+function sendMessage() {
+  console.assert(hasRoute());
+  var message = getCurrentMessage();
+  console.assert(message !== null);
+
+  if (demoMode) {
+    showSentMessage(message);
+    setTimeout(function(){ if (hasRoute()) { recvMessage('Demo ACK for: ' + message); } }, 1000);
+    return;
+  }
+
+  sendCefQuery(
+    {name: 'sendMessage', route_id: currentRouteId, message: message},
+    (unused) => showSentMessage(message)
+  );
+}
+
+function recvMessage(message) {
+  console.assert(hasRoute());
+  console.assert(message !== undefined && message !== null && message.length > 0);
+  showRecvMessage(message);
+}
+
+function showStatusMessage(message) {
+  showMessage('status', message);
+}
+
+function showSentMessage(message) {
+  showMessage('sent', message);
+}
+
+function showRecvMessage(message) {
+  showMessage('recv', message);
+}
+
+function showMessage(type, message) {
+  if (!['status', 'sent', 'recv'].includes(type)) {
+    console.warn('Invalid message type: ' + type);
+    return;
+  }
+
+  if (message[0] === '{') {
+    try {
+      // Pretty print JSON strings.
+      message = JSON.stringify(JSON.parse(message), null, 2);
+    } catch(e) {}
+  }
+
+  var messagesDiv = document.getElementById('messages');
+
+  var newDiv = document.createElement("div");
+  newDiv.innerHTML =
+      '<span class="timestamp">' + (new Date().toLocaleString()) +
+      ' (' + type.toUpperCase() + ')</span><br/>';
+  // Escape any HTML tags or entities in |message|.
+  var pre = document.createElement('pre');
+  pre.appendChild(document.createTextNode(message));
+  newDiv.appendChild(pre);
+  newDiv.className = 'message ' + type;
+
+  messagesDiv.appendChild(newDiv);
+
+  // Always scroll to bottom.
+  messagesDiv.scrollTop = messagesDiv.scrollHeight;
+}
+
+
+///
+// Manage communication with native code in media_router_test.cc.
+///
+
+function onCefError(code, message) {
+  showStatusMessage('ERROR: ' + message + ' (' + code + ')');
+}
+
+function sendCefQuery(payload, onSuccess, onFailure=onCefError, persistent=false) {
+  // Results in a call to the OnQuery method in media_router_test.cc
+  return window.cefQuery({
+    request: JSON.stringify(payload),
+    onSuccess: onSuccess,
+    onFailure: onFailure,
+    persistent: persistent
+  });
+}
+
+/*
+Expected format for |message| is:
+  {
+    name: string,
+    payload: dictionary
+  }
+*/
+function onCefSubscriptionMessage(message) {
+  if (message.name === 'onSinks') {
+    // List of sinks.
+    updateSinks(message.payload.sinks_list);
+  } else if (message.name === 'onRouteStateChanged') {
+    // Route status changed.
+    if (message.payload.route_id === currentRouteId) {
+      var connection_state = message.payload.connection_state;
+      showStatusMessage('Route ' + currentRouteId +
+                        '\nconnection state ' + getStateLabel(connection_state) +
+                        ' (' + connection_state + ')');
+      if ([CEF_MRCS_CLOSED, CEF_MRCS_TERMINATED].includes(connection_state)) {
+        onRouteTerminated();
+      }
+    }
+  } else if (message.name === 'onRouteMessageReceived') {
+    // Route message received.
+    if (message.payload.route_id === currentRouteId) {
+      recvMessage(message.payload.message);
+    }
+  }
+}
+
+// Subscribe to ongoing message notifications from the native code.
+function startCefSubscription() {
+  currentSubscriptionId = sendCefQuery(
+    {name: 'subscribe'},
+    (message) => onCefSubscriptionMessage(JSON.parse(message)),
+    (code, message) => {
+      onCefError(code, message);
+      currentSubscriptionId = null;
+    },
+    true
+  );
+}
+
+function stopCefSubscription() {
+  if (currentSubscriptionId !== null) {
+    // Results in a call to the OnQueryCanceled method in media_router_test.cc
+    window.cefQueryCancel(currentSubscriptionId);
+  }
+}
+
+
+///
+// Example app load/unload.
+///
+
+function initDemoMode() {
+  demoMode = true;
+
+  var sinks = [
+    {
+      name: 'Sink 1',
+      type: 'cast',
+      id: 'sink1',
+      desc: 'My cast device',
+      icon: CEF_MSIT_CAST
+    },
+    {
+      name: 'Sink 2',
+      type: 'dial',
+      id: 'sink2',
+      desc: 'My dial device',
+      icon: CEF_MSIT_GENERIC
+    }
+  ];
+  updateSinks(sinks);
+
+  showStatusMessage('Running in Demo mode.');
+  showSentMessage('Demo sent message.');
+  showRecvMessage('Demo recv message.');
+}
+
+function onLoad() {
+  initDefaultText();
+
+  if (window.cefQuery === undefined) {
+    // Initialize demo mode when running outside of CEF.
+    // This supports development and testing of the HTML/JS behavior outside
+    // of a cefclient build.
+    initDemoMode();
+    return;
+  }
+
+  startCefSubscription()
+}
+
+function onUnload() {
+  if (demoMode)
+    return;
+
+  if (hasRoute())
+    terminateRoute();
+  stopCefSubscription();
+}
+</script>
+<title>Media Router Example</title>
+</head>
+<body bgcolor="white" onLoad="onLoad()" onUnload="onUnload()">
+<div class="content">
+  <div class="description">
+    <span class="title">Media Router Example</span>
+    <p>
+      <b>Overview:</b>
+      Chromium supports communication with devices on the local network via the
+      <a href="https://blog.oakbits.com/google-cast-protocol-overview.html" target="_blank">Cast</a> and
+      <a href="http://www.dial-multiscreen.org/" target="_blank">DIAL</a> protocols.
+      CEF exposes this functionality via the CefMediaRouter interface which is demonstrated by this test.
+      Test code is implemented in resources/media_router.html and browser/media_router_test.cc.
+    </p>
+    <p>
+      <b>Usage:</b>
+      Devices available on your local network will be discovered automatically and populated in the "Sink" list.
+      Enter a URN for "Source", select an available device from the "Sink" list, and click the "Create Route" button.
+      Cast URNs take the form "cast:<i>&lt;appId&gt;</i>?clientId=<i>&lt;clientId&gt;</i>" and DIAL URNs take the form "dial:<i>&lt;appId&gt;</i>",
+      where <i>&lt;appId&gt;</i> is the <a href="https://developers.google.com/cast/docs/registration" target="_blank">registered application ID</a>
+      and <i>&lt;clientId&gt;</i> is an arbitrary numeric identifier.
+      Status information and messages will be displayed in the center of the screen.
+      After creating a route you can send messages to the receiver app using the textarea at the bottom of the screen.
+      Messages are usually in JSON format with a example of Cast communication to be found
+      <a href="https://bitbucket.org/chromiumembedded/cef/issues/2900/add-mediarouter-support-for-cast-receiver#comment-56680326" target="_blank">here</a>.
+    </p>
+  </div>
+  <div class="route_controls">
+    <span class="label">Source:</span>
+    <input type="text" id="source" class="control" onInput="updateControls()" onFocus="hideDefaultText('source')" onBlur="showDefaultText('source')"/>
+    <br/>
+    <span class="label">Sink:</span>
+    <select id="sink" size="3" class="control"></select>
+    <br/>
+    <input type="button" id="create_route" onclick="createRoute()" value="Create Route" disabled/>
+    <input type="button" id="terminate_route" onclick="terminateRoute()" value="Terminate Route" disabled/>
+  </div>
+  <div id="messages" class="messages">
+  </div>
+  <div class="message_controls">
+    <textarea id="message" onInput="updateControls()" onFocus="hideDefaultText('message')" onBlur="showDefaultText('message')" disabled></textarea>
+    <br/><input type="button" id="send_message" onclick="sendMessage()" value="Send Message" disabled/>
+  </div>
+</div>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/menu_icon.1x.png b/src/tests/cefclient/resources/menu_icon.1x.png
new file mode 100644
index 0000000..976f524
--- /dev/null
+++ b/src/tests/cefclient/resources/menu_icon.1x.png
Binary files differ
diff --git a/src/tests/cefclient/resources/menu_icon.2x.png b/src/tests/cefclient/resources/menu_icon.2x.png
new file mode 100644
index 0000000..1ab841c
--- /dev/null
+++ b/src/tests/cefclient/resources/menu_icon.2x.png
Binary files differ
diff --git a/src/tests/cefclient/resources/other_tests.html b/src/tests/cefclient/resources/other_tests.html
new file mode 100644
index 0000000..a92b849
--- /dev/null
+++ b/src/tests/cefclient/resources/other_tests.html
@@ -0,0 +1,45 @@
+<html>
+<head>
+<title>Other Tests</title>
+</head>
+<body bgcolor="white">
+<h3>Various other internal and external tests.</h3>
+<ul>
+<li><a href="http://mudcu.be/labs/JS1k/BreathingGalaxies.html">Accelerated 2D Canvas</a></li>
+<li><a href="http://webkit.org/blog-files/3d-transforms/poster-circle.html">Accelerated Layers</a></li>
+<li><a href="https://jigsaw.w3.org/HTTP/Basic/">Authentication (Basic)</a> - credentials returned via GetAuthCredentials</li>
+<li><a href="https://jigsaw.w3.org/HTTP/Digest/">Authentication (Digest)</a> - credentials returned via GetAuthCredentials</li>
+<li><a href="http://html5advent2011.digitpaint.nl/3/index.html">Cursors</a></li>
+<li><a href="dialogs">Dialogs</a></li>
+<li><a href="http://html5demos.com/drag">Drag & Drop</a></li>
+<li><a href="draggable">Draggable Regions</a></li>
+<li><a href="drm">DRM (Clearkey, Widevine)</a> - Widevine requires setup as described in cef_web_plugin.h</li>
+<li><a href="http://www.adobe.com/software/flash/about/">Flash Plugin</a> - requires "enable-system-flash" flag on Win/Mac and "ppapi-flash-path", "ppapi-flash-version" flags on Linux</li>
+<li><a href="http://www.html5test.com">HTML5 Feature Test</a></li>
+<li><a href="http://html5-demos.appspot.com/static/filesystem/filer.js/demos/index.html">HTML5 Filesystem</a> - requires "cache-path" flag</li>
+<li><a href="http://www.youtube.com/watch?v=siOHh0uzcuY&html5=True">HTML5 Video</a></li>
+<li><a href="binding">JavaScript Binding</a></li>
+<li><a href="performance">JavaScript Performance Tests</a></li>
+<li><a href="performance2">JavaScript Performance (2) Tests</a></li>
+<li><a href="window">JavaScript Window Manipulation</a></li>
+<li><a href="localstorage">Local Storage</a></li>
+<li><a href="media_router">Media Router (Cast/DIAL)</a></li>
+<li><a href="pdf.pdf">PDF Viewer direct</a></li>
+<li><a href="pdf">PDF Viewer iframe</a></li>
+<li><a href="preferences">Preferences</a></li>
+<li><a href="http://mrdoob.com/lab/javascript/requestanimationframe/">requestAnimationFrame</a></li>
+<li><a href="response_filter">Response Filtering</a></li>
+<li><a href="client://tests/handler.html">Scheme Handler</a></li>
+<li><a href="server">HTTP/WebSocket Server</a></li>
+<li><a href="websocket">WebSocket Client</a></li>
+<li><a href="https://www.google.com/intl/en/chrome/demos/speech.html">Speech Input</a> - requires "enable-speech-input" flag</li>
+<li><a href="transparency">Transparency</a></li>
+<li><a href="http://webglsamples.org/field/field.html">WebGL</a></li>
+<li><a href="http://apprtc.appspot.com/">WebRTC</a> - requires "enable-media-stream" flag</li>
+<li><a href="urlrequest">CefURLRequest</a></li>
+<li><a href="xmlhttprequest">XMLHttpRequest</a></li>
+<li><a href="javascript:window.print();">Print this page with &quot;javascript:window.print();&quot;</a></li>
+<li><a href="https://patrickhlauke.github.io/touch">Touch Feature Tests</a> - requires "touch-events=enabled" flag (and CAPS LOCK on Mac for Trackpad simulation)</li>
+</ul>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/performance.html b/src/tests/cefclient/resources/performance.html
new file mode 100644
index 0000000..aa64d51
--- /dev/null
+++ b/src/tests/cefclient/resources/performance.html
@@ -0,0 +1,293 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Performance Tests</title>
+    <style>
+      body { font-family: Tahoma, Serif; font-size: 9pt; }
+    </style>
+  </head>
+  <body bgcolor="white">
+    <h1>Performance Tests</h1>
+    <input type="button" value="Run Tests" onClick="run();" id="run"/> Filter: <input type="text" size="50" id="filters"/>
+    <div><span id="statusBox"></span> <progress id="progressBox" value="0" style="display:none"></progress></div>
+
+    <div style="padding-top:10px; padding-bottom:10px">
+    <table id="resultTable" border="1" cellspacing="1" cellpadding="4">
+      <thead>
+        <tr>
+          <td>Name</td>
+          <td>Iterations per Run</td>
+          <td>Avg (ms)</td>
+          <td>Min (ms)</td>
+          <td>Max (ms)</td>
+          <td>StdDev (ms)</td>
+          <td>Runs (ms)</td>
+        </tr>
+      </thead>
+      <!-- result rows here -->
+    </table>
+    </div>
+
+    <hr width="80%">
+
+    Result 1: <input type="text" size="100" id="result1"/>
+    <br/>Result 2: <input type="text" size="100" id="result2"/>
+    <br/><input type="button" value="Compare" onClick="compare();" id="compare"/>
+
+    <div style="padding-top:10px; padding-bottom:10px">
+    <table id="compareTable" border="1" cellspacing="1" cellpadding="4">
+      <thead>
+        <tr>
+          <td>Name</td>
+          <td>Result 1 Avg (ms)</td>
+          <td>Result 2 Avg (ms)</td>
+          <td>% Diff</td>
+        </tr>
+      </thead>
+      <!-- result rows here -->
+    </table>
+    </div>
+
+<script type="text/javascript">
+function run() {
+  var runElement = document.getElementById("run");
+  var filtersElement = document.getElementById("filters");
+  var compareElement = document.getElementById("compare");
+  var result1Element = document.getElementById("result1");
+  var result2Element = document.getElementById("result2");
+
+  // Number of runs for each test.
+  var testRuns = 10;
+
+  // Delay between test runs.
+  var runDelay = 0;
+
+  // Retrieve the list of all tests.
+  var allTests = window.GetPerfTests();
+
+  // Populated with the list of tests that will be run.
+  var tests = [];
+  var currentTest = 0;
+
+  var testList = filtersElement.value.trim();
+  if (testList.length > 0) {
+    // Include or exclude specific tests.
+    var included = [];
+    var excluded = [];
+
+    var testNames = testList.split(",");
+
+    // Identify included and excluded tests.
+    for (i = 0; i < testNames.length; ++i) {
+      var testName = testNames[i].trim();
+      if (testName[0] == '-') {
+        // Exclude the test.
+        excluded.push(testName.substr(1));
+      } else {
+        // Include the test.
+        included.push(testName);
+      }
+    }
+
+    if (included.length > 0) {
+      // Only use the included tests.
+      for (i = 0; i < allTests.length; ++i) {
+        var test = allTests[i];
+        var testName = test[0];
+        if (included.indexOf(testName) >= 0)
+          tests.push(test);
+      }
+    } else if (excluded.length > 0) {
+      // Use all tests except the excluded tests.
+      for (i = 0; i < allTests.length; ++i) {
+        var test = allTests[i];
+        var testName = test[0];
+        if (excluded.indexOf(testName) < 0)
+          tests.push(test);
+      }
+    }
+  } else {
+    // Run all tests.
+    tests = allTests;
+  }
+
+  function updateStatusComplete() {
+    var statusBox = document.getElementById("statusBox");
+    statusBox.innerText = 'All tests completed.';
+
+    runElement.disabled = false;
+    filtersElement.disabled = false;
+    result1Element.disabled = false;
+    result2Element.disabled = false;
+    compareElement.disabled = false;
+  }
+
+  function updateStatus(test) {
+    var statusBox = document.getElementById("statusBox");
+    var progressBox = document.getElementById("progressBox");
+
+    if (test.run >= test.totalRuns) {
+      statusBox.innerText = test.name + " completed.";
+      progressBox.style.display = 'none';
+    } else {
+      statusBox.innerText = test.name + " (" + test.run + "/" + test.totalRuns + ")";
+      progressBox.value = (test.run / test.totalRuns);
+      progressBox.style.display = 'inline';
+    }
+  }
+
+  function appendResult(test) {
+    var e = document.getElementById("resultTable");
+
+    // Calculate the average.
+    var avg = test.total / test.totalRuns;
+
+    // Calculate the standard deviation.
+    var sqsum = 0;
+    for (i = 0; i < test.results.length; ++i) {
+      var diff = test.results[i] - avg;
+      sqsum += diff * diff;
+    }
+    var stddev = Math.round(Math.sqrt(sqsum / test.totalRuns) * 100.0) / 100.0;
+
+    e.insertAdjacentHTML("beforeEnd", [
+        "<tr>",
+        "<td>", test.name, "</td>",
+        "<td>", test.iterations, "</td>",
+        "<td>", avg, "</td>",
+        "<td>", test.min, "</td>",
+        "<td>", test.max, "</td>",
+        "<td>", stddev, "</td>",
+        "<td>", test.results.join(", "), "</td>",
+        "<tr>"
+        ].join(""));
+
+    if (result1Element.value.length > 0)
+      result1Element.value += ",";
+    result1Element.value += test.name + "=" + avg;
+  }
+
+  // Execute the test function.
+  function execTestFunc(name) {
+    return window.RunPerfTest(name);
+  }
+
+  // Schedule the next test.
+  function nextTest(test) {
+    appendResult(test);
+    currentTest++;
+    runTest();
+  }
+
+  // Schedule the next step for the current test.
+  function nextTestStep(test) {
+    setTimeout(function () { execTest(test); }, runDelay);
+  }
+
+  // Perform the next step for the current test.
+  function execTest(test) {
+    updateStatus(test);
+
+    if (!test.warmedUp) {
+      execTestFunc(test.name);
+      test.warmedUp = true;
+      return nextTestStep(test);
+    }
+
+    if (test.run >= test.totalRuns)
+      return nextTest(test);
+
+    var elapsed = execTestFunc(test.name);
+    test.results.push(elapsed);
+
+    test.total += elapsed;
+    if (!test.min) test.min = elapsed;
+    else if (test.min > elapsed) test.min = elapsed;
+    if (!test.max) test.max = elapsed;
+    else if (test.max < elapsed) test.max = elapsed;
+
+    test.run++;
+
+    return nextTestStep(test);
+  }
+
+  function runTest() {
+    if (currentTest == tests.length) {
+      updateStatusComplete();
+      return;
+    }
+
+    var test = {
+        name: tests[currentTest][0],
+        iterations: tests[currentTest][1],
+        warmedUp: false,
+        total: 0,
+        totalRuns: testRuns,
+        run: 0,
+        results: []
+    };
+    setTimeout(function () { execTest(test); }, runDelay);
+  }
+
+  // Schedule the first test.
+  if (tests.length > 0) {
+    runElement.disabled = true;
+    filtersElement.disabled = true;
+    result1Element.value = "";
+    result1Element.disabled = true;
+    result2Element.disabled = true;
+    compareElement.disabled = true;
+
+    runTest();
+  }
+}
+
+function compare() {
+  var result1 = document.getElementById("result1").value.trim();
+  var result2 = document.getElementById("result2").value.trim();
+
+  if (result1.length == 0 || result2.length == 0)
+    return;
+
+  var r1values = result1.split(",");
+  var r2values = result2.split(",");
+  for (i = 0; i < r1values.length; ++i) {
+    var r1parts = r1values[i].split("=");
+    var r1name = r1parts[0].trim();
+    var r1val = r1parts[1].trim();
+
+    for (x = 0; x < r2values.length; ++x) {
+      var r2parts = r2values[x].split("=");
+      var r2name = r2parts[0].trim();
+      var r2val = r2parts[1].trim();
+
+      if (r2name == r1name) {
+        appendResult(r1name, r1val, r2val);
+
+        // Remove the matching index.
+        r2values.splice(x, 1);
+        break;
+      }
+    }
+  }
+  
+  function appendResult(name, r1val, r2val) {
+    var e = document.getElementById("compareTable");
+ 
+    // Calculate the percent difference.
+    var diff = Math.round(((r2val - r1val) / r1val) * 10000.0) / 100.0;
+
+    e.insertAdjacentHTML("beforeEnd", [
+        "<tr>",
+        "<td>", name, "</td>",
+        "<td>", r1val, "</td>",
+        "<td>", r2val, "</td>",
+        "<td>", diff, "</td>",
+        "<tr>"
+        ].join(""));
+  }
+}
+</script>
+
+  </body>
+</html>
diff --git a/src/tests/cefclient/resources/performance2.html b/src/tests/cefclient/resources/performance2.html
new file mode 100644
index 0000000..6664de7
--- /dev/null
+++ b/src/tests/cefclient/resources/performance2.html
@@ -0,0 +1,442 @@
+<!DOCTYPE HTML>
+<html>
+    <head>
+        <title>Performance Tests (2)</title>
+        <style>
+            body { font-family: Tahoma, Serif; font-size: 9pt; }
+
+            .left { text-align: left; }
+            .right { text-align: right; }
+            .center { text-align: center; }
+
+            table.resultTable 
+            {
+                border: 1px solid black;
+                border-collapse: collapse;
+                empty-cells: show;
+                width: 100%;
+            }
+            table.resultTable td
+            {
+                padding: 2px 4px;
+                border: 1px solid black;
+            }
+            table.resultTable > thead > tr
+            {
+                font-weight: bold;
+                background: lightblue;
+            }
+            table.resultTable > tbody > tr:nth-child(odd)
+            {
+                background: white;
+            }
+            table.resultTable > tbody > tr:nth-child(even)
+            {
+                background: lightgray;
+            }
+
+            .hide { display: none; }
+        </style>
+    </head>
+    <body bgcolor="white">
+        <h1>Performance Tests (2)</h1>
+
+        <form id="sForm" onsubmit="runTestSuite();return false">
+            <table>
+                <tr>
+                    <td colspan="2">Settings:</td>
+                </tr>
+                <tr>
+                    <td class="right">Iterations:</td>
+                    <td><input id="sIterations" type="text" value="1000" required pattern="[0-9]+" /></td>
+                </tr>
+                <tr>
+                    <td class="right">Samples:</td>
+                    <td><input id="sSamples" type="text" value="100" required pattern="[0-9]+" /></td>
+                </tr>
+                <tr>
+                    <td class="right">Mode:</td>
+                    <td><input id="sAsync" name="sMode" type="radio" value="async" checked>Asynchronous</input>
+                        <input id="sSync" name="sMode" type="radio" value="sync">Synchronous</input>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2"><button type="submit" id="sRun" autofocus>Run!</button></td>
+                </tr>
+            </table>
+        </form>
+
+
+        <div><span id="statusBox"></span> <progress id="progressBox" value="0" style="display:none"></progress></div>
+
+        <div style="padding-top:10px; padding-bottom:10px">
+        <table id="resultTable" class="resultTable">
+            <thead>
+                <tr>
+                    <td class="center" style="width:1%">Enabled</td>
+                    <td class="center" style="width:10%">Name</td>
+                    <td class="center" style="width:5%">Samples x Iterations</td>
+                    <td class="center" style="width:5%">Min,&nbsp;ms</td>
+                    <td class="center" style="width:5%">Avg,&nbsp;ms</td>
+                    <td class="center" style="width:5%">Max,&nbsp;ms</td>
+                    <td class="center" style="width:5%">Average calls/sec</td>
+                    <td class="center" style="width:5%">Measuring Inacurracy</td>
+                    <td class="center hide" style="width:5%">Memory, MB</td>
+                    <td class="center hide" style="width:5%">Memory delta, MB</td>
+                    <td class="center" style="width:55%">Description</td>
+                </tr>
+            </thead>
+            <tbody>
+                <!-- result rows here -->
+            </tbody>
+        </table>
+        </div>
+
+<script type="text/javascript">
+(function () {
+    function getPrivateWorkingSet() {
+        return 0; // TODO: window.PerfTestGetPrivateWorkingSet();
+    }
+
+    var disableWarmUp = true;
+
+    var asyncExecution = true;
+    var testIterations = 1000;
+    var totalSamples = 100;
+    var sampleDelay = 0;
+
+    var collectSamples = false;
+
+    var tests = [];
+    var testIndex = -1;
+
+    function execTestFunc(test) {
+        try {
+            var begin = new Date();
+            test.func(test.totalIterations);
+            var end = new Date();
+            return (end - begin);
+        } catch (e) {
+            test.error = e.toString();
+            return 0;
+        }
+    }
+
+    function execTest(test) {
+        if (disableWarmUp) { test.warmedUp = true; }
+
+        function nextStep() {
+            if (asyncExecution) {
+                setTimeout(function () { execTest(test); }, sampleDelay);
+            } else {
+                execTest(test);
+            }
+        }
+
+        function nextTest() {
+            updateStatus(test);
+            appendResult(test);
+
+            return execNextTest();
+        }
+
+        updateStatus(test);
+        if (!test.warmedUp) {
+            execTestFunc(test);
+            if (!test.error) {
+                test.warmedUp = true;
+                test.beginMemory = getPrivateWorkingSet();
+                return nextStep();
+            } else {
+                return nextTest();
+            }
+        }
+
+        if (test.sample >= test.totalSamples) {
+            test.avg = test.total / test.totalSamples;
+            test.endMemory = getPrivateWorkingSet();
+            return nextTest();
+        }
+
+        if (test.skipped) return nextTest();
+
+        var elapsed = execTestFunc(test);
+        if (!test.error) {
+            test.total += elapsed;
+            if (!test.min) test.min = elapsed;
+            else if (test.min > elapsed) test.min = elapsed;
+            if (!test.max) test.max = elapsed;
+            else if (test.max < elapsed) test.max = elapsed;
+            if (collectSamples) {
+                test.results.push(elapsed);
+            }
+            test.sample++;
+            return nextStep();
+        } else {
+            return nextTest();
+        }
+    }
+
+    function updateStatus(test) {
+        var statusBox = document.getElementById("statusBox");
+        var progressBox = document.getElementById("progressBox");
+
+        if (test.skipped || test.error || test.sample >= test.totalSamples) {
+            statusBox.innerText = "";
+            progressBox.style.display = "none";
+        } else {
+            statusBox.innerText = (testIndex + 1) + "/" + tests.length + ": " + test.name + " (" + test.sample + "/" + test.totalSamples + ")";
+            progressBox.value = (test.sample / test.totalSamples);
+            progressBox.style.display = "inline";
+        }
+    }
+
+    function appendResult(test) {
+        if (test.name == "warmup") return;
+
+        var id = "testResultRow_" + test.index;
+
+        var nearBound = (test.max - test.avg) < (test.avg - test.min) ? test.max : test.min;
+        var memoryDelta = test.endMemory - test.beginMemory;
+        if (memoryDelta < 0) memoryDelta = "-" + Math.abs(memoryDelta).toFixed(2);
+        else memoryDelta = "+" + Math.abs(memoryDelta).toFixed(2);
+
+        var markup = ["<tr id='" + id + "'>",
+                      "<td class='left'><input type='checkbox' id='test_enabled_", test.index ,"' ", (!test.skipped ? "checked" : "") ," /></td>",
+                      "<td class='left'>", test.name, "</td>",
+                      "<td class='right'>", test.totalSamples, "x", test.totalIterations, "</td>",
+                      "<td class='right'>", test.skipped || test.error || !test.prepared ? "-" : test.min.toFixed(2), "</td>",
+                      "<td class='right'>", test.skipped || test.error || !test.prepared ? "-" : test.avg.toFixed(2), "</td>",
+                      "<td class='right'>", test.skipped || test.error || !test.prepared ? "-" : test.max.toFixed(2), "</td>",
+                      "<td class='right'>", test.skipped || test.error || !test.prepared ? "-" : (test.totalIterations * 1000 / test.avg).toFixed(2), "</td>",
+                      "<td class='right'>", test.skipped || test.error || !test.prepared ? "-" : ("&#x00B1; " + (Math.abs(test.avg - nearBound) / (test.avg) * (100)).toFixed(2) + "%"), "</td>",
+                      "<td class='right hide'>", test.skipped || test.error || !test.prepared ? "-" : test.endMemory.toFixed(2), "</td>",
+                      "<td class='right hide'>", test.skipped || test.error || !test.prepared ? "-" : memoryDelta, "</td>",
+                      "<td class='left'>", test.description, test.error ? (test.description ? "<br/>" : "") + "<span style='color:red'>" + test.error + "</span>" : "", "</td>",
+                      "</tr>"
+                      ].join("");
+        // test.results.join(", "), "<br/>",
+
+        var row = document.getElementById(id);
+        if (row) {
+            row.outerHTML = markup;
+        } else {
+            var tbody = document.getElementById("resultTable").tBodies[0];
+            tbody.insertAdjacentHTML("beforeEnd", markup);
+        }
+    }
+
+    function prepareQueuedTests() {
+        testIndex = -1;
+        for (var i = 0; i < tests.length; i++) {
+            var test = tests[i];
+            test.index = i;
+            test.prepared = false;
+            test.warmedUp = false;
+            test.sample = 0;
+            test.total = 0;
+            test.results = [];
+            test.error = false;
+            test.min = null;
+            test.avg = null;
+            test.max = null;
+            test.beginMemory = null;
+            test.endMemory = null;
+            test.totalIterations = parseInt(testIterations / test.complex);
+            test.totalSamples = parseInt(totalSamples / test.complex);
+
+            var skipElement = document.getElementById('test_enabled_' + test.index);
+            test.skipped = skipElement ? !skipElement.checked : (test.skipped || false);
+
+            if (test.totalIterations <= 0) test.totalIterations = 1;
+            if (test.totalSamples <= 0) test.totalSamples = 1;
+
+            appendResult(test);
+            test.prepared = true;
+        }
+    }
+
+    function queueTest(func, name, description) {
+        var test;
+        if (typeof func === "function") {
+            test = {
+                name: name,
+                func: func,
+                description: description
+            };
+        } else {
+            test = func;
+        }
+        test.warmedUp = false;
+        test.complex = test.complex || 1;
+        tests.push(test);
+    }
+
+    function execNextTest() {
+        testIndex++;
+        if (tests.length <= testIndex) {
+            return testSuiteFinished();
+        } else {
+            return execTest(tests[testIndex]);
+        }
+    }
+
+    function execQueuedTests() {
+        prepareQueuedTests();
+        execNextTest();
+    }
+
+    function setSettingsState(disabled) {
+        document.getElementById('sIterations').disabled = disabled;
+        document.getElementById('sSamples').disabled = disabled;
+        document.getElementById('sAsync').disabled = disabled;
+        document.getElementById('sSync').disabled = disabled;
+        document.getElementById('sRun').disabled = disabled;
+    }
+
+    function testSuiteFinished() {
+        setSettingsState(false);
+    }
+
+    window.runTestSuite = function () {
+        setSettingsState(true);
+
+        testIterations = parseInt(document.getElementById('sIterations').value);
+        totalSamples = parseInt(document.getElementById('sSamples').value);
+        asyncExecution = document.getElementById('sAsync').checked;
+
+        setTimeout(execQueuedTests, 0);
+    }
+
+    setTimeout(prepareQueuedTests, 0);
+
+    // Test queue.
+    queueTest({
+        name: "PerfTestReturnValue Default",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue();
+            }
+        },
+        description: "No arguments, returns int32 value.",
+        skipped: true,
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (0, Undefined)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(0);
+            }
+        },
+        description: "Int argument, returns undefined value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (1, Null)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(1);
+            }
+        },
+        description: "Int argument, returns null value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (2, Bool)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(2);
+            }
+        },
+        description: "Int argument, returns bool value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (3, Int)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(3);
+            }
+        },
+        description: "Int argument, returns int value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (4, UInt)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(4);
+            }
+        },
+        description: "Int argument, returns uint value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (5, Double)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(5);
+            }
+        },
+        description: "Int argument, returns double value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (6, Date)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(6);
+            }
+        },
+        description: "Int argument, returns date value.",
+        skipped: true,
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (7, String)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(7);
+            }
+        },
+        description: "Int argument, returns string value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (8, Object)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(8);
+            }
+        },
+        description: "Int argument, returns object value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (9, Array)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(9);
+            }
+        },
+        description: "Int argument, returns array value."
+    });
+
+    queueTest({
+        name: "PerfTestReturnValue (10, Function)",
+        func: function (count) {
+            for (var i = 0; i < count; i++) {
+                window.PerfTestReturnValue(10);
+            }
+        },
+        description: "Int argument, returns function value.",
+        skipped: true,
+    });
+    // add more tests to queueTest
+
+})();
+</script>
+
+    </body>
+</html>
diff --git a/src/tests/cefclient/resources/preferences.html b/src/tests/cefclient/resources/preferences.html
new file mode 100644
index 0000000..1062f1e
--- /dev/null
+++ b/src/tests/cefclient/resources/preferences.html
@@ -0,0 +1,338 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Preferences Test</title>
+
+  <!-- When using the mode "code" it's important to specify charset utf-8 -->
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+
+  <!-- jsoneditor project from https://github.com/josdejong/jsoneditor/
+       script hosting from http://cdnjs.com/libraries/jsoneditor -->
+  <link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/4.2.1/jsoneditor.min.css" rel="stylesheet" type="text/css">
+  <script src="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/4.2.1/jsoneditor.min.js"></script>
+
+  <script>
+    function setup() {
+      if (location.hostname == 'tests' || location.hostname == 'localhost')
+        return;
+
+      alert('This page can only be run from tests or localhost.');
+
+      // Disable all elements.
+      var elements = document.getElementById("form").elements;
+      for (var i = 0, element; element = elements[i++]; ) {
+        element.disabled = true;
+      }
+    }
+  </script>
+</head>
+<body bgcolor="white" onload="setup()">
+  <!-- Header -->
+  <div id="simple_links">
+    [ <b>Simple</b> ]
+    [ <a href="#" onClick="toggleView(); return false;">Advanced</a> ]
+  </div>
+  <div id="advanced_links" style="display:none">
+    [ <a href="#" onClick="toggleView(); return false;">Simple</a> ]
+    [ <b>Advanced</b> ]
+  </div>
+
+  <form id="form">
+
+  <!-- Simple view -->
+  <div id="simple">
+    <p>
+      This page supports display and configuration of a few sample preferences.
+      <table width="100%" style="border: 1px solid #97B0F8">
+        <tr>
+          <td>
+            <input type="checkbox" id="enable_spellchecking"/> Enable spell checking
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <br/>
+            <input type="checkbox" id="allow_running_insecure_content"/> Allow running insecure content
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <br/>
+            Proxy type:
+            <select id="proxy_type" onChange="proxyTypeChange()">
+              <option value="direct">Direct</option>
+              <option value="auto_detect">Auto-Detect</option>
+              <option value="pac_script">PAC Script</option>
+              <option value="fixed_servers">Fixed Servers</option>
+              <option value="system">System</option>
+            </select>
+            <input id="proxy_value" type="text" size="80" disabled/>
+          </td>
+        </tr>
+      </table>
+      <table border="0" width="100%">
+        <tr>
+          <td align="left">
+            <input type="button" value="Refresh" onClick="refreshSimple()"/>
+          </td>
+          <td align="right">
+            <input type="button" value="Apply Changes" onClick="applySimpleChanges()"/>
+          </td>
+        </tr>
+      </table>
+    </p>
+  </div>
+
+  <!-- Advanced view -->
+  <div id="advanced" style="display:none">
+    <p>
+      This page displays all preferences organized in a tree structure. Arbitrary changes are
+      allowed, however <b>changing preferences in arbitrary ways may result in crashes</b>. If you
+      experience a crash while setting preferences then run a Debug build of CEF/Chromium and watch
+      for DCHECKs in the Chromium code to figure out what went wrong.
+    </p>
+    <div id="jsoneditor" style="width: 100%; height: 100%;"></div>
+    <table border="0" width="100%">
+      <tr>
+        <td align="left">
+          <input type="button" value="Refresh" onClick="refreshEditor()"/>
+          <input type="checkbox" id="hide_defaults"/> Show modified preferences only
+        </td>
+        <td align="right">
+          <input type="button" value="Apply Changes" onClick="applyEditorChanges()"/>
+        </td>
+      </tr>
+    </table>
+  </div>
+
+  </form>
+
+  <script>
+    // Reference to the JSONEditor.
+    var editor = null;
+
+    // Preferences state information.
+    var preferences_state = null;
+
+    // Toggle between the simple and advanced views.
+    function toggleView() {
+      var simple = document.getElementById("simple");
+      var advanced = document.getElementById("advanced");
+      var simple_links = document.getElementById("simple_links");
+      var advanced_links = document.getElementById("advanced_links");
+
+      if (simple.style.display == "none") {
+        // Show the simple view.
+        simple.style.display = "";
+        simple_links.style.display = "";
+        advanced.style.display = "none";
+        advanced_links.style.display = "none";
+
+        // Refresh the simple view contents.
+        refreshSimple();
+      } else {
+        // Show the advanced view.
+        simple.style.display = "none";
+        simple_links.style.display = "none";
+        advanced.style.display = "";
+        advanced_links.style.display = "";
+
+        if (editor == null) {
+          // Create the editor.
+          editor = new JSONEditor(document.getElementById("jsoneditor"));
+        }
+
+        // Refesh the editor contents.
+        refreshEditor();
+      }
+    }
+
+    // Send a request to C++.
+    function sendRequest(request, onSuccessCallback) {
+      // Results in a call to the OnQuery method in preferences_test.cpp.
+      window.cefQuery({
+        request: JSON.stringify(request),
+        onSuccess: onSuccessCallback,
+        onFailure: function(error_code, error_message) {
+          alert(error_message + ' (' + error_code + ')');
+        }
+      });
+    }
+
+    // Get the preferences and execute |onSuccessCallback| with the resulting
+    // JSON object.
+    function getPreferences(include_defaults, onSuccessCallback) {
+      // Create the request object.
+      var request = {};
+      request.name = "preferences_get";
+      request.include_defaults = include_defaults;
+
+      // Send the request to C++.
+      sendRequest(
+        request,
+        function(response) {
+          onSuccessCallback(JSON.parse(response));
+        }
+      );
+    }
+
+    // Set the preferences.
+    function setPreferences(preferences) {
+      // Create the request object.
+      var request = {};
+      request.name = "preferences_set";
+      request.preferences = preferences;
+
+      // Send the request to C++.
+      sendRequest(
+        request,
+        function(response) {
+          // Show the informative response message.
+          alert(response);
+        }
+      );
+    }
+
+    // Get the global preference state.
+    function getPreferenceState() {
+      // Create the request object.
+      var request = {};
+      request.name = "preferences_state";
+
+      // Send the request to C++.
+      sendRequest(
+        request,
+        function(response) {
+          // Populate the global state object.
+          preferences_state = JSON.parse(response);
+
+          // Refresh the simple view contents.
+          refreshSimple();
+        }
+      );
+    }
+
+    // Refresh the editor view contents.
+    function refreshEditor() {
+      include_defaults = !document.getElementById("hide_defaults").checked;
+      getPreferences(include_defaults, function(response) {
+        // Set the JSON in the editor.
+        editor.set(response);
+      });
+    }
+
+    // Apply changes from the editor view.
+    function applyEditorChanges() {
+      setPreferences(editor.get());
+    }
+
+    // Refresh the simple view contents.
+    function refreshSimple() {
+      getPreferences(true, function(response) {
+        // Spellcheck settings.
+        if (preferences_state.spellcheck_disabled) {
+          // Cannot enable spell checking when disabled via the command-line.
+          document.getElementById("enable_spellchecking").checked = false;
+          document.getElementById("enable_spellchecking").disabled = true;
+        } else {
+          document.getElementById("enable_spellchecking").checked =
+              response.browser.enable_spellchecking;
+        }
+
+        // Web content settings.
+        if (preferences_state.allow_running_insecure_content) {
+          // Cannot disable running insecure content when enabled via the
+          // command-line.
+          document.getElementById("allow_running_insecure_content").checked =
+              true;
+          document.getElementById("allow_running_insecure_content").disabled =
+              true;
+        } else {
+          document.getElementById("allow_running_insecure_content").checked =
+              response.webkit.webprefs.allow_running_insecure_content;
+        }
+
+        // Proxy settings.
+        document.getElementById("proxy_type").value = response.proxy.mode;
+
+        // Some proxy modes have associated values.
+        if (response.proxy.mode == "pac_script")
+          proxy_value = response.proxy.pac_url;
+        else if (response.proxy.mode == "fixed_servers")
+          proxy_value = response.proxy.server;
+        else
+          proxy_value = null;
+
+        if (proxy_value != null)
+          document.getElementById("proxy_value").value = proxy_value;
+        document.getElementById("proxy_value").disabled = (proxy_value == null);
+
+        if (preferences_state.proxy_configured) {
+          // Cannot modify proxy settings that are configured via the command-
+          // line.
+          document.getElementById("proxy_type").disabled = true;
+          document.getElementById("proxy_value").disabled = true;
+        }
+      });
+    }
+
+    // Apply changes from the simple view.
+    function applySimpleChanges() {
+      has_preferences = false;
+      preferences = {};
+
+      // Spellcheck settings.
+      if (!preferences_state.spellcheck_disabled) {
+        has_preferences = true;
+
+        preferences.browser = {};
+        preferences.browser.enable_spellchecking =
+            document.getElementById("enable_spellchecking").checked;
+      }
+
+      // Web content settings.
+      if (!preferences_state.allow_running_insecure_content) {
+        has_preferences = true;
+
+        preferences.webkit = {};
+        preferences.webkit.webprefs = {};
+        preferences.webkit.webprefs.allow_running_insecure_content =
+            document.getElementById("allow_running_insecure_content").checked;
+      }
+
+      // Proxy settings.
+      if (!preferences_state.proxy_configured) {
+        has_preferences = true;
+
+        preferences.proxy = {};
+        preferences.proxy.mode = document.getElementById("proxy_type").value;
+
+        // Some proxy modes have associated values.
+        if (preferences.proxy.mode == "pac_script") {
+          preferences.proxy.pac_script =
+              document.getElementById("proxy_value").value;
+        } else  if (preferences.proxy.mode == "fixed_servers") {
+          preferences.proxy.server =
+              document.getElementById("proxy_value").value;
+        }
+      }
+
+      if (has_preferences)
+        setPreferences(preferences);
+    }
+
+    // Called when the proxy type is changed.
+    function proxyTypeChange() {
+      proxy_type = document.getElementById("proxy_type").value;
+      document.getElementById("proxy_value").value = "";
+
+      // Only enable the value field for the proxy modes that require it.
+      document.getElementById("proxy_value").disabled =
+          (proxy_type != "pac_script" && proxy_type != "fixed_servers");
+    }
+
+    // Retrieve global preferences state.
+    getPreferenceState();
+  </script>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/response_filter.html b/src/tests/cefclient/resources/response_filter.html
new file mode 100644
index 0000000..d67e457
--- /dev/null
+++ b/src/tests/cefclient/resources/response_filter.html
@@ -0,0 +1,142 @@
+<html>
+<head>
+<title>Response Filter Test</title>
+</head>
+<body bgcolor="white">
+<p>The text shown below in <font color="red">red</font> has been replaced by the filter. This document is > 64kb in order to exceed the standard output buffer size.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>0. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>1. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>2. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>3. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>4. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>5. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>6. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>7. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>8. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p>9. It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way -- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.</p>
+<p><font color="red">REPLACE_THIS_STRING</font></p>
+<p>End.</p>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/server.html b/src/tests/cefclient/resources/server.html
new file mode 100644
index 0000000..9e49e06
--- /dev/null
+++ b/src/tests/cefclient/resources/server.html
@@ -0,0 +1,104 @@
+<html>
+<head>
+<title>Server Test</title>
+<script language="JavaScript">
+
+// Send a query to the browser process.
+function sendMessage(request, success_callback) {
+  // Results in a call to the OnQuery method in server_test.cc
+  window.cefQuery({
+    request: JSON.stringify(request),
+    onSuccess: function(response) {
+      success_callback(response.length == 0 ? {} : JSON.parse(response));
+    },
+    onFailure: function(error_code, error_message) {
+      alert("Request failed with error " + error_message + "(" + error_code + ")");
+    }
+  });
+}
+
+function setButtonState(start_enabled, stop_enabled) {
+  document.getElementById('start').disabled = !start_enabled;
+  document.getElementById('stop').disabled = !stop_enabled;
+  document.getElementById('open').disabled = !stop_enabled;
+}
+
+function setup() {
+  if (location.origin != 'http://tests') {
+    document.getElementById('warning').style.display = 'block';
+    return;
+  }
+
+  // Query the current server state.
+  sendMessage({'action':'query'}, function(response) {
+    if (response['result'] == 'success') {
+      var running = (response['status'] == 'running')
+      setButtonState(!running, running);
+
+      var port_element = document.getElementById('port');
+      port_element.value = response['port'];
+      port_element.disabled = false;
+    }
+  });
+}
+
+function startServer() {  
+  var port = parseInt(document.getElementById('port').value);
+  if (port < 1025 || port > 65535) {
+    alert('Specify a port number between 1025 and 65535');
+    return;
+  }
+
+  setButtonState(false, false);
+
+  sendMessage({'action':'start', 'port':port}, function(response) {
+    if (response['result'] == 'success') {
+      setButtonState(false, true);
+    } else {
+      setButtonState(true, false);
+      alert(response['message']);
+    }
+  });
+}
+
+function stopServer() {
+  setButtonState(false, false);
+
+  sendMessage({'action':'stop'}, function(response) {
+    if (response['result'] == 'success') {
+      setButtonState(true, false);
+    } else {
+      setButtonState(false, true);
+      alert(response['message']);
+    }
+  });
+}
+
+function openServer() {
+  var port = document.getElementById('port').value;
+  window.open('http://localhost:' + port);
+}
+
+</script>
+
+</head>
+<body bgcolor="white" onload="setup()">
+<div id="warning" style="display:none;color:red;font-weight:bold;">
+This page can only be run from the http://tests origin.
+</div>
+<p>
+This page starts an HTTP/WebSocket server on localhost with the specified port number.
+After starting the server click the "Open Example" button to open the WebSocket Client test in a popup window.
+</p>
+<p>
+With this example each browser window can create/manage a separate server instance.
+The server will be stopped automatically when the managing browser window is closed.
+</p>
+<form>
+Server port: <input type="text" id="port" value="" disabled="true">
+<br/><input type="button" id="start" onclick="startServer()" value="Start Server" disabled="true">
+<input type="button" id="stop" onclick="stopServer()" value="Stop Server" disabled="true">
+<input type="button" id="open" onclick="openServer()" value="Open Example" disabled="true">
+</form>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/transparency.html b/src/tests/cefclient/resources/transparency.html
new file mode 100644
index 0000000..a8dd3b4
--- /dev/null
+++ b/src/tests/cefclient/resources/transparency.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>Transparency Examples</title>
+<style type="text/css">
+body {
+font-family: Verdana, Arial;
+}
+img {
+opacity:0.4;
+}
+img:hover {
+opacity:1.0;
+}
+.box_white, .box_black {
+font-size: 14px;
+font-weight: bold;
+text-align: center;
+padding: 10px;
+display: inline-block;
+width: 100px;
+}
+.box_white {
+background-color: white;
+border: 2px solid black;
+color: black;
+}
+.box_black {
+background-color: black;
+border: 2px solid white;
+color: white;
+}
+.box_0 {
+opacity: 1.0;
+}
+.box_25 {
+opacity: 0.75;
+}
+.box_50 {
+opacity: 0.5;
+}
+.box_75 {
+opacity: 0.25;
+}
+.box_100 {
+opacity: 0;
+}
+</style>
+</head>
+<body>
+
+<h1>Image Transparency</h1>
+Hover over an image to make it fully opaque.<br>
+<img src="http://www.w3schools.com/css/klematis.jpg" width="150" height="113" alt="klematis" />
+<img src="http://www.w3schools.com/css/klematis2.jpg" width="150" height="113" alt="klematis" />
+
+<h1>Block Transparency</h1>
+<span class="box_white box_0">White 0%</span> <span class="box_white box_25">White 25%</span> <span class="box_white box_50">White 50%</span> <span class="box_white box_75">White 75%</span> <span class="box_white box_100">White 100%</span>
+<br>
+<span class="box_black box_0">Black 0%</span> <span class="box_black box_25">Black 25%</span> <span class="box_black box_50">Black 50%</span> <span class="box_black box_75">Black 75%</span> <span class="box_black box_100">Black 100%</span>
+
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/urlrequest.html b/src/tests/cefclient/resources/urlrequest.html
new file mode 100644
index 0000000..fc7484d
--- /dev/null
+++ b/src/tests/cefclient/resources/urlrequest.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+<script language="JavaScript">
+
+function setup() {
+  if (location.hostname == 'tests' || location.hostname == 'localhost')
+    return;
+
+  alert('This page can only be run from tests or localhost');
+
+  // Disable all elements.
+  var elements = document.getElementById("form").elements;
+  for (var i = 0, element; element = elements[i++]; ) {
+    element.disabled = true;
+  }
+}
+
+// Send a query to the browser process.
+function execURLRequest() {
+  document.getElementById('ta').value = 'Request pending...';
+
+  // Results in a call to the OnQuery method in urlrequest_test.cpp
+  window.cefQuery({
+    request: 'URLRequestTest:' + document.getElementById("url").value,
+    onSuccess: function(response) {
+      document.getElementById('ta').value = response;
+    },
+    onFailure: function(error_code, error_message) {
+      document.getElementById('ta').value = 'Failed with error ' + error_message + ' (' + error_code + ')';
+    }
+  });
+}
+</script>
+</head>
+<body bgcolor="white" onload="setup()">
+<form id="form">
+URL: <input type="text" id="url" value="http://www.google.com">
+<br/><input type="button" onclick="execURLRequest();" value="Execute CefURLRequest">
+<br/><textarea rows="10" cols="40" id="ta"></textarea>
+</form>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/websocket.html b/src/tests/cefclient/resources/websocket.html
new file mode 100644
index 0000000..e13abc0
--- /dev/null
+++ b/src/tests/cefclient/resources/websocket.html
@@ -0,0 +1,107 @@
+<html>
+<head>
+<title>WebSocket Test</title>
+<script language="JavaScript">
+
+var ws = null;
+
+function setup() {
+  // Match the secure state of the current origin.
+  var origin = location.origin;
+  if (origin.indexOf('http://') == 0) {
+    origin = origin.replace('http://', 'ws://');
+  } else if (origin.indexOf('https://') == 0) {
+    origin = origin.replace('https://', 'wss://');
+  } else {
+    origin = '';
+  }
+
+  if (origin.length > 0)
+    document.getElementById('server').value = origin;
+  document.getElementById('server').disabled = false;
+
+  if (location.hostname != 'localhost')
+    document.getElementById('warning').style.display = 'block';
+
+  setConnected(false);
+}
+
+function setConnected(connected) {
+  document.getElementById('connect').disabled = connected;
+  document.getElementById('disconnect').disabled = !connected;
+  document.getElementById('message').disabled = !connected;
+  document.getElementById('response').disabled = !connected;
+  document.getElementById('send').disabled = !connected;
+}
+
+function doConnect() {
+  var url = document.getElementById('server').value;
+  if (url.indexOf('ws://') < 0 && url.indexOf('wss://') < 0) {
+    alert('Specify a valid WebSocket server URL.');
+    return;
+  }
+
+  if (ws) {
+    alert('WebSocket is already connected.');
+    return;
+  }
+
+  ws = new WebSocket(url);
+  ws.onopen = function() { setConnected(true); };
+  ws.onmessage = function(event) {
+    document.getElementById('response').value = event.data;
+  };
+  ws.onclose = function(event) {
+    setConnected(false);
+    ws = null;
+  };
+  ws.onerror = function(event) {
+    if (ws.readyState == 3)
+      alert('WebSocket connection failed.');
+  }
+}
+
+function doDisconnect() {
+  if (!ws) {
+    alert('WebSocket is not currently connected.');
+    return;
+  }
+
+  ws.close();
+}
+
+function doSend() {
+  if (!ws) {
+    alert('WebSocket is not currently connected.');
+    return;
+  }
+
+  var value = document.getElementById('message').value;
+  if (value.length > 0)
+    ws.send(value);
+}
+
+</script>
+
+</head>
+<body bgcolor="white" onload="setup()">
+<div id="warning" style="display:none;color:red;font-weight:bold;">
+This page is most useful when loaded from localhost.
+You should first create a server using the <a href="http://tests/server">HTTP/WebSocket Server test</a>.
+</div>
+<p>
+This page tests a WebSocket connection.
+The example implementation in server_test.cc will then echo the message contents in reverse.
+</p>
+<form>
+Server URL: <input type="text" id="server" value="" disabled="true">
+<br/><input type="button" id="connect" onclick="doConnect()" value="Connect" disabled="true">
+<input type="button" id="disconnect" onclick="doDisconnect()" value="Disconnect" disabled="true">
+<br/>Message: <input type="text" id="message" value="Test Message" disabled="true">
+<input type="button" id="send" onclick="doSend()" value="Send" disabled="true">
+<br/>Response: <input type="text" id="response" value="" disabled="true">
+<br/><br/>
+The example implementation in server_test.cc can also serve the HTTP-based <a href="other_tests">Other Tests</a>.
+</form>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/win/cefclient.exe.manifest b/src/tests/cefclient/resources/win/cefclient.exe.manifest
new file mode 100644
index 0000000..d36f084
--- /dev/null
+++ b/src/tests/cefclient/resources/win/cefclient.exe.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+
+  <!--The compatibility section will be merged from build/win/compatibility.manifest -->
+
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
+    </dependentAssembly>
+  </dependency>
+
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+</assembly>
diff --git a/src/tests/cefclient/resources/win/cefclient.ico b/src/tests/cefclient/resources/win/cefclient.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/src/tests/cefclient/resources/win/cefclient.ico
Binary files differ
diff --git a/src/tests/cefclient/resources/win/cefclient.rc b/src/tests/cefclient/resources/win/cefclient.rc
new file mode 100644
index 0000000..77bf9df
--- /dev/null
+++ b/src/tests/cefclient/resources/win/cefclient.rc
@@ -0,0 +1,232 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "tests/cefclient/browser/resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#define APSTUDIO_HIDDEN_SYMBOLS

+#include "windows.h"

+#include "include/cef_version.h"

+#undef APSTUDIO_HIDDEN_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Binary

+//

+

+IDS_BINDING_HTML BINARY "..\\binding.html"

+IDS_DIALOGS_HTML BINARY "..\\dialogs.html"

+IDS_DRAGGABLE_HTML BINARY "..\\draggable.html"

+IDS_DRM_HTML BINARY "..\\drm.html"

+IDS_LOCALSTORAGE_HTML BINARY "..\\localstorage.html"

+IDS_LOGO_PNG BINARY "..\\logo.png"

+IDS_MEDIA_ROUTER_HTML BINARY "..\\media_router.html"

+IDS_MENU_ICON_1X_PNG BINARY "..\\menu_icon.1x.png"

+IDS_MENU_ICON_2X_PNG BINARY "..\\menu_icon.2x.png"

+IDS_OSRTEST_HTML BINARY "..\\..\\..\\shared\\resources\\osr_test.html"

+IDS_OTHER_TESTS_HTML BINARY "..\\other_tests.html"

+IDS_PDF_HTML BINARY "..\\..\\..\\shared\\resources\\pdf.html"

+IDS_PDF_PDF BINARY "..\\..\\..\\shared\\resources\\pdf.pdf"

+IDS_PERFORMANCE_HTML BINARY "..\\performance.html"

+IDS_PERFORMANCE2_HTML BINARY "..\\performance2.html"

+IDS_PREFERENCES_HTML BINARY "..\\preferences.html"

+IDS_RESPONSE_FILTER_HTML BINARY "..\\response_filter.html"

+IDS_SERVER_HTML BINARY "..\\server.html"

+IDS_TRANSPARENCY_HTML BINARY "..\\transparency.html"

+IDS_URLREQUEST_HTML BINARY "..\\urlrequest.html"

+IDS_WEBSOCKET_HTML BINARY "..\\websocket.html"

+IDS_WINDOW_HTML BINARY "..\\window.html"

+IDS_WINDOW_ICON_1X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.1x.png"

+IDS_WINDOW_ICON_2X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.2x.png"

+IDS_XMLHTTPREQUEST_HTML BINARY "..\\xmlhttprequest.html"

+

+IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG BINARY "..\\extensions\\set_page_color\\icon.png"

+IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON BINARY "..\\extensions\\set_page_color\\manifest.json"

+IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML BINARY "..\\extensions\\set_page_color\\popup.html"

+IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS BINARY "..\\extensions\\set_page_color\\popup.js"

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Icon

+//

+

+// Icon with lowest ID value placed first to ensure application icon

+// remains consistent on all systems.

+IDI_CEFCLIENT           ICON                    "cefclient.ico"

+IDI_SMALL               ICON                    "small.ico"

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Menu

+//

+

+IDC_CEFCLIENT MENU

+BEGIN

+    POPUP "&File"

+    BEGIN

+        MENUITEM "&Find...",                    ID_FIND

+        MENUITEM SEPARATOR

+        MENUITEM "E&xit",                       IDM_EXIT

+    END

+    POPUP "&Help"

+    BEGIN

+        MENUITEM "&About ...",                  IDM_ABOUT

+    END

+    POPUP "Tests"

+    BEGIN

+        MENUITEM "Get Source",                  ID_TESTS_GETSOURCE

+        MENUITEM "Get Text",                    ID_TESTS_GETTEXT

+        MENUITEM "New Window",                  ID_TESTS_WINDOW_NEW

+        MENUITEM "Popup Window",                ID_TESTS_WINDOW_POPUP

+        MENUITEM "Request",                     ID_TESTS_REQUEST

+        MENUITEM "Plugin Info",                 ID_TESTS_PLUGIN_INFO

+        MENUITEM "Zoom In",                     ID_TESTS_ZOOM_IN

+        MENUITEM "Zoom Out",                    ID_TESTS_ZOOM_OUT

+        MENUITEM "Zoom Reset",                  ID_TESTS_ZOOM_RESET

+        MENUITEM "Set FPS",                     ID_TESTS_OSR_FPS

+        MENUITEM "Set Scale Factor",            ID_TESTS_OSR_DSF

+        MENUITEM "Begin Tracing",               ID_TESTS_TRACING_BEGIN

+        MENUITEM "End Tracing",                 ID_TESTS_TRACING_END

+        MENUITEM "Print",                       ID_TESTS_PRINT

+        MENUITEM "Print to PDF",                ID_TESTS_PRINT_TO_PDF

+        MENUITEM "Mute Audio",                  ID_TESTS_MUTE_AUDIO

+        MENUITEM "Unmute Audio",                ID_TESTS_UNMUTE_AUDIO

+        MENUITEM "Other Tests",                 ID_TESTS_OTHER_TESTS

+   END

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Accelerator

+//

+

+IDC_CEFCLIENT ACCELERATORS

+BEGIN

+    "?",            IDM_ABOUT,              ASCII,  ALT

+    "/",            IDM_ABOUT,              ASCII,  ALT

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Dialog

+//

+

+IDD_ABOUTBOX DIALOG  22, 17, 230, 75

+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU

+CAPTION "About"

+FONT 8, "System"

+BEGIN

+    ICON            IDI_CEFCLIENT,IDC_MYICON,14,9,16,16

+    LTEXT           "cefclient Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX

+    LTEXT           "Copyright (C) 2008",IDC_STATIC,49,20,119,8

+    DEFPUSHBUTTON   "OK",IDOK,195,6,30,11,WS_GROUP

+END

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0

+ PRODUCTVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0

+ FILEFLAGSMASK 0x17L

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x2L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904b0"

+        BEGIN

+            VALUE "FileDescription", "Chromium Embedded Framework (CEF) Client Application"

+            VALUE "FileVersion", CEF_VERSION

+            VALUE "InternalName", "cefclient"

+            VALUE "LegalCopyright", "Copyright (C) " MAKE_STRING(COPYRIGHT_YEAR) " The Chromium Embedded Framework Authors"

+            VALUE "OriginalFilename", "cefclient.exe"

+            VALUE "ProductName", "Chromium Embedded Framework (CEF) Client Application"

+            VALUE "ProductVersion", CEF_VERSION

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1200

+    END

+END

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE

+BEGIN

+    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"

+    "#include ""windows.h""\r\n"

+    "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE

+BEGIN

+    "\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// String Table

+//

+

+STRINGTABLE

+BEGIN

+    IDS_APP_TITLE           "cefclient"

+    IDC_CEFCLIENT           "CEFCLIENT"

+END

+

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/src/tests/cefclient/resources/win/small.ico b/src/tests/cefclient/resources/win/small.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/src/tests/cefclient/resources/win/small.ico
Binary files differ
diff --git a/src/tests/cefclient/resources/window.html b/src/tests/cefclient/resources/window.html
new file mode 100644
index 0000000..7703d0d
--- /dev/null
+++ b/src/tests/cefclient/resources/window.html
@@ -0,0 +1,65 @@
+<html>
+<head>
+<title>Window Test</title>
+<script>
+function setup() {
+  if (location.hostname == 'tests' || location.hostname == 'localhost')
+    return;
+
+  alert('This page can only be run from tests or localhost.');
+
+  // Disable all elements.
+  var elements = document.getElementById("form").elements;
+  for (var i = 0, element; element = elements[i++]; ) {
+    element.disabled = true;
+  }
+}
+
+function send_message(test, params) {
+  var message = 'WindowTest.' + test;
+  if (typeof params != 'undefined')
+    message += ':' + params;
+
+  // Results in a call to the OnQuery method in window_test.cpp.
+  window.cefQuery({'request' : message});
+}
+
+function minimize() {
+  send_message('Minimize');
+}
+
+function maximize() {
+  send_message('Maximize');
+}
+
+function restore() {
+  minimize();
+  setTimeout(function() { send_message('Restore'); }, 1000);
+}
+
+function position() {
+  var x = parseInt(document.getElementById('x').value);
+  var y = parseInt(document.getElementById('y').value);
+  var width = parseInt(document.getElementById('width').value);
+  var height = parseInt(document.getElementById('height').value);
+  if (isNaN(x) || isNaN(y) || isNaN(width) || isNaN(height))
+    alert('Please specify a valid numeric value.');
+  else
+    send_message('Position', x + ',' + y + ',' + width + ',' + height);
+}
+</script>
+</head>
+<body bgcolor="white" onload="setup()">
+<form id="form">
+Click a button to perform the associated window action.
+<br/><input type="button" onclick="minimize();" value="Minimize">
+<br/><input type="button" onclick="maximize();" value="Maximize">
+<br/><input type="button" onclick="restore();" value="Restore"> (minimizes and then restores the window as topmost)
+<br/><input type="button" onclick="position();" value="Set Position">
+X: <input type="text" size="4" id="x" value="200">
+Y: <input type="text" size="4" id="y" value="100">
+Width: <input type="text" size="4" id="width" value="800">
+Height: <input type="text" size="4" id="height" value="600">
+</form>
+</body>
+</html>
diff --git a/src/tests/cefclient/resources/xmlhttprequest.html b/src/tests/cefclient/resources/xmlhttprequest.html
new file mode 100644
index 0000000..8da20f3
--- /dev/null
+++ b/src/tests/cefclient/resources/xmlhttprequest.html
@@ -0,0 +1,39 @@
+<html>
+<body bgcolor="white">
+<script language="JavaScript">
+function execXMLHttpRequest()
+{
+  var url = document.getElementById("url").value;
+  var warningElement = document.getElementById("warning");
+  if (url.indexOf(location.origin) != 0) {
+    warningElement.innerHTML =
+      'For cross-origin requests to succeed the server must return CORS headers:' +
+      '<pre>Access-Control-Allow-Origin: ' + location.origin +
+      '<br/>Access-Control-Allow-Header: My-Custom-Header</pre>';
+    warningElement.style.display = 'block';
+  } else {
+    warningElement.style.display = 'none';
+  }
+
+  xhr = new XMLHttpRequest();
+  xhr.open("GET", url, true);
+  xhr.setRequestHeader('My-Custom-Header', 'Some Value');
+  xhr.onload = function(e) {
+    if (xhr.readyState === 4) {
+      var value = "Status Code: "+xhr.status;
+      if (xhr.status === 200)
+        value += "\n\n"+xhr.responseText;
+      document.getElementById('ta').value = value;
+    }
+  }
+  xhr.send();
+}
+</script>
+<form>
+URL: <input type="text" id="url" value="http://tests/request">
+<br/><input type="button" onclick="execXMLHttpRequest();" value="Execute XMLHttpRequest">
+<br/><textarea rows="10" cols="40" id="ta"></textarea>
+</form>
+<div id="warning" style="display:none;font-weight:bold;"></div>
+</body>
+</html>
diff --git a/src/tests/cefsimple/CMakeLists.txt.in b/src/tests/cefsimple/CMakeLists.txt.in
new file mode 100644
index 0000000..12b1c84
--- /dev/null
+++ b/src/tests/cefsimple/CMakeLists.txt.in
@@ -0,0 +1,214 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+#
+# Source files.
+#
+
+# cefsimple sources.
+{{
+  'prefix': 'cefsimple',
+  'set': 'CEFSIMPLE_SRCS',
+  'includes': [
+    'cefsimple_sources_common',
+    'cefsimple_sources_win:WINDOWS',
+    'cefsimple_sources_mac:MACOSX',
+    'cefsimple_sources_linux:LINUX',
+  ],
+}}
+
+# cefsimple helper sources.
+{{
+  'prefix': 'cefsimple_helper',
+  'includes': [
+    'cefsimple_sources_mac_helper:MACOSX',
+  ],
+}}
+
+# cefsimple resources.
+{{
+  'prefix': 'cefsimple_resources',
+  'set': 'CEFSIMPLE_RESOURCES_SRCS',
+  'includes': [
+    'cefsimple_bundle_resources_mac:MACOSX',
+  ],
+}}
+
+
+#
+# Shared configuration.
+#
+
+# Target executable names.
+set(CEF_TARGET "cefsimple")
+if(OS_MACOSX)
+  set(CEF_HELPER_TARGET "cefsimple_Helper")
+  set(CEF_HELPER_OUTPUT_NAME "cefsimple Helper")
+else()
+  # Logical target used to link the libcef library.
+  ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}")
+endif()
+
+# Determine the target output directory.
+SET_CEF_TARGET_OUT_DIR()
+
+
+#
+# Linux configuration.
+#
+
+if(OS_LINUX)
+  # Executable target.
+  add_executable(${CEF_TARGET} ${CEFSIMPLE_SRCS})
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
+  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})
+
+  # Set rpath so that libraries can be placed next to the executable.
+  set_target_properties(${CEF_TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN")
+  set_target_properties(${CEF_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
+  set_target_properties(${CEF_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})
+
+  # Copy binary and resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
+  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
+  if (EXISTS "${CEF_BINARY_DIR}/libminigbm.so")
+    COPY_FILES("${CEF_TARGET}" "libminigbm.so" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
+  endif()
+
+  # Set SUID permissions on the chrome-sandbox target.
+  SET_LINUX_SUID_PERMISSIONS("${CEF_TARGET}" "${CEF_TARGET_OUT_DIR}/chrome-sandbox")
+endif()
+
+
+#
+# Mac OS X configuration.
+#
+
+if(OS_MACOSX)
+  option(OPTION_USE_ARC "Build with ARC (automatic Reference Counting) on macOS." ON)
+  if(OPTION_USE_ARC)
+    list(APPEND CEF_COMPILER_FLAGS
+      -fobjc-arc
+      )
+    set_target_properties(${target} PROPERTIES
+      CLANG_ENABLE_OBJC_ARC "YES"
+      )
+  endif()
+
+  # Output path for the main app bundle.
+  set(CEF_APP "${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.app")
+
+  # Variables referenced from the main Info.plist file.
+  set(EXECUTABLE_NAME "${CEF_TARGET}")
+  set(PRODUCT_NAME "${CEF_TARGET}")
+
+  if(USE_SANDBOX)
+    # Logical target used to link the cef_sandbox library.
+    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
+  endif()
+
+  # Main app bundle target.
+  add_executable(${CEF_TARGET} MACOSX_BUNDLE ${CEFSIMPLE_RESOURCES_SRCS} ${CEFSIMPLE_SRCS})
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
+  target_link_libraries(${CEF_TARGET} libcef_dll_wrapper ${CEF_STANDARD_LIBS})
+  set_target_properties(${CEF_TARGET} PROPERTIES
+    MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist
+    )
+
+  # Copy the CEF framework into the Frameworks directory.
+  add_custom_command(
+    TARGET ${CEF_TARGET}
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy_directory
+            "${CEF_BINARY_DIR}/Chromium Embedded Framework.framework"
+            "${CEF_APP}/Contents/Frameworks/Chromium Embedded Framework.framework"
+    VERBATIM
+    )
+
+  # Create the multiple Helper app bundle targets.
+  foreach(_suffix_list ${CEF_HELPER_APP_SUFFIXES})
+    # Convert to a list and extract the suffix values.
+    string(REPLACE ":" ";" _suffix_list ${_suffix_list})
+    list(GET _suffix_list 0 _name_suffix)
+    list(GET _suffix_list 1 _target_suffix)
+    list(GET _suffix_list 2 _plist_suffix)
+
+    # Define Helper target and output names.
+    set(_helper_target "${CEF_HELPER_TARGET}${_target_suffix}")
+    set(_helper_output_name "${CEF_HELPER_OUTPUT_NAME}${_name_suffix}")
+
+    # Create Helper-specific variants of the helper-Info.plist file. Do this
+    # manually because the configure_file command (which is executed as part of
+    # MACOSX_BUNDLE_INFO_PLIST) uses global env variables and would insert the
+    # wrong values with multiple targets.
+    set(_helper_info_plist "${CMAKE_CURRENT_BINARY_DIR}/helper-Info${_target_suffix}.plist")
+    file(READ "${CMAKE_CURRENT_SOURCE_DIR}/mac/helper-Info.plist" _plist_contents)
+    string(REPLACE "\${EXECUTABLE_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
+    string(REPLACE "\${PRODUCT_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
+    string(REPLACE "\${BUNDLE_ID_SUFFIX}" "${_plist_suffix}" _plist_contents ${_plist_contents})
+    file(WRITE ${_helper_info_plist} ${_plist_contents})
+
+    # Create Helper executable target.
+    add_executable(${_helper_target} MACOSX_BUNDLE ${CEFSIMPLE_HELPER_SRCS})
+    SET_EXECUTABLE_TARGET_PROPERTIES(${_helper_target})
+    add_dependencies(${_helper_target} libcef_dll_wrapper)
+    target_link_libraries(${_helper_target} libcef_dll_wrapper ${CEF_STANDARD_LIBS})
+    set_target_properties(${_helper_target} PROPERTIES
+      MACOSX_BUNDLE_INFO_PLIST ${_helper_info_plist}
+      OUTPUT_NAME ${_helper_output_name}
+      )
+
+    if(USE_SANDBOX)
+      target_link_libraries(${_helper_target} cef_sandbox_lib)
+    endif()
+
+    # Add the Helper as a dependency of the main executable target.
+    add_dependencies(${CEF_TARGET} "${_helper_target}")
+
+    # Copy the Helper app bundle into the Frameworks directory.
+    add_custom_command(
+      TARGET ${CEF_TARGET}
+      POST_BUILD
+      COMMAND ${CMAKE_COMMAND} -E copy_directory
+              "${CEF_TARGET_OUT_DIR}/${_helper_output_name}.app"
+              "${CEF_APP}/Contents/Frameworks/${_helper_output_name}.app"
+      VERBATIM
+      )
+  endforeach()
+
+  # Manually process and copy over resource files.
+  # The Xcode generator can support this via the set_target_properties RESOURCE
+  # directive but that doesn't properly handle nested resource directories.
+  # Remove these prefixes from input file paths.
+  set(PREFIXES "mac/")
+  COPY_MACOSX_RESOURCES("${CEFSIMPLE_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
+endif()
+
+
+#
+# Windows configuration.
+#
+
+if(OS_WINDOWS)
+  # Executable target.
+  add_executable(${CEF_TARGET} WIN32 ${CEFSIMPLE_SRCS})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})
+
+  if(USE_SANDBOX)
+    # Logical target used to link the cef_sandbox library.
+    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
+    target_link_libraries(${CEF_TARGET} cef_sandbox_lib ${CEF_SANDBOX_STANDARD_LIBS})
+  endif()
+
+  # Add the custom manifest files to the executable.
+  ADD_WINDOWS_MANIFEST("${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_TARGET}" "exe")
+
+  # Copy binary and resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
+  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
+endif()
diff --git a/src/tests/cefsimple/cefsimple.exe.manifest b/src/tests/cefsimple/cefsimple.exe.manifest
new file mode 100644
index 0000000..d36f084
--- /dev/null
+++ b/src/tests/cefsimple/cefsimple.exe.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+
+  <!--The compatibility section will be merged from build/win/compatibility.manifest -->
+
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
+    </dependentAssembly>
+  </dependency>
+
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+</assembly>
diff --git a/src/tests/cefsimple/cefsimple.rc b/src/tests/cefsimple/cefsimple.rc
new file mode 100644
index 0000000..c4f3f29
--- /dev/null
+++ b/src/tests/cefsimple/cefsimple.rc
@@ -0,0 +1,79 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#define APSTUDIO_HIDDEN_SYMBOLS

+#include "windows.h"

+#undef APSTUDIO_HIDDEN_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Icon

+//

+

+// Icon with lowest ID value placed first to ensure application icon

+// remains consistent on all systems.

+IDI_CEFSIMPLE           ICON                    "res\cefsimple.ico"

+IDI_SMALL               ICON                    "res\small.ico"

+

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE

+BEGIN

+    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"

+    "#include ""windows.h""\r\n"

+    "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE

+BEGIN

+    "\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/src/tests/cefsimple/cefsimple_linux.cc b/src/tests/cefsimple/cefsimple_linux.cc
new file mode 100644
index 0000000..87efbb7
--- /dev/null
+++ b/src/tests/cefsimple/cefsimple_linux.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefsimple/simple_app.h"
+
+#if defined(CEF_X11)
+#include <X11/Xlib.h>
+#endif
+
+#include "include/base/cef_logging.h"
+
+#if defined(CEF_X11)
+namespace {
+
+int XErrorHandlerImpl(Display* display, XErrorEvent* event) {
+  LOG(WARNING) << "X error received: "
+               << "type " << event->type << ", "
+               << "serial " << event->serial << ", "
+               << "error_code " << static_cast<int>(event->error_code) << ", "
+               << "request_code " << static_cast<int>(event->request_code)
+               << ", "
+               << "minor_code " << static_cast<int>(event->minor_code);
+  return 0;
+}
+
+int XIOErrorHandlerImpl(Display* display) {
+  return 0;
+}
+
+}  // namespace
+#endif  // defined(CEF_X11)
+
+// Entry point function for all processes.
+int main(int argc, char* argv[]) {
+  // Provide CEF with command-line arguments.
+  CefMainArgs main_args(argc, argv);
+
+  // CEF applications have multiple sub-processes (render, plugin, GPU, etc)
+  // that share the same executable. This function checks the command-line and,
+  // if this is a sub-process, executes the appropriate logic.
+  int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
+  if (exit_code >= 0) {
+    // The sub-process has completed so return here.
+    return exit_code;
+  }
+
+#if defined(CEF_X11)
+  // Install xlib error handlers so that the application won't be terminated
+  // on non-fatal errors.
+  XSetErrorHandler(XErrorHandlerImpl);
+  XSetIOErrorHandler(XIOErrorHandlerImpl);
+#endif
+
+  // Specify CEF global settings here.
+  CefSettings settings;
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
+// use of the sandbox.
+#if !defined(CEF_USE_SANDBOX)
+  settings.no_sandbox = true;
+#endif
+
+  // SimpleApp implements application-level callbacks for the browser process.
+  // It will create the first browser instance in OnContextInitialized() after
+  // CEF has initialized.
+  CefRefPtr<SimpleApp> app(new SimpleApp);
+
+  // Initialize CEF for the browser process.
+  CefInitialize(main_args, settings, app.get(), nullptr);
+
+  // Run the CEF message loop. This will block until CefQuitMessageLoop() is
+  // called.
+  CefRunMessageLoop();
+
+  // Shut down CEF.
+  CefShutdown();
+
+  return 0;
+}
diff --git a/src/tests/cefsimple/cefsimple_mac.mm b/src/tests/cefsimple/cefsimple_mac.mm
new file mode 100644
index 0000000..e5301b4
--- /dev/null
+++ b/src/tests/cefsimple/cefsimple_mac.mm
@@ -0,0 +1,165 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "include/cef_application_mac.h"
+#include "include/wrapper/cef_helpers.h"
+#include "include/wrapper/cef_library_loader.h"
+#include "tests/cefsimple/simple_app.h"
+#include "tests/cefsimple/simple_handler.h"
+
+// Receives notifications from the application.
+@interface SimpleAppDelegate : NSObject <NSApplicationDelegate>
+- (void)createApplication:(id)object;
+- (void)tryToTerminateApplication:(NSApplication*)app;
+@end
+
+// Provide the CefAppProtocol implementation required by CEF.
+@interface SimpleApplication : NSApplication <CefAppProtocol> {
+ @private
+  BOOL handlingSendEvent_;
+}
+@end
+
+@implementation SimpleApplication
+- (BOOL)isHandlingSendEvent {
+  return handlingSendEvent_;
+}
+
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
+  handlingSendEvent_ = handlingSendEvent;
+}
+
+- (void)sendEvent:(NSEvent*)event {
+  CefScopedSendingEvent sendingEventScoper;
+  [super sendEvent:event];
+}
+
+// |-terminate:| is the entry point for orderly "quit" operations in Cocoa. This
+// includes the application menu's quit menu item and keyboard equivalent, the
+// application's dock icon menu's quit menu item, "quit" (not "force quit") in
+// the Activity Monitor, and quits triggered by user logout and system restart
+// and shutdown.
+//
+// The default |-terminate:| implementation ends the process by calling exit(),
+// and thus never leaves the main run loop. This is unsuitable for Chromium
+// since Chromium depends on leaving the main run loop to perform an orderly
+// shutdown. We support the normal |-terminate:| interface by overriding the
+// default implementation. Our implementation, which is very specific to the
+// needs of Chromium, works by asking the application delegate to terminate
+// using its |-tryToTerminateApplication:| method.
+//
+// |-tryToTerminateApplication:| differs from the standard
+// |-applicationShouldTerminate:| in that no special event loop is run in the
+// case that immediate termination is not possible (e.g., if dialog boxes
+// allowing the user to cancel have to be shown). Instead, this method tries to
+// close all browsers by calling CloseBrowser(false) via
+// ClientHandler::CloseAllBrowsers. Calling CloseBrowser will result in a call
+// to ClientHandler::DoClose and execution of |-performClose:| on the NSWindow.
+// DoClose sets a flag that is used to differentiate between new close events
+// (e.g., user clicked the window close button) and in-progress close events
+// (e.g., user approved the close window dialog). The NSWindowDelegate
+// |-windowShouldClose:| method checks this flag and either calls
+// CloseBrowser(false) in the case of a new close event or destructs the
+// NSWindow in the case of an in-progress close event.
+// ClientHandler::OnBeforeClose will be called after the CEF NSView hosted in
+// the NSWindow is dealloc'ed.
+//
+// After the final browser window has closed ClientHandler::OnBeforeClose will
+// begin actual tear-down of the application by calling CefQuitMessageLoop.
+// This ends the NSApplication event loop and execution then returns to the
+// main() function for cleanup before application termination.
+//
+// The standard |-applicationShouldTerminate:| is not supported, and code paths
+// leading to it must be redirected.
+- (void)terminate:(id)sender {
+  SimpleAppDelegate* delegate =
+      static_cast<SimpleAppDelegate*>([NSApp delegate]);
+  [delegate tryToTerminateApplication:self];
+  // Return, don't exit. The application is responsible for exiting on its own.
+}
+@end
+
+@implementation SimpleAppDelegate
+
+// Create the application on the UI thread.
+- (void)createApplication:(id)object {
+  [NSApplication sharedApplication];
+  [[NSBundle mainBundle] loadNibNamed:@"MainMenu"
+                                owner:NSApp
+                      topLevelObjects:nil];
+
+  // Set the delegate for application events.
+  [[NSApplication sharedApplication] setDelegate:self];
+}
+
+- (void)tryToTerminateApplication:(NSApplication*)app {
+  SimpleHandler* handler = SimpleHandler::GetInstance();
+  if (handler && !handler->IsClosing())
+    handler->CloseAllBrowsers(false);
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:
+    (NSApplication*)sender {
+  return NSTerminateNow;
+}
+@end
+
+// Entry point function for the browser process.
+int main(int argc, char* argv[]) {
+  // Load the CEF framework library at runtime instead of linking directly
+  // as required by the macOS sandbox implementation.
+  CefScopedLibraryLoader library_loader;
+  if (!library_loader.LoadInMain())
+    return 1;
+
+  // Provide CEF with command-line arguments.
+  CefMainArgs main_args(argc, argv);
+
+  @autoreleasepool {
+    // Initialize the SimpleApplication instance.
+    [SimpleApplication sharedApplication];
+
+    // Specify CEF global settings here.
+    CefSettings settings;
+
+    // When generating projects with CMake the CEF_USE_SANDBOX value will be
+    // defined automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line
+    // to disable use of the sandbox.
+#if !defined(CEF_USE_SANDBOX)
+    settings.no_sandbox = true;
+#endif
+
+    // SimpleApp implements application-level callbacks for the browser process.
+    // It will create the first browser instance in OnContextInitialized() after
+    // CEF has initialized.
+    CefRefPtr<SimpleApp> app(new SimpleApp);
+
+    // Initialize CEF for the browser process.
+    CefInitialize(main_args, settings, app.get(), NULL);
+
+    // Create the application delegate.
+    NSObject* delegate = [[SimpleAppDelegate alloc] init];
+    [delegate performSelectorOnMainThread:@selector(createApplication:)
+                               withObject:nil
+                            waitUntilDone:NO];
+
+    // Run the CEF message loop. This will block until CefQuitMessageLoop() is
+    // called.
+    CefRunMessageLoop();
+
+    // Shut down CEF.
+    CefShutdown();
+
+    // Release the delegate.
+#if !__has_feature(objc_arc)
+    [delegate release];
+#endif  // !__has_feature(objc_arc)
+    delegate = nil;
+  }  // @autoreleasepool
+
+  return 0;
+}
diff --git a/src/tests/cefsimple/cefsimple_win.cc b/src/tests/cefsimple/cefsimple_win.cc
new file mode 100644
index 0000000..14fe5e1
--- /dev/null
+++ b/src/tests/cefsimple/cefsimple_win.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <windows.h>
+
+#include "include/cef_sandbox_win.h"
+#include "tests/cefsimple/simple_app.h"
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
+// to the CMake command-line to disable use of the sandbox.
+// Uncomment this line to manually enable sandbox support.
+// #define CEF_USE_SANDBOX 1
+
+#if defined(CEF_USE_SANDBOX)
+// The cef_sandbox.lib static library may not link successfully with all VS
+// versions.
+#pragma comment(lib, "cef_sandbox.lib")
+#endif
+
+// Entry point function for all processes.
+int APIENTRY wWinMain(HINSTANCE hInstance,
+                      HINSTANCE hPrevInstance,
+                      LPTSTR lpCmdLine,
+                      int nCmdShow) {
+  UNREFERENCED_PARAMETER(hPrevInstance);
+  UNREFERENCED_PARAMETER(lpCmdLine);
+
+  // Enable High-DPI support on Windows 7 or newer.
+  CefEnableHighDPISupport();
+
+  void* sandbox_info = nullptr;
+
+#if defined(CEF_USE_SANDBOX)
+  // Manage the life span of the sandbox information object. This is necessary
+  // for sandbox support on Windows. See cef_sandbox_win.h for complete details.
+  CefScopedSandboxInfo scoped_sandbox;
+  sandbox_info = scoped_sandbox.sandbox_info();
+#endif
+
+  // Provide CEF with command-line arguments.
+  CefMainArgs main_args(hInstance);
+
+  // CEF applications have multiple sub-processes (render, plugin, GPU, etc)
+  // that share the same executable. This function checks the command-line and,
+  // if this is a sub-process, executes the appropriate logic.
+  int exit_code = CefExecuteProcess(main_args, nullptr, sandbox_info);
+  if (exit_code >= 0) {
+    // The sub-process has completed so return here.
+    return exit_code;
+  }
+
+  // Specify CEF global settings here.
+  CefSettings settings;
+
+#if !defined(CEF_USE_SANDBOX)
+  settings.no_sandbox = true;
+#endif
+
+  // SimpleApp implements application-level callbacks for the browser process.
+  // It will create the first browser instance in OnContextInitialized() after
+  // CEF has initialized.
+  CefRefPtr<SimpleApp> app(new SimpleApp);
+
+  // Initialize CEF.
+  CefInitialize(main_args, settings, app.get(), sandbox_info);
+
+  // Run the CEF message loop. This will block until CefQuitMessageLoop() is
+  // called.
+  CefRunMessageLoop();
+
+  // Shut down CEF.
+  CefShutdown();
+
+  return 0;
+}
diff --git a/src/tests/cefsimple/mac/English.lproj/InfoPlist.strings b/src/tests/cefsimple/mac/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..dc5ebb0
--- /dev/null
+++ b/src/tests/cefsimple/mac/English.lproj/InfoPlist.strings
@@ -0,0 +1,3 @@
+/* Localized versions of Info.plist keys */
+
+NSHumanReadableCopyright = "© Chromium Embedded Framework Authors, 2013";
diff --git a/src/tests/cefsimple/mac/English.lproj/MainMenu.xib b/src/tests/cefsimple/mac/English.lproj/MainMenu.xib
new file mode 100644
index 0000000..5d2ceec
--- /dev/null
+++ b/src/tests/cefsimple/mac/English.lproj/MainMenu.xib
@@ -0,0 +1,330 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16B2657" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+    <dependencies>
+        <deployment version="1090" identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application"/>
+        <menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
+            <items>
+                <menuItem title="cefsimple" id="56">
+                    <menu key="submenu" title="TestShell" systemMenu="apple" id="57">
+                        <items>
+                            <menuItem title="About cefsimple" id="58">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="236">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121"/>
+                            <menuItem isSeparatorItem="YES" id="143">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Services" id="131">
+                                <menu key="submenu" title="Services" systemMenu="services" id="130"/>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="144">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Hide cefsimple" keyEquivalent="h" id="134">
+                                <connections>
+                                    <action selector="hide:" target="-1" id="367"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Hide Others" keyEquivalent="h" id="145">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="hideOtherApplications:" target="-1" id="368"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Show All" id="150">
+                                <connections>
+                                    <action selector="unhideAllApplications:" target="-1" id="370"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="149">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Quit cefsimple" keyEquivalent="q" id="136" userLabel="1111">
+                                <connections>
+                                    <action selector="terminate:" target="-1" id="369"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="File" id="83">
+                    <menu key="submenu" title="File" id="81">
+                        <items>
+                            <menuItem title="New" keyEquivalent="n" id="82" userLabel="9">
+                                <connections>
+                                    <action selector="newDocument:" target="-1" id="373"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open…" keyEquivalent="o" id="72">
+                                <connections>
+                                    <action selector="openDocument:" target="-1" id="374"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open Recent" id="124">
+                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
+                                    <items>
+                                        <menuItem title="Clear Menu" id="126">
+                                            <connections>
+                                                <action selector="clearRecentDocuments:" target="-1" id="127"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="79" userLabel="7">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Close" keyEquivalent="w" id="73" userLabel="1">
+                                <connections>
+                                    <action selector="performClose:" target="-1" id="193"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save" keyEquivalent="s" id="75" userLabel="3">
+                                <connections>
+                                    <action selector="saveDocument:" target="-1" id="362"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save As…" keyEquivalent="S" id="80" userLabel="8">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="saveDocumentAs:" target="-1" id="363"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Revert to Saved" id="112" userLabel="10">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="revertDocumentToSaved:" target="-1" id="364"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="74" userLabel="2">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Page Setup..." keyEquivalent="P" id="77" userLabel="5">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="runPageLayout:" target="-1" id="87"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print…" keyEquivalent="p" id="78" userLabel="6">
+                                <connections>
+                                    <action selector="print:" target="-1" id="86"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Edit" id="217">
+                    <menu key="submenu" title="Edit" id="205">
+                        <items>
+                            <menuItem title="Undo" keyEquivalent="z" id="207">
+                                <connections>
+                                    <action selector="undo:" target="-1" id="223"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Redo" keyEquivalent="Z" id="215">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="redo:" target="-1" id="231"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="206">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Cut" keyEquivalent="x" id="199">
+                                <connections>
+                                    <action selector="cut:" target="-1" id="228"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Copy" keyEquivalent="c" id="197">
+                                <connections>
+                                    <action selector="copy:" target="-1" id="224"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste" keyEquivalent="v" id="203">
+                                <connections>
+                                    <action selector="paste:" target="-1" id="226"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Delete" id="202">
+                                <connections>
+                                    <action selector="delete:" target="-1" id="235"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Select All" keyEquivalent="a" id="198">
+                                <connections>
+                                    <action selector="selectAll:" target="-1" id="232"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="214">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Find" id="218">
+                                <menu key="submenu" title="Find" id="220">
+                                    <items>
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="241"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
+                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                        </menuItem>
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="210">
+                                            <connections>
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Spelling and Grammar" id="216">
+                                <menu key="submenu" title="Spelling and Grammar" id="200">
+                                    <items>
+                                        <menuItem title="Show Spelling…" keyEquivalent=":" id="204">
+                                            <connections>
+                                                <action selector="showGuessPanel:" target="-1" id="230"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Spelling" keyEquivalent=";" id="201">
+                                            <connections>
+                                                <action selector="checkSpelling:" target="-1" id="225"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Spelling While Typing" id="219">
+                                            <connections>
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Grammar With Spelling" id="346">
+                                            <connections>
+                                                <action selector="toggleGrammarChecking:" target="-1" id="347"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Substitutions" id="348">
+                                <menu key="submenu" title="Substitutions" id="349">
+                                    <items>
+                                        <menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
+                                            <connections>
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
+                                            <connections>
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
+                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Speech" id="211">
+                                <menu key="submenu" title="Speech" id="212">
+                                    <items>
+                                        <menuItem title="Start Speaking" id="196">
+                                            <connections>
+                                                <action selector="startSpeaking:" target="-1" id="233"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Stop Speaking" id="195">
+                                            <connections>
+                                                <action selector="stopSpeaking:" target="-1" id="227"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Format" id="299">
+                    <menu key="submenu" title="Format" id="300">
+                        <items>
+                            <menuItem title="Show Fonts" keyEquivalent="t" id="344"/>
+                            <menuItem title="Show Colors" keyEquivalent="C" id="345">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="orderFrontColorPanel:" target="-1" id="361"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="View" id="295">
+                    <menu key="submenu" title="View" id="296">
+                        <items>
+                            <menuItem title="Show Toolbar" keyEquivalent="t" id="297">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleToolbarShown:" target="-1" id="366"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Customize Toolbar…" id="298">
+                                <connections>
+                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Window" id="19">
+                    <menu key="submenu" title="Window" systemMenu="window" id="24">
+                        <items>
+                            <menuItem title="Minimize" keyEquivalent="m" id="23">
+                                <connections>
+                                    <action selector="performMiniaturize:" target="-1" id="37"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom" id="239">
+                                <connections>
+                                    <action selector="performZoom:" target="-1" id="240"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="92">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Bring All to Front" id="5">
+                                <connections>
+                                    <action selector="arrangeInFront:" target="-1" id="39"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Help" id="103" userLabel="1">
+                    <menu key="submenu" title="Help" id="106" userLabel="2">
+                        <items>
+                            <menuItem title="cefsimple Help" keyEquivalent="?" id="111">
+                                <connections>
+                                    <action selector="showHelp:" target="-1" id="360"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+            </items>
+        </menu>
+        <userDefaultsController representsSharedInstance="YES" id="389"/>
+    </objects>
+</document>
diff --git a/src/tests/cefsimple/mac/Info.plist b/src/tests/cefsimple/mac/Info.plist
new file mode 100644
index 0000000..102c9b5
--- /dev/null
+++ b/src/tests/cefsimple/mac/Info.plist
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string>cefsimple.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.cef.cefsimple</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>LSEnvironment</key>
+	<dict>
+		<key>MallocNanoZone</key>
+		<string>0</string>
+	</dict>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.9.0</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+	<key>NSSupportsAutomaticGraphicsSwitching</key>
+	<true/>
+</dict>
+</plist>
diff --git a/src/tests/cefsimple/mac/cefsimple.icns b/src/tests/cefsimple/mac/cefsimple.icns
new file mode 100644
index 0000000..f36742d
--- /dev/null
+++ b/src/tests/cefsimple/mac/cefsimple.icns
Binary files differ
diff --git a/src/tests/cefsimple/mac/helper-Info.plist b/src/tests/cefsimple/mac/helper-Info.plist
new file mode 100644
index 0000000..46c1e2b
--- /dev/null
+++ b/src/tests/cefsimple/mac/helper-Info.plist
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleDisplayName</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.cef.cefsimple.helper${BUNDLE_ID_SUFFIX}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>LSEnvironment</key>
+	<dict>
+		<key>MallocNanoZone</key>
+		<string>0</string>
+	</dict>
+	<key>LSFileQuarantineEnabled</key>
+	<true/>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.9.0</string>
+	<key>LSUIElement</key>
+	<string>1</string>
+	<key>NSSupportsAutomaticGraphicsSwitching</key>
+	<true/>
+</dict>
+</plist>
diff --git a/src/tests/cefsimple/process_helper_mac.cc b/src/tests/cefsimple/process_helper_mac.cc
new file mode 100644
index 0000000..224593d
--- /dev/null
+++ b/src/tests/cefsimple/process_helper_mac.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_app.h"
+#include "include/wrapper/cef_library_loader.h"
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
+// use of the sandbox.
+#if defined(CEF_USE_SANDBOX)
+#include "include/cef_sandbox_mac.h"
+#endif
+
+// Entry point function for sub-processes.
+int main(int argc, char* argv[]) {
+#if defined(CEF_USE_SANDBOX)
+  // Initialize the macOS sandbox for this helper process.
+  CefScopedSandboxContext sandbox_context;
+  if (!sandbox_context.Initialize(argc, argv))
+    return 1;
+#endif
+
+  // Load the CEF framework library at runtime instead of linking directly
+  // as required by the macOS sandbox implementation.
+  CefScopedLibraryLoader library_loader;
+  if (!library_loader.LoadInHelper())
+    return 1;
+
+  // Provide CEF with command-line arguments.
+  CefMainArgs main_args(argc, argv);
+
+  // Execute the sub-process.
+  return CefExecuteProcess(main_args, nullptr, nullptr);
+}
diff --git a/src/tests/cefsimple/res/cefsimple.ico b/src/tests/cefsimple/res/cefsimple.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/src/tests/cefsimple/res/cefsimple.ico
Binary files differ
diff --git a/src/tests/cefsimple/res/small.ico b/src/tests/cefsimple/res/small.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/src/tests/cefsimple/res/small.ico
Binary files differ
diff --git a/src/tests/cefsimple/resource.h b/src/tests/cefsimple/resource.h
new file mode 100644
index 0000000..ba1f3a9
--- /dev/null
+++ b/src/tests/cefsimple/resource.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by cefsimple.rc
+//
+
+#define IDI_CEFSIMPLE 100
+#define IDI_SMALL 101
+
+// Avoid files associated with MacOS
+#define _X86_
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 32700
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif
diff --git a/src/tests/cefsimple/simple_app.cc b/src/tests/cefsimple/simple_app.cc
new file mode 100644
index 0000000..baa20d1
--- /dev/null
+++ b/src/tests/cefsimple/simple_app.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefsimple/simple_app.h"
+
+#include <string>
+
+#include "include/cef_browser.h"
+#include "include/cef_command_line.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_window.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/cefsimple/simple_handler.h"
+
+namespace {
+
+// When using the Views framework this object provides the delegate
+// implementation for the CefWindow that hosts the Views-based browser.
+class SimpleWindowDelegate : public CefWindowDelegate {
+ public:
+  explicit SimpleWindowDelegate(CefRefPtr<CefBrowserView> browser_view)
+      : browser_view_(browser_view) {}
+
+  void OnWindowCreated(CefRefPtr<CefWindow> window) OVERRIDE {
+    // Add the browser view and show the window.
+    window->AddChildView(browser_view_);
+    window->Show();
+
+    // Give keyboard focus to the browser view.
+    browser_view_->RequestFocus();
+  }
+
+  void OnWindowDestroyed(CefRefPtr<CefWindow> window) OVERRIDE {
+    browser_view_ = nullptr;
+  }
+
+  bool CanClose(CefRefPtr<CefWindow> window) OVERRIDE {
+    // Allow the window to close if the browser says it's OK.
+    CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
+    if (browser)
+      return browser->GetHost()->TryCloseBrowser();
+    return true;
+  }
+
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) OVERRIDE {
+    return CefSize(800, 600);
+  }
+
+ private:
+  CefRefPtr<CefBrowserView> browser_view_;
+
+  IMPLEMENT_REFCOUNTING(SimpleWindowDelegate);
+  DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate);
+};
+
+class SimpleBrowserViewDelegate : public CefBrowserViewDelegate {
+ public:
+  SimpleBrowserViewDelegate() {}
+
+  bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
+                                 CefRefPtr<CefBrowserView> popup_browser_view,
+                                 bool is_devtools) OVERRIDE {
+    // Create a new top-level Window for the popup. It will show itself after
+    // creation.
+    CefWindow::CreateTopLevelWindow(
+        new SimpleWindowDelegate(popup_browser_view));
+
+    // We created the Window.
+    return true;
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(SimpleBrowserViewDelegate);
+  DISALLOW_COPY_AND_ASSIGN(SimpleBrowserViewDelegate);
+};
+
+}  // namespace
+
+SimpleApp::SimpleApp() {}
+
+void SimpleApp::OnContextInitialized() {
+  CEF_REQUIRE_UI_THREAD();
+
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+  // Create the browser using the Views framework if "--use-views" is specified
+  // via the command-line. Otherwise, create the browser using the native
+  // platform framework. The Views framework is currently only supported on
+  // Windows and Linux.
+  const bool use_views = command_line->HasSwitch("use-views");
+#else
+  const bool use_views = false;
+#endif
+
+  // SimpleHandler implements browser-level callbacks.
+  CefRefPtr<SimpleHandler> handler(new SimpleHandler(use_views));
+
+  // Specify CEF browser settings here.
+  CefBrowserSettings browser_settings;
+
+  std::string url;
+
+  // Check if a "--url=" value was provided via the command-line. If so, use
+  // that instead of the default URL.
+  url = command_line->GetSwitchValue("url");
+  if (url.empty())
+    url = "http://www.google.com";
+
+  if (use_views) {
+    // Create the BrowserView.
+    CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
+        handler, url, browser_settings, nullptr, nullptr,
+        new SimpleBrowserViewDelegate());
+
+    // Create the Window. It will show itself after creation.
+    CefWindow::CreateTopLevelWindow(new SimpleWindowDelegate(browser_view));
+  } else {
+    // Information used when creating the native window.
+    CefWindowInfo window_info;
+
+#if defined(OS_WIN)
+    // On Windows we need to specify certain flags that will be passed to
+    // CreateWindowEx().
+    window_info.SetAsPopup(NULL, "cefsimple");
+#endif
+
+    // Create the first browser window.
+    CefBrowserHost::CreateBrowser(window_info, handler, url, browser_settings,
+                                  nullptr, nullptr);
+  }
+}
diff --git a/src/tests/cefsimple/simple_app.h b/src/tests/cefsimple/simple_app.h
new file mode 100644
index 0000000..8ed9969
--- /dev/null
+++ b/src/tests/cefsimple/simple_app.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
+#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
+
+#include "include/cef_app.h"
+
+// Implement application-level callbacks for the browser process.
+class SimpleApp : public CefApp, public CefBrowserProcessHandler {
+ public:
+  SimpleApp();
+
+  // CefApp methods:
+  virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
+      OVERRIDE {
+    return this;
+  }
+
+  // CefBrowserProcessHandler methods:
+  virtual void OnContextInitialized() OVERRIDE;
+
+ private:
+  // Include the default reference counting implementation.
+  IMPLEMENT_REFCOUNTING(SimpleApp);
+};
+
+#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
diff --git a/src/tests/cefsimple/simple_handler.cc b/src/tests/cefsimple/simple_handler.cc
new file mode 100644
index 0000000..fafedc8
--- /dev/null
+++ b/src/tests/cefsimple/simple_handler.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefsimple/simple_handler.h"
+
+#include <sstream>
+#include <string>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_app.h"
+#include "include/cef_parser.h"
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_window.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+
+namespace {
+
+SimpleHandler* g_instance = nullptr;
+
+// Returns a data: URI with the specified contents.
+std::string GetDataURI(const std::string& data, const std::string& mime_type) {
+  return "data:" + mime_type + ";base64," +
+         CefURIEncode(CefBase64Encode(data.data(), data.size()), false)
+             .ToString();
+}
+
+}  // namespace
+
+SimpleHandler::SimpleHandler(bool use_views)
+    : use_views_(use_views), is_closing_(false) {
+  DCHECK(!g_instance);
+  g_instance = this;
+}
+
+SimpleHandler::~SimpleHandler() {
+  g_instance = nullptr;
+}
+
+// static
+SimpleHandler* SimpleHandler::GetInstance() {
+  return g_instance;
+}
+
+void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
+                                  const CefString& title) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (use_views_) {
+    // Set the title of the window using the Views framework.
+    CefRefPtr<CefBrowserView> browser_view =
+        CefBrowserView::GetForBrowser(browser);
+    if (browser_view) {
+      CefRefPtr<CefWindow> window = browser_view->GetWindow();
+      if (window)
+        window->SetTitle(title);
+    }
+  } else {
+    // Set the title of the window using platform APIs.
+    PlatformTitleChange(browser, title);
+  }
+}
+
+void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Add to the list of existing browsers.
+  browser_list_.push_back(browser);
+}
+
+bool SimpleHandler::DoClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Closing the main window requires special handling. See the DoClose()
+  // documentation in the CEF header for a detailed destription of this
+  // process.
+  if (browser_list_.size() == 1) {
+    // Set a flag to indicate that the window close should be allowed.
+    is_closing_ = true;
+  }
+
+  // Allow the close. For windowed browsers this will result in the OS close
+  // event being sent.
+  return false;
+}
+
+void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Remove from the list of existing browsers.
+  BrowserList::iterator bit = browser_list_.begin();
+  for (; bit != browser_list_.end(); ++bit) {
+    if ((*bit)->IsSame(browser)) {
+      browser_list_.erase(bit);
+      break;
+    }
+  }
+
+  if (browser_list_.empty()) {
+    // All browser windows have closed. Quit the application message loop.
+    CefQuitMessageLoop();
+  }
+}
+
+void SimpleHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                ErrorCode errorCode,
+                                const CefString& errorText,
+                                const CefString& failedUrl) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Don't display an error for downloaded files.
+  if (errorCode == ERR_ABORTED)
+    return;
+
+  // Display a load error message using a data: URI.
+  std::stringstream ss;
+  ss << "<html><body bgcolor=\"white\">"
+        "<h2>Failed to load URL "
+     << std::string(failedUrl) << " with error " << std::string(errorText)
+     << " (" << errorCode << ").</h2></body></html>";
+
+  frame->LoadURL(GetDataURI(ss.str(), "text/html"));
+}
+
+void SimpleHandler::CloseAllBrowsers(bool force_close) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&SimpleHandler::CloseAllBrowsers, this,
+                                   force_close));
+    return;
+  }
+
+  if (browser_list_.empty())
+    return;
+
+  BrowserList::const_iterator it = browser_list_.begin();
+  for (; it != browser_list_.end(); ++it)
+    (*it)->GetHost()->CloseBrowser(force_close);
+}
diff --git a/src/tests/cefsimple/simple_handler.h b/src/tests/cefsimple/simple_handler.h
new file mode 100644
index 0000000..10194f0
--- /dev/null
+++ b/src/tests/cefsimple/simple_handler.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
+#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
+
+#include "include/cef_client.h"
+
+#include <list>
+
+class SimpleHandler : public CefClient,
+                      public CefDisplayHandler,
+                      public CefLifeSpanHandler,
+                      public CefLoadHandler {
+ public:
+  explicit SimpleHandler(bool use_views);
+  ~SimpleHandler();
+
+  // Provide access to the single global instance of this object.
+  static SimpleHandler* GetInstance();
+
+  // CefClient methods:
+  virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE {
+    return this;
+  }
+  virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE {
+    return this;
+  }
+  virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE { return this; }
+
+  // CefDisplayHandler methods:
+  virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
+                             const CefString& title) OVERRIDE;
+
+  // CefLifeSpanHandler methods:
+  virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+  // CefLoadHandler methods:
+  virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           ErrorCode errorCode,
+                           const CefString& errorText,
+                           const CefString& failedUrl) OVERRIDE;
+
+  // Request that all existing browser windows close.
+  void CloseAllBrowsers(bool force_close);
+
+  bool IsClosing() const { return is_closing_; }
+
+ private:
+  // Platform-specific implementation.
+  void PlatformTitleChange(CefRefPtr<CefBrowser> browser,
+                           const CefString& title);
+
+  // True if the application is using the Views framework.
+  const bool use_views_;
+
+  // List of existing browser windows. Only accessed on the CEF UI thread.
+  typedef std::list<CefRefPtr<CefBrowser>> BrowserList;
+  BrowserList browser_list_;
+
+  bool is_closing_;
+
+  // Include the default reference counting implementation.
+  IMPLEMENT_REFCOUNTING(SimpleHandler);
+};
+
+#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
diff --git a/src/tests/cefsimple/simple_handler_linux.cc b/src/tests/cefsimple/simple_handler_linux.cc
new file mode 100644
index 0000000..239791c
--- /dev/null
+++ b/src/tests/cefsimple/simple_handler_linux.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefsimple/simple_handler.h"
+
+#if defined(CEF_X11)
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#endif
+
+#include <string>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_browser.h"
+
+void SimpleHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser,
+                                        const CefString& title) {
+  std::string titleStr(title);
+
+#if defined(CEF_X11)
+  // Retrieve the X11 display shared with Chromium.
+  ::Display* display = cef_get_xdisplay();
+  DCHECK(display);
+
+  // Retrieve the X11 window handle for the browser.
+  ::Window window = browser->GetHost()->GetWindowHandle();
+  DCHECK(window != kNullWindowHandle);
+
+  // Retrieve the atoms required by the below XChangeProperty call.
+  const char* kAtoms[] = {"_NET_WM_NAME", "UTF8_STRING"};
+  Atom atoms[2];
+  int result =
+      XInternAtoms(display, const_cast<char**>(kAtoms), 2, false, atoms);
+  if (!result)
+    NOTREACHED();
+
+  // Set the window title.
+  XChangeProperty(display, window, atoms[0], atoms[1], 8, PropModeReplace,
+                  reinterpret_cast<const unsigned char*>(titleStr.c_str()),
+                  titleStr.size());
+
+  // TODO(erg): This is technically wrong. So XStoreName and friends expect
+  // this in Host Portable Character Encoding instead of UTF-8, which I believe
+  // is Compound Text. This shouldn't matter 90% of the time since this is the
+  // fallback to the UTF8 property above.
+  XStoreName(display, browser->GetHost()->GetWindowHandle(), titleStr.c_str());
+#endif  // defined(CEF_X11)
+}
diff --git a/src/tests/cefsimple/simple_handler_mac.mm b/src/tests/cefsimple/simple_handler_mac.mm
new file mode 100644
index 0000000..5a66662
--- /dev/null
+++ b/src/tests/cefsimple/simple_handler_mac.mm
@@ -0,0 +1,19 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefsimple/simple_handler.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "include/cef_browser.h"
+
+void SimpleHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser,
+                                        const CefString& title) {
+  NSView* view =
+      CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetHost()->GetWindowHandle());
+  NSWindow* window = [view window];
+  std::string titleStr(title);
+  NSString* str = [NSString stringWithUTF8String:titleStr.c_str()];
+  [window setTitle:str];
+}
diff --git a/src/tests/cefsimple/simple_handler_win.cc b/src/tests/cefsimple/simple_handler_win.cc
new file mode 100644
index 0000000..23f0533
--- /dev/null
+++ b/src/tests/cefsimple/simple_handler_win.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefsimple/simple_handler.h"
+
+#include <windows.h>
+#include <string>
+
+#include "include/cef_browser.h"
+
+void SimpleHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser,
+                                        const CefString& title) {
+  CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle();
+  SetWindowText(hwnd, std::wstring(title).c_str());
+}
diff --git a/src/tests/ceftests/CMakeLists.txt.in b/src/tests/ceftests/CMakeLists.txt.in
new file mode 100644
index 0000000..cdda2b7
--- /dev/null
+++ b/src/tests/ceftests/CMakeLists.txt.in
@@ -0,0 +1,230 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+#
+# Source files.
+#
+
+# ceftests sources.
+{{
+  'prefix': 'ceftests',
+  'set': 'UNITTESTS_SRCS',
+  'includes': [
+    'shared_sources_browser',
+    'shared_sources_common',
+    'shared_sources_linux:LINUX',
+    'shared_sources_mac:MACOSX',
+    'shared_sources_renderer:WINDOWS',
+    'shared_sources_renderer:LINUX',
+    'shared_sources_win:WINDOWS',
+    'ceftests_sources_common',
+    'ceftests_sources_linux:LINUX',
+    'ceftests_sources_mac:MACOSX',
+    'ceftests_sources_views:WINDOWS',
+    'ceftests_sources_views:LINUX',
+    'ceftests_sources_win:WINDOWS',
+  ],
+}}
+
+# ceftests helper sources.
+{{
+  'prefix': 'ceftests_helper',
+  'set': 'UNITTESTS_HELPER_SRCS',
+  'includes': [
+    'shared_sources_common',
+    'shared_sources_mac_helper:MACOSX',
+    'shared_sources_renderer',
+    'ceftests_sources_mac_helper:MACOSX',
+  ],
+}}
+
+# ceftests resources.
+{{
+  'prefix': 'ceftests_resources',
+  'set': 'UNITTESTS_RESOURCES_SRCS',
+  'includes': [
+    'shared_sources_resources',
+    'ceftests_bundle_resources_mac:MACOSX',
+  ],
+}}
+
+
+#
+# Shared configuration.
+#
+
+# Target executable names.
+set(CEF_TARGET "ceftests")
+if(OS_MACOSX)
+  set(CEF_HELPER_TARGET "ceftests_Helper")
+  set(CEF_HELPER_OUTPUT_NAME "ceftests Helper")
+else()
+  # Logical target used to link the libcef library.
+  ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}")
+endif()
+
+# Determine the target output directory.
+SET_CEF_TARGET_OUT_DIR()
+
+
+#
+# Linux configuration.
+#
+
+if(OS_LINUX)
+  # Find required libraries and update compiler/linker variables.
+  FIND_LINUX_LIBRARIES("glib-2.0")
+
+  # Executable target.
+  add_executable(${CEF_TARGET} ${UNITTESTS_SRCS} ${UNITTESTS_RESOURCES_SRCS})
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper cef_gtest)
+  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper cef_gtest ${CEF_STANDARD_LIBS})
+
+  # Set rpath so that libraries can be placed next to the executable.
+  set_target_properties(${CEF_TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN")
+  set_target_properties(${CEF_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
+  set_target_properties(${CEF_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})
+
+  # Copy binary and resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
+  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
+
+  # Copy ceftests resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${UNITTESTS_RESOURCES_SRCS}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_TARGET_OUT_DIR}/ceftests_files")
+
+  # Set SUID permissions on the chrome-sandbox target.
+  SET_LINUX_SUID_PERMISSIONS("${CEF_TARGET}" "${CEF_TARGET_OUT_DIR}/chrome-sandbox")
+endif()
+
+
+#
+# Mac OS X configuration.
+#
+
+if(OS_MACOSX)
+  # Output path for the main app bundle.
+  set(CEF_APP "${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.app")
+
+  # Variables referenced from the main Info.plist file.
+  set(EXECUTABLE_NAME "${CEF_TARGET}")
+  set(PRODUCT_NAME "${CEF_TARGET}")
+
+  if(USE_SANDBOX)
+    # Logical target used to link the cef_sandbox library.
+    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
+  endif()
+
+  # Main app bundle target.
+  add_executable(${CEF_TARGET} MACOSX_BUNDLE ${UNITTESTS_RESOURCES_SRCS} ${UNITTESTS_SRCS})
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper cef_gtest)
+  target_link_libraries(${CEF_TARGET} libcef_dll_wrapper cef_gtest ${CEF_STANDARD_LIBS})
+  set_target_properties(${CEF_TARGET} PROPERTIES
+    MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/Info.plist
+    )
+
+  # Copy the CEF framework into the Frameworks directory.
+  add_custom_command(
+    TARGET ${CEF_TARGET}
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy_directory
+            "${CEF_BINARY_DIR}/Chromium Embedded Framework.framework"
+            "${CEF_APP}/Contents/Frameworks/Chromium Embedded Framework.framework"
+    VERBATIM
+    )
+
+  # Create the multiple Helper app bundle targets.
+  foreach(_suffix_list ${CEF_HELPER_APP_SUFFIXES})
+    # Convert to a list and extract the suffix values.
+    string(REPLACE ":" ";" _suffix_list ${_suffix_list})
+    list(GET _suffix_list 0 _name_suffix)
+    list(GET _suffix_list 1 _target_suffix)
+    list(GET _suffix_list 2 _plist_suffix)
+
+    # Define Helper target and output names.
+    set(_helper_target "${CEF_HELPER_TARGET}${_target_suffix}")
+    set(_helper_output_name "${CEF_HELPER_OUTPUT_NAME}${_name_suffix}")
+
+    # Create Helper-specific variants of the helper-Info.plist file. Do this
+    # manually because the configure_file command (which is executed as part of
+    # MACOSX_BUNDLE_INFO_PLIST) uses global env variables and would insert the
+    # wrong values with multiple targets.
+    set(_helper_info_plist "${CMAKE_CURRENT_BINARY_DIR}/helper-Info${_target_suffix}.plist")
+    file(READ "${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/helper-Info.plist" _plist_contents)
+    string(REPLACE "\${EXECUTABLE_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
+    string(REPLACE "\${PRODUCT_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
+    string(REPLACE "\${BUNDLE_ID_SUFFIX}" "${_plist_suffix}" _plist_contents ${_plist_contents})
+    file(WRITE ${_helper_info_plist} ${_plist_contents})
+
+    # Create Helper executable target.
+    add_executable(${_helper_target} MACOSX_BUNDLE ${UNITTESTS_HELPER_SRCS})
+    SET_EXECUTABLE_TARGET_PROPERTIES(${_helper_target})
+    add_dependencies(${_helper_target} libcef_dll_wrapper cef_gtest)
+    target_link_libraries(${_helper_target} libcef_dll_wrapper cef_gtest ${CEF_STANDARD_LIBS})
+    set_target_properties(${_helper_target} PROPERTIES
+      MACOSX_BUNDLE_INFO_PLIST ${_helper_info_plist}
+      OUTPUT_NAME ${_helper_output_name}
+      )
+
+    if(USE_SANDBOX)
+      target_link_libraries(${_helper_target} cef_sandbox_lib)
+    endif()
+
+    # Add the Helper as a dependency of the main executable target.
+    add_dependencies(${CEF_TARGET} "${_helper_target}")
+
+    # Copy the Helper app bundle into the Frameworks directory.
+    add_custom_command(
+      TARGET ${CEF_TARGET}
+      POST_BUILD
+      COMMAND ${CMAKE_COMMAND} -E copy_directory
+              "${CEF_TARGET_OUT_DIR}/${_helper_output_name}.app"
+              "${CEF_APP}/Contents/Frameworks/${_helper_output_name}.app"
+      VERBATIM
+      )
+  endforeach()
+
+  # Manually process and copy over resource files.
+  # The Xcode generator can support this via the set_target_properties RESOURCE
+  # directive but that doesn't properly handle nested resource directories.
+  # Remove these prefixes from input file paths.
+  set(PREFIXES
+    "resources/mac/"
+    "../shared/resources/"
+    )
+  COPY_MACOSX_RESOURCES("${UNITTESTS_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
+endif()
+
+
+#
+# Windows configuration.
+#
+
+if(OS_WINDOWS)
+  # Executable target.
+  add_executable(${CEF_TARGET} WIN32 ${UNITTESTS_SRCS} ${UNITTESTS_RESOURCES_SRCS})
+  add_dependencies(${CEF_TARGET} libcef_dll_wrapper cef_gtest)
+
+  list(APPEND CEF_EXE_LINKER_FLAGS
+    /SUBSYSTEM:CONSOLE  # Configure as a console application.
+  )
+
+  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
+  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper cef_gtest ${CEF_STANDARD_LIBS})
+
+  if(USE_SANDBOX)
+    # Logical target used to link the cef_sandbox library.
+    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
+    target_link_libraries(${CEF_TARGET} cef_sandbox_lib ${CEF_SANDBOX_STANDARD_LIBS})
+  endif()
+
+  # Add the custom manifest files to the executable.
+  ADD_WINDOWS_MANIFEST("${CMAKE_CURRENT_SOURCE_DIR}/resources/win" "${CEF_TARGET}" "exe")
+
+  # Copy CEF binary and resource files to the target output directory.
+  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
+  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
+endif()
+
diff --git a/src/tests/ceftests/audio_output_unittest.cc b/src/tests/ceftests/audio_output_unittest.cc
new file mode 100644
index 0000000..8d134b6
--- /dev/null
+++ b/src/tests/ceftests/audio_output_unittest.cc
@@ -0,0 +1,1074 @@
+// Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+
+using client::ClientAppBrowser;
+
+// Taken from:
+// http://www.iandevlin.com/blog/2012/09/html5/html5-media-and-data-uri/
+#define AUDIO_DATA                                                             \
+  "data:audio/"                                                                \
+  "ogg;base64,T2dnUwACAAAAAAAAAAA+"                                            \
+  "HAAAAAAAAGyawCEBQGZpc2hlYWQAAwAAAAAAAAAAAAAA6AMAAAAAAAAAAAAAAAAAAOgDAAAAAA" \
+  "AAAAAAAAAAAAAAAAAAAAAAAAAAAABPZ2dTAAIAAAAAAAAAAINDAAAAAAAA9LkergEeAXZvcmJp" \
+  "cwAAAAACRKwAAAAAAAAA7gIAAAAAALgBT2dnUwAAAAAAAAAAAAA+"                       \
+  "HAAAAQAAAPvOJxcBUGZpc2JvbmUALAAAAINDAAADAAAARKwAAAAAAAABAAAAAAAAAAAAAAAAAA" \
+  "AAAgAAAAAAAABDb250ZW50LVR5cGU6IGF1ZGlvL3ZvcmJpcw0KT2dnUwAAAAAAAAAAAACDQwAA" \
+  "AQAAAGLSAC4Qdv//////////////////"                                           \
+  "cQN2b3JiaXMdAAAAWGlwaC5PcmcgbGliVm9yYmlzIEkgMjAwOTA3MDkCAAAAIwAAAEVOQ09ERV" \
+  "I9ZmZtcGVnMnRoZW9yYS0wLjI2K3N2bjE2OTI0HgAAAFNPVVJDRV9PU0hBU0g9ODExM2FhYWI5" \
+  "YzFiNjhhNwEFdm9yYmlzK0JDVgEACAAAADFMIMWA0JBVAAAQAABgJCkOk2ZJKaWUoSh5mJRISS" \
+  "mllMUwiZiUicUYY4wxxhhjjDHGGGOMIDRkFQAABACAKAmOo+"                           \
+  "ZJas45ZxgnjnKgOWlOOKcgB4pR4DkJwvUmY26mtKZrbs4pJQgNWQUAAAIAQEghhRRSSCGFFGKI" \
+  "IYYYYoghhxxyyCGnnHIKKqigggoyyCCDTDLppJNOOumoo4466ii00EILLbTSSkwx1VZjrr0GXX" \
+  "xzzjnnnHPOOeecc84JQkNWAQAgAAAEQgYZZBBCCCGFFFKIKaaYcgoyyIDQkFUAACAAgAAAAABH" \
+  "kRRJsRTLsRzN0SRP8ixREzXRM0VTVE1VVVVVdV1XdmXXdnXXdn1ZmIVbuH1ZuIVb2IVd94VhGI" \
+  "ZhGIZhGIZh+"                                                                \
+  "H3f933f930gNGQVACABAKAjOZbjKaIiGqLiOaIDhIasAgBkAAAEACAJkiIpkqNJpmZqrmmbtmi" \
+  "rtm3LsizLsgyEhqwCAAABAAQAAAAAAKBpmqZpmqZpmqZpmqZpmqZpmqZpmmZZlmVZlmVZlmVZl" \
+  "mVZlmVZlmVZlmVZlmVZlmVZlmVZlmVZlmVZQGjIKgBAAgBAx3Ecx3EkRVIkx3IsBwgNWQUAyAA" \
+  "ACABAUizFcjRHczTHczzHczxHdETJlEzN9EwPCA1ZBQAAAgAIAAAAAABAMRzFcRzJ0SRPUi3Tc" \
+  "jVXcz3Xc03XdV1XVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVYHQkFUAAAQAACG" \
+  "dZpZqgAgzkGEgNGQVAIAAAAAYoQhDDAgNWQUAAAQAAIih5CCa0JrzzTkOmuWgqRSb08GJVJsnu" \
+  "amYm3POOeecbM4Z45xzzinKmcWgmdCac85JDJqloJnQmnPOeRKbB62p0ppzzhnnnA7GGWGcc85" \
+  "p0poHqdlYm3POWdCa5qi5FJtzzomUmye1uVSbc84555xzzjnnnHPOqV6czsE54Zxzzonam2u5C" \
+  "V2cc875ZJzuzQnhnHPOOeecc84555xzzglCQ1YBAEAAAARh2BjGnYIgfY4GYhQhpiGTHnSPDpO" \
+  "gMcgppB6NjkZKqYNQUhknpXSC0JBVAAAgAACEEFJIIYUUUkghhRRSSCGGGGKIIaeccgoqqKSSi" \
+  "irKKLPMMssss8wyy6zDzjrrsMMQQwwxtNJKLDXVVmONteaec645SGultdZaK6WUUkoppSA0ZBU" \
+  "AAAIAQCBkkEEGGYUUUkghhphyyimnoIIKCA1ZBQAAAgAIAAAA8CTPER3RER3RER3RER3RER3P8" \
+  "RxREiVREiXRMi1TMz1VVFVXdm1Zl3Xbt4Vd2HXf133f141fF4ZlWZZlWZZlWZZlWZZlWZZlCUJ" \
+  "DVgEAIAAAAEIIIYQUUkghhZRijDHHnINOQgmB0JBVAAAgAIAAAAAAR3EUx5EcyZEkS7IkTdIsz" \
+  "fI0T/M00RNFUTRNUxVd0RV10xZlUzZd0zVl01Vl1XZl2bZlW7d9WbZ93/d93/d93/d93/"      \
+  "d939d1IDRkFQAgAQCgIzmSIimSIjmO40iSBISGrAIAZAAABACgKI7iOI4jSZIkWZImeZZniZqp" \
+  "mZ7pqaIKhIasAgAAAQAEAAAAAACgaIqnmIqniIrniI4oiZZpiZqquaJsyq7ruq7ruq7ruq7ruq" \
+  "7ruq7ruq7ruq7ruq7ruq7ruq7ruq7rukBoyCoAQAIAQEdyJEdyJEVSJEVyJAcIDVkFAMgAAAgA" \
+  "wDEcQ1Ikx7IsTfM0T/"                                                         \
+  "M00RM90TM9VXRFFwgNWQUAAAIACAAAAAAAwJAMS7EczdEkUVIt1VI11VItVVQ9VVVVVVVVVVVV" \
+  "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV1TRN0zSB0JCVAAAZAAACKcWahFCSQU5K7EVpxiAHrQ" \
+  "blKYQYk9iL6ZhCyFFQKmQMGeRAydQxhhDzYmOnFELMi/"                               \
+  "Glc4xBL8a4UkIowQhCQ1YEAFEAAAZJIkkkSfI0okj0JM0jijwRgCR6PI/"                  \
+  "nSZ7I83geAEkUeR7Pk0SR5/"                                                    \
+  "E8AQAAAQ4AAAEWQqEhKwKAOAEAiyR5HknyPJLkeTRNFCGKkqaJIs8zTZ5mikxTVaGqkqaJIs8z" \
+  "TZonmkxTVaGqniiqKlV1XarpumTbtmHLniiqKlV1XabqumzZtiHbAAAAJE9TTZpmmjTNNImiak" \
+  "JVJc0zVZpmmjTNNImiqUJVPVN0XabpukzTdbmuLEOWPdF0XaapukzTdbmuLEOWAQAASJ6nqjTN" \
+  "NGmaaRJFU4VqSp6nqjTNNGmaaRJFVYWpeqbpukzTdZmm63JlWYYte6bpukzTdZmm65JdWYYsAw" \
+  "AA0EzTlomi7BJF12WargvX1UxTtomiKxNF12WargvXFVXVlqmmLVNVWea6sgxZFlVVtpmqbFNV" \
+  "Wea6sgxZBgAAAAAAAAAAgKiqtk1VZZlqyjLXlWXIsqiqtk1VZZmpyjLXtWXIsgAAgAEHAIAAE8" \
+  "pAoSErAYAoAACH4liWpokix7EsTRNNjmNZmmaKJEnTPM80oVmeZ5rQNFFUVWiaKKoqAAACAAAK" \
+  "HAAAAmzQlFgcoNCQlQBASACAw3EsS9M8z/"                                         \
+  "NEUTRNk+"                                                                   \
+  "NYlueJoiiapmmqKsexLM8TRVE0TdNUVZalaZ4niqJomqqqqtA0zxNFUTRNVVVVaJoomqZpqqqq" \
+  "ui40TRRN0zRVVVVdF5rmeaJomqrquq4LPE8UTVNVXdd1AQAAAAAAAAAAAAAAAAAAAAAEAAAcOA" \
+  "AABBhBJxlVFmGjCRcegEJDVgQAUQAAgDGIMcWYUQpCKSU0SkEJJZQKQmmppJRJSK211jIpqbXW" \
+  "WiWltJZay6Ck1lprmYTWWmutAACwAwcAsAMLodCQlQBAHgAAgoxSjDnnHDVGKcacc44aoxRjzj" \
+  "lHlVLKOecgpJQqxZxzDlJKGXPOOecopYw555xzlFLnnHPOOUqplM455xylVErnnHOOUiolY845" \
+  "JwAAqMABACDARpHNCUaCCg1ZCQCkAgAYHMeyPM/"                                    \
+  "zTNE0LUnSNFEURdNUVUuSNE0UTVE1VZVlaZoomqaqui5N0zRRNE1VdV2q6nmmqaqu67pUV/"    \
+  "RMU1VdV5YBAAAAAAAAAAAAAQDgCQ4AQAU2rI5wUjQWWGjISgAgAwAAMQYhZAxCyBiEFEIIKaUQ" \
+  "EgAAMOAAABBgQhkoNGQlAJAKAAAYo5RzzklJpUKIMecglNJShRBjzkEopaWoMcYglJJSa1FjjE" \
+  "EoJaXWomshlJJSSq1F10IoJaXWWotSqlRKaq3FGKVUqZTWWosxSqlzSq3FGGOUUveUWoux1iil" \
+  "dDLGGGOtzTnnZIwxxloLAEBocAAAO7BhdYSTorHAQkNWAgB5AAAIQkoxxhhjECGlGGPMMYeQUo" \
+  "wxxhhUijHGHGMOQsgYY4wxByFkjDHnnIMQMsYYY85BCJ1zjjHnIITQOceYcxBC55xjzDkIoXOM" \
+  "MeacAACgAgcAgAAbRTYnGAkqNGQlABAOAAAYw5hzjDkGnYQKIecgdA5CKqlUCDkHoXMQSkmpeA" \
+  "46KSGUUkoqxXMQSgmhlJRaKy6GUkoopaTUUpExhFJKKSWl1ooxpoSQUkqptVaMMaGEVFJKKbZi" \
+  "jI2lpNRaa60VY2wsJZXWWmutGGOMaym1FmOsxRhjXEuppRhrLMYY43tqLcZYYzHGGJ9baimmXA" \
+  "sAMHlwAIBKsHGGlaSzwtHgQkNWAgC5AQAIQkoxxphjzjnnnHPOSaUYc8455yCEEEIIIZRKMeac" \
+  "c85BByGEEEIoGXPOOQchhBBCCCGEUFLqmHMOQgghhBBCCCGl1DnnIIQQQgghhBBCSqlzzkEIIY" \
+  "QQQgghhJRSCCGEEEIIIYQQQggppZRCCCGEEEIIIZQSUkophRBCCCWEEkoIJaSUUgohhBBCKaWE" \
+  "UkJJKaUUQgillFBKKaGUkFJKKaUQQiillFBKKSWllFJKJZRSSikllFBKSimllEoooZRQSimllJ" \
+  "RSSimVUkopJZRSSgkppZRSSqmUUkoppZRSUkoppZRSKaWUUkoppaSUUkoppVJKKaWUEkpJKaWU" \
+  "UkqllFBKKaWUUlJKKaWUSgqllFJKKaUAAKADBwCAACMqLcROM648AkcUMkxAhYasBABSAQAAQi" \
+  "illFJKKTWMUUoppZRSihyklFJKKaWUUkoppZRSSimVUkoppZRSSimllFJKKaWUUkoppZRSSiml" \
+  "lFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKAcDdFw6APh" \
+  "M2rI5wUjQWWGjISgAgFQAAMIYxxphyzjmllHPOOQadlEgp5yB0TkopPYQQQgidhJR6ByGEEEIp" \
+  "KfUYQyghlJRS67GGTjoIpbTUaw8hhJRaaqn3HjKoKKWSUu89tVBSainG3ntLJbPSWmu9595LKi" \
+  "nG2nrvObeSUkwtFgBgEuEAgLhgw+"                                               \
+  "oIJ0VjgYWGrAIAYgAACEMMQkgppZRSSinGGGOMMcYYY4wxxhhjjDHGGGOMMQEAgAkOAAABVrAr" \
+  "s7Rqo7ipk7zog8AndMRmZMilVMzkRNAjNdRiJdihFdzgBWChISsBADIAAMRRrDXGXitiGISSai" \
+  "wNQYxBibllxijlJObWKaWUk1hTyJRSzFmKJXRMKUYpphJCxpSkGGOMKXTSWs49t1RKCwAAgCAA" \
+  "wECEzAQCBVBgIAMADhASpACAwgJDx3AREJBLyCgwKBwTzkmnDQBAECIzRCJiMUhMqAaKiukAYH" \
+  "GBIR8AMjQ20i4uoMsAF3Rx14EQghCEIBYHUEACDk644Yk3POEGJ+"                       \
+  "gUlToQAAAAAAAIAHgAAEg2gIhoZuY4Ojw+"                                         \
+  "QEJERkhKTE5QUlQEAAAAAAAQAD4AAJIVICKamTmODo8PkBCREZISkxOUFJUAAEAAAQAAAAAQQA" \
+  "ACAgIAAAAAAAEAAAACAk9nZ1MABAAAAAAAAAAAPhwAAAIAAADItsciAQBPZ2dTAABAKgAAAAAA" \
+  "AINDAAACAAAAi/k29xgB/4b/av9h/0j/Wv9g/1r/UP9l/1//"                           \
+  "Wv8A2jWsrb6NXUc1CJ0sSdewtPbGlo1NaJI8UVTVUGRZipC555WVlSnnZVlZWVlZljm1c+"     \
+  "zimE1lYRMrAAAAAEGChIyc4DjOGcNecpzj3e5eskWraU5OsZ1ma2tra+/"                  \
+  "QoUNbkyPMXUZO1Skw1yh8+fLly+84juMURSFhhhyPx1EDqAmLBR0xchzH8XgcYYYknU5HIoc//" \
+  "F1uAOa6rplb8brWAjo6AuBaCWnBu1yRw+9I6HTe9bomx3Ecj8eHR7jhpx2EJSwhwxKWDtHpSA/" \
+  "hd+Q6Q/"                                                                    \
+  "XNZeIut1JxXdd1FzPAbGI6kyYm6HQcNmEJi07r6Ojo6OjQ6XQdhMWCTscBwEd2xARjIprIJiYm" \
+  "JiYyf2KCACDkOB6Px+O3AKDQkNscN32A7tIn3tm+wPdQiK1gI2FpTbSPWkfP39+nb29vT9+3/"  \
+  "Y+8NdEAfA+OmQ6zRtfR0dHR8ahTR0fH4+PjY0dHx2ynx8dHgB8U/"                       \
+  "i6fLaUnx1wT25MmJiYmJqYDACDTYdbodB2EQ+9aRwD+Nkw+"                            \
+  "hfQxSPHBdvQ2TD5FpJFBCCtwtLsEMYc15nbtXNNdkgqHYiKRlIwAAABAlCZiYkIIiThNSRKhE8" \
+  "KqUrnsJ2hxoZt4CRurX076XaZaxJetiVOHTp0a+PgINiJWq8VwfLk+"                     \
+  "cITkeOQ14Y4rvOkFV5gNbxGwcVJTDea6zsoAASCExwDXWK1chON6pdVirqN3roR6RupwgcQ1uT" \
+  "LXI+"                                                                       \
+  "HyOoth7KQkYR7fAFOJv3TclGuuX2CS60rmmwgoZRIFU8icwlwDSea3MKrOGxMM1XtqaLgmDcCL" \
+  "YEbscM8PuoIEXYE9Qj08y62k5aQRDimNrAslDCa0CL3XGSYaTW0Q2etDMZyiS435NgHG4HACkQ" \
+  "xzYNnYqtvRwqPLDKAT1fRDd5KIJ45cOoeyA1FHC455K8BYpAAAZ2gMqDAOQPcz9/"           \
+  "v3uTNAASBXhW/"                                                              \
+  "+wqevLAUrnjUnS7YzOs8s+"                                                     \
+  "bpwXYrKdoXXGjBgp10SlQ8A3jb0scTwUeAFrmtD70uMfSS4gJeZlUhIlNsKco2uXVeY2VWl6JR" \
+  "DSAhW4jYAQCYAAJCXD9bEGgGxF1Oz2UgEAhOlC0q5pjzL3fxjlQcAAACAjx8bmMEYnbAb1U4nz" \
+  "BE2MsOHLwGuHz8oUi2qnhqYoTAuZWUNo0sfSn2HJJcA1xVleDATYEDmjGsqfYuV1VW3dhdQ11Y" \
+  "rko0xrJHM6qZIpxW2qPLKAiBzakFdDasdLWtAzpUaGbaUXhZzReGzLuS71zqMZIhap91418WyG" \
+  "4stA5xvC5AWfdPC0KFnhug9EJ6h0yAGfs54rQNMjP2JYPT0RkeosWCnkZ1GGYvGLCMRrhdEj8C" \
+  "h8OOUvsYFzIyKCO+"                                                           \
+  "MNsuxBAyvGFnp9QwEhcblgg5xA7gRNLmyHjMwEAWu57SEt2AIXIbqDRCqICCh7QEAvBIAAACSr" \
+  "VYG45afyShwcSuuNzIo4AUK/"                                                   \
+  "1ZvfgUABf42jCVGeVhwqQpxQ99SBB86rGrhPqKsDImUIoPYFTmNXd0Vlks7U8FRjYAEAACYOUp" \
+  "Uk8RSEAkCIWK0JOXukmSu2R1+iWGIWLBM+mt0Up2tqni9YR6/"                          \
+  "b6aK70i+"                                                                   \
+  "IV0EAzUMs4ZAYRQJwvNSInBWKJtFAgS9MiFgEEYDmIOZMK8E4h5xAegwErEGYWbSzKJ0E5mz+"  \
+  "AozI2QYjutAsEbhzsrxtoHkIjxIZo4Ho/"                                          \
+  "RpMMDTvsug986rhaceoIQiQucUUCJPKaOJwDKa0Y2kiRhjDxOG6EGJEyEhATLXC0j4qKckgkeE" \
+  "ugjRA8B1MY8D3sIBr0kOVsvFwQTfLbj3ABCIMrHSLyQ4qbOdjCEK8gghdCNG3wyjgskAAICiE8" \
+  "D3VkkAYPyxpQAAwPXlW2HA/I/NAfx2KQA+3q4MBYDE/"                                \
+  "X+cAACyJJGwfpZxAP42TMkH+"                                                   \
+  "0AOLxIzb8MUfZQRHR7AkSHICOeGMHdG55ULc3qMjEjSBwO0SAEAAAgZYhCWUSyoKAVKKKsQL5v" \
+  "0RJaFKF0iIp4A4u42AYA9HEIPhlrCoWNiOrlxU6OmOcVsXyAWNWWyYvEg1fLKMHi1MRcAqZ6qs" \
+  "KKPcJIoAfWgjkIjWXkBzZLBQ2X0djWBHvsi6aIQ6rQmZ50vcrgEuGNleEwUBA1WpKJiZkbhShj" \
+  "a5TrjZ8uHdL4p6sJpn0748t/"                                                   \
+  "4Ky401MB4FwAw6vRWc8BMAjnySEYJoc5+"                                          \
+  "VpmHtCG9622e9msJozQgHQ18GB16oycX3odS6siozeCNd2g8ow/"                        \
+  "jDOloAgg3GK9JhkfU4FAwCLci2kD8KNGqMLrinHR6yujhyHcCArjgYYwpicBMtEILRJRmAK8nc" \
+  "HC0MBHPNnh8fASAue68jrwrIuG/vZnupwGA/"                                       \
+  "v5t2CyABGSAA942LF5E8x+"                                                     \
+  "NWB4kPtqGxYto9scgpxdJx11V1ABBREREEsTMk6t0u1QyNV3MRCIdAgAA4C6ZGSzQBMyUmAhNp" \
+  "RgtYEkrCYlUtqSPpYbpbf2LmAYxxcEodhZD1DDVNE1z4VabMg0MUzksaBW5vkWD45q5luFKQ4x" \
+  "Dl6XhA0w4GGKQyRZwjQbX8Y1Q1y10x21clDHA4EPjADidlLiWSmXCUjIzk18yZHjkFeGU5hOws" \
+  "jSXKma1g2NpIJmVsRIyHQb04fcMMljQLC4eVrUpAsBbR2P80rIFh7xkaD+"                 \
+  "qQRJAhCF6amxRZzQLYCgAwGHk4tTxhp46UZ1GR01UxokEw3RgDR8aYLLBhBVWdRfXkdlKNjRIn" \
+  "tIN5WpsAhOGYW3WNE6ychBocusS6He+3SoISm3RZ/"                                  \
+  "ity3SkcDrh1O3GnqUWeMII2FsBAACOXK+ZAayOaQAAAMDr9fgQAAAQpgyQURIsAD43bNUE+"    \
+  "YGaVgonZW7YqgtmBDX1FCdVPNoASzrC7pwh6GrXnSTOhZliQkJITZQjAQAAEBayiEAASkSEEhU" \
+  "nAlqMIl4iEBUvFwr9KaKiwRKlIixCWBS0QJTtFQwxraBqER3lw4O5wsxcLRbgeBwXM5WRLq6EX" \
+  "AII0SuonlAqIjsEkPAiYT5wvA6YhOuYQ5AkIsK4nA7DuAjJhDkrtwWLUBWrcSQhFlOglSQ3XXB" \
+  "N6sIjyXFczCTkSo5X5hpGyJJ7MtDTHSJLkPBbpMBy2F1Er3PU7qKM5yMsFhDGeEooCKVk4zwFE" \
+  "9pNnPoeCBgAYsJ9JsGHiYDTHHEpzKldb3scr1fvvJRLdLrQPhYwq7FljfBg/"               \
+  "R6eNRZEBhQGYVX9QEdsDeG6qA6OAGjMBADgO6AIdQEAlaUfAAAAEM/"                     \
+  "wQZz6AQAoK06aAAAAAGTherz+MgAAAMAHlAX1Uy0qAL42LNpFsx8HpWdH0LVh0T4i/"         \
+  "zhIHWskPTKhHTrYuWpKV5KkjEUnpIYIJAEAAIhAIBSI0aIlSlQoChGhUKQQioEWUqJCUYeEmB9" \
+  "8T5k2E/"                                                                    \
+  "+DL3t1UQNjJmHEFP+"                                                          \
+  "mrQyEabFWhEmubBFIrmAMZaYgjrBwHIfFMHkdTzDRuXSQqUP1KAKoAGJv6IC8ddTpGcyxIIyMN" \
+  "kIHYlVoMSLCOpcBXBAKSR4Z5iJSHo/"                                             \
+  "h+sSn1w1zcL2+t+"                                                            \
+  "vggs6EoRb1YfQRvRtEfcwboET1ohMO0JBOF4wuclcSqndFBhgiPPWeAT6CUAZAZwylLtL7MC6Y" \
+  "NRgRVn1YnQwIRYTuUl3EIdFbu47ru3l9uMJcud4JcxHQ6RSD7hqATRcNGv0Q6EIJPAUwANBRKP" \
+  "xWAYAIcjoAAADwMf0lHqCej/"                                                   \
+  "n7+"                                                                        \
+  "9sAAAsWFtSVSDhHPrjcDBlFPWb95RQZfjds3cfIH5Ae5KTOd8PeYkz2Q8BDwXYfeeK0w1i5SQT" \
+  "t1sByrhw9LiVZHOVBEgEAAKDECjFxMWamy8tFabpcQBMlLIwymhKDW/"                    \
+  "motIQ8H8rNy3MJzBhFOsIQ0gAhTIG23swQ9rJa+"                                    \
+  "Vo1kgrfbpgarwknpUFFUXhkAZnUyFzo9nYLTpTQKNarPtd46sfSmS7CSgl1VkzAxOFWowvyqwi" \
+  "yQM9Io9rMVtMNIw0N6duZoR3zWRYFWWQhVKbjuF7WnVTZYY8CY4e4jCT93sfJJACJBRpY1l6NS" \
+  "QDEmmzDBPqhSdNmLPURTE4SPg9PiIuMSJ5SGvOh8fQAYFSRR2T4XYMZETqDnihEsuKtxoSvzEQ" \
+  "OhhXKl2oCK1LfMJe11t/NvT/"                                                   \
+  "EuCdvlaj1JXsG+"                                                             \
+  "sLnIEHNJtDRjXHAGhudngIATJQBHPC5X0dy8cgAwAAAnP3s8A77BoAGigofEvUvtSQzzckL3lz" \
+  "5SgA+N2xVxqYfiG2hQDRxw5p0TP7RUFYed2WZkTJkEIIMG+"                            \
+  "ax2rmxQ086qaIcOihPIgEAQAgBQniAcIAZEPPwclRAKFomKe7hVnULf3/"                  \
+  "PrQ43ubI48InJ1po2TLadA0rEUDVUsVMxwBjVmBC0w250lKGkDmEFeevFhBnCXCZDGDrECQYkg" \
+  "QebcT2OKzNKQDKHqgnDSkN6oiMCsInFdb1GjTwpqw4J2QmJpC4LKoDrLwrHbc/"             \
+  "zI95E+Ki0iHd+wOuhI5EXB7E4UwUkRwKwRFLVul6uHiATCy54aCgNFdC7/"                 \
+  "pjGcUyYMJw4OrgMxutRSL3lRBFhhNE7tAhCI0BJWCOb1aJpVEbyCMmnhOosy5XrmptCmDLAOhf" \
+  "11KO/3ahqxOhRJZwP1/"                                                        \
+  "AEZkTAcQBMVwwAAu8E9KzWgEiZCJPhBQAAMJcCAHp1D6QSAAAAzKfbAVCHthYLAAAAlQI8macA" \
+  "IFQ+AD43bFXEXvzAuvFRN2xdx24/"                                               \
+  "FOvOfNzVZYRMMqQMOWHNBpJ27Ry92YXHdDyJgHJYAAAAPxIzmSBiki6amEBSVBYqBTFapoRCod" \
+  "BcQooSE4rQEqQLIYHXj79ceaxwo/D71/"                                           \
+  "JVcaMMtwDAHFyFJLmS6+"                                                       \
+  "hmgeso9zEQShTggBx3gco4QjLABeTBMDMltQ8Brgk7g1kxLtIOwrwCEwhsUeMKC7rG5ArX5HgN" \
+  "fDrOIsfjlFavcc2EnBx3IoaOFs0aHKEuK26Jo1oGKpcKCVPHpO3dn7yJ0X8xXC73Lnd26CXLjg" \
+  "MtCjN5hCFThAQ2oSFtOqOVGSDuE32hhYk4jsTlovpQozGwRbDa4UortUhDwkUAGGOR6jFYw2wX" \
+  "FJWsYBDy4MgWAXCbVTMEAABQEJ9LgCwH3xYAAIBMa6ZNaQCgAcBTJ/"                     \
+  "x3AOj3+wDAcLwer2MAAAAy2ACgwXf8bXKBCjY2xMkkMx+KLi9G1SRfQ+"                   \
+  "pkrPnA2nUmeyDHu80EQVKEELbtjomYPTNESOfauaokPSZJA0UIkQAAYM42+guCLGQ+ns1y+"    \
+  "KgSupXRApdXJEpi4pSLDJJKRcndw82tg0wkAgPrVMAMn+iQ3W/huqg/"                    \
+  "Dn51VgoLfPn9gVTIoESUeFyxOCk/hSsDF5NZW70MrmNVrOoWZlo5bmhxTDLHOHTC3u/"        \
+  "ymKjiOlaAEIzU6EcUCYPLmDnmVK1esZjWOPXKWdXtVr01YJGhu4TiVoJMBjheB9dxCXLovTEEh" \
+  "RLGaUp2Y+m+"                                                                \
+  "rAscdQDonFbotozWUeP0Lop2ZioVzASyLqcOHV0lOE9nZ1MAAUBTAAAAAAAAg0MAAAMAAABNPA" \
+  "GzHHBCSXWA/4hFeXd4eHD/Tf9a/0z/RP9T/1T/"                                     \
+  "VP80TOZAm1Nx5OLKuqg4gORtwnDFBzFFjQzs0XZdrDdKYSDmSjid4LQfDhKi5+"             \
+  "h240ACA0CNW3cUDABUawBGbEDXTQBrAQCVA+gxJAeAooIVRYmfpDQ/"                     \
+  "ADDPAJiZPJ4CKL0VwmKNDgAAABB4wcksgAwAnP100SFYKhzvrKcPFkH6ob/"                \
+  "mUaXIRDBTcgHEGFi5xJcO3laj2nwCDrhVGxPvuosRTqeLwWlAfYSrIzNoeauADicEfPVoAlDqQ" \
+  "aRFq+"                                                                      \
+  "snciCkfCHS4qa8or0MBDMmF1j6cY1Cm4iGhjfGaeO3aieOGe5NDbGwDjMMcczOSPXRFkE9H8+"  \
+  "2tivg+AwTAGQNEQxiLnIquGjWEMUgZgkKXsxZc5FSaqfd3Uw1z81sIB8+X5WJ4h5VVZ9m4+"    \
+  "6i1buron8SP34vySmxQ7qLMCG0QEz0/"                                            \
+  "dT7XwCo2VZgmg6XM5ywWqdMz5QJ8fsMtqXl9ASTOGbvISIWw86dzoFrOPK5YYp0AJQNE0WyW7g" \
+  "QLT5gX0YMCEhLFyIFZx7T9tdYUcZB0VtJu6JumJQl5fBOSLhW37S5VXs34jK1Jk6VT/"        \
+  "x83HsZZHFaDLQov1dP9gNAMvmItMzF99N26+QM0xzGyd1NG3vx7cvGMQe2Flvf/"            \
+  "nbSajF9W32brvVMk+7b9uwetObQFh8AUjYUlVb4B2Kq5oLCGBuKhIP8byZy6k0Fy/"          \
+  "P7VLTPiCKAqKtn1I4yy4MRVYUi94hYM9q163TiXMVijoCQBlgBIAEAAGyfygLXGCzgsKJUvzv9" \
+  "XsNFCjlU+yZdS4D2WD4dm8oyL0+5TdC9AAAAAOSQ49//"                               \
+  "kCTDIgMwAI6dPc3tnLUcLWWKMTh3gAtGuRBrX8HcVR2OmbiORPgtuEJZSZiw4OIuJlOhrDgCWN" \
+  "qTvazq8R1kGqXJdTJxtXJKFMGgpws9wcyEk9FRCbfVYK7HpfDKt6iRl2yPW3hTVjWw8HljZcsx" \
+  "BrKd+rDCSQCAGKtxnRWOVybT4lpXTXidEUdPVgF4UL9swRV23u0ZD+"                     \
+  "F0MRKXIawEFIB1ekIgDYGbwsMKU+"                                               \
+  "0ozKo9Z2TkC0c3gMxmkYQbJsxQOzYjDyQkIuv7bsFeisuWiRwzYCSRqHXOjQg98QZbtYEYxjGc" \
+  "QcARkID2pQDgwPm9ngRunDrVrqxYyYraKzL4/"                                      \
+  "nJO3t3aACCBuWOB5ax1sNREyTiHA2wFfRZkaS4gkWMr6LMg2DOhYKapgsoCSDNCBtw9OxjKFWD" \
+  "CykgduoxFYqpga3WXZEJdsKgHsYBJTARBmHkdFKsAQCmnXnwROSE7Hv6llTQVfDiNLyIjZBf2/" \
+  "ksraS74cBrfqkTCRD5PUtVGNNMKXBQCXNlcVlBc3JeFFaQQFSmXY1K0woOGWAXocnERodAlpA6" \
+  "qrJyaaTkB4Jizva1jptWWCYvjdk79BZjTT5tmqsXOxhCQCDJiPlKf74sLJSRcEWkl5I7tv9mpY" \
+  "BrTuCLSSsgd27/"                                                             \
+  "ZqWCapv0HIOTKyCpCDKk6j0huPhQVAAtocQIRWkiGIAYtJsnw9BAjYl4xuuSLUnHcx2tjuUQyZ" \
+  "hTkDACHjsQhFltHamcRO8SwkekNAZhRrXYOp5uxrLd4XtEwbb8z73zWNpwRWZbbM239xM8kh+"  \
+  "uMyLLcnj0cip8LZsytqkYyyYqoriqQu5FSj1dkFaJAQSnL43E4lBKlTCgqQknRouwQXhEVEbg7" \
+  "NHGEdOWrR9X0wRWbhOXu4rUZu3p+"                                               \
+  "8FtsDIsThmlnNTCMKQ4thuOOKKuNZUbFxBOPz3vyLzwRKSzpbvMJu4IBp56ICCKJx9zGrmDDqb" \
+  "OGUiFCT7KTkUeojozcJkSBhIijQiLK54mJE4GIKAsFkCwT8yUwIelyMU2EIAJRP8Vo0dPebkqG" \
+  "aBR4ylMQA3+cdMJiYz7r7wm1Mez8OGaorRXUNFVncCyO9y+tTnwJTwwRNQtG5BcKZh5BQ70FA/" \
+  "IHBTPXWRUpUnQexLSqkClyVlENHAqLYQOXD15ITCgkhBDxMhE3DxaKif+"                  \
+  "YkFkgIqZ4SgmQMW2qv3wdmTb2rsVGrGpx6LhDJ2wcc4TF3saBL6Gd+vhJLbZdxG7+Z25+"      \
+  "jQzaNYQuBh1BDYutlYCuIXQx6Aiq1aqVkFstWUNWFBEioowYI7tudxub1uSo3FXFSbHERiQSAg" \
+  "IAAJAymXQJKSDADKEYiBhdQYsKPMVBM0UkxcSYEqFEpJDaskhhOmY6sBhiqCm2YmIRU7Cghi9b" \
+  "QQwceq3qcWAxCjlmZiH6YF0HALHFRchc/"                                          \
+  "IWBhRpjMYxFxrA4wLQKomKVxEB1ozEEOBlBjDOceDHHXZlryEFrZOCVme9uTSOkpQbDgxAsBoQ" \
+  "6dBjBwWrXj4MpbNqgByzqAzrwgIMhAGSRUAI4ImPoj+4YhtEJ450rTr3Te+"                \
+  "oKQ0EdEh0AuLwvoUgAWPT6ibwjn2ZyAcMQETA6C9hhQkdsorFi0RUGeqCkDB1Dd4A28ScCMQJw" \
+  "uxH6QfQ/"                                                                   \
+  "yMCNHCVggaJ1aR0BADDzeh0BwEQEwFyvuyQAAODD7zgA4LJkAL4G9D5GmqVR02oJnHPAa0DvY6" \
+  "RZGjUtlsA5B3Rn1hAyRSpTRUo7mDuT66pYUiZ62NwCAxIAAACNtZcoBlgINkBEZhUtSYASZ1pI" \
+  "CE0IAehCyA7TQgCCvQOL2OG46YRjoqO964AUEUVtMdWiMpMcF3OEHHwIyRNOXSSFFzOi1+"     \
+  "tdFlz6MJEIiHfoFEGsBWqK2llRk+NFMszjmrkOYygQDQIEGAA+"                         \
+  "HOSYuSZcnIoDMgcTuGgKb4MccORxwXUwMNM5NAQ+"                                   \
+  "jEtmJnk8GAgTGCOIAaGRvV5HqNPpDSamPgLUZYEEhXR53c0QJzo6dJE7ZC1BLTrDgLgQUV0Y1Z" \
+  "PI8bg60YUL6iMCM8fjQ0JCwjg9wzJDSCeIN5aeRgETm7ShW7pNB2jdSQAA1YwA0I0Y3ZIcAgAA" \
+  "76QMFAAnAgAAoe4uy6WEMgFavwv68IdhABANMmTIIwAAAAA2XwkkAD43TD0Fe0BLqzkJe6wblp" \
+  "6CPaCl1ZSEPR4R0mKQdrfEUNXOo1NjxXQ2M5EiggQAAKgQTLsoYbmoUISmWBTJoFKcEitjFhGy" \
+  "QCgmQigBDQjFBZ4QZYPjTsiOMNVioDY+dqjHBzjITDKZHBoZrnA6iiCYOUJgwqFq+"          \
+  "mDEu24kxORIYLjm+"                                                           \
+  "PR65PUKYWASXsmhngAEHkhMCHx4ZAgAmbmSkb5JI4ZhgHh2YtpgAvN4XMOQmTMRjA6aHkCH+"   \
+  "jA6FVDEAWqKgjhHlD9ZS4bOogVPLEZQBj2SnvQEZxim+"                               \
+  "mvvsK7dgCUswSOc9JLIBK6oCaPbNTkHBedS9YS4cPG65rQAudgwO6bHoKsJYTTAMMK4AANmHVx" \
+  "jTJ7Q6QEdEKZ7kpuib8EQLZAqQIkrdnQAGCnS4ojg7a1FusboazG0MBFHAGiOI6/"           \
+  "H2pEcTw7goALeNkzFJ3EoxLSYAkePbcNUfBKHQk6LKVD0eNdQEYqElJIghn03CFyXqw6RHi66w" \
+  "hQTcEMAAADsJRmSWJKEhBABRQi7i3sIRDxERcFEIE6J00wooiZWgLI6sqqoaaImA+"          \
+  "AdUsJsHFq5eAwXHDQG1CCPzECqbQSSuXI9EjRqkAnfqWtrj2u6Y38kvY8UZgIRAAA9gwTCcT0O" \
+  "QgBi8JqZ4zokjJNENj7UEZiDVyYzBMiQObjpNXAMqKGRYaiDoaEGQAgBHUEGgBAe0gRASDCOv8" \
+  "NijCH5HbiKapzzQjhouA6MQFLiGN2g85EoRSdULj4MpRwDF0XuDnXc2ccYPjlmjuEBMwSjdvQR" \
+  "SBM69k4wgteORhhoAuA7ZAMAoGH0pwAAKjI5AfAMBEZERr9LRwdR/f/"                    \
+  "WBm+EjcfMXDOQBGAec8FkAsD/"                                                  \
+  "AD4HLClF2mYmMaxmckRzw5pKSIdJDqsJz0ecGC0J59zOuW5Oko7MwuxAAgAAiAgkxSBKiREWij" \
+  "MlLi6kaAERZYoCU2IQiGH1rfZGI+Y0W78s/"                                        \
+  "lhtUBVDXdPJ0QA8ZxyJAOSovfLiMcBwMFwRSeciPbgYDXgEeBzcdBDURwTpIjNAHpnhYuYguTK" \
+  "g3unQCUwgGeARIgDDzMxc4XEcTy23Ta6LzCOv15VPDK/"                               \
+  "MXBnpOulLtVsXBVEAxmithYNQOPXElRBBXLggBE6XkyI0JMwyYBWGMgJ6q9BkKNxjaz1K1TsDI" \
+  "oyDzmvPYKiekGJIEE67I4w5XhwXAANwweMUkuGCQRwqtQSNAIgJWVdWnOzt0Z73R6kl6IqhpHs" \
+  "KQi20iNlJYKzVeep1jEBkLAHjLRq/"                                              \
+  "UnJQIQC0AXjipN3gQHy1Bl5P4B0CMsRhT7d3VQAEcgya72QAvgY0MUVpGiE98OhpDWiijxBNIX" \
+  "iQwwLyXWNlLSkJRJkI5pi5UlTXSULsyMwiIgAAACAhhBASEoKFEC4uToiQFkIImoYERQpM7Jzu" \
+  "sG7HVhTTnGaL1ZaZDDK4tqNimK5HPlyfXuGYK5lrGByZSHqmoJCghDFsJCSBHqEkssICuV7MlS" \
+  "HDpYuwyHgD4CnRASEjTEoCBEN8gwr0npGEhhokDMcVLq6oulqoGphjJoGEXHPku2pJQZM6zuIt" \
+  "3uC7hCC6CBCqj0U4dfoOA9BhlLln1LEUBsTodNAZpm5GSuBRjpJ+R5Ek3R4mHnEpz/"         \
+  "woPkxaVjIZVmODUztO1TAhxmg8DmYgD7CYCYOzn0EaEhI3AgAQI7XSEZoyRajTAI3kO0Dr0Emc" \
+  "DwBtiE5koAsZFh2DbidOjEjVWTcA72J0RKGoGj76McwC3UjFxPOcEMipPa7jejAXHjcs0QdzQc" \
+  "ovE8YYN0zJB/"                                                               \
+  "lAyR8mjOEuq0UkyCICO4Spq9o1McwpI4rXhEUzgwAAAECCGVJIZgmWaCICIi6ghOIULUoEQhCA" \
+  "6RKI+"                                                                      \
+  "oEC7IVD7Oysin0xzEStE6bM1rjSClwzOYtrjuHTzMxr5i7CvI6LyeQY5piDQF4Mrwk51tqiBrC" \
+  "gMwfXNWTmOI7HXIrwWSwhShx5b+"                                                \
+  "Ii1xxZywAw5EiYRxgwNJIFRw6Ba3LAcaAiL64jny6u1yTMEI5h4Jh5EVCXHiZidw8ByIsUYNY6" \
+  "nYnwnrh0YRwaCjh1jND6IoN+e8D4SOgefGSnBRCdK+KC+"                              \
+  "hVLp40uMkAoHI1PHK8Mw0UgALCo07kiQpd1YEHEOLMMAxOMQM0igWcEwKcPiOP5aGVv1DEGeD0" \
+  "41V0twoN6iyaCSnJjqzbkmg9D5s02YTJgmh1GAABAJF1E6GIVBYcAABIAPjesNQUzQkMCPHqaG" \
+  "5aWgrnQlAyPHBnsGoyI2J1YxnYulVRiRRQPzUSSQAAAAOICIkIJxRjulBjTIqJu4hRhEeKQ9BB" \
+  "4sCgoIikBdwJxmhIQEVERePaOrBbUFKsFq9Via8xoMdRixUQUMdH7kA4dmdBkmMnwIExawKglH" \
+  "DM5mAcLxjNur2OQcJDU8jgezKGMoTTCSSjIcISEH/"                                  \
+  "DAZSzCSRGZhkZQFzWOzHzIPI55kVeyaJyOLOjDeMB4x32EThqElRiLbu4hSagB4JPVGX106CQw" \
+  "FxlezAwL1AmnI2OgJ95FTYTXUWJCHXogYjiJ9zeAQk+"                                \
+  "MMzz6qVPvGfo6OkJ3T2dnUwABQIMAAAAAAACDQwAABAAAAIrNerQYWv9V/0//Xf9p/2H/ef9p/" \
+  "2X/Yf9Q/1b/BEzE2jK0NDQQNcgJ14gIsT+BVgws0GAgojdE9hi06B0utk6OBEYZxNa/"        \
+  "YWB1IIIaiC3o9ycQrxHPWQR2Beo57/"                                             \
+  "Q8dRMQZxfcCgCgLFSCVcrkbAMF1KpCqhQAnjY0MQb5gPRhjae0oYk+"                     \
+  "yA9IH9Z4vmUNKSIyKUUp2G7NVa66kjgVnUUcZ1EOBAAAIIiYWLIJMjJIQolT7uJM00SMFhFj2F" \
+  "nNqfaGWu0x7MVi8SNjYScCA2AY4ksuMpMZDi6yzpho1rAWIcMx8IKBa+"                   \
+  "IZaoHVE4SPIiKyR1iEDMnFqYXM41L4r2C4C2auQrtpqFOnM5JMmHkkHMeRIeF1vOYxyafjejxe" \
+  "jxzDF+"                                                                     \
+  "B6cEJaoEZP9HoSwUAZLvROGEM8OINxkgiqB0LJCojXQ98rMUTnbyQgCKKskRBKoYPTAI6RpU8J" \
+  "dfOn9Bj92A+xhdANvOATJADQqojR7XLp+"                                          \
+  "uW0OQjWYCAD4hiDETvevXUkAjpgjECjdaxVgTpi7BgQBghIvwmmQweKgROAYQAnxdyfDyZVwGw" \
+  "GHRx6WToDAiaKIgB0n3G26RFiNDNjZQ4AABbyfsJZF7429M5FZh8N7ZfZo6e2YfQuMhtpGJ9mT" \
+  "56PdjGbaAfz5LpdV+"                                                          \
+  "gcuhHFY8dAAAAAUMYUGOJgSkSCAoGAQkH7NEkIxQUUoWgBamtnqDoQx8wwzLa4VuxtRcQwxM6i" \
+  "mGqnKQgBNWW0tiGP43gquIHMiPBMTgaPsCI8ZgZqRyCZFwfDXMfMwIthOI4QjtGYuYgAwPBipc" \
+  "k181o4BlCAhBIGF2aOubgG8uDBHDMz4WKGPHhxVXExgRmmiiZqE1MnGAB6Z0gaGuHOQEANIaCV" \
+  "QV8tAuHgAOBFZmI3honY1a0m+v2+"                                               \
+  "jzxGOAidEZ46gShAZwqN14ePmBfHCnAAAsHotmG8h8swmg5oTWjInYPwm4BSGAA0Qz8WEOK0MK" \
+  "kB6L0BHLkmVidRw/"                                                           \
+  "CXKQaiJhRE56mXHICVMMNHIt8dVwAAAADQYAoA9IyMjJ7oqQTSj5kFKAA+N0wtxmQPSB+"      \
+  "szA1jTSF7QPgEji6TiF1WMldVeoorFzqdKN4sa+"                                    \
+  "xAICQkRAIAAIZUUE6xD1SQ9LIn8SmvZ8kzWFRMVAhJmWEhSJAyL4MrQAABCCGEfYoqBwVfvvLU" \
+  "rU/"                                                                        \
+  "4c2L1xM7OVuzTDlVwzGqjVrEI8prXcU0y88plIvltLPrw60Py4gQ40ylhApdQi5FAHcwDkosPF" \
+  "zB9RBhi2eV9xrjA6HRKoY9s4zLPO6ZcHEoCcDGvYTLDPI/"                             \
+  "PbC+uHFExMNQWcxog2GKZ5jpUTZBGGcM2sX2mQCLtBWzc9M5oBKIhr2nVZDJEvSJUA+"        \
+  "bQsEDzom5Y7CBDnBL20EigLuFgAMG9AkZhqAXooQNcJrRGBoQkwGOCCf0JQICKmBWByTq0iKlp" \
+  "+hN3YnvaOAbIg5khYARg0RrITBhYACXbggyTaw4uJi84xkxEBhVUAAAATK6zqrqO9dNIgA+"    \
+  "4ABn+NuzexMcPq+gTqae3YYsmmn248EvZQ0C6y6xWikQZGWFyJ9ZmmaCqXU/"               \
+  "0xDlHOc08ToQAgQQAACQxCyoKXMYUlIiAeJhQKkm7OrF0BwRSl9Qzyq+naTefUI6H1/"        \
+  "G6XhfX6qqwlu8+DWR+cCiM0qKDbrp2C04LrjmuaZEHM1dUC60NP6HujMTM9SCbkDmuHEeu+"    \
+  "QETMqpjoWG7hHjqCZiQU2DmIg8qhDnIMY/"                                         \
+  "rSrWjQTUvLdbUWsHpYRhQmVBVRamO7zK3TKPOlFoV1xChAZB7O8K0/"                     \
+  "17piAkJSr3pA5yUM91WPSEOzTCRBYmgoSE7UhOkc+j0AGASjpYbGQLWggCQDLoB/"           \
+  "EqPIhdqU3C5wu9BFhSl5AjAcPBYWnE1mQsAAADhxRjzGPI0AGACAACEOgkoEXAjXOTQg6ja2k2" \
+  "ozdSZmEgougvGG6nOAqNiqqCGKkxTGXF9GFQdEgAQWU8AACxYFD9ZTwBcHAD+"              \
+  "NizJxdgf4BeePL0NUzTB/EAIH3jy/"                                              \
+  "K7MDEAicmDsIKnqdq7MQiw6UcSxCUFAYAAAAAYOS8EFAeHGuBwX8vRSBSUAMxGIOSZgEXEBYUp" \
+  "5UkCRGU6IomKqrW8MFWPsE+"                                                    \
+  "p4BKLiSo5cWxwy6i2aCIvGlVzkQZhhqre1vk9GIfwduyysMYfp6kqSHOQRAIWFrTlamUtRVOsC" \
+  "i4ASxy2Xp8bxI6UeNLl0JOylIcm8KoEFTxgd0SGyjzADxwVh5p2qbG7NoLBCFj0NvaP21M25Z0" \
+  "CihupMaGN0YHS6fEgnRjQmpm83Ol/"                                              \
+  "jVmSZIsJTR4674wtdQXMxTPU4YYl55a1SW+"                                        \
+  "nKRe3FFAlAmAhGIcmscRzzms0MoAVaoI95esuyDgBoTDDNfS8gTmABgIQQkH4R8cepiMUvv8Yx" \
+  "DR+"                                                                        \
+  "M3lgy6zpakJmQK0dy8SHhs3CtwLJn7gACzghHACADVAAk4JUSb0tKfjb0UobsAelFTvZ4NnTSR" \
+  "NMDygtPnu/"                                                                 \
+  "qsiIRAoGNNZ25q+PSqThXbDzNxIIAAAAihw8RwCQFFUtKRCjmFa3wFAiFFWIV5V4RB/"        \
+  "aAb4d+jW3v1Ll3hOFnwiIRzm1rGKhPc3yaHNfBNY9j4PW4UCBz3NIMKRb/"                 \
+  "Yo7Zgi3mhEMIHYwD9BaCSHPCGeE1DxXXzMxpB8z1mEzRc1bhNy7PUAIsIHwGl7vCGhk6Y7wXmG" \
+  "vO4pK+lbRF1hPoXU6dtybCUH349R7h4KmaiyFcmTA3qJCNGVzAkTN2GcUle/"               \
+  "mWDIKhu76tydqwxRWyU3KxDUNIu59GGQwRZDu7naQbpjp7QCTjgcxcM6zwF4E5Oq0eoRG0o6+"  \
+  "3hCHJ0l8UhguOJFHBYmTcfN4aYJ10jKBL7xEZRcAIaxUQw59h3ALQQJ1wU/"                \
+  "pIYPpbVNRfRiBtMpLXDDNMLDGMhsCo2hgOWsIyvao0kQAAgRgAQAPLaiUAXBzCv5fAVKXAdwUB" \
+  "AGRZAl43LF0nmR8b+TdyHHXDNOpY+"                                              \
+  "2MjPvg8HhPD5OZY7Oy621URi7stBMTJI6Qoh0AkAACgRGUm4oQC7aVExEUQLBAFERX47hSBQKW" \
+  "OqROGjZ3VoVVU7GRyl1E27YQKri81sQoR4Yeehgy4+0R0LUBtGnOVfneARcIAs/"            \
+  "bIqpLUhiHKQFpM4F0ZjIQrVgjMZLmkwEQVhFaMIWozuVYbnAauQIQXmKtKmBVApYiiqBSFL/"   \
+  "NFWWAZvstNhHESPTJXWnpxhDmszIoF0OzhMNHRotGHmZkRViFoQvXSjkfR5jgqXPNU5yPBSWjt" \
+  "ElhAARBjYLXHXDCEqZYhx/"                                                     \
+  "BY4WCuWQmktTxgkp+6MDxGQRE1jaGRSdjOKJxwYlrOrE1EEAA9AZV0UwB+"                 \
+  "SdMZDfoRAACAUfrDY7euoerjy7nMmgTkQxIIJG8Xql1h7lLtxbdXHcFpAAAAoCZPi1BjyeQAAE" \
+  "DcvM4MAFxwRLKT+"                                                            \
+  "VIFAB43jMmF7EMR8yeXuGFMJjL7UIifPO6qooqQMkhEMGbu6nhjkrhYRNwsgZmQIpBIAABAGYi" \
+  "YBZiIIJmwkGkRES5RolwhpMUBQaBgYZnA933QqL3VcVcmdTKMmboWrGdB1er50kSGFKPaMT/"   \
+  "mePHi0xWF3xwXcyQXmcAjheOaGqx0pDDw0LkYoZYZMseDyfEIrwEmF5lc5LJl1wU5dNFLCAlpT" \
+  "HjoHAxhmLPCkIliCmIFmc6G0QZ71bSoKBgYmIjkDJu/"                                \
+  "IJRR54TLhyRwMXrDIhj0YW51qHNBRYJDEdbIAPRhyDAMC2pAEZkAPkJPXKBwMl5yEWyF4QqXk8" \
+  "JTuxwXx8XMHWeFOfpIFp36ZVZnREf7pe8jT7pIFKErIuL5806oVQGAulqAwEwAQBQFA9hKQzAA" \
+  "wGQSGHjAXBfDHGGuDzk4nRKiUmplyvR2CNPZAwAAAIZh2idUVVUB4YAA3AXkmytQOFQO/"      \
+  "jYsSSalkUYOFznb49swJZksjcxCeZCTuryriiyFFCECllZOnTHtXBLXxtLYhCAMAAAASAgJyYI" \
+  "lS5IshGJCQiAqFIgJxZl9j/"                                                    \
+  "333W7fpr05MZp24cSkeH2tSnMsFKXMW9qaikylHLmuSXU4lCgLP05DdRTO3rc7GW11BpMW1kY0" \
+  "WI0IKGm0kx5qjFZjIpiivDq3YqSMulB1Ce/"                                        \
+  "f4dTnLa+"                                                                   \
+  "O2IKtrKH2mnnSRpi8uE6bvx0rESWgBr6HEKpYWSJNhuqSdKnDhXeh6MSWruKu6hyZM0pIGDjEL" \
+  "xUMwkfCygCHDg3Vha6jhITx+UIAnnjjw+oylIFs7gYnnGElegYIR8hw5Bg+"                \
+  "hDkGCCPhdlFHjUvhRwBAFvB6i3CGt80JIf/"                                        \
+  "eAE8RviQdBRYacEIEQRAijEDrmjDFbopMWMVTUQzf7fUeI5iGHsD8pfkrfjOnUgAAAECvF0P7e" \
+  "aABAEBk1zsCAF43bFFH88dEXg0/"                                                \
+  "6oYt6oj8MQl5Nfy4a8iSSCIRDjYnzE3veOwqsYrYTG4CCQAAwCBmEEkhJQtJtBhExUVExQQCCM" \
+  "XFxIgBTkxvbxo6xfAvFov/"                                                     \
+  "GfBvljmtJ0T8a1m0dl0zw+"                                                     \
+  "u6AjM55vVYFWEIA3NcMMPkw0FeCR1DdyIiyY95XI8p5C3kT3nlAJ4C16gRnoRkRBgPPa1hi+"   \
+  "xICJTmMUPITAG1mlZ7MAwbw9VO0Pp9i66wIjSy3hGB0zDC6wx5wIvhNQwBeJAwcDF0TmNRZ6Gv" \
+  "kokIwm8hmNM4Q/"                                                             \
+  "VY0Vk0jozLWlAXIgwjP3aag9GR3qUG9hhtuoh82CAPhrnmEwuu6yhGhILBFYlQQp0UodRTCUAL" \
+  "sQ/"                                                                        \
+  "CRAtBEE2YuEWKMOBAZEAPCBm3AwBqSZcC4BeYmABMAMBYkACOxyMAAABUHLMaUgAA2BqfXgMAA" \
+  "NQLHjcsSUcOI1AfPA9xw5J0TP5A7B6GHndFdRIZMqEUHLN2EIzV7ZxLLC5sinMeAUkCAAAJKUg" \
+  "ykSBmgCgxUTBDhIlAnA6AhpAWEOJXiCahabB1mnjOpaZaDBUMg6lYBAXMAUPUdc3AECmyI4DMZ" \
+  "F6PjbxicKPLSuf1kYQAczHD63qQXLcAVbRu4BgmeZGLgLQwDMzMHD8yoJH1HbhohKH05QjAdXE" \
+  "cjFBH1Bs9o8d1zQOODEAgAhERxsBFJrklKxcDTyRjvJPAGLtbWAm8xRHNaEgYi05XJJ2nLjiBv" \
+  "giHThJpY6P1o4lBLEYihlBCdQRhaHeQ1IHRGO+UHK/"                                 \
+  "JNZMUZdUIdTGA6OBDwhgE6ToiBgCAUADXFJLZRegIIYQoijAitBpBAYB0mAfamTgAAMkqChSAi" \
+  "hVZmAAAoCMFMlhXBQAAQKXfX1ZIgOk/hyXbVQAAfAAuAAf+NozRRI4/NiVcwNswRhM5/"       \
+  "NgkF3DXrFYBRMiIyNOM3Vpg2lW6E2NqlUIXnUAEIQEAgJNUJSAFRcFgZqFATOApLVScEtKAmDh" \
+  "Nu4kLKHF2l8sUdJljntIH5tPxOCSuKl7MKvh0zTFIJ4YaqsLwIONWlytkqK4B4Qay8MgE6kzDc" \
+  "VwXgXmRRb2erGJHdpmQFiLadToZYXxknY4YSzNHOKWwiuPDI7kyc+V4C78TrkyOx+"          \
+  "T6CpOBqTS8Xj+OzDFkFmE8jajuDNSEtGiNnsEbwOkYSMDFXAFvQPQK+ohQo2XDs8hfHcVMZG+"  \
+  "cej0BjCdmMwERRMUU1CL2qFzHHA9eV9RPZ2dTAAEArQAAAAAAAINDAAAFAAAAVx6YmR9Y/2H/"  \
+  "V/9m/2H/WP9Z/3VORkpMSk58cGtuam5nZ2tqCCObwnf0LmQeXJnfa+BIPtTG5UaNjfA6/"      \
+  "XTYrl1Li5wcMDBMyyEBAF4AAABUOFPjubFDzrqMwarPAKjxugADIIHebgW63cx9egLIFsQMGgk" \
+  "uPwwAFbhABp421M7EjP9ohbCwHc6G0piw+"                                         \
+  "UcKuCR0nV41SQCASbETOXdcdVVFrlJ0c4QJJCMAAADPcA5xIzxWA8M6xnl8N9fwkEppIgEPyXI" \
+  "3oSjtWYZYTHtnDH/Z7mKZTNdfNJ74Fp3eFECMtjGQhfnA9UYpJ/"                        \
+  "1YvXJYOTJjany7DRgsABTFaIsrEaAurw8LkbAxmeNrAQQr05IYJkEtMEgE9W+"              \
+  "PA3LlOF7HhKtFKadthaM1K5xauaZ9l2vmygFMF1ZJqI6V4pUBIDOBmyw6DXHrSMHFUMWvchozI" \
+  "Gwb0TA6EO/yEQB0PhIsIBkT/"                                                   \
+  "RZH60dad4vrPXQTKg9UoU0grXHkSLlIuGTg8ehSXBchQwkIM3VasAXXhY8n0g36Ic8XYiWlQQt" \
+  "y6B0tXAROgEgwWAHzCgAAAPezppotW0BBAOCrZgzA9ZIAbrfLxAToYGdTAAAwJlYqgErGhYwDF" \
+  "BfYYEPg7h84CgC+"                                                            \
+  "BkxRJo1LIaeFgiLTBkxFJxsW5LQQKPKqSkhAwCJkbLnspE5VuyqLa7pJKULKAQAAAC+"        \
+  "VM14YjpC4cagMhwhyKWMIadCdl4iKeFaQ8umy6RmFTzy9AlpMBGJer+"                    \
+  "FT8iBH5nqQyUG2wAjvysAMw1y5VtnmhHS6Iul8+"                                    \
+  "KkzguhJqEXqgoS8siog00U4CTajcR2vOTIhZCA9jeS0gY5a4Zr5cF2Pa5iAwadjOK4EJtfNnlw" \
+  "gDE7zGAGAKc2R1zFpZAYgw1Dohu8LhgvVmdqchRXCFiw6zuI9hYwa0n8F1dGaMeAawS6RgGF06" \
+  "b2OOkicjlG9t4KZHADwuI5T8ZihdnE8husDr3nwSUgmkE0oTLIEc/"                      \
+  "EYZh9t0hS9h8uEpMAf6MIv/X/"                                                  \
+  "RFyLUMrp4HJEHAG8R8B0BAABxCQBw8TB7JwdsAICKjT0CAKLW6Cx4QDLX9fp0DfnwVQDeNqzFx" \
+  "+YHKBeePLcNWzFJ+QPKQk42xndRAYJMQSdlrrmqXSplrsvCLKUAAAAAC7COyxK+"            \
+  "gDiOLYwwEihIqTcioqJiLooSnzad5t+"                                            \
+  "XI0uovdXHMqONWu1trehoTigttn5GYxgHG8NkOF7XrY0L9PqG8IxsjhZYcNZ2LQ0LBYlEXYwl4" \
+  "VWCmQCEa4WbVrf5ckEGxLIEdx5WXWHh0h7AAEdZEniGHUFCRgpmTRgSYXSOC401a4Qh87ZYdDo" \
+  "VkcJurIvWXwpjFF2RnKYI6iwwE5UUojSIgvCdauUWic5Jik642t86i2NiZAeZEMbo9DqjFwgc3" \
+  "dI1QlOla7G0pQzDkLlLrGqyF0BmjlzJ8XSMs77qyLUbDQ95sTx2hEVzhs7PbdbWAT2rl4TFaQE" \
+  "I+"                                                                         \
+  "D7KBRgTjiQAMLBEOZHA5R4JAELo6Jn16Rh7Ygwshn5kljY5WKeDdAGAIQBuAlQtDFxcAIxFvZ4" \
+  "6HQSeNozOxc1Dk9OFJw9rw+"                                                    \
+  "RdhHkYclkNPBnDq45JAGDkEHO77q6E45qlROYgAACAd7s4eePywOcolC/"                  \
+  "CqoxMsaiwAiIlUUI8iJP+svEx+2QbbYpv9TO1J7H1a6oxTcxBWgfTROPK5IqK8+"            \
+  "JhfYfXcWWOuRXMMYiwBWZnV4UXB3PClYAWAbiL+"                                    \
+  "fAQ4UjpD1cJok3mLICBex2oogaEeRjDHLW4YbrlB5mZYQaLg7nWTulBJnM0huPIMR/"         \
+  "mEmVyRnVK4iPTIAvdTkcuh/2BCAaHr9EOEIuk2NVw7iAl/bqH56EwAkMkSIdiBdAZvUt/"      \
+  "ddH6ZCx6Qr1BEyARBNdw0azorHiMIbngNbOAdrLwOhamZSgiIrQ3Y5i0sPQUNtb5FifoEdtmD6" \
+  "RbgBsb6O+VlBWA0CNsBtuLG4V7IDw4UhUelkBk94g+/"                                \
+  "WKK6YV11wYAoDduA4B7z1+"                                                     \
+  "6Lr2XymIDAACQYEnIPgcsKSbSAj6YKwjQHLBEn7INhBd4fleUGQAyxJHL5NyZ0pUUOyeW6CZHA" \
+  "gAAgMcIhByNclkurFTAJ6Ll4uoVifIQgAgrxMqn+Ip0ZFOu+mBrGr5lMid90mIxp472/"       \
+  "h0nd3h8F65wXOGlXkxIaVWNMDNXKcF+"                                            \
+  "XWbyxBk1dBIPP6qBiMpUAyk6MXBNBqgqicXujEyd1JGBMa6QIBSNwMwr18DcXoMrWenWtU9zHc" \
+  "Pjw1yZBA7ChMUXOmkEwh9q0TgRBH2HUggSegw4QUI3ABdsJl62hemlSmOaFWRIAgu3MMEuD3H0" \
+  "e3RHJA4JAy2SxQaaTjfDgFxTUa5cGWDABtb1HhdBq9pox5U1RTkOvXr73dZtaG01Ebq6Tn3EhQ" \
+  "sAgU/"                                                                      \
+  "oNpFRIgYDVYtOrlF1qXhqryu5JjoinVEAzovr0NEmAmmijwwTu7EjrkABJFseTe0sAIC5XjcDA" \
+  "B439CWWNo2azpJW55jpHokbuhpj7YdOX1bnmOmVuavKIgmZskwZx4plgyWamrXzBBljFucSRaQ" \
+  "nAgEAAMDNwYGJSDxRWeEpLvBShHKoaGgrMYREkqgIXGLihBBHjIWevu8pJAQEAChnMfPpE8x1M" \
+  "Giva5JHjszM9YJrCWbgel1jcW906QAgsipqWic7DN82mBrhoCM6AIbAkPBf6IgFn5zeMeNJWDx" \
+  "8KEGoy5HBSoTj4WyAmmIYgqoxNSwRjA4GufTGBUd6KwcFnATQ1y4XgEOAEmix6dkdRq9bahQaI" \
+  "N1+iK2FyY1BI+"                                                              \
+  "sIjbZGR1zUQnK6vLHIIEHTNddrhms0TqZnCEkNSBjXzKwCciCFRabeI9qEQRQoqAc1FkAcp3A6" \
+  "S1QoAGDryQi0GOjDGwCWhRcUABAGLhv4IIR+"                                       \
+  "v98HAMhIAPDppHj2hkWVUlFR2Quwt4tuJIMDuMAHFjYkhSzy/L8ZsuVnkSupZMresCEhRDEM/"  \
+  "83E+"                                                                       \
+  "rPKlVQyUfdHWVGkQIgyiCJFWYQKZWQxNdVNvQGScooeYtPZmEpKYhIAAHCFggYx8hmGFTMyw6X" \
+  "kLgFRCEILqUX4oJUQBrvAvUjJfibTT6cOHNjFTHG87DxduU7Vh1wXTK7hOK7VXhOurRyPXMOkX" \
+  "HD8VhbhdxsEhiGS8OKgRfGlrbZqqHavXmdA4FQYQgIwL1AO1J+"                         \
+  "p9k6nmV6iJngxIQxLwwDMABxzw2++ASgxOXL8MvPI9zEAkMkcx+"                        \
+  "MgE8hkAikBLf7alVlCnYIwbGfGzNqCVVuZbUbbzCE969R0OpDQkBYIQQ+"                  \
+  "GOGKZBm3P5IkRZYC5kDiu65dJEtJipop3Fi9mhhkSgwpHDsAKOQpwg3FnuhicFPXBW8vpSlEFW" \
+  "AEAd6w8ecSZhJ6bwBva91t3Avs1S7b8yQ8yYqiiowpTDXokAFBEALgsFJz7CeCaU3JRPVysbc4" \
+  "KAPABPAV3W0MRxvdyBAX/NBUV3OUMRRi/"                                          \
+  "nyNEch+cSQ2VpTKTNyM0IErFMTvMa4a5MpPhQS5XZL1D6KOyvLBW74Q+/"                  \
+  "LZQzerw1OCbEgBoYNcAlAULDSuNVA8UfG7aMmChYqWR+"                               \
+  "gUFvxvTmmpGGXhTFQAawExCsAe1iDDHBw5GF1LvNJsxFm1CGgYLtMU4GJj5ziFCfRjzAmQBdxi" \
+  "QcF6kggXbPLaC5Q2wuD6kggXbNPk8kQApvRGEYqj3IW7UlXAthj2gJnE5JPiQ60gy4RHM1ZBeE" \
+  "I7wIsIAnjgESCiQYNgZpAW9nBhHsr5Ach+"                                         \
+  "YWgt6ODGOZP6A5H5QfwF4cwRTlAHf6yIWknkMlIDhADBRk/"                            \
+  "k0pFCnwxsjESZ3GmFcMnA9Ul8YE+"                                               \
+  "JlBAHyP6UWANQBCyM4JH8kgZHkfsg6YGEEt8SHzERyL6ijogqQ5j1TUFErK+"               \
+  "SzxPUKcwtIRPgdgWuG11w+"                                                     \
+  "OtTpLVDDZF4zx6knBlUAhwDICmQALCKB08VwOgaSu6apRiR0jpu3QyC5zziNs7JaCAqQZFQwMN" \
+  "c5uY3QLbMSrgmNzxINUxl7OyYslrwoYTSW5hsgvtFBQqEPnf1YBIBj6z8BXDpDN7Ff+"        \
+  "Wx9MUF2jU5VgT/"                                                             \
+  "1w1Es0H+1J1qEFCsX5TGX0IZvTtN9S5llTiNyVKec5rJmvh6CYjGTi9Bn4tLhaREGlmcL5nBu+" \
+  "KDZnq2Kiol8RH9qfpfQf6ZSVK0dP/e/"                                            \
+  "nvvUaGuZNm3KVN++ZbvTOpOdjzawOi+O9yoXAHQ+0RXo8vzCWFyz9/lEVuAzf2EuPvO/"       \
+  "sjx0WR0hcqc5I4MaznYaOVWEsijZZpWBique7CZhMbGQZfEyWVyG0LMCYp4Oza4KUjZViMiSQg" \
+  "/JWxerNC3JT/767BP/xVSfXYYZZ5wwxSqfPgto4LprbSdcLtEVXI/"                      \
+  "fo3THu1TCFeTTKaXN2VbNrK4qg85GO2bFWkWILNyZbK/"                               \
+  "KP1NAdz0CADFN8i4ltJAISgLi0X9a2ynottztoFPoZvWfneL3HKV90HsypJK7Z4GTo+"        \
+  "rlc5fjyjLaOUypO8x83scuACw2qQ7qtd7EAgV+"                                     \
+  "5BJXoPs91L7p3TgWRSQRmxcH564OZydzCfU0SV57KRj4ToRDUoFkPXZKTjVV3okKiaruMtXOiJ" \
+  "vA0/zjx//HqlnfY8n74+l7HveURKJzFxWIUOel4rq+e5dTPPUjM/"                       \
+  "23WFUCjErBCfplfdjiwjcqhRcQp/"                                               \
+  "qYYjPcVVZEYmJ2CVlVIYnssMHUPUuHciRXDLCaCJMScvjseLhRLq8nNuOopQoxmYiKv4i4lWhP" \
+  "UcrfRaso7b1OeIiPC09dSCl4giLiB5UKKWZX6D2OAPgkAGQ+"                           \
+  "qQOs25VQbOBdQtEVaCs90Di9V11URiLaIeY5Y8x+"                                   \
+  "s01mCsGpqjlRM0w5kbtfAbg2X7Y5ooTqyNL1iX234M3noeyJB8rDzTbe/"                  \
+  "NOZnDUSdBctE3OXWHOV0Hru/"                                                   \
+  "z8zXbkYTn+tVnNvilOx1wIAjE7FAfSov5nimu1oVZtB7Pk3U7xm/"                       \
+  "4zKyoR2Zhumjfs8c1oUMI8ngKhITnHK41EyJeLu3qyMogICupCAL8aiNMUQp0VFiFBUPJj4EzN" \
+  "25n8O7Wc6Go4cOdiN+LLFwUwc+"                                                 \
+  "AAVvvxKAJxS4QD9dn1JxTFeKhXPoF7uDxSL2dtlecgRKUQVisiLrMpA6ggbRs0jjnKEfgOgpdr" \
+  "GJEmCSIha0HPVlKMFk4QiE4JF1+bDDBGaRT0YxK13A0tkcaGKi4iAYFT+/"                 \
+  "W2YfgarKWpoBQCMUsmKtB5iEFZcwyutkg2cSxwhWlzjv1ElEmfMLMrRNnpJm5k5p0iVFcoR4QY" \
+  "AaqZoCY7B2Ht3D9GSj5UqYu5SuApSXjaqRFGWFWVX7g1JNJIsdMVGBnXsrEM17v79bbU6mtJ+"  \
+  "1MbHl20AAFRavZMB3cRI3ALd8C+"                                                \
+  "tnmHEN3GkbvHDtpvihMRpzsEZGciKmJYo5h63sigOZkVZBsqSekW2KwFcl6qEF+"            \
+  "YzIkKKQctyeZlBtNogIMHzUTQ571WEF0+a8GJSCLgRqQb+"                             \
+  "Ga5LgvTOmLssAABPZ2dTAADA1wAAAAAAAINDAAAGAAAAssrB0Bhy/5H/Yv9Z/0j/Wf92/0r/"   \
+  "Vv9N/17/"                                                                   \
+  "VP9sOrUDVEK81OJAtxnVTcDvIWrbbP+qRVGJOCSJg2sRZVYUgzhr8q+"                    \
+  "OySJtSwRgYVvWvMClNgVxqmmBq7XQR4pyuSiRllAhTU/"                               \
+  "J80pq59H9GYf2dEFIWTNjavbxba7fe+OzU2w2vu0F347gu3zb2vq6AQBaJx0+LCWy/"         \
+  "w6fDJKL0EZwnXRoeIwP/"                                                       \
+  "z18GjtrPSVoJb4DAJBZWQaiMiPZj7TIXiTY8nimN0md5gjdTTmENAAJAAAAoAIUURwBLHQ+"    \
+  "h8sTCIWiPLNMeQmDxdwkmdC+CLzUAdMyBAIPBUlZIkwN7fnb+"                          \
+  "9uSwtRWkmuyyocmOYtBEel1HmpeTJ1EVLFOb0lEnO0AXMdjMseLD1Mp8wDCL2uEL0vATRNAZyp" \
+  "a49olwNrQqAJwT3iAkFusfXgdw8VZCVX8WMGwTjUUFEPpfgfmOncd+"                     \
+  "rHhdRSxWu3EBExD0NS2mRys5YNvKavYqwyfMhfAhNCeup1HCj7GrEZVA7mODVjh+BT+"        \
+  "9PvuptHu3jSigQFX1zBEmbaGaDb2JQaCVRhGwwoyevNdOw9O15QrnybH5LqurGVP20xayn4QQB" \
+  "FRLNMQF1QHHcxIBM6wdmFYKUG3AQBwGjQAAAOnZf1Ub/"                               \
+  "e9v37rOD2YuEfcx5PCbDSLZCYAAKCX25caq19ul9qapX61+"                            \
+  "lIDABAZSVxfXy8QCQBYc3MAHjkFVsyd/TfmSw7VR2o/O0v5UYpsnz37D/Mhh+wjsZ/"         \
+  "V4v2goqyEoiJRoSzJogx4HDqDIpEqE6EqJWUi14uAo+4E4GnIM6qbRCCmFwAAAABEA+"        \
+  "qwXTLOUhBj28gygOcTCBhaGosRLslN8EpBjMajIMEsUkAQkyAGBEIBE1AQJ2AKYAGLEBqgKIqi" \
+  "mTCID0NNSEaLLkYDnQvEN18VGb45rkw4rqOnYaV6GJ2YACioYLGzmqZFrQ7FFAciIOiIIzQAAF" \
+  "4XqrYWe4vVBsVQUUGGOSbXcX14JZmdaYqKKIiCoa1NBDF0zSOW7AEDTIwyGBNWY8ImffuJWy/"  \
+  "n9DB6AMBHPXz4HIbqDEUEtccwTauqOKLOMMbD5ZrjmhnC9ciHmWlnscFiFSyY2IsDPPLpyHUw4" \
+  "TpYALQIILSubgSg3+/3+/1+v98Pow83Ajh3lCBkrhyPYzJCnQIAAMgovA6uh9EJAABeKZVWTb/" \
+  "60V7qPRw9u5P3C16lVFo1/"                                                     \
+  "epHe+"                                                                      \
+  "nu4ejZnbxfsP4ga4RCkllZkYEAOTnnefRqj3JmH9iZ6QAhtWtTGgAAAAAAwhLwGSFHCB6XRMWU" \
+  "R1nKslxCGD5DweeAwIEwXKCZoigIBdNuXW+"                                        \
+  "u6XmVQBqiHoQyTBsxHNkLpmlrZ6uG48Prxeu6Zo7jw4cPQ/"                            \
+  "hDnTowOvIY5pjhOq7rypEF4vTGQlhZ3fSY2+"                                       \
+  "AgDOHWUYPHMMxFEsRQFDUNMVA7Hz7l4nhdwPAgwKFaUbW3qGCxcb1CZsJM8kpAaJjj9bgCwwBR" \
+  "mPCFMYS4rlcmBwPMkWFyXJnMdXDMCz5NJvMhMGGA2ohpOCbqmDghiE8PAAAAAAAAYq9qMbFxYI" \
+  "ppZ2M6hmNitXfwIddxvSbD9WEAo402Vgdq65jjamPrUAXEsLERAPV6TcJIAOBooXUEAADAsb5P" \
+  "ql+mBQAAAN4J9ZZPu/wP45f1bp5ZsmuFdUK96tMu/"                                  \
+  "0P7Zb2bZ5bsWmEPRHV1EDUkqbKiSFFUhCiCuV06Xs1G9VyLiZQHAAAAACCClHTIhfTAEMK3JKn" \
+  "VdmIjsNo6ZtoYYmOjIlgdM0yrKXaYNqjVDodWPB6QA8K8rpnJQcJ1zeuagzkWqKckjAW9g+"    \
+  "s14fUaOI4HUEYXITpqIkAM1Tu9jut4TciVMITJ8HodCWQ4YfQADOANoUR/"                 \
+  "A6NjXnPk9SKBgymYKphqWGzAYudxQK6LGQAA3kSYCJcdDm0xTNMiiAiqqoIhYie2IoYQfkcA4C" \
+  "lghIT+"                                                                     \
+  "Bpe3CDXtBNSRaasI9qbjmjm4Ei6OeRFM03DQo4cAiLFIunWAd11M5hWuOQ4GwhUgD8jBNXNdV3" \
+  "jNwQDAmEBHABgTEWBCWgCgowMAAACu49OHa7GxcYQDHAQoAP74JDM9PfvDL92dfFUybqQ9Osmw" \
+  "p2V/"                                                                       \
+  "+KW7+"                                                                      \
+  "apk3Ih7oIaKDBUQUmRZGRlRU0QRe1pQ3VN5Q1SKidQKkXuiGAAAAAAIAsFdEIKLZMmAkwlyuWH" \
+  "BXaYIxWJioCAUsIhLfKqKWkVMwKq2Zntcx8wxuRVpwpV3EW/"                           \
+  "RRyZeM2FWCsOEa3g9ZmbIMLQY5qwMc1zr0sFAGCMMTGskbXXMFAO1mmJgimGu5HYFwJLEawIzE" \
+  "NapAwBQFsf1Vjzy3QFzHZk7BTENW7W0VRxa1Cr2zGCrPVpMAQDAIGH1DomxGKG2aoM64TDT12C" \
+  "L4rolxwGE0UN39IchwOvxOraYx+"                                                \
+  "t4bwBdhE5ADTUxHNjZ22LYuD7NZF6v1e31zcx6zrEAKfTQU5eHAcLoXQwAANBiX0heAwAAoxl9" \
+  "XQLY2gCw6koAAAAAwJi5JflldKc4cDAPu7NKAQAAANB9/7YUAAAA3vjE1hG/"               \
+  "eISH7tv5rFmwJWx8UnWPXj186Hb2SbcVu6ZUSwQ11CIzK5AZqMgQaTcwymUaDxMWgKQ8nxgowo" \
+  "TvGRllZlE2ByJDep1kiMCIE+"                                                   \
+  "UBAAAAgIDwJjjo5aaInToqytXNJkNBGrxRElEWdZgihGYwiJBLAiKeyfS0hGkuioJQhMoSuMzr" \
+  "ifT3D1OQMmGwWLh5ywBRCMhQ8o93apqvqaryMorpq5x0LBQTAAEph2La2hoGlmn2FityDMLrGO" \
+  "1k2HS4PjSsrjAd6pDRJdE1kmsZxyOv445hcjDHcb1FHbp5aFF/"                         \
+  "dXoE86GG6AwGkxNWEHOQAIxqxY/AcAxnFJmGJ1YBFRcA7xhC/"                          \
+  "zvtjPnm7M3up1AAMMTMAHIwn5W15st2SgAAABBHAABDVEAEI6aaXWD0AYQcpaGbD1/"         \
+  "DiJwhDDNMAgAoSgC6Edx44wDgdcETADRHRn+/9/ern6+ur+1+2d2c9XF1M/y93QAAAABz5/"    \
+  "ufBgAAAB7pNMoRs33saBnTJ2D0uEU6jXLEbB87Wsb0CRg9btdEVZFU1ZRRkYQkKjJkcBKGoBBZ" \
+  "GYkIVLmatjWrg/II0wm7yQghJiWSBAAAABGzZJAgIkjJLOEKWVDK4/"                     \
+  "AJhyEMiIBQzKJMWCBOUQxJLCQJAksWzJQIREDTIjSIOAhhATgIPCYzx2WROHU0DBGOBI6EeXDM" \
+  "os5pMQyj8SAW9RHUMOLINUNgMpPXI59myDDAFfDEIqN3MZhqbzHEECVwRwQAAOSaDzyOJHMk4f" \
+  "jAY2axsTPV1lAbAYAf+"                                                        \
+  "k3sDkTFztRSiwmmIWAegetg6XowdKMwEQEAiqiomFaHtjaMkWgoBYC1NYDMdQxvCiZWB7Zib2J" \
+  "abEdz5HVdHPPiAbwMFgyIk+giEwDAWPSOdMDoBwCgG1aLAKD3/"                         \
+  "S0AsGO+VNLeqaW8tpS3+UVmUQAu3sjU/"                                           \
+  "B47Pgb1zr4gS7ISNzI1v8eOj0F9Zl+"                                             \
+  "QA1npfEjVZYYaa0rVRNRQGWrImlA4ZjEcgHY9LcA4EAkwZjAbgKO7qkiEGCWRAAAAAAvBEJJYN" \
+  "CIpWBKxZLBkKSUTxFmEAk0LAEdKEzEhBSZCmmJCiwsYDEIAZjBFgWkBTZiIQxxCBpimCBgOM2j" \
+  "QBG6SLpaiysW0izAYAAC5wkCGB1y5BnIM5FNmCF0k6Bn0kYBPB5kjgWuYORKO12hXCAfAvBhIg" \
+  "CnUYicihokCqgKTSRIyk9cUTId2YkFSAwAAwGKk0vGFVPi8tTC54HhNcj1eTwVkwIi0LkXKBHX" \
+  "gARgOrgw8hmMekwECQ2YE1OkkobwNQBYRJnlri2G1SA2AEuCMFBHhtOCQGFAAiKOBiTjV7/"    \
+  "e7fToicLYBgA8/I0JEbe2tpioigHcsJAMsiZ8AgAuwcgG2EoACIAP+6CzKkpRfTI97tbNDya/"  \
+  "RmZQ5Kb+YHvdqZ4ew/"                                                         \
+  "zWFWguhfSWqoCpTlAWE3diQ2hvp0wIkujNXNRLCeKIYAAAAAMCBv2Zuo0sBJbg8DkTw+"       \
+  "MIwCAPKZUDoEgUWEfeCkoAQFJgmTEGUov0iCe1pGvYq9oaihtqiYo4DrjDhFcIV4IKZV2CuMEw" \
+  "yTBheDFNs7VUEBdRUwFTAHMGBaSthCCwAXucxF9fxIddjvjsOVqc6EPc6mGLYMgmoAAiYgfC6y" \
+  "DHXAcOiFsfUsJiqVjF9yBwEAACApje6qBEmAvI7gAkvXjmOxzsdeQKAAQBUUNbBysytJu3F1oh" \
+  "FdRXJMVcAAGC7PGFxhSQkBPJYgAKeXkcIMqqNlemmODCwEdsIO7BcFggFgw4oIuzQE4QadIjBy" \
+  "mC0xnhEg5BQBVsDh6JWC6oC5pYeM69hmNeBEQAAAB4JrdaYSBfbx+ys5DS/"                \
+  "SGix57TsYPvYPTnN75qozqCqOlNZERTVJUqIPCezIGsMImlqDbVXjk54G8BQMRM3y4BiAAAAAJ" \
+  "BgiuzGvaSw2COcUPAsKgwQtVLFCAXMjE5kisGCZIk4oKgqJGEATADaK+"                   \
+  "JGBBRFi1B0EFqu6zGZD5yK7w5uMW46gNdkroOZTDjINWPlOCJ8jJ7ICHT17Ywa4pepioEgZozV" \
+  "BLVrVvUp6wihFEbP0BZBDaymyjQRIY4Wp6b5bo4ClmwhM8OlruXD5FvIACRHyKeT3e6EEYKKho" \
+  "TgnO3b67hOKQIAAPir3b0QhhICAL2E4x1zzGxcR0ZmBTL9qCxGrjFhjGgCiH2YOhgDKmJ1YtJy" \
+  "8xUAANWeUkgZSecbRxsRUxXt4/"                                                 \
+  "QEAOR4cVzHpw8fzDoAAADb26LUAxeXWn2M4NKBCQDoUMcAINMAGPK2yQOAifAxxggAAAA+"     \
+  "KZ3VGs8Opl93kpyTI2xSuqg1nl9MnzetLicOuR+KTFRDGWR1VZCgOI+"                    \
+  "IyqoIMjIydNfoqBUj7I2ORdytWZAAAAAAwGcIFVIOy7IChku5BIQGoDEORyB5hHg+"          \
+  "SUkMFpICMXcoMEXTghIlQiAUo4itMX/FEAJfOFaddCzcisxAVuDI5C/"                    \
+  "BAaNwbWSGqmY1Wuqa6qt1FdYsDuaCmYGZFZiZH9fr0GEiFBWq4Sm2NmJVe9c1lSEBFLg52NV2T" \
+  "DBO43HCj2fCdUyAE2rRWyCAs4Au9QQnccwQqfhskEkGAAC8ziIQui0wGgJE2NKnM8J13MkY5nx" \
+  "qwUfou6OV0d0LMDwVwLTgyyL+Ej+GKz80AAAAoBgiiJNi6+"                            \
+  "xj8W2q1alYxUCsgqc9uAg10OkoJT6yU6oYCBAAYHRFEAAAdAHQIgEUYbVPMI/"              \
+  "NmPmU6jRQDKvLdYsxZgD+"                                                      \
+  "2FxCl2VHjIAbORGNzjnjs7IRze9PTPwQEyALiQoUlQGC2M8D6B4a2gDjkw8gOmOPvoGFmN5NSq" \
+  "wAZwIAAAAARqNG0ysYsTgPzUCEQgGPbxDyNMKwoAJQCIViREDcKiihCO3uEyIAGJRKChwPdciT" \
+  "pAyT6ODP5QiJiyJgGrSQVUsByq8mQcmUBD1LHlFhQrpERCgFAACgKRYpwNNkVKyGgGFYZTINPA" \
+  "7y4rThFo4ZOKTMgxkVJwwTmEy8gKIrjtdR1fV7UYEUARIDI0fx7TONAC3kxOjfGM6YmExpYh+"  \
+  "EtSpleWO+1p6ziSjXSooCmAZA/"                                                 \
+  "okWJjoAAAAAjsdJSrFPZ2dTAAHABwEAAAAAAINDAAAHAAAASEashxh0/2H/Tf9Q/1z/a/9J/"   \
+  "0P/Uf9f/1v/T/"                                                              \
+  "9ThxSNkAfKZZHcwvT9bk446gQtnFQwGkw88zQxMEa6wgUAkqSiKb0lYqgtKiLTxMTADHOsbcED" \
+  "Zm1ioq8n617fHM16LwAAGP0g1wOAGQAAAIBgyvQuAVIIB8Cnu8LjCWrwOJgd1o8nEhcDDFVkEm" \
+  "Kq86YqAF7ZPEBJ42J7xIdyju+ubB4gpYuL6ZUfSrWo+"                                \
+  "4esKqACKgURiahItg1YTZ3oE9W51Bx6RJw2sAAAAAAAh4LH03mEcIUcygFEWZbH53A5ABEh0qS" \
+  "IuCPOvgrZEaGFspgYAAAA8apAKteLn8K1JdekzFHpUjEH8HoG8EqmugZMOSBPYoTVU4suGL2ip" \
+  "YZOnSLCKJigBghq4LU5Mc2XyVgEBoYJxFL1LmksY7X1I6CYFsMYVG0NW8Pega4AACMRJkZG6KA" \
+  "zYbN1aDvZDk9YDRMQJDKBcxkAAABeBJiTtSISAuEL3V2/caEUAD3l/"                     \
+  "cT1UdBtEd47CaATYAAA5l8C3vtLABCAOegkFk9j6hlTdRQagUCMdE2NlwJDRyUrsY6VUHkxOWW" \
+  "UhXnGcZ1uNPgARFboAyz0Xm8AEUyIY6K5L2MLIawsNgACDIT5kTIdGGJYLU5FRI2c7lWkCSTXh" \
+  "ZfVygAAAAAe6dxyPkcfmstFTnVHOrecz9GL5mElJ3s/"                                \
+  "kEUNkIGKskpQBEUlpuoa6ctkB+"                                                 \
+  "iJpjI24ngAYQEAAAAAIGDBYSgFjxIenwFLkJKEFFKyEmDhiEpApsXZAcUEDAbNgJQI3URYnAaH" \
+  "VAaDmQEAAKyObK1iUWeLTF92Dm38+"                                              \
+  "BTCiyuP42LIMQ8CxzFzXMwTRh8B43Q5NIQ8eAWFwMzj4G2Ti8ZFcgAEJhetHNcZCRYI9QDUtFi" \
+  "nU7EiqhgCwKm3aABex8yDYzL7oYU4RkCNMhGxYrEaVos9FQGYAQCA4WR0qAfgdDoB6FmMw4gIZ" \
+  "yQLFdCFYdQDGP1+H0YYQQwdpqmK2IGlTAUwFzfkYODIhw/HNddjVWCxlDDXfeBI/"           \
+  "VySEdFdiNTCAJSg6qC44iIrRUYLeidggE6tgQgAEAgXKCqLYm/"                         \
+  "YCDbiC1UrkEshOoDBAB7ZvIlUHHNQXOVBqCSyeRUlS140a3sRGvlAZk0yRAnVWVGKjAxRGUzdS" \
+  "Xmhh92Qk3iciCQAAADAY20asg7iCcEXgFCGAOpMEJBSMZtMMcUik45mOjVLZpwwcSFLOR6PB5D" \
+  "hjluXmVVDXg9mDnjADBNpgFl4XFytJEGCT8fkejw+"                                  \
+  "JaNkARjcrYwzDoRHgk0Yh0RHKWW0ajhjznLYjSUxDK/"                                \
+  "Md8cFM0MXKMZBXYdrsGEGRFJXrfBtHac4g2oqhNWgTPAUAABOoSKElcjjIqkQADrgzYrqMop5Y" \
+  "Wcni7RSJtA6qrWegui9kxuENayEOC42dzyLXDABFtPO1NJABa37q0EHjB+yz5cgQ/"          \
+  "QIT4ALk0ObCELMcSJEXRGM6/ZtpQi9AOPujcpbFJfpDRH/"                             \
+  "TgwthqkIhpMtGMZQUIlGULX6rZiuB/"                                             \
+  "B6DADAIwBpEgCSVQgA3mi8+"                                                    \
+  "5gyE3Tt01gRGbNovLqUliao7dMwvEGNVYlItLlhmdt1UT2uMFGReNyARQjKAQAAAMQQ8GnrgM5" \
+  "huDEOGK4nLWBIuMItWURSVFZCEUI4DQBUXybOjpw6tBF7U896yhwn2j2K0c/"               \
+  "EgAVcKxNWMI8Z3LqejHpVW1Eb5kjIA1NpqN7orXSMevC6HpNVSNFhAkmOMDnmw2sq/"         \
+  "UVhZipwcYDSb2AIQtRV7rgNoFy3SCwwA6ZDahcFYXG347lwE/"                          \
+  "NVTKjELdYuy5eJm3qY4l8FBQH4JFyAGYWuKFcxn3INwMw1jBOGATpa1nunK8q03an3RC90jib0" \
+  "w40xMEZYUetcOt3odtA6OtIxwIXEzPyZogjSHCN2uJztxPSq73YIGD0cIjJhCG2ioxVogIUJjd" \
+  "a0ACgkxuyQamJrb7EROxsdVbUs9UwGyVu81z48Kq00ACCyiwAGQRgALGu8AwBeB+"           \
+  "w0ZdKMjF9KFMjobjjSlMWkVHwqYRG+"                                             \
+  "oagRRJK5xqaZ3l3eVElV6CyZESIkAAAAaDTKHTwifEIjQpuFoBBjMRFSEhWyoNz3CkWsM6Tt1A" \
+  "krFnvfzn7sygxqu08G9nYWBQtuLSEwxwXXaya6/RhaA/"                               \
+  "NiEvJ6VMqR6zoyrDbXU8UM2jHXY2vSp8w8HkcZWbTHTCYH/CZbYyBXjqpmwJbGhCGsXDPXZ/"   \
+  "geTrtyUIbRJq2maeN/"                                                         \
+  "YnoDtYjFWuJARAXTAiscRzIzM6mUokaLBpWDa1QSA2zZdBksXOgD8CBBjihLH3ywlp4YFqqHd1" \
+  "owTlDjlIMArNJpSxgLKzw1Xo+"                                                  \
+  "B40U2ZqOcUSIKymQZqsdydKjwiaw40pmcRVqGTWEFlcIcGdYbQy/"                       \
+  "I3e4JY7MvDI1R2IuCTRVAt2uUAWLLjEhdpgurtZEItvq6sXWj03snCRNW2g/"               \
+  "NADYxQIRnpQAp2w8sFGEJ2WP0SjsJAN43nH1J4wAW8NA3nH1JcQCLAjwcmQuCgKkyttcuKZeoi" \
+  "SASUiMIAAAAZDERUQoolxNikjQtzhCnQIEIBJRoOaGIf3vTfgYnJqzTWWY6zUSsjYOvKuD1sXi" \
+  "Mqslr0rhIOK7jRlgkVGcYsTos+LAwC+"                                            \
+  "qMAByG9kcbIojamYKhIipOY3yEt3gAxzETJvmUueY1AwDXNeSYydGCa5hDy+"               \
+  "uqvSlKG6jYmIYVcwSO48U8hmSYyZGRHhdk4y2ZYCJRIsfoaYQHAyMeiAQ9nKsI8oY4kxOIEILQ" \
+  "lVBQY0BAAoD2m4uIbsWRIxc8gWu0myBaGsUowfXXk9BX+"                              \
+  "4kLKYIBTgM4hYGO6p2WDgEkxlCnjkSve+O4tAkAABL/"                                \
+  "7dSQZsbc+VZbCbmdQasaeBENAOj3GRMZALlKJByPAADUxM5qihoKQGInJgBgbHkCKAB+"       \
+  "N2wpBh7I6TIXTuZu2FIM4oEcLnOBZI7MYJGnNDFVjZWuVFwkZopbDBIAAEBETFzoQVOgaBYRCI" \
+  "WUqDglIhCIiFIUEaXERYmIOrCooPZWbB1Xi+t6cOV6HC8CIS9mQEyhbhzjPWOooTC5Ji+"      \
+  "YmUxYh4aCBgzRGwpWmaoAouDPdxlEGEON+e4gmXBdjFoEQgIkTl1yhVpFckb2CEl0Ou/"       \
+  "IHOERyAwh1wWZDwckTKQEIKz0pgQAAhMbGQowUgZiBkm+dz0ACaxoLwLBIfVO/"             \
+  "fUyIlhWARrJjayiTyIkXIDOM8K4/"                                               \
+  "GFyE1ZgVqblQ11wSfg4M55AR51jYoTpcXIMUb91xI7QoQdMJPrHBGijj3C08L3YETVl0OeA/"   \
+  "F0BAGAYHAEAsxkwjwtmZgmAFo/"                                                 \
+  "rMdasMTEBZ0xMoOi9gMjhpwBIhfUCnjccaw1yguphjce84VhrkBNUD2s87WbS2rmqq9vJ2BWHZ" \
+  "cYNAgAAAECcmaJEaZpFxIlQTCwOsbU4YWOxmo7ZGAaO4RA7MR1XHKhptcWwE1OcUDHFVDFU8Ch" \
+  "cXMSYTISxqEdEZFuFhLxORS4UKnBhdFFQPYWbkvl0XJnM5DrgMTArTLgCB7wyAJMJCwUiNtGFA" \
+  "RwcBObT8ZjAXKBdOebT43Vdr8eHTy9gyAUXGeb1AgLXxKHTh791tTphShMRY6VnND8VJ6I2gWm" \
+  "E5vXS2IRGRA7G6Pplchz9iRiatbSjQw/"                                           \
+  "o9RG0BOPhJt+"                                                               \
+  "EXHNrC6QCXPMCIEOYERci0McYOkaI8f8OROg4CHSghUEXiAC1VNB7E0rHWXV0GBecjhRMgPYsP" \
+  "Z+"                                                                         \
+  "tXz8AAORpKHpnRgT4PxUCgdaFLn1bfpig8MvGGe4AAICpXi8pT3291qQDAAAJPjeckw9mQbUYE" \
+  "jKGueGYYpATxLywPV+"                                                         \
+  "qRESUEBkZ01Rd7ZVLrBNmEzsAAABAMpGQzAxIFpItToiNvYkVi42TDsWBGuKEwDgMtoZjlolJG" \
+  "9/leqTFu/"                                                                  \
+  "hYLbhrsDUFi+"                                                               \
+  "2gE4PqNFSm66oco14twjpVYgIJx4MrJ4Jc64CLFRzHXMnA6whruS7CDNerpnTwllKYBMhkOkAf" \
+  "HdGKQDgOXoRg3UuvCqomZ82ka2hluODUte/"                                        \
+  "mQC3fQSZMRKaKIioGQ2O1qNbYwNDtG3YREyAASUQxmggzACoi6IUk74JHXC4n0XlE1ofNBcawX" \
+  "B/jNOd76rjxMMSt30R2vaduv/"                                                  \
+  "TDcquhxZXaMUwyR3gRYoUAR0fpACapOgZjAMCCkyAl47SOQlQX9UbPrclh/"                \
+  "sCJYACWn7Vu7Pf71cnkAQDERfpnLAC3RVrsh0GH98kRxlNG/"                           \
+  "F1AN2RXSareh0aDAAwvaVKAkwA+B2zFB9EghsWC5zlgKz7QQFjA87EHkLpzR+"              \
+  "7Estuuq7vCLnSOscwcAAAAQNFCSYHAJaDFaUlKICYQgxBi4mJiAC1K0SIQZZoWJQIxlEOMiNC1" \
+  "3a7A8I+NjcWwtZRD0/HpuI5MjvCaYxg4dBhVTIVN77SlMy69/"                          \
+  "gbXoF5YyVxpkRz5hYHAXCThINdxMDwSLua4huR1JJCoMxiknUEnYOUAhrXVOLi45jWTj7HFNWG" \
+  "OPGocAzOxMrN0DWQCQxi4eB3HxQHizLNywJKAOo0nkdKAcek9a+"                        \
+  "ttYcQ2wQI3wwAAxngnwSoMHDMzxytkDIWprhvO99dwNHc6vccsXRczx8wcr3VjHLH1O0rtnxB6" \
+  "3sscGhAXHdCDtHtPCXQMhFAn0QGI7FSPk6M2dPvdgVupidB5eFdkq9M3Af3YoJt6QJKOEHCyp4" \
+  "ABq0sII8C1Q+"                                                               \
+  "oi6RwPj0SuwCfI9SIAHjcsqQazIHoY4SluWFILdgJe4OmYEAgOMxwGCEBXJ00nhuOkkokbJAAA" \
+  "gLioCE3R4hRhSgChi4iIikiKijBFCcULJhCKCsWZiHCFuyjEiZAZbpQog1BgIiqkaQrPKryGa8" \
+  "gVjrwIk4thgCRzzU1UbWxsmocJkPBccekpoJQfUUgIrCJK1yk9gMl1RXL6MHoD4hkVjHNFYYWB" \
+  "ImpdNILVUdANYbGI+S4P2CBj5PV4MNdk5mBkIDBoTLOmTfR/"                           \
+  "pze6Rpu4rWPwyMAMMFGnii7jVkOCYBUlMpGNNxPDCEx3UUfOMMzxmg6xYM4viYQWQykoCaVMMW" \
+  "uMD+l9GL8jtYx0wEVIS4uhGUJP1gZCCIOOoEVYDRgyr5mZURKJDkIGPkPr/"                \
+  "shgABdMKPorPG8xRIFWCmF7ROs1NMAwwbswp35SMgl5wpgBw3y5frcAAO5RAZ42TC7GyEOjj5e" \
+  "lNLKHtGF0MYgJurhaF03q8a7OShARqchMZ86cOU81OVd0V6mYjB6K5RGQAAAAgCQ0MEsI6sIFK" \
+  "XDJkiIiQtGKifLpycL/"                                                        \
+  "qdOjOVkOjLR3Nm3sdq2qatnFV4pTUdT00l86gWIRFNTE9QjDjIIwSbByTQ4Yrhz13GmBQxfhFA" \
+  "RxAkVQVBW5rr8Q5mJWt0LlxBSWFpSZXMegNPNiDoChel1lNACvYZjjmgDHXA+"              \
+  "g0ooMHEamoYDxMsdMFsKnUEPH3DbR6PXRFLpI5gAqkWRQwcBwyjh4FJjjQ0NGWCyRO43sAacOQ" \
+  "7ikYB1UbzLJKxNGI1zXdjAwsE9nZ1MAAYA1AQAAAAAAg0MAAAgAAABdaUsxHF7/aP9I/1T/"    \
+  "Zf9o/2VCaGv/Yf9P/2xFSERse/"                                                 \
+  "9GhzM8e5fRoM+b5OAGfAv2HQqqOog9JsfCj3TjREdf7JhGeAIAhCGAs/"                   \
+  "3wAwAMjwBAC7pdHQBPIwHifACATdHdiX4sBayk0G+"                                  \
+  "19VdyEWOOLXK8AdCFWC0f5wAAFOAfPgesvcRQqFgV4CluWGoKTKAt4Olup2ZJIMvISBlWHnO50" \
+  "fWUcDoWmck5igUAAHBjumQpndi5qSAu+"                                           \
+  "2RKnKK5XMzEtEIg06kf66Tfx4ZMz1Q3nfoNprOHC5h+kTtLFaUxhFFYm89qYmLZhKF+"        \
+  "h0YQywuvFabB5JphZqURU8W0hjYqiF4fhyuXnXqXdRZMigCBCwaGzNaIAWzlgAsmxxwhU1H1uO" \
+  "PC4qUaPWDxURWwBwS/"                                                         \
+  "g0sFv5AE6KC6KF8Anowg3yMtWjA6qg8HygEwOinpdrtjYsJgSEGGwEiqi3IwZ4lsKElRsRDvAs" \
+  "YKwBXmtBk2tbC0ZY1ZXUuPMGGCqMuHp1XHDX3UoC+"                                  \
+  "UX9zoGRB19DemuhXgYyIYihW3GykLE9z3UsNEbUEMd2aKFXC69HrP5n+"                   \
+  "s3moUYlwUXA9esFLvXI+"                                                       \
+  "v7UVde0fwAADUYjKA2KsJGswMFQ6tBX4CAIDMl1N4z1MAAAAqGQCeNvSuBCZgBU9pQ29KYAJW8" \
+  "HTXUsoUGSgzDWbMXKnQADGacdErikwJSAAAACmYiEERUkZJEKmgIFrG8BYsrgIqQQkpiNIQgoW" \
+  "EOCLKMNX0b2NvYzdV1M6Yzr8gIogDm9ZCTfOJmZCo5RaToSEpnI50Do0jQ4mLuCwweuqE3iPz5" \
+  "Ua1UONIruPhDfWhHowMniJMJIs+rNc+UjRohCNPrtYiRLGxmphYta2KPYNxwqnz3BAfSR8+"    \
+  "r5vo6BH7ITRKPJgMw55pYxAjtzXdYUtYOUlsOwEPjisZOGZGwknTpdBIFnEBgxW45kCnII+"    \
+  "JFgUAnjHMZcoLGIfUpWMggzitIVayXObeOyO49pfWMeKET0sAIgghvjs5ZCDUqW+gI3QB/"     \
+  "jrBrcgBIbaerbWY0DEmnJkAACsA6Q2wJcNud1FAB+MQ2PmwhiVkBQD+NuwxBjlBVy+"         \
+  "j7elt2GIKLKj1Mm3PhwmwYs6ws1cd1+"                                            \
+  "biUjKOMTcBAAAAREXFKFEWEQhFaTFaBJSYQCgUF6NoAYQiFSIQMQ2xsdpYbKyG6ZitgTqy2hoD" \
+  "ajVtNXyZExYfJhjcFq4Lo/"                                                     \
+  "YiBlAwCmXUtWSuDFcmjxkh3iCUEHPG47pIAklUBSbXQym55pSOaBgbYtG7XNUnRN7O7SOot4ek" \
+  "OJjHycaH45rJ9el6TUKMIRfMNa9j7UEIugkBwgdrlTEJYRh3hgQCD4C8DjJkVrjgyFqiixylBs" \
+  "4ImGVZRFhpqC6Kl5cybwTJ9CQi4zJOajqANYToGaqJAyAMYQv4ZU64M3TrKELHDYHpidhtgTtW" \
+  "IXgAIGtHONjqd2vVRwIAxlAAoETnLWCKxCC6BXQ6AiD0RyBIkQkCFgCEsoTQB6ByY2wrkAmgTS" \
+  "bzumo8bcity2wT4AC+"                                                         \
+  "NmwiBbtMOg9jeF4DFuGDaSY1Xmw7gN4VKgAiIImU2zEBU1XprrhOwmYsdiAAAAAwRvhcDpfyKB" \
+  "VwuVyGAEIxoZtvQjdJAcTFBOI+"                                                 \
+  "EXeVSm5lLkogpKUeVnjkuq4Xx3XMmfD6cByzWonS8RjBIqCjq7DAgCNhMoEc36xgMsRFIsLKKP" \
+  "ABCwEADqUu0oP3cKhH+AxA7IjucHffIRFg4FKYTBgeUo5LzMp8un6P4+DDIzMcV+"           \
+  "b6njKB2nfHGmABk0G6XlgwAHYKHxFdz/"                                           \
+  "fSgoOFxpQsgHJPDqlT364nHBXXEhvHAVOlBMObCAfXJsNEW8VNDAHgCtVHTXsYoyNh9EQPco1I" \
+  "16PfOos28UYwlLBMdP7qnBTAba2w25wMGRTd9jxoW/"                                 \
+  "0LD8AQqtcXKXDqIYsjEgAgwm+MIGhxJJ1HG0DH6KIA2l5GaCOGjt+"                      \
+  "N4E7qRgZAqCsiwrsQmRFAyDNdM8DlOOZeAF4HXLwLphj0eSFne64DTjFFZSLq46XIYvB0V0RVC" \
+  "aQoInfFkLNLOVeVuE4sZjIHEgAAgJmVkBAkHRwAgZhAjJZKQGZHjBrIgPHZFvNMi8W08cHq7to" \
+  "oZ1/"                                                                       \
+  "bnTplsEyvk2IyOF4CJZFLDV+"                                                   \
+  "EEnDwpfTQqs3eexZGggtd4arG4ZXxXqLMWaquTbRJGLJIb4YBdM5L6XJGxOkrcoVDLqaKnKmDH" \
+  "FHHLfYMDEjmhCAXr1TKkXkC1Bi0F5CwRsd0gHoU2T6dggaYyJ0ALvh+"                    \
+  "3QpjABFwHKuw3wDovfPSTREWXBdhIe6UxALG+62jzGeQ04qVOSgC5EyahRCl+"              \
+  "cENZIZrYG6lHFm03BczCsVIO1pS9MNEh81Dp2s3pv/"                                 \
+  "aE7kJo25ZBeT4pml0gTAuF5abHe19EskfAfg+TDuffRgA0NuiuNGEEfjlebmjrE5hN/"        \
+  "cBZ0I8SQ9wOket640+"                                                         \
+  "FiaiT859USQNpVU8JMgANjZ0ySTjIuACT2NDF10wIxAe4OmuqqEylFkZFVlGWZWZu4c4t1gptZ" \
+  "WpvK6EUyUWkdlBJAAAkMYshYwaHkcRRTlLGAIx2lOCEiyniUAgaW6iss8SZevVSrr1uvIalYvh" \
+  "V+H6XY/"                                                                    \
+  "5I0tvxe+4vskAmWsh9R3utFYXWR+yB+"                                            \
+  "K9uTrjhI3Oul6dRKRkRhSxyOgsOYim6ruDEAioHRctK3dGQkYZjRPLZiPqbsZ316ha6WtSm0mL" \
+  "82GyBnN3TMBMVDHJwJEhOWK1MnARAw4K89uYGQDEI5TonZ5SXRgAOgpG4TLE0kk9YJHApSNw9j" \
+  "A9nTBSHL4Sa/TBpHTpQupIdUSoM3JIT6NvLJm0AteV/"                                \
+  "JgrQ2wAHWGSQLgVkDAMlPbqcuQyI5oG1LsXYDxvADWwilwj7ST6RgPMyhMDFAJcSDO8Y4LuqbU" \
+  "6XS7ekbjouHmmAwCEugMAAIy/"                                                  \
+  "DQA89rVFBlDIcLKVgAVc+UQCDBfOqdq7+"                                          \
+  "gkMSOAcdf9k1ZIZEcwrIRkwtMikFa8WOYBv3mUY9FGF9KsIEDiMDnUkmd2DmqD1wTAcH/"      \
+  "LhFQCs+STkJIWbf+"                                                           \
+  "i1noRUk8LNa4RaNStrliIy7ZmnJZ6KioqqiuB2ZkveACC5lSARpEUIHQGhRMBlROiLPgrqdPjo" \
+  "gN2JQIRiBgD4pYZpa2+KQ78cs9rMZCZTzafreLRabNW0cYBiiMDvAXwBBb2KvZavEyjk+"      \
+  "wIKepX0LF8nqJBv1ayhrChFxNjsdKRpGaG6jIzMVEZlDWUEIA3SdddcSYkTWjQFFAsYTACQKMZ" \
+  "O1oqYJ2Tbmf3GdFJMw960FYemjWlBDQeOY1htBFARwV4cDQTZkZIIGjYUGgX/A/"            \
+  "21UBF4jsKEUhJBf2C5eioYLxB6aqyuijIjK7I6iojILMuK6pBFBuw74jgO7aoq9BbPbaS4GiAw" \
+  "EgAAkGBJxEzEIEkEFoIhIKWQDAh3SuiC0KHdxMTFhAIxOLJV1wQMW8PE1vTl9eEZxxxhrmMbrx" \
+  "/MS0UeK9xSwuXSMUXQbhgycx0Mp3Tw4ZUwD2ASItldCUp1C2ktjhwrXjxkUcoMBh08CsMStQIZ" \
+  "nQDIizFhKHXoMJJDBhc8JcQT4vVOC2F1PQgHGRYmpBsjY2hA5nER5rjCVEuuFwGYXKPfDyEIWs" \
+  "TohzgCHVjWMyIk478QRFHTYme1mKYgFluL1dZG1c6hYczUsDHf5dN1VmsPXhcHr2EeA5ODQBYp" \
+  "wngdgH4bQXdo3T66QMAEwshJAACEwyJ1Obi+"                                       \
+  "zPXg03G8clwLdelg6Z2hrgBcj5UOXvn06XHleAsAAIAMAEkAdyjABX42lE6HPoLaZ1POlTwbGi" \
+  "dCjaD2kZwr+"                                                                \
+  "Q4VkQA4ogN7WiemLlehh56ejBAssocIkyQAAICQb4yDR8GCoeAR0CK0UITQRETANGFRyG7sC9x" \
+  "lSkTSjRIQERG1ADaIvxETVfXU8GMKID6iYpp4BMIlKmBQuH4TQBuSNXhFbSCTSTLkwyMQwszMg" \
+  "o54Z0R0hpQ0AZ0OnuIiw29a8ABmGNJkDg4e4YTeqbNokeh0xmnC5y0gpJ5Bj1BvPs11ZEmzJMb" \
+  "z0G0v3YFhwKJTTyIc6kOJq+d5GOFVDZiECVpHgA7oATQoo97QCBCXHhkmB0wy5NMxJK8Pr+"    \
+  "QKcxw1bQzDtLG3KFZMBXkNC5lkEljwgBMUGFoAAMYagAkmIqDbIQCEgF8Ljhzp0RE2AwAAbSa/" \
+  "ZIXJfHMB83oRAAAAafi2AExM4JxtN/"                                             \
+  "DESd8CtKpCoqoscAA2BjReBHlA7Dr+"                                             \
+  "HNEY0HgZmCD2ncGXE7prqIrKSFkZkRk1tW2cs2HvdjbD0cRyrq2T6rCxiakgAQAABKQRJAlmkk" \
+  "JAKBAVF5cKZFFxSkwoTmgxIRGAZjEKtCglJipmY2uLrWKI/"                            \
+  "WiYtn7Zy+"                                                                  \
+  "tWveCVuSbHdTBz5XjNdT2GZpl8RujoCNPweHEwMGGYzMzxLEshzDGZLImB15OIsBEYhvB5gsDM" \
+  "EeAHiGJoD07S7dBLmNetmMcpkSOvJGRmuOA1gdKPvI4jMzw+"                           \
+  "HT2BsUB1ZqLRRh6A4eARZq6FAN8SsJ4ajPLrBOBC54kTicW7LXMgsZANqjM90+ucyIQE+"      \
+  "H1DFQcMemZmH4mh9q0wc7wmU+kDx5UXMQVggMzpNHovTChAe/"                          \
+  "EA6feMuge9d3rJlADvHSxK6Mpo0EYgjT+"                                          \
+  "lN4DLlZ4t1YUZJwBdgogOevvBVmv29EsXHcYEAIb4qOJcu5TvtVOAaYiBALGE0ykWcDNs+"     \
+  "VALJFMcp9F1Fk+"                                                             \
+  "wwJmi2BpdTNSsUUrBItgBxJ6CAOCx8ID52yQZjpnXdV0cwlCG2DCmqaY9hnRcJ6cWSr8wmTurP" \
+  "gl89cRIzKBFcO739eUTHFGDJuXU77q8qhII6hA6APgEAp6Qo7xFV495hVEj1zVWI5KLtbUbJY/" \
+  "jlQdd0/"                                                                    \
+  "cyGKjgGIbvhVNcCwBs+"                                                        \
+  "VDDUEANE7nG1o6SDAnUMLFTeVERQTLhCAcQMpQPwmGZLNoaoa4IXSj0kbpjcmOkR7ggzG8dydA" \
+  "bPgixyHjNx8yaACT9fEbDNTnZ7ZPPa0FWTGfW00NVR9TbZtbUtiluzGZGAQTQv7AkEoSmiIebh" \
+  "zhAIABlEHq60XRvRYQoY0q0EDOtDqfMqPamA79tpsPWCccc88ehQ6ti2Nk4cGTnt8UUtbWO9j5" \
+  "WHEdmHh+7C9wIRcbudjS9CXjM7kaocWm3CfsmIKfZpvtHUVaURZ6uy8pxxLmzR5SEHNsXspeo+" \
+  "DDzw4x9MaFSHm6tIyWiFempO9P2dtOMiWnTObTzx19O+Msvi/htb6NqsZ/"                 \
+  "O3s7eMiJWj9dTFq5AEM9w6Lh/X076mm6aA9sCSIoJAHo13PxwXzBABvcU8c/"               \
+  "u7iQHUUTgzNM0Wo1dlmJRzDKZSAAAAAAwJwLO5a/4yl+5v++H209u/"                     \
+  "94v13Pn59nDcvtSL21xLUpndfvy7VA5//"                                          \
+  "w4f27LOL795PYo+f7974+"                                                      \
+  "SXupFFhk591zTOL79eH98nts4eItR07Oe9axnPeuZE0CZ27tPW1u3OzOKit91rOe+"          \
+  "1K7jFmVbZRyPx0AxaDGNyYnJp/7+eXL7yaB/9H/Pku1wvejpgenJsPvz+/"                 \
+  "+mrSgjkevxerzVlbPImoxrYbKeY8BJsnrRY89KMmG12IoUvs3b7eanpycFiVHdouYWg8lsNmt1" \
+  "FS+33+9zIiFVOq5jbU9nZ1MABYA2AQAAAAAAg0MAAAkAAACfd/skAWoWAKqz3++H+/"         \
+  "T0pGA6hrVD9XUrrc4zc3Nzc/r9sOZyIGq6bDnpYwKensDQ9y/"                          \
+  "K37+cAFtbW3TudhWOtwZzlstb/X5/a6vf72/"                                       \
+  "x5+fm5nB6slBlZ3Fcha363d5ut7u3ni1rLoPf728l3KcK"
+
+namespace {
+
+const int kToggleCount = 4;
+const int kNumChannels = 2;
+const int kSampleRate = 44100;
+const int kFramesPerBuffer = 882;  // 10ms
+const CefAudioHandler::ChannelLayout kChannelLayout = CEF_CHANNEL_LAYOUT_STEREO;
+
+const char kAudioOutputTestUrl[] = "http://tests/audiooutputtest";
+const char kAudioCloseBrowserTestUrl[] = "http://tests/audioclosebrowsertest";
+const char kAudioTogglePlaybackTestUrl[] =
+    "http://tests/audiotoggleplaybacktest";
+
+const char kTestHtml[] =
+    "<!DOCTYPE html><html><head><meta "
+    "charset=\"utf-8\"/></head><body><p>TEST</p><audio "
+    "id=\"audio_output_frame\" src=\"" AUDIO_DATA
+    "\" autoplay "
+    "style=\"display:none\"></audio></body></html>";
+
+const char kToggleTestHtml[] =
+    "<!DOCTYPE html><html><head><meta "
+    "charset=\"utf-8\"/><script type=\"text/javascript\">var timeouts = [ "
+    "1950, 150, 2000, 150, 2050, 150, 2100, 150, 2200, 150 "
+    "]; function togglePlayback(el, playing, count) { if (count < 10) { var "
+    "timeout = "
+    "timeouts[count]; if (playing) { el.pause(); } else { el.play(); } "
+    "setTimeout(togglePlayback, timeout, el, !playing, ++count); } } function "
+    "loadHandler() { var audioStarted = false; var el = "
+    "document.getElementById(\"audio_output_frame\"); el.onplay = (event) => "
+    "{ if (audioStarted) { return; } audioStarted = true; "
+    "setTimeout(togglePlayback, 150, el, audioStarted, 0); "
+    "}; }</script></head><body "
+    "onload=\"loadHandler()\"><p>TEST</p><audio "
+    "id=\"audio_output_frame\" src=\"" AUDIO_DATA
+    "\" autoplay "
+    "style=\"display:none\"></audio></body></html>";
+
+class AudioOutputTest : public ClientAppBrowser::Delegate {
+ public:
+  AudioOutputTest() {}
+
+  void OnBeforeCommandLineProcessing(
+      CefRefPtr<ClientAppBrowser> app,
+      CefRefPtr<CefCommandLine> command_line) override {
+    // Allow media to autoplay without requiring user interaction.
+    command_line->AppendSwitchWithValue("autoplay-policy",
+                                        "no-user-gesture-required");
+  }
+
+ protected:
+  IMPLEMENT_REFCOUNTING(AudioOutputTest);
+};
+
+class AudioTestHandler : public TestHandler, public CefAudioHandler {
+ public:
+  AudioTestHandler() {}
+
+  void SetupAudioTest(const std::string& testUrl) {
+    // Add the resource.
+    AddResource(testUrl, kTestHtml, "text/html");
+
+    // Create the browser.
+    CreateBrowser(testUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    browser_ = browser;
+    TestHandler::OnAfterCreated(browser);
+  }
+
+  CefRefPtr<CefAudioHandler> GetAudioHandler() override { return this; }
+
+  bool GetAudioParameters(CefRefPtr<CefBrowser> browser,
+                          CefAudioParameters& params) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    params.channel_layout = kChannelLayout;
+    params.sample_rate = kSampleRate;
+    params.frames_per_buffer = kFramesPerBuffer;
+    got_audio_parameters_.yes();
+    return true;
+  }
+
+  void OnAudioStreamStarted(CefRefPtr<CefBrowser> browser,
+                            const CefAudioParameters& params,
+                            int channels) override {
+    EXPECT_FALSE(got_on_audio_stream_started_);
+    EXPECT_TRUE(got_audio_parameters_);
+    EXPECT_TRUE(browser_->IsSame(browser));
+    EXPECT_EQ(channels, kNumChannels);
+    EXPECT_EQ(params.channel_layout, kChannelLayout);
+    EXPECT_EQ(params.sample_rate, kSampleRate);
+    EXPECT_EQ(params.frames_per_buffer, kFramesPerBuffer);
+    got_on_audio_stream_started_.yes();
+  }
+
+  void OnAudioStreamPacket(CefRefPtr<CefBrowser> browser,
+                           const float** data,
+                           int frames,
+                           int64 pts) override {
+    EXPECT_TRUE(got_on_audio_stream_started_);
+    EXPECT_TRUE(browser_->IsSame(browser));
+    EXPECT_EQ(frames, kFramesPerBuffer);
+    got_on_audio_stream_packet_.yes();
+  }
+
+  void OnAudioStreamStopped(CefRefPtr<CefBrowser> browser) override {
+    EXPECT_FALSE(got_on_audio_stream_stopped_);
+    EXPECT_TRUE(got_on_audio_stream_started_);
+    EXPECT_TRUE(browser_->IsSame(browser));
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    got_on_audio_stream_stopped_.yes();
+    DestroyTest();
+  }
+
+  void OnAudioStreamError(CefRefPtr<CefBrowser> browser,
+                          const CefString& message) override {
+    LOG(WARNING) << "OnAudioStreamError: message = " << message << ".";
+    got_on_audio_stream_error_.yes();
+    DestroyTest();
+  }
+
+ protected:
+  void DestroyTest() override {
+    browser_ = nullptr;
+    EXPECT_TRUE(got_audio_parameters_);
+    EXPECT_TRUE(got_on_audio_stream_started_);
+    EXPECT_TRUE(got_on_audio_stream_packet_);
+    EXPECT_TRUE(got_on_audio_stream_stopped_);
+    EXPECT_FALSE(got_on_audio_stream_error_);
+    TestHandler::DestroyTest();
+  }
+
+  CefRefPtr<CefBrowser> browser_;
+  TrackCallback got_on_audio_stream_started_;
+  TrackCallback got_on_audio_stream_packet_;
+  TrackCallback got_on_audio_stream_stopped_;
+  TrackCallback got_on_audio_stream_error_;
+  TrackCallback got_audio_parameters_;
+};
+
+// A common base class for audio output tests.
+class AudioOutputTestHandler : public AudioTestHandler {
+ public:
+  AudioOutputTestHandler() {}
+
+  void RunTest() override {
+    // Setup the resource.
+    SetupAudioTest(kAudioOutputTestUrl);
+  }
+
+  void OnAudioStreamPacket(CefRefPtr<CefBrowser> browser,
+                           const float** data,
+                           int frames,
+                           int64 pts) override {
+    if (!got_on_audio_stream_packet_.isSet()) {
+      browser->GetMainFrame()->ExecuteJavaScript(
+          "var ifr = document.getElementById(\"audio_output_frame\"); "
+          "ifr.parentNode.removeChild(ifr);",
+          CefString(), 0);
+    }
+    AudioTestHandler::OnAudioStreamPacket(browser, data, frames, pts);
+  }
+
+ protected:
+  IMPLEMENT_REFCOUNTING(AudioOutputTestHandler);
+};
+
+class AudioCloseBrowserTest : public AudioTestHandler {
+ public:
+  AudioCloseBrowserTest() {}
+
+  void RunTest() override {
+    // Setup the resource.
+    SetupAudioTest(kAudioCloseBrowserTestUrl);
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    EXPECT_FALSE(got_on_audio_stream_stopped_);
+    TestHandler::OnBeforeClose(browser);
+  }
+
+  void OnAudioStreamPacket(CefRefPtr<CefBrowser> browser,
+                           const float** data,
+                           int frames,
+                           int64 pts) override {
+    if (!got_on_audio_stream_packet_.isSet()) {
+      CloseBrowser(browser, true);
+    }
+    AudioTestHandler::OnAudioStreamPacket(browser, data, frames, pts);
+  }
+
+ protected:
+  IMPLEMENT_REFCOUNTING(AudioCloseBrowserTest);
+};
+
+// kToggleCount represents the times the OnAudioStreamStarted and
+// OnAudioStreamStopped should have been called.
+// Both get called on playback start and end respectively. As well as when there
+// is a period of silence for more than 2 seconds (see
+// CefBrowserHostImpl::OnAudioStateChangedand and
+// CefBrowserHostImpl::OnRecentlyAudibleTimerFired). The timeouts in kTestHtml
+// represent play and pause timeouts. So for example it should play for 150ms
+// and then pause for 1950ms. In this example OnAudioStreamStopped must not be
+// called because it is below the 2 seconds threshold. So in this 10 timeouts
+// there are exactly 3 which should trigger OnAudioStreamStarted and
+// OnAudioStreamStopped together with the initial stream start and the final
+// stream stop. And due to the need to test the toggle bounderies it results in
+// this nearly 15 seconds test run.
+class AudioTogglePlaybackTest : public AudioTestHandler {
+ public:
+  AudioTogglePlaybackTest() {}
+
+  void RunTest() override {
+    // Add the resource.
+    AddResource(kAudioTogglePlaybackTestUrl, kToggleTestHtml, "text/html");
+
+    // Create the browser.
+    CreateBrowser(kAudioTogglePlaybackTestUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout(20000);
+  }
+
+  void OnAudioStreamStarted(CefRefPtr<CefBrowser> browser,
+                            const CefAudioParameters& params,
+                            int channels) override {
+    EXPECT_TRUE(browser_->IsSame(browser));
+    EXPECT_EQ(channels, kNumChannels);
+    EXPECT_EQ(params.channel_layout, kChannelLayout);
+    EXPECT_EQ(params.sample_rate, kSampleRate);
+    EXPECT_EQ(params.frames_per_buffer, kFramesPerBuffer);
+    got_on_audio_stream_started_.yes();
+    start_count_++;
+  }
+
+  void OnAudioStreamStopped(CefRefPtr<CefBrowser> browser) override {
+    EXPECT_EQ(start_count_, ++stop_count_);
+    if (stop_count_ == kToggleCount)
+      AudioTestHandler::OnAudioStreamStopped(browser);
+  }
+
+ protected:
+  int start_count_ = 0;
+  int stop_count_ = 0;
+  IMPLEMENT_REFCOUNTING(AudioTogglePlaybackTest);
+};
+
+}  // namespace
+
+// Test audio output callbacks called on valid threads.
+TEST(AudioOutputTest, AudioOutputTest) {
+  CefRefPtr<AudioOutputTestHandler> handler = new AudioOutputTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test audio stream stopped callback is called on browser close.
+TEST(AudioOutputTest, AudioCloseBrowserTest) {
+  CefRefPtr<AudioCloseBrowserTest> handler = new AudioCloseBrowserTest();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test audio stream starts/stops properly on certain time bounderies.
+TEST(AudioOutputTest, AudioTogglePlaybackTest) {
+  CefRefPtr<AudioTogglePlaybackTest> handler = new AudioTogglePlaybackTest();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Entry point for creating audio output test objects.
+// Called from client_app_delegates.cc.
+void CreateAudioOutputTests(ClientAppBrowser::DelegateSet& delegates) {
+  delegates.insert(new AudioOutputTest);
+}
diff --git a/src/tests/ceftests/browser_info_map_unittest.cc b/src/tests/ceftests/browser_info_map_unittest.cc
new file mode 100644
index 0000000..ef53050
--- /dev/null
+++ b/src/tests/ceftests/browser_info_map_unittest.cc
@@ -0,0 +1,712 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <list>
+
+#include "libcef_dll/wrapper/cef_browser_info_map.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+struct MyObject {
+  MyObject(int val = 0) : member(val) {}
+  int member;
+};
+
+int g_destruct_ct = 0;
+
+struct MyObjectTraits {
+  static void Destruct(MyObject info) { g_destruct_ct++; }
+};
+
+typedef CefBrowserInfoMap<int, MyObject, MyObjectTraits> MyObjectMap;
+
+class MyVisitor : public MyObjectMap::Visitor {
+ public:
+  MyVisitor(bool remove = false,
+            int remove_browser_id = 0,
+            InfoIdType remove_info_id = 0)
+      : remove_(remove),
+        remove_browser_id_(remove_browser_id),
+        remove_info_id_(remove_info_id) {}
+
+  bool OnNextInfo(int browser_id,
+                  InfoIdType info_id,
+                  InfoObjectType info,
+                  bool* remove) override {
+    Info info_rec;
+    info_rec.browser_id = browser_id;
+    info_rec.info_id = info_id;
+    info_rec.info = info;
+    info_list_.push_back(info_rec);
+
+    // Based on test configuration remove no objects, all objects, or only the
+    // specified object.
+    *remove = remove_ ||
+              (browser_id == remove_browser_id_ && info_id == remove_info_id_);
+    return true;
+  }
+
+  // Returns true if the specified info was passed to OnNextInfo. Removes the
+  // record if found.
+  bool Exists(int browser_id, InfoIdType info_id, InfoObjectType info) {
+    InfoList::iterator it = info_list_.begin();
+    for (; it != info_list_.end(); ++it) {
+      const Info& found_info = *it;
+      if (browser_id == found_info.browser_id &&
+          info_id == found_info.info_id &&
+          info.member == found_info.info.member) {
+        info_list_.erase(it);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  size_t info_size() const { return info_list_.size(); }
+
+ private:
+  bool remove_;
+  int remove_browser_id_;
+  InfoIdType remove_info_id_;
+
+  struct Info {
+    int browser_id;
+    InfoIdType info_id;
+    InfoObjectType info;
+  };
+
+  // Track calls to OnNextInfo.
+  typedef std::list<Info> InfoList;
+  InfoList info_list_;
+};
+
+}  // namespace
+
+TEST(BrowserInfoMapTest, AddSingleBrowser) {
+  MyObjectMap map;
+  const int kBrowserId = 1;
+
+  g_destruct_ct = 0;
+
+  EXPECT_EQ(0U, map.size());
+  EXPECT_EQ(0U, map.size(kBrowserId));
+
+  MyObject obj1(1);
+  map.Add(kBrowserId, 1, obj1);
+  EXPECT_EQ(1U, map.size());
+  EXPECT_EQ(1U, map.size(kBrowserId));
+
+  MyObject obj2(2);
+  map.Add(kBrowserId, 2, obj2);
+  EXPECT_EQ(2U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId));
+
+  MyObject obj3(3);
+  map.Add(kBrowserId, 3, obj3);
+  EXPECT_EQ(3U, map.size());
+  EXPECT_EQ(3U, map.size(kBrowserId));
+
+  EXPECT_EQ(0, g_destruct_ct);
+
+  map.clear(kBrowserId);
+  EXPECT_EQ(0U, map.size());
+  EXPECT_EQ(0U, map.size(kBrowserId));
+
+  EXPECT_EQ(3, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, AddMultipleBrowsers) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  EXPECT_EQ(0U, map.size());
+  EXPECT_EQ(0U, map.size(kBrowserId1));
+  EXPECT_EQ(0U, map.size(kBrowserId2));
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+  EXPECT_EQ(1U, map.size());
+  EXPECT_EQ(1U, map.size(kBrowserId1));
+  EXPECT_EQ(0U, map.size(kBrowserId2));
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+  EXPECT_EQ(2U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(0U, map.size(kBrowserId2));
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+  EXPECT_EQ(3U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(1U, map.size(kBrowserId2));
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  EXPECT_EQ(0, g_destruct_ct);
+
+  map.clear(kBrowserId1);
+  EXPECT_EQ(2U, map.size());
+  EXPECT_EQ(0U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  EXPECT_EQ(2, g_destruct_ct);
+
+  map.clear(kBrowserId2);
+  EXPECT_EQ(0U, map.size());
+  EXPECT_EQ(0U, map.size(kBrowserId1));
+  EXPECT_EQ(0U, map.size(kBrowserId2));
+
+  EXPECT_EQ(4, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindSingleBrowser) {
+  MyObjectMap map;
+  const int kBrowserId = 1;
+
+  g_destruct_ct = 0;
+
+  // obj1 not added yet.
+  MyObject nf1 = map.Find(kBrowserId, 1, nullptr);
+  EXPECT_EQ(0, nf1.member);
+
+  MyObject obj1(1);
+  map.Add(kBrowserId, 1, obj1);
+
+  // obj1 should exist.
+  MyObject f1 = map.Find(kBrowserId, 1, nullptr);
+  EXPECT_EQ(obj1.member, f1.member);
+
+  // obj2 not added yet.
+  MyObject nf2 = map.Find(kBrowserId, 2, nullptr);
+  EXPECT_EQ(0, nf2.member);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId, 2, obj2);
+
+  // obj2 should exist.
+  MyObject f2 = map.Find(kBrowserId, 2, nullptr);
+  EXPECT_EQ(obj2.member, f2.member);
+
+  // find obj1 again.
+  MyObject f1b = map.Find(kBrowserId, 1, nullptr);
+  EXPECT_EQ(obj1.member, f1b.member);
+
+  // find obj2 again.
+  MyObject f2b = map.Find(kBrowserId, 2, nullptr);
+  EXPECT_EQ(obj2.member, f2b.member);
+
+  // doesn't exist.
+  MyObject nf3 = map.Find(kBrowserId, 3, nullptr);
+  EXPECT_EQ(0, nf3.member);
+  MyObject nf4 = map.Find(10, 1, nullptr);
+  EXPECT_EQ(0, nf4.member);
+  MyObject nf5 = map.Find(10, 3, nullptr);
+  EXPECT_EQ(0, nf5.member);
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  EXPECT_EQ(2, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindMultipleBrowsers) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  // obj1 not added yet.
+  MyObject nf1 = map.Find(kBrowserId1, 1, nullptr);
+  EXPECT_EQ(0, nf1.member);
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  // obj1 should exist.
+  MyObject f1 = map.Find(kBrowserId1, 1, nullptr);
+  EXPECT_EQ(obj1.member, f1.member);
+
+  // obj2 not added yet.
+  MyObject nf2 = map.Find(kBrowserId1, 2, nullptr);
+  EXPECT_EQ(0, nf2.member);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  // obj2 should exist.
+  MyObject f2 = map.Find(kBrowserId1, 2, nullptr);
+  EXPECT_EQ(obj2.member, f2.member);
+
+  // obj3 not added yet.
+  MyObject nf3 = map.Find(kBrowserId2, 3, nullptr);
+  EXPECT_EQ(0, nf3.member);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  // obj3 should exist.
+  MyObject f3 = map.Find(kBrowserId2, 3, nullptr);
+  EXPECT_EQ(obj3.member, f3.member);
+
+  // obj4 not added yet.
+  MyObject nf4 = map.Find(kBrowserId2, 4, nullptr);
+  EXPECT_EQ(0, nf4.member);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  // obj4 should exist.
+  MyObject f4 = map.Find(kBrowserId2, 4, nullptr);
+  EXPECT_EQ(obj4.member, f4.member);
+
+  // obj1-3 should exist.
+  MyObject f1b = map.Find(kBrowserId1, 1, nullptr);
+  EXPECT_EQ(obj1.member, f1b.member);
+  MyObject f2b = map.Find(kBrowserId1, 2, nullptr);
+  EXPECT_EQ(obj2.member, f2b.member);
+  MyObject f3b = map.Find(kBrowserId2, 3, nullptr);
+  EXPECT_EQ(obj3.member, f3b.member);
+
+  // wrong browser
+  MyObject nf5 = map.Find(kBrowserId1, 4, nullptr);
+  EXPECT_EQ(0, nf5.member);
+  MyObject nf6 = map.Find(kBrowserId2, 1, nullptr);
+  EXPECT_EQ(0, nf6.member);
+
+  // deosn't exist
+  MyObject nf7 = map.Find(kBrowserId2, 5, nullptr);
+  EXPECT_EQ(0, nf7.member);
+  MyObject nf8 = map.Find(8, 1, nullptr);
+  EXPECT_EQ(0, nf8.member);
+  MyObject nf9 = map.Find(8, 10, nullptr);
+  EXPECT_EQ(0, nf9.member);
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  EXPECT_EQ(4, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, Find) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should only visit the single object
+  MyVisitor visitor;
+  map.Find(kBrowserId2, 4, &visitor);
+  EXPECT_EQ(1U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  EXPECT_EQ(4, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindAndRemove) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should only visit and remove the single object.
+  MyVisitor visitor(true);
+  map.Find(kBrowserId1, 2, &visitor);
+  EXPECT_EQ(1U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(3U, map.size());
+  EXPECT_EQ(1U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // visited object shouldn't exist
+  MyObject nf2 = map.Find(kBrowserId1, 2, nullptr);
+  EXPECT_EQ(0, nf2.member);
+
+  // other objects should exist
+  MyObject nf1 = map.Find(kBrowserId1, 1, nullptr);
+  EXPECT_EQ(obj1.member, nf1.member);
+  MyObject nf3 = map.Find(kBrowserId2, 3, nullptr);
+  EXPECT_EQ(obj3.member, nf3.member);
+  MyObject nf4 = map.Find(kBrowserId2, 4, nullptr);
+  EXPECT_EQ(obj4.member, nf4.member);
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  // should destruct the remaining 3 objects
+  EXPECT_EQ(3, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindAll) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should visit all objects
+  MyVisitor visitor;
+  map.FindAll(&visitor);
+  EXPECT_EQ(4U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
+  // should be no unexpected visits
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  EXPECT_EQ(4, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindAllByBrowser) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should only visit browser2 objects
+  MyVisitor visitor;
+  map.FindAll(kBrowserId2, &visitor);
+  EXPECT_EQ(2U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
+  // should be no unexpected visits
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  EXPECT_EQ(4, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindAllAndRemoveAll) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should visit all objects
+  MyVisitor visitor(true);
+  map.FindAll(&visitor);
+  EXPECT_EQ(4U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
+  // should be no unexpected visits
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(0U, map.size());
+  EXPECT_EQ(0U, map.size(kBrowserId1));
+  EXPECT_EQ(0U, map.size(kBrowserId2));
+
+  EXPECT_EQ(0, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindAllAndRemoveOne) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should visit all objects and remove one
+  MyVisitor visitor(false, kBrowserId2, 3);
+  map.FindAll(&visitor);
+  EXPECT_EQ(4U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
+  // should be no unexpected visits
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(3U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(1U, map.size(kBrowserId2));
+
+  // removed object shouldn't exist
+  MyObject nf3 = map.Find(kBrowserId2, 3, nullptr);
+  EXPECT_EQ(0, nf3.member);
+
+  // other objects should exist
+  MyObject f1 = map.Find(kBrowserId1, 1, nullptr);
+  EXPECT_EQ(obj1.member, f1.member);
+  MyObject f2 = map.Find(kBrowserId1, 2, nullptr);
+  EXPECT_EQ(obj2.member, f2.member);
+  MyObject f4 = map.Find(kBrowserId2, 4, nullptr);
+  EXPECT_EQ(obj4.member, f4.member);
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  // should destruct the remaining 3 objects
+  EXPECT_EQ(3, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindAllAndRemoveAllByBrowser) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should only visit browser1 objects
+  MyVisitor visitor(true);
+  map.FindAll(kBrowserId1, &visitor);
+  EXPECT_EQ(2U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
+  EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
+  // should be no unexpected visits
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(2U, map.size());
+  EXPECT_EQ(0U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // browser1 objects shouldn't exist
+  MyObject nf1 = map.Find(kBrowserId1, 1, nullptr);
+  EXPECT_EQ(0, nf1.member);
+  MyObject nf2 = map.Find(kBrowserId1, 2, nullptr);
+  EXPECT_EQ(0, nf2.member);
+
+  // browser 2 objects should exist
+  MyObject f3 = map.Find(kBrowserId2, 3, nullptr);
+  EXPECT_EQ(obj3.member, f3.member);
+  MyObject f4 = map.Find(kBrowserId2, 4, nullptr);
+  EXPECT_EQ(obj4.member, f4.member);
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  // should destruct the remaining 2 objects
+  EXPECT_EQ(2, g_destruct_ct);
+}
+
+TEST(BrowserInfoMapTest, FindAllAndRemoveOneByBrowser) {
+  MyObjectMap map;
+  const int kBrowserId1 = 1;
+  const int kBrowserId2 = 2;
+
+  g_destruct_ct = 0;
+
+  MyObject obj1(1);
+  map.Add(kBrowserId1, 1, obj1);
+
+  MyObject obj2(2);
+  map.Add(kBrowserId1, 2, obj2);
+
+  MyObject obj3(3);
+  map.Add(kBrowserId2, 3, obj3);
+
+  MyObject obj4(4);
+  map.Add(kBrowserId2, 4, obj4);
+
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(2U, map.size(kBrowserId2));
+
+  // should visit browser2 objects and remove one
+  MyVisitor visitor(false, kBrowserId2, 4);
+  map.FindAll(kBrowserId2, &visitor);
+  EXPECT_EQ(2U, visitor.info_size());
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
+  EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
+  // should be no unexpected visits
+  EXPECT_EQ(0U, visitor.info_size());
+
+  EXPECT_EQ(3U, map.size());
+  EXPECT_EQ(2U, map.size(kBrowserId1));
+  EXPECT_EQ(1U, map.size(kBrowserId2));
+
+  // removed object shouldn't exist
+  MyObject nf4 = map.Find(kBrowserId2, 4, nullptr);
+  EXPECT_EQ(0, nf4.member);
+
+  // other objects should exist
+  MyObject f1 = map.Find(kBrowserId1, 1, nullptr);
+  EXPECT_EQ(obj1.member, f1.member);
+  MyObject f2 = map.Find(kBrowserId1, 2, nullptr);
+  EXPECT_EQ(obj2.member, f2.member);
+  MyObject f3 = map.Find(kBrowserId2, 3, nullptr);
+  EXPECT_EQ(obj3.member, f3.member);
+
+  EXPECT_EQ(0, g_destruct_ct);
+  map.clear();
+  // should destruct the remaining 3 objects
+  EXPECT_EQ(3, g_destruct_ct);
+}
+
+namespace {
+
+class MyHeapObject {
+ public:
+  MyHeapObject(int* destroy_ct) : destroy_ct_(destroy_ct) {}
+  ~MyHeapObject() { (*destroy_ct_)++; }
+
+ private:
+  int* destroy_ct_;
+};
+
+}  // namespace
+
+TEST(BrowserInfoMapTest, DefaultTraits) {
+  CefBrowserInfoMap<int, MyHeapObject*> map;
+
+  int destroy_ct = 0;
+  map.Add(1, 1, new MyHeapObject(&destroy_ct));
+  map.Add(1, 2, new MyHeapObject(&destroy_ct));
+  map.Add(2, 1, new MyHeapObject(&destroy_ct));
+  map.Add(2, 2, new MyHeapObject(&destroy_ct));
+  map.Add(3, 1, new MyHeapObject(&destroy_ct));
+  map.Add(3, 2, new MyHeapObject(&destroy_ct));
+
+  EXPECT_EQ(6U, map.size());
+
+  map.clear(1);
+  EXPECT_EQ(4U, map.size());
+  EXPECT_EQ(2, destroy_ct);
+
+  map.clear();
+  EXPECT_EQ(0U, map.size());
+  EXPECT_EQ(6, destroy_ct);
+}
diff --git a/src/tests/ceftests/client_app_delegates.cc b/src/tests/ceftests/client_app_delegates.cc
new file mode 100644
index 0000000..a577a53
--- /dev/null
+++ b/src/tests/ceftests/client_app_delegates.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppBrowser;
+using client::ClientAppRenderer;
+
+void CreateBrowserDelegates(ClientAppBrowser::DelegateSet& delegates) {
+  // Bring in audio output tests.
+  extern void CreateAudioOutputTests(ClientAppBrowser::DelegateSet & delegates);
+  CreateAudioOutputTests(delegates);
+
+  // Bring in the Navigation tests.
+  extern void CreateNavigationBrowserTests(ClientAppBrowser::DelegateSet &
+                                           delegates);
+  CreateNavigationBrowserTests(delegates);
+
+  // Bring in the plugin tests.
+  extern void CreatePluginBrowserTests(ClientAppBrowser::DelegateSet &
+                                       delegates);
+  CreatePluginBrowserTests(delegates);
+
+  // Bring in the preference tests.
+  extern void CreatePreferenceBrowserTests(ClientAppBrowser::DelegateSet &
+                                           delegates);
+  CreatePreferenceBrowserTests(delegates);
+}
+
+void CreateRenderDelegates(ClientAppRenderer::DelegateSet& delegates) {
+  // Bring in the Frame tests.
+  extern void CreateFrameRendererTests(ClientAppRenderer::DelegateSet &
+                                       delegates);
+  CreateFrameRendererTests(delegates);
+
+  // Bring in the DOM tests.
+  extern void CreateDOMRendererTests(ClientAppRenderer::DelegateSet &
+                                     delegates);
+  CreateDOMRendererTests(delegates);
+
+  // Bring in the message router tests.
+  extern void CreateMessageRouterRendererTests(ClientAppRenderer::DelegateSet &
+                                               delegates);
+  CreateMessageRouterRendererTests(delegates);
+
+  // Bring in the Navigation tests.
+  extern void CreateNavigationRendererTests(ClientAppRenderer::DelegateSet &
+                                            delegates);
+  CreateNavigationRendererTests(delegates);
+
+  // Bring in the process message tests.
+  extern void CreateProcessMessageRendererTests(ClientAppRenderer::DelegateSet &
+                                                delegates);
+  CreateProcessMessageRendererTests(delegates);
+
+  // Bring in the RequestHandler tests.
+  extern void CreateRequestHandlerRendererTests(ClientAppRenderer::DelegateSet &
+                                                delegates);
+  CreateRequestHandlerRendererTests(delegates);
+
+  // Bring in the routing test handler delegate.
+  extern void CreateRoutingTestHandlerDelegate(ClientAppRenderer::DelegateSet &
+                                               delegates);
+  CreateRoutingTestHandlerDelegate(delegates);
+
+  // Bring in the thread tests.
+  extern void CreateThreadRendererTests(ClientAppRenderer::DelegateSet &
+                                        delegates);
+  CreateThreadRendererTests(delegates);
+
+  // Bring in the URLRequest tests.
+  extern void CreateURLRequestRendererTests(ClientAppRenderer::DelegateSet &
+                                            delegates);
+  CreateURLRequestRendererTests(delegates);
+
+  // Bring in the V8 tests.
+  extern void CreateV8RendererTests(ClientAppRenderer::DelegateSet & delegates);
+  CreateV8RendererTests(delegates);
+}
+
+void RegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar,
+                           std::vector<CefString>& cookiable_schemes) {
+  // Bring in the scheme handler tests.
+  extern void RegisterSchemeHandlerCustomSchemes(
+      CefRawPtr<CefSchemeRegistrar> registrar,
+      std::vector<CefString> & cookiable_schemes);
+  RegisterSchemeHandlerCustomSchemes(registrar, cookiable_schemes);
+
+  // Bring in the cookie tests.
+  extern void RegisterCookieCustomSchemes(
+      CefRawPtr<CefSchemeRegistrar> registrar,
+      std::vector<CefString> & cookiable_schemes);
+  RegisterCookieCustomSchemes(registrar, cookiable_schemes);
+
+  // Bring in the URLRequest tests.
+  extern void RegisterURLRequestCustomSchemes(
+      CefRawPtr<CefSchemeRegistrar> registrar,
+      std::vector<CefString> & cookiable_schemes);
+  RegisterURLRequestCustomSchemes(registrar, cookiable_schemes);
+
+  // Bring in the resource request handler tests.
+  extern void RegisterResourceRequestHandlerCustomSchemes(
+      CefRawPtr<CefSchemeRegistrar> registrar,
+      std::vector<CefString> & cookiable_schemes);
+  RegisterResourceRequestHandlerCustomSchemes(registrar, cookiable_schemes);
+}
+
+namespace client {
+
+// static
+void ClientAppBrowser::CreateDelegates(DelegateSet& delegates) {
+  ::CreateBrowserDelegates(delegates);
+}
+
+// static
+CefRefPtr<CefPrintHandler> ClientAppBrowser::CreatePrintHandler() {
+  return nullptr;
+}
+
+// static
+void ClientAppRenderer::CreateDelegates(DelegateSet& delegates) {
+  ::CreateRenderDelegates(delegates);
+}
+
+// static
+void ClientApp::RegisterCustomSchemes(
+    CefRawPtr<CefSchemeRegistrar> registrar,
+    std::vector<CefString>& cookiable_schemes) {
+  ::RegisterCustomSchemes(registrar, cookiable_schemes);
+}
+
+}  // namespace client
diff --git a/src/tests/ceftests/command_line_unittest.cc b/src/tests/ceftests/command_line_unittest.cc
new file mode 100644
index 0000000..abb71c4
--- /dev/null
+++ b/src/tests/ceftests/command_line_unittest.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_command_line.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void VerifyCommandLine(CefRefPtr<CefCommandLine> command_line,
+                       const char* expected_arg1 = "arg1",
+                       const char* expected_arg2 = "arg 2") {
+  std::string program = command_line->GetProgram();
+  EXPECT_EQ("test.exe", program);
+
+  EXPECT_TRUE(command_line->HasSwitches());
+
+  EXPECT_TRUE(command_line->HasSwitch("switch1"));
+  std::string switch1 = command_line->GetSwitchValue("switch1");
+  EXPECT_EQ("", switch1);
+  EXPECT_TRUE(command_line->HasSwitch("switch2"));
+  std::string switch2 = command_line->GetSwitchValue("switch2");
+  EXPECT_EQ("val2", switch2);
+  EXPECT_TRUE(command_line->HasSwitch("switch3"));
+  std::string switch3 = command_line->GetSwitchValue("switch3");
+  EXPECT_EQ("val3", switch3);
+  EXPECT_TRUE(command_line->HasSwitch("switch4"));
+  std::string switch4 = command_line->GetSwitchValue("switch4");
+  EXPECT_EQ("val 4", switch4);
+  EXPECT_FALSE(command_line->HasSwitch("switchnoexist"));
+
+  CefCommandLine::SwitchMap switches;
+  command_line->GetSwitches(switches);
+  EXPECT_EQ((size_t)4, switches.size());
+
+  bool has1 = false, has2 = false, has3 = false, has4 = false;
+
+  CefCommandLine::SwitchMap::const_iterator it = switches.begin();
+  for (; it != switches.end(); ++it) {
+    std::string name = it->first;
+    std::string val = it->second;
+
+    if (name == "switch1") {
+      has1 = true;
+      EXPECT_EQ("", val);
+    } else if (name == "switch2") {
+      has2 = true;
+      EXPECT_EQ("val2", val);
+    } else if (name == "switch3") {
+      has3 = true;
+      EXPECT_EQ("val3", val);
+    } else if (name == "switch4") {
+      has4 = true;
+      EXPECT_EQ("val 4", val);
+    }
+  }
+
+  EXPECT_TRUE(has1);
+  EXPECT_TRUE(has2);
+  EXPECT_TRUE(has3);
+  EXPECT_TRUE(has4);
+
+  EXPECT_TRUE(command_line->HasArguments());
+
+  CefCommandLine::ArgumentList args;
+  command_line->GetArguments(args);
+  EXPECT_EQ((size_t)2, args.size());
+  std::string arg0 = args[0];
+  EXPECT_EQ(expected_arg1, arg0);
+  std::string arg1 = args[1];
+  EXPECT_EQ(expected_arg2, arg1);
+
+  command_line->Reset();
+  EXPECT_FALSE(command_line->HasSwitches());
+  EXPECT_FALSE(command_line->HasArguments());
+  std::string cur_program = command_line->GetProgram();
+  EXPECT_EQ(program, cur_program);
+}
+
+}  // namespace
+
+// Test creating a command line from argc/argv or string.
+TEST(CommandLineTest, Init) {
+  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+  EXPECT_TRUE(command_line.get() != nullptr);
+
+#if defined(OS_WIN)
+  command_line->InitFromString(
+      "test.exe --switch1 -switch2=val2 /switch3=val3 "
+      "-switch4=\"val 4\" arg1 \"arg 2\"");
+#else
+  const char* args[] = {"test.exe",      "--switch1",      "-switch2=val2",
+                        "-switch3=val3", "-switch4=val 4", "arg1",
+                        "arg 2"};
+  command_line->InitFromArgv(sizeof(args) / sizeof(char*), args);
+#endif
+
+  VerifyCommandLine(command_line);
+}
+
+// Test creating a command line using set and append methods.
+TEST(CommandLineTest, Manual) {
+  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+  EXPECT_TRUE(command_line.get() != nullptr);
+
+  command_line->SetProgram("test.exe");
+  command_line->AppendSwitch("switch1");
+  command_line->AppendSwitchWithValue("switch2", "val2");
+  command_line->AppendSwitchWithValue("switch3", "val3");
+  command_line->AppendSwitchWithValue("switch4", "val 4");
+  command_line->AppendArgument("arg1");
+  command_line->AppendArgument("arg 2");
+
+  VerifyCommandLine(command_line);
+}
+
+// Test that any prefixes included with the switches are ignored.
+TEST(CommandLineTest, IgnorePrefixes) {
+  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+  EXPECT_TRUE(command_line.get() != nullptr);
+
+  command_line->SetProgram("test.exe");
+  command_line->AppendSwitch("-switch1");
+  command_line->AppendSwitchWithValue("--switch2", "val2");
+  command_line->AppendSwitchWithValue("-switch3", "val3");
+  command_line->AppendSwitchWithValue("-switch4", "val 4");
+
+  // Prefixes will not be removed from arguments.
+  const char arg1[] = "-arg1";
+  const char arg2[] = "--arg 2";
+  command_line->AppendArgument(arg1);
+  command_line->AppendArgument(arg2);
+
+  VerifyCommandLine(command_line, arg1, arg2);
+}
diff --git a/src/tests/ceftests/cookie_unittest.cc b/src/tests/ceftests/cookie_unittest.cc
new file mode 100644
index 0000000..e4672a1
--- /dev/null
+++ b/src/tests/ceftests/cookie_unittest.cc
@@ -0,0 +1,2413 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <vector>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/cef_cookie.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+#include "include/cef_server.h"
+#include "include/cef_waitable_event.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_suite.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char* kTestUrl = "http://www.test.com/path/to/cookietest/foo.html";
+const char* kTestDomain = "www.test.com";
+const char* kTestPath = "/path/to/cookietest";
+
+const int kIgnoreNumDeleted = -2;
+
+typedef std::vector<CefCookie> CookieVector;
+
+class TestCompletionCallback : public CefCompletionCallback {
+ public:
+  explicit TestCompletionCallback(CefRefPtr<CefWaitableEvent> event)
+      : event_(event) {}
+
+  void OnComplete() override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    event_->Signal();
+  }
+
+ private:
+  CefRefPtr<CefWaitableEvent> event_;
+
+  IMPLEMENT_REFCOUNTING(TestCompletionCallback);
+  DISALLOW_COPY_AND_ASSIGN(TestCompletionCallback);
+};
+
+class TestSetCookieCallback : public CefSetCookieCallback {
+ public:
+  TestSetCookieCallback(bool expected_success,
+                        CefRefPtr<CefWaitableEvent> event)
+      : expected_success_(expected_success), event_(event) {}
+
+  void OnComplete(bool success) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_EQ(expected_success_, success);
+    event_->Signal();
+  }
+
+ private:
+  bool expected_success_;
+  CefRefPtr<CefWaitableEvent> event_;
+
+  IMPLEMENT_REFCOUNTING(TestSetCookieCallback);
+  DISALLOW_COPY_AND_ASSIGN(TestSetCookieCallback);
+};
+
+class TestDeleteCookiesCallback : public CefDeleteCookiesCallback {
+ public:
+  TestDeleteCookiesCallback(int expected_num_deleted,
+                            CefRefPtr<CefWaitableEvent> event)
+      : expected_num_deleted_(expected_num_deleted), event_(event) {}
+
+  void OnComplete(int num_deleted) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    if (expected_num_deleted_ != kIgnoreNumDeleted) {
+      EXPECT_EQ(expected_num_deleted_, num_deleted);
+    }
+    event_->Signal();
+  }
+
+ private:
+  int expected_num_deleted_;
+  CefRefPtr<CefWaitableEvent> event_;
+
+  IMPLEMENT_REFCOUNTING(TestDeleteCookiesCallback);
+  DISALLOW_COPY_AND_ASSIGN(TestDeleteCookiesCallback);
+};
+
+class TestVisitor : public CefCookieVisitor {
+ public:
+  TestVisitor(CookieVector* cookies,
+              bool deleteCookies,
+              const base::Closure& callback)
+      : cookies_(cookies), delete_cookies_(deleteCookies), callback_(callback) {
+    EXPECT_TRUE(cookies_);
+    EXPECT_FALSE(callback_.is_null());
+  }
+  ~TestVisitor() override { callback_.Run(); }
+
+  bool Visit(const CefCookie& cookie,
+             int count,
+             int total,
+             bool& deleteCookie) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    cookies_->push_back(cookie);
+    if (delete_cookies_)
+      deleteCookie = true;
+    return true;
+  }
+
+ private:
+  CookieVector* cookies_;
+  bool delete_cookies_;
+  base::Closure callback_;
+
+  IMPLEMENT_REFCOUNTING(TestVisitor);
+};
+
+// Set the cookies.
+void SetCookies(CefRefPtr<CefCookieManager> manager,
+                const CefString& url,
+                const CookieVector& cookies,
+                bool expected_success,
+                CefRefPtr<CefWaitableEvent> event) {
+  CookieVector::const_iterator it = cookies.begin();
+  for (; it != cookies.end(); ++it) {
+    EXPECT_TRUE(manager->SetCookie(
+        url, *it, new TestSetCookieCallback(expected_success, event)));
+    event->Wait();
+  }
+}
+
+// Delete the cookie.
+void DeleteCookies(CefRefPtr<CefCookieManager> manager,
+                   const CefString& url,
+                   const CefString& cookie_name,
+                   int expected_num_deleted,
+                   CefRefPtr<CefWaitableEvent> event) {
+  EXPECT_TRUE(manager->DeleteCookies(
+      url, cookie_name,
+      new TestDeleteCookiesCallback(expected_num_deleted, event)));
+  event->Wait();
+}
+
+// Create a test cookie. If |withDomain| is true a domain cookie will be
+// created, otherwise a host cookie will be created.
+void CreateCookie(CefRefPtr<CefCookieManager> manager,
+                  CefCookie& cookie,
+                  bool withDomain,
+                  bool sessionCookie,
+                  CefRefPtr<CefWaitableEvent> event) {
+  CefString(&cookie.name).FromASCII("my_cookie");
+  CefString(&cookie.value).FromASCII("My Value");
+  if (withDomain)
+    CefString(&cookie.domain).FromASCII(kTestDomain);
+  CefString(&cookie.path).FromASCII(kTestPath);
+  if (!sessionCookie) {
+    cookie.has_expires = true;
+    cookie.expires.year = 2200;
+    cookie.expires.month = 4;
+    cookie.expires.day_of_week = 5;
+    cookie.expires.day_of_month = 11;
+  }
+
+  CookieVector cookies;
+  cookies.push_back(cookie);
+
+  SetCookies(manager, kTestUrl, cookies, true, event);
+}
+
+// Visit URL cookies. Execute |callback| on completion.
+void VisitUrlCookies(CefRefPtr<CefCookieManager> manager,
+                     const CefString& url,
+                     bool includeHttpOnly,
+                     CookieVector& cookies,
+                     bool deleteCookies,
+                     const base::Closure& callback) {
+  EXPECT_TRUE(manager->VisitUrlCookies(
+      url, includeHttpOnly,
+      new TestVisitor(&cookies, deleteCookies, callback)));
+}
+
+// Visit URL cookies. Block on |event|.
+void VisitUrlCookies(CefRefPtr<CefCookieManager> manager,
+                     const CefString& url,
+                     bool includeHttpOnly,
+                     CookieVector& cookies,
+                     bool deleteCookies,
+                     CefRefPtr<CefWaitableEvent> event) {
+  VisitUrlCookies(manager, url, includeHttpOnly, cookies, deleteCookies,
+                  base::Bind(&CefWaitableEvent::Signal, event));
+  event->Wait();
+}
+
+// Visit all cookies. Execute |callback| on completion.
+void VisitAllCookies(CefRefPtr<CefCookieManager> manager,
+                     CookieVector& cookies,
+                     bool deleteCookies,
+                     const base::Closure& callback) {
+  EXPECT_TRUE(manager->VisitAllCookies(
+      new TestVisitor(&cookies, deleteCookies, callback)));
+}
+
+// Visit all cookies. Block on |event|.
+void VisitAllCookies(CefRefPtr<CefCookieManager> manager,
+                     CookieVector& cookies,
+                     bool deleteCookies,
+                     CefRefPtr<CefWaitableEvent> event) {
+  VisitAllCookies(manager, cookies, deleteCookies,
+                  base::Bind(&CefWaitableEvent::Signal, event));
+  event->Wait();
+}
+
+// Retrieve the test cookie. If |withDomain| is true check that the cookie
+// is a domain cookie, otherwise a host cookie. if |deleteCookies| is true
+// the cookie will be deleted when it's retrieved.
+void GetCookie(CefRefPtr<CefCookieManager> manager,
+               const CefCookie& cookie,
+               bool withDomain,
+               CefRefPtr<CefWaitableEvent> event,
+               bool deleteCookies) {
+  CookieVector cookies;
+
+  // Get the cookie and delete it.
+  VisitUrlCookies(manager, kTestUrl, false, cookies, deleteCookies, event);
+
+  EXPECT_EQ(1U, cookies.size());
+  if (cookies.size() != 1U)
+    return;
+
+  const CefCookie& cookie_read = cookies[0];
+  EXPECT_EQ(CefString(&cookie_read.name), "my_cookie");
+  EXPECT_EQ(CefString(&cookie_read.value), "My Value");
+  if (withDomain)
+    EXPECT_EQ(CefString(&cookie_read.domain), ".www.test.com");
+  else
+    EXPECT_EQ(CefString(&cookie_read.domain), kTestDomain);
+  EXPECT_EQ(CefString(&cookie_read.path), kTestPath);
+  EXPECT_EQ(cookie.has_expires, cookie_read.has_expires);
+  EXPECT_EQ(cookie.expires.year, cookie_read.expires.year);
+  EXPECT_EQ(cookie.expires.month, cookie_read.expires.month);
+  EXPECT_EQ(cookie.expires.day_of_week, cookie_read.expires.day_of_week);
+  EXPECT_EQ(cookie.expires.day_of_month, cookie_read.expires.day_of_month);
+  EXPECT_EQ(cookie.expires.hour, cookie_read.expires.hour);
+  EXPECT_EQ(cookie.expires.minute, cookie_read.expires.minute);
+  EXPECT_EQ(cookie.expires.second, cookie_read.expires.second);
+  EXPECT_EQ(cookie.expires.millisecond, cookie_read.expires.millisecond);
+  EXPECT_EQ(cookie.same_site, cookie_read.same_site);
+  EXPECT_EQ(cookie.priority, cookie_read.priority);
+}
+
+// Verify that no cookies exist. If |withUrl| is true it will only check for
+// cookies matching the URL.
+void VerifyNoCookies(CefRefPtr<CefCookieManager> manager,
+                     CefRefPtr<CefWaitableEvent> event,
+                     bool withUrl) {
+  CookieVector cookies;
+
+  // Verify that the cookie has been deleted.
+  if (withUrl) {
+    VisitUrlCookies(manager, kTestUrl, false, cookies, false, event);
+  } else {
+    VisitAllCookies(manager, cookies, false, event);
+  }
+
+  EXPECT_EQ(0U, cookies.size());
+}
+
+// Delete all system cookies.
+void DeleteAllCookies(CefRefPtr<CefCookieManager> manager,
+                      CefRefPtr<CefWaitableEvent> event) {
+  DeleteCookies(manager, CefString(), CefString(), kIgnoreNumDeleted, event);
+}
+
+void TestDomainCookie(CefRefPtr<CefCookieManager> manager,
+                      CefRefPtr<CefWaitableEvent> event) {
+  CefCookie cookie;
+
+  // Create a domain cookie.
+  CreateCookie(manager, cookie, true, false, event);
+
+  // Retrieve, verify and delete the domain cookie.
+  GetCookie(manager, cookie, true, event, true);
+
+  // Verify that the cookie was deleted.
+  VerifyNoCookies(manager, event, true);
+}
+
+void TestHostCookie(CefRefPtr<CefCookieManager> manager,
+                    CefRefPtr<CefWaitableEvent> event) {
+  CefCookie cookie;
+
+  // Create a host cookie.
+  CreateCookie(manager, cookie, false, false, event);
+
+  // Retrieve, verify and delete the host cookie.
+  GetCookie(manager, cookie, false, event, true);
+
+  // Verify that the cookie was deleted.
+  VerifyNoCookies(manager, event, true);
+}
+
+void TestInvalidCookie(CefRefPtr<CefCookieManager> manager,
+                       CefRefPtr<CefWaitableEvent> event) {
+  CookieVector cookies;
+
+  CefCookie cookie;
+  const char* kUrl = "http://www.xyz.com";
+  CefString(&cookie.name).FromASCII("invalid1");
+  CefString(&cookie.value).FromASCII("invalid1");
+  CefString(&cookie.domain).FromASCII(".zyx.com");  // domain mismatch
+
+  cookies.push_back(cookie);
+
+  // No cookies will be set due to non canonical cookie
+  SetCookies(manager, kUrl, cookies, false, event);
+}
+
+void TestMultipleCookies(CefRefPtr<CefCookieManager> manager,
+                         CefRefPtr<CefWaitableEvent> event) {
+  std::stringstream ss;
+  int i;
+
+  CookieVector cookies;
+
+  const int kNumCookies = 4;
+
+  // Create the cookies.
+  for (i = 0; i < kNumCookies; i++) {
+    CefCookie cookie;
+
+    ss << "my_cookie" << i;
+    CefString(&cookie.name).FromASCII(ss.str().c_str());
+    ss.str("");
+    ss << "My Value " << i;
+    CefString(&cookie.value).FromASCII(ss.str().c_str());
+    ss.str("");
+
+    cookies.push_back(cookie);
+  }
+
+  // Set the cookies.
+  SetCookies(manager, kTestUrl, cookies, true, event);
+  cookies.clear();
+
+  // Get the cookies without deleting them.
+  VisitUrlCookies(manager, kTestUrl, false, cookies, false, event);
+
+  EXPECT_EQ((CookieVector::size_type)kNumCookies, cookies.size());
+
+  CookieVector::const_iterator it = cookies.begin();
+  for (i = 0; it != cookies.end(); ++it, ++i) {
+    const CefCookie& cookie = *it;
+
+    ss << "my_cookie" << i;
+    EXPECT_EQ(CefString(&cookie.name), ss.str());
+    ss.str("");
+    ss << "My Value " << i;
+    EXPECT_EQ(CefString(&cookie.value), ss.str());
+    ss.str("");
+  }
+
+  cookies.clear();
+
+  // Delete the 2nd cookie.
+  DeleteCookies(manager, kTestUrl, CefString("my_cookie1"), 1, event);
+
+  // Verify that the cookie has been deleted.
+  VisitUrlCookies(manager, kTestUrl, false, cookies, false, event);
+
+  EXPECT_EQ(3U, cookies.size());
+  if (cookies.size() != 3U)
+    return;
+
+  EXPECT_EQ(CefString(&cookies[0].name), "my_cookie0");
+  EXPECT_EQ(CefString(&cookies[1].name), "my_cookie2");
+  EXPECT_EQ(CefString(&cookies[2].name), "my_cookie3");
+
+  cookies.clear();
+
+  // Delete the rest of the cookies.
+  DeleteCookies(manager, kTestUrl, CefString(), 3, event);
+
+  // Verify that the cookies have been deleted.
+  VisitUrlCookies(manager, kTestUrl, false, cookies, false, event);
+
+  EXPECT_EQ(0U, cookies.size());
+
+  // Create the cookies.
+  for (i = 0; i < kNumCookies; i++) {
+    CefCookie cookie;
+
+    ss << "my_cookie" << i;
+    CefString(&cookie.name).FromASCII(ss.str().c_str());
+    ss.str("");
+    ss << "My Value " << i;
+    CefString(&cookie.value).FromASCII(ss.str().c_str());
+    ss.str("");
+
+    cookies.push_back(cookie);
+  }
+
+  // Delete all of the cookies using the visitor.
+  VisitUrlCookies(manager, kTestUrl, false, cookies, true, event);
+
+  cookies.clear();
+
+  // Verify that the cookies have been deleted.
+  VisitUrlCookies(manager, kTestUrl, false, cookies, false, event);
+
+  EXPECT_EQ(0U, cookies.size());
+}
+
+void TestAllCookies(CefRefPtr<CefCookieManager> manager,
+                    CefRefPtr<CefWaitableEvent> event) {
+  CookieVector cookies;
+
+  // Delete all system cookies just in case something is left over from a
+  // different test.
+  DeleteAllCookies(manager, event);
+
+  // Verify that all system cookies have been deleted.
+  VisitAllCookies(manager, cookies, false, event);
+
+  EXPECT_EQ(0U, cookies.size());
+
+  // Create cookies with 2 separate hosts.
+  CefCookie cookie1;
+  const char* kUrl1 = "http://www.foo.com";
+  CefString(&cookie1.name).FromASCII("my_cookie1");
+  CefString(&cookie1.value).FromASCII("My Value 1");
+
+  cookies.push_back(cookie1);
+  SetCookies(manager, kUrl1, cookies, true, event);
+  cookies.clear();
+
+  CefCookie cookie2;
+  const char* kUrl2 = "http://www.bar.com";
+  CefString(&cookie2.name).FromASCII("my_cookie2");
+  CefString(&cookie2.value).FromASCII("My Value 2");
+
+  cookies.push_back(cookie2);
+  SetCookies(manager, kUrl2, cookies, true, event);
+  cookies.clear();
+
+  // Verify that all system cookies can be retrieved.
+  VisitAllCookies(manager, cookies, false, event);
+
+  EXPECT_EQ(2U, cookies.size());
+  if (cookies.size() != 2U)
+    return;
+
+  EXPECT_EQ(CefString(&cookies[0].name), "my_cookie1");
+  EXPECT_EQ(CefString(&cookies[0].value), "My Value 1");
+  EXPECT_EQ(CefString(&cookies[0].domain), "www.foo.com");
+  EXPECT_EQ(CefString(&cookies[1].name), "my_cookie2");
+  EXPECT_EQ(CefString(&cookies[1].value), "My Value 2");
+  EXPECT_EQ(CefString(&cookies[1].domain), "www.bar.com");
+  cookies.clear();
+
+  // Verify that the cookies can be retrieved separately.
+  VisitUrlCookies(manager, kUrl1, false, cookies, false, event);
+
+  EXPECT_EQ(1U, cookies.size());
+  if (cookies.size() != 1U)
+    return;
+
+  EXPECT_EQ(CefString(&cookies[0].name), "my_cookie1");
+  EXPECT_EQ(CefString(&cookies[0].value), "My Value 1");
+  EXPECT_EQ(CefString(&cookies[0].domain), "www.foo.com");
+  cookies.clear();
+
+  VisitUrlCookies(manager, kUrl2, false, cookies, false, event);
+
+  EXPECT_EQ(1U, cookies.size());
+  if (cookies.size() != 1U)
+    return;
+
+  EXPECT_EQ(CefString(&cookies[0].name), "my_cookie2");
+  EXPECT_EQ(CefString(&cookies[0].value), "My Value 2");
+  EXPECT_EQ(CefString(&cookies[0].domain), "www.bar.com");
+  cookies.clear();
+
+  // Delete all of the system cookies.
+  DeleteAllCookies(manager, event);
+
+  // Verify that all system cookies have been deleted.
+  VerifyNoCookies(manager, event, false);
+}
+
+}  // namespace
+
+// Test creation of a invalid cookie.
+TEST(CookieTest, BasicInvalidCookie) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefCookieManager> manager =
+      CefCookieManager::GetGlobalManager(new TestCompletionCallback(event));
+  event->Wait();
+  EXPECT_TRUE(manager.get());
+
+  TestInvalidCookie(manager, event);
+}
+
+// Test creation of a domain cookie.
+TEST(CookieTest, BasicDomainCookie) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefCookieManager> manager =
+      CefCookieManager::GetGlobalManager(new TestCompletionCallback(event));
+  event->Wait();
+  EXPECT_TRUE(manager.get());
+
+  TestDomainCookie(manager, event);
+}
+
+// Test creation of a host cookie.
+TEST(CookieTest, BasicHostCookie) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefCookieManager> manager =
+      CefCookieManager::GetGlobalManager(new TestCompletionCallback(event));
+  event->Wait();
+  EXPECT_TRUE(manager.get());
+
+  TestHostCookie(manager, event);
+}
+
+// Test creation of multiple cookies.
+TEST(CookieTest, BasicMultipleCookies) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefCookieManager> manager =
+      CefCookieManager::GetGlobalManager(new TestCompletionCallback(event));
+  event->Wait();
+  EXPECT_TRUE(manager.get());
+
+  TestMultipleCookies(manager, event);
+}
+
+TEST(CookieTest, BasicAllCookies) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefCookieManager> manager =
+      CefCookieManager::GetGlobalManager(new TestCompletionCallback(event));
+  event->Wait();
+  EXPECT_TRUE(manager.get());
+
+  TestAllCookies(manager, event);
+}
+
+namespace {
+
+const char* kCookieJSUrl1 = "http://tests/cookie1.html";
+const char* kCookieJSUrl2 = "http://tests/cookie2.html";
+
+class CookieTestJSHandler : public TestHandler {
+ public:
+  CookieTestJSHandler() {}
+
+  void RunTest() override {
+    std::string page =
+        "<html><head>"
+        "<script>"
+        "document.cookie='name1=value1;"
+        // Invalid date should not cause a crash (see issue #2927).
+        " expires=Tue, 07 Nov 94276 07:58:05 GMT'"
+        "</script>"
+        "</head><body>COOKIE TEST1</body></html>";
+    AddResource(kCookieJSUrl1, page, "text/html");
+
+    page =
+        "<html><head>"
+        "<script>"
+        "document.cookie='name2=value2';"
+        "</script>"
+        "</head><body>COOKIE TEST2</body></html>";
+    AddResource(kCookieJSUrl2, page, "text/html");
+
+    // Create the request context that will use an in-memory cache.
+    CefRequestContextSettings settings;
+    CefRefPtr<CefRequestContext> request_context =
+        CefRequestContext::CreateContext(settings, nullptr);
+    manager_ = request_context->GetCookieManager(nullptr);
+
+    // Create the browser.
+    CreateBrowser(kCookieJSUrl1, request_context);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  // Go to the next URL.
+  void LoadNextURL(CefRefPtr<CefFrame> frame) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&CookieTestJSHandler::LoadNextURL, this, frame));
+      return;
+    }
+
+    frame->LoadURL(kCookieJSUrl2);
+  }
+
+  void CompleteTest() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&CookieTestJSHandler::CompleteTest, this));
+      return;
+    }
+
+    DestroyTest();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    std::string url = frame->GetURL();
+    if (url == kCookieJSUrl1) {
+      got_load_end1_.yes();
+      VerifyCookie(manager_, url, "name1", "value1", true, &got_cookie1_,
+                   base::Bind(&CookieTestJSHandler::LoadNextURL, this, frame));
+    } else {
+      got_load_end2_.yes();
+      VerifyCookie(manager_, url, "name2", "value2", true, &got_cookie2_,
+                   base::Bind(&CookieTestJSHandler::CompleteTest, this));
+    }
+  }
+
+  // Verify that the cookie was set successfully.
+  void VerifyCookie(CefRefPtr<CefCookieManager> manager,
+                    const std::string& url,
+                    const std::string& name,
+                    const std::string& value,
+                    bool deleteCookie,
+                    TrackCallback* callback,
+                    const base::Closure& continue_callback) {
+    // Get the cookie.
+    EXPECT_TRUE(cookies_.empty());
+    VisitUrlCookies(manager, url, false, cookies_, deleteCookie,
+                    base::Bind(&CookieTestJSHandler::VerifyCookieComplete, this,
+                               name, value, callback, continue_callback));
+  }
+
+  void VerifyCookieComplete(const std::string& name,
+                            const std::string& value,
+                            TrackCallback* callback,
+                            const base::Closure& continue_callback) {
+    if (cookies_.size() == 1U && CefString(&cookies_[0].name) == name &&
+        CefString(&cookies_[0].value) == value) {
+      callback->yes();
+    }
+
+    cookies_.clear();
+    continue_callback.Run();
+  }
+
+  CefRefPtr<CefCookieManager> manager_;
+
+  CookieVector cookies_;
+
+  TrackCallback got_load_end1_;
+  TrackCallback got_load_end2_;
+  TrackCallback got_cookie1_;
+  TrackCallback got_cookie2_;
+
+  IMPLEMENT_REFCOUNTING(CookieTestJSHandler);
+};
+
+}  // namespace
+
+// Verify use of multiple cookie managers vis JS.
+TEST(CookieTest, GetCookieManagerJS) {
+  CefRefPtr<CookieTestJSHandler> handler = new CookieTestJSHandler();
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_load_end1_);
+  EXPECT_TRUE(handler->got_load_end2_);
+  EXPECT_TRUE(handler->got_cookie1_);
+  EXPECT_TRUE(handler->got_cookie2_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kCustomCookieScheme[] = "ccustom";
+
+class CompletionCallback : public CefCompletionCallback {
+ public:
+  explicit CompletionCallback(const base::Closure& callback)
+      : callback_(callback) {}
+
+  void OnComplete() override {
+    callback_.Run();
+    callback_.Reset();
+  }
+
+ private:
+  base::Closure callback_;
+  IMPLEMENT_REFCOUNTING(CompletionCallback);
+};
+
+class CookieTestSchemeHandler : public TestHandler {
+ public:
+  class SchemeHandler : public CefResourceHandler {
+   public:
+    explicit SchemeHandler(CookieTestSchemeHandler* handler)
+        : handler_(handler), offset_(0) {}
+
+    bool Open(CefRefPtr<CefRequest> request,
+              bool& handle_request,
+              CefRefPtr<CefCallback> callback) override {
+      EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+      std::string url = request->GetURL();
+      if (url == handler_->url1_) {
+        content_ = "<html><body>COOKIE TEST1</body></html>";
+        cookie_ = "name1=value1";
+        handler_->got_process_request1_.yes();
+      } else if (url == handler_->url2_) {
+        content_ = "<html><body>COOKIE TEST2</body></html>";
+        cookie_ = "name2=value2";
+        handler_->got_process_request2_.yes();
+      } else if (url == handler_->url3_) {
+        content_ = "<html><body>COOKIE TEST3</body></html>";
+        handler_->got_process_request3_.yes();
+
+        // Verify that the cookie was passed in.
+        CefRequest::HeaderMap headerMap;
+        request->GetHeaderMap(headerMap);
+        CefRequest::HeaderMap::iterator it = headerMap.find("Cookie");
+        if (it != headerMap.end() && it->second == "name2=value2")
+          handler_->got_process_request_cookie_.yes();
+      }
+
+      // Continue immediately.
+      handle_request = true;
+      return true;
+    }
+
+    void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                            int64& response_length,
+                            CefString& redirectUrl) override {
+      response_length = content_.size();
+
+      response->SetStatus(200);
+      response->SetMimeType("text/html");
+
+      if (!cookie_.empty()) {
+        CefResponse::HeaderMap headerMap;
+        response->GetHeaderMap(headerMap);
+        headerMap.insert(std::make_pair("Set-Cookie", cookie_));
+        response->SetHeaderMap(headerMap);
+      }
+    }
+
+    bool Read(void* data_out,
+              int bytes_to_read,
+              int& bytes_read,
+              CefRefPtr<CefResourceReadCallback> callback) override {
+      EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+      bool has_data = false;
+      bytes_read = 0;
+
+      size_t size = content_.size();
+      if (offset_ < size) {
+        int transfer_size =
+            std::min(bytes_to_read, static_cast<int>(size - offset_));
+        memcpy(data_out, content_.c_str() + offset_, transfer_size);
+        offset_ += transfer_size;
+
+        bytes_read = transfer_size;
+        has_data = true;
+      }
+
+      return has_data;
+    }
+
+    void Cancel() override {}
+
+   private:
+    CookieTestSchemeHandler* handler_;
+    std::string content_;
+    size_t offset_;
+    std::string cookie_;
+
+    IMPLEMENT_REFCOUNTING(SchemeHandler);
+    DISALLOW_COPY_AND_ASSIGN(SchemeHandler);
+  };
+
+  class SchemeHandlerFactory : public CefSchemeHandlerFactory {
+   public:
+    explicit SchemeHandlerFactory(CookieTestSchemeHandler* handler)
+        : handler_(handler) {}
+
+    CefRefPtr<CefResourceHandler> Create(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        const CefString& scheme_name,
+        CefRefPtr<CefRequest> request) override {
+      std::string url = request->GetURL();
+      if (url == handler_->url3_) {
+        // Verify that the cookie was not passed in.
+        CefRequest::HeaderMap headerMap;
+        request->GetHeaderMap(headerMap);
+        CefRequest::HeaderMap::iterator it = headerMap.find("Cookie");
+        if (it != headerMap.end() && it->second == "name2=value2")
+          handler_->got_create_cookie_.yes();
+      }
+
+      return new SchemeHandler(handler_);
+    }
+
+   private:
+    CookieTestSchemeHandler* handler_;
+
+    IMPLEMENT_REFCOUNTING(SchemeHandlerFactory);
+    DISALLOW_COPY_AND_ASSIGN(SchemeHandlerFactory);
+  };
+
+  CookieTestSchemeHandler(const std::string& scheme,
+                          bool use_global,
+                          bool block_cookies = false)
+      : scheme_(scheme),
+        use_global_(use_global),
+        block_cookies_(block_cookies) {
+    url1_ = scheme + "://cookie-tests/cookie1.html";
+    url2_ = scheme + "://cookie-tests/cookie2.html";
+    url3_ = scheme + "://cookie-tests/cookie3.html";
+  }
+
+  void RunTest() override {
+    if (use_global_) {
+      request_context_ = CefRequestContext::GetGlobalContext();
+    } else {
+      // Create the request context that will use an in-memory cache.
+      CefRequestContextSettings settings;
+      request_context_ = CefRequestContext::CreateContext(settings, nullptr);
+    }
+
+    // Register the scheme handler.
+    request_context_->RegisterSchemeHandlerFactory(
+        scheme_, "cookie-tests", new SchemeHandlerFactory(this));
+    manager_ = request_context_->GetCookieManager(nullptr);
+    if (!use_global_ && (scheme_ == kCustomCookieScheme || block_cookies_)) {
+      std::vector<CefString> schemes;
+      if (!block_cookies_)
+        schemes.push_back(kCustomCookieScheme);
+
+      // Need to wait for completion before creating the browser.
+      manager_->SetSupportedSchemes(
+          schemes, !block_cookies_ /* include_defaults */,
+          new CompletionCallback(base::Bind(
+              &CookieTestSchemeHandler::CreateBrowserContinue, this)));
+    } else {
+      CreateBrowserContinue();
+    }
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void CreateBrowserContinue() {
+    // Create the browser.
+    CreateBrowser(url1_, request_context_);
+  }
+
+  // Go to the next URL.
+  void LoadNextURL(CefRefPtr<CefFrame> frame, const std::string& url) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&CookieTestSchemeHandler::LoadNextURL,
+                                     this, frame, url));
+      return;
+    }
+
+    frame->LoadURL(url);
+  }
+
+  void CompleteTest(CefRefPtr<CefBrowser> browser) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&CookieTestSchemeHandler::CompleteTest,
+                                     this, browser));
+      return;
+    }
+
+    // Unregister the scheme handler.
+    browser->GetHost()->GetRequestContext()->RegisterSchemeHandlerFactory(
+        scheme_, "cookie-tests", nullptr);
+
+    DestroyTest();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    std::string url = frame->GetURL();
+    if (url == url1_) {
+      got_load_end1_.yes();
+      VerifyCookie(manager_, url, "name1", "value1", true, &got_cookie1_,
+                   base::Bind(&CookieTestSchemeHandler::LoadNextURL, this,
+                              frame, url2_));
+    } else if (url == url2_) {
+      got_load_end2_.yes();
+      VerifyCookie(manager_, url, "name2", "value2", false, &got_cookie2_,
+                   base::Bind(&CookieTestSchemeHandler::LoadNextURL, this,
+                              frame, url3_));
+    } else {
+      got_load_end3_.yes();
+      VerifyCookie(
+          manager_, url, "name2", "value2", true, &got_cookie3_,
+          base::Bind(&CookieTestSchemeHandler::CompleteTest, this, browser));
+    }
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_process_request1_);
+    EXPECT_TRUE(got_process_request2_);
+    EXPECT_TRUE(got_process_request3_);
+    EXPECT_TRUE(got_load_end1_);
+    EXPECT_TRUE(got_load_end2_);
+    EXPECT_TRUE(got_load_end3_);
+
+    if (block_cookies_) {
+      EXPECT_FALSE(got_create_cookie_);
+      EXPECT_FALSE(got_process_request_cookie_);
+      EXPECT_FALSE(got_cookie1_);
+      EXPECT_FALSE(got_cookie2_);
+      EXPECT_FALSE(got_cookie3_);
+    } else {
+      EXPECT_TRUE(got_create_cookie_);
+      EXPECT_TRUE(got_process_request_cookie_);
+      EXPECT_TRUE(got_cookie1_);
+      EXPECT_TRUE(got_cookie2_);
+      EXPECT_TRUE(got_cookie3_);
+    }
+
+    // Unregister the scheme handler.
+    request_context_->RegisterSchemeHandlerFactory(scheme_, "cookie-tests",
+                                                   nullptr);
+    request_context_ = nullptr;
+
+    TestHandler::DestroyTest();
+  }
+
+  // Verify that the cookie was set successfully.
+  void VerifyCookie(CefRefPtr<CefCookieManager> manager,
+                    const std::string& url,
+                    const std::string& name,
+                    const std::string& value,
+                    bool deleteCookie,
+                    TrackCallback* callback,
+                    const base::Closure& continue_callback) {
+    // Get the cookie.
+    EXPECT_TRUE(cookies_.empty());
+    VisitUrlCookies(manager, url, false, cookies_, deleteCookie,
+                    base::Bind(&CookieTestSchemeHandler::VerifyCookieComplete,
+                               this, name, value, callback, continue_callback));
+  }
+
+  void VerifyCookieComplete(const std::string& name,
+                            const std::string& value,
+                            TrackCallback* callback,
+                            const base::Closure& continue_callback) {
+    if (cookies_.size() == 1U && CefString(&cookies_[0].name) == name &&
+        CefString(&cookies_[0].value) == value) {
+      callback->yes();
+    }
+
+    cookies_.clear();
+    continue_callback.Run();
+  }
+
+  const std::string scheme_;
+  const bool use_global_;
+  const bool block_cookies_;
+  std::string url1_;
+  std::string url2_;
+  std::string url3_;
+
+  CefRefPtr<CefRequestContext> request_context_;
+  CefRefPtr<CefCookieManager> manager_;
+
+  CookieVector cookies_;
+
+  TrackCallback got_process_request1_;
+  TrackCallback got_process_request2_;
+  TrackCallback got_process_request3_;
+  TrackCallback got_create_cookie_;
+  TrackCallback got_process_request_cookie_;
+  TrackCallback got_load_end1_;
+  TrackCallback got_load_end2_;
+  TrackCallback got_load_end3_;
+  TrackCallback got_cookie1_;
+  TrackCallback got_cookie2_;
+  TrackCallback got_cookie3_;
+
+  IMPLEMENT_REFCOUNTING(CookieTestSchemeHandler);
+};
+
+}  // namespace
+
+// Verify use of the global cookie manager with HTTP.
+TEST(CookieTest, GetCookieManagerHttpGlobal) {
+  CefRefPtr<CookieTestSchemeHandler> handler =
+      new CookieTestSchemeHandler("http", true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify use of an in-memory cookie manager with HTTP.
+TEST(CookieTest, GetCookieManagerHttpInMemory) {
+  CefRefPtr<CookieTestSchemeHandler> handler =
+      new CookieTestSchemeHandler("http", false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify use of an in-memory cookie manager with HTTP to block all cookies.
+TEST(CookieTest, GetCookieManagerHttpInMemoryBlocked) {
+  CefRefPtr<CookieTestSchemeHandler> handler =
+      new CookieTestSchemeHandler("http", false, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify use of the global cookie manager with a custom scheme.
+TEST(CookieTest, GetCookieManagerCustomGlobal) {
+  CefRefPtr<CookieTestSchemeHandler> handler =
+      new CookieTestSchemeHandler(kCustomCookieScheme, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify use of an in-memory cookie manager with a custom scheme.
+TEST(CookieTest, GetCookieManagerCustomInMemory) {
+  CefRefPtr<CookieTestSchemeHandler> handler =
+      new CookieTestSchemeHandler(kCustomCookieScheme, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kCookieAccessScheme[] = "http";
+const char kCookieAccessDomain[] = "test-cookies.com";
+const char kCookieAccessServerIP[] = "127.0.0.1";
+const uint16 kCookieAccessServerPort = 8099;
+
+std::string GetCookieAccessOrigin(const std::string& scheme,
+                                  bool server_backend) {
+  std::stringstream ss;
+  if (server_backend) {
+    ss << scheme << "://" << kCookieAccessServerIP << ":"
+       << kCookieAccessServerPort;
+  } else {
+    ss << scheme << "://" << kCookieAccessDomain;
+  }
+  ss << "/";
+  return ss.str();
+}
+
+std::string GetCookieAccessUrl1(const std::string& scheme,
+                                bool server_backend) {
+  return GetCookieAccessOrigin(scheme, server_backend) + "cookie1.html";
+}
+
+std::string GetCookieAccessUrl2(const std::string& scheme,
+                                bool server_backend) {
+  return GetCookieAccessOrigin(scheme, server_backend) + "cookie2.html";
+}
+
+void TestCookieString(const std::string& cookie_str,
+                      int& cookie_js_ct,
+                      int& cookie_net_ct) {
+  if (cookie_str.find("name_js=value_js") != std::string::npos) {
+    cookie_js_ct++;
+  }
+  if (cookie_str.find("name_net=value_net") != std::string::npos) {
+    cookie_net_ct++;
+  }
+}
+
+struct CookieAccessData {
+  CefRefPtr<CefResponse> response;
+  std::string response_data;
+
+  int request_ct_ = 0;
+  int cookie_js_ct_ = 0;
+  int cookie_net_ct_ = 0;
+};
+
+class CookieAccessResponseHandler {
+ public:
+  CookieAccessResponseHandler() {}
+  virtual void AddResponse(const std::string& url, CookieAccessData* data) = 0;
+
+ protected:
+  virtual ~CookieAccessResponseHandler() {}
+};
+
+std::string GetHeaderValue(const CefServer::HeaderMap& header_map,
+                           const std::string& header_name) {
+  CefServer::HeaderMap::const_iterator it = header_map.find(header_name);
+  if (it != header_map.end())
+    return it->second;
+  return std::string();
+}
+
+// Serves request responses.
+class CookieAccessSchemeHandler : public CefResourceHandler {
+ public:
+  explicit CookieAccessSchemeHandler(CookieAccessData* data)
+      : data_(data), offset_(0) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    CefRequest::HeaderMap headerMap;
+    request->GetHeaderMap(headerMap);
+    const std::string& cookie_str = GetHeaderValue(headerMap, "Cookie");
+    TestCookieString(cookie_str, data_->cookie_js_ct_, data_->cookie_net_ct_);
+
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+
+    response->SetStatus(data_->response->GetStatus());
+    response->SetStatusText(data_->response->GetStatusText());
+    response->SetMimeType(data_->response->GetMimeType());
+
+    CefResponse::HeaderMap headerMap;
+    data_->response->GetHeaderMap(headerMap);
+    response->SetHeaderMap(headerMap);
+
+    response_length = data_->response_data.length();
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    bool has_data = false;
+    bytes_read = 0;
+
+    size_t size = data_->response_data.length();
+    if (offset_ < size) {
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(size - offset_));
+      memcpy(data_out, data_->response_data.c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+  void Cancel() override { EXPECT_IO_THREAD(); }
+
+ private:
+  static void TestCookie(const CefCookie& cookie,
+                         TrackCallback& got_cookie_js,
+                         TrackCallback& got_cookie_net) {
+    const std::string& cookie_name = CefString(&cookie.name);
+    const std::string& cookie_val = CefString(&cookie.value);
+    if (cookie_name == "name_js") {
+      EXPECT_STREQ("value_js", cookie_val.c_str());
+      got_cookie_js.yes();
+    } else if (cookie_name == "name_net") {
+      EXPECT_STREQ("value_net", cookie_val.c_str());
+      got_cookie_net.yes();
+    } else {
+      ADD_FAILURE() << "Unexpected cookie: " << cookie_name;
+    }
+  }
+
+  // |data_| is not owned by this object.
+  CookieAccessData* data_;
+
+  size_t offset_;
+
+  IMPLEMENT_REFCOUNTING(CookieAccessSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(CookieAccessSchemeHandler);
+};
+
+class CookieAccessSchemeHandlerFactory : public CefSchemeHandlerFactory,
+                                         public CookieAccessResponseHandler {
+ public:
+  CookieAccessSchemeHandlerFactory() {}
+
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+    const std::string& url = request->GetURL();
+    ResponseDataMap::const_iterator it = data_map_.find(url);
+    if (it != data_map_.end()) {
+      it->second->request_ct_++;
+
+      return new CookieAccessSchemeHandler(it->second);
+    }
+
+    // Unknown test.
+    ADD_FAILURE() << "Unexpected url: " << url;
+    return nullptr;
+  }
+
+  void AddResponse(const std::string& url, CookieAccessData* data) override {
+    data_map_.insert(std::make_pair(url, data));
+  }
+
+  void Shutdown(const base::Closure& complete_callback) {
+    if (!CefCurrentlyOn(TID_IO)) {
+      CefPostTask(TID_IO,
+                  base::Bind(&CookieAccessSchemeHandlerFactory::Shutdown, this,
+                             complete_callback));
+      return;
+    }
+
+    complete_callback.Run();
+  }
+
+ private:
+  // Map of URL to Data.
+  typedef std::map<std::string, CookieAccessData*> ResponseDataMap;
+  ResponseDataMap data_map_;
+
+  IMPLEMENT_REFCOUNTING(CookieAccessSchemeHandlerFactory);
+};
+
+// HTTP server handler.
+class CookieAccessServerHandler : public CefServerHandler,
+                                  public CookieAccessResponseHandler {
+ public:
+  CookieAccessServerHandler()
+      : initialized_(false),
+        expected_connection_ct_(-1),
+        actual_connection_ct_(0),
+        expected_http_request_ct_(-1),
+        actual_http_request_ct_(0) {}
+
+  virtual ~CookieAccessServerHandler() { RunCompleteCallback(); }
+
+  // Must be called before CreateServer().
+  void AddResponse(const std::string& url, CookieAccessData* data) override {
+    EXPECT_FALSE(initialized_);
+    data_map_.insert(std::make_pair(url, data));
+  }
+
+  // Must be called before CreateServer().
+  void SetExpectedRequestCount(int count) {
+    EXPECT_FALSE(initialized_);
+    expected_connection_ct_ = expected_http_request_ct_ = count;
+  }
+
+  // |complete_callback| will be executed on the UI thread after the server is
+  // started.
+  void CreateServer(const base::Closure& complete_callback) {
+    EXPECT_UI_THREAD();
+
+    if (expected_connection_ct_ < 0) {
+      // Default to the assumption of one request per registered URL.
+      SetExpectedRequestCount(static_cast<int>(data_map_.size()));
+    }
+
+    EXPECT_FALSE(initialized_);
+    initialized_ = true;
+
+    EXPECT_TRUE(complete_callback_.is_null());
+    complete_callback_ = complete_callback;
+
+    CefServer::CreateServer(kCookieAccessServerIP, kCookieAccessServerPort, 10,
+                            this);
+  }
+
+  // Results in a call to VerifyResults() and eventual execution of the
+  // |complete_callback| on the UI thread via CookieAccessServerHandler
+  // destruction.
+  void ShutdownServer(const base::Closure& complete_callback) {
+    EXPECT_UI_THREAD();
+
+    EXPECT_TRUE(complete_callback_.is_null());
+    complete_callback_ = complete_callback;
+
+    EXPECT_TRUE(server_);
+    if (server_)
+      server_->Shutdown();
+  }
+
+  void OnServerCreated(CefRefPtr<CefServer> server) override {
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(server->IsRunning());
+    EXPECT_FALSE(server->HasConnection());
+
+    EXPECT_FALSE(got_server_created_);
+    got_server_created_.yes();
+
+    EXPECT_FALSE(server_);
+    server_ = server;
+
+    EXPECT_FALSE(server_runner_);
+    server_runner_ = server_->GetTaskRunner();
+    EXPECT_TRUE(server_runner_);
+    EXPECT_TRUE(server_runner_->BelongsToCurrentThread());
+
+    CefPostTask(
+        TID_UI,
+        base::Bind(&CookieAccessServerHandler::RunCompleteCallback, this));
+  }
+
+  void OnServerDestroyed(CefRefPtr<CefServer> server) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_FALSE(server->IsRunning());
+    EXPECT_FALSE(server->HasConnection());
+
+    EXPECT_FALSE(got_server_destroyed_);
+    got_server_destroyed_.yes();
+
+    server_ = nullptr;
+
+    VerifyResults();
+  }
+
+  void OnClientConnected(CefRefPtr<CefServer> server,
+                         int connection_id) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(server->HasConnection());
+    EXPECT_TRUE(server->IsValidConnection(connection_id));
+
+    EXPECT_TRUE(connection_id_set_.find(connection_id) ==
+                connection_id_set_.end());
+    connection_id_set_.insert(connection_id);
+
+    actual_connection_ct_++;
+  }
+
+  void OnClientDisconnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_FALSE(server->IsValidConnection(connection_id));
+
+    ConnectionIdSet::iterator it = connection_id_set_.find(connection_id);
+    EXPECT_TRUE(it != connection_id_set_.end());
+    connection_id_set_.erase(it);
+  }
+
+  void OnHttpRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const CefString& client_address,
+                     CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(VerifyConnection(connection_id));
+    EXPECT_FALSE(client_address.empty());
+
+    // Log the requests for better error reporting.
+    request_log_ += request->GetMethod().ToString() + " " +
+                    request->GetURL().ToString() + "\n";
+
+    HandleRequest(server, connection_id, request);
+
+    actual_http_request_ct_++;
+  }
+
+  void OnWebSocketRequest(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const CefString& client_address,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefCallback> callback) override {
+    NOTREACHED();
+  }
+
+  void OnWebSocketConnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {
+    NOTREACHED();
+  }
+
+  void OnWebSocketMessage(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const void* data,
+                          size_t data_size) override {
+    NOTREACHED();
+  }
+
+ private:
+  bool RunningOnServerThread() {
+    return server_runner_ && server_runner_->BelongsToCurrentThread();
+  }
+
+  bool VerifyServer(CefRefPtr<CefServer> server) {
+    V_DECLARE();
+    V_EXPECT_TRUE(RunningOnServerThread());
+    V_EXPECT_TRUE(server);
+    V_EXPECT_TRUE(server_);
+    V_EXPECT_TRUE(server->GetAddress().ToString() ==
+                  server_->GetAddress().ToString());
+    V_RETURN();
+  }
+
+  bool VerifyConnection(int connection_id) {
+    return connection_id_set_.find(connection_id) != connection_id_set_.end();
+  }
+
+  void VerifyResults() {
+    EXPECT_TRUE(RunningOnServerThread());
+
+    EXPECT_TRUE(got_server_created_);
+    EXPECT_TRUE(got_server_destroyed_);
+    EXPECT_TRUE(connection_id_set_.empty());
+    EXPECT_EQ(expected_connection_ct_, actual_connection_ct_) << request_log_;
+    EXPECT_EQ(expected_http_request_ct_, actual_http_request_ct_)
+        << request_log_;
+  }
+
+  void HandleRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     CefRefPtr<CefRequest> request) {
+    const std::string& url = request->GetURL();
+    ResponseDataMap::const_iterator it = data_map_.find(url);
+    if (it != data_map_.end()) {
+      it->second->request_ct_++;
+
+      CefRequest::HeaderMap headerMap;
+      request->GetHeaderMap(headerMap);
+      const std::string& cookie_str = GetHeaderValue(headerMap, "cookie");
+      TestCookieString(cookie_str, it->second->cookie_js_ct_,
+                       it->second->cookie_net_ct_);
+
+      SendResponse(server, connection_id, it->second->response,
+                   it->second->response_data);
+    } else {
+      // Unknown test.
+      ADD_FAILURE() << "Unexpected url: " << url;
+      server->SendHttp500Response(connection_id, "Unknown test");
+    }
+  }
+
+  void SendResponse(CefRefPtr<CefServer> server,
+                    int connection_id,
+                    CefRefPtr<CefResponse> response,
+                    const std::string& response_data) {
+    int response_code = response->GetStatus();
+    const CefString& content_type = response->GetMimeType();
+    int64 content_length = static_cast<int64>(response_data.size());
+
+    CefResponse::HeaderMap extra_headers;
+    response->GetHeaderMap(extra_headers);
+
+    server->SendHttpResponse(connection_id, response_code, content_type,
+                             content_length, extra_headers);
+
+    if (content_length != 0) {
+      server->SendRawData(connection_id, response_data.data(),
+                          response_data.size());
+      server->CloseConnection(connection_id);
+    }
+
+    // The connection should be closed.
+    EXPECT_FALSE(server->IsValidConnection(connection_id));
+  }
+
+  void RunCompleteCallback() {
+    EXPECT_UI_THREAD();
+
+    EXPECT_FALSE(complete_callback_.is_null());
+    complete_callback_.Run();
+    complete_callback_.Reset();
+  }
+
+  // Map of URL to Data.
+  typedef std::map<std::string, CookieAccessData*> ResponseDataMap;
+  ResponseDataMap data_map_;
+
+  CefRefPtr<CefServer> server_;
+  CefRefPtr<CefTaskRunner> server_runner_;
+  bool initialized_;
+
+  // Only accessed on the UI thread.
+  base::Closure complete_callback_;
+
+  // After initialization the below members are only accessed on the server
+  // thread.
+
+  TrackCallback got_server_created_;
+  TrackCallback got_server_destroyed_;
+
+  typedef std::set<int> ConnectionIdSet;
+  ConnectionIdSet connection_id_set_;
+
+  int expected_connection_ct_;
+  int actual_connection_ct_;
+  int expected_http_request_ct_;
+  int actual_http_request_ct_;
+
+  std::string request_log_;
+
+  IMPLEMENT_REFCOUNTING(CookieAccessServerHandler);
+  DISALLOW_COPY_AND_ASSIGN(CookieAccessServerHandler);
+};
+
+class CookieAccessTestHandler : public RoutingTestHandler,
+                                public CefCookieAccessFilter {
+ public:
+  enum TestMode {
+    ALLOW = 0,
+    BLOCK_READ = 1 << 0,
+    BLOCK_WRITE = 1 << 1,
+    BLOCK_READ_WRITE = BLOCK_READ | BLOCK_WRITE,
+    ALLOW_NO_FILTER = 1 << 2,
+
+    // Block all cookies using SetSupportedSchemes. Can only be used with a
+    // non-global request context because it's too late (during test execution)
+    // to call this method on the global context.
+    BLOCK_ALL_COOKIES = 1 << 3,
+
+    // Return nullptr from GetResourceRequestHandler. Can only be used in
+    // combination with the SERVER or SCHEME_HANDLER backend (the
+    // RESOURCE_HANDLER backend would not be called).
+    ALLOW_NO_HANDLER = 1 << 4,
+  };
+
+  enum TestBackend {
+    // Test an HTTP server backend.
+    SERVER,
+
+    // Test a custom scheme handler backend.
+    SCHEME_HANDLER,
+
+    // Test that GetResourceHandler behaves the same as a custom scheme handler.
+    RESOURCE_HANDLER,
+  };
+
+  CookieAccessTestHandler(TestMode test_mode,
+                          TestBackend test_backend,
+                          bool custom_scheme,
+                          bool use_global)
+      : test_mode_(test_mode),
+        test_backend_(test_backend),
+        scheme_(custom_scheme ? kCustomCookieScheme : kCookieAccessScheme),
+        use_global_(use_global) {
+    if (test_mode_ == BLOCK_ALL_COOKIES)
+      CHECK(!use_global_);
+    else if (test_mode_ == ALLOW_NO_HANDLER)
+      CHECK_NE(RESOURCE_HANDLER, test_backend_);
+    if (test_backend_ == SERVER)
+      CHECK(!custom_scheme);
+  }
+
+  void RunTest() override {
+    if (use_global_) {
+      context_ = CefRequestContext::GetGlobalContext();
+    } else {
+      // Create the request context that will use an in-memory cache.
+      CefRequestContextSettings settings;
+      context_ = CefRequestContext::CreateContext(settings, nullptr);
+    }
+
+    cookie_manager_ = context_->GetCookieManager(nullptr);
+
+    SetTestTimeout();
+
+    const bool block_cookies = (test_mode_ == BLOCK_ALL_COOKIES);
+    if (!use_global_ && (scheme_ == kCustomCookieScheme || block_cookies)) {
+      std::vector<CefString> schemes;
+      if (!block_cookies)
+        schemes.push_back(kCustomCookieScheme);
+
+      // Need to wait for completion before creating the browser.
+      cookie_manager_->SetSupportedSchemes(
+          schemes, !block_cookies /* include_defaults */,
+          new CompletionCallback(base::Bind(
+              &CookieAccessTestHandler::RunTestSetupContinue, this)));
+    } else {
+      RunTestSetupContinue();
+    }
+  }
+
+  void DestroyTest() override {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&CookieAccessTestHandler::DestroyTest, this));
+      return;
+    }
+
+    cookie_manager_ = nullptr;
+    context_ = nullptr;
+
+    // Got both network requests.
+    EXPECT_EQ(1, data1_.request_ct_);
+    EXPECT_EQ(1, data2_.request_ct_);
+
+    if (test_mode_ == ALLOW_NO_FILTER || test_mode_ == ALLOW_NO_HANDLER) {
+      EXPECT_EQ(0, can_save_cookie1_ct_);
+      EXPECT_EQ(0, can_send_cookie2_ct_);
+    } else {
+      // Get 1 call to CanSaveCookie for the 1st network request due to the
+      // network cookie.
+      EXPECT_EQ(1, can_save_cookie1_ct_);
+      if (test_mode_ == BLOCK_ALL_COOKIES) {
+        // Never send any cookies.
+        EXPECT_EQ(0, can_send_cookie2_ct_);
+      } else if (test_mode_ & BLOCK_WRITE) {
+        // Get 1 calls to CanSendCookie for the 2nd network request due to the
+        // JS cookie (network cookie is blocked).
+        EXPECT_EQ(1, can_send_cookie2_ct_);
+      } else {
+        // Get 2 calls to CanSendCookie for the 2nd network request due to the
+        // network cookie + JS cookie.
+        EXPECT_EQ(2, can_send_cookie2_ct_);
+      }
+    }
+
+    if (test_mode_ == BLOCK_ALL_COOKIES) {
+      // Never get the JS cookie via JS.
+      EXPECT_EQ(0, cookie_js1_ct_);
+      EXPECT_EQ(0, cookie_js2_ct_);
+      EXPECT_EQ(0, cookie_js3_ct_);
+    } else {
+      // Always get the JS cookie via JS.
+      EXPECT_EQ(1, cookie_js1_ct_);
+      EXPECT_EQ(1, cookie_js2_ct_);
+      EXPECT_EQ(1, cookie_js3_ct_);
+    }
+
+    // Only get the net cookie via JS if cookie write was allowed.
+    if ((test_mode_ & BLOCK_WRITE) || test_mode_ == BLOCK_ALL_COOKIES) {
+      EXPECT_EQ(0, cookie_net1_ct_);
+      EXPECT_EQ(0, cookie_net2_ct_);
+      EXPECT_EQ(0, cookie_net3_ct_);
+    } else {
+      EXPECT_EQ(1, cookie_net1_ct_);
+      EXPECT_EQ(1, cookie_net2_ct_);
+      EXPECT_EQ(1, cookie_net3_ct_);
+    }
+
+    // No cookies sent for the 1st network request.
+    EXPECT_EQ(0, data1_.cookie_js_ct_);
+    EXPECT_EQ(0, data1_.cookie_net_ct_);
+
+    // 2nd network request...
+    if ((test_mode_ & BLOCK_READ) || test_mode_ == BLOCK_ALL_COOKIES) {
+      // No cookies sent if reading was blocked.
+      EXPECT_EQ(0, data2_.cookie_js_ct_);
+      EXPECT_EQ(0, data2_.cookie_net_ct_);
+    } else if (test_mode_ & BLOCK_WRITE) {
+      // Only JS cookie sent if writing was blocked.
+      EXPECT_EQ(1, data2_.cookie_js_ct_);
+      EXPECT_EQ(0, data2_.cookie_net_ct_);
+    } else {
+      // All cookies sent.
+      EXPECT_EQ(1, data2_.cookie_js_ct_);
+      EXPECT_EQ(1, data2_.cookie_net_ct_);
+    }
+
+    TestHandler::DestroyTest();
+  }
+
+  CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+
+    if (test_mode_ == ALLOW_NO_FILTER)
+      return nullptr;
+
+    return this;
+  }
+
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override {
+    if (test_mode_ == ALLOW_NO_HANDLER)
+      return nullptr;
+
+    return this;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    if (test_backend_ == RESOURCE_HANDLER) {
+      return scheme_factory_->Create(browser, frame, scheme_, request);
+    }
+
+    return nullptr;
+  }
+
+  bool CanSendCookie(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     CefRefPtr<CefRequest> request,
+                     const CefCookie& cookie) override {
+    EXPECT_IO_THREAD();
+
+    const std::string& url = request->GetURL();
+    if (url == GetCookieAccessUrl2(scheme_, test_backend_ == SERVER)) {
+      can_send_cookie2_ct_++;
+    } else {
+      ADD_FAILURE() << "Unexpected url: " << url;
+    }
+
+    return !(test_mode_ & BLOCK_READ);
+  }
+
+  bool CanSaveCookie(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     CefRefPtr<CefRequest> request,
+                     CefRefPtr<CefResponse> response,
+                     const CefCookie& cookie) override {
+    EXPECT_IO_THREAD();
+
+    // Expecting the network cookie only.
+    EXPECT_STREQ("name_net", CefString(&cookie.name).ToString().c_str());
+    EXPECT_STREQ("value_net", CefString(&cookie.value).ToString().c_str());
+
+    const std::string& url = request->GetURL();
+    if (url == GetCookieAccessUrl1(scheme_, test_backend_ == SERVER)) {
+      can_save_cookie1_ct_++;
+    } else {
+      ADD_FAILURE() << "Unexpected url: " << url;
+    }
+
+    return !(test_mode_ & BLOCK_WRITE);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    const std::string& url = frame->GetURL();
+    const std::string& cookie_str = request.ToString();
+    if (url == GetCookieAccessUrl1(scheme_, test_backend_ == SERVER)) {
+      TestCookieString(cookie_str, cookie_js1_ct_, cookie_net1_ct_);
+      browser->GetMainFrame()->LoadURL(
+          GetCookieAccessUrl2(scheme_, test_backend_ == SERVER));
+    } else if (url == GetCookieAccessUrl2(scheme_, test_backend_ == SERVER)) {
+      TestCookieString(cookie_str, cookie_js2_ct_, cookie_net2_ct_);
+      FinishTest();
+    } else {
+      ADD_FAILURE() << "Unexpected url: " << url;
+    }
+    return true;
+  }
+
+ private:
+  void AddResponses(CookieAccessResponseHandler* handler) {
+    // 1st request sets a cookie via net response headers and JS, then retrieves
+    // the cookies via JS.
+    {
+      data1_.response = CefResponse::Create();
+      data1_.response->SetMimeType("text/html");
+      data1_.response->SetStatus(200);
+      data1_.response->SetStatusText("OK");
+
+      CefResponse::HeaderMap headerMap;
+      data1_.response->GetHeaderMap(headerMap);
+      headerMap.insert(std::make_pair("Set-Cookie", "name_net=value_net"));
+      data1_.response->SetHeaderMap(headerMap);
+
+      data1_.response_data =
+          "<html><head>"
+          "<script>"
+          "document.cookie='name_js=value_js';"
+          "window.testQuery({request:document.cookie});"
+          "</script>"
+          "</head><body>COOKIE ACCESS TEST 1</body></html>";
+
+      handler->AddResponse(
+          GetCookieAccessUrl1(scheme_, test_backend_ == SERVER), &data1_);
+    }
+
+    // 2nd request retrieves the cookies via JS.
+    {
+      data2_.response = CefResponse::Create();
+      data2_.response->SetMimeType("text/html");
+      data2_.response->SetStatus(200);
+      data2_.response->SetStatusText("OK");
+
+      data2_.response_data =
+          "<html><head>"
+          "<script>"
+          "window.testQuery({request:document.cookie});"
+          "</script>"
+          "</head><body>COOKIE ACCESS TEST 2</body></html>";
+
+      handler->AddResponse(
+          GetCookieAccessUrl2(scheme_, test_backend_ == SERVER), &data2_);
+    }
+  }
+
+  void RunTestSetupContinue() {
+    CefPostTask(TID_UI,
+                base::Bind(&CookieAccessTestHandler::StartBackend, this,
+                           base::Bind(&CookieAccessTestHandler::RunTestContinue,
+                                      this)));
+  }
+
+  void StartBackend(const base::Closure& complete_callback) {
+    if (test_backend_ == SERVER) {
+      StartServer(complete_callback);
+    } else {
+      StartSchemeHandler(complete_callback);
+    }
+  }
+
+  void StartServer(const base::Closure& complete_callback) {
+    EXPECT_FALSE(server_handler_);
+
+    server_handler_ = new CookieAccessServerHandler();
+    AddResponses(server_handler_.get());
+    server_handler_->CreateServer(complete_callback);
+  }
+
+  void StartSchemeHandler(const base::Closure& complete_callback) {
+    // Add the factory registration.
+    scheme_factory_ = new CookieAccessSchemeHandlerFactory();
+    AddResponses(scheme_factory_.get());
+    if (test_backend_ == SCHEME_HANDLER) {
+      context_->RegisterSchemeHandlerFactory(scheme_, kCookieAccessDomain,
+                                             scheme_factory_.get());
+    }
+
+    complete_callback.Run();
+  }
+
+  void RunTestContinue() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&CookieAccessTestHandler::RunTestContinue, this));
+      return;
+    }
+
+    CreateBrowser(GetCookieAccessUrl1(scheme_, test_backend_ == SERVER),
+                  context_);
+  }
+
+  void FinishTest() {
+    // Verify that cookies were set correctly.
+    class TestVisitor : public CefCookieVisitor {
+     public:
+      explicit TestVisitor(CookieAccessTestHandler* handler)
+          : handler_(handler) {}
+      ~TestVisitor() override {
+        // Destroy the test.
+        CefPostTask(
+            TID_UI,
+            base::Bind(
+                &CookieAccessTestHandler::ShutdownBackend, handler_,
+                base::Bind(&CookieAccessTestHandler::DestroyTest, handler_)));
+      }
+
+      bool Visit(const CefCookie& cookie,
+                 int count,
+                 int total,
+                 bool& deleteCookie) override {
+        const std::string& name = CefString(&cookie.name);
+        const std::string& value = CefString(&cookie.value);
+        if (name == "name_js" && value == "value_js")
+          handler_->cookie_js3_ct_++;
+        else if (name == "name_net" && value == "value_net")
+          handler_->cookie_net3_ct_++;
+
+        // Clean up the cookies.
+        deleteCookie = true;
+
+        return true;
+      }
+
+     private:
+      CookieAccessTestHandler* handler_;
+      IMPLEMENT_REFCOUNTING(TestVisitor);
+    };
+
+    cookie_manager_->VisitAllCookies(new TestVisitor(this));
+  }
+
+  void ShutdownBackend(const base::Closure& complete_callback) {
+    if (test_backend_ == SERVER) {
+      ShutdownServer(complete_callback);
+    } else {
+      ShutdownSchemeHandler(complete_callback);
+    }
+  }
+
+  void ShutdownServer(const base::Closure& complete_callback) {
+    EXPECT_TRUE(server_handler_);
+
+    server_handler_->ShutdownServer(complete_callback);
+    server_handler_ = nullptr;
+  }
+
+  void ShutdownSchemeHandler(const base::Closure& complete_callback) {
+    EXPECT_TRUE(scheme_factory_);
+
+    if (test_backend_ == SCHEME_HANDLER) {
+      context_->RegisterSchemeHandlerFactory(scheme_, kCookieAccessDomain,
+                                             nullptr);
+    }
+    scheme_factory_->Shutdown(complete_callback);
+    scheme_factory_ = nullptr;
+  }
+
+  const TestMode test_mode_;
+  const TestBackend test_backend_;
+  const std::string scheme_;
+  const bool use_global_;
+  CefRefPtr<CefRequestContext> context_;
+  CefRefPtr<CefCookieManager> cookie_manager_;
+
+  CefRefPtr<CookieAccessServerHandler> server_handler_;
+  CefRefPtr<CookieAccessSchemeHandlerFactory> scheme_factory_;
+
+  CookieAccessData data1_;
+  CookieAccessData data2_;
+
+  // 1st request.
+  int can_save_cookie1_ct_ = 0;
+  int cookie_js1_ct_ = 0;
+  int cookie_net1_ct_ = 0;
+
+  // 2nd request.
+  int can_send_cookie2_ct_ = 0;
+  int cookie_js2_ct_ = 0;
+  int cookie_net2_ct_ = 0;
+
+  // From cookie manager.
+  int cookie_js3_ct_ = 0;
+  int cookie_net3_ct_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(CookieAccessTestHandler);
+  IMPLEMENT_REFCOUNTING(CookieAccessTestHandler);
+};
+
+}  // namespace
+
+#define ACCESS_TEST(name, test_mode, backend_mode, custom_scheme, use_global) \
+  TEST(CookieTest, Access##name) {                                            \
+    CefRefPtr<CookieAccessTestHandler> handler = new CookieAccessTestHandler( \
+        CookieAccessTestHandler::test_mode,                                   \
+        CookieAccessTestHandler::backend_mode, custom_scheme, use_global);    \
+    handler->ExecuteTest();                                                   \
+    ReleaseAndWaitForDestructor(handler);                                     \
+  }
+
+#define ACCESS_TEST_ALL_MODES(name, backend_mode, custom_scheme, use_global) \
+  ACCESS_TEST(name##Allow, ALLOW, backend_mode, custom_scheme, use_global)   \
+  ACCESS_TEST(name##AllowNoFilter, ALLOW_NO_FILTER, backend_mode,            \
+              custom_scheme, use_global)                                     \
+  ACCESS_TEST(name##BlockRead, BLOCK_READ, backend_mode, custom_scheme,      \
+              use_global)                                                    \
+  ACCESS_TEST(name##BlockWrite, BLOCK_WRITE, backend_mode, custom_scheme,    \
+              use_global)                                                    \
+  ACCESS_TEST(name##BlockReadWrite, BLOCK_READ_WRITE, backend_mode,          \
+              custom_scheme, use_global)
+
+// These tests only work with a non-global context.
+#define ACCESS_TEST_BLOCKALLCOOKIES_MODES(name, backend_mode, custom_scheme) \
+  ACCESS_TEST(name##BlockAllCookies, BLOCK_ALL_COOKIES, backend_mode,        \
+              custom_scheme, false)
+
+// These tests only work with SERVER and SCHEME_HANDLER backends.
+#define ACCESS_TEST_ALLOWNOHANDLER_MODES(name, backend_mode, custom_scheme) \
+  ACCESS_TEST(name##GlobalAllowNoHandler, ALLOW_NO_HANDLER, backend_mode,   \
+              custom_scheme, false)                                         \
+  ACCESS_TEST(name##InMemoryAllowNoHandler, ALLOW_NO_HANDLER, backend_mode, \
+              custom_scheme, true)
+
+#define ACCESS_TEST_CUSTOM(name, backend_mode)                           \
+  ACCESS_TEST_ALL_MODES(name##CustomGlobal, backend_mode, true, true)    \
+  ACCESS_TEST_ALL_MODES(name##CustomInMemory, backend_mode, true, false) \
+  ACCESS_TEST_BLOCKALLCOOKIES_MODES(name##CustomInMemory, backend_mode, true)
+
+#define ACCESS_TEST_STANDARD(name, backend_mode)                            \
+  ACCESS_TEST_ALL_MODES(name##StandardGlobal, backend_mode, false, true)    \
+  ACCESS_TEST_ALL_MODES(name##StandardInMemory, backend_mode, false, false) \
+  ACCESS_TEST_BLOCKALLCOOKIES_MODES(name##StandardInMemory, backend_mode, false)
+
+// Server backend only works with standard schemes.
+ACCESS_TEST_STANDARD(Server, SERVER)
+ACCESS_TEST_ALLOWNOHANDLER_MODES(ServerStandard, SERVER, false)
+
+// Other backends work with all schemes.
+ACCESS_TEST_CUSTOM(Scheme, SCHEME_HANDLER)
+ACCESS_TEST_ALLOWNOHANDLER_MODES(SchemeCustom, SCHEME_HANDLER, true)
+ACCESS_TEST_STANDARD(Scheme, SCHEME_HANDLER)
+ACCESS_TEST_ALLOWNOHANDLER_MODES(SchemeStandard, SCHEME_HANDLER, false)
+
+ACCESS_TEST_CUSTOM(Resource, RESOURCE_HANDLER)
+ACCESS_TEST_STANDARD(Resource, RESOURCE_HANDLER)
+
+namespace {
+
+// Tests the behavior of restarting of a network request that sets cookies and
+// a network request that includes cookies.
+// 1. Begin loading URL1, then restart the request in OnResourceResponse.
+//    No cookies are saved.
+// 2. Load URL1 successfully. Network and JS cookies are saved.
+// 3. Begin loading URL2, then restart the request in OnResourceResponse.
+//    Cookies are sent with the request/response.
+// 4. Load URL2 successfully. Cookies are sent with the request/response.
+class CookieRestartTestHandler : public RoutingTestHandler,
+                                 public CefCookieAccessFilter {
+ public:
+  explicit CookieRestartTestHandler(bool use_global)
+      : scheme_(kCookieAccessScheme), use_global_(use_global) {}
+
+  void RunTest() override {
+    if (use_global_) {
+      context_ = CefRequestContext::GetGlobalContext();
+    } else {
+      // Create the request context that will use an in-memory cache.
+      CefRequestContextSettings settings;
+      context_ = CefRequestContext::CreateContext(settings, nullptr);
+    }
+
+    cookie_manager_ = context_->GetCookieManager(nullptr);
+
+    SetTestTimeout();
+    RunTestSetupContinue();
+  }
+
+  void DestroyTest() override {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&CookieRestartTestHandler::DestroyTest, this));
+      return;
+    }
+
+    cookie_manager_ = nullptr;
+    context_ = nullptr;
+
+    // Get 2 network requests for each URL.
+    EXPECT_EQ(2, data1_.request_ct_);
+    EXPECT_EQ(2, data2_.request_ct_);
+
+    // Get resource request callbacks for all requests (2 for each URL).
+    EXPECT_EQ(4, before_resource_load_ct_);
+    EXPECT_EQ(4, resource_response_ct_);
+
+    // Get JS query callbacks for the successful requests (1 for each URL).
+    EXPECT_EQ(2, query_ct_);
+
+    // No cookies sent for the URL1 network requests because (a) we don't have
+    // any cookies set initially and (b) we don't save cookies from the 1st URL1
+    // request which is restarted.
+    EXPECT_EQ(0, data1_.cookie_js_ct_);
+    EXPECT_EQ(0, data1_.cookie_net_ct_);
+
+    // Net and JS cookies sent for both URL2 network requests.
+    EXPECT_EQ(2, data2_.cookie_js_ct_);
+    EXPECT_EQ(2, data2_.cookie_net_ct_);
+
+    // 1 call to CanSaveCookie for the net cookie returned by the successful
+    // URL1 request.
+    EXPECT_EQ(1, can_save_cookie_ct_);
+    // 4 calls to CanSendCookie because both net and JS cookies are sent for
+    // each URL2 request.
+    EXPECT_EQ(4, can_send_cookie_ct_);
+
+    // Get the net and JS cookies from the JS query for the successful requests
+    // (1 for each URL).
+    EXPECT_EQ(1, cookie_js1_ct_);
+    EXPECT_EQ(1, cookie_net1_ct_);
+    EXPECT_EQ(1, cookie_js2_ct_);
+    EXPECT_EQ(1, cookie_net2_ct_);
+
+    // Get the net and JS cookies from the cookie manager at the end.
+    EXPECT_EQ(1, cookie_manager_js_ct_);
+    EXPECT_EQ(1, cookie_manager_net_ct_);
+
+    TestHandler::DestroyTest();
+  }
+
+  CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+    return this;
+  }
+
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override {
+    return this;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    return nullptr;
+  }
+
+  bool CanSendCookie(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     CefRefPtr<CefRequest> request,
+                     const CefCookie& cookie) override {
+    EXPECT_IO_THREAD();
+    can_send_cookie_ct_++;
+
+    // Called before the URL2 network requests.
+    EXPECT_LE(2, before_resource_load_ct_);
+
+    return true;
+  }
+
+  bool CanSaveCookie(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     CefRefPtr<CefRequest> request,
+                     CefRefPtr<CefResponse> response,
+                     const CefCookie& cookie) override {
+    EXPECT_IO_THREAD();
+    can_save_cookie_ct_++;
+
+    // Called after the successful URL1 network request.
+    EXPECT_EQ(2, before_resource_load_ct_);
+
+    // Expecting the network cookie only.
+    EXPECT_STREQ("name_net", CefString(&cookie.name).ToString().c_str());
+    EXPECT_STREQ("value_net", CefString(&cookie.value).ToString().c_str());
+
+    return true;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_IO_THREAD();
+    before_resource_load_ct_++;
+
+    const std::string& url = request->GetURL();
+
+    if (before_resource_load_ct_ <= 2) {
+      EXPECT_STREQ(GetCookieAccessUrl1(scheme_, true).c_str(), url.c_str());
+    } else {
+      EXPECT_STREQ(GetCookieAccessUrl2(scheme_, true).c_str(), url.c_str());
+    }
+
+    const std::string& cookie_str = request->GetHeaderByName("Cookie");
+    int cookie_js_ct = 0;
+    int cookie_net_ct = 0;
+    TestCookieString(cookie_str, cookie_js_ct, cookie_net_ct);
+
+    // Expect both cookies with the URL2 requests only.
+    if (before_resource_load_ct_ >= 3) {
+      EXPECT_EQ(1, cookie_js_ct);
+      EXPECT_EQ(1, cookie_net_ct);
+    } else {
+      EXPECT_EQ(0, cookie_js_ct);
+      EXPECT_EQ(0, cookie_net_ct);
+    }
+
+    return RV_CONTINUE;
+  }
+
+  bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+    resource_response_ct_++;
+
+    const std::string& url = request->GetURL();
+    const std::string& set_cookie_str = response->GetHeaderByName("Set-Cookie");
+
+    // Expect the network cookie with URL1 requests only.
+    if (resource_response_ct_ <= 2) {
+      EXPECT_STREQ(GetCookieAccessUrl1(scheme_, true).c_str(), url.c_str());
+      EXPECT_STREQ("name_net=value_net", set_cookie_str.c_str());
+    } else {
+      EXPECT_STREQ(GetCookieAccessUrl2(scheme_, true).c_str(), url.c_str());
+      EXPECT_TRUE(set_cookie_str.empty());
+    }
+
+    if (resource_response_ct_ == 1 || resource_response_ct_ == 3) {
+      // Restart the request loading this data.
+      request->SetHeaderByName("X-Custom-Header", "value", false);
+      return true;
+    }
+    return false;
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    query_ct_++;
+
+    const std::string& url = frame->GetURL();
+    const std::string& cookie_str = request.ToString();
+    if (url == GetCookieAccessUrl1(scheme_, true)) {
+      TestCookieString(cookie_str, cookie_js1_ct_, cookie_net1_ct_);
+      browser->GetMainFrame()->LoadURL(GetCookieAccessUrl2(scheme_, true));
+    } else if (url == GetCookieAccessUrl2(scheme_, true)) {
+      TestCookieString(cookie_str, cookie_js2_ct_, cookie_net2_ct_);
+      FinishTest();
+    } else {
+      ADD_FAILURE() << "Unexpected url: " << url;
+    }
+    return true;
+  }
+
+ private:
+  void AddResponses(CookieAccessResponseHandler* handler) {
+    // Sets a cookie via net response headers and JS, then retrieves the cookies
+    // via JS.
+    {
+      data1_.response = CefResponse::Create();
+      data1_.response->SetMimeType("text/html");
+      data1_.response->SetStatus(200);
+      data1_.response->SetStatusText("OK");
+
+      CefResponse::HeaderMap headerMap;
+      data1_.response->GetHeaderMap(headerMap);
+      headerMap.insert(std::make_pair("Set-Cookie", "name_net=value_net"));
+      data1_.response->SetHeaderMap(headerMap);
+
+      data1_.response_data =
+          "<html><head>"
+          "<script>"
+          "document.cookie='name_js=value_js';"
+          "window.testQuery({request:document.cookie});"
+          "</script>"
+          "</head><body>COOKIE RESTART TEST1</body></html>";
+
+      handler->AddResponse(GetCookieAccessUrl1(scheme_, true), &data1_);
+    }
+
+    // Retrieves the cookies via JS.
+    {
+      data2_.response = CefResponse::Create();
+      data2_.response->SetMimeType("text/html");
+      data2_.response->SetStatus(200);
+      data2_.response->SetStatusText("OK");
+
+      data2_.response_data =
+          "<html><head>"
+          "<script>"
+          "window.testQuery({request:document.cookie});"
+          "</script>"
+          "</head><body>COOKIE RESTART TEST2</body></html>";
+
+      handler->AddResponse(GetCookieAccessUrl2(scheme_, true), &data2_);
+    }
+  }
+
+  void RunTestSetupContinue() {
+    CefPostTask(
+        TID_UI,
+        base::Bind(
+            &CookieRestartTestHandler::StartServer, this,
+            base::Bind(&CookieRestartTestHandler::RunTestContinue, this)));
+  }
+
+  void StartServer(const base::Closure& complete_callback) {
+    EXPECT_FALSE(server_handler_);
+
+    server_handler_ = new CookieAccessServerHandler();
+    AddResponses(server_handler_.get());
+    // 2 requests for each URL.
+    server_handler_->SetExpectedRequestCount(4);
+    server_handler_->CreateServer(complete_callback);
+  }
+
+  void RunTestContinue() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&CookieRestartTestHandler::RunTestContinue, this));
+      return;
+    }
+
+    CreateBrowser(GetCookieAccessUrl1(scheme_, true), context_);
+  }
+
+  void FinishTest() {
+    // Verify that cookies were set correctly.
+    class TestVisitor : public CefCookieVisitor {
+     public:
+      explicit TestVisitor(CookieRestartTestHandler* handler)
+          : handler_(handler) {}
+      ~TestVisitor() override {
+        // Destroy the test.
+        CefPostTask(
+            TID_UI,
+            base::Bind(
+                &CookieRestartTestHandler::ShutdownServer, handler_,
+                base::Bind(&CookieRestartTestHandler::DestroyTest, handler_)));
+      }
+
+      bool Visit(const CefCookie& cookie,
+                 int count,
+                 int total,
+                 bool& deleteCookie) override {
+        const std::string& name = CefString(&cookie.name);
+        const std::string& value = CefString(&cookie.value);
+        if (name == "name_js" && value == "value_js")
+          handler_->cookie_manager_js_ct_++;
+        else if (name == "name_net" && value == "value_net")
+          handler_->cookie_manager_net_ct_++;
+
+        // Clean up the cookies.
+        deleteCookie = true;
+
+        return true;
+      }
+
+     private:
+      CookieRestartTestHandler* handler_;
+      IMPLEMENT_REFCOUNTING(TestVisitor);
+    };
+
+    cookie_manager_->VisitAllCookies(new TestVisitor(this));
+  }
+
+  void ShutdownServer(const base::Closure& complete_callback) {
+    EXPECT_TRUE(server_handler_);
+
+    server_handler_->ShutdownServer(complete_callback);
+    server_handler_ = nullptr;
+  }
+
+  const std::string scheme_;
+  const bool use_global_;
+  CefRefPtr<CefRequestContext> context_;
+  CefRefPtr<CefCookieManager> cookie_manager_;
+
+  CefRefPtr<CookieAccessServerHandler> server_handler_;
+
+  CookieAccessData data1_;
+  CookieAccessData data2_;
+
+  int before_resource_load_ct_ = 0;
+  int resource_response_ct_ = 0;
+  int query_ct_ = 0;
+
+  // From network requests.
+  int can_save_cookie_ct_ = 0;
+  int can_send_cookie_ct_ = 0;
+  int cookie_js1_ct_ = 0;
+  int cookie_net1_ct_ = 0;
+  int cookie_js2_ct_ = 0;
+  int cookie_net2_ct_ = 0;
+
+  // From cookie manager.
+  int cookie_manager_js_ct_ = 0;
+  int cookie_manager_net_ct_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(CookieRestartTestHandler);
+  IMPLEMENT_REFCOUNTING(CookieRestartTestHandler);
+};
+
+}  // namespace
+
+TEST(CookieTest, RestartGlobal) {
+  CefRefPtr<CookieRestartTestHandler> handler =
+      new CookieRestartTestHandler(true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(CookieTest, RestartInMemory) {
+  CefRefPtr<CookieRestartTestHandler> handler =
+      new CookieRestartTestHandler(false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Entry point for registering custom schemes.
+// Called from client_app_delegates.cc.
+void RegisterCookieCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar,
+                                 std::vector<CefString>& cookiable_schemes) {
+  // Used by GetCookieManagerCustom* tests.
+  registrar->AddCustomScheme(
+      kCustomCookieScheme,
+      CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
+  cookiable_schemes.push_back(kCustomCookieScheme);
+}
diff --git a/src/tests/ceftests/devtools_message_unittest.cc b/src/tests/ceftests/devtools_message_unittest.cc
new file mode 100644
index 0000000..13f070e
--- /dev/null
+++ b/src/tests/ceftests/devtools_message_unittest.cc
@@ -0,0 +1,375 @@
+// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+#include <sstream>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_callback.h"
+#include "include/cef_devtools_message_observer.h"
+#include "include/cef_parser.h"
+#include "include/wrapper/cef_closure_task.h"
+
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestUrl1[] = "http://tests/DevToolsMessage1";
+const char kTestUrl2[] = "http://tests/DevToolsMessage2";
+
+class DevToolsMessageTestHandler : public TestHandler {
+ public:
+  DevToolsMessageTestHandler() {}
+
+  void RunTest() override {
+    // Add HTML resources.
+    AddResource(kTestUrl1, "<html><body>Test1</body></html>", "text/html");
+    AddResource(kTestUrl2, "<html><body>Test2</body></html>", "text/html");
+
+    // Create the browser.
+    CreateBrowser(kTestUrl1);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    // STEP 1: Add the DevTools observer. Wait for the 1st load.
+    registration_ = browser->GetHost()->AddDevToolsMessageObserver(
+        new TestMessageObserver(this));
+    EXPECT_TRUE(registration_);
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (!isLoading) {
+      load_ct_++;
+      if (load_ct_ == 1) {
+        // STEP 2: 1st load has completed. Now enable page domain notifications
+        // and wait for the method result.
+        ExecuteMethod("Page.enable", "",
+                      base::Bind(&DevToolsMessageTestHandler::Navigate, this));
+      } else if (load_ct_ == 2) {
+        MaybeDestroyTest();
+      }
+    }
+  }
+
+  void DestroyTest() override {
+    // STEP 7: Remove the DevTools observer. This should result in the observer
+    // object being destroyed.
+    EXPECT_TRUE(registration_);
+    registration_ = nullptr;
+    EXPECT_TRUE(observer_destroyed_);
+
+    // Each send message variant should be called at least a single time.
+    EXPECT_GE(method_send_ct_, 1);
+    EXPECT_GE(method_execute_ct_, 1);
+
+    // All sent messages should receive a result callback.
+    EXPECT_EQ(expected_method_ct_, method_send_ct_ + method_execute_ct_);
+    EXPECT_EQ(expected_method_ct_, result_ct_);
+    EXPECT_EQ(expected_method_ct_, last_result_id_);
+
+    // Every received message should parse successfully to a result or event
+    // callback.
+    EXPECT_EQ(message_ct_, result_ct_ + event_ct_);
+
+    // Should receive 1 or more events (probably just 1, but who knows?).
+    EXPECT_GE(event_ct_, 1);
+
+    // OnLoadingStateChange(isLoading=false) should be called twice.
+    EXPECT_EQ(expected_load_ct_, load_ct_);
+
+    // Should get callbacks for agent attached but not detached.
+    EXPECT_EQ(1, attached_ct_);
+    EXPECT_EQ(0, detached_ct_);
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  struct MethodResult {
+    int message_id;
+    bool success;
+    std::string result;
+  };
+
+  struct Event {
+    std::string method;
+    std::string params;
+  };
+
+  class TestMessageObserver : public CefDevToolsMessageObserver {
+   public:
+    explicit TestMessageObserver(DevToolsMessageTestHandler* handler)
+        : handler_(handler) {}
+
+    virtual ~TestMessageObserver() { handler_->observer_destroyed_.yes(); }
+
+    bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser,
+                           const void* message,
+                           size_t message_size) override {
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
+      handler_->message_ct_++;
+      return false;
+    }
+
+    void OnDevToolsMethodResult(CefRefPtr<CefBrowser> browser,
+                                int message_id,
+                                bool success,
+                                const void* result,
+                                size_t result_size) override {
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
+      handler_->result_ct_++;
+
+      MethodResult mr;
+      mr.message_id = message_id;
+      mr.success = success;
+      if (result) {
+        // Intentionally truncating at small size.
+        mr.result = std::string(static_cast<const char*>(result),
+                                std::min(static_cast<int>(result_size), 80));
+      }
+      handler_->OnMethodResult(mr);
+    }
+
+    void OnDevToolsEvent(CefRefPtr<CefBrowser> browser,
+                         const CefString& method,
+                         const void* params,
+                         size_t params_size) override {
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
+      handler_->event_ct_++;
+
+      Event ev;
+      ev.method = method;
+      if (params) {
+        // Intentionally truncating at small size.
+        ev.params = std::string(static_cast<const char*>(params),
+                                std::min(static_cast<int>(params_size), 80));
+      }
+      handler_->OnEvent(ev);
+    }
+
+    void OnDevToolsAgentAttached(CefRefPtr<CefBrowser> browser) override {
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
+      handler_->attached_ct_++;
+    }
+
+    void OnDevToolsAgentDetached(CefRefPtr<CefBrowser> browser) override {
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
+      handler_->detached_ct_++;
+    }
+
+   private:
+    DevToolsMessageTestHandler* handler_;
+
+    IMPLEMENT_REFCOUNTING(TestMessageObserver);
+    DISALLOW_COPY_AND_ASSIGN(TestMessageObserver);
+  };
+
+  // Execute a DevTools method. Expected results will be verified in
+  // OnMethodResult, and |next_step| will then be executed.
+  // |expected_result| can be a fragment that the result should start with.
+  void ExecuteMethod(const std::string& method,
+                     const std::string& params,
+                     const base::Closure& next_step,
+                     const std::string& expected_result = "{}",
+                     bool expected_success = true) {
+    CHECK(!method.empty());
+    CHECK(!next_step.is_null());
+
+    int message_id = next_message_id_++;
+
+    std::stringstream message;
+    message << "{\"id\":" << message_id << ",\"method\":\"" << method << "\"";
+    if (!params.empty()) {
+      message << ",\"params\":" << params;
+    }
+    message << "}";
+
+    // Set expected result state.
+    pending_message_ = message.str();
+    pending_result_next_ = next_step;
+    pending_result_ = {message_id, expected_success, expected_result};
+
+    if (message_id % 2 == 0) {
+      // Use the less structured method.
+      method_send_ct_++;
+      GetBrowser()->GetHost()->SendDevToolsMessage(pending_message_.data(),
+                                                   pending_message_.size());
+    } else {
+      // Use the more structured method.
+      method_execute_ct_++;
+      CefRefPtr<CefDictionaryValue> dict;
+      if (!params.empty()) {
+        CefRefPtr<CefValue> value =
+            CefParseJSON(params.data(), params.size(), JSON_PARSER_RFC);
+        EXPECT_TRUE(value && value->GetType() == VTYPE_DICTIONARY)
+            << "failed to parse: " << params;
+        if (value && value->GetType() == VTYPE_DICTIONARY) {
+          dict = value->GetDictionary();
+        }
+      }
+      GetBrowser()->GetHost()->ExecuteDevToolsMethod(message_id, method, dict);
+    }
+  }
+
+  // Every call to ExecuteMethod should result in a single call to this method
+  // with the same message_id.
+  void OnMethodResult(const MethodResult& result) {
+    EXPECT_EQ(pending_result_.message_id, result.message_id)
+        << "with message=" << pending_message_;
+    if (result.message_id != pending_result_.message_id)
+      return;
+
+    EXPECT_EQ(pending_result_.success, result.success)
+        << "with message=" << pending_message_;
+
+    EXPECT_TRUE(result.result.find(pending_result_.result) == 0)
+        << "with message=" << pending_message_
+        << "\nand actual result=" << result.result
+        << "\nand expected result=" << pending_result_.result;
+
+    last_result_id_ = result.message_id;
+
+    // Continue asynchronously to allow the callstack to unwind.
+    CefPostTask(TID_UI, pending_result_next_);
+
+    // Clear expected result state.
+    pending_message_.clear();
+    pending_result_next_.Reset();
+    pending_result_ = {};
+  }
+
+  void OnEvent(const Event& event) {
+    if (event.method != pending_event_.method)
+      return;
+
+    EXPECT_TRUE(event.params.find(pending_event_.params) == 0)
+        << "with method=" << event.method
+        << "\nand actual params=" << event.params
+        << "\nand expected params=" << pending_event_.params;
+
+    // Continue asynchronously to allow the callstack to unwind.
+    CefPostTask(TID_UI, pending_event_next_);
+
+    // Clear expected result state.
+    pending_event_ = {};
+    pending_event_next_.Reset();
+  }
+
+  void Navigate() {
+    pending_event_ = {"Page.frameNavigated", "{\"frame\":"};
+    pending_event_next_ =
+        base::Bind(&DevToolsMessageTestHandler::AfterNavigate, this);
+
+    std::stringstream params;
+    params << "{\"url\":\"" << kTestUrl2 << "\"}";
+
+    // STEP 3: Page domain notifications are enabled. Now start a new
+    // navigation (but do nothing on method result) and wait for the
+    // "Page.frameNavigated" event.
+    ExecuteMethod("Page.navigate", params.str(), base::Bind(base::DoNothing),
+                  /*expected_result=*/"{\"frameId\":");
+  }
+
+  void AfterNavigate() {
+    // STEP 4: Got the "Page.frameNavigated" event. Now disable page domain
+    // notifications.
+    ExecuteMethod(
+        "Page.disable", "",
+        base::Bind(&DevToolsMessageTestHandler::AfterPageDisabled, this));
+  }
+
+  void AfterPageDisabled() {
+    // STEP 5: Got the the "Page.disable" method result. Now call a non-existant
+    // method to verify an error result, and then destroy the test when done.
+    ExecuteMethod(
+        "Foo.doesNotExist", "",
+        base::Bind(&DevToolsMessageTestHandler::MaybeDestroyTest, this),
+        /*expected_result=*/
+        "{\"code\":-32601,\"message\":\"'Foo.doesNotExist' wasn't found\"}",
+        /*expected_success=*/false);
+  }
+
+  void MaybeDestroyTest() {
+    if (result_ct_ == expected_method_ct_ && load_ct_ == expected_load_ct_) {
+      // STEP 6: Got confirmation of all expected method results and load
+      // events. Now destroy the test.
+      DestroyTest();
+    }
+  }
+
+  // Total # of times we're planning to call ExecuteMethod.
+  const int expected_method_ct_ = 4;
+
+  // Total # of times we're expecting OnLoadingStateChange(isLoading=false) to
+  // be called.
+  const int expected_load_ct_ = 2;
+
+  // In ExecuteMethod:
+  //   Next message ID to use.
+  int next_message_id_ = 1;
+  //   Last message that was sent (used for debug messages only).
+  std::string pending_message_;
+  //   SendDevToolsMessage call count.
+  int method_send_ct_ = 0;
+  //   ExecuteDevToolsMethod call count.
+  int method_execute_ct_ = 0;
+
+  // Expect |pending_result_.message_id| in OnMethodResult.
+  // The result should start with the |pending_result_.result| fragment.
+  MethodResult pending_result_;
+  //   Tracks the last message ID received.
+  int last_result_id_ = -1;
+  //   When received, execute this callback.
+  base::Closure pending_result_next_;
+
+  // Wait for |pending_event_.method| in OnEvent.
+  // The params should start with the |pending_event_.params| fragment.
+  Event pending_event_;
+  //   When received, execute this callback.
+  base::Closure pending_event_next_;
+
+  CefRefPtr<CefRegistration> registration_;
+
+  // Observer callback count.
+  int message_ct_ = 0;
+  int result_ct_ = 0;
+  int event_ct_ = 0;
+  int attached_ct_ = 0;
+  int detached_ct_ = 0;
+
+  // OnLoadingStateChange(isLoading=false) count.
+  int load_ct_ = 0;
+
+  TrackCallback observer_destroyed_;
+
+  IMPLEMENT_REFCOUNTING(DevToolsMessageTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(DevToolsMessageTestHandler);
+};
+
+}  // namespace
+
+// Test everything related to DevTools messages:
+// - CefDevToolsMessageObserver registration and life span.
+// - SendDevToolsMessage/ExecuteDevToolsMethod calls.
+// - CefDevToolsMessageObserver callbacks for method results and events.
+TEST(DevToolsMessageTest, Messages) {
+  CefRefPtr<DevToolsMessageTestHandler> handler =
+      new DevToolsMessageTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/dialog_unittest.cc b/src/tests/ceftests/dialog_unittest.cc
new file mode 100644
index 0000000..800dc1c
--- /dev/null
+++ b/src/tests/ceftests/dialog_unittest.cc
@@ -0,0 +1,341 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char* kTestUrl = "http://tests/DialogTestHandler";
+
+class DialogTestHandler : public TestHandler {
+ public:
+  struct TestConfig {
+    explicit TestConfig(FileDialogMode dialog_mode)
+        : mode(dialog_mode),
+          title("Test Title"),
+          default_file_name("Test File Name"),
+          selected_accept_filter(1),  // Something other than 0 for testing.
+          callback_async(false),
+          callback_cancel(false) {
+      accept_types.push_back("text/*");
+      accept_types.push_back(".js");
+      accept_types.push_back(".css");
+    }
+
+    FileDialogMode mode;
+    CefString title;
+    CefString default_file_name;
+    std::vector<CefString> accept_types;
+    int selected_accept_filter;
+
+    bool callback_async;  // True if the callback should execute asynchronously.
+    bool callback_cancel;  // True if the callback should cancel.
+    std::vector<CefString> callback_paths;  // Resulting paths if not cancelled.
+  };
+
+  class Callback : public CefRunFileDialogCallback {
+   public:
+    explicit Callback(DialogTestHandler* handler) : handler_(handler) {}
+
+    void OnFileDialogDismissed(
+        int selected_accept_filter,
+        const std::vector<CefString>& file_paths) override {
+      handler_->got_onfiledialogdismissed_.yes();
+
+      if (handler_->config_.callback_cancel) {
+        EXPECT_EQ(0, selected_accept_filter);
+        EXPECT_TRUE(file_paths.empty());
+      } else {
+        EXPECT_EQ(handler_->config_.selected_accept_filter,
+                  selected_accept_filter);
+        TestStringVectorEqual(handler_->config_.callback_paths, file_paths);
+      }
+
+      handler_->DestroyTest();
+      handler_ = nullptr;
+    }
+
+   private:
+    DialogTestHandler* handler_;
+
+    IMPLEMENT_REFCOUNTING(Callback);
+  };
+
+  explicit DialogTestHandler(const TestConfig& config) : config_(config) {}
+
+  void RunTest() override {
+    AddResource(kTestUrl, "<html><body>TEST</body></html>", "text/html");
+
+    // Create the browser
+    CreateBrowser(kTestUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    browser->GetHost()->RunFileDialog(
+        config_.mode, config_.title, config_.default_file_name,
+        config_.accept_types, config_.selected_accept_filter,
+        new Callback(this));
+  }
+
+  void ExecuteCallback(CefRefPtr<CefFileDialogCallback> callback) {
+    if (config_.callback_cancel)
+      callback->Cancel();
+    else
+      callback->Continue(config_.selected_accept_filter,
+                         config_.callback_paths);
+  }
+
+  // CefDialogHandler
+  bool OnFileDialog(CefRefPtr<CefBrowser> browser,
+                    FileDialogMode mode,
+                    const CefString& title,
+                    const CefString& default_file_name,
+                    const std::vector<CefString>& accept_types,
+                    int selected_accept_filter,
+                    CefRefPtr<CefFileDialogCallback> callback) override {
+    got_onfiledialog_.yes();
+
+    std::string url = browser->GetMainFrame()->GetURL();
+    EXPECT_STREQ(kTestUrl, url.c_str());
+
+    EXPECT_EQ(config_.mode, mode);
+    EXPECT_STREQ(config_.title.ToString().c_str(), title.ToString().c_str());
+    EXPECT_STREQ(config_.default_file_name.ToString().c_str(),
+                 default_file_name.ToString().c_str());
+    TestStringVectorEqual(config_.accept_types, accept_types);
+
+    if (config_.callback_async) {
+      CefPostTask(TID_UI, base::Bind(&DialogTestHandler::ExecuteCallback, this,
+                                     callback));
+    } else {
+      ExecuteCallback(callback);
+    }
+
+    return true;
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_onfiledialog_);
+    EXPECT_TRUE(got_onfiledialogdismissed_);
+
+    TestHandler::DestroyTest();
+  }
+
+  TestConfig config_;
+
+  TrackCallback got_onfiledialog_;
+  TrackCallback got_onfiledialogdismissed_;
+
+  IMPLEMENT_REFCOUNTING(DialogTestHandler);
+};
+
+}  // namespace
+
+// Test with all parameters empty.
+TEST(DialogTest, FileEmptyParams) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN);
+  config.title.clear();
+  config.default_file_name.clear();
+  config.accept_types.clear();
+  config.callback_async = false;
+  config.callback_cancel = false;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileAdditionalFlags) {
+  DialogTestHandler::TestConfig config(static_cast<cef_file_dialog_mode_t>(
+      FILE_DIALOG_OPEN | FILE_DIALOG_HIDEREADONLY_FLAG |
+      FILE_DIALOG_OVERWRITEPROMPT_FLAG));
+  config.title.clear();
+  config.default_file_name.clear();
+  config.accept_types.clear();
+  config.callback_async = false;
+  config.callback_cancel = false;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpen) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN);
+  config.callback_async = false;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/file1.txt");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenAsync) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN);
+  config.callback_async = true;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/file1.txt");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenAsyncCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenMultiple) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_MULTIPLE);
+  config.callback_async = false;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/file1.txt");
+  config.callback_paths.push_back("/path/to/file2.txt");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenMultipleCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_MULTIPLE);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenMultipleAsync) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_MULTIPLE);
+  config.callback_async = true;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/file1.txt");
+  config.callback_paths.push_back("/path/to/file2.txt");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenMultipleAsyncCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_MULTIPLE);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenFolder) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
+  config.callback_async = false;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/folder");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenFolderCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenFolderAsync) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
+  config.callback_async = true;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/folder");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileOpenFolderAsyncCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileSave) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_SAVE);
+  config.callback_async = false;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/file1.txt");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileSaveCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_SAVE);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileSaveAsync) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_SAVE);
+  config.callback_async = true;
+  config.callback_cancel = false;
+  config.callback_paths.push_back("/path/to/file1.txt");
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DialogTest, FileSaveAsyncCancel) {
+  DialogTestHandler::TestConfig config(FILE_DIALOG_SAVE);
+  config.callback_async = false;
+  config.callback_cancel = true;
+
+  CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/display_unittest.cc b/src/tests/ceftests/display_unittest.cc
new file mode 100644
index 0000000..06ba211
--- /dev/null
+++ b/src/tests/ceftests/display_unittest.cc
@@ -0,0 +1,526 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <list>
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// How it works:
+// 1. Load kTitleUrl1 (title should be kTitleStr1)
+// 2. Load kTitleUrl2 (title should be kTitleStr2)
+// 3. History back to kTitleUrl1 (title should be kTitleStr1)
+// 4. History forward to kTitleUrl2 (title should be kTitleStr2)
+// 5. Set title via JavaScript (title should be kTitleStr3)
+
+const char kTitleUrl1[] = "http://tests-title/nav1.html";
+const char kTitleUrl2[] = "http://tests-title/nav2.html";
+const char kTitleStr1[] = "Title 1";
+const char kTitleStr2[] = "Title 2";
+const char kTitleStr3[] = "Title 3";
+
+// Browser side.
+class TitleTestHandler : public TestHandler {
+ public:
+  TitleTestHandler()
+      : step_(0), got_title_change_(false), got_loading_state_change_(false) {}
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    AddResource(kTitleUrl1,
+                "<html><head><title>" + std::string(kTitleStr1) +
+                    "</title></head>Nav1</html>",
+                "text/html");
+    AddResource(kTitleUrl2,
+                "<html><head><title>" + std::string(kTitleStr2) +
+                    "</title></head>Nav2" +
+                    "<script>function setTitle() { window.document.title = '" +
+                    std::string(kTitleStr3) + "'; }</script>" + "</html>",
+                "text/html");
+
+    // Create the browser.
+    CreateBrowser(kTitleUrl1);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnTitleChange(CefRefPtr<CefBrowser> browser,
+                     const CefString& title) override {
+    // Ignore the 2nd OnTitleChange call which arrives after navigation
+    // completion.
+    if (got_title_change_)
+      return;
+
+    std::string title_str = title;
+    if (step_ == 0 || step_ == 2) {
+      EXPECT_STREQ(kTitleStr1, title_str.c_str());
+    } else if (step_ == 1 || step_ == 3) {
+      EXPECT_STREQ(kTitleStr2, title_str.c_str());
+    } else if (step_ == 4) {
+      EXPECT_STREQ(kTitleStr3, title_str.c_str());
+    }
+
+    got_title_[step_].yes();
+
+    if (step_ == 4) {
+      DestroyTest();
+    } else {
+      got_title_change_ = true;
+      NextIfReady(browser);
+    }
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    // Call NextIfReady asynchronously because an additional call to
+    // OnTitleChange will be triggered later in the current call stack due to
+    // navigation completion and we want that call to arrive before execution of
+    // NextIfReady.
+    got_loading_state_change_ = true;
+    CefPostTask(TID_UI,
+                base::Bind(&TitleTestHandler::NextIfReady, this, browser));
+  }
+
+ private:
+  void NextIfReady(CefRefPtr<CefBrowser> browser) {
+    if (!got_title_change_ || !got_loading_state_change_)
+      return;
+
+    got_title_change_ = false;
+    got_loading_state_change_ = false;
+
+    switch (step_++) {
+      case 0:
+        browser->GetMainFrame()->LoadURL(kTitleUrl2);
+        break;
+      case 1:
+        browser->GoBack();
+        break;
+      case 2:
+        browser->GoForward();
+        break;
+      case 3:
+        browser->GetMainFrame()->ExecuteJavaScript("setTitle()", kTitleUrl2, 0);
+        break;
+      default:
+        EXPECT_TRUE(false);  // Not reached.
+    }
+  }
+
+  void DestroyTest() override {
+    for (int i = 0; i < 5; ++i)
+      EXPECT_TRUE(got_title_[i]) << "step " << i;
+
+    TestHandler::DestroyTest();
+  }
+
+  int step_;
+
+  bool got_title_change_;
+  bool got_loading_state_change_;
+
+  TrackCallback got_title_[5];
+
+  IMPLEMENT_REFCOUNTING(TitleTestHandler);
+};
+
+}  // namespace
+
+// Test title notifications.
+TEST(DisplayTest, Title) {
+  CefRefPtr<TitleTestHandler> handler = new TitleTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kAutoResizeUrl[] = "http://tests-display/auto-resize.html";
+
+class AutoResizeTestHandler : public RoutingTestHandler {
+ public:
+  AutoResizeTestHandler() {}
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    AddResource(kAutoResizeUrl,
+                "<html><head><style>"
+                "body {overflow:hidden;margin:0px;padding:0px;}"
+                "</style></head><body><div id=a>Content</div></body></html>",
+                "text/html");
+
+    // Create the browser.
+    CreateBrowser(kAutoResizeUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    RoutingTestHandler::OnAfterCreated(browser);
+    browser->GetHost()->SetAutoResizeEnabled(true, CefSize(10, 10),
+                                             CefSize(500, 500));
+  }
+
+  bool OnAutoResize(CefRefPtr<CefBrowser> browser,
+                    const CefSize& new_size) override {
+    if (new_size.width == 1064 && new_size.height == 576) {
+      // Ignore this initial resize that may or may not occur.
+    } else if (!got_auto_resize1_) {
+      got_auto_resize1_.yes();
+      EXPECT_EQ(50, new_size.width);
+      EXPECT_EQ(18, new_size.height);
+
+      // Trigger a resize.
+      browser->GetMainFrame()->ExecuteJavaScript(
+          "document.getElementById('a').innerText='New Content';",
+          kAutoResizeUrl, 0);
+    } else if (!got_auto_resize2_) {
+      got_auto_resize2_.yes();
+      EXPECT_EQ(50, new_size.width);
+      EXPECT_EQ(36, new_size.height);
+
+      // Disable resize notifications.
+      browser->GetHost()->SetAutoResizeEnabled(false, CefSize(), CefSize());
+
+      // There should be no more resize notifications. End the test after a
+      // short delay.
+      browser->GetMainFrame()->ExecuteJavaScript(
+          "document.getElementById('a').innerText='New Content Again';"
+          "var interval = setInterval(function() {"
+          "window.testQuery({request:'done'});clearInterval(interval);}, 50);",
+          kAutoResizeUrl, 0);
+    } else {
+      EXPECT_TRUE(false);  // Not reached.
+    }
+    return true;
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    EXPECT_STREQ("done", request.ToString().c_str());
+    EXPECT_FALSE(got_done_message_);
+    got_done_message_.yes();
+    DestroyTest();
+    return true;
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_auto_resize1_);
+    EXPECT_TRUE(got_auto_resize2_);
+    EXPECT_TRUE(got_done_message_);
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  TrackCallback got_auto_resize1_;
+  TrackCallback got_auto_resize2_;
+  TrackCallback got_done_message_;
+
+  IMPLEMENT_REFCOUNTING(AutoResizeTestHandler);
+};
+
+}  // namespace
+
+// Test OnAutoResize notification.
+TEST(DisplayTest, AutoResize) {
+  CefRefPtr<AutoResizeTestHandler> handler = new AutoResizeTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// Browser side.
+class ConsoleTestHandler : public TestHandler {
+ public:
+  struct TestConfig {
+    // Use something other than 1 as |line| for testing.
+    explicit TestConfig(cef_log_severity_t message_level)
+        : level(message_level),
+          message("'Test Message'"),
+          expected_message("Test Message"),
+          source("http://tests-console-message/level.html"),
+          line(42) {}
+
+    cef_log_severity_t level;
+    std::string message;
+    std::string expected_message;
+    std::string source;
+    int line;
+    std::string function;
+  };
+
+  ConsoleTestHandler(const TestConfig& config) : config_(config) {}
+
+  void RunTest() override {
+    // Add the resources that will be used to print to console.
+    AddResource(
+        config_.source,
+        CreateResourceContent(config_.message, config_.function, config_.line),
+        "text/html");
+
+    // Create the browser.
+    CreateBrowser(config_.source);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    // Print console message after loading.
+    browser->GetMainFrame()->ExecuteJavaScript("printMessage()", config_.source,
+                                               0);
+  }
+
+  bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
+                        cef_log_severity_t level,
+                        const CefString& message,
+                        const CefString& source,
+                        int line) override {
+    EXPECT_EQ(config_.level, level);
+    EXPECT_EQ(config_.expected_message, message.ToString());
+    EXPECT_EQ(config_.source, source.ToString());
+    EXPECT_EQ(config_.line, line);
+
+    TestHandler::DestroyTest();
+
+    return false;
+  }
+
+ private:
+  std::string CreateResourceContent(const CefString& message,
+                                    const CefString& function,
+                                    int line) {
+    std::string content = "<html><script>function printMessage() { ";
+    for (int i = 1; i < line; ++i) {
+      // Add additional lines to test the |line| argument in |OnConsoleMessage|.
+      content += ";\n";
+    }
+    content += "console." + function.ToString() + "(" + message.ToString() +
+               "); }</script></html>";
+
+    return content;
+  }
+
+  TestConfig config_;
+
+  IMPLEMENT_REFCOUNTING(ConsoleTestHandler);
+};
+
+}  // namespace
+
+TEST(DisplayTest, OnConsoleMessageDebug) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_DEBUG);
+  config.function = "debug";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageCount) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_DEBUG);
+  config.function = "count";
+  config.expected_message = "Test Message: 1";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageTimeEnd) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_WARNING);
+  config.function = "timeEnd";
+  config.expected_message = "Timer 'Test Message' does not exist";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageInfo) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_INFO);
+  config.function = "info";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageLog) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_INFO);
+  config.function = "log";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageGroup) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_INFO);
+  config.function = "group";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageGroupCollapsed) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_INFO);
+  config.function = "groupCollapsed";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageGroupEnd) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_INFO);
+  config.function = "groupEnd";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageTable) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_INFO);
+  config.function = "table";
+  config.message = "[1, 2, 3]";
+  config.expected_message = "1,2,3";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageTrace) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_INFO);
+  config.function = "trace";
+  config.message = "";
+  config.expected_message = "console.trace";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageWarn) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_WARNING);
+  config.function = "warn";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageError) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_ERROR);
+  config.function = "error";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(DisplayTest, OnConsoleMessageAssert) {
+  ConsoleTestHandler::TestConfig config(LOGSEVERITY_ERROR);
+  config.function = "assert";
+  config.message = "false";
+  config.expected_message = "console.assert";
+
+  CefRefPtr<ConsoleTestHandler> handler = new ConsoleTestHandler(config);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kLoadinProgressUrl[] = "http://tests-display/loading-progress.html";
+
+// Browser side.
+class LoadingProgressTestHandler : public TestHandler {
+ public:
+  LoadingProgressTestHandler() {}
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    AddResource(kLoadinProgressUrl,
+                "<html><head><style>"
+                "body {overflow:hidden;margin:0px;padding:0px;}"
+                "</style></head><body><div id=a>Content</div></body></html>",
+                "text/html");
+
+    // Create the browser.
+    CreateBrowser(kLoadinProgressUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    DestroyTest();
+  }
+
+  void OnLoadingProgressChange(CefRefPtr<CefBrowser> browser,
+                               double progress) override {
+    if (!got_loading_progress_change0_) {
+      got_loading_progress_change0_.yes();
+      EXPECT_GE(progress, 0.0);
+    } else if (!got_loading_progress_change1_) {
+      got_loading_progress_change1_.yes();
+      EXPECT_LE(progress, 1.0);
+    }
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_loading_progress_change0_);
+    EXPECT_TRUE(got_loading_progress_change1_);
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  TrackCallback got_loading_progress_change0_;
+  TrackCallback got_loading_progress_change1_;
+
+  IMPLEMENT_REFCOUNTING(LoadingProgressTestHandler);
+};
+
+}  // namespace
+
+// Test OnLoadingProgressChange notification.
+TEST(DisplayTest, LoadingProgress) {
+  CefRefPtr<LoadingProgressTestHandler> handler =
+      new LoadingProgressTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/dom_unittest.cc b/src/tests/ceftests/dom_unittest.cc
new file mode 100644
index 0000000..1b26252
--- /dev/null
+++ b/src/tests/ceftests/dom_unittest.cc
@@ -0,0 +1,348 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_dom.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppRenderer;
+
+namespace {
+
+const char* kTestUrl = "http://tests/DOMTest.Test";
+const char* kTestMessage = "DOMTest.Message";
+
+enum DOMTestType {
+  DOM_TEST_STRUCTURE,
+  DOM_TEST_MODIFY,
+};
+
+class TestDOMVisitor : public CefDOMVisitor {
+ public:
+  explicit TestDOMVisitor(CefRefPtr<CefBrowser> browser, DOMTestType test_type)
+      : browser_(browser), test_type_(test_type) {}
+
+  void TestHeadNodeStructure(CefRefPtr<CefDOMNode> headNode) {
+    EXPECT_TRUE(headNode.get());
+    EXPECT_TRUE(headNode->IsElement());
+    EXPECT_FALSE(headNode->IsText());
+    EXPECT_EQ(headNode->GetName(), "HEAD");
+    EXPECT_EQ(headNode->GetElementTagName(), "HEAD");
+
+    EXPECT_TRUE(headNode->HasChildren());
+    EXPECT_FALSE(headNode->HasElementAttributes());
+
+    CefRefPtr<CefDOMNode> titleNode = headNode->GetFirstChild();
+    EXPECT_TRUE(titleNode.get());
+    EXPECT_TRUE(titleNode->IsElement());
+    EXPECT_FALSE(titleNode->IsText());
+    EXPECT_EQ(titleNode->GetName(), "TITLE");
+    EXPECT_EQ(titleNode->GetElementTagName(), "TITLE");
+    EXPECT_TRUE(titleNode->GetParent()->IsSame(headNode));
+
+    EXPECT_FALSE(titleNode->GetNextSibling().get());
+    EXPECT_FALSE(titleNode->GetPreviousSibling().get());
+    EXPECT_TRUE(titleNode->HasChildren());
+    EXPECT_FALSE(titleNode->HasElementAttributes());
+
+    CefRefPtr<CefDOMNode> textNode = titleNode->GetFirstChild();
+    EXPECT_TRUE(textNode.get());
+    EXPECT_FALSE(textNode->IsElement());
+    EXPECT_TRUE(textNode->IsText());
+    EXPECT_EQ(textNode->GetValue(), "The Title");
+    EXPECT_TRUE(textNode->GetParent()->IsSame(titleNode));
+
+    EXPECT_FALSE(textNode->GetNextSibling().get());
+    EXPECT_FALSE(textNode->GetPreviousSibling().get());
+    EXPECT_FALSE(textNode->HasChildren());
+  }
+
+  void TestBodyNodeStructure(CefRefPtr<CefDOMNode> bodyNode) {
+    EXPECT_TRUE(bodyNode.get());
+    EXPECT_TRUE(bodyNode->IsElement());
+    EXPECT_FALSE(bodyNode->IsText());
+    EXPECT_EQ(bodyNode->GetName(), "BODY");
+    EXPECT_EQ(bodyNode->GetElementTagName(), "BODY");
+
+    EXPECT_TRUE(bodyNode->HasChildren());
+    EXPECT_FALSE(bodyNode->HasElementAttributes());
+
+    CefRefPtr<CefDOMNode> h1Node = bodyNode->GetFirstChild();
+    EXPECT_TRUE(h1Node.get());
+    EXPECT_TRUE(h1Node->IsElement());
+    EXPECT_FALSE(h1Node->IsText());
+    EXPECT_EQ(h1Node->GetName(), "H1");
+    EXPECT_EQ(h1Node->GetElementTagName(), "H1");
+
+    EXPECT_TRUE(h1Node->GetNextSibling().get());
+    EXPECT_FALSE(h1Node->GetPreviousSibling().get());
+    EXPECT_TRUE(h1Node->HasChildren());
+    EXPECT_FALSE(h1Node->HasElementAttributes());
+
+    CefRefPtr<CefDOMNode> textNode = h1Node->GetFirstChild();
+    EXPECT_TRUE(textNode.get());
+    EXPECT_FALSE(textNode->IsElement());
+    EXPECT_TRUE(textNode->IsText());
+    EXPECT_EQ(textNode->GetValue(), "Hello From");
+
+    EXPECT_FALSE(textNode->GetPreviousSibling().get());
+    EXPECT_FALSE(textNode->HasChildren());
+
+    CefRefPtr<CefDOMNode> brNode = textNode->GetNextSibling();
+    EXPECT_TRUE(brNode.get());
+    EXPECT_TRUE(brNode->IsElement());
+    EXPECT_FALSE(brNode->IsText());
+    EXPECT_EQ(brNode->GetName(), "BR");
+    EXPECT_EQ(brNode->GetElementTagName(), "BR");
+
+    EXPECT_FALSE(brNode->HasChildren());
+
+    EXPECT_TRUE(brNode->HasElementAttributes());
+    EXPECT_TRUE(brNode->HasElementAttribute("class"));
+    EXPECT_EQ(brNode->GetElementAttribute("class"), "some_class");
+    EXPECT_TRUE(brNode->HasElementAttribute("id"));
+    EXPECT_EQ(brNode->GetElementAttribute("id"), "some_id");
+    EXPECT_FALSE(brNode->HasElementAttribute("no_existing"));
+
+    CefDOMNode::AttributeMap map;
+    brNode->GetElementAttributes(map);
+    ASSERT_EQ(map.size(), (size_t)2);
+    EXPECT_EQ(map["class"], "some_class");
+    EXPECT_EQ(map["id"], "some_id");
+
+    // Can also retrieve by ID.
+    brNode = bodyNode->GetDocument()->GetElementById("some_id");
+    EXPECT_TRUE(brNode.get());
+    EXPECT_TRUE(brNode->IsElement());
+    EXPECT_FALSE(brNode->IsText());
+    EXPECT_EQ(brNode->GetName(), "BR");
+    EXPECT_EQ(brNode->GetElementTagName(), "BR");
+
+    textNode = brNode->GetNextSibling();
+    EXPECT_TRUE(textNode.get());
+    EXPECT_FALSE(textNode->IsElement());
+    EXPECT_TRUE(textNode->IsText());
+    EXPECT_EQ(textNode->GetValue(), "Main Frame");
+
+    EXPECT_FALSE(textNode->GetNextSibling().get());
+    EXPECT_FALSE(textNode->HasChildren());
+
+    CefRefPtr<CefDOMNode> divNode = h1Node->GetNextSibling();
+    EXPECT_TRUE(divNode.get());
+    EXPECT_TRUE(divNode->IsElement());
+    EXPECT_FALSE(divNode->IsText());
+    CefRect divRect = divNode->GetElementBounds();
+    EXPECT_EQ(divRect.width, 50);
+    EXPECT_EQ(divRect.height, 25);
+    EXPECT_EQ(divRect.x, 150);
+    EXPECT_EQ(divRect.y, 100);
+    EXPECT_FALSE(divNode->GetNextSibling().get());
+  }
+
+  // Test document structure by iterating through the DOM tree.
+  void TestStructure(CefRefPtr<CefDOMDocument> document) {
+    EXPECT_EQ(document->GetTitle(), "The Title");
+    EXPECT_EQ(document->GetBaseURL(), kTestUrl);
+    EXPECT_EQ(document->GetCompleteURL("foo.html"), "http://tests/foo.html");
+
+    // Navigate the complete document structure.
+    CefRefPtr<CefDOMNode> docNode = document->GetDocument();
+    EXPECT_TRUE(docNode.get());
+    EXPECT_FALSE(docNode->IsElement());
+    EXPECT_FALSE(docNode->IsText());
+
+    CefRefPtr<CefDOMNode> htmlNode = docNode->GetFirstChild();
+    EXPECT_TRUE(htmlNode.get());
+    EXPECT_TRUE(htmlNode->IsElement());
+    EXPECT_FALSE(htmlNode->IsText());
+    EXPECT_EQ(htmlNode->GetName(), "HTML");
+    EXPECT_EQ(htmlNode->GetElementTagName(), "HTML");
+
+    EXPECT_TRUE(htmlNode->HasChildren());
+    EXPECT_FALSE(htmlNode->HasElementAttributes());
+
+    CefRefPtr<CefDOMNode> headNode = htmlNode->GetFirstChild();
+    TestHeadNodeStructure(headNode);
+
+    CefRefPtr<CefDOMNode> bodyNode = headNode->GetNextSibling();
+    TestBodyNodeStructure(bodyNode);
+
+    // Retrieve the head node directly.
+    headNode = document->GetHead();
+    TestHeadNodeStructure(headNode);
+
+    // Retrieve the body node directly.
+    bodyNode = document->GetBody();
+    TestBodyNodeStructure(bodyNode);
+  }
+
+  // Test document modification by changing the H1 tag.
+  void TestModify(CefRefPtr<CefDOMDocument> document) {
+    CefRefPtr<CefDOMNode> bodyNode = document->GetBody();
+    CefRefPtr<CefDOMNode> h1Node = bodyNode->GetFirstChild();
+
+    ASSERT_EQ(h1Node->GetAsMarkup(),
+              "<h1>Hello From<br class=\"some_class\" id=\"some_id\">"
+              "Main Frame</h1>");
+
+    CefRefPtr<CefDOMNode> textNode = h1Node->GetFirstChild();
+    ASSERT_EQ(textNode->GetValue(), "Hello From");
+    ASSERT_TRUE(textNode->SetValue("A Different Message From"));
+    ASSERT_EQ(textNode->GetValue(), "A Different Message From");
+
+    CefRefPtr<CefDOMNode> brNode = textNode->GetNextSibling();
+    EXPECT_EQ(brNode->GetElementAttribute("class"), "some_class");
+    EXPECT_TRUE(brNode->SetElementAttribute("class", "a_different_class"));
+    EXPECT_EQ(brNode->GetElementAttribute("class"), "a_different_class");
+
+    ASSERT_EQ(h1Node->GetAsMarkup(),
+              "<h1>A Different Message From<br class=\"a_different_class\" "
+              "id=\"some_id\">Main Frame</h1>");
+
+    ASSERT_FALSE(h1Node->SetValue("Something Different"));
+  }
+
+  void Visit(CefRefPtr<CefDOMDocument> document) override {
+    if (test_type_ == DOM_TEST_STRUCTURE)
+      TestStructure(document);
+    else if (test_type_ == DOM_TEST_MODIFY)
+      TestModify(document);
+
+    DestroyTest();
+  }
+
+ protected:
+  // Return from the test.
+  void DestroyTest() {
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kTestMessage);
+    EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
+    browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
+  }
+
+  CefRefPtr<CefBrowser> browser_;
+  DOMTestType test_type_;
+
+  IMPLEMENT_REFCOUNTING(TestDOMVisitor);
+};
+
+// Used in the render process.
+class DOMRendererTest : public ClientAppRenderer::Delegate {
+ public:
+  DOMRendererTest() {}
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName() == kTestMessage) {
+      EXPECT_EQ(message->GetArgumentList()->GetSize(), (size_t)1);
+      int test_type = message->GetArgumentList()->GetInt(0);
+
+      browser->GetMainFrame()->VisitDOM(
+          new TestDOMVisitor(browser, static_cast<DOMTestType>(test_type)));
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(DOMRendererTest);
+};
+
+// Used in the browser process.
+class TestDOMHandler : public TestHandler {
+ public:
+  explicit TestDOMHandler(DOMTestType test) : test_type_(test) {}
+
+  void RunTest() override {
+    std::stringstream mainHtml;
+    mainHtml << "<html>"
+                "<head><title>The Title</title></head>"
+                "<body>"
+                "<h1>Hello From<br class=\"some_class\"/ id=\"some_id\"/>"
+                "Main Frame</h1>"
+                "<div id=\"sized_element\" style=\"width: 50px; height: 25px; "
+                "position: fixed; top: 100px; left: 150px;\"/>"
+                "</body>"
+                "</html>";
+
+    AddResource(kTestUrl, mainHtml.str(), "text/html");
+    CreateBrowser(kTestUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (frame->IsMain()) {
+      // Start the test in the render process.
+      CefRefPtr<CefProcessMessage> message(
+          CefProcessMessage::Create(kTestMessage));
+      message->GetArgumentList()->SetInt(0, test_type_);
+      frame->SendProcessMessage(PID_RENDERER, message);
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    EXPECT_STREQ(message->GetName().ToString().c_str(), kTestMessage);
+
+    got_message_.yes();
+
+    if (message->GetArgumentList()->GetBool(0))
+      got_success_.yes();
+
+    // Test is complete.
+    DestroyTest();
+
+    return true;
+  }
+
+  DOMTestType test_type_;
+  TrackCallback got_message_;
+  TrackCallback got_success_;
+
+  IMPLEMENT_REFCOUNTING(TestDOMHandler);
+};
+
+}  // namespace
+
+// Test DOM structure reading.
+TEST(DOMTest, Read) {
+  CefRefPtr<TestDOMHandler> handler = new TestDOMHandler(DOM_TEST_STRUCTURE);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_message_);
+  EXPECT_TRUE(handler->got_success_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test DOM modifications.
+TEST(DOMTest, Modify) {
+  CefRefPtr<TestDOMHandler> handler = new TestDOMHandler(DOM_TEST_MODIFY);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_message_);
+  EXPECT_TRUE(handler->got_success_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Entry point for creating DOM renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateDOMRendererTests(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new DOMRendererTest);
+}
diff --git a/src/tests/ceftests/download_unittest.cc b/src/tests/ceftests/download_unittest.cc
new file mode 100644
index 0000000..6408f6f
--- /dev/null
+++ b/src/tests/ceftests/download_unittest.cc
@@ -0,0 +1,532 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_scheme.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_scoped_temp_dir.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/file_util.h"
+
+namespace {
+
+const char kTestDomain[] = "test-download.com";
+const char kTestStartUrl[] = "http://test-download.com/test.html";
+const char kTestDownloadUrl[] = "http://test-download.com/download.txt";
+const char kTestNavUrl[] = "http://test-download-nav.com/nav.html";
+const char kTestFileName[] = "download_test.txt";
+const char kTestContentDisposition[] =
+    "attachment; filename=\"download_test.txt\"";
+const char kTestMimeType[] = "text/plain";
+const char kTestContent[] = "Download test text";
+
+typedef base::Callback<void(const base::Closure& /*callback*/)> DelayCallback;
+
+class DownloadSchemeHandler : public CefResourceHandler {
+ public:
+  DownloadSchemeHandler(const DelayCallback& delay_callback,
+                        TrackCallback* got_download_request)
+      : delay_callback_(delay_callback),
+        got_download_request_(got_download_request),
+        should_delay_(false),
+        offset_(0) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    std::string url = request->GetURL();
+    if (url == kTestDownloadUrl) {
+      got_download_request_->yes();
+      content_ = kTestContent;
+      mime_type_ = kTestMimeType;
+      content_disposition_ = kTestContentDisposition;
+      should_delay_ = true;
+    } else {
+      EXPECT_TRUE(false);  // Not reached.
+
+      // Cancel immediately.
+      handle_request = true;
+      return false;
+    }
+
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    response_length = content_.size();
+
+    response->SetStatus(200);
+    response->SetMimeType(mime_type_);
+
+    if (!content_disposition_.empty()) {
+      CefResponse::HeaderMap headerMap;
+      response->GetHeaderMap(headerMap);
+      headerMap.insert(
+          std::make_pair("Content-Disposition", content_disposition_));
+      response->SetHeaderMap(headerMap);
+    }
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    bytes_read = 0;
+
+    if (should_delay_ && !delay_callback_.is_null()) {
+      // Delay the download response a single time.
+      delay_callback_.Run(base::Bind(&DownloadSchemeHandler::ContinueRead, this,
+                                     data_out, bytes_to_read, callback));
+      delay_callback_.Reset();
+      return true;
+    }
+
+    return DoRead(data_out, bytes_to_read, bytes_read);
+  }
+
+  void Cancel() override {}
+
+ private:
+  void ContinueRead(void* data_out,
+                    int bytes_to_read,
+                    CefRefPtr<CefResourceReadCallback> callback) {
+    int bytes_read = 0;
+    DoRead(data_out, bytes_to_read, bytes_read);
+    callback->Continue(bytes_read);
+  }
+
+  bool DoRead(void* data_out, int bytes_to_read, int& bytes_read) {
+    bool has_data = false;
+    size_t size = content_.size();
+    if (offset_ < size) {
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(size - offset_));
+      memcpy(data_out, content_.c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+  DelayCallback delay_callback_;
+  TrackCallback* got_download_request_;
+  bool should_delay_;
+  std::string content_;
+  std::string mime_type_;
+  std::string content_disposition_;
+  size_t offset_;
+  CefRefPtr<CefResourceReadCallback> read_callback_;
+
+  IMPLEMENT_REFCOUNTING(DownloadSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(DownloadSchemeHandler);
+};
+
+class DownloadSchemeHandlerFactory : public CefSchemeHandlerFactory {
+ public:
+  DownloadSchemeHandlerFactory(const DelayCallback& delay_callback,
+                               TrackCallback* got_download_request)
+      : delay_callback_(delay_callback),
+        got_download_request_(got_download_request) {}
+
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) override {
+    return new DownloadSchemeHandler(delay_callback_, got_download_request_);
+  }
+
+ private:
+  DelayCallback delay_callback_;
+  TrackCallback* got_download_request_;
+
+  IMPLEMENT_REFCOUNTING(DownloadSchemeHandlerFactory);
+  DISALLOW_COPY_AND_ASSIGN(DownloadSchemeHandlerFactory);
+};
+
+class DownloadTestHandler : public TestHandler {
+ public:
+  enum TestMode {
+    PROGAMMATIC,
+    NAVIGATED,
+    PENDING,
+    CLICKED,
+    CLICKED_REJECTED,
+  };
+
+  DownloadTestHandler(TestMode test_mode,
+                      TestRequestContextMode rc_mode,
+                      const std::string& rc_cache_path)
+      : test_mode_(test_mode),
+        rc_mode_(rc_mode),
+        rc_cache_path_(rc_cache_path),
+        download_id_(0),
+        verified_results_(false) {}
+
+  bool is_clicked() const {
+    return test_mode_ == CLICKED || test_mode_ == CLICKED_REJECTED;
+  }
+
+  void RunTest() override {
+    DelayCallback delay_callback;
+    if (test_mode_ == NAVIGATED || test_mode_ == PENDING)
+      delay_callback = base::Bind(&DownloadTestHandler::OnDelayCallback, this);
+
+    CefRefPtr<CefSchemeHandlerFactory> scheme_factory =
+        new DownloadSchemeHandlerFactory(delay_callback,
+                                         &got_download_request_);
+
+    CefRefPtr<CefRequestContext> request_context =
+        CreateTestRequestContext(rc_mode_, rc_cache_path_);
+    if (request_context) {
+      request_context->RegisterSchemeHandlerFactory("http", kTestDomain,
+                                                    scheme_factory);
+    } else {
+      CefRegisterSchemeHandlerFactory("http", kTestDomain, scheme_factory);
+    }
+
+    if (test_mode_ != CLICKED_REJECTED) {
+      // Create a new temporary directory.
+      EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+      test_path_ =
+          client::file_util::JoinPath(temp_dir_.GetPath(), kTestFileName);
+    }
+
+    if (test_mode_ == NAVIGATED) {
+      // Add the resource that we'll navigate to.
+      AddResource(kTestNavUrl, "<html><body>Navigated</body></html>",
+                  "text/html");
+    }
+
+    if (is_clicked()) {
+      std::string url;
+      if (test_mode_ == CLICKED) {
+        url = kTestDownloadUrl;
+      } else if (test_mode_ == CLICKED_REJECTED) {
+        url = "invalid:foo@example.com";
+      } else {
+        EXPECT_TRUE(false);  // Not reached.
+      }
+      AddResource(
+          kTestStartUrl,
+          "<html><body><a href=\"" + url + "\">CLICK ME</a></body></html>",
+          "text/html");
+    } else {
+      AddResource(kTestStartUrl, "<html><body>Download Test</body></html>",
+                  "text/html");
+    }
+
+    // Create the browser
+    CreateBrowser(kTestStartUrl, request_context);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    const std::string& url = frame->GetURL().ToString();
+    if (url == kTestNavUrl) {
+      got_nav_load_.yes();
+      ContinueNavigatedIfReady();
+      return;
+    }
+
+    if (is_clicked()) {
+      // Begin the download by clicking a link.
+      // ALT key will trigger download of custom protocol links.
+      SendClick(browser,
+                test_mode_ == CLICKED_REJECTED ? EVENTFLAG_ALT_DOWN : 0);
+
+      if (test_mode_ == CLICKED_REJECTED) {
+        // Destroy the test after a bit because there will be no further
+        // callbacks.
+        CefPostDelayedTask(
+            TID_UI, base::Bind(&DownloadTestHandler::DestroyTest, this), 200);
+      }
+    } else {
+      // Begin the download progammatically.
+      browser->GetHost()->StartDownload(kTestDownloadUrl);
+    }
+  }
+
+  // Callback from the scheme handler when the download request is delayed.
+  void OnDelayCallback(const base::Closure& callback) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&DownloadTestHandler::OnDelayCallback,
+                                     this, callback));
+      return;
+    }
+
+    got_delay_callback_.yes();
+
+    if (test_mode_ == NAVIGATED) {
+      delay_callback_ = callback;
+      ContinueNavigatedIfReady();
+    } else if (test_mode_ == PENDING) {
+      ContinuePendingIfReady();
+    } else {
+      EXPECT_TRUE(false);  // Not reached.
+    }
+  }
+
+  void ContinueNavigatedIfReady() {
+    EXPECT_EQ(test_mode_, NAVIGATED);
+    if (got_delay_callback_ && got_nav_load_) {
+      EXPECT_FALSE(delay_callback_.is_null());
+      delay_callback_.Run();
+      delay_callback_.Reset();
+    }
+  }
+
+  void ContinuePendingIfReady() {
+    EXPECT_EQ(test_mode_, PENDING);
+    if (got_delay_callback_ && got_on_before_download_ &&
+        got_on_download_updated_) {
+      // Destroy the test without waiting for the download to complete.
+      DestroyTest();
+    }
+  }
+
+  void OnBeforeDownload(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefDownloadItem> download_item,
+      const CefString& suggested_name,
+      CefRefPtr<CefBeforeDownloadCallback> callback) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_FALSE(got_on_before_download_);
+
+    got_on_before_download_.yes();
+
+    EXPECT_TRUE(browser->IsSame(GetBrowser()));
+    EXPECT_STREQ(kTestFileName, suggested_name.ToString().c_str());
+    EXPECT_TRUE(download_item.get());
+    EXPECT_TRUE(callback.get());
+
+    download_id_ = download_item->GetId();
+    EXPECT_LT(0U, download_id_);
+
+    EXPECT_TRUE(download_item->IsValid());
+    EXPECT_TRUE(download_item->IsInProgress());
+    EXPECT_FALSE(download_item->IsComplete());
+    EXPECT_FALSE(download_item->IsCanceled());
+    EXPECT_EQ(static_cast<int64>(sizeof(kTestContent) - 1),
+              download_item->GetTotalBytes());
+    EXPECT_EQ(0UL, download_item->GetFullPath().length());
+    EXPECT_STREQ(kTestDownloadUrl, download_item->GetURL().ToString().c_str());
+    EXPECT_EQ(0UL, download_item->GetSuggestedFileName().length());
+    EXPECT_STREQ(kTestContentDisposition,
+                 download_item->GetContentDisposition().ToString().c_str());
+    EXPECT_STREQ(kTestMimeType,
+                 download_item->GetMimeType().ToString().c_str());
+
+    callback->Continue(test_path_, false);
+
+    if (test_mode_ == NAVIGATED) {
+      CefRefPtr<CefFrame> main_frame = browser->GetMainFrame();
+      EXPECT_TRUE(main_frame->IsMain());
+      main_frame->LoadURL(kTestNavUrl);
+    } else if (test_mode_ == PENDING) {
+      ContinuePendingIfReady();
+    }
+  }
+
+  void OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefDownloadItem> download_item,
+                         CefRefPtr<CefDownloadItemCallback> callback) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+
+    if (destroyed_)
+      return;
+
+    got_on_download_updated_.yes();
+
+    EXPECT_TRUE(browser->IsSame(GetBrowser()));
+    EXPECT_TRUE(download_item.get());
+    EXPECT_TRUE(callback.get());
+
+    if (got_on_before_download_) {
+      EXPECT_EQ(download_id_, download_item->GetId());
+    }
+
+    EXPECT_LE(0LL, download_item->GetCurrentSpeed());
+    EXPECT_LE(0, download_item->GetPercentComplete());
+
+    EXPECT_TRUE(download_item->IsValid());
+    EXPECT_FALSE(download_item->IsCanceled());
+    EXPECT_STREQ(kTestDownloadUrl, download_item->GetURL().ToString().c_str());
+    EXPECT_STREQ(kTestContentDisposition,
+                 download_item->GetContentDisposition().ToString().c_str());
+    EXPECT_STREQ(kTestMimeType,
+                 download_item->GetMimeType().ToString().c_str());
+
+    std::string full_path = download_item->GetFullPath();
+    if (!full_path.empty()) {
+      got_full_path_.yes();
+      EXPECT_STREQ(test_path_.c_str(), full_path.c_str());
+    }
+
+    if (download_item->IsComplete()) {
+      got_download_complete_.yes();
+
+      EXPECT_FALSE(download_item->IsInProgress());
+      EXPECT_EQ(100, download_item->GetPercentComplete());
+      EXPECT_EQ(static_cast<int64>(sizeof(kTestContent) - 1),
+                download_item->GetReceivedBytes());
+      EXPECT_EQ(static_cast<int64>(sizeof(kTestContent) - 1),
+                download_item->GetTotalBytes());
+
+      DestroyTest();
+    } else {
+      EXPECT_TRUE(download_item->IsInProgress());
+      EXPECT_LE(0LL, download_item->GetReceivedBytes());
+    }
+
+    if (test_mode_ == PENDING) {
+      download_item_callback_ = callback;
+      ContinuePendingIfReady();
+    }
+  }
+
+  void VerifyResultsOnFileThread() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_FILE));
+
+    if (test_mode_ != PENDING) {
+      // Verify the file contents.
+      std::string contents;
+      EXPECT_TRUE(client::file_util::ReadFileToString(test_path_, &contents));
+      EXPECT_STREQ(kTestContent, contents.c_str());
+    }
+
+    EXPECT_TRUE(temp_dir_.Delete());
+    EXPECT_TRUE(temp_dir_.IsEmpty());
+
+    CefPostTask(TID_UI, base::Bind(&DownloadTestHandler::DestroyTest, this));
+  }
+
+  void DestroyTest() override {
+    if (!verified_results_ && !temp_dir_.IsEmpty()) {
+      // Avoid an endless failure loop.
+      verified_results_ = true;
+      // Clean up temp_dir_ on the FILE thread before destroying the test.
+      CefPostTask(
+          TID_FILE,
+          base::Bind(&DownloadTestHandler::VerifyResultsOnFileThread, this));
+      return;
+    }
+
+    destroyed_ = true;
+
+    if (download_item_callback_) {
+      // Cancel the pending download to avoid leaking request objects.
+      download_item_callback_->Cancel();
+      download_item_callback_ = nullptr;
+    }
+
+    if (request_context_) {
+      request_context_->RegisterSchemeHandlerFactory("http", kTestDomain,
+                                                     nullptr);
+      request_context_ = nullptr;
+    } else {
+      CefRegisterSchemeHandlerFactory("http", kTestDomain, nullptr);
+    }
+
+    if (test_mode_ == CLICKED_REJECTED) {
+      EXPECT_FALSE(got_download_request_);
+      EXPECT_FALSE(got_on_before_download_);
+      EXPECT_FALSE(got_on_download_updated_);
+    } else {
+      EXPECT_TRUE(got_download_request_);
+      EXPECT_TRUE(got_on_before_download_);
+      EXPECT_TRUE(got_on_download_updated_);
+    }
+
+    if (test_mode_ == NAVIGATED)
+      EXPECT_TRUE(got_nav_load_);
+    else
+      EXPECT_FALSE(got_nav_load_);
+
+    if (test_mode_ == PENDING || test_mode_ == CLICKED_REJECTED) {
+      EXPECT_FALSE(got_download_complete_);
+      EXPECT_FALSE(got_full_path_);
+    } else {
+      EXPECT_TRUE(got_download_complete_);
+      EXPECT_TRUE(got_full_path_);
+    }
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  void SendClick(CefRefPtr<CefBrowser> browser, uint32_t modifiers) {
+    EXPECT_TRUE(is_clicked());
+    CefMouseEvent mouse_event;
+    mouse_event.x = 20;
+    mouse_event.y = 20;
+    mouse_event.modifiers = modifiers;
+    browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1);
+    browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
+  }
+
+  const TestMode test_mode_;
+  const TestRequestContextMode rc_mode_;
+  const std::string rc_cache_path_;
+
+  CefRefPtr<CefRequestContext> request_context_;
+
+  // Used with NAVIGATED and PENDING test modes.
+  base::Closure delay_callback_;
+
+  // Used with PENDING test mode.
+  CefRefPtr<CefDownloadItemCallback> download_item_callback_;
+
+  CefScopedTempDir temp_dir_;
+  std::string test_path_;
+  uint32 download_id_;
+  bool verified_results_;
+  bool destroyed_ = false;
+
+  TrackCallback got_download_request_;
+  TrackCallback got_on_before_download_;
+  TrackCallback got_on_download_updated_;
+  TrackCallback got_full_path_;
+  TrackCallback got_download_complete_;
+  TrackCallback got_delay_callback_;
+  TrackCallback got_nav_load_;
+
+  IMPLEMENT_REFCOUNTING(DownloadTestHandler);
+};
+
+}  // namespace
+
+#define DOWNLOAD_TEST_GROUP(test_name, test_mode) \
+  RC_TEST_GROUP_ALL(DownloadTest, test_name, DownloadTestHandler, test_mode)
+
+// Test a programmatic download.
+DOWNLOAD_TEST_GROUP(Programmatic, PROGAMMATIC)
+
+// Test a clicked download.
+DOWNLOAD_TEST_GROUP(Clicked, CLICKED)
+
+// Test a clicked download where the protocol is invalid and therefore rejected.
+// There will be no resulting CefDownloadHandler callbacks.
+DOWNLOAD_TEST_GROUP(ClickedRejected, CLICKED_REJECTED)
+
+// Test where the download completes after cross-origin navigation.
+DOWNLOAD_TEST_GROUP(Navigated, NAVIGATED)
+
+// Test where the download is still pending when the browser is destroyed.
+DOWNLOAD_TEST_GROUP(Pending, PENDING)
diff --git a/src/tests/ceftests/draggable_regions_unittest.cc b/src/tests/ceftests/draggable_regions_unittest.cc
new file mode 100644
index 0000000..fea4fc0
--- /dev/null
+++ b/src/tests/ceftests/draggable_regions_unittest.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestURLWithRegions[] = "http://test.com/regions";
+const char kTestHTMLWithRegions[] =
+    "<html>"
+    "  <body>"
+    "    <div style=\"position: absolute; top: 50px; left: 50px; width: 200px; "
+    "height: 200px; background-color: red; -webkit-app-region: drag;\">"
+    "      <div style=\"position: absolute; top: 50%; left: 50%; "
+    "transform: translate(-50%, -50%); width: 50px; height: 50px; "
+    "background-color: blue; -webkit-app-region: no-drag;\">"
+    "      </div>"
+    "    </div>"
+    "  </body>"
+    "</html>";
+
+const char kTestURLWithoutRegions[] = "http://test.com/no-regions";
+const char kTestHTMLWithoutRegions[] = "<html><body>Hello World!</body></html>";
+
+const char kTestURLWithChangingRegions[] = "http://test.com/changing-regions";
+const char kTestHTMLWithChangingRegions[] =
+    "<html>"
+    "  <body>"
+    "    <div id=\"layer\" style=\"position: absolute; top: 50px; left: 50px; "
+    "width: 200px; height: 200px; background-color: red; "
+    "-webkit-app-region: drag;\">"
+    "      <div style=\"position: absolute; top: 50%; left: 50%; "
+    "transform: translate(-50%, -50%); width: 50px; height: 50px; "
+    "background-color: blue; -webkit-app-region: no-drag;\">"
+    "      </div>"
+    "    </div>"
+    "    <script>"
+    "      window.setTimeout(function() {"
+    "        var layer = document.getElementById('layer');"
+    "        layer.style.top = '0px';"
+    "        layer.style.left = '0px';"
+    "      }, 500);"
+    "    </script>"
+    "  </body>"
+    "</html>";
+
+class DraggableRegionsTestHandler : public TestHandler, public CefDragHandler {
+ public:
+  DraggableRegionsTestHandler() : step_(kStepWithRegions) {}
+
+  void RunTest() override {
+    // Add HTML documents with and without draggable regions.
+    AddResource(kTestURLWithRegions, kTestHTMLWithRegions, "text/html");
+    AddResource(kTestURLWithoutRegions, kTestHTMLWithoutRegions, "text/html");
+    AddResource(kTestURLWithChangingRegions, kTestHTMLWithChangingRegions,
+                "text/html");
+
+    // Create the browser
+    CreateBrowser(kTestURLWithRegions);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  CefRefPtr<CefDragHandler> GetDragHandler() override { return this; }
+
+  void OnDraggableRegionsChanged(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const std::vector<CefDraggableRegion>& regions) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(browser->IsSame(GetBrowser()));
+    EXPECT_TRUE(frame->IsMain());
+
+    did_call_on_draggable_regions_changed_.yes();
+
+    switch (step_) {
+      case kStepWithRegions:
+      case kStepWithChangingRegions1:
+        EXPECT_EQ(2U, regions.size());
+        EXPECT_EQ(50, regions[0].bounds.x);
+        EXPECT_EQ(50, regions[0].bounds.y);
+        EXPECT_EQ(200, regions[0].bounds.width);
+        EXPECT_EQ(200, regions[0].bounds.height);
+        EXPECT_EQ(1, regions[0].draggable);
+        EXPECT_EQ(125, regions[1].bounds.x);
+        EXPECT_EQ(125, regions[1].bounds.y);
+        EXPECT_EQ(50, regions[1].bounds.width);
+        EXPECT_EQ(50, regions[1].bounds.height);
+        EXPECT_EQ(0, regions[1].draggable);
+        break;
+      case kStepWithChangingRegions2:
+        EXPECT_EQ(2U, regions.size());
+        EXPECT_EQ(0, regions[0].bounds.x);
+        EXPECT_EQ(0, regions[0].bounds.y);
+        EXPECT_EQ(200, regions[0].bounds.width);
+        EXPECT_EQ(200, regions[0].bounds.height);
+        EXPECT_EQ(1, regions[0].draggable);
+        EXPECT_EQ(75, regions[1].bounds.x);
+        EXPECT_EQ(75, regions[1].bounds.y);
+        EXPECT_EQ(50, regions[1].bounds.width);
+        EXPECT_EQ(50, regions[1].bounds.height);
+        EXPECT_EQ(0, regions[1].draggable);
+        break;
+      case kStepWithoutRegions:
+        // Should not be reached.
+        EXPECT_TRUE(false);
+        break;
+    }
+
+    NextTest(browser);
+  }
+
+  void DestroyTest() override {
+    EXPECT_FALSE(did_call_on_draggable_regions_changed_);
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  void NextTest(CefRefPtr<CefBrowser> browser) {
+    CefRefPtr<CefFrame> frame(browser->GetMainFrame());
+
+    did_call_on_draggable_regions_changed_.reset();
+
+    switch (step_) {
+      case kStepWithRegions:
+        step_ = kStepWithChangingRegions1;
+        frame->LoadURL(kTestURLWithChangingRegions);
+        break;
+      case kStepWithChangingRegions1:
+        step_ = kStepWithChangingRegions2;
+        break;
+      case kStepWithChangingRegions2:
+        step_ = kStepWithoutRegions;
+        frame->LoadURL(kTestURLWithoutRegions);
+        // Needed because this test doesn't call OnDraggableRegionsChanged.
+        CefPostDelayedTask(
+            TID_UI, base::Bind(&DraggableRegionsTestHandler::DestroyTest, this),
+            500);
+        break;
+      case kStepWithoutRegions: {
+        // Should not be reached.
+        EXPECT_TRUE(false);
+        break;
+      }
+    }
+  }
+
+  enum Step {
+    kStepWithRegions,
+    kStepWithChangingRegions1,
+    kStepWithChangingRegions2,
+    kStepWithoutRegions,
+  } step_;
+
+  TrackCallback did_call_on_draggable_regions_changed_;
+
+  IMPLEMENT_REFCOUNTING(DraggableRegionsTestHandler);
+};
+
+}  // namespace
+
+// Verify that draggable regions work.
+TEST(DraggableRegionsTest, DraggableRegions) {
+  CefRefPtr<DraggableRegionsTestHandler> handler =
+      new DraggableRegionsTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/extensions/background_unittest.cc b/src/tests/ceftests/extensions/background_unittest.cc
new file mode 100644
index 0000000..b7961d2
--- /dev/null
+++ b/src/tests/ceftests/extensions/background_unittest.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/extensions/extension_test_handler.h"
+
+#include "tests/ceftests/test_util.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/browser/extension_util.h"
+
+using client::ClientAppBrowser;
+
+namespace {
+
+const char kExtensionPath[] = "background-extension";
+const char kBackgroundScript[] = "background.js";
+// HTML file created internally to load the background script.
+const char kGeneratedBackgroundPage[] = "_generated_background_page.html";
+
+// Test load/unload of an extension with a background script.
+class BackgroundLoadUnloadTestHandler : public ExtensionTestHandler {
+ public:
+  explicit BackgroundLoadUnloadTestHandler(
+      RequestContextType request_context_type)
+      : ExtensionTestHandler(request_context_type) {
+    // Only creating the extension browser.
+    set_create_main_browser(false);
+  }
+
+  // CefExtensionHandler methods:
+  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension->IsLoaded());
+    EXPECT_TRUE(extension->GetLoaderContext());
+    EXPECT_TRUE(
+        loader_request_context()->IsSame(extension->GetLoaderContext()));
+    VerifyExtension(extension);
+
+    EXPECT_FALSE(got_loaded_);
+    got_loaded_.yes();
+
+    EXPECT_FALSE(extension_);
+    extension_ = extension;
+
+    background_page_url_ = GetExtensionURL(extension, kGeneratedBackgroundPage);
+
+    // Add extension resources.
+    script_url_ = GetExtensionURL(extension, kBackgroundScript);
+    AddResource(script_url_, GetMessageJS("extension_onload"),
+                "text/javascript");
+  }
+
+  void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension);
+    EXPECT_FALSE(extension->IsLoaded());
+    EXPECT_FALSE(extension->GetLoaderContext());
+
+    EXPECT_FALSE(got_unloaded_);
+    got_unloaded_.yes();
+
+    EXPECT_TRUE(extension_);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    // The extension should no longer be registered with the context.
+    if (loader_request_context())
+      VerifyExtensionInContext(extension, loader_request_context(), false,
+                               true);
+    if (request_context() && !request_context_same_loader())
+      VerifyExtensionInContext(extension, request_context(), false, false);
+
+    extension_ = nullptr;
+
+    // Execute asynchronously so call stacks have a chance to unwind.
+    // Will close the browser windows.
+    CefPostTask(
+        TID_UI,
+        base::Bind(&BackgroundLoadUnloadTestHandler::DestroyTest, this));
+  }
+
+  bool OnBeforeBackgroundBrowser(CefRefPtr<CefExtension> extension,
+                                 const CefString& url,
+                                 CefRefPtr<CefClient>& client,
+                                 CefBrowserSettings& settings) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension->IsLoaded());
+    EXPECT_TRUE(extension->GetLoaderContext());
+    EXPECT_TRUE(
+        loader_request_context()->IsSame(extension->GetLoaderContext()));
+    VerifyExtension(extension);
+
+    const std::string& background_page_url =
+        GetExtensionURL(extension, kGeneratedBackgroundPage);
+    EXPECT_STREQ(background_page_url.c_str(), url.ToString().c_str());
+
+    EXPECT_FALSE(client);
+    client = this;
+
+    // Allow the browser creation.
+    return false;
+  }
+
+  // CefLoadHandler methods:
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    if (isLoading) {
+      EXPECT_FALSE(extension_browser_);
+      extension_browser_ = browser;
+    } else {
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      EXPECT_STREQ(background_page_url_.c_str(), url.c_str());
+
+      EXPECT_FALSE(got_load_done_);
+      got_load_done_.yes();
+
+      TriggerDestroyTestIfDone();
+    }
+  }
+
+  // CefResourceRequestHandler methods:
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    const std::string& url = request->GetURL();
+    if (url == background_page_url_) {
+      EXPECT_FALSE(got_background_page_url_request_);
+      got_background_page_url_request_.yes();
+    } else if (url == script_url_) {
+      EXPECT_FALSE(got_script_url_request_);
+      got_script_url_request_.yes();
+    } else {
+      EXPECT_TRUE(false);  // Not reached.
+    }
+
+    // Handle the resource request.
+    return RoutingTestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+  CefRefPtr<CefExtension> extension() const { return extension_; }
+
+  // Verify |extension| contents.
+  void VerifyExtension(CefRefPtr<CefExtension> extension) const {
+    EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
+                 client::extension_util::GetInternalExtensionResourcePath(
+                     extension->GetPath())
+                     .c_str());
+
+    CefRefPtr<CefDictionaryValue> expected_manifest = CreateManifest();
+    TestDictionaryEqual(expected_manifest, extension->GetManifest());
+
+    VerifyExtensionInContext(extension, loader_request_context(), true, true);
+    if (!request_context_same_loader())
+      VerifyExtensionInContext(extension, request_context(), true, false);
+  }
+
+  std::string GetExtensionURL(CefRefPtr<CefExtension> extension,
+                              const std::string& resource_path) const {
+    const std::string& identifier = extension->GetIdentifier();
+    const std::string& origin =
+        client::extension_util::GetExtensionOrigin(identifier);
+    EXPECT_FALSE(origin.empty());
+    return origin + resource_path;
+  }
+
+ protected:
+  void OnLoadExtensions() override {
+    LoadExtension(kExtensionPath, CreateManifest());
+  }
+
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    EXPECT_STREQ("extension_onload", message.c_str());
+    EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+    EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
+
+    EXPECT_FALSE(got_body_onload_);
+    got_body_onload_.yes();
+
+    TriggerDestroyTestIfDone();
+    return true;
+  }
+
+  void OnDestroyTest() override {
+    extension_browser_ = nullptr;
+
+    EXPECT_TRUE(got_loaded_);
+    EXPECT_TRUE(got_background_page_url_request_);
+    EXPECT_TRUE(got_script_url_request_);
+    EXPECT_TRUE(got_body_onload_);
+    EXPECT_TRUE(got_load_done_);
+    EXPECT_TRUE(got_unloaded_);
+  }
+
+  // Create a manifest with background script.
+  CefRefPtr<CefDictionaryValue> CreateManifest() const {
+    CefRefPtr<CefDictionaryValue> manifest =
+        CreateDefaultManifest(ApiPermissionsList());
+
+    CefRefPtr<CefDictionaryValue> background = CefDictionaryValue::Create();
+    CefRefPtr<CefListValue> scripts = CefListValue::Create();
+    scripts->SetString(0, kBackgroundScript);
+    background->SetList("scripts", scripts);
+    manifest->SetDictionary("background", background);
+
+    return manifest;
+  }
+
+  void TriggerDestroyTestIfDone() {
+    if (got_body_onload_ && got_load_done_) {
+      TriggerDestroyTest();
+    }
+  }
+
+  virtual void TriggerDestroyTest() {
+    // Execute asynchronously so call stacks have a chance to unwind.
+    CefPostTask(TID_UI,
+                base::Bind(&BackgroundLoadUnloadTestHandler::UnloadExtension,
+                           this, extension_));
+  }
+
+  CefRefPtr<CefExtension> extension_;
+  std::string script_url_;
+  std::string background_page_url_;
+  CefRefPtr<CefBrowser> extension_browser_;
+
+  TrackCallback got_loaded_;
+  TrackCallback got_background_page_url_request_;
+  TrackCallback got_script_url_request_;
+  TrackCallback got_body_onload_;
+  TrackCallback got_load_done_;
+  TrackCallback got_unloaded_;
+
+  IMPLEMENT_REFCOUNTING(BackgroundLoadUnloadTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(BackgroundLoadUnloadTestHandler);
+};
+
+}  // namespace
+
+EXTENSION_TEST_GROUP_ALL(BackgroundLoadUnload, BackgroundLoadUnloadTestHandler)
+
+namespace {
+
+// Same as above but without the unload. Only do this with a custom context to
+// avoid poluting the global context.
+class BackgroundLoadNoUnloadTestHandler
+    : public BackgroundLoadUnloadTestHandler {
+ public:
+  explicit BackgroundLoadNoUnloadTestHandler(
+      RequestContextType request_context_type)
+      : BackgroundLoadUnloadTestHandler(request_context_type) {}
+
+ protected:
+  void TriggerDestroyTest() override {
+    // Release everything that references the request context. This should
+    // trigger unload of the extension.
+    CloseBrowser(extension_browser_, false);
+    extension_browser_ = nullptr;
+    ReleaseRequestContexts();
+  }
+};
+
+}  // namespace
+
+EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(BackgroundLoadNoUnload,
+                                    BackgroundLoadNoUnloadTestHandler)
diff --git a/src/tests/ceftests/extensions/chrome_alarms_unittest.cc b/src/tests/ceftests/extensions/chrome_alarms_unittest.cc
new file mode 100644
index 0000000..a4abc86
--- /dev/null
+++ b/src/tests/ceftests/extensions/chrome_alarms_unittest.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/extensions/extension_test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/shared/browser/extension_util.h"
+
+#define ALARMS_TEST_GROUP_ALL(name, test_class) \
+  EXTENSION_TEST_GROUP_ALL(ChromeAlarms##name, test_class)
+#define ALARMS_TEST_GROUP_MINIMAL(name, test_class) \
+  EXTENSION_TEST_GROUP_MINIMAL(ChromeAlarms##name, test_class)
+
+namespace {
+
+const char kExtensionPath[] = "alarms-extension";
+const char kSuccessMessage[] = "success";
+
+// Base class for testing chrome.alarms methods.
+// See https://developer.chrome.com/extensions/alarms
+class AlarmsTestHandler : public ExtensionTestHandler {
+ public:
+  explicit AlarmsTestHandler(RequestContextType request_context_type)
+      : ExtensionTestHandler(request_context_type) {
+    // Only creating the extension browser.
+    set_create_main_browser(false);
+  }
+
+  // CefExtensionHandler methods:
+  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_FALSE(got_loaded_);
+    got_loaded_.yes();
+
+    // Verify |extension| contents.
+    EXPECT_FALSE(extension->GetIdentifier().empty());
+    EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
+                 client::extension_util::GetInternalExtensionResourcePath(
+                     extension->GetPath())
+                     .c_str());
+    TestDictionaryEqual(CreateManifest(), extension->GetManifest());
+
+    EXPECT_FALSE(extension_);
+    extension_ = extension;
+
+    CreateBrowserForExtension();
+  }
+
+  void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension_);
+    EXPECT_TRUE(extension_->IsSame(extension));
+    EXPECT_FALSE(got_unloaded_);
+    got_unloaded_.yes();
+    extension_ = nullptr;
+
+    // Execute asynchronously so call stacks have a chance to unwind.
+    // Will close the browser windows.
+    CefPostTask(TID_UI, base::Bind(&AlarmsTestHandler::DestroyTest, this));
+  }
+
+  // CefLoadHandler methods:
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    if (isLoading) {
+      EXPECT_FALSE(extension_browser_);
+      extension_browser_ = browser;
+    } else {
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      EXPECT_STREQ(extension_url_.c_str(), url.c_str());
+    }
+  }
+
+  // CefResourceRequestHandler methods:
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(extension_url_.c_str(), url.c_str());
+
+    EXPECT_FALSE(got_url_request_);
+    got_url_request_.yes();
+
+    // Handle the resource request.
+    return RoutingTestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+ protected:
+  void OnLoadExtensions() override {
+    LoadExtension(kExtensionPath, CreateManifest());
+  }
+
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    if (message == "extension_onload") {
+      // From body onLoad in the extension browser.
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+      EXPECT_FALSE(got_body_onload_);
+      got_body_onload_.yes();
+      TriggerAlarmsApiJSFunction();
+      return true;
+    }
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+    EXPECT_FALSE(got_success_message_);
+    got_success_message_.yes();
+    EXPECT_STREQ(kSuccessMessage, message.c_str());
+    TriggerDestroyTest();
+    return true;
+  }
+
+  void OnDestroyTest() override {
+    extension_browser_ = nullptr;
+
+    EXPECT_TRUE(got_loaded_);
+    EXPECT_TRUE(got_url_request_);
+    EXPECT_TRUE(got_body_onload_);
+    EXPECT_TRUE(got_trigger_api_function_);
+    EXPECT_TRUE(got_success_message_);
+    EXPECT_TRUE(got_unloaded_);
+  }
+
+  // Create a manifest that grants access to the alarms API.
+  virtual CefRefPtr<CefDictionaryValue> CreateManifest() const {
+    ApiPermissionsList api_permissions;
+    api_permissions.push_back("alarms");
+    return CreateDefaultManifest(api_permissions);
+  }
+
+  // Add resources in the extension browser.
+  virtual void OnAddExtensionResources(const std::string& origin) {
+    extension_url_ = origin + "extension.html";
+    AddResource(extension_url_, GetExtensionHTML(), "text/html");
+  }
+
+  // Returns the chrome.alarms.* JS that is executed in the extension browser
+  // when the triggerAlarmsApi() JS function is called.
+  virtual std::string GetAlarmsApiJS() const = 0;
+
+  // Returns the JS that will be loaded in the extension browser. This
+  // implements the triggerAlarmsApi() JS function called from
+  // TriggerAlarmsApiJSFunction().
+  virtual std::string GetExtensionJS() const {
+    return "function triggerAlarmsApi() {" + GetAlarmsApiJS() + "}";
+  }
+
+  // Returns the HTML that will be loaded in the extension browser.
+  virtual std::string GetExtensionHTML() const {
+    return "<html><head><script>" + GetExtensionJS() +
+           "</script></head><body onLoad=" + GetMessageJS("extension_onload") +
+           ">Extension</body></html>";
+  }
+
+  virtual void TriggerDestroyTest() {
+    // Execute asynchronously so call stacks have a chance to unwind.
+    CefPostTask(TID_UI, base::Bind(&AlarmsTestHandler::UnloadExtension, this,
+                                   extension_));
+  }
+
+  CefRefPtr<CefExtension> extension() const { return extension_; }
+  std::string extension_url() const { return extension_url_; }
+  CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; }
+
+  bool got_success_message() const { return got_success_message_; }
+
+ private:
+  void CreateBrowserForExtension() {
+    const std::string& identifier = extension_->GetIdentifier();
+    EXPECT_FALSE(identifier.empty());
+    const std::string& origin =
+        client::extension_util::GetExtensionOrigin(identifier);
+    EXPECT_FALSE(origin.empty());
+
+    // Add extension resources.
+    OnAddExtensionResources(origin);
+
+    // Create a browser to host the extension.
+    CreateBrowser(extension_url_, request_context());
+  }
+
+  void TriggerAlarmsApiJSFunction() {
+    EXPECT_FALSE(got_trigger_api_function_);
+    got_trigger_api_function_.yes();
+
+    extension_browser_->GetMainFrame()->ExecuteJavaScript("triggerAlarmsApi();",
+                                                          extension_url_, 0);
+  }
+
+  CefRefPtr<CefExtension> extension_;
+  std::string extension_url_;
+  CefRefPtr<CefBrowser> extension_browser_;
+
+  TrackCallback got_loaded_;
+  TrackCallback got_url_request_;
+  TrackCallback got_body_onload_;
+  TrackCallback got_trigger_api_function_;
+  TrackCallback got_success_message_;
+  TrackCallback got_unloaded_;
+};
+}  // namespace
+
+namespace {
+
+// Test for chrome.alarms.create(string name, object alarmInfo)
+// and chrome.alarms.onAlarm.addListener(function callback)
+class CreateAlarmTestHandler : public AlarmsTestHandler {
+ public:
+  explicit CreateAlarmTestHandler(RequestContextType request_context_type)
+      : AlarmsTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetAlarmsApiJS() const override {
+    return "chrome.alarms.onAlarm.addListener(function (alarm) {" +
+           GetMessageJS(kSuccessMessage) +
+           "});"
+           "chrome.alarms.create(\"test\", {delayInMinutes:0.01})";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(CreateAlarmTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(CreateAlarmTestHandler);
+};
+}  // namespace
+
+ALARMS_TEST_GROUP_ALL(CreateAlarm, CreateAlarmTestHandler)
+
+namespace {
+
+// Test for chrome.alarms.get(string name, function callback)
+class GetAlarmTestHandler : public AlarmsTestHandler {
+ public:
+  explicit GetAlarmTestHandler(RequestContextType request_context_type)
+      : AlarmsTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetAlarmsApiJS() const override {
+    return "chrome.alarms.create(\"test\", {delayInMinutes:1});"
+           "setTimeout(function() {"
+           "chrome.alarms.get(\"test\", function (alarm) {" +
+           GetMessageJS(kSuccessMessage) + "})}, 100)";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(GetAlarmTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(GetAlarmTestHandler);
+};
+}  // namespace
+
+ALARMS_TEST_GROUP_MINIMAL(GetAlarm, GetAlarmTestHandler)
+
+namespace {
+
+// Test for chrome.alarms.getAll(function callback)
+class GetAllAlarmsTestHandler : public AlarmsTestHandler {
+ public:
+  explicit GetAllAlarmsTestHandler(RequestContextType request_context_type)
+      : AlarmsTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetAlarmsApiJS() const override {
+    return "chrome.alarms.create(\"alarm1\", {delayInMinutes:1});"
+           "chrome.alarms.create(\"alarm2\", {delayInMinutes:1});"
+           "setTimeout(function() {"
+           "chrome.alarms.getAll(function (alarms) {"
+           "if (alarms.length == 2) {" +
+           GetMessageJS(kSuccessMessage) + "}})}, 100)";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(GetAllAlarmsTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(GetAllAlarmsTestHandler);
+};
+}  // namespace
+
+ALARMS_TEST_GROUP_MINIMAL(GetAllAlarms, GetAllAlarmsTestHandler)
+
+namespace {
+
+// Test for chrome.alarms.clear(string name, function callback)
+class ClearAlarmTestHandler : public AlarmsTestHandler {
+ public:
+  explicit ClearAlarmTestHandler(RequestContextType request_context_type)
+      : AlarmsTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetAlarmsApiJS() const override {
+    return "chrome.alarms.create(\"test\", {delayInMinutes:1});"
+           "setTimeout(function() {"
+           "chrome.alarms.clear(\"test\", function (wasCleared) {"
+           "if (wasCleared) {" +
+           GetMessageJS(kSuccessMessage) + "}})}, 100)";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(ClearAlarmTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ClearAlarmTestHandler);
+};
+}  // namespace
+
+ALARMS_TEST_GROUP_MINIMAL(ClearAlarm, ClearAlarmTestHandler)
+
+namespace {
+
+// Test for chrome.alarms.clearAll(function callback)
+class ClearAllAlarmsTestHandler : public AlarmsTestHandler {
+ public:
+  explicit ClearAllAlarmsTestHandler(RequestContextType request_context_type)
+      : AlarmsTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetAlarmsApiJS() const override {
+    return "chrome.alarms.create(\"alarm1\", {delayInMinutes:1});"
+           "chrome.alarms.create(\"alarm2\", {delayInMinutes:1});"
+           "setTimeout(function() {"
+           "chrome.alarms.clearAll(function (wasCleared) {"
+           "if (wasCleared) {" +
+           GetMessageJS(kSuccessMessage) + "}})}, 100)";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(ClearAllAlarmsTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ClearAllAlarmsTestHandler);
+};
+}  // namespace
+
+ALARMS_TEST_GROUP_MINIMAL(ClearAllAlarms, ClearAllAlarmsTestHandler)
diff --git a/src/tests/ceftests/extensions/chrome_storage_unittest.cc b/src/tests/ceftests/extensions/chrome_storage_unittest.cc
new file mode 100644
index 0000000..f71d6d8
--- /dev/null
+++ b/src/tests/ceftests/extensions/chrome_storage_unittest.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/extensions/extension_test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/shared/browser/extension_util.h"
+
+#define STORAGE_TEST_GROUP_ALL(name, test_class) \
+  EXTENSION_TEST_GROUP_ALL(ChromeStorage##name, test_class)
+#define STORAGE_TEST_GROUP_MINIMAL(name, test_class) \
+  EXTENSION_TEST_GROUP_MINIMAL(ChromeStorage##name, test_class)
+
+namespace {
+
+const char kExtensionPath[] = "storage-extension";
+const char kSuccessMessage[] = "success";
+
+// Base class for testing chrome.storage methods.
+// See https://developer.chrome.com/extensions/storage
+class StorageTestHandler : public ExtensionTestHandler {
+ public:
+  explicit StorageTestHandler(RequestContextType request_context_type)
+      : ExtensionTestHandler(request_context_type) {
+    // Only creating the extension browser.
+    set_create_main_browser(false);
+  }
+
+  // CefExtensionHandler methods:
+  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_FALSE(got_loaded_);
+    got_loaded_.yes();
+
+    // Verify |extension| contents.
+    EXPECT_FALSE(extension->GetIdentifier().empty());
+    EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
+                 client::extension_util::GetInternalExtensionResourcePath(
+                     extension->GetPath())
+                     .c_str());
+    TestDictionaryEqual(CreateManifest(), extension->GetManifest());
+
+    EXPECT_FALSE(extension_);
+    extension_ = extension;
+
+    CreateBrowserForExtension();
+  }
+
+  void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension_);
+    EXPECT_TRUE(extension_->IsSame(extension));
+    EXPECT_FALSE(got_unloaded_);
+    got_unloaded_.yes();
+    extension_ = nullptr;
+
+    // Execute asynchronously so call stacks have a chance to unwind.
+    // Will close the browser windows.
+    CefPostTask(TID_UI, base::Bind(&StorageTestHandler::DestroyTest, this));
+  }
+
+  // CefLoadHandler methods:
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    if (isLoading) {
+      EXPECT_FALSE(extension_browser_);
+      extension_browser_ = browser;
+    } else {
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      EXPECT_STREQ(extension_url_.c_str(), url.c_str());
+    }
+  }
+
+  // CefResourceRequestHandler methods:
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(extension_url_.c_str(), url.c_str());
+
+    EXPECT_FALSE(got_url_request_);
+    got_url_request_.yes();
+
+    // Handle the resource request.
+    return RoutingTestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+ protected:
+  void OnLoadExtensions() override {
+    LoadExtension(kExtensionPath, CreateManifest());
+  }
+
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    if (message == "extension_onload") {
+      // From body onLoad in the extension browser.
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+      EXPECT_FALSE(got_body_onload_);
+      got_body_onload_.yes();
+      TriggerStorageApiJSFunction();
+      return true;
+    }
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+    EXPECT_FALSE(got_success_message_);
+    got_success_message_.yes();
+    EXPECT_STREQ(kSuccessMessage, message.c_str());
+    TriggerDestroyTest();
+    return true;
+  }
+
+  void OnDestroyTest() override {
+    extension_browser_ = nullptr;
+
+    EXPECT_TRUE(got_loaded_);
+    EXPECT_TRUE(got_url_request_);
+    EXPECT_TRUE(got_body_onload_);
+    EXPECT_TRUE(got_trigger_api_function_);
+    EXPECT_TRUE(got_success_message_);
+    EXPECT_TRUE(got_unloaded_);
+  }
+
+  // Create a manifest that grants access to the storage API.
+  virtual CefRefPtr<CefDictionaryValue> CreateManifest() const {
+    ApiPermissionsList api_permissions;
+    api_permissions.push_back("storage");
+    return CreateDefaultManifest(api_permissions);
+  }
+
+  // Add resources in the extension browser.
+  virtual void OnAddExtensionResources(const std::string& origin) {
+    extension_url_ = origin + "extension.html";
+    AddResource(extension_url_, GetExtensionHTML(), "text/html");
+  }
+
+  // Returns the chrome.storage.* JS that is executed in the extension browser
+  // when the triggerStorageApi() JS function is called.
+  virtual std::string GetStorageApiJS() const = 0;
+
+  // Returns the JS that will be loaded in the extension browser. This
+  // implements the triggerStorageApi() JS function called from
+  // TriggerStorageApiJSFunction().
+  virtual std::string GetExtensionJS() const {
+    return "function triggerStorageApi() {" + GetStorageApiJS() + "}";
+  }
+
+  // Returns the HTML that will be loaded in the extension browser.
+  virtual std::string GetExtensionHTML() const {
+    return "<html><head><script>" + GetExtensionJS() +
+           "</script></head><body onLoad=" + GetMessageJS("extension_onload") +
+           ">Extension</body></html>";
+  }
+
+  virtual void TriggerDestroyTest() {
+    // Execute asynchronously so call stacks have a chance to unwind.
+    CefPostTask(TID_UI, base::Bind(&StorageTestHandler::UnloadExtension, this,
+                                   extension_));
+  }
+
+  CefRefPtr<CefExtension> extension() const { return extension_; }
+  std::string extension_url() const { return extension_url_; }
+  CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; }
+
+  bool got_success_message() const { return got_success_message_; }
+
+ private:
+  void CreateBrowserForExtension() {
+    const std::string& identifier = extension_->GetIdentifier();
+    EXPECT_FALSE(identifier.empty());
+    const std::string& origin =
+        client::extension_util::GetExtensionOrigin(identifier);
+    EXPECT_FALSE(origin.empty());
+
+    // Add extension resources.
+    OnAddExtensionResources(origin);
+
+    // Create a browser to host the extension.
+    CreateBrowser(extension_url_, request_context());
+  }
+
+  void TriggerStorageApiJSFunction() {
+    EXPECT_FALSE(got_trigger_api_function_);
+    got_trigger_api_function_.yes();
+
+    extension_browser_->GetMainFrame()->ExecuteJavaScript(
+        "triggerStorageApi();", extension_url_, 0);
+  }
+
+  CefRefPtr<CefExtension> extension_;
+  std::string extension_url_;
+  CefRefPtr<CefBrowser> extension_browser_;
+
+  TrackCallback got_loaded_;
+  TrackCallback got_url_request_;
+  TrackCallback got_body_onload_;
+  TrackCallback got_trigger_api_function_;
+  TrackCallback got_success_message_;
+  TrackCallback got_unloaded_;
+};
+}  // namespace
+
+namespace {
+
+// Test for chrome.storage.local.set(object items, function callback)
+// and  for chrome.storage.local.get(string or array of string or object keys,
+//                                   function callback)
+class LocalStorageTestHandler : public StorageTestHandler {
+ public:
+  explicit LocalStorageTestHandler(RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "chrome.storage.local.set({\"local_key_1\": \"local_value_1\"}, "
+           "function() {"
+           "chrome.storage.local.get(\"local_key_1\", function (items) {"
+           "if(items[\"local_key_1\"] == \"local_value_1\") {" +
+           GetMessageJS(kSuccessMessage) +
+           "}});"
+           "});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(LocalStorageTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(LocalStorageTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_ALL(LocalStorage, LocalStorageTestHandler)
+
+namespace {
+
+// Test for chrome.storage.local.getBytesInUse(string or array of string keys,
+//                                             function callback)
+class LocalStorageGetBytesInUseTestHandler : public StorageTestHandler {
+ public:
+  explicit LocalStorageGetBytesInUseTestHandler(
+      RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "chrome.storage.local.set({\"local_key_2\": \"local_value_2\"}, "
+           "function() {"
+           "chrome.storage.local.getBytesInUse(\"local_key_2\", function "
+           "(bytesInUse) {"
+           "if (bytesInUse == 26) {" +
+           GetMessageJS(kSuccessMessage) +
+           "}});"
+           "});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(LocalStorageGetBytesInUseTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(LocalStorageGetBytesInUseTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_MINIMAL(LocalStorageGetBytesInUse,
+                           LocalStorageGetBytesInUseTestHandler)
+
+namespace {
+
+// Test for chrome.storage.local.remove(string or array of string keys, function
+// callback)
+class LocalStorageRemoveTestHandler : public StorageTestHandler {
+ public:
+  explicit LocalStorageRemoveTestHandler(
+      RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "chrome.storage.local.set({\"local_key_3\": \"local_value_3\"}, "
+           "function() {"
+           "chrome.storage.local.remove(\"local_key_3\", function () {"
+           "chrome.storage.local.get(\"local_key_3\", function(items) {"
+           "if (items[\"local_key_3\"] == undefined) {" +
+           GetMessageJS(kSuccessMessage) +
+           "}})})"
+           "});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(LocalStorageRemoveTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(LocalStorageRemoveTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_MINIMAL(LocalStorageRemove, LocalStorageRemoveTestHandler)
+
+namespace {
+
+// Test for chrome.storage.local.clear(function callback)
+class LocalStorageClearTestHandler : public StorageTestHandler {
+ public:
+  explicit LocalStorageClearTestHandler(RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "var value1Cleared = false;"
+           "var value2Cleared = false;"
+           "function checkCleared() {"
+           "if (value1Cleared && value2Cleared) {" +
+           GetMessageJS(kSuccessMessage) +
+           "}}"
+           "chrome.storage.local.set({\"local_key_4\": \"local_value_4\","
+           "\"local_key_5\": \"local_value_5\"}, function() {"
+           "chrome.storage.local.clear(function () {"
+
+           "chrome.storage.local.get(\"local_key_4\", function(items) {"
+           "if (items[\"local_key_4\"] == undefined) {"
+           "value1Cleared = true;"
+           "checkCleared();"
+           "}});"
+
+           "chrome.storage.local.get(\"local_key_5\", function(items) {"
+           "if (items[\"local_key_5\"] == undefined) {"
+           "value2Cleared = true;"
+           "checkCleared();"
+           "}});"
+           "})});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(LocalStorageClearTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(LocalStorageClearTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_MINIMAL(LocalStorageClear, LocalStorageClearTestHandler)
+
+namespace {
+
+// Test for chrome.storage.sync.set(object items, function callback)
+// and  for chrome.storage.sync.get(string or array of string or object keys,
+//                                   function callback)
+class SyncStorageTestHandler : public StorageTestHandler {
+ public:
+  explicit SyncStorageTestHandler(RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "chrome.storage.sync.set({\"sync_key_1\": \"sync_value_1\"}, "
+           "function() {"
+           "chrome.storage.sync.get(\"sync_key_1\", function (items) {"
+           "if (items[\"sync_key_1\"] == \"sync_value_1\") {" +
+           GetMessageJS(kSuccessMessage) +
+           "}});"
+           "});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(SyncStorageTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(SyncStorageTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_ALL(SyncStorage, SyncStorageTestHandler)
+
+namespace {
+
+// Test for chrome.storage.sync.getBytesInUse(string or array of string keys,
+//                                             function callback)
+class SyncStorageGetBytesInUseTestHandler : public StorageTestHandler {
+ public:
+  explicit SyncStorageGetBytesInUseTestHandler(
+      RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "chrome.storage.sync.set({\"sync_key_2\": \"sync_value_2\"}, "
+           "function() {"
+           "chrome.storage.sync.getBytesInUse(\"sync_key_2\", function "
+           "(bytesInUse) {"
+           "if (bytesInUse == 24) {" +
+           GetMessageJS(kSuccessMessage) +
+           "}});"
+           "});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(SyncStorageGetBytesInUseTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(SyncStorageGetBytesInUseTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_MINIMAL(SyncStorageGetBytesInUse,
+                           SyncStorageGetBytesInUseTestHandler)
+
+namespace {
+
+// Test for chrome.storage.sync.remove(string or array of string keys, function
+// callback)
+class SyncStorageRemoveTestHandler : public StorageTestHandler {
+ public:
+  explicit SyncStorageRemoveTestHandler(RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "chrome.storage.sync.set({\"sync_key_3\": \"sync_value_3\"}, "
+           "function() {"
+           "chrome.storage.sync.remove(\"sync_key_3\", function () {"
+           "chrome.storage.sync.get(\"sync_key_3\", function(items) {"
+           "if (items[\"sync_key_3\"] == undefined) {" +
+           GetMessageJS(kSuccessMessage) +
+           "}})})"
+           "});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(SyncStorageRemoveTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(SyncStorageRemoveTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_MINIMAL(SyncStorageRemove, SyncStorageRemoveTestHandler)
+
+namespace {
+
+// Test for chrome.storage.sync.clear(function callback)
+class SyncStorageClearTestHandler : public StorageTestHandler {
+ public:
+  explicit SyncStorageClearTestHandler(RequestContextType request_context_type)
+      : StorageTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetStorageApiJS() const override {
+    return "var value1Cleared = false;"
+           "var value2Cleared = false;"
+
+           "function checkCleared() {"
+           "if (value1Cleared && value2Cleared) {" +
+           GetMessageJS(kSuccessMessage) +
+           "}}"
+
+           "chrome.storage.sync.set({\"sync_key_4\": \"sync_value_4\","
+           "\"sync_key_5\": \"sync_value_5\"}, function() {"
+           "chrome.storage.sync.clear(function () {"
+
+           "chrome.storage.sync.get(\"sync_key_4\", function(items) {"
+           "if (items[\"sync_key_4\"] == undefined) {"
+           "value1Cleared = true;"
+           "checkCleared();"
+           "}});"
+
+           "chrome.storage.sync.get(\"sync_key_5\", function(items) {"
+           "if (items[\"sync_key_5\"] == undefined) {"
+           "value2Cleared = true;"
+           "checkCleared();"
+           "}});"
+
+           "})});";
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(SyncStorageClearTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(SyncStorageClearTestHandler);
+};
+}  // namespace
+
+STORAGE_TEST_GROUP_MINIMAL(SyncStorageClear, SyncStorageClearTestHandler)
diff --git a/src/tests/ceftests/extensions/chrome_tabs_unittest.cc b/src/tests/ceftests/extensions/chrome_tabs_unittest.cc
new file mode 100644
index 0000000..ae0306e
--- /dev/null
+++ b/src/tests/ceftests/extensions/chrome_tabs_unittest.cc
@@ -0,0 +1,1218 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/extensions/extension_test_handler.h"
+
+#include <sstream>
+
+#include "include/cef_parser.h"
+
+#include "tests/ceftests/test_util.h"
+#include "tests/shared/browser/extension_util.h"
+
+#define TABS_TEST_GROUP_ALL(name, test_class) \
+  EXTENSION_TEST_GROUP_ALL(ChromeTabs##name, test_class)
+#define TABS_TEST_GROUP_MINIMAL(name, test_class) \
+  EXTENSION_TEST_GROUP_MINIMAL(ChromeTabs##name, test_class)
+
+namespace {
+
+const char kMainBrowserURL[] = "https://test-extensions.com/chrome-tabs";
+const char kExtensionPath[] = "tabs-extension";
+const char kSuccessMessage[] = "success";
+
+// Base class for testing chrome.tabs methods.
+// See https://developer.chrome.com/extensions/tabs
+class TabsTestHandler : public ExtensionTestHandler {
+ public:
+  explicit TabsTestHandler(RequestContextType request_context_type)
+      : ExtensionTestHandler(request_context_type),
+        create_main_browser_first_(false),
+        expect_get_active_browser_(true),
+        expect_success_in_main_browser_(true),
+        expected_api_call_count_(1),
+        got_get_active_browser_count_(0),
+        got_can_access_browser_count_(0) {}
+
+  // CefExtensionHandler methods:
+  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_FALSE(got_extension_loaded_);
+    got_extension_loaded_.yes();
+
+    // Verify |extension| contents.
+    EXPECT_FALSE(extension->GetIdentifier().empty());
+    EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
+                 client::extension_util::GetInternalExtensionResourcePath(
+                     extension->GetPath())
+                     .c_str());
+    TestDictionaryEqual(CreateManifest(), extension->GetManifest());
+
+    EXPECT_FALSE(extension_);
+    extension_ = extension;
+
+    if (create_main_browser_first_)
+      CreateBrowserForExtensionIfReady();
+    else
+      CreateBrowserForExtension();
+  }
+
+  void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension_);
+    EXPECT_TRUE(extension_->IsSame(extension));
+    EXPECT_FALSE(got_extension_unloaded_);
+    got_extension_unloaded_.yes();
+    extension_ = nullptr;
+
+    // Execute asynchronously so call stacks have a chance to unwind.
+    // Will close the browser windows.
+    CefPostTask(TID_UI, base::Bind(&TabsTestHandler::DestroyTest, this));
+  }
+
+  CefRefPtr<CefBrowser> GetActiveBrowser(CefRefPtr<CefExtension> extension,
+                                         CefRefPtr<CefBrowser> browser,
+                                         bool include_incognito) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension_);
+    EXPECT_TRUE(extension_->IsSame(extension));
+    EXPECT_TRUE(main_browser_);
+
+    EXPECT_LE(got_get_active_browser_count_, expected_api_call_count_);
+    got_get_active_browser_count_++;
+
+    // Tabs APIs will operate on the main browser.
+    return main_browser_;
+  }
+
+  bool CanAccessBrowser(CefRefPtr<CefExtension> extension,
+                        CefRefPtr<CefBrowser> browser,
+                        bool include_incognito,
+                        CefRefPtr<CefBrowser> target_browser) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension_);
+    EXPECT_TRUE(extension_->IsSame(extension));
+    EXPECT_TRUE(main_browser_);
+    EXPECT_TRUE(main_browser_->IsSame(target_browser));
+
+    EXPECT_LE(got_can_access_browser_count_, expected_api_call_count_);
+    got_can_access_browser_count_++;
+
+    return true;
+  }
+
+  // CefLoadHandler methods:
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading) {
+      // Keep a reference to both browsers.
+      if (browser->GetHost()->GetExtension()) {
+        EXPECT_FALSE(extension_browser_);
+        extension_browser_ = browser;
+      } else {
+        EXPECT_FALSE(main_browser_);
+        main_browser_ = browser;
+      }
+    } else {
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      if (browser->GetHost()->GetExtension()) {
+        EXPECT_TRUE(browser->IsSame(extension_browser_));
+        EXPECT_STREQ(extension_url_.c_str(), url.c_str());
+      } else {
+        EXPECT_TRUE(browser->IsSame(main_browser_));
+        EXPECT_STREQ(kMainBrowserURL, url.c_str());
+      }
+    }
+  }
+
+  // CefResourceRequestHandler methods:
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    const std::string& url = request->GetURL();
+    if (url == kMainBrowserURL) {
+      EXPECT_TRUE(browser->IsSame(main_browser_));
+      EXPECT_FALSE(got_main_url_request_);
+      got_main_url_request_.yes();
+    } else if (url == extension_url_) {
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+      EXPECT_FALSE(got_extension_url_request_);
+      got_extension_url_request_.yes();
+    }
+
+    // Handle the resource request.
+    return RoutingTestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+ protected:
+  void OnAddMainBrowserResources() override {
+    AddResource(kMainBrowserURL, GetMainBrowserHTML(), "text/html");
+  }
+
+  void OnCreateMainBrowser() override {
+    CreateBrowser(kMainBrowserURL, request_context());
+  }
+
+  void OnLoadExtensions() override {
+    LoadExtension(kExtensionPath, CreateManifest());
+  }
+
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    if (message == "main_onload") {
+      // From body onLoad in the main browser.
+      EXPECT_TRUE(browser->IsSame(main_browser_));
+      EXPECT_FALSE(got_main_body_onload_);
+      got_main_body_onload_.yes();
+      if (create_main_browser_first_)
+        CreateBrowserForExtensionIfReady();
+      TriggerTabsApiJSFunctionIfReady();
+      return true;
+    }
+    if (message == "extension_onload") {
+      // From body onLoad in the extension browser.
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+      EXPECT_FALSE(got_extension_body_onload_);
+      got_extension_body_onload_.yes();
+      TriggerTabsApiJSFunctionIfReady();
+      return true;
+    }
+
+    // The success message usually orginates from the logic in
+    // GetMainBrowserSuccessHEAD/BODY(). It may occasionally originate from the
+    // extension browser if we don't know how to detect success in the main
+    // browser.
+    if (expect_success_in_main_browser_) {
+      EXPECT_TRUE(browser->IsSame(main_browser_));
+    } else {
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+    }
+    EXPECT_FALSE(got_success_message_);
+    got_success_message_.yes();
+    EXPECT_STREQ(kSuccessMessage, message.c_str());
+    TriggerDestroyTest();
+    return true;
+  }
+
+  void OnDestroyTest() override {
+    main_browser_ = nullptr;
+    extension_browser_ = nullptr;
+
+    EXPECT_TRUE(got_extension_loaded_);
+    EXPECT_TRUE(got_main_url_request_);
+    EXPECT_TRUE(got_extension_url_request_);
+    EXPECT_TRUE(got_main_body_onload_);
+    EXPECT_TRUE(got_extension_body_onload_);
+    EXPECT_TRUE(got_trigger_api_function_);
+    EXPECT_TRUE(got_success_message_);
+    EXPECT_TRUE(got_extension_unloaded_);
+
+    if (expect_get_active_browser_) {
+      EXPECT_EQ(expected_api_call_count_, got_get_active_browser_count_);
+      EXPECT_EQ(0, got_can_access_browser_count_);
+    } else {
+      EXPECT_EQ(0, got_get_active_browser_count_);
+      EXPECT_EQ(expected_api_call_count_, got_can_access_browser_count_);
+    }
+  }
+
+  // Create a manifest that grants access to the tabs API.
+  virtual CefRefPtr<CefDictionaryValue> CreateManifest() const {
+    ApiPermissionsList api_permissions;
+    api_permissions.push_back("tabs");
+    return CreateDefaultManifest(api_permissions);
+  }
+
+  // Add resources in the extension browser.
+  virtual void OnAddExtensionResources(const std::string& origin) {
+    extension_url_ = origin + "extension.html";
+    AddResource(extension_url_, GetExtensionHTML(), "text/html");
+  }
+
+  // Returns the target tabId (null, or value >= 0).
+  virtual std::string GetTargetTabId() const { return "null"; }
+
+  // Returns the logic in the main browser that triggers on success. It should
+  // execute GetMessageJS(kSuccessMessage).
+  virtual std::string GetMainBrowserSuccessHEAD() const {
+    return std::string();
+  }
+  virtual std::string GetMainBrowserSuccessBODY() const {
+    return std::string();
+  }
+
+  // Returns the HTML that will be loaded in the main browser.
+  virtual std::string GetMainBrowserHTML() const {
+    return "<html><head>" + GetMainBrowserSuccessHEAD() +
+           "</head><body onLoad=" + GetMessageJS("main_onload") + ">Main" +
+           GetMainBrowserSuccessBODY() + "</body></html>";
+  }
+
+  // Returns the chrome.tabs.* JS that is executed in the extension browser
+  // when the triggerTabsApi() JS function is called.
+  virtual std::string GetTabsApiJS() const = 0;
+
+  // Returns the JS that will be loaded in the extension browser. This
+  // implements the triggerTabsApi() JS function called from
+  // TriggerTabsApiJSFunction().
+  virtual std::string GetExtensionJS() const {
+    return "function triggerTabsApi() {" + GetTabsApiJS() + "}";
+  }
+
+  // Returns the HTML that will be loaded in the extension browser.
+  virtual std::string GetExtensionHTML() const {
+    return "<html><head><script>" + GetExtensionJS() +
+           "</script></head><body onLoad=" + GetMessageJS("extension_onload") +
+           ">Extension</body></html>";
+  }
+
+  virtual void TriggerDestroyTest() {
+    // Execute asynchronously so call stacks have a chance to unwind.
+    CefPostTask(TID_UI, base::Bind(&TabsTestHandler::UnloadExtension, this,
+                                   extension_));
+  }
+
+  CefRefPtr<CefExtension> extension() const { return extension_; }
+  std::string extension_url() const { return extension_url_; }
+  CefRefPtr<CefBrowser> main_browser() const { return main_browser_; }
+  CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; }
+
+  void set_create_main_browser_first(bool val) {
+    create_main_browser_first_ = val;
+  }
+  void set_expect_get_active_browser(bool val) {
+    expect_get_active_browser_ = val;
+  }
+  void set_expect_success_in_main_browser(bool val) {
+    expect_success_in_main_browser_ = val;
+  }
+  void set_expected_api_call_count(int val) { expected_api_call_count_ = val; }
+
+  bool got_success_message() const { return got_success_message_; }
+  void set_got_success_message() { got_success_message_.yes(); }
+
+ private:
+  void CreateBrowserForExtensionIfReady() {
+    DCHECK(create_main_browser_first_);
+    if (extension_ && main_browser_)
+      CreateBrowserForExtension();
+  }
+
+  void CreateBrowserForExtension() {
+    const std::string& identifier = extension_->GetIdentifier();
+    EXPECT_FALSE(identifier.empty());
+    const std::string& origin =
+        client::extension_util::GetExtensionOrigin(identifier);
+    EXPECT_FALSE(origin.empty());
+
+    // Add extension resources.
+    OnAddExtensionResources(origin);
+
+    // Create a browser to host the extension.
+    CreateBrowser(extension_url_, request_context());
+  }
+
+  void TriggerTabsApiJSFunctionIfReady() {
+    if (got_main_body_onload_ && got_extension_body_onload_)
+      TriggerTabsApiJSFunction();
+  }
+
+  void TriggerTabsApiJSFunction() {
+    EXPECT_FALSE(got_trigger_api_function_);
+    got_trigger_api_function_.yes();
+
+    extension_browser_->GetMainFrame()->ExecuteJavaScript("triggerTabsApi();",
+                                                          extension_url_, 0);
+  }
+
+  // If true the main browser will be created before the extension browser.
+  // Otherwise the creation order is undefined.
+  bool create_main_browser_first_;
+
+  // If true we expect GetActiveBrowser() but not CanAccessBrowser() to be
+  // called. Else visa-versa.
+  bool expect_get_active_browser_;
+
+  // If true we expect the success message to be delivered in the main browser.
+  // Else expect it in the extension browser.
+  bool expect_success_in_main_browser_;
+
+  // Number of expected calls to GetActiveBrowser or CanAccessBrowser. This
+  // should match the number of calls to chrome.tabs.* API functions in the
+  // test.
+  int expected_api_call_count_;
+
+  CefRefPtr<CefExtension> extension_;
+  std::string extension_url_;
+  CefRefPtr<CefBrowser> main_browser_;
+  CefRefPtr<CefBrowser> extension_browser_;
+
+  TrackCallback got_extension_loaded_;
+  TrackCallback got_main_url_request_;
+  TrackCallback got_extension_url_request_;
+  TrackCallback got_main_body_onload_;
+  TrackCallback got_extension_body_onload_;
+  TrackCallback got_trigger_api_function_;
+  TrackCallback got_success_message_;
+  TrackCallback got_extension_unloaded_;
+
+  int got_get_active_browser_count_;
+  int got_can_access_browser_count_;
+};
+
+//
+// chrome.tabs.create tests.
+//
+
+const char kCreateBrowserURL[] =
+    "https://test-extensions.com/chrome-tabs-create";
+const char kTabCallbackMessage[] = "tab-callback";
+const int kCreateTabIndex = 2;
+
+// Class for chrome.tabs.create tests.
+class CreateTestHandler : public TabsTestHandler {
+ public:
+  explicit CreateTestHandler(RequestContextType request_context_type)
+      : TabsTestHandler(request_context_type) {}
+
+  bool OnBeforeBrowser(CefRefPtr<CefExtension> extension,
+                       CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefBrowser> active_browser,
+                       int index,
+                       const CefString& url,
+                       bool foreground,
+                       CefWindowInfo& windowInfo,
+                       CefRefPtr<CefClient>& client,
+                       CefBrowserSettings& settings) override {
+    EXPECT_TRUE(extension->IsSame(this->extension()));
+    EXPECT_TRUE(browser->IsSame(extension_browser()));
+    EXPECT_TRUE(active_browser->IsSame(main_browser()));
+    EXPECT_EQ(kCreateTabIndex, index);
+    EXPECT_STREQ(kCreateBrowserURL, url.ToString().c_str());
+    EXPECT_TRUE(foreground);
+    EXPECT_TRUE(client);
+
+    EXPECT_FALSE(got_on_before_browser_);
+    got_on_before_browser_.yes();
+
+    return false;
+  }
+
+  void OnAddMainBrowserResources() override {
+    AddResource(kCreateBrowserURL, GetCreatedBrowserHTML(), "text/html");
+
+    TabsTestHandler::OnAddMainBrowserResources();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (extension_browser() && main_browser()) {
+      if (isLoading) {
+        // Keep a reference to the newly created browser.
+        EXPECT_FALSE(created_browser_);
+        created_browser_ = browser;
+        return;
+      } else {
+        const std::string& url = browser->GetMainFrame()->GetURL();
+        if (url == kCreateBrowserURL) {
+          EXPECT_TRUE(browser->IsSame(created_browser_));
+          return;
+        }
+      }
+    }
+
+    TabsTestHandler::OnLoadingStateChange(browser, isLoading, canGoBack,
+                                          canGoForward);
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    const std::string& url = request->GetURL();
+    if (url == kCreateBrowserURL) {
+      EXPECT_TRUE(browser->IsSame(created_browser_));
+      EXPECT_FALSE(got_create_browser_url_request_);
+      got_create_browser_url_request_.yes();
+    }
+
+    return TabsTestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+ protected:
+  std::string GetTabsApiJS() const override {
+    std::stringstream ss;
+    ss << kCreateTabIndex;
+
+    return "chrome.tabs.create({url: \"" + std::string(kCreateBrowserURL) +
+           "\", index: " + ss.str() +
+           "}, function(tab) { window.testQuery({request:'" +
+           kTabCallbackMessage + ":' + JSON.stringify(tab)}); });";
+  }
+
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    if (message.find(kTabCallbackMessage) != std::string::npos) {
+      EXPECT_TRUE(browser->IsSame(extension_browser()));
+      EXPECT_FALSE(got_tab_callback_message_);
+      got_tab_callback_message_.yes();
+
+      // Verify the contents of the Tab object.
+      const std::string& json_str =
+          message.substr(strlen(kTabCallbackMessage) + 1);
+      CefRefPtr<CefValue> obj = CefParseJSON(json_str, JSON_PARSER_RFC);
+      EXPECT_TRUE(obj);
+      EXPECT_EQ(VTYPE_DICTIONARY, obj->GetType());
+      CefRefPtr<CefDictionaryValue> dict = obj->GetDictionary();
+
+      int index = dict->GetInt("index");
+      EXPECT_EQ(kCreateTabIndex, index);
+
+      int id = dict->GetInt("id");
+      int windowId = dict->GetInt("windowId");
+      EXPECT_EQ(created_browser_->GetIdentifier(), id);
+      EXPECT_EQ(created_browser_->GetIdentifier(), windowId);
+
+      const std::string& url = dict->GetString("url");
+      EXPECT_STREQ(kCreateBrowserURL, url.c_str());
+
+      TriggerDestroyTestIfReady();
+      return true;
+    } else if (message == kSuccessMessage) {
+      // Overriding default kSuccessMessage handling.
+      EXPECT_TRUE(browser->IsSame(created_browser_));
+      EXPECT_FALSE(got_success_message());
+      set_got_success_message();
+      TriggerDestroyTestIfReady();
+      return true;
+    }
+
+    return TabsTestHandler::OnMessage(browser, message);
+  }
+
+  void OnDestroyTest() override {
+    created_browser_ = nullptr;
+
+    EXPECT_TRUE(got_on_before_browser_);
+    EXPECT_TRUE(got_create_browser_url_request_);
+    EXPECT_TRUE(got_tab_callback_message_);
+
+    TabsTestHandler::OnDestroyTest();
+  }
+
+ private:
+  std::string GetCreatedBrowserHTML() {
+    return "<html><body onLoad=" + GetMessageJS(kSuccessMessage) +
+           ">Created</body></html>";
+  }
+
+  void TriggerDestroyTestIfReady() {
+    if (got_tab_callback_message_ && got_success_message())
+      TriggerDestroyTest();
+  }
+
+  CefRefPtr<CefBrowser> created_browser_;
+
+  TrackCallback got_on_before_browser_;
+  TrackCallback got_create_browser_url_request_;
+  TrackCallback got_tab_callback_message_;
+
+  IMPLEMENT_REFCOUNTING(CreateTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(CreateTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(Create, CreateTestHandler)
+
+namespace {
+
+//
+// chrome.tabs.executeScript tests.
+//
+
+// Base class for chrome.tabs.executeScript tests.
+class ExecuteScriptTestHandler : public TabsTestHandler {
+ public:
+  explicit ExecuteScriptTestHandler(RequestContextType request_context_type)
+      : TabsTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetMainBrowserSuccessHEAD() const override {
+    return "<script>function onTrigger() {" + GetMessageJS(kSuccessMessage) +
+           "}</script>";
+  }
+
+  // Returns the code that will be injected as a content script.
+  virtual std::string GetContentScriptJS() const {
+    // Execute the onTrigger() JS function.
+    return "var s = document.createElement('script');"
+           "s.textContent = 'onTrigger();';"
+           "document.head.appendChild(s);";
+  }
+
+  std::string GetTabsApiJS() const override {
+    return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" +
+           GetContentScriptJS() + "\"});";
+  }
+};
+
+// Test for chrome.tabs.executeScript with a null tabId value.
+class ExecuteScriptNullTabTestHandler : public ExecuteScriptTestHandler {
+ public:
+  explicit ExecuteScriptNullTabTestHandler(
+      RequestContextType request_context_type)
+      : ExecuteScriptTestHandler(request_context_type) {}
+
+ private:
+  IMPLEMENT_REFCOUNTING(ExecuteScriptNullTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ExecuteScriptNullTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(ExecuteScriptNullTab, ExecuteScriptNullTabTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.executeScript with an explicit tabId value.
+class ExecuteScriptExplicitTabTestHandler : public ExecuteScriptTestHandler {
+ public:
+  explicit ExecuteScriptExplicitTabTestHandler(
+      RequestContextType request_context_type)
+      : ExecuteScriptTestHandler(request_context_type) {
+    // Create the main browser first so we can retrieve the id.
+    set_create_main_browser_first(true);
+    // When a tabId is specified we should get a call to CanAccessBrowser
+    // instead of GetActiveBrowser.
+    set_expect_get_active_browser(false);
+  }
+
+ protected:
+  std::string GetTargetTabId() const override {
+    DCHECK(main_browser());
+    std::stringstream ss;
+    ss << main_browser()->GetIdentifier();
+    return ss.str();
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(ExecuteScriptExplicitTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ExecuteScriptExplicitTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(ExecuteScriptExplicitTab,
+                    ExecuteScriptExplicitTabTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.executeScript with a file argument loading a content
+// script.
+class ExecuteScriptFileTestHandler : public ExecuteScriptTestHandler {
+ public:
+  explicit ExecuteScriptFileTestHandler(RequestContextType request_context_type)
+      : ExecuteScriptTestHandler(request_context_type) {}
+
+  // CefExtensionHandler methods:
+  bool GetExtensionResource(
+      CefRefPtr<CefExtension> extension,
+      CefRefPtr<CefBrowser> browser,
+      const CefString& file,
+      CefRefPtr<CefGetExtensionResourceCallback> callback) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(this->extension());
+    EXPECT_TRUE(this->extension()->IsSame(extension));
+
+    if (file == "script.js") {
+      EXPECT_FALSE(got_get_extension_resource_);
+      got_get_extension_resource_.yes();
+
+      const std::string& content = GetContentScriptJS();
+      CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+          const_cast<char*>(content.data()), content.size());
+      callback->Continue(stream);
+      return true;
+    }
+
+    EXPECT_FALSE(true);  // Not reached.
+    return false;
+  }
+
+ protected:
+  std::string GetTabsApiJS() const override {
+    return "chrome.tabs.executeScript(" + GetTargetTabId() +
+           ", {file:\"script.js\"});";
+  }
+
+  void OnDestroyTest() override {
+    ExecuteScriptTestHandler::OnDestroyTest();
+    EXPECT_TRUE(got_get_extension_resource_);
+  }
+
+ private:
+  TrackCallback got_get_extension_resource_;
+
+  IMPLEMENT_REFCOUNTING(ExecuteScriptFileTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ExecuteScriptFileTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(ExecuteScriptFile, ExecuteScriptFileTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.executeScript with a callback argument.
+class ExecuteScriptCallbackTestHandler : public ExecuteScriptTestHandler {
+ public:
+  explicit ExecuteScriptCallbackTestHandler(
+      RequestContextType request_context_type)
+      : ExecuteScriptTestHandler(request_context_type) {}
+
+ protected:
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    if (message == "callback") {
+      EXPECT_FALSE(got_callback_message_);
+      got_callback_message_.yes();
+      EXPECT_TRUE(browser->IsSame(extension_browser()));
+      TriggerDestroyTest();
+      return true;
+    }
+    return ExecuteScriptTestHandler::OnMessage(browser, message);
+  }
+
+  std::string GetTabsApiJS() const override {
+    return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" +
+           GetContentScriptJS() + "\"}, function(results) {" +
+           GetMessageJS("callback") + "});";
+  }
+
+  void TriggerDestroyTest() override {
+    // Only destroy the test if we got both callbacks.
+    if (got_callback_message_ && got_success_message()) {
+      ExecuteScriptTestHandler::TriggerDestroyTest();
+    }
+  }
+
+  void OnDestroyTest() override {
+    ExecuteScriptTestHandler::OnDestroyTest();
+    EXPECT_TRUE(got_callback_message_);
+  }
+
+ private:
+  TrackCallback got_callback_message_;
+
+  IMPLEMENT_REFCOUNTING(ExecuteScriptCallbackTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ExecuteScriptCallbackTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_MINIMAL(ExecuteScriptCallback, ExecuteScriptCallbackTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.executeScript with execution occuring from a separate
+// resource script.
+class ExecuteScriptResourceTabTestHandler : public ExecuteScriptTestHandler {
+ public:
+  explicit ExecuteScriptResourceTabTestHandler(
+      RequestContextType request_context_type)
+      : ExecuteScriptTestHandler(request_context_type) {}
+
+  // CefResourceRequestHandler methods:
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    const std::string& url = request->GetURL();
+    if (url == resource_url_) {
+      EXPECT_TRUE(browser->IsSame(extension_browser()));
+      EXPECT_FALSE(got_resource_url_request_);
+      got_resource_url_request_.yes();
+    }
+
+    return ExecuteScriptTestHandler::GetResourceHandler(browser, frame,
+                                                        request);
+  }
+
+ protected:
+  void OnAddExtensionResources(const std::string& origin) override {
+    ExecuteScriptTestHandler::OnAddExtensionResources(origin);
+    resource_url_ = origin + "resource.js";
+    AddResource(resource_url_, GetExtensionJS(), "text/javascript");
+  }
+
+  std::string GetExtensionHTML() const override {
+    return "<html><head><script src=\"resource.js\"></script></head><body "
+           "onLoad=" +
+           GetMessageJS("extension_onload") + ">Extension</body></html>";
+  }
+
+  void OnDestroyTest() override {
+    ExecuteScriptTestHandler::OnDestroyTest();
+    EXPECT_TRUE(got_resource_url_request_);
+  }
+
+ private:
+  std::string resource_url_;
+  TrackCallback got_resource_url_request_;
+
+  IMPLEMENT_REFCOUNTING(ExecuteScriptResourceTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ExecuteScriptResourceTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_MINIMAL(ExecuteScriptResource,
+                        ExecuteScriptResourceTabTestHandler)
+
+//
+// chrome.tabs.insertCSS tests.
+//
+
+namespace {
+
+// Base class for chrome.tabs.insertCSS tests.
+class InsertCSSTestHandler : public TabsTestHandler {
+ public:
+  explicit InsertCSSTestHandler(RequestContextType request_context_type)
+      : TabsTestHandler(request_context_type) {}
+
+ protected:
+  std::string GetMainBrowserSuccessBODY() const override {
+    // We can't use a MutationObserver here because insertCSS does not modify
+    // the style attribute. We could detect the change by tracking modifications
+    // to document.styleSheets but that's complicated. Use a simple timer loop
+    // implementation calling getComputedStyle instead.
+    return "<script>var interval = setInterval(function() {"
+           "if (window.getComputedStyle(document.body,null)."
+           "getPropertyValue('background-color') == 'rgb(255, 0, 0)') {" +
+           GetMessageJS(kSuccessMessage) +
+           "clearInterval(interval);}}, 100);</script>";
+  }
+
+  // Returns the code that will be injected as a content script.
+  virtual std::string GetContentScriptCSS() const {
+    return "body{background-color:red}";
+  }
+
+  std::string GetTabsApiJS() const override {
+    return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" +
+           GetContentScriptCSS() + "\"});";
+  }
+};
+
+// Test for chrome.tabs.insertCSS with a null tabId value.
+class InsertCSSNullTabTestHandler : public InsertCSSTestHandler {
+ public:
+  explicit InsertCSSNullTabTestHandler(RequestContextType request_context_type)
+      : InsertCSSTestHandler(request_context_type) {}
+
+ private:
+  IMPLEMENT_REFCOUNTING(InsertCSSNullTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(InsertCSSNullTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(InsertCSSNullTab, InsertCSSNullTabTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.insertCSS with an explicit tabId value.
+class InsertCSSExplicitTabTestHandler : public InsertCSSTestHandler {
+ public:
+  explicit InsertCSSExplicitTabTestHandler(
+      RequestContextType request_context_type)
+      : InsertCSSTestHandler(request_context_type) {
+    // Create the main browser first so we can retrieve the id.
+    set_create_main_browser_first(true);
+    // When a tabId is specified we should get a call to CanAccessBrowser
+    // instead of GetActiveBrowser.
+    set_expect_get_active_browser(false);
+  }
+
+ protected:
+  std::string GetTargetTabId() const override {
+    DCHECK(main_browser());
+    std::stringstream ss;
+    ss << main_browser()->GetIdentifier();
+    return ss.str();
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(InsertCSSExplicitTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(InsertCSSExplicitTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(InsertCSSExplicitTab, InsertCSSExplicitTabTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.insertCSS with a file argument loading a content
+// script.
+class InsertCSSFileTestHandler : public InsertCSSTestHandler {
+ public:
+  explicit InsertCSSFileTestHandler(RequestContextType request_context_type)
+      : InsertCSSTestHandler(request_context_type) {}
+
+  // CefExtensionHandler methods:
+  bool GetExtensionResource(
+      CefRefPtr<CefExtension> extension,
+      CefRefPtr<CefBrowser> browser,
+      const CefString& file,
+      CefRefPtr<CefGetExtensionResourceCallback> callback) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(this->extension());
+    EXPECT_TRUE(this->extension()->IsSame(extension));
+
+    if (file == "script.css") {
+      EXPECT_FALSE(got_get_extension_resource_);
+      got_get_extension_resource_.yes();
+
+      const std::string& content = GetContentScriptCSS();
+      CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+          const_cast<char*>(content.data()), content.size());
+      callback->Continue(stream);
+      return true;
+    }
+
+    EXPECT_FALSE(true);  // Not reached.
+    return false;
+  }
+
+ protected:
+  std::string GetTabsApiJS() const override {
+    return "chrome.tabs.insertCSS(" + GetTargetTabId() +
+           ", {file:\"script.css\"});";
+  }
+
+  void OnDestroyTest() override {
+    InsertCSSTestHandler::OnDestroyTest();
+    EXPECT_TRUE(got_get_extension_resource_);
+  }
+
+ private:
+  TrackCallback got_get_extension_resource_;
+
+  IMPLEMENT_REFCOUNTING(InsertCSSFileTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(InsertCSSFileTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(InsertCSSFile, InsertCSSFileTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.insertCSS with a callback argument.
+class InsertCSSCallbackTestHandler : public InsertCSSTestHandler {
+ public:
+  explicit InsertCSSCallbackTestHandler(RequestContextType request_context_type)
+      : InsertCSSTestHandler(request_context_type) {}
+
+ protected:
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    if (message == "callback") {
+      EXPECT_FALSE(got_callback_message_);
+      got_callback_message_.yes();
+      EXPECT_TRUE(browser->IsSame(extension_browser()));
+      TriggerDestroyTest();
+      return true;
+    }
+    return InsertCSSTestHandler::OnMessage(browser, message);
+  }
+
+  std::string GetTabsApiJS() const override {
+    return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" +
+           GetContentScriptCSS() + "\"}, function(results) {" +
+           GetMessageJS("callback") + "});";
+  }
+
+  void TriggerDestroyTest() override {
+    // Only destroy the test if we got both callbacks.
+    if (got_callback_message_ && got_success_message()) {
+      InsertCSSTestHandler::TriggerDestroyTest();
+    }
+  }
+
+  void OnDestroyTest() override {
+    InsertCSSTestHandler::OnDestroyTest();
+    EXPECT_TRUE(got_callback_message_);
+  }
+
+ private:
+  TrackCallback got_callback_message_;
+
+  IMPLEMENT_REFCOUNTING(InsertCSSCallbackTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(InsertCSSCallbackTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_MINIMAL(InsertCSSCallback, InsertCSSCallbackTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.insertCSS with execution occuring from a separate
+// resource script.
+class InsertCSSResourceTabTestHandler : public InsertCSSTestHandler {
+ public:
+  explicit InsertCSSResourceTabTestHandler(
+      RequestContextType request_context_type)
+      : InsertCSSTestHandler(request_context_type) {}
+
+  // CefResourceRequestHandler methods:
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    const std::string& url = request->GetURL();
+    if (url == resource_url_) {
+      EXPECT_TRUE(browser->IsSame(extension_browser()));
+      EXPECT_FALSE(got_resource_url_request_);
+      got_resource_url_request_.yes();
+    }
+
+    return InsertCSSTestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+ protected:
+  void OnAddExtensionResources(const std::string& origin) override {
+    InsertCSSTestHandler::OnAddExtensionResources(origin);
+    resource_url_ = origin + "resource.js";
+    AddResource(resource_url_, GetExtensionJS(), "text/javascript");
+  }
+
+  std::string GetExtensionHTML() const override {
+    return "<html><head><script src=\"resource.js\"></script></head><body "
+           "onLoad=" +
+           GetMessageJS("extension_onload") + ">Extension</body></html>";
+  }
+
+  void OnDestroyTest() override {
+    InsertCSSTestHandler::OnDestroyTest();
+    EXPECT_TRUE(got_resource_url_request_);
+  }
+
+ private:
+  std::string resource_url_;
+  TrackCallback got_resource_url_request_;
+
+  IMPLEMENT_REFCOUNTING(InsertCSSResourceTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(InsertCSSResourceTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_MINIMAL(InsertCSSResource, InsertCSSResourceTabTestHandler)
+
+//
+// chrome.tabs.setZoom/getZoom tests.
+//
+
+namespace {
+
+// Base class for chrome.tabs.setZoom/getZoom tests.
+class ZoomTestHandler : public TabsTestHandler {
+ public:
+  explicit ZoomTestHandler(RequestContextType request_context_type)
+      : TabsTestHandler(request_context_type) {
+    // We call API functions three times in this handler.
+    set_expected_api_call_count(3);
+  }
+
+ protected:
+  std::string GetMainBrowserSuccessHEAD() const override {
+    return "<script>var orig_width = window.innerWidth;</script>";
+  }
+
+  std::string GetMainBrowserSuccessBODY() const override {
+    // We can't directly detect zoom changes, so instead we look for changes
+    // in window.innerWidth.
+    return "<script>var interval = setInterval(function() {"
+           "if (window.innerWidth != orig_width) {" +
+           GetMessageJS(kSuccessMessage) +
+           "clearInterval(interval);}}, 100);</script>";
+  }
+
+  std::string GetTabsApiJS() const override {
+    // Results in a change to window.innerWidth.
+    return "chrome.tabs.setZoom(" + GetTargetTabId() + ", 2.0);";
+  }
+
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    if (message == "restored") {
+      EXPECT_TRUE(browser->IsSame(extension_browser()));
+      EXPECT_FALSE(got_restored_message_);
+      got_restored_message_.yes();
+      // Destroy the test for real.
+      TabsTestHandler::TriggerDestroyTest();
+      return true;
+    }
+    return TabsTestHandler::OnMessage(browser, message);
+  }
+
+  void TriggerDestroyTest() override {
+    // Before destroying the test we need to restore the zoom factor so that
+    // it doesn't persist in the RequestContext. This also tests the callback
+    // argument and the getZoom function so there's no need to do that
+    // separately.
+    extension_browser()->GetMainFrame()->ExecuteJavaScript(
+        "chrome.tabs.setZoom(" + GetTargetTabId() + ", 1.0, function() {" +
+            "chrome.tabs.getZoom(" + GetTargetTabId() +
+            ", function(zoomFactor) { if (zoomFactor == 1.0) {" +
+            GetMessageJS("restored") + "}})});",
+        extension_url(), 0);
+  }
+
+  void OnDestroyTest() override {
+    TabsTestHandler::OnDestroyTest();
+    EXPECT_TRUE(got_restored_message_);
+  }
+
+ private:
+  TrackCallback got_restored_message_;
+};
+
+// Test for chrome.tabs.setZoom/getZoom with a null tabId value.
+class ZoomNullTabTestHandler : public ZoomTestHandler {
+ public:
+  explicit ZoomNullTabTestHandler(RequestContextType request_context_type)
+      : ZoomTestHandler(request_context_type) {}
+
+ private:
+  IMPLEMENT_REFCOUNTING(ZoomNullTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ZoomNullTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(ZoomNullTab, ZoomNullTabTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.setZoom/getZoom with an explicit tabId value.
+class ZoomExplicitTabTestHandler : public ZoomTestHandler {
+ public:
+  explicit ZoomExplicitTabTestHandler(RequestContextType request_context_type)
+      : ZoomTestHandler(request_context_type) {
+    // Create the main browser first so we can retrieve the id.
+    set_create_main_browser_first(true);
+    // When a tabId is specified we should get a call to CanAccessBrowser
+    // instead of GetActiveBrowser.
+    set_expect_get_active_browser(false);
+  }
+
+ protected:
+  std::string GetTargetTabId() const override {
+    DCHECK(main_browser());
+    std::stringstream ss;
+    ss << main_browser()->GetIdentifier();
+    return ss.str();
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(ZoomExplicitTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ZoomExplicitTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(ZoomExplicitTab, ZoomExplicitTabTestHandler)
+
+//
+// chrome.tabs.setZoomSettings/getZoomSettings tests.
+//
+
+namespace {
+
+// Base class for chrome.tabs.setZoomSettings/getZoomSettings tests.
+class ZoomSettingsTestHandler : public TabsTestHandler {
+ public:
+  explicit ZoomSettingsTestHandler(RequestContextType request_context_type)
+      : TabsTestHandler(request_context_type) {
+    // We call API functions two times in this handler.
+    set_expected_api_call_count(2);
+    // Success message will be delivered in the extension browser because we
+    // don't know how to detect zoom settings changes in the main browser.
+    set_expect_success_in_main_browser(false);
+  }
+
+ protected:
+  std::string GetTabsApiJS() const override {
+    // Set and restore the zoom settings. This also tests the callback argument
+    // and the getZoomSettings function so there's no need to do that
+    // separately. This is safe because zoom settings are not persisted in the
+    // RequestContext across navigations.
+    return "chrome.tabs.setZoomSettings(" + GetTargetTabId() +
+           ", {mode: 'manual', scope: 'per-tab'}, function() {" +
+           "chrome.tabs.getZoomSettings(" + GetTargetTabId() +
+           ", function(zoomSettings) { if (zoomSettings.mode == 'manual' "
+           "&& zoomSettings.scope == 'per-tab') {" +
+           GetMessageJS(kSuccessMessage) + "}})});";
+  }
+};
+
+// Test for chrome.tabs.setZoomSettings/getZoomSettings with a null tabId value.
+class ZoomSettingsNullTabTestHandler : public ZoomSettingsTestHandler {
+ public:
+  explicit ZoomSettingsNullTabTestHandler(
+      RequestContextType request_context_type)
+      : ZoomSettingsTestHandler(request_context_type) {}
+
+ private:
+  IMPLEMENT_REFCOUNTING(ZoomSettingsNullTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ZoomSettingsNullTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(ZoomSettingsNullTab, ZoomSettingsNullTabTestHandler)
+
+namespace {
+
+// Test for chrome.tabs.setZoomSettings/getZoomSettings with an explicit tabId
+// value.
+class ZoomSettingsExplicitTabTestHandler : public ZoomSettingsTestHandler {
+ public:
+  explicit ZoomSettingsExplicitTabTestHandler(
+      RequestContextType request_context_type)
+      : ZoomSettingsTestHandler(request_context_type) {
+    // Create the main browser first so we can retrieve the id.
+    set_create_main_browser_first(true);
+    // When a tabId is specified we should get a call to CanAccessBrowser
+    // instead of GetActiveBrowser.
+    set_expect_get_active_browser(false);
+  }
+
+ protected:
+  std::string GetTargetTabId() const override {
+    DCHECK(main_browser());
+    std::stringstream ss;
+    ss << main_browser()->GetIdentifier();
+    return ss.str();
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(ZoomSettingsExplicitTabTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ZoomSettingsExplicitTabTestHandler);
+};
+
+}  // namespace
+
+TABS_TEST_GROUP_ALL(ZoomSettingsExplicitTab, ZoomSettingsExplicitTabTestHandler)
diff --git a/src/tests/ceftests/extensions/extension_test_handler.cc b/src/tests/ceftests/extensions/extension_test_handler.cc
new file mode 100644
index 0000000..b47c230
--- /dev/null
+++ b/src/tests/ceftests/extensions/extension_test_handler.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/extensions/extension_test_handler.h"
+
+#include "include/cef_request_context_handler.h"
+#include "tests/ceftests/test_suite.h"
+#include "tests/ceftests/test_util.h"
+
+ExtensionTestHandler::ExtensionTestHandler(
+    RequestContextType request_context_type)
+    : request_context_type_(request_context_type), create_main_browser_(true) {
+  // Verify supported flag combinations.
+  if (request_context_on_disk()) {
+    EXPECT_TRUE(request_context_is_custom());
+  }
+  if (request_context_load_with_handler()) {
+    EXPECT_FALSE(request_context_load_without_handler());
+  }
+  if (request_context_load_without_handler()) {
+    EXPECT_TRUE(request_context_with_handler());
+    EXPECT_FALSE(request_context_load_with_handler());
+  }
+}
+
+ExtensionTestHandler::~ExtensionTestHandler() {
+  if (!request_context_temp_dir_.IsEmpty()) {
+    // Delete temporary directories on shutdown.
+    CefTestSuite::GetInstance()->RegisterTempDirectory(
+        request_context_temp_dir_.Take());
+  }
+}
+
+void ExtensionTestHandler::RunTest() {
+  if (create_main_browser_)
+    OnAddMainBrowserResources();
+
+  CefRefPtr<CefRequestContextHandler> rc_handler;
+  if (request_context_with_handler()) {
+    class Handler : public CefRequestContextHandler {
+     public:
+      explicit Handler(ExtensionTestHandler* test_handler)
+          : test_handler_(test_handler) {}
+
+      void OnRequestContextInitialized(
+          CefRefPtr<CefRequestContext> request_context) override {
+        if (test_handler_->create_main_browser()) {
+          // Load extensions after the RequestContext has been initialized by
+          // creation of the main browser.
+          test_handler_->OnLoadExtensions();
+        }
+      }
+
+     private:
+      ExtensionTestHandler* test_handler_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+    rc_handler = new Handler(this);
+  }
+
+  if (request_context_is_custom()) {
+    CefRequestContextSettings settings;
+
+    if (request_context_on_disk()) {
+      // Create a new temporary directory.
+      EXPECT_TRUE(request_context_temp_dir_.CreateUniqueTempDir());
+      CefString(&settings.cache_path) = request_context_temp_dir_.GetPath();
+    }
+
+    request_context_ = CefRequestContext::CreateContext(settings, rc_handler);
+  } else {
+    request_context_ = CefRequestContext::CreateContext(
+        CefRequestContext::GetGlobalContext(), rc_handler);
+  }
+
+  if (request_context_load_with_handler()) {
+    class Handler : public CefRequestContextHandler {
+     public:
+      Handler() {}
+
+     private:
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+    loader_request_context_ =
+        CefRequestContext::CreateContext(request_context_, new Handler());
+  } else if (request_context_load_without_handler()) {
+    loader_request_context_ =
+        CefRequestContext::CreateContext(request_context_, nullptr);
+  } else {
+    loader_request_context_ = request_context_;
+  }
+
+  if (create_main_browser_) {
+    OnCreateMainBrowser();
+  } else {
+    // Creation of the extension browser will trigger initialization of the
+    // RequestContext, so just load the extensions now.
+    OnLoadExtensions();
+  }
+
+  // Time out the test after a reasonable period of time.
+  SetTestTimeout();
+}
+
+void ExtensionTestHandler::DestroyTest() {
+  OnDestroyTest();
+  ReleaseRequestContexts();
+  RoutingTestHandler::DestroyTest();
+}
+
+void ExtensionTestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  RoutingTestHandler::OnAfterCreated(browser);
+
+  if (create_main_browser() && !request_context_with_handler() &&
+      GetBrowserId() == browser->GetIdentifier()) {
+    // When the RequestContext doesn't have a handler we won't get a
+    // notification for RequestContext initialization. Instead use main browser
+    // creation to indicate that the RequestContext has been initialized.
+    OnLoadExtensions();
+  }
+}
+
+void ExtensionTestHandler::OnExtensionLoadFailed(cef_errorcode_t result) {
+  EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+  EXPECT_TRUE(false);  // Not reached.
+}
+
+// CefMessageRouterBrowserSide::Handler methods:
+bool ExtensionTestHandler::OnQuery(CefRefPtr<CefBrowser> browser,
+                                   CefRefPtr<CefFrame> frame,
+                                   int64 query_id,
+                                   const CefString& request,
+                                   bool persistent,
+                                   CefRefPtr<Callback> callback) {
+  if (OnMessage(browser, request))
+    return true;
+
+  EXPECT_FALSE(true) << "Unexpected message: " << request.ToString();
+  return false;
+}
+
+// static
+CefRefPtr<CefDictionaryValue> ExtensionTestHandler::CreateDefaultManifest(
+    const std::vector<std::string>& api_permissions) {
+  CefRefPtr<CefDictionaryValue> manifest = CefDictionaryValue::Create();
+  manifest->SetString("name", "An extension");
+  manifest->SetString("description", "An extension description");
+  manifest->SetString("version", "1.0");
+  manifest->SetInt("manifest_version", 2);
+
+  CefRefPtr<CefListValue> permissions = CefListValue::Create();
+  permissions->SetSize(api_permissions.size() + 2);
+  size_t idx = 0;
+  for (; idx < api_permissions.size(); ++idx)
+    permissions->SetString(idx, api_permissions[idx]);
+
+  // Allow access to all http/https origins.
+  permissions->SetString(idx++, "http://*/*");
+  permissions->SetString(idx++, "https://*/*");
+
+  manifest->SetList("permissions", permissions);
+
+  return manifest;
+}
+
+// static
+std::string ExtensionTestHandler::GetMessageJS(const std::string& message) {
+  EXPECT_TRUE(!message.empty());
+  return "window.testQuery({request:'" + message + "'});";
+}
+
+// static
+void ExtensionTestHandler::VerifyExtensionInContext(
+    CefRefPtr<CefExtension> extension,
+    CefRefPtr<CefRequestContext> context,
+    bool has_access,
+    bool is_loader) {
+  const CefString& extension_id = extension->GetIdentifier();
+  EXPECT_FALSE(extension_id.empty());
+
+  if (has_access) {
+    EXPECT_TRUE(context->DidLoadExtension(extension_id));
+    EXPECT_TRUE(context->HasExtension(extension_id));
+  } else {
+    EXPECT_FALSE(context->DidLoadExtension(extension_id));
+    EXPECT_FALSE(context->HasExtension(extension_id));
+  }
+
+  CefRefPtr<CefExtension> extension2 = context->GetExtension(extension_id);
+  if (has_access) {
+    EXPECT_TRUE(extension2);
+    EXPECT_TRUE(extension->IsSame(extension2));
+    TestDictionaryEqual(extension->GetManifest(), extension2->GetManifest());
+  } else {
+    EXPECT_FALSE(extension2);
+  }
+
+  std::vector<CefString> extension_ids;
+  EXPECT_TRUE(context->GetExtensions(extension_ids));
+
+  // Should be our test extension and possibly the builtin PDF extension if it
+  // has finished loading (our extension may load first if the call to
+  // LoadExtension initializes the request context).
+  bool has_extension = false;
+  for (size_t i = 0; i < extension_ids.size(); ++i) {
+    if (extension_ids[i] == extension_id) {
+      has_extension = true;
+      break;
+    }
+  }
+  if (has_access) {
+    EXPECT_TRUE(has_extension);
+  } else {
+    EXPECT_FALSE(has_extension);
+  }
+}
+
+void ExtensionTestHandler::LoadExtension(
+    const std::string& extension_path,
+    CefRefPtr<CefDictionaryValue> manifest) {
+  EXPECT_TRUE(!extension_path.empty());
+  loader_request_context_->LoadExtension(extension_path, manifest, this);
+}
+
+void ExtensionTestHandler::UnloadExtension(CefRefPtr<CefExtension> extension) {
+  EXPECT_TRUE(extension);
+  extension->Unload();
+  EXPECT_FALSE(extension->IsLoaded());
+  EXPECT_FALSE(extension->GetLoaderContext());
+}
+
+void ExtensionTestHandler::ReleaseRequestContexts() {
+  request_context_ = nullptr;
+  loader_request_context_ = nullptr;
+}
diff --git a/src/tests/ceftests/extensions/extension_test_handler.h b/src/tests/ceftests/extensions/extension_test_handler.h
new file mode 100644
index 0000000..a2a87e3
--- /dev/null
+++ b/src/tests/ceftests/extensions/extension_test_handler.h
@@ -0,0 +1,238 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_
+#define CEF_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_extension_handler.h"
+#include "include/cef_values.h"
+#include "include/wrapper/cef_scoped_temp_dir.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+class ExtensionTestHandler : public RoutingTestHandler,
+                             public CefExtensionHandler {
+ public:
+  // All tests must be able to run with all RequestContext combinations. See the
+  // EXTENSION_TEST_GROUP_* macros below.
+  enum RequestContextType {
+    // If set create a custom context. Otherwise, use the global context.
+    RC_TYPE_FLAG_CUSTOM = 1 << 0,
+
+    // If set store data on disk. Otherwise, store data in memory.
+    // Requires RC_TYPE_FLAG_CUSTOM.
+    RC_TYPE_FLAG_ON_DISK = 1 << 1,
+
+    // If set use a handler. Otherwise, don't.
+    RC_TYPE_FLAG_WITH_HANDLER = 1 << 2,
+
+    // If set load extensions with a different context that shares the same
+    // storage but specifies a different handler.
+    // Excludes RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER.
+    RC_TYPE_FLAG_LOAD_WITH_HANDLER = 1 << 3,
+
+    // If set load extensions with a different context that shares the same
+    // storage but doesn't specify a handler.
+    // Requires RC_TYPE_FLAG_WITH_HANDLER.
+    // Excludes RC_TYPE_FLAG_LOAD_WITH_HANDLER.
+    RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER = 1 << 4,
+  };
+
+  explicit ExtensionTestHandler(RequestContextType request_context_type);
+  virtual ~ExtensionTestHandler();
+
+  // TestHandler methods:
+  void RunTest() override;
+  void DestroyTest() override;
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
+
+  // CefExtensionHandler methods:
+  void OnExtensionLoadFailed(cef_errorcode_t result) override;
+
+  // CefMessageRouterBrowserSide::Handler methods:
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override;
+
+  CefRefPtr<CefRequestContext> request_context() const {
+    return request_context_;
+  }
+  CefRefPtr<CefRequestContext> loader_request_context() const {
+    return loader_request_context_;
+  }
+
+  bool request_context_is_custom() const {
+    return !!(request_context_type_ & RC_TYPE_FLAG_CUSTOM);
+  }
+  bool request_context_on_disk() const {
+    return !!(request_context_type_ & RC_TYPE_FLAG_ON_DISK);
+  }
+  bool request_context_with_handler() const {
+    return !!(request_context_type_ & RC_TYPE_FLAG_WITH_HANDLER);
+  }
+  bool request_context_load_with_handler() const {
+    return !!(request_context_type_ & RC_TYPE_FLAG_LOAD_WITH_HANDLER);
+  }
+  bool request_context_load_without_handler() const {
+    return !!(request_context_type_ & RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER);
+  }
+  bool request_context_same_loader() const {
+    return !(request_context_load_with_handler() ||
+             request_context_load_without_handler());
+  }
+
+ protected:
+  // Returns the default extension manifest.
+  typedef std::vector<std::string> ApiPermissionsList;
+  static CefRefPtr<CefDictionaryValue> CreateDefaultManifest(
+      const ApiPermissionsList& api_permissions);
+
+  // Returns the JS code that, when executed, will deliver |message| to the
+  // OnMessage callback.
+  static std::string GetMessageJS(const std::string& message);
+
+  // Run checks on the state of |extension| in |context|. If |has_access| is
+  // true then |context| is expected to have access to |extension|. If
+  // |is_loader| is true then |context| is expected to have loaded |extension|.
+  static void VerifyExtensionInContext(CefRefPtr<CefExtension> extension,
+                                       CefRefPtr<CefRequestContext> context,
+                                       bool has_access,
+                                       bool is_loader);
+
+  // Helper for loading/unloading an extension.
+  void LoadExtension(const std::string& extension_path,
+                     CefRefPtr<CefDictionaryValue> manifest);
+  void UnloadExtension(CefRefPtr<CefExtension> extension);
+
+  // Release request contexts. This is normally called from DestroyTest().
+  void ReleaseRequestContexts();
+
+  void set_create_main_browser(bool val) { create_main_browser_ = val; }
+  bool create_main_browser() const { return create_main_browser_; }
+
+  // Called when its time to add resources for the main browser if
+  // |create_main_browser_| is true.
+  virtual void OnAddMainBrowserResources() {}
+  // Called when its time to create the main browser if
+  // |create_main_browser_| is true.
+  virtual void OnCreateMainBrowser() {}
+
+  // Called when its time to load extensions.
+  virtual void OnLoadExtensions() = 0;
+
+  // Called when |browser| receives |message|. Return true if the message is
+  // handled. The JS code that sends messages is created by GetMessageJS().
+  virtual bool OnMessage(CefRefPtr<CefBrowser> browser,
+                         const std::string& message) = 0;
+
+  // Called to perform verification on test destruction.
+  virtual void OnDestroyTest() = 0;
+
+ private:
+  const RequestContextType request_context_type_;
+  CefScopedTempDir request_context_temp_dir_;
+
+  // Context used when creating browsers.
+  CefRefPtr<CefRequestContext> request_context_;
+
+  // Context used when loading extensions.
+  CefRefPtr<CefRequestContext> loader_request_context_;
+
+  // If true expect creation of a main browser. Default is true.
+  bool create_main_browser_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionTestHandler);
+};
+
+// Helper for implementing an extension test.
+#define EXTENSION_TEST(name, test_class, rc_type)                        \
+  TEST(ExtensionTest, name) {                                            \
+    CefRefPtr<test_class> handler = new test_class(                      \
+        static_cast<ExtensionTestHandler::RequestContextType>(rc_type)); \
+    handler->ExecuteTest();                                              \
+    ReleaseAndWaitForDestructor(handler);                                \
+  }
+
+// Helper for implementing extension tests that include all RequestContext
+// combinations. When two or more extension tests significantly overlap in
+// tested functionality the first test should use the ALL macro and the others
+// should use the MINIMAL macro.
+#define EXTENSION_TEST_GROUP_ALL(name, test_class)                             \
+  EXTENSION_TEST(name##RCGlobal, test_class, 0)                                \
+  EXTENSION_TEST(name##RCGlobalLoadWithHandler, test_class,                    \
+                 ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER)         \
+  EXTENSION_TEST(name##RCGlobalWithHandler, test_class,                        \
+                 ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER)              \
+  EXTENSION_TEST(name##RCGlobalWithHandlerLoadWithHandler, test_class,         \
+                 ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER |             \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER)     \
+  EXTENSION_TEST(name##RCGlobalWithHandlerLoadWithoutHandler, test_class,      \
+                 ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER |             \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER)  \
+  EXTENSION_TEST(name##RCCustomInMemory, test_class,                           \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM)                    \
+  EXTENSION_TEST(name##RCCustomInMemoryLoadWithHandler, test_class,            \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER)     \
+  EXTENSION_TEST(name##RCCustomInMemoryWithHandler, test_class,                \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER)          \
+  EXTENSION_TEST(name##RCCustomInMemoryWithHandlerLoadWithHandler, test_class, \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER |         \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER)     \
+  EXTENSION_TEST(name##RCCustomInMemoryWithHandlerLoadWithoutHandler,          \
+                 test_class,                                                   \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER |         \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER)  \
+  EXTENSION_TEST(name##RCCustomOnDisk, test_class,                             \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK)               \
+  EXTENSION_TEST(name##RCCustomOnDiskLoadWithHandler, test_class,              \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK |              \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER)     \
+  EXTENSION_TEST(name##RCCustomOnDiskWithHandler, test_class,                  \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK |              \
+                     ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER)          \
+  EXTENSION_TEST(name##RCCustomOnDiskWithHandlerLoadWithHandler, test_class,   \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK |              \
+                     ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER |         \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER)     \
+  EXTENSION_TEST(name##RCCustomOnDiskWithHandlerLoadWithoutHandler,            \
+                 test_class,                                                   \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |                   \
+                     ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK |              \
+                     ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER |         \
+                     ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER)
+
+#define EXTENSION_TEST_GROUP_MINIMAL_GLOBAL(name, test_class) \
+  EXTENSION_TEST(name##RCGlobal, test_class, 0)               \
+  EXTENSION_TEST(name##RCGlobalWithHandler, test_class,       \
+                 ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER)
+
+#define EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(name, test_class)   \
+  EXTENSION_TEST(name##RCCustomInMemory, test_class,            \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM)     \
+  EXTENSION_TEST(name##RCCustomInMemoryWithHandler, test_class, \
+                 ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM |    \
+                     ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER)
+
+// Helper for implementing extension tests that include a minimal set of
+// RequestContext combinations. This mostly just verifies that the test runs
+// and doesn't leak state information in the context.
+#define EXTENSION_TEST_GROUP_MINIMAL(name, test_class)  \
+  EXTENSION_TEST_GROUP_MINIMAL_GLOBAL(name, test_class) \
+  EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(name, test_class)
+
+#endif  // CEF_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_
diff --git a/src/tests/ceftests/extensions/view_unittest.cc b/src/tests/ceftests/extensions/view_unittest.cc
new file mode 100644
index 0000000..444ed1e
--- /dev/null
+++ b/src/tests/ceftests/extensions/view_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/extensions/extension_test_handler.h"
+
+#include "tests/ceftests/test_util.h"
+#include "tests/shared/browser/extension_util.h"
+
+namespace {
+
+const char kExtensionPath[] = "view-extension";
+
+// Test extension load/unload.
+class ViewLoadUnloadTestHandler : public ExtensionTestHandler {
+ public:
+  explicit ViewLoadUnloadTestHandler(RequestContextType request_context_type)
+      : ExtensionTestHandler(request_context_type) {
+    // Only creating the extension browser.
+    set_create_main_browser(false);
+  }
+
+  // CefExtensionHandler methods:
+  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension->IsLoaded());
+    EXPECT_TRUE(extension->GetLoaderContext());
+    EXPECT_TRUE(
+        loader_request_context()->IsSame(extension->GetLoaderContext()));
+    VerifyExtension(extension);
+
+    EXPECT_FALSE(got_loaded_);
+    got_loaded_.yes();
+
+    EXPECT_FALSE(extension_);
+    extension_ = extension;
+
+    CreateBrowserForExtension();
+  }
+
+  void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(extension);
+    EXPECT_FALSE(extension->IsLoaded());
+    EXPECT_FALSE(extension->GetLoaderContext());
+
+    EXPECT_FALSE(got_unloaded_);
+    got_unloaded_.yes();
+
+    EXPECT_TRUE(extension_);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    // The extension should no longer be registered with the context.
+    if (loader_request_context())
+      VerifyExtensionInContext(extension, loader_request_context(), false,
+                               true);
+    if (request_context() && !request_context_same_loader())
+      VerifyExtensionInContext(extension, request_context(), false, false);
+
+    extension_ = nullptr;
+
+    // Execute asynchronously so call stacks have a chance to unwind.
+    // Will close the browser windows.
+    CefPostTask(TID_UI,
+                base::Bind(&ViewLoadUnloadTestHandler::DestroyTest, this));
+  }
+
+  // CefLoadHandler methods:
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    EXPECT_FALSE(browser->GetHost()->IsBackgroundHost());
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    if (isLoading) {
+      EXPECT_FALSE(extension_browser_);
+      extension_browser_ = browser;
+    } else {
+      EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      EXPECT_STREQ(extension_url_.c_str(), url.c_str());
+
+      EXPECT_FALSE(got_load_done_);
+      got_load_done_.yes();
+
+      TriggerDestroyTestIfDone();
+    }
+  }
+
+  // CefResourceRequestHandler methods:
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_FALSE(browser->GetHost()->IsBackgroundHost());
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(extension_url_.c_str(), url.c_str());
+
+    EXPECT_FALSE(got_url_request_);
+    got_url_request_.yes();
+
+    // Handle the resource request.
+    return RoutingTestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+ protected:
+  void OnLoadExtensions() override {
+    LoadExtension(kExtensionPath, CreateManifest());
+  }
+
+  bool OnMessage(CefRefPtr<CefBrowser> browser,
+                 const std::string& message) override {
+    EXPECT_FALSE(browser->GetHost()->IsBackgroundHost());
+    EXPECT_STREQ("extension_onload", message.c_str());
+
+    CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
+    EXPECT_TRUE(extension);
+    EXPECT_TRUE(extension_->IsSame(extension));
+
+    EXPECT_TRUE(browser->IsSame(extension_browser_));
+
+    EXPECT_FALSE(got_body_onload_);
+    got_body_onload_.yes();
+
+    TriggerDestroyTestIfDone();
+    return true;
+  }
+
+  void OnDestroyTest() override {
+    extension_browser_ = nullptr;
+
+    EXPECT_TRUE(got_loaded_);
+    EXPECT_TRUE(got_url_request_);
+    EXPECT_TRUE(got_body_onload_);
+    EXPECT_TRUE(got_load_done_);
+    EXPECT_TRUE(got_unloaded_);
+  }
+
+  // Create the default manifest.
+  CefRefPtr<CefDictionaryValue> CreateManifest() const {
+    return CreateDefaultManifest(ApiPermissionsList());
+  }
+
+  // Verify |extension| contents.
+  void VerifyExtension(CefRefPtr<CefExtension> extension) const {
+    EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
+                 client::extension_util::GetInternalExtensionResourcePath(
+                     extension->GetPath())
+                     .c_str());
+
+    CefRefPtr<CefDictionaryValue> expected_manifest = CreateManifest();
+    TestDictionaryEqual(expected_manifest, extension->GetManifest());
+
+    VerifyExtensionInContext(extension, loader_request_context(), true, true);
+    if (!request_context_same_loader())
+      VerifyExtensionInContext(extension, request_context(), true, false);
+  }
+
+  void CreateBrowserForExtension() {
+    const std::string& identifier = extension_->GetIdentifier();
+    EXPECT_FALSE(identifier.empty());
+    const std::string& origin =
+        client::extension_util::GetExtensionOrigin(identifier);
+    EXPECT_FALSE(origin.empty());
+
+    // Add extension resources.
+    extension_url_ = origin + "extension.html";
+    AddResource(extension_url_,
+                "<html><body onLoad=" + GetMessageJS("extension_onload") +
+                    ">Extension</body></html>",
+                "text/html");
+
+    // Create a browser to host the extension.
+    CreateBrowser(extension_url_, request_context());
+  }
+
+  void TriggerDestroyTestIfDone() {
+    if (got_body_onload_ && got_load_done_) {
+      TriggerDestroyTest();
+    }
+  }
+
+  virtual void TriggerDestroyTest() {
+    // Execute asynchronously so call stacks have a chance to unwind.
+    CefPostTask(TID_UI, base::Bind(&ViewLoadUnloadTestHandler::UnloadExtension,
+                                   this, extension_));
+  }
+
+  CefRefPtr<CefExtension> extension_;
+  std::string extension_url_;
+  CefRefPtr<CefBrowser> extension_browser_;
+
+  TrackCallback got_loaded_;
+  TrackCallback got_url_request_;
+  TrackCallback got_body_onload_;
+  TrackCallback got_load_done_;
+  TrackCallback got_unloaded_;
+
+  IMPLEMENT_REFCOUNTING(ViewLoadUnloadTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(ViewLoadUnloadTestHandler);
+};
+
+}  // namespace
+
+EXTENSION_TEST_GROUP_ALL(ViewLoadUnload, ViewLoadUnloadTestHandler)
+
+namespace {
+
+// Same as above but without the unload. Only do this with a custom context to
+// avoid poluting the global context.
+class ViewLoadNoUnloadTestHandler : public ViewLoadUnloadTestHandler {
+ public:
+  explicit ViewLoadNoUnloadTestHandler(RequestContextType request_context_type)
+      : ViewLoadUnloadTestHandler(request_context_type) {}
+
+ protected:
+  void TriggerDestroyTest() override {
+    // Release everything that references the request context. This should
+    // trigger unload of the extension.
+    CloseBrowser(extension_browser_, false);
+    extension_browser_ = nullptr;
+    ReleaseRequestContexts();
+  }
+};
+
+}  // namespace
+
+EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(ViewLoadNoUnload,
+                                    ViewLoadNoUnloadTestHandler)
diff --git a/src/tests/ceftests/file_util_unittest.cc b/src/tests/ceftests/file_util_unittest.cc
new file mode 100644
index 0000000..9a54ba4
--- /dev/null
+++ b/src/tests/ceftests/file_util_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2011 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include <string>
+
+#include "include/wrapper/cef_scoped_temp_dir.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/file_util.h"
+
+TEST(FileUtil, JoinPath) {
+  // Should return whichever path component is non-empty.
+  EXPECT_STREQ("", client::file_util::JoinPath("", "").c_str());
+  EXPECT_STREQ("path1", client::file_util::JoinPath("path1", "").c_str());
+  EXPECT_STREQ("path2", client::file_util::JoinPath("", "path2").c_str());
+
+  const std::string& expected =
+      std::string("path1") + client::file_util::kPathSep + std::string("path2");
+
+  // Should always be 1 kPathSep character between paths.
+  EXPECT_STREQ(expected.c_str(),
+               client::file_util::JoinPath("path1", "path2").c_str());
+  EXPECT_STREQ(expected.c_str(),
+               client::file_util::JoinPath(
+                   std::string("path1") + client::file_util::kPathSep, "path2")
+                   .c_str());
+  EXPECT_STREQ(expected.c_str(),
+               client::file_util::JoinPath(
+                   "path1", client::file_util::kPathSep + std::string("path2"))
+                   .c_str());
+  EXPECT_STREQ(expected.c_str(),
+               client::file_util::JoinPath(
+                   std::string("path1") + client::file_util::kPathSep,
+                   client::file_util::kPathSep + std::string("path2"))
+                   .c_str());
+}
+
+TEST(FileUtil, WriteAndReadFile) {
+  CefScopedTempDir dir;
+  EXPECT_TRUE(dir.CreateUniqueTempDir());
+
+  const std::string& data = "Test contents to read/write";
+  const std::string& path =
+      client::file_util::JoinPath(dir.GetPath(), "test.txt");
+
+  EXPECT_EQ(static_cast<int>(data.size()),
+            client::file_util::WriteFile(path.c_str(), data.data(),
+                                         static_cast<int>(data.size())));
+
+  std::string read;
+  EXPECT_TRUE(client::file_util::ReadFileToString(path.c_str(), &read));
+  EXPECT_STREQ(data.c_str(), read.c_str());
+}
+
+TEST(FileUtil, GetFileExtension) {
+  EXPECT_TRUE(client::file_util::GetFileExtension(std::string()).empty());
+  EXPECT_TRUE(client::file_util::GetFileExtension("/path/to/foo").empty());
+  EXPECT_STREQ("ext",
+               client::file_util::GetFileExtension("/path/to/foo.ext").c_str());
+}
diff --git a/src/tests/ceftests/frame_unittest.cc b/src/tests/ceftests/frame_unittest.cc
new file mode 100644
index 0000000..1498a32
--- /dev/null
+++ b/src/tests/ceftests/frame_unittest.cc
@@ -0,0 +1,2309 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppBrowser;
+using client::ClientAppRenderer;
+
+namespace {
+
+// The frame navigation test harness work as follows:
+//
+// In the browser process:
+// 1. TEST() function creates a new FrameNavTestHandler instance with a unique
+//    FrameNavFactoryId.
+// 2. FrameNavTestHandler calls FrameNavExpectationsFactoryBrowser::FromID to
+//    create a new factory instance.
+// 3. FrameNavTestHandler calls FrameNavExpectationsFactoryBrowser::Create to
+//    create a new FrameNavExpectationsBrowser instance for the current
+//    navigation.
+// 4. FrameNavTestHandler retrieves the URL to load via
+//    FrameNavExpectationsBrowser::GetMainURL and calls either CreateBrowser
+//    (for the first navigation) or LoadURL (for the following navigations).
+// 5. If the renderer process does not already exist CEF creates it with
+//    command-line arguments that specify the FrameNavFactoryId via
+//    FrameNavBrowserTest::OnBeforeChildProcessLaunch.
+//
+// In the renderer process:
+// 6. If the renderer process is newly created FrameNavRendererTest calls
+//    FrameNavExpectationsFactoryRenderer::FromID to create a new factory
+//    instance.
+// 7. FrameNavRendererTest calls FrameNavExpectationsFactoryRenderer::Create to
+//    create a new FrameNavExpectationsRenderer instance for the current
+//    navigation.
+//
+// In both processes:
+// 8. Callback notifications are sent to the FrameNavExpectations* instances.
+//
+// In the renderer process:
+// 9. When the FrameNavExpectationsRenderer instance determines that the
+//    renderer side of the test is complete it calls SignalComplete which
+//    finalizes and deletes the FrameNavExpectationsRenderer instance and
+//    sends an IPC message to the browser process.
+//
+// In the browser process:
+// 11.FrameNavExpectationsBrowser::OnRendererComplete is called in response to
+//    renderer-side test completion message.
+// 12.When the FrameNavExpectationsBrowser instance determines that the browser
+//    side of the test is complete it calls SignalComplete which finalizes and
+//    deletes the FrameNavExpectationsBrowser instance.
+// 13.If FrameNavExpectationsFactoryBrowser::HasMoreNavigations returns false
+//    then DestroyTest is called and the test ends. Otherwise, the navigation
+//    count is incremented and the process repeats starting with step #3.
+//
+//
+// To add a new test case:
+// 1. Add a new value to the FrameNavFactoryId enumeration.
+// 2. Provide implementations of FrameNavExpectations*.
+// 3. Add a case for the new factory ID to FrameNavExpectationsFactory*::FromID.
+// 4. Implement a TEST() function that creates a FrameNavTestHandler instance
+//    and passes the new factory ID.
+//
+//
+// Run with the `--single-process` command-line flag to see expectation failures
+// from the renderer process.
+//
+
+// All known factory IDs.
+enum FrameNavFactoryId {
+  FNF_ID_INVALID = 0,
+  FNF_ID_SINGLE_NAV_HARNESS,
+  FNF_ID_SINGLE_NAV,
+  FNF_ID_MULTI_NAV_HARNESS,
+  FNF_ID_MULTI_NAV,
+  FNF_ID_NESTED_IFRAMES_SAME_ORIGIN,
+  FNF_ID_NESTED_IFRAMES_DIFF_ORIGIN,
+};
+
+// IPC message name.
+const char kFrameNavMsg[] = "FrameTest.Navigation";
+
+// Extra info parameter keys.
+const char kFrameNavTestCmdKey[] = "frame-nav-test";
+
+// Origins used in tests.
+const char kFrameNavOrigin0[] = "http://tests-framenav0.com/";
+const char kFrameNavOrigin1[] = "http://tests-framenav1.com/";
+const char kFrameNavOrigin2[] = "http://tests-framenav2.com/";
+const char kFrameNavOrigin3[] = "http://tests-framenav3.com/";
+
+// Maximum number of navigations. Should be kept synchronized with the number
+// of kFrameNavOrigin* values. Don't modify this value without checking the
+// below use cases.
+const int kMaxMultiNavNavigations = 4;
+
+// Abstract base class representing expectations that result from a navigation.
+class FrameNavExpectations {
+ public:
+  typedef base::Callback<void(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>)>
+      CompletionCallback;
+
+  FrameNavExpectations(int nav, bool renderer)
+      : nav_(nav), renderer_(renderer) {}
+  virtual ~FrameNavExpectations() {}
+
+  // Browser and renderer notifications.
+  virtual bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                                    bool isLoading) {
+    return true;
+  }
+  virtual bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame) {
+    return true;
+  }
+  virtual bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame) {
+    return true;
+  }
+
+  // Final expectations check before this object is deleted.
+  virtual bool Finalize() = 0;
+
+  // Signal that all expectations are completed. Should be called as a result of
+  // notifications.
+  void SignalComplete(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame) {
+    if (!completion_callback_.is_null()) {
+      // Execute the callback asynchronously to avoid any issues with what's
+      // currently on the stack.
+      CefPostTask((renderer_ ? TID_RENDERER : TID_UI),
+                  base::Bind(completion_callback_, browser, frame));
+      completion_callback_.Reset();
+    }
+  }
+
+  // Returns the current navigation count. In the browser process this value
+  // increments over the life span of the FrameNavTestHandler instance. In the
+  // renderer process this value increments over the life span of a single
+  // renderer instance (i.e. cross-origin navigations will cause this value to
+  // reset).
+  int nav() const { return nav_; }
+
+  // Returns true if this is a renderer-side expectation object.
+  bool renderer() const { return renderer_; }
+
+  void set_completion_callback(const CompletionCallback& completion_callback) {
+    completion_callback_ = completion_callback;
+  }
+
+ private:
+  int nav_;
+  bool renderer_;
+  CompletionCallback completion_callback_;
+};
+
+// Browser process expectations abstract base class.
+class FrameNavExpectationsBrowser : public FrameNavExpectations {
+ public:
+  explicit FrameNavExpectationsBrowser(int nav)
+      : FrameNavExpectations(nav, false) {}
+
+  // Loading information.
+  virtual std::string GetMainURL() = 0;
+  virtual std::string GetContentForURL(const std::string& url) = 0;
+
+  // Browser-only notifications.
+  virtual bool OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+    EXPECT_TRUE(browser.get());
+    return true;
+  }
+  virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              const std::string& url) {
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    EXPECT_FALSE(url.empty());
+    return true;
+  }
+  virtual bool GetResourceHandler(CefRefPtr<CefBrowser> browser,
+                                  CefRefPtr<CefFrame> frame) {
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    return true;
+  }
+
+  // Called when the renderer signals completion.
+  virtual bool OnRendererComplete(CefRefPtr<CefBrowser> browser,
+                                  CefRefPtr<CefFrame> frame,
+                                  int renderer_nav,
+                                  bool renderer_result) = 0;
+};
+
+// Renderer process expectations abstract base class.
+class FrameNavExpectationsRenderer : public FrameNavExpectations {
+ public:
+  explicit FrameNavExpectationsRenderer(int nav)
+      : FrameNavExpectations(nav, true) {}
+};
+
+// Abstract base class for the factory that creates expectations objects.
+class FrameNavExpectationsFactory {
+ public:
+  FrameNavExpectationsFactory() {}
+  virtual ~FrameNavExpectationsFactory() {}
+
+  // Returns the unique ID for this factory type.
+  virtual FrameNavFactoryId GetID() const = 0;
+};
+
+// Browser process expectations factory abstact base class.
+class FrameNavExpectationsFactoryBrowser : public FrameNavExpectationsFactory {
+ public:
+  FrameNavExpectationsFactoryBrowser() {}
+
+  // Create a new factory instance of the specified type.
+  static scoped_ptr<FrameNavExpectationsFactoryBrowser> FromID(
+      FrameNavFactoryId id);
+
+  // Returns true if there will be more navigations in the browser process
+  // handler.
+  virtual bool HasMoreNavigations() const = 0;
+
+  // Verify final expectations results.
+  virtual bool Finalize() = 0;
+
+  scoped_ptr<FrameNavExpectationsBrowser> Create(
+      int nav,
+      const FrameNavExpectations::CompletionCallback& completion_callback) {
+    scoped_ptr<FrameNavExpectationsBrowser> expectations;
+    expectations = Create(nav);
+    expectations->set_completion_callback(completion_callback);
+    return expectations.Pass();
+  }
+
+ protected:
+  // Implement in the test-specific factory instance.
+  virtual scoped_ptr<FrameNavExpectationsBrowser> Create(int nav) = 0;
+};
+
+// Renderer process expectations factory abstact base class.
+class FrameNavExpectationsFactoryRenderer : public FrameNavExpectationsFactory {
+ public:
+  FrameNavExpectationsFactoryRenderer() {}
+
+  // Create a new factory instance of the specified type.
+  static scoped_ptr<FrameNavExpectationsFactoryRenderer> FromID(
+      FrameNavFactoryId id);
+
+  scoped_ptr<FrameNavExpectationsRenderer> Create(
+      int nav,
+      const FrameNavExpectations::CompletionCallback& completion_callback) {
+    scoped_ptr<FrameNavExpectationsRenderer> expectations;
+    expectations = Create(nav);
+    expectations->set_completion_callback(completion_callback);
+    return expectations.Pass();
+  }
+
+ protected:
+  // Implement in the test-specific factory instance.
+  virtual scoped_ptr<FrameNavExpectationsRenderer> Create(int nav) = 0;
+};
+
+// Renderer side handler.
+class FrameNavRendererTest : public ClientAppRenderer::Delegate,
+                             public CefLoadHandler {
+ public:
+  FrameNavRendererTest() : run_test_(false), nav_(0) {}
+
+  void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override {
+    if (!extra_info->HasKey(kFrameNavTestCmdKey))
+      return;
+
+    FrameNavFactoryId factory_id =
+        static_cast<FrameNavFactoryId>(extra_info->GetInt(kFrameNavTestCmdKey));
+    run_test_ = factory_id != FNF_ID_INVALID;
+    if (!run_test_)
+      return;
+
+    factory_ = FrameNavExpectationsFactoryRenderer::FromID(factory_id);
+  }
+
+  CefRefPtr<CefLoadHandler> GetLoadHandler(
+      CefRefPtr<ClientAppRenderer> app) override {
+    if (!run_test_)
+      return nullptr;
+
+    return this;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    CreateExpectationsIfNecessary();
+    EXPECT_TRUE(expectations_->OnLoadingStateChange(browser, isLoading))
+        << "isLoading = " << isLoading << ", nav = " << nav_;
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    CreateExpectationsIfNecessary();
+    EXPECT_TRUE(expectations_->OnLoadStart(browser, frame)) << "nav = " << nav_;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    CreateExpectationsIfNecessary();
+    EXPECT_TRUE(expectations_->OnLoadEnd(browser, frame)) << "nav = " << nav_;
+  }
+
+ protected:
+  // Create a new expectations object if one does not already exist for the
+  // current navigation.
+  void CreateExpectationsIfNecessary() {
+    if (expectations_)
+      return;
+    expectations_ = factory_->Create(
+        nav_, base::Bind(&FrameNavRendererTest::SendTestResults, this));
+  }
+
+  // Send the test results.
+  // Will be called via FrameNavExpectations::SignalComplete.
+  void SendTestResults(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame) {
+    // End of the current expectations object.
+    EXPECT_TRUE(expectations_->Finalize()) << "nav = " << nav_;
+    expectations_.reset(nullptr);
+
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kFrameNavMsg);
+    CefRefPtr<CefListValue> args = return_msg->GetArgumentList();
+    EXPECT_TRUE(args.get());
+    EXPECT_TRUE(args->SetInt(0, nav_));
+    EXPECT_TRUE(args->SetBool(1, result));
+
+    const int64 frame_id = frame->GetIdentifier();
+    EXPECT_TRUE(args->SetInt(2, CefInt64GetLow(frame_id)));
+    EXPECT_TRUE(args->SetInt(3, CefInt64GetHigh(frame_id)));
+
+    frame->SendProcessMessage(PID_BROWSER, return_msg);
+
+    nav_++;
+  }
+
+  bool run_test_;
+  int nav_;
+  scoped_ptr<FrameNavExpectationsFactoryRenderer> factory_;
+  scoped_ptr<FrameNavExpectationsRenderer> expectations_;
+
+  IMPLEMENT_REFCOUNTING(FrameNavRendererTest);
+};
+
+// Browser side handler.
+class FrameNavTestHandler : public TestHandler {
+ public:
+  explicit FrameNavTestHandler(FrameNavFactoryId factory_id)
+      : nav_(0),
+        factory_(FrameNavExpectationsFactoryBrowser::FromID(factory_id)) {}
+
+  ~FrameNavTestHandler() override { EXPECT_TRUE(got_destroyed_); }
+
+  void RunTest() override {
+    // Create the first expectations object.
+    expectations_ = factory_->Create(
+        nav_, base::Bind(&FrameNavTestHandler::RunNextNav, this));
+
+    CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
+    extra_info->SetInt(kFrameNavTestCmdKey, factory_->GetID());
+
+    // Create the browser with the initial URL.
+    CreateBrowser(expectations_->GetMainURL(), nullptr, extra_info);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout(15000);
+  }
+
+  // Transition to the next navigation.
+  // Will be called via FrameNavExpectations::SignalComplete.
+  void RunNextNav(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) {
+    // End of the current expectations object.
+    EXPECT_TRUE(expectations_->Finalize());
+    expectations_.reset(nullptr);
+
+    if (!factory_->HasMoreNavigations()) {
+      // End of the test.
+      DestroyTest();
+      return;
+    }
+
+    nav_++;
+
+    // Create the next expectations object.
+    expectations_ = factory_->Create(
+        nav_, base::Bind(&FrameNavTestHandler::RunNextNav, this));
+
+    // Load the main URL.
+    browser->GetMainFrame()->LoadURL(expectations_->GetMainURL());
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    EXPECT_TRUE(expectations_->OnAfterCreated(browser)) << "nav = " << nav_;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(expectations_->GetResourceHandler(browser, frame))
+        << "nav = " << nav_;
+
+    const std::string& url = request->GetURL();
+    const std::string& content = expectations_->GetContentForURL(url);
+    EXPECT_TRUE(!content.empty()) << "nav = " << nav_;
+
+    CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+        static_cast<void*>(const_cast<char*>(content.c_str())),
+        content.length());
+    return new CefStreamResourceHandler("text/html", stream);
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_TRUE(
+        expectations_->OnBeforeBrowse(browser, frame, request->GetURL()))
+        << "nav = " << nav_;
+
+    return false;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    EXPECT_TRUE(expectations_->OnLoadingStateChange(browser, isLoading))
+        << "isLoading = " << isLoading << ", nav = " << nav_;
+    ;
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    EXPECT_TRUE(expectations_->OnLoadStart(browser, frame)) << "nav = " << nav_;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_TRUE(expectations_->OnLoadEnd(browser, frame)) << "nav = " << nav_;
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName().ToString() == kFrameNavMsg) {
+      // Test that the renderer side succeeded.
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_TRUE(args.get());
+
+      EXPECT_TRUE(expectations_->OnRendererComplete(
+          browser, frame, args->GetInt(0), args->GetBool(1)))
+          << "nav = " << nav_;
+
+      // Test that browser and render process frame IDs match.
+      const int64 frame_id = CefInt64Set(args->GetInt(2), args->GetInt(3));
+      EXPECT_EQ(frame->GetIdentifier(), frame_id);
+
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+  void DestroyTest() override {
+    if (got_destroyed_)
+      return;
+
+    got_destroyed_.yes();
+
+    // The expectations should have been tested already.
+    EXPECT_FALSE(expectations_.get());
+
+    // Test that factory conditions we met.
+    EXPECT_TRUE(factory_->Finalize()) << "nav = " << nav_;
+
+    TestHandler::DestroyTest();
+  }
+
+  int nav_;
+  TrackCallback got_destroyed_;
+  scoped_ptr<FrameNavExpectationsFactoryBrowser> factory_;
+  scoped_ptr<FrameNavExpectationsBrowser> expectations_;
+
+  IMPLEMENT_REFCOUNTING(FrameNavTestHandler);
+};
+
+// Helper for defining frame tests.
+#define FRAME_TEST(name, factory_id)         \
+  TEST(FrameTest, name) {                    \
+    CefRefPtr<FrameNavTestHandler> handler = \
+        new FrameNavTestHandler(factory_id); \
+    handler->ExecuteTest();                  \
+    ReleaseAndWaitForDestructor(handler);    \
+  }
+
+// Browser process expectations for a single navigation.
+class FrameNavExpectationsBrowserSingleNav
+    : public FrameNavExpectationsBrowser {
+ public:
+  explicit FrameNavExpectationsBrowserSingleNav(int nav)
+      : FrameNavExpectationsBrowser(nav) {}
+
+  ~FrameNavExpectationsBrowserSingleNav() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (isLoading) {
+      EXPECT_FALSE(got_loading_state_change_start_);
+      got_loading_state_change_start_.yes();
+    } else {
+      EXPECT_FALSE(got_loading_state_change_end_);
+      got_loading_state_change_end_.yes();
+      SignalCompleteIfDone(browser, browser->GetMainFrame());
+    }
+    return true;
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_load_start_);
+    got_load_start_.yes();
+    return true;
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_load_end_);
+    got_load_end_.yes();
+    SignalCompleteIfDone(browser, frame);
+    return true;
+  }
+
+  bool OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    EXPECT_FALSE(got_after_created_);
+    got_after_created_.yes();
+    return true;
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      const std::string& url) override {
+    EXPECT_FALSE(got_before_browse_);
+    got_before_browse_.yes();
+    return true;
+  }
+
+  bool GetResourceHandler(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_get_resource_handler_);
+    got_get_resource_handler_.yes();
+    return true;
+  }
+
+  bool OnRendererComplete(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          int renderer_nav,
+                          bool renderer_result) override {
+    EXPECT_EQ(nav(), renderer_nav);
+    EXPECT_TRUE(renderer_result);
+    EXPECT_FALSE(got_renderer_done_);
+    got_renderer_done_.yes();
+    SignalCompleteIfDone(browser, frame);
+    return true;
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_start_);
+    V_EXPECT_TRUE(got_load_end_);
+    V_EXPECT_TRUE(got_loading_state_change_start_);
+    V_EXPECT_TRUE(got_loading_state_change_end_);
+    V_EXPECT_TRUE(got_renderer_done_);
+    V_EXPECT_TRUE(got_after_created_);
+    V_EXPECT_TRUE(got_before_browse_);
+    V_EXPECT_TRUE(got_get_resource_handler_);
+    V_EXPECT_FALSE(got_finalize_);
+
+    got_finalize_.yes();
+
+    V_RETURN();
+  }
+
+ private:
+  void SignalCompleteIfDone(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame) {
+    if (got_renderer_done_ && got_load_end_ && got_loading_state_change_end_)
+      SignalComplete(browser, frame);
+  }
+
+  TrackCallback got_load_start_;
+  TrackCallback got_load_end_;
+  TrackCallback got_loading_state_change_start_;
+  TrackCallback got_loading_state_change_end_;
+  TrackCallback got_renderer_done_;
+  TrackCallback got_after_created_;
+  TrackCallback got_before_browse_;
+  TrackCallback got_get_resource_handler_;
+  TrackCallback got_finalize_;
+};
+
+// Renderer process expectations for a single navigation.
+class FrameNavExpectationsRendererSingleNav
+    : public FrameNavExpectationsRenderer {
+ public:
+  explicit FrameNavExpectationsRendererSingleNav(int nav)
+      : FrameNavExpectationsRenderer(nav) {}
+
+  ~FrameNavExpectationsRendererSingleNav() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (isLoading) {
+      EXPECT_FALSE(got_loading_state_change_start_);
+      got_loading_state_change_start_.yes();
+    } else {
+      EXPECT_FALSE(got_loading_state_change_end_);
+      got_loading_state_change_end_.yes();
+      SignalCompleteIfDone(browser, browser->GetMainFrame());
+    }
+    return true;
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_load_start_);
+    got_load_start_.yes();
+    return true;
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_load_end_);
+    got_load_end_.yes();
+    SignalCompleteIfDone(browser, frame);
+    return true;
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_start_);
+    V_EXPECT_TRUE(got_load_end_);
+    V_EXPECT_TRUE(got_loading_state_change_start_);
+    V_EXPECT_TRUE(got_loading_state_change_end_);
+    V_EXPECT_FALSE(got_finalize_);
+
+    got_finalize_.yes();
+
+    V_RETURN();
+  }
+
+ private:
+  void SignalCompleteIfDone(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame) {
+    if (got_load_end_ && got_loading_state_change_end_)
+      SignalComplete(browser, frame);
+  }
+
+  TrackCallback got_load_start_;
+  TrackCallback got_load_end_;
+  TrackCallback got_loading_state_change_start_;
+  TrackCallback got_loading_state_change_end_;
+  TrackCallback got_finalize_;
+};
+
+// Test that the single nav harness works.
+class FrameNavExpectationsBrowserTestSingleNavHarness
+    : public FrameNavExpectationsBrowserSingleNav {
+ public:
+  typedef FrameNavExpectationsBrowserSingleNav parent;
+
+  explicit FrameNavExpectationsBrowserTestSingleNavHarness(int nav)
+      : parent(nav) {}
+
+  ~FrameNavExpectationsBrowserTestSingleNavHarness() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  std::string GetMainURL() override {
+    EXPECT_FALSE(got_get_main_url_);
+    got_get_main_url_.yes();
+    return kFrameNavOrigin0;
+  }
+
+  std::string GetContentForURL(const std::string& url) override {
+    EXPECT_FALSE(got_get_content_for_url_);
+    got_get_content_for_url_.yes();
+    EXPECT_STREQ(kFrameNavOrigin0, url.c_str());
+    return "<html><body>Nav</body></html>";
+  }
+
+  bool Finalize() override {
+    EXPECT_FALSE(got_finalize_);
+    got_finalize_.yes();
+
+    V_DECLARE();
+    V_EXPECT_TRUE(got_get_main_url_);
+    V_EXPECT_TRUE(got_get_content_for_url_);
+    V_EXPECT_TRUE(parent::Finalize());
+    V_RETURN();
+  }
+
+ private:
+  TrackCallback got_get_main_url_;
+  TrackCallback got_get_content_for_url_;
+  TrackCallback got_finalize_;
+};
+
+class FrameNavExpectationsRendererTestSingleNavHarness
+    : public FrameNavExpectationsRendererSingleNav {
+ public:
+  typedef FrameNavExpectationsRendererSingleNav parent;
+
+  explicit FrameNavExpectationsRendererTestSingleNavHarness(int nav)
+      : parent(nav) {}
+
+  ~FrameNavExpectationsRendererTestSingleNavHarness() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  bool Finalize() override {
+    EXPECT_FALSE(got_finalize_);
+    got_finalize_.yes();
+    return parent::Finalize();
+  }
+
+ private:
+  TrackCallback got_finalize_;
+};
+
+class FrameNavExpectationsFactoryBrowserTestSingleNavHarness
+    : public FrameNavExpectationsFactoryBrowser {
+ public:
+  FrameNavExpectationsFactoryBrowserTestSingleNavHarness() {}
+
+  ~FrameNavExpectationsFactoryBrowserTestSingleNavHarness() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV_HARNESS; }
+
+  bool HasMoreNavigations() const override {
+    EXPECT_FALSE(got_get_browser_navigation_count_);
+    got_get_browser_navigation_count_.yes();
+    return false;
+  }
+
+  bool Finalize() override {
+    EXPECT_FALSE(got_finalize_);
+    got_finalize_.yes();
+
+    V_DECLARE();
+    V_EXPECT_TRUE(got_get_browser_navigation_count_);
+    V_EXPECT_TRUE(got_create_);
+    V_RETURN();
+  }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsBrowser> Create(int nav) override {
+    EXPECT_FALSE(got_create_);
+    got_create_.yes();
+    return scoped_ptr<FrameNavExpectationsBrowser>(
+        new FrameNavExpectationsBrowserTestSingleNavHarness(nav));
+  }
+
+ private:
+  mutable TrackCallback got_get_browser_navigation_count_;
+  TrackCallback got_create_;
+  TrackCallback got_finalize_;
+};
+
+class FrameNavExpectationsFactoryRendererTestSingleNavHarness
+    : public FrameNavExpectationsFactoryRenderer {
+ public:
+  FrameNavExpectationsFactoryRendererTestSingleNavHarness() {}
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV_HARNESS; }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsRenderer> Create(int nav) override {
+    return scoped_ptr<FrameNavExpectationsRenderer>(
+        new FrameNavExpectationsRendererTestSingleNavHarness(nav));
+  }
+};
+
+}  // namespace
+
+// Test that the single nav harness works.
+FRAME_TEST(SingleNavHarness, FNF_ID_SINGLE_NAV_HARNESS)
+
+namespace {
+
+bool VerifySingleBrowserFrame(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              const std::string& expected_url) {
+  V_DECLARE();
+  V_EXPECT_TRUE(frame.get());
+  V_EXPECT_TRUE(frame->IsValid());
+  const int64 frame_id = frame->GetIdentifier();
+  V_EXPECT_TRUE(frame_id > 0) << frame_id;
+  V_EXPECT_TRUE(frame->IsValid());
+  V_EXPECT_TRUE(frame->IsMain());
+  V_EXPECT_TRUE(frame->IsFocused());
+  V_EXPECT_FALSE(frame->GetParent().get());
+  V_EXPECT_TRUE(frame->GetName().empty());
+  V_EXPECT_TRUE(browser->GetIdentifier() ==
+                frame->GetBrowser()->GetIdentifier());
+
+  const std::string& frame_url = frame->GetURL();
+  V_EXPECT_TRUE(frame_url == expected_url)
+      << "frame_url = " << frame_url << ", expected_url = " << expected_url;
+
+  V_RETURN();
+}
+
+bool VerifySingleBrowserFrames(CefRefPtr<CefBrowser> browser,
+                               CefRefPtr<CefFrame> frame,
+                               const std::string& expected_url) {
+  V_DECLARE();
+  V_EXPECT_TRUE(browser.get());
+
+  // |frame| may be nullptr for callbacks that don't specify one.
+  if (frame.get()) {
+    V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, frame, expected_url));
+  }
+
+  CefRefPtr<CefFrame> main_frame = browser->GetMainFrame();
+  V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, main_frame, expected_url));
+
+  CefRefPtr<CefFrame> focused_frame = browser->GetFocusedFrame();
+  V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, focused_frame, expected_url));
+
+  size_t frame_count = browser->GetFrameCount();
+  V_EXPECT_TRUE(frame_count == 1U);
+
+  std::vector<int64> identifiers;
+  browser->GetFrameIdentifiers(identifiers);
+  V_EXPECT_TRUE(identifiers.size() == 1U);
+  if (identifiers.size() == 1U) {
+    V_EXPECT_TRUE(identifiers[0] == main_frame->GetIdentifier());
+    V_EXPECT_TRUE(identifiers[0] == focused_frame->GetIdentifier());
+  }
+
+  // Names may be empty for callbacks that execute while the frame is loading.
+  std::vector<CefString> names;
+  browser->GetFrameNames(names);
+  V_EXPECT_TRUE(names.size() <= 1U);
+  if (names.size() == 1U) {
+    V_EXPECT_TRUE(names[0].ToString() == main_frame->GetName().ToString());
+    V_EXPECT_TRUE(names[0].ToString() == focused_frame->GetName().ToString());
+  }
+
+  V_RETURN();
+}
+
+// Test that single navigation works.
+class FrameNavExpectationsBrowserTestSingleNav
+    : public FrameNavExpectationsBrowserSingleNav {
+ public:
+  typedef FrameNavExpectationsBrowserSingleNav parent;
+
+  explicit FrameNavExpectationsBrowserTestSingleNav(int nav) : parent(nav) {}
+
+  std::string GetMainURL() override { return kFrameNavOrigin0; }
+
+  std::string GetContentForURL(const std::string& url) override {
+    return "<html><body>Nav</body></html>";
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(
+        browser, nullptr, isLoading ? std::string() : kFrameNavOrigin0));
+    V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading));
+    V_RETURN();
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0));
+    V_EXPECT_TRUE(parent::OnLoadStart(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0));
+    V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, nullptr, std::string()));
+    V_EXPECT_TRUE(parent::OnAfterCreated(browser));
+    V_RETURN();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      const std::string& url) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, std::string()));
+    V_EXPECT_TRUE(parent::OnBeforeBrowse(browser, frame, url));
+    V_RETURN();
+  }
+
+  bool GetResourceHandler(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, std::string()));
+    V_EXPECT_TRUE(parent::GetResourceHandler(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnRendererComplete(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          int renderer_nav,
+                          bool renderer_result) override {
+    return parent::OnRendererComplete(browser, frame, renderer_nav,
+                                      renderer_result);
+  }
+
+  bool Finalize() override { return parent::Finalize(); }
+};
+
+class FrameNavExpectationsRendererTestSingleNav
+    : public FrameNavExpectationsRendererSingleNav {
+ public:
+  typedef FrameNavExpectationsRendererSingleNav parent;
+
+  explicit FrameNavExpectationsRendererTestSingleNav(int nav) : parent(nav) {}
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    V_DECLARE();
+    // A frame should always exist in the renderer process.
+    V_EXPECT_TRUE(
+        VerifySingleBrowserFrames(browser, nullptr, kFrameNavOrigin0));
+    V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading));
+    V_RETURN();
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0));
+    V_EXPECT_TRUE(parent::OnLoadStart(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0));
+    V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame));
+    V_RETURN();
+  }
+
+  bool Finalize() override { return parent::Finalize(); }
+};
+
+class FrameNavExpectationsFactoryBrowserTestSingleNav
+    : public FrameNavExpectationsFactoryBrowser {
+ public:
+  FrameNavExpectationsFactoryBrowserTestSingleNav() {}
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV; }
+
+  bool HasMoreNavigations() const override { return false; }
+
+  bool Finalize() override { return true; }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsBrowser> Create(int nav) override {
+    return scoped_ptr<FrameNavExpectationsBrowser>(
+        new FrameNavExpectationsBrowserTestSingleNav(nav));
+  }
+};
+
+class FrameNavExpectationsFactoryRendererTestSingleNav
+    : public FrameNavExpectationsFactoryRenderer {
+ public:
+  FrameNavExpectationsFactoryRendererTestSingleNav() {}
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV; }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsRenderer> Create(int nav) override {
+    return scoped_ptr<FrameNavExpectationsRenderer>(
+        new FrameNavExpectationsRendererTestSingleNav(nav));
+  }
+};
+
+}  // namespace
+
+// Test that single navigation works.
+FRAME_TEST(SingleNav, FNF_ID_SINGLE_NAV)
+
+namespace {
+
+// Browser process expectations for a multiple navigations.
+class FrameNavExpectationsBrowserMultiNav : public FrameNavExpectationsBrowser {
+ public:
+  explicit FrameNavExpectationsBrowserMultiNav(int nav)
+      : FrameNavExpectationsBrowser(nav) {}
+
+  ~FrameNavExpectationsBrowserMultiNav() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  // Returns true if all navigation is done.
+  virtual bool IsNavigationDone() const = 0;
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (!isLoading)
+      SignalCompleteIfDone(browser, browser->GetMainFrame());
+    return true;
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    SignalCompleteIfDone(browser, frame);
+    return true;
+  }
+
+  bool OnRendererComplete(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          int renderer_nav,
+                          bool renderer_result) override {
+    EXPECT_TRUE(renderer_result);
+    SignalCompleteIfDone(browser, frame);
+    return true;
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_FALSE(got_finalize_);
+
+    got_finalize_.yes();
+
+    V_RETURN();
+  }
+
+ private:
+  void SignalCompleteIfDone(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame) {
+    if (IsNavigationDone())
+      SignalComplete(browser, frame);
+  }
+
+  TrackCallback got_finalize_;
+};
+
+// Renderer process expectations for a multiple navigations.
+class FrameNavExpectationsRendererMultiNav
+    : public FrameNavExpectationsRenderer {
+ public:
+  explicit FrameNavExpectationsRendererMultiNav(int nav)
+      : FrameNavExpectationsRenderer(nav) {}
+
+  ~FrameNavExpectationsRendererMultiNav() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  // Returns true if all navigation is done.
+  virtual bool IsNavigationDone() const = 0;
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (!isLoading)
+      SignalCompleteIfDone(browser, browser->GetMainFrame());
+    return true;
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    SignalCompleteIfDone(browser, frame);
+    return true;
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_FALSE(got_finalize_);
+
+    got_finalize_.yes();
+
+    V_RETURN();
+  }
+
+ private:
+  void SignalCompleteIfDone(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame) {
+    if (IsNavigationDone())
+      SignalComplete(browser, frame);
+  }
+
+  TrackCallback got_finalize_;
+};
+
+// Create a URL containing the nav number.
+std::string GetMultiNavURL(const std::string& origin, int nav) {
+  std::stringstream ss;
+  ss << origin << "nav" << nav << ".html";
+  return ss.str();
+}
+
+// Extract the nav number from the URL.
+int GetNavFromMultiNavURL(const std::string& url) {
+  const size_t start = url.find("/nav");
+  const size_t end = url.find(".html", start);
+  EXPECT_TRUE(start < end && start > 0U);
+  const std::string& nav = url.substr(start + 4, end - start - 4);
+  return atoi(nav.c_str());
+}
+
+// Extract the origin from the URL.
+std::string GetOriginFromMultiNavURL(const std::string& url) {
+  const size_t pos = url.rfind("/");
+  EXPECT_TRUE(pos > 0U);
+  return url.substr(0, pos + 1);
+}
+
+// Test that the multi nav harness works.
+class FrameNavExpectationsBrowserTestMultiNavHarness
+    : public FrameNavExpectationsBrowserMultiNav {
+ public:
+  typedef FrameNavExpectationsBrowserMultiNav parent;
+
+  explicit FrameNavExpectationsBrowserTestMultiNavHarness(int nav)
+      : parent(nav), navigation_done_count_(0) {}
+
+  ~FrameNavExpectationsBrowserTestMultiNavHarness() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  std::string GetMainURL() override {
+    EXPECT_FALSE(got_get_main_url_);
+    got_get_main_url_.yes();
+    return GetMultiNavURL(kFrameNavOrigin0, nav());
+  }
+
+  std::string GetContentForURL(const std::string& url) override {
+    EXPECT_FALSE(got_get_content_for_url_);
+    got_get_content_for_url_.yes();
+    EXPECT_STREQ(GetMultiNavURL(kFrameNavOrigin0, nav()).c_str(), url.c_str());
+    return "<html><body>Nav</body></html>";
+  }
+
+  bool IsNavigationDone() const override {
+    navigation_done_count_++;
+    return got_load_state_change_done_ && got_load_end_ &&
+           got_renderer_complete_;
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (!isLoading) {
+      EXPECT_FALSE(got_load_state_change_done_);
+      got_load_state_change_done_.yes();
+    }
+    return parent::OnLoadingStateChange(browser, isLoading);
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_load_end_);
+    got_load_end_.yes();
+    return parent::OnLoadEnd(browser, frame);
+  }
+
+  bool OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    EXPECT_FALSE(got_on_after_created_);
+    got_on_after_created_.yes();
+    return parent::OnAfterCreated(browser);
+  }
+
+  bool OnRendererComplete(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          int renderer_nav,
+                          bool renderer_result) override {
+    EXPECT_FALSE(got_renderer_complete_);
+    got_renderer_complete_.yes();
+    EXPECT_EQ(nav(), renderer_nav);
+    return parent::OnRendererComplete(browser, frame, renderer_nav,
+                                      renderer_result);
+  }
+
+  bool Finalize() override {
+    EXPECT_FALSE(got_finalize_);
+    got_finalize_.yes();
+
+    V_DECLARE();
+    V_EXPECT_TRUE(got_get_main_url_);
+    V_EXPECT_TRUE(got_get_content_for_url_);
+    V_EXPECT_TRUE(got_load_state_change_done_);
+    V_EXPECT_TRUE(got_load_end_);
+    if (nav() == 0) {
+      V_EXPECT_TRUE(got_on_after_created_);
+    } else {
+      V_EXPECT_FALSE(got_on_after_created_);
+    }
+    V_EXPECT_TRUE(got_renderer_complete_);
+    V_EXPECT_TRUE(navigation_done_count_ == 3);
+    V_EXPECT_TRUE(parent::Finalize());
+    V_RETURN();
+  }
+
+ private:
+  TrackCallback got_get_main_url_;
+  TrackCallback got_get_content_for_url_;
+  TrackCallback got_load_state_change_done_;
+  TrackCallback got_load_end_;
+  TrackCallback got_on_after_created_;
+  TrackCallback got_renderer_complete_;
+  mutable int navigation_done_count_;
+  TrackCallback got_finalize_;
+};
+
+class FrameNavExpectationsRendererTestMultiNavHarness
+    : public FrameNavExpectationsRendererMultiNav {
+ public:
+  typedef FrameNavExpectationsRendererMultiNav parent;
+
+  explicit FrameNavExpectationsRendererTestMultiNavHarness(int nav)
+      : parent(nav), navigation_done_count_(0) {}
+
+  ~FrameNavExpectationsRendererTestMultiNavHarness() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  bool IsNavigationDone() const override {
+    navigation_done_count_++;
+    return got_load_state_change_done_ && got_load_end_;
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (!isLoading) {
+      EXPECT_FALSE(got_load_state_change_done_);
+      got_load_state_change_done_.yes();
+    }
+    return parent::OnLoadingStateChange(browser, isLoading);
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_load_end_);
+    got_load_end_.yes();
+    return parent::OnLoadEnd(browser, frame);
+  }
+
+  bool Finalize() override {
+    EXPECT_FALSE(got_finalize_);
+    got_finalize_.yes();
+
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_state_change_done_);
+    V_EXPECT_TRUE(got_load_end_);
+    V_EXPECT_TRUE(navigation_done_count_ == 2);
+    V_EXPECT_TRUE(parent::Finalize());
+    V_RETURN();
+  }
+
+ private:
+  TrackCallback got_load_state_change_done_;
+  TrackCallback got_load_end_;
+  mutable int navigation_done_count_;
+  TrackCallback got_finalize_;
+};
+
+class FrameNavExpectationsFactoryBrowserTestMultiNavHarness
+    : public FrameNavExpectationsFactoryBrowser {
+ public:
+  FrameNavExpectationsFactoryBrowserTestMultiNavHarness()
+      : get_browser_navigation_count_(0), create_count_(0) {}
+
+  ~FrameNavExpectationsFactoryBrowserTestMultiNavHarness() override {
+    EXPECT_TRUE(got_finalize_);
+  }
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV_HARNESS; }
+
+  bool HasMoreNavigations() const override {
+    get_browser_navigation_count_++;
+    return (get_browser_navigation_count_ < kMaxMultiNavNavigations);
+  }
+
+  bool Finalize() override {
+    EXPECT_FALSE(got_finalize_);
+    got_finalize_.yes();
+
+    V_DECLARE();
+    V_EXPECT_TRUE(get_browser_navigation_count_ == kMaxMultiNavNavigations);
+    V_EXPECT_TRUE(create_count_ == kMaxMultiNavNavigations);
+    V_RETURN();
+  }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsBrowser> Create(int nav) override {
+    create_count_++;
+    return scoped_ptr<FrameNavExpectationsBrowser>(
+        new FrameNavExpectationsBrowserTestMultiNavHarness(nav));
+  }
+
+ private:
+  mutable int get_browser_navigation_count_;
+  int create_count_;
+  TrackCallback got_finalize_;
+};
+
+class FrameNavExpectationsFactoryRendererTestMultiNavHarness
+    : public FrameNavExpectationsFactoryRenderer {
+ public:
+  FrameNavExpectationsFactoryRendererTestMultiNavHarness() {}
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV_HARNESS; }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsRenderer> Create(int nav) override {
+    return scoped_ptr<FrameNavExpectationsRenderer>(
+        new FrameNavExpectationsRendererTestMultiNavHarness(nav));
+  }
+};
+
+}  // namespace
+
+// Test that the multiple nav harness works.
+FRAME_TEST(MultiNavHarness, FNF_ID_MULTI_NAV_HARNESS)
+
+namespace {
+
+// Test that multiple navigation works.
+class FrameNavExpectationsBrowserTestMultiNav
+    : public FrameNavExpectationsBrowserMultiNav {
+ public:
+  typedef FrameNavExpectationsBrowserMultiNav parent;
+
+  explicit FrameNavExpectationsBrowserTestMultiNav(int nav) : parent(nav) {}
+
+  std::string GetMainURL() override {
+    return GetMultiNavURL(kFrameNavOrigin0, nav());
+  }
+
+  std::string GetContentForURL(const std::string& url) override {
+    return "<html><body>Nav</body></html>";
+  }
+
+  bool IsNavigationDone() const override {
+    return got_load_state_change_done_ && got_load_end_ &&
+           got_renderer_complete_;
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (!isLoading)
+      got_load_state_change_done_.yes();
+    V_DECLARE();
+    if (isLoading && nav() == 0) {
+      V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, nullptr, std::string()));
+    } else if (isLoading) {
+      // Expect the URL from the previous load.
+      V_EXPECT_TRUE(
+          VerifySingleBrowserFrames(browser, nullptr, GetPreviousMainURL()));
+    } else {
+      V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, nullptr, GetMainURL()));
+    }
+    V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading));
+    V_RETURN();
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL()));
+    V_EXPECT_TRUE(parent::OnLoadStart(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    got_load_end_.yes();
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL()));
+    V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, nullptr, std::string()));
+    V_EXPECT_TRUE(parent::OnAfterCreated(browser));
+    V_RETURN();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      const std::string& url) override {
+    V_DECLARE();
+    std::string expected_url;
+    if (nav() > 0)
+      expected_url = GetPreviousMainURL();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, expected_url));
+    V_EXPECT_TRUE(parent::OnBeforeBrowse(browser, frame, url));
+    V_RETURN();
+  }
+
+  bool GetResourceHandler(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    std::string expected_url;
+    if (nav() > 0)
+      expected_url = GetPreviousMainURL();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, expected_url));
+    V_EXPECT_TRUE(parent::GetResourceHandler(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnRendererComplete(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          int renderer_nav,
+                          bool renderer_result) override {
+    got_renderer_complete_.yes();
+    V_DECLARE();
+    V_EXPECT_TRUE(nav() == renderer_nav);
+    V_EXPECT_TRUE(parent::OnRendererComplete(browser, frame, renderer_nav,
+                                             renderer_result));
+    V_RETURN();
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_state_change_done_);
+    V_EXPECT_TRUE(got_load_end_);
+    V_EXPECT_TRUE(got_renderer_complete_);
+    V_EXPECT_TRUE(parent::Finalize());
+    V_RETURN();
+  }
+
+ private:
+  // Helper for VerifySingleBrowserFrames.
+  std::string GetPreviousMainURL() {
+    EXPECT_GT(nav(), 0);
+    return GetMultiNavURL(kFrameNavOrigin0, nav() - 1);
+  }
+
+  TrackCallback got_load_state_change_done_;
+  TrackCallback got_load_end_;
+  TrackCallback got_renderer_complete_;
+};
+
+class FrameNavExpectationsRendererTestMultiNav
+    : public FrameNavExpectationsRendererMultiNav {
+ public:
+  typedef FrameNavExpectationsRendererMultiNav parent;
+
+  explicit FrameNavExpectationsRendererTestMultiNav(int nav) : parent(nav) {}
+
+  bool IsNavigationDone() const override {
+    return got_load_state_change_done_ && got_load_end_;
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    if (!isLoading)
+      got_load_state_change_done_.yes();
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, nullptr, GetMainURL()));
+    V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading));
+    V_RETURN();
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL()));
+    V_EXPECT_TRUE(parent::OnLoadStart(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    got_load_end_.yes();
+    V_DECLARE();
+    V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL()));
+    V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame));
+    V_RETURN();
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_state_change_done_);
+    V_EXPECT_TRUE(got_load_end_);
+    V_EXPECT_TRUE(parent::Finalize());
+    V_RETURN();
+  }
+
+ private:
+  // Helpers for calling VerifySingleBrowserFrames.
+  std::string GetMainURL() const {
+    return GetMultiNavURL(kFrameNavOrigin0, nav());
+  }
+  std::string GetPreviousMainURL() {
+    EXPECT_GT(nav(), 0);
+    return GetMultiNavURL(kFrameNavOrigin0, nav() - 1);
+  }
+
+  TrackCallback got_load_state_change_done_;
+  TrackCallback got_load_end_;
+};
+
+class FrameNavExpectationsFactoryBrowserTestMultiNav
+    : public FrameNavExpectationsFactoryBrowser {
+ public:
+  FrameNavExpectationsFactoryBrowserTestMultiNav() : nav_count_(0) {}
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV; }
+
+  bool HasMoreNavigations() const override {
+    return (nav_count_ < kMaxMultiNavNavigations);
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(nav_count_ == kMaxMultiNavNavigations);
+    V_RETURN();
+  }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsBrowser> Create(int nav) override {
+    nav_count_++;
+    return scoped_ptr<FrameNavExpectationsBrowser>(
+        new FrameNavExpectationsBrowserTestMultiNav(nav));
+  }
+
+ private:
+  int nav_count_;
+};
+
+class FrameNavExpectationsFactoryRendererTestMultiNav
+    : public FrameNavExpectationsFactoryRenderer {
+ public:
+  FrameNavExpectationsFactoryRendererTestMultiNav() {}
+
+  FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV; }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsRenderer> Create(int nav) override {
+    return scoped_ptr<FrameNavExpectationsRenderer>(
+        new FrameNavExpectationsRendererTestMultiNav(nav));
+  }
+};
+
+}  // namespace
+
+// Test that multiple navigation works.
+FRAME_TEST(MultiNav, FNF_ID_MULTI_NAV)
+
+namespace {
+
+const char kFrame0Name[] = "";
+const char kFrame1Name[] = "nav2";
+const char kFrame2Name[] = "<!--framePath //nav2/<!--frame0-->-->";
+const char kFrame3Name[] = "nav3";
+
+bool VerifyBrowserIframe(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         const std::string& origin,
+                         int frame_number) {
+  V_DECLARE();
+
+  // frame0 contains frame1 contains frame2, contains frame3.
+  CefRefPtr<CefFrame> frame0, frame1, frame2, frame3;
+  CefRefPtr<CefFrame> frame0b, frame1b, frame2b, frame3b;
+  int64 frame0id, frame1id, frame2id, frame3id;
+  std::string frame0url, frame1url, frame2url, frame3url;
+
+  // Verify the GetFrameNames result.
+  std::set<std::string> expected_names = {kFrame0Name, kFrame1Name, kFrame2Name,
+                                          kFrame3Name};
+
+  std::vector<CefString> names;
+  browser->GetFrameNames(names);
+  V_EXPECT_TRUE(names.size() == expected_names.size())
+      << "expected: " << expected_names.size() << " actual: " << names.size();
+
+  for (const auto& name : names) {
+    const std::string& nameStr = name;
+    auto it = expected_names.find(nameStr);
+    V_EXPECT_FALSE(it == expected_names.end())
+        << "Unexpected name: \"" << nameStr << "\"";
+    if (it != expected_names.end())
+      expected_names.erase(it);
+  }
+
+  for (const auto& name : expected_names) {
+    V_EXPECT_FALSE(true) << "Missing name: \"" << name << "\"";
+  }
+
+  // Find frames by name.
+  frame0 = browser->GetFrame(kFrame0Name);
+  V_EXPECT_TRUE(frame0.get());
+  frame1 = browser->GetFrame(kFrame1Name);
+  V_EXPECT_TRUE(frame1.get());
+  frame2 = browser->GetFrame(kFrame2Name);
+  V_EXPECT_TRUE(frame2.get());
+  frame3 = browser->GetFrame(kFrame3Name);
+  V_EXPECT_TRUE(frame3.get());
+
+  if (!frame0 || !frame1 || !frame2 || !frame3) {
+    V_RETURN();
+  }
+
+  // Verify that the name matches.
+  V_EXPECT_TRUE(frame0->GetName().ToString() == kFrame0Name)
+      << "expected: " << kFrame0Name
+      << " actual: " << frame0->GetName().ToString();
+  V_EXPECT_TRUE(frame1->GetName().ToString() == kFrame1Name)
+      << "expected: " << kFrame1Name
+      << " actual: " << frame1->GetName().ToString();
+  V_EXPECT_TRUE(frame2->GetName().ToString() == kFrame2Name)
+      << "expected: " << kFrame2Name
+      << " actual: " << frame2->GetName().ToString();
+  V_EXPECT_TRUE(frame3->GetName().ToString() == kFrame3Name)
+      << "expected: " << kFrame3Name
+      << " actual: " << frame3->GetName().ToString();
+
+  // Verify that the URL matches.
+  frame0url = GetMultiNavURL(origin, 0);
+  V_EXPECT_TRUE(frame0->GetURL() == frame0url)
+      << "expected: " << frame0url
+      << " actual: " << frame0->GetURL().ToString();
+  frame1url = GetMultiNavURL(origin, 1);
+  V_EXPECT_TRUE(frame1->GetURL() == frame1url)
+      << "expected: " << frame1url
+      << " actual: " << frame1->GetURL().ToString();
+  frame2url = GetMultiNavURL(origin, 2);
+  V_EXPECT_TRUE(frame2->GetURL() == frame2url)
+      << "expected: " << frame2url
+      << " actual: " << frame2->GetURL().ToString();
+  frame3url = GetMultiNavURL(origin, 3);
+  V_EXPECT_TRUE(frame3->GetURL() == frame3url)
+      << "expected: " << frame3url
+      << " actual: " << frame3->GetURL().ToString();
+
+  // Verify that the frame id is valid.
+  frame0id = frame0->GetIdentifier();
+  V_EXPECT_TRUE(frame0id > 0) << "actual: " << frame0id;
+  frame1id = frame1->GetIdentifier();
+  V_EXPECT_TRUE(frame1id > 0) << "actual: " << frame1id;
+  frame2id = frame2->GetIdentifier();
+  V_EXPECT_TRUE(frame2id > 0) << "actual: " << frame2id;
+  frame3id = frame3->GetIdentifier();
+  V_EXPECT_TRUE(frame3id > 0) << "actual: " << frame3id;
+
+  // Verify that the current frame has the correct id.
+  if (frame_number == 0) {
+    V_EXPECT_TRUE(frame->GetIdentifier() == frame0id)
+        << "expected: " << frame0id << " actual: " << frame->GetIdentifier();
+  } else if (frame_number == 1) {
+    V_EXPECT_TRUE(frame->GetIdentifier() == frame1id)
+        << "expected: " << frame1id << " actual: " << frame->GetIdentifier();
+  } else if (frame_number == 2) {
+    V_EXPECT_TRUE(frame->GetIdentifier() == frame2id)
+        << "expected: " << frame2id << " actual: " << frame->GetIdentifier();
+  } else if (frame_number == 3) {
+    V_EXPECT_TRUE(frame->GetIdentifier() == frame3id)
+        << "expected: " << frame3id << " actual: " << frame->GetIdentifier();
+  }
+
+  // Find frames by id.
+  frame0b = browser->GetFrame(frame0->GetIdentifier());
+  V_EXPECT_TRUE(frame0b.get());
+  frame1b = browser->GetFrame(frame1->GetIdentifier());
+  V_EXPECT_TRUE(frame1b.get());
+  frame2b = browser->GetFrame(frame2->GetIdentifier());
+  V_EXPECT_TRUE(frame2b.get());
+  frame3b = browser->GetFrame(frame3->GetIdentifier());
+  V_EXPECT_TRUE(frame3b.get());
+
+  if (!frame0b || !frame1b || !frame2b || !frame3b) {
+    V_RETURN();
+  }
+
+  // Verify that the id matches.
+  V_EXPECT_TRUE(frame0b->GetIdentifier() == frame0id)
+      << "expected: " << frame0id << " actual: " << frame0b->GetIdentifier();
+  V_EXPECT_TRUE(frame1b->GetIdentifier() == frame1id)
+      << "expected: " << frame1id << " actual: " << frame1b->GetIdentifier();
+  V_EXPECT_TRUE(frame2b->GetIdentifier() == frame2id)
+      << "expected: " << frame2id << " actual: " << frame2b->GetIdentifier();
+  V_EXPECT_TRUE(frame3b->GetIdentifier() == frame3id)
+      << "expected: " << frame3id << " actual: " << frame3b->GetIdentifier();
+
+  size_t frame_count = browser->GetFrameCount();
+  V_EXPECT_TRUE(frame_count == 4U) << " actual: " << frame_count;
+
+  // Verify the GetFrameIdentifiers result.
+  std::set<int64> expected_idents = {frame0id, frame1id, frame2id, frame3id};
+
+  std::vector<int64> idents;
+  browser->GetFrameIdentifiers(idents);
+  V_EXPECT_TRUE(idents.size() == expected_idents.size())
+      << "expected: " << expected_idents.size() << " actual: " << idents.size();
+
+  for (const auto& ident : idents) {
+    auto it = expected_idents.find(ident);
+    V_EXPECT_FALSE(it == expected_idents.end()) << "Unexpected id: " << ident;
+    if (it != expected_idents.end())
+      expected_idents.erase(it);
+  }
+
+  for (const auto& ident : expected_idents) {
+    V_EXPECT_FALSE(true) << "Missing id: " << ident;
+  }
+
+  // Verify parent hierarchy.
+  V_EXPECT_FALSE(frame0->GetParent().get());
+  V_EXPECT_TRUE(frame1->GetParent()->GetIdentifier() == frame0id)
+      << "expected: " << frame0id
+      << " actual: " << frame1->GetParent()->GetIdentifier();
+  V_EXPECT_TRUE(frame2->GetParent()->GetIdentifier() == frame1id)
+      << "expected: " << frame1id
+      << " actual: " << frame2->GetParent()->GetIdentifier();
+  V_EXPECT_TRUE(frame3->GetParent()->GetIdentifier() == frame2id)
+      << "expected: " << frame2id
+      << " actual: " << frame3->GetParent()->GetIdentifier();
+
+  V_RETURN();
+}
+
+// Test that nested iframes work.
+class FrameNavExpectationsBrowserTestNestedIframes
+    : public FrameNavExpectationsBrowserMultiNav {
+ public:
+  typedef FrameNavExpectationsBrowserMultiNav parent;
+
+  FrameNavExpectationsBrowserTestNestedIframes(int nav, bool same_origin)
+      : parent(nav), same_origin_(same_origin) {
+    // In the browser process we can rely on the |nav| value to determine the
+    // origin.
+    if (same_origin) {
+      origin_ = kFrameNavOrigin0;
+    } else {
+      switch (nav) {
+        case 0:
+          origin_ = kFrameNavOrigin0;
+          break;
+        case 1:
+          origin_ = kFrameNavOrigin1;
+          break;
+        case 2:
+          origin_ = kFrameNavOrigin2;
+          break;
+        case 3:
+          origin_ = kFrameNavOrigin3;
+          break;
+        default:
+          EXPECT_TRUE(false);  // Not reached.
+          break;
+      }
+    }
+  }
+
+  std::string GetMainURL() override {
+    // Load the first (main) frame.
+    return GetMultiNavURL(origin_, 0);
+  }
+
+  std::string GetContentForURL(const std::string& url) override {
+    const int frame_number = GetNavFromMultiNavURL(url);
+    switch (frame_number) {
+      case 0:
+        // Frame 0. Contains a named iframe.
+        return "<html><body>Nav1<iframe src=\"" + GetMultiNavURL(origin_, 1) +
+               "\" name=\"nav2\"></body></html>";
+      case 1:
+        // Frame 1. Contains an unnamed iframe.
+        return "<html><body>Nav2<iframe src=\"" + GetMultiNavURL(origin_, 2) +
+               "\"></body></html>";
+      case 2: {
+        // Frame 2. Contains an named iframe created via javascript.
+        std::stringstream ss;
+        ss << "<html><script>"
+           << "  function createFrame() {"
+           << "    var f = document.createElement('iframe');"
+           << "    f.name = 'nav3';"
+           << "    f.src = '" << GetMultiNavURL(origin_, 3) << "';"
+           << "    document.body.appendChild(f);"
+           << "  }</script><body onload=\"createFrame()\">Nav3</body></html>";
+        return ss.str();
+      }
+      case 3:
+        // Frame 3.
+        return "<html><body>Nav4</body></html>";
+      default:
+        EXPECT_TRUE(false);  // Not reached.
+        return "";
+    }
+  }
+
+  bool IsNavigationDone() const override {
+    return got_load_state_change_done_ && got_renderer_complete_ &&
+           got_load_end_[0] && got_load_end_[1] && got_load_end_[2] &&
+           got_load_end_[3];
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      const std::string& url) override {
+    V_DECLARE();
+    V_EXPECT_TRUE(frame.get());
+    const int frame_number = GetNavFromMultiNavURL(url);
+    if (frame_number == 0) {
+      // Main frame.
+      V_EXPECT_TRUE(frame->IsMain());
+    } else {
+      // Sub frame.
+      V_EXPECT_FALSE(frame->IsMain());
+    }
+    V_EXPECT_TRUE(parent::OnBeforeBrowse(browser, frame, url));
+    V_RETURN();
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    V_DECLARE();
+    V_EXPECT_FALSE(got_load_state_change_done_);
+
+    if (!isLoading) {
+      got_load_state_change_done_.yes();
+    }
+
+    V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading));
+    V_RETURN();
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    const int frame_number = GetNavFromMultiNavURL(frame->GetURL());
+
+    V_DECLARE();
+    V_EXPECT_FALSE(got_load_start_[frame_number]);
+    V_EXPECT_FALSE(got_load_end_[frame_number]);
+
+    // Notification should be received for parent frame before child frame.
+    if (frame_number == 0) {
+      V_EXPECT_FALSE(got_load_start_[1]);
+      V_EXPECT_FALSE(got_load_start_[2]);
+      V_EXPECT_FALSE(got_load_start_[3]);
+    } else if (frame_number == 1) {
+      V_EXPECT_TRUE(got_load_start_[0]);
+      V_EXPECT_FALSE(got_load_start_[2]);
+      V_EXPECT_FALSE(got_load_start_[3]);
+    } else if (frame_number == 2) {
+      V_EXPECT_TRUE(got_load_start_[0]);
+      V_EXPECT_TRUE(got_load_start_[1]);
+      V_EXPECT_FALSE(got_load_start_[3]);
+    } else if (frame_number == 3) {
+      V_EXPECT_TRUE(got_load_start_[0]);
+      V_EXPECT_TRUE(got_load_start_[1]);
+      V_EXPECT_TRUE(got_load_start_[2]);
+    } else {
+      V_EXPECT_TRUE(false);  // Not reached.
+    }
+
+    got_load_start_[frame_number].yes();
+
+    V_EXPECT_TRUE(parent::OnLoadStart(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    const int frame_number = GetNavFromMultiNavURL(frame->GetURL());
+
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_start_[frame_number]);
+    V_EXPECT_FALSE(got_load_end_[frame_number]);
+
+    // Notification should be received for child frame before parent frame.
+    if (frame_number == 0) {
+      V_EXPECT_TRUE(got_load_end_[1]);
+      V_EXPECT_TRUE(got_load_end_[2]);
+      V_EXPECT_TRUE(got_load_end_[3]);
+    } else if (frame_number == 1) {
+      V_EXPECT_FALSE(got_load_end_[0]);
+      V_EXPECT_TRUE(got_load_end_[2]);
+      V_EXPECT_TRUE(got_load_end_[3]);
+    } else if (frame_number == 2) {
+      V_EXPECT_FALSE(got_load_end_[0]);
+      V_EXPECT_FALSE(got_load_end_[1]);
+      V_EXPECT_TRUE(got_load_end_[3]);
+    } else if (frame_number == 3) {
+      V_EXPECT_FALSE(got_load_end_[0]);
+      V_EXPECT_FALSE(got_load_end_[1]);
+      V_EXPECT_FALSE(got_load_end_[2]);
+    } else {
+      V_EXPECT_TRUE(false);  // Not reached.
+    }
+
+    V_EXPECT_TRUE(VerifyBrowserIframe(browser, frame, origin_, frame_number))
+        << "frame_number = " << frame_number;
+
+    got_load_end_[frame_number].yes();
+
+    V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnRendererComplete(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          int renderer_nav,
+                          bool renderer_result) override {
+    V_DECLARE();
+    V_EXPECT_FALSE(got_renderer_complete_);
+    if (same_origin_) {
+      V_EXPECT_TRUE(renderer_nav == nav());
+    } else {
+      // Because each navigation is in a new renderer process.
+      V_EXPECT_TRUE(renderer_nav == 0);
+    }
+
+    got_renderer_complete_.yes();
+
+    V_EXPECT_TRUE(parent::OnRendererComplete(browser, frame, renderer_nav,
+                                             renderer_result));
+    V_RETURN();
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_state_change_done_);
+    V_EXPECT_TRUE(got_load_start_[0]);
+    V_EXPECT_TRUE(got_load_start_[1]);
+    V_EXPECT_TRUE(got_load_start_[2]);
+    V_EXPECT_TRUE(got_load_start_[3]);
+    V_EXPECT_TRUE(got_load_end_[0]);
+    V_EXPECT_TRUE(got_load_end_[1]);
+    V_EXPECT_TRUE(got_load_end_[2]);
+    V_EXPECT_TRUE(got_load_end_[3]);
+    V_EXPECT_TRUE(got_renderer_complete_);
+    V_EXPECT_TRUE(parent::Finalize());
+    V_RETURN();
+  }
+
+ private:
+  bool same_origin_;
+  std::string origin_;
+
+  TrackCallback got_load_state_change_done_;
+  TrackCallback got_load_start_[4];
+  TrackCallback got_load_end_[4];
+  TrackCallback got_renderer_complete_;
+};
+
+class FrameNavExpectationsRendererTestNestedIframes
+    : public FrameNavExpectationsRendererMultiNav {
+ public:
+  typedef FrameNavExpectationsRendererMultiNav parent;
+
+  FrameNavExpectationsRendererTestNestedIframes(int nav, bool same_origin)
+      : parent(nav) {
+    if (same_origin)
+      origin_ = kFrameNavOrigin0;
+  }
+
+  bool IsNavigationDone() const override {
+    return got_load_state_change_done_ && got_load_end_[0] &&
+           got_load_end_[1] && got_load_end_[2] && got_load_end_[3];
+  }
+
+  bool OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading) override {
+    V_DECLARE();
+    V_EXPECT_FALSE(got_load_state_change_done_);
+
+    if (!isLoading) {
+      got_load_state_change_done_.yes();
+    }
+
+    V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading));
+    V_RETURN();
+  }
+
+  bool OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame) override {
+    if (origin_.empty()) {
+      // When navigating different origins we can't rely on the nav() value
+      // because each navigation creates a new renderer process. Get the origin
+      // by parsing the URL instead.
+      origin_ = GetOriginFromMultiNavURL(browser->GetMainFrame()->GetURL());
+    }
+
+    const int frame_number = GetNavFromMultiNavURL(frame->GetURL());
+
+    V_DECLARE();
+    V_EXPECT_FALSE(got_load_start_[frame_number]);
+    V_EXPECT_FALSE(got_load_end_[frame_number]);
+
+    // Notification should be received for parent frame before child frame.
+    if (frame_number == 0) {
+      V_EXPECT_FALSE(got_load_start_[1]);
+      V_EXPECT_FALSE(got_load_start_[2]);
+      V_EXPECT_FALSE(got_load_start_[3]);
+    } else if (frame_number == 1) {
+      V_EXPECT_TRUE(got_load_start_[0]);
+      V_EXPECT_FALSE(got_load_start_[2]);
+      V_EXPECT_FALSE(got_load_start_[3]);
+    } else if (frame_number == 2) {
+      V_EXPECT_TRUE(got_load_start_[0]);
+      V_EXPECT_TRUE(got_load_start_[1]);
+      V_EXPECT_FALSE(got_load_start_[3]);
+    } else if (frame_number == 3) {
+      V_EXPECT_TRUE(got_load_start_[0]);
+      V_EXPECT_TRUE(got_load_start_[1]);
+      V_EXPECT_TRUE(got_load_start_[2]);
+    }
+
+    got_load_start_[frame_number].yes();
+
+    V_EXPECT_TRUE(parent::OnLoadStart(browser, frame));
+    V_RETURN();
+  }
+
+  bool OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame) override {
+    const int frame_number = GetNavFromMultiNavURL(frame->GetURL());
+
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_start_[frame_number]);
+    V_EXPECT_FALSE(got_load_end_[frame_number]);
+
+    // Notification should be received for child frame before parent frame.
+    if (frame_number == 0) {
+      V_EXPECT_TRUE(got_load_end_[1]);
+      V_EXPECT_TRUE(got_load_end_[2]);
+      V_EXPECT_TRUE(got_load_end_[3]);
+    } else if (frame_number == 1) {
+      V_EXPECT_FALSE(got_load_end_[0]);
+      V_EXPECT_TRUE(got_load_end_[2]);
+      V_EXPECT_TRUE(got_load_end_[3]);
+    } else if (frame_number == 2) {
+      V_EXPECT_FALSE(got_load_end_[0]);
+      V_EXPECT_FALSE(got_load_end_[1]);
+      V_EXPECT_TRUE(got_load_end_[3]);
+    } else if (frame_number == 3) {
+      V_EXPECT_FALSE(got_load_end_[0]);
+      V_EXPECT_FALSE(got_load_end_[1]);
+      V_EXPECT_FALSE(got_load_end_[2]);
+    }
+
+    V_EXPECT_TRUE(VerifyBrowserIframe(browser, frame, origin_, frame_number))
+        << "frame_number = " << frame_number;
+
+    got_load_end_[frame_number].yes();
+
+    V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame));
+    V_RETURN();
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(got_load_state_change_done_);
+    V_EXPECT_TRUE(got_load_start_[0]);
+    V_EXPECT_TRUE(got_load_start_[1]);
+    V_EXPECT_TRUE(got_load_start_[2]);
+    V_EXPECT_TRUE(got_load_start_[3]);
+    V_EXPECT_TRUE(got_load_end_[0]);
+    V_EXPECT_TRUE(got_load_end_[1]);
+    V_EXPECT_TRUE(got_load_end_[2]);
+    V_EXPECT_TRUE(got_load_end_[3]);
+    V_EXPECT_TRUE(parent::Finalize());
+    V_RETURN();
+  }
+
+ private:
+  std::string origin_;
+
+  TrackCallback got_load_state_change_done_;
+  TrackCallback got_load_start_[4];
+  TrackCallback got_load_end_[4];
+};
+
+class FrameNavExpectationsFactoryBrowserTestNestedIframesSameOrigin
+    : public FrameNavExpectationsFactoryBrowser {
+ public:
+  FrameNavExpectationsFactoryBrowserTestNestedIframesSameOrigin()
+      : create_count_(0) {}
+
+  FrameNavFactoryId GetID() const override {
+    return FNF_ID_NESTED_IFRAMES_SAME_ORIGIN;
+  }
+
+  bool HasMoreNavigations() const override {
+    return (create_count_ < kMaxMultiNavNavigations);
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(create_count_ == kMaxMultiNavNavigations);
+    V_RETURN();
+  }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsBrowser> Create(int nav) override {
+    create_count_++;
+    return scoped_ptr<FrameNavExpectationsBrowser>(
+        new FrameNavExpectationsBrowserTestNestedIframes(nav, true));
+  }
+
+ private:
+  int create_count_;
+};
+
+class FrameNavExpectationsFactoryRendererTestNestedIframesSameOrigin
+    : public FrameNavExpectationsFactoryRenderer {
+ public:
+  FrameNavExpectationsFactoryRendererTestNestedIframesSameOrigin() {}
+
+  FrameNavFactoryId GetID() const override {
+    return FNF_ID_NESTED_IFRAMES_SAME_ORIGIN;
+  }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsRenderer> Create(int nav) override {
+    return scoped_ptr<FrameNavExpectationsRenderer>(
+        new FrameNavExpectationsRendererTestNestedIframes(nav, true));
+  }
+};
+
+}  // namespace
+
+// Test that nested iframes work.
+FRAME_TEST(NestedIframesSameOrigin, FNF_ID_NESTED_IFRAMES_SAME_ORIGIN)
+
+namespace {
+
+class FrameNavExpectationsFactoryBrowserTestNestedIframesDiffOrigin
+    : public FrameNavExpectationsFactoryBrowser {
+ public:
+  FrameNavExpectationsFactoryBrowserTestNestedIframesDiffOrigin()
+      : create_count_(0) {}
+
+  FrameNavFactoryId GetID() const override {
+    return FNF_ID_NESTED_IFRAMES_DIFF_ORIGIN;
+  }
+
+  bool HasMoreNavigations() const override {
+    return (create_count_ < kMaxMultiNavNavigations);
+  }
+
+  bool Finalize() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(create_count_ == kMaxMultiNavNavigations);
+    V_RETURN();
+  }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsBrowser> Create(int nav) override {
+    create_count_++;
+    return scoped_ptr<FrameNavExpectationsBrowser>(
+        new FrameNavExpectationsBrowserTestNestedIframes(nav, false));
+  }
+
+ private:
+  int create_count_;
+};
+
+class FrameNavExpectationsFactoryRendererTestNestedIframesDiffOrigin
+    : public FrameNavExpectationsFactoryRenderer {
+ public:
+  FrameNavExpectationsFactoryRendererTestNestedIframesDiffOrigin() {}
+
+  FrameNavFactoryId GetID() const override {
+    return FNF_ID_NESTED_IFRAMES_DIFF_ORIGIN;
+  }
+
+ protected:
+  scoped_ptr<FrameNavExpectationsRenderer> Create(int nav) override {
+    return scoped_ptr<FrameNavExpectationsRenderer>(
+        new FrameNavExpectationsRendererTestNestedIframes(nav, false));
+  }
+};
+
+}  // namespace
+
+// Test that nested iframes work.
+FRAME_TEST(NestedIframesDiffOrigin, FNF_ID_NESTED_IFRAMES_DIFF_ORIGIN)
+
+namespace {
+
+// Returns a new factory in the browser or renderer process. All factory types
+// must be listed here.
+
+// static
+scoped_ptr<FrameNavExpectationsFactoryBrowser>
+FrameNavExpectationsFactoryBrowser::FromID(FrameNavFactoryId id) {
+  scoped_ptr<FrameNavExpectationsFactoryBrowser> factory;
+  switch (id) {
+    case FNF_ID_SINGLE_NAV_HARNESS:
+      factory.reset(new FrameNavExpectationsFactoryBrowserTestSingleNavHarness);
+      break;
+    case FNF_ID_SINGLE_NAV:
+      factory.reset(new FrameNavExpectationsFactoryBrowserTestSingleNav);
+      break;
+    case FNF_ID_MULTI_NAV_HARNESS:
+      factory.reset(new FrameNavExpectationsFactoryBrowserTestMultiNavHarness);
+      break;
+    case FNF_ID_MULTI_NAV:
+      factory.reset(new FrameNavExpectationsFactoryBrowserTestMultiNav);
+      break;
+    case FNF_ID_NESTED_IFRAMES_SAME_ORIGIN:
+      factory.reset(
+          new FrameNavExpectationsFactoryBrowserTestNestedIframesSameOrigin);
+      break;
+    case FNF_ID_NESTED_IFRAMES_DIFF_ORIGIN:
+      factory.reset(
+          new FrameNavExpectationsFactoryBrowserTestNestedIframesDiffOrigin);
+      break;
+    default:
+      break;
+  }
+  EXPECT_TRUE(factory);
+  EXPECT_EQ(id, factory->GetID());
+  return factory.Pass();
+}
+
+// static
+scoped_ptr<FrameNavExpectationsFactoryRenderer>
+FrameNavExpectationsFactoryRenderer::FromID(FrameNavFactoryId id) {
+  scoped_ptr<FrameNavExpectationsFactoryRenderer> factory;
+  switch (id) {
+    case FNF_ID_SINGLE_NAV_HARNESS:
+      factory.reset(
+          new FrameNavExpectationsFactoryRendererTestSingleNavHarness);
+      break;
+    case FNF_ID_SINGLE_NAV:
+      factory.reset(new FrameNavExpectationsFactoryRendererTestSingleNav);
+      break;
+    case FNF_ID_MULTI_NAV_HARNESS:
+      factory.reset(new FrameNavExpectationsFactoryRendererTestMultiNavHarness);
+      break;
+    case FNF_ID_MULTI_NAV:
+      factory.reset(new FrameNavExpectationsFactoryRendererTestMultiNav);
+      break;
+    case FNF_ID_NESTED_IFRAMES_SAME_ORIGIN:
+      factory.reset(
+          new FrameNavExpectationsFactoryRendererTestNestedIframesSameOrigin);
+      break;
+    case FNF_ID_NESTED_IFRAMES_DIFF_ORIGIN:
+      factory.reset(
+          new FrameNavExpectationsFactoryRendererTestNestedIframesDiffOrigin);
+      break;
+    default:
+      break;
+  }
+  EXPECT_TRUE(factory);
+  EXPECT_EQ(id, factory->GetID());
+  return factory.Pass();
+}
+
+}  // namespace
+
+// Entry point for creating frame renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateFrameRendererTests(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new FrameNavRendererTest);
+}
diff --git a/src/tests/ceftests/image_unittest.cc b/src/tests/ceftests/image_unittest.cc
new file mode 100644
index 0000000..dba6a7c
--- /dev/null
+++ b/src/tests/ceftests/image_unittest.cc
@@ -0,0 +1,283 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_image.h"
+#include "tests/ceftests/image_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// The expected image size in device independent pixels (DIPs).
+const int kExpectedDIPSize = 16;
+
+void LoadImage(CefRefPtr<CefImage> image, double scale_factor) {
+  image_util::LoadIconImage(image, scale_factor);
+}
+
+void VerifyScaleEmpty(CefRefPtr<CefImage> image, float scale_factor) {
+  float actual_scale_factor = 0.0f;
+  int pixel_width = 0;
+  int pixel_height = 0;
+
+  EXPECT_FALSE(image->HasRepresentation(scale_factor));
+  EXPECT_FALSE(image->GetRepresentationInfo(scale_factor, actual_scale_factor,
+                                            pixel_width, pixel_height));
+  EXPECT_EQ(0.0f, actual_scale_factor);
+  EXPECT_EQ(0, pixel_width);
+  EXPECT_EQ(0, pixel_height);
+  EXPECT_FALSE(image->RemoveRepresentation(scale_factor));
+}
+
+void VerifyScaleExists(CefRefPtr<CefImage> image,
+                       float scale_factor,
+                       float expected_scale_factor) {
+  float actual_scale_factor = 0.0f;
+  int pixel_width = 0;
+  int pixel_height = 0;
+  int expected_pixel_size = kExpectedDIPSize * expected_scale_factor;
+
+  // Only returns true for exact matches.
+  if (scale_factor == expected_scale_factor)
+    EXPECT_TRUE(image->HasRepresentation(scale_factor));
+  else
+    EXPECT_FALSE(image->HasRepresentation(scale_factor));
+
+  // Returns the closest match.
+  EXPECT_TRUE(image->GetRepresentationInfo(scale_factor, actual_scale_factor,
+                                           pixel_width, pixel_height));
+  EXPECT_EQ(expected_scale_factor, actual_scale_factor);
+  EXPECT_EQ(expected_pixel_size, pixel_width);
+  EXPECT_EQ(expected_pixel_size, pixel_height);
+
+  // Only returns true for exact matches.
+  if (scale_factor == expected_scale_factor) {
+    EXPECT_TRUE(image->RemoveRepresentation(scale_factor));
+    EXPECT_FALSE(image->HasRepresentation(scale_factor));
+  } else {
+    EXPECT_FALSE(image->RemoveRepresentation(scale_factor));
+  }
+}
+
+void VerifySaveAsBitmap(CefRefPtr<CefImage> image,
+                        float scale_factor,
+                        float expected_scale_factor) {
+  int pixel_width = 0;
+  int pixel_height = 0;
+  int expected_pixel_size = kExpectedDIPSize * expected_scale_factor;
+  size_t expected_data_size = expected_pixel_size * expected_pixel_size * 4U;
+
+  CefRefPtr<CefBinaryValue> value = image->GetAsBitmap(
+      scale_factor, CEF_COLOR_TYPE_RGBA_8888, CEF_ALPHA_TYPE_PREMULTIPLIED,
+      pixel_width, pixel_height);
+  EXPECT_TRUE(value.get());
+  size_t data_size = value->GetSize();
+  EXPECT_EQ(expected_data_size, data_size);
+  EXPECT_EQ(expected_pixel_size, pixel_width);
+  EXPECT_EQ(expected_pixel_size, pixel_height);
+
+  std::vector<unsigned char> data(data_size);
+  value->GetData(&data[0], data_size, 0U);
+
+  CefRefPtr<CefImage> image2 = CefImage::CreateImage();
+  EXPECT_TRUE(image2.get());
+  EXPECT_TRUE(image2->AddBitmap(expected_scale_factor, pixel_width,
+                                pixel_height, CEF_COLOR_TYPE_RGBA_8888,
+                                CEF_ALPHA_TYPE_PREMULTIPLIED, &data[0],
+                                data_size));
+  VerifyScaleExists(image2, expected_scale_factor, expected_scale_factor);
+}
+
+void VerifySaveAsPNG(CefRefPtr<CefImage> image,
+                     float scale_factor,
+                     float expected_scale_factor) {
+  int pixel_width = 0;
+  int pixel_height = 0;
+  int expected_pixel_size = kExpectedDIPSize * expected_scale_factor;
+
+  CefRefPtr<CefBinaryValue> value =
+      image->GetAsPNG(scale_factor, true, pixel_width, pixel_height);
+  EXPECT_TRUE(value.get());
+  size_t data_size = value->GetSize();
+  EXPECT_GT(data_size, 0U);
+  EXPECT_EQ(expected_pixel_size, pixel_width);
+  EXPECT_EQ(expected_pixel_size, pixel_height);
+
+  std::vector<unsigned char> data(data_size);
+  value->GetData(&data[0], data_size, 0U);
+
+  CefRefPtr<CefImage> image2 = CefImage::CreateImage();
+  EXPECT_TRUE(image2.get());
+  EXPECT_TRUE(image2->AddPNG(expected_scale_factor, &data[0], data_size));
+  VerifyScaleExists(image2, expected_scale_factor, expected_scale_factor);
+}
+
+void VerifySaveAsJPEG(CefRefPtr<CefImage> image,
+                      float scale_factor,
+                      float expected_scale_factor) {
+  int pixel_width = 0;
+  int pixel_height = 0;
+  int expected_pixel_size = kExpectedDIPSize * expected_scale_factor;
+
+  CefRefPtr<CefBinaryValue> value =
+      image->GetAsJPEG(scale_factor, 80, pixel_width, pixel_height);
+  EXPECT_TRUE(value.get());
+  size_t data_size = value->GetSize();
+  EXPECT_GT(data_size, 0U);
+  EXPECT_EQ(expected_pixel_size, pixel_width);
+  EXPECT_EQ(expected_pixel_size, pixel_height);
+
+  std::vector<unsigned char> data(data_size);
+  value->GetData(&data[0], data_size, 0U);
+
+  CefRefPtr<CefImage> image2 = CefImage::CreateImage();
+  EXPECT_TRUE(image2.get());
+  EXPECT_TRUE(image2->AddJPEG(expected_scale_factor, &data[0], data_size));
+  VerifyScaleExists(image2, expected_scale_factor, expected_scale_factor);
+}
+
+}  // namespace
+
+TEST(ImageTest, Empty) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  // An image is the same as itself.
+  EXPECT_TRUE(image->IsSame(image));
+
+  EXPECT_TRUE(image->IsEmpty());
+  EXPECT_EQ(0U, image->GetWidth());
+  EXPECT_EQ(0U, image->GetHeight());
+
+  // Empty images are the same.
+  CefRefPtr<CefImage> image2 = CefImage::CreateImage();
+  EXPECT_TRUE(image->IsSame(image2));
+  EXPECT_TRUE(image2->IsSame(image));
+
+  // 1x scale does not exist.
+  VerifyScaleEmpty(image, 1.0f);
+
+  // 2x scale does not exist.
+  VerifyScaleEmpty(image, 2.0f);
+}
+
+TEST(ImageTest, Scale1x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 1.0f);
+
+  // 1x scale should exist.
+  VerifyScaleExists(image, 1.0f, 1.0f);
+
+  // 2x scale should not exist.
+  VerifyScaleEmpty(image, 2.0f);
+}
+
+TEST(ImageTest, Scale2x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 2.0f);
+
+  // 1x scale should return the 2x image.
+  VerifyScaleExists(image, 1.0f, 2.0f);
+
+  // 2x scale should exist.
+  VerifyScaleExists(image, 2.0f, 2.0f);
+}
+
+TEST(ImageTest, ScaleMulti) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 1.0f);
+  LoadImage(image, 2.0f);
+
+  // 1x scale should exist.
+  VerifyScaleExists(image, 1.0f, 1.0f);
+
+  // 2x scale should exist.
+  VerifyScaleExists(image, 2.0f, 2.0f);
+}
+
+TEST(ImageTest, SaveBitmap1x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 1.0f);
+
+  VerifySaveAsBitmap(image, 1.0f, 1.0f);
+}
+
+TEST(ImageTest, SaveBitmap2x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 2.0f);
+
+  VerifySaveAsBitmap(image, 2.0f, 2.0f);
+}
+
+TEST(ImageTest, SaveBitmapMulti) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 2.0f);
+
+  VerifySaveAsBitmap(image, 1.0f, 2.0f);
+}
+
+TEST(ImageTest, SavePNG1x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 1.0f);
+
+  VerifySaveAsPNG(image, 1.0f, 1.0f);
+}
+
+TEST(ImageTest, SavePNG2x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 2.0f);
+
+  VerifySaveAsPNG(image, 2.0f, 2.0f);
+}
+
+TEST(ImageTest, SavePNGMulti) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 2.0f);
+
+  VerifySaveAsPNG(image, 1.0f, 2.0f);
+}
+
+TEST(ImageTest, SaveJPEG1x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 1.0f);
+
+  VerifySaveAsJPEG(image, 1.0f, 1.0f);
+}
+
+TEST(ImageTest, SaveJPEG2x) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 2.0f);
+
+  VerifySaveAsJPEG(image, 2.0f, 2.0f);
+}
+
+TEST(ImageTest, SaveJPEGMulti) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  EXPECT_TRUE(image.get());
+
+  LoadImage(image, 2.0f);
+
+  VerifySaveAsJPEG(image, 1.0f, 2.0f);
+}
diff --git a/src/tests/ceftests/image_util.cc b/src/tests/ceftests/image_util.cc
new file mode 100644
index 0000000..29b7618
--- /dev/null
+++ b/src/tests/ceftests/image_util.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/image_util.h"
+
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/resource_util.h"
+
+namespace image_util {
+
+void LoadImage(CefRefPtr<CefImage> image,
+               double scale_factor,
+               const std::string& name,
+               const CefSize& expected_size) {
+  std::string image_str;
+
+  std::string name_str;
+  if (scale_factor == 1.0f)
+    name_str = name + ".1x.png";
+  else if (scale_factor == 2.0f)
+    name_str = name + ".2x.png";
+
+  EXPECT_TRUE(client::LoadBinaryResource(name_str.c_str(), image_str));
+  EXPECT_TRUE(image->AddPNG(scale_factor, image_str.c_str(), image_str.size()));
+
+  EXPECT_FALSE(image->IsEmpty());
+  EXPECT_EQ(expected_size.width, static_cast<int>(image->GetWidth()));
+  EXPECT_EQ(expected_size.height, static_cast<int>(image->GetHeight()));
+}
+
+void LoadIconImage(CefRefPtr<CefImage> image,
+                   double scale_factor,
+                   const std::string& name) {
+  LoadImage(image, scale_factor, name, CefSize(16, 16));
+}
+
+}  // namespace image_util
diff --git a/src/tests/ceftests/image_util.h b/src/tests/ceftests/image_util.h
new file mode 100644
index 0000000..709e764
--- /dev/null
+++ b/src/tests/ceftests/image_util.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_IMAGE_UTIL_H_
+#define CEF_TESTS_UNITTESTS_IMAGE_UTIL_H_
+#pragma once
+
+#include "include/cef_image.h"
+
+namespace image_util {
+
+// Load an PNG image. Tests that the size is |expected_size| in DIPs. Call
+// multiple times to load the same image at different scale factors.
+void LoadImage(CefRefPtr<CefImage> image,
+               double scale_factor,
+               const std::string& name,
+               const CefSize& expected_size);
+
+// Load an icon image. Expected size is 16x16 DIPs.
+void LoadIconImage(CefRefPtr<CefImage> image,
+                   double scale_factor,
+                   const std::string& name = "window_icon");
+
+}  // namespace image_util
+
+#endif  // CEF_TESTS_UNITTESTS_IMAGE_UTIL_H_
diff --git a/src/tests/ceftests/jsdialog_unittest.cc b/src/tests/ceftests/jsdialog_unittest.cc
new file mode 100644
index 0000000..ae645a1
--- /dev/null
+++ b/src/tests/ceftests/jsdialog_unittest.cc
@@ -0,0 +1,449 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/test/cef_test_helpers.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char* kStartUrl = "http://tests/JSDialogTestHandler.Start";
+const char* kEndUrl = "http://tests/JSDialogTestHandler.End?r=";
+
+class JSDialogTestHandler : public TestHandler {
+ public:
+  enum TestType {
+    TYPE_ALERT,
+    TYPE_CONFIRM,
+    TYPE_PROMPT,
+    TYPE_ONBEFOREUNLOAD,
+  };
+  enum TestMode {
+    MODE_SUPPRESS,
+    MODE_RUN_IMMEDIATE,
+    MODE_RUN_DELAYED,
+  };
+
+  JSDialogTestHandler(TestType type,
+                      TestMode mode,
+                      bool success,
+                      const std::string& user_input,
+                      const std::string& result)
+      : type_(type),
+        mode_(mode),
+        success_(success),
+        user_input_(user_input),
+        result_(result) {}
+
+  void RunTest() override {
+    std::string content = "<html><head><body>START<script>";
+    if (type_ == TYPE_ALERT) {
+      content +=
+          "alert('My alert message'); "
+          "document.location='" +
+          std::string(kEndUrl) + "';";
+    } else if (type_ == TYPE_CONFIRM) {
+      content +=
+          "var r = confirm('My confirm message')?'ok':'cancel'; "
+          "document.location='" +
+          std::string(kEndUrl) + "'+r;";
+    } else if (type_ == TYPE_PROMPT) {
+      content +=
+          "var r = prompt('My prompt message','my default'); "
+          "document.location='" +
+          std::string(kEndUrl) + "'+r;";
+    } else if (type_ == TYPE_ONBEFOREUNLOAD) {
+      content +=
+          "window.onbeforeunload=function() {"
+          " return 'My unload message'; };";
+    }
+    content += "</script></body></html>";
+
+    AddResource(kStartUrl, content, "text/html");
+    AddResource(kEndUrl, "<html><body>END</body></html>", "text/html");
+
+    // Create the browser
+    CreateBrowser(kStartUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (!frame->IsMain())
+      return;
+
+    std::string url = frame->GetURL();
+    if (url.find(kEndUrl) == 0) {
+      got_onloadend_.yes();
+
+      std::string result = url.substr(strlen(kEndUrl));
+      EXPECT_STREQ(result_.c_str(), result.c_str());
+
+      DestroyTest();
+    } else if (type_ == TYPE_ONBEFOREUNLOAD) {
+      // Send the page a user gesture to enable firing of the onbefureunload
+      // handler. See https://crbug.com/707007.
+      CefExecuteJavaScriptWithUserGestureForTests(frame, CefString());
+
+      // Trigger the onunload handler.
+      frame->LoadURL(kEndUrl);
+    }
+  }
+
+  void Continue(CefRefPtr<CefJSDialogCallback> callback) {
+    callback->Continue(success_, user_input_);
+  }
+
+  bool OnJSDialog(CefRefPtr<CefBrowser> browser,
+                  const CefString& origin_url,
+                  JSDialogType dialog_type,
+                  const CefString& message_text,
+                  const CefString& default_prompt_text,
+                  CefRefPtr<CefJSDialogCallback> callback,
+                  bool& suppress_message) override {
+    got_onjsdialog_.yes();
+
+    EXPECT_STREQ(kStartUrl, origin_url.ToString().c_str());
+
+    if (type_ == TYPE_ALERT) {
+      EXPECT_EQ(JSDIALOGTYPE_ALERT, dialog_type);
+      EXPECT_STREQ("My alert message", message_text.ToString().c_str());
+      EXPECT_TRUE(default_prompt_text.empty());
+    } else if (type_ == TYPE_CONFIRM) {
+      EXPECT_EQ(JSDIALOGTYPE_CONFIRM, dialog_type);
+      EXPECT_STREQ("My confirm message", message_text.ToString().c_str());
+      EXPECT_TRUE(default_prompt_text.empty());
+    } else if (type_ == TYPE_PROMPT) {
+      EXPECT_EQ(JSDIALOGTYPE_PROMPT, dialog_type);
+      EXPECT_STREQ("My prompt message", message_text.ToString().c_str());
+      EXPECT_STREQ("my default", default_prompt_text.ToString().c_str());
+    }
+
+    EXPECT_FALSE(suppress_message);
+
+    if (mode_ == MODE_SUPPRESS) {
+      // Suppress the dialog.
+      suppress_message = true;
+      return false;
+    } else if (mode_ == MODE_RUN_IMMEDIATE) {
+      // Continue immediately.
+      callback->Continue(success_, user_input_);
+    } else if (mode_ == MODE_RUN_DELAYED) {
+      // Continue asynchronously.
+      CefPostTask(TID_UI,
+                  base::Bind(&JSDialogTestHandler::Continue, this, callback));
+    }
+
+    return true;
+  }
+
+  bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
+                            const CefString& message_text,
+                            bool is_reload,
+                            CefRefPtr<CefJSDialogCallback> callback) override {
+    got_onbeforeunloaddialog_.yes();
+
+    if (type_ == TYPE_ONBEFOREUNLOAD) {
+      // The message is no longer configurable via JavaScript.
+      // See http://crbug.com/587940.
+      EXPECT_STREQ("Is it OK to leave/reload this page?",
+                   message_text.ToString().c_str());
+      EXPECT_FALSE(is_reload);
+    }
+
+    if (mode_ == MODE_RUN_IMMEDIATE) {
+      // Continue immediately.
+      callback->Continue(success_, user_input_);
+    } else if (mode_ == MODE_RUN_DELAYED) {
+      // Continue asynchronously.
+      CefPostTask(TID_UI,
+                  base::Bind(&JSDialogTestHandler::Continue, this, callback));
+    }
+
+    return true;
+  }
+
+  void OnResetDialogState(CefRefPtr<CefBrowser> browser) override {
+    got_onresetdialogstate_.yes();
+  }
+
+  TestType type_;
+  TestMode mode_;
+  bool success_;
+  std::string user_input_;
+  std::string result_;
+
+  TrackCallback got_onjsdialog_;
+  TrackCallback got_onbeforeunloaddialog_;
+  TrackCallback got_onresetdialogstate_;
+  TrackCallback got_onloadend_;
+
+  IMPLEMENT_REFCOUNTING(JSDialogTestHandler);
+};
+
+}  // namespace
+
+// Alert dialog with suppression.
+TEST(JSDialogTest, AlertSuppress) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_ALERT, JSDialogTestHandler::MODE_SUPPRESS,
+      true,  // success
+      "",    // user_input
+      "");   // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Alert dialog with immediate callback.
+TEST(JSDialogTest, AlertRunImmediate) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_ALERT, JSDialogTestHandler::MODE_RUN_IMMEDIATE,
+      true,  // success
+      "",    // user_input
+      "");   // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Alert dialog with delayed callback.
+TEST(JSDialogTest, AlertRunDelayed) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_ALERT, JSDialogTestHandler::MODE_RUN_DELAYED,
+      true,  // success
+      "",    // user_input
+      "");   // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Confirm dialog with suppression.
+TEST(JSDialogTest, ConfirmSuppress) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_CONFIRM, JSDialogTestHandler::MODE_SUPPRESS,
+      true,       // success
+      "",         // user_input
+      "cancel");  // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Confirm dialog run immediately return OK.
+TEST(JSDialogTest, ConfirmRunImmediateOk) {
+  CefRefPtr<JSDialogTestHandler> handler =
+      new JSDialogTestHandler(JSDialogTestHandler::TYPE_CONFIRM,
+                              JSDialogTestHandler::MODE_RUN_IMMEDIATE,
+                              true,   // success
+                              "",     // user_input
+                              "ok");  // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Confirm dialog run immediately return Cancel.
+TEST(JSDialogTest, ConfirmRunImmediateCancel) {
+  CefRefPtr<JSDialogTestHandler> handler =
+      new JSDialogTestHandler(JSDialogTestHandler::TYPE_CONFIRM,
+                              JSDialogTestHandler::MODE_RUN_IMMEDIATE,
+                              false,      // success
+                              "",         // user_input
+                              "cancel");  // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Confirm dialog run delayed return OK.
+TEST(JSDialogTest, ConfirmRunDelayedOk) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_CONFIRM, JSDialogTestHandler::MODE_RUN_DELAYED,
+      true,   // success
+      "",     // user_input
+      "ok");  // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Confirm dialog run delayed return Cancel.
+TEST(JSDialogTest, ConfirmRunDelayedCancel) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_CONFIRM, JSDialogTestHandler::MODE_RUN_DELAYED,
+      false,      // success
+      "",         // user_input
+      "cancel");  // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Prompt dialog with suppression.
+TEST(JSDialogTest, PromptSuppress) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_PROMPT, JSDialogTestHandler::MODE_SUPPRESS,
+      true,          // success
+      "some_value",  // user_input
+      "null");       // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Prompt dialog run immediately return OK.
+TEST(JSDialogTest, PromptRunImmediateOk) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_PROMPT, JSDialogTestHandler::MODE_RUN_IMMEDIATE,
+      true,           // success
+      "some_value",   // user_input
+      "some_value");  // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Prompt dialog run immediately return Cancel.
+TEST(JSDialogTest, PromptRunImmediateCancel) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_PROMPT, JSDialogTestHandler::MODE_RUN_IMMEDIATE,
+      false,         // success
+      "some_value",  // user_input
+      "null");       // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Prompt dialog run delayed return OK.
+TEST(JSDialogTest, PromptRunDelayedOk) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_PROMPT, JSDialogTestHandler::MODE_RUN_DELAYED,
+      true,           // success
+      "some_value",   // user_input
+      "some_value");  // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Prompt dialog run delayed return Cancel.
+TEST(JSDialogTest, PromptRunDelayedCancel) {
+  CefRefPtr<JSDialogTestHandler> handler = new JSDialogTestHandler(
+      JSDialogTestHandler::TYPE_PROMPT, JSDialogTestHandler::MODE_RUN_DELAYED,
+      false,         // success
+      "some_value",  // user_input
+      "null");       // result
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_onjsdialog_);
+  EXPECT_FALSE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// OnBeforeUnload dialog with immediate callback.
+TEST(JSDialogTest, OnBeforeUnloadRunImmediate) {
+  CefRefPtr<JSDialogTestHandler> handler =
+      new JSDialogTestHandler(JSDialogTestHandler::TYPE_ONBEFOREUNLOAD,
+                              JSDialogTestHandler::MODE_RUN_IMMEDIATE,
+                              true,  // success
+                              "",    // user_input
+                              "");   // result
+  handler->ExecuteTest();
+
+  EXPECT_FALSE(handler->got_onjsdialog_);
+  EXPECT_TRUE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// OnBeforeUnload dialog with delayed callback.
+TEST(JSDialogTest, OnBeforeUnloadRunDelayed) {
+  CefRefPtr<JSDialogTestHandler> handler =
+      new JSDialogTestHandler(JSDialogTestHandler::TYPE_ONBEFOREUNLOAD,
+                              JSDialogTestHandler::MODE_RUN_DELAYED,
+                              true,  // success
+                              "",    // user_input
+                              "");   // result
+  handler->ExecuteTest();
+
+  EXPECT_FALSE(handler->got_onjsdialog_);
+  EXPECT_TRUE(handler->got_onbeforeunloaddialog_);
+  EXPECT_TRUE(handler->got_onresetdialogstate_);
+  EXPECT_TRUE(handler->got_onloadend_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/life_span_unittest.cc b/src/tests/ceftests/life_span_unittest.cc
new file mode 100644
index 0000000..7e71d04
--- /dev/null
+++ b/src/tests/ceftests/life_span_unittest.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/test/cef_test_helpers.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kLifeSpanUrl[] = "http://tests-life-span/test.html";
+const char kUnloadDialogText[] = "Are you sure?";
+const char kUnloadMsg[] = "LifeSpanTestHandler.Unload";
+
+// Browser side.
+class LifeSpanTestHandler : public RoutingTestHandler {
+ public:
+  struct Settings {
+    Settings()
+        : force_close(false),
+          add_onunload_handler(false),
+          allow_do_close(true),
+          accept_before_unload_dialog(true) {}
+
+    bool force_close;
+    bool add_onunload_handler;
+    bool allow_do_close;
+    bool accept_before_unload_dialog;
+  };
+
+  explicit LifeSpanTestHandler(const Settings& settings)
+      : settings_(settings), executing_delay_close_(false) {
+    // By default no LifeSpan tests call DestroyTest().
+    SetDestroyTestExpected(false);
+  }
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    std::string page = "<html><script>";
+
+    page += "window.onunload = function() { window.testQuery({request:'" +
+            std::string(kUnloadMsg) + "'}); };";
+
+    if (settings_.add_onunload_handler) {
+      page += "window.onbeforeunload = function() { return '" +
+              std::string(kUnloadDialogText) + "'; };";
+    }
+
+    page += "</script><body>Page</body></html>";
+    AddResource(kLifeSpanUrl, page, "text/html");
+
+    // Create the browser.
+    CreateBrowser(kLifeSpanUrl);
+
+    // Intentionally don't call SetTestTimeout() for these tests.
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    got_after_created_.yes();
+    RoutingTestHandler::OnAfterCreated(browser);
+  }
+
+  bool DoClose(CefRefPtr<CefBrowser> browser) override {
+    if (executing_delay_close_)
+      return false;
+
+    EXPECT_TRUE(browser->IsSame(GetBrowser()));
+
+    got_do_close_.yes();
+
+    if (!settings_.allow_do_close) {
+      // The close will be canceled.
+      ScheduleDelayClose();
+    }
+
+    return !settings_.allow_do_close;
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    if (!executing_delay_close_) {
+      got_before_close_.yes();
+      EXPECT_TRUE(browser->IsSame(GetBrowser()));
+    }
+
+    RoutingTestHandler::OnBeforeClose(browser);
+  }
+
+  bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
+                            const CefString& message_text,
+                            bool is_reload,
+                            CefRefPtr<CefJSDialogCallback> callback) override {
+    if (executing_delay_close_) {
+      callback->Continue(true, CefString());
+      return true;
+    }
+
+    EXPECT_TRUE(browser->IsSame(GetBrowser()));
+
+    // The message is no longer configurable via JavaScript.
+    // See http://crbug.com/587940.
+    EXPECT_STREQ("Is it OK to leave/reload this page?",
+                 message_text.ToString().c_str());
+
+    EXPECT_FALSE(is_reload);
+    EXPECT_TRUE(callback.get());
+
+    if (!settings_.accept_before_unload_dialog) {
+      // The close will be canceled.
+      ScheduleDelayClose();
+    }
+
+    got_before_unload_dialog_.yes();
+    callback->Continue(settings_.accept_before_unload_dialog, CefString());
+    return true;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    got_load_end_.yes();
+    EXPECT_TRUE(browser->IsSame(GetBrowser()));
+
+    if (settings_.add_onunload_handler) {
+      // Send the page a user gesture to enable firing of the onbefureunload
+      // handler. See https://crbug.com/707007.
+      CefExecuteJavaScriptWithUserGestureForTests(frame, CefString());
+    }
+
+    // Attempt to close the browser.
+    CloseBrowser(browser, settings_.force_close);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    if (request.ToString() == kUnloadMsg) {
+      if (!executing_delay_close_)
+        got_unload_message_.yes();
+    }
+    callback->Success("");
+    return true;
+  }
+
+  TrackCallback got_after_created_;
+  TrackCallback got_do_close_;
+  TrackCallback got_before_close_;
+  TrackCallback got_before_unload_dialog_;
+  TrackCallback got_unload_message_;
+  TrackCallback got_load_end_;
+  TrackCallback got_delay_close_;
+
+ private:
+  // Wait a bit to make sure no additional events are received and then close
+  // the window.
+  void ScheduleDelayClose() {
+    // This test will call DestroyTest().
+    SetDestroyTestExpected(true);
+
+    CefPostDelayedTask(TID_UI,
+                       base::Bind(&LifeSpanTestHandler::DelayClose, this), 100);
+  }
+
+  void DelayClose() {
+    got_delay_close_.yes();
+    executing_delay_close_ = true;
+    DestroyTest();
+  }
+
+  Settings settings_;
+
+  // Forces the window to close (bypasses test conditions).
+  bool executing_delay_close_;
+
+  IMPLEMENT_REFCOUNTING(LifeSpanTestHandler);
+};
+
+}  // namespace
+
+TEST(LifeSpanTest, DoCloseAllow) {
+  LifeSpanTestHandler::Settings settings;
+  settings.allow_do_close = true;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_TRUE(handler->got_before_close_);
+  EXPECT_FALSE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_FALSE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, DoCloseAllowForce) {
+  LifeSpanTestHandler::Settings settings;
+  settings.allow_do_close = true;
+  settings.force_close = true;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_TRUE(handler->got_before_close_);
+  EXPECT_FALSE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_FALSE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, DoCloseDisallow) {
+  LifeSpanTestHandler::Settings settings;
+  settings.allow_do_close = false;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_FALSE(handler->got_before_close_);
+  EXPECT_FALSE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_TRUE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, DoCloseDisallowForce) {
+  LifeSpanTestHandler::Settings settings;
+  settings.allow_do_close = false;
+  settings.force_close = true;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_FALSE(handler->got_before_close_);
+  EXPECT_FALSE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_TRUE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, DoCloseDisallowWithOnUnloadAllow) {
+  LifeSpanTestHandler::Settings settings;
+  settings.allow_do_close = false;
+  settings.add_onunload_handler = true;
+  settings.accept_before_unload_dialog = true;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_FALSE(handler->got_before_close_);
+  EXPECT_TRUE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_TRUE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, DoCloseAllowWithOnUnloadForce) {
+  LifeSpanTestHandler::Settings settings;
+  settings.allow_do_close = true;
+  settings.add_onunload_handler = true;
+  settings.force_close = true;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_TRUE(handler->got_before_close_);
+  EXPECT_FALSE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_FALSE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, DoCloseDisallowWithOnUnloadForce) {
+  LifeSpanTestHandler::Settings settings;
+  settings.allow_do_close = false;
+  settings.add_onunload_handler = true;
+  settings.force_close = true;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_FALSE(handler->got_before_close_);
+  EXPECT_FALSE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_TRUE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, OnUnloadAllow) {
+  LifeSpanTestHandler::Settings settings;
+  settings.add_onunload_handler = true;
+  settings.accept_before_unload_dialog = true;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_TRUE(handler->got_do_close_);
+  EXPECT_TRUE(handler->got_before_close_);
+  EXPECT_TRUE(handler->got_before_unload_dialog_);
+  EXPECT_TRUE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_FALSE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(LifeSpanTest, OnUnloadDisallow) {
+  LifeSpanTestHandler::Settings settings;
+  settings.add_onunload_handler = true;
+  settings.accept_before_unload_dialog = false;
+  CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(handler->got_after_created_);
+  EXPECT_FALSE(handler->got_do_close_);
+  EXPECT_FALSE(handler->got_before_close_);
+  EXPECT_TRUE(handler->got_before_unload_dialog_);
+  EXPECT_FALSE(handler->got_unload_message_);
+  EXPECT_TRUE(handler->got_load_end_);
+  EXPECT_TRUE(handler->got_delay_close_);
+
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/message_router_unittest.cc b/src/tests/ceftests/message_router_unittest.cc
new file mode 100644
index 0000000..d904734
--- /dev/null
+++ b/src/tests/ceftests/message_router_unittest.cc
@@ -0,0 +1,2927 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <cstdlib>
+#include <set>
+#include <sstream>
+#include <vector>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_weak_ptr.h"
+#include "include/cef_v8.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppRenderer;
+
+namespace {
+
+const char kTestDomainRoot[] = "http://tests-mr";
+const char kTestDomain1[] = "http://tests-mr1.com/";
+const char kTestDomain2[] = "http://tests-mr2.com/";
+const char kTestDomain3[] = "http://tests-mr3.com/";
+
+const char kDoneMessageName[] = "mrtNotifyMsg";
+
+const char kJSNotifyFunc[] = "mrtNotify";
+const char kJSAssertTotalCountFunc[] = "mrtAssertTotalCount";
+const char kJSAssertBrowserCountFunc[] = "mrtAssertBrowserCount";
+const char kJSAssertContextCountFunc[] = "mrtAssertContextCount";
+
+void SetRouterConfig(CefMessageRouterConfig& config) {
+  config.js_query_function = "mrtQuery";
+  config.js_cancel_function = "mrtQueryCancel";
+}
+
+// Handle the renderer side of the routing implementation.
+class MRRenderDelegate : public ClientAppRenderer::Delegate {
+ public:
+  class V8HandlerImpl : public CefV8Handler {
+   public:
+    explicit V8HandlerImpl(CefRefPtr<MRRenderDelegate> delegate)
+        : delegate_(delegate) {}
+
+    bool Execute(const CefString& name,
+                 CefRefPtr<CefV8Value> object,
+                 const CefV8ValueList& arguments,
+                 CefRefPtr<CefV8Value>& retval,
+                 CefString& exception) override {
+      const std::string& message_name = name;
+      if (message_name == kJSNotifyFunc) {
+        EXPECT_EQ(1U, arguments.size());
+        EXPECT_TRUE(arguments[0]->IsString());
+
+        const CefString& msg = arguments[0]->GetStringValue();
+        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+        CefRefPtr<CefFrame> frame = context->GetFrame();
+
+        CefRefPtr<CefProcessMessage> message =
+            CefProcessMessage::Create(kDoneMessageName);
+        CefRefPtr<CefListValue> args = message->GetArgumentList();
+        args->SetString(0, msg);
+        frame->SendProcessMessage(PID_BROWSER, message);
+        return true;
+      } else {
+        EXPECT_EQ(1U, arguments.size());
+        EXPECT_TRUE(arguments[0]->IsInt());
+
+        const int expected_count = arguments[0]->GetIntValue();
+        int actual_count = -1;
+
+        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+        CefRefPtr<CefBrowser> browser = context->GetBrowser();
+
+        if (name == kJSAssertTotalCountFunc) {
+          actual_count =
+              delegate_->message_router_->GetPendingCount(nullptr, nullptr);
+        } else if (name == kJSAssertBrowserCountFunc) {
+          actual_count =
+              delegate_->message_router_->GetPendingCount(browser, nullptr);
+        } else if (name == kJSAssertContextCountFunc) {
+          actual_count =
+              delegate_->message_router_->GetPendingCount(browser, context);
+        }
+
+        if (expected_count != actual_count) {
+          std::stringstream ss;
+          ss << message_name << " failed; expected " << expected_count
+             << ", got " << actual_count;
+          exception = ss.str();
+        }
+      }
+
+      return true;
+    }
+
+   private:
+    CefRefPtr<MRRenderDelegate> delegate_;
+
+    IMPLEMENT_REFCOUNTING(V8HandlerImpl);
+  };
+
+  MRRenderDelegate() {}
+
+  void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) override {
+    // Create the renderer-side router for query handling.
+    CefMessageRouterConfig config;
+    SetRouterConfig(config);
+    message_router_ = CefMessageRouterRendererSide::Create(config);
+  }
+
+  void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefV8Context> context) override {
+    const std::string& url = frame->GetURL();
+    if (url.find(kTestDomainRoot) != 0)
+      return;
+
+    message_router_->OnContextCreated(browser, frame, context);
+
+    // Register function handlers with the 'window' object.
+    CefRefPtr<CefV8Value> window = context->GetGlobal();
+
+    CefRefPtr<V8HandlerImpl> handler = new V8HandlerImpl(this);
+    CefV8Value::PropertyAttribute attributes =
+        static_cast<CefV8Value::PropertyAttribute>(
+            V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM |
+            V8_PROPERTY_ATTRIBUTE_DONTDELETE);
+
+    CefRefPtr<CefV8Value> notify_func =
+        CefV8Value::CreateFunction(kJSNotifyFunc, handler.get());
+    window->SetValue(kJSNotifyFunc, notify_func, attributes);
+
+    CefRefPtr<CefV8Value> total_count_func =
+        CefV8Value::CreateFunction(kJSAssertTotalCountFunc, handler.get());
+    window->SetValue(kJSAssertTotalCountFunc, total_count_func, attributes);
+
+    CefRefPtr<CefV8Value> browser_count_func =
+        CefV8Value::CreateFunction(kJSAssertBrowserCountFunc, handler.get());
+    window->SetValue(kJSAssertBrowserCountFunc, browser_count_func, attributes);
+
+    CefRefPtr<CefV8Value> context_count_func =
+        CefV8Value::CreateFunction(kJSAssertContextCountFunc, handler.get());
+    window->SetValue(kJSAssertContextCountFunc, context_count_func, attributes);
+  }
+
+  void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
+                         CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefV8Context> context) override {
+    const std::string& url = frame->GetURL();
+    if (url.find(kTestDomainRoot) != 0)
+      return;
+
+    message_router_->OnContextReleased(browser, frame, context);
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    const std::string& url = browser->GetMainFrame()->GetURL();
+    if (url.find(kTestDomainRoot) != 0)
+      return false;
+
+    return message_router_->OnProcessMessageReceived(browser, frame,
+                                                     source_process, message);
+  }
+
+ private:
+  CefRefPtr<CefMessageRouterRendererSide> message_router_;
+
+  IMPLEMENT_REFCOUNTING(MRRenderDelegate);
+};
+
+}  // namespace
+
+// Entry point for creating the test delegate.
+// Called from client_app_delegates.cc.
+void CreateMessageRouterRendererTests(
+    ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new MRRenderDelegate);
+}
+
+namespace {
+
+class MRTestHandler : public TestHandler {
+ public:
+  MRTestHandler() {}
+
+  void RunTest() override {
+    RunMRTest();
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout(10000);
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    if (!message_router_.get()) {
+      // Create the browser-side router for query handling.
+      CefMessageRouterConfig config;
+      SetRouterConfig(config);
+      message_router_ = CefMessageRouterBrowserSide::Create(config);
+      AddHandlers(message_router_);
+    }
+    TestHandler::OnAfterCreated(browser);
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    message_router_->OnBeforeClose(browser);
+    TestHandler::OnBeforeClose(browser);
+  }
+
+  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                 TerminationStatus status) override {
+    message_router_->OnRenderProcessTerminated(browser);
+  }
+
+  // Only call this method if the navigation isn't canceled.
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    message_router_->OnBeforeBrowse(browser, frame);
+    return false;
+  }
+
+  // Returns true if the router handled the navigation.
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    const std::string& message_name = message->GetName();
+    if (message_name == kDoneMessageName) {
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_EQ(1U, args->GetSize());
+      EXPECT_EQ(VTYPE_STRING, args->GetType(0));
+      OnNotify(browser, frame, args->GetString(0));
+      return true;
+    }
+
+    return message_router_->OnProcessMessageReceived(browser, frame,
+                                                     source_process, message);
+  }
+
+  CefRefPtr<CefMessageRouterBrowserSide> GetRouter() const {
+    return message_router_;
+  }
+
+ protected:
+  virtual void RunMRTest() = 0;
+
+  virtual void AddHandlers(
+      CefRefPtr<CefMessageRouterBrowserSide> message_router) = 0;
+
+  virtual void OnNotify(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        const std::string& message) = 0;
+
+  bool AssertQueryCount(CefRefPtr<CefBrowser> browser,
+                        CefMessageRouterBrowserSide::Handler* handler,
+                        int expected_count) {
+    int actual_count = message_router_->GetPendingCount(browser, handler);
+    EXPECT_EQ(expected_count, actual_count);
+    return (expected_count == actual_count);
+  }
+
+  void AssertMainBrowser(CefRefPtr<CefBrowser> browser) {
+    EXPECT_TRUE(browser.get());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+  }
+
+ private:
+  CefRefPtr<CefMessageRouterBrowserSide> message_router_;
+
+  IMPLEMENT_REFCOUNTING(MRTestHandler);
+};
+
+// Implementation of MRTestHandler that loads a single page.
+class SingleLoadTestHandler : public MRTestHandler,
+                              public CefMessageRouterBrowserSide::Handler {
+ public:
+  SingleLoadTestHandler()
+      : main_url_(std::string(kTestDomain1) + "main.html") {}
+
+  const std::string& GetMainURL() { return main_url_; }
+
+ protected:
+  void RunMRTest() override {
+    AddOtherResources();
+    AddResource(main_url_, GetMainHTML(), "text/html");
+
+    CreateBrowser(main_url_, nullptr);
+  }
+
+  void AddHandlers(
+      CefRefPtr<CefMessageRouterBrowserSide> message_router) override {
+    message_router->AddHandler(this, false);
+  }
+
+  virtual void AddOtherResources() {}
+
+  virtual std::string GetMainHTML() = 0;
+
+  void AssertMainFrame(CefRefPtr<CefFrame> frame) {
+    EXPECT_TRUE(frame.get());
+    EXPECT_TRUE(frame->IsMain());
+    EXPECT_STREQ(main_url_.c_str(), frame->GetURL().ToString().c_str());
+  }
+
+ private:
+  const std::string main_url_;
+};
+
+// Used to verify that the test harness (bound functions) behave correctly.
+class HarnessTestHandler : public SingleLoadTestHandler {
+ public:
+  HarnessTestHandler(bool test_success) : test_success_(test_success) {}
+
+  std::string GetMainHTML() override {
+    std::string html;
+    if (test_success_) {
+      // All assertions should pass.
+      html =
+          "<html><body><script>\n"
+          "var fail_ct = 0;\n"
+          "try { window.mrtAssertTotalCount(0); } catch (e) { fail_ct++; }\n"
+          "try { window.mrtAssertBrowserCount(0); } catch (e) { fail_ct++; }\n"
+          "try { window.mrtAssertContextCount(0); } catch (e) { fail_ct++; }\n"
+          "window.mrtNotify('' + (fail_ct == 0));"
+          "</script></body></html>";
+    } else {
+      // All assertions should fail.
+      html =
+          "<html><body><script>\n"
+          "var fail_ct = 0;\n"
+          "try { window.mrtAssertTotalCount(1); } catch (e) { fail_ct++; }\n"
+          "try { window.mrtAssertBrowserCount(1); } catch (e) { fail_ct++; }\n"
+          "try { window.mrtAssertContextCount(1); } catch (e) { fail_ct++; }\n"
+          "window.mrtNotify('' + (fail_ct == 3));"
+          "</script></body></html>";
+    }
+    return html;
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    got_done_.yes();
+    EXPECT_STREQ("true", message.c_str());
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_done_);
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  const bool test_success_;
+  TrackCallback got_done_;
+};
+
+}  // namespace
+
+// Verify that the test harness works with successful assertions.
+TEST(MessageRouterTest, HarnessSuccess) {
+  CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify that the test harness works with failed assertions.
+TEST(MessageRouterTest, HarnessFailure) {
+  CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kSingleQueryRequest[] = "request_context";
+const char kSingleQueryResponse[] = "success_response";
+const int kSingleQueryErrorCode = 5;
+const char kSingleQueryErrorMessage[] = "error_message";
+
+// Test a single query in a single page load.
+class SingleQueryTestHandler : public SingleLoadTestHandler {
+ public:
+  enum TestType {
+    SUCCESS,
+    FAILURE,
+    CANCEL,
+  };
+
+  SingleQueryTestHandler(TestType type, bool sync_callback)
+      : test_type_(type), sync_callback_(sync_callback), query_id_(0) {}
+
+  std::string GetMainHTML() override {
+    std::string html;
+
+    std::stringstream ss;
+    ss << kSingleQueryErrorCode;
+    const std::string& errorCodeStr = ss.str();
+
+    html =
+        "<html><body><script>\n"
+        // No requests should exist.
+        "window.mrtAssertTotalCount(0);\n"
+        "window.mrtAssertBrowserCount(0);\n"
+        "window.mrtAssertContextCount(0);\n"
+        // Send the query.
+        "var request_id = window.mrtQuery({\n"
+        "  request: '" +
+        std::string(kSingleQueryRequest) +
+        "',\n"
+        "  persistent: false,\n"
+        "  onSuccess: function(response) {\n"
+        // Request should be removed before callback is executed.
+        "    window.mrtAssertTotalCount(0);\n"
+        "    window.mrtAssertBrowserCount(0);\n"
+        "    window.mrtAssertContextCount(0);\n"
+        "    if (response == '" +
+        std::string(kSingleQueryResponse) +
+        "')\n"
+        "      window.mrtNotify('success');\n"
+        "    else\n"
+        "      window.mrtNotify('error-onSuccess');\n"
+        "  },\n"
+        "  onFailure: function(error_code, error_message) {\n"
+        // Request should be removed before callback is executed.
+        "    window.mrtAssertTotalCount(0);\n"
+        "    window.mrtAssertBrowserCount(0);\n"
+        "    window.mrtAssertContextCount(0);\n"
+        "    if (error_code == " +
+        errorCodeStr + " && error_message == '" +
+        std::string(kSingleQueryErrorMessage) +
+        "')\n"
+        "      window.mrtNotify('failure');\n"
+        "    else\n"
+        "      window.mrtNotify('error-onFailure');\n"
+        "  }\n"
+        "});\n"
+        // Request should exist.
+        "window.mrtAssertTotalCount(1);\n"
+        "window.mrtAssertBrowserCount(1);\n"
+        "window.mrtAssertContextCount(1);\n";
+
+    if (test_type_ == CANCEL) {
+      html +=
+          "window.mrtQueryCancel(request_id);\n"
+          // Request should be removed immediately.
+          "window.mrtAssertTotalCount(0);\n"
+          "window.mrtAssertBrowserCount(0);\n"
+          "window.mrtAssertContextCount(0);\n"
+          "window.mrtNotify('cancel');\n";
+    }
+
+    html += "</script></body></html>";
+    return html;
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    // OnNotify only be called once.
+    EXPECT_FALSE(got_notify_);
+    got_notify_.yes();
+
+    if (test_type_ == SUCCESS) {
+      EXPECT_STREQ("success", message.c_str());
+    } else if (test_type_ == FAILURE) {
+      EXPECT_STREQ("failure", message.c_str());
+    } else if (test_type_ == CANCEL) {
+      EXPECT_STREQ("cancel", message.c_str());
+    }
+
+    DestroyTestIfDone();
+  }
+
+  void ExecuteCallback() {
+    EXPECT_TRUE(callback_.get());
+    if (test_type_ == SUCCESS) {
+      callback_->Success(kSingleQueryResponse);
+    } else if (test_type_ == FAILURE) {
+      callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage);
+    } else {
+      EXPECT_TRUE(false);  // Not reached.
+    }
+    callback_ = nullptr;
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+    EXPECT_NE(0, query_id);
+    EXPECT_FALSE(persistent);
+    EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
+
+    got_on_query_.yes();
+
+    query_id_ = query_id;
+    callback_ = callback;
+
+    if (test_type_ == SUCCESS || test_type_ == FAILURE) {
+      if (sync_callback_) {
+        ExecuteCallback();
+      } else {
+        CefPostTask(TID_UI,
+                    base::Bind(&SingleQueryTestHandler::ExecuteCallback, this));
+      }
+    }
+
+    return true;
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+    EXPECT_EQ(test_type_, CANCEL);
+    EXPECT_EQ(query_id_, query_id);
+    EXPECT_TRUE(got_on_query_);
+    EXPECT_TRUE(callback_.get());
+
+    got_on_query_canceled_.yes();
+    callback_ = nullptr;
+
+    DestroyTestIfDone();
+  }
+
+  void DestroyTestIfDone() {
+    bool destroy_test = false;
+    if (test_type_ == CANCEL)
+      destroy_test = got_notify_ && got_on_query_canceled_;
+    else
+      destroy_test = got_notify_;
+    if (destroy_test)
+      DestroyTest();
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_notify_);
+    EXPECT_TRUE(got_on_query_);
+    EXPECT_FALSE(callback_.get());
+
+    if (test_type_ == CANCEL)
+      EXPECT_TRUE(got_on_query_canceled_);
+    else
+      EXPECT_FALSE(got_on_query_canceled_);
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  const TestType test_type_;
+  const bool sync_callback_;
+
+  int64 query_id_;
+  CefRefPtr<Callback> callback_;
+
+  TrackCallback got_on_query_;
+  TrackCallback got_on_query_canceled_;
+  TrackCallback got_notify_;
+};
+
+}  // namespace
+
+// Test that a single query with successful result delivered synchronously.
+TEST(MessageRouterTest, SingleQuerySuccessSyncCallback) {
+  CefRefPtr<SingleQueryTestHandler> handler =
+      new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a single query with successful result delivered asynchronously.
+TEST(MessageRouterTest, SingleQuerySuccessAsyncCallback) {
+  CefRefPtr<SingleQueryTestHandler> handler =
+      new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a single query with failure result delivered synchronously.
+TEST(MessageRouterTest, SingleQueryFailureSyncCallback) {
+  CefRefPtr<SingleQueryTestHandler> handler =
+      new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a single query with failure result delivered asynchronously.
+TEST(MessageRouterTest, SingleQueryFailureAsyncCallback) {
+  CefRefPtr<SingleQueryTestHandler> handler =
+      new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a single query with cancellation.
+TEST(MessageRouterTest, SingleQueryCancel) {
+  CefRefPtr<SingleQueryTestHandler> handler =
+      new SingleQueryTestHandler(SingleQueryTestHandler::CANCEL, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const int kSinglePersistentQueryResponseCount = 10;
+
+// Test a single persistent query in a single page load.
+class SinglePersistentQueryTestHandler : public SingleLoadTestHandler {
+ public:
+  enum TestType {
+    SUCCESS,
+    FAILURE,
+  };
+
+  SinglePersistentQueryTestHandler(TestType test_type, bool sync_callback)
+      : test_type_(test_type), sync_callback_(sync_callback), query_id_(0) {}
+
+  std::string GetMainHTML() override {
+    std::string html;
+
+    std::stringstream ss;
+    ss << kSinglePersistentQueryResponseCount;
+    const std::string& responseCountStr = ss.str();
+    ss.str("");
+    ss << kSingleQueryErrorCode;
+    const std::string& errorCodeStr = ss.str();
+
+    html =
+        "<html><body><script>\n"
+        // No requests should exist.
+        "window.mrtAssertTotalCount(0);\n"
+        "window.mrtAssertBrowserCount(0);\n"
+        "window.mrtAssertContextCount(0);\n"
+        // Keep track of the number of responses.
+        "var count = 0;\n"
+        // Send the query.
+        "var request_id = window.mrtQuery({\n"
+        "  request: '" +
+        std::string(kSingleQueryRequest) +
+        "',\n"
+        "  persistent: true,\n"
+        "  onSuccess: function(response) {\n"
+        // Request should not be removed.
+        "    window.mrtAssertTotalCount(1);\n"
+        "    window.mrtAssertBrowserCount(1);\n"
+        "    window.mrtAssertContextCount(1);\n"
+        "    if (response == '" +
+        std::string(kSingleQueryResponse) +
+        "') {\n"
+        "      if (++count == " +
+        responseCountStr +
+        ") {\n"
+        "        window.mrtNotify('success');\n"
+        "        window.mrtQueryCancel(request_id);\n"
+        // Request should be removed immediately.
+        "        window.mrtAssertTotalCount(0);\n"
+        "        window.mrtAssertBrowserCount(0);\n"
+        "        window.mrtAssertContextCount(0);\n"
+        "      }\n"
+        "    } else {\n"
+        "      window.mrtNotify('error-onSuccess');\n"
+        "    }\n"
+        "  },\n"
+        "  onFailure: function(error_code, error_message) {\n"
+        // Request should be removed before callback is executed.
+        "    window.mrtAssertTotalCount(0);\n"
+        "    window.mrtAssertBrowserCount(0);\n"
+        "    window.mrtAssertContextCount(0);\n"
+        "    if (error_code == " +
+        errorCodeStr + " && error_message == '" +
+        std::string(kSingleQueryErrorMessage) +
+        "') {\n"
+        "      window.mrtNotify('failure');\n"
+        "    } else {\n"
+        "      window.mrtNotify('error-onFailure');\n"
+        "    }\n"
+        "  }\n"
+        "});\n"
+        // Request should exist.
+        "window.mrtAssertTotalCount(1);\n"
+        "window.mrtAssertBrowserCount(1);\n"
+        "window.mrtAssertContextCount(1);\n";
+
+    html += "</script></body></html>";
+    return html;
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    if (test_type_ == SUCCESS) {
+      EXPECT_STREQ("success", message.c_str());
+    } else if (test_type_ == FAILURE) {
+      EXPECT_STREQ("failure", message.c_str());
+    }
+
+    got_notify_.yes();
+
+    DestroyTestIfDone();
+  }
+
+  void ExecuteCallback() {
+    EXPECT_TRUE(callback_.get());
+    if (test_type_ == SUCCESS) {
+      callback_->Success(kSingleQueryResponse);
+    } else {
+      callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage);
+      callback_ = nullptr;
+    }
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+    EXPECT_NE(0, query_id);
+    EXPECT_TRUE(persistent);
+    EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
+
+    got_on_query_.yes();
+
+    query_id_ = query_id;
+    callback_ = callback;
+
+    int repeat =
+        (test_type_ == SUCCESS ? kSinglePersistentQueryResponseCount : 1);
+
+    for (int i = 0; i < repeat; ++i) {
+      if (sync_callback_) {
+        ExecuteCallback();
+      } else {
+        CefPostTask(
+            TID_UI,
+            base::Bind(&SinglePersistentQueryTestHandler::ExecuteCallback,
+                       this));
+      }
+    }
+
+    return true;
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+    EXPECT_EQ(query_id_, query_id);
+    EXPECT_TRUE(got_on_query_);
+    EXPECT_TRUE(callback_.get());
+
+    got_on_query_canceled_.yes();
+    callback_ = nullptr;
+
+    DestroyTestIfDone();
+  }
+
+  void DestroyTestIfDone() {
+    bool destroy_test = false;
+    if (test_type_ == SUCCESS) {
+      if (got_on_query_ && got_on_query_canceled_ && got_notify_)
+        destroy_test = true;
+    } else if (got_on_query_ && got_notify_) {
+      destroy_test = true;
+    }
+
+    if (destroy_test)
+      DestroyTest();
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_notify_);
+    EXPECT_TRUE(got_on_query_);
+    EXPECT_FALSE(callback_.get());
+
+    if (test_type_ == SUCCESS)
+      EXPECT_TRUE(got_on_query_canceled_);
+    else
+      EXPECT_FALSE(got_on_query_canceled_);
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  const TestType test_type_;
+  const bool sync_callback_;
+
+  int64 query_id_;
+  CefRefPtr<Callback> callback_;
+
+  TrackCallback got_on_query_;
+  TrackCallback got_on_query_canceled_;
+  TrackCallback got_notify_;
+};
+
+}  // namespace
+
+// Test that a single query with successful result delivered synchronously.
+TEST(MessageRouterTest, SinglePersistentQuerySuccessSyncCallback) {
+  CefRefPtr<SinglePersistentQueryTestHandler> handler =
+      new SinglePersistentQueryTestHandler(
+          SinglePersistentQueryTestHandler::SUCCESS, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a single query with successful result delivered asynchronously.
+TEST(MessageRouterTest, SinglePersistentQuerySuccessAsyncCallback) {
+  CefRefPtr<SinglePersistentQueryTestHandler> handler =
+      new SinglePersistentQueryTestHandler(
+          SinglePersistentQueryTestHandler::SUCCESS, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a single query with failure result delivered synchronously.
+TEST(MessageRouterTest, SinglePersistentQueryFailureSyncCallback) {
+  CefRefPtr<SinglePersistentQueryTestHandler> handler =
+      new SinglePersistentQueryTestHandler(
+          SinglePersistentQueryTestHandler::FAILURE, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a single query with failure result delivered asynchronously.
+TEST(MessageRouterTest, SinglePersistentQueryFailureAsyncCallback) {
+  CefRefPtr<SinglePersistentQueryTestHandler> handler =
+      new SinglePersistentQueryTestHandler(
+          SinglePersistentQueryTestHandler::FAILURE, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// Test a single unhandled query in a single page load.
+class SingleUnhandledQueryTestHandler : public SingleLoadTestHandler {
+ public:
+  SingleUnhandledQueryTestHandler() {}
+
+  std::string GetMainHTML() override {
+    std::string html;
+
+    html =
+        "<html><body><script>\n"
+        // No requests should exist.
+        "window.mrtAssertTotalCount(0);\n"
+        "window.mrtAssertBrowserCount(0);\n"
+        "window.mrtAssertContextCount(0);\n"
+        // Keep track of the number of responses.
+        "var count = 0;\n"
+        // Send the query.
+        "var request_id = window.mrtQuery({\n"
+        "  request: '" +
+        std::string(kSingleQueryRequest) +
+        "',\n"
+        "  persistent: false,\n"
+        "  onSuccess: function(response) {\n"
+        "    window.mrtNotify('error-onSuccess');\n"
+        "  },\n"
+        "  onFailure: function(error_code, error_message) {\n"
+        // Request should be removed before callback is executed.
+        "    window.mrtAssertTotalCount(0);\n"
+        "    window.mrtAssertBrowserCount(0);\n"
+        "    window.mrtAssertContextCount(0);\n"
+        "    if (error_code == -1 && "
+        "error_message == 'The query has been canceled') {\n"
+        "      window.mrtNotify('failure');\n"
+        "    } else {\n"
+        "      window.mrtNotify('error-onFailure');\n"
+        "    }\n"
+        "  }\n"
+        "});\n"
+        // Request should exist.
+        "window.mrtAssertTotalCount(1);\n"
+        "window.mrtAssertBrowserCount(1);\n"
+        "window.mrtAssertContextCount(1);\n";
+
+    html += "</script></body></html>";
+    return html;
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+    EXPECT_STREQ("failure", message.c_str());
+
+    got_notify_.yes();
+
+    DestroyTest();
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+    EXPECT_NE(0, query_id);
+    EXPECT_FALSE(persistent);
+    EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
+
+    got_on_query_.yes();
+
+    return false;
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    EXPECT_FALSE(true);  // Not reached.
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_on_query_);
+    EXPECT_TRUE(got_notify_);
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  TrackCallback got_on_query_;
+  TrackCallback got_notify_;
+};
+
+}  // namespace
+
+// Test that a single unhandled query results in a call to onFailure.
+TEST(MessageRouterTest, SingleUnhandledQuery) {
+  CefRefPtr<SingleUnhandledQueryTestHandler> handler =
+      new SingleUnhandledQueryTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kMultiQueryRequestId[] = "request_id";
+const char kMultiQueryRepeatCt[] = "repeat_ct";
+const char kMultiQueryRequest[] = "request";
+const char kMultiQueryResponse[] = "response";
+const char kMultiQuerySuccess[] = "success";
+const char kMultiQueryError[] = "error";
+const char kMultiQueryErrorMessage[] = "errormsg";
+const int kMultiQueryPersistentResponseCount = 5;
+
+// Generates HTML and verifies results for multiple simultanious queries.
+class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
+ public:
+  enum TestType {
+    // Initiates a non-persistent query with a successful response.
+    // OnQuery and OnNotify will be called.
+    SUCCESS,
+
+    // Initiates a non-persistent query with a failure response.
+    // OnQuery and OnNotify will be called.
+    FAILURE,
+
+    // Initiates a persistent query with multiple successful responses.
+    // OnQuery, OnNotify and OnQueryCanceled will be called.
+    PERSISTENT_SUCCESS,
+
+    // Initiates a persistent query with multiple successful responses and one
+    // failure response.
+    // OnQuery and OnNotify will be called.
+    PERSISTENT_FAILURE,
+
+    // Initiates a non-persistent query that will be canceled via JavaScript.
+    // No JavaScript callbacks will be executed.
+    // OnQuery and OnQueryCanceled will be called.
+    CANCEL,
+
+    // Initiates a non-persistent query that will not be manually canceled.
+    // No JavaScript callbacks will be executed.
+    // OnQuery and OnQueryCanceled will be called.
+    AUTOCANCEL,
+
+    // Initiates a persistent query with multiple successful responses that will
+    // not be manually canceled.
+    // OnQuery, OnNotify and OnQueryCanceled will be called.
+    PERSISTENT_AUTOCANCEL,
+  };
+
+  class Observer {
+   public:
+    // Called when all manual queries are complete.
+    virtual void OnManualQueriesCompleted(MultiQueryManager* manager) {}
+
+    // Called when all queries are complete.
+    virtual void OnAllQueriesCompleted(MultiQueryManager* manager) {}
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  MultiQueryManager(const std::string& label,
+                    bool synchronous,
+                    int id_offset = 0)
+      : label_(label),
+        synchronous_(synchronous),
+        id_offset_(id_offset),
+        finalized_(false),
+        running_(false),
+        manual_total_(0),
+        received_count_(0),
+        manual_complete_count_(0),
+        auto_complete_count_(0),
+        will_cancel_by_removing_handler_(false),
+        weak_ptr_factory_(this) {}
+
+  virtual ~MultiQueryManager() {}
+
+  std::string label() const { return label_; }
+
+  void AddObserver(Observer* observer) {
+    EXPECT_FALSE(running_);
+    observer_set_.insert(observer);
+  }
+
+  void RemoveObserver(Observer* observer) {
+    EXPECT_FALSE(running_);
+    EXPECT_TRUE(observer_set_.erase(observer));
+  }
+
+  // Can be called from any thread, but should always be called from the same
+  // thread.
+  void AddTestQuery(TestType type) {
+    EXPECT_FALSE(finalized_);
+    test_query_vector_.push_back(TestQuery(type));
+    if (!IsAuto(type))
+      manual_total_++;
+  }
+
+  // Must be called after AddTestQuery and before the manager is used.
+  void Finalize() {
+    EXPECT_FALSE(finalized_);
+    finalized_ = true;
+  }
+
+  // Call after all manual queries have completed if you intend to cancel auto
+  // queries by removing the handler.
+  void WillCancelByRemovingHandler() {
+    EXPECT_TRUE(IsManualComplete());
+    will_cancel_by_removing_handler_ = true;
+  }
+
+  std::string GetHTML(bool assert_total, bool assert_browser) const {
+    EXPECT_TRUE(finalized_);
+    EXPECT_FALSE(running_);
+
+    std::string html;
+
+    html = "<html><body>" + label_ + "<script>\n";
+
+    // No requests should exist.
+    if (assert_total)
+      html += "window.mrtAssertTotalCount(0);\n";
+    if (assert_browser)
+      html += "window.mrtAssertBrowserCount(0);\n";
+    html += "window.mrtAssertContextCount(0);\n";
+
+    if (synchronous_) {
+      // Run all of the queries synchronously. None will complete before the
+      // last one begins.
+      for (size_t i = 0; i < test_query_vector_.size(); ++i) {
+        const TestQuery& query = test_query_vector_[i];
+        html += GetQueryHTML(static_cast<int>(i), query);
+      }
+
+      const int total_ct = static_cast<int>(test_query_vector_.size());
+
+      // Pending requests should match the total created.
+      const std::string& total_val = GetIntString(total_ct);
+      if (assert_total)
+        html += "window.mrtAssertTotalCount(" + total_val + ");\n";
+      if (assert_browser)
+        html += "window.mrtAssertBrowserCount(" + total_val + ");\n";
+      html += "window.mrtAssertContextCount(" + total_val + ");\n";
+
+      int cancel_ct = 0;
+
+      // Cancel all of the queries with type CANCEL.
+      for (size_t i = 0; i < test_query_vector_.size(); ++i) {
+        const TestQuery& query = test_query_vector_[i];
+        if (query.type == CANCEL) {
+          html += GetCancelHTML(static_cast<int>(i), query);
+          cancel_ct++;
+        }
+      }
+
+      if (cancel_ct > 0) {
+        // Pending requests should match the total not canceled.
+        const std::string& cancel_val = GetIntString(total_ct - cancel_ct);
+        if (assert_total)
+          html += "window.mrtAssertTotalCount(" + cancel_val + ");\n";
+        if (assert_browser)
+          html += "window.mrtAssertBrowserCount(" + cancel_val + ");\n";
+        html += "window.mrtAssertContextCount(" + cancel_val + ");\n";
+      }
+    } else {
+      // Run all of the queries asynchronously. Some may complete before
+      // others begin.
+      for (size_t i = 0; i < test_query_vector_.size(); ++i) {
+        const TestQuery& query = test_query_vector_[i];
+
+        const int index = static_cast<int>(i);
+
+        // Each request is delayed by 10ms from the previous request.
+        const std::string& delay_val = GetIntString(index);
+        const std::string& query_html = GetQueryHTML(index, query);
+
+        html += "window.setTimeout(function() {\n" + query_html;
+
+        if (query.type == CANCEL) {
+          // Cancel the query asynchronously with a 10ms delay.
+          const std::string& request_id_var =
+              GetIDString(kMultiQueryRequestId, index);
+          html +=
+              "  window.setTimeout(function() {\n"
+              "    window.mrtQueryCancel(" +
+              request_id_var +
+              ");\n"
+              "  }, 1);\n";
+        }
+
+        html += "\n}, " + delay_val + ");\n";
+      }
+    }
+
+    html += "</script></body></html>";
+
+    return html;
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) {
+    EXPECT_TRUE(finalized_);
+    EXPECT_UI_THREAD();
+
+    if (!running_)
+      running_ = true;
+
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+
+    std::string value;
+    int index = 0;
+    EXPECT_TRUE(SplitIDString(message, &value, &index));
+
+    TestQuery& query = test_query_vector_[index];
+
+    // Verify that browser and frame are the same.
+    EXPECT_EQ(query.browser_id, browser->GetIdentifier()) << index;
+    EXPECT_EQ(query.frame_id, frame->GetIdentifier()) << index;
+
+    // Verify a successful/expected result.
+    if (will_cancel_by_removing_handler_) {
+      // Auto queries receive an onFailure callback which will notify with error
+      // when the handler is removed.
+      EXPECT_STREQ(kMultiQueryError, value.c_str()) << index;
+      EXPECT_TRUE(IsAuto(query.type)) << index;
+      EXPECT_TRUE(query.got_query) << index;
+      if (query.type == PERSISTENT_AUTOCANCEL)
+        EXPECT_TRUE(query.got_success) << index;
+      else
+        EXPECT_FALSE(query.got_success) << index;
+
+      query.got_error.yes();
+
+      // There's a race between OnQueryCanceled and OnNotification. Only call
+      // OnQueryCompleted a single time.
+      if (query.got_query_canceled)
+        OnQueryCompleted(query.type);
+    } else {
+      EXPECT_STREQ(kMultiQuerySuccess, value.c_str()) << index;
+      EXPECT_TRUE(WillNotify(query.type)) << index;
+      EXPECT_TRUE(query.got_query) << index;
+      EXPECT_FALSE(query.got_query_canceled) << index;
+      EXPECT_FALSE(query.got_success) << index;
+
+      query.got_success.yes();
+
+      // PERSISTENT_AUTOCANCEL doesn't call OnReceiveCompleted from OnQuery.
+      if (query.type == PERSISTENT_AUTOCANCEL)
+        OnReceiveCompleted(query.type);
+
+      // Call OnQueryCompleted for types that don't get OnQueryCanceled.
+      if (!WillCancel(query.type))
+        OnQueryCompleted(query.type);
+    }
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    EXPECT_TRUE(finalized_);
+    EXPECT_UI_THREAD();
+
+    if (!running_)
+      running_ = true;
+
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    EXPECT_NE(0, query_id);
+
+    std::string value;
+    int index = 0;
+    EXPECT_TRUE(SplitIDString(request, &value, &index));
+
+    TestQuery& query = test_query_vector_[index];
+
+    if (IsPersistent(query.type))
+      EXPECT_TRUE(persistent);
+    else
+      EXPECT_FALSE(persistent);
+
+    // Verify expected request.
+    EXPECT_STREQ(kMultiQueryRequest, value.c_str()) << index;
+
+    // Verify that call order is correct.
+    EXPECT_FALSE(query.got_query) << index;
+    EXPECT_FALSE(query.got_query_canceled) << index;
+    EXPECT_FALSE(query.got_success) << index;
+    EXPECT_FALSE(query.got_error) << index;
+
+    query.got_query.yes();
+
+    query.browser_id = browser->GetIdentifier();
+    query.frame_id = frame->GetIdentifier();
+    query.is_main_frame = frame->IsMain();
+
+    if (query.type == SUCCESS) {
+      // Send the single success response.
+      callback->Success(GetIDString(kMultiQueryResponse, index));
+    } else if (IsPersistent(query.type)) {
+      // Send the required number of successful responses.
+      const std::string& response = GetIDString(kMultiQueryResponse, index);
+      for (int i = 0; i < kMultiQueryPersistentResponseCount; ++i)
+        callback->Success(response);
+    }
+
+    if (WillFail(query.type)) {
+      // Send the single failure response.
+      callback->Failure(index, GetIDString(kMultiQueryErrorMessage, index));
+    }
+
+    if (WillCancel(query.type)) {
+      // Hold onto the callback until the query is canceled.
+      query.query_id = query_id;
+      query.callback = callback;
+    }
+
+    // PERSISTENT_AUTOCANCEL will call OnReceiveCompleted once the success
+    // notification is received.
+    if (query.type != PERSISTENT_AUTOCANCEL)
+      OnReceiveCompleted(query.type);
+
+    return true;
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    EXPECT_TRUE(finalized_);
+    EXPECT_UI_THREAD();
+
+    if (!running_)
+      running_ = true;
+
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    EXPECT_NE(0, query_id);
+
+    bool found = false;
+    for (size_t i = 0; i < test_query_vector_.size(); ++i) {
+      TestQuery& query = test_query_vector_[i];
+      if (query.query_id == query_id) {
+        // Verify that browser and frame are the same.
+        EXPECT_EQ(query.browser_id, browser->GetIdentifier()) << i;
+        if (query.is_main_frame) {
+          EXPECT_TRUE(frame->IsMain()) << i;
+        } else {
+          EXPECT_FALSE(frame->IsMain()) << i;
+          EXPECT_EQ(query.frame_id, frame->GetIdentifier()) << i;
+        }
+
+        // Verify a successful/expected result.
+        EXPECT_TRUE(WillCancel(query.type)) << i;
+        EXPECT_TRUE(query.callback.get()) << i;
+
+        // Release the callback.
+        query.callback = nullptr;
+
+        // Verify that call order is correct.
+        EXPECT_TRUE(query.got_query) << i;
+
+        if (query.type == CANCEL || query.type == AUTOCANCEL) {
+          // No JavaScript onSuccess callback executes.
+          EXPECT_FALSE(query.got_success) << i;
+        } else {
+          // JavaScript onSuccess does execute before cancellation.
+          EXPECT_TRUE(query.got_success) << i;
+        }
+
+        query.got_query_canceled.yes();
+
+        if (will_cancel_by_removing_handler_) {
+          // There's a race between OnQueryCanceled and OnNotification. Only
+          // call OnQueryCompleted a single time.
+          if (query.got_error)
+            OnQueryCompleted(query.type);
+        } else {
+          EXPECT_FALSE(query.got_error) << i;
+
+          // Cancellation is always completion.
+          OnQueryCompleted(query.type);
+        }
+
+        found = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(found);
+  }
+
+  // Asserts that all queries have completed.
+  void AssertAllComplete() const {
+    EXPECT_TRUE(finalized_);
+    EXPECT_FALSE(running_);
+    EXPECT_UI_THREAD();
+
+    for (size_t i = 0; i < test_query_vector_.size(); ++i) {
+      const TestQuery& query = test_query_vector_[i];
+      EXPECT_TRUE(query.got_query) << i;
+
+      if (WillCancel(query.type))
+        EXPECT_TRUE(query.got_query_canceled) << i;
+      else
+        EXPECT_FALSE(query.got_query_canceled) << i;
+
+      if (WillNotify(query.type))
+        EXPECT_TRUE(query.got_success) << i;
+      else
+        EXPECT_FALSE(query.got_success) << i;
+
+      if (IsAuto(query.type) && will_cancel_by_removing_handler_)
+        EXPECT_TRUE(query.got_error);
+      else
+        EXPECT_FALSE(query.got_error);
+
+      EXPECT_FALSE(query.callback.get()) << i;
+    }
+  }
+
+  // Returns true if all manual queries have completed.
+  bool IsManualComplete() const {
+    EXPECT_TRUE(finalized_);
+    EXPECT_UI_THREAD();
+
+    return (manual_complete_count_ == manual_total_);
+  }
+
+  // Returns true if all queries have completed.
+  bool IsAllComplete() const {
+    EXPECT_TRUE(finalized_);
+    EXPECT_UI_THREAD();
+
+    return (manual_complete_count_ + auto_complete_count_ ==
+            static_cast<int>(test_query_vector_.size()));
+  }
+
+  bool HasAutoQueries() const {
+    return (manual_total_ != static_cast<int>(test_query_vector_.size()));
+  }
+
+ private:
+  struct TestQuery {
+    explicit TestQuery(TestType test_type)
+        : type(test_type),
+          browser_id(0),
+          frame_id(0),
+          is_main_frame(false),
+          query_id(0) {}
+
+    TestType type;
+
+    // Set in OnQuery and verified in OnNotify or OnQueryCanceled.
+    int browser_id;
+    int64 frame_id;
+    bool is_main_frame;
+
+    // Used when a query is canceled.
+    int64 query_id;
+    CefRefPtr<Callback> callback;
+
+    TrackCallback got_query;
+    TrackCallback got_query_canceled;
+    TrackCallback got_success;
+    TrackCallback got_error;
+  };
+
+  class NotifyTask : public CefTask {
+   public:
+    NotifyTask(base::WeakPtr<MultiQueryManager> weak_ptr, bool notify_all)
+        : weak_ptr_(weak_ptr), notify_all_(notify_all) {}
+
+    void Execute() override {
+      if (weak_ptr_) {
+        if (notify_all_)
+          weak_ptr_->NotifyAllQueriesCompleted();
+        else
+          weak_ptr_->NotifyManualQueriesCompleted();
+      }
+    }
+
+   private:
+    base::WeakPtr<MultiQueryManager> weak_ptr_;
+    const bool notify_all_;
+
+    IMPLEMENT_REFCOUNTING(NotifyTask);
+  };
+
+  static bool IsAuto(TestType type) {
+    return (type == AUTOCANCEL || type == PERSISTENT_AUTOCANCEL);
+  }
+
+  static bool IsPersistent(TestType type) {
+    return (type == PERSISTENT_SUCCESS || type == PERSISTENT_FAILURE ||
+            type == PERSISTENT_AUTOCANCEL);
+  }
+
+  static bool WillFail(TestType type) {
+    return (type == FAILURE || type == PERSISTENT_FAILURE);
+  }
+
+  static bool WillCancel(TestType type) {
+    return (type == PERSISTENT_SUCCESS || type == CANCEL ||
+            type == AUTOCANCEL || type == PERSISTENT_AUTOCANCEL);
+  }
+
+  static bool WillNotify(TestType type) {
+    return (type == SUCCESS || type == PERSISTENT_SUCCESS || type == FAILURE ||
+            type == PERSISTENT_FAILURE || type == PERSISTENT_AUTOCANCEL);
+  }
+
+  void OnReceiveCompleted(TestType type) {
+    const int total_count = static_cast<int>(test_query_vector_.size());
+    if (++received_count_ == total_count && manual_total_ == 0) {
+      // There aren't any manual queries so notify here.
+      CefPostTask(TID_UI,
+                  new NotifyTask(weak_ptr_factory_.GetWeakPtr(), false));
+    }
+  }
+
+  void OnQueryCompleted(TestType type) {
+    const int total_count = static_cast<int>(test_query_vector_.size());
+    EXPECT_LT(manual_complete_count_ + auto_complete_count_, total_count);
+    EXPECT_LE(manual_complete_count_, manual_total_);
+
+    const bool is_auto = IsAuto(type);
+    if (is_auto)
+      auto_complete_count_++;
+    else if (++manual_complete_count_ == manual_total_) {
+      CefPostTask(TID_UI,
+                  new NotifyTask(weak_ptr_factory_.GetWeakPtr(), false));
+    }
+
+    if (auto_complete_count_ + manual_complete_count_ == total_count) {
+      running_ = false;
+      CefPostTask(TID_UI, new NotifyTask(weak_ptr_factory_.GetWeakPtr(), true));
+    }
+  }
+
+  void NotifyManualQueriesCompleted() {
+    if (observer_set_.empty())
+      return;
+
+    // Use a copy of the set in case an Observer is removed while we're
+    // iterating.
+    ObserverSet observer_set = observer_set_;
+
+    ObserverSet::const_iterator it = observer_set.begin();
+    for (; it != observer_set.end(); ++it) {
+      (*it)->OnManualQueriesCompleted(this);
+    }
+  }
+
+  void NotifyAllQueriesCompleted() {
+    if (observer_set_.empty())
+      return;
+
+    // Use a copy of the set in case an Observer is removed while we're
+    // iterating.
+    ObserverSet observer_set = observer_set_;
+
+    ObserverSet::const_iterator it = observer_set.begin();
+    for (; it != observer_set.end(); ++it) {
+      (*it)->OnAllQueriesCompleted(this);
+    }
+  }
+
+  std::string GetQueryHTML(const int index, const TestQuery& query) const {
+    const std::string& request_id_var =
+        GetIDString(kMultiQueryRequestId, index);
+    const std::string& repeat_ct_var = GetIDString(kMultiQueryRepeatCt, index);
+    const std::string& request_val =
+        GetIDString(std::string(kMultiQueryRequest) + ":", index);
+    const std::string& success_val =
+        GetIDString(std::string(kMultiQuerySuccess) + ":", index);
+    const std::string& error_val =
+        GetIDString(std::string(kMultiQueryError) + ":", index);
+
+    std::string html;
+
+    const bool persistent = IsPersistent(query.type);
+
+    if (persistent)
+      html += "var " + repeat_ct_var + " = 0;\n";
+
+    html += "var " + request_id_var +
+            " = window.mrtQuery({\n"
+            "  request: '" +
+            request_val +
+            "',\n"
+            "  persistent: " +
+            (persistent ? "true" : "false") + ",\n";
+
+    if (query.type == SUCCESS) {
+      const std::string& response_val = GetIDString(kMultiQueryResponse, index);
+
+      html +=
+          "  onSuccess: function(response) {\n"
+          "    if (response == '" +
+          response_val +
+          "')\n"
+          "      window.mrtNotify('" +
+          success_val +
+          "');\n"
+          "    else\n"
+          "      window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  },\n"
+          "  onFailure: function(error_code, error_message) {\n"
+          "    window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  }\n";
+    } else if (query.type == FAILURE) {
+      const std::string& error_code_val = GetIntString(index);
+      const std::string& error_message_val =
+          GetIDString(kMultiQueryErrorMessage, index);
+
+      html +=
+          "  onSuccess: function(response) {\n"
+          "    window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  },\n"
+          "  onFailure: function(error_code, error_message) {\n"
+          "    if (error_code == " +
+          error_code_val + " && error_message == '" + error_message_val +
+          "')\n"
+          "      window.mrtNotify('" +
+          success_val +
+          "');\n"
+          "    else\n"
+          "      window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  }\n";
+    } else if (query.type == PERSISTENT_SUCCESS ||
+               query.type == PERSISTENT_AUTOCANCEL) {
+      const std::string& response_val = GetIDString(kMultiQueryResponse, index);
+      const std::string& repeat_ct =
+          GetIntString(kMultiQueryPersistentResponseCount);
+
+      html +=
+          "  onSuccess: function(response) {\n"
+          "    if (response == '" +
+          response_val +
+          "') {\n"
+          // Should get repeat_ct number of successful responses.
+          "      if (++" +
+          repeat_ct_var + " == " + repeat_ct +
+          ") {\n"
+          "        window.mrtNotify('" +
+          success_val + "');\n";
+
+      if (query.type == PERSISTENT_SUCCESS) {
+        // Manually cancel the request.
+        html += "        window.mrtQueryCancel(" + request_id_var + ");\n";
+      }
+
+      html +=
+          "      }\n"
+          "    } else {\n"
+          "      window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "    }\n"
+          "  },\n"
+          "  onFailure: function(error_code, error_message) {\n"
+          "    window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  }\n";
+    } else if (query.type == PERSISTENT_FAILURE) {
+      const std::string& error_code_val = GetIntString(index);
+      const std::string& error_message_val =
+          GetIDString(kMultiQueryErrorMessage, index);
+      const std::string& repeat_ct =
+          GetIntString(kMultiQueryPersistentResponseCount);
+
+      html +=
+          "  onSuccess: function(response) {\n"
+          // Should get some successful responses before failure.
+          "    if (++" +
+          repeat_ct_var + " > " + repeat_ct +
+          ") {\n"
+          "      window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "    }\n"
+          "  },\n"
+          "  onFailure: function(error_code, error_message) {\n"
+          "    if (error_code == " +
+          error_code_val + " && error_message == '" + error_message_val +
+          "'"
+          " && " +
+          repeat_ct_var + " == " + repeat_ct +
+          ")\n"
+          "      window.mrtNotify('" +
+          success_val +
+          "');\n"
+          "    else\n"
+          "      window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  }\n";
+    } else if (query.type == CANCEL || query.type == AUTOCANCEL) {
+      html +=
+          "  onSuccess: function(response) {\n"
+          "    window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  },\n"
+          "  onFailure: function(error_code, error_message) {\n"
+          "    window.mrtNotify('" +
+          error_val +
+          "');\n"
+          "  }\n";
+    }
+
+    html += "});\n";
+
+    return html;
+  }
+
+  std::string GetCancelHTML(const int index, const TestQuery& query) const {
+    const std::string& request_id_var =
+        GetIDString(kMultiQueryRequestId, index);
+    return "window.mrtQueryCancel(" + request_id_var + ");\n";
+  }
+
+  std::string GetIDString(const std::string& prefix, int index) const {
+    EXPECT_TRUE(!prefix.empty());
+    std::stringstream ss;
+    ss << prefix << GetIDFromIndex(index);
+    return ss.str();
+  }
+
+  bool SplitIDString(const std::string& str,
+                     std::string* value,
+                     int* index) const {
+    size_t pos = str.find(':');
+    if (pos != std::string::npos) {
+      *value = str.substr(0, pos);
+      *index = GetIndexFromID(atoi(str.substr(pos + 1).c_str()));
+      return (*index >= 0 &&
+              *index < static_cast<int>(test_query_vector_.size()));
+    }
+
+    return false;
+  }
+
+  std::string GetIntString(int val) const {
+    std::stringstream ss;
+    ss << val;
+    return ss.str();
+  }
+
+  int GetIDFromIndex(int index) const { return id_offset_ + index; }
+  int GetIndexFromID(int id) const { return id - id_offset_; }
+
+  const std::string label_;
+  const bool synchronous_;
+  const int id_offset_;
+
+  typedef std::vector<TestQuery> TestQueryVector;
+  TestQueryVector test_query_vector_;
+
+  typedef std::set<Observer*> ObserverSet;
+  ObserverSet observer_set_;
+
+  // Set to true after all queries have been added.
+  bool finalized_;
+  // Set to true while queries are pending.
+  bool running_;
+
+  // Total number of queries that will manually complete.
+  int manual_total_;
+
+  // Number of queries that have been received.
+  int received_count_;
+
+  // Number of queries that have completed successfully.
+  int manual_complete_count_;
+  int auto_complete_count_;
+
+  // If true any pending queries will receive an onFailure callback in addition
+  // to be canceled.
+  bool will_cancel_by_removing_handler_;
+
+  // Should always be the last member.
+  base::WeakPtrFactory<MultiQueryManager> weak_ptr_factory_;
+};
+
+void MakeTestQueries(MultiQueryManager* manager,
+                     bool some,
+                     int many_count = 200) {
+  if (some) {
+    // Test some queries of arbitrary types.
+    // Use a hard-coded list so the behavior is deterministic across test runs.
+    MultiQueryManager::TestType types[] = {
+        MultiQueryManager::PERSISTENT_AUTOCANCEL,
+        MultiQueryManager::SUCCESS,
+        MultiQueryManager::AUTOCANCEL,
+        MultiQueryManager::PERSISTENT_FAILURE,
+        MultiQueryManager::CANCEL,
+        MultiQueryManager::FAILURE,
+        MultiQueryManager::AUTOCANCEL,
+        MultiQueryManager::SUCCESS,
+        MultiQueryManager::PERSISTENT_SUCCESS,
+        MultiQueryManager::SUCCESS,
+        MultiQueryManager::PERSISTENT_AUTOCANCEL,
+        MultiQueryManager::CANCEL,
+        MultiQueryManager::PERSISTENT_SUCCESS,
+        MultiQueryManager::FAILURE,
+    };
+    for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
+      manager->AddTestQuery(types[i]);
+    }
+  } else {
+    // Test every type of query.
+    for (int i = 0; i < many_count; ++i) {
+      MultiQueryManager::TestType type = MultiQueryManager::SUCCESS;
+      switch (i % 7) {
+        case 0:
+          type = MultiQueryManager::SUCCESS;
+          break;
+        case 1:
+          type = MultiQueryManager::FAILURE;
+          break;
+        case 2:
+          type = MultiQueryManager::PERSISTENT_SUCCESS;
+          break;
+        case 3:
+          type = MultiQueryManager::PERSISTENT_FAILURE;
+          break;
+        case 4:
+          type = MultiQueryManager::CANCEL;
+          break;
+        case 5:
+          type = MultiQueryManager::AUTOCANCEL;
+          break;
+        case 6:
+          type = MultiQueryManager::PERSISTENT_AUTOCANCEL;
+          break;
+      }
+      manager->AddTestQuery(type);
+    }
+  }
+  manager->Finalize();
+}
+
+// Test multiple queries in a single page load with a single frame.
+class MultiQuerySingleFrameTestHandler : public SingleLoadTestHandler,
+                                         public MultiQueryManager::Observer {
+ public:
+  enum CancelType {
+    CANCEL_BY_NAVIGATION,
+    CANCEL_BY_REMOVING_HANDLER,
+    CANCEL_BY_CLOSING_BROWSER,
+  };
+
+  MultiQuerySingleFrameTestHandler(
+      bool synchronous,
+      CancelType cancel_type = CANCEL_BY_NAVIGATION)
+      : manager_(std::string(), synchronous), cancel_type_(cancel_type) {
+    manager_.AddObserver(this);
+  }
+
+  std::string GetMainHTML() override { return manager_.GetHTML(true, true); }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    manager_.OnNotify(browser, frame, message);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    return manager_.OnQuery(browser, frame, query_id, request, persistent,
+                            callback);
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    manager_.OnQueryCanceled(browser, frame, query_id);
+  }
+
+  void OnManualQueriesCompleted(MultiQueryManager* manager) override {
+    EXPECT_EQ(manager, &manager_);
+    if (manager_.HasAutoQueries()) {
+      if (cancel_type_ == CANCEL_BY_NAVIGATION) {
+        // Navigate somewhere else to terminate the auto queries.
+        GetBrowser()->GetMainFrame()->LoadURL(std::string(kTestDomain1) +
+                                              "cancel.html");
+      } else if (cancel_type_ == CANCEL_BY_REMOVING_HANDLER) {
+        // Change the expected behavior in the manager.
+        manager_.WillCancelByRemovingHandler();
+        GetRouter()->RemoveHandler(this);
+        // All queries should be immediately canceled.
+        AssertQueryCount(nullptr, nullptr, 0);
+      } else if (cancel_type_ == CANCEL_BY_CLOSING_BROWSER) {
+        // Change the expected behavior in the handler.
+        SetSignalCompletionWhenAllBrowsersClose(false);
+        CloseBrowser(GetBrowser(), false);
+      }
+    }
+  }
+
+  void OnAllQueriesCompleted(MultiQueryManager* manager) override {
+    EXPECT_EQ(manager, &manager_);
+
+    // All queries should be canceled.
+    AssertQueryCount(nullptr, nullptr, 0);
+
+    DestroyTest();
+
+    if (!SignalCompletionWhenAllBrowsersClose()) {
+      // Complete asynchronously so the call stack has a chance to unwind.
+      CefPostTask(
+          TID_UI,
+          base::Bind(&MultiQuerySingleFrameTestHandler::TestComplete, this));
+    }
+  }
+
+  void DestroyTest() override {
+    manager_.AssertAllComplete();
+    TestHandler::DestroyTest();
+  }
+
+  MultiQueryManager* GetManager() { return &manager_; }
+
+ private:
+  MultiQueryManager manager_;
+  const CancelType cancel_type_;
+};
+
+}  // namespace
+
+#define MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(name, type, synchronous) \
+  TEST(MessageRouterTest, name) {                                   \
+    CefRefPtr<MultiQuerySingleFrameTestHandler> handler =           \
+        new MultiQuerySingleFrameTestHandler(synchronous);          \
+    MultiQueryManager* manager = handler->GetManager();             \
+    manager->AddTestQuery(MultiQueryManager::type);                 \
+    manager->Finalize();                                            \
+    handler->ExecuteTest();                                         \
+    ReleaseAndWaitForDestructor(handler);                           \
+  }
+
+// Test the query types individually.
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncSuccess,
+                                   SUCCESS,
+                                   true)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncSuccess,
+                                   SUCCESS,
+                                   false)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncFailure,
+                                   FAILURE,
+                                   true)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncFailure,
+                                   FAILURE,
+                                   false)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentSuccess,
+                                   PERSISTENT_SUCCESS,
+                                   true)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentSuccess,
+                                   PERSISTENT_SUCCESS,
+                                   false)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentFailure,
+                                   PERSISTENT_FAILURE,
+                                   true)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentFailure,
+                                   PERSISTENT_FAILURE,
+                                   false)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameCancel, CANCEL, true)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAutoCancel,
+                                   AUTOCANCEL,
+                                   true)
+MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFramePersistentAutoCancel,
+                                   PERSISTENT_AUTOCANCEL,
+                                   true)
+
+// Test that one frame can run some queries successfully in a synchronous
+// manner.
+TEST(MessageRouterTest, MultiQuerySingleFrameSyncSome) {
+  CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
+      new MultiQuerySingleFrameTestHandler(true);
+  MakeTestQueries(handler->GetManager(), true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that one frame can run some queries successfully in an asynchronous
+// manner.
+TEST(MessageRouterTest, MultiQuerySingleFrameAsyncSome) {
+  CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
+      new MultiQuerySingleFrameTestHandler(false);
+  MakeTestQueries(handler->GetManager(), true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that one frame can run many queries successfully in a synchronous
+// manner.
+TEST(MessageRouterTest, MultiQuerySingleFrameSyncMany) {
+  CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
+      new MultiQuerySingleFrameTestHandler(true);
+  MakeTestQueries(handler->GetManager(), false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that one frame can run many queries successfully in an asynchronous
+// manner.
+TEST(MessageRouterTest, MultiQuerySingleFrameAsyncMany) {
+  CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
+      new MultiQuerySingleFrameTestHandler(false);
+  MakeTestQueries(handler->GetManager(), false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that pending queries can be canceled by removing the handler.
+TEST(MessageRouterTest, MultiQuerySingleFrameCancelByRemovingHandler) {
+  CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
+      new MultiQuerySingleFrameTestHandler(
+          false, MultiQuerySingleFrameTestHandler::CANCEL_BY_REMOVING_HANDLER);
+  MakeTestQueries(handler->GetManager(), false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that pending queries can be canceled by closing the browser.
+TEST(MessageRouterTest, MultiQuerySingleFrameCancelByClosingBrowser) {
+  CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
+      new MultiQuerySingleFrameTestHandler(
+          false, MultiQuerySingleFrameTestHandler::CANCEL_BY_CLOSING_BROWSER);
+  MakeTestQueries(handler->GetManager(), false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// Test multiple handlers.
+class MultiQueryMultiHandlerTestHandler : public SingleLoadTestHandler,
+                                          public MultiQueryManager::Observer {
+ public:
+  class Handler : public CefMessageRouterBrowserSide::Handler {
+   public:
+    Handler(MultiQueryMultiHandlerTestHandler* test_handler, int index)
+        : test_handler_(test_handler), index_(index), query_id_(0) {}
+
+    bool OnQuery(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int64 query_id,
+                 const CefString& request,
+                 bool persistent,
+                 CefRefPtr<Callback> callback) override {
+      // Each handler only handles a single request.
+      std::stringstream ss;
+      ss << kMultiQueryRequest << ":" << index_;
+      const std::string& handled_request = ss.str();
+      if (request != handled_request)
+        return false;
+
+      // Verify that handlers are called in the correct order.
+      if (index_ == 0) {
+        EXPECT_FALSE(test_handler_->got_query0_);
+        EXPECT_FALSE(test_handler_->got_query1_);
+        EXPECT_FALSE(test_handler_->got_query2_);
+
+        test_handler_->got_query0_.yes();
+      } else if (index_ == 1) {
+        EXPECT_TRUE(test_handler_->got_query0_);
+        EXPECT_FALSE(test_handler_->got_query1_);
+        EXPECT_FALSE(test_handler_->got_query2_);
+
+        test_handler_->got_query1_.yes();
+      } else if (index_ == 2) {
+        EXPECT_TRUE(test_handler_->got_query0_);
+        EXPECT_TRUE(test_handler_->got_query1_);
+        EXPECT_FALSE(test_handler_->got_query2_);
+
+        test_handler_->got_query2_.yes();
+      }
+
+      query_id_ = query_id;
+      return test_handler_->OnQuery(browser, frame, query_id, request,
+                                    persistent, callback);
+    }
+
+    void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         int64 query_id) override {
+      // Verify that the correct handler is called for cancellation.
+      EXPECT_EQ(query_id_, query_id);
+
+      if (index_ == 0) {
+        EXPECT_FALSE(test_handler_->got_query_canceled0_);
+        test_handler_->got_query_canceled0_.yes();
+      } else if (index_ == 1) {
+        EXPECT_FALSE(test_handler_->got_query_canceled1_);
+        test_handler_->got_query_canceled1_.yes();
+      } else if (index_ == 2) {
+        EXPECT_FALSE(test_handler_->got_query_canceled2_);
+        test_handler_->got_query_canceled2_.yes();
+      }
+
+      test_handler_->OnQueryCanceled(browser, frame, query_id);
+    }
+
+   private:
+    MultiQueryMultiHandlerTestHandler* test_handler_;
+    const int index_;
+    int query_id_;
+  };
+
+  MultiQueryMultiHandlerTestHandler(bool synchronous,
+                                    bool cancel_by_removing_handler)
+      : manager_(std::string(), synchronous, 0),
+        handler0_(this, 0),
+        handler1_(this, 1),
+        handler2_(this, 2),
+        cancel_by_removing_handler_(cancel_by_removing_handler) {
+    manager_.AddObserver(this);
+
+    // Each handler will handle one of the queries.
+    manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL);
+    manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL);
+    manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL);
+    manager_.Finalize();
+  }
+
+  std::string GetMainHTML() override { return manager_.GetHTML(true, true); }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    manager_.OnNotify(browser, frame, message);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    return manager_.OnQuery(browser, frame, query_id, request, persistent,
+                            callback);
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    AssertMainBrowser(browser);
+    AssertMainFrame(frame);
+
+    manager_.OnQueryCanceled(browser, frame, query_id);
+  }
+
+  void OnManualQueriesCompleted(MultiQueryManager* manager) override {
+    EXPECT_EQ(manager, &manager_);
+
+    EXPECT_TRUE(got_query0_);
+    EXPECT_TRUE(got_query1_);
+    EXPECT_TRUE(got_query2_);
+    EXPECT_FALSE(got_query_canceled0_);
+    EXPECT_FALSE(got_query_canceled1_);
+    EXPECT_FALSE(got_query_canceled2_);
+
+    EXPECT_TRUE(manager_.HasAutoQueries());
+
+    CefRefPtr<CefMessageRouterBrowserSide> router = GetRouter();
+
+    // Remove one handler to cancel a query.
+
+    if (cancel_by_removing_handler_) {
+      manager_.WillCancelByRemovingHandler();
+
+      // Each query should be canceled as the handler is removed.
+      EXPECT_TRUE(router->RemoveHandler(&handler1_));
+      EXPECT_FALSE(got_query_canceled0_);
+      EXPECT_TRUE(got_query_canceled1_);
+      EXPECT_FALSE(got_query_canceled2_);
+
+      EXPECT_TRUE(router->RemoveHandler(&handler2_));
+      EXPECT_FALSE(got_query_canceled0_);
+      EXPECT_TRUE(got_query_canceled2_);
+
+      EXPECT_TRUE(router->RemoveHandler(&handler0_));
+      EXPECT_TRUE(got_query_canceled0_);
+    } else {
+      GetBrowser()->GetMainFrame()->LoadURL(std::string(kTestDomain1) +
+                                            "cancel.html");
+    }
+  }
+
+  void OnAllQueriesCompleted(MultiQueryManager* manager) override {
+    EXPECT_EQ(manager, &manager_);
+
+    // All queries should be canceled.
+    AssertQueryCount(nullptr, nullptr, 0);
+
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_query0_);
+    EXPECT_TRUE(got_query1_);
+    EXPECT_TRUE(got_query2_);
+    EXPECT_TRUE(got_query_canceled0_);
+    EXPECT_TRUE(got_query_canceled1_);
+    EXPECT_TRUE(got_query_canceled2_);
+
+    manager_.AssertAllComplete();
+    TestHandler::DestroyTest();
+  }
+
+ protected:
+  void AddHandlers(
+      CefRefPtr<CefMessageRouterBrowserSide> message_router) override {
+    // OnQuery call order will verify that the first/last ordering works as
+    // expected.
+    EXPECT_TRUE(message_router->AddHandler(&handler1_, true));
+    EXPECT_TRUE(message_router->AddHandler(&handler0_, true));
+    EXPECT_TRUE(message_router->AddHandler(&handler2_, false));
+
+    // Can't add the same handler multiple times.
+    EXPECT_FALSE(message_router->AddHandler(&handler1_, true));
+  }
+
+ private:
+  MultiQueryManager manager_;
+  Handler handler0_;
+  Handler handler1_;
+  Handler handler2_;
+
+  const bool cancel_by_removing_handler_;
+
+  TrackCallback got_query0_;
+  TrackCallback got_query1_;
+  TrackCallback got_query2_;
+
+  TrackCallback got_query_canceled0_;
+  TrackCallback got_query_canceled1_;
+  TrackCallback got_query_canceled2_;
+};
+
+}  // namespace
+
+// Test that multiple handlers behave correctly.
+TEST(MessageRouterTest, MultiQueryMultiHandler) {
+  CefRefPtr<MultiQueryMultiHandlerTestHandler> handler =
+      new MultiQueryMultiHandlerTestHandler(false, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple handlers behave correctly. Cancel by removing the
+// handlers.
+TEST(MessageRouterTest, MultiQueryMultiHandlerCancelByRemovingHandler) {
+  CefRefPtr<MultiQueryMultiHandlerTestHandler> handler =
+      new MultiQueryMultiHandlerTestHandler(false, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// Map of managers on a per-URL basis.
+class MultiQueryManagerMap : public CefMessageRouterBrowserSide::Handler,
+                             public MultiQueryManager::Observer {
+ public:
+  class Observer {
+   public:
+    // Called when all manual queries are complete.
+    virtual void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) {}
+
+    // Called when all queries are complete.
+    virtual void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) {}
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  MultiQueryManagerMap()
+      : finalized_(false),
+        running_(false),
+        manual_complete_count_(0),
+        total_complete_count_(0) {}
+
+  virtual ~MultiQueryManagerMap() { RemoveAllManagers(); }
+
+  void AddObserver(Observer* observer) {
+    EXPECT_FALSE(running_);
+    observer_set_.insert(observer);
+  }
+
+  void RemoveObserver(Observer* observer) {
+    EXPECT_FALSE(running_);
+    EXPECT_TRUE(observer_set_.erase(observer));
+  }
+
+  MultiQueryManager* CreateManager(const std::string& url, bool synchronous) {
+    EXPECT_FALSE(finalized_);
+
+    MultiQueryManager* manager = new MultiQueryManager(
+        url, synchronous, static_cast<int>(manager_map_.size()) * 1000);
+    manager->AddObserver(this);
+    all_managers_.push_back(manager);
+    pending_managers_.push_back(manager);
+
+    return manager;
+  }
+
+  void Finalize() {
+    EXPECT_FALSE(finalized_);
+    finalized_ = true;
+  }
+
+  std::string GetMainHTML() const {
+    EXPECT_TRUE(finalized_);
+    EXPECT_FALSE(running_);
+
+    std::string html = "<html><body>\n";
+
+    for (size_t i = 0; i < all_managers_.size(); ++i) {
+      const std::string& url = all_managers_[i]->label();
+      const std::string& name = GetNameForURL(url);
+      html += "<iframe id=\"" + name + "\" src=\"" + url + "\"></iframe>\n";
+    }
+
+    html += "</body></html>";
+    return html;
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) {
+    EXPECT_TRUE(finalized_);
+    if (!running_)
+      running_ = true;
+
+    MultiQueryManager* manager = GetManager(browser, frame);
+    manager->OnNotify(browser, frame, message);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    EXPECT_TRUE(finalized_);
+    if (!running_)
+      running_ = true;
+
+    MultiQueryManager* manager = GetManager(browser, frame);
+    return manager->OnQuery(browser, frame, query_id, request, persistent,
+                            callback);
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    EXPECT_TRUE(finalized_);
+    if (!running_)
+      running_ = true;
+
+    MultiQueryManager* manager = GetManager(browser, frame);
+    manager->OnQueryCanceled(browser, frame, query_id);
+  }
+
+  void OnManualQueriesCompleted(MultiQueryManager* manager) override {
+    const int size = static_cast<int>(all_managers_.size());
+    EXPECT_LT(manual_complete_count_, size);
+    if (++manual_complete_count_ == size) {
+      running_ = false;
+
+      // Notify observers.
+      if (!observer_set_.empty()) {
+        // Use a copy of the set in case an Observer is removed while we're
+        // iterating.
+        ObserverSet observer_set = observer_set_;
+
+        ObserverSet::const_iterator it = observer_set.begin();
+        for (; it != observer_set.end(); ++it) {
+          (*it)->OnMapManualQueriesCompleted(this);
+        }
+      }
+    }
+  }
+
+  void OnAllQueriesCompleted(MultiQueryManager* manager) override {
+    const int size = static_cast<int>(all_managers_.size());
+    EXPECT_LT(total_complete_count_, size);
+    if (++total_complete_count_ == size) {
+      running_ = false;
+
+      // Notify observers.
+      if (!observer_set_.empty()) {
+        // Use a copy of the set in case an Observer is removed while we're
+        // iterating.
+        ObserverSet observer_set = observer_set_;
+
+        ObserverSet::const_iterator it = observer_set.begin();
+        for (; it != observer_set.end(); ++it) {
+          (*it)->OnMapAllQueriesCompleted(this);
+        }
+      }
+    }
+  }
+
+  bool AllComplete() const {
+    EXPECT_TRUE(finalized_);
+
+    for (size_t i = 0; i < all_managers_.size(); ++i) {
+      if (!all_managers_[i]->IsAllComplete())
+        return false;
+    }
+    return true;
+  }
+
+  void AssertAllComplete() const {
+    EXPECT_TRUE(finalized_);
+    EXPECT_TRUE(pending_managers_.empty());
+    EXPECT_FALSE(running_);
+
+    for (size_t i = 0; i < all_managers_.size(); ++i) {
+      all_managers_[i]->AssertAllComplete();
+    }
+  }
+
+  bool HasAutoQueries() const {
+    for (size_t i = 0; i < all_managers_.size(); ++i) {
+      if (all_managers_[i]->HasAutoQueries())
+        return true;
+    }
+
+    return false;
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) {
+    if (pending_managers_.empty())
+      return;
+
+    const std::string& expected_url = frame->GetURL();
+    MultiQueryManager* next_manager = nullptr;
+
+    // Find the pending manager that matches the expected URL.
+    ManagerList::iterator it = pending_managers_.begin();
+    for (; it != pending_managers_.end(); ++it) {
+      if ((*it)->label() == expected_url) {
+        next_manager = *it;
+        pending_managers_.erase(it);
+        break;
+      }
+    }
+
+    EXPECT_TRUE(next_manager);
+
+    const int browser_id = browser->GetIdentifier();
+    // Always use the same ID for the main frame.
+    const int64 frame_id = frame->IsMain() ? -1 : frame->GetIdentifier();
+
+    const std::pair<int, int64>& id = std::make_pair(browser_id, frame_id);
+
+    // Remove the currently active manager, if any.
+    ManagerMap::iterator it2 = manager_map_.find(id);
+    if (it2 != manager_map_.end())
+      manager_map_.erase(it2);
+
+    // Add the next manager to the active map.
+    manager_map_.insert(std::make_pair(id, next_manager));
+  }
+
+  MultiQueryManager* GetManager(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame) const {
+    const int browser_id = browser->GetIdentifier();
+    // Always use the same ID for the main frame.
+    const int64 frame_id = frame->IsMain() ? -1 : frame->GetIdentifier();
+
+    // Find the manager in the active map.
+    ManagerMap::const_iterator it =
+        manager_map_.find(std::make_pair(browser_id, frame_id));
+    EXPECT_NE(it, manager_map_.end())
+        << "browser_id = " << browser_id << ", frame_id = " << frame_id;
+    return it->second;
+  }
+
+  void RemoveAllManagers() {
+    EXPECT_TRUE(pending_managers_.empty());
+    if (all_managers_.empty())
+      return;
+
+    for (size_t i = 0; i < all_managers_.size(); ++i) {
+      delete all_managers_[i];
+    }
+    all_managers_.clear();
+    manager_map_.clear();
+  }
+
+  static std::string GetNameForURL(const std::string& url) {
+    // Extract the file name without extension.
+    int pos1 = static_cast<int>(url.rfind("/"));
+    int pos2 = static_cast<int>(url.rfind("."));
+    EXPECT_TRUE(pos1 >= 0 && pos2 >= 0 && pos1 < pos2);
+    return url.substr(pos1 + 1, pos2 - pos1 - 1);
+  }
+
+ private:
+  typedef std::vector<MultiQueryManager*> ManagerList;
+  // Map of (browser ID, frame ID) to manager.
+  typedef std::map<std::pair<int, int64>, MultiQueryManager*> ManagerMap;
+
+  // All managers that have been created.
+  ManagerList all_managers_;
+  // Managers that have not yet associated with a frame.
+  ManagerList pending_managers_;
+  // Managers that are currently active.
+  ManagerMap manager_map_;
+
+  typedef std::set<Observer*> ObserverSet;
+  ObserverSet observer_set_;
+
+  // Set to true after all query managers have been added.
+  bool finalized_;
+  // Set to true while queries are pending.
+  bool running_;
+
+  // Number of managers that have completed.
+  int manual_complete_count_;
+  int total_complete_count_;
+};
+
+// Test multiple queries in a single page load with multiple frames.
+class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler,
+                                        public MultiQueryManagerMap::Observer {
+ public:
+  MultiQueryMultiFrameTestHandler(bool synchronous, bool cancel_with_subnav)
+      : synchronous_(synchronous), cancel_with_subnav_(cancel_with_subnav) {
+    manager_map_.AddObserver(this);
+  }
+
+  void AddOtherResources() override {
+    AddSubFrameResource("sub1");
+    AddSubFrameResource("sub2");
+    AddSubFrameResource("sub3");
+    manager_map_.Finalize();
+
+    if (manager_map_.HasAutoQueries()) {
+      cancel_url_ = std::string(kTestDomain1) + "cancel.html";
+      AddResource(cancel_url_, "<html><body>cancel</body></html>", "text/html");
+    }
+  }
+
+  std::string GetMainHTML() override { return manager_map_.GetMainHTML(); }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    AssertMainBrowser(browser);
+    if (!frame->IsMain())
+      manager_map_.OnLoadStart(browser, frame);
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    AssertMainBrowser(browser);
+    EXPECT_FALSE(frame->IsMain());
+
+    manager_map_.OnNotify(browser, frame, message);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    AssertMainBrowser(browser);
+    EXPECT_FALSE(frame->IsMain());
+
+    return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
+                                callback);
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    AssertMainBrowser(browser);
+    EXPECT_FALSE(frame->IsMain());
+
+    manager_map_.OnQueryCanceled(browser, frame, query_id);
+  }
+
+  void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) override {
+    EXPECT_EQ(map, &manager_map_);
+    if (manager_map_.HasAutoQueries()) {
+      CefRefPtr<CefFrame> frame = GetBrowser()->GetMainFrame();
+
+      // Navigate somewhere else to terminate the auto queries.
+      if (cancel_with_subnav_) {
+        // Navigate each subframe individually.
+        const std::string js = "document.getElementById('sub1').src = '" +
+                               cancel_url_ +
+                               "';"
+                               "document.getElementById('sub2').src = '" +
+                               cancel_url_ +
+                               "';"
+                               "document.getElementById('sub3').src = '" +
+                               cancel_url_ + "';";
+
+        frame->ExecuteJavaScript(js, frame->GetURL(), 0);
+      } else {
+        // Navigate the main frame.
+        frame->LoadURL(cancel_url_);
+      }
+    }
+  }
+
+  void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) override {
+    EXPECT_EQ(map, &manager_map_);
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    manager_map_.AssertAllComplete();
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  void AddSubFrameResource(const std::string& name) {
+    const std::string& url = std::string(kTestDomain1) + name + ".html";
+
+    MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_);
+    MakeTestQueries(manager, false, 100);
+
+    const std::string& html = manager->GetHTML(false, false);
+    AddResource(url, html, "text/html");
+  }
+
+  const bool synchronous_;
+  const bool cancel_with_subnav_;
+
+  MultiQueryManagerMap manager_map_;
+
+  std::string cancel_url_;
+};
+
+}  // namespace
+
+// Test that multiple frames can run many queries successfully in a synchronous
+// manner.
+TEST(MessageRouterTest, MultiQueryMultiFrameSync) {
+  CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
+      new MultiQueryMultiFrameTestHandler(true, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple frames can run many queries successfully in an
+// asynchronous manner.
+TEST(MessageRouterTest, MultiQueryMultiFrameAsync) {
+  CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
+      new MultiQueryMultiFrameTestHandler(false, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple frames can run many queries successfully in a synchronous
+// manner. Cancel auto queries with sub-frame navigation.
+TEST(MessageRouterTest, MultiQueryMultiFrameSyncSubnavCancel) {
+  CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
+      new MultiQueryMultiFrameTestHandler(true, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple frames can run many queries successfully in an
+// asynchronous manner. Cancel auto queries with sub-frame navigation.
+TEST(MessageRouterTest, MultiQueryMultiFrameAsyncSubnavCancel) {
+  CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
+      new MultiQueryMultiFrameTestHandler(false, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// Implementation of MRTestHandler that loads multiple pages and/or browsers and
+// executes multiple queries.
+class MultiQueryMultiLoadTestHandler
+    : public MRTestHandler,
+      public CefMessageRouterBrowserSide::Handler,
+      public MultiQueryManagerMap::Observer,
+      public MultiQueryManager::Observer {
+ public:
+  MultiQueryMultiLoadTestHandler(bool some, bool synchronous)
+      : some_(some), synchronous_(synchronous) {
+    manager_map_.AddObserver(this);
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    manager_map_.OnLoadStart(browser, frame);
+  }
+
+  void OnNotify(CefRefPtr<CefBrowser> browser,
+                CefRefPtr<CefFrame> frame,
+                const std::string& message) override {
+    manager_map_.OnNotify(browser, frame, message);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
+                                callback);
+  }
+
+  void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int64 query_id) override {
+    manager_map_.OnQueryCanceled(browser, frame, query_id);
+  }
+
+  void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) override {
+    EXPECT_EQ(map, &manager_map_);
+    if (manager_map_.HasAutoQueries()) {
+      // Navigate all browsers somewhere else to terminate the auto queries.
+      BrowserMap browser_map;
+      GetAllBrowsers(&browser_map);
+
+      BrowserMap::const_iterator it = browser_map.begin();
+      for (; it != browser_map.end(); ++it) {
+        it->second->GetMainFrame()->LoadURL(cancel_url_);
+      }
+    }
+  }
+
+  void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) override {
+    EXPECT_EQ(map, &manager_map_);
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    manager_map_.AssertAllComplete();
+    TestHandler::DestroyTest();
+  }
+
+ protected:
+  void AddHandlers(
+      CefRefPtr<CefMessageRouterBrowserSide> message_router) override {
+    message_router->AddHandler(this, false);
+  }
+
+  void AddManagedResource(const std::string& url,
+                          bool assert_total,
+                          bool assert_browser) {
+    MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_);
+    manager->AddObserver(this);
+    MakeTestQueries(manager, some_, 75);
+
+    const std::string& html = manager->GetHTML(assert_total, assert_browser);
+    AddResource(url, html, "text/html");
+  }
+
+  void Finalize() {
+    manager_map_.Finalize();
+
+    if (manager_map_.HasAutoQueries()) {
+      cancel_url_ = std::string(kTestDomain1) + "cancel.html";
+      AddResource(cancel_url_, "<html><body>cancel</body></html>", "text/html");
+    }
+  }
+
+  MultiQueryManagerMap manager_map_;
+
+ private:
+  const bool some_;
+  const bool synchronous_;
+
+  std::string cancel_url_;
+};
+
+// Test multiple browsers that send queries at the same time.
+class MultiQueryMultiBrowserTestHandler
+    : public MultiQueryMultiLoadTestHandler {
+ public:
+  MultiQueryMultiBrowserTestHandler(bool synchronous, bool same_origin)
+      : MultiQueryMultiLoadTestHandler(false, synchronous),
+        same_origin_(same_origin) {}
+
+ protected:
+  void RunMRTest() override {
+    const std::string& url1 = std::string(kTestDomain1) + "browser1.html";
+    const std::string& url2 =
+        std::string(same_origin_ ? kTestDomain1 : kTestDomain2) +
+        "browser2.html";
+    const std::string& url3 =
+        std::string(same_origin_ ? kTestDomain1 : kTestDomain3) +
+        "browser3.html";
+
+    AddManagedResource(url1, false, true);
+    AddManagedResource(url2, false, true);
+    AddManagedResource(url3, false, true);
+    Finalize();
+
+    // Create 2 browsers simultaniously.
+    CreateBrowser(url1, nullptr);
+    CreateBrowser(url2, nullptr);
+    CreateBrowser(url3, nullptr);
+  }
+
+ private:
+  bool same_origin_;
+};
+
+}  // namespace
+
+// Test that multiple browsers can query simultaniously from the same origin.
+TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginSync) {
+  CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
+      new MultiQueryMultiBrowserTestHandler(true, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple browsers can query simultaniously from the same origin.
+TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginAsync) {
+  CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
+      new MultiQueryMultiBrowserTestHandler(false, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple browsers can query simultaniously from different origins.
+TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginSync) {
+  CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
+      new MultiQueryMultiBrowserTestHandler(true, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple browsers can query simultaniously from different origins.
+TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginAsync) {
+  CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
+      new MultiQueryMultiBrowserTestHandler(false, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// Test multiple navigations that send queries sequentially.
+class MultiQueryMultiNavigateTestHandler
+    : public MultiQueryMultiLoadTestHandler {
+ public:
+  MultiQueryMultiNavigateTestHandler(bool synchronous, bool same_origin)
+      : MultiQueryMultiLoadTestHandler(false, synchronous),
+        same_origin_(same_origin) {}
+
+  void OnManualQueriesCompleted(MultiQueryManager* manager) override {
+    const std::string& url = manager->label();
+    if (url == url1_)  // 2. Load the 2nd url.
+      GetBrowser()->GetMainFrame()->LoadURL(url2_);
+    else if (url == url2_)  // 3. Load the 3rd url.
+      GetBrowser()->GetMainFrame()->LoadURL(url3_);
+  }
+
+ protected:
+  void RunMRTest() override {
+    url1_ = std::string(kTestDomain1) + "browser1.html";
+    url2_ = std::string(same_origin_ ? kTestDomain1 : kTestDomain2) +
+            "browser2.html";
+    url3_ = std::string(same_origin_ ? kTestDomain1 : kTestDomain3) +
+            "browser3.html";
+
+    AddManagedResource(url1_, true, true);
+    AddManagedResource(url2_, true, true);
+    AddManagedResource(url3_, true, true);
+    Finalize();
+
+    // 1. Load the 1st url.
+    CreateBrowser(url1_, nullptr);
+  }
+
+ private:
+  bool same_origin_;
+
+  std::string url1_;
+  std::string url2_;
+  std::string url3_;
+};
+
+}  // namespace
+
+// Test that multiple navigations can query from the same origin.
+TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginSync) {
+  CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
+      new MultiQueryMultiNavigateTestHandler(true, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple navigations can query from the same origin.
+TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginAsync) {
+  CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
+      new MultiQueryMultiNavigateTestHandler(false, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple navigations can query from different origins.
+TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginSync) {
+  CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
+      new MultiQueryMultiNavigateTestHandler(true, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that multiple navigations can query from different origins.
+TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginAsync) {
+  CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
+      new MultiQueryMultiNavigateTestHandler(false, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/navigation_unittest.cc b/src/tests/ceftests/navigation_unittest.cc
new file mode 100644
index 0000000..b69100c
--- /dev/null
+++ b/src/tests/ceftests/navigation_unittest.cc
@@ -0,0 +1,3546 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+#include <list>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_callback.h"
+#include "include/cef_scheme.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppBrowser;
+using client::ClientAppRenderer;
+
+namespace {
+
+const char kHNav1[] = "http://tests-hnav.com/nav1.html";
+const char kHNav2[] = "http://tests-hnav.com/nav2.html";
+const char kHNav3[] = "http://tests-hnav.com/nav3.html";
+const char kHistoryNavMsg[] = "NavigationTest.HistoryNav";
+const char kHistoryNavTestCmdKey[] = "nav-history-test";
+
+const cef_transition_type_t kTransitionExplicitLoad =
+    static_cast<cef_transition_type_t>(TT_EXPLICIT | TT_DIRECT_LOAD_FLAG);
+
+// TT_FORWARD_BACK_FLAG is added to the original transition flags.
+const cef_transition_type_t kTransitionExplicitForwardBack =
+    static_cast<cef_transition_type_t>(kTransitionExplicitLoad |
+                                       TT_FORWARD_BACK_FLAG);
+
+enum NavAction { NA_LOAD = 1, NA_BACK, NA_FORWARD, NA_CLEAR };
+
+typedef struct {
+  NavAction action;     // What to do
+  const char* target;   // Where to be after navigation
+  bool can_go_back;     // After navigation, can go back?
+  bool can_go_forward;  // After navigation, can go forward?
+} NavListItem;
+
+// Array of navigation actions: X = current page, . = history exists
+static NavListItem kHNavList[] = {
+    // kHNav1 | kHNav2 | kHNav3
+    {NA_LOAD, kHNav1, false, false},    //   X
+    {NA_LOAD, kHNav2, true, false},     //   .        X
+    {NA_BACK, kHNav1, false, true},     //   X        .
+    {NA_FORWARD, kHNav2, true, false},  //   .        X
+    {NA_LOAD, kHNav3, true, false},     //   .        .        X
+    {NA_BACK, kHNav2, true, true},      //   .        X        .
+    // TODO(cef): Enable once ClearHistory is implemented
+    // {NA_CLEAR, kHNav2, false, false},   //            X
+};
+
+#define NAV_LIST_SIZE() (sizeof(kHNavList) / sizeof(NavListItem))
+
+// Renderer side.
+class HistoryNavRendererTest : public ClientAppRenderer::Delegate,
+                               public CefLoadHandler {
+ public:
+  HistoryNavRendererTest() : run_test_(false), nav_(0) {}
+
+  void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override {
+    run_test_ = extra_info->HasKey(kHistoryNavTestCmdKey);
+  }
+
+  CefRefPtr<CefLoadHandler> GetLoadHandler(
+      CefRefPtr<ClientAppRenderer> app) override {
+    if (!run_test_)
+      return nullptr;
+
+    return this;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    const NavListItem& item = kHNavList[nav_];
+
+    const std::string& url = browser->GetMainFrame()->GetURL();
+    EXPECT_STREQ(item.target, url.c_str());
+
+    EXPECT_EQ(item.can_go_back, browser->CanGoBack())
+        << "nav: " << nav_ << " isLoading: " << isLoading;
+    EXPECT_EQ(item.can_go_back, canGoBack)
+        << "nav: " << nav_ << " isLoading: " << isLoading;
+    EXPECT_EQ(item.can_go_forward, browser->CanGoForward())
+        << "nav: " << nav_ << " isLoading: " << isLoading;
+    EXPECT_EQ(item.can_go_forward, canGoForward)
+        << "nav: " << nav_ << " isLoading: " << isLoading;
+
+    if (isLoading) {
+      got_loading_state_start_.yes();
+    } else {
+      got_loading_state_end_.yes();
+      SendTestResultsIfDone(browser, browser->GetMainFrame());
+    }
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    const NavListItem& item = kHNavList[nav_];
+
+    got_load_start_.yes();
+
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(item.target, url.c_str());
+
+    EXPECT_EQ(TT_EXPLICIT, transition_type);
+
+    EXPECT_EQ(item.can_go_back, browser->CanGoBack());
+    EXPECT_EQ(item.can_go_forward, browser->CanGoForward());
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    const NavListItem& item = kHNavList[nav_];
+
+    got_load_end_.yes();
+
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(item.target, url.c_str());
+
+    EXPECT_EQ(item.can_go_back, browser->CanGoBack());
+    EXPECT_EQ(item.can_go_forward, browser->CanGoForward());
+
+    SendTestResultsIfDone(browser, frame);
+  }
+
+ protected:
+  void SendTestResultsIfDone(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefFrame> frame) {
+    if (got_load_end_ && got_loading_state_end_)
+      SendTestResults(browser, frame);
+  }
+
+  // Send the test results.
+  void SendTestResults(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame) {
+    EXPECT_TRUE(got_loading_state_start_);
+    EXPECT_TRUE(got_loading_state_end_);
+    EXPECT_TRUE(got_load_start_);
+    EXPECT_TRUE(got_load_end_);
+
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kHistoryNavMsg);
+    CefRefPtr<CefListValue> args = return_msg->GetArgumentList();
+    EXPECT_TRUE(args.get());
+    EXPECT_TRUE(args->SetInt(0, nav_));
+    EXPECT_TRUE(args->SetBool(1, result));
+    frame->SendProcessMessage(PID_BROWSER, return_msg);
+
+    // Reset the test results for the next navigation.
+    got_loading_state_start_.reset();
+    got_loading_state_end_.reset();
+    got_load_start_.reset();
+    got_load_end_.reset();
+
+    nav_++;
+  }
+
+  bool run_test_;
+  int nav_;
+
+  TrackCallback got_loading_state_start_;
+  TrackCallback got_loading_state_end_;
+  TrackCallback got_load_start_;
+  TrackCallback got_load_end_;
+
+  IMPLEMENT_REFCOUNTING(HistoryNavRendererTest);
+};
+
+class NavigationEntryVisitor : public CefNavigationEntryVisitor {
+ public:
+  NavigationEntryVisitor(int nav, TrackCallback* callback)
+      : nav_(nav),
+        callback_(callback),
+        expected_total_(0),
+        expected_current_index_(-1),
+        expected_forwardback_(),
+        callback_count_(0) {
+    // Determine the expected values.
+    for (int i = 0; i <= nav_; ++i) {
+      if (kHNavList[i].action == NA_LOAD) {
+        expected_total_++;
+        expected_current_index_++;
+      } else if (kHNavList[i].action == NA_BACK) {
+        expected_current_index_--;
+      } else if (kHNavList[i].action == NA_FORWARD) {
+        expected_current_index_++;
+      }
+      expected_forwardback_[expected_current_index_] =
+          (kHNavList[i].action != NA_LOAD);
+    }
+  }
+
+  ~NavigationEntryVisitor() override {
+    EXPECT_EQ(callback_count_, expected_total_);
+    callback_->yes();
+  }
+
+  bool Visit(CefRefPtr<CefNavigationEntry> entry,
+             bool current,
+             int index,
+             int total) override {
+    // Only 3 loads total.
+    EXPECT_LT(index, 3);
+    EXPECT_LE(total, 3);
+
+    EXPECT_EQ((expected_current_index_ == index), current);
+    EXPECT_EQ(callback_count_, index);
+    EXPECT_EQ(expected_total_, total);
+
+    std::string expected_url;
+    std::string expected_title;
+    if (index == 0) {
+      expected_url = kHNav1;
+      expected_title = "Nav1";
+    } else if (index == 1) {
+      expected_url = kHNav2;
+      expected_title = "Nav2";
+    } else if (index == 2) {
+      expected_url = kHNav3;
+      expected_title = "Nav3";
+    }
+
+    EXPECT_TRUE(entry->IsValid());
+    EXPECT_STREQ(expected_url.c_str(), entry->GetURL().ToString().c_str());
+    EXPECT_STREQ(expected_url.c_str(),
+                 entry->GetDisplayURL().ToString().c_str());
+    EXPECT_STREQ(expected_url.c_str(),
+                 entry->GetOriginalURL().ToString().c_str());
+    EXPECT_STREQ(expected_title.c_str(), entry->GetTitle().ToString().c_str());
+
+    const auto transition_type = entry->GetTransitionType();
+    if (expected_forwardback_[index])
+      EXPECT_EQ(kTransitionExplicitForwardBack, transition_type);
+    else
+      EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+
+    EXPECT_FALSE(entry->HasPostData());
+    EXPECT_GT(entry->GetCompletionTime().GetTimeT(), 0);
+    EXPECT_EQ(200, entry->GetHttpStatusCode());
+
+    callback_count_++;
+    return true;
+  }
+
+ private:
+  const int nav_;
+  TrackCallback* callback_;
+  int expected_total_;
+  int expected_current_index_;
+  bool expected_forwardback_[3];  // Only 3 loads total.
+  int callback_count_;
+
+  IMPLEMENT_REFCOUNTING(NavigationEntryVisitor);
+};
+
+// Browser side.
+class HistoryNavTestHandler : public TestHandler {
+ public:
+  HistoryNavTestHandler()
+      : nav_(0),
+        load_end_confirmation_(false),
+        load_state_change_loaded_confirmation_(false),
+        renderer_confirmation_(false) {}
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    AddResource(
+        kHNav1,
+        "<html><head><title>Nav1</title></head><body>Nav1</body></html>",
+        "text/html");
+    AddResource(kHNav2,
+                "<html><head><title>Nav2</title><body>Nav2</body></html>",
+                "text/html");
+    AddResource(kHNav3,
+                "<html><head><title>Nav3</title><body>Nav3</body></html>",
+                "text/html");
+
+    CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
+    extra_info->SetBool(kHistoryNavTestCmdKey, true);
+
+    // Create the browser.
+    CreateBrowser(CefString(), nullptr, extra_info);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void RunNav(CefRefPtr<CefBrowser> browser) {
+    if (nav_ == NAV_LIST_SIZE()) {
+      // End of the nav list.
+      DestroyTest();
+      return;
+    }
+
+    const NavListItem& item = kHNavList[nav_];
+
+    // Perform the action.
+    switch (item.action) {
+      case NA_LOAD:
+        browser->GetMainFrame()->LoadURL(item.target);
+        break;
+      case NA_BACK:
+        browser->GoBack();
+        break;
+      case NA_FORWARD:
+        browser->GoForward();
+        break;
+      case NA_CLEAR:
+        // TODO(cef): Enable once ClearHistory is implemented
+        // browser->GetHost()->ClearHistory();
+        // Not really a navigation action so go to the next one.
+        nav_++;
+        RunNav(browser);
+        break;
+      default:
+        break;
+    }
+  }
+
+  void RunNextNavIfReady(CefRefPtr<CefBrowser> browser) {
+    if (load_end_confirmation_ && load_state_change_loaded_confirmation_ &&
+        renderer_confirmation_) {
+      load_end_confirmation_ = false;
+      load_state_change_loaded_confirmation_ = false;
+      renderer_confirmation_ = false;
+      nav_++;
+      RunNav(browser);
+    }
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    RunNav(browser);
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    const NavListItem& item = kHNavList[nav_];
+
+    got_before_browse_[nav_].yes();
+
+    std::string url = request->GetURL();
+    EXPECT_STREQ(item.target, url.c_str());
+
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+
+    const auto transition_type = request->GetTransitionType();
+    if (item.action == NA_LOAD) {
+      EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+    } else if (item.action == NA_BACK || item.action == NA_FORWARD) {
+      EXPECT_EQ(kTransitionExplicitForwardBack, transition_type);
+    }
+
+    if (nav_ > 0) {
+      const NavListItem& last_item = kHNavList[nav_ - 1];
+      EXPECT_EQ(last_item.can_go_back, browser->CanGoBack());
+      EXPECT_EQ(last_item.can_go_forward, browser->CanGoForward());
+    } else {
+      EXPECT_FALSE(browser->CanGoBack());
+      EXPECT_FALSE(browser->CanGoForward());
+    }
+
+    return false;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    const NavListItem& item = kHNavList[nav_];
+
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+
+    const auto transition_type = request->GetTransitionType();
+    if (item.action == NA_LOAD) {
+      EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+    } else if (item.action == NA_BACK || item.action == NA_FORWARD) {
+      EXPECT_EQ(kTransitionExplicitForwardBack, transition_type);
+    }
+
+    got_before_resource_load_[nav_].yes();
+
+    std::string url = request->GetURL();
+    if (url == item.target)
+      got_correct_target_[nav_].yes();
+
+    return RV_CONTINUE;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    const NavListItem& item = kHNavList[nav_];
+
+    got_loading_state_change_[nav_].yes();
+
+    if (item.can_go_back == canGoBack)
+      got_correct_can_go_back_[nav_].yes();
+    if (item.can_go_forward == canGoForward)
+      got_correct_can_go_forward_[nav_].yes();
+
+    load_state_change_loaded_confirmation_ = true;
+    RunNextNavIfReady(browser);
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    if (browser->IsPopup() || !frame->IsMain())
+      return;
+
+    const NavListItem& item = kHNavList[nav_];
+
+    got_load_start_[nav_].yes();
+
+    if (item.action == NA_LOAD) {
+      EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+    } else if (item.action == NA_BACK || item.action == NA_FORWARD) {
+      EXPECT_EQ(kTransitionExplicitForwardBack, transition_type);
+    }
+
+    std::string url1 = browser->GetMainFrame()->GetURL();
+    std::string url2 = frame->GetURL();
+    if (url1 == item.target && url2 == item.target)
+      got_correct_load_start_url_[nav_].yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (browser->IsPopup() || !frame->IsMain())
+      return;
+
+    const NavListItem& item = kHNavList[nav_];
+
+    got_load_end_[nav_].yes();
+
+    // Test that navigation entries are correct.
+    CefRefPtr<NavigationEntryVisitor> visitor =
+        new NavigationEntryVisitor(nav_, &got_correct_history_[nav_]);
+    browser->GetHost()->GetNavigationEntries(visitor.get(), false);
+    visitor = nullptr;
+
+    std::string url1 = browser->GetMainFrame()->GetURL();
+    std::string url2 = frame->GetURL();
+    if (url1 == item.target && url2 == item.target)
+      got_correct_load_end_url_[nav_].yes();
+
+    load_end_confirmation_ = true;
+    RunNextNavIfReady(browser);
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName().ToString() == kHistoryNavMsg) {
+      got_before_navigation_[nav_].yes();
+
+      // Test that the renderer side succeeded.
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_TRUE(args.get());
+      EXPECT_EQ(nav_, args->GetInt(0));
+      EXPECT_TRUE(args->GetBool(1));
+
+      renderer_confirmation_ = true;
+      RunNextNavIfReady(browser);
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+  int nav_;
+  bool load_end_confirmation_;
+  bool load_state_change_loaded_confirmation_;
+  bool renderer_confirmation_;
+
+  TrackCallback got_before_browse_[NAV_LIST_SIZE()];
+  TrackCallback got_before_navigation_[NAV_LIST_SIZE()];
+  TrackCallback got_before_resource_load_[NAV_LIST_SIZE()];
+  TrackCallback got_correct_target_[NAV_LIST_SIZE()];
+  TrackCallback got_loading_state_change_[NAV_LIST_SIZE()];
+  TrackCallback got_correct_can_go_back_[NAV_LIST_SIZE()];
+  TrackCallback got_correct_can_go_forward_[NAV_LIST_SIZE()];
+  TrackCallback got_load_start_[NAV_LIST_SIZE()];
+  TrackCallback got_correct_load_start_url_[NAV_LIST_SIZE()];
+  TrackCallback got_load_end_[NAV_LIST_SIZE()];
+  TrackCallback got_correct_history_[NAV_LIST_SIZE()];
+  TrackCallback got_correct_load_end_url_[NAV_LIST_SIZE()];
+
+  IMPLEMENT_REFCOUNTING(HistoryNavTestHandler);
+};
+
+}  // namespace
+
+// Verify history navigation.
+TEST(NavigationTest, History) {
+  CefRefPtr<HistoryNavTestHandler> handler = new HistoryNavTestHandler();
+  handler->ExecuteTest();
+
+  for (size_t i = 0; i < NAV_LIST_SIZE(); ++i) {
+    if (kHNavList[i].action != NA_CLEAR) {
+      ASSERT_TRUE(handler->got_before_browse_[i]) << "i = " << i;
+      ASSERT_TRUE(handler->got_before_navigation_[i]) << "i = " << i;
+      ASSERT_TRUE(handler->got_before_resource_load_[i]) << "i = " << i;
+      ASSERT_TRUE(handler->got_correct_target_[i]) << "i = " << i;
+      ASSERT_TRUE(handler->got_load_start_[i]) << "i = " << i;
+      ASSERT_TRUE(handler->got_correct_load_start_url_[i]) << "i = " << i;
+    }
+
+    ASSERT_TRUE(handler->got_loading_state_change_[i]) << "i = " << i;
+    ASSERT_TRUE(handler->got_correct_can_go_back_[i]) << "i = " << i;
+    ASSERT_TRUE(handler->got_correct_can_go_forward_[i]) << "i = " << i;
+
+    if (kHNavList[i].action != NA_CLEAR) {
+      ASSERT_TRUE(handler->got_load_end_[i]) << "i = " << i;
+      ASSERT_TRUE(handler->got_correct_history_[i]) << "i = " << i;
+      ASSERT_TRUE(handler->got_correct_load_end_url_[i]) << "i = " << i;
+    }
+  }
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kDynIfrNav1[] = "http://tests-dynframe/nav1.html";
+const char kDynIfrNav2[] = "http://tests-dynframe/nav2.html";
+
+// Browser side.
+class HistoryDynamicIFramesNavTestHandler : public TestHandler {
+ public:
+  HistoryDynamicIFramesNavTestHandler() : nav_(-1) {}
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    AddResource(kDynIfrNav1,
+                "<html>"
+                " <head>"
+                "  <title>Nav1</title>"
+                "  <script language='javascript'>"
+                "    function onload() {"
+                "      fr = Math.floor(Math.random() * 10);"
+                "      if(fr == 0) "
+                "        fr = 1;"
+                "      console.log('fr=' + fr);"
+                "      for(i = 1; i <= fr; i++) {"
+                "        try {"
+                "          var n = 'DYN_' + Math.floor(Math.random() * 10000);"
+                "  "
+                "          d = document.createElement('div');"
+                "          d.id = 'sf' + i; "
+                "          d.innerText = n; "
+                "          document.body.appendChild(d); "
+                " "
+                "          f = document.createElement('iframe'); "
+                "          f.id = 'f_' + i; "
+                "          f.name = n; "
+                "          f.src = 'nav2.html'; "
+                "          document.body.appendChild(f); "
+                "        } catch(e) { "
+                "          console.log('frame[' + i + ']: ' + e); "
+                "        } "
+                "      } "
+                "    } "
+                "  </script> "
+                " </head> "
+                " <body onload='onload();'> "
+                "  Nav1 "
+                " </body> "
+                "</html>",
+                "text/html");
+    AddResource(
+        kDynIfrNav2,
+        "<html><head><title>Nav2</title></head><body>Nav2</body></html>",
+        "text/html");
+
+    // Create the browser.
+    CreateBrowser(CefString());
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void RunNav(CefRefPtr<CefBrowser> browser) {
+    EXPECT_LE(nav_, 3);
+    EXPECT_FALSE(got_load_start_[nav_]);
+    EXPECT_FALSE(got_load_end_[nav_]);
+
+    if (nav_ == 0) {
+      browser->GetMainFrame()->LoadURL(kDynIfrNav1);
+    } else if (nav_ == 1) {
+      browser->GetMainFrame()->LoadURL(kDynIfrNav2);
+    } else if (nav_ == 2) {
+      browser->GoBack();
+    } else if (nav_ == 3) {
+      browser->Reload();
+    }
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    nav_ = 0;
+    RunNav(browser);
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    if (!frame->IsMain())
+      return;
+    got_load_start_[nav_].yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (!frame->IsMain())
+      return;
+    CefString url = browser->GetMainFrame()->GetURL();
+    got_load_end_[nav_].yes();
+
+    if (nav_ == 3) {
+      EXPECT_STREQ(url.ToString().c_str(), kDynIfrNav1);
+      DestroyTest();
+      return;
+    }
+
+    nav_++;
+    RunNav(browser);
+  }
+
+  int nav_;
+  TrackCallback got_load_start_[4];
+  TrackCallback got_load_end_[4];
+
+  IMPLEMENT_REFCOUNTING(HistoryDynamicIFramesNavTestHandler);
+};
+
+}  // namespace
+
+// Verify history navigation of pages containing dynamically created iframes.
+// See issue #2022 for background.
+TEST(NavigationTest, HistoryDynamicIFrames) {
+  CefRefPtr<HistoryDynamicIFramesNavTestHandler> handler =
+      new HistoryDynamicIFramesNavTestHandler();
+  handler->ExecuteTest();
+
+  for (int i = 0; i < 4; ++i) {
+    EXPECT_TRUE(handler->got_load_start_[i]);
+    EXPECT_TRUE(handler->got_load_end_[i]);
+  }
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kRNav1[] = "http://tests/nav1.html";
+const char kRNav2[] = "http://tests/nav2.html";
+const char kRNav3[] = "http://tests/nav3.html";
+const char kRNav4[] = "http://tests/nav4.html";
+
+bool g_got_nav1_request = false;
+bool g_got_nav3_request = false;
+bool g_got_nav4_request = false;
+bool g_got_invalid_request = false;
+
+class RedirectSchemeHandler : public CefResourceHandler {
+ public:
+  RedirectSchemeHandler() : offset_(0), status_(0) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    std::string url = request->GetURL();
+    if (url == kRNav1) {
+      // Redirect using HTTP 302
+      g_got_nav1_request = true;
+      status_ = 302;
+      location_ = kRNav2;
+      content_ = "<html><body>Redirected Nav1</body></html>";
+    } else if (url == kRNav3) {
+      // Redirect using redirectUrl
+      g_got_nav3_request = true;
+      status_ = -1;
+      location_ = kRNav4;
+      content_ = "<html><body>Redirected Nav3</body></html>";
+    } else if (url == kRNav4) {
+      g_got_nav4_request = true;
+      status_ = 200;
+      content_ = "<html><body>Nav4</body></html>";
+    }
+
+    handle_request = true;
+
+    if (status_ != 0) {
+      // Continue request.
+      return true;
+    }
+
+    // Cancel request.
+    g_got_invalid_request = true;
+    return false;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+
+    EXPECT_NE(status_, 0);
+
+    response->SetStatus(status_);
+    response->SetMimeType("text/html");
+    response_length = content_.size();
+
+    if (status_ == 302) {
+      // Redirect using HTTP 302
+      EXPECT_GT(location_.size(), static_cast<size_t>(0));
+      response->SetStatusText("Found");
+      CefResponse::HeaderMap headers;
+      response->GetHeaderMap(headers);
+      headers.insert(std::make_pair("Location", location_));
+      response->SetHeaderMap(headers);
+    } else if (status_ == -1) {
+      // Rdirect using redirectUrl
+      EXPECT_GT(location_.size(), static_cast<size_t>(0));
+      redirectUrl = location_;
+    }
+  }
+
+  void Cancel() override { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    bytes_read = 0;
+    bool has_data = false;
+
+    size_t size = content_.size();
+    if (offset_ < size) {
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(size - offset_));
+      memcpy(data_out, content_.c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+ protected:
+  std::string content_;
+  size_t offset_;
+  int status_;
+  std::string location_;
+
+  IMPLEMENT_REFCOUNTING(RedirectSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(RedirectSchemeHandler);
+};
+
+class RedirectSchemeHandlerFactory : public CefSchemeHandlerFactory {
+ public:
+  RedirectSchemeHandlerFactory() {
+    g_got_nav1_request = false;
+    g_got_nav3_request = false;
+    g_got_nav4_request = false;
+    g_got_invalid_request = false;
+  }
+
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+    return new RedirectSchemeHandler();
+  }
+
+  IMPLEMENT_REFCOUNTING(RedirectSchemeHandlerFactory);
+  DISALLOW_COPY_AND_ASSIGN(RedirectSchemeHandlerFactory);
+};
+
+class RedirectTestHandler : public TestHandler {
+ public:
+  RedirectTestHandler() {}
+
+  void RunTest() override {
+    // Create the browser.
+    CreateBrowser(kRNav1);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    // Should be called for all but the second URL.
+    std::string url = request->GetURL();
+
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+    EXPECT_EQ(kTransitionExplicitLoad, request->GetTransitionType());
+
+    if (url == kRNav1) {
+      got_nav1_before_resource_load_.yes();
+    } else if (url == kRNav3) {
+      got_nav3_before_resource_load_.yes();
+    } else if (url == kRNav4) {
+      got_nav4_before_resource_load_.yes();
+    } else {
+      got_invalid_before_resource_load_.yes();
+    }
+
+    return RV_CONTINUE;
+  }
+
+  void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response,
+                          CefString& new_url) override {
+    // Should be called for each redirected URL.
+
+    const std::string& old_url = request->GetURL();
+    if (old_url == kRNav1 && new_url == kRNav2) {
+      // Called due to the nav1 redirect response.
+      got_nav1_redirect_.yes();
+
+      EXPECT_EQ(302, response->GetStatus());
+      EXPECT_STREQ("Found", response->GetStatusText().ToString().c_str());
+      EXPECT_STREQ("", response->GetMimeType().ToString().c_str());
+      EXPECT_STREQ(kRNav2,
+                   response->GetHeaderByName("Location").ToString().c_str());
+
+      // Change the redirect to the 3rd URL.
+      new_url = kRNav3;
+    } else if (old_url == kRNav1 && new_url == kRNav3) {
+      // Called due to the redirect change above.
+      got_nav2_redirect_.yes();
+
+      EXPECT_EQ(307, response->GetStatus());
+      EXPECT_STREQ("Internal Redirect",
+                   response->GetStatusText().ToString().c_str());
+      EXPECT_TRUE(response->GetMimeType().empty());
+      EXPECT_STREQ(kRNav3,
+                   response->GetHeaderByName("Location").ToString().c_str());
+    } else if (old_url == kRNav3 && new_url == kRNav4) {
+      // Called due to the nav3 redirect response.
+      got_nav3_redirect_.yes();
+
+      EXPECT_EQ(307, response->GetStatus());
+      EXPECT_STREQ("Temporary Redirect",
+                   response->GetStatusText().ToString().c_str());
+      EXPECT_STREQ("", response->GetMimeType().ToString().c_str());
+    } else {
+      got_invalid_redirect_.yes();
+    }
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    // Should only be called for the final loaded URL.
+    std::string url = frame->GetURL();
+
+    EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+
+    if (url == kRNav4) {
+      got_nav4_load_start_.yes();
+    } else {
+      got_invalid_load_start_.yes();
+    }
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    // Should only be called for the final loaded URL.
+    std::string url = frame->GetURL();
+
+    if (url == kRNav4) {
+      got_nav4_load_end_.yes();
+      DestroyTest();
+    } else {
+      got_invalid_load_end_.yes();
+    }
+  }
+
+  TrackCallback got_nav1_before_resource_load_;
+  TrackCallback got_nav3_before_resource_load_;
+  TrackCallback got_nav4_before_resource_load_;
+  TrackCallback got_invalid_before_resource_load_;
+  TrackCallback got_nav4_load_start_;
+  TrackCallback got_invalid_load_start_;
+  TrackCallback got_nav4_load_end_;
+  TrackCallback got_invalid_load_end_;
+  TrackCallback got_nav1_redirect_;
+  TrackCallback got_nav2_redirect_;
+  TrackCallback got_nav3_redirect_;
+  TrackCallback got_invalid_redirect_;
+
+  IMPLEMENT_REFCOUNTING(RedirectTestHandler);
+};
+
+// Like above but destroy the WebContents while the redirect is in-progress.
+class RedirectDestroyTestHandler : public TestHandler {
+ public:
+  RedirectDestroyTestHandler() {}
+
+  void RunTest() override {
+    // Create the browser.
+    CreateBrowser(kRNav1);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response,
+                          CefString& new_url) override {
+    const std::string& old_url = request->GetURL();
+    if (old_url == kRNav1 && new_url == kRNav2) {
+      // Called due to the nav1 redirect response.
+      got_nav1_redirect_.yes();
+
+      new_url = "about:blank";
+
+      // Destroy the test (and the underlying WebContents) while the redirect
+      // is still pending.
+      DestroyTest();
+    }
+  }
+
+  TrackCallback got_nav1_redirect_;
+
+  IMPLEMENT_REFCOUNTING(RedirectDestroyTestHandler);
+};
+
+}  // namespace
+
+// Verify frame names and identifiers.
+TEST(NavigationTest, Redirect) {
+  CefRegisterSchemeHandlerFactory("http", "tests",
+                                  new RedirectSchemeHandlerFactory());
+  WaitForIOThread();
+
+  CefRefPtr<RedirectTestHandler> handler = new RedirectTestHandler();
+  handler->ExecuteTest();
+
+  CefClearSchemeHandlerFactories();
+  WaitForIOThread();
+
+  ASSERT_TRUE(handler->got_nav1_before_resource_load_);
+  ASSERT_TRUE(handler->got_nav3_before_resource_load_);
+  ASSERT_TRUE(handler->got_nav4_before_resource_load_);
+  ASSERT_FALSE(handler->got_invalid_before_resource_load_);
+  ASSERT_TRUE(handler->got_nav4_load_start_);
+  ASSERT_FALSE(handler->got_invalid_load_start_);
+  ASSERT_TRUE(handler->got_nav4_load_end_);
+  ASSERT_FALSE(handler->got_invalid_load_end_);
+  ASSERT_TRUE(handler->got_nav1_redirect_);
+  ASSERT_FALSE(handler->got_nav2_redirect_);
+  ASSERT_TRUE(handler->got_nav3_redirect_);
+  ASSERT_FALSE(handler->got_invalid_redirect_);
+  ASSERT_TRUE(g_got_nav1_request);
+  ASSERT_TRUE(g_got_nav3_request);
+  ASSERT_TRUE(g_got_nav4_request);
+  ASSERT_FALSE(g_got_invalid_request);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify that destroying the WebContents while the redirect is in-progress does
+// not result in a crash.
+TEST(NavigationTest, RedirectDestroy) {
+  CefRegisterSchemeHandlerFactory("http", "tests",
+                                  new RedirectSchemeHandlerFactory());
+  WaitForIOThread();
+
+  CefRefPtr<RedirectDestroyTestHandler> handler =
+      new RedirectDestroyTestHandler();
+  handler->ExecuteTest();
+
+  CefClearSchemeHandlerFactories();
+  WaitForIOThread();
+
+  ASSERT_TRUE(handler->got_nav1_redirect_);
+  ASSERT_TRUE(g_got_nav1_request);
+  ASSERT_FALSE(g_got_nav3_request);
+  ASSERT_FALSE(g_got_nav4_request);
+  ASSERT_FALSE(g_got_invalid_request);
+
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char KONav1[] = "http://tests-onav.com/nav1.html";
+const char KONav2[] = "http://tests-onav.com/nav2.html";
+const char kOrderNavMsg[] = "NavigationTest.OrderNav";
+const char kOrderNavClosedMsg[] = "NavigationTest.OrderNavClosed";
+const char kOrderNavTestCmdKey[] = "nav-order-test";
+
+void SetOrderNavExtraInfo(CefRefPtr<CefListValue> extra_info) {
+  // Arbitrary data for testing.
+  extra_info->SetBool(0, true);
+  CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
+  dict->SetInt("key1", 5);
+  dict->SetString("key2", "test string");
+  extra_info->SetDictionary(1, dict);
+  extra_info->SetDouble(2, 5.43322);
+  extra_info->SetString(3, "some string");
+}
+
+// Browser side.
+class OrderNavBrowserTest : public ClientAppBrowser::Delegate {
+ public:
+  OrderNavBrowserTest() {}
+
+  void OnRenderProcessThreadCreated(
+      CefRefPtr<ClientAppBrowser> app,
+      CefRefPtr<CefListValue> extra_info) override {
+    // Some data that we'll check for. Note that this leaks into all renderer
+    // process test cases, but that shouldn't be an issue since we only check
+    // the result in this test case.
+    SetOrderNavExtraInfo(extra_info);
+  }
+
+ protected:
+  IMPLEMENT_REFCOUNTING(OrderNavBrowserTest);
+};
+
+class OrderNavLoadState {
+ public:
+  OrderNavLoadState(bool is_popup, bool browser_side)
+      : is_popup_(is_popup), browser_side_(browser_side) {}
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) {
+    if (isLoading) {
+      EXPECT_TRUE(Verify(false, false, false, false));
+
+      got_loading_state_start_.yes();
+    } else {
+      EXPECT_TRUE(Verify(true, false, true, true));
+
+      got_loading_state_end_.yes();
+    }
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) {
+    EXPECT_TRUE(Verify(true, false, false, false));
+
+    got_load_start_.yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) {
+    EXPECT_TRUE(Verify(true, false, true, false));
+
+    got_load_end_.yes();
+  }
+
+  bool IsStarted() const {
+    return got_loading_state_start_ || got_loading_state_end_ ||
+           got_load_start_ || got_load_end_;
+  }
+
+  bool IsDone() const {
+    return got_loading_state_start_ && got_loading_state_end_ &&
+           got_load_start_ && got_load_end_;
+  }
+
+  bool Verify(bool got_loading_state_start,
+              bool got_loading_state_end,
+              bool got_load_start,
+              bool got_load_end) const {
+    EXPECT_EQ(got_loading_state_start, got_loading_state_start_)
+        << "Popup: " << is_popup_ << "; Browser Side: " << browser_side_;
+    EXPECT_EQ(got_loading_state_end, got_loading_state_end_)
+        << "Popup: " << is_popup_ << "; Browser Side: " << browser_side_;
+    EXPECT_EQ(got_load_start, got_load_start_)
+        << "Popup: " << is_popup_ << "; Browser Side: " << browser_side_;
+    EXPECT_EQ(got_load_end, got_load_end_)
+        << "Popup: " << is_popup_ << "; Browser Side: " << browser_side_;
+
+    return got_loading_state_start == got_loading_state_start_ &&
+           got_loading_state_end == got_loading_state_end_ &&
+           got_load_start == got_load_start_ && got_load_end == got_load_end_;
+  }
+
+ private:
+  bool is_popup_;
+  bool browser_side_;
+
+  TrackCallback got_loading_state_start_;
+  TrackCallback got_loading_state_end_;
+  TrackCallback got_load_start_;
+  TrackCallback got_load_end_;
+};
+
+// Renderer side.
+class OrderNavRendererTest : public ClientAppRenderer::Delegate,
+                             public CefLoadHandler {
+ public:
+  OrderNavRendererTest()
+      : run_test_(false),
+        browser_id_main_(0),
+        browser_id_popup_(0),
+        state_main_(false, false),
+        state_popup_(true, false) {}
+
+  void OnRenderThreadCreated(CefRefPtr<ClientAppRenderer> app,
+                             CefRefPtr<CefListValue> extra_info) override {
+    EXPECT_FALSE(got_render_thread_created_);
+    EXPECT_FALSE(got_webkit_initialized_);
+
+    got_render_thread_created_.yes();
+
+    // Verify that |extra_info| transferred successfully.
+    CefRefPtr<CefListValue> expected = CefListValue::Create();
+    SetOrderNavExtraInfo(expected);
+    TestListEqual(expected, extra_info);
+  }
+
+  void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) override {
+    EXPECT_TRUE(got_render_thread_created_);
+    EXPECT_FALSE(got_webkit_initialized_);
+
+    got_webkit_initialized_.yes();
+  }
+
+  void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override {
+    run_test_ = extra_info->HasKey(kOrderNavTestCmdKey);
+    if (!run_test_)
+      return;
+
+    EXPECT_TRUE(got_render_thread_created_);
+    EXPECT_TRUE(got_webkit_initialized_);
+
+    if (browser->IsPopup()) {
+      EXPECT_FALSE(got_browser_created_popup_);
+      EXPECT_FALSE(got_browser_destroyed_popup_);
+      EXPECT_FALSE(state_popup_.IsStarted());
+
+      got_browser_created_popup_.yes();
+      browser_id_popup_ = browser->GetIdentifier();
+      EXPECT_GT(browser->GetIdentifier(), 0);
+    } else {
+      EXPECT_FALSE(got_browser_created_main_);
+      EXPECT_FALSE(got_browser_destroyed_main_);
+      EXPECT_FALSE(state_main_.IsStarted());
+
+      got_browser_created_main_.yes();
+      browser_id_main_ = browser->GetIdentifier();
+      EXPECT_GT(browser->GetIdentifier(), 0);
+
+      browser_main_ = browser;
+    }
+  }
+
+  void OnBrowserDestroyed(CefRefPtr<ClientAppRenderer> app,
+                          CefRefPtr<CefBrowser> browser) override {
+    if (!run_test_)
+      return;
+
+    EXPECT_TRUE(got_render_thread_created_);
+    EXPECT_TRUE(got_webkit_initialized_);
+
+    if (browser->IsPopup()) {
+      EXPECT_TRUE(got_browser_created_popup_);
+      EXPECT_FALSE(got_browser_destroyed_popup_);
+      EXPECT_TRUE(state_popup_.IsDone());
+
+      got_browser_destroyed_popup_.yes();
+      EXPECT_EQ(browser_id_popup_, browser->GetIdentifier());
+      EXPECT_GT(browser->GetIdentifier(), 0);
+
+      // Use |browser_main_| to send the message otherwise it will fail.
+      SendTestResults(browser_main_, browser_main_->GetMainFrame(),
+                      kOrderNavClosedMsg);
+    } else {
+      EXPECT_TRUE(got_browser_created_main_);
+      EXPECT_FALSE(got_browser_destroyed_main_);
+      EXPECT_TRUE(state_main_.IsDone());
+
+      got_browser_destroyed_main_.yes();
+      EXPECT_EQ(browser_id_main_, browser->GetIdentifier());
+      EXPECT_GT(browser->GetIdentifier(), 0);
+
+      browser_main_ = nullptr;
+    }
+  }
+
+  CefRefPtr<CefLoadHandler> GetLoadHandler(
+      CefRefPtr<ClientAppRenderer> app) override {
+    if (!run_test_)
+      return nullptr;
+
+    return this;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    EXPECT_TRUE(got_render_thread_created_);
+    EXPECT_TRUE(got_webkit_initialized_);
+
+    if (browser->IsPopup()) {
+      EXPECT_TRUE(got_browser_created_popup_);
+      EXPECT_FALSE(got_browser_destroyed_popup_);
+
+      state_popup_.OnLoadingStateChange(browser, isLoading, canGoBack,
+                                        canGoForward);
+    } else {
+      EXPECT_TRUE(got_browser_created_main_);
+      EXPECT_FALSE(got_browser_destroyed_main_);
+
+      state_main_.OnLoadingStateChange(browser, isLoading, canGoBack,
+                                       canGoForward);
+    }
+
+    if (!isLoading)
+      SendTestResultsIfDone(browser, browser->GetMainFrame());
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    EXPECT_TRUE(got_render_thread_created_);
+    EXPECT_TRUE(got_webkit_initialized_);
+
+    if (browser->IsPopup()) {
+      EXPECT_TRUE(got_browser_created_popup_);
+      EXPECT_FALSE(got_browser_destroyed_popup_);
+
+      state_popup_.OnLoadStart(browser, frame);
+    } else {
+      EXPECT_TRUE(got_browser_created_main_);
+      EXPECT_FALSE(got_browser_destroyed_main_);
+
+      state_main_.OnLoadStart(browser, frame);
+    }
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_TRUE(got_render_thread_created_);
+    EXPECT_TRUE(got_webkit_initialized_);
+
+    if (browser->IsPopup()) {
+      EXPECT_TRUE(got_browser_created_popup_);
+      EXPECT_FALSE(got_browser_destroyed_popup_);
+
+      state_popup_.OnLoadEnd(browser, frame, httpStatusCode);
+    } else {
+      EXPECT_TRUE(got_browser_created_main_);
+      EXPECT_FALSE(got_browser_destroyed_main_);
+
+      state_main_.OnLoadEnd(browser, frame, httpStatusCode);
+    }
+
+    SendTestResultsIfDone(browser, frame);
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    ADD_FAILURE() << "renderer OnLoadError url: " << failedUrl.ToString()
+                  << " error: " << errorCode;
+  }
+
+ protected:
+  void SendTestResultsIfDone(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefFrame> frame) {
+    bool done = false;
+    if (browser->IsPopup())
+      done = state_popup_.IsDone();
+    else
+      done = state_main_.IsDone();
+
+    if (done)
+      SendTestResults(browser, frame, kOrderNavMsg);
+  }
+
+  // Send the test results.
+  void SendTestResults(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       const char* msg_name) {
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(msg_name);
+    CefRefPtr<CefListValue> args = return_msg->GetArgumentList();
+    EXPECT_TRUE(args.get());
+    EXPECT_TRUE(args->SetBool(0, result));
+    if (browser->IsPopup())
+      EXPECT_TRUE(args->SetInt(1, browser_id_popup_));
+    else
+      EXPECT_TRUE(args->SetInt(1, browser_id_main_));
+    frame->SendProcessMessage(PID_BROWSER, return_msg);
+  }
+
+  bool run_test_;
+
+  int browser_id_main_;
+  int browser_id_popup_;
+  CefRefPtr<CefBrowser> browser_main_;
+  TrackCallback got_render_thread_created_;
+  TrackCallback got_webkit_initialized_;
+  TrackCallback got_browser_created_main_;
+  TrackCallback got_browser_destroyed_main_;
+  TrackCallback got_browser_created_popup_;
+  TrackCallback got_browser_destroyed_popup_;
+
+  OrderNavLoadState state_main_;
+  OrderNavLoadState state_popup_;
+
+  IMPLEMENT_REFCOUNTING(OrderNavRendererTest);
+};
+
+// Browser side.
+class OrderNavTestHandler : public TestHandler {
+ public:
+  OrderNavTestHandler()
+      : browser_id_main_(0),
+        browser_id_popup_(0),
+        state_main_(false, true),
+        state_popup_(true, true),
+        got_message_(false) {}
+
+  // Returns state that will be checked in the renderer process via
+  // OrderNavRendererTest::OnBrowserCreated.
+  CefRefPtr<CefDictionaryValue> GetExtraInfo() {
+    CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
+    extra_info->SetBool(kOrderNavTestCmdKey, true);
+    return extra_info;
+  }
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    AddResource(KONav1, "<html>Nav1</html>", "text/html");
+    AddResource(KONav2, "<html>Nav2</html>", "text/html");
+
+    // Create the browser.
+    CreateBrowser(KONav1, nullptr, GetExtraInfo());
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void ContinueIfReady(CefRefPtr<CefBrowser> browser) {
+    if (!got_message_)
+      return;
+
+    bool done = false;
+    if (browser->IsPopup())
+      done = state_popup_.IsDone();
+    else
+      done = state_main_.IsDone();
+    if (!done)
+      return;
+
+    got_message_ = false;
+
+    if (!browser->IsPopup()) {
+      // Create the popup window.
+      browser->GetMainFrame()->ExecuteJavaScript(
+          "window.open('" + std::string(KONav2) + "');", CefString(), 0);
+    } else {
+      // Close the popup window.
+      CloseBrowser(browser_popup_, false);
+    }
+  }
+
+  bool OnBeforePopup(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      const CefString& target_url,
+      const CefString& target_frame_name,
+      CefLifeSpanHandler::WindowOpenDisposition target_disposition,
+      bool user_gesture,
+      const CefPopupFeatures& popupFeatures,
+      CefWindowInfo& windowInfo,
+      CefRefPtr<CefClient>& client,
+      CefBrowserSettings& settings,
+      CefRefPtr<CefDictionaryValue>& extra_info,
+      bool* no_javascript_access) override {
+    extra_info = GetExtraInfo();
+    return false;
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    if (browser->IsPopup()) {
+      browser_id_popup_ = browser->GetIdentifier();
+      EXPECT_GT(browser_id_popup_, 0);
+      browser_popup_ = browser;
+    } else {
+      browser_id_main_ = browser->GetIdentifier();
+      EXPECT_GT(browser_id_main_, 0);
+    }
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+
+    if (browser->IsPopup()) {
+      EXPECT_EQ(TT_LINK, request->GetTransitionType());
+      EXPECT_GT(browser->GetIdentifier(), 0);
+      EXPECT_EQ(browser_id_popup_, browser->GetIdentifier());
+      got_before_browse_popup_.yes();
+    } else {
+      EXPECT_EQ(kTransitionExplicitLoad, request->GetTransitionType());
+      EXPECT_GT(browser->GetIdentifier(), 0);
+      EXPECT_EQ(browser_id_main_, browser->GetIdentifier());
+      got_before_browse_main_.yes();
+    }
+
+    std::string url = request->GetURL();
+    if (url == KONav1)
+      EXPECT_FALSE(browser->IsPopup());
+    else if (url == KONav2)
+      EXPECT_TRUE(browser->IsPopup());
+    else
+      EXPECT_TRUE(false);  // not reached
+
+    return false;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+
+    if (browser->IsPopup()) {
+      EXPECT_EQ(TT_LINK, request->GetTransitionType());
+      EXPECT_GT(browser->GetIdentifier(), 0);
+      EXPECT_EQ(browser_id_popup_, browser->GetIdentifier());
+    } else {
+      EXPECT_EQ(kTransitionExplicitLoad, request->GetTransitionType());
+      EXPECT_GT(browser->GetIdentifier(), 0);
+      EXPECT_EQ(browser_id_main_, browser->GetIdentifier());
+    }
+
+    return RV_CONTINUE;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (browser->IsPopup()) {
+      state_popup_.OnLoadingStateChange(browser, isLoading, canGoBack,
+                                        canGoForward);
+    } else {
+      state_main_.OnLoadingStateChange(browser, isLoading, canGoBack,
+                                       canGoForward);
+    }
+
+    if (!isLoading)
+      ContinueIfReady(browser);
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    if (browser->IsPopup()) {
+      EXPECT_EQ(TT_LINK, transition_type);
+      state_popup_.OnLoadStart(browser, frame);
+    } else {
+      EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+      state_main_.OnLoadStart(browser, frame);
+    }
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (browser->IsPopup()) {
+      state_popup_.OnLoadEnd(browser, frame, httpStatusCode);
+    } else {
+      state_main_.OnLoadEnd(browser, frame, httpStatusCode);
+    }
+
+    ContinueIfReady(browser);
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    ADD_FAILURE() << "browser OnLoadError url: " << failedUrl.ToString()
+                  << " error: " << errorCode;
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (browser->IsPopup()) {
+      EXPECT_GT(browser->GetIdentifier(), 0);
+      EXPECT_EQ(browser_id_popup_, browser->GetIdentifier());
+    } else {
+      EXPECT_GT(browser->GetIdentifier(), 0);
+      EXPECT_EQ(browser_id_main_, browser->GetIdentifier());
+    }
+
+    const std::string& msg_name = message->GetName();
+    if (msg_name == kOrderNavMsg || msg_name == kOrderNavClosedMsg) {
+      // Test that the renderer side succeeded.
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_TRUE(args.get());
+      EXPECT_TRUE(args->GetBool(0));
+
+      if (browser->IsPopup()) {
+        EXPECT_EQ(browser_id_popup_, args->GetInt(1));
+      } else {
+        EXPECT_EQ(browser_id_main_, args->GetInt(1));
+      }
+
+      if (msg_name == kOrderNavMsg) {
+        // Continue with the test.
+        got_message_ = true;
+        ContinueIfReady(browser);
+      } else {
+        // Popup was closed. End the test.
+        browser_popup_ = nullptr;
+        DestroyTest();
+      }
+
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+ protected:
+  void DestroyTest() override {
+    // Verify test expectations.
+    EXPECT_TRUE(got_before_browse_main_);
+    EXPECT_TRUE(got_before_browse_popup_);
+
+    EXPECT_TRUE(state_main_.Verify(true, true, true, true));
+    EXPECT_TRUE(state_popup_.Verify(true, true, true, true));
+
+    TestHandler::DestroyTest();
+  }
+
+  int browser_id_main_;
+  int browser_id_popup_;
+  CefRefPtr<CefBrowser> browser_popup_;
+
+  TrackCallback got_before_browse_main_;
+  TrackCallback got_before_browse_popup_;
+
+  OrderNavLoadState state_main_;
+  OrderNavLoadState state_popup_;
+
+  bool got_message_;
+
+  IMPLEMENT_REFCOUNTING(OrderNavTestHandler);
+};
+
+}  // namespace
+
+// Verify the order of navigation-related callbacks.
+TEST(NavigationTest, Order) {
+  CefRefPtr<OrderNavTestHandler> handler = new OrderNavTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kLoadNav1[] = "http://tests-conav1.com/nav1.html";
+const char kLoadNavSameOrigin2[] = "http://tests-conav1.com/nav2.html";
+const char kLoadNavCrossOrigin2[] = "http://tests-conav2.com/nav2.html";
+const char kLoadNavMsg[] = "NavigationTest.LoadNav";
+const char kLoadNavTestCmdKey[] = "nav-load-test";
+
+// Renderer side.
+class LoadNavRendererTest : public ClientAppRenderer::Delegate,
+                            public CefLoadHandler {
+ public:
+  LoadNavRendererTest() : run_test_(false), browser_id_(0), load_ct_(0) {}
+  ~LoadNavRendererTest() override { EXPECT_EQ(0, browser_id_); }
+
+  void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override {
+    run_test_ = extra_info->HasKey(kLoadNavTestCmdKey);
+    if (!run_test_)
+      return;
+
+    EXPECT_EQ(0, browser_id_);
+    browser_id_ = browser->GetIdentifier();
+    EXPECT_GT(browser_id_, 0);
+    got_browser_created_.yes();
+  }
+
+  void OnBrowserDestroyed(CefRefPtr<ClientAppRenderer> app,
+                          CefRefPtr<CefBrowser> browser) override {
+    if (!run_test_)
+      return;
+
+    EXPECT_TRUE(got_browser_created_);
+    EXPECT_TRUE(got_loading_state_end_);
+
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    browser_id_ = 0;
+  }
+
+  CefRefPtr<CefLoadHandler> GetLoadHandler(
+      CefRefPtr<ClientAppRenderer> app) override {
+    if (!run_test_)
+      return nullptr;
+
+    return this;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (!isLoading) {
+      EXPECT_TRUE(got_browser_created_);
+
+      got_loading_state_end_.yes();
+
+      EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+      load_ct_++;
+      SendTestResults(browser, browser->GetMainFrame());
+    }
+  }
+
+ protected:
+  // Send the test results.
+  void SendTestResults(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame) {
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kLoadNavMsg);
+    CefRefPtr<CefListValue> args = return_msg->GetArgumentList();
+    EXPECT_TRUE(args.get());
+    EXPECT_TRUE(args->SetBool(0, result));
+    EXPECT_TRUE(args->SetInt(1, browser->GetIdentifier()));
+    EXPECT_TRUE(args->SetInt(2, load_ct_));
+    frame->SendProcessMessage(PID_BROWSER, return_msg);
+  }
+
+  bool run_test_;
+
+  int browser_id_;
+  int load_ct_;
+  TrackCallback got_browser_created_;
+  TrackCallback got_loading_state_end_;
+
+  IMPLEMENT_REFCOUNTING(LoadNavRendererTest);
+};
+
+// Browser side.
+class LoadNavTestHandler : public TestHandler {
+ public:
+  enum TestMode {
+    LOAD,
+    LEFT_CLICK,
+    MIDDLE_CLICK,
+    CTRL_LEFT_CLICK,
+  };
+
+  LoadNavTestHandler(TestMode mode,
+                     bool same_origin,
+                     bool cancel_in_open_url = false)
+      : mode_(mode),
+        same_origin_(same_origin),
+        cancel_in_open_url_(cancel_in_open_url),
+        browser_id_current_(0),
+        renderer_load_ct_(0) {}
+
+  std::string GetURL2() const {
+    return same_origin_ ? kLoadNavSameOrigin2 : kLoadNavCrossOrigin2;
+  }
+
+  bool ExpectOpenURL() const {
+    return mode_ == MIDDLE_CLICK || mode_ == CTRL_LEFT_CLICK;
+  }
+
+  void RunTest() override {
+    const std::string& url2 = GetURL2();
+    std::string link;
+    if (mode_ != LOAD)
+      link = "<a href=\"" + url2 + "\">CLICK ME</a>";
+
+    // Add the resources that we will navigate to/from.
+    AddResource(kLoadNav1,
+                "<html><body><h1>" + link + "Nav1</h1></body></html>",
+                "text/html");
+    AddResource(url2, "<html>Nav2</html>", "text/html");
+
+    CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
+    extra_info->SetBool(kLoadNavTestCmdKey, true);
+
+    // Create the browser.
+    CreateBrowser(kLoadNav1, nullptr, extra_info);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void ContinueIfReady(CefRefPtr<CefBrowser> browser) {
+    if (!got_message_ || !got_load_end_)
+      return;
+
+    std::string url = browser->GetMainFrame()->GetURL();
+    if (url == kLoadNav1) {
+      // Verify the behavior of the previous load.
+      EXPECT_TRUE(got_before_browse_);
+      EXPECT_TRUE(got_before_resource_load_);
+      EXPECT_TRUE(got_load_start_);
+      EXPECT_TRUE(got_load_end_);
+      EXPECT_FALSE(got_open_url_from_tab_);
+
+      got_before_browse_.reset();
+      got_before_resource_load_.reset();
+      got_load_start_.reset();
+      got_load_end_.reset();
+      got_message_.reset();
+
+      EXPECT_EQ(1, renderer_load_ct_);
+
+      // Load the next url.
+      if (mode_ == LOAD) {
+        browser->GetMainFrame()->LoadURL(GetURL2());
+      } else {
+        // Navigate to the URL by clicking a link.
+        CefMouseEvent mouse_event;
+        mouse_event.x = 20;
+        mouse_event.y = 20;
+#if defined(OS_MACOSX)
+        // Use cmd instead of ctrl on OS X.
+        mouse_event.modifiers =
+            (mode_ == CTRL_LEFT_CLICK ? EVENTFLAG_COMMAND_DOWN : 0);
+#else
+        mouse_event.modifiers =
+            (mode_ == CTRL_LEFT_CLICK ? EVENTFLAG_CONTROL_DOWN : 0);
+#endif
+
+        cef_mouse_button_type_t button_type =
+            (mode_ == MIDDLE_CLICK ? MBT_MIDDLE : MBT_LEFT);
+        browser->GetHost()->SendMouseClickEvent(mouse_event, button_type, false,
+                                                1);
+        browser->GetHost()->SendMouseClickEvent(mouse_event, button_type, true,
+                                                1);
+      }
+
+      if (cancel_in_open_url_) {
+        // The next navigation should not occur. Therefore call DestroyTest()
+        // after a reasonable timeout.
+        CefPostDelayedTask(
+            TID_UI, base::Bind(&LoadNavTestHandler::DestroyTest, this), 500);
+      }
+    } else {
+      // Done with the test.
+      DestroyTest();
+    }
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    EXPECT_EQ(browser_id_current_, 0);
+    browser_id_current_ = browser->GetIdentifier();
+    EXPECT_GT(browser_id_current_, 0);
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+    if (mode_ == LOAD || request->GetURL() == kLoadNav1) {
+      EXPECT_EQ(kTransitionExplicitLoad, request->GetTransitionType());
+      EXPECT_FALSE(user_gesture);
+    } else {
+      EXPECT_EQ(TT_LINK, request->GetTransitionType());
+
+      if (mode_ == LEFT_CLICK) {
+        EXPECT_TRUE(user_gesture);
+      } else {
+        EXPECT_FALSE(user_gesture);
+      }
+    }
+
+    EXPECT_GT(browser_id_current_, 0);
+    EXPECT_EQ(browser_id_current_, browser->GetIdentifier());
+
+    if (ExpectOpenURL() && request->GetURL() == GetURL2()) {
+      // OnOpenURLFromTab should be called first for the file URL navigation.
+      EXPECT_TRUE(got_open_url_from_tab_);
+    } else {
+      EXPECT_FALSE(got_open_url_from_tab_);
+    }
+
+    got_before_browse_.yes();
+
+    return false;
+  }
+
+  bool OnOpenURLFromTab(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        const CefString& target_url,
+                        cef_window_open_disposition_t target_disposition,
+                        bool user_gesture) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+
+    EXPECT_GT(browser_id_current_, 0);
+    EXPECT_EQ(browser_id_current_, browser->GetIdentifier());
+
+    // OnOpenURLFromTab should only be called for the file URL.
+    EXPECT_STREQ(GetURL2().c_str(), target_url.ToString().c_str());
+
+    if (mode_ == LOAD)
+      EXPECT_FALSE(user_gesture);
+    else
+      EXPECT_TRUE(user_gesture);
+
+    EXPECT_EQ(WOD_NEW_BACKGROUND_TAB, target_disposition);
+
+    // OnOpenURLFromTab should be called before OnBeforeBrowse for the file URL.
+    EXPECT_FALSE(got_before_browse_);
+
+    got_open_url_from_tab_.yes();
+
+    return cancel_in_open_url_;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+
+    const auto transition_type = request->GetTransitionType();
+    if (mode_ == LOAD || request->GetURL() == kLoadNav1)
+      EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+    else
+      EXPECT_EQ(TT_LINK, transition_type);
+
+    EXPECT_GT(browser_id_current_, 0);
+    EXPECT_EQ(browser_id_current_, browser->GetIdentifier());
+
+    got_before_resource_load_.yes();
+
+    return RV_CONTINUE;
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    EXPECT_GT(browser_id_current_, 0);
+    EXPECT_EQ(browser_id_current_, browser->GetIdentifier());
+
+    if (mode_ == LOAD || frame->GetURL() == kLoadNav1)
+      EXPECT_EQ(kTransitionExplicitLoad, transition_type);
+    else
+      EXPECT_EQ(TT_LINK, transition_type);
+
+    got_load_start_.yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_GT(browser_id_current_, 0);
+    EXPECT_EQ(browser_id_current_, browser->GetIdentifier());
+
+    got_load_end_.yes();
+    ContinueIfReady(browser);
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    EXPECT_GT(browser_id_current_, 0);
+    EXPECT_EQ(browser_id_current_, browser->GetIdentifier());
+
+    const std::string& msg_name = message->GetName();
+    if (msg_name == kLoadNavMsg) {
+      // Test that the renderer side succeeded.
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_TRUE(args.get());
+      EXPECT_TRUE(args->GetBool(0));
+
+      EXPECT_EQ(browser_id_current_, args->GetInt(1));
+
+      renderer_load_ct_ = args->GetInt(2);
+      EXPECT_GE(renderer_load_ct_, 1);
+
+      // Continue with the test.
+      got_message_.yes();
+      ContinueIfReady(browser);
+
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+  void DestroyTest() override {
+    if (cancel_in_open_url_) {
+      EXPECT_FALSE(got_before_browse_);
+      EXPECT_FALSE(got_before_resource_load_);
+      EXPECT_FALSE(got_load_start_);
+      EXPECT_FALSE(got_load_end_);
+      EXPECT_FALSE(got_message_);
+
+      // We should only navigate a single time if the 2nd load is canceled.
+      EXPECT_EQ(1, renderer_load_ct_);
+    } else {
+      EXPECT_TRUE(got_before_browse_);
+      EXPECT_TRUE(got_before_resource_load_);
+      EXPECT_TRUE(got_load_start_);
+      EXPECT_TRUE(got_load_end_);
+      EXPECT_TRUE(got_message_);
+
+      if (same_origin_) {
+        // The renderer process should always be reused.
+        EXPECT_EQ(2, renderer_load_ct_);
+      } else {
+        // Each renderer process is only used for a single navigation.
+        EXPECT_EQ(1, renderer_load_ct_);
+      }
+    }
+
+    if (ExpectOpenURL())
+      EXPECT_TRUE(got_open_url_from_tab_);
+    else
+      EXPECT_FALSE(got_open_url_from_tab_);
+
+    TestHandler::DestroyTest();
+  }
+
+ protected:
+  const TestMode mode_;
+  const bool same_origin_;
+  const bool cancel_in_open_url_;
+
+  int browser_id_current_;
+  int renderer_load_ct_;
+
+  TrackCallback got_before_browse_;
+  TrackCallback got_open_url_from_tab_;
+  TrackCallback got_before_resource_load_;
+  TrackCallback got_load_start_;
+  TrackCallback got_load_end_;
+  TrackCallback got_message_;
+
+  IMPLEMENT_REFCOUNTING(LoadNavTestHandler);
+};
+
+}  // namespace
+
+// Verify navigation-related callbacks when browsing same-origin via LoadURL().
+TEST(NavigationTest, LoadSameOriginLoadURL) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::LOAD, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify navigation-related callbacks when browsing same-origin via left-click.
+TEST(NavigationTest, LoadSameOriginLeftClick) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::LEFT_CLICK, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify navigation-related callbacks when browsing same-origin via middle-
+// click.
+TEST(NavigationTest, LoadSameOriginMiddleClick) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::MIDDLE_CLICK, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Same as above but cancel the 2nd navigation in OnOpenURLFromTab.
+TEST(NavigationTest, LoadSameOriginMiddleClickCancel) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::MIDDLE_CLICK, true, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify navigation-related callbacks when browsing same-origin via ctrl+left-
+// click.
+TEST(NavigationTest, LoadSameOriginCtrlLeftClick) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::CTRL_LEFT_CLICK, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Same as above but cancel the 2nd navigation in OnOpenURLFromTab.
+TEST(NavigationTest, LoadSameOriginCtrlLeftClickCancel) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::CTRL_LEFT_CLICK, true, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify navigation-related callbacks when browsing cross-origin via LoadURL().
+TEST(NavigationTest, LoadCrossOriginLoadURL) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::LOAD, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify navigation-related callbacks when browsing cross-origin via left-
+// click.
+TEST(NavigationTest, LoadCrossOriginLeftClick) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::LEFT_CLICK, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify navigation-related callbacks when browsing cross-origin via middle-
+// click.
+TEST(NavigationTest, LoadCrossOriginMiddleClick) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::MIDDLE_CLICK, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Same as above but cancel the 2nd navigation in OnOpenURLFromTab.
+TEST(NavigationTest, LoadCrossOriginMiddleClickCancel) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::MIDDLE_CLICK, false, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify navigation-related callbacks when browsing cross-origin via ctrl+left-
+// click.
+TEST(NavigationTest, LoadCrossOriginCtrlLeftClick) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::CTRL_LEFT_CLICK, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Same as above but cancel the 2nd navigation in OnOpenURLFromTab.
+TEST(NavigationTest, LoadCrossOriginCtrlLeftClickCancel) {
+  CefRefPtr<LoadNavTestHandler> handler =
+      new LoadNavTestHandler(LoadNavTestHandler::CTRL_LEFT_CLICK, false, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kSimultPopupMainUrl[] = "http://www.tests-sp.com/main.html";
+const char kSimultPopupPopupUrl[] = "http://www.tests-sp.com/popup";
+const size_t kSimultPopupCount = 5U;
+
+// Test multiple popups simultaniously.
+class PopupSimultaneousTestHandler : public TestHandler {
+ public:
+  explicit PopupSimultaneousTestHandler(bool same_url)
+      : same_url_(same_url),
+        before_popup_ct_(0U),
+        after_created_ct_(0U),
+        before_close_ct_(0U) {}
+
+  void RunTest() override {
+    std::string main_html = "<html><script>\n";
+    for (size_t i = 0; i < kSimultPopupCount; ++i) {
+      if (same_url_) {
+        popup_url_[i] = std::string(kSimultPopupPopupUrl) + ".html";
+      } else {
+        std::stringstream ss;
+        ss << kSimultPopupPopupUrl << i << ".html";
+        popup_url_[i] = ss.str();
+      }
+      main_html += "window.open('" + popup_url_[i] + "');\n";
+      AddResource(popup_url_[i], "<html>Popup " + popup_url_[i] + "</html>",
+                  "text/html");
+    }
+    main_html += "</script></html>";
+
+    AddResource(kSimultPopupMainUrl, main_html, "text/html");
+
+    // Create the browser.
+    CreateBrowser(kSimultPopupMainUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     cef_window_open_disposition_t target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override {
+    const std::string& url = target_url;
+    EXPECT_LT(before_popup_ct_, kSimultPopupCount);
+    EXPECT_STREQ(popup_url_[before_popup_ct_].c_str(), url.c_str())
+        << before_popup_ct_;
+    before_popup_ct_++;
+    return false;
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    if (browser->IsPopup()) {
+      EXPECT_LT(after_created_ct_, kSimultPopupCount);
+      browser_id_[after_created_ct_] = browser->GetIdentifier();
+      after_created_ct_++;
+    }
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    if (browser->IsPopup()) {
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      for (size_t i = 0; i < kSimultPopupCount; ++i) {
+        if (browser->GetIdentifier() == browser_id_[i]) {
+          EXPECT_STREQ(popup_url_[i].c_str(), url.c_str()) << i;
+
+          got_loading_state_change_[i].yes();
+          CloseBrowser(browser, true);
+          return;
+        }
+      }
+      EXPECT_FALSE(true);  // Not reached.
+    }
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnBeforeClose(browser);
+
+    if (browser->IsPopup()) {
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      for (size_t i = 0; i < kSimultPopupCount; ++i) {
+        if (browser->GetIdentifier() == browser_id_[i]) {
+          EXPECT_STREQ(popup_url_[i].c_str(), url.c_str()) << i;
+
+          got_before_close_[i].yes();
+
+          if (++before_close_ct_ == kSimultPopupCount)
+            DestroyTest();
+          return;
+        }
+      }
+      EXPECT_FALSE(true);  // Not reached.
+    }
+  }
+
+ private:
+  void DestroyTest() override {
+    EXPECT_EQ(kSimultPopupCount, before_popup_ct_);
+    EXPECT_EQ(kSimultPopupCount, after_created_ct_);
+    EXPECT_EQ(kSimultPopupCount, before_close_ct_);
+
+    for (size_t i = 0; i < kSimultPopupCount; ++i) {
+      EXPECT_GT(browser_id_[i], 0) << i;
+      EXPECT_TRUE(got_loading_state_change_[i]) << i;
+      EXPECT_TRUE(got_before_close_[i]) << i;
+    }
+
+    TestHandler::DestroyTest();
+  }
+
+  const bool same_url_;
+  std::string popup_url_[kSimultPopupCount];
+  size_t before_popup_ct_;
+  int browser_id_[kSimultPopupCount];
+  size_t after_created_ct_;
+  TrackCallback got_loading_state_change_[kSimultPopupCount];
+  TrackCallback got_before_close_[kSimultPopupCount];
+  size_t before_close_ct_;
+
+  IMPLEMENT_REFCOUNTING(PopupSimultaneousTestHandler);
+};
+
+}  // namespace
+
+// Test simultaneous popups with different URLs.
+TEST(NavigationTest, PopupSimultaneousDifferentUrl) {
+  CefRefPtr<PopupSimultaneousTestHandler> handler =
+      new PopupSimultaneousTestHandler(false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test simultaneous popups with the same URL.
+TEST(NavigationTest, PopupSimultaneousSameUrl) {
+  CefRefPtr<PopupSimultaneousTestHandler> handler =
+      new PopupSimultaneousTestHandler(true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kPopupJSOpenMainUrl[] = "http://www.tests-pjso.com/main.html";
+const char kPopupJSOpenPopupUrl[] = "http://www.tests-pjso.com/popup.html";
+
+// Test a popup where the URL is a JavaScript URI that opens another popup.
+class PopupJSWindowOpenTestHandler : public TestHandler {
+ public:
+  PopupJSWindowOpenTestHandler()
+      : before_popup_ct_(0U),
+        after_created_ct_(0U),
+        load_end_ct_(0U),
+        before_close_ct_(0U) {}
+
+  void RunTest() override {
+    AddResource(kPopupJSOpenMainUrl, "<html>Main</html>", "text/html");
+    AddResource(kPopupJSOpenPopupUrl, "<html>Popup</html>", "text/html");
+
+    // Create the browser.
+    CreateBrowser(kPopupJSOpenMainUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     cef_window_open_disposition_t target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override {
+    before_popup_ct_++;
+    return false;
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    if (browser->IsPopup()) {
+      after_created_ct_++;
+      if (!popup1_)
+        popup1_ = browser;
+      else if (!popup2_)
+        popup2_ = browser;
+      else
+        ADD_FAILURE();
+    }
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    if (browser->IsPopup()) {
+      const std::string& url = browser->GetMainFrame()->GetURL();
+      if (url == kPopupJSOpenPopupUrl) {
+        EXPECT_TRUE(browser->IsSame(popup2_));
+        popup2_ = nullptr;
+
+        // OnLoadingStateChange is not currently called for browser-side
+        // navigations of empty popups. See https://crbug.com/789252.
+        // Explicitly close the empty popup here as a workaround.
+        CloseBrowser(popup1_, true);
+        popup1_ = nullptr;
+      } else {
+        // Empty popup.
+        EXPECT_TRUE(url.empty());
+        EXPECT_TRUE(browser->IsSame(popup1_));
+        popup1_ = nullptr;
+      }
+
+      load_end_ct_++;
+      CloseBrowser(browser, true);
+    } else if (browser->GetMainFrame()->GetURL() == kPopupJSOpenMainUrl) {
+      // Load the problematic JS URI.
+      // This will result in 2 popups being created:
+      // - An empty popup
+      // - A popup that loads kPopupJSOpenPopupUrl
+      browser->GetMainFrame()->LoadURL(
+          "javascript:window.open(\"javascript:window.open('" +
+          std::string(kPopupJSOpenPopupUrl) + "')\")");
+    }
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    ADD_FAILURE() << "OnLoadError url: " << failedUrl.ToString()
+                  << " error: " << errorCode;
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnBeforeClose(browser);
+
+    before_close_ct_++;
+    if (before_close_ct_ == 2U)
+      DestroyTest();
+  }
+
+ private:
+  void DestroyTest() override {
+    EXPECT_EQ(2U, before_popup_ct_);
+    EXPECT_EQ(2U, after_created_ct_);
+    EXPECT_EQ(2U, before_close_ct_);
+
+    // OnLoadingStateChange is not currently called for browser-side
+    // navigations of empty popups. See https://crbug.com/789252.
+    EXPECT_EQ(1U, load_end_ct_);
+
+    TestHandler::DestroyTest();
+  }
+
+  CefRefPtr<CefBrowser> popup1_;
+  CefRefPtr<CefBrowser> popup2_;
+
+  size_t before_popup_ct_;
+  size_t after_created_ct_;
+  size_t load_end_ct_;
+  size_t before_close_ct_;
+
+  IMPLEMENT_REFCOUNTING(PopupJSWindowOpenTestHandler);
+};
+
+}  // namespace
+
+// Test a popup where the URL is a JavaScript URI that opens another popup.
+TEST(NavigationTest, PopupJSWindowOpen) {
+  CefRefPtr<PopupJSWindowOpenTestHandler> handler =
+      new PopupJSWindowOpenTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kPopupJSEmptyMainUrl[] = "http://www.tests-pjse.com/main.html";
+
+// Test creation of a popup where the URL is empty.
+class PopupJSWindowEmptyTestHandler : public TestHandler {
+ public:
+  PopupJSWindowEmptyTestHandler() {}
+
+  void RunTest() override {
+    AddResource(kPopupJSEmptyMainUrl, "<html>Main</html>", "text/html");
+
+    // Create the browser.
+    CreateBrowser(kPopupJSEmptyMainUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     cef_window_open_disposition_t target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override {
+    got_before_popup_.yes();
+    return false;
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    if (browser->IsPopup()) {
+      got_after_created_popup_.yes();
+    }
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    if (browser->IsPopup()) {
+      got_load_end_popup_.yes();
+      CloseBrowser(browser, true);
+    } else {
+      browser->GetMainFrame()->LoadURL("javascript:window.open('')");
+    }
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    ADD_FAILURE() << "OnLoadError url: " << failedUrl.ToString()
+                  << " error: " << errorCode;
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnBeforeClose(browser);
+
+    if (browser->IsPopup()) {
+      got_before_close_popup_.yes();
+      DestroyTest();
+    }
+  }
+
+ private:
+  void DestroyTest() override {
+    EXPECT_TRUE(got_before_popup_);
+    EXPECT_TRUE(got_after_created_popup_);
+    EXPECT_TRUE(got_load_end_popup_);
+    EXPECT_TRUE(got_before_close_popup_);
+
+    TestHandler::DestroyTest();
+  }
+
+  TrackCallback got_before_popup_;
+  TrackCallback got_after_created_popup_;
+  TrackCallback got_load_end_popup_;
+  TrackCallback got_before_close_popup_;
+
+  IMPLEMENT_REFCOUNTING(PopupJSWindowEmptyTestHandler);
+};
+
+}  // namespace
+
+// Test creation of a popup where the URL is empty.
+TEST(NavigationTest, PopupJSWindowEmpty) {
+  CefRefPtr<PopupJSWindowEmptyTestHandler> handler =
+      new PopupJSWindowEmptyTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kBrowseNavPageUrl[] = "http://tests-browsenav/nav.html";
+
+// Browser side.
+class BrowseNavTestHandler : public TestHandler {
+ public:
+  BrowseNavTestHandler(bool allow) : allow_(allow), destroyed_(false) {}
+
+  void RunTest() override {
+    AddResource(kBrowseNavPageUrl, "<html>Test</html>", "text/html");
+
+    // Create the browser.
+    CreateBrowser(kBrowseNavPageUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(kBrowseNavPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_before_browse_.yes();
+
+    return !allow_;
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(kBrowseNavPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_load_start_.yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(kBrowseNavPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_load_end_.yes();
+    DestroyTestIfDone();
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ("", url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    EXPECT_EQ(ERR_ABORTED, errorCode);
+    EXPECT_STREQ(kBrowseNavPageUrl, failedUrl.ToString().c_str());
+
+    got_load_error_.yes();
+    DestroyTestIfDone();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    const std::string& url = browser->GetMainFrame()->GetURL();
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+
+    if (isLoading) {
+      EXPECT_STREQ("", url.c_str());
+
+      got_loading_state_changed_start_.yes();
+    } else {
+      if (allow_)
+        EXPECT_STREQ(kBrowseNavPageUrl, url.c_str());
+      else
+        EXPECT_STREQ("", url.c_str());
+
+      got_loading_state_changed_end_.yes();
+      DestroyTestIfDone();
+    }
+  }
+
+ private:
+  void DestroyTestIfDone() {
+    if (destroyed_)
+      return;
+
+    if (got_loading_state_changed_end_) {
+      if (allow_) {
+        if (got_load_end_)
+          DestroyTest();
+      } else if (got_load_error_) {
+        DestroyTest();
+      }
+    }
+  }
+
+  void DestroyTest() override {
+    if (destroyed_)
+      return;
+    destroyed_ = true;
+
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_loading_state_changed_end_);
+
+    if (allow_) {
+      EXPECT_TRUE(got_load_start_);
+      EXPECT_TRUE(got_load_end_);
+      EXPECT_FALSE(got_load_error_);
+    } else {
+      EXPECT_FALSE(got_load_start_);
+      EXPECT_FALSE(got_load_end_);
+      EXPECT_TRUE(got_load_error_);
+    }
+
+    TestHandler::DestroyTest();
+  }
+
+  bool allow_;
+  bool destroyed_;
+
+  TrackCallback got_before_browse_;
+  TrackCallback got_load_start_;
+  TrackCallback got_load_end_;
+  TrackCallback got_load_error_;
+  TrackCallback got_loading_state_changed_start_;
+  TrackCallback got_loading_state_changed_end_;
+
+  IMPLEMENT_REFCOUNTING(BrowseNavTestHandler);
+};
+
+}  // namespace
+
+// Test allowing navigation.
+TEST(NavigationTest, BrowseAllow) {
+  CefRefPtr<BrowseNavTestHandler> handler = new BrowseNavTestHandler(true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test denying navigation.
+TEST(NavigationTest, BrowseDeny) {
+  CefRefPtr<BrowseNavTestHandler> handler = new BrowseNavTestHandler(false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kSameNavPageUrl[] = "http://tests-samenav/nav.html";
+
+// Browser side.
+class SameNavTestHandler : public TestHandler {
+ public:
+  SameNavTestHandler() : destroyed_(false), step_(0) {}
+
+  void RunTest() override {
+    AddResource(kSameNavPageUrl, "<html>Test</html>", "text/html");
+
+    // Create the browser.
+    expected_url_ = kSameNavPageUrl;
+    CreateBrowser(kSameNavPageUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(expected_url_.c_str(), url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_before_browse_.yes();
+
+    return false;
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(expected_url_.c_str(), url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_load_start_.yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(expected_url_.c_str(), url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_load_end_.yes();
+    ContinueTestIfDone();
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    got_load_error_.yes();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    const std::string& url = browser->GetMainFrame()->GetURL();
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+
+    if (isLoading) {
+      // Verify the previous URL.
+      if (step_ == 0)
+        EXPECT_TRUE(url.empty());
+      else
+        EXPECT_STREQ(kSameNavPageUrl, url.c_str());
+
+      got_loading_state_changed_start_.yes();
+    } else {
+      EXPECT_STREQ(expected_url_.c_str(), url.c_str());
+
+      got_loading_state_changed_end_.yes();
+      ContinueTestIfDone();
+    }
+  }
+
+ private:
+  void ContinueTestIfDone() {
+    if (step_ == 0) {
+      // First navigation should trigger all callbacks except OnLoadError.
+      if (got_loading_state_changed_end_ && got_load_end_) {
+        EXPECT_TRUE(got_before_browse_);
+        EXPECT_TRUE(got_loading_state_changed_start_);
+        EXPECT_TRUE(got_load_start_);
+        EXPECT_FALSE(got_load_error_);
+
+        got_before_browse_.reset();
+        got_loading_state_changed_start_.reset();
+        got_loading_state_changed_end_.reset();
+        got_load_start_.reset();
+        got_load_end_.reset();
+
+        step_++;
+        expected_url_ = kSameNavPageUrl + std::string("#fragment");
+        GetBrowser()->GetMainFrame()->LoadURL(expected_url_);
+      }
+    } else if (step_ == 1) {
+      step_++;
+      DestroyTest();
+    } else {
+      EXPECT_TRUE(false);  // Not reached.
+    }
+  }
+
+  void DestroyTest() override {
+    if (destroyed_)
+      return;
+    destroyed_ = true;
+
+    EXPECT_EQ(2, step_);
+
+    // Second (fragment) navigation should only trigger OnLoadingStateChange.
+    EXPECT_FALSE(got_before_browse_);
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_loading_state_changed_end_);
+    EXPECT_FALSE(got_load_start_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_load_error_);
+
+    TestHandler::DestroyTest();
+  }
+
+  bool destroyed_;
+  int step_;
+  std::string expected_url_;
+
+  TrackCallback got_before_browse_;
+  TrackCallback got_load_start_;
+  TrackCallback got_load_end_;
+  TrackCallback got_load_error_;
+  TrackCallback got_loading_state_changed_start_;
+  TrackCallback got_loading_state_changed_end_;
+
+  IMPLEMENT_REFCOUNTING(SameNavTestHandler);
+};
+
+}  // namespace
+
+// Test that same page navigation does not call OnLoadStart/OnLoadEnd.
+TEST(NavigationTest, SamePage) {
+  CefRefPtr<SameNavTestHandler> handler = new SameNavTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kCancelPageUrl[] = "http://tests-cancelnav/nav.html";
+
+// A scheme handler that never starts sending data.
+class UnstartedSchemeHandler : public CefResourceHandler {
+ public:
+  UnstartedSchemeHandler() {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    response->SetStatus(200);
+    response->SetMimeType("text/html");
+    response_length = 100;
+  }
+
+  void Cancel() override { callback_ = nullptr; }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    callback_ = callback;
+
+    // Pretend that we'll provide the data later.
+    bytes_read = 0;
+    return true;
+  }
+
+ protected:
+  CefRefPtr<CefResourceReadCallback> callback_;
+
+  IMPLEMENT_REFCOUNTING(UnstartedSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(UnstartedSchemeHandler);
+};
+
+// Browser side.
+class CancelBeforeNavTestHandler : public TestHandler {
+ public:
+  CancelBeforeNavTestHandler() : destroyed_(false) {}
+
+  void RunTest() override {
+    // Create the browser.
+    CreateBrowser(kCancelPageUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_FALSE(got_before_browse_);
+    EXPECT_FALSE(got_get_resource_handler_);
+    EXPECT_FALSE(got_load_start_);
+    EXPECT_FALSE(got_cancel_load_);
+    EXPECT_FALSE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_loading_state_changed_end_);
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(kCancelPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_before_browse_.yes();
+
+    return false;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_FALSE(got_get_resource_handler_);
+    EXPECT_FALSE(got_load_start_);
+    EXPECT_FALSE(got_cancel_load_);
+    EXPECT_FALSE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_loading_state_changed_end_);
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(kCancelPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_get_resource_handler_.yes();
+
+    CefPostTask(TID_UI,
+                base::Bind(&CancelBeforeNavTestHandler::CancelLoad, this));
+
+    return new UnstartedSchemeHandler();
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    EXPECT_TRUE(false);  // Not reached.
+    got_load_start_.yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_TRUE(false);  // Not reached.
+    got_load_end_.yes();
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_STREQ("", frame->GetURL().ToString().c_str());
+    EXPECT_EQ(ERR_ABORTED, errorCode);
+    EXPECT_STREQ(kCancelPageUrl, failedUrl.ToString().c_str());
+    got_load_error_.yes();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    const std::string& url = browser->GetMainFrame()->GetURL();
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(url.empty());
+
+    if (isLoading) {
+      EXPECT_FALSE(got_loading_state_changed_start_);
+      EXPECT_FALSE(got_before_browse_);
+      EXPECT_FALSE(got_get_resource_handler_);
+      EXPECT_FALSE(got_load_start_);
+      EXPECT_FALSE(got_cancel_load_);
+      EXPECT_FALSE(got_load_error_);
+      EXPECT_FALSE(got_load_end_);
+      EXPECT_FALSE(got_loading_state_changed_end_);
+
+      got_loading_state_changed_start_.yes();
+    } else {
+      EXPECT_TRUE(got_loading_state_changed_start_);
+      EXPECT_TRUE(got_before_browse_);
+      EXPECT_TRUE(got_get_resource_handler_);
+      EXPECT_FALSE(got_load_start_);
+      EXPECT_TRUE(got_cancel_load_);
+      EXPECT_TRUE(got_load_error_);
+      EXPECT_FALSE(got_load_end_);
+      EXPECT_FALSE(got_loading_state_changed_end_);
+
+      got_loading_state_changed_end_.yes();
+
+      DestroyTest();
+    }
+  }
+
+ private:
+  void CancelLoad() {
+    got_cancel_load_.yes();
+    GetBrowser()->StopLoad();
+  }
+
+  void DestroyTest() override {
+    if (destroyed_)
+      return;
+    destroyed_ = true;
+
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_TRUE(got_get_resource_handler_);
+    EXPECT_FALSE(got_load_start_);
+    EXPECT_TRUE(got_cancel_load_);
+    EXPECT_TRUE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_TRUE(got_loading_state_changed_end_);
+
+    TestHandler::DestroyTest();
+  }
+
+  bool destroyed_;
+
+  TrackCallback got_loading_state_changed_start_;
+  TrackCallback got_before_browse_;
+  TrackCallback got_get_resource_handler_;
+  TrackCallback got_load_start_;
+  TrackCallback got_cancel_load_;
+  TrackCallback got_load_error_;
+  TrackCallback got_load_end_;
+  TrackCallback got_loading_state_changed_end_;
+
+  IMPLEMENT_REFCOUNTING(CancelBeforeNavTestHandler);
+};
+
+}  // namespace
+
+// Test that navigation canceled before commit does not call
+// OnLoadStart/OnLoadEnd.
+TEST(NavigationTest, CancelBeforeCommit) {
+  CefRefPtr<CancelBeforeNavTestHandler> handler =
+      new CancelBeforeNavTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// A scheme handler that stalls after writing some data.
+class StalledSchemeHandler : public CefResourceHandler {
+ public:
+  StalledSchemeHandler() : offset_(0), write_size_(0) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    response->SetStatus(200);
+    response->SetMimeType("text/html");
+    content_ = "<html><body>Test</body></html>";
+    // Write this number of bytes and then stall.
+    write_size_ = content_.size() / 2U;
+    response_length = content_.size();
+  }
+
+  void Cancel() override { callback_ = nullptr; }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    bytes_read = 0;
+
+    size_t size = content_.size();
+    if (offset_ >= write_size_) {
+      // Now stall.
+      callback_ = callback;
+      return true;
+    }
+
+    bool has_data = false;
+
+    if (offset_ < size) {
+      // Write up to |write_size_| bytes.
+      int transfer_size =
+          std::min(bytes_to_read, std::min(static_cast<int>(write_size_),
+                                           static_cast<int>(size - offset_)));
+      memcpy(data_out, content_.c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+ protected:
+  std::string content_;
+  size_t offset_;
+  size_t write_size_;
+  CefRefPtr<CefResourceReadCallback> callback_;
+
+  IMPLEMENT_REFCOUNTING(StalledSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(StalledSchemeHandler);
+};
+
+// Browser side.
+class CancelAfterNavTestHandler : public TestHandler {
+ public:
+  CancelAfterNavTestHandler() : destroyed_(false) {}
+
+  void RunTest() override {
+    // Create the browser.
+    CreateBrowser(kCancelPageUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_FALSE(got_before_browse_);
+    EXPECT_FALSE(got_get_resource_handler_);
+    EXPECT_FALSE(got_load_start_);
+    EXPECT_FALSE(got_cancel_load_);
+    EXPECT_FALSE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_loading_state_changed_end_);
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(kCancelPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_before_browse_.yes();
+
+    return false;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_FALSE(got_get_resource_handler_);
+    EXPECT_FALSE(got_load_start_);
+    EXPECT_FALSE(got_cancel_load_);
+    EXPECT_FALSE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_loading_state_changed_end_);
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(kCancelPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_get_resource_handler_.yes();
+
+    // The required delay is longer when browser-side navigation is enabled.
+    CefPostDelayedTask(
+        TID_UI, base::Bind(&CancelAfterNavTestHandler::CancelLoad, this), 1000);
+
+    return new StalledSchemeHandler();
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_TRUE(got_get_resource_handler_);
+    EXPECT_FALSE(got_load_start_);
+    EXPECT_FALSE(got_cancel_load_);
+    EXPECT_FALSE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_loading_state_changed_end_);
+
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(kCancelPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_load_start_.yes();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_TRUE(got_get_resource_handler_);
+    EXPECT_TRUE(got_load_start_);
+    EXPECT_TRUE(got_cancel_load_);
+    EXPECT_TRUE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_loading_state_changed_end_);
+
+    const std::string& url = frame->GetURL();
+    EXPECT_STREQ(kCancelPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_load_end_.yes();
+    DestroyTestIfDone();
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_TRUE(got_get_resource_handler_);
+    EXPECT_TRUE(got_load_start_);
+    EXPECT_TRUE(got_cancel_load_);
+    EXPECT_FALSE(got_load_error_);
+    EXPECT_FALSE(got_load_end_);
+    EXPECT_FALSE(got_loading_state_changed_end_);
+
+    const std::string& url = failedUrl;
+    EXPECT_STREQ(kCancelPageUrl, url.c_str());
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    got_load_error_.yes();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    const std::string& url = browser->GetMainFrame()->GetURL();
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+
+    if (isLoading) {
+      EXPECT_FALSE(got_loading_state_changed_start_);
+      EXPECT_FALSE(got_before_browse_);
+      EXPECT_FALSE(got_get_resource_handler_);
+      EXPECT_FALSE(got_load_start_);
+      EXPECT_FALSE(got_cancel_load_);
+      EXPECT_FALSE(got_load_error_);
+      EXPECT_FALSE(got_load_end_);
+      EXPECT_FALSE(got_loading_state_changed_end_);
+
+      EXPECT_TRUE(url.empty());
+
+      got_loading_state_changed_start_.yes();
+    } else {
+      EXPECT_TRUE(got_loading_state_changed_start_);
+      EXPECT_TRUE(got_before_browse_);
+      EXPECT_TRUE(got_get_resource_handler_);
+      EXPECT_TRUE(got_load_start_);
+      EXPECT_TRUE(got_cancel_load_);
+      EXPECT_TRUE(got_load_error_);
+      EXPECT_TRUE(got_load_end_);
+      EXPECT_FALSE(got_loading_state_changed_end_);
+
+      EXPECT_STREQ(kCancelPageUrl, url.c_str());
+
+      got_loading_state_changed_end_.yes();
+      DestroyTestIfDone();
+    }
+  }
+
+ private:
+  void CancelLoad() {
+    got_cancel_load_.yes();
+    GetBrowser()->StopLoad();
+  }
+
+  void DestroyTestIfDone() {
+    if (got_loading_state_changed_end_ && got_load_end_)
+      DestroyTest();
+  }
+
+  void DestroyTest() override {
+    if (destroyed_)
+      return;
+    destroyed_ = true;
+
+    EXPECT_TRUE(got_loading_state_changed_start_);
+    EXPECT_TRUE(got_before_browse_);
+    EXPECT_TRUE(got_get_resource_handler_);
+    EXPECT_TRUE(got_load_start_);
+    EXPECT_TRUE(got_cancel_load_);
+    EXPECT_TRUE(got_load_error_);
+    EXPECT_TRUE(got_load_end_);
+    EXPECT_TRUE(got_loading_state_changed_end_);
+
+    TestHandler::DestroyTest();
+  }
+
+  bool destroyed_;
+
+  TrackCallback got_loading_state_changed_start_;
+  TrackCallback got_before_browse_;
+  TrackCallback got_get_resource_handler_;
+  TrackCallback got_load_start_;
+  TrackCallback got_cancel_load_;
+  TrackCallback got_load_error_;
+  TrackCallback got_load_end_;
+  TrackCallback got_loading_state_changed_end_;
+
+  IMPLEMENT_REFCOUNTING(CancelAfterNavTestHandler);
+};
+
+}  // namespace
+
+// Test that navigation canceled after commit calls everything.
+TEST(NavigationTest, CancelAfterCommit) {
+  CefRefPtr<CancelAfterNavTestHandler> handler =
+      new CancelAfterNavTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kExtraInfoUrl[] = "http://tests-extrainfonav.com/extra.html";
+const char kExtraInfoPopupUrl[] =
+    "http://tests-extrainfonav.com/extra_popup.html";
+const char kExtraInfoNavMsg[] = "NavigationTest.ExtraInfoNav";
+const char kExtraInfoTestCmdKey[] = "nav-extra-info-test";
+
+void SetBrowserExtraInfo(CefRefPtr<CefDictionaryValue> extra_info) {
+  // Necessary for identifying the test case.
+  extra_info->SetBool(kExtraInfoTestCmdKey, true);
+
+  // Arbitrary data for testing.
+  extra_info->SetBool("bool", true);
+  CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
+  dict->SetInt("key1", 5);
+  dict->SetString("key2", "test string");
+  extra_info->SetDictionary("dictionary", dict);
+  extra_info->SetDouble("double", 5.43322);
+  extra_info->SetString("string", "some string");
+}
+
+// Renderer side
+class ExtraInfoNavRendererTest : public ClientAppRenderer::Delegate {
+ public:
+  ExtraInfoNavRendererTest() : run_test_(false) {}
+
+  void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override {
+    run_test_ = extra_info->HasKey(kExtraInfoTestCmdKey);
+    if (!run_test_)
+      return;
+
+    CefRefPtr<CefDictionaryValue> expected = CefDictionaryValue::Create();
+    SetBrowserExtraInfo(expected);
+    TestDictionaryEqual(expected, extra_info);
+
+    SendTestResults(browser, browser->GetMainFrame());
+  }
+
+ protected:
+  // Send the test results.
+  void SendTestResults(CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame) {
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kExtraInfoNavMsg);
+    CefRefPtr<CefListValue> args = return_msg->GetArgumentList();
+    EXPECT_TRUE(args.get());
+    EXPECT_TRUE(args->SetBool(0, result));
+    EXPECT_TRUE(args->SetBool(1, browser->IsPopup()));
+    frame->SendProcessMessage(PID_BROWSER, return_msg);
+  }
+
+  bool run_test_;
+
+  IMPLEMENT_REFCOUNTING(ExtraInfoNavRendererTest);
+};
+
+class ExtraInfoNavTestHandler : public TestHandler {
+ public:
+  ExtraInfoNavTestHandler() : popup_opened_(false) {}
+
+  void RunTest() override {
+    AddResource(kExtraInfoUrl,
+                "<html><head></head><body>ExtraInfo</body></html>",
+                "text/html");
+    AddResource(kExtraInfoPopupUrl, "<html>ExtraInfoPopup</html>", "text/html");
+
+    CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
+    SetBrowserExtraInfo(extra_info);
+
+    // Create the browser.
+    CreateBrowser(kExtraInfoUrl, nullptr, extra_info);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (popup_opened_) {
+      DestroyTest();
+    } else {
+      browser->GetMainFrame()->ExecuteJavaScript(
+          "window.open('" + std::string(kExtraInfoPopupUrl) + "');",
+          CefString(), 0);
+    }
+  }
+
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     cef_window_open_disposition_t target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override {
+    const std::string& url = target_url;
+    EXPECT_FALSE(popup_opened_);
+    EXPECT_STREQ(kExtraInfoPopupUrl, url.c_str());
+
+    CefRefPtr<CefDictionaryValue> extra = CefDictionaryValue::Create();
+    SetBrowserExtraInfo(extra);
+
+    extra_info = extra;
+
+    popup_opened_ = true;
+    return false;
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName().ToString() == kExtraInfoNavMsg) {
+      // Test that the renderer side succeeded.
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_TRUE(args.get());
+      EXPECT_TRUE(args->GetBool(0));
+      if (popup_opened_) {
+        EXPECT_TRUE(args->GetBool(1));
+        got_process_message_popup_.yes();
+      } else {
+        EXPECT_FALSE(args->GetBool(1));
+        got_process_message_main_.yes();
+      }
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+ protected:
+  bool popup_opened_;
+  TrackCallback got_process_message_main_;
+  TrackCallback got_process_message_popup_;
+
+  void DestroyTest() override {
+    // Verify test expectations.
+    EXPECT_TRUE(got_process_message_main_);
+    EXPECT_TRUE(got_process_message_popup_);
+
+    TestHandler::DestroyTest();
+  }
+
+  IMPLEMENT_REFCOUNTING(ExtraInfoNavTestHandler);
+};
+}  // namespace
+
+TEST(NavigationTest, ExtraInfo) {
+  CefRefPtr<ExtraInfoNavTestHandler> handler = new ExtraInfoNavTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Entry point for creating navigation browser test objects.
+// Called from client_app_delegates.cc.
+void CreateNavigationBrowserTests(ClientAppBrowser::DelegateSet& delegates) {
+  delegates.insert(new OrderNavBrowserTest);
+}
+
+// Entry point for creating navigation renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateNavigationRendererTests(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new HistoryNavRendererTest);
+  delegates.insert(new OrderNavRendererTest);
+  delegates.insert(new LoadNavRendererTest);
+  delegates.insert(new ExtraInfoNavRendererTest);
+}
diff --git a/src/tests/ceftests/os_rendering_unittest.cc b/src/tests/ceftests/os_rendering_unittest.cc
new file mode 100644
index 0000000..37c7af5
--- /dev/null
+++ b/src/tests/ceftests/os_rendering_unittest.cc
@@ -0,0 +1,1608 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/cef_parser.h"
+#include "include/cef_v8.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/geometry_util.h"
+#include "tests/shared/browser/resource_util.h"
+
+#if defined(OS_MACOSX)
+#include <Carbon/Carbon.h>  // For character codes.
+#include "tests/ceftests/os_rendering_unittest_mac.h"
+#elif defined(OS_LINUX)
+#include <X11/keysym.h>
+#elif defined(OS_WIN)
+// Required for resource_util_win, which uses this as an extern
+HINSTANCE hInst = ::GetModuleHandle(nullptr);
+#endif
+
+namespace {
+
+const char kTestUrl[] = "http://tests/osrtest";
+
+// this html should render on a 600 x 400 window with a little vertical
+// offset with scrollbar.
+
+// default osr widget size
+const int kOsrWidth = 600;
+const int kOsrHeight = 400;
+
+// bounding client rects for edit box and navigate button
+#if defined(OS_WIN)
+const CefRect kExpandedSelectRect(462, 42, 81, 334);
+#elif defined(OS_MACOSX)
+const CefRect kExpandedSelectRect(462, 42, 75, 334);
+#elif defined(OS_LINUX)
+const CefRect kExpandedSelectRect(462, 42, 79, 334);
+#else
+#error "Unsupported platform"
+#endif  // defined(OS_WIN)
+
+// word to be written into edit box
+const char kKeyTestWord[] = "done";
+
+#if defined(OS_LINUX)
+
+// From ui/events/keycodes/keyboard_codes_posix.h
+#define VKEY_D 0x44
+#define VKEY_O 0x4F
+#define VKEY_N 0x4E
+#define VKEY_E 0x45
+#define VKEY_ESCAPE 0x1B
+#define VKEY_TAB 0x09
+
+const unsigned int kNativeKeyTestCodes[] = {XK_d, XK_o, XK_n, XK_e};
+
+const unsigned int kNativeKeyEscape = XK_Escape;
+const unsigned int kNativeKeyTab = XK_Tab;
+
+#elif defined(OS_MACOSX)
+
+// See kKeyCodesMap in ui/events/keycodes/keyboard_code_conversion_mac.mm
+#define VKEY_D 'd'
+#define VKEY_O 'o'
+#define VKEY_N 'n'
+#define VKEY_E 'e'
+#define VKEY_ESCAPE kEscapeCharCode
+#define VKEY_TAB kTabCharCode
+
+const unsigned int kNativeKeyTestCodes[] = {kVK_ANSI_D, kVK_ANSI_O, kVK_ANSI_N,
+                                            kVK_ANSI_E};
+
+const unsigned int kNativeKeyEscape = kVK_Escape;
+const unsigned int kNativeKeyTab = kVK_Tab;
+
+#endif
+
+#if defined(OS_MACOSX) || defined(OS_LINUX)
+
+const unsigned int kKeyTestCodes[] = {VKEY_D, VKEY_O, VKEY_N, VKEY_E};
+
+#endif
+
+// test type
+enum OSRTestType {
+  // IsWindowRenderingDisabled should be true
+  OSR_TEST_IS_WINDOWLESS,
+  // focusing webview, LI00 will get red & repainted
+  OSR_TEST_FOCUS,
+  // tab key traversal should iterate the focus across HTML element and
+  // subsequently after last element CefFocusHandler::OnTakeFocus should be
+  // called to allow giving focus to the next component.
+  OSR_TEST_TAKE_FOCUS,
+  // send focus event should set focus on the webview
+  OSR_TEST_GOT_FOCUS,
+  // loading webview should trigger a full paint (L01)
+  OSR_TEST_PAINT,
+  // same as OSR_TEST_PAINT but with alpha values
+  OSR_TEST_TRANSPARENCY,
+  // moving mouse over L02, OnCursorChange will be called
+  OSR_TEST_CURSOR,
+  // moving mouse on L03, OnPaint will be called for its bounding rectangle
+  OSR_TEST_MOUSE_MOVE,
+  // right clicking an element (L04), OnBeforeContextMenu should be called
+  OSR_TEST_CLICK_RIGHT,
+  // right clicking an element (L04), context menu will query screen point
+  OSR_TEST_SCREEN_POINT,
+  // left click in text box should query repainting edit box area
+  OSR_TEST_CLICK_LEFT,
+  // Resize should trigger a full repaint with the new given size
+  OSR_TEST_RESIZE,
+  // Invalidate should trigger repaint synchronously
+  OSR_TEST_INVALIDATE,
+  // write into editbox LI08, click to navigate on LI09
+  OSR_TEST_KEY_EVENTS,
+  // mouse over LI10 will show a tooltip
+  OSR_TEST_TOOLTIP,
+  // mouse wheel will trigger a scroll event
+  OSR_TEST_SCROLLING,
+  // Right click will trigger a context menu, and on destroying the test, it
+  // should not crash
+  OSR_TEST_CONTEXT_MENU,
+  // clicking on dropdown box, PET_POPUP OnPaint is triggered
+  OSR_TEST_POPUP_PAINT,
+  // clicking on dropdown box, a popup will show up
+  OSR_TEST_POPUP_SHOW,
+  // clicking on dropdown box, OnPopupSize should be called
+  OSR_TEST_POPUP_SIZE,
+  // taking focus away from the webview, will close popup
+  OSR_TEST_POPUP_HIDE_ON_BLUR,
+  // clicking outside the popup widget will close popup
+  OSR_TEST_POPUP_HIDE_ON_CLICK,
+  // scrolling outside the popup widget will close popup
+  OSR_TEST_POPUP_HIDE_ON_SCROLL,
+  // pressing ESC will close popup
+  OSR_TEST_POPUP_HIDE_ON_ESC,
+  // scrolling inside the popup should trigger repaint for popup area
+  OSR_TEST_POPUP_SCROLL_INSIDE,
+  // clicking and moving the mouse will call StartDragging
+  OSR_TEST_DRAG_DROP_START_DRAGGING,
+  // starting dragging over the drop region will call UpdateDragCursor
+  OSR_TEST_DRAG_DROP_UPDATE_CURSOR,
+  // dropping element inside drop region will move the element
+  OSR_TEST_DRAG_DROP_DROP,
+  // IMESetComposition will update the composition range
+  OSR_TEST_IME_SET_COMPOSITION,
+  // IMECommitText inserts the specified text
+  OSR_TEST_IME_COMMIT_TEXT,
+  // IMEFinishComposition will commit the text present composition text
+  OSR_TEST_IME_FINISH_COMPOSITION,
+  // IMECancelComposition will update the composition range
+  OSR_TEST_IME_CANCEL_COMPOSITION,
+  // text selection range changed
+  OSR_TEST_TEXT_SELECTION_CHANGE,
+  // clicking on text input will call OnVirtualKeyboardRequested
+  OSR_TEST_VIRTUAL_KEYBOARD,
+  // touchStart event is triggered and contains the touch point count
+  OSR_TEST_TOUCH_START,
+  // touchMove event is triggered and contains the changed touch points
+  OSR_TEST_TOUCH_MOVE,
+  // touchEnd is triggered on completion
+  OSR_TEST_TOUCH_END,
+  // touchCancel is triggered on dismissing
+  OSR_TEST_TOUCH_CANCEL,
+  // CEF_POINTER_TYPE_PEN is mapped to pen pointer event
+  OSR_TEST_PEN,
+  // Define the range for popup tests.
+  OSR_TEST_POPUP_FIRST = OSR_TEST_POPUP_PAINT,
+  OSR_TEST_POPUP_LAST = OSR_TEST_POPUP_SCROLL_INSIDE,
+};
+
+// Used in the browser process.
+class OSRTestHandler : public RoutingTestHandler,
+                       public CefFocusHandler,
+                       public CefRenderHandler,
+                       public CefContextMenuHandler {
+ public:
+  OSRTestHandler(OSRTestType test_type, float scale_factor)
+      : test_type_(test_type),
+        scale_factor_(scale_factor),
+        event_count_(0),
+        event_total_(1),
+        started_(false),
+        touch_state_(CEF_TET_CANCELLED) {}
+
+  // TestHandler methods
+  void RunTest() override {
+    CreateOSRBrowser(kTestUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    if (test_type_ == OSR_TEST_IS_WINDOWLESS) {
+      EXPECT_TRUE(browser->GetHost()->IsWindowRenderingDisabled());
+      DestroySucceededTestSoon();
+    }
+    RoutingTestHandler::OnAfterCreated(browser);
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (!started())
+      return;
+
+    switch (test_type_) {
+      case OSR_TEST_KEY_EVENTS: {
+        const std::string& expected_url =
+            std::string(kTestUrl) + "?k=" + kKeyTestWord;
+        EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str());
+        DestroySucceededTestSoon();
+      } break;
+      case OSR_TEST_IME_COMMIT_TEXT: {
+        const std::string& expected_url =
+            std::string(kTestUrl) + "?k=osrimecommit";
+        EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str());
+        DestroySucceededTestSoon();
+      } break;
+      case OSR_TEST_IME_FINISH_COMPOSITION: {
+        const std::string& expected_url =
+            std::string(kTestUrl) + "?k=" + kKeyTestWord;
+        EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str());
+        DestroySucceededTestSoon();
+      } break;
+      case OSR_TEST_IME_CANCEL_COMPOSITION: {
+        const std::string& expected_url = std::string(kTestUrl) + "?k=";
+        EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str());
+        DestroySucceededTestSoon();
+      } break;
+      default:
+        // Intentionally left blank
+        break;
+    }
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    EXPECT_TRUE(browser.get());
+
+    if (!started()) {
+      return handleBoundsQuery(browser, frame, query_id, request, persistent,
+                               callback);
+    }
+
+    const std::string& messageStr = request;
+    switch (test_type_) {
+      case OSR_TEST_FOCUS:
+        EXPECT_STREQ(messageStr.c_str(), "osrfocus");
+        DestroySucceededTestSoon();
+        break;
+      case OSR_TEST_CLICK_LEFT:
+        EXPECT_STREQ(messageStr.c_str(), "osrclick0");
+        DestroySucceededTestSoon();
+        break;
+      case OSR_TEST_MOUSE_MOVE:
+        EXPECT_STREQ(messageStr.c_str(), "osrmousemove");
+        DestroySucceededTestSoon();
+        break;
+      case OSR_TEST_DRAG_DROP_DROP:
+        EXPECT_STREQ(messageStr.c_str(), "osrdrop");
+        DestroySucceededTestSoon();
+        break;
+      case OSR_TEST_TOUCH_START:
+      case OSR_TEST_TOUCH_MOVE:
+      case OSR_TEST_TOUCH_END:
+      case OSR_TEST_TOUCH_CANCEL: {
+        switch (touch_state_) {
+          case CEF_TET_CANCELLED: {
+            // The first message expected is touchstart.
+            // We expect multitouch, so touches length should be 2.
+            // Ignore intermediate touch start events.
+            if (messageStr == "osrtouchstart1")
+              break;
+            EXPECT_STREQ(messageStr.c_str(), "osrtouchstart2");
+            // Close Touch Start Tests.
+            if (test_type_ == OSR_TEST_TOUCH_START) {
+              DestroySucceededTestSoon();
+              touch_state_ = CEF_TET_RELEASED;
+            } else {
+              touch_state_ = CEF_TET_PRESSED;
+            }
+          } break;
+          case CEF_TET_PRESSED: {
+            // Touch Move include the touches that changed, should be 2.
+            EXPECT_STREQ(messageStr.c_str(), "osrtouchmove2");
+            if (test_type_ == OSR_TEST_TOUCH_MOVE) {
+              DestroySucceededTestSoon();
+              touch_state_ = CEF_TET_RELEASED;
+            } else {
+              touch_state_ = CEF_TET_MOVED;
+            }
+          } break;
+          case CEF_TET_MOVED: {
+            // There might be multiple touchmove events, ignore.
+            if (messageStr != "osrtouchmove2") {
+              if (test_type_ == OSR_TEST_TOUCH_END) {
+                EXPECT_STREQ(messageStr.c_str(), "osrtouchend");
+                DestroySucceededTestSoon();
+                touch_state_ = CEF_TET_RELEASED;
+              } else if (test_type_ == OSR_TEST_TOUCH_CANCEL) {
+                EXPECT_STREQ(messageStr.c_str(), "osrtouchcancel");
+                DestroySucceededTestSoon();
+                touch_state_ = CEF_TET_RELEASED;
+              }
+            }
+          } break;
+          default:
+            break;
+        }
+      } break;
+      case OSR_TEST_PEN: {
+        switch (touch_state_) {
+          case CEF_TET_CANCELLED:
+            // The first message expected is pointerdown.
+            EXPECT_STREQ(messageStr.c_str(), "osrpointerdown pen");
+            touch_state_ = CEF_TET_PRESSED;
+            break;
+          case CEF_TET_PRESSED:
+            EXPECT_STREQ(messageStr.c_str(), "osrpointermove pen");
+            touch_state_ = CEF_TET_MOVED;
+            break;
+          case CEF_TET_MOVED:
+            // There might be multiple pointermove events, ignore.
+            if (messageStr != "osrpointermove pen") {
+              EXPECT_STREQ(messageStr.c_str(), "osrpointerup pen");
+              DestroySucceededTestSoon();
+            }
+            break;
+          default:
+            break;
+        }
+      } break;
+      default:
+        // Intentionally left blank
+        break;
+    }
+    callback->Success("");
+    return true;
+  }
+
+  bool handleBoundsQuery(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         int64 query_id,
+                         const CefString& request,
+                         bool persistent,
+                         CefRefPtr<Callback> callback) {
+    CefRefPtr<CefValue> jsonObj = CefParseJSON(request, JSON_PARSER_RFC);
+    if (jsonObj.get()) {
+      CefRefPtr<CefDictionaryValue> dict = jsonObj->GetDictionary();
+      const std::string& type = dict->GetString("type");
+      if (type == "ElementBounds") {
+        CefRefPtr<CefListValue> elems = dict->GetList("elems");
+
+        for (size_t i = 0; i < elems->GetSize(); i++) {
+          CefRefPtr<CefDictionaryValue> elem = elems->GetDictionary(i);
+          std::string elementId = elem->GetString("id");
+          CefRect bounds(elem->GetInt("x"), elem->GetInt("y"),
+                         elem->GetInt("width"), elem->GetInt("height"));
+          element_bounds_.insert(std::make_pair(elementId, bounds));
+        }
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  // CefClient methods, providing handlers
+  CefRefPtr<CefFocusHandler> GetFocusHandler() override { return this; }
+
+  CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; }
+
+  CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override {
+    return this;
+  }
+
+  // CefResourceRequestHandler methods
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    std::string url = request->GetURL();
+
+    if (url.find(kTestUrl) == 0) {
+      // Show the osr test contents
+      CefRefPtr<CefStreamReader> stream =
+          client::GetBinaryResourceReader("osr_test.html");
+      return new CefStreamResourceHandler("text/html", stream);
+    }
+
+    return nullptr;
+  }
+
+  // CefRenderHandler methods
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override {
+    if (test_type_ == OSR_TEST_RESIZE && started()) {
+      rect = CefRect(0, 0, kOsrWidth * 2, kOsrHeight * 2);
+      return;
+    }
+    rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+  }
+
+  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                      int viewX,
+                      int viewY,
+                      int& screenX,
+                      int& screenY) override {
+    if (test_type_ == OSR_TEST_SCREEN_POINT && started()) {
+      const CefRect& expected_rect = GetElementBounds("LI04");
+      EXPECT_EQ(viewX, MiddleX(expected_rect));
+      EXPECT_EQ(viewY, MiddleY(expected_rect));
+      DestroySucceededTestSoon();
+    } else if (test_type_ == OSR_TEST_CONTEXT_MENU && started()) {
+      screenX = 0;
+      screenY = 0;
+      return true;
+    }
+    // we don't want to see a contextual menu. stop here.
+    return false;
+  }
+
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) override {
+    screen_info.device_scale_factor = scale_factor_;
+
+    // The screen info rectangles are used by the renderer to create and
+    // position popups. If not overwritten in this function, the rectangle
+    // returned from GetViewRect will be used to popuplate them.
+    // The popup in the test fits without modifications in the test window, so
+    // setting the screen to the test window size does not affect its rectangle.
+    screen_info.rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+    screen_info.available_rect = screen_info.rect;
+    return true;
+  }
+
+  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) override {
+    if (show && started()) {
+      switch (test_type_) {
+        case OSR_TEST_POPUP_SHOW:
+          if (!succeeded()) {
+            EXPECT_TRUE(show);
+            DestroySucceededTestSoon();
+          }
+          break;
+        default:
+          break;
+      }
+    }
+    if (!show && started()) {
+      switch (test_type_) {
+        case OSR_TEST_POPUP_HIDE_ON_BLUR:
+        case OSR_TEST_POPUP_HIDE_ON_CLICK:
+        case OSR_TEST_POPUP_HIDE_ON_ESC:
+        case OSR_TEST_POPUP_HIDE_ON_SCROLL:
+          DestroySucceededTestSoon();
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  void OnPopupSize(CefRefPtr<CefBrowser> browser,
+                   const CefRect& rect) override {
+    if (started()) {
+      switch (test_type_) {
+        case OSR_TEST_POPUP_SIZE:
+          EXPECT_EQ(kExpandedSelectRect.x, rect.x);
+          EXPECT_EQ(kExpandedSelectRect.y, rect.y);
+          if (ExpectComputedPopupSize()) {
+            EXPECT_EQ(kExpandedSelectRect.width, rect.width);
+            EXPECT_EQ(kExpandedSelectRect.height, rect.height);
+          } else {
+            EXPECT_GT(rect.width, kExpandedSelectRect.width);
+            EXPECT_GT(rect.height, kExpandedSelectRect.height);
+          }
+          DestroySucceededTestSoon();
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               PaintElementType type,
+               const RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) override {
+    // bitmap must as big as GetViewRect said
+    if (test_type_ != OSR_TEST_RESIZE && type == PET_VIEW) {
+      EXPECT_EQ(GetScaledInt(kOsrWidth), width);
+      EXPECT_EQ(GetScaledInt(kOsrHeight), height);
+    } else if (type == PET_POPUP) {
+      const CefRect& expanded_select_rect = GetScaledRect(kExpandedSelectRect);
+      if (ExpectComputedPopupSize()) {
+        EXPECT_EQ(expanded_select_rect.width, width);
+        EXPECT_EQ(expanded_select_rect.height, height);
+      } else {
+        EXPECT_GT(width, kExpandedSelectRect.width);
+        EXPECT_GT(height, kExpandedSelectRect.height);
+      }
+    }
+
+    EXPECT_TRUE(browser->GetHost()->IsWindowRenderingDisabled());
+
+    // start test only when painting something else then background
+    if (IsBackgroundInBuffer(
+            reinterpret_cast<const uint32*>(buffer), width * height,
+            test_type_ == OSR_TEST_TRANSPARENCY ? 0x00000000 : 0xFFFFFFFF))
+      return;
+
+    // Send events after the first full repaint
+    switch (test_type_) {
+      case OSR_TEST_PAINT:
+        if (StartTest()) {
+          // test that we have a full repaint
+          EXPECT_EQ(dirtyRects.size(), 1U);
+          EXPECT_TRUE(IsFullRepaint(dirtyRects[0], GetScaledInt(kOsrWidth),
+                                    GetScaledInt(kOsrHeight)));
+          EXPECT_EQ(0xffff7f7fU, *(reinterpret_cast<const uint32*>(buffer)));
+          DestroySucceededTestSoon();
+        }
+        break;
+      case OSR_TEST_TRANSPARENCY:
+        if (StartTest()) {
+          // test that we have a full repaint
+          EXPECT_EQ(dirtyRects.size(), 1U);
+          EXPECT_TRUE(IsFullRepaint(dirtyRects[0], GetScaledInt(kOsrWidth),
+                                    GetScaledInt(kOsrHeight)));
+          EXPECT_EQ(0x80800000U, *(reinterpret_cast<const uint32*>(buffer)));
+          DestroySucceededTestSoon();
+        }
+        break;
+      case OSR_TEST_FOCUS:
+        if (StartTest()) {
+          // body.onfocus will make LI00 red
+          browser->GetHost()->SendFocusEvent(true);
+        }
+        break;
+      case OSR_TEST_TAKE_FOCUS:
+        if (StartTest() || started()) {
+          // Tab traversal across HTML element
+
+#if defined(OS_WIN)
+          SendKeyEvent(browser, VK_TAB);
+#elif defined(OS_MACOSX) || defined(OS_LINUX)
+          SendKeyEvent(browser, kNativeKeyTab, VKEY_TAB);
+#else
+#error "Unsupported platform"
+#endif
+        }
+        break;
+      case OSR_TEST_GOT_FOCUS:
+        if (StartTest()) {
+          browser->GetHost()->SendFocusEvent(true);
+        }
+        break;
+      case OSR_TEST_CURSOR:
+        if (StartTest()) {
+          // make mouse leave first
+          CefMouseEvent mouse_event;
+          mouse_event.x = 0;
+          mouse_event.y = 0;
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseMoveEvent(mouse_event, true);
+          // enter mouse in the LI2 element having hand cursor
+          const CefRect& expected_rect = GetElementBounds("LI02");
+          mouse_event.x = MiddleX(expected_rect);
+          mouse_event.y = MiddleY(expected_rect);
+          browser->GetHost()->SendMouseMoveEvent(mouse_event, false);
+        }
+        break;
+      case OSR_TEST_MOUSE_MOVE:
+        if (StartTest()) {
+          CefMouseEvent mouse_event;
+          const CefRect& expected_rect = GetElementBounds("LI03");
+          mouse_event.x = MiddleX(expected_rect);
+          mouse_event.y = MiddleY(expected_rect);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseMoveEvent(mouse_event, false);
+        }
+        break;
+      case OSR_TEST_CLICK_RIGHT:
+      case OSR_TEST_SCREEN_POINT:
+      case OSR_TEST_CONTEXT_MENU:
+        if (StartTest()) {
+          CefMouseEvent mouse_event;
+          const CefRect& expected_rect = GetElementBounds("LI04");
+          mouse_event.x = MiddleX(expected_rect);
+          mouse_event.y = MiddleY(expected_rect);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_RIGHT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_RIGHT, true,
+                                                  1);
+        }
+        break;
+      case OSR_TEST_CLICK_LEFT:
+        if (StartTest()) {
+          CefMouseEvent mouse_event;
+          const CefRect& expected_rect = GetElementBounds("LI00");
+          mouse_event.x = MiddleX(expected_rect);
+          mouse_event.y = MiddleY(expected_rect);
+
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+        }
+        break;
+      case OSR_TEST_RESIZE:
+        if (StartTest()) {
+          browser->GetHost()->WasResized();
+        } else {
+          // There may be some partial repaints before the full repaint at the
+          // desired size.
+          const int desired_width = GetScaledInt(kOsrWidth) * 2;
+          const int desired_height = GetScaledInt(kOsrHeight) * 2;
+
+          EXPECT_EQ(dirtyRects.size(), 1U);
+          if (width == desired_width && height == desired_height &&
+              IsFullRepaint(dirtyRects[0], width, height)) {
+            DestroySucceededTestSoon();
+          }
+        }
+        break;
+      case OSR_TEST_INVALIDATE: {
+        if (StartTest()) {
+          browser->GetHost()->Invalidate(PET_VIEW);
+        } else {
+          EXPECT_EQ(dirtyRects.size(), 1U);
+          const CefRect& expected_rect =
+              GetScaledRect(CefRect(0, 0, kOsrWidth, kOsrHeight));
+          // There may be some partial repaints before the full repaint.
+          if (dirtyRects[0] == expected_rect)
+            DestroySucceededTestSoon();
+        }
+        break;
+      }
+      case OSR_TEST_KEY_EVENTS:
+        if (StartTest()) {
+          // click inside edit box
+          CefMouseEvent mouse_event;
+          const CefRect& editbox = GetElementBounds("editbox");
+          mouse_event.x = MiddleX(editbox);
+          mouse_event.y = MiddleY(editbox);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+
+          // write "done" word
+          CefKeyEvent event;
+          event.is_system_key = false;
+          event.modifiers = 0;
+
+          size_t word_length = strlen(kKeyTestWord);
+          for (size_t i = 0; i < word_length; ++i) {
+#if defined(OS_WIN)
+            SendKeyEvent(browser, kKeyTestWord[i]);
+#elif defined(OS_MACOSX) || defined(OS_LINUX)
+            SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]);
+#else
+#error "Unsupported platform"
+#endif
+          }
+
+          // click button to navigate
+          const CefRect& btnnavigate = GetElementBounds("btnnavigate");
+          mouse_event.x = MiddleX(btnnavigate);
+          mouse_event.y = MiddleY(btnnavigate);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+        }
+        break;
+      case OSR_TEST_TOOLTIP:
+        if (StartTest()) {
+          CefMouseEvent mouse_event;
+          const CefRect& expected_rect = GetElementBounds("LI10");
+          mouse_event.x = MiddleX(expected_rect);
+          mouse_event.y = MiddleY(expected_rect);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseMoveEvent(mouse_event, false);
+        }
+        break;
+      case OSR_TEST_SCROLLING: {
+        static const int deltaY = 10;
+        if (StartTest()) {
+          // scroll down once
+          CefMouseEvent mouse_event;
+          const CefRect& expected_rect = GetElementBounds("LI00");
+          mouse_event.x = MiddleX(expected_rect);
+          mouse_event.y = MiddleY(expected_rect);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -deltaY);
+        } else {
+          EXPECT_EQ(dirtyRects.size(), 1U);
+          const CefRect& expected_rect =
+              GetScaledRect(CefRect(0, 0, kOsrWidth, kOsrHeight));
+          // There may be some partial repaints before the full repaint.
+          if (dirtyRects[0] == expected_rect)
+            DestroySucceededTestSoon();
+        }
+        break;
+      }
+      case OSR_TEST_POPUP_HIDE_ON_CLICK:
+        if (StartTest()) {
+          ExpandDropDown();
+          // Wait for the first popup paint to occur
+        } else if (type == PET_POPUP) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = 1;
+          mouse_event.y = 1;
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+        }
+        break;
+      case OSR_TEST_POPUP_HIDE_ON_SCROLL:
+        if (StartTest()) {
+          ExpandDropDown();
+          // Wait for the first popup paint to occur
+        } else if (type == PET_POPUP) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = mouse_event.y = 1;
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -10);
+        }
+        break;
+      case OSR_TEST_POPUP_HIDE_ON_BLUR:
+        if (StartTest()) {
+          ExpandDropDown();
+          // Wait for the first popup paint to occur
+        } else if (type == PET_POPUP) {
+          browser->GetHost()->SendFocusEvent(false);
+        }
+        break;
+      case OSR_TEST_POPUP_HIDE_ON_ESC:
+        if (StartTest()) {
+          ExpandDropDown();
+          // Wait for the first popup paint to occur
+        } else if (type == PET_POPUP) {
+#if defined(OS_WIN)
+          SendKeyEvent(browser, VK_ESCAPE);
+#elif defined(OS_MACOSX) || defined(OS_LINUX)
+          SendKeyEvent(browser, kNativeKeyEscape, VKEY_ESCAPE);
+#else
+#error "Unsupported platform"
+#endif
+        }
+        break;
+      case OSR_TEST_POPUP_SHOW:
+      case OSR_TEST_POPUP_SIZE:
+        if (StartTest()) {
+          ExpandDropDown();
+        }
+        break;
+      case OSR_TEST_POPUP_PAINT:
+        if (StartTest()) {
+          ExpandDropDown();
+        } else if (type == PET_POPUP) {
+          EXPECT_EQ(dirtyRects.size(), 1U);
+          const CefRect& expanded_select_rect =
+              GetScaledRect(kExpandedSelectRect);
+          EXPECT_EQ(0, dirtyRects[0].x);
+          EXPECT_EQ(0, dirtyRects[0].y);
+          if (ExpectComputedPopupSize()) {
+            EXPECT_EQ(expanded_select_rect.width, dirtyRects[0].width);
+            EXPECT_EQ(expanded_select_rect.height, dirtyRects[0].height);
+          } else {
+            EXPECT_GT(dirtyRects[0].width, kExpandedSelectRect.width);
+            EXPECT_GT(dirtyRects[0].height, kExpandedSelectRect.height);
+          }
+
+          // Unselected option background color is cyan.
+          // Go down 100 pixels to skip the selected option and over 5 pixels to
+          // avoid hitting the border.
+          const uint32 offset = dirtyRects[0].width * 100 + 5;
+          EXPECT_EQ(0xff00ffff,
+                    *(reinterpret_cast<const uint32*>(buffer) + offset));
+
+          if (ExpectComputedPopupSize()) {
+            EXPECT_EQ(expanded_select_rect.width, width);
+            EXPECT_EQ(expanded_select_rect.height, height);
+          } else {
+            EXPECT_GT(width, kExpandedSelectRect.width);
+            EXPECT_GT(height, kExpandedSelectRect.height);
+          }
+          DestroySucceededTestSoon();
+        }
+        break;
+      case OSR_TEST_POPUP_SCROLL_INSIDE: {
+        static enum {
+          NotStarted,
+          Started,
+          Scrolled
+        } scroll_inside_state = NotStarted;
+        if (StartTest()) {
+          ExpandDropDown();
+          scroll_inside_state = Started;
+        } else if (type == PET_POPUP) {
+          if (scroll_inside_state == Started) {
+            CefMouseEvent mouse_event;
+            mouse_event.x = MiddleX(kExpandedSelectRect);
+            mouse_event.y = MiddleY(kExpandedSelectRect);
+            mouse_event.modifiers = 0;
+            browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -10);
+            scroll_inside_state = Scrolled;
+          } else if (scroll_inside_state == Scrolled) {
+            const CefRect& expanded_select_rect =
+                GetScaledRect(kExpandedSelectRect);
+            EXPECT_EQ(dirtyRects.size(), 1U);
+
+            const int scaled_int_1 = GetScaledInt(1);
+            EXPECT_NEAR(0, dirtyRects[0].x, scaled_int_1);
+            EXPECT_NEAR(0, dirtyRects[0].y, scaled_int_1);
+            if (ExpectComputedPopupSize()) {
+              EXPECT_NEAR(expanded_select_rect.width, dirtyRects[0].width,
+                          scaled_int_1 * 2);
+              EXPECT_NEAR(expanded_select_rect.height, dirtyRects[0].height,
+                          scaled_int_1 * 2);
+            } else {
+              EXPECT_GT(dirtyRects[0].width, kExpandedSelectRect.width);
+              EXPECT_GT(dirtyRects[0].height, kExpandedSelectRect.height);
+            }
+            DestroySucceededTestSoon();
+          }
+        }
+      } break;
+      case OSR_TEST_DRAG_DROP_START_DRAGGING:
+      case OSR_TEST_DRAG_DROP_UPDATE_CURSOR:
+      case OSR_TEST_DRAG_DROP_DROP: {
+        // trigger the StartDragging event
+        if (StartTest()) {
+          // move the mouse over the element to drag
+          CefMouseEvent mouse_event;
+          const CefRect& dragdiv = GetElementBounds("dragdiv");
+          mouse_event.x = MiddleX(dragdiv);
+          mouse_event.y = MiddleY(dragdiv);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseMoveEvent(mouse_event, false);
+          // click on the element to drag
+          mouse_event.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          // move the mouse to start dragging
+          mouse_event.x -= 5;
+          mouse_event.y -= 5;
+          browser->GetHost()->SendMouseMoveEvent(mouse_event, false);
+        }
+      } break;
+      case OSR_TEST_IME_COMMIT_TEXT: {
+        // trigger the IME Set Composition event
+        if (StartTest()) {
+          // click inside edit box so that text could be entered
+          CefMouseEvent mouse_event;
+          const CefRect& editbox = GetElementBounds("editbox");
+          mouse_event.x = MiddleX(editbox);
+          mouse_event.y = MiddleY(editbox);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+
+          size_t word_length = strlen(kKeyTestWord);
+          // Add some input keys to edit box
+          for (size_t i = 0; i < word_length; ++i) {
+#if defined(OS_WIN)
+            SendKeyEvent(browser, kKeyTestWord[i]);
+#elif defined(OS_MACOSX) || defined(OS_LINUX)
+            SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]);
+#else
+#error "Unsupported platform"
+#endif
+          }
+          // This text should be honored instead of 'ka' added via key events
+          CefString markedText("osrimecommit");
+
+          CefRange range(0, static_cast<int>(markedText.length()));
+          browser->GetHost()->ImeCommitText(markedText, range, 0);
+
+          // click button to navigate
+          const CefRect& btnnavigate = GetElementBounds("btnnavigate");
+          mouse_event.x = MiddleX(btnnavigate);
+          mouse_event.y = MiddleY(btnnavigate);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+        }
+      } break;
+      case OSR_TEST_IME_FINISH_COMPOSITION: {
+        // trigger the IME Set Composition event
+        if (StartTest()) {
+          // click inside edit box so that text could be entered
+          CefMouseEvent mouse_event;
+          const CefRect& editbox = GetElementBounds("editbox");
+          mouse_event.x = MiddleX(editbox);
+          mouse_event.y = MiddleY(editbox);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+
+          size_t word_length = strlen(kKeyTestWord);
+          // Add some input keys to edit box
+          for (size_t i = 0; i < word_length; ++i) {
+#if defined(OS_WIN)
+            SendKeyEvent(browser, kKeyTestWord[i]);
+#elif defined(OS_MACOSX) || defined(OS_LINUX)
+            SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]);
+#else
+#error "Unsupported platform"
+#endif
+          }
+
+          // Finish Composition should set the existing composition
+          browser->GetHost()->ImeFinishComposingText(true);
+
+          // click button to navigate
+          const CefRect& btnnavigate = GetElementBounds("btnnavigate");
+          mouse_event.x = MiddleX(btnnavigate);
+          mouse_event.y = MiddleY(btnnavigate);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+        }
+      } break;
+      case OSR_TEST_IME_CANCEL_COMPOSITION: {
+        // trigger the IME Set Composition event
+        if (StartTest()) {
+          // click inside edit box so that text could be entered
+          CefMouseEvent mouse_event;
+          const CefRect& editbox = GetElementBounds("editbox");
+          mouse_event.x = MiddleX(editbox);
+          mouse_event.y = MiddleY(editbox);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+          // Add some input keys to edit box
+          CefString markedText("か");
+          std::vector<CefCompositionUnderline> underlines;
+
+          // Use a thin black underline by default.
+          CefRange range(0, static_cast<int>(markedText.length()));
+          cef_composition_underline_t line = {range, 0xFF000000, 0, false};
+          underlines.push_back(line);
+
+          CefRange replacement_range(0, static_cast<int>(markedText.length()));
+          CefRange selection_range(0, static_cast<int>(markedText.length()));
+
+          // Composition should be updated
+          browser->GetHost()->ImeSetComposition(
+              markedText, underlines, replacement_range, selection_range);
+
+          // CancelComposition should clean up the edit text
+          browser->GetHost()->ImeCancelComposition();
+
+          // click button to navigate and verify
+          const CefRect& btnnavigate = GetElementBounds("btnnavigate");
+          mouse_event.x = MiddleX(btnnavigate);
+          mouse_event.y = MiddleY(btnnavigate);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+        }
+      } break;
+      case OSR_TEST_IME_SET_COMPOSITION: {
+        // trigger the IME Set Composition event
+        if (StartTest()) {
+          // click inside edit box so that text could be entered
+          CefMouseEvent mouse_event;
+          const CefRect& editbox = GetElementBounds("editbox");
+          mouse_event.x = MiddleX(editbox);
+          mouse_event.y = MiddleY(editbox);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+
+          // Now set some intermediate text composition
+          CefString markedText("か");
+          std::vector<CefCompositionUnderline> underlines;
+
+          // Use a thin black underline by default.
+          CefRange range(0, static_cast<int>(markedText.length()));
+          cef_composition_underline_t line = {range, 0xFF000000, 0, false};
+          underlines.push_back(line);
+
+          CefRange replacement_range(0, static_cast<int>(markedText.length()));
+          CefRange selection_range(0, static_cast<int>(markedText.length()));
+
+          // This should update composition range and
+          // trigger the compositionRangeChanged callback
+          browser->GetHost()->ImeSetComposition(
+              markedText, underlines, replacement_range, selection_range);
+        }
+      } break;
+      case OSR_TEST_TEXT_SELECTION_CHANGE: {
+        // trigger the text selection changed event
+        if (StartTest()) {
+          // click inside list element so text range will be selected.
+          CefMouseEvent mouse_event;
+          const CefRect& expected_rect = GetElementBounds("LI11");
+          mouse_event.x = MiddleX(expected_rect);
+          mouse_event.y = MiddleY(expected_rect);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+        }
+      } break;
+      case OSR_TEST_VIRTUAL_KEYBOARD: {
+        if (StartTest()) {
+          CefMouseEvent mouse_event;
+          const CefRect& input = GetElementBounds("email");
+          mouse_event.x = MiddleX(input);
+          mouse_event.y = MiddleY(input);
+          mouse_event.modifiers = 0;
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                  1);
+          browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true,
+                                                  1);
+        }
+      } break;
+      case OSR_TEST_TOUCH_START:
+      case OSR_TEST_TOUCH_MOVE:
+      case OSR_TEST_TOUCH_END:
+      case OSR_TEST_TOUCH_CANCEL: {
+        // We trigger a valid Touch workflow sequence and close the tests
+        // at seperate points for the 4 cases
+        if (StartTest()) {
+          const CefRect& touchdiv = GetElementBounds("touchdiv");
+          // click inside edit box so that text could be entered
+          CefTouchEvent touch_event1;
+          touch_event1.id = 0;
+          touch_event1.x = MiddleX(touchdiv) - 45;
+          touch_event1.y = MiddleY(touchdiv);
+          touch_event1.modifiers = 0;
+          touch_event1.type = CEF_TET_PRESSED;
+
+          CefTouchEvent touch_event2;
+          touch_event2.id = 1;
+          touch_event2.x = MiddleX(touchdiv) + 45;
+          touch_event2.y = MiddleY(touchdiv);
+          touch_event2.modifiers = 0;
+          touch_event2.type = CEF_TET_PRESSED;
+
+          browser->GetHost()->SendTouchEvent(touch_event1);
+          browser->GetHost()->SendTouchEvent(touch_event2);
+
+          // Move the Touch fingers closer
+          touch_event1.type = touch_event2.type = CEF_TET_MOVED;
+          for (size_t i = 0; i < 40; i++) {
+            touch_event1.x++;
+            touch_event2.x--;
+            browser->GetHost()->SendTouchEvent(touch_event1);
+            browser->GetHost()->SendTouchEvent(touch_event2);
+          }
+
+          // Now release the Touch fingers or cancel them
+          if (test_type_ == OSR_TEST_TOUCH_CANCEL)
+            touch_event1.type = touch_event2.type = CEF_TET_CANCELLED;
+          else
+            touch_event1.type = touch_event2.type = CEF_TET_RELEASED;
+          browser->GetHost()->SendTouchEvent(touch_event1);
+          browser->GetHost()->SendTouchEvent(touch_event2);
+        }
+      } break;
+      case OSR_TEST_PEN: {
+        if (StartTest()) {
+          const CefRect& pointerdiv = GetElementBounds("pointerdiv");
+          CefTouchEvent touch_event;
+          touch_event.x = MiddleX(pointerdiv) - 45;
+          touch_event.y = MiddleY(pointerdiv);
+          touch_event.type = CEF_TET_PRESSED;
+          touch_event.pointer_type = CEF_POINTER_TYPE_PEN;
+
+          browser->GetHost()->SendTouchEvent(touch_event);
+
+          touch_event.type = CEF_TET_MOVED;
+          for (size_t i = 0; i < 40; i++) {
+            touch_event.x++;
+            browser->GetHost()->SendTouchEvent(touch_event);
+          }
+
+          touch_event.type = CEF_TET_RELEASED;
+          browser->GetHost()->SendTouchEvent(touch_event);
+        }
+      } break;
+      default:
+        break;
+    }
+  }
+
+  bool OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source) override {
+    if (source == FOCUS_SOURCE_NAVIGATION) {
+      got_navigation_focus_event_.yes();
+
+      // Ignore focus from the original navigation when we're testing focus
+      // event delivery.
+      if (test_type_ == OSR_TEST_FOCUS)
+        return true;
+      return false;
+    }
+
+    EXPECT_EQ(source, FOCUS_SOURCE_SYSTEM);
+    got_system_focus_event_.yes();
+    return false;
+  }
+
+  void OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) override {
+    if (test_type_ == OSR_TEST_TAKE_FOCUS) {
+      EXPECT_TRUE(true);
+      DestroySucceededTestSoon();
+    }
+  }
+
+  void OnGotFocus(CefRefPtr<CefBrowser> browser) override {
+    if (test_type_ == OSR_TEST_GOT_FOCUS) {
+      EXPECT_TRUE(true);
+      DestroySucceededTestSoon();
+    }
+  }
+
+  void OnCursorChange(CefRefPtr<CefBrowser> browser,
+                      CefCursorHandle cursor,
+                      CursorType type,
+                      const CefCursorInfo& custom_cursor_info) override {
+    if (test_type_ == OSR_TEST_CURSOR && started()) {
+      EXPECT_EQ(CT_HAND, type);
+      EXPECT_EQ(nullptr, custom_cursor_info.buffer);
+      DestroySucceededTestSoon();
+    }
+  }
+
+  void OnImeCompositionRangeChanged(
+      CefRefPtr<CefBrowser> browser,
+      const CefRange& range,
+      const CefRenderHandler::RectList& bounds) override {
+    if (test_type_ == OSR_TEST_IME_SET_COMPOSITION && started()) {
+      EXPECT_EQ(range.from, 0);
+      EXPECT_EQ(range.to, 1);
+      EXPECT_EQ(1U, bounds.size());
+      DestroySucceededTestSoon();
+    }
+  }
+
+  bool StartDragging(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefDragData> drag_data,
+                     CefRenderHandler::DragOperationsMask allowed_ops,
+                     int x,
+                     int y) override {
+    if (test_type_ == OSR_TEST_DRAG_DROP_START_DRAGGING && started()) {
+      // Verify the drag image representation.
+      const CefRect& dragdiv = GetElementBounds("dragdiv");
+      EXPECT_TRUE(drag_data->HasImage());
+      CefRefPtr<CefImage> image = drag_data->GetImage();
+      EXPECT_TRUE(image.get() != nullptr);
+      if (image.get()) {
+        // Drag image height seems to always be + 1px greater than the drag rect
+        // on Linux. Therefore allow it to be +/- 1px.
+        EXPECT_NEAR(static_cast<int>(image->GetWidth()), dragdiv.width, 1);
+        EXPECT_NEAR(static_cast<int>(image->GetHeight()), dragdiv.height, 1);
+      }
+      // During testing hotspot (x, y) was (15, 23) at 1x scale and (15, 18) at
+      // 2x scale. Since the mechanism for determining this position is unclear
+      // test only that it falls within the rect boundaries.
+      CefPoint hotspot = drag_data->GetImageHotspot();
+      EXPECT_GT(hotspot.x, 0);
+      EXPECT_LT(hotspot.x, GetScaledInt(dragdiv.width));
+      EXPECT_GT(hotspot.y, 0);
+      EXPECT_LT(hotspot.y, GetScaledInt(dragdiv.height));
+
+      DestroySucceededTestSoon();
+      return false;
+    } else if ((test_type_ == OSR_TEST_DRAG_DROP_UPDATE_CURSOR ||
+                test_type_ == OSR_TEST_DRAG_DROP_DROP) &&
+               started()) {
+      // place the mouse over the drop area to trigger UpdateDragCursor
+      CefRefPtr<CefDragData> data = drag_data->Clone();
+      data->ResetFileContents();
+      CefMouseEvent ev;
+      const CefRect& dragdiv = GetElementBounds("dragdiv");
+      ev.x = MiddleX(dragdiv) - 5;
+      ev.y = MiddleY(dragdiv) - 5;
+      ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
+      browser->GetHost()->DragTargetDragEnter(data, ev, allowed_ops);
+
+      const CefRect& dropdiv = GetElementBounds("dropdiv");
+      ev.x = MiddleX(dropdiv);
+      ev.y = MiddleY(dropdiv);
+      browser->GetHost()->SendMouseMoveEvent(ev, false);
+      browser->GetHost()->DragTargetDragOver(ev, allowed_ops);
+
+      ev.x += 5;
+      ev.y += 5;
+      browser->GetHost()->SendMouseMoveEvent(ev, false);
+      browser->GetHost()->DragTargetDragOver(ev, allowed_ops);
+      return true;
+    }
+    return false;
+  }
+
+  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
+                        DragOperation operation) override {
+    if (test_type_ == OSR_TEST_DRAG_DROP_UPDATE_CURSOR && started()) {
+      if (operation != DRAG_OPERATION_NONE) {
+        const CefRect& dropdiv = GetElementBounds("dropdiv");
+        browser->GetHost()->DragSourceEndedAt(
+            MiddleX(dropdiv), MiddleY(dropdiv), DRAG_OPERATION_NONE);
+        browser->GetHost()->DragSourceSystemDragEnded();
+        DestroySucceededTestSoon();
+      }
+    } else if (test_type_ == OSR_TEST_DRAG_DROP_DROP && started()) {
+      // Don't end the drag multiple times.
+      if (got_update_cursor_)
+        return;
+      got_update_cursor_.yes();
+
+      CefMouseEvent ev;
+      const CefRect& dropdiv = GetElementBounds("dropdiv");
+      ev.x = MiddleX(dropdiv);
+      ev.y = MiddleY(dropdiv);
+      ev.modifiers = 0;
+      browser->GetHost()->SendMouseClickEvent(ev, MBT_LEFT, true, 1);
+      browser->GetHost()->DragTargetDrop(ev);
+      browser->GetHost()->DragSourceEndedAt(ev.x, ev.y, operation);
+      browser->GetHost()->DragSourceSystemDragEnded();
+    }
+  }
+
+  void OnTextSelectionChanged(CefRefPtr<CefBrowser> browser,
+                              const CefString& selected_text,
+                              const CefRange& selected_range) override {
+    if (test_type_ == OSR_TEST_TEXT_SELECTION_CHANGE && started()) {
+      if (!got_initial_text_selection_event_) {
+        got_initial_text_selection_event_.yes();
+      } else {
+        EXPECT_STREQ("SELECTED_TEXT_RANGE", selected_text.ToString().c_str());
+        DestroySucceededTestSoon();
+      }
+    }
+  }
+
+  void OnVirtualKeyboardRequested(CefRefPtr<CefBrowser> browser,
+                                  TextInputMode input_mode) override {
+    if (test_type_ == OSR_TEST_VIRTUAL_KEYBOARD && started()) {
+      if (!got_virtual_keyboard_event_.isSet()) {
+        got_virtual_keyboard_event_.yes();
+        EXPECT_EQ(CEF_TEXT_INPUT_MODE_EMAIL, input_mode);
+
+        CefMouseEvent mouse_event;
+        const CefRect& input = GetElementBounds("LI01");
+        mouse_event.x = MiddleX(input);
+        mouse_event.y = MiddleY(input);
+        mouse_event.modifiers = 0;
+        browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                1);
+        browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
+      } else {
+        EXPECT_EQ(CEF_TEXT_INPUT_MODE_NONE, input_mode);
+        DestroySucceededTestSoon();
+      }
+    }
+  }
+
+  bool OnTooltip(CefRefPtr<CefBrowser> browser, CefString& text) override {
+    if (test_type_ == OSR_TEST_TOOLTIP && started()) {
+      EXPECT_STREQ("EXPECTED_TOOLTIP", text.ToString().c_str());
+      DestroySucceededTestSoon();
+    }
+    return false;
+  }
+
+  void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefContextMenuParams> params,
+                           CefRefPtr<CefMenuModel> model) override {
+    if (!started())
+      return;
+    if (test_type_ == OSR_TEST_CLICK_RIGHT) {
+      const CefRect& expected_rect = GetElementBounds("LI04");
+      EXPECT_EQ(params->GetXCoord(), MiddleX(expected_rect));
+      EXPECT_EQ(params->GetYCoord(), MiddleY(expected_rect));
+      DestroySucceededTestSoon();
+    } else if (test_type_ == OSR_TEST_CONTEXT_MENU) {
+      // This test will pass if it does not crash on destruction
+      DestroySucceededTestSoon();
+    }
+  }
+
+  // OSRTestHandler functions
+  void CreateOSRBrowser(const CefString& url) {
+    CefWindowInfo windowInfo;
+    CefBrowserSettings settings;
+
+    if (test_type_ != OSR_TEST_TRANSPARENCY) {
+      // Explicitly set an opaque background color to disable transparency.
+      settings.background_color = CefColorSetARGB(255, 255, 255, 255);
+    }
+
+#if defined(OS_WIN)
+    windowInfo.SetAsWindowless(GetDesktopWindow());
+#elif defined(OS_MACOSX)
+    // An actual vies is needed only for the ContextMenu test. The menu runner
+    // checks if the view is not nil before showing the context menu.
+    if (test_type_ == OSR_TEST_CONTEXT_MENU)
+      windowInfo.SetAsWindowless(osr_unittests::GetFakeView());
+    else
+      windowInfo.SetAsWindowless(kNullWindowHandle);
+#elif defined(OS_LINUX)
+    windowInfo.SetAsWindowless(kNullWindowHandle);
+#else
+#error "Unsupported platform"
+#endif
+    CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, nullptr,
+                                  nullptr);
+  }
+
+  CefRect GetScaledRect(const CefRect& rect) const {
+    return client::LogicalToDevice(rect, scale_factor_);
+  }
+
+  int GetScaledInt(int value) const {
+    return client::LogicalToDevice(value, scale_factor_);
+  }
+
+  CefRect GetElementBounds(const std::string& id) {
+    ElementBoundsMap::const_iterator it = element_bounds_.find(id);
+    if (it != element_bounds_.end()) {
+      return it->second;
+    }
+    return CefRect();
+  }
+
+  static bool IsFullRepaint(const CefRect& rc, int width, int height) {
+    return rc.width == width && rc.height == height;
+  }
+
+  static bool IsBackgroundInBuffer(const uint32* buffer,
+                                   size_t size,
+                                   uint32 rgba) {
+    for (size_t i = 0; i < size; i++) {
+      if (buffer[i] != rgba) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  static inline int MiddleX(const CefRect& rect) {
+    return rect.x + rect.width / 2;
+  }
+
+  static inline int MiddleY(const CefRect& rect) {
+    return rect.y + rect.height / 2;
+  }
+
+  bool ExpectComputedPopupSize() const {
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+    // On Windows the device scale factor is ignored in Blink when computing
+    // the default form control font size (see https://crbug.com/674663#c11).
+    // This results in better font size display but also means that we won't
+    // get the expected (scaled) width/height value for non-1.0 scale factor
+    // select popups.
+    // On both Windows and Linux the non-1.0 scale factor size is off by a few
+    // pixels so we can't perform an exact comparison.
+    return scale_factor_ == 1.0;
+#else
+    return true;
+#endif
+  }
+
+  void DestroySucceededTestSoon() {
+    if (succeeded())
+      return;
+    if (++event_count_ == event_total_)
+      CefPostTask(TID_UI, base::Bind(&OSRTestHandler::DestroyTest, this));
+  }
+
+  void DestroyTest() override {
+    // Always get the OnSetFocus call for the initial navigation.
+    EXPECT_TRUE(got_navigation_focus_event_);
+
+    if (test_type_ == OSR_TEST_FOCUS || (test_type_ >= OSR_TEST_POPUP_FIRST &&
+                                         test_type_ <= OSR_TEST_POPUP_LAST)) {
+      // SetFocus is called by the system when we explicitly set the focus and
+      // when popups are dismissed.
+      EXPECT_TRUE(got_system_focus_event_);
+    } else if (test_type_ == OSR_TEST_TEXT_SELECTION_CHANGE) {
+      EXPECT_TRUE(got_initial_text_selection_event_);
+    } else {
+      EXPECT_FALSE(got_system_focus_event_);
+    }
+
+    RoutingTestHandler::DestroyTest();
+  }
+
+  void ExpandDropDown() {
+    GetBrowser()->GetHost()->SendFocusEvent(true);
+    CefMouseEvent mouse_event;
+
+    const CefRect& LI11select = GetElementBounds("LI11select");
+    mouse_event.x = MiddleX(LI11select);
+    mouse_event.y = MiddleY(LI11select);
+    mouse_event.modifiers = 0;
+    GetBrowser()->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false,
+                                                 1);
+  }
+
+  void SendKeyEvent(CefRefPtr<CefBrowser> browser,
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+                    unsigned int native_key_code,
+#endif
+                    int key_code) {
+    CefKeyEvent event;
+    event.is_system_key = false;
+    event.modifiers = 0;
+
+#if defined(OS_WIN)
+    BYTE VkCode = LOBYTE(VkKeyScanA(key_code));
+    UINT scanCode = MapVirtualKey(VkCode, MAPVK_VK_TO_VSC);
+    event.native_key_code = (scanCode << 16) |  // key scan code
+                            1;                  // key repeat count
+    event.windows_key_code = VkCode;
+#elif defined(OS_MACOSX)
+    event.native_key_code = native_key_code;
+    // Note that this is only correct for lower-case characters. If |key_code|
+    // was an upper-case character then |event.character| would be the upper-
+    // case character and |event.unmodified_character| would be the lower-case
+    // character (e.g. the character without the shift modifier applied).
+    event.character = event.unmodified_character = key_code;
+#elif defined(OS_LINUX)
+    event.native_key_code = native_key_code;
+    event.windows_key_code = key_code;
+    event.character = event.unmodified_character = native_key_code;
+#else
+    NOTREACHED();
+#endif
+    event.type = KEYEVENT_RAWKEYDOWN;
+    browser->GetHost()->SendKeyEvent(event);
+
+#if defined(OS_WIN)
+    event.windows_key_code = key_code;
+#endif
+    event.type = KEYEVENT_CHAR;
+    browser->GetHost()->SendKeyEvent(event);
+
+#if defined(OS_WIN)
+    event.windows_key_code = VkCode;
+    // bits 30 and 31 should be always 1 for WM_KEYUP
+    event.native_key_code |= 0xC0000000;
+#endif
+    event.type = KEYEVENT_KEYUP;
+    browser->GetHost()->SendKeyEvent(event);
+  }
+
+  // true if the events for this test are already sent
+  bool started() { return started_; }
+
+  // true if the exit point was reached, even the result is not
+  // the expected one
+  bool succeeded() { return (event_count_ == event_total_); }
+
+  // will mark test as started and will return true only the first time
+  // it is called
+  bool StartTest() {
+    if (started_)
+      return false;
+    started_ = true;
+    return true;
+  }
+
+ private:
+  OSRTestType test_type_;
+  float scale_factor_;
+  int event_count_;
+  int event_total_;
+  bool started_;
+  cef_touch_event_type_t touch_state_;
+  TrackCallback got_update_cursor_;
+  TrackCallback got_navigation_focus_event_;
+  TrackCallback got_system_focus_event_;
+  TrackCallback got_initial_text_selection_event_;
+  TrackCallback got_virtual_keyboard_event_;
+
+  typedef std::map<std::string, CefRect> ElementBoundsMap;
+  ElementBoundsMap element_bounds_;
+
+  IMPLEMENT_REFCOUNTING(OSRTestHandler);
+};
+
+}  // namespace
+
+// generic test
+#define OSR_TEST(name, test_mode, scale_factor)      \
+  TEST(OSRTest, name) {                              \
+    CefRefPtr<OSRTestHandler> handler =              \
+        new OSRTestHandler(test_mode, scale_factor); \
+    handler->ExecuteTest();                          \
+    EXPECT_TRUE(handler->succeeded());               \
+    ReleaseAndWaitForDestructor(handler);            \
+  }
+
+// tests
+OSR_TEST(Windowless, OSR_TEST_IS_WINDOWLESS, 1.0f)
+OSR_TEST(Windowless2x, OSR_TEST_IS_WINDOWLESS, 2.0f)
+OSR_TEST(Focus, OSR_TEST_FOCUS, 1.0f)
+OSR_TEST(Focus2x, OSR_TEST_FOCUS, 2.0f)
+OSR_TEST(TakeFocus, OSR_TEST_TAKE_FOCUS, 1.0f)
+OSR_TEST(TakeFocus2x, OSR_TEST_TAKE_FOCUS, 2.0f)
+OSR_TEST(GotFocus, OSR_TEST_GOT_FOCUS, 1.0f)
+OSR_TEST(GotFocus2x, OSR_TEST_GOT_FOCUS, 2.0f)
+OSR_TEST(Paint, OSR_TEST_PAINT, 1.0f)
+OSR_TEST(Paint2x, OSR_TEST_PAINT, 2.0f)
+OSR_TEST(TransparentPaint, OSR_TEST_TRANSPARENCY, 1.0f)
+OSR_TEST(TransparentPaint2x, OSR_TEST_TRANSPARENCY, 2.0f)
+OSR_TEST(Cursor, OSR_TEST_CURSOR, 1.0f)
+OSR_TEST(Cursor2x, OSR_TEST_CURSOR, 2.0f)
+OSR_TEST(MouseMove, OSR_TEST_MOUSE_MOVE, 1.0f)
+OSR_TEST(MouseMove2x, OSR_TEST_MOUSE_MOVE, 2.0f)
+OSR_TEST(MouseRightClick, OSR_TEST_CLICK_RIGHT, 1.0f)
+OSR_TEST(MouseRightClick2x, OSR_TEST_CLICK_RIGHT, 2.0f)
+OSR_TEST(MouseLeftClick, OSR_TEST_CLICK_LEFT, 1.0f)
+OSR_TEST(MouseLeftClick2x, OSR_TEST_CLICK_LEFT, 2.0f)
+OSR_TEST(ScreenPoint, OSR_TEST_SCREEN_POINT, 1.0f)
+OSR_TEST(ScreenPoint2x, OSR_TEST_SCREEN_POINT, 2.0f)
+OSR_TEST(Resize, OSR_TEST_RESIZE, 1.0f)
+OSR_TEST(Resize2x, OSR_TEST_RESIZE, 2.0f)
+OSR_TEST(Invalidate, OSR_TEST_INVALIDATE, 1.0f)
+OSR_TEST(Invalidate2x, OSR_TEST_INVALIDATE, 2.0f)
+OSR_TEST(KeyEvents, OSR_TEST_KEY_EVENTS, 1.0f)
+OSR_TEST(KeyEvents2x, OSR_TEST_KEY_EVENTS, 2.0f)
+OSR_TEST(Tooltip, OSR_TEST_TOOLTIP, 1.0f)
+OSR_TEST(Tooltip2x, OSR_TEST_TOOLTIP, 2.0f)
+OSR_TEST(Scrolling, OSR_TEST_SCROLLING, 1.0f)
+OSR_TEST(Scrolling2x, OSR_TEST_SCROLLING, 2.0f)
+OSR_TEST(ContextMenu, OSR_TEST_CONTEXT_MENU, 1.0f)
+OSR_TEST(ContextMenu2x, OSR_TEST_CONTEXT_MENU, 2.0f)
+OSR_TEST(PopupPaint, OSR_TEST_POPUP_PAINT, 1.0f)
+OSR_TEST(PopupPaint2x, OSR_TEST_POPUP_PAINT, 2.0f)
+OSR_TEST(PopupShow, OSR_TEST_POPUP_SHOW, 1.0f)
+OSR_TEST(PopupShow2x, OSR_TEST_POPUP_SHOW, 2.0f)
+OSR_TEST(PopupSize, OSR_TEST_POPUP_SIZE, 1.0f)
+OSR_TEST(PopupSize2x, OSR_TEST_POPUP_SIZE, 2.0f)
+OSR_TEST(PopupHideOnBlur, OSR_TEST_POPUP_HIDE_ON_BLUR, 1.0f)
+OSR_TEST(PopupHideOnBlur2x, OSR_TEST_POPUP_HIDE_ON_BLUR, 2.0f)
+OSR_TEST(PopupHideOnClick, OSR_TEST_POPUP_HIDE_ON_CLICK, 1.0f)
+OSR_TEST(PopupHideOnClick2x, OSR_TEST_POPUP_HIDE_ON_CLICK, 2.0f)
+OSR_TEST(PopupHideOnScroll, OSR_TEST_POPUP_HIDE_ON_SCROLL, 1.0f)
+OSR_TEST(PopupHideOnScroll2x, OSR_TEST_POPUP_HIDE_ON_SCROLL, 2.0f)
+OSR_TEST(PopupHideOnEsc, OSR_TEST_POPUP_HIDE_ON_ESC, 1.0f)
+OSR_TEST(PopupHideOnEsc2x, OSR_TEST_POPUP_HIDE_ON_ESC, 2.0f)
+OSR_TEST(PopupScrollInside, OSR_TEST_POPUP_SCROLL_INSIDE, 1.0f)
+OSR_TEST(PopupScrollInside2x, OSR_TEST_POPUP_SCROLL_INSIDE, 2.0f)
+OSR_TEST(DragDropStartDragging, OSR_TEST_DRAG_DROP_START_DRAGGING, 1.0f)
+OSR_TEST(DragDropStartDragging2x, OSR_TEST_DRAG_DROP_START_DRAGGING, 2.0f)
+OSR_TEST(DragDropUpdateCursor, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 1.0f)
+OSR_TEST(DragDropUpdateCursor2x, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 2.0f)
+OSR_TEST(DragDropDropElement, OSR_TEST_DRAG_DROP_DROP, 1.0f)
+OSR_TEST(DragDropDropElement2x, OSR_TEST_DRAG_DROP_DROP, 2.0f)
+OSR_TEST(IMESetComposition, OSR_TEST_IME_SET_COMPOSITION, 1.0f)
+OSR_TEST(IMESetComposition2x, OSR_TEST_IME_SET_COMPOSITION, 2.0f)
+OSR_TEST(IMECommitText, OSR_TEST_IME_COMMIT_TEXT, 1.0f)
+OSR_TEST(IMECommitText2x, OSR_TEST_IME_COMMIT_TEXT, 2.0f)
+OSR_TEST(IMEFinishComposition, OSR_TEST_IME_FINISH_COMPOSITION, 1.0f)
+OSR_TEST(IMEFinishComposition2x, OSR_TEST_IME_FINISH_COMPOSITION, 2.0f)
+OSR_TEST(IMECancelComposition, OSR_TEST_IME_CANCEL_COMPOSITION, 1.0f)
+OSR_TEST(IMECancelComposition2x, OSR_TEST_IME_CANCEL_COMPOSITION, 2.0f)
+OSR_TEST(TextSelectionChanged, OSR_TEST_TEXT_SELECTION_CHANGE, 1.0f)
+OSR_TEST(TextSelectionChanged2x, OSR_TEST_TEXT_SELECTION_CHANGE, 2.0f)
+OSR_TEST(VirtualKeyboard, OSR_TEST_VIRTUAL_KEYBOARD, 1.0f)
+OSR_TEST(TouchStart, OSR_TEST_TOUCH_START, 1.0f)
+OSR_TEST(TouchStart2X, OSR_TEST_TOUCH_START, 2.0f)
+OSR_TEST(TouchMove, OSR_TEST_TOUCH_MOVE, 1.0f)
+OSR_TEST(TouchMove2X, OSR_TEST_TOUCH_MOVE, 2.0f)
+OSR_TEST(TouchEnd, OSR_TEST_TOUCH_END, 1.0f)
+OSR_TEST(TouchEnd2X, OSR_TEST_TOUCH_END, 2.0f)
+OSR_TEST(TouchCancel, OSR_TEST_TOUCH_CANCEL, 1.0f)
+OSR_TEST(TouchCancel2X, OSR_TEST_TOUCH_CANCEL, 2.0f)
+OSR_TEST(PenEvent, OSR_TEST_PEN, 1.0f)
diff --git a/src/tests/ceftests/os_rendering_unittest_mac.h b/src/tests/ceftests/os_rendering_unittest_mac.h
new file mode 100644
index 0000000..b01cc0a
--- /dev/null
+++ b/src/tests/ceftests/os_rendering_unittest_mac.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_OS_RENDERING_UNITTEST_MAC_H_
+#define CEF_TESTS_UNITTESTS_OS_RENDERING_UNITTEST_MAC_H_
+
+#include "include/cef_base.h"
+
+namespace osr_unittests {
+
+CefWindowHandle GetFakeView();
+
+}  // namespace osr_unittests
+
+#endif
diff --git a/src/tests/ceftests/os_rendering_unittest_mac.mm b/src/tests/ceftests/os_rendering_unittest_mac.mm
new file mode 100644
index 0000000..6e24dc9
--- /dev/null
+++ b/src/tests/ceftests/os_rendering_unittest_mac.mm
@@ -0,0 +1,18 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "tests/ceftests/os_rendering_unittest_mac.h"
+
+namespace osr_unittests {
+
+CefWindowHandle GetFakeView() {
+  NSScreen* mainScreen = [NSScreen mainScreen];
+  NSRect screenRect = [mainScreen visibleFrame];
+  NSView* fakeView = [[NSView alloc] initWithFrame:screenRect];
+  return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(fakeView);
+}
+
+}  // namespace osr_unittests
diff --git a/src/tests/ceftests/osr_accessibility_unittest.cc b/src/tests/ceftests/osr_accessibility_unittest.cc
new file mode 100644
index 0000000..0cf10a4
--- /dev/null
+++ b/src/tests/ceftests/osr_accessibility_unittest.cc
@@ -0,0 +1,505 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_accessibility_handler.h"
+#include "include/cef_parser.h"
+#include "include/cef_waitable_event.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestUrl[] = "https://tests/AccessibilityTestHandler";
+const char kTipText[] = "Also known as User ID";
+
+// Default OSR widget size.
+const int kOsrWidth = 600;
+const int kOsrHeight = 400;
+
+// Test type.
+enum AccessibilityTestType {
+  // Enabling Accessibility should trigger the AccessibilityHandler callback
+  // with Accessibility tree details
+  TEST_ENABLE,
+  // Disabling Accessibility should disable accessibility notification changes
+  TEST_DISABLE,
+  // Focus change on element should trigger Accessibility focus event
+  TEST_FOCUS_CHANGE,
+  // Hide/Show etc should trigger Location Change callbacks
+  TEST_LOCATION_CHANGE
+};
+
+class AccessibilityTestHandler : public TestHandler,
+                                 public CefRenderHandler,
+                                 public CefAccessibilityHandler {
+ public:
+  AccessibilityTestHandler(const AccessibilityTestType& type)
+      : test_type_(type), edit_box_id_(-1), accessibility_disabled_(false) {}
+
+  CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() override {
+    return this;
+  }
+
+  CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; }
+
+  // Cef Renderer Handler Methods
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override {
+    rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+  }
+
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) override {
+    screen_info.rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+    screen_info.available_rect = screen_info.rect;
+    return true;
+  }
+
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) override {
+    // Do nothing.
+  }
+
+  void RunTest() override {
+    std::string html =
+        "<html><head><title>AccessibilityTest</title></head>"
+        "<body><span id='tipspan' role='tooltip' style='color:red;"
+        "margin:20px'>";
+    html += kTipText;
+    html +=
+        "</span>"
+        "<input id='editbox' type='text' aria-describedby='tipspan' "
+        "value='editbox' size='25px'/><input id='button' type='button' "
+        "value='button' style='margin:20px'/></body></html>";
+    AddResource(kTestUrl, html, "text/html");
+
+    // Create the browser
+    CreateOSRBrowser(kTestUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout(5000);
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    // Enable Accessibility
+    browser->GetHost()->SetAccessibilityState(STATE_ENABLED);
+    switch (test_type_) {
+      case TEST_ENABLE: {
+        // This should trigger OnAccessibilityTreeChange
+        // And update will be validated
+      } break;
+      case TEST_DISABLE: {
+        // Post a delayed task to disable Accessibility
+        CefPostDelayedTask(
+            TID_UI,
+            base::Bind(&AccessibilityTestHandler::DisableAccessibility, this,
+                       browser),
+            200);
+      } break;
+      // Delayed task will posted later after we have initial details
+      case TEST_FOCUS_CHANGE: {
+      } break;
+      case TEST_LOCATION_CHANGE: {
+      } break;
+    }
+  }
+
+  void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) override {
+    switch (test_type_) {
+      case TEST_ENABLE: {
+        TestEnableAccessibilityUpdate(value);
+      } break;
+      case TEST_DISABLE: {
+        // Once Accessibility is disabled in the delayed Task
+        // We should not reach here
+        EXPECT_FALSE(accessibility_disabled_);
+      } break;
+      case TEST_LOCATION_CHANGE: {
+        // find accessibility id of the edit box, before setting focus
+        if (edit_box_id_ == -1) {
+          CefRefPtr<CefDictionaryValue> update, event;
+          GetFirstUpdateAndEvent(value, update, event);
+          EXPECT_TRUE(update.get());
+
+          // Ignore other events.
+          if (!event.get() ||
+              event->GetString("event_type") != "layoutComplete") {
+            break;
+          }
+
+          SetEditBoxIdAndRect(update);
+          EXPECT_NE(edit_box_id_, -1);
+          // Post a delayed task to hide the span and trigger location change
+          CefPostDelayedTask(TID_UI,
+                             base::Bind(&AccessibilityTestHandler::HideEditBox,
+                                        this, GetBrowser()),
+                             200);
+        }
+      } break;
+      case TEST_FOCUS_CHANGE: {
+        // find accessibility id of the edit box, before setting focus
+        if (edit_box_id_ == -1) {
+          CefRefPtr<CefDictionaryValue> update, event;
+          GetFirstUpdateAndEvent(value, update, event);
+          EXPECT_TRUE(update.get());
+
+          // Ignore other events.
+          if (!event.get() ||
+              event->GetString("event_type") != "layoutComplete") {
+            break;
+          }
+
+          // Now post a delayed task to trigger focus to edit box
+          SetEditBoxIdAndRect(update);
+          EXPECT_NE(edit_box_id_, -1);
+
+          CefPostDelayedTask(
+              TID_UI,
+              base::Bind(&AccessibilityTestHandler::SetFocusOnEditBox, this,
+                         GetBrowser()),
+              200);
+        } else {
+          // Retrieve the "focus" event.
+          CefRefPtr<CefDictionaryValue> event;
+          if (!GetFirstMatchingEvent(value, "focus", event))
+            return;
+          EXPECT_TRUE(event.get());
+
+          // Verify that focus is set to expected element edit_box.
+          EXPECT_EQ(edit_box_id_, event->GetInt("id"));
+
+          // Now Post a delayed task to destroy the test giving
+          // sufficient time for any accessibility updates to come through
+          CefPostDelayedTask(
+              TID_UI, base::Bind(&AccessibilityTestHandler::DestroyTest, this),
+              500);
+        }
+      } break;
+    }
+  }
+
+  void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) override {
+    if (test_type_ == TEST_LOCATION_CHANGE) {
+      EXPECT_NE(edit_box_id_, -1);
+      EXPECT_TRUE(value.get());
+
+      // Change has a valid list
+      EXPECT_EQ(VTYPE_LIST, value->GetType());
+      CefRefPtr<CefListValue> list = value->GetList();
+      EXPECT_TRUE(list.get());
+
+      got_accessibility_location_change_.yes();
+    }
+
+    if (got_hide_edit_box_) {
+      // Now destroy the test.
+      CefPostTask(TID_UI,
+                  base::Bind(&AccessibilityTestHandler::DestroyTest, this));
+    }
+  }
+
+ private:
+  void CreateOSRBrowser(const CefString& url) {
+    CefWindowInfo windowInfo;
+    CefBrowserSettings settings;
+
+#if defined(OS_WIN)
+    windowInfo.SetAsWindowless(GetDesktopWindow());
+#elif defined(OS_MACOSX)
+    windowInfo.SetAsWindowless(kNullWindowHandle);
+#elif defined(OS_LINUX)
+    windowInfo.SetAsWindowless(kNullWindowHandle);
+#else
+#error "Unsupported platform"
+#endif
+    CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, nullptr,
+                                  nullptr);
+  }
+
+  void HideEditBox(CefRefPtr<CefBrowser> browser) {
+    // Hide the edit box.
+    // This should trigger Location update if enabled
+    browser->GetMainFrame()->ExecuteJavaScript(
+        "document.getElementById('editbox').style.display = 'none';", kTestUrl,
+        0);
+
+    got_hide_edit_box_.yes();
+  }
+
+  void SetFocusOnEditBox(CefRefPtr<CefBrowser> browser) {
+    // Set focus on edit box
+    // This should trigger accessibility update if enabled
+    browser->GetMainFrame()->ExecuteJavaScript(
+        "document.getElementById('editbox').focus();", kTestUrl, 0);
+  }
+
+  void DisableAccessibility(CefRefPtr<CefBrowser> browser) {
+    browser->GetHost()->SetAccessibilityState(STATE_DISABLED);
+    accessibility_disabled_ = true;
+    // Set focus on edit box
+    SetFocusOnEditBox(browser);
+
+    // Now Post a delayed task to destroy the test
+    // giving sufficient time for any accessibility updates to come through
+    CefPostDelayedTask(
+        TID_UI, base::Bind(&AccessibilityTestHandler::DestroyTest, this), 500);
+  }
+
+  static CefRefPtr<CefListValue> GetUpdateList(CefRefPtr<CefValue> value) {
+    EXPECT_TRUE(value.get());
+    EXPECT_EQ(value->GetType(), VTYPE_DICTIONARY);
+    CefRefPtr<CefDictionaryValue> topLevel = value->GetDictionary();
+    EXPECT_TRUE(topLevel.get());
+
+    return topLevel->GetList("updates");
+  }
+
+  static size_t GetUpdateListSize(CefRefPtr<CefValue> value) {
+    CefRefPtr<CefListValue> updates = GetUpdateList(value);
+    if (updates)
+      return updates->GetSize();
+    return 0U;
+  }
+
+  static CefRefPtr<CefDictionaryValue> GetUpdateValue(CefRefPtr<CefValue> value,
+                                                      size_t index) {
+    CefRefPtr<CefListValue> updates = GetUpdateList(value);
+    if (!updates)
+      return nullptr;
+    EXPECT_LT(index, updates->GetSize());
+    CefRefPtr<CefDictionaryValue> update = updates->GetDictionary(index);
+    EXPECT_TRUE(update);
+    return update;
+  }
+
+  static CefRefPtr<CefListValue> GetEventList(CefRefPtr<CefValue> value) {
+    EXPECT_TRUE(value.get());
+    EXPECT_EQ(value->GetType(), VTYPE_DICTIONARY);
+    CefRefPtr<CefDictionaryValue> topLevel = value->GetDictionary();
+    EXPECT_TRUE(topLevel.get());
+
+    return topLevel->GetList("events");
+  }
+
+  static size_t GetEventListSize(CefRefPtr<CefValue> value) {
+    CefRefPtr<CefListValue> events = GetEventList(value);
+    if (events)
+      return events->GetSize();
+    return 0U;
+  }
+
+  static CefRefPtr<CefDictionaryValue> GetEventValue(CefRefPtr<CefValue> value,
+                                                     size_t index) {
+    CefRefPtr<CefListValue> events = GetEventList(value);
+    if (!events)
+      return nullptr;
+    EXPECT_LT(index, events->GetSize());
+    CefRefPtr<CefDictionaryValue> event = events->GetDictionary(index);
+    EXPECT_TRUE(event);
+    return event;
+  }
+
+  static void GetFirstUpdateAndEvent(CefRefPtr<CefValue> value,
+                                     CefRefPtr<CefDictionaryValue>& update,
+                                     CefRefPtr<CefDictionaryValue>& event) {
+    if (GetUpdateListSize(value) > 0U)
+      update = GetUpdateValue(value, 0U);
+    if (GetEventListSize(value) > 0U)
+      event = GetEventValue(value, 0U);
+  }
+
+  static bool GetFirstMatchingEvent(CefRefPtr<CefValue> value,
+                                    const std::string& event_type,
+                                    CefRefPtr<CefDictionaryValue>& event) {
+    // Return the first event that matches the requested |event_type|.
+    const size_t event_size = GetEventListSize(value);
+    for (size_t i = 0; i < event_size; ++i) {
+      CefRefPtr<CefDictionaryValue> cur_event = GetEventValue(value, i);
+      const std::string& cur_event_type = cur_event->GetString("event_type");
+      if (cur_event_type == event_type) {
+        event = cur_event;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void TestEnableAccessibilityUpdate(CefRefPtr<CefValue> value) {
+    CefRefPtr<CefDictionaryValue> update, event;
+    GetFirstUpdateAndEvent(value, update, event);
+    EXPECT_TRUE(update.get());
+
+    // Ignore other events.
+    if (!event.get() || event->GetString("event_type") != "layoutComplete") {
+      return;
+    }
+
+    // Get update and validate it has tree data
+    EXPECT_TRUE(update->GetBool("has_tree_data"));
+    CefRefPtr<CefDictionaryValue> treeData = update->GetDictionary("tree_data");
+
+    // Validate title and Url
+    EXPECT_STREQ("AccessibilityTest",
+                 treeData->GetString("title").ToString().c_str());
+    EXPECT_STREQ(kTestUrl, treeData->GetString("url").ToString().c_str());
+
+    // Validate node data
+    CefRefPtr<CefListValue> nodes = update->GetList("nodes");
+    EXPECT_TRUE(nodes.get());
+    EXPECT_GT(nodes->GetSize(), 0U);
+
+    // Update has a valid root
+    CefRefPtr<CefDictionaryValue> root;
+    for (size_t index = 0; index < nodes->GetSize(); index++) {
+      CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
+      if (node->GetString("role").ToString() == "rootWebArea") {
+        root = node;
+        break;
+      }
+    }
+    EXPECT_TRUE(root.get());
+
+    // One div containing the tree elements.
+    CefRefPtr<CefListValue> childIDs = root->GetList("child_ids");
+    EXPECT_TRUE(childIDs.get());
+    EXPECT_EQ(1U, childIDs->GetSize());
+
+    // A parent Group div containing the child.
+    CefRefPtr<CefDictionaryValue> group;
+    for (size_t index = 0; index < nodes->GetSize(); index++) {
+      CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
+      if (node->GetString("role").ToString() == "genericContainer") {
+        group = node;
+        break;
+      }
+    }
+    EXPECT_TRUE(group.get());
+    // Validate Group is child of root WebArea.
+    EXPECT_EQ(group->GetInt("id"), childIDs->GetInt(0));
+
+    CefRefPtr<CefListValue> parentdiv = group->GetList("child_ids");
+    EXPECT_TRUE(parentdiv.get());
+    EXPECT_EQ(3U, parentdiv->GetSize());
+
+    int tipId = parentdiv->GetInt(0);
+    int editBoxId = parentdiv->GetInt(1);
+    int buttonId = parentdiv->GetInt(2);
+
+    // A parent Group div containing the child.
+    CefRefPtr<CefDictionaryValue> tip, editbox, button;
+    for (size_t index = 0; index < nodes->GetSize(); index++) {
+      CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
+      if (node->GetInt("id") == tipId) {
+        tip = node;
+      }
+      if (node->GetInt("id") == editBoxId) {
+        editbox = node;
+      }
+      if (node->GetInt("id") == buttonId) {
+        button = node;
+      }
+    }
+    EXPECT_TRUE(tip.get());
+    EXPECT_STREQ("tooltip", tip->GetString("role").ToString().c_str());
+    CefRefPtr<CefDictionaryValue> tipattr = tip->GetDictionary("attributes");
+    EXPECT_TRUE(tipattr.get());
+
+    EXPECT_TRUE(editbox.get());
+    EXPECT_STREQ("textField", editbox->GetString("role").ToString().c_str());
+    CefRefPtr<CefDictionaryValue> editattr =
+        editbox->GetDictionary("attributes");
+    // Validate ARIA Description tags for tipIdare associated with editbox.
+    EXPECT_TRUE(editattr.get());
+    EXPECT_EQ(tipId, editattr->GetList("describedbyIds")->GetInt(0));
+    EXPECT_STREQ(kTipText,
+                 editattr->GetString("description").ToString().c_str());
+
+    EXPECT_TRUE(button.get());
+    EXPECT_STREQ("button", button->GetString("role").ToString().c_str());
+
+    // Now Post a delayed task to destroy the test
+    // giving sufficient time for any accessibility updates to come through
+    CefPostDelayedTask(
+        TID_UI, base::Bind(&AccessibilityTestHandler::DestroyTest, this), 500);
+  }
+
+  // Find Edit box Id in accessibility tree.
+  void SetEditBoxIdAndRect(CefRefPtr<CefDictionaryValue> value) {
+    EXPECT_TRUE(value.get());
+    // Validate node data.
+    CefRefPtr<CefListValue> nodes = value->GetList("nodes");
+    EXPECT_TRUE(nodes.get());
+    EXPECT_GT(nodes->GetSize(), 0U);
+
+    // Find accessibility id for the text field.
+    for (size_t index = 0; index < nodes->GetSize(); index++) {
+      CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
+      if (node->GetString("role").ToString() == "textField") {
+        edit_box_id_ = node->GetInt("id");
+        CefRefPtr<CefDictionaryValue> loc = node->GetDictionary("location");
+        EXPECT_TRUE(loc.get());
+        EXPECT_GT(loc->GetDouble("x"), 0);
+        EXPECT_GT(loc->GetDouble("y"), 0);
+        EXPECT_GT(loc->GetDouble("width"), 0);
+        EXPECT_GT(loc->GetDouble("height"), 0);
+        break;
+      }
+    }
+  }
+
+  void DestroyTest() override {
+    if (test_type_ == TEST_LOCATION_CHANGE) {
+      EXPECT_GT(edit_box_id_, 0);
+      EXPECT_TRUE(got_hide_edit_box_);
+      EXPECT_TRUE(got_accessibility_location_change_);
+    }
+    TestHandler::DestroyTest();
+  }
+
+  AccessibilityTestType test_type_;
+  int edit_box_id_;
+  bool accessibility_disabled_;
+  TrackCallback got_hide_edit_box_;
+  TrackCallback got_accessibility_location_change_;
+
+  IMPLEMENT_REFCOUNTING(AccessibilityTestHandler);
+};
+
+}  // namespace
+
+TEST(OSRTest, AccessibilityEnable) {
+  CefRefPtr<AccessibilityTestHandler> handler =
+      new AccessibilityTestHandler(TEST_ENABLE);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(OSRTest, AccessibilityDisable) {
+  CefRefPtr<AccessibilityTestHandler> handler =
+      new AccessibilityTestHandler(TEST_DISABLE);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(OSRTest, AccessibilityFocusChange) {
+  CefRefPtr<AccessibilityTestHandler> handler =
+      new AccessibilityTestHandler(TEST_FOCUS_CHANGE);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(OSRTest, AccessibilityLocationChange) {
+  CefRefPtr<AccessibilityTestHandler> handler =
+      new AccessibilityTestHandler(TEST_LOCATION_CHANGE);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/osr_display_unittest.cc b/src/tests/ceftests/osr_display_unittest.cc
new file mode 100644
index 0000000..0855726
--- /dev/null
+++ b/src/tests/ceftests/osr_display_unittest.cc
@@ -0,0 +1,393 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/wrapper/cef_closure_task.h"
+
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestUrl1[] = "https://tests/DisplayTestHandler.START";
+const char kTestUrl2[] = "https://tests/DisplayTestHandler.NAVIGATE";
+const char kTestMsg[] = "DisplayTestHandler.Status";
+
+// Default OSR widget size.
+const int kOsrWidth = 600;
+const int kOsrHeight = 400;
+
+class DisplayTestHandler : public RoutingTestHandler, public CefRenderHandler {
+ public:
+  DisplayTestHandler() : status_(START) {}
+
+  CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; }
+
+  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override {
+    rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+  }
+
+  bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                     CefScreenInfo& screen_info) override {
+    screen_info.rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+    screen_info.available_rect = screen_info.rect;
+    return true;
+  }
+
+  void OnPaint(CefRefPtr<CefBrowser> browser,
+               CefRenderHandler::PaintElementType type,
+               const CefRenderHandler::RectList& dirtyRects,
+               const void* buffer,
+               int width,
+               int height) override {
+    if (!got_paint_[status_]) {
+      got_paint_[status_].yes();
+
+      if (status_ == START)
+        OnStartIfDone();
+      else if (status_ == SHOW)
+        CefPostTask(TID_UI, base::Bind(&DisplayTestHandler::DestroyTest, this));
+      else
+        EXPECT_FALSE(true);  // Not reached.
+    }
+  }
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    AddResource(kTestUrl1, GetPageContents("Page1", "START"), "text/html");
+    AddResource(kTestUrl2, GetPageContents("Page2", "NAVIGATE"), "text/html");
+
+    // Create the browser.
+    CreateOSRBrowser(kTestUrl1);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout(5000);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    const std::string& request_str = request.ToString();
+    if (request_str.find(kTestMsg) == 0) {
+      const std::string& status = request_str.substr(sizeof(kTestMsg));
+      if (status == "START") {
+        got_start_msg_.yes();
+        OnStartIfDone();
+      } else if (status == "NAVIGATE") {
+        got_navigate_msg_.yes();
+        // Wait a bit to verify no OnPaint callback.
+        CefPostDelayedTask(
+            TID_UI, base::Bind(&DisplayTestHandler::OnNavigate, this), 250);
+      }
+    }
+    callback->Success("");
+    return true;
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_paint_[START]);
+    EXPECT_FALSE(got_paint_[NAVIGATE]);
+    EXPECT_TRUE(got_paint_[SHOW]);
+
+    EXPECT_TRUE(got_start_msg_);
+    EXPECT_TRUE(got_navigate_msg_);
+
+    EXPECT_EQ(status_, SHOW);
+
+    RoutingTestHandler::DestroyTest();
+  }
+
+ private:
+  void CreateOSRBrowser(const CefString& url) {
+    CefWindowInfo windowInfo;
+    CefBrowserSettings settings;
+
+#if defined(OS_WIN)
+    windowInfo.SetAsWindowless(GetDesktopWindow());
+#else
+    windowInfo.SetAsWindowless(kNullWindowHandle);
+#endif
+
+    CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, nullptr,
+                                  nullptr);
+  }
+
+  std::string GetPageContents(const std::string& name,
+                              const std::string& status) {
+    return "<html><body>" + name + "<script>window.testQuery({request:'" +
+           std::string(kTestMsg) + ":" + status + "'});</script></body></html>";
+  }
+
+  void OnStartIfDone() {
+    if (got_start_msg_ && got_paint_[START])
+      CefPostTask(TID_UI, base::Bind(&DisplayTestHandler::OnStart, this));
+  }
+
+  void OnStart() {
+    EXPECT_EQ(status_, START);
+
+    // Hide the browser. OnPaint should not be called again until
+    // WasHidden(false) is explicitly called.
+    GetBrowser()->GetHost()->WasHidden(true);
+    status_ = NAVIGATE;
+
+    GetBrowser()->GetMainFrame()->LoadURL(kTestUrl2);
+  }
+
+  void OnNavigate() {
+    EXPECT_EQ(status_, NAVIGATE);
+
+    // Show the browser.
+    status_ = SHOW;
+    GetBrowser()->GetHost()->WasHidden(false);
+
+    // Force a call to OnPaint.
+    GetBrowser()->GetHost()->Invalidate(PET_VIEW);
+  }
+
+  enum Status {
+    START,
+    NAVIGATE,
+    SHOW,
+    STATUS_COUNT,
+  };
+  Status status_;
+
+  TrackCallback got_paint_[STATUS_COUNT];
+  TrackCallback got_start_msg_;
+  TrackCallback got_navigate_msg_;
+
+  IMPLEMENT_REFCOUNTING(DisplayTestHandler);
+};
+
+}  // namespace
+
+// Test that browser visibility is not changed due to navigation.
+TEST(OSRTest, NavigateWhileHidden) {
+  CefRefPtr<DisplayTestHandler> handler = new DisplayTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kOsrPopupJSOtherClientMainUrl[] =
+    "http://www.tests-pjse.com/main.html";
+
+class OsrPopupJSOtherClientTestHandler : public TestHandler,
+                                         public CefRenderHandler {
+ public:
+  OsrPopupJSOtherClientTestHandler(CefRefPtr<CefClient> other) {
+    other_ = other;
+  }
+
+  virtual CefRefPtr<CefRenderHandler> GetRenderHandler() override {
+    return this;
+  }
+
+  virtual void GetViewRect(CefRefPtr<CefBrowser> browser,
+                           CefRect& rect) override {
+    rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+  }
+
+  virtual void OnPaint(CefRefPtr<CefBrowser> browser,
+                       PaintElementType type,
+                       const RectList& dirtyRects,
+                       const void* buffer,
+                       int width,
+                       int height) override {}
+
+  void RunTest() override {
+    AddResource(kOsrPopupJSOtherClientMainUrl, "<html>Main</html>",
+                "text/html");
+
+    // Create the browser.
+    CreateOSRBrowser(kOsrPopupJSOtherClientMainUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     cef_window_open_disposition_t target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override {
+#if defined(OS_WIN)
+    windowInfo.SetAsWindowless(GetDesktopWindow());
+#else
+    windowInfo.SetAsWindowless(kNullWindowHandle);
+#endif
+
+    client = other_;
+
+    got_before_popup_.yes();
+    return false;
+  }
+
+  void Close(CefRefPtr<CefBrowser> browser) {
+    browser->StopLoad();
+    CloseBrowser(browser, true);
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+    if (browser->IsPopup()) {
+      got_after_created_popup_.yes();
+    }
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (isLoading)
+      return;
+
+    if (browser->IsPopup()) {
+      got_load_end_popup_.yes();
+      CefPostDelayedTask(
+          TID_UI,
+          base::Bind(&OsrPopupJSOtherClientTestHandler::Close, this, browser),
+          100);
+    } else {
+      browser->GetMainFrame()->LoadURL("javascript:window.open('about:blank')");
+    }
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnBeforeClose(browser);
+    other_ = nullptr;
+    if (browser->IsPopup()) {
+      got_before_close_popup_.yes();
+      DestroyTest();
+    }
+  }
+
+ private:
+  void CreateOSRBrowser(const CefString& url) {
+    CefWindowInfo windowInfo;
+    CefBrowserSettings settings;
+
+#if defined(OS_WIN)
+    windowInfo.SetAsWindowless(GetDesktopWindow());
+#else
+    windowInfo.SetAsWindowless(kNullWindowHandle);
+#endif
+
+    CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, nullptr,
+                                  nullptr);
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_after_created_popup_);
+    EXPECT_TRUE(got_load_end_popup_);
+    EXPECT_TRUE(got_before_close_popup_);
+    EXPECT_TRUE(got_before_popup_);
+    TestHandler::DestroyTest();
+  }
+
+  TrackCallback got_before_popup_;
+  TrackCallback got_after_created_popup_;
+  TrackCallback got_load_end_popup_;
+  TrackCallback got_before_close_popup_;
+  CefRefPtr<CefClient> other_;
+
+  IMPLEMENT_REFCOUNTING(OsrPopupJSOtherClientTestHandler);
+};
+
+class OsrPopupJSOtherCefClient : public CefClient,
+                                 public CefLoadHandler,
+                                 public CefLifeSpanHandler,
+                                 public CefRenderHandler {
+ public:
+  OsrPopupJSOtherCefClient() { handler_ = NULL; }
+
+  void SetHandler(CefRefPtr<OsrPopupJSOtherClientTestHandler> handler) {
+    handler_ = handler;
+  }
+
+  virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
+
+  virtual CefRefPtr<CefRenderHandler> GetRenderHandler() override {
+    return this;
+  }
+
+  virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override {
+    return this;
+  }
+
+  virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                                    bool isLoading,
+                                    bool canGoBack,
+                                    bool canGoForward) override {
+    if (handler_) {
+      handler_->OnLoadingStateChange(browser, isLoading, canGoBack,
+                                     canGoForward);
+    }
+  }
+
+  virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    handler_->OnAfterCreated(browser);
+  }
+
+  virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    handler_->OnBeforeClose(browser);
+    handler_ = nullptr;
+  }
+
+  virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefFrame> frame,
+                             const CefString& target_url,
+                             const CefString& target_frame_name,
+                             cef_window_open_disposition_t target_disposition,
+                             bool user_gesture,
+                             const CefPopupFeatures& popupFeatures,
+                             CefWindowInfo& windowInfo,
+                             CefRefPtr<CefClient>& client,
+                             CefBrowserSettings& settings,
+                             CefRefPtr<CefDictionaryValue>& extra_info,
+                             bool* no_javascript_access) override {
+    return true;
+  }
+
+  virtual void GetViewRect(CefRefPtr<CefBrowser> browser,
+                           CefRect& rect) override {
+    rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
+  }
+
+  virtual void OnPaint(CefRefPtr<CefBrowser> browser,
+                       PaintElementType type,
+                       const RectList& dirtyRects,
+                       const void* buffer,
+                       int width,
+                       int height) override {}
+
+ private:
+  CefRefPtr<OsrPopupJSOtherClientTestHandler> handler_;
+
+  IMPLEMENT_REFCOUNTING(OsrPopupJSOtherCefClient);
+};
+
+}  // namespace
+
+// Test creation of an OSR-popup with another client.
+TEST(OSRTest, OsrPopupJSOtherClient) {
+  CefRefPtr<OsrPopupJSOtherCefClient> client = new OsrPopupJSOtherCefClient();
+  CefRefPtr<OsrPopupJSOtherClientTestHandler> handler =
+      new OsrPopupJSOtherClientTestHandler(client);
+  client->SetHandler(handler);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/parser_unittest.cc b/src/tests/ceftests/parser_unittest.cc
new file mode 100644
index 0000000..cc5e6be
--- /dev/null
+++ b/src/tests/ceftests/parser_unittest.cc
@@ -0,0 +1,488 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_parser.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+// Create the URL using the spec.
+TEST(ParserTest, CreateURLSpec) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.spec)
+      .FromASCII(
+          "http://user:pass@www.example.com:88/path/"
+          "to.html?foo=test&bar=test2");
+  EXPECT_TRUE(CefCreateURL(parts, url));
+  EXPECT_STREQ(
+      "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2",
+      url.ToString().c_str());
+}
+
+// Test that host is required.
+TEST(ParserTest, CreateURLHostRequired) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.scheme).FromASCII("http");
+  EXPECT_FALSE(CefCreateURL(parts, url));
+}
+
+// Test that scheme is required.
+TEST(ParserTest, CreateURLSchemeRequired) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.host).FromASCII("www.example.com");
+  EXPECT_FALSE(CefCreateURL(parts, url));
+}
+
+// Create the URL using scheme and host.
+TEST(ParserTest, CreateURLSchemeHost) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.scheme).FromASCII("http");
+  CefString(&parts.host).FromASCII("www.example.com");
+  EXPECT_TRUE(CefCreateURL(parts, url));
+  EXPECT_STREQ("http://www.example.com/", url.ToString().c_str());
+}
+
+// Create the URL using scheme, host and path.
+TEST(ParserTest, CreateURLSchemeHostPath) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.scheme).FromASCII("http");
+  CefString(&parts.host).FromASCII("www.example.com");
+  CefString(&parts.path).FromASCII("/path/to.html");
+  EXPECT_TRUE(CefCreateURL(parts, url));
+  EXPECT_STREQ("http://www.example.com/path/to.html", url.ToString().c_str());
+}
+
+// Create the URL using scheme, host, path and query.
+TEST(ParserTest, CreateURLSchemeHostPathQuery) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.scheme).FromASCII("http");
+  CefString(&parts.host).FromASCII("www.example.com");
+  CefString(&parts.path).FromASCII("/path/to.html");
+  CefString(&parts.query).FromASCII("foo=test&bar=test2");
+  EXPECT_TRUE(CefCreateURL(parts, url));
+  EXPECT_STREQ("http://www.example.com/path/to.html?foo=test&bar=test2",
+               url.ToString().c_str());
+}
+
+// Create the URL using scheme, host, path, query and Fragment
+TEST(ParserTest, CreateURLSchemeHostPathQueryFragment) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.scheme).FromASCII("http");
+  CefString(&parts.host).FromASCII("www.example.com");
+  CefString(&parts.path).FromASCII("/path/to.html");
+  CefString(&parts.query).FromASCII("foo=test&bar=test2");
+  CefString(&parts.fragment).FromASCII("ref");
+  EXPECT_TRUE(CefCreateURL(parts, url));
+  EXPECT_STREQ("http://www.example.com/path/to.html?foo=test&bar=test2#ref",
+               url.ToString().c_str());
+}
+
+// Create the URL using all the various components.
+TEST(ParserTest, CreateURLAll) {
+  CefURLParts parts;
+  CefString url;
+  CefString(&parts.scheme).FromASCII("http");
+  CefString(&parts.username).FromASCII("user");
+  CefString(&parts.password).FromASCII("pass");
+  CefString(&parts.host).FromASCII("www.example.com");
+  CefString(&parts.port).FromASCII("88");
+  CefString(&parts.path).FromASCII("/path/to.html");
+  CefString(&parts.query).FromASCII("foo=test&bar=test2");
+  CefString(&parts.fragment).FromASCII("ref");
+  EXPECT_TRUE(CefCreateURL(parts, url));
+  EXPECT_STREQ(
+      "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2#ref",
+      url.ToString().c_str());
+}
+
+// Parse the URL using scheme and host.
+TEST(ParserTest, ParseURLSchemeHost) {
+  CefURLParts parts;
+  CefString url;
+  url.FromASCII("http://www.example.com");
+  EXPECT_TRUE(CefParseURL(url, parts));
+
+  CefString spec(&parts.spec);
+  EXPECT_STREQ("http://www.example.com/", spec.ToString().c_str());
+  EXPECT_EQ(0U, parts.username.length);
+  EXPECT_EQ(0U, parts.password.length);
+  CefString scheme(&parts.scheme);
+  EXPECT_STREQ("http", scheme.ToString().c_str());
+  CefString host(&parts.host);
+  EXPECT_STREQ("www.example.com", host.ToString().c_str());
+  EXPECT_EQ(0U, parts.port.length);
+  CefString origin(&parts.origin);
+  EXPECT_STREQ(origin.ToString().c_str(), "http://www.example.com/");
+  CefString path(&parts.path);
+  EXPECT_STREQ("/", path.ToString().c_str());
+  EXPECT_EQ(0U, parts.query.length);
+}
+
+// Parse the URL using scheme, host and path.
+TEST(ParserTest, ParseURLSchemeHostPath) {
+  CefURLParts parts;
+  CefString url;
+  url.FromASCII("http://www.example.com/path/to.html");
+  EXPECT_TRUE(CefParseURL(url, parts));
+
+  CefString spec(&parts.spec);
+  EXPECT_STREQ("http://www.example.com/path/to.html", spec.ToString().c_str());
+  EXPECT_EQ(0U, parts.username.length);
+  EXPECT_EQ(0U, parts.password.length);
+  CefString scheme(&parts.scheme);
+  EXPECT_STREQ("http", scheme.ToString().c_str());
+  CefString host(&parts.host);
+  EXPECT_STREQ("www.example.com", host.ToString().c_str());
+  EXPECT_EQ(0U, parts.port.length);
+  CefString origin(&parts.origin);
+  EXPECT_STREQ(origin.ToString().c_str(), "http://www.example.com/");
+  CefString path(&parts.path);
+  EXPECT_STREQ("/path/to.html", path.ToString().c_str());
+  EXPECT_EQ(0U, parts.query.length);
+}
+
+// Parse the URL using scheme, host, path and query.
+TEST(ParserTest, ParseURLSchemeHostPathQuery) {
+  CefURLParts parts;
+  CefString url;
+  url.FromASCII("http://www.example.com/path/to.html?foo=test&bar=test2");
+  EXPECT_TRUE(CefParseURL(url, parts));
+
+  CefString spec(&parts.spec);
+  EXPECT_STREQ("http://www.example.com/path/to.html?foo=test&bar=test2",
+               spec.ToString().c_str());
+  EXPECT_EQ(0U, parts.username.length);
+  EXPECT_EQ(0U, parts.password.length);
+  CefString scheme(&parts.scheme);
+  EXPECT_STREQ("http", scheme.ToString().c_str());
+  CefString host(&parts.host);
+  EXPECT_STREQ("www.example.com", host.ToString().c_str());
+  EXPECT_EQ(0U, parts.port.length);
+  CefString origin(&parts.origin);
+  EXPECT_STREQ(origin.ToString().c_str(), "http://www.example.com/");
+  CefString path(&parts.path);
+  EXPECT_STREQ("/path/to.html", path.ToString().c_str());
+  CefString query(&parts.query);
+  EXPECT_STREQ("foo=test&bar=test2", query.ToString().c_str());
+}
+
+// Parse the URL using scheme, host, path, query and fragment.
+TEST(ParserTest, ParseURLSchemeHostPathQueryFragment) {
+  CefURLParts parts;
+  CefString url;
+  url.FromASCII("http://www.example.com/path/to.html?foo=test&bar=test2#ref");
+  EXPECT_TRUE(CefParseURL(url, parts));
+
+  CefString spec(&parts.spec);
+  EXPECT_STREQ("http://www.example.com/path/to.html?foo=test&bar=test2#ref",
+               spec.ToString().c_str());
+  EXPECT_EQ(0U, parts.username.length);
+  EXPECT_EQ(0U, parts.password.length);
+  CefString scheme(&parts.scheme);
+  EXPECT_STREQ("http", scheme.ToString().c_str());
+  CefString host(&parts.host);
+  EXPECT_STREQ("www.example.com", host.ToString().c_str());
+  EXPECT_EQ(0U, parts.port.length);
+  CefString origin(&parts.origin);
+  EXPECT_STREQ(origin.ToString().c_str(), "http://www.example.com/");
+  CefString path(&parts.path);
+  EXPECT_STREQ("/path/to.html", path.ToString().c_str());
+  CefString query(&parts.query);
+  EXPECT_STREQ("foo=test&bar=test2", query.ToString().c_str());
+  CefString ref(&parts.fragment);
+  EXPECT_STREQ("ref", ref.ToString().c_str());
+}
+
+// Parse the URL using all the various components.
+TEST(ParserTest, ParseURLAll) {
+  CefURLParts parts;
+  CefString url;
+  url.FromASCII(
+      "http://user:pass@www.example.com:88/path/"
+      "to.html?foo=test&bar=test2#ref");
+  EXPECT_TRUE(CefParseURL(url, parts));
+
+  CefString spec(&parts.spec);
+  EXPECT_STREQ(
+      "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2#ref",
+      spec.ToString().c_str());
+  CefString scheme(&parts.scheme);
+  EXPECT_STREQ("http", scheme.ToString().c_str());
+  CefString username(&parts.username);
+  EXPECT_STREQ("user", username.ToString().c_str());
+  CefString password(&parts.password);
+  EXPECT_STREQ("pass", password.ToString().c_str());
+  CefString host(&parts.host);
+  EXPECT_STREQ("www.example.com", host.ToString().c_str());
+  CefString port(&parts.port);
+  EXPECT_STREQ("88", port.ToString().c_str());
+  CefString origin(&parts.origin);
+  EXPECT_STREQ(origin.ToString().c_str(), "http://www.example.com:88/");
+  CefString path(&parts.path);
+  EXPECT_STREQ("/path/to.html", path.ToString().c_str());
+  CefString query(&parts.query);
+  EXPECT_STREQ("foo=test&bar=test2", query.ToString().c_str());
+  CefString ref(&parts.fragment);
+  EXPECT_STREQ("ref", ref.ToString().c_str());
+}
+
+// Parse an invalid URL.
+TEST(ParserTest, ParseURLInvalid) {
+  CefURLParts parts;
+  CefString url;
+  url.FromASCII("www.example.com");
+  EXPECT_FALSE(CefParseURL(url, parts));
+}
+
+// Parse a non-standard scheme.
+TEST(ParserTest, ParseURLNonStandard) {
+  CefURLParts parts;
+  CefString url;
+  url.FromASCII("custom:something%20else?foo#ref");
+  EXPECT_TRUE(CefParseURL(url, parts));
+
+  CefString spec(&parts.spec);
+  EXPECT_STREQ("custom:something%20else?foo#ref", spec.ToString().c_str());
+  EXPECT_EQ(0U, parts.username.length);
+  EXPECT_EQ(0U, parts.password.length);
+  CefString scheme(&parts.scheme);
+  EXPECT_STREQ("custom", scheme.ToString().c_str());
+  EXPECT_EQ(0U, parts.host.length);
+  EXPECT_EQ(0U, parts.port.length);
+  EXPECT_EQ(0U, parts.origin.length);
+  CefString path(&parts.path);
+  EXPECT_STREQ("something%20else", path.ToString().c_str());
+  CefString query(&parts.query);
+  EXPECT_STREQ("foo", query.ToString().c_str());
+  CefString ref(&parts.fragment);
+  EXPECT_STREQ("ref", ref.ToString().c_str());
+}
+
+TEST(ParserTest, FormatUrlForSecurityDisplay) {
+  CefString result;
+
+  // Omits the protocol if it's standard.
+  result = CefFormatUrlForSecurityDisplay("http://tests.com/foo.html");
+  EXPECT_STREQ("http://tests.com", result.ToString().c_str());
+
+  // Omits the port if it's the expected value for the protocol.
+  result = CefFormatUrlForSecurityDisplay("http://tests.com:80/foo.html");
+  EXPECT_STREQ("http://tests.com", result.ToString().c_str());
+
+  // Don't omit non-standard ports.
+  result = CefFormatUrlForSecurityDisplay("http://tests.com:8088/foo.html");
+  EXPECT_STREQ("http://tests.com:8088", result.ToString().c_str());
+
+  // Don't omit the protocol for file URLs.
+  result = CefFormatUrlForSecurityDisplay("file:///c/tests/foo.html");
+  EXPECT_STREQ("file:///c/tests/foo.html", result.ToString().c_str());
+}
+
+TEST(ParserTest, GetMimeType) {
+  CefString mime_type;
+
+  mime_type = CefGetMimeType("html");
+  EXPECT_STREQ("text/html", mime_type.ToString().c_str());
+
+  mime_type = CefGetMimeType("txt");
+  EXPECT_STREQ("text/plain", mime_type.ToString().c_str());
+
+  mime_type = CefGetMimeType("gif");
+  EXPECT_STREQ("image/gif", mime_type.ToString().c_str());
+}
+
+TEST(ParserTest, Base64Encode) {
+  const std::string& test_str_decoded = "A test string";
+  const std::string& test_str_encoded = "QSB0ZXN0IHN0cmluZw==";
+  const CefString& encoded_value =
+      CefBase64Encode(test_str_decoded.data(), test_str_decoded.size());
+  EXPECT_STREQ(test_str_encoded.c_str(), encoded_value.ToString().c_str());
+}
+
+TEST(ParserTest, Base64Decode) {
+  const std::string& test_str_decoded = "A test string";
+  const std::string& test_str_encoded = "QSB0ZXN0IHN0cmluZw==";
+  CefRefPtr<CefBinaryValue> decoded_value = CefBase64Decode(test_str_encoded);
+  EXPECT_TRUE(decoded_value.get());
+
+  const size_t decoded_size = decoded_value->GetSize();
+  EXPECT_EQ(test_str_decoded.size(), decoded_size);
+
+  std::string decoded_str;
+  decoded_str.resize(decoded_size + 1);  // Include space for NUL-terminator.
+  const size_t get_data_result = decoded_value->GetData(
+      const_cast<char*>(decoded_str.data()), decoded_size, 0);
+  EXPECT_EQ(decoded_size, get_data_result);
+  EXPECT_STREQ(test_str_decoded.c_str(), decoded_str.c_str());
+}
+
+TEST(ParserTest, URIEncode) {
+  const std::string& test_str_decoded = "A test string=";
+  const std::string& test_str_encoded = "A%20test%20string%3D";
+  const CefString& encoded_value = CefURIEncode(test_str_decoded, false);
+  EXPECT_STREQ(test_str_encoded.c_str(), encoded_value.ToString().c_str());
+}
+
+TEST(ParserTest, URIEncodeWithPlusSpace) {
+  const std::string& test_str_decoded = "A test string=";
+  const std::string& test_str_encoded = "A+test+string%3D";
+  const CefString& encoded_value = CefURIEncode(test_str_decoded, true);
+  EXPECT_STREQ(test_str_encoded.c_str(), encoded_value.ToString().c_str());
+}
+
+TEST(ParserTest, URIDecode) {
+  const std::string& test_str_decoded = "A test string=";
+  const std::string& test_str_encoded = "A%20test%20string%3D";
+  const CefString& decoded_value = CefURIDecode(
+      test_str_encoded, false,
+      static_cast<cef_uri_unescape_rule_t>(
+          UU_SPACES | UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS));
+  EXPECT_STREQ(test_str_decoded.c_str(), decoded_value.ToString().c_str());
+}
+
+TEST(ParserTest, URIDecodeWithPlusSpace) {
+  const std::string& test_str_decoded = "A test string=";
+  const std::string& test_str_encoded = "A+test+string%3D";
+  const CefString& decoded_value =
+      CefURIDecode(test_str_encoded, false,
+                   static_cast<cef_uri_unescape_rule_t>(
+                       UU_SPACES | UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
+                       UU_REPLACE_PLUS_WITH_SPACE));
+  EXPECT_STREQ(test_str_decoded.c_str(), decoded_value.ToString().c_str());
+}
+
+TEST(ParserTest, ParseJSONInvalid) {
+  const char data[] = "This is my test data";
+  CefRefPtr<CefValue> value = CefParseJSON(data, JSON_PARSER_RFC);
+  EXPECT_FALSE(value.get());
+}
+
+TEST(ParserTest, ParseJSONNull) {
+  const char data[] = "{\"key1\":null}";
+  CefRefPtr<CefValue> value = CefParseJSON(data, JSON_PARSER_RFC);
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_TRUE(value->GetType() == VTYPE_DICTIONARY);
+  EXPECT_FALSE(value->IsOwned());
+  CefRefPtr<CefDictionaryValue> dict = value->GetDictionary();
+  CefDictionaryValue::KeyList key_list;
+  EXPECT_TRUE(dict->GetKeys(key_list));
+  EXPECT_EQ(1U, key_list.size());
+  EXPECT_EQ("key1", key_list[0].ToString());
+  EXPECT_EQ(VTYPE_NULL, dict->GetType("key1"));
+
+  // generate string from parsed result
+  CefString result = CefWriteJSON(value, JSON_WRITER_DEFAULT);
+  CefString expected_result = data;
+  EXPECT_EQ(expected_result, result);
+}
+
+TEST(ParserTest, WriteJSONBinary) {
+  const char data[] = "\00\01\02";
+  CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
+  CefRefPtr<CefBinaryValue> binary = CefBinaryValue::Create(data, sizeof(data));
+  dict->SetBinary("key1", binary);
+  CefRefPtr<CefValue> node = CefValue::Create();
+  node->SetDictionary(dict);
+  CefString result = CefWriteJSON(node, JSON_WRITER_DEFAULT);
+  CefString expect_result = "";
+  // binary data will be omitted.
+  EXPECT_EQ(expect_result, result);
+}
+
+TEST(ParserTest, ParseJSONDictionary) {
+  const char data[] = "{\"key1\":\"value1\",\"key2\":123,\"key3\":[1,2,3]}";
+  CefRefPtr<CefValue> value = CefParseJSON(data, JSON_PARSER_RFC);
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_TRUE(value->GetType() == VTYPE_DICTIONARY);
+  CefRefPtr<CefDictionaryValue> dict = value->GetDictionary();
+  CefDictionaryValue::KeyList key_list;
+  EXPECT_TRUE(dict->GetKeys(key_list));
+  EXPECT_EQ(3U, key_list.size());
+  EXPECT_EQ("key1", key_list[0].ToString());
+  EXPECT_EQ("key2", key_list[1].ToString());
+  EXPECT_EQ("key3", key_list[2].ToString());
+  EXPECT_EQ(VTYPE_STRING, dict->GetType("key1"));
+  EXPECT_EQ(dict->GetString("key1"), "value1");
+  EXPECT_EQ(VTYPE_INT, dict->GetType("key2"));
+  EXPECT_EQ(123, dict->GetInt("key2"));
+  EXPECT_EQ(VTYPE_LIST, dict->GetType("key3"));
+  CefRefPtr<CefListValue> key3 = dict->GetList("key3");
+  EXPECT_TRUE(nullptr != key3);
+  EXPECT_TRUE(key3->IsValid());
+  EXPECT_EQ(3U, key3->GetSize());
+  EXPECT_EQ(1, key3->GetInt(0));
+  EXPECT_EQ(2, key3->GetInt(1));
+  EXPECT_EQ(3, key3->GetInt(2));
+
+  // generate string from parsed result
+  CefString result = CefWriteJSON(value, JSON_WRITER_DEFAULT);
+  CefString expected_result = data;
+  EXPECT_EQ(expected_result, result);
+}
+
+TEST(ParserTest, ParseJSONList) {
+  const char data[] = "[\"value1\", 123, {\"key3\": [1, 2, 3]}]";
+  CefRefPtr<CefValue> value = CefParseJSON(data, JSON_PARSER_RFC);
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_TRUE(value->GetType() == VTYPE_LIST);
+  EXPECT_FALSE(value->IsOwned());
+  CefRefPtr<CefListValue> list = value->GetList();
+  EXPECT_TRUE(nullptr != list);
+  EXPECT_TRUE(list->IsValid());
+  EXPECT_EQ(3U, list->GetSize());
+
+  EXPECT_EQ(VTYPE_STRING, list->GetType(0));
+  EXPECT_EQ(list->GetString(0), "value1");
+  EXPECT_EQ(VTYPE_INT, list->GetType(1));
+  EXPECT_EQ(123, list->GetInt(1));
+  EXPECT_EQ(VTYPE_DICTIONARY, list->GetType(2));
+  CefRefPtr<CefDictionaryValue> dict = list->GetDictionary(2);
+  CefDictionaryValue::KeyList key_list2;
+  EXPECT_TRUE(dict->GetKeys(key_list2));
+  EXPECT_EQ(1U, key_list2.size());
+  CefRefPtr<CefListValue> list2 = dict->GetList("key3");
+  EXPECT_EQ(3U, list2->GetSize());
+  EXPECT_EQ(1, list2->GetInt(0));
+  EXPECT_EQ(2, list2->GetInt(1));
+  EXPECT_EQ(3, list2->GetInt(2));
+
+  // generate string from parsed result
+  CefString result = CefWriteJSON(value, JSON_WRITER_DEFAULT);
+  CefString expected_result = "[\"value1\",123,{\"key3\":[1,2,3]}]";
+  EXPECT_EQ(expected_result.ToString(), result.ToString());
+}
+
+TEST(ParserTest, ParseJSONAndReturnErrorInvalid) {
+  const char data[] = "This is my test data";
+  cef_json_parser_error_t error_code;
+  CefString error_msg;
+  CefRefPtr<CefValue> value =
+      CefParseJSONAndReturnError(data, JSON_PARSER_RFC, error_code, error_msg);
+  CefString expect_error_msg = "Line: 1, column: 1, Unexpected token.";
+  EXPECT_FALSE(value.get());
+  EXPECT_EQ(JSON_UNEXPECTED_TOKEN, error_code);
+  EXPECT_EQ(expect_error_msg, error_msg);
+}
+
+TEST(ParserTest, ParseJSONAndReturnErrorTrailingComma) {
+  const char data[] = "{\"key1\":123,}";
+  cef_json_parser_error_t error_code;
+  CefString error_msg;
+  CefRefPtr<CefValue> value =
+      CefParseJSONAndReturnError(data, JSON_PARSER_RFC, error_code, error_msg);
+  CefString expect_error_msg =
+      "Line: 1, column: 13, Trailing comma not allowed.";
+  EXPECT_FALSE(value.get());
+  EXPECT_EQ(JSON_TRAILING_COMMA, error_code);
+  EXPECT_EQ(expect_error_msg, error_msg);
+}
diff --git a/src/tests/ceftests/plugin_unittest.cc b/src/tests/ceftests/plugin_unittest.cc
new file mode 100644
index 0000000..a030c52
--- /dev/null
+++ b/src/tests/ceftests/plugin_unittest.cc
@@ -0,0 +1,626 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_pack_resources.h"
+#include "include/cef_request_context_handler.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/browser/resource_util.h"
+
+namespace {
+
+// Browser-side app delegate.
+class PluginBrowserTest : public client::ClientAppBrowser::Delegate {
+ public:
+  PluginBrowserTest() {}
+
+  void OnBeforeCommandLineProcessing(
+      CefRefPtr<client::ClientAppBrowser> app,
+      CefRefPtr<CefCommandLine> command_line) override {
+    // Allow all plugin loading by default.
+    command_line->AppendSwitchWithValue("plugin-policy", "allow");
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(PluginBrowserTest);
+};
+
+const char kPdfTestOrigin[] = "http://tests/";
+const char kPdfHtmlUrl[] = "http://tests/pdf.html";
+const char kPdfDirectUrl[] = "http://tests/pdf.pdf";
+
+// Delay waiting for iframe tests to load the PDF file.
+#if defined(OS_LINUX)
+const int64 kPdfLoadDelayMs = 7000;
+#else
+const int64 kPdfLoadDelayMs = 5000;
+#endif
+
+// Browser-side test handler.
+class PluginTestHandler : public RoutingTestHandler,
+                          public CefContextMenuHandler {
+ public:
+  enum Mode {
+    // No specified context or handler (implicitly uses the global context).
+    GLOBAL_DEFAULT,
+
+    // Global context with no handler.
+    GLOBAL_NO_HANDLER,
+
+    // Global context with handler that allows plugin load.
+    GLOBAL_ALLOW,
+
+    // Global context with handler that blocks plugin load. Then, load the
+    // plugin via the context menu.
+    GLOBAL_BLOCK_LOAD,
+
+    // Global context with handler that blocks plugin load. Then, hide the
+    // plugin via the context menu.
+    GLOBAL_BLOCK_HIDE,
+
+    // Global context with handler that disables plugin load. Then, hide the
+    // plugin via the context menu.
+    GLOBAL_DISABLE_HIDE,
+
+    // Global context with handler that removes the plugin from the
+    // `navigator.plugins` list and consequently disables plugin load.
+    GLOBAL_NO_LIST,
+
+    // Custom context with no handler.
+    CUSTOM_NO_HANDLER,
+
+    // Custom context with handler that allows plugin load.
+    CUSTOM_ALLOW,
+
+    // Custom context with handler that blocks plugin load. Then, load the
+    // plugin via the context menu.
+    CUSTOM_BLOCK_LOAD,
+
+    // Custom context with handler that blocks plugin load. Then, hide the
+    // plugin via the context menu.
+    CUSTOM_BLOCK_HIDE,
+
+    // Custom context with handler that disables plugin load. Then, hide the
+    // plugin via the context menu.
+    CUSTOM_DISABLE_HIDE,
+
+    // Custom context with handler that removes the plugin from the
+    // `navigator.plugins` list and consequently disables plugin load.
+    CUSTOM_NO_LIST,
+  };
+
+  class RequestContextHandler : public CefRequestContextHandler {
+   public:
+    explicit RequestContextHandler(PluginTestHandler* handler)
+        : handler_(handler) {}
+
+    bool OnBeforePluginLoad(const CefString& mime_type,
+                            const CefString& plugin_url,
+                            bool is_main_frame,
+                            const CefString& top_origin_url,
+                            CefRefPtr<CefWebPluginInfo> plugin_info,
+                            PluginPolicy* plugin_policy) override {
+      const std::string& mime_type_str = mime_type;
+      EXPECT_STREQ("application/pdf", mime_type_str.c_str());
+      EXPECT_STREQ("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/",
+                   plugin_info->GetPath().ToString().c_str());
+
+      if (top_origin_url.empty()) {
+        if (!handler_->got_on_before_plugin_empty_origin_) {
+          // Checking for PDF support in the plugin frame (navigator.plugins
+          // listing, pdf load, etc).
+          handler_->got_on_before_plugin_empty_origin_.yes();
+        } else if (handler_->HasNoList()) {
+          // When listing is disabled there should be an additional check in the
+          // main frame for the navigator.plugins listing.
+          if (!handler_->got_on_before_plugin_empty_origin2_) {
+            EXPECT_TRUE(is_main_frame);
+            handler_->got_on_before_plugin_empty_origin2_.yes();
+          }
+        }
+
+        if (handler_->HasNoList()) {
+          // Remove the PDF plugin from the `navigator.plugins` list.
+          *plugin_policy = PLUGIN_POLICY_DISABLE;
+          return true;
+        }
+
+        // Ignore requests for building the plugin list.
+        return false;
+      }
+
+      // Should only get requests for the test origin.
+      EXPECT_STREQ(kPdfTestOrigin, top_origin_url.ToString().c_str());
+
+      if (!handler_->got_on_before_plugin_load_pdf_)
+        handler_->got_on_before_plugin_load_pdf_.yes();
+      else
+        NOTREACHED();
+
+      if (handler_->HasAllow()) {
+        *plugin_policy = PLUGIN_POLICY_ALLOW;
+        return true;
+      } else if (handler_->HasBlock()) {
+        *plugin_policy = PLUGIN_POLICY_BLOCK;
+        return true;
+      } else if (handler_->HasDisable()) {
+        *plugin_policy = PLUGIN_POLICY_DISABLE;
+        return true;
+      }
+
+      return false;
+    }
+
+    void Detach() { handler_ = nullptr; }
+
+   private:
+    PluginTestHandler* handler_;
+
+    IMPLEMENT_REFCOUNTING(RequestContextHandler);
+  };
+
+  PluginTestHandler(Mode mode, const std::string& url)
+      : mode_(mode), url_(url) {}
+
+  // Loading the PDF directly in the main frame instead of a sub-frame.
+  bool HasDirectPluginLoad() const { return url_ == kPdfDirectUrl; }
+
+  // Has a specified RequestContext but not necessarily a custom handler.
+  bool HasRequestContext() const { return mode_ != GLOBAL_DEFAULT; }
+
+  // Has a specified RequestContext and custom handler.
+  bool HasRequestContextHandler() const {
+    return mode_ != GLOBAL_DEFAULT && mode_ != GLOBAL_NO_HANDLER &&
+           mode_ != CUSTOM_NO_HANDLER;
+  }
+
+  // Using the global request context, either directly or with a custom handler.
+  bool HasGlobalRequestContext() const {
+    return mode_ >= GLOBAL_DEFAULT && mode_ <= GLOBAL_NO_LIST;
+  }
+
+  // Should allow the plugin load via the custom handler.
+  bool HasAllow() const {
+    return mode_ == GLOBAL_ALLOW || mode_ == CUSTOM_ALLOW;
+  }
+
+  // Should block the plugin load via the custom handler.
+  bool HasBlock() const {
+    return mode_ == GLOBAL_BLOCK_LOAD || mode_ == GLOBAL_BLOCK_HIDE ||
+           mode_ == CUSTOM_BLOCK_LOAD || mode_ == CUSTOM_BLOCK_HIDE;
+  }
+
+  // Should disable the plugin load via the custom handler.
+  bool HasDisable() const {
+    return mode_ == GLOBAL_DISABLE_HIDE || mode_ == CUSTOM_DISABLE_HIDE;
+  }
+
+  // Should exclude the plugin from the `navigator.plugins` list.
+  bool HasNoList() const {
+    return mode_ == GLOBAL_NO_LIST || mode_ == CUSTOM_NO_LIST;
+  }
+
+  // Should load the plugin via the context menu.
+  bool HasContextLoad() const {
+    return mode_ == GLOBAL_BLOCK_LOAD || mode_ == CUSTOM_BLOCK_LOAD;
+  }
+
+  // Should hide the plugin via the context menu.
+  bool HasContextHide() const {
+    return mode_ == GLOBAL_BLOCK_HIDE || mode_ == GLOBAL_DISABLE_HIDE ||
+           mode_ == CUSTOM_BLOCK_HIDE || mode_ == CUSTOM_DISABLE_HIDE;
+  }
+
+  std::string GetPluginNode() const {
+    return "document.getElementsByTagName('embed')[0]";
+  }
+
+  void WaitForNavigatorPlugins(CefRefPtr<CefFrame> frame) const {
+    // Test if the `navigator.plugins` list includes the PDF extension.
+    const std::string& code =
+        " if (navigator.plugins['Chrome PDF Viewer'].filename == "
+        "       'mhjfbmdgcfjbbpaeojofohoefgiehjai') {"
+        "  window.testQuery({request:'pdf_plugin_found'});"
+        "} else {"
+        "  window.testQuery({request:'pdf_plugin_missing'});"
+        "}";
+    frame->ExecuteJavaScript(code, frame->GetURL(), 0);
+  }
+
+  void WaitForPlaceholderLoad(CefRefPtr<CefFrame> frame) {
+    // Keep track of the frame that contains the placeholder.
+    placeholder_frame_ = frame;
+
+    // Waits for the placeholder to load. This is indicated by the presence
+    // of the plugin node.
+    const std::string& code =
+        "(function waitForNode(i) {"
+        "   setTimeout(function() {"
+        "     var plugin = " +
+        GetPluginNode() +
+        ";"
+        "     if (plugin) {"
+        "        window.testQuery({request:'placeholder_loaded'});"
+        "        return;"
+        "     }"
+        "     if (--i) {"
+        "       waitForNode(i);"
+        "     } else {"
+        "       console.log('Timeout waiting for plugin load');"
+        "     }"
+        "   }, 250)"
+        "})(4);";
+    frame->ExecuteJavaScript(code, frame->GetURL(), 0);
+  }
+
+  void WaitForPlaceholderHide() {
+    // Waits for the placeholder to be hidden (style set to display:none).
+    // See PluginPlaceholderBase::HidePlugin.
+    const std::string& code =
+        "var plugin = " + GetPluginNode() +
+        ";"
+        "var observer = new MutationObserver(function(mutations) {"
+        "  mutations.forEach(function(mutationRecord) {"
+        "    window.testQuery({request:'placeholder_hidden'});"
+        "    observer.disconnect();"
+        "  });"
+        "});"
+        "observer.observe(plugin, "
+        "  { attributes : true, attributeFilter : ['style'] });";
+    placeholder_frame_->ExecuteJavaScript(code, placeholder_frame_->GetURL(),
+                                          0);
+    placeholder_frame_ = nullptr;
+  }
+
+  void WaitForPluginLoad(CefRefPtr<CefFrame> frame) {
+    if (got_context_menu_dismissed_) {
+      // After context menu display. Destroy the test.
+      CefPostDelayedTask(TID_UI,
+                         base::Bind(&PluginTestHandler::DestroyTest, this),
+                         kPdfLoadDelayMs);
+    } else {
+      // Trigger the context menu.
+      CefPostDelayedTask(TID_UI,
+                         base::Bind(&PluginTestHandler::TriggerContextMenu,
+                                    this, frame->GetBrowser()),
+                         kPdfLoadDelayMs);
+    }
+  }
+
+  void EndTest() {
+    CefPostTask(TID_UI, base::Bind(&PluginTestHandler::DestroyTest, this));
+  }
+
+  CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override {
+    return this;
+  }
+
+  void RunTest() override {
+    CefRefPtr<CefRequestContext> request_context;
+
+    if (HasRequestContext()) {
+      if (HasRequestContextHandler())
+        context_handler_ = new RequestContextHandler(this);
+
+      if (HasGlobalRequestContext()) {
+        // Use the global request context.
+        request_context = CefRequestContext::CreateContext(
+            CefRequestContext::GetGlobalContext(), context_handler_.get());
+      } else {
+        // Create the request context that will use an in-memory cache.
+        CefRequestContextSettings settings;
+        request_context =
+            CefRequestContext::CreateContext(settings, context_handler_.get());
+      }
+    }
+
+    // Create the browser.
+    CreateBrowser(url_, request_context);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout(5000 + kPdfLoadDelayMs);
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    const std::string& url = request->GetURL();
+    if (url == kPdfHtmlUrl) {
+      CefRefPtr<CefStreamReader> stream =
+          client::GetBinaryResourceReader("pdf.html");
+      return new CefStreamResourceHandler("text/html", stream);
+    } else if (url == kPdfDirectUrl) {
+      CefRefPtr<CefStreamReader> stream =
+          client::GetBinaryResourceReader("pdf.pdf");
+      return new CefStreamResourceHandler("application/pdf", stream);
+    }
+
+    return nullptr;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    bool is_pdf1 = false;
+    const std::string& url = frame->GetURL();
+    if (url == kPdfHtmlUrl) {
+      if (!got_on_load_end_html_)
+        got_on_load_end_html_.yes();
+      else
+        NOTREACHED();
+    } else if (url == kPdfDirectUrl) {
+      if (!got_on_load_end_pdf1_) {
+        got_on_load_end_pdf1_.yes();
+        is_pdf1 = true;
+      } else if (!got_on_load_end_pdf2_) {
+        got_on_load_end_pdf2_.yes();
+      } else {
+        NOTREACHED();
+      }
+    } else {
+      NOTREACHED();
+    }
+
+    if (HasNoList()) {
+      // If the plugin is not listed then the PDF documents will never load.
+      EXPECT_STREQ(kPdfHtmlUrl, url.c_str());
+      WaitForNavigatorPlugins(frame);
+    } else if (is_pdf1) {
+      // The first PDF document has loaded.
+      WaitForNavigatorPlugins(frame);
+    }
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    if (request == "pdf_plugin_found" || request == "pdf_plugin_missing") {
+      if (request == "pdf_plugin_found")
+        got_pdf_plugin_found_.yes();
+      else
+        got_pdf_plugin_missing_.yes();
+
+      if (HasNoList()) {
+        // The plugin will not load. End the test.
+        EndTest();
+      } else if (HasBlock() || HasDisable()) {
+        // Wait for the plugin placeholder for the first PDF file to load. The
+        // test will continue from OnQuery.
+        WaitForPlaceholderLoad(frame);
+      } else {
+        // Wait for the first PDF file to load.
+        WaitForPluginLoad(frame);
+      }
+    } else if (request == "placeholder_loaded") {
+      EXPECT_FALSE(got_placeholder_loaded_);
+      EXPECT_FALSE(got_placeholder_hidden_);
+      got_placeholder_loaded_.yes();
+
+      // The plugin placeholder has loaded. Trigger the context menu.
+      TriggerContextMenu(frame->GetBrowser());
+    } else if (request == "placeholder_hidden") {
+      EXPECT_TRUE(got_placeholder_loaded_);
+      EXPECT_FALSE(got_placeholder_hidden_);
+      got_placeholder_hidden_.yes();
+
+      if (HasContextLoad()) {
+        // Wait for the PDF plugin to load.
+        WaitForPluginLoad(frame);
+      } else {
+        // The plugin placeholder has been hidden. End the test.
+        EndTest();
+      }
+    } else {
+      NOTREACHED();
+    }
+
+    return true;
+  }
+
+  void TriggerContextMenu(CefRefPtr<CefBrowser> browser) {
+    CefMouseEvent mouse_event;
+
+    // Somewhere in the first plugin.
+    mouse_event.x = 100;
+    mouse_event.y = 100;
+
+    // Send right-click mouse down and mouse up to tigger context menu.
+    browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_RIGHT, false, 1);
+    browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_RIGHT, true, 1);
+  }
+
+  bool RunContextMenu(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefContextMenuParams> params,
+                      CefRefPtr<CefMenuModel> model,
+                      CefRefPtr<CefRunContextMenuCallback> callback) override {
+    EXPECT_FALSE(got_run_context_menu_);
+    got_run_context_menu_.yes();
+
+    if (HasContextLoad() || HasContextHide()) {
+      // Should have 4 elements -- plugin name, separator, run, hide.
+      EXPECT_EQ(4, model->GetCount());
+
+      // Wait for the placeholder to be hidden.
+      WaitForPlaceholderHide();
+
+      int command_id;
+      if (HasContextLoad()) {
+        // Execute the run command.
+        command_id = model->GetCommandIdAt(2);
+      } else {
+        // Execute the hide command.
+        command_id = model->GetCommandIdAt(3);
+      }
+
+      EXPECT_GE(command_id, MENU_ID_CUSTOM_FIRST);
+      EXPECT_LE(command_id, MENU_ID_CUSTOM_LAST);
+
+      callback->Continue(command_id, static_cast<EventFlags>(0));
+    } else {
+      // Do nothing with the context menu.
+      callback->Cancel();
+    }
+    return true;
+  }
+
+  void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame) override {
+    EXPECT_FALSE(got_context_menu_dismissed_);
+    got_context_menu_dismissed_.yes();
+
+    if (HasContextHide()) {
+      // Nothing to do here. The test will continue from OnQuery.
+      // TODO: Once block-then-load is working this should also be the case for
+      // HasContextLoad().
+      return;
+    }
+
+    EndTest();
+  }
+
+  void DestroyTest() override {
+    if (context_handler_.get()) {
+      context_handler_->Detach();
+      context_handler_ = nullptr;
+    }
+
+    if (HasContextLoad()) {
+      EXPECT_TRUE(got_placeholder_loaded_);
+      // TODO: Once block-then-load is working this value should be true.
+      EXPECT_FALSE(got_placeholder_hidden_);
+    } else if (HasContextHide()) {
+      EXPECT_TRUE(got_placeholder_loaded_);
+      EXPECT_TRUE(got_placeholder_hidden_);
+    } else {
+      EXPECT_FALSE(got_placeholder_loaded_);
+      EXPECT_FALSE(got_placeholder_hidden_);
+    }
+    EXPECT_FALSE(placeholder_frame_);
+
+    if (HasRequestContextHandler())
+      EXPECT_TRUE(got_on_before_plugin_empty_origin_);
+    else
+      EXPECT_FALSE(got_on_before_plugin_empty_origin_);
+
+    if (HasNoList()) {
+      EXPECT_TRUE(got_on_before_plugin_empty_origin2_);
+      EXPECT_FALSE(got_pdf_plugin_found_);
+      EXPECT_TRUE(got_pdf_plugin_missing_);
+      EXPECT_FALSE(got_run_context_menu_);
+      EXPECT_FALSE(got_context_menu_dismissed_);
+    } else {
+      EXPECT_FALSE(got_on_before_plugin_empty_origin2_);
+      EXPECT_TRUE(got_pdf_plugin_found_);
+      EXPECT_FALSE(got_pdf_plugin_missing_);
+      EXPECT_TRUE(got_run_context_menu_);
+      EXPECT_TRUE(got_context_menu_dismissed_);
+    }
+
+    if (url_ == kPdfHtmlUrl) {
+      // The HTML file will load the PDF twice in iframes.
+      EXPECT_TRUE(got_on_load_end_html_);
+
+      if (!HasNoList()) {
+        EXPECT_TRUE(got_on_load_end_pdf1_);
+        EXPECT_TRUE(got_on_load_end_pdf2_);
+
+        if (HasRequestContextHandler()) {
+          EXPECT_TRUE(got_on_before_plugin_load_pdf_);
+        }
+      }
+    } else if (url_ == kPdfDirectUrl) {
+      // Load the PDF file directly.
+      EXPECT_FALSE(got_on_load_end_html_);
+      EXPECT_TRUE(got_on_load_end_pdf1_);
+      EXPECT_FALSE(got_on_load_end_pdf2_);
+
+      if (HasRequestContextHandler()) {
+        EXPECT_TRUE(got_on_before_plugin_load_pdf_);
+      }
+    } else {
+      NOTREACHED();
+    }
+
+    if (!HasRequestContextHandler() || HasNoList()) {
+      EXPECT_FALSE(got_on_before_plugin_load_pdf_);
+    }
+
+    TestHandler::DestroyTest();
+  }
+
+  const Mode mode_;
+  const std::string url_;
+
+  CefRefPtr<CefFrame> placeholder_frame_;
+
+  TrackCallback got_on_before_plugin_empty_origin_;
+  TrackCallback got_on_before_plugin_empty_origin2_;
+  TrackCallback got_on_before_plugin_load_pdf_;
+  TrackCallback got_on_load_end_html_;
+  TrackCallback got_on_load_end_pdf1_;
+  TrackCallback got_on_load_end_pdf2_;
+  TrackCallback got_pdf_plugin_found_;
+  TrackCallback got_pdf_plugin_missing_;
+  TrackCallback got_placeholder_loaded_;
+  TrackCallback got_placeholder_hidden_;
+  TrackCallback got_run_context_menu_;
+  TrackCallback got_context_menu_dismissed_;
+
+  CefRefPtr<RequestContextHandler> context_handler_;
+
+  IMPLEMENT_REFCOUNTING(PluginTestHandler);
+};
+
+}  // namespace
+
+#define RUN_TEST(name, type, url)                            \
+  TEST(PluginTest, name) {                                   \
+    CefRefPtr<PluginTestHandler> handler =                   \
+        new PluginTestHandler(PluginTestHandler::type, url); \
+    handler->ExecuteTest();                                  \
+    ReleaseAndWaitForDestructor(handler);                    \
+  }
+
+RUN_TEST(GlobalDefaultPdfDirect, GLOBAL_DEFAULT, kPdfDirectUrl)
+RUN_TEST(GlobalDefaultPdfHtml, GLOBAL_DEFAULT, kPdfHtmlUrl)
+
+RUN_TEST(GlobalNoHandlerPdfDirect, GLOBAL_NO_HANDLER, kPdfDirectUrl)
+RUN_TEST(GlobalNoHandlerPdfHtml, GLOBAL_NO_HANDLER, kPdfHtmlUrl)
+RUN_TEST(GlobalAllowPdfDirect, GLOBAL_ALLOW, kPdfDirectUrl)
+RUN_TEST(GlobalAllowPdfHtml, GLOBAL_ALLOW, kPdfHtmlUrl)
+RUN_TEST(GlobalBlockThenLoadPdfDirect, GLOBAL_BLOCK_LOAD, kPdfDirectUrl)
+RUN_TEST(GlobalBlockThenLoadPdfHtml, GLOBAL_BLOCK_LOAD, kPdfHtmlUrl)
+RUN_TEST(GlobalBlockThenHidePdfDirect, GLOBAL_BLOCK_HIDE, kPdfDirectUrl)
+RUN_TEST(GlobalBlockThenHidePdfHtml, GLOBAL_BLOCK_HIDE, kPdfHtmlUrl)
+RUN_TEST(GlobalDisableThenHidePdfDirect, GLOBAL_DISABLE_HIDE, kPdfDirectUrl)
+RUN_TEST(GlobalDisableThenHidePdfHtml, GLOBAL_DISABLE_HIDE, kPdfHtmlUrl)
+RUN_TEST(GlobalNoListHtml, GLOBAL_NO_LIST, kPdfHtmlUrl)
+
+RUN_TEST(CustomNoHandlerPdfDirect, CUSTOM_NO_HANDLER, kPdfDirectUrl)
+RUN_TEST(CustomNoHandlerPdfHtml, CUSTOM_NO_HANDLER, kPdfHtmlUrl)
+RUN_TEST(CustomAllowPdfDirect, CUSTOM_ALLOW, kPdfDirectUrl)
+RUN_TEST(CustomAllowPdfHtml, CUSTOM_ALLOW, kPdfHtmlUrl)
+RUN_TEST(CustomBlockThenLoadPdfDirect, CUSTOM_BLOCK_LOAD, kPdfDirectUrl)
+RUN_TEST(CustomBlockThenLoadPdfHtml, CUSTOM_BLOCK_LOAD, kPdfHtmlUrl)
+RUN_TEST(CustomBlockThenHidePdfDirect, CUSTOM_BLOCK_HIDE, kPdfDirectUrl)
+RUN_TEST(CustomBlockThenHidePdfHtml, CUSTOM_BLOCK_HIDE, kPdfHtmlUrl)
+RUN_TEST(CustomDisableThenHidePdfDirect, CUSTOM_DISABLE_HIDE, kPdfDirectUrl)
+RUN_TEST(CustomDisableThenHidePdfHtml, CUSTOM_DISABLE_HIDE, kPdfHtmlUrl)
+RUN_TEST(CustomNoListHtml, CUSTOM_NO_LIST, kPdfHtmlUrl)
+
+// Entry point for creating plugin browser test objects.
+// Called from client_app_delegates.cc.
+void CreatePluginBrowserTests(
+    client::ClientAppBrowser::DelegateSet& delegates) {
+  delegates.insert(new PluginBrowserTest);
+}
diff --git a/src/tests/ceftests/preference_unittest.cc b/src/tests/ceftests/preference_unittest.cc
new file mode 100644
index 0000000..8c190d3
--- /dev/null
+++ b/src/tests/ceftests/preference_unittest.cc
@@ -0,0 +1,553 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_waitable_event.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+
+namespace {
+
+// Fully qualified preference names.
+const char kPrefTest[] = "test";
+const char kPrefTestBool[] = "test.bool";
+const char kPrefTestInt[] = "test.int";
+const char kPrefTestDouble[] = "test.double";
+const char kPrefTestString[] = "test.string";
+const char kPrefTestList[] = "test.list";
+const char kPrefTestDict[] = "test.dict";
+const char kPrefTestNoExist[] = "test.noexist";
+
+// Unqualified preference names.
+const char kPrefBool[] = "bool";
+const char kPrefInt[] = "int";
+const char kPrefDouble[] = "double";
+const char kPrefString[] = "string";
+const char kPrefList[] = "list";
+const char kPrefDict[] = "dict";
+
+// Browser-side app delegate.
+class PreferenceBrowserTest : public client::ClientAppBrowser::Delegate {
+ public:
+  PreferenceBrowserTest() {}
+
+  void OnBeforeCommandLineProcessing(
+      CefRefPtr<client::ClientAppBrowser> app,
+      CefRefPtr<CefCommandLine> command_line) override {
+    // Enables testing of preferences.
+    // See CefBrowserPrefStore::CreateService.
+    command_line->AppendSwitch("enable-preference-testing");
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(PreferenceBrowserTest);
+};
+
+void ValidateReset(CefRefPtr<CefRequestContext> context, const char* name) {
+  EXPECT_TRUE(context->HasPreference(name));
+  EXPECT_TRUE(context->CanSetPreference(name));
+
+  CefString error;
+  EXPECT_TRUE(context->SetPreference(name, nullptr, error));
+  EXPECT_TRUE(error.empty());
+}
+
+void ValidateBool(CefRefPtr<CefRequestContext> context,
+                  bool set,
+                  bool expected,
+                  const char* name = kPrefTestBool) {
+  EXPECT_TRUE(context->HasPreference(name));
+  EXPECT_TRUE(context->CanSetPreference(name));
+
+  CefRefPtr<CefValue> value;
+
+  if (set) {
+    value = CefValue::Create();
+    value->SetBool(expected);
+    CefString error;
+    EXPECT_TRUE(context->SetPreference(name, value, error));
+    EXPECT_TRUE(error.empty());
+  }
+
+  value = context->GetPreference(name);
+  EXPECT_TRUE(value.get());
+  EXPECT_EQ(VTYPE_BOOL, value->GetType());
+  EXPECT_EQ(expected, value->GetBool());
+}
+
+void ValidateInt(CefRefPtr<CefRequestContext> context,
+                 bool set,
+                 int expected,
+                 const char* name = kPrefTestInt) {
+  EXPECT_TRUE(context->HasPreference(name));
+  EXPECT_TRUE(context->CanSetPreference(name));
+
+  CefRefPtr<CefValue> value;
+
+  if (set) {
+    value = CefValue::Create();
+    value->SetInt(expected);
+    CefString error;
+    EXPECT_TRUE(context->SetPreference(name, value, error));
+    EXPECT_TRUE(error.empty());
+  }
+
+  value = context->GetPreference(name);
+  EXPECT_TRUE(value.get());
+  EXPECT_EQ(VTYPE_INT, value->GetType());
+  EXPECT_EQ(expected, value->GetInt());
+}
+
+void ValidateDouble(CefRefPtr<CefRequestContext> context,
+                    bool set,
+                    double expected,
+                    const char* name = kPrefTestDouble) {
+  EXPECT_TRUE(context->HasPreference(name));
+  EXPECT_TRUE(context->CanSetPreference(name));
+
+  CefRefPtr<CefValue> value;
+
+  if (set) {
+    value = CefValue::Create();
+    value->SetDouble(expected);
+    CefString error;
+    EXPECT_TRUE(context->SetPreference(name, value, error));
+    EXPECT_TRUE(error.empty());
+  }
+
+  value = context->GetPreference(name);
+  EXPECT_TRUE(value.get());
+  EXPECT_EQ(VTYPE_DOUBLE, value->GetType());
+  EXPECT_EQ(expected, value->GetDouble());
+}
+
+void ValidateString(CefRefPtr<CefRequestContext> context,
+                    bool set,
+                    const std::string& expected,
+                    const char* name = kPrefTestString) {
+  EXPECT_TRUE(context->HasPreference(name));
+  EXPECT_TRUE(context->CanSetPreference(name));
+
+  CefRefPtr<CefValue> value;
+
+  if (set) {
+    value = CefValue::Create();
+    value->SetString(expected);
+    CefString error;
+    EXPECT_TRUE(context->SetPreference(name, value, error));
+    EXPECT_TRUE(error.empty());
+  }
+
+  value = context->GetPreference(name);
+  EXPECT_TRUE(value.get());
+  EXPECT_EQ(VTYPE_STRING, value->GetType());
+  EXPECT_STREQ(expected.c_str(), value->GetString().ToString().c_str());
+}
+
+void ValidateList(CefRefPtr<CefRequestContext> context,
+                  bool set,
+                  CefRefPtr<CefListValue> expected,
+                  const char* name = kPrefTestList) {
+  EXPECT_TRUE(context->HasPreference(name));
+  EXPECT_TRUE(context->CanSetPreference(name));
+
+  CefRefPtr<CefValue> value;
+
+  if (set) {
+    value = CefValue::Create();
+    value->SetList(expected);
+    CefString error;
+    EXPECT_TRUE(context->SetPreference(name, value, error));
+    EXPECT_TRUE(error.empty());
+  }
+
+  value = context->GetPreference(name);
+  EXPECT_TRUE(value.get());
+  EXPECT_EQ(VTYPE_LIST, value->GetType());
+  CefRefPtr<CefListValue> list_val = value->GetList();
+  EXPECT_TRUE(list_val);
+  TestListEqual(expected, list_val);
+}
+
+void ValidateDict(CefRefPtr<CefRequestContext> context,
+                  bool set,
+                  CefRefPtr<CefDictionaryValue> expected,
+                  const char* name = kPrefTestDict) {
+  EXPECT_TRUE(context->HasPreference(name));
+  EXPECT_TRUE(context->CanSetPreference(name));
+
+  CefRefPtr<CefValue> value;
+
+  if (set) {
+    value = CefValue::Create();
+    value->SetDictionary(expected);
+    CefString error;
+    EXPECT_TRUE(context->SetPreference(name, value, error));
+    EXPECT_TRUE(error.empty());
+  }
+
+  value = context->GetPreference(name);
+  EXPECT_TRUE(value.get());
+  EXPECT_EQ(VTYPE_DICTIONARY, value->GetType());
+  CefRefPtr<CefDictionaryValue> dict_val = value->GetDictionary();
+  EXPECT_TRUE(dict_val);
+  TestDictionaryEqual(expected, dict_val);
+}
+
+void ValidateNoExist(CefRefPtr<CefRequestContext> context,
+                     bool set,
+                     const char* name = kPrefTestNoExist) {
+  EXPECT_FALSE(context->HasPreference(name));
+  EXPECT_FALSE(context->CanSetPreference(name));
+
+  CefRefPtr<CefValue> value;
+
+  if (set) {
+    value = CefValue::Create();
+    value->SetBool(false);
+    CefString error;
+    EXPECT_FALSE(context->SetPreference(name, value, error));
+    EXPECT_FALSE(error.empty());
+  }
+
+  value = context->GetPreference(name);
+  EXPECT_FALSE(value.get());
+}
+
+void PopulateRootDefaults(CefRefPtr<CefDictionaryValue> val) {
+  // Should match the values in CefBrowserPrefStore::CreateService.
+  val->SetBool(kPrefBool, true);
+  val->SetInt(kPrefInt, 2);
+  val->SetDouble(kPrefDouble, 5.0);
+  val->SetString(kPrefString, "default");
+  val->SetList(kPrefList, CefListValue::Create());
+  val->SetDictionary(kPrefDict, CefDictionaryValue::Create());
+}
+
+void ValidateRoot(CefRefPtr<CefDictionaryValue> root,
+                  CefRefPtr<CefDictionaryValue> expected,
+                  const char* name = kPrefTest) {
+  EXPECT_TRUE(root->HasKey(kPrefTest));
+  EXPECT_EQ(VTYPE_DICTIONARY, root->GetType(kPrefTest));
+
+  CefRefPtr<CefDictionaryValue> actual = root->GetDictionary(kPrefTest);
+  TestDictionaryEqual(expected, actual);
+}
+
+// Validate getting default values.
+void ValidateDefaults(CefRefPtr<CefRequestContext> context,
+                      bool reset,
+                      CefRefPtr<CefWaitableEvent> event) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(ValidateDefaults, context, reset, event));
+    return;
+  }
+
+  if (reset) {
+    // Reset default values.
+    ValidateReset(context, kPrefTestBool);
+    ValidateReset(context, kPrefTestInt);
+    ValidateReset(context, kPrefTestDouble);
+    ValidateReset(context, kPrefTestString);
+    ValidateReset(context, kPrefTestList);
+    ValidateReset(context, kPrefTestDict);
+  }
+
+  // Test default values.
+  // Should match the values in CefBrowserPrefStore::CreateService.
+  ValidateBool(context, false, true);
+  ValidateInt(context, false, 2);
+  ValidateDouble(context, false, 5.0);
+  ValidateString(context, false, "default");
+  ValidateList(context, false, CefListValue::Create());
+  ValidateDict(context, false, CefDictionaryValue::Create());
+  ValidateNoExist(context, false);
+
+  // Expected value of the tests root.
+  CefRefPtr<CefDictionaryValue> expected = CefDictionaryValue::Create();
+  PopulateRootDefaults(expected);
+
+  // Test all preferences including defaults.
+  ValidateRoot(context->GetAllPreferences(true), expected);
+
+  // Test all preferences excluding defaults.
+  EXPECT_FALSE(context->GetAllPreferences(false)->HasKey(kPrefTest));
+
+  event->Signal();
+}
+
+void PopulateListValue(CefRefPtr<CefListValue> val) {
+  // Test list values.
+  val->SetInt(0, 54);
+  val->SetString(1, "foobar");
+  val->SetDouble(2, 99.7643);
+}
+
+void PopulateDictValue(CefRefPtr<CefDictionaryValue> val) {
+  // Test dictionary values.
+  val->SetString("key1", "some string");
+  val->SetBool("key2", false);
+
+  CefRefPtr<CefListValue> list_val = CefListValue::Create();
+  PopulateListValue(list_val);
+  val->SetList("key3", list_val);
+}
+
+void PopulateRootSet(CefRefPtr<CefDictionaryValue> val) {
+  CefRefPtr<CefListValue> list_val = CefListValue::Create();
+  CefRefPtr<CefDictionaryValue> dict_val = CefDictionaryValue::Create();
+
+  PopulateListValue(list_val);
+  PopulateDictValue(dict_val);
+
+  // Should match the values in ValidateSetGet and ValidateGet.
+  val->SetBool(kPrefBool, true);
+  val->SetInt(kPrefInt, 65);
+  val->SetDouble(kPrefDouble, 54.5443);
+  val->SetString(kPrefString, "My test string");
+  val->SetList(kPrefList, list_val);
+  val->SetDictionary(kPrefDict, dict_val);
+}
+
+// Validate getting and setting values.
+void ValidateSetGet(CefRefPtr<CefRequestContext> context,
+                    CefRefPtr<CefWaitableEvent> event) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(ValidateSetGet, context, event));
+    return;
+  }
+
+  CefRefPtr<CefListValue> list_val = CefListValue::Create();
+  CefRefPtr<CefDictionaryValue> dict_val = CefDictionaryValue::Create();
+
+  PopulateListValue(list_val);
+  PopulateDictValue(dict_val);
+
+  // Test setting/getting values.
+  // Should match the values in PopulateRootSet and ValidateGet.
+  ValidateBool(context, true, true);
+  ValidateInt(context, true, 65);
+  ValidateDouble(context, true, 54.5443);
+  ValidateString(context, true, "My test string");
+  ValidateList(context, true, list_val);
+  ValidateDict(context, true, dict_val);
+  ValidateNoExist(context, true);
+
+  // Expected value of the tests root.
+  CefRefPtr<CefDictionaryValue> expected = CefDictionaryValue::Create();
+  PopulateRootSet(expected);
+
+  // Validate all preferences including defaults.
+  ValidateRoot(context->GetAllPreferences(true), expected);
+
+  // Validate all preferences excluding defaults.
+  ValidateRoot(context->GetAllPreferences(false), expected);
+
+  event->Signal();
+}
+
+// Validate getting values.
+void ValidateGet(CefRefPtr<CefRequestContext> context,
+                 CefRefPtr<CefWaitableEvent> event) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(ValidateGet, context, event));
+    return;
+  }
+
+  CefRefPtr<CefListValue> list_val = CefListValue::Create();
+  CefRefPtr<CefDictionaryValue> dict_val = CefDictionaryValue::Create();
+
+  PopulateListValue(list_val);
+  PopulateDictValue(dict_val);
+
+  // Test getting values.
+  // Should match the values in PopulateRootSet and ValidateSetGet.
+  ValidateBool(context, false, true);
+  ValidateInt(context, false, 65);
+  ValidateDouble(context, false, 54.5443);
+  ValidateString(context, false, "My test string");
+  ValidateList(context, false, list_val);
+  ValidateDict(context, false, dict_val);
+  ValidateNoExist(context, false);
+
+  // Expected value of the tests root.
+  CefRefPtr<CefDictionaryValue> expected = CefDictionaryValue::Create();
+  PopulateRootSet(expected);
+
+  // Validate all preferences including defaults.
+  ValidateRoot(context->GetAllPreferences(true), expected);
+
+  // Validate all preferences excluding defaults.
+  ValidateRoot(context->GetAllPreferences(false), expected);
+
+  event->Signal();
+}
+
+// No-op implementation.
+class TestRequestContextHandler : public CefRequestContextHandler {
+ public:
+  TestRequestContextHandler() {}
+
+  IMPLEMENT_REFCOUNTING(TestRequestContextHandler);
+};
+
+}  // namespace
+
+// Verify default preference values on the global context.
+TEST(PreferenceTest, GlobalDefaults) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefRequestContext> context = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context.get());
+
+  ValidateDefaults(context, false, event);
+  event->Wait();
+}
+
+// Verify setting/getting preference values on the global context.
+TEST(PreferenceTest, GlobalSetGet) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefRequestContext> context = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context.get());
+
+  ValidateSetGet(context, event);
+  event->Wait();
+
+  // Reset to the default values.
+  ValidateDefaults(context, true, event);
+  event->Wait();
+}
+
+// Verify setting/getting preference values on shared global contexts.
+TEST(PreferenceTest, GlobalSetGetShared) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefRequestContext> context = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context.get());
+
+  // Sharing storage.
+  CefRefPtr<CefRequestContext> context2 =
+      CefRequestContext::CreateContext(context, nullptr);
+  EXPECT_TRUE(context2.get());
+
+  // Sharing storage.
+  CefRefPtr<CefRequestContext> context3 =
+      CefRequestContext::CreateContext(context, new TestRequestContextHandler);
+  EXPECT_TRUE(context3.get());
+
+  // Unassociated context.
+  CefRequestContextSettings settings;
+  CefRefPtr<CefRequestContext> context4 =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context.get());
+
+  // Set/get the values on the first context.
+  ValidateSetGet(context, event);
+  event->Wait();
+
+  // Get the values from the 2nd and 3rd contexts. They should be the same.
+  ValidateGet(context2, event);
+  event->Wait();
+  ValidateGet(context3, event);
+  event->Wait();
+
+  // Get the values from the 4th context. They should be at the default.
+  ValidateDefaults(context4, false, event);
+  event->Wait();
+
+  // Reset to the default values.
+  ValidateDefaults(context, true, event);
+  event->Wait();
+}
+
+// Verify default preference values on a custom context.
+TEST(PreferenceTest, CustomDefaults) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRequestContextSettings settings;
+  CefRefPtr<CefRequestContext> context =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context.get());
+
+  ValidateDefaults(context, false, event);
+  event->Wait();
+}
+
+// Verify setting/getting preference values on a custom context.
+TEST(PreferenceTest, CustomSetGet) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRequestContextSettings settings;
+  CefRefPtr<CefRequestContext> context =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context.get());
+
+  ValidateSetGet(context, event);
+  event->Wait();
+
+  // Reset to the default values.
+  ValidateDefaults(context, true, event);
+  event->Wait();
+}
+
+// Verify setting/getting preference values on shared custom contexts.
+TEST(PreferenceTest, CustomSetGetShared) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRequestContextSettings settings;
+  CefRefPtr<CefRequestContext> context =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context.get());
+
+  // Sharing storage.
+  CefRefPtr<CefRequestContext> context2 =
+      CefRequestContext::CreateContext(context, nullptr);
+  EXPECT_TRUE(context2.get());
+
+  // Sharing storage.
+  CefRefPtr<CefRequestContext> context3 =
+      CefRequestContext::CreateContext(context, new TestRequestContextHandler);
+  EXPECT_TRUE(context3.get());
+
+  // Unassociated context.
+  CefRefPtr<CefRequestContext> context4 =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context.get());
+
+  // Set/get the values on the first context.
+  ValidateSetGet(context, event);
+  event->Wait();
+
+  // Get the values from the 2nd and 3d contexts. They should be the same.
+  ValidateGet(context2, event);
+  event->Wait();
+  ValidateGet(context3, event);
+  event->Wait();
+
+  // Get the values from the 4th context. They should be at the default.
+  ValidateDefaults(context4, false, event);
+  event->Wait();
+
+  // Reset to the default values.
+  ValidateDefaults(context, true, event);
+  event->Wait();
+}
+
+// Entry point for creating preference browser test objects.
+// Called from client_app_delegates.cc.
+void CreatePreferenceBrowserTests(
+    client::ClientAppBrowser::DelegateSet& delegates) {
+  delegates.insert(new PreferenceBrowserTest);
+}
diff --git a/src/tests/ceftests/print_unittest.cc b/src/tests/ceftests/print_unittest.cc
new file mode 100644
index 0000000..3a81edc
--- /dev/null
+++ b/src/tests/ceftests/print_unittest.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+
+#include "include/cef_print_settings.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+// Verify Set/Get methods for CefPrintSettings.
+TEST(PrintTest, SettingsSetGet) {
+  // CefRequest CreateRequest
+  CefRefPtr<CefPrintSettings> settings(CefPrintSettings::Create());
+  EXPECT_TRUE(settings.get() != nullptr);
+  EXPECT_TRUE(settings->IsValid());
+  EXPECT_FALSE(settings->IsReadOnly());
+
+  bool landscape = true;
+  settings->SetOrientation(landscape);
+  EXPECT_EQ(landscape, settings->IsLandscape());
+  landscape = false;
+  settings->SetOrientation(landscape);
+  EXPECT_EQ(landscape, settings->IsLandscape());
+
+  const char device_name[] = "my_device_name";
+  settings->SetDeviceName(device_name);
+  EXPECT_STREQ(device_name, settings->GetDeviceName().ToString().c_str());
+
+  int dpi = 25;
+  settings->SetDPI(dpi);
+  EXPECT_EQ(dpi, settings->GetDPI());
+
+  CefPrintSettings::PageRangeList page_ranges;
+  page_ranges.push_back(CefRange(1, 3));
+  page_ranges.push_back(CefRange(5, 6));
+  settings->SetPageRanges(page_ranges);
+  EXPECT_EQ(page_ranges.size(), settings->GetPageRangesCount());
+  CefPrintSettings::PageRangeList page_ranges2;
+  settings->GetPageRanges(page_ranges2);
+  EXPECT_EQ(page_ranges.size(), page_ranges2.size());
+  for (size_t i = 0; i < page_ranges.size(); ++i)
+    EXPECT_EQ(page_ranges[i], page_ranges2[i]);
+
+  bool selection_only = true;
+  settings->SetSelectionOnly(selection_only);
+  EXPECT_EQ(selection_only, settings->IsSelectionOnly());
+  selection_only = false;
+  settings->SetSelectionOnly(selection_only);
+  EXPECT_EQ(selection_only, settings->IsSelectionOnly());
+
+  bool collate = true;
+  settings->SetCollate(collate);
+  EXPECT_EQ(collate, settings->WillCollate());
+  collate = false;
+  settings->SetCollate(collate);
+  EXPECT_EQ(collate, settings->WillCollate());
+
+  CefPrintSettings::ColorModel color_model = COLOR_MODEL_CMYK;
+  settings->SetColorModel(color_model);
+  EXPECT_EQ(color_model, settings->GetColorModel());
+
+  int copies = 3;
+  settings->SetCopies(copies);
+  EXPECT_EQ(copies, settings->GetCopies());
+
+  CefPrintSettings::DuplexMode duplex_mode = DUPLEX_MODE_SIMPLEX;
+  settings->SetDuplexMode(duplex_mode);
+  EXPECT_EQ(duplex_mode, settings->GetDuplexMode());
+}
diff --git a/src/tests/ceftests/process_message_unittest.cc b/src/tests/ceftests/process_message_unittest.cc
new file mode 100644
index 0000000..2626128
--- /dev/null
+++ b/src/tests/ceftests/process_message_unittest.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_process_message.h"
+#include "include/cef_task.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppRenderer;
+
+namespace {
+
+// Unique values for the SendRecv test.
+const char kSendRecvUrl[] = "http://tests/ProcessMessageTest.SendRecv";
+const char kSendRecvMsg[] = "ProcessMessageTest.SendRecv";
+
+// Creates a test message.
+CefRefPtr<CefProcessMessage> CreateTestMessage() {
+  CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create(kSendRecvMsg);
+  EXPECT_TRUE(msg.get());
+
+  CefRefPtr<CefListValue> args = msg->GetArgumentList();
+  EXPECT_TRUE(args.get());
+
+  size_t index = 0;
+  args->SetNull(index++);
+  args->SetInt(index++, 5);
+  args->SetDouble(index++, 10.543);
+  args->SetBool(index++, true);
+  args->SetString(index++, "test string");
+  args->SetList(index++, args->Copy());
+
+  EXPECT_EQ(index, args->GetSize());
+
+  return msg;
+}
+
+// Renderer side.
+class SendRecvRendererTest : public ClientAppRenderer::Delegate {
+ public:
+  SendRecvRendererTest() {}
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName() == kSendRecvMsg) {
+      EXPECT_TRUE(browser.get());
+      EXPECT_TRUE(frame.get());
+      EXPECT_EQ(PID_BROWSER, source_process);
+      EXPECT_TRUE(message.get());
+
+      const std::string& url = frame->GetURL();
+      if (url == kSendRecvUrl) {
+        // Echo the message back to the sender natively.
+        frame->SendProcessMessage(PID_BROWSER, message);
+        return true;
+      }
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+  IMPLEMENT_REFCOUNTING(SendRecvRendererTest);
+};
+
+// Browser side.
+class SendRecvTestHandler : public TestHandler {
+ public:
+  explicit SendRecvTestHandler(cef_thread_id_t send_thread)
+      : send_thread_(send_thread) {}
+
+  void RunTest() override {
+    message_ = CreateTestMessage();
+
+    AddResource(kSendRecvUrl, "<html><body>TEST</body></html>", "text/html");
+    CreateBrowser(kSendRecvUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+
+    // Send the message to the renderer process.
+    if (!CefCurrentlyOn(send_thread_)) {
+      CefPostTask(send_thread_, base::Bind(&SendRecvTestHandler::SendMessage,
+                                           this, browser, frame));
+    } else {
+      SendMessage(browser, frame);
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    EXPECT_EQ(PID_RENDERER, source_process);
+    EXPECT_TRUE(message.get());
+    EXPECT_TRUE(message->IsReadOnly());
+
+    // Verify that the recieved message is the same as the sent message.
+    TestProcessMessageEqual(message_, message);
+
+    got_message_.yes();
+
+    // Test is complete.
+    DestroyTest();
+
+    return true;
+  }
+
+ protected:
+  void DestroyTest() override {
+    EXPECT_TRUE(got_message_);
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  void SendMessage(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) {
+    EXPECT_TRUE(CefCurrentlyOn(send_thread_));
+    frame->SendProcessMessage(PID_RENDERER, message_);
+  }
+
+  cef_thread_id_t send_thread_;
+
+  CefRefPtr<CefProcessMessage> message_;
+  TrackCallback got_message_;
+
+  IMPLEMENT_REFCOUNTING(SendRecvTestHandler);
+};
+
+}  // namespace
+
+// Verify send from the UI thread and recieve.
+TEST(ProcessMessageTest, SendRecvUI) {
+  CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler(TID_UI);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify send from the IO thread and recieve.
+TEST(ProcessMessageTest, SendRecvIO) {
+  CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler(TID_IO);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify create.
+TEST(ProcessMessageTest, Create) {
+  CefRefPtr<CefProcessMessage> message =
+      CefProcessMessage::Create(kSendRecvMsg);
+  EXPECT_TRUE(message.get());
+  EXPECT_TRUE(message->IsValid());
+  EXPECT_FALSE(message->IsReadOnly());
+  EXPECT_STREQ(kSendRecvMsg, message->GetName().ToString().c_str());
+
+  CefRefPtr<CefListValue> args = message->GetArgumentList();
+  EXPECT_TRUE(args.get());
+  EXPECT_TRUE(args->IsValid());
+  EXPECT_TRUE(args->IsOwned());
+  EXPECT_FALSE(args->IsReadOnly());
+}
+
+// Verify copy.
+TEST(ProcessMessageTest, Copy) {
+  CefRefPtr<CefProcessMessage> message = CreateTestMessage();
+  CefRefPtr<CefProcessMessage> message2 = message->Copy();
+  TestProcessMessageEqual(message, message2);
+}
+
+// Entry point for creating process message renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateProcessMessageRendererTests(
+    ClientAppRenderer::DelegateSet& delegates) {
+  // For ProcessMessageTest.SendRecv
+  delegates.insert(new SendRecvRendererTest);
+}
diff --git a/src/tests/ceftests/request_context_unittest.cc b/src/tests/ceftests/request_context_unittest.cc
new file mode 100644
index 0000000..5058ea9
--- /dev/null
+++ b/src/tests/ceftests/request_context_unittest.cc
@@ -0,0 +1,907 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_request_context.h"
+#include "include/cef_request_context_handler.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_scoped_temp_dir.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+TEST(RequestContextTest, BasicGetGlobal) {
+  CefRefPtr<CefRequestContext> context1 = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context1.get());
+  EXPECT_TRUE(context1->IsGlobal());
+  EXPECT_TRUE(context1->IsSame(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context1));
+
+  CefRefPtr<CefRequestContext> context2 = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context2.get());
+  EXPECT_TRUE(context2->IsGlobal());
+  EXPECT_TRUE(context2->IsSame(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context2));
+
+  EXPECT_TRUE(context1->IsSame(context2));
+  EXPECT_TRUE(context2->IsSame(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context1));
+}
+
+TEST(RequestContextTest, BasicCreate) {
+  class Handler : public CefRequestContextHandler {
+   public:
+    Handler() {}
+
+   private:
+    IMPLEMENT_REFCOUNTING(Handler);
+  };
+
+  CefRefPtr<CefRequestContextHandler> handler = new Handler();
+
+  CefRequestContextSettings settings;
+
+  CefRefPtr<CefRequestContext> context1 =
+      CefRequestContext::CreateContext(settings, handler.get());
+  EXPECT_TRUE(context1.get());
+  EXPECT_FALSE(context1->IsGlobal());
+  EXPECT_TRUE(context1->IsSame(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context1));
+  EXPECT_EQ(context1->GetHandler().get(), handler.get());
+
+  CefRefPtr<CefRequestContext> context2 =
+      CefRequestContext::CreateContext(settings, handler.get());
+  EXPECT_TRUE(context2.get());
+  EXPECT_FALSE(context2->IsGlobal());
+  EXPECT_TRUE(context2->IsSame(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context2));
+  EXPECT_EQ(context2->GetHandler().get(), handler.get());
+
+  EXPECT_FALSE(context1->IsSame(context2));
+  EXPECT_FALSE(context1->IsSharingWith(context2));
+  EXPECT_FALSE(context2->IsSame(context1));
+  EXPECT_FALSE(context2->IsSharingWith(context1));
+
+  CefRefPtr<CefRequestContext> context3 = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context3.get());
+  EXPECT_FALSE(context3->IsSame(context1));
+  EXPECT_FALSE(context3->IsSharingWith(context1));
+  EXPECT_FALSE(context3->IsSame(context2));
+  EXPECT_FALSE(context3->IsSharingWith(context2));
+  EXPECT_FALSE(context1->IsSame(context3));
+  EXPECT_FALSE(context1->IsSharingWith(context3));
+  EXPECT_FALSE(context2->IsSame(context3));
+  EXPECT_FALSE(context2->IsSharingWith(context3));
+}
+
+TEST(RequestContextTest, BasicCreateNoHandler) {
+  CefRequestContextSettings settings;
+
+  CefRefPtr<CefRequestContext> context1 =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context1.get());
+  EXPECT_FALSE(context1->IsGlobal());
+  EXPECT_TRUE(context1->IsSame(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context1));
+  EXPECT_FALSE(context1->GetHandler().get());
+
+  CefRefPtr<CefRequestContext> context2 =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context2.get());
+  EXPECT_FALSE(context2->IsGlobal());
+  EXPECT_TRUE(context2->IsSame(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context2));
+  EXPECT_FALSE(context2->GetHandler().get());
+
+  EXPECT_FALSE(context1->IsSame(context2));
+  EXPECT_FALSE(context1->IsSharingWith(context2));
+  EXPECT_FALSE(context2->IsSame(context1));
+  EXPECT_FALSE(context2->IsSharingWith(context1));
+
+  CefRefPtr<CefRequestContext> context3 = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context3.get());
+  EXPECT_FALSE(context3->IsSame(context1));
+  EXPECT_FALSE(context3->IsSharingWith(context1));
+  EXPECT_FALSE(context3->IsSame(context2));
+  EXPECT_FALSE(context3->IsSharingWith(context2));
+  EXPECT_FALSE(context1->IsSame(context3));
+  EXPECT_FALSE(context1->IsSharingWith(context3));
+  EXPECT_FALSE(context2->IsSame(context3));
+  EXPECT_FALSE(context2->IsSharingWith(context3));
+}
+
+TEST(RequestContextTest, BasicCreateSharedGlobal) {
+  CefRequestContextSettings settings;
+
+  CefRefPtr<CefRequestContext> context1 = CefRequestContext::GetGlobalContext();
+  EXPECT_TRUE(context1.get());
+  EXPECT_TRUE(context1->IsGlobal());
+  EXPECT_TRUE(context1->IsSame(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context1));
+
+  // Returns the same global context.
+  CefRefPtr<CefRequestContext> context2 =
+      CefRequestContext::CreateContext(context1, nullptr);
+  EXPECT_TRUE(context2.get());
+  EXPECT_TRUE(context2->IsGlobal());
+  EXPECT_TRUE(context2->IsSame(context2));
+  EXPECT_TRUE(context2->IsSame(context1));
+  EXPECT_TRUE(context1->IsSame(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context2));
+}
+
+TEST(RequestContextTest, BasicCreateSharedOnDisk) {
+  CefScopedTempDir tempdir;
+  EXPECT_TRUE(tempdir.CreateUniqueTempDir());
+
+  CefRequestContextSettings settings;
+  CefString(&settings.cache_path) = tempdir.GetPath();
+
+  CefRefPtr<CefRequestContext> context1 =
+      CefRequestContext::CreateContext(settings, nullptr);
+  EXPECT_TRUE(context1.get());
+  EXPECT_FALSE(context1->IsGlobal());
+  EXPECT_TRUE(context1->IsSame(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context1));
+
+  CefRefPtr<CefRequestContext> context2 =
+      CefRequestContext::CreateContext(context1, nullptr);
+  EXPECT_TRUE(context2.get());
+  EXPECT_FALSE(context2->IsGlobal());
+  EXPECT_TRUE(context2->IsSame(context2));
+  EXPECT_FALSE(context2->IsSame(context1));
+  EXPECT_FALSE(context1->IsSame(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context2));
+  EXPECT_TRUE(context2->IsSharingWith(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context2));
+
+  CefRefPtr<CefRequestContext> context3 =
+      CefRequestContext::CreateContext(context2, nullptr);
+  EXPECT_TRUE(context3.get());
+  EXPECT_FALSE(context3->IsGlobal());
+  EXPECT_TRUE(context3->IsSame(context3));
+  EXPECT_FALSE(context3->IsSame(context2));
+  EXPECT_FALSE(context3->IsSame(context1));
+  EXPECT_FALSE(context1->IsSame(context3));
+  EXPECT_FALSE(context2->IsSame(context3));
+  EXPECT_TRUE(context3->IsSharingWith(context3));
+  EXPECT_TRUE(context3->IsSharingWith(context2));
+  EXPECT_TRUE(context3->IsSharingWith(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context3));
+  EXPECT_TRUE(context2->IsSharingWith(context3));
+
+  CefRefPtr<CefRequestContext> context4 =
+      CefRequestContext::CreateContext(context1, nullptr);
+  EXPECT_TRUE(context4.get());
+  EXPECT_FALSE(context4->IsGlobal());
+  EXPECT_TRUE(context4->IsSame(context4));
+  EXPECT_FALSE(context4->IsSame(context3));
+  EXPECT_FALSE(context4->IsSame(context2));
+  EXPECT_FALSE(context4->IsSame(context1));
+  EXPECT_FALSE(context1->IsSame(context4));
+  EXPECT_FALSE(context2->IsSame(context4));
+  EXPECT_FALSE(context3->IsSame(context4));
+  EXPECT_TRUE(context4->IsSharingWith(context4));
+  EXPECT_TRUE(context4->IsSharingWith(context3));
+  EXPECT_TRUE(context4->IsSharingWith(context2));
+  EXPECT_TRUE(context4->IsSharingWith(context1));
+  EXPECT_TRUE(context1->IsSharingWith(context4));
+  EXPECT_TRUE(context2->IsSharingWith(context4));
+  EXPECT_TRUE(context3->IsSharingWith(context4));
+}
+
+namespace {
+
+class PopupTestHandler : public TestHandler {
+ public:
+  enum Mode {
+    MODE_WINDOW_OPEN,
+    MODE_TARGETED_LINK,
+    MODE_NOREFERRER_LINK,
+  };
+
+  PopupTestHandler(bool same_origin, Mode mode) : mode_(mode) {
+    url_ = "http://tests-simple-rch1.com/nav1.html";
+    if (same_origin)
+      popup_url_ = "http://tests-simple-rch1.com/pop1.html";
+    else
+      popup_url_ = "http://tests-simple-rch2.com/pop1.html";
+  }
+
+  void RunTest() override {
+    std::string link;
+    if (mode_ == MODE_TARGETED_LINK) {
+      link = "<a href=\"" + std::string(popup_url_) +
+             "\" target=\"mytarget\"\">CLICK ME</a>";
+    } else if (mode_ == MODE_NOREFERRER_LINK) {
+      link = "<a href=\"" + std::string(popup_url_) +
+             "\" rel=\"noreferrer\" target=\"_blank\"\">CLICK ME</a>";
+    }
+
+    AddResource(url_,
+                "<html>"
+                "<head><script>document.cookie='name1=value1';"
+                "function doPopup() { window.open('" +
+                    std::string(popup_url_) +
+                    "'); }"
+                    "</script></head>"
+                    "<body><h1>" +
+                    link +
+                    "</h1></body>"
+                    "</html>",
+                "text/html");
+
+    AddResource(popup_url_,
+                "<html>"
+                "<head><script>document.cookie='name2=value2';</script></head>"
+                "<body>Nav1</body>"
+                "</html>",
+                "text/html");
+
+    CefRequestContextSettings settings;
+
+    context_ = CefRequestContext::CreateContext(settings, nullptr);
+    cookie_manager_ = context_->GetCookieManager(nullptr);
+
+    // Create browser that loads the 1st URL.
+    CreateBrowser(url_, context_);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    CefRefPtr<CefRequestContext> context =
+        browser->GetHost()->GetRequestContext();
+    EXPECT_TRUE(context.get());
+    EXPECT_TRUE(context->IsSame(context_));
+    EXPECT_FALSE(context->IsGlobal());
+
+    EXPECT_TRUE(frame->IsMain());
+
+    const std::string& url = frame->GetURL();
+    if (url == url_) {
+      got_load_end1_.yes();
+      LaunchPopup(browser);
+    } else if (url == popup_url_) {
+      got_load_end2_.yes();
+      EXPECT_TRUE(browser->IsPopup());
+      // Close the popup window.
+      CloseBrowser(browser, true);
+    }
+  }
+
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     cef_window_open_disposition_t target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override {
+    got_on_before_popup_.yes();
+
+    const std::string& url = target_url;
+    EXPECT_STREQ(url.c_str(), popup_url_.c_str());
+
+    EXPECT_EQ(WOD_NEW_FOREGROUND_TAB, target_disposition);
+
+    if (mode_ == MODE_WINDOW_OPEN)
+      EXPECT_FALSE(user_gesture);
+    else
+      EXPECT_TRUE(user_gesture);
+
+    return false;
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnBeforeClose(browser);
+
+    if (browser->IsPopup())
+      FinishTest();
+  }
+
+ protected:
+  void LaunchPopup(CefRefPtr<CefBrowser> browser) {
+    if (mode_ == MODE_WINDOW_OPEN) {
+      browser->GetMainFrame()->ExecuteJavaScript("doPopup()", url_, 0);
+    } else if (mode_ == MODE_TARGETED_LINK || mode_ == MODE_NOREFERRER_LINK) {
+      CefMouseEvent mouse_event;
+      mouse_event.x = 20;
+      mouse_event.y = 20;
+      mouse_event.modifiers = 0;
+      browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1);
+      browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
+    } else {
+      EXPECT_TRUE(false);  // Not reached.
+    }
+  }
+
+  void FinishTest() {
+    // Verify that the cookies were set correctly.
+    class TestVisitor : public CefCookieVisitor {
+     public:
+      explicit TestVisitor(PopupTestHandler* handler) : handler_(handler) {}
+      ~TestVisitor() override {
+        // Destroy the test.
+        CefPostTask(TID_UI,
+                    base::Bind(&PopupTestHandler::DestroyTest, handler_));
+      }
+
+      bool Visit(const CefCookie& cookie,
+                 int count,
+                 int total,
+                 bool& deleteCookie) override {
+        const std::string& name = CefString(&cookie.name);
+        const std::string& value = CefString(&cookie.value);
+        if (name == "name1" && value == "value1") {
+          handler_->got_cookie1_.yes();
+          deleteCookie = true;
+        } else if (name == "name2" && value == "value2") {
+          handler_->got_cookie2_.yes();
+          deleteCookie = true;
+        }
+        return true;
+      }
+
+     private:
+      PopupTestHandler* handler_;
+      IMPLEMENT_REFCOUNTING(TestVisitor);
+    };
+
+    cookie_manager_->VisitAllCookies(new TestVisitor(this));
+  }
+
+  void DestroyTest() override {
+    // Verify test expectations.
+    EXPECT_TRUE(got_load_end1_);
+    EXPECT_TRUE(got_on_before_popup_);
+    EXPECT_TRUE(got_load_end2_);
+    EXPECT_TRUE(got_cookie1_);
+    EXPECT_TRUE(got_cookie2_);
+    context_ = nullptr;
+
+    TestHandler::DestroyTest();
+  }
+
+  std::string url_;
+  std::string popup_url_;
+  Mode mode_;
+
+  CefRefPtr<CefRequestContext> context_;
+  CefRefPtr<CefCookieManager> cookie_manager_;
+
+  TrackCallback got_load_end1_;
+  TrackCallback got_on_before_popup_;
+  TrackCallback got_load_end2_;
+  TrackCallback got_cookie1_;
+  TrackCallback got_cookie2_;
+
+  IMPLEMENT_REFCOUNTING(PopupTestHandler);
+};
+
+}  // namespace
+
+// Test that a popup created using window.open() will get the same request
+// context as the parent browser.
+TEST(RequestContextTest, PopupBasicWindowOpenSameOrigin) {
+  CefRefPtr<PopupTestHandler> handler =
+      new PopupTestHandler(true, PopupTestHandler::MODE_WINDOW_OPEN);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(RequestContextTest, PopupBasicWindowOpenDifferentOrigin) {
+  CefRefPtr<PopupTestHandler> handler =
+      new PopupTestHandler(false, PopupTestHandler::MODE_WINDOW_OPEN);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a popup created using a targeted link will get the same request
+// context as the parent browser.
+TEST(RequestContextTest, PopupBasicTargetedLinkSameOrigin) {
+  CefRefPtr<PopupTestHandler> handler =
+      new PopupTestHandler(true, PopupTestHandler::MODE_TARGETED_LINK);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(RequestContextTest, PopupBasicTargetedLinkDifferentOrigin) {
+  CefRefPtr<PopupTestHandler> handler =
+      new PopupTestHandler(false, PopupTestHandler::MODE_TARGETED_LINK);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test that a popup created using a noreferrer link will get the same
+// request context as the parent browser. A new render process will
+// be created for the popup browser.
+TEST(RequestContextTest, PopupBasicNoReferrerLinkSameOrigin) {
+  CefRefPtr<PopupTestHandler> handler =
+      new PopupTestHandler(true, PopupTestHandler::MODE_NOREFERRER_LINK);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(RequestContextTest, PopupBasicNoReferrerLinkDifferentOrigin) {
+  CefRefPtr<PopupTestHandler> handler =
+      new PopupTestHandler(false, PopupTestHandler::MODE_NOREFERRER_LINK);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kPopupNavPageUrl[] = "http://tests-popup.com/page.html";
+const char kPopupNavPopupUrl[] = "http://tests-popup.com/popup.html";
+const char kPopupNavPopupUrl2[] = "http://tests-popup2.com/popup.html";
+const char kPopupNavPopupName[] = "my_popup";
+
+// Browser side.
+class PopupNavTestHandler : public TestHandler {
+ public:
+  enum TestMode {
+    ALLOW_CLOSE_POPUP_FIRST,
+    ALLOW_CLOSE_POPUP_LAST,
+    DENY,
+    NAVIGATE_AFTER_CREATION,
+    DESTROY_PARENT_BEFORE_CREATION,
+    DESTROY_PARENT_BEFORE_CREATION_FORCE,
+    DESTROY_PARENT_DURING_CREATION,
+    DESTROY_PARENT_DURING_CREATION_FORCE,
+    DESTROY_PARENT_AFTER_CREATION,
+    DESTROY_PARENT_AFTER_CREATION_FORCE,
+  };
+
+  PopupNavTestHandler(TestMode test_mode,
+                      TestRequestContextMode rc_mode,
+                      const std::string& rc_cache_path)
+      : mode_(test_mode), rc_mode_(rc_mode), rc_cache_path_(rc_cache_path) {}
+
+  void RunTest() override {
+    // Add the resources that we will navigate to/from.
+    std::string page = "<html><script>function doPopup() { window.open('" +
+                       std::string(kPopupNavPopupUrl) + "', '" +
+                       std::string(kPopupNavPopupName) +
+                       "'); }</script>Page</html>";
+    AddResource(kPopupNavPageUrl, page, "text/html");
+    AddResource(kPopupNavPopupUrl, "<html>Popup</html>", "text/html");
+    if (mode_ == NAVIGATE_AFTER_CREATION)
+      AddResource(kPopupNavPopupUrl2, "<html>Popup2</html>", "text/html");
+
+    CefRefPtr<CefRequestContext> request_context =
+        CreateTestRequestContext(rc_mode_, rc_cache_path_);
+
+    // Create the browser.
+    CreateBrowser(kPopupNavPageUrl, request_context);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
+                     CefRefPtr<CefFrame> frame,
+                     const CefString& target_url,
+                     const CefString& target_frame_name,
+                     cef_window_open_disposition_t target_disposition,
+                     bool user_gesture,
+                     const CefPopupFeatures& popupFeatures,
+                     CefWindowInfo& windowInfo,
+                     CefRefPtr<CefClient>& client,
+                     CefBrowserSettings& settings,
+                     CefRefPtr<CefDictionaryValue>& extra_info,
+                     bool* no_javascript_access) override {
+    EXPECT_FALSE(got_on_before_popup_);
+    got_on_before_popup_.yes();
+
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+    EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
+    EXPECT_STREQ(kPopupNavPageUrl, frame->GetURL().ToString().c_str());
+    EXPECT_STREQ(kPopupNavPopupUrl, target_url.ToString().c_str());
+    EXPECT_STREQ(kPopupNavPopupName, target_frame_name.ToString().c_str());
+    EXPECT_EQ(WOD_NEW_FOREGROUND_TAB, target_disposition);
+    EXPECT_FALSE(user_gesture);
+    EXPECT_FALSE(*no_javascript_access);
+
+    if (mode_ == DESTROY_PARENT_DURING_CREATION ||
+        mode_ == DESTROY_PARENT_DURING_CREATION_FORCE) {
+      // Destroy the main (parent) browser while popup creation is pending.
+      CloseBrowser(browser, mode_ == DESTROY_PARENT_DURING_CREATION_FORCE);
+    }
+
+    return (mode_ == DENY);  // Return true to cancel the popup.
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    if (browser->IsPopup() && (mode_ == DESTROY_PARENT_AFTER_CREATION ||
+                               mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE)) {
+      // Destroy the main (parent) browser immediately after the popup is
+      // created.
+      CloseBrowser(GetBrowser(), mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE);
+    }
+
+    if (mode_ == NAVIGATE_AFTER_CREATION && browser->IsPopup()) {
+      // Navigate to the 2nd popup URL instead of the 1st popup URL.
+      browser->GetMainFrame()->LoadURL(kPopupNavPopupUrl2);
+    }
+  }
+
+  void OnLoadStart(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   TransitionType transition_type) override {
+    const std::string& url = frame->GetURL();
+    if (url == kPopupNavPageUrl) {
+      EXPECT_FALSE(got_load_start_);
+      got_load_start_.yes();
+    } else if (url == kPopupNavPopupUrl) {
+      EXPECT_FALSE(got_popup_load_start_);
+      got_popup_load_start_.yes();
+    } else if (url == kPopupNavPopupUrl2) {
+      EXPECT_FALSE(got_popup_load_start2_);
+      got_popup_load_start2_.yes();
+    }
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    if (failedUrl == kPopupNavPageUrl) {
+      EXPECT_FALSE(got_load_error_);
+      got_load_error_.yes();
+    } else if (failedUrl == kPopupNavPopupUrl) {
+      EXPECT_FALSE(got_popup_load_error_);
+      got_popup_load_error_.yes();
+    } else if (failedUrl == kPopupNavPopupUrl2) {
+      EXPECT_FALSE(got_popup_load_error2_);
+      got_popup_load_error2_.yes();
+    }
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    const std::string& url = frame->GetURL();
+    if (url == kPopupNavPageUrl) {
+      EXPECT_FALSE(got_load_end_);
+      got_load_end_.yes();
+
+      frame->ExecuteJavaScript("doPopup()", kPopupNavPageUrl, 0);
+
+      if (mode_ == DESTROY_PARENT_BEFORE_CREATION ||
+          mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE) {
+        // Destroy the main (parent) browser immediately before the popup is
+        // created.
+        CloseBrowser(browser, mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE);
+      }
+
+      if (mode_ == DENY) {
+        // Wait a bit to make sure the popup window isn't created.
+        CefPostDelayedTask(
+            TID_UI, base::Bind(&PopupNavTestHandler::DestroyTest, this), 200);
+      }
+    } else if (url == kPopupNavPopupUrl) {
+      EXPECT_FALSE(got_popup_load_end_);
+      got_popup_load_end_.yes();
+
+      if (mode_ == ALLOW_CLOSE_POPUP_FIRST) {
+        // Close the popup browser first.
+        CloseBrowser(browser, false);
+      } else if (mode_ == ALLOW_CLOSE_POPUP_LAST) {
+        // Close the main browser first.
+        CloseBrowser(GetBrowser(), false);
+      } else if (mode_ != NAVIGATE_AFTER_CREATION) {
+        EXPECT_FALSE(true);  // Not reached.
+      }
+    } else if (url == kPopupNavPopupUrl2) {
+      EXPECT_FALSE(got_popup_load_end2_);
+      got_popup_load_end2_.yes();
+
+      if (mode_ == NAVIGATE_AFTER_CREATION) {
+        // Close the popup browser first.
+        CloseBrowser(browser, false);
+      } else {
+        EXPECT_FALSE(true);  // Not reached.
+      }
+    } else {
+      EXPECT_FALSE(true);  // Not reached.
+    }
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnBeforeClose(browser);
+
+    bool destroy_test = false;
+    if (mode_ == ALLOW_CLOSE_POPUP_FIRST || mode_ == NAVIGATE_AFTER_CREATION) {
+      // Destroy the test after the popup browser closes.
+      if (browser->IsPopup())
+        destroy_test = true;
+    } else if (mode_ == ALLOW_CLOSE_POPUP_LAST ||
+               mode_ == DESTROY_PARENT_BEFORE_CREATION ||
+               mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE ||
+               mode_ == DESTROY_PARENT_DURING_CREATION ||
+               mode_ == DESTROY_PARENT_DURING_CREATION_FORCE ||
+               mode_ == DESTROY_PARENT_AFTER_CREATION ||
+               mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE) {
+      // Destroy the test after the main browser closes.
+      if (!browser->IsPopup())
+        destroy_test = true;
+    }
+
+    if (destroy_test) {
+      CefPostTask(TID_UI, base::Bind(&PopupNavTestHandler::DestroyTest, this));
+    }
+  }
+
+ private:
+  void DestroyTest() override {
+    EXPECT_TRUE(got_load_start_);
+    EXPECT_FALSE(got_load_error_);
+    EXPECT_TRUE(got_load_end_);
+
+    // OnBeforePopup may come before or after browser destruction with the
+    // DESTROY_PARENT_BEFORE_CREATION* tests.
+    if (mode_ != DESTROY_PARENT_BEFORE_CREATION &&
+        mode_ != DESTROY_PARENT_BEFORE_CREATION_FORCE) {
+      EXPECT_TRUE(got_on_before_popup_);
+    }
+
+    if (mode_ == ALLOW_CLOSE_POPUP_FIRST || mode_ == ALLOW_CLOSE_POPUP_LAST) {
+      EXPECT_TRUE(got_popup_load_start_);
+      EXPECT_FALSE(got_popup_load_error_);
+      EXPECT_TRUE(got_popup_load_end_);
+      EXPECT_FALSE(got_popup_load_start2_);
+      EXPECT_FALSE(got_popup_load_error2_);
+      EXPECT_FALSE(got_popup_load_end2_);
+    } else if (mode_ == DENY || mode_ == DESTROY_PARENT_BEFORE_CREATION ||
+               mode_ == DESTROY_PARENT_BEFORE_CREATION_FORCE ||
+               mode_ == DESTROY_PARENT_DURING_CREATION ||
+               mode_ == DESTROY_PARENT_DURING_CREATION_FORCE ||
+               mode_ == DESTROY_PARENT_AFTER_CREATION ||
+               mode_ == DESTROY_PARENT_AFTER_CREATION_FORCE) {
+      EXPECT_FALSE(got_popup_load_start_);
+      EXPECT_FALSE(got_popup_load_error_);
+      EXPECT_FALSE(got_popup_load_end_);
+      EXPECT_FALSE(got_popup_load_start2_);
+      EXPECT_FALSE(got_popup_load_error2_);
+      EXPECT_FALSE(got_popup_load_end2_);
+    } else if (mode_ == NAVIGATE_AFTER_CREATION) {
+      EXPECT_FALSE(got_popup_load_start_);
+
+      // With browser-side navigation we will never actually begin the
+      // navigation to the 1st popup URL, so there will be no load error.
+      EXPECT_FALSE(got_popup_load_error_);
+
+      EXPECT_FALSE(got_popup_load_end_);
+      EXPECT_TRUE(got_popup_load_start2_);
+      EXPECT_FALSE(got_popup_load_error2_);
+      EXPECT_TRUE(got_popup_load_end2_);
+    }
+
+    // Will trigger destruction of all remaining browsers.
+    TestHandler::DestroyTest();
+  }
+
+  const TestMode mode_;
+  const TestRequestContextMode rc_mode_;
+  const std::string rc_cache_path_;
+
+  TrackCallback got_on_before_popup_;
+  TrackCallback got_load_start_;
+  TrackCallback got_load_error_;
+  TrackCallback got_load_end_;
+  TrackCallback got_popup_load_start_;
+  TrackCallback got_popup_load_error_;
+  TrackCallback got_popup_load_end_;
+  TrackCallback got_popup_load_start2_;
+  TrackCallback got_popup_load_error2_;
+  TrackCallback got_popup_load_end2_;
+
+  IMPLEMENT_REFCOUNTING(PopupNavTestHandler);
+};
+
+}  // namespace
+#define POPUP_TEST_GROUP(test_name, test_mode)                     \
+  RC_TEST_GROUP_IN_MEMORY(RequestContextTest, PopupNav##test_name, \
+                          PopupNavTestHandler, test_mode)
+
+// Test allowing popups and closing the popup browser first.
+POPUP_TEST_GROUP(AllowClosePopupFirst, ALLOW_CLOSE_POPUP_FIRST)
+
+// Test allowing popups and closing the main browser first to verify
+// that internal objects are tracked correctly (see issue #2162).
+POPUP_TEST_GROUP(AllowClosePopupLast, ALLOW_CLOSE_POPUP_LAST)
+
+// Test denying popups.
+POPUP_TEST_GROUP(Deny, DENY)
+
+// Test navigation to a different origin after popup creation to
+// verify that internal objects are tracked correctly (see issue
+// #1392).
+POPUP_TEST_GROUP(NavigateAfterCreation, NAVIGATE_AFTER_CREATION)
+
+// Test destroying the parent browser during or immediately after
+// popup creation to verify that internal objects are tracked
+// correctly (see issue #2041).
+POPUP_TEST_GROUP(DestroyParentBeforeCreation, DESTROY_PARENT_BEFORE_CREATION)
+POPUP_TEST_GROUP(DestroyParentBeforeCreationForce,
+                 DESTROY_PARENT_BEFORE_CREATION_FORCE)
+POPUP_TEST_GROUP(DestroyParentDuringCreation, DESTROY_PARENT_DURING_CREATION)
+POPUP_TEST_GROUP(DestroyParentDuringCreationForce,
+                 DESTROY_PARENT_DURING_CREATION_FORCE)
+POPUP_TEST_GROUP(DestroyParentAfterCreation, DESTROY_PARENT_AFTER_CREATION)
+POPUP_TEST_GROUP(DestroyParentAfterCreationForce,
+                 DESTROY_PARENT_AFTER_CREATION_FORCE)
+
+namespace {
+
+const char kResolveOrigin[] = "http://www.google.com";
+
+class MethodTestHandler : public TestHandler {
+ public:
+  enum Method {
+    METHOD_CLEAR_CERTIFICATE_EXCEPTIONS,
+    METHOD_CLOSE_ALL_CONNECTIONS,
+    METHOD_RESOLVE_HOST,
+  };
+
+  class CompletionCallback : public CefCompletionCallback,
+                             public CefResolveCallback {
+   public:
+    CompletionCallback(MethodTestHandler* test_handler,
+                       CefRefPtr<CefBrowser> browser)
+        : test_handler_(test_handler), browser_(browser) {}
+
+    ~CompletionCallback() override {
+      // OnComplete should be executed.
+      EXPECT_FALSE(test_handler_);
+    }
+
+    void OnComplete() override {
+      EXPECT_UI_THREAD();
+
+      // OnComplete should be executed only one time.
+      EXPECT_TRUE(test_handler_);
+      test_handler_->OnCompleteCallback(browser_);
+      test_handler_ = nullptr;
+      browser_ = nullptr;
+    }
+
+    void OnResolveCompleted(
+        cef_errorcode_t result,
+        const std::vector<CefString>& resolved_ips) override {
+      EXPECT_EQ(ERR_NONE, result);
+      EXPECT_TRUE(!resolved_ips.empty());
+      OnComplete();
+    }
+
+   private:
+    MethodTestHandler* test_handler_;
+    CefRefPtr<CefBrowser> browser_;
+
+    IMPLEMENT_REFCOUNTING(CompletionCallback);
+  };
+
+  MethodTestHandler(bool global_context, Method method)
+      : global_context_(global_context), method_(method) {}
+
+  void RunTest() override {
+    const char kUrl[] = "http://tests/method.html";
+
+    AddResource(kUrl, "<html><body>Method</body></html>", "text/html");
+
+    CefRefPtr<CefRequestContext> request_context;
+    if (!global_context_) {
+      CefRequestContextSettings settings;
+      request_context = CefRequestContext::CreateContext(settings, nullptr);
+    }
+
+    CreateBrowser(kUrl, request_context);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    CefRefPtr<CefRequestContext> context =
+        browser->GetHost()->GetRequestContext();
+    CefRefPtr<CompletionCallback> callback =
+        new CompletionCallback(this, browser);
+    if (method_ == METHOD_CLEAR_CERTIFICATE_EXCEPTIONS)
+      context->ClearCertificateExceptions(callback);
+    else if (method_ == METHOD_CLOSE_ALL_CONNECTIONS)
+      context->CloseAllConnections(callback);
+    else if (method_ == METHOD_RESOLVE_HOST)
+      context->ResolveHost(kResolveOrigin, callback);
+  }
+
+  void OnCompleteCallback(CefRefPtr<CefBrowser> browser) {
+    EXPECT_UI_THREAD();
+    EXPECT_FALSE(got_completion_callback_);
+    got_completion_callback_.yes();
+
+    DestroyTest();
+  }
+
+ private:
+  void DestroyTest() override {
+    EXPECT_TRUE(got_completion_callback_);
+    TestHandler::DestroyTest();
+  }
+
+  const bool global_context_;
+  const Method method_;
+
+  TrackCallback got_completion_callback_;
+
+  IMPLEMENT_REFCOUNTING(MethodTestHandler);
+};
+
+}  // namespace
+
+// Test CefRequestContext::ClearCertificateExceptions with the global
+// context.
+TEST(RequestContextTest, ClearCertificateExceptionsGlobal) {
+  CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
+      true, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test CefRequestContext::ClearCertificateExceptions with a custom
+// context.
+TEST(RequestContextTest, ClearCertificateExceptionsCustom) {
+  CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
+      false, MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test CefRequestContext::CloseAllConnections with the global
+// context.
+TEST(RequestContextTest, CloseAllConnectionsGlobal) {
+  CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
+      true, MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test CefRequestContext::CloseAllConnections with a custom context.
+TEST(RequestContextTest, CloseAllConnectionsCustom) {
+  CefRefPtr<MethodTestHandler> handler = new MethodTestHandler(
+      false, MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test CefRequestContext::ResolveHost with the global context.
+TEST(RequestContextTest, ResolveHostGlobal) {
+  CefRefPtr<MethodTestHandler> handler =
+      new MethodTestHandler(true, MethodTestHandler::METHOD_RESOLVE_HOST);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test CefRequestContext::ResolveHost with a custom context.
+TEST(RequestContextTest, ResolveHostCustom) {
+  CefRefPtr<MethodTestHandler> handler =
+      new MethodTestHandler(false, MethodTestHandler::METHOD_RESOLVE_HOST);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/request_handler_unittest.cc b/src/tests/ceftests/request_handler_unittest.cc
new file mode 100644
index 0000000..37aa65b
--- /dev/null
+++ b/src/tests/ceftests/request_handler_unittest.cc
@@ -0,0 +1,521 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+#include <cmath>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_cookie.h"
+#include "include/cef_request_context_handler.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppBrowser;
+using client::ClientAppRenderer;
+
+namespace {
+
+enum NetNotifyTestType {
+  NNTT_NONE = 0,
+  NNTT_NORMAL,
+  NNTT_DELAYED_RENDERER,
+  NNTT_DELAYED_BROWSER,
+};
+
+const char kNetNotifyOrigin1[] = "http://tests-netnotify1/";
+const char kNetNotifyOrigin2[] = "http://tests-netnotify2/";
+const char kNetNotifyMsg[] = "RequestHandlerTest.NetNotify";
+const char kNetNotifyTestCmdKey[] = "rh-net-notify-test";
+
+// Browser side.
+class NetNotifyTestHandler : public TestHandler {
+ public:
+  NetNotifyTestHandler(CompletionState* completion_state,
+                       NetNotifyTestType test_type,
+                       bool same_origin)
+      : TestHandler(completion_state),
+        test_type_(test_type),
+        same_origin_(same_origin) {}
+
+  void SetupTest() override {
+    std::stringstream ss;
+    ss << kNetNotifyOrigin1 << "nav1.html?t=" << test_type_;
+    url1_ = ss.str();
+    ss.str("");
+    ss << (same_origin_ ? kNetNotifyOrigin1 : kNetNotifyOrigin2)
+       << "nav2.html?t=" << test_type_;
+    url2_ = ss.str();
+
+    const std::string& resource1 =
+        "<html>"
+        "<head><script>document.cookie='name1=value1';</script></head>"
+        "<body>Nav1</body>"
+        "</html>";
+    response_length1_ = static_cast<int64>(resource1.size());
+    AddResource(url1_, resource1, "text/html");
+
+    const std::string& resource2 =
+        "<html>"
+        "<head><script>document.cookie='name2=value2';</script></head>"
+        "<body>Nav2</body>"
+        "</html>";
+    response_length2_ = static_cast<int64>(resource2.size());
+    AddResource(url2_, resource2, "text/html");
+
+    // Create the request context that will use an in-memory cache.
+    CefRequestContextSettings settings;
+    CefRefPtr<CefRequestContext> request_context =
+        CefRequestContext::CreateContext(settings, nullptr);
+    cookie_manager_ = request_context->GetCookieManager(nullptr);
+
+    CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
+    extra_info->SetBool(kNetNotifyTestCmdKey, true);
+
+    // Create browser that loads the 1st URL.
+    CreateBrowser(url1_, request_context, extra_info);
+  }
+
+  void RunTest() override {
+    // Navigate to the 2nd URL.
+    GetBrowser()->GetMainFrame()->LoadURL(url2_);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+
+    const std::string& url = request->GetURL();
+    if (url.find(url1_) == 0)
+      got_before_resource_load1_.yes();
+    else if (url.find(url2_) == 0)
+      got_before_resource_load2_.yes();
+    else
+      EXPECT_TRUE(false);  // Not reached
+
+    return RV_CONTINUE;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+
+    const std::string& url = request->GetURL();
+    if (url.find(url1_) == 0)
+      got_get_resource_handler1_.yes();
+    else if (url.find(url2_) == 0)
+      got_get_resource_handler2_.yes();
+    else
+      EXPECT_TRUE(false);  // Not reached
+
+    return TestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+  void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefRequest> request,
+                              CefRefPtr<CefResponse> response,
+                              URLRequestStatus status,
+                              int64 received_content_length) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+    EXPECT_EQ(UR_SUCCESS, status);
+
+    const std::string& url = request->GetURL();
+    if (url.find(url1_) == 0) {
+      got_resource_load_complete1_.yes();
+      EXPECT_EQ(response_length1_, received_content_length);
+    } else if (url.find(url2_) == 0) {
+      got_resource_load_complete2_.yes();
+      EXPECT_EQ(response_length2_, received_content_length);
+    } else {
+      EXPECT_TRUE(false);  // Not reached
+    }
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    std::string url = request->GetURL();
+
+    // Check if the load has already been delayed.
+    bool delay_loaded = (url.find("delayed=true") != std::string::npos);
+
+    if (url.find(url1_) == 0) {
+      got_before_browse1_.yes();
+      EXPECT_FALSE(delay_loaded);
+    } else if (url.find(url2_) == 0) {
+      got_before_browse2_.yes();
+      if (delay_loaded) {
+        got_before_browse2_delayed_.yes();
+      } else if (test_type_ == NNTT_DELAYED_RENDERER ||
+                 test_type_ == NNTT_DELAYED_BROWSER) {
+        got_before_browse2_will_delay_.yes();
+
+        // Navigating cross-origin from the browser process will cause a new
+        // render process to be created. We therefore need some information in
+        // the request itself to tell us that the navigation has already been
+        // delayed.
+        // Navigating cross-origin from the renderer process will cause the
+        // process to be terminated with "bad IPC message" reason
+        // INVALID_INITIATOR_ORIGIN (213).
+        url += "&delayed=true";
+
+        if (test_type_ == NNTT_DELAYED_RENDERER) {
+          // Load the URL from the render process.
+          CefRefPtr<CefProcessMessage> message =
+              CefProcessMessage::Create(kNetNotifyMsg);
+          CefRefPtr<CefListValue> args = message->GetArgumentList();
+          args->SetInt(0, test_type_);
+          args->SetString(1, url);
+          frame->SendProcessMessage(PID_RENDERER, message);
+        } else {
+          // Load the URL from the browser process.
+          frame->LoadURL(url);
+        }
+
+        // Cancel the load.
+        return true;
+      }
+    } else {
+      EXPECT_TRUE(false);  // Not reached
+    }
+
+    // Allow the load to continue.
+    return false;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    const std::string& url = frame->GetURL();
+    if (url.find(url1_) == 0) {
+      got_load_end1_.yes();
+      SetupCompleteIfDone();
+    } else if (url.find(url2_) == 0) {
+      got_load_end2_.yes();
+      FinishTestIfDone();
+    } else {
+      EXPECT_TRUE(false);  // Not reached
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName().ToString() == kNetNotifyMsg) {
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_TRUE(args.get());
+
+      std::string url = args->GetString(0);
+      if (url.find(url1_) == 0) {
+        got_process_message1_.yes();
+        SetupCompleteIfDone();
+      } else if (url.find(url2_) == 0) {
+        got_process_message2_.yes();
+        FinishTestIfDone();
+      } else {
+        EXPECT_TRUE(false);  // Not reached
+      }
+
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                 TerminationStatus status) override {
+    got_process_terminated_ct_++;
+
+    // Termination is expected for cross-origin requests initiated from the
+    // renderer process.
+    if (!(test_type_ == NNTT_DELAYED_RENDERER && !same_origin_)) {
+      TestHandler::OnRenderProcessTerminated(browser, status);
+    }
+
+    FinishTest();
+  }
+
+ protected:
+  void SetupCompleteIfDone() {
+    if (got_load_end1_ && got_process_message1_)
+      SetupComplete();
+  }
+
+  void FinishTestIfDone() {
+    if (got_load_end2_ && got_process_message2_)
+      FinishTest();
+  }
+
+  void FinishTest() {
+    // Verify that cookies were set correctly.
+    class TestVisitor : public CefCookieVisitor {
+     public:
+      explicit TestVisitor(NetNotifyTestHandler* handler) : handler_(handler) {}
+      ~TestVisitor() override {
+        // Destroy the test.
+        CefPostTask(TID_UI,
+                    base::Bind(&NetNotifyTestHandler::DestroyTest, handler_));
+      }
+
+      bool Visit(const CefCookie& cookie,
+                 int count,
+                 int total,
+                 bool& deleteCookie) override {
+        const std::string& name = CefString(&cookie.name);
+        const std::string& value = CefString(&cookie.value);
+        if (name == "name1" && value == "value1") {
+          handler_->got_cookie1_.yes();
+          deleteCookie = true;
+        } else if (name == "name2" && value == "value2") {
+          handler_->got_cookie2_.yes();
+          deleteCookie = true;
+        }
+        return true;
+      }
+
+     private:
+      NetNotifyTestHandler* handler_;
+      IMPLEMENT_REFCOUNTING(TestVisitor);
+    };
+
+    cookie_manager_->VisitAllCookies(new TestVisitor(this));
+  }
+
+  void DestroyTest() override {
+    int browser_id = GetBrowser()->GetIdentifier();
+
+    // Verify test expectations.
+    EXPECT_TRUE(got_before_browse1_) << " browser " << browser_id;
+    EXPECT_TRUE(got_load_end1_) << " browser " << browser_id;
+    EXPECT_TRUE(got_before_resource_load1_) << " browser " << browser_id;
+    EXPECT_TRUE(got_get_resource_handler1_) << " browser " << browser_id;
+    EXPECT_TRUE(got_resource_load_complete1_) << " browser " << browser_id;
+    EXPECT_TRUE(got_cookie1_) << " browser " << browser_id;
+    EXPECT_TRUE(got_process_message1_) << " browser " << browser_id;
+    EXPECT_TRUE(got_before_browse2_) << " browser " << browser_id;
+
+    if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) {
+      EXPECT_EQ(1, got_process_terminated_ct_) << " browser " << browser_id;
+      EXPECT_FALSE(got_load_end2_) << " browser " << browser_id;
+      EXPECT_FALSE(got_before_resource_load2_) << " browser " << browser_id;
+      EXPECT_FALSE(got_get_resource_handler2_) << " browser " << browser_id;
+      EXPECT_FALSE(got_resource_load_complete2_) << " browser " << browser_id;
+      EXPECT_FALSE(got_cookie2_) << " browser " << browser_id;
+      EXPECT_FALSE(got_process_message2_) << " browser " << browser_id;
+    } else {
+      EXPECT_EQ(0, got_process_terminated_ct_) << " browser " << browser_id;
+      EXPECT_TRUE(got_load_end2_) << " browser " << browser_id;
+      EXPECT_TRUE(got_before_resource_load2_) << " browser " << browser_id;
+      EXPECT_TRUE(got_get_resource_handler2_) << " browser " << browser_id;
+      EXPECT_TRUE(got_resource_load_complete2_) << " browser " << browser_id;
+      EXPECT_TRUE(got_cookie2_) << " browser " << browser_id;
+      EXPECT_TRUE(got_process_message2_) << " browser " << browser_id;
+    }
+
+    if (test_type_ == NNTT_DELAYED_RENDERER ||
+        test_type_ == NNTT_DELAYED_BROWSER) {
+      EXPECT_TRUE(got_before_browse2_will_delay_) << " browser " << browser_id;
+      if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) {
+        EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id;
+      } else {
+        EXPECT_TRUE(got_before_browse2_delayed_) << " browser " << browser_id;
+      }
+    } else {
+      EXPECT_FALSE(got_before_browse2_will_delay_) << " browser " << browser_id;
+      EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id;
+    }
+
+    cookie_manager_ = nullptr;
+
+    TestHandler::DestroyTest();
+  }
+
+  NetNotifyTestType test_type_;
+  bool same_origin_;
+  std::string url1_;
+  std::string url2_;
+
+  CefRefPtr<CefCookieManager> cookie_manager_;
+
+  TrackCallback got_before_browse1_;
+  TrackCallback got_load_end1_;
+  TrackCallback got_before_resource_load1_;
+  TrackCallback got_get_resource_handler1_;
+  TrackCallback got_resource_load_complete1_;
+  TrackCallback got_cookie1_;
+  TrackCallback got_process_message1_;
+  TrackCallback got_before_browse2_;
+  TrackCallback got_load_end2_;
+  TrackCallback got_before_resource_load2_;
+  TrackCallback got_get_resource_handler2_;
+  TrackCallback got_resource_load_complete2_;
+  TrackCallback got_cookie2_;
+  TrackCallback got_process_message2_;
+  TrackCallback got_before_browse2_will_delay_;
+  TrackCallback got_before_browse2_delayed_;
+  int got_process_terminated_ct_ = 0;
+
+  int64 response_length1_;
+  int64 response_length2_;
+
+  IMPLEMENT_REFCOUNTING(NetNotifyTestHandler);
+};
+
+// Renderer side.
+class NetNotifyRendererTest : public ClientAppRenderer::Delegate,
+                              public CefLoadHandler {
+ public:
+  NetNotifyRendererTest() : run_test_(false) {}
+
+  void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override {
+    run_test_ = extra_info->HasKey(kNetNotifyTestCmdKey);
+  }
+
+  CefRefPtr<CefLoadHandler> GetLoadHandler(
+      CefRefPtr<ClientAppRenderer> app) override {
+    if (run_test_)
+      return this;
+    return nullptr;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (!run_test_)
+      return;
+
+    const std::string& url = frame->GetURL();
+
+    // Continue in the browser process.
+    CefRefPtr<CefProcessMessage> message =
+        CefProcessMessage::Create(kNetNotifyMsg);
+    CefRefPtr<CefListValue> args = message->GetArgumentList();
+    args->SetString(0, url);
+    frame->SendProcessMessage(PID_BROWSER, message);
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName().ToString() == kNetNotifyMsg) {
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+      EXPECT_TRUE(args.get());
+
+      NetNotifyTestType test_type =
+          static_cast<NetNotifyTestType>(args->GetInt(0));
+      EXPECT_EQ(test_type, NNTT_DELAYED_RENDERER);
+
+      const std::string& url = args->GetString(1);
+
+      // Load the URL from the render process.
+      frame->LoadURL(url);
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+ private:
+  bool run_test_;
+
+  IMPLEMENT_REFCOUNTING(NetNotifyRendererTest);
+};
+
+void RunNetNotifyTest(NetNotifyTestType test_type,
+                      bool same_origin,
+                      size_t count = 3U) {
+  TestHandler::CompletionState completion_state(static_cast<int>(count));
+  TestHandler::Collection collection(&completion_state);
+
+  std::vector<CefRefPtr<NetNotifyTestHandler>> handlers;
+  for (size_t i = 0U; i < count; ++i) {
+    CefRefPtr<NetNotifyTestHandler> handler =
+        new NetNotifyTestHandler(&completion_state, test_type, same_origin);
+    collection.AddTestHandler(handler);
+    handlers.push_back(handler);
+  }
+
+  collection.ExecuteTests();
+
+  while (!handlers.empty()) {
+    auto handler = handlers.front();
+    handlers.erase(handlers.begin());
+    ReleaseAndWaitForDestructor(handler);
+  }
+}
+
+}  // namespace
+
+// Verify network notifications for multiple browsers existing simultaniously.
+// URL loading is from the same origin and is not delayed.
+TEST(RequestHandlerTest, NotificationsSameOriginDirect) {
+  RunNetNotifyTest(NNTT_NORMAL, true);
+}
+
+// Verify network notifications for multiple browsers existing simultaniously.
+// URL loading is from the same origin and is continued asynchronously from the
+// render process.
+TEST(RequestHandlerTest, NotificationsSameOriginDelayedRenderer) {
+  RunNetNotifyTest(NNTT_DELAYED_RENDERER, true);
+}
+
+// Verify network notifications for multiple browsers existing simultaniously.
+// URL loading is from the same origin and is continued asynchronously from the
+// browser process.
+TEST(RequestHandlerTest, NotificationsSameOriginDelayedBrowser) {
+  RunNetNotifyTest(NNTT_DELAYED_BROWSER, true);
+}
+
+// Verify network notifications for multiple browsers existing simultaniously.
+// URL loading is from a different origin and is not delayed.
+TEST(RequestHandlerTest, NotificationsCrossOriginDirect) {
+  RunNetNotifyTest(NNTT_NORMAL, false);
+}
+
+// Verify network notifications for multiple browsers existing simultaniously.
+// URL loading is from a different origin and is continued asynchronously from
+// the render process.
+TEST(RequestHandlerTest, NotificationsCrossOriginDelayedRenderer) {
+  RunNetNotifyTest(NNTT_DELAYED_RENDERER, false);
+}
+
+// Verify network notifications for multiple browsers existing simultaniously.
+// URL loading is from a different origin and is continued asynchronously from
+// the browser process.
+TEST(RequestHandlerTest, NotificationsCrossOriginDelayedBrowser) {
+  RunNetNotifyTest(NNTT_DELAYED_BROWSER, false);
+}
+
+// Entry point for creating request handler renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateRequestHandlerRendererTests(
+    ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new NetNotifyRendererTest);
+}
diff --git a/src/tests/ceftests/request_unittest.cc b/src/tests/ceftests/request_unittest.cc
new file mode 100644
index 0000000..653c032
--- /dev/null
+++ b/src/tests/ceftests/request_unittest.cc
@@ -0,0 +1,622 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <map>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_request.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppRenderer;
+
+// Verify Set/Get methods for CefRequest, CefPostData and CefPostDataElement.
+TEST(RequestTest, SetGet) {
+  // CefRequest CreateRequest
+  CefRefPtr<CefRequest> request(CefRequest::Create());
+  EXPECT_TRUE(request.get() != nullptr);
+  EXPECT_EQ(0U, request->GetIdentifier());
+
+  CefString url = "http://tests.com/run.html";
+  CefString method = "POST";
+  CefRequest::HeaderMap setHeaders, getHeaders;
+  setHeaders.insert(std::make_pair("HeaderA", "ValueA"));
+  setHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+
+  // CefPostData CreatePostData
+  CefRefPtr<CefPostData> postData(CefPostData::Create());
+  EXPECT_TRUE(postData.get() != nullptr);
+
+  // CefPostDataElement CreatePostDataElement
+  CefRefPtr<CefPostDataElement> element1(CefPostDataElement::Create());
+  EXPECT_TRUE(element1.get() != nullptr);
+  CefRefPtr<CefPostDataElement> element2(CefPostDataElement::Create());
+  EXPECT_TRUE(element2.get() != nullptr);
+
+  // CefPostDataElement SetToFile
+  CefString file = "c:\\path\\to\\file.ext";
+  element1->SetToFile(file);
+  EXPECT_EQ(PDE_TYPE_FILE, element1->GetType());
+  EXPECT_EQ(file, element1->GetFile());
+
+  // CefPostDataElement SetToBytes
+  char bytes[] = "Test Bytes";
+  element2->SetToBytes(sizeof(bytes), bytes);
+  EXPECT_EQ(PDE_TYPE_BYTES, element2->GetType());
+  EXPECT_EQ(sizeof(bytes), element2->GetBytesCount());
+  char bytesOut[sizeof(bytes)];
+  element2->GetBytes(sizeof(bytes), bytesOut);
+  EXPECT_TRUE(!memcmp(bytes, bytesOut, sizeof(bytes)));
+
+  // CefPostData AddElement
+  postData->AddElement(element1);
+  postData->AddElement(element2);
+  EXPECT_EQ((size_t)2, postData->GetElementCount());
+
+  // CefPostData RemoveElement
+  postData->RemoveElement(element1);
+  EXPECT_EQ((size_t)1, postData->GetElementCount());
+
+  // CefPostData RemoveElements
+  postData->RemoveElements();
+  EXPECT_EQ((size_t)0, postData->GetElementCount());
+
+  postData->AddElement(element1);
+  postData->AddElement(element2);
+  EXPECT_EQ((size_t)2, postData->GetElementCount());
+  CefPostData::ElementVector elements;
+  postData->GetElements(elements);
+  CefPostData::ElementVector::const_iterator it = elements.begin();
+  for (size_t i = 0; it != elements.end(); ++it, ++i) {
+    if (i == 0)
+      TestPostDataElementEqual(element1, (*it).get());
+    else if (i == 1)
+      TestPostDataElementEqual(element2, (*it).get());
+  }
+
+  // CefRequest SetURL
+  request->SetURL(url);
+  EXPECT_EQ(url, request->GetURL());
+
+  // CefRequest SetMethod
+  request->SetMethod(method);
+  EXPECT_EQ(method, request->GetMethod());
+
+  // CefRequest SetReferrer
+  CefString referrer = "http://tests.com/referrer.html";
+  CefRequest::ReferrerPolicy policy = REFERRER_POLICY_ORIGIN;
+  request->SetReferrer(referrer, policy);
+  EXPECT_STREQ("http://tests.com/",
+               request->GetReferrerURL().ToString().c_str());
+  EXPECT_EQ(policy, request->GetReferrerPolicy());
+
+  // CefRequest SetHeaderMap
+  request->SetHeaderMap(setHeaders);
+  request->GetHeaderMap(getHeaders);
+  TestMapEqual(setHeaders, getHeaders, false);
+  getHeaders.clear();
+
+  // CefRequest SetPostData
+  request->SetPostData(postData);
+  TestPostDataEqual(postData, request->GetPostData());
+
+  EXPECT_EQ(0U, request->GetIdentifier());
+
+  request = CefRequest::Create();
+  EXPECT_TRUE(request.get() != nullptr);
+  EXPECT_EQ(0U, request->GetIdentifier());
+
+  // CefRequest Set
+  request->Set(url, method, postData, setHeaders);
+  EXPECT_EQ(0U, request->GetIdentifier());
+  EXPECT_EQ(url, request->GetURL());
+  EXPECT_EQ(method, request->GetMethod());
+  request->GetHeaderMap(getHeaders);
+  TestMapEqual(setHeaders, getHeaders, false);
+  getHeaders.clear();
+  TestPostDataEqual(postData, request->GetPostData());
+}
+
+TEST(RequestTest, SetGetHeaderByName) {
+  CefRefPtr<CefRequest> request(CefRequest::Create());
+  EXPECT_TRUE(request.get() != nullptr);
+
+  CefRequest::HeaderMap headers, expectedHeaders;
+
+  request->SetHeaderByName("HeaderA", "ValueA", false);
+  request->SetHeaderByName("HeaderB", "ValueB", false);
+
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueA"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+
+  // Case insensitive retrieval.
+  EXPECT_STREQ("ValueA",
+               request->GetHeaderByName("headera").ToString().c_str());
+  EXPECT_STREQ("ValueB",
+               request->GetHeaderByName("headerb").ToString().c_str());
+  EXPECT_STREQ("", request->GetHeaderByName("noexist").ToString().c_str());
+
+  request->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+
+  // Replace an existing value.
+  request->SetHeaderByName("HeaderA", "ValueANew", true);
+
+  expectedHeaders.clear();
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueANew"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+
+  // Case insensitive retrieval.
+  EXPECT_STREQ("ValueANew",
+               request->GetHeaderByName("headerA").ToString().c_str());
+
+  request->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+
+  // Header with multiple values.
+  expectedHeaders.clear();
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueA1"));
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueA2"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+  request->SetHeaderMap(expectedHeaders);
+
+  // When there are multiple values only the first is returned.
+  EXPECT_STREQ("ValueA1",
+               request->GetHeaderByName("headera").ToString().c_str());
+
+  // Don't overwrite the value.
+  request->SetHeaderByName("HeaderA", "ValueANew", false);
+
+  request->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+
+  // Overwrite the value (remove the duplicates).
+  request->SetHeaderByName("HeaderA", "ValueANew", true);
+
+  expectedHeaders.clear();
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueANew"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+
+  request->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+}
+
+namespace {
+
+const char kTestUrl[] = "http://tests.com/run.html";
+
+void CreateRequest(CefRefPtr<CefRequest>& request) {
+  request = CefRequest::Create();
+  EXPECT_TRUE(request.get() != nullptr);
+
+  request->SetURL(kTestUrl);
+  request->SetMethod("POST");
+
+  request->SetReferrer("http://tests.com/main.html", REFERRER_POLICY_DEFAULT);
+
+  CefRequest::HeaderMap headers;
+  headers.insert(std::make_pair("HeaderA", "ValueA"));
+  headers.insert(std::make_pair("HeaderB", "ValueB"));
+  request->SetHeaderMap(headers);
+
+  CefRefPtr<CefPostData> postData(CefPostData::Create());
+  EXPECT_TRUE(postData.get() != nullptr);
+
+  CefRefPtr<CefPostDataElement> element1(CefPostDataElement::Create());
+  EXPECT_TRUE(element1.get() != nullptr);
+  char bytes[] = "Test Bytes";
+  element1->SetToBytes(sizeof(bytes), bytes);
+  postData->AddElement(element1);
+
+  request->SetPostData(postData);
+}
+
+class RequestSendRecvTestHandler : public TestHandler {
+ public:
+  RequestSendRecvTestHandler() : response_length_(0), request_id_(0U) {}
+
+  void RunTest() override {
+    // Create the test request.
+    CreateRequest(request_);
+
+    const std::string& resource = "<html><body>SendRecv Test</body></html>";
+    response_length_ = static_cast<int64>(resource.size());
+    AddResource(kTestUrl, resource, "text/html");
+
+    // Create the browser.
+    CreateBrowser("about:blank");
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    TestHandler::OnAfterCreated(browser);
+
+    // Load the test request.
+    browser->GetMainFrame()->LoadRequest(request_);
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_IO_THREAD();
+
+    request_id_ = request->GetIdentifier();
+    DCHECK_GT(request_id_, 0U);
+
+    TestRequest(request);
+    EXPECT_FALSE(request->IsReadOnly());
+
+    got_before_resource_load_.yes();
+
+    return RV_CONTINUE;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+
+    TestRequest(request);
+    EXPECT_TRUE(request->IsReadOnly());
+
+    got_resource_handler_.yes();
+
+    return TestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+  bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+
+    TestRequest(request);
+    EXPECT_FALSE(request->IsReadOnly());
+    TestResponse(response);
+    EXPECT_TRUE(response->IsReadOnly());
+
+    got_resource_response_.yes();
+
+    return false;
+  }
+
+  CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+
+    TestRequest(request);
+    EXPECT_TRUE(request->IsReadOnly());
+    TestResponse(response);
+    EXPECT_TRUE(response->IsReadOnly());
+
+    got_resource_response_filter_.yes();
+    return nullptr;
+  }
+
+  void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefRequest> request,
+                              CefRefPtr<CefResponse> response,
+                              URLRequestStatus status,
+                              int64 received_content_length) override {
+    EXPECT_IO_THREAD();
+
+    TestRequest(request);
+    EXPECT_TRUE(request->IsReadOnly());
+    TestResponse(response);
+    EXPECT_TRUE(response->IsReadOnly());
+    EXPECT_EQ(UR_SUCCESS, status);
+    EXPECT_EQ(response_length_, received_content_length);
+
+    got_resource_load_complete_.yes();
+
+    DestroyTest();
+  }
+
+ private:
+  void TestRequest(CefRefPtr<CefRequest> request) {
+    TestRequestEqual(request_, request, true);
+    EXPECT_EQ(request_id_, request->GetIdentifier());
+    EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
+    EXPECT_EQ(TT_FORM_SUBMIT, request->GetTransitionType());
+  }
+
+  void TestResponse(CefRefPtr<CefResponse> response) {
+    EXPECT_EQ(200, response->GetStatus());
+    EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str());
+    EXPECT_STREQ("text/html", response->GetMimeType().ToString().c_str());
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_before_resource_load_);
+    EXPECT_TRUE(got_resource_handler_);
+    EXPECT_TRUE(got_resource_response_);
+    EXPECT_TRUE(got_resource_response_filter_);
+    EXPECT_TRUE(got_resource_load_complete_);
+
+    TestHandler::DestroyTest();
+  }
+
+  CefRefPtr<CefRequest> request_;
+  int64 response_length_;
+  uint64 request_id_;
+
+  TrackCallback got_before_resource_load_;
+  TrackCallback got_resource_handler_;
+  TrackCallback got_resource_response_;
+  TrackCallback got_resource_response_filter_;
+  TrackCallback got_resource_load_complete_;
+
+  IMPLEMENT_REFCOUNTING(RequestSendRecvTestHandler);
+};
+
+}  // namespace
+
+// Verify send and recieve
+TEST(RequestTest, SendRecv) {
+  CefRefPtr<RequestSendRecvTestHandler> handler =
+      new RequestSendRecvTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kTypeTestOrigin[] = "http://tests-requesttt.com/";
+const cef_transition_type_t kTransitionExplicitLoad =
+    static_cast<cef_transition_type_t>(TT_EXPLICIT | TT_DIRECT_LOAD_FLAG);
+
+static struct TypeExpected {
+  const char* file;
+  bool navigation;  // True if this expectation represents a navigation.
+  cef_transition_type_t transition_type;
+  cef_resource_type_t resource_type;
+  int expected_count;
+} g_type_expected[] = {
+    // Initial main frame load due to browser creation.
+    {"main.html", true, kTransitionExplicitLoad, RT_MAIN_FRAME, 1},
+
+    // Sub frame load.
+    {"sub.html", true, TT_AUTO_SUBFRAME, RT_SUB_FRAME, 1},
+
+    // Stylesheet load.
+    {"style.css", false, TT_LINK, RT_STYLESHEET, 1},
+
+    // Script load.
+    {"script.js", false, TT_LINK, RT_SCRIPT, 1},
+
+    // Image load.
+    {"image.png", false, TT_LINK, RT_IMAGE, 1},
+
+    // Font load.
+    {"font.ttf", false, TT_LINK, RT_FONT_RESOURCE, 1},
+
+    // XHR load.
+    {"xhr.html", false, TT_LINK, RT_XHR, 1},
+};
+
+class TypeExpectations {
+ public:
+  explicit TypeExpectations(bool navigation) : navigation_(navigation) {
+    // Build the map of relevant requests.
+    for (int i = 0;
+         i < static_cast<int>(sizeof(g_type_expected) / sizeof(TypeExpected));
+         ++i) {
+      if (navigation_ && g_type_expected[i].navigation != navigation_)
+        continue;
+
+      request_count_.insert(std::make_pair(i, 0));
+    }
+  }
+
+  // Notify that a request has been received. Returns true if the request is
+  // something we care about.
+  bool GotRequest(CefRefPtr<CefRequest> request) {
+    const std::string& url = request->GetURL();
+    if (url.find(kTypeTestOrigin) != 0)
+      return false;
+
+    const std::string& file = url.substr(sizeof(kTypeTestOrigin) - 1);
+    cef_transition_type_t transition_type = request->GetTransitionType();
+    cef_resource_type_t resource_type = request->GetResourceType();
+
+    const int index = GetExpectedIndex(file, transition_type, resource_type);
+    EXPECT_GE(index, 0) << "File: " << file.c_str()
+                        << "; Navigation: " << navigation_
+                        << "; Transition Type: " << transition_type
+                        << "; Resource Type: " << resource_type;
+
+    RequestCount::iterator it = request_count_.find(index);
+    EXPECT_TRUE(it != request_count_.end());
+
+    const int actual_count = ++it->second;
+    const int expected_count = g_type_expected[index].expected_count;
+    EXPECT_LE(actual_count, expected_count)
+        << "File: " << file.c_str() << "; Navigation: " << navigation_
+        << "; Transition Type: " << transition_type
+        << "; Resource Type: " << resource_type;
+
+    return true;
+  }
+
+  // Test if all expectations have been met.
+  bool IsDone(bool assert) {
+    for (int i = 0;
+         i < static_cast<int>(sizeof(g_type_expected) / sizeof(TypeExpected));
+         ++i) {
+      if (navigation_ && g_type_expected[i].navigation != navigation_)
+        continue;
+
+      RequestCount::const_iterator it = request_count_.find(i);
+      EXPECT_TRUE(it != request_count_.end());
+      if (it->second != g_type_expected[i].expected_count) {
+        if (assert) {
+          EXPECT_EQ(g_type_expected[i].expected_count, it->second)
+              << "File: " << g_type_expected[i].file
+              << "; Navigation: " << navigation_
+              << "; Transition Type: " << g_type_expected[i].transition_type
+              << "; Resource Type: " << g_type_expected[i].resource_type;
+        }
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  // Returns the index for the specified navigation.
+  int GetExpectedIndex(const std::string& file,
+                       cef_transition_type_t transition_type,
+                       cef_resource_type_t resource_type) {
+    for (int i = 0;
+         i < static_cast<int>(sizeof(g_type_expected) / sizeof(TypeExpected));
+         ++i) {
+      if (g_type_expected[i].file == file &&
+          (!navigation_ || g_type_expected[i].navigation == navigation_) &&
+          g_type_expected[i].transition_type == transition_type &&
+          g_type_expected[i].resource_type == resource_type) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  bool navigation_;
+
+  // Map of TypeExpected index to actual request count.
+  typedef std::map<int, int> RequestCount;
+  RequestCount request_count_;
+};
+
+// Browser side.
+class TypeTestHandler : public TestHandler {
+ public:
+  TypeTestHandler()
+      : browse_expectations_(true),
+        load_expectations_(false),
+        get_expectations_(false),
+        completed_browser_side_(false),
+        destroyed_(false) {}
+
+  void RunTest() override {
+    AddResource(std::string(kTypeTestOrigin) + "main.html",
+                "<html>"
+                "<head>"
+                "<link rel=\"stylesheet\" href=\"style.css\" type=\"text/css\">"
+                "<script type=\"text/javascript\" src=\"script.js\"></script>"
+                "</head>"
+                "<body><p>Main</p>"
+                "<script>xhr = new XMLHttpRequest();"
+                "xhr.open('GET', 'xhr.html', false);"
+                "xhr.send();</script>"
+                "<iframe src=\"sub.html\"></iframe>"
+                "<img src=\"image.png\">"
+                "</body></html>",
+                "text/html");
+    AddResource(std::string(kTypeTestOrigin) + "sub.html", "<html>Sub</html>",
+                "text/html");
+    AddResource(std::string(kTypeTestOrigin) + "style.css",
+                "@font-face {"
+                "  font-family: custom_font;"
+                "  src: url('font.ttf');"
+                "}"
+                "p {"
+                "  font-family: custom_font;"
+                "}",
+                "text/css");
+    AddResource(std::string(kTypeTestOrigin) + "script.js", "<!-- -->",
+                "text/javascript");
+    AddResource(std::string(kTypeTestOrigin) + "image.png", "<!-- -->",
+                "image/png");
+    AddResource(std::string(kTypeTestOrigin) + "font.ttf", "<!-- -->",
+                "font/ttf");
+    AddResource(std::string(kTypeTestOrigin) + "xhr.html", "<html>XHR</html>",
+                "text/html");
+    AddResource(std::string(kTypeTestOrigin) + "fetch.html",
+                "<html>Fetch</html>", "text/html");
+
+    CreateBrowser(std::string(kTypeTestOrigin) + "main.html");
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    browse_expectations_.GotRequest(request);
+
+    return false;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    load_expectations_.GotRequest(request);
+
+    return RV_CONTINUE;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    if (get_expectations_.GotRequest(request) &&
+        get_expectations_.IsDone(false)) {
+      completed_browser_side_ = true;
+      // Destroy the test on the UI thread.
+      CefPostTask(TID_UI, base::Bind(&TypeTestHandler::DestroyTest, this));
+    }
+
+    return TestHandler::GetResourceHandler(browser, frame, request);
+  }
+
+ private:
+  void DestroyTest() override {
+    if (destroyed_)
+      return;
+    destroyed_ = true;
+
+    // Verify test expectations.
+    EXPECT_TRUE(completed_browser_side_);
+    EXPECT_TRUE(browse_expectations_.IsDone(true));
+    EXPECT_TRUE(load_expectations_.IsDone(true));
+    EXPECT_TRUE(get_expectations_.IsDone(true));
+
+    TestHandler::DestroyTest();
+  }
+
+  TypeExpectations browse_expectations_;
+  TypeExpectations load_expectations_;
+  TypeExpectations get_expectations_;
+
+  bool completed_browser_side_;
+  bool destroyed_;
+
+  IMPLEMENT_REFCOUNTING(TypeTestHandler);
+};
+
+}  // namespace
+
+// Verify the order of navigation-related callbacks.
+TEST(RequestTest, ResourceAndTransitionType) {
+  CefRefPtr<TypeTestHandler> handler = new TypeTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/resource.h b/src/tests/ceftests/resource.h
new file mode 100644
index 0000000..f87af0f
--- /dev/null
+++ b/src/tests/ceftests/resource.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by cefclient.rc
+//
+#define BINARY 256
+#define IDS_OSRTEST_HTML 1000
+#define IDS_PDF_HTML 1001
+#define IDS_PDF_PDF 1002
+#define IDS_WINDOW_ICON_1X_PNG 1003
+#define IDS_WINDOW_ICON_2X_PNG 1004
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 130
+#define _APS_NEXT_COMMAND_VALUE 32774
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 111
+#endif
+#endif
diff --git a/src/tests/ceftests/resource_manager_unittest.cc b/src/tests/ceftests/resource_manager_unittest.cc
new file mode 100644
index 0000000..fff45e7
--- /dev/null
+++ b/src/tests/ceftests/resource_manager_unittest.cc
@@ -0,0 +1,1893 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <vector>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_file_util.h"
+#include "include/cef_waitable_event.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_resource_manager.h"
+#include "include/wrapper/cef_scoped_temp_dir.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/file_util.h"
+
+namespace {
+
+std::string CreateMessage(const std::string& name, const std::string& value) {
+  return name + ":" + value;
+}
+
+// The returned contents execute a JavaScript callback containing |message|.
+std::string CreateContents(const std::string& message) {
+  return "<html><body>" + message +
+         "<script>"
+         "window.testQuery({request:'" +
+         message +
+         "'});"
+         "</script></html></body>";
+}
+
+void WriteFile(const std::string& path, const std::string& contents) {
+  int contents_size = static_cast<int>(contents.size());
+  int write_ct =
+      client::file_util::WriteFile(path, contents.data(), contents_size);
+  EXPECT_EQ(contents_size, write_ct);
+}
+
+CefRefPtr<CefResourceHandler> CreateContentsResourceHandler(
+    const std::string& message) {
+  const std::string& contents = CreateContents(message);
+  CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+      static_cast<void*>(const_cast<char*>(contents.data())),
+      contents.length());
+  return new CefStreamResourceHandler("text/html", stream);
+}
+
+const char kDoneMsg[] = "ResourceManagerTestHandler.Done";
+const char kNotHandled[] = "NotHandled";
+
+// Browser side.
+class ResourceManagerTestHandler : public RoutingTestHandler {
+ public:
+  struct State {
+    State() : manager_(new CefResourceManager()), expected_message_ct_(0) {}
+
+    CefRefPtr<CefResourceManager> manager_;
+
+    // Set of URLs that will be loaded.
+    std::vector<std::string> urls_;
+
+    // Set of messages that were received.
+    std::vector<std::string> messages_;
+
+    // If non-zero the test will not complete until the expected number of
+    // messages have been received.
+    size_t expected_message_ct_;
+  };
+
+  explicit ResourceManagerTestHandler(State* state)
+      : state_(state), current_url_(0) {
+    EXPECT_TRUE(state_);
+    EXPECT_TRUE(state_->manager_.get());
+    EXPECT_TRUE(!state_->urls_.empty());
+    EXPECT_TRUE(state_->messages_.empty());
+  }
+
+  void RunTest() override {
+    // Create the browser.
+    CreateBrowser(GetNextURL());
+
+    SetTestTimeout();
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    return state_->manager_->OnBeforeResourceLoad(browser, frame, request,
+                                                  callback);
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    // The ProviderDoNothing test destroys the manager before the handler.
+    if (state_->manager_) {
+      CefRefPtr<CefResourceHandler> handler =
+          state_->manager_->GetResourceHandler(browser, frame, request);
+      if (handler.get())
+        return handler;
+    }
+
+    return CreateContentsResourceHandler(CreateMessage(kDoneMsg, kNotHandled));
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    state_->messages_.push_back(request);
+    callback->Success("");
+
+    CefPostTask(TID_UI, base::Bind(&ResourceManagerTestHandler::Continue, this,
+                                   browser));
+
+    return true;
+  }
+
+  // Wait a bit before destroying the test. Used with ProviderDoNothing.
+  void DelayedDestroyTest() {
+    CefPostDelayedTask(
+        TID_UI, base::Bind(&ResourceManagerTestHandler::DestroyTest, this),
+        100);
+  }
+
+ private:
+  void Continue(CefRefPtr<CefBrowser> browser) {
+    if (state_->expected_message_ct_ == 0) {
+      // Load each URL sequentially.
+      const std::string& next_url = GetNextURL();
+      if (next_url.empty())
+        DestroyTest();
+      else
+        browser->GetMainFrame()->LoadURL(next_url);
+    } else if (state_->messages_.size() == state_->expected_message_ct_) {
+      DestroyTest();
+    }
+  }
+
+  std::string GetNextURL() {
+    if (current_url_ >= state_->urls_.size())
+      return std::string();
+    return state_->urls_[current_url_++];
+  }
+
+  State* state_;
+  size_t current_url_;
+
+  IMPLEMENT_REFCOUNTING(ResourceManagerTestHandler);
+};
+
+}  // namespace
+
+// Test with no providers.
+TEST(ResourceManagerTest, NoProviders) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Should get the message returned from GetResourceHandler when the request is
+  // not handled.
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+namespace {
+
+// Content provider that tracks execution state.
+class TestProvider : public CefResourceManager::Provider {
+ public:
+  struct State {
+    TrackCallback got_on_request_;
+    TrackCallback got_on_request_canceled_;
+    TrackCallback got_destruct_;
+    base::Closure destruct_callback_;
+    std::string request_url_;
+  };
+
+  explicit TestProvider(State* state) : state_(state) { EXPECT_TRUE(state_); }
+
+  ~TestProvider() {
+    CEF_REQUIRE_IO_THREAD();
+    state_->got_destruct_.yes();
+    if (!state_->destruct_callback_.is_null())
+      state_->destruct_callback_.Run();
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    CEF_REQUIRE_IO_THREAD();
+    EXPECT_FALSE(state_->got_on_request_);
+    EXPECT_FALSE(state_->got_on_request_canceled_);
+
+    state_->got_on_request_.yes();
+    state_->request_url_ = request->url();
+
+    return false;
+  }
+
+  void OnRequestCanceled(
+      scoped_refptr<CefResourceManager::Request> request) override {
+    CEF_REQUIRE_IO_THREAD();
+    EXPECT_TRUE(state_->got_on_request_);
+    EXPECT_FALSE(state_->got_on_request_canceled_);
+
+    state_->got_on_request_canceled_.yes();
+    EXPECT_STREQ(state_->request_url_.c_str(), request->url().c_str());
+  }
+
+ private:
+  State* state_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestProvider);
+};
+
+// Helper that blocks on destruction of 1 or more TestProviders.
+class ProviderDestructHelper {
+ public:
+  explicit ProviderDestructHelper(int expected_count) : current_count_(0) {
+    event_ = CefWaitableEvent::CreateWaitableEvent(true, false);
+    callback_ =
+        base::Bind(&DestructCallback, expected_count, &current_count_, event_);
+  }
+
+  const base::Closure& callback() const { return callback_; }
+
+  void Wait() { event_->Wait(); }
+
+ private:
+  static void DestructCallback(int expected_count,
+                               int* current_count,
+                               CefRefPtr<CefWaitableEvent> event) {
+    if (++(*current_count) == expected_count)
+      event->Signal();
+  }
+
+  int current_count_;
+  CefRefPtr<CefWaitableEvent> event_;
+  base::Closure callback_;
+};
+
+// Test that that the URL retrieved via Request::url() is parsed as expected.
+// Fragment or query components in any order should be removed.
+void TestUrlParsing(const char* kUrl) {
+  const char kRequestUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state;
+
+  ProviderDestructHelper destruct_helper(1);
+  provider_state.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(new TestProvider(&provider_state), 0,
+                              std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // The provider is called.
+  EXPECT_TRUE(provider_state.got_on_request_);
+  EXPECT_FALSE(provider_state.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state.got_destruct_);
+
+  // The expected URL is received.
+  EXPECT_STREQ(kRequestUrl, provider_state.request_url_.c_str());
+
+  // The request is not handled.
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+}  // namespace
+
+TEST(ResourceManagerTest, UrlParsingNoQueryOrFragment) {
+  TestUrlParsing("http://test.com/ResourceManagerTest");
+}
+
+TEST(ResourceManagerTest, UrlParsingWithQuery) {
+  TestUrlParsing("http://test.com/ResourceManagerTest?foo=bar&choo=too");
+}
+
+TEST(ResourceManagerTest, UrlParsingWithFragment) {
+  TestUrlParsing("http://test.com/ResourceManagerTest#some/fragment");
+}
+
+TEST(ResourceManagerTest, UrlParsingWithQueryAndFragment) {
+  TestUrlParsing("http://test.com/ResourceManagerTest?foo=bar#some/fragment");
+}
+
+TEST(ResourceManagerTest, UrlParsingWithFragmentAndQuery) {
+  TestUrlParsing("http://test.com/ResourceManagerTest#some/fragment?foo=bar");
+}
+
+namespace {
+
+const char kProviderId[] = "provider";
+
+// Content provider that performs simple tests.
+class SimpleTestProvider : public TestProvider {
+ public:
+  enum Mode {
+    NOT_HANDLED,
+    CONTINUE,
+    STOP,
+    REMOVE,
+    REMOVE_ALL,
+    DO_NOTHING,
+  };
+
+  SimpleTestProvider(State* state, Mode mode, CefResourceManager* manager)
+      : TestProvider(state), mode_(mode), manager_(manager) {}
+
+  SimpleTestProvider(State* state,
+                     Mode mode,
+                     CefResourceManager* manager,
+                     base::Closure do_nothing_callback)
+      : TestProvider(state),
+        mode_(mode),
+        manager_(manager),
+        do_nothing_callback_(do_nothing_callback) {}
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    TestProvider::OnRequest(request);
+
+    if (mode_ == NOT_HANDLED)
+      return false;
+    else if (mode_ == CONTINUE)
+      request->Continue(nullptr);
+    else if (mode_ == STOP)
+      request->Stop();
+    else if (mode_ == REMOVE)
+      manager_->RemoveProviders(kProviderId);
+    else if (mode_ == REMOVE_ALL)
+      manager_->RemoveAllProviders();
+    else if (mode_ == DO_NOTHING) {
+      EXPECT_FALSE(do_nothing_callback_.is_null());
+      do_nothing_callback_.Run();
+      do_nothing_callback_.Reset();
+    }
+
+    return true;
+  }
+
+ private:
+  Mode mode_;
+  CefResourceManager* manager_;  // Weak reference.
+  base::Closure do_nothing_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleTestProvider);
+};
+
+}  // namespace
+
+// Test with multiple providers that do not handle the request.
+TEST(ResourceManagerTest, ProviderNotHandled) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state1, SimpleTestProvider::NOT_HANDLED,
+                             nullptr),
+      0, std::string());
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state2, SimpleTestProvider::NOT_HANDLED,
+                             nullptr),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers are called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test with multiple providers that all continue.
+TEST(ResourceManagerTest, ProviderContinue) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state1, SimpleTestProvider::CONTINUE,
+                             nullptr),
+      0, std::string());
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state2, SimpleTestProvider::CONTINUE,
+                             nullptr),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers are called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test with multiple providers where the first one stops.
+TEST(ResourceManagerTest, ProviderStop) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state1, SimpleTestProvider::STOP,
+                             nullptr),
+      0, std::string());
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state2, SimpleTestProvider::CONTINUE,
+                             nullptr),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // 1st provider is called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  // 2nd provider is not called because the 1st provider stopped.
+  EXPECT_FALSE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test with multiple providers where the first one removes multiple providers
+// including itself.
+TEST(ResourceManagerTest, ProviderRemove) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+  TestProvider::State provider_state3;
+
+  ProviderDestructHelper destruct_helper(3);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+  provider_state3.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state1, SimpleTestProvider::REMOVE,
+                             state.manager_.get()),
+      0, kProviderId);
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state2, SimpleTestProvider::CONTINUE,
+                             nullptr),
+      0, kProviderId);
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state3, SimpleTestProvider::CONTINUE,
+                             nullptr),
+      1, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // 1st provider is called and canceled.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_TRUE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  // 2nd provider is removed with the 1st provider due to sharing the same
+  // identifier.
+  EXPECT_FALSE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  // 3rd provider is called.
+  EXPECT_TRUE(provider_state3.got_on_request_);
+  EXPECT_FALSE(provider_state3.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state3.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test with multiple providers where the first provider removes all.
+TEST(ResourceManagerTest, ProviderRemoveAll) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+  TestProvider::State provider_state3;
+
+  ProviderDestructHelper destruct_helper(3);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+  provider_state3.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state1, SimpleTestProvider::REMOVE_ALL,
+                             state.manager_.get()),
+      0, std::string());
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state2, SimpleTestProvider::CONTINUE,
+                             nullptr),
+      0, std::string());
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state3, SimpleTestProvider::CONTINUE,
+                             nullptr),
+      1, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // 1st provider is called and canceled.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_TRUE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  // 2nd and 3rd providers are not called due to removal.
+  EXPECT_FALSE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_FALSE(provider_state3.got_on_request_);
+  EXPECT_FALSE(provider_state3.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state3.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test with multiple providers that do not continue and will be destroyed when
+// the manager is destroyed.
+TEST(ResourceManagerTest, ProviderDoNothing) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+
+  // DelayedDestroyTest will be executed from SimpleTestHandler::OnRequest.
+  state.manager_->AddProvider(
+      new SimpleTestProvider(
+          &provider_state1, SimpleTestProvider::DO_NOTHING, nullptr,
+          base::Bind(&ResourceManagerTestHandler::DelayedDestroyTest, handler)),
+      0, std::string());
+  state.manager_->AddProvider(
+      new SimpleTestProvider(&provider_state2, SimpleTestProvider::DO_NOTHING,
+                             nullptr),
+      0, std::string());
+
+  handler->ExecuteTest();
+
+  // Destroy the resource manager before the handler so that pending requests
+  // are canceled. ResourceManagerTestHandler::GetResourceHandler will be called
+  // after the manager is destroyed.
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  // Only the first provider is called. It will be canceled when the manager is
+  // destroyed.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_TRUE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_FALSE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  // Nothing completed.
+  EXPECT_EQ(state.messages_.size(), 0U);
+}
+
+// Test AddContentProvider.
+TEST(ResourceManagerTest, ContentProvider) {
+  const char kUrl1[] = "http://test.com/ResourceManagerTest1";
+  const char kUrl2[] = "http://test.com/ResourceManagerTest2";
+  const char kUrl3[] = "http://test.com/ResourceManagerTest3";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl1);
+  state.urls_.push_back(kUrl2);
+  state.urls_.push_back(kUrl3);
+
+  // Only the first 2 URLs will be handled.
+  state.manager_->AddContentProvider(kUrl1, CreateContents(success1_message),
+                                     "text/html", 0, std::string());
+  state.manager_->AddContentProvider(kUrl2, CreateContents(success2_message),
+                                     "text/html", 0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  WaitForIOThread();
+
+  // Both providers are called and return the expected results.
+  EXPECT_EQ(state.messages_.size(), 3U);
+  EXPECT_EQ(success1_message, state.messages_[0]);
+  EXPECT_EQ(success2_message, state.messages_[1]);
+  EXPECT_EQ(not_handled_message, state.messages_[2]);
+}
+
+// Test AddDirectoryProvider.
+TEST(ResourceManagerTest, DirectoryProvider) {
+  const char kUrlBase[] = "http://test.com/ResourceManager";
+  const char kFile1[] = "File1.html";
+  const char kFile2[] = "File2.html";
+  const char kFile3[] = "File3.html";
+  const char kFile4[] = "File4.html";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& success3_message = CreateMessage(kDoneMsg, "Success3");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrlBase + std::string("/") + kFile1);
+  state.urls_.push_back(kUrlBase + std::string("/") + kFile2);
+  state.urls_.push_back(kUrlBase + std::string("/sub/") + kFile3);
+  state.urls_.push_back(kUrlBase + std::string("/") + kFile4);
+
+  CefScopedTempDir scoped_dir;
+  EXPECT_TRUE(scoped_dir.CreateUniqueTempDir());
+
+  // Write the files to disk.
+  const std::string& temp_dir = scoped_dir.GetPath();
+  WriteFile(client::file_util::JoinPath(temp_dir, kFile1),
+            CreateContents(success1_message));
+  WriteFile(client::file_util::JoinPath(temp_dir, kFile2),
+            CreateContents(success2_message));
+
+  // Also include a subdirectory.
+  const std::string& sub_dir = client::file_util::JoinPath(temp_dir, "sub");
+  EXPECT_TRUE(CefCreateDirectory(sub_dir));
+  WriteFile(client::file_util::JoinPath(sub_dir, kFile3),
+            CreateContents(success3_message));
+
+  state.manager_->AddDirectoryProvider(kUrlBase, temp_dir, 0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  WaitForIOThread();
+
+  EXPECT_TRUE(scoped_dir.Delete());
+
+  // The first 3 URLs are handled.
+  EXPECT_EQ(state.messages_.size(), 4U);
+  EXPECT_EQ(success1_message, state.messages_[0]);
+  EXPECT_EQ(success2_message, state.messages_[1]);
+  EXPECT_EQ(success3_message, state.messages_[2]);
+  EXPECT_EQ(not_handled_message, state.messages_[3]);
+}
+
+// Test AddArchiveProvider.
+TEST(ResourceManagerTest, ArchiveProvider) {
+  const char kUrlBase[] = "http://test.com/ResourceManager";
+  const char kFile1[] = "File1.html";
+  const char kFile2[] = "File2.html";
+  const char kFile3[] = "File3.html";
+  const char kFile4[] = "File4.html";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& success3_message = CreateMessage(kDoneMsg, "Success3");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrlBase + std::string("/") + kFile1);
+  state.urls_.push_back(kUrlBase + std::string("/") + kFile2);
+  state.urls_.push_back(kUrlBase + std::string("/sub/") + kFile3);
+  state.urls_.push_back(kUrlBase + std::string("/") + kFile4);
+
+  // Only the first 2 URLs will be handled.
+  CefScopedTempDir scoped_dir;
+  EXPECT_TRUE(scoped_dir.CreateUniqueTempDir());
+
+  const std::string& temp_dir = scoped_dir.GetPath();
+
+  // Write the files to disk.
+  const std::string& file_dir = client::file_util::JoinPath(temp_dir, "files");
+  EXPECT_TRUE(CefCreateDirectory(file_dir));
+  WriteFile(client::file_util::JoinPath(file_dir, kFile1),
+            CreateContents(success1_message));
+  WriteFile(client::file_util::JoinPath(file_dir, kFile2),
+            CreateContents(success2_message));
+
+  // Also include a subdirectory.
+  const std::string& sub_dir = client::file_util::JoinPath(file_dir, "sub");
+  EXPECT_TRUE(CefCreateDirectory(sub_dir));
+  WriteFile(client::file_util::JoinPath(sub_dir, kFile3),
+            CreateContents(success3_message));
+
+  const std::string& archive_path =
+      client::file_util::JoinPath(temp_dir, "archive.zip");
+
+  // Create the archive file.
+  EXPECT_TRUE(CefZipDirectory(file_dir, archive_path, false));
+
+  state.manager_->AddArchiveProvider(kUrlBase, archive_path, std::string(), 0,
+                                     std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  WaitForIOThread();
+
+  EXPECT_TRUE(scoped_dir.Delete());
+
+  // The first 3 URLs are handled.
+  EXPECT_EQ(state.messages_.size(), 4U);
+  EXPECT_EQ(success1_message, state.messages_[0]);
+  EXPECT_EQ(success2_message, state.messages_[1]);
+  EXPECT_EQ(success3_message, state.messages_[2]);
+  EXPECT_EQ(not_handled_message, state.messages_[3]);
+}
+
+namespace {
+
+// Content provider that only handles a single request.
+class OneShotProvider : public CefResourceManager::Provider {
+ public:
+  OneShotProvider(const std::string& content,
+                  const base::Closure& destruct_callback)
+      : done_(false), content_(content), destruct_callback_(destruct_callback) {
+    EXPECT_FALSE(content.empty());
+  }
+
+  ~OneShotProvider() {
+    CEF_REQUIRE_IO_THREAD();
+    destruct_callback_.Run();
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    CEF_REQUIRE_IO_THREAD();
+
+    if (done_) {
+      // Provider only handles a single request.
+      return false;
+    }
+
+    done_ = true;
+
+    CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+        static_cast<void*>(const_cast<char*>(content_.data())),
+        content_.length());
+
+    request->Continue(new CefStreamResourceHandler("text/html", stream));
+    return true;
+  }
+
+ private:
+  bool done_;
+  std::string content_;
+  base::Closure destruct_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(OneShotProvider);
+};
+
+}  // namespace
+
+// Test that providers are called in the expected order and return expected
+// results.
+TEST(ResourceManagerTest, ProviderOrder) {
+  const char kUrl1[] = "http://test.com/ResourceManagerTest1";
+  const char kUrl2[] = "http://test.com/ResourceManagerTest2";
+  const char kUrl3[] = "http://test.com/ResourceManagerTest3";
+  const char kUrl4[] = "http://test.com/ResourceManagerTest4";
+  const char kUrl5[] = "http://test.com/ResourceManagerTest5";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& success3_message = CreateMessage(kDoneMsg, "Success3");
+  const std::string& success4_message = CreateMessage(kDoneMsg, "Success4");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl1);
+  state.urls_.push_back(kUrl2);
+  state.urls_.push_back(kUrl3);
+  state.urls_.push_back(kUrl4);
+  state.urls_.push_back(kUrl5);
+
+  ProviderDestructHelper destruct_helper(4);
+
+  // Resulting order should be sequential; success1 .. success4.
+  state.manager_->AddProvider(
+      new OneShotProvider(CreateContents(success2_message),
+                          destruct_helper.callback()),
+      0, std::string());
+  state.manager_->AddProvider(
+      new OneShotProvider(CreateContents(success1_message),
+                          destruct_helper.callback()),
+      -100, std::string());
+  state.manager_->AddProvider(
+      new OneShotProvider(CreateContents(success4_message),
+                          destruct_helper.callback()),
+      100, std::string());
+  state.manager_->AddProvider(
+      new OneShotProvider(CreateContents(success3_message),
+                          destruct_helper.callback()),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  EXPECT_EQ(state.messages_.size(), 5U);
+  EXPECT_EQ(success1_message, state.messages_[0]);
+  EXPECT_EQ(success2_message, state.messages_[1]);
+  EXPECT_EQ(success3_message, state.messages_[2]);
+  EXPECT_EQ(success4_message, state.messages_[3]);
+  EXPECT_EQ(not_handled_message, state.messages_[4]);
+}
+
+namespace {
+
+// Content provider that returns the path component as the result.
+class EchoProvider : public CefResourceManager::Provider {
+ public:
+  EchoProvider(const std::string& base_url) : base_url_(base_url) {
+    EXPECT_TRUE(!base_url_.empty());
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    CEF_REQUIRE_IO_THREAD();
+
+    const std::string& url = request->url();
+    if (url.find(base_url_) != 0U) {
+      // Not handled by this provider.
+      return false;
+    }
+
+    const std::string& content = CreateContents(url);
+
+    // Parse the URL to identify the delay.
+    int delay = atoi(url.substr(base_url_.size()).data());
+    EXPECT_GE(delay, 0);
+
+    CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+        static_cast<void*>(const_cast<char*>(content.data())),
+        content.length());
+
+    CefPostDelayedTask(
+        TID_IO,
+        base::Bind(&CefResourceManager::Request::Continue, request,
+                   new CefStreamResourceHandler("text/html", stream)),
+        delay);
+
+    return true;
+  }
+
+ private:
+  std::string base_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(EchoProvider);
+};
+
+}  // namespace
+
+// Test that many requests pending at the same time complete in the expected
+// order and return correct results.
+TEST(ResourceManagerTest, ManyRequests) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+  const char kBaseUrl[] = "http://test.com/ResourceManagerSubTest/";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  state.expected_message_ct_ = 50U;
+
+  // Build a page with lots of iframes.
+  std::stringstream ss;
+  ss << "<html><body>";
+  for (size_t i = 0U; i < state.expected_message_ct_; ++i) {
+    ss << "<iframe src=\"" << kBaseUrl << (i * 10) << "\"></iframe>";
+  }
+  ss << "</body></html>";
+
+  state.manager_->AddContentProvider(kUrl, ss.str(), "text/html", 0,
+                                     std::string());
+  state.manager_->AddProvider(new EchoProvider(kBaseUrl), 0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  WaitForIOThread();
+
+  EXPECT_EQ(state.messages_.size(), state.expected_message_ct_);
+
+  // Requests should complete in order due to the delay.
+  for (size_t i = 0; i < state.messages_.size(); ++i) {
+    ss.str("");
+    ss << kBaseUrl << (i * 10);
+    EXPECT_EQ(ss.str(), state.messages_[i]);
+  }
+}
+
+namespace {
+
+// Content provider that only handles a single request and then removes itself
+// from the manager.
+class OneShotRemovalProvider : public TestProvider {
+ public:
+  OneShotRemovalProvider(State* state,
+                         const std::string& content,
+                         CefResourceManager* manager,
+                         const std::string& identifier,
+                         bool remove_before_continue)
+      : TestProvider(state),
+        content_(content),
+        manager_(manager),
+        identifier_(identifier),
+        remove_before_continue_(remove_before_continue) {
+    EXPECT_FALSE(content.empty());
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    TestProvider::OnRequest(request);
+
+    CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+        static_cast<void*>(const_cast<char*>(content_.data())),
+        content_.length());
+
+    if (remove_before_continue_) {
+      // Removing the provider before continuing should trigger a call to
+      // OnRequestCanceled.
+      if (identifier_.empty())
+        manager_->RemoveAllProviders();
+      else
+        manager_->RemoveProviders(identifier_);
+    }
+
+    request->Continue(new CefStreamResourceHandler("text/html", stream));
+
+    if (!remove_before_continue_) {
+      // The request has already completed so OnRequestCanceled is not called.
+      if (identifier_.empty())
+        manager_->RemoveAllProviders();
+      else
+        manager_->RemoveProviders(identifier_);
+    }
+
+    return true;
+  }
+
+ private:
+  std::string content_;
+  CefResourceManager* manager_;  // Weak reference.
+  std::string identifier_;
+  bool remove_before_continue_;
+
+  DISALLOW_COPY_AND_ASSIGN(OneShotRemovalProvider);
+};
+
+}  // namespace
+
+// Test that removal of the current provider after continue has the expected
+// results.
+TEST(ResourceManagerTest, RemoveProviderAfterContinue) {
+  const char kUrl1[] = "http://test.com/ResourceManagerTest1";
+  const char kUrl2[] = "http://test.com/ResourceManagerTest2";
+  const char kUrl3[] = "http://test.com/ResourceManagerTest3";
+  const char kUrl4[] = "http://test.com/ResourceManagerTest4";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& success3_message = CreateMessage(kDoneMsg, "Success3");
+  const std::string& success4_message = CreateMessage(kDoneMsg, "Success4");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl1);
+  state.urls_.push_back(kUrl2);
+  state.urls_.push_back(kUrl3);
+  state.urls_.push_back(kUrl4);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+  TestProvider::State provider_state3;
+  TestProvider::State provider_state4;
+
+  ProviderDestructHelper destruct_helper(4);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+  provider_state3.destruct_callback_ = destruct_helper.callback();
+  provider_state4.destruct_callback_ = destruct_helper.callback();
+
+  const char kIdentifier1[] = "id1";
+  const char kIdentifier2[] = "id2";
+  const char kIdentifier4[] = "id4";
+
+  // Resulting order should be sequential; success1 .. success4.
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state2,
+                                 CreateContents(success2_message),
+                                 state.manager_.get(), kIdentifier2, false),
+      0, kIdentifier2);
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state1,
+                                 CreateContents(success1_message),
+                                 state.manager_.get(), kIdentifier1, false),
+      -100, kIdentifier1);
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state4,
+                                 CreateContents(success4_message),
+                                 state.manager_.get(), kIdentifier4, false),
+      100, kIdentifier4);
+  // Will be removed at the same time as #2 and therefore never called.
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state3,
+                                 CreateContents(success3_message),
+                                 state.manager_.get(), kIdentifier2, false),
+      0, kIdentifier2);
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers except for 3 (which is removed at the same time as 2 and
+  // therefore never executed) should complete.
+  EXPECT_EQ(state.messages_.size(), 4U);
+  EXPECT_EQ(success1_message, state.messages_[0]);
+  EXPECT_EQ(success2_message, state.messages_[1]);
+  EXPECT_EQ(success4_message, state.messages_[2]);
+  EXPECT_EQ(not_handled_message, state.messages_[3]);
+
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  // Removed at the same time as 2 and therefore not executed.
+  EXPECT_FALSE(provider_state3.got_on_request_);
+  EXPECT_FALSE(provider_state3.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state3.got_destruct_);
+
+  EXPECT_TRUE(provider_state4.got_on_request_);
+  EXPECT_FALSE(provider_state4.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state4.got_destruct_);
+}
+
+// Test that removal of the current provider before continue has the expected
+// results.
+TEST(ResourceManagerTest, RemoveProviderBeforeContinue) {
+  const char kUrl1[] = "http://test.com/ResourceManagerTest1";
+  const char kUrl2[] = "http://test.com/ResourceManagerTest2";
+  const char kUrl3[] = "http://test.com/ResourceManagerTest3";
+  const char kUrl4[] = "http://test.com/ResourceManagerTest4";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& success3_message = CreateMessage(kDoneMsg, "Success3");
+  const std::string& success4_message = CreateMessage(kDoneMsg, "Success4");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl1);
+  state.urls_.push_back(kUrl2);
+  state.urls_.push_back(kUrl3);
+  state.urls_.push_back(kUrl4);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+  TestProvider::State provider_state3;
+  TestProvider::State provider_state4;
+
+  ProviderDestructHelper destruct_helper(4);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+  provider_state3.destruct_callback_ = destruct_helper.callback();
+  provider_state4.destruct_callback_ = destruct_helper.callback();
+
+  const char kIdentifier1[] = "id1";
+  const char kIdentifier2[] = "id2";
+  const char kIdentifier4[] = "id4";
+
+  // Resulting order should be sequential; success1 .. success4.
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state2,
+                                 CreateContents(success2_message),
+                                 state.manager_.get(), kIdentifier2, true),
+      0, kIdentifier2);
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state1,
+                                 CreateContents(success1_message),
+                                 state.manager_.get(), kIdentifier1, true),
+      -100, kIdentifier1);
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state4,
+                                 CreateContents(success4_message),
+                                 state.manager_.get(), kIdentifier4, true),
+      100, kIdentifier4);
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state3,
+                                 CreateContents(success3_message),
+                                 state.manager_.get(), kIdentifier2, true),
+      0, kIdentifier2);
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // No providers should complete.
+  EXPECT_EQ(state.messages_.size(), 4U);
+  EXPECT_EQ(not_handled_message, state.messages_[0]);
+  EXPECT_EQ(not_handled_message, state.messages_[1]);
+  EXPECT_EQ(not_handled_message, state.messages_[2]);
+  EXPECT_EQ(not_handled_message, state.messages_[3]);
+
+  // All providers except 3 should be called and then canceled.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_TRUE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_TRUE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  // Removed at the same time as 2 and therefore not executed.
+  EXPECT_FALSE(provider_state3.got_on_request_);
+  EXPECT_FALSE(provider_state3.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state3.got_destruct_);
+
+  EXPECT_TRUE(provider_state4.got_on_request_);
+  EXPECT_TRUE(provider_state4.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state4.got_destruct_);
+}
+
+// Test that removal of all providers after continue has the expected results.
+TEST(ResourceManagerTest, RemoveAllProvidersAfterContinue) {
+  const char kUrl1[] = "http://test.com/ResourceManagerTest1";
+  const char kUrl2[] = "http://test.com/ResourceManagerTest2";
+  const char kUrl3[] = "http://test.com/ResourceManagerTest3";
+  const char kUrl4[] = "http://test.com/ResourceManagerTest4";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& success3_message = CreateMessage(kDoneMsg, "Success3");
+  const std::string& success4_message = CreateMessage(kDoneMsg, "Success4");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl1);
+  state.urls_.push_back(kUrl2);
+  state.urls_.push_back(kUrl3);
+  state.urls_.push_back(kUrl4);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+  TestProvider::State provider_state3;
+  TestProvider::State provider_state4;
+
+  ProviderDestructHelper destruct_helper(4);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+  provider_state3.destruct_callback_ = destruct_helper.callback();
+  provider_state4.destruct_callback_ = destruct_helper.callback();
+
+  // Resulting order should be sequential; success1 .. success4.
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state2,
+                                 CreateContents(success2_message),
+                                 state.manager_.get(), std::string(), false),
+      0, std::string());
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state1,
+                                 CreateContents(success1_message),
+                                 state.manager_.get(), std::string(), false),
+      -100, std::string());
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state4,
+                                 CreateContents(success4_message),
+                                 state.manager_.get(), std::string(), false),
+      100, std::string());
+  // Will be removed at the same time as #2 and therefore never called.
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state3,
+                                 CreateContents(success3_message),
+                                 state.manager_.get(), std::string(), false),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // Only the 1st provider should complete
+  EXPECT_EQ(state.messages_.size(), 4U);
+  EXPECT_EQ(success1_message, state.messages_[0]);
+  EXPECT_EQ(not_handled_message, state.messages_[1]);
+  EXPECT_EQ(not_handled_message, state.messages_[2]);
+  EXPECT_EQ(not_handled_message, state.messages_[3]);
+
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_FALSE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_FALSE(provider_state3.got_on_request_);
+  EXPECT_FALSE(provider_state3.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state3.got_destruct_);
+
+  EXPECT_FALSE(provider_state4.got_on_request_);
+  EXPECT_FALSE(provider_state4.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state4.got_destruct_);
+}
+
+// Test that removal of all providers before continue has the expected results.
+TEST(ResourceManagerTest, RemoveAllProvidersBeforeContinue) {
+  const char kUrl1[] = "http://test.com/ResourceManagerTest1";
+  const char kUrl2[] = "http://test.com/ResourceManagerTest2";
+  const char kUrl3[] = "http://test.com/ResourceManagerTest3";
+  const char kUrl4[] = "http://test.com/ResourceManagerTest4";
+
+  const std::string& success1_message = CreateMessage(kDoneMsg, "Success1");
+  const std::string& success2_message = CreateMessage(kDoneMsg, "Success2");
+  const std::string& success3_message = CreateMessage(kDoneMsg, "Success3");
+  const std::string& success4_message = CreateMessage(kDoneMsg, "Success4");
+  const std::string& not_handled_message = CreateMessage(kDoneMsg, kNotHandled);
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl1);
+  state.urls_.push_back(kUrl2);
+  state.urls_.push_back(kUrl3);
+  state.urls_.push_back(kUrl4);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+  TestProvider::State provider_state3;
+  TestProvider::State provider_state4;
+
+  ProviderDestructHelper destruct_helper(4);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+  provider_state3.destruct_callback_ = destruct_helper.callback();
+  provider_state4.destruct_callback_ = destruct_helper.callback();
+
+  // Resulting order should be sequential; success1 .. success4.
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state2,
+                                 CreateContents(success2_message),
+                                 state.manager_.get(), std::string(), true),
+      0, std::string());
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state1,
+                                 CreateContents(success1_message),
+                                 state.manager_.get(), std::string(), true),
+      -100, std::string());
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state4,
+                                 CreateContents(success4_message),
+                                 state.manager_.get(), std::string(), true),
+      100, std::string());
+  // Will be removed at the same time as #2 and therefore never called.
+  state.manager_->AddProvider(
+      new OneShotRemovalProvider(&provider_state3,
+                                 CreateContents(success3_message),
+                                 state.manager_.get(), std::string(), true),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // No providers should complete.
+  EXPECT_EQ(state.messages_.size(), 4U);
+  EXPECT_EQ(not_handled_message, state.messages_[0]);
+  EXPECT_EQ(not_handled_message, state.messages_[1]);
+  EXPECT_EQ(not_handled_message, state.messages_[2]);
+  EXPECT_EQ(not_handled_message, state.messages_[3]);
+
+  // 1st provider should also be canceled.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_TRUE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_FALSE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_FALSE(provider_state3.got_on_request_);
+  EXPECT_FALSE(provider_state3.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state3.got_destruct_);
+
+  EXPECT_FALSE(provider_state4.got_on_request_);
+  EXPECT_FALSE(provider_state4.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state4.got_destruct_);
+}
+
+namespace {
+
+// Test the URL filter capability.
+class UrlFilterTestProvider : public TestProvider {
+ public:
+  UrlFilterTestProvider(State* state,
+                        const std::string& expected_url,
+                        const std::string& expected_url_after_filter)
+      : TestProvider(state),
+        expected_url_(expected_url),
+        expected_url_after_filter_(expected_url_after_filter) {}
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    TestProvider::OnRequest(request);
+
+    // Request::url is already filtered.
+    EXPECT_EQ(expected_url_, request->url());
+
+    // Explicitly filter the URL again.
+    const std::string& filtered_url =
+        request->url_filter().Run(request->request()->GetURL());
+    EXPECT_EQ(expected_url_after_filter_, filtered_url);
+
+    request->Continue(nullptr);
+    return true;
+  }
+
+ private:
+  std::string expected_url_;
+  std::string expected_url_after_filter_;
+  DISALLOW_COPY_AND_ASSIGN(UrlFilterTestProvider);
+};
+
+std::string TestUrlFilter(const std::string& url) {
+  return url + "Rewrite";
+}
+
+// Add to the URL but keep the query component.
+std::string TestUrlFilterWithQuery(const std::string& url) {
+  size_t pos = url.find('?');
+  if (pos == std::string::npos)
+    return url;
+  return url.substr(0, pos) + "Rewrite" + url.substr(pos);
+}
+
+// Add to the URL but keep the fragment component.
+std::string TestUrlFilterWithFragment(const std::string& url) {
+  size_t pos = url.find('#');
+  if (pos == std::string::npos)
+    return url;
+  return url.substr(0, pos) + "Rewrite" + url.substr(pos);
+}
+
+}  // namespace
+
+// Test the URL filter capability.
+TEST(ResourceManagerTest, UrlFilter) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+  const char kExpectedUrl[] = "http://test.com/ResourceManagerTestRewrite";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  // Set the URL filter.
+  state.manager_->SetUrlFilter(base::Bind(TestUrlFilter));
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new UrlFilterTestProvider(&provider_state1, kExpectedUrl, kExpectedUrl),
+      0, std::string());
+  state.manager_->AddProvider(
+      new UrlFilterTestProvider(&provider_state2, kExpectedUrl, kExpectedUrl),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers are called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test the URL filter capability with a query component.
+TEST(ResourceManagerTest, UrlFilterWithQuery) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest?foo=bar";
+  const char kExpectedUrl[] = "http://test.com/ResourceManagerTestRewrite";
+  const char kExpectedUrlAfterFilter[] =
+      "http://test.com/ResourceManagerTestRewrite?foo=bar";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  // Set the URL filter.
+  state.manager_->SetUrlFilter(base::Bind(TestUrlFilterWithQuery));
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new UrlFilterTestProvider(&provider_state1, kExpectedUrl,
+                                kExpectedUrlAfterFilter),
+      0, std::string());
+  state.manager_->AddProvider(
+      new UrlFilterTestProvider(&provider_state2, kExpectedUrl,
+                                kExpectedUrlAfterFilter),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers are called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test the URL filter capability with a fragment component.
+TEST(ResourceManagerTest, UrlFilterWithFragment) {
+  // Fragment components will not be passed with the request.
+  const char kUrl[] = "http://test.com/ResourceManagerTest#fragment";
+  const char kExpectedUrl[] = "http://test.com/ResourceManagerTestRewrite";
+  const char kExpectedUrlAfterFilter[] =
+      "http://test.com/ResourceManagerTestRewrite#fragment";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  // Set the URL filter.
+  state.manager_->SetUrlFilter(base::Bind(TestUrlFilterWithFragment));
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new UrlFilterTestProvider(&provider_state1, kExpectedUrl,
+                                kExpectedUrlAfterFilter),
+      0, std::string());
+  state.manager_->AddProvider(
+      new UrlFilterTestProvider(&provider_state2, kExpectedUrl,
+                                kExpectedUrlAfterFilter),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers are called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+namespace {
+
+// Test the mime type resolver capability.
+class MimeTypeTestProvider : public TestProvider {
+ public:
+  MimeTypeTestProvider(State* state, const std::string& expected_mime_type)
+      : TestProvider(state), expected_mime_type_(expected_mime_type) {}
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    TestProvider::OnRequest(request);
+
+    const std::string& mime_type =
+        request->mime_type_resolver().Run(request->url());
+    EXPECT_EQ(expected_mime_type_, mime_type);
+
+    request->Continue(nullptr);
+    return true;
+  }
+
+ private:
+  std::string expected_mime_type_;
+  DISALLOW_COPY_AND_ASSIGN(MimeTypeTestProvider);
+};
+
+const char kExpectedMimeType[] = "foo/bar";
+
+std::string TestMimeTypeResolver(const std::string& url) {
+  return kExpectedMimeType;
+}
+
+}  // namespace
+
+// Test the mime type resolver capability.
+TEST(ResourceManagerTest, MimeTypeResolver) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  // Set the mime type resolver.
+  state.manager_->SetMimeTypeResolver(base::Bind(TestMimeTypeResolver));
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new MimeTypeTestProvider(&provider_state1, kExpectedMimeType), 0,
+      std::string());
+  state.manager_->AddProvider(
+      new MimeTypeTestProvider(&provider_state2, kExpectedMimeType), 0,
+      std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers are called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+namespace {
+
+// Content provider that adds another provider before or after the current
+// provider.
+class AddingTestProvider : public TestProvider {
+ public:
+  AddingTestProvider(State* state,
+                     State* new_state,
+                     CefResourceManager* manager,
+                     bool before)
+      : TestProvider(state),
+        new_state_(new_state),
+        manager_(manager),
+        before_(before) {}
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
+    TestProvider::OnRequest(request);
+
+    manager_->AddProvider(
+        new SimpleTestProvider(new_state_, SimpleTestProvider::CONTINUE,
+                               nullptr),
+        before_ ? -1 : 1, std::string());
+
+    request->Continue(nullptr);
+    return true;
+  }
+
+ private:
+  State* new_state_;
+  CefResourceManager* manager_;  // Weak reference.
+  bool before_;
+
+  DISALLOW_COPY_AND_ASSIGN(AddingTestProvider);
+};
+
+}  // namespace
+
+// Test adding a new provider after the current provider.
+TEST(ResourceManagerTest, AddProviderAfter) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new AddingTestProvider(&provider_state1, &provider_state2,
+                             state.manager_.get(), false),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // All providers are called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  EXPECT_TRUE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
+
+// Test adding a new provider before the current provider.
+TEST(ResourceManagerTest, AddProviderBefore) {
+  const char kUrl[] = "http://test.com/ResourceManagerTest";
+
+  ResourceManagerTestHandler::State state;
+  state.urls_.push_back(kUrl);
+
+  TestProvider::State provider_state1;
+  TestProvider::State provider_state2;
+
+  ProviderDestructHelper destruct_helper(2);
+  provider_state1.destruct_callback_ = destruct_helper.callback();
+  provider_state2.destruct_callback_ = destruct_helper.callback();
+
+  state.manager_->AddProvider(
+      new AddingTestProvider(&provider_state1, &provider_state2,
+                             state.manager_.get(), true),
+      0, std::string());
+
+  CefRefPtr<ResourceManagerTestHandler> handler =
+      new ResourceManagerTestHandler(&state);
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  state.manager_ = nullptr;
+
+  // Wait for the manager to be deleted.
+  destruct_helper.Wait();
+
+  // 1st provider is called.
+  EXPECT_TRUE(provider_state1.got_on_request_);
+  EXPECT_FALSE(provider_state1.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state1.got_destruct_);
+
+  // 2nd provider is not called because it was added before the 1st provider.
+  EXPECT_FALSE(provider_state2.got_on_request_);
+  EXPECT_FALSE(provider_state2.got_on_request_canceled_);
+  EXPECT_TRUE(provider_state2.got_destruct_);
+
+  EXPECT_EQ(state.messages_.size(), 1U);
+  EXPECT_EQ(CreateMessage(kDoneMsg, kNotHandled), state.messages_[0]);
+}
diff --git a/src/tests/ceftests/resource_request_handler_unittest.cc b/src/tests/ceftests/resource_request_handler_unittest.cc
new file mode 100644
index 0000000..300692f
--- /dev/null
+++ b/src/tests/ceftests/resource_request_handler_unittest.cc
@@ -0,0 +1,3884 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+#include <cmath>
+#include <sstream>
+#include <string>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Normal stream resource handler implementation that additionally verifies
+// calls to Cancel.
+// This also tests the CefStreamResourceHandler implementation.
+class NormalResourceHandler : public CefStreamResourceHandler {
+ public:
+  NormalResourceHandler(int status_code,
+                        const CefString& status_text,
+                        const CefString& mime_type,
+                        CefResponse::HeaderMap header_map,
+                        CefRefPtr<CefStreamReader> stream,
+                        const base::Closure& destroy_callback)
+      : CefStreamResourceHandler(status_code,
+                                 status_text,
+                                 mime_type,
+                                 header_map,
+                                 stream),
+        destroy_callback_(destroy_callback) {}
+
+  ~NormalResourceHandler() override {
+    EXPECT_EQ(1, cancel_ct_);
+    destroy_callback_.Run();
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  const base::Closure destroy_callback_;
+  int cancel_ct_ = 0;
+};
+
+// Normal stream resource handler implementation that additionally continues
+// using the callback object and verifies calls to Cancel.
+class CallbackResourceHandler : public CefResourceHandler {
+ public:
+  enum Mode {
+    DELAYED_OPEN,
+    DELAYED_READ,
+    IMMEDIATE_OPEN,
+    IMMEDIATE_READ,
+    DELAYED_ALL,
+    IMMEDIATE_ALL,
+  };
+
+  bool IsDelayedOpen() const {
+    return mode_ == DELAYED_OPEN || mode_ == DELAYED_ALL;
+  }
+
+  bool IsDelayedRead() const {
+    return mode_ == DELAYED_READ || mode_ == DELAYED_ALL;
+  }
+
+  bool IsImmediateOpen() const {
+    return mode_ == IMMEDIATE_OPEN || mode_ == IMMEDIATE_ALL;
+  }
+
+  bool IsImmediateRead() const {
+    return mode_ == IMMEDIATE_READ || mode_ == IMMEDIATE_ALL;
+  }
+
+  CallbackResourceHandler(Mode mode,
+                          int status_code,
+                          const CefString& status_text,
+                          const CefString& mime_type,
+                          CefResponse::HeaderMap header_map,
+                          CefRefPtr<CefStreamReader> stream,
+                          const base::Closure& destroy_callback)
+      : mode_(mode),
+        status_code_(status_code),
+        status_text_(status_text),
+        mime_type_(mime_type),
+        header_map_(header_map),
+        stream_(stream),
+        destroy_callback_(destroy_callback) {
+    DCHECK(!mime_type_.empty());
+    DCHECK(stream_.get());
+  }
+
+  ~CallbackResourceHandler() override {
+    EXPECT_EQ(1, cancel_ct_);
+    destroy_callback_.Run();
+  }
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    if (IsDelayedOpen()) {
+      // Continue the request asynchronously by executing the callback.
+      CefPostTask(TID_FILE_USER_VISIBLE,
+                  base::Bind(&CefCallback::Continue, callback));
+      handle_request = false;
+      return true;
+    } else if (IsImmediateOpen()) {
+      // Continue the request immediately be executing the callback.
+      callback->Continue();
+      handle_request = false;
+      return true;
+    }
+
+    // Continue the request immediately in the default manner.
+    handle_request = true;
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    response->SetStatus(status_code_);
+    response->SetStatusText(status_text_);
+    response->SetMimeType(mime_type_);
+
+    if (!header_map_.empty())
+      response->SetHeaderMap(header_map_);
+
+    response_length = -1;
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+    EXPECT_GT(bytes_to_read, 0);
+
+    bytes_read = 0;
+
+    if (IsDelayedRead()) {
+      // Continue the request asynchronously by executing the callback.
+      CefPostTask(TID_FILE_USER_VISIBLE,
+                  base::Bind(&CallbackResourceHandler::ContinueRead, this,
+                             data_out, bytes_to_read, callback));
+      return true;
+    } else if (IsImmediateRead()) {
+      // Continue the request immediately be executing the callback.
+      ContinueRead(data_out, bytes_to_read, callback);
+      return true;
+    }
+
+    // Continue the request immediately in the default manner.
+    return DoRead(data_out, bytes_to_read, bytes_read);
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  void ContinueRead(void* data_out,
+                    int bytes_to_read,
+                    CefRefPtr<CefResourceReadCallback> callback) {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    int bytes_read = 0;
+    DoRead(data_out, bytes_to_read, bytes_read);
+    callback->Continue(bytes_read);
+  }
+
+  bool DoRead(void* data_out, int bytes_to_read, int& bytes_read) {
+    DCHECK_GT(bytes_to_read, 0);
+
+    // Read until the buffer is full or until Read() returns 0 to indicate no
+    // more data.
+    bytes_read = 0;
+    int read = 0;
+    do {
+      read = static_cast<int>(
+          stream_->Read(static_cast<char*>(data_out) + bytes_read, 1,
+                        bytes_to_read - bytes_read));
+      bytes_read += read;
+    } while (read != 0 && bytes_read < bytes_to_read);
+
+    return (bytes_read > 0);
+  }
+
+  const Mode mode_;
+
+  const int status_code_;
+  const CefString status_text_;
+  const CefString mime_type_;
+  const CefResponse::HeaderMap header_map_;
+  const CefRefPtr<CefStreamReader> stream_;
+
+  const base::Closure destroy_callback_;
+  int cancel_ct_ = 0;
+
+  IMPLEMENT_REFCOUNTING(CallbackResourceHandler);
+  DISALLOW_COPY_AND_ASSIGN(CallbackResourceHandler);
+};
+
+// Resource handler implementation that never completes. Used to test
+// destruction handling behavior for in-progress requests.
+class IncompleteResourceHandlerOld : public CefResourceHandler {
+ public:
+  enum TestMode {
+    BLOCK_PROCESS_REQUEST,
+    BLOCK_READ_RESPONSE,
+  };
+
+  IncompleteResourceHandlerOld(TestMode test_mode,
+                               const std::string& mime_type,
+                               const base::Closure& destroy_callback)
+      : test_mode_(test_mode),
+        mime_type_(mime_type),
+        destroy_callback_(destroy_callback) {}
+
+  ~IncompleteResourceHandlerOld() override {
+    EXPECT_EQ(1, process_request_ct_);
+
+    EXPECT_EQ(1, cancel_ct_);
+
+    if (test_mode_ == BLOCK_READ_RESPONSE) {
+      EXPECT_EQ(1, get_response_headers_ct_);
+      EXPECT_EQ(1, read_response_ct_);
+    } else {
+      EXPECT_EQ(0, get_response_headers_ct_);
+      EXPECT_EQ(0, read_response_ct_);
+    }
+
+    destroy_callback_.Run();
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+
+    process_request_ct_++;
+
+    if (test_mode_ == BLOCK_PROCESS_REQUEST) {
+      // Never release or execute this callback.
+      incomplete_callback_ = callback;
+    } else {
+      callback->Continue();
+    }
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(test_mode_, BLOCK_READ_RESPONSE);
+
+    get_response_headers_ct_++;
+
+    response->SetStatus(200);
+    response->SetStatusText("OK");
+    response->SetMimeType(mime_type_);
+    response_length = 100;
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(test_mode_, BLOCK_READ_RESPONSE);
+
+    read_response_ct_++;
+
+    // Never release or execute this callback.
+    incomplete_callback_ = callback;
+    bytes_read = 0;
+    return true;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  const TestMode test_mode_;
+  const std::string mime_type_;
+  const base::Closure destroy_callback_;
+
+  int process_request_ct_ = 0;
+  int get_response_headers_ct_ = 0;
+  int read_response_ct_ = 0;
+  int cancel_ct_ = 0;
+
+  CefRefPtr<CefCallback> incomplete_callback_;
+
+  IMPLEMENT_REFCOUNTING(IncompleteResourceHandlerOld);
+  DISALLOW_COPY_AND_ASSIGN(IncompleteResourceHandlerOld);
+};
+
+class IncompleteResourceHandler : public CefResourceHandler {
+ public:
+  enum TestMode {
+    BLOCK_OPEN,
+    BLOCK_READ,
+  };
+
+  IncompleteResourceHandler(TestMode test_mode,
+                            const std::string& mime_type,
+                            const base::Closure& destroy_callback)
+      : test_mode_(test_mode),
+        mime_type_(mime_type),
+        destroy_callback_(destroy_callback) {}
+
+  ~IncompleteResourceHandler() override {
+    EXPECT_EQ(1, open_ct_);
+
+    EXPECT_EQ(1, cancel_ct_);
+
+    if (test_mode_ == BLOCK_READ) {
+      EXPECT_EQ(1, get_response_headers_ct_);
+      EXPECT_EQ(1, read_ct_);
+    } else {
+      EXPECT_EQ(0, get_response_headers_ct_);
+      EXPECT_EQ(0, read_ct_);
+    }
+
+    destroy_callback_.Run();
+  }
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    open_ct_++;
+
+    if (test_mode_ == BLOCK_OPEN) {
+      // Never release or execute this callback.
+      incomplete_open_callback_ = callback;
+    } else {
+      // Continue immediately.
+      handle_request = true;
+    }
+    return true;
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    return false;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(test_mode_, BLOCK_READ);
+
+    get_response_headers_ct_++;
+
+    response->SetStatus(200);
+    response->SetStatusText("OK");
+    response->SetMimeType(mime_type_);
+    response_length = 100;
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+    EXPECT_EQ(test_mode_, BLOCK_READ);
+
+    read_ct_++;
+
+    // Never release or execute this callback.
+    incomplete_read_callback_ = callback;
+    bytes_read = 0;
+    return true;
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    bytes_read = -2;
+    return false;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  const TestMode test_mode_;
+  const std::string mime_type_;
+  const base::Closure destroy_callback_;
+
+  int open_ct_ = 0;
+  int get_response_headers_ct_ = 0;
+  int read_ct_ = 0;
+  int cancel_ct_ = 0;
+
+  CefRefPtr<CefCallback> incomplete_open_callback_;
+  CefRefPtr<CefResourceReadCallback> incomplete_read_callback_;
+
+  IMPLEMENT_REFCOUNTING(IncompleteResourceHandler);
+  DISALLOW_COPY_AND_ASSIGN(IncompleteResourceHandler);
+};
+
+class BasicResponseTest : public TestHandler {
+ public:
+  enum TestMode {
+    // Normal load, nothing fancy.
+    LOAD,
+
+    // Close the browser in OnAfterCreated to verify destruction handling of
+    // uninitialized requests.
+    ABORT_AFTER_CREATED,
+
+    // Close the browser in OnBeforeBrowse to verify destruction handling of
+    // uninitialized requests.
+    ABORT_BEFORE_BROWSE,
+
+    // Don't continue from OnBeforeResourceLoad, then close the browser to
+    // verify destruction handling of in-progress requests.
+    INCOMPLETE_BEFORE_RESOURCE_LOAD,
+
+    // Modify the request (add headers) in OnBeforeResourceLoad.
+    MODIFY_BEFORE_RESOURCE_LOAD,
+
+    // Redirect the request (change the URL) in OnBeforeResourceLoad.
+    REDIRECT_BEFORE_RESOURCE_LOAD,
+
+    // Return a CefResourceHandler from GetResourceHandler that continues
+    // immediately by using the callback object instead of the return value.
+    IMMEDIATE_REQUEST_HANDLER_OPEN,
+    IMMEDIATE_REQUEST_HANDLER_READ,
+    IMMEDIATE_REQUEST_HANDLER_ALL,
+
+    // Return a CefResourceHandler from GetResourceHandler that continues with
+    // a delay by using the callback object.
+    DELAYED_REQUEST_HANDLER_OPEN,
+    DELAYED_REQUEST_HANDLER_READ,
+    DELAYED_REQUEST_HANDLER_ALL,
+
+    // Return a CefResourceHandler from GetResourceHandler that never completes,
+    // then close the browser to verify destruction handling of in-progress
+    // requests.
+    INCOMPLETE_REQUEST_HANDLER_OPEN,
+    INCOMPLETE_REQUEST_HANDLER_READ,
+
+    // Redirect the request using a CefResourceHandler returned from
+    // GetResourceHandler.
+    REDIRECT_REQUEST_HANDLER,
+
+    // Redirect the request (change the URL) an additional time in
+    // OnResourceRedirect after using a CefResourceHandler returned from
+    // GetResourceHandler for the first redirect.
+    REDIRECT_RESOURCE_REDIRECT,
+
+    // Redirect the request (change the URL) in OnResourceResponse.
+    REDIRECT_RESOURCE_RESPONSE,
+
+    // Restart the request (add headers) in OnResourceResponse.
+    RESTART_RESOURCE_RESPONSE,
+  };
+
+  // If |custom_scheme| is true all requests will use a custom scheme.
+  // If |unhandled| is true the final request (after any redirects) will be
+  // unhandled, meaning that default handling is disabled and GetResourceHandler
+  // returns null.
+  BasicResponseTest(TestMode mode, bool custom_scheme, bool unhandled)
+      : mode_(mode), custom_scheme_(custom_scheme), unhandled_(unhandled) {}
+
+  void RunTest() override {
+    CreateBrowser(GetStartupURL());
+    SetTestTimeout();
+  }
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
+    EXPECT_UI_THREAD();
+    TestHandler::OnAfterCreated(browser);
+
+    if (mode_ == ABORT_AFTER_CREATED) {
+      SetSignalCompletionWhenAllBrowsersClose(false);
+      CloseBrowser(browser, false);
+    }
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    EXPECT_UI_THREAD();
+    TestHandler::OnBeforeClose(browser);
+
+    if (IsAborted()) {
+      DestroyTest();
+    }
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_UI_THREAD();
+    if (browser_id_ == 0) {
+      // This is the first callback that provides a browser ID.
+      browser_id_ = browser->GetIdentifier();
+      EXPECT_GT(browser_id_, 0);
+    } else {
+      EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    }
+    EXPECT_TRUE(frame->IsMain());
+
+    EXPECT_FALSE(user_gesture);
+    if (on_before_browse_ct_ == 0 || mode_ == RESTART_RESOURCE_RESPONSE) {
+      EXPECT_FALSE(is_redirect) << on_before_browse_ct_;
+    } else {
+      EXPECT_TRUE(is_redirect) << on_before_browse_ct_;
+    }
+
+    on_before_browse_ct_++;
+
+    VerifyState(kOnBeforeBrowse, request, nullptr);
+
+    if (mode_ == ABORT_BEFORE_BROWSE) {
+      SetSignalCompletionWhenAllBrowsersClose(false);
+      CloseBrowser(browser, false);
+    }
+
+    return false;
+  }
+
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    if (request_id_ == 0U) {
+      // This is the first callback that provides a request ID.
+      request_id_ = request->GetIdentifier();
+      EXPECT_GT(request_id_, 0U);
+    }
+
+    VerifyState(kGetResourceRequestHandler, request, nullptr);
+
+    EXPECT_TRUE(is_navigation);
+    EXPECT_FALSE(is_download);
+    EXPECT_STREQ("null", request_initiator.ToString().c_str());
+
+    // Check expected default value.
+    if (custom_scheme_) {
+      // There is no default handling for custom schemes.
+      EXPECT_TRUE(disable_default_handling);
+    } else {
+      EXPECT_FALSE(disable_default_handling);
+      // If |unhandled_| is true then we don't want default handling of requests
+      // (e.g. attempts to resolve over the network).
+      disable_default_handling = unhandled_;
+    }
+
+    get_resource_request_handler_ct_++;
+
+    return this;
+  }
+
+  CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    VerifyState(kGetCookieAccessFilter, request, nullptr);
+
+    get_cookie_access_filter_ct_++;
+
+    return nullptr;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    VerifyState(kOnBeforeResourceLoad, request, nullptr);
+
+    on_before_resource_load_ct_++;
+
+    if (mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD) {
+      incomplete_callback_ = callback;
+
+      // Close the browser asynchronously to complete the test.
+      CloseBrowserAsync();
+      return RV_CONTINUE_ASYNC;
+    }
+
+    if (mode_ == MODIFY_BEFORE_RESOURCE_LOAD) {
+      // Expect this data in the request for future callbacks.
+      SetCustomHeader(request);
+    } else if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
+      // Redirect to this URL.
+      request->SetURL(GetURL(RESULT_HTML));
+    }
+
+    // Other continuation modes are tested by
+    // ResourceRequestHandlerTest.BeforeResourceLoad*.
+    return RV_CONTINUE;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    VerifyState(kGetResourceHandler, request, nullptr);
+
+    get_resource_handler_ct_++;
+
+    if (IsIncompleteRequestHandler()) {
+      // Close the browser asynchronously to complete the test.
+      CloseBrowserAsync();
+      return GetIncompleteResource();
+    }
+
+    const std::string& url = request->GetURL();
+    if (url == GetURL(RESULT_HTML) && mode_ == RESTART_RESOURCE_RESPONSE) {
+      if (get_resource_handler_ct_ == 1) {
+        // First request that will be restarted after response.
+        return GetOKResource();
+      } else {
+        // Restarted request.
+        if (unhandled_)
+          return nullptr;
+        return GetOKResource();
+      }
+    } else if (url == GetURL(RESULT_HTML)) {
+      if (unhandled_)
+        return nullptr;
+      return GetOKResource();
+    } else if (url == GetURL(REDIRECT_HTML) &&
+               mode_ == REDIRECT_RESOURCE_RESPONSE) {
+      if (get_resource_handler_ct_ == 1) {
+        // First request that will be redirected after response.
+        return GetOKResource();
+      } else {
+        // Redirected request.
+        if (unhandled_)
+          return nullptr;
+        return GetOKResource();
+      }
+    } else if (url == GetURL(REDIRECT_HTML) || url == GetURL(REDIRECT2_HTML)) {
+      std::string redirect_url;
+      if (mode_ == REDIRECT_REQUEST_HANDLER ||
+          mode_ == REDIRECT_RESOURCE_RESPONSE) {
+        EXPECT_STREQ(GetURL(REDIRECT_HTML), url.c_str());
+        redirect_url = GetURL(RESULT_HTML);
+      } else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
+        EXPECT_STREQ(GetURL(REDIRECT2_HTML), url.c_str());
+        redirect_url = GetURL(REDIRECT_HTML);
+      } else {
+        NOTREACHED();
+      }
+
+      return GetRedirectResource(redirect_url);
+    } else {
+      NOTREACHED();
+      return nullptr;
+    }
+  }
+
+  void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response,
+                          CefString& new_url) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    VerifyState(kOnResourceRedirect, request, response);
+
+    if (mode_ == REDIRECT_REQUEST_HANDLER ||
+        mode_ == REDIRECT_RESOURCE_RESPONSE) {
+      // The URL redirected to from GetResourceHandler or OnResourceResponse.
+      EXPECT_STREQ(GetURL(RESULT_HTML), new_url.ToString().c_str());
+    } else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
+      if (on_resource_redirect_ct_ == 0) {
+        // The URL redirected to from GetResourceHandler.
+        EXPECT_STREQ(GetURL(REDIRECT_HTML), new_url.ToString().c_str());
+        // Redirect again.
+        new_url = GetURL(RESULT_HTML);
+      } else {
+        NOTREACHED();
+      }
+    }
+
+    on_resource_redirect_ct_++;
+  }
+
+  bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    VerifyState(kOnResourceResponse, request, response);
+
+    on_resource_response_ct_++;
+
+    if (on_resource_response_ct_ == 1) {
+      if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
+        // Redirect the request to this URL.
+        request->SetURL(GetURL(RESULT_HTML));
+        return true;
+      } else if (mode_ == RESTART_RESOURCE_RESPONSE) {
+        // Restart the request loading this data.
+        SetCustomHeader(request);
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    VerifyState(kGetResourceResponseFilter, request, response);
+
+    get_resource_response_filter_ct_++;
+
+    return nullptr;
+  }
+
+  void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefRequest> request,
+                              CefRefPtr<CefResponse> response,
+                              URLRequestStatus status,
+                              int64 received_content_length) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    VerifyState(kOnResourceLoadComplete, request, response);
+
+    if (unhandled_ || IsIncomplete() || IsAborted()) {
+      EXPECT_EQ(UR_FAILED, status);
+      EXPECT_EQ(0, received_content_length);
+    } else {
+      EXPECT_EQ(UR_SUCCESS, status);
+      EXPECT_EQ(static_cast<int64>(GetResponseBody().length()),
+                received_content_length);
+    }
+
+    on_resource_load_complete_ct_++;
+
+    if (IsIncomplete()) {
+      MaybeDestroyTest(false);
+    }
+  }
+
+  void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefRequest> request,
+                           bool& allow_os_execution) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    EXPECT_TRUE(custom_scheme_);
+    EXPECT_TRUE(unhandled_);
+
+    // Check expected default value.
+    EXPECT_FALSE(allow_os_execution);
+
+    VerifyState(kOnProtocolExecution, request, nullptr);
+    on_protocol_execution_ct_++;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_UI_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    EXPECT_TRUE(frame->IsMain());
+
+    if (unhandled_)
+      EXPECT_EQ(httpStatusCode, 0);
+    else
+      EXPECT_EQ(httpStatusCode, 200);
+
+    on_load_end_ct_++;
+
+    TestHandler::OnLoadEnd(browser, frame, httpStatusCode);
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    if (mode_ == RESTART_RESOURCE_RESPONSE) {
+      EXPECT_EQ(1, on_before_browse_ct_);
+      EXPECT_EQ(2, get_resource_request_handler_ct_);
+      EXPECT_EQ(2, get_cookie_access_filter_ct_);
+      EXPECT_EQ(2, on_before_resource_load_ct_);
+      EXPECT_EQ(2, get_resource_handler_ct_);
+      EXPECT_EQ(0, on_resource_redirect_ct_);
+      // Unhandled requests won't see a call to GetResourceResponseFilter or
+      // OnResourceResponse. In this case we're restarting from inside
+      // OnResourceResponse.
+      if (unhandled_) {
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+        EXPECT_EQ(1, on_resource_response_ct_);
+      } else {
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+        EXPECT_EQ(2, on_resource_response_ct_);
+      }
+    } else if (IsLoad()) {
+      EXPECT_EQ(1, on_before_browse_ct_);
+      EXPECT_EQ(1, get_resource_request_handler_ct_);
+      EXPECT_EQ(1, get_cookie_access_filter_ct_);
+      EXPECT_EQ(1, on_before_resource_load_ct_);
+      EXPECT_EQ(1, get_resource_handler_ct_);
+      EXPECT_EQ(0, on_resource_redirect_ct_);
+
+      // Unhandled requests won't see a call to GetResourceResponseFilter
+      // or OnResourceResponse.
+      if (unhandled_) {
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+        EXPECT_EQ(0, on_resource_response_ct_);
+      } else {
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+        EXPECT_EQ(1, on_resource_response_ct_);
+      }
+    } else if (IsRedirect()) {
+      EXPECT_EQ(2, on_before_browse_ct_);
+      EXPECT_EQ(2, get_resource_request_handler_ct_);
+      EXPECT_EQ(2, get_cookie_access_filter_ct_);
+      EXPECT_EQ(2, on_before_resource_load_ct_);
+      if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
+        EXPECT_EQ(1, get_resource_handler_ct_);
+      } else {
+        EXPECT_EQ(2, get_resource_handler_ct_);
+      }
+      EXPECT_EQ(1, on_resource_redirect_ct_);
+
+      // Unhandled requests won't see a call to GetResourceResponseFilter.
+      if (unhandled_) {
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+      } else {
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+      }
+
+      // Unhandled requests won't see a call to OnResourceResponse.
+      if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
+        // In this case we're redirecting from inside OnResourceResponse.
+        if (unhandled_) {
+          EXPECT_EQ(1, on_resource_response_ct_);
+        } else {
+          EXPECT_EQ(2, on_resource_response_ct_);
+        }
+      } else {
+        if (unhandled_) {
+          EXPECT_EQ(0, on_resource_response_ct_);
+        } else {
+          EXPECT_EQ(1, on_resource_response_ct_);
+        }
+      }
+    } else if (IsIncomplete()) {
+      EXPECT_EQ(1, on_before_browse_ct_);
+      EXPECT_EQ(1, get_resource_request_handler_ct_);
+      EXPECT_EQ(1, get_cookie_access_filter_ct_);
+      EXPECT_EQ(1, on_before_resource_load_ct_);
+
+      if (IsIncompleteRequestHandler()) {
+        EXPECT_EQ(1, get_resource_handler_ct_);
+      } else {
+        EXPECT_EQ(0, get_resource_handler_ct_);
+      }
+
+      EXPECT_EQ(0, on_resource_redirect_ct_);
+
+      if (mode_ == INCOMPLETE_REQUEST_HANDLER_READ) {
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+        EXPECT_EQ(1, on_resource_response_ct_);
+      } else {
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+        EXPECT_EQ(0, on_resource_response_ct_);
+      }
+    } else if (IsAborted()) {
+      EXPECT_EQ(1, on_before_browse_ct_);
+      if (custom_scheme_) {
+        EXPECT_EQ(0, get_resource_request_handler_ct_);
+        EXPECT_EQ(0, get_cookie_access_filter_ct_);
+      } else {
+        // The callbacks executed for standard schemes may vary based on timing.
+      }
+      EXPECT_EQ(0, on_before_resource_load_ct_);
+      EXPECT_EQ(0, get_resource_handler_ct_);
+      EXPECT_EQ(0, on_resource_redirect_ct_);
+      EXPECT_EQ(0, get_resource_response_filter_ct_);
+      EXPECT_EQ(0, on_resource_response_ct_);
+    } else {
+      NOTREACHED();
+    }
+
+    EXPECT_EQ(resource_handler_created_ct_, resource_handler_destroyed_ct_);
+
+    if (IsAborted()) {
+      EXPECT_EQ(0, on_resource_load_complete_ct_);
+    } else {
+      EXPECT_EQ(1, on_resource_load_complete_ct_);
+    }
+
+    if (IsIncomplete() || IsAborted()) {
+      EXPECT_EQ(0, on_load_end_ct_);
+    } else {
+      EXPECT_EQ(1, on_load_end_ct_);
+    }
+
+    if (custom_scheme_ && unhandled_ && !(IsIncomplete() || IsAborted())) {
+      EXPECT_EQ(1, on_protocol_execution_ct_);
+    } else {
+      EXPECT_EQ(0, on_protocol_execution_ct_);
+    }
+
+    TestHandler::DestroyTest();
+
+    if (!SignalCompletionWhenAllBrowsersClose()) {
+      // Complete asynchronously so the call stack has a chance to unwind.
+      CefPostTask(TID_UI, base::Bind(&BasicResponseTest::TestComplete, this));
+    }
+  }
+
+ private:
+  enum TestUrl {
+    RESULT_HTML,
+    REDIRECT_HTML,
+    REDIRECT2_HTML,
+  };
+
+  const char* GetURL(TestUrl url) const {
+    if (custom_scheme_) {
+      if (url == RESULT_HTML)
+        return "rrhcustom://test.com/result.html";
+      if (url == REDIRECT_HTML)
+        return "rrhcustom://test.com/redirect.html";
+      if (url == REDIRECT2_HTML)
+        return "rrhcustom://test.com/redirect2.html";
+    } else {
+      if (url == RESULT_HTML)
+        return "http://test.com/result.html";
+      if (url == REDIRECT_HTML)
+        return "http://test.com/redirect.html";
+      if (url == REDIRECT2_HTML)
+        return "http://test.com/redirect2.html";
+    }
+
+    NOTREACHED();
+    return "";
+  }
+
+  const char* GetStartupURL() const {
+    if (IsLoad() || IsIncomplete() || IsAborted()) {
+      return GetURL(RESULT_HTML);
+    } else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
+      return GetURL(REDIRECT2_HTML);
+    } else if (IsRedirect()) {
+      return GetURL(REDIRECT_HTML);
+    }
+
+    NOTREACHED();
+    return "";
+  }
+
+  std::string GetResponseBody() const {
+    return "<html><body>Response</body></html>";
+  }
+  std::string GetRedirectBody() const {
+    return "<html><body>Redirect</body></html>";
+  }
+
+  base::Closure GetResourceDestroyCallback() {
+    resource_handler_created_ct_++;
+    return base::Bind(&BasicResponseTest::MaybeDestroyTest, this, true);
+  }
+
+  bool GetCallbackResourceHandlerMode(CallbackResourceHandler::Mode& mode) {
+    switch (mode_) {
+      case IMMEDIATE_REQUEST_HANDLER_OPEN:
+        mode = CallbackResourceHandler::IMMEDIATE_OPEN;
+        return true;
+      case IMMEDIATE_REQUEST_HANDLER_READ:
+        mode = CallbackResourceHandler::IMMEDIATE_READ;
+        return true;
+      case IMMEDIATE_REQUEST_HANDLER_ALL:
+        mode = CallbackResourceHandler::IMMEDIATE_ALL;
+        return true;
+      case DELAYED_REQUEST_HANDLER_OPEN:
+        mode = CallbackResourceHandler::DELAYED_OPEN;
+        return true;
+      case DELAYED_REQUEST_HANDLER_READ:
+        mode = CallbackResourceHandler::DELAYED_READ;
+        return true;
+      case DELAYED_REQUEST_HANDLER_ALL:
+        mode = CallbackResourceHandler::DELAYED_ALL;
+        return true;
+      default:
+        break;
+    }
+    return false;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResource(int status_code,
+                                            const CefString& status_text,
+                                            const CefString& mime_type,
+                                            CefResponse::HeaderMap header_map,
+                                            const std::string& body) {
+    CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+        const_cast<char*>(body.c_str()), body.size());
+
+    CallbackResourceHandler::Mode handler_mode;
+    if (GetCallbackResourceHandlerMode(handler_mode)) {
+      return new CallbackResourceHandler(handler_mode, status_code, status_text,
+                                         mime_type, header_map, stream,
+                                         GetResourceDestroyCallback());
+    }
+
+    return new NormalResourceHandler(status_code, status_text, mime_type,
+                                     header_map, stream,
+                                     GetResourceDestroyCallback());
+  }
+
+  CefRefPtr<CefResourceHandler> GetOKResource() {
+    return GetResource(200, "OK", "text/html", CefResponse::HeaderMap(),
+                       GetResponseBody());
+  }
+
+  CefRefPtr<CefResourceHandler> GetRedirectResource(
+      const std::string& redirect_url) {
+    CefResponse::HeaderMap headerMap;
+    headerMap.insert(std::make_pair("Location", redirect_url));
+
+    return GetResource(307, "Temporary Redirect", "text/html", headerMap,
+                       GetRedirectBody());
+  }
+
+  CefRefPtr<CefResourceHandler> GetIncompleteResource() {
+    if (TestOldResourceAPI()) {
+      return new IncompleteResourceHandlerOld(
+          mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
+              ? IncompleteResourceHandlerOld::BLOCK_PROCESS_REQUEST
+              : IncompleteResourceHandlerOld::BLOCK_READ_RESPONSE,
+          "text/html", GetResourceDestroyCallback());
+    }
+
+    return new IncompleteResourceHandler(
+        mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
+            ? IncompleteResourceHandler::BLOCK_OPEN
+            : IncompleteResourceHandler::BLOCK_READ,
+        "text/html", GetResourceDestroyCallback());
+  }
+
+  bool IsLoad() const {
+    return mode_ == LOAD || mode_ == MODIFY_BEFORE_RESOURCE_LOAD ||
+           mode_ == RESTART_RESOURCE_RESPONSE ||
+           mode_ == IMMEDIATE_REQUEST_HANDLER_OPEN ||
+           mode_ == IMMEDIATE_REQUEST_HANDLER_READ ||
+           mode_ == IMMEDIATE_REQUEST_HANDLER_ALL ||
+           mode_ == DELAYED_REQUEST_HANDLER_OPEN ||
+           mode_ == DELAYED_REQUEST_HANDLER_READ ||
+           mode_ == DELAYED_REQUEST_HANDLER_ALL;
+  }
+
+  bool IsIncompleteRequestHandler() const {
+    return mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN ||
+           mode_ == INCOMPLETE_REQUEST_HANDLER_READ;
+  }
+
+  bool IsIncomplete() const {
+    return mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
+           IsIncompleteRequestHandler();
+  }
+
+  bool IsAborted() const {
+    return mode_ == ABORT_AFTER_CREATED || mode_ == ABORT_BEFORE_BROWSE;
+  }
+
+  bool IsRedirect() const {
+    return mode_ == REDIRECT_BEFORE_RESOURCE_LOAD ||
+           mode_ == REDIRECT_REQUEST_HANDLER ||
+           mode_ == REDIRECT_RESOURCE_REDIRECT ||
+           mode_ == REDIRECT_RESOURCE_RESPONSE;
+  }
+
+  static void SetCustomHeader(CefRefPtr<CefRequest> request) {
+    EXPECT_FALSE(request->IsReadOnly());
+    request->SetHeaderByName("X-Custom-Header", "value", false);
+  }
+
+  static std::string GetCustomHeader(CefRefPtr<CefRequest> request) {
+    return request->GetHeaderByName("X-Custom-Header");
+  }
+
+  // Resource-related callbacks.
+  enum Callback {
+    kOnBeforeBrowse,
+    kGetResourceRequestHandler,
+    kGetCookieAccessFilter,
+    kOnBeforeResourceLoad,
+    kGetResourceHandler,
+    kOnResourceRedirect,
+    kOnResourceResponse,
+    kGetResourceResponseFilter,
+    kOnResourceLoadComplete,
+    kOnProtocolExecution,
+  };
+
+  bool ShouldHaveResponse(Callback callback) const {
+    return callback >= kOnResourceRedirect &&
+           callback <= kOnResourceLoadComplete;
+  }
+
+  bool ShouldHaveWritableRequest(Callback callback) const {
+    return callback == kOnBeforeResourceLoad || callback == kOnResourceResponse;
+  }
+
+  void VerifyState(Callback callback,
+                   CefRefPtr<CefRequest> request,
+                   CefRefPtr<CefResponse> response) const {
+    EXPECT_TRUE(request) << callback;
+
+    if (ShouldHaveResponse(callback)) {
+      EXPECT_TRUE(response) << callback;
+      EXPECT_TRUE(response->IsReadOnly()) << callback;
+    } else {
+      EXPECT_FALSE(response) << callback;
+    }
+
+    if (ShouldHaveWritableRequest(callback)) {
+      EXPECT_FALSE(request->IsReadOnly()) << callback;
+    } else {
+      EXPECT_TRUE(request->IsReadOnly()) << callback;
+    }
+
+    if (callback == kOnBeforeBrowse) {
+      // Browser-side navigation no longer exposes the actual request
+      // information.
+      EXPECT_EQ(0U, request->GetIdentifier()) << callback;
+    } else {
+      // All resource-related callbacks share the same request ID.
+      EXPECT_EQ(request_id_, request->GetIdentifier()) << callback;
+    }
+
+    if (IsLoad() || IsIncomplete() || IsAborted()) {
+      EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
+      EXPECT_STREQ(GetURL(RESULT_HTML), request->GetURL().ToString().c_str())
+          << callback;
+
+      // Expect the header for all callbacks following the callback that
+      // initially sets it.
+      const std::string& custom_header = GetCustomHeader(request);
+      if ((mode_ == RESTART_RESOURCE_RESPONSE &&
+           on_resource_response_ct_ > 0) ||
+          (mode_ == MODIFY_BEFORE_RESOURCE_LOAD &&
+           on_before_resource_load_ct_ > 0)) {
+        EXPECT_STREQ("value", custom_header.c_str()) << callback;
+      } else {
+        EXPECT_STREQ("", custom_header.c_str()) << callback;
+      }
+
+      if (response)
+        VerifyOKResponse(callback, response);
+    } else if (IsRedirect()) {
+      EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
+      if (on_before_browse_ct_ == 1) {
+        // Before the redirect.
+        EXPECT_STREQ(GetStartupURL(), request->GetURL().ToString().c_str())
+            << callback;
+      } else if (on_before_browse_ct_ == 2) {
+        // After the redirect.
+        EXPECT_STREQ(GetURL(RESULT_HTML), request->GetURL().ToString().c_str())
+            << callback;
+      } else {
+        NOTREACHED() << callback;
+      }
+
+      if (response) {
+        if (callback == kOnResourceRedirect) {
+          // Before the redirect.
+          VerifyRedirectResponse(callback, response);
+        } else {
+          // After the redirect.
+          VerifyOKResponse(callback, response);
+        }
+      }
+    } else {
+      NOTREACHED() << callback;
+    }
+  }
+
+  void VerifyOKResponse(Callback callback,
+                        CefRefPtr<CefResponse> response) const {
+    // True for the first response in cases where we're redirecting/restarting
+    // from inside OnResourceResponse (e.g. the first response always succeeds).
+    const bool override_unhandled = unhandled_ &&
+                                    (mode_ == REDIRECT_RESOURCE_RESPONSE ||
+                                     mode_ == RESTART_RESOURCE_RESPONSE) &&
+                                    get_resource_handler_ct_ == 1;
+
+    // True for tests where the request will be incomplete and never receive a
+    // response.
+    const bool incomplete_unhandled =
+        (mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
+         mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN ||
+         (IsAborted() && !custom_scheme_));
+
+    if ((unhandled_ && !override_unhandled) || incomplete_unhandled) {
+      if (incomplete_unhandled) {
+        EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
+      } else {
+        EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, response->GetError()) << callback;
+      }
+      EXPECT_EQ(0, response->GetStatus()) << callback;
+      EXPECT_STREQ("", response->GetStatusText().ToString().c_str())
+          << callback;
+      EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
+      EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
+      EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
+    } else {
+      if ((mode_ == INCOMPLETE_REQUEST_HANDLER_READ || IsAborted()) &&
+          callback == kOnResourceLoadComplete) {
+        // We got a response, but we also got aborted.
+        EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
+      } else {
+        EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
+      }
+      EXPECT_EQ(200, response->GetStatus()) << callback;
+      EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str())
+          << callback;
+      EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
+      EXPECT_STREQ("text/html", response->GetMimeType().ToString().c_str())
+          << callback;
+      EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
+    }
+  }
+
+  void VerifyRedirectResponse(Callback callback,
+                              CefRefPtr<CefResponse> response) const {
+    EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
+    EXPECT_EQ(307, response->GetStatus()) << callback;
+    const std::string& status_text = response->GetStatusText();
+    EXPECT_TRUE(status_text == "Internal Redirect" ||
+                status_text == "Temporary Redirect")
+        << status_text << callback;
+    EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
+    EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
+    EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
+  }
+
+  void CloseBrowserAsync() {
+    EXPECT_TRUE(IsIncomplete());
+    SetSignalCompletionWhenAllBrowsersClose(false);
+    CefPostDelayedTask(
+        TID_UI, base::Bind(&TestHandler::CloseBrowser, GetBrowser(), false),
+        100);
+  }
+
+  void MaybeDestroyTest(bool from_handler) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&BasicResponseTest::MaybeDestroyTest, this,
+                                     from_handler));
+      return;
+    }
+
+    if (from_handler) {
+      resource_handler_destroyed_ct_++;
+    }
+
+    bool destroy_test = false;
+    if (IsIncomplete()) {
+      // Destroy the test if we got OnResourceLoadComplete and either the
+      // resource handler will never complete or it was destroyed.
+      destroy_test =
+          on_resource_load_complete_ct_ > 0 &&
+          (!IsIncompleteRequestHandler() ||
+           resource_handler_destroyed_ct_ == resource_handler_created_ct_);
+    } else {
+      // Destroy the test if we got OnLoadEnd and the expected number of
+      // resource handlers were destroyed.
+      destroy_test = on_load_end_ct_ > 0 && resource_handler_destroyed_ct_ ==
+                                                resource_handler_created_ct_;
+    }
+
+    if (destroy_test) {
+      DestroyTest();
+    }
+  }
+
+  const TestMode mode_;
+  const bool custom_scheme_;
+  const bool unhandled_;
+
+  int browser_id_ = 0;
+  uint64 request_id_ = 0U;
+
+  int resource_handler_created_ct_ = 0;
+
+  int on_before_browse_ct_ = 0;
+  int on_load_end_ct_ = 0;
+
+  int get_resource_request_handler_ct_ = 0;
+  int on_before_resource_load_ct_ = 0;
+  int get_cookie_access_filter_ct_ = 0;
+  int get_resource_handler_ct_ = 0;
+  int on_resource_redirect_ct_ = 0;
+  int on_resource_response_ct_ = 0;
+  int get_resource_response_filter_ct_ = 0;
+  int on_resource_load_complete_ct_ = 0;
+  int on_protocol_execution_ct_ = 0;
+  int resource_handler_destroyed_ct_ = 0;
+
+  // Used with INCOMPLETE_BEFORE_RESOURCE_LOAD.
+  CefRefPtr<CefRequestCallback> incomplete_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(BasicResponseTest);
+  IMPLEMENT_REFCOUNTING(BasicResponseTest);
+};
+
+}  // namespace
+
+#define BASIC_TEST(name, test_mode, custom, unhandled)            \
+  TEST(ResourceRequestHandlerTest, Basic##name) {                 \
+    CefRefPtr<BasicResponseTest> handler = new BasicResponseTest( \
+        BasicResponseTest::test_mode, custom, unhandled);         \
+    handler->ExecuteTest();                                       \
+    ReleaseAndWaitForDestructor(handler);                         \
+  }
+
+#define BASIC_TEST_ALL_MODES(name, custom, unhandled)                          \
+  BASIC_TEST(name##Load, LOAD, custom, unhandled)                              \
+  BASIC_TEST(name##AbortAfterCreated, ABORT_AFTER_CREATED, custom, unhandled)  \
+  BASIC_TEST(name##AbortBeforeBrowse, ABORT_BEFORE_BROWSE, custom, unhandled)  \
+  BASIC_TEST(name##ModifyBeforeResourceLoad, MODIFY_BEFORE_RESOURCE_LOAD,      \
+             custom, unhandled)                                                \
+  BASIC_TEST(name##RedirectBeforeResourceLoad, REDIRECT_BEFORE_RESOURCE_LOAD,  \
+             custom, unhandled)                                                \
+  BASIC_TEST(name##RedirectRequestHandler, REDIRECT_REQUEST_HANDLER, custom,   \
+             unhandled)                                                        \
+  BASIC_TEST(name##RedirectResourceRedirect, REDIRECT_RESOURCE_REDIRECT,       \
+             custom, unhandled)                                                \
+  BASIC_TEST(name##RedirectResourceResponse, REDIRECT_RESOURCE_RESPONSE,       \
+             custom, unhandled)                                                \
+  BASIC_TEST(name##RestartResourceResponse, RESTART_RESOURCE_RESPONSE, custom, \
+             unhandled)
+
+// Tests only supported in handled mode.
+#define BASIC_TEST_HANDLED_MODES(name, custom)                                \
+  BASIC_TEST(name##ImmediateRequestHandlerOpen,                               \
+             IMMEDIATE_REQUEST_HANDLER_OPEN, custom, false)                   \
+  BASIC_TEST(name##ImmediateRequestHandlerRead,                               \
+             IMMEDIATE_REQUEST_HANDLER_READ, custom, false)                   \
+  BASIC_TEST(name##ImmediateRequestHandlerAll, IMMEDIATE_REQUEST_HANDLER_ALL, \
+             custom, false)                                                   \
+  BASIC_TEST(name##DelayedRequestHandlerOpen, DELAYED_REQUEST_HANDLER_OPEN,   \
+             custom, false)                                                   \
+  BASIC_TEST(name##DelayedRequestHandlerRead, DELAYED_REQUEST_HANDLER_READ,   \
+             custom, false)                                                   \
+  BASIC_TEST(name##DelayedRequestHandlerAll, DELAYED_REQUEST_HANDLER_ALL,     \
+             custom, false)                                                   \
+  BASIC_TEST(name##IncompleteBeforeResourceLoad,                              \
+             INCOMPLETE_BEFORE_RESOURCE_LOAD, custom, false)                  \
+  BASIC_TEST(name##IncompleteRequestHandlerOpen,                              \
+             INCOMPLETE_REQUEST_HANDLER_OPEN, custom, false)                  \
+  BASIC_TEST(name##IncompleteRequestHandlerRead,                              \
+             INCOMPLETE_REQUEST_HANDLER_READ, custom, false)
+
+BASIC_TEST_ALL_MODES(StandardHandled, false, false)
+BASIC_TEST_ALL_MODES(StandardUnhandled, false, true)
+BASIC_TEST_ALL_MODES(CustomHandled, true, false)
+BASIC_TEST_ALL_MODES(CustomUnhandled, true, true)
+
+BASIC_TEST_HANDLED_MODES(StandardHandled, false)
+BASIC_TEST_HANDLED_MODES(CustomHandled, true)
+
+namespace {
+
+const char kSubresourceProcessMsg[] = "SubresourceMsg";
+
+class SubresourceResponseTest : public RoutingTestHandler {
+ public:
+  enum TestMode {
+    // Normal load, nothing fancy.
+    LOAD,
+
+    // Don't continue from OnBeforeResourceLoad, then close the browser to
+    // verify destruction handling of in-progress requests.
+    INCOMPLETE_BEFORE_RESOURCE_LOAD,
+
+    // Modify the request (add headers) in OnBeforeResourceLoad.
+    MODIFY_BEFORE_RESOURCE_LOAD,
+
+    // Redirect the request (change the URL) in OnBeforeResourceLoad.
+    REDIRECT_BEFORE_RESOURCE_LOAD,
+
+    // Return a CefResourceHandler from GetResourceHandler that continues
+    // immediately by using the callback object instead of the return value.
+    IMMEDIATE_REQUEST_HANDLER_OPEN,
+    IMMEDIATE_REQUEST_HANDLER_READ,
+    IMMEDIATE_REQUEST_HANDLER_ALL,
+
+    // Return a CefResourceHandler from GetResourceHandler that continues with
+    // a delay by using the callback object.
+    DELAYED_REQUEST_HANDLER_OPEN,
+    DELAYED_REQUEST_HANDLER_READ,
+    DELAYED_REQUEST_HANDLER_ALL,
+
+    // Return a CefResourceHandler from GetResourceHandler that never completes,
+    // then close the browser to verify destruction handling of in-progress
+    // requests.
+    INCOMPLETE_REQUEST_HANDLER_OPEN,
+    INCOMPLETE_REQUEST_HANDLER_READ,
+
+    // Redirect the request using a CefResourceHandler returned from
+    // GetResourceHandler.
+    REDIRECT_REQUEST_HANDLER,
+
+    // Redirect the request (change the URL) an additional time in
+    // OnResourceRedirect after using a CefResourceHandler returned from
+    // GetResourceHandler for the first redirect.
+    REDIRECT_RESOURCE_REDIRECT,
+
+    // Redirect the request (change the URL) in OnResourceResponse.
+    REDIRECT_RESOURCE_RESPONSE,
+
+    // Restart the request (add headers) in OnResourceResponse.
+    RESTART_RESOURCE_RESPONSE,
+  };
+
+  // If |custom_scheme| is true all requests will use a custom scheme.
+  // If |unhandled| is true the final request (after any redirects) will be
+  // unhandled, meaning that default handling is disabled and GetResourceHandler
+  // returns null.
+  // If |subframe| is true the resource will be loaded in an iframe.
+  SubresourceResponseTest(TestMode mode,
+                          bool custom_scheme,
+                          bool unhandled,
+                          bool subframe)
+      : mode_(mode),
+        custom_scheme_(custom_scheme),
+        unhandled_(unhandled),
+        subframe_(subframe) {}
+
+  void RunTest() override {
+    CreateBrowser(GetMainURL());
+    SetTestTimeout();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_UI_THREAD();
+    if (browser_id_ == 0) {
+      // This is the first callback that provides a browser ID.
+      browser_id_ = browser->GetIdentifier();
+      EXPECT_GT(browser_id_, 0);
+    } else {
+      EXPECT_EQ(browser_id_, browser->GetIdentifier());
+    }
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_TRUE(subframe_);
+    } else {
+      EXPECT_FALSE(true);  // Not reached.
+    }
+
+    EXPECT_FALSE(user_gesture);
+    EXPECT_FALSE(is_redirect);
+
+    on_before_browse_ct_++;
+    return false;
+  }
+
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_TRUE(subframe_);
+    }
+
+    if (IsMainURL(request->GetURL()) || IsSubURL(request->GetURL())) {
+      // Track the frame ID that we'll expect for resource callbacks.
+      // Do this here instead of OnBeforeBrowse because OnBeforeBrowse may
+      // return -4 (kInvalidFrameId) for the initial navigation.
+      if (frame_id_ == 0) {
+        if (subframe_) {
+          if (IsSubURL(request->GetURL()))
+            frame_id_ = frame->GetIdentifier();
+        } else {
+          frame_id_ = frame->GetIdentifier();
+        }
+      }
+      return this;
+    }
+
+    VerifyFrame(kGetResourceRequestHandler, frame);
+
+    if (request_id_ == 0U) {
+      // This is the first callback that provides a request ID.
+      request_id_ = request->GetIdentifier();
+      EXPECT_GT(request_id_, 0U);
+    }
+
+    VerifyState(kGetResourceRequestHandler, request, nullptr);
+
+    EXPECT_FALSE(is_navigation);
+    EXPECT_FALSE(is_download);
+    EXPECT_STREQ(GetOrigin(), request_initiator.ToString().c_str());
+
+    // Check expected default value.
+    if (custom_scheme_) {
+      // There is no default handling for custom schemes.
+      EXPECT_TRUE(disable_default_handling);
+    } else {
+      EXPECT_FALSE(disable_default_handling);
+      // If |unhandled_| is true then we don't want default handling of requests
+      // (e.g. attempts to resolve over the network).
+      disable_default_handling = unhandled_;
+    }
+
+    get_resource_request_handler_ct_++;
+
+    return this;
+  }
+
+  CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+      return nullptr;
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_TRUE(subframe_);
+      return nullptr;
+    }
+
+    VerifyFrame(kGetCookieAccessFilter, frame);
+
+    VerifyState(kGetCookieAccessFilter, request, nullptr);
+
+    get_cookie_access_filter_ct_++;
+
+    return nullptr;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+      return RV_CONTINUE;
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_TRUE(subframe_);
+      return RV_CONTINUE;
+    }
+
+    VerifyFrame(kOnBeforeResourceLoad, frame);
+
+    VerifyState(kOnBeforeResourceLoad, request, nullptr);
+
+    on_before_resource_load_ct_++;
+
+    if (mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD) {
+      incomplete_callback_ = callback;
+
+      // Close the browser asynchronously to complete the test.
+      CloseBrowserAsync();
+      return RV_CONTINUE_ASYNC;
+    }
+
+    if (mode_ == MODIFY_BEFORE_RESOURCE_LOAD) {
+      // Expect this data in the request for future callbacks.
+      SetCustomHeader(request);
+    } else if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
+      // Redirect to this URL.
+      request->SetURL(GetURL(RESULT_JS));
+    }
+
+    // Other continuation modes are tested by
+    // ResourceRequestHandlerTest.BeforeResourceLoad*.
+    return RV_CONTINUE;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+      return GetMainResource();
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_TRUE(subframe_);
+      return GetSubResource();
+    }
+
+    VerifyFrame(kGetResourceHandler, frame);
+
+    VerifyState(kGetResourceHandler, request, nullptr);
+
+    get_resource_handler_ct_++;
+
+    if (IsIncompleteRequestHandler()) {
+      // Close the browser asynchronously to complete the test.
+      CloseBrowserAsync();
+      return GetIncompleteResource();
+    }
+
+    const std::string& url = request->GetURL();
+    if (url == GetURL(RESULT_JS) && mode_ == RESTART_RESOURCE_RESPONSE) {
+      if (get_resource_handler_ct_ == 1) {
+        // First request that will be restarted after response.
+        return GetOKResource();
+      } else {
+        // Restarted request.
+        if (unhandled_)
+          return nullptr;
+        return GetOKResource();
+      }
+    } else if (url == GetURL(RESULT_JS)) {
+      if (unhandled_)
+        return nullptr;
+      return GetOKResource();
+    } else if (url == GetURL(REDIRECT_JS) &&
+               mode_ == REDIRECT_RESOURCE_RESPONSE) {
+      if (get_resource_handler_ct_ == 1) {
+        // First request that will be redirected after response.
+        return GetOKResource();
+      } else {
+        // Redirected request.
+        if (unhandled_)
+          return nullptr;
+        return GetOKResource();
+      }
+    } else if (url == GetURL(REDIRECT_JS) || url == GetURL(REDIRECT2_JS)) {
+      std::string redirect_url;
+      if (mode_ == REDIRECT_REQUEST_HANDLER ||
+          mode_ == REDIRECT_RESOURCE_RESPONSE) {
+        EXPECT_STREQ(GetURL(REDIRECT_JS), url.c_str());
+        redirect_url = GetURL(RESULT_JS);
+      } else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
+        EXPECT_STREQ(GetURL(REDIRECT2_JS), url.c_str());
+        redirect_url = GetURL(REDIRECT_JS);
+      } else {
+        NOTREACHED();
+      }
+
+      return GetRedirectResource(redirect_url);
+    } else {
+      NOTREACHED();
+      return nullptr;
+    }
+  }
+
+  void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response,
+                          CefString& new_url) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL()) || IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(true);  // Not reached.
+      return;
+    }
+
+    VerifyFrame(kOnResourceRedirect, frame);
+
+    VerifyState(kOnResourceRedirect, request, response);
+
+    if (mode_ == REDIRECT_REQUEST_HANDLER ||
+        mode_ == REDIRECT_RESOURCE_RESPONSE) {
+      // The URL redirected to from GetResourceHandler or OnResourceResponse.
+      EXPECT_STREQ(GetURL(RESULT_JS), new_url.ToString().c_str());
+    } else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
+      if (on_resource_redirect_ct_ == 0) {
+        // The URL redirected to from GetResourceHandler.
+        EXPECT_STREQ(GetURL(REDIRECT_JS), new_url.ToString().c_str());
+        // Redirect again.
+        new_url = GetURL(RESULT_JS);
+      } else {
+        NOTREACHED();
+      }
+    }
+
+    on_resource_redirect_ct_++;
+  }
+
+  bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+      return false;
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_TRUE(subframe_);
+      return false;
+    }
+
+    VerifyFrame(kOnResourceResponse, frame);
+
+    VerifyState(kOnResourceResponse, request, response);
+
+    on_resource_response_ct_++;
+
+    if (on_resource_response_ct_ == 1) {
+      if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
+        // Redirect the request to this URL.
+        request->SetURL(GetURL(RESULT_JS));
+        return true;
+      } else if (mode_ == RESTART_RESOURCE_RESPONSE) {
+        // Restart the request loading this data.
+        SetCustomHeader(request);
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+      return nullptr;
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_TRUE(subframe_);
+      return nullptr;
+    }
+
+    VerifyFrame(kGetResourceResponseFilter, frame);
+
+    VerifyState(kGetResourceResponseFilter, request, response);
+
+    get_resource_response_filter_ct_++;
+
+    return nullptr;
+  }
+
+  void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefRequest> request,
+                              CefRefPtr<CefResponse> response,
+                              URLRequestStatus status,
+                              int64 received_content_length) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL())) {
+      EXPECT_TRUE(frame->IsMain());
+      EXPECT_EQ(UR_SUCCESS, status);
+      EXPECT_EQ(static_cast<int64>(GetMainResponseBody().length()),
+                received_content_length);
+      return;
+    } else if (IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(frame->IsMain());
+      EXPECT_EQ(UR_SUCCESS, status);
+      EXPECT_EQ(static_cast<int64>(GetSubResponseBody().length()),
+                received_content_length);
+      EXPECT_TRUE(subframe_);
+      return;
+    }
+
+    VerifyFrame(kOnResourceLoadComplete, frame);
+
+    VerifyState(kOnResourceLoadComplete, request, response);
+
+    if (unhandled_ || IsIncomplete()) {
+      EXPECT_EQ(UR_FAILED, status);
+      EXPECT_EQ(0, received_content_length);
+    } else {
+      EXPECT_EQ(UR_SUCCESS, status);
+      EXPECT_EQ(static_cast<int64>(GetResponseBody().length()),
+                received_content_length);
+    }
+
+    on_resource_load_complete_ct_++;
+
+    if (IsIncomplete()) {
+      MaybeDestroyTest(false);
+    }
+  }
+
+  void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefRequest> request,
+                           bool& allow_os_execution) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    if (IsMainURL(request->GetURL()) || IsSubURL(request->GetURL())) {
+      EXPECT_FALSE(true);  // Not reached.
+      return;
+    }
+
+    VerifyFrame(kOnProtocolExecution, frame);
+
+    EXPECT_TRUE(custom_scheme_);
+    EXPECT_TRUE(unhandled_);
+
+    // Check expected default value.
+    EXPECT_FALSE(allow_os_execution);
+
+    VerifyState(kOnProtocolExecution, request, nullptr);
+    on_protocol_execution_ct_++;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_UI_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    EXPECT_EQ(httpStatusCode, 200);
+
+    on_load_end_ct_++;
+
+    TestHandler::OnLoadEnd(browser, frame, httpStatusCode);
+    MaybeDestroyTest(false);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    EXPECT_UI_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    EXPECT_STREQ(kSubresourceProcessMsg, request.ToString().c_str());
+
+    VerifyFrame(kOnQuery, frame);
+
+    callback->Success("");
+
+    on_query_ct_++;
+    MaybeDestroyTest(false);
+
+    return true;
+  }
+
+  void DestroyTest() override {
+    // Only called for the main and/or sub frame load.
+    if (subframe_) {
+      EXPECT_EQ(2, on_before_browse_ct_);
+    } else {
+      EXPECT_EQ(1, on_before_browse_ct_);
+    }
+
+    if (mode_ == RESTART_RESOURCE_RESPONSE) {
+      EXPECT_EQ(2, get_resource_request_handler_ct_);
+      EXPECT_EQ(2, get_cookie_access_filter_ct_);
+      EXPECT_EQ(2, on_before_resource_load_ct_);
+      EXPECT_EQ(2, get_resource_handler_ct_);
+      EXPECT_EQ(0, on_resource_redirect_ct_);
+      // Unhandled requests won't see a call to GetResourceResponseFilter or
+      // OnResourceResponse. In this case we're restarting from inside
+      // OnResourceResponse.
+      if (unhandled_) {
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+        EXPECT_EQ(1, on_resource_response_ct_);
+      } else {
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+        EXPECT_EQ(2, on_resource_response_ct_);
+      }
+    } else if (IsLoad()) {
+      EXPECT_EQ(1, get_resource_request_handler_ct_);
+      EXPECT_EQ(1, get_cookie_access_filter_ct_);
+      EXPECT_EQ(1, on_before_resource_load_ct_);
+      EXPECT_EQ(1, get_resource_handler_ct_);
+      EXPECT_EQ(0, on_resource_redirect_ct_);
+      // Unhandled requests won't see a call to GetResourceResponseFilter or
+      // OnResourceResponse.
+      if (unhandled_) {
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+        EXPECT_EQ(0, on_resource_response_ct_);
+      } else {
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+        EXPECT_EQ(1, on_resource_response_ct_);
+      }
+    } else if (IsRedirect()) {
+      EXPECT_EQ(2, get_resource_request_handler_ct_);
+      EXPECT_EQ(2, get_cookie_access_filter_ct_);
+      EXPECT_EQ(2, on_before_resource_load_ct_);
+      if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
+        EXPECT_EQ(1, get_resource_handler_ct_);
+      } else {
+        EXPECT_EQ(2, get_resource_handler_ct_);
+      }
+      EXPECT_EQ(1, on_resource_redirect_ct_);
+
+      // Unhandled requests won't see a call to GetResourceResponseFilter.
+      if (unhandled_)
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+      else
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+
+      // Unhandled requests won't see a call to OnResourceResponse.
+      if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
+        // In this case we're redirecting from inside OnResourceResponse.
+        if (unhandled_)
+          EXPECT_EQ(1, on_resource_response_ct_);
+        else
+          EXPECT_EQ(2, on_resource_response_ct_);
+      } else {
+        if (unhandled_)
+          EXPECT_EQ(0, on_resource_response_ct_);
+        else
+          EXPECT_EQ(1, on_resource_response_ct_);
+      }
+    } else if (IsIncomplete()) {
+      EXPECT_EQ(1, get_resource_request_handler_ct_);
+      EXPECT_EQ(1, get_cookie_access_filter_ct_);
+      EXPECT_EQ(1, on_before_resource_load_ct_);
+
+      if (IsIncompleteRequestHandler()) {
+        EXPECT_EQ(1, get_resource_handler_ct_);
+      } else {
+        EXPECT_EQ(0, get_resource_handler_ct_);
+      }
+
+      EXPECT_EQ(0, on_resource_redirect_ct_);
+
+      if (mode_ == INCOMPLETE_REQUEST_HANDLER_READ) {
+        EXPECT_EQ(1, get_resource_response_filter_ct_);
+        EXPECT_EQ(1, on_resource_response_ct_);
+      } else {
+        EXPECT_EQ(0, get_resource_response_filter_ct_);
+        EXPECT_EQ(0, on_resource_response_ct_);
+      }
+    } else {
+      NOTREACHED();
+    }
+
+    EXPECT_EQ(resource_handler_created_ct_, resource_handler_destroyed_ct_);
+    EXPECT_EQ(1, on_resource_load_complete_ct_);
+
+    // Only called for the main and/or sub frame load.
+    if (IsIncomplete()) {
+      EXPECT_EQ(0, on_load_end_ct_);
+    } else {
+      if (subframe_) {
+        EXPECT_EQ(2, on_load_end_ct_);
+      } else {
+        EXPECT_EQ(1, on_load_end_ct_);
+      }
+    }
+
+    if (unhandled_ || IsIncomplete()) {
+      EXPECT_EQ(0, on_query_ct_);
+    } else {
+      EXPECT_EQ(1, on_query_ct_);
+    }
+
+    if (custom_scheme_ && unhandled_ && !IsIncomplete()) {
+      EXPECT_EQ(1, on_protocol_execution_ct_);
+    } else {
+      EXPECT_EQ(0, on_protocol_execution_ct_);
+    }
+
+    TestHandler::DestroyTest();
+
+    if (!SignalCompletionWhenAllBrowsersClose()) {
+      // Complete asynchronously so the call stack has a chance to unwind.
+      CefPostTask(TID_UI,
+                  base::Bind(&SubresourceResponseTest::TestComplete, this));
+    }
+  }
+
+ private:
+  const char* GetMainURL() const {
+    if (custom_scheme_) {
+      return "rrhcustom://test.com/main.html";
+    } else {
+      return "http://test.com/main.html";
+    }
+  }
+
+  const char* GetSubURL() const {
+    if (custom_scheme_) {
+      return "rrhcustom://test.com/subframe.html";
+    } else {
+      return "http://test.com/subframe.html";
+    }
+  }
+
+  const char* GetOrigin() const {
+    if (custom_scheme_) {
+      return "rrhcustom://test.com";
+    } else {
+      return "http://test.com";
+    }
+  }
+
+  bool IsMainURL(const std::string& url) const { return url == GetMainURL(); }
+  bool IsSubURL(const std::string& url) const { return url == GetSubURL(); }
+
+  enum TestUrl {
+    RESULT_JS,
+    REDIRECT_JS,
+    REDIRECT2_JS,
+  };
+
+  const char* GetURL(TestUrl url) const {
+    if (custom_scheme_) {
+      if (url == RESULT_JS)
+        return "rrhcustom://test.com/result.js";
+      if (url == REDIRECT_JS)
+        return "rrhcustom://test.com/redirect.js";
+      if (url == REDIRECT2_JS)
+        return "rrhcustom://test.com/redirect2.js";
+    } else {
+      if (url == RESULT_JS)
+        return "http://test.com/result.js";
+      if (url == REDIRECT_JS)
+        return "http://test.com/redirect.js";
+      if (url == REDIRECT2_JS)
+        return "http://test.com/redirect2.js";
+    }
+
+    NOTREACHED();
+    return "";
+  }
+
+  const char* GetStartupURL() const {
+    if (IsLoad() || IsIncomplete()) {
+      return GetURL(RESULT_JS);
+    } else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
+      return GetURL(REDIRECT2_JS);
+    } else if (IsRedirect()) {
+      return GetURL(REDIRECT_JS);
+    }
+
+    NOTREACHED();
+    return "";
+  }
+
+  std::string GetMainResponseBody() const {
+    std::stringstream html;
+    html << "<html><head>";
+
+    if (subframe_) {
+      const std::string& url = GetSubURL();
+      html << "<iframe src=\"" << url << "\"></iframe>";
+    } else {
+      const std::string& url = GetStartupURL();
+      html << "<script type=\"text/javascript\" src=\"" << url
+           << "\"></script>";
+    }
+
+    html << "</head><body><p>Main</p></body></html>";
+    return html.str();
+  }
+
+  std::string GetSubResponseBody() const {
+    DCHECK(subframe_);
+
+    std::stringstream html;
+    html << "<html><head>";
+
+    const std::string& url = GetStartupURL();
+    html << "<script type=\"text/javascript\" src=\"" << url << "\"></script>";
+
+    html << "</head><body><p>Sub</p></body></html>";
+    return html.str();
+  }
+
+  std::string GetResponseBody() const {
+    return "window.testQuery({request:'" + std::string(kSubresourceProcessMsg) +
+           "'});";
+  }
+  std::string GetRedirectBody() const {
+    return "<html><body>Redirect</body></html>";
+  }
+
+  base::Closure GetResourceDestroyCallback() {
+    resource_handler_created_ct_++;
+    return base::Bind(&SubresourceResponseTest::MaybeDestroyTest, this, true);
+  }
+
+  bool GetCallbackResourceHandlerMode(CallbackResourceHandler::Mode& mode) {
+    switch (mode_) {
+      case IMMEDIATE_REQUEST_HANDLER_OPEN:
+        mode = CallbackResourceHandler::IMMEDIATE_OPEN;
+        return true;
+      case IMMEDIATE_REQUEST_HANDLER_READ:
+        mode = CallbackResourceHandler::IMMEDIATE_READ;
+        return true;
+      case IMMEDIATE_REQUEST_HANDLER_ALL:
+        mode = CallbackResourceHandler::IMMEDIATE_ALL;
+        return true;
+      case DELAYED_REQUEST_HANDLER_OPEN:
+        mode = CallbackResourceHandler::DELAYED_OPEN;
+        return true;
+      case DELAYED_REQUEST_HANDLER_READ:
+        mode = CallbackResourceHandler::DELAYED_READ;
+        return true;
+      case DELAYED_REQUEST_HANDLER_ALL:
+        mode = CallbackResourceHandler::DELAYED_ALL;
+        return true;
+      default:
+        break;
+    }
+    return false;
+  }
+
+  CefRefPtr<CefResourceHandler> GetResource(int status_code,
+                                            const CefString& status_text,
+                                            const CefString& mime_type,
+                                            CefResponse::HeaderMap header_map,
+                                            const std::string& body) {
+    CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+        const_cast<char*>(body.c_str()), body.size());
+
+    CallbackResourceHandler::Mode handler_mode;
+    if (GetCallbackResourceHandlerMode(handler_mode)) {
+      return new CallbackResourceHandler(handler_mode, status_code, status_text,
+                                         mime_type, header_map, stream,
+                                         GetResourceDestroyCallback());
+    }
+
+    return new NormalResourceHandler(status_code, status_text, mime_type,
+                                     header_map, stream,
+                                     GetResourceDestroyCallback());
+  }
+
+  CefRefPtr<CefResourceHandler> GetMainResource() {
+    return GetResource(200, "OK", "text/html", CefResponse::HeaderMap(),
+                       GetMainResponseBody());
+  }
+
+  CefRefPtr<CefResourceHandler> GetSubResource() {
+    return GetResource(200, "OK", "text/html", CefResponse::HeaderMap(),
+                       GetSubResponseBody());
+  }
+
+  CefRefPtr<CefResourceHandler> GetOKResource() {
+    return GetResource(200, "OK", "text/javascript", CefResponse::HeaderMap(),
+                       GetResponseBody());
+  }
+
+  CefRefPtr<CefResourceHandler> GetRedirectResource(
+      const std::string& redirect_url) {
+    CefResponse::HeaderMap headerMap;
+    headerMap.insert(std::make_pair("Location", redirect_url));
+
+    return GetResource(307, "Temporary Redirect", "text/javascript", headerMap,
+                       GetRedirectBody());
+  }
+
+  CefRefPtr<CefResourceHandler> GetIncompleteResource() {
+    if (TestOldResourceAPI()) {
+      return new IncompleteResourceHandlerOld(
+          mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
+              ? IncompleteResourceHandlerOld::BLOCK_PROCESS_REQUEST
+              : IncompleteResourceHandlerOld::BLOCK_READ_RESPONSE,
+          "text/javascript", GetResourceDestroyCallback());
+    }
+
+    return new IncompleteResourceHandler(
+        mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
+            ? IncompleteResourceHandler::BLOCK_OPEN
+            : IncompleteResourceHandler::BLOCK_READ,
+        "text/javascript", GetResourceDestroyCallback());
+  }
+
+  bool IsLoad() const {
+    return mode_ == LOAD || mode_ == MODIFY_BEFORE_RESOURCE_LOAD ||
+           mode_ == RESTART_RESOURCE_RESPONSE ||
+           mode_ == IMMEDIATE_REQUEST_HANDLER_OPEN ||
+           mode_ == IMMEDIATE_REQUEST_HANDLER_READ ||
+           mode_ == IMMEDIATE_REQUEST_HANDLER_ALL ||
+           mode_ == DELAYED_REQUEST_HANDLER_OPEN ||
+           mode_ == DELAYED_REQUEST_HANDLER_READ ||
+           mode_ == DELAYED_REQUEST_HANDLER_ALL;
+  }
+
+  bool IsIncompleteRequestHandler() const {
+    return mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN ||
+           mode_ == INCOMPLETE_REQUEST_HANDLER_READ;
+  }
+
+  bool IsIncomplete() const {
+    return mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
+           IsIncompleteRequestHandler();
+  }
+
+  bool IsRedirect() const {
+    return mode_ == REDIRECT_BEFORE_RESOURCE_LOAD ||
+           mode_ == REDIRECT_REQUEST_HANDLER ||
+           mode_ == REDIRECT_RESOURCE_REDIRECT ||
+           mode_ == REDIRECT_RESOURCE_RESPONSE;
+  }
+
+  static void SetCustomHeader(CefRefPtr<CefRequest> request) {
+    EXPECT_FALSE(request->IsReadOnly());
+    request->SetHeaderByName("X-Custom-Header", "value", false);
+  }
+
+  static std::string GetCustomHeader(CefRefPtr<CefRequest> request) {
+    return request->GetHeaderByName("X-Custom-Header");
+  }
+
+  // Resource-related callbacks.
+  enum Callback {
+    kGetResourceRequestHandler,
+    kGetCookieAccessFilter,
+    kOnBeforeResourceLoad,
+    kGetResourceHandler,
+    kOnResourceRedirect,
+    kOnResourceResponse,
+    kGetResourceResponseFilter,
+    kOnResourceLoadComplete,
+    kOnProtocolExecution,
+    kOnQuery,
+  };
+
+  bool ShouldHaveResponse(Callback callback) const {
+    return callback >= kOnResourceRedirect &&
+           callback <= kOnResourceLoadComplete;
+  }
+
+  bool ShouldHaveWritableRequest(Callback callback) const {
+    return callback == kOnBeforeResourceLoad || callback == kOnResourceResponse;
+  }
+
+  void VerifyFrame(Callback callback, CefRefPtr<CefFrame> frame) const {
+    EXPECT_TRUE(frame);
+
+    if (subframe_)
+      EXPECT_FALSE(frame->IsMain()) << callback;
+    else
+      EXPECT_TRUE(frame->IsMain()) << callback;
+
+    EXPECT_EQ(frame_id_, frame->GetIdentifier()) << callback;
+  }
+
+  void VerifyState(Callback callback,
+                   CefRefPtr<CefRequest> request,
+                   CefRefPtr<CefResponse> response) const {
+    EXPECT_TRUE(request) << callback;
+
+    if (ShouldHaveResponse(callback)) {
+      EXPECT_TRUE(response) << callback;
+      EXPECT_TRUE(response->IsReadOnly()) << callback;
+    } else {
+      EXPECT_FALSE(response) << callback;
+    }
+
+    if (ShouldHaveWritableRequest(callback)) {
+      EXPECT_FALSE(request->IsReadOnly()) << callback;
+    } else {
+      EXPECT_TRUE(request->IsReadOnly()) << callback;
+    }
+
+    // All resource-related callbacks share the same request ID.
+    EXPECT_EQ(request_id_, request->GetIdentifier()) << callback;
+
+    if (IsLoad() || IsIncomplete()) {
+      EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
+      EXPECT_STREQ(GetURL(RESULT_JS), request->GetURL().ToString().c_str())
+          << callback;
+
+      // Expect the header for all callbacks following the callback that
+      // initially sets it.
+      const std::string& custom_header = GetCustomHeader(request);
+      if ((mode_ == RESTART_RESOURCE_RESPONSE &&
+           on_resource_response_ct_ > 0) ||
+          (mode_ == MODIFY_BEFORE_RESOURCE_LOAD &&
+           on_before_resource_load_ct_ > 0)) {
+        EXPECT_STREQ("value", custom_header.c_str()) << callback;
+      } else {
+        EXPECT_STREQ("", custom_header.c_str()) << callback;
+      }
+
+      if (response)
+        VerifyOKResponse(callback, response);
+    } else if (IsRedirect()) {
+      EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
+      // Subresource loads don't get OnBeforeBrowse calls, so this check is a
+      // bit less exact then with main resource loads.
+      if (on_resource_redirect_ct_ == 0) {
+        // Before the redirect.
+        EXPECT_STREQ(GetStartupURL(), request->GetURL().ToString().c_str())
+            << callback;
+      } else {
+        // After the redirect.
+        EXPECT_STREQ(GetURL(RESULT_JS), request->GetURL().ToString().c_str())
+            << callback;
+      }
+
+      if (response) {
+        if (callback == kOnResourceRedirect) {
+          // Before the redirect.
+          VerifyRedirectResponse(callback, response);
+        } else {
+          // After the redirect.
+          VerifyOKResponse(callback, response);
+        }
+      }
+    } else {
+      NOTREACHED() << callback;
+    }
+  }
+
+  void VerifyOKResponse(Callback callback,
+                        CefRefPtr<CefResponse> response) const {
+    // True for the first response in cases where we're redirecting/restarting
+    // from inside OnResourceResponse (e.g. the first response always succeeds).
+    const bool override_unhandled = unhandled_ &&
+                                    (mode_ == REDIRECT_RESOURCE_RESPONSE ||
+                                     mode_ == RESTART_RESOURCE_RESPONSE) &&
+                                    get_resource_handler_ct_ == 1;
+
+    // True for tests where the request will be incomplete and never receive a
+    // response.
+    const bool incomplete_unhandled =
+        (mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
+         mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN);
+
+    if ((unhandled_ && !override_unhandled) || incomplete_unhandled) {
+      if (incomplete_unhandled) {
+        EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
+      } else {
+        EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, response->GetError()) << callback;
+      }
+      EXPECT_EQ(0, response->GetStatus()) << callback;
+      EXPECT_STREQ("", response->GetStatusText().ToString().c_str())
+          << callback;
+      EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
+      EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
+      EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
+    } else {
+      if (mode_ == INCOMPLETE_REQUEST_HANDLER_READ &&
+          callback == kOnResourceLoadComplete) {
+        // We got a response, but we also got aborted.
+        EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
+      } else {
+        EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
+      }
+      EXPECT_EQ(200, response->GetStatus()) << callback;
+      EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str())
+          << callback;
+      EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
+      EXPECT_STREQ("text/javascript",
+                   response->GetMimeType().ToString().c_str())
+          << callback;
+      EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
+    }
+  }
+
+  void VerifyRedirectResponse(Callback callback,
+                              CefRefPtr<CefResponse> response) const {
+    EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
+    EXPECT_EQ(307, response->GetStatus()) << callback;
+    const std::string& status_text = response->GetStatusText();
+    EXPECT_TRUE(status_text == "Internal Redirect" ||
+                status_text == "Temporary Redirect")
+        << status_text << callback;
+    EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
+    EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
+    EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
+  }
+
+  void CloseBrowserAsync() {
+    EXPECT_TRUE(IsIncomplete());
+    SetSignalCompletionWhenAllBrowsersClose(false);
+    CefPostDelayedTask(
+        TID_UI, base::Bind(&TestHandler::CloseBrowser, GetBrowser(), false),
+        100);
+  }
+
+  void MaybeDestroyTest(bool from_handler) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&SubresourceResponseTest::MaybeDestroyTest,
+                                     this, from_handler));
+      return;
+    }
+
+    if (from_handler) {
+      resource_handler_destroyed_ct_++;
+    }
+
+    bool destroy_test = false;
+    if (IsIncomplete()) {
+      // Destroy the test if we got OnResourceLoadComplete and either the
+      // resource handler will never complete or it was destroyed.
+      destroy_test =
+          on_resource_load_complete_ct_ > 0 &&
+          (!IsIncompleteRequestHandler() ||
+           resource_handler_destroyed_ct_ == resource_handler_created_ct_);
+    } else {
+      // Destroy the test if we got the expected number of OnLoadEnd and
+      // OnQuery, and the expected number of resource handlers were destroyed.
+      destroy_test =
+          on_load_end_ct_ > (subframe_ ? 1 : 0) &&
+          (on_query_ct_ > 0 || unhandled_) &&
+          resource_handler_destroyed_ct_ == resource_handler_created_ct_;
+    }
+
+    if (destroy_test) {
+      DestroyTest();
+    }
+  }
+
+  const TestMode mode_;
+  const bool custom_scheme_;
+  const bool unhandled_;
+  const bool subframe_;
+
+  int browser_id_ = 0;
+  int64 frame_id_ = 0;
+  uint64 request_id_ = 0U;
+
+  int resource_handler_created_ct_ = 0;
+
+  int on_before_browse_ct_ = 0;
+  int on_load_end_ct_ = 0;
+  int on_query_ct_ = 0;
+
+  int get_resource_request_handler_ct_ = 0;
+  int get_cookie_access_filter_ct_ = 0;
+  int on_before_resource_load_ct_ = 0;
+  int get_resource_handler_ct_ = 0;
+  int on_resource_redirect_ct_ = 0;
+  int on_resource_response_ct_ = 0;
+  int get_resource_response_filter_ct_ = 0;
+  int on_resource_load_complete_ct_ = 0;
+  int on_protocol_execution_ct_ = 0;
+  int resource_handler_destroyed_ct_ = 0;
+
+  // Used with INCOMPLETE_BEFORE_RESOURCE_LOAD.
+  CefRefPtr<CefRequestCallback> incomplete_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(SubresourceResponseTest);
+  IMPLEMENT_REFCOUNTING(SubresourceResponseTest);
+};
+
+}  // namespace
+
+#define SUBRESOURCE_TEST(name, test_mode, custom, unhandled, subframe)        \
+  TEST(ResourceRequestHandlerTest, Subresource##name) {                       \
+    CefRefPtr<SubresourceResponseTest> handler = new SubresourceResponseTest( \
+        SubresourceResponseTest::test_mode, custom, unhandled, subframe);     \
+    handler->ExecuteTest();                                                   \
+    ReleaseAndWaitForDestructor(handler);                                     \
+  }
+
+#define SUBRESOURCE_TEST_ALL_MODES(name, custom, unhandled, subframe)          \
+  SUBRESOURCE_TEST(name##Load, LOAD, custom, unhandled, subframe)              \
+  SUBRESOURCE_TEST(name##ModifyBeforeResourceLoad,                             \
+                   MODIFY_BEFORE_RESOURCE_LOAD, custom, unhandled, subframe)   \
+  SUBRESOURCE_TEST(name##RedirectBeforeResourceLoad,                           \
+                   REDIRECT_BEFORE_RESOURCE_LOAD, custom, unhandled, subframe) \
+  SUBRESOURCE_TEST(name##RedirectRequestHandler, REDIRECT_REQUEST_HANDLER,     \
+                   custom, unhandled, subframe)                                \
+  SUBRESOURCE_TEST(name##RedirectResourceRedirect, REDIRECT_RESOURCE_REDIRECT, \
+                   custom, unhandled, subframe)                                \
+  SUBRESOURCE_TEST(name##RedirectResourceResponse, REDIRECT_RESOURCE_RESPONSE, \
+                   custom, unhandled, subframe)                                \
+  SUBRESOURCE_TEST(name##RestartResourceResponse, RESTART_RESOURCE_RESPONSE,   \
+                   custom, unhandled, subframe)
+
+// Tests only supported in handled mode.
+#define SUBRESOURCE_TEST_HANDLED_MODES(name, custom, subframe)               \
+  SUBRESOURCE_TEST(name##ImmediateRequestHandlerOpen,                        \
+                   IMMEDIATE_REQUEST_HANDLER_OPEN, custom, false, subframe)  \
+  SUBRESOURCE_TEST(name##ImmediateRequestHandlerRead,                        \
+                   IMMEDIATE_REQUEST_HANDLER_READ, custom, false, subframe)  \
+  SUBRESOURCE_TEST(name##ImmediateRequestHandlerAll,                         \
+                   IMMEDIATE_REQUEST_HANDLER_ALL, custom, false, subframe)   \
+  SUBRESOURCE_TEST(name##DelayedRequestHandlerOpen,                          \
+                   DELAYED_REQUEST_HANDLER_OPEN, custom, false, subframe)    \
+  SUBRESOURCE_TEST(name##DelayedRequestHandlerRead,                          \
+                   DELAYED_REQUEST_HANDLER_READ, custom, false, subframe)    \
+  SUBRESOURCE_TEST(name##DelayedRequestHandlerAll,                           \
+                   DELAYED_REQUEST_HANDLER_ALL, custom, false, subframe)     \
+  SUBRESOURCE_TEST(name##IncompleteBeforeResourceLoad,                       \
+                   INCOMPLETE_BEFORE_RESOURCE_LOAD, custom, false, subframe) \
+  SUBRESOURCE_TEST(name##IncompleteRequestHandlerOpen,                       \
+                   INCOMPLETE_REQUEST_HANDLER_OPEN, custom, false, subframe) \
+  SUBRESOURCE_TEST(name##IncompleteRequestHandlerRead,                       \
+                   INCOMPLETE_REQUEST_HANDLER_READ, custom, false, subframe)
+
+SUBRESOURCE_TEST_ALL_MODES(StandardHandledMainFrame, false, false, false)
+SUBRESOURCE_TEST_ALL_MODES(StandardUnhandledMainFrame, false, true, false)
+SUBRESOURCE_TEST_ALL_MODES(CustomHandledMainFrame, true, false, false)
+SUBRESOURCE_TEST_ALL_MODES(CustomUnhandledMainFrame, true, true, false)
+
+SUBRESOURCE_TEST_ALL_MODES(StandardHandledSubFrame, false, false, true)
+SUBRESOURCE_TEST_ALL_MODES(StandardUnhandledSubFrame, false, true, true)
+SUBRESOURCE_TEST_ALL_MODES(CustomHandledSubFrame, true, false, true)
+SUBRESOURCE_TEST_ALL_MODES(CustomUnhandledSubFrame, true, true, true)
+
+SUBRESOURCE_TEST_HANDLED_MODES(StandardHandledMainFrame, false, false)
+SUBRESOURCE_TEST_HANDLED_MODES(CustomHandledMainFrame, true, false)
+
+SUBRESOURCE_TEST_HANDLED_MODES(StandardHandledSubFrame, false, true)
+SUBRESOURCE_TEST_HANDLED_MODES(CustomHandledSubFrame, true, true)
+
+namespace {
+
+const char kResourceTestHtml[] = "http://test.com/resource.html";
+
+class RedirectResponseTest : public TestHandler {
+ public:
+  enum TestMode {
+    URL,
+    HEADER,
+    POST,
+  };
+
+  RedirectResponseTest(TestMode mode, bool via_request_context_handler)
+      : via_request_context_handler_(via_request_context_handler) {
+    if (mode == URL)
+      resource_test_.reset(new UrlResourceTest);
+    else if (mode == HEADER)
+      resource_test_.reset(new HeaderResourceTest);
+    else
+      resource_test_.reset(new PostResourceTest);
+  }
+
+  void RunTest() override {
+    AddResource(kResourceTestHtml, GetHtml(), "text/html");
+
+    resource_request_handler_ = new ResourceRequestHandler(this);
+
+    CefRefPtr<CefRequestContext> request_context =
+        CefRequestContext::GetGlobalContext();
+    if (via_request_context_handler_) {
+      CefRefPtr<CefRequestContextHandler> request_context_handler =
+          new RequestContextHandler(resource_request_handler_.get());
+      CefRequestContextSettings settings;
+      request_context = CefRequestContext::CreateContext(
+          request_context, request_context_handler);
+    }
+
+    CreateBrowser(kResourceTestHtml, request_context);
+    SetTestTimeout();
+  }
+
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override {
+    if (via_request_context_handler_) {
+      // Use the handler returned by RequestContextHandler.
+      return nullptr;
+    }
+    return resource_request_handler_.get();
+  }
+
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override {
+    EXPECT_UI_THREAD();
+    EXPECT_EQ(0, browser_id_);
+    browser_id_ = browser->GetIdentifier();
+    EXPECT_GT(browser_id_, 0);
+
+    // This method is only called for the main resource.
+    EXPECT_STREQ(kResourceTestHtml, request->GetURL().ToString().c_str());
+
+    // Browser-side navigation no longer exposes the actual request information.
+    EXPECT_EQ(0U, request->GetIdentifier());
+
+    return false;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_UI_THREAD();
+    EXPECT_EQ(browser_id_, browser->GetIdentifier());
+
+    TestHandler::OnLoadEnd(browser, frame, httpStatusCode);
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    resource_test_->CheckExpected();
+    resource_test_.reset(nullptr);
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  std::string GetHtml() const {
+    std::stringstream html;
+    html << "<html><head>";
+
+    const std::string& url = resource_test_->start_url();
+    html << "<script type=\"text/javascript\" src=\"" << url << "\"></script>";
+
+    html << "</head><body><p>Main</p></body></html>";
+    return html.str();
+  }
+
+  class ResourceTest {
+   public:
+    ResourceTest(const std::string& start_url,
+                 size_t expected_resource_response_ct = 2U,
+                 size_t expected_before_resource_load_ct = 1U,
+                 size_t expected_resource_redirect_ct = 0U,
+                 size_t expected_resource_load_complete_ct = 1U)
+        : start_url_(start_url),
+          expected_resource_response_ct_(expected_resource_response_ct),
+          expected_before_resource_load_ct_(expected_before_resource_load_ct),
+          expected_resource_redirect_ct_(expected_resource_redirect_ct),
+          expected_resource_load_complete_ct_(
+              expected_resource_load_complete_ct) {}
+    virtual ~ResourceTest() {}
+
+    const std::string& start_url() const { return start_url_; }
+
+    virtual bool OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,
+                                      CefRefPtr<CefFrame> frame,
+                                      CefRefPtr<CefRequest> request) {
+      before_resource_load_ct_++;
+      return false;
+    }
+
+    virtual CefRefPtr<CefResourceHandler> GetResourceHandler(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefRefPtr<CefRequest> request) {
+      get_resource_handler_ct_++;
+
+      const std::string& js_content = "<!-- -->";
+
+      CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+          const_cast<char*>(js_content.c_str()), js_content.size());
+
+      return new CefStreamResourceHandler(200, "OK", "text/javascript",
+                                          CefResponse::HeaderMap(), stream);
+    }
+
+    virtual void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                                    CefRefPtr<CefFrame> frame,
+                                    CefRefPtr<CefRequest> request,
+                                    CefString& new_url) {
+      resource_redirect_ct_++;
+    }
+
+    bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            CefRefPtr<CefResponse> response) {
+      EXPECT_TRUE(CheckUrl(request->GetURL()));
+
+      // Verify the response returned by GetResourceHandler.
+      EXPECT_EQ(200, response->GetStatus());
+      EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str());
+      EXPECT_STREQ("text/javascript",
+                   response->GetMimeType().ToString().c_str());
+
+      if (resource_response_ct_++ == 0U) {
+        // Always redirect at least one time.
+        OnResourceReceived(browser, frame, request, response);
+        return true;
+      }
+
+      OnRetryReceived(browser, frame, request, response);
+      return (resource_response_ct_ < expected_resource_response_ct_);
+    }
+
+    CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefRefPtr<CefRequest> request,
+        CefRefPtr<CefResponse> response) {
+      get_resource_response_filter_ct_++;
+      return nullptr;
+    }
+
+    void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefRefPtr<CefRequest> request,
+                                CefRefPtr<CefResponse> response,
+                                URLRequestStatus status,
+                                int64 received_content_length) {
+      EXPECT_TRUE(CheckUrl(request->GetURL()));
+
+      // Verify the response returned by GetResourceHandler.
+      EXPECT_EQ(200, response->GetStatus());
+      EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str());
+      EXPECT_STREQ("text/javascript",
+                   response->GetMimeType().ToString().c_str());
+
+      resource_load_complete_ct_++;
+    }
+
+    virtual bool CheckUrl(const std::string& url) const {
+      return (url == start_url_);
+    }
+
+    virtual void CheckExpected() {
+      EXPECT_TRUE(got_resource_);
+      EXPECT_TRUE(got_resource_retry_);
+
+      EXPECT_EQ(expected_resource_response_ct_, resource_response_ct_);
+      EXPECT_EQ(expected_resource_response_ct_, get_resource_handler_ct_);
+      EXPECT_EQ(expected_resource_load_complete_ct_,
+                get_resource_response_filter_ct_);
+      EXPECT_EQ(expected_before_resource_load_ct_, before_resource_load_ct_);
+      EXPECT_EQ(expected_resource_redirect_ct_, resource_redirect_ct_);
+      EXPECT_EQ(expected_resource_load_complete_ct_,
+                resource_load_complete_ct_);
+    }
+
+   protected:
+    virtual void OnResourceReceived(CefRefPtr<CefBrowser> browser,
+                                    CefRefPtr<CefFrame> frame,
+                                    CefRefPtr<CefRequest> request,
+                                    CefRefPtr<CefResponse> response) {
+      got_resource_.yes();
+    }
+
+    virtual void OnRetryReceived(CefRefPtr<CefBrowser> browser,
+                                 CefRefPtr<CefFrame> frame,
+                                 CefRefPtr<CefRequest> request,
+                                 CefRefPtr<CefResponse> response) {
+      got_resource_retry_.yes();
+    }
+
+   private:
+    std::string start_url_;
+
+    size_t resource_response_ct_ = 0U;
+    size_t expected_resource_response_ct_;
+    size_t before_resource_load_ct_ = 0U;
+    size_t expected_before_resource_load_ct_;
+    size_t get_resource_handler_ct_ = 0U;
+    size_t resource_redirect_ct_ = 0U;
+    size_t expected_resource_redirect_ct_;
+    size_t get_resource_response_filter_ct_ = 0U;
+    size_t resource_load_complete_ct_ = 0U;
+    size_t expected_resource_load_complete_ct_;
+
+    TrackCallback got_resource_;
+    TrackCallback got_resource_retry_;
+  };
+
+  class UrlResourceTest : public ResourceTest {
+   public:
+    // With NetworkService we don't get an additional (unnecessary) redirect
+    // callback.
+    UrlResourceTest()
+        : ResourceTest("http://test.com/start_url.js", 2U, 2U, 1U) {
+      redirect_url_ = "http://test.com/redirect_url.js";
+    }
+
+    bool CheckUrl(const std::string& url) const override {
+      if (url == redirect_url_)
+        return true;
+
+      return ResourceTest::CheckUrl(url);
+    }
+
+    void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            CefString& new_url) override {
+      ResourceTest::OnResourceRedirect(browser, frame, request, new_url);
+      const std::string& old_url = request->GetURL();
+      EXPECT_STREQ(start_url().c_str(), old_url.c_str());
+      EXPECT_STREQ(redirect_url_.c_str(), new_url.ToString().c_str());
+    }
+
+   private:
+    void OnResourceReceived(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            CefRefPtr<CefResponse> response) override {
+      ResourceTest::OnResourceReceived(browser, frame, request, response);
+      request->SetURL(redirect_url_);
+    }
+
+    void OnRetryReceived(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefRequest> request,
+                         CefRefPtr<CefResponse> response) override {
+      ResourceTest::OnRetryReceived(browser, frame, request, response);
+      const std::string& new_url = request->GetURL();
+      EXPECT_STREQ(redirect_url_.c_str(), new_url.c_str());
+    }
+
+    std::string redirect_url_;
+  };
+
+  class HeaderResourceTest : public ResourceTest {
+   public:
+    // With NetworkService we restart the request, so we get another call to
+    // OnBeforeResourceLoad.
+    HeaderResourceTest()
+        : ResourceTest("http://test.com/start_header.js", 2U, 2U) {
+      expected_headers_.insert(std::make_pair("Test-Key1", "Value1"));
+      expected_headers_.insert(std::make_pair("Test-Key2", "Value2"));
+    }
+
+   private:
+    void OnResourceReceived(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            CefRefPtr<CefResponse> response) override {
+      ResourceTest::OnResourceReceived(browser, frame, request, response);
+      request->SetHeaderMap(expected_headers_);
+    }
+
+    void OnRetryReceived(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefRequest> request,
+                         CefRefPtr<CefResponse> response) override {
+      ResourceTest::OnRetryReceived(browser, frame, request, response);
+      CefRequest::HeaderMap actual_headers;
+      request->GetHeaderMap(actual_headers);
+      TestMapEqual(expected_headers_, actual_headers, true);
+    }
+
+    CefRequest::HeaderMap expected_headers_;
+  };
+
+  class PostResourceTest : public ResourceTest {
+   public:
+    // With NetworkService we restart the request, so we get another call to
+    // OnBeforeResourceLoad.
+    PostResourceTest() : ResourceTest("http://test.com/start_post.js", 2U, 2U) {
+      CefRefPtr<CefPostDataElement> elem = CefPostDataElement::Create();
+      const std::string data("Test Post Data");
+      elem->SetToBytes(data.size(), data.c_str());
+
+      expected_post_ = CefPostData::Create();
+      expected_post_->AddElement(elem);
+    }
+
+   private:
+    void OnResourceReceived(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            CefRefPtr<CefResponse> response) override {
+      ResourceTest::OnResourceReceived(browser, frame, request, response);
+      request->SetPostData(expected_post_);
+    }
+
+    void OnRetryReceived(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefRequest> request,
+                         CefRefPtr<CefResponse> response) override {
+      ResourceTest::OnRetryReceived(browser, frame, request, response);
+      CefRefPtr<CefPostData> actual_post = request->GetPostData();
+      TestPostDataEqual(expected_post_, actual_post);
+    }
+
+    CefRefPtr<CefPostData> expected_post_;
+  };
+
+  class RequestContextHandler : public CefRequestContextHandler {
+   public:
+    explicit RequestContextHandler(
+        CefRefPtr<CefResourceRequestHandler> resource_request_handler)
+        : resource_request_handler_(resource_request_handler) {}
+
+    CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefRefPtr<CefRequest> request,
+        bool is_navigation,
+        bool is_download,
+        const CefString& request_initiator,
+        bool& disable_default_handling) override {
+      return resource_request_handler_;
+    }
+
+   private:
+    CefRefPtr<CefResourceRequestHandler> resource_request_handler_;
+
+    IMPLEMENT_REFCOUNTING(RequestContextHandler);
+    DISALLOW_COPY_AND_ASSIGN(RequestContextHandler);
+  };
+
+  class ResourceRequestHandler : public CefResourceRequestHandler {
+   public:
+    explicit ResourceRequestHandler(RedirectResponseTest* test) : test_(test) {}
+
+    cef_return_value_t OnBeforeResourceLoad(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefRefPtr<CefRequest> request,
+        CefRefPtr<CefRequestCallback> callback) override {
+      EXPECT_IO_THREAD();
+      EXPECT_EQ(test_->browser_id_, browser->GetIdentifier());
+
+      if (request->GetURL() == kResourceTestHtml) {
+        // All loads of the main resource should keep the same request id.
+        EXPECT_EQ(0U, main_request_id_);
+        main_request_id_ = request->GetIdentifier();
+        EXPECT_GT(main_request_id_, 0U);
+        return RV_CONTINUE;
+      }
+
+      // All redirects of the sub-resource should keep the same request id.
+      if (sub_request_id_ == 0U) {
+        sub_request_id_ = request->GetIdentifier();
+        EXPECT_GT(sub_request_id_, 0U);
+      } else {
+        EXPECT_EQ(sub_request_id_, request->GetIdentifier());
+      }
+
+      return test_->resource_test_->OnBeforeResourceLoad(browser, frame,
+                                                         request)
+                 ? RV_CANCEL
+                 : RV_CONTINUE;
+    }
+
+    CefRefPtr<CefResourceHandler> GetResourceHandler(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefRefPtr<CefRequest> request) override {
+      EXPECT_IO_THREAD();
+      EXPECT_EQ(test_->browser_id_, browser->GetIdentifier());
+
+      if (request->GetURL() == kResourceTestHtml) {
+        EXPECT_EQ(main_request_id_, request->GetIdentifier());
+        return test_->GetResourceHandler(browser, frame, request);
+      }
+
+      EXPECT_EQ(sub_request_id_, request->GetIdentifier());
+      return test_->resource_test_->GetResourceHandler(browser, frame, request);
+    }
+
+    void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            CefRefPtr<CefResponse> response,
+                            CefString& new_url) override {
+      EXPECT_IO_THREAD();
+      EXPECT_EQ(test_->browser_id_, browser->GetIdentifier());
+      EXPECT_EQ(sub_request_id_, request->GetIdentifier());
+
+      test_->resource_test_->OnResourceRedirect(browser, frame, request,
+                                                new_url);
+    }
+
+    bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            CefRefPtr<CefResponse> response) override {
+      EXPECT_IO_THREAD();
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(test_->browser_id_, browser->GetIdentifier());
+
+      EXPECT_TRUE(frame.get());
+      EXPECT_TRUE(frame->IsMain());
+
+      if (request->GetURL() == kResourceTestHtml) {
+        EXPECT_EQ(main_request_id_, request->GetIdentifier());
+        return false;
+      }
+
+      EXPECT_EQ(sub_request_id_, request->GetIdentifier());
+      return test_->resource_test_->OnResourceResponse(browser, frame, request,
+                                                       response);
+    }
+
+    CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefRefPtr<CefRequest> request,
+        CefRefPtr<CefResponse> response) override {
+      EXPECT_IO_THREAD();
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(test_->browser_id_, browser->GetIdentifier());
+
+      EXPECT_TRUE(frame.get());
+      EXPECT_TRUE(frame->IsMain());
+
+      if (request->GetURL() == kResourceTestHtml) {
+        EXPECT_EQ(main_request_id_, request->GetIdentifier());
+        return nullptr;
+      }
+
+      return test_->resource_test_->GetResourceResponseFilter(
+          browser, frame, request, response);
+    }
+
+    void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefRefPtr<CefRequest> request,
+                                CefRefPtr<CefResponse> response,
+                                URLRequestStatus status,
+                                int64 received_content_length) override {
+      EXPECT_IO_THREAD();
+      EXPECT_TRUE(browser.get());
+      EXPECT_EQ(test_->browser_id_, browser->GetIdentifier());
+
+      EXPECT_TRUE(frame.get());
+      EXPECT_TRUE(frame->IsMain());
+
+      if (request->GetURL() == kResourceTestHtml) {
+        EXPECT_EQ(main_request_id_, request->GetIdentifier());
+        return;
+      }
+
+      EXPECT_EQ(sub_request_id_, request->GetIdentifier());
+      test_->resource_test_->OnResourceLoadComplete(
+          browser, frame, request, response, status, received_content_length);
+    }
+
+   private:
+    RedirectResponseTest* const test_;
+
+    uint64 main_request_id_ = 0U;
+    uint64 sub_request_id_ = 0U;
+
+    IMPLEMENT_REFCOUNTING(ResourceRequestHandler);
+    DISALLOW_COPY_AND_ASSIGN(ResourceRequestHandler);
+  };
+
+  const bool via_request_context_handler_;
+
+  int browser_id_ = 0;
+  scoped_ptr<ResourceTest> resource_test_;
+  CefRefPtr<ResourceRequestHandler> resource_request_handler_;
+
+  IMPLEMENT_REFCOUNTING(RedirectResponseTest);
+};
+
+}  // namespace
+
+// Verify redirect with client handler.
+TEST(ResourceRequestHandlerTest, RedirectURLViaClient) {
+  CefRefPtr<RedirectResponseTest> handler =
+      new RedirectResponseTest(RedirectResponseTest::URL, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify redirect + modified headers with client handler.
+TEST(ResourceRequestHandlerTest, RedirectHeaderViaClient) {
+  CefRefPtr<RedirectResponseTest> handler =
+      new RedirectResponseTest(RedirectResponseTest::HEADER, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify redirect + modified post data with client handler.
+TEST(ResourceRequestHandlerTest, RedirectPostViaClient) {
+  CefRefPtr<RedirectResponseTest> handler =
+      new RedirectResponseTest(RedirectResponseTest::POST, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify redirect with context handler.
+TEST(ResourceRequestHandlerTest, RedirectURLViaContext) {
+  CefRefPtr<RedirectResponseTest> handler =
+      new RedirectResponseTest(RedirectResponseTest::URL, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify redirect + modified headers with context handler.
+TEST(ResourceRequestHandlerTest, RedirectHeaderViaContext) {
+  CefRefPtr<RedirectResponseTest> handler =
+      new RedirectResponseTest(RedirectResponseTest::HEADER, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Verify redirect + modified post data with context handler.
+TEST(ResourceRequestHandlerTest, RedirectPostViaContext) {
+  CefRefPtr<RedirectResponseTest> handler =
+      new RedirectResponseTest(RedirectResponseTest::POST, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+const char kResourceTestHtml2[] = "http://test.com/resource2.html";
+
+class BeforeResourceLoadTest : public TestHandler {
+ public:
+  enum TestMode {
+    CANCEL,
+    CANCEL_ASYNC,
+    CANCEL_NAV,
+    CONTINUE,
+    CONTINUE_ASYNC,
+  };
+
+  explicit BeforeResourceLoadTest(TestMode mode) : test_mode_(mode) {}
+
+  void RunTest() override {
+    AddResource(kResourceTestHtml, "<html><body>Test</body></html>",
+                "text/html");
+    AddResource(kResourceTestHtml2, "<html><body>Test2</body></html>",
+                "text/html");
+    CreateBrowser(kResourceTestHtml);
+    SetTestTimeout();
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    EXPECT_IO_THREAD();
+
+    // Allow the 2nd navigation to continue.
+    const std::string& url = request->GetURL();
+    if (url == kResourceTestHtml2) {
+      got_before_resource_load2_.yes();
+      EXPECT_EQ(CANCEL_NAV, test_mode_);
+      return RV_CONTINUE;
+    }
+
+    EXPECT_FALSE(got_before_resource_load_);
+    got_before_resource_load_.yes();
+
+    if (test_mode_ == CANCEL) {
+      // Cancel immediately.
+      return RV_CANCEL;
+    } else if (test_mode_ == CONTINUE) {
+      // Continue immediately.
+      return RV_CONTINUE;
+    } else {
+      if (test_mode_ == CANCEL_NAV) {
+        // Cancel the request by navigating to a new URL.
+        browser->GetMainFrame()->LoadURL(kResourceTestHtml2);
+      } else {
+        // Continue or cancel asynchronously.
+        CefPostTask(TID_UI,
+                    base::Bind(&CefRequestCallback::Continue, callback.get(),
+                               test_mode_ == CONTINUE_ASYNC));
+      }
+      return RV_CONTINUE_ASYNC;
+    }
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    EXPECT_UI_THREAD();
+
+    EXPECT_FALSE(got_load_end_);
+    got_load_end_.yes();
+
+    const std::string& url = frame->GetURL();
+    if (test_mode_ == CANCEL_NAV)
+      EXPECT_STREQ(kResourceTestHtml2, url.data());
+    else
+      EXPECT_STREQ(kResourceTestHtml, url.data());
+
+    TestHandler::OnLoadEnd(browser, frame, httpStatusCode);
+    DestroyTest();
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    EXPECT_UI_THREAD();
+
+    EXPECT_FALSE(got_load_error_);
+    got_load_error_.yes();
+
+    const std::string& url = failedUrl;
+    EXPECT_STREQ(kResourceTestHtml, url.data());
+
+    TestHandler::OnLoadError(browser, frame, errorCode, errorText, failedUrl);
+    if (test_mode_ != CANCEL_NAV)
+      DestroyTest();
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_before_resource_load_);
+
+    if (test_mode_ == CANCEL_NAV)
+      EXPECT_TRUE(got_before_resource_load2_);
+    else
+      EXPECT_FALSE(got_before_resource_load2_);
+
+    if (test_mode_ == CONTINUE || test_mode_ == CONTINUE_ASYNC) {
+      EXPECT_TRUE(got_load_end_);
+      EXPECT_FALSE(got_load_error_);
+    } else if (test_mode_ == CANCEL || test_mode_ == CANCEL_ASYNC) {
+      EXPECT_FALSE(got_load_end_);
+      EXPECT_TRUE(got_load_error_);
+    }
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  const TestMode test_mode_;
+
+  TrackCallback got_before_resource_load_;
+  TrackCallback got_before_resource_load2_;
+  TrackCallback got_load_end_;
+  TrackCallback got_load_error_;
+
+  IMPLEMENT_REFCOUNTING(BeforeResourceLoadTest);
+};
+
+}  // namespace
+
+TEST(ResourceRequestHandlerTest, BeforeResourceLoadCancel) {
+  CefRefPtr<BeforeResourceLoadTest> handler =
+      new BeforeResourceLoadTest(BeforeResourceLoadTest::CANCEL);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(ResourceRequestHandlerTest, BeforeResourceLoadCancelAsync) {
+  CefRefPtr<BeforeResourceLoadTest> handler =
+      new BeforeResourceLoadTest(BeforeResourceLoadTest::CANCEL_ASYNC);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(ResourceRequestHandlerTest, BeforeResourceLoadCancelNav) {
+  CefRefPtr<BeforeResourceLoadTest> handler =
+      new BeforeResourceLoadTest(BeforeResourceLoadTest::CANCEL_NAV);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(ResourceRequestHandlerTest, BeforeResourceLoadContinue) {
+  CefRefPtr<BeforeResourceLoadTest> handler =
+      new BeforeResourceLoadTest(BeforeResourceLoadTest::CONTINUE);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(ResourceRequestHandlerTest, BeforeResourceLoadContinueAsync) {
+  CefRefPtr<BeforeResourceLoadTest> handler =
+      new BeforeResourceLoadTest(BeforeResourceLoadTest::CONTINUE_ASYNC);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// For response filtering we need to test:
+// - Passing through content unchanged.
+// - Not reading all of the input buffer.
+// - Needing more input and getting it.
+// - Needing more input and not getting it.
+// - Filter error.
+
+const char kResponseFilterTestUrl[] = "http://tests.com/response_filter.html";
+
+size_t GetResponseBufferSize() {
+  // Match the default |capacity_num_bytes| value from
+  // mojo::Core::CreateDataPipe.
+  return 64 * 1024;  // 64kb
+}
+
+const char kInputHeader[] = "<html><head></head><body>";
+const char kInputFooter[] = "</body></html>";
+
+// Repeat |content| the minimum number of times necessary to satisfy
+// |desired_min_size|. If |calculated_repeat_ct| is non-NULL it will be set to
+// the number of times that |content| was repeated.
+std::string CreateInput(const std::string& content,
+                        size_t desired_min_size,
+                        size_t* calculated_repeat_ct = nullptr) {
+  const size_t header_footer_size =
+      sizeof(kInputHeader) + sizeof(kInputFooter) - 2;
+  EXPECT_GE(desired_min_size, header_footer_size + content.size());
+  desired_min_size -= header_footer_size;
+
+  size_t repeat_ct =
+      static_cast<size_t>(std::ceil(static_cast<double>(desired_min_size) /
+                                    static_cast<double>(content.size())));
+  if (calculated_repeat_ct)
+    *calculated_repeat_ct = repeat_ct;
+
+  std::string result;
+  result.reserve(header_footer_size + (content.size() * repeat_ct));
+
+  result = kInputHeader;
+  while (repeat_ct--)
+    result += content;
+  result += kInputFooter;
+
+  return result;
+}
+
+std::string CreateOutput(const std::string& content, size_t repeat_ct) {
+  const size_t header_footer_size =
+      sizeof(kInputHeader) + sizeof(kInputFooter) - 2;
+
+  std::string result;
+  result.reserve(header_footer_size + (content.size() * repeat_ct));
+
+  result = kInputHeader;
+  while (repeat_ct--)
+    result += content;
+  result += kInputFooter;
+
+  return result;
+}
+
+// Base class for test filters.
+class ResponseFilterTestBase : public CefResponseFilter {
+ public:
+  ResponseFilterTestBase() : filter_count_(0U) {}
+
+  bool InitFilter() override {
+    EXPECT_FALSE(got_init_filter_);
+    got_init_filter_.yes();
+    return true;
+  }
+
+  FilterStatus Filter(void* data_in,
+                      size_t data_in_size,
+                      size_t& data_in_read,
+                      void* data_out,
+                      size_t data_out_size,
+                      size_t& data_out_written) override {
+    if (data_in_size == 0U)
+      EXPECT_FALSE(data_in);
+    else
+      EXPECT_TRUE(data_in);
+    EXPECT_EQ(data_in_read, 0U);
+    EXPECT_TRUE(data_out);
+    EXPECT_GT(data_out_size, 0U);
+    EXPECT_EQ(data_out_written, 0U);
+    filter_count_++;
+    return RESPONSE_FILTER_ERROR;
+  }
+
+  // Returns the input that will be fed into the filter.
+  virtual std::string GetInput() = 0;
+
+  // Verify the output from the filter.
+  virtual void VerifyOutput(cef_urlrequest_status_t status,
+                            int64 received_content_length,
+                            const std::string& received_content) {
+    EXPECT_TRUE(got_init_filter_);
+    EXPECT_GT(filter_count_, 0U);
+  }
+
+  virtual void VerifyStatusCode(int httpStatusCode) const {
+    EXPECT_TRUE(httpStatusCode == 0 || httpStatusCode == 200) << httpStatusCode;
+  }
+
+ protected:
+  TrackCallback got_init_filter_;
+  size_t filter_count_;
+
+  IMPLEMENT_REFCOUNTING(ResponseFilterTestBase);
+};
+
+// Pass through the contents unchanged.
+class ResponseFilterPassThru : public ResponseFilterTestBase {
+ public:
+  explicit ResponseFilterPassThru(bool limit_read) : limit_read_(limit_read) {}
+
+  FilterStatus Filter(void* data_in,
+                      size_t data_in_size,
+                      size_t& data_in_read,
+                      void* data_out,
+                      size_t data_out_size,
+                      size_t& data_out_written) override {
+    ResponseFilterTestBase::Filter(data_in, data_in_size, data_in_read,
+                                   data_out, data_out_size, data_out_written);
+
+    if (limit_read_) {
+      // Read at most 1k bytes.
+      data_in_read = std::min(data_in_size, static_cast<size_t>(1024U));
+    } else {
+      // Read all available bytes.
+      data_in_read = data_in_size;
+    }
+
+    data_out_written = std::min(data_in_read, data_out_size);
+    memcpy(data_out, data_in, data_out_written);
+
+    return RESPONSE_FILTER_DONE;
+  }
+
+  std::string GetInput() override {
+    input_ = CreateInput("FOOBAR ", GetResponseBufferSize() * 2U + 1);
+    return input_;
+  }
+
+  void VerifyOutput(cef_urlrequest_status_t status,
+                    int64 received_content_length,
+                    const std::string& received_content) override {
+    ResponseFilterTestBase::VerifyOutput(status, received_content_length,
+                                         received_content);
+
+    if (limit_read_)
+      // Expected to read 2 full buffers of GetResponseBufferSize() at 1kb
+      // increments and one partial buffer.
+      EXPECT_EQ(2U * (GetResponseBufferSize() / 1024) + 1U, filter_count_);
+    else {
+      // Expected to read 2 full buffers of GetResponseBufferSize() and one
+      // partial buffer.
+      EXPECT_EQ(3U, filter_count_);
+    }
+    EXPECT_STREQ(input_.c_str(), received_content.c_str());
+
+    // Input size and content size should match.
+    EXPECT_EQ(input_.size(), static_cast<size_t>(received_content_length));
+    EXPECT_EQ(input_.size(), received_content.size());
+  }
+
+ private:
+  std::string input_;
+  bool limit_read_;
+};
+
+const char kFindString[] = "REPLACE_THIS_STRING";
+const char kReplaceString[] = "This is the replaced string!";
+
+// Helper for passing params to Write().
+#define WRITE_PARAMS data_out_ptr, data_out_size, data_out_written
+
+// Replace all instances of |kFindString| with |kReplaceString|.
+// This implementation is similar to the example in
+// tests/shared/response_filter_test.cc.
+class ResponseFilterNeedMore : public ResponseFilterTestBase {
+ public:
+  ResponseFilterNeedMore()
+      : find_match_offset_(0U),
+        replace_overflow_size_(0U),
+        input_size_(0U),
+        repeat_ct_(0U) {}
+
+  FilterStatus Filter(void* data_in,
+                      size_t data_in_size,
+                      size_t& data_in_read,
+                      void* data_out,
+                      size_t data_out_size,
+                      size_t& data_out_written) override {
+    ResponseFilterTestBase::Filter(data_in, data_in_size, data_in_read,
+                                   data_out, data_out_size, data_out_written);
+
+    // All data will be read.
+    data_in_read = data_in_size;
+
+    const size_t find_size = sizeof(kFindString) - 1;
+
+    const char* data_in_ptr = static_cast<char*>(data_in);
+    char* data_out_ptr = static_cast<char*>(data_out);
+
+    // Reset the overflow.
+    std::string old_overflow;
+    if (!overflow_.empty()) {
+      old_overflow = overflow_;
+      overflow_.clear();
+    }
+
+    const size_t likely_out_size =
+        data_in_size + replace_overflow_size_ + old_overflow.size();
+    if (data_out_size < likely_out_size) {
+      // We'll likely need to use the overflow buffer. Size it appropriately.
+      overflow_.reserve(likely_out_size - data_out_size);
+    }
+
+    if (!old_overflow.empty()) {
+      // Write the overflow from last time.
+      Write(old_overflow.c_str(), old_overflow.size(), WRITE_PARAMS);
+    }
+
+    // Evaluate each character in the input buffer. Track how many characters in
+    // a row match kFindString. If kFindString is completely matched then write
+    // kReplaceString. Otherwise, write the input characters as-is.
+    for (size_t i = 0U; i < data_in_size; ++i) {
+      if (data_in_ptr[i] == kFindString[find_match_offset_]) {
+        // Matched the next character in the find string.
+        if (++find_match_offset_ == find_size) {
+          // Complete match of the find string. Write the replace string.
+          Write(kReplaceString, sizeof(kReplaceString) - 1, WRITE_PARAMS);
+
+          // Start over looking for a match.
+          find_match_offset_ = 0;
+        }
+        continue;
+      }
+
+      // Character did not match the find string.
+      if (find_match_offset_ > 0) {
+        // Write the portion of the find string that has matched so far.
+        Write(kFindString, find_match_offset_, WRITE_PARAMS);
+
+        // Start over looking for a match.
+        find_match_offset_ = 0;
+      }
+
+      // Write the current character.
+      Write(&data_in_ptr[i], 1, WRITE_PARAMS);
+    }
+
+    // If a match is currently in-progress and input was provided then we need
+    // more data. Otherwise, we're done.
+    return find_match_offset_ > 0 && data_in_size > 0
+               ? RESPONSE_FILTER_NEED_MORE_DATA
+               : RESPONSE_FILTER_DONE;
+  }
+
+  std::string GetInput() override {
+    const std::string& input =
+        CreateInput(std::string(kFindString) + " ",
+                    GetResponseBufferSize() * 2U + 1, &repeat_ct_);
+    input_size_ = input.size();
+
+    const size_t find_size = sizeof(kFindString) - 1;
+    const size_t replace_size = sizeof(kReplaceString) - 1;
+
+    // Determine a reasonable amount of space for find/replace overflow.
+    if (replace_size > find_size)
+      replace_overflow_size_ = (replace_size - find_size) * repeat_ct_;
+
+    return input;
+  }
+
+  void VerifyOutput(cef_urlrequest_status_t status,
+                    int64 received_content_length,
+                    const std::string& received_content) override {
+    ResponseFilterTestBase::VerifyOutput(status, received_content_length,
+                                         received_content);
+
+    const std::string& output =
+        CreateOutput(std::string(kReplaceString) + " ", repeat_ct_);
+    EXPECT_STREQ(output.c_str(), received_content.c_str());
+
+    // Pre-filter content length should be the original input size.
+    EXPECT_EQ(input_size_, static_cast<size_t>(received_content_length));
+
+    // Filtered content length should be the output size.
+    EXPECT_EQ(output.size(), received_content.size());
+
+    // Expected to read 2 full buffers of GetResponseBufferSize() and one
+    // partial buffer, and then one additional call to drain the overflow.
+    EXPECT_EQ(4U, filter_count_);
+  }
+
+ private:
+  inline void Write(const char* str,
+                    size_t str_size,
+                    char*& data_out_ptr,
+                    size_t data_out_size,
+                    size_t& data_out_written) {
+    // Number of bytes remaining in the output buffer.
+    const size_t remaining_space = data_out_size - data_out_written;
+    // Maximum number of bytes we can write into the output buffer.
+    const size_t max_write = std::min(str_size, remaining_space);
+
+    // Write the maximum portion that fits in the output buffer.
+    if (max_write == 1) {
+      // Small optimization for single character writes.
+      *data_out_ptr = str[0];
+      data_out_ptr += 1;
+      data_out_written += 1;
+    } else if (max_write > 1) {
+      memcpy(data_out_ptr, str, max_write);
+      data_out_ptr += max_write;
+      data_out_written += max_write;
+    }
+
+    if (max_write < str_size) {
+      // Need to write more bytes than will fit in the output buffer. Store the
+      // remainder in the overflow buffer.
+      overflow_ += std::string(str + max_write, str_size - max_write);
+    }
+  }
+
+  // The portion of the find string that is currently matching.
+  size_t find_match_offset_;
+
+  // The likely amount of overflow.
+  size_t replace_overflow_size_;
+
+  // Overflow from the output buffer.
+  std::string overflow_;
+
+  // The original input size.
+  size_t input_size_;
+
+  // The number of times the find string was repeated.
+  size_t repeat_ct_;
+};
+
+// Return a filter error.
+class ResponseFilterError : public ResponseFilterTestBase {
+ public:
+  ResponseFilterError() {}
+
+  FilterStatus Filter(void* data_in,
+                      size_t data_in_size,
+                      size_t& data_in_read,
+                      void* data_out,
+                      size_t data_out_size,
+                      size_t& data_out_written) override {
+    ResponseFilterTestBase::Filter(data_in, data_in_size, data_in_read,
+                                   data_out, data_out_size, data_out_written);
+
+    return RESPONSE_FILTER_ERROR;
+  }
+
+  std::string GetInput() override {
+    return kInputHeader + std::string("ERROR") + kInputFooter;
+  }
+
+  void VerifyOutput(cef_urlrequest_status_t status,
+                    int64 received_content_length,
+                    const std::string& received_content) override {
+    ResponseFilterTestBase::VerifyOutput(status, received_content_length,
+                                         received_content);
+
+    EXPECT_EQ(UR_FAILED, status);
+
+    // Expect empty content.
+    EXPECT_STREQ("", received_content.c_str());
+    EXPECT_EQ(0U, received_content_length);
+
+    // Expect to only be called one time.
+    EXPECT_EQ(filter_count_, 1U);
+  }
+
+  void VerifyStatusCode(int httpStatusCode) const override {
+    EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, httpStatusCode);
+  }
+};
+
+class ResponseFilterTestHandler : public TestHandler {
+ public:
+  explicit ResponseFilterTestHandler(
+      CefRefPtr<ResponseFilterTestBase> response_filter)
+      : response_filter_(response_filter) {}
+
+  void RunTest() override {
+    const std::string& resource = response_filter_->GetInput();
+    AddResource(kResponseFilterTestUrl, resource, "text/html");
+
+    // Create the browser.
+    CreateBrowser(kResponseFilterTestUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefResponse> response) override {
+    EXPECT_IO_THREAD();
+
+    DCHECK(!got_resource_response_filter_);
+    got_resource_response_filter_.yes();
+    return response_filter_;
+  }
+
+  void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefRequest> request,
+                              CefRefPtr<CefResponse> response,
+                              URLRequestStatus status,
+                              int64 received_content_length) override {
+    EXPECT_IO_THREAD();
+
+    DCHECK(!got_resource_load_complete_);
+    got_resource_load_complete_.yes();
+
+    status_ = status;
+    received_content_length_ = received_content_length;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    DCHECK(!got_load_end_);
+    got_load_end_.yes();
+
+    response_filter_->VerifyStatusCode(httpStatusCode);
+
+    GetOutputContent(frame);
+  }
+
+ private:
+  // Retrieve the output content using a StringVisitor. This effectively
+  // serializes the DOM from the renderer process so any comparison to the
+  // filter output is somewhat error-prone.
+  void GetOutputContent(CefRefPtr<CefFrame> frame) {
+    class StringVisitor : public CefStringVisitor {
+     public:
+      typedef base::Callback<void(const std::string& /*received_content*/)>
+          VisitorCallback;
+
+      explicit StringVisitor(const VisitorCallback& callback)
+          : callback_(callback) {}
+
+      void Visit(const CefString& string) override {
+        callback_.Run(string);
+        callback_.Reset();
+      }
+
+     private:
+      VisitorCallback callback_;
+
+      IMPLEMENT_REFCOUNTING(StringVisitor);
+    };
+
+    frame->GetSource(new StringVisitor(
+        base::Bind(&ResponseFilterTestHandler::VerifyOutput, this)));
+  }
+
+  void VerifyOutput(const std::string& received_content) {
+    response_filter_->VerifyOutput(status_, received_content_length_,
+                                   received_content);
+    response_filter_ = nullptr;
+
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_resource_response_filter_);
+    EXPECT_TRUE(got_resource_load_complete_);
+    EXPECT_TRUE(got_load_end_);
+
+    TestHandler::DestroyTest();
+  }
+
+  CefRefPtr<ResponseFilterTestBase> response_filter_;
+
+  TrackCallback got_resource_response_filter_;
+  TrackCallback got_resource_load_complete_;
+  TrackCallback got_load_end_;
+
+  URLRequestStatus status_;
+  int64 received_content_length_;
+
+  IMPLEMENT_REFCOUNTING(ResponseFilterTestHandler);
+};
+
+}  // namespace
+
+// Pass through contents unchanged. Read all available input.
+TEST(ResourceRequestHandlerTest, FilterPassThruReadAll) {
+  CefRefPtr<ResponseFilterTestHandler> handler =
+      new ResponseFilterTestHandler(new ResponseFilterPassThru(false));
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Pass through contents unchanged. Read limited input.
+TEST(ResourceRequestHandlerTest, FilterPassThruReadLimited) {
+  CefRefPtr<ResponseFilterTestHandler> handler =
+      new ResponseFilterTestHandler(new ResponseFilterPassThru(true));
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Find/replace contents such that we occasionally need more data.
+TEST(ResourceRequestHandlerTest, FilterNeedMore) {
+  CefRefPtr<ResponseFilterTestHandler> handler =
+      new ResponseFilterTestHandler(new ResponseFilterNeedMore());
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Error during filtering.
+TEST(ResourceRequestHandlerTest, FilterError) {
+  CefRefPtr<ResponseFilterTestHandler> handler =
+      new ResponseFilterTestHandler(new ResponseFilterError());
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Entry point for registering custom schemes.
+// Called from client_app_delegates.cc.
+void RegisterResourceRequestHandlerCustomSchemes(
+    CefRawPtr<CefSchemeRegistrar> registrar,
+    std::vector<CefString>& cookiable_schemes) {
+  // Add a custom standard scheme.
+  registrar->AddCustomScheme(
+      "rrhcustom", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
+}
diff --git a/src/tests/ceftests/resource_util_linux.cc b/src/tests/ceftests/resource_util_linux.cc
new file mode 100644
index 0000000..6409a0d
--- /dev/null
+++ b/src/tests/ceftests/resource_util_linux.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tests/shared/browser/resource_util.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+namespace client {
+
+bool GetResourceDir(std::string& dir) {
+  char buff[1024];
+
+  // Retrieve the executable path.
+  ssize_t len = readlink("/proc/self/exe", buff, sizeof(buff) - 1);
+  if (len == -1)
+    return false;
+
+  buff[len] = 0;
+
+  // Remove the executable name from the path.
+  char* pos = strrchr(buff, '/');
+  if (!pos)
+    return false;
+
+  // Add "ceftests_files" to the path.
+  strcpy(pos + 1, "ceftests_files");
+  dir = std::string(buff);
+  return true;
+}
+
+}  // namespace client
diff --git a/src/tests/ceftests/resource_util_win_idmap.cc b/src/tests/ceftests/resource_util_win_idmap.cc
new file mode 100644
index 0000000..cd54dba
--- /dev/null
+++ b/src/tests/ceftests/resource_util_win_idmap.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <cstring>
+
+#include "tests/ceftests/resource.h"
+
+namespace client {
+
+int GetResourceId(const char* resource_name) {
+  // Map of resource labels to BINARY id values.
+  static struct _resource_map {
+    const char* name;
+    int id;
+  } resource_map[] = {
+      {"osr_test.html", IDS_OSRTEST_HTML},
+      {"pdf.html", IDS_PDF_HTML},
+      {"pdf.pdf", IDS_PDF_PDF},
+      {"window_icon.1x.png", IDS_WINDOW_ICON_1X_PNG},
+      {"window_icon.2x.png", IDS_WINDOW_ICON_2X_PNG},
+  };
+
+  for (size_t i = 0; i < sizeof(resource_map) / sizeof(_resource_map); ++i) {
+    if (!strcmp(resource_map[i].name, resource_name))
+      return resource_map[i].id;
+  }
+
+  return 0;
+}
+
+}  // namespace client
diff --git a/src/tests/ceftests/resources/mac/English.lproj/InfoPlist.strings b/src/tests/ceftests/resources/mac/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..fe2abe1
--- /dev/null
+++ b/src/tests/ceftests/resources/mac/English.lproj/InfoPlist.strings
@@ -0,0 +1,3 @@
+/* Localized versions of Info.plist keys */
+
+NSHumanReadableCopyright = "© Chromium Embedded Framework Authors, 2010";
diff --git a/src/tests/ceftests/resources/mac/English.lproj/MainMenu.xib b/src/tests/ceftests/resources/mac/English.lproj/MainMenu.xib
new file mode 100644
index 0000000..bd8a2c0
--- /dev/null
+++ b/src/tests/ceftests/resources/mac/English.lproj/MainMenu.xib
@@ -0,0 +1,330 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16B2657" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+    <dependencies>
+        <deployment version="1090" identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application"/>
+        <menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
+            <items>
+                <menuItem title="cefclient" id="56">
+                    <menu key="submenu" title="TestShell" systemMenu="apple" id="57">
+                        <items>
+                            <menuItem title="About cefclient" id="58">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="236">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121"/>
+                            <menuItem isSeparatorItem="YES" id="143">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Services" id="131">
+                                <menu key="submenu" title="Services" systemMenu="services" id="130"/>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="144">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Hide cefclient" keyEquivalent="h" id="134">
+                                <connections>
+                                    <action selector="hide:" target="-1" id="367"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Hide Others" keyEquivalent="h" id="145">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="hideOtherApplications:" target="-1" id="368"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Show All" id="150">
+                                <connections>
+                                    <action selector="unhideAllApplications:" target="-1" id="370"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="149">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Quit cefclient" keyEquivalent="q" id="136" userLabel="1111">
+                                <connections>
+                                    <action selector="terminate:" target="-1" id="369"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="File" id="83">
+                    <menu key="submenu" title="File" id="81">
+                        <items>
+                            <menuItem title="New" keyEquivalent="n" id="82" userLabel="9">
+                                <connections>
+                                    <action selector="newDocument:" target="-1" id="373"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open…" keyEquivalent="o" id="72">
+                                <connections>
+                                    <action selector="openDocument:" target="-1" id="374"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open Recent" id="124">
+                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
+                                    <items>
+                                        <menuItem title="Clear Menu" id="126">
+                                            <connections>
+                                                <action selector="clearRecentDocuments:" target="-1" id="127"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="79" userLabel="7">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Close" keyEquivalent="w" id="73" userLabel="1">
+                                <connections>
+                                    <action selector="performClose:" target="-1" id="193"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save" keyEquivalent="s" id="75" userLabel="3">
+                                <connections>
+                                    <action selector="saveDocument:" target="-1" id="362"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save As…" keyEquivalent="S" id="80" userLabel="8">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="saveDocumentAs:" target="-1" id="363"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Revert to Saved" id="112" userLabel="10">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="revertDocumentToSaved:" target="-1" id="364"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="74" userLabel="2">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Page Setup..." keyEquivalent="P" id="77" userLabel="5">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="runPageLayout:" target="-1" id="87"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print…" keyEquivalent="p" id="78" userLabel="6">
+                                <connections>
+                                    <action selector="print:" target="-1" id="86"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Edit" id="217">
+                    <menu key="submenu" title="Edit" id="205">
+                        <items>
+                            <menuItem title="Undo" keyEquivalent="z" id="207">
+                                <connections>
+                                    <action selector="undo:" target="-1" id="223"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Redo" keyEquivalent="Z" id="215">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="redo:" target="-1" id="231"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="206">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Cut" keyEquivalent="x" id="199">
+                                <connections>
+                                    <action selector="cut:" target="-1" id="228"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Copy" keyEquivalent="c" id="197">
+                                <connections>
+                                    <action selector="copy:" target="-1" id="224"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste" keyEquivalent="v" id="203">
+                                <connections>
+                                    <action selector="paste:" target="-1" id="226"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Delete" id="202">
+                                <connections>
+                                    <action selector="delete:" target="-1" id="235"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Select All" keyEquivalent="a" id="198">
+                                <connections>
+                                    <action selector="selectAll:" target="-1" id="232"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="214">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Find" id="218">
+                                <menu key="submenu" title="Find" id="220">
+                                    <items>
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="241"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
+                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                        </menuItem>
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="210">
+                                            <connections>
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Spelling and Grammar" id="216">
+                                <menu key="submenu" title="Spelling and Grammar" id="200">
+                                    <items>
+                                        <menuItem title="Show Spelling…" keyEquivalent=":" id="204">
+                                            <connections>
+                                                <action selector="showGuessPanel:" target="-1" id="230"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Spelling" keyEquivalent=";" id="201">
+                                            <connections>
+                                                <action selector="checkSpelling:" target="-1" id="225"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Spelling While Typing" id="219">
+                                            <connections>
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Grammar With Spelling" id="346">
+                                            <connections>
+                                                <action selector="toggleGrammarChecking:" target="-1" id="347"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Substitutions" id="348">
+                                <menu key="submenu" title="Substitutions" id="349">
+                                    <items>
+                                        <menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
+                                            <connections>
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
+                                            <connections>
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
+                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Speech" id="211">
+                                <menu key="submenu" title="Speech" id="212">
+                                    <items>
+                                        <menuItem title="Start Speaking" id="196">
+                                            <connections>
+                                                <action selector="startSpeaking:" target="-1" id="233"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Stop Speaking" id="195">
+                                            <connections>
+                                                <action selector="stopSpeaking:" target="-1" id="227"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Format" id="299">
+                    <menu key="submenu" title="Format" id="300">
+                        <items>
+                            <menuItem title="Show Fonts" keyEquivalent="t" id="344"/>
+                            <menuItem title="Show Colors" keyEquivalent="C" id="345">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="orderFrontColorPanel:" target="-1" id="361"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="View" id="295">
+                    <menu key="submenu" title="View" id="296">
+                        <items>
+                            <menuItem title="Show Toolbar" keyEquivalent="t" id="297">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleToolbarShown:" target="-1" id="366"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Customize Toolbar…" id="298">
+                                <connections>
+                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Window" id="19">
+                    <menu key="submenu" title="Window" systemMenu="window" id="24">
+                        <items>
+                            <menuItem title="Minimize" keyEquivalent="m" id="23">
+                                <connections>
+                                    <action selector="performMiniaturize:" target="-1" id="37"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom" id="239">
+                                <connections>
+                                    <action selector="performZoom:" target="-1" id="240"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="92">
+                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+                            </menuItem>
+                            <menuItem title="Bring All to Front" id="5">
+                                <connections>
+                                    <action selector="arrangeInFront:" target="-1" id="39"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Help" id="103" userLabel="1">
+                    <menu key="submenu" title="Help" id="106" userLabel="2">
+                        <items>
+                            <menuItem title="cefclient Help" keyEquivalent="?" id="111">
+                                <connections>
+                                    <action selector="showHelp:" target="-1" id="360"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+            </items>
+        </menu>
+        <userDefaultsController representsSharedInstance="YES" id="389"/>
+    </objects>
+</document>
diff --git a/src/tests/ceftests/resources/mac/Info.plist b/src/tests/ceftests/resources/mac/Info.plist
new file mode 100644
index 0000000..18c373b
--- /dev/null
+++ b/src/tests/ceftests/resources/mac/Info.plist
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string>ceftests.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.cef.ceftests</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>LSEnvironment</key>
+	<dict>
+		<key>MallocNanoZone</key>
+		<string>0</string>
+	</dict>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/src/tests/ceftests/resources/mac/ceftests.icns b/src/tests/ceftests/resources/mac/ceftests.icns
new file mode 100644
index 0000000..f36742d
--- /dev/null
+++ b/src/tests/ceftests/resources/mac/ceftests.icns
Binary files differ
diff --git a/src/tests/ceftests/resources/mac/helper-Info.plist b/src/tests/ceftests/resources/mac/helper-Info.plist
new file mode 100644
index 0000000..d2359cc
--- /dev/null
+++ b/src/tests/ceftests/resources/mac/helper-Info.plist
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleDisplayName</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.cef.ceftests.helper${BUNDLE_ID_SUFFIX}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>LSEnvironment</key>
+	<dict>
+		<key>MallocNanoZone</key>
+		<string>0</string>
+	</dict>
+	<key>LSFileQuarantineEnabled</key>
+	<true/>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.9.0</string>
+	<key>LSUIElement</key>
+	<string>1</string>
+	<key>NSSupportsAutomaticGraphicsSwitching</key>
+	<true/>
+</dict>
+</plist>
diff --git a/src/tests/ceftests/resources/win/ceftests.exe.manifest b/src/tests/ceftests/resources/win/ceftests.exe.manifest
new file mode 100644
index 0000000..d36f084
--- /dev/null
+++ b/src/tests/ceftests/resources/win/ceftests.exe.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+
+  <!--The compatibility section will be merged from build/win/compatibility.manifest -->
+
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
+    </dependentAssembly>
+  </dependency>
+
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+</assembly>
diff --git a/src/tests/ceftests/resources/win/ceftests.ico b/src/tests/ceftests/resources/win/ceftests.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/src/tests/ceftests/resources/win/ceftests.ico
Binary files differ
diff --git a/src/tests/ceftests/resources/win/ceftests.rc b/src/tests/ceftests/resources/win/ceftests.rc
new file mode 100644
index 0000000..841b8eb
--- /dev/null
+++ b/src/tests/ceftests/resources/win/ceftests.rc
@@ -0,0 +1,126 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "tests/ceftests/resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#include "include/cef_version.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Binary
+//
+
+IDS_OSRTEST_HTML BINARY "..\\..\\..\\shared\\resources\\osr_test.html"
+IDS_PDF_HTML BINARY "..\\..\\..\\shared\\resources\\pdf.html"
+IDS_PDF_PDF BINARY "..\\..\\..\\shared\\resources\\pdf.pdf"
+IDS_WINDOW_ICON_1X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.1x.png"
+IDS_WINDOW_ICON_2X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.2x.png"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+ICI_UNITTESTS           ICON                    "ceftests.ico"
+IDI_SMALL               ICON                    "small.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0
+ PRODUCTVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "FileDescription", "Chromium Embedded Framework (CEF) Unit Test Application"
+            VALUE "FileVersion", CEF_VERSION
+            VALUE "InternalName", "ceftests"
+            VALUE "LegalCopyright", "Copyright (C) " MAKE_STRING(COPYRIGHT_YEAR) " The Chromium Embedded Framework Authors"
+            VALUE "OriginalFilename", "ceftests.exe"
+            VALUE "ProductName", "Chromium Embedded Framework (CEF) Unit Test Application"
+            VALUE "ProductVersion", CEF_VERSION
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+    "#include ""windows.h""\r\n"
+    "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/src/tests/ceftests/resources/win/small.ico b/src/tests/ceftests/resources/win/small.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/src/tests/ceftests/resources/win/small.ico
Binary files differ
diff --git a/src/tests/ceftests/response_unittest.cc b/src/tests/ceftests/response_unittest.cc
new file mode 100644
index 0000000..4ef70be
--- /dev/null
+++ b/src/tests/ceftests/response_unittest.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_response.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+TEST(ResponseTest, SetGetHeaderByName) {
+  CefRefPtr<CefResponse> response(CefResponse::Create());
+  EXPECT_TRUE(response.get() != nullptr);
+
+  CefResponse::HeaderMap headers, expectedHeaders;
+
+  response->SetHeaderByName("HeaderA", "ValueA", false);
+  response->SetHeaderByName("HeaderB", "ValueB", false);
+
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueA"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+
+  // Case insensitive retrieval.
+  EXPECT_STREQ("ValueA",
+               response->GetHeaderByName("headera").ToString().c_str());
+  EXPECT_STREQ("ValueB",
+               response->GetHeaderByName("headerb").ToString().c_str());
+  EXPECT_STREQ("", response->GetHeaderByName("noexist").ToString().c_str());
+
+  response->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+
+  // Replace an existing value.
+  response->SetHeaderByName("HeaderA", "ValueANew", true);
+
+  expectedHeaders.clear();
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueANew"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+
+  // Case insensitive retrieval.
+  EXPECT_STREQ("ValueANew",
+               response->GetHeaderByName("headerA").ToString().c_str());
+
+  response->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+
+  // Header with multiple values.
+  expectedHeaders.clear();
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueA1"));
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueA2"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+  response->SetHeaderMap(expectedHeaders);
+
+  // When there are multiple values only the first is returned.
+  EXPECT_STREQ("ValueA1",
+               response->GetHeaderByName("headera").ToString().c_str());
+
+  // Don't overwrite the value.
+  response->SetHeaderByName("HeaderA", "ValueANew", false);
+
+  response->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+
+  // Overwrite the value (remove the duplicates).
+  response->SetHeaderByName("HeaderA", "ValueANew", true);
+
+  expectedHeaders.clear();
+  expectedHeaders.insert(std::make_pair("HeaderA", "ValueANew"));
+  expectedHeaders.insert(std::make_pair("HeaderB", "ValueB"));
+
+  response->GetHeaderMap(headers);
+  TestMapEqual(expectedHeaders, headers, false);
+}
diff --git a/src/tests/ceftests/routing_test_handler.cc b/src/tests/ceftests/routing_test_handler.cc
new file mode 100644
index 0000000..0f02ced
--- /dev/null
+++ b/src/tests/ceftests/routing_test_handler.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppRenderer;
+
+namespace {
+
+void SetRouterConfig(CefMessageRouterConfig& config) {
+  config.js_query_function = "testQuery";
+  config.js_cancel_function = "testQueryCancel";
+}
+
+// Handle the renderer side of the routing implementation.
+class RoutingRenderDelegate : public ClientAppRenderer::Delegate {
+ public:
+  RoutingRenderDelegate() {}
+
+  void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) override {
+    // Create the renderer-side router for query handling.
+    CefMessageRouterConfig config;
+    SetRouterConfig(config);
+    message_router_ = CefMessageRouterRendererSide::Create(config);
+  }
+
+  void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefV8Context> context) override {
+    message_router_->OnContextCreated(browser, frame, context);
+  }
+
+  void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
+                         CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefV8Context> context) override {
+    message_router_->OnContextReleased(browser, frame, context);
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    return message_router_->OnProcessMessageReceived(browser, frame,
+                                                     source_process, message);
+  }
+
+ private:
+  CefRefPtr<CefMessageRouterRendererSide> message_router_;
+
+  IMPLEMENT_REFCOUNTING(RoutingRenderDelegate);
+};
+
+}  // namespace
+
+RoutingTestHandler::RoutingTestHandler() {}
+
+void RoutingTestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  if (!message_router_.get()) {
+    // Create the browser-side router for query handling.
+    CefMessageRouterConfig config;
+    SetRouterConfig(config);
+    message_router_ = CefMessageRouterBrowserSide::Create(config);
+    message_router_->AddHandler(this, false);
+  }
+  TestHandler::OnAfterCreated(browser);
+}
+
+void RoutingTestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  message_router_->OnBeforeClose(browser);
+  TestHandler::OnBeforeClose(browser);
+}
+
+void RoutingTestHandler::OnRenderProcessTerminated(
+    CefRefPtr<CefBrowser> browser,
+    TerminationStatus status) {
+  message_router_->OnRenderProcessTerminated(browser);
+}
+
+bool RoutingTestHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                                        CefRefPtr<CefFrame> frame,
+                                        CefRefPtr<CefRequest> request,
+                                        bool user_gesture,
+                                        bool is_redirect) {
+  message_router_->OnBeforeBrowse(browser, frame);
+  return false;
+}
+
+bool RoutingTestHandler::OnProcessMessageReceived(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefProcessId source_process,
+    CefRefPtr<CefProcessMessage> message) {
+  return message_router_->OnProcessMessageReceived(browser, frame,
+                                                   source_process, message);
+}
+
+// Entry point for creating the test delegate.
+// Called from client_app_delegates.cc.
+void CreateRoutingTestHandlerDelegate(
+    ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new RoutingRenderDelegate);
+}
diff --git a/src/tests/ceftests/routing_test_handler.h b/src/tests/ceftests/routing_test_handler.h
new file mode 100644
index 0000000..89f820c
--- /dev/null
+++ b/src/tests/ceftests/routing_test_handler.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_ROUTING_TEST_HANDLER_H_
+#define CEF_TESTS_UNITTESTS_ROUTING_TEST_HANDLER_H_
+#pragma once
+
+#include "include/wrapper/cef_message_router.h"
+#include "tests/ceftests/test_handler.h"
+
+// Extends TestHandler to provide message routing functionality. The
+// RoutingTestHandler implementation must be called from subclass
+// overrides unless otherwise indicated.
+class RoutingTestHandler : public TestHandler,
+                           public CefMessageRouterBrowserSide::Handler {
+ public:
+  RoutingTestHandler();
+
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
+  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                 TerminationStatus status) override;
+
+  // Only call this method if the navigation isn't canceled.
+  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                      CefRefPtr<CefFrame> frame,
+                      CefRefPtr<CefRequest> request,
+                      bool user_gesture,
+                      bool is_redirect) override;
+
+  // Returns true if the router handled the navigation.
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override;
+
+ private:
+  CefRefPtr<CefMessageRouterBrowserSide> message_router_;
+};
+
+#endif  // CEF_TESTS_UNITTESTS_ROUTING_TEST_HANDLER_H_
diff --git a/src/tests/ceftests/run_all_unittests.cc b/src/tests/ceftests/run_all_unittests.cc
new file mode 100644
index 0000000..4c46abd
--- /dev/null
+++ b/src/tests/ceftests/run_all_unittests.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_build.h"
+#include "include/cef_config.h"
+
+#if defined(OS_LINUX) && defined(CEF_X11)
+#include <X11/Xlib.h>
+// Definitions conflict with gtest.
+#undef None
+#undef Bool
+#endif
+
+#if defined(OS_POSIX)
+#include <unistd.h>
+#endif
+
+#include "include/base/cef_bind.h"
+#include "include/cef_app.h"
+#include "include/cef_task.h"
+#include "include/cef_thread.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_suite.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+#include "tests/shared/browser/main_message_loop_std.h"
+#include "tests/shared/common/client_app_other.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+#if defined(OS_MACOSX)
+#include "include/wrapper/cef_library_loader.h"
+#endif
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
+// to the CMake command-line to disable use of the sandbox.
+#if defined(OS_WIN) && defined(CEF_USE_SANDBOX)
+#include "include/cef_sandbox_win.h"
+
+// The cef_sandbox.lib static library may not link successfully with all VS
+// versions.
+#pragma comment(lib, "cef_sandbox.lib")
+#endif
+
+namespace {
+
+void QuitMessageLoop() {
+  client::MainMessageLoop* message_loop = client::MainMessageLoop::Get();
+  if (message_loop)
+    message_loop->Quit();
+  else
+    CefQuitMessageLoop();
+}
+
+void sleep(int64 ms) {
+#if defined(OS_WIN)
+  Sleep(ms);
+#elif defined(OS_POSIX)
+  usleep(ms * 1000);
+#else
+#error Unsupported platform
+#endif
+}
+
+// Called on the test thread.
+void RunTestsOnTestThread() {
+  // Run the test suite.
+  CefTestSuite::GetInstance()->Run();
+
+  // Wait for all browsers to exit.
+  while (TestHandler::HasBrowser())
+    sleep(100);
+
+  // Quit the CEF message loop.
+  CefPostTask(TID_UI, base::Bind(&QuitMessageLoop));
+}
+
+// Called on the UI thread.
+void ContinueOnUIThread(CefRefPtr<CefTaskRunner> test_task_runner) {
+  // Run the test suite on the test thread.
+  test_task_runner->PostTask(
+      CefCreateClosureTask(base::Bind(&RunTestsOnTestThread)));
+}
+
+#if defined(OS_LINUX) && defined(CEF_X11)
+int XErrorHandlerImpl(Display* display, XErrorEvent* event) {
+  LOG(WARNING) << "X error received: "
+               << "type " << event->type << ", "
+               << "serial " << event->serial << ", "
+               << "error_code " << static_cast<int>(event->error_code) << ", "
+               << "request_code " << static_cast<int>(event->request_code)
+               << ", "
+               << "minor_code " << static_cast<int>(event->minor_code);
+  return 0;
+}
+
+int XIOErrorHandlerImpl(Display* display) {
+  return 0;
+}
+#endif  // defined(OS_LINUX) && defined(CEF_X11)
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+#if defined(OS_MACOSX)
+  // Load the CEF framework library at runtime instead of linking directly
+  // as required by the macOS sandbox implementation.
+  CefScopedLibraryLoader library_loader;
+  if (!library_loader.LoadInMain())
+    return 1;
+#endif
+
+  // Create the singleton test suite object.
+  CefTestSuite test_suite(argc, argv);
+
+#if defined(OS_WIN)
+  if (test_suite.command_line()->HasSwitch("enable-high-dpi-support")) {
+    // Enable High-DPI support on Windows 7 and newer.
+    CefEnableHighDPISupport();
+  }
+
+  CefMainArgs main_args(::GetModuleHandle(nullptr));
+#else
+  CefMainArgs main_args(argc, argv);
+#endif
+
+  void* windows_sandbox_info = nullptr;
+
+#if defined(OS_WIN) && defined(CEF_USE_SANDBOX)
+  // Manages the life span of the sandbox information object.
+  CefScopedSandboxInfo scoped_sandbox;
+  windows_sandbox_info = scoped_sandbox.sandbox_info();
+#endif
+
+  // Create a ClientApp of the correct type.
+  CefRefPtr<CefApp> app;
+  client::ClientApp::ProcessType process_type =
+      client::ClientApp::GetProcessType(test_suite.command_line());
+  if (process_type == client::ClientApp::BrowserProcess) {
+    app = new client::ClientAppBrowser();
+#if !defined(OS_MACOSX)
+  } else if (process_type == client::ClientApp::RendererProcess ||
+             process_type == client::ClientApp::ZygoteProcess) {
+    app = new client::ClientAppRenderer();
+  } else if (process_type == client::ClientApp::OtherProcess) {
+    app = new client::ClientAppOther();
+  }
+
+  // Execute the secondary process, if any.
+  int exit_code = CefExecuteProcess(main_args, app, windows_sandbox_info);
+  if (exit_code >= 0)
+    return exit_code;
+#else
+  } else {
+    // On OS X this executable is only used for the main process.
+    NOTREACHED();
+  }
+#endif
+
+  CefSettings settings;
+
+#if !defined(CEF_USE_SANDBOX)
+  settings.no_sandbox = true;
+#endif
+
+  test_suite.GetSettings(settings);
+
+#if defined(OS_MACOSX)
+  // Platform-specific initialization.
+  extern void PlatformInit();
+  PlatformInit();
+#endif
+
+#if defined(OS_LINUX) && defined(CEF_X11)
+  // Install xlib error handlers so that the application won't be terminated
+  // on non-fatal errors.
+  XSetErrorHandler(XErrorHandlerImpl);
+  XSetIOErrorHandler(XIOErrorHandlerImpl);
+#endif
+
+  // Create the MessageLoop.
+  scoped_ptr<client::MainMessageLoop> message_loop;
+  if (!settings.multi_threaded_message_loop) {
+    if (settings.external_message_pump)
+      message_loop = client::MainMessageLoopExternalPump::Create();
+    else
+      message_loop.reset(new client::MainMessageLoopStd);
+  }
+
+  // Initialize CEF.
+  CefInitialize(main_args, settings, app, windows_sandbox_info);
+
+  // Initialize the testing framework.
+  test_suite.InitMainProcess();
+
+  int retval;
+
+  if (settings.multi_threaded_message_loop) {
+    // Run the test suite on the main thread.
+    retval = test_suite.Run();
+  } else {
+    // Create and start the test thread.
+    CefRefPtr<CefThread> thread = CefThread::CreateThread("test_thread");
+    if (!thread)
+      return 1;
+
+    // Start the tests from the UI thread so that any pending UI tasks get a
+    // chance to execute first.
+    CefPostTask(TID_UI,
+                base::Bind(&ContinueOnUIThread, thread->GetTaskRunner()));
+
+    // Run the CEF message loop.
+    message_loop->Run();
+
+    // The test suite has completed.
+    retval = test_suite.retval();
+
+    // Terminate the test thread.
+    thread->Stop();
+    thread = nullptr;
+  }
+
+  // Shut down CEF.
+  CefShutdown();
+
+  test_suite.DeleteTempDirectories();
+
+  // Destroy the MessageLoop.
+  message_loop.reset(nullptr);
+
+#if defined(OS_MACOSX)
+  // Platform-specific cleanup.
+  extern void PlatformCleanup();
+  PlatformCleanup();
+#endif
+
+  return retval;
+}
diff --git a/src/tests/ceftests/run_all_unittests_mac.mm b/src/tests/ceftests/run_all_unittests_mac.mm
new file mode 100644
index 0000000..2c8c17b
--- /dev/null
+++ b/src/tests/ceftests/run_all_unittests_mac.mm
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_app.h"
+#import "include/cef_application_mac.h"
+
+// Memory AutoRelease pool.
+static NSAutoreleasePool* g_autopool = nil;
+
+// Provide the CefAppProtocol implementation required by CEF.
+@interface TestApplication : NSApplication<CefAppProtocol> {
+ @private
+  BOOL handlingSendEvent_;
+}
+@end
+
+@implementation TestApplication
+- (BOOL)isHandlingSendEvent {
+  return handlingSendEvent_;
+}
+
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
+  handlingSendEvent_ = handlingSendEvent;
+}
+
+- (void)sendEvent:(NSEvent*)event {
+  CefScopedSendingEvent sendingEventScoper;
+  [super sendEvent:event];
+}
+@end
+
+void PlatformInit() {
+  // Initialize the AutoRelease pool.
+  g_autopool = [[NSAutoreleasePool alloc] init];
+
+  // Initialize the TestApplication instance.
+  [TestApplication sharedApplication];
+}
+
+void PlatformCleanup() {
+  // Release the AutoRelease pool.
+  [g_autopool release];
+}
diff --git a/src/tests/ceftests/scheme_handler_unittest.cc b/src/tests/ceftests/scheme_handler_unittest.cc
new file mode 100644
index 0000000..4fd4d07
--- /dev/null
+++ b/src/tests/ceftests/scheme_handler_unittest.cc
@@ -0,0 +1,2440 @@
+// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_callback.h"
+#include "include/cef_origin_whitelist.h"
+#include "include/cef_scheme.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_suite.h"
+#include "tests/ceftests/test_util.h"
+
+namespace {
+
+class TestResults {
+ public:
+  TestResults() : status_code(200), sub_status_code(200), delay(0) {}
+
+  void reset() {
+    url.clear();
+    html.clear();
+    status_code = 200;
+    response_error_code = ERR_NONE;
+    expected_error_code = ERR_NONE;
+    redirect_url.clear();
+    sub_url.clear();
+    sub_html.clear();
+    sub_status_code = 200;
+    sub_allow_origin.clear();
+    exit_url.clear();
+    accept_language.clear();
+    delay = 0;
+    got_request.reset();
+    got_read.reset();
+    got_output.reset();
+    got_sub_output.reset();
+    got_redirect.reset();
+    got_error.reset();
+    got_sub_error.reset();
+    got_sub_request.reset();
+    got_sub_read.reset();
+    got_sub_success.reset();
+    got_exit_request.reset();
+  }
+
+  std::string url;
+  std::string html;
+  int status_code;
+
+  // Error code set on the response.
+  cef_errorcode_t response_error_code;
+  // Error code expected in OnLoadError.
+  cef_errorcode_t expected_error_code;
+
+  // Used for testing redirects
+  std::string redirect_url;
+
+  // Used for testing XHR requests
+  std::string sub_url;
+  std::string sub_html;
+  int sub_status_code;
+  std::string sub_allow_origin;
+  std::string sub_redirect_url;
+  std::string exit_url;
+
+  // Used for testing per-browser Accept-Language.
+  std::string accept_language;
+
+  // Delay for returning scheme handler results.
+  int delay;
+
+  TrackCallback got_request, got_read, got_output, got_sub_output, got_redirect,
+      got_error, got_sub_error, got_sub_redirect, got_sub_request, got_sub_read,
+      got_sub_success, got_exit_request;
+};
+
+// Current scheme handler object. Used when destroying the test from
+// ClientSchemeHandler::ProcessRequest().
+class TestSchemeHandler;
+TestSchemeHandler* g_current_handler = nullptr;
+
+class TestSchemeHandler : public TestHandler {
+ public:
+  explicit TestSchemeHandler(TestResults* tr) : test_results_(tr) {
+    g_current_handler = this;
+  }
+
+  void PopulateBrowserSettings(CefBrowserSettings* settings) override {
+    if (!test_results_->accept_language.empty()) {
+      CefString(&settings->accept_language_list) =
+          test_results_->accept_language;
+    }
+  }
+
+  void RunTest() override {
+    CreateBrowser(test_results_->url);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  // Necessary to make the method public in order to destroy the test from
+  // ClientSchemeHandler::ProcessRequest().
+  void DestroyTest() override { TestHandler::DestroyTest(); }
+
+  void DestroyTestIfDone() {
+    if (!test_results_->exit_url.empty() && !test_results_->got_exit_request) {
+      return;
+    }
+
+    if (!test_results_->sub_url.empty() &&
+        !(test_results_->got_sub_output || test_results_->got_sub_error ||
+          test_results_->got_exit_request)) {
+      return;
+    }
+
+    if (!(test_results_->got_output || test_results_->got_error)) {
+      return;
+    }
+
+    DestroyTest();
+  }
+
+  bool IsExitURL(const std::string& url) const {
+    return !test_results_->exit_url.empty() &&
+           url.find(test_results_->exit_url) != std::string::npos;
+  }
+
+  cef_return_value_t OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    const std::string& newUrl = request->GetURL();
+    if (IsExitURL(newUrl)) {
+      test_results_->got_exit_request.yes();
+      // XHR tests use an exit URL to destroy the test.
+      if (newUrl.find("SUCCESS") != std::string::npos)
+        test_results_->got_sub_success.yes();
+      DestroyTestIfDone();
+      return RV_CANCEL;
+    }
+
+    if (!test_results_->sub_redirect_url.empty() &&
+        newUrl == test_results_->sub_redirect_url) {
+      test_results_->got_sub_redirect.yes();
+      // Redirect to the sub URL.
+      request->SetURL(test_results_->sub_url);
+    } else if (newUrl == test_results_->redirect_url) {
+      test_results_->got_redirect.yes();
+
+      // No read should have occurred for the redirect.
+      EXPECT_TRUE(test_results_->got_request);
+      EXPECT_FALSE(test_results_->got_read);
+
+      // Now loading the redirect URL.
+      test_results_->url = test_results_->redirect_url;
+      test_results_->redirect_url.clear();
+    }
+
+    return RV_CONTINUE;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    const std::string& url = frame->GetURL();
+    if (url == test_results_->url)
+      test_results_->got_output.yes();
+    else if (url == test_results_->sub_url)
+      test_results_->got_sub_output.yes();
+    else if (IsExitURL(url))
+      return;
+
+    if (url == test_results_->url || test_results_->status_code != 200) {
+      // Test that the status code is correct.
+      EXPECT_EQ(httpStatusCode, test_results_->status_code);
+    }
+
+    DestroyTestIfDone();
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    const std::string& url = failedUrl;
+    if (url == test_results_->url)
+      test_results_->got_error.yes();
+    else if (url == test_results_->sub_url)
+      test_results_->got_sub_error.yes();
+    else if (IsExitURL(url))
+      return;
+
+    // Tests sometimes also fail with ERR_ABORTED.
+    if (!(test_results_->expected_error_code == 0 &&
+          errorCode == ERR_ABORTED)) {
+      EXPECT_EQ(test_results_->expected_error_code, errorCode)
+          << failedUrl.ToString();
+    }
+
+    DestroyTestIfDone();
+  }
+
+ protected:
+  TestResults* test_results_;
+
+  IMPLEMENT_REFCOUNTING(TestSchemeHandler);
+};
+
+class ClientSchemeHandlerOld : public CefResourceHandler {
+ public:
+  explicit ClientSchemeHandlerOld(TestResults* tr)
+      : test_results_(tr), offset_(0), is_sub_(false), has_delayed_(false) {}
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+
+    bool handled = false;
+
+    std::string url = request->GetURL();
+    is_sub_ =
+        (!test_results_->sub_url.empty() && test_results_->sub_url == url);
+
+    if (is_sub_) {
+      test_results_->got_sub_request.yes();
+
+      if (!test_results_->sub_html.empty())
+        handled = true;
+    } else {
+      EXPECT_EQ(url, test_results_->url);
+
+      test_results_->got_request.yes();
+
+      if (!test_results_->html.empty())
+        handled = true;
+    }
+
+    std::string accept_language;
+    CefRequest::HeaderMap headerMap;
+    CefRequest::HeaderMap::iterator headerIter;
+    request->GetHeaderMap(headerMap);
+    headerIter = headerMap.find("Accept-Language");
+    if (headerIter != headerMap.end())
+      accept_language = headerIter->second;
+    EXPECT_TRUE(!accept_language.empty());
+
+    if (!test_results_->accept_language.empty()) {
+      // Value from CefBrowserSettings.accept_language set in
+      // PopulateBrowserSettings().
+      EXPECT_STREQ(test_results_->accept_language.data(),
+                   accept_language.data());
+    } else {
+      // CEF_SETTINGS_ACCEPT_LANGUAGE value from
+      // CefSettings.accept_language_list set in CefTestSuite::GetSettings()
+      // and expanded internally by ComputeAcceptLanguageFromPref.
+      EXPECT_STREQ("en-GB,en;q=0.9", accept_language.data());
+    }
+
+    if (handled) {
+      if (test_results_->delay > 0) {
+        // Continue after the delay.
+        CefPostDelayedTask(TID_IO,
+                           base::Bind(&CefCallback::Continue, callback.get()),
+                           test_results_->delay);
+      } else {
+        // Continue immediately.
+        callback->Continue();
+      }
+      return true;
+    } else if (test_results_->response_error_code != ERR_NONE) {
+      // Propagate the error code.
+      callback->Continue();
+      return true;
+    }
+
+    // Response was canceled.
+    if (g_current_handler)
+      g_current_handler->DestroyTest();
+    return false;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    if (is_sub_) {
+      response->SetStatus(test_results_->sub_status_code);
+
+      if (!test_results_->sub_allow_origin.empty()) {
+        // Set the Access-Control-Allow-Origin header to allow cross-domain
+        // scripting.
+        CefResponse::HeaderMap headers;
+        headers.insert(std::make_pair("Access-Control-Allow-Origin",
+                                      test_results_->sub_allow_origin));
+        response->SetHeaderMap(headers);
+      }
+
+      if (!test_results_->sub_html.empty()) {
+        response->SetMimeType("text/html");
+        response_length = test_results_->sub_html.size();
+      }
+    } else if (!test_results_->redirect_url.empty()) {
+      redirectUrl = test_results_->redirect_url;
+    } else if (test_results_->response_error_code != ERR_NONE) {
+      response->SetError(test_results_->response_error_code);
+    } else {
+      response->SetStatus(test_results_->status_code);
+
+      if (!test_results_->html.empty()) {
+        response->SetMimeType("text/html");
+        response_length = test_results_->html.size();
+      }
+    }
+  }
+
+  void Cancel() override { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+
+    if (test_results_->delay > 0) {
+      if (!has_delayed_) {
+        // Continue after a delay.
+        CefPostDelayedTask(
+            TID_IO,
+            base::Bind(&ClientSchemeHandlerOld::ContinueAfterDelay, this,
+                       callback),
+            test_results_->delay);
+        bytes_read = 0;
+        return true;
+      }
+
+      has_delayed_ = false;
+    }
+
+    std::string* data;
+
+    if (is_sub_) {
+      test_results_->got_sub_read.yes();
+      data = &test_results_->sub_html;
+    } else {
+      test_results_->got_read.yes();
+      data = &test_results_->html;
+    }
+
+    bool has_data = false;
+    bytes_read = 0;
+
+    size_t size = data->size();
+    if (offset_ < size) {
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(size - offset_));
+      memcpy(data_out, data->c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+ private:
+  void ContinueAfterDelay(CefRefPtr<CefCallback> callback) {
+    has_delayed_ = true;
+    callback->Continue();
+  }
+
+  TestResults* test_results_;
+  size_t offset_;
+  bool is_sub_;
+  bool has_delayed_;
+
+  IMPLEMENT_REFCOUNTING(ClientSchemeHandlerOld);
+  DISALLOW_COPY_AND_ASSIGN(ClientSchemeHandlerOld);
+};
+
+class ClientSchemeHandler : public CefResourceHandler {
+ public:
+  explicit ClientSchemeHandler(TestResults* tr)
+      : test_results_(tr), offset_(0), is_sub_(false), has_delayed_(false) {}
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    bool handled = false;
+
+    std::string url = request->GetURL();
+    is_sub_ =
+        (!test_results_->sub_url.empty() && test_results_->sub_url == url);
+
+    if (is_sub_) {
+      test_results_->got_sub_request.yes();
+
+      if (!test_results_->sub_html.empty())
+        handled = true;
+    } else {
+      EXPECT_EQ(url, test_results_->url);
+
+      test_results_->got_request.yes();
+
+      if (!test_results_->html.empty())
+        handled = true;
+    }
+
+    std::string accept_language;
+    CefRequest::HeaderMap headerMap;
+    CefRequest::HeaderMap::iterator headerIter;
+    request->GetHeaderMap(headerMap);
+    headerIter = headerMap.find("Accept-Language");
+    if (headerIter != headerMap.end())
+      accept_language = headerIter->second;
+    EXPECT_TRUE(!accept_language.empty());
+
+    if (!test_results_->accept_language.empty()) {
+      // Value from CefBrowserSettings.accept_language set in
+      // PopulateBrowserSettings().
+      EXPECT_STREQ(test_results_->accept_language.data(),
+                   accept_language.data());
+    } else {
+      // CEF_SETTINGS_ACCEPT_LANGUAGE value from
+      // CefSettings.accept_language_list set in CefTestSuite::GetSettings()
+      // and expanded internally by ComputeAcceptLanguageFromPref.
+      EXPECT_STREQ("en-GB,en;q=0.9", accept_language.data());
+    }
+
+    // Continue or cancel the request immediately based on the return value.
+    handle_request = true;
+
+    if (handled) {
+      if (test_results_->delay > 0) {
+        // Continue after the delay.
+        handle_request = false;
+        CefPostDelayedTask(TID_FILE_USER_BLOCKING,
+                           base::Bind(&CefCallback::Continue, callback.get()),
+                           test_results_->delay);
+      }
+      return true;
+    } else if (test_results_->response_error_code != ERR_NONE) {
+      // Propagate the error code.
+      return true;
+    }
+
+    // Response was canceled.
+    if (g_current_handler)
+      g_current_handler->DestroyTest();
+    return false;
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    return false;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    if (is_sub_) {
+      response->SetStatus(test_results_->sub_status_code);
+
+      if (!test_results_->sub_allow_origin.empty()) {
+        // Set the Access-Control-Allow-Origin header to allow cross-domain
+        // scripting.
+        CefResponse::HeaderMap headers;
+        headers.insert(std::make_pair("Access-Control-Allow-Origin",
+                                      test_results_->sub_allow_origin));
+        response->SetHeaderMap(headers);
+      }
+
+      if (!test_results_->sub_html.empty()) {
+        response->SetMimeType("text/html");
+        response_length = test_results_->sub_html.size();
+      }
+    } else if (!test_results_->redirect_url.empty()) {
+      redirectUrl = test_results_->redirect_url;
+    } else if (test_results_->response_error_code != ERR_NONE) {
+      response->SetError(test_results_->response_error_code);
+    } else {
+      response->SetStatus(test_results_->status_code);
+
+      if (!test_results_->html.empty()) {
+        response->SetMimeType("text/html");
+        response_length = test_results_->html.size();
+      }
+    }
+  }
+
+  void Cancel() override { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    if (test_results_->delay > 0) {
+      if (!has_delayed_) {
+        // Continue after a delay.
+        CefPostDelayedTask(TID_FILE_USER_BLOCKING,
+                           base::Bind(&ClientSchemeHandler::ContinueAfterDelay,
+                                      this, data_out, bytes_to_read, callback),
+                           test_results_->delay);
+        bytes_read = 0;
+        return true;
+      }
+
+      has_delayed_ = false;
+    }
+
+    return GetData(data_out, bytes_to_read, bytes_read);
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    bytes_read = -2;
+    return false;
+  }
+
+ private:
+  void ContinueAfterDelay(void* data_out,
+                          int bytes_to_read,
+                          CefRefPtr<CefResourceReadCallback> callback) {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    has_delayed_ = true;
+
+    int bytes_read = 0;
+    GetData(data_out, bytes_to_read, bytes_read);
+    callback->Continue(bytes_read);
+  }
+
+  bool GetData(void* data_out, int bytes_to_read, int& bytes_read) {
+    std::string* data;
+
+    if (is_sub_) {
+      test_results_->got_sub_read.yes();
+      data = &test_results_->sub_html;
+    } else {
+      test_results_->got_read.yes();
+      data = &test_results_->html;
+    }
+
+    // Default to response complete.
+    bool has_data = false;
+    bytes_read = 0;
+
+    size_t size = data->size();
+    if (offset_ < size) {
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(size - offset_));
+      memcpy(data_out, data->c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+  TestResults* test_results_;
+  size_t offset_;
+  bool is_sub_;
+  bool has_delayed_;
+
+  IMPLEMENT_REFCOUNTING(ClientSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(ClientSchemeHandler);
+};
+
+class ClientSchemeHandlerFactory : public CefSchemeHandlerFactory {
+ public:
+  explicit ClientSchemeHandlerFactory(TestResults* tr) : test_results_(tr) {}
+
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+    if (TestOldResourceAPI()) {
+      return new ClientSchemeHandlerOld(test_results_);
+    }
+    return new ClientSchemeHandler(test_results_);
+  }
+
+  TestResults* test_results_;
+
+  IMPLEMENT_REFCOUNTING(ClientSchemeHandlerFactory);
+  DISALLOW_COPY_AND_ASSIGN(ClientSchemeHandlerFactory);
+};
+
+// Global test results object.
+TestResults g_TestResults;
+
+// If |domain| is empty the scheme will be registered as non-standard.
+void RegisterTestScheme(const std::string& scheme, const std::string& domain) {
+  g_TestResults.reset();
+
+  EXPECT_TRUE(CefRegisterSchemeHandlerFactory(
+      scheme, domain, new ClientSchemeHandlerFactory(&g_TestResults)));
+  WaitForIOThread();
+}
+
+void ClearTestSchemes() {
+  EXPECT_TRUE(CefClearSchemeHandlerFactories());
+  WaitForIOThread();
+}
+
+struct XHRTestSettings {
+  XHRTestSettings() : synchronous(true) {}
+
+  std::string url;
+  std::string sub_url;
+  std::string sub_allow_origin;
+  std::string sub_redirect_url;
+  bool synchronous;
+};
+
+void SetUpXHR(const XHRTestSettings& settings) {
+  g_TestResults.sub_url = settings.sub_url;
+  g_TestResults.sub_html = "SUCCESS";
+  g_TestResults.sub_allow_origin = settings.sub_allow_origin;
+  g_TestResults.sub_redirect_url = settings.sub_redirect_url;
+
+  std::string request_url;
+  if (!settings.sub_redirect_url.empty())
+    request_url = settings.sub_redirect_url;
+  else
+    request_url = settings.sub_url;
+
+  g_TestResults.url = settings.url;
+  std::stringstream ss;
+  ss << "<html><head>"
+        "<script language=\"JavaScript\">"
+        "function onResult(val) {"
+        "  document.location = \"http://tests/exit?result=\"+val;"
+        "}"
+        "function execXMLHttpRequest() {";
+  if (settings.synchronous) {
+    ss << "var result = 'FAILURE';"
+          "try {"
+          "  xhr = new XMLHttpRequest();"
+          "  xhr.open(\"GET\", \""
+       << request_url.c_str()
+       << "\", false);"
+          "  xhr.send();"
+          "  result = xhr.responseText;"
+          "} catch(e) {}"
+          "onResult(result)";
+  } else {
+    ss << "xhr = new XMLHttpRequest();"
+          "xhr.open(\"GET\", \""
+       << request_url.c_str()
+       << "\", true);"
+          "xhr.onload = function(e) {"
+          "  if (xhr.readyState === 4) {"
+          "    if (xhr.status === 200) {"
+          "      onResult(xhr.responseText);"
+          "    } else {"
+          "      console.log('XMLHttpRequest failed with status ' + "
+          "xhr.status);"
+          "      onResult('FAILURE');"
+          "    }"
+          "  }"
+          "};"
+          "xhr.onerror = function(e) {"
+          "  console.log('XMLHttpRequest failed with error ' + e);"
+          "  onResult('FAILURE');"
+          "};"
+          "xhr.send()";
+  }
+  ss << "}"
+        "</script>"
+        "</head><body onload=\"execXMLHttpRequest();\">"
+        "Running execXMLHttpRequest..."
+        "</body></html>";
+  g_TestResults.html = ss.str();
+
+  g_TestResults.exit_url = "http://tests/exit";
+}
+
+struct FetchTestSettings {
+  FetchTestSettings() {}
+
+  std::string url;
+  std::string sub_url;
+  std::string sub_allow_origin;
+  std::string sub_redirect_url;
+};
+
+void SetUpFetch(const FetchTestSettings& settings) {
+  g_TestResults.sub_url = settings.sub_url;
+  g_TestResults.sub_html = "SUCCESS";
+  g_TestResults.sub_allow_origin = settings.sub_allow_origin;
+  g_TestResults.sub_redirect_url = settings.sub_redirect_url;
+
+  std::string request_url;
+  if (!settings.sub_redirect_url.empty())
+    request_url = settings.sub_redirect_url;
+  else
+    request_url = settings.sub_url;
+
+  g_TestResults.url = settings.url;
+  std::stringstream ss;
+  ss << "<html><head>"
+        "<script language=\"JavaScript\">"
+        "function onResult(val) {"
+        "  document.location = \"http://tests/exit?result=\"+val;"
+        "}"
+        "function execFetchHttpRequest() {";
+  ss << "fetch('" << request_url.c_str()
+     << "')"
+        ".then(function(response) {"
+        "  if (response.status === 200) {"
+        "      response.text().then(function(text) {"
+        "          onResult(text);"
+        "      }).catch(function(e) {"
+        "          console.log('FetchHttpRequest failed with error ' + e);"
+        "          onResult('FAILURE');        "
+        "      });"
+        "  } else {"
+        "      console.log('XMLHttpRequest failed with status ' + "
+        "      response.status);"
+        "      onResult('FAILURE');"
+        "  }"
+        "}).catch(function(e) {"
+        "  console.log('FetchHttpRequest failed with error ' + e);"
+        "  onResult('FAILURE');"
+        "});"
+     << "}"
+        "</script>"
+        "</head><body onload=\"execFetchHttpRequest();\">"
+        "Running execFetchHttpRequest..."
+        "</body></html>";
+  g_TestResults.html = ss.str();
+
+  g_TestResults.exit_url = "http://tests/exit";
+}  // namespace
+
+void SetUpXSS(const std::string& url,
+              const std::string& sub_url,
+              const std::string& domain = std::string()) {
+  // 1. Load |url| which contains an iframe.
+  // 2. The iframe loads |sub_url|.
+  // 3. |sub_url| tries to call a JS function in |url|.
+  // 4. |url| tries to call a JS function in |sub_url|.
+
+  std::stringstream ss;
+  std::string domain_line;
+  if (!domain.empty())
+    domain_line = "document.domain = '" + domain + "';";
+
+  g_TestResults.sub_url = sub_url;
+  ss << "<html><head>"
+        "<script language=\"JavaScript\">"
+     << domain_line
+     << "function getResult() {"
+        "  return 'SUCCESS';"
+        "}"
+        "function execXSSRequest() {"
+        "  var result = 'FAILURE';"
+        "  try {"
+        "    result = parent.getResult();"
+        "  } catch(e) { console.log(e.stack); }"
+        "  document.location = \"http://tests/exit?result=\"+result;"
+        "}"
+        "</script>"
+        "</head><body onload=\"execXSSRequest();\">"
+        "Running execXSSRequest..."
+        "</body></html>";
+  g_TestResults.sub_html = ss.str();
+
+  g_TestResults.url = url;
+  ss.str("");
+  ss << "<html><head>"
+        "<script language=\"JavaScript\">"
+     << domain_line
+     << ""
+        "function getResult() {"
+        "  try {"
+        "    return document.getElementById('s').contentWindow.getResult();"
+        "  } catch(e) { console.log(e.stack); }"
+        "  return 'FAILURE';"
+        "}"
+        "</script>"
+        "</head><body>"
+        "<iframe src=\""
+     << sub_url.c_str()
+     << "\" id=\"s\">"
+        "</body></html>";
+  g_TestResults.html = ss.str();
+
+  g_TestResults.exit_url = "http://tests/exit";
+}
+
+}  // namespace
+
+// Test that scheme registration/unregistration works as expected.
+TEST(SchemeHandlerTest, Registration) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+  g_TestResults.html =
+      "<html><head></head><body><h1>Success!</h1></body></html>";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  // Unregister the handler.
+  EXPECT_TRUE(CefRegisterSchemeHandlerFactory("customstd", "test", nullptr));
+  WaitForIOThread();
+
+  g_TestResults.got_request.reset();
+  g_TestResults.got_read.reset();
+  g_TestResults.got_output.reset();
+  g_TestResults.expected_error_code = ERR_UNKNOWN_URL_SCHEME;
+  handler->ExecuteTest();
+
+  EXPECT_TRUE(g_TestResults.got_error);
+  EXPECT_FALSE(g_TestResults.got_request);
+  EXPECT_FALSE(g_TestResults.got_read);
+  EXPECT_FALSE(g_TestResults.got_output);
+
+  // Re-register the handler.
+  EXPECT_TRUE(CefRegisterSchemeHandlerFactory(
+      "customstd", "test", new ClientSchemeHandlerFactory(&g_TestResults)));
+  WaitForIOThread();
+
+  g_TestResults.got_error.reset();
+  g_TestResults.expected_error_code = ERR_NONE;
+  handler->ExecuteTest();
+
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_FALSE(g_TestResults.got_error);
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can return normal results.
+TEST(SchemeHandlerTest, CustomStandardNormalResponse) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+  g_TestResults.html =
+      "<html><head></head><body><h1>Success!</h1></body></html>";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can return normal results with delayed
+// responses.
+TEST(SchemeHandlerTest, CustomStandardNormalResponseDelayed) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+  g_TestResults.html =
+      "<html><head></head><body><h1>Success!</h1></body></html>";
+  g_TestResults.delay = 100;
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom nonstandard scheme can return normal results.
+TEST(SchemeHandlerTest, CustomNonStandardNormalResponse) {
+  RegisterTestScheme("customnonstd", std::string());
+  g_TestResults.url = "customnonstd:some%20value";
+  g_TestResults.html =
+      "<html><head></head><body><h1>Success!</h1></body></html>";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can return an error code.
+TEST(SchemeHandlerTest, CustomStandardErrorResponse) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+  g_TestResults.html = "<html><head></head><body><h1>404</h1></body></html>";
+  g_TestResults.status_code = 404;
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can return a CEF error code in the
+// response.
+TEST(SchemeHandlerTest, CustomStandardErrorCodeResponse) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+  g_TestResults.response_error_code = ERR_FILE_TOO_BIG;
+  g_TestResults.expected_error_code = ERR_FILE_TOO_BIG;
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_FALSE(g_TestResults.got_read);
+  EXPECT_FALSE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_error);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom nonstandard scheme can return an error code.
+TEST(SchemeHandlerTest, CustomNonStandardErrorResponse) {
+  RegisterTestScheme("customnonstd", std::string());
+  g_TestResults.url = "customnonstd:some%20value";
+  g_TestResults.html = "<html><head></head><body><h1>404</h1></body></html>";
+  g_TestResults.status_code = 404;
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that custom standard scheme handling fails when the scheme name is
+// incorrect.
+TEST(SchemeHandlerTest, CustomStandardNameNotHandled) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd2://test/run.html";
+  g_TestResults.expected_error_code = ERR_UNKNOWN_URL_SCHEME;
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_FALSE(g_TestResults.got_request);
+  EXPECT_FALSE(g_TestResults.got_read);
+  EXPECT_FALSE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_error);
+
+  ClearTestSchemes();
+}
+
+// Test that custom nonstandard scheme handling fails when the scheme name is
+// incorrect.
+TEST(SchemeHandlerTest, CustomNonStandardNameNotHandled) {
+  RegisterTestScheme("customnonstd", std::string());
+  g_TestResults.url = "customnonstd2:some%20value";
+  g_TestResults.expected_error_code = ERR_UNKNOWN_URL_SCHEME;
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_FALSE(g_TestResults.got_request);
+  EXPECT_FALSE(g_TestResults.got_read);
+  EXPECT_FALSE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_error);
+
+  ClearTestSchemes();
+}
+
+// Test that custom standard scheme handling fails when the domain name is
+// incorrect.
+TEST(SchemeHandlerTest, CustomStandardDomainNotHandled) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://noexist/run.html";
+  g_TestResults.expected_error_code = ERR_UNKNOWN_URL_SCHEME;
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_FALSE(g_TestResults.got_request);
+  EXPECT_FALSE(g_TestResults.got_read);
+  EXPECT_FALSE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_error);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can return no response.
+TEST(SchemeHandlerTest, CustomStandardNoResponse) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_FALSE(g_TestResults.got_read);
+  EXPECT_FALSE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom nonstandard scheme can return no response.
+TEST(SchemeHandlerTest, CustomNonStandardNoResponse) {
+  RegisterTestScheme("customnonstd", std::string());
+  g_TestResults.url = "customnonstd:some%20value";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_FALSE(g_TestResults.got_read);
+  EXPECT_FALSE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate redirects.
+TEST(SchemeHandlerTest, CustomStandardRedirect) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+  g_TestResults.redirect_url = "customstd://test/redirect.html";
+  g_TestResults.html =
+      "<html><head></head><body><h1>Redirected</h1></body></html>";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_redirect);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom nonstandard scheme can generate redirects.
+TEST(SchemeHandlerTest, CustomNonStandardRedirect) {
+  RegisterTestScheme("customnonstd", std::string());
+  g_TestResults.url = "customnonstd:some%20value";
+  g_TestResults.redirect_url = "customnonstd:some%20other%20value";
+  g_TestResults.html =
+      "<html><head></head><body><h1>Redirected</h1></body></html>";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_redirect);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate same origin XHR requests.
+TEST(SchemeHandlerTest, CustomStandardXHRSameOriginSync) {
+  RegisterTestScheme("customstd", "test");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test/run.html";
+  settings.sub_url = "customstd://test/xhr.html";
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate same origin XHR requests.
+TEST(SchemeHandlerTest, CustomStandardXHRSameOriginAsync) {
+  RegisterTestScheme("customstd", "test");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test/run.html";
+  settings.sub_url = "customstd://test/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that custom nonstandard schemes are treated as unique origins that
+// cannot generate XHR requests.
+TEST(SchemeHandlerTest, CustomNonStandardXHRSameOriginSync) {
+  RegisterTestScheme("customnonstd", std::string());
+
+  XHRTestSettings settings;
+  settings.url = "customnonstd:some%20value";
+  settings.sub_url = "customnonstd:xhr%20value";
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_FALSE(g_TestResults.got_sub_request);
+  EXPECT_FALSE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that custom nonstandard schemes are treated as unique origins that
+// cannot generate XHR requests.
+TEST(SchemeHandlerTest, CustomNonStandardXHRSameOriginAsync) {
+  RegisterTestScheme("customnonstd", std::string());
+
+  XHRTestSettings settings;
+  settings.url = "customnonstd:some%20value";
+  settings.sub_url = "customnonstd:xhr%20value";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_FALSE(g_TestResults.got_sub_request);
+  EXPECT_FALSE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a non fetch enabled custom standard scheme can't generate same
+// origin Fetch requests.
+TEST(SchemeHandlerTest, CustomStandardFetchSameOrigin) {
+  RegisterTestScheme("customstd", "test");
+
+  FetchTestSettings settings;
+  settings.url = "customstd://test/run.html";
+  settings.sub_url = "customstd://test/fetch.html";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_FALSE(g_TestResults.got_sub_request);
+  EXPECT_FALSE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a fetch enabled custom standard scheme can generate same origin
+// Fetch requests.
+TEST(SchemeHandlerTest, FetchCustomStandardFetchSameOrigin) {
+  RegisterTestScheme("customstdfetch", "test");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test/run.html";
+  settings.sub_url = "customstdfetch://test/fetch.html";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that custom nonstandard schemes are treated as unique origins that
+// cannot generate Fetch requests.
+TEST(SchemeHandlerTest, CustomNonStandardFetchSameOrigin) {
+  RegisterTestScheme("customnonstd", std::string());
+
+  FetchTestSettings settings;
+  settings.url = "customnonstd:some%20value";
+  settings.sub_url = "customnonstd:xhr%20value";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_FALSE(g_TestResults.got_sub_request);
+  EXPECT_FALSE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate same origin XSS requests.
+TEST(SchemeHandlerTest, CustomStandardXSSSameOrigin) {
+  RegisterTestScheme("customstd", "test");
+  SetUpXSS("customstd://test/run.html", "customstd://test/iframe.html");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that custom nonstandard schemes are treated as unique origins that
+// cannot generate XSS requests.
+TEST(SchemeHandlerTest, CustomNonStandardXSSSameOrigin) {
+  RegisterTestScheme("customnonstd", std::string());
+  SetUpXSS("customnonstd:some%20value", "customnonstd:xhr%20value");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme cannot generate cross-domain XHR requests
+// by default. Behavior should be the same as with HTTP.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginSync) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme cannot generate cross-domain XHR requests
+// by default. Behavior should be the same as with HTTP.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginAsync) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme cannot generate cross-domain Fetch
+// requests by default. Behavior should be the same as with HTTP.
+TEST(SchemeHandlerTest, CustomStandardFetchDifferentOrigin) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://test2/fetch.html";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme cannot generate cross-domain XSS requests
+// by default.
+TEST(SchemeHandlerTest, CustomStandardXSSDifferentOrigin) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+  SetUpXSS("customstd://test1/run.html", "customstd://test2/iframe.html");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a cross-protocol iframe load succeeds, and that the custom
+// standard scheme cannot generate XSS requests to the HTTP protocol by default.
+TEST(SchemeHandlerTest, CustomStandardXSSDifferentProtocolHttp) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("http", "test2");
+  SetUpXSS("customstd://test1/run.html", "http://test2/iframe.html");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a cross-protocol iframe load succeeds, and that the custom
+// standard scheme cannot generate XSS requests to a non-standard scheme by
+// default.
+TEST(SchemeHandlerTest, CustomStandardXSSDifferentProtocolCustomNonStandard) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customnonstd", std::string());
+  SetUpXSS("customstd://test1/run.html", "customnonstd:some%20value");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a cross-protocol iframe load succeeds, and that the HTTP protocol
+// cannot generate XSS requests to the custom standard scheme by default.
+TEST(SchemeHandlerTest, HttpXSSDifferentProtocolCustomStandard) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("customstd", "test2");
+  SetUpXSS("http://test1/run.html", "customstd://test2/iframe.html");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a cross-protocol iframe load succeeds, and that the HTTP protocol
+// cannot generate XSS requests to the custom non-standard scheme by default.
+TEST(SchemeHandlerTest, HttpXSSDifferentProtocolCustomNonStandard) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("customnonstd", std::string());
+  SetUpXSS("http://test1/run.html", "customnonstd:some%20value");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme cannot generate cross-domain XHR requests by
+// default.
+TEST(SchemeHandlerTest, HttpXHRDifferentOriginSync) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("http", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "http://test1/run.html";
+  settings.sub_url = "http://test2/xhr.html";
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme cannot generate cross-domain XHR requests by
+// default.
+TEST(SchemeHandlerTest, HttpXHRDifferentOriginAsync) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("http", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "http://test1/run.html";
+  settings.sub_url = "http://test2/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme cannot generate cross-domain Fetch requests by
+// default.
+TEST(SchemeHandlerTest, HttpFetchDifferentOriginAsync) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("http", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "http://test1/run.html";
+  settings.sub_url = "http://test2/fetch.html";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme cannot generate cross-domain XSS requests by
+// default.
+TEST(SchemeHandlerTest, HttpXSSDifferentOrigin) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("http", "test2");
+  SetUpXSS("http://test1/run.html", "http://test2/xss.html");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain XHR requests
+// when setting the Access-Control-Allow-Origin header. Should behave the same
+// as HTTP.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeaderSync) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.sub_allow_origin = "customstd://test1";
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain XHR requests
+// when setting the Access-Control-Allow-Origin header. Should behave the same
+// as HTTP.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeaderAsync) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.sub_allow_origin = "customstd://test1";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain Fetch requests
+// when setting the Access-Control-Allow-Origin header. Should behave the same
+// as HTTP.
+TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithHeader) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://test2/fetch.html";
+  settings.sub_allow_origin = "customstdfetch://test1";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain XHR requests
+// when using the cross-origin whitelist.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistSync1) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              "test2", false));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches any domain.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistSync2) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              CefString(), true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches sub-domains.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistSync3) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "a.test2.foo");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://a.test2.foo/xhr.html";
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              "test2.foo", true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain XHR requests
+// when using the cross-origin whitelist.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync1) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              "test2", false));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches any domain.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync2) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              CefString(), true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches sub-domains.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync3) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "a.test2.foo");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://a.test2.foo/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              "test2.foo", true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain Fetch requests
+// when using the cross-origin whitelist.
+TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithWhitelist1) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://test2/fetch.html";
+  SetUpFetch(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
+      "customstdfetch://test1", "customstdfetch", "test2", false));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches any domain.
+TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithWhitelist2) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://test2/fetch.html";
+  SetUpFetch(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
+      "customstdfetch://test1", "customstdfetch", CefString(), true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches sub-domains.
+TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithWhitelist3) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "a.test2.foo");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://a.test2.foo/fetch.html";
+  SetUpFetch(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
+      "customstdfetch://test1", "customstdfetch", "test2.foo", true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme can generate cross-domain XHR requests when setting
+// the Access-Control-Allow-Origin header.
+TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeaderSync) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("http", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "http://test1/run.html";
+  settings.sub_url = "http://test2/xhr.html";
+  settings.sub_allow_origin = "http://test1";
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme can generate cross-domain XHR requests when setting
+// the Access-Control-Allow-Origin header.
+TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeaderAsync) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("http", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "http://test1/run.html";
+  settings.sub_url = "http://test2/xhr.html";
+  settings.sub_allow_origin = "http://test1";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme can generate cross-domain XHR requests when setting
+// the Access-Control-Allow-Origin header.
+TEST(SchemeHandlerTest, HttpFetchDifferentOriginWithHeader) {
+  RegisterTestScheme("http", "test1");
+  RegisterTestScheme("http", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "http://test1/run.html";
+  settings.sub_url = "http://test2/fetch.html";
+  settings.sub_allow_origin = "http://test1";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain XSS requests
+// when using document.domain.
+TEST(SchemeHandlerTest, CustomStandardXSSDifferentOriginWithDomain) {
+  RegisterTestScheme("customstd", "a.test.com");
+  RegisterTestScheme("customstd", "b.test.com");
+  SetUpXSS("customstd://a.test.com/run.html",
+           "customstd://b.test.com/iframe.html", "test.com");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that an HTTP scheme can generate cross-domain XSS requests when using
+// document.domain.
+TEST(SchemeHandlerTest, HttpXSSDifferentOriginWithDomain) {
+  RegisterTestScheme("http", "a.test.com");
+  RegisterTestScheme("http", "b.test.com");
+  SetUpXSS("http://a.test.com/run.html", "http://b.test.com/iframe.html",
+           "test.com");
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme cannot generate cross-domain XHR requests
+// that perform redirects.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginRedirectSync) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.sub_redirect_url = "customstd://test1/xhr.html";
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme cannot generate cross-domain XHR requests
+// that perform redirects.
+TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginRedirectAsync) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.sub_redirect_url = "customstd://test1/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme cannot generate cross-domain Fetch
+// requests that perform redirects.
+TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginRedirect) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://test2/fetch.html";
+  settings.sub_redirect_url = "customstdfetch://test1/fetch.html";
+  SetUpFetch(settings);
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_FALSE(g_TestResults.got_sub_success);
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain XHR requests
+// that perform redirects when using the cross-origin whitelist.
+TEST(SchemeHandlerTest,
+     CustomStandardXHRDifferentOriginRedirectWithWhitelistSync) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.sub_redirect_url = "customstd://test1/xhr.html";
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              "test2", false));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain XHR requests
+// that perform redirects when using the cross-origin whitelist.
+TEST(SchemeHandlerTest,
+     CustomStandardXHRDifferentOriginRedirectWithWhitelistAsync1) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.sub_redirect_url = "customstd://test1/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              "test2", false));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches any domain.
+TEST(SchemeHandlerTest,
+     CustomStandardXHRDifferentOriginRedirectWithWhitelistAsync2) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "test2");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://test2/xhr.html";
+  settings.sub_redirect_url = "customstd://test1/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              CefString(), true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches sub-domains.
+TEST(SchemeHandlerTest,
+     CustomStandardXHRDifferentOriginRedirectWithWhitelistAsync3) {
+  RegisterTestScheme("customstd", "test1");
+  RegisterTestScheme("customstd", "a.test2.foo");
+
+  XHRTestSettings settings;
+  settings.url = "customstd://test1/run.html";
+  settings.sub_url = "customstd://a.test2.foo/xhr.html";
+  settings.sub_redirect_url = "customstd://test1/xhr.html";
+  settings.synchronous = false;
+  SetUpXHR(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd",
+                                              "test2.foo", true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Test that a custom standard scheme can generate cross-domain Fetch requests
+// that perform redirects when using the cross-origin whitelist.
+TEST(SchemeHandlerTest,
+     CustomStandardFetchDifferentOriginRedirectWithWhitelist1) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://test2/fetch.html";
+  settings.sub_redirect_url = "customstdfetch://test1/fetch.html";
+  SetUpFetch(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
+      "customstdfetch://test1", "customstdfetch", "test2", false));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches any domain.
+TEST(SchemeHandlerTest,
+     CustomStandardFetchDifferentOriginRedirectWithWhitelist2) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "test2");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://test2/fetch.html";
+  settings.sub_redirect_url = "customstdfetch://test1/fetch.html";
+  SetUpFetch(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
+      "customstdfetch://test1", "customstdfetch", CefString(), true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Same as above but origin whitelist matches sub-domains.
+TEST(SchemeHandlerTest,
+     CustomStandardFetchDifferentOriginRedirectWithWhitelist3) {
+  RegisterTestScheme("customstdfetch", "test1");
+  RegisterTestScheme("customstdfetch", "a.test2.foo");
+
+  FetchTestSettings settings;
+  settings.url = "customstdfetch://test1/run.html";
+  settings.sub_url = "customstdfetch://a.test2.foo/fetch.html";
+  settings.sub_redirect_url = "customstdfetch://test1/fetch.html";
+  SetUpFetch(settings);
+
+  EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
+      "customstdfetch://test1", "customstdfetch", "test2.foo", true));
+  WaitForUIThread();
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+  EXPECT_TRUE(g_TestResults.got_sub_redirect);
+  EXPECT_TRUE(g_TestResults.got_sub_request);
+  EXPECT_TRUE(g_TestResults.got_sub_read);
+  EXPECT_TRUE(g_TestResults.got_sub_success);
+
+  EXPECT_TRUE(CefClearCrossOriginWhitelist());
+  WaitForUIThread();
+
+  ClearTestSchemes();
+}
+
+// Test per-browser setting of Accept-Language.
+TEST(SchemeHandlerTest, AcceptLanguage) {
+  RegisterTestScheme("customstd", "test");
+  g_TestResults.url = "customstd://test/run.html";
+  g_TestResults.html =
+      "<html><head></head><body><h1>Success!</h1></body></html>";
+
+  // Value that will be set via CefBrowserSettings.accept_language in
+  // PopulateBrowserSettings().
+  g_TestResults.accept_language = "uk";
+
+  CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+
+  EXPECT_TRUE(g_TestResults.got_request);
+  EXPECT_TRUE(g_TestResults.got_read);
+  EXPECT_TRUE(g_TestResults.got_output);
+
+  ClearTestSchemes();
+}
+
+// Entry point for registering custom schemes.
+// Called from client_app_delegates.cc.
+void RegisterSchemeHandlerCustomSchemes(
+    CefRawPtr<CefSchemeRegistrar> registrar,
+    std::vector<CefString>& cookiable_schemes) {
+  // Add a custom standard scheme.
+  registrar->AddCustomScheme(
+      "customstd", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
+  registrar->AddCustomScheme("customstdfetch",
+                             CEF_SCHEME_OPTION_STANDARD |
+                                 CEF_SCHEME_OPTION_CORS_ENABLED |
+                                 CEF_SCHEME_OPTION_FETCH_ENABLED);
+  // Add a custom non-standard scheme.
+  registrar->AddCustomScheme("customnonstd", CEF_SCHEME_OPTION_NONE);
+  registrar->AddCustomScheme("customnonstdfetch",
+                             CEF_SCHEME_OPTION_FETCH_ENABLED);
+}
diff --git a/src/tests/ceftests/scoped_temp_dir_unittest.cc b/src/tests/ceftests/scoped_temp_dir_unittest.cc
new file mode 100644
index 0000000..5728aff
--- /dev/null
+++ b/src/tests/ceftests/scoped_temp_dir_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2011 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include <string>
+
+#include "include/cef_file_util.h"
+#include "include/wrapper/cef_scoped_temp_dir.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+TEST(ScopedTempDir, FullPath) {
+  CefString test_path;
+  CefCreateNewTempDirectory("scoped_temp_dir", test_path);
+
+  // Against an existing dir, it should get destroyed when leaving scope.
+  EXPECT_TRUE(CefDirectoryExists(test_path));
+  {
+    CefScopedTempDir dir;
+    EXPECT_TRUE(dir.Set(test_path));
+    EXPECT_TRUE(dir.IsValid());
+  }
+  EXPECT_FALSE(CefDirectoryExists(test_path));
+
+  {
+    CefScopedTempDir dir;
+    EXPECT_TRUE(dir.Set(test_path));
+    // Now the dir doesn't exist, so ensure that it gets created.
+    EXPECT_TRUE(CefDirectoryExists(test_path));
+    // When we call Take(), it shouldn't get destroyed when leaving scope.
+    CefString path = dir.Take();
+    EXPECT_STREQ(path.ToString().c_str(), test_path.ToString().c_str());
+    EXPECT_FALSE(dir.IsValid());
+  }
+  EXPECT_TRUE(CefDirectoryExists(test_path));
+
+  // Clean up.
+  {
+    CefScopedTempDir dir;
+    EXPECT_TRUE(dir.Set(test_path));
+  }
+  EXPECT_FALSE(CefDirectoryExists(test_path));
+}
+
+TEST(ScopedTempDir, TempDir) {
+  // In this case, just verify that a directory was created and that it's a
+  // child of TempDir.
+  CefString test_path;
+  {
+    CefScopedTempDir dir;
+    EXPECT_TRUE(dir.CreateUniqueTempDir());
+    test_path = dir.GetPath();
+    EXPECT_TRUE(CefDirectoryExists(test_path));
+    CefString tmp_dir;
+    EXPECT_TRUE(CefGetTempDirectory(tmp_dir));
+    EXPECT_TRUE(test_path.ToString().find(tmp_dir.ToString()) !=
+                std::string::npos);
+  }
+  EXPECT_FALSE(CefDirectoryExists(test_path));
+}
+
+TEST(ScopedTempDir, UniqueTempDirUnderPath) {
+  // Create a path which will contain a unique temp path.
+  CefString base_path;
+  ASSERT_TRUE(CefCreateNewTempDirectory("base_dir", base_path));
+
+  CefString test_path;
+  {
+    CefScopedTempDir dir;
+    EXPECT_TRUE(dir.CreateUniqueTempDirUnderPath(base_path));
+    test_path = dir.GetPath();
+    EXPECT_TRUE(CefDirectoryExists(test_path));
+    EXPECT_TRUE(test_path.ToString().find(base_path.ToString()) == 0);
+  }
+  EXPECT_FALSE(CefDirectoryExists(test_path));
+  CefDeleteFile(base_path, true);
+}
+
+TEST(ScopedTempDir, MultipleInvocations) {
+  CefScopedTempDir dir;
+  EXPECT_TRUE(dir.CreateUniqueTempDir());
+  EXPECT_FALSE(dir.CreateUniqueTempDir());
+  EXPECT_TRUE(dir.Delete());
+  EXPECT_TRUE(dir.CreateUniqueTempDir());
+  EXPECT_FALSE(dir.CreateUniqueTempDir());
+  CefScopedTempDir other_dir;
+  EXPECT_TRUE(other_dir.Set(dir.Take()));
+  EXPECT_TRUE(dir.CreateUniqueTempDir());
+  EXPECT_FALSE(dir.CreateUniqueTempDir());
+  EXPECT_FALSE(other_dir.CreateUniqueTempDir());
+}
diff --git a/src/tests/ceftests/server_unittest.cc b/src/tests/ceftests/server_unittest.cc
new file mode 100644
index 0000000..b3f8c2a
--- /dev/null
+++ b/src/tests/ceftests/server_unittest.cc
@@ -0,0 +1,1485 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <list>
+#include <map>
+#include <set>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_ref_counted.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_command_line.h"
+#include "include/cef_server.h"
+#include "include/cef_task.h"
+#include "include/cef_urlrequest.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestServerAddress[] = "127.0.0.1";
+const uint16 kTestServerPort = 8099;
+const int kTestTimeout = 5000;
+
+std::string GetTestServerOrigin(bool is_websocket) {
+  std::stringstream ss;
+  ss << (is_websocket ? "ws://" : "http://") << kTestServerAddress << ":"
+     << kTestServerPort << "/";
+  return ss.str();
+}
+
+// Handles the test server. Used for both HTTP and WebSocket tests.
+class TestServerHandler : public CefServerHandler {
+ public:
+  // HTTP test handler.
+  // The methods of this class are always executed on the server thread.
+  class HttpRequestHandler {
+   public:
+    virtual ~HttpRequestHandler() {}
+    virtual bool HandleRequest(CefRefPtr<CefServer> server,
+                               int connection_id,
+                               const CefString& client_address,
+                               CefRefPtr<CefRequest> request) = 0;
+    virtual bool VerifyResults() = 0;
+    virtual std::string ToString() = 0;
+  };
+
+  // WebSocket test handler.
+  // The methods of this class are always executed on the server thread.
+  class WsRequestHandler {
+   public:
+    virtual ~WsRequestHandler() {}
+    virtual bool HandleRequest(CefRefPtr<CefServer> server,
+                               int connection_id,
+                               const CefString& client_address,
+                               CefRefPtr<CefRequest> request,
+                               CefRefPtr<CefCallback> callback) = 0;
+    virtual bool HandleConnected(CefRefPtr<CefServer> server,
+                                 int connection_id) = 0;
+    virtual bool HandleMessage(CefRefPtr<CefServer> server,
+                               int connection_id,
+                               const void* data,
+                               size_t data_size) = 0;
+    virtual bool VerifyResults() = 0;
+    virtual std::string ToString() = 0;
+  };
+
+  // |start_callback| will be executed on the UI thread after the server is
+  // started.
+  // |destroy_callback| will be executed on the UI thread after this handler
+  // object is destroyed.
+  TestServerHandler(const base::Closure& start_callback,
+                    const base::Closure& destroy_callback)
+      : initialized_(false),
+        start_callback_(start_callback),
+        destroy_callback_(destroy_callback),
+        expected_connection_ct_(0),
+        actual_connection_ct_(0),
+        expected_http_request_ct_(0),
+        actual_http_request_ct_(0),
+        expected_ws_request_ct_(0),
+        actual_ws_request_ct_(0),
+        expected_ws_connected_ct_(0),
+        actual_ws_connected_ct_(0),
+        expected_ws_message_ct_(0),
+        actual_ws_message_ct_(0) {
+    EXPECT_FALSE(destroy_callback_.is_null());
+  }
+
+  virtual ~TestServerHandler() {
+    EXPECT_UI_THREAD();
+
+    if (!http_request_handler_list_.empty()) {
+      HttpRequestHandlerList::const_iterator it =
+          http_request_handler_list_.begin();
+      for (; it != http_request_handler_list_.end(); ++it)
+        delete *it;
+    }
+
+    if (!ws_request_handler_list_.empty()) {
+      WsRequestHandlerList::const_iterator it =
+          ws_request_handler_list_.begin();
+      for (; it != ws_request_handler_list_.end(); ++it)
+        delete *it;
+    }
+
+    destroy_callback_.Run();
+  }
+
+  // Must be called before CreateServer().
+  void SetExpectedConnectionCount(int expected) {
+    EXPECT_FALSE(initialized_);
+    expected_connection_ct_ = expected;
+  }
+
+  // Must be called before CreateServer().
+  void AddHttpRequestHandler(scoped_ptr<HttpRequestHandler> request_handler) {
+    EXPECT_FALSE(initialized_);
+    EXPECT_TRUE(request_handler);
+    http_request_handler_list_.push_back(request_handler.release());
+  }
+
+  // Must be called before CreateServer().
+  void SetExpectedHttpRequestCount(int expected) {
+    EXPECT_FALSE(initialized_);
+    expected_http_request_ct_ = expected;
+  }
+
+  // Must be called before CreateServer().
+  void AddWsRequestHandler(scoped_ptr<WsRequestHandler> request_handler) {
+    EXPECT_FALSE(initialized_);
+    EXPECT_TRUE(request_handler);
+    ws_request_handler_list_.push_back(request_handler.release());
+  }
+
+  // Must be called before CreateServer().
+  void SetExpectedWsRequestCount(int expected) {
+    EXPECT_FALSE(initialized_);
+    expected_ws_request_ct_ = expected;
+  }
+
+  // Must be called before CreateServer().
+  void SetExpectedWsConnectedCount(int expected) {
+    EXPECT_FALSE(initialized_);
+    expected_ws_connected_ct_ = expected;
+  }
+
+  // Must be called before CreateServer().
+  void SetExpectedWsMessageCount(int expected) {
+    EXPECT_FALSE(initialized_);
+    expected_ws_message_ct_ = expected;
+  }
+
+  void CreateServer() {
+    EXPECT_FALSE(initialized_);
+    initialized_ = true;
+    CefServer::CreateServer(kTestServerAddress, kTestServerPort, 10, this);
+  }
+
+  // Results in a call to VerifyResults() and eventual execution of the
+  // |destroy_callback|.
+  void ShutdownServer() {
+    EXPECT_TRUE(server_);
+    if (server_)
+      server_->Shutdown();
+  }
+
+  void OnServerCreated(CefRefPtr<CefServer> server) override {
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(server->IsRunning());
+    EXPECT_FALSE(server->HasConnection());
+
+    EXPECT_FALSE(got_server_created_);
+    got_server_created_.yes();
+
+    EXPECT_FALSE(server_);
+    server_ = server;
+
+    EXPECT_FALSE(server_runner_);
+    server_runner_ = server_->GetTaskRunner();
+    EXPECT_TRUE(server_runner_);
+    EXPECT_TRUE(server_runner_->BelongsToCurrentThread());
+
+    RunStartCallback();
+  }
+
+  void OnServerDestroyed(CefRefPtr<CefServer> server) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_FALSE(server->IsRunning());
+    EXPECT_FALSE(server->HasConnection());
+
+    EXPECT_FALSE(got_server_destroyed_);
+    got_server_destroyed_.yes();
+
+    server_ = nullptr;
+
+    VerifyResults();
+  }
+
+  void OnClientConnected(CefRefPtr<CefServer> server,
+                         int connection_id) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(server->HasConnection());
+    EXPECT_TRUE(server->IsValidConnection(connection_id));
+
+    EXPECT_TRUE(connection_id_set_.find(connection_id) ==
+                connection_id_set_.end());
+    connection_id_set_.insert(connection_id);
+
+    actual_connection_ct_++;
+  }
+
+  void OnClientDisconnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_FALSE(server->IsValidConnection(connection_id));
+
+    ConnectionIdSet::iterator it = connection_id_set_.find(connection_id);
+    EXPECT_TRUE(it != connection_id_set_.end());
+    connection_id_set_.erase(it);
+
+    ConnectionIdSet::iterator it2 = ws_connection_id_set_.find(connection_id);
+    if (it2 != ws_connection_id_set_.end())
+      ws_connection_id_set_.erase(it2);
+
+    if (connection_id_set_.empty()) {
+      EXPECT_TRUE(ws_connection_id_set_.empty());
+      EXPECT_FALSE(server->HasConnection());
+    }
+  }
+
+  void OnHttpRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const CefString& client_address,
+                     CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(VerifyConnection(connection_id));
+    EXPECT_FALSE(client_address.empty());
+    EXPECT_TRUE(VerifyRequest(request, false));
+
+    bool handled = false;
+    HttpRequestHandlerList::const_iterator it =
+        http_request_handler_list_.begin();
+    for (; it != http_request_handler_list_.end(); ++it) {
+      handled =
+          (*it)->HandleRequest(server, connection_id, client_address, request);
+      if (handled)
+        break;
+    }
+    EXPECT_TRUE(handled) << "missing HttpRequestHandler for "
+                         << request->GetURL().ToString();
+
+    actual_http_request_ct_++;
+  }
+
+  void OnWebSocketRequest(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const CefString& client_address,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(VerifyConnection(connection_id));
+    EXPECT_FALSE(client_address.empty());
+    EXPECT_TRUE(VerifyRequest(request, true));
+
+    EXPECT_TRUE(ws_connection_id_set_.find(connection_id) ==
+                ws_connection_id_set_.end());
+    ws_connection_id_set_.insert(connection_id);
+
+    bool handled = false;
+    WsRequestHandlerList::const_iterator it = ws_request_handler_list_.begin();
+    for (; it != ws_request_handler_list_.end(); ++it) {
+      handled = (*it)->HandleRequest(server, connection_id, client_address,
+                                     request, callback);
+      if (handled)
+        break;
+    }
+    EXPECT_TRUE(handled) << "missing WsRequestHandler for "
+                         << request->GetURL().ToString();
+
+    actual_ws_request_ct_++;
+  }
+
+  void OnWebSocketConnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(VerifyConnection(connection_id));
+
+    EXPECT_TRUE(ws_connection_id_set_.find(connection_id) !=
+                ws_connection_id_set_.end());
+
+    bool handled = false;
+    WsRequestHandlerList::const_iterator it = ws_request_handler_list_.begin();
+    for (; it != ws_request_handler_list_.end(); ++it) {
+      handled = (*it)->HandleConnected(server, connection_id);
+      if (handled)
+        break;
+    }
+    EXPECT_TRUE(handled) << "missing WsRequestHandler for " << connection_id;
+
+    actual_ws_connected_ct_++;
+  }
+
+  void OnWebSocketMessage(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const void* data,
+                          size_t data_size) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(VerifyConnection(connection_id));
+    EXPECT_TRUE(data);
+    EXPECT_GT(data_size, 0U);
+
+    EXPECT_TRUE(ws_connection_id_set_.find(connection_id) !=
+                ws_connection_id_set_.end());
+
+    bool handled = false;
+    WsRequestHandlerList::const_iterator it = ws_request_handler_list_.begin();
+    for (; it != ws_request_handler_list_.end(); ++it) {
+      handled = (*it)->HandleMessage(server, connection_id, data, data_size);
+      if (handled)
+        break;
+    }
+    EXPECT_TRUE(handled) << "missing WsRequestHandler for " << connection_id;
+
+    actual_ws_message_ct_++;
+  }
+
+ private:
+  bool RunningOnServerThread() {
+    return server_runner_ && server_runner_->BelongsToCurrentThread();
+  }
+
+  bool VerifyServer(CefRefPtr<CefServer> server) {
+    V_DECLARE();
+    V_EXPECT_TRUE(RunningOnServerThread());
+    V_EXPECT_TRUE(server);
+    V_EXPECT_TRUE(server_);
+    V_EXPECT_TRUE(server->GetAddress().ToString() ==
+                  server_->GetAddress().ToString());
+    V_RETURN();
+  }
+
+  bool VerifyConnection(int connection_id) {
+    return connection_id_set_.find(connection_id) != connection_id_set_.end();
+  }
+
+  bool VerifyRequest(CefRefPtr<CefRequest> request, bool is_websocket) {
+    V_DECLARE();
+
+    V_EXPECT_FALSE(request->GetMethod().empty());
+
+    const std::string& url = request->GetURL();
+    V_EXPECT_FALSE(url.empty());
+    const std::string& address = server_->GetAddress();
+    V_EXPECT_TRUE(url.find((is_websocket ? "ws://" : "http://") + address) == 0)
+        << "url " << url << " address " << address;
+
+    CefRefPtr<CefPostData> post_data = request->GetPostData();
+    if (post_data) {
+      CefPostData::ElementVector elements;
+      post_data->GetElements(elements);
+      V_EXPECT_TRUE(elements.size() == 1);
+      V_EXPECT_TRUE(elements[0]->GetBytesCount() > 0U);
+    }
+
+    V_RETURN();
+  }
+
+  void VerifyResults() {
+    EXPECT_TRUE(RunningOnServerThread());
+
+    EXPECT_TRUE(got_server_created_);
+    EXPECT_TRUE(got_server_destroyed_);
+    EXPECT_TRUE(connection_id_set_.empty());
+    EXPECT_EQ(expected_connection_ct_, actual_connection_ct_);
+
+    // HTTP
+
+    EXPECT_EQ(expected_http_request_ct_, actual_http_request_ct_);
+
+    if (!http_request_handler_list_.empty()) {
+      HttpRequestHandlerList::const_iterator it =
+          http_request_handler_list_.begin();
+      for (; it != http_request_handler_list_.end(); ++it) {
+        EXPECT_TRUE((*it)->VerifyResults())
+            << "HttpRequestHandler for " << (*it)->ToString();
+      }
+    }
+
+    // WebSocket
+
+    EXPECT_EQ(expected_ws_request_ct_, actual_ws_request_ct_);
+    EXPECT_EQ(expected_ws_connected_ct_, actual_ws_connected_ct_);
+    EXPECT_EQ(expected_ws_message_ct_, actual_ws_message_ct_);
+
+    if (!ws_request_handler_list_.empty()) {
+      WsRequestHandlerList::const_iterator it =
+          ws_request_handler_list_.begin();
+      for (; it != ws_request_handler_list_.end(); ++it) {
+        EXPECT_TRUE((*it)->VerifyResults())
+            << "WsRequestHandler for " << (*it)->ToString();
+      }
+    }
+  }
+
+ private:
+  void RunStartCallback() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&TestServerHandler::RunStartCallback, this));
+      return;
+    }
+
+    EXPECT_FALSE(start_callback_.is_null());
+    start_callback_.Run();
+    start_callback_.Reset();
+  }
+
+  CefRefPtr<CefServer> server_;
+  CefRefPtr<CefTaskRunner> server_runner_;
+  bool initialized_;
+
+  // After initialization only accessed on the UI thread.
+  base::Closure start_callback_;
+  base::Closure destroy_callback_;
+
+  // After initialization the below members are only accessed on the server
+  // thread.
+
+  TrackCallback got_server_created_;
+  TrackCallback got_server_destroyed_;
+
+  typedef std::set<int> ConnectionIdSet;
+  ConnectionIdSet connection_id_set_;
+
+  int expected_connection_ct_;
+  int actual_connection_ct_;
+
+  // HTTP
+
+  typedef std::list<HttpRequestHandler*> HttpRequestHandlerList;
+  HttpRequestHandlerList http_request_handler_list_;
+
+  int expected_http_request_ct_;
+  int actual_http_request_ct_;
+
+  // WebSocket
+
+  typedef std::list<WsRequestHandler*> WsRequestHandlerList;
+  WsRequestHandlerList ws_request_handler_list_;
+
+  ConnectionIdSet ws_connection_id_set_;
+
+  int expected_ws_request_ct_;
+  int actual_ws_request_ct_;
+
+  int expected_ws_connected_ct_;
+  int actual_ws_connected_ct_;
+
+  int expected_ws_message_ct_;
+  int actual_ws_message_ct_;
+
+  IMPLEMENT_REFCOUNTING(TestServerHandler);
+  DISALLOW_COPY_AND_ASSIGN(TestServerHandler);
+};
+
+// HTTP TESTS
+
+// Test runner for 1 or more HTTP requests/responses.
+// Works similarly to TestHandler but without the CefClient dependencies.
+class HttpTestRunner : public base::RefCountedThreadSafe<HttpTestRunner> {
+ public:
+  // The methods of this class are always executed on the UI thread.
+  class RequestRunner {
+   public:
+    virtual ~RequestRunner() {}
+
+    // Create the server-side handler for the request.
+    virtual scoped_ptr<TestServerHandler::HttpRequestHandler>
+    CreateHttpRequestHandler() = 0;
+
+    // Run the request and execute |complete_callback| on completion.
+    virtual void RunRequest(const base::Closure& complete_callback) = 0;
+
+    virtual bool VerifyResults() = 0;
+    virtual std::string ToString() = 0;
+  };
+
+  // If |parallel_requests| is true all requests will be run at the same time,
+  // otherwise one request will be run at a time.
+  HttpTestRunner(bool parallel_requests)
+      : parallel_requests_(parallel_requests),
+        initialized_(false),
+        next_request_id_(0) {
+    handler_ = new TestServerHandler(
+        base::Bind(&HttpTestRunner::OnServerStarted, this),
+        base::Bind(&HttpTestRunner::OnServerDestroyed, this));
+  }
+
+  virtual ~HttpTestRunner() {
+    if (destroy_event_)
+      destroy_event_->Signal();
+  }
+
+  void AddRequestRunner(scoped_ptr<RequestRunner> request_runner) {
+    EXPECT_FALSE(initialized_);
+    request_runner_map_.insert(
+        std::make_pair(++next_request_id_, request_runner.release()));
+  }
+
+  // Blocks until the test has completed or timed out.
+  void ExecuteTest() {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI));
+
+    run_event_ = CefWaitableEvent::CreateWaitableEvent(false, false);
+
+    CefPostTask(TID_UI, base::Bind(&HttpTestRunner::RunTest, this));
+
+    // Block until test completion.
+    run_event_->Wait();
+  }
+
+  // Event that will be signaled from the HttpTestRunner destructor.
+  // Used by ReleaseAndWaitForDestructor.
+  void SetDestroyEvent(CefRefPtr<CefWaitableEvent> event) {
+    destroy_event_ = event;
+  }
+
+ private:
+  void RunTest() {
+    EXPECT_UI_THREAD();
+    EXPECT_FALSE(initialized_);
+    initialized_ = true;
+
+    EXPECT_FALSE(request_runner_map_.empty());
+    RequestRunnerMap::const_iterator it = request_runner_map_.begin();
+    for (; it != request_runner_map_.end(); ++it) {
+      handler_->AddHttpRequestHandler(it->second->CreateHttpRequestHandler());
+    }
+
+    handler_->SetExpectedConnectionCount(
+        static_cast<int>(request_runner_map_.size()));
+    handler_->SetExpectedHttpRequestCount(
+        static_cast<int>(request_runner_map_.size()));
+
+    handler_->CreateServer();
+
+    SetTestTimeout(kTestTimeout);
+  }
+
+  void OnServerStarted() {
+    EXPECT_UI_THREAD();
+    if (parallel_requests_) {
+      RunAllRequests();
+    } else {
+      RunNextRequest();
+    }
+  }
+
+  void OnServerDestroyed() {
+    EXPECT_UI_THREAD();
+    EXPECT_FALSE(got_server_destroyed_);
+    got_server_destroyed_.yes();
+
+    // Allow the call stack to unwind.
+    CefPostTask(TID_UI, base::Bind(&HttpTestRunner::DestroyTest, this));
+  }
+
+  // Run all requests in parallel.
+  void RunAllRequests() {
+    RequestRunnerMap::const_iterator it = request_runner_map_.begin();
+    for (; it != request_runner_map_.end(); ++it) {
+      it->second->RunRequest(
+          base::Bind(&HttpTestRunner::OnRequestComplete, this, it->first));
+    }
+  }
+
+  // Run one request at a time.
+  void RunNextRequest() {
+    RequestRunnerMap::const_iterator it = request_runner_map_.begin();
+    it->second->RunRequest(
+        base::Bind(&HttpTestRunner::OnRequestComplete, this, it->first));
+  }
+
+  void OnRequestComplete(int request_id) {
+    EXPECT_UI_THREAD()
+    // Allow the call stack to unwind.
+    CefPostTask(TID_UI, base::Bind(&HttpTestRunner::OnRequestCompleteContinue,
+                                   this, request_id));
+  }
+
+  void OnRequestCompleteContinue(int request_id) {
+    RequestRunnerMap::iterator it = request_runner_map_.find(request_id);
+    EXPECT_TRUE(it != request_runner_map_.end());
+
+    // Verify the request results.
+    EXPECT_TRUE(it->second->VerifyResults())
+        << "request_id " << request_id << " RequestRunner for "
+        << it->second->ToString();
+    delete it->second;
+    request_runner_map_.erase(it);
+
+    if (request_runner_map_.empty()) {
+      got_all_requests_.yes();
+
+      // Will trigger TestServerHandler::HttpRequestHandler verification and a
+      // call to OnServerDestroyed().
+      handler_->ShutdownServer();
+      handler_ = nullptr;
+    } else if (!parallel_requests_) {
+      RunNextRequest();
+    }
+  }
+
+  void DestroyTest() {
+    EXPECT_UI_THREAD();
+
+    EXPECT_TRUE(got_all_requests_);
+    EXPECT_TRUE(got_server_destroyed_);
+    EXPECT_TRUE(request_runner_map_.empty());
+
+    // Cancel the timeout, if any.
+    if (ui_thread_helper_)
+      ui_thread_helper_.reset();
+
+    // Signal test completion.
+    run_event_->Signal();
+  }
+
+  TestHandler::UIThreadHelper* GetUIThreadHelper() {
+    EXPECT_UI_THREAD();
+    if (!ui_thread_helper_)
+      ui_thread_helper_.reset(new TestHandler::UIThreadHelper());
+    return ui_thread_helper_.get();
+  }
+
+  void SetTestTimeout(int timeout_ms) {
+    EXPECT_UI_THREAD();
+    if (CefCommandLine::GetGlobalCommandLine()->HasSwitch(
+            "disable-test-timeout")) {
+      return;
+    }
+
+    // Use a weak reference to |this| via UIThreadHelper so that the
+    // test runner can be destroyed before the timeout expires.
+    GetUIThreadHelper()->PostDelayedTask(
+        base::Bind(&HttpTestRunner::OnTestTimeout, base::Unretained(this),
+                   timeout_ms),
+        timeout_ms);
+  }
+
+  void OnTestTimeout(int timeout_ms) {
+    EXPECT_UI_THREAD();
+    EXPECT_TRUE(false) << "Test timed out after " << timeout_ms << "ms";
+    DestroyTest();
+  }
+
+  bool parallel_requests_;
+  CefRefPtr<CefWaitableEvent> run_event_;
+  CefRefPtr<CefWaitableEvent> destroy_event_;
+  CefRefPtr<TestServerHandler> handler_;
+  bool initialized_;
+
+  // After initialization the below members are only accessed on the UI thread.
+
+  int next_request_id_;
+
+  // Map of request ID to RequestRunner.
+  typedef std::map<int, RequestRunner*> RequestRunnerMap;
+  RequestRunnerMap request_runner_map_;
+
+  TrackCallback got_all_requests_;
+  TrackCallback got_server_destroyed_;
+
+  scoped_ptr<TestHandler::UIThreadHelper> ui_thread_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpTestRunner);
+};
+
+// Structure representing the data that can be sent via
+// CefServer::SendHttp*Response().
+struct HttpServerResponse {
+  enum Type { TYPE_200, TYPE_404, TYPE_500, TYPE_CUSTOM };
+
+  explicit HttpServerResponse(Type response_type)
+      : type(response_type), no_content_length(false) {}
+
+  Type type;
+
+  // Used with 200 and CUSTOM response type.
+  std::string content;
+  std::string content_type;
+
+  // Used with 500 response type.
+  std::string error_message;
+
+  // Used with CUSTOM response type.
+  int response_code;
+  CefServer::HeaderMap extra_headers;
+  bool no_content_length;
+};
+
+void SendHttpServerResponse(CefRefPtr<CefServer> server,
+                            int connection_id,
+                            const HttpServerResponse& response) {
+  EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread());
+  EXPECT_TRUE(server->IsValidConnection(connection_id));
+
+  switch (response.type) {
+    case HttpServerResponse::TYPE_200:
+      EXPECT_TRUE(!response.content_type.empty());
+      server->SendHttp200Response(connection_id, response.content_type,
+                                  response.content.data(),
+                                  response.content.size());
+      break;
+    case HttpServerResponse::TYPE_404:
+      server->SendHttp404Response(connection_id);
+      break;
+    case HttpServerResponse::TYPE_500:
+      server->SendHttp500Response(connection_id, response.error_message);
+      break;
+    case HttpServerResponse::TYPE_CUSTOM:
+      EXPECT_TRUE(!response.content_type.empty());
+      server->SendHttpResponse(
+          connection_id, response.response_code, response.content_type,
+          response.no_content_length
+              ? -1
+              : static_cast<int64>(response.content.size()),
+          response.extra_headers);
+      if (!response.content.empty()) {
+        server->SendRawData(connection_id, response.content.data(),
+                            response.content.size());
+      }
+      if (!response.content.empty() ||
+          (response.content.empty() && response.no_content_length)) {
+        server->CloseConnection(connection_id);
+      }
+      break;
+  }
+
+  // All of the above responses should close the connection.
+  EXPECT_FALSE(server->IsValidConnection(connection_id));
+}
+
+std::string GetHeaderValue(const CefServer::HeaderMap& header_map,
+                           const std::string& header_name) {
+  CefServer::HeaderMap::const_iterator it = header_map.find(header_name);
+  if (it != header_map.end())
+    return it->second;
+  return std::string();
+}
+
+void VerifyHttpServerResponse(const HttpServerResponse& expected_response,
+                              CefRefPtr<CefResponse> response,
+                              const std::string& data) {
+  CefServer::HeaderMap header_map;
+  response->GetHeaderMap(header_map);
+
+  switch (expected_response.type) {
+    case HttpServerResponse::TYPE_200:
+      EXPECT_EQ(200, response->GetStatus());
+      EXPECT_STREQ(expected_response.content_type.c_str(),
+                   GetHeaderValue(header_map, "Content-Type").c_str());
+      EXPECT_STREQ(expected_response.content.c_str(), data.c_str());
+      break;
+    case HttpServerResponse::TYPE_404:
+      EXPECT_EQ(404, response->GetStatus());
+      break;
+    case HttpServerResponse::TYPE_500:
+      EXPECT_EQ(500, response->GetStatus());
+      break;
+    case HttpServerResponse::TYPE_CUSTOM:
+      EXPECT_EQ(expected_response.response_code, response->GetStatus());
+      EXPECT_STREQ(expected_response.content_type.c_str(),
+                   GetHeaderValue(header_map, "Content-Type").c_str());
+      if (expected_response.no_content_length) {
+        EXPECT_TRUE(GetHeaderValue(header_map, "Content-Length").empty());
+      } else {
+        EXPECT_FALSE(GetHeaderValue(header_map, "Content-Length").empty());
+      }
+      EXPECT_STREQ(expected_response.content.c_str(), data.c_str());
+      TestMapEqual(expected_response.extra_headers, header_map, true);
+      break;
+  }
+}
+
+CefRefPtr<CefRequest> CreateTestServerRequest(
+    const std::string& path,
+    const std::string& method,
+    const std::string& data = std::string(),
+    const std::string& content_type = std::string(),
+    const CefRequest::HeaderMap& extra_headers = CefRequest::HeaderMap()) {
+  CefRefPtr<CefRequest> request = CefRequest::Create();
+  request->SetURL(GetTestServerOrigin(false) + path);
+  request->SetMethod(method);
+
+  CefRequest::HeaderMap header_map;
+
+  if (!data.empty()) {
+    CefRefPtr<CefPostData> post_data = CefPostData::Create();
+    CefRefPtr<CefPostDataElement> post_element = CefPostDataElement::Create();
+    post_element->SetToBytes(data.size(), data.data());
+    post_data->AddElement(post_element);
+    request->SetPostData(post_data);
+
+    EXPECT_FALSE(content_type.empty());
+    header_map.insert(std::make_pair("content-type", content_type));
+  }
+
+  if (!extra_headers.empty())
+    header_map.insert(extra_headers.begin(), extra_headers.end());
+  request->SetHeaderMap(header_map);
+
+  return request;
+}
+
+// RequestHandler that returns a static response for 1 or more requests.
+class StaticHttpServerRequestHandler
+    : public TestServerHandler::HttpRequestHandler {
+ public:
+  StaticHttpServerRequestHandler(CefRefPtr<CefRequest> expected_request,
+                                 int expected_request_ct,
+                                 const HttpServerResponse& response)
+      : expected_request_(expected_request),
+        expected_request_ct_(expected_request_ct),
+        actual_request_ct_(0),
+        response_(response) {}
+
+  bool HandleRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const CefString& client_address,
+                     CefRefPtr<CefRequest> request) override {
+    if (request->GetURL() == expected_request_->GetURL() &&
+        request->GetMethod() == expected_request_->GetMethod()) {
+      TestRequestEqual(expected_request_, request, true);
+      actual_request_ct_++;
+
+      SendHttpServerResponse(server, connection_id, response_);
+      return true;
+    }
+
+    return false;
+  }
+
+  bool VerifyResults() override {
+    EXPECT_EQ(expected_request_ct_, actual_request_ct_);
+    return expected_request_ct_ == actual_request_ct_;
+  }
+
+  std::string ToString() override { return expected_request_->GetURL(); }
+
+ private:
+  CefRefPtr<CefRequest> expected_request_;
+  int expected_request_ct_;
+  int actual_request_ct_;
+  HttpServerResponse response_;
+
+  DISALLOW_COPY_AND_ASSIGN(StaticHttpServerRequestHandler);
+};
+
+// URLRequestClient that runs a single request and executes a callback with the
+// response.
+class StaticHttpURLRequestClient : public CefURLRequestClient {
+ public:
+  typedef base::Callback<void(cef_errorcode_t /* error */,
+                              CefRefPtr<CefResponse> /* response */,
+                              const std::string& /* data */)>
+      ResponseCallback;
+
+  // |response_callback| will be executed on the UI thread when the response
+  // is complete.
+  StaticHttpURLRequestClient(CefRefPtr<CefRequest> request,
+                             const ResponseCallback& response_callback)
+      : request_(request), response_callback_(response_callback) {
+    EXPECT_TRUE(request_);
+    EXPECT_FALSE(response_callback_.is_null());
+  }
+
+  void RunRequest() {
+    EXPECT_UI_THREAD();
+    CefURLRequest::Create(request_, this, nullptr);
+  }
+
+  void OnRequestComplete(CefRefPtr<CefURLRequest> request) override {
+    EXPECT_FALSE(response_callback_.is_null());
+    response_callback_.Run(request->GetRequestError(), request->GetResponse(),
+                           data_);
+    response_callback_.Reset();
+  }
+
+  void OnUploadProgress(CefRefPtr<CefURLRequest> request,
+                        int64 current,
+                        int64 total) override {}
+
+  void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
+                          int64 current,
+                          int64 total) override {}
+
+  void OnDownloadData(CefRefPtr<CefURLRequest> request,
+                      const void* data,
+                      size_t data_length) override {
+    data_.append(static_cast<const char*>(data), data_length);
+  }
+
+  bool GetAuthCredentials(bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) override {
+    return false;
+  }
+
+ private:
+  CefRefPtr<CefRequest> request_;
+  ResponseCallback response_callback_;
+  std::string data_;
+
+  IMPLEMENT_REFCOUNTING(StaticHttpURLRequestClient);
+  DISALLOW_COPY_AND_ASSIGN(StaticHttpURLRequestClient);
+};
+
+// RequestRunner that will manage a single static HTTP request/response.
+class StaticHttpRequestRunner : public HttpTestRunner::RequestRunner {
+ public:
+  StaticHttpRequestRunner(CefRefPtr<CefRequest> request,
+                          const HttpServerResponse& response)
+      : request_(request), response_(response) {}
+
+  static scoped_ptr<HttpTestRunner::RequestRunner> Create200(
+      const std::string& path,
+      bool with_content = true) {
+    CefRefPtr<CefRequest> request = CreateTestServerRequest(path, "GET");
+    HttpServerResponse response(HttpServerResponse::TYPE_200);
+    response.content_type = "text/html";
+    if (with_content)
+      response.content = "<html>200 response content</html>";
+    return make_scoped_ptr<HttpTestRunner::RequestRunner>(
+        new StaticHttpRequestRunner(request, response));
+  }
+
+  static scoped_ptr<HttpTestRunner::RequestRunner> Create404(
+      const std::string& path) {
+    CefRefPtr<CefRequest> request = CreateTestServerRequest(path, "GET");
+    HttpServerResponse response(HttpServerResponse::TYPE_404);
+    return make_scoped_ptr<HttpTestRunner::RequestRunner>(
+        new StaticHttpRequestRunner(request, response));
+  }
+
+  static scoped_ptr<HttpTestRunner::RequestRunner> Create500(
+      const std::string& path) {
+    CefRefPtr<CefRequest> request = CreateTestServerRequest(path, "GET");
+    // Don't retry the request.
+    request->SetFlags(UR_FLAG_NO_RETRY_ON_5XX);
+    HttpServerResponse response(HttpServerResponse::TYPE_500);
+    response.error_message = "Something went wrong!";
+    return make_scoped_ptr<HttpTestRunner::RequestRunner>(
+        new StaticHttpRequestRunner(request, response));
+  }
+
+  static scoped_ptr<HttpTestRunner::RequestRunner> CreateCustom(
+      const std::string& path,
+      bool with_content = true,
+      bool with_content_length = true) {
+    CefRequest::HeaderMap request_headers;
+    request_headers.insert(std::make_pair("x-request-custom1", "My Value A"));
+    request_headers.insert(std::make_pair("x-request-custom2", "My Value B"));
+    CefRefPtr<CefRequest> request = CreateTestServerRequest(
+        path, "POST", "foo=bar&choo=too", "application/x-www-form-urlencoded",
+        request_headers);
+    request->SetReferrer("http://tests/referer.html", REFERRER_POLICY_DEFAULT);
+
+    HttpServerResponse response(HttpServerResponse::TYPE_CUSTOM);
+    response.response_code = 202;
+    if (with_content)
+      response.content = "BlahBlahBlah";
+    if (!with_content_length)
+      response.no_content_length = true;
+    response.content_type = "application/x-blah-blah";
+    response.extra_headers.insert(
+        std::make_pair("x-response-custom1", "My Value 1"));
+    response.extra_headers.insert(
+        std::make_pair("x-response-custom2", "My Value 2"));
+
+    return make_scoped_ptr<HttpTestRunner::RequestRunner>(
+        new StaticHttpRequestRunner(request, response));
+  }
+
+  scoped_ptr<TestServerHandler::HttpRequestHandler> CreateHttpRequestHandler()
+      override {
+    EXPECT_FALSE(got_create_handler_);
+    got_create_handler_.yes();
+    return make_scoped_ptr<TestServerHandler::HttpRequestHandler>(
+        new StaticHttpServerRequestHandler(request_, 1, response_));
+  }
+
+  void RunRequest(const base::Closure& complete_callback) override {
+    EXPECT_UI_THREAD();
+
+    EXPECT_FALSE(got_run_request_);
+    got_run_request_.yes();
+
+    complete_callback_ = complete_callback;
+
+    request_client_ = new StaticHttpURLRequestClient(
+        request_, base::Bind(&StaticHttpRequestRunner::OnResponseComplete,
+                             base::Unretained(this)));
+    request_client_->RunRequest();
+  }
+
+  bool VerifyResults() override {
+    V_DECLARE();
+    V_EXPECT_TRUE(got_create_handler_);
+    V_EXPECT_TRUE(got_run_request_);
+    V_EXPECT_TRUE(got_response_complete_);
+    V_RETURN();
+  }
+
+  std::string ToString() override { return request_->GetURL(); }
+
+ private:
+  void OnResponseComplete(cef_errorcode_t error,
+                          CefRefPtr<CefResponse> response,
+                          const std::string& data) {
+    EXPECT_UI_THREAD();
+
+    EXPECT_FALSE(got_response_complete_);
+    got_response_complete_.yes();
+
+    EXPECT_EQ(error, ERR_NONE)
+        << "OnResponseComplete for " << request_->GetURL().ToString();
+    if (error == ERR_NONE)
+      VerifyHttpServerResponse(response_, response, data);
+
+    complete_callback_.Run();
+    complete_callback_.Reset();
+  }
+
+  CefRefPtr<CefRequest> request_;
+  HttpServerResponse response_;
+
+  CefRefPtr<StaticHttpURLRequestClient> request_client_;
+  base::Closure complete_callback_;
+
+  TrackCallback got_run_request_;
+  TrackCallback got_create_handler_;
+  TrackCallback got_response_complete_;
+
+  DISALLOW_COPY_AND_ASSIGN(StaticHttpRequestRunner);
+};
+
+}  // namespace
+
+// Verify handling of a single HTTP 200 request.
+TEST(ServerTest, HttpSingle200) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of a single HTTP 200 request with no content.
+TEST(ServerTest, HttpSingle200NoContent) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(
+      StaticHttpRequestRunner::Create200("200.html", false));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of a single HTTP 404 request.
+TEST(ServerTest, HttpSingle404) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create404("404.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of a single HTTP 500 request.
+TEST(ServerTest, HttpSingle500) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create500("500.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of a single HTTP custom request.
+TEST(ServerTest, HttpSingleCustom) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(StaticHttpRequestRunner::CreateCustom("202.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of a single HTTP custom request with no content.
+TEST(ServerTest, HttpSingleCustomNoContent) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(
+      StaticHttpRequestRunner::CreateCustom("202.html", false));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of a single HTTP custom request with no Content-Length
+// header.
+TEST(ServerTest, HttpSingleCustomNoContentLength) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(
+      StaticHttpRequestRunner::CreateCustom("202.html", true, false));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of a single HTTP custom request with no content and no
+// Content-Length header.
+TEST(ServerTest, HttpSingleCustomNoContentAndNoLength) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(
+      StaticHttpRequestRunner::CreateCustom("202.html", false, false));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of multiple HTTP requests in parallel.
+TEST(ServerTest, HttpMultipleParallel200) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(true);
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200a.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200b.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200c.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of multiple HTTP requests in serial.
+TEST(ServerTest, HttpMultipleSerial200) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200a.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200b.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200c.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of multiple HTTP requests in parallel.
+TEST(ServerTest, HttpMultipleParallelMixed) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(true);
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create404("404.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create500("500.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::CreateCustom("202.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+// Verify handling of multiple HTTP requests in serial.
+TEST(ServerTest, HttpMultipleSerialMixed) {
+  CefRefPtr<HttpTestRunner> runner = new HttpTestRunner(false);
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create200("200.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create404("404.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::Create500("500.html"));
+  runner->AddRequestRunner(StaticHttpRequestRunner::CreateCustom("202.html"));
+  runner->ExecuteTest();
+  ReleaseAndWaitForDestructor(runner);
+}
+
+namespace {
+
+// WEBSOCKET TESTS
+
+const char kWebSocketUrl[] = "http://tests-display/websocket.html";
+const char kDoneMsgPrefix[] = "done:";
+
+class WebSocketTestHandler : public RoutingTestHandler {
+ public:
+  WebSocketTestHandler() {}
+
+  void RunTest() override {
+    handler_ = new TestServerHandler(
+        base::Bind(&WebSocketTestHandler::OnServerStarted, this),
+        base::Bind(&WebSocketTestHandler::OnServerDestroyed, this));
+    OnHandlerCreated(handler_);
+
+    handler_->CreateServer();
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    const std::string& request_str = request.ToString();
+    if (request_str.find(kDoneMsgPrefix) == 0) {
+      EXPECT_FALSE(got_done_message_);
+      got_done_message_.yes();
+      OnDoneMessage(request_str.substr(strlen(kDoneMsgPrefix)));
+      DestroyTestIfDone();
+      return true;
+    }
+    return false;
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_server_started_);
+    EXPECT_TRUE(got_done_message_);
+    EXPECT_TRUE(got_server_destroyed_);
+
+    TestHandler::DestroyTest();
+  }
+
+ protected:
+  // Returns the HTML/JS for the client.
+  virtual std::string GetClientHtml() = 0;
+
+  // Called after the server handler is created to set test expectations.
+  virtual void OnHandlerCreated(CefRefPtr<TestServerHandler> handler) = 0;
+
+  // Returns the JS to execute when the test is done.
+  std::string GetDoneJS(const std::string& result) {
+    return "window.testQuery({request:'" + std::string(kDoneMsgPrefix) +
+           "' + " + result + "});";
+  }
+
+  // Called with the result from the done message.
+  virtual void OnDoneMessage(const std::string& result) = 0;
+
+  void ShutdownServer() {
+    EXPECT_TRUE(handler_);
+    handler_->ShutdownServer();
+    handler_ = nullptr;
+  }
+
+ private:
+  void OnServerStarted() {
+    EXPECT_UI_THREAD();
+    EXPECT_FALSE(got_server_started_);
+    got_server_started_.yes();
+
+    // Add the WebSocket client code.
+    AddResource(kWebSocketUrl, GetClientHtml(), "text/html");
+
+    // Create the browser.
+    CreateBrowser(kWebSocketUrl);
+  }
+
+  void OnServerDestroyed() {
+    EXPECT_UI_THREAD();
+    EXPECT_FALSE(got_server_destroyed_);
+    got_server_destroyed_.yes();
+    DestroyTestIfDone();
+  }
+
+  void DestroyTestIfDone() {
+    if (got_server_destroyed_ && got_done_message_) {
+      // Allow the call stack to unwind.
+      CefPostTask(TID_UI, base::Bind(&WebSocketTestHandler::DestroyTest, this));
+    }
+  }
+
+  CefRefPtr<TestServerHandler> handler_;
+
+  TrackCallback got_server_started_;
+  TrackCallback got_done_message_;
+  TrackCallback got_server_destroyed_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketTestHandler);
+};
+
+// WebSocket request handler that echoes each message sent.
+class EchoWebSocketRequestHandler : public TestServerHandler::WsRequestHandler {
+ public:
+  explicit EchoWebSocketRequestHandler(int expected_message_ct)
+      : expected_message_ct_(expected_message_ct), actual_message_ct_(0) {}
+
+  std::string GetWebSocketUrl() { return GetTestServerOrigin(true) + "echo"; }
+
+  bool HandleRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const CefString& client_address,
+                     CefRefPtr<CefRequest> request,
+                     CefRefPtr<CefCallback> callback) override {
+    EXPECT_STREQ(GetWebSocketUrl().c_str(),
+                 request->GetURL().ToString().c_str());
+
+    callback->Continue();
+    return true;
+  }
+
+  bool HandleConnected(CefRefPtr<CefServer> server,
+                       int connection_id) override {
+    return true;
+  }
+
+  bool HandleMessage(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const void* data,
+                     size_t data_size) override {
+    actual_message_ct_++;
+
+    // Echo the message back to the sender.
+    server->SendWebSocketMessage(connection_id, data, data_size);
+
+    return true;
+  }
+
+  bool VerifyResults() override {
+    EXPECT_EQ(expected_message_ct_, actual_message_ct_);
+    return expected_message_ct_ == actual_message_ct_;
+  }
+
+  std::string ToString() override { return "EchoRequestHandler"; }
+
+ private:
+  int expected_message_ct_;
+  int actual_message_ct_;
+
+  DISALLOW_COPY_AND_ASSIGN(EchoWebSocketRequestHandler);
+};
+
+class EchoWebSocketTestHandler : public WebSocketTestHandler {
+ public:
+  // Create |connection_ct| connections and send |message_ct| messages to each
+  // connection. If |in_parallel| is true the connections will be created in
+  // parallel.
+  EchoWebSocketTestHandler(int connection_ct, int message_ct, bool in_parallel)
+      : connection_ct_(connection_ct),
+        message_ct_(message_ct),
+        in_parallel_(in_parallel) {}
+
+  std::string GetClientHtml() override {
+    std::stringstream ss;
+    ss << connection_ct_;
+    std::string cct_str = ss.str();
+    ss.str("");
+    ss << message_ct_;
+    std::string mct_str = ss.str();
+
+    // clang-format off
+    return
+        "<html><body><script>\n"
+
+        // Input variables.
+        "var url = '" + ws_url_ +"';\n"
+        "var expected_connection_ct = " + cct_str + ";\n"
+        "var expected_message_ct = " + mct_str + ";\n"
+        "var in_parallel = " + (in_parallel_ ? "true" : "false") + ";\n"
+        "var complete_callback = function() { " +
+            GetDoneJS("complete_message_ct") + " }\n"
+
+        // Result variables.
+        "var complete_connection_ct = 0;\n"
+        "var complete_message_ct = 0;\n"
+
+        // Send the next message on the connection asynchronously, or close the
+        // connection if all messages have been sent.
+        "function sendNextMessage(ws, connection_id, message_id) {\n"
+        "  if (message_id < expected_message_ct) {\n"
+        "    setTimeout(function() {\n"
+        "      ws.send('message:' + connection_id + ':' + message_id);\n"
+        "    }, 1);\n"
+        "  } else {\n"
+        "    ws.close();\n"
+        "  }\n"
+        "}\n"
+
+        // Handle a received message.
+        "function onMessage(ws, connection_id, data) {\n"
+        "  var parts = data.split(':');\n"
+        "  if (parts.length == 3 && parts[0] == 'message') {\n"
+        "    var cid = parseInt(parts[1]);\n"
+        "    var mid = parseInt(parts[2]);\n"
+        "    if (cid == connection_id) {\n"
+        "      complete_message_ct++;\n"
+        "      sendNextMessage(ws, connection_id, mid + 1);\n"
+        "    } else {\n"
+        "      console.log('Connection id mismatch; expected ' +\n"
+        "                  connection_id + ', actual ' + cid);\n"
+        "    }\n"
+        "  } else {\n"
+        "    console.log('Unexpected message format: ' + data);\n"
+        "  }\n"
+        "}\n"
+
+        // Handle socket closed. If all messages have been sent on all
+        // connections then complete the test.
+        "function onClose(ws) {\n"
+        "  if (++complete_connection_ct == expected_connection_ct) {\n"
+        "    complete_callback();\n"
+        "  } else if (!in_parallel) {\n"
+        "    startConnection(complete_connection_ct);\n"
+        "  }\n"
+        "}\n"
+
+        // Start a new WebSocket connection.
+        "function startConnection(connection_id) {\n"
+        "  var ws = new WebSocket(url);\n"
+        "  ws.onopen = function() {\n"
+        "    sendNextMessage(ws, connection_id, 0);\n"
+        "  };\n"
+        "  ws.onmessage = function(event) {\n"
+        "    onMessage(ws, connection_id, event.data);\n"
+        "  };\n"
+        "  ws.onclose = function() { onClose(ws); };\n"
+        "}\n"
+
+        // JS entry point.
+        "if (in_parallel) {\n"
+        "  for (var i = 0; i < expected_connection_ct; ++i) {\n"
+        "    startConnection(i);\n"
+        "  }\n"
+        "} else {\n"
+        "  startConnection(0);\n"
+        "}\n"
+
+        "</script>WebSocket Test</body></html>";
+    // clang-format on
+  }
+
+  void OnHandlerCreated(CefRefPtr<TestServerHandler> handler) override {
+    handler->SetExpectedConnectionCount(connection_ct_);
+    handler->SetExpectedWsRequestCount(connection_ct_);
+    handler->SetExpectedWsConnectedCount(connection_ct_);
+    handler->SetExpectedWsMessageCount(connection_ct_ * message_ct_);
+
+    EchoWebSocketRequestHandler* echo_handler =
+        new EchoWebSocketRequestHandler(connection_ct_ * message_ct_);
+    ws_url_ = echo_handler->GetWebSocketUrl();
+    handler->AddWsRequestHandler(
+        make_scoped_ptr<TestServerHandler::WsRequestHandler>(echo_handler));
+  }
+
+  void OnDoneMessage(const std::string& result) override {
+    const int complete_message_ct = atoi(result.c_str());
+    EXPECT_EQ(connection_ct_ * message_ct_, complete_message_ct);
+    ShutdownServer();
+  }
+
+ private:
+  int connection_ct_;
+  int message_ct_;
+  bool in_parallel_;
+  std::string ws_url_;
+
+  IMPLEMENT_REFCOUNTING(EchoWebSocketTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(EchoWebSocketTestHandler);
+};
+
+}  // namespace
+
+// Test handling of a single connection with a single message.
+TEST(ServerTest, WebSocketSingleConnectionSingleMessage) {
+  CefRefPtr<EchoWebSocketTestHandler> handler =
+      new EchoWebSocketTestHandler(1, 1, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test handling of a single connection with multiple messages.
+TEST(ServerTest, WebSocketSingleConnectionMultipleMessages) {
+  CefRefPtr<EchoWebSocketTestHandler> handler =
+      new EchoWebSocketTestHandler(1, 5, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test handling of multiple connections and multiple messages in parallel.
+TEST(ServerTest, WebSocketMultipleConnectionsMultipleMessagesInParallel) {
+  CefRefPtr<EchoWebSocketTestHandler> handler =
+      new EchoWebSocketTestHandler(4, 6, true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test handling of multiple connections and multiple messages in serial.
+TEST(ServerTest, WebSocketMultipleConnectionsMultipleMessagesInSerial) {
+  CefRefPtr<EchoWebSocketTestHandler> handler =
+      new EchoWebSocketTestHandler(4, 6, false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/stream_resource_handler_unittest.cc b/src/tests/ceftests/stream_resource_handler_unittest.cc
new file mode 100644
index 0000000..462f418
--- /dev/null
+++ b/src/tests/ceftests/stream_resource_handler_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_stream.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/ceftests/routing_test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestUrl[] = "http://tests-srh/test.html";
+const size_t kReadBlockSize = 1024U;  // 1k.
+
+// The usual network buffer size is about 32k. Choose a value that's larger.
+const size_t kReadDesiredSize = 100U * 1024U;  // 100k
+
+class ReadHandler : public CefReadHandler {
+ public:
+  explicit ReadHandler(bool may_block)
+      : may_block_(may_block), offset_(0), expected_result_(0) {}
+
+  void CreateContent() {
+    // To verify that the data transers successfully we're going to make a big
+    // math problem.
+    content_.reserve(kReadDesiredSize + 50U);
+    content_ = "<html><body><script>var myratherlongvariablename=0;";
+
+    while (content_.size() < kReadDesiredSize) {
+      content_ += "myratherlongvariablename=myratherlongvariablename+1;";
+      expected_result_++;
+    }
+
+    content_ +=
+        "window.testQuery({request:myratherlongvariablename+''});"
+        "</script></body></html>";
+  }
+
+  int GetExpectedResult() const { return expected_result_; }
+
+  size_t Read(void* ptr, size_t size, size_t n) override {
+    EXPECT_EQ(1U, size);
+
+    // Read the minimum of requested size, remaining size or kReadBlockSize.
+    const size_t read_bytes =
+        std::min(std::min(size * n, content_.size() - offset_), kReadBlockSize);
+    if (read_bytes > 0) {
+      memcpy(ptr, content_.c_str() + offset_, read_bytes);
+      offset_ += read_bytes;
+    }
+
+    return read_bytes;
+  }
+
+  int Seek(int64 offset, int whence) override {
+    EXPECT_TRUE(false);  // Not reached.
+    return 0;
+  }
+
+  int64 Tell() override {
+    EXPECT_TRUE(false);  // Not reached.
+    return 0;
+  }
+
+  int Eof() override {
+    EXPECT_TRUE(false);  // Not reached.
+    return 0;
+  }
+
+  bool MayBlock() override { return may_block_; }
+
+ private:
+  const bool may_block_;
+  std::string content_;
+  size_t offset_;
+  int expected_result_;
+
+  IMPLEMENT_REFCOUNTING(ReadHandler);
+};
+
+class ReadTestHandler : public RoutingTestHandler {
+ public:
+  explicit ReadTestHandler(bool may_block)
+      : may_block_(may_block), expected_result_(0) {}
+
+  void RunTest() override {
+    // Create the browser.
+    CreateBrowser(kTestUrl);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override {
+    got_resource_handler_.yes();
+
+    const std::string& url = request->GetURL();
+    EXPECT_STREQ(kTestUrl, url.c_str());
+
+    CefRefPtr<ReadHandler> handler = new ReadHandler(may_block_);
+    handler->CreateContent();
+    expected_result_ = handler->GetExpectedResult();
+
+    CefRefPtr<CefStreamReader> stream =
+        CefStreamReader::CreateForHandler(handler.get());
+    return new CefStreamResourceHandler("text/html", stream);
+  }
+
+  bool OnQuery(CefRefPtr<CefBrowser> browser,
+               CefRefPtr<CefFrame> frame,
+               int64 query_id,
+               const CefString& request,
+               bool persistent,
+               CefRefPtr<Callback> callback) override {
+    got_on_query_.yes();
+
+    const int actual_result = atoi(request.ToString().c_str());
+    EXPECT_EQ(expected_result_, actual_result);
+
+    DestroyTestIfDone();
+
+    return true;
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (!isLoading) {
+      got_on_loading_state_change_done_.yes();
+      DestroyTestIfDone();
+    }
+  }
+
+ private:
+  void DestroyTestIfDone() {
+    if (got_on_query_ && got_on_loading_state_change_done_)
+      DestroyTest();
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_resource_handler_);
+    EXPECT_TRUE(got_on_query_);
+    EXPECT_TRUE(got_on_loading_state_change_done_);
+    RoutingTestHandler::DestroyTest();
+  }
+
+  const bool may_block_;
+
+  int expected_result_;
+  TrackCallback got_resource_handler_;
+  TrackCallback got_on_query_;
+  TrackCallback got_on_loading_state_change_done_;
+
+  IMPLEMENT_REFCOUNTING(ReadTestHandler);
+};
+
+}  // namespace
+
+TEST(StreamResourceHandlerTest, ReadWillBlock) {
+  CefRefPtr<ReadTestHandler> handler = new ReadTestHandler(true);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+TEST(StreamResourceHandlerTest, ReadWontBlock) {
+  CefRefPtr<ReadTestHandler> handler = new ReadTestHandler(false);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
diff --git a/src/tests/ceftests/stream_unittest.cc b/src/tests/ceftests/stream_unittest.cc
new file mode 100644
index 0000000..89c7df9
--- /dev/null
+++ b/src/tests/ceftests/stream_unittest.cc
@@ -0,0 +1,367 @@
+// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+
+#include "include/cef_stream.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+static void VerifyStreamReadBehavior(CefRefPtr<CefStreamReader> stream,
+                                     const std::string& contents) {
+  int contentSize = static_cast<int>(contents.size());
+  const char* contentStr = contents.c_str();
+
+  // Move to the beginning of the stream
+  ASSERT_EQ(0, stream->Seek(0, SEEK_SET));
+  ASSERT_EQ(0, stream->Tell());
+
+  // Move to the end of the stream
+  ASSERT_EQ(0, stream->Seek(0, SEEK_END));
+  ASSERT_EQ(contentSize, stream->Tell());
+
+  // Move to the beginning of the stream
+  ASSERT_EQ(0, stream->Seek(-contentSize, SEEK_CUR));
+  ASSERT_EQ(0, stream->Tell());
+
+  // Read 10 characters at a time and verify the result
+  char buff[10];
+  int res, read, offset = 0;
+  do {
+    read = std::min(static_cast<int>(sizeof(buff)), contentSize - offset);
+    res = static_cast<int>(stream->Read(buff, 1, read));
+    ASSERT_EQ(read, res);
+    ASSERT_TRUE(!memcmp(contentStr + offset, buff, res));
+    offset += res;
+  } while (offset < contentSize);
+
+  // Read past the end of the file
+  stream->Read(buff, 1, 1);
+  ASSERT_TRUE(stream->Eof());
+}
+
+static void VerifyStreamWriteBehavior(CefRefPtr<CefStreamWriter> stream,
+                                      const std::string& contents) {
+  int contentSize = static_cast<int>(contents.size());
+  const char* contentStr = contents.c_str();
+
+  // Write 10 characters at a time and verify the result
+  int res, write, offset = 0;
+  do {
+    write = std::min(10, contentSize - offset);
+    res = static_cast<int>(stream->Write(contentStr + offset, 1, write));
+    ASSERT_EQ(write, res);
+    offset += res;
+    ASSERT_EQ(offset, stream->Tell());
+  } while (offset < contentSize);
+
+  // Move to the beginning of the stream
+  ASSERT_EQ(0, stream->Seek(-contentSize, SEEK_CUR));
+  ASSERT_EQ(0, stream->Tell());
+
+  // Move to the end of the stream
+  ASSERT_EQ(0, stream->Seek(0, SEEK_END));
+  ASSERT_EQ(contentSize, stream->Tell());
+
+  // Move to the beginning of the stream
+  ASSERT_EQ(0, stream->Seek(0, SEEK_SET));
+  ASSERT_EQ(0, stream->Tell());
+}
+
+TEST(StreamTest, ReadFile) {
+  const char* fileName = "StreamTest.VerifyReadFile.txt";
+  CefString fileNameStr = "StreamTest.VerifyReadFile.txt";
+  std::string contents = "This is my test\ncontents for the file";
+
+  // Create the file
+  FILE* f = nullptr;
+#ifdef _WIN32
+  fopen_s(&f, fileName, "wb");
+#else
+  f = fopen(fileName, "wb");
+#endif
+  ASSERT_TRUE(f != nullptr);
+  ASSERT_EQ((size_t)1, fwrite(contents.c_str(), contents.size(), 1, f));
+  fclose(f);
+
+  // Test the stream
+  CefRefPtr<CefStreamReader> stream(
+      CefStreamReader::CreateForFile(fileNameStr));
+  ASSERT_TRUE(stream.get() != nullptr);
+  ASSERT_TRUE(stream->MayBlock());
+  VerifyStreamReadBehavior(stream, contents);
+
+  // Release the file pointer
+  stream = nullptr;
+
+// Delete the file
+#ifdef _WIN32
+  ASSERT_EQ(0, _unlink(fileName));
+#else
+  ASSERT_EQ(0, unlink(fileName));
+#endif
+}
+
+TEST(StreamTest, ReadData) {
+  std::string contents = "This is my test\ncontents for the file";
+
+  // Test the stream
+  CefRefPtr<CefStreamReader> stream(CefStreamReader::CreateForData(
+      static_cast<void*>(const_cast<char*>(contents.c_str())),
+      contents.size()));
+  ASSERT_TRUE(stream.get() != nullptr);
+  ASSERT_FALSE(stream->MayBlock());
+  VerifyStreamReadBehavior(stream, contents);
+}
+
+TEST(StreamTest, WriteFile) {
+  const char* fileName = "StreamTest.VerifyWriteFile.txt";
+  CefString fileNameStr = "StreamTest.VerifyWriteFile.txt";
+  std::string contents = "This is my test\ncontents for the file";
+
+  // Test the stream
+  CefRefPtr<CefStreamWriter> stream(
+      CefStreamWriter::CreateForFile(fileNameStr));
+  ASSERT_TRUE(stream.get() != nullptr);
+  ASSERT_TRUE(stream->MayBlock());
+  VerifyStreamWriteBehavior(stream, contents);
+
+  // Release the file pointer
+  stream = nullptr;
+
+  // Read the file that was written
+  FILE* f = nullptr;
+  char* buff = new char[contents.size()];
+#ifdef _WIN32
+  fopen_s(&f, fileName, "rb");
+#else
+  f = fopen(fileName, "rb");
+#endif
+  ASSERT_TRUE(f != nullptr);
+  ASSERT_EQ((size_t)1, fread(buff, contents.size(), 1, f));
+
+  // Read past the end of the file
+  fgetc(f);
+  ASSERT_TRUE(feof(f));
+  fclose(f);
+
+  // Verify the file contents
+  ASSERT_TRUE(!memcmp(contents.c_str(), buff, contents.size()));
+  delete[] buff;
+
+// Delete the file
+#ifdef _WIN32
+  ASSERT_EQ(0, _unlink(fileName));
+#else
+  ASSERT_EQ(0, unlink(fileName));
+#endif
+}
+
+bool g_ReadHandlerTesterDeleted = false;
+
+class ReadHandlerTester : public CefReadHandler {
+ public:
+  ReadHandlerTester()
+      : read_called_(false),
+        read_ptr_(nullptr),
+        read_size_(0),
+        read_n_(0),
+        seek_called_(false),
+        seek_offset_(0),
+        seek_whence_(0),
+        tell_called_(false),
+        eof_called_(false) {}
+  ~ReadHandlerTester() override { g_ReadHandlerTesterDeleted = true; }
+
+  size_t Read(void* ptr, size_t size, size_t n) override {
+    read_called_ = true;
+    read_ptr_ = ptr;
+    read_size_ = size;
+    read_n_ = n;
+    return 10;
+  }
+
+  int Seek(int64 offset, int whence) override {
+    seek_called_ = true;
+    seek_offset_ = offset;
+    seek_whence_ = whence;
+    return 10;
+  }
+
+  int64 Tell() override {
+    tell_called_ = true;
+    return 10;
+  }
+
+  int Eof() override {
+    eof_called_ = true;
+    return 10;
+  }
+
+  bool MayBlock() override { return false; }
+
+  bool read_called_;
+  const void* read_ptr_;
+  size_t read_size_;
+  size_t read_n_;
+
+  bool seek_called_;
+  int64 seek_offset_;
+  int seek_whence_;
+
+  bool tell_called_;
+
+  bool eof_called_;
+
+  IMPLEMENT_REFCOUNTING(ReadHandlerTester);
+};
+
+TEST(StreamTest, ReadHandler) {
+  ReadHandlerTester* handler = new ReadHandlerTester();
+  ASSERT_TRUE(handler != nullptr);
+
+  CefRefPtr<CefStreamReader> stream(CefStreamReader::CreateForHandler(handler));
+  ASSERT_TRUE(stream.get() != nullptr);
+  ASSERT_FALSE(stream->MayBlock());
+
+  // CefReadHandler Read
+  const char* read_ptr = "My data";
+  size_t read_size = sizeof(read_ptr);
+  size_t read_n = 1;
+  size_t read_res = stream->Read(
+      static_cast<void*>(const_cast<char*>(read_ptr)), read_size, read_n);
+  ASSERT_TRUE(handler->read_called_);
+  ASSERT_EQ((size_t)10, read_res);
+  ASSERT_EQ(read_ptr, handler->read_ptr_);
+  ASSERT_EQ(read_size, handler->read_size_);
+  ASSERT_EQ(read_n, handler->read_n_);
+
+  // CefReadHandler Seek
+  int64 seek_offset = 10;
+  int seek_whence = SEEK_CUR;
+  int seek_res = stream->Seek(seek_offset, seek_whence);
+  ASSERT_TRUE(handler->seek_called_);
+  ASSERT_EQ(10, seek_res);
+  ASSERT_EQ(seek_offset, handler->seek_offset_);
+  ASSERT_EQ(seek_whence, handler->seek_whence_);
+
+  // CefReadHandler Tell
+  int64 tell_res = stream->Tell();
+  ASSERT_TRUE(handler->tell_called_);
+  ASSERT_EQ(10, tell_res);
+
+  // CefReadHandler Eof
+  int eof_res = stream->Eof();
+  ASSERT_TRUE(handler->eof_called_);
+  ASSERT_EQ(10, eof_res);
+
+  // Delete the stream
+  stream = nullptr;
+
+  // Verify that the handler object was deleted
+  ASSERT_TRUE(g_ReadHandlerTesterDeleted);
+}
+
+bool g_WriteHandlerTesterDeleted = false;
+
+class WriteHandlerTester : public CefWriteHandler {
+ public:
+  WriteHandlerTester()
+      : write_called_(false),
+        write_ptr_(nullptr),
+        write_size_(0),
+        write_n_(0),
+        seek_called_(false),
+        seek_offset_(0),
+        seek_whence_(0),
+        tell_called_(false),
+        flush_called_(false) {}
+  ~WriteHandlerTester() override { g_WriteHandlerTesterDeleted = true; }
+
+  size_t Write(const void* ptr, size_t size, size_t n) override {
+    write_called_ = true;
+    write_ptr_ = ptr;
+    write_size_ = size;
+    write_n_ = n;
+    return 10;
+  }
+
+  int Seek(int64 offset, int whence) override {
+    seek_called_ = true;
+    seek_offset_ = offset;
+    seek_whence_ = whence;
+    return 10;
+  }
+
+  int64 Tell() override {
+    tell_called_ = true;
+    return 10;
+  }
+
+  int Flush() override {
+    flush_called_ = true;
+    return 10;
+  }
+
+  bool MayBlock() override { return false; }
+
+  bool write_called_;
+  const void* write_ptr_;
+  size_t write_size_;
+  size_t write_n_;
+
+  bool seek_called_;
+  int64 seek_offset_;
+  int seek_whence_;
+
+  bool tell_called_;
+
+  bool flush_called_;
+
+  IMPLEMENT_REFCOUNTING(WriteHandlerTester);
+};
+
+TEST(StreamTest, WriteHandler) {
+  WriteHandlerTester* handler = new WriteHandlerTester();
+  ASSERT_TRUE(handler != nullptr);
+
+  CefRefPtr<CefStreamWriter> stream(CefStreamWriter::CreateForHandler(handler));
+  ASSERT_TRUE(stream.get() != nullptr);
+  ASSERT_FALSE(stream->MayBlock());
+
+  // CefWriteHandler Write
+  const char* write_ptr = "My data";
+  size_t write_size = sizeof(write_ptr);
+  size_t write_n = 1;
+  size_t write_res = stream->Write(write_ptr, write_size, write_n);
+  ASSERT_TRUE(handler->write_called_);
+  ASSERT_EQ((size_t)10, write_res);
+  ASSERT_EQ(write_ptr, handler->write_ptr_);
+  ASSERT_EQ(write_size, handler->write_size_);
+  ASSERT_EQ(write_n, handler->write_n_);
+
+  // CefWriteHandler Seek
+  int64 seek_offset = 10;
+  int seek_whence = SEEK_CUR;
+  int seek_res = stream->Seek(seek_offset, seek_whence);
+  ASSERT_TRUE(handler->seek_called_);
+  ASSERT_EQ(10, seek_res);
+  ASSERT_EQ(seek_offset, handler->seek_offset_);
+  ASSERT_EQ(seek_whence, handler->seek_whence_);
+
+  // CefWriteHandler Tell
+  int64 tell_res = stream->Tell();
+  ASSERT_TRUE(handler->tell_called_);
+  ASSERT_EQ(10, tell_res);
+
+  // CefWriteHandler Flush
+  int flush_res = stream->Flush();
+  ASSERT_TRUE(handler->flush_called_);
+  ASSERT_EQ(10, flush_res);
+
+  // Delete the stream
+  stream = nullptr;
+
+  // Verify that the handler object was deleted
+  ASSERT_TRUE(g_WriteHandlerTesterDeleted);
+}
diff --git a/src/tests/ceftests/string_unittest.cc b/src/tests/ceftests/string_unittest.cc
new file mode 100644
index 0000000..a260960
--- /dev/null
+++ b/src/tests/ceftests/string_unittest.cc
@@ -0,0 +1,365 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <map>
+#include <vector>
+
+#include "include/base/cef_string16.h"
+#include "include/internal/cef_string.h"
+#include "include/internal/cef_string_list.h"
+#include "include/internal/cef_string_map.h"
+#include "include/internal/cef_string_multimap.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+// Test UTF8 strings.
+TEST(StringTest, UTF8) {
+  CefStringUTF8 str1("Test String");
+  EXPECT_EQ(str1.length(), (size_t)11);
+  EXPECT_FALSE(str1.empty());
+  EXPECT_TRUE(str1.IsOwner());
+
+  // Test equality.
+  CefStringUTF8 str2("Test String");
+  EXPECT_EQ(str1, str2);
+  EXPECT_LE(str1, str2);
+  EXPECT_GE(str1, str2);
+
+  str2 = "Test Test";
+  EXPECT_LT(str1, str2);
+  EXPECT_GT(str2, str1);
+
+  // When strings are the same but of unequal length, the longer string is
+  // greater.
+  str2 = "Test";
+  EXPECT_LT(str2, str1);
+  EXPECT_GT(str1, str2);
+
+  // Test conversions.
+  str2 = str1.ToString();
+  EXPECT_EQ(str1, str2);
+  str2 = str1.ToWString();
+  EXPECT_EQ(str1, str2);
+
+  // Test userfree assignment.
+  cef_string_userfree_utf8_t uf = str2.DetachToUserFree();
+  EXPECT_TRUE(uf != nullptr);
+  EXPECT_TRUE(str2.empty());
+  str2.AttachToUserFree(uf);
+  EXPECT_FALSE(str2.empty());
+  EXPECT_EQ(str1, str2);
+}
+
+// Test UTF16 strings.
+TEST(StringTest, UTF16) {
+  CefStringUTF16 str1("Test String");
+  EXPECT_EQ(str1.length(), (size_t)11);
+  EXPECT_FALSE(str1.empty());
+  EXPECT_TRUE(str1.IsOwner());
+
+  // Test equality.
+  CefStringUTF16 str2("Test String");
+  EXPECT_EQ(str1, str2);
+  EXPECT_LE(str1, str2);
+  EXPECT_GE(str1, str2);
+
+  str2 = "Test Test";
+  EXPECT_LT(str1, str2);
+  EXPECT_GT(str2, str1);
+
+  // When strings are the same but of unequal length, the longer string is
+  // greater.
+  str2 = "Test";
+  EXPECT_LT(str2, str1);
+  EXPECT_GT(str1, str2);
+
+  // Test conversions.
+  str2 = str1.ToString();
+  EXPECT_EQ(str1, str2);
+  str2 = str1.ToWString();
+  EXPECT_EQ(str1, str2);
+
+  // Test userfree assignment.
+  cef_string_userfree_utf16_t uf = str2.DetachToUserFree();
+  EXPECT_TRUE(uf != nullptr);
+  EXPECT_TRUE(str2.empty());
+  str2.AttachToUserFree(uf);
+  EXPECT_FALSE(str2.empty());
+  EXPECT_EQ(str1, str2);
+}
+
+// Test wide strings.
+TEST(StringTest, Wide) {
+  CefStringWide str1("Test String");
+  EXPECT_EQ(str1.length(), (size_t)11);
+  EXPECT_FALSE(str1.empty());
+  EXPECT_TRUE(str1.IsOwner());
+
+  // Test equality.
+  CefStringWide str2("Test String");
+  EXPECT_EQ(str1, str2);
+  EXPECT_LE(str1, str2);
+  EXPECT_GE(str1, str2);
+
+  str2 = "Test Test";
+  EXPECT_LT(str1, str2);
+  EXPECT_GT(str2, str1);
+
+  // When strings are the same but of unequal length, the longer string is
+  // greater.
+  str2 = "Test";
+  EXPECT_LT(str2, str1);
+  EXPECT_GT(str1, str2);
+
+  // Test conversions.
+  str2 = str1.ToString();
+  EXPECT_EQ(str1, str2);
+  str2 = str1.ToWString();
+  EXPECT_EQ(str1, str2);
+
+  // Test userfree assignment.
+  cef_string_userfree_wide_t uf = str2.DetachToUserFree();
+  EXPECT_TRUE(uf != nullptr);
+  EXPECT_TRUE(str2.empty());
+  str2.AttachToUserFree(uf);
+  EXPECT_FALSE(str2.empty());
+  EXPECT_EQ(str1, str2);
+}
+
+// Test base::string16 convertion to/from CefString types.
+TEST(StringTest, string16) {
+  CefStringUTF8 str8("Test String 1"), str8b;
+  CefStringUTF16 str16("Test String 2"), str16b;
+  CefStringWide strwide("Test String 3"), strwideb;
+  base::string16 base_str;
+
+  base_str = str8;
+  str8b = base_str;
+  EXPECT_EQ(str8, base_str);
+  EXPECT_EQ(str8, str8b);
+
+  base_str = str16;
+  str16b = base_str;
+  EXPECT_EQ(str16, base_str);
+  EXPECT_EQ(str16, str16b);
+
+  base_str = strwide;
+  strwideb = base_str;
+  EXPECT_EQ(strwide, base_str);
+  EXPECT_EQ(strwide, strwideb);
+}
+
+// Test string lists.
+TEST(StringTest, List) {
+  typedef std::vector<CefString> ListType;
+  ListType list;
+  list.push_back("String 1");
+  list.push_back("String 2");
+  list.push_back("String 3");
+
+  EXPECT_EQ(list[0], "String 1");
+  EXPECT_EQ(list[1], "String 2");
+  EXPECT_EQ(list[2], "String 3");
+
+  cef_string_list_t listPtr = cef_string_list_alloc();
+  EXPECT_TRUE(listPtr != nullptr);
+  ListType::const_iterator it = list.begin();
+  for (; it != list.end(); ++it)
+    cef_string_list_append(listPtr, it->GetStruct());
+
+  CefString str;
+  int ret;
+
+  EXPECT_EQ(cef_string_list_size(listPtr), 3U);
+
+  ret = cef_string_list_value(listPtr, 0U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 1");
+  ret = cef_string_list_value(listPtr, 1U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 2");
+  ret = cef_string_list_value(listPtr, 2U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 3");
+
+  cef_string_list_t listPtr2 = cef_string_list_copy(listPtr);
+  cef_string_list_clear(listPtr);
+  EXPECT_EQ(cef_string_list_size(listPtr), 0U);
+  cef_string_list_free(listPtr);
+
+  EXPECT_EQ(cef_string_list_size(listPtr2), 3U);
+
+  ret = cef_string_list_value(listPtr2, 0U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 1");
+  ret = cef_string_list_value(listPtr2, 1U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 2");
+  ret = cef_string_list_value(listPtr2, 2U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 3");
+
+  cef_string_list_free(listPtr2);
+}
+
+// Test string maps.
+TEST(StringTest, Map) {
+  typedef std::map<CefString, CefString> MapType;
+  MapType map;
+  map.insert(std::make_pair("Key 1", "String 1"));
+  map.insert(std::make_pair("Key 2", "String 2"));
+  map.insert(std::make_pair("Key 3", "String 3"));
+
+  MapType::const_iterator it;
+
+  it = map.find("Key 2");
+  EXPECT_TRUE(it != map.end());
+  EXPECT_EQ(it->first, "Key 2");
+  EXPECT_EQ(it->second, "String 2");
+
+  it = map.find(L"Key 2");
+  EXPECT_TRUE(it != map.end());
+  EXPECT_EQ(it->first, L"Key 2");
+  EXPECT_EQ(it->second, L"String 2");
+
+  EXPECT_EQ(map["Key 1"], "String 1");
+  EXPECT_EQ(map["Key 2"], "String 2");
+  EXPECT_EQ(map["Key 3"], "String 3");
+
+  cef_string_map_t mapPtr = cef_string_map_alloc();
+
+  it = map.begin();
+  for (; it != map.end(); ++it) {
+    cef_string_map_append(mapPtr, it->first.GetStruct(),
+                          it->second.GetStruct());
+  }
+
+  CefString str;
+  int ret;
+
+  EXPECT_EQ(cef_string_map_size(mapPtr), 3U);
+
+  ret = cef_string_map_key(mapPtr, 0U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "Key 1");
+  ret = cef_string_map_value(mapPtr, 0U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 1");
+
+  ret = cef_string_map_key(mapPtr, 1U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "Key 2");
+  ret = cef_string_map_value(mapPtr, 1U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 2");
+
+  ret = cef_string_map_key(mapPtr, 2U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "Key 3");
+  ret = cef_string_map_value(mapPtr, 2U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 3");
+
+  CefString key;
+  key.FromASCII("Key 2");
+  ret = cef_string_map_find(mapPtr, key.GetStruct(), str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 2");
+
+  cef_string_map_clear(mapPtr);
+  EXPECT_EQ(cef_string_map_size(mapPtr), 0U);
+
+  cef_string_map_free(mapPtr);
+}
+
+// Test string maps.
+TEST(StringTest, Multimap) {
+  typedef std::multimap<CefString, CefString> MapType;
+  MapType map;
+  map.insert(std::make_pair("Key 1", "String 1"));
+  map.insert(std::make_pair("Key 2", "String 2"));
+  map.insert(std::make_pair("Key 2", "String 2.1"));
+  map.insert(std::make_pair("Key 3", "String 3"));
+
+  MapType::const_iterator it;
+
+  it = map.find("Key 2");
+  EXPECT_TRUE(it != map.end());
+  EXPECT_EQ(it->first, "Key 2");
+  EXPECT_EQ(it->second, "String 2");
+
+  std::pair<MapType::const_iterator, MapType::const_iterator> range_it =
+      map.equal_range("Key 2");
+  EXPECT_TRUE(range_it.first != range_it.second);
+  MapType::const_iterator same_key_it = range_it.first;
+  // Either of "String 2" or "String 2.1" is fine since
+  // std::multimap provides no guarantee wrt the order of
+  // values with the same key.
+  EXPECT_EQ(same_key_it->second.ToString().find("String 2"), 0U);
+  EXPECT_EQ((++same_key_it)->second.ToString().find("String 2"), 0U);
+  EXPECT_EQ(map.count("Key 2"), 2U);
+
+  EXPECT_EQ(map.find("Key 1")->second, "String 1");
+  EXPECT_EQ(map.find("Key 3")->second, "String 3");
+
+  cef_string_multimap_t mapPtr = cef_string_multimap_alloc();
+
+  it = map.begin();
+  for (; it != map.end(); ++it) {
+    cef_string_multimap_append(mapPtr, it->first.GetStruct(),
+                               it->second.GetStruct());
+  }
+
+  CefString str;
+  int ret;
+
+  EXPECT_EQ(cef_string_multimap_size(mapPtr), 4U);
+
+  ret = cef_string_multimap_key(mapPtr, 0U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "Key 1");
+  ret = cef_string_multimap_value(mapPtr, 0U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 1");
+
+  ret = cef_string_multimap_key(mapPtr, 1U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "Key 2");
+  ret = cef_string_multimap_value(mapPtr, 1U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str.ToString().find("String 2"), 0U);
+
+  ret = cef_string_multimap_key(mapPtr, 2U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "Key 2");
+  ret = cef_string_multimap_value(mapPtr, 2U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str.ToString().find("String 2"), 0U);
+
+  ret = cef_string_multimap_key(mapPtr, 3U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "Key 3");
+  ret = cef_string_multimap_value(mapPtr, 3U, str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str, "String 3");
+
+  CefString key;
+  key.FromASCII("Key 2");
+  size_t size = cef_string_multimap_find_count(mapPtr, key.GetStruct());
+  EXPECT_EQ(size, 2U);
+
+  ret = cef_string_multimap_enumerate(mapPtr, key.GetStruct(), 0U,
+                                      str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str.ToString().find("String 2"), 0U);
+
+  ret = cef_string_multimap_enumerate(mapPtr, key.GetStruct(), 1U,
+                                      str.GetWritableStruct());
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(str.ToString().find("String 2"), 0U);
+
+  cef_string_multimap_clear(mapPtr);
+  EXPECT_EQ(cef_string_multimap_size(mapPtr), 0U);
+
+  cef_string_multimap_free(mapPtr);
+}
diff --git a/src/tests/ceftests/task_unittest.cc b/src/tests/ceftests/task_unittest.cc
new file mode 100644
index 0000000..24a39cb
--- /dev/null
+++ b/src/tests/ceftests/task_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_command_line.h"
+#include "include/cef_task.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void WaitForEvent(CefRefPtr<CefWaitableEvent> event) {
+  if (CefCommandLine::GetGlobalCommandLine()->HasSwitch(
+          "disable-test-timeout")) {
+    event->Wait();
+  } else {
+    EXPECT_TRUE(event->TimedWait(1000));
+  }
+}
+
+void GetForCurrentThread(bool* ran_test, CefRefPtr<CefWaitableEvent> event) {
+  // Currently on the FILE thread.
+  CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForCurrentThread();
+  EXPECT_TRUE(runner.get());
+  EXPECT_TRUE(runner->BelongsToCurrentThread());
+  EXPECT_TRUE(runner->BelongsToThread(TID_FILE));
+  EXPECT_FALSE(runner->BelongsToThread(TID_IO));
+  EXPECT_TRUE(runner->IsSame(runner));
+
+  CefRefPtr<CefTaskRunner> runner2 = CefTaskRunner::GetForCurrentThread();
+  EXPECT_TRUE(runner2.get());
+  EXPECT_TRUE(runner->IsSame(runner2));
+  EXPECT_TRUE(runner2->IsSame(runner));
+
+  // Not on the IO thread.
+  CefRefPtr<CefTaskRunner> runner3 = CefTaskRunner::GetForThread(TID_IO);
+  EXPECT_TRUE(runner3.get());
+  EXPECT_FALSE(runner->IsSame(runner3));
+  EXPECT_FALSE(runner3->IsSame(runner));
+
+  *ran_test = true;
+  event->Signal();
+}
+
+void GetForThread(bool* ran_test, CefRefPtr<CefWaitableEvent> event) {
+  // Currently on the FILE thread.
+  CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForThread(TID_IO);
+  EXPECT_TRUE(runner.get());
+  EXPECT_FALSE(runner->BelongsToCurrentThread());
+  EXPECT_TRUE(runner->BelongsToThread(TID_IO));
+  EXPECT_FALSE(runner->BelongsToThread(TID_FILE));
+  EXPECT_TRUE(runner->IsSame(runner));
+
+  CefRefPtr<CefTaskRunner> runner2 = CefTaskRunner::GetForThread(TID_IO);
+  EXPECT_TRUE(runner2.get());
+  EXPECT_TRUE(runner->IsSame(runner2));
+  EXPECT_TRUE(runner2->IsSame(runner));
+
+  CefRefPtr<CefTaskRunner> runner3 = CefTaskRunner::GetForThread(TID_FILE);
+  EXPECT_TRUE(runner3.get());
+  EXPECT_FALSE(runner->IsSame(runner3));
+  EXPECT_FALSE(runner3->IsSame(runner));
+
+  *ran_test = true;
+  event->Signal();
+}
+
+void PostTaskEvent1(bool* ran_test,
+                    CefRefPtr<CefWaitableEvent> event,
+                    CefRefPtr<CefTaskRunner> runner) {
+  // Currently on the IO thread.
+  EXPECT_TRUE(runner->BelongsToCurrentThread());
+  EXPECT_TRUE(runner->BelongsToThread(TID_IO));
+  EXPECT_FALSE(runner->BelongsToThread(TID_FILE));
+
+  // Current thread should be the IO thread.
+  CefRefPtr<CefTaskRunner> runner2 = CefTaskRunner::GetForCurrentThread();
+  EXPECT_TRUE(runner2.get());
+  EXPECT_TRUE(runner2->BelongsToCurrentThread());
+  EXPECT_TRUE(runner2->BelongsToThread(TID_IO));
+  EXPECT_FALSE(runner2->BelongsToThread(TID_FILE));
+  EXPECT_TRUE(runner->IsSame(runner2));
+  EXPECT_TRUE(runner2->IsSame(runner));
+
+  // Current thread should be the IO thread.
+  CefRefPtr<CefTaskRunner> runner3 = CefTaskRunner::GetForThread(TID_IO);
+  EXPECT_TRUE(runner3.get());
+  EXPECT_TRUE(runner3->BelongsToCurrentThread());
+  EXPECT_TRUE(runner3->BelongsToThread(TID_IO));
+  EXPECT_FALSE(runner3->BelongsToThread(TID_FILE));
+  EXPECT_TRUE(runner->IsSame(runner3));
+  EXPECT_TRUE(runner3->IsSame(runner));
+
+  // Current thread should not be the FILE thread.
+  CefRefPtr<CefTaskRunner> runner4 = CefTaskRunner::GetForThread(TID_FILE);
+  EXPECT_TRUE(runner4.get());
+  EXPECT_FALSE(runner4->BelongsToCurrentThread());
+  EXPECT_FALSE(runner4->BelongsToThread(TID_IO));
+  EXPECT_TRUE(runner4->BelongsToThread(TID_FILE));
+  EXPECT_FALSE(runner->IsSame(runner4));
+  EXPECT_FALSE(runner4->IsSame(runner));
+
+  *ran_test = true;
+  event->Signal();
+}
+
+void PostTask1(bool* ran_test, CefRefPtr<CefWaitableEvent> event) {
+  // Currently on the FILE thread.
+  CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForThread(TID_IO);
+  EXPECT_TRUE(runner.get());
+  EXPECT_FALSE(runner->BelongsToCurrentThread());
+  EXPECT_TRUE(runner->BelongsToThread(TID_IO));
+
+  runner->PostTask(CefCreateClosureTask(
+      base::Bind(&PostTaskEvent1, ran_test, event, runner)));
+}
+
+void PostDelayedTask1(bool* ran_test, CefRefPtr<CefWaitableEvent> event) {
+  // Currently on the FILE thread.
+  CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForThread(TID_IO);
+  EXPECT_TRUE(runner.get());
+  EXPECT_FALSE(runner->BelongsToCurrentThread());
+  EXPECT_TRUE(runner->BelongsToThread(TID_IO));
+
+  runner->PostDelayedTask(CefCreateClosureTask(base::Bind(
+                              &PostTaskEvent1, ran_test, event, runner)),
+                          0);
+}
+
+void PostTaskEvent2(bool* ran_test, CefRefPtr<CefWaitableEvent> event) {
+  EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+  EXPECT_FALSE(CefCurrentlyOn(TID_FILE));
+
+  *ran_test = true;
+  event->Signal();
+}
+
+void PostTask2(bool* ran_test, CefRefPtr<CefWaitableEvent> event) {
+  // Currently on the FILE thread.
+  EXPECT_FALSE(CefCurrentlyOn(TID_IO));
+
+  CefPostTask(TID_IO, CefCreateClosureTask(
+                          base::Bind(&PostTaskEvent2, ran_test, event)));
+}
+
+void PostDelayedTask2(bool* ran_test, CefRefPtr<CefWaitableEvent> event) {
+  // Currently on the FILE thread.
+  EXPECT_FALSE(CefCurrentlyOn(TID_IO));
+
+  CefPostDelayedTask(
+      TID_IO,
+      CefCreateClosureTask(base::Bind(&PostTaskEvent2, ran_test, event)), 0);
+}
+
+}  // namespace
+
+TEST(TaskTest, GetForCurrentThread) {
+  bool ran_test = false;
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  CefPostTask(TID_FILE, CefCreateClosureTask(base::Bind(&GetForCurrentThread,
+                                                        &ran_test, event)));
+  WaitForEvent(event);
+  EXPECT_TRUE(ran_test);
+}
+
+TEST(TaskTest, GetForThread) {
+  bool ran_test = false;
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  CefPostTask(TID_FILE, CefCreateClosureTask(
+                            base::Bind(&GetForThread, &ran_test, event)));
+  WaitForEvent(event);
+  EXPECT_TRUE(ran_test);
+}
+
+TEST(TaskTest, PostTask1) {
+  bool ran_test = false;
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  CefPostTask(TID_FILE,
+              CefCreateClosureTask(base::Bind(&PostTask1, &ran_test, event)));
+  WaitForEvent(event);
+  EXPECT_TRUE(ran_test);
+}
+
+TEST(TaskTest, PostDelayedTask1) {
+  bool ran_test = false;
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  CefPostTask(TID_FILE, CefCreateClosureTask(
+                            base::Bind(&PostDelayedTask1, &ran_test, event)));
+  WaitForEvent(event);
+  EXPECT_TRUE(ran_test);
+}
+
+TEST(TaskTest, PostTask2) {
+  bool ran_test = false;
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  CefPostTask(TID_FILE,
+              CefCreateClosureTask(base::Bind(&PostTask2, &ran_test, event)));
+  WaitForEvent(event);
+  EXPECT_TRUE(ran_test);
+}
+
+TEST(TaskTest, PostDelayedTask2) {
+  bool ran_test = false;
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  CefPostTask(TID_FILE, CefCreateClosureTask(
+                            base::Bind(&PostDelayedTask2, &ran_test, event)));
+  WaitForEvent(event);
+  EXPECT_TRUE(ran_test);
+}
diff --git a/src/tests/ceftests/test_handler.cc b/src/tests/ceftests/test_handler.cc
new file mode 100644
index 0000000..ff064bf
--- /dev/null
+++ b/src/tests/ceftests/test_handler.cc
@@ -0,0 +1,505 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/test_handler.h"
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/cef_command_line.h"
+#include "include/cef_stream.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+#include "tests/shared/common/client_switches.h"
+
+#if defined(USE_AURA)
+#include "include/views/cef_browser_view.h"
+#include "include/views/cef_window.h"
+#endif
+
+namespace {
+
+#if defined(USE_AURA)
+
+// Delegate implementation for the CefWindow that will host the Views-based
+// browser.
+class TestWindowDelegate : public CefWindowDelegate {
+ public:
+  // Create a new top-level Window hosting |browser_view|.
+  static void CreateBrowserWindow(CefRefPtr<CefBrowserView> browser_view,
+                                  const std::string& title) {
+    CefWindow::CreateTopLevelWindow(
+        new TestWindowDelegate(browser_view, "CefUnitTestViews " + title));
+  }
+
+  // CefWindowDelegate methods:
+
+  void OnWindowCreated(CefRefPtr<CefWindow> window) override {
+    // Add the browser view and show the window.
+    window->CenterWindow(CefSize(800, 600));
+    window->SetTitle(title_);
+    window->AddChildView(browser_view_);
+    window->Show();
+  }
+
+  void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
+    browser_view_ = nullptr;
+  }
+
+  bool CanClose(CefRefPtr<CefWindow> window) override {
+    // Allow the window to close if the browser says it's OK.
+    CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
+    if (browser)
+      return browser->GetHost()->TryCloseBrowser();
+    return true;
+  }
+
+ private:
+  TestWindowDelegate(CefRefPtr<CefBrowserView> browser_view,
+                     const CefString& title)
+      : browser_view_(browser_view), title_(title) {}
+
+  CefRefPtr<CefBrowserView> browser_view_;
+  CefString title_;
+
+  IMPLEMENT_REFCOUNTING(TestWindowDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestWindowDelegate);
+};
+
+// Delegate implementation for the CefBrowserView.
+class TestBrowserViewDelegate : public CefBrowserViewDelegate {
+ public:
+  TestBrowserViewDelegate() {}
+
+  // CefBrowserViewDelegate methods:
+
+  bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
+                                 CefRefPtr<CefBrowserView> popup_browser_view,
+                                 bool is_devtools) override {
+    // Create our own Window for popups. It will show itself after creation.
+    TestWindowDelegate::CreateBrowserWindow(popup_browser_view,
+                                            is_devtools ? "DevTools" : "Popup");
+    return true;
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(TestBrowserViewDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestBrowserViewDelegate);
+};
+
+#endif  // defined(USE_AURA)
+
+}  // namespace
+
+// TestHandler::CompletionState
+
+TestHandler::CompletionState::CompletionState(int total)
+    : total_(total), count_(0) {
+  event_ = CefWaitableEvent::CreateWaitableEvent(true, false);
+}
+
+void TestHandler::CompletionState::TestComplete() {
+  if (++count_ == total_) {
+    count_ = 0;
+
+    // Signal that the test is now complete. Do not access any object members
+    // after this call because |this| might be deleted.
+    event_->Signal();
+  }
+}
+
+void TestHandler::CompletionState::WaitForTests() {
+  // Wait for the test to complete
+  event_->Wait();
+
+  // Reset the event so the same test can be executed again.
+  event_->Reset();
+}
+
+// TestHandler::Collection
+
+TestHandler::Collection::Collection(CompletionState* completion_state)
+    : completion_state_(completion_state) {
+  EXPECT_TRUE(completion_state_);
+}
+
+void TestHandler::Collection::AddTestHandler(TestHandler* test_handler) {
+  EXPECT_EQ(test_handler->completion_state_, completion_state_);
+  handler_list_.push_back(test_handler);
+}
+
+void TestHandler::Collection::ExecuteTests() {
+  EXPECT_GT(handler_list_.size(), 0UL);
+
+  TestHandlerList::const_iterator it;
+
+  it = handler_list_.begin();
+  for (; it != handler_list_.end(); ++it)
+    (*it)->SetupTest();
+
+  completion_state_->WaitForTests();
+
+  it = handler_list_.begin();
+  for (; it != handler_list_.end(); ++it)
+    (*it)->RunTest();
+
+  completion_state_->WaitForTests();
+}
+
+// TestHandler::UIThreadHelper
+
+TestHandler::UIThreadHelper::UIThreadHelper() : weak_ptr_factory_(this) {}
+
+void TestHandler::UIThreadHelper::PostTask(const base::Closure& task) {
+  EXPECT_UI_THREAD();
+  CefPostTask(TID_UI, base::Bind(&UIThreadHelper::TaskHelper,
+                                 weak_ptr_factory_.GetWeakPtr(), task));
+}
+
+void TestHandler::UIThreadHelper::PostDelayedTask(const base::Closure& task,
+                                                  int delay_ms) {
+  EXPECT_UI_THREAD();
+  CefPostDelayedTask(TID_UI,
+                     base::Bind(&UIThreadHelper::TaskHelper,
+                                weak_ptr_factory_.GetWeakPtr(), task),
+                     delay_ms);
+}
+
+void TestHandler::UIThreadHelper::TaskHelper(const base::Closure& task) {
+  EXPECT_UI_THREAD();
+  task.Run();
+}
+
+// TestHandler
+
+int TestHandler::browser_count_ = 0;
+
+TestHandler::TestHandler(CompletionState* completion_state)
+    : first_browser_id_(0),
+      signal_completion_when_all_browsers_close_(true),
+      destroy_event_(nullptr),
+      destroy_test_expected_(true),
+      destroy_test_called_(false) {
+  if (completion_state) {
+    completion_state_ = completion_state;
+    completion_state_owned_ = false;
+  } else {
+    completion_state_ = new CompletionState(1);
+    completion_state_owned_ = true;
+  }
+}
+
+TestHandler::~TestHandler() {
+  DCHECK(!ui_thread_helper_.get());
+  if (destroy_test_expected_)
+    EXPECT_TRUE(destroy_test_called_);
+  else
+    EXPECT_FALSE(destroy_test_called_);
+  EXPECT_TRUE(browser_map_.empty());
+
+  if (completion_state_owned_)
+    delete completion_state_;
+
+  if (destroy_event_)
+    destroy_event_->Signal();
+}
+
+void TestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  EXPECT_UI_THREAD();
+
+  browser_count_++;
+
+  const int browser_id = browser->GetIdentifier();
+  EXPECT_EQ(browser_map_.find(browser_id), browser_map_.end());
+  if (browser_map_.empty()) {
+    first_browser_id_ = browser_id;
+    first_browser_ = browser;
+  }
+  browser_map_.insert(std::make_pair(browser_id, browser));
+}
+
+void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  EXPECT_UI_THREAD();
+
+  // Free the browser pointer so that the browser can be destroyed.
+  const int browser_id = browser->GetIdentifier();
+  BrowserMap::iterator it = browser_map_.find(browser_id);
+  EXPECT_NE(it, browser_map_.end());
+  browser_map_.erase(it);
+
+  if (browser_id == first_browser_id_) {
+    first_browser_id_ = 0;
+    first_browser_ = nullptr;
+  }
+
+  if (browser_map_.empty() && signal_completion_when_all_browsers_close_) {
+    // Signal that the test is now complete.
+    TestComplete();
+  }
+
+  browser_count_--;
+}
+
+namespace {
+
+CefResponse::HeaderMap ToCefHeaderMap(
+    const ResourceContent::HeaderMap& headerMap) {
+  CefResponse::HeaderMap result;
+  ResourceContent::HeaderMap::const_iterator it = headerMap.begin();
+  for (; it != headerMap.end(); ++it) {
+    result.insert(std::pair<CefString, CefString>(it->first, it->second));
+  }
+  return result;
+}
+
+}  // namespace
+
+CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefRequest> request) {
+  EXPECT_IO_THREAD();
+
+  if (resource_map_.size() > 0) {
+    CefString url = request->GetURL();
+
+    // Ignore the query component, if any.
+    std::string urlStr = url;
+    size_t idx = urlStr.find('?');
+    if (idx > 0)
+      urlStr = urlStr.substr(0, idx);
+
+    ResourceMap::const_iterator it = resource_map_.find(urlStr);
+    if (it != resource_map_.end()) {
+      // Return the previously mapped resource
+      CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
+          static_cast<void*>(const_cast<char*>(it->second.content().c_str())),
+          it->second.content().length());
+      return new CefStreamResourceHandler(
+          200, "OK", it->second.mimeType(),
+          ToCefHeaderMap(it->second.headerMap()), stream);
+    }
+  }
+
+  return nullptr;
+}
+
+void TestHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                            TerminationStatus status) {
+  LOG(WARNING) << "OnRenderProcessTerminated: status = " << status << ".";
+}
+
+CefRefPtr<CefBrowser> TestHandler::GetBrowser() {
+  return first_browser_;
+}
+
+int TestHandler::GetBrowserId() {
+  return first_browser_id_;
+}
+
+void TestHandler::GetAllBrowsers(BrowserMap* map) {
+  EXPECT_UI_THREAD();
+  EXPECT_TRUE(map);
+  *map = browser_map_;
+}
+
+void TestHandler::ExecuteTest() {
+  EXPECT_EQ(completion_state_->total(), 1);
+
+  // Reset any state from the previous run.
+  if (destroy_test_called_)
+    destroy_test_called_ = false;
+
+  // Run the test.
+  RunTest();
+
+  // Wait for the test to complete.
+  completion_state_->WaitForTests();
+}
+
+void TestHandler::SetupComplete() {
+  // Signal that the test setup is complete.
+  completion_state_->TestComplete();
+}
+
+void TestHandler::DestroyTest() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(&TestHandler::DestroyTest, this));
+    return;
+  }
+
+  EXPECT_TRUE(destroy_test_expected_);
+  if (destroy_test_called_)
+    return;
+  destroy_test_called_ = true;
+
+  if (!browser_map_.empty()) {
+    // Use a copy of the map since the original may be modified while we're
+    // iterating.
+    BrowserMap browser_map = browser_map_;
+
+    // Tell all browsers to close.
+    BrowserMap::const_iterator it = browser_map.begin();
+    for (; it != browser_map.end(); ++it)
+      CloseBrowser(it->second, false);
+  }
+
+  if (ui_thread_helper_.get())
+    ui_thread_helper_.reset(nullptr);
+}
+
+void TestHandler::OnTestTimeout(int timeout_ms, bool treat_as_error) {
+  EXPECT_UI_THREAD();
+  if (treat_as_error) {
+    EXPECT_TRUE(false) << "Test timed out after " << timeout_ms << "ms";
+  }
+  DestroyTest();
+}
+
+void TestHandler::CreateBrowser(const CefString& url,
+                                CefRefPtr<CefRequestContext> request_context,
+                                CefRefPtr<CefDictionaryValue> extra_info) {
+#if defined(USE_AURA)
+  const bool use_views = CefCommandLine::GetGlobalCommandLine()->HasSwitch(
+      client::switches::kUseViews);
+  if (use_views && !CefCurrentlyOn(TID_UI)) {
+    // Views classes must be accessed on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&TestHandler::CreateBrowser, this, url,
+                                   request_context, extra_info));
+    return;
+  }
+#endif  // defined(USE_AURA)
+
+  CefWindowInfo windowInfo;
+  CefBrowserSettings settings;
+  PopulateBrowserSettings(&settings);
+
+#if defined(USE_AURA)
+  if (use_views) {
+    // Create the BrowserView.
+    CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
+        this, url, settings, extra_info, request_context,
+        new TestBrowserViewDelegate());
+
+    // Create the Window. It will show itself after creation.
+    TestWindowDelegate::CreateBrowserWindow(browser_view, std::string());
+  } else
+#endif  // defined(USE_AURA)
+  {
+#if defined(OS_WIN)
+    windowInfo.SetAsPopup(nullptr, "CefUnitTest");
+    windowInfo.style |= WS_VISIBLE;
+#endif
+    CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, extra_info,
+                                  request_context);
+  }
+}
+
+// static
+void TestHandler::CloseBrowser(CefRefPtr<CefBrowser> browser,
+                               bool force_close) {
+  browser->GetHost()->CloseBrowser(force_close);
+}
+
+void TestHandler::AddResource(const std::string& url,
+                              const std::string& content,
+                              const std::string& mime_type) {
+  ResourceContent::HeaderMap headerMap = ResourceContent::HeaderMap();
+  ResourceContent rc = ResourceContent(content, mime_type, headerMap);
+  AddResourceEx(url, rc);
+}
+
+void TestHandler::AddResource(const std::string& url,
+                              const std::string& content,
+                              const std::string& mime_type,
+                              const ResourceContent::HeaderMap& header_map) {
+  ResourceContent rc = ResourceContent(content, mime_type, header_map);
+  AddResourceEx(url, rc);
+}
+
+void TestHandler::AddResourceEx(const std::string& url,
+                                const ResourceContent& content) {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO,
+                base::Bind(&TestHandler::AddResourceEx, this, url, content));
+    return;
+  }
+
+  // Ignore the query component, if any.
+  std::string urlStr = url;
+  size_t idx = urlStr.find('?');
+  if (idx > 0)
+    urlStr = urlStr.substr(0, idx);
+
+  resource_map_.insert(std::make_pair(urlStr, content));
+}
+
+void TestHandler::ClearResources() {
+  if (!CefCurrentlyOn(TID_IO)) {
+    CefPostTask(TID_IO, base::Bind(&TestHandler::ClearResources, this));
+    return;
+  }
+
+  resource_map_.clear();
+}
+
+void TestHandler::SetTestTimeout(int timeout_ms, bool treat_as_error) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(&TestHandler::SetTestTimeout, this,
+                                   timeout_ms, treat_as_error));
+    return;
+  }
+
+  if (destroy_test_called_) {
+    // No need to set the timeout if the test has already completed.
+    return;
+  }
+
+  if (treat_as_error && CefCommandLine::GetGlobalCommandLine()->HasSwitch(
+                            "disable-test-timeout")) {
+    return;
+  }
+
+  // Use a weak reference to |this| via UIThreadHelper so that the TestHandler
+  // can be destroyed before the timeout expires.
+  GetUIThreadHelper()->PostDelayedTask(
+      base::Bind(&TestHandler::OnTestTimeout, base::Unretained(this),
+                 timeout_ms, treat_as_error),
+      timeout_ms);
+}
+
+void TestHandler::TestComplete() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    CefPostTask(TID_UI, base::Bind(&TestHandler::TestComplete, this));
+    return;
+  }
+
+  EXPECT_TRUE(browser_map_.empty());
+  completion_state_->TestComplete();
+}
+
+TestHandler::UIThreadHelper* TestHandler::GetUIThreadHelper() {
+  EXPECT_UI_THREAD();
+  CHECK(!destroy_test_called_);
+
+  if (!ui_thread_helper_.get())
+    ui_thread_helper_.reset(new UIThreadHelper());
+  return ui_thread_helper_.get();
+}
+
+// global functions
+
+bool TestFailed() {
+  CefRefPtr<CefCommandLine> command_line =
+      CefCommandLine::GetGlobalCommandLine();
+  if (command_line->HasSwitch("single-process")) {
+    // Check for a failure on the current test only.
+    return ::testing::UnitTest::GetInstance()
+        ->current_test_info()
+        ->result()
+        ->Failed();
+  } else {
+    // Check for any global failure.
+    return ::testing::UnitTest::GetInstance()->Failed();
+  }
+}
diff --git a/src/tests/ceftests/test_handler.h b/src/tests/ceftests/test_handler.h
new file mode 100644
index 0000000..0c7c514
--- /dev/null
+++ b/src/tests/ceftests/test_handler.h
@@ -0,0 +1,366 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_TEST_HANDLER_H_
+#define CEF_TESTS_UNITTESTS_TEST_HANDLER_H_
+#pragma once
+
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_browser.h"
+#include "include/cef_client.h"
+#include "include/cef_frame.h"
+#include "include/cef_task.h"
+#include "include/cef_waitable_event.h"
+#include "tests/ceftests/thread_helper.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+class TrackCallback {
+ public:
+  TrackCallback() : gotit_(false) {}
+  void yes() { gotit_ = true; }
+  bool isSet() { return gotit_; }
+  void reset() { gotit_ = false; }
+  operator bool() const { return gotit_; }
+
+ protected:
+  bool gotit_;
+};
+
+class ResourceContent {
+ public:
+  typedef std::multimap<std::string, std::string> HeaderMap;
+
+  ResourceContent(const std::string& content,
+                  const std::string& mime_type,
+                  const HeaderMap& header_map)
+      : content_(content), mime_type_(mime_type), header_map_(header_map) {}
+
+  const std::string& content() const { return content_; }
+  const std::string& mimeType() const { return mime_type_; }
+  const HeaderMap& headerMap() const { return header_map_; }
+
+ private:
+  std::string content_;
+  std::string mime_type_;
+  HeaderMap header_map_;
+};
+
+// Base implementation of CefClient for unit tests. Add new interfaces as needed
+// by test cases.
+class TestHandler : public CefClient,
+                    public CefDialogHandler,
+                    public CefDisplayHandler,
+                    public CefDownloadHandler,
+                    public CefJSDialogHandler,
+                    public CefLifeSpanHandler,
+                    public CefLoadHandler,
+                    public CefRequestHandler,
+                    public CefResourceRequestHandler {
+ public:
+  // Tracks the completion state of related test runs.
+  class CompletionState {
+   public:
+    // |total| is the number of times that TestComplete() must be called before
+    // WaitForTests() will return.
+    explicit CompletionState(int total);
+
+    // Call this method to indicate that a test has completed.
+    void TestComplete();
+
+    // This method blocks until TestComplete() has been called the required
+    // number of times.
+    void WaitForTests();
+
+    int total() const { return total_; }
+    int count() const { return count_; }
+
+   private:
+    int total_;
+    int count_;
+
+    // Handle used to notify when the test is complete
+    CefRefPtr<CefWaitableEvent> event_;
+  };
+
+  // Represents a collection of related tests that need to be run
+  // simultaniously.
+  class Collection {
+   public:
+    // The |completion_state| object must outlive this class.
+    explicit Collection(CompletionState* completion_state);
+
+    // The |test_handler| object must outlive this class and it must share the
+    // same CompletionState object passed to the constructor.
+    void AddTestHandler(TestHandler* test_handler);
+
+    // Manages the test run.
+    // 1. Calls TestHandler::SetupTest() for all of the test objects.
+    // 2. Waits for all TestHandler objects to report that initial setup is
+    //    complete by calling TestHandler::SetupComplete().
+    // 3. Calls TestHandler::RunTest() for all of the test objects.
+    // 4. Waits for all TestHandler objects to report that the test is
+    //    complete by calling TestHandler::DestroyTest().
+    void ExecuteTests();
+
+   private:
+    CompletionState* completion_state_;
+
+    typedef std::list<TestHandler*> TestHandlerList;
+    TestHandlerList handler_list_;
+  };
+
+  typedef std::map<int, CefRefPtr<CefBrowser>> BrowserMap;
+
+  // Helper for executing methods using WeakPtr references to TestHandler.
+  class UIThreadHelper {
+   public:
+    UIThreadHelper();
+
+    // Pass in a |task| with an unretained reference to TestHandler. |task| will
+    // be executed only if TestHandler::DestroyTest has not yet been called.
+    // For example:
+    //    GetUIThreadHelper()->PostTask(
+    //        base::Bind(&TestHandler::DoSomething, base::Unretained(this)));
+    void PostTask(const base::Closure& task);
+    void PostDelayedTask(const base::Closure& task, int delay_ms);
+
+   private:
+    void TaskHelper(const base::Closure& task);
+
+    // Must be the last member.
+    base::WeakPtrFactory<UIThreadHelper> weak_ptr_factory_;
+  };
+
+  // The |completion_state| object if specified must outlive this class.
+  explicit TestHandler(CompletionState* completion_state = nullptr);
+  ~TestHandler() override;
+
+  // Implement this method to set up the test. Only used in combination with a
+  // Collection. Call SetupComplete() once the setup is complete.
+  virtual void SetupTest() {}
+
+  // Implement this method to run the test. Call DestroyTest() once the test is
+  // complete.
+  virtual void RunTest() = 0;
+
+  // CefClient methods. Add new methods as needed by test cases.
+  CefRefPtr<CefDialogHandler> GetDialogHandler() override { return this; }
+  CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
+  CefRefPtr<CefDownloadHandler> GetDownloadHandler() override { return this; }
+  CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() override { return this; }
+  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
+  CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
+  CefRefPtr<CefRequestHandler> GetRequestHandler() override { return this; }
+
+  // CefDownloadHandler methods
+  void OnBeforeDownload(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefDownloadItem> download_item,
+      const CefString& suggested_name,
+      CefRefPtr<CefBeforeDownloadCallback> callback) override {}
+
+  // CefLifeSpanHandler methods
+  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
+
+  // CefRequestHandler methods
+  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      bool is_navigation,
+      bool is_download,
+      const CefString& request_initiator,
+      bool& disable_default_handling) override {
+    return this;
+  }
+
+  // CefResourceRequestHandler methods
+  CefRefPtr<CefResourceHandler> GetResourceHandler(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request) override;
+
+  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                 TerminationStatus status) override;
+
+  // These methods should only be used if at most one non-popup browser exists.
+  CefRefPtr<CefBrowser> GetBrowser();
+  int GetBrowserId();
+
+  // Copies the map of all the currently existing browsers into |map|. Must be
+  // called on the UI thread.
+  void GetAllBrowsers(BrowserMap* map);
+
+  // Called by the test function to execute the test. This method blocks until
+  // the test is complete. Do not reference the object after this method
+  // returns. Do not use this method if the CompletionState object is shared by
+  // multiple handlers or when using a Collection object.
+  void ExecuteTest();
+
+  // Event that will be signaled from the TestHandler destructor.
+  // Used by ReleaseAndWaitForDestructor.
+  void SetDestroyEvent(CefRefPtr<CefWaitableEvent> event) {
+    destroy_event_ = event;
+  }
+
+  // If a test will not call DestroyTest() indicate so using this method.
+  void SetDestroyTestExpected(bool expected) {
+    destroy_test_expected_ = expected;
+  }
+
+  // Returns true if a browser currently exists.
+  static bool HasBrowser() { return browser_count_ > 0; }
+
+ protected:
+  // Indicate that test setup is complete. Only used in combination with a
+  // Collection.
+  virtual void SetupComplete();
+
+  // Close any existing non-popup browsers. Test completion will be signaled
+  // once all the browsers have closed if
+  // |signal_completion_when_all_browsers_close_| is true (default value).
+  // If no browsers exist then this method will do nothing and
+  // TestComplete() must be called manually.
+  virtual void DestroyTest();
+
+  // Called on the UI thread if the test times out as a result of calling
+  // SetTestTimeout(). Calls DestroyTest() by default.
+  virtual void OnTestTimeout(int timeout_ms, bool treat_as_error);
+
+  // Called from CreateBrowser() to optionally set per-browser settings.
+  virtual void PopulateBrowserSettings(CefBrowserSettings* settings) {}
+
+  void CreateBrowser(const CefString& url,
+                     CefRefPtr<CefRequestContext> request_context = nullptr,
+                     CefRefPtr<CefDictionaryValue> extra_info = nullptr);
+  static void CloseBrowser(CefRefPtr<CefBrowser> browser, bool force_close);
+
+  void AddResource(const std::string& url,
+                   const std::string& content,
+                   const std::string& mime_type);
+
+  void AddResource(const std::string& url,
+                   const std::string& content,
+                   const std::string& mime_type,
+                   const ResourceContent::HeaderMap& header_map);
+
+  void AddResourceEx(const std::string& url, const ResourceContent& content);
+
+  void ClearResources();
+
+  void SetSignalCompletionWhenAllBrowsersClose(bool val) {
+    signal_completion_when_all_browsers_close_ = val;
+  }
+  bool SignalCompletionWhenAllBrowsersClose() const {
+    return signal_completion_when_all_browsers_close_;
+  }
+
+  // Call OnTestTimeout() after the specified amount of time.
+  void SetTestTimeout(int timeout_ms = 5000, bool treat_as_error = true);
+
+  // Signal that the test is complete. This will be called automatically when
+  // all existing non-popup browsers are closed if
+  // |signal_completion_when_all_browsers_close_| is true (default value). It
+  // is an error to call this method before all browsers have closed.
+  void TestComplete();
+
+  // Returns the single UIThreadHelper instance, creating it if necessary. Must
+  // be called on the UI thread.
+  UIThreadHelper* GetUIThreadHelper();
+
+ private:
+  // Used to notify when the test is complete. Can be accessed on any thread.
+  CompletionState* completion_state_;
+  bool completion_state_owned_;
+
+  // Map browser ID to browser object for non-popup browsers. Only accessed on
+  // the UI thread.
+  BrowserMap browser_map_;
+
+  // Values for the first created browser. Modified on the UI thread but can be
+  // accessed on any thread.
+  int first_browser_id_;
+  CefRefPtr<CefBrowser> first_browser_;
+
+  // Map of resources that can be automatically loaded. Only accessed on the
+  // IO thread.
+  typedef std::map<std::string, ResourceContent> ResourceMap;
+  ResourceMap resource_map_;
+
+  // If true test completion will be signaled when all browsers have closed.
+  bool signal_completion_when_all_browsers_close_;
+
+  CefRefPtr<CefWaitableEvent> destroy_event_;
+
+  // Tracks whether DestroyTest() is expected or has been called.
+  bool destroy_test_expected_;
+  bool destroy_test_called_;
+
+  scoped_ptr<UIThreadHelper> ui_thread_helper_;
+
+  // Used to track the number of currently existing browser windows.
+  static int browser_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestHandler);
+};
+
+// Release |handler| and wait for the destructor to be called.
+// This function is used to avoid test state leakage and to verify that
+// all Handler references have been released on test completion.
+template <typename T>
+void ReleaseAndWaitForDestructor(CefRefPtr<T>& handler, int delay_ms = 2000) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  handler->SetDestroyEvent(event);
+  T* _handler_ptr = handler.get();
+  handler = nullptr;
+  bool handler_destructed = event->TimedWait(delay_ms);
+  EXPECT_TRUE(handler_destructed);
+  if (!handler_destructed) {
+    // |event| is a stack variable so clear the reference before returning.
+    _handler_ptr->SetDestroyEvent(nullptr);
+  }
+}
+
+// Returns true if the currently running test has failed.
+bool TestFailed();
+
+// Helper macros for executing checks in a method with a boolean return value.
+// For example:
+//
+// bool VerifyVals(bool a, bool b) {
+//   V_DECLARE();
+//   V_EXPECT_TRUE(a);
+//   V_EXPECT_FALSE(b);
+//   V_RETURN();
+// }
+//
+// EXPECT_TRUE(VerifyVals(true, false));
+
+#define V_DECLARE()     \
+  bool __verify = true; \
+  bool __result
+
+#define V_RETURN() return __verify
+
+#define V_EXPECT_TRUE(condition)                         \
+  __result = !!(condition);                              \
+  __verify &= __result;                                  \
+  GTEST_TEST_BOOLEAN_(__result, #condition, false, true, \
+                      GTEST_NONFATAL_FAILURE_)
+
+#define V_EXPECT_FALSE(condition)                           \
+  __result = !!(condition);                                 \
+  __verify &= !__result;                                    \
+  GTEST_TEST_BOOLEAN_(!(__result), #condition, true, false, \
+                      GTEST_NONFATAL_FAILURE_)
+
+#endif  // CEF_TESTS_UNITTESTS_TEST_HANDLER_H_
diff --git a/src/tests/ceftests/test_suite.cc b/src/tests/ceftests/test_suite.cc
new file mode 100644
index 0000000..d96eefd
--- /dev/null
+++ b/src/tests/ceftests/test_suite.cc
@@ -0,0 +1,204 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Postions copyright
+// 2012 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "tests/ceftests/test_suite.h"
+
+#include "include/cef_file_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace {
+
+CefTestSuite* g_test_suite = nullptr;
+
+#if defined(OS_WIN)
+
+// From base/process/launch_win.cc.
+void RouteStdioToConsole(bool create_console_if_not_found) {
+  // Don't change anything if stdout or stderr already point to a
+  // valid stream.
+  //
+  // If we are running under Buildbot or under Cygwin's default
+  // terminal (mintty), stderr and stderr will be pipe handles.  In
+  // that case, we don't want to open CONOUT$, because its output
+  // likely does not go anywhere.
+  //
+  // We don't use GetStdHandle() to check stdout/stderr here because
+  // it can return dangling IDs of handles that were never inherited
+  // by this process.  These IDs could have been reused by the time
+  // this function is called.  The CRT checks the validity of
+  // stdout/stderr on startup (before the handle IDs can be reused).
+  // _fileno(stdout) will return -2 (_NO_CONSOLE_FILENO) if stdout was
+  // invalid.
+  if (_fileno(stdout) >= 0 || _fileno(stderr) >= 0) {
+    return;
+  }
+
+  if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+    unsigned int result = GetLastError();
+    // Was probably already attached.
+    if (result == ERROR_ACCESS_DENIED)
+      return;
+    // Don't bother creating a new console for each child process if the
+    // parent process is invalid (eg: crashed).
+    if (result == ERROR_GEN_FAILURE)
+      return;
+    if (create_console_if_not_found) {
+      // Make a new console if attaching to parent fails with any other error.
+      // It should be ERROR_INVALID_HANDLE at this point, which means the
+      // browser was likely not started from a console.
+      AllocConsole();
+    } else {
+      return;
+    }
+  }
+
+  // Arbitrary byte count to use when buffering output lines.  More
+  // means potential waste, less means more risk of interleaved
+  // log-lines in output.
+  enum { kOutputBufferSize = 64 * 1024 };
+
+  if (freopen("CONOUT$", "w", stdout)) {
+    setvbuf(stdout, nullptr, _IOLBF, kOutputBufferSize);
+    // Overwrite FD 1 for the benefit of any code that uses this FD
+    // directly.  This is safe because the CRT allocates FDs 0, 1 and
+    // 2 at startup even if they don't have valid underlying Windows
+    // handles.  This means we won't be overwriting an FD created by
+    // _open() after startup.
+    _dup2(_fileno(stdout), 1);
+  }
+  if (freopen("CONOUT$", "w", stderr)) {
+    setvbuf(stderr, nullptr, _IOLBF, kOutputBufferSize);
+    _dup2(_fileno(stderr), 2);
+  }
+
+  // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
+  std::ios::sync_with_stdio();
+}
+
+#endif  // defined(OS_WIN)
+
+}  // namespace
+
+CefTestSuite::CefTestSuite(int argc, char** argv)
+    : argc_(argc), argv_(argc, argv), retval_(0) {
+  g_test_suite = this;
+
+  // Keep a representation of the original command-line.
+  command_line_ = CefCommandLine::CreateCommandLine();
+#if defined(OS_WIN)
+  command_line_->InitFromString(::GetCommandLineW());
+#else
+  command_line_->InitFromArgv(argc, argv);
+#endif
+}
+
+CefTestSuite::~CefTestSuite() {
+  g_test_suite = nullptr;
+}
+
+// static
+CefTestSuite* CefTestSuite::GetInstance() {
+  return g_test_suite;
+}
+
+void CefTestSuite::InitMainProcess() {
+  PreInitialize();
+
+  // This will modify |argc_| and |argv_|.
+  testing::InitGoogleTest(&argc_, argv_.array());
+}
+
+// Don't add additional code to this method. Instead add it to Initialize().
+int CefTestSuite::Run() {
+  Initialize();
+  retval_ = RUN_ALL_TESTS();
+  Shutdown();
+  return retval_;
+}
+
+void CefTestSuite::GetSettings(CefSettings& settings) const {
+#if (defined(OS_WIN) || defined(OS_LINUX))
+  settings.multi_threaded_message_loop =
+      command_line_->HasSwitch(client::switches::kMultiThreadedMessageLoop);
+#endif
+
+  if (!settings.multi_threaded_message_loop) {
+    settings.external_message_pump =
+        command_line_->HasSwitch(client::switches::kExternalMessagePump);
+  }
+
+  CefString(&settings.cache_path) =
+      command_line_->GetSwitchValue(client::switches::kCachePath);
+
+  // Always expose the V8 gc() function to give tests finer-grained control over
+  // memory management.
+  std::string javascript_flags = "--expose-gc";
+  // Value of kJavascriptFlags switch.
+  std::string other_javascript_flags =
+      command_line_->GetSwitchValue("js-flags");
+  if (!other_javascript_flags.empty())
+    javascript_flags += " " + other_javascript_flags;
+  CefString(&settings.javascript_flags) = javascript_flags;
+
+  // Necessary for V8Test.OnUncaughtException tests.
+  settings.uncaught_exception_stack_size = 10;
+
+  // Necessary for the OSRTest tests.
+  settings.windowless_rendering_enabled = true;
+
+  // For Accept-Language test
+  CefString(&settings.accept_language_list) = CEF_SETTINGS_ACCEPT_LANGUAGE;
+}
+
+// static
+bool CefTestSuite::GetCachePath(std::string& path) const {
+  if (command_line_->HasSwitch(client::switches::kCachePath)) {
+    // Set the cache_path value.
+    path = command_line_->GetSwitchValue(client::switches::kCachePath);
+    return true;
+  }
+
+  return false;
+}
+
+void CefTestSuite::RegisterTempDirectory(const CefString& directory) {
+  base::AutoLock lock_scope(temp_directories_lock_);
+  temp_directories_.push_back(directory);
+}
+
+void CefTestSuite::DeleteTempDirectories() {
+  base::AutoLock lock_scope(temp_directories_lock_);
+  for (size_t i = 0U; i < temp_directories_.size(); ++i) {
+    CefDeleteFile(temp_directories_[i], true);
+  }
+  temp_directories_.clear();
+}
+
+void CefTestSuite::PreInitialize() {
+#if defined(OS_WIN)
+  testing::GTEST_FLAG(catch_exceptions) = false;
+
+  // Enable termination on heap corruption.
+  // Ignore the result code. Supported starting with XP SP3 and Vista.
+  HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
+#endif
+
+#if defined(OS_LINUX) && defined(USE_AURA)
+  // When calling native char conversion functions (e.g wrctomb) we need to
+  // have the locale set. In the absence of such a call the "C" locale is the
+  // default. In the gtk code (below) gtk_init() implicitly sets a locale.
+  setlocale(LC_ALL, "");
+#endif  // defined(OS_LINUX) && defined(USE_AURA)
+
+  // Don't add additional code to this function. Instead add it to Initialize().
+}
+
+void CefTestSuite::Initialize() {
+#if defined(OS_WIN)
+  RouteStdioToConsole(true);
+#endif  // defined(OS_WIN)
+}
+
+void CefTestSuite::Shutdown() {}
diff --git a/src/tests/ceftests/test_suite.h b/src/tests/ceftests/test_suite.h
new file mode 100644
index 0000000..77ce333
--- /dev/null
+++ b/src/tests/ceftests/test_suite.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Postions copyright
+// 2012 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_TEST_SUITE_H_
+#define CEF_TESTS_UNITTESTS_TEST_SUITE_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "include/cef_command_line.h"
+#include "include/wrapper/cef_helpers.h"
+
+// A single instance of this object will be created by main() in
+// run_all_unittests.cc.
+class CefTestSuite {
+ public:
+  CefTestSuite(int argc, char** argv);
+  ~CefTestSuite();
+
+  static CefTestSuite* GetInstance();
+
+  void InitMainProcess();
+  int Run();
+
+  void GetSettings(CefSettings& settings) const;
+  bool GetCachePath(std::string& path) const;
+
+  // Register a temp directory that should be deleted on shutdown.
+  void RegisterTempDirectory(const CefString& directory);
+
+  // Called after shutdown to delete any registered temp directories.
+  void DeleteTempDirectories();
+
+  CefRefPtr<CefCommandLine> command_line() const { return command_line_; }
+
+  // The return value from Run().
+  int retval() const { return retval_; }
+
+ private:
+  void PreInitialize();
+  void Initialize();
+  void Shutdown();
+
+  int argc_;
+  CefScopedArgArray argv_;
+  CefRefPtr<CefCommandLine> command_line_;
+
+  std::vector<CefString> temp_directories_;
+  base::Lock temp_directories_lock_;
+
+  int retval_;
+};
+
+#define CEF_SETTINGS_ACCEPT_LANGUAGE "en-GB"
+
+#endif  // CEF_TESTS_UNITTESTS_TEST_SUITE_H_
diff --git a/src/tests/ceftests/test_util.cc b/src/tests/ceftests/test_util.cc
new file mode 100644
index 0000000..5058b1f
--- /dev/null
+++ b/src/tests/ceftests/test_util.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/test_util.h"
+#include "include/cef_command_line.h"
+#include "include/cef_request_context_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+void TestMapEqual(const CefRequest::HeaderMap& map1,
+                  const CefRequest::HeaderMap& map2,
+                  bool allowExtras) {
+  if (!allowExtras) {
+    EXPECT_EQ(map1.size(), map2.size());
+  }
+
+  TestMapNoDuplicates(map1);
+  TestMapNoDuplicates(map2);
+
+  CefRequest::HeaderMap::const_iterator it1, it2;
+
+  for (it1 = map1.begin(); it1 != map1.end(); ++it1) {
+    bool found = false;
+    std::string name1 = it1->first;
+    std::transform(name1.begin(), name1.end(), name1.begin(), ::tolower);
+    for (it2 = map2.begin(); it2 != map2.end(); ++it2) {
+      std::string name2 = it2->first;
+      std::transform(name2.begin(), name2.end(), name2.begin(), ::tolower);
+      if (name1 == name2 && it1->second == it2->second) {
+        found = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(found) << "No entry for " << it1->first.ToString() << ": "
+                       << it1->second.ToString();
+  }
+}
+
+void TestMapNoDuplicates(const CefRequest::HeaderMap& map) {
+  CefRequest::HeaderMap::const_iterator it1 = map.begin();
+  for (; it1 != map.end(); ++it1) {
+    CefRequest::HeaderMap::const_iterator it2 = it1;
+    for (++it2; it2 != map.end(); ++it2) {
+      EXPECT_FALSE(it1->first == it2->first && it1->second == it2->second)
+          << "Duplicate entry for " << it1->first.ToString() << ": "
+          << it1->second.ToString();
+    }
+  }
+}
+
+void TestPostDataElementEqual(CefRefPtr<CefPostDataElement> elem1,
+                              CefRefPtr<CefPostDataElement> elem2) {
+  EXPECT_TRUE(elem1.get());
+  EXPECT_TRUE(elem2.get());
+
+  EXPECT_EQ(elem1->GetType(), elem2->GetType());
+  switch (elem1->GetType()) {
+    case PDE_TYPE_BYTES: {
+      EXPECT_EQ(elem1->GetBytesCount(), elem2->GetBytesCount());
+      size_t bytesCt = elem1->GetBytesCount();
+      char* buff1 = new char[bytesCt];
+      char* buff2 = new char[bytesCt];
+      elem1->GetBytes(bytesCt, buff1);
+      elem2->GetBytes(bytesCt, buff2);
+      EXPECT_TRUE(!memcmp(buff1, buff2, bytesCt));
+      delete[] buff1;
+      delete[] buff2;
+    } break;
+    case PDE_TYPE_FILE:
+      EXPECT_EQ(elem1->GetFile(), elem2->GetFile());
+      break;
+    default:
+      break;
+  }
+}
+
+void TestPostDataEqual(CefRefPtr<CefPostData> postData1,
+                       CefRefPtr<CefPostData> postData2) {
+  EXPECT_TRUE(postData1.get());
+  EXPECT_TRUE(postData2.get());
+
+  EXPECT_EQ(postData1->GetElementCount(), postData2->GetElementCount());
+
+  CefPostData::ElementVector ev1, ev2;
+  postData1->GetElements(ev1);
+  postData1->GetElements(ev2);
+  ASSERT_EQ(ev1.size(), ev2.size());
+
+  CefPostData::ElementVector::const_iterator it1 = ev1.begin();
+  CefPostData::ElementVector::const_iterator it2 = ev2.begin();
+  for (; it1 != ev1.end() && it2 != ev2.end(); ++it1, ++it2)
+    TestPostDataElementEqual((*it1), (*it2));
+}
+
+void TestRequestEqual(CefRefPtr<CefRequest> request1,
+                      CefRefPtr<CefRequest> request2,
+                      bool allowExtras) {
+  EXPECT_TRUE(request1.get());
+  EXPECT_TRUE(request2.get());
+
+  EXPECT_STREQ(request1->GetURL().ToString().c_str(),
+               request2->GetURL().ToString().c_str());
+  EXPECT_STREQ(request1->GetMethod().ToString().c_str(),
+               request2->GetMethod().ToString().c_str());
+
+  EXPECT_STREQ(request1->GetReferrerURL().ToString().c_str(),
+               request2->GetReferrerURL().ToString().c_str());
+  EXPECT_EQ(request1->GetReferrerPolicy(), request2->GetReferrerPolicy());
+
+  CefRequest::HeaderMap headers1, headers2;
+  request1->GetHeaderMap(headers1);
+  request2->GetHeaderMap(headers2);
+  TestMapEqual(headers1, headers2, allowExtras);
+
+  CefRefPtr<CefPostData> postData1 = request1->GetPostData();
+  CefRefPtr<CefPostData> postData2 = request2->GetPostData();
+  EXPECT_EQ(!!(postData1.get()), !!(postData2.get()));
+  if (postData1.get() && postData2.get())
+    TestPostDataEqual(postData1, postData2);
+}
+
+void TestResponseEqual(CefRefPtr<CefResponse> response1,
+                       CefRefPtr<CefResponse> response2,
+                       bool allowExtras) {
+  EXPECT_TRUE(response1.get());
+  EXPECT_TRUE(response2.get());
+
+  EXPECT_EQ(response1->GetStatus(), response2->GetStatus());
+  EXPECT_STREQ(response1->GetStatusText().ToString().c_str(),
+               response2->GetStatusText().ToString().c_str());
+  EXPECT_STREQ(response1->GetMimeType().ToString().c_str(),
+               response2->GetMimeType().ToString().c_str());
+
+  CefRequest::HeaderMap headers1, headers2;
+  response1->GetHeaderMap(headers1);
+  response2->GetHeaderMap(headers2);
+  TestMapEqual(headers1, headers2, allowExtras);
+}
+
+void TestBinaryEqual(CefRefPtr<CefBinaryValue> val1,
+                     CefRefPtr<CefBinaryValue> val2) {
+  EXPECT_TRUE(val1.get());
+  EXPECT_TRUE(val2.get());
+
+  EXPECT_TRUE(val1->IsEqual(val2));
+  EXPECT_TRUE(val2->IsEqual(val1));
+
+  size_t data_size = val1->GetSize();
+  EXPECT_EQ(data_size, val2->GetSize());
+
+  EXPECT_GT(data_size, (size_t)0);
+
+  char* data1 = new char[data_size + 1];
+  char* data2 = new char[data_size + 1];
+
+  EXPECT_EQ(data_size, val1->GetData(data1, data_size, 0));
+  data1[data_size] = 0;
+  EXPECT_EQ(data_size, val2->GetData(data2, data_size, 0));
+  data2[data_size] = 0;
+
+  EXPECT_STREQ(data1, data2);
+
+  delete[] data1;
+  delete[] data2;
+}
+
+void TestDictionaryEqual(CefRefPtr<CefDictionaryValue> val1,
+                         CefRefPtr<CefDictionaryValue> val2) {
+  EXPECT_TRUE(val1.get());
+  EXPECT_TRUE(val2.get());
+
+  EXPECT_TRUE(val1->IsEqual(val2));
+  EXPECT_TRUE(val2->IsEqual(val1));
+
+  EXPECT_EQ(val1->GetSize(), val2->GetSize());
+
+  CefDictionaryValue::KeyList keys;
+  EXPECT_TRUE(val1->GetKeys(keys));
+
+  CefDictionaryValue::KeyList::const_iterator it = keys.begin();
+  for (; it != keys.end(); ++it) {
+    CefString key = *it;
+    EXPECT_TRUE(val2->HasKey(key));
+    CefValueType type = val1->GetType(key);
+    EXPECT_EQ(type, val2->GetType(key));
+    switch (type) {
+      case VTYPE_INVALID:
+      case VTYPE_NULL:
+        break;
+      case VTYPE_BOOL:
+        EXPECT_EQ(val1->GetBool(key), val2->GetBool(key));
+        break;
+      case VTYPE_INT:
+        EXPECT_EQ(val1->GetInt(key), val2->GetInt(key));
+        break;
+      case VTYPE_DOUBLE:
+        EXPECT_EQ(val1->GetDouble(key), val2->GetDouble(key));
+        break;
+      case VTYPE_STRING:
+        EXPECT_EQ(val1->GetString(key), val2->GetString(key));
+        break;
+      case VTYPE_BINARY:
+        TestBinaryEqual(val1->GetBinary(key), val2->GetBinary(key));
+        break;
+      case VTYPE_DICTIONARY:
+        TestDictionaryEqual(val1->GetDictionary(key), val2->GetDictionary(key));
+        break;
+      case VTYPE_LIST:
+        TestListEqual(val1->GetList(key), val2->GetList(key));
+        break;
+    }
+  }
+}
+
+void TestListEqual(CefRefPtr<CefListValue> val1, CefRefPtr<CefListValue> val2) {
+  EXPECT_TRUE(val1.get());
+  EXPECT_TRUE(val2.get());
+
+  EXPECT_TRUE(val1->IsEqual(val2));
+  EXPECT_TRUE(val2->IsEqual(val1));
+
+  size_t size = val1->GetSize();
+  EXPECT_EQ(size, val2->GetSize());
+
+  for (size_t i = 0; i < size; ++i) {
+    CefValueType type = val1->GetType(i);
+    EXPECT_EQ(type, val2->GetType(i));
+    switch (type) {
+      case VTYPE_INVALID:
+      case VTYPE_NULL:
+        break;
+      case VTYPE_BOOL:
+        EXPECT_EQ(val1->GetBool(i), val2->GetBool(i));
+        break;
+      case VTYPE_INT:
+        EXPECT_EQ(val1->GetInt(i), val2->GetInt(i));
+        break;
+      case VTYPE_DOUBLE:
+        EXPECT_EQ(val1->GetDouble(i), val2->GetDouble(i));
+        break;
+      case VTYPE_STRING:
+        EXPECT_EQ(val1->GetString(i), val2->GetString(i));
+        break;
+      case VTYPE_BINARY:
+        TestBinaryEqual(val1->GetBinary(i), val2->GetBinary(i));
+        break;
+      case VTYPE_DICTIONARY:
+        TestDictionaryEqual(val1->GetDictionary(i), val2->GetDictionary(i));
+        break;
+      case VTYPE_LIST:
+        TestListEqual(val1->GetList(i), val2->GetList(i));
+        break;
+    }
+  }
+}
+
+void TestProcessMessageEqual(CefRefPtr<CefProcessMessage> val1,
+                             CefRefPtr<CefProcessMessage> val2) {
+  EXPECT_TRUE(val1.get());
+  EXPECT_TRUE(val2.get());
+  EXPECT_EQ(val1->GetName(), val2->GetName());
+
+  TestListEqual(val1->GetArgumentList(), val2->GetArgumentList());
+}
+
+void TestStringVectorEqual(const std::vector<CefString>& val1,
+                           const std::vector<CefString>& val2) {
+  EXPECT_EQ(val1.size(), val2.size());
+
+  for (size_t i = 0; i < val1.size(); ++i)
+    EXPECT_STREQ(val1[i].ToString().c_str(), val2[i].ToString().c_str());
+}
+
+bool TestOldResourceAPI() {
+  static int state = -1;
+  if (state == -1) {
+    CefRefPtr<CefCommandLine> command_line =
+        CefCommandLine::GetGlobalCommandLine();
+    state = command_line->HasSwitch("test-old-resource-api") ? 1 : 0;
+  }
+  return state ? true : false;
+}
+
+CefRefPtr<CefRequestContext> CreateTestRequestContext(
+    TestRequestContextMode mode,
+    const std::string& cache_path) {
+  EXPECT_TRUE(cache_path.empty() || mode == TEST_RC_MODE_CUSTOM ||
+              mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER);
+
+  if (mode == TEST_RC_MODE_NONE)
+    return nullptr;
+  if (mode == TEST_RC_MODE_GLOBAL)
+    return CefRequestContext::GetGlobalContext();
+
+  CefRefPtr<CefRequestContextHandler> rc_handler;
+  if (mode == TEST_RC_MODE_GLOBAL_WITH_HANDLER ||
+      mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER) {
+    class Handler : public CefRequestContextHandler {
+     public:
+      Handler() {}
+
+     private:
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+    rc_handler = new Handler();
+  }
+
+  if (mode == TEST_RC_MODE_CUSTOM || mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER) {
+    CefRequestContextSettings settings;
+    if (!cache_path.empty())
+      CefString(&settings.cache_path) = cache_path;
+    return CefRequestContext::CreateContext(settings, rc_handler);
+  }
+
+  EXPECT_EQ(mode, TEST_RC_MODE_GLOBAL_WITH_HANDLER);
+  return CefRequestContext::CreateContext(CefRequestContext::GetGlobalContext(),
+                                          rc_handler);
+}
diff --git a/src/tests/ceftests/test_util.h b/src/tests/ceftests/test_util.h
new file mode 100644
index 0000000..7f0fe4a
--- /dev/null
+++ b/src/tests/ceftests/test_util.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_TEST_UTIL_H_
+#define CEF_TESTS_UNITTESTS_TEST_UTIL_H_
+#pragma once
+
+#include "include/cef_process_message.h"
+#include "include/cef_request.h"
+#include "include/cef_request_context.h"
+#include "include/cef_response.h"
+#include "include/cef_values.h"
+#include "tests/ceftests/test_suite.h"
+
+// Test that CefRequest::HeaderMap objects are equal. Multiple values with the
+// same key are allowed, but not duplicate entries with the same key/value. If
+// |allowExtras| is true then additional header fields will be allowed in
+// |map2|.
+void TestMapEqual(const CefRequest::HeaderMap& map1,
+                  const CefRequest::HeaderMap& map2,
+                  bool allowExtras);
+
+// Test that the CefRequest::HeaderMap object contains no duplicate entries.
+void TestMapNoDuplicates(const CefRequest::HeaderMap& map);
+
+// Test that CefPostDataElement objects are equal
+void TestPostDataElementEqual(CefRefPtr<CefPostDataElement> elem1,
+                              CefRefPtr<CefPostDataElement> elem2);
+
+// Test that CefPostData objects are equal
+void TestPostDataEqual(CefRefPtr<CefPostData> postData1,
+                       CefRefPtr<CefPostData> postData2);
+
+// Test that CefRequest objects are equal
+// If |allowExtras| is true then additional header fields will be allowed in
+// |request2|.
+void TestRequestEqual(CefRefPtr<CefRequest> request1,
+                      CefRefPtr<CefRequest> request2,
+                      bool allowExtras);
+
+// Test that CefResponse objects are equal
+// If |allowExtras| is true then additional header fields will be allowed in
+// |response2|.
+void TestResponseEqual(CefRefPtr<CefResponse> response1,
+                       CefRefPtr<CefResponse> response2,
+                       bool allowExtras);
+
+// Test if two binary values are equal.
+void TestBinaryEqual(CefRefPtr<CefBinaryValue> val1,
+                     CefRefPtr<CefBinaryValue> val2);
+
+// Test if two list values are equal.
+void TestListEqual(CefRefPtr<CefListValue> val1, CefRefPtr<CefListValue> val2);
+
+// Test if two dictionary values are equal.
+void TestDictionaryEqual(CefRefPtr<CefDictionaryValue> val1,
+                         CefRefPtr<CefDictionaryValue> val2);
+
+// Test if two process message values are equal.
+void TestProcessMessageEqual(CefRefPtr<CefProcessMessage> val1,
+                             CefRefPtr<CefProcessMessage> val2);
+
+// Test if two CefString vectors are equal.
+void TestStringVectorEqual(const std::vector<CefString>& val1,
+                           const std::vector<CefString>& val2);
+
+enum TestRequestContextMode {
+  TEST_RC_MODE_NONE,
+  TEST_RC_MODE_GLOBAL,
+  TEST_RC_MODE_GLOBAL_WITH_HANDLER,
+  TEST_RC_MODE_CUSTOM,
+  TEST_RC_MODE_CUSTOM_WITH_HANDLER,
+};
+
+inline bool IsTestRequestContextModeCustom(TestRequestContextMode mode) {
+  return mode == TEST_RC_MODE_CUSTOM ||
+         mode == TEST_RC_MODE_CUSTOM_WITH_HANDLER;
+}
+
+// Returns true if the old CefResourceHandler API should be tested.
+bool TestOldResourceAPI();
+
+// Return a RequestContext object matching the specified |mode|.
+// |cache_path| may be specified for CUSTOM modes.
+// Use the RC_TEST_GROUP_BASE macro to test all valid combinations.
+CefRefPtr<CefRequestContext> CreateTestRequestContext(
+    TestRequestContextMode mode,
+    const std::string& cache_path);
+
+// Helper macro for testing a single RequestContextMode value.
+// See RC_TEST_GROUP_ALL documentation for example usage.
+#define RC_TEST_BASE(test_case_name, test_name, test_class, test_mode, \
+                     rc_mode, with_cache_path)                         \
+  TEST(test_case_name, test_name) {                                    \
+    CefScopedTempDir scoped_temp_dir;                                  \
+    std::string cache_path;                                            \
+    if (with_cache_path) {                                             \
+      EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());              \
+      cache_path = scoped_temp_dir.GetPath();                          \
+    }                                                                  \
+    CefRefPtr<test_class> handler =                                    \
+        new test_class(test_class::test_mode, rc_mode, cache_path);    \
+    handler->ExecuteTest();                                            \
+    ReleaseAndWaitForDestructor(handler);                              \
+    if (!scoped_temp_dir.IsEmpty()) {                                  \
+      CefTestSuite::GetInstance()->RegisterTempDirectory(              \
+          scoped_temp_dir.Take());                                     \
+    }                                                                  \
+  }
+
+// RequestContextModes that operate in memory.
+#define RC_TEST_GROUP_IN_MEMORY(test_case_name, test_name, test_class,     \
+                                test_mode)                                 \
+  RC_TEST_BASE(test_case_name, test_name##RCNone, test_class, test_mode,   \
+               TEST_RC_MODE_NONE, false)                                   \
+  RC_TEST_BASE(test_case_name, test_name##RCGlobal, test_class, test_mode, \
+               TEST_RC_MODE_GLOBAL, false)                                 \
+  RC_TEST_BASE(test_case_name, test_name##RCGlobalWithHandler, test_class, \
+               test_mode, TEST_RC_MODE_GLOBAL_WITH_HANDLER, false)         \
+  RC_TEST_BASE(test_case_name, test_name##RCCustomInMemory, test_class,    \
+               test_mode, TEST_RC_MODE_CUSTOM, false)                      \
+  RC_TEST_BASE(test_case_name, test_name##RCCustomInMemoryWithHandler,     \
+               test_class, test_mode, TEST_RC_MODE_CUSTOM_WITH_HANDLER, false)
+
+// RequestContextModes that operate on disk.
+#define RC_TEST_GROUP_ON_DISK(test_case_name, test_name, test_class,  \
+                              test_mode)                              \
+  RC_TEST_BASE(test_case_name, test_name##RCCustomOnDisk, test_class, \
+               test_mode, TEST_RC_MODE_CUSTOM, true)                  \
+  RC_TEST_BASE(test_case_name, test_name##RCCustomOnDiskWithHandler,  \
+               test_class, test_mode, TEST_RC_MODE_CUSTOM_WITH_HANDLER, true)
+
+// Helper macro for testing all valid combinations of RequestContextMode values.
+// For example:
+//
+//   // Test handler implementation.
+//   class MyTestHandler : public TestHandler {
+//    public:
+//     // Test modes supported by MyTestHandler.
+//     enum TestMode {
+//       FIRST,
+//       SECOND,
+//     };
+//
+//     // Constructor always accepts three arguments.
+//     MyTestHandler(TestMode test_mode,
+//                   TestRequestContextMode rc_mode,
+//                   const std::string& rc_cache_path)
+//         : test_mode_(test_mode), rc_mode_(rc_mode),
+//           rc_cache_path_(rc_cache_path) {}
+//
+//     void RunTest() override {
+//        // Create a RequestContext with the specified attributes.
+//        CefRefPtr<CefRequestContext> request_context =
+//            CreateTestRequestContext(rc_mode_, rc_cache_path_);
+//
+//        // Do something with |test_mode_| and |request_context|...
+//     }
+//
+//    private:
+//     const TestMode test_mode_;
+//     const TestRequestContextMode rc_mode_;
+//     const std::string rc_cache_path_;
+//
+//     IMPLEMENT_REFCOUNTING(MyTestHandler);
+//   };
+//
+//   // Helper macro for defining tests using MyTestHandler.
+//   #define MY_TEST_GROUP(test_name, test_mode) \
+//     RC_TEST_GROUP_ALL(MyTest, test_name, MyTestHandler, test_mode)
+//
+//   // Implementation for MyTest.First* tests.
+//   MY_TEST_GROUP(First, FIRST);
+//   // Implementation for MyTest.Second* tests.
+//   MY_TEST_GROUP(Second, SECOND);
+//
+#define RC_TEST_GROUP_ALL(test_case_name, test_name, test_class, test_mode) \
+  RC_TEST_GROUP_IN_MEMORY(test_case_name, test_name, test_class, test_mode) \
+  RC_TEST_GROUP_ON_DISK(test_case_name, test_name, test_class, test_mode)
+
+#endif  // CEF_TESTS_UNITTESTS_TEST_UTIL_H_
diff --git a/src/tests/ceftests/thread_helper.cc b/src/tests/ceftests/thread_helper.cc
new file mode 100644
index 0000000..dffac1b
--- /dev/null
+++ b/src/tests/ceftests/thread_helper.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/thread_helper.h"
+
+#include "include/wrapper/cef_closure_task.h"
+
+void SignalEvent(CefRefPtr<CefWaitableEvent> event) {
+  event->Signal();
+}
+
+void WaitForThread(CefThreadId thread_id, int64 delay_ms) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  CefPostDelayedTask(thread_id, base::Bind(SignalEvent, event), delay_ms);
+  event->Wait();
+}
+
+void WaitForThread(CefRefPtr<CefTaskRunner> task_runner, int64 delay_ms) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+  task_runner->PostDelayedTask(
+      CefCreateClosureTask(base::Bind(SignalEvent, event)), delay_ms);
+  event->Wait();
+}
+
+void RunOnThread(CefThreadId thread_id,
+                 const base::Callback<void(void)>& test_impl,
+                 CefRefPtr<CefWaitableEvent> event) {
+  if (!CefCurrentlyOn(thread_id)) {
+    CefPostTask(thread_id,
+                base::Bind(RunOnThread, thread_id, test_impl, event));
+    return;
+  }
+
+  test_impl.Run();
+  SignalEvent(event);
+}
+
+void RunOnThreadAsync(
+    CefThreadId thread_id,
+    const base::Callback<void(CefRefPtr<CefWaitableEvent>)>& test_impl,
+    CefRefPtr<CefWaitableEvent> event) {
+  if (!CefCurrentlyOn(thread_id)) {
+    CefPostTask(thread_id,
+                base::Bind(RunOnThreadAsync, thread_id, test_impl, event));
+    return;
+  }
+
+  test_impl.Run(event);
+}
diff --git a/src/tests/ceftests/thread_helper.h b/src/tests/ceftests/thread_helper.h
new file mode 100644
index 0000000..abc332b
--- /dev/null
+++ b/src/tests/ceftests/thread_helper.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_UNITTESTS_THREAD_HELPER_H_
+#define CEF_TESTS_UNITTESTS_THREAD_HELPER_H_
+#pragma once
+
+#include "include/base/cef_bind.h"
+#include "include/cef_task.h"
+#include "include/cef_waitable_event.h"
+
+// Helper for signaling |event|.
+void SignalEvent(CefRefPtr<CefWaitableEvent> event);
+
+// Post a task to the specified thread and wait for the task to execute as
+// indication that all previously pending tasks on that thread have completed.
+void WaitForThread(CefThreadId thread_id, int64 delay_ms = 0);
+void WaitForThread(CefRefPtr<CefTaskRunner> task_runner, int64 delay_ms = 0);
+
+#define WaitForIOThread() WaitForThread(TID_IO)
+#define WaitForUIThread() WaitForThread(TID_UI)
+#define WaitForFILEThread() WaitForThread(TID_FILE)
+#define WaitForIOThreadWithDelay(delay_ms) WaitForThread(TID_IO, delay_ms)
+#define WaitForUIThreadWithDelay(delay_ms) WaitForThread(TID_UI, delay_ms)
+#define WaitForFILEThreadWithDelay(delay_ms) WaitForThread(TID_FILE, delay_ms)
+
+// Assert that execution is occuring on the named thread.
+#define EXPECT_UI_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+#define EXPECT_IO_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_IO));
+#define EXPECT_FILE_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_FILE));
+#define EXPECT_RENDERER_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
+
+// Executes |test_impl| on the specified |thread_id|. |event| will be signaled
+// once execution is complete.
+void RunOnThread(CefThreadId thread_id,
+                 const base::Callback<void(void)>& test_impl,
+                 CefRefPtr<CefWaitableEvent> event);
+
+#define NAMED_THREAD_TEST(thread_id, test_case_name, test_name) \
+  TEST(test_case_name, test_name) {                             \
+    CefRefPtr<CefWaitableEvent> event =                         \
+        CefWaitableEvent::CreateWaitableEvent(true, false);     \
+    RunOnThread(thread_id, base::Bind(test_name##Impl), event); \
+    event->Wait();                                              \
+  }
+
+// Execute "test_case_name.test_name" test on the named thread. The test
+// implementation is "void test_nameImpl()".
+#define UI_THREAD_TEST(test_case_name, test_name) \
+  NAMED_THREAD_TEST(TID_UI, test_case_name, test_name)
+
+// Like RunOnThread() but |test_impl| is responsible for signaling |event|.
+void RunOnThreadAsync(
+    CefThreadId thread_id,
+    const base::Callback<void(CefRefPtr<CefWaitableEvent>)>& test_impl,
+    CefRefPtr<CefWaitableEvent>);
+
+#define NAMED_THREAD_TEST_ASYNC(thread_id, test_case_name, test_name) \
+  TEST(test_case_name, test_name) {                                   \
+    CefRefPtr<CefWaitableEvent> event =                               \
+        CefWaitableEvent::CreateWaitableEvent(true, false);           \
+    RunOnThreadAsync(thread_id, base::Bind(test_name##Impl), event);  \
+    event->Wait();                                                    \
+  }
+
+// Execute "test_case_name.test_name" test on the named thread. The test
+// implementation is "void test_nameImpl(CefRefPtr<CefWaitableEvent> event)".
+#define UI_THREAD_TEST_ASYNC(test_case_name, test_name) \
+  NAMED_THREAD_TEST_ASYNC(TID_UI, test_case_name, test_name)
+
+#endif  // CEF_TESTS_UNITTESTS_THREAD_HELPER_H_
diff --git a/src/tests/ceftests/thread_unittest.cc b/src/tests/ceftests/thread_unittest.cc
new file mode 100644
index 0000000..db23909
--- /dev/null
+++ b/src/tests/ceftests/thread_unittest.cc
@@ -0,0 +1,438 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_task.h"
+#include "include/cef_thread.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppBrowser;
+using client::ClientAppRenderer;
+
+namespace {
+
+// Base class for creating and testing threads.
+class ThreadTest : public base::RefCountedThreadSafe<ThreadTest> {
+ public:
+  ThreadTest() {}
+  virtual ~ThreadTest() {}
+
+  // Create the test thread. Should only be called one time.
+  void CreateTestThread() {
+    EXPECT_TRUE(!thread_.get());
+
+    owner_task_runner_ = CefTaskRunner::GetForCurrentThread();
+    EXPECT_TRUE(owner_task_runner_.get());
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+
+    thread_ = CefThread::CreateThread("test_thread");
+    EXPECT_TRUE(thread_.get());
+    EXPECT_TRUE(thread_->IsRunning());
+
+    thread_id_ = thread_->GetPlatformThreadId();
+    EXPECT_NE(thread_id_, kInvalidPlatformThreadId);
+
+    thread_task_runner_ = thread_->GetTaskRunner();
+    EXPECT_TRUE(thread_task_runner_.get());
+
+    AssertOwnerThread();
+  }
+
+  // Destroy the test thread. Should only be called one time.
+  void DestroyTestThread() {
+    EXPECT_TRUE(thread_.get());
+    AssertOwnerThread();
+
+    EXPECT_TRUE(thread_->IsRunning());
+    thread_->Stop();
+    EXPECT_FALSE(thread_->IsRunning());
+
+    AssertOwnerThread();
+
+    thread_ = nullptr;
+  }
+
+  // Execute |test_task| on the test thread. After execution |callback| will be
+  // posted to |callback_task_runner|.
+  void PostOnTestThreadAndCallback(
+      const base::Closure& test_task,
+      CefRefPtr<CefTaskRunner> callback_task_runner,
+      const base::Closure& callback) {
+    EXPECT_TRUE(thread_.get());
+    thread_task_runner_->PostTask(CefCreateClosureTask(
+        base::Bind(&ThreadTest::ExecuteOnTestThread, this, test_task,
+                   callback_task_runner, callback)));
+  }
+
+  CefRefPtr<CefTaskRunner> owner_task_runner() const {
+    return owner_task_runner_;
+  }
+  CefRefPtr<CefTaskRunner> thread_task_runner() const {
+    return thread_task_runner_;
+  }
+
+  // Assert that we're running on the owner thread.
+  void AssertOwnerThread() {
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+    EXPECT_FALSE(thread_task_runner_->BelongsToCurrentThread());
+    EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
+    EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
+  }
+
+  // Assert that we're running on the test thread.
+  void AssertTestThread() {
+    EXPECT_FALSE(owner_task_runner_->BelongsToCurrentThread());
+    EXPECT_TRUE(thread_task_runner_->BelongsToCurrentThread());
+    EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
+    EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
+  }
+
+ private:
+  // Helper for PostOnTestThreadAndCallback().
+  void ExecuteOnTestThread(const base::Closure& test_task,
+                           CefRefPtr<CefTaskRunner> callback_task_runner,
+                           const base::Closure& callback) {
+    AssertTestThread();
+
+    test_task.Run();
+
+    callback_task_runner->PostTask(CefCreateClosureTask(callback));
+  }
+
+  CefRefPtr<CefTaskRunner> owner_task_runner_;
+
+  CefRefPtr<CefThread> thread_;
+  cef_platform_thread_id_t thread_id_;
+  CefRefPtr<CefTaskRunner> thread_task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadTest);
+};
+
+}  // namespace
+
+// Test thread creation and destruction without any task execution.
+TEST(ThreadTest, Create) {
+  scoped_refptr<ThreadTest> thread_test = new ThreadTest();
+  thread_test->CreateTestThread();
+  thread_test->DestroyTestThread();
+  thread_test = nullptr;
+}
+
+namespace {
+
+// Simple implementation of ThreadTest that creates a thread, executes tasks
+// on the thread, then destroys the thread after all tasks have completed.
+class SimpleThreadTest : public ThreadTest {
+ public:
+  SimpleThreadTest(size_t expected_task_count,
+                   const base::Closure& task_callback,
+                   const base::Closure& done_callback)
+      : expected_task_count_(expected_task_count),
+        task_callback_(task_callback),
+        done_callback_(done_callback),
+        got_task_count_(0U),
+        got_done_count_(0U) {}
+
+  void RunTest() {
+    // Create the test thread.
+    CreateTestThread();
+
+    for (size_t i = 0U; i < expected_task_count_; ++i) {
+      // Execute Task() on the test thread and then call Done() on this thread.
+      PostOnTestThreadAndCallback(base::Bind(&SimpleThreadTest::Task, this),
+                                  owner_task_runner(),
+                                  base::Bind(&SimpleThreadTest::Done, this));
+    }
+  }
+
+  void DestroyTest() {
+    EXPECT_EQ(expected_task_count_, got_task_count_);
+    EXPECT_EQ(expected_task_count_, got_done_count_);
+
+    // Destroy the test thread.
+    DestroyTestThread();
+  }
+
+ private:
+  void Task() {
+    AssertTestThread();
+    got_task_count_++;
+    if (!task_callback_.is_null())
+      task_callback_.Run();
+  }
+
+  void Done() {
+    AssertOwnerThread();
+    if (++got_done_count_ == expected_task_count_ && !done_callback_.is_null())
+      done_callback_.Run();
+  }
+
+  const size_t expected_task_count_;
+  base::Closure task_callback_;
+  base::Closure done_callback_;
+
+  size_t got_task_count_;
+  size_t got_done_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleThreadTest);
+};
+
+// Test creation/execution of threads in the browser process.
+
+const char kBrowserThreadTestHtml[] = "http://test.com/browserthread.html";
+
+// Browser side.
+class BrowserThreadTestHandler : public TestHandler {
+ public:
+  explicit BrowserThreadTestHandler(CefThreadId owner_thread_id)
+      : owner_thread_id_(owner_thread_id) {}
+
+  void RunTest() override {
+    AddResource(kBrowserThreadTestHtml, "<html><body>Test</body></html>",
+                "text/html");
+
+    CreateBrowser(kBrowserThreadTestHtml);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void RunThreadTestOnOwnerThread() {
+    if (!CefCurrentlyOn(owner_thread_id_)) {
+      // Run the test on the desired owner thread.
+      CefPostTask(
+          owner_thread_id_,
+          base::Bind(&BrowserThreadTestHandler::RunThreadTestOnOwnerThread,
+                     this));
+      return;
+    }
+
+    EXPECT_FALSE(thread_test_.get());
+    thread_test_ = new SimpleThreadTest(
+        3, base::Closure(),
+        base::Bind(&BrowserThreadTestHandler::DoneOnOwnerThread, this));
+    thread_test_->RunTest();
+  }
+
+  void DoneOnOwnerThread() {
+    // Let the call stack unwind before destroying |thread_test_|.
+    CefPostTask(
+        owner_thread_id_,
+        base::Bind(&BrowserThreadTestHandler::DestroyTestOnOwnerThread, this));
+  }
+
+  void DestroyTestOnOwnerThread() {
+    EXPECT_TRUE(CefCurrentlyOn(owner_thread_id_));
+
+    EXPECT_TRUE(thread_test_.get());
+    if (thread_test_) {
+      thread_test_->DestroyTest();
+      thread_test_ = nullptr;
+    }
+
+    got_test_done_.yes();
+
+    // Call DestroyTest() on the UI thread.
+    CefPostTask(TID_UI,
+                base::Bind(&BrowserThreadTestHandler::DestroyTest, this));
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (!isLoading)
+      RunThreadTestOnOwnerThread();
+  }
+
+ private:
+  void DestroyTest() override {
+    EXPECT_FALSE(thread_test_.get());
+    EXPECT_TRUE(got_test_done_);
+
+    TestHandler::DestroyTest();
+  }
+
+  const CefThreadId owner_thread_id_;
+
+  scoped_refptr<SimpleThreadTest> thread_test_;
+  TrackCallback got_test_done_;
+
+  IMPLEMENT_REFCOUNTING(BrowserThreadTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(BrowserThreadTestHandler);
+};
+
+}  // namespace
+
+// Test creation of new threads from the browser UI thread.
+TEST(ThreadTest, CreateFromBrowserUIThread) {
+  CefRefPtr<BrowserThreadTestHandler> handler =
+      new BrowserThreadTestHandler(TID_UI);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test creation of new threads from the browser IO thread.
+TEST(ThreadTest, CreateFromBrowserIOThread) {
+  CefRefPtr<BrowserThreadTestHandler> handler =
+      new BrowserThreadTestHandler(TID_IO);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test creation of new threads from the browser FILE thread.
+TEST(ThreadTest, CreateFromBrowserFILEThread) {
+  CefRefPtr<BrowserThreadTestHandler> handler =
+      new BrowserThreadTestHandler(TID_FILE);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+namespace {
+
+// Test creation/execution of threads in the render process.
+
+const char kRenderThreadTestHtml[] = "http://test.com/renderthread.html";
+const char kRenderThreadTestMsg[] = "ThreadTest.RenderThreadTest";
+
+// Browser side.
+class RenderThreadTestHandler : public TestHandler {
+ public:
+  RenderThreadTestHandler() {}
+
+  void RunTest() override {
+    AddResource(kRenderThreadTestHtml, "<html><body>Test</body></html>",
+                "text/html");
+
+    CreateBrowser(kRenderThreadTestHtml);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (!isLoading) {
+      // Return the test in the render process.
+      CefRefPtr<CefProcessMessage> msg =
+          CefProcessMessage::Create(kRenderThreadTestMsg);
+      browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, msg);
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    EXPECT_EQ(PID_RENDERER, source_process);
+    EXPECT_TRUE(message.get());
+    EXPECT_TRUE(message->IsReadOnly());
+
+    const std::string& message_name = message->GetName();
+    EXPECT_STREQ(kRenderThreadTestMsg, message_name.c_str());
+
+    got_message_.yes();
+
+    if (message->GetArgumentList()->GetBool(0))
+      got_success_.yes();
+
+    // Test is complete.
+    DestroyTest();
+
+    return true;
+  }
+
+ protected:
+  void DestroyTest() override {
+    EXPECT_TRUE(got_message_);
+    EXPECT_TRUE(got_success_);
+
+    TestHandler::DestroyTest();
+  }
+
+  TrackCallback got_message_;
+  TrackCallback got_success_;
+
+  IMPLEMENT_REFCOUNTING(RenderThreadTestHandler);
+  DISALLOW_COPY_AND_ASSIGN(RenderThreadTestHandler);
+};
+
+// Renderer side.
+class RenderThreadRendererTest : public ClientAppRenderer::Delegate {
+ public:
+  RenderThreadRendererTest() {}
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName().ToString() == kRenderThreadTestMsg) {
+      browser_ = browser;
+      EXPECT_FALSE(thread_test_.get());
+      thread_test_ = new SimpleThreadTest(
+          3, base::Closure(),
+          base::Bind(&RenderThreadRendererTest::Done, this));
+      thread_test_->RunTest();
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+ private:
+  void Done() {
+    // Let the call stack unwind before destroying |thread_test_|.
+    CefPostTask(TID_RENDERER,
+                base::Bind(&RenderThreadRendererTest::DestroyTest, this));
+  }
+
+  void DestroyTest() {
+    EXPECT_TRUE(thread_test_.get());
+    if (thread_test_) {
+      thread_test_->DestroyTest();
+      thread_test_ = nullptr;
+    }
+
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kRenderThreadTestMsg);
+    EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
+    browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
+
+    browser_ = nullptr;
+  }
+
+  CefRefPtr<CefBrowser> browser_;
+  scoped_refptr<SimpleThreadTest> thread_test_;
+
+  IMPLEMENT_REFCOUNTING(RenderThreadRendererTest);
+  DISALLOW_COPY_AND_ASSIGN(RenderThreadRendererTest);
+};
+
+}  // namespace
+
+TEST(ThreadTest, CreateFromRenderThread) {
+  CefRefPtr<RenderThreadTestHandler> handler = new RenderThreadTestHandler();
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Entry point for creating request handler renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateThreadRendererTests(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new RenderThreadRendererTest);
+}
diff --git a/src/tests/ceftests/tracing_unittest.cc b/src/tests/ceftests/tracing_unittest.cc
new file mode 100644
index 0000000..caa3942
--- /dev/null
+++ b/src/tests/ceftests/tracing_unittest.cc
@@ -0,0 +1,417 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_file_util.h"
+#include "include/cef_task.h"
+#include "include/cef_trace.h"
+#include "include/cef_waitable_event.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/file_util.h"
+
+// Use the CEF version of the TRACE_* macros instead of the Chromium version.
+#undef USING_CHROMIUM_INCLUDES
+#include "include/base/cef_trace_event.h"
+
+enum TracingTestType {
+  TT_TRACE_EVENT0,
+  TT_TRACE_EVENT1,
+  TT_TRACE_EVENT2,
+  TT_TRACE_EVENT_INSTANT0,
+  TT_TRACE_EVENT_INSTANT1,
+  TT_TRACE_EVENT_INSTANT2,
+  TT_TRACE_EVENT_COPY_INSTANT0,
+  TT_TRACE_EVENT_COPY_INSTANT1,
+  TT_TRACE_EVENT_COPY_INSTANT2,
+  TT_TRACE_EVENT_BEGIN0,
+  TT_TRACE_EVENT_BEGIN1,
+  TT_TRACE_EVENT_BEGIN2,
+  TT_TRACE_EVENT_COPY_BEGIN0,
+  TT_TRACE_EVENT_COPY_BEGIN1,
+  TT_TRACE_EVENT_COPY_BEGIN2,
+  TT_TRACE_EVENT_END0,
+  TT_TRACE_EVENT_END1,
+  TT_TRACE_EVENT_END2,
+  TT_TRACE_EVENT_COPY_END0,
+  TT_TRACE_EVENT_COPY_END1,
+  TT_TRACE_EVENT_COPY_END2,
+  TT_TRACE_COUNTER1,
+  TT_TRACE_COPY_COUNTER1,
+  TT_TRACE_COUNTER2,
+  TT_TRACE_COPY_COUNTER2,
+  TT_TRACE_COUNTER_ID1,
+  TT_TRACE_COPY_COUNTER_ID1,
+  TT_TRACE_COUNTER_ID2,
+  TT_TRACE_COPY_COUNTER_ID2,
+  TT_TRACE_EVENT_ASYNC_BEGIN0,
+  TT_TRACE_EVENT_ASYNC_BEGIN1,
+  TT_TRACE_EVENT_ASYNC_BEGIN2,
+  TT_TRACE_EVENT_COPY_ASYNC_BEGIN0,
+  TT_TRACE_EVENT_COPY_ASYNC_BEGIN1,
+  TT_TRACE_EVENT_COPY_ASYNC_BEGIN2,
+  TT_TRACE_EVENT_ASYNC_STEP_INTO0,
+  TT_TRACE_EVENT_ASYNC_STEP_INTO1,
+  TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO0,
+  TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO1,
+  TT_TRACE_EVENT_ASYNC_STEP_PAST0,
+  TT_TRACE_EVENT_ASYNC_STEP_PAST1,
+  TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST0,
+  TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST1,
+  TT_TRACE_EVENT_ASYNC_END0,
+  TT_TRACE_EVENT_ASYNC_END1,
+  TT_TRACE_EVENT_ASYNC_END2,
+  TT_TRACE_EVENT_COPY_ASYNC_END0,
+  TT_TRACE_EVENT_COPY_ASYNC_END1,
+  TT_TRACE_EVENT_COPY_ASYNC_END2
+};
+
+const char kTraceTestCategory[] = "cef.client";
+
+class TracingTestHandler : public CefEndTracingCallback,
+                           public CefCompletionCallback {
+ public:
+  TracingTestHandler(TracingTestType type, const char* trace_type)
+      : trace_type_(trace_type), type_(type) {
+    completion_event_ = CefWaitableEvent::CreateWaitableEvent(true, false);
+  }
+
+  void ExecuteTest() {
+    // Run the test.
+    CefPostTask(TID_UI, base::Bind(&TracingTestHandler::BeginTracing, this));
+
+    // Wait for the test to complete.
+    completion_event_->Wait();
+
+    // Verify the results.
+    EXPECT_TRUE(!trace_data_.empty());
+    EXPECT_TRUE(trace_type_ != nullptr);
+    EXPECT_TRUE(strstr(trace_data_.c_str(), trace_type_) != nullptr);
+  }
+
+  void BeginTracing() {
+    EXPECT_UI_THREAD();
+
+    // Results in a call to OnComplete.
+    CefBeginTracing(kTraceTestCategory, this);
+  }
+
+  void OnComplete() override {
+    EXPECT_UI_THREAD();
+
+    // Add some delay to avoid timing-related test failures.
+    CefPostDelayedTask(TID_UI,
+                       base::Bind(&TracingTestHandler::TestTracing, this), 50);
+  }
+
+  void TestTracing() {
+    EXPECT_UI_THREAD();
+
+    switch (type_) {
+      case TT_TRACE_EVENT0: {
+        TRACE_EVENT0(kTraceTestCategory, "TT_TRACE_EVENT0");
+      } break;
+      case TT_TRACE_EVENT1: {
+        TRACE_EVENT1(kTraceTestCategory, "TT_TRACE_EVENT1", "arg1", 1);
+      } break;
+      case TT_TRACE_EVENT2: {
+        TRACE_EVENT2(kTraceTestCategory, "TT_TRACE_EVENT2", "arg1", 1, "arg2",
+                     2);
+      } break;
+      case TT_TRACE_EVENT_INSTANT0:
+        TRACE_EVENT_INSTANT0(kTraceTestCategory, "TT_TRACE_EVENT_INSTANT0");
+        break;
+      case TT_TRACE_EVENT_INSTANT1:
+        TRACE_EVENT_INSTANT1(kTraceTestCategory, "TT_TRACE_EVENT_INSTANT1",
+                             "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_INSTANT2:
+        TRACE_EVENT_INSTANT2(kTraceTestCategory, "TT_TRACE_EVENT_INSTANT2",
+                             "arg1", 1, "arg2", 2);
+        break;
+      case TT_TRACE_EVENT_COPY_INSTANT0:
+        TRACE_EVENT_COPY_INSTANT0(kTraceTestCategory,
+                                  "TT_TRACE_EVENT_COPY_INSTANT0");
+        break;
+      case TT_TRACE_EVENT_COPY_INSTANT1:
+        TRACE_EVENT_COPY_INSTANT1(kTraceTestCategory,
+                                  "TT_TRACE_EVENT_COPY_INSTANT1", "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_COPY_INSTANT2:
+        TRACE_EVENT_COPY_INSTANT2(kTraceTestCategory,
+                                  "TT_TRACE_EVENT_COPY_INSTANT2", "arg1", 1,
+                                  "arg2", 2);
+        break;
+      case TT_TRACE_EVENT_BEGIN0:
+        TRACE_EVENT_BEGIN0(kTraceTestCategory, "TT_TRACE_EVENT_BEGIN0");
+        break;
+      case TT_TRACE_EVENT_BEGIN1:
+        TRACE_EVENT_BEGIN1(kTraceTestCategory, "TT_TRACE_EVENT_BEGIN1", "arg1",
+                           1);
+        break;
+      case TT_TRACE_EVENT_BEGIN2:
+        TRACE_EVENT_BEGIN2(kTraceTestCategory, "TT_TRACE_EVENT_BEGIN2", "arg1",
+                           1, "arg2", 2);
+        break;
+      case TT_TRACE_EVENT_COPY_BEGIN0:
+        TRACE_EVENT_COPY_BEGIN0(kTraceTestCategory,
+                                "TT_TRACE_EVENT_COPY_BEGIN0");
+        break;
+      case TT_TRACE_EVENT_COPY_BEGIN1:
+        TRACE_EVENT_COPY_BEGIN1(kTraceTestCategory,
+                                "TT_TRACE_EVENT_COPY_BEGIN1", "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_COPY_BEGIN2:
+        TRACE_EVENT_COPY_BEGIN2(kTraceTestCategory,
+                                "TT_TRACE_EVENT_COPY_BEGIN2", "arg1", 1, "arg2",
+                                2);
+        break;
+      case TT_TRACE_EVENT_END0:
+        TRACE_EVENT_BEGIN0(kTraceTestCategory, "TT_TRACE_EVENT_END0");
+        TRACE_EVENT_END0(kTraceTestCategory, "TT_TRACE_EVENT_END0");
+        break;
+      case TT_TRACE_EVENT_END1:
+        TRACE_EVENT_BEGIN1(kTraceTestCategory, "TT_TRACE_EVENT_END1", "arg1",
+                           1);
+        TRACE_EVENT_END1(kTraceTestCategory, "TT_TRACE_EVENT_END1", "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_END2:
+        TRACE_EVENT_BEGIN2(kTraceTestCategory, "TT_TRACE_EVENT_END2", "arg1", 1,
+                           "arg2", 2);
+        TRACE_EVENT_END2(kTraceTestCategory, "TT_TRACE_EVENT_END2", "arg1", 1,
+                         "arg2", 2);
+        break;
+      case TT_TRACE_EVENT_COPY_END0:
+        TRACE_EVENT_COPY_BEGIN0(kTraceTestCategory, "TT_TRACE_EVENT_COPY_END0");
+        TRACE_EVENT_COPY_END0(kTraceTestCategory, "TT_TRACE_EVENT_COPY_END0");
+        break;
+      case TT_TRACE_EVENT_COPY_END1:
+        TRACE_EVENT_COPY_BEGIN1(kTraceTestCategory, "TT_TRACE_EVENT_COPY_END1",
+                                "arg1", 1);
+        TRACE_EVENT_COPY_END1(kTraceTestCategory, "TT_TRACE_EVENT_COPY_END1",
+                              "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_COPY_END2:
+        TRACE_EVENT_COPY_BEGIN2(kTraceTestCategory, "TT_TRACE_EVENT_COPY_END2",
+                                "arg1", 1, "arg2", 2);
+        TRACE_EVENT_COPY_END2(kTraceTestCategory, "TT_TRACE_EVENT_COPY_END2",
+                              "arg1", 1, "arg2", 2);
+        break;
+      case TT_TRACE_COUNTER1:
+        TRACE_COUNTER1(kTraceTestCategory, "TT_TRACE_COUNTER1", 5);
+        break;
+      case TT_TRACE_COPY_COUNTER1:
+        TRACE_COPY_COUNTER1(kTraceTestCategory, "TT_TRACE_COPY_COUNTER1", 5);
+        break;
+      case TT_TRACE_COUNTER2:
+        TRACE_COUNTER2(kTraceTestCategory, "TT_TRACE_COUNTER2", "val1", 5,
+                       "val2", 10);
+        break;
+      case TT_TRACE_COPY_COUNTER2:
+        TRACE_COPY_COUNTER2(kTraceTestCategory, "TT_TRACE_COPY_COUNTER2",
+                            "val1", 5, "val2", 10);
+        break;
+      case TT_TRACE_COUNTER_ID1:
+        TRACE_COUNTER_ID1(kTraceTestCategory, "TT_TRACE_COUNTER_ID1", 100, 5);
+        break;
+      case TT_TRACE_COPY_COUNTER_ID1:
+        TRACE_COPY_COUNTER_ID1(kTraceTestCategory, "TT_TRACE_COPY_COUNTER_ID1",
+                               100, 5);
+        break;
+      case TT_TRACE_COUNTER_ID2:
+        TRACE_COUNTER_ID2(kTraceTestCategory, "TT_TRACE_COUNTER_ID2", 100,
+                          "val1", 5, "val2", 10);
+        break;
+      case TT_TRACE_COPY_COUNTER_ID2:
+        TRACE_COPY_COUNTER_ID2(kTraceTestCategory, "TT_TRACE_COPY_COUNTER_ID2",
+                               100, "val1", 5, "val2", 10);
+        break;
+      case TT_TRACE_EVENT_ASYNC_BEGIN0:
+        TRACE_EVENT_ASYNC_BEGIN0(kTraceTestCategory,
+                                 "TT_TRACE_EVENT_ASYNC_BEGIN0", 100);
+        break;
+      case TT_TRACE_EVENT_ASYNC_BEGIN1:
+        TRACE_EVENT_ASYNC_BEGIN1(kTraceTestCategory,
+                                 "TT_TRACE_EVENT_ASYNC_BEGIN1", 100, "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_ASYNC_BEGIN2:
+        TRACE_EVENT_ASYNC_BEGIN2(kTraceTestCategory,
+                                 "TT_TRACE_EVENT_ASYNC_BEGIN2", 100, "arg1", 1,
+                                 "arg2", 2);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_BEGIN0:
+        TRACE_EVENT_COPY_ASYNC_BEGIN0(kTraceTestCategory,
+                                      "TT_TRACE_EVENT_COPY_ASYNC_BEGIN0", 100);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_BEGIN1:
+        TRACE_EVENT_COPY_ASYNC_BEGIN1(kTraceTestCategory,
+                                      "TT_TRACE_EVENT_COPY_ASYNC_BEGIN1", 100,
+                                      "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_BEGIN2:
+        TRACE_EVENT_COPY_ASYNC_BEGIN2(kTraceTestCategory,
+                                      "TT_TRACE_EVENT_COPY_ASYNC_BEGIN2", 100,
+                                      "arg1", 1, "arg2", 2);
+        break;
+      case TT_TRACE_EVENT_ASYNC_STEP_INTO0:
+        TRACE_EVENT_ASYNC_STEP_INTO0(
+            kTraceTestCategory, "TT_TRACE_EVENT_ASYNC_STEP_INTO0", 100, 1000);
+        break;
+      case TT_TRACE_EVENT_ASYNC_STEP_INTO1:
+        TRACE_EVENT_ASYNC_STEP_INTO1(kTraceTestCategory,
+                                     "TT_TRACE_EVENT_ASYNC_STEP_INTO1", 100,
+                                     1000, "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO0:
+        TRACE_EVENT_COPY_ASYNC_STEP_INTO0(
+            kTraceTestCategory, "TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO0", 100,
+            1000);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO1:
+        TRACE_EVENT_COPY_ASYNC_STEP_INTO1(
+            kTraceTestCategory, "TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO1", 100,
+            1000, "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_ASYNC_STEP_PAST0:
+        TRACE_EVENT_ASYNC_STEP_PAST0(
+            kTraceTestCategory, "TT_TRACE_EVENT_ASYNC_STEP_PAST0", 100, 1000);
+        break;
+      case TT_TRACE_EVENT_ASYNC_STEP_PAST1:
+        TRACE_EVENT_ASYNC_STEP_PAST1(kTraceTestCategory,
+                                     "TT_TRACE_EVENT_ASYNC_STEP_PAST1", 100,
+                                     1000, "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST0:
+        TRACE_EVENT_COPY_ASYNC_STEP_PAST0(
+            kTraceTestCategory, "TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST0", 100,
+            1000);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST1:
+        TRACE_EVENT_COPY_ASYNC_STEP_PAST1(
+            kTraceTestCategory, "TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST1", 100,
+            1000, "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_ASYNC_END0:
+        TRACE_EVENT_ASYNC_END0(kTraceTestCategory, "TT_TRACE_EVENT_ASYNC_END0",
+                               100);
+        break;
+      case TT_TRACE_EVENT_ASYNC_END1:
+        TRACE_EVENT_ASYNC_END1(kTraceTestCategory, "TT_TRACE_EVENT_ASYNC_END1",
+                               100, "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_ASYNC_END2:
+        TRACE_EVENT_ASYNC_END2(kTraceTestCategory, "TT_TRACE_EVENT_ASYNC_END2",
+                               100, "arg1", 1, "arg2", 2);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_END0:
+        TRACE_EVENT_COPY_ASYNC_END0(kTraceTestCategory,
+                                    "TT_TRACE_EVENT_COPY_ASYNC_END0", 100);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_END1:
+        TRACE_EVENT_COPY_ASYNC_END1(kTraceTestCategory,
+                                    "TT_TRACE_EVENT_COPY_ASYNC_END1", 100,
+                                    "arg1", 1);
+        break;
+      case TT_TRACE_EVENT_COPY_ASYNC_END2:
+        TRACE_EVENT_COPY_ASYNC_END2(kTraceTestCategory,
+                                    "TT_TRACE_EVENT_COPY_ASYNC_END2", 100,
+                                    "arg1", 1, "arg2", 2);
+        break;
+    }
+
+    // Results in a call to OnEndTracingComplete.
+    CefEndTracing(CefString(), this);
+  }
+
+  void OnEndTracingComplete(const CefString& tracing_file) override {
+    EXPECT_UI_THREAD();
+
+    CefPostTask(TID_FILE, base::Bind(&TracingTestHandler::ReadTracingFile, this,
+                                     tracing_file));
+  }
+
+  void ReadTracingFile(const std::string& file_path) {
+    EXPECT_FILE_THREAD();
+
+    EXPECT_TRUE(client::file_util::ReadFileToString(file_path, &trace_data_));
+    EXPECT_TRUE(CefDeleteFile(file_path, false));
+
+    completion_event_->Signal();
+  }
+
+ private:
+  ~TracingTestHandler() override {}
+
+  // Handle used to notify when the test is complete.
+  CefRefPtr<CefWaitableEvent> completion_event_;
+
+  const char* trace_type_;
+  TracingTestType type_;
+  std::string trace_data_;
+
+  IMPLEMENT_REFCOUNTING(TracingTestHandler);
+};
+
+// Helper for defining tracing tests.
+#define TRACING_TEST(name, test_type)                  \
+  TEST(TracingTest, name) {                            \
+    CefRefPtr<TracingTestHandler> handler =            \
+        new TracingTestHandler(test_type, #test_type); \
+    handler->ExecuteTest();                            \
+  }
+
+// Define the tests.
+TRACING_TEST(TraceEvent0, TT_TRACE_EVENT0)
+TRACING_TEST(TraceEvent1, TT_TRACE_EVENT1)
+TRACING_TEST(TraceEvent2, TT_TRACE_EVENT2)
+TRACING_TEST(TraceEventInstant0, TT_TRACE_EVENT_INSTANT0)
+TRACING_TEST(TraceEventInstant1, TT_TRACE_EVENT_INSTANT1)
+TRACING_TEST(TraceEventInstant2, TT_TRACE_EVENT_INSTANT2)
+TRACING_TEST(TraceEventCopyInstant0, TT_TRACE_EVENT_COPY_INSTANT0)
+TRACING_TEST(TraceEventCopyInstant1, TT_TRACE_EVENT_COPY_INSTANT1)
+TRACING_TEST(TraceEventCopyInstant2, TT_TRACE_EVENT_COPY_INSTANT2)
+TRACING_TEST(TraceEventBegin0, TT_TRACE_EVENT_BEGIN0)
+TRACING_TEST(TraceEventBegin1, TT_TRACE_EVENT_BEGIN1)
+TRACING_TEST(TraceEventBegin2, TT_TRACE_EVENT_BEGIN2)
+TRACING_TEST(TraceEventCopyBegin0, TT_TRACE_EVENT_COPY_BEGIN0)
+TRACING_TEST(TraceEventCopyBegin1, TT_TRACE_EVENT_COPY_BEGIN1)
+TRACING_TEST(TraceEventCopyBegin2, TT_TRACE_EVENT_COPY_BEGIN2)
+TRACING_TEST(TraceEventEnd0, TT_TRACE_EVENT_END0)
+TRACING_TEST(TraceEventEnd1, TT_TRACE_EVENT_END1)
+TRACING_TEST(TraceEventEnd2, TT_TRACE_EVENT_END2)
+TRACING_TEST(TraceEventCopyEnd0, TT_TRACE_EVENT_COPY_END0)
+TRACING_TEST(TraceEventCopyEnd1, TT_TRACE_EVENT_COPY_END1)
+TRACING_TEST(TraceEventCopyEnd2, TT_TRACE_EVENT_COPY_END1)
+TRACING_TEST(TraceCounter1, TT_TRACE_COUNTER1)
+TRACING_TEST(TraceCopyCounter1, TT_TRACE_COPY_COUNTER1)
+TRACING_TEST(TraceCounter2, TT_TRACE_COUNTER2)
+TRACING_TEST(TraceCopyCounter2, TT_TRACE_COPY_COUNTER2)
+TRACING_TEST(TraceCounterId1, TT_TRACE_COUNTER_ID1)
+TRACING_TEST(TraceCopyCounterId1, TT_TRACE_COPY_COUNTER_ID1)
+TRACING_TEST(TraceCounterId2, TT_TRACE_COUNTER_ID2)
+TRACING_TEST(TraceCopyCounterId2, TT_TRACE_COPY_COUNTER_ID1)
+TRACING_TEST(TraceEventAsyncBegin0, TT_TRACE_EVENT_ASYNC_BEGIN0)
+TRACING_TEST(TraceEventAsyncBegin1, TT_TRACE_EVENT_ASYNC_BEGIN1)
+TRACING_TEST(TraceEventAsyncBegin2, TT_TRACE_EVENT_ASYNC_BEGIN2)
+TRACING_TEST(TraceEventCopyAsyncBegin0, TT_TRACE_EVENT_COPY_ASYNC_BEGIN0)
+TRACING_TEST(TraceEventCopyAsyncBegin1, TT_TRACE_EVENT_COPY_ASYNC_BEGIN1)
+TRACING_TEST(TraceEventCopyAsyncBegin2, TT_TRACE_EVENT_COPY_ASYNC_BEGIN2)
+TRACING_TEST(TraceEventAsyncStepInto0, TT_TRACE_EVENT_ASYNC_STEP_INTO0)
+TRACING_TEST(TraceEventAsyncStepInto1, TT_TRACE_EVENT_ASYNC_STEP_INTO1)
+TRACING_TEST(TraceEventCopyAsyncStepInto0, TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO0)
+TRACING_TEST(TraceEventCopyAsyncStepInto1, TT_TRACE_EVENT_COPY_ASYNC_STEP_INTO1)
+TRACING_TEST(TraceEventAsyncStepPast0, TT_TRACE_EVENT_ASYNC_STEP_PAST0)
+TRACING_TEST(TraceEventAsyncStepPast1, TT_TRACE_EVENT_ASYNC_STEP_PAST1)
+TRACING_TEST(TraceEventCopyAsyncStepPast0, TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST0)
+TRACING_TEST(TraceEventCopyAsyncStepPast1, TT_TRACE_EVENT_COPY_ASYNC_STEP_PAST1)
+TRACING_TEST(TraceEventAsyncEnd0, TT_TRACE_EVENT_ASYNC_END0)
+TRACING_TEST(TraceEventAsyncEnd1, TT_TRACE_EVENT_ASYNC_END1)
+TRACING_TEST(TraceEventAsyncEnd2, TT_TRACE_EVENT_ASYNC_END2)
+TRACING_TEST(TraceEventCopyAsyncEnd0, TT_TRACE_EVENT_COPY_ASYNC_END0)
+
+TEST(TracingTest, NowFromSystemTraceTime) {
+  int64 val = CefNowFromSystemTraceTime();
+  EXPECT_NE(val, 0);
+}
diff --git a/src/tests/ceftests/translator_unittest.cc b/src/tests/ceftests/translator_unittest.cc
new file mode 100644
index 0000000..b039cbc
--- /dev/null
+++ b/src/tests/ceftests/translator_unittest.cc
@@ -0,0 +1,741 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/test/cef_translator_test.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+// Test getting/setting primitive types.
+TEST(TranslatorTest, Primitive) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+  obj->GetVoid();  // Does nothing, but shouldn't crash.
+  EXPECT_EQ(TEST_BOOL_VAL, obj->GetBool());
+  EXPECT_EQ(TEST_INT_VAL, obj->GetInt());
+  EXPECT_EQ(TEST_DOUBLE_VAL, obj->GetDouble());
+  EXPECT_EQ(TEST_LONG_VAL, obj->GetLong());
+  EXPECT_EQ(TEST_SIZET_VAL, obj->GetSizet());
+  EXPECT_TRUE(obj->SetVoid());  // Does nothing, but shouldn't crash.
+  EXPECT_TRUE(obj->SetBool(TEST_BOOL_VAL));
+  EXPECT_TRUE(obj->SetInt(TEST_INT_VAL));
+  EXPECT_TRUE(obj->SetDouble(TEST_DOUBLE_VAL));
+  EXPECT_TRUE(obj->SetLong(TEST_LONG_VAL));
+  EXPECT_TRUE(obj->SetSizet(TEST_SIZET_VAL));
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting primitive list types.
+TEST(TranslatorTest, PrimitiveList) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  std::vector<int> list;
+  list.push_back(TEST_INT_VAL);
+  list.push_back(TEST_INT_VAL2);
+  EXPECT_TRUE(obj->SetIntList(list));
+
+  list.clear();
+  EXPECT_TRUE(obj->GetIntListByRef(list));
+  EXPECT_EQ(2U, list.size());
+  EXPECT_EQ(TEST_INT_VAL, list[0]);
+  EXPECT_EQ(TEST_INT_VAL2, list[1]);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting string types.
+TEST(TranslatorTest, String) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+  EXPECT_STREQ(TEST_STRING_VAL, obj->GetString().ToString().c_str());
+  EXPECT_TRUE(obj->SetString(TEST_STRING_VAL));
+
+  CefString str;
+  obj->GetStringByRef(str);
+  EXPECT_STREQ(TEST_STRING_VAL, str.ToString().c_str());
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting string list types.
+TEST(TranslatorTest, StringList) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  std::vector<CefString> list;
+  list.push_back(TEST_STRING_VAL);
+  list.push_back(TEST_STRING_VAL2);
+  list.push_back(TEST_STRING_VAL3);
+  EXPECT_TRUE(obj->SetStringList(list));
+
+  list.clear();
+  EXPECT_TRUE(obj->GetStringListByRef(list));
+  EXPECT_EQ(3U, list.size());
+  EXPECT_STREQ(TEST_STRING_VAL, list[0].ToString().c_str());
+  EXPECT_STREQ(TEST_STRING_VAL2, list[1].ToString().c_str());
+  EXPECT_STREQ(TEST_STRING_VAL3, list[2].ToString().c_str());
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting string map types.
+TEST(TranslatorTest, StringMap) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  CefTranslatorTest::StringMap map;
+  map.insert(std::make_pair(TEST_STRING_KEY, TEST_STRING_VAL));
+  map.insert(std::make_pair(TEST_STRING_KEY2, TEST_STRING_VAL2));
+  map.insert(std::make_pair(TEST_STRING_KEY3, TEST_STRING_VAL3));
+  EXPECT_TRUE(obj->SetStringMap(map));
+
+  map.clear();
+  EXPECT_TRUE(obj->GetStringMapByRef(map));
+  EXPECT_EQ(3U, map.size());
+
+  CefTranslatorTest::StringMap::const_iterator it;
+
+  it = map.find(TEST_STRING_KEY);
+  EXPECT_TRUE(it != map.end() && it->second == TEST_STRING_VAL);
+  it = map.find(TEST_STRING_KEY2);
+  EXPECT_TRUE(it != map.end() && it->second == TEST_STRING_VAL2);
+  it = map.find(TEST_STRING_KEY3);
+  EXPECT_TRUE(it != map.end() && it->second == TEST_STRING_VAL3);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting string multimap types.
+TEST(TranslatorTest, StringMultimap) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  CefTranslatorTest::StringMultimap map;
+  map.insert(std::make_pair(TEST_STRING_KEY, TEST_STRING_VAL));
+  map.insert(std::make_pair(TEST_STRING_KEY2, TEST_STRING_VAL2));
+  map.insert(std::make_pair(TEST_STRING_KEY3, TEST_STRING_VAL3));
+  EXPECT_TRUE(obj->SetStringMultimap(map));
+
+  map.clear();
+  EXPECT_TRUE(obj->GetStringMultimapByRef(map));
+  EXPECT_EQ(3U, map.size());
+
+  CefTranslatorTest::StringMultimap::const_iterator it;
+
+  it = map.find(TEST_STRING_KEY);
+  EXPECT_TRUE(it != map.end() && it->second == TEST_STRING_VAL);
+  it = map.find(TEST_STRING_KEY2);
+  EXPECT_TRUE(it != map.end() && it->second == TEST_STRING_VAL2);
+  it = map.find(TEST_STRING_KEY3);
+  EXPECT_TRUE(it != map.end() && it->second == TEST_STRING_VAL3);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting struct types.
+TEST(TranslatorTest, Struct) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  CefPoint point(TEST_X_VAL, TEST_Y_VAL);
+  EXPECT_EQ(point, obj->GetPoint());
+  EXPECT_TRUE(obj->SetPoint(point));
+
+  CefPoint point2;
+  obj->GetPointByRef(point2);
+  EXPECT_EQ(point, point2);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting struct list types.
+TEST(TranslatorTest, StructList) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  std::vector<CefPoint> list;
+  list.push_back(CefPoint(TEST_X_VAL, TEST_Y_VAL));
+  list.push_back(CefPoint(TEST_X_VAL2, TEST_Y_VAL2));
+  EXPECT_TRUE(obj->SetPointList(list));
+
+  list.clear();
+  EXPECT_TRUE(obj->GetPointListByRef(list));
+  EXPECT_EQ(2U, list.size());
+  EXPECT_EQ(CefPoint(TEST_X_VAL, TEST_Y_VAL), list[0]);
+  EXPECT_EQ(CefPoint(TEST_X_VAL2, TEST_Y_VAL2), list[1]);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting library-side RefPtr types.
+TEST(TranslatorTest, RefPtrLibrary) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> test_obj =
+      CefTranslatorTestRefPtrLibrary::Create(kTestVal);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  int retval = obj->SetRefPtrLibrary(test_obj);
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+
+  const int kTestVal2 = 30;
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> test_obj2 =
+      obj->GetRefPtrLibrary(kTestVal2);
+  EXPECT_EQ(kTestVal2, test_obj2->GetValue());
+  int retval2 = obj->SetRefPtrLibrary(test_obj2);
+  EXPECT_EQ(kTestVal2, retval2);
+  EXPECT_EQ(kTestVal2, test_obj2->GetValue());
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+  EXPECT_TRUE(test_obj->HasOneRef());
+  EXPECT_TRUE(test_obj2->HasOneRef());
+}
+
+// Test getting/setting inherited library-side RefPtr types.
+TEST(TranslatorTest, RefPtrLibraryInherit) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  const int kTestVal2 = 40;
+  CefRefPtr<CefTranslatorTestRefPtrLibraryChild> test_obj =
+      CefTranslatorTestRefPtrLibraryChild::Create(kTestVal, kTestVal2);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+  int retval = obj->SetRefPtrLibrary(test_obj);
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+
+  EXPECT_EQ(kTestVal, obj->SetChildRefPtrLibrary(test_obj));
+  EXPECT_EQ(kTestVal,
+            obj->SetChildRefPtrLibraryAndReturnParent(test_obj)->GetValue());
+
+  const int kTestVal3 = 100;
+  CefRefPtr<CefTranslatorTestRefPtrLibraryChildChild> test_obj2 =
+      CefTranslatorTestRefPtrLibraryChildChild::Create(kTestVal, kTestVal2,
+                                                       kTestVal3);
+  EXPECT_EQ(kTestVal, test_obj2->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue());
+  EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue());
+  int retval2 = obj->SetRefPtrLibrary(test_obj2);
+  EXPECT_EQ(kTestVal, retval2);
+  EXPECT_EQ(kTestVal, test_obj2->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue());
+  EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue());
+
+  EXPECT_EQ(kTestVal, obj->SetChildRefPtrLibrary(test_obj2));
+  EXPECT_EQ(kTestVal,
+            obj->SetChildRefPtrLibraryAndReturnParent(test_obj2)->GetValue());
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+  EXPECT_TRUE(test_obj->HasOneRef());
+  EXPECT_TRUE(test_obj2->HasOneRef());
+}
+
+// Test getting/setting library-side RefPtr list types.
+TEST(TranslatorTest, RefPtrLibraryList) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kVal1 = 34;
+  const int kVal2 = 10;
+
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> val1 =
+      CefTranslatorTestRefPtrLibrary::Create(kVal1);
+  CefRefPtr<CefTranslatorTestRefPtrLibrary> val2 =
+      CefTranslatorTestRefPtrLibraryChild::Create(kVal2, 0);
+
+  std::vector<CefRefPtr<CefTranslatorTestRefPtrLibrary>> list;
+  list.push_back(val1);
+  list.push_back(val2);
+  EXPECT_TRUE(obj->SetRefPtrLibraryList(list, kVal1, kVal2));
+
+  list.clear();
+  EXPECT_TRUE(obj->GetRefPtrLibraryListByRef(list, kVal1, kVal2));
+  EXPECT_EQ(2U, list.size());
+  EXPECT_EQ(kVal1, list[0]->GetValue());
+  EXPECT_EQ(kVal2, list[1]->GetValue());
+
+  list.clear();
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+  EXPECT_TRUE(val1->HasOneRef());
+  EXPECT_TRUE(val2->HasOneRef());
+}
+
+namespace {
+
+class TranslatorTestRefPtrClient : public CefTranslatorTestRefPtrClient {
+ public:
+  explicit TranslatorTestRefPtrClient(const int val) : val_(val) {}
+
+  virtual int GetValue() override { return val_; }
+
+ private:
+  const int val_;
+
+  IMPLEMENT_REFCOUNTING(TranslatorTestRefPtrClient);
+  DISALLOW_COPY_AND_ASSIGN(TranslatorTestRefPtrClient);
+};
+
+class TranslatorTestRefPtrClientChild
+    : public CefTranslatorTestRefPtrClientChild {
+ public:
+  TranslatorTestRefPtrClientChild(const int val, const int other_val)
+      : val_(val), other_val_(other_val) {}
+
+  virtual int GetValue() override { return val_; }
+
+  virtual int GetOtherValue() override { return other_val_; }
+
+ private:
+  const int val_;
+  const int other_val_;
+
+  IMPLEMENT_REFCOUNTING(TranslatorTestRefPtrClientChild);
+  DISALLOW_COPY_AND_ASSIGN(TranslatorTestRefPtrClientChild);
+};
+
+}  // namespace
+
+// Test getting/setting client-side RefPtr types.
+TEST(TranslatorTest, RefPtrClient) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+
+  CefRefPtr<TranslatorTestRefPtrClient> test_obj =
+      new TranslatorTestRefPtrClient(kTestVal);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal, obj->SetRefPtrClient(test_obj.get()));
+  CefRefPtr<CefTranslatorTestRefPtrClient> handler =
+      obj->SetRefPtrClientAndReturn(test_obj.get());
+  EXPECT_EQ(test_obj.get(), handler.get());
+  EXPECT_EQ(kTestVal, handler->GetValue());
+  handler = nullptr;
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+  EXPECT_TRUE(test_obj->HasOneRef());
+}
+
+// Test getting/setting inherited client-side RefPtr types.
+TEST(TranslatorTest, RefPtrClientInherit) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  const int kTestVal2 = 86;
+
+  CefRefPtr<TranslatorTestRefPtrClientChild> test_obj =
+      new TranslatorTestRefPtrClientChild(kTestVal, kTestVal2);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+  int retval = obj->SetRefPtrClient(test_obj);
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+
+  EXPECT_EQ(kTestVal, obj->SetChildRefPtrClient(test_obj));
+  CefRefPtr<CefTranslatorTestRefPtrClient> handler =
+      obj->SetChildRefPtrClientAndReturnParent(test_obj);
+  EXPECT_EQ(kTestVal, handler->GetValue());
+  EXPECT_EQ(test_obj.get(), handler.get());
+  handler = nullptr;
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+  EXPECT_TRUE(test_obj->HasOneRef());
+}
+
+// Test getting/setting client-side RefPtr list types.
+TEST(TranslatorTest, RefPtrClientList) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kVal1 = 34;
+  const int kVal2 = 10;
+
+  CefRefPtr<CefTranslatorTestRefPtrClient> val1 =
+      new TranslatorTestRefPtrClient(kVal1);
+  CefRefPtr<CefTranslatorTestRefPtrClient> val2 =
+      new TranslatorTestRefPtrClientChild(kVal2, 0);
+
+  std::vector<CefRefPtr<CefTranslatorTestRefPtrClient>> list;
+  list.push_back(val1);
+  list.push_back(val2);
+  EXPECT_TRUE(obj->SetRefPtrClientList(list, kVal1, kVal2));
+
+  list.clear();
+  EXPECT_TRUE(obj->GetRefPtrClientListByRef(list, val1, val2));
+  EXPECT_EQ(2U, list.size());
+  EXPECT_EQ(kVal1, list[0]->GetValue());
+  EXPECT_EQ(val1.get(), list[0].get());
+  EXPECT_EQ(kVal2, list[1]->GetValue());
+  EXPECT_EQ(val2.get(), list[1].get());
+
+  list.clear();
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+  EXPECT_TRUE(val1->HasOneRef());
+  EXPECT_TRUE(val2->HasOneRef());
+}
+
+// Test getting/setting library-side OwnPtr types.
+TEST(TranslatorTest, OwnPtrLibrary) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  CefOwnPtr<CefTranslatorTestScopedLibrary> test_obj =
+      CefTranslatorTestScopedLibrary::Create(kTestVal);
+  EXPECT_TRUE(test_obj.get());
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  int retval = obj->SetOwnPtrLibrary(test_obj.Pass());
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_FALSE(test_obj.get());
+
+  const int kTestVal2 = 30;
+  CefOwnPtr<CefTranslatorTestScopedLibrary> test_obj2 =
+      obj->GetOwnPtrLibrary(kTestVal2);
+  EXPECT_TRUE(test_obj2.get());
+  EXPECT_EQ(kTestVal2, test_obj2->GetValue());
+  int retval2 = obj->SetOwnPtrLibrary(test_obj2.Pass());
+  EXPECT_EQ(kTestVal2, retval2);
+  EXPECT_FALSE(test_obj2.get());
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting inherited library-side OwnPtr types.
+TEST(TranslatorTest, OwnPtrLibraryInherit) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  const int kTestVal2 = 40;
+  CefOwnPtr<CefTranslatorTestScopedLibraryChild> test_obj =
+      CefTranslatorTestScopedLibraryChild::Create(kTestVal, kTestVal2);
+  EXPECT_TRUE(test_obj.get());
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+  int retval =
+      obj->SetOwnPtrLibrary(test_obj.PassAs<CefTranslatorTestScopedLibrary>());
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_FALSE(test_obj.get());
+
+  test_obj = CefTranslatorTestScopedLibraryChild::Create(kTestVal, kTestVal2);
+  EXPECT_TRUE(test_obj.get());
+  EXPECT_EQ(kTestVal, obj->SetChildOwnPtrLibrary(test_obj.Pass()));
+  EXPECT_FALSE(test_obj.get());
+
+  test_obj = CefTranslatorTestScopedLibraryChild::Create(kTestVal, kTestVal2);
+  EXPECT_TRUE(test_obj.get());
+  CefOwnPtr<CefTranslatorTestScopedLibrary> test_obj_parent =
+      obj->SetChildOwnPtrLibraryAndReturnParent(test_obj.Pass());
+  EXPECT_FALSE(test_obj.get());
+  EXPECT_TRUE(test_obj_parent.get());
+  EXPECT_EQ(kTestVal, test_obj_parent->GetValue());
+  test_obj_parent.reset(nullptr);
+
+  const int kTestVal3 = 100;
+  CefOwnPtr<CefTranslatorTestScopedLibraryChildChild> test_obj2 =
+      CefTranslatorTestScopedLibraryChildChild::Create(kTestVal, kTestVal2,
+                                                       kTestVal3);
+  EXPECT_EQ(kTestVal, test_obj2->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue());
+  EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue());
+  int retval2 =
+      obj->SetOwnPtrLibrary(test_obj2.PassAs<CefTranslatorTestScopedLibrary>());
+  EXPECT_EQ(kTestVal, retval2);
+  EXPECT_FALSE(test_obj2.get());
+
+  test_obj2 = CefTranslatorTestScopedLibraryChildChild::Create(
+      kTestVal, kTestVal2, kTestVal3);
+  EXPECT_EQ(kTestVal,
+            obj->SetChildOwnPtrLibrary(
+                test_obj2.PassAs<CefTranslatorTestScopedLibraryChild>()));
+  EXPECT_FALSE(test_obj2.get());
+
+  test_obj2 = CefTranslatorTestScopedLibraryChildChild::Create(
+      kTestVal, kTestVal2, kTestVal3);
+  test_obj_parent = obj->SetChildOwnPtrLibraryAndReturnParent(
+      test_obj2.PassAs<CefTranslatorTestScopedLibraryChild>());
+  EXPECT_FALSE(test_obj2.get());
+  EXPECT_TRUE(test_obj_parent.get());
+  EXPECT_EQ(kTestVal, test_obj_parent->GetValue());
+  test_obj_parent.reset(nullptr);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+namespace {
+
+class TranslatorTestScopedClient : public CefTranslatorTestScopedClient {
+ public:
+  TranslatorTestScopedClient(const int val, TrackCallback* got_delete)
+      : val_(val), got_delete_(got_delete) {}
+  ~TranslatorTestScopedClient() override { got_delete_->yes(); }
+
+  virtual int GetValue() override { return val_; }
+
+ private:
+  const int val_;
+  TrackCallback* got_delete_;
+
+  DISALLOW_COPY_AND_ASSIGN(TranslatorTestScopedClient);
+};
+
+class TranslatorTestScopedClientChild
+    : public CefTranslatorTestScopedClientChild {
+ public:
+  TranslatorTestScopedClientChild(const int val,
+                                  const int other_val,
+                                  TrackCallback* got_delete)
+      : val_(val), other_val_(other_val), got_delete_(got_delete) {}
+  ~TranslatorTestScopedClientChild() override { got_delete_->yes(); }
+
+  virtual int GetValue() override { return val_; }
+
+  virtual int GetOtherValue() override { return other_val_; }
+
+ private:
+  const int val_;
+  const int other_val_;
+  TrackCallback* got_delete_;
+
+  DISALLOW_COPY_AND_ASSIGN(TranslatorTestScopedClientChild);
+};
+
+}  // namespace
+
+// Test getting/setting client-side OwnPtr types.
+TEST(TranslatorTest, OwnPtrClient) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  TrackCallback got_delete;
+
+  CefOwnPtr<CefTranslatorTestScopedClient> test_obj(
+      new TranslatorTestScopedClient(kTestVal, &got_delete));
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal, obj->SetOwnPtrClient(test_obj.Pass()));
+  EXPECT_FALSE(test_obj.get());
+  EXPECT_TRUE(got_delete);
+
+  got_delete.reset();
+  test_obj.reset(new TranslatorTestScopedClient(kTestVal, &got_delete));
+  CefOwnPtr<CefTranslatorTestScopedClient> handler =
+      obj->SetOwnPtrClientAndReturn(test_obj.Pass());
+  EXPECT_FALSE(test_obj.get());
+  EXPECT_TRUE(handler.get());
+  EXPECT_FALSE(got_delete);
+  EXPECT_EQ(kTestVal, handler->GetValue());
+  handler.reset(nullptr);
+  EXPECT_TRUE(got_delete);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting inherited client-side OwnPtr types.
+TEST(TranslatorTest, OwnPtrClientInherit) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  const int kTestVal2 = 86;
+  TrackCallback got_delete;
+
+  CefOwnPtr<CefTranslatorTestScopedClientChild> test_obj(
+      new TranslatorTestScopedClientChild(kTestVal, kTestVal2, &got_delete));
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+  EXPECT_EQ(kTestVal, obj->SetOwnPtrClient(
+                          test_obj.PassAs<CefTranslatorTestScopedClient>()));
+  EXPECT_FALSE(test_obj.get());
+  EXPECT_TRUE(got_delete);
+
+  got_delete.reset();
+  test_obj.reset(
+      new TranslatorTestScopedClientChild(kTestVal, kTestVal2, &got_delete));
+  EXPECT_EQ(kTestVal, obj->SetChildOwnPtrClient(test_obj.Pass()));
+  EXPECT_FALSE(test_obj.get());
+  EXPECT_TRUE(got_delete);
+
+  got_delete.reset();
+  test_obj.reset(
+      new TranslatorTestScopedClientChild(kTestVal, kTestVal2, &got_delete));
+  CefOwnPtr<CefTranslatorTestScopedClient> handler(
+      obj->SetChildOwnPtrClientAndReturnParent(test_obj.Pass()));
+  EXPECT_EQ(kTestVal, handler->GetValue());
+  EXPECT_FALSE(test_obj.get());
+  EXPECT_FALSE(got_delete);
+  handler.reset(nullptr);
+  EXPECT_TRUE(got_delete);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting library-side RawPtr types.
+TEST(TranslatorTest, RawPtrLibrary) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  CefOwnPtr<CefTranslatorTestScopedLibrary> test_obj(
+      CefTranslatorTestScopedLibrary::Create(kTestVal));
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  int retval = obj->SetRawPtrLibrary(test_obj.get());
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+
+  const int kTestVal2 = 30;
+  CefOwnPtr<CefTranslatorTestScopedLibrary> test_obj2(
+      obj->GetOwnPtrLibrary(kTestVal2));
+  EXPECT_EQ(kTestVal2, test_obj2->GetValue());
+  int retval2 = obj->SetRawPtrLibrary(test_obj2.get());
+  EXPECT_EQ(kTestVal2, retval2);
+  EXPECT_EQ(kTestVal2, test_obj2->GetValue());
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting inherited library-side RawPtr types.
+TEST(TranslatorTest, RawPtrLibraryInherit) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  const int kTestVal2 = 40;
+  CefOwnPtr<CefTranslatorTestScopedLibraryChild> test_obj(
+      CefTranslatorTestScopedLibraryChild::Create(kTestVal, kTestVal2));
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+  int retval = obj->SetRawPtrLibrary(test_obj.get());
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+
+  EXPECT_EQ(kTestVal, obj->SetChildRawPtrLibrary(test_obj.get()));
+
+  const int kTestVal3 = 100;
+  CefOwnPtr<CefTranslatorTestScopedLibraryChildChild> test_obj2(
+      CefTranslatorTestScopedLibraryChildChild::Create(kTestVal, kTestVal2,
+                                                       kTestVal3));
+  EXPECT_EQ(kTestVal, test_obj2->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue());
+  EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue());
+  int retval2 = obj->SetRawPtrLibrary(test_obj2.get());
+  EXPECT_EQ(kTestVal, retval2);
+  EXPECT_EQ(kTestVal, test_obj2->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue());
+  EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue());
+
+  EXPECT_EQ(kTestVal, obj->SetChildRawPtrLibrary(test_obj2.get()));
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting library-side RawPtr list types.
+TEST(TranslatorTest, RawPtrLibraryList) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kVal1 = 34;
+  const int kVal2 = 10;
+
+  CefOwnPtr<CefTranslatorTestScopedLibrary> val1(
+      CefTranslatorTestScopedLibrary::Create(kVal1));
+  CefOwnPtr<CefTranslatorTestScopedLibraryChild> val2(
+      CefTranslatorTestScopedLibraryChild::Create(kVal2, 0));
+
+  std::vector<CefRawPtr<CefTranslatorTestScopedLibrary>> list;
+  list.push_back(val1.get());
+  list.push_back(val2.get());
+  EXPECT_TRUE(obj->SetRawPtrLibraryList(list, kVal1, kVal2));
+  list.clear();
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting client-side RawPtr types.
+TEST(TranslatorTest, RawPtrClient) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  TrackCallback got_delete;
+
+  CefOwnPtr<TranslatorTestScopedClient> test_obj(
+      new TranslatorTestScopedClient(kTestVal, &got_delete));
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal, obj->SetRawPtrClient(test_obj.get()));
+  EXPECT_FALSE(got_delete);
+  test_obj.reset(nullptr);
+  EXPECT_TRUE(got_delete);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting inherited client-side RawPtr types.
+TEST(TranslatorTest, RawPtrClientInherit) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kTestVal = 12;
+  const int kTestVal2 = 86;
+  TrackCallback got_delete;
+
+  CefOwnPtr<TranslatorTestScopedClientChild> test_obj(
+      new TranslatorTestScopedClientChild(kTestVal, kTestVal2, &got_delete));
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+  int retval = obj->SetRawPtrClient(test_obj.get());
+  EXPECT_EQ(kTestVal, retval);
+  EXPECT_EQ(kTestVal, test_obj->GetValue());
+  EXPECT_EQ(kTestVal2, test_obj->GetOtherValue());
+  EXPECT_FALSE(got_delete);
+
+  EXPECT_EQ(kTestVal, obj->SetChildRawPtrClient(test_obj.get()));
+  EXPECT_FALSE(got_delete);
+  test_obj.reset(nullptr);
+  EXPECT_TRUE(got_delete);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
+
+// Test getting/setting client-side RawPtr list types.
+TEST(TranslatorTest, RawPtrClientList) {
+  CefRefPtr<CefTranslatorTest> obj = CefTranslatorTest::Create();
+
+  const int kVal1 = 34;
+  const int kVal2 = 10;
+  TrackCallback got_delete1, got_delete2;
+
+  CefOwnPtr<CefTranslatorTestScopedClient> val1(
+      new TranslatorTestScopedClient(kVal1, &got_delete1));
+  CefOwnPtr<CefTranslatorTestScopedClient> val2(
+      new TranslatorTestScopedClientChild(kVal2, 0, &got_delete2));
+
+  std::vector<CefRawPtr<CefTranslatorTestScopedClient>> list;
+  list.push_back(val1.get());
+  list.push_back(val2.get());
+  EXPECT_TRUE(obj->SetRawPtrClientList(list, kVal1, kVal2));
+  list.clear();
+
+  EXPECT_FALSE(got_delete1);
+  val1.reset(nullptr);
+  EXPECT_TRUE(got_delete1);
+
+  EXPECT_FALSE(got_delete2);
+  val2.reset(nullptr);
+  EXPECT_TRUE(got_delete2);
+
+  // Only one reference to the object should exist.
+  EXPECT_TRUE(obj->HasOneRef());
+}
diff --git a/src/tests/ceftests/urlrequest_unittest.cc b/src/tests/ceftests/urlrequest_unittest.cc
new file mode 100644
index 0000000..a7e8fec
--- /dev/null
+++ b/src/tests/ceftests/urlrequest_unittest.cc
@@ -0,0 +1,3646 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <algorithm>
+#include <map>
+#include <sstream>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_parser.h"
+#include "include/cef_request_context_handler.h"
+#include "include/cef_scheme.h"
+#include "include/cef_server.h"
+#include "include/cef_task.h"
+#include "include/cef_urlrequest.h"
+#include "include/cef_waitable_event.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_scoped_temp_dir.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_suite.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/file_util.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppRenderer;
+
+// How to add a new test:
+// 1. Add a new value to the RequestTestMode enumeration.
+// 2. Add methods to set up and run the test in RequestTestRunner.
+// 3. Add a line for the test in the RequestTestRunner constructor.
+// 4. Add lines for the test in the "Define the tests" section at the bottom of
+//    the file.
+
+namespace {
+
+// Unique values for URLRequest tests.
+const char kRequestTestMsg[] = "URLRequestTest.Test";
+const char kIncompleteRequestTestMsg[] = "URLRequestTest.IncompleteRequestTest";
+
+// TEST DATA
+
+// Custom scheme handler backend.
+const char kRequestSchemeCustom[] = "urcustom";
+const char kRequestHostCustom[] = "test";
+
+// Server backend.
+const char kRequestAddressServer[] = "127.0.0.1";
+const uint16 kRequestPortServer = 8099;
+const char kRequestSchemeServer[] = "http";
+
+const char kRequestSendCookieName[] = "urcookie_send";
+const char kRequestSaveCookieName[] = "urcookie_save";
+
+const char kCacheControlHeader[] = "cache-control";
+
+// Used with incomplete tests for data that should not be sent.
+const char kIncompleteDoNotSendData[] = "DO NOT SEND";
+
+enum RequestTestMode {
+  REQTEST_GET = 0,
+  REQTEST_GET_NODATA,
+  REQTEST_GET_PARTIAL_CONTENT,
+  REQTEST_GET_ALLOWCOOKIES,
+  REQTEST_GET_REDIRECT,
+  REQTEST_GET_REDIRECT_STOP,
+  REQTEST_GET_REDIRECT_LOCATION,
+  REQTEST_GET_REFERRER,
+  REQTEST_GET_AUTH,
+  REQTEST_POST,
+  REQTEST_POST_FILE,
+  REQTEST_POST_WITHPROGRESS,
+  REQTEST_POST_REDIRECT,
+  REQTEST_POST_REDIRECT_TOGET,
+  REQTEST_HEAD,
+  REQTEST_CACHE_WITH_CONTROL,
+  REQTEST_CACHE_WITHOUT_CONTROL,
+  REQTEST_CACHE_SKIP_FLAG,
+  REQTEST_CACHE_SKIP_HEADER,
+  REQTEST_CACHE_ONLY_FAILURE_FLAG,
+  REQTEST_CACHE_ONLY_FAILURE_HEADER,
+  REQTEST_CACHE_ONLY_SUCCESS_FLAG,
+  REQTEST_CACHE_ONLY_SUCCESS_HEADER,
+  REQTEST_CACHE_DISABLE_FLAG,
+  REQTEST_CACHE_DISABLE_HEADER,
+  REQTEST_INCOMPLETE_PROCESS_REQUEST,
+  REQTEST_INCOMPLETE_READ_RESPONSE,
+};
+
+enum ContextTestMode {
+  CONTEXT_GLOBAL,
+  CONTEXT_INMEMORY,
+  CONTEXT_ONDISK,
+};
+
+// Defines test expectations for a request.
+struct RequestRunSettings {
+  RequestRunSettings() {}
+
+  // Set expectations for request failure.
+  void SetRequestFailureExpected(cef_errorcode_t error_code) {
+    expect_upload_progress = false;
+    expect_download_progress = false;
+    expect_download_data = false;
+    expected_status = UR_FAILED;
+    expected_error_code = error_code;
+    response = nullptr;
+    response_data.clear();
+  }
+
+  // Request that will be sent.
+  CefRefPtr<CefRequest> request;
+
+  // Response that will be returned by the backend.
+  CefRefPtr<CefResponse> response;
+
+  // Optional response data that will be returned by the backend.
+  std::string response_data;
+
+  // Create an incomplete request to test shutdown behavior.
+  enum IncompleteType {
+    INCOMPLETE_NONE,
+    INCOMPLETE_PROCESS_REQUEST,
+    INCOMPLETE_READ_RESPONSE,
+  };
+  IncompleteType incomplete_type = INCOMPLETE_NONE;
+
+  // If true upload progress notification will be expected.
+  bool expect_upload_progress = false;
+
+  // If true download progress notification will be expected.
+  bool expect_download_progress = true;
+
+  // If true download data will be expected.
+  bool expect_download_data = true;
+
+  // The offset from what we passed that we expect to receive.
+  size_t expected_download_offset = 0;
+
+  // Expected status value.
+  CefURLRequest::Status expected_status = UR_SUCCESS;
+
+  // Expected error code value.
+  CefURLRequest::ErrorCode expected_error_code = ERR_NONE;
+
+  // If true the request cookie should be sent to the server.
+  bool expect_send_cookie = false;
+
+  // If true the response cookie should be saved.
+  bool expect_save_cookie = false;
+
+  // If true the test will begin by requiring Basic authentication and then
+  // continue with the actual request. The UR_FLAG_ALLOW_STORED_CREDENTIALS
+  // flag must be set on the request. When using the global request context
+  // CefRequestContext::ClearHttpAuthCredentials should be called to avoid
+  // leaking state across test runs. Authentication is only supported with
+  // browser-initiated requests and the server backend.
+  bool expect_authentication = false;
+  std::string username;
+  std::string password;
+
+  // If specified the test will begin with this redirect request and response.
+  CefRefPtr<CefRequest> redirect_request;
+  CefRefPtr<CefResponse> redirect_response;
+
+  // If true the redirect is expected to be followed.
+  bool expect_follow_redirect = true;
+
+  // If true the response is expected to be served from cache.
+  bool expect_response_was_cached = false;
+
+  // The expected number of requests to send, or -1 if unspecified.
+  // Used only with the server backend.
+  int expected_send_count = -1;
+
+  // The expected number of requests to receive, or -1 if unspecified.
+  // Used only with the server backend.
+  int expected_receive_count = -1;
+
+  typedef base::Callback<void(int /* next_send_count */,
+                              const base::Closure& /* complete_callback */)>
+      NextRequestCallback;
+
+  // If non-null this callback will be executed before subsequent requests are
+  // sent.
+  NextRequestCallback setup_next_request;
+};
+
+// Manages the map of request URL to test expectations.
+class RequestDataMap {
+ public:
+  struct Entry {
+    enum Type {
+      TYPE_UNKNOWN,
+      TYPE_NORMAL,
+      TYPE_REDIRECT,
+    };
+
+    Entry(Type entry_type) : type(entry_type), settings(nullptr) {}
+
+    Type type;
+
+    // Used with TYPE_NORMAL.
+    // |settings| is not owned by this object.
+    RequestRunSettings* settings;
+
+    // Used with TYPE_REDIRECT.
+    CefRefPtr<CefRequest> redirect_request;
+    CefRefPtr<CefResponse> redirect_response;
+  };
+
+  RequestDataMap() : owner_task_runner_(CefTaskRunner::GetForCurrentThread()) {}
+
+  // Pass ownership to the specified |task_runner| thread.
+  // If |task_runner| is nullptr the test is considered destroyed.
+  void SetOwnerTaskRunner(CefRefPtr<CefTaskRunner> task_runner) {
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+    owner_task_runner_ = task_runner;
+  }
+
+  void AddSchemeHandler(RequestRunSettings* settings) {
+    EXPECT_TRUE(settings);
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+
+    const std::string& url = settings->request->GetURL();
+    data_map_.insert(std::make_pair(url, settings));
+
+    if (settings->redirect_request) {
+      const std::string& redirect_url = settings->redirect_request->GetURL();
+      redirect_data_map_.insert(std::make_pair(
+          redirect_url, std::make_pair(settings->redirect_request,
+                                       settings->redirect_response)));
+    }
+  }
+
+  Entry Find(const std::string& url) {
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+
+    Entry entry(Entry::TYPE_UNKNOWN);
+
+    // Try to find a test match.
+    {
+      DataMap::const_iterator it = data_map_.find(url);
+      if (it != data_map_.end()) {
+        entry.type = Entry::TYPE_NORMAL;
+        entry.settings = it->second;
+        return entry;
+      }
+    }
+
+    // Try to find a redirect match.
+    {
+      RedirectDataMap::const_iterator it = redirect_data_map_.find(url);
+      if (it != redirect_data_map_.end()) {
+        entry.type = Entry::TYPE_REDIRECT;
+        entry.redirect_request = it->second.first;
+        entry.redirect_response = it->second.second;
+        return entry;
+      }
+    }
+
+    // Unknown test.
+    ADD_FAILURE() << "url: " << url;
+    return entry;
+  }
+
+  size_t size() const { return data_map_.size() + redirect_data_map_.size(); }
+
+ private:
+  CefRefPtr<CefTaskRunner> owner_task_runner_;
+
+  // The below members are only accessed on the |owner_task_runner_| thread.
+
+  // RequestRunSettings pointer is not owned by this object.
+  typedef std::map<std::string, RequestRunSettings*> DataMap;
+  DataMap data_map_;
+
+  typedef std::map<std::string,
+                   std::pair<CefRefPtr<CefRequest>, CefRefPtr<CefResponse>>>
+      RedirectDataMap;
+  RedirectDataMap redirect_data_map_;
+};
+
+class TestCompletionCallback : public CefCompletionCallback {
+ public:
+  explicit TestCompletionCallback(const base::Closure& complete_callback)
+      : complete_callback_(complete_callback) {
+    EXPECT_FALSE(complete_callback_.is_null());
+  }
+
+  void OnComplete() override {
+    complete_callback_.Run();
+    complete_callback_.Reset();
+  }
+
+ private:
+  base::Closure complete_callback_;
+
+  IMPLEMENT_REFCOUNTING(TestCompletionCallback);
+};
+
+std::string GetRequestScheme(bool server_backend) {
+  return server_backend ? kRequestSchemeServer : kRequestSchemeCustom;
+}
+
+std::string GetRequestHost(bool server_backend, bool with_port) {
+  if (server_backend) {
+    if (with_port) {
+      std::stringstream ss;
+      ss << kRequestAddressServer << ":" << kRequestPortServer;
+      return ss.str();
+    } else {
+      return kRequestAddressServer;
+    }
+  } else {
+    return kRequestHostCustom;
+  }
+}
+
+std::string GetRequestOrigin(bool server_backend) {
+  return GetRequestScheme(server_backend) + "://" +
+         GetRequestHost(server_backend, true);
+}
+
+void SetUploadData(CefRefPtr<CefRequest> request, const std::string& data) {
+  CefRefPtr<CefPostData> postData = CefPostData::Create();
+  CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create();
+  element->SetToBytes(data.size(), data.c_str());
+  postData->AddElement(element);
+  request->SetPostData(postData);
+}
+
+void SetUploadFile(CefRefPtr<CefRequest> request, const std::string& file) {
+  CefRefPtr<CefPostData> postData = CefPostData::Create();
+  CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create();
+  element->SetToFile(file);
+  postData->AddElement(element);
+  request->SetPostData(postData);
+}
+
+void GetUploadData(CefRefPtr<CefRequest> request, std::string& data) {
+  CefRefPtr<CefPostData> postData = request->GetPostData();
+  EXPECT_TRUE(postData.get());
+  CefPostData::ElementVector elements;
+  postData->GetElements(elements);
+  EXPECT_EQ((size_t)1, elements.size());
+  CefRefPtr<CefPostDataElement> element = elements[0];
+  EXPECT_TRUE(element.get());
+
+  size_t size = element->GetBytesCount();
+
+  data.resize(size);
+  EXPECT_EQ(size, data.size());
+  EXPECT_EQ(size, element->GetBytes(size, const_cast<char*>(data.c_str())));
+}
+
+// Set a cookie so that we can test if it's sent with the request.
+void SetTestCookie(CefRefPtr<CefRequestContext> request_context,
+                   bool server_backend,
+                   const base::Closure& callback) {
+  class Callback : public CefSetCookieCallback {
+   public:
+    explicit Callback(const base::Closure& callback) : callback_(callback) {
+      EXPECT_FALSE(callback_.is_null());
+    }
+
+    void OnComplete(bool success) override {
+      EXPECT_TRUE(success);
+      callback_.Run();
+      callback_.Reset();
+    }
+
+   private:
+    base::Closure callback_;
+
+    IMPLEMENT_REFCOUNTING(Callback);
+  };
+
+  CefCookie cookie;
+  CefString(&cookie.name) = kRequestSendCookieName;
+  CefString(&cookie.value) = "send-cookie-value";
+  CefString(&cookie.domain) = GetRequestHost(server_backend, false);
+  CefString(&cookie.path) = "/";
+  cookie.has_expires = false;
+  EXPECT_TRUE(request_context->GetCookieManager(nullptr)->SetCookie(
+      GetRequestOrigin(server_backend), cookie, new Callback(callback)));
+}
+
+typedef base::Callback<void(bool /* cookie exists */)> GetTestCookieCallback;
+
+// Tests if the save cookie has been set. If set, it will be deleted at the same
+// time.
+void GetTestCookie(CefRefPtr<CefRequestContext> request_context,
+                   bool server_backend,
+                   const GetTestCookieCallback& callback) {
+  class Visitor : public CefCookieVisitor {
+   public:
+    explicit Visitor(const GetTestCookieCallback& callback)
+        : callback_(callback), cookie_exists_(false) {
+      EXPECT_FALSE(callback_.is_null());
+    }
+    ~Visitor() override { callback_.Run(cookie_exists_); }
+
+    bool Visit(const CefCookie& cookie,
+               int count,
+               int total,
+               bool& deleteCookie) override {
+      std::string cookie_name = CefString(&cookie.name);
+      if (cookie_name == kRequestSaveCookieName) {
+        cookie_exists_ = true;
+        deleteCookie = true;
+        return false;
+      }
+      return true;
+    }
+
+   private:
+    GetTestCookieCallback callback_;
+    bool cookie_exists_;
+
+    IMPLEMENT_REFCOUNTING(Visitor);
+  };
+
+  CefRefPtr<CefCookieManager> cookie_manager =
+      request_context->GetCookieManager(nullptr);
+  cookie_manager->VisitUrlCookies(GetRequestOrigin(server_backend), true,
+                                  new Visitor(callback));
+}
+
+std::string GetHeaderValue(const CefRequest::HeaderMap& header_map,
+                           const std::string& header_name_lower) {
+  CefRequest::HeaderMap::const_iterator it = header_map.begin();
+  for (; it != header_map.end(); ++it) {
+    std::string name = it->first;
+    std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+    if (name == header_name_lower)
+      return it->second;
+  }
+  return std::string();
+}
+
+// Verify normal request expectations.
+void VerifyNormalRequest(const RequestRunSettings* settings,
+                         CefRefPtr<CefRequest> request,
+                         bool server_backend) {
+  // Shouldn't get here if we're not following redirects.
+  EXPECT_TRUE(settings->expect_follow_redirect);
+
+  // Verify that the request was sent correctly.
+  TestRequestEqual(settings->request, request, true);
+
+  CefRequest::HeaderMap headerMap;
+  request->GetHeaderMap(headerMap);
+
+  // Check if the default headers were sent.
+  EXPECT_FALSE(GetHeaderValue(headerMap, "user-agent").empty());
+
+  // CEF_SETTINGS_ACCEPT_LANGUAGE value from CefSettings.accept_language_list
+  // set in CefTestSuite::GetSettings() and expanded internally by
+  // ComputeAcceptLanguageFromPref.
+  EXPECT_STREQ("en-GB,en;q=0.9",
+               GetHeaderValue(headerMap, "accept-language").c_str());
+
+  if (server_backend) {
+    EXPECT_FALSE(GetHeaderValue(headerMap, "accept-encoding").empty());
+    EXPECT_STREQ(GetRequestHost(true, true).c_str(),
+                 GetHeaderValue(headerMap, "host").c_str());
+  }
+
+  // Check if the request cookie was sent.
+  const std::string& cookie_value = GetHeaderValue(headerMap, "cookie");
+  bool has_send_cookie = false;
+  if (!cookie_value.empty() &&
+      cookie_value.find(kRequestSendCookieName) != std::string::npos) {
+    has_send_cookie = true;
+  }
+
+  EXPECT_EQ(settings->expect_send_cookie, has_send_cookie);
+}
+
+// Populate normal response contents.
+void GetNormalResponse(const RequestRunSettings* settings,
+                       CefRefPtr<CefResponse> response) {
+  EXPECT_TRUE(settings->response);
+  if (!settings->response)
+    return;
+
+  response->SetStatus(settings->response->GetStatus());
+  response->SetStatusText(settings->response->GetStatusText());
+  response->SetMimeType(settings->response->GetMimeType());
+
+  CefResponse::HeaderMap headerMap;
+  settings->response->GetHeaderMap(headerMap);
+
+  if (settings->expect_save_cookie) {
+    std::stringstream ss;
+    ss << kRequestSaveCookieName << "="
+       << "save-cookie-value";
+    headerMap.insert(std::make_pair("Set-Cookie", ss.str()));
+  }
+
+  response->SetHeaderMap(headerMap);
+}
+
+// Based on https://en.wikipedia.org/wiki/Basic_access_authentication#Protocol
+void GetAuthResponse(CefRefPtr<CefResponse> response) {
+  response->SetStatus(401);
+  response->SetStatusText("Unauthorized");
+  response->SetMimeType("text/html");
+
+  CefResponse::HeaderMap headerMap;
+  headerMap.insert(
+      std::make_pair("WWW-Authenticate", "Basic realm=\"Test Realm\""));
+  response->SetHeaderMap(headerMap);
+}
+
+bool IsAuthorized(CefRefPtr<CefRequest> request,
+                  const std::string& username,
+                  const std::string& password) {
+  const std::string& authHeader = request->GetHeaderByName("Authorization");
+  if (authHeader.empty())
+    return false;
+
+  if (authHeader.find("Basic ") == 0) {
+    const std::string& base64 = authHeader.substr(6);
+    CefRefPtr<CefBinaryValue> data = CefBase64Decode(base64);
+    EXPECT_TRUE(data);
+    if (!data) {
+      LOG(ERROR) << "Failed to decode Authorization value: " << base64;
+      return false;
+    }
+
+    std::string decoded;
+    decoded.resize(data->GetSize());
+    data->GetData(&decoded[0], data->GetSize(), 0);
+
+    const std::string& expected = username + ":" + password;
+    EXPECT_STREQ(expected.c_str(), decoded.c_str());
+    return decoded == expected;
+  }
+
+  LOG(ERROR) << "Unexpected Authorization value: " << authHeader;
+  return false;
+}
+
+// SCHEME HANDLER BACKEND
+
+// Serves request responses.
+class RequestSchemeHandlerOld : public CefResourceHandler {
+ public:
+  RequestSchemeHandlerOld(RequestRunSettings* settings,
+                          const base::Closure& destroy_callback)
+      : settings_(settings), destroy_callback_(destroy_callback) {}
+
+  ~RequestSchemeHandlerOld() override {
+    EXPECT_EQ(1, cancel_ct_);
+    destroy_callback_.Run();
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+    VerifyNormalRequest(settings_, request, false);
+
+    // HEAD requests are identical to GET requests except no response data is
+    // sent.
+    if (request->GetMethod() != "HEAD")
+      response_data_ = settings_->response_data;
+
+    // Continue immediately.
+    callback->Continue();
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+    GetNormalResponse(settings_, response);
+    response_length = response_data_.length();
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+
+    bool has_data = false;
+    bytes_read = 0;
+
+    size_t size = response_data_.length();
+    if (offset_ < size) {
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(size - offset_));
+      memcpy(data_out, response_data_.c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  // |settings_| is not owned by this object.
+  RequestRunSettings* settings_;
+  base::Closure destroy_callback_;
+
+  std::string response_data_;
+  size_t offset_ = 0;
+
+  int cancel_ct_ = 0;
+
+  IMPLEMENT_REFCOUNTING(RequestSchemeHandlerOld);
+  DISALLOW_COPY_AND_ASSIGN(RequestSchemeHandlerOld);
+};
+
+class RequestSchemeHandler : public CefResourceHandler {
+ public:
+  RequestSchemeHandler(RequestRunSettings* settings,
+                       const base::Closure& destroy_callback)
+      : settings_(settings), destroy_callback_(destroy_callback) {}
+
+  ~RequestSchemeHandler() override {
+    EXPECT_EQ(1, cancel_ct_);
+    destroy_callback_.Run();
+  }
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+    VerifyNormalRequest(settings_, request, false);
+
+    // HEAD requests are identical to GET requests except no response data is
+    // sent.
+    if (request->GetMethod() != "HEAD")
+      response_data_ = settings_->response_data;
+
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    return false;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+    GetNormalResponse(settings_, response);
+    response_length = response_data_.length() - offset_;
+  }
+
+  bool Skip(int64 bytes_to_skip,
+            int64& bytes_skipped,
+            CefRefPtr<CefResourceSkipCallback> callback) override {
+    size_t size = response_data_.length();
+    if (offset_ < size) {
+      bytes_skipped =
+          std::min(bytes_to_skip, static_cast<int64>(size - offset_));
+      offset_ += bytes_skipped;
+    } else {
+      bytes_skipped = ERR_FAILED;
+    }
+
+    return bytes_skipped > 0;
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    // Default to response complete.
+    bool has_data = false;
+    bytes_read = 0;
+
+    size_t size = response_data_.length();
+    if (offset_ < size) {
+      int transfer_size =
+          std::min(bytes_to_read, static_cast<int>(size - offset_));
+      memcpy(data_out, response_data_.c_str() + offset_, transfer_size);
+      offset_ += transfer_size;
+
+      bytes_read = transfer_size;
+      has_data = true;
+    }
+
+    return has_data;
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    bytes_read = -2;
+    return false;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  // |settings_| is not owned by this object.
+  RequestRunSettings* settings_;
+  base::Closure destroy_callback_;
+
+  std::string response_data_;
+  size_t offset_ = 0;
+
+  int cancel_ct_ = 0;
+
+  IMPLEMENT_REFCOUNTING(RequestSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(RequestSchemeHandler);
+};
+
+// Serves redirect request responses.
+class RequestRedirectSchemeHandlerOld : public CefResourceHandler {
+ public:
+  RequestRedirectSchemeHandlerOld(CefRefPtr<CefRequest> request,
+                                  CefRefPtr<CefResponse> response,
+                                  const base::Closure& destroy_callback)
+      : request_(request),
+        response_(response),
+        destroy_callback_(destroy_callback) {}
+
+  ~RequestRedirectSchemeHandlerOld() override {
+    EXPECT_EQ(1, cancel_ct_);
+    destroy_callback_.Run();
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+
+    // Verify that the request was sent correctly.
+    TestRequestEqual(request_, request, true);
+
+    // Continue immediately.
+    callback->Continue();
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+
+    response->SetStatus(response_->GetStatus());
+    response->SetStatusText(response_->GetStatusText());
+    response->SetMimeType(response_->GetMimeType());
+
+    CefResponse::HeaderMap headerMap;
+    response_->GetHeaderMap(headerMap);
+    response->SetHeaderMap(headerMap);
+
+    response_length = 0;
+  }
+
+  bool ReadResponse(void* response_data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+    NOTREACHED();
+    return false;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  CefRefPtr<CefRequest> request_;
+  CefRefPtr<CefResponse> response_;
+  base::Closure destroy_callback_;
+
+  int cancel_ct_ = 0;
+
+  IMPLEMENT_REFCOUNTING(RequestRedirectSchemeHandlerOld);
+  DISALLOW_COPY_AND_ASSIGN(RequestRedirectSchemeHandlerOld);
+};
+
+class RequestRedirectSchemeHandler : public CefResourceHandler {
+ public:
+  RequestRedirectSchemeHandler(CefRefPtr<CefRequest> request,
+                               CefRefPtr<CefResponse> response,
+                               const base::Closure& destroy_callback)
+      : request_(request),
+        response_(response),
+        destroy_callback_(destroy_callback) {}
+
+  ~RequestRedirectSchemeHandler() override {
+    EXPECT_EQ(1, cancel_ct_);
+    destroy_callback_.Run();
+  }
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    // Verify that the request was sent correctly.
+    TestRequestEqual(request_, request, true);
+
+    // Continue immediately.
+    handle_request = true;
+    return true;
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    return false;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+
+    response->SetStatus(response_->GetStatus());
+    response->SetStatusText(response_->GetStatusText());
+    response->SetMimeType(response_->GetMimeType());
+
+    CefResponse::HeaderMap headerMap;
+    response_->GetHeaderMap(headerMap);
+    response->SetHeaderMap(headerMap);
+
+    response_length = 0;
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    bytes_read = -1;
+    return false;
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    bytes_read = -2;
+    return false;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  CefRefPtr<CefRequest> request_;
+  CefRefPtr<CefResponse> response_;
+  base::Closure destroy_callback_;
+
+  int cancel_ct_ = 0;
+
+  IMPLEMENT_REFCOUNTING(RequestRedirectSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(RequestRedirectSchemeHandler);
+};
+
+// Resource handler implementation that never completes. Used to test
+// destruction handling behavior for in-progress requests.
+class IncompleteSchemeHandlerOld : public CefResourceHandler {
+ public:
+  IncompleteSchemeHandlerOld(RequestRunSettings* settings,
+                             const base::Closure& destroy_callback)
+      : settings_(settings), destroy_callback_(destroy_callback) {
+    EXPECT_NE(settings_->incomplete_type, RequestRunSettings::INCOMPLETE_NONE);
+  }
+
+  ~IncompleteSchemeHandlerOld() override {
+    EXPECT_EQ(1, process_request_ct_);
+    EXPECT_EQ(1, cancel_ct_);
+
+    if (settings_->incomplete_type ==
+        RequestRunSettings::INCOMPLETE_READ_RESPONSE) {
+      EXPECT_EQ(1, get_response_headers_ct_);
+      EXPECT_EQ(1, read_response_ct_);
+    } else {
+      EXPECT_EQ(0, get_response_headers_ct_);
+      EXPECT_EQ(0, read_response_ct_);
+    }
+
+    destroy_callback_.Run();
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+
+    process_request_ct_++;
+
+    if (settings_->incomplete_type ==
+        RequestRunSettings::INCOMPLETE_PROCESS_REQUEST) {
+      // Never release or execute this callback.
+      incomplete_callback_ = callback;
+    } else {
+      callback->Continue();
+    }
+    return true;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(settings_->incomplete_type,
+              RequestRunSettings::INCOMPLETE_READ_RESPONSE);
+
+    get_response_headers_ct_++;
+
+    response->SetStatus(settings_->response->GetStatus());
+    response->SetStatusText(settings_->response->GetStatusText());
+    response->SetMimeType(settings_->response->GetMimeType());
+
+    CefResponse::HeaderMap headerMap;
+    settings_->response->GetHeaderMap(headerMap);
+    settings_->response->SetHeaderMap(headerMap);
+
+    response_length = static_cast<int64>(settings_->response_data.size());
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(settings_->incomplete_type,
+              RequestRunSettings::INCOMPLETE_READ_RESPONSE);
+
+    read_response_ct_++;
+
+    // Never release or execute this callback.
+    incomplete_callback_ = callback;
+    bytes_read = 0;
+    return true;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  RequestRunSettings* const settings_;
+  const base::Closure destroy_callback_;
+
+  int process_request_ct_ = 0;
+  int get_response_headers_ct_ = 0;
+  int read_response_ct_ = 0;
+  int cancel_ct_ = 0;
+
+  CefRefPtr<CefCallback> incomplete_callback_;
+
+  IMPLEMENT_REFCOUNTING(IncompleteSchemeHandlerOld);
+  DISALLOW_COPY_AND_ASSIGN(IncompleteSchemeHandlerOld);
+};
+
+class IncompleteSchemeHandler : public CefResourceHandler {
+ public:
+  IncompleteSchemeHandler(RequestRunSettings* settings,
+                          const base::Closure& destroy_callback)
+      : settings_(settings), destroy_callback_(destroy_callback) {
+    EXPECT_NE(settings_->incomplete_type, RequestRunSettings::INCOMPLETE_NONE);
+  }
+
+  ~IncompleteSchemeHandler() override {
+    EXPECT_EQ(1, open_ct_);
+    EXPECT_EQ(1, cancel_ct_);
+
+    if (settings_->incomplete_type ==
+        RequestRunSettings::INCOMPLETE_READ_RESPONSE) {
+      EXPECT_EQ(1, get_response_headers_ct_);
+      EXPECT_EQ(1, read_ct_);
+    } else {
+      EXPECT_EQ(0, get_response_headers_ct_);
+      EXPECT_EQ(0, read_ct_);
+    }
+
+    destroy_callback_.Run();
+  }
+
+  bool Open(CefRefPtr<CefRequest> request,
+            bool& handle_request,
+            CefRefPtr<CefCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+
+    open_ct_++;
+
+    if (settings_->incomplete_type ==
+        RequestRunSettings::INCOMPLETE_PROCESS_REQUEST) {
+      // Never release or execute this callback.
+      incomplete_open_callback_ = callback;
+    } else {
+      // Continue immediately.
+      handle_request = true;
+    }
+    return true;
+  }
+
+  bool ProcessRequest(CefRefPtr<CefRequest> request,
+                      CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    return false;
+  }
+
+  void GetResponseHeaders(CefRefPtr<CefResponse> response,
+                          int64& response_length,
+                          CefString& redirectUrl) override {
+    EXPECT_IO_THREAD();
+    EXPECT_EQ(settings_->incomplete_type,
+              RequestRunSettings::INCOMPLETE_READ_RESPONSE);
+
+    get_response_headers_ct_++;
+
+    response->SetStatus(settings_->response->GetStatus());
+    response->SetStatusText(settings_->response->GetStatusText());
+    response->SetMimeType(settings_->response->GetMimeType());
+
+    CefResponse::HeaderMap headerMap;
+    settings_->response->GetHeaderMap(headerMap);
+    settings_->response->SetHeaderMap(headerMap);
+
+    response_length = static_cast<int64>(settings_->response_data.size());
+  }
+
+  bool Read(void* data_out,
+            int bytes_to_read,
+            int& bytes_read,
+            CefRefPtr<CefResourceReadCallback> callback) override {
+    EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
+    EXPECT_EQ(settings_->incomplete_type,
+              RequestRunSettings::INCOMPLETE_READ_RESPONSE);
+
+    read_ct_++;
+
+    // Never release or execute this callback.
+    incomplete_read_callback_ = callback;
+    bytes_read = 0;
+    return true;
+  }
+
+  bool ReadResponse(void* data_out,
+                    int bytes_to_read,
+                    int& bytes_read,
+                    CefRefPtr<CefCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    bytes_read = -2;
+    return false;
+  }
+
+  void Cancel() override {
+    EXPECT_IO_THREAD();
+    cancel_ct_++;
+  }
+
+ private:
+  RequestRunSettings* const settings_;
+  const base::Closure destroy_callback_;
+
+  int open_ct_ = 0;
+  int get_response_headers_ct_ = 0;
+  int read_ct_ = 0;
+  int cancel_ct_ = 0;
+
+  CefRefPtr<CefCallback> incomplete_open_callback_;
+  CefRefPtr<CefResourceReadCallback> incomplete_read_callback_;
+
+  IMPLEMENT_REFCOUNTING(IncompleteSchemeHandler);
+  DISALLOW_COPY_AND_ASSIGN(IncompleteSchemeHandler);
+};
+
+class RequestSchemeHandlerFactory : public CefSchemeHandlerFactory {
+ public:
+  RequestSchemeHandlerFactory() {}
+
+  CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       const CefString& scheme_name,
+                                       CefRefPtr<CefRequest> request) override {
+    EXPECT_IO_THREAD();
+
+    handler_create_ct_++;
+    const base::Closure destroy_callback =
+        base::Bind(&RequestSchemeHandlerFactory::OnHandlerDestroyed, this);
+
+    RequestDataMap::Entry entry = data_map_.Find(request->GetURL());
+    if (entry.type == RequestDataMap::Entry::TYPE_NORMAL) {
+      if (entry.settings->incomplete_type ==
+          RequestRunSettings::INCOMPLETE_NONE) {
+        if (TestOldResourceAPI()) {
+          return new RequestSchemeHandlerOld(entry.settings, destroy_callback);
+        }
+        return new RequestSchemeHandler(entry.settings, destroy_callback);
+      }
+
+      if (TestOldResourceAPI()) {
+        return new IncompleteSchemeHandlerOld(entry.settings, destroy_callback);
+      }
+      return new IncompleteSchemeHandler(entry.settings, destroy_callback);
+    } else if (entry.type == RequestDataMap::Entry::TYPE_REDIRECT) {
+      if (TestOldResourceAPI()) {
+        return new RequestRedirectSchemeHandlerOld(
+            entry.redirect_request, entry.redirect_response, destroy_callback);
+      }
+      return new RequestRedirectSchemeHandler(
+          entry.redirect_request, entry.redirect_response, destroy_callback);
+    }
+
+    // Unknown test.
+    ADD_FAILURE();
+    return nullptr;
+  }
+
+  void SetOwnerTaskRunner(CefRefPtr<CefTaskRunner> task_runner) {
+    data_map_.SetOwnerTaskRunner(task_runner);
+  }
+
+  void AddSchemeHandler(RequestRunSettings* settings) {
+    const std::string& scheme = GetRequestScheme(false);
+
+    // Verify that the scheme is correct.
+    const std::string& url = settings->request->GetURL();
+    EXPECT_EQ(0U, url.find(scheme));
+
+    if (settings->redirect_request) {
+      // Verify that the scheme is correct.
+      const std::string& redirect_url = settings->redirect_request->GetURL();
+      EXPECT_EQ(0U, redirect_url.find(scheme));
+    }
+
+    data_map_.AddSchemeHandler(settings);
+  }
+
+  void OnHandlerDestroyed() {
+    if (!CefCurrentlyOn(TID_IO)) {
+      CefPostTask(
+          TID_IO,
+          base::Bind(&RequestSchemeHandlerFactory::OnHandlerDestroyed, this));
+      return;
+    }
+
+    handler_destroy_ct_++;
+
+    MaybeShutdown();
+  }
+
+  void Shutdown(const base::Closure& complete_callback) {
+    if (!CefCurrentlyOn(TID_IO)) {
+      CefPostTask(TID_IO, base::Bind(&RequestSchemeHandlerFactory::Shutdown,
+                                     this, complete_callback));
+      return;
+    }
+
+    EXPECT_TRUE(shutdown_callback_.is_null());
+    shutdown_callback_ = complete_callback;
+
+    data_map_.SetOwnerTaskRunner(nullptr);
+
+    MaybeShutdown();
+  }
+
+ private:
+  void MaybeShutdown() {
+    if (!shutdown_callback_.is_null() &&
+        handler_create_ct_ == handler_destroy_ct_) {
+      shutdown_callback_.Run();
+      shutdown_callback_.Reset();
+    }
+  }
+
+  RequestDataMap data_map_;
+
+  int handler_create_ct_ = 0;
+  int handler_destroy_ct_ = 0;
+  base::Closure shutdown_callback_;
+
+  IMPLEMENT_REFCOUNTING(RequestSchemeHandlerFactory);
+  DISALLOW_COPY_AND_ASSIGN(RequestSchemeHandlerFactory);
+};
+
+// SERVER BACKEND
+
+// HTTP server handler.
+class RequestServerHandler : public CefServerHandler {
+ public:
+  RequestServerHandler()
+      : initialized_(false),
+        expected_connection_ct_(-1),
+        actual_connection_ct_(0),
+        expected_http_request_ct_(-1),
+        actual_http_request_ct_(0) {}
+
+  virtual ~RequestServerHandler() { RunCompleteCallback(false); }
+
+  // Must be called before CreateServer().
+  void AddSchemeHandler(RequestRunSettings* settings) {
+    EXPECT_FALSE(initialized_);
+    data_map_.AddSchemeHandler(settings);
+  }
+
+  // Must be called before CreateServer().
+  void SetExpectedRequestCount(int count) {
+    EXPECT_FALSE(initialized_);
+    expected_connection_ct_ = expected_http_request_ct_ = count;
+  }
+
+  // |complete_callback| will be executed on the UI thread after the server is
+  // started.
+  void CreateServer(const base::Closure& complete_callback) {
+    EXPECT_UI_THREAD();
+
+    if (expected_connection_ct_ < 0) {
+      // Default to the assumption of one request per registered URL.
+      SetExpectedRequestCount(static_cast<int>(data_map_.size()));
+    }
+
+    EXPECT_FALSE(initialized_);
+    initialized_ = true;
+
+    EXPECT_TRUE(complete_callback_.is_null());
+    complete_callback_ = complete_callback;
+
+    CefServer::CreateServer(kRequestAddressServer, kRequestPortServer, 10,
+                            this);
+  }
+
+  // Results in a call to VerifyResults() and eventual execution of the
+  // |complete_callback| on the UI thread via RequestServerHandler destruction.
+  void ShutdownServer(const base::Closure& complete_callback) {
+    EXPECT_UI_THREAD();
+
+    EXPECT_TRUE(complete_callback_.is_null());
+    complete_callback_ = complete_callback;
+
+    EXPECT_TRUE(server_);
+    if (server_)
+      server_->Shutdown();
+  }
+
+  void OnServerCreated(CefRefPtr<CefServer> server) override {
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(server->IsRunning());
+    EXPECT_FALSE(server->HasConnection());
+
+    EXPECT_FALSE(got_server_created_);
+    got_server_created_.yes();
+
+    EXPECT_FALSE(server_);
+    server_ = server;
+
+    EXPECT_FALSE(server_runner_);
+    server_runner_ = server_->GetTaskRunner();
+    EXPECT_TRUE(server_runner_);
+    EXPECT_TRUE(server_runner_->BelongsToCurrentThread());
+
+    CefPostTask(TID_UI, base::Bind(&RequestServerHandler::RunCompleteCallback,
+                                   this, true));
+  }
+
+  void OnServerDestroyed(CefRefPtr<CefServer> server) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_FALSE(server->IsRunning());
+    EXPECT_FALSE(server->HasConnection());
+
+    EXPECT_FALSE(got_server_destroyed_);
+    got_server_destroyed_.yes();
+
+    data_map_.SetOwnerTaskRunner(nullptr);
+    server_ = nullptr;
+
+    VerifyResults();
+  }
+
+  void OnClientConnected(CefRefPtr<CefServer> server,
+                         int connection_id) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(server->HasConnection());
+    EXPECT_TRUE(server->IsValidConnection(connection_id));
+
+    EXPECT_TRUE(connection_id_set_.find(connection_id) ==
+                connection_id_set_.end());
+    connection_id_set_.insert(connection_id);
+
+    actual_connection_ct_++;
+  }
+
+  void OnClientDisconnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_FALSE(server->IsValidConnection(connection_id));
+
+    ConnectionIdSet::iterator it = connection_id_set_.find(connection_id);
+    EXPECT_TRUE(it != connection_id_set_.end());
+    connection_id_set_.erase(it);
+  }
+
+  void OnHttpRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     const CefString& client_address,
+                     CefRefPtr<CefRequest> request) override {
+    EXPECT_TRUE(VerifyServer(server));
+    EXPECT_TRUE(VerifyConnection(connection_id));
+    EXPECT_FALSE(client_address.empty());
+
+    // Log the requests for better error reporting.
+    request_log_ += request->GetMethod().ToString() + " " +
+                    request->GetURL().ToString() + "\n";
+
+    HandleRequest(server, connection_id, request);
+
+    actual_http_request_ct_++;
+  }
+
+  void OnWebSocketRequest(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const CefString& client_address,
+                          CefRefPtr<CefRequest> request,
+                          CefRefPtr<CefCallback> callback) override {
+    NOTREACHED();
+  }
+
+  void OnWebSocketConnected(CefRefPtr<CefServer> server,
+                            int connection_id) override {
+    NOTREACHED();
+  }
+
+  void OnWebSocketMessage(CefRefPtr<CefServer> server,
+                          int connection_id,
+                          const void* data,
+                          size_t data_size) override {
+    NOTREACHED();
+  }
+
+ private:
+  bool RunningOnServerThread() {
+    return server_runner_ && server_runner_->BelongsToCurrentThread();
+  }
+
+  bool VerifyServer(CefRefPtr<CefServer> server) {
+    V_DECLARE();
+    V_EXPECT_TRUE(RunningOnServerThread());
+    V_EXPECT_TRUE(server);
+    V_EXPECT_TRUE(server_);
+    V_EXPECT_TRUE(server->GetAddress().ToString() ==
+                  server_->GetAddress().ToString());
+    V_RETURN();
+  }
+
+  bool VerifyConnection(int connection_id) {
+    return connection_id_set_.find(connection_id) != connection_id_set_.end();
+  }
+
+  void VerifyResults() {
+    EXPECT_TRUE(RunningOnServerThread());
+
+    EXPECT_TRUE(got_server_created_);
+    EXPECT_TRUE(got_server_destroyed_);
+    EXPECT_TRUE(connection_id_set_.empty());
+    EXPECT_EQ(expected_connection_ct_, actual_connection_ct_) << request_log_;
+    EXPECT_EQ(expected_http_request_ct_, actual_http_request_ct_)
+        << request_log_;
+  }
+
+  void HandleRequest(CefRefPtr<CefServer> server,
+                     int connection_id,
+                     CefRefPtr<CefRequest> request) {
+    RequestDataMap::Entry entry = data_map_.Find(request->GetURL());
+    if (entry.type == RequestDataMap::Entry::TYPE_NORMAL) {
+      const bool needs_auth = entry.settings->expect_authentication &&
+                              !IsAuthorized(request, entry.settings->username,
+                                            entry.settings->password);
+      if (needs_auth) {
+        HandleAuthRequest(server, connection_id, request);
+        return;
+      }
+
+      HandleNormalRequest(server, connection_id, request, entry.settings);
+    } else if (entry.type == RequestDataMap::Entry::TYPE_REDIRECT) {
+      HandleRedirectRequest(server, connection_id, request,
+                            entry.redirect_request, entry.redirect_response);
+    } else {
+      // Unknown test.
+      ADD_FAILURE() << "url: " << request->GetURL().ToString();
+      server->SendHttp500Response(connection_id, "Unknown test");
+    }
+  }
+
+  void HandleAuthRequest(CefRefPtr<CefServer> server,
+                         int connection_id,
+                         CefRefPtr<CefRequest> request) {
+    CefRefPtr<CefResponse> response = CefResponse::Create();
+    GetAuthResponse(response);
+    SendResponse(server, connection_id, response, std::string());
+  }
+
+  void HandleNormalRequest(CefRefPtr<CefServer> server,
+                           int connection_id,
+                           CefRefPtr<CefRequest> request,
+                           RequestRunSettings* settings) {
+    VerifyNormalRequest(settings, request, true);
+
+    CefRefPtr<CefResponse> response = CefResponse::Create();
+    GetNormalResponse(settings, response);
+
+    // HEAD requests are identical to GET requests except no response data is
+    // sent.
+    std::string response_data;
+    if (request->GetMethod() != "HEAD") {
+      size_t expected_offset = settings->expected_download_offset;
+      response_data = settings->response_data.substr(expected_offset);
+    }
+
+    SendResponse(server, connection_id, response, response_data);
+  }
+
+  void HandleRedirectRequest(CefRefPtr<CefServer> server,
+                             int connection_id,
+                             CefRefPtr<CefRequest> request,
+                             CefRefPtr<CefRequest> redirect_request,
+                             CefRefPtr<CefResponse> redirect_response) {
+    if (redirect_response->GetStatus() == 302) {
+      // Simulate wrong copying of POST-specific headers Content-Type and
+      // Content-Length. A 302 redirect should end up in a GET request and
+      // these headers should not propagate from a 302 POST-to-GET redirect.
+      CefResponse::HeaderMap redirectHeaderMap;
+      redirect_response->GetHeaderMap(redirectHeaderMap);
+      redirectHeaderMap.insert(
+          std::make_pair("content-type", "application/x-www-form-urlencoded"));
+      redirectHeaderMap.insert(std::make_pair("content-length", "0"));
+      redirect_response->SetHeaderMap(redirectHeaderMap);
+    }
+
+    // Verify that the request was sent correctly.
+    TestRequestEqual(redirect_request, request, true);
+
+    SendResponse(server, connection_id, redirect_response, std::string());
+  }
+
+  void SendResponse(CefRefPtr<CefServer> server,
+                    int connection_id,
+                    CefRefPtr<CefResponse> response,
+                    const std::string& response_data) {
+    const int response_code = response->GetStatus();
+    if (response_code <= 0) {
+      // Intentionally not responding for incomplete request tests.
+      return;
+    }
+
+    const CefString& content_type = response->GetMimeType();
+    int64 content_length = static_cast<int64>(response_data.size());
+
+    CefResponse::HeaderMap extra_headers;
+    response->GetHeaderMap(extra_headers);
+
+    server->SendHttpResponse(connection_id, response_code, content_type,
+                             content_length, extra_headers);
+
+    if (response_data == kIncompleteDoNotSendData) {
+      // Intentionally not sending data for incomplete request tests.
+      return;
+    }
+
+    if (content_length != 0) {
+      server->SendRawData(connection_id, response_data.data(),
+                          response_data.size());
+      server->CloseConnection(connection_id);
+    }
+
+    // The connection should be closed.
+    EXPECT_FALSE(server->IsValidConnection(connection_id));
+  }
+
+  void RunCompleteCallback(bool startup) {
+    EXPECT_UI_THREAD();
+
+    if (startup) {
+      // Transfer DataMap ownership to the server thread.
+      data_map_.SetOwnerTaskRunner(server_->GetTaskRunner());
+    }
+
+    EXPECT_FALSE(complete_callback_.is_null());
+    complete_callback_.Run();
+    complete_callback_.Reset();
+  }
+
+  RequestDataMap data_map_;
+
+  CefRefPtr<CefServer> server_;
+  CefRefPtr<CefTaskRunner> server_runner_;
+  bool initialized_;
+
+  // Only accessed on the UI thread.
+  base::Closure complete_callback_;
+
+  // After initialization the below members are only accessed on the server
+  // thread.
+
+  TrackCallback got_server_created_;
+  TrackCallback got_server_destroyed_;
+
+  typedef std::set<int> ConnectionIdSet;
+  ConnectionIdSet connection_id_set_;
+
+  int expected_connection_ct_;
+  int actual_connection_ct_;
+  int expected_http_request_ct_;
+  int actual_http_request_ct_;
+
+  std::string request_log_;
+
+  IMPLEMENT_REFCOUNTING(RequestServerHandler);
+  DISALLOW_COPY_AND_ASSIGN(RequestServerHandler);
+};
+
+// URLREQUEST CLIENT
+
+// Implementation of CefURLRequestClient that stores response information.
+class RequestClient : public CefURLRequestClient {
+ public:
+  typedef base::Callback<void(CefRefPtr<RequestClient>)>
+      RequestCompleteCallback;
+
+  explicit RequestClient(const RequestCompleteCallback& complete_callback)
+      : complete_callback_(complete_callback) {
+    EXPECT_FALSE(complete_callback_.is_null());
+  }
+
+  void OnRequestComplete(CefRefPtr<CefURLRequest> request) override {
+    request_complete_ct_++;
+
+    request_ = request->GetRequest();
+    EXPECT_TRUE(request_->IsReadOnly());
+    status_ = request->GetRequestStatus();
+    error_code_ = request->GetRequestError();
+    response_was_cached_ = request->ResponseWasCached();
+    response_ = request->GetResponse();
+    if (response_) {
+      EXPECT_TRUE(response_->IsReadOnly());
+    }
+
+    complete_callback_.Run(this);
+  }
+
+  void OnUploadProgress(CefRefPtr<CefURLRequest> request,
+                        int64 current,
+                        int64 total) override {
+    upload_progress_ct_++;
+    upload_total_ = total;
+  }
+
+  void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
+                          int64 current,
+                          int64 total) override {
+    response_ = request->GetResponse();
+    EXPECT_TRUE(response_.get());
+    EXPECT_TRUE(response_->IsReadOnly());
+    download_progress_ct_++;
+    download_total_ = total;
+  }
+
+  void OnDownloadData(CefRefPtr<CefURLRequest> request,
+                      const void* data,
+                      size_t data_length) override {
+    response_ = request->GetResponse();
+    EXPECT_TRUE(response_.get());
+    EXPECT_TRUE(response_->IsReadOnly());
+    download_data_ct_++;
+    download_data_ += std::string(static_cast<const char*>(data), data_length);
+  }
+
+  bool GetAuthCredentials(bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) override {
+    auth_credentials_ct_++;
+    if (has_authentication_) {
+      callback->Continue(username_, password_);
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  const RequestCompleteCallback complete_callback_;
+
+ public:
+  bool has_authentication_ = false;
+  std::string username_;
+  std::string password_;
+
+  int request_complete_ct_ = 0;
+  int upload_progress_ct_ = 0;
+  int download_progress_ct_ = 0;
+  int download_data_ct_ = 0;
+  int auth_credentials_ct_ = 0;
+
+  int64 upload_total_ = 0;
+  int64 download_total_ = 0;
+  std::string download_data_;
+  CefRefPtr<CefRequest> request_;
+  CefURLRequest::Status status_ = UR_UNKNOWN;
+  CefURLRequest::ErrorCode error_code_ = ERR_NONE;
+  CefRefPtr<CefResponse> response_;
+  bool response_was_cached_ = false;
+
+ private:
+  IMPLEMENT_REFCOUNTING(RequestClient);
+};
+
+// SHARED TEST RUNNER
+
+// Executes the tests.
+class RequestTestRunner : public base::RefCountedThreadSafe<RequestTestRunner> {
+ public:
+  typedef base::Callback<void(const base::Closure&)> TestCallback;
+
+  RequestTestRunner(bool is_browser_process,
+                    bool is_server_backend,
+                    bool use_frame_method,
+                    bool run_in_browser_process,
+                    const base::Closure& incomplete_request_callback)
+      : is_browser_process_(is_browser_process),
+        is_server_backend_(is_server_backend),
+        use_frame_method_(use_frame_method),
+        run_in_browser_process_(run_in_browser_process),
+        incomplete_request_callback_(incomplete_request_callback) {
+    owner_task_runner_ = CefTaskRunner::GetForCurrentThread();
+    EXPECT_TRUE(owner_task_runner_.get());
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+
+// Helper macro for registering test callbacks.
+#define REGISTER_TEST(test_mode, setup_method, run_method)                    \
+  RegisterTest(test_mode, base::Bind(&RequestTestRunner::setup_method, this), \
+               base::Bind(&RequestTestRunner::run_method, this));
+
+    // Register the test callbacks.
+    REGISTER_TEST(REQTEST_GET, SetupGetTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_NODATA, SetupGetNoDataTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_PARTIAL_CONTENT, SetupGetPartialContentTest,
+                  SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_ALLOWCOOKIES, SetupGetAllowCookiesTest,
+                  SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_REDIRECT, SetupGetRedirectTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_REDIRECT_STOP, SetupGetRedirectStopTest,
+                  SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_REDIRECT_LOCATION, SetupGetRedirectLocationTest,
+                  SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_REFERRER, SetupGetReferrerTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_GET_AUTH, SetupGetAuthTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_POST, SetupPostTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_POST_FILE, SetupPostFileTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_POST_WITHPROGRESS, SetupPostWithProgressTest,
+                  SingleRunTest);
+    REGISTER_TEST(REQTEST_POST_REDIRECT, SetupPostRedirectTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_POST_REDIRECT_TOGET, SetupPostRedirectToGetTest,
+                  SingleRunTest);
+    REGISTER_TEST(REQTEST_HEAD, SetupHeadTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_WITH_CONTROL, SetupCacheWithControlTest,
+                  MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_WITHOUT_CONTROL, SetupCacheWithoutControlTest,
+                  MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_SKIP_FLAG, SetupCacheSkipFlagTest,
+                  MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_SKIP_HEADER, SetupCacheSkipHeaderTest,
+                  MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_ONLY_FAILURE_FLAG,
+                  SetupCacheOnlyFailureFlagTest, MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_ONLY_FAILURE_HEADER,
+                  SetupCacheOnlyFailureHeaderTest, MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_ONLY_SUCCESS_FLAG,
+                  SetupCacheOnlySuccessFlagTest, MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_ONLY_SUCCESS_HEADER,
+                  SetupCacheOnlySuccessHeaderTest, MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_DISABLE_FLAG, SetupCacheDisableFlagTest,
+                  MultipleRunTest);
+    REGISTER_TEST(REQTEST_CACHE_DISABLE_HEADER, SetupCacheDisableHeaderTest,
+                  MultipleRunTest);
+    REGISTER_TEST(REQTEST_INCOMPLETE_PROCESS_REQUEST,
+                  SetupIncompleteProcessRequestTest, SingleRunTest);
+    REGISTER_TEST(REQTEST_INCOMPLETE_READ_RESPONSE,
+                  SetupIncompleteReadResponseTest, SingleRunTest);
+  }
+
+  void Destroy() {
+    owner_task_runner_ = nullptr;
+    request_context_ = nullptr;
+    incomplete_request_callback_.Reset();
+  }
+
+  // Called in the browser process to set the request context that will be used
+  // when creating the URL request.
+  void SetRequestContext(CefRefPtr<CefRequestContext> request_context) {
+    request_context_ = request_context;
+  }
+  CefRefPtr<CefRequestContext> GetRequestContext() const {
+    return request_context_;
+  }
+
+  // Called in both the browser and render process to setup the test.
+  void SetupTest(RequestTestMode test_mode,
+                 const base::Closure& complete_callback) {
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+
+    const base::Closure& safe_complete_callback = base::Bind(
+        &RequestTestRunner::CompleteOnCorrectThread, this, complete_callback);
+
+    TestMap::const_iterator it = test_map_.find(test_mode);
+    if (it != test_map_.end()) {
+      it->second.setup.Run(base::Bind(&RequestTestRunner::SetupContinue, this,
+                                      safe_complete_callback));
+    } else {
+      // Unknown test.
+      ADD_FAILURE();
+      complete_callback.Run();
+    }
+  }
+
+  // Called in either the browser or render process to run the test.
+  void RunTest(RequestTestMode test_mode,
+               CefRefPtr<CefFrame> frame,
+               const base::Closure& complete_callback) {
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+
+    frame_ = frame;
+
+    const base::Closure& safe_complete_callback = base::Bind(
+        &RequestTestRunner::CompleteOnCorrectThread, this, complete_callback);
+
+    TestMap::const_iterator it = test_map_.find(test_mode);
+    if (it != test_map_.end()) {
+      it->second.run.Run(safe_complete_callback);
+    } else {
+      // Unknown test.
+      ADD_FAILURE();
+      complete_callback.Run();
+    }
+  }
+
+  // Called in both the browser and render process to shut down the test.
+  void ShutdownTest(const base::Closure& complete_callback) {
+    EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
+
+    const base::Closure& safe_complete_callback = base::Bind(
+        &RequestTestRunner::CompleteOnCorrectThread, this, complete_callback);
+
+    if (!post_file_tmpdir_.IsEmpty()) {
+      EXPECT_TRUE(is_browser_process_);
+      CefPostTask(TID_FILE,
+                  base::Bind(&RequestTestRunner::RunDeleteTempDirectory, this,
+                             safe_complete_callback));
+      return;
+    }
+
+    // Continue with test shutdown.
+    RunShutdown(safe_complete_callback);
+  }
+
+ private:
+  // Continued after |settings_| is populated for the test.
+  void SetupContinue(const base::Closure& complete_callback) {
+    if (!owner_task_runner_->BelongsToCurrentThread()) {
+      owner_task_runner_->PostTask(CefCreateClosureTask(base::Bind(
+          &RequestTestRunner::SetupContinue, this, complete_callback)));
+      return;
+    }
+
+    if (is_browser_process_) {
+      SetupTestBackend(complete_callback);
+    } else {
+      complete_callback.Run();
+    }
+  }
+
+  std::string GetTestPath(const std::string& name) {
+    return std::string(run_in_browser_process_ ? "/Browser" : "/Renderer") +
+           name;
+  }
+
+  std::string GetTestURL(const std::string& name) {
+    // Avoid name duplication between tests running in different processes.
+    // Otherwise we'll get unexpected state leakage (cache hits) when running
+    // multiple tests.
+    return GetRequestOrigin(is_server_backend_) + GetTestPath(name);
+  }
+
+  void SetupGetTestShared() {
+    settings_.request = CefRequest::Create();
+    settings_.request->SetURL(GetTestURL("GetTest.html"));
+    settings_.request->SetMethod("GET");
+
+    settings_.response = CefResponse::Create();
+    settings_.response->SetMimeType("text/html");
+    settings_.response->SetStatus(200);
+    settings_.response->SetStatusText("OK");
+
+    settings_.response_data = "GET TEST SUCCESS";
+  }
+
+  void SetupGetTest(const base::Closure& complete_callback) {
+    SetupGetTestShared();
+    complete_callback.Run();
+  }
+
+  void SetupGetNoDataTest(const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    // Disable download data notifications.
+    settings_.request->SetFlags(UR_FLAG_NO_DOWNLOAD_DATA);
+
+    settings_.expect_download_data = false;
+
+    complete_callback.Run();
+  }
+
+  void SetupGetPartialContentTest(const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    // Skip first 4 bytes of content and expect to receive the rest
+    settings_.request->SetHeaderByName("Range", "bytes=4-", true);
+    settings_.response->SetHeaderByName("Content-Range", "bytes 4-8/8", true);
+    settings_.response->SetStatus(206);
+    settings_.response->SetStatusText("Partial Content");
+    settings_.expected_download_offset = 4;
+
+    complete_callback.Run();
+  }
+
+  void SetupGetAllowCookiesTest(const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    // Send cookies.
+    settings_.request->SetFlags(UR_FLAG_ALLOW_STORED_CREDENTIALS);
+
+    settings_.expect_save_cookie = true;
+    settings_.expect_send_cookie = true;
+
+    complete_callback.Run();
+  }
+
+  void SetupGetRedirectTest(const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    // Add a redirect request.
+    settings_.redirect_request = CefRequest::Create();
+    settings_.redirect_request->SetURL(GetTestURL("redirect.html"));
+    settings_.redirect_request->SetMethod("GET");
+
+    settings_.redirect_response = CefResponse::Create();
+    settings_.redirect_response->SetMimeType("text/html");
+    settings_.redirect_response->SetStatus(302);
+    settings_.redirect_response->SetStatusText("Found");
+
+    CefResponse::HeaderMap headerMap;
+    headerMap.insert(std::make_pair("Location", settings_.request->GetURL()));
+    settings_.redirect_response->SetHeaderMap(headerMap);
+
+    complete_callback.Run();
+  }
+
+  void SetupGetRedirectStopTest(const base::Closure& complete_callback) {
+    settings_.request = CefRequest::Create();
+    settings_.request->SetURL(GetTestURL("GetTest.html"));
+    settings_.request->SetMethod("GET");
+
+    // With the test server only the status is expected
+    // on stop redirects.
+    settings_.response = CefResponse::Create();
+    settings_.response->SetStatus(302);
+    if (is_browser_process_) {
+      settings_.response->SetStatusText("Found");
+    }
+
+    // Add a redirect request.
+    settings_.redirect_request = CefRequest::Create();
+    settings_.redirect_request->SetURL(GetTestURL("redirect.html"));
+    settings_.redirect_request->SetMethod("GET");
+    settings_.redirect_request->SetFlags(UR_FLAG_STOP_ON_REDIRECT);
+
+    settings_.redirect_response = CefResponse::Create();
+    settings_.redirect_response->SetMimeType("text/html");
+    settings_.redirect_response->SetStatus(302);
+    settings_.redirect_response->SetStatusText("Found");
+
+    CefResponse::HeaderMap headerMap;
+    headerMap.insert(std::make_pair("Location", settings_.request->GetURL()));
+    settings_.redirect_response->SetHeaderMap(headerMap);
+
+    settings_.expected_status = UR_CANCELED;
+    settings_.expected_error_code = ERR_ABORTED;
+    settings_.expect_download_data = false;
+    settings_.expect_download_progress = false;
+    settings_.expected_send_count = 1;
+    settings_.expected_receive_count = 1;
+
+    complete_callback.Run();
+  }
+
+  void SetupGetRedirectLocationTest(const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    // Add a redirect request.
+    settings_.redirect_request = CefRequest::Create();
+    settings_.redirect_request->SetURL(GetTestURL("redirect.html"));
+    settings_.redirect_request->SetMethod("GET");
+
+    settings_.redirect_response = CefResponse::Create();
+    settings_.redirect_response->SetMimeType("text/html");
+    settings_.redirect_response->SetStatus(302);
+    settings_.redirect_response->SetStatusText("Found");
+
+    CefResponse::HeaderMap headerMap;
+    headerMap.insert(std::make_pair("LoCaTioN", GetTestPath("GetTest.html")));
+    settings_.redirect_response->SetHeaderMap(headerMap);
+
+    complete_callback.Run();
+  }
+
+  void SetupGetReferrerTest(const base::Closure& complete_callback) {
+    settings_.request = CefRequest::Create();
+    settings_.request->SetURL(GetTestURL("GetTest.html"));
+    settings_.request->SetMethod("GET");
+
+    // The referrer URL must be HTTP or HTTPS. This is enforced by
+    // GURL::GetAsReferrer() called from URLRequest::SetReferrer().
+    settings_.request->SetReferrer("http://tests.com/referrer.html",
+                                   REFERRER_POLICY_DEFAULT);
+
+    settings_.response = CefResponse::Create();
+    settings_.response->SetMimeType("text/html");
+    settings_.response->SetStatus(200);
+    settings_.response->SetStatusText("OK");
+
+    settings_.response_data = "GET TEST SUCCESS";
+
+    complete_callback.Run();
+  }
+
+  void SetupGetAuthTest(const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    // Require Basic authentication.
+    settings_.expect_authentication = true;
+    settings_.username = "user";
+    settings_.password = "pass";
+
+    // This flag is required to support credentials, which means we'll also get
+    // the cookies.
+    settings_.request->SetFlags(UR_FLAG_ALLOW_STORED_CREDENTIALS);
+    settings_.expect_save_cookie = true;
+    settings_.expect_send_cookie = true;
+
+    // The authentication request will come first, then the actual request.
+    settings_.expected_receive_count = 2;
+    settings_.expected_send_count = 2;
+
+    complete_callback.Run();
+  }
+
+  void SetupPostTestShared() {
+    settings_.request = CefRequest::Create();
+    settings_.request->SetURL(GetTestURL("PostTest.html"));
+    settings_.request->SetMethod("POST");
+    SetUploadData(settings_.request, "the_post_data");
+
+    settings_.response = CefResponse::Create();
+    settings_.response->SetMimeType("text/html");
+    settings_.response->SetStatus(200);
+    settings_.response->SetStatusText("OK");
+
+    settings_.response_data = "POST TEST SUCCESS";
+  }
+
+  void SetupPostTest(const base::Closure& complete_callback) {
+    SetupPostTestShared();
+    complete_callback.Run();
+  }
+
+  void SetupPostFileTest(const base::Closure& complete_callback) {
+    // This test is only supported in the browser process.
+    EXPECT_TRUE(is_browser_process_);
+
+    settings_.request = CefRequest::Create();
+    settings_.request->SetURL(GetTestURL("PostFileTest.html"));
+    settings_.request->SetMethod("POST");
+
+    settings_.response = CefResponse::Create();
+    settings_.response->SetMimeType("text/html");
+    settings_.response->SetStatus(200);
+    settings_.response->SetStatusText("OK");
+
+    settings_.response_data = "POST TEST SUCCESS";
+
+    CefPostTask(TID_FILE,
+                base::Bind(&RequestTestRunner::SetupPostFileTestContinue, this,
+                           complete_callback));
+  }
+
+  void SetupPostFileTestContinue(const base::Closure& complete_callback) {
+    EXPECT_TRUE(CefCurrentlyOn(TID_FILE));
+
+    EXPECT_TRUE(post_file_tmpdir_.CreateUniqueTempDir());
+    const std::string& path =
+        client::file_util::JoinPath(post_file_tmpdir_.GetPath(), "example.txt");
+    const char content[] = "HELLO FRIEND!";
+    int write_ct =
+        client::file_util::WriteFile(path, content, sizeof(content) - 1);
+    EXPECT_EQ(static_cast<int>(sizeof(content) - 1), write_ct);
+    SetUploadFile(settings_.request, path);
+
+    complete_callback.Run();
+  }
+
+  void SetupPostWithProgressTest(const base::Closure& complete_callback) {
+    // Start with the normal post test.
+    SetupPostTestShared();
+
+    // Enable upload progress notifications.
+    settings_.request->SetFlags(UR_FLAG_REPORT_UPLOAD_PROGRESS);
+
+    settings_.expect_upload_progress = true;
+
+    complete_callback.Run();
+  }
+
+  void SetupPostRedirectTest(const base::Closure& complete_callback) {
+    // Start with the normal post test.
+    SetupPostTestShared();
+
+    // Add a redirect request.
+    settings_.redirect_request = CefRequest::Create();
+    settings_.redirect_request->SetURL(GetTestURL("redirect.html"));
+    settings_.redirect_request->SetMethod("POST");
+    SetUploadData(settings_.redirect_request, "the_post_data");
+
+    settings_.redirect_response = CefResponse::Create();
+    settings_.redirect_response->SetMimeType("text/html");
+    // Only 307 is supported for redirecting the same method and post data.
+    settings_.redirect_response->SetStatus(307);
+    settings_.redirect_response->SetStatusText("Found");
+
+    CefResponse::HeaderMap headerMap;
+    headerMap.insert(std::make_pair("Location", settings_.request->GetURL()));
+    settings_.redirect_response->SetHeaderMap(headerMap);
+
+    complete_callback.Run();
+  }
+
+  void SetupPostRedirectToGetTest(const base::Closure& complete_callback) {
+    // Start with the normal post test.
+    SetupPostTestShared();
+
+    // The expected result after redirect is a GET request without POST data.
+    settings_.request = CefRequest::Create();
+    settings_.request->SetURL(GetTestURL("PostTest.html"));
+    settings_.request->SetMethod("GET");
+
+    // Add a redirect request.
+    settings_.redirect_request = CefRequest::Create();
+    settings_.redirect_request->SetURL(GetTestURL("redirect.html"));
+    settings_.redirect_request->SetMethod("POST");
+    SetUploadData(settings_.redirect_request, "the_post_data");
+
+    settings_.redirect_response = CefResponse::Create();
+    settings_.redirect_response->SetMimeType("text/html");
+    // Redirect codes other than 307 will cause conversion to GET and removal
+    // of POST data.
+    settings_.redirect_response->SetStatus(302);
+    settings_.redirect_response->SetStatusText("Found");
+
+    CefResponse::HeaderMap headerMap;
+    headerMap.insert(std::make_pair("Location", settings_.request->GetURL()));
+    settings_.redirect_response->SetHeaderMap(headerMap);
+
+    complete_callback.Run();
+  }
+
+  void SetupHeadTest(const base::Closure& complete_callback) {
+    settings_.request = CefRequest::Create();
+    settings_.request->SetURL(GetTestURL("HeadTest.html"));
+    settings_.request->SetMethod("HEAD");
+
+    settings_.response = CefResponse::Create();
+    settings_.response->SetMimeType("text/html");
+    settings_.response->SetStatus(200);
+    settings_.response->SetStatusText("OK");
+
+    // The backend will disregard this value when it returns the result.
+    settings_.response_data = "HEAD TEST SUCCESS";
+
+    settings_.expect_download_progress = false;
+    settings_.expect_download_data = false;
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheShared(const std::string& name, bool with_cache_control) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    // Specify a unique URL.
+    settings_.request->SetURL(GetTestURL(name));
+
+    if (with_cache_control) {
+      // Allow the page to be cached for 10 seconds.
+      CefResponse::HeaderMap headerMap;
+      headerMap.insert(std::make_pair(kCacheControlHeader, "max-age=10"));
+      settings_.response->SetHeaderMap(headerMap);
+    }
+  }
+
+  void SetupCacheWithControlTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheWithControlTest.html", true);
+
+    // Send multiple requests. With the Cache-Control response header the 2nd+
+    // should receive cached data.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 1;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheWithControlTestNext, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheWithControlTestNext(int next_send_count,
+                                     const base::Closure& complete_callback) {
+    // Only handle from the cache.
+    settings_.expect_response_was_cached = true;
+
+    // The following requests will use the same setup, so no more callbacks
+    // are required.
+    settings_.setup_next_request.Reset();
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheWithoutControlTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheWithoutControlTest.html", false);
+
+    // Send multiple requests. Without the Cache-Control response header all
+    // should be received.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 3;
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheSkipFlagTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheSkipFlagTest.html", true);
+
+    // Skip the cache despite the the Cache-Control response header.
+    // This will not read from the cache, but still write to the cache.
+    settings_.request->SetFlags(UR_FLAG_SKIP_CACHE);
+
+    // Send multiple requests. The 1st request will be handled normally,
+    // but not result in any reads from the cache. The 2nd request will
+    // expect a cached response and the 3nd request will skip the cache
+    // again.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 2;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheSkipFlagTestNext, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheSkipFlagTestNext(int next_send_count,
+                                  const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheSkipFlagTest.html", true);
+
+    // Expect a cached response.
+    settings_.expect_response_was_cached = true;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheSkipFlagTestLast, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheSkipFlagTestLast(int next_send_count,
+                                  const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheSkipFlagTest.html", true);
+
+    // Skip the cache despite the the Cache-Control response header.
+    settings_.request->SetFlags(UR_FLAG_SKIP_CACHE);
+
+    // Expect the cache to be skipped.
+    settings_.expect_response_was_cached = false;
+    settings_.setup_next_request.Reset();
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheSkipHeaderTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheSkipHeaderTest.html", true);
+
+    // Skip the cache despite the the Cache-Control response header.
+    // This will not read from the cache, but still write to the cache.
+    CefRequest::HeaderMap headerMap;
+    headerMap.insert(std::make_pair(kCacheControlHeader, "no-cache"));
+    settings_.request->SetHeaderMap(headerMap);
+
+    // Send multiple requests. The 1st request will be handled normally,
+    // but not result in any reads from the cache. The 2nd request will
+    // expect a cached response and the 3nd request will skip the cache
+    // again.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 2;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheSkipHeaderTestNext, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheSkipHeaderTestNext(int next_send_count,
+                                    const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheSkipHeaderTest.html", true);
+
+    // Expect a cached response.
+    settings_.expect_response_was_cached = true;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheSkipHeaderTestLast, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheSkipHeaderTestLast(int next_send_count,
+                                    const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheSkipHeaderTest.html", true);
+
+    // Skip the cache despite the the Cache-Control response header.
+    settings_.request->SetFlags(UR_FLAG_SKIP_CACHE);
+
+    // Expect the cache to be skipped.
+    settings_.expect_response_was_cached = false;
+    settings_.setup_next_request.Reset();
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheOnlyFailureFlagTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheOnlyFailureFlagTest.html", true);
+
+    // Only handle from the cache.
+    settings_.request->SetFlags(UR_FLAG_ONLY_FROM_CACHE);
+
+    // Send multiple requests. All should fail because there's no entry in the
+    // cache currently.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 0;
+
+    // The request is expected to fail.
+    settings_.SetRequestFailureExpected(ERR_CACHE_MISS);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheOnlyFailureHeaderTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheOnlyFailureFlagTest.html", true);
+
+    // Only handle from the cache.
+    CefRequest::HeaderMap headerMap;
+    headerMap.insert(std::make_pair(kCacheControlHeader, "only-if-cached"));
+    settings_.request->SetHeaderMap(headerMap);
+
+    // Send multiple requests. All should fail because there's no entry in the
+    // cache currently.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 0;
+
+    // The request is expected to fail.
+    settings_.SetRequestFailureExpected(ERR_CACHE_MISS);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheOnlySuccessFlagTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheOnlySuccessFlagTest.html", false);
+
+    // Send multiple requests. The 1st request will be handled normally. The
+    // 2nd+ requests will be configured by SetupCacheOnlySuccessFlagNext to
+    // require cached data.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 1;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheOnlySuccessFlagNext, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheOnlySuccessFlagNext(int next_send_count,
+                                     const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheOnlySuccessFlagTest.html", false);
+
+    // Only handle from the cache.
+    settings_.request->SetFlags(UR_FLAG_ONLY_FROM_CACHE);
+    settings_.expect_response_was_cached = true;
+
+    // The following requests will use the same setup, so no more callbacks
+    // are required.
+    settings_.setup_next_request.Reset();
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheOnlySuccessHeaderTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheOnlySuccessHeaderTest.html", false);
+
+    // Send multiple requests. The 1st request will be handled normally. The
+    // 2nd+ requests will be configured by SetupCacheOnlySuccessHeaderNext to
+    // require cached data.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 1;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheOnlySuccessHeaderNext, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheOnlySuccessHeaderNext(int next_send_count,
+                                       const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheOnlySuccessHeaderTest.html", false);
+
+    // Only handle from the cache.
+    CefRequest::HeaderMap headerMap;
+    headerMap.insert(std::make_pair(kCacheControlHeader, "only-if-cached"));
+    settings_.request->SetHeaderMap(headerMap);
+    settings_.expect_response_was_cached = true;
+
+    // The following requests will use the same setup, so no more callbacks
+    // are required.
+    settings_.setup_next_request.Reset();
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheDisableFlagTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheDisableFlagTest.html", true);
+
+    // Disable the cache despite the the Cache-Control response header.
+    settings_.request->SetFlags(UR_FLAG_DISABLE_CACHE);
+
+    // Send multiple requests. The 1st request will be handled normally,
+    // but not result in any reads from or writes to the cache.
+    // Therefore all following requests that are set to be only handled
+    // from the cache should fail.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 1;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheDisableFlagTestNext, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheDisableFlagTestNext(int next_send_count,
+                                     const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheDisableFlagTest.html", true);
+
+    // Only handle from the cache.
+    settings_.request->SetFlags(UR_FLAG_ONLY_FROM_CACHE);
+
+    // The request is expected to fail.
+    settings_.SetRequestFailureExpected(ERR_CACHE_MISS);
+
+    // The following requests will use the same setup, so no more callbacks
+    // are required.
+    settings_.setup_next_request.Reset();
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheDisableHeaderTest(const base::Closure& complete_callback) {
+    SetupCacheShared("CacheDisableHeaderTest.html", true);
+
+    // Disable the cache despite the the Cache-Control response header.
+    CefRequest::HeaderMap headerMap;
+    headerMap.insert(std::make_pair(kCacheControlHeader, "no-store"));
+    settings_.request->SetHeaderMap(headerMap);
+
+    // Send multiple requests. The 1st request will be handled normally,
+    // but not result in any reads from or writes to the cache.
+    // Therefore all following requests that are set to be only handled
+    // from the cache should fail.
+    settings_.expected_send_count = 3;
+    settings_.expected_receive_count = 1;
+    settings_.setup_next_request =
+        base::Bind(&RequestTestRunner::SetupCacheDisableHeaderTestNext, this);
+
+    complete_callback.Run();
+  }
+
+  void SetupCacheDisableHeaderTestNext(int next_send_count,
+                                       const base::Closure& complete_callback) {
+    // Recreate the request object because the existing object will now be
+    // read-only.
+    EXPECT_TRUE(settings_.request->IsReadOnly());
+    SetupCacheShared("CacheDisableHeaderTest.html", true);
+
+    // Only handle from the cache.
+    CefRequest::HeaderMap headerMap;
+    headerMap.insert(std::make_pair(kCacheControlHeader, "only-if-cached"));
+    settings_.request->SetHeaderMap(headerMap);
+
+    // The request is expected to fail.
+    settings_.SetRequestFailureExpected(ERR_CACHE_MISS);
+
+    // The following requests will use the same setup, so no more callbacks
+    // are required.
+    settings_.setup_next_request.Reset();
+
+    complete_callback.Run();
+  }
+
+  void SetupIncompleteProcessRequestTest(
+      const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    settings_.incomplete_type = RequestRunSettings::INCOMPLETE_PROCESS_REQUEST;
+
+    // There will be no response and the request will be aborted.
+    settings_.response = CefResponse::Create();
+    settings_.response_data.clear();
+    settings_.expected_error_code = ERR_ABORTED;
+    settings_.expected_status = UR_FAILED;
+    settings_.expect_download_progress = false;
+    settings_.expect_download_data = false;
+
+    complete_callback.Run();
+  }
+
+  void SetupIncompleteReadResponseTest(const base::Closure& complete_callback) {
+    // Start with the normal get test.
+    SetupGetTestShared();
+
+    settings_.incomplete_type = RequestRunSettings::INCOMPLETE_READ_RESPONSE;
+
+    // There will be a response but the request will be aborted without
+    // receiving any data.
+    settings_.response_data = kIncompleteDoNotSendData;
+    settings_.expected_error_code = ERR_ABORTED;
+    settings_.expected_status = UR_FAILED;
+    // TODO(network): Download progress notifications are sent for incomplete
+    // (with no data sent) requests in the browser process but not the renderer
+    // process. Consider standardizing this behavior.
+    settings_.expect_download_progress = is_browser_process_;
+    settings_.expect_download_data = false;
+
+    complete_callback.Run();
+  }
+
+  // Send a request. |complete_callback| will be executed on request completion.
+  void SendRequest(
+      const RequestClient::RequestCompleteCallback& complete_callback) {
+    CefRefPtr<CefRequest> request;
+    if (settings_.redirect_request)
+      request = settings_.redirect_request;
+    else
+      request = settings_.request;
+    EXPECT_TRUE(request.get());
+
+    CefRefPtr<RequestClient> client = new RequestClient(complete_callback);
+
+    // Not delegating to CefRequestHandler::GetAuthCredentials.
+    if (!use_frame_method_ && settings_.expect_authentication) {
+      client->has_authentication_ = true;
+      client->username_ = settings_.username;
+      client->password_ = settings_.password;
+    }
+
+    if (use_frame_method_) {
+      EXPECT_TRUE(frame_);
+      frame_->CreateURLRequest(request, client.get());
+    } else {
+      CefURLRequest::Create(request, client.get(), request_context_);
+    }
+
+    if (settings_.incomplete_type != RequestRunSettings::INCOMPLETE_NONE) {
+      incomplete_request_callback_.Run();
+      incomplete_request_callback_.Reset();
+    }
+  }
+
+  // Verify a response.
+  void VerifyResponse(CefRefPtr<RequestClient> client) {
+    CefRefPtr<CefRequest> expected_request;
+    CefRefPtr<CefResponse> expected_response;
+
+    if (settings_.redirect_request)
+      expected_request = settings_.redirect_request;
+    else
+      expected_request = settings_.request;
+
+    if (settings_.redirect_response && !settings_.expect_follow_redirect) {
+      // A redirect response was sent but the redirect is not expected to be
+      // followed.
+      expected_response = settings_.redirect_response;
+    } else {
+      expected_response = settings_.response;
+    }
+
+    TestRequestEqual(expected_request, client->request_, false);
+
+    EXPECT_EQ(settings_.expected_status, client->status_);
+    EXPECT_EQ(settings_.expected_error_code, client->error_code_);
+    if (expected_response && client->response_)
+      TestResponseEqual(expected_response, client->response_, true);
+
+    EXPECT_EQ(settings_.expect_response_was_cached,
+              client->response_was_cached_);
+
+    EXPECT_EQ(1, client->request_complete_ct_);
+
+    if (settings_.expect_upload_progress) {
+      EXPECT_LE(1, client->upload_progress_ct_);
+
+      std::string upload_data;
+      GetUploadData(expected_request, upload_data);
+      EXPECT_EQ((int64)upload_data.size(), client->upload_total_);
+    } else {
+      EXPECT_EQ(0, client->upload_progress_ct_);
+      EXPECT_EQ(0, client->upload_total_);
+    }
+
+    if (settings_.expect_download_progress) {
+      EXPECT_LE(1, client->download_progress_ct_);
+      EXPECT_EQ((int64)(settings_.response_data.size() -
+                        settings_.expected_download_offset),
+                client->download_total_);
+    } else {
+      EXPECT_EQ(0, client->download_progress_ct_);
+      EXPECT_EQ(0, client->download_total_);
+    }
+
+    if (settings_.expect_download_data) {
+      size_t expected_offset = settings_.expected_download_offset;
+      EXPECT_LE(1, client->download_data_ct_);
+      EXPECT_STREQ(settings_.response_data.substr(expected_offset).c_str(),
+                   client->download_data_.c_str());
+    } else {
+      EXPECT_EQ(0, client->download_data_ct_);
+      EXPECT_TRUE(client->download_data_.empty());
+    }
+
+    if (settings_.expect_authentication) {
+      EXPECT_EQ(1, client->auth_credentials_ct_);
+    } else {
+      EXPECT_EQ(0, client->auth_credentials_ct_);
+    }
+  }
+
+  // Run a test with a single request.
+  void SingleRunTest(const base::Closure& complete_callback) {
+    SendRequest(base::Bind(&RequestTestRunner::SingleRunTestComplete, this,
+                           complete_callback));
+  }
+
+  void SingleRunTestComplete(const base::Closure& complete_callback,
+                             CefRefPtr<RequestClient> completed_client) {
+    VerifyResponse(completed_client);
+    complete_callback.Run();
+  }
+
+  // Run a test with multiple requests.
+  void MultipleRunTest(const base::Closure& complete_callback) {
+    EXPECT_GT(settings_.expected_send_count, 0);
+    EXPECT_GE(settings_.expected_receive_count, 0);
+    MultipleRunTestContinue(complete_callback, 1);
+  }
+
+  void MultipleRunTestContinue(const base::Closure& complete_callback,
+                               int send_count) {
+    // Send the next request.
+    SendRequest(base::Bind(&RequestTestRunner::MultipleRunTestNext, this,
+                           complete_callback, send_count));
+  }
+
+  void MultipleRunTestNext(const base::Closure& complete_callback,
+                           int send_count,
+                           CefRefPtr<RequestClient> completed_client) {
+    // Verify the completed request.
+    VerifyResponse(completed_client);
+
+    if (send_count == settings_.expected_send_count) {
+      // All requests complete.
+      complete_callback.Run();
+      return;
+    }
+
+    const int next_send_count = send_count + 1;
+    const base::Closure& continue_callback =
+        base::Bind(&RequestTestRunner::MultipleRunTestContinue, this,
+                   complete_callback, next_send_count);
+
+    if (!settings_.setup_next_request.is_null()) {
+      // Provide an opportunity to modify expectations before the next request.
+      // Copy the callback object in case |settings_.setup_next_request| is
+      // modified as a result of the call.
+      RequestRunSettings::NextRequestCallback next_callback =
+          settings_.setup_next_request;
+      next_callback.Run(next_send_count, continue_callback);
+    } else {
+      continue_callback.Run();
+    }
+  }
+
+  // Register a test. Called in the constructor.
+  void RegisterTest(RequestTestMode test_mode,
+                    TestCallback setup,
+                    TestCallback run) {
+    TestEntry entry = {setup, run};
+    test_map_.insert(std::make_pair(test_mode, entry));
+  }
+
+  void CompleteOnCorrectThread(const base::Closure& complete_callback) {
+    if (!owner_task_runner_->BelongsToCurrentThread()) {
+      owner_task_runner_->PostTask(CefCreateClosureTask(
+          base::Bind(&RequestTestRunner::CompleteOnCorrectThread, this,
+                     complete_callback)));
+      return;
+    }
+
+    complete_callback.Run();
+  }
+
+  void RunDeleteTempDirectory(const base::Closure& complete_callback) {
+    EXPECT_TRUE(CefCurrentlyOn(TID_FILE));
+
+    EXPECT_TRUE(post_file_tmpdir_.Delete());
+    EXPECT_TRUE(post_file_tmpdir_.IsEmpty());
+
+    // Continue with test shutdown.
+    RunShutdown(complete_callback);
+  }
+
+  void RunShutdown(const base::Closure& complete_callback) {
+    if (!owner_task_runner_->BelongsToCurrentThread()) {
+      owner_task_runner_->PostTask(CefCreateClosureTask(base::Bind(
+          &RequestTestRunner::RunShutdown, this, complete_callback)));
+      return;
+    }
+
+    if (is_browser_process_) {
+      ShutdownTestBackend(complete_callback);
+    } else {
+      complete_callback.Run();
+    }
+  }
+
+  // Create the backend for the current test. Called during test setup.
+  void SetupTestBackend(const base::Closure& complete_callback) {
+    // Backends are only created in the browser process.
+    EXPECT_TRUE(is_browser_process_);
+
+    EXPECT_TRUE(settings_.request.get());
+    EXPECT_TRUE(settings_.response.get() ||
+                settings_.expected_status == UR_FAILED);
+
+    if (is_server_backend_)
+      StartServer(complete_callback);
+    else
+      AddSchemeHandler(complete_callback);
+  }
+
+  void StartServer(const base::Closure& complete_callback) {
+    EXPECT_FALSE(server_handler_);
+
+    server_handler_ = new RequestServerHandler();
+
+    server_handler_->AddSchemeHandler(&settings_);
+    if (settings_.expected_receive_count >= 0) {
+      server_handler_->SetExpectedRequestCount(
+          settings_.expected_receive_count);
+    }
+
+    server_handler_->CreateServer(complete_callback);
+  }
+
+  void AddSchemeHandler(const base::Closure& complete_callback) {
+    EXPECT_FALSE(scheme_factory_);
+
+    // Add the factory registration.
+    scheme_factory_ = new RequestSchemeHandlerFactory();
+    request_context_->RegisterSchemeHandlerFactory(GetRequestScheme(false),
+                                                   GetRequestHost(false, false),
+                                                   scheme_factory_.get());
+
+    scheme_factory_->AddSchemeHandler(&settings_);
+
+    // Any further calls will come from the IO thread.
+    scheme_factory_->SetOwnerTaskRunner(CefTaskRunner::GetForThread(TID_IO));
+
+    complete_callback.Run();
+  }
+
+  // Shutdown the backend for the current test. Called during test shutdown.
+  void ShutdownTestBackend(const base::Closure& complete_callback) {
+    // Backends are only created in the browser process.
+    EXPECT_TRUE(is_browser_process_);
+    if (is_server_backend_)
+      ShutdownServer(complete_callback);
+    else
+      RemoveSchemeHandler(complete_callback);
+  }
+
+  void ShutdownServer(const base::Closure& complete_callback) {
+    EXPECT_TRUE(server_handler_);
+
+    server_handler_->ShutdownServer(complete_callback);
+    server_handler_ = nullptr;
+  }
+
+  void RemoveSchemeHandler(const base::Closure& complete_callback) {
+    EXPECT_TRUE(scheme_factory_);
+
+    // Remove the factory registration.
+    request_context_->RegisterSchemeHandlerFactory(
+        GetRequestScheme(false), GetRequestHost(false, false), nullptr);
+    scheme_factory_->Shutdown(complete_callback);
+    scheme_factory_ = nullptr;
+  }
+
+  const bool is_browser_process_;
+  const bool is_server_backend_;
+  const bool use_frame_method_;
+  const bool run_in_browser_process_;
+
+  // Used with incomplete request tests.
+  base::Closure incomplete_request_callback_;
+
+  // Primary thread runner for the object that owns us. In the browser process
+  // this will be the UI thread and in the renderer process this will be the
+  // RENDERER thread.
+  CefRefPtr<CefTaskRunner> owner_task_runner_;
+
+  CefRefPtr<CefRequestContext> request_context_;
+
+  // Frame that originates the request. May be nullptr.
+  CefRefPtr<CefFrame> frame_;
+
+  struct TestEntry {
+    TestCallback setup;
+    TestCallback run;
+  };
+  typedef std::map<RequestTestMode, TestEntry> TestMap;
+  TestMap test_map_;
+
+  // Server backend.
+  CefRefPtr<RequestServerHandler> server_handler_;
+
+  // Scheme handler backend.
+  std::string scheme_name_;
+  CefRefPtr<RequestSchemeHandlerFactory> scheme_factory_;
+
+  CefScopedTempDir post_file_tmpdir_;
+
+ public:
+  RequestRunSettings settings_;
+};
+
+// RENDERER-SIDE TEST HARNESS
+
+class RequestRendererTest : public ClientAppRenderer::Delegate {
+ public:
+  RequestRendererTest() {}
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (message->GetName() == kRequestTestMsg) {
+      EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
+      EXPECT_TRUE(frame->IsMain());
+
+      app_ = app;
+      browser_ = browser;
+      frame_ = nullptr;
+
+      CefRefPtr<CefListValue> args = message->GetArgumentList();
+
+      const bool use_frame_method = args->GetBool(2);
+      if (use_frame_method)
+        frame_ = frame;
+
+      test_mode_ = static_cast<RequestTestMode>(args->GetInt(0));
+      test_runner_ = new RequestTestRunner(
+          false, args->GetBool(1), use_frame_method, false,
+          base::Bind(&RequestRendererTest::OnIncompleteRequest, this));
+
+      // Setup the test. This will create the objects that we test against but
+      // not register any backend (because we're in the render process).
+      test_runner_->SetupTest(
+          test_mode_, base::Bind(&RequestRendererTest::OnSetupComplete, this));
+
+      return true;
+    }
+
+    // Message not handled.
+    return false;
+  }
+
+ private:
+  void OnSetupComplete() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
+
+    // Run the test.
+    test_runner_->RunTest(
+        test_mode_, frame_,
+        base::Bind(&RequestRendererTest::OnRunComplete, this));
+  }
+
+  void OnRunComplete() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
+
+    // Shutdown the test.
+    test_runner_->ShutdownTest(
+        base::Bind(&RequestRendererTest::OnShutdownComplete, this));
+  }
+
+  void OnIncompleteRequest() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
+
+    // This method will only be called for incomplete requests.
+    EXPECT_NE(test_runner_->settings_.incomplete_type,
+              RequestRunSettings::INCOMPLETE_NONE);
+
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // The browser will be closed to abort in-progress requests.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kIncompleteRequestTestMsg);
+    EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
+    browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
+  }
+
+  void OnShutdownComplete() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
+
+    if (test_runner_->settings_.incomplete_type !=
+        RequestRunSettings::INCOMPLETE_NONE) {
+      // For incomplete tests there's a race between process destruction due to
+      // the browser closing, and the test possibly completing due to request
+      // cancellation. We therefore ignore test completion in this case.
+      return;
+    }
+
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kRequestTestMsg);
+    EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
+    browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
+
+    app_ = nullptr;
+    browser_ = nullptr;
+  }
+
+  CefRefPtr<ClientAppRenderer> app_;
+  CefRefPtr<CefBrowser> browser_;
+  CefRefPtr<CefFrame> frame_;
+  RequestTestMode test_mode_;
+
+  scoped_refptr<RequestTestRunner> test_runner_;
+
+  IMPLEMENT_REFCOUNTING(RequestRendererTest);
+};
+
+// BROWSER-SIDE TEST HARNESS
+
+class RequestTestHandler : public TestHandler {
+ public:
+  RequestTestHandler(RequestTestMode test_mode,
+                     ContextTestMode context_mode,
+                     bool test_in_browser,
+                     bool test_server_backend,
+                     bool test_frame_method)
+      : test_mode_(test_mode),
+        context_mode_(context_mode),
+        test_in_browser_(test_in_browser),
+        test_server_backend_(test_server_backend),
+        test_frame_method_(test_frame_method),
+        // Must use the request origin to avoid failures in
+        // CorsURLLoaderFactory::IsSane for requests originating from the
+        // renderer process.
+        test_url_(GetRequestOrigin(test_server_backend) +
+                  "/URLRequestTest.Test") {}
+
+  void RunTest() override {
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+
+    // Start pre-setup actions.
+    PreSetupStart();
+  }
+
+  void PreSetupStart() {
+    CefPostTask(TID_FILE,
+                base::Bind(&RequestTestHandler::PreSetupFileTasks, this));
+  }
+
+  void PreSetupFileTasks() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_FILE));
+
+    if (context_mode_ == CONTEXT_ONDISK) {
+      EXPECT_TRUE(context_tmpdir_.CreateUniqueTempDir());
+      context_tmpdir_path_ = context_tmpdir_.GetPath();
+      EXPECT_FALSE(context_tmpdir_path_.empty());
+    }
+
+    CefPostTask(TID_UI,
+                base::Bind(&RequestTestHandler::PreSetupContinue, this));
+  }
+
+  void PreSetupContinue() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_UI));
+
+    test_runner_ = new RequestTestRunner(
+        true, test_server_backend_, test_frame_method_, test_in_browser_,
+        base::Bind(&RequestTestHandler::OnIncompleteRequest, this));
+
+    // Get or create the request context.
+    if (context_mode_ == CONTEXT_GLOBAL) {
+      CefRefPtr<CefRequestContext> request_context =
+          CefRequestContext::GetGlobalContext();
+      EXPECT_TRUE(request_context.get());
+      test_runner_->SetRequestContext(request_context);
+
+      PreSetupComplete();
+    } else {
+      // Don't end the test until the temporary request context has been
+      // destroyed.
+      SetSignalCompletionWhenAllBrowsersClose(false);
+
+      CefRequestContextSettings settings;
+
+      if (context_mode_ == CONTEXT_ONDISK) {
+        EXPECT_FALSE(context_tmpdir_.IsEmpty());
+        CefString(&settings.cache_path) = context_tmpdir_path_;
+      }
+
+      // Create a new temporary request context.
+      CefRefPtr<CefRequestContext> request_context =
+          CefRequestContext::CreateContext(settings,
+                                           new RequestContextHandler(this));
+      EXPECT_TRUE(request_context.get());
+      test_runner_->SetRequestContext(request_context);
+
+      if (!test_server_backend_) {
+        // Set the schemes that are allowed to store cookies.
+        std::vector<CefString> supported_schemes;
+        supported_schemes.push_back(GetRequestScheme(false));
+
+        // Continue the test once supported schemes has been set.
+        request_context->GetCookieManager(nullptr)->SetSupportedSchemes(
+            supported_schemes, true,
+            new TestCompletionCallback(
+                base::Bind(&RequestTestHandler::PreSetupComplete, this)));
+      } else {
+        PreSetupComplete();
+      }
+    }
+  }
+
+  void PreSetupComplete() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&RequestTestHandler::PreSetupComplete, this));
+      return;
+    }
+
+    // Setup the test. This will create the objects that we test against and
+    // register the backend.
+    test_runner_->SetupTest(
+        test_mode_, base::Bind(&RequestTestHandler::OnSetupComplete, this));
+  }
+
+  // Browser process setup is complete.
+  void OnSetupComplete() {
+    // Start post-setup actions.
+    SetTestCookie(test_runner_->GetRequestContext(), test_server_backend_,
+                  base::Bind(&RequestTestHandler::PostSetupComplete, this));
+  }
+
+  void PostSetupComplete() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&RequestTestHandler::PostSetupComplete, this));
+      return;
+    }
+
+    if (test_in_browser_) {
+      if (test_frame_method_) {
+        AddResource(test_url_, "<html><body>TEST</body></html>", "text/html");
+
+        // Create the browser who's main frame will be the initiator for the
+        // request.
+        CreateBrowser(test_url_, test_runner_->GetRequestContext());
+      } else {
+        // Run the test now.
+        test_running_ = true;
+        test_runner_->RunTest(
+            test_mode_, nullptr /* frame */,
+            base::Bind(&RequestTestHandler::OnRunComplete, this));
+      }
+    } else {
+      AddResource(test_url_, "<html><body>TEST</body></html>", "text/html");
+
+      // Create a browser to run the test in the renderer process.
+      CreateBrowser(test_url_, test_runner_->GetRequestContext());
+    }
+  }
+
+  ReturnValue OnBeforeResourceLoad(
+      CefRefPtr<CefBrowser> browser,
+      CefRefPtr<CefFrame> frame,
+      CefRefPtr<CefRequest> request,
+      CefRefPtr<CefRequestCallback> callback) override {
+    if (test_running_ && test_frame_method_) {
+      EXPECT_TRUE(frame);
+      EXPECT_EQ(test_frame_->GetIdentifier(), frame->GetIdentifier());
+      test_frame_resource_load_ct_++;
+    }
+
+    return TestHandler::OnBeforeResourceLoad(browser, frame, request, callback);
+  }
+
+  bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
+                          const CefString& origin_url,
+                          bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) override {
+    EXPECT_TRUE(test_in_browser_);
+    EXPECT_TRUE(test_frame_method_);
+    auth_credentials_ct_++;
+    if (test_runner_->settings_.expect_authentication) {
+      callback->Continue(test_runner_->settings_.username,
+                         test_runner_->settings_.password);
+      return true;
+    }
+    return false;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (test_in_browser_ && test_frame_method_) {
+      // Run the test now.
+      test_frame_ = frame;
+      test_running_ = true;
+      test_runner_->RunTest(
+          test_mode_, frame,
+          base::Bind(&RequestTestHandler::OnRunComplete, this));
+      return;
+    }
+
+    EXPECT_FALSE(test_in_browser_);
+    if (frame->IsMain()) {
+      CefRefPtr<CefProcessMessage> test_message =
+          CefProcessMessage::Create(kRequestTestMsg);
+      CefRefPtr<CefListValue> args = test_message->GetArgumentList();
+      EXPECT_TRUE(args->SetInt(0, test_mode_));
+      EXPECT_TRUE(args->SetBool(1, test_server_backend_));
+      EXPECT_TRUE(args->SetBool(2, test_frame_method_));
+
+      if (test_frame_method_)
+        test_frame_ = frame;
+      test_running_ = true;
+
+      // Send a message to the renderer process to run the test.
+      frame->SendProcessMessage(PID_RENDERER, test_message);
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    EXPECT_EQ(PID_RENDERER, source_process);
+    EXPECT_TRUE(message.get());
+    EXPECT_TRUE(message->IsReadOnly());
+    EXPECT_FALSE(test_in_browser_);
+
+    EXPECT_FALSE(got_message_);
+    got_message_.yes();
+
+    if (message->GetArgumentList()->GetBool(0))
+      got_success_.yes();
+
+    const std::string& message_name = message->GetName();
+    if (message_name == kRequestTestMsg) {
+      // Renderer process test is complete.
+      OnRunComplete();
+    } else if (message_name == kIncompleteRequestTestMsg) {
+      // Incomplete renderer tests will not complete normally. Instead, trigger
+      // browser close and then signal completion from OnBeforeClose.
+      OnIncompleteRequest();
+    }
+
+    return true;
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    if (!test_in_browser_ && test_runner_->settings_.incomplete_type !=
+                                 RequestRunSettings::INCOMPLETE_NONE) {
+      // Incomplete tests running in the renderer process will never recieve the
+      // test complete process message, so call the method here.
+      OnRunComplete();
+    }
+
+    TestHandler::OnBeforeClose(browser);
+  }
+
+  // Incomplete tests will not complete normally. Instead, we trigger a browser
+  // close to abort in-progress requests.
+  void OnIncompleteRequest() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&RequestTestHandler::OnIncompleteRequest, this));
+      return;
+    }
+
+    EXPECT_TRUE(test_frame_method_);
+    EXPECT_NE(RequestRunSettings::INCOMPLETE_NONE,
+              test_runner_->settings_.incomplete_type);
+
+    // TestComplete will eventually be called from DestroyTest instead of being
+    // triggered by browser destruction.
+    SetSignalCompletionWhenAllBrowsersClose(false);
+    CefPostDelayedTask(
+        TID_UI, base::Bind(&TestHandler::CloseBrowser, GetBrowser(), false),
+        1000);
+  }
+
+  // Test run is complete. It ran in either the browser or render process.
+  void OnRunComplete() {
+    GetTestCookie(test_runner_->GetRequestContext(), test_server_backend_,
+                  base::Bind(&RequestTestHandler::PostRunComplete, this));
+  }
+
+  void PostRunComplete(bool has_save_cookie) {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI, base::Bind(&RequestTestHandler::PostRunComplete, this,
+                                     has_save_cookie));
+      return;
+    }
+
+    EXPECT_EQ(test_runner_->settings_.expect_save_cookie, has_save_cookie);
+
+    // Shut down the browser side of the test.
+    test_runner_->ShutdownTest(
+        base::Bind(&RequestTestHandler::MaybeClearAuthCredentials, this));
+  }
+
+  void MaybeClearAuthCredentials() {
+    if (test_runner_->settings_.expect_authentication &&
+        context_mode_ == CONTEXT_GLOBAL) {
+      // Clear the HTTP authentication cache to avoid leaking state between
+      // test runs when using the global request context.
+      test_runner_->GetRequestContext()->ClearHttpAuthCredentials(
+          new TestCompletionCallback(
+              base::Bind(&RequestTestHandler::DestroyTest, this)));
+      return;
+    }
+
+    DestroyTest();
+  }
+
+  void DestroyTest() override {
+    if (!test_in_browser_) {
+      EXPECT_TRUE(got_message_);
+      EXPECT_TRUE(got_success_);
+    }
+
+    if (test_frame_method_) {
+      // Expect at least 1 call to OnBeforeResourceLoad for every test.
+      // Redirect tests may get multiple calls.
+      EXPECT_LE(1, test_frame_resource_load_ct_);
+    }
+
+    // CefRequestHandler::GetAuthCredentials should be called after
+    // CefURLRequestClient::GetAuthCredentials when the request has an
+    // associated frame.
+    if (test_in_browser_ && test_frame_method_ &&
+        test_runner_->settings_.expect_authentication) {
+      EXPECT_EQ(1, auth_credentials_ct_);
+    } else {
+      EXPECT_EQ(0, auth_credentials_ct_);
+    }
+
+    TestHandler::DestroyTest();
+
+    // For non-global contexts OnTestComplete() will be called when the
+    // RequestContextHandler is destroyed.
+    bool call_test_complete = false;
+    if (context_mode_ == CONTEXT_GLOBAL) {
+      if (test_in_browser_ && !test_frame_method_) {
+        // These tests don't create a browser that would signal implicitly.
+        call_test_complete = true;
+      } else if (!SignalCompletionWhenAllBrowsersClose()) {
+        // These tests close the browser to terminate in-progress requests
+        // before test completion.
+        call_test_complete = true;
+      }
+    }
+
+    // Release references to the context and handler.
+    test_runner_->Destroy();
+
+    if (call_test_complete)
+      OnTestComplete();
+  }
+
+  void OnTestComplete() {
+    if (!CefCurrentlyOn(TID_UI)) {
+      CefPostTask(TID_UI,
+                  base::Bind(&RequestTestHandler::OnTestComplete, this));
+      return;
+    }
+
+    EXPECT_FALSE(got_on_test_complete_);
+    got_on_test_complete_.yes();
+
+    if (!context_tmpdir_.IsEmpty()) {
+      // Wait a bit for cache file handles to close after browser or request
+      // context destruction.
+      CefPostDelayedTask(
+          TID_FILE,
+          base::Bind(&RequestTestHandler::PostTestCompleteFileTasks, this),
+          100);
+    } else {
+      TestComplete();
+    }
+  }
+
+  void PostTestCompleteFileTasks() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_FILE));
+
+    EXPECT_TRUE(context_tmpdir_.Delete());
+    EXPECT_TRUE(context_tmpdir_.IsEmpty());
+
+    CefPostTask(TID_UI, base::Bind(&RequestTestHandler::TestComplete, this));
+  }
+
+ private:
+  // Used with temporary request contexts to signal test completion once the
+  // temporary context has been destroyed.
+  class RequestContextHandler : public CefRequestContextHandler {
+   public:
+    explicit RequestContextHandler(CefRefPtr<RequestTestHandler> test_handler)
+        : test_handler_(test_handler) {}
+    ~RequestContextHandler() override { test_handler_->OnTestComplete(); }
+
+   private:
+    CefRefPtr<RequestTestHandler> test_handler_;
+
+    IMPLEMENT_REFCOUNTING(RequestContextHandler);
+  };
+
+  const RequestTestMode test_mode_;
+  const ContextTestMode context_mode_;
+  const bool test_in_browser_;
+  const bool test_server_backend_;
+  const bool test_frame_method_;
+  const std::string test_url_;
+
+  scoped_refptr<RequestTestRunner> test_runner_;
+
+  bool test_running_ = false;
+
+  CefRefPtr<CefFrame> test_frame_;
+  int test_frame_resource_load_ct_ = 0;
+
+  CefScopedTempDir context_tmpdir_;
+  CefString context_tmpdir_path_;
+
+ public:
+  // Only used when the test runs in the render process.
+  TrackCallback got_message_;
+  TrackCallback got_success_;
+
+  int auth_credentials_ct_ = 0;
+  TrackCallback got_on_test_complete_;
+
+  IMPLEMENT_REFCOUNTING(RequestTestHandler);
+};
+
+bool IsTestSupported(RequestTestMode test_mode,
+                     ContextTestMode context_mode,
+                     bool test_in_browser,
+                     bool test_server_backend,
+                     bool test_frame_method) {
+  if (!test_in_browser && !test_server_backend && !test_frame_method) {
+    // Requests from the render process can only reach non-server backends when
+    // using the CefFrame::CreateURLRequest method.
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+// Entry point for creating URLRequest renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateURLRequestRendererTests(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new RequestRendererTest);
+}
+
+// Entry point for registering custom schemes.
+// Called from client_app_delegates.cc.
+void RegisterURLRequestCustomSchemes(
+    CefRawPtr<CefSchemeRegistrar> registrar,
+    std::vector<CefString>& cookiable_schemes) {
+  const std::string& scheme = GetRequestScheme(false);
+  registrar->AddCustomScheme(
+      scheme, CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
+  cookiable_schemes.push_back(scheme);
+}
+
+// Helpers for defining URLRequest tests.
+#define REQ_TEST_EX(name, test_mode, context_mode, test_in_browser,      \
+                    test_server_backend, test_frame_method)              \
+  TEST(URLRequestTest, name) {                                           \
+    if (!IsTestSupported(test_mode, context_mode, test_in_browser,       \
+                         test_server_backend, test_frame_method)) {      \
+      return;                                                            \
+    }                                                                    \
+    CefRefPtr<RequestTestHandler> handler =                              \
+        new RequestTestHandler(test_mode, context_mode, test_in_browser, \
+                               test_server_backend, test_frame_method);  \
+    handler->ExecuteTest();                                              \
+    ReleaseAndWaitForDestructor(handler);                                \
+  }
+
+#define REQ_TEST(name, test_mode, context_mode, test_in_browser, \
+                 test_server_backend, test_frame_method)         \
+  REQ_TEST_EX(name, test_mode, context_mode, test_in_browser,    \
+              test_server_backend, test_frame_method)
+
+// Define the tests.
+#define REQ_TEST_SET_EX(suffix, context_mode, test_server_backend,             \
+                        test_frame_method)                                     \
+  REQ_TEST(BrowserGET##suffix, REQTEST_GET, context_mode, true,                \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(BrowserGETNoData##suffix, REQTEST_GET_NODATA, context_mode, true,   \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(BrowserGETPartialContent##suffix, REQTEST_GET_PARTIAL_CONTENT,      \
+           context_mode, true, test_server_backend, test_frame_method)         \
+  REQ_TEST(BrowserGETAllowCookies##suffix, REQTEST_GET_ALLOWCOOKIES,           \
+           context_mode, true, test_server_backend, test_frame_method)         \
+  REQ_TEST(BrowserGETRedirect##suffix, REQTEST_GET_REDIRECT, context_mode,     \
+           true, test_server_backend, test_frame_method)                       \
+  REQ_TEST(BrowserGETRedirectStop##suffix, REQTEST_GET_REDIRECT_STOP,          \
+           context_mode, true, test_server_backend, test_frame_method)         \
+  REQ_TEST(BrowserGETRedirectLocation##suffix, REQTEST_GET_REDIRECT_LOCATION,  \
+           context_mode, true, test_server_backend, test_frame_method)         \
+  REQ_TEST(BrowserGETReferrer##suffix, REQTEST_GET_REFERRER, context_mode,     \
+           true, test_server_backend, test_frame_method)                       \
+  REQ_TEST(BrowserPOST##suffix, REQTEST_POST, context_mode, true,              \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(BrowserPOSTFile##suffix, REQTEST_POST_FILE, context_mode, true,     \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(BrowserPOSTWithProgress##suffix, REQTEST_POST_WITHPROGRESS,         \
+           context_mode, true, test_server_backend, test_frame_method)         \
+  REQ_TEST(BrowserPOSTRedirect##suffix, REQTEST_POST_REDIRECT, context_mode,   \
+           true, test_server_backend, test_frame_method)                       \
+  REQ_TEST(BrowserPOSTRedirectToGET##suffix, REQTEST_POST_REDIRECT_TOGET,      \
+           context_mode, true, test_server_backend, test_frame_method)         \
+  REQ_TEST(BrowserHEAD##suffix, REQTEST_HEAD, context_mode, true,              \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(RendererGET##suffix, REQTEST_GET, context_mode, false,              \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(RendererGETNoData##suffix, REQTEST_GET_NODATA, context_mode, false, \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(RendererGETAllowCookies##suffix, REQTEST_GET_ALLOWCOOKIES,          \
+           context_mode, false, test_server_backend, test_frame_method)        \
+  REQ_TEST(RendererGETRedirect##suffix, REQTEST_GET_REDIRECT, context_mode,    \
+           false, test_server_backend, test_frame_method)                      \
+  REQ_TEST(RendererGETRedirectStop##suffix, REQTEST_GET_REDIRECT_STOP,         \
+           context_mode, false, test_server_backend, test_frame_method)        \
+  REQ_TEST(RendererGETRedirectLocation##suffix, REQTEST_GET_REDIRECT_LOCATION, \
+           context_mode, false, test_server_backend, test_frame_method)        \
+  REQ_TEST(RendererGETReferrer##suffix, REQTEST_GET_REFERRER, context_mode,    \
+           false, test_server_backend, test_frame_method)                      \
+  REQ_TEST(RendererPOST##suffix, REQTEST_POST, context_mode, false,            \
+           test_server_backend, test_frame_method)                             \
+  REQ_TEST(RendererPOSTWithProgress##suffix, REQTEST_POST_WITHPROGRESS,        \
+           context_mode, false, test_server_backend, test_frame_method)        \
+  REQ_TEST(RendererPOSTRedirect##suffix, REQTEST_POST_REDIRECT, context_mode,  \
+           false, test_server_backend, test_frame_method)                      \
+  REQ_TEST(RendererPOSTRedirectToGET##suffix, REQTEST_POST_REDIRECT_TOGET,     \
+           context_mode, false, test_server_backend, test_frame_method)        \
+  REQ_TEST(RendererHEAD##suffix, REQTEST_HEAD, context_mode, false,            \
+           test_server_backend, test_frame_method)
+
+#define REQ_TEST_SET(suffix, test_frame_method)                           \
+  REQ_TEST_SET_EX(ContextGlobalCustom##suffix, CONTEXT_GLOBAL, false,     \
+                  test_frame_method)                                      \
+  REQ_TEST_SET_EX(ContextInMemoryCustom##suffix, CONTEXT_INMEMORY, false, \
+                  test_frame_method)                                      \
+  REQ_TEST_SET_EX(ContextOnDiskCustom##suffix, CONTEXT_ONDISK, false,     \
+                  test_frame_method)                                      \
+  REQ_TEST_SET_EX(ContextGlobalServer##suffix, CONTEXT_GLOBAL, true,      \
+                  test_frame_method)                                      \
+  REQ_TEST_SET_EX(ContextInMemoryServer##suffix, CONTEXT_INMEMORY, true,  \
+                  test_frame_method)                                      \
+  REQ_TEST_SET_EX(ContextOnDiskServer##suffix, CONTEXT_ONDISK, true,      \
+                  test_frame_method)
+
+REQ_TEST_SET(WithoutFrame, false)
+REQ_TEST_SET(WithFrame, true)
+
+// Define tests that can only run with a frame.
+#define REQ_TEST_FRAME_SET_EX(suffix, context_mode, test_server_backend) \
+  REQ_TEST(BrowserIncompleteProcessRequest##suffix,                      \
+           REQTEST_INCOMPLETE_PROCESS_REQUEST, context_mode, true,       \
+           test_server_backend, true)                                    \
+  REQ_TEST(BrowserIncompleteReadResponse##suffix,                        \
+           REQTEST_INCOMPLETE_READ_RESPONSE, context_mode, true,         \
+           test_server_backend, true)                                    \
+  REQ_TEST(RendererIncompleteProcessRequest##suffix,                     \
+           REQTEST_INCOMPLETE_PROCESS_REQUEST, context_mode, false,      \
+           test_server_backend, true)                                    \
+  REQ_TEST(RendererIncompleteReadResponse##suffix,                       \
+           REQTEST_INCOMPLETE_READ_RESPONSE, context_mode, false,        \
+           test_server_backend, true)
+
+#define REQ_TEST_FRAME_SET()                                                 \
+  REQ_TEST_FRAME_SET_EX(ContextGlobalCustomWithFrame, CONTEXT_GLOBAL, false) \
+  REQ_TEST_FRAME_SET_EX(ContextInMemoryCustomWithFrame, CONTEXT_INMEMORY,    \
+                        false)                                               \
+  REQ_TEST_FRAME_SET_EX(ContextOnDiskCustomWithFrame, CONTEXT_ONDISK, false) \
+  REQ_TEST_FRAME_SET_EX(ContextGlobalServerWithFrame, CONTEXT_GLOBAL, true)  \
+  REQ_TEST_FRAME_SET_EX(ContextInMemoryServerWithFrame, CONTEXT_INMEMORY,    \
+                        true)                                                \
+  REQ_TEST_FRAME_SET_EX(ContextOnDiskServerWithFrame, CONTEXT_ONDISK, true)
+
+REQ_TEST_FRAME_SET()
+
+// Cache and authentication tests can only be run with the server backend.
+#define REQ_TEST_CACHE_SET_EX(suffix, context_mode, test_frame_method)         \
+  REQ_TEST(BrowserGETCacheWithControl##suffix, REQTEST_CACHE_WITH_CONTROL,     \
+           context_mode, true, true, test_frame_method)                        \
+  REQ_TEST(BrowserGETCacheWithoutControl##suffix,                              \
+           REQTEST_CACHE_WITHOUT_CONTROL, context_mode, true, true,            \
+           test_frame_method)                                                  \
+  REQ_TEST(BrowserGETCacheSkipFlag##suffix, REQTEST_CACHE_SKIP_FLAG,           \
+           context_mode, true, true, test_frame_method)                        \
+  REQ_TEST(BrowserGETCacheSkipHeader##suffix, REQTEST_CACHE_SKIP_HEADER,       \
+           context_mode, true, true, test_frame_method)                        \
+  REQ_TEST(BrowserGETCacheOnlyFailureFlag##suffix,                             \
+           REQTEST_CACHE_ONLY_FAILURE_FLAG, context_mode, true, true,          \
+           test_frame_method)                                                  \
+  REQ_TEST(BrowserGETCacheOnlyFailureHeader##suffix,                           \
+           REQTEST_CACHE_ONLY_FAILURE_HEADER, context_mode, true, true,        \
+           test_frame_method)                                                  \
+  REQ_TEST(BrowserGETCacheOnlySuccessFlag##suffix,                             \
+           REQTEST_CACHE_ONLY_SUCCESS_FLAG, context_mode, true, true,          \
+           test_frame_method)                                                  \
+  REQ_TEST(BrowserGETCacheOnlySuccessHeader##suffix,                           \
+           REQTEST_CACHE_ONLY_SUCCESS_HEADER, context_mode, true, true,        \
+           test_frame_method)                                                  \
+  REQ_TEST(BrowserGETCacheDisableFlag##suffix, REQTEST_CACHE_DISABLE_FLAG,     \
+           context_mode, true, true, test_frame_method)                        \
+  REQ_TEST(BrowserGETCacheDisableHeader##suffix, REQTEST_CACHE_DISABLE_HEADER, \
+           context_mode, true, true, test_frame_method)                        \
+  REQ_TEST(RendererGETCacheWithControl##suffix, REQTEST_CACHE_WITH_CONTROL,    \
+           context_mode, false, true, test_frame_method)                       \
+  REQ_TEST(RendererGETCacheWithoutControl##suffix,                             \
+           REQTEST_CACHE_WITHOUT_CONTROL, context_mode, false, true,           \
+           test_frame_method)                                                  \
+  REQ_TEST(BrowserGETAuth##suffix, REQTEST_GET_AUTH, context_mode, true, true, \
+           test_frame_method)                                                  \
+  REQ_TEST(RendererGETCacheSkipFlag##suffix, REQTEST_CACHE_SKIP_FLAG,          \
+           context_mode, false, true, test_frame_method)                       \
+  REQ_TEST(RendererGETCacheSkipHeader##suffix, REQTEST_CACHE_SKIP_HEADER,      \
+           context_mode, false, true, test_frame_method)                       \
+  REQ_TEST(RendererGETCacheOnlyFailureFlag##suffix,                            \
+           REQTEST_CACHE_ONLY_FAILURE_FLAG, context_mode, false, true,         \
+           test_frame_method)                                                  \
+  REQ_TEST(RendererGETCacheOnlyFailureHeader##suffix,                          \
+           REQTEST_CACHE_ONLY_FAILURE_HEADER, context_mode, false, true,       \
+           test_frame_method)                                                  \
+  REQ_TEST(RendererGETCacheOnlySuccessFlag##suffix,                            \
+           REQTEST_CACHE_ONLY_SUCCESS_FLAG, context_mode, false, true,         \
+           test_frame_method)                                                  \
+  REQ_TEST(RendererGETCacheOnlySuccessHeader##suffix,                          \
+           REQTEST_CACHE_ONLY_SUCCESS_HEADER, context_mode, false, true,       \
+           test_frame_method)                                                  \
+  REQ_TEST(RendererGETCacheDisableFlag##suffix, REQTEST_CACHE_DISABLE_FLAG,    \
+           context_mode, false, true, test_frame_method)                       \
+  REQ_TEST(RendererGETCacheDisableHeader##suffix,                              \
+           REQTEST_CACHE_DISABLE_HEADER, context_mode, false, true,            \
+           test_frame_method)
+
+#define REQ_TEST_CACHE_SET(suffix, test_frame_method)                    \
+  REQ_TEST_CACHE_SET_EX(ContextGlobalServer##suffix, CONTEXT_GLOBAL,     \
+                        test_frame_method)                               \
+  REQ_TEST_CACHE_SET_EX(ContextInMemoryServer##suffix, CONTEXT_INMEMORY, \
+                        test_frame_method)                               \
+  REQ_TEST_CACHE_SET_EX(ContextOnDiskServer##suffix, CONTEXT_ONDISK,     \
+                        test_frame_method)
+
+REQ_TEST_CACHE_SET(WithoutFrame, false)
+REQ_TEST_CACHE_SET(WithFrame, true)
+
+namespace {
+
+class InvalidURLTestClient : public CefURLRequestClient {
+ public:
+  InvalidURLTestClient() {
+    event_ = CefWaitableEvent::CreateWaitableEvent(true, false);
+  }
+
+  void RunTest() {
+    CefPostTask(TID_UI, base::Bind(&InvalidURLTestClient::RunOnUIThread, this));
+
+    // Wait for the test to complete.
+    event_->Wait();
+  }
+
+  void OnRequestComplete(CefRefPtr<CefURLRequest> client) override {
+    EXPECT_EQ(UR_FAILED, client->GetRequestStatus());
+    EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, client->GetRequestError());
+
+    // Let the call stack unwind before signaling completion.
+    CefPostTask(TID_UI,
+                base::Bind(&InvalidURLTestClient::CompleteOnUIThread, this));
+  }
+
+  void OnUploadProgress(CefRefPtr<CefURLRequest> request,
+                        int64 current,
+                        int64 total) override {
+    EXPECT_TRUE(false);  // Not reached.
+  }
+
+  void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
+                          int64 current,
+                          int64 total) override {
+    EXPECT_TRUE(false);  // Not reached.
+  }
+
+  void OnDownloadData(CefRefPtr<CefURLRequest> request,
+                      const void* data,
+                      size_t data_length) override {
+    EXPECT_TRUE(false);  // Not reached.
+  }
+
+  bool GetAuthCredentials(bool isProxy,
+                          const CefString& host,
+                          int port,
+                          const CefString& realm,
+                          const CefString& scheme,
+                          CefRefPtr<CefAuthCallback> callback) override {
+    EXPECT_TRUE(false);  // Not reached.
+    return false;
+  }
+
+ private:
+  void RunOnUIThread() {
+    EXPECT_UI_THREAD();
+    CefRefPtr<CefRequest> request = CefRequest::Create();
+    request->SetMethod("GET");
+    request->SetURL("foo://invalidurl");
+
+    CefURLRequest::Create(request, this, nullptr);
+  }
+
+  void CompleteOnUIThread() {
+    EXPECT_UI_THREAD();
+    // Signal that the test is complete.
+    event_->Signal();
+  }
+
+  CefRefPtr<CefWaitableEvent> event_;
+
+  IMPLEMENT_REFCOUNTING(InvalidURLTestClient);
+};
+
+}  // namespace
+
+// Verify that failed requests do not leak references.
+TEST(URLRequestTest, BrowserInvalidURL) {
+  CefRefPtr<InvalidURLTestClient> client = new InvalidURLTestClient();
+  client->RunTest();
+}
diff --git a/src/tests/ceftests/v8_unittest.cc b/src/tests/ceftests/v8_unittest.cc
new file mode 100644
index 0000000..f720603
--- /dev/null
+++ b/src/tests/ceftests/v8_unittest.cc
@@ -0,0 +1,3083 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include <sstream>
+
+#include "include/base/cef_bind.h"
+#include "include/cef_task.h"
+#include "include/cef_v8.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+#include "tests/shared/browser/client_app_browser.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+using client::ClientAppBrowser;
+using client::ClientAppRenderer;
+
+// How to add a new test:
+// 1. Add a new value to the V8TestMode enumeration.
+// 2. Add a method that implements the test in V8RendererTest.
+// 3. Add a case for the new enumeration value in V8RendererTest::RunTest.
+// 4. Add a line for the test in the "Define the tests" section at the bottom of
+//    the file.
+
+namespace {
+
+// Unique values for V8 tests.
+const char kV8TestUrl[] = "http://tests/V8Test.Test";
+const char kV8BindingTestUrl[] = "http://tests/V8Test.BindingTest";
+const char kV8ContextParentTestUrl[] = "http://tests/V8Test.ContextParentTest";
+const char kV8ContextChildTestUrl[] = "http://tests/V8Test.ContextChildTest";
+const char kV8NavTestUrl[] = "http://tests/V8Test.NavTest";
+const char kV8ContextEvalCspBypassUnsafeEval[] =
+    "http://tests/V8Test.ContextEvalCspBypassUnsafeEval";
+const char kV8ContextEvalCspBypassSandbox[] =
+    "http://tests/V8Test.ContextEvalCspBypassSandbox";
+const char kV8OnUncaughtExceptionTestUrl[] =
+    "http://tests/V8Test.OnUncaughtException";
+const char kV8HandlerCallOnReleasedContextUrl[] =
+    "http://tests/V8Test.HandlerCallOnReleasedContext/main.html";
+const char kV8HandlerCallOnReleasedContextChildUrl[] =
+    "http://tests/V8Test.HandlerCallOnReleasedContext/child.html";
+const char kV8TestMsg[] = "V8Test.Test";
+const char kV8TestCmdKey[] = "v8-test";
+const char kV8RunTestMsg[] = "V8Test.RunTest";
+
+enum V8TestMode {
+  V8TEST_NONE = 0,
+  V8TEST_NULL_CREATE,
+  V8TEST_BOOL_CREATE,
+  V8TEST_INT_CREATE,
+  V8TEST_UINT_CREATE,
+  V8TEST_DOUBLE_CREATE,
+  V8TEST_DATE_CREATE,
+  V8TEST_STRING_CREATE,
+  V8TEST_EMPTY_STRING_CREATE,
+  V8TEST_ARRAY_CREATE,
+  V8TEST_ARRAY_VALUE,
+  V8TEST_ARRAY_BUFFER,
+  V8TEST_ARRAY_BUFFER_VALUE,
+  V8TEST_OBJECT_CREATE,
+  V8TEST_OBJECT_USERDATA,
+  V8TEST_OBJECT_ACCESSOR,
+  V8TEST_OBJECT_ACCESSOR_EXCEPTION,
+  V8TEST_OBJECT_ACCESSOR_FAIL,
+  V8TEST_OBJECT_ACCESSOR_READONLY,
+  V8TEST_OBJECT_INTERCEPTOR,
+  V8TEST_OBJECT_INTERCEPTOR_FAIL,
+  V8TEST_OBJECT_INTERCEPTOR_EXCEPTION,
+  V8TEST_OBJECT_INTERCEPTOR_AND_ACCESSOR,
+  V8TEST_OBJECT_VALUE,
+  V8TEST_OBJECT_VALUE_READONLY,
+  V8TEST_OBJECT_VALUE_ENUM,
+  V8TEST_OBJECT_VALUE_DONTENUM,
+  V8TEST_OBJECT_VALUE_DELETE,
+  V8TEST_OBJECT_VALUE_DONTDELETE,
+  V8TEST_OBJECT_VALUE_EMPTYKEY,
+  V8TEST_FUNCTION_CREATE,
+  V8TEST_FUNCTION_HANDLER,
+  V8TEST_FUNCTION_HANDLER_EXCEPTION,
+  V8TEST_FUNCTION_HANDLER_FAIL,
+  V8TEST_FUNCTION_HANDLER_NO_OBJECT,
+  V8TEST_FUNCTION_HANDLER_WITH_CONTEXT,
+  V8TEST_FUNCTION_HANDLER_EMPTY_STRING,
+  V8TEST_CONTEXT_EVAL,
+  V8TEST_CONTEXT_EVAL_EXCEPTION,
+  V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL,
+  V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX,
+  V8TEST_CONTEXT_ENTERED,
+  V8TEST_BINDING,
+  V8TEST_STACK_TRACE,
+  V8TEST_ON_UNCAUGHT_EXCEPTION,
+  V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS,
+  V8TEST_EXTENSION,
+  V8TEST_HANDLER_CALL_ON_RELEASED_CONTEXT,
+};
+
+// Renderer side.
+class V8RendererTest : public ClientAppRenderer::Delegate,
+                       public CefLoadHandler {
+ public:
+  V8RendererTest() : test_mode_(V8TEST_NONE) {}
+
+  // Run a test when the process message is received from the browser.
+  void RunTest() {
+    switch (test_mode_) {
+      case V8TEST_NULL_CREATE:
+        RunNullCreateTest();
+        break;
+      case V8TEST_BOOL_CREATE:
+        RunBoolCreateTest();
+        break;
+      case V8TEST_INT_CREATE:
+        RunIntCreateTest();
+        break;
+      case V8TEST_UINT_CREATE:
+        RunUIntCreateTest();
+        break;
+      case V8TEST_DOUBLE_CREATE:
+        RunDoubleCreateTest();
+        break;
+      case V8TEST_DATE_CREATE:
+        RunDateCreateTest();
+        break;
+      case V8TEST_STRING_CREATE:
+        RunStringCreateTest();
+        break;
+      case V8TEST_EMPTY_STRING_CREATE:
+        RunEmptyStringCreateTest();
+        break;
+      case V8TEST_ARRAY_CREATE:
+        RunArrayCreateTest();
+        break;
+      case V8TEST_ARRAY_VALUE:
+        RunArrayValueTest();
+        break;
+      case V8TEST_ARRAY_BUFFER:
+        RunArrayBufferTest();
+        break;
+      case V8TEST_ARRAY_BUFFER_VALUE:
+        RunArrayBufferValueTest();
+        break;
+      case V8TEST_OBJECT_CREATE:
+        RunObjectCreateTest();
+        break;
+      case V8TEST_OBJECT_USERDATA:
+        RunObjectUserDataTest();
+        break;
+      case V8TEST_OBJECT_ACCESSOR:
+        RunObjectAccessorTest();
+        break;
+      case V8TEST_OBJECT_ACCESSOR_EXCEPTION:
+        RunObjectAccessorExceptionTest();
+        break;
+      case V8TEST_OBJECT_ACCESSOR_FAIL:
+        RunObjectAccessorFailTest();
+        break;
+      case V8TEST_OBJECT_ACCESSOR_READONLY:
+        RunObjectAccessorReadOnlyTest();
+        break;
+      case V8TEST_OBJECT_INTERCEPTOR:
+        RunObjectInterceptorTest();
+        break;
+      case V8TEST_OBJECT_INTERCEPTOR_FAIL:
+        RunObjectInterceptorFailTest();
+        break;
+      case V8TEST_OBJECT_INTERCEPTOR_EXCEPTION:
+        RunObjectInterceptorExceptionTest();
+        break;
+      case V8TEST_OBJECT_INTERCEPTOR_AND_ACCESSOR:
+        RunObjectInterceptorAndAccessorTest();
+        break;
+      case V8TEST_OBJECT_VALUE:
+        RunObjectValueTest();
+        break;
+      case V8TEST_OBJECT_VALUE_READONLY:
+        RunObjectValueReadOnlyTest();
+        break;
+      case V8TEST_OBJECT_VALUE_ENUM:
+        RunObjectValueEnumTest();
+        break;
+      case V8TEST_OBJECT_VALUE_DONTENUM:
+        RunObjectValueDontEnumTest();
+        break;
+      case V8TEST_OBJECT_VALUE_DELETE:
+        RunObjectValueDeleteTest();
+        break;
+      case V8TEST_OBJECT_VALUE_DONTDELETE:
+        RunObjectValueDontDeleteTest();
+        break;
+      case V8TEST_OBJECT_VALUE_EMPTYKEY:
+        RunObjectValueEmptyKeyTest();
+        break;
+      case V8TEST_FUNCTION_CREATE:
+        RunFunctionCreateTest();
+        break;
+      case V8TEST_FUNCTION_HANDLER:
+        RunFunctionHandlerTest();
+        break;
+      case V8TEST_FUNCTION_HANDLER_EXCEPTION:
+        RunFunctionHandlerExceptionTest();
+        break;
+      case V8TEST_FUNCTION_HANDLER_FAIL:
+        RunFunctionHandlerFailTest();
+        break;
+      case V8TEST_FUNCTION_HANDLER_NO_OBJECT:
+        RunFunctionHandlerNoObjectTest();
+        break;
+      case V8TEST_FUNCTION_HANDLER_WITH_CONTEXT:
+        RunFunctionHandlerWithContextTest();
+        break;
+      case V8TEST_FUNCTION_HANDLER_EMPTY_STRING:
+        RunFunctionHandlerEmptyStringTest();
+        break;
+      case V8TEST_CONTEXT_EVAL:
+        RunContextEvalTest();
+        break;
+      case V8TEST_CONTEXT_EVAL_EXCEPTION:
+        RunContextEvalExceptionTest();
+        break;
+      case V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL:
+        RunContextEvalCspBypassUnsafeEval();
+        break;
+      case V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX:
+        RunContextEvalCspBypassSandbox();
+        break;
+      case V8TEST_CONTEXT_ENTERED:
+        RunContextEnteredTest();
+        break;
+      case V8TEST_BINDING:
+        RunBindingTest();
+        break;
+      case V8TEST_STACK_TRACE:
+        RunStackTraceTest();
+        break;
+      case V8TEST_ON_UNCAUGHT_EXCEPTION:
+        RunOnUncaughtExceptionTest();
+        break;
+      case V8TEST_HANDLER_CALL_ON_RELEASED_CONTEXT:
+        break;
+      default:
+        // Was a startup test.
+        EXPECT_TRUE(startup_test_success_);
+        DestroyTest();
+        break;
+    }
+  }
+
+  // Run a test on render process startup.
+  void RunStartupTest() {
+    switch (test_mode_) {
+      case V8TEST_EXTENSION:
+        RunExtensionTest();
+        break;
+      default:
+        break;
+    }
+  }
+
+  void RunNullCreateTest() {
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateNull();
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsNull());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsObject());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunBoolCreateTest() {
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateBool(true);
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsBool());
+    EXPECT_EQ(true, value->GetBoolValue());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsObject());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunIntCreateTest() {
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateInt(12);
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsInt());
+    EXPECT_TRUE(value->IsUInt());
+    EXPECT_TRUE(value->IsDouble());
+    EXPECT_EQ(12, value->GetIntValue());
+    EXPECT_EQ((uint32)12, value->GetUIntValue());
+    EXPECT_EQ(12, value->GetDoubleValue());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsObject());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunUIntCreateTest() {
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateUInt(12);
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsInt());
+    EXPECT_TRUE(value->IsUInt());
+    EXPECT_TRUE(value->IsDouble());
+    EXPECT_EQ(12, value->GetIntValue());
+    EXPECT_EQ((uint32)12, value->GetUIntValue());
+    EXPECT_EQ(12, value->GetDoubleValue());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsObject());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunDoubleCreateTest() {
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateDouble(12.1223);
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsDouble());
+    EXPECT_EQ(12.1223, value->GetDoubleValue());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsObject());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunDateCreateTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    CefTime date;
+    date.year = 2200;
+    date.month = 4;
+#if !defined(OS_MACOSX)
+    date.day_of_week = 5;
+#endif
+    date.day_of_month = 11;
+    date.hour = 20;
+    date.minute = 15;
+    date.second = 42;
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateDate(date);
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsDate());
+    EXPECT_EQ(date.GetTimeT(), value->GetDateValue().GetTimeT());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsObject());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunStringCreateTest() {
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateString("My string");
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ("My string", value->GetStringValue().ToString().c_str());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsObject());
+
+    DestroyTest();
+  }
+
+  void RunEmptyStringCreateTest() {
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateString(CefString());
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ("", value->GetStringValue().ToString().c_str());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsObject());
+
+    DestroyTest();
+  }
+
+  void RunArrayCreateTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateArray(2);
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsArray());
+    EXPECT_TRUE(value->IsObject());
+    EXPECT_EQ(2, value->GetArrayLength());
+    EXPECT_FALSE(value->HasValue(0));
+    EXPECT_FALSE(value->HasValue(1));
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunArrayValueTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateArray(0);
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsArray());
+    EXPECT_EQ(0, value->GetArrayLength());
+
+    // Test addng values.
+    EXPECT_FALSE(value->HasValue(0));
+    EXPECT_FALSE(value->HasValue(1));
+
+    EXPECT_TRUE(value->SetValue(0, CefV8Value::CreateInt(10)));
+    EXPECT_FALSE(value->HasException());
+    EXPECT_TRUE(value->HasValue(0));
+    EXPECT_FALSE(value->HasValue(1));
+
+    EXPECT_TRUE(value->GetValue(0)->IsInt());
+    EXPECT_EQ(10, value->GetValue(0)->GetIntValue());
+    EXPECT_FALSE(value->HasException());
+    EXPECT_EQ(1, value->GetArrayLength());
+
+    EXPECT_TRUE(value->SetValue(1, CefV8Value::CreateInt(43)));
+    EXPECT_FALSE(value->HasException());
+    EXPECT_TRUE(value->HasValue(0));
+    EXPECT_TRUE(value->HasValue(1));
+
+    EXPECT_TRUE(value->GetValue(1)->IsInt());
+    EXPECT_EQ(43, value->GetValue(1)->GetIntValue());
+    EXPECT_FALSE(value->HasException());
+    EXPECT_EQ(2, value->GetArrayLength());
+
+    EXPECT_TRUE(value->DeleteValue(0));
+    EXPECT_FALSE(value->HasValue(0));
+    EXPECT_TRUE(value->HasValue(1));
+    EXPECT_EQ(2, value->GetArrayLength());
+
+    EXPECT_TRUE(value->DeleteValue(1));
+    EXPECT_FALSE(value->HasValue(0));
+    EXPECT_FALSE(value->HasValue(1));
+    EXPECT_EQ(2, value->GetArrayLength());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunArrayBufferTest() {
+    class TestArrayBufferReleaseCallback
+        : public CefV8ArrayBufferReleaseCallback {
+     public:
+      TestArrayBufferReleaseCallback(bool* destructorCalled,
+                                     bool* releaseBufferCalled)
+          : destructorCalled_(destructorCalled),
+            releaseBufferCalled_(releaseBufferCalled) {}
+
+      ~TestArrayBufferReleaseCallback() { *destructorCalled_ = true; }
+
+      void ReleaseBuffer(void* buffer) override {
+        *releaseBufferCalled_ = true;
+      }
+
+      IMPLEMENT_REFCOUNTING(TestArrayBufferReleaseCallback);
+
+     private:
+      bool* destructorCalled_;
+      bool* releaseBufferCalled_;
+    };
+
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    bool destructorCalled = false;
+    bool releaseBufferCalled = false;
+
+    bool neuteredDestructorCalled = false;
+    bool neuteredReleaseBufferCalled = false;
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+    {
+      int static_data[16];
+      CefRefPtr<CefV8Value> value;
+      CefRefPtr<TestArrayBufferReleaseCallback> release_callback =
+          new TestArrayBufferReleaseCallback(&destructorCalled,
+                                             &releaseBufferCalled);
+
+      CefRefPtr<CefV8Value> neuteredValue;
+      CefRefPtr<TestArrayBufferReleaseCallback> neuteredReleaseCallback =
+          new TestArrayBufferReleaseCallback(&neuteredDestructorCalled,
+                                             &neuteredReleaseBufferCalled);
+      value = CefV8Value::CreateArrayBuffer(static_data, sizeof(static_data),
+                                            release_callback);
+      neuteredValue = CefV8Value::CreateArrayBuffer(
+          static_data, sizeof(static_data), neuteredReleaseCallback);
+      EXPECT_TRUE(value.get());
+      EXPECT_TRUE(value->IsArrayBuffer());
+      EXPECT_TRUE(value->IsObject());
+      EXPECT_FALSE(value->HasValue(0));
+      EXPECT_FALSE(destructorCalled);
+      EXPECT_TRUE(value->GetArrayBufferReleaseCallback().get() != nullptr);
+      EXPECT_TRUE(((TestArrayBufferReleaseCallback*)value
+                       ->GetArrayBufferReleaseCallback()
+                       .get()) == release_callback);
+
+      EXPECT_TRUE(neuteredValue->NeuterArrayBuffer());
+    }
+    // Exit the V8 context.
+    EXPECT_TRUE(destructorCalled);
+    EXPECT_TRUE(releaseBufferCalled);
+    EXPECT_TRUE(neuteredDestructorCalled);
+    EXPECT_FALSE(neuteredReleaseBufferCalled);
+    EXPECT_TRUE(context->Exit());
+    DestroyTest();
+  }
+
+  void RunArrayBufferValueTest() {
+    class TestArrayBufferReleaseCallback
+        : public CefV8ArrayBufferReleaseCallback {
+     public:
+      TestArrayBufferReleaseCallback() {}
+
+      ~TestArrayBufferReleaseCallback() {}
+
+      void ReleaseBuffer(void* buffer) override {}
+
+      IMPLEMENT_REFCOUNTING(TestArrayBufferReleaseCallback);
+    };
+
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    // Enter the V8 context.
+    CefRefPtr<CefV8Value> value;
+
+    CefRefPtr<TestArrayBufferReleaseCallback> owner =
+        new TestArrayBufferReleaseCallback();
+    EXPECT_TRUE(context->Enter());
+    int static_data[16];
+    static_data[0] = 3;
+    value =
+        CefV8Value::CreateArrayBuffer(static_data, sizeof(static_data), owner);
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+    object->SetValue("arr", value, V8_PROPERTY_ATTRIBUTE_NONE);
+    std::string test =
+        "let data = new Int32Array(window.arr); data[0] += data.length";
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+    EXPECT_TRUE(context->Eval(test, CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    EXPECT_TRUE(static_data[0] == 19);
+    EXPECT_TRUE(value->GetArrayBufferReleaseCallback().get() != nullptr);
+    EXPECT_TRUE(value->NeuterArrayBuffer());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+    DestroyTest();
+  }
+
+  void RunObjectCreateTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateObject(nullptr, nullptr);
+
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsObject());
+    EXPECT_FALSE(value->GetUserData().get());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsFunction());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsString());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectUserDataTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    class UserData : public CefBaseRefCounted {
+     public:
+      explicit UserData(int value) : value_(value) {}
+      int value_;
+      IMPLEMENT_REFCOUNTING(UserData);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateObject(nullptr, nullptr);
+    EXPECT_TRUE(value.get());
+
+    EXPECT_TRUE(value->SetUserData(new UserData(10)));
+
+    CefRefPtr<CefBaseRefCounted> user_data = value->GetUserData();
+    EXPECT_TRUE(user_data.get());
+    UserData* user_data_impl = static_cast<UserData*>(user_data.get());
+    EXPECT_EQ(10, user_data_impl->value_);
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectAccessorTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "val";
+    static const int kValue = 20;
+
+    class Accessor : public CefV8Accessor {
+     public:
+      Accessor() : value_(0) {}
+      bool Get(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               CefRefPtr<CefV8Value>& retval,
+               CefString& exception) override {
+        EXPECT_STREQ(kName, name.ToString().c_str());
+
+        EXPECT_TRUE(object.get());
+        EXPECT_TRUE(object->IsSame(object_));
+
+        EXPECT_FALSE(retval.get());
+        EXPECT_TRUE(exception.empty());
+
+        got_get_.yes();
+        retval = CefV8Value::CreateInt(value_);
+        EXPECT_EQ(kValue, retval->GetIntValue());
+        return true;
+      }
+
+      bool Set(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               const CefRefPtr<CefV8Value> value,
+               CefString& exception) override {
+        EXPECT_STREQ(kName, name.ToString().c_str());
+
+        EXPECT_TRUE(object.get());
+        EXPECT_TRUE(object->IsSame(object_));
+
+        EXPECT_TRUE(value.get());
+        EXPECT_TRUE(exception.empty());
+
+        got_set_.yes();
+        value_ = value->GetIntValue();
+        EXPECT_EQ(kValue, value_);
+        return true;
+      }
+
+      CefRefPtr<CefV8Value> object_;
+      int value_;
+      TrackCallback got_get_;
+      TrackCallback got_set_;
+
+      IMPLEMENT_REFCOUNTING(Accessor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Accessor* accessor = new Accessor;
+    CefRefPtr<CefV8Accessor> accessorPtr(accessor);
+
+    CefRefPtr<CefV8Value> object = CefV8Value::CreateObject(accessor, nullptr);
+    EXPECT_TRUE(object.get());
+    accessor->object_ = object;
+
+    EXPECT_FALSE(object->HasValue(kName));
+
+    EXPECT_TRUE(object->SetValue(kName, V8_ACCESS_CONTROL_DEFAULT,
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(object->HasValue(kName));
+
+    EXPECT_TRUE(object->SetValue(kName, CefV8Value::CreateInt(kValue),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(accessor->got_set_);
+    EXPECT_EQ(kValue, accessor->value_);
+
+    CefRefPtr<CefV8Value> val = object->GetValue(kName);
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(val.get());
+    EXPECT_TRUE(accessor->got_get_);
+    EXPECT_TRUE(val->IsInt());
+    EXPECT_EQ(kValue, val->GetIntValue());
+
+    accessor->object_ = nullptr;
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectAccessorExceptionTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "val";
+    static const char* kGetException = "My get exception";
+    static const char* kSetException = "My set exception";
+    static const char* kGetExceptionMsg = "Uncaught Error: My get exception";
+    static const char* kSetExceptionMsg = "Uncaught Error: My set exception";
+
+    class Accessor : public CefV8Accessor {
+     public:
+      Accessor() {}
+      bool Get(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               CefRefPtr<CefV8Value>& retval,
+               CefString& exception) override {
+        got_get_.yes();
+        exception = kGetException;
+        return true;
+      }
+
+      bool Set(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               const CefRefPtr<CefV8Value> value,
+               CefString& exception) override {
+        got_set_.yes();
+        exception = kSetException;
+        return true;
+      }
+
+      TrackCallback got_get_;
+      TrackCallback got_set_;
+
+      IMPLEMENT_REFCOUNTING(Accessor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Exception> exception;
+    Accessor* accessor = new Accessor;
+    CefRefPtr<CefV8Accessor> accessorPtr(accessor);
+
+    CefRefPtr<CefV8Value> object = CefV8Value::CreateObject(accessor, nullptr);
+    EXPECT_TRUE(object.get());
+
+    EXPECT_FALSE(object->HasValue(kName));
+
+    EXPECT_TRUE(object->SetValue(kName, V8_ACCESS_CONTROL_DEFAULT,
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(object->HasValue(kName));
+
+    EXPECT_FALSE(object->SetValue(kName, CefV8Value::CreateInt(1),
+                                  V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_TRUE(object->HasException());
+    EXPECT_TRUE(accessor->got_set_);
+    exception = object->GetException();
+    EXPECT_TRUE(exception.get());
+    EXPECT_STREQ(kSetExceptionMsg, exception->GetMessage().ToString().c_str());
+
+    EXPECT_TRUE(object->ClearException());
+    EXPECT_FALSE(object->HasException());
+
+    CefRefPtr<CefV8Value> val = object->GetValue(kName);
+    EXPECT_FALSE(val.get());
+    EXPECT_TRUE(object->HasException());
+    EXPECT_TRUE(accessor->got_get_);
+    exception = object->GetException();
+    EXPECT_TRUE(exception.get());
+    EXPECT_STREQ(kGetExceptionMsg, exception->GetMessage().ToString().c_str());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectAccessorFailTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "val";
+
+    class Accessor : public CefV8Accessor {
+     public:
+      Accessor() {}
+      bool Get(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               CefRefPtr<CefV8Value>& retval,
+               CefString& exception) override {
+        got_get_.yes();
+        return false;
+      }
+
+      bool Set(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               const CefRefPtr<CefV8Value> value,
+               CefString& exception) override {
+        got_set_.yes();
+        return false;
+      }
+
+      TrackCallback got_get_;
+      TrackCallback got_set_;
+
+      IMPLEMENT_REFCOUNTING(Accessor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Exception> exception;
+    Accessor* accessor = new Accessor;
+    CefRefPtr<CefV8Accessor> accessorPtr(accessor);
+
+    CefRefPtr<CefV8Value> object = CefV8Value::CreateObject(accessor, nullptr);
+    EXPECT_TRUE(object.get());
+
+    EXPECT_FALSE(object->HasValue(kName));
+
+    EXPECT_TRUE(object->SetValue(kName, V8_ACCESS_CONTROL_DEFAULT,
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(object->HasValue(kName));
+
+    EXPECT_TRUE(object->SetValue(kName, CefV8Value::CreateInt(1),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(accessor->got_set_);
+
+    CefRefPtr<CefV8Value> val = object->GetValue(kName);
+    EXPECT_TRUE(val.get());
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(accessor->got_get_);
+    EXPECT_TRUE(val->IsUndefined());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectAccessorReadOnlyTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "val";
+
+    class Accessor : public CefV8Accessor {
+     public:
+      Accessor() {}
+      bool Get(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               CefRefPtr<CefV8Value>& retval,
+               CefString& exception) override {
+        got_get_.yes();
+        return true;
+      }
+
+      bool Set(const CefString& name,
+               const CefRefPtr<CefV8Value> object,
+               const CefRefPtr<CefV8Value> value,
+               CefString& exception) override {
+        got_set_.yes();
+        return true;
+      }
+
+      TrackCallback got_get_;
+      TrackCallback got_set_;
+
+      IMPLEMENT_REFCOUNTING(Accessor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Exception> exception;
+    Accessor* accessor = new Accessor;
+    CefRefPtr<CefV8Accessor> accessorPtr(accessor);
+
+    CefRefPtr<CefV8Value> object = CefV8Value::CreateObject(accessor, nullptr);
+    EXPECT_TRUE(object.get());
+
+    EXPECT_FALSE(object->HasValue(kName));
+
+    EXPECT_TRUE(object->SetValue(kName, V8_ACCESS_CONTROL_DEFAULT,
+                                 V8_PROPERTY_ATTRIBUTE_READONLY));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(object->HasValue(kName));
+
+    EXPECT_TRUE(object->SetValue(kName, CefV8Value::CreateInt(1),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_FALSE(accessor->got_set_);
+
+    CefRefPtr<CefV8Value> val = object->GetValue(kName);
+    EXPECT_TRUE(val.get());
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(accessor->got_get_);
+    EXPECT_TRUE(val->IsUndefined());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectInterceptorTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName1 = "val1";
+    static const char* kName2 = "val2";
+    static const char* kName3 = "val3";
+
+    static const int kValue1 = 20;
+    static const uint32 kValue2 = 30u;
+    static const char* kValue3 = "40";
+
+    static const int kArray[] = {50, 60, 70};
+
+    class Interceptor : public CefV8Interceptor {
+     public:
+      Interceptor() : value1_(0), value2_(0u), array_() {}
+      virtual bool Get(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        EXPECT_TRUE(name.ToString() == kName1 || name.ToString() == kName2 ||
+                    name.ToString() == kName3);
+
+        EXPECT_TRUE(object.get());
+        EXPECT_TRUE(object->IsSame(object_));
+
+        EXPECT_FALSE(retval.get());
+        EXPECT_TRUE(exception.empty());
+
+        got_get_byname_.yes();
+        if (name.ToString() == kName1) {
+          retval = CefV8Value::CreateInt(value1_);
+          EXPECT_EQ(kValue1, retval->GetIntValue());
+        } else if (name.ToString() == kName2) {
+          retval = CefV8Value::CreateUInt(value2_);
+          EXPECT_EQ(kValue2, retval->GetUIntValue());
+        } else if (name.ToString() == kName3) {
+          retval = CefV8Value::CreateString(value3_);
+          EXPECT_STREQ(kValue3, retval->GetStringValue().ToString().c_str());
+        }
+        return true;
+      }
+
+      virtual bool Get(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        EXPECT_TRUE(index >= 0 && index < 3);
+
+        EXPECT_TRUE(object.get());
+        EXPECT_TRUE(object->IsSame(object_));
+
+        EXPECT_FALSE(retval.get());
+        EXPECT_TRUE(exception.empty());
+
+        got_get_byindex_.yes();
+        retval = CefV8Value::CreateInt(array_[index]);
+        EXPECT_EQ(kArray[index], retval->GetIntValue());
+        return true;
+      }
+
+      virtual bool Set(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        EXPECT_TRUE(name.ToString() == kName1 || name.ToString() == kName2 ||
+                    name.ToString() == kName3);
+
+        EXPECT_TRUE(object.get());
+        EXPECT_TRUE(object->IsSame(object_));
+
+        EXPECT_TRUE(value.get());
+        EXPECT_TRUE(exception.empty());
+
+        got_set_byname_.yes();
+        if (name.ToString() == kName1) {
+          value1_ = value->GetIntValue();
+          EXPECT_EQ(kValue1, value1_);
+        } else if (name.ToString() == kName2) {
+          value2_ = value->GetUIntValue();
+          EXPECT_EQ(kValue2, value2_);
+        } else if (name.ToString() == kName3) {
+          value3_ = value->GetStringValue();
+          EXPECT_STREQ(kValue3, value3_.ToString().c_str());
+        }
+        return true;
+      }
+
+      virtual bool Set(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        EXPECT_TRUE(index >= 0 && index < 3);
+
+        EXPECT_TRUE(object.get());
+        EXPECT_TRUE(object->IsSame(object_));
+
+        EXPECT_TRUE(value.get());
+        EXPECT_TRUE(exception.empty());
+
+        got_set_byindex_.yes();
+        array_[index] = value->GetIntValue();
+        EXPECT_EQ(array_[index], kArray[index]);
+        return true;
+      }
+
+      CefRefPtr<CefV8Value> object_;
+      int value1_;
+      unsigned int value2_;
+      CefString value3_;
+      int array_[3];
+
+      TrackCallback got_get_byname_;
+      TrackCallback got_get_byindex_;
+      TrackCallback got_set_byname_;
+      TrackCallback got_set_byindex_;
+
+      IMPLEMENT_REFCOUNTING(Interceptor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Interceptor* interceptor = new Interceptor;
+    CefRefPtr<CefV8Interceptor> interceptorPtr(interceptor);
+
+    CefRefPtr<CefV8Value> object =
+        CefV8Value::CreateObject(nullptr, interceptor);
+    EXPECT_TRUE(object.get());
+    interceptor->object_ = object;
+
+    EXPECT_FALSE(object->HasException());
+
+    EXPECT_TRUE(object->SetValue(kName1, CefV8Value::CreateInt(kValue1),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_set_byname_);
+    interceptor->got_set_byname_.reset();
+
+    EXPECT_TRUE(object->SetValue(kName2, CefV8Value::CreateUInt(kValue2),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_set_byname_);
+    interceptor->got_set_byname_.reset();
+
+    EXPECT_TRUE(object->SetValue(kName3, CefV8Value::CreateString(kValue3),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_set_byname_);
+    interceptor->got_set_byname_.reset();
+
+    EXPECT_EQ(kValue1, interceptor->value1_);
+    EXPECT_EQ(kValue2, interceptor->value2_);
+    EXPECT_STREQ(kValue3, interceptor->value3_.ToString().c_str());
+
+    for (int i = 0; i < 3; ++i) {
+      EXPECT_TRUE(object->SetValue(i, CefV8Value::CreateInt(kArray[i])));
+      EXPECT_FALSE(object->HasException());
+      EXPECT_TRUE(interceptor->got_set_byindex_);
+      interceptor->got_set_byindex_.reset();
+      EXPECT_EQ(kArray[i], interceptor->array_[i]);
+    }
+
+    CefRefPtr<CefV8Value> val1 = object->GetValue(kName1);
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(val1.get());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    interceptor->got_get_byname_.reset();
+    EXPECT_TRUE(val1->IsInt());
+    EXPECT_EQ(kValue1, val1->GetIntValue());
+
+    CefRefPtr<CefV8Value> val2 = object->GetValue(kName2);
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(val2.get());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    interceptor->got_get_byname_.reset();
+    EXPECT_TRUE(val2->IsUInt());
+    EXPECT_EQ(kValue2, val2->GetUIntValue());
+
+    CefRefPtr<CefV8Value> val3 = object->GetValue(kName3);
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(val3.get());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    interceptor->got_get_byname_.reset();
+    EXPECT_TRUE(val3->IsString());
+    EXPECT_STREQ(kValue3, val3->GetStringValue().ToString().c_str());
+
+    for (int i = 0; i < 3; ++i) {
+      CefRefPtr<CefV8Value> val = object->GetValue(i);
+      EXPECT_FALSE(object->HasException());
+      EXPECT_TRUE(val.get());
+      EXPECT_TRUE(interceptor->got_get_byindex_);
+      interceptor->got_get_byname_.reset();
+      EXPECT_EQ(kArray[i], val->GetIntValue());
+    }
+
+    interceptor->object_ = nullptr;
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectInterceptorFailTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "val";
+    static const int kIndex = 0;
+    static const int kValue1 = 20;
+    static const int kValue2 = 30;
+
+    class Interceptor : public CefV8Interceptor {
+      typedef std::map<int, int> IntMap;
+      typedef std::map<std::string, int> StringMap;
+
+     public:
+      Interceptor() {}
+      virtual bool Get(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        got_get_byname_.yes();
+        StringMap::iterator it = string_map_.find(name.ToString());
+        if (it != string_map_.end()) {
+          retval = CefV8Value::CreateInt(it->second);
+        }
+        return true;
+      }
+
+      virtual bool Get(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        got_get_byindex_.yes();
+        IntMap::iterator it = int_map_.find(index);
+        if (it != int_map_.end()) {
+          retval = CefV8Value::CreateInt(it->second);
+        }
+        return true;
+      }
+
+      virtual bool Set(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        EXPECT_TRUE(value->IsInt());
+        got_set_byname_.yes();
+        string_map_[name.ToString()] = value->GetIntValue();
+        return true;
+      }
+
+      virtual bool Set(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        EXPECT_TRUE(value->IsInt());
+        got_set_byindex_.yes();
+        int_map_[index] = value->GetIntValue();
+        return true;
+      }
+
+      IntMap int_map_;
+      StringMap string_map_;
+
+      TrackCallback got_get_byname_;
+      TrackCallback got_get_byindex_;
+      TrackCallback got_set_byname_;
+      TrackCallback got_set_byindex_;
+
+      IMPLEMENT_REFCOUNTING(Interceptor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Interceptor* interceptor = new Interceptor;
+    CefRefPtr<CefV8Interceptor> interceptorPtr(interceptor);
+
+    CefRefPtr<CefV8Value> object =
+        CefV8Value::CreateObject(nullptr, interceptor);
+    EXPECT_TRUE(object.get());
+
+    EXPECT_FALSE(object->HasValue(kName));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    interceptor->got_get_byname_.reset();
+
+    CefRefPtr<CefV8Value> val1 = object->GetValue(kName);
+    EXPECT_TRUE(val1.get());
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    EXPECT_TRUE(val1->IsUndefined());
+    interceptor->got_get_byname_.reset();
+
+    EXPECT_TRUE(object->SetValue(kName, CefV8Value::CreateInt(kValue1),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_set_byname_);
+
+    val1 = object->GetValue(kName);
+    EXPECT_TRUE(val1.get());
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    EXPECT_EQ(kValue1, val1->GetIntValue());
+
+    EXPECT_FALSE(object->HasValue(kIndex));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byindex_);
+    interceptor->got_get_byindex_.reset();
+
+    CefRefPtr<CefV8Value> val2 = object->GetValue(kIndex);
+    EXPECT_TRUE(val2.get());
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byindex_);
+    EXPECT_TRUE(val2->IsUndefined());
+    interceptor->got_get_byindex_.reset();
+
+    EXPECT_TRUE(object->SetValue(kIndex, CefV8Value::CreateInt(kValue2)));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_set_byindex_);
+
+    val2 = object->GetValue(kIndex);
+    EXPECT_TRUE(val2.get());
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byindex_);
+    EXPECT_EQ(kValue2, val2->GetIntValue());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectInterceptorExceptionTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+    static const char* kName = "val";
+    static const int kIndex = 1;
+
+    static const char* kGetByNameException = "My get_byname exception";
+    static const char* kGetByIndexException = "My get_byindex exception";
+    static const char* kSetByNameException = "My set_byname exception";
+    static const char* kSetByIndexException = "My set_byindex exception";
+
+    static const char* kGetByNameExceptionMsg =
+        "Uncaught Error: My get_byname exception";
+    static const char* kGetByIndexExceptionMsg =
+        "Uncaught Error: My get_byindex exception";
+    static const char* kSetByNameExceptionMsg =
+        "Uncaught Error: My set_byname exception";
+    static const char* kSetByIndexExceptionMsg =
+        "Uncaught Error: My set_byindex exception";
+
+    class Interceptor : public CefV8Interceptor {
+     public:
+      Interceptor() {}
+      virtual bool Get(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        got_get_byname_.yes();
+        exception = kGetByNameException;
+        return true;
+      }
+
+      virtual bool Get(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        got_get_byindex_.yes();
+        exception = kGetByIndexException;
+        return true;
+      }
+
+      virtual bool Set(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        got_set_byname_.yes();
+        exception = kSetByNameException;
+        return true;
+      }
+
+      virtual bool Set(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        got_set_byindex_.yes();
+        exception = kSetByIndexException;
+        return true;
+      }
+
+      TrackCallback got_get_byname_;
+      TrackCallback got_get_byindex_;
+      TrackCallback got_set_byname_;
+      TrackCallback got_set_byindex_;
+
+      IMPLEMENT_REFCOUNTING(Interceptor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Exception> exception;
+    Interceptor* interceptor = new Interceptor;
+    CefRefPtr<CefV8Interceptor> interceptorPtr(interceptor);
+
+    CefRefPtr<CefV8Value> object =
+        CefV8Value::CreateObject(nullptr, interceptor);
+    EXPECT_TRUE(object.get());
+
+    EXPECT_FALSE(object->SetValue(kName, CefV8Value::CreateInt(1),
+                                  V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_TRUE(object->HasException());
+    EXPECT_TRUE(interceptor->got_set_byname_);
+    exception = object->GetException();
+    EXPECT_TRUE(exception.get());
+    EXPECT_STREQ(kSetByNameExceptionMsg,
+                 exception->GetMessage().ToString().c_str());
+
+    EXPECT_TRUE(object->ClearException());
+    EXPECT_FALSE(object->HasException());
+
+    CefRefPtr<CefV8Value> val1 = object->GetValue(kName);
+    EXPECT_FALSE(val1.get());
+    EXPECT_TRUE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    exception = object->GetException();
+    EXPECT_TRUE(exception.get());
+    EXPECT_STREQ(kGetByNameExceptionMsg,
+                 exception->GetMessage().ToString().c_str());
+
+    EXPECT_TRUE(object->ClearException());
+    EXPECT_FALSE(object->HasException());
+
+    EXPECT_FALSE(object->SetValue(kIndex, CefV8Value::CreateInt(1)));
+    EXPECT_TRUE(object->HasException());
+    EXPECT_TRUE(interceptor->got_set_byindex_);
+    exception = object->GetException();
+    EXPECT_TRUE(exception.get());
+    EXPECT_STREQ(kSetByIndexExceptionMsg,
+                 exception->GetMessage().ToString().c_str());
+
+    EXPECT_TRUE(object->ClearException());
+    EXPECT_FALSE(object->HasException());
+
+    CefRefPtr<CefV8Value> val2 = object->GetValue(kIndex);
+    EXPECT_FALSE(val2.get());
+    EXPECT_TRUE(object->HasException());
+    EXPECT_TRUE(interceptor->got_get_byindex_);
+    exception = object->GetException();
+    EXPECT_TRUE(exception.get());
+    EXPECT_STREQ(kGetByIndexExceptionMsg,
+                 exception->GetMessage().ToString().c_str());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectInterceptorAndAccessorTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+    static const char* kInterceptorName = "val1";
+    static const char* kAccessorName = "val2";
+
+    static const int kInterceptorValue = 20;
+    static const int kAccessorValue = 30;
+
+    class Interceptor : public CefV8Interceptor {
+     public:
+      Interceptor() {}
+      virtual bool Get(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        EXPECT_FALSE(retval.get());
+        got_get_byname_.yes();
+        if (name.ToString() == kInterceptorName) {
+          retval = CefV8Value::CreateInt(kInterceptorValue);
+        }
+        return true;
+      }
+
+      virtual bool Get(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        got_get_byindex_.yes();
+        return true;
+      }
+
+      virtual bool Set(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        got_set_byname_.yes();
+        return true;
+      }
+
+      virtual bool Set(int index,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        got_set_byindex_.yes();
+        return true;
+      }
+
+      TrackCallback got_get_byname_;
+      TrackCallback got_get_byindex_;
+      TrackCallback got_set_byname_;
+      TrackCallback got_set_byindex_;
+
+      IMPLEMENT_REFCOUNTING(Interceptor);
+    };
+
+    class Accessor : public CefV8Accessor {
+     public:
+      Accessor() {}
+      virtual bool Get(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       CefRefPtr<CefV8Value>& retval,
+                       CefString& exception) OVERRIDE {
+        got_get_.yes();
+        retval = CefV8Value::CreateInt(kAccessorValue);
+        return true;
+      }
+
+      virtual bool Set(const CefString& name,
+                       const CefRefPtr<CefV8Value> object,
+                       const CefRefPtr<CefV8Value> value,
+                       CefString& exception) OVERRIDE {
+        got_set_.yes();
+        return true;
+      }
+
+      TrackCallback got_get_;
+      TrackCallback got_set_;
+
+      IMPLEMENT_REFCOUNTING(Accessor);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Interceptor* interceptor = new Interceptor;
+    CefRefPtr<CefV8Interceptor> interceptorPtr(interceptor);
+
+    Accessor* accessor = new Accessor;
+    CefRefPtr<CefV8Accessor> accessorPtr(accessor);
+
+    CefRefPtr<CefV8Value> object =
+        CefV8Value::CreateObject(accessor, interceptor);
+    EXPECT_TRUE(object.get());
+
+    // We register both names for accessor.
+    EXPECT_TRUE(object->SetValue(kAccessorName, V8_ACCESS_CONTROL_DEFAULT,
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+
+    EXPECT_TRUE(object->SetValue(kInterceptorName, V8_ACCESS_CONTROL_DEFAULT,
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+
+    EXPECT_TRUE(object->SetValue(kAccessorName,
+                                 CefV8Value::CreateInt(kAccessorValue),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(accessor->got_set_);
+    accessor->got_set_.reset();
+    EXPECT_TRUE(interceptor->got_set_byname_);
+    interceptor->got_set_byname_.reset();
+
+    EXPECT_TRUE(object->SetValue(kInterceptorName,
+                                 CefV8Value::CreateInt(kInterceptorValue),
+                                 V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_FALSE(object->HasException());
+    EXPECT_TRUE(accessor->got_set_);
+    accessor->got_set_.reset();
+    EXPECT_TRUE(interceptor->got_set_byname_);
+    interceptor->got_set_byname_.reset();
+
+    // When interceptor returns nothing, accessor's getter is called.
+    CefRefPtr<CefV8Value> val1 = object->GetValue(kAccessorName);
+    EXPECT_TRUE(val1.get());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    interceptor->got_get_byname_.reset();
+    EXPECT_TRUE(accessor->got_get_);
+    accessor->got_get_.reset();
+    EXPECT_EQ(kAccessorValue, val1->GetIntValue());
+
+    // When interceptor returns value, accessor's getter is not called.
+    CefRefPtr<CefV8Value> val2 = object->GetValue(kInterceptorName);
+    EXPECT_TRUE(val2.get());
+    EXPECT_TRUE(interceptor->got_get_byname_);
+    EXPECT_FALSE(accessor->got_get_);
+    EXPECT_EQ(kInterceptorValue, val2->GetIntValue());
+
+    EXPECT_FALSE(interceptor->got_get_byindex_);
+    EXPECT_FALSE(interceptor->got_set_byindex_);
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectValueTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "test_arg";
+    static const int kVal1 = 13;
+    static const int kVal2 = 65;
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    object->SetValue(kName, CefV8Value::CreateInt(kVal1),
+                     V8_PROPERTY_ATTRIBUTE_NONE);
+
+    std::stringstream test;
+    test << "if (window." << kName << " != " << kVal1 << ") throw 'Fail';\n"
+         << "window." << kName << " = " << kVal2 << ";";
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    CefRefPtr<CefV8Value> newval = object->GetValue(kName);
+    EXPECT_TRUE(newval.get());
+    EXPECT_TRUE(newval->IsInt());
+    EXPECT_EQ(kVal2, newval->GetIntValue());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectValueReadOnlyTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "test_arg";
+    static const int kVal1 = 13;
+    static const int kVal2 = 65;
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    object->SetValue(kName, CefV8Value::CreateInt(kVal1),
+                     V8_PROPERTY_ATTRIBUTE_READONLY);
+
+    std::stringstream test;
+    test << "if (window." << kName << " != " << kVal1 << ") throw 'Fail';\n"
+         << "window." << kName << " = " << kVal2 << ";";
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    CefRefPtr<CefV8Value> newval = object->GetValue(kName);
+    EXPECT_TRUE(newval.get());
+    EXPECT_TRUE(newval->IsInt());
+    EXPECT_EQ(kVal1, newval->GetIntValue());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectValueEnumTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kObjName = "test_obj";
+    static const char* kArgName = "test_arg";
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    CefRefPtr<CefV8Value> obj1 = CefV8Value::CreateObject(nullptr, nullptr);
+    object->SetValue(kObjName, obj1, V8_PROPERTY_ATTRIBUTE_NONE);
+
+    obj1->SetValue(kArgName, CefV8Value::CreateInt(0),
+                   V8_PROPERTY_ATTRIBUTE_NONE);
+
+    std::stringstream test;
+    test << "for (var i in window." << kObjName
+         << ") {\n"
+            "window."
+         << kObjName
+         << "[i]++;\n"
+            "}";
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    CefRefPtr<CefV8Value> newval = obj1->GetValue(kArgName);
+    EXPECT_TRUE(newval.get());
+    EXPECT_TRUE(newval->IsInt());
+    EXPECT_EQ(1, newval->GetIntValue());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectValueDontEnumTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kObjName = "test_obj";
+    static const char* kArgName = "test_arg";
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    CefRefPtr<CefV8Value> obj1 = CefV8Value::CreateObject(nullptr, nullptr);
+    object->SetValue(kObjName, obj1, V8_PROPERTY_ATTRIBUTE_NONE);
+
+    obj1->SetValue(kArgName, CefV8Value::CreateInt(0),
+                   V8_PROPERTY_ATTRIBUTE_DONTENUM);
+
+    std::stringstream test;
+    test << "for (var i in window." << kObjName
+         << ") {\n"
+            "window."
+         << kObjName
+         << "[i]++;\n"
+            "}";
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    CefRefPtr<CefV8Value> newval = obj1->GetValue(kArgName);
+    EXPECT_TRUE(newval.get());
+    EXPECT_TRUE(newval->IsInt());
+    EXPECT_EQ(0, newval->GetIntValue());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectValueDeleteTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "test_arg";
+    static const int kVal1 = 13;
+    static const int kVal2 = 65;
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    object->SetValue(kName, CefV8Value::CreateInt(kVal1),
+                     V8_PROPERTY_ATTRIBUTE_NONE);
+
+    std::stringstream test;
+    test << "if (window." << kName << " != " << kVal1 << ") throw 'Fail';\n"
+         << "window." << kName << " = " << kVal2
+         << ";\n"
+            "delete window."
+         << kName << ";";
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    CefRefPtr<CefV8Value> newval = object->GetValue(kName);
+    EXPECT_TRUE(newval.get());
+    EXPECT_TRUE(newval->IsUndefined());
+    EXPECT_FALSE(newval->IsInt());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectValueDontDeleteTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "test_arg";
+    static const int kVal1 = 13;
+    static const int kVal2 = 65;
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    object->SetValue(kName, CefV8Value::CreateInt(kVal1),
+                     V8_PROPERTY_ATTRIBUTE_DONTDELETE);
+
+    std::stringstream test;
+    test << "if (window." << kName << " != " << kVal1 << ") throw 'Fail';\n"
+         << "window." << kName << " = " << kVal2
+         << ";\n"
+            "delete window."
+         << kName << ";";
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    CefRefPtr<CefV8Value> newval = object->GetValue(kName);
+    EXPECT_TRUE(newval.get());
+    EXPECT_TRUE(newval->IsInt());
+    EXPECT_EQ(kVal2, newval->GetIntValue());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunObjectValueEmptyKeyTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kName = "";
+    static const int kVal = 13;
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    EXPECT_FALSE(object->HasValue(kName));
+
+    object->SetValue(kName, CefV8Value::CreateInt(kVal),
+                     V8_PROPERTY_ATTRIBUTE_NONE);
+    EXPECT_TRUE(object->HasValue(kName));
+
+    CefRefPtr<CefV8Value> newval = object->GetValue(kName);
+    EXPECT_TRUE(newval.get());
+    EXPECT_TRUE(newval->IsInt());
+    EXPECT_EQ(kVal, newval->GetIntValue());
+
+    EXPECT_TRUE(object->DeleteValue(kName));
+    EXPECT_FALSE(object->HasValue(kName));
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunFunctionCreateTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        return false;
+      }
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> value = CefV8Value::CreateFunction("f", new Handler);
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsFunction());
+    EXPECT_TRUE(value->IsObject());
+
+    EXPECT_FALSE(value->IsUndefined());
+    EXPECT_FALSE(value->IsArray());
+    EXPECT_FALSE(value->IsBool());
+    EXPECT_FALSE(value->IsDate());
+    EXPECT_FALSE(value->IsDouble());
+    EXPECT_FALSE(value->IsInt());
+    EXPECT_FALSE(value->IsUInt());
+    EXPECT_FALSE(value->IsNull());
+    EXPECT_FALSE(value->IsString());
+
+    DestroyTest();
+  }
+
+  void RunFunctionHandlerTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kFuncName = "myfunc";
+    static const int kVal1 = 32;
+    static const int kVal2 = 41;
+    static const int kRetVal = 8;
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        EXPECT_STREQ(kFuncName, name.ToString().c_str());
+        EXPECT_TRUE(object->IsSame(object_));
+
+        EXPECT_EQ((size_t)2, arguments.size());
+        EXPECT_TRUE(arguments[0]->IsInt());
+        EXPECT_EQ(kVal1, arguments[0]->GetIntValue());
+        EXPECT_TRUE(arguments[1]->IsInt());
+        EXPECT_EQ(kVal2, arguments[1]->GetIntValue());
+
+        EXPECT_TRUE(exception.empty());
+
+        retval = CefV8Value::CreateInt(kRetVal);
+        EXPECT_TRUE(retval.get());
+        EXPECT_EQ(kRetVal, retval->GetIntValue());
+
+        got_execute_.yes();
+        return true;
+      }
+
+      CefRefPtr<CefV8Value> object_;
+      TrackCallback got_execute_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Handler* handler = new Handler;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(kFuncName, handler);
+    EXPECT_TRUE(func.get());
+
+    CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(nullptr, nullptr);
+    EXPECT_TRUE(obj.get());
+    handler->object_ = obj;
+
+    CefV8ValueList args;
+    args.push_back(CefV8Value::CreateInt(kVal1));
+    args.push_back(CefV8Value::CreateInt(kVal2));
+
+    CefRefPtr<CefV8Value> retval = func->ExecuteFunction(obj, args);
+    EXPECT_TRUE(handler->got_execute_);
+    EXPECT_TRUE(retval.get());
+    EXPECT_FALSE(func->HasException());
+    EXPECT_TRUE(retval->IsInt());
+    EXPECT_EQ(kRetVal, retval->GetIntValue());
+
+    handler->object_ = nullptr;
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunFunctionHandlerExceptionTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kException = "My error";
+    static const char* kExceptionMsg = "Uncaught Error: My error";
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        exception = kException;
+        got_execute_.yes();
+        return true;
+      }
+
+      TrackCallback got_execute_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Handler* handler = new Handler;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
+    EXPECT_TRUE(func.get());
+
+    CefV8ValueList args;
+
+    CefRefPtr<CefV8Value> retval = func->ExecuteFunction(nullptr, args);
+    EXPECT_TRUE(handler->got_execute_);
+    EXPECT_FALSE(retval.get());
+    EXPECT_TRUE(func->HasException());
+    CefRefPtr<CefV8Exception> exception = func->GetException();
+    EXPECT_TRUE(exception.get());
+    EXPECT_STREQ(kExceptionMsg, exception->GetMessage().ToString().c_str());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunFunctionHandlerFailTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        got_execute_.yes();
+        return false;
+      }
+
+      TrackCallback got_execute_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Handler* handler = new Handler;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
+    EXPECT_TRUE(func.get());
+
+    CefV8ValueList args;
+
+    CefRefPtr<CefV8Value> retval = func->ExecuteFunction(nullptr, args);
+    EXPECT_TRUE(handler->got_execute_);
+    EXPECT_TRUE(retval.get());
+    EXPECT_FALSE(func->HasException());
+    EXPECT_TRUE(retval->IsUndefined());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunFunctionHandlerNoObjectTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        EXPECT_TRUE(object.get());
+        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+        EXPECT_TRUE(context.get());
+        CefRefPtr<CefV8Value> global = context->GetGlobal();
+        EXPECT_TRUE(global.get());
+        EXPECT_TRUE(global->IsSame(object));
+
+        got_execute_.yes();
+        return true;
+      }
+
+      TrackCallback got_execute_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Handler* handler = new Handler;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
+    EXPECT_TRUE(func.get());
+
+    CefV8ValueList args;
+
+    CefRefPtr<CefV8Value> retval = func->ExecuteFunction(nullptr, args);
+    EXPECT_TRUE(handler->got_execute_);
+    EXPECT_TRUE(retval.get());
+    EXPECT_FALSE(func->HasException());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunFunctionHandlerWithContextTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+        EXPECT_TRUE(context.get());
+        EXPECT_TRUE(context->IsSame(context_));
+        got_execute_.yes();
+        return true;
+      }
+
+      CefRefPtr<CefV8Context> context_;
+      TrackCallback got_execute_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Handler* handler = new Handler;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+    handler->context_ = context;
+
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
+    EXPECT_TRUE(func.get());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    CefV8ValueList args;
+
+    CefRefPtr<CefV8Value> retval =
+        func->ExecuteFunctionWithContext(context, nullptr, args);
+    EXPECT_TRUE(handler->got_execute_);
+    EXPECT_TRUE(retval.get());
+    EXPECT_FALSE(func->HasException());
+
+    handler->context_ = nullptr;
+
+    DestroyTest();
+  }
+
+  void RunFunctionHandlerEmptyStringTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        EXPECT_TRUE(object.get());
+        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+        EXPECT_TRUE(context.get());
+        CefRefPtr<CefV8Value> global = context->GetGlobal();
+        EXPECT_TRUE(global.get());
+        EXPECT_TRUE(global->IsSame(object));
+
+        EXPECT_EQ((size_t)1, arguments.size());
+        EXPECT_TRUE(arguments[0]->IsString());
+        EXPECT_STREQ("", arguments[0]->GetStringValue().ToString().c_str());
+
+        retval = CefV8Value::CreateString(CefString());
+
+        got_execute_.yes();
+        return true;
+      }
+
+      TrackCallback got_execute_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Handler* handler = new Handler;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
+    EXPECT_TRUE(func.get());
+
+    CefV8ValueList args;
+    args.push_back(CefV8Value::CreateString(CefString()));
+
+    CefRefPtr<CefV8Value> retval = func->ExecuteFunction(nullptr, args);
+    EXPECT_TRUE(handler->got_execute_);
+    EXPECT_TRUE(retval.get());
+    EXPECT_FALSE(func->HasException());
+
+    EXPECT_TRUE(retval->IsString());
+    EXPECT_STREQ("", retval->GetStringValue().ToString().c_str());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunContextEvalTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(context->Eval("1+2", CefString(), 0, retval, exception));
+    EXPECT_TRUE(retval.get());
+    EXPECT_TRUE(retval->IsInt());
+    EXPECT_EQ(3, retval->GetIntValue());
+    EXPECT_FALSE(exception.get());
+
+    DestroyTest();
+  }
+
+  void RunContextEvalExceptionTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_FALSE(
+        context->Eval("\n\n\n1+foo", CefString(), 0, retval, exception));
+    EXPECT_FALSE(retval.get());
+    EXPECT_TRUE(exception.get());
+    EXPECT_EQ(4, exception->GetLineNumber());
+
+    DestroyTest();
+  }
+
+  void RunContextEvalCspBypassUnsafeEval() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    bool success =
+        context->Eval("(document.getElementById('result').innerHTML)",
+                      CefString(), 0, retval, exception);
+    if (exception.get()) {
+      ADD_FAILURE() << exception->GetMessage().c_str();
+      EXPECT_FALSE(success);
+    }
+
+    EXPECT_TRUE(success);
+    EXPECT_TRUE(retval.get());
+    if (retval.get()) {
+      EXPECT_TRUE(retval->IsString());
+      EXPECT_EQ(CefString("CSP_BYPASSED"), retval->GetStringValue());
+    }
+
+    DestroyTest();
+  }
+
+  void RunContextEvalCspBypassSandbox() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    bool success =
+        context->Eval("(document.getElementById('result').innerHTML)",
+                      CefString(), 0, retval, exception);
+    if (exception.get()) {
+      ADD_FAILURE() << exception->GetMessage().c_str();
+      EXPECT_FALSE(success);
+    }
+
+    EXPECT_TRUE(success);
+    EXPECT_TRUE(retval.get());
+    if (retval.get()) {
+      EXPECT_TRUE(retval->IsString());
+      EXPECT_EQ(CefString("CSP_BYPASSED"), retval->GetStringValue());
+    }
+
+    DestroyTest();
+  }
+
+  void RunContextEnteredTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    // Test value defined in OnContextCreated
+    EXPECT_TRUE(context->Eval(
+        "document.getElementById('f').contentWindow.v8_context_entered_test()",
+        CefString(), 0, retval, exception));
+    if (exception.get())
+      ADD_FAILURE() << exception->GetMessage().c_str();
+
+    EXPECT_TRUE(retval.get());
+    EXPECT_TRUE(retval->IsInt());
+    EXPECT_EQ(21, retval->GetIntValue());
+
+    DestroyTest();
+  }
+
+  void RunBindingTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    CefRefPtr<CefV8Value> object = context->GetGlobal();
+    EXPECT_TRUE(object.get());
+
+    // Test value defined in OnContextCreated
+    CefRefPtr<CefV8Value> value = object->GetValue("v8_binding_test");
+    EXPECT_TRUE(value.get());
+    EXPECT_TRUE(value->IsInt());
+    EXPECT_EQ(12, value->GetIntValue());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunStackTraceTest() {
+    CefRefPtr<CefV8Context> context = GetContext();
+
+    static const char* kFuncName = "myfunc";
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        EXPECT_STREQ(kFuncName, name.ToString().c_str());
+
+        stack_trace_ = CefV8StackTrace::GetCurrent(10);
+
+        retval = CefV8Value::CreateInt(3);
+        got_execute_.yes();
+        return true;
+      }
+
+      TrackCallback got_execute_;
+      CefRefPtr<CefV8StackTrace> stack_trace_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    // Enter the V8 context.
+    EXPECT_TRUE(context->Enter());
+
+    Handler* handler = new Handler;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(kFuncName, handler);
+    EXPECT_TRUE(func.get());
+    CefRefPtr<CefV8Value> obj = context->GetGlobal();
+    EXPECT_TRUE(obj.get());
+    obj->SetValue(kFuncName, func, V8_PROPERTY_ATTRIBUTE_NONE);
+
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+
+    EXPECT_TRUE(
+        context->Eval("function jsfunc() { return window.myfunc(); }\n"
+                      "jsfunc();",
+                      CefString(), 0, retval, exception));
+    EXPECT_TRUE(retval.get());
+    EXPECT_TRUE(retval->IsInt());
+    EXPECT_EQ(3, retval->GetIntValue());
+    EXPECT_FALSE(exception.get());
+
+    EXPECT_TRUE(handler->stack_trace_.get());
+    EXPECT_EQ(2, handler->stack_trace_->GetFrameCount());
+
+    CefRefPtr<CefV8StackFrame> frame;
+
+    frame = handler->stack_trace_->GetFrame(0);
+    EXPECT_TRUE(frame->GetScriptName().empty());
+    EXPECT_TRUE(frame->GetScriptNameOrSourceURL().empty());
+    EXPECT_STREQ("jsfunc", frame->GetFunctionName().ToString().c_str());
+    EXPECT_EQ(1, frame->GetLineNumber());
+    EXPECT_EQ(35, frame->GetColumn());
+    EXPECT_TRUE(frame.get());
+    EXPECT_FALSE(frame->IsEval());
+    EXPECT_FALSE(frame->IsConstructor());
+
+    frame = handler->stack_trace_->GetFrame(1);
+    EXPECT_TRUE(frame->GetScriptName().empty());
+    EXPECT_TRUE(frame->GetScriptNameOrSourceURL().empty());
+    EXPECT_TRUE(frame->GetFunctionName().empty());
+    EXPECT_EQ(2, frame->GetLineNumber());
+    EXPECT_EQ(1, frame->GetColumn());
+    EXPECT_TRUE(frame.get());
+    EXPECT_FALSE(frame->IsEval());
+    EXPECT_FALSE(frame->IsConstructor());
+
+    // Exit the V8 context.
+    EXPECT_TRUE(context->Exit());
+
+    DestroyTest();
+  }
+
+  void RunOnUncaughtExceptionTest() {
+    test_context_ = browser_->GetMainFrame()->GetV8Context();
+    browser_->GetMainFrame()->ExecuteJavaScript(
+        "window.setTimeout(test, 0)", browser_->GetMainFrame()->GetURL(), 0);
+  }
+
+  // Test execution of a native function when the extension is loaded.
+  void RunExtensionTest() {
+    std::string code =
+        "native function v8_extension_test();"
+        "v8_extension_test();";
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler(TrackCallback* callback) : callback_(callback) {}
+
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        EXPECT_STREQ("v8_extension_test", name.ToString().c_str());
+        callback_->yes();
+        return true;
+      }
+
+      TrackCallback* callback_;
+
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    CefRegisterExtension("v8/test-extension", code,
+                         new Handler(&startup_test_success_));
+  }
+
+  void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) override {
+    if (extra_info->HasKey(kV8TestCmdKey)) {
+      test_mode_ = static_cast<V8TestMode>(extra_info->GetInt(kV8TestCmdKey));
+    }
+    if (test_mode_ > V8TEST_NONE)
+      RunStartupTest();
+    if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL ||
+        test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) {
+      browser_ = browser;
+    }
+  }
+
+  CefRefPtr<CefLoadHandler> GetLoadHandler(
+      CefRefPtr<ClientAppRenderer> app) override {
+    if (test_mode_ == V8TEST_NONE)
+      return nullptr;
+
+    return this;
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS &&
+        browser->IsPopup()) {
+      DevToolsLoadHook(browser);
+    }
+  }
+
+  void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
+                        CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefV8Context> context) override {
+    if (test_mode_ == V8TEST_NONE)
+      return;
+
+    if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
+      if (!browser->IsPopup()) {
+        app_ = app;
+        browser_ = browser;
+        test_context_ = context;
+      }
+      return;
+    }
+
+    app_ = app;
+    browser_ = browser;
+
+    std::string url = frame->GetURL();
+    if (url == kV8ContextChildTestUrl) {
+      // For V8TEST_CONTEXT_ENTERED
+      class Handler : public CefV8Handler {
+       public:
+        Handler() {}
+        bool Execute(const CefString& name,
+                     CefRefPtr<CefV8Value> object,
+                     const CefV8ValueList& arguments,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) override {
+          // context for the sub-frame
+          CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
+          EXPECT_TRUE(context.get());
+
+          // entered context should be the same as the main frame context
+          CefRefPtr<CefV8Context> entered = CefV8Context::GetEnteredContext();
+          EXPECT_TRUE(entered.get());
+          EXPECT_TRUE(entered->IsSame(context_));
+
+          context_ = nullptr;
+          retval = CefV8Value::CreateInt(21);
+          return true;
+        }
+
+        CefRefPtr<CefV8Context> context_;
+        IMPLEMENT_REFCOUNTING(Handler);
+      };
+
+      Handler* handler = new Handler;
+      CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+      // main frame context
+      handler->context_ = GetContext();
+
+      // Function that will be called from the parent frame context.
+      CefRefPtr<CefV8Value> func =
+          CefV8Value::CreateFunction("v8_context_entered_test", handler);
+      EXPECT_TRUE(func.get());
+
+      CefRefPtr<CefV8Value> object = context->GetGlobal();
+      EXPECT_TRUE(object.get());
+      EXPECT_TRUE(object->SetValue("v8_context_entered_test", func,
+                                   V8_PROPERTY_ATTRIBUTE_NONE));
+    } else if (url == kV8ContextParentTestUrl) {
+      // For V8TEST_CONTEXT_ENTERED. Do nothing.
+      return;
+    } else if (url == kV8BindingTestUrl) {
+      // For V8TEST_BINDING
+      CefRefPtr<CefV8Value> object = context->GetGlobal();
+      EXPECT_TRUE(object.get());
+      EXPECT_TRUE(object->SetValue("v8_binding_test", CefV8Value::CreateInt(12),
+                                   V8_PROPERTY_ATTRIBUTE_NONE));
+    } else if (url == kV8HandlerCallOnReleasedContextUrl) {
+      // For V8TEST_HANDLER_CALL_ON_RELEASED_CONTEXT
+      class Handler : public CefV8Handler {
+       public:
+        Handler(CefRefPtr<V8RendererTest> renderer_test)
+            : renderer_test_(renderer_test) {}
+
+        bool Execute(const CefString& name,
+                     CefRefPtr<CefV8Value> object,
+                     const CefV8ValueList& arguments,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) override {
+          if (name == "notify_test_done") {
+            CefPostDelayedTask(
+                TID_RENDERER,
+                base::Bind(&V8RendererTest::DestroyTest, renderer_test_.get()),
+                1000);
+            return true;
+          }
+
+          return false;
+        }
+
+       private:
+        CefRefPtr<V8RendererTest> renderer_test_;
+        IMPLEMENT_REFCOUNTING(Handler);
+      };
+
+      Handler* handler = new Handler(this);
+      CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+      // Function that will be called from the parent frame context.
+      CefRefPtr<CefV8Value> func =
+          CefV8Value::CreateFunction("notify_test_done", handler);
+      EXPECT_TRUE(func.get());
+
+      CefRefPtr<CefV8Value> object = context->GetGlobal();
+      EXPECT_TRUE(object.get());
+      EXPECT_TRUE(object->SetValue("notify_test_done", func,
+                                   V8_PROPERTY_ATTRIBUTE_NONE));
+    } else if (url == kV8HandlerCallOnReleasedContextChildUrl) {
+      // For V8TEST_HANDLER_CALL_ON_RELEASED_CONTEXT
+      class Handler : public CefV8Handler {
+       public:
+        Handler() {}
+        bool Execute(const CefString& name,
+                     CefRefPtr<CefV8Value> object,
+                     const CefV8ValueList& arguments,
+                     CefRefPtr<CefV8Value>& retval,
+                     CefString& exception) override {
+          if (name == "v8_context_is_alive") {
+            retval = CefV8Value::CreateBool(true);
+            return true;
+          }
+
+          return false;
+        }
+
+        IMPLEMENT_REFCOUNTING(Handler);
+      };
+
+      Handler* handler = new Handler;
+      CefRefPtr<CefV8Handler> handlerPtr(handler);
+
+      // Function that will be called from the parent frame context.
+      CefRefPtr<CefV8Value> func =
+          CefV8Value::CreateFunction("v8_context_is_alive", handler);
+      EXPECT_TRUE(func.get());
+
+      CefRefPtr<CefV8Value> object = context->GetGlobal();
+      EXPECT_TRUE(object.get());
+      EXPECT_TRUE(object->SetValue("v8_context_is_alive", func,
+                                   V8_PROPERTY_ATTRIBUTE_NONE));
+    }
+  }
+
+  void OnUncaughtException(CefRefPtr<ClientAppRenderer> app,
+                           CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefV8Context> context,
+                           CefRefPtr<CefV8Exception> exception,
+                           CefRefPtr<CefV8StackTrace> stackTrace) override {
+    if (test_mode_ == V8TEST_NONE)
+      return;
+
+    if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION ||
+        test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
+      EXPECT_TRUE(test_context_->IsSame(context));
+      EXPECT_STREQ("Uncaught ReferenceError: asd is not defined",
+                   exception->GetMessage().ToString().c_str());
+      std::ostringstream stackFormatted;
+      for (int i = 0; i < stackTrace->GetFrameCount(); ++i) {
+        stackFormatted << "at "
+                       << stackTrace->GetFrame(i)->GetFunctionName().ToString()
+                       << "() in "
+                       << stackTrace->GetFrame(i)->GetScriptName().ToString()
+                       << " on line "
+                       << stackTrace->GetFrame(i)->GetLineNumber() << "\n";
+      }
+      const char* stackFormattedShouldBe =
+          "at test2() in http://tests/V8Test.OnUncaughtException on line 3\n"
+          "at test() in http://tests/V8Test.OnUncaughtException on line 2\n";
+      EXPECT_STREQ(stackFormattedShouldBe, stackFormatted.str().c_str());
+      DestroyTest();
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
+                                CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    if (test_mode_ == V8TEST_NONE)
+      return false;
+
+    const std::string& message_name = message->GetName();
+    if (message_name == kV8RunTestMsg) {
+      // Run the test asynchronously.
+      CefPostTask(TID_RENDERER, base::Bind(&V8RendererTest::RunTest, this));
+      return true;
+    }
+    return false;
+  }
+
+  void DevToolsLoadHook(CefRefPtr<CefBrowser> browser) {
+    EXPECT_TRUE(browser->IsPopup());
+    CefRefPtr<CefFrame> frame = browser->GetMainFrame();
+    CefRefPtr<CefV8Context> context = frame->GetV8Context();
+    static const char* kFuncName = "DevToolsLoaded";
+
+    class Handler : public CefV8Handler {
+     public:
+      Handler() {}
+      bool Execute(const CefString& name,
+                   CefRefPtr<CefV8Value> object,
+                   const CefV8ValueList& arguments,
+                   CefRefPtr<CefV8Value>& retval,
+                   CefString& exception) override {
+        EXPECT_STREQ(kFuncName, name.ToString().c_str());
+        if (name == kFuncName) {
+          EXPECT_TRUE(exception.empty());
+          retval = CefV8Value::CreateNull();
+          EXPECT_TRUE(retval.get());
+          renderer_test_->DevToolsLoaded(browser_);
+          return true;
+        }
+        return false;
+      }
+      CefRefPtr<V8RendererTest> renderer_test_;
+      CefRefPtr<CefBrowser> browser_;
+      IMPLEMENT_REFCOUNTING(Handler);
+    };
+
+    EXPECT_TRUE(context->Enter());
+    Handler* handler = new Handler;
+    handler->renderer_test_ = this;
+    handler->browser_ = browser;
+    CefRefPtr<CefV8Handler> handlerPtr(handler);
+    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(kFuncName, handler);
+    EXPECT_TRUE(func.get());
+    EXPECT_TRUE(context->GetGlobal()->SetValue(kFuncName, func,
+                                               V8_PROPERTY_ATTRIBUTE_NONE));
+    EXPECT_TRUE(context->Exit());
+
+    // Dismiss the DevTools window after 500ms. It would be better to hook the
+    // DevTools JS to receive notification of when loading is complete but that
+    // is no longer possible.
+    CefPostDelayedTask(
+        TID_RENDERER,
+        base::Bind(&CefFrame::ExecuteJavaScript, frame.get(),
+                   "window.DevToolsLoaded()", frame->GetURL(), 0),
+        500);
+  }
+
+  void DevToolsLoaded(CefRefPtr<CefBrowser> browser) {
+    EXPECT_TRUE(browser->IsPopup());
+    // |browser_| will be nullptr if the DevTools window is opened in a separate
+    // render process.
+    const int other_browser_id =
+        (browser_.get() ? browser_->GetIdentifier() : -1);
+    EXPECT_NE(browser->GetIdentifier(), other_browser_id);
+
+    // Close the DevTools window. This will trigger OnBeforeClose in the browser
+    // process.
+    CefRefPtr<CefV8Value> retval;
+    CefRefPtr<CefV8Exception> exception;
+    EXPECT_TRUE(browser->GetMainFrame()->GetV8Context()->Eval(
+        "window.close()", CefString(), 0, retval, exception));
+  }
+
+ protected:
+  // Return from the test.
+  void DestroyTest() {
+    EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
+
+    // Check if the test has failed.
+    bool result = !TestFailed();
+
+    // Return the result to the browser process.
+    CefRefPtr<CefProcessMessage> return_msg =
+        CefProcessMessage::Create(kV8TestMsg);
+    EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
+    browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
+
+    app_ = nullptr;
+    browser_ = nullptr;
+    test_context_ = nullptr;
+    test_object_ = nullptr;
+  }
+
+  // Return the V8 context.
+  CefRefPtr<CefV8Context> GetContext() {
+    CefRefPtr<CefV8Context> context = browser_->GetMainFrame()->GetV8Context();
+    EXPECT_TRUE(context.get());
+    return context;
+  }
+
+  CefRefPtr<ClientAppRenderer> app_;
+  CefRefPtr<CefBrowser> browser_;
+  V8TestMode test_mode_;
+
+  CefRefPtr<CefV8Context> test_context_;
+  CefRefPtr<CefV8Value> test_object_;
+
+  // Used by startup tests to indicate success.
+  TrackCallback startup_test_success_;
+
+  IMPLEMENT_REFCOUNTING(V8RendererTest);
+};
+
+// Browser side.
+class V8TestHandler : public TestHandler {
+ public:
+  V8TestHandler(V8TestMode test_mode, const char* test_url)
+      : test_mode_(test_mode), test_url_(test_url) {}
+
+  void RunTest() override {
+    CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
+    extra_info->SetInt(kV8TestCmdKey, test_mode_);
+
+    // Nested script tag forces creation of the V8 context.
+    if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL ||
+        test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) {
+      std::string url;
+      ResourceContent::HeaderMap headers;
+      if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL) {
+        url = kV8ContextEvalCspBypassUnsafeEval;
+        headers.insert(std::pair<std::string, std::string>(
+            "Content-Security-Policy", "script-src 'self'"));
+      } else if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) {
+        url = kV8ContextEvalCspBypassSandbox;
+        headers.insert(std::pair<std::string, std::string>(
+            "Content-Security-Policy", "sandbox"));
+      } else {
+        NOTREACHED();
+      }
+      AddResource(url,
+                  "<html><body>" + url +
+                      "<p id='result' style='display:none'>CSP_BYPASSED</p>"
+                      "</body></html>",
+                  "text/html", headers);
+      CreateBrowser(test_url_, nullptr, extra_info);
+    } else if (test_mode_ == V8TEST_CONTEXT_ENTERED) {
+      AddResource(kV8ContextParentTestUrl,
+                  "<html><body>"
+                  "<script>var i = 0;</script><iframe src=\"" +
+                      std::string(kV8ContextChildTestUrl) +
+                      "\" id=\"f\"></iframe></body>"
+                      "</html>",
+                  "text/html");
+      AddResource(kV8ContextChildTestUrl,
+                  "<html><body>"
+                  "<script>var i = 0;</script>CHILD</body></html>",
+                  "text/html");
+      CreateBrowser(kV8ContextParentTestUrl, nullptr, extra_info);
+    } else if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION ||
+               test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
+      AddResource(kV8OnUncaughtExceptionTestUrl,
+                  "<html><body>"
+                  "<h1>OnUncaughtException</h1>"
+                  "<script>\n"
+                  "function test(){ test2(); }\n"
+                  "function test2(){ asd(); }\n"
+                  "</script>\n"
+                  "</body></html>\n",
+                  "text/html");
+      CreateBrowser(kV8OnUncaughtExceptionTestUrl, nullptr, extra_info);
+    } else if (test_mode_ == V8TEST_HANDLER_CALL_ON_RELEASED_CONTEXT) {
+      AddResource(kV8HandlerCallOnReleasedContextUrl,
+                  "<html><body onload='createFrame()'>"
+                  "(main)"
+                  "<script>"
+                  "function createFrame() {"
+                  "  var el = document.createElement('iframe');"
+                  "  el.id = 'child';"
+                  "  el.src = '" +
+                      std::string(kV8HandlerCallOnReleasedContextChildUrl) +
+                      "';"
+                      "  el.onload = function() {"
+                      "    setTimeout(function() {"
+                      "      try {"
+                      "        el.contentWindow.removeMe();"
+                      "        window.notify_test_done();"
+                      "      } catch (e) { alert('Unit test error.\\n' + e); }"
+                      "    }, 1000);"
+                      "  };"
+                      "  document.body.appendChild(el);"
+                      "}"
+                      ""
+                      "function removeFrame(id) {"
+                      "  var el = document.getElementById(id);"
+                      "  if (el) { el.parentElement.removeChild(el); }"
+                      "  else { alert('Error in test. No element \"' + id + "
+                      "'\" found.'); }"
+                      "}"
+                      "</script>"
+                      "</body></html>",
+                  "text/html");
+      AddResource(kV8HandlerCallOnReleasedContextChildUrl,
+                  "<html><body>"
+                  "(child)"
+                  "<script>"
+                  "try {"
+                  "  if (!window.v8_context_is_alive()) {"
+                  "    throw 'v8_context_is_alive returns non-true value.';"
+                  "  }"
+                  "} catch (e) {"
+                  "  alert('Unit test error.\\n' + e);"
+                  "}"
+                  ""
+                  "function removeMe() {"
+                  "  var w = window;"
+                  "  w.parent.removeFrame('child');"
+                  "  return w.v8_context_is_alive();"
+                  "}"
+                  "</script>"
+                  "</body></html>",
+                  "text/html");
+      CreateBrowser(kV8HandlerCallOnReleasedContextUrl, nullptr, extra_info);
+    } else {
+      EXPECT_TRUE(test_url_ != nullptr);
+      AddResource(test_url_,
+                  "<html><body>"
+                  "<script>var i = 0;</script>TEST</body></html>",
+                  "text/html");
+      CreateBrowser(test_url_, nullptr, extra_info);
+    }
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout();
+  }
+
+  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
+    if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS &&
+        browser->IsPopup()) {
+      // Generate the uncaught exception in the main browser. Use a 200ms delay
+      // because there's a bit of a lag between destroying the DevToolsAgent and
+      // re-registering for uncaught exceptions.
+      GetBrowser()->GetMainFrame()->ExecuteJavaScript(
+          "window.setTimeout(test, 200);",
+          GetBrowser()->GetMainFrame()->GetURL(), 0);
+    }
+
+    TestHandler::OnBeforeClose(browser);
+  }
+
+  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int httpStatusCode) override {
+    if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
+      if (!browser->IsPopup()) {
+        // Create the DevTools window.
+        CefWindowInfo windowInfo;
+        CefBrowserSettings settings;
+
+#if defined(OS_WIN)
+        windowInfo.SetAsPopup(browser->GetHost()->GetWindowHandle(),
+                              "DevTools");
+#endif
+
+        browser->GetHost()->ShowDevTools(windowInfo, this, settings,
+                                         CefPoint());
+      }
+      return;
+    }
+
+    const std::string& url = frame->GetURL();
+    if (url != kV8NavTestUrl && url != kV8ContextParentTestUrl &&
+        url.find("http://tests/") != std::string::npos) {
+      // Run the test.
+      CefRefPtr<CefProcessMessage> return_msg =
+          CefProcessMessage::Create(kV8RunTestMsg);
+      frame->SendProcessMessage(PID_RENDERER, return_msg);
+    }
+  }
+
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) override {
+    EXPECT_TRUE(browser.get());
+    EXPECT_TRUE(frame.get());
+    EXPECT_TRUE(frame->IsMain());
+    EXPECT_EQ(PID_RENDERER, source_process);
+    EXPECT_TRUE(message.get());
+    EXPECT_TRUE(message->IsReadOnly());
+
+    const std::string& message_name = message->GetName();
+    EXPECT_STREQ(kV8TestMsg, message_name.c_str());
+
+    got_message_.yes();
+
+    if (message->GetArgumentList()->GetBool(0))
+      got_success_.yes();
+
+    // Test is complete.
+    DestroyTest();
+
+    return true;
+  }
+
+  V8TestMode test_mode_;
+  const char* test_url_;
+  TrackCallback got_message_;
+  TrackCallback got_success_;
+
+  IMPLEMENT_REFCOUNTING(V8TestHandler);
+};
+
+}  // namespace
+
+// Entry point for creating V8 renderer test objects.
+// Called from client_app_delegates.cc.
+void CreateV8RendererTests(ClientAppRenderer::DelegateSet& delegates) {
+  delegates.insert(new V8RendererTest);
+}
+
+// Helpers for defining V8 tests.
+#define V8_TEST_EX(name, test_mode, test_url)                                  \
+  TEST(V8Test, name) {                                                         \
+    CefRefPtr<V8TestHandler> handler = new V8TestHandler(test_mode, test_url); \
+    handler->ExecuteTest();                                                    \
+    EXPECT_TRUE(handler->got_message_);                                        \
+    EXPECT_TRUE(handler->got_success_);                                        \
+    ReleaseAndWaitForDestructor(handler);                                      \
+  }
+
+#define V8_TEST(name, test_mode) V8_TEST_EX(name, test_mode, kV8TestUrl)
+
+// Define the tests.
+V8_TEST(NullCreate, V8TEST_NULL_CREATE)
+V8_TEST(BoolCreate, V8TEST_BOOL_CREATE)
+V8_TEST(IntCreate, V8TEST_INT_CREATE)
+V8_TEST(UIntCreate, V8TEST_UINT_CREATE)
+V8_TEST(DoubleCreate, V8TEST_DOUBLE_CREATE)
+V8_TEST(DateCreate, V8TEST_DATE_CREATE)
+V8_TEST(StringCreate, V8TEST_STRING_CREATE)
+V8_TEST(EmptyStringCreate, V8TEST_EMPTY_STRING_CREATE)
+V8_TEST(ArrayCreate, V8TEST_ARRAY_CREATE)
+V8_TEST(ArrayValue, V8TEST_ARRAY_VALUE)
+V8_TEST(ArrayBuffer, V8TEST_ARRAY_BUFFER)
+V8_TEST(ArrayBufferValue, V8TEST_ARRAY_BUFFER_VALUE)
+V8_TEST(ObjectCreate, V8TEST_OBJECT_CREATE)
+V8_TEST(ObjectUserData, V8TEST_OBJECT_USERDATA)
+V8_TEST(ObjectAccessor, V8TEST_OBJECT_ACCESSOR)
+V8_TEST(ObjectAccessorException, V8TEST_OBJECT_ACCESSOR_EXCEPTION)
+V8_TEST(ObjectAccessorFail, V8TEST_OBJECT_ACCESSOR_FAIL)
+V8_TEST(ObjectAccessorReadOnly, V8TEST_OBJECT_ACCESSOR_READONLY)
+V8_TEST(ObjectInterceptor, V8TEST_OBJECT_INTERCEPTOR)
+V8_TEST(ObjectInterceptorFail, V8TEST_OBJECT_INTERCEPTOR_FAIL)
+V8_TEST(ObjectInterceptorException, V8TEST_OBJECT_INTERCEPTOR_EXCEPTION)
+V8_TEST(ObjectInterceptorAndAccessor, V8TEST_OBJECT_INTERCEPTOR_AND_ACCESSOR)
+V8_TEST(ObjectValue, V8TEST_OBJECT_VALUE)
+V8_TEST(ObjectValueReadOnly, V8TEST_OBJECT_VALUE_READONLY)
+V8_TEST(ObjectValueEnum, V8TEST_OBJECT_VALUE_ENUM)
+V8_TEST(ObjectValueDontEnum, V8TEST_OBJECT_VALUE_DONTENUM)
+V8_TEST(ObjectValueDelete, V8TEST_OBJECT_VALUE_DELETE)
+V8_TEST(ObjectValueDontDelete, V8TEST_OBJECT_VALUE_DONTDELETE)
+V8_TEST(ObjectValueEmptyKey, V8TEST_OBJECT_VALUE_EMPTYKEY)
+V8_TEST(FunctionCreate, V8TEST_FUNCTION_CREATE)
+V8_TEST(FunctionHandler, V8TEST_FUNCTION_HANDLER)
+V8_TEST(FunctionHandlerException, V8TEST_FUNCTION_HANDLER_EXCEPTION)
+V8_TEST(FunctionHandlerFail, V8TEST_FUNCTION_HANDLER_FAIL)
+V8_TEST(FunctionHandlerNoObject, V8TEST_FUNCTION_HANDLER_NO_OBJECT)
+V8_TEST(FunctionHandlerWithContext, V8TEST_FUNCTION_HANDLER_WITH_CONTEXT)
+V8_TEST(FunctionHandlerEmptyString, V8TEST_FUNCTION_HANDLER_EMPTY_STRING)
+V8_TEST(ContextEval, V8TEST_CONTEXT_EVAL)
+V8_TEST(ContextEvalException, V8TEST_CONTEXT_EVAL_EXCEPTION)
+V8_TEST_EX(ContextEvalCspBypassUnsafeEval,
+           V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL,
+           kV8ContextEvalCspBypassUnsafeEval)
+V8_TEST_EX(ContextEvalCspBypassSandbox,
+           V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX,
+           kV8ContextEvalCspBypassSandbox)
+V8_TEST_EX(ContextEntered, V8TEST_CONTEXT_ENTERED, nullptr)
+V8_TEST_EX(Binding, V8TEST_BINDING, kV8BindingTestUrl)
+V8_TEST(StackTrace, V8TEST_STACK_TRACE)
+V8_TEST(OnUncaughtException, V8TEST_ON_UNCAUGHT_EXCEPTION)
+V8_TEST(OnUncaughtExceptionDevTools, V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS)
+V8_TEST(Extension, V8TEST_EXTENSION)
+V8_TEST_EX(HandlerCallOnReleasedContext,
+           V8TEST_HANDLER_CALL_ON_RELEASED_CONTEXT,
+           kV8HandlerCallOnReleasedContextUrl)
diff --git a/src/tests/ceftests/values_unittest.cc b/src/tests/ceftests/values_unittest.cc
new file mode 100644
index 0000000..fa91ad9
--- /dev/null
+++ b/src/tests/ceftests/values_unittest.cc
@@ -0,0 +1,1379 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_task.h"
+#include "include/cef_values.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/test_util.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Dictionary test keys.
+const char* kNullKey = "null_key";
+const char* kBoolKey = "bool_key";
+const char* kIntKey = "int_key";
+const char* kDoubleKey = "double_key";
+const char* kStringKey = "string_key";
+const char* kBinaryKey = "binary_key";
+const char* kDictionaryKey = "dict_key";
+const char* kListKey = "list_key";
+
+// List test indexes.
+enum {
+  kNullIndex = 0,
+  kBoolIndex,
+  kIntIndex,
+  kDoubleIndex,
+  kStringIndex,
+  kBinaryIndex,
+  kDictionaryIndex,
+  kListIndex,
+};
+
+// Dictionary/list test values.
+const bool kBoolValue = true;
+const int kIntValue = 12;
+const double kDoubleValue = 4.5432;
+const char* kStringValue = "My string value";
+
+// BINARY TEST HELPERS
+
+// Test a binary value.
+void TestBinary(CefRefPtr<CefBinaryValue> value, char* data, size_t data_size) {
+  // Testing requires strings longer than 15 characters.
+  EXPECT_GT(data_size, 15U);
+
+  EXPECT_EQ(data_size, value->GetSize());
+
+  char* buff = new char[data_size + 1];
+  char old_char;
+
+  // Test full read.
+  memset(buff, 0, data_size + 1);
+  EXPECT_EQ(data_size, value->GetData(buff, data_size, 0));
+  EXPECT_TRUE(!strcmp(buff, data));
+
+  // Test partial read with offset.
+  memset(buff, 0, data_size + 1);
+  old_char = data[15];
+  data[15] = 0;
+  EXPECT_EQ(10U, value->GetData(buff, 10, 5));
+  EXPECT_TRUE(!strcmp(buff, data + 5));
+  data[15] = old_char;
+
+  // Test that changes to the original data have no effect.
+  memset(buff, 0, data_size + 1);
+  old_char = data[0];
+  data[0] = '.';
+  EXPECT_EQ(1U, value->GetData(buff, 1, 0));
+  EXPECT_EQ(old_char, buff[0]);
+  data[0] = old_char;
+
+  // Test copy.
+  CefRefPtr<CefBinaryValue> copy = value->Copy();
+  TestBinaryEqual(copy, value);
+
+  delete[] buff;
+}
+
+// Used to test access of binary data on a different thread.
+class BinaryTask : public CefTask {
+ public:
+  BinaryTask(CefRefPtr<CefBinaryValue> value, char* data, size_t data_size)
+      : value_(value), data_(data), data_size_(data_size) {}
+
+  void Execute() override { TestBinary(value_, data_, data_size_); }
+
+ private:
+  CefRefPtr<CefBinaryValue> value_;
+  char* data_;
+  size_t data_size_;
+
+  IMPLEMENT_REFCOUNTING(BinaryTask);
+};
+
+// DICTIONARY TEST HELPERS
+
+// Test dictionary null value.
+void TestDictionaryNull(CefRefPtr<CefDictionaryValue> value) {
+  EXPECT_FALSE(value->HasKey(kNullKey));
+  EXPECT_TRUE(value->SetNull(kNullKey));
+  EXPECT_TRUE(value->HasKey(kNullKey));
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kNullKey));
+}
+
+// Test dictionary bool value.
+void TestDictionaryBool(CefRefPtr<CefDictionaryValue> value) {
+  EXPECT_FALSE(value->HasKey(kBoolKey));
+  EXPECT_TRUE(value->SetBool(kBoolKey, kBoolValue));
+  EXPECT_TRUE(value->HasKey(kBoolKey));
+  EXPECT_EQ(VTYPE_BOOL, value->GetType(kBoolKey));
+  EXPECT_EQ(kBoolValue, value->GetBool(kBoolKey));
+}
+
+// Test dictionary int value.
+void TestDictionaryInt(CefRefPtr<CefDictionaryValue> value) {
+  EXPECT_FALSE(value->HasKey(kIntKey));
+  EXPECT_TRUE(value->SetInt(kIntKey, kIntValue));
+  EXPECT_TRUE(value->HasKey(kIntKey));
+  EXPECT_EQ(VTYPE_INT, value->GetType(kIntKey));
+  EXPECT_EQ(kIntValue, value->GetInt(kIntKey));
+}
+
+// Test dictionary double value.
+void TestDictionaryDouble(CefRefPtr<CefDictionaryValue> value) {
+  EXPECT_FALSE(value->HasKey(kDoubleKey));
+  EXPECT_TRUE(value->SetDouble(kDoubleKey, kDoubleValue));
+  EXPECT_TRUE(value->HasKey(kDoubleKey));
+  EXPECT_EQ(VTYPE_DOUBLE, value->GetType(kDoubleKey));
+  EXPECT_EQ(kDoubleValue, value->GetDouble(kDoubleKey));
+}
+
+// Test dictionary string value.
+void TestDictionaryString(CefRefPtr<CefDictionaryValue> value) {
+  EXPECT_FALSE(value->HasKey(kStringKey));
+  EXPECT_TRUE(value->SetString(kStringKey, kStringValue));
+  EXPECT_TRUE(value->HasKey(kStringKey));
+  EXPECT_EQ(VTYPE_STRING, value->GetType(kStringKey));
+  EXPECT_EQ(kStringValue, value->GetString(kStringKey).ToString());
+}
+
+// Test dictionary binary value.
+void TestDictionaryBinary(CefRefPtr<CefDictionaryValue> value,
+                          char* binary_data,
+                          size_t binary_data_size,
+                          CefRefPtr<CefBinaryValue>& binary_value) {
+  binary_value = CefBinaryValue::Create(binary_data, binary_data_size);
+  EXPECT_TRUE(binary_value.get());
+  EXPECT_TRUE(binary_value->IsValid());
+  EXPECT_FALSE(binary_value->IsOwned());
+  EXPECT_FALSE(value->HasKey(kBinaryKey));
+  EXPECT_TRUE(value->SetBinary(kBinaryKey, binary_value));
+  EXPECT_FALSE(binary_value->IsValid());  // Value should be detached
+  EXPECT_TRUE(value->HasKey(kBinaryKey));
+  EXPECT_EQ(VTYPE_BINARY, value->GetType(kBinaryKey));
+  binary_value = value->GetBinary(kBinaryKey);
+  EXPECT_TRUE(binary_value.get());
+  EXPECT_TRUE(binary_value->IsValid());
+  EXPECT_TRUE(binary_value->IsOwned());
+  TestBinary(binary_value, binary_data, binary_data_size);
+}
+
+// Test dictionary dictionary value.
+void TestDictionaryDictionary(CefRefPtr<CefDictionaryValue> value,
+                              CefRefPtr<CefDictionaryValue>& dictionary_value) {
+  dictionary_value = CefDictionaryValue::Create();
+  EXPECT_TRUE(dictionary_value.get());
+  EXPECT_TRUE(dictionary_value->IsValid());
+  EXPECT_FALSE(dictionary_value->IsOwned());
+  EXPECT_FALSE(dictionary_value->IsReadOnly());
+  EXPECT_TRUE(dictionary_value->SetInt(kIntKey, kIntValue));
+  EXPECT_EQ(1U, dictionary_value->GetSize());
+  EXPECT_FALSE(value->HasKey(kDictionaryKey));
+  EXPECT_TRUE(value->SetDictionary(kDictionaryKey, dictionary_value));
+  EXPECT_FALSE(dictionary_value->IsValid());  // Value should be detached
+  EXPECT_TRUE(value->HasKey(kDictionaryKey));
+  EXPECT_EQ(VTYPE_DICTIONARY, value->GetType(kDictionaryKey));
+  dictionary_value = value->GetDictionary(kDictionaryKey);
+  EXPECT_TRUE(dictionary_value.get());
+  EXPECT_TRUE(dictionary_value->IsValid());
+  EXPECT_TRUE(dictionary_value->IsOwned());
+  EXPECT_FALSE(dictionary_value->IsReadOnly());
+  EXPECT_EQ(1U, dictionary_value->GetSize());
+  EXPECT_EQ(kIntValue, dictionary_value->GetInt(kIntKey));
+}
+
+// Test dictionary list value.
+void TestDictionaryList(CefRefPtr<CefDictionaryValue> value,
+                        CefRefPtr<CefListValue>& list_value) {
+  list_value = CefListValue::Create();
+  EXPECT_TRUE(list_value.get());
+  EXPECT_TRUE(list_value->IsValid());
+  EXPECT_FALSE(list_value->IsOwned());
+  EXPECT_FALSE(list_value->IsReadOnly());
+  EXPECT_TRUE(list_value->SetInt(0, kIntValue));
+  EXPECT_EQ(1U, list_value->GetSize());
+  EXPECT_FALSE(value->HasKey(kListKey));
+  EXPECT_TRUE(value->SetList(kListKey, list_value));
+  EXPECT_FALSE(list_value->IsValid());  // Value should be detached
+  EXPECT_TRUE(value->HasKey(kListKey));
+  EXPECT_EQ(VTYPE_LIST, value->GetType(kListKey));
+  list_value = value->GetList(kListKey);
+  EXPECT_TRUE(list_value.get());
+  EXPECT_TRUE(list_value->IsValid());
+  EXPECT_TRUE(list_value->IsOwned());
+  EXPECT_FALSE(list_value->IsReadOnly());
+  EXPECT_EQ(1U, list_value->GetSize());
+  EXPECT_EQ(kIntValue, list_value->GetInt(0));
+}
+
+// Test dictionary value.
+void TestDictionary(CefRefPtr<CefDictionaryValue> value,
+                    char* binary_data,
+                    size_t binary_data_size) {
+  CefRefPtr<CefBinaryValue> binary_value;
+  CefRefPtr<CefDictionaryValue> dictionary_value;
+  CefRefPtr<CefListValue> list_value;
+
+  // Test the size.
+  EXPECT_EQ(0U, value->GetSize());
+
+  TestDictionaryNull(value);
+  TestDictionaryBool(value);
+  TestDictionaryInt(value);
+  TestDictionaryDouble(value);
+  TestDictionaryString(value);
+  TestDictionaryBinary(value, binary_data, binary_data_size, binary_value);
+  TestDictionaryDictionary(value, dictionary_value);
+  TestDictionaryList(value, list_value);
+
+  // Test the size.
+  EXPECT_EQ(8U, value->GetSize());
+
+  // Test copy.
+  CefRefPtr<CefDictionaryValue> copy = value->Copy(false);
+  TestDictionaryEqual(value, copy);
+
+  // Test removal.
+  EXPECT_TRUE(value->Remove(kNullKey));
+  EXPECT_FALSE(value->HasKey(kNullKey));
+
+  EXPECT_TRUE(value->Remove(kBoolKey));
+  EXPECT_FALSE(value->HasKey(kBoolKey));
+
+  EXPECT_TRUE(value->Remove(kIntKey));
+  EXPECT_FALSE(value->HasKey(kIntKey));
+
+  EXPECT_TRUE(value->Remove(kDoubleKey));
+  EXPECT_FALSE(value->HasKey(kDoubleKey));
+
+  EXPECT_TRUE(value->Remove(kStringKey));
+  EXPECT_FALSE(value->HasKey(kStringKey));
+
+  EXPECT_TRUE(value->Remove(kBinaryKey));
+  EXPECT_FALSE(value->HasKey(kBinaryKey));
+  EXPECT_FALSE(binary_value->IsValid());  // Value should be detached
+
+  EXPECT_TRUE(value->Remove(kDictionaryKey));
+  EXPECT_FALSE(value->HasKey(kDictionaryKey));
+  EXPECT_FALSE(dictionary_value->IsValid());  // Value should be detached
+
+  EXPECT_TRUE(value->Remove(kListKey));
+  EXPECT_FALSE(value->HasKey(kListKey));
+  EXPECT_FALSE(list_value->IsValid());  // Value should be detached
+
+  // Test the size.
+  EXPECT_EQ(0U, value->GetSize());
+
+  // Re-add some values.
+  TestDictionaryNull(value);
+  TestDictionaryBool(value);
+  TestDictionaryDictionary(value, dictionary_value);
+
+  // Test the size.
+  EXPECT_EQ(3U, value->GetSize());
+
+  // Clear the values.
+  EXPECT_TRUE(value->Clear());
+  EXPECT_EQ(0U, value->GetSize());
+  EXPECT_FALSE(dictionary_value->IsValid());  // Value should be detached
+}
+
+// Used to test access of dictionary data on a different thread.
+class DictionaryTask : public CefTask {
+ public:
+  DictionaryTask(CefRefPtr<CefDictionaryValue> value,
+                 char* binary_data,
+                 size_t binary_data_size)
+      : value_(value),
+        binary_data_(binary_data),
+        binary_data_size_(binary_data_size) {}
+
+  void Execute() override {
+    TestDictionary(value_, binary_data_, binary_data_size_);
+  }
+
+ private:
+  CefRefPtr<CefDictionaryValue> value_;
+  char* binary_data_;
+  size_t binary_data_size_;
+
+  IMPLEMENT_REFCOUNTING(DictionaryTask);
+};
+
+// LIST TEST HELPERS
+
+// Test list null value.
+void TestListNull(CefRefPtr<CefListValue> value, size_t index) {
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetNull(index));
+  EXPECT_EQ(VTYPE_NULL, value->GetType(index));
+}
+
+// Test list bool value.
+void TestListBool(CefRefPtr<CefListValue> value, size_t index) {
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetBool(index, kBoolValue));
+  EXPECT_EQ(VTYPE_BOOL, value->GetType(index));
+  EXPECT_EQ(kBoolValue, value->GetBool(index));
+}
+
+// Test list int value.
+void TestListInt(CefRefPtr<CefListValue> value, size_t index) {
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetInt(index, kIntValue));
+  EXPECT_EQ(VTYPE_INT, value->GetType(index));
+  EXPECT_EQ(kIntValue, value->GetInt(index));
+}
+
+// Test list double value.
+void TestListDouble(CefRefPtr<CefListValue> value, size_t index) {
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetDouble(index, kDoubleValue));
+  EXPECT_EQ(VTYPE_DOUBLE, value->GetType(index));
+  EXPECT_EQ(kDoubleValue, value->GetDouble(index));
+}
+
+// Test list string value.
+void TestListString(CefRefPtr<CefListValue> value, size_t index) {
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetString(index, kStringValue));
+  EXPECT_EQ(VTYPE_STRING, value->GetType(index));
+  EXPECT_EQ(kStringValue, value->GetString(index).ToString());
+}
+
+// Test list binary value.
+void TestListBinary(CefRefPtr<CefListValue> value,
+                    size_t index,
+                    char* binary_data,
+                    size_t binary_data_size,
+                    CefRefPtr<CefBinaryValue>& binary_value) {
+  binary_value = CefBinaryValue::Create(binary_data, binary_data_size);
+  EXPECT_TRUE(binary_value.get());
+  EXPECT_TRUE(binary_value->IsValid());
+  EXPECT_FALSE(binary_value->IsOwned());
+
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetBinary(index, binary_value));
+  EXPECT_FALSE(binary_value->IsValid());  // Value should be detached
+  EXPECT_EQ(VTYPE_BINARY, value->GetType(index));
+  binary_value = value->GetBinary(index);
+  EXPECT_TRUE(binary_value.get());
+  EXPECT_TRUE(binary_value->IsValid());
+  EXPECT_TRUE(binary_value->IsOwned());
+  TestBinary(binary_value, binary_data, binary_data_size);
+}
+
+// Test list dictionary value.
+void TestListDictionary(CefRefPtr<CefListValue> value,
+                        size_t index,
+                        CefRefPtr<CefDictionaryValue>& dictionary_value) {
+  dictionary_value = CefDictionaryValue::Create();
+  EXPECT_TRUE(dictionary_value.get());
+  EXPECT_TRUE(dictionary_value->IsValid());
+  EXPECT_FALSE(dictionary_value->IsOwned());
+  EXPECT_FALSE(dictionary_value->IsReadOnly());
+  EXPECT_TRUE(dictionary_value->SetInt(kIntKey, kIntValue));
+  EXPECT_EQ(1U, dictionary_value->GetSize());
+
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetDictionary(index, dictionary_value));
+  EXPECT_FALSE(dictionary_value->IsValid());  // Value should be detached
+  EXPECT_EQ(VTYPE_DICTIONARY, value->GetType(index));
+  dictionary_value = value->GetDictionary(index);
+  EXPECT_TRUE(dictionary_value.get());
+  EXPECT_TRUE(dictionary_value->IsValid());
+  EXPECT_TRUE(dictionary_value->IsOwned());
+  EXPECT_FALSE(dictionary_value->IsReadOnly());
+  EXPECT_EQ(1U, dictionary_value->GetSize());
+  EXPECT_EQ(kIntValue, dictionary_value->GetInt(kIntKey));
+}
+
+// Test list list value.
+void TestListList(CefRefPtr<CefListValue> value,
+                  size_t index,
+                  CefRefPtr<CefListValue>& list_value) {
+  list_value = CefListValue::Create();
+  EXPECT_TRUE(list_value.get());
+  EXPECT_TRUE(list_value->IsValid());
+  EXPECT_FALSE(list_value->IsOwned());
+  EXPECT_FALSE(list_value->IsReadOnly());
+  EXPECT_TRUE(list_value->SetInt(0, kIntValue));
+  EXPECT_EQ(1U, list_value->GetSize());
+
+  CefValueType type = value->GetType(index);
+  EXPECT_TRUE(type == VTYPE_INVALID || type == VTYPE_NULL);
+
+  EXPECT_TRUE(value->SetList(index, list_value));
+  EXPECT_FALSE(list_value->IsValid());  // Value should be detached
+  EXPECT_EQ(VTYPE_LIST, value->GetType(index));
+  list_value = value->GetList(index);
+  EXPECT_TRUE(list_value.get());
+  EXPECT_TRUE(list_value->IsValid());
+  EXPECT_TRUE(list_value->IsOwned());
+  EXPECT_FALSE(list_value->IsReadOnly());
+  EXPECT_EQ(1U, list_value->GetSize());
+  EXPECT_EQ(kIntValue, list_value->GetInt(0));
+}
+
+// Test list value.
+void TestList(CefRefPtr<CefListValue> value,
+              char* binary_data,
+              size_t binary_data_size) {
+  CefRefPtr<CefBinaryValue> binary_value;
+  CefRefPtr<CefDictionaryValue> dictionary_value;
+  CefRefPtr<CefListValue> list_value;
+
+  // Test the size.
+  EXPECT_EQ(0U, value->GetSize());
+
+  // Set the size.
+  EXPECT_TRUE(value->SetSize(8));
+  EXPECT_EQ(8U, value->GetSize());
+
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kNullIndex));
+  TestListNull(value, kNullIndex);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kBoolIndex));
+  TestListBool(value, kBoolIndex);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kIntIndex));
+  TestListInt(value, kIntIndex);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kDoubleIndex));
+  TestListDouble(value, kDoubleIndex);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kStringIndex));
+  TestListString(value, kStringIndex);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kBinaryIndex));
+  TestListBinary(value, kBinaryIndex, binary_data, binary_data_size,
+                 binary_value);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kDictionaryIndex));
+  TestListDictionary(value, kDictionaryIndex, dictionary_value);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(kListIndex));
+  TestListList(value, kListIndex, list_value);
+
+  // Test the size.
+  EXPECT_EQ(8U, value->GetSize());
+
+  // Test various operations with invalid index.
+  EXPECT_FALSE(list_value->Remove(9U));
+  EXPECT_TRUE(list_value->GetType(10U) == VTYPE_INVALID);
+  EXPECT_FALSE(list_value->GetValue(11U).get() != nullptr &&
+               list_value->GetValue(11U)->IsValid());
+
+  // Test copy.
+  CefRefPtr<CefListValue> copy = value->Copy();
+  TestListEqual(value, copy);
+
+  // Test removal (in reverse order so indexes stay valid).
+  EXPECT_TRUE(value->Remove(kListIndex));
+  EXPECT_EQ(7U, value->GetSize());
+  EXPECT_FALSE(list_value->IsValid());  // Value should be detached
+
+  EXPECT_TRUE(value->Remove(kDictionaryIndex));
+  EXPECT_EQ(6U, value->GetSize());
+  EXPECT_FALSE(dictionary_value->IsValid());  // Value should be detached
+
+  EXPECT_TRUE(value->Remove(kBinaryIndex));
+  EXPECT_EQ(5U, value->GetSize());
+  EXPECT_FALSE(binary_value->IsValid());  // Value should be detached
+
+  EXPECT_TRUE(value->Remove(kStringIndex));
+  EXPECT_EQ(4U, value->GetSize());
+
+  EXPECT_TRUE(value->Remove(kDoubleIndex));
+  EXPECT_EQ(3U, value->GetSize());
+
+  EXPECT_TRUE(value->Remove(kIntIndex));
+  EXPECT_EQ(2U, value->GetSize());
+
+  EXPECT_TRUE(value->Remove(kBoolIndex));
+  EXPECT_EQ(1U, value->GetSize());
+
+  EXPECT_TRUE(value->Remove(kNullIndex));
+  EXPECT_EQ(0U, value->GetSize());
+
+  // Re-add some values.
+  EXPECT_EQ(VTYPE_INVALID, value->GetType(0));
+  TestListNull(value, 0);
+  EXPECT_EQ(VTYPE_INVALID, value->GetType(1));
+  TestListBool(value, 1);
+  EXPECT_EQ(VTYPE_INVALID, value->GetType(2));
+  TestListList(value, 2, list_value);
+
+  // Test the size.
+  EXPECT_EQ(3U, value->GetSize());
+
+  // Clear the values.
+  EXPECT_TRUE(value->Clear());
+  EXPECT_EQ(0U, value->GetSize());
+  EXPECT_FALSE(list_value->IsValid());  // Value should be detached
+
+  // Add some values in random order.
+  EXPECT_EQ(VTYPE_INVALID, value->GetType(2));
+  TestListInt(value, 2);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(0));
+  TestListBool(value, 0);
+  EXPECT_EQ(VTYPE_NULL, value->GetType(1));
+  TestListList(value, 1, list_value);
+
+  EXPECT_EQ(VTYPE_BOOL, value->GetType(0));
+  EXPECT_EQ(VTYPE_LIST, value->GetType(1));
+  EXPECT_EQ(VTYPE_INT, value->GetType(2));
+
+  // Test the size.
+  EXPECT_EQ(3U, value->GetSize());
+
+  // Clear some values.
+  EXPECT_TRUE(value->SetSize(1));
+  EXPECT_EQ(1U, value->GetSize());
+  EXPECT_FALSE(list_value->IsValid());  // Value should be detached
+
+  EXPECT_EQ(VTYPE_BOOL, value->GetType(0));
+  EXPECT_EQ(VTYPE_INVALID, value->GetType(1));
+  EXPECT_EQ(VTYPE_INVALID, value->GetType(2));
+
+  // Clear all values.
+  EXPECT_TRUE(value->Clear());
+  EXPECT_EQ(0U, value->GetSize());
+}
+
+// Used to test access of list data on a different thread.
+class ListTask : public CefTask {
+ public:
+  ListTask(CefRefPtr<CefListValue> value,
+           char* binary_data,
+           size_t binary_data_size)
+      : value_(value),
+        binary_data_(binary_data),
+        binary_data_size_(binary_data_size) {}
+
+  void Execute() override { TestList(value_, binary_data_, binary_data_size_); }
+
+ private:
+  CefRefPtr<CefListValue> value_;
+  char* binary_data_;
+  size_t binary_data_size_;
+
+  IMPLEMENT_REFCOUNTING(ListTask);
+};
+
+void CreateAndCompareCopy(CefRefPtr<CefValue> value) {
+  CefRefPtr<CefValue> value2 = value->Copy();
+  EXPECT_TRUE(value->IsEqual(value));
+  EXPECT_TRUE(value->IsSame(value));
+  EXPECT_TRUE(value2->IsEqual(value2));
+  EXPECT_TRUE(value2->IsSame(value2));
+  EXPECT_TRUE(value->IsEqual(value2));
+  EXPECT_FALSE(value->IsSame(value2));
+  EXPECT_TRUE(value2->IsEqual(value));
+  EXPECT_FALSE(value2->IsSame(value));
+}
+
+CefRefPtr<CefBinaryValue> CreateBinaryValue() {
+  char binary_data[] = "This is my test data";
+  const size_t binary_data_size = sizeof(binary_data) - 1;
+
+  CefRefPtr<CefBinaryValue> binary_value =
+      CefBinaryValue::Create(binary_data, binary_data_size);
+  EXPECT_TRUE(binary_value.get());
+  EXPECT_TRUE(binary_value->IsValid());
+  EXPECT_FALSE(binary_value->IsOwned());
+  TestBinary(binary_value, binary_data, binary_data_size);
+  return binary_value;
+}
+
+CefRefPtr<CefListValue> CreateListValue() {
+  CefRefPtr<CefListValue> list_value = CefListValue::Create();
+  EXPECT_TRUE(list_value.get());
+  EXPECT_TRUE(list_value->IsValid());
+  EXPECT_FALSE(list_value->IsOwned());
+  EXPECT_FALSE(list_value->IsReadOnly());
+  EXPECT_TRUE(list_value->SetInt(0, kIntValue));
+  EXPECT_TRUE(list_value->SetInt(1, kDoubleValue));
+  return list_value;
+}
+
+const char* kKey1 = "key1";
+const char* kKey2 = "key2";
+
+CefRefPtr<CefDictionaryValue> CreateDictionaryValue() {
+  // Create the dictionary.
+  CefRefPtr<CefDictionaryValue> dict_value = CefDictionaryValue::Create();
+  EXPECT_TRUE(dict_value.get());
+  EXPECT_TRUE(dict_value->IsValid());
+  EXPECT_FALSE(dict_value->IsOwned());
+  EXPECT_FALSE(dict_value->IsReadOnly());
+  EXPECT_TRUE(dict_value->SetInt(kKey1, kIntValue));
+  EXPECT_TRUE(dict_value->SetInt(kKey2, kDoubleValue));
+  return dict_value;
+}
+
+}  // namespace
+
+// Test binary value access.
+TEST(ValuesTest, BinaryAccess) {
+  char data[] = "This is my test data";
+
+  CefRefPtr<CefBinaryValue> value =
+      CefBinaryValue::Create(data, sizeof(data) - 1);
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+
+  // Test on this thread.
+  TestBinary(value, data, sizeof(data) - 1);
+}
+
+// Test binary value access on a different thread.
+TEST(ValuesTest, BinaryAccessOtherThread) {
+  char data[] = "This is my test data";
+
+  CefRefPtr<CefBinaryValue> value =
+      CefBinaryValue::Create(data, sizeof(data) - 1);
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+
+  // Test on a different thread.
+  CefPostTask(TID_UI, new BinaryTask(value, data, sizeof(data) - 1));
+  WaitForUIThread();
+}
+
+// Test dictionary value access.
+TEST(ValuesTest, DictionaryAccess) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_FALSE(value->IsReadOnly());
+
+  char binary_data[] = "This is my test data";
+
+  // Test on this thread.
+  TestDictionary(value, binary_data, sizeof(binary_data) - 1);
+}
+
+// Test dictionary value access on a different thread.
+TEST(ValuesTest, DictionaryAccessOtherThread) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_FALSE(value->IsReadOnly());
+
+  char binary_data[] = "This is my test data";
+
+  // Test on a different thread.
+  CefPostTask(TID_UI,
+              new DictionaryTask(value, binary_data, sizeof(binary_data) - 1));
+  WaitForUIThread();
+}
+
+// Test dictionary value nested detachment
+TEST(ValuesTest, DictionaryDetachment) {
+  CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_FALSE(value->IsReadOnly());
+
+  CefRefPtr<CefDictionaryValue> dictionary_value = CefDictionaryValue::Create();
+  CefRefPtr<CefDictionaryValue> dictionary_value2 =
+      CefDictionaryValue::Create();
+  CefRefPtr<CefDictionaryValue> dictionary_value3 =
+      CefDictionaryValue::Create();
+
+  dictionary_value2->SetDictionary(kDictionaryKey, dictionary_value3);
+  EXPECT_FALSE(dictionary_value3->IsValid());
+  dictionary_value->SetDictionary(kDictionaryKey, dictionary_value2);
+  EXPECT_FALSE(dictionary_value2->IsValid());
+  value->SetDictionary(kDictionaryKey, dictionary_value);
+  EXPECT_FALSE(dictionary_value->IsValid());
+
+  dictionary_value = value->GetDictionary(kDictionaryKey);
+  EXPECT_TRUE(dictionary_value.get());
+  EXPECT_TRUE(dictionary_value->IsValid());
+
+  dictionary_value2 = dictionary_value->GetDictionary(kDictionaryKey);
+  EXPECT_TRUE(dictionary_value2.get());
+  EXPECT_TRUE(dictionary_value2->IsValid());
+
+  dictionary_value3 = dictionary_value2->GetDictionary(kDictionaryKey);
+  EXPECT_TRUE(dictionary_value3.get());
+  EXPECT_TRUE(dictionary_value3->IsValid());
+
+  EXPECT_TRUE(value->Remove(kDictionaryKey));
+  EXPECT_FALSE(dictionary_value->IsValid());
+  EXPECT_FALSE(dictionary_value2->IsValid());
+  EXPECT_FALSE(dictionary_value3->IsValid());
+}
+
+// Test list value access.
+TEST(ValuesTest, ListAccess) {
+  CefRefPtr<CefListValue> value = CefListValue::Create();
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_FALSE(value->IsReadOnly());
+
+  char binary_data[] = "This is my test data";
+
+  // Test on this thread.
+  TestList(value, binary_data, sizeof(binary_data) - 1);
+}
+
+// Test list value access on a different thread.
+TEST(ValuesTest, ListAccessOtherThread) {
+  CefRefPtr<CefListValue> value = CefListValue::Create();
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_FALSE(value->IsReadOnly());
+
+  char binary_data[] = "This is my test data";
+
+  // Test on a different thread.
+  CefPostTask(TID_UI,
+              new ListTask(value, binary_data, sizeof(binary_data) - 1));
+  WaitForUIThread();
+}
+
+// Test list value nested detachment
+TEST(ValuesTest, ListDetachment) {
+  CefRefPtr<CefListValue> value = CefListValue::Create();
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_FALSE(value->IsReadOnly());
+
+  CefRefPtr<CefListValue> list_value = CefListValue::Create();
+  CefRefPtr<CefListValue> list_value2 = CefListValue::Create();
+  CefRefPtr<CefListValue> list_value3 = CefListValue::Create();
+
+  list_value2->SetList(0, list_value3);
+  EXPECT_FALSE(list_value3->IsValid());
+  list_value->SetList(0, list_value2);
+  EXPECT_FALSE(list_value2->IsValid());
+  value->SetList(0, list_value);
+  EXPECT_FALSE(list_value->IsValid());
+
+  list_value = value->GetList(0);
+  EXPECT_TRUE(list_value.get());
+  EXPECT_TRUE(list_value->IsValid());
+
+  list_value2 = list_value->GetList(0);
+  EXPECT_TRUE(list_value2.get());
+  EXPECT_TRUE(list_value2->IsValid());
+
+  list_value3 = list_value2->GetList(0);
+  EXPECT_TRUE(list_value3.get());
+  EXPECT_TRUE(list_value3->IsValid());
+
+  EXPECT_TRUE(value->Remove(0));
+  EXPECT_FALSE(list_value->IsValid());
+  EXPECT_FALSE(list_value2->IsValid());
+  EXPECT_FALSE(list_value3->IsValid());
+}
+
+// Test get/set of a CefValue simple types.
+TEST(ValuesTest, ValueSimple) {
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value.get());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  EXPECT_EQ(VTYPE_NULL, value->GetType());
+  CreateAndCompareCopy(value);
+
+  EXPECT_TRUE(value->SetBool(true));
+  EXPECT_EQ(VTYPE_BOOL, value->GetType());
+  EXPECT_TRUE(value->GetBool());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  EXPECT_TRUE(value->SetBool(false));
+  EXPECT_EQ(VTYPE_BOOL, value->GetType());
+  EXPECT_FALSE(value->GetBool());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  EXPECT_TRUE(value->SetInt(3));
+  EXPECT_EQ(VTYPE_INT, value->GetType());
+  EXPECT_EQ(3, value->GetInt());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  EXPECT_TRUE(value->SetDouble(5.665));
+  EXPECT_EQ(VTYPE_DOUBLE, value->GetType());
+  EXPECT_EQ(5.665, value->GetDouble());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  const char* str = "Test string";
+  EXPECT_TRUE(value->SetString(str));
+  EXPECT_EQ(VTYPE_STRING, value->GetType());
+  EXPECT_STREQ(str, value->GetString().ToString().data());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  EXPECT_TRUE(value->SetNull());
+  EXPECT_EQ(VTYPE_NULL, value->GetType());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+}
+
+// Test association of a CefValue simple type with a CefListValue.
+TEST(ValuesTest, ValueSimpleToList) {
+  const double double_value = 5.665;
+
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetDouble(double_value));
+
+  // Add the value to the target list.
+  CefRefPtr<CefListValue> target_list = CefListValue::Create();
+  EXPECT_TRUE(target_list->SetValue(0, value));
+
+  // Test the value in the target list.
+  EXPECT_EQ(VTYPE_DOUBLE, target_list->GetType(0));
+  EXPECT_EQ(double_value, target_list->GetDouble(0));
+
+  // Get the value from the target list.
+  CefRefPtr<CefValue> value2 = target_list->GetValue(0);
+  EXPECT_TRUE(value2.get());
+  EXPECT_FALSE(value2->IsOwned());
+  EXPECT_FALSE(value2->IsReadOnly());
+  EXPECT_EQ(VTYPE_DOUBLE, value2->GetType());
+  EXPECT_EQ(double_value, value2->GetDouble());
+
+  // Values are equal but not the same.
+  EXPECT_TRUE(value->IsEqual(value2));
+  EXPECT_TRUE(value2->IsEqual(value));
+  EXPECT_FALSE(value->IsSame(value2));
+  EXPECT_FALSE(value2->IsSame(value));
+
+  // Change the value in the target list.
+  EXPECT_TRUE(target_list->SetInt(0, 5));
+  EXPECT_EQ(VTYPE_INT, target_list->GetType(0));
+  EXPECT_EQ(5, target_list->GetInt(0));
+
+  // The other values are still valid.
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_TRUE(value2->IsValid());
+}
+
+// Test association of a CefValue simple type with a CefDictionaryValue.
+TEST(ValuesTest, ValueSimpleToDictionary) {
+  const double double_value = 5.665;
+  const char* key = "key";
+
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetDouble(double_value));
+
+  // Add the value to the target dictionary.
+  CefRefPtr<CefDictionaryValue> target_dict = CefDictionaryValue::Create();
+  EXPECT_TRUE(target_dict->SetValue(key, value));
+
+  // Test the value in the target dictionary.
+  EXPECT_EQ(VTYPE_DOUBLE, target_dict->GetType(key));
+  EXPECT_EQ(double_value, target_dict->GetDouble(key));
+
+  // Get the value from the target dictionary.
+  CefRefPtr<CefValue> value2 = target_dict->GetValue(key);
+  EXPECT_TRUE(value2.get());
+  EXPECT_FALSE(value2->IsOwned());
+  EXPECT_FALSE(value2->IsReadOnly());
+  EXPECT_EQ(VTYPE_DOUBLE, value2->GetType());
+  EXPECT_EQ(double_value, value2->GetDouble());
+
+  // Values are equal but not the same.
+  EXPECT_TRUE(value->IsEqual(value2));
+  EXPECT_TRUE(value2->IsEqual(value));
+  EXPECT_FALSE(value->IsSame(value2));
+  EXPECT_FALSE(value2->IsSame(value));
+
+  // Change the value in the target dictionary.
+  EXPECT_TRUE(target_dict->SetInt(key, 5));
+  EXPECT_EQ(VTYPE_INT, target_dict->GetType(key));
+  EXPECT_EQ(5, target_dict->GetInt(key));
+
+  // The other values are still valid.
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_TRUE(value2->IsValid());
+}
+
+// Test get/set of a CefValue binary type.
+TEST(ValuesTest, ValueBinary) {
+  // Create the binary.
+  CefRefPtr<CefBinaryValue> binary_value = CreateBinaryValue();
+
+  // Create the value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetBinary(binary_value));
+  EXPECT_EQ(VTYPE_BINARY, value->GetType());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_TRUE(value->IsReadOnly());  // Binary values are always read-only.
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  // Get the binary reference from the value.
+  CefRefPtr<CefBinaryValue> binary_value2 = value->GetBinary();
+  EXPECT_TRUE(binary_value2.get());
+  EXPECT_TRUE(binary_value2->IsValid());
+  EXPECT_FALSE(binary_value2->IsOwned());
+
+  // The binaries are the same and equal.
+  TestBinaryEqual(binary_value, binary_value2);
+  EXPECT_TRUE(binary_value->IsSame(binary_value2));
+  EXPECT_TRUE(binary_value2->IsSame(binary_value));
+}
+
+// Test association of a CefValue binary with a CefListValue.
+TEST(ValuesTest, ValueBinaryToList) {
+  // Create the binary.
+  CefRefPtr<CefBinaryValue> binary_value = CreateBinaryValue();
+
+  // Add the binary to a value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetBinary(binary_value));
+
+  // Add the value to the target list.
+  CefRefPtr<CefListValue> target_list = CefListValue::Create();
+  EXPECT_TRUE(target_list->SetValue(0, value));
+
+  // The binary value is now owned by the target list.
+  EXPECT_FALSE(binary_value->IsValid());
+
+  // The value is still valid and points to the new reference list.
+  EXPECT_TRUE(value->IsValid());
+
+  CefRefPtr<CefBinaryValue> binary_value2 = value->GetBinary();
+  CefRefPtr<CefBinaryValue> binary_value3 = target_list->GetBinary(0);
+  CefRefPtr<CefValue> value2 = target_list->GetValue(0);
+  CefRefPtr<CefBinaryValue> binary_value4 = value2->GetBinary();
+
+  // All values are owned by the target list.
+  EXPECT_TRUE(value->IsOwned());
+  EXPECT_TRUE(value2->IsOwned());
+  EXPECT_TRUE(binary_value2->IsOwned());
+  EXPECT_TRUE(binary_value3->IsOwned());
+  EXPECT_TRUE(binary_value4->IsOwned());
+
+  // All values are the same.
+  EXPECT_TRUE(binary_value2->IsSame(binary_value3));
+  TestBinaryEqual(binary_value2, binary_value3);
+  EXPECT_TRUE(binary_value2->IsSame(binary_value4));
+  TestBinaryEqual(binary_value2, binary_value4);
+
+  // Change the value to something else.
+  EXPECT_TRUE(target_list->SetInt(0, kIntValue));
+  EXPECT_EQ(VTYPE_INT, target_list->GetType(0));
+  EXPECT_EQ(kIntValue, target_list->GetInt(0));
+
+  // Now the references are invalid.
+  EXPECT_FALSE(value->IsValid());
+  EXPECT_FALSE(value2->IsValid());
+  EXPECT_FALSE(binary_value2->IsValid());
+  EXPECT_FALSE(binary_value3->IsValid());
+  EXPECT_FALSE(binary_value4->IsValid());
+
+  // Verify that adding a binary to a list directly invalidates both the binary
+  // and the value that references it.
+  binary_value = CreateBinaryValue();
+  value = CefValue::Create();
+  EXPECT_TRUE(value->SetBinary(binary_value));
+  target_list->SetBinary(0, binary_value);
+  EXPECT_FALSE(binary_value->IsValid());
+  EXPECT_FALSE(value->IsValid());
+}
+
+// Test association of a CefValue binary with a CefDictionaryValue.
+TEST(ValuesTest, ValueBinaryToDictionary) {
+  const char* key = "key";
+
+  // Create the binary.
+  CefRefPtr<CefBinaryValue> binary_value = CreateBinaryValue();
+
+  // Add the binary to a value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetBinary(binary_value));
+
+  // Add the value to the target dictionary.
+  CefRefPtr<CefDictionaryValue> target_dict = CefDictionaryValue::Create();
+  EXPECT_TRUE(target_dict->SetValue(key, value));
+
+  // The list value is now owned by the target dictionary.
+  EXPECT_FALSE(binary_value->IsValid());
+
+  // The value is still valid and points to the new reference list.
+  EXPECT_TRUE(value->IsValid());
+
+  CefRefPtr<CefBinaryValue> binary_value2 = value->GetBinary();
+  CefRefPtr<CefBinaryValue> binary_value3 = target_dict->GetBinary(key);
+  CefRefPtr<CefValue> value2 = target_dict->GetValue(key);
+  CefRefPtr<CefBinaryValue> binary_value4 = value2->GetBinary();
+
+  // All values are owned by the target dictionary.
+  EXPECT_TRUE(value->IsOwned());
+  EXPECT_TRUE(value2->IsOwned());
+  EXPECT_TRUE(binary_value2->IsOwned());
+  EXPECT_TRUE(binary_value3->IsOwned());
+  EXPECT_TRUE(binary_value4->IsOwned());
+
+  // All values are the same.
+  EXPECT_TRUE(binary_value2->IsSame(binary_value3));
+  TestBinaryEqual(binary_value2, binary_value3);
+  EXPECT_TRUE(binary_value2->IsSame(binary_value4));
+  TestBinaryEqual(binary_value2, binary_value4);
+
+  // Change the value to something else.
+  EXPECT_TRUE(target_dict->SetInt(key, kIntValue));
+  EXPECT_EQ(VTYPE_INT, target_dict->GetType(key));
+  EXPECT_EQ(kIntValue, target_dict->GetInt(key));
+
+  // Now the references are invalid.
+  EXPECT_FALSE(value->IsValid());
+  EXPECT_FALSE(value2->IsValid());
+  EXPECT_FALSE(binary_value2->IsValid());
+  EXPECT_FALSE(binary_value3->IsValid());
+  EXPECT_FALSE(binary_value4->IsValid());
+
+  // Verify that adding a binary to a dictionary directly invalidates both the
+  // binary and the value that references it.
+  binary_value = CreateBinaryValue();
+  value = CefValue::Create();
+  EXPECT_TRUE(value->SetBinary(binary_value));
+  target_dict->SetBinary(key, binary_value);
+  EXPECT_FALSE(binary_value->IsValid());
+  EXPECT_FALSE(value->IsValid());
+}
+
+// Test get/set of a CefValue list type.
+TEST(ValuesTest, ValueList) {
+  // Create the list.
+  CefRefPtr<CefListValue> list_value = CreateListValue();
+
+  // Create the value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetList(list_value));
+  EXPECT_EQ(VTYPE_LIST, value->GetType());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  // Get the list reference from the value.
+  CefRefPtr<CefListValue> list_value2 = value->GetList();
+  EXPECT_TRUE(list_value2.get());
+  EXPECT_TRUE(list_value2->IsValid());
+  EXPECT_FALSE(list_value2->IsOwned());
+  EXPECT_FALSE(list_value2->IsReadOnly());
+
+  // The lists are the same and equal.
+  TestListEqual(list_value, list_value2);
+  EXPECT_TRUE(list_value->IsSame(list_value2));
+  EXPECT_TRUE(list_value2->IsSame(list_value));
+
+  // Change a value in one list and verify that it's changed in the other list.
+  EXPECT_TRUE(list_value->SetString(0, kStringValue));
+  EXPECT_EQ(VTYPE_STRING, list_value->GetType(0));
+  EXPECT_STREQ(kStringValue, list_value->GetString(0).ToString().data());
+}
+
+// Test association of a CefValue list with a CefListValue.
+TEST(ValuesTest, ValueListToList) {
+  // Create the list.
+  CefRefPtr<CefListValue> list_value = CreateListValue();
+
+  // Add the list to a value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetList(list_value));
+
+  // Add the value to the target list.
+  CefRefPtr<CefListValue> target_list = CefListValue::Create();
+  EXPECT_TRUE(target_list->SetValue(0, value));
+
+  // The list value is now owned by the target list.
+  EXPECT_FALSE(list_value->IsValid());
+
+  // The value is still valid and points to the new reference list.
+  EXPECT_TRUE(value->IsValid());
+
+  CefRefPtr<CefListValue> list_value2 = value->GetList();
+  CefRefPtr<CefListValue> list_value3 = target_list->GetList(0);
+  CefRefPtr<CefValue> value2 = target_list->GetValue(0);
+  CefRefPtr<CefListValue> list_value4 = value2->GetList();
+
+  // All values are owned by the target list.
+  EXPECT_TRUE(value->IsOwned());
+  EXPECT_TRUE(value2->IsOwned());
+  EXPECT_TRUE(list_value2->IsOwned());
+  EXPECT_TRUE(list_value3->IsOwned());
+  EXPECT_TRUE(list_value4->IsOwned());
+
+  // All values are the same.
+  EXPECT_TRUE(list_value2->IsSame(list_value3));
+  TestListEqual(list_value2, list_value3);
+  EXPECT_TRUE(list_value2->IsSame(list_value4));
+  TestListEqual(list_value2, list_value4);
+
+  // Change the value to something else.
+  EXPECT_TRUE(target_list->SetInt(0, kIntValue));
+  EXPECT_EQ(VTYPE_INT, target_list->GetType(0));
+  EXPECT_EQ(kIntValue, target_list->GetInt(0));
+
+  // Now the references are invalid.
+  EXPECT_FALSE(value->IsValid());
+  EXPECT_FALSE(value2->IsValid());
+  EXPECT_FALSE(list_value2->IsValid());
+  EXPECT_FALSE(list_value3->IsValid());
+  EXPECT_FALSE(list_value4->IsValid());
+
+  // Verify that adding a list to a list directly invalidates both the list
+  // and the value that references it.
+  list_value = CreateListValue();
+  value = CefValue::Create();
+  EXPECT_TRUE(value->SetList(list_value));
+  target_list->SetList(0, list_value);
+  EXPECT_FALSE(list_value->IsValid());
+  EXPECT_FALSE(value->IsValid());
+}
+
+// Test association of a CefValue list with a CefDictionaryValue.
+TEST(ValuesTest, ValueListToDictionary) {
+  const char* key = "key";
+
+  // Create the list.
+  CefRefPtr<CefListValue> list_value = CreateListValue();
+
+  // Add the list to a value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetList(list_value));
+
+  // Add the value to the target dictionary.
+  CefRefPtr<CefDictionaryValue> target_dict = CefDictionaryValue::Create();
+  EXPECT_TRUE(target_dict->SetValue(key, value));
+
+  // The list value is now owned by the target dictionary.
+  EXPECT_FALSE(list_value->IsValid());
+
+  // The value is still valid and points to the new reference list.
+  EXPECT_TRUE(value->IsValid());
+
+  CefRefPtr<CefListValue> list_value2 = value->GetList();
+  CefRefPtr<CefListValue> list_value3 = target_dict->GetList(key);
+  CefRefPtr<CefValue> value2 = target_dict->GetValue(key);
+  CefRefPtr<CefListValue> list_value4 = value2->GetList();
+
+  // All values are owned by the target dictionary.
+  EXPECT_TRUE(value->IsOwned());
+  EXPECT_TRUE(value2->IsOwned());
+  EXPECT_TRUE(list_value2->IsOwned());
+  EXPECT_TRUE(list_value3->IsOwned());
+  EXPECT_TRUE(list_value4->IsOwned());
+
+  // All values are the same.
+  EXPECT_TRUE(list_value2->IsSame(list_value3));
+  TestListEqual(list_value2, list_value3);
+  EXPECT_TRUE(list_value2->IsSame(list_value4));
+  TestListEqual(list_value2, list_value4);
+
+  // Change the value to something else.
+  EXPECT_TRUE(target_dict->SetInt(key, kIntValue));
+  EXPECT_EQ(VTYPE_INT, target_dict->GetType(key));
+  EXPECT_EQ(kIntValue, target_dict->GetInt(key));
+
+  // Now the references are invalid.
+  EXPECT_FALSE(value->IsValid());
+  EXPECT_FALSE(value2->IsValid());
+  EXPECT_FALSE(list_value2->IsValid());
+  EXPECT_FALSE(list_value3->IsValid());
+  EXPECT_FALSE(list_value4->IsValid());
+
+  // Verify that adding a list to a dictionary directly invalidates both the
+  // list and the value that references it.
+  list_value = CreateListValue();
+  value = CefValue::Create();
+  EXPECT_TRUE(value->SetList(list_value));
+  target_dict->SetList(key, list_value);
+  EXPECT_FALSE(list_value->IsValid());
+  EXPECT_FALSE(value->IsValid());
+}
+
+// Test get/set of a CefValue dictionary type.
+TEST(ValuesTest, ValueDictionary) {
+  // Create the dictionary.
+  CefRefPtr<CefDictionaryValue> dict_value = CreateDictionaryValue();
+
+  // Create the value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetDictionary(dict_value));
+  EXPECT_EQ(VTYPE_DICTIONARY, value->GetType());
+  EXPECT_TRUE(value->IsValid());
+  EXPECT_FALSE(value->IsReadOnly());
+  EXPECT_FALSE(value->IsOwned());
+  CreateAndCompareCopy(value);
+
+  // Get the dictionary reference from the value.
+  CefRefPtr<CefDictionaryValue> dict_value2 = value->GetDictionary();
+  EXPECT_TRUE(dict_value2.get());
+  EXPECT_TRUE(dict_value2->IsValid());
+  EXPECT_FALSE(dict_value2->IsOwned());
+  EXPECT_FALSE(dict_value2->IsReadOnly());
+
+  // The dictionaries are the same and equal.
+  TestDictionaryEqual(dict_value, dict_value2);
+  EXPECT_TRUE(dict_value->IsSame(dict_value2));
+  EXPECT_TRUE(dict_value2->IsSame(dict_value));
+
+  // Change a value in one dictionary and verify that it's changed in the other
+  // dictionary.
+  EXPECT_TRUE(dict_value->SetString(kKey1, kStringValue));
+  EXPECT_EQ(VTYPE_STRING, dict_value->GetType(kKey1));
+  EXPECT_STREQ(kStringValue, dict_value->GetString(kKey1).ToString().data());
+}
+
+// Test association of a CefValue dictionary with a CefListValue.
+TEST(ValuesTest, ValueDictionaryToList) {
+  // Create the dictionary.
+  CefRefPtr<CefDictionaryValue> dict_value = CreateDictionaryValue();
+
+  // Add the list to a value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetDictionary(dict_value));
+
+  // Add the value to the target list.
+  CefRefPtr<CefListValue> target_list = CefListValue::Create();
+  EXPECT_TRUE(target_list->SetValue(0, value));
+
+  // The list value is now owned by the target list.
+  EXPECT_FALSE(dict_value->IsValid());
+
+  // The value is still valid and points to the new reference list.
+  EXPECT_TRUE(value->IsValid());
+
+  CefRefPtr<CefDictionaryValue> dict_value2 = value->GetDictionary();
+  CefRefPtr<CefDictionaryValue> dict_value3 = target_list->GetDictionary(0);
+  CefRefPtr<CefValue> value2 = target_list->GetValue(0);
+  CefRefPtr<CefDictionaryValue> dict_value4 = value2->GetDictionary();
+
+  // All values are owned by the target list.
+  EXPECT_TRUE(value->IsOwned());
+  EXPECT_TRUE(value2->IsOwned());
+  EXPECT_TRUE(dict_value2->IsOwned());
+  EXPECT_TRUE(dict_value3->IsOwned());
+  EXPECT_TRUE(dict_value4->IsOwned());
+
+  // All values are the same.
+  EXPECT_TRUE(dict_value2->IsSame(dict_value3));
+  TestDictionaryEqual(dict_value2, dict_value3);
+  EXPECT_TRUE(dict_value2->IsSame(dict_value4));
+  TestDictionaryEqual(dict_value2, dict_value4);
+
+  // Change the value to something else.
+  EXPECT_TRUE(target_list->SetInt(0, kIntValue));
+  EXPECT_EQ(VTYPE_INT, target_list->GetType(0));
+  EXPECT_EQ(kIntValue, target_list->GetInt(0));
+
+  // Now the references are invalid.
+  EXPECT_FALSE(value->IsValid());
+  EXPECT_FALSE(value2->IsValid());
+  EXPECT_FALSE(dict_value2->IsValid());
+  EXPECT_FALSE(dict_value3->IsValid());
+  EXPECT_FALSE(dict_value4->IsValid());
+
+  // Verify that adding a dictionary to a list directly invalidates both the
+  // dictionary and the value that references it.
+  dict_value = CreateDictionaryValue();
+  value = CefValue::Create();
+  EXPECT_TRUE(value->SetDictionary(dict_value));
+  target_list->SetDictionary(0, dict_value);
+  EXPECT_FALSE(dict_value->IsValid());
+  EXPECT_FALSE(value->IsValid());
+}
+
+// Test association of a CefValue dictionary with a CefDictionaryValue.
+TEST(ValuesTest, ValueDictionaryToDictionary) {
+  const char* key = "key";
+
+  // Create the dictionary.
+  CefRefPtr<CefDictionaryValue> dict_value = CreateDictionaryValue();
+
+  // Add the list to a value.
+  CefRefPtr<CefValue> value = CefValue::Create();
+  EXPECT_TRUE(value->SetDictionary(dict_value));
+
+  // Add the value to the target dictionary.
+  CefRefPtr<CefDictionaryValue> target_dict = CefDictionaryValue::Create();
+  EXPECT_TRUE(target_dict->SetValue(key, value));
+
+  // The list value is now owned by the target dictionary.
+  EXPECT_FALSE(dict_value->IsValid());
+
+  // The value is still valid and points to the new reference list.
+  EXPECT_TRUE(value->IsValid());
+
+  CefRefPtr<CefDictionaryValue> dict_value2 = value->GetDictionary();
+  CefRefPtr<CefDictionaryValue> dict_value3 = target_dict->GetDictionary(key);
+  CefRefPtr<CefValue> value2 = target_dict->GetValue(key);
+  CefRefPtr<CefDictionaryValue> dict_value4 = value2->GetDictionary();
+
+  // All values are owned by the target dictionary.
+  EXPECT_TRUE(value->IsOwned());
+  EXPECT_TRUE(value2->IsOwned());
+  EXPECT_TRUE(dict_value2->IsOwned());
+  EXPECT_TRUE(dict_value3->IsOwned());
+  EXPECT_TRUE(dict_value4->IsOwned());
+
+  // All values are the same.
+  EXPECT_TRUE(dict_value2->IsSame(dict_value3));
+  TestDictionaryEqual(dict_value2, dict_value3);
+  EXPECT_TRUE(dict_value2->IsSame(dict_value4));
+  TestDictionaryEqual(dict_value2, dict_value4);
+
+  // Change the value to something else.
+  EXPECT_TRUE(target_dict->SetInt(key, kIntValue));
+  EXPECT_EQ(VTYPE_INT, target_dict->GetType(key));
+  EXPECT_EQ(kIntValue, target_dict->GetInt(key));
+
+  // Now the references are invalid.
+  EXPECT_FALSE(value->IsValid());
+  EXPECT_FALSE(value2->IsValid());
+  EXPECT_FALSE(dict_value2->IsValid());
+  EXPECT_FALSE(dict_value3->IsValid());
+  EXPECT_FALSE(dict_value4->IsValid());
+
+  // Verify that adding a dictionary to a dictionary directly invalidates both
+  // the dictionary and the value that references it.
+  dict_value = CreateDictionaryValue();
+  value = CefValue::Create();
+  EXPECT_TRUE(value->SetDictionary(dict_value));
+  target_dict->SetDictionary(key, dict_value);
+  EXPECT_FALSE(dict_value->IsValid());
+  EXPECT_FALSE(value->IsValid());
+}
diff --git a/src/tests/ceftests/version_unittest.cc b/src/tests/ceftests/version_unittest.cc
new file mode 100644
index 0000000..54776e5
--- /dev/null
+++ b/src/tests/ceftests/version_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_api_hash.h"
+#include "include/cef_version.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+TEST(VersionTest, VersionInfo) {
+  EXPECT_EQ(CEF_VERSION_MAJOR, cef_version_info(0));
+  EXPECT_EQ(CEF_VERSION_MINOR, cef_version_info(1));
+  EXPECT_EQ(CEF_VERSION_PATCH, cef_version_info(2));
+  EXPECT_EQ(CEF_COMMIT_NUMBER, cef_version_info(3));
+  EXPECT_EQ(CHROME_VERSION_MAJOR, cef_version_info(4));
+  EXPECT_EQ(CHROME_VERSION_MINOR, cef_version_info(5));
+  EXPECT_EQ(CHROME_VERSION_BUILD, cef_version_info(6));
+  EXPECT_EQ(CHROME_VERSION_PATCH, cef_version_info(7));
+}
+
+TEST(VersionTest, ApiHash) {
+  EXPECT_STREQ(CEF_API_HASH_PLATFORM, cef_api_hash(0));
+  EXPECT_STREQ(CEF_API_HASH_UNIVERSAL, cef_api_hash(1));
+  EXPECT_STREQ(CEF_COMMIT_HASH, cef_api_hash(2));
+}
diff --git a/src/tests/ceftests/views/button_unittest.cc b/src/tests/ceftests/views/button_unittest.cc
new file mode 100644
index 0000000..1bd6363
--- /dev/null
+++ b/src/tests/ceftests/views/button_unittest.cc
@@ -0,0 +1,641 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/views/cef_button.h"
+#include "include/views/cef_button_delegate.h"
+#include "include/views/cef_label_button.h"
+#include "include/views/cef_menu_button.h"
+#include "include/views/cef_menu_button_delegate.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/image_util.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/ceftests/thread_helper.h"
+#include "tests/ceftests/views/test_window_delegate.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+#define BUTTON_TEST(name) UI_THREAD_TEST(ViewsButtonTest, name)
+#define BUTTON_TEST_ASYNC(name) UI_THREAD_TEST_ASYNC(ViewsButtonTest, name)
+
+namespace {
+
+CefRefPtr<CefImage> CreateIconImage() {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  image_util::LoadIconImage(image, 1.0);
+  image_util::LoadIconImage(image, 2.0);
+  return image;
+}
+
+const char kButtonText[] = "My Button";
+
+void VerifyButtonStyle(CefRefPtr<CefButton> button) {
+  // Test state.
+  EXPECT_EQ(CEF_BUTTON_STATE_NORMAL, button->GetState());
+  button->SetState(CEF_BUTTON_STATE_HOVERED);
+  EXPECT_EQ(CEF_BUTTON_STATE_HOVERED, button->GetState());
+  button->SetState(CEF_BUTTON_STATE_PRESSED);
+  EXPECT_EQ(CEF_BUTTON_STATE_PRESSED, button->GetState());
+  button->SetState(CEF_BUTTON_STATE_DISABLED);
+  EXPECT_EQ(CEF_BUTTON_STATE_DISABLED, button->GetState());
+  button->SetState(CEF_BUTTON_STATE_NORMAL);
+
+  button->SetTooltipText("Some tooltip text");
+  button->SetAccessibleName("MyButton");
+}
+
+void VerifyLabelButtonImage(CefRefPtr<CefLabelButton> button,
+                            cef_button_state_t state,
+                            CefRefPtr<CefImage> image) {
+  EXPECT_FALSE(button->GetImage(state).get()) << "state = " << state;
+  button->SetImage(state, image);
+  EXPECT_TRUE(image->IsSame(button->GetImage(state))) << "state = " << state;
+  button->SetImage(state, nullptr);
+  EXPECT_FALSE(button->GetImage(state).get()) << "state = " << state;
+}
+
+void VerifyLabelButtonStyle(CefRefPtr<CefLabelButton> button) {
+  VerifyButtonStyle(button);
+
+  // Test set/get text.
+  EXPECT_STREQ(kButtonText, button->GetText().ToString().c_str());
+  const char kText[] = "My text";
+  button->SetText(kText);
+  EXPECT_STREQ(kText, button->GetText().ToString().c_str());
+
+  // Test images.
+  CefRefPtr<CefImage> image = CreateIconImage();
+  VerifyLabelButtonImage(button, CEF_BUTTON_STATE_NORMAL, image);
+  VerifyLabelButtonImage(button, CEF_BUTTON_STATE_HOVERED, image);
+  VerifyLabelButtonImage(button, CEF_BUTTON_STATE_PRESSED, image);
+  VerifyLabelButtonImage(button, CEF_BUTTON_STATE_DISABLED, image);
+
+  // Test colors.
+  const cef_color_t color = CefColorSetARGB(255, 255, 0, 255);
+  button->SetTextColor(CEF_BUTTON_STATE_NORMAL, color);
+  button->SetTextColor(CEF_BUTTON_STATE_HOVERED, color);
+  button->SetTextColor(CEF_BUTTON_STATE_PRESSED, color);
+  button->SetTextColor(CEF_BUTTON_STATE_DISABLED, color);
+  button->SetEnabledTextColors(color);
+
+  // Test alignment.
+  button->SetHorizontalAlignment(CEF_HORIZONTAL_ALIGNMENT_LEFT);
+  button->SetHorizontalAlignment(CEF_HORIZONTAL_ALIGNMENT_CENTER);
+  button->SetHorizontalAlignment(CEF_HORIZONTAL_ALIGNMENT_RIGHT);
+
+  // Test fonts.
+  button->SetFontList("Arial, 14px");
+
+  // Test sizes.
+  button->SetMinimumSize(CefSize(100, 100));
+  button->SetMaximumSize(CefSize(100, 100));
+}
+
+void VerifyMenuButtonStyle(CefRefPtr<CefMenuButton> button) {
+  VerifyLabelButtonStyle(button);
+}
+
+class EmptyMenuButtonDelegate : public CefMenuButtonDelegate {
+ public:
+  EmptyMenuButtonDelegate() {}
+
+  void OnMenuButtonPressed(
+      CefRefPtr<CefMenuButton> menu_button,
+      const CefPoint& screen_point,
+      CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) override {
+    EXPECT_TRUE(false);  // Not reached.
+  }
+
+  void OnButtonPressed(CefRefPtr<CefButton> button) override {
+    EXPECT_TRUE(false);  // Not reached.
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(EmptyMenuButtonDelegate);
+  DISALLOW_COPY_AND_ASSIGN(EmptyMenuButtonDelegate);
+};
+
+void LabelButtonStyle() {
+  CefRefPtr<CefLabelButton> button = CefLabelButton::CreateLabelButton(
+      new EmptyMenuButtonDelegate(), kButtonText);
+  VerifyLabelButtonStyle(button);
+}
+
+void LabelButtonStyleFramelessImpl() {
+  LabelButtonStyle();
+}
+
+void MenuButtonStyle() {
+  CefRefPtr<CefMenuButton> button = CefMenuButton::CreateMenuButton(
+      new EmptyMenuButtonDelegate(), kButtonText);
+  VerifyMenuButtonStyle(button);
+}
+
+void MenuButtonStyleFramelessImpl() {
+  MenuButtonStyle();
+}
+
+}  // namespace
+
+// Test Button getters/setters.
+BUTTON_TEST(LabelButtonStyleFrameless)
+BUTTON_TEST(MenuButtonStyleFrameless)
+
+namespace {
+
+// Mouse click delay in MS.
+const int kClickDelayMS = 100;
+
+const int kButtonID = 1;
+
+class TestButtonDelegate : public CefButtonDelegate {
+ public:
+  TestButtonDelegate() {}
+
+  void OnButtonPressed(CefRefPtr<CefButton> button) override {
+    EXPECT_TRUE(button.get());
+    EXPECT_EQ(button->GetID(), kButtonID);
+
+    // Complete the test by closing the window.
+    button->GetWindow()->Close();
+  }
+
+ private:
+  IMPLEMENT_REFCOUNTING(TestButtonDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestButtonDelegate);
+};
+
+void ClickButton(CefRefPtr<CefWindow> window, int button_id) {
+  CefRefPtr<CefView> button = window->GetViewForID(button_id);
+  EXPECT_TRUE(button->AsButton());
+
+  // Determine the middle of the button in screen coordinates.
+  const CefRect& bounds = button->GetBoundsInScreen();
+  const CefPoint& click_point =
+      CefPoint(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
+
+  // Click the button.
+  window->SendMouseMove(click_point.x, click_point.y);
+  window->SendMouseEvents(MBT_LEFT, true, true);
+}
+
+void AddImage(CefRefPtr<CefLabelButton> button) {
+  CefRefPtr<CefImage> image = CreateIconImage();
+  button->SetImage(CEF_BUTTON_STATE_NORMAL, image);
+}
+
+void RunLabelButtonClick(bool with_text,
+                         bool with_image,
+                         CefRefPtr<CefWindow> window) {
+  CefRefPtr<CefLabelButton> button = CefLabelButton::CreateLabelButton(
+      new TestButtonDelegate(), with_text ? kButtonText : "");
+  button->SetID(kButtonID);
+
+  EXPECT_TRUE(button->AsButton());
+  EXPECT_TRUE(button->AsButton()->AsLabelButton());
+  EXPECT_EQ(kButtonID, button->GetID());
+  EXPECT_TRUE(button->IsVisible());
+  EXPECT_FALSE(button->IsDrawn());
+
+  if (with_text)
+    EXPECT_STREQ(kButtonText, button->GetText().ToString().c_str());
+  else
+    EXPECT_TRUE(button->GetText().empty());
+
+  if (with_image)
+    AddImage(button);
+
+  window->AddChildView(button);
+  window->Layout();
+
+  EXPECT_TRUE(window->IsSame(button->GetWindow()));
+  EXPECT_TRUE(window->IsSame(button->GetParentView()));
+  EXPECT_TRUE(button->IsSame(window->GetViewForID(kButtonID)));
+  EXPECT_TRUE(button->IsVisible());
+  EXPECT_TRUE(button->IsDrawn());
+
+  window->Show();
+
+  // Wait a bit before trying to click the button.
+  CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID),
+                     kClickDelayMS);
+}
+
+void LabelButtonClick(CefRefPtr<CefWaitableEvent> event,
+                      bool with_button_frame,
+                      bool with_button_text,
+                      bool with_button_image) {
+  TestWindowDelegate::Config config;
+  config.on_window_created =
+      base::Bind(RunLabelButtonClick, with_button_text, with_button_image);
+  config.frameless = false;
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void LabelButtonClickFramedWithTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, true, true, true);
+}
+
+void LabelButtonClickFramedWithTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, true, true, false);
+}
+
+void LabelButtonClickFramedNoTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, true, false, true);
+}
+
+void LabelButtonClickFramedNoTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, true, false, false);
+}
+
+void LabelButtonClickFramelessWithTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, false, true, true);
+}
+
+void LabelButtonClickFramelessWithTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, false, true, false);
+}
+
+void LabelButtonClickFramelessNoTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, false, false, true);
+}
+
+void LabelButtonClickFramelessNoTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  LabelButtonClick(event, false, false, false);
+}
+
+}  // namespace
+
+// Test LabelButton functionality. This is primarily to exercise exposed CEF
+// APIs and is not intended to comprehensively test button-related behavior
+// (which we presume that Chromium is testing).
+BUTTON_TEST_ASYNC(LabelButtonClickFramedWithTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(LabelButtonClickFramedWithTextNoImageFramelessWindow)
+BUTTON_TEST_ASYNC(LabelButtonClickFramedNoTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(LabelButtonClickFramedNoTextNoImageFramelessWindow)
+BUTTON_TEST_ASYNC(LabelButtonClickFramelessWithTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(LabelButtonClickFramelessWithTextNoImageFramelessWindow)
+BUTTON_TEST_ASYNC(LabelButtonClickFramelessNoTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(LabelButtonClickFramelessNoTextNoImageFramelessWindow)
+
+namespace {
+
+const int kMenuItemID = 2;
+const char kMenuItemLabel[] = "My Menu Item";
+
+void ClickMenuItem(CefRefPtr<CefMenuButton> menu_button) {
+  // Determine the lower-right corner of the menu button, then offset a bit to
+  // hit the first menu item.
+  const CefRect& bounds = menu_button->GetBoundsInScreen();
+  const CefPoint& click_point =
+      CefPoint(bounds.x + bounds.width + 10, bounds.y + bounds.height + 10);
+
+  // Click the menu item.
+  CefRefPtr<CefWindow> window = menu_button->GetWindow();
+  window->SendMouseMove(click_point.x, click_point.y);
+  window->SendMouseEvents(MBT_LEFT, true, true);
+}
+
+class TestMenuButtonDelegate : public CefMenuButtonDelegate,
+                               public CefMenuModelDelegate {
+ public:
+  TestMenuButtonDelegate() {}
+
+  void OnMenuButtonPressed(
+      CefRefPtr<CefMenuButton> menu_button,
+      const CefPoint& screen_point,
+      CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) override {
+    window_ = menu_button->GetWindow();
+
+    CefRefPtr<CefMenuModel> model = CefMenuModel::CreateMenuModel(this);
+    model->AddItem(kMenuItemID, kMenuItemLabel);
+
+    // Verify color accessors.
+    for (int i = 0; i < CEF_MENU_COLOR_COUNT; ++i) {
+      cef_menu_color_type_t color_type = static_cast<cef_menu_color_type_t>(i);
+      cef_color_t color_out;
+      cef_color_t color = CefColorSetARGB(255, 255, 255, i);
+
+      // No color set yet.
+      color_out = 1;
+      EXPECT_TRUE(model->GetColor(kMenuItemID, color_type, color_out));
+      EXPECT_EQ(0U, color_out);
+      color_out = 1;
+      EXPECT_TRUE(model->GetColorAt(0, color_type, color_out));
+      EXPECT_EQ(0U, color_out);
+      color_out = 1;
+      EXPECT_TRUE(model->GetColorAt(-1, color_type, color_out));
+      EXPECT_EQ(0U, color_out);
+
+      // Set the default color.
+      EXPECT_TRUE(model->SetColorAt(-1, color_type, color));
+      color_out = 1;
+      EXPECT_TRUE(model->GetColorAt(-1, color_type, color_out));
+      EXPECT_EQ(color, color_out);
+
+      // Clear the default color.
+      EXPECT_TRUE(model->SetColorAt(-1, color_type, 0));
+      color_out = 1;
+      EXPECT_TRUE(model->GetColorAt(-1, color_type, color_out));
+      EXPECT_EQ(0U, color_out);
+
+      // Set the index color.
+      EXPECT_TRUE(model->SetColorAt(0, color_type, color));
+      color_out = 1;
+      EXPECT_TRUE(model->GetColorAt(0, color_type, color_out));
+      EXPECT_EQ(color, color_out);
+
+      // Clear the index color.
+      EXPECT_TRUE(model->SetColorAt(0, color_type, 0));
+      color_out = 1;
+      EXPECT_TRUE(model->GetColorAt(0, color_type, color_out));
+      EXPECT_EQ(0U, color_out);
+
+      // Set the ID color.
+      EXPECT_TRUE(model->SetColor(kMenuItemID, color_type, color));
+      color_out = 1;
+      EXPECT_TRUE(model->GetColor(kMenuItemID, color_type, color_out));
+      EXPECT_EQ(color, color_out);
+
+      // Clear the ID color.
+      EXPECT_TRUE(model->SetColor(kMenuItemID, color_type, 0));
+      color_out = 1;
+      EXPECT_TRUE(model->GetColor(kMenuItemID, color_type, color_out));
+      EXPECT_EQ(0U, color_out);
+
+      // Index/ID doesn't exist.
+      EXPECT_FALSE(model->SetColorAt(4, color_type, color));
+      EXPECT_FALSE(model->SetColor(4, color_type, color));
+      color_out = 1;
+      EXPECT_FALSE(model->GetColorAt(4, color_type, color_out));
+      EXPECT_FALSE(model->GetColor(4, color_type, color_out));
+      EXPECT_EQ(1U, color_out);
+    }
+
+    // Verify font accessors.
+    const std::string& font = "Tahoma, 12px";
+    EXPECT_TRUE(model->SetFontListAt(0, font));
+    EXPECT_TRUE(model->SetFontListAt(0, CefString()));
+    EXPECT_TRUE(model->SetFontList(kMenuItemID, font));
+    EXPECT_TRUE(model->SetFontList(kMenuItemID, CefString()));
+
+    // Index/ID doesn't exist.
+    EXPECT_FALSE(model->SetFontListAt(4, font));
+    EXPECT_FALSE(model->SetFontList(4, font));
+
+    // Wait a bit before trying to click the menu item.
+    CefPostDelayedTask(TID_UI, base::Bind(ClickMenuItem, menu_button),
+                       kClickDelayMS);
+
+    menu_button->ShowMenu(model, screen_point, CEF_MENU_ANCHOR_TOPLEFT);
+  }
+
+  void OnButtonPressed(CefRefPtr<CefButton> button) override {}
+
+  void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
+                      int command_id,
+                      cef_event_flags_t event_flags) override {
+    EXPECT_TRUE(menu_model.get());
+    EXPECT_EQ(command_id, kMenuItemID);
+
+    // Complete the test by closing the window.
+    window_->GetWindow()->Close();
+    window_ = nullptr;
+  }
+
+ private:
+  CefRefPtr<CefWindow> window_;
+
+  IMPLEMENT_REFCOUNTING(TestMenuButtonDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestMenuButtonDelegate);
+};
+
+void RunMenuButtonClick(bool with_text,
+                        bool with_image,
+                        CefRefPtr<CefWindow> window) {
+  CefRefPtr<CefMenuButton> button = CefMenuButton::CreateMenuButton(
+      new TestMenuButtonDelegate(), with_text ? kButtonText : "");
+  button->SetID(kButtonID);
+
+  EXPECT_TRUE(button->AsButton());
+  EXPECT_TRUE(button->AsButton()->AsLabelButton());
+  EXPECT_TRUE(button->AsButton()->AsLabelButton()->AsMenuButton());
+  EXPECT_EQ(kButtonID, button->GetID());
+  EXPECT_TRUE(button->IsVisible());
+  EXPECT_FALSE(button->IsDrawn());
+
+  if (with_text)
+    EXPECT_STREQ(kButtonText, button->GetText().ToString().c_str());
+  else
+    EXPECT_TRUE(button->GetText().empty());
+
+  if (with_image)
+    AddImage(button);
+
+  window->AddChildView(button);
+  window->Layout();
+
+  EXPECT_TRUE(window->IsSame(button->GetWindow()));
+  EXPECT_TRUE(window->IsSame(button->GetParentView()));
+  EXPECT_TRUE(button->IsSame(window->GetViewForID(kButtonID)));
+  EXPECT_TRUE(button->IsVisible());
+  EXPECT_TRUE(button->IsDrawn());
+
+  window->Show();
+
+  // Wait a bit before trying to click the button.
+  CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID),
+                     kClickDelayMS);
+}
+
+void MenuButtonClick(CefRefPtr<CefWaitableEvent> event,
+                     bool with_button_frame,
+                     bool with_button_text,
+                     bool with_button_image) {
+  TestWindowDelegate::Config config;
+  config.on_window_created =
+      base::Bind(RunMenuButtonClick, with_button_text, with_button_image);
+  config.frameless = false;
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void MenuButtonClickFramedWithTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, true, true, true);
+}
+
+void MenuButtonClickFramedWithTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, true, true, false);
+}
+
+void MenuButtonClickFramedNoTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, true, false, true);
+}
+
+void MenuButtonClickFramedNoTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, true, false, false);
+}
+
+void MenuButtonClickFramelessWithTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, false, true, true);
+}
+
+void MenuButtonClickFramelessWithTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, false, true, false);
+}
+
+void MenuButtonClickFramelessNoTextWithImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, false, false, true);
+}
+
+void MenuButtonClickFramelessNoTextNoImageFramelessWindowImpl(
+    CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonClick(event, false, false, false);
+}
+
+}  // namespace
+
+// Test MenuButton functionality. This is primarily to exercise exposed CEF
+// APIs and is not intended to comprehensively test button-related behavior
+// (which we presume that Chromium is testing).
+BUTTON_TEST_ASYNC(MenuButtonClickFramedWithTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(MenuButtonClickFramedWithTextNoImageFramelessWindow)
+BUTTON_TEST_ASYNC(MenuButtonClickFramedNoTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(MenuButtonClickFramedNoTextNoImageFramelessWindow)
+BUTTON_TEST_ASYNC(MenuButtonClickFramelessWithTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(MenuButtonClickFramelessWithTextNoImageFramelessWindow)
+BUTTON_TEST_ASYNC(MenuButtonClickFramelessNoTextWithImageFramelessWindow)
+BUTTON_TEST_ASYNC(MenuButtonClickFramelessNoTextNoImageFramelessWindow)
+
+namespace {
+
+class TestMenuButtonCustomPopupDelegate : public CefMenuButtonDelegate,
+                                          public CefWindowDelegate {
+ public:
+  explicit TestMenuButtonCustomPopupDelegate(bool can_activate)
+      : can_activate_(can_activate) {}
+
+  void OnMenuButtonPressed(
+      CefRefPtr<CefMenuButton> menu_button,
+      const CefPoint& screen_point,
+      CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) override {
+    parent_window_ = menu_button->GetWindow();
+    button_pressed_lock_ = button_pressed_lock;
+
+    popup_window_ = CefWindow::CreateTopLevelWindow(this);
+    popup_window_->SetBounds(CefRect(screen_point.x, screen_point.y, 100, 100));
+
+    CefRefPtr<CefLabelButton> button =
+        CefLabelButton::CreateLabelButton(this, "Button");
+    button->SetFocusable(can_activate_);
+    popup_window_->AddChildView(button);
+
+    popup_window_->Show();
+
+    // Wait a bit before trying to click the popup button.
+    CefPostDelayedTask(TID_UI, base::Bind(ClickMenuItem, menu_button),
+                       kClickDelayMS);
+  }
+
+  void OnButtonPressed(CefRefPtr<CefButton> button) override {
+    EXPECT_TRUE(button->GetWindow()->IsSame(popup_window_));
+    popup_window_->Close();
+    popup_window_ = nullptr;
+    button_pressed_lock_ = nullptr;
+  }
+
+  CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
+                                       bool* is_menu,
+                                       bool* can_activate_menu) override {
+    EXPECT_TRUE(parent_window_);
+    *is_menu = true;
+    *can_activate_menu = can_activate_;
+    return parent_window_;
+  }
+
+  bool IsFrameless(CefRefPtr<CefWindow> window) override { return true; }
+
+  void OnFocus(CefRefPtr<CefView> view) override {
+    if (popup_window_ && view->GetWindow()->IsSame(popup_window_)) {
+      EXPECT_TRUE(can_activate_);
+      got_focus_.yes();
+    }
+  }
+
+  void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
+    if (can_activate_)
+      EXPECT_TRUE(got_focus_);
+    else
+      EXPECT_FALSE(got_focus_);
+
+    // Complete the test by closing the parent window.
+    parent_window_->Close();
+    parent_window_ = nullptr;
+  }
+
+ private:
+  const bool can_activate_;
+
+  CefRefPtr<CefWindow> parent_window_;
+  CefRefPtr<CefWindow> popup_window_;
+  CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock_;
+
+  TrackCallback got_focus_;
+
+  IMPLEMENT_REFCOUNTING(TestMenuButtonCustomPopupDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestMenuButtonCustomPopupDelegate);
+};
+
+void RunMenuButtonCustomPopupClick(bool can_activate,
+                                   CefRefPtr<CefWindow> window) {
+  CefRefPtr<CefMenuButton> button = CefMenuButton::CreateMenuButton(
+      new TestMenuButtonCustomPopupDelegate(can_activate), "Custom");
+  button->SetID(kButtonID);
+
+  window->AddChildView(button);
+  window->Layout();
+
+  window->Show();
+
+  // Wait a bit before trying to click the button.
+  CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID),
+                     kClickDelayMS);
+}
+
+void MenuButtonCustomPopupClick(CefRefPtr<CefWaitableEvent> event,
+                                bool can_activate) {
+  TestWindowDelegate::Config config;
+  config.on_window_created =
+      base::Bind(RunMenuButtonCustomPopupClick, can_activate);
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void MenuButtonCustomPopupActivateImpl(CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonCustomPopupClick(event, true);
+}
+
+void MenuButtonCustomPopupNoActivateImpl(CefRefPtr<CefWaitableEvent> event) {
+  MenuButtonCustomPopupClick(event, false);
+}
+
+}  // namespace
+
+BUTTON_TEST_ASYNC(MenuButtonCustomPopupActivate)
+BUTTON_TEST_ASYNC(MenuButtonCustomPopupNoActivate)
diff --git a/src/tests/ceftests/views/panel_unittest.cc b/src/tests/ceftests/views/panel_unittest.cc
new file mode 100644
index 0000000..5cd1954
--- /dev/null
+++ b/src/tests/ceftests/views/panel_unittest.cc
@@ -0,0 +1,1387 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_fill_layout.h"
+#include "include/views/cef_layout.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_panel_delegate.h"
+#include "include/views/cef_window.h"
+#include "tests/ceftests/thread_helper.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+#define PANEL_TEST(name) UI_THREAD_TEST(ViewsPanelTest, name)
+
+namespace {
+
+class EmptyPanelDelegate : public CefPanelDelegate {
+ public:
+  EmptyPanelDelegate() {}
+
+ private:
+  IMPLEMENT_REFCOUNTING(EmptyPanelDelegate);
+  DISALLOW_COPY_AND_ASSIGN(EmptyPanelDelegate);
+};
+
+void CreatePanel(CefRefPtr<CefPanelDelegate> delegate) {
+  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);
+  EXPECT_TRUE(panel.get());
+
+  // Verify the derived View relationship.
+  EXPECT_TRUE(panel->AsPanel().get());
+  EXPECT_FALSE(panel->AsWindow().get());
+  EXPECT_TRUE(panel->IsSame(panel));
+
+  // Verify default View state.
+  EXPECT_STREQ("Panel", panel->GetTypeString().ToString().c_str());
+  EXPECT_TRUE(panel->IsValid());
+  EXPECT_FALSE(panel->IsAttached());
+  if (delegate)
+    EXPECT_EQ(delegate.get(), panel->GetDelegate().get());
+  else
+    EXPECT_FALSE(panel->GetDelegate().get());
+  EXPECT_EQ(0, panel->GetID());
+  EXPECT_FALSE(panel->GetParentView().get());
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBoundsInScreen());
+  EXPECT_EQ(CefSize(0, 0), panel->GetPreferredSize());
+  EXPECT_EQ(CefSize(0, 0), panel->GetMinimumSize());
+  EXPECT_EQ(CefSize(0, 0), panel->GetMaximumSize());
+  EXPECT_EQ(CefSize(0, 0), panel->GetMaximumSize());
+  EXPECT_EQ(0, panel->GetHeightForWidth(100));
+  EXPECT_TRUE(panel->IsVisible());
+  EXPECT_FALSE(panel->IsDrawn());
+  EXPECT_TRUE(panel->IsEnabled());
+  EXPECT_FALSE(panel->IsFocusable());
+  EXPECT_FALSE(panel->IsAccessibilityFocusable());
+  EXPECT_EQ(CefColorSetARGB(255, 255, 255, 255), panel->GetBackgroundColor());
+
+  // Verify default Panel state.
+  EXPECT_TRUE(panel->GetLayout().get());
+  EXPECT_EQ(0U, panel->GetChildViewCount());
+
+  // Destroy the Panel.
+  panel = nullptr;
+
+  if (delegate) {
+    // Verify that nothing is keeping a reference to the delegate.
+    EXPECT_TRUE(delegate->HasOneRef());
+  }
+}
+
+void CreatePanelNoDelegateImpl() {
+  CreatePanel(nullptr);
+}
+
+void CreatePanelWithDelegateImpl() {
+  CreatePanel(new EmptyPanelDelegate);
+}
+
+}  // namespace
+
+// Test creation.
+PANEL_TEST(CreatePanelNoDelegate)
+PANEL_TEST(CreatePanelWithDelegate)
+
+namespace {
+
+class ParentPanelDelegate : public CefPanelDelegate {
+ public:
+  ParentPanelDelegate() {}
+
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override {
+    EXPECT_FALSE(true);  // Not reached.
+  }
+
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override {
+    Changed changed;
+    changed.view_ = view;
+    changed.added_ = added;
+    changed.child_ = child;
+    changed_.push_back(changed);
+  }
+
+  void Verify(int callback_index,
+              CefRefPtr<CefView> view,
+              bool added,
+              CefRefPtr<CefView> child) {
+    EXPECT_LT(callback_index, static_cast<int>(changed_.size()));
+    EXPECT_TRUE(view->IsSame(changed_[callback_index].view_))
+        << "callback_index " << callback_index;
+    EXPECT_EQ(added, changed_[callback_index].added_)
+        << "callback_index " << callback_index;
+    EXPECT_TRUE(child->IsSame(changed_[callback_index].child_))
+        << "callback_index " << callback_index;
+  }
+
+  void Reset() { changed_.clear(); }
+
+  bool IsReset() const { return changed_.empty(); }
+
+  struct Changed {
+    CefRefPtr<CefView> view_;
+    bool added_ = false;
+    CefRefPtr<CefView> child_;
+  };
+  std::vector<Changed> changed_;
+
+ private:
+  IMPLEMENT_REFCOUNTING(ParentPanelDelegate);
+  DISALLOW_COPY_AND_ASSIGN(ParentPanelDelegate);
+};
+
+class ChildPanelDelegate : public CefPanelDelegate {
+ public:
+  ChildPanelDelegate() {}
+
+  void OnParentViewChanged(CefRefPtr<CefView> view,
+                           bool added,
+                           CefRefPtr<CefView> parent) override {
+    EXPECT_FALSE(on_parent_view_changed_);
+    on_parent_view_changed_ = true;
+    view_ = view;
+    added_ = added;
+    parent_ = parent;
+  }
+
+  void OnChildViewChanged(CefRefPtr<CefView> view,
+                          bool added,
+                          CefRefPtr<CefView> child) override {
+    EXPECT_FALSE(true);  // Not reached.
+  }
+
+  void Verify(CefRefPtr<CefView> view, bool added, CefRefPtr<CefView> parent) {
+    EXPECT_TRUE(on_parent_view_changed_);
+    EXPECT_TRUE(view->IsSame(view_));
+    EXPECT_EQ(added, added_);
+    EXPECT_TRUE(parent->IsSame(parent_));
+  }
+
+  void Reset() {
+    on_parent_view_changed_ = false;
+    view_ = nullptr;
+    added_ = false;
+    parent_ = nullptr;
+  }
+
+  bool IsReset() const { return !on_parent_view_changed_; }
+
+  bool on_parent_view_changed_ = false;
+  CefRefPtr<CefView> view_;
+  bool added_ = false;
+  CefRefPtr<CefView> parent_;
+
+ private:
+  IMPLEMENT_REFCOUNTING(ChildPanelDelegate);
+  DISALLOW_COPY_AND_ASSIGN(ChildPanelDelegate);
+};
+
+void ChildVerifyRemovedState(CefRefPtr<ParentPanelDelegate> parent_delegate,
+                             CefRefPtr<CefPanel> parent_panel,
+                             CefRefPtr<ChildPanelDelegate> child_delegate,
+                             CefRefPtr<CefPanel> child_panel) {
+  EXPECT_FALSE(parent_panel->IsSame(child_panel));
+  EXPECT_FALSE(child_panel->IsSame(parent_panel));
+  EXPECT_FALSE(parent_panel->IsAttached());
+  EXPECT_FALSE(child_panel->IsAttached());
+  EXPECT_FALSE(parent_panel->GetParentView().get());
+  EXPECT_FALSE(child_panel->GetParentView().get());
+}
+
+void ChildVerifyAddedState(CefRefPtr<ParentPanelDelegate> parent_delegate,
+                           CefRefPtr<CefPanel> parent_panel,
+                           CefRefPtr<ChildPanelDelegate> child_delegate,
+                           CefRefPtr<CefPanel> child_panel,
+                           int expected_child_index) {
+  EXPECT_FALSE(parent_panel->IsSame(child_panel));
+  EXPECT_FALSE(child_panel->IsSame(parent_panel));
+  EXPECT_FALSE(parent_panel->IsAttached());
+  EXPECT_TRUE(child_panel->IsAttached());
+  EXPECT_TRUE(
+      child_panel->IsSame(parent_panel->GetChildViewAt(expected_child_index)));
+  EXPECT_TRUE(child_panel->GetParentView()->IsSame(parent_panel));
+}
+
+void ChildVerifyFinalCallbackState(
+    CefRefPtr<ParentPanelDelegate> parent_delegate,
+    CefRefPtr<CefPanel> parent_panel,
+    CefRefPtr<ChildPanelDelegate> child_delegate,
+    CefRefPtr<CefPanel> child_panel,
+    int expected_parent_callback_index,
+    bool added) {
+  parent_delegate->Verify(expected_parent_callback_index, parent_panel, added,
+                          child_panel);
+  child_delegate->Verify(child_panel, added, parent_panel);
+}
+
+void ChildAdd(CefRefPtr<ParentPanelDelegate> parent_delegate,
+              CefRefPtr<CefPanel> parent_panel,
+              CefRefPtr<ChildPanelDelegate> child_delegate,
+              CefRefPtr<CefPanel> child_panel,
+              int expected_child_index = 0,
+              int expected_parent_callback_index = 0) {
+  // Verify initial parent/child state.
+  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate,
+                          child_panel);
+
+  // Verify initial child callback state.
+  EXPECT_TRUE(child_delegate->IsReset());
+
+  // Add the child view.
+  parent_panel->AddChildView(child_panel);
+
+  // Verify final callback state.
+  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate,
+                                child_panel, expected_parent_callback_index,
+                                true);
+
+  // Reset child callback state.
+  child_delegate->Reset();
+
+  // Verify final parent/child state.
+  ChildVerifyAddedState(parent_delegate, parent_panel, child_delegate,
+                        child_panel, expected_child_index);
+}
+
+void ChildAddAt(CefRefPtr<ParentPanelDelegate> parent_delegate,
+                CefRefPtr<CefPanel> parent_panel,
+                CefRefPtr<ChildPanelDelegate> child_delegate,
+                CefRefPtr<CefPanel> child_panel,
+                int child_index,
+                int expected_parent_callback_index) {
+  // Verify initial parent/child state.
+  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate,
+                          child_panel);
+
+  // Verify initial child callback state.
+  EXPECT_TRUE(child_delegate->IsReset());
+
+  // Add the child view.
+  parent_panel->AddChildViewAt(child_panel, child_index);
+
+  // Verify final callback state.
+  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate,
+                                child_panel, expected_parent_callback_index,
+                                true);
+
+  // Reset child callback state.
+  child_delegate->Reset();
+
+  // Verify final parent/child state.
+  ChildVerifyAddedState(parent_delegate, parent_panel, child_delegate,
+                        child_panel, child_index);
+}
+
+void ChildRemove(CefRefPtr<ParentPanelDelegate> parent_delegate,
+                 CefRefPtr<CefPanel> parent_panel,
+                 CefRefPtr<ChildPanelDelegate> child_delegate,
+                 CefRefPtr<CefPanel> child_panel,
+                 bool remove_all,
+                 int expected_child_index = 0,
+                 int expected_parent_callback_index = 0) {
+  // Verify initial parent/child state.
+  ChildVerifyAddedState(parent_delegate, parent_panel, child_delegate,
+                        child_panel, expected_child_index);
+
+  // Verify initial child callback state.
+  EXPECT_TRUE(child_delegate->IsReset());
+
+  // Remove the child view.
+  if (remove_all)
+    parent_panel->RemoveAllChildViews();
+  else
+    parent_panel->RemoveChildView(child_panel);
+
+  // Verify final callback state.
+  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate,
+                                child_panel, expected_parent_callback_index,
+                                false);
+
+  // Reset child callback state.
+  child_delegate->Reset();
+
+  // Verify final parent/child state.
+  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate,
+                          child_panel);
+}
+
+void ChildAddRemoveSingleImpl() {
+  CefRefPtr<ParentPanelDelegate> parent_delegate = new ParentPanelDelegate();
+  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(parent_delegate);
+
+  CefRefPtr<ChildPanelDelegate> child_delegate = new ChildPanelDelegate();
+  CefRefPtr<CefPanel> child_panel = CefPanel::CreatePanel(child_delegate);
+
+  // Add and explicitly remove the child view.
+  EXPECT_TRUE(parent_delegate->IsReset());
+  ChildAdd(parent_delegate, parent_panel, child_delegate, child_panel);
+  parent_delegate->Reset();
+
+  ChildRemove(parent_delegate, parent_panel, child_delegate, child_panel,
+              false);
+  parent_delegate->Reset();
+
+  // Add and implicitly remove the child view.
+  ChildAdd(parent_delegate, parent_panel, child_delegate, child_panel);
+  parent_delegate->Reset();
+
+  ChildRemove(parent_delegate, parent_panel, child_delegate, child_panel,
+              false);
+  parent_delegate->Reset();
+}
+
+void ChildAddRemoveMultipleImpl() {
+  CefRefPtr<ParentPanelDelegate> parent_delegate = new ParentPanelDelegate();
+  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(parent_delegate);
+
+  CefRefPtr<ChildPanelDelegate> child_delegate1 = new ChildPanelDelegate();
+  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(child_delegate1);
+
+  CefRefPtr<ChildPanelDelegate> child_delegate2 = new ChildPanelDelegate();
+  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(child_delegate2);
+
+  // Add multiple child views.
+  EXPECT_TRUE(parent_delegate->IsReset());
+  ChildAdd(parent_delegate, parent_panel, child_delegate1, child_panel1, 0, 0);
+  EXPECT_TRUE(child_delegate2->IsReset());  // child2 not called.
+  ChildAdd(parent_delegate, parent_panel, child_delegate2, child_panel2, 1, 1);
+  EXPECT_TRUE(child_delegate1->IsReset());  // child1 not called.
+  parent_delegate->Reset();
+
+  EXPECT_EQ(2U, parent_panel->GetChildViewCount());
+
+  // Explicitly remove specific child views.
+  ChildRemove(parent_delegate, parent_panel, child_delegate1, child_panel1,
+              false, 0, 0);
+  EXPECT_TRUE(child_delegate2->IsReset());  // child2 not called.
+  ChildRemove(parent_delegate, parent_panel, child_delegate2, child_panel2,
+              false, 0, 1);
+  EXPECT_TRUE(child_delegate1->IsReset());  // child1 not called.
+  parent_delegate->Reset();
+
+  EXPECT_EQ(0U, parent_panel->GetChildViewCount());
+
+  // Add multiple child views.
+  ChildAdd(parent_delegate, parent_panel, child_delegate1, child_panel1, 0, 0);
+  EXPECT_TRUE(child_delegate2->IsReset());  // child2 not called.
+  ChildAdd(parent_delegate, parent_panel, child_delegate2, child_panel2, 1, 1);
+  EXPECT_TRUE(child_delegate1->IsReset());  // child1 not called.
+  parent_delegate->Reset();
+
+  EXPECT_EQ(2U, parent_panel->GetChildViewCount());
+
+  EXPECT_TRUE(child_delegate1->IsReset());
+  EXPECT_TRUE(child_delegate2->IsReset());
+
+  // Implicitly remove all child views.
+  parent_panel->RemoveAllChildViews();
+
+  // Verify final callback state.
+  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate1,
+                                child_panel1, 0, false);
+  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate2,
+                                child_panel2, 1, false);
+
+  EXPECT_EQ(0U, parent_panel->GetChildViewCount());
+
+  // Reset callback state.
+  parent_delegate->Reset();
+  child_delegate1->Reset();
+  child_delegate2->Reset();
+
+  // Verify final parent/child state.
+  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate1,
+                          child_panel1);
+  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate2,
+                          child_panel2);
+}
+
+void ChildOrderImpl() {
+  CefRefPtr<ParentPanelDelegate> parent_delegate = new ParentPanelDelegate();
+  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(parent_delegate);
+
+  CefRefPtr<ChildPanelDelegate> child_delegate1 = new ChildPanelDelegate();
+  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(child_delegate1);
+
+  CefRefPtr<ChildPanelDelegate> child_delegate2 = new ChildPanelDelegate();
+  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(child_delegate2);
+
+  CefRefPtr<ChildPanelDelegate> child_delegate3 = new ChildPanelDelegate();
+  CefRefPtr<CefPanel> child_panel3 = CefPanel::CreatePanel(child_delegate3);
+
+  // Add child views at specific indexes.
+  ChildAddAt(parent_delegate, parent_panel, child_delegate2, child_panel2, 0,
+             0);
+  ChildAddAt(parent_delegate, parent_panel, child_delegate3, child_panel3, 0,
+             1);
+  ChildAddAt(parent_delegate, parent_panel, child_delegate1, child_panel1, 1,
+             2);
+  parent_delegate->Reset();
+
+  EXPECT_EQ(3U, parent_panel->GetChildViewCount());
+
+  // ChildAddAt() will verify these results but let's check again just to make
+  // sure.
+  EXPECT_TRUE(child_panel3->IsSame(parent_panel->GetChildViewAt(0)));
+  EXPECT_TRUE(child_panel1->IsSame(parent_panel->GetChildViewAt(1)));
+  EXPECT_TRUE(child_panel2->IsSame(parent_panel->GetChildViewAt(2)));
+
+  // Move panel2 to the front.
+  parent_panel->ReorderChildView(child_panel2, 0);
+
+  EXPECT_TRUE(child_panel2->IsSame(parent_panel->GetChildViewAt(0)));
+  EXPECT_TRUE(child_panel3->IsSame(parent_panel->GetChildViewAt(1)));
+  EXPECT_TRUE(child_panel1->IsSame(parent_panel->GetChildViewAt(2)));
+
+  // Move panel3 to the end.
+  parent_panel->ReorderChildView(child_panel3, -1);
+
+  EXPECT_TRUE(child_panel2->IsSame(parent_panel->GetChildViewAt(0)));
+  EXPECT_TRUE(child_panel1->IsSame(parent_panel->GetChildViewAt(1)));
+  EXPECT_TRUE(child_panel3->IsSame(parent_panel->GetChildViewAt(2)));
+}
+
+void ChildVisibleImpl() {
+  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(nullptr);
+  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(nullptr);
+  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(nullptr);
+
+  // Nothing drawn by default.
+  EXPECT_FALSE(parent_panel->IsDrawn());
+  EXPECT_FALSE(child_panel1->IsDrawn());
+  EXPECT_FALSE(child_panel2->IsDrawn());
+
+  // Everything visible by default.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel2->IsVisible());
+
+  parent_panel->AddChildView(child_panel1);
+  parent_panel->AddChildView(child_panel2);
+
+  // Still the same.
+  EXPECT_FALSE(parent_panel->IsDrawn());
+  EXPECT_FALSE(child_panel1->IsDrawn());
+  EXPECT_FALSE(child_panel2->IsDrawn());
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel2->IsVisible());
+
+  child_panel1->SetVisible(false);
+
+  // Child1 not visible.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_FALSE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel2->IsVisible());
+
+  child_panel1->SetVisible(true);
+
+  // Everything visible.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel2->IsVisible());
+
+  parent_panel->SetVisible(false);
+
+  // Children visible.
+  EXPECT_FALSE(parent_panel->IsVisible());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel2->IsVisible());
+
+  parent_panel->SetVisible(true);
+
+  // Everything visible.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel2->IsVisible());
+}
+
+void ChildDrawnImpl() {
+  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(nullptr);
+  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(nullptr);
+  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(nullptr);
+
+  // Nothing drawn by default.
+  EXPECT_FALSE(parent_panel->IsDrawn());
+  EXPECT_FALSE(child_panel1->IsDrawn());
+  EXPECT_FALSE(child_panel2->IsDrawn());
+
+  // Everything visible by default.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel2->IsVisible());
+
+  parent_panel->AddChildView(child_panel1);
+  parent_panel->AddChildView(child_panel2);
+
+  // Create and show a Window.
+  CefRefPtr<CefWindow> window = CefWindow::CreateTopLevelWindow(nullptr);
+  window->AddChildView(parent_panel);
+  window->CenterWindow(CefSize(400, 400));
+  window->Show();
+
+  // Everything visible and drawn now.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(parent_panel->IsDrawn());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel1->IsDrawn());
+  EXPECT_TRUE(child_panel2->IsVisible());
+  EXPECT_TRUE(child_panel2->IsDrawn());
+
+  child_panel1->SetVisible(false);
+
+  // Child1 not visible or drawn.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(parent_panel->IsDrawn());
+  EXPECT_FALSE(child_panel1->IsVisible());
+  EXPECT_FALSE(child_panel1->IsDrawn());
+  EXPECT_TRUE(child_panel2->IsVisible());
+  EXPECT_TRUE(child_panel2->IsDrawn());
+
+  child_panel1->SetVisible(true);
+
+  // Everything visible and drawn.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(parent_panel->IsDrawn());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel1->IsDrawn());
+  EXPECT_TRUE(child_panel2->IsVisible());
+  EXPECT_TRUE(child_panel2->IsDrawn());
+
+  parent_panel->SetVisible(false);
+
+  // Children visible, but nothing drawn.
+  EXPECT_FALSE(parent_panel->IsVisible());
+  EXPECT_FALSE(parent_panel->IsDrawn());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_FALSE(child_panel1->IsDrawn());
+  EXPECT_TRUE(child_panel2->IsVisible());
+  EXPECT_FALSE(child_panel2->IsDrawn());
+
+  parent_panel->SetVisible(true);
+
+  // Everything visible and drawn.
+  EXPECT_TRUE(parent_panel->IsVisible());
+  EXPECT_TRUE(parent_panel->IsDrawn());
+  EXPECT_TRUE(child_panel1->IsVisible());
+  EXPECT_TRUE(child_panel1->IsDrawn());
+  EXPECT_TRUE(child_panel2->IsVisible());
+  EXPECT_TRUE(child_panel2->IsDrawn());
+
+  // Close the window.
+  window->Close();
+}
+
+}  // namespace
+
+// Test child behaviors.
+PANEL_TEST(ChildAddRemoveSingle)
+PANEL_TEST(ChildAddRemoveMultiple)
+PANEL_TEST(ChildOrder)
+PANEL_TEST(ChildVisible)
+PANEL_TEST(ChildDrawn)
+
+namespace {
+
+class SizingPanelDelegate : public CefPanelDelegate {
+ public:
+  SizingPanelDelegate() {}
+
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override {
+    got_get_preferred_size_ = true;
+    view_ = view;
+    return preferred_size_;
+  }
+
+  CefSize GetMinimumSize(CefRefPtr<CefView> view) override {
+    got_get_minimum_size_ = true;
+    view_ = view;
+    return minimum_size_;
+  }
+
+  CefSize GetMaximumSize(CefRefPtr<CefView> view) override {
+    got_get_maximum_size_ = true;
+    view_ = view;
+    return maximum_size_;
+  }
+
+  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override {
+    got_get_height_for_width_ = true;
+    view_ = view;
+    width_ = width;
+    return height_for_width_;
+  }
+
+  void Reset() {
+    preferred_size_ = CefSize(0, 0);
+    minimum_size_ = CefSize(0, 0);
+    maximum_size_ = CefSize(0, 0);
+    height_for_width_ = 0;
+    got_get_preferred_size_ = false;
+    got_get_minimum_size_ = false;
+    got_get_maximum_size_ = false;
+    got_get_height_for_width_ = false;
+    view_ = nullptr;
+    width_ = 0;
+  }
+
+  bool IsReset() const {
+    return !got_get_preferred_size_ && !got_get_minimum_size_ &&
+           !got_get_maximum_size_ && !got_get_height_for_width_;
+  }
+
+  CefSize preferred_size_;
+  CefSize minimum_size_;
+  CefSize maximum_size_;
+  int height_for_width_ = 0;
+
+  bool got_get_preferred_size_ = false;
+  bool got_get_minimum_size_ = false;
+  bool got_get_maximum_size_ = false;
+  bool got_get_height_for_width_ = false;
+
+  CefRefPtr<CefView> view_;
+  int width_ = 0;
+
+ private:
+  IMPLEMENT_REFCOUNTING(SizingPanelDelegate);
+  DISALLOW_COPY_AND_ASSIGN(SizingPanelDelegate);
+};
+
+void SizeNoDelegateImpl() {
+  CefRefPtr<SizingPanelDelegate> delegate = new SizingPanelDelegate();
+  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);
+
+  // Default bounds are empty.
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());
+
+  // Set and get the bounds.
+  panel->SetBounds(CefRect(100, 100, 200, 200));
+  EXPECT_EQ(CefRect(100, 100, 200, 200), panel->GetBounds());
+  EXPECT_EQ(CefSize(200, 200), panel->GetSize());
+  EXPECT_EQ(CefPoint(100, 100), panel->GetPosition());
+
+  // GetBoundsInScreen() drops the position because there is no Window.
+  EXPECT_EQ(CefRect(0, 0, 200, 200), panel->GetBoundsInScreen());
+
+  // Adjust the position but keep the size the same.
+  panel->SetPosition(CefPoint(50, 50));
+  EXPECT_EQ(CefRect(50, 50, 200, 200), panel->GetBounds());
+  EXPECT_EQ(CefSize(200, 200), panel->GetSize());
+  EXPECT_EQ(CefPoint(50, 50), panel->GetPosition());
+
+  // Adjust the size but keep the position the same.
+  panel->SetSize(CefSize(400, 400));
+  EXPECT_EQ(CefRect(50, 50, 400, 400), panel->GetBounds());
+  EXPECT_EQ(CefSize(400, 400), panel->GetSize());
+  EXPECT_EQ(CefPoint(50, 50), panel->GetPosition());
+
+  // No delegate methods were called during this test.
+  EXPECT_TRUE(delegate->IsReset());
+}
+
+void SizeWithDelegateImpl() {
+  CefRefPtr<SizingPanelDelegate> delegate = new SizingPanelDelegate();
+  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);
+
+  // Default bounds are empty.
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());
+
+  CefSize expected_size(100, 100);
+
+  // Test GetPreferredSize().
+  delegate->preferred_size_ = expected_size;
+  EXPECT_EQ(expected_size, panel->GetPreferredSize());
+  EXPECT_TRUE(delegate->got_get_preferred_size_);
+  EXPECT_FALSE(delegate->got_get_minimum_size_);
+  EXPECT_FALSE(delegate->got_get_maximum_size_);
+  EXPECT_FALSE(delegate->got_get_height_for_width_);
+  EXPECT_TRUE(panel->IsSame(delegate->view_));
+  delegate->Reset();
+
+  // Test GetMinimumSize().
+  delegate->minimum_size_ = expected_size;
+  EXPECT_EQ(expected_size, panel->GetMinimumSize());
+  EXPECT_FALSE(delegate->got_get_preferred_size_);
+  EXPECT_TRUE(delegate->got_get_minimum_size_);
+  EXPECT_FALSE(delegate->got_get_maximum_size_);
+  EXPECT_FALSE(delegate->got_get_height_for_width_);
+  EXPECT_TRUE(panel->IsSame(delegate->view_));
+  delegate->Reset();
+
+  // Test GetMaximumSize().
+  delegate->maximum_size_ = expected_size;
+  EXPECT_EQ(expected_size, panel->GetMaximumSize());
+  EXPECT_FALSE(delegate->got_get_preferred_size_);
+  EXPECT_FALSE(delegate->got_get_minimum_size_);
+  EXPECT_TRUE(delegate->got_get_maximum_size_);
+  EXPECT_FALSE(delegate->got_get_height_for_width_);
+  EXPECT_TRUE(panel->IsSame(delegate->view_));
+  delegate->Reset();
+
+  int expected_width = 200;
+  int expected_height = 100;
+
+  // Test GetHeightForWidth().
+  delegate->height_for_width_ = expected_height;
+  EXPECT_EQ(expected_height, panel->GetHeightForWidth(expected_width));
+  EXPECT_FALSE(delegate->got_get_preferred_size_);
+  EXPECT_FALSE(delegate->got_get_minimum_size_);
+  EXPECT_FALSE(delegate->got_get_maximum_size_);
+  EXPECT_TRUE(delegate->got_get_height_for_width_);
+  EXPECT_EQ(expected_width, delegate->width_);
+  EXPECT_TRUE(panel->IsSame(delegate->view_));
+  delegate->Reset();
+}
+
+}  // namespace
+
+// Test sizing.
+PANEL_TEST(SizeNoDelegate)
+PANEL_TEST(SizeWithDelegate)
+
+namespace {
+
+void FillLayoutCreateImpl() {
+  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(nullptr);
+
+  // Explicitly set to FillLayout.
+  panel->SetToFillLayout();
+
+  CefRefPtr<CefLayout> layout = panel->GetLayout();
+  EXPECT_TRUE(layout.get());
+  EXPECT_TRUE(layout->AsFillLayout().get());
+}
+
+void FillLayoutSizeToPreferredSizeImpl() {
+  CefRefPtr<SizingPanelDelegate> delegate = new SizingPanelDelegate();
+  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);
+
+  // Default Layout is FillLayout.
+  CefRefPtr<CefLayout> layout = panel->GetLayout();
+  EXPECT_TRUE(layout.get());
+  EXPECT_TRUE(layout->AsFillLayout().get());
+
+  // Default bounds are empty.
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());
+
+  CefSize expected_size(100, 100);
+
+  delegate->preferred_size_ = expected_size;
+
+  // Trigger use of the preferred size.
+  panel->Layout();
+
+  EXPECT_TRUE(delegate->got_get_preferred_size_);
+  EXPECT_FALSE(delegate->got_get_minimum_size_);
+  EXPECT_FALSE(delegate->got_get_maximum_size_);
+  EXPECT_FALSE(delegate->got_get_height_for_width_);
+  EXPECT_TRUE(panel->IsSame(delegate->view_));
+  delegate->Reset();
+
+  // Size is now the preferred size.
+  EXPECT_EQ(expected_size, panel->GetSize());
+
+  // No additional delegate methods were called.
+  EXPECT_TRUE(delegate->IsReset());
+}
+
+void FillLayoutSizeHierarchyImpl() {
+  CefRefPtr<CefPanel> panel_parent = CefPanel::CreatePanel(nullptr);
+  CefRefPtr<CefPanel> panel_child = CefPanel::CreatePanel(nullptr);
+
+  CefSize expected_size(100, 100);
+
+  // Default Layout is FillLayout.
+  CefRefPtr<CefLayout> layout1 = panel_parent->GetLayout();
+  EXPECT_TRUE(layout1.get());
+  EXPECT_TRUE(layout1->AsFillLayout().get());
+
+  // Default bounds are empty.
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_parent->GetBounds());
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child->GetBounds());
+
+  // Without delegates the size must be set on the parent.
+  panel_parent->SetSize(expected_size);
+
+  // FillLayout is the default Layout. Both panels should end up with the same
+  // size.
+  panel_parent->AddChildView(panel_child);
+
+  // Force layout.
+  panel_parent->Layout();
+
+  // Panels are now the same size.
+  EXPECT_EQ(expected_size, panel_parent->GetSize());
+  EXPECT_EQ(expected_size, panel_child->GetSize());
+
+  // Resize the parent panel to a larger size.
+  CefSize expected_size2(200, 200);
+  panel_parent->SetSize(expected_size2);
+
+  // Force layout.
+  panel_parent->Layout();
+
+  // Panels are now the larger size.
+  EXPECT_EQ(expected_size2, panel_parent->GetSize());
+  EXPECT_EQ(expected_size2, panel_child->GetSize());
+}
+
+void FillLayoutSizeHierarchyWithDelegate(bool size_from_parent) {
+  CefRefPtr<SizingPanelDelegate> delegate_parent = new SizingPanelDelegate();
+  CefRefPtr<CefPanel> panel_parent = CefPanel::CreatePanel(delegate_parent);
+  CefRefPtr<SizingPanelDelegate> delegate_child = new SizingPanelDelegate();
+  CefRefPtr<CefPanel> panel_child = CefPanel::CreatePanel(delegate_child);
+
+  CefSize expected_size(100, 100);
+
+  // The default layout is FillLayout, but explicitly set it anyways just for
+  // some testing variety.
+  panel_parent->SetToFillLayout();
+  panel_child->SetToFillLayout();
+
+  // Default bounds are empty.
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_parent->GetBounds());
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child->GetBounds());
+
+  // With delegates the size can come from either the parent or child.
+  if (size_from_parent)
+    delegate_parent->preferred_size_ = expected_size;
+  else
+    delegate_child->preferred_size_ = expected_size;
+
+  // FillLayout is the default Layout. Both panels should end up with the same
+  // size.
+  panel_parent->AddChildView(panel_child);
+
+  // No delegate methods were called yet.
+  EXPECT_TRUE(delegate_parent->IsReset());
+  EXPECT_TRUE(delegate_child->IsReset());
+
+  // Force layout.
+  panel_parent->Layout();
+
+  // delegate_parent will be called to get the preferred size for panel_parent.
+  EXPECT_TRUE(delegate_parent->got_get_preferred_size_);
+  EXPECT_FALSE(delegate_parent->got_get_minimum_size_);
+  EXPECT_FALSE(delegate_parent->got_get_maximum_size_);
+  EXPECT_FALSE(delegate_parent->got_get_height_for_width_);
+  EXPECT_TRUE(panel_parent->IsSame(delegate_parent->view_));
+  delegate_parent->Reset();
+
+  // delegate_child will be called to get the preferred size for panel_child.
+  EXPECT_TRUE(delegate_child->got_get_preferred_size_);
+  EXPECT_FALSE(delegate_child->got_get_minimum_size_);
+  EXPECT_FALSE(delegate_child->got_get_maximum_size_);
+  EXPECT_FALSE(delegate_child->got_get_height_for_width_);
+  EXPECT_TRUE(panel_child->IsSame(delegate_child->view_));
+  delegate_child->Reset();
+
+  // Panels are now the same size.
+  EXPECT_EQ(expected_size, panel_parent->GetSize());
+  EXPECT_EQ(expected_size, panel_child->GetSize());
+
+  // Resize the parent panel to a larger size.
+  CefSize expected_size2(200, 200);
+  panel_parent->SetSize(expected_size2);
+
+  // Force layout.
+  panel_parent->Layout();
+
+  // Panels are now the larger size.
+  EXPECT_EQ(expected_size2, panel_parent->GetSize());
+  EXPECT_EQ(expected_size2, panel_child->GetSize());
+
+  // No additional delegate methods were called.
+  EXPECT_TRUE(delegate_parent->IsReset());
+  EXPECT_TRUE(delegate_child->IsReset());
+}
+
+void FillLayoutSizeHierarchyFromParentWithDelegateImpl() {
+  FillLayoutSizeHierarchyWithDelegate(true);
+}
+
+void FillLayoutSizeHierarchyFromChildWithDelegateImpl() {
+  FillLayoutSizeHierarchyWithDelegate(false);
+}
+
+}  // namespace
+
+// Test FillLayout.
+PANEL_TEST(FillLayoutCreate)
+PANEL_TEST(FillLayoutSizeToPreferredSize)
+PANEL_TEST(FillLayoutSizeHierarchy)
+PANEL_TEST(FillLayoutSizeHierarchyFromParentWithDelegate)
+PANEL_TEST(FillLayoutSizeHierarchyFromChildWithDelegate)
+
+namespace {
+
+void BoxLayoutCreateImpl() {
+  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(nullptr);
+
+  CefBoxLayoutSettings settings;
+
+  // Explicitly set to BoxLayout.
+  panel->SetToBoxLayout(settings);
+
+  CefRefPtr<CefLayout> layout = panel->GetLayout();
+  EXPECT_TRUE(layout.get());
+  EXPECT_TRUE(layout->AsBoxLayout().get());
+}
+
+const int kBLParentSize = 100;
+const int kBLChildSize = 10;
+
+void BoxLayoutSizeHierarchy(bool with_delegate,
+                            const CefBoxLayoutSettings& settings,
+                            const CefRect& expected_child1_bounds,
+                            const CefRect& expected_child2_bounds,
+                            int child1_flex = 0,
+                            int child2_flex = 0) {
+  CefRefPtr<SizingPanelDelegate> delegate_parent;
+  if (with_delegate)
+    delegate_parent = new SizingPanelDelegate();
+  CefRefPtr<CefPanel> panel_parent = CefPanel::CreatePanel(delegate_parent);
+
+  CefRefPtr<SizingPanelDelegate> delegate_child1, delegate_child2;
+  if (with_delegate) {
+    delegate_child1 = new SizingPanelDelegate();
+    delegate_child2 = new SizingPanelDelegate();
+  }
+  CefRefPtr<CefPanel> panel_child1 = CefPanel::CreatePanel(delegate_child1);
+  CefRefPtr<CefPanel> panel_child2 = CefPanel::CreatePanel(delegate_child2);
+
+  // Default bounds are empty.
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_parent->GetBounds());
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child1->GetBounds());
+  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child2->GetBounds());
+
+  // Give the parent a size.
+  CefSize initial_parent_size(kBLParentSize, kBLParentSize);
+  if (with_delegate)
+    delegate_parent->preferred_size_ = initial_parent_size;
+  else
+    panel_parent->SetSize(initial_parent_size);
+
+  // Give the children a size smaller than the parent.
+  CefSize initial_child_size(kBLChildSize, kBLChildSize);
+  if (with_delegate) {
+    delegate_child1->preferred_size_ = initial_child_size;
+    delegate_child2->preferred_size_ = initial_child_size;
+  } else {
+    panel_child1->SetSize(initial_child_size);
+    panel_child2->SetSize(initial_child_size);
+  }
+
+  // Set to BoxLayout with |settings|.
+  panel_parent->SetToBoxLayout(settings);
+
+  panel_parent->AddChildView(panel_child1);
+  panel_parent->AddChildView(panel_child2);
+
+  if (child1_flex > 0 || child2_flex > 0) {
+    // Flex will apply relative stretch in the main axis direction.
+    CefRefPtr<CefBoxLayout> layout = panel_parent->GetLayout()->AsBoxLayout();
+    if (child1_flex > 0)
+      layout->SetFlexForView(panel_child1, child1_flex);
+    if (child2_flex > 0)
+      layout->SetFlexForView(panel_child2, child2_flex);
+  }
+
+  if (with_delegate) {
+    // No delegate methods were called yet.
+    EXPECT_TRUE(delegate_parent->IsReset());
+    EXPECT_TRUE(delegate_child1->IsReset());
+    EXPECT_TRUE(delegate_child2->IsReset());
+  }
+
+  // Force layout.
+  panel_parent->Layout();
+
+  if (with_delegate) {
+    // delegate_parent will be called to get the preferred size for
+    // panel_parent.
+    EXPECT_TRUE(delegate_parent->got_get_preferred_size_);
+    EXPECT_FALSE(delegate_parent->got_get_minimum_size_);
+    EXPECT_FALSE(delegate_parent->got_get_maximum_size_);
+    EXPECT_FALSE(delegate_parent->got_get_height_for_width_);
+    EXPECT_TRUE(panel_parent->IsSame(delegate_parent->view_));
+    delegate_parent->Reset();
+
+    // delegate_child1 will be called to get the preferred size for
+    // panel_child1.
+    // GetHeightForWidth may also be called depending on the settings.
+    EXPECT_TRUE(delegate_child1->got_get_preferred_size_);
+    EXPECT_FALSE(delegate_child1->got_get_minimum_size_);
+    EXPECT_FALSE(delegate_child1->got_get_maximum_size_);
+    EXPECT_TRUE(panel_child1->IsSame(delegate_child1->view_));
+    delegate_child1->Reset();
+
+    // delegate_child2 will be called to get the preferred size for
+    // panel_child2.
+    // GetHeightForWidth may also be called depending on the settings.
+    EXPECT_TRUE(delegate_child2->got_get_preferred_size_);
+    EXPECT_FALSE(delegate_child2->got_get_minimum_size_);
+    EXPECT_FALSE(delegate_child2->got_get_maximum_size_);
+    EXPECT_TRUE(panel_child2->IsSame(delegate_child2->view_));
+    delegate_child2->Reset();
+  }
+
+  // The parent should be the same size.
+  EXPECT_EQ(initial_parent_size, panel_parent->GetSize());
+
+  // Children should have the expected bounds.
+  EXPECT_EQ(expected_child1_bounds, panel_child1->GetBounds());
+  EXPECT_EQ(expected_child2_bounds, panel_child2->GetBounds());
+
+  if (with_delegate) {
+    // No additional delegate methods were called.
+    EXPECT_TRUE(delegate_parent->IsReset());
+    EXPECT_TRUE(delegate_child1->IsReset());
+    EXPECT_TRUE(delegate_child2->IsReset());
+  }
+}
+
+void BoxLayoutSizeHierarchyVerticalStretch(bool with_delegate) {
+  // Vertical layout with children stretched along the horizontal axis.
+  //
+  // -----------
+  // |111111111|
+  // |222222222|
+  // |         |
+  // |         |
+  // |         |
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+
+  CefRect expected_child1_bounds(0, 0, kBLParentSize, kBLChildSize);
+  CefRect expected_child2_bounds(0, kBLChildSize, kBLParentSize, kBLChildSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchImpl() {
+  BoxLayoutSizeHierarchyVerticalStretch(false);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchWithDelegateImpl() {
+  BoxLayoutSizeHierarchyVerticalStretch(true);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretch(bool with_delegate) {
+  // Horizontal layout with children stretched along the vertical axis.
+  //
+  // -----------
+  // |12       |
+  // |12       |
+  // |12       |
+  // |12       |
+  // |12       |
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+  settings.horizontal = true;
+
+  CefRect expected_child1_bounds(0, 0, kBLChildSize, kBLParentSize);
+  CefRect expected_child2_bounds(kBLChildSize, 0, kBLChildSize, kBLParentSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchImpl() {
+  BoxLayoutSizeHierarchyHorizontalStretch(false);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchWithDelegateImpl() {
+  BoxLayoutSizeHierarchyHorizontalStretch(true);
+}
+
+void BoxLayoutSizeHierarchyVerticalCenter(bool with_delegate) {
+  // Vertical layout with children centered along the horizontal axis.
+  //
+  // -----------
+  // |    1    |
+  // |    2    |
+  // |         |
+  // |         |
+  // |         |
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;
+
+  int xoffset = (kBLParentSize - kBLChildSize) / 2;
+  CefRect expected_child1_bounds(xoffset, 0, kBLChildSize, kBLChildSize);
+  CefRect expected_child2_bounds(xoffset, kBLChildSize, kBLChildSize,
+                                 kBLChildSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds);
+}
+
+void BoxLayoutSizeHierarchyVerticalCenterImpl() {
+  BoxLayoutSizeHierarchyVerticalCenter(false);
+}
+
+void BoxLayoutSizeHierarchyVerticalCenterWithDelegateImpl() {
+  BoxLayoutSizeHierarchyVerticalCenter(true);
+}
+
+void BoxLayoutSizeHierarchyHorizontalCenter(bool with_delegate) {
+  // Horizontal layout with children centered along the vertical axis.
+  //
+  // -----------
+  // |         |
+  // |         |
+  // |12       |
+  // |         |
+  // |         |
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+  settings.horizontal = true;
+  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;
+
+  int yoffset = (kBLParentSize - kBLChildSize) / 2;
+  CefRect expected_child1_bounds(0, yoffset, kBLChildSize, kBLChildSize);
+  CefRect expected_child2_bounds(kBLChildSize, yoffset, kBLChildSize,
+                                 kBLChildSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds);
+}
+
+void BoxLayoutSizeHierarchyHorizontalCenterImpl() {
+  BoxLayoutSizeHierarchyHorizontalCenter(false);
+}
+
+void BoxLayoutSizeHierarchyHorizontalCenterWithDelegateImpl() {
+  BoxLayoutSizeHierarchyHorizontalCenter(true);
+}
+
+void BoxLayoutSizeHierarchyVerticalCenterCenter(bool with_delegate) {
+  // Vertical layout with children centered along the horizontal and vertical
+  // axis.
+  //
+  // -----------
+  // |         |
+  // |    1    |
+  // |    2    |
+  // |         |
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+  settings.main_axis_alignment = CEF_MAIN_AXIS_ALIGNMENT_CENTER;
+  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;
+
+  int xoffset = (kBLParentSize - kBLChildSize) / 2;
+  int yoffset = (kBLParentSize - (kBLChildSize * 2)) / 2;
+  CefRect expected_child1_bounds(xoffset, yoffset, kBLChildSize, kBLChildSize);
+  CefRect expected_child2_bounds(xoffset, yoffset + kBLChildSize, kBLChildSize,
+                                 kBLChildSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds);
+}
+
+void BoxLayoutSizeHierarchyVerticalCenterCenterImpl() {
+  BoxLayoutSizeHierarchyVerticalCenterCenter(false);
+}
+
+void BoxLayoutSizeHierarchyVerticalCenterCenterWithDelegateImpl() {
+  BoxLayoutSizeHierarchyVerticalCenterCenter(true);
+}
+
+void BoxLayoutSizeHierarchyHorizontalCenterCenter(bool with_delegate) {
+  // Horizontal layout with children centered along the vertical and horizontal
+  // axis.
+  //
+  // -----------
+  // |         |
+  // |         |
+  // |   12    |
+  // |         |
+  // |         |
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+  settings.horizontal = true;
+  settings.main_axis_alignment = CEF_MAIN_AXIS_ALIGNMENT_CENTER;
+  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;
+
+  int xoffset = (kBLParentSize - (kBLChildSize * 2)) / 2;
+  int yoffset = (kBLParentSize - kBLChildSize) / 2;
+  CefRect expected_child1_bounds(xoffset, yoffset, kBLChildSize, kBLChildSize);
+  CefRect expected_child2_bounds(xoffset + kBLChildSize, yoffset, kBLChildSize,
+                                 kBLChildSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds);
+}
+
+void BoxLayoutSizeHierarchyHorizontalCenterCenterImpl() {
+  BoxLayoutSizeHierarchyHorizontalCenterCenter(false);
+}
+
+void BoxLayoutSizeHierarchyHorizontalCenterCenterWithDelegateImpl() {
+  BoxLayoutSizeHierarchyHorizontalCenterCenter(true);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchFlexOne(bool with_delegate) {
+  // Vertical layout with child1 stretched along the horizontal and vertical
+  // axis and child2 stretched along the horizontal axis only (unequal flex).
+  //
+  // -----------
+  // |111111111|
+  // |111111111|
+  // |111111111|
+  // |111111111|
+  // |222222222|
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+
+  CefRect expected_child1_bounds(0, 0, kBLParentSize,
+                                 kBLParentSize - kBLChildSize);
+  CefRect expected_child2_bounds(0, kBLParentSize - kBLChildSize, kBLParentSize,
+                                 kBLChildSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds, 1, 0);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchFlexOneImpl() {
+  BoxLayoutSizeHierarchyVerticalStretchFlexOne(false);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchFlexOneWithDelegateImpl() {
+  BoxLayoutSizeHierarchyVerticalStretchFlexOne(true);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchFlexOne(bool with_delegate) {
+  // Horizontal layout with child1 stretched along the vertical and horizontal
+  // axis and child2 stretched along the vertical axis only (unequal flex).
+  //
+  // -----------
+  // |111111112|
+  // |111111112|
+  // |111111112|
+  // |111111112|
+  // |111111112|
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+  settings.horizontal = true;
+
+  CefRect expected_child1_bounds(0, 0, kBLParentSize - kBLChildSize,
+                                 kBLParentSize);
+  CefRect expected_child2_bounds(kBLParentSize - kBLChildSize, 0, kBLChildSize,
+                                 kBLParentSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds, 1, 0);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchFlexOneImpl() {
+  BoxLayoutSizeHierarchyHorizontalStretchFlexOne(false);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchFlexOneWithDelegateImpl() {
+  BoxLayoutSizeHierarchyHorizontalStretchFlexOne(true);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchFlexBoth(bool with_delegate) {
+  // Vertical layout with children stretched along the horizontal and vertical
+  // axis (equal flex).
+  //
+  // -----------
+  // |111111111|
+  // |111111111|
+  // |111111111|
+  // |222222222|
+  // |222222222|
+  // |222222222|
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+
+  CefRect expected_child1_bounds(0, 0, kBLParentSize, kBLParentSize / 2);
+  CefRect expected_child2_bounds(0, kBLParentSize / 2, kBLParentSize,
+                                 kBLParentSize / 2);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds, 1, 1);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchFlexBothImpl() {
+  BoxLayoutSizeHierarchyVerticalStretchFlexBoth(false);
+}
+
+void BoxLayoutSizeHierarchyVerticalStretchFlexBothWithDelegateImpl() {
+  BoxLayoutSizeHierarchyVerticalStretchFlexBoth(true);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchFlexBoth(bool with_delegate) {
+  // Horizontal layout with children stretched along the vertical and horizontal
+  // axis (equal flex).
+  //
+  // -----------
+  // |111122222|
+  // |111122222|
+  // |111122222|
+  // |111122222|
+  // |111122222|
+  // -----------
+  //
+  CefBoxLayoutSettings settings;
+  settings.horizontal = true;
+
+  CefRect expected_child1_bounds(0, 0, kBLParentSize / 2, kBLParentSize);
+  CefRect expected_child2_bounds(kBLParentSize / 2, 0, kBLParentSize / 2,
+                                 kBLParentSize);
+
+  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
+                         expected_child2_bounds, 1, 1);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchFlexBothImpl() {
+  BoxLayoutSizeHierarchyHorizontalStretchFlexBoth(false);
+}
+
+void BoxLayoutSizeHierarchyHorizontalStretchFlexBothWithDelegateImpl() {
+  BoxLayoutSizeHierarchyHorizontalStretchFlexBoth(true);
+}
+
+}  // namespace
+
+// Test BoxLayout. The BoxLayoutSizeHierarchy* tests are representative but not
+// comprehensive (e.g. not all possible configurations are tested).
+PANEL_TEST(BoxLayoutCreate)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretch)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretch)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenter)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenterWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenter)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenterWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenterCenter)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenterCenterWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenterCenter)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenterCenterWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexOne)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexOneWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexOne)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexOneWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexBoth)
+PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexBothWithDelegate)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexBoth)
+PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexBothWithDelegate)
diff --git a/src/tests/ceftests/views/scroll_view_unittest.cc b/src/tests/ceftests/views/scroll_view_unittest.cc
new file mode 100644
index 0000000..5896211
--- /dev/null
+++ b/src/tests/ceftests/views/scroll_view_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_pack_strings.h"
+#include "include/views/cef_panel.h"
+#include "include/views/cef_panel_delegate.h"
+#include "include/views/cef_scroll_view.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/thread_helper.h"
+#include "tests/ceftests/views/test_window_delegate.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+#define SCROLL_VIEW_TEST_ASYNC(name) \
+  UI_THREAD_TEST_ASYNC(ViewsScrollViewTest, name)
+
+namespace {
+
+const int kScrollViewID = 1;
+const int kContentPanelID = 2;
+
+// Make the Panel larger then the Window so scroll bars appear.
+const int kContentPanelSize = TestWindowDelegate::kWSize + 200;
+
+class TestScrollViewDelegate : public CefViewDelegate {
+ public:
+  TestScrollViewDelegate() {}
+
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override {
+    EXPECT_EQ(kScrollViewID, view->GetID());
+    got_get_preferred_size_ = true;
+    return CefSize(kContentPanelSize, kContentPanelSize);
+  }
+
+  bool got_get_preferred_size_ = false;
+
+ private:
+  IMPLEMENT_REFCOUNTING(TestScrollViewDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestScrollViewDelegate);
+};
+
+class TestPanelDelegate : public CefPanelDelegate {
+ public:
+  TestPanelDelegate() {}
+
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override {
+    EXPECT_EQ(kContentPanelID, view->GetID());
+    got_get_preferred_size_ = true;
+    return CefSize(kContentPanelSize, kContentPanelSize);
+  }
+
+  bool got_get_preferred_size_ = false;
+
+ private:
+  IMPLEMENT_REFCOUNTING(TestPanelDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestPanelDelegate);
+};
+
+void RunScrollViewLayout(bool with_delegate, CefRefPtr<CefWindow> window) {
+  CefRefPtr<TestScrollViewDelegate> scroll_view_delegate;
+  CefRefPtr<TestPanelDelegate> panel_delegate;
+  if (with_delegate) {
+    scroll_view_delegate = new TestScrollViewDelegate();
+    panel_delegate = new TestPanelDelegate();
+  }
+
+  CefRefPtr<CefScrollView> scroll_view =
+      CefScrollView::CreateScrollView(scroll_view_delegate);
+  EXPECT_TRUE(scroll_view.get());
+  EXPECT_TRUE(scroll_view->AsScrollView().get());
+
+  // Verify default state.
+  EXPECT_FALSE(scroll_view->GetContentView().get());
+  EXPECT_EQ(CefRect(0, 0, 0, 0), scroll_view->GetVisibleContentRect());
+  EXPECT_FALSE(scroll_view->HasHorizontalScrollbar());
+  EXPECT_FALSE(scroll_view->HasVerticalScrollbar());
+
+  scroll_view->SetID(kScrollViewID);
+  scroll_view->SetBackgroundColor(CefColorSetARGB(255, 0, 255, 0));
+
+  CefRefPtr<CefPanel> content_panel = CefPanel::CreatePanel(panel_delegate);
+  content_panel->SetID(kContentPanelID);
+  content_panel->SetBackgroundColor(CefColorSetARGB(255, 255, 0, 0));
+
+  if (!with_delegate) {
+    // Explicitly set the content panel size. Otherwise, it will come from the
+    // delegate.
+    content_panel->SetSize(CefSize(kContentPanelSize, kContentPanelSize));
+  }
+
+  scroll_view->SetContentView(content_panel);
+  EXPECT_TRUE(content_panel->IsSame(scroll_view->GetContentView()));
+
+  window->AddChildView(scroll_view);
+
+  // Force layout.
+  window->Layout();
+
+  EXPECT_TRUE(scroll_view->HasHorizontalScrollbar());
+  EXPECT_TRUE(scroll_view->HasVerticalScrollbar());
+
+  if (with_delegate) {
+    EXPECT_TRUE(scroll_view_delegate->got_get_preferred_size_);
+    EXPECT_TRUE(panel_delegate->got_get_preferred_size_);
+  }
+
+  window->Show();
+
+  // With default FillLayout the ScrollView should be the size of the Window's
+  // client area.
+  const CefRect& client_bounds = window->GetClientAreaBoundsInScreen();
+  const CefRect& scroll_view_bounds = scroll_view->GetBoundsInScreen();
+  EXPECT_EQ(client_bounds, scroll_view_bounds);
+
+  // Content panel size should be unchanged.
+  const CefSize& content_panel_size = content_panel->GetSize();
+  EXPECT_EQ(CefSize(kContentPanelSize, kContentPanelSize), content_panel_size);
+
+  const int sb_height = scroll_view->GetHorizontalScrollbarHeight();
+  EXPECT_GT(sb_height, 0);
+  const int sb_width = scroll_view->GetVerticalScrollbarWidth();
+  EXPECT_GT(sb_width, 0);
+
+  // Verify visible content panel region.
+  const CefRect& visible_rect = scroll_view->GetVisibleContentRect();
+  EXPECT_EQ(CefRect(0, 0, scroll_view_bounds.width - sb_width,
+                    scroll_view_bounds.height - sb_height),
+            visible_rect);
+}
+
+void ScrollViewLayout(CefRefPtr<CefWaitableEvent> event, bool with_delegate) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunScrollViewLayout, with_delegate);
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void ScrollViewLayoutWithDelegateImpl(CefRefPtr<CefWaitableEvent> event) {
+  ScrollViewLayout(event, true);
+}
+
+void ScrollViewLayoutNoDelegateImpl(CefRefPtr<CefWaitableEvent> event) {
+  ScrollViewLayout(event, false);
+}
+
+}  // namespace
+
+// Test ScrollView layout. This is primarily to exercise exposed CEF APIs and is
+// not intended to comprehensively test ScrollView-related behavior (which we
+// presume that Chromium is testing).
+SCROLL_VIEW_TEST_ASYNC(ScrollViewLayoutWithDelegate)
+SCROLL_VIEW_TEST_ASYNC(ScrollViewLayoutNoDelegate)
diff --git a/src/tests/ceftests/views/test_window_delegate.cc b/src/tests/ceftests/views/test_window_delegate.cc
new file mode 100644
index 0000000..95c4ede
--- /dev/null
+++ b/src/tests/ceftests/views/test_window_delegate.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/ceftests/views/test_window_delegate.h"
+
+#include "include/cef_command_line.h"
+#include "include/views/cef_window.h"
+#include "include/views/cef_window_delegate.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/thread_helper.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+// Test timeout in MS.
+const int kTestTimeout = 5000;
+
+}  // namespace
+
+// static
+const int TestWindowDelegate::kWSize = 400;
+
+// static
+void TestWindowDelegate::RunTest(CefRefPtr<CefWaitableEvent> event,
+                                 const Config& config) {
+#if defined(OS_WIN)
+  RECT rect = {0, 0, config.window_size, config.window_size};
+  if (!config.frameless) {
+    // The size value is for the client area. Calculate the whole window size
+    // based on the default frame window style.
+    AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
+                     false /* has_menu */);
+  }
+  CefSize window_size = CefSize(rect.right - rect.left, rect.bottom - rect.top);
+#else
+  CefSize window_size = CefSize(config.window_size, config.window_size);
+#endif
+
+  CefWindow::CreateTopLevelWindow(
+      new TestWindowDelegate(event, config, window_size));
+}
+
+void TestWindowDelegate::OnWindowCreated(CefRefPtr<CefWindow> window) {
+  EXPECT_FALSE(window_);
+  window_ = window;
+
+  EXPECT_TRUE(window->IsValid());
+  EXPECT_FALSE(window->IsClosed());
+
+  EXPECT_FALSE(window->IsVisible());
+  EXPECT_FALSE(window->IsDrawn());
+
+  EXPECT_FALSE(window->IsActive());
+  EXPECT_FALSE(window->IsAlwaysOnTop());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_FALSE(window->IsFullscreen());
+
+  const char* title = "ViewsTest";
+  window->SetTitle(title);
+  EXPECT_STREQ(title, window->GetTitle().ToString().c_str());
+
+  EXPECT_FALSE(window->GetWindowIcon().get());
+  EXPECT_FALSE(window->GetWindowAppIcon().get());
+
+  EXPECT_TRUE(window->GetDisplay().get());
+
+  // Size will come from GetPreferredSize() on initial Window creation.
+  EXPECT_TRUE(got_get_preferred_size_);
+  CefRect client_bounds = window->GetBounds();
+  if (config_.frameless) {
+    EXPECT_EQ(config_.window_size, client_bounds.width);
+    EXPECT_EQ(config_.window_size, client_bounds.height);
+  } else {
+    // Client area bounds calculation might have off-by-one errors on Windows
+    // due to non-client frame size being calculated internally in pixels and
+    // then converted to DIPs. See http://crbug.com/602692.
+    EXPECT_TRUE(abs(client_bounds.width - window_size_.width) <= 1);
+    EXPECT_TRUE(abs(client_bounds.height - window_size_.height) <= 1);
+  }
+
+  // Run the callback.
+  if (!config_.on_window_created.is_null())
+    config_.on_window_created.Run(window);
+
+  if (config_.close_window) {
+    // Close the window asynchronously.
+    CefPostTask(TID_UI, base::Bind(&TestWindowDelegate::OnCloseWindow, this));
+  } else if (!CefCommandLine::GetGlobalCommandLine()->HasSwitch(
+                 "disable-test-timeout")) {
+    // Timeout the test after a reasonable delay. Use a WeakPtr so that the
+    // delayed task doesn't keep this object alive.
+    CefPostDelayedTask(TID_UI,
+                       base::Bind(&TestWindowDelegate::OnTimeoutWindow,
+                                  weak_ptr_factory_.GetWeakPtr()),
+                       kTestTimeout);
+  }
+}
+
+void TestWindowDelegate::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
+  EXPECT_TRUE(window->IsSame(window_));
+
+  EXPECT_TRUE(window->IsValid());
+  EXPECT_TRUE(window->IsClosed());
+  EXPECT_FALSE(window->IsVisible());
+  EXPECT_FALSE(window->IsDrawn());
+
+  // Run the callback.
+  if (!config_.on_window_destroyed.is_null())
+    config_.on_window_destroyed.Run(window);
+
+  window_ = nullptr;
+
+  // Don't execute the timeout callback.
+  weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+bool TestWindowDelegate::IsFrameless(CefRefPtr<CefWindow> window) {
+  return config_.frameless;
+}
+
+CefSize TestWindowDelegate::GetPreferredSize(CefRefPtr<CefView> view) {
+  got_get_preferred_size_ = true;
+  return window_size_;
+}
+
+bool TestWindowDelegate::OnAccelerator(CefRefPtr<CefWindow> window,
+                                       int command_id) {
+  if (!config_.on_accelerator.is_null())
+    return config_.on_accelerator.Run(window_, command_id);
+  return false;
+}
+
+bool TestWindowDelegate::OnKeyEvent(CefRefPtr<CefWindow> window,
+                                    const CefKeyEvent& event) {
+  if (!config_.on_key_event.is_null())
+    return config_.on_key_event.Run(window_, event);
+  return false;
+}
+
+TestWindowDelegate::TestWindowDelegate(CefRefPtr<CefWaitableEvent> event,
+                                       const Config& config,
+                                       const CefSize& window_size)
+    : event_(event),
+      config_(config),
+      window_size_(window_size),
+      weak_ptr_factory_(this) {}
+
+TestWindowDelegate::~TestWindowDelegate() {
+  // Complete the test (signal the event) asynchronously so objects on the call
+  // stack have a chance to unwind.
+  CefPostTask(TID_UI, base::Bind(SignalEvent, event_));
+}
+
+void TestWindowDelegate::OnCloseWindow() {
+  if (!window_)
+    return;
+
+  EXPECT_TRUE(window_->IsValid());
+  EXPECT_FALSE(window_->IsClosed());
+
+  // Close() may clear |window_| so keep a reference.
+  CefRefPtr<CefWindow> window = window_;
+  window->Close();
+
+  EXPECT_TRUE(window->IsValid());
+  EXPECT_TRUE(window->IsClosed());
+}
+
+void TestWindowDelegate::OnTimeoutWindow() {
+  EXPECT_TRUE(false) << "Test timed out after " << kTestTimeout << "ms";
+  OnCloseWindow();
+}
diff --git a/src/tests/ceftests/views/test_window_delegate.h b/src/tests/ceftests/views/test_window_delegate.h
new file mode 100644
index 0000000..2f0519b
--- /dev/null
+++ b/src/tests/ceftests/views/test_window_delegate.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_callback.h"
+#include "include/base/cef_weak_ptr.h"
+#include "include/cef_waitable_event.h"
+#include "include/views/cef_window.h"
+#include "include/views/cef_window_delegate.h"
+
+class TestWindowDelegate : public CefWindowDelegate {
+ public:
+  // Default window size.
+  static const int kWSize;
+
+  // Test execution callback.
+  typedef base::Callback<void(CefRefPtr<CefWindow>)> OnWindowCreatedCallback;
+  typedef base::Callback<void(CefRefPtr<CefWindow>)> OnWindowDestroyedCallback;
+  typedef base::Callback<bool(CefRefPtr<CefWindow>, int)> OnAcceleratorCallback;
+  typedef base::Callback<bool(CefRefPtr<CefWindow>, const CefKeyEvent&)>
+      OnKeyEventCallback;
+
+  struct Config {
+    OnWindowCreatedCallback on_window_created;
+    OnWindowDestroyedCallback on_window_destroyed;
+    OnAcceleratorCallback on_accelerator;
+    OnKeyEventCallback on_key_event;
+    bool frameless = false;
+    bool close_window = true;
+    int window_size = kWSize;
+  };
+
+  // Creates a Window with a new TestWindowDelegate instance and executes
+  // |window_test| after the Window is created. |event| will be signaled once
+  // the Window is closed. If |frameless| is true the Window will be created
+  // without a frame. If |close_window| is true the Window will be closed
+  // immediately after |window_test| returns. Otherwise, the caller is
+  // responsible for closing the Window passed to |window_test|.
+  static void RunTest(CefRefPtr<CefWaitableEvent> event, const Config& config);
+
+  // CefWindowDelegate methods:
+  void OnWindowCreated(CefRefPtr<CefWindow> window) override;
+  void OnWindowDestroyed(CefRefPtr<CefWindow> window) override;
+  bool IsFrameless(CefRefPtr<CefWindow> window) override;
+  CefSize GetPreferredSize(CefRefPtr<CefView> view) override;
+  bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) override;
+  bool OnKeyEvent(CefRefPtr<CefWindow> window,
+                  const CefKeyEvent& event) override;
+
+ private:
+  TestWindowDelegate(CefRefPtr<CefWaitableEvent> event,
+                     const Config& config,
+                     const CefSize& window_size);
+  ~TestWindowDelegate() override;
+
+  void OnCloseWindow();
+  void OnTimeoutWindow();
+
+  CefRefPtr<CefWaitableEvent> event_;
+  const Config config_;
+  const CefSize window_size_;
+
+  CefRefPtr<CefWindow> window_;
+
+  bool got_get_preferred_size_ = false;
+
+  // Must be the last member.
+  base::WeakPtrFactory<TestWindowDelegate> weak_ptr_factory_;
+
+  IMPLEMENT_REFCOUNTING(TestWindowDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestWindowDelegate);
+};
diff --git a/src/tests/ceftests/views/textfield_unittest.cc b/src/tests/ceftests/views/textfield_unittest.cc
new file mode 100644
index 0000000..a5b6b1a
--- /dev/null
+++ b/src/tests/ceftests/views/textfield_unittest.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_pack_strings.h"
+#include "include/views/cef_textfield.h"
+#include "include/views/cef_textfield_delegate.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/thread_helper.h"
+#include "tests/ceftests/views/test_window_delegate.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+// See ui/events/keycodes/keyboard_codes.h
+#define VKEY_UNKNOWN 0
+#if defined(OS_WIN)
+#define VKEY_A 'A'
+#define VKEY_SPACE VK_SPACE
+#define VKEY_RETURN VK_RETURN
+#elif defined(OS_POSIX)
+#define VKEY_A 0x41
+#define VKEY_SPACE 0x20
+#define VKEY_RETURN 0x0D
+#else
+#error "Unsupported platform"
+#endif
+
+#define TEXTFIELD_TEST(name) UI_THREAD_TEST(ViewsTextfieldTest, name)
+#define TEXTFIELD_TEST_ASYNC(name) \
+  UI_THREAD_TEST_ASYNC(ViewsTextfieldTest, name)
+
+namespace {
+
+void TextfieldContentsImpl() {
+  CefRefPtr<CefTextfield> textfield = CefTextfield::CreateTextfield(nullptr);
+  EXPECT_TRUE(textfield.get());
+  EXPECT_TRUE(textfield->AsTextfield().get());
+
+  // Test defaults.
+  EXPECT_TRUE(textfield->GetText().empty());
+  EXPECT_FALSE(textfield->HasSelection());
+  EXPECT_EQ(CefRange(0, 0), textfield->GetSelectedRange());
+  EXPECT_EQ(0U, textfield->GetCursorPosition());
+
+  // Test set/get text.
+  const char kText[] = "My test message!";
+  textfield->SetText(kText);
+  EXPECT_STREQ(kText, textfield->GetText().ToString().c_str());
+
+  size_t cursor_pos = sizeof(kText) - 1;
+  EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
+
+  // Test append text.
+  const char kAppendText[] = " And more.";
+  textfield->AppendText(kAppendText);
+  EXPECT_STREQ((std::string(kText) + kAppendText).c_str(),
+               textfield->GetText().ToString().c_str());
+  EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
+
+  // Test select range.
+  EXPECT_FALSE(textfield->HasSelection());
+  EXPECT_EQ(
+      CefRange(static_cast<int>(cursor_pos), static_cast<int>(cursor_pos)),
+      textfield->GetSelectedRange());
+  textfield->SelectRange(CefRange(0, static_cast<int>(cursor_pos)));
+  EXPECT_TRUE(textfield->HasSelection());
+  EXPECT_EQ(CefRange(0, static_cast<int>(cursor_pos)),
+            textfield->GetSelectedRange());
+  EXPECT_STREQ(kText, textfield->GetSelectedText().ToString().c_str());
+  EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
+
+  // Test insert or replace.
+  const char kReplaceText[] = "Other text.";
+  textfield->InsertOrReplaceText(kReplaceText);
+  EXPECT_STREQ((std::string(kReplaceText) + kAppendText).c_str(),
+               textfield->GetText().ToString().c_str());
+
+  cursor_pos = sizeof(kReplaceText) - 1;
+  EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
+
+  // Test select all.
+  EXPECT_FALSE(textfield->HasSelection());
+  textfield->SelectAll(false);
+  EXPECT_TRUE(textfield->HasSelection());
+
+  cursor_pos = sizeof(kReplaceText) + sizeof(kAppendText) - 2;
+  EXPECT_EQ(CefRange(0, static_cast<int>(cursor_pos)),
+            textfield->GetSelectedRange());
+  EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
+
+  // Test clear selection.
+  textfield->ClearSelection();
+  EXPECT_FALSE(textfield->HasSelection());
+  EXPECT_EQ(
+      CefRange(static_cast<int>(cursor_pos), static_cast<int>(cursor_pos)),
+      textfield->GetSelectedRange());
+  EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
+
+  // Test selection with command.
+  EXPECT_TRUE(textfield->IsCommandEnabled(IDS_APP_SELECT_ALL));
+  textfield->ExecuteCommand(IDS_APP_SELECT_ALL);
+  EXPECT_TRUE(textfield->HasSelection());
+  EXPECT_EQ(CefRange(0, static_cast<int>(cursor_pos)),
+            textfield->GetSelectedRange());
+  EXPECT_EQ(cursor_pos, textfield->GetCursorPosition());
+
+  textfield->ClearEditHistory();
+}
+
+void TextfieldStyleImpl() {
+  CefRefPtr<CefTextfield> textfield = CefTextfield::CreateTextfield(nullptr);
+  EXPECT_TRUE(textfield.get());
+
+  // Test defaults.
+  EXPECT_FALSE(textfield->IsPasswordInput());
+  EXPECT_FALSE(textfield->IsReadOnly());
+
+  // Test password input.
+  textfield->SetPasswordInput(true);
+  EXPECT_TRUE(textfield->IsPasswordInput());
+  textfield->SetPasswordInput(false);
+  EXPECT_FALSE(textfield->IsPasswordInput());
+
+  // Test read only.
+  textfield->SetReadOnly(true);
+  EXPECT_TRUE(textfield->IsReadOnly());
+  textfield->SetReadOnly(false);
+  EXPECT_FALSE(textfield->IsReadOnly());
+
+  // Test colors.
+  const cef_color_t color = CefColorSetARGB(255, 255, 0, 255);
+
+  EXPECT_NE(color, textfield->GetTextColor());
+  textfield->SetTextColor(color);
+  EXPECT_EQ(color, textfield->GetTextColor());
+
+  EXPECT_NE(color, textfield->GetSelectionTextColor());
+  textfield->SetSelectionTextColor(color);
+  EXPECT_EQ(color, textfield->GetSelectionTextColor());
+
+  EXPECT_NE(color, textfield->GetSelectionBackgroundColor());
+  textfield->SetSelectionBackgroundColor(color);
+  EXPECT_EQ(color, textfield->GetSelectionBackgroundColor());
+
+  textfield->SetPlaceholderTextColor(color);
+
+  // Test fonts.
+  textfield->SetFontList("Arial, 14px");
+
+  // Test format ranges.
+  const char kText[] = "test text";
+  textfield->SetText(kText);
+  textfield->ApplyTextColor(color, CefRange(0, 5));
+  textfield->ApplyTextStyle(CEF_TEXT_STYLE_BOLD, true, CefRange(0, 5));
+
+  // Test placeholder text.
+  textfield->SetPlaceholderText(kText);
+  EXPECT_STREQ(kText, textfield->GetPlaceholderText().ToString().c_str());
+
+  textfield->SetAccessibleName("MyTextfield");
+}
+
+}  // namespace
+
+// Test Textfield getters/setters.
+TEXTFIELD_TEST(TextfieldContents)
+TEXTFIELD_TEST(TextfieldStyle)
+
+namespace {
+
+const int kTextfieldID = 1;
+
+// Contents need to be supported by the TranslateKey function.
+const char kTestInputMessage[] = "Test Message";
+
+void TranslateKey(int c, int* keycode, uint32* modifiers) {
+  *keycode = VKEY_UNKNOWN;
+  *modifiers = 0;
+
+  if (c >= 'a' && c <= 'z') {
+    *keycode = VKEY_A + (c - 'a');
+  } else if (c >= 'A' && c <= 'Z') {
+    *keycode = VKEY_A + (c - 'A');
+    *modifiers = EVENTFLAG_SHIFT_DOWN;
+  } else if (c == ' ') {
+    *keycode = VKEY_SPACE;
+  }
+}
+
+class TestTextfieldDelegate : public CefTextfieldDelegate {
+ public:
+  TestTextfieldDelegate() {}
+
+  bool OnKeyEvent(CefRefPtr<CefTextfield> textfield,
+                  const CefKeyEvent& event) override {
+    EXPECT_TRUE(textfield.get());
+    EXPECT_EQ(textfield->GetID(), kTextfieldID);
+
+    if (event.type == KEYEVENT_RAWKEYDOWN &&
+        event.windows_key_code == VKEY_RETURN) {
+      // Got the whole string. Finish the test asynchronously.
+      CefPostTask(TID_UI, base::Bind(&TestTextfieldDelegate::FinishTest, this,
+                                     textfield));
+      return true;
+    }
+
+    if (event.type == KEYEVENT_CHAR) {
+      int keycode;
+      uint32 modifiers;
+      TranslateKey(kTestInputMessage[index_++], &keycode, &modifiers);
+
+      EXPECT_EQ(keycode, event.windows_key_code);
+      EXPECT_EQ(modifiers, event.modifiers);
+    }
+
+    return false;
+  }
+
+  void OnAfterUserAction(CefRefPtr<CefTextfield> textfield) override {
+    after_user_action_ct_++;
+  }
+
+ private:
+  void FinishTest(CefRefPtr<CefTextfield> textfield) {
+    // OnAfterUserAction() should be called for each unhandled character.
+    EXPECT_EQ(sizeof(kTestInputMessage) - 1, after_user_action_ct_);
+
+    // Verify the completed contents.
+    EXPECT_STREQ(kTestInputMessage, textfield->GetText().ToString().c_str());
+
+    // Close the window to end the test.
+    textfield->GetWindow()->Close();
+  }
+
+  int index_ = 0;
+  size_t after_user_action_ct_ = 0;
+
+  IMPLEMENT_REFCOUNTING(TestTextfieldDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestTextfieldDelegate);
+};
+
+void RunTextfieldKeyEvent(CefRefPtr<CefWindow> window) {
+  CefRefPtr<CefTextfield> textfield =
+      CefTextfield::CreateTextfield(new TestTextfieldDelegate());
+  textfield->SetID(kTextfieldID);
+
+  EXPECT_TRUE(textfield->AsTextfield());
+  EXPECT_EQ(kTextfieldID, textfield->GetID());
+  EXPECT_TRUE(textfield->IsVisible());
+  EXPECT_FALSE(textfield->IsDrawn());
+
+  window->AddChildView(textfield);
+  window->Layout();
+
+  EXPECT_TRUE(window->IsSame(textfield->GetWindow()));
+  EXPECT_TRUE(window->IsSame(textfield->GetParentView()));
+  EXPECT_TRUE(textfield->IsSame(window->GetViewForID(kTextfieldID)));
+  EXPECT_TRUE(textfield->IsVisible());
+  EXPECT_TRUE(textfield->IsDrawn());
+
+  window->Show();
+
+  // Give input focus to the textfield.
+  textfield->RequestFocus();
+
+  // Send the contents of |kTestInputMessage| to the textfield.
+  for (size_t i = 0; i < sizeof(kTestInputMessage) - 1; ++i) {
+    int keycode;
+    uint32 modifiers;
+    TranslateKey(kTestInputMessage[i], &keycode, &modifiers);
+    window->SendKeyPress(keycode, modifiers);
+  }
+
+  // Send return to end the text input.
+  window->SendKeyPress(VKEY_RETURN, 0);
+}
+
+void TextfieldKeyEventImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunTextfieldKeyEvent);
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+}  // namespace
+
+// Test Textfield input and events. This is primarily to exercise exposed CEF
+// APIs and is not intended to comprehensively test Textfield-related behavior
+// (which we presume that Chromium is testing).
+TEXTFIELD_TEST_ASYNC(TextfieldKeyEvent)
diff --git a/src/tests/ceftests/views/window_unittest.cc b/src/tests/ceftests/views/window_unittest.cc
new file mode 100644
index 0000000..a2be321
--- /dev/null
+++ b/src/tests/ceftests/views/window_unittest.cc
@@ -0,0 +1,496 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/views/cef_box_layout.h"
+#include "include/views/cef_layout.h"
+#include "include/views/cef_panel.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/image_util.h"
+#include "tests/ceftests/thread_helper.h"
+#include "tests/ceftests/views/test_window_delegate.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+#define WINDOW_TEST_ASYNC(name) UI_THREAD_TEST_ASYNC(ViewsWindowTest, name)
+
+#if !defined(OS_WIN)
+#define VK_MENU 0x12  // ALT key.
+#endif
+
+namespace {
+
+// Window state change delay in MS.
+const int kStateDelayMS = 200;
+
+const int kWSize = TestWindowDelegate::kWSize;
+
+// Test that |expected| and |actual| are within |allowed_deviance| of each
+// other.
+void ExpectCloseRects(const CefRect& expected,
+                      const CefRect& actual,
+                      int allowed_deviance) {
+  EXPECT_LE(abs(expected.x - actual.x), allowed_deviance);
+  EXPECT_LE(abs(expected.y - actual.y), allowed_deviance);
+  EXPECT_LE(abs(expected.width - actual.width), allowed_deviance);
+  EXPECT_LE(abs(expected.height - actual.height), allowed_deviance);
+}
+
+void WindowCreateImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void WindowCreateFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.frameless = true;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void RunWindowShowHide(CefRefPtr<CefWindow> window) {
+  EXPECT_FALSE(window->IsVisible());
+  EXPECT_FALSE(window->IsDrawn());
+  window->Show();
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+  window->Hide();
+  EXPECT_FALSE(window->IsVisible());
+  EXPECT_FALSE(window->IsDrawn());
+}
+
+void WindowShowHideImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowShowHide);
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void WindowShowHideFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowShowHide);
+  config.frameless = true;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+const int kWPanel1ID = 1;
+const int kWPanel2ID = 2;
+
+void CreateBoxLayout(CefRefPtr<CefWindow> parent) {
+  CefRefPtr<CefPanel> panel_child1 = CefPanel::CreatePanel(nullptr);
+  panel_child1->SetID(kWPanel1ID);
+  panel_child1->SetBackgroundColor(CefColorSetARGB(255, 0, 0, 255));
+  EXPECT_TRUE(panel_child1->IsVisible());
+  EXPECT_FALSE(panel_child1->IsDrawn());
+
+  CefRefPtr<CefPanel> panel_child2 = CefPanel::CreatePanel(nullptr);
+  panel_child2->SetID(kWPanel2ID);
+  panel_child2->SetBackgroundColor(CefColorSetARGB(255, 0, 255, 0));
+  EXPECT_TRUE(panel_child2->IsVisible());
+  EXPECT_FALSE(panel_child2->IsDrawn());
+
+  // Set to BoxLayout. Default layout is vertical with children stretched along
+  // the horizontal axis.
+  CefBoxLayoutSettings settings;
+  parent->SetToBoxLayout(settings);
+
+  parent->AddChildView(panel_child1);
+  parent->AddChildView(panel_child2);
+
+  // IsDrawn() returns true because the Panels now have a RootView from the
+  // Window.
+  EXPECT_TRUE(panel_child1->IsDrawn());
+  EXPECT_TRUE(panel_child1->IsDrawn());
+
+  // Stretch children equally along the vertical axis using flex.
+  CefRefPtr<CefBoxLayout> layout = parent->GetLayout()->AsBoxLayout();
+  layout->SetFlexForView(panel_child1, 1);
+  layout->SetFlexForView(panel_child2, 1);
+
+  // Force layout.
+  parent->Layout();
+
+  // The children should each take up 50% of the client area.
+  ExpectCloseRects(CefRect(0, 0, kWSize, kWSize / 2), panel_child1->GetBounds(),
+                   1);
+  ExpectCloseRects(CefRect(0, kWSize / 2, kWSize, kWSize / 2),
+                   panel_child2->GetBounds(), 1);
+}
+
+void RunWindowLayoutAndCoords(CefRefPtr<CefWindow> window) {
+  CreateBoxLayout(window);
+
+  CefRefPtr<CefView> view1 = window->GetViewForID(kWPanel1ID);
+  EXPECT_TRUE(view1.get());
+  CefRefPtr<CefView> view2 = window->GetViewForID(kWPanel2ID);
+  EXPECT_TRUE(view2.get());
+
+  window->Show();
+
+  CefRect client_bounds_in_screen = window->GetClientAreaBoundsInScreen();
+  CefPoint point;
+
+  // Test view to screen coordinate conversions.
+  point = CefPoint(0, 0);
+  EXPECT_TRUE(view1->ConvertPointToScreen(point));
+  EXPECT_EQ(CefPoint(client_bounds_in_screen.x, client_bounds_in_screen.y),
+            point);
+  point = CefPoint(0, 0);
+  EXPECT_TRUE(view2->ConvertPointToScreen(point));
+  EXPECT_EQ(CefPoint(client_bounds_in_screen.x,
+                     client_bounds_in_screen.y + kWSize / 2),
+            point);
+
+  // Test view from screen coordinate conversions.
+  point = CefPoint(client_bounds_in_screen.x, client_bounds_in_screen.y);
+  EXPECT_TRUE(view1->ConvertPointFromScreen(point));
+  EXPECT_EQ(CefPoint(0, 0), point);
+  point = CefPoint(client_bounds_in_screen.x,
+                   client_bounds_in_screen.y + kWSize / 2);
+  EXPECT_TRUE(view2->ConvertPointFromScreen(point));
+  EXPECT_EQ(CefPoint(0, 0), point);
+
+  // Test view to window coordinate conversions.
+  point = CefPoint(0, 0);
+  EXPECT_TRUE(view1->ConvertPointToWindow(point));
+  EXPECT_EQ(CefPoint(0, 0), point);
+  point = CefPoint(0, 0);
+  EXPECT_TRUE(view2->ConvertPointToWindow(point));
+  EXPECT_EQ(CefPoint(0, kWSize / 2), point);
+
+  // Test view from window coordinate conversions.
+  point = CefPoint(0, 0);
+  EXPECT_TRUE(view1->ConvertPointFromWindow(point));
+  EXPECT_EQ(CefPoint(0, 0), point);
+  point = CefPoint(0, kWSize / 2);
+  EXPECT_TRUE(view2->ConvertPointFromWindow(point));
+  EXPECT_EQ(CefPoint(0, 0), point);
+
+  // Test view to view coordinate conversions.
+  point = CefPoint(0, 0);
+  EXPECT_TRUE(view1->ConvertPointToView(view2, point));
+  EXPECT_EQ(CefPoint(0, -kWSize / 2), point);
+  point = CefPoint(0, 0);
+  EXPECT_TRUE(view2->ConvertPointToView(view1, point));
+  EXPECT_EQ(CefPoint(0, kWSize / 2), point);
+
+  // Test view from view coordinate conversions.
+  point = CefPoint(0, -kWSize / 2);
+  EXPECT_TRUE(view1->ConvertPointFromView(view2, point));
+  EXPECT_EQ(CefPoint(0, 0), point);
+  point = CefPoint(0, kWSize / 2);
+  EXPECT_TRUE(view2->ConvertPointFromView(view1, point));
+  EXPECT_EQ(CefPoint(0, 0), point);
+
+  CefRefPtr<CefDisplay> display = window->GetDisplay();
+  EXPECT_TRUE(display.get());
+
+  // We don't know what the pixel values will be, but they should be reversable.
+  point = CefPoint(client_bounds_in_screen.x, client_bounds_in_screen.y);
+  display->ConvertPointToPixels(point);
+  display->ConvertPointFromPixels(point);
+  EXPECT_EQ(CefPoint(client_bounds_in_screen.x, client_bounds_in_screen.y),
+            point);
+}
+
+void WindowLayoutAndCoordsImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowLayoutAndCoords);
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void WindowLayoutAndCoordsFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowLayoutAndCoords);
+  config.frameless = true;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void VerifyRestore(CefRefPtr<CefWindow> window) {
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_FALSE(window->IsFullscreen());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  // End the test by closing the Window.
+  window->Close();
+}
+
+void VerifyMaximize(CefRefPtr<CefWindow> window) {
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_TRUE(window->IsMaximized());
+  EXPECT_FALSE(window->IsFullscreen());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  window->Restore();
+  CefPostDelayedTask(TID_UI, base::Bind(VerifyRestore, window), kStateDelayMS);
+}
+
+void RunWindowMaximize(CefRefPtr<CefWindow> window) {
+  CreateBoxLayout(window);
+  window->Show();
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_FALSE(window->IsFullscreen());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  window->Maximize();
+  CefPostDelayedTask(TID_UI, base::Bind(VerifyMaximize, window), kStateDelayMS);
+}
+
+void WindowMaximizeImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowMaximize);
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void WindowMaximizeFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowMaximize);
+  config.frameless = true;
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void VerifyMinimize(CefRefPtr<CefWindow> window) {
+  EXPECT_TRUE(window->IsMinimized());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_FALSE(window->IsFullscreen());
+
+  // This result is a bit unexpected, but I guess the platform considers a
+  // window to be visible even when it's minimized.
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  window->Restore();
+  CefPostDelayedTask(TID_UI, base::Bind(VerifyRestore, window), kStateDelayMS);
+}
+
+void RunWindowMinimize(CefRefPtr<CefWindow> window) {
+  CreateBoxLayout(window);
+  window->Show();
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_FALSE(window->IsFullscreen());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  window->Minimize();
+  CefPostDelayedTask(TID_UI, base::Bind(VerifyMinimize, window), kStateDelayMS);
+}
+
+void WindowMinimizeImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowMinimize);
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void WindowMinimizeFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowMinimize);
+  config.frameless = true;
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void VerifyFullscreenExit(CefRefPtr<CefWindow> window) {
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_FALSE(window->IsFullscreen());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  // End the test by closing the Window.
+  window->Close();
+}
+
+void VerifyFullscreen(CefRefPtr<CefWindow> window) {
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_TRUE(window->IsFullscreen());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  window->SetFullscreen(false);
+  CefPostDelayedTask(TID_UI, base::Bind(VerifyFullscreenExit, window),
+                     kStateDelayMS);
+}
+
+void RunWindowFullscreen(CefRefPtr<CefWindow> window) {
+  CreateBoxLayout(window);
+  window->Show();
+  EXPECT_FALSE(window->IsMinimized());
+  EXPECT_FALSE(window->IsMaximized());
+  EXPECT_FALSE(window->IsFullscreen());
+  EXPECT_TRUE(window->IsVisible());
+  EXPECT_TRUE(window->IsDrawn());
+
+  window->SetFullscreen(true);
+  CefPostDelayedTask(TID_UI, base::Bind(VerifyFullscreen, window),
+                     kStateDelayMS);
+}
+
+void WindowFullscreenImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowFullscreen);
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void WindowFullscreenFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowFullscreen);
+  config.frameless = true;
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void RunWindowIcon(CefRefPtr<CefWindow> window) {
+  CefRefPtr<CefImage> image = CefImage::CreateImage();
+  image_util::LoadIconImage(image, 1.0);
+  image_util::LoadIconImage(image, 2.0);
+
+  EXPECT_FALSE(window->GetWindowIcon().get());
+  window->SetWindowIcon(image);
+  EXPECT_TRUE(image->IsSame(window->GetWindowIcon()));
+
+  EXPECT_FALSE(window->GetWindowAppIcon().get());
+  window->SetWindowAppIcon(image);
+  EXPECT_TRUE(image->IsSame(window->GetWindowAppIcon()));
+
+  window->Show();
+}
+
+void WindowIconImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowIcon);
+  TestWindowDelegate::RunTest(event, config);
+}
+
+void WindowIconFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowIcon);
+  config.frameless = true;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+const int kChar = 'A';
+const int kCloseWindowId = 2;
+bool got_accelerator;
+int got_key_event_alt_count;
+bool got_key_event_char;
+
+void TriggerAccelerator(CefRefPtr<CefWindow> window) {
+  window->SendKeyPress(kChar, EVENTFLAG_ALT_DOWN);
+}
+
+bool OnKeyEvent(CefRefPtr<CefWindow> window, const CefKeyEvent& event) {
+  if (event.type != KEYEVENT_RAWKEYDOWN)
+    return false;
+
+  if (event.windows_key_code == VK_MENU) {
+    // First we get the ALT key press in all cases.
+    EXPECT_FALSE(got_key_event_char);
+    if (got_key_event_alt_count == 0)
+      EXPECT_FALSE(got_accelerator);
+    else
+      EXPECT_TRUE(got_accelerator);
+
+    EXPECT_EQ(EVENTFLAG_ALT_DOWN, static_cast<int>(event.modifiers));
+    got_key_event_alt_count++;
+  } else if (event.windows_key_code == kChar) {
+    // Then we get the char key press with the ALT modifier if the accelerator
+    // isn't registered.
+    EXPECT_TRUE(got_accelerator);
+    EXPECT_EQ(got_key_event_alt_count, 2);
+    EXPECT_FALSE(got_key_event_char);
+
+    EXPECT_EQ(EVENTFLAG_ALT_DOWN, static_cast<int>(event.modifiers));
+    got_key_event_char = true;
+
+    // Call this method just to make sure it doesn't crash.
+    window->RemoveAllAccelerators();
+
+    // End the test by closing the Window.
+    window->Close();
+
+    return true;
+  }
+
+  return false;
+}
+
+bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
+  EXPECT_FALSE(got_accelerator);
+  EXPECT_EQ(got_key_event_alt_count, 1);
+  EXPECT_FALSE(got_key_event_char);
+
+  EXPECT_EQ(kCloseWindowId, command_id);
+  got_accelerator = true;
+
+  // Remove the accelerator.
+  window->RemoveAccelerator(kCloseWindowId);
+
+  // Now send the event without the accelerator registered. Should result in a
+  // call to OnKeyEvent.
+  TriggerAccelerator(window);
+
+  return true;
+}
+
+void RunWindowAccelerator(CefRefPtr<CefWindow> window) {
+  window->SetAccelerator(kCloseWindowId, kChar, false, false, true);
+  window->Show();
+
+  CefPostDelayedTask(TID_UI, base::Bind(TriggerAccelerator, window),
+                     kStateDelayMS);
+}
+
+void VerifyWindowAccelerator(CefRefPtr<CefWindow> window) {
+  EXPECT_TRUE(got_accelerator);
+  EXPECT_EQ(got_key_event_alt_count, 2);
+  EXPECT_TRUE(got_key_event_char);
+}
+
+// Expected order of events:
+// 1. OnKeyEvent for ALT key press.
+// 2. OnAccelerator for ALT+Char key press (with accelerator registered).
+// 3. OnKeyEvent for ALT key press.
+// 4. OnKeyEvent for ALT+Char key press (without accelerator registered).
+void WindowAcceleratorImpl(CefRefPtr<CefWaitableEvent> event) {
+  got_accelerator = false;
+  got_key_event_alt_count = 0;
+  got_key_event_char = false;
+
+  TestWindowDelegate::Config config;
+  config.on_window_created = base::Bind(RunWindowAccelerator);
+  config.on_window_destroyed = base::Bind(VerifyWindowAccelerator);
+  config.on_accelerator = base::Bind(OnAccelerator);
+  config.on_key_event = base::Bind(OnKeyEvent);
+  config.close_window = false;
+  TestWindowDelegate::RunTest(event, config);
+}
+
+}  // namespace
+
+// Test window functionality. This is primarily to exercise exposed CEF APIs
+// and is not intended to comprehensively test window-related behavior (which
+// we presume that Chromium is testing).
+WINDOW_TEST_ASYNC(WindowCreate)
+WINDOW_TEST_ASYNC(WindowCreateFrameless)
+WINDOW_TEST_ASYNC(WindowShowHide)
+WINDOW_TEST_ASYNC(WindowShowHideFrameless)
+WINDOW_TEST_ASYNC(WindowLayoutAndCoords)
+WINDOW_TEST_ASYNC(WindowLayoutAndCoordsFrameless)
+WINDOW_TEST_ASYNC(WindowMaximize)
+WINDOW_TEST_ASYNC(WindowMaximizeFrameless)
+WINDOW_TEST_ASYNC(WindowMinimize)
+WINDOW_TEST_ASYNC(WindowMinimizeFrameless)
+WINDOW_TEST_ASYNC(WindowFullscreen)
+WINDOW_TEST_ASYNC(WindowFullscreenFrameless)
+WINDOW_TEST_ASYNC(WindowIcon)
+WINDOW_TEST_ASYNC(WindowIconFrameless)
+WINDOW_TEST_ASYNC(WindowAccelerator)
diff --git a/src/tests/ceftests/waitable_event_unittest.cc b/src/tests/ceftests/waitable_event_unittest.cc
new file mode 100644
index 0000000..4e55655
--- /dev/null
+++ b/src/tests/ceftests/waitable_event_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2012 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_thread.h"
+#include "include/cef_waitable_event.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+// Test manual reset.
+TEST(WaitableEventTest, ManualReset) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(false, false);
+
+  EXPECT_FALSE(event->IsSignaled());
+
+  event->Signal();
+  EXPECT_TRUE(event->IsSignaled());
+  EXPECT_TRUE(event->IsSignaled());
+
+  event->Reset();
+  EXPECT_FALSE(event->IsSignaled());
+  EXPECT_FALSE(event->TimedWait(10));
+
+  event->Signal();
+  event->Wait();
+  EXPECT_TRUE(event->TimedWait(10));
+}
+
+// Test automatic reset.
+TEST(WaitableEventTest, AutomaticReset) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  EXPECT_FALSE(event->IsSignaled());
+
+  event->Signal();
+  EXPECT_TRUE(event->IsSignaled());
+  EXPECT_FALSE(event->IsSignaled());
+
+  event->Reset();
+  EXPECT_FALSE(event->IsSignaled());
+  EXPECT_FALSE(event->TimedWait(10));
+
+  event->Signal();
+  event->Wait();
+  EXPECT_FALSE(event->TimedWait(10));
+
+  event->Signal();
+  EXPECT_TRUE(event->TimedWait(10));
+}
+
+namespace {
+
+void SignalEvent(CefWaitableEvent* event) {
+  event->Signal();
+}
+
+}  // namespace
+
+// Tests that a WaitableEvent can be safely deleted when |Wait| is done without
+// additional synchronization.
+TEST(WaitableEventTest, WaitAndDelete) {
+  CefRefPtr<CefWaitableEvent> event =
+      CefWaitableEvent::CreateWaitableEvent(true, false);
+
+  CefRefPtr<CefThread> thread = CefThread::CreateThread("waitable_event_test");
+  thread->GetTaskRunner()->PostDelayedTask(
+      CefCreateClosureTask(
+          base::Bind(SignalEvent, base::Unretained(event.get()))),
+      10);
+
+  event->Wait();
+  event = nullptr;
+
+  thread->Stop();
+  thread = nullptr;
+}
diff --git a/src/tests/ceftests/webui_unittest.cc b/src/tests/ceftests/webui_unittest.cc
new file mode 100644
index 0000000..7c69d86
--- /dev/null
+++ b/src/tests/ceftests/webui_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/base/cef_bind.h"
+#include "include/cef_callback.h"
+#include "include/cef_parser.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/ceftests/test_handler.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+typedef std::vector<std::string> UrlList;
+
+class WebUITestHandler : public TestHandler {
+ public:
+  explicit WebUITestHandler(const UrlList& url_list)
+      : url_list_(url_list), url_index_(0U), expected_error_code_(ERR_NONE) {
+    CHECK(!url_list_.empty());
+  }
+
+  void set_expected_url(const std::string& expected_url) {
+    expected_url_ = expected_url;
+  }
+
+  void set_expected_error_code(int error_code) {
+    expected_error_code_ = error_code;
+  }
+
+  void RunTest() override {
+    // Create the browser.
+    CreateBrowser(url_list_[0]);
+
+    // Time out the test after a reasonable period of time.
+    SetTestTimeout((int(url_list_.size() / 5U) + 1) * 5000);
+  }
+
+  void NextNav() {
+    base::Closure next_action;
+
+    if (++url_index_ < url_list_.size()) {
+      next_action =
+          base::Bind(&WebUITestHandler::LoadURL, this, url_list_[url_index_]);
+    } else {
+      next_action = base::Bind(&WebUITestHandler::DestroyTest, this);
+    }
+
+    // Wait a bit for the WebUI content to finish loading before performing the
+    // next action.
+    CefPostDelayedTask(TID_UI, next_action, 200);
+  }
+
+  void LoadURL(const std::string& url) {
+    GetBrowser()->GetMainFrame()->LoadURL(url);
+  }
+
+  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
+                            bool isLoading,
+                            bool canGoBack,
+                            bool canGoForward) override {
+    if (!isLoading) {
+      got_loading_state_done_.yes();
+      NextNavIfDone(browser->GetMainFrame()->GetURL());
+    }
+  }
+
+  void OnLoadError(CefRefPtr<CefBrowser> browser,
+                   CefRefPtr<CefFrame> frame,
+                   ErrorCode errorCode,
+                   const CefString& errorText,
+                   const CefString& failedUrl) override {
+    got_load_error_.yes();
+    EXPECT_EQ(expected_error_code_, errorCode)
+        << "failedUrl = " << failedUrl.ToString();
+    NextNavIfDone(failedUrl);
+  }
+
+  void DestroyTest() override {
+    EXPECT_TRUE(got_loading_state_done_);
+    if (expected_error_code_ == ERR_NONE)
+      EXPECT_FALSE(got_load_error_);
+    else
+      EXPECT_TRUE(got_load_error_);
+
+    TestHandler::DestroyTest();
+  }
+
+ private:
+  void NextNavIfDone(const std::string& url) {
+    bool done = false;
+    if (expected_error_code_ == ERR_NONE) {
+      if (got_loading_state_done_)
+        done = true;
+    } else if (got_load_error_ && got_loading_state_done_) {
+      done = true;
+    }
+
+    if (done) {
+      // Verify that we navigated to the expected URL.
+      std::string expected_url = expected_url_;
+      if (expected_url.empty() && url_index_ < url_list_.size())
+        expected_url = url_list_[url_index_];
+      EXPECT_STREQ(expected_url.c_str(), url.c_str());
+
+      NextNav();
+    }
+  }
+
+  UrlList url_list_;
+  size_t url_index_;
+
+  std::string expected_url_;
+  int expected_error_code_;
+
+  TrackCallback got_loading_state_done_;
+  TrackCallback got_load_error_;
+
+  IMPLEMENT_REFCOUNTING(WebUITestHandler);
+};
+
+}  // namespace
+
+// Test hosts with special behaviors.
+
+// about:* URIs should redirect to chrome://*.
+TEST(WebUITest, about) {
+  UrlList url_list;
+  url_list.push_back("about:license");
+  CefRefPtr<WebUITestHandler> handler = new WebUITestHandler(url_list);
+  handler->set_expected_url("chrome://license/");
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// chrome://network-error/X should generate network error X.
+TEST(WebUITest, network_error) {
+  UrlList url_list;
+  // -310 is ERR_TOO_MANY_REDIRECTS
+  url_list.push_back("chrome://network-error/-310");
+  CefRefPtr<WebUITestHandler> handler = new WebUITestHandler(url_list);
+  handler->set_expected_error_code(ERR_TOO_MANY_REDIRECTS);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+// Test hosts with a single URL.
+
+namespace {
+
+void RunWebUITest(const UrlList& url_list) {
+  CefRefPtr<WebUITestHandler> handler = new WebUITestHandler(url_list);
+  handler->ExecuteTest();
+  ReleaseAndWaitForDestructor(handler);
+}
+
+void RunWebUITest(const std::string& url) {
+  UrlList url_list;
+  url_list.push_back(url);
+  RunWebUITest(url_list);
+}
+
+}  // namespace
+
+#define WEBUI_TEST(name)                                      \
+  TEST(WebUITest, name) {                                     \
+    std::string name_str = #name;                             \
+    std::replace(name_str.begin(), name_str.end(), '_', '-'); \
+    RunWebUITest("chrome://" + name_str + "/");               \
+  }
+
+WEBUI_TEST(appcache_internals)
+WEBUI_TEST(accessibility)
+WEBUI_TEST(blob_internals)
+WEBUI_TEST(extensions_support)
+WEBUI_TEST(gpu)
+WEBUI_TEST(histograms)
+WEBUI_TEST(indexeddb_internals)
+WEBUI_TEST(license)
+WEBUI_TEST(media_internals)
+WEBUI_TEST(net_export)
+WEBUI_TEST(network_errors)
+WEBUI_TEST(serviceworker_internals)
+WEBUI_TEST(system)
+WEBUI_TEST(tracing)
+WEBUI_TEST(version)
+WEBUI_TEST(webrtc_internals)
+WEBUI_TEST(webui_hosts)
+
+// Test hosts with multiple URLs.
+
+TEST(WebUITest, net_internals) {
+  UrlList url_list;
+  url_list.push_back("chrome://net-internals/#events");
+  url_list.push_back("chrome://net-internals/#proxy");
+  url_list.push_back("chrome://net-internals/#dns");
+  url_list.push_back("chrome://net-internals/#sockets");
+  url_list.push_back("chrome://net-internals/#hsts");
+
+  RunWebUITest(url_list);
+}
diff --git a/src/tests/ceftests/xml_reader_unittest.cc b/src/tests/ceftests/xml_reader_unittest.cc
new file mode 100644
index 0000000..b6b7651
--- /dev/null
+++ b/src/tests/ceftests/xml_reader_unittest.cc
@@ -0,0 +1,640 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_stream.h"
+#include "include/cef_xml_reader.h"
+#include "include/wrapper/cef_xml_object.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+char g_test_xml[] =
+    "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
+    "<?my_instruction my_value?>\n"
+    "<!DOCTYPE my_document SYSTEM \"example.dtd\" [\n"
+    "    <!ENTITY EA \"EA Value\">\n"
+    "    <!ENTITY EB \"EB Value\">\n"
+    "]>\n"
+    "<ns:obj xmlns:ns=\"http://www.example.org/ns\">\n"
+    "  <ns:objA>value A</ns:objA>\n"
+    "  <!-- my comment -->\n"
+    "  <ns:objB>\n"
+    "    <ns:objB_1>value B1</ns:objB_1>\n"
+    "    <ns:objB_2><![CDATA[some <br/> data]]></ns:objB_2>\n"
+    "    <ns:objB_3>&EB;</ns:objB_3>\n"
+    "    <ns:objB_4><b>this is</b> mixed content &EA;</ns:objB_4>\n"
+    "  </ns:objB>\n"
+    "  <ns:objC ns:attr1=\"value C1\" ns:attr2=\"value C2\"/><ns:objD>"
+    "</ns:objD>\n"
+    "</ns:obj>\n";
+
+}  // namespace
+
+// Test XML reading
+TEST(XmlReaderTest, Read) {
+  // Create the stream reader.
+  CefRefPtr<CefStreamReader> stream(
+      CefStreamReader::CreateForData(g_test_xml, sizeof(g_test_xml) - 1));
+  ASSERT_TRUE(stream.get() != nullptr);
+
+  // Create the XML reader.
+  CefRefPtr<CefXmlReader> reader(CefXmlReader::Create(
+      stream, XML_ENCODING_NONE, "http://www.example.org/example.xml"));
+  ASSERT_TRUE(reader.get() != nullptr);
+
+  // Move to the processing instruction node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 0);
+  ASSERT_EQ(reader->GetType(), XML_NODE_PROCESSING_INSTRUCTION);
+  ASSERT_EQ(reader->GetLocalName(), "my_instruction");
+  ASSERT_EQ(reader->GetQualifiedName(), "my_instruction");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), "my_value");
+
+  // Move to the DOCTYPE node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 0);
+  ASSERT_EQ(reader->GetType(), XML_NODE_DOCUMENT_TYPE);
+  ASSERT_EQ(reader->GetLocalName(), "my_document");
+  ASSERT_EQ(reader->GetQualifiedName(), "my_document");
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to ns:obj element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 0);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "obj");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:obj");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_TRUE(reader->HasAttributes());
+  ASSERT_EQ(reader->GetAttributeCount(), (size_t)1);
+  ASSERT_EQ(reader->GetAttribute(0), "http://www.example.org/ns");
+  ASSERT_EQ(reader->GetAttribute("xmlns:ns"), "http://www.example.org/ns");
+  ASSERT_EQ(reader->GetAttribute("ns", "http://www.w3.org/2000/xmlns/"),
+            "http://www.example.org/ns");
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objA element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objA");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objA");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the ns:objA value node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_TEXT);
+  ASSERT_EQ(reader->GetLocalName(), "#text");
+  ASSERT_EQ(reader->GetQualifiedName(), "#text");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), "value A");
+
+  // Move to the ns:objA element ending node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "objA");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objA");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the comment node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_COMMENT);
+  ASSERT_EQ(reader->GetLocalName(), "#comment");
+  ASSERT_EQ(reader->GetQualifiedName(), "#comment");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), " my comment ");
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objB element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objB");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objB_1 element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objB_1");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_1");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the ns:objB_1 value node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 3);
+  ASSERT_EQ(reader->GetType(), XML_NODE_TEXT);
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), "value B1");
+
+  // Move to the ns:objB_1 element ending node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "objB_1");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_1");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objB_2 element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objB_2");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_2");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the ns:objB_2 value node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 3);
+  ASSERT_EQ(reader->GetType(), XML_NODE_CDATA);
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), "some <br/> data");
+
+  // Move to the ns:objB_2 element ending node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "objB_2");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_2");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objB_3 element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objB_3");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_3");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the EB entity reference node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 3);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ENTITY_REFERENCE);
+  ASSERT_EQ(reader->GetLocalName(), "EB");
+  ASSERT_EQ(reader->GetQualifiedName(), "EB");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), "EB Value");
+
+  // Move to the ns:objB_3 element ending node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "objB_3");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_3");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objB_4 element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objB_4");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_4");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+  ASSERT_EQ(reader->GetInnerXml(), "<b>this is</b> mixed content &EA;");
+  ASSERT_EQ(reader->GetOuterXml(),
+            "<ns:objB_4 xmlns:ns=\"http://www.example.org/ns\">"
+            "<b>this is</b> mixed content &EA;</ns:objB_4>");
+
+  // Move to the <b> element node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 3);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "b");
+  ASSERT_EQ(reader->GetQualifiedName(), "b");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the text node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 4);
+  ASSERT_EQ(reader->GetType(), XML_NODE_TEXT);
+  ASSERT_EQ(reader->GetLocalName(), "#text");
+  ASSERT_EQ(reader->GetQualifiedName(), "#text");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), "this is");
+
+  // Move to the </b> element node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 3);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "b");
+  ASSERT_EQ(reader->GetQualifiedName(), "b");
+
+  // Move to the text node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 3);
+  ASSERT_EQ(reader->GetType(), XML_NODE_TEXT);
+  ASSERT_EQ(reader->GetLocalName(), "#text");
+  ASSERT_EQ(reader->GetQualifiedName(), "#text");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), " mixed content ");
+
+  // Move to the EA entity reference node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 3);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ENTITY_REFERENCE);
+  ASSERT_EQ(reader->GetLocalName(), "EA");
+  ASSERT_EQ(reader->GetQualifiedName(), "EA");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_EQ(reader->GetValue(), "EA Value");
+
+  // Move to the ns:objB_4 element ending node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "objB_4");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB_4");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objB element ending node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "objB");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objB");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to the ns:objC element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objC");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objC");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_TRUE(reader->IsEmptyElement());
+  ASSERT_TRUE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+  ASSERT_EQ(reader->GetAttributeCount(), (size_t)2);
+  ASSERT_EQ(reader->GetAttribute(0), "value C1");
+  ASSERT_EQ(reader->GetAttribute("ns:attr1"), "value C1");
+  ASSERT_EQ(reader->GetAttribute("attr1", "http://www.example.org/ns"),
+            "value C1");
+  ASSERT_EQ(reader->GetAttribute(1), "value C2");
+  ASSERT_EQ(reader->GetAttribute("ns:attr2"), "value C2");
+  ASSERT_EQ(reader->GetAttribute("attr2", "http://www.example.org/ns"),
+            "value C2");
+
+  // Move to the ns:attr1 attribute.
+  ASSERT_TRUE(reader->MoveToFirstAttribute());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ATTRIBUTE);
+  ASSERT_EQ(reader->GetLocalName(), "attr1");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:attr1");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_EQ(reader->GetValue(), "value C1");
+
+  // Move to the ns:attr2 attribute.
+  ASSERT_TRUE(reader->MoveToNextAttribute());
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ATTRIBUTE);
+  ASSERT_EQ(reader->GetLocalName(), "attr2");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:attr2");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_EQ(reader->GetValue(), "value C2");
+
+  // No more attributes.
+  ASSERT_FALSE(reader->MoveToNextAttribute());
+
+  // Return to the ns:objC element start node.
+  ASSERT_TRUE(reader->MoveToCarryingElement());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objC");
+
+  // Move to the ns:attr1 attribute.
+  ASSERT_TRUE(reader->MoveToAttribute(0));
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ATTRIBUTE);
+  ASSERT_EQ(reader->GetLocalName(), "attr1");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:attr1");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_EQ(reader->GetValue(), "value C1");
+
+  // Return to the ns:objC element start node.
+  ASSERT_TRUE(reader->MoveToCarryingElement());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objC");
+
+  // Move to the ns:attr2 attribute.
+  ASSERT_TRUE(reader->MoveToAttribute("ns:attr2"));
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ATTRIBUTE);
+  ASSERT_EQ(reader->GetLocalName(), "attr2");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:attr2");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_EQ(reader->GetValue(), "value C2");
+
+  // Move to the ns:attr1 attribute without returning to the ns:objC element.
+  ASSERT_TRUE(reader->MoveToAttribute("attr1", "http://www.example.org/ns"));
+  ASSERT_EQ(reader->GetDepth(), 2);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ATTRIBUTE);
+  ASSERT_EQ(reader->GetLocalName(), "attr1");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:attr1");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_TRUE(reader->HasValue());
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_EQ(reader->GetValue(), "value C1");
+
+  // Move to the ns:objD element start node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_START);
+  ASSERT_EQ(reader->GetLocalName(), "objD");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objD");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the ns:objD element end node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 1);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "objD");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:objD");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_FALSE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+
+  // Move to the whitespace node without returning to the ns:objC element.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetType(), XML_NODE_WHITESPACE);
+
+  // Move to ns:obj element ending node.
+  ASSERT_TRUE(reader->MoveToNextNode());
+  ASSERT_EQ(reader->GetDepth(), 0);
+  ASSERT_EQ(reader->GetType(), XML_NODE_ELEMENT_END);
+  ASSERT_EQ(reader->GetLocalName(), "obj");
+  ASSERT_EQ(reader->GetPrefix(), "ns");
+  ASSERT_EQ(reader->GetQualifiedName(), "ns:obj");
+  ASSERT_EQ(reader->GetNamespaceURI(), "http://www.example.org/ns");
+  ASSERT_FALSE(reader->IsEmptyElement());
+  ASSERT_TRUE(reader->HasAttributes());
+  ASSERT_FALSE(reader->HasValue());
+  // Strangely, the end node will report if the starting node has attributes
+  // but will not provide access to them.
+  ASSERT_TRUE(reader->HasAttributes());
+  ASSERT_EQ(reader->GetAttributeCount(), (size_t)0);
+
+  // And we're done.
+  ASSERT_FALSE(reader->MoveToNextNode());
+
+  ASSERT_TRUE(reader->Close());
+}
+
+// Test XML read error handling.
+TEST(XmlReaderTest, ReadError) {
+  char test_str[] =
+      "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
+      "<!ATTRIBUTE foo bar>\n";
+
+  // Create the stream reader.
+  CefRefPtr<CefStreamReader> stream(
+      CefStreamReader::CreateForData(test_str, sizeof(test_str) - 1));
+  ASSERT_TRUE(stream.get() != nullptr);
+
+  // Create the XML reader.
+  CefRefPtr<CefXmlReader> reader(CefXmlReader::Create(
+      stream, XML_ENCODING_NONE, "http://www.example.org/example.xml"));
+  ASSERT_TRUE(reader.get() != nullptr);
+
+  // Move to the processing instruction node and generate parser error.
+  ASSERT_FALSE(reader->MoveToNextNode());
+  ASSERT_TRUE(reader->HasError());
+}
+
+// Test XmlObject load behavior.
+TEST(XmlReaderTest, ObjectLoad) {
+  // Create the stream reader.
+  CefRefPtr<CefStreamReader> stream(
+      CefStreamReader::CreateForData(g_test_xml, sizeof(g_test_xml) - 1));
+  ASSERT_TRUE(stream.get() != nullptr);
+
+  // Create the XML reader.
+  CefRefPtr<CefXmlObject> object(new CefXmlObject("object"));
+  ASSERT_TRUE(object->Load(stream, XML_ENCODING_NONE,
+                           "http://www.example.org/example.xml", nullptr));
+
+  ASSERT_FALSE(object->HasAttributes());
+  ASSERT_TRUE(object->HasChildren());
+  ASSERT_EQ(object->GetChildCount(), (size_t)1);
+
+  CefRefPtr<CefXmlObject> obj(object->FindChild("ns:obj"));
+  ASSERT_TRUE(obj.get());
+  ASSERT_TRUE(obj->HasChildren());
+  ASSERT_EQ(obj->GetChildCount(), (size_t)4);
+
+  CefRefPtr<CefXmlObject> obj_child(obj->FindChild("ns:objC"));
+  ASSERT_TRUE(obj_child.get());
+  ASSERT_EQ(obj_child->GetName(), "ns:objC");
+  ASSERT_FALSE(obj_child->HasChildren());
+  ASSERT_FALSE(obj_child->HasValue());
+  ASSERT_TRUE(obj_child->HasAttributes());
+
+  CefXmlObject::ObjectVector obj_children;
+  ASSERT_EQ(obj->GetChildren(obj_children), (size_t)4);
+  ASSERT_EQ(obj_children.size(), (size_t)4);
+
+  CefXmlObject::ObjectVector::const_iterator it = obj_children.begin();
+  for (int ct = 0; it != obj_children.end(); ++it, ++ct) {
+    obj_child = *it;
+    ASSERT_TRUE(obj_child.get());
+    if (ct == 0) {
+      // ns:objA
+      ASSERT_EQ(obj_child->GetName(), "ns:objA");
+      ASSERT_FALSE(obj_child->HasChildren());
+      ASSERT_TRUE(obj_child->HasValue());
+      ASSERT_FALSE(obj_child->HasAttributes());
+      ASSERT_EQ(obj_child->GetValue(), "value A");
+    } else if (ct == 1) {
+      // ns:objB
+      ASSERT_EQ(obj_child->GetName(), "ns:objB");
+      ASSERT_TRUE(obj_child->HasChildren());
+      ASSERT_FALSE(obj_child->HasValue());
+      ASSERT_FALSE(obj_child->HasAttributes());
+      ASSERT_EQ(obj_child->GetChildCount(), (size_t)4);
+      obj_child = obj_child->FindChild("ns:objB_4");
+      ASSERT_TRUE(obj_child.get());
+      ASSERT_TRUE(obj_child->HasValue());
+      ASSERT_EQ(obj_child->GetValue(), "<b>this is</b> mixed content EA Value");
+    } else if (ct == 2) {
+      // ns:objC
+      ASSERT_EQ(obj_child->GetName(), "ns:objC");
+      ASSERT_FALSE(obj_child->HasChildren());
+      ASSERT_FALSE(obj_child->HasValue());
+      ASSERT_TRUE(obj_child->HasAttributes());
+
+      CefXmlObject::AttributeMap attribs;
+      ASSERT_EQ(obj_child->GetAttributes(attribs), (size_t)2);
+      ASSERT_EQ(attribs.size(), (size_t)2);
+      ASSERT_EQ(attribs["ns:attr1"], "value C1");
+      ASSERT_EQ(attribs["ns:attr2"], "value C2");
+
+      ASSERT_EQ(obj_child->GetAttributeCount(), (size_t)2);
+      ASSERT_TRUE(obj_child->HasAttribute("ns:attr1"));
+      ASSERT_EQ(obj_child->GetAttributeValue("ns:attr1"), "value C1");
+      ASSERT_TRUE(obj_child->HasAttribute("ns:attr2"));
+      ASSERT_EQ(obj_child->GetAttributeValue("ns:attr2"), "value C2");
+    } else if (ct == 3) {
+      // ns:objD
+      ASSERT_EQ(obj_child->GetName(), "ns:objD");
+      ASSERT_FALSE(obj_child->HasChildren());
+      ASSERT_FALSE(obj_child->HasValue());
+      ASSERT_FALSE(obj_child->HasAttributes());
+    }
+  }
+}
+
+// Test XmlObject load error handling behavior.
+TEST(XmlReaderTest, ObjectLoadError) {
+  // Test start/end tag mismatch error.
+  {
+    char error_xml[] = "<obj>\n<foo>\n</obj>\n</foo>";
+
+    // Create the stream reader.
+    CefRefPtr<CefStreamReader> stream(
+        CefStreamReader::CreateForData(error_xml, sizeof(error_xml) - 1));
+    ASSERT_TRUE(stream.get() != nullptr);
+
+    CefString error_str;
+
+    // Create the XML reader.
+    CefRefPtr<CefXmlObject> object(new CefXmlObject("object"));
+    ASSERT_FALSE(object->Load(stream, XML_ENCODING_NONE,
+                              "http://www.example.org/example.xml",
+                              &error_str));
+    ASSERT_EQ(error_str,
+              "Opening and ending tag mismatch: foo line 2 and obj, line 3");
+  }
+
+  // Test value following child error.
+  {
+    char error_xml[] = "<obj>\n<foo>\n</foo>disallowed value\n</obj>";
+
+    // Create the stream reader.
+    CefRefPtr<CefStreamReader> stream(
+        CefStreamReader::CreateForData(error_xml, sizeof(error_xml) - 1));
+    ASSERT_TRUE(stream.get() != nullptr);
+
+    CefString error_str;
+
+    // Create the XML reader.
+    CefRefPtr<CefXmlObject> object(new CefXmlObject("object"));
+    ASSERT_FALSE(object->Load(stream, XML_ENCODING_NONE,
+                              "http://www.example.org/example.xml",
+                              &error_str));
+    ASSERT_EQ(error_str, "Value following child element, line 4");
+  }
+}
diff --git a/src/tests/ceftests/zip_reader_unittest.cc b/src/tests/ceftests/zip_reader_unittest.cc
new file mode 100644
index 0000000..7181bff
--- /dev/null
+++ b/src/tests/ceftests/zip_reader_unittest.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "include/cef_stream.h"
+#include "include/cef_zip_reader.h"
+#include "include/wrapper/cef_zip_archive.h"
+#include "tests/gtest/include/gtest/gtest.h"
+
+namespace {
+
+unsigned char g_test_zip[] = {
+    0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x7f,
+    0x57, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61,
+    0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x0a,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x7f, 0x57, 0x3d, 0xf8, 0x47, 0x0c,
+    0xc6, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
+    0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76,
+    0x65, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x2e, 0x74, 0x78, 0x74,
+    0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20,
+    0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x2e, 0x50, 0x4b, 0x03, 0x04, 0x0a,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x7f, 0x57, 0x3d, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
+    0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76,
+    0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31, 0x2f, 0x50,
+    0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x7f, 0x57,
+    0x3d, 0x43, 0xe3, 0x11, 0x5f, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
+    0x00, 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72,
+    0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72,
+    0x20, 0x31, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x61, 0x2e, 0x74,
+    0x78, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f,
+    0x66, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x41, 0x2e, 0x50, 0x4b,
+    0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x7f, 0x57, 0x3d,
+    0x80, 0xb0, 0x3c, 0x74, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+    0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63,
+    0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20,
+    0x31, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x62, 0x2e, 0x74, 0x78,
+    0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66,
+    0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x42, 0x2e, 0x50, 0x4b, 0x03,
+    0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x7f, 0x57, 0x3d, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+    0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68,
+    0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31,
+    0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31, 0x61, 0x2f, 0x50,
+    0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7f, 0x57,
+    0x3d, 0x15, 0xed, 0x04, 0x2c, 0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00,
+    0x00, 0x2c, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72,
+    0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72,
+    0x20, 0x31, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31, 0x61,
+    0x2f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x61, 0x31, 0x2e, 0x74, 0x78,
+    0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66,
+    0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x41, 0x31, 0x2e, 0x50, 0x4b,
+    0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x7f, 0x57, 0x3d,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x16, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63,
+    0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20,
+    0x32, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x02, 0x80, 0x57, 0x3d, 0x1a, 0x5d, 0x57, 0x5d, 0x14, 0x00, 0x00, 0x00,
+    0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74,
+    0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c,
+    0x64, 0x65, 0x72, 0x20, 0x32, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x32,
+    0x61, 0x2e, 0x74, 0x78, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+    0x73, 0x20, 0x6f, 0x66, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x32, 0x41,
+    0x2e, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x67, 0x7f, 0x57, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74,
+    0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f,
+    0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x70, 0x7f, 0x57, 0x3d, 0xf8, 0x47, 0x0c, 0xc6, 0x13, 0x00, 0x00, 0x00,
+    0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x74, 0x65,
+    0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66,
+    0x69, 0x6c, 0x65, 0x20, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b, 0x01,
+    0x02, 0x14, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x7f, 0x57,
+    0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+    0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f,
+    0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64,
+    0x65, 0x72, 0x20, 0x31, 0x2f, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x0a,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x7f, 0x57, 0x3d, 0x43, 0xe3, 0x11,
+    0x5f, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xa7,
+    0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68,
+    0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31,
+    0x2f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x31, 0x61, 0x2e, 0x74, 0x78, 0x74,
+    0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x78, 0x7f, 0x57, 0x3d, 0x80, 0xb0, 0x3c, 0x74, 0x14, 0x00, 0x00, 0x00,
+    0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x74, 0x65,
+    0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66,
+    0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31, 0x2f, 0x66, 0x69, 0x6c, 0x65,
+    0x20, 0x31, 0x62, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b, 0x01, 0x02, 0x14,
+    0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x7f, 0x57, 0x3d, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+    0x00, 0x4d, 0x01, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72,
+    0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72,
+    0x20, 0x31, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31, 0x61,
+    0x2f, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x7c, 0x7f, 0x57, 0x3d, 0x15, 0xed, 0x04, 0x2c, 0x15, 0x00, 0x00,
+    0x00, 0x15, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x8b, 0x01, 0x00, 0x00, 0x74,
+    0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f,
+    0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x31, 0x2f, 0x66, 0x6f, 0x6c,
+    0x64, 0x65, 0x72, 0x20, 0x31, 0x61, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x20,
+    0x31, 0x61, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b, 0x01, 0x02, 0x14,
+    0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x7f, 0x57, 0x3d, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+    0x00, 0xea, 0x01, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72,
+    0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72,
+    0x20, 0x32, 0x2f, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x0a, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x02, 0x80, 0x57, 0x3d, 0x1a, 0x5d, 0x57, 0x5d, 0x14,
+    0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00,
+    0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76,
+    0x65, 0x2f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x32, 0x2f, 0x66,
+    0x69, 0x6c, 0x65, 0x20, 0x32, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b,
+    0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x09, 0x00, 0x9d, 0x02,
+    0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+}  // namespace
+
+// Test Zip reading.
+TEST(ZipReaderTest, Read) {
+  // Create the stream reader.
+  CefRefPtr<CefStreamReader> stream(
+      CefStreamReader::CreateForData(g_test_zip, sizeof(g_test_zip) - 1));
+  ASSERT_TRUE(stream.get() != nullptr);
+
+  // Create the Zip reader.
+  CefRefPtr<CefZipReader> reader(CefZipReader::Create(stream));
+  ASSERT_TRUE(reader.get() != nullptr);
+
+  char buff[25];
+
+  // Walk through the archive contents.
+  ASSERT_TRUE(reader->MoveToFirstFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/");
+  ASSERT_EQ(reader->GetFileSize(), 0);
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/file 1.txt");
+  ASSERT_EQ(reader->GetFileSize(), 19);
+  ASSERT_TRUE(reader->OpenFile(""));
+  ASSERT_EQ(reader->ReadFile(buff, sizeof(buff)), 19);
+  ASSERT_TRUE(!strncmp(buff, "Contents of file 1.", 19));
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/folder 1/");
+  ASSERT_EQ(reader->GetFileSize(), 0);
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/folder 1/file 1a.txt");
+  ASSERT_EQ(reader->GetFileSize(), 20);
+  ASSERT_TRUE(reader->OpenFile(""));
+  ASSERT_EQ(reader->ReadFile(buff, sizeof(buff)), 20);
+  ASSERT_TRUE(reader->CloseFile());
+  ASSERT_TRUE(!strncmp(buff, "Contents of file 1A.", 20));
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/folder 1/file 1b.txt");
+  ASSERT_EQ(reader->GetFileSize(), 20);
+  ASSERT_TRUE(reader->OpenFile(""));
+  ASSERT_EQ(reader->ReadFile(buff, sizeof(buff)), 20);
+  ASSERT_TRUE(reader->CloseFile());
+  ASSERT_TRUE(!strncmp(buff, "Contents of file 1B.", 20));
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/folder 1/folder 1a/");
+  ASSERT_EQ(reader->GetFileSize(), 0);
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(),
+            "test_archive/folder 1/folder 1a/file 1a1.txt");
+  ASSERT_EQ(reader->GetFileSize(), 21);
+  ASSERT_TRUE(reader->OpenFile(""));
+  ASSERT_EQ(reader->ReadFile(buff, sizeof(buff)), 21);
+  ASSERT_TRUE(reader->CloseFile());
+  ASSERT_TRUE(!strncmp(buff, "Contents of file 1A1.", 21));
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/folder 2/");
+  ASSERT_EQ(reader->GetFileSize(), 0);
+
+  ASSERT_TRUE(reader->MoveToNextFile());
+  ASSERT_EQ(reader->GetFileName(), "test_archive/folder 2/file 2a.txt");
+  ASSERT_EQ(reader->GetFileSize(), 20);
+  ASSERT_TRUE(reader->OpenFile(""));
+  ASSERT_EQ(reader->ReadFile(buff, sizeof(buff)), 20);
+  ASSERT_TRUE(reader->CloseFile());
+  ASSERT_TRUE(!strncmp(buff, "Contents of file 2A.", 20));
+
+  ASSERT_FALSE(reader->MoveToNextFile());
+
+  // Try seeking a particular file
+  ASSERT_TRUE(reader->MoveToFile("TEST_ARCHIVE/FOLDER 1/FILE 1B.TXT", false));
+  ASSERT_EQ(reader->GetFileName(), "test_archive/folder 1/file 1b.txt");
+  ASSERT_EQ(reader->GetFileSize(), 20);
+  ASSERT_TRUE(reader->OpenFile(""));
+  ASSERT_EQ(reader->ReadFile(buff, sizeof(buff)), 20);
+  ASSERT_TRUE(reader->CloseFile());
+  ASSERT_TRUE(!strncmp(buff, "Contents of file 1B.", 20));
+
+  ASSERT_TRUE(reader->MoveToFile("test_archive/folder 1/file 1b.txt", true));
+  ASSERT_FALSE(reader->MoveToFile("test_archive/folder 1/FILE 1B.txt", true));
+
+  ASSERT_TRUE(reader->Close());
+}
+
+// Test CefZipArchive object.
+TEST(ZipReaderTest, ReadArchive) {
+  // Create the stream reader.
+  CefRefPtr<CefStreamReader> stream(
+      CefStreamReader::CreateForData(g_test_zip, sizeof(g_test_zip) - 1));
+  ASSERT_TRUE(stream.get() != nullptr);
+
+  // Create the Zip archive object.
+  CefRefPtr<CefZipArchive> archive(new CefZipArchive());
+
+  ASSERT_EQ(archive->Load(stream, CefString(), false), (size_t)5);
+
+  ASSERT_TRUE(archive->HasFile("test_archive/file 1.txt"));
+  ASSERT_TRUE(archive->HasFile("test_archive/folder 1/file 1a.txt"));
+  ASSERT_TRUE(archive->HasFile("test_archive/FOLDER 1/file 1b.txt"));
+  ASSERT_TRUE(archive->HasFile("test_archive/folder 1/folder 1a/file 1a1.txt"));
+  ASSERT_TRUE(archive->HasFile("test_archive/folder 2/file 2a.txt"));
+
+  // Test content retrieval.
+  CefRefPtr<CefZipArchive::File> file;
+  file = archive->GetFile("test_archive/folder 2/file 2a.txt");
+  ASSERT_TRUE(file.get());
+
+  ASSERT_EQ(file->GetDataSize(), (size_t)20);
+  ASSERT_TRUE(!strncmp(reinterpret_cast<const char*>(file->GetData()),
+                       "Contents of file 2A.", 20));
+
+  // Test stream reading.
+  CefRefPtr<CefStreamReader> reader(file->GetStreamReader());
+  ASSERT_TRUE(reader.get());
+
+  char buff[8];
+  ASSERT_EQ(reader->Read(buff, 1, 8), (size_t)8);
+  ASSERT_TRUE(!strncmp(buff, "Contents", 8));
+  ASSERT_EQ(reader->Read(buff, 1, 8), (size_t)8);
+  ASSERT_TRUE(!strncmp(buff, " of file", 8));
+  ASSERT_EQ(reader->Read(buff, 1, 8), (size_t)4);
+  ASSERT_TRUE(!strncmp(buff, " 2A.", 4));
+  ASSERT_TRUE(reader->Eof());
+}
diff --git a/src/tests/gtest/CMakeLists.txt.in b/src/tests/gtest/CMakeLists.txt.in
new file mode 100644
index 0000000..32fcc87
--- /dev/null
+++ b/src/tests/gtest/CMakeLists.txt.in
@@ -0,0 +1,30 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+set(CEF_TARGET "cef_gtest")
+
+set(GTEST_SRCS
+  src/gtest-all.cc
+  )
+source_group(cef_gtest FILES ${GTEST_SRCS})
+
+add_library(${CEF_TARGET} ${GTEST_SRCS})
+
+# Start with CEF default properties.
+SET_LIBRARY_TARGET_PROPERTIES(${CEF_TARGET})
+
+# The gtest-all.cc file uses #include "gtest/gtest.h"
+target_include_directories(${CEF_TARGET} PRIVATE "include")
+
+# In order to allow regex matches in gtest to be shared between Windows
+# and other systems we tell gtest to always use it's internal engine.
+target_compile_definitions(${CEF_TARGET} PRIVATE -DGTEST_HAS_POSIX_RE=0 -DGTEST_LANG_CXX11=1)
+
+# All dependent targets are unit tests.
+target_compile_definitions(${CEF_TARGET} PUBLIC -DUNIT_TEST)
+
+if(OS_WINDOWS)
+  # Disable unused variable warning.
+  target_compile_options(${CEF_TARGET} PRIVATE "/wd4800")
+endif()
diff --git a/src/tests/gtest/README.cef.in b/src/tests/gtest/README.cef.in
new file mode 100644
index 0000000..5b0b472
--- /dev/null
+++ b/src/tests/gtest/README.cef.in
@@ -0,0 +1,14 @@
+Name: Google C++ Testing Framework
+Short Name: gtest
+URL: https://github.com/google/googletest
+License: BSD (see LICENSE file)
+
+Description:
+Fuzed (single header/source file) version of GoogleTest. Generated from the
+Chromium source checkout with the following command:
+
+cd /path/to/chromium/src/third_party/googletest/src/googletest/scripts
+./fuse_gtest_files.py <out_directory>
+
+Local Modifications:
+None.
diff --git a/src/tests/gtest/include/gtest/gtest.h b/src/tests/gtest/include/gtest/gtest.h
new file mode 100644
index 0000000..aece904
--- /dev/null
+++ b/src/tests/gtest/include/gtest/gtest.h
@@ -0,0 +1,9 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+// Include GoogleTest from the Chromium source location. This file will be
+// re-written in the CEF binary distribution to include a fuzed version of
+// GoogleTest from the distribution tests/ folder.
+
+#include "testing/gtest/include/gtest/gtest.h"
diff --git a/src/tests/shared/browser/client_app_browser.cc b/src/tests/shared/browser/client_app_browser.cc
new file mode 100644
index 0000000..adf2fff
--- /dev/null
+++ b/src/tests/shared/browser/client_app_browser.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/client_app_browser.h"
+
+#include "include/base/cef_logging.h"
+#include "include/cef_cookie.h"
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+
+ClientAppBrowser::ClientAppBrowser() {
+  CreateDelegates(delegates_);
+}
+
+void ClientAppBrowser::OnBeforeCommandLineProcessing(
+    const CefString& process_type,
+    CefRefPtr<CefCommandLine> command_line) {
+  // Pass additional command-line flags to the browser process.
+  if (process_type.empty()) {
+    // Pass additional command-line flags when off-screen rendering is enabled.
+    if (command_line->HasSwitch(switches::kOffScreenRenderingEnabled) &&
+        !command_line->HasSwitch(switches::kSharedTextureEnabled)) {
+      // Use software rendering and compositing (disable GPU) for increased FPS
+      // and decreased CPU usage. This will also disable WebGL so remove these
+      // switches if you need that capability.
+      // See https://bitbucket.org/chromiumembedded/cef/issues/1257 for details.
+      if (!command_line->HasSwitch(switches::kEnableGPU)) {
+        command_line->AppendSwitch("disable-gpu");
+        command_line->AppendSwitch("disable-gpu-compositing");
+      }
+    }
+
+    if (command_line->HasSwitch(switches::kUseViews) &&
+        !command_line->HasSwitch("top-chrome-md")) {
+      // Use non-material mode on all platforms by default. Among other things
+      // this causes menu buttons to show hover state. See usage of
+      // MaterialDesignController::IsModeMaterial() in Chromium code.
+      command_line->AppendSwitchWithValue("top-chrome-md", "non-material");
+    }
+
+    if (!command_line->HasSwitch(switches::kCachePath) &&
+        !command_line->HasSwitch("disable-gpu-shader-disk-cache")) {
+      // Don't create a "GPUCache" directory when cache-path is unspecified.
+      command_line->AppendSwitch("disable-gpu-shader-disk-cache");
+    }
+
+#if defined(OS_MACOSX)
+    // Disable the toolchain prompt on macOS.
+    command_line->AppendSwitch("use-mock-keychain");
+#endif
+
+    DelegateSet::iterator it = delegates_.begin();
+    for (; it != delegates_.end(); ++it)
+      (*it)->OnBeforeCommandLineProcessing(this, command_line);
+  }
+}
+
+void ClientAppBrowser::OnContextInitialized() {
+  if (!cookieable_schemes_.empty()) {
+    // Register cookieable schemes with the global cookie manager.
+    CefRefPtr<CefCookieManager> manager =
+        CefCookieManager::GetGlobalManager(nullptr);
+    DCHECK(manager.get());
+    manager->SetSupportedSchemes(cookieable_schemes_, true, nullptr);
+  }
+
+  print_handler_ = CreatePrintHandler();
+
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnContextInitialized(this);
+}
+
+void ClientAppBrowser::OnBeforeChildProcessLaunch(
+    CefRefPtr<CefCommandLine> command_line) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnBeforeChildProcessLaunch(this, command_line);
+}
+
+void ClientAppBrowser::OnRenderProcessThreadCreated(
+    CefRefPtr<CefListValue> extra_info) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnRenderProcessThreadCreated(this, extra_info);
+}
+
+void ClientAppBrowser::OnScheduleMessagePumpWork(int64 delay) {
+  // Only used when `--external-message-pump` is passed via the command-line.
+  MainMessageLoopExternalPump* message_pump =
+      MainMessageLoopExternalPump::Get();
+  if (message_pump)
+    message_pump->OnScheduleMessagePumpWork(delay);
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/client_app_browser.h b/src/tests/shared/browser/client_app_browser.h
new file mode 100644
index 0000000..1f25994
--- /dev/null
+++ b/src/tests/shared/browser/client_app_browser.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_CLIENT_APP_BROWSER_H_
+#define CEF_TESTS_SHARED_BROWSER_CLIENT_APP_BROWSER_H_
+#pragma once
+
+#include <set>
+
+#include "tests/shared/common/client_app.h"
+
+namespace client {
+
+// Client app implementation for the browser process.
+class ClientAppBrowser : public ClientApp, public CefBrowserProcessHandler {
+ public:
+  // Interface for browser delegates. All Delegates must be returned via
+  // CreateDelegates. Do not perform work in the Delegate
+  // constructor. See CefBrowserProcessHandler for documentation.
+  class Delegate : public virtual CefBaseRefCounted {
+   public:
+    virtual void OnBeforeCommandLineProcessing(
+        CefRefPtr<ClientAppBrowser> app,
+        CefRefPtr<CefCommandLine> command_line) {}
+
+    virtual void OnContextInitialized(CefRefPtr<ClientAppBrowser> app) {}
+
+    virtual void OnBeforeChildProcessLaunch(
+        CefRefPtr<ClientAppBrowser> app,
+        CefRefPtr<CefCommandLine> command_line) {}
+
+    virtual void OnRenderProcessThreadCreated(
+        CefRefPtr<ClientAppBrowser> app,
+        CefRefPtr<CefListValue> extra_info) {}
+  };
+
+  typedef std::set<CefRefPtr<Delegate>> DelegateSet;
+
+  ClientAppBrowser();
+
+ private:
+  // Creates all of the Delegate objects. Implemented by cefclient in
+  // client_app_delegates_browser.cc
+  static void CreateDelegates(DelegateSet& delegates);
+
+  // Create the Linux print handler. Implemented by cefclient in
+  // client_app_delegates_browser.cc
+  static CefRefPtr<CefPrintHandler> CreatePrintHandler();
+
+  // CefApp methods.
+  void OnBeforeCommandLineProcessing(
+      const CefString& process_type,
+      CefRefPtr<CefCommandLine> command_line) OVERRIDE;
+  CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE {
+    return this;
+  }
+
+  // CefBrowserProcessHandler methods.
+  void OnContextInitialized() OVERRIDE;
+  void OnBeforeChildProcessLaunch(
+      CefRefPtr<CefCommandLine> command_line) OVERRIDE;
+  void OnRenderProcessThreadCreated(
+      CefRefPtr<CefListValue> extra_info) OVERRIDE;
+  CefRefPtr<CefPrintHandler> GetPrintHandler() OVERRIDE {
+    return print_handler_;
+  }
+  void OnScheduleMessagePumpWork(int64 delay) OVERRIDE;
+
+  // Set of supported Delegates.
+  DelegateSet delegates_;
+
+  CefRefPtr<CefPrintHandler> print_handler_;
+
+  IMPLEMENT_REFCOUNTING(ClientAppBrowser);
+  DISALLOW_COPY_AND_ASSIGN(ClientAppBrowser);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_CLIENT_APP_BROWSER_H_
diff --git a/src/tests/shared/browser/extension_util.cc b/src/tests/shared/browser/extension_util.cc
new file mode 100644
index 0000000..5d2e535
--- /dev/null
+++ b/src/tests/shared/browser/extension_util.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/extension_util.h"
+
+#include "include/base/cef_bind.h"
+#include "include/cef_parser.h"
+#include "include/cef_path_util.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "tests/shared/browser/file_util.h"
+#include "tests/shared/browser/resource_util.h"
+
+namespace client {
+namespace extension_util {
+
+namespace {
+
+std::string GetResourcesPath() {
+  CefString resources_dir;
+  if (CefGetPath(PK_DIR_RESOURCES, resources_dir) && !resources_dir.empty()) {
+    return resources_dir.ToString() + file_util::kPathSep;
+  }
+  return std::string();
+}
+
+// Internal extension paths may be prefixed with PK_DIR_RESOURCES and always
+// use forward slash as path separator.
+std::string GetInternalPath(const std::string& extension_path) {
+  std::string resources_path_lower = GetResourcesPath();
+  std::string extension_path_lower = extension_path;
+
+#if defined(OS_WIN)
+  // Convert to lower-case, since Windows paths are case-insensitive.
+  std::transform(resources_path_lower.begin(), resources_path_lower.end(),
+                 resources_path_lower.begin(), ::tolower);
+  std::transform(extension_path_lower.begin(), extension_path_lower.end(),
+                 extension_path_lower.begin(), ::tolower);
+#endif
+
+  std::string internal_path;
+  if (!resources_path_lower.empty() &&
+      extension_path_lower.find(resources_path_lower) == 0U) {
+    internal_path = extension_path.substr(resources_path_lower.size());
+  } else {
+    internal_path = extension_path;
+  }
+
+#if defined(OS_WIN)
+  // Normalize path separators.
+  std::replace(internal_path.begin(), internal_path.end(), '\\', '/');
+#endif
+
+  return internal_path;
+}
+
+typedef base::Callback<void(CefRefPtr<CefDictionaryValue> /*manifest*/)>
+    ManifestCallback;
+
+void RunManifestCallback(const ManifestCallback& callback,
+                         CefRefPtr<CefDictionaryValue> manifest) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the browser UI thread.
+    CefPostTask(TID_UI, base::Bind(RunManifestCallback, callback, manifest));
+    return;
+  }
+  callback.Run(manifest);
+}
+
+// Asynchronously reads the manifest and executes |callback| on the UI thread.
+void GetInternalManifest(const std::string& extension_path,
+                         const ManifestCallback& callback) {
+  if (!CefCurrentlyOn(TID_FILE)) {
+    // Execute on the browser FILE thread.
+    CefPostTask(TID_FILE,
+                base::Bind(GetInternalManifest, extension_path, callback));
+    return;
+  }
+
+  const std::string& manifest_path = GetInternalExtensionResourcePath(
+      file_util::JoinPath(extension_path, "manifest.json"));
+  std::string manifest_contents;
+  if (!LoadBinaryResource(manifest_path.c_str(), manifest_contents) ||
+      manifest_contents.empty()) {
+    LOG(ERROR) << "Failed to load manifest from " << manifest_path;
+    RunManifestCallback(callback, nullptr);
+    return;
+  }
+
+  cef_json_parser_error_t error_code;
+  CefString error_msg;
+  CefRefPtr<CefValue> value = CefParseJSONAndReturnError(
+      manifest_contents, JSON_PARSER_RFC, error_code, error_msg);
+  if (!value || value->GetType() != VTYPE_DICTIONARY) {
+    if (error_msg.empty())
+      error_msg = "Incorrectly formatted dictionary contents.";
+    LOG(ERROR) << "Failed to parse manifest from " << manifest_path << "; "
+               << error_msg.ToString();
+    RunManifestCallback(callback, nullptr);
+    return;
+  }
+
+  RunManifestCallback(callback, value->GetDictionary());
+}
+
+void LoadExtensionWithManifest(CefRefPtr<CefRequestContext> request_context,
+                               const std::string& extension_path,
+                               CefRefPtr<CefExtensionHandler> handler,
+                               CefRefPtr<CefDictionaryValue> manifest) {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Load the extension internally. Resource requests will be handled via
+  // AddInternalExtensionToResourceManager.
+  request_context->LoadExtension(extension_path, manifest, handler);
+}
+
+}  // namespace
+
+bool IsInternalExtension(const std::string& extension_path) {
+  // List of internally handled extensions.
+  static const char* extensions[] = {"set_page_color"};
+
+  const std::string& internal_path = GetInternalPath(extension_path);
+  for (size_t i = 0; i < arraysize(extensions); ++i) {
+    // Exact match or first directory component.
+    const std::string& extension = extensions[i];
+    if (internal_path == extension ||
+        internal_path.find(extension + '/') == 0) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+std::string GetInternalExtensionResourcePath(
+    const std::string& extension_path) {
+  return "extensions/" + GetInternalPath(extension_path);
+}
+
+std::string GetExtensionResourcePath(const std::string& extension_path,
+                                     bool* internal) {
+  const bool is_internal = IsInternalExtension(extension_path);
+  if (internal)
+    *internal = is_internal;
+  if (is_internal)
+    return GetInternalExtensionResourcePath(extension_path);
+  return extension_path;
+}
+
+bool GetExtensionResourceContents(const std::string& extension_path,
+                                  std::string& contents) {
+  CEF_REQUIRE_FILE_THREAD();
+
+  if (IsInternalExtension(extension_path)) {
+    const std::string& contents_path =
+        GetInternalExtensionResourcePath(extension_path);
+    return LoadBinaryResource(contents_path.c_str(), contents);
+  }
+
+  return file_util::ReadFileToString(extension_path, &contents);
+}
+
+void LoadExtension(CefRefPtr<CefRequestContext> request_context,
+                   const std::string& extension_path,
+                   CefRefPtr<CefExtensionHandler> handler) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute on the browser UI thread.
+    CefPostTask(TID_UI, base::Bind(LoadExtension, request_context,
+                                   extension_path, handler));
+    return;
+  }
+
+  if (IsInternalExtension(extension_path)) {
+    // Read the extension manifest and load asynchronously.
+    GetInternalManifest(extension_path,
+                        base::Bind(LoadExtensionWithManifest, request_context,
+                                   extension_path, handler));
+  } else {
+    // Load the extension from disk.
+    request_context->LoadExtension(extension_path, nullptr, handler);
+  }
+}
+
+void AddInternalExtensionToResourceManager(
+    CefRefPtr<CefExtension> extension,
+    CefRefPtr<CefResourceManager> resource_manager) {
+  DCHECK(IsInternalExtension(extension->GetPath()));
+
+  if (!CefCurrentlyOn(TID_IO)) {
+    // Execute on the browser IO thread.
+    CefPostTask(TID_IO, base::Bind(AddInternalExtensionToResourceManager,
+                                   extension, resource_manager));
+    return;
+  }
+
+  const std::string& origin = GetExtensionOrigin(extension->GetIdentifier());
+  const std::string& resource_path =
+      GetInternalExtensionResourcePath(extension->GetPath());
+
+// Add provider for bundled resource files.
+#if defined(OS_WIN)
+  // Read resources from the binary.
+  resource_manager->AddProvider(
+      CreateBinaryResourceProvider(origin, resource_path), 50, std::string());
+#elif defined(OS_POSIX)
+  // Read resources from a directory on disk.
+  std::string resource_dir;
+  if (GetResourceDir(resource_dir)) {
+    resource_dir += "/" + resource_path;
+    resource_manager->AddDirectoryProvider(origin, resource_dir, 50,
+                                           std::string());
+  }
+#endif
+}
+
+std::string GetExtensionOrigin(const std::string& extension_id) {
+  return "chrome-extension://" + extension_id + "/";
+}
+
+std::string GetExtensionURL(CefRefPtr<CefExtension> extension) {
+  CefRefPtr<CefDictionaryValue> browser_action =
+      extension->GetManifest()->GetDictionary("browser_action");
+  if (browser_action) {
+    const std::string& default_popup =
+        browser_action->GetString("default_popup");
+    if (!default_popup.empty())
+      return GetExtensionOrigin(extension->GetIdentifier()) + default_popup;
+  }
+
+  return std::string();
+}
+
+std::string GetExtensionIconPath(CefRefPtr<CefExtension> extension,
+                                 bool* internal) {
+  CefRefPtr<CefDictionaryValue> browser_action =
+      extension->GetManifest()->GetDictionary("browser_action");
+  if (browser_action) {
+    const std::string& default_icon = browser_action->GetString("default_icon");
+    if (!default_icon.empty()) {
+      return GetExtensionResourcePath(
+          file_util::JoinPath(extension->GetPath(), default_icon), internal);
+    }
+  }
+
+  return std::string();
+}
+
+}  // namespace extension_util
+}  // namespace client
diff --git a/src/tests/shared/browser/extension_util.h b/src/tests/shared/browser/extension_util.h
new file mode 100644
index 0000000..f3e643a
--- /dev/null
+++ b/src/tests/shared/browser/extension_util.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_
+#define CEF_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_
+#pragma once
+
+#include <string>
+
+#include "include/cef_extension.h"
+#include "include/cef_extension_handler.h"
+#include "include/wrapper/cef_resource_manager.h"
+
+namespace client {
+namespace extension_util {
+
+// Returns true if |extension_path| can be handled internally via
+// LoadBinaryResource. This checks a hard-coded list of allowed extension path
+// components.
+bool IsInternalExtension(const std::string& extension_path);
+
+// Returns the path relative to the resource directory after removing the
+// PK_DIR_RESOURCES prefix. This will be the relative path expected by
+// LoadBinaryResource (uses '/' as path separator on all platforms). Only call
+// this method for internal extensions, either when IsInternalExtension returns
+// true or when the extension is handled internally through some means other
+// than LoadBinaryResource. Use GetExtensionResourcePath instead if you are
+// unsure whether the extension is internal or external.
+std::string GetInternalExtensionResourcePath(const std::string& extension_path);
+
+// Returns the resource path for |extension_path|. For external extensions this
+// will be the full file path on disk. For internal extensions this will be the
+// relative path expected by LoadBinaryResource (uses '/' as path separator on
+// all platforms). Internal extensions must be on the hard-coded list enforced
+// by IsInternalExtension. If |internal| is non-NULL it will be set to true if
+// the extension is handled internally.
+std::string GetExtensionResourcePath(const std::string& extension_path,
+                                     bool* internal);
+
+// Read the contents of |extension_path| into |contents|. For external
+// extensions this will read the file from disk. For internal extensions this
+// will call LoadBinaryResource. Internal extensions must be on the hard-coded
+// list enforced by IsInternalExtension. Returns true on success. Must be
+// called on the FILE thread.
+bool GetExtensionResourceContents(const std::string& extension_path,
+                                  std::string& contents);
+
+// Load |extension_path| in |request_context|. May be an internal or external
+// extension. Internal extensions must be on the hard-coded list enforced by
+// IsInternalExtension.
+void LoadExtension(CefRefPtr<CefRequestContext> request_context,
+                   const std::string& extension_path,
+                   CefRefPtr<CefExtensionHandler> handler);
+
+// Register an internal handler for extension resources. Internal extensions
+// must be on the hard-coded list enforced by IsInternalExtension.
+void AddInternalExtensionToResourceManager(
+    CefRefPtr<CefExtension> extension,
+    CefRefPtr<CefResourceManager> resource_manager);
+
+// Returns the URL origin for |extension_id|.
+std::string GetExtensionOrigin(const std::string& extension_id);
+
+// Parse browser_action manifest values as defined at
+// https://developer.chrome.com/extensions/browserAction
+
+// Look for a browser_action.default_popup manifest value.
+std::string GetExtensionURL(CefRefPtr<CefExtension> extension);
+
+// Look for a browser_action.default_icon manifest value and return the resource
+// path. If |internal| is non-NULL it will be set to true if the extension is
+// handled internally.
+std::string GetExtensionIconPath(CefRefPtr<CefExtension> extension,
+                                 bool* internal);
+
+}  // namespace extension_util
+}  // namespace client
+
+#endif  // CEF_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_
diff --git a/src/tests/shared/browser/file_util.cc b/src/tests/shared/browser/file_util.cc
new file mode 100644
index 0000000..73c4159
--- /dev/null
+++ b/src/tests/shared/browser/file_util.cc
@@ -0,0 +1,121 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2012 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "tests/shared/browser/file_util.h"
+
+#include "include/base/cef_build.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_task.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <memory>
+
+namespace client {
+namespace file_util {
+
+namespace {
+
+bool AllowFileIO() {
+  if (CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO)) {
+    NOTREACHED() << "file IO is not allowed on the current thread";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+#if defined(OS_WIN)
+const char kPathSep = '\\';
+#else
+const char kPathSep = '/';
+#endif
+
+bool ReadFileToString(const std::string& path,
+                      std::string* contents,
+                      size_t max_size) {
+  if (!AllowFileIO())
+    return false;
+
+  if (contents)
+    contents->clear();
+  FILE* file = fopen(path.c_str(), "rb");
+  if (!file)
+    return false;
+
+  const size_t kBufferSize = 1 << 16;
+  scoped_ptr<char[]> buf(new char[kBufferSize]);
+  size_t len;
+  size_t size = 0;
+  bool read_status = true;
+
+  // Many files supplied in |path| have incorrect size (proc files etc).
+  // Hence, the file is read sequentially as opposed to a one-shot read.
+  while ((len = fread(buf.get(), 1, kBufferSize, file)) > 0) {
+    if (contents)
+      contents->append(buf.get(), std::min(len, max_size - size));
+
+    if ((max_size - size) < len) {
+      read_status = false;
+      break;
+    }
+
+    size += len;
+  }
+  read_status = read_status && !ferror(file);
+  fclose(file);
+
+  return read_status;
+}
+
+int WriteFile(const std::string& path, const char* data, int size) {
+  if (!AllowFileIO())
+    return -1;
+
+  FILE* file = fopen(path.c_str(), "wb");
+  if (!file)
+    return -1;
+
+  int written = 0;
+
+  do {
+    size_t write = fwrite(data + written, 1, size - written, file);
+    if (write == 0)
+      break;
+    written += static_cast<int>(write);
+  } while (written < size);
+
+  fclose(file);
+
+  return written;
+}
+
+std::string JoinPath(const std::string& path1, const std::string& path2) {
+  if (path1.empty() && path2.empty())
+    return std::string();
+  if (path1.empty())
+    return path2;
+  if (path2.empty())
+    return path1;
+
+  std::string result = path1;
+  if (result[result.size() - 1] != kPathSep)
+    result += kPathSep;
+  if (path2[0] == kPathSep)
+    result += path2.substr(1);
+  else
+    result += path2;
+  return result;
+}
+
+std::string GetFileExtension(const std::string& path) {
+  size_t sep = path.find_last_of(".");
+  if (sep != std::string::npos)
+    return path.substr(sep + 1);
+  return std::string();
+}
+
+}  // namespace file_util
+}  // namespace client
diff --git a/src/tests/shared/browser/file_util.h b/src/tests/shared/browser/file_util.h
new file mode 100644
index 0000000..cc484b7
--- /dev/null
+++ b/src/tests/shared/browser/file_util.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+// 2012 The Chromium Authors. All rights reserved. Use of this source code is
+// governed by a BSD-style license that can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_
+#define CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_
+#pragma once
+
+#include <limits>
+#include <string>
+
+namespace client {
+namespace file_util {
+
+// Platform-specific path separator.
+extern const char kPathSep;
+
+// Reads the file at |path| into |contents| and returns true on success and
+// false on error.  In case of I/O error, |contents| holds the data that could
+// be read from the file before the error occurred.  When the file size exceeds
+// max_size|, the function returns false with |contents| holding the file
+// truncated to |max_size|. |contents| may be NULL, in which case this function
+// is useful for its side effect of priming the disk cache (could be used for
+// unit tests). Calling this function on the browser process UI or IO threads is
+// not allowed.
+bool ReadFileToString(const std::string& path,
+                      std::string* contents,
+                      size_t max_size = std::numeric_limits<size_t>::max());
+
+// Writes the given buffer into the file, overwriting any data that was
+// previously there. Returns the number of bytes written, or -1 on error.
+// Calling this function on the browser process UI or IO threads is not allowed.
+int WriteFile(const std::string& path, const char* data, int size);
+
+// Combines |path1| and |path2| with the correct platform-specific path
+// separator.
+std::string JoinPath(const std::string& path1, const std::string& path2);
+
+// Extracts the file extension from |path|.
+std::string GetFileExtension(const std::string& path);
+
+}  // namespace file_util
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_
diff --git a/src/tests/shared/browser/geometry_util.cc b/src/tests/shared/browser/geometry_util.cc
new file mode 100644
index 0000000..206edbc
--- /dev/null
+++ b/src/tests/shared/browser/geometry_util.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/geometry_util.h"
+
+#include <cmath>
+
+namespace client {
+
+int LogicalToDevice(int value, float device_scale_factor) {
+  float scaled_val = static_cast<float>(value) * device_scale_factor;
+  return static_cast<int>(std::floor(scaled_val));
+}
+
+CefRect LogicalToDevice(const CefRect& value, float device_scale_factor) {
+  return CefRect(LogicalToDevice(value.x, device_scale_factor),
+                 LogicalToDevice(value.y, device_scale_factor),
+                 LogicalToDevice(value.width, device_scale_factor),
+                 LogicalToDevice(value.height, device_scale_factor));
+}
+
+int DeviceToLogical(int value, float device_scale_factor) {
+  float scaled_val = static_cast<float>(value) / device_scale_factor;
+  return static_cast<int>(std::floor(scaled_val));
+}
+
+void DeviceToLogical(CefMouseEvent& value, float device_scale_factor) {
+  value.x = DeviceToLogical(value.x, device_scale_factor);
+  value.y = DeviceToLogical(value.y, device_scale_factor);
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/geometry_util.h b/src/tests/shared/browser/geometry_util.h
new file mode 100644
index 0000000..1f1e188
--- /dev/null
+++ b/src/tests/shared/browser/geometry_util.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_GEOMETRY_UTIL_H_
+#define CEF_TESTS_SHARED_BROWSER_GEOMETRY_UTIL_H_
+#pragma once
+
+#include "include/internal/cef_types_wrappers.h"
+
+namespace client {
+
+// Convert |value| from logical coordinates to device coordinates.
+int LogicalToDevice(int value, float device_scale_factor);
+CefRect LogicalToDevice(const CefRect& value, float device_scale_factor);
+
+// Convert |value| from device coordinates to logical coordinates.
+int DeviceToLogical(int value, float device_scale_factor);
+void DeviceToLogical(CefMouseEvent& value, float device_scale_factor);
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_GEOMETRY_UTIL_H_
diff --git a/src/tests/shared/browser/main_message_loop.cc b/src/tests/shared/browser/main_message_loop.cc
new file mode 100644
index 0000000..481e2b4
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/main_message_loop.h"
+
+#include "include/cef_task.h"
+#include "include/wrapper/cef_closure_task.h"
+
+namespace client {
+
+namespace {
+
+MainMessageLoop* g_main_message_loop = nullptr;
+
+}  // namespace
+
+MainMessageLoop::MainMessageLoop() {
+  DCHECK(!g_main_message_loop);
+  g_main_message_loop = this;
+}
+
+MainMessageLoop::~MainMessageLoop() {
+  g_main_message_loop = nullptr;
+}
+
+// static
+MainMessageLoop* MainMessageLoop::Get() {
+  DCHECK(g_main_message_loop);
+  return g_main_message_loop;
+}
+
+void MainMessageLoop::PostClosure(const base::Closure& closure) {
+  PostTask(CefCreateClosureTask(closure));
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/main_message_loop.h b/src/tests/shared/browser/main_message_loop.h
new file mode 100644
index 0000000..90edd8d
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_H_
+#define CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_H_
+#pragma once
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_scoped_ptr.h"
+#include "include/cef_task.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace client {
+
+// Represents the message loop running on the main application thread in the
+// browser process. This will be the same as the CEF UI thread on Linux, OS X
+// and Windows when not using multi-threaded message loop mode. The methods of
+// this class are thread-safe unless otherwise indicated.
+class MainMessageLoop {
+ public:
+  // Returns the singleton instance of this object.
+  static MainMessageLoop* Get();
+
+  // Run the message loop. The thread that this method is called on will be
+  // considered the main thread. This blocks until Quit() is called.
+  virtual int Run() = 0;
+
+  // Quit the message loop.
+  virtual void Quit() = 0;
+
+  // Post a task for execution on the main message loop.
+  virtual void PostTask(CefRefPtr<CefTask> task) = 0;
+
+  // Returns true if this message loop runs tasks on the current thread.
+  virtual bool RunsTasksOnCurrentThread() const = 0;
+
+#if defined(OS_WIN)
+  // Set the current modeless dialog on Windows for proper delivery of dialog
+  // messages when using multi-threaded message loop mode. This method must be
+  // called from the main thread. See http://support.microsoft.com/kb/71450 for
+  // background.
+  virtual void SetCurrentModelessDialog(HWND hWndDialog) = 0;
+#endif
+
+  // Post a closure for execution on the main message loop.
+  void PostClosure(const base::Closure& closure);
+
+ protected:
+  // Only allow deletion via scoped_ptr.
+  friend struct base::DefaultDeleter<MainMessageLoop>;
+
+  MainMessageLoop();
+  virtual ~MainMessageLoop();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MainMessageLoop);
+};
+
+#define CURRENTLY_ON_MAIN_THREAD() \
+  client::MainMessageLoop::Get()->RunsTasksOnCurrentThread()
+
+#define REQUIRE_MAIN_THREAD() DCHECK(CURRENTLY_ON_MAIN_THREAD())
+
+#define MAIN_POST_TASK(task) client::MainMessageLoop::Get()->PostTask(task)
+
+#define MAIN_POST_CLOSURE(closure) \
+  client::MainMessageLoop::Get()->PostClosure(closure)
+
+// Use this struct in conjuction with RefCountedThreadSafe to ensure that an
+// object is deleted on the main thread. For example:
+//
+// class Foo : public base::RefCountedThreadSafe<Foo, DeleteOnMainThread> {
+//  public:
+//   Foo();
+//   void DoSomething();
+//
+//  private:
+//   // Allow deletion via scoped_refptr only.
+//   friend struct DeleteOnMainThread;
+//   friend class base::RefCountedThreadSafe<Foo, DeleteOnMainThread>;
+//
+//   virtual ~Foo() {}
+// };
+//
+// base::scoped_refptr<Foo> foo = new Foo();
+// foo->DoSomething();
+// foo = nullptr;  // Deletion of |foo| will occur on the main thread.
+//
+struct DeleteOnMainThread {
+  template <typename T>
+  static void Destruct(const T* x) {
+    if (CURRENTLY_ON_MAIN_THREAD()) {
+      delete x;
+    } else {
+      client::MainMessageLoop::Get()->PostClosure(
+          base::Bind(&DeleteOnMainThread::Destruct<T>, x));
+    }
+  }
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_H_
diff --git a/src/tests/shared/browser/main_message_loop_external_pump.cc b/src/tests/shared/browser/main_message_loop_external_pump.cc
new file mode 100644
index 0000000..b589f66
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop_external_pump.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+
+#include <climits>
+
+#include "include/cef_app.h"
+#include "include/wrapper/cef_helpers.h"
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+namespace {
+
+// Special timer delay placeholder value. Intentionally 32-bit for Windows and
+// OS X platform API compatibility.
+const int32 kTimerDelayPlaceholder = INT_MAX;
+
+// The maximum number of milliseconds we're willing to wait between calls to
+// DoWork().
+const int64 kMaxTimerDelay = 1000 / 30;  // 30fps
+
+client::MainMessageLoopExternalPump* g_external_message_pump = nullptr;
+
+}  // namespace
+
+MainMessageLoopExternalPump::MainMessageLoopExternalPump()
+    : is_active_(false), reentrancy_detected_(false) {
+  DCHECK(!g_external_message_pump);
+  g_external_message_pump = this;
+}
+
+MainMessageLoopExternalPump::~MainMessageLoopExternalPump() {
+  g_external_message_pump = nullptr;
+}
+
+MainMessageLoopExternalPump* MainMessageLoopExternalPump::Get() {
+  return g_external_message_pump;
+}
+
+void MainMessageLoopExternalPump::OnScheduleWork(int64 delay_ms) {
+  REQUIRE_MAIN_THREAD();
+
+  if (delay_ms == kTimerDelayPlaceholder && IsTimerPending()) {
+    // Don't set the maximum timer requested from DoWork() if a timer event is
+    // currently pending.
+    return;
+  }
+
+  KillTimer();
+
+  if (delay_ms <= 0) {
+    // Execute the work immediately.
+    DoWork();
+  } else {
+    // Never wait longer than the maximum allowed time.
+    if (delay_ms > kMaxTimerDelay)
+      delay_ms = kMaxTimerDelay;
+
+    // Results in call to OnTimerTimeout() after the specified delay.
+    SetTimer(delay_ms);
+  }
+}
+
+void MainMessageLoopExternalPump::OnTimerTimeout() {
+  REQUIRE_MAIN_THREAD();
+
+  KillTimer();
+  DoWork();
+}
+
+void MainMessageLoopExternalPump::DoWork() {
+  const bool was_reentrant = PerformMessageLoopWork();
+  if (was_reentrant) {
+    // Execute the remaining work as soon as possible.
+    OnScheduleMessagePumpWork(0);
+  } else if (!IsTimerPending()) {
+    // Schedule a timer event at the maximum allowed time. This may be dropped
+    // in OnScheduleWork() if another timer event is already in-flight.
+    OnScheduleMessagePumpWork(kTimerDelayPlaceholder);
+  }
+}
+
+bool MainMessageLoopExternalPump::PerformMessageLoopWork() {
+  if (is_active_) {
+    // When CefDoMessageLoopWork() is called there may be various callbacks
+    // (such as paint and IPC messages) that result in additional calls to this
+    // method. If re-entrancy is detected we must repost a request again to the
+    // owner thread to ensure that the discarded call is executed in the future.
+    reentrancy_detected_ = true;
+    return false;
+  }
+
+  reentrancy_detected_ = false;
+
+  is_active_ = true;
+  CefDoMessageLoopWork();
+  is_active_ = false;
+
+  // |reentrancy_detected_| may have changed due to re-entrant calls to this
+  // method.
+  return reentrancy_detected_;
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/main_message_loop_external_pump.h b/src/tests/shared/browser/main_message_loop_external_pump.h
new file mode 100644
index 0000000..a25af46
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop_external_pump.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_EXTERNAL_PUMP_H_
+#define CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_EXTERNAL_PUMP_H_
+#pragma once
+
+#include "tests/shared/browser/main_message_loop_std.h"
+
+namespace client {
+
+// This MessageLoop implementation simulates the embedding of CEF into an
+// existing host application that runs its own message loop. The scheduling
+// implementation provided by this class is very simplistic and does not handle
+// all cases (for example, nested message loops on Windows will not function
+// correctly). See comments in Chromium's platform-specific
+// base/message_loop/message_pump_* source files for additional guidance when
+// implementing CefBrowserProcessHandler::OnScheduleMessagePumpWork() in your
+// application. Run cefclient or ceftests with the
+// "--external-message-pump" command-line flag to test this mode.
+class MainMessageLoopExternalPump : public MainMessageLoopStd {
+ public:
+  // Creates the singleton instance of this object. Must be called on the main
+  // application thread.
+  static scoped_ptr<MainMessageLoopExternalPump> Create();
+
+  // Returns the singleton instance of this object. Safe to call from any
+  // thread.
+  static MainMessageLoopExternalPump* Get();
+
+  // Called from CefBrowserProcessHandler::OnScheduleMessagePumpWork() on any
+  // thread. The platform subclass must implement this method and schedule a
+  // call to OnScheduleWork() on the main application thread.
+  virtual void OnScheduleMessagePumpWork(int64 delay_ms) = 0;
+
+ protected:
+  // Only allow deletion via scoped_ptr.
+  friend struct base::DefaultDeleter<MainMessageLoopExternalPump>;
+
+  // Construct and destruct this object on the main application thread.
+  MainMessageLoopExternalPump();
+  ~MainMessageLoopExternalPump();
+
+  // The platform subclass calls this method on the main application thread in
+  // response to the OnScheduleMessagePumpWork() call.
+  void OnScheduleWork(int64 delay_ms);
+
+  // The platform subclass calls this method on the main application thread when
+  // the pending work timer times out.
+  void OnTimerTimeout();
+
+  // Control the pending work timer in the platform subclass. Only called on
+  // the main application thread.
+  virtual void SetTimer(int64 delay_ms) = 0;
+  virtual void KillTimer() = 0;
+  virtual bool IsTimerPending() = 0;
+
+ private:
+  // Handle work processing.
+  void DoWork();
+  bool PerformMessageLoopWork();
+
+  bool is_active_;
+  bool reentrancy_detected_;
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_EXTERNAL_PUMP_H_
diff --git a/src/tests/shared/browser/main_message_loop_external_pump_linux.cc b/src/tests/shared/browser/main_message_loop_external_pump_linux.cc
new file mode 100644
index 0000000..1afe8fe
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop_external_pump_linux.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include "include/base/cef_logging.h"
+#include "include/cef_app.h"
+
+// From base/posix/eintr_wrapper.h.
+// This provides a wrapper around system calls which may be interrupted by a
+// signal and return EINTR. See man 7 signal.
+// To prevent long-lasting loops (which would likely be a bug, such as a signal
+// that should be masked) to go unnoticed, there is a limit after which the
+// caller will nonetheless see an EINTR in Debug builds.
+#if !defined(HANDLE_EINTR)
+#if !DCHECK_IS_ON()
+
+#define HANDLE_EINTR(x)                                     \
+  ({                                                        \
+    decltype(x) eintr_wrapper_result;                       \
+    do {                                                    \
+      eintr_wrapper_result = (x);                           \
+    } while (eintr_wrapper_result == -1 && errno == EINTR); \
+    eintr_wrapper_result;                                   \
+  })
+
+#else
+
+#define HANDLE_EINTR(x)                                      \
+  ({                                                         \
+    int eintr_wrapper_counter = 0;                           \
+    decltype(x) eintr_wrapper_result;                        \
+    do {                                                     \
+      eintr_wrapper_result = (x);                            \
+    } while (eintr_wrapper_result == -1 && errno == EINTR && \
+             eintr_wrapper_counter++ < 100);                 \
+    eintr_wrapper_result;                                    \
+  })
+
+#endif  // !DCHECK_IS_ON()
+#endif  // !defined(HANDLE_EINTR)
+
+namespace client {
+
+namespace {
+
+class MainMessageLoopExternalPumpLinux : public MainMessageLoopExternalPump {
+ public:
+  MainMessageLoopExternalPumpLinux();
+  ~MainMessageLoopExternalPumpLinux();
+
+  // MainMessageLoopStd methods:
+  void Quit() OVERRIDE;
+  int Run() OVERRIDE;
+
+  // MainMessageLoopExternalPump methods:
+  void OnScheduleMessagePumpWork(int64 delay_ms) OVERRIDE;
+
+  // Internal methods used for processing the pump callbacks. They are public
+  // for simplicity but should not be used directly. HandlePrepare is called
+  // during the prepare step of glib, and returns a timeout that will be passed
+  // to the poll. HandleCheck is called after the poll has completed, and
+  // returns whether or not HandleDispatch should be called. HandleDispatch is
+  // called if HandleCheck returned true.
+  int HandlePrepare();
+  bool HandleCheck();
+  void HandleDispatch();
+
+ protected:
+  // MainMessageLoopExternalPump methods:
+  void SetTimer(int64 delay_ms) OVERRIDE;
+  void KillTimer() OVERRIDE;
+  bool IsTimerPending() OVERRIDE;
+
+ private:
+  // Used to flag that the Run() invocation should return ASAP.
+  bool should_quit_;
+
+  // A GLib structure that we can add event sources to. We use the default GLib
+  // context, which is the one to which all GTK events are dispatched.
+  GMainContext* context_;
+
+  // The work source. It is destroyed when the message pump is destroyed.
+  GSource* work_source_;
+
+  // The time when we need to do delayed work.
+  CefTime delayed_work_time_;
+
+  // We use a wakeup pipe to make sure we'll get out of the glib polling phase
+  // when another thread has scheduled us to do some work. There is a glib
+  // mechanism g_main_context_wakeup, but this won't guarantee that our event's
+  // Dispatch() will be called.
+  int wakeup_pipe_read_;
+  int wakeup_pipe_write_;
+
+  // Use a scoped_ptr to avoid needing the definition of GPollFD in the header.
+  scoped_ptr<GPollFD> wakeup_gpollfd_;
+};
+
+// Return a timeout suitable for the glib loop, -1 to block forever,
+// 0 to return right away, or a timeout in milliseconds from now.
+int GetTimeIntervalMilliseconds(const CefTime& from) {
+  if (from.GetDoubleT() == 0.0)
+    return -1;
+
+  CefTime now;
+  now.Now();
+
+  // Be careful here. CefTime has a precision of microseconds, but we want a
+  // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
+  // 6?  It should be 6 to avoid executing delayed work too early.
+  int delay =
+      static_cast<int>(ceil((from.GetDoubleT() - now.GetDoubleT()) * 1000.0));
+
+  // If this value is negative, then we need to run delayed work soon.
+  return delay < 0 ? 0 : delay;
+}
+
+struct WorkSource : public GSource {
+  MainMessageLoopExternalPumpLinux* pump;
+};
+
+gboolean WorkSourcePrepare(GSource* source, gint* timeout_ms) {
+  *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
+  // We always return FALSE, so that our timeout is honored.  If we were
+  // to return TRUE, the timeout would be considered to be 0 and the poll
+  // would never block.  Once the poll is finished, Check will be called.
+  return FALSE;
+}
+
+gboolean WorkSourceCheck(GSource* source) {
+  // Only return TRUE if Dispatch should be called.
+  return static_cast<WorkSource*>(source)->pump->HandleCheck();
+}
+
+gboolean WorkSourceDispatch(GSource* source,
+                            GSourceFunc unused_func,
+                            gpointer unused_data) {
+  static_cast<WorkSource*>(source)->pump->HandleDispatch();
+  // Always return TRUE so our source stays registered.
+  return TRUE;
+}
+
+// I wish these could be const, but g_source_new wants non-const.
+GSourceFuncs WorkSourceFuncs = {WorkSourcePrepare, WorkSourceCheck,
+                                WorkSourceDispatch, NULL};
+
+MainMessageLoopExternalPumpLinux::MainMessageLoopExternalPumpLinux()
+    : should_quit_(false),
+      context_(g_main_context_default()),
+      wakeup_gpollfd_(new GPollFD) {
+  // Create our wakeup pipe, which is used to flag when work was scheduled.
+  int fds[2];
+  int ret = pipe(fds);
+  DCHECK_EQ(ret, 0);
+  (void)ret;  // Prevent warning in release mode.
+
+  wakeup_pipe_read_ = fds[0];
+  wakeup_pipe_write_ = fds[1];
+  wakeup_gpollfd_->fd = wakeup_pipe_read_;
+  wakeup_gpollfd_->events = G_IO_IN;
+
+  work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
+  static_cast<WorkSource*>(work_source_)->pump = this;
+  g_source_add_poll(work_source_, wakeup_gpollfd_.get());
+  // Use a low priority so that we let other events in the queue go first.
+  g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE);
+  // This is needed to allow Run calls inside Dispatch.
+  g_source_set_can_recurse(work_source_, TRUE);
+  g_source_attach(work_source_, context_);
+}
+
+MainMessageLoopExternalPumpLinux::~MainMessageLoopExternalPumpLinux() {
+  g_source_destroy(work_source_);
+  g_source_unref(work_source_);
+  close(wakeup_pipe_read_);
+  close(wakeup_pipe_write_);
+}
+
+void MainMessageLoopExternalPumpLinux::Quit() {
+  should_quit_ = true;
+}
+
+int MainMessageLoopExternalPumpLinux::Run() {
+  // We really only do a single task for each iteration of the loop. If we
+  // have done something, assume there is likely something more to do. This
+  // will mean that we don't block on the message pump until there was nothing
+  // more to do. We also set this to true to make sure not to block on the
+  // first iteration of the loop.
+  bool more_work_is_plausible = true;
+
+  // We run our own loop instead of using g_main_loop_quit in one of the
+  // callbacks. This is so we only quit our own loops, and we don't quit
+  // nested loops run by others.
+  for (;;) {
+    // Don't block if we think we have more work to do.
+    bool block = !more_work_is_plausible;
+
+    more_work_is_plausible = g_main_context_iteration(context_, block);
+    if (should_quit_)
+      break;
+  }
+
+  // We need to run the message pump until it is idle. However we don't have
+  // that information here so we run the message loop "for a while".
+  for (int i = 0; i < 10; ++i) {
+    // Do some work.
+    CefDoMessageLoopWork();
+
+    // Sleep to allow the CEF proc to do work.
+    usleep(50000);
+  }
+
+  return 0;
+}
+
+void MainMessageLoopExternalPumpLinux::OnScheduleMessagePumpWork(
+    int64 delay_ms) {
+  // This can be called on any thread, so we don't want to touch any state
+  // variables as we would then need locks all over. This ensures that if we
+  // are sleeping in a poll that we will wake up.
+  if (HANDLE_EINTR(write(wakeup_pipe_write_, &delay_ms, sizeof(int64))) !=
+      sizeof(int64)) {
+    NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
+  }
+}
+
+// Return the timeout we want passed to poll.
+int MainMessageLoopExternalPumpLinux::HandlePrepare() {
+  // We don't think we have work to do, but make sure not to block longer than
+  // the next time we need to run delayed work.
+  return GetTimeIntervalMilliseconds(delayed_work_time_);
+}
+
+bool MainMessageLoopExternalPumpLinux::HandleCheck() {
+  // We usually have a single message on the wakeup pipe, since we are only
+  // signaled when the queue went from empty to non-empty, but there can be
+  // two messages if a task posted a task, hence we read at most two bytes.
+  // The glib poll will tell us whether there was data, so this read shouldn't
+  // block.
+  if (wakeup_gpollfd_->revents & G_IO_IN) {
+    int64 delay_ms[2];
+    const size_t num_bytes =
+        HANDLE_EINTR(read(wakeup_pipe_read_, delay_ms, sizeof(int64) * 2));
+    if (num_bytes < sizeof(int64)) {
+      NOTREACHED() << "Error reading from the wakeup pipe.";
+    }
+    if (num_bytes == sizeof(int64))
+      OnScheduleWork(delay_ms[0]);
+    if (num_bytes == sizeof(int64) * 2)
+      OnScheduleWork(delay_ms[1]);
+  }
+
+  if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
+    // The timer has expired. That condition will stay true until we process
+    // that delayed work, so we don't need to record this differently.
+    return true;
+  }
+
+  return false;
+}
+
+void MainMessageLoopExternalPumpLinux::HandleDispatch() {
+  OnTimerTimeout();
+}
+
+void MainMessageLoopExternalPumpLinux::SetTimer(int64 delay_ms) {
+  DCHECK_GT(delay_ms, 0);
+
+  CefTime now;
+  now.Now();
+
+  delayed_work_time_ =
+      CefTime(now.GetDoubleT() + static_cast<double>(delay_ms) / 1000.0);
+}
+
+void MainMessageLoopExternalPumpLinux::KillTimer() {
+  delayed_work_time_ = CefTime();
+}
+
+bool MainMessageLoopExternalPumpLinux::IsTimerPending() {
+  return GetTimeIntervalMilliseconds(delayed_work_time_) > 0;
+}
+
+}  // namespace
+
+// static
+scoped_ptr<MainMessageLoopExternalPump> MainMessageLoopExternalPump::Create() {
+  return scoped_ptr<MainMessageLoopExternalPump>(
+      new MainMessageLoopExternalPumpLinux());
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/main_message_loop_external_pump_mac.mm b/src/tests/shared/browser/main_message_loop_external_pump_mac.mm
new file mode 100644
index 0000000..f88361b
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop_external_pump_mac.mm
@@ -0,0 +1,181 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+#include "include/cef_app.h"
+
+@class EventHandler;
+
+namespace client {
+
+class MainMessageLoopExternalPumpMac : public MainMessageLoopExternalPump {
+ public:
+  MainMessageLoopExternalPumpMac();
+  ~MainMessageLoopExternalPumpMac();
+
+  // MainMessageLoopStd methods:
+  void Quit() OVERRIDE;
+  int Run() OVERRIDE;
+
+  // MainMessageLoopExternalPump methods:
+  void OnScheduleMessagePumpWork(int64 delay_ms) OVERRIDE;
+
+  // Internal methods used for processing the event callbacks. They are public
+  // for simplicity but should not be used directly.
+  void HandleScheduleWork(int64 delay_ms);
+  void HandleTimerTimeout();
+
+ protected:
+  // MainMessageLoopExternalPump methods:
+  void SetTimer(int64 delay_ms) OVERRIDE;
+  void KillTimer() OVERRIDE;
+  bool IsTimerPending() OVERRIDE { return timer_ != nil; }
+
+ private:
+  // Owner thread that will run events.
+  NSThread* owner_thread_;
+
+  // Pending work timer.
+  NSTimer* timer_;
+
+  // Used to handle event callbacks on the owner thread.
+  EventHandler* event_handler_;
+};
+
+}  // namespace client
+
+// Object that handles event callbacks on the owner thread.
+@interface EventHandler : NSObject {
+ @private
+  client::MainMessageLoopExternalPumpMac* pump_;
+}
+
+- (id)initWithPump:(client::MainMessageLoopExternalPumpMac*)pump;
+- (void)scheduleWork:(NSNumber*)delay_ms;
+- (void)timerTimeout:(id)obj;
+@end
+
+@implementation EventHandler
+
+- (id)initWithPump:(client::MainMessageLoopExternalPumpMac*)pump {
+  if (self = [super init]) {
+    pump_ = pump;
+  }
+  return self;
+}
+
+- (void)scheduleWork:(NSNumber*)delay_ms {
+  pump_->HandleScheduleWork([delay_ms integerValue]);
+}
+
+- (void)timerTimeout:(id)obj {
+  pump_->HandleTimerTimeout();
+}
+
+@end
+
+namespace client {
+
+MainMessageLoopExternalPumpMac::MainMessageLoopExternalPumpMac()
+    : owner_thread_([NSThread currentThread]), timer_(nil) {
+#if !__has_feature(objc_arc)
+  [owner_thread_ retain];
+#endif  // !__has_feature(objc_arc)
+  event_handler_ = [[EventHandler alloc] initWithPump:this];
+}
+
+MainMessageLoopExternalPumpMac::~MainMessageLoopExternalPumpMac() {
+  KillTimer();
+#if !__has_feature(objc_arc)
+  [owner_thread_ release];
+  [event_handler_ release];
+#endif  // !__has_feature(objc_arc)
+  owner_thread_ = nil;
+  event_handler_ = nil;
+}
+
+void MainMessageLoopExternalPumpMac::Quit() {
+  [NSApp stop:nil];
+}
+
+int MainMessageLoopExternalPumpMac::Run() {
+  // Run the message loop.
+  [NSApp run];
+
+  KillTimer();
+
+  // We need to run the message pump until it is idle. However we don't have
+  // that information here so we run the message loop "for a while".
+  for (int i = 0; i < 10; ++i) {
+    // Let default runloop observers run.
+    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, 1);
+
+    // Do some work.
+    CefDoMessageLoopWork();
+
+    // Sleep to allow the CEF proc to do work.
+    [NSThread sleepForTimeInterval:0.05];
+  }
+
+  return 0;
+}
+
+void MainMessageLoopExternalPumpMac::OnScheduleMessagePumpWork(int64 delay_ms) {
+  // This method may be called on any thread.
+  NSNumber* number = [NSNumber numberWithInt:static_cast<int>(delay_ms)];
+  [event_handler_ performSelector:@selector(scheduleWork:)
+                         onThread:owner_thread_
+                       withObject:number
+                    waitUntilDone:NO];
+}
+
+void MainMessageLoopExternalPumpMac::HandleScheduleWork(int64 delay_ms) {
+  OnScheduleWork(delay_ms);
+}
+
+void MainMessageLoopExternalPumpMac::HandleTimerTimeout() {
+  OnTimerTimeout();
+}
+
+void MainMessageLoopExternalPumpMac::SetTimer(int64 delay_ms) {
+  DCHECK_GT(delay_ms, 0);
+  DCHECK(!timer_);
+
+  const double delay_s = static_cast<double>(delay_ms) / 1000.0;
+  timer_ = [NSTimer timerWithTimeInterval:delay_s
+                                   target:event_handler_
+                                 selector:@selector(timerTimeout:)
+                                 userInfo:nil
+                                  repeats:NO];
+#if !__has_feature(objc_arc)
+  [timer_ retain];
+#endif  // !__has_feature(objc_arc)
+
+  // Add the timer to default and tracking runloop modes.
+  NSRunLoop* owner_runloop = [NSRunLoop currentRunLoop];
+  [owner_runloop addTimer:timer_ forMode:NSRunLoopCommonModes];
+  [owner_runloop addTimer:timer_ forMode:NSEventTrackingRunLoopMode];
+}
+
+void MainMessageLoopExternalPumpMac::KillTimer() {
+  if (timer_ != nil) {
+    [timer_ invalidate];
+#if !__has_feature(objc_arc)
+    [timer_ release];
+#endif  // !__has_feature(objc_arc)
+    timer_ = nil;
+  }
+}
+
+// static
+scoped_ptr<MainMessageLoopExternalPump> MainMessageLoopExternalPump::Create() {
+  return scoped_ptr<MainMessageLoopExternalPump>(
+      new MainMessageLoopExternalPumpMac());
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/main_message_loop_external_pump_win.cc b/src/tests/shared/browser/main_message_loop_external_pump_win.cc
new file mode 100644
index 0000000..496bcdd
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop_external_pump_win.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/main_message_loop_external_pump.h"
+
+#include <CommCtrl.h>
+
+#include "include/cef_app.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+namespace {
+
+// Message sent to get an additional time slice for pumping (processing) another
+// task (a series of such messages creates a continuous task pump).
+static const int kMsgHaveWork = WM_USER + 1;
+
+class MainMessageLoopExternalPumpWin : public MainMessageLoopExternalPump {
+ public:
+  MainMessageLoopExternalPumpWin();
+  ~MainMessageLoopExternalPumpWin();
+
+  // MainMessageLoopStd methods:
+  void Quit() OVERRIDE;
+  int Run() OVERRIDE;
+
+  // MainMessageLoopExternalPump methods:
+  void OnScheduleMessagePumpWork(int64 delay_ms) OVERRIDE;
+
+ protected:
+  // MainMessageLoopExternalPump methods:
+  void SetTimer(int64 delay_ms) OVERRIDE;
+  void KillTimer() OVERRIDE;
+  bool IsTimerPending() OVERRIDE { return timer_pending_; }
+
+ private:
+  static LRESULT CALLBACK WndProc(HWND hwnd,
+                                  UINT msg,
+                                  WPARAM wparam,
+                                  LPARAM lparam);
+
+  // True if a timer event is currently pending.
+  bool timer_pending_;
+
+  // HWND owned by the thread that CefDoMessageLoopWork should be invoked on.
+  HWND main_thread_target_;
+};
+
+MainMessageLoopExternalPumpWin::MainMessageLoopExternalPumpWin()
+    : timer_pending_(false), main_thread_target_(NULL) {
+  HINSTANCE hInstance = GetModuleHandle(NULL);
+  const wchar_t* const kClassName = L"CEFMainTargetHWND";
+
+  WNDCLASSEX wcex = {};
+  wcex.cbSize = sizeof(WNDCLASSEX);
+  wcex.lpfnWndProc = WndProc;
+  wcex.hInstance = hInstance;
+  wcex.lpszClassName = kClassName;
+  RegisterClassEx(&wcex);
+
+  // Create the message handling window.
+  main_thread_target_ =
+      CreateWindowW(kClassName, NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
+                    HWND_MESSAGE, NULL, hInstance, NULL);
+  DCHECK(main_thread_target_);
+  SetUserDataPtr(main_thread_target_, this);
+}
+
+MainMessageLoopExternalPumpWin::~MainMessageLoopExternalPumpWin() {
+  KillTimer();
+  if (main_thread_target_)
+    DestroyWindow(main_thread_target_);
+}
+
+void MainMessageLoopExternalPumpWin::Quit() {
+  PostMessage(NULL, WM_QUIT, 0, 0);
+}
+
+int MainMessageLoopExternalPumpWin::Run() {
+  // Run the message loop.
+  MSG msg;
+  while (GetMessage(&msg, NULL, 0, 0)) {
+    TranslateMessage(&msg);
+    DispatchMessage(&msg);
+  }
+
+  KillTimer();
+
+  // We need to run the message pump until it is idle. However we don't have
+  // that information here so we run the message loop "for a while".
+  for (int i = 0; i < 10; ++i) {
+    // Do some work.
+    CefDoMessageLoopWork();
+
+    // Sleep to allow the CEF proc to do work.
+    Sleep(50);
+  }
+
+  return 0;
+}
+
+void MainMessageLoopExternalPumpWin::OnScheduleMessagePumpWork(int64 delay_ms) {
+  // This method may be called on any thread.
+  PostMessage(main_thread_target_, kMsgHaveWork, 0,
+              static_cast<LPARAM>(delay_ms));
+}
+
+void MainMessageLoopExternalPumpWin::SetTimer(int64 delay_ms) {
+  DCHECK(!timer_pending_);
+  DCHECK_GT(delay_ms, 0);
+  timer_pending_ = true;
+  ::SetTimer(main_thread_target_, 1, static_cast<UINT>(delay_ms), NULL);
+}
+
+void MainMessageLoopExternalPumpWin::KillTimer() {
+  if (timer_pending_) {
+    ::KillTimer(main_thread_target_, 1);
+    timer_pending_ = false;
+  }
+}
+
+// static
+LRESULT CALLBACK MainMessageLoopExternalPumpWin::WndProc(HWND hwnd,
+                                                         UINT msg,
+                                                         WPARAM wparam,
+                                                         LPARAM lparam) {
+  if (msg == WM_TIMER || msg == kMsgHaveWork) {
+    MainMessageLoopExternalPumpWin* message_loop =
+        GetUserDataPtr<MainMessageLoopExternalPumpWin*>(hwnd);
+    if (msg == kMsgHaveWork) {
+      // OnScheduleMessagePumpWork() request.
+      const int64 delay_ms = static_cast<int64>(lparam);
+      message_loop->OnScheduleWork(delay_ms);
+    } else {
+      // Timer timed out.
+      message_loop->OnTimerTimeout();
+    }
+  }
+  return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+}  // namespace
+
+// static
+scoped_ptr<MainMessageLoopExternalPump> MainMessageLoopExternalPump::Create() {
+  return scoped_ptr<MainMessageLoopExternalPump>(
+      new MainMessageLoopExternalPumpWin());
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/main_message_loop_std.cc b/src/tests/shared/browser/main_message_loop_std.cc
new file mode 100644
index 0000000..d329f90
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop_std.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/main_message_loop_std.h"
+
+#include "include/cef_app.h"
+
+namespace client {
+
+MainMessageLoopStd::MainMessageLoopStd() {}
+
+int MainMessageLoopStd::Run() {
+  CefRunMessageLoop();
+  return 0;
+}
+
+void MainMessageLoopStd::Quit() {
+  CefQuitMessageLoop();
+}
+
+void MainMessageLoopStd::PostTask(CefRefPtr<CefTask> task) {
+  CefPostTask(TID_UI, task);
+}
+
+bool MainMessageLoopStd::RunsTasksOnCurrentThread() const {
+  return CefCurrentlyOn(TID_UI);
+}
+
+#if defined(OS_WIN)
+void MainMessageLoopStd::SetCurrentModelessDialog(HWND hWndDialog) {
+  // Nothing to do here. The Chromium message loop implementation will
+  // internally route dialog messages.
+}
+#endif
+
+}  // namespace client
diff --git a/src/tests/shared/browser/main_message_loop_std.h b/src/tests/shared/browser/main_message_loop_std.h
new file mode 100644
index 0000000..22b34a5
--- /dev/null
+++ b/src/tests/shared/browser/main_message_loop_std.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_STD_H_
+#define CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_STD_H_
+#pragma once
+
+#include "tests/shared/browser/main_message_loop.h"
+
+namespace client {
+
+// Represents the main message loop in the browser process. This implementation
+// is a light-weight wrapper around the Chromium UI thread.
+class MainMessageLoopStd : public MainMessageLoop {
+ public:
+  MainMessageLoopStd();
+
+  // MainMessageLoop methods.
+  int Run() OVERRIDE;
+  void Quit() OVERRIDE;
+  void PostTask(CefRefPtr<CefTask> task) OVERRIDE;
+  bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+#if defined(OS_WIN)
+  void SetCurrentModelessDialog(HWND hWndDialog) OVERRIDE;
+#endif
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MainMessageLoopStd);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_MAIN_MESSAGE_LOOP_STD_H_
diff --git a/src/tests/shared/browser/resource_util.h b/src/tests/shared/browser/resource_util.h
new file mode 100644
index 0000000..cf4e5af
--- /dev/null
+++ b/src/tests/shared/browser/resource_util.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_RESOURCE_UTIL_H_
+#define CEF_TESTS_SHARED_BROWSER_RESOURCE_UTIL_H_
+#pragma once
+
+#include <string>
+#include "include/cef_image.h"
+#include "include/cef_stream.h"
+
+#if defined(OS_WIN)
+#include "include/wrapper/cef_resource_manager.h"
+#endif
+
+namespace client {
+
+#if defined(OS_POSIX)
+// Returns the directory containing resource files.
+bool GetResourceDir(std::string& dir);
+#endif
+
+// Retrieve a resource as a string.
+bool LoadBinaryResource(const char* resource_name, std::string& resource_data);
+
+// Retrieve a resource as a steam reader.
+CefRefPtr<CefStreamReader> GetBinaryResourceReader(const char* resource_name);
+
+#if defined(OS_WIN)
+// Create a new provider for loading binary resources.
+CefResourceManager::Provider* CreateBinaryResourceProvider(
+    const std::string& url_path,
+    const std::string& resource_path_prefix);
+#endif
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_RESOURCE_UTIL_H_
diff --git a/src/tests/shared/browser/resource_util_linux.cc b/src/tests/shared/browser/resource_util_linux.cc
new file mode 100644
index 0000000..7ec263e
--- /dev/null
+++ b/src/tests/shared/browser/resource_util_linux.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tests/shared/browser/resource_util.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+namespace client {
+
+bool GetResourceDir(std::string& dir) {
+  char buff[1024];
+
+  // Retrieve the executable path.
+  ssize_t len = readlink("/proc/self/exe", buff, sizeof(buff) - 1);
+  if (len == -1)
+    return false;
+
+  buff[len] = 0;
+
+  // Remove the executable name from the path.
+  char* pos = strrchr(buff, '/');
+  if (!pos)
+    return false;
+
+  // Add "files" to the path.
+  strcpy(pos + 1, "files");
+  dir = std::string(buff);
+  return true;
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/resource_util_mac.mm b/src/tests/shared/browser/resource_util_mac.mm
new file mode 100644
index 0000000..219f12b
--- /dev/null
+++ b/src/tests/shared/browser/resource_util_mac.mm
@@ -0,0 +1,52 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors.
+// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tests/shared/browser/resource_util.h"
+
+#import <Foundation/Foundation.h>
+#include <mach-o/dyld.h>
+#include <stdio.h>
+
+#include "include/base/cef_logging.h"
+
+namespace client {
+
+namespace {
+
+// Implementation adapted from Chromium's base/mac/foundation_util.mm
+bool UncachedAmIBundled() {
+  return [[[NSBundle mainBundle] bundlePath] hasSuffix:@".app"];
+}
+
+bool AmIBundled() {
+  static bool am_i_bundled = UncachedAmIBundled();
+  return am_i_bundled;
+}
+
+}  // namespace
+
+// Implementation adapted from Chromium's base/base_path_mac.mm
+bool GetResourceDir(std::string& dir) {
+  // Retrieve the executable directory.
+  uint32_t pathSize = 0;
+  _NSGetExecutablePath(NULL, &pathSize);
+  if (pathSize > 0) {
+    dir.resize(pathSize);
+    _NSGetExecutablePath(const_cast<char*>(dir.c_str()), &pathSize);
+  }
+
+  if (AmIBundled()) {
+    // Trim executable name up to the last separator.
+    std::string::size_type last_separator = dir.find_last_of("/");
+    dir.resize(last_separator);
+    dir.append("/../Resources");
+    return true;
+  }
+
+  dir.append("/Resources");
+  return true;
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/resource_util_posix.cc b/src/tests/shared/browser/resource_util_posix.cc
new file mode 100644
index 0000000..71e73ea
--- /dev/null
+++ b/src/tests/shared/browser/resource_util_posix.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/resource_util.h"
+
+#include <stdio.h>
+
+namespace client {
+
+namespace {
+
+bool FileExists(const char* path) {
+  FILE* f = fopen(path, "rb");
+  if (f) {
+    fclose(f);
+    return true;
+  }
+  return false;
+}
+
+bool ReadFileToString(const char* path, std::string& data) {
+  // Implementation adapted from base/file_util.cc
+  FILE* file = fopen(path, "rb");
+  if (!file)
+    return false;
+
+  char buf[1 << 16];
+  size_t len;
+  while ((len = fread(buf, 1, sizeof(buf), file)) > 0)
+    data.append(buf, len);
+  fclose(file);
+
+  return true;
+}
+
+}  // namespace
+
+bool LoadBinaryResource(const char* resource_name, std::string& resource_data) {
+  std::string path;
+  if (!GetResourceDir(path))
+    return false;
+
+  path.append("/");
+  path.append(resource_name);
+
+  return ReadFileToString(path.c_str(), resource_data);
+}
+
+CefRefPtr<CefStreamReader> GetBinaryResourceReader(const char* resource_name) {
+  std::string path;
+  if (!GetResourceDir(path))
+    return nullptr;
+
+  path.append("/");
+  path.append(resource_name);
+
+  if (!FileExists(path.c_str()))
+    return nullptr;
+
+  return CefStreamReader::CreateForFile(path);
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/resource_util_win.cc b/src/tests/shared/browser/resource_util_win.cc
new file mode 100644
index 0000000..055288c
--- /dev/null
+++ b/src/tests/shared/browser/resource_util_win.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/resource_util.h"
+
+#include "include/base/cef_logging.h"
+#include "include/cef_stream.h"
+#include "include/wrapper/cef_byte_read_handler.h"
+#include "include/wrapper/cef_stream_resource_handler.h"
+
+namespace client {
+
+namespace {
+
+bool LoadBinaryResource(int binaryId, DWORD& dwSize, LPBYTE& pBytes) {
+  HINSTANCE hInst = GetModuleHandle(NULL);
+  HRSRC hRes =
+      FindResource(hInst, MAKEINTRESOURCE(binaryId), MAKEINTRESOURCE(256));
+  if (hRes) {
+    HGLOBAL hGlob = LoadResource(hInst, hRes);
+    if (hGlob) {
+      dwSize = SizeofResource(hInst, hRes);
+      pBytes = (LPBYTE)LockResource(hGlob);
+      if (dwSize > 0 && pBytes)
+        return true;
+    }
+  }
+
+  return false;
+}
+
+// Provider of binary resources.
+class BinaryResourceProvider : public CefResourceManager::Provider {
+ public:
+  BinaryResourceProvider(const std::string& url_path,
+                         const std::string& resource_path_prefix)
+      : url_path_(url_path), resource_path_prefix_(resource_path_prefix) {
+    DCHECK(!url_path.empty());
+    if (!resource_path_prefix_.empty() &&
+        resource_path_prefix_[resource_path_prefix_.length() - 1] != '/') {
+      resource_path_prefix_ += "/";
+    }
+  }
+
+  bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
+    CEF_REQUIRE_IO_THREAD();
+
+    const std::string& url = request->url();
+    if (url.find(url_path_) != 0L) {
+      // Not handled by this provider.
+      return false;
+    }
+
+    CefRefPtr<CefResourceHandler> handler;
+
+    std::string relative_path = url.substr(url_path_.length());
+    if (!relative_path.empty()) {
+      if (!resource_path_prefix_.empty())
+        relative_path = resource_path_prefix_ + relative_path;
+
+      CefRefPtr<CefStreamReader> stream =
+          GetBinaryResourceReader(relative_path.data());
+      if (stream.get()) {
+        handler = new CefStreamResourceHandler(
+            request->mime_type_resolver().Run(url), stream);
+      }
+    }
+
+    request->Continue(handler);
+    return true;
+  }
+
+ private:
+  std::string url_path_;
+  std::string resource_path_prefix_;
+
+  DISALLOW_COPY_AND_ASSIGN(BinaryResourceProvider);
+};
+
+}  // namespace
+
+// Implemented in resource_util_win_idmap.cc.
+extern int GetResourceId(const char* resource_name);
+
+bool LoadBinaryResource(const char* resource_name, std::string& resource_data) {
+  int resource_id = GetResourceId(resource_name);
+  if (resource_id == 0)
+    return false;
+
+  DWORD dwSize;
+  LPBYTE pBytes;
+
+  if (LoadBinaryResource(resource_id, dwSize, pBytes)) {
+    resource_data = std::string(reinterpret_cast<char*>(pBytes), dwSize);
+    return true;
+  }
+
+  NOTREACHED();  // The resource should be found.
+  return false;
+}
+
+CefRefPtr<CefStreamReader> GetBinaryResourceReader(const char* resource_name) {
+  int resource_id = GetResourceId(resource_name);
+  if (resource_id == 0)
+    return nullptr;
+
+  DWORD dwSize;
+  LPBYTE pBytes;
+
+  if (LoadBinaryResource(resource_id, dwSize, pBytes)) {
+    return CefStreamReader::CreateForHandler(
+        new CefByteReadHandler(pBytes, dwSize, nullptr));
+  }
+
+  NOTREACHED();  // The resource should be found.
+  return nullptr;
+}
+
+CefResourceManager::Provider* CreateBinaryResourceProvider(
+    const std::string& url_path,
+    const std::string& resource_path_prefix) {
+  return new BinaryResourceProvider(url_path, resource_path_prefix);
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/util_win.cc b/src/tests/shared/browser/util_win.cc
new file mode 100644
index 0000000..0be060f
--- /dev/null
+++ b/src/tests/shared/browser/util_win.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/browser/util_win.h"
+
+#include "include/base/cef_logging.h"
+
+namespace client {
+
+namespace {
+
+LARGE_INTEGER qi_freq_ = {};
+
+}  // namespace
+
+uint64_t GetTimeNow() {
+  if (!qi_freq_.HighPart && !qi_freq_.LowPart) {
+    QueryPerformanceFrequency(&qi_freq_);
+  }
+  LARGE_INTEGER t = {};
+  QueryPerformanceCounter(&t);
+  return static_cast<uint64_t>((t.QuadPart / double(qi_freq_.QuadPart)) *
+                               1000000);
+}
+
+void SetUserDataPtr(HWND hWnd, void* ptr) {
+  SetLastError(ERROR_SUCCESS);
+  LONG_PTR result =
+      ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(ptr));
+  CHECK(result != 0 || GetLastError() == ERROR_SUCCESS);
+}
+
+WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc) {
+  WNDPROC old =
+      reinterpret_cast<WNDPROC>(::GetWindowLongPtr(hWnd, GWLP_WNDPROC));
+  CHECK(old != NULL);
+  LONG_PTR result = ::SetWindowLongPtr(hWnd, GWLP_WNDPROC,
+                                       reinterpret_cast<LONG_PTR>(wndProc));
+  CHECK(result != 0 || GetLastError() == ERROR_SUCCESS);
+  return old;
+}
+
+std::wstring GetResourceString(UINT id) {
+#define MAX_LOADSTRING 100
+  TCHAR buff[MAX_LOADSTRING] = {0};
+  LoadString(::GetModuleHandle(NULL), id, buff, MAX_LOADSTRING);
+  return buff;
+}
+
+int GetCefMouseModifiers(WPARAM wparam) {
+  int modifiers = 0;
+  if (wparam & MK_CONTROL)
+    modifiers |= EVENTFLAG_CONTROL_DOWN;
+  if (wparam & MK_SHIFT)
+    modifiers |= EVENTFLAG_SHIFT_DOWN;
+  if (IsKeyDown(VK_MENU))
+    modifiers |= EVENTFLAG_ALT_DOWN;
+  if (wparam & MK_LBUTTON)
+    modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
+  if (wparam & MK_MBUTTON)
+    modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
+  if (wparam & MK_RBUTTON)
+    modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
+
+  // Low bit set from GetKeyState indicates "toggled".
+  if (::GetKeyState(VK_NUMLOCK) & 1)
+    modifiers |= EVENTFLAG_NUM_LOCK_ON;
+  if (::GetKeyState(VK_CAPITAL) & 1)
+    modifiers |= EVENTFLAG_CAPS_LOCK_ON;
+  return modifiers;
+}
+
+int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam) {
+  int modifiers = 0;
+  if (IsKeyDown(VK_SHIFT))
+    modifiers |= EVENTFLAG_SHIFT_DOWN;
+  if (IsKeyDown(VK_CONTROL))
+    modifiers |= EVENTFLAG_CONTROL_DOWN;
+  if (IsKeyDown(VK_MENU))
+    modifiers |= EVENTFLAG_ALT_DOWN;
+
+  // Low bit set from GetKeyState indicates "toggled".
+  if (::GetKeyState(VK_NUMLOCK) & 1)
+    modifiers |= EVENTFLAG_NUM_LOCK_ON;
+  if (::GetKeyState(VK_CAPITAL) & 1)
+    modifiers |= EVENTFLAG_CAPS_LOCK_ON;
+
+  switch (wparam) {
+    case VK_RETURN:
+      if ((lparam >> 16) & KF_EXTENDED)
+        modifiers |= EVENTFLAG_IS_KEY_PAD;
+      break;
+    case VK_INSERT:
+    case VK_DELETE:
+    case VK_HOME:
+    case VK_END:
+    case VK_PRIOR:
+    case VK_NEXT:
+    case VK_UP:
+    case VK_DOWN:
+    case VK_LEFT:
+    case VK_RIGHT:
+      if (!((lparam >> 16) & KF_EXTENDED))
+        modifiers |= EVENTFLAG_IS_KEY_PAD;
+      break;
+    case VK_NUMLOCK:
+    case VK_NUMPAD0:
+    case VK_NUMPAD1:
+    case VK_NUMPAD2:
+    case VK_NUMPAD3:
+    case VK_NUMPAD4:
+    case VK_NUMPAD5:
+    case VK_NUMPAD6:
+    case VK_NUMPAD7:
+    case VK_NUMPAD8:
+    case VK_NUMPAD9:
+    case VK_DIVIDE:
+    case VK_MULTIPLY:
+    case VK_SUBTRACT:
+    case VK_ADD:
+    case VK_DECIMAL:
+    case VK_CLEAR:
+      modifiers |= EVENTFLAG_IS_KEY_PAD;
+      break;
+    case VK_SHIFT:
+      if (IsKeyDown(VK_LSHIFT))
+        modifiers |= EVENTFLAG_IS_LEFT;
+      else if (IsKeyDown(VK_RSHIFT))
+        modifiers |= EVENTFLAG_IS_RIGHT;
+      break;
+    case VK_CONTROL:
+      if (IsKeyDown(VK_LCONTROL))
+        modifiers |= EVENTFLAG_IS_LEFT;
+      else if (IsKeyDown(VK_RCONTROL))
+        modifiers |= EVENTFLAG_IS_RIGHT;
+      break;
+    case VK_MENU:
+      if (IsKeyDown(VK_LMENU))
+        modifiers |= EVENTFLAG_IS_LEFT;
+      else if (IsKeyDown(VK_RMENU))
+        modifiers |= EVENTFLAG_IS_RIGHT;
+      break;
+    case VK_LWIN:
+      modifiers |= EVENTFLAG_IS_LEFT;
+      break;
+    case VK_RWIN:
+      modifiers |= EVENTFLAG_IS_RIGHT;
+      break;
+  }
+  return modifiers;
+}
+
+bool IsKeyDown(WPARAM wparam) {
+  return (GetKeyState(wparam) & 0x8000) != 0;
+}
+
+float GetDeviceScaleFactor() {
+  static float scale_factor = 1.0;
+  static bool initialized = false;
+
+  if (!initialized) {
+    // This value is safe to cache for the life time of the app since the user
+    // must logout to change the DPI setting. This value also applies to all
+    // screens.
+    HDC screen_dc = ::GetDC(NULL);
+    int dpi_x = GetDeviceCaps(screen_dc, LOGPIXELSX);
+    scale_factor = static_cast<float>(dpi_x) / 96.0f;
+    ::ReleaseDC(NULL, screen_dc);
+    initialized = true;
+  }
+
+  return scale_factor;
+}
+
+}  // namespace client
diff --git a/src/tests/shared/browser/util_win.h b/src/tests/shared/browser/util_win.h
new file mode 100644
index 0000000..a46d77e
--- /dev/null
+++ b/src/tests/shared/browser/util_win.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_BROWSER_UTIL_WIN_H_
+#define CEF_TESTS_SHARED_BROWSER_UTIL_WIN_H_
+#pragma once
+
+#include <windows.h>
+#include <string>
+
+#include "include/internal/cef_types_wrappers.h"
+
+namespace client {
+
+// Returns the current time in microseconds.
+uint64_t GetTimeNow();
+
+// Set the window's user data pointer.
+void SetUserDataPtr(HWND hWnd, void* ptr);
+
+// Return the window's user data pointer.
+template <typename T>
+T GetUserDataPtr(HWND hWnd) {
+  return reinterpret_cast<T>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
+}
+
+// Set the window's window procedure pointer and return the old value.
+WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc);
+
+// Return the resource string with the specified id.
+std::wstring GetResourceString(UINT id);
+
+int GetCefMouseModifiers(WPARAM wparam);
+int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam);
+bool IsKeyDown(WPARAM wparam);
+
+// Returns the device scale factor. For example, 200% display scaling will
+// return 2.0.
+float GetDeviceScaleFactor();
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_BROWSER_UTIL_WIN_H_
diff --git a/src/tests/shared/common/client_app.cc b/src/tests/shared/common/client_app.cc
new file mode 100644
index 0000000..1a76d4b
--- /dev/null
+++ b/src/tests/shared/common/client_app.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/common/client_app.h"
+
+#include "include/cef_command_line.h"
+
+namespace client {
+
+namespace {
+
+// These flags must match the Chromium values.
+const char kProcessType[] = "type";
+const char kRendererProcess[] = "renderer";
+#if defined(OS_LINUX)
+const char kZygoteProcess[] = "zygote";
+#endif
+
+}  // namespace
+
+ClientApp::ClientApp() {}
+
+// static
+ClientApp::ProcessType ClientApp::GetProcessType(
+    CefRefPtr<CefCommandLine> command_line) {
+  // The command-line flag won't be specified for the browser process.
+  if (!command_line->HasSwitch(kProcessType))
+    return BrowserProcess;
+
+  const std::string& process_type = command_line->GetSwitchValue(kProcessType);
+  if (process_type == kRendererProcess)
+    return RendererProcess;
+#if defined(OS_LINUX)
+  else if (process_type == kZygoteProcess)
+    return ZygoteProcess;
+#endif
+
+  return OtherProcess;
+}
+
+void ClientApp::OnRegisterCustomSchemes(
+    CefRawPtr<CefSchemeRegistrar> registrar) {
+  RegisterCustomSchemes(registrar, cookieable_schemes_);
+}
+
+}  // namespace client
diff --git a/src/tests/shared/common/client_app.h b/src/tests/shared/common/client_app.h
new file mode 100644
index 0000000..225f808
--- /dev/null
+++ b/src/tests/shared/common/client_app.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_COMMON_CLIENT_APP_H_
+#define CEF_TESTS_SHARED_COMMON_CLIENT_APP_H_
+#pragma once
+
+#include <vector>
+
+#include "include/cef_app.h"
+
+namespace client {
+
+// Base class for customizing process-type-based behavior.
+class ClientApp : public CefApp {
+ public:
+  ClientApp();
+
+  enum ProcessType {
+    BrowserProcess,
+    RendererProcess,
+    ZygoteProcess,
+    OtherProcess,
+  };
+
+  // Determine the process type based on command-line arguments.
+  static ProcessType GetProcessType(CefRefPtr<CefCommandLine> command_line);
+
+ protected:
+  // Schemes that will be registered with the global cookie manager.
+  std::vector<CefString> cookieable_schemes_;
+
+ private:
+  // Registers custom schemes. Implemented by cefclient in
+  // client_app_delegates_common.cc
+  static void RegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar,
+                                    std::vector<CefString>& cookiable_schemes);
+
+  // CefApp methods.
+  void OnRegisterCustomSchemes(
+      CefRawPtr<CefSchemeRegistrar> registrar) OVERRIDE;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientApp);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_COMMON_CLIENT_APP_H_
diff --git a/src/tests/shared/common/client_app_other.cc b/src/tests/shared/common/client_app_other.cc
new file mode 100644
index 0000000..9f6a0c4
--- /dev/null
+++ b/src/tests/shared/common/client_app_other.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/common/client_app_other.h"
+
+#include "include/cef_command_line.h"
+
+namespace client {
+
+ClientAppOther::ClientAppOther() {}
+
+}  // namespace client
diff --git a/src/tests/shared/common/client_app_other.h b/src/tests/shared/common/client_app_other.h
new file mode 100644
index 0000000..b8a7342
--- /dev/null
+++ b/src/tests/shared/common/client_app_other.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_COMMON_CLIENT_APP_OTHER_H_
+#define CEF_TESTS_SHARED_COMMON_CLIENT_APP_OTHER_H_
+#pragma once
+
+#include "tests/shared/common/client_app.h"
+
+namespace client {
+
+// Client app implementation for other process types.
+class ClientAppOther : public ClientApp {
+ public:
+  ClientAppOther();
+
+ private:
+  IMPLEMENT_REFCOUNTING(ClientAppOther);
+  DISALLOW_COPY_AND_ASSIGN(ClientAppOther);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_COMMON_CLIENT_APP_OTHER_H_
diff --git a/src/tests/shared/common/client_switches.cc b/src/tests/shared/common/client_switches.cc
new file mode 100644
index 0000000..9feef7c
--- /dev/null
+++ b/src/tests/shared/common/client_switches.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/common/client_switches.h"
+
+namespace client {
+namespace switches {
+
+// CEF and Chromium support a wide range of command-line switches. This file
+// only contains command-line switches specific to the cefclient application.
+// View CEF/Chromium documentation or search for *_switches.cc files in the
+// Chromium source code to identify other existing command-line switches.
+// Below is a partial listing of relevant *_switches.cc files:
+//   base/base_switches.cc
+//   cef/libcef/common/cef_switches.cc
+//   chrome/common/chrome_switches.cc (not all apply)
+//   content/public/common/content_switches.cc
+
+const char kMultiThreadedMessageLoop[] = "multi-threaded-message-loop";
+const char kExternalMessagePump[] = "external-message-pump";
+const char kCachePath[] = "cache-path";
+const char kUrl[] = "url";
+const char kOffScreenRenderingEnabled[] = "off-screen-rendering-enabled";
+const char kOffScreenFrameRate[] = "off-screen-frame-rate";
+const char kTransparentPaintingEnabled[] = "transparent-painting-enabled";
+const char kShowUpdateRect[] = "show-update-rect";
+const char kSharedTextureEnabled[] = "shared-texture-enabled";
+const char kExternalBeginFrameEnabled[] = "external-begin-frame-enabled";
+const char kMouseCursorChangeDisabled[] = "mouse-cursor-change-disabled";
+const char kOffline[] = "offline";
+const char kRequestContextPerBrowser[] = "request-context-per-browser";
+const char kRequestContextSharedCache[] = "request-context-shared-cache";
+const char kBackgroundColor[] = "background-color";
+const char kEnableGPU[] = "enable-gpu";
+const char kFilterURL[] = "filter-url";
+const char kUseViews[] = "use-views";
+const char kHideFrame[] = "hide-frame";
+const char kHideControls[] = "hide-controls";
+const char kAlwaysOnTop[] = "always-on-top";
+const char kHideTopMenu[] = "hide-top-menu";
+const char kWidevineCdmPath[] = "widevine-cdm-path";
+const char kSslClientCertificate[] = "ssl-client-certificate";
+const char kCRLSetsPath[] = "crl-sets-path";
+const char kLoadExtension[] = "load-extension";
+const char kNoActivate[] = "no-activate";
+
+}  // namespace switches
+}  // namespace client
diff --git a/src/tests/shared/common/client_switches.h b/src/tests/shared/common/client_switches.h
new file mode 100644
index 0000000..0ff5938
--- /dev/null
+++ b/src/tests/shared/common/client_switches.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+// Defines all of the command line switches used by cefclient.
+
+#ifndef CEF_TESTS_SHARED_SHARED_COMMON_SWITCHES_H_
+#define CEF_TESTS_SHARED_SHARED_COMMON_SWITCHES_H_
+#pragma once
+
+namespace client {
+namespace switches {
+
+extern const char kMultiThreadedMessageLoop[];
+extern const char kExternalMessagePump[];
+extern const char kCachePath[];
+extern const char kUrl[];
+extern const char kOffScreenRenderingEnabled[];
+extern const char kOffScreenFrameRate[];
+extern const char kTransparentPaintingEnabled[];
+extern const char kShowUpdateRect[];
+extern const char kSharedTextureEnabled[];
+extern const char kExternalBeginFrameEnabled[];
+extern const char kMouseCursorChangeDisabled[];
+extern const char kOffline[];
+extern const char kRequestContextPerBrowser[];
+extern const char kRequestContextSharedCache[];
+extern const char kBackgroundColor[];
+extern const char kEnableGPU[];
+extern const char kFilterURL[];
+extern const char kUseViews[];
+extern const char kHideFrame[];
+extern const char kHideControls[];
+extern const char kAlwaysOnTop[];
+extern const char kHideTopMenu[];
+extern const char kWidevineCdmPath[];
+extern const char kSslClientCertificate[];
+extern const char kCRLSetsPath[];
+extern const char kLoadExtension[];
+extern const char kNoActivate[];
+
+}  // namespace switches
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_SHARED_COMMON_SWITCHES_H_
diff --git a/src/tests/shared/process_helper_mac.cc b/src/tests/shared/process_helper_mac.cc
new file mode 100644
index 0000000..9efa2e3
--- /dev/null
+++ b/src/tests/shared/process_helper_mac.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+#include "include/cef_app.h"
+#include "include/wrapper/cef_library_loader.h"
+
+#include "tests/shared/common/client_app_other.h"
+#include "tests/shared/renderer/client_app_renderer.h"
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
+// use of the sandbox.
+#if defined(CEF_USE_SANDBOX)
+#include "include/cef_sandbox_mac.h"
+#endif
+
+namespace client {
+
+int RunMain(int argc, char* argv[]) {
+#if defined(CEF_USE_SANDBOX)
+  // Initialize the macOS sandbox for this helper process.
+  CefScopedSandboxContext sandbox_context;
+  if (!sandbox_context.Initialize(argc, argv))
+    return 1;
+#endif
+
+  // Load the CEF framework library at runtime instead of linking directly
+  // as required by the macOS sandbox implementation.
+  CefScopedLibraryLoader library_loader;
+  if (!library_loader.LoadInHelper())
+    return 1;
+
+  CefMainArgs main_args(argc, argv);
+
+  // Parse command-line arguments.
+  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+  command_line->InitFromArgv(argc, argv);
+
+  // Create a ClientApp of the correct type.
+  CefRefPtr<CefApp> app;
+  ClientApp::ProcessType process_type = ClientApp::GetProcessType(command_line);
+  if (process_type == ClientApp::RendererProcess)
+    app = new ClientAppRenderer();
+  else if (process_type == ClientApp::OtherProcess)
+    app = new ClientAppOther();
+
+  // Execute the secondary process.
+  return CefExecuteProcess(main_args, app, nullptr);
+}
+
+}  // namespace client
+
+// Entry point function for sub-processes.
+int main(int argc, char* argv[]) {
+  return client::RunMain(argc, argv);
+}
diff --git a/src/tests/shared/renderer/client_app_renderer.cc b/src/tests/shared/renderer/client_app_renderer.cc
new file mode 100644
index 0000000..b2020a5
--- /dev/null
+++ b/src/tests/shared/renderer/client_app_renderer.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/shared/renderer/client_app_renderer.h"
+
+#include "include/base/cef_logging.h"
+
+namespace client {
+
+ClientAppRenderer::ClientAppRenderer() {
+  CreateDelegates(delegates_);
+}
+
+void ClientAppRenderer::OnRenderThreadCreated(
+    CefRefPtr<CefListValue> extra_info) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnRenderThreadCreated(this, extra_info);
+}
+
+void ClientAppRenderer::OnWebKitInitialized() {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnWebKitInitialized(this);
+}
+
+void ClientAppRenderer::OnBrowserCreated(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDictionaryValue> extra_info) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnBrowserCreated(this, browser, extra_info);
+}
+
+void ClientAppRenderer::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnBrowserDestroyed(this, browser);
+}
+
+CefRefPtr<CefLoadHandler> ClientAppRenderer::GetLoadHandler() {
+  CefRefPtr<CefLoadHandler> load_handler;
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end() && !load_handler.get(); ++it)
+    load_handler = (*it)->GetLoadHandler(this);
+
+  return load_handler;
+}
+
+void ClientAppRenderer::OnContextCreated(CefRefPtr<CefBrowser> browser,
+                                         CefRefPtr<CefFrame> frame,
+                                         CefRefPtr<CefV8Context> context) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnContextCreated(this, browser, frame, context);
+}
+
+void ClientAppRenderer::OnContextReleased(CefRefPtr<CefBrowser> browser,
+                                          CefRefPtr<CefFrame> frame,
+                                          CefRefPtr<CefV8Context> context) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnContextReleased(this, browser, frame, context);
+}
+
+void ClientAppRenderer::OnUncaughtException(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefRefPtr<CefV8Context> context,
+    CefRefPtr<CefV8Exception> exception,
+    CefRefPtr<CefV8StackTrace> stackTrace) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it) {
+    (*it)->OnUncaughtException(this, browser, frame, context, exception,
+                               stackTrace);
+  }
+}
+
+void ClientAppRenderer::OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
+                                             CefRefPtr<CefFrame> frame,
+                                             CefRefPtr<CefDOMNode> node) {
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end(); ++it)
+    (*it)->OnFocusedNodeChanged(this, browser, frame, node);
+}
+
+bool ClientAppRenderer::OnProcessMessageReceived(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefFrame> frame,
+    CefProcessId source_process,
+    CefRefPtr<CefProcessMessage> message) {
+  DCHECK_EQ(source_process, PID_BROWSER);
+
+  bool handled = false;
+
+  DelegateSet::iterator it = delegates_.begin();
+  for (; it != delegates_.end() && !handled; ++it) {
+    handled = (*it)->OnProcessMessageReceived(this, browser, frame,
+                                              source_process, message);
+  }
+
+  return handled;
+}
+
+}  // namespace client
diff --git a/src/tests/shared/renderer/client_app_renderer.h b/src/tests/shared/renderer/client_app_renderer.h
new file mode 100644
index 0000000..ce206b9
--- /dev/null
+++ b/src/tests/shared/renderer/client_app_renderer.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#ifndef CEF_TESTS_SHARED_RENDERER_CLIENT_APP_RENDERER_H_
+#define CEF_TESTS_SHARED_RENDERER_CLIENT_APP_RENDERER_H_
+#pragma once
+
+#include <set>
+
+#include "tests/shared/common/client_app.h"
+
+namespace client {
+
+// Client app implementation for the renderer process.
+class ClientAppRenderer : public ClientApp, public CefRenderProcessHandler {
+ public:
+  // Interface for renderer delegates. All Delegates must be returned via
+  // CreateDelegates. Do not perform work in the Delegate
+  // constructor. See CefRenderProcessHandler for documentation.
+  class Delegate : public virtual CefBaseRefCounted {
+   public:
+    virtual void OnRenderThreadCreated(CefRefPtr<ClientAppRenderer> app,
+                                       CefRefPtr<CefListValue> extra_info) {}
+
+    virtual void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) {}
+
+    virtual void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
+                                  CefRefPtr<CefBrowser> browser,
+                                  CefRefPtr<CefDictionaryValue> extra_info) {}
+
+    virtual void OnBrowserDestroyed(CefRefPtr<ClientAppRenderer> app,
+                                    CefRefPtr<CefBrowser> browser) {}
+
+    virtual CefRefPtr<CefLoadHandler> GetLoadHandler(
+        CefRefPtr<ClientAppRenderer> app) {
+      return nullptr;
+    }
+
+    virtual void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
+                                  CefRefPtr<CefBrowser> browser,
+                                  CefRefPtr<CefFrame> frame,
+                                  CefRefPtr<CefV8Context> context) {}
+
+    virtual void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
+                                   CefRefPtr<CefBrowser> browser,
+                                   CefRefPtr<CefFrame> frame,
+                                   CefRefPtr<CefV8Context> context) {}
+
+    virtual void OnUncaughtException(CefRefPtr<ClientAppRenderer> app,
+                                     CefRefPtr<CefBrowser> browser,
+                                     CefRefPtr<CefFrame> frame,
+                                     CefRefPtr<CefV8Context> context,
+                                     CefRefPtr<CefV8Exception> exception,
+                                     CefRefPtr<CefV8StackTrace> stackTrace) {}
+
+    virtual void OnFocusedNodeChanged(CefRefPtr<ClientAppRenderer> app,
+                                      CefRefPtr<CefBrowser> browser,
+                                      CefRefPtr<CefFrame> frame,
+                                      CefRefPtr<CefDOMNode> node) {}
+
+    // Called when a process message is received. Return true if the message was
+    // handled and should not be passed on to other handlers. Delegates
+    // should check for unique message names to avoid interfering with each
+    // other.
+    virtual bool OnProcessMessageReceived(
+        CefRefPtr<ClientAppRenderer> app,
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefProcessId source_process,
+        CefRefPtr<CefProcessMessage> message) {
+      return false;
+    }
+  };
+
+  typedef std::set<CefRefPtr<Delegate>> DelegateSet;
+
+  ClientAppRenderer();
+
+ private:
+  // Creates all of the Delegate objects. Implemented by cefclient in
+  // client_app_delegates_renderer.cc
+  static void CreateDelegates(DelegateSet& delegates);
+
+  // CefApp methods.
+  CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE {
+    return this;
+  }
+
+  // CefRenderProcessHandler methods.
+  void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) OVERRIDE;
+  void OnWebKitInitialized() OVERRIDE;
+  void OnBrowserCreated(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefDictionaryValue> extra_info) OVERRIDE;
+  void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) OVERRIDE;
+  CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE;
+  void OnContextCreated(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefV8Context> context) OVERRIDE;
+  void OnContextReleased(CefRefPtr<CefBrowser> browser,
+                         CefRefPtr<CefFrame> frame,
+                         CefRefPtr<CefV8Context> context) OVERRIDE;
+  void OnUncaughtException(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefV8Context> context,
+                           CefRefPtr<CefV8Exception> exception,
+                           CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE;
+  void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefDOMNode> node) OVERRIDE;
+  bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
+                                CefRefPtr<CefFrame> frame,
+                                CefProcessId source_process,
+                                CefRefPtr<CefProcessMessage> message) OVERRIDE;
+
+ private:
+  // Set of supported Delegates.
+  DelegateSet delegates_;
+
+  IMPLEMENT_REFCOUNTING(ClientAppRenderer);
+  DISALLOW_COPY_AND_ASSIGN(ClientAppRenderer);
+};
+
+}  // namespace client
+
+#endif  // CEF_TESTS_SHARED_RENDERER_CLIENT_APP_RENDERER_H_
diff --git a/src/tests/shared/resources/osr_test.html b/src/tests/shared/resources/osr_test.html
new file mode 100644
index 0000000..a91c585
--- /dev/null
+++ b/src/tests/shared/resources/osr_test.html
@@ -0,0 +1,198 @@
+<html>
+  <head><title>OSR Test</title></head>
+  <style>
+  .red_hover:hover {color:red;}
+  #li { width: 530px; }
+  body {background:rgba(255, 0, 0, 0.5); }
+  input {-webkit-appearance: none; }
+  #LI11select {width: 75px;}
+  #LI11select option { background-color: cyan; }
+  .dropdiv {
+    width:50px;
+    height:50px;
+    border:1px solid #aaaaaa;
+    float: left;
+  }
+  #dragdiv {
+    width: 30px;
+    height: 30px;
+    background-color: green;
+    margin: 10px;
+  }
+  #draghere {
+    position: relative;
+    z-index: -1;
+    top: 7px;
+    left: 7px;
+    opacity: 0.4;
+  }
+  #touchdiv, #pointerdiv {
+    width: 100px;
+    height: 50px;
+    background-color: red;
+    float: left;
+    margin-left: 10px;
+  }
+  </style>
+  <script>
+  function getElement(id) { return document.getElementById(id); }
+  function makeH1Red() { getElement('LI00').style.color='red'; }
+  function makeH1Black() { getElement('LI00').style.color='black'; }
+  function navigate() { location.href='?k='+getElement('editbox').value; }
+  function load() {
+    var elems = [];
+    var param = { type: 'ElementBounds', elems: elems };
+
+    elems.push(getElementBounds('LI00'));
+    elems.push(getElementBounds('LI01'));
+    elems.push(getElementBounds('LI02'));
+    elems.push(getElementBounds('LI03'));
+    elems.push(getElementBounds('LI04'));
+    elems.push(getElementBounds('LI05'));
+    elems.push(getElementBounds('LI06'));
+    elems.push(getElementBounds('LI07'));
+    elems.push(getElementBounds('LI08'));
+    elems.push(getElementBounds('LI09'));
+    elems.push(getElementBounds('LI10'));
+    elems.push(getElementBounds('LI11'));
+    elems.push(getElementBounds('LI11select'));
+    elems.push(getElementBounds('email'));
+    elems.push(getElementBounds('editbox'));
+    elems.push(getElementBounds('btnnavigate'));
+    elems.push(getElementBounds('dropdiv'));
+    elems.push(getElementBounds('dragdiv'));
+    elems.push(getElementBounds('touchdiv'));
+    elems.push(getElementBounds('pointerdiv'));
+
+    if (window.testQuery)
+      window.testQuery({request: JSON.stringify(param)});
+
+    fillDropDown();
+  }
+
+  function fillDropDown() {
+    var select = document.getElementById('LI11select');
+    for (var i = 1; i < 21; i++)
+      select.options.add(new Option('Option ' + i, i));
+  }
+
+  function getElementBounds(id) {
+    var element = document.getElementById(id);
+    var bounds = element.getBoundingClientRect();
+    return {
+      id: id,
+      x: Math.floor(bounds.x),
+      y: Math.floor(bounds.y),
+      width: Math.floor(bounds.width),
+      height: Math.floor(bounds.height)
+    };
+  }
+
+  function onEventTest(event) {
+    var param = 'osr' + event.type;
+
+    if (event.type == "click")
+      param += event.button;
+
+    // Results in a call to the OnQuery method in os_rendering_unittest.cc.
+    if (window.testQuery)
+      window.testQuery({request: param});
+  }
+
+  function allowDrop(ev) {
+    ev.preventDefault();
+  }
+
+  function drag(ev) {
+    ev.dataTransfer.setData("Text",ev.target.id);
+  }
+
+  function drop(ev) {
+    var data=ev.dataTransfer.getData("Text");
+    ev.target.innerHTML = '';
+    var dragged = document.getElementById(data);
+    dragged.setAttribute('draggable', 'false');
+    ev.target.appendChild(dragged);
+    if (window.testQuery)
+      window.testQuery({request: "osrdrop"});
+  }
+
+  function selectText(ev) {
+    var element = ev.target;
+    var selection = window.getSelection();
+    var range = document.createRange();
+    range.selectNodeContents(element);
+    selection.removeAllRanges();
+    selection.addRange(range);
+  }
+
+  function onTouchEvent(ev) {
+    var param = 'osr' + ev.type;
+    // For Touch start also include touch points.
+    if (event.type == "touchstart")
+      param += ev.touches.length;
+    // For Touch Move include the touches that changed.
+    if (event.type == "touchmove")
+      param += ev.changedTouches.length;
+
+    // Results in a call to the OnQuery method in os_rendering_unittest.cc.
+    if (window.testQuery)
+      window.testQuery({request: param});
+  }
+
+  function onPointerEvent(ev) {
+    var param = 'osr' +  ev.type + ' ' + ev.pointerType;
+    if (window.testQuery)
+      window.testQuery({request: param});
+  }
+
+  </script>
+  <body onfocus='onEventTest(event)' onblur='onEventTest(event)' onload='load();'>
+  <h1 id='LI00' onclick="onEventTest(event)">
+    OSR Testing h1 - Focus and blur
+    <select id='LI11select'>
+      <option value='0'>Default</option>
+    </select>
+    this page and will get this red black
+  </h1>
+  <ol>
+  <li id='LI01'>OnPaint should be called each time a page loads</li>
+  <li id='LI02' style='cursor:pointer;'><span>Move mouse
+      to require an OnCursorChange call</span></li>
+  <li id='LI03' onmousemove="onEventTest(event)"><span>Hover will color this with
+      red. Will trigger OnPaint once on enter and once on leave</span></li>
+  <li id='LI04'>Right clicking will show contextual menu and will request
+      GetScreenPoint</li>
+  <li id='LI05'>IsWindowRenderingDisabled should be true</li>
+  <li id='LI06'>WasResized should trigger full repaint if size changes.
+      </li>
+  <li id='LI07'>Invalidate should trigger OnPaint once</li>
+  <li id='LI08'>Click and write here with SendKeyEvent to trigger repaints:
+      <input id='editbox' type='text' value='' size="5"></li>
+  <li id='LI09'>Click here with SendMouseClickEvent to navigate:
+      <input id='btnnavigate' type='button' onclick='navigate()'
+      value='Click here to navigate' /></li>
+  <li id='LI10' title='EXPECTED_TOOLTIP'>Mouse over this element will
+      trigger show a tooltip</li>
+  <li id='LI11' onclick='selectText(event)'>SELECTED_TEXT_RANGE</li>
+  <li><input id='email' type='text' size=10 inputmode='email'></li>
+  </ol>
+
+  <div class="dropdiv" id="dropdiv" ondrop="drop(event)" ondragover="allowDrop(event)">
+    <span id="draghere">Drag here</span>
+  </div>
+  <div class="dropdiv">
+    <div id="dragdiv" draggable="true" ondragstart="drag(event)"></div>
+  </div>
+  <div id="touchdiv" ontouchstart="onTouchEvent(event)" ontouchend="onTouchEvent(event)" ontouchmove="onTouchEvent(event)" ontouchcancel="onTouchEvent(event)">
+  </div>
+  <div id="pointerdiv" onpointerdown="onPointerEvent(event)" onpointerup="onPointerEvent(event)" onpointermove="onPointerEvent(event)" onpointercancel="onPointerEvent(event)">
+  </div>
+  <br />
+  <br />
+  <br />
+  <br />
+  <br />
+  <br />
+  </body>
+</html>
diff --git a/src/tests/shared/resources/pdf.html b/src/tests/shared/resources/pdf.html
new file mode 100644
index 0000000..619b1ce
--- /dev/null
+++ b/src/tests/shared/resources/pdf.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>PDF Test</title>
+</head>
+<body bgcolor="white">
+<iframe src="pdf.pdf" width="500" height="500"></iframe>
+<iframe src="pdf.pdf" width="500" height="500"></iframe>
+</body>
+</html>
diff --git a/src/tests/shared/resources/pdf.pdf b/src/tests/shared/resources/pdf.pdf
new file mode 100644
index 0000000..1e0dad5
--- /dev/null
+++ b/src/tests/shared/resources/pdf.pdf
Binary files differ
diff --git a/src/tests/shared/resources/window_icon.1x.png b/src/tests/shared/resources/window_icon.1x.png
new file mode 100644
index 0000000..9d28862
--- /dev/null
+++ b/src/tests/shared/resources/window_icon.1x.png
Binary files differ
diff --git a/src/tests/shared/resources/window_icon.2x.png b/src/tests/shared/resources/window_icon.2x.png
new file mode 100644
index 0000000..59b9d49
--- /dev/null
+++ b/src/tests/shared/resources/window_icon.2x.png
Binary files differ
diff --git a/src/tools/automate/automate-git.py b/src/tools/automate/automate-git.py
new file mode 100644
index 0000000..b33e4a5
--- /dev/null
+++ b/src/tools/automate/automate-git.py
@@ -0,0 +1,1625 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from datetime import datetime
+import json
+from io import open
+from optparse import OptionParser
+import os
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import tempfile
+import zipfile
+
+is_python2 = sys.version_info.major == 2
+
+if is_python2:
+  from urllib import FancyURLopener
+  from urllib2 import urlopen
+else:
+  from urllib.request import FancyURLopener, urlopen
+
+##
+# Default URLs.
+##
+
+depot_tools_url = 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
+depot_tools_archive_url = 'https://storage.googleapis.com/chrome-infra/depot_tools.zip'
+
+cef_git_url = 'https://bitbucket.org/chromiumembedded/cef.git'
+
+chromium_channel_json_url = 'https://omahaproxy.appspot.com/all.json'
+
+##
+# Global system variables.
+##
+
+# Script directory.
+script_dir = os.path.dirname(__file__)
+
+##
+# Helper functions.
+##
+
+
+def msg(message):
+  """ Output a message. """
+  sys.stdout.write('--> ' + message + "\n")
+
+
+def run(command_line, working_dir, depot_tools_dir=None, output_file=None):
+  """ Runs the specified command. """
+  # add depot_tools to the path
+  env = os.environ
+  if not depot_tools_dir is None:
+    env['PATH'] = depot_tools_dir + os.pathsep + env['PATH']
+
+  sys.stdout.write('-------- Running "'+command_line+'" in "'+\
+                   working_dir+'"...'+"\n")
+  if not options.dryrun:
+    args = shlex.split(command_line.replace('\\', '\\\\'))
+
+    if not output_file:
+      return subprocess.check_call(
+          args, cwd=working_dir, env=env, shell=(sys.platform == 'win32'))
+    try:
+      msg('Writing %s' % output_file)
+      with open(output_file, 'w', encoding='utf-8') as fp:
+        return subprocess.check_call(
+            args,
+            cwd=working_dir,
+            env=env,
+            shell=(sys.platform == 'win32'),
+            stderr=subprocess.STDOUT,
+            stdout=fp)
+    except subprocess.CalledProcessError:
+      msg('ERROR Run failed. See %s for output.' % output_file)
+      raise
+
+
+def create_directory(path):
+  """ Creates a directory if it doesn't already exist. """
+  if not os.path.exists(path):
+    msg("Creating directory %s" % (path))
+    if not options.dryrun:
+      os.makedirs(path)
+
+
+def delete_directory(path):
+  """ Removes an existing directory. """
+  if os.path.exists(path):
+    msg("Removing directory %s" % (path))
+    if not options.dryrun:
+      shutil.rmtree(path, onerror=onerror)
+
+
+def copy_directory(source, target, allow_overwrite=False):
+  """ Copies a directory from source to target. """
+  if not options.dryrun and os.path.exists(target):
+    if not allow_overwrite:
+      raise Exception("Directory %s already exists" % (target))
+    remove_directory(target)
+  if os.path.exists(source):
+    msg("Copying directory %s to %s" % (source, target))
+    if not options.dryrun:
+      shutil.copytree(source, target)
+
+
+def move_directory(source, target, allow_overwrite=False):
+  """ Copies a directory from source to target. """
+  if not options.dryrun and os.path.exists(target):
+    if not allow_overwrite:
+      raise Exception("Directory %s already exists" % (target))
+    remove_directory(target)
+  if os.path.exists(source):
+    msg("Moving directory %s to %s" % (source, target))
+    if not options.dryrun:
+      shutil.move(source, target)
+
+
+def is_git_checkout(path):
+  """ Returns true if the path represents a git checkout. """
+  return os.path.exists(os.path.join(path, '.git'))
+
+
+def exec_cmd(cmd, path):
+  """ Execute the specified command and return the result. """
+  out = ''
+  err = ''
+  sys.stdout.write("-------- Running \"%s\" in \"%s\"...\n" % (cmd, path))
+  parts = cmd.split()
+  try:
+    process = subprocess.Popen(
+        parts,
+        cwd=path,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        shell=(sys.platform == 'win32'))
+    out, err = process.communicate()
+  except IOError as e:
+    (errno, strerror) = e.args
+    raise
+  except:
+    raise
+  return {'out': out.decode('utf-8'), 'err': err.decode('utf-8')}
+
+
+def get_git_hash(path, branch):
+  """ Returns the git hash for the specified branch/tag/hash. """
+  cmd = "%s rev-parse %s" % (git_exe, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return result['out'].strip()
+  return 'Unknown'
+
+
+def get_git_date(path, branch):
+  """ Returns the date for the specified branch/tag/hash. """
+  cmd = "%s show -s --format=%%ct %s" % (git_exe, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return datetime.utcfromtimestamp(
+        int(result['out'].strip())).strftime('%Y-%m-%d %H:%M:%S UTC')
+  return 'Unknown'
+
+
+def get_git_url(path):
+  """ Returns the origin url for the specified path. """
+  cmd = "%s config --get remote.origin.url" % (git_exe)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return result['out'].strip()
+  return 'Unknown'
+
+
+def download_and_extract(src, target):
+  """ Extracts the contents of src, which may be a URL or local file, to the
+      target directory. """
+  temporary = False
+
+  if src[:4] == 'http':
+    # Attempt to download a URL.
+    opener = FancyURLopener({})
+    response = opener.open(src)
+
+    temporary = True
+    handle, archive_path = tempfile.mkstemp(suffix='.zip')
+    os.write(handle, response.read())
+    os.close(handle)
+  elif os.path.exists(src):
+    # Use a local file.
+    archive_path = src
+  else:
+    raise Exception('Path type is unsupported or does not exist: ' + src)
+
+  if not zipfile.is_zipfile(archive_path):
+    raise Exception('Not a valid zip archive: ' + src)
+
+  # Attempt to extract the archive file.
+  try:
+    os.makedirs(target)
+    zf = zipfile.ZipFile(archive_path, 'r')
+    zf.extractall(target)
+  except:
+    shutil.rmtree(target, onerror=onerror)
+    raise
+  zf.close()
+
+  # Delete the archive file if temporary.
+  if temporary and os.path.exists(archive_path):
+    os.remove(archive_path)
+
+
+def read_file(path):
+  """ Read a file. """
+  if os.path.exists(path):
+    with open(path, 'r', encoding='utf-8') as fp:
+      return fp.read()
+  else:
+    raise Exception("Path does not exist: %s" % (path))
+
+
+def write_fp(fp, data):
+  if is_python2:
+    fp.write(data.decode('utf-8'))
+  else:
+    fp.write(data)
+
+
+def write_file(path, data):
+  """ Write a file. """
+  msg('Writing %s' % path)
+  if not options.dryrun:
+    with open(path, 'w', encoding='utf-8') as fp:
+      write_fp(fp, data)
+
+
+def read_config_file(path):
+  """ Read a configuration file. """
+  # Parse the contents.
+  return eval(read_file(path), {'__builtins__': None}, None)
+
+
+def write_config_file(path, contents):
+  """ Write a configuration file. """
+  data = "{\n"
+  for key in sorted(contents.keys()):
+    data += "  '%s': '%s',\n" % (key, contents[key])
+  data += "}\n"
+  write_file(path, data)
+
+
+def read_branch_config_file(path):
+  """ Read the CEF branch from the specified path. """
+  config_file = os.path.join(path, 'cef.branch')
+  if os.path.isfile(config_file):
+    contents = read_config_file(config_file)
+    if 'branch' in contents:
+      return contents['branch']
+  return ''
+
+
+def write_branch_config_file(path, branch):
+  """ Write the CEF branch to the specified path. """
+  config_file = os.path.join(path, 'cef.branch')
+  if not os.path.isfile(config_file):
+    write_config_file(config_file, {'branch': branch})
+
+
+def apply_patch(name):
+  patch_file = os.path.join(cef_dir, 'patch', 'patches', name)
+  if os.path.exists(patch_file + ".patch"):
+    # Attempt to apply the patch file.
+    patch_tool = os.path.join(cef_dir, 'tools', 'patcher.py')
+    run('%s %s --patch-file "%s" --patch-dir "%s"' %
+        (python_exe, patch_tool, patch_file,
+         chromium_src_dir), chromium_src_dir, depot_tools_dir)
+
+
+def apply_deps_patch():
+  """ Patch the Chromium DEPS file before `gclient sync` if necessary. """
+  deps_path = os.path.join(chromium_src_dir, deps_file)
+  if os.path.isfile(deps_path):
+    msg("Chromium DEPS file: %s" % (deps_path))
+    apply_patch(deps_file)
+  else:
+    raise Exception("Path does not exist: %s" % (deps_path))
+
+
+def apply_runhooks_patch():
+  """ Patch the Chromium runhooks files before `gclient runhooks` if necessary. """
+  apply_patch('runhooks')
+
+
+def run_patch_updater(args='', output_file=None):
+  """ Run the patch updater script. """
+  tool = os.path.join(cef_src_dir, 'tools', 'patch_updater.py')
+  if len(args) > 0:
+    args = ' ' + args
+  run('%s %s%s' % (python_exe, tool, args), cef_src_dir, depot_tools_dir,
+      output_file)
+
+
+def onerror(func, path, exc_info):
+  """
+  Error handler for ``shutil.rmtree``.
+
+  If the error is due to an access error (read only file)
+  it attempts to add write permission and then retries.
+
+  If the error is for another reason it re-raises the error.
+
+  Usage : ``shutil.rmtree(path, onerror=onerror)``
+  """
+  import stat
+  if not os.access(path, os.W_OK):
+    # Is the error an access error ?
+    os.chmod(path, stat.S_IWUSR)
+    func(path)
+  else:
+    raise
+
+
+def read_json_url(url):
+  """ Read a JSON URL. """
+  msg('Downloading %s' % url)
+  return json.loads(urlopen(url).read())
+
+
+g_channel_data = None
+
+
+def get_chromium_channel_data(os, channel, param=None):
+  """ Returns all data for the specified Chromium channel. """
+  global g_channel_data
+
+  if g_channel_data is None:
+    g_channel_data = read_json_url(chromium_channel_json_url)
+    assert len(g_channel_data) > 0, 'Failed to load Chromium channel data'
+
+  for oses in g_channel_data:
+    if oses['os'] == os:
+      for version in oses['versions']:
+        if version['channel'] == channel:
+          assert version['os'] == os
+          assert version['channel'] == channel
+          if param is None:
+            return version
+          else:
+            assert param in version, 'Missing parameter %s for Chromium channel %s %s' % (
+                param, os, channel)
+            return version[param]
+      raise Exception("Invalid Chromium channel value: %s" % channel)
+  raise Exception("Invalid Chromium os value: %s" % os)
+
+
+def get_chromium_channel_commit(os, channel):
+  """ Returns the current branch commit for the specified Chromium channel. """
+  return get_chromium_channel_data(os, channel, 'branch_commit')
+
+
+def get_chromium_channel_version(os, channel):
+  """ Returns the current version for the specified Chromium channel. """
+  return get_chromium_channel_data(os, channel, 'current_version')
+
+
+def get_chromium_master_position(commit):
+  """ Returns the closest master position for the specified Chromium commit. """
+  # Using -2 because a "Publish DEPS" commit which does not have a master
+  # position may be first.
+  cmd = "%s log -2 %s" % (git_exe, commit)
+  result = exec_cmd(cmd, chromium_src_dir)
+  if result['out'] != '':
+    match = re.search(r'refs/heads/master@{#([\d]+)}', result['out'])
+    assert match != None, 'Failed to find position'
+    return int(match.groups()[0])
+  return None
+
+
+def get_chromium_master_commit(position):
+  """ Returns the master commit for the specified Chromium commit position. """
+  cmd = '%s log -1 --grep=refs/heads/master@{#%s} origin/master' % (
+      git_exe, str(position))
+  result = exec_cmd(cmd, chromium_src_dir)
+  if result['out'] != '':
+    match = re.search(r'^commit ([a-f0-9]+)', result['out'])
+    assert match != None, 'Failed to find commit'
+    return match.groups()[0]
+  return None
+
+
+def get_chromium_versions(commit):
+  """ Returns the list of Chromium versions that contain the specified commit.
+      Versions are listed oldest to newest. """
+  cmd = '%s tag --contains %s' % (git_exe, commit)
+  result = exec_cmd(cmd, chromium_src_dir)
+  if result['out'] != '':
+    return [line.strip() for line in result['out'].strip().split('\n')]
+  return None
+
+
+def get_build_compat_versions():
+  """ Returns the compatible Chromium and (optionally) depot_tools versions
+      specified by the CEF checkout. """
+  compat_path = os.path.join(cef_dir, 'CHROMIUM_BUILD_COMPATIBILITY.txt')
+  msg("Reading %s" % compat_path)
+  config = read_config_file(compat_path)
+
+  if not 'chromium_checkout' in config:
+    raise Exception("Missing chromium_checkout value in %s" % (compat_path))
+  return config
+
+
+def get_chromium_target_version(os='win', channel='canary', target_distance=0):
+  """ Returns the target Chromium version based on a heuristic. """
+  # The current compatible version from CEF.
+  compat_version = chromium_compat_version
+  compat_commit = get_git_hash(chromium_src_dir, compat_version)
+  if compat_version == compat_commit:
+    versions = get_chromium_versions(compat_commit)
+    if len(versions) > 0:
+      compat_version = 'refs/tags/' + versions[0]
+      # Closest version may not align with the compat position, so adjust the
+      # commit to match.
+      compat_commit = get_git_hash(chromium_src_dir, compat_version)
+  compat_position = get_chromium_master_position(compat_commit)
+  compat_date = get_git_date(chromium_src_dir, compat_commit)
+
+  # The most recent channel version from the Chromium website.
+  channel_version = 'refs/tags/' + get_chromium_channel_version(os, channel)
+  channel_commit = get_chromium_channel_commit(os, channel)
+  channel_position = get_chromium_master_position(channel_commit)
+  channel_date = get_git_date(chromium_src_dir, channel_commit)
+
+  if compat_position >= channel_position:
+    # Already compatible with the channel version or newer.
+    target_version = compat_version
+    target_commit = compat_commit
+    target_position = compat_position
+    target_date = compat_date
+  elif target_distance <= 0 or compat_position + target_distance >= channel_position:
+    # Channel version is within the target distance.
+    target_version = channel_version
+    target_commit = channel_commit
+    target_position = channel_position
+    target_date = channel_date
+  else:
+    # Find an intermediary version that's within the target distance.
+    target_position = compat_position + target_distance
+    target_commit = get_chromium_master_commit(target_position)
+    versions = get_chromium_versions(target_commit)
+    if len(versions) > 0:
+      target_version = 'refs/tags/' + versions[0]
+      # Closest version may not align with the target position, so adjust the
+      # commit and position to match.
+      target_commit = get_git_hash(chromium_src_dir, target_version)
+      target_position = get_chromium_master_position(target_commit)
+    else:
+      target_version = target_commit
+    target_date = get_git_date(chromium_src_dir, target_commit)
+
+  msg("")
+  msg("Computed Chromium update for %s %s at distance %d" % (os, channel,
+                                                             target_distance))
+  msg("Compat:  %s %s %s (#%d)" % (compat_date, compat_version, compat_commit,
+                                   compat_position))
+  msg("Target:  %s %s %s (#%d)" % (target_date, target_version, target_commit,
+                                   target_position))
+  msg("Channel: %s %s %s (#%d)" % (channel_date, channel_version,
+                                   channel_commit, channel_position))
+  msg("")
+
+  return target_version
+
+
+def get_build_directory_name(is_debug):
+  build_dir = ('Debug' if is_debug else 'Release') + '_'
+
+  # CEF uses a consistent directory naming scheme for GN via
+  # GetAllPlatformConfigs in tools/gn_args.py.
+  if options.x64build:
+    build_dir += 'GN_x64'
+  elif options.armbuild:
+    build_dir += 'GN_arm'
+  elif options.arm64build:
+    build_dir += 'GN_arm64'
+  else:
+    build_dir += 'GN_x86'
+  return build_dir
+
+
+def read_update_file():
+  update_path = os.path.join(cef_src_dir, 'CHROMIUM_UPDATE.txt')
+  if not os.path.exists(update_path):
+    msg("Missing file: %s" % update_path)
+    return None
+
+  msg("Reading %s" % update_path)
+  return read_config_file(update_path)
+
+
+def log_chromium_changes():
+  """ Evaluate the Chromium checkout for changes. """
+  config = read_update_file()
+  if config is None:
+    msg("Skipping Chromium changes log.")
+    return
+
+  if 'files' in config:
+    out_file = os.path.join(download_dir, 'chromium_update_changes.diff')
+    if os.path.exists(out_file):
+      os.remove(out_file)
+
+    old_commit = get_chromium_master_commit(
+        get_chromium_master_position(chromium_compat_version))
+    new_commit = get_chromium_master_commit(
+        get_chromium_master_position(chromium_checkout))
+
+    cmd = '%s diff --relative --no-prefix %s..%s -- %s' % (
+        git_exe, old_commit, new_commit, ' '.join(config['files']))
+    result = exec_cmd(cmd, chromium_src_dir)
+    if result['out'] != '':
+      write_file(out_file, result['out'])
+
+
+def check_pattern_matches(output_file=None):
+  """ Evaluate the Chromium checkout for pattern matches. """
+  config = read_update_file()
+  if config is None:
+    msg("Skipping Chromium pattern matching.")
+    return
+
+  if 'patterns' in config:
+    if output_file is None:
+      fp = sys.stdout
+    else:
+      msg('Writing %s' % output_file)
+      fp = open(output_file, 'w', encoding='utf-8')
+
+    has_output = False
+    for entry in config['patterns']:
+      msg("Evaluating pattern: %s" % entry['pattern'])
+
+      # Read patterns from a file to avoid formatting problems.
+      pattern_handle, pattern_file = tempfile.mkstemp()
+      os.write(pattern_handle, entry['pattern'])
+      os.close(pattern_handle)
+
+      cmd = '%s grep -n -f %s' % (git_exe, pattern_file)
+      result = exec_cmd(cmd, chromium_src_dir)
+      os.remove(pattern_file)
+
+      if result['out'] != '':
+        write_msg = True
+        re_exclude = re.compile(
+            entry['exclude_matches']) if 'exclude_matches' in entry else None
+
+        for line in result['out'].split('\n'):
+          line = line.strip()
+          if len(line) == 0:
+            continue
+          skip = not re_exclude is None and re_exclude.match(line) != None
+          if not skip:
+            if write_msg:
+              if has_output:
+                write_fp(fp, '\n')
+              write_fp(fp,
+                       '!!!! WARNING: FOUND PATTERN: %s\n' % entry['pattern'])
+              if 'message' in entry:
+                write_fp(fp, entry['message'] + '\n')
+              write_fp(fp, '\n')
+              write_msg = False
+            write_fp(fp, line + '\n')
+            has_output = True
+
+    if not output_file is None:
+      if has_output:
+        msg('ERROR Matches found. See %s for output.' % out_file)
+      else:
+        write_fp(fp, 'Good news! No matches.\n')
+      fp.close()
+
+    if has_output:
+      # Don't continue when we know the build will be wrong.
+      sys.exit(1)
+
+
+##
+# Program entry point.
+##
+
+# Cannot be loaded as a module.
+if __name__ != "__main__":
+  sys.stderr.write('This file cannot be loaded as a module!')
+  sys.exit()
+
+# Parse command-line options.
+disc = """
+This utility implements automation for the download, update, build and
+distribution of CEF.
+"""
+
+parser = OptionParser(description=disc)
+
+# Setup options.
+parser.add_option(
+    '--download-dir',
+    dest='downloaddir',
+    metavar='DIR',
+    help='Download directory with no spaces [required].')
+parser.add_option(
+    '--depot-tools-dir',
+    dest='depottoolsdir',
+    metavar='DIR',
+    help='Download directory for depot_tools.',
+    default='')
+parser.add_option('--depot-tools-archive', dest='depottoolsarchive',
+                  help='Zip archive file that contains a single top-level '+\
+                       'depot_tools directory.', default='')
+parser.add_option('--branch', dest='branch',
+                  help='Branch of CEF to build (master, 3987, ...). This '+\
+                       'will be used to name the CEF download directory and '+\
+                       'to identify the correct URL if --url is not '+\
+                       'specified. The default value is master.',
+                  default='master')
+parser.add_option('--url', dest='url',
+                  help='CEF download URL. If not specified the default URL '+\
+                       'will be used.',
+                  default='')
+parser.add_option('--chromium-url', dest='chromiumurl',
+                  help='Chromium download URL. If not specified the default '+\
+                       'URL will be used.',
+                  default='')
+parser.add_option('--checkout', dest='checkout',
+                  help='Version of CEF to checkout. If not specified the '+\
+                       'most recent remote version of the branch will be used.',
+                  default='')
+parser.add_option('--chromium-checkout', dest='chromiumcheckout',
+                  help='Version of Chromium to checkout (Git '+\
+                       'branch/hash/tag). This overrides the value specified '+\
+                       'by CEF in CHROMIUM_BUILD_COMPATIBILITY.txt.',
+                  default='')
+parser.add_option('--chromium-channel', dest='chromiumchannel',
+                  help='Chromium channel to check out (canary, dev, beta or '+\
+                       'stable). This overrides the value specified by CEF '+\
+                       'in CHROMIUM_BUILD_COMPATIBILITY.txt.',
+                  default='')
+parser.add_option('--chromium-channel-distance', dest='chromiumchanneldistance',
+                  help='The target number of commits to step in the '+\
+                       'channel, or 0 to use the newest channel version. '+\
+                       'Used in combination with --chromium-channel.',
+                  default='')
+
+# Miscellaneous options.
+parser.add_option(
+    '--force-config',
+    action='store_true',
+    dest='forceconfig',
+    default=False,
+    help='Force creation of a new gclient config file.')
+parser.add_option('--force-clean',
+                  action='store_true', dest='forceclean', default=False,
+                  help='Force a clean checkout of Chromium and CEF. This will'+\
+                       ' trigger a new update, build and distribution.')
+parser.add_option('--force-clean-deps',
+                  action='store_true', dest='forcecleandeps', default=False,
+                  help='Force a clean checkout of Chromium dependencies. Used'+\
+                       ' in combination with --force-clean.')
+parser.add_option(
+    '--dry-run',
+    action='store_true',
+    dest='dryrun',
+    default=False,
+    help="Output commands without executing them.")
+parser.add_option('--dry-run-platform', dest='dryrunplatform', default=None,
+                  help='Simulate a dry run on the specified platform '+\
+                       '(windows, macosx, linux). Must be used in combination'+\
+                       ' with the --dry-run flag.')
+
+# Update-related options.
+parser.add_option('--force-update',
+                  action='store_true', dest='forceupdate', default=False,
+                  help='Force a Chromium and CEF update. This will trigger a '+\
+                       'new build and distribution.')
+parser.add_option('--no-update',
+                  action='store_true', dest='noupdate', default=False,
+                  help='Do not update Chromium or CEF. Pass --force-build or '+\
+                       '--force-distrib if you desire a new build or '+\
+                       'distribution.')
+parser.add_option('--no-cef-update',
+                  action='store_true', dest='nocefupdate', default=False,
+                  help='Do not update CEF. Pass --force-build or '+\
+                       '--force-distrib if you desire a new build or '+\
+                       'distribution.')
+parser.add_option('--force-cef-update',
+                  action='store_true', dest='forcecefupdate', default=False,
+                  help='Force a CEF update. This will cause local changes in '+\
+                       'the CEF checkout to be discarded and patch files to '+\
+                       'be reapplied.')
+parser.add_option(
+    '--no-chromium-update',
+    action='store_true',
+    dest='nochromiumupdate',
+    default=False,
+    help='Do not update Chromium.')
+parser.add_option(
+    '--no-depot-tools-update',
+    action='store_true',
+    dest='nodepottoolsupdate',
+    default=False,
+    help='Do not update depot_tools.')
+parser.add_option('--fast-update',
+                  action='store_true', dest='fastupdate', default=False,
+                  help='Update existing Chromium/CEF checkouts for fast incremental '+\
+                       'builds by attempting to minimize the number of modified files. '+\
+                       'The update will fail if there are unstaged CEF changes or if '+\
+                       'Chromium changes are not included in a patch file.')
+parser.add_option(
+    '--force-patch-update',
+    action='store_true',
+    dest='forcepatchupdate',
+    default=False,
+    help='Force update of patch files.')
+parser.add_option(
+    '--resave',
+    action='store_true',
+    dest='resave',
+    default=False,
+    help='Resave patch files.')
+parser.add_option(
+    '--log-chromium-changes',
+    action='store_true',
+    dest='logchromiumchanges',
+    default=False,
+    help='Create a log of the Chromium changes.')
+
+# Build-related options.
+parser.add_option('--force-build',
+                  action='store_true', dest='forcebuild', default=False,
+                  help='Force CEF debug and release builds. This builds '+\
+                       '[build-target] on all platforms and chrome_sandbox '+\
+                       'on Linux.')
+parser.add_option(
+    '--no-build',
+    action='store_true',
+    dest='nobuild',
+    default=False,
+    help='Do not build CEF.')
+parser.add_option(
+    '--build-target',
+    dest='buildtarget',
+    default='cefclient',
+    help='Target name(s) to build (defaults to "cefclient").')
+parser.add_option(
+    '--build-tests',
+    action='store_true',
+    dest='buildtests',
+    default=False,
+    help='Also build the test target specified via --test-target.')
+parser.add_option(
+    '--no-debug-build',
+    action='store_true',
+    dest='nodebugbuild',
+    default=False,
+    help="Don't perform the CEF debug build.")
+parser.add_option(
+    '--no-release-build',
+    action='store_true',
+    dest='noreleasebuild',
+    default=False,
+    help="Don't perform the CEF release build.")
+parser.add_option(
+    '--verbose-build',
+    action='store_true',
+    dest='verbosebuild',
+    default=False,
+    help='Show all command lines while building.')
+parser.add_option(
+    '--build-failure-limit',
+    dest='buildfailurelimit',
+    default=1,
+    type="int",
+    help='Keep going until N jobs fail.')
+parser.add_option('--build-log-file',
+                  action='store_true', dest='buildlogfile', default=False,
+                  help='Write build logs to file. The file will be named '+\
+                       '"build-[branch]-[debug|release].log" in the download '+\
+                       'directory.')
+parser.add_option(
+    '--x64-build',
+    action='store_true',
+    dest='x64build',
+    default=False,
+    help='Create a 64-bit build.')
+parser.add_option(
+    '--arm-build',
+    action='store_true',
+    dest='armbuild',
+    default=False,
+    help='Create an ARM build.')
+parser.add_option(
+    '--arm64-build',
+    action='store_true',
+    dest='arm64build',
+    default=False,
+    help='Create an ARM64 build.')
+
+# Test-related options.
+parser.add_option(
+    '--run-tests',
+    action='store_true',
+    dest='runtests',
+    default=False,
+    help='Run the ceftests target.')
+parser.add_option(
+    '--no-debug-tests',
+    action='store_true',
+    dest='nodebugtests',
+    default=False,
+    help="Don't run debug build tests.")
+parser.add_option(
+    '--no-release-tests',
+    action='store_true',
+    dest='noreleasetests',
+    default=False,
+    help="Don't run release build tests.")
+parser.add_option(
+    '--test-target',
+    dest='testtarget',
+    default='ceftests',
+    help='Test target name to build (defaults to "ceftests").')
+parser.add_option(
+    '--test-prefix',
+    dest='testprefix',
+    default='',
+    help='Prefix for running the test executable (e.g. `xvfb-run` on Linux).')
+parser.add_option(
+    '--test-args',
+    dest='testargs',
+    default='',
+    help='Arguments that will be passed to the test executable.')
+
+# Distribution-related options.
+parser.add_option(
+    '--force-distrib',
+    action='store_true',
+    dest='forcedistrib',
+    default=False,
+    help='Force creation of a CEF binary distribution.')
+parser.add_option(
+    '--no-distrib',
+    action='store_true',
+    dest='nodistrib',
+    default=False,
+    help="Don't create a CEF binary distribution.")
+parser.add_option(
+    '--minimal-distrib',
+    action='store_true',
+    dest='minimaldistrib',
+    default=False,
+    help='Create a minimal CEF binary distribution.')
+parser.add_option(
+    '--minimal-distrib-only',
+    action='store_true',
+    dest='minimaldistribonly',
+    default=False,
+    help='Create a minimal CEF binary distribution only.')
+parser.add_option(
+    '--client-distrib',
+    action='store_true',
+    dest='clientdistrib',
+    default=False,
+    help='Create a client CEF binary distribution.')
+parser.add_option(
+    '--client-distrib-only',
+    action='store_true',
+    dest='clientdistribonly',
+    default=False,
+    help='Create a client CEF binary distribution only.')
+parser.add_option(
+    '--sandbox-distrib',
+    action='store_true',
+    dest='sandboxdistrib',
+    default=False,
+    help='Create a cef_sandbox static library distribution.')
+parser.add_option(
+    '--sandbox-distrib-only',
+    action='store_true',
+    dest='sandboxdistribonly',
+    default=False,
+    help='Create a cef_sandbox static library distribution only.')
+parser.add_option(
+    '--no-distrib-docs',
+    action='store_true',
+    dest='nodistribdocs',
+    default=False,
+    help="Don't create CEF documentation.")
+parser.add_option(
+    '--no-distrib-archive',
+    action='store_true',
+    dest='nodistribarchive',
+    default=False,
+    help="Don't create archives for output directories.")
+parser.add_option(
+    '--clean-artifacts',
+    action='store_true',
+    dest='cleanartifacts',
+    default=False,
+    help='Clean the artifacts output directory.')
+parser.add_option('--distrib-subdir', dest='distribsubdir',
+                  help='CEF distrib dir name, child of '+\
+                       'chromium/src/cef/binary_distrib',
+                  default='')
+
+(options, args) = parser.parse_args()
+
+if options.downloaddir is None:
+  print("The --download-dir option is required.")
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+# Opt into component-specific flags for later use.
+if options.noupdate:
+  options.nocefupdate = True
+  options.nochromiumupdate = True
+  options.nodepottoolsupdate = True
+
+if options.runtests:
+  options.buildtests = True
+
+if (options.nochromiumupdate and options.forceupdate) or \
+   (options.nocefupdate and options.forceupdate) or \
+   (options.nobuild and options.forcebuild) or \
+   (options.nodistrib and options.forcedistrib) or \
+   ((options.forceclean or options.forcecleandeps) and options.fastupdate) or \
+   (options.chromiumcheckout and options.chromiumchannel):
+  print("Invalid combination of options.")
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if (options.noreleasebuild and \
+     (options.minimaldistrib or options.minimaldistribonly or \
+      options.clientdistrib or options.clientdistribonly)) or \
+   (options.minimaldistribonly + options.clientdistribonly + options.sandboxdistribonly > 1):
+  print('Invalid combination of options.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if options.x64build + options.armbuild + options.arm64build > 1:
+  print('Invalid combination of options.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if (options.buildtests or options.runtests) and len(options.testtarget) == 0:
+  print("A test target must be specified via --test-target.")
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+# Operating system.
+if options.dryrun and options.dryrunplatform is not None:
+  platform = options.dryrunplatform
+  if not platform in ['windows', 'macosx', 'linux']:
+    print('Invalid dry-run-platform value: %s' % (platform))
+    sys.exit()
+elif sys.platform == 'win32':
+  platform = 'windows'
+elif sys.platform == 'darwin':
+  platform = 'macosx'
+elif sys.platform.startswith('linux'):
+  platform = 'linux'
+else:
+  print('Unknown operating system platform')
+  sys.exit()
+
+if options.clientdistrib or options.clientdistribonly:
+  if platform == 'linux':
+    client_app = 'cefsimple'
+  else:
+    client_app = 'cefclient'
+  if options.buildtarget.find(client_app) == -1:
+    print('A client distribution cannot be generated if --build-target ' +
+          'excludes %s.' % client_app)
+    parser.print_help(sys.stderr)
+    sys.exit()
+
+# CEF branch.
+cef_branch = options.branch
+
+branch_is_master = (cef_branch == 'master' or cef_branch == 'trunk')
+if not branch_is_master:
+  # Verify that the branch value is numeric.
+  if not cef_branch.isdigit():
+    print('Invalid branch value: %s' % cef_branch)
+    sys.exit()
+
+  # Verify the minimum supported branch number.
+  if int(cef_branch) < 3071:
+    print('The requested branch (%s) is too old to build using this tool. ' +
+          'The minimum supported branch is 3071.' % cef_branch)
+    sys.exit()
+
+# True if the requested branch is 3538 or newer.
+branch_is_3538_or_newer = (branch_is_master or int(cef_branch) >= 3538)
+
+# True if the requested branch is 3945 or newer.
+branch_is_3945_or_newer = (branch_is_master or int(cef_branch) >= 3945)
+
+# Enable Python 3 usage in Chromium for branches 3945 and newer.
+if branch_is_3945_or_newer and not is_python2 and \
+    not 'GCLIENT_PY3' in os.environ.keys():
+  os.environ['GCLIENT_PY3'] = '1'
+
+if not branch_is_3945_or_newer and \
+  (not is_python2 or bool(int(os.environ.get('GCLIENT_PY3', '0')))):
+  print('Python 3 is not supported with branch 3904 and older ' +
+        '(set GCLIENT_PY3=0 and run with Python 2 executable).')
+  sys.exit()
+
+if options.armbuild:
+  if platform != 'linux':
+    print('The ARM build option is only supported on Linux.')
+    sys.exit()
+
+if options.arm64build:
+  if platform != 'linux' and platform != 'windows':
+    print('The ARM64 build option is only supported on Linux and Windows.')
+    sys.exit()
+
+deps_file = 'DEPS'
+
+if platform == 'macosx' and not options.x64build:
+  print('32-bit Mac OS X builds are not supported. ' +
+        'Add --x64-build flag to generate a 64-bit build.')
+  sys.exit()
+
+# Platforms that build a cef_sandbox library.
+sandbox_lib_platforms = ['windows']
+if branch_is_3538_or_newer:
+  sandbox_lib_platforms.append('macosx')
+
+if not platform in sandbox_lib_platforms and (options.sandboxdistrib or
+                                              options.sandboxdistribonly):
+  print('The sandbox distribution is not supported on this platform.')
+  sys.exit()
+
+# Options that force the sources to change.
+force_change = options.forceclean or options.forceupdate
+
+# Options that cause local changes to be discarded.
+discard_local_changes = force_change or options.forcecefupdate
+
+if options.resave and (options.forcepatchupdate or discard_local_changes):
+  print('--resave cannot be combined with options that modify or discard ' +
+        'patches.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if platform == 'windows':
+  # Avoid errors when the "vs_toolchain.py update" Chromium hook runs.
+  os.environ['DEPOT_TOOLS_WIN_TOOLCHAIN'] = '0'
+
+download_dir = os.path.abspath(options.downloaddir)
+chromium_dir = os.path.join(download_dir, 'chromium')
+chromium_src_dir = os.path.join(chromium_dir, 'src')
+out_src_dir = os.path.join(chromium_src_dir, 'out')
+cef_src_dir = os.path.join(chromium_src_dir, 'cef')
+
+if options.fastupdate and os.path.exists(cef_src_dir):
+  cef_dir = cef_src_dir
+else:
+  cef_dir = os.path.join(download_dir, 'cef')
+
+##
+# Manage the download directory.
+##
+
+# Create the download directory if necessary.
+create_directory(download_dir)
+
+msg("Download Directory: %s" % (download_dir))
+
+##
+# Manage the depot_tools directory.
+##
+
+# Check if the depot_tools directory exists.
+if options.depottoolsdir != '':
+  depot_tools_dir = os.path.abspath(options.depottoolsdir)
+else:
+  depot_tools_dir = os.path.join(download_dir, 'depot_tools')
+
+msg("Depot Tools Directory: %s" % (depot_tools_dir))
+
+if not os.path.exists(depot_tools_dir):
+  if platform == 'windows' and options.depottoolsarchive == '':
+    # On Windows download depot_tools as an archive file since we can't assume
+    # that git is already installed.
+    options.depottoolsarchive = depot_tools_archive_url
+
+  if options.depottoolsarchive != '':
+    # Extract depot_tools from an archive file.
+    msg('Extracting %s to %s.' % \
+        (options.depottoolsarchive, depot_tools_dir))
+    if not options.dryrun:
+      download_and_extract(options.depottoolsarchive, depot_tools_dir)
+  else:
+    # On Linux and OS X check out depot_tools using Git.
+    run('git clone ' + depot_tools_url + ' ' + depot_tools_dir, download_dir)
+
+if not options.nodepottoolsupdate:
+  # Update depot_tools.
+  # On Windows this will download required python and git binaries.
+  msg('Updating depot_tools')
+  if platform == 'windows':
+    run('update_depot_tools.bat', depot_tools_dir, depot_tools_dir)
+  else:
+    run('update_depot_tools', depot_tools_dir, depot_tools_dir)
+
+# Determine the executables to use.
+if platform == 'windows':
+  # Force use of the version bundled with depot_tools.
+  git_exe = os.path.join(depot_tools_dir, 'git.bat')
+  python_bat = 'python.bat' if is_python2 else 'python3.bat'
+  python_exe = os.path.join(depot_tools_dir, python_bat)
+  if options.dryrun and not os.path.exists(git_exe):
+    sys.stdout.write("WARNING: --dry-run assumes that depot_tools" \
+                     " is already in your PATH. If it isn't\nplease" \
+                     " specify a --depot-tools-dir value.\n")
+    git_exe = 'git.bat'
+    python_exe = python_bat
+else:
+  git_exe = 'git'
+  python_exe = sys.executable
+
+##
+# Manage the cef directory.
+##
+
+# Delete the existing CEF directory if requested.
+if options.forceclean and os.path.exists(cef_dir):
+  delete_directory(cef_dir)
+
+# Determine the type of CEF checkout to use.
+if os.path.exists(cef_dir) and not is_git_checkout(cef_dir):
+  raise Exception("Not a valid CEF Git checkout: %s" % (cef_dir))
+
+# Determine the CEF download URL to use.
+cef_url = options.url.strip()
+if cef_url == '':
+  cef_url = cef_git_url
+
+# Verify that the requested CEF URL matches the existing checkout.
+if not options.nocefupdate and os.path.exists(cef_dir):
+  cef_existing_url = get_git_url(cef_dir)
+  if cef_url != cef_existing_url:
+    raise Exception(
+        'Requested CEF checkout URL %s does not match existing URL %s' %
+        (cef_url, cef_existing_url))
+
+msg("CEF Branch: %s" % (cef_branch))
+msg("CEF URL: %s" % (cef_url))
+msg("CEF Source Directory: %s" % (cef_dir))
+
+# Determine the CEF Git branch to use.
+if options.checkout == '':
+  # Target the most recent branch commit from the remote repo.
+  if branch_is_master:
+    cef_checkout = 'origin/master'
+  else:
+    cef_checkout = 'origin/' + cef_branch
+else:
+  cef_checkout = options.checkout
+
+# Create the CEF checkout if necessary.
+if not options.nocefupdate and not os.path.exists(cef_dir):
+  cef_checkout_new = True
+  run('%s clone %s %s' % (git_exe, cef_url, cef_dir), download_dir,
+      depot_tools_dir)
+else:
+  cef_checkout_new = False
+
+# Determine if the CEF checkout needs to change.
+if not options.nocefupdate and os.path.exists(cef_dir):
+  cef_current_hash = get_git_hash(cef_dir, 'HEAD')
+
+  if not cef_checkout_new:
+    # Fetch updated sources.
+    run('%s fetch' % (git_exe), cef_dir, depot_tools_dir)
+
+  cef_desired_hash = get_git_hash(cef_dir, cef_checkout)
+  cef_checkout_changed = cef_checkout_new or force_change or \
+                         options.forcecefupdate or \
+                         cef_current_hash != cef_desired_hash
+
+  msg("CEF Current Checkout: %s" % (cef_current_hash))
+  msg("CEF Desired Checkout: %s (%s)" % (cef_desired_hash, cef_checkout))
+
+  if cef_checkout_changed:
+    if cef_dir == cef_src_dir:
+      # Running in fast update mode. Backup and revert the patched files before
+      # changing the CEF checkout.
+      run_patch_updater("--backup --revert")
+
+    # Update the CEF checkout.
+    run('%s checkout %s%s' %
+      (git_exe, '--force ' if discard_local_changes else '', cef_checkout), \
+      cef_dir, depot_tools_dir)
+else:
+  cef_checkout_changed = False
+
+build_compat_versions = get_build_compat_versions()
+
+if not options.nodepottoolsupdate and \
+    'depot_tools_checkout' in build_compat_versions:
+  # Update the depot_tools checkout.
+  depot_tools_compat_version = build_compat_versions['depot_tools_checkout']
+  run('%s checkout %s%s' %
+      (git_exe, '--force ' if discard_local_changes else '', depot_tools_compat_version), \
+      depot_tools_dir, depot_tools_dir)
+
+# Disable further depot_tools updates.
+os.environ['DEPOT_TOOLS_UPDATE'] = '0'
+
+##
+# Manage the out directory.
+##
+
+out_dir = os.path.join(download_dir, 'out_' + cef_branch)
+
+# Delete the existing out directory if requested.
+if options.forceclean and os.path.exists(out_dir):
+  delete_directory(out_dir)
+
+msg("CEF Output Directory: %s" % (out_dir))
+
+##
+# Manage the chromium directory.
+##
+
+# Create the chromium directory if necessary.
+create_directory(chromium_dir)
+
+if options.chromiumurl != '':
+  chromium_url = options.chromiumurl
+else:
+  chromium_url = 'https://chromium.googlesource.com/chromium/src.git'
+
+# Create gclient configuration file.
+gclient_file = os.path.join(chromium_dir, '.gclient')
+if not os.path.exists(gclient_file) or options.forceconfig:
+  # Exclude unnecessary directories. Intentionally written without newlines.
+  gclient_spec = \
+      "solutions = [{"+\
+        "'managed': False,"+\
+        "'name': 'src', "+\
+        "'url': '" + chromium_url + "', "+\
+        "'custom_deps': {"+\
+          "'build': None, "+\
+          "'build/scripts/command_wrapper/bin': None, "+\
+          "'build/scripts/gsd_generate_index': None, "+\
+          "'build/scripts/private/data/reliability': None, "+\
+          "'build/scripts/tools/deps2git': None, "+\
+          "'build/third_party/lighttpd': None, "+\
+          "'commit-queue': None, "+\
+          "'depot_tools': None, "+\
+          "'src/chrome_frame/tools/test/reference_build/chrome': None, "+\
+          "'src/chrome/tools/test/reference_build/chrome_linux': None, "+\
+          "'src/chrome/tools/test/reference_build/chrome_mac': None, "+\
+          "'src/chrome/tools/test/reference_build/chrome_win': None, "+\
+        "}, "+\
+        "'deps_file': '" + deps_file + "', "+\
+        "'safesync_url': ''"+\
+      "}]"
+
+  msg('Writing %s' % gclient_file)
+  if not options.dryrun:
+    with open(gclient_file, 'w', encoding='utf-8') as fp:
+      write_fp(fp, gclient_spec)
+
+# Initial Chromium checkout.
+if not options.nochromiumupdate and not os.path.exists(chromium_src_dir):
+  chromium_checkout_new = True
+  run("gclient sync --nohooks --with_branch_heads --jobs 16", \
+      chromium_dir, depot_tools_dir)
+else:
+  chromium_checkout_new = False
+
+# Verify the Chromium checkout.
+if not options.dryrun and not is_git_checkout(chromium_src_dir):
+  raise Exception('Not a valid git checkout: %s' % (chromium_src_dir))
+
+if os.path.exists(chromium_src_dir):
+  msg("Chromium URL: %s" % (get_git_url(chromium_src_dir)))
+
+# Fetch Chromium changes so that we can perform the necessary calculations using
+# local history.
+if not options.nochromiumupdate and os.path.exists(chromium_src_dir):
+  # Fetch updated sources.
+  run("%s fetch" % (git_exe), chromium_src_dir, depot_tools_dir)
+  # Also fetch tags, which are required for release branch builds.
+  run("%s fetch --tags" % (git_exe), chromium_src_dir, depot_tools_dir)
+
+# Determine the Chromium checkout options required by CEF.
+chromium_compat_version = build_compat_versions['chromium_checkout']
+if len(options.chromiumcheckout) > 0:
+  chromium_checkout = options.chromiumcheckout
+elif len(options.chromiumchannel) > 0:
+  target_distance = int(options.chromiumchanneldistance
+                       ) if len(options.chromiumchanneldistance) > 0 else 0
+  chromium_checkout = get_chromium_target_version(
+      channel=options.chromiumchannel, target_distance=target_distance)
+else:
+  chromium_checkout = chromium_compat_version
+
+# Determine if the Chromium checkout needs to change.
+if not options.nochromiumupdate and os.path.exists(chromium_src_dir):
+  chromium_current_hash = get_git_hash(chromium_src_dir, 'HEAD')
+  chromium_desired_hash = get_git_hash(chromium_src_dir, chromium_checkout)
+  chromium_checkout_changed = chromium_checkout_new or force_change or \
+                              chromium_current_hash != chromium_desired_hash
+
+  msg("Chromium Current Checkout: %s" % (chromium_current_hash))
+  msg("Chromium Desired Checkout: %s (%s)" % \
+      (chromium_desired_hash, chromium_checkout))
+else:
+  chromium_checkout_changed = options.dryrun
+
+if cef_checkout_changed:
+  if cef_dir != cef_src_dir and os.path.exists(cef_src_dir):
+    # Delete the existing src/cef directory. It will be re-copied from the
+    # download directory later.
+    delete_directory(cef_src_dir)
+elif chromium_checkout_changed and cef_dir == cef_src_dir:
+  # Running in fast update mode. Backup and revert the patched files before
+  # changing the Chromium checkout.
+  run_patch_updater("--backup --revert")
+
+# Delete the existing src/out directory if requested.
+if options.forceclean and os.path.exists(out_src_dir):
+  delete_directory(out_src_dir)
+
+# Move the existing src/out directory to the correct location in the download
+# directory. It will be moved back from the download directory later.
+if os.path.exists(out_src_dir):
+  old_branch = read_branch_config_file(out_src_dir)
+  if old_branch != '' and (chromium_checkout_changed or
+                           old_branch != cef_branch):
+    old_out_dir = os.path.join(download_dir, 'out_' + old_branch)
+    move_directory(out_src_dir, old_out_dir)
+
+# Update the Chromium checkout.
+if chromium_checkout_changed:
+  if not chromium_checkout_new and not options.fastupdate:
+    if options.forceclean and options.forcecleandeps:
+      # Remove all local changes including third-party git checkouts managed by
+      # gclient.
+      run("%s clean -dffx" % (git_exe), chromium_src_dir, depot_tools_dir)
+    else:
+      # Revert all changes in the Chromium checkout.
+      run("gclient revert --nohooks", chromium_dir, depot_tools_dir)
+
+  # Checkout the requested branch.
+  run("%s checkout %s%s" % \
+    (git_exe, '--force ' if discard_local_changes else '', chromium_checkout), \
+    chromium_src_dir, depot_tools_dir)
+
+  # Patch the Chromium DEPS file if necessary.
+  apply_deps_patch()
+
+  # Update third-party dependencies including branch/tag information.
+  run("gclient sync %s--nohooks --with_branch_heads --jobs 16" % \
+      ('--reset ' if discard_local_changes else ''), chromium_dir, depot_tools_dir)
+
+  # Patch the Chromium runhooks scripts if necessary.
+  apply_runhooks_patch()
+
+  # Runs hooks for files that have been modified in the local working copy.
+  run("gclient runhooks --jobs 16", chromium_dir, depot_tools_dir)
+
+  # Delete the src/out directory created by `gclient sync`.
+  delete_directory(out_src_dir)
+
+if cef_dir == cef_src_dir:
+  # Running in fast update mode.
+  if cef_checkout_changed or chromium_checkout_changed:
+    # Check and restore the patched files.
+    run_patch_updater("--reapply --restore")
+elif os.path.exists(cef_dir) and not os.path.exists(cef_src_dir):
+  # Restore the src/cef directory.
+  copy_directory(cef_dir, cef_src_dir)
+
+# Restore the src/out directory.
+out_src_dir_exists = os.path.exists(out_src_dir)
+if os.path.exists(out_dir) and not out_src_dir_exists:
+  move_directory(out_dir, out_src_dir)
+  out_src_dir_exists = True
+elif not out_src_dir_exists:
+  create_directory(out_src_dir)
+
+# Write the config file for identifying the branch.
+write_branch_config_file(out_src_dir, cef_branch)
+
+if options.logchromiumchanges and chromium_checkout != chromium_compat_version:
+  log_chromium_changes()
+
+if options.forcepatchupdate or ((chromium_checkout_new or not options.fastupdate) and \
+                                chromium_checkout_changed and \
+                                chromium_checkout != chromium_compat_version):
+  # Not using the known-compatible Chromium version. Try to update patch files.
+  if options.logchromiumchanges:
+    out_file = os.path.join(download_dir, 'chromium_update_patches.txt')
+    if os.path.exists(out_file):
+      os.remove(out_file)
+  else:
+    out_file = None
+  run_patch_updater(output_file=out_file)
+elif options.resave:
+  # Resave patch files.
+  run_patch_updater("--resave")
+
+if chromium_checkout != chromium_compat_version:
+  if options.logchromiumchanges:
+    out_file = os.path.join(download_dir, 'chromium_update_patterns.txt')
+    if os.path.exists(out_file):
+      os.remove(out_file)
+  else:
+    out_file = None
+  check_pattern_matches(output_file=out_file)
+
+##
+# Build CEF.
+##
+
+if not options.nobuild and (chromium_checkout_changed or \
+                            cef_checkout_changed or options.forcebuild or \
+                            not out_src_dir_exists):
+  # Building should also force a distribution.
+  options.forcedistrib = True
+
+  # Make sure the GN configuration exists.
+  if not options.dryrun and \
+    not os.path.exists(os.path.join(cef_src_dir, 'BUILD.gn')):
+    raise Exception('GN configuration does not exist.')
+
+  # Print all build-related environment variables including any that were set
+  # previously.
+  for key in os.environ.keys():
+    if key.startswith('CEF_') or key.startswith('GCLIENT_') or \
+       key.startswith('GN_') or key.startswith('GYP_') or \
+       key.startswith('DEPOT_TOOLS_'):
+      msg('%s=%s' % (key, os.environ[key]))
+
+  # Generate project files.
+  tool = os.path.join(cef_src_dir, 'tools', 'gclient_hook.py')
+  run('%s %s' % (python_exe, tool), cef_src_dir, depot_tools_dir)
+
+  # Build using Ninja.
+  command = 'ninja '
+  if options.verbosebuild:
+    command += '-v '
+  if options.buildfailurelimit != 1:
+    command += '-k %d ' % options.buildfailurelimit
+  command += '-C '
+  target = ' ' + options.buildtarget
+  if options.buildtests:
+    target += ' ' + options.testtarget
+  if platform == 'linux':
+    target += ' chrome_sandbox'
+
+  # Make a CEF Debug build.
+  if not options.nodebugbuild:
+    build_path = os.path.join('out', get_build_directory_name(True))
+    args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+    msg(args_path + ' contents:\n' + read_file(args_path))
+
+    run(command + build_path + target, chromium_src_dir, depot_tools_dir,
+        os.path.join(download_dir, 'build-%s-debug.log' % (cef_branch)) \
+          if options.buildlogfile else None)
+
+    if platform in sandbox_lib_platforms:
+      # Make the separate cef_sandbox build when GN is_official_build=true.
+      build_path += '_sandbox'
+      if os.path.exists(os.path.join(chromium_src_dir, build_path)):
+        args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+        msg(args_path + ' contents:\n' + read_file(args_path))
+
+        run(command + build_path + ' cef_sandbox', chromium_src_dir, depot_tools_dir,
+            os.path.join(download_dir, 'build-%s-debug-sandbox.log' % (cef_branch)) \
+              if options.buildlogfile else None)
+
+  # Make a CEF Release build.
+  if not options.noreleasebuild:
+    build_path = os.path.join('out', get_build_directory_name(False))
+    args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+    msg(args_path + ' contents:\n' + read_file(args_path))
+
+    run(command + build_path + target, chromium_src_dir, depot_tools_dir,
+        os.path.join(download_dir, 'build-%s-release.log' % (cef_branch)) \
+          if options.buildlogfile else None)
+
+    if platform in sandbox_lib_platforms:
+      # Make the separate cef_sandbox build when GN is_official_build=true.
+      build_path += '_sandbox'
+      if os.path.exists(os.path.join(chromium_src_dir, build_path)):
+        args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+        msg(args_path + ' contents:\n' + read_file(args_path))
+
+        run(command + build_path + ' cef_sandbox', chromium_src_dir, depot_tools_dir,
+            os.path.join(download_dir, 'build-%s-release-sandbox.log' % (cef_branch)) \
+              if options.buildlogfile else None)
+
+elif not options.nobuild:
+  msg('Not building. The source hashes have not changed and ' +
+      'the output folder "%s" already exists' % (out_src_dir))
+
+##
+# Run CEF tests.
+##
+
+if options.runtests:
+  if platform == 'windows':
+    test_exe = '%s.exe' % options.testtarget
+  elif platform == 'macosx':
+    test_exe = '%s.app/Contents/MacOS/%s' % (options.testtarget,
+                                             options.testtarget)
+  elif platform == 'linux':
+    test_exe = options.testtarget
+
+  test_prefix = options.testprefix
+  if len(test_prefix) > 0:
+    test_prefix += ' '
+
+  test_args = options.testargs
+  if len(test_args) > 0:
+    test_args = ' ' + test_args
+
+  if not options.nodebugtests:
+    build_path = os.path.join(out_src_dir, get_build_directory_name(True))
+    test_path = os.path.join(build_path, test_exe)
+    if os.path.exists(test_path):
+      run(test_prefix + test_path + test_args, build_path, depot_tools_dir)
+    else:
+      msg('Not running debug tests. Missing executable: %s' % test_path)
+
+  if not options.noreleasetests:
+    build_path = os.path.join(out_src_dir, get_build_directory_name(False))
+    test_path = os.path.join(build_path, test_exe)
+    if os.path.exists(test_path):
+      run(test_prefix + test_path + test_args, build_path, depot_tools_dir)
+    else:
+      msg('Not running release tests. Missing executable: %s' % test_path)
+
+##
+# Create the CEF binary distribution.
+##
+
+if not options.nodistrib and (chromium_checkout_changed or \
+                              cef_checkout_changed or options.forcedistrib):
+  if not options.forceclean and options.cleanartifacts:
+    # Clean the artifacts output directory.
+    artifacts_path = os.path.join(cef_src_dir, 'binary_distrib')
+    delete_directory(artifacts_path)
+
+  # Determine the requested distribution types.
+  distrib_types = []
+  if options.minimaldistribonly:
+    distrib_types.append('minimal')
+  elif options.clientdistribonly:
+    distrib_types.append('client')
+  elif options.sandboxdistribonly:
+    distrib_types.append('sandbox')
+  else:
+    distrib_types.append('standard')
+    if options.minimaldistrib:
+      distrib_types.append('minimal')
+    if options.clientdistrib:
+      distrib_types.append('client')
+    if options.sandboxdistrib:
+      distrib_types.append('sandbox')
+
+  cef_tools_dir = os.path.join(cef_src_dir, 'tools')
+
+  # Create the requested distribution types.
+  first_type = True
+  for type in distrib_types:
+    path = '%s make_distrib.py --output-dir=../binary_distrib/' % python_exe
+
+    if options.nodebugbuild or options.noreleasebuild or type != 'standard':
+      path += ' --allow-partial'
+    path = path + ' --ninja-build'
+    if options.x64build:
+      path += ' --x64-build'
+    elif options.armbuild:
+      path += ' --arm-build'
+    elif options.arm64build:
+      path += ' --arm64-build'
+
+    if type == 'minimal':
+      path += ' --minimal'
+    elif type == 'client':
+      path += ' --client'
+    elif type == 'sandbox':
+      path += ' --sandbox'
+
+    if first_type:
+      if options.nodistribdocs:
+        path += ' --no-docs'
+      if options.nodistribarchive:
+        path += ' --no-archive'
+      first_type = False
+    else:
+      # Don't create the symbol archives or documentation more than once.
+      path += ' --no-symbols --no-docs'
+
+    # Override the subdirectory name of binary_distrib if the caller requested.
+    if options.distribsubdir != '':
+      path += ' --distrib-subdir=' + options.distribsubdir
+
+    # Create the distribution.
+    run(path, cef_tools_dir, depot_tools_dir)
diff --git a/src/tools/cef_api_hash.py b/src/tools/cef_api_hash.py
new file mode 100644
index 0000000..2ef6237
--- /dev/null
+++ b/src/tools/cef_api_hash.py
@@ -0,0 +1,283 @@
+# Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from file_util import *
+import os
+import re
+import shutil
+import string
+import sys
+import textwrap
+import time
+import itertools
+import hashlib
+
+
+class cef_api_hash:
+  """ CEF API hash calculator """
+
+  def __init__(self, headerdir, debugdir=None, verbose=False):
+    if headerdir is None or len(headerdir) == 0:
+      raise AssertionError("headerdir is not specified")
+
+    self.__headerdir = headerdir
+    self.__debugdir = debugdir
+    self.__verbose = verbose
+    self.__debug_enabled = not (self.__debugdir is
+                                None) and len(self.__debugdir) > 0
+
+    self.platforms = ["windows", "macosx", "linux"]
+
+    self.platform_files = {
+        # List of includes_win_capi from cef_paths2.gypi.
+        "windows": [
+            "internal/cef_types_win.h",
+        ],
+        # List of includes_mac_capi from cef_paths2.gypi.
+        "macosx": [
+            "internal/cef_types_mac.h",
+        ],
+        # List of includes_linux_capi from cef_paths2.gypi.
+        "linux": [
+            "internal/cef_types_linux.h",
+        ]
+    }
+
+    self.included_files = []
+
+    # List of include/ and include/internal/ files from cef_paths2.gypi.
+    self.excluded_files = [
+        # includes_common
+        "cef_api_hash.h",
+        "cef_base.h",
+        "cef_config.h",
+        "cef_version.h",
+        "internal/cef_export.h",
+        "internal/cef_ptr.h",
+        "internal/cef_string_wrappers.h",
+        "internal/cef_types_wrappers.h",
+        # includes_win
+        "cef_sandbox_win.h",
+        "internal/cef_win.h",
+        # includes_mac
+        "cef_application_mac.h",
+        "cef_sandbox_mac.h",
+        "internal/cef_mac.h",
+        # includes_linux
+        "internal/cef_linux.h",
+    ]
+
+  def calculate(self):
+    filenames = [
+        filename for filename in self.__get_filenames()
+        if not filename in self.excluded_files
+    ]
+
+    objects = []
+    for filename in filenames:
+      if self.__verbose:
+        print("Processing " + filename + "...")
+      content = read_file(os.path.join(self.__headerdir, filename), True)
+      platforms = list([
+          p for p in self.platforms if self.__is_platform_filename(filename, p)
+      ])
+
+      # Parse cef_string.h happens in special case: grab only defined CEF_STRING_TYPE_xxx declaration
+      content_objects = None
+      if filename == "internal/cef_string.h":
+        content_objects = self.__parse_string_type(content)
+      else:
+        content_objects = self.__parse_objects(content)
+
+      for o in content_objects:
+        o["text"] = self.__prepare_text(o["text"])
+        o["platforms"] = platforms
+        o["filename"] = filename
+        objects.append(o)
+
+    # objects will be sorted including filename, to make stable universal hashes
+    objects = sorted(objects, key=lambda o: o["name"] + "@" + o["filename"])
+
+    if self.__debug_enabled:
+      namelen = max([len(o["name"]) for o in objects])
+      filenamelen = max([len(o["filename"]) for o in objects])
+      dumpsig = []
+      for o in objects:
+        dumpsig.append(
+            format(o["name"], str(namelen) + "s") + "|" + format(
+                o["filename"], "" + str(filenamelen) + "s") + "|" + o["text"])
+      self.__write_debug_file("objects.txt", dumpsig)
+
+    revisions = {}
+
+    for platform in itertools.chain(["universal"], self.platforms):
+      sig = self.__get_final_sig(objects, platform)
+      if self.__debug_enabled:
+        self.__write_debug_file(platform + ".sig", sig)
+      revstr = hashlib.sha1(sig.encode('utf-8')).hexdigest()
+      revisions[platform] = revstr
+
+    return revisions
+
+  def __parse_objects(self, content):
+    """ Returns array of objects in content file. """
+    objects = []
+    content = re.sub("//.*\n", "", content)
+
+    # function declarations
+    for m in re.finditer(
+        "\nCEF_EXPORT\s+?.*?\s+?(\w+)\s*?\(.*?\)\s*?;",
+        content,
+        flags=re.DOTALL):
+      object = {"name": m.group(1), "text": m.group(0).strip()}
+      objects.append(object)
+
+    # structs
+    for m in re.finditer(
+        "\ntypedef\s+?struct\s+?(\w+)\s+?\{.*?\}\s+?(\w+)\s*?;",
+        content,
+        flags=re.DOTALL):
+      object = {"name": m.group(2), "text": m.group(0).strip()}
+      objects.append(object)
+
+    # enums
+    for m in re.finditer(
+        "\nenum\s+?(\w+)\s+?\{.*?\}\s*?;", content, flags=re.DOTALL):
+      object = {"name": m.group(1), "text": m.group(0).strip()}
+      objects.append(object)
+
+    # typedefs
+    for m in re.finditer("\ntypedef\s+?.*?\s+(\w+);", content, flags=0):
+      object = {"name": m.group(1), "text": m.group(0).strip()}
+      objects.append(object)
+
+    return objects
+
+  def __parse_string_type(self, content):
+    """ Grab defined CEF_STRING_TYPE_xxx """
+    objects = []
+    for m in re.finditer(
+        "\n\s*?#\s*?define\s+?(CEF_STRING_TYPE_\w+)\s+?.*?\n", content,
+        flags=0):
+      object = {
+          "name": m.group(1),
+          "text": m.group(0),
+      }
+      objects.append(object)
+    return objects
+
+  def __prepare_text(self, text):
+    text = text.strip()
+    text = re.sub("\s+", " ", text)
+    text = re.sub("\(\s+", "(", text)
+    return text
+
+  def __get_final_sig(self, objects, platform):
+    sig = []
+
+    for o in objects:
+      if platform == "universal" or platform in o["platforms"]:
+        sig.append(o["text"])
+
+    return "\n".join(sig)
+
+  def __get_filenames(self):
+    """ Returns file names to be processed, relative to headerdir """
+    headers = [
+        os.path.join(self.__headerdir, filename)
+        for filename in self.included_files
+    ]
+    headers = itertools.chain(
+        headers, get_files(os.path.join(self.__headerdir, "capi", "*.h")))
+    headers = itertools.chain(
+        headers, get_files(os.path.join(self.__headerdir, "internal", "*.h")))
+
+    for v in self.platform_files.values():
+      headers = itertools.chain(headers,
+                                [os.path.join(self.__headerdir, f) for f in v])
+
+    normalized = [
+        os.path.relpath(filename, self.__headerdir) for filename in headers
+    ]
+    normalized = [f.replace('\\', '/').lower() for f in normalized]
+
+    return list(set(normalized))
+
+  def __is_platform_filename(self, filename, platform):
+    if platform == "universal":
+      return True
+    if not platform in self.platform_files:
+      return False
+    listed = False
+    for p in self.platforms:
+      if filename in self.platform_files[p]:
+        if p == platform:
+          return True
+        else:
+          listed = True
+    return not listed
+
+  def __write_debug_file(self, filename, content):
+    make_dir(self.__debugdir)
+    outfile = os.path.join(self.__debugdir, filename)
+    dir = os.path.dirname(outfile)
+    make_dir(dir)
+    if not isinstance(content, basestring):
+      content = "\n".join(content)
+    write_file(outfile, content)
+
+
+if __name__ == "__main__":
+  from optparse import OptionParser
+  import time
+
+  disc = """
+    This utility calculates CEF API hash.
+    """
+
+  parser = OptionParser(description=disc)
+  parser.add_option(
+      '--cpp-header-dir',
+      dest='cppheaderdir',
+      metavar='DIR',
+      help='input directory for C++ header files [required]')
+  parser.add_option(
+      '--debug-dir',
+      dest='debugdir',
+      metavar='DIR',
+      help='intermediate directory for easy debugging')
+  parser.add_option(
+      '-v',
+      '--verbose',
+      action='store_true',
+      dest='verbose',
+      default=False,
+      help='output detailed status information')
+  (options, args) = parser.parse_args()
+
+  # the cppheader option is required
+  if options.cppheaderdir is None:
+    parser.print_help(sys.stdout)
+    sys.exit()
+
+  # calculate
+  c_start_time = time.time()
+
+  calc = cef_api_hash(options.cppheaderdir, options.debugdir, options.verbose)
+  revisions = calc.calculate()
+
+  c_completed_in = time.time() - c_start_time
+
+  print("{")
+  for k in sorted(revisions.keys()):
+    print(format("\"" + k + "\"", ">12s") + ": \"" + revisions[k] + "\"")
+  print("}")
+  # print
+  # print 'Completed in: ' + str(c_completed_in)
+  # print
+
+  # print "Press any key to continue...";
+  # sys.stdin.readline();
diff --git a/src/tools/cef_parser.py b/src/tools/cef_parser.py
new file mode 100644
index 0000000..dd80aed
--- /dev/null
+++ b/src/tools/cef_parser.py
@@ -0,0 +1,2118 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from date_util import *
+from file_util import *
+import os
+import re
+import shutil
+import string
+import sys
+import textwrap
+import time
+
+
+def notify(msg):
+  """ Display a message. """
+  sys.stdout.write('  NOTE: ' + msg + '\n')
+
+
+def wrap_text(text, indent='', maxchars=80):
+  """ Wrap the text to the specified number of characters. If
+    necessary a line will be broken and wrapped after a word.
+    """
+  result = ''
+  lines = textwrap.wrap(text, maxchars - len(indent))
+  for line in lines:
+    result += indent + line + '\n'
+  return result
+
+
+def is_base_class(clsname):
+  """ Returns true if |clsname| is a known base (root) class in the object
+        hierarchy.
+    """
+  return clsname == 'CefBaseRefCounted' or clsname == 'CefBaseScoped'
+
+
+def get_capi_file_name(cppname):
+  """ Convert a C++ header file name to a C API header file name. """
+  return cppname[:-2] + '_capi.h'
+
+
+def get_capi_name(cppname, isclassname, prefix=None):
+  """ Convert a C++ CamelCaps name to a C API underscore name. """
+  result = ''
+  lastchr = ''
+  for chr in cppname:
+    # add an underscore if the current character is an upper case letter
+    # and the last character was a lower case letter
+    if len(result) > 0 and not chr.isdigit() \
+        and chr.upper() == chr \
+        and not lastchr.upper() == lastchr:
+      result += '_'
+    result += chr.lower()
+    lastchr = chr
+
+  if isclassname:
+    result += '_t'
+
+  if not prefix is None:
+    if prefix[0:3] == 'cef':
+      # if the prefix name is duplicated in the function name
+      # remove that portion of the function name
+      subprefix = prefix[3:]
+      pos = result.find(subprefix)
+      if pos >= 0:
+        result = result[0:pos] + result[pos + len(subprefix):]
+    result = prefix + '_' + result
+
+  return result
+
+
+def get_wrapper_type_enum(cppname):
+  """ Returns the wrapper type enumeration value for the specified C++ class
+        name. """
+  return 'WT_' + get_capi_name(cppname, False)[4:].upper()
+
+
+def get_prev_line(body, pos):
+  """ Retrieve the start and end positions and value for the line immediately
+    before the line containing the specified position.
+    """
+  end = body.rfind('\n', 0, pos)
+  start = body.rfind('\n', 0, end) + 1
+  line = body[start:end]
+  return {'start': start, 'end': end, 'line': line}
+
+
+def get_comment(body, name):
+  """ Retrieve the comment for a class or function. """
+  result = []
+
+  pos = body.find(name)
+  in_block_comment = False
+  while pos > 0:
+    data = get_prev_line(body, pos)
+    line = data['line'].strip()
+    pos = data['start']
+    if len(line) == 0:
+      # check if the next previous line is a comment
+      prevdata = get_prev_line(body, pos)
+      prevline = prevdata['line'].strip()
+      if prevline[0:2] == '//' and prevline[0:3] != '///':
+        result.append(None)
+      else:
+        break
+    # single line /*--cef()--*/
+    elif line[0:2] == '/*' and line[-2:] == '*/':
+      continue
+    # start of multi line /*--cef()--*/
+    elif in_block_comment and line[0:2] == '/*':
+      in_block_comment = False
+      continue
+    # end of multi line /*--cef()--*/
+    elif not in_block_comment and line[-2:] == '*/':
+      in_block_comment = True
+      continue
+    elif in_block_comment:
+      continue
+    elif line[0:2] == '//':
+      # keep the comment line including any leading spaces
+      result.append(line[2:])
+    else:
+      break
+
+  result.reverse()
+  return result
+
+
+def validate_comment(file, name, comment):
+  """ Validate the comment array returned by get_comment(). """
+  # Verify that the comment contains beginning and ending '///' as required by
+  # CppDoc (the leading '//' from each line will already have been removed by
+  # the get_comment() logic). There may be additional comments proceeding the
+  # CppDoc block so we look at the quantity of lines equaling '/' and expect
+  # the last line to be '/'.
+  docct = 0
+  for line in comment:
+    if not line is None and len(line) > 0 and line == '/':
+      docct = docct + 1
+  if docct != 2 or len(comment) < 3 or comment[len(comment) - 1] != '/':
+    raise Exception('Missing or incorrect comment in %s for: %s' % \
+        (file, name))
+
+
+def format_comment(comment, indent, translate_map=None, maxchars=80):
+  """ Return the comments array as a formatted string. """
+  if not translate_map is None:
+    # Replace longest keys first in translation.
+    translate_keys = sorted(
+        translate_map.keys(), key=lambda item: (-len(item), item))
+
+  result = ''
+  wrapme = ''
+  hasemptyline = False
+  for line in comment:
+    # if the line starts with a leading space, remove that space
+    if not line is None and len(line) > 0 and line[0:1] == ' ':
+      line = line[1:]
+      didremovespace = True
+    else:
+      didremovespace = False
+
+    if line is None or len(line) == 0 or line[0:1] == ' ' \
+        or line[0:1] == '/':
+      # the previous paragraph, if any, has ended
+      if len(wrapme) > 0:
+        if not translate_map is None:
+          # apply the translation
+          for key in translate_keys:
+            wrapme = wrapme.replace(key, translate_map[key])
+        # output the previous paragraph
+        result += wrap_text(wrapme, indent + '// ', maxchars)
+        wrapme = ''
+
+    if not line is None:
+      if len(line) == 0 or line[0:1] == ' ' or line[0:1] == '/':
+        # blank lines or anything that's further indented should be
+        # output as-is
+        result += indent + '//'
+        if len(line) > 0:
+          if didremovespace:
+            result += ' ' + line
+          else:
+            result += line
+        result += '\n'
+      else:
+        # add to the current paragraph
+        wrapme += line + ' '
+    else:
+      # output an empty line
+      hasemptyline = True
+      result += '\n'
+
+  if len(wrapme) > 0:
+    if not translate_map is None:
+      # apply the translation
+      for key in translate_map.keys():
+        wrapme = wrapme.replace(key, translate_map[key])
+    # output the previous paragraph
+    result += wrap_text(wrapme, indent + '// ', maxchars)
+
+  if hasemptyline:
+    # an empty line means a break between comments, so the comment is
+    # probably a section heading and should have an extra line before it
+    result = '\n' + result
+  return result
+
+
+def format_translation_changes(old, new):
+  """ Return a comment stating what is different between the old and new
+    function prototype parts.
+    """
+  changed = False
+  result = ''
+
+  # normalize C API attributes
+  oldargs = [x.replace('struct _', '') for x in old['args']]
+  oldretval = old['retval'].replace('struct _', '')
+  newargs = [x.replace('struct _', '') for x in new['args']]
+  newretval = new['retval'].replace('struct _', '')
+
+  # check if the prototype has changed
+  oldset = set(oldargs)
+  newset = set(newargs)
+  if len(oldset.symmetric_difference(newset)) > 0:
+    changed = True
+    result += '\n  // WARNING - CHANGED ATTRIBUTES'
+
+    # in the implementation set only
+    oldonly = oldset.difference(newset)
+    for arg in oldonly:
+      result += '\n  //   REMOVED: ' + arg
+
+    # in the current set only
+    newonly = newset.difference(oldset)
+    for arg in newonly:
+      result += '\n  //   ADDED:   ' + arg
+
+  # check if the return value has changed
+  if oldretval != newretval:
+    changed = True
+    result += '\n  // WARNING - CHANGED RETURN VALUE'+ \
+              '\n  //   WAS: '+old['retval']+ \
+              '\n  //   NOW: '+new['retval']
+
+  if changed:
+    result += '\n  #pragma message("Warning: "__FILE__": '+new['name']+ \
+              ' prototype has changed")\n'
+
+  return result
+
+
+def format_translation_includes(header, body):
+  """ Return the necessary list of includes based on the contents of the
+    body.
+    """
+  result = ''
+
+  # <algorithm> required for VS2013.
+  if body.find('std::min') > 0 or body.find('std::max') > 0:
+    result += '#include <algorithm>\n'
+
+  if body.find('cef_api_hash(') > 0:
+    result += '#include "include/cef_api_hash.h"\n'
+
+  # identify what CppToC classes are being used
+  p = re.compile('([A-Za-z0-9_]{1,})CppToC')
+  list = sorted(set(p.findall(body)))
+  for item in list:
+    directory = ''
+    if not is_base_class(item):
+      cls = header.get_class(item)
+      dir = cls.get_file_directory()
+      if not dir is None:
+        directory = dir + '/'
+    result += '#include "libcef_dll/cpptoc/'+directory+ \
+              get_capi_name(item[3:], False)+'_cpptoc.h"\n'
+
+  # identify what CToCpp classes are being used
+  p = re.compile('([A-Za-z0-9_]{1,})CToCpp')
+  list = sorted(set(p.findall(body)))
+  for item in list:
+    directory = ''
+    if not is_base_class(item):
+      cls = header.get_class(item)
+      dir = cls.get_file_directory()
+      if not dir is None:
+        directory = dir + '/'
+    result += '#include "libcef_dll/ctocpp/'+directory+ \
+              get_capi_name(item[3:], False)+'_ctocpp.h"\n'
+
+  if body.find('shutdown_checker') > 0:
+    result += '#include "libcef_dll/shutdown_checker.h"\n'
+
+  if body.find('transfer_') > 0:
+    result += '#include "libcef_dll/transfer_util.h"\n'
+
+  return result
+
+
+def str_to_dict(str):
+  """ Convert a string to a dictionary. If the same key has multiple values
+        the values will be stored in a list. """
+  dict = {}
+  parts = str.split(',')
+  for part in parts:
+    part = part.strip()
+    if len(part) == 0:
+      continue
+    sparts = part.split('=')
+    if len(sparts) > 2:
+      raise Exception('Invalid dictionary pair format: ' + part)
+    name = sparts[0].strip()
+    if len(sparts) == 2:
+      val = sparts[1].strip()
+    else:
+      val = True
+    if name in dict:
+      # a value with this name already exists
+      curval = dict[name]
+      if not isinstance(curval, list):
+        # convert the string value to a list
+        dict[name] = [curval]
+      dict[name].append(val)
+    else:
+      dict[name] = val
+  return dict
+
+
+def dict_to_str(dict):
+  """ Convert a dictionary to a string. """
+  str = []
+  for name in dict.keys():
+    if not isinstance(dict[name], list):
+      if dict[name] is True:
+        # currently a bool value
+        str.append(name)
+      else:
+        # currently a string value
+        str.append(name + '=' + dict[name])
+    else:
+      # currently a list value
+      for val in dict[name]:
+        str.append(name + '=' + val)
+  return ','.join(str)
+
+
+# regex for matching comment-formatted attributes
+_cre_attrib = '/\*--cef\(([A-Za-z0-9_ ,=:\n]{0,})\)--\*/'
+# regex for matching class and function names
+_cre_cfname = '([A-Za-z0-9_]{1,})'
+# regex for matching class and function names including path separators
+_cre_cfnameorpath = '([A-Za-z0-9_\/]{1,})'
+# regex for matching function return values
+_cre_retval = '([A-Za-z0-9_<>:,\*\&]{1,})'
+# regex for matching typedef value and name combination
+_cre_typedef = '([A-Za-z0-9_<>:,\*\&\s]{1,})'
+# regex for matching function return value and name combination
+_cre_func = '([A-Za-z][A-Za-z0-9_<>:,\*\&\s]{1,})'
+# regex for matching virtual function modifiers + arbitrary whitespace
+_cre_vfmod = '([\sA-Za-z0-9_]{0,})'
+# regex for matching arbitrary whitespace
+_cre_space = '[\s]{1,}'
+# regex for matching optional virtual keyword
+_cre_virtual = '(?:[\s]{1,}virtual){0,1}'
+
+# Simple translation types. Format is:
+#   'cpp_type' : ['capi_type', 'capi_default_value']
+_simpletypes = {
+    'void': ['void', ''],
+    'void*': ['void*', 'NULL'],
+    'int': ['int', '0'],
+    'int16': ['int16', '0'],
+    'uint16': ['uint16', '0'],
+    'int32': ['int32', '0'],
+    'uint32': ['uint32', '0'],
+    'int64': ['int64', '0'],
+    'uint64': ['uint64', '0'],
+    'double': ['double', '0'],
+    'float': ['float', '0'],
+    'float*': ['float*', 'NULL'],
+    'long': ['long', '0'],
+    'unsigned long': ['unsigned long', '0'],
+    'long long': ['long long', '0'],
+    'size_t': ['size_t', '0'],
+    'bool': ['int', '0'],
+    'char': ['char', '0'],
+    'char* const': ['char* const', 'NULL'],
+    'cef_color_t': ['cef_color_t', '0'],
+    'cef_json_parser_error_t': ['cef_json_parser_error_t', 'JSON_NO_ERROR'],
+    'cef_plugin_policy_t': ['cef_plugin_policy_t', 'PLUGIN_POLICY_ALLOW'],
+    'CefCursorHandle': ['cef_cursor_handle_t', 'kNullCursorHandle'],
+    'CefCompositionUnderline': [
+        'cef_composition_underline_t', 'CefCompositionUnderline()'
+    ],
+    'CefEventHandle': ['cef_event_handle_t', 'kNullEventHandle'],
+    'CefWindowHandle': ['cef_window_handle_t', 'kNullWindowHandle'],
+    'CefPoint': ['cef_point_t', 'CefPoint()'],
+    'CefRect': ['cef_rect_t', 'CefRect()'],
+    'CefSize': ['cef_size_t', 'CefSize()'],
+    'CefRange': ['cef_range_t', 'CefRange()'],
+    'CefDraggableRegion': ['cef_draggable_region_t', 'CefDraggableRegion()'],
+    'CefThreadId': ['cef_thread_id_t', 'TID_UI'],
+    'CefTime': ['cef_time_t', 'CefTime()'],
+    'CefAudioParameters': ['cef_audio_parameters_t', 'CefAudioParameters()']
+}
+
+
+def get_function_impls(content, ident, has_impl=True):
+  """ Retrieve the function parts from the specified contents as a set of
+    return value, name, arguments and body. Ident must occur somewhere in
+    the value.
+    """
+  # extract the functions
+  find_regex = '\n' + _cre_func + '\((.*?)\)([A-Za-z0-9_\s]{0,})'
+  if has_impl:
+    find_regex += '\{(.*?)\n\}'
+  else:
+    find_regex += '(;)'
+  p = re.compile(find_regex, re.MULTILINE | re.DOTALL)
+  list = p.findall(content)
+
+  # build the function map with the function name as the key
+  result = []
+  for retval, argval, vfmod, body in list:
+    if retval.find(ident) < 0:
+      # the identifier was not found
+      continue
+
+    # remove the identifier
+    retval = retval.replace(ident, '')
+    retval = retval.strip()
+
+    # Normalize the delimiter.
+    retval = retval.replace('\n', ' ')
+
+    # retrieve the function name
+    parts = retval.split(' ')
+    name = parts[-1]
+    del parts[-1]
+    retval = ' '.join(parts)
+
+    # parse the arguments
+    args = []
+    for v in argval.split(','):
+      v = v.strip()
+      if len(v) > 0:
+        args.append(v)
+
+    result.append({
+        'retval': retval.strip(),
+        'name': name,
+        'args': args,
+        'vfmod': vfmod.strip(),
+        'body': body if has_impl else '',
+    })
+
+  return result
+
+
+def get_next_function_impl(existing, name):
+  result = None
+  for item in existing:
+    if item['name'] == name:
+      result = item
+      existing.remove(item)
+      break
+  return result
+
+
+def get_copyright(full=False, translator=True):
+  if full:
+    result = \
+"""// Copyright (c) $YEAR$ Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+  else:
+    result = \
+"""// Copyright (c) $YEAR$ The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+"""
+
+  if translator:
+    result += \
+"""//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool. If making changes by
+// hand only do so within the body of existing method and function
+// implementations. See the translator.README.txt file in the tools directory
+// for more information.
+//
+// $hash=$$HASH$$$
+//
+
+"""
+
+  # add the copyright year
+  return result.replace('$YEAR$', get_year())
+
+
+class obj_header:
+  """ Class representing a C++ header file. """
+
+  def __init__(self):
+    self.filenames = []
+    self.typedefs = []
+    self.funcs = []
+    self.classes = []
+    self.root_directory = None
+
+  def set_root_directory(self, root_directory):
+    """ Set the root directory. """
+    self.root_directory = root_directory
+
+  def get_root_directory(self):
+    """ Get the root directory. """
+    return self.root_directory
+
+  def add_directory(self, directory, excluded_files=[]):
+    """ Add all header files from the specified directory. """
+    files = get_files(os.path.join(directory, '*.h'))
+    for file in files:
+      if len(excluded_files) == 0 or \
+          not os.path.split(file)[1] in excluded_files:
+        self.add_file(file)
+
+  def add_file(self, filepath):
+    """ Add a header file. """
+
+    if self.root_directory is None:
+      filename = os.path.split(filepath)[1]
+    else:
+      filename = os.path.relpath(filepath, self.root_directory)
+      filename = filename.replace('\\', '/')
+
+    # read the input file into memory
+    self.add_data(filename, read_file(filepath))
+
+  def add_data(self, filename, data):
+    """ Add header file contents. """
+
+    added = False
+
+    # remove space from between template definition end brackets
+    data = data.replace("> >", ">>")
+
+    # extract global typedefs
+    p = re.compile('\ntypedef' + _cre_space + _cre_typedef + ';',
+                   re.MULTILINE | re.DOTALL)
+    list = p.findall(data)
+    if len(list) > 0:
+      # build the global typedef objects
+      for value in list:
+        pos = value.rfind(' ')
+        if pos < 0:
+          raise Exception('Invalid typedef: ' + value)
+        alias = value[pos + 1:].strip()
+        value = value[:pos].strip()
+        self.typedefs.append(obj_typedef(self, filename, value, alias))
+
+    # extract global functions
+    p = re.compile('\n' + _cre_attrib + '\n' + _cre_func + '\((.*?)\)',
+                   re.MULTILINE | re.DOTALL)
+    list = p.findall(data)
+    if len(list) > 0:
+      added = True
+
+      # build the global function objects
+      for attrib, retval, argval in list:
+        comment = get_comment(data, retval + '(' + argval + ');')
+        validate_comment(filename, retval, comment)
+        self.funcs.append(
+            obj_function(self, filename, attrib, retval, argval, comment))
+
+    # extract includes
+    p = re.compile('\n#include \"include/' + _cre_cfnameorpath + '.h')
+    includes = p.findall(data)
+
+    # extract forward declarations
+    p = re.compile('\nclass' + _cre_space + _cre_cfname + ';')
+    forward_declares = p.findall(data)
+
+    # extract empty classes
+    p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname +
+                   _cre_space + ':' + _cre_space + 'public' + _cre_virtual +
+                   _cre_space + _cre_cfname + _cre_space + '{};',
+                   re.MULTILINE | re.DOTALL)
+    list = p.findall(data)
+    if len(list) > 0:
+      added = True
+
+      # build the class objects
+      for attrib, name, parent_name in list:
+        # Style may place the ':' on the next line.
+        comment = get_comment(data, name + ' :')
+        if len(comment) == 0:
+          comment = get_comment(data, name + "\n")
+        validate_comment(filename, name, comment)
+        self.classes.append(
+            obj_class(self, filename, attrib, name, parent_name, "", comment,
+                      includes, forward_declares))
+
+      # Remove empty classes from |data| so we don't mess up the non-empty
+      # class search that follows.
+      data = p.sub('', data)
+
+    # extract classes
+    p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname +
+                   _cre_space + ':' + _cre_space + 'public' + _cre_virtual +
+                   _cre_space + _cre_cfname + _cre_space + '{(.*?)\n};',
+                   re.MULTILINE | re.DOTALL)
+    list = p.findall(data)
+    if len(list) > 0:
+      added = True
+
+      # build the class objects
+      for attrib, name, parent_name, body in list:
+        # Style may place the ':' on the next line.
+        comment = get_comment(data, name + ' :')
+        if len(comment) == 0:
+          comment = get_comment(data, name + "\n")
+        validate_comment(filename, name, comment)
+        self.classes.append(
+            obj_class(self, filename, attrib, name, parent_name, body, comment,
+                      includes, forward_declares))
+
+    if added:
+      # a global function or class was read from the header file
+      self.filenames.append(filename)
+
+  def __repr__(self):
+    result = ''
+
+    if len(self.typedefs) > 0:
+      strlist = []
+      for cls in self.typedefs:
+        strlist.append(str(cls))
+      result += "\n".join(strlist) + "\n\n"
+
+    if len(self.funcs) > 0:
+      strlist = []
+      for cls in self.funcs:
+        strlist.append(str(cls))
+      result += "\n".join(strlist) + "\n\n"
+
+    if len(self.classes) > 0:
+      strlist = []
+      for cls in self.classes:
+        strlist.append(str(cls))
+      result += "\n".join(strlist)
+
+    return result
+
+  def get_file_names(self):
+    """ Return the array of header file names. """
+    return self.filenames
+
+  def get_typedefs(self):
+    """ Return the array of typedef objects. """
+    return self.typedefs
+
+  def get_funcs(self, filename=None):
+    """ Return the array of function objects. """
+    if filename is None:
+      return self.funcs
+    else:
+      # only return the functions in the specified file
+      res = []
+      for func in self.funcs:
+        if func.get_file_name() == filename:
+          res.append(func)
+      return res
+
+  def get_classes(self, filename=None):
+    """ Return the array of class objects. """
+    if filename is None:
+      return self.classes
+    else:
+      # only return the classes in the specified file
+      res = []
+      for cls in self.classes:
+        if cls.get_file_name() == filename:
+          res.append(cls)
+      return res
+
+  def get_class(self, classname, defined_structs=None):
+    """ Return the specified class or None if not found. """
+    for cls in self.classes:
+      if cls.get_name() == classname:
+        return cls
+      elif not defined_structs is None:
+        defined_structs.append(cls.get_capi_name())
+    return None
+
+  def get_class_names(self):
+    """ Returns the names of all classes in this object. """
+    result = []
+    for cls in self.classes:
+      result.append(cls.get_name())
+    return result
+
+  def get_base_class_name(self, classname):
+    """ Returns the base (root) class name for |classname|. """
+    cur_cls = self.get_class(classname)
+    while True:
+      parent_name = cur_cls.get_parent_name()
+      if is_base_class(parent_name):
+        return parent_name
+      else:
+        parent_cls = self.get_class(parent_name)
+        if parent_cls is None:
+          break
+      cur_cls = self.get_class(parent_name)
+    return None
+
+  def get_types(self, list):
+    """ Return a dictionary mapping data types to analyzed values. """
+    for cls in self.typedefs:
+      cls.get_types(list)
+
+    for cls in self.classes:
+      cls.get_types(list)
+
+  def get_alias_translation(self, alias):
+    """ Return a translation of alias to value based on typedef
+            statements. """
+    for cls in self.typedefs:
+      if cls.alias == alias:
+        return cls.value
+    return None
+
+  def get_analysis(self, value, named=True):
+    """ Return an analysis of the value based the header file context. """
+    return obj_analysis([self], value, named)
+
+  def get_defined_structs(self):
+    """ Return a list of already defined structure names. """
+    return [
+        'cef_print_info_t', 'cef_window_info_t', 'cef_base_ref_counted_t',
+        'cef_base_scoped_t'
+    ]
+
+  def get_capi_translations(self):
+    """ Return a dictionary that maps C++ terminology to C API terminology.
+        """
+    # strings that will be changed in C++ comments
+    map = {
+        'class': 'structure',
+        'Class': 'Structure',
+        'interface': 'structure',
+        'Interface': 'Structure',
+        'true': 'true (1)',
+        'false': 'false (0)',
+        'empty': 'NULL',
+        'method': 'function'
+    }
+
+    # add mappings for all classes and functions
+    funcs = self.get_funcs()
+    for func in funcs:
+      map[func.get_name() + '()'] = func.get_capi_name() + '()'
+
+    classes = self.get_classes()
+    for cls in classes:
+      map[cls.get_name()] = cls.get_capi_name()
+
+      funcs = cls.get_virtual_funcs()
+      for func in funcs:
+        map[func.get_name() + '()'] = func.get_capi_name() + '()'
+
+      funcs = cls.get_static_funcs()
+      for func in funcs:
+        map[func.get_name() + '()'] = func.get_capi_name() + '()'
+
+    return map
+
+
+class obj_class:
+  """ Class representing a C++ class. """
+
+  def __init__(self, parent, filename, attrib, name, parent_name, body, comment,
+               includes, forward_declares):
+    if not isinstance(parent, obj_header):
+      raise Exception('Invalid parent object type')
+
+    self.parent = parent
+    self.filename = filename
+    self.attribs = str_to_dict(attrib)
+    self.name = name
+    self.parent_name = parent_name
+    self.comment = comment
+    self.includes = includes
+    self.forward_declares = forward_declares
+
+    # extract typedefs
+    p = re.compile(
+        '\n' + _cre_space + 'typedef' + _cre_space + _cre_typedef + ';',
+        re.MULTILINE | re.DOTALL)
+    list = p.findall(body)
+
+    # build the typedef objects
+    self.typedefs = []
+    for value in list:
+      pos = value.rfind(' ')
+      if pos < 0:
+        raise Exception('Invalid typedef: ' + value)
+      alias = value[pos + 1:].strip()
+      value = value[:pos].strip()
+      self.typedefs.append(obj_typedef(self, filename, value, alias))
+
+    # extract static functions
+    p = re.compile('\n' + _cre_space + _cre_attrib + '\n' + _cre_space +
+                   'static' + _cre_space + _cre_func + '\((.*?)\)',
+                   re.MULTILINE | re.DOTALL)
+    list = p.findall(body)
+
+    # build the static function objects
+    self.staticfuncs = []
+    for attrib, retval, argval in list:
+      comment = get_comment(body, retval + '(' + argval + ')')
+      validate_comment(filename, retval, comment)
+      self.staticfuncs.append(
+          obj_function_static(self, attrib, retval, argval, comment))
+
+    # extract virtual functions
+    p = re.compile(
+        '\n' + _cre_space + _cre_attrib + '\n' + _cre_space + 'virtual' +
+        _cre_space + _cre_func + '\((.*?)\)' + _cre_vfmod,
+        re.MULTILINE | re.DOTALL)
+    list = p.findall(body)
+
+    # build the virtual function objects
+    self.virtualfuncs = []
+    for attrib, retval, argval, vfmod in list:
+      comment = get_comment(body, retval + '(' + argval + ')')
+      validate_comment(filename, retval, comment)
+      self.virtualfuncs.append(
+          obj_function_virtual(self, attrib, retval, argval, comment,
+                               vfmod.strip()))
+
+  def __repr__(self):
+    result = '/* ' + dict_to_str(
+        self.attribs) + ' */ class ' + self.name + "\n{"
+
+    if len(self.typedefs) > 0:
+      result += "\n\t"
+      strlist = []
+      for cls in self.typedefs:
+        strlist.append(str(cls))
+      result += "\n\t".join(strlist)
+
+    if len(self.staticfuncs) > 0:
+      result += "\n\t"
+      strlist = []
+      for cls in self.staticfuncs:
+        strlist.append(str(cls))
+      result += "\n\t".join(strlist)
+
+    if len(self.virtualfuncs) > 0:
+      result += "\n\t"
+      strlist = []
+      for cls in self.virtualfuncs:
+        strlist.append(str(cls))
+      result += "\n\t".join(strlist)
+
+    result += "\n};\n"
+    return result
+
+  def get_file_name(self):
+    """ Return the C++ header file name. Includes the directory component,
+            if any. """
+    return self.filename
+
+  def get_capi_file_name(self):
+    """ Return the CAPI header file name. Includes the directory component,
+            if any. """
+    return get_capi_file_name(self.filename)
+
+  def get_file_directory(self):
+    """ Return the file directory component, if any. """
+    pos = self.filename.rfind('/')
+    if pos >= 0:
+      return self.filename[:pos]
+    return None
+
+  def get_name(self):
+    """ Return the class name. """
+    return self.name
+
+  def get_capi_name(self):
+    """ Return the CAPI structure name for this class. """
+    return get_capi_name(self.name, True)
+
+  def get_parent_name(self):
+    """ Return the parent class name. """
+    return self.parent_name
+
+  def get_parent_capi_name(self):
+    """ Return the CAPI structure name for the parent class. """
+    return get_capi_name(self.parent_name, True)
+
+  def has_parent(self, parent_name):
+    """ Returns true if this class has the specified class anywhere in its
+            inheritance hierarchy. """
+    # Every class has a known base class as the top-most parent.
+    if is_base_class(parent_name) or parent_name == self.parent_name:
+      return True
+    if is_base_class(self.parent_name):
+      return False
+
+    cur_cls = self.parent.get_class(self.parent_name)
+    while True:
+      cur_parent_name = cur_cls.get_parent_name()
+      if is_base_class(cur_parent_name):
+        break
+      elif cur_parent_name == parent_name:
+        return True
+      cur_cls = self.parent.get_class(cur_parent_name)
+
+    return False
+
+  def get_comment(self):
+    """ Return the class comment as an array of lines. """
+    return self.comment
+
+  def get_includes(self):
+    """ Return the list of classes that are included from this class'
+            header file. """
+    return self.includes
+
+  def get_forward_declares(self):
+    """ Return the list of classes that are forward declared for this
+            class. """
+    return self.forward_declares
+
+  def get_attribs(self):
+    """ Return all attributes as a dictionary. """
+    return self.attribs
+
+  def has_attrib(self, name):
+    """ Return true if the specified attribute exists. """
+    return name in self.attribs
+
+  def get_attrib(self, name):
+    """ Return the first or only value for specified attribute. """
+    if name in self.attribs:
+      if isinstance(self.attribs[name], list):
+        # the value is a list
+        return self.attribs[name][0]
+      else:
+        # the value is a string
+        return self.attribs[name]
+    return None
+
+  def get_attrib_list(self, name):
+    """ Return all values for specified attribute as a list. """
+    if name in self.attribs:
+      if isinstance(self.attribs[name], list):
+        # the value is already a list
+        return self.attribs[name]
+      else:
+        # convert the value to a list
+        return [self.attribs[name]]
+    return None
+
+  def get_typedefs(self):
+    """ Return the array of typedef objects. """
+    return self.typedefs
+
+  def has_typedef_alias(self, alias):
+    """ Returns true if the specified typedef alias is defined in the scope
+            of this class declaration. """
+    for typedef in self.typedefs:
+      if typedef.get_alias() == alias:
+        return True
+    return False
+
+  def get_static_funcs(self):
+    """ Return the array of static function objects. """
+    return self.staticfuncs
+
+  def get_virtual_funcs(self):
+    """ Return the array of virtual function objects. """
+    return self.virtualfuncs
+
+  def get_types(self, list):
+    """ Return a dictionary mapping data types to analyzed values. """
+    for cls in self.typedefs:
+      cls.get_types(list)
+
+    for cls in self.staticfuncs:
+      cls.get_types(list)
+
+    for cls in self.virtualfuncs:
+      cls.get_types(list)
+
+  def get_alias_translation(self, alias):
+    for cls in self.typedefs:
+      if cls.alias == alias:
+        return cls.value
+    return None
+
+  def get_analysis(self, value, named=True):
+    """ Return an analysis of the value based on the class definition
+        context.
+        """
+    return obj_analysis([self, self.parent], value, named)
+
+  def is_library_side(self):
+    """ Returns true if the class is implemented by the library. """
+    return self.attribs['source'] == 'library'
+
+  def is_client_side(self):
+    """ Returns true if the class is implemented by the client. """
+    return self.attribs['source'] == 'client'
+
+
+class obj_typedef:
+  """ Class representing a typedef statement. """
+
+  def __init__(self, parent, filename, value, alias):
+    if not isinstance(parent, obj_header) \
+        and not isinstance(parent, obj_class):
+      raise Exception('Invalid parent object type')
+
+    self.parent = parent
+    self.filename = filename
+    self.alias = alias
+    self.value = self.parent.get_analysis(value, False)
+
+  def __repr__(self):
+    return 'typedef ' + self.value.get_type() + ' ' + self.alias + ';'
+
+  def get_file_name(self):
+    """ Return the C++ header file name. """
+    return self.filename
+
+  def get_capi_file_name(self):
+    """ Return the CAPI header file name. """
+    return get_capi_file_name(self.filename)
+
+  def get_alias(self):
+    """ Return the alias. """
+    return self.alias
+
+  def get_value(self):
+    """ Return an analysis of the value based on the class or header file
+        definition context.
+        """
+    return self.value
+
+  def get_types(self, list):
+    """ Return a dictionary mapping data types to analyzed values. """
+    name = self.value.get_type()
+    if not name in list:
+      list[name] = self.value
+
+
+class obj_function:
+  """ Class representing a function. """
+
+  def __init__(self, parent, filename, attrib, retval, argval, comment):
+    self.parent = parent
+    self.filename = filename
+    self.attribs = str_to_dict(attrib)
+    self.retval = obj_argument(self, retval)
+    self.name = self.retval.remove_name()
+    self.comment = comment
+
+    # build the argument objects
+    self.arguments = []
+    arglist = argval.split(',')
+    argindex = 0
+    while argindex < len(arglist):
+      arg = arglist[argindex]
+      if arg.find('<') >= 0 and arg.find('>') == -1:
+        # We've split inside of a template type declaration. Join the
+        # next argument with this argument.
+        argindex += 1
+        arg += ',' + arglist[argindex]
+
+      arg = arg.strip()
+      if len(arg) > 0:
+        argument = obj_argument(self, arg)
+        if argument.needs_attrib_count_func() and \
+            argument.get_attrib_count_func() is None:
+          raise Exception("A 'count_func' attribute is required "+ \
+                          "for the '"+argument.get_name()+ \
+                          "' parameter to "+self.get_qualified_name())
+        self.arguments.append(argument)
+
+      argindex += 1
+
+    if self.retval.needs_attrib_default_retval() and \
+        self.retval.get_attrib_default_retval() is None:
+      raise Exception("A 'default_retval' attribute is required for "+ \
+                      self.get_qualified_name())
+
+  def __repr__(self):
+    return '/* ' + dict_to_str(self.attribs) + ' */ ' + self.get_cpp_proto()
+
+  def get_file_name(self):
+    """ Return the C++ header file name. """
+    return self.filename
+
+  def get_capi_file_name(self):
+    """ Return the CAPI header file name. """
+    return get_capi_file_name(self.filename)
+
+  def get_name(self):
+    """ Return the function name. """
+    return self.name
+
+  def get_qualified_name(self):
+    """ Return the fully qualified function name. """
+    if isinstance(self.parent, obj_header):
+      # global function
+      return self.name
+    else:
+      # member function
+      return self.parent.get_name() + '::' + self.name
+
+  def get_capi_name(self, prefix=None):
+    """ Return the CAPI function name. """
+    if 'capi_name' in self.attribs:
+      return self.attribs['capi_name']
+    return get_capi_name(self.name, False, prefix)
+
+  def get_comment(self):
+    """ Return the function comment as an array of lines. """
+    return self.comment
+
+  def get_attribs(self):
+    """ Return all attributes as a dictionary. """
+    return self.attribs
+
+  def has_attrib(self, name):
+    """ Return true if the specified attribute exists. """
+    return name in self.attribs
+
+  def get_attrib(self, name):
+    """ Return the first or only value for specified attribute. """
+    if name in self.attribs:
+      if isinstance(self.attribs[name], list):
+        # the value is a list
+        return self.attribs[name][0]
+      else:
+        # the value is a string
+        return self.attribs[name]
+    return None
+
+  def get_attrib_list(self, name):
+    """ Return all values for specified attribute as a list. """
+    if name in self.attribs:
+      if isinstance(self.attribs[name], list):
+        # the value is already a list
+        return self.attribs[name]
+      else:
+        # convert the value to a list
+        return [self.attribs[name]]
+    return None
+
+  def get_retval(self):
+    """ Return the return value object. """
+    return self.retval
+
+  def get_arguments(self):
+    """ Return the argument array. """
+    return self.arguments
+
+  def get_types(self, list):
+    """ Return a dictionary mapping data types to analyzed values. """
+    for cls in self.arguments:
+      cls.get_types(list)
+
+  def get_capi_parts(self, defined_structs=[], prefix=None):
+    """ Return the parts of the C API function definition. """
+    retval = ''
+    dict = self.retval.get_type().get_capi(defined_structs)
+    if dict['format'] == 'single':
+      retval = dict['value']
+
+    name = self.get_capi_name(prefix)
+    args = []
+
+    if isinstance(self, obj_function_virtual):
+      # virtual functions get themselves as the first argument
+      str = 'struct _' + self.parent.get_capi_name() + '* self'
+      if isinstance(self, obj_function_virtual) and self.is_const():
+        # const virtual functions get const self pointers
+        str = 'const ' + str
+      args.append(str)
+
+    if len(self.arguments) > 0:
+      for cls in self.arguments:
+        type = cls.get_type()
+        dict = type.get_capi(defined_structs)
+        if dict['format'] == 'single':
+          args.append(dict['value'])
+        elif dict['format'] == 'multi-arg':
+          # add an additional argument for the size of the array
+          type_name = type.get_name()
+          if type.is_const():
+            # for const arrays pass the size argument by value
+            args.append('size_t ' + type_name + 'Count')
+          else:
+            # for non-const arrays pass the size argument by address
+            args.append('size_t* ' + type_name + 'Count')
+          args.append(dict['value'])
+
+    return {'retval': retval, 'name': name, 'args': args}
+
+  def get_capi_proto(self, defined_structs=[], prefix=None):
+    """ Return the prototype of the C API function. """
+    parts = self.get_capi_parts(defined_structs, prefix)
+    result = parts['retval']+' '+parts['name']+ \
+             '('+', '.join(parts['args'])+')'
+    return result
+
+  def get_cpp_parts(self, isimpl=False):
+    """ Return the parts of the C++ function definition. """
+    retval = str(self.retval)
+    name = self.name
+
+    args = []
+    if len(self.arguments) > 0:
+      for cls in self.arguments:
+        args.append(str(cls))
+
+    if isimpl and isinstance(self, obj_function_virtual):
+      # enumeration return values must be qualified with the class name
+      # if the type is defined in the class declaration scope.
+      type = self.get_retval().get_type()
+      if type.is_result_struct() and type.is_result_struct_enum() and \
+          self.parent.has_typedef_alias(retval):
+        retval = self.parent.get_name() + '::' + retval
+
+    return {'retval': retval, 'name': name, 'args': args}
+
+  def get_cpp_proto(self, classname=None):
+    """ Return the prototype of the C++ function. """
+    parts = self.get_cpp_parts()
+    result = parts['retval'] + ' '
+    if not classname is None:
+      result += classname + '::'
+    result += parts['name'] + '(' + ', '.join(parts['args']) + ')'
+    if isinstance(self, obj_function_virtual) and self.is_const():
+      result += ' const'
+    return result
+
+  def is_same_side(self, other_class_name):
+    """ Returns true if this function is on the same side (library or
+            client) and the specified class. """
+    if isinstance(self.parent, obj_class):
+      # this function is part of a class
+      this_is_library_side = self.parent.is_library_side()
+      header = self.parent.parent
+    else:
+      # this function is global
+      this_is_library_side = True
+      header = self.parent
+
+    if is_base_class(other_class_name):
+      other_is_library_side = False
+    else:
+      other_class = header.get_class(other_class_name)
+      if other_class is None:
+        raise Exception('Unknown class: ' + other_class_name)
+      other_is_library_side = other_class.is_library_side()
+
+    return other_is_library_side == this_is_library_side
+
+
+class obj_function_static(obj_function):
+  """ Class representing a static function. """
+
+  def __init__(self, parent, attrib, retval, argval, comment):
+    if not isinstance(parent, obj_class):
+      raise Exception('Invalid parent object type')
+    obj_function.__init__(self, parent, parent.filename, attrib, retval, argval,
+                          comment)
+
+  def __repr__(self):
+    return 'static ' + obj_function.__repr__(self) + ';'
+
+  def get_capi_name(self, prefix=None):
+    """ Return the CAPI function name. """
+    if prefix is None:
+      # by default static functions are prefixed with the class name
+      prefix = get_capi_name(self.parent.get_name(), False)
+    return obj_function.get_capi_name(self, prefix)
+
+
+class obj_function_virtual(obj_function):
+  """ Class representing a virtual function. """
+
+  def __init__(self, parent, attrib, retval, argval, comment, vfmod):
+    if not isinstance(parent, obj_class):
+      raise Exception('Invalid parent object type')
+    obj_function.__init__(self, parent, parent.filename, attrib, retval, argval,
+                          comment)
+    if vfmod == 'const':
+      self.isconst = True
+    else:
+      self.isconst = False
+
+  def __repr__(self):
+    return 'virtual ' + obj_function.__repr__(self) + ';'
+
+  def is_const(self):
+    """ Returns true if the method declaration is const. """
+    return self.isconst
+
+
+class obj_argument:
+  """ Class representing a function argument. """
+
+  def __init__(self, parent, argval):
+    if not isinstance(parent, obj_function):
+      raise Exception('Invalid parent object type')
+
+    self.parent = parent
+    self.type = self.parent.parent.get_analysis(argval)
+
+  def __repr__(self):
+    result = ''
+    if self.type.is_const():
+      result += 'const '
+    result += self.type.get_type()
+    if self.type.is_byref():
+      result += '&'
+    elif self.type.is_byaddr():
+      result += '*'
+    if self.type.has_name():
+      result += ' ' + self.type.get_name()
+    return result
+
+  def get_name(self):
+    """ Return the name for this argument. """
+    return self.type.get_name()
+
+  def remove_name(self):
+    """ Remove and return the name value. """
+    name = self.type.get_name()
+    self.type.name = None
+    return name
+
+  def get_type(self):
+    """ Return an analysis of the argument type based on the class
+        definition context.
+        """
+    return self.type
+
+  def get_types(self, list):
+    """ Return a dictionary mapping data types to analyzed values. """
+    name = self.type.get_type()
+    if not name in list:
+      list[name] = self.type
+
+  def needs_attrib_count_func(self):
+    """ Returns true if this argument requires a 'count_func' attribute. """
+    # A 'count_func' attribute is required for non-const non-string vector
+    # attribute types
+    return self.type.has_name() and \
+        self.type.is_result_vector() and \
+        not self.type.is_result_vector_string() and \
+        not self.type.is_const()
+
+  def get_attrib_count_func(self):
+    """ Returns the count function for this argument. """
+    # The 'count_func' attribute value format is name:function
+    if not self.parent.has_attrib('count_func'):
+      return None
+    name = self.type.get_name()
+    vals = self.parent.get_attrib_list('count_func')
+    for val in vals:
+      parts = val.split(':')
+      if len(parts) != 2:
+        raise Exception("Invalid 'count_func' attribute value for "+ \
+                        self.parent.get_qualified_name()+': '+val)
+      if parts[0].strip() == name:
+        return parts[1].strip()
+    return None
+
+  def needs_attrib_default_retval(self):
+    """ Returns true if this argument requires a 'default_retval' attribute.
+        """
+    # A 'default_retval' attribute is required for enumeration return value
+    # types.
+    return not self.type.has_name() and \
+        self.type.is_result_struct() and \
+        self.type.is_result_struct_enum()
+
+  def get_attrib_default_retval(self):
+    """ Returns the defualt return value for this argument. """
+    return self.parent.get_attrib('default_retval')
+
+  def get_arg_type(self):
+    """ Returns the argument type as defined in translator.README.txt. """
+    if not self.type.has_name():
+      raise Exception('Cannot be called for retval types')
+
+    # simple or enumeration type
+    if (self.type.is_result_simple() and \
+            self.type.get_type() != 'bool') or \
+       (self.type.is_result_struct() and \
+            self.type.is_result_struct_enum()):
+      if self.type.is_byref():
+        if self.type.is_const():
+          return 'simple_byref_const'
+        return 'simple_byref'
+      elif self.type.is_byaddr():
+        return 'simple_byaddr'
+      return 'simple_byval'
+
+    # boolean type
+    if self.type.get_type() == 'bool':
+      if self.type.is_byref():
+        return 'bool_byref'
+      elif self.type.is_byaddr():
+        return 'bool_byaddr'
+      return 'bool_byval'
+
+    # structure type
+    if self.type.is_result_struct() and self.type.is_byref():
+      if self.type.is_const():
+        return 'struct_byref_const'
+      return 'struct_byref'
+
+    # string type
+    if self.type.is_result_string() and self.type.is_byref():
+      if self.type.is_const():
+        return 'string_byref_const'
+      return 'string_byref'
+
+    # *ptr type
+    if self.type.is_result_ptr():
+      prefix = self.type.get_result_ptr_type_prefix()
+      same_side = self.parent.is_same_side(self.type.get_ptr_type())
+      if self.type.is_byref():
+        if same_side:
+          return prefix + 'ptr_same_byref'
+        return prefix + 'ptr_diff_byref'
+      if same_side:
+        return prefix + 'ptr_same'
+      return prefix + 'ptr_diff'
+
+    if self.type.is_result_vector():
+      # all vector types must be passed by reference
+      if not self.type.is_byref():
+        return 'invalid'
+
+      if self.type.is_result_vector_string():
+        # string vector type
+        if self.type.is_const():
+          return 'string_vec_byref_const'
+        return 'string_vec_byref'
+
+      if self.type.is_result_vector_simple():
+        if self.type.get_vector_type() != 'bool':
+          # simple/enumeration vector types
+          if self.type.is_const():
+            return 'simple_vec_byref_const'
+          return 'simple_vec_byref'
+
+        # boolean vector types
+        if self.type.is_const():
+          return 'bool_vec_byref_const'
+        return 'bool_vec_byref'
+
+      if self.type.is_result_vector_ptr():
+        # *ptr vector types
+        prefix = self.type.get_result_vector_ptr_type_prefix()
+        same_side = self.parent.is_same_side(self.type.get_ptr_type())
+        if self.type.is_const():
+          if same_side:
+            return prefix + 'ptr_vec_same_byref_const'
+          return prefix + 'ptr_vec_diff_byref_const'
+        if same_side:
+          return prefix + 'ptr_vec_same_byref'
+        return prefix + 'ptr_vec_diff_byref'
+
+    # string single map type
+    if self.type.is_result_map_single():
+      if not self.type.is_byref():
+        return 'invalid'
+      if self.type.is_const():
+        return 'string_map_single_byref_const'
+      return 'string_map_single_byref'
+
+    # string multi map type
+    if self.type.is_result_map_multi():
+      if not self.type.is_byref():
+        return 'invalid'
+      if self.type.is_const():
+        return 'string_map_multi_byref_const'
+      return 'string_map_multi_byref'
+
+    return 'invalid'
+
+  def get_retval_type(self):
+    """ Returns the retval type as defined in translator.README.txt. """
+    if self.type.has_name():
+      raise Exception('Cannot be called for argument types')
+
+    # unsupported modifiers
+    if self.type.is_const() or self.type.is_byref() or \
+        self.type.is_byaddr():
+      return 'invalid'
+
+    # void types don't have a return value
+    if self.type.get_type() == 'void':
+      return 'none'
+
+    if (self.type.is_result_simple() and \
+            self.type.get_type() != 'bool') or \
+       (self.type.is_result_struct() and self.type.is_result_struct_enum()):
+      return 'simple'
+
+    if self.type.get_type() == 'bool':
+      return 'bool'
+
+    if self.type.is_result_string():
+      return 'string'
+
+    if self.type.is_result_ptr():
+      prefix = self.type.get_result_ptr_type_prefix()
+      if self.parent.is_same_side(self.type.get_ptr_type()):
+        return prefix + 'ptr_same'
+      else:
+        return prefix + 'ptr_diff'
+
+    return 'invalid'
+
+  def get_retval_default(self, for_capi):
+    """ Returns the default return value based on the retval type. """
+    # start with the default retval attribute, if any.
+    retval = self.get_attrib_default_retval()
+    if not retval is None:
+      if for_capi:
+        # apply any appropriate C API translations.
+        if retval == 'true':
+          return '1'
+        if retval == 'false':
+          return '0'
+      return retval
+
+    # next look at the retval type value.
+    type = self.get_retval_type()
+    if type == 'simple':
+      return self.get_type().get_result_simple_default()
+    elif type == 'bool':
+      if for_capi:
+        return '0'
+      return 'false'
+    elif type == 'string':
+      if for_capi:
+        return 'NULL'
+      return 'CefString()'
+    elif type == 'refptr_same' or type == 'refptr_diff' or \
+         type == 'rawptr_same' or type == 'rawptr_diff':
+      if for_capi:
+        return 'NULL'
+      return 'nullptr'
+    elif type == 'ownptr_same' or type == 'ownptr_diff':
+      if for_capi:
+        return 'NULL'
+      return 'CefOwnPtr<' + self.type.get_ptr_type() + '>()'
+
+    return ''
+
+
+class obj_analysis:
+  """ Class representing an analysis of a data type value. """
+
+  def __init__(self, scopelist, value, named):
+    self.value = value
+    self.result_type = 'unknown'
+    self.result_value = None
+    self.result_default = None
+    self.ptr_type = None
+
+    # parse the argument string
+    partlist = value.strip().split()
+
+    if named == True:
+      # extract the name value
+      self.name = partlist[-1]
+      del partlist[-1]
+    else:
+      self.name = None
+
+    if len(partlist) == 0:
+      raise Exception('Invalid argument value: ' + value)
+
+    # check const status
+    if partlist[0] == 'const':
+      self.isconst = True
+      del partlist[0]
+    else:
+      self.isconst = False
+
+    if len(partlist) == 0:
+      raise Exception('Invalid argument value: ' + value)
+
+    # combine the data type
+    self.type = ' '.join(partlist)
+
+    # extract the last character of the data type
+    endchar = self.type[-1]
+
+    # check if the value is passed by reference
+    if endchar == '&':
+      self.isbyref = True
+      self.type = self.type[:-1]
+    else:
+      self.isbyref = False
+
+    # check if the value is passed by address
+    if endchar == '*':
+      self.isbyaddr = True
+      self.type = self.type[:-1]
+    else:
+      self.isbyaddr = False
+
+    # see if the value is directly identifiable
+    if self._check_advanced(self.type) == True:
+      return
+
+    # not identifiable, so look it up
+    translation = None
+    for scope in scopelist:
+      if not isinstance(scope, obj_header) \
+          and not isinstance(scope, obj_class):
+        raise Exception('Invalid scope object type')
+      translation = scope.get_alias_translation(self.type)
+      if not translation is None:
+        break
+
+    if translation is None:
+      raise Exception('Failed to translate type: ' + self.type)
+
+    # the translation succeeded so keep the result
+    self.result_type = translation.result_type
+    self.result_value = translation.result_value
+
+  def _check_advanced(self, value):
+    # check for vectors
+    if value.find('std::vector') == 0:
+      self.result_type = 'vector'
+      val = value[12:-1].strip()
+      self.result_value = [self._get_basic(val)]
+      self.result_value[0]['vector_type'] = val
+      return True
+
+    # check for maps
+    if value.find('std::map') == 0:
+      self.result_type = 'map'
+      vals = value[9:-1].split(',')
+      if len(vals) == 2:
+        self.result_value = [
+            self._get_basic(vals[0].strip()),
+            self._get_basic(vals[1].strip())
+        ]
+        return True
+
+    # check for multimaps
+    if value.find('std::multimap') == 0:
+      self.result_type = 'multimap'
+      vals = value[14:-1].split(',')
+      if len(vals) == 2:
+        self.result_value = [
+            self._get_basic(vals[0].strip()),
+            self._get_basic(vals[1].strip())
+        ]
+        return True
+
+    # check for basic types
+    basic = self._get_basic(value)
+    if not basic is None:
+      self.result_type = basic['result_type']
+      self.result_value = basic['result_value']
+      if 'ptr_type' in basic:
+        self.ptr_type = basic['ptr_type']
+      if 'result_default' in basic:
+        self.result_default = basic['result_default']
+      return True
+
+    return False
+
+  def _get_basic(self, value):
+    # check for string values
+    if value == "CefString":
+      return {'result_type': 'string', 'result_value': None}
+
+    # check for simple direct translations
+    if value in _simpletypes.keys():
+      return {
+          'result_type': 'simple',
+          'result_value': _simpletypes[value][0],
+          'result_default': _simpletypes[value][1],
+      }
+
+    # check if already a C API structure
+    if value[-2:] == '_t':
+      return {'result_type': 'structure', 'result_value': value}
+
+    # check for CEF reference pointers
+    p = re.compile('^CefRefPtr<(.*?)>$', re.DOTALL)
+    list = p.findall(value)
+    if len(list) == 1:
+      return {
+          'result_type': 'refptr',
+          'result_value': get_capi_name(list[0], True) + '*',
+          'ptr_type': list[0]
+      }
+
+    # check for CEF owned pointers
+    p = re.compile('^CefOwnPtr<(.*?)>$', re.DOTALL)
+    list = p.findall(value)
+    if len(list) == 1:
+      return {
+          'result_type': 'ownptr',
+          'result_value': get_capi_name(list[0], True) + '*',
+          'ptr_type': list[0]
+      }
+
+    # check for CEF raw pointers
+    p = re.compile('^CefRawPtr<(.*?)>$', re.DOTALL)
+    list = p.findall(value)
+    if len(list) == 1:
+      return {
+          'result_type': 'rawptr',
+          'result_value': get_capi_name(list[0], True) + '*',
+          'ptr_type': list[0]
+      }
+
+    # check for CEF structure types
+    if value[0:3] == 'Cef' and value[-4:] != 'List':
+      return {
+          'result_type': 'structure',
+          'result_value': get_capi_name(value, True)
+      }
+
+    return None
+
+  def __repr__(self):
+    return '(' + self.result_type + ') ' + str(self.result_value)
+
+  def has_name(self):
+    """ Returns true if a name value exists. """
+    return (not self.name is None)
+
+  def get_name(self):
+    """ Return the name. """
+    return self.name
+
+  def get_value(self):
+    """ Return the C++ value (type + name). """
+    return self.value
+
+  def get_type(self):
+    """ Return the C++ type. """
+    return self.type
+
+  def get_ptr_type(self):
+    """ Return the C++ class type referenced by a CefRefPtr. """
+    if self.is_result_vector() and self.is_result_vector_ptr():
+      # return the vector RefPtr type
+      return self.result_value[0]['ptr_type']
+    # return the basic RefPtr type
+    return self.ptr_type
+
+  def get_vector_type(self):
+    """ Return the C++ class type referenced by a std::vector. """
+    if self.is_result_vector():
+      return self.result_value[0]['vector_type']
+    return None
+
+  def is_const(self):
+    """ Returns true if the argument value is constant. """
+    return self.isconst
+
+  def is_byref(self):
+    """ Returns true if the argument is passed by reference. """
+    return self.isbyref
+
+  def is_byaddr(self):
+    """ Returns true if the argument is passed by address. """
+    return self.isbyaddr
+
+  def is_result_simple(self):
+    """ Returns true if this is a simple argument type. """
+    return (self.result_type == 'simple')
+
+  def get_result_simple_type_root(self):
+    """ Return the simple structure or basic type name. """
+    return self.result_value
+
+  def get_result_simple_type(self):
+    """ Return the simple type. """
+    result = ''
+    if self.is_const():
+      result += 'const '
+    result += self.result_value
+    if self.is_byaddr() or self.is_byref():
+      result += '*'
+    return result
+
+  def get_result_simple_default(self):
+    """ Return the default value fo the basic type. """
+    return self.result_default
+
+  def is_result_ptr(self):
+    """ Returns true if this is a *Ptr type. """
+    return self.is_result_refptr() or self.is_result_ownptr() or \
+           self.is_result_rawptr()
+
+  def get_result_ptr_type_root(self):
+    """ Return the *Ptr type structure name. """
+    return self.result_value[:-1]
+
+  def get_result_ptr_type(self, defined_structs=[]):
+    """ Return the *Ptr type. """
+    result = ''
+    if not self.result_value[:-1] in defined_structs:
+      result += 'struct _'
+    result += self.result_value
+    if self.is_byref() or self.is_byaddr():
+      result += '*'
+    return result
+
+  def get_result_ptr_type_prefix(self):
+    """ Returns the *Ptr type prefix. """
+    if self.is_result_refptr():
+      return 'ref'
+    if self.is_result_ownptr():
+      return 'own'
+    if self.is_result_rawptr():
+      return 'raw'
+    raise Exception('Not a pointer type')
+
+  def is_result_refptr(self):
+    """ Returns true if this is a RefPtr type. """
+    return (self.result_type == 'refptr')
+
+  def is_result_ownptr(self):
+    """ Returns true if this is a OwnPtr type. """
+    return (self.result_type == 'ownptr')
+
+  def is_result_rawptr(self):
+    """ Returns true if this is a RawPtr type. """
+    return (self.result_type == 'rawptr')
+
+  def is_result_struct(self):
+    """ Returns true if this is a structure type. """
+    return (self.result_type == 'structure')
+
+  def is_result_struct_enum(self):
+    """ Returns true if this struct type is likely an enumeration. """
+    # structure values that are passed by reference or address must be
+    # structures and not enumerations
+    if not self.is_byref() and not self.is_byaddr():
+      return True
+    return False
+
+  def get_result_struct_type(self, defined_structs=[]):
+    """ Return the structure or enumeration type. """
+    result = ''
+    is_enum = self.is_result_struct_enum()
+    if not is_enum:
+      if self.is_const():
+        result += 'const '
+      if not self.result_value in defined_structs:
+        result += 'struct _'
+    result += self.result_value
+    if not is_enum:
+      result += '*'
+    return result
+
+  def is_result_string(self):
+    """ Returns true if this is a string type. """
+    return (self.result_type == 'string')
+
+  def get_result_string_type(self):
+    """ Return the string type. """
+    if not self.has_name():
+      # Return values are string structs that the user must free. Use
+      # the name of the structure as a hint.
+      return 'cef_string_userfree_t'
+    elif not self.is_const() and (self.is_byref() or self.is_byaddr()):
+      # Parameters passed by reference or address. Use the normal
+      # non-const string struct.
+      return 'cef_string_t*'
+    # Const parameters use the const string struct.
+    return 'const cef_string_t*'
+
+  def is_result_vector(self):
+    """ Returns true if this is a vector type. """
+    return (self.result_type == 'vector')
+
+  def is_result_vector_string(self):
+    """ Returns true if this is a string vector. """
+    return self.result_value[0]['result_type'] == 'string'
+
+  def is_result_vector_simple(self):
+    """ Returns true if this is a string vector. """
+    return self.result_value[0]['result_type'] == 'simple'
+
+  def is_result_vector_ptr(self):
+    """ Returns true if this is a *Ptr vector. """
+    return self.is_result_vector_refptr() or \
+           self.is_result_vector_ownptr() or \
+           self.is_result_vector_rawptr()
+
+  def get_result_vector_ptr_type_prefix(self):
+    """ Returns the *Ptr type prefix. """
+    if self.is_result_vector_refptr():
+      return 'ref'
+    if self.is_result_vector_ownptr():
+      return 'own'
+    if self.is_result_vector_rawptr():
+      return 'raw'
+    raise Exception('Not a pointer type')
+
+  def is_result_vector_refptr(self):
+    """ Returns true if this is a RefPtr vector. """
+    return self.result_value[0]['result_type'] == 'refptr'
+
+  def is_result_vector_ownptr(self):
+    """ Returns true if this is a OwnPtr vector. """
+    return self.result_value[0]['result_type'] == 'ownptr'
+
+  def is_result_vector_rawptr(self):
+    """ Returns true if this is a RawPtr vector. """
+    return self.result_value[0]['result_type'] == 'rawptr'
+
+  def get_result_vector_type_root(self):
+    """ Return the vector structure or basic type name. """
+    return self.result_value[0]['result_value']
+
+  def get_result_vector_type(self, defined_structs=[]):
+    """ Return the vector type. """
+    if not self.has_name():
+      raise Exception('Cannot use vector as a return type')
+
+    type = self.result_value[0]['result_type']
+    value = self.result_value[0]['result_value']
+
+    result = {}
+    if type == 'string':
+      result['value'] = 'cef_string_list_t'
+      result['format'] = 'single'
+      return result
+
+    if type == 'simple':
+      str = value
+      if self.is_const():
+        str += ' const'
+      str += '*'
+      result['value'] = str
+    elif type == 'refptr' or type == 'ownptr' or type == 'rawptr':
+      str = ''
+      if not value[:-1] in defined_structs:
+        str += 'struct _'
+      str += value
+      if self.is_const():
+        str += ' const'
+      str += '*'
+      result['value'] = str
+    else:
+      raise Exception('Unsupported vector type: ' + type)
+
+    # vector values must be passed as a value array parameter
+    # and a size parameter
+    result['format'] = 'multi-arg'
+    return result
+
+  def is_result_map(self):
+    """ Returns true if this is a map type. """
+    return (self.result_type == 'map' or self.result_type == 'multimap')
+
+  def is_result_map_single(self):
+    """ Returns true if this is a single map type. """
+    return (self.result_type == 'map')
+
+  def is_result_map_multi(self):
+    """ Returns true if this is a multi map type. """
+    return (self.result_type == 'multimap')
+
+  def get_result_map_type(self, defined_structs=[]):
+    """ Return the map type. """
+    if not self.has_name():
+      raise Exception('Cannot use map as a return type')
+    if self.result_value[0]['result_type'] == 'string' \
+        and self.result_value[1]['result_type'] == 'string':
+      if self.result_type == 'map':
+        return {'value': 'cef_string_map_t', 'format': 'single'}
+      elif self.result_type == 'multimap':
+        return {'value': 'cef_string_multimap_t', 'format': 'multi'}
+    raise Exception('Only mappings of strings to strings are supported')
+
+  def get_capi(self, defined_structs=[]):
+    """ Format the value for the C API. """
+    result = ''
+    format = 'single'
+    if self.is_result_simple():
+      result += self.get_result_simple_type()
+    elif self.is_result_ptr():
+      result += self.get_result_ptr_type(defined_structs)
+    elif self.is_result_struct():
+      result += self.get_result_struct_type(defined_structs)
+    elif self.is_result_string():
+      result += self.get_result_string_type()
+    elif self.is_result_map():
+      resdict = self.get_result_map_type(defined_structs)
+      if resdict['format'] == 'single' or resdict['format'] == 'multi':
+        result += resdict['value']
+      else:
+        raise Exception('Unsupported map type')
+    elif self.is_result_vector():
+      resdict = self.get_result_vector_type(defined_structs)
+      if resdict['format'] != 'single':
+        format = resdict['format']
+      result += resdict['value']
+
+    if self.has_name():
+      result += ' ' + self.get_name()
+
+    return {'format': format, 'value': result}
+
+
+# test the module
+if __name__ == "__main__":
+  import pprint
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) != 2:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <directory>')
+    sys.exit()
+
+  pp = pprint.PrettyPrinter(indent=4)
+
+  # create the header object
+  header = obj_header()
+  header.add_directory(sys.argv[1])
+
+  # output the type mapping
+  types = {}
+  header.get_types(types)
+  pp.pprint(types)
+  sys.stdout.write('\n')
+
+  # output the parsed C++ data
+  sys.stdout.write(str(header))
+
+  # output the C API formatted data
+  defined_names = header.get_defined_structs()
+  result = ''
+
+  # global functions
+  funcs = header.get_funcs()
+  if len(funcs) > 0:
+    for func in funcs:
+      result += func.get_capi_proto(defined_names) + ';\n'
+    result += '\n'
+
+  classes = header.get_classes()
+  for cls in classes:
+    # virtual functions are inside a structure
+    result += 'struct ' + cls.get_capi_name() + '\n{\n'
+    funcs = cls.get_virtual_funcs()
+    if len(funcs) > 0:
+      for func in funcs:
+        result += '\t' + func.get_capi_proto(defined_names) + ';\n'
+    result += '}\n\n'
+
+    defined_names.append(cls.get_capi_name())
+
+    # static functions become global
+    funcs = cls.get_static_funcs()
+    if len(funcs) > 0:
+      for func in funcs:
+        result += func.get_capi_proto(defined_names) + ';\n'
+      result += '\n'
+  sys.stdout.write(result)
diff --git a/src/tools/cef_version.py b/src/tools/cef_version.py
new file mode 100644
index 0000000..132438b
--- /dev/null
+++ b/src/tools/cef_version.py
@@ -0,0 +1,275 @@
+# Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from file_util import *
+import git_util as git
+import os
+
+
+class VersionFormatter:
+  """ Formats CEF version information. """
+
+  def __init__(self, chromium_src_path=None):
+    if chromium_src_path is None:
+      # Relative to the current directory.
+      script_path = os.path.abspath(os.path.dirname(__file__))
+      chromium_src_path = os.path.abspath(
+          os.path.join(script_path, os.pardir, os.pardir))
+
+    self.src_path = chromium_src_path
+    assert os.path.isdir(self.src_path), self.src_path
+    self.cef_path = os.path.join(self.src_path, 'cef')
+    assert os.path.isdir(self.cef_path), self.cef_path
+
+    # Whether to use the old version format by default.
+    self._old_format_default = \
+        bool(int(os.environ.get('CEF_OLD_VERSION_FORMAT', '0')))
+
+    self.reset()
+
+  def reset(self):
+    """ Reset internal state. """
+    self._chrome_version = {}
+    self._cef_version = {}
+    self._cef_commit = {}
+    self._branch_version = {}
+    self._old_version_string = None
+    self._old_version_parts = {}
+    self._version_string = None
+    self._version_parts = {}
+
+  def get_chrome_version_components(self):
+    """ Returns Chrome version components. """
+    if not bool(self._chrome_version):
+      file_path = os.path.join(self.src_path, 'chrome', 'VERSION')
+      assert os.path.isfile(file_path), file_path
+      read_version_file(file_path, self._chrome_version)
+    return self._chrome_version
+
+  def get_cef_version_components(self):
+    """ Returns CEF version components. """
+    if not bool(self._cef_version):
+      file_path = os.path.join(self.cef_path, 'VERSION.in')
+      assert os.path.isfile(file_path), file_path
+      read_version_file(file_path, self._cef_version)
+    return self._cef_version
+
+  def get_cef_commit_components(self):
+    """ Returns CEF commit components. """
+    if not bool(self._cef_commit):
+      hash = git.get_hash(self.cef_path)
+      number = git.get_commit_number(self.cef_path)
+      self._cef_commit = {'HASH': hash, 'NUMBER': number}
+    return self._cef_commit
+
+  def get_cef_branch_version_components(self):
+    """ Computes the CEF branch version. """
+    if not bool(self._branch_version):
+      minor = 0
+      bugfix = 0
+
+      # Retrieve the list of commits that have been applied on the current
+      # branch since branching from origin/master.
+      hashes = git.get_branch_hashes(self.cef_path)
+      for hash in hashes:
+        # Determine if the API hash file was modified by the commit.
+        found = False
+        files = git.get_changed_files(self.cef_path, hash)
+        for file in files:
+          if file.find('cef_api_hash.h') >= 0:
+            found = True
+            break
+
+        if found:
+          minor += 1
+          bugfix = 0
+        else:
+          bugfix += 1
+
+      self._branch_version = {'MINOR': minor, 'PATCH': bugfix}
+    return self._branch_version
+
+  def get_chromium_version_string(self):
+    """ Returns the Chromium version number string. """
+    chrome_version = self.get_chrome_version_components()
+    return '%s.%s.%s.%s' % (chrome_version['MAJOR'], chrome_version['MINOR'],
+                            chrome_version['BUILD'], chrome_version['PATCH'])
+
+  @staticmethod
+  def _format_commit_hash(hash):
+    return 'g%s' % hash[:7]
+
+  # Computes old version numbers in the format "X.YYYY.A.gHHHHHHH".
+  #
+  # Where:
+  # - "X" is the CEF major version (currently 3).
+  # - "YYYY" is the Chromium branch.
+  # - "A" is an incremental number representing the number of commits in the
+  #   current branch. This is roughly equivalent to the SVN revision number but
+  #   on a per-branch basis and assists people in quickly determining the order
+  #   of builds in the same branch (for bug reports, etc).
+  # - "gHHHHHHH" is the 7-character abbreviation for the Git commit hash. This
+  #   facilitates lookup of the relevant commit history in Git.
+  #
+  # Example: "3.3729.1921.g62d140e"
+  def _compute_old_version(self):
+    if not self._old_version_string is None:
+      return
+
+    chrome_version = self.get_chrome_version_components()
+    cef_version = self.get_cef_version_components()
+    cef_commit = self.get_cef_commit_components()
+    cef_commit_hash = self._format_commit_hash(cef_commit['HASH'])
+
+    self._old_version_parts = {
+        'MAJOR': int(cef_version['CEF_MAJOR']),
+        'MINOR': int(chrome_version['BUILD']),
+        'PATCH': int(cef_commit['NUMBER'])
+    }
+    self._old_version_string = \
+        '%s.%s.%s.%s' % (cef_version['CEF_MAJOR'], chrome_version['BUILD'],
+                         cef_commit['NUMBER'], cef_commit_hash)
+
+  def _get_old_version_string(self):
+    self._compute_old_version()
+    return self._old_version_string
+
+  def _get_old_version_parts(self):
+    self._compute_old_version()
+    return self._old_version_parts
+
+  # Computes version numbers in the format:
+  # - "X.Y.Z+gHHHHHHH+chromium-A.B.C.D" for release branch builds.
+  # - "X.0.0-master.N+gHHHHHHH+chromium-A.B.C.D" for master branch builds.
+  #
+  # Where:
+  # - "X" is the Chromium major version (e.g. 74).
+  # - "Y" is an incremental number that starts at 0 when a release branch is
+  #   created and changes only when the CEF C/C++ API changes (similar to how
+  #   the CEF_API_HASH_UNIVERSAL value behaves in cef_version.h) (release branch
+  #   only).
+  # - "Z" is an incremental number that starts at 0 when a release branch is
+  #   created and changes on each commit, with reset to 0 when "Y" changes
+  #   (release branch only).
+  # - "N" is an incremental number representing the number of commits (master
+  #   branch only).
+  # - "gHHHHHHH" is the 7-character abbreviation for the Git commit hash. This
+  #   facilitates lookup of the relevant commit history in Git.
+  # - "A.B.C.D" is the Chromium version (e.g. 74.0.3729.6).
+  #
+  # Examples:
+  # - "74.0.1+g62d140e+chromium-74.0.3729.6" for a release build.
+  # - "74.0.0-master.1920+g725ed88+chromium-74.0.3729.0" for a master build.
+  def _compute_version(self):
+    if not self._version_string is None:
+      return
+
+    chrome_version = self.get_chrome_version_components()
+    chrome_major = chrome_version['MAJOR']
+    chrome_version_part = 'chromium-' + self.get_chromium_version_string()
+
+    cef_commit = self.get_cef_commit_components()
+    cef_commit_hash = self._format_commit_hash(cef_commit['HASH'])
+
+    # Determine whether the current commit is on a release branch. For example,
+    # if using Chrome build 3683, are we on CEF branch "3683" or "origin/3683"?
+    release_branch = chrome_version['BUILD']
+    on_release_branch = (
+        git.is_ancestor(self.cef_path, 'HEAD', release_branch) or
+        git.is_ancestor(self.cef_path, 'HEAD', 'origin/' + release_branch))
+
+    if not on_release_branch:
+      # Not on a commit that is part of an official named release branch. See
+      # if we can get the name of the branch we are on (may be just "HEAD").
+      cef_branch_name = git.get_branch_name(self.cef_path).split('/')[-1]
+
+      self._version_parts = {'MAJOR': int(chrome_major), 'MINOR': 0, 'PATCH': 0}
+      self._version_string = '%s.0.0-%s.%s+%s+%s' % \
+             (chrome_major, cef_branch_name, cef_commit['NUMBER'],
+              cef_commit_hash, chrome_version_part)
+    else:
+      cef_branch = self.get_cef_branch_version_components()
+
+      self._version_parts = {
+          'MAJOR': int(chrome_major),
+          'MINOR': cef_branch['MINOR'],
+          'PATCH': cef_branch['PATCH']
+      }
+      self._version_string = '%s.%d.%d+%s+%s' % \
+             (chrome_major, cef_branch['MINOR'], cef_branch['PATCH'],
+              cef_commit_hash, chrome_version_part)
+
+  def _get_version_string(self):
+    self._compute_version()
+    return self._version_string
+
+  def _get_version_parts(self):
+    self._compute_version()
+    return self._version_parts
+
+  def get_version_string(self, oldFormat=None):
+    """ Returns the CEF version number string based on current checkout state.
+    """
+    if oldFormat is None:
+      oldFormat = self._old_format_default
+
+    if oldFormat:
+      return self._get_old_version_string()
+    return self._get_version_string()
+
+  def get_version_parts(self, oldFormat=None):
+    """ Returns the CEF version number parts based on current checkout state.
+    """
+    if oldFormat is None:
+      oldFormat = self._old_format_default
+
+    if oldFormat:
+      return self._get_old_version_parts()
+    return self._get_version_parts()
+
+  def get_plist_version_string(self, oldFormat=None):
+    """ Returns the CEF version number string for plist files based on current
+        checkout state. """
+    parts = self.get_version_parts(oldFormat=oldFormat)
+    return "%d.%d.%d.0" % (parts['MAJOR'], parts['MINOR'], parts['PATCH'])
+
+  def get_dylib_version_string(self, oldFormat=None):
+    """ Returns the CEF version number string for dylib files based on current
+        checkout state. """
+    parts = self.get_version_parts(oldFormat=oldFormat)
+    # Dylib format supports a max value of 255 for the 2nd and 3rd components.
+    return "%d%d.%d.%d" % (parts['MAJOR'], parts['MINOR'], parts['PATCH'] / 255,
+                           parts['PATCH'] % 255)
+
+
+# Test the module.
+if __name__ == "__main__":
+  import sys
+
+  # Optionally specify a format.
+  formats = ['current', 'old', 'plist', 'dylib']
+  if len(sys.argv) >= 2:
+    formats = [sys.argv[1]]
+
+  # Optionally specify the path to chromium/src.
+  chromium_src_path = None
+  if len(sys.argv) >= 3:
+    chromium_src_path = sys.argv[2]
+
+  formatter = VersionFormatter(chromium_src_path)
+
+  for format in formats:
+    if len(formats) > 1:
+      print(format)
+
+    if format == 'old':
+      print(formatter.get_version_string(True))
+    elif format == 'dylib':
+      print(formatter.get_dylib_version_string())
+    elif format == 'plist':
+      print(formatter.get_plist_version_string())
+    else:
+      print(formatter.get_version_string(False))
diff --git a/src/tools/cefbuilds/cef_html_builder.py b/src/tools/cefbuilds/cef_html_builder.py
new file mode 100644
index 0000000..f8fbc2a
--- /dev/null
+++ b/src/tools/cefbuilds/cef_html_builder.py
@@ -0,0 +1,323 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from cef_json_builder import cef_json_builder
+import datetime
+import math
+import os
+import sys
+
+# Class used to build the cefbuilds HTML file. Generate an index.json file using
+# the cef_json_builder_example.py tool (or some other means) and then run:
+# > python cef_html_builder.py index.json index.html.in index.html
+#
+# Expected HTML template format is:
+#
+# Header
+# <section:platform_link>
+#  $platform_name$
+# </section:platform_link>
+# <section:platform>
+#   $platform_name$ Builds:
+#   <section:version>
+#     CEF version $cef_version$ Files:
+#     <section:file>
+#       File $file$ size $size$ sha1 $sha1$
+#     </section:file>
+#   </section:version>
+# </section:platform>
+# Footer
+#
+# Notes:
+# - The variables ("$key$") available in each section generally match the key
+#   names that exist in the JSON file for that section. Additional variables are
+#   exposed where needed.
+# - Some global variables like "$year$" will be replaced in the whole template
+#   before further parsing occurs.
+
+
+class cef_html_builder:
+  """ Class used to build the cefbuilds HTML file. """
+
+  def __init__(self, branding=''):
+    """ Create a new cef_html_builder object. """
+    self.clear()
+    self._branding = branding
+
+  def clear(self):
+    """ Clear the contents of this object. """
+    self._parts = {}
+    return
+
+  @staticmethod
+  def _token(key):
+    # Returns the token representation of |key|
+    return '$' + key + '$'
+
+  @staticmethod
+  def _section_tags(section):
+    # Returns the start and end tags for |section|
+    return ('<section:' + section + '>', '</section:' + section + '>')
+
+  @staticmethod
+  def _section_key(section):
+    # Returns the replacement key for |section|
+    return section + '_section'
+
+  @staticmethod
+  def _replace(str, key, value):
+    # Replaces all instances of |key| with |value| in |str|.
+    return str.replace(cef_html_builder._token(key), value)
+
+  @staticmethod
+  def _replace_all(str, dict):
+    for (key, value) in dict.items():
+      str = cef_html_builder._replace(str, key, value)
+    return str
+
+  @staticmethod
+  def _extract(str, section):
+    # Extracts the |section| region and replaces it with a token named
+    # "<section>_section".
+    (start_tag, end_tag) = cef_html_builder._section_tags(section)
+    start_pos = str.find(start_tag)
+    end_pos = str.rfind(end_tag)
+    if start_pos < 0 or end_pos < 0:
+      raise Exception('Failed to find section %s' % section)
+    top = str[:start_pos]
+    middle = str[start_pos + len(start_tag):end_pos]
+    bottom = str[end_pos + len(end_tag):]
+    return (
+        top + cef_html_builder._token(cef_html_builder._section_key(section)) +
+        bottom, middle)
+
+  def load(self, html_template):
+    """ Load the specified |html_template| string. """
+    self.clear()
+    root = html_template
+
+    # Extract the platform link section from root.
+    (root, platform_link) = self._extract(root, 'platform_link')
+
+    # Extract platform section from root.
+    (root, platform) = self._extract(root, 'platform')
+
+    # Extract version section from platform.
+    (platform, version) = self._extract(platform, 'version')
+
+    # Extract file section from version.
+    (version, file) = self._extract(version, 'file')
+
+    self._parts = {
+        'root': root,
+        'platform_link': platform_link,
+        'platform': platform,
+        'version': version,
+        'file': file
+    }
+
+  @staticmethod
+  def _get_platform_name(platform):
+    return {
+        'linux32': 'Linux 32-bit',
+        'linux64': 'Linux 64-bit',
+        'linuxarm': 'Linux ARM',
+        'linuxarm64': 'Linux ARM64',
+        'macosx64': 'Mac OS X 64-bit',
+        'windows32': 'Windows 32-bit',
+        'windows64': 'Windows 64-bit'
+    }[platform]
+
+  @staticmethod
+  def _get_type_name(type):
+    return {
+        'standard': 'Standard Distribution',
+        'minimal': 'Minimal Distribution',
+        'client': 'Sample Application',
+        'debug_symbols': 'Debug Symbols',
+        'release_symbols': 'Release Symbols'
+    }[type]
+
+  @staticmethod
+  def _get_date(date):
+    return date.strftime('%m/%d/%Y')
+
+  @staticmethod
+  def _get_file_size(size):
+    if (size == 0):
+      return '0B'
+    size_name = ('B', 'KB', 'MB', 'GB')
+    i = int(math.floor(math.log(size, 1024)))
+    p = math.pow(1024, i)
+    s = round(size / p, 2)
+    return '%.2f %s' % (s, size_name[i])
+
+  @staticmethod
+  def _get_cef_source_url(cef_version):
+    if cef_version.find('+chromium') > 0:
+      # New-style CEF version numbers include the Chromium version number.
+      # Example: 74.0.1+g62d140e+chromium-74.0.3729.6
+      chromium_version = cef_version[cef_version.rfind('-') + 1:]
+      branch = chromium_version.split('.')[2]
+    else:
+      branch = cef_version.split('.')[1]
+    return 'https://bitbucket.org/chromiumembedded/cef/get/%s.tar.bz2' % branch
+
+  @staticmethod
+  def _get_chromium_source_url(chromium_version):
+    if chromium_version == 'master':
+      return 'https://chromium.googlesource.com/chromium/src.git'
+    return 'https://gsdview.appspot.com/chromium-browser-official/chromium-%s.tar.xz' % chromium_version
+
+  @staticmethod
+  def _get_file_url(platform, cef_version, file):
+    return file['name'].replace('+', '%2B')
+
+  @staticmethod
+  def _get_sha1_url(platform, cef_version, file):
+    return cef_html_builder._get_file_url(platform, cef_version, file) + '.sha1'
+
+  @staticmethod
+  def _get_tooltip_text(platform, cef_version, file):
+    if platform.startswith('linux'):
+      sample_app = 'cefsimple'
+    else:
+      sample_app = 'cefclient'
+    return {
+        'standard':
+            'Standard binary distribution. Includes header files, libcef_dll_wrapper source code, binary files, CMake configuration files and source code for the cefclient and cefsimple sample applications. See the included README.txt file for usage and build requirements.',
+        'minimal':
+            'Minimal binary distribution. Includes header files, libcef_dll_wrapper source code, Release build binary files and CMake configuration files. Does not include Debug build binary files or sample application source code. See the included README.txt file for usage and build requirements.',
+        'client':
+            'Release build of the ' + sample_app +
+            ' sample application. See the included README.txt file for usage requirements.',
+        'debug_symbols':
+            'Debug build symbols. Must be extracted and placed next to the CEF Debug binary file with the same name and version.',
+        'release_symbols':
+            'Release build symbols. Must be extracted and placed next to the CEF Release binary file with the same name and version.'
+    }[file['type']]
+
+  def generate(self, json_builder):
+    """ Generate HTML output based on the contents of |json_builder|. """
+    if not isinstance(json_builder, cef_json_builder):
+      raise Exception('Invalid argument')
+
+    # Substitution values are augmented at each nesting level.
+    subs = {
+        'year': '2016',
+        'branding': self._branding,
+    }
+
+    # Substitute variables.
+    root_str = self._replace_all(self._parts['root'], subs)
+
+    platform_link_strs = []
+    platform_strs = []
+    for platform in json_builder.get_platforms():
+      subs['platform'] = platform
+      subs['platform_name'] = self._get_platform_name(platform)
+
+      # Substitute variables.
+      platform_link_str = self._replace_all(self._parts['platform_link'], subs)
+      platform_str = self._replace_all(self._parts['platform'], subs)
+
+      version_strs = []
+      for version in json_builder.get_versions(platform):
+        subs['cef_version'] = version['cef_version']
+        subs['chromium_version'] = version['chromium_version']
+        subs['last_modified'] = self._get_date(
+            version['files'][0]['last_modified'])
+        subs['cef_source_url'] = self._get_cef_source_url(
+            version['cef_version'])
+        subs['chromium_source_url'] = self._get_chromium_source_url(
+            version['chromium_version'])
+
+        # Substitute variables.
+        version_str = self._replace_all(self._parts['version'], subs)
+
+        file_strs = {}
+        for file in version['files']:
+          subs['last_modified'] = self._get_date(file['last_modified'])
+          subs['name'] = file['name']
+          subs['sha1'] = file['sha1']
+          subs['size'] = self._get_file_size(file['size'])
+          subs['type'] = file['type']
+          subs['type_name'] = self._get_type_name(file['type'])
+          subs['file_url'] = self._get_file_url(platform,
+                                                version['cef_version'], file)
+          subs['sha1_url'] = self._get_sha1_url(platform,
+                                                version['cef_version'], file)
+          subs['tooltip_text'] = self._get_tooltip_text(
+              platform, version['cef_version'], file)
+
+          # Substitute variables.
+          file_str = self._replace_all(self._parts['file'], subs)
+          file_strs[file['type']] = file_str
+
+        if len(file_strs) > 0:
+          # Always output file types in the same order.
+          file_out = ''
+          type_order = [
+              'standard', 'minimal', 'client', 'debug_symbols',
+              'release_symbols'
+          ]
+          for type in type_order:
+            if type in file_strs:
+              file_out = file_out + file_strs[type]
+
+          # Insert files.
+          version_str = self._replace(version_str,
+                                      self._section_key('file'), file_out)
+          version_strs.append(version_str)
+
+      if len(version_strs) > 0:
+        # Insert versions.
+        platform_str = self._replace(platform_str,
+                                     self._section_key('version'),
+                                     "".join(version_strs))
+        platform_strs.append(platform_str)
+        platform_link_strs.append(platform_link_str)
+
+    if len(platform_strs) > 0:
+      # Insert platforms.
+      root_str = self._replace(root_str,
+                               self._section_key('platform_link'),
+                               "".join(platform_link_strs))
+      root_str = self._replace(root_str,
+                               self._section_key('platform'),
+                               "".join(platform_strs))
+
+    return root_str
+
+
+# Program entry point.
+if __name__ == '__main__':
+  # Verify command-line arguments.
+  if len(sys.argv) < 4:
+    sys.stderr.write('Usage: %s <json_file_in> <html_file_in> <html_file_out>\n'
+                     % sys.argv[0])
+    sys.exit()
+
+  json_file_in = sys.argv[1]
+  html_file_in = sys.argv[2]
+  html_file_out = sys.argv[3]
+
+  # Create the HTML builder and load the HTML template.
+  print('--> Reading %s' % html_file_in)
+  html_builder = cef_html_builder()
+  with open(html_file_in, 'r') as f:
+    html_builder.load(f.read())
+
+  # Create the JSON builder and load the JSON file.
+  print('--> Reading %s' % json_file_in)
+  json_builder = cef_json_builder(silent=False)
+  with open(json_file_in, 'r') as f:
+    json_builder.load(f.read())
+
+  # Write the HTML output file.
+  print('--> Writing %s' % html_file_out)
+  with open(html_file_out, 'w') as f:
+    f.write(html_builder.generate(json_builder))
diff --git a/src/tools/cefbuilds/cef_json_builder.py b/src/tools/cefbuilds/cef_json_builder.py
new file mode 100644
index 0000000..6d41d3f
--- /dev/null
+++ b/src/tools/cefbuilds/cef_json_builder.py
@@ -0,0 +1,489 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+import datetime
+import json
+import os
+import re
+import sys
+
+if sys.version_info.major == 2:
+  from urllib2 import urlopen
+else:
+  from urllib.request import urlopen
+
+# Class used to build the cefbuilds JSON file. See cef_json_builder_example.py
+# for example usage. See cef_json_builder_test.py for unit tests.
+#
+# Example JSON structure:
+# {
+#   "linux32": {
+#     "versions": [
+#       {
+#         "cef_version": "3.2704.1414.g185cd6c",
+#         "chromium_version": "51.0.2704.47"
+#         "files": [
+#           {
+#             "last_modified": "2016-05-18T22:42:14.066Z"
+#             "name": "cef_binary_3.2704.1414.g185cd6c_linux32.tar.bz2",
+#             "sha1": "47c5cfea43912a1d1771f343de35b205f388415f"
+#             "size": "48549450",
+#             "type": "standard",
+#           }, ...
+#         ],
+#       }, ...
+#     ]
+#   }, ...
+# }
+#
+# Notes:
+# - "files" in a given version will be sorted from newest to oldest based on the
+#   "last_modified" value.
+# - "versions" in a given platform will be sorted from newest to oldest based on
+#   the "last_modified" value of the first (newest) "file" sub-value.
+# - There will be at most one record at the "files" level for each "type".
+
+# This date format intentionally matches the format used in Artifactory
+# directory listings.
+_CEF_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
+
+
+def parse_date(date):
+  return datetime.datetime.strptime(date, _CEF_DATE_FORMAT)
+
+
+def format_date(date):
+  return date.strftime(_CEF_DATE_FORMAT)
+
+
+# Helpers to format datetime values on JSON read/write.
+def cef_from_json(json_object):
+  if 'last_modified' in json_object:
+    json_object['last_modified'] = parse_date(json_object['last_modified'])
+  return json_object
+
+
+class cef_json_encoder(json.JSONEncoder):
+
+  def default(self, o):
+    if isinstance(o, datetime.datetime):
+      return format_date(o)
+    return o
+
+
+_chromium_version_regex = '[1-9]{1}[0-9]{1,2}\.0\.[1-9]{1}[0-9]{2,4}\.(0|[1-9]{1}[0-9]{0,2})'
+_cef_hash_regex = 'g[0-9a-f]{7}'
+_cef_number_regex = '[0-9]{1,5}\.[0-9]{1,5}\.[0-9]{1,5}'
+
+# Example: 3.2704.1414.g185cd6c
+_cef_old_version_regex = _cef_number_regex + '\.' + _cef_hash_regex
+# Example: 74.0.1+g62d140e+chromium-74.0.3729.6
+_cef_version_release_regex = _cef_number_regex + '\+' + _cef_hash_regex + '\+chromium\-' + _chromium_version_regex
+# Example: 74.0.0-master.1920+g725ed88+chromium-74.0.3729.0
+_cef_version_dev_regex = _cef_number_regex + '\-\w+\.[0-9]{1,7}\+' + _cef_hash_regex + '\+chromium\-' + _chromium_version_regex
+
+
+class cef_json_builder:
+  """ Class used to build the cefbuilds JSON file. """
+
+  def __init__(self, prettyprint=False, silent=True):
+    """ Create a new cef_json_builder object. """
+    self._prettyprint = prettyprint
+    self._silent = silent
+    self._fatalerrors = False
+    self.clear()
+
+  @staticmethod
+  def get_platforms():
+    """ Returns the list of supported platforms. """
+    return ('linux32', 'linux64', 'linuxarm', 'linuxarm64', 'macosx64',
+            'windows32', 'windows64')
+
+  @staticmethod
+  def get_distrib_types():
+    """ Returns the list of supported distribution types. """
+    return ('standard', 'minimal', 'client', 'release_symbols', 'debug_symbols')
+
+  @staticmethod
+  def is_valid_version(version):
+    """ Returns true if the specified CEF version is fully qualified and valid. """
+    if version is None:
+      return False
+    return bool(re.compile('^' + _cef_old_version_regex + '$').match(version)) \
+        or bool(re.compile('^' + _cef_version_release_regex + '$').match(version)) \
+        or bool(re.compile('^' + _cef_version_dev_regex + '$').match(version))
+
+  @staticmethod
+  def is_valid_chromium_version(version):
+    """ Returns true if the specified Chromium version is fully qualified and valid. """
+    if version is None:
+      return False
+    return version == 'master' or \
+        bool(re.compile('^' + _chromium_version_regex + '$').match(version))
+
+  @staticmethod
+  def get_file_name(version, platform, type):
+    """ Returns the expected distribution file name excluding extension based on
+        the input parameters. """
+    if type != 'standard':
+      type_str = '_' + type
+    else:
+      type_str = ''
+    return 'cef_binary_%s_%s%s' % (version, platform, type_str)
+
+  def clear(self):
+    """ Clear the contents of this object. """
+    self._data = {}
+    for platform in self.get_platforms():
+      self._data[platform] = {'versions': []}
+    self._versions = {}
+    self._queryct = 0
+
+  def __repr__(self):
+    # Return a string representation of this object.
+    self._sort_versions()
+    if self._prettyprint:
+      return json.dumps(
+          self._data,
+          cls=cef_json_encoder,
+          sort_keys=True,
+          indent=2,
+          separators=(',', ': '))
+    else:
+      return json.dumps(self._data, cls=cef_json_encoder, sort_keys=True)
+
+  def _print(self, msg):
+    if self._fatalerrors:
+      raise Exception(msg)
+    if not self._silent:
+      print(msg)
+
+  def get_query_count(self):
+    """ Returns the number of queries sent while building. """
+    return self._queryct
+
+  def _query_chromium_version(self, cef_version):
+    """ Try to remotely query the Chromium version for old-style CEF version
+        numbers. """
+    chromium_version = 'master'
+    git_hash = cef_version[-7:]
+    query_url = 'https://bitbucket.org/chromiumembedded/cef/raw/%s/CHROMIUM_BUILD_COMPATIBILITY.txt' % git_hash
+    self._queryct = self._queryct + 1
+    if not self._silent:
+      print('Reading %s' % query_url)
+
+    try:
+      # Read the remote URL contents.
+      handle = urlopen(query_url)
+      compat_value = handle.read().strip()
+      handle.close()
+
+      # Parse the contents.
+      config = eval(compat_value, {'__builtins__': None}, None)
+      if not 'chromium_checkout' in config:
+        raise Exception('Unexpected contents')
+
+      val = config['chromium_checkout']
+      if val.find('refs/tags/') == 0:
+        chromium_version = val[10:]
+    except Exception as e:
+      print('Failed to read Chromium version information')
+      raise
+
+    return chromium_version
+
+  def set_chromium_version(self, cef_version, chromium_version=None):
+    """ Set the matching Chromium version. If the specified Chromium version is
+        invalid then it will be queried remotely. """
+    if not self.is_valid_version(cef_version):
+      raise Exception('Invalid CEF version: %s' % cef_version)
+
+    if not self.is_valid_chromium_version(chromium_version):
+      if cef_version in self._versions:
+        # Keep the Chromium version that we already know about.
+        return self._versions[cef_version]
+
+      if cef_version.find('+chromium') > 0:
+        # New-style CEF version numbers include the Chromium version number.
+        # Example: 74.0.1+g62d140e+chromium-74.0.3729.6
+        chromium_version = cef_version[cef_version.rfind('-') + 1:]
+      else:
+        chromium_version = self._query_chromium_version(cef_version)
+
+    if not self.is_valid_chromium_version(chromium_version):
+      raise Exception('Invalid Chromium version: %s' % chromium_version)
+
+    self._versions[cef_version] = chromium_version
+    return chromium_version
+
+  def get_chromium_version(self, cef_version):
+    """ Return the matching Chromium version. If not currently known it will
+        be parsed from the CEF version or queried remotely. """
+    if cef_version in self._versions:
+      return self._versions[cef_version]
+    # Identify the Chromium version.
+    return self.set_chromium_version(cef_version)
+
+  def has_chromium_version(self, cef_version):
+    """ Return True if a matching Chromium version is known. """
+    return cef_version in self._versions
+
+  def load(self, json_string, fatalerrors=True):
+    """ Load new JSON into this object. Any existing contents will be cleared.
+        If |fatalerrors| is True then any errors while loading the JSON file
+        will cause an Exception to be thrown. Otherwise, malformed entries will
+        will be discarded. Unrecognized keys will always be discarded silently.
+    """
+    self.clear()
+
+    self._fatalerrors = fatalerrors
+
+    new_data = json.JSONDecoder(object_hook=cef_from_json).decode(json_string)
+
+    # Validate the new data's structure.
+    for platform in self._data.keys():
+      if not platform in new_data:
+        self._print('load: Platform %s not found' % platform)
+        continue
+      if not 'versions' in new_data[platform]:
+        self._print('load: Missing platform key(s) for %s' % platform)
+        continue
+
+      valid_versions = []
+      for version in new_data[platform]['versions']:
+        if not 'cef_version' in version or \
+            not 'chromium_version' in version or \
+            not 'files' in version:
+          self._print('load: Missing version key(s) for %s' % platform)
+          continue
+
+        valid_files = []
+        found_types = []
+        for file in version['files']:
+          if not 'type' in file or \
+              not 'name' in file or \
+              not 'size' in file or \
+              not 'last_modified' in file or \
+              not 'sha1' in file:
+            self._print('load: Missing file key(s) for %s %s' %
+                        (platform, version['cef_version']))
+            continue
+          (expected_platform, expected_version,
+           expected_type) = self._parse_name(file['name'])
+          if expected_platform != platform or \
+              expected_version != version['cef_version'] or \
+              expected_type != file['type']:
+            self._print('load: File name/attribute mismatch for %s %s %s' %
+                        (platform, version['cef_version'], file['name']))
+            continue
+          self._validate_args(platform, version['cef_version'], file['type'],
+                              file['size'], file['last_modified'], file['sha1'])
+          if file['type'] in found_types:
+            self._print('load: Duplicate %s type for %s %s' %
+                        (file['type'], platform, version['cef_version']))
+            continue
+          found_types.append(file['type'])
+          valid_files.append({
+              'type': file['type'],
+              'name': file['name'],
+              'size': file['size'],
+              'last_modified': file['last_modified'],
+              'sha1': file['sha1'],
+          })
+
+        if len(valid_files) > 0:
+          valid_versions.append({
+              'cef_version':
+                  version['cef_version'],
+              'chromium_version':
+                  self.set_chromium_version(version['cef_version'],
+                                            version['chromium_version']),
+              'files':
+                  self._sort_files(valid_files)
+          })
+
+      if len(valid_versions) > 0:
+        self._data[platform]['versions'] = valid_versions
+
+    self._fatalerrors = False
+
+  def _sort_versions(self):
+    # Sort version records by first (newest) file last_modified value.
+    for platform in self._data.keys():
+      for i in range(0, len(self._data[platform]['versions'])):
+        self._data[platform]['versions'] = \
+          sorted(self._data[platform]['versions'],
+                 key=lambda k: k['files'][0]['last_modified'],
+                 reverse=True)
+
+  @staticmethod
+  def _sort_files(files):
+    # Sort file records by last_modified.
+    return sorted(files, key=lambda k: k['last_modified'], reverse=True)
+
+  @staticmethod
+  def _parse_name(name):
+    # Remove file extension.
+    name_no_ext = os.path.splitext(name)[0]
+    if name_no_ext[-4:] == '.tar':
+      name_no_ext = name_no_ext[:-4]
+    name_parts = name_no_ext.split('_')
+    if len(
+        name_parts) < 4 or name_parts[0] != 'cef' or name_parts[1] != 'binary':
+      raise Exception('Invalid filename: %s' % name)
+
+    # Remove 'cef' and 'binary'.
+    del name_parts[0]
+    del name_parts[0]
+
+    type = None
+
+    # Might be '<version>_<platform>_[debug|release]_symbols'.
+    if name_parts[-1] == 'symbols':
+      del name_parts[-1]
+      if name_parts[-1] == 'debug' or name_parts[-1] == 'release':
+        type = name_parts[-1] + '_symbols'
+        del name_parts[-1]
+
+    # Might be '<version>_<platform>_minimal'.
+    if name_parts[-1] == 'minimal':
+      type = 'minimal'
+      del name_parts[-1]
+
+    # Might be '<version>_<platform>_client'.
+    if name_parts[-1] == 'client':
+      type = 'client'
+      del name_parts[-1]
+
+    # Remainder must be '<version>_<platform>'.
+    if len(name_parts) != 2:
+      raise Exception('Invalid filename: %s' % name)
+
+    if type is None:
+      type = 'standard'
+
+    version = name_parts[0]
+    platform = name_parts[1]
+
+    return [platform, version, type]
+
+  @staticmethod
+  def _validate_args(platform, version, type, size, last_modified, sha1):
+    # Validate input arguments.
+    if not platform in cef_json_builder.get_platforms():
+      raise Exception('Unsupported platform: %s' % platform)
+
+    if not cef_json_builder.is_valid_version(version):
+      raise Exception('Invalid version: %s' % version)
+
+    if not type in cef_json_builder.get_distrib_types():
+      raise Exception('Unsupported distribution type: %s' % type)
+
+    if int(size) <= 0:
+      raise Exception('Invalid size: %s' % size)
+
+    if not isinstance(last_modified, datetime.datetime):
+      # datetime will throw a ValueException if it doesn't parse.
+      parse_date(last_modified)
+
+    if not re.compile('^[0-9a-f]{40}$').match(sha1):
+      raise Exception('Invalid sha1: %s' % sha1)
+
+  def add_file(self, name, size, last_modified, sha1):
+    """ Add a file record with the specified attributes. Returns True if the
+        file is added or False if a file with the same |name| and |sha1|
+        already exists. """
+    # Parse the file name.
+    (platform, version, type) = self._parse_name(name)
+
+    if not isinstance(size, int):
+      size = int(size)
+    if not isinstance(last_modified, datetime.datetime):
+      last_modified = parse_date(last_modified)
+
+    # Validate arguments.
+    self._validate_args(platform, version, type, size, last_modified, sha1)
+
+    # Find the existing version record.
+    version_idx = -1
+    for i in range(0, len(self._data[platform]['versions'])):
+      if self._data[platform]['versions'][i]['cef_version'] == version:
+        # Check the version record.
+        self._print('add_file: Check %s %s' % (platform, version))
+        version_idx = i
+        break
+
+    if version_idx == -1:
+      # Add a new version record.
+      self._print('add_file: Add %s %s' % (platform, version))
+      self._data[platform]['versions'].append({
+          'cef_version': version,
+          'chromium_version': self.get_chromium_version(version),
+          'files': []
+      })
+      version_idx = len(self._data[platform]['versions']) - 1
+
+    # Find the existing file record with matching type.
+    file_changed = True
+    for i in range(0,
+                   len(self._data[platform]['versions'][version_idx]['files'])):
+      if self._data[platform]['versions'][version_idx]['files'][i][
+          'type'] == type:
+        existing_sha1 = self._data[platform]['versions'][version_idx]['files'][
+            i]['sha1']
+        if existing_sha1 != sha1:
+          # Remove the existing file record.
+          self._print('  Remove %s %s' % (name, existing_sha1))
+          del self._data[platform]['versions'][version_idx]['files'][i]
+        else:
+          file_changed = False
+        break
+
+    if file_changed:
+      # Add a new file record.
+      self._print('  Add %s %s' % (name, sha1))
+      self._data[platform]['versions'][version_idx]['files'].append({
+          'type': type,
+          'name': name,
+          'size': size,
+          'last_modified': last_modified,
+          'sha1': sha1
+      })
+
+      # Sort file records by last_modified.
+      # This is necessary for _sort_versions() to function correctly.
+      self._data[platform]['versions'][version_idx]['files'] = \
+          self._sort_files(self._data[platform]['versions'][version_idx]['files'])
+
+    return file_changed
+
+  def get_files(self, platform=None, version=None, type=None):
+    """ Return the files that match the input parameters.
+        All parameters are optional. Version will do partial matching. """
+    results = []
+
+    if platform is None:
+      platforms = self._data.keys()
+    else:
+      platforms = [platform]
+
+    for platform in platforms:
+      for version_obj in self._data[platform]['versions']:
+        if version is None or version_obj['cef_version'].find(version) == 0:
+          for file_obj in version_obj['files']:
+            if type is None or type == file_obj['type']:
+              result_obj = file_obj
+              # Add additional metadata.
+              result_obj['platform'] = platform
+              result_obj['cef_version'] = version_obj['cef_version']
+              result_obj['chromium_version'] = version_obj['chromium_version']
+              results.append(result_obj)
+
+    return results
+
+  def get_versions(self, platform):
+    """ Return all versions for the specified |platform|. """
+    return self._data[platform]['versions']
diff --git a/src/tools/cefbuilds/cef_json_builder_example.py b/src/tools/cefbuilds/cef_json_builder_example.py
new file mode 100644
index 0000000..1403edb
--- /dev/null
+++ b/src/tools/cefbuilds/cef_json_builder_example.py
@@ -0,0 +1,152 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+# This example utility uses the cef_json_builder class to create and update an
+# index.json file in the same directory as this file. Example usage:
+#
+# Add files for macosx64 platform at the specified version:
+# > python cef_json_builder_example.py add macosx64 3.2704.1416.g185cd6c 51.0.2704.47
+#
+# Add files for all platforms at the specified version:
+# > python cef_json_builder_example.py add all 3.2704.1416.g185cd6c 51.0.2704.47
+#
+# See cef_json_builder.get_platforms() for the list of supported platforms.
+#
+# After creating an index.json file you can use the cef_html_builder.py tool to
+# create an HTML file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from cef_json_builder import cef_json_builder
+import datetime
+import os
+import random
+import string
+import sys
+
+
+# Create a fake sha1 checksum value.
+def make_fake_sha1():
+  return ''.join(random.SystemRandom().choice('abcdef' + string.digits)
+                 for _ in range(40))
+
+
+# Create a fake file size value.
+def make_fake_size():
+  return random.randint(30000000, 60000000)
+
+
+# Create fake file info based on |platform| and |version|.
+def make_fake_file_info(platform, version, type):
+  return {
+      'name':
+          cef_json_builder.get_file_name(version, platform, type) + '.tar.gz',
+      'size':
+          make_fake_size(),
+      'lastModified':
+          datetime.datetime.now(),
+      'sha1':
+          make_fake_sha1()
+  }
+
+
+# Returns a list of fake files based on |platform| and |version|.
+def create_fake_files(platform, version):
+  files = []
+
+  # All platforms create standard, minimal and client distributions.
+  files.append(make_fake_file_info(platform, version, 'standard'))
+  files.append(make_fake_file_info(platform, version, 'minimal'))
+  files.append(make_fake_file_info(platform, version, 'client'))
+
+  # Windows and OS X platforms create debug and release symbols.
+  if platform.find('windows') == 0 or platform.find('macosx') == 0:
+    files.append(make_fake_file_info(platform, version, 'debug_symbols'))
+    files.append(make_fake_file_info(platform, version, 'release_symbols'))
+
+  return files
+
+
+# Program entry point.
+if __name__ == '__main__':
+  # Verify command-line arguments.
+  if len(sys.argv) < 5 or sys.argv[1] != 'add':
+    sys.stderr.write(
+        'Usage: %s add <platform> <cef_version> <chromium_version>\n' %
+        sys.argv[0])
+    sys.exit()
+
+  # Requested platform.
+  if sys.argv[2] == 'all':
+    platforms = cef_json_builder.get_platforms()
+  elif sys.argv[2] in cef_json_builder.get_platforms():
+    platforms = [sys.argv[2]]
+  else:
+    sys.stderr.write('Invalid platform: %s\n' % platform)
+    sys.exit()
+
+  # Requested CEF version.
+  cef_version = sys.argv[3]
+  if not cef_json_builder.is_valid_version(cef_version):
+    sys.stderr.write('Invalid CEF version: %s\n' % cef_version)
+    sys.exit()
+
+  # Requested Chromium version.
+  chromium_version = sys.argv[4]
+  if not cef_json_builder.is_valid_chromium_version(chromium_version):
+    sys.stderr.write('Invalid Chromium version: %s\n' % chromium_version)
+    sys.exit()
+
+  # Write the JSON file in the same directory as this file.
+  current_dir = os.path.dirname(__file__)
+  json_file = os.path.join(current_dir, 'index.json')
+
+  # Create the builder object. Enable pretty printing and extra output for
+  # example purposes.
+  builder = cef_json_builder(prettyprint=True, silent=False)
+
+  # Map the CEF version to the Chromium version to avoid a remote query.
+  builder.set_chromium_version(cef_version, chromium_version)
+
+  # Load the existing JSON file, if any. Ignore format errors for example
+  # purposes.
+  if os.path.exists(json_file):
+    print('--> Reading index.json')
+    with open(json_file, 'r') as f:
+      builder.load(f.read(), fatalerrors=False)
+
+  # Create fake file info based on |platform| and |version|. A real
+  # implementation should retrieve the list of files from an external source
+  # like a Web or filesystem directory listing. If using Artifactory, for
+  # example, then "size", "lastModified" and "sha1" attributes would be included
+  # in the directory listing metadata.
+  # For this example we:
+  # - Always use now() as the last modified date. Consequently newly added files
+  #   will always be listed at the top of the JSON platform versions list.
+  # - Always create a new (fake) sha1 checksum value for each file. Consequently
+  #   duplicate calls for the same |platform| + |version| will always replace
+  #   the existing entries. In real implementations the sha1 may be the same
+  #   across calls if the file contents have not changed, in which case
+  #   cef_json_builder.add_file() will return False and upload of the file
+  #   should be skipped.
+  new_files = []
+  for platform in platforms:
+    new_files.extend(create_fake_files(platform, cef_version))
+
+  # Add new files to the builder.
+  changed_files = []
+  for file in new_files:
+    if builder.add_file(file['name'], file['size'], file['lastModified'],
+                        file['sha1']):
+      changed_files.append(file)
+
+  if len(changed_files) > 0:
+    # Write the updated JSON file.
+    print('--> Writing index.json')
+    with open(json_file, 'w') as f:
+      f.write(str(builder))
+
+    # A real implementation would now upload the changed files.
+    for file in changed_files:
+      print('--> Upload file %s' % file['name'])
diff --git a/src/tools/cefbuilds/cef_json_builder_test.py b/src/tools/cefbuilds/cef_json_builder_test.py
new file mode 100644
index 0000000..44eb756
--- /dev/null
+++ b/src/tools/cefbuilds/cef_json_builder_test.py
@@ -0,0 +1,412 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_json_builder import cef_json_builder
+import datetime
+import unittest
+
+
+class TestCefJSONBuilder(unittest.TestCase):
+
+  # Test CEF version number matching.
+  def test_valid_version(self):
+    # Old-style version numbers.
+    self.assertTrue(cef_json_builder.is_valid_version('3.2704.1414.g185cd6c'))
+    # New-style version numbers.
+    self.assertTrue(
+        cef_json_builder.is_valid_version(
+            '74.0.1+g62d140e+chromium-74.0.3729.6'))
+    self.assertTrue(
+        cef_json_builder.is_valid_version(
+            '74.0.0-master.1920+g725ed88+chromium-74.0.3729.0'))
+    self.assertTrue(
+        cef_json_builder.is_valid_version(
+            '74.0.0-my_branch.1920+g725ed88+chromium-74.0.3729.0'))
+
+    # Must be a completely qualified version number.
+    self.assertFalse(cef_json_builder.is_valid_version(None))
+    self.assertFalse(cef_json_builder.is_valid_version('foobar'))
+    self.assertFalse(cef_json_builder.is_valid_version('3.2704.1414'))
+    self.assertFalse(cef_json_builder.is_valid_version('74.0.1+g62d140e'))
+    self.assertFalse(
+        cef_json_builder.is_valid_version('74.0.0-master.1920+g725ed88'))
+    self.assertFalse(
+        cef_json_builder.is_valid_version('74.0.0+chromium-74.0.3729.0'))
+
+  # Test Chromium version number matching.
+  def test_valid_chromium_version(self):
+    self.assertTrue(cef_json_builder.is_valid_chromium_version('74.0.3729.0'))
+    self.assertTrue(cef_json_builder.is_valid_chromium_version('74.0.3729.6'))
+    self.assertTrue(
+        cef_json_builder.is_valid_chromium_version('100.0.59200.602'))
+
+    # self.assertFalse(cef_json_builder.is_valid_version(None))
+    self.assertFalse(cef_json_builder.is_valid_chromium_version('foobar'))
+    # Must be 4 numbers.
+    self.assertFalse(cef_json_builder.is_valid_chromium_version('74.0.3729'))
+    self.assertFalse(cef_json_builder.is_valid_chromium_version('74.0.3729.A'))
+    # 1st number is max 3 digits with no leading 0's.
+    self.assertFalse(
+        cef_json_builder.is_valid_chromium_version('7400.0.3729.6'))
+    self.assertFalse(cef_json_builder.is_valid_chromium_version('04.0.3729.6'))
+    # 2nd number is always 0.
+    self.assertFalse(cef_json_builder.is_valid_chromium_version('74.1.3729.6'))
+    # 3rd number is max 5 digits with no leading 0's.
+    self.assertFalse(cef_json_builder.is_valid_chromium_version('74.0.0372.6'))
+    self.assertFalse(
+        cef_json_builder.is_valid_chromium_version('74.0.372900.6'))
+    # 4rd number is max 3 digits with no leading 0's.
+    self.assertFalse(
+        cef_json_builder.is_valid_chromium_version('74.0.3729.6000'))
+    self.assertFalse(cef_json_builder.is_valid_chromium_version('74.0.3729.06'))
+    pass
+
+  # Write builder contents to string and then read in.
+  def _verify_write_read(self, builder):
+    output = str(builder)
+    builder2 = cef_json_builder()
+    builder2.load(output)
+    self.assertEqual(output, str(builder2))
+
+  # Add a file record for testing purposes.
+  def _add_test_file(self,
+                     builder,
+                     platform='linux32',
+                     version='3.2704.1414.g185cd6c',
+                     type='standard',
+                     attrib_idx=0,
+                     shouldfail=False):
+    name = cef_json_builder.get_file_name(version, platform, type) + '.tar.gz'
+
+    # Some random attribute information. sha1 must be different to trigger replacement.
+    attribs = [{
+        'date_str': '2016-05-18T22:42:15.487Z',
+        'date_val': datetime.datetime(2016, 5, 18, 22, 42, 15, 487000),
+        'sha1': '2d48ee05ea6385c8fe80879c98c5dd505ad4b100',
+        'size': 48395610
+    }, {
+        'date_str': '2016-05-14T22:42:15.487Z',
+        'date_val': datetime.datetime(2016, 5, 14, 22, 42, 15, 487000),
+        'sha1': '2d48ee05ea6385c8fe80879c98c5dd505ad4b200',
+        'size': 48395620
+    }]
+
+    # Populate the Chromium version to avoid queries.
+    chromium_version = '49.0.2705.50'
+    self.assertEqual(chromium_version,
+                     builder.set_chromium_version(version, chromium_version))
+    self.assertEqual(0, builder.get_query_count())
+
+    result = builder.add_file(name, attribs[attrib_idx]['size'],
+                              attribs[attrib_idx]['date_str'],
+                              attribs[attrib_idx]['sha1'])
+    # Failure should be expected when adding the same file multiple times with the same sha1.
+    self.assertEqual(not shouldfail, result)
+
+    # Return the result expected from get_files().
+    return {
+        'chromium_version': chromium_version,
+        'sha1': attribs[attrib_idx]['sha1'],
+        'name': name,
+        'platform': platform,
+        'last_modified': attribs[attrib_idx]['date_val'],
+        'cef_version': version,
+        'type': type,
+        'size': attribs[attrib_idx]['size']
+    }
+
+  # Test with no file contents.
+  def test_empty(self):
+    builder = cef_json_builder()
+
+    self._verify_write_read(builder)
+
+    files = builder.get_files()
+    self.assertEqual(len(files), 0)
+
+  # Test add/get of a single file with the specified type.
+  def _test_add_file(self, type):
+    builder = cef_json_builder()
+
+    expected = self._add_test_file(builder, type=type)
+    self._verify_write_read(builder)
+
+    files = builder.get_files()
+    self.assertEqual(len(files), 1)
+    self.assertEqual(expected, files[0])
+
+  # Test add/get of a standard type file.
+  def test_add_standard_file(self):
+    self._test_add_file('standard')
+
+  # Test add/get of a minimal type file.
+  def test_add_minimal_file(self):
+    self._test_add_file('minimal')
+
+  # Test add/get of a client type file.
+  def test_add_client_file(self):
+    self._test_add_file('client')
+
+  # Test add/get of a debug_symbols type file.
+  def test_add_debug_symbols_file(self):
+    self._test_add_file('debug_symbols')
+
+  # Test add/get of a release_symbols type file.
+  def test_add_release_symbols_file(self):
+    self._test_add_file('release_symbols')
+
+  # Test get_files() behavior with a single file.
+  def test_get_files_single(self):
+    # yapf: disable
+    versions = (
+        ('3.2704.1414.g185cd6c', '3.2704'),
+        ('74.0.1+g62d140e+chromium-74.0.3729.6', '74.0'),
+    )
+    # yapf: enable
+
+    for version, partial_version in versions:
+      builder = cef_json_builder()
+
+      # Specify all values just in case the defaults change.
+      expected = self._add_test_file(
+          builder, platform='linux32', version=version, type='standard')
+
+      # No filter.
+      files = builder.get_files()
+      self.assertEqual(len(files), 1)
+      self.assertEqual(expected, files[0])
+
+      # Platform filter.
+      files = builder.get_files(platform='linux32')
+      self.assertEqual(len(files), 1)
+      self.assertEqual(expected, files[0])
+      files = builder.get_files(platform='linux64')
+      self.assertEqual(len(files), 0)
+
+      # Version filter exact.
+      files = builder.get_files(version=version)
+      self.assertEqual(len(files), 1)
+      self.assertEqual(expected, files[0])
+
+      # Version filter partial.
+      files = builder.get_files(version=partial_version)
+      self.assertEqual(len(files), 1)
+      self.assertEqual(expected, files[0])
+      files = builder.get_files(version='3.2623')
+      self.assertEqual(len(files), 0)
+
+      # Type filter.
+      files = builder.get_files(type='standard')
+      self.assertEqual(len(files), 1)
+      self.assertEqual(expected, files[0])
+      files = builder.get_files(type='client')
+      self.assertEqual(len(files), 0)
+
+      # All filters.
+      files = builder.get_files(
+          platform='linux32', version=partial_version, type='standard')
+      self.assertEqual(len(files), 1)
+      self.assertEqual(expected, files[0])
+      files = builder.get_files(
+          platform='linux32', version=partial_version, type='minimal')
+      self.assertEqual(len(files), 0)
+      files = builder.get_files(
+          platform='linux32', version='3.2623', type='standard')
+      self.assertEqual(len(files), 0)
+      files = builder.get_files(
+          platform='linux64', version=partial_version, type='standard')
+      self.assertEqual(len(files), 0)
+
+  # Test add/get of multiple files.
+  def test_add_multiple_files(self):
+    builder = cef_json_builder()
+
+    expected = []
+
+    platforms = cef_json_builder.get_platforms()
+    versions = [
+        # Old-style version numbers.
+        '3.2704.1414.g185cd6c',
+        '3.2704.1400.gde36543',
+        # New-style version numbers.
+        '74.0.1+g62d140e+chromium-74.0.3729.6'
+    ]
+    types = cef_json_builder.get_distrib_types()
+    for platform in platforms:
+      for version in versions:
+        for type in types:
+          expected.append(
+              self._add_test_file(
+                  builder, platform=platform, type=type, version=version))
+
+    self._verify_write_read(builder)
+
+    # No filter.
+    files = builder.get_files()
+    self.assertEqual(len(files), len(platforms) * len(versions) * len(types))
+
+    # Version filter.
+    for version in versions:
+      files = builder.get_files(version=version)
+      self.assertEqual(len(files), len(platforms) * len(types))
+
+    # Type filter.
+    files = builder.get_files(type='client')
+    self.assertEqual(len(files), len(platforms) * len(versions))
+
+    # Platform filter.
+    files = builder.get_files(platform='windows32')
+    self.assertEqual(len(files), len(types) * len(versions))
+
+    # All filters.
+    idx = 0
+    for platform in platforms:
+      for version in versions:
+        for type in types:
+          files = builder.get_files(
+              platform=platform, type=type, version=version)
+          self.assertEqual(len(files), 1)
+          self.assertEqual(expected[idx], files[0])
+          idx += 1
+
+  # Test add/get/replace of multiple files.
+  def test_replace_all_files(self):
+    versions = ['3.2704.1414.g185cd6c', '74.0.1+g62d140e+chromium-74.0.3729.6']
+    platforms = ['linux32', 'linux64']
+    types = ['standard', 'minimal']
+
+    for version in versions:
+      builder = cef_json_builder()
+
+      # Initial file versions.
+      for platform in platforms:
+        for type in types:
+          self._add_test_file(
+              builder, platform=platform, type=type, version=version)
+
+      # No filter.
+      files = builder.get_files()
+      self.assertEqual(len(files), len(platforms) * len(types))
+
+      expected = []
+
+      # Replace all file versions (due to new sha1).
+      for platform in platforms:
+        for type in types:
+          expected.append(
+              self._add_test_file(
+                  builder,
+                  platform=platform,
+                  type=type,
+                  version=version,
+                  attrib_idx=1))
+
+      # No filter.
+      files = builder.get_files()
+      self.assertEqual(len(files), len(platforms) * len(types))
+
+      # All filters.
+      idx = 0
+      for platform in platforms:
+        for type in types:
+          files = builder.get_files(
+              platform=platform, type=type, version=version)
+          self.assertEqual(len(files), 1)
+          self.assertEqual(expected[idx], files[0])
+          idx += 1
+
+  # Test add/get/no replace of multiple files.
+  def test_replace_no_files(self):
+    versions = ['3.2704.1414.g185cd6c', '74.0.1+g62d140e+chromium-74.0.3729.6']
+    platforms = ['linux32', 'linux64']
+    types = ['standard', 'minimal']
+
+    for version in versions:
+      builder = cef_json_builder()
+
+      # Initial file versions.
+      for platform in platforms:
+        for type in types:
+          self._add_test_file(
+              builder, platform=platform, type=type, version=version)
+
+      # No filter.
+      files = builder.get_files()
+      self.assertEqual(len(files), len(platforms) * len(types))
+
+      expected = []
+
+      # Replace no file versions (due to same sha1).
+      for platform in platforms:
+        for type in types:
+          expected.append(
+              self._add_test_file(
+                  builder,
+                  platform=platform,
+                  type=type,
+                  version=version,
+                  shouldfail=True))
+
+      # No filter.
+      files = builder.get_files()
+      self.assertEqual(len(files), len(platforms) * len(types))
+
+      # All filters.
+      idx = 0
+      for platform in platforms:
+        for type in types:
+          files = builder.get_files(
+              platform=platform, type=type, version=version)
+          self.assertEqual(len(files), 1)
+          self.assertEqual(expected[idx], files[0])
+          idx += 1
+
+  # Test Chromium version.
+  def test_chromium_version(self):
+    builder = cef_json_builder()
+
+    # For old-style CEF version numbers the Git hashes must exist but the rest
+    # of the CEF version can be fake.
+    # yapf: disable
+    versions = (
+        # Old-style version numbers.
+        ('3.2704.1414.g185cd6c', '51.0.2704.47'),
+        ('3.2623.9999.gb90a3be', '49.0.2623.110'),
+        ('3.2623.9999.g2a6491b', '49.0.2623.87'),
+        ('3.9999.9999.gab2636b', 'master'),
+        # New-style version numbers.
+        ('74.0.1+g62d140e+chromium-74.0.3729.6', '74.0.3729.6'),
+        ('74.0.0-master.1920+g725ed88+chromium-74.0.3729.0', '74.0.3729.0'),
+        ('74.0.0-my_branch.1920+g725ed88+chromium-74.0.3729.0', '74.0.3729.0'),
+    )
+    # yapf: enable
+
+    # Test with no query.
+    for (cef, chromium) in versions:
+      self.assertFalse(builder.has_chromium_version(cef))
+      # Valid version specified, so no query sent.
+      self.assertEqual(chromium, builder.set_chromium_version(cef, chromium))
+      self.assertEqual(chromium, builder.get_chromium_version(cef))
+      self.assertTrue(builder.has_chromium_version(cef))
+      # Ignore attempts to set invalid version after setting valid version.
+      self.assertEqual(chromium, builder.set_chromium_version(cef, None))
+
+    self.assertEqual(0, builder.get_query_count())
+    builder.clear()
+
+    # Test with query.
+    for (cef, chromium) in versions:
+      self.assertFalse(builder.has_chromium_version(cef))
+      # No version known, so query sent for old-style version numbers.
+      self.assertEqual(chromium, builder.get_chromium_version(cef))
+      self.assertTrue(builder.has_chromium_version(cef))
+
+    # Only old-style version numbers.
+    self.assertEqual(4, builder.get_query_count())
+
+
+# Program entry point.
+if __name__ == '__main__':
+  unittest.main()
diff --git a/src/tools/cefbuilds/index.html.in b/src/tools/cefbuilds/index.html.in
new file mode 100644
index 0000000..9b0adec
--- /dev/null
+++ b/src/tools/cefbuilds/index.html.in
@@ -0,0 +1,178 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us">
+<head>
+  <title>CEF Automated Builds</title>
+  <meta name="author" content="The Chromium Embedded Framework Authors" />
+  <meta name="copyright" content="$year$ The Chromium Embedded Framework Authors. All rights reserved." />
+  <style type="text/css">
+    body {font-family: Verdana,Arial; font-size: 10pt}
+    table.top {border-spacing: 0; border: 0px; padding: 0px}
+    table.top td.logo {padding-right: 10px}
+    table.top td.header {font-size: 14pt; white-space: nowrap; padding-right: 10px}
+    table.top td.header .links {font-size: 10pt}
+    table.top td.branding {font-size: 8pt; white-space: nowrap; text-align: center}
+    table.top td.filters {font-size: 10pt}
+    table.list {border-spacing: 0; border: 0px; margin-left: 20px; font-size: 10pt}
+    table.list tr.topheader td {border-bottom: 1px solid black; padding: 5px; padding-top: 4px; font-size: 9pt}
+    table.list tr.toprow:nth-child(even) {color: #000; background: #FFF}
+    table.list tr.toprow:nth-child(odd) {color: #000; background: #DDD}
+    table.version {border-spacing: 0; border: 0px; padding: 4px 4px; font-size: 10pt}
+    table.version td.header {font-weight: bold; font-size: 9pt}
+    table.version td.links {font-size: 8pt}
+    table.files {border-spacing: 0; border: 0px; font-size: 10pt}
+    table.files td {padding-top: 5px; padding-left: 20px; font-size: 10pt; white-space: nowrap}
+    table.files td.first {padding-left: 0px;}
+    .text_header {font-size: 12pt; font-weight: bold}
+    .bullet {font-size: 8pt; vertical-align: top}
+    .footer {font-size: 8pt; text-align: center}
+    .tooltip {position: relative; display: inline-block; border-bottom: 1px dotted black}
+    .tooltip .tooltiptext {visibility: hidden; width: 500px; background-color: black; color: #FFF; text-align: left; border-radius: 6px; padding: 5px; position: absolute; z-index: 1; top: -5px; left: 105%}
+    .tooltip:hover .tooltiptext {visibility: visible;}
+    .tooltiptext {white-space: normal}
+  </style>
+  <script>
+    var version_filter = ''
+
+    // Show more rows and change related UI.
+    function show(platform, showmore) {
+      var more = document.getElementById(platform).getElementsByClassName('showmore')[0]
+      var less = document.getElementById(platform).getElementsByClassName('showless')[0]
+      var notes = document.getElementById(platform).getElementsByClassName('shownotes')[0]
+
+      var rows = document.getElementById(platform).getElementsByClassName('toprow')
+
+      // Number of rows we would like to display.
+      var desired_ct = showmore ? rows.length : 1
+      // Number of rows allowed by the version filter.
+      var allowed_ct = 0
+      // Number of rows actually displayed.
+      var displayed_ct = 0
+
+      for (var i = 0; i < rows.length; ++i) {
+        var version = rows[i].getAttribute('data-version')
+        var allow = (version_filter == '' || version.indexOf(version_filter) == 0)
+        var display = (allow && displayed_ct < desired_ct)
+        rows[i].style.display = (display ? '' : 'none')
+        rows[i].style.background = (displayed_ct % 2 == 0 ? '#FFF' : '#DDD')
+        allowed_ct += allow
+        displayed_ct += display
+      }
+
+      if (allowed_ct <= 1) {
+        // One or less rows. No need for controls.
+        more.style.display = 'none'
+        less.style.display = 'none'
+      } else if (showmore) {
+        more.style.display = 'none'
+        less.style.display = ''
+      } else {
+        less.style.display = 'none'
+        more.style.display = ''
+      }
+
+      var notes_str = ''
+      if (allowed_ct != rows.length) {
+        var hidden_ct = rows.length - allowed_ct
+        notes_str += '(' + hidden_ct + ' build' + (hidden_ct == 1 ? '' : 's') + ' hidden, <a href="#" onclick="resetVersionFilter(); return false;">reset filter</a> to restore)'
+      }
+      notes.innerHTML = notes_str
+      notes.style.display = (notes_str == '' ? 'none' : '')
+    }
+
+    // Shrink all lists on page load.
+    function shrinkAll() {
+      var platforms = document.getElementsByClassName('list')
+      for (var i = 0; i < platforms.length; ++i)
+        show(platforms[i].id, false)
+    }
+
+    // Only show rows that start with the version string.
+    function applyVersionFilter() {
+      version_filter = document.getElementById('version_filter').value
+      shrinkAll()
+    }
+    function resetVersionFilter() {
+      version_filter = ''
+      document.getElementById('version_filter').value = ''
+      shrinkAll()
+    }
+
+    function onLoad() {
+      document.getElementById('version_filter_div').style.display = ''
+      shrinkAll()
+    }
+  </script>
+</head>
+<body onload="onLoad()">
+  <table class="top">
+    <tr>
+      <td class="logo"><img width="66" height="50" alt="CEF Logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAAAyCAIAAABnFJp+AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAABA0SURBVHjaYvz//z/D0AcAAcRCmfb/P3///fr997efv7///PPz9z9WZkZeLnZ+HnYONhZmJka6eQMggMjxxqsP3248fvPw5cdnb768/vjty4/ff//+//P3359//5gZGFlYmDjZWPi52cUEuWRE+DTkhFWlBVlZmGnqDYAAYiQ+UQFV3nv+4eCFh0euPv3689e/fwyszMzAMGcChjojEEDV/P8HhP///vv3G+izf/9ZWZgUJQR8LFQNVMS5OVhp5A2AACLWG1cfvl598PrtJ++AKYeNjZkV6HYkwMzwn4GR4R8DI9BvEL+AvQMm//3/9ecPkC0uwONlpuxtoQL1MVUBQAAR9sb7z9/XHr5x4OIjYB7g5GBhYgR5ABoBcAAxCeSN///AqQuUxv6CPAL0FFAtUBbIA8qaakhFOGoDkxl1vQEQQAS8cfPx20nrTz94+ZGHHZiCmKAOZmRAiQuw4K/ff4Hu5mRn5uFkF+bjFOBmA+ZyYD758/sfsAD48OXHqw9fP3z5CSRF+DkLgs2stGWp6A2AAMLnjd1n78/bceHbj98c7CzA8GSC+gHkbEY4G+iBP3+B+UNTTkRPURSYoaWEgR5hRSumgDHx4+ffZ+8+33j45tSN5+fvvvAxV0n0NGCjUtYHCCCc3th07ObUDWeADmJjZYImIFA0MDBCIJgPzMJ//zEoSPAH26qba0iBJIkD5++8mLLhtLGaZJafCVW8ARBA2L2x6sDV2VvPs7Iys7MwszAxg7MCKBODPAR2KzBmfv75A8wjiR56biZKLExMpFoMzCq7Tt8V4OWw1JKh3BsAAYTFG6duPK2YvRcoDExLwOIUiICuZAJHCTgmgAwmYGUnI8qT6GFgoiZJft35nwGYnYAlMuXeAAggdG88fPEho2/T5x9/2dlYgM4GeoAZFBmg7M0MyddMoMwgKchXEm6hIiXIMDgAQACh1+J7Lr++9/idmAgvsCYAlo+/gYn/PyhN/WNm/PsPFCe/f/wT4eeqi7OREuYFBijx+YGmACCAULxx6/nX2++ZdHU0bt+4wS/Ix8bGCvTJnz//GRn/gP3wDwiATabcIBOwHxggfgDG56NXHy/ffwUsl998+Pbj1x9gBAOTCh8Xm7gAt5aimL6yOLBEuvXk7dErj4FxC05PcM//h5QaYCHG33/+KUsJ2OsrQDLP1hO33336zoRc6EEt/A8s/r0t1IBNHogwQACxIJWJ/zecefnt5y81NaXnT5++f/tRSFiAhZUFXBcz/P377+9fhs/ff6V4G5mqS8N1Xbj7Yv6OC9cfvv7yHdiy+gevRkAGArX8/w+0aXaJj5Kk0JUHr/vXnOBgxd2KY2T4+uM30HEwb/xbuufSrcdvgSUNA2r+/Q+qpv4AyWhnXYgIQACxIEXFt6fvf7CzMbOxsenpaR87cvLTx898grzAsgqSf4CNWWCp4mepDlEPjKCNR2/O3XH+45efPJxsAjwcIKf/A9XWoLbUP1CMAHORvDi/rKgAUApYlfBwsLGzskC8Ca1HoQ0xBrANoDjiYEXUJFwcrECT0ZqVYIX/f7EwrT10zcNUWZiPCygAEECIUuLIzXdAD4KKzn//FBRkFBRkf/z4+eXTN2BCAgYwMK6A6STeXZ+THerzpXsu96w+AXSwKD83N7B6AVnGCKy5+bjZgV7i4mQDOvLj1x/K0kLIZRGoBcDEyMrKxAKq44HNYWYQBJYeQGvBBRcwFJBTECjNAdWzMLEBkxEIgIocRnB58+jlZ2D9A1EJEEBQN91/9e3yo88crOAWE7BsZfivraP28NHjXz9/AeOEX4AP2FzVUhA1UJGAqN9w9MbiPZeAjQ5gIgE66/uvP7ycbIGmysaqEkJ8XMAQ//bjz+sPX0/ffKYqI4TsB2DzBBi6wLB3N1G20Jb59esvOIChTUmgLRJCPMhhzwpu9gO1SonwRjlpA338D9bo/P37rxKsqAQIIKg3br34+vPfP3YmcLiAwv6fqLiQvoH2mdOX/vz+8+njF2BSAyZZSBPj7cdvK/ZfBYYOyA/MoHxpoCyR4K6njFz+8jMAk5OJutR/pKAF+gGYqIDx9vPPX2UZQXDFj7f8YWZmZ2UFFv1//v4HxrCtrhzQfVhVAgQQVPTB6+/A6gFU2QEjA9wIB3pXS1uFn58X2CX69u0nNxuTh6kKRPGhy4/efvrOzcEGVA2UlRDiLQo1V8ZRh8BLGWDwANMRKG2wMDIxEdVYBzUjWEFdGlDwMTLgacMCBBDIGz9+/X367gewCGGCpCjG/5D2EhcXh4am0t8/wO7CXyVxfmDLFKj4+6/f207eBsUDC7SpFeGoJQjO3/gBKEuAWgWgGpQZknbxV8zgUoGRGVzlgrXgqaIAAgiUqB6++f7h2x9gExDc8oM2wsFl0R91TcX7957ce/hMXkIAmouef3jx7iuw+ADmVWBUKEkKWhHXKAK6A+ILoHOAqeXp689X7r8Gln6QvAGqbcFllqYcsOxhh0cgOD+DqixQXwCYkVgZ4M2O/+DMxgpKZowAAQTyxp2XwAz5j4OdGdaQBfkG6CdgN4ebh8PKzuDB0peSwnwQzU/ffgaWTkzgoAFWFMDmLfGNIkZYWxlo1+HLjw9feQzpbf0H9bWA9RKoem1IsNdREIWVCJA+ASMw1F5/+Naw6DA4ZhjhnWQhXo6KSEtg/gEIIJA3Pnz7jT05MzD8/vNHQkpESVmaDeZWYPfjH6zEBzZ6gTmE6OYboryCpDFwJgSVs8BqEuwfaF8A0yFAhcC2xKuP3xnAYQfsXQL9DWwugNMOyGEAAQTyBrD1wATrTEBLaxBghLCAIhY2esLC0NT/H+iJf+Q3aSHNEEZQiv3/9/cfUHIBRgU4IoBZENzPRc/J0OoS1lcDuhzsChBHXJAbUjkCBBALpPXCwIDkCQYGRK//PyOwbyoswi8jDyuhmYElwD9QyIF9/vnbT6I9wQCNQgam33//6SmLqUoJ/f4LqZTBwyngrruoABdyhf0f6nlQCgfbCanzQT18YEtHRhSa1AECCOQNFibG/xDlUBWIOIV4Chjpf2CCQnyczODi+B8o1zI+ef0ZWPWyMBOTPUANlH/gcQdgvWGoLA5sShDhd1DCAHbR5ET5Mn2NgfkQHF0Qo/6L8EH9DBBAIG8AK29I+w9aaECiHymVAjV8+fUHwpYR4QPWLkCPMYF6IYz3n787feOpJRHjA//+gRqL0NAEpx4itEDHvf7+ATZpmeTF+XBVfwABBBIV52dnZISWerDGFwN0aAYcHMCcCCyRIRrkxPmtdKQ///gFGZL68+//4j1XXrz/QrjX+v8fsKUILKOBjL+g0PxPMB5AA3Z/oUnuD94hHIAAAnlDRRzYkGP+C26VgsIMpIERlilB6Q2YiV59+Q0slCGCnqYqwCLhNyhLMgAjBFgEd688ef/FBxyuQSQpYFr69Qfolf/gookwAHrjB9Cav//BRTG+ggUggECJSkKQXZSX7dWnn9BxPnCkAyse8PgZuFfDyPDh++8br74aSIF6S8AGIrA5tO/CQ2DTENTsYWa++/Rt9dz92goihioSylJCwE78p68/7z5/e/r6c0cDBRdjJYh//v4BlZLA4AKWHKdvPAOW3b9BQQO28T+4hQ8eofO2VJUUAlkE9MHPX7+BhQqooviDzxsAAcQC6QnICXM8ef+DAzRWCW1tAgvk/4yQMU1IYcJw89U3iDeAoCDE4v2XH2dvPxfi4QQNnjAxfPv5++iVJ8BKDdQxYgRVL1+///zw+QewxQrxBqREAo+D/gcWCUeuPDpw8SGkSPwHrvvAA40guwzVJCHe+A/u1fz8CYp3YLTg8QZAAEFzjJI4F7jz/Q8y6gopB+ClFihdMTI8//Tzxedf0PKKl7MqytZETerD1x+fvv349hPYDv4LKdy+/vj1GdiL+/MX2Jjl4+Z4/u4LWvICjer+BvUMgQUd0FgwArYambnYgZ0kUD+JGd7cYoQWMMBIA2rBkzkAAgjaUNeW4eHnYvkBmqD4D8keTH8hJS4jRDMwZf36/f/Eww8BOmIQLZLCPP1ZbsAO4KKdl159+ApMvozgBjLEvWBD/gGbTE9efwR6CVhJ/fr779P3Xxx//uFp20Lqvr+QbPCf4ev335+//QI17H//AfbxGXCXCgABBPWGABero5bQxrOv2Fn/Mf8DNr/BI2z/GJkgdS44zwOD7caLb5eFv+hK8kAaZsCWXpCtpr2Bwtlbz+48eQcMeKCtP3/9AeZ7YKDy83BICfMaKItDKlpteeGcABNgBwUSKv9RBgmQ67v/kuDxCqAhYQ5awB4B0BZg0gJWi8y4B/UAAggxTvX+6++OTXd//vnPyQHq2YB6mMDQZWGEegXUemcEJmxBLuZwI3F+ThZcxeRfUA3/n4mRCoNoxAOAAEJYBmyeuOuJgvLZH9DYOGgoBJTzIF1GWMnLyPj2y991F15//vEXV/sP3H+kqx+AACCAUOxz0hbWkuH59vMveDjnPzDT/gOX8f//wT0Dark8/fBr06U3X37+pYoLwJUJpUYBBBB6sLnrigKD8ycoQkCl+G8wAnX/QKU6KMUAsyE7E+P919+Xn3z56vNvCq3/8fvv9effGCmOPYAAQtevLM4VYioBLJSABdxvsGf+/PkPakH8BZV6v8FFOxABtT15/3PRkWcXH38m2+6XH3/tvf6ek5WZlZnSEVSAAMI+MbDo8LMTd94BS3BgIcMMnqRkgk2UQZsp/yBtnv+///3Tk+Fx0RES42Mn3i3ff/299fLbxcdfBLlYvPRFmCmeDQQIIOzeAAotO/ps//V3wFY5ZK6VCdwtRu4Z/gN3AYA15s9foMF9OUEOYyU+EyV+Ntx9WqBVwCz18O3Ppx9+AKNCnI/dTUeYk5UK5QFAAOGcbQJWhVvPv9p//S0wq4BGKEC9cyZ4L/M/uKr6D67jgJUVMIt+/wkqCsKsJDWkeIBphA0yFAhuyoCbEv+AhRuwmfzmy++vP/9++/VXnI/NTk1QgIuFKuUEQAARmMI8evP98mPPfv//z8XKzIjU0QV1nf/+h/QegNkG2NQDlgrSwhw+RqL/wVU4tKf3n+H3X1BZ9OMPuEHx9983kB/+a0lz26kKUHEZA0AAEZ5Qvv/q2+Zzr64++QKMC9A4MiOshw5qOTBC+lvAWoWZmcHDQERaiB3YhAPFALAVCOpeAxvn/4GuB3a6gIUSMB54OJmN5fm0JLmoOzECEEDETu8fuPZ296U3rz79YgANDcFzOhQAW9/ACsdWU+jb739/QL08UNYHVvnA5tyv36AU9Q28nERRhNNKhZ+LjfoLLwACiITFFkDXXHnyad/Vd3defAXP3TBAhiVBtTvTf1ttEX5O1l9/QGkN4oGfv4FNQ1AHiYudWUeaW02cC2cThmIAEECMpC5EAjr0wZtvQJ88evP9xcdf7z6DsqySBLeSBBfID+CeLbCjw87CxM3ODMzBEvzsUgJstIgBZAAQQIyUrKcC1oMvP/589uEnJ3jE8S9oMQwDJxsTMNR52YGdKbTRMxoCgABiHB7LwgACDACHKi6GTvcogQAAAABJRU5ErkJggg=="></td>
+      <td class="header" width="100%">
+        Chromium Embedded Framework (CEF) Automated Builds
+        <br/><span class="links">[
+          <a href="https://bitbucket.org/chromiumembedded/cef/">Project Page</a> |
+          <a href="http://www.magpcss.org/ceforum">Support Forum</a>
+        ]</span></td>
+      <td class="branding" rowspan="2" valign="top">
+        $branding$
+      </td>
+    </tr>
+    <tr>
+      <td class="filters" colspan="2">
+        <br/>
+        Builds:
+<section:platform_link>
+        <span class="bullet">&#9654;</span> <a href="#$platform$_builds">$platform_name$</a>
+</section:platform_link>
+        <br/><br/>
+        <div id="version_filter_div" style="display:none">
+          <div class="tooltip">Version Filter<span class="tooltiptext">Optionally filter builds by version number. The filter supports partial matching. For example, enter "3.2704" to only show builds for the 2704 branch.</span></div>:
+          <input type="text" size="20" id="version_filter"/><input type="button" value="Apply" onclick="applyVersionFilter();"/>
+        </div>
+      </td>
+    </tr>
+  </table>
+  <br/><br/>
+<section:platform>
+  <a name="$platform$_builds"></a>
+  <span class="text_header">$platform_name$ Builds</span>
+  <table id="$platform$" class="list" width="825">
+    <tr class="topheader">
+      <td>
+        <span class="showmore" style="display:none"><a href="#" onclick="show('$platform$', true); return false;">Show more builds</a></span>
+        <span class="showless" style="display:none"><a href="#" onclick="show('$platform$', false); return false;">Show less builds</a></span>
+        <span class="shownotes" style="display:none"></span>
+      </td>
+    </tr>
+<section:version>
+    <tr class="toprow" data-version="$cef_version$">
+      <td width="100%">
+        <table class="version" width="100%">
+          <tr>
+            <td class="header">$last_modified$ - CEF $cef_version$ / Chromium $chromium_version$</td>
+            <td class="links" align="right"><a href="$cef_source_url$">CEF source</a> | <a href="$chromium_source_url$">Chromium source</a></td>
+          </tr>
+          <tr>
+            <td colspan="2">
+              <table class="files" width="100%">
+<section:file>
+                <tr>
+                  <td class="first"><span class="bullet">&#9654;</span> <div class="tooltip">$type_name$<span class="tooltiptext">$tooltip_text$</span></div></td>
+                  <td width="100%"><a href="$file_url$">$name$</a></td>
+                  <td align="right">$size$</td>
+                  <td><a href="$sha1_url$">sha1</a></td>
+                </tr>
+</section:file>
+              </table>
+            </td>
+          </tr>
+        </table>
+      </td>
+    </tr>
+</section:version>
+  </table>
+  <br/>
+</section:platform>
+  <p class="footer">Copyright &copy; $year$ The Chromium Embedded Framework Authors. All rights reserved.</p>
+</body>
+</html>
diff --git a/src/tools/clang_util.py b/src/tools/clang_util.py
new file mode 100644
index 0000000..4835472
--- /dev/null
+++ b/src/tools/clang_util.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file
+
+from __future__ import absolute_import
+from __future__ import print_function
+from exec_util import exec_cmd
+import os
+import sys
+
+# Script directory.
+script_dir = os.path.dirname(__file__)
+root_dir = os.path.join(script_dir, os.pardir)
+
+if sys.platform == 'win32':
+  # Force use of the clang-format version bundled with depot_tools.
+  clang_format_exe = 'clang-format.bat'
+else:
+  clang_format_exe = 'clang-format'
+
+
+def clang_format(file_name, file_contents):
+  # -assume-filename is necessary to find the .clang-format file and determine
+  # the language when specifying contents via stdin.
+  result = exec_cmd("%s -assume-filename=%s" % (clang_format_exe, file_name), \
+                    root_dir, file_contents.encode('utf-8'))
+  if result['err'] != '':
+    print("clang-format error: %s" % result['err'])
+  if result['out'] != '':
+    output = result['out']
+    if sys.platform == 'win32':
+      # Convert to Unix line endings.
+      output = output.replace("\r", "")
+    return output
+  return None
diff --git a/src/tools/combine_libs.py b/src/tools/combine_libs.py
new file mode 100644
index 0000000..ec3fb6c
--- /dev/null
+++ b/src/tools/combine_libs.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(slightlyoff): move to using shared version of this script.
+'''This script makes it easy to combine libs and object files to a new lib,
+optionally removing some of the object files in the input libs by regular
+expression matching.
+For usage information, run the script with a --help argument.
+'''
+from __future__ import absolute_import
+from __future__ import print_function
+import optparse
+import os
+import re
+import subprocess
+import sys
+
+
+def Shell(*args):
+  '''Runs the program and args in args, returns the output from the program.'''
+  process = subprocess.Popen(
+      args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  output = process.stdout.readlines()
+  process.wait()
+  retcode = process.returncode
+  if retcode != 0:
+    raise RuntimeError('%s exited with status %d' % (args[0], retcode))
+  return output
+
+
+def CollectRemovals(remove_re, inputs):
+  '''Returns a list of all object files in inputs that match remove_re.'''
+  removals = []
+  for input in inputs:
+    output = Shell('lib.exe', '/list', input)
+
+    for line in output:
+      line = line.rstrip()
+      if remove_re.search(line):
+        removals.append(line)
+
+  return removals
+
+
+def CombineLibraries(output, remove_re, inputs):
+  '''Combines all the libraries and objects in inputs, while removing any
+  object files that match remove_re.
+  '''
+  removals = []
+  if remove_re:
+    removals = CollectRemovals(remove_re, inputs)
+
+  if len(removals) > 0:
+    print('Removals: ', removals)
+
+  args = ['lib.exe', '/out:%s' % output]
+  args += ['/remove:%s' % obj for obj in removals]
+  args += inputs
+  Shell(*args)
+
+
+USAGE = '''usage: %prog [options] <lib or obj>+
+
+Combines input libraries or objects into an output library, while removing
+any object file (in the input libraries) that matches a given regular
+expression.
+'''
+
+
+def GetOptionParser():
+  parser = optparse.OptionParser(USAGE)
+  parser.add_option(
+      '-o', '--output', dest='output', help='write to this output library')
+  parser.add_option(
+      '-r',
+      '--remove',
+      dest='remove',
+      help='object files matching this regexp will be removed '
+      'from the output library')
+  return parser
+
+
+def Main():
+  '''Main function for this script'''
+  parser = GetOptionParser()
+  (opt, args) = parser.parse_args()
+  output = opt.output
+  remove = opt.remove
+  if not output:
+    parser.error('You must specify an output file')
+
+  if not args:
+    parser.error('You must specify at least one object or library')
+
+  output = output.strip()
+  if remove:
+    remove = remove.strip()
+
+  if remove:
+    try:
+      remove_re = re.compile(opt.remove)
+    except:
+      parser.error('%s is not a valid regular expression' % opt.remove)
+  else:
+    remove_re = None
+
+  if sys.platform != 'win32' and sys.platform != 'cygwin':
+    parser.error('this script only works on Windows for now')
+
+  # If this is set, we can't capture lib.exe's output.
+  if 'VS_UNICODE_OUTPUT' in os.environ:
+    del os.environ['VS_UNICODE_OUTPUT']
+
+  CombineLibraries(output, remove_re, args)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(Main())
diff --git a/src/tools/compile_ib_files.py b/src/tools/compile_ib_files.py
new file mode 100644
index 0000000..eb4980a
--- /dev/null
+++ b/src/tools/compile_ib_files.py
@@ -0,0 +1,56 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from __future__ import print_function
+from __future__ import absolute_import
+import argparse
+import logging
+import os
+import re
+import subprocess
+import sys
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description='A script to compile xib and storyboard.',
+      fromfile_prefix_chars='@')
+  parser.add_argument(
+      '-o', '--output', required=True, help='Path to output bundle.')
+  parser.add_argument(
+      '-i', '--input', required=True, help='Path to input xib or storyboard.')
+  parser.add_argument('--developer_dir', required=False, help='Path to Xcode.')
+  args, unknown_args = parser.parse_known_args()
+  if args.developer_dir:
+    os.environ['DEVELOPER_DIR'] = args.developer_dir
+  ibtool_args = [
+      'xcrun', 'ibtool', '--errors', '--warnings', '--notices',
+      '--output-format', 'human-readable-text'
+  ]
+  ibtool_args += unknown_args
+  ibtool_args += [
+      '--compile',
+      os.path.abspath(args.output),
+      os.path.abspath(args.input)
+  ]
+  ibtool_section_re = re.compile(r'/\*.*\*/')
+  ibtool_re = re.compile(r'.*note:.*is clipping its content')
+  try:
+    stdout = subprocess.check_output(ibtool_args)
+  except subprocess.CalledProcessError as e:
+    print(e.output)
+    raise
+  current_section_header = None
+  for line in stdout.splitlines():
+    if ibtool_section_re.match(line):
+      current_section_header = line
+    elif not ibtool_re.match(line):
+      if current_section_header:
+        print(current_section_header)
+        current_section_header = None
+      print(line)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/tools/crash_server.py b/src/tools/crash_server.py
new file mode 100644
index 0000000..e10273e
--- /dev/null
+++ b/src/tools/crash_server.py
@@ -0,0 +1,340 @@
+#!/usr/bin/env python

+# Copyright 2017 The Chromium Embedded Framework Authors. All rights reserved.

+# Use of this source code is governed by a BSD-style license that can be found

+# in the LICENSE file.

+"""

+This script implements a simple HTTP server for receiving crash report uploads

+from a Breakpad/Crashpad client (any CEF-based application). This script is

+intended for testing purposes only. An HTTPS server and a system such as Socorro

+(https://wiki.mozilla.org/Socorro) should be used when uploading crash reports

+from production applications.

+

+Usage of this script is as follows:

+

+1. Run this script from the command-line. The first argument is the server port

+   number and the second argument is the directory where uploaded report

+   information will be saved:

+

+   > python crash_server.py 8080 /path/to/dumps

+

+2. Create a "crash_reporter.cfg" file at the required platform-specific

+   location. On Windows and Linux this file must be placed next to the main

+   application executable. On macOS this file must be placed in the top-level

+   app bundle Resources directory (e.g. "<appname>.app/Contents/Resources"). At

+   a minimum it must contain a "ServerURL=http://localhost:8080" line under the

+   "[Config]" section (make sure the port number matches the value specified in

+   step 1). See comments in include/cef_crash_util.h for a complete

+   specification of this file.

+

+   Example file contents:

+

+   [Config]

+   ServerURL=http://localhost:8080

+   # Disable rate limiting so that all crashes are uploaded.

+   RateLimitEnabled=false

+   MaxUploadsPerDay=0

+

+   [CrashKeys]

+   # The cefclient sample application sets these values (see step 5 below).

+   testkey_small1=small

+   testkey_small2=small

+   testkey_medium1=medium

+   testkey_medium2=medium

+   testkey_large1=large

+   testkey_large2=large

+

+3. Load one of the following URLs in the CEF-based application to cause a crash:

+

+   Main (browser) process crash:   chrome://inducebrowsercrashforrealz

+   Renderer process crash:         chrome://crash

+   GPU process crash:              chrome://gpucrash

+

+4. When this script successfully receives a crash report upload you will see

+   console output like the following:

+

+   01/10/2017 12:31:23: Dump <id>

+

+   The "<id>" value is a 16 digit hexadecimal string that uniquely identifies

+   the dump. Crash dumps and metadata (product state, command-line flags, crash

+   keys, etc.) will be written to the "<id>.dmp" and "<id>.json" files

+   underneath the directory specified in step 1.

+

+   On Linux Breakpad uses the wget utility to upload crash dumps, so make sure

+   that utility is installed. If the crash is handled correctly then you should

+   see console output like the following when the client uploads a crash dump:

+

+   --2017-01-10 12:31:22--  http://localhost:8080/

+   Resolving localhost (localhost)... 127.0.0.1

+   Connecting to localhost (localhost)|127.0.0.1|:8080... connected.

+   HTTP request sent, awaiting response... 200 OK

+   Length: unspecified [text/html]

+   Saving to: '/dev/fd/3'

+   Crash dump id: <id>

+

+   On macOS when uploading a crash report to this script over HTTP you may

+   receive an error like the following:

+

+   "Transport security has blocked a cleartext HTTP (http://) resource load

+   since it is insecure. Temporary exceptions can be configured via your app's

+   Info.plist file."

+

+   You can work around this error by adding the following key to the Helper app

+   Info.plist file (e.g. "<appname>.app/Contents/Frameworks/

+   <appname> Helper.app/Contents/Info.plist"):

+

+   <key>NSAppTransportSecurity</key>

+   <dict>

+     <!--Allow all connections (for testing only!)-->

+     <key>NSAllowsArbitraryLoads</key>

+     <true/>

+   </dict>

+

+5. The cefclient sample application sets test crash key values in the browser

+   and renderer processes. To work properly these values must also be defined

+   in the "[CrashKeys]" section of "crash_reporter.cfg" as shown above.

+

+   In tests/cefclient/browser/client_browser.cc (browser process):

+

+   CefSetCrashKeyValue("testkey1", "value1_browser");

+   CefSetCrashKeyValue("testkey2", "value2_browser");

+   CefSetCrashKeyValue("testkey3", "value3_browser");

+

+   In tests/cefclient/renderer/client_renderer.cc (renderer process):

+

+   CefSetCrashKeyValue("testkey1", "value1_renderer");

+   CefSetCrashKeyValue("testkey2", "value2_renderer");

+   CefSetCrashKeyValue("testkey3", "value3_renderer");

+

+   When crashing the browser or renderer processes with cefclient you should

+   verify that the test crash key values are included in the metadata

+   ("<id>.json") file. Some values may be chunked as described in

+   include/cef_crash_util.h.

+"""

+

+from __future__ import absolute_import

+from __future__ import print_function

+import cgi

+import datetime

+import json

+import os

+import shutil

+import sys

+import uuid

+import zlib

+

+is_python2 = sys.version_info.major == 2

+

+if is_python2:

+  from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

+  from cStringIO import StringIO as BytesIO

+else:

+  from http.server import BaseHTTPRequestHandler, HTTPServer

+  from io import BytesIO, open

+

+

+def print_msg(msg):

+  """ Write |msg| to stdout and flush. """

+  timestr = datetime.datetime.now().strftime("%m/%d/%Y %H:%M:%S")

+  sys.stdout.write("%s: %s\n" % (timestr, msg))

+  sys.stdout.flush()

+

+

+# Key identifying the minidump file.

+minidump_key = 'upload_file_minidump'

+

+

+class CrashHTTPRequestHandler(BaseHTTPRequestHandler):

+

+  def __init__(self, dump_directory, *args):

+    self._dump_directory = dump_directory

+    BaseHTTPRequestHandler.__init__(self, *args)

+

+  def _send_default_response_headers(self):

+    """ Send default response headers. """

+    self.send_response(200)

+    self.send_header('Content-type', 'text/html')

+    self.end_headers()

+

+  def _parse_post_data(self, data):

+    """ Returns a cgi.FieldStorage object for this request or None if this is

+        not a POST request. """

+    if self.command != 'POST':

+      return None

+    return cgi.FieldStorage(

+        fp=BytesIO(data),

+        headers=self.headers,

+        environ={

+            'REQUEST_METHOD': 'POST',

+            'CONTENT_TYPE': self.headers['Content-Type'],

+        })

+

+  def _get_chunk_size(self):

+    # Read to the next "\r\n".

+    size_str = self.rfile.read(2)

+    while size_str[-2:] != b"\r\n":

+      size_str += self.rfile.read(1)

+    # Remove the trailing "\r\n".

+    size_str = size_str[:-2]

+    assert len(size_str) <= 4

+    return int(size_str, 16)

+

+  def _get_chunk_data(self, chunk_size):

+    data = self.rfile.read(chunk_size)

+    assert len(data) == chunk_size

+    # Skip the trailing "\r\n".

+    self.rfile.read(2)

+    return data

+

+  def _unchunk_request(self, compressed):

+    """ Read a chunked request body. Optionally decompress the result. """

+    if compressed:

+      d = zlib.decompressobj(16 + zlib.MAX_WBITS)

+

+    # Chunked format is: <size>\r\n<bytes>\r\n<size>\r\n<bytes>\r\n0\r\n

+    unchunked = b""

+    while True:

+      chunk_size = self._get_chunk_size()

+      print('Chunk size 0x%x' % chunk_size)

+      if (chunk_size == 0):

+        break

+      chunk_data = self._get_chunk_data(chunk_size)

+      if compressed:

+        unchunked += d.decompress(chunk_data)

+      else:

+        unchunked += chunk_data

+

+    if compressed:

+      unchunked += d.flush()

+

+    return unchunked

+

+  def _create_new_dump_id(self):

+    """ Breakpad requires a 16 digit hexadecimal dump ID. """

+    return uuid.uuid4().hex.upper()[0:16]

+

+  def do_GET(self):

+    """ Default empty implementation for handling GET requests. """

+    self._send_default_response_headers()

+    self.wfile.write("<html><body><h1>GET!</h1></body></html>")

+

+  def do_HEAD(self):

+    """ Default empty implementation for handling HEAD requests. """

+    self._send_default_response_headers()

+

+  def do_POST(self):

+    """ Handle a multi-part POST request submitted by Breakpad/Crashpad. """

+    self._send_default_response_headers()

+

+    # Create a unique ID for the dump.

+    dump_id = self._create_new_dump_id()

+

+    # Return the unique ID to the caller.

+    self.wfile.write(dump_id.encode('utf-8'))

+

+    dmp_stream = None

+    metadata = {}

+

+    # Request body may be chunked and/or gzip compressed. For example:

+    #

+    # 3029 branch on Windows:

+    #   User-Agent: Crashpad/0.8.0

+    #   Host: localhost:8080

+    #   Connection: Keep-Alive

+    #   Transfer-Encoding: chunked

+    #   Content-Type: multipart/form-data; boundary=---MultipartBoundary-vp5j9HdSRYK8DvX2DhtpqEbMNjSN1wnL---

+    #   Content-Encoding: gzip

+    #

+    # 2987 branch on Windows:

+    #   User-Agent: Crashpad/0.8.0

+    #   Host: localhost:8080

+    #   Connection: Keep-Alive

+    #   Content-Type: multipart/form-data; boundary=---MultipartBoundary-qFhorGA40vDJ1fgmc2mjorL0fRfKOqup---

+    #   Content-Length: 609894

+    #

+    # 2883 branch on Linux:

+    #   User-Agent: Wget/1.15 (linux-gnu)

+    #   Host: localhost:8080

+    #   Accept: */*

+    #   Connection: Keep-Alive

+    #   Content-Type: multipart/form-data; boundary=--------------------------83572861f14cc736

+    #   Content-Length: 32237

+    #   Content-Encoding: gzip

+    print(self.headers)

+

+    chunked = 'Transfer-Encoding' in self.headers and self.headers['Transfer-Encoding'].lower(

+    ) == 'chunked'

+    compressed = 'Content-Encoding' in self.headers and self.headers['Content-Encoding'].lower(

+    ) == 'gzip'

+    if chunked:

+      request_body = self._unchunk_request(compressed)

+    else:

+      content_length = int(self.headers[

+          'Content-Length']) if 'Content-Length' in self.headers else 0

+      if content_length > 0:

+        request_body = self.rfile.read(content_length)

+      else:

+        request_body = self.rfile.read()

+      if compressed:

+        request_body = zlib.decompress(request_body, 16 + zlib.MAX_WBITS)

+

+    # Parse the multi-part request.

+    form_data = self._parse_post_data(request_body)

+    for key in form_data.keys():

+      if key == minidump_key and form_data[minidump_key].file:

+        dmp_stream = form_data[minidump_key].file

+      else:

+        metadata[key] = form_data[key].value

+

+    if dmp_stream is None:

+      # Exit early if the request is invalid.

+      print_msg('Invalid dump %s' % dump_id)

+      return

+

+    print_msg('Dump %s' % dump_id)

+

+    # Write the minidump to file.

+    dump_file = os.path.join(self._dump_directory, dump_id + '.dmp')

+    with open(dump_file, 'wb') as fp:

+      shutil.copyfileobj(dmp_stream, fp)

+

+    # Write the metadata to file.

+    meta_file = os.path.join(self._dump_directory, dump_id + '.json')

+    if is_python2:

+      with open(meta_file, 'w') as fp:

+        json.dump(

+            metadata,

+            fp,

+            ensure_ascii=False,

+            encoding='utf-8',

+            indent=2,

+            sort_keys=True)

+    else:

+      with open(meta_file, 'w', encoding='utf-8') as fp:

+        json.dump(metadata, fp, indent=2, sort_keys=True)

+

+

+def HandleRequestsUsing(dump_store):

+  return lambda *args: CrashHTTPRequestHandler(dump_directory, *args)

+

+

+def RunCrashServer(port, dump_directory):

+  """ Run the crash handler HTTP server. """

+  httpd = HTTPServer(('', port), HandleRequestsUsing(dump_directory))

+  print_msg('Starting httpd on port %d' % port)

+  httpd.serve_forever()

+

+

+# Program entry point.

+if __name__ == "__main__":

+  if len(sys.argv) != 3:

+    print('Usage: %s <port> <dump_directory>' % os.path.basename(sys.argv[0]))

+    sys.exit(1)

+

+  # Create the dump directory if necessary.

+  dump_directory = sys.argv[2]

+  if not os.path.exists(dump_directory):

+    os.makedirs(dump_directory)

+  if not os.path.isdir(dump_directory):

+    raise Exception('Directory does not exist: %s' % dump_directory)

+

+  RunCrashServer(int(sys.argv[1]), dump_directory)

diff --git a/src/tools/date_util.py b/src/tools/date_util.py
new file mode 100644
index 0000000..4c17620
--- /dev/null
+++ b/src/tools/date_util.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+import datetime
+
+
+def get_year():
+  """ Returns the current year. """
+  return str(datetime.datetime.now().year)
+
+
+def get_date():
+  """ Returns the current date. """
+  return datetime.datetime.now().strftime('%B %d, %Y')
diff --git a/src/tools/distrib/README-TRANSFER.txt b/src/tools/distrib/README-TRANSFER.txt
new file mode 100644
index 0000000..6843cc2
--- /dev/null
+++ b/src/tools/distrib/README-TRANSFER.txt
@@ -0,0 +1,5 @@
+Files in this directory have been copied from other locations in the Chromium
+source tree. They have been modified only to the extent necessary to work in
+the CEF Binary Distribution directory structure. Below is a listing of the
+original file locations.
+
diff --git a/src/tools/distrib/README.client.txt b/src/tools/distrib/README.client.txt
new file mode 100644
index 0000000..37970dd
--- /dev/null
+++ b/src/tools/distrib/README.client.txt
@@ -0,0 +1,12 @@
+CONTENTS
+--------
+
+Release     Contains a release build of the sample application.
+
+
+USAGE
+-----
+
+Please visit the CEF Website for additional usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/README.footer.txt b/src/tools/distrib/README.footer.txt
new file mode 100644
index 0000000..e57a662
--- /dev/null
+++ b/src/tools/distrib/README.footer.txt
@@ -0,0 +1,8 @@
+LICENSING
+---------
+
+The CEF project is BSD licensed. Please read the LICENSE.txt file included with
+this binary distribution for licensing terms and conditions. Other software
+included in this distribution is provided under other licenses. Please visit
+"about:credits" in a CEF-based application for complete Chromium and third-party
+licensing information.
diff --git a/src/tools/distrib/README.header.txt b/src/tools/distrib/README.header.txt
new file mode 100644
index 0000000..26c0f98
--- /dev/null
+++ b/src/tools/distrib/README.header.txt
@@ -0,0 +1,14 @@
+Chromium Embedded Framework (CEF) $DISTRIB_TYPE$ Binary Distribution for $PLATFORM$
+-------------------------------------------------------------------------------
+
+Date:             $DATE$
+
+CEF Version:      $CEF_VER$
+CEF URL:          $CEF_URL$
+                  @$CEF_REV$
+
+Chromium Version: $CHROMIUM_VER$
+Chromium URL:     $CHROMIUM_URL$
+                  @$CHROMIUM_REV$
+
+$DISTRIB_DESC$
diff --git a/src/tools/distrib/linux/README.minimal.txt b/src/tools/distrib/linux/README.minimal.txt
new file mode 100644
index 0000000..16cf3cb
--- /dev/null
+++ b/src/tools/distrib/linux/README.minimal.txt
@@ -0,0 +1,28 @@
+CONTENTS
+--------
+
+cmake       Contains CMake configuration files shared by all targets.
+
+include     Contains all required CEF header files.
+
+libcef_dll  Contains the source code for the libcef_dll_wrapper static library
+            that all applications using the CEF C++ API must link against.
+
+Release     Contains libcef.so and other components required to run the release
+            version of CEF-based applications. By default these files should be
+            placed in the same directory as the executable.
+
+Resources   Contains resources required by libcef.so. By default these files
+            should be placed in the same directory as libcef.so.
+
+
+USAGE
+-----
+
+Building using CMake:
+  CMake can be used to generate project files in many different formats. See
+  usage instructions at the top of the CMakeLists.txt file.
+
+Please visit the CEF Website for additional usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/linux/README.redistrib.txt b/src/tools/distrib/linux/README.redistrib.txt
new file mode 100644
index 0000000..29c5120
--- /dev/null
+++ b/src/tools/distrib/linux/README.redistrib.txt
@@ -0,0 +1,70 @@
+REDISTRIBUTION
+--------------
+
+This binary distribution contains the below components.
+
+Required components:
+
+The following components are required. CEF will not function without them.
+
+* CEF core library.
+  * libcef.so
+
+* Unicode support data.
+  * icudtl.dat
+
+* V8 snapshot data.
+  * snapshot_blob.bin
+  * v8_context_snapshot.bin
+
+Optional components:
+
+The following components are optional. If they are missing CEF will continue to
+run but any related functionality may become broken or disabled.
+
+* Localized resources.
+  Locale file loading can be disabled completely using
+  CefSettings.pack_loading_disabled. The locales directory path can be
+  customized using CefSettings.locales_dir_path. 
+ 
+  * locales/
+    Directory containing localized resources used by CEF, Chromium and Blink. A
+    .pak file is loaded from this directory based on the value of environment
+    variables which are read with the following precedence order: LANGUAGE,
+    LC_ALL, LC_MESSAGES and LANG. Only configured locales need to be
+    distributed. If no locale is configured the default locale of "en-US" will
+    be used. Without these files arbitrary Web components may display
+    incorrectly.
+
+* Other resources.
+  Pack file loading can be disabled completely using
+  CefSettings.pack_loading_disabled. The resources directory path can be
+  customized using CefSettings.resources_dir_path.
+
+  * cef.pak
+  * cef_100_percent.pak
+  * cef_200_percent.pak
+    These files contain non-localized resources used by CEF, Chromium and Blink.
+    Without these files arbitrary Web components may display incorrectly.
+
+  * cef_extensions.pak
+    This file contains non-localized resources required for extension loading.
+    Pass the `--disable-extensions` command-line flag to disable use of this
+    file. Without this file components that depend on the extension system,
+    such as the PDF viewer, will not function.
+
+  * devtools_resources.pak
+    This file contains non-localized resources required for Chrome Developer
+    Tools. Without this file Chrome Developer Tools will not function.
+
+* Angle support.
+  * libEGL.so
+  * libGLESv2.so
+  Without these files HTML5 accelerated content like 2D canvas, 3D CSS and WebGL
+  will not function.
+
+* SwiftShader support.
+  * swiftshader/libEGL.so
+  * swiftshader/libGLESv2.so
+  Without these files WebGL will not function in software-only mode when the GPU
+  is not available or disabled.
diff --git a/src/tools/distrib/linux/README.standard.txt b/src/tools/distrib/linux/README.standard.txt
new file mode 100644
index 0000000..24c48da
--- /dev/null
+++ b/src/tools/distrib/linux/README.standard.txt
@@ -0,0 +1,52 @@
+CONTENTS
+--------
+
+cmake       Contains CMake configuration files shared by all targets.
+
+Debug       Contains libcef.so and other components required to run the debug
+            version of CEF-based applications. By default these files should be
+            placed in the same directory as libcef.so and will be copied there
+            as part of the build process.
+
+include     Contains all required CEF header files.
+
+libcef_dll  Contains the source code for the libcef_dll_wrapper static library
+            that all applications using the CEF C++ API must link against.
+
+Release     Contains libcef.so and other components required to run the release
+            version of CEF-based applications. By default these files should be
+            placed in the same directory as libcef.so and will be copied there
+            as part of the build process.
+
+Resources   Contains resources required by libcef.so. By default these files
+            should be placed in the same directory as libcef.so and will be
+            copied there as part of the build process.
+
+tests/      Directory of tests that demonstrate CEF usage.
+
+  cefclient Contains the cefclient sample application configured to build
+            using the files in this distribution. This application demonstrates
+            a wide range of CEF functionalities.
+
+  cefsimple Contains the cefsimple sample application configured to build
+            using the files in this distribution. This application demonstrates
+            the minimal functionality required to create a browser window.
+
+  ceftests  Contains unit tests that exercise the CEF APIs.
+
+  gtest     Contains the Google C++ Testing Framework used by the ceftests
+            target.
+
+  shared    Contains source code shared by the cefclient and ceftests targets.
+
+
+USAGE
+-----
+
+Building using CMake:
+  CMake can be used to generate project files in many different formats. See
+  usage instructions at the top of the CMakeLists.txt file.
+
+Please visit the CEF Website for additional usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/mac/README.minimal.txt b/src/tools/distrib/mac/README.minimal.txt
new file mode 100644
index 0000000..723edbd
--- /dev/null
+++ b/src/tools/distrib/mac/README.minimal.txt
@@ -0,0 +1,25 @@
+CONTENTS
+--------
+
+cmake       Contains CMake configuration files shared by all targets.
+
+include     Contains all required CEF header files.
+
+libcef_dll  Contains the source code for the libcef_dll_wrapper static library
+            that all applications using the CEF C++ API must link against.
+
+Release     Contains the "Chromium Embedded Framework.framework" and other
+            components required to run the release version of CEF-based
+            applications.
+
+
+USAGE
+-----
+
+Building using CMake:
+  CMake can be used to generate project files in many different formats. See
+  usage instructions at the top of the CMakeLists.txt file.
+
+Please visit the CEF Website for additional usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/mac/README.redistrib.txt b/src/tools/distrib/mac/README.redistrib.txt
new file mode 100644
index 0000000..5a9fdef
--- /dev/null
+++ b/src/tools/distrib/mac/README.redistrib.txt
@@ -0,0 +1,116 @@
+REDISTRIBUTION
+--------------
+
+This binary distribution contains the below components. Components listed under
+the "required" section must be redistributed with all applications using CEF.
+Components listed under the "optional" section may be excluded if the related
+features will not be used.
+
+Applications using CEF on OS X must follow a specific app bundle structure.
+Replace "cefclient" in the below example with your application name.
+
+cefclient.app/
+  Contents/
+    Frameworks/
+      Chromium Embedded Framework.framework/
+        Chromium Embedded Framework <= main application library
+        Libraries/
+          libEGL.dylib <= angle support libraries
+          libGLESv2.dylib <=^
+          libswiftshader_libEGL.dylib <= swiftshader support libraries
+          libswiftshader_libGLESv2.dylib <=^
+        Resources/
+          cef.pak <= non-localized resources and strings
+          cef_100_percent.pak <====^
+          cef_200_percent.pak <====^
+          cef_extensions.pak <=====^
+          devtools_resources.pak <=^
+          icudtl.dat <= unicode support
+          snapshot_blob.bin, v8_context_snapshot.bin <= V8 initial snapshot
+          en.lproj/, ... <= locale-specific resources and strings
+          Info.plist
+      cefclient Helper.app/
+        Contents/
+          Info.plist
+          MacOS/
+            cefclient Helper <= helper executable
+          Pkginfo
+      Info.plist
+    MacOS/
+      cefclient <= cefclient application executable
+    Pkginfo
+    Resources/
+      binding.html, ... <= cefclient application resources
+
+The "Chromium Embedded Framework.framework" is an unversioned framework that
+contains CEF binaries and resources. Executables (cefclient, cefclient Helper,
+etc) must load this framework dynamically at runtime instead of linking it
+directly. See the documentation in include/wrapper/cef_library_loader.h for
+more information.
+
+The "cefclient Helper" app is used for executing separate processes (renderer,
+plugin, etc) with different characteristics. It needs to have a separate app
+bundle and Info.plist file so that, among other things, it doesn't show dock
+icons.
+
+Required components:
+
+The following components are required. CEF will not function without them.
+
+* CEF core library.
+  * Chromium Embedded Framework.framework/Chromium Embedded Framework
+
+* Unicode support data.
+  * Chromium Embedded Framework.framework/Resources/icudtl.dat
+
+* V8 snapshot data.
+  * Chromium Embedded Framework.framework/Resources/snapshot_blob.bin
+  * Chromium Embedded Framework.framework/Resources/v8_context_snapshot.bin
+
+Optional components:
+
+The following components are optional. If they are missing CEF will continue to
+run but any related functionality may become broken or disabled.
+
+* Localized resources.
+  Locale file loading can be disabled completely using
+  CefSettings.pack_loading_disabled.
+
+  * Chromium Embedded Framework.framework/Resources/*.lproj/
+    Directory containing localized resources used by CEF, Chromium and Blink. A
+    .pak file is loaded from this directory based on the CefSettings.locale
+    value. Only configured locales need to be distributed. If no locale is
+    configured the default locale of "en" will be used. Without these files
+    arbitrary Web components may display incorrectly.
+
+* Other resources.
+  Pack file loading can be disabled completely using
+  CefSettings.pack_loading_disabled.
+
+  * Chromium Embedded Framework.framework/Resources/cef.pak
+  * Chromium Embedded Framework.framework/Resources/cef_100_percent.pak
+  * Chromium Embedded Framework.framework/Resources/cef_200_percent.pak
+    These files contain non-localized resources used by CEF, Chromium and Blink.
+    Without these files arbitrary Web components may display incorrectly.
+
+  * Chromium Embedded Framework.framework/Resources/cef_extensions.pak
+    This file contains non-localized resources required for extension loading.
+    Pass the `--disable-extensions` command-line flag to disable use of this
+    file. Without this file components that depend on the extension system,
+    such as the PDF viewer, will not function.
+
+  * Chromium Embedded Framework.framework/Resources/devtools_resources.pak
+    This file contains non-localized resources required for Chrome Developer
+    Tools. Without this file Chrome Developer Tools will not function.
+
+* Angle support.
+  * Chromium Embedded Framework.framework/Libraries/libEGL.dylib
+  * Chromium Embedded Framework.framework/Libraries/libGLESv2.dylib
+  Without these files HTML5 accelerated content like 2D canvas, 3D CSS and WebGL
+  will not function.
+
+* SwiftShader support.
+  * Chromium Embedded Framework.framework/Libraries/libswiftshader_libEGL.dylib
+  * Chromium Embedded Framework.framework/Libraries/libswiftshader_libGLESv2.dylib
+  Without these files WebGL will not function in software-only mode when the GPU
+  is not available or disabled.
diff --git a/src/tools/distrib/mac/README.standard.txt b/src/tools/distrib/mac/README.standard.txt
new file mode 100644
index 0000000..4de70c2
--- /dev/null
+++ b/src/tools/distrib/mac/README.standard.txt
@@ -0,0 +1,46 @@
+CONTENTS
+--------
+
+cmake       Contains CMake configuration files shared by all targets.
+
+Debug       Contains the "Chromium Embedded Framework.framework" and other
+            components required to run the debug version of CEF-based
+            applications.
+
+include     Contains all required CEF header files.
+
+libcef_dll  Contains the source code for the libcef_dll_wrapper static library
+            that all applications using the CEF C++ API must link against.
+
+Release     Contains the "Chromium Embedded Framework.framework" and other
+            components required to run the release version of CEF-based
+            applications.
+
+tests/      Directory of tests that demonstrate CEF usage.
+
+  cefclient Contains the cefclient sample application configured to build
+            using the files in this distribution. This application demonstrates
+            a wide range of CEF functionalities.
+
+  cefsimple Contains the cefsimple sample application configured to build
+            using the files in this distribution. This application demonstrates
+            the minimal functionality required to create a browser window.
+
+  ceftests  Contains unit tests that exercise the CEF APIs.
+
+  gtest     Contains the Google C++ Testing Framework used by the ceftests
+            target.
+
+  shared    Contains source code shared by the cefclient and ceftests targets.
+
+
+USAGE
+-----
+
+Building using CMake:
+  CMake can be used to generate project files in many different formats. See
+  usage instructions at the top of the CMakeLists.txt file.
+
+Please visit the CEF Website for additional usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/transfer.cfg b/src/tools/distrib/transfer.cfg
new file mode 100644
index 0000000..6b8a5cd
--- /dev/null
+++ b/src/tools/distrib/transfer.cfg
@@ -0,0 +1,17 @@
+# Additional handling of transfer files.
+# target: Target location relative to the target release directory. This
+#     value is required.
+# source: Source location relative to the CEF root directory. This value
+#     is optional. If specified the source file will be copied to the target
+#     location and a TRANSFER-README.txt file will be created.
+# post-process: Post-processing operation to perform. This value is
+#     optional and may be any one of the following:
+#   'normalize_headers': Replace fully-qualified project header paths with
+#     the optionally specified 'new_header_path' value.
+
+[
+  {
+    'source' : '../net/base/net_error_list.h',
+    'target' : 'include/base/internal/cef_net_error_list.h',
+  },
+]
\ No newline at end of file
diff --git a/src/tools/distrib/win/README.minimal.txt b/src/tools/distrib/win/README.minimal.txt
new file mode 100644
index 0000000..4d8207e
--- /dev/null
+++ b/src/tools/distrib/win/README.minimal.txt
@@ -0,0 +1,29 @@
+CONTENTS
+--------
+
+cmake       Contains CMake configuration files shared by all targets.
+
+include     Contains all required CEF header files.
+
+libcef_dll  Contains the source code for the libcef_dll_wrapper static library
+            that all applications using the CEF C++ API must link against.
+
+Release     Contains libcef.dll, libcef.lib and other components required to
+            build and run the release version of CEF-based applications. By
+            default these files should be placed in the same directory as the
+            executable.
+
+Resources   Contains resources required by libcef.dll. By default these files
+            should be placed in the same directory as libcef.dll.
+
+
+USAGE
+-----
+
+Building using CMake:
+  CMake can be used to generate project files in many different formats. See
+  usage instructions at the top of the CMakeLists.txt file.
+
+Please visit the CEF Website for additional usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/win/README.redistrib.txt b/src/tools/distrib/win/README.redistrib.txt
new file mode 100644
index 0000000..b96ab35
--- /dev/null
+++ b/src/tools/distrib/win/README.redistrib.txt
@@ -0,0 +1,72 @@
+REDISTRIBUTION
+--------------
+
+This binary distribution contains the below components.
+
+Required components:
+
+The following components are required. CEF will not function without them.
+
+* CEF core library.
+  * libcef.dll
+
+* Crash reporting library.
+  * chrome_elf.dll
+
+* Unicode support data.
+  * icudtl.dat
+
+* V8 snapshot data.
+  * snapshot_blob.bin
+  * v8_context_snapshot.bin
+
+Optional components:
+
+The following components are optional. If they are missing CEF will continue to
+run but any related functionality may become broken or disabled.
+
+* Localized resources.
+  Locale file loading can be disabled completely using
+  CefSettings.pack_loading_disabled. The locales directory path can be
+  customized using CefSettings.locales_dir_path. 
+ 
+  * locales/
+    Directory containing localized resources used by CEF, Chromium and Blink. A
+    .pak file is loaded from this directory based on the CefSettings.locale
+    value. Only configured locales need to be distributed. If no locale is
+    configured the default locale of "en-US" will be used. Without these files
+    arbitrary Web components may display incorrectly.
+
+* Other resources.
+  Pack file loading can be disabled completely using
+  CefSettings.pack_loading_disabled. The resources directory path can be
+  customized using CefSettings.resources_dir_path.
+
+  * cef.pak
+  * cef_100_percent.pak
+  * cef_200_percent.pak
+    These files contain non-localized resources used by CEF, Chromium and Blink.
+    Without these files arbitrary Web components may display incorrectly.
+
+  * cef_extensions.pak
+    This file contains non-localized resources required for extension loading.
+    Pass the `--disable-extensions` command-line flag to disable use of this
+    file. Without this file components that depend on the extension system,
+    such as the PDF viewer, will not function.
+
+  * devtools_resources.pak
+    This file contains non-localized resources required for Chrome Developer
+    Tools. Without this file Chrome Developer Tools will not function.
+
+* Angle and Direct3D support.
+  * d3dcompiler_47.dll (required for Windows Vista and newer)
+  * libEGL.dll
+  * libGLESv2.dll
+  Without these files HTML5 accelerated content like 2D canvas, 3D CSS and WebGL
+  will not function.
+
+* SwiftShader support.
+  * swiftshader/libEGL.dll
+  * swiftshader/libGLESv2.dll
+  Without these files WebGL will not function in software-only mode when the GPU
+  is not available or disabled.
diff --git a/src/tools/distrib/win/README.sandbox.txt b/src/tools/distrib/win/README.sandbox.txt
new file mode 100644
index 0000000..09ee832
--- /dev/null
+++ b/src/tools/distrib/win/README.sandbox.txt
@@ -0,0 +1,14 @@
+CONTENTS
+--------
+
+Debug       Contains the Debug build of cef_sandbox.lib.
+
+Release     Contains the Release build of cef_sandbox.lib.
+
+
+USAGE
+-----
+
+Please visit the CEF Website for usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/win/README.standard.txt b/src/tools/distrib/win/README.standard.txt
new file mode 100644
index 0000000..92d7797
--- /dev/null
+++ b/src/tools/distrib/win/README.standard.txt
@@ -0,0 +1,52 @@
+CONTENTS
+--------
+
+cmake       Contains CMake configuration files shared by all targets.
+
+Debug       Contains libcef.dll, libcef.lib and other components required to
+            build and run the debug version of CEF-based applications. By
+            default these files should be placed in the same directory as the
+            executable and will be copied there as part of the build process.
+
+include     Contains all required CEF header files.
+
+libcef_dll  Contains the source code for the libcef_dll_wrapper static library
+            that all applications using the CEF C++ API must link against.
+
+Release     Contains libcef.dll, libcef.lib and other components required to
+            build and run the release version of CEF-based applications. By
+            default these files should be placed in the same directory as the
+            executable and will be copied there as part of the build process.
+
+Resources   Contains resources required by libcef.dll. By default these files
+            should be placed in the same directory as libcef.dll and will be
+            copied there as part of the build process.
+
+tests/      Directory of tests that demonstrate CEF usage.
+
+  cefclient Contains the cefclient sample application configured to build
+            using the files in this distribution. This application demonstrates
+            a wide range of CEF functionalities.
+
+  cefsimple Contains the cefsimple sample application configured to build
+            using the files in this distribution. This application demonstrates
+            the minimal functionality required to create a browser window.
+
+  ceftests  Contains unit tests that exercise the CEF APIs.
+
+  gtest     Contains the Google C++ Testing Framework used by the ceftests
+            target.
+
+  shared    Contains source code shared by the cefclient and ceftests targets.
+
+
+USAGE
+-----
+
+Building using CMake:
+  CMake can be used to generate project files in many different formats. See
+  usage instructions at the top of the CMakeLists.txt file.
+
+Please visit the CEF Website for additional usage information.
+
+https://bitbucket.org/chromiumembedded/cef/
diff --git a/src/tools/distrib/win/transfer_standard.cfg b/src/tools/distrib/win/transfer_standard.cfg
new file mode 100644
index 0000000..cb06d2f
--- /dev/null
+++ b/src/tools/distrib/win/transfer_standard.cfg
@@ -0,0 +1,25 @@
+# Additional handling of transfer files.
+# target: Target location relative to the target release directory. This
+#     value is required.
+# source: Source location relative to the CEF root directory. This value
+#     is optional. If specified the source file will be copied to the target
+#     location and a TRANSFER-README.txt file will be created.
+# post-process: Post-processing operation to perform. This value is
+#     optional and may be any one of the following:
+#   'normalize_headers': Replace fully-qualified project header paths with
+#     the optionally specified 'new_header_path' value.
+
+[
+  {
+    'source' : '../build/win/compatibility.manifest',
+    'target' : 'tests/cefclient/resources/win/compatibility.manifest',
+  },
+  {
+    'source' : '../build/win/compatibility.manifest',
+    'target' : 'tests/cefsimple/compatibility.manifest',
+  },
+  {
+    'source' : '../build/win/compatibility.manifest',
+    'target' : 'tests/ceftests/resources/win/compatibility.manifest',
+  },
+]
diff --git a/src/tools/distrib/win/x64/d3dcompiler_47.dll b/src/tools/distrib/win/x64/d3dcompiler_47.dll
new file mode 100644
index 0000000..b120261
--- /dev/null
+++ b/src/tools/distrib/win/x64/d3dcompiler_47.dll
Binary files differ
diff --git a/src/tools/distrib/win/x86/d3dcompiler_47.dll b/src/tools/distrib/win/x86/d3dcompiler_47.dll
new file mode 100644
index 0000000..967ee40
--- /dev/null
+++ b/src/tools/distrib/win/x86/d3dcompiler_47.dll
Binary files differ
diff --git a/src/tools/exec_util.py b/src/tools/exec_util.py
new file mode 100644
index 0000000..a6c2347
--- /dev/null
+++ b/src/tools/exec_util.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file
+
+from __future__ import absolute_import
+from subprocess import Popen, PIPE
+import sys
+
+
+def exec_cmd(cmd, path, input_string=None):
+  """ Execute the specified command and return the result. """
+  out = ''
+  err = ''
+  ret = -1
+  parts = cmd.split()
+  try:
+    if input_string is None:
+      process = Popen(
+          parts,
+          cwd=path,
+          stdout=PIPE,
+          stderr=PIPE,
+          shell=(sys.platform == 'win32'))
+      out, err = process.communicate()
+      ret = process.returncode
+    else:
+      process = Popen(
+          parts,
+          cwd=path,
+          stdin=PIPE,
+          stdout=PIPE,
+          stderr=PIPE,
+          shell=(sys.platform == 'win32'))
+      out, err = process.communicate(input=input_string)
+      ret = process.returncode
+  except IOError as e:
+    (errno, strerror) = e.args
+    raise
+  except:
+    raise
+  return {'out': out.decode('utf-8'), 'err': err.decode('utf-8'), 'ret': ret}
diff --git a/src/tools/file_util.py b/src/tools/file_util.py
new file mode 100644
index 0000000..54d79ee
--- /dev/null
+++ b/src/tools/file_util.py
@@ -0,0 +1,181 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from glob import iglob
+from io import open
+import os
+import shutil
+import sys
+import time
+
+
+def read_file(name, normalize=True):
+  """ Read a file. """
+  try:
+    with open(name, 'r', encoding='utf-8') as f:
+      # read the data
+      data = f.read()
+      if normalize:
+        # normalize line endings
+        data = data.replace("\r\n", "\n")
+      return data
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to read file ' + name + ': ' + strerror)
+    raise
+
+
+def write_file(name, data):
+  """ Write a file. """
+  try:
+    with open(name, 'w', encoding='utf-8') as f:
+      # write the data
+      if sys.version_info.major == 2:
+        f.write(data.decode('utf-8'))
+      else:
+        f.write(data)
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to write file ' + name + ': ' + strerror)
+    raise
+
+
+def path_exists(name):
+  """ Returns true if the path currently exists. """
+  return os.path.exists(name)
+
+
+def write_file_if_changed(name, data):
+  """ Write a file if the contents have changed. Returns True if the file was written. """
+  if path_exists(name):
+    old_contents = read_file(name)
+  else:
+    old_contents = ''
+
+  if (data != old_contents):
+    write_file(name, data)
+    return True
+  return False
+
+
+def backup_file(name):
+  """ Rename the file to a name that includes the current time stamp. """
+  move_file(name, name + '.' + time.strftime('%Y-%m-%d-%H-%M-%S'))
+
+
+def copy_file(src, dst, quiet=True):
+  """ Copy a file. """
+  try:
+    shutil.copy2(src, dst)
+    if not quiet:
+      sys.stdout.write('Transferring ' + src + ' file.\n')
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to copy file from ' + src + ' to ' + dst + ': ' +
+                     strerror)
+    raise
+
+
+def move_file(src, dst, quiet=True):
+  """ Move a file. """
+  try:
+    shutil.move(src, dst)
+    if not quiet:
+      sys.stdout.write('Moving ' + src + ' file.\n')
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to move file from ' + src + ' to ' + dst + ': ' +
+                     strerror)
+    raise
+
+
+def copy_files(src_glob, dst_folder, quiet=True):
+  """ Copy multiple files. """
+  for fname in iglob(src_glob):
+    dst = os.path.join(dst_folder, os.path.basename(fname))
+    if os.path.isdir(fname):
+      copy_dir(fname, dst, quiet)
+    else:
+      copy_file(fname, dst, quiet)
+
+
+def remove_file(name, quiet=True):
+  """ Remove the specified file. """
+  try:
+    if path_exists(name):
+      os.remove(name)
+      if not quiet:
+        sys.stdout.write('Removing ' + name + ' file.\n')
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to remove file ' + name + ': ' + strerror)
+    raise
+
+
+def copy_dir(src, dst, quiet=True):
+  """ Copy a directory tree. """
+  try:
+    remove_dir(dst, quiet)
+    shutil.copytree(src, dst)
+    if not quiet:
+      sys.stdout.write('Transferring ' + src + ' directory.\n')
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to copy directory from ' + src + ' to ' + dst +
+                     ': ' + strerror)
+    raise
+
+
+def remove_dir(name, quiet=True):
+  """ Remove the specified directory. """
+  try:
+    if path_exists(name):
+      shutil.rmtree(name)
+      if not quiet:
+        sys.stdout.write('Removing ' + name + ' directory.\n')
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to remove directory ' + name + ': ' + strerror)
+    raise
+
+
+def make_dir(name, quiet=True):
+  """ Create the specified directory. """
+  try:
+    if not path_exists(name):
+      if not quiet:
+        sys.stdout.write('Creating ' + name + ' directory.\n')
+      os.makedirs(name)
+  except IOError as e:
+    (errno, strerror) = e.args
+    sys.stderr.write('Failed to create directory ' + name + ': ' + strerror)
+    raise
+
+
+def get_files(search_glob):
+  """ Returns all files matching the search glob. """
+  # Sort the result for consistency across platforms.
+  return sorted(iglob(search_glob))
+
+
+def read_version_file(file, args):
+  """ Read and parse a version file (key=value pairs, one per line). """
+  lines = read_file(file).split("\n")
+  for line in lines:
+    parts = line.split('=', 1)
+    if len(parts) == 2:
+      args[parts[0]] = parts[1]
+
+
+def eval_file(src):
+  """ Loads and evaluates the contents of the specified file. """
+  return eval(read_file(src), {'__builtins__': None}, None)
+
+
+def normalize_path(path):
+  """ Normalizes the path separator to match the Unix standard. """
+  if sys.platform == 'win32':
+    return path.replace('\\', '/')
+  return path
diff --git a/src/tools/fix_style.bat b/src/tools/fix_style.bat
new file mode 100644
index 0000000..09ea0be
--- /dev/null
+++ b/src/tools/fix_style.bat
@@ -0,0 +1,2 @@
+@echo off
+python.bat %~dp0\fix_style.py %*
diff --git a/src/tools/fix_style.py b/src/tools/fix_style.py
new file mode 100644
index 0000000..e0debae
--- /dev/null
+++ b/src/tools/fix_style.py
@@ -0,0 +1,145 @@
+# Copyright (c) 2017 The Chromium Embedded Framework Authors.
+# Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os, re, sys
+from clang_util import clang_format
+from file_util import eval_file, get_files, read_file, write_file
+from git_util import get_changed_files
+from yapf_util import yapf_format
+
+# File extensions that can be formatted.
+DEFAULT_LINT_WHITELIST_REGEX = r"(.*\.cpp|.*\.cc|.*\.h|.*\.java|.*\.mm|.*\.py)$"
+DEFAULT_LINT_BLACKLIST_REGEX = r"$^"
+
+# Directories containing these path components will be ignored.
+IGNORE_DIRECTORIES = []
+
+# Script directory.
+script_dir = os.path.dirname(__file__)
+root_dir = os.path.join(script_dir, os.pardir)
+
+
+def msg(filename, status):
+  if sys.platform == 'win32':
+    # Use Unix path separator.
+    filename = filename.replace("\\", "/")
+
+  if len(filename) > 60:
+    # Truncate the file path in a nice way.
+    filename = filename[-57:]
+    pos = filename.find("/")
+    if pos > 0:
+      filename = filename[pos:]
+    filename = "..." + filename
+
+  print("%-60s %s" % (filename, status))
+
+
+updatect = 0
+
+
+def read_config():
+  style_cfg = os.path.join(root_dir, ".style.cfg")
+  if os.path.exists(style_cfg):
+    config = eval_file(style_cfg)
+    if 'ignore_directories' in config:
+      global IGNORE_DIRECTORIES
+      IGNORE_DIRECTORIES = config['ignore_directories']
+
+
+def update_file(filename):
+  oldcontents = read_file(filename)
+  if len(oldcontents) == 0:
+    msg(filename, "empty")
+    return
+
+  if os.path.splitext(filename)[1] == ".py":
+    # Format Python files using YAPF.
+    newcontents = yapf_format(filename, oldcontents)
+  else:
+    # Format C/C++/ObjC/Java files using clang-format.
+    newcontents = clang_format(filename, oldcontents)
+
+  if newcontents is None:
+    raise Exception("Failed to process %s" % filename)
+
+  if newcontents != oldcontents:
+    msg(filename, "fixed")
+    global updatect
+    updatect += 1
+    write_file(filename, newcontents)
+  else:
+    msg(filename, "ok")
+  return
+
+
+def fix_style(filenames, white_list=None, black_list=None):
+  """ Execute clang-format with the specified arguments. """
+  if not white_list:
+    white_list = DEFAULT_LINT_WHITELIST_REGEX
+  white_regex = re.compile(white_list)
+  if not black_list:
+    black_list = DEFAULT_LINT_BLACKLIST_REGEX
+  black_regex = re.compile(black_list)
+
+  for filename in filenames:
+    # Ignore files from specific directories.
+    ignore = False
+    for dir_part in filename.split(os.sep):
+      if dir_part in IGNORE_DIRECTORIES:
+        msg(filename, "ignored")
+        ignore = True
+        break
+    if ignore:
+      continue
+
+    if filename.find('*') > 0:
+      # Expand wildcards.
+      filenames.extend(get_files(filename))
+      continue
+
+    if os.path.isdir(filename):
+      # Add directory contents.
+      filenames.extend(get_files(os.path.join(filename, "*")))
+      continue
+
+    if not os.path.exists(filename):
+      files = get_changed_files(".", filename)
+      if len(files) > 0:
+        filenames.extend(files)
+      else:
+        msg(filename, "missing")
+      continue
+
+    if white_regex.match(filename):
+      if black_regex.match(filename):
+        msg(filename, "ignored")
+      else:
+        update_file(filename)
+    else:
+      msg(filename, "skipped")
+
+
+if __name__ == "__main__":
+  if len(sys.argv) == 1:
+    print("Usage: %s [file-path|git-hash|unstaged|staged] ...\n" % sys.argv[0])
+    print(" Format C, C++ and ObjC files using Chromium's clang-format style.")
+    print("\nOptions:")
+    print(" file-path\tProcess the specified file or directory.")
+    print(" \t\tDirectories will be processed recursively.")
+    print(" \t\tThe \"*\" wildcard character is supported.")
+    print(" git-hash\tProcess all files changed in the specified Git commit.")
+    print(" unstaged\tProcess all unstaged files in the Git repo.")
+    print(" staged\t\tProcess all staged files in the Git repo.")
+    sys.exit(1)
+
+  # Read the configuration file.
+  read_config()
+
+  # Process anything passed on the command-line.
+  fix_style(sys.argv[1:])
+  print('Done - Wrote %d files.' % updatect)
diff --git a/src/tools/fix_style.sh b/src/tools/fix_style.sh
new file mode 100755
index 0000000..c466ab0
--- /dev/null
+++ b/src/tools/fix_style.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python tools/fix_style.py $@
diff --git a/src/tools/gclient_hook.py b/src/tools/gclient_hook.py
new file mode 100644
index 0000000..f0be2d4
--- /dev/null
+++ b/src/tools/gclient_hook.py
@@ -0,0 +1,156 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors.
+# Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from file_util import make_dir, write_file
+from gclient_util import *
+from gn_args import GetAllPlatformConfigs, GetConfigFileContents
+import issue_1999
+import os
+import sys
+
+# The CEF directory is the parent directory of _this_ script.
+cef_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
+# The src directory is the parent directory of the CEF directory.
+src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir))
+
+# Determine the platform.
+if sys.platform == 'win32':
+  platform = 'windows'
+elif sys.platform == 'darwin':
+  platform = 'macosx'
+elif sys.platform.startswith('linux'):
+  platform = 'linux'
+else:
+  print('Unknown operating system platform')
+  sys.exit()
+
+print("\nGenerating CEF version header file...")
+cmd = [sys.executable, 'tools/make_version_header.py', 'include/cef_version.h']
+RunAction(cef_dir, cmd)
+
+print("\nPatching build configuration and source files for CEF...")
+cmd = [sys.executable, 'tools/patcher.py']
+RunAction(cef_dir, cmd)
+
+if platform == 'linux' and 'CEF_INSTALL_SYSROOT' in os.environ:
+  for arch in os.environ['CEF_INSTALL_SYSROOT'].split(','):
+    if len(arch) == 0:
+      continue
+    print("\nInstalling %s sysroot environment..." % arch)
+    cmd = [
+        sys.executable, 'build/linux/sysroot_scripts/install-sysroot.py',
+        '--arch', arch
+    ]
+    RunAction(src_dir, cmd)
+
+print("\nGenerating CEF project files...")
+
+gn_args = {}
+
+if platform == 'windows':
+  # Force use of the locally installed version of Visual Studio.
+  if not 'DEPOT_TOOLS_WIN_TOOLCHAIN' in os.environ:
+    os.environ['DEPOT_TOOLS_WIN_TOOLCHAIN'] = '0'
+
+  # By default GN+Ninja on Windows expects Visual Studio to be installed on
+  # the local machine. To build when Visual Studio is extracted to a directory
+  # but not installed (e.g. via a custom toolchain) set the following
+  # environment variables:
+  #
+  # o Enable use of a custom toolchain on Windows.
+  #
+  #   set WIN_CUSTOM_TOOLCHAIN=1
+  #
+  # o Used by tools/msvs_env.bat to configure the MSVS tools environment.
+  #   Should be set to "none" because VC variables for CEF will be set via
+  #   INCLUDE/LIB/PATH.
+  #
+  #   set CEF_VCVARS=none
+  #
+  # o Used by the following scripts:
+  #   (a) build/vs_toolchain.py SetEnvironmentAndGetRuntimeDllDirs when
+  #   determining whether to copy VS runtime binaries to the output directory.
+  #   If GYP_MSVS_OVERRIDE_PATH exists then binaries will not be copied and
+  #   should instead be discoverable via the PATH env variable.
+  #   (b) build/toolchain/win/setup_toolchain.py _LoadToolchainEnv when
+  #   writing environment.* files that specify INCLUDE/LIB/PATH values. If
+  #   "%GYP_MSVS_OVERRIDE_PATH%\VC\vcvarsall.bat" exists then environment
+  #   variables will be derived from there and the specified INCLUDE/LIB/PATH
+  #   values will be ignored by Chromium. If this file does not exist then the
+  #   INCLUDE/LIB/PATH values are also required by Chromium.
+  #   TODO(cef): Rename to VS_ROOT and VS_VERSION after Chromium cleans up GYP
+  #   dependencies.
+  #
+  #   set GYP_MSVS_OVERRIDE_PATH=<VS root directory>
+  #   set GYP_MSVS_VERSION=<VS version>
+  #
+  # o Used to configure GN arguments in this script.
+  #
+  #   set VS_CRT_ROOT=<VS CRT root directory>
+  #   set SDK_ROOT=<Platform SDK root directory>
+  #
+  # o Used by various scripts as described above.
+  #   TODO(cef): Make these values optional when
+  #   "%GYP_MSVS_OVERRIDE_PATH%\VC\vcvarsall.bat" exists (use values from that
+  #   script instead).
+  #
+  #   set INCLUDE=<VS include paths>
+  #   set LIB=<VS library paths>
+  #   set PATH=<VS executable paths>
+  #
+  # See tools/depot_tools/win_toolchain/package_from_installed.py for an example
+  # packaging script along with required directory contents and INCLUDE/LIB/PATH
+  # values.
+  #
+  if bool(int(os.environ.get('WIN_CUSTOM_TOOLCHAIN', '0'))):
+    required_vars = [
+        'CEF_VCVARS',
+        'GYP_MSVS_OVERRIDE_PATH',
+        'GYP_MSVS_VERSION',
+        'VS_CRT_ROOT',
+        'SDK_ROOT',
+        'INCLUDE',
+        'LIB',
+        'PATH',
+    ]
+    for var in required_vars:
+      if not var in os.environ.keys():
+        raise Exception('%s environment variable must be set' % var)
+
+    # Windows custom toolchain requirements. See comments in gn_args.py.
+    gn_args['visual_studio_path'] = os.environ['GYP_MSVS_OVERRIDE_PATH']
+    gn_args['visual_studio_version'] = os.environ['GYP_MSVS_VERSION']
+    gn_args['visual_studio_runtime_dirs'] = os.environ['VS_CRT_ROOT']
+    gn_args['windows_sdk_path'] = os.environ['SDK_ROOT']
+
+configs = GetAllPlatformConfigs(gn_args)
+for dir, config in configs.items():
+  # Create out directories and write the args.gn file.
+  out_path = os.path.join(src_dir, 'out', dir)
+  make_dir(out_path, False)
+  args_gn_path = os.path.join(out_path, 'args.gn')
+  args_gn_contents = GetConfigFileContents(config)
+  write_file(args_gn_path, args_gn_contents)
+
+  # Generate the Ninja config.
+  cmd = ['gn', 'gen', os.path.join('out', dir)]
+  if 'GN_ARGUMENTS' in os.environ.keys():
+    cmd.extend(os.environ['GN_ARGUMENTS'].split(' '))
+  RunAction(src_dir, cmd)
+  if platform == 'windows':
+    issue_1999.apply(out_path)
+
+gn_dir = list(configs.keys())[0]
+out_gn_path = os.path.join(src_dir, 'out', gn_dir)
+gn_path = os.path.join(out_gn_path, 'args.gn')
+print("\nGenerating CEF buildinfo header file...")
+cmd = [
+    sys.executable, 'tools/make_config_header.py', '--header',
+    'include/cef_config.h', '--cef_gn_config', gn_path
+]
+
+RunAction(cef_dir, cmd)
diff --git a/src/tools/gclient_util.py b/src/tools/gclient_util.py
new file mode 100644
index 0000000..5a6da54
--- /dev/null
+++ b/src/tools/gclient_util.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors.
+# Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os, sys
+
+try:
+  # depot_tools may already be in the import path.
+  import gclient_utils
+except ImportError as e:
+  # Search the PATH environment variable to find the depot_tools folder.
+  depot_tools = None
+  paths = os.environ.get('PATH').split(os.pathsep)
+  for path in paths:
+    if os.path.exists(os.path.join(path, 'gclient_utils.py')):
+      depot_tools = path
+      break
+
+  if depot_tools is None:
+    print('Error: could not find depot_tools in PATH.', file=sys.stderr)
+    sys.exit(2)
+
+  # Add depot_tools to import path.
+  sys.path.append(depot_tools)
+  import gclient_utils
+
+
+# Copied from gclient.py python code.
+def RunAction(dir, command):
+  """Runs the action."""
+  try:
+    gclient_utils.CheckCallAndFilter(
+        command, cwd=dir, always_show_header=True, print_stdout=True)
+  except gclient_utils.Error as e:
+    # Use a discrete exit status code of 2 to indicate that a hook action
+    # failed.  Users of this script may wish to treat hook action failures
+    # differently from VC failures.
+    print('Error: %s' % str(e), file=sys.stderr)
+    sys.exit(2)
diff --git a/src/tools/git_util.py b/src/tools/git_util.py
new file mode 100644
index 0000000..3a3abb4
--- /dev/null
+++ b/src/tools/git_util.py
@@ -0,0 +1,172 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file
+
+from __future__ import absolute_import
+from exec_util import exec_cmd
+import os
+import sys
+
+if sys.platform == 'win32':
+  # Force use of the git version bundled with depot_tools.
+  git_exe = 'git.bat'
+else:
+  git_exe = 'git'
+
+
+def is_checkout(path):
+  """ Returns true if the path represents a git checkout. """
+  return os.path.isdir(os.path.join(path, '.git'))
+
+
+def is_ancestor(path='.', commit1='HEAD', commit2='master'):
+  """ Returns whether |commit1| is an ancestor of |commit2|. """
+  cmd = "%s merge-base --is-ancestor %s %s" % (git_exe, commit1, commit2)
+  result = exec_cmd(cmd, path)
+  return result['ret'] == 0
+
+
+def get_hash(path='.', branch='HEAD'):
+  """ Returns the git hash for the specified branch/tag/hash. """
+  cmd = "%s rev-parse %s" % (git_exe, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return result['out'].strip()
+  return 'Unknown'
+
+
+def get_branch_name(path='.', branch='HEAD'):
+  """ Returns the branch name for the specified branch/tag/hash. """
+  # Returns the branch name if not in detached HEAD state, else an empty string
+  # or "HEAD".
+  cmd = "%s rev-parse --abbrev-ref %s" % (git_exe, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    name = result['out'].strip()
+    if len(name) > 0 and name != 'HEAD':
+      return name
+
+    # Returns a value like "(HEAD, origin/3729, 3729)".
+    # Ubuntu 14.04 uses Git version 1.9.1 which does not support %D (which
+    # provides the same output but without the parentheses).
+    cmd = "%s log -n 1 --pretty=%%d %s" % (git_exe, branch)
+    result = exec_cmd(cmd, path)
+    if result['out'] != '':
+      return result['out'].strip()[1:-1].split(', ')[-1]
+  return 'Unknown'
+
+
+def get_url(path='.'):
+  """ Returns the origin url for the specified path. """
+  cmd = "%s config --get remote.origin.url" % git_exe
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return result['out'].strip()
+  return 'Unknown'
+
+
+def get_commit_number(path='.', branch='HEAD'):
+  """ Returns the number of commits in the specified branch/tag/hash. """
+  cmd = "%s rev-list --count %s" % (git_exe, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return result['out'].strip()
+  return '0'
+
+
+def get_changed_files(path, hash):
+  """ Retrieves the list of changed files. """
+  if hash == 'unstaged':
+    cmd = "%s diff --name-only" % git_exe
+  elif hash == 'staged':
+    cmd = "%s diff --name-only --cached" % git_exe
+  else:
+    cmd = "%s diff-tree --no-commit-id --name-only -r %s" % (git_exe, hash)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    files = result['out']
+    if sys.platform == 'win32':
+      # Convert to Unix line endings.
+      files = files.replace('\r\n', '\n')
+    return files.strip().split("\n")
+  return []
+
+
+def get_branch_hashes(path='.', branch='HEAD', ref='origin/master'):
+  """ Returns an ordered list of hashes for commits that have been applied since
+      branching from ref. """
+  cmd = "%s cherry %s %s" % (git_exe, ref, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    hashes = result['out']
+    if sys.platform == 'win32':
+      # Convert to Unix line endings.
+      hashes = hashes.replace('\r\n', '\n')
+    # Remove the "+ " or "- " prefix.
+    return [line[2:] for line in hashes.strip().split('\n')]
+  return []
+
+
+def write_indented_output(output):
+  """ Apply a fixed amount of intent to lines before printing. """
+  if output == '':
+    return
+  for line in output.split('\n'):
+    line = line.strip()
+    if len(line) == 0:
+      continue
+    sys.stdout.write('\t%s\n' % line)
+
+
+def git_apply_patch_file(patch_path, patch_dir):
+  """ Apply |patch_path| to files in |patch_dir|. """
+  patch_name = os.path.basename(patch_path)
+  sys.stdout.write('\nApply %s in %s\n' % (patch_name, patch_dir))
+
+  if not os.path.isfile(patch_path):
+    sys.stdout.write('... patch file does not exist.\n')
+    return 'fail'
+
+  patch_string = open(patch_path, 'rb').read()
+  if sys.platform == 'win32':
+    # Convert the patch to Unix line endings. This is necessary to avoid
+    # whitespace errors with git apply.
+    patch_string = patch_string.replace(b'\r\n', b'\n')
+
+  # Git apply fails silently if not run relative to a respository root.
+  if not is_checkout(patch_dir):
+    sys.stdout.write('... patch directory is not a repository root.\n')
+    return 'fail'
+
+  config = '-p0 --ignore-whitespace'
+
+  # Output patch contents.
+  cmd = '%s apply %s --numstat' % (git_exe, config)
+  result = exec_cmd(cmd, patch_dir, patch_string)
+  write_indented_output(result['out'].replace('<stdin>', patch_name))
+
+  # Reverse check to see if the patch has already been applied.
+  cmd = '%s apply %s --reverse --check' % (git_exe, config)
+  result = exec_cmd(cmd, patch_dir, patch_string)
+  if result['err'].find('error:') < 0:
+    sys.stdout.write('... already applied (skipping).\n')
+    return 'skip'
+
+  # Normal check to see if the patch can be applied cleanly.
+  cmd = '%s apply %s --check' % (git_exe, config)
+  result = exec_cmd(cmd, patch_dir, patch_string)
+  if result['err'].find('error:') >= 0:
+    sys.stdout.write('... failed to apply:\n')
+    write_indented_output(result['err'].replace('<stdin>', patch_name))
+    return 'fail'
+
+  # Apply the patch file. This should always succeed because the previous
+  # command succeeded.
+  cmd = '%s apply %s' % (git_exe, config)
+  result = exec_cmd(cmd, patch_dir, patch_string)
+  if result['err'] == '':
+    sys.stdout.write('... successfully applied.\n')
+  else:
+    sys.stdout.write('... successfully applied (with warnings):\n')
+    write_indented_output(result['err'].replace('<stdin>', patch_name))
+  return 'apply'
diff --git a/src/tools/gn_args.py b/src/tools/gn_args.py
new file mode 100644
index 0000000..1d118f9
--- /dev/null
+++ b/src/tools/gn_args.py
@@ -0,0 +1,576 @@
+# Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
+# 2012 Google Inc. All rights reserved. Use of this source code is governed by
+# a BSD-style license that can be found in the LICENSE file.
+
+# This script determines the contents of the per-configuration `args.gn` files
+# that are used to build CEF/Chromium with GN. See comments in CEF's top-level
+# BUILD.gn file for general GN usage instructions.
+#
+# This script performs the following tasks:
+#
+# - Defines CEF's default and required arg values in cases where they differ
+#   from Chromium's.
+# - Accepts user-defined arg values via the GN_DEFINES environment variable.
+# - Verifies that user-defined arg values do not conflict with CEF's
+#   requirements.
+# - Generates multiple configurations by combining user-defined arg values with
+#   CEF's default and required values.
+#
+# Before adding a new arg value in this script determine the following:
+#
+# - Chromium's default value. Default values are defined in the declare_args()
+#   sections of *.gni files.
+# - Chromium's value requirements. Check for assert()s related to the value in
+#   Chromium code.
+# - Whether a particular value is optional or required for CEF.
+# - Under what conditions a particular value is required for CEF (platform,
+#   build type, CPU architecture, etc).
+#
+# If CEF can use Chromium's default value and has no additional validation
+# requirements then do nothing.
+#
+# If CEF can use Chromium's default value but would like to enforce additional
+# validation requirements then go to 3B.
+#
+# If CEF cannot or should not use Chromium's default value then choose one of
+# the following:
+#
+# 1. If CEF requires a different value either globally or based on the platform:
+#  - Add an assert() for the value in CEF's top-level BUILD.gn file.
+#  - Add the required value in GetRequiredArgs().
+#  - Result: CEF's required value will be used. The user cannot override the
+#    value via GN_DEFINES.
+#
+# 2. If CEF requires a different value based on the build type or CPU
+#    architecture:
+#  - Add an assert() for the value in CEF's top-level BUILD.gn file.
+#  - Add the required value in GetConfigArgs().
+#  - Result: CEF's required value will be used. The user cannot override the
+#    value via GN_DEFINES.
+#
+# 3. If CEF recommends (but does not require) a different value either globally
+#    or based on the platform:
+#    A. Set the default value:
+#     - Add the recommended value in GetRecommendedDefaultArgs().
+#     - Result: CEF's recommended value will be used by default. The user can
+#       override the value via GN_DEFINES.
+#
+#    B. If CEF has additional validation requirements:
+#     - Add the default Chromium value in GetChromiumDefaultArgs().
+#     - Perform validation in ValidateArgs().
+#     - Result: An AssertionError will be thrown if validation fails.
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os
+import shlex
+import sys
+
+# The CEF directory is the parent directory of _this_ script.
+cef_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
+# The src directory is the parent directory of the CEF directory.
+src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir))
+
+# Determine the platform.
+if sys.platform == 'win32':
+  platform = 'windows'
+elif sys.platform == 'darwin':
+  platform = 'macosx'
+elif sys.platform.startswith('linux'):
+  platform = 'linux'
+else:
+  print('Unknown operating system platform')
+  sys.exit()
+
+
+def msg(msg):
+  print('NOTE: ' + msg)
+
+
+def NameValueListToDict(name_value_list):
+  """
+  Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary
+  of the pairs. If a string is simply NAME, then the value in the dictionary
+  is set to True. If VALUE can be converted to a boolean or integer, it is.
+  """
+  result = {}
+  for item in name_value_list:
+    tokens = item.split('=', 1)
+    if len(tokens) == 2:
+      token_value = tokens[1]
+      if token_value.lower() == 'true':
+        token_value = True
+      elif token_value.lower() == 'false':
+        token_value = False
+      else:
+        # If we can make it an int, use that, otherwise, use the string.
+        try:
+          token_value = int(token_value)
+        except ValueError:
+          pass
+      # Set the variable to the supplied value.
+      result[tokens[0]] = token_value
+    else:
+      # No value supplied, treat it as a boolean and set it.
+      result[tokens[0]] = True
+  return result
+
+
+def ShlexEnv(env_name):
+  """
+  Split an environment variable using shell-like syntax.
+  """
+  flags = os.environ.get(env_name, [])
+  if flags:
+    flags = shlex.split(flags)
+  return flags
+
+
+def MergeDicts(*dict_args):
+  """
+  Given any number of dicts, shallow copy and merge into a new dict.
+  Precedence goes to key value pairs in latter dicts.
+  """
+  result = {}
+  for dictionary in dict_args:
+    result.update(dictionary)
+  return result
+
+
+def GetValueString(val):
+  """
+  Return the string representation of |val| expected by GN.
+  """
+  if isinstance(val, bool):
+    if val:
+      return 'true'
+    else:
+      return 'false'
+  elif isinstance(val, int):
+    return val
+  else:
+    return '"%s"' % val
+  return val
+
+
+def GetChromiumDefaultArgs():
+  """
+  Return default GN args. These must match the Chromium defaults.
+  Only args that may be retrieved via GetArgValue() need to be specified here.
+  """
+  # Search for these values in declare_args() sections of *.gni files to find
+  # the defaults.
+
+  defaults = {
+      'dcheck_always_on': False,
+      'is_asan': False,
+      'is_debug': True,
+      'is_official_build': False,
+      'target_cpu': 'x64',
+  }
+
+  if platform == 'linux':
+    defaults['use_sysroot'] = True
+
+  if platform == 'windows':
+    defaults['is_win_fastlink'] = False
+    defaults['visual_studio_path'] = ''
+    defaults['visual_studio_version'] = ''
+    defaults['visual_studio_runtime_dirs'] = ''
+    defaults['windows_sdk_path'] = ''
+
+  return defaults
+
+
+def GetArgValue(args, key):
+  """
+  Return an existing GN arg value or the Chromium default.
+  """
+  defaults = GetChromiumDefaultArgs()
+  assert key in defaults, "No default Chromium value specified for %s" % key
+  return args.get(key, defaults[key])
+
+
+def GetRecommendedDefaultArgs():
+  """
+  Return recommended default GN args that differ from Chromium defaults.
+  """
+  # Search for these values in declare_args() sections of *.gni files to find
+  # the defaults.
+
+  result = {
+      # Enable NaCL. Default is true. False is recommended for faster builds.
+      'enable_nacl': False,
+
+      # Disable component builds. Default depends on the platform. True results
+      # in faster local builds but False is required to create a CEF binary
+      # distribution.
+      'is_component_build': False,
+
+      # Don't enforce component builds in debug mode
+      'forbid_non_component_debug_builds': False,
+  }
+
+  if platform == 'linux':
+    # Use a sysroot environment. Default is true. False is recommended for local
+    # builds.
+    # Run the following commands to download the sysroot environment:
+    # x86 build only:   $ export GYP_DEFINES='target_arch=ia32'
+    # x86 or x64 build: $ gclient runhooks
+    result['use_sysroot'] = False
+
+    # Don't add the `-Wl,--fatal-warnings` linker flag when building on Ubuntu
+    # 14 (Trusty) host systems. It results in errors like the following:
+    # ld.lld: error: found local symbol '__bss_start' in global part of symbol
+    # table in file /usr/lib/x86_64-linux-gnu/libGL.so
+    # TODO(cef): Remove this flag once we require a newer host system.
+    result['fatal_linker_warnings'] = False
+
+  return result
+
+
+def GetGNEnvArgs():
+  """
+  Return GN args specified via the GN_DEFINES env variable.
+  """
+  return NameValueListToDict(ShlexEnv('GN_DEFINES'))
+
+
+def GetRequiredArgs():
+  """
+  Return required GN args. Also enforced by assert() in //cef/BUILD.gn.
+  """
+  result = {
+      # Set ENABLE_PRINTING=1 ENABLE_BASIC_PRINTING=1.
+      'enable_basic_printing': True,
+      # ENABLE_SERVICE_DISCOVERY=0 for print preview support
+      'enable_print_preview': True,
+      'optimize_webui': True,
+      # Enable support for Widevine CDM.
+      'enable_widevine': True,
+
+      # Don't use the chrome style plugin.
+      'clang_use_chrome_plugins': False,
+  }
+
+  if platform == 'linux':
+    # Don't generate Chromium installer packages. This avoids GN dependency
+    # errors with CEF (see issue #2301).
+    # Due to the way this variable is declared in chrome/installer/BUILD.gn it
+    # can't be enforced by assert().
+    result['enable_linux_installer'] = False
+
+    # Build without GTK dependencies (see issue #2014).
+    result['use_gtk'] = False
+
+  if platform == 'macosx':
+    # Always generate dSYM files. The make_distrib script will fail if
+    # enable_dsyms=true is not explicitly set when is_official_build=false.
+    result['enable_dsyms'] = True
+
+  return result
+
+
+def GetMergedArgs(build_args):
+  """
+  Return merged GN args.
+  """
+  dict = MergeDicts(GetRecommendedDefaultArgs(), GetGNEnvArgs(), build_args)
+
+  # Verify that the user is not trying to override required args.
+  required = GetRequiredArgs()
+  for key in required.keys():
+    if key in dict:
+      assert dict[key] == required[key], \
+          "%s=%s is required" % (key, GetValueString(required[key]))
+
+  return MergeDicts(dict, required)
+
+
+def ValidateArgs(args):
+  """
+  Validate GN arg combinations that we know about. Also provide suggestions
+  where appropriate.
+  """
+  dcheck_always_on = GetArgValue(args, 'dcheck_always_on')
+  is_asan = GetArgValue(args, 'is_asan')
+  is_debug = GetArgValue(args, 'is_debug')
+  is_official_build = GetArgValue(args, 'is_official_build')
+  target_cpu = GetArgValue(args, 'target_cpu')
+
+  if platform == 'linux':
+    use_sysroot = GetArgValue(args, 'use_sysroot')
+
+  if platform == 'windows':
+    is_win_fastlink = GetArgValue(args, 'is_win_fastlink')
+    visual_studio_path = GetArgValue(args, 'visual_studio_path')
+    visual_studio_version = GetArgValue(args, 'visual_studio_version')
+    visual_studio_runtime_dirs = GetArgValue(args, 'visual_studio_runtime_dirs')
+    windows_sdk_path = GetArgValue(args, 'windows_sdk_path')
+
+  # Target CPU architecture.
+  # - Windows supports "x86" and "x64".
+  # - Mac supports only "x64".
+  # - Linux supports only "x64" unless using a sysroot environment.
+  if platform == 'macosx':
+    assert target_cpu == 'x64', 'target_cpu must be "x64"'
+  elif platform == 'windows':
+    assert target_cpu in (
+        'x86', 'x64', 'arm64'), 'target_cpu must be "x86", "x64" or "arm64"'
+  elif platform == 'linux':
+    assert target_cpu in (
+        'x86', 'x64', 'arm',
+        'arm64'), 'target_cpu must be "x86", "x64", "arm" or "arm64"'
+
+  if platform == 'linux':
+    if target_cpu == 'x86':
+      assert use_sysroot, 'target_cpu="x86" requires use_sysroot=true'
+    elif target_cpu == 'arm':
+      assert use_sysroot, 'target_cpu="arm" requires use_sysroot=true'
+    elif target_cpu == 'arm64':
+      assert use_sysroot, 'target_cpu="arm64" requires use_sysroot=true'
+
+  # ASAN requires Release builds.
+  if is_asan:
+    assert not is_debug, "is_asan=true requires is_debug=false"
+    if not dcheck_always_on:
+      msg('is_asan=true recommends dcheck_always_on=true')
+
+  # Official build requires Release builds.
+  if is_official_build:
+    assert not is_debug, "is_official_build=true requires is_debug=false"
+
+  if platform == 'windows':
+    # Official builds should not use /DEBUG:FASTLINK.
+    if is_official_build:
+      assert not is_win_fastlink, "is_official_build=true precludes is_win_fastlink=true"
+
+    # Windows custom toolchain requirements.
+    #
+    # Required GN arguments:
+    #   visual_studio_path="<path to VS root>"
+    #     The directory that contains Visual Studio. For example, a subset of
+    #     "C:\Program Files (x86)\Microsoft Visual Studio 14.0".
+    #   visual_studio_version="<VS version>"
+    #     The VS version. For example, "2015".
+    #   visual_studio_runtime_dirs="<path to VS CRT>"
+    #     The directory that contains the VS CRT. For example, the contents of
+    #     "C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64" plus
+    #     "C:\Windows\System32\ucrtbased.dll"
+    #   windows_sdk_path="<path to WinSDK>"
+    #     The directory that contains the Win SDK. For example, a subset of
+    #     "C:\Program Files (x86)\Windows Kits\10".
+    #
+    # Required environment variables:
+    #   DEPOT_TOOLS_WIN_TOOLCHAIN=0
+    #   GYP_MSVS_OVERRIDE_PATH=<path to VS root, must match visual_studio_path>
+    #   GYP_MSVS_VERSION=<VS version, must match visual_studio_version>
+    #   CEF_VCVARS=none
+    #   INCLUDE=<VS include paths>
+    #   LIB=<VS library paths>
+    #   PATH=<VS executable paths>
+    #
+    # See comments in gclient_hook.py for environment variable usage.
+    #
+    if visual_studio_path != '':
+      assert visual_studio_version != '', 'visual_studio_path requires visual_studio_version'
+      assert visual_studio_runtime_dirs != '', 'visual_studio_path requires visual_studio_runtime_dirs'
+      assert windows_sdk_path != '', 'visual_studio_path requires windows_sdk_path'
+
+      assert os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '') == '0', \
+        "visual_studio_path requires DEPOT_TOOLS_WIN_TOOLCHAIN=0 env variable"
+
+      msvs_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH', '')
+      assert msvs_path == visual_studio_path and os.path.exists(msvs_path), \
+        "visual_studio_path requires matching GYP_MSVS_OVERRIDE_PATH env variable"
+
+      msvs_version = os.environ.get('GYP_MSVS_VERSION', '')
+      assert msvs_version == visual_studio_version, \
+        "visual_studio_version requires matching GYP_MSVS_VERSION env variable"
+
+      assert os.environ.get('CEF_VCVARS', '') == 'none', \
+        "visual_studio_path requires CEF_VCVARS=none env variable"
+
+      assert 'INCLUDE' in os.environ \
+        and 'LIB' in os.environ \
+        and 'PATH' in os.environ, \
+        "visual_studio_path requires INCLUDE, LIB and PATH env variables"
+
+      # If "%GYP_MSVS_OVERRIDE_PATH%\VC\vcvarsall.bat" exists then environment
+      # variables will be derived from there and the specified INCLUDE/LIB/PATH
+      # values will be ignored by Chromium. If this file does not exist then the
+      # INCLUDE/LIB/PATH values are also required by Chromium.
+      vcvars_path = os.path.join(msvs_path, 'VC', 'vcvarsall.bat')
+      if (os.path.exists(vcvars_path)):
+        msg('INCLUDE/LIB/PATH values will be derived from %s' % vcvars_path)
+
+
+def GetConfigArgs(args, is_debug, cpu):
+  """
+  Return merged GN args for the configuration and validate.
+  """
+  add_args = {}
+
+  # Cannot create is_official_build=true is_debug=true builds.
+  # This restriction is enforced in //build/config/BUILDCONFIG.gn.
+  # Instead, our "official Debug" build is a Release build with dchecks and
+  # symbols. Symbols will be generated by default for official builds; see the
+  # definition of 'symbol_level' in //build/config/compiler/compiler.gni.
+  if is_debug and GetArgValue(args, 'is_official_build'):
+    is_debug = False
+    add_args['dcheck_always_on'] = True
+
+  result = MergeDicts(args, add_args, {
+      'is_debug': is_debug,
+      'target_cpu': cpu,
+  })
+
+  if platform == 'linux' and not cpu.startswith('arm'):
+    # Remove any arm-related values from non-arm configs.
+    for key in result.keys():
+      if key.startswith('arm_'):
+        del result[key]
+
+  ValidateArgs(result)
+  return result
+
+
+def GetConfigArgsSandbox(platform, args, is_debug, cpu):
+  """
+  Return merged GN args for the cef_sandbox configuration and validate.
+  """
+  add_args = {
+      # Avoid libucrt.lib linker errors.
+      'use_allocator_shim': False,
+
+      # Avoid /LTCG linker warnings and generate smaller lib files.
+      'is_official_build': False,
+
+      # Enable base target customizations necessary for distribution of the
+      # cef_sandbox static library.
+      'is_cef_sandbox_build': True,
+  }
+
+  if is_debug:
+    # Enable iterator debugging (_ITERATOR_DEBUG_LEVEL=2).
+    add_args['enable_iterator_debugging'] = True
+
+  if platform == 'windows':
+    # Avoid Debug build linker errors caused by custom libc++.
+    add_args['use_custom_libcxx'] = False
+
+  result = MergeDicts(args, add_args, {
+      'is_debug': is_debug,
+      'target_cpu': cpu,
+  })
+
+  ValidateArgs(result)
+  return result
+
+
+def LinuxSysrootExists(cpu):
+  """
+  Returns true if the sysroot for the specified |cpu| architecture exists.
+  """
+  # Directory that contains sysroots.
+  sysroot_root = os.path.join(src_dir, 'build', 'linux')
+  # CPU-specific sysroot directory names.
+  # Should match the values in build/config/sysroot.gni.
+  if cpu == 'x86':
+    sysroot_name = 'debian_sid_i386-sysroot'
+  elif cpu == 'x64':
+    sysroot_name = 'debian_sid_amd64-sysroot'
+  elif cpu == 'arm':
+    sysroot_name = 'debian_sid_arm-sysroot'
+  elif cpu == 'arm64':
+    sysroot_name = 'debian_sid_arm64-sysroot'
+  else:
+    raise Exception('Unrecognized sysroot CPU: %s' % cpu)
+
+  return os.path.isdir(os.path.join(sysroot_root, sysroot_name))
+
+
+def GetAllPlatformConfigs(build_args):
+  """
+  Return a map of directory name to GN args for the current platform.
+  """
+  result = {}
+
+  # Merged args without validation.
+  args = GetMergedArgs(build_args)
+
+  create_debug = True
+
+  # Don't create debug directories for asan builds.
+  if GetArgValue(args, 'is_asan'):
+    create_debug = False
+    msg('Not generating Debug configuration due to is_asan=true')
+
+  supported_cpus = []
+
+  if platform == 'linux':
+    use_sysroot = GetArgValue(args, 'use_sysroot')
+    if use_sysroot:
+      # Only generate configurations for sysroots that have been installed.
+      for cpu in ('x86', 'x64', 'arm', 'arm64'):
+        if LinuxSysrootExists(cpu):
+          supported_cpus.append(cpu)
+        else:
+          msg('Not generating %s configuration due to missing sysroot directory'
+              % cpu)
+    else:
+      supported_cpus = ['x64']
+  elif platform == 'windows':
+    supported_cpus = ['x86', 'x64']
+    if os.environ.get('CEF_ENABLE_ARM64', '') == '1':
+      supported_cpus.append('arm64')
+  elif platform == 'macosx':
+    supported_cpus = ['x64']
+  else:
+    raise Exception('Unsupported platform')
+
+  for cpu in supported_cpus:
+    if create_debug:
+      result['Debug_GN_' + cpu] = GetConfigArgs(args, True, cpu)
+    result['Release_GN_' + cpu] = GetConfigArgs(args, False, cpu)
+
+    if platform in ('windows', 'macosx') and GetArgValue(
+        args, 'is_official_build'):
+      # Build cef_sandbox.lib with a different configuration.
+      if create_debug:
+        result['Debug_GN_' + cpu + '_sandbox'] = GetConfigArgsSandbox(
+            platform, args, True, cpu)
+      result['Release_GN_' + cpu + '_sandbox'] = GetConfigArgsSandbox(
+          platform, args, False, cpu)
+
+  return result
+
+
+def GetConfigFileContents(args):
+  """
+  Generate config file contents for the arguments.
+  """
+  pairs = []
+  for k in sorted(args.keys()):
+    pairs.append("%s=%s" % (k, GetValueString(args[k])))
+  return "\n".join(pairs)
+
+
+# Program entry point.
+if __name__ == '__main__':
+  import sys
+
+  # Allow override of the platform via the command-line for testing.
+  if len(sys.argv) > 1:
+    platform = sys.argv[1]
+    if not platform in ('linux', 'macosx', 'windows'):
+      sys.stderr.write('Usage: %s <platform>\n' % sys.argv[0])
+      sys.exit()
+
+  print('Platform: %s' % platform)
+
+  # Dump the configuration based on platform and environment.
+  configs = GetAllPlatformConfigs({})
+  for dir, config in configs.items():
+    print('\n\nout/%s:\n' % dir)
+    print(GetConfigFileContents(config))
diff --git a/src/tools/gypi_to_gn.py b/src/tools/gypi_to_gn.py
new file mode 100644
index 0000000..3782535
--- /dev/null
+++ b/src/tools/gypi_to_gn.py
@@ -0,0 +1,206 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# Deleted from Chromium in https://crrev.com/097f64c631.
+
+"""Converts a given gypi file to a python scope and writes the result to stdout.
+
+USING THIS SCRIPT IN CHROMIUM
+
+Forking Python to run this script in the middle of GN is slow, especially on
+Windows, and it makes both the GYP and GN files harder to follow. You can't
+use "git grep" to find files in the GN build any more, and tracking everything
+in GYP down requires a level of indirection. Any calls will have to be removed
+and cleaned up once the GYP-to-GN transition is complete.
+
+As a result, we only use this script when the list of files is large and
+frequently-changing. In these cases, having one canonical list outweights the
+downsides.
+
+As of this writing, the GN build is basically complete. It's likely that all
+large and frequently changing targets where this is appropriate use this
+mechanism already. And since we hope to turn down the GYP build soon, the time
+horizon is also relatively short. As a result, it is likely that no additional
+uses of this script should every be added to the build. During this later part
+of the transition period, we should be focusing more and more on the absolute
+readability of the GN build.
+
+
+HOW TO USE
+
+It is assumed that the file contains a toplevel dictionary, and this script
+will return that dictionary as a GN "scope" (see example below). This script
+does not know anything about GYP and it will not expand variables or execute
+conditions.
+
+It will strip conditions blocks.
+
+A variables block at the top level will be flattened so that the variables
+appear in the root dictionary. This way they can be returned to the GN code.
+
+Say your_file.gypi looked like this:
+  {
+     'sources': [ 'a.cc', 'b.cc' ],
+     'defines': [ 'ENABLE_DOOM_MELON' ],
+  }
+
+You would call it like this:
+  gypi_values = exec_script("//build/gypi_to_gn.py",
+                            [ rebase_path("your_file.gypi") ],
+                            "scope",
+                            [ "your_file.gypi" ])
+
+Notes:
+ - The rebase_path call converts the gypi file from being relative to the
+   current build file to being system absolute for calling the script, which
+   will have a different current directory than this file.
+
+ - The "scope" parameter tells GN to interpret the result as a series of GN
+   variable assignments.
+
+ - The last file argument to exec_script tells GN that the given file is a
+   dependency of the build so Ninja can automatically re-run GN if the file
+   changes.
+
+Read the values into a target like this:
+  component("mycomponent") {
+    sources = gypi_values.sources
+    defines = gypi_values.defines
+  }
+
+Sometimes your .gypi file will include paths relative to a different
+directory than the current .gn file. In this case, you can rebase them to
+be relative to the current directory.
+  sources = rebase_path(gypi_values.sources, ".",
+                        "//path/gypi/input/values/are/relative/to")
+
+This script will tolerate a 'variables' in the toplevel dictionary or not. If
+the toplevel dictionary just contains one item called 'variables', it will be
+collapsed away and the result will be the contents of that dictinoary. Some
+.gypi files are written with or without this, depending on how they expect to
+be embedded into a .gyp file.
+
+This script also has the ability to replace certain substrings in the input.
+Generally this is used to emulate GYP variable expansion. If you passed the
+argument "--replace=<(foo)=bar" then all instances of "<(foo)" in strings in
+the input will be replaced with "bar":
+
+  gypi_values = exec_script("//build/gypi_to_gn.py",
+                            [ rebase_path("your_file.gypi"),
+                              "--replace=<(foo)=bar"],
+                            "scope",
+                            [ "your_file.gypi" ])
+
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+from optparse import OptionParser
+import os
+import sys
+
+try:
+  # May already be in the import path.
+  import gn_helpers
+except ImportError as e:
+  # Add src/build to import path.
+  cef_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
+  src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir))
+  sys.path.append(os.path.join(src_dir, 'build'))
+  import gn_helpers
+
+
+def LoadPythonDictionary(path):
+  file_string = open(path).read()
+  try:
+    file_data = eval(file_string, {'__builtins__': None}, None)
+  except SyntaxError as e:
+    e.filename = path
+    raise
+  except Exception as e:
+    raise Exception("Unexpected error while reading %s: %s" % (path, str(e)))
+
+  assert isinstance(file_data, dict), "%s does not eval to a dictionary" % path
+
+  # Flatten any variables to the top level.
+  if 'variables' in file_data:
+    file_data.update(file_data['variables'])
+    del file_data['variables']
+
+  # Strip all elements that this script can't process.
+  elements_to_strip = [
+    'conditions',
+    'target_conditions',
+    'target_defaults',
+    'targets',
+    'includes',
+    'actions',
+  ]
+  for element in elements_to_strip:
+    if element in file_data:
+      del file_data[element]
+
+  return file_data
+
+
+def ReplaceSubstrings(values, search_for, replace_with):
+  """Recursively replaces substrings in a value.
+
+  Replaces all substrings of the "search_for" with "repace_with" for all
+  strings occurring in "values". This is done by recursively iterating into
+  lists as well as the keys and values of dictionaries."""
+  if isinstance(values, str):
+    return values.replace(search_for, replace_with)
+
+  if isinstance(values, list):
+    return [ReplaceSubstrings(v, search_for, replace_with) for v in values]
+
+  if isinstance(values, dict):
+    # For dictionaries, do the search for both the key and values.
+    result = {}
+    for key, value in values.items():
+      new_key = ReplaceSubstrings(key, search_for, replace_with)
+      new_value = ReplaceSubstrings(value, search_for, replace_with)
+      result[new_key] = new_value
+    return result
+
+  # Assume everything else is unchanged.
+  return values
+
+def main():
+  parser = OptionParser()
+  parser.add_option("-r", "--replace", action="append",
+    help="Replaces substrings. If passed a=b, replaces all substrs a with b.")
+  (options, args) = parser.parse_args()
+
+  if len(args) != 1:
+    raise Exception("Need one argument which is the .gypi file to read.")
+
+  data = LoadPythonDictionary(args[0])
+  if options.replace:
+    # Do replacements for all specified patterns.
+    for replace in options.replace:
+      split = replace.split('=')
+      # Allow "foo=" to replace with nothing.
+      if len(split) == 1:
+        split.append('')
+      assert len(split) == 2, "Replacement must be of the form 'key=value'."
+      data = ReplaceSubstrings(data, split[0], split[1])
+
+  # Sometimes .gypi files use the GYP syntax with percents at the end of the
+  # variable name (to indicate not to overwrite a previously-defined value):
+  #   'foo%': 'bar',
+  # Convert these to regular variables.
+  for key in data:
+    if len(key) > 1 and key[len(key) - 1] == '%':
+      data[key[:-1]] = data[key]
+      del data[key]
+
+  print(gn_helpers.ToGNString(data))
+
+if __name__ == '__main__':
+  try:
+    main()
+  except Exception as e:
+    print(str(e))
+    sys.exit(1)
diff --git a/src/tools/issue_1999.py b/src/tools/issue_1999.py
new file mode 100644
index 0000000..0143690
--- /dev/null
+++ b/src/tools/issue_1999.py
@@ -0,0 +1,107 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+#
+# Resort order of object files in libcef.ninja file.
+#
+# See: https://bitbucket.org/chromiumembedded/cef/issues/1999
+#
+# Usage:
+#   import issue_1999
+#   issue_1999.apply(output_path)
+#
+from __future__ import absolute_import
+from __future__ import print_function
+from io import open
+import sys
+import os
+
+module_order = [
+    "_sse",
+    "-sse",
+    "_ssse",
+    "-ssse",
+    "_sse2",
+    "-sse2",
+    "_ssse2",
+    "-ssse2",
+    "_sse3",
+    "-sse3",
+    "_ssse3",
+    "-ssse3",
+    "_sse4",
+    "-sse4",
+    "_ssse4",
+    "-ssse4",
+    "_avx",
+    "-avx",
+    "_savx",
+    "-savx",
+    "_avx1",
+    "-avx1",
+    "_savx1",
+    "-savx1",
+    "_avx2",
+    "-avx2",
+    "_savx2",
+    "-savx2",
+]
+
+
+def get_obj_class(item):
+  item = item.lower()
+  for i in range(len(module_order) - 1, -1, -1):
+    x = module_order[i]
+    if x in item:
+      return 1 + i
+  return 0
+
+
+def obj_compare(x, y):
+  xc = get_obj_class(x)
+  yc = get_obj_class(y)
+  if xc < yc:
+    return -1
+  elif xc > yc:
+    return 1
+  else:
+    return 0
+
+
+def process_line(line):
+  if line.startswith("build ./libcef.dll ./libcef.dll.lib: solink "):
+    index = line.find("solink")
+    if index >= 0:
+      part1 = line[0:index + 6]
+      part2 = line[index + 6:]
+      stampsIndex = part2.find("||")
+      stamps = part2[stampsIndex:]
+      objects = part2[:stampsIndex]
+
+      objects_list = objects.split()
+      objects_list = sorted(objects_list, cmp=obj_compare)
+      return part1 + " " + " ".join(objects_list) + " " + stamps
+  return line
+
+
+def process_file(path):
+  print("Applying issue #1999 fix to " + path)
+
+  with open(path, 'r', encoding='utf-8') as f:
+    content = f.read().splitlines()
+
+  result = []
+
+  for line in content:
+    result.append(process_line(line))
+
+  with open(path, 'w', encoding='utf-8') as fp:
+    str = "\n".join(result) + "\n"
+    if sys.version_info.major == 2:
+      fp.write(str.decode('utf-8'))
+    else:
+      fp.write(str)
+
+
+def apply(confpath):
+  process_file(os.path.join(confpath, "obj", "cef", "libcef.ninja"))
diff --git a/src/tools/make_api_hash_header.py b/src/tools/make_api_hash_header.py
new file mode 100644
index 0000000..2d565f8
--- /dev/null
+++ b/src/tools/make_api_hash_header.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_api_hash import cef_api_hash
+from cef_parser import get_copyright
+from file_util import *
+import os
+import sys
+
+
+def make_api_hash_header(cpp_header_dir):
+  # calculate api hashes
+  api_hash_calculator = cef_api_hash(cpp_header_dir, verbose=False)
+  api_hash = api_hash_calculator.calculate()
+
+  result = get_copyright(full=True, translator=False) + \
+"""//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the make_api_hash_header.py tool.
+//
+
+#ifndef CEF_INCLUDE_API_HASH_H_
+#define CEF_INCLUDE_API_HASH_H_
+
+#include "include/internal/cef_export.h"
+
+// The API hash is created by analyzing CEF header files for C API type
+// definitions. The hash value will change when header files are modified in a
+// way that may cause binary incompatibility with other builds. The universal
+// hash value will change if any platform is affected whereas the platform hash
+// values will change only if that particular platform is affected.
+#define CEF_API_HASH_UNIVERSAL "$UNIVERSAL$"
+#if defined(OS_WIN)
+#define CEF_API_HASH_PLATFORM "$WINDOWS$"
+#elif defined(OS_MACOSX)
+#define CEF_API_HASH_PLATFORM "$MACOSX$"
+#elif defined(OS_LINUX)
+#define CEF_API_HASH_PLATFORM "$LINUX$"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///
+// Returns CEF API hashes for the libcef library. The returned string is owned
+// by the library and should not be freed. The |entry| parameter describes which
+// hash value will be returned:
+// 0 - CEF_API_HASH_PLATFORM
+// 1 - CEF_API_HASH_UNIVERSAL
+// 2 - CEF_COMMIT_HASH (from cef_version.h)
+///
+CEF_EXPORT const char* cef_api_hash(int entry);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  // CEF_INCLUDE_API_HASH_H_
+"""
+
+  # Substitute hash values for placeholders.
+  for platform, value in api_hash.items():
+    result = result.replace('$%s$' % platform.upper(), value)
+
+  return result
+
+
+def write_api_hash_header(output, cpp_header_dir):
+  output = os.path.abspath(output)
+  result = make_api_hash_header(cpp_header_dir)
+  ret = write_file_if_changed(output, result)
+
+  # Also write to |cpp_header_dir| if a different path from |output|, since we
+  # need to commit the hash header for cef_version.py to correctly calculate the
+  # version number based on git history.
+  header_path = os.path.abspath(
+      os.path.join(cpp_header_dir, os.path.basename(output)))
+  if (output != header_path):
+    write_file_if_changed(header_path, result)
+
+  return ret
+
+
+def main(argv):
+  if len(argv) < 3:
+    print(("Usage:\n  %s <output_filename> <cpp_header_dir>" % argv[0]))
+    sys.exit(-1)
+  write_api_hash_header(argv[1], argv[2])
+
+
+if '__main__' == __name__:
+  main(sys.argv)
diff --git a/src/tools/make_capi_header.py b/src/tools/make_capi_header.py
new file mode 100644
index 0000000..52f99b9
--- /dev/null
+++ b/src/tools/make_capi_header.py
@@ -0,0 +1,227 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+from date_util import *
+
+
+def make_capi_global_funcs(funcs, defined_names, translate_map, indent):
+  result = ''
+  first = True
+  for func in funcs:
+    comment = func.get_comment()
+    if first or len(comment) > 0:
+      result += '\n' + format_comment(comment, indent, translate_map)
+    if func.get_retval().get_type().is_result_string():
+      result += indent + '// The resulting string must be freed by calling cef_string_userfree_free().\n'
+    result += indent + 'CEF_EXPORT ' + func.get_capi_proto(defined_names) + ';\n'
+    if first:
+      first = False
+  return result
+
+
+def make_capi_member_funcs(funcs, defined_names, translate_map, indent):
+  result = ''
+  first = True
+  for func in funcs:
+    comment = func.get_comment()
+    if first or len(comment) > 0:
+      result += '\n' + format_comment(comment, indent, translate_map)
+    if func.get_retval().get_type().is_result_string():
+      result += indent + '// The resulting string must be freed by calling cef_string_userfree_free().\n'
+    parts = func.get_capi_parts()
+    result += indent+parts['retval']+' (CEF_CALLBACK *'+parts['name']+ \
+              ')('+', '.join(parts['args'])+');\n'
+    if first:
+      first = False
+  return result
+
+
+def make_capi_header(header, filename):
+  # structure names that have already been defined
+  defined_names = header.get_defined_structs()
+
+  # map of strings that will be changed in C++ comments
+  translate_map = header.get_capi_translations()
+
+  # header string
+  result = \
+"""// Copyright (c) $YEAR$ Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the CEF translator tool and should not edited
+// by hand. See the translator.README.txt file in the tools directory for
+// more information.
+//
+// $hash=$$HASH$$$
+//
+
+#ifndef $GUARD$
+#define $GUARD$
+#pragma once
+
+"""
+
+  # Protect against incorrect use of test headers.
+  if filename.startswith('test/'):
+    result += \
+"""#if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \\
+    !defined(UNIT_TEST)
+#error This file can be included for unit tests only
+#endif
+
+"""
+
+  classes = header.get_classes(filename)
+
+  # identify all includes and forward declarations
+  translated_includes = set([])
+  internal_includes = set([])
+  all_declares = set([])
+  for cls in classes:
+    includes = cls.get_includes()
+    for include in includes:
+      if include.startswith('base/'):
+        # base/ headers are C++. They should not be included by
+        # translated CEF API headers.
+        raise Exception('Disallowed include of %s.h from %s' % (include,
+                                                                filename))
+      elif include.startswith('internal/'):
+        # internal/ headers may be C or C++. Include them as-is.
+        internal_includes.add(include)
+      else:
+        translated_includes.add(include)
+    declares = cls.get_forward_declares()
+    for declare in declares:
+      declare_cls = header.get_class(declare)
+      if declare_cls is None:
+        raise Exception('Unknown class: %s' % declare)
+      all_declares.add(declare_cls.get_capi_name())
+
+  # output translated includes
+  if len(translated_includes) > 0:
+    sorted_includes = sorted(translated_includes)
+    for include in sorted_includes:
+      result += '#include "include/capi/' + include + '_capi.h"\n'
+  else:
+    result += '#include "include/capi/cef_base_capi.h"\n'
+
+  # output internal includes
+  if len(internal_includes) > 0:
+    sorted_includes = sorted(internal_includes)
+    for include in sorted_includes:
+      result += '#include "include/' + include + '.h"\n'
+
+  result += \
+"""
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+"""
+
+  # output forward declarations
+  if len(all_declares) > 0:
+    sorted_declares = sorted(all_declares)
+    for declare in sorted_declares:
+      result += 'struct _' + declare + ';\n'
+
+  # output classes
+  for cls in classes:
+    # virtual functions are inside the structure
+    classname = cls.get_capi_name()
+    result += '\n' + format_comment(cls.get_comment(), '', translate_map)
+    result += 'typedef struct _'+classname+' {\n'+\
+              '  ///\n'+\
+              '  // Base structure.\n'+\
+              '  ///\n'+\
+              '  '+cls.get_parent_capi_name()+' base;\n'
+    funcs = cls.get_virtual_funcs()
+    result += make_capi_member_funcs(funcs, defined_names, translate_map, '  ')
+    result += '} ' + classname + ';\n\n'
+
+    defined_names.append(cls.get_capi_name())
+
+    # static functions become global
+    funcs = cls.get_static_funcs()
+    if len(funcs) > 0:
+      result += make_capi_global_funcs(funcs, defined_names, translate_map,
+                                       '') + '\n'
+
+  # output global functions
+  funcs = header.get_funcs(filename)
+  if len(funcs) > 0:
+    result += make_capi_global_funcs(funcs, defined_names, translate_map, '')
+
+  # footer string
+  result += \
+"""
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // $GUARD$
+"""
+
+  # add the copyright year
+  result = result.replace('$YEAR$', get_year())
+  # add the guard string
+  guard = 'CEF_INCLUDE_CAPI_' + \
+      filename.replace('/', '_').replace('.', '_capi_').upper() + '_'
+  result = result.replace('$GUARD$', guard)
+
+  return result
+
+
+def write_capi_header(header, header_dir, filename):
+  file = get_capi_file_name(os.path.join(header_dir, filename))
+  newcontents = make_capi_header(header, filename)
+  return (file, newcontents)
+
+
+# test the module
+if __name__ == "__main__":
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) < 2:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <infile>\n')
+    sys.exit()
+
+  # create the header object
+  header = obj_header()
+  header.add_file(sys.argv[1])
+
+  # dump the result to stdout
+  filename = os.path.split(sys.argv[1])[1]
+  sys.stdout.write(make_capi_header(header, filename))
diff --git a/src/tools/make_cmake.py b/src/tools/make_cmake.py
new file mode 100644
index 0000000..5e4d2d2
--- /dev/null
+++ b/src/tools/make_cmake.py
@@ -0,0 +1,254 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+import os
+from file_util import *
+import sys
+
+# script directory.
+script_dir = os.path.dirname(__file__)
+
+# CEF root directory.
+cef_dir = os.path.abspath(os.path.join(script_dir, os.pardir))
+
+
+def get_files_for_variable(cmake_path, variables, variable):
+  """ Returns the path values associated with |variable| and relative to the
+        |cmake_path| directory. """
+  if not variable in variables:
+    raise Exception('Variable %s does not exist' % variable)
+
+  # Cmake file directory.
+  cmake_dirname = os.path.dirname(cmake_path) + '/'
+
+  # Return path values relative to the cmake file directory.
+  # Example 1:
+  #   cmake file   = "/path/to/libcef_dll/CMakeLists.txt"
+  #   include path = "/path/to/libcef_dll/wrapper/cef_browser_info_map.h"
+  #   return path  = "wrapper/cef_browser_info_map.h"
+  # Example 2:
+  #   cmake file   = "/path/to/libcef_dll/CMakeLists.txt"
+  #   include path = "/path/to/include/internal/cef_export.h"
+  #   return path  = "../include/internal/cef_export.h"
+  new_paths = []
+  paths = variables[variable]
+  for path in paths:
+    abspath = os.path.join(cef_dir, path)
+    newpath = normalize_path(os.path.relpath(abspath, cmake_dirname))
+    new_paths.append(newpath)
+  return new_paths
+
+
+def format_cmake_set(name, values):
+  result = 'set(%s\n' % name
+  for value in values:
+    result += '  %s\n' % value
+  return result + '  )\n'
+
+
+def format_cmake_group(cmake_path, name, files, platform_sep, append_macro):
+  platforms = {}
+  common = []
+
+  # Folder will be the cmake parent directory name combined with the path to
+  # first file in the files list.
+  # Example 1:
+  #   cmake file   = "/path/to/libcef_dll/CMakeLists.txt"
+  #   include path = "wrapper/cef_browser_info_map.h"
+  #   folder       = "libcef_dll\\\\wrapper"
+  # Example 2:
+  #   cmake file   = "/path/to/libcef_dll/CMakeLists.txt"
+  #   include path = "../include/internal/cef_export.h"
+  #   folder       = "include\\\\internal"
+  folder = os.path.basename(os.path.dirname(cmake_path))
+  folder = os.path.dirname(os.path.normpath(os.path.join(folder, files[0])))
+  folder = normalize_path(folder).replace('/', '\\\\\\\\')
+
+  # Group the files by platform.
+  for file in files:
+    parts = file.split(platform_sep)
+    file = parts[0]
+    if len(parts) > 1:
+      # Add the file under the platform.
+      platform = parts[1]
+      if not platform in platforms:
+        platforms[platform] = []
+      platforms[platform].append(file)
+    else:
+      common.append(file)
+
+  result = ''
+  if len(common) > 0:
+    result += format_cmake_set(name, common)
+
+  if len(platforms) > 0:
+    keys = sorted(platforms.keys())
+    for key in keys:
+      result += format_cmake_set(name + '_' + key, platforms[key])
+    result += '%s(%s)\n' % (append_macro, name)
+
+  result += 'source_group(%s FILES ${%s})\n\n' % (folder, name)
+  return result
+
+
+def format_cmake_library(name, group_names):
+  result = 'add_library(%s\n' % name
+  for group in group_names:
+    result += '  ${%s}\n' % group
+  return result + '  )\n\n'
+
+
+def process_cmake_template_segment(segment, segment_ct, cmake_path, variables):
+  prefix = None
+  library = None
+  set = None
+  includes = []
+  suffix = '_SRCS'  # Appended to each group name before the platform name.
+  platform_sep = ':'  # Used to separate value from platform name.
+  append_macro = 'APPEND_PLATFORM_SOURCES'  # CMake macro name.
+
+  # Extract values from |segment|. Example |segment| contents:
+  #  'prefix': 'cefsimple',
+  #  'includes': [
+  #    'cefsimple_sources_common',
+  #    'cefsimple_sources_win:WINDOWS',
+  #    'cefsimple_sources_mac:MACOSX',
+  #    'cefsimple_sources_linux:LINUX',
+  #  ],
+  values = eval('{' + segment + '}', {'__builtins__': None}, None)
+  if 'prefix' in values:
+    prefix = values['prefix']
+  else:
+    raise Exception('Missing prefix value in segment %d' % segment_ct)
+
+  if 'library' in values:
+    library = values['library']
+
+  if 'set' in values:
+    set = values['set']
+
+  if 'append_macro' in values:
+    append_macro = values['append_macro']
+
+  if 'includes' in values and len(values['includes']) > 0:
+    for include in values['includes']:
+      parts = include.strip().split(platform_sep)
+      files = get_files_for_variable(cmake_path, variables, parts[0])
+      if len(parts) == 2:
+        # Append the platform to each file path.
+        files = [file + platform_sep + parts[1] for file in files]
+      includes.extend(files)
+  else:
+    raise Exception('Missing includes value in segment %d' % segment_ct)
+
+  # Sort the file paths alphabetically.
+  includes.sort()
+
+  # Group files by path.
+  # For example, '../include/base/foo.h' and '../include/base/bar.h' will be
+  # grouped as 'PREFIX_INCLUDE_BASE'.
+  groups = {}
+  for include in includes:
+    paths = include.split('/')
+    label = prefix
+    for path in paths[0:-1]:
+      if path == '..':
+        continue
+      label += '_' + path
+    label = label.replace('.', '_').upper()
+    if not label in groups:
+      groups[label] = []
+    groups[label].append(include)
+
+  # Create the output results.
+  result = ''
+
+  keys = sorted(groups.keys())
+  for key in keys:
+    # Add a group of files that share the same path.
+    result += format_cmake_group(cmake_path, key + suffix, groups[key], \
+                                 platform_sep, append_macro)
+
+  if not library is None:
+    # Add the library declaration if requested.
+    result += format_cmake_library(library, [key + suffix for key in keys])
+
+  if not set is None:
+    # Add the set declaration if requested.
+    result += format_cmake_set(set, \
+                               ['${' + key + suffix + '}' for key in keys])
+
+  return result.strip()
+
+
+def process_cmake_template(input, output, variables, quiet=False):
+  """ Reads the |input| template, parses variable substitution sections and
+        writes |output|. """
+  if not quiet:
+    sys.stdout.write('Processing "%s" to "%s"...\n' % (input, output))
+
+  if not os.path.exists(input):
+    raise Exception('File %s does not exist' % input)
+
+  cmake_path = normalize_path(os.path.abspath(input))
+  template = read_file(cmake_path)
+
+  delim_start = '{{'
+  delim_end = '}}'
+
+  # Process the template file, replacing segments delimited by |delim_start|
+  # and |delim_end|.
+  result = ''
+  end = 0
+  segment_ct = 0
+  while True:
+    start = template.find(delim_start, end)
+    if start == -1:
+      break
+    result += template[end:start]
+    end = template.find(delim_end, start + len(delim_start))
+    if end == -1:
+      break
+    segment = template[start + len(delim_start):end]
+    segment_ct = segment_ct + 1
+    result += process_cmake_template_segment(segment, segment_ct, \
+                                             cmake_path, variables)
+    end += len(delim_end)
+  result += template[end:]
+
+  # Only write the output file if the contents have changed.
+  changed = True
+  if os.path.exists(output):
+    existing = read_file(output)
+    changed = result != existing
+  if changed:
+    write_file(output, result)
+
+
+def read_gypi_variables(source):
+  """ Read the |source| gypi file and extract the variables section. """
+  path = os.path.join(cef_dir, source + '.gypi')
+  if not os.path.exists(path):
+    raise Exception('File %s does not exist' % path)
+  contents = eval_file(path)
+  if not 'variables' in contents:
+    raise Exception('File %s does not have a variables section' % path)
+  return contents['variables']
+
+
+# File entry point.
+if __name__ == "__main__":
+  # Verify that the correct number of command-line arguments are provided.
+  if len(sys.argv) != 3:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <infile> <outfile>')
+    sys.exit()
+
+  # Read the gypi files and combine into a single dictionary.
+  variables1 = read_gypi_variables('cef_paths')
+  variables2 = read_gypi_variables('cef_paths2')
+  variables = dict(variables1.items() + variables2.items())
+
+  # Process the cmake template.
+  process_cmake_template(sys.argv[1], sys.argv[2], variables)
diff --git a/src/tools/make_config_header.py b/src/tools/make_config_header.py
new file mode 100644
index 0000000..0930192
--- /dev/null
+++ b/src/tools/make_config_header.py
@@ -0,0 +1,124 @@
+# Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from date_util import *
+from file_util import *
+from optparse import OptionParser
+import sys
+
+# cannot be loaded as a module
+if __name__ != "__main__":
+  sys.stderr.write('This file cannot be loaded as a module!')
+  sys.exit()
+
+# parse command-line options
+disc = """
+This utility creates the config header file.
+"""
+parser = OptionParser(description=disc)
+parser.add_option(
+    '--header',
+    dest='header',
+    metavar='FILE',
+    help='output config header file [required]')
+parser.add_option(
+    '--cef_gn_config',
+    dest='cef_gn_config',
+    metavar='FILE',
+    help='input CEF gn config file [required]')
+parser.add_option(
+    '-q',
+    '--quiet',
+    action='store_true',
+    dest='quiet',
+    default=False,
+    help='do not output detailed status information')
+(options, args) = parser.parse_args()
+
+# the header option is required
+if options.header is None or options.cef_gn_config is None:
+  parser.print_help(sys.stdout)
+  sys.exit()
+
+
+def check_x11_build(gn_config):
+  """ Scan gn configuration file and decide whether it's x11 build or not """
+  lines = read_file(gn_config).split("\n")
+  for line in lines:
+    parts = line.split('=', 1)
+    if (parts[0] == "use_x11" and
+        parts[1] == "false") or (parts[0] == "use_ozone" and
+                                 parts[1] == "true"):
+      return False
+
+  return True
+
+
+def write_config_header(header, cef_gn_config):
+  """ Creates the header file for the cef build configuration
+       if the information has changed or if the file doesn't already exist. """
+
+  if not path_exists(cef_gn_config):
+    raise Exception('file ' + cef_gn_config + ' does not exist.')
+
+  if path_exists(header):
+    oldcontents = read_file(header)
+  else:
+    oldcontents = ''
+
+  year = get_year()
+
+  cef_x11_defines = "#define CEF_X11 1" if check_x11_build(
+      cef_gn_config) else ""
+
+  newcontents = '// Copyright (c) '+year+' Marshall A. Greenblatt. All rights reserved.\n'+\
+                '//\n'+\
+                '// Redistribution and use in source and binary forms, with or without\n'+\
+                '// modification, are permitted provided that the following conditions are\n'+\
+                '// met:\n'+\
+                '//\n'+\
+                '//    * Redistributions of source code must retain the above copyright\n'+\
+                '// notice, this list of conditions and the following disclaimer.\n'+\
+                '//    * Redistributions in binary form must reproduce the above\n'+\
+                '// copyright notice, this list of conditions and the following disclaimer\n'+\
+                '// in the documentation and/or other materials provided with the\n'+\
+                '// distribution.\n'+\
+                '//    * Neither the name of Google Inc. nor the name Chromium Embedded\n'+\
+                '// Framework nor the names of its contributors may be used to endorse\n'+\
+                '// or promote products derived from this software without specific prior\n'+\
+                '// written permission.\n'+\
+                '//\n'+\
+                '// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n'+\
+                '// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n'+\
+                '// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n'+\
+                '// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n'+\
+                '// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n'+\
+                '// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n'+\
+                '// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n'+\
+                '// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n'+\
+                '// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n'+\
+                '// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n'+\
+                '// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n'+\
+                '//\n'+\
+                '// ---------------------------------------------------------------------------\n'+\
+                '//\n'+\
+                '// This file is generated by the make_config_header.py tool.\n'+\
+                '//\n\n'+\
+                '#ifndef CEF_INCLUDE_CEF_CONFIG_H_\n'+\
+                '#define CEF_INCLUDE_CEF_CONFIG_H_\n\n'+\
+                '' + cef_x11_defines + '\n'+\
+                '#endif  // CEF_INCLUDE_CEF_CONFIG_H_\n'
+  if newcontents != oldcontents:
+    write_file(header, newcontents)
+    return True
+  return False
+
+
+written = write_config_header(options.header, options.cef_gn_config)
+if not options.quiet:
+  if written:
+    sys.stdout.write('File ' + options.header + ' updated.\n')
+  else:
+    sys.stdout.write('File ' + options.header + ' is already up to date.\n')
diff --git a/src/tools/make_cppdocs.bat b/src/tools/make_cppdocs.bat
new file mode 100644
index 0000000..dd9809c
--- /dev/null
+++ b/src/tools/make_cppdocs.bat
@@ -0,0 +1,18 @@
+@echo off

+setlocal

+

+if "%1"=="" (

+set CPPDOC_EXE="C:\Program Files (x86)\richfeit\CppDoc\CppDoc.exe"

+set CPPDOC_REV="XXX"

+) else (

+set CPPDOC_EXE="C:\Program Files (x86)\richfeit\CppDoc\cppdoc_cmd.exe"

+set CPPDOC_REV="%1"

+)

+

+if not exist %CPPDOC_EXE% (

+echo ERROR: Please install CppDoc from http://www.cppdoc.com/

+) else (

+%CPPDOC_EXE% -overwrite -title="CEF3 C++ API Docs - Revision %CPPDOC_REV%"  -footer="<center><a href="https://bitbucket.org/chromiumembedded/cef" target="_top">Chromium Embedded Framework (CEF)</a> Copyright &copy 2016 Marshall A. Greenblatt</center>" -namespace-as-project -comment-format="///;//;///" -classdir=projects -module="cppdoc-standard" -extensions=h -languages="c=cpp,cc=cpp,cpp=cpp,cs=csharp,cxx=cpp,h=cpp,hpp=cpp,hxx=cpp,java=java" -D"OS_WIN" -D"USING_CEF_SHARED" -D"__cplusplus" -D"CEF_STRING_TYPE_UTF16" -enable-author=false -enable-deprecations=true -enable-since=true -enable-version=false -file-links-for-globals=false -generate-deprecations-list=false -generate-hierarchy=true -header-background-dark="#ccccff" -header-background-light="#eeeeff" -include-private=false -include-protected=true -index-file-base=index -overview-html=overview.html -reduce-summary-font=true -selected-text-background=navy -selected-text-foreground=white -separate-index-pages=false -show-cppdoc-version=false -show-timestamp=false -summary-html=project.html -suppress-details=false -suppress-frames-links=false -table-background=white -wrap-long-lines=false ..\include #capi "..\docs\index.html"

+)

+

+endlocal
\ No newline at end of file
diff --git a/src/tools/make_cpptoc_header.py b/src/tools/make_cpptoc_header.py
new file mode 100644
index 0000000..8375553
--- /dev/null
+++ b/src/tools/make_cpptoc_header.py
@@ -0,0 +1,109 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+
+
+def make_cpptoc_header(header, clsname):
+  cls = header.get_class(clsname)
+  if cls is None:
+    raise Exception('Class does not exist: ' + clsname)
+
+  dllside = cls.is_library_side()
+
+  directory = cls.get_file_directory()
+  defname = ''
+  if not directory is None:
+    defname += directory + '_'
+  defname += get_capi_name(clsname[3:], False)
+  defname = defname.upper()
+
+  capiname = cls.get_capi_name()
+
+  result = get_copyright()
+
+  result += '#ifndef CEF_LIBCEF_DLL_CPPTOC_'+defname+'_CPPTOC_H_\n'+ \
+            '#define CEF_LIBCEF_DLL_CPPTOC_'+defname+'_CPPTOC_H_\n' + \
+            '#pragma once\n'
+
+  if dllside:
+    result += """
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+"""
+  else:
+    result += """
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+"""
+
+  # include the headers for this class
+  result += '\n#include "include/'+cls.get_file_name()+'"\n' \
+            '#include "include/capi/'+cls.get_capi_file_name()+'"\n'
+
+  # include headers for any forward declared classes that are not in the same file
+  declares = cls.get_forward_declares()
+  for declare in declares:
+    dcls = header.get_class(declare)
+    if dcls.get_file_name() != cls.get_file_name():
+      result += '#include "include/'+dcls.get_file_name()+'"\n' \
+                '#include "include/capi/'+dcls.get_capi_file_name()+'"\n'
+
+  base_class_name = header.get_base_class_name(clsname)
+  base_scoped = True if base_class_name == 'CefBaseScoped' else False
+  if base_scoped:
+    template_file = 'cpptoc_scoped.h'
+    template_class = 'CefCppToCScoped'
+  else:
+    template_file = 'cpptoc_ref_counted.h'
+    template_class = 'CefCppToCRefCounted'
+
+  result += '#include "libcef_dll/cpptoc/' + template_file + '"'
+  result += '\n\n// Wrap a C++ class with a C structure.\n'
+
+  if dllside:
+    result += '// This class may be instantiated and accessed DLL-side only.\n'
+  else:
+    result += '// This class may be instantiated and accessed wrapper-side only.\n'
+
+  result +=  'class '+clsname+'CppToC\n'+ \
+             '    : public ' + template_class + '<'+clsname+'CppToC, '+clsname+', '+capiname+'> {\n'+ \
+             ' public:\n'+ \
+             '  '+clsname+'CppToC();\n'+ \
+             '  virtual ~'+clsname+'CppToC();\n'+ \
+             '};\n\n'
+
+  result += '#endif  // CEF_LIBCEF_DLL_CPPTOC_' + defname + '_CPPTOC_H_'
+
+  return result
+
+
+def write_cpptoc_header(header, clsname, dir):
+  # give the output file the same directory offset as the input file
+  cls = header.get_class(clsname)
+  dir = os.path.dirname(os.path.join(dir, cls.get_file_name()))
+  file = os.path.join(dir, get_capi_name(clsname[3:], False) + '_cpptoc.h')
+
+  newcontents = make_cpptoc_header(header, clsname)
+  return (file, newcontents)
+
+
+# test the module
+if __name__ == "__main__":
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) < 3:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <infile> <classname>')
+    sys.exit()
+
+  # create the header object
+  header = obj_header()
+  header.add_file(sys.argv[1])
+
+  # dump the result to stdout
+  sys.stdout.write(make_cpptoc_header(header, sys.argv[2]))
diff --git a/src/tools/make_cpptoc_impl.py b/src/tools/make_cpptoc_impl.py
new file mode 100644
index 0000000..305244b
--- /dev/null
+++ b/src/tools/make_cpptoc_impl.py
@@ -0,0 +1,748 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+
+
+def make_cpptoc_impl_proto(name, func, parts):
+  if isinstance(func, obj_function_virtual):
+    proto = parts['retval'] + ' CEF_CALLBACK'
+  else:
+    proto = 'CEF_EXPORT ' + parts['retval']
+
+  proto += ' ' + name + '(' + ', '.join(parts['args']) + ')'
+  return proto
+
+
+def make_cpptoc_function_impl_existing(cls, name, func, impl, defined_names):
+  notify(name + ' has manual edits')
+
+  # retrieve the C API prototype parts
+  parts = func.get_capi_parts(defined_names)
+
+  changes = format_translation_changes(impl, parts)
+  if len(changes) > 0:
+    notify(name + ' prototype changed')
+
+  return make_cpptoc_impl_proto(
+      name, func, parts) + '{' + changes + impl['body'] + '\n}\n\n'
+
+
+def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped):
+  # Special handling for the cef_shutdown global function.
+  is_cef_shutdown = name == 'cef_shutdown' and isinstance(
+      func.parent, obj_header)
+
+  # retrieve the C API prototype parts
+  parts = func.get_capi_parts(defined_names)
+  result = make_cpptoc_impl_proto(name, func, parts) + ' {'
+
+  if isinstance(func.parent, obj_class) and \
+      not func.parent.has_attrib('no_debugct_check') and \
+      not base_scoped:
+    result += '\n  shutdown_checker::AssertNotShutdown();\n'
+
+  invalid = []
+
+  # retrieve the function arguments
+  args = func.get_arguments()
+
+  # determine the argument types
+  for arg in args:
+    if arg.get_arg_type() == 'invalid':
+      invalid.append(arg.get_name())
+
+  # retrieve the function return value
+  retval = func.get_retval()
+  retval_type = retval.get_retval_type()
+  if retval_type == 'invalid':
+    invalid.append('(return value)')
+    retval_default = ''
+  else:
+    retval_default = retval.get_retval_default(True)
+    if len(retval_default) > 0:
+      retval_default = ' ' + retval_default
+
+  if len(invalid) > 0:
+    notify(name + ' could not be autogenerated')
+    # code could not be auto-generated
+    result += '\n  // BEGIN DELETE BEFORE MODIFYING'
+    result += '\n  // AUTO-GENERATED CONTENT'
+    result += '\n  // COULD NOT IMPLEMENT DUE TO: ' + ', '.join(invalid)
+    result += '\n  #pragma message("Warning: "__FILE__": ' + name + ' is not implemented")'
+    result += '\n  // END DELETE BEFORE MODIFYING'
+    result += '\n}\n\n'
+    return result
+
+  result += '\n  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING\n'
+
+  result_len = len(result)
+
+  optional = []
+
+  # parameter verification
+  if isinstance(func, obj_function_virtual):
+    result += '\n  DCHECK(self);'\
+              '\n  if (!self)'\
+              '\n    return'+retval_default+';'
+
+  for arg in args:
+    arg_type = arg.get_arg_type()
+    arg_name = arg.get_type().get_name()
+
+    # skip optional params
+    optional_params = arg.parent.get_attrib_list('optional_param')
+    if not optional_params is None and arg_name in optional_params:
+      optional.append(arg_name)
+      continue
+
+    comment = '\n  // Verify param: ' + arg_name + '; type: ' + arg_type
+
+    if arg_type == 'simple_byref' or arg_type == 'simple_byref_const' or \
+       arg_type == 'simple_byaddr' or arg_type == 'bool_byref' or arg_type == 'bool_byaddr' or \
+       arg_type == 'struct_byref_const' or arg_type == 'struct_byref' or \
+       arg_type == 'string_byref_const' or arg_type == 'string_byref' or \
+       arg_type == 'refptr_same' or arg_type == 'refptr_same_byref' or \
+       arg_type == 'refptr_diff' or arg_type == 'refptr_diff_byref' or \
+       arg_type == 'ownptr_same' or arg_type == 'ownptr_same_byref' or \
+       arg_type == 'ownptr_diff' or arg_type == 'ownptr_diff_byref' or \
+       arg_type == 'rawptr_same' or arg_type == 'rawptr_same_byref' or \
+       arg_type == 'rawptr_diff' or arg_type == 'rawptr_diff_byref' or \
+       arg_type == 'string_vec_byref' or arg_type == 'string_vec_byref_const' or \
+       arg_type == 'string_map_single_byref' or arg_type == 'string_map_single_byref_const' or \
+       arg_type == 'string_map_multi_byref' or arg_type == 'string_map_multi_byref_const':
+      result += comment+\
+                '\n  DCHECK('+arg_name+');'\
+                '\n  if (!'+arg_name+')'\
+                '\n    return'+retval_default+';'
+    elif arg_type == 'simple_vec_byref' or arg_type == 'bool_vec_byref' or \
+        arg_type == 'refptr_vec_same_byref' or arg_type == 'refptr_vec_diff_byref' or \
+        arg_type == 'ownptr_vec_same_byref' or arg_type == 'ownptr_vec_diff_byref' or \
+        arg_type == 'rawptr_vec_same_byref' or arg_type == 'rawptr_vec_diff_byref':
+      result += comment+\
+                '\n  DCHECK('+arg_name+'Count && (*'+arg_name+'Count == 0 || '+arg_name+'));'\
+                '\n  if (!'+arg_name+'Count || (*'+arg_name+'Count > 0 && !'+arg_name+'))'\
+                '\n    return'+retval_default+';'
+    elif arg_type == 'simple_vec_byref_const' or arg_type == 'bool_vec_byref_const' or \
+        arg_type == 'refptr_vec_same_byref_const' or arg_type == 'refptr_vec_diff_byref_const' or \
+        arg_type == 'ownptr_vec_same_byref_const' or arg_type == 'ownptr_vec_diff_byref_const' or \
+        arg_type == 'rawptr_vec_same_byref_const' or arg_type == 'rawptr_vec_diff_byref_const':
+      result += comment+\
+                '\n  DCHECK('+arg_name+'Count == 0 || '+arg_name+');'\
+                '\n  if ('+arg_name+'Count > 0 && !'+arg_name+')'\
+                '\n    return'+retval_default+';'
+
+    # check index params
+    index_params = arg.parent.get_attrib_list('index_param')
+    if not index_params is None and arg_name in index_params:
+      result += comment+\
+                '\n  DCHECK_GE('+arg_name+', 0);'\
+                '\n  if ('+arg_name+' < 0)'\
+                '\n    return'+retval_default+';'
+
+  if len(optional) > 0:
+    # Wrap the comment at 80 characters.
+    str = '\n  // Unverified params: ' + optional[0]
+    for name in optional[1:]:
+      str += ','
+      if len(str) + len(name) + 1 > 80:
+        result += str
+        str = '\n  //'
+      str += ' ' + name
+    result += str
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  # parameter translation
+  params = []
+
+  for arg in args:
+    arg_type = arg.get_arg_type()
+    arg_name = arg.get_type().get_name()
+
+    comment = '\n  // Translate param: ' + arg_name + '; type: ' + arg_type
+
+    if arg_type == 'simple_byval' or arg_type == 'simple_byaddr':
+      params.append(arg_name)
+    elif arg_type == 'simple_byref' or arg_type == 'simple_byref_const':
+      data_type = arg.get_type().get_type()
+      default = arg.get_type().get_result_simple_default()
+      result += comment+\
+                '\n  '+data_type+' '+arg_name+'Val = '+arg_name+'?*'+arg_name+':'+default+';'
+      params.append(arg_name + 'Val')
+    elif arg_type == 'bool_byval':
+      params.append(arg_name + '?true:false')
+    elif arg_type == 'bool_byref' or arg_type == 'bool_byaddr':
+      result += comment+\
+                '\n  bool '+arg_name+'Bool = ('+arg_name+' && *'+arg_name+')?true:false;'
+      if arg_type == 'bool_byref':
+        params.append(arg_name + 'Bool')
+      else:
+        params.append('&' + arg_name + 'Bool')
+    elif arg_type == 'struct_byref_const':
+      struct_type = arg.get_type().get_type()
+      result += comment+\
+                '\n  '+struct_type+' '+arg_name+'Obj;'\
+                '\n  if ('+arg_name+')'\
+                '\n    '+arg_name+'Obj.Set(*'+arg_name+', false);'
+      params.append(arg_name + 'Obj')
+    elif arg_type == 'struct_byref':
+      struct_type = arg.get_type().get_type()
+      result += comment+\
+                '\n  '+struct_type+' '+arg_name+'Obj;'\
+                '\n  if ('+arg_name+')'\
+                '\n    '+arg_name+'Obj.AttachTo(*'+arg_name+');'
+      params.append(arg_name + 'Obj')
+    elif arg_type == 'string_byref_const':
+      params.append('CefString(' + arg_name + ')')
+    elif arg_type == 'string_byref':
+      result += comment+\
+                '\n  CefString '+arg_name+'Str('+arg_name+');'
+      params.append(arg_name + 'Str')
+    elif arg_type == 'refptr_same' or arg_type == 'refptr_diff':
+      ptr_class = arg.get_type().get_ptr_type()
+      if arg_type == 'refptr_same':
+        params.append(ptr_class + 'CppToC::Unwrap(' + arg_name + ')')
+      else:
+        params.append(ptr_class + 'CToCpp::Wrap(' + arg_name + ')')
+    elif arg_type == 'ownptr_same' or arg_type == 'rawptr_same':
+      ptr_class = arg.get_type().get_ptr_type()
+      if arg_type == 'ownptr_same':
+        params.append(ptr_class + 'CppToC::UnwrapOwn(' + arg_name + ')')
+      else:
+        params.append(ptr_class + 'CppToC::UnwrapRaw(' + arg_name + ')')
+    elif arg_type == 'ownptr_diff' or arg_type == 'rawptr_diff':
+      ptr_class = arg.get_type().get_ptr_type()
+      result += comment+\
+                '\n  CefOwnPtr<'+ptr_class+'> '+arg_name+'Ptr('+ptr_class+'CToCpp::Wrap('+arg_name+'));'
+      if arg_type == 'ownptr_diff':
+        params.append('OWN_PASS(' + arg_name + 'Ptr)')
+      else:
+        params.append(arg_name + 'Ptr.get()')
+    elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
+      ptr_class = arg.get_type().get_ptr_type()
+      if arg_type == 'refptr_same_byref':
+        assign = ptr_class + 'CppToC::Unwrap(*' + arg_name + ')'
+      else:
+        assign = ptr_class + 'CToCpp::Wrap(*' + arg_name + ')'
+      result += comment+\
+                '\n  CefRefPtr<'+ptr_class+'> '+arg_name+'Ptr;'\
+                '\n  if ('+arg_name+' && *'+arg_name+')'\
+                '\n    '+arg_name+'Ptr = '+assign+';'\
+                '\n  '+ptr_class+'* '+arg_name+'Orig = '+arg_name+'Ptr.get();'
+      params.append(arg_name + 'Ptr')
+    elif arg_type == 'string_vec_byref' or arg_type == 'string_vec_byref_const':
+      result += comment+\
+                '\n  std::vector<CefString> '+arg_name+'List;'\
+                '\n  transfer_string_list_contents('+arg_name+', '+arg_name+'List);'
+      params.append(arg_name + 'List')
+    elif arg_type == 'string_map_single_byref' or arg_type == 'string_map_single_byref_const':
+      result += comment+\
+                '\n  std::map<CefString, CefString> '+arg_name+'Map;'\
+                '\n  transfer_string_map_contents('+arg_name+', '+arg_name+'Map);'
+      params.append(arg_name + 'Map')
+    elif arg_type == 'string_map_multi_byref' or arg_type == 'string_map_multi_byref_const':
+      result += comment+\
+                '\n  std::multimap<CefString, CefString> '+arg_name+'Multimap;'\
+                '\n  transfer_string_multimap_contents('+arg_name+', '+arg_name+'Multimap);'
+      params.append(arg_name + 'Multimap')
+    elif arg_type == 'simple_vec_byref' or arg_type == 'bool_vec_byref' or \
+        arg_type == 'refptr_vec_same_byref' or arg_type == 'refptr_vec_diff_byref':
+      vec_type = arg.get_type().get_vector_type()
+      if arg_type == 'simple_vec_byref':
+        assign = arg_name + '[i]'
+      elif arg_type == 'bool_vec_byref':
+        assign = arg_name + '[i]?true:false'
+      elif arg_type == 'refptr_vec_same_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CppToC::Unwrap(' + arg_name + '[i])'
+      elif arg_type == 'refptr_vec_diff_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CToCpp::Wrap(' + arg_name + '[i])'
+      result += comment+\
+                '\n  std::vector<'+vec_type+' > '+arg_name+'List;'\
+                '\n  if ('+arg_name+'Count && *'+arg_name+'Count > 0 && '+arg_name+') {'\
+                '\n    for (size_t i = 0; i < *'+arg_name+'Count; ++i) {'\
+                '\n      '+arg_name+'List.push_back('+assign+');'\
+                '\n    }'\
+                '\n  }'
+      params.append(arg_name + 'List')
+    elif arg_type == 'simple_vec_byref_const' or arg_type == 'bool_vec_byref_const' or \
+        arg_type == 'refptr_vec_same_byref_const' or arg_type == 'refptr_vec_diff_byref_const' or \
+        arg_type == 'rawptr_vec_same_byref_const' or arg_type == 'rawptr_vec_diff_byref_const':
+      vec_type = arg.get_type().get_vector_type()
+      if arg_type == 'simple_vec_byref_const':
+        assign = arg_name + '[i]'
+      elif arg_type == 'bool_vec_byref_const':
+        assign = arg_name + '[i]?true:false'
+      else:
+        ptr_class = arg.get_type().get_ptr_type()
+        if arg_type == 'refptr_vec_same_byref_const':
+          assign = ptr_class + 'CppToC::Unwrap(' + arg_name + '[i])'
+        elif arg_type == 'refptr_vec_diff_byref_const':
+          assign = ptr_class + 'CToCpp::Wrap(' + arg_name + '[i])'
+        elif arg_type == 'rawptr_vec_same_byref_const':
+          assign = ptr_class + 'CppToC::UnwrapRaw(' + arg_name + '[i])'
+        elif arg_type == 'rawptr_vec_diff_byref_const':
+          assign = ptr_class + 'CToCpp::Wrap(' + arg_name + '[i]).release()'
+      result += comment+\
+                '\n  std::vector<'+vec_type+' > '+arg_name+'List;'\
+                '\n  if ('+arg_name+'Count > 0) {'\
+                '\n    for (size_t i = 0; i < '+arg_name+'Count; ++i) {'\
+                '\n      '+vec_type+' '+arg_name+'Val = '+assign+';'\
+                '\n      '+arg_name+'List.push_back('+arg_name+'Val);'\
+                '\n    }'\
+                '\n  }'
+      params.append(arg_name + 'List')
+    else:
+      raise Exception('Unsupported argument type %s for parameter %s in %s' %
+                      (arg_type, arg_name, name))
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  if is_cef_shutdown:
+    result += '\n\n#if DCHECK_IS_ON()'\
+              '\n  shutdown_checker::SetIsShutdown();'\
+              '\n#endif\n'
+
+  # execution
+  result += '\n  // Execute\n  '
+
+  if retval_type != 'none':
+    # has a return value
+    if retval_type == 'simple':
+      result += retval.get_type().get_result_simple_type()
+    else:
+      result += retval.get_type().get_type()
+    result += ' _retval = '
+
+  if isinstance(func.parent, obj_class):
+    # virtual and static class methods
+    if isinstance(func, obj_function_virtual):
+      if cls.get_name() == func.parent.get_name():
+        # virtual method for the current class
+        result += func.parent.get_name() + 'CppToC::Get(self)->'
+      else:
+        # virtual method for a parent class
+        result += cls.get_name(
+        ) + 'CppToC::Get(reinterpret_cast<' + cls.get_capi_name() + '*>(self))->'
+    else:
+      result += func.parent.get_name() + '::'
+  result += func.get_name() + '('
+
+  if len(params) > 0:
+    result += '\n      ' + ',\n      '.join(params)
+
+  result += ');\n'
+
+  result_len = len(result)
+
+  # parameter restoration
+  for arg in args:
+    arg_type = arg.get_arg_type()
+    arg_name = arg.get_type().get_name()
+
+    comment = '\n  // Restore param: ' + arg_name + '; type: ' + arg_type
+
+    if arg_type == 'simple_byref':
+      result += comment+\
+                '\n  if ('+arg_name+')'\
+                '\n    *'+arg_name+' = '+arg_name+'Val;'
+    elif arg_type == 'bool_byref' or arg_type == 'bool_byaddr':
+      result += comment+\
+                '\n  if ('+arg_name+')'\
+                '\n    *'+arg_name+' = '+arg_name+'Bool?true:false;'
+    elif arg_type == 'struct_byref':
+      result += comment+\
+                '\n  if ('+arg_name+')'\
+                '\n    '+arg_name+'Obj.DetachTo(*'+arg_name+');'
+    elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
+      ptr_class = arg.get_type().get_ptr_type()
+      if arg_type == 'refptr_same_byref':
+        assign = ptr_class + 'CppToC::Wrap(' + arg_name + 'Ptr)'
+      else:
+        assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + 'Ptr)'
+      result += comment+\
+                '\n  if ('+arg_name+') {'\
+                '\n    if ('+arg_name+'Ptr.get()) {'\
+                '\n      if ('+arg_name+'Ptr.get() != '+arg_name+'Orig) {'\
+                '\n        *'+arg_name+' = '+assign+';'\
+                '\n      }'\
+                '\n    } else {'\
+                '\n      *'+arg_name+' = nullptr;'\
+                '\n    }'\
+                '\n  }'
+    elif arg_type == 'string_vec_byref':
+      result += comment+\
+                '\n  cef_string_list_clear('+arg_name+');'\
+                '\n  transfer_string_list_contents('+arg_name+'List, '+arg_name+');'
+    elif arg_type == 'string_map_single_byref':
+      result += comment+\
+                '\n  cef_string_map_clear('+arg_name+');'\
+                '\n  transfer_string_map_contents('+arg_name+'Map, '+arg_name+');'
+    elif arg_type == 'string_map_multi_byref':
+      result += comment+\
+                '\n  cef_string_multimap_clear('+arg_name+');'\
+                '\n  transfer_string_multimap_contents('+arg_name+'Multimap, '+arg_name+');'
+    elif arg_type == 'simple_vec_byref' or arg_type == 'bool_vec_byref' or \
+        arg_type == 'refptr_vec_same_byref' or arg_type == 'refptr_vec_diff_byref':
+      if arg_type == 'simple_vec_byref' or arg_type == 'bool_vec_byref':
+        assign = arg_name + 'List[i]'
+      elif arg_type == 'refptr_vec_same_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CppToC::Wrap(' + arg_name + 'List[i])'
+      elif arg_type == 'refptr_vec_diff_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + 'List[i])'
+      result += comment+\
+                '\n  if ('+arg_name+'Count && '+arg_name+') {'\
+                '\n    *'+arg_name+'Count = std::min('+arg_name+'List.size(), *'+arg_name+'Count);'\
+                '\n    if (*'+arg_name+'Count > 0) {'\
+                '\n      for (size_t i = 0; i < *'+arg_name+'Count; ++i) {'\
+                '\n        '+arg_name+'[i] = '+assign+';'\
+                '\n      }'\
+                '\n    }'\
+                '\n  }'
+    elif arg_type == 'rawptr_vec_diff_byref_const':
+      result += comment+\
+                '\n  if ('+arg_name+'Count > 0) {'\
+                '\n    for (size_t i = 0; i < '+arg_name+'Count; ++i) {'\
+                '\n      delete '+arg_name+'List[i];'\
+                '\n    }'\
+                '\n  }'
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  # return translation
+  if retval_type != 'none':
+    # has a return value
+    result += '\n  // Return type: ' + retval_type
+    if retval_type == 'simple' or retval_type == 'bool':
+      result += '\n  return _retval;'
+    elif retval_type == 'string':
+      result += '\n  return _retval.DetachToUserFree();'
+    elif retval_type == 'refptr_same':
+      ptr_class = retval.get_type().get_ptr_type()
+      result += '\n  return ' + ptr_class + 'CppToC::Wrap(_retval);'
+    elif retval_type == 'refptr_diff':
+      ptr_class = retval.get_type().get_ptr_type()
+      result += '\n  return ' + ptr_class + 'CToCpp::Unwrap(_retval);'
+    elif retval_type == 'ownptr_same':
+      ptr_class = retval.get_type().get_ptr_type()
+      result += '\n  return ' + ptr_class + 'CppToC::WrapOwn(OWN_PASS(_retval));'
+    elif retval_type == 'ownptr_diff':
+      ptr_class = retval.get_type().get_ptr_type()
+      result += '\n  return ' + ptr_class + 'CToCpp::UnwrapOwn(OWN_PASS(_retval));'
+    else:
+      raise Exception('Unsupported return type %s in %s' % (retval_type, name))
+
+  if len(result) != result_len:
+    result += '\n'
+
+  result += '}\n\n'
+  return result
+
+
+def make_cpptoc_function_impl(cls, funcs, existing, prefixname, defined_names,
+                              base_scoped):
+  impl = ''
+
+  for func in funcs:
+    if not prefixname is None:
+      name = prefixname + '_' + func.get_capi_name()
+    else:
+      name = func.get_capi_name()
+    value = get_next_function_impl(existing, name)
+    if not value is None \
+        and value['body'].find('// AUTO-GENERATED CONTENT') < 0:
+      # an implementation exists that was not auto-generated
+      impl += make_cpptoc_function_impl_existing(cls, name, func, value,
+                                                 defined_names)
+    else:
+      impl += make_cpptoc_function_impl_new(cls, name, func, defined_names,
+                                            base_scoped)
+
+  return impl
+
+
+def make_cpptoc_virtual_function_impl(header, cls, existing, prefixname,
+                                      defined_names, base_scoped):
+  funcs = []
+  funcs.extend(cls.get_virtual_funcs())
+  cur_cls = cls
+  while True:
+    parent_name = cur_cls.get_parent_name()
+    if is_base_class(parent_name):
+      break
+    else:
+      parent_cls = header.get_class(parent_name, defined_names)
+      if parent_cls is None:
+        raise Exception('Class does not exist: ' + parent_name)
+      funcs.extend(parent_cls.get_virtual_funcs())
+    cur_cls = header.get_class(parent_name, defined_names)
+
+  return make_cpptoc_function_impl(cls, funcs, existing, prefixname,
+                                   defined_names, base_scoped)
+
+
+def make_cpptoc_virtual_function_assignment_block(funcs, offset, prefixname):
+  impl = ''
+  for func in funcs:
+    name = func.get_capi_name()
+    impl += '  GetStruct()->' + offset + name + ' = ' + prefixname + '_' + name + ';\n'
+  return impl
+
+
+def make_cpptoc_virtual_function_assignment(header, cls, prefixname,
+                                            defined_names):
+  impl = make_cpptoc_virtual_function_assignment_block(cls.get_virtual_funcs(),
+                                                       '', prefixname)
+
+  cur_cls = cls
+  offset = ''
+  while True:
+    parent_name = cur_cls.get_parent_name()
+    offset += 'base.'
+    if is_base_class(parent_name):
+      break
+    else:
+      parent_cls = header.get_class(parent_name, defined_names)
+      if parent_cls is None:
+        raise Exception('Class does not exist: ' + parent_name)
+      impl += make_cpptoc_virtual_function_assignment_block(
+          parent_cls.get_virtual_funcs(), offset, prefixname)
+    cur_cls = header.get_class(parent_name, defined_names)
+
+  return impl
+
+
+def make_cpptoc_unwrap_derived(header, cls, base_scoped):
+  # identify all classes that derive from cls
+  derived_classes = []
+  cur_clsname = cls.get_name()
+  allclasses = header.get_classes()
+  for cur_cls in allclasses:
+    if cur_cls.get_name() == cur_clsname:
+      continue
+    if cur_cls.has_parent(cur_clsname):
+      derived_classes.append(cur_cls.get_name())
+
+  derived_classes = sorted(derived_classes)
+
+  if base_scoped:
+    impl = ['', '']
+    for clsname in derived_classes:
+      impl[0] += '  if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\
+                 '    return OWN_RETURN_AS('+clsname+'CppToC::UnwrapOwn(reinterpret_cast<'+\
+                 get_capi_name(clsname, True)+'*>(s)), '+cur_clsname+');\n'+\
+                 '  }\n'
+      impl[1] += '  if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\
+                 '    return '+clsname+'CppToC::UnwrapRaw(reinterpret_cast<'+\
+                 get_capi_name(clsname, True)+'*>(s));\n'+\
+                 '  }\n'
+  else:
+    impl = ''
+    for clsname in derived_classes:
+      impl += '  if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\
+              '    return '+clsname+'CppToC::Unwrap(reinterpret_cast<'+\
+              get_capi_name(clsname, True)+'*>(s));\n'+\
+              '  }\n'
+  return impl
+
+
+def make_cpptoc_class_impl(header, clsname, impl):
+  # structure names that have already been defined
+  defined_names = header.get_defined_structs()
+
+  # retrieve the class and populate the defined names
+  cls = header.get_class(clsname, defined_names)
+  if cls is None:
+    raise Exception('Class does not exist: ' + clsname)
+
+  capiname = cls.get_capi_name()
+  prefixname = get_capi_name(clsname[3:], False)
+
+  # retrieve the existing virtual function implementations
+  existing = get_function_impls(impl, 'CEF_CALLBACK')
+
+  base_class_name = header.get_base_class_name(clsname)
+  base_scoped = True if base_class_name == 'CefBaseScoped' else False
+  if base_scoped:
+    template_class = 'CefCppToCScoped'
+  else:
+    template_class = 'CefCppToCRefCounted'
+
+  # generate virtual functions
+  virtualimpl = make_cpptoc_virtual_function_impl(
+      header, cls, existing, prefixname, defined_names, base_scoped)
+  if len(virtualimpl) > 0:
+    virtualimpl = '\nnamespace {\n\n// MEMBER FUNCTIONS - Body may be edited by hand.\n\n' + virtualimpl + '}  // namespace'
+
+  # the current class is already defined for static functions
+  defined_names.append(cls.get_capi_name())
+
+  # retrieve the existing static function implementations
+  existing = get_function_impls(impl, 'CEF_EXPORT')
+
+  # generate static functions
+  staticimpl = make_cpptoc_function_impl(cls,
+                                         cls.get_static_funcs(), existing, None,
+                                         defined_names, base_scoped)
+  if len(staticimpl) > 0:
+    staticimpl = '\n// GLOBAL FUNCTIONS - Body may be edited by hand.\n\n' + staticimpl
+
+  resultingimpl = staticimpl + virtualimpl
+
+  # any derived classes can be unwrapped
+  unwrapderived = make_cpptoc_unwrap_derived(header, cls, base_scoped)
+
+  const =  '// CONSTRUCTOR - Do not edit by hand.\n\n'+ \
+           clsname+'CppToC::'+clsname+'CppToC() {\n'
+  const += make_cpptoc_virtual_function_assignment(header, cls, prefixname,
+                                                   defined_names)
+  const += '}\n\n'+ \
+           '// DESTRUCTOR - Do not edit by hand.\n\n'+ \
+           clsname+'CppToC::~'+clsname+'CppToC() {\n'
+
+  if not cls.has_attrib('no_debugct_check') and not base_scoped:
+    const += '  shutdown_checker::AssertNotShutdown();\n'
+
+  const += '}\n\n'
+
+  # determine what includes are required by identifying what translation
+  # classes are being used
+  includes = format_translation_includes(header, const + resultingimpl +
+                                         (unwrapderived[0]
+                                          if base_scoped else unwrapderived))
+
+  # build the final output
+  result = get_copyright()
+
+  result += includes + '\n' + resultingimpl + '\n'
+
+  parent_sig = template_class + '<' + clsname + 'CppToC, ' + clsname + ', ' + capiname + '>'
+
+  if base_scoped:
+    const += 'template<> CefOwnPtr<'+clsname+'> '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, '+capiname+'* s) {\n' + \
+             unwrapderived[0] + \
+             '  NOTREACHED() << "Unexpected class type: " << type;\n'+ \
+             '  return CefOwnPtr<'+clsname+'>();\n'+ \
+             '}\n\n' + \
+             'template<> CefRawPtr<'+clsname+'> '+parent_sig+'::UnwrapDerivedRaw(CefWrapperType type, '+capiname+'* s) {\n' + \
+             unwrapderived[1] + \
+             '  NOTREACHED() << "Unexpected class type: " << type;\n'+ \
+             '  return nullptr;\n'+ \
+             '}\n\n'
+  else:
+    const += 'template<> CefRefPtr<'+clsname+'> '+parent_sig+'::UnwrapDerived(CefWrapperType type, '+capiname+'* s) {\n' + \
+             unwrapderived + \
+             '  NOTREACHED() << "Unexpected class type: " << type;\n'+ \
+             '  return nullptr;\n'+ \
+             '}\n\n'
+
+  const += 'template<> CefWrapperType ' + parent_sig + '::kWrapperType = ' + get_wrapper_type_enum(
+      clsname) + ';'
+
+  result += '\n\n' + const
+
+  return result
+
+
+def make_cpptoc_global_impl(header, impl):
+  # structure names that have already been defined
+  defined_names = header.get_defined_structs()
+
+  # retrieve the existing global function implementations
+  existing = get_function_impls(impl, 'CEF_EXPORT')
+
+  # generate global functions
+  impl = make_cpptoc_function_impl(None,
+                                   header.get_funcs(), existing, None,
+                                   defined_names, False)
+  if len(impl) > 0:
+    impl = '\n// GLOBAL FUNCTIONS - Body may be edited by hand.\n\n' + impl
+
+  includes = ''
+
+  # include required headers for global functions
+  filenames = []
+  for func in header.get_funcs():
+    filename = func.get_file_name()
+    if not filename in filenames:
+      includes += '#include "include/'+func.get_file_name()+'"\n' \
+                  '#include "include/capi/'+func.get_capi_file_name()+'"\n'
+      filenames.append(filename)
+
+  # determine what includes are required by identifying what translation
+  # classes are being used
+  includes += format_translation_includes(header, impl)
+
+  # build the final output
+  result = get_copyright()
+
+  result += includes + '\n' + impl
+
+  return result
+
+
+def write_cpptoc_impl(header, clsname, dir):
+  if clsname is None:
+    # global file
+    file = dir
+  else:
+    # class file
+    # give the output file the same directory offset as the input file
+    cls = header.get_class(clsname)
+    dir = os.path.dirname(os.path.join(dir, cls.get_file_name()))
+    file = os.path.join(dir, get_capi_name(clsname[3:], False) + '_cpptoc.cc')
+
+  if path_exists(file):
+    oldcontents = read_file(file)
+  else:
+    oldcontents = ''
+
+  if clsname is None:
+    newcontents = make_cpptoc_global_impl(header, oldcontents)
+  else:
+    newcontents = make_cpptoc_class_impl(header, clsname, oldcontents)
+  return (file, newcontents)
+
+
+# test the module
+if __name__ == "__main__":
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) < 4:
+    sys.stderr.write('Usage: ' + sys.argv[0] +
+                     ' <infile> <classname> <existing_impl>')
+    sys.exit()
+
+  # create the header object
+  header = obj_header()
+  header.add_file(sys.argv[1])
+
+  # read the existing implementation file into memory
+  try:
+    with open(sys.argv[3], 'r') as f:
+      data = f.read()
+  except IOError as e:
+    (errno, strerror) = e.args
+    raise Exception('Failed to read file ' + sys.argv[3] + ': ' + strerror)
+  else:
+    f.close()
+
+  # dump the result to stdout
+  sys.stdout.write(make_cpptoc_class_impl(header, sys.argv[2], data))
diff --git a/src/tools/make_ctocpp_header.py b/src/tools/make_ctocpp_header.py
new file mode 100644
index 0000000..2218198
--- /dev/null
+++ b/src/tools/make_ctocpp_header.py
@@ -0,0 +1,154 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+
+
+def make_function_body_block(cls):
+  impl = '  // ' + cls.get_name() + ' methods.\n'
+
+  funcs = cls.get_virtual_funcs()
+  for func in funcs:
+    impl += '  ' + func.get_cpp_proto()
+    if cls.is_client_side():
+      impl += ' override;\n'
+    else:
+      impl += ' OVERRIDE;\n'
+
+  return impl
+
+
+def make_function_body(header, cls):
+  impl = make_function_body_block(cls)
+
+  cur_cls = cls
+  while True:
+    parent_name = cur_cls.get_parent_name()
+    if is_base_class(parent_name):
+      break
+    else:
+      parent_cls = header.get_class(parent_name)
+      if parent_cls is None:
+        raise Exception('Class does not exist: ' + parent_name)
+      if len(impl) > 0:
+        impl += '\n'
+      impl += make_function_body_block(parent_cls)
+    cur_cls = header.get_class(parent_name)
+
+  return impl
+
+
+def make_ctocpp_header(header, clsname):
+  cls = header.get_class(clsname)
+  if cls is None:
+    raise Exception('Class does not exist: ' + clsname)
+
+  clientside = cls.is_client_side()
+
+  directory = cls.get_file_directory()
+  defname = ''
+  if not directory is None:
+    defname += directory + '_'
+  defname += get_capi_name(clsname[3:], False)
+  defname = defname.upper()
+
+  capiname = cls.get_capi_name()
+
+  result = get_copyright()
+
+  result += '#ifndef CEF_LIBCEF_DLL_CTOCPP_'+defname+'_CTOCPP_H_\n'+ \
+            '#define CEF_LIBCEF_DLL_CTOCPP_'+defname+'_CTOCPP_H_\n' + \
+            '#pragma once\n'
+
+  if clientside:
+    result += """
+#if !defined(BUILDING_CEF_SHARED)
+#error This file can be included DLL-side only
+#endif
+"""
+  else:
+    result += """
+#if !defined(WRAPPING_CEF_SHARED)
+#error This file can be included wrapper-side only
+#endif
+"""
+
+  # build the function body
+  func_body = make_function_body(header, cls)
+
+  # include standard headers
+  if func_body.find('std::map') > 0 or func_body.find('std::multimap') > 0:
+    result += '\n#include <map>'
+  if func_body.find('std::vector') > 0:
+    result += '\n#include <vector>'
+
+  # include the headers for this class
+  result += '\n#include "include/'+cls.get_file_name()+'"'+ \
+            '\n#include "include/capi/'+cls.get_capi_file_name()+'"\n'
+
+  # include headers for any forward declared classes that are not in the same file
+  declares = cls.get_forward_declares()
+  for declare in declares:
+    dcls = header.get_class(declare)
+    if dcls.get_file_name() != cls.get_file_name():
+      result += '#include "include/'+dcls.get_file_name()+'"\n' \
+                '#include "include/capi/'+dcls.get_capi_file_name()+'"\n'
+
+  base_class_name = header.get_base_class_name(clsname)
+  base_scoped = True if base_class_name == 'CefBaseScoped' else False
+  if base_scoped:
+    template_file = 'ctocpp_scoped.h'
+    template_class = 'CefCToCppScoped'
+  else:
+    template_file = 'ctocpp_ref_counted.h'
+    template_class = 'CefCToCppRefCounted'
+
+  result += '#include "libcef_dll/ctocpp/' + template_file + '"'
+  result += '\n\n// Wrap a C structure with a C++ class.\n'
+
+  if clientside:
+    result += '// This class may be instantiated and accessed DLL-side only.\n'
+  else:
+    result += '// This class may be instantiated and accessed wrapper-side only.\n'
+
+  result +=   'class '+clsname+'CToCpp\n'+ \
+              '    : public ' + template_class + '<'+clsname+'CToCpp, '+clsname+', '+capiname+'> {\n'+ \
+              ' public:\n'+ \
+              '  '+clsname+'CToCpp();\n'+ \
+              '  virtual ~'+clsname+'CToCpp();\n\n'
+
+  result += func_body
+  result += '};\n\n'
+
+  result += '#endif  // CEF_LIBCEF_DLL_CTOCPP_' + defname + '_CTOCPP_H_'
+
+  return result
+
+
+def write_ctocpp_header(header, clsname, dir):
+  # give the output file the same directory offset as the input file
+  cls = header.get_class(clsname)
+  dir = os.path.dirname(os.path.join(dir, cls.get_file_name()))
+  file = os.path.join(dir, get_capi_name(clsname[3:], False) + '_ctocpp.h')
+
+  newcontents = make_ctocpp_header(header, clsname)
+  return (file, newcontents)
+
+
+# test the module
+if __name__ == "__main__":
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) < 3:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <infile> <classname>')
+    sys.exit()
+
+  # create the header object
+  header = obj_header()
+  header.add_file(sys.argv[1])
+
+  # dump the result to stdout
+  sys.stdout.write(make_ctocpp_header(header, sys.argv[2]))
diff --git a/src/tools/make_ctocpp_impl.py b/src/tools/make_ctocpp_impl.py
new file mode 100644
index 0000000..52dd9c5
--- /dev/null
+++ b/src/tools/make_ctocpp_impl.py
@@ -0,0 +1,738 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+
+
+def make_ctocpp_impl_proto(clsname, name, func, parts):
+  const = ''
+
+  proto = 'NO_SANITIZE("cfi-icall") '
+  if clsname is None:
+    proto += 'CEF_GLOBAL ' + parts['retval'] + ' '
+  else:
+    proto += parts['retval'] + ' ' + clsname
+    if isinstance(func, obj_function_virtual):
+      proto += 'CToCpp'
+      if func.is_const():
+        const = ' const'
+
+    proto += '::'
+
+  proto += name + '(' + ', '.join(parts['args']) + ')' + const
+  return proto
+
+
+def make_ctocpp_function_impl_existing(clsname, name, func, impl):
+  notify(name + ' has manual edits')
+
+  # retrieve the C++ prototype parts
+  parts = func.get_cpp_parts(True)
+
+  changes = format_translation_changes(impl, parts)
+  if len(changes) > 0:
+    notify(name + ' prototype changed')
+
+  return make_ctocpp_impl_proto(clsname, name, func, parts)+'{'+ \
+         changes+impl['body']+'\n}\n\n'
+
+
+def make_ctocpp_function_impl_new(clsname, name, func, base_scoped):
+  # Special handling for the CefShutdown global function.
+  is_cef_shutdown = name == 'CefShutdown' and isinstance(
+      func.parent, obj_header)
+
+  # build the C++ prototype
+  parts = func.get_cpp_parts(True)
+  result = make_ctocpp_impl_proto(clsname, name, func, parts) + ' {'
+
+  if isinstance(func.parent, obj_class) and \
+      not func.parent.has_attrib('no_debugct_check') and \
+      not base_scoped:
+    result += '\n  shutdown_checker::AssertNotShutdown();\n'
+
+  if isinstance(func, obj_function_virtual):
+    # determine how the struct should be referenced
+    if clsname == func.parent.get_name():
+      result += '\n  ' + get_capi_name(clsname,
+                                       True) + '* _struct = GetStruct();'
+    else:
+      result += '\n  '+func.parent.get_capi_name()+'* _struct = reinterpret_cast<'+\
+                func.parent.get_capi_name()+'*>(GetStruct());'
+
+  invalid = []
+
+  # retrieve the function arguments
+  args = func.get_arguments()
+
+  # determine the argument types
+  for arg in args:
+    if arg.get_arg_type() == 'invalid':
+      invalid.append(arg.get_name())
+
+  # retrieve the function return value
+  retval = func.get_retval()
+  retval_type = retval.get_retval_type()
+  if retval_type == 'invalid':
+    invalid.append('(return value)')
+    retval_default = ''
+  else:
+    retval_default = retval.get_retval_default(False)
+    if len(retval_default) > 0:
+      retval_default = ' ' + retval_default
+
+  # add API hash check
+  if func.has_attrib('api_hash_check'):
+    result += '\n  const char* api_hash = cef_api_hash(0);'\
+              '\n  if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) {'\
+              '\n    // The libcef API hash does not match the current header API hash.'\
+              '\n    NOTREACHED();'\
+              '\n    return'+retval_default+';'\
+              '\n  }\n'
+
+  if isinstance(func, obj_function_virtual):
+    # add the structure size check
+    result += '\n  if (CEF_MEMBER_MISSING(_struct, ' + func.get_capi_name(
+    ) + '))'
+    result += '\n    return' + retval_default + ';\n'
+
+  if len(invalid) > 0:
+    notify(name + ' could not be autogenerated')
+    # code could not be auto-generated
+    result += '\n  // BEGIN DELETE BEFORE MODIFYING'
+    result += '\n  // AUTO-GENERATED CONTENT'
+    result += '\n  // COULD NOT IMPLEMENT DUE TO: ' + ', '.join(invalid)
+    result += '\n  #pragma message("Warning: "__FILE__": ' + name + ' is not implemented")'
+    result += '\n  // END DELETE BEFORE MODIFYING'
+    result += '\n}\n\n'
+    return result
+
+  result += '\n  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING\n'
+
+  result_len = len(result)
+
+  optional = []
+
+  # parameter verification
+  for arg in args:
+    arg_type = arg.get_arg_type()
+    arg_name = arg.get_type().get_name()
+
+    # skip optional params
+    optional_params = arg.parent.get_attrib_list('optional_param')
+    if not optional_params is None and arg_name in optional_params:
+      optional.append(arg_name)
+      continue
+
+    comment = '\n  // Verify param: ' + arg_name + '; type: ' + arg_type
+
+    if arg_type == 'simple_byaddr' or arg_type == 'bool_byaddr':
+      result += comment+\
+                '\n  DCHECK('+arg_name+');'\
+                '\n  if (!'+arg_name+')'\
+                '\n    return'+retval_default+';'
+    elif arg_type == 'refptr_same' or arg_type == 'refptr_diff' or \
+         arg_type == 'ownptr_same' or arg_type == 'ownptr_diff':
+      result += comment+\
+                '\n  DCHECK('+arg_name+'.get());'\
+                '\n  if (!'+arg_name+'.get())'\
+                '\n    return'+retval_default+';'
+    elif arg_type == 'rawptr_same' or arg_type == 'rawptr_diff':
+      result += comment+\
+                '\n  DCHECK('+arg_name+');'\
+                '\n  if (!'+arg_name+')'\
+                '\n    return'+retval_default+';'
+    elif arg_type == 'string_byref_const':
+      result += comment+\
+                '\n  DCHECK(!'+arg_name+'.empty());'\
+                '\n  if ('+arg_name+'.empty())'\
+                '\n    return'+retval_default+';'
+
+    # check index params
+    index_params = arg.parent.get_attrib_list('index_param')
+    if not index_params is None and arg_name in index_params:
+      result += comment+\
+                '\n  DCHECK_GE('+arg_name+', 0);'\
+                '\n  if ('+arg_name+' < 0)'\
+                '\n    return'+retval_default+';'
+
+  if len(optional) > 0:
+    # Wrap the comment at 80 characters.
+    str = '\n  // Unverified params: ' + optional[0]
+    for name in optional[1:]:
+      str += ','
+      if len(str) + len(name) + 1 > 80:
+        result += str
+        str = '\n  //'
+      str += ' ' + name
+    result += str
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  # parameter translation
+  params = []
+  if isinstance(func, obj_function_virtual):
+    params.append('_struct')
+
+  for arg in args:
+    arg_type = arg.get_arg_type()
+    arg_name = arg.get_type().get_name()
+
+    comment = '\n  // Translate param: ' + arg_name + '; type: ' + arg_type
+
+    if arg_type == 'simple_byval' or arg_type == 'simple_byaddr' or \
+       arg_type == 'bool_byval':
+      params.append(arg_name)
+    elif arg_type == 'simple_byref' or arg_type == 'simple_byref_const' or \
+        arg_type == 'struct_byref_const' or arg_type == 'struct_byref':
+      params.append('&' + arg_name)
+    elif arg_type == 'bool_byref':
+      result += comment+\
+                '\n  int '+arg_name+'Int = '+arg_name+';'
+      params.append('&' + arg_name + 'Int')
+    elif arg_type == 'bool_byaddr':
+      result += comment+\
+                '\n  int '+arg_name+'Int = '+arg_name+'?*'+arg_name+':0;'
+      params.append('&' + arg_name + 'Int')
+    elif arg_type == 'string_byref_const':
+      params.append(arg_name + '.GetStruct()')
+    elif arg_type == 'string_byref':
+      params.append(arg_name + '.GetWritableStruct()')
+    elif arg_type == 'refptr_same':
+      ptr_class = arg.get_type().get_ptr_type()
+      params.append(ptr_class + 'CToCpp::Unwrap(' + arg_name + ')')
+    elif arg_type == 'ownptr_same':
+      ptr_class = arg.get_type().get_ptr_type()
+      params.append(ptr_class + 'CToCpp::UnwrapOwn(OWN_PASS(' + arg_name + '))')
+    elif arg_type == 'rawptr_same':
+      ptr_class = arg.get_type().get_ptr_type()
+      params.append(ptr_class + 'CToCpp::UnwrapRaw(' + arg_name + ')')
+    elif arg_type == 'refptr_diff':
+      ptr_class = arg.get_type().get_ptr_type()
+      params.append(ptr_class + 'CppToC::Wrap(' + arg_name + ')')
+    elif arg_type == 'ownptr_diff':
+      ptr_class = arg.get_type().get_ptr_type()
+      params.append(ptr_class + 'CppToC::WrapOwn(OWN_PASS(' + arg_name + '))')
+    elif arg_type == 'rawptr_diff':
+      ptr_class = arg.get_type().get_ptr_type()
+      result += comment+\
+                '\n  CefOwnPtr<'+ptr_class+'CppToC> '+arg_name+'Ptr('+ptr_class+'CppToC::WrapRaw('+arg_name+'));'
+      params.append(arg_name + 'Ptr->GetStruct()')
+    elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
+      ptr_class = arg.get_type().get_ptr_type()
+      ptr_struct = arg.get_type().get_result_ptr_type_root()
+      if arg_type == 'refptr_same_byref':
+        assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + ')'
+      else:
+        assign = ptr_class + 'CppToC::Wrap(' + arg_name + ')'
+      result += comment+\
+                '\n  '+ptr_struct+'* '+arg_name+'Struct = NULL;'\
+                '\n  if ('+arg_name+'.get())'\
+                '\n    '+arg_name+'Struct = '+assign+';'\
+                '\n  '+ptr_struct+'* '+arg_name+'Orig = '+arg_name+'Struct;'
+      params.append('&' + arg_name + 'Struct')
+    elif arg_type == 'string_vec_byref' or arg_type == 'string_vec_byref_const':
+      result += comment+\
+                '\n  cef_string_list_t '+arg_name+'List = cef_string_list_alloc();'\
+                '\n  DCHECK('+arg_name+'List);'\
+                '\n  if ('+arg_name+'List)'\
+                '\n    transfer_string_list_contents('+arg_name+', '+arg_name+'List);'
+      params.append(arg_name + 'List')
+    elif arg_type == 'string_map_single_byref' or arg_type == 'string_map_single_byref_const':
+      result += comment+\
+                '\n  cef_string_map_t '+arg_name+'Map = cef_string_map_alloc();'\
+                '\n  DCHECK('+arg_name+'Map);'\
+                '\n  if ('+arg_name+'Map)'\
+                '\n    transfer_string_map_contents('+arg_name+', '+arg_name+'Map);'
+      params.append(arg_name + 'Map')
+    elif arg_type == 'string_map_multi_byref' or arg_type == 'string_map_multi_byref_const':
+      result += comment+\
+                '\n  cef_string_multimap_t '+arg_name+'Multimap = cef_string_multimap_alloc();'\
+                '\n  DCHECK('+arg_name+'Multimap);'\
+                '\n  if ('+arg_name+'Multimap)'\
+                '\n    transfer_string_multimap_contents('+arg_name+', '+arg_name+'Multimap);'
+      params.append(arg_name + 'Multimap')
+    elif arg_type == 'simple_vec_byref' or arg_type == 'bool_vec_byref' or \
+         arg_type == 'refptr_vec_same_byref' or arg_type == 'refptr_vec_diff_byref':
+      count_func = arg.get_attrib_count_func()
+      vec_type = arg.get_type().get_result_vector_type_root()
+      if arg_type == 'refptr_vec_same_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + '[i])'
+      elif arg_type == 'refptr_vec_diff_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CppToC::Wrap(' + arg_name + '[i])'
+      else:
+        assign = arg_name + '[i]'
+      result += comment+\
+                '\n  size_t '+arg_name+'Size = '+arg_name+'.size();'\
+                '\n  size_t '+arg_name+'Count = std::max('+count_func+'(), '+arg_name+'Size);'\
+                '\n  '+vec_type+'* '+arg_name+'List = NULL;'\
+                '\n  if ('+arg_name+'Count > 0) {'\
+                '\n    '+arg_name+'List = new '+vec_type+'['+arg_name+'Count];'\
+                '\n    DCHECK('+arg_name+'List);'\
+                '\n    if ('+arg_name+'List) {'\
+                '\n       memset('+arg_name+'List, 0, sizeof('+vec_type+')*'+arg_name+'Count);'\
+                '\n    }'\
+                '\n    if ('+arg_name+'List && '+arg_name+'Size > 0) {'\
+                '\n      for (size_t i = 0; i < '+arg_name+'Size; ++i) {'\
+                '\n        '+arg_name+'List[i] = '+assign+';'\
+                '\n      }'\
+                '\n    }'\
+                '\n  }'
+      params.append('&' + arg_name + 'Count')
+      params.append(arg_name + 'List')
+    elif arg_type == 'simple_vec_byref_const' or arg_type == 'bool_vec_byref_const' or \
+         arg_type == 'refptr_vec_same_byref_const' or arg_type == 'refptr_vec_diff_byref_const' or \
+         arg_type == 'rawptr_vec_same_byref_const' or arg_type == 'rawptr_vec_diff_byref_const':
+      count_func = arg.get_attrib_count_func()
+      vec_type = arg.get_type().get_result_vector_type_root()
+      if arg_type == 'simple_vec_byref_const' or arg_type == 'bool_vec_byref_const':
+        assign = arg_name + '[i]'
+      else:
+        ptr_class = arg.get_type().get_ptr_type()
+        if arg_type == 'refptr_vec_same_byref_const':
+          assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + '[i])'
+        elif arg_type == 'refptr_vec_diff_byref_const':
+          assign = ptr_class + 'CppToC::Wrap(' + arg_name + '[i])'
+        elif arg_type == 'rawptr_vec_same_byref_const':
+          assign = ptr_class + 'CToCpp::UnwrapRaw(' + arg_name + '[i])'
+        elif arg_type == 'rawptr_vec_diff_byref_const':
+          assign = ptr_class + 'CppToC::WrapRaw(' + arg_name + '[i]).release()->GetStruct()'
+      result += comment+\
+                '\n  const size_t '+arg_name+'Count = '+arg_name+'.size();'\
+                '\n  '+vec_type+'* '+arg_name+'List = NULL;'\
+                '\n  if ('+arg_name+'Count > 0) {'\
+                '\n    '+arg_name+'List = new '+vec_type+'['+arg_name+'Count];'\
+                '\n    DCHECK('+arg_name+'List);'\
+                '\n    if ('+arg_name+'List) {'\
+                '\n      for (size_t i = 0; i < '+arg_name+'Count; ++i) {'\
+                '\n        '+arg_name+'List[i] = '+assign+';'\
+                '\n      }'\
+                '\n    }'\
+                '\n  }'
+      params.append(arg_name + 'Count')
+      params.append(arg_name + 'List')
+    else:
+      raise Exception('Unsupported argument type %s for parameter %s in %s' %
+                      (arg_type, arg_name, name))
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  if is_cef_shutdown:
+    result += '\n\n#if DCHECK_IS_ON()'\
+              '\n  shutdown_checker::SetIsShutdown();'\
+              '\n#endif\n'
+
+  # execution
+  result += '\n  // Execute\n  '
+
+  if retval_type != 'none':
+    # has a return value
+    if retval_type == 'simple' or retval_type == 'bool':
+      result += retval.get_type().get_result_simple_type_root()
+    elif retval_type == 'string':
+      result += 'cef_string_userfree_t'
+    elif retval_type == 'refptr_same' or retval_type == 'refptr_diff' or \
+         retval_type == 'ownptr_same' or retval_type == 'ownptr_diff':
+      ptr_struct = retval.get_type().get_result_ptr_type_root()
+      result += ptr_struct + '*'
+    else:
+      raise Exception('Unsupported return type %s in %s' % (retval_type, name))
+
+    result += ' _retval = '
+
+  if isinstance(func, obj_function_virtual):
+    result += '_struct->'
+  result += func.get_capi_name() + '('
+
+  if len(params) > 0:
+    if not isinstance(func, obj_function_virtual):
+      result += '\n      '
+    result += ',\n      '.join(params)
+
+  result += ');\n'
+
+  result_len = len(result)
+
+  # parameter restoration
+  for arg in args:
+    arg_type = arg.get_arg_type()
+    arg_name = arg.get_type().get_name()
+
+    comment = '\n  // Restore param:' + arg_name + '; type: ' + arg_type
+
+    if arg_type == 'bool_byref':
+      result += comment+\
+                '\n  '+arg_name+' = '+arg_name+'Int?true:false;'
+    elif arg_type == 'bool_byaddr':
+      result += comment+\
+                '\n  if ('+arg_name+')'\
+                '\n    *'+arg_name+' = '+arg_name+'Int?true:false;'
+    elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
+      ptr_class = arg.get_type().get_ptr_type()
+      ptr_struct = arg.get_type().get_result_ptr_type_root()
+      if arg_type == 'refptr_same_byref':
+        assign = ptr_class + 'CToCpp::Wrap(' + arg_name + 'Struct)'
+      else:
+        assign = ptr_class + 'CppToC::Unwrap(' + arg_name + 'Struct)'
+      result += comment+\
+                '\n  if ('+arg_name+'Struct) {'\
+                '\n    if ('+arg_name+'Struct != '+arg_name+'Orig) {'\
+                '\n      '+arg_name+' = '+assign+';'\
+                '\n    }'\
+                '\n  } else {'\
+                '\n    '+arg_name+' = nullptr;'\
+                '\n  }'
+    elif arg_type == 'string_vec_byref':
+      result += comment+\
+                '\n  if ('+arg_name+'List) {'\
+                '\n    '+arg_name+'.clear();'\
+                '\n    transfer_string_list_contents('+arg_name+'List, '+arg_name+');'\
+                '\n    cef_string_list_free('+arg_name+'List);'\
+                '\n  }'
+    elif arg_type == 'string_vec_byref_const':
+      result += comment+\
+                '\n  if ('+arg_name+'List)'\
+                '\n    cef_string_list_free('+arg_name+'List);'
+    elif arg_type == 'string_map_single_byref':
+      result += comment+\
+                '\n  if ('+arg_name+'Map) {'\
+                '\n    '+arg_name+'.clear();'\
+                '\n    transfer_string_map_contents('+arg_name+'Map, '+arg_name+');'\
+                '\n    cef_string_map_free('+arg_name+'Map);'\
+                '\n  }'
+    elif arg_type == 'string_map_single_byref_const':
+      result += comment+\
+                '\n  if ('+arg_name+'Map)'\
+                '\n    cef_string_map_free('+arg_name+'Map);'
+    elif arg_type == 'string_map_multi_byref':
+      result += comment+\
+                '\n  if ('+arg_name+'Multimap) {'\
+                '\n    '+arg_name+'.clear();'\
+                '\n    transfer_string_multimap_contents('+arg_name+'Multimap, '+arg_name+');'\
+                '\n    cef_string_multimap_free('+arg_name+'Multimap);'\
+                '\n  }'
+    elif arg_type == 'string_map_multi_byref_const':
+      result += comment+\
+                '\n  if ('+arg_name+'Multimap)'\
+                '\n    cef_string_multimap_free('+arg_name+'Multimap);'
+    elif arg_type == 'simple_vec_byref' or arg_type == 'bool_vec_byref' or \
+         arg_type == 'refptr_vec_same_byref' or arg_type == 'refptr_vec_diff_byref':
+      count_func = arg.get_attrib_count_func()
+      vec_type = arg.get_type().get_result_vector_type_root()
+      if arg_type == 'refptr_vec_same_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CToCpp::Wrap(' + arg_name + 'List[i])'
+      elif arg_type == 'refptr_vec_diff_byref':
+        ptr_class = arg.get_type().get_ptr_type()
+        assign = ptr_class + 'CppToC::Unwrap(' + arg_name + 'List[i])'
+      elif arg_type == 'bool_vec_byref':
+        assign = arg_name + 'List[i]?true:false'
+      else:
+        assign = arg_name + 'List[i]'
+      result += comment+\
+                '\n  '+arg_name+'.clear();'\
+                '\n  if ('+arg_name+'Count > 0 && '+arg_name+'List) {'\
+                '\n    for (size_t i = 0; i < '+arg_name+'Count; ++i) {'\
+                '\n      '+arg_name+'.push_back('+assign+');'\
+                '\n    }'\
+                '\n    delete [] '+arg_name+'List;'\
+                '\n  }'
+    elif arg_type == 'simple_vec_byref_const' or arg_type == 'bool_vec_byref_const' or \
+         arg_type == 'refptr_vec_same_byref_const' or arg_type == 'refptr_vec_diff_byref_const' or \
+         arg_type == 'rawptr_vec_same_byref_const' or arg_type == 'rawptr_vec_diff_byref_const':
+      result += comment
+      if arg_type == 'rawptr_vec_diff_byref_const':
+        result += '\n  if ('+arg_name+'Count > 0) {'\
+                  '\n    for (size_t i = 0; i < '+arg_name+'Count; ++i) {'\
+                  '\n      delete '+ptr_class+'CppToC::GetWrapper('+arg_name+'List[i]);'\
+                  '\n    }'\
+                  '\n  }'
+      result += '\n  if ('+arg_name+'List)'\
+                '\n    delete [] '+arg_name+'List;'
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  if len(result) != result_len:
+    result += '\n'
+  result_len = len(result)
+
+  # return translation
+  if retval_type != 'none':
+    # has a return value
+    result += '\n  // Return type: ' + retval_type
+    if retval_type == 'simple':
+      result += '\n  return _retval;'
+    elif retval_type == 'bool':
+      result += '\n  return _retval?true:false;'
+    elif retval_type == 'string':
+      result += '\n  CefString _retvalStr;'\
+                '\n  _retvalStr.AttachToUserFree(_retval);'\
+                '\n  return _retvalStr;'
+    elif retval_type == 'refptr_same' or retval_type == 'ownptr_same':
+      ptr_class = retval.get_type().get_ptr_type()
+      result += '\n  return ' + ptr_class + 'CToCpp::Wrap(_retval);'
+    elif retval_type == 'refptr_diff':
+      ptr_class = retval.get_type().get_ptr_type()
+      result += '\n  return ' + ptr_class + 'CppToC::Unwrap(_retval);'
+    elif retval_type == 'ownptr_diff':
+      ptr_class = retval.get_type().get_ptr_type()
+      result += '\n  return ' + ptr_class + 'CppToC::UnwrapOwn(_retval);'
+    else:
+      raise Exception('Unsupported return type %s in %s' % (retval_type, name))
+
+  if len(result) != result_len:
+    result += '\n'
+
+  result += '}\n\n'
+  return result
+
+
+def make_ctocpp_function_impl(clsname, funcs, existing, base_scoped):
+  impl = ''
+
+  for func in funcs:
+    name = func.get_name()
+    value = get_next_function_impl(existing, name)
+    if not value is None \
+        and value['body'].find('// AUTO-GENERATED CONTENT') < 0:
+      # an implementation exists that was not auto-generated
+      impl += make_ctocpp_function_impl_existing(clsname, name, func, value)
+    else:
+      impl += make_ctocpp_function_impl_new(clsname, name, func, base_scoped)
+
+  return impl
+
+
+def make_ctocpp_virtual_function_impl(header, cls, existing, base_scoped):
+  impl = make_ctocpp_function_impl(cls.get_name(),
+                                   cls.get_virtual_funcs(), existing,
+                                   base_scoped)
+
+  cur_cls = cls
+  while True:
+    parent_name = cur_cls.get_parent_name()
+    if is_base_class(parent_name):
+      break
+    else:
+      parent_cls = header.get_class(parent_name)
+      if parent_cls is None:
+        raise Exception('Class does not exist: ' + parent_name)
+      impl += make_ctocpp_function_impl(cls.get_name(),
+                                        parent_cls.get_virtual_funcs(),
+                                        existing, base_scoped)
+    cur_cls = header.get_class(parent_name)
+
+  return impl
+
+
+def make_ctocpp_unwrap_derived(header, cls, base_scoped):
+  # identify all classes that derive from cls
+  derived_classes = []
+  clsname = cls.get_name()
+  allclasses = header.get_classes()
+  for cur_cls in allclasses:
+    if cur_cls.get_name() == clsname:
+      continue
+    if cur_cls.has_parent(clsname):
+      derived_classes.append(cur_cls.get_name())
+
+  derived_classes = sorted(derived_classes)
+
+  if base_scoped:
+    impl = ['', '']
+    for clsname in derived_classes:
+      impl[0] += '  if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\
+                 '    return reinterpret_cast<'+get_capi_name(cls.get_name(), True)+'*>('+\
+                 clsname+'CToCpp::UnwrapOwn(CefOwnPtr<'+clsname+'>(reinterpret_cast<'+clsname+'*>(c.release()))));\n'+\
+                 '  }\n'
+      impl[1] += '  if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\
+                 '    return reinterpret_cast<'+get_capi_name(cls.get_name(), True)+'*>('+\
+                 clsname+'CToCpp::UnwrapRaw(CefRawPtr<'+clsname+'>(reinterpret_cast<'+clsname+'*>(CEF_RAW_PTR_GET(c)))));\n'+\
+                 '  }\n'
+  else:
+    impl = ''
+    for clsname in derived_classes:
+      impl += '  if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\
+              '    return reinterpret_cast<'+get_capi_name(cls.get_name(), True)+'*>('+\
+              clsname+'CToCpp::Unwrap(reinterpret_cast<'+clsname+'*>(c)));\n'+\
+              '  }\n'
+  return impl
+
+
+def make_ctocpp_class_impl(header, clsname, impl):
+  cls = header.get_class(clsname)
+  if cls is None:
+    raise Exception('Class does not exist: ' + clsname)
+
+  capiname = cls.get_capi_name()
+
+  # retrieve the existing virtual function implementations
+  existing = get_function_impls(impl, clsname + 'CToCpp::')
+
+  base_class_name = header.get_base_class_name(clsname)
+  base_scoped = True if base_class_name == 'CefBaseScoped' else False
+  if base_scoped:
+    template_class = 'CefCToCppScoped'
+  else:
+    template_class = 'CefCToCppRefCounted'
+
+  # generate virtual functions
+  virtualimpl = make_ctocpp_virtual_function_impl(header, cls, existing,
+                                                  base_scoped)
+  if len(virtualimpl) > 0:
+    virtualimpl = '\n// VIRTUAL METHODS - Body may be edited by hand.\n\n' + virtualimpl
+
+  # retrieve the existing static function implementations
+  existing = get_function_impls(impl, clsname + '::')
+
+  # generate static functions
+  staticimpl = make_ctocpp_function_impl(clsname,
+                                         cls.get_static_funcs(), existing,
+                                         base_scoped)
+  if len(staticimpl) > 0:
+    staticimpl = '\n// STATIC METHODS - Body may be edited by hand.\n\n' + staticimpl
+
+  resultingimpl = staticimpl + virtualimpl
+
+  # any derived classes can be unwrapped
+  unwrapderived = make_ctocpp_unwrap_derived(header, cls, base_scoped)
+
+  const =  '// CONSTRUCTOR - Do not edit by hand.\n\n'+ \
+           clsname+'CToCpp::'+clsname+'CToCpp() {\n'+ \
+           '}\n\n'+ \
+           '// DESTRUCTOR - Do not edit by hand.\n\n'+ \
+           clsname+'CToCpp::~'+clsname+'CToCpp() {\n'
+
+  if not cls.has_attrib('no_debugct_check') and not base_scoped:
+    const += '  shutdown_checker::AssertNotShutdown();\n'
+
+  const += '}\n\n'
+
+  # determine what includes are required by identifying what translation
+  # classes are being used
+  includes = format_translation_includes(header, const + resultingimpl +
+                                         (unwrapderived[0]
+                                          if base_scoped else unwrapderived))
+
+  # build the final output
+  result = get_copyright()
+
+  result += includes + '\n' + resultingimpl + '\n'
+
+  parent_sig = template_class + '<' + clsname + 'CToCpp, ' + clsname + ', ' + capiname + '>'
+
+  if base_scoped:
+    const += 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, CefOwnPtr<'+clsname+'> c) {\n'+ \
+             unwrapderived[0] + \
+             '  NOTREACHED() << "Unexpected class type: " << type;\n'+ \
+             '  return nullptr;\n'+ \
+             '}\n\n' + \
+             'template<> '+capiname+'* '+parent_sig+'::UnwrapDerivedRaw(CefWrapperType type, CefRawPtr<'+clsname+'> c) {\n'+ \
+             unwrapderived[1] + \
+             '  NOTREACHED() << "Unexpected class type: " << type;\n'+ \
+             '  return nullptr;\n'+ \
+             '}\n\n'
+  else:
+    const += 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerived(CefWrapperType type, '+clsname+'* c) {\n'+ \
+             unwrapderived + \
+             '  NOTREACHED() << "Unexpected class type: " << type;\n'+ \
+             '  return nullptr;\n'+ \
+             '}\n\n'
+
+  const += 'template<> CefWrapperType ' + parent_sig + '::kWrapperType = ' + get_wrapper_type_enum(
+      clsname) + ';'
+
+  result += const
+
+  return result
+
+
+def make_ctocpp_global_impl(header, impl):
+  # retrieve the existing global function implementations
+  existing = get_function_impls(impl, 'CEF_GLOBAL')
+
+  # generate static functions
+  impl = make_ctocpp_function_impl(None, header.get_funcs(), existing, False)
+  if len(impl) > 0:
+    impl = '\n// GLOBAL METHODS - Body may be edited by hand.\n\n' + impl
+
+  includes = ''
+
+  # include required headers for global functions
+  filenames = []
+  for func in header.get_funcs():
+    filename = func.get_file_name()
+    if not filename in filenames:
+      includes += '#include "include/'+func.get_file_name()+'"\n' \
+                  '#include "include/capi/'+func.get_capi_file_name()+'"\n'
+      filenames.append(filename)
+
+  # determine what includes are required by identifying what translation
+  # classes are being used
+  includes += format_translation_includes(header, impl)
+
+  # build the final output
+  result = get_copyright()
+
+  result += includes + '\n// Define used to facilitate parsing.\n#define CEF_GLOBAL\n\n' + impl
+
+  return result
+
+
+def write_ctocpp_impl(header, clsname, dir):
+  if clsname is None:
+    # global file
+    file = dir
+  else:
+    # class file
+    # give the output file the same directory offset as the input file
+    cls = header.get_class(clsname)
+    dir = os.path.dirname(os.path.join(dir, cls.get_file_name()))
+    file = os.path.join(dir, get_capi_name(clsname[3:], False) + '_ctocpp.cc')
+
+  if path_exists(file):
+    oldcontents = read_file(file)
+  else:
+    oldcontents = ''
+
+  if clsname is None:
+    newcontents = make_ctocpp_global_impl(header, oldcontents)
+  else:
+    newcontents = make_ctocpp_class_impl(header, clsname, oldcontents)
+  return (file, newcontents)
+
+
+# test the module
+if __name__ == "__main__":
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) < 4:
+    sys.stderr.write('Usage: ' + sys.argv[0] +
+                     ' <infile> <classname> <existing_impl>\n')
+    sys.exit()
+
+  # create the header object
+  header = obj_header()
+  header.add_file(sys.argv[1])
+
+  # read the existing implementation file into memory
+  try:
+    with open(sys.argv[3], 'r') as f:
+      data = f.read()
+  except IOError as e:
+    (errno, strerror) = e.args
+    raise Exception('Failed to read file ' + sys.argv[3] + ': ' + strerror)
+
+  # dump the result to stdout
+  sys.stdout.write(make_ctocpp_class_impl(header, sys.argv[2], data))
diff --git a/src/tools/make_distrib.bat b/src/tools/make_distrib.bat
new file mode 100644
index 0000000..aea1ee5
--- /dev/null
+++ b/src/tools/make_distrib.bat
@@ -0,0 +1,2 @@
+@echo off

+python.bat %~dp0\make_distrib.py --output-dir %~dp0\..\binary_distrib\ %*

diff --git a/src/tools/make_distrib.py b/src/tools/make_distrib.py
new file mode 100644
index 0000000..80ab514
--- /dev/null
+++ b/src/tools/make_distrib.py
@@ -0,0 +1,1267 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from cef_version import VersionFormatter
+from date_util import *
+from exec_util import exec_cmd
+from file_util import *
+import git_util as git
+from io import open
+from make_cmake import process_cmake_template
+from optparse import OptionParser
+import os
+import re
+import shlex
+import subprocess
+import sys
+import tarfile
+import zipfile
+
+
+def create_zip_archive(input_dir):
+  """ Creates a zip archive of the specified input directory. """
+  zip_file = input_dir + '.zip'
+  zf = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED, True)
+
+  def addDir(dir):
+    for f in os.listdir(dir):
+      full_path = os.path.join(dir, f)
+      if os.path.isdir(full_path):
+        addDir(full_path)
+      else:
+        zf.write(full_path, os.path.relpath(full_path, \
+                 os.path.join(input_dir, os.pardir)))
+
+  addDir(input_dir)
+  zf.close()
+
+
+def create_tar_archive(input_dir, format):
+  """ Creates a tar archive of the specified input directory. """
+  # Supported formats include "gz" and "bz2".
+  tar_file = input_dir + '.tar.' + format
+  tf = tarfile.open(tar_file, "w:" + format)
+  # The default tar format changed from GNU_FORMAT to PAX_FORMAT in Python 3.8.
+  # However, PAX_FORMAT generates additional @PaxHeader entries and truncates file
+  # names on Windows, so we'll stick with the previous default.
+  tf.format = tarfile.GNU_FORMAT
+  tf.add(input_dir, arcname=os.path.basename(input_dir))
+  tf.close()
+
+
+def create_7z_archive(input_dir, format):
+  """ Creates a 7z archive of the specified input directory. """
+  # CEF_COMMAND_7ZIP might be "c:\Program Files (x86)\7Zip\7z.exe" or /usr/bin/7za
+  # or simply 7z if the user knows that it's in the PATH var. Supported formats
+  # depend on the 7za version -- check the 7-zip documentation for details.
+  command = os.environ['CEF_COMMAND_7ZIP']
+  working_dir = os.path.abspath(os.path.join(input_dir, os.pardir))
+
+  tar_file = None
+  if format in ('xz', 'gzip', 'bzip2'):
+    # These formats only support one file per archive. Create a tar file first.
+    tar_file = input_dir + '.tar'
+    run('"%s" a -ttar -y %s %s' % (command, tar_file, input_dir), working_dir)
+    zip_file = tar_file + '.' + format
+    zip_input = tar_file
+  else:
+    zip_file = input_dir + '.' + format
+    zip_input = input_dir
+
+  # Create the compressed archive.
+  run('"%s" a -t%s -y %s %s' % (command, format, zip_file, zip_input),
+      working_dir)
+
+  if not tar_file is None:
+    remove_file(tar_file)
+
+
+def create_output_dir(name, parent_dir):
+  """ Creates an output directory and adds the path to the archive list. """
+  output_dir = os.path.abspath(os.path.join(parent_dir, name))
+  remove_dir(output_dir, options.quiet)
+  make_dir(output_dir, options.quiet)
+  archive_dirs.append(output_dir)
+  return output_dir
+
+
+def get_readme_component(name):
+  """ Loads a README file component. """
+  paths = []
+  # platform directory
+  if platform == 'windows':
+    platform_cmp = 'win'
+  elif platform == 'macosx':
+    platform_cmp = 'mac'
+  elif platform == 'linux':
+    platform_cmp = 'linux'
+  paths.append(os.path.join(script_dir, 'distrib', platform_cmp))
+
+  # shared directory
+  paths.append(os.path.join(script_dir, 'distrib'))
+
+  # load the file if it exists
+  for path in paths:
+    file = os.path.join(path, 'README.' + name + '.txt')
+    if path_exists(file):
+      return read_file(file)
+
+  raise Exception('Readme component not found: ' + name)
+
+
+def create_readme():
+  """ Creates the README.TXT file. """
+  # gather the components
+  header_data = get_readme_component('header')
+  mode_data = get_readme_component(mode)
+  redistrib_data = get_readme_component('redistrib')
+  footer_data = get_readme_component('footer')
+
+  # format the file
+  data = header_data + '\n\n' + mode_data
+  if mode != 'sandbox':
+    data += '\n\n' + redistrib_data
+  data += '\n\n' + footer_data
+  data = data.replace('$CEF_URL$', cef_url)
+  data = data.replace('$CEF_REV$', cef_rev)
+  data = data.replace('$CEF_VER$', cef_ver)
+  data = data.replace('$CHROMIUM_URL$', chromium_url)
+  data = data.replace('$CHROMIUM_REV$', chromium_rev)
+  data = data.replace('$CHROMIUM_VER$', chromium_ver)
+  data = data.replace('$DATE$', date)
+
+  if platform == 'windows':
+    platform_str = 'Windows'
+  elif platform == 'macosx':
+    platform_str = 'Mac OS-X'
+  elif platform == 'linux':
+    platform_str = 'Linux'
+
+  data = data.replace('$PLATFORM$', platform_str)
+
+  if mode == 'standard':
+    distrib_type = 'Standard'
+    distrib_desc = 'This distribution contains all components necessary to build and distribute an\n' \
+                   'application using CEF on the ' + platform_str + ' platform. Please see the LICENSING\n' \
+                   'section of this document for licensing terms and conditions.'
+  elif mode == 'minimal':
+    distrib_type = 'Minimal'
+    distrib_desc = 'This distribution contains the minimial components necessary to build and\n' \
+                   'distribute an application using CEF on the ' + platform_str + ' platform. Please see\n' \
+                   'the LICENSING section of this document for licensing terms and conditions.'
+  elif mode == 'client':
+    distrib_type = 'Client'
+    if platform == 'linux':
+      client_app = 'cefsimple'
+    else:
+      client_app = 'cefclient'
+    distrib_desc = 'This distribution contains a release build of the ' + client_app + ' sample application\n' \
+                   'for the ' + platform_str + ' platform. Please see the LICENSING section of this document for\n' \
+                   'licensing terms and conditions.'
+  elif mode == 'sandbox':
+    distrib_type = 'Sandbox'
+    distrib_desc = 'This distribution contains only the cef_sandbox static library. Please see\n' \
+                   'the LICENSING section of this document for licensing terms and conditions.'
+
+  data = data.replace('$DISTRIB_TYPE$', distrib_type)
+  data = data.replace('$DISTRIB_DESC$', distrib_desc)
+
+  write_file(os.path.join(output_dir, 'README.txt'), data)
+  if not options.quiet:
+    sys.stdout.write('Creating README.TXT file.\n')
+
+
+def create_fuzed_gtest(tests_dir):
+  """ Generate a fuzed version of gtest and build the expected directory structure. """
+  src_gtest_dir = os.path.join(src_dir, 'third_party', 'googletest', 'src',
+                               'googletest')
+  run('%s fuse_gtest_files.py \"%s\"' % (sys.executable, tests_dir),
+      os.path.join(src_gtest_dir, 'scripts'))
+
+  if not options.quiet:
+    sys.stdout.write('Building gtest directory structure.\n')
+
+  target_gtest_dir = os.path.join(tests_dir, 'gtest')
+  gtest_header = os.path.join(target_gtest_dir, 'gtest.h')
+  gtest_cpp = os.path.join(target_gtest_dir, 'gtest-all.cc')
+
+  if not os.path.exists(gtest_header):
+    raise Exception('Generated file not found: %s' % gtest_header)
+  if not os.path.exists(gtest_cpp):
+    raise Exception('Generated file not found: %s' % gtest_cpp)
+
+  # gtest header file at tests/gtest/include/gtest/gtest.h
+  target_gtest_header_dir = os.path.join(target_gtest_dir, 'include', 'gtest')
+  make_dir(target_gtest_header_dir, options.quiet)
+  move_file(gtest_header, target_gtest_header_dir, options.quiet)
+
+  # gtest source file at tests/gtest/src/gtest-all.cc
+  target_gtest_cpp_dir = os.path.join(target_gtest_dir, 'src')
+  make_dir(target_gtest_cpp_dir, options.quiet)
+  move_file(gtest_cpp, target_gtest_cpp_dir, options.quiet)
+
+  # gtest LICENSE file at tests/gtest/LICENSE
+  copy_file(
+      os.path.join(src_gtest_dir, 'LICENSE'), target_gtest_dir, options.quiet)
+
+  # CEF README file at tests/gtest/README.cef
+  copy_file(
+      os.path.join(cef_dir, 'tests', 'gtest', 'README.cef.in'),
+      os.path.join(target_gtest_dir, 'README.cef'), options.quiet)
+
+
+def transfer_gypi_files(src_dir, gypi_paths, gypi_path_prefix, dst_dir, quiet):
+  """ Transfer files from one location to another. """
+  for path in gypi_paths:
+    src = os.path.join(src_dir, path)
+    dst = os.path.join(dst_dir, path.replace(gypi_path_prefix, ''))
+    dst_path = os.path.dirname(dst)
+    make_dir(dst_path, quiet)
+    copy_file(src, dst, quiet)
+
+
+def normalize_headers(file, new_path=''):
+  """ Normalize headers post-processing. Remove the path component from any
+      project include directives. """
+  data = read_file(file)
+  data = re.sub(r'''#include \"(?!include\/)[a-zA-Z0-9_\/]+\/+([a-zA-Z0-9_\.]+)\"''', \
+                "// Include path modified for CEF Binary Distribution.\n#include \""+new_path+"\\1\"", data)
+  write_file(file, data)
+
+
+def eval_transfer_file(cef_dir, script_dir, transfer_cfg, output_dir, quiet):
+  """ Transfer files based on the specified configuration. """
+  if not path_exists(transfer_cfg):
+    return
+
+  configs = eval_file(transfer_cfg)
+  for cfg in configs:
+    dst = os.path.join(output_dir, cfg['target'])
+
+    # perform a copy if source is specified
+    if not cfg['source'] is None:
+      src = os.path.join(cef_dir, cfg['source'])
+      dst_path = os.path.dirname(dst)
+      make_dir(dst_path, quiet)
+      copy_file(src, dst, quiet)
+
+      # place a readme file in the destination directory
+      readme = os.path.join(dst_path, 'README-TRANSFER.txt')
+      if not path_exists(readme):
+        copy_file(
+            os.path.join(script_dir, 'distrib/README-TRANSFER.txt'), readme)
+
+      str = cfg['source'] + "\n"
+      with open(readme, 'a', encoding='utf-8') as fp:
+        if sys.version_info.major == 2:
+          fp.write(str.decode('utf-8'))
+        else:
+          fp.write(str)
+
+    # perform any required post-processing
+    if 'post-process' in cfg:
+      post = cfg['post-process']
+      if post == 'normalize_headers':
+        new_path = ''
+        if 'new_header_path' in cfg:
+          new_path = cfg['new_header_path']
+        normalize_headers(dst, new_path)
+
+
+def transfer_files(cef_dir, script_dir, transfer_cfg_dir, mode, output_dir,
+                   quiet):
+  # Non-mode-specific transfers.
+  transfer_cfg = os.path.join(transfer_cfg_dir, 'transfer.cfg')
+  eval_transfer_file(cef_dir, script_dir, transfer_cfg, output_dir, quiet)
+  # Mode-specific transfers.
+  transfer_cfg = os.path.join(transfer_cfg_dir, 'transfer_%s.cfg' % mode)
+  eval_transfer_file(cef_dir, script_dir, transfer_cfg, output_dir, quiet)
+
+
+# |paths| is a list of dictionary values with the following keys:
+# path        [required]  Input file or directory path relative to |build_dir|.
+#                         By default this will also be the output path relative
+#                         to |dst_dir|.
+# out_path    [optional]  Override the output path relative to |dst_dir|.
+# conditional [optional]  Set to True if the path is conditional on build
+#                         settings. Missing conditional paths will not be
+#                         treated as an error.
+# delete      [optional]  Glob pattern of files to delete after the copy.
+def copy_files_list(build_dir, dst_dir, paths):
+  ''' Copy the files listed in |paths| from |build_dir| to |dst_dir|. '''
+  for entry in paths:
+    source_path = os.path.join(build_dir, entry['path'])
+    if os.path.exists(source_path):
+      target_path = os.path.join(dst_dir, entry['out_path']
+                                 if 'out_path' in entry else entry['path'])
+      make_dir(os.path.dirname(target_path), options.quiet)
+      if os.path.isdir(source_path):
+        copy_dir(source_path, target_path, options.quiet)
+        if 'delete' in entry:
+          for delete_path in get_files(
+              os.path.join(target_path, entry['delete'])):
+            if not os.path.isdir(delete_path):
+              remove_file(delete_path, options.quiet)
+            else:
+              raise Exception('Refusing to delete directory: %s' % delete_path)
+      else:
+        copy_file(source_path, target_path, options.quiet)
+    else:
+      if 'conditional' in entry and entry['conditional']:
+        sys.stdout.write('Missing conditional path: %s.\n' % source_path)
+      else:
+        raise Exception('Missing required path: %s' % source_path)
+
+
+def get_exported_symbols(file):
+  """ Returns the global symbols exported by |file|. """
+  symbols = []
+
+  # Each symbol line has a value like:
+  # 0000000000000000 T _cef_sandbox_initialize
+  cmdline = 'nm -g -U %s' % file
+  result = exec_cmd(cmdline, os.path.join(cef_dir, 'tools'))
+  if len(result['err']) > 0:
+    raise Exception('ERROR: nm failed: %s' % result['err'])
+  for line in result['out'].split('\n'):
+    if line.find(' T ') < 0:
+      continue
+    symbol = line[line.rfind(' ') + 1:]
+    symbols.append(symbol)
+
+  return symbols
+
+
+def get_undefined_symbols(file):
+  """ Returns the undefined symbols imported by |file|. """
+  symbols = []
+
+  # Each symbol line has a value like:
+  # cef_sandbox.a:cef_sandbox.o: _memcpy
+  cmdline = 'nm -u -A %s' % file
+  result = exec_cmd(cmdline, os.path.join(cef_dir, 'tools'))
+  if len(result['err']) > 0:
+    raise Exception('ERROR: nm failed: %s' % result['err'])
+  for line in result['out'].split('\n'):
+    if line.find(': ') < 0:
+      continue
+    symbol = line[line.rfind(': ') + 2:]
+    symbols.append(symbol)
+
+  return symbols
+
+
+def combine_libs(platform, build_dir, libs, dest_lib):
+  """ Combine multiple static libraries into a single static library. """
+  intermediate_obj = None
+  if platform == 'windows':
+    cmdline = 'msvs_env.bat win%s "%s" combine_libs.py -o "%s"' % (
+        platform_arch, sys.executable, dest_lib)
+  elif platform == 'macosx':
+    # Find CEF_EXPORT symbols from libcef_sandbox.a (include/cef_sandbox_mac.h)
+    # Export only symbols that include these strings.
+    symbol_match = [
+        '_cef_',  # C symbols
+        'Cef',  # C++ symbols
+    ]
+
+    print('Finding exported symbols...')
+    assert 'libcef_sandbox.a' in libs[0], libs[0]
+    symbols = []
+    for symbol in get_exported_symbols(os.path.join(build_dir, libs[0])):
+      for match in symbol_match:
+        if symbol.find(match) >= 0:
+          symbols.append(symbol)
+          break
+    assert len(symbols) > 0
+
+    # Create an intermediate object file that combines all other object files.
+    # Symbols not identified above will be made private (local).
+    intermediate_obj = os.path.splitext(dest_lib)[0] + '.o'
+    cmdline = 'ld -arch x86_64 -r -o "%s"' % intermediate_obj
+    for symbol in symbols:
+      cmdline += ' -exported_symbol %s' % symbol
+
+  for lib in libs:
+    lib_path = os.path.join(build_dir, lib)
+    for path in get_files(lib_path):  # Expand wildcards in |lib_path|.
+      if not path_exists(path):
+        raise Exception('File not found: ' + path)
+      cmdline += ' "%s"' % path
+  run(cmdline, os.path.join(cef_dir, 'tools'))
+
+  if not intermediate_obj is None:
+    # Create an archive file containing the new object file.
+    cmdline = 'libtool -static -o "%s" "%s"' % (dest_lib, intermediate_obj)
+    run(cmdline, os.path.join(cef_dir, 'tools'))
+    remove_file(intermediate_obj)
+
+    # Verify that only the expected symbols are exported from the archive file.
+    print('Verifying exported symbols...')
+    result_symbols = get_exported_symbols(dest_lib)
+    if set(symbols) != set(result_symbols):
+      print('Expected', symbols)
+      print('Got', result_symbols)
+      raise Exception('Failure verifying exported symbols')
+
+    # Verify that no C++ symbols are imported by the archive file. If the
+    # archive imports C++ symbols and the client app links an incompatible C++
+    # library, the result will be undefined behavior.
+    print('Verifying imported (undefined) symbols...')
+    undefined_symbols = get_undefined_symbols(dest_lib)
+    cpp_symbols = list(
+        filter(lambda symbol: symbol.startswith('__Z'), undefined_symbols))
+    if cpp_symbols:
+      print('Found C++ symbols:', cpp_symbols)
+      raise Exception('Failure verifying imported (undefined) symbols')
+
+
+def run(command_line, working_dir):
+  """ Run a command. """
+  sys.stdout.write('-------- Running "'+command_line+'" in "'+\
+                   working_dir+'"...'+"\n")
+  args = shlex.split(command_line.replace('\\', '\\\\'))
+  return subprocess.check_call(
+      args, cwd=working_dir, env=os.environ, shell=(sys.platform == 'win32'))
+
+
+def print_error(msg):
+  print('Error: %s\nSee --help for usage.' % msg)
+
+
+# cannot be loaded as a module
+if __name__ != "__main__":
+  sys.stderr.write('This file cannot be loaded as a module!')
+  sys.exit()
+
+# parse command-line options
+disc = """
+This utility builds the CEF Binary Distribution.
+"""
+
+parser = OptionParser(description=disc)
+parser.add_option(
+    '--output-dir',
+    dest='outputdir',
+    metavar='DIR',
+    help='output directory [required]')
+parser.add_option(
+    '--distrib-subdir',
+    dest='distribsubdir',
+    help='name of the subdirectory for the distribution',
+    default='')
+parser.add_option(
+    '--allow-partial',
+    action='store_true',
+    dest='allowpartial',
+    default=False,
+    help='allow creation of partial distributions')
+parser.add_option(
+    '--no-symbols',
+    action='store_true',
+    dest='nosymbols',
+    default=False,
+    help='don\'t create symbol files')
+parser.add_option(
+    '--no-docs',
+    action='store_true',
+    dest='nodocs',
+    default=False,
+    help='don\'t create documentation')
+parser.add_option(
+    '--no-archive',
+    action='store_true',
+    dest='noarchive',
+    default=False,
+    help='don\'t create archives for output directories')
+parser.add_option(
+    '--ninja-build',
+    action='store_true',
+    dest='ninjabuild',
+    default=False,
+    help='build was created using ninja')
+parser.add_option(
+    '--x64-build',
+    action='store_true',
+    dest='x64build',
+    default=False,
+    help='create a 64-bit binary distribution')
+parser.add_option(
+    '--arm-build',
+    action='store_true',
+    dest='armbuild',
+    default=False,
+    help='create an ARM binary distribution (Linux only)')
+parser.add_option(
+    '--arm64-build',
+    action='store_true',
+    dest='arm64build',
+    default=False,
+    help='create an ARM64 binary distribution (Linux only)')
+parser.add_option(
+    '--minimal',
+    action='store_true',
+    dest='minimal',
+    default=False,
+    help='include only release build binary files')
+parser.add_option(
+    '--client',
+    action='store_true',
+    dest='client',
+    default=False,
+    help='include only the sample application')
+parser.add_option(
+    '--sandbox',
+    action='store_true',
+    dest='sandbox',
+    default=False,
+    help='include only the cef_sandbox static library (macOS and Windows only)')
+parser.add_option(
+    '--ozone',
+    action='store_true',
+    dest='ozone',
+    default=False,
+    help='include ozone build related files (Linux only)')
+parser.add_option(
+    '-q',
+    '--quiet',
+    action='store_true',
+    dest='quiet',
+    default=False,
+    help='do not output detailed status information')
+(options, args) = parser.parse_args()
+
+# Test the operating system.
+platform = ''
+if sys.platform == 'win32':
+  platform = 'windows'
+elif sys.platform == 'darwin':
+  platform = 'macosx'
+elif sys.platform.startswith('linux'):
+  platform = 'linux'
+
+# the outputdir option is required
+if options.outputdir is None:
+  print_error('--output-dir is required.')
+  sys.exit()
+
+if options.minimal and options.client:
+  print_error('Cannot specify both --minimal and --client.')
+  sys.exit()
+
+if options.x64build + options.armbuild + options.arm64build > 1:
+  print_error('Invalid combination of build options.')
+  sys.exit()
+
+if options.armbuild and platform != 'linux':
+  print_error('--arm-build is only supported on Linux.')
+  sys.exit()
+
+if options.arm64build and not platform in ('linux', 'windows'):
+  print_error('--arm64-build is only supported on Linux and Windows.')
+  sys.exit()
+
+if options.sandbox and not platform in ('macosx', 'windows'):
+  print_error('--sandbox is only supported on macOS and Windows.')
+  sys.exit()
+
+if not options.ninjabuild:
+  print_error('--ninja-build is required.')
+  sys.exit()
+
+if options.ozone and platform != 'linux':
+  print_error('--ozone is only supported on Linux.')
+  sys.exit()
+
+# script directory
+script_dir = os.path.dirname(__file__)
+
+# CEF root directory
+cef_dir = os.path.abspath(os.path.join(script_dir, os.pardir))
+
+# src directory
+src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir))
+
+if not git.is_checkout(cef_dir):
+  raise Exception('Not a valid checkout: %s' % (cef_dir))
+
+# retrieve information for CEF
+cef_url = git.get_url(cef_dir)
+cef_rev = git.get_hash(cef_dir)
+cef_commit_number = git.get_commit_number(cef_dir)
+
+if not git.is_checkout(src_dir):
+  raise Exception('Not a valid checkout: %s' % (src_dir))
+
+# retrieve information for Chromium
+chromium_url = git.get_url(src_dir)
+chromium_rev = git.get_hash(src_dir)
+
+date = get_date()
+
+# format version strings
+formatter = VersionFormatter()
+cef_ver = formatter.get_version_string()
+chromium_ver = formatter.get_chromium_version_string()
+
+# list of output directories to be archived
+archive_dirs = []
+
+if options.x64build:
+  platform_arch = '64'
+  binary_arch = 'x64'
+elif options.armbuild:
+  platform_arch = 'arm'
+  binary_arch = 'arm'
+elif options.arm64build:
+  platform_arch = 'arm64'
+  binary_arch = 'arm64'
+else:
+  platform_arch = '32'
+  binary_arch = 'x86'
+
+# output directory
+output_dir_base = 'cef_binary_' + cef_ver
+
+if options.distribsubdir == '':
+  output_dir_name = output_dir_base + '_' + platform + platform_arch
+else:
+  output_dir_name = options.distribsubdir
+
+if options.minimal:
+  mode = 'minimal'
+  output_dir_name = output_dir_name + '_minimal'
+elif options.client:
+  mode = 'client'
+  output_dir_name = output_dir_name + '_client'
+elif options.sandbox:
+  mode = 'sandbox'
+  output_dir_name = output_dir_name + '_sandbox'
+else:
+  mode = 'standard'
+
+if options.ozone:
+  output_dir_name = output_dir_name + '_ozone'
+
+output_dir = create_output_dir(output_dir_name, options.outputdir)
+
+# create the README.TXT file
+create_readme()
+
+# transfer the LICENSE.txt file
+copy_file(os.path.join(cef_dir, 'LICENSE.txt'), output_dir, options.quiet)
+
+# read the variables list from the autogenerated cef_paths.gypi file
+cef_paths = eval_file(os.path.join(cef_dir, 'cef_paths.gypi'))
+cef_paths = cef_paths['variables']
+
+# read the variables list from the manually edited cef_paths2.gypi file
+cef_paths2 = eval_file(os.path.join(cef_dir, 'cef_paths2.gypi'))
+cef_paths2 = cef_paths2['variables']
+
+# Determine the build directory suffix. CEF uses a consistent directory naming
+# scheme for GN via GetAllPlatformConfigs in gn_args.py.
+if options.x64build:
+  build_dir_suffix = '_GN_x64'
+elif options.armbuild:
+  build_dir_suffix = '_GN_arm'
+elif options.arm64build:
+  build_dir_suffix = '_GN_arm64'
+else:
+  build_dir_suffix = '_GN_x86'
+
+# Determine the build directory paths.
+out_dir = os.path.join(src_dir, 'out')
+build_dir_debug = os.path.join(out_dir, 'Debug' + build_dir_suffix)
+build_dir_release = os.path.join(out_dir, 'Release' + build_dir_suffix)
+
+if mode == 'standard' or mode == 'minimal':
+  # create the include directory
+  include_dir = os.path.join(output_dir, 'include')
+  make_dir(include_dir, options.quiet)
+
+  # create the cmake directory
+  cmake_dir = os.path.join(output_dir, 'cmake')
+  make_dir(cmake_dir, options.quiet)
+
+  # create the libcef_dll_wrapper directory
+  libcef_dll_dir = os.path.join(output_dir, 'libcef_dll')
+  make_dir(libcef_dll_dir, options.quiet)
+
+  # transfer common include files
+  transfer_gypi_files(cef_dir, cef_paths2['includes_common'], \
+                      'include/', include_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths2['includes_common_capi'], \
+                      'include/', include_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths2['includes_capi'], \
+                      'include/', include_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths2['includes_wrapper'], \
+                      'include/', include_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths['autogen_cpp_includes'], \
+                      'include/', include_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths['autogen_capi_includes'], \
+                      'include/', include_dir, options.quiet)
+
+  # Transfer generated include files.
+  generated_includes = [
+      'cef_pack_resources.h',
+      'cef_pack_strings.h',
+  ]
+  for include in generated_includes:
+    # Debug and Release build should be the same so grab whichever exists.
+    src_path = os.path.join(build_dir_release, 'includes', 'include', include)
+    if not os.path.exists(src_path):
+      src_path = os.path.join(build_dir_debug, 'includes', 'include', include)
+      if not os.path.exists(src_path):
+        raise Exception('Missing generated header file: %s' % include)
+    copy_file(src_path, os.path.join(include_dir, include), options.quiet)
+
+  # transfer common libcef_dll_wrapper files
+  transfer_gypi_files(cef_dir, cef_paths2['libcef_dll_wrapper_sources_base'], \
+                      'libcef_dll/', libcef_dll_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths2['libcef_dll_wrapper_sources_common'], \
+                      'libcef_dll/', libcef_dll_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths['autogen_client_side'], \
+                      'libcef_dll/', libcef_dll_dir, options.quiet)
+
+  if mode == 'standard' or mode == 'minimal':
+    # transfer additional files
+    transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib'), \
+                   mode, output_dir, options.quiet)
+
+  # process cmake templates
+  variables = cef_paths.copy()
+  variables.update(cef_paths2)
+  process_cmake_template(os.path.join(cef_dir, 'CMakeLists.txt.in'), \
+                         os.path.join(output_dir, 'CMakeLists.txt'), \
+                         variables, options.quiet)
+  process_cmake_template(os.path.join(cef_dir, 'cmake', 'cef_macros.cmake.in'), \
+                         os.path.join(cmake_dir, 'cef_macros.cmake'), \
+                         variables, options.quiet)
+  process_cmake_template(os.path.join(cef_dir, 'cmake', 'cef_variables.cmake.in'), \
+                         os.path.join(cmake_dir, 'cef_variables.cmake'), \
+                         variables, options.quiet)
+  process_cmake_template(os.path.join(cef_dir, 'cmake', 'FindCEF.cmake.in'), \
+                         os.path.join(cmake_dir, 'FindCEF.cmake'), \
+                         variables, options.quiet)
+  process_cmake_template(os.path.join(cef_dir, 'libcef_dll', 'CMakeLists.txt.in'), \
+                         os.path.join(libcef_dll_dir, 'CMakeLists.txt'), \
+                         variables, options.quiet)
+
+if mode == 'standard':
+  # create the tests directory
+  tests_dir = os.path.join(output_dir, 'tests')
+  make_dir(tests_dir, options.quiet)
+
+  # create the tests/shared directory
+  shared_dir = os.path.join(tests_dir, 'shared')
+  make_dir(shared_dir, options.quiet)
+
+  if not options.ozone:
+    # create the tests/cefclient directory
+    cefclient_dir = os.path.join(tests_dir, 'cefclient')
+    make_dir(cefclient_dir, options.quiet)
+
+  # create the tests/cefsimple directory
+  cefsimple_dir = os.path.join(tests_dir, 'cefsimple')
+  make_dir(cefsimple_dir, options.quiet)
+
+  # create the tests/ceftests directory
+  ceftests_dir = os.path.join(tests_dir, 'ceftests')
+  make_dir(ceftests_dir, options.quiet)
+
+  # transfer common shared files
+  transfer_gypi_files(cef_dir, cef_paths2['shared_sources_browser'], \
+                      'tests/shared/', shared_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths2['shared_sources_common'], \
+                      'tests/shared/', shared_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths2['shared_sources_renderer'], \
+                      'tests/shared/', shared_dir, options.quiet)
+  transfer_gypi_files(cef_dir, cef_paths2['shared_sources_resources'], \
+                      'tests/shared/', shared_dir, options.quiet)
+
+  if not options.ozone:
+    # transfer common cefclient files
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_browser'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_common'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_renderer'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_resources'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_resources_extensions_set_page_color'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+
+  # transfer common cefsimple files
+  transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_common'], \
+                      'tests/cefsimple/', cefsimple_dir, options.quiet)
+
+  # transfer common ceftests files
+  transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_common'], \
+                      'tests/ceftests/', ceftests_dir, options.quiet)
+
+  # create the fuzed gtest version
+  create_fuzed_gtest(tests_dir)
+
+  # process cmake templates
+  if not options.ozone:
+    process_cmake_template(os.path.join(cef_dir, 'tests', 'cefclient', 'CMakeLists.txt.in'), \
+                           os.path.join(cefclient_dir, 'CMakeLists.txt'), \
+                           variables, options.quiet)
+  process_cmake_template(os.path.join(cef_dir, 'tests', 'cefsimple', 'CMakeLists.txt.in'), \
+                         os.path.join(cefsimple_dir, 'CMakeLists.txt'), \
+                         variables, options.quiet)
+  process_cmake_template(os.path.join(cef_dir, 'tests', 'gtest', 'CMakeLists.txt.in'), \
+                         os.path.join(tests_dir, 'gtest', 'CMakeLists.txt'), \
+                         variables, options.quiet)
+  process_cmake_template(os.path.join(cef_dir, 'tests', 'ceftests', 'CMakeLists.txt.in'), \
+                         os.path.join(ceftests_dir, 'CMakeLists.txt'), \
+                         variables, options.quiet)
+
+  # transfer gypi files
+  copy_file(os.path.join(cef_dir, 'cef_paths.gypi'), \
+            os.path.join(output_dir, 'cef_paths.gypi'), options.quiet)
+  copy_file(os.path.join(cef_dir, 'cef_paths2.gypi'), \
+            os.path.join(output_dir, 'cef_paths2.gypi'), options.quiet)
+
+if platform == 'windows':
+  libcef_dll = 'libcef.dll'
+  libcef_dll_lib = '%s.lib' % libcef_dll
+  libcef_dll_pdb = '%s.pdb' % libcef_dll
+  # yapf: disable
+  binaries = [
+      {'path': 'chrome_elf.dll'},
+      {'path': libcef_dll},
+      {'path': 'libEGL.dll'},
+      {'path': 'libGLESv2.dll'},
+      {'path': 'snapshot_blob.bin', 'conditional': True},
+      {'path': 'v8_context_snapshot.bin', 'conditional': True},
+      {'path': 'swiftshader\\libEGL.dll'},
+      {'path': 'swiftshader\\libGLESv2.dll'},
+  ]
+  # yapf: enable
+
+  if mode == 'client':
+    binaries.append({'path': 'cefclient.exe'})
+  else:
+    binaries.append({'path': libcef_dll_lib, 'out_path': 'libcef.lib'})
+
+  # yapf: disable
+  resources = [
+      {'path': 'cef.pak'},
+      {'path': 'cef_100_percent.pak'},
+      {'path': 'cef_200_percent.pak'},
+      {'path': 'cef_extensions.pak'},
+      {'path': 'devtools_resources.pak'},
+      {'path': 'icudtl.dat'},
+      {'path': 'locales', 'delete': '*.info'},
+  ]
+  # yapf: enable
+
+  cef_sandbox_lib = 'obj\\cef\\cef_sandbox.lib'
+  sandbox_libs = [
+      'obj\\base\\base.lib',
+      'obj\\base\\base_static.lib',
+      'obj\\base\\third_party\\double_conversion\\double_conversion.lib',
+      'obj\\base\\third_party\\dynamic_annotations\\dynamic_annotations.lib',
+      'obj\\base\\win\\pe_image.lib',
+      cef_sandbox_lib,
+      'obj\\sandbox\\win\\sandbox.lib',
+  ]
+
+  # Generate the cef_sandbox.lib merged library. A separate *_sandbox build
+  # should exist when GN is_official_build=true.
+  if mode in ('standard', 'minimal', 'sandbox'):
+    dirs = {
+        'Debug': (build_dir_debug + '_sandbox', build_dir_debug),
+        'Release': (build_dir_release + '_sandbox', build_dir_release)
+    }
+    for dir_name in dirs.keys():
+      for src_dir in dirs[dir_name]:
+        if path_exists(os.path.join(src_dir, cef_sandbox_lib)):
+          dst_dir = os.path.join(output_dir, dir_name)
+          make_dir(dst_dir, options.quiet)
+          combine_libs(platform, src_dir, sandbox_libs,
+                       os.path.join(dst_dir, 'cef_sandbox.lib'))
+          break
+
+  valid_build_dir = None
+
+  if mode == 'standard':
+    # transfer Debug files
+    build_dir = build_dir_debug
+    if not options.allowpartial or path_exists(
+        os.path.join(build_dir, libcef_dll)):
+      valid_build_dir = build_dir
+      dst_dir = os.path.join(output_dir, 'Debug')
+      copy_files_list(build_dir, dst_dir, binaries)
+      copy_files(
+          os.path.join(script_dir, 'distrib/win/%s/*.dll' % binary_arch),
+          dst_dir, options.quiet)
+
+      if not options.nosymbols:
+        # create the symbol output directory
+        symbol_output_dir = create_output_dir(
+            output_dir_name + '_debug_symbols', options.outputdir)
+        # transfer contents
+        copy_file(
+            os.path.join(build_dir, libcef_dll_pdb), symbol_output_dir,
+            options.quiet)
+    else:
+      sys.stdout.write("No Debug build files.\n")
+
+  if mode != 'sandbox':
+    # transfer Release files
+    build_dir = build_dir_release
+    if not options.allowpartial or path_exists(
+        os.path.join(build_dir, libcef_dll)):
+      valid_build_dir = build_dir
+      dst_dir = os.path.join(output_dir, 'Release')
+      copy_files_list(build_dir, dst_dir, binaries)
+      copy_files(
+          os.path.join(script_dir, 'distrib/win/%s/*.dll' % binary_arch),
+          dst_dir, options.quiet)
+
+      if not options.nosymbols:
+        # create the symbol output directory
+        symbol_output_dir = create_output_dir(
+            output_dir_name + '_release_symbols', options.outputdir)
+        # transfer contents
+        copy_file(
+            os.path.join(build_dir, libcef_dll_pdb), symbol_output_dir,
+            options.quiet)
+    else:
+      sys.stdout.write("No Release build files.\n")
+
+  if not valid_build_dir is None:
+    # transfer resource files
+    build_dir = valid_build_dir
+    if mode == 'client':
+      dst_dir = os.path.join(output_dir, 'Release')
+    else:
+      dst_dir = os.path.join(output_dir, 'Resources')
+    copy_files_list(build_dir, dst_dir, resources)
+
+  if mode == 'standard' or mode == 'minimal':
+    # transfer include files
+    transfer_gypi_files(cef_dir, cef_paths2['includes_win'], \
+                        'include/', include_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['includes_win_capi'], \
+                        'include/', include_dir, options.quiet)
+
+    # transfer additional files, if any
+    transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib', 'win'), \
+                   mode, output_dir, options.quiet)
+
+  if mode == 'standard':
+    # transfer shared files
+    transfer_gypi_files(cef_dir, cef_paths2['shared_sources_win'], \
+                        'tests/shared/', shared_dir, options.quiet)
+
+    # transfer cefclient files
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_win'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_resources_win'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+
+    # transfer cefsimple files
+    transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_win'], \
+                        'tests/cefsimple/', cefsimple_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_resources_win'], \
+                        'tests/cefsimple/', cefsimple_dir, options.quiet)
+
+    # transfer ceftests files
+    transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_win'], \
+                        'tests/ceftests/', ceftests_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_resources_win'], \
+                        'tests/ceftests/', ceftests_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_views'], \
+                        'tests/ceftests/', ceftests_dir, options.quiet)
+
+  if not options.nodocs:
+    # generate doc files
+    os.popen('make_cppdocs.bat ' + cef_rev)
+
+    src_dir = os.path.join(cef_dir, 'docs')
+    if path_exists(src_dir):
+      # create the docs output directory
+      docs_output_dir = create_output_dir(output_dir_base + '_docs',
+                                          options.outputdir)
+      # transfer contents
+      copy_dir(src_dir, docs_output_dir, options.quiet)
+
+elif platform == 'macosx':
+  framework_name = 'Chromium Embedded Framework'
+  framework_dsym = '%s.dSYM' % framework_name
+  cefclient_app = 'cefclient.app'
+
+  cef_sandbox_lib = 'obj/cef/libcef_sandbox.a'
+  sandbox_libs = [
+      cef_sandbox_lib,
+      'obj/sandbox/mac/libseatbelt.a',
+      'obj/sandbox/mac/libseatbelt_proto.a',
+      'obj/third_party/protobuf/libprotobuf_lite.a',
+      'obj/buildtools/third_party/libc++/libc++/*.o',
+      'obj/buildtools/third_party/libc++abi/libc++abi/*.o',
+  ]
+
+  # Generate the cef_sandbox.a merged library. A separate *_sandbox build
+  # should exist when GN is_official_build=true.
+  if mode in ('standard', 'minimal', 'sandbox'):
+    dirs = {
+        'Debug': (build_dir_debug + '_sandbox', build_dir_debug),
+        'Release': (build_dir_release + '_sandbox', build_dir_release)
+    }
+    for dir_name in dirs.keys():
+      for src_dir in dirs[dir_name]:
+        if path_exists(os.path.join(src_dir, cef_sandbox_lib)):
+          dst_dir = os.path.join(output_dir, dir_name)
+          make_dir(dst_dir, options.quiet)
+          combine_libs(platform, src_dir, sandbox_libs,
+                       os.path.join(dst_dir, 'cef_sandbox.a'))
+          break
+
+  valid_build_dir = None
+
+  if mode == 'standard':
+    # transfer Debug files
+    build_dir = build_dir_debug
+    if not options.allowpartial or path_exists(
+        os.path.join(build_dir, cefclient_app)):
+      valid_build_dir = build_dir
+      dst_dir = os.path.join(output_dir, 'Debug')
+      make_dir(dst_dir, options.quiet)
+      framework_src_dir = os.path.join(
+          build_dir, '%s/Contents/Frameworks/%s.framework/Versions/A' %
+          (cefclient_app, framework_name))
+      framework_dst_dir = os.path.join(dst_dir, '%s.framework' % framework_name)
+      copy_dir(framework_src_dir, framework_dst_dir, options.quiet)
+
+      if not options.nosymbols:
+        # create the symbol output directory
+        symbol_output_dir = create_output_dir(
+            output_dir_name + '_debug_symbols', options.outputdir)
+
+        # The real dSYM already exists, just copy it to the output directory.
+        # dSYMs are only generated when is_official_build=true or enable_dsyms=true.
+        # See //build/config/mac/symbols.gni.
+        copy_dir(
+            os.path.join(build_dir, framework_dsym),
+            os.path.join(symbol_output_dir, framework_dsym), options.quiet)
+    else:
+      sys.stdout.write("No Debug build files.\n")
+
+  if mode != 'sandbox':
+    # transfer Release files
+    build_dir = build_dir_release
+    if not options.allowpartial or path_exists(
+        os.path.join(build_dir, cefclient_app)):
+      valid_build_dir = build_dir
+      dst_dir = os.path.join(output_dir, 'Release')
+      make_dir(dst_dir, options.quiet)
+      framework_src_dir = os.path.join(
+          build_dir, '%s/Contents/Frameworks/%s.framework/Versions/A' %
+          (cefclient_app, framework_name))
+      if mode != 'client':
+        framework_dst_dir = os.path.join(dst_dir,
+                                         '%s.framework' % framework_name)
+      else:
+        copy_dir(
+            os.path.join(build_dir, cefclient_app),
+            os.path.join(dst_dir, cefclient_app), options.quiet)
+        # Replace the versioned framework with an unversioned framework in the sample app.
+        framework_dst_dir = os.path.join(
+            dst_dir, '%s/Contents/Frameworks/%s.framework' % (cefclient_app,
+                                                              framework_name))
+        remove_dir(framework_dst_dir, options.quiet)
+      copy_dir(framework_src_dir, framework_dst_dir, options.quiet)
+
+      if not options.nosymbols:
+        # create the symbol output directory
+        symbol_output_dir = create_output_dir(
+            output_dir_name + '_release_symbols', options.outputdir)
+
+        # The real dSYM already exists, just copy it to the output directory.
+        # dSYMs are only generated when is_official_build=true or enable_dsyms=true.
+        # See //build/config/mac/symbols.gni.
+        copy_dir(
+            os.path.join(build_dir, framework_dsym),
+            os.path.join(symbol_output_dir, framework_dsym), options.quiet)
+    else:
+      sys.stdout.write("No Release build files.\n")
+
+  if mode == 'standard' or mode == 'minimal':
+    # transfer include files
+    transfer_gypi_files(cef_dir, cef_paths2['includes_mac'], \
+                        'include/', include_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['includes_mac_capi'], \
+                        'include/', include_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['includes_wrapper_mac'], \
+                        'include/', include_dir, options.quiet)
+
+    # transfer libcef_dll_wrapper files
+    transfer_gypi_files(cef_dir, cef_paths2['libcef_dll_wrapper_sources_mac'], \
+                      'libcef_dll/', libcef_dll_dir, options.quiet)
+
+    # transfer additional files, if any
+    transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib', 'mac'), \
+                   mode, output_dir, options.quiet)
+
+  if mode == 'standard':
+    # transfer shared files
+    transfer_gypi_files(cef_dir, cef_paths2['shared_sources_mac'], \
+                        'tests/shared/', shared_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['shared_sources_mac_helper'], \
+                        'tests/shared/', shared_dir, options.quiet)
+
+    # transfer cefclient files
+    transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_mac'], \
+                        'tests/cefclient/', cefclient_dir, options.quiet)
+
+    # transfer cefclient/resources/mac files
+    copy_dir(os.path.join(cef_dir, 'tests/cefclient/resources/mac'), \
+             os.path.join(cefclient_dir, 'resources/mac'), \
+             options.quiet)
+
+    # transfer cefsimple files
+    transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_mac'], \
+                        'tests/cefsimple/', cefsimple_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_mac_helper'], \
+                        'tests/cefsimple/', cefsimple_dir, options.quiet)
+
+    # transfer cefsimple/mac files
+    copy_dir(os.path.join(cef_dir, 'tests/cefsimple/mac'), \
+             os.path.join(cefsimple_dir, 'mac'), \
+             options.quiet)
+
+    # transfer ceftests files
+    transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_mac'], \
+                        'tests/ceftests/', ceftests_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_mac_helper'], \
+                        'tests/ceftests/', ceftests_dir, options.quiet)
+
+    # transfer ceftests/resources/mac files
+    copy_dir(os.path.join(cef_dir, 'tests/ceftests/resources/mac'), \
+             os.path.join(ceftests_dir, 'resources/mac'), \
+             options.quiet)
+
+elif platform == 'linux':
+  libcef_so = 'libcef.so'
+  # yapf: disable
+  binaries = [
+      {'path': 'chrome_sandbox', 'out_path': 'chrome-sandbox'},
+      {'path': libcef_so},
+      {'path': 'libEGL.so'},
+      {'path': 'libGLESv2.so'},
+      {'path': 'snapshot_blob.bin', 'conditional': True},
+      {'path': 'v8_context_snapshot.bin', 'conditional': True},
+      {'path': 'swiftshader/libEGL.so'},
+      {'path': 'swiftshader/libGLESv2.so'},
+  ]
+  # yapf: enable
+  if options.ozone:
+    binaries.append({'path': 'libminigbm.so', 'conditional': True})
+
+  if mode == 'client':
+    binaries.append({'path': 'cefsimple'})
+
+  # yapf: disable
+  resources = [
+      {'path': 'cef.pak'},
+      {'path': 'cef_100_percent.pak'},
+      {'path': 'cef_200_percent.pak'},
+      {'path': 'cef_extensions.pak'},
+      {'path': 'devtools_resources.pak'},
+      {'path': 'icudtl.dat'},
+      {'path': 'locales', 'delete': '*.info'},
+  ]
+  # yapf: enable
+
+  valid_build_dir = None
+
+  if mode == 'standard':
+    # transfer Debug files
+    build_dir = build_dir_debug
+    libcef_path = os.path.join(build_dir, libcef_so)
+    if not options.allowpartial or path_exists(libcef_path):
+      valid_build_dir = build_dir
+      dst_dir = os.path.join(output_dir, 'Debug')
+      copy_files_list(build_dir, dst_dir, binaries)
+    else:
+      sys.stdout.write("No Debug build files.\n")
+
+  # transfer Release files
+  build_dir = build_dir_release
+  libcef_path = os.path.join(build_dir, libcef_so)
+  if not options.allowpartial or path_exists(libcef_path):
+    valid_build_dir = build_dir
+    dst_dir = os.path.join(output_dir, 'Release')
+    copy_files_list(build_dir, dst_dir, binaries)
+  else:
+    sys.stdout.write("No Release build files.\n")
+
+  if not valid_build_dir is None:
+    # transfer resource files
+    build_dir = valid_build_dir
+    if mode == 'client':
+      dst_dir = os.path.join(output_dir, 'Release')
+    else:
+      dst_dir = os.path.join(output_dir, 'Resources')
+    copy_files_list(build_dir, dst_dir, resources)
+
+  if mode == 'standard' or mode == 'minimal':
+    # transfer include files
+    transfer_gypi_files(cef_dir, cef_paths2['includes_linux'], \
+                        'include/', include_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['includes_linux_capi'], \
+                        'include/', include_dir, options.quiet)
+
+    # transfer additional files, if any
+    transfer_files(cef_dir, script_dir, os.path.join(script_dir, 'distrib', 'linux'), \
+                   mode, output_dir, options.quiet)
+
+  if mode == 'standard':
+    # transfer shared files
+    transfer_gypi_files(cef_dir, cef_paths2['shared_sources_linux'], \
+                        'tests/shared/', shared_dir, options.quiet)
+
+    if not options.ozone:
+      # transfer cefclient files
+      transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_linux'], \
+                          'tests/cefclient/', cefclient_dir, options.quiet)
+
+    # transfer cefsimple files
+    transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_linux'], \
+                        'tests/cefsimple/', cefsimple_dir, options.quiet)
+
+    # transfer ceftests files
+    transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_linux'], \
+                        'tests/ceftests/', ceftests_dir, options.quiet)
+    transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_views'], \
+                        'tests/ceftests/', ceftests_dir, options.quiet)
+
+if not options.noarchive:
+  # create an archive for each output directory
+  archive_format = os.getenv('CEF_ARCHIVE_FORMAT', 'zip')
+  if archive_format not in ('zip', 'tar.gz', 'tar.bz2'):
+    raise Exception('Unsupported archive format: %s' % archive_format)
+
+  if os.getenv('CEF_COMMAND_7ZIP', '') != '':
+    archive_format = os.getenv('CEF_COMMAND_7ZIP_FORMAT', '7z')
+
+  for dir in archive_dirs:
+    if not options.quiet:
+      sys.stdout.write("Creating %s archive for %s...\n" %
+                       (archive_format, os.path.basename(dir)))
+    if archive_format == 'zip':
+      create_zip_archive(dir)
+    elif archive_format == 'tar.gz':
+      create_tar_archive(dir, 'gz')
+    elif archive_format == 'tar.bz2':
+      create_tar_archive(dir, 'bz2')
+    else:
+      create_7z_archive(dir, archive_format)
diff --git a/src/tools/make_distrib.sh b/src/tools/make_distrib.sh
new file mode 100755
index 0000000..273a3e1
--- /dev/null
+++ b/src/tools/make_distrib.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python make_distrib.py --output-dir ../binary_distrib/ $@
diff --git a/src/tools/make_gypi_file.py b/src/tools/make_gypi_file.py
new file mode 100644
index 0000000..581a7b5
--- /dev/null
+++ b/src/tools/make_gypi_file.py
@@ -0,0 +1,108 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+
+
+def make_gypi_file(header):
+  # header string
+  result = \
+"""# Copyright (c) $YEAR$ The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+#
+# ---------------------------------------------------------------------------
+#
+# This file was generated by the CEF translator tool and should not edited
+# by hand. See the translator.README.txt file in the tools directory for
+# more information.
+#
+# $hash=$$HASH$$$
+#
+
+{
+  'variables': {
+"""
+
+  filenames = sorted(header.get_file_names())
+
+  # cpp includes
+  result += "    'autogen_cpp_includes': [\n"
+  for filename in filenames:
+    result += "      'include/" + filename + "',\n"
+  result += "    ],\n"
+
+  # capi includes
+  result += "    'autogen_capi_includes': [\n"
+  for filename in filenames:
+    result += "      'include/capi/" + get_capi_file_name(filename) + "',\n"
+  result += "    ],\n"
+
+  classes = sorted(header.get_class_names())
+
+  # library side includes
+  result += "    'autogen_library_side': [\n"
+  for clsname in classes:
+    cls = header.get_class(clsname)
+    filename = get_capi_name(clsname[3:], False)
+    dir = cls.get_file_directory()
+    if not dir is None:
+      filename = dir + '/' + filename
+    if cls.is_library_side():
+      result += "      'libcef_dll/cpptoc/"+filename+"_cpptoc.cc',\n" \
+                "      'libcef_dll/cpptoc/"+filename+"_cpptoc.h',\n"
+    else:
+      result += "      'libcef_dll/ctocpp/"+filename+"_ctocpp.cc',\n" \
+                "      'libcef_dll/ctocpp/"+filename+"_ctocpp.h',\n"
+  result += "    ],\n"
+
+  # client side includes
+  result += "    'autogen_client_side': [\n"
+  for clsname in classes:
+    cls = header.get_class(clsname)
+    filename = get_capi_name(clsname[3:], False)
+    dir = cls.get_file_directory()
+    if not dir is None:
+      filename = dir + '/' + filename
+    if cls.is_library_side():
+      result += "      'libcef_dll/ctocpp/"+filename+"_ctocpp.cc',\n" \
+                "      'libcef_dll/ctocpp/"+filename+"_ctocpp.h',\n"
+    else:
+      result += "      'libcef_dll/cpptoc/"+filename+"_cpptoc.cc',\n" \
+                "      'libcef_dll/cpptoc/"+filename+"_cpptoc.h',\n"
+  result += "    ],\n"
+
+  # footer string
+  result += \
+"""  },
+}
+"""
+
+  # add the copyright year
+  result = result.replace('$YEAR$', get_year())
+
+  return result
+
+
+def write_gypi_file(header, file):
+  newcontents = make_gypi_file(header)
+  return (file, newcontents)
+
+
+# test the module
+if __name__ == "__main__":
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) < 2:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <infile>\n')
+    sys.exit()
+
+  # create the header object
+  header = obj_header()
+  header.add_file(sys.argv[1])
+
+  # dump the result to stdout
+  sys.stdout.write(make_gypi_file(header))
diff --git a/src/tools/make_libcef_dll_dylib_impl.py b/src/tools/make_libcef_dll_dylib_impl.py
new file mode 100644
index 0000000..14e3696
--- /dev/null
+++ b/src/tools/make_libcef_dll_dylib_impl.py
@@ -0,0 +1,216 @@
+# Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+from file_util import *
+import os
+
+# Other headers that export C API functions.
+OTHER_HEADERS = [
+    'cef_api_hash.h',
+    'cef_version.h',
+    'internal/cef_logging_internal.h',
+    'internal/cef_string_list.h',
+    'internal/cef_string_map.h',
+    'internal/cef_string_multimap.h',
+    'internal/cef_string_types.h',
+    'internal/cef_thread_internal.h',
+    'internal/cef_time.h',
+    'internal/cef_trace_event_internal.h',
+]
+
+
+def make_libcef_dll_dylib_impl_parts(name, retval, args):
+  # Split arguments into types and names.
+  arg_types = ''
+  arg_names = ''
+  for arg in args:
+    if len(arg_types) > 0:
+      arg_types += ', '
+      arg_names += ', '
+    pos = arg.rfind(' ')
+    arg_types += arg[0:pos]
+    arg_names += arg[pos + 1:]
+
+  typedef = 'typedef %s (*%s_ptr)(%s);\n' % (retval, name, arg_types)
+
+  declare = '%s_ptr %s;\n' % (name, name)
+
+  init = '  INIT_ENTRY(%s);' % name
+
+  impl = """NO_SANITIZE("cfi-icall") %s %s(%s) {
+  %sg_libcef_pointers.%s(%s);
+}
+
+""" % (retval, name, ', '.join(args), 'return '
+       if retval != 'void' else '', name, arg_names)
+
+  return (typedef, declare, init, impl)
+
+
+def make_libcef_dll_dylib_impl_func(func):
+  name = func.get_capi_name()
+  parts = func.get_capi_parts([])
+  retval = parts['retval']
+  args = parts['args']
+  return make_libcef_dll_dylib_impl_parts(name, retval, args)
+
+
+def make_libcef_dll_dylib_impl(header):
+  filenames = []
+  includes = []
+  ptr_typedef = ''
+  ptr_declare = ''
+  ptr_init = ''
+  ptr_impl = ''
+
+  # Include required headers for global functions.
+  for func in header.get_funcs():
+    typedef, declare, init, impl = make_libcef_dll_dylib_impl_func(func)
+    ptr_typedef += typedef
+    ptr_declare += declare
+    ptr_init += init
+    ptr_impl += impl
+
+    filename = func.get_file_name()
+    if not filename in filenames:
+      includes.append('#include "include/capi/%s"' % func.get_capi_file_name())
+      filenames.append(filename)
+
+  # Include required headers for static class functions.
+  allclasses = header.get_classes()
+  for cls in allclasses:
+    funcs = cls.get_static_funcs()
+    for func in funcs:
+      typedef, declare, init, impl = make_libcef_dll_dylib_impl_func(func)
+      ptr_typedef += typedef
+      ptr_declare += declare
+      ptr_init += init
+      ptr_impl += impl
+
+    if len(funcs) > 0:
+      filename = cls.get_file_name()
+      if not filename in filenames:
+        includes.append('#include "include/capi/%s"' % cls.get_capi_file_name())
+        filenames.append(filename)
+
+  # Parse other headers.
+  root_directory = header.get_root_directory()
+  for other in OTHER_HEADERS:
+    path = os.path.join(root_directory, other)
+    content = read_file(path)
+    funcs = get_function_impls(content, 'CEF_EXPORT', False)
+    for func in funcs:
+      typedef, declare, init, impl = make_libcef_dll_dylib_impl_parts(
+          func['name'], func['retval'], func['args'])
+      ptr_typedef += typedef
+      ptr_declare += declare
+      ptr_init += init
+      ptr_impl += impl
+
+    includes.append('#include "include/%s"' % other)
+
+  # Build the final output.
+  result = get_copyright() + """
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+""" + "\n".join(sorted(includes)) + """
+#include "include/wrapper/cef_library_loader.h"
+
+// GLOBAL WRAPPER FUNCTIONS - Do not edit by hand.
+
+namespace {
+
+void* g_libcef_handle = nullptr;
+
+void* libcef_get_ptr(const char* path, const char* name) {
+  void* ptr = dlsym(g_libcef_handle, name);
+  if (!ptr) {
+    fprintf(stderr, "dlsym %s: %s\\n", path, dlerror());
+  }
+  return ptr;
+}
+
+""" + ptr_typedef + """
+
+struct libcef_pointers {
+""" + ptr_declare + """
+} g_libcef_pointers = {0};
+
+#define INIT_ENTRY(name) \
+  g_libcef_pointers.name = (name##_ptr)libcef_get_ptr(path, #name); \
+  if (!g_libcef_pointers.name) { \
+    return 0; \
+  }
+
+int libcef_init_pointers(const char* path) {
+""" + ptr_init + """
+  return 1;
+}
+
+}  // namespace
+
+int cef_load_library(const char* path) {
+  if (g_libcef_handle)
+    return 0;
+
+  g_libcef_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL | RTLD_FIRST);
+  if (!g_libcef_handle) {
+    fprintf(stderr, "dlopen %s: %s\\n", path, dlerror());
+    return 0;
+  }
+
+  if (!libcef_init_pointers(path)) {
+    cef_unload_library();
+    return 0;
+  }
+
+  return 1;
+}
+
+int cef_unload_library() {
+  int result = 0;
+  if (g_libcef_handle) {
+    result = !dlclose(g_libcef_handle);
+    if (!result) {
+      fprintf(stderr, "dlclose: %s\\n", dlerror());
+    }
+    g_libcef_handle = nullptr;
+  }
+  return result;
+}
+
+""" + ptr_impl
+  return result
+
+
+def write_libcef_dll_dylib_impl(header, file):
+  newcontents = make_libcef_dll_dylib_impl(header)
+  return (file, newcontents)
+
+
+# Test the module.
+if __name__ == "__main__":
+  import sys
+
+  # Verify that the correct number of command-line arguments are provided.
+  if len(sys.argv) < 2:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <cpp_header_dir>\n')
+    sys.exit()
+
+  cpp_header_dir = sys.argv[1]
+
+  # Create the header object. Should match the logic in translator.py.
+  header = obj_header()
+  header.set_root_directory(cpp_header_dir)
+  excluded_files = ['cef_api_hash.h', 'cef_application_mac.h', 'cef_version.h']
+  header.add_directory(cpp_header_dir, excluded_files)
+  header.add_directory(os.path.join(cpp_header_dir, 'test'))
+  header.add_directory(os.path.join(cpp_header_dir, 'views'))
+
+  # Dump the result to stdout.
+  sys.stdout.write(make_libcef_dll_dylib_impl(header))
diff --git a/src/tools/make_pack_header.py b/src/tools/make_pack_header.py
new file mode 100644
index 0000000..4f65d76
--- /dev/null
+++ b/src/tools/make_pack_header.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file.
+"""
+A simple utility function to merge pack resource files into a single resource file.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+from date_util import *
+from file_util import *
+import os
+import re
+import string
+import sys
+
+
+def MakeFileSegment(input, all_names):
+  result = """
+
+// ---------------------------------------------------------------------------
+// From $FILE$:
+"""
+
+  filename = os.path.split(input)[1]
+  result = result.replace('$FILE$', filename)
+
+  contents = read_file(input)
+
+  # Format for Windows builds with resource whitelisting enabled [1]:
+  #   #define IDR_RESOURCE_NAME (::ui::WhitelistedResource<12345>(), 12345)
+  # Format for other builds:
+  #   #define IDR_RESOURCE_NAME 12345
+  # [1] See https://crbug.com/684788#c18
+
+  regex = '#define\s([A-Za-z0-9_]{1,})\s'
+  if contents.find('ui::WhitelistedResource') > 0:
+    regex += '.*<'
+  regex += '([0-9]{1,})'
+
+  # identify the defines in the file
+  p = re.compile(regex)
+  list = p.findall(contents)
+  for name, id in list:
+    # If the same define exists in multiple files add a suffix.
+    if name in all_names:
+      all_names[name] += 1
+      name += '_%d' % all_names[name]
+    else:
+      all_names[name] = 1
+
+    result += "\n#define %s %s" % (name, id)
+
+  return result
+
+
+def MakeFile(output, input):
+  # header string
+  result = \
+"""// Copyright (c) $YEAR$ Marshall A. Greenblatt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the name Chromium Embedded
+// Framework nor the names of its contributors may be used to endorse
+// or promote products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ---------------------------------------------------------------------------
+//
+// This file is generated by the make_pack_header.py tool.
+//
+
+#ifndef $GUARD$
+#define $GUARD$
+#pragma once"""
+
+  # sort the input files by name
+  input = sorted(input, key=lambda path: os.path.split(path)[1])
+
+  all_names = {}
+
+  # generate the file segments
+  for file in input:
+    result += MakeFileSegment(file, all_names)
+
+  # footer string
+  result += \
+"""
+
+#endif  // $GUARD$
+"""
+
+  # add the copyright year
+  result = result.replace('$YEAR$', get_year())
+  # add the guard string
+  filename = os.path.split(output)[1]
+  guard = 'CEF_INCLUDE_' + filename.replace('.', '_').upper() + '_'
+  result = result.replace('$GUARD$', guard)
+
+  write_file_if_changed(output, result)
+
+
+def main(argv):
+  if len(argv) < 3:
+    print(("Usage:\n  %s <output_filename> <input_file1> [input_file2] ... " %
+           argv[0]))
+    sys.exit(-1)
+  MakeFile(argv[1], argv[2:])
+
+
+if '__main__' == __name__:
+  main(sys.argv)
diff --git a/src/tools/make_version_header.bat b/src/tools/make_version_header.bat
new file mode 100644
index 0000000..e888746
--- /dev/null
+++ b/src/tools/make_version_header.bat
@@ -0,0 +1,2 @@
+@echo off

+python.bat tools\make_version_header.py include\cef_version.h

diff --git a/src/tools/make_version_header.py b/src/tools/make_version_header.py
new file mode 100644
index 0000000..fa2325a
--- /dev/null
+++ b/src/tools/make_version_header.py
@@ -0,0 +1,109 @@
+# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import get_copyright
+from cef_version import VersionFormatter
+from date_util import *
+from file_util import *
+import git_util as git
+import sys
+
+
+def make_version_header(header):
+  if not git.is_checkout('.'):
+    raise Exception('Not a valid checkout')
+
+  result = get_copyright(full=True, translator=False) + \
+"""//
+// ---------------------------------------------------------------------------
+//
+// This file was generated by the make_version_header.py tool.
+//
+
+#ifndef CEF_INCLUDE_CEF_VERSION_H_
+#define CEF_INCLUDE_CEF_VERSION_H_
+
+#define CEF_VERSION "$VERSION$"
+#define CEF_VERSION_MAJOR $VERSION_MAJOR$
+#define CEF_VERSION_MINOR $VERSION_MINOR$
+#define CEF_VERSION_PATCH $VERSION_PATCH$
+#define CEF_COMMIT_NUMBER $COMMIT_NUMBER$
+#define CEF_COMMIT_HASH "$COMMIT_HASH$"
+#define COPYRIGHT_YEAR $YEAR$
+
+#define CHROME_VERSION_MAJOR $CHROME_MAJOR$
+#define CHROME_VERSION_MINOR $CHROME_MINOR$
+#define CHROME_VERSION_BUILD $CHROME_BUILD$
+#define CHROME_VERSION_PATCH $CHROME_PATCH$
+
+#define DO_MAKE_STRING(p) #p
+#define MAKE_STRING(p) DO_MAKE_STRING(p)
+
+#ifndef APSTUDIO_HIDDEN_SYMBOLS
+
+#include "include/internal/cef_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns CEF version information for the libcef library. The |entry|
+// parameter describes which version component will be returned:
+// 0 - CEF_VERSION_MAJOR
+// 1 - CEF_VERSION_MINOR
+// 2 - CEF_VERSION_PATCH
+// 3 - CEF_COMMIT_NUMBER
+// 4 - CHROME_VERSION_MAJOR
+// 5 - CHROME_VERSION_MINOR
+// 6 - CHROME_VERSION_BUILD
+// 7 - CHROME_VERSION_PATCH
+///
+CEF_EXPORT int cef_version_info(int entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // APSTUDIO_HIDDEN_SYMBOLS
+
+#endif  // CEF_INCLUDE_CEF_VERSION_H_
+"""
+
+  formatter = VersionFormatter()
+
+  # Substitute hash values for placeholders.
+  result = result.replace('$YEAR$', get_year())
+  result = result.replace('$VERSION$', formatter.get_version_string())
+
+  commit_components = formatter.get_cef_commit_components()
+  for key in ('HASH', 'NUMBER'):
+    result = result.replace('$COMMIT_%s$' % key, str(commit_components[key]))
+
+  version_parts = formatter.get_version_parts()
+  for key in ('MAJOR', 'MINOR', 'PATCH'):
+    result = result.replace('$VERSION_%s$' % key, str(version_parts[key]))
+
+  chrome_version_components = formatter.get_chrome_version_components()
+  for key in ('MAJOR', 'MINOR', 'BUILD', 'PATCH'):
+    result = result.replace('$CHROME_%s$' % key,
+                            str(chrome_version_components[key]))
+
+  return result
+
+
+def write_version_header(output):
+  result = make_version_header(output)
+  return write_file_if_changed(output, result)
+
+
+def main(argv):
+  if len(argv) < 2:
+    print(("Usage:\n  %s <output_filename>" % argv[0]))
+    sys.exit(-1)
+  write_version_header(argv[1])
+
+
+if '__main__' == __name__:
+  main(sys.argv)
diff --git a/src/tools/make_version_header.sh b/src/tools/make_version_header.sh
new file mode 100755
index 0000000..50f83fc
--- /dev/null
+++ b/src/tools/make_version_header.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python tools/make_version_header.py include/cef_version.h
diff --git a/src/tools/make_views_stub_impl.py b/src/tools/make_views_stub_impl.py
new file mode 100644
index 0000000..5302b86
--- /dev/null
+++ b/src/tools/make_views_stub_impl.py
@@ -0,0 +1,96 @@
+# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+from make_ctocpp_impl import *
+
+
+def make_views_function_stub_impl(clsname, func):
+  name = func.get_name()
+
+  # Build the C++ prototype.
+  parts = func.get_cpp_parts(True)
+  result = make_ctocpp_impl_proto(clsname, name, func, parts) + ' {'
+
+  # Retrieve the function return value.
+  retval = func.get_retval()
+  retval_type = retval.get_retval_type()
+  if retval_type == 'invalid':
+    notify(name + ' could not be autogenerated')
+    # Code could not be auto-generated.
+    result += '\n  // COULD NOT IMPLEMENT DUE TO: (return value)'
+    result += '\n  #pragma message("Warning: "__FILE__": ' + name + ' is not implemented")'
+    retval_default = ''
+  else:
+    retval_default = retval.get_retval_default(False)
+
+  result += '\n  NOTIMPLEMENTED();'
+  if retval_default != '':
+    result += '\n  return ' + retval_default + ';'
+
+  result += '\n}\n\n'
+
+  return result
+
+
+def make_views_class_stub_impl(header, cls):
+  impl = ''
+
+  clsname = cls.get_name()
+  funcs = cls.get_static_funcs()
+  for func in funcs:
+    impl += make_views_function_stub_impl(clsname, func)
+
+  return impl
+
+
+def make_views_stub_impl(header):
+  includes = ''
+  impl = ''
+
+  allclasses = header.get_classes()
+  for cls in allclasses:
+    dir = cls.get_file_directory()
+    # Only process files in the views/ directory.
+    if dir != None and dir.find('views') == 0:
+      cls_impl = make_views_class_stub_impl(header, cls)
+      if cls_impl != '':
+        impl += cls_impl
+        includes += '#include "include/' + cls.get_file_name() + '"\n'
+
+  includes += '\n#include "base/logging.h"\n'
+
+  # Build the final output.
+  result = get_copyright() + includes
+  result += '\n\n// STATIC STUB METHODS - Do not edit by hand.\n\n'
+  result += impl
+  return result
+
+
+def write_views_stub_impl(header, file):
+  newcontents = make_views_stub_impl(header)
+  return (file, newcontents)
+
+
+# Test the module.
+if __name__ == "__main__":
+  import sys
+
+  # Verify that the correct number of command-line arguments are provided.
+  if len(sys.argv) < 2:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <cpp_header_dir>\n')
+    sys.exit()
+
+  cpp_header_dir = sys.argv[1]
+
+  # Create the header object. Should match the logic in translator.py.
+  header = obj_header()
+  header.set_root_directory(cpp_header_dir)
+  excluded_files = ['cef_api_hash.h', 'cef_application_mac.h', 'cef_version.h']
+  header.add_directory(cpp_header_dir, excluded_files)
+  header.add_directory(os.path.join(cpp_header_dir, 'views'))
+
+  # Dump the result to stdout.
+  sys.stdout.write(make_views_stub_impl(header))
diff --git a/src/tools/make_wrapper_types_header.py b/src/tools/make_wrapper_types_header.py
new file mode 100644
index 0000000..213c75f
--- /dev/null
+++ b/src/tools/make_wrapper_types_header.py
@@ -0,0 +1,50 @@
+# Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from cef_parser import *
+
+
+def make_wrapper_types_header(header):
+  result = get_copyright()
+
+  result += '#ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_\n'+ \
+            '#define CEF_LIBCEF_DLL_WRAPPER_TYPES_H_\n' + \
+            '#pragma once\n\n' + \
+            'enum CefWrapperType {\n' + \
+            '  WT_BASE_REF_COUNTED = 1,\n' + \
+            '  WT_BASE_SCOPED,\n'
+
+  clsnames = sorted(header.get_class_names())
+  for clsname in clsnames:
+    result += '  ' + get_wrapper_type_enum(clsname) + ',\n'
+
+  result += '\n  WT_LAST\n'
+  result += '};\n\n' + \
+            '#endif  // CEF_LIBCEF_DLL_WRAPPER_TYPES_H_'
+
+  return result
+
+
+def write_wrapper_types_header(header, file):
+  newcontents = make_wrapper_types_header(header)
+  return (file, newcontents)
+
+
+# test the module
+if __name__ == "__main__":
+  import sys
+
+  # verify that the correct number of command-line arguments are provided
+  if len(sys.argv) < 2:
+    sys.stderr.write('Usage: ' + sys.argv[0] + ' <include_dir>\n')
+    sys.exit()
+
+  # create the header object
+  header = obj_header()
+  excluded_files = ['cef_api_hash.h', 'cef_application_mac.h', 'cef_version.h']
+  header.add_directory(sys.argv[1], excluded_files)
+
+  # dump the result to stdout
+  sys.stdout.write(make_wrapper_types_header(header))
diff --git a/src/tools/msvs_env.bat b/src/tools/msvs_env.bat
new file mode 100644
index 0000000..0e797c3
--- /dev/null
+++ b/src/tools/msvs_env.bat
@@ -0,0 +1,73 @@
+@echo off

+:: Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights

+:: reserved. Use of this source code is governed by a BSD-style license

+:: that can be found in the LICENSE file.

+

+:: Set up the environment for use with MSVS tools and then execute whatever

+:: was specified on the command-line.

+

+set RC=

+

+:: Support !! syntax for delayed variable expansion.

+setlocal enabledelayedexpansion

+

+:: Require that platform is passed as the first argument.

+if "%1" == "win32" (

+  set vcvarsbat=vcvars32.bat

+) else if "%1" == "win64" (

+  set vcvarsbat=vcvars64.bat

+) else if "%1" == "winarm64" (

+  set vcvarsbat=vcvarsamd64_arm64.bat

+) else (

+  echo ERROR: Please specify a target platform: win32, win64 or winarm64

+  set ERRORLEVEL=1

+  goto end

+)

+

+:: Check if vcvars is already provided via the environment.

+set vcvars="%CEF_VCVARS%"

+if %vcvars% == "none" goto found_vcvars

+if exist %vcvars% goto found_vcvars

+

+:: Search for the default VS installation path.

+for %%x in ("%PROGRAMFILES(X86)%" "%PROGRAMFILES%") do (

+  for %%y in (2019 2017) do (

+    for %%z in (Professional Enterprise Community BuildTools) do (

+      set vcvars="%%~x\Microsoft Visual Studio\%%y\%%z\VC\Auxiliary\Build\%vcvarsbat%"

+      if exist !vcvars! goto found_vcvars

+    )

+  )

+)

+

+echo ERROR: Failed to find vcvars

+set ERRORLEVEL=1

+goto end

+

+:found_vcvars

+echo vcvars:

+echo %vcvars%

+

+if not %vcvars% == "none" (

+  :: Set this variable to keep VS2017 < 15.5 from changing the current working directory.

+  set "VSCMD_START_DIR=%CD%"

+  call %vcvars%

+)

+

+echo PATH:

+echo %PATH%

+

+:: Remove the first argument and execute the command.

+for /f "tokens=1,* delims= " %%a in ("%*") do set ALL_BUT_FIRST=%%b

+echo command:

+echo %ALL_BUT_FIRST%

+%ALL_BUT_FIRST%

+

+:end

+endlocal & set RC=%ERRORLEVEL%

+goto omega

+

+:returncode

+exit /B %RC%

+

+:omega

+call :returncode %RC%

diff --git a/src/tools/patch.bat b/src/tools/patch.bat
new file mode 100644
index 0000000..ae3616d
--- /dev/null
+++ b/src/tools/patch.bat
@@ -0,0 +1,2 @@
+@echo off

+python.bat %~dp0\patcher.py
\ No newline at end of file
diff --git a/src/tools/patch.sh b/src/tools/patch.sh
new file mode 100755
index 0000000..eb97c53
--- /dev/null
+++ b/src/tools/patch.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python tools/patcher.py
diff --git a/src/tools/patch_updater.bat b/src/tools/patch_updater.bat
new file mode 100644
index 0000000..94ca4de
--- /dev/null
+++ b/src/tools/patch_updater.bat
@@ -0,0 +1,2 @@
+@echo off
+python.bat %~dp0\patch_updater.py %*
\ No newline at end of file
diff --git a/src/tools/patch_updater.py b/src/tools/patch_updater.py
new file mode 100644
index 0000000..b636c3d
--- /dev/null
+++ b/src/tools/patch_updater.py
@@ -0,0 +1,328 @@
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from io import open
+from optparse import Option, OptionParser, OptionValueError
+import os
+import re
+import sys
+from exec_util import exec_cmd
+from file_util import copy_file, move_file, read_file, remove_file
+import git_util as git
+
+backup_ext = '.cefbak'
+
+
+def msg(message):
+  """ Output a message. """
+  sys.stdout.write('--> ' + message + "\n")
+
+
+def linebreak():
+  """ Output a line break. """
+  sys.stdout.write('-' * 80 + "\n")
+
+
+def warn(message):
+  """ Output a warning. """
+  linebreak()
+  sys.stdout.write('!!!! WARNING: ' + message + "\n")
+  linebreak()
+
+
+def extract_paths(file):
+  """ Extract the list of modified paths from the patch file. """
+  paths = []
+  with open(file, 'r', encoding='utf-8') as fp:
+    for line in fp:
+      if line[:4] != '+++ ':
+        continue
+      match = re.match('^([^\t]+)', line[4:])
+      if not match:
+        continue
+      paths.append(match.group(1).strip())
+  return paths
+
+
+# Cannot be loaded as a module.
+if __name__ != "__main__":
+  sys.stderr.write('This file cannot be loaded as a module!')
+  sys.exit()
+
+# Parse command-line options.
+disc = """
+This utility updates existing patch files.
+"""
+
+
+# Support options with multiple arguments.
+class MultipleOption(Option):
+  ACTIONS = Option.ACTIONS + ("extend",)
+  STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
+  TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
+  ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)
+
+  def take_action(self, action, dest, opt, value, values, parser):
+    if action == "extend":
+      values.ensure_value(dest, []).append(value)
+    else:
+      Option.take_action(self, action, dest, opt, value, values, parser)
+
+
+parser = OptionParser(option_class=MultipleOption, description=disc)
+parser.add_option(
+    '--resave',
+    action='store_true',
+    dest='resave',
+    default=False,
+    help='resave existing patch files to pick up manual changes')
+parser.add_option(
+    '--reapply',
+    action='store_true',
+    dest='reapply',
+    default=False,
+    help='reapply the patch without first reverting changes')
+parser.add_option(
+    '--revert',
+    action='store_true',
+    dest='revert',
+    default=False,
+    help='revert all changes from existing patch files')
+parser.add_option(
+    '--backup',
+    action='store_true',
+    dest='backup',
+    default=False,
+    help='backup patched files. Used in combination with --revert.')
+parser.add_option(
+    '--restore',
+    action='store_true',
+    dest='restore',
+    default=False,
+    help='restore backup of patched files that have not changed. If a backup has ' +\
+         'changed the patch file will be resaved. Used in combination with --reapply.')
+parser.add_option(
+    '--patch',
+    action='extend',
+    dest='patch',
+    type='string',
+    default=[],
+    help='optional patch name to process (multiples allowed)')
+parser.add_option(
+    '--add',
+    action='extend',
+    dest='add',
+    type='string',
+    default=[],
+    help='optional relative file paths to add (multiples allowed). Used in ' +\
+         'combination with --resave and a single --patch value.')
+(options, args) = parser.parse_args()
+
+if options.resave and options.revert:
+  print('Invalid combination of options.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if len(options.add) > 0 and (len(options.patch) != 1 or not options.resave):
+  print('--add can only be used with --resave and a single --patch value.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+# The CEF root directory is the parent directory of _this_ script.
+cef_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
+src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir))
+
+# Determine the type of Chromium checkout.
+if not git.is_checkout(src_dir):
+  raise Exception('Not a valid checkout: %s' % src_dir)
+
+patch_dir = os.path.join(cef_dir, 'patch')
+patch_cfg = os.path.join(patch_dir, 'patch.cfg')
+if not os.path.isfile(patch_cfg):
+  raise Exception('File does not exist: %s' % patch_cfg)
+
+# Read the patch configuration file.
+msg('Reading patch config %s' % patch_cfg)
+scope = {}
+exec (compile(open(patch_cfg, "rb").read(), patch_cfg, 'exec'), scope)
+patches = scope["patches"]
+
+failed_patches = {}
+
+# Read each individual patch file.
+patches_dir = os.path.join(patch_dir, 'patches')
+for patch in patches:
+  # If specific patch names are specified only process those patches.
+  if options.patch and not patch['name'] in options.patch:
+    continue
+
+  sys.stdout.write('\n')
+  patch_file = os.path.join(patches_dir, patch['name'] + '.patch')
+
+  if os.path.isfile(patch_file):
+    msg('Reading patch file %s' % patch_file)
+    if 'path' in patch:
+      patch_root_abs = os.path.abspath(os.path.join(src_dir, patch['path']))
+    else:
+      patch_root_abs = src_dir
+
+    # Retrieve the list of paths modified by the patch file.
+    patch_paths = extract_paths(patch_file)
+
+    # List of paths added by the patch file.
+    added_paths = []
+
+    # True if any backed up files have changed.
+    has_backup_changes = False
+
+    if not options.resave:
+      if not options.reapply:
+        # Revert any changes to existing files in the patch.
+        for patch_path in patch_paths:
+          patch_path_abs = os.path.abspath(os.path.join(patch_root_abs, \
+                                                        patch_path))
+          if os.path.exists(patch_path_abs):
+            if options.backup:
+              backup_path_abs = patch_path_abs + backup_ext
+              if not os.path.exists(backup_path_abs):
+                msg('Creating backup of %s' % patch_path_abs)
+                copy_file(patch_path_abs, backup_path_abs)
+              else:
+                msg('Skipping backup of %s' % patch_path_abs)
+
+            msg('Reverting changes to %s' % patch_path_abs)
+            cmd = 'git checkout -- %s' % (patch_path_abs)
+            result = exec_cmd(cmd, patch_root_abs)
+            if result['err'] != '':
+              msg('Failed to revert file: %s' % result['err'])
+              msg('Deleting file %s' % patch_path_abs)
+              os.remove(patch_path_abs)
+              added_paths.append(patch_path_abs)
+            if result['out'] != '':
+              sys.stdout.write(result['out'])
+          else:
+            msg('Skipping non-existing file %s' % patch_path_abs)
+            added_paths.append(patch_path_abs)
+
+      if not options.revert:
+        # Chromium files are occasionally (incorrectly) checked in with Windows
+        # line endings. This will cause the patch tool to fail when attempting
+        # to patch those files on Posix systems. Convert any such files to Posix
+        # line endings before applying the patch.
+        converted_files = []
+        for patch_path in patch_paths:
+          patch_path_abs = os.path.abspath(os.path.join(patch_root_abs, \
+                                                        patch_path))
+          if os.path.exists(patch_path_abs):
+            with open(patch_path_abs, 'r', encoding='utf-8') as fp:
+              contents = fp.read()
+            if "\r\n" in contents:
+              msg('Converting to Posix line endings for %s' % patch_path_abs)
+              converted_files.append(patch_path_abs)
+              contents = contents.replace("\r\n", "\n")
+              with open(patch_path_abs, 'wb') as fp:
+                fp.write(contents)
+
+        # Apply the patch file.
+        msg('Applying patch to %s' % patch_root_abs)
+        patch_string = open(patch_file, 'rb').read()
+        result = exec_cmd('patch -p0', patch_root_abs, patch_string)
+
+        if len(converted_files) > 0:
+          # Restore Windows line endings in converted files so that the diff is
+          # correct if/when the patch file is re-saved.
+          for patch_path_abs in converted_files:
+            with open(patch_path_abs, 'rb') as fp:
+              contents = fp.read()
+            msg('Converting to Windows line endings for %s' % patch_path_abs)
+            contents = contents.replace("\n", "\r\n")
+            with open(patch_path_abs, 'wb') as fp:
+              fp.write(contents)
+
+        if result['err'] != '':
+          raise Exception('Failed to apply patch file: %s' % result['err'])
+        sys.stdout.write(result['out'])
+        if result['out'].find('FAILED') != -1:
+          failed_lines = []
+          for line in result['out'].split('\n'):
+            if line.find('FAILED') != -1:
+              failed_lines.append(line.strip())
+          warn('Failed to apply %s, fix manually and run with --resave' % \
+               patch['name'])
+          failed_patches[patch['name']] = failed_lines
+          continue
+
+        if options.restore:
+          # Restore from backup if a backup exists.
+          for patch_path in patch_paths:
+            patch_path_abs = os.path.abspath(os.path.join(patch_root_abs, \
+                                                          patch_path))
+            backup_path_abs = patch_path_abs + backup_ext
+            if os.path.exists(backup_path_abs):
+              if read_file(patch_path_abs) == read_file(backup_path_abs):
+                msg('Restoring backup of %s' % patch_path_abs)
+                remove_file(patch_path_abs)
+                move_file(backup_path_abs, patch_path_abs)
+              else:
+                msg('Discarding backup of %s' % patch_path_abs)
+                remove_file(backup_path_abs)
+                has_backup_changes = True
+            else:
+              msg('No backup of %s' % patch_path_abs)
+
+    if (not options.revert and not options.reapply) or has_backup_changes:
+      if len(options.add) > 0:
+        # Add additional requested files to the patch.
+        for patch_path in options.add:
+          patch_path_abs = os.path.abspath(os.path.join(patch_root_abs, \
+                                                        patch_path))
+          if os.path.exists(patch_path_abs):
+            msg('Adding file %s' % patch_path_abs)
+            patch_paths.append(patch_path)
+          else:
+            msg('Skipping non-existing file %s' % patch_path_abs)
+
+      msg('Saving changes to %s' % patch_file)
+      if added_paths:
+        # Inform git of the added paths so they appear in the patch file.
+        cmd = 'git add -N %s' % ' '.join(added_paths)
+        result = exec_cmd(cmd, patch_root_abs)
+        if result['err'] != '' and result['err'].find('warning:') != 0:
+          raise Exception('Failed to add paths: %s' % result['err'])
+
+      # Re-create the patch file.
+      patch_paths_str = ' '.join(patch_paths)
+      cmd = 'git diff --no-prefix --relative %s' % patch_paths_str
+      result = exec_cmd(cmd, patch_root_abs)
+      if result['err'] != '' and result['err'].find('warning:') != 0:
+        raise Exception('Failed to create patch file: %s' % result['err'])
+
+      if "\r\n" in result['out']:
+        # Patch files should always be saved with Posix line endings.
+        # This will avoid problems when attempting to re-apply the patch
+        # file on Posix systems.
+        msg('Converting to Posix line endings for %s' % patch_file)
+        result['out'] = result['out'].replace("\r\n", "\n")
+
+      f = open(patch_file, 'w', encoding='utf-8')
+      f.write(result['out'])
+      f.close()
+  else:
+    raise Exception('Patch file does not exist: %s' % patch_file)
+
+if len(failed_patches) > 0:
+  sys.stdout.write("\n")
+  linebreak()
+  sys.stdout.write("!!!! FAILED PATCHES, fix manually and run with --resave\n")
+  for name in sorted(failed_patches.keys()):
+    sys.stdout.write("%s:\n" % name)
+    for line in failed_patches[name]:
+      if sys.platform == 'win32' and line.find('.rej') > 0:
+        # Convert paths to use Windows-style separator.
+        line = line.replace('/', '\\')
+      sys.stdout.write("  %s\n" % line)
+  linebreak()
+  sys.exit(1)
diff --git a/src/tools/patch_updater.sh b/src/tools/patch_updater.sh
new file mode 100755
index 0000000..17b3413
--- /dev/null
+++ b/src/tools/patch_updater.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python tools/patch_updater.py $@
diff --git a/src/tools/patcher.README.txt b/src/tools/patcher.README.txt
new file mode 100644
index 0000000..5705601
--- /dev/null
+++ b/src/tools/patcher.README.txt
@@ -0,0 +1,18 @@
+Chromium Embedded Framework (CEF) Patch Application Tool -- patcher.py
+-------------------------------------------------------------------------------
+
+Document Last Updated: April 26, 2017
+
+
+OVERVIEW
+--------
+
+The CEF patch application tool is used to apply patches to the Chromium, Blink
+and third-party code bases. Currently only unified diff format is supported.
+See the README.txt file in the patch directory for information on how this tool
+is used.
+
+The patch.[bat|sh] file can be used to run the patch application tool with
+command-line arguments that match the default CEF directory structure and
+output options. Run 'patcher.py -h' for a complete list of available command-
+line arguments.
diff --git a/src/tools/patcher.py b/src/tools/patcher.py
new file mode 100644
index 0000000..0ac98ea
--- /dev/null
+++ b/src/tools/patcher.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+import pickle
+from optparse import OptionParser
+import os
+import sys
+from file_util import *
+from git_util import git_apply_patch_file
+
+# Cannot be loaded as a module.
+if __name__ != "__main__":
+  sys.stdout.write('This file cannot be loaded as a module!')
+  sys.exit()
+
+# The CEF root directory is the parent directory of _this_ script.
+cef_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
+cef_patch_dir = os.path.join(cef_dir, 'patch')
+src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir))
+
+
+def write_note(type, note):
+  separator = '-' * 79 + '\n'
+  sys.stdout.write(separator)
+  sys.stdout.write('!!!! %s: %s\n' % (type, note))
+  sys.stdout.write(separator)
+
+
+def apply_patch_file(patch_file, patch_dir):
+  ''' Apply a specific patch file in optional patch directory. '''
+  patch_path = os.path.join(cef_patch_dir, 'patches', patch_file + '.patch')
+
+  if patch_dir is None or len(patch_dir) == 0:
+    patch_dir = src_dir
+  else:
+    if not os.path.isabs(patch_dir):
+      # Apply patch relative to the Chromium 'src' directory.
+      patch_dir = os.path.join(src_dir, patch_dir)
+    patch_dir = os.path.abspath(patch_dir)
+
+  result = git_apply_patch_file(patch_path, patch_dir)
+  if result == 'fail':
+    write_note('ERROR',
+               'This patch failed to apply. Your build will not be correct.')
+  return result
+
+
+def apply_patch_config():
+  ''' Apply patch files based on a configuration file. '''
+  config_file = os.path.join(cef_patch_dir, 'patch.cfg')
+  if not os.path.isfile(config_file):
+    raise Exception('Patch config file %s does not exist.' % config_file)
+
+  # Parse the configuration file.
+  scope = {}
+  exec (compile(open(config_file, "rb").read(), config_file, 'exec'), scope)
+  patches = scope["patches"]
+
+  results = {'apply': 0, 'skip': 0, 'fail': 0}
+
+  for patch in patches:
+    patch_file = patch['name']
+    dopatch = True
+
+    if 'condition' in patch:
+      # Check that the environment variable is set.
+      if patch['condition'] not in os.environ:
+        sys.stdout.write('\nSkipping patch file %s\n' % patch_file)
+        dopatch = False
+
+    if dopatch:
+      result = apply_patch_file(patch_file, patch['path']
+                                if 'path' in patch else None)
+      results[result] += 1
+
+      if 'note' in patch:
+        write_note('NOTE', patch['note'])
+    else:
+      results['skip'] += 1
+
+  sys.stdout.write('\n%d patches total (%d applied, %d skipped, %d failed)\n' % \
+      (len(patches), results['apply'], results['skip'], results['fail']))
+
+  if results['fail'] > 0:
+    sys.stdout.write('\n')
+    write_note('ERROR',
+               '%d patches failed to apply. Your build will not be correct.' %
+               results['fail'])
+    sys.exit(1)
+
+
+# Parse command-line options.
+disc = """
+This utility applies patch files.
+"""
+
+parser = OptionParser(description=disc)
+parser.add_option(
+    '--patch-file', dest='patchfile', metavar='FILE', help='patch source file')
+parser.add_option(
+    '--patch-dir',
+    dest='patchdir',
+    metavar='DIR',
+    help='patch target directory')
+(options, args) = parser.parse_args()
+
+if not options.patchfile is None:
+  apply_patch_file(options.patchfile, options.patchdir)
+else:
+  apply_patch_config()
diff --git a/src/tools/translator.README.txt b/src/tools/translator.README.txt
new file mode 100644
index 0000000..2be478f
--- /dev/null
+++ b/src/tools/translator.README.txt
@@ -0,0 +1,1697 @@
+Chromium Embedded Framework (CEF) Translator Tool -- translator.py
+-------------------------------------------------------------------------------
+
+Document Last Updated: February 14, 2012
+
+
+OVERVIEW
+--------
+
+The CEF translator tool automatically generates CEF source code based on the
+contents of the CEF header file (cef.h). The generated source code includes the
+main C API header file (cef_capi.h) and all files in the libcef_dll/cpptoc and
+libcef_dll/ctocpp directories.
+
+If any differences are detected between the new translator-generated output and
+the file that currently exists on disk a backup of the existing file will be
+created before the new file is written (this behavior can be controlled using
+a command-line switch -- see 'translator.py -h' for more information). Header
+files (*.h) are completely generated by the translator and should never be
+edited by hand. Implementation files (*.cc) may contain user-created content
+within method and function body blocks. The user-created content is extracted
+from the existing file and inserted into the new translator-generated file. Any
+differences between existing method/function prototypes and new method/function
+prototypes in manually edited implementations will be noted as a warning in new
+output file.
+
+   // WARNING - CHANGED ATTRIBUTES
+   //   REMOVED: const wchar_t* key
+   //   ADDED:   int index
+   // WARNING - CHANGED RETURN VALUE
+   //   WAS: void
+   //   NOW: int
+   #pragma message("Warning: "__FILE__": MyFunction prototype has changed")
+
+Auto-generated implementations will be added in the new output file for any
+methods/functions that exist in the CEF header file but did not exist in the
+current on-disk implementation file. Each time the translator re-generates the
+implementation file it will warn if an implementation could not be auto-
+generated. Delete the indicated portion of the generated code after adding the
+implementation manually.
+
+   size_t CEF_CALLBACK frame_new_func(struct _cef_frame_t* self)
+   {
+     // BEGIN DELETE BEFORE MODIFYING
+     // AUTO-GENERATED CONTENT
+     #pragma message("Warning: "__FILE__": frame_new_func is not implemented")
+     // END DELETE BEFORE MODIFYING
+   }
+
+If the complete function or method implementation has been auto-generated the
+body of the function or method will contain the following comment.
+
+   // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
+
+If you edit the implementation manually you should remove this comment so that
+CEF will not discard your changes on the next run of the translator tool.
+
+The 'translator.[bat|sh]' file can be used to run the translator tool with
+command- line arguments that match the default CEF directory structure and
+output options. Run 'translator.py -h' for a complete list of available command-
+line arguments.
+
+
+HEADER ATTRIBUTES
+-----------------
+
+Comment-based attribute tags are added before each function, class and method
+definition in the CEF header file to provide the translator with additional
+information about how the output should be generated. The attribute tags must
+be in the form of a comma-delimited list of name=value pairs. Attribute names
+and values must contain only alpha-numeric characters, numbers and underscores,
+and must all exist on a single line.
+
+   /*--cef(name1=value1,name2=value2,name3=value3)--*/
+
+Supported method/function attributes:
+
+   capi_name=[string]      (Optional) Force a specific output name for the
+                           resulting C API function.
+   optional_param=[param]  (Optional) Parameter name that will be optional
+                           instead of required.
+   index_param=[param]     (Optional) Parameter name representing an index
+                           value that will be verified as >= 0.
+   default_retval=[string] (Required for enumeration types, Optional for other
+                           types) Specify the default return value.
+   count_func=[param:func] (Required for non-const non-string std::vector
+                           types) Specify the C++ function that returns the
+                           count of elements for a vector parameter.
+   api_hash_check          (Optional) If set an API hash check will be added
+                           to the CToCpp version of the method/function.
+   
+Supported class attributes:
+
+   source=[library|client] (Required) Indicates whether the class
+                           implementation is provided by the library or the
+                           client. This effects the generation of guard
+                           blocks in the cpptoc and ctocpp header files.
+   no_debugct_check       (Optional) If set the debug reference count
+                           of the object will not be checked on shutdown.
+
+
+TRANSLATION RULES
+-----------------
+
+All C++ names in the CEF header file are written in CamelCaps format and all
+C API translations are generated in lowercase_underscore format.
+
+
+Translating Classes and Methods
+-------------------------------
+
+Class names and global function names must be prefixed with the 'Cef' string.
+
+   Global function translation
+      C++:   void CefShutdown()
+      C API: void cef_shutdown()
+
+The translation of a C++ class name to a C API structure name is prefixed with
+'_' and postfixed with '_t'.  A typedef of the C API structure to a value
+without the prefixed '_' is also provided and may be used interchangeably.
+
+   Class name translation
+      C++:   class CefPostData
+      C API: typedef struct _cef_post_data_t { ... } cef_post_data_t
+
+The translation of a C++ virtual class method to a C API member function adds a
+'self' structure pointer as the first parameter. This will always be a pointer
+to the structure that contains the member function.
+
+   Virtual method translation
+      C++:   virtual void SetFocus(bool enable)
+      C API: void set_focus(struct _cef_browser_t* self, int enable)
+
+The translation of a C++ static class method to a C API global function
+is prefixed with 'cef_classname_' where 'classname' is the
+lowercase_underscore name of the class that contains the static method.  Any
+repeat of 'classname' in the function name is removed.
+
+   Static method translation
+      C++:   static CefRefPtr<CefRequest> CreateRequest()
+      C API: struct _cef_request_t* cef_request_create()
+
+Implementation of the wrapper method/function body is generally formatted as
+follows.
+
+   Static/Global CppToC (without Return):
+
+      CEF_EXPORT void cef_function(capi_params)
+      {
+        // Parameter Verification (Optional)
+        // Verify the C parameter values.
+        // ...
+
+        // Parameter Translation (Optional)
+        // Convert C parameter values to C++ parameter values.
+        // ...
+
+        // Execution
+        CefFunction(cpp_arams);
+        
+        // Parameter Restoration (Optional)
+        // Retore the C parameter values if changed.
+        // ...
+      }
+   
+   Static/Global CppToC (with Return):
+
+      CEF_EXPORT capi_retval cef_function(capi_params)
+      {
+        // Parameter Verification (Optional)
+        // Verify the C parameter values.
+        // ...
+
+        // Parameter Translation (Optional)
+        // Convert C parameter values to C++ parameter values.
+        // ...
+
+        // Execution
+        cpp_retval _rv = CefFunction(cpp_params);
+        
+        // Parameter Restoration (Optional)
+        // Restore the C parameter values if changed.
+        // ...
+        
+        // Return Translation
+        // Convert the C++ return value to a C return value.
+        return ...;
+      }
+   
+   Static/Global CToCpp (without Return):
+   
+      void CefFunction(cpp_params)
+      {
+        // Parameter Verification (Optional)
+        // Verify the C++ parameter values.
+        // ...
+
+        // Parameter Translation (Optional)
+        // Convert C++ parameter values to C parameter values.
+        // ...
+        
+        // Execution
+        cef_function(capi_params);
+        
+        // Parameter Restoration (Optional)
+        // Restore the C++ parameter values if changed.
+        // ...
+      }
+   
+   Static/Global CToCpp (with Return):
+   
+      cpp_retval CefFunction(cpp_params)
+      {
+        // Parameter Verification (Optional)
+        // Verify the C++ parameter values.
+        // ...
+
+        // Parameter Translation (Optional)
+        // Convert C++ parameter values to C parameter values.
+        // ...
+        
+        // Execution
+        capi_retval _rv = cef_function(capi_params);
+        
+        // Parameter Restoration (Optional)
+        // Restore the C++ parameter values if changed.
+        // ...
+        
+        // Return Translation
+        // Convert the C return value to a C++ return value.
+        return ...;
+      }
+   
+   Member CppToC (without Return):
+   
+      CEF_CALLBACK void class_function(cef_class_t* self, capi_params)
+      {
+        // Parameter Verification.
+        // Verify the C parameter values.
+        DCHECK(self);
+        DCHECK(...);
+        if (!self || ...)
+          return;
+          
+        // Parameter Translation (Optional)
+        // Convert the C parameter values to C++ parameter values.
+        // ...
+
+        // Execution
+        CefClassCppToC::Get(self)->CefFunction(cpp_params);
+        
+        // Parameter Restoration (Optional)
+        // Restore the C parameter values if changed.
+        // ...
+      }
+
+   Member CppToC (with Return):
+   
+      CEF_CALLBACK capi_retval class_function(cef_class_t* self, capi_params)
+      {
+        // Parameter Verification.
+        // Verify the C parameter values.
+        DCHECK(self);
+        DCHECK(...);
+        if (!self || ...)
+          return default_retval; // Configured or defaulted automatically.
+          
+        // Parameter Translation (Optional)
+        // Convert the C parameter values to C++ parameter values.
+        // ...
+
+        // Execution
+        cpp_retval _rv = CefClassCppToC::Get(self)->CefFunction(cpp_params);
+        
+        // Parameter Restoration (Optional)
+        // Restore the C parameter values if changed.
+        // ...
+        
+        // Return Translation
+        // Convert the C++ return value to a C return value.
+        return ...;
+      }
+
+   Member CToCpp (without Return):
+
+      void CefClassCToCpp::Function(cpp_params)
+      {
+        // Structure Verification
+        if (CEF_MEMBER_MISSING(struct_, function))
+          return;
+
+        // Parameter Verification (Optional)
+        // Verify the C++ parameter values.
+        // ...
+
+        // Parameter Translation (Optional)
+        // Convert C++ parameter values to C parameter values.
+        // ...
+        
+        // Execution
+        struct_->class_function(struct_, capi_params);
+        
+        // Parameter Restoration (Optional)
+        // Restore the C++ parameter values if changed.
+        // ...
+      }
+
+   Member CToCpp (with Return):
+
+      cpp_retval CefClassCToCpp::Function(cpp_params)
+      {
+        // Structure Verification
+        if (CEF_MEMBER_MISSING(struct_, function))
+          return default_retval; // Configured or defaulted automatically.
+
+        // Parameter Verification (Optional)
+        // Verify the C++ parameter values.
+        // ...
+
+        // Parameter Translation (Optional)
+        // Convert C++ parameter values to C parameter values.
+        // ...
+        
+        // Execution
+        capi_retval _rv = struct_->class_function(struct_, capi_params);
+        
+        // Parameter Restoration (Optional)
+        // Restore the C++ parameter values if changed.
+        // ...
+        
+        // Return Translation
+        // Convert the C return value to a C++ return value.
+        return ...;
+      }
+   
+
+Translating Data Types
+----------------------
+
+Data types that are available in both C++ and C are left unchanged. This
+includes the 'double', 'int', 'long', 'size_t' and 'void' basic types. Other
+data types have differing levels of support as indicated below. The translation
+tool will terminate with an exception if it encounters a data type that it
+cannot translate.
+
+Parameters:
+
+   Simple/enumeration type by value (simple_byval):
+      C++:   int value
+      C API: int value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(int value)
+      {
+        // Execution
+        CefFunction(value);
+      }
+
+      // CToCpp Example
+      void CefFunction(int value)
+      {
+        // Execution
+        cef_function(value);
+      }
+
+   Simple/enumeration type by reference (simple_byref):
+      C++:   int& value
+      C API: int* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(int* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Parameter Translation
+        int valueVal = value?*value:0;
+          
+        // Execution
+        CefFunction(valueVal);
+        
+        // Parameter Restoration
+        if (value)
+          *value = valueVal;
+      }
+
+      // CToCpp Example
+      void CefFunction(int& value)
+      {
+        // Execution
+        cef_function(&value);
+      }
+
+   Simple/enumeration const type by reference (simple_byref_const):
+      C++:   const int& value
+      C API: const int* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(const int* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Parameter Translation
+        int valueVal = value?*value:0;
+          
+        // Execution
+        CefFunction(valueVal);
+      }
+
+      // CToCpp Example
+      void CefFunction(const int& value)
+      {
+        // Execution
+        cef_function(&value);
+      }
+
+   Simple/enumeration type by address (simple_byaddr):
+      C++:   int* value
+      C API: int* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(int* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Execution
+        CefFunction(value);
+      }
+
+      // CToCpp Example
+      void CefFunction(int* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Execution
+        cef_function(value);
+      }
+
+   Boolean type by value (bool_byval):
+      C++:   bool value
+      C API: int value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(int value)
+      {
+        // Execution
+        CefFunction(value?true:false);
+      }
+
+      // CToCpp Example
+      void CefFunction(bool value)
+      {
+        // Execution
+        cef_function(value);
+      }
+
+   Boolean type by reference (bool_byref):
+      C++:   bool& value
+      C API: int* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(int* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Parameter Translation
+        bool valueBool = (value && *value)?true:false;
+          
+        // Execution
+        CefFunction(valueBool);
+        
+        // Parameter Restoration
+        if (value)
+          *value = valueBool?true:false;
+      }
+
+      // CToCpp Example
+      void CefFunction(bool& value)
+      {
+        // Parameter Translation
+        int valueInt = value;
+
+        // Execution
+        cef_function(&valueInt);
+        
+        // Parameter Restoration
+        value = valueInt?true:false;
+      }
+
+   Boolean type by address (bool_byaddr):
+      C++:   bool* value
+      C API: int* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(int* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Parameter Translation
+        bool valueBool = (value && *value)?true:false;
+          
+        // Execution
+        CefFunction(&valueBool);
+        
+        // Parameter Restoration
+        if (value)
+          *value = valueBool?true:false;
+      }
+
+      // CToCpp Example
+      void CefFunction(bool* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Parameter Translation
+        int valueInt = value?*value:0;
+
+        // Execution
+        cef_function(&valueInt);
+        
+        // Parameter Restoration
+        if (value)
+          *value = valueInt?true:false;
+      }
+
+   Structure const type by reference (struct_byref_const):
+      C++:   const CefPopupFeatures& value
+      C API: const cef_popup_features_t* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(const cef_popup_features_t* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+          
+        // Parameter Translation
+        CefPopupFeatures valueObj;
+        // Reference the existing values instead of copying.
+        if (value)
+          valueObj.Set(*value, false);
+          
+        // Execution
+        CefFunction(valueObj);
+      }
+
+      // CToCpp Example
+      void CefFunction(const CefPopupFeatures& value)
+      {
+        // Execution
+        cef_function(&value);
+      }
+
+   Structure non-const type by reference (struct_byref):
+      C++:   CefWindowInfo& value
+      C API: cef_window_info_t* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_window_info_t* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        CefWindowInfo valueObj;
+        // Take ownership of the values.
+        if (value)
+          valueObj.AttachTo(*value);
+
+        // Execution
+        CefFunction(valueObj);
+
+        // Parameter Restoration
+        // Return the values to the structure.
+        if (value)
+          valueObj.DetachTo(*value);
+      }
+
+      // CToCpp Example
+      void CefFunction(CefWindowInfo& value)
+      {
+        // Execution
+        cef_function(&value);
+      }
+      
+   String const type by reference (string_byref_const):
+      C++:   const CefString& value
+      C API: const cef_string_t* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(const cef_string_t* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Execution
+        CefFunction(CefString(value));
+      }
+
+      // CToCpp Example
+      void CefFunction(const CefString& value)
+      {
+        // Execution
+        cef_function(value.GetStruct());
+      }
+
+   String non-const type by reference (string_byref):
+      C++:   CefString& value
+      C API: cef_string_t* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_string_t* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        CefString valueStr(value);
+
+        // Execution
+        CefFunction(valueStr);
+      }
+
+      // CToCpp Example
+      void CefFunction(CefString& value)
+      {
+        // Execution
+        cef_function(value.GetWritableStruct());
+      }
+
+   Smart pointer type same boundary side (refptr_same):
+      C++:   CefRefPtr<CefBrowser> value
+      C API: cef_browser_t* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_browser_t* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Execution
+        CefFunction(CefBrowserCppToC::Unwrap(value));
+      }
+
+      // CToCpp Example
+      void CefFunction(CefRefPtr<CefBrowser> value)
+      {
+        // Execution
+        cef_function(CefBrowserCToCpp::Unwrap(value));
+      }
+
+   Smart pointer type same boundary side by reference (refptr_same_byref):
+      C++:   CefRefPtr<CefClient>& value
+      C API: cef_client_t** value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_client_t** value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        CefRefPtr<CefClient> valuePtr;
+        if (value && *value)
+          valuePtr = CefClientCppToC::Unwrap(*value);
+        CefClient* valueOrig = valuePtr.get();
+
+        // Execution
+        CefFunction(valuePtr);
+
+        // Parameter Restoration
+        if (value) {
+          if (valuePtr.get()) {
+            if (valuePtr.get() != valueOrig) {
+              // The value has been changed.
+              *value = CefClientCppToC::Wrap(valuePtr);
+            }
+          } else {
+            *value = NULL;
+          }
+        }
+      }
+
+      // CToCpp Example
+      void CefFunction(CefRefPtr<CefClient>& value)
+      {
+        // Parameter Translation
+        cef_client_t* valueStruct = NULL;
+        if(value.get())
+          valueStruct = CefClientCToCpp::Unwrap(value);
+        cef_client_t* valueOrig = valueStruct;
+
+        // Execution
+        cef_function(valueStuct);
+        
+        // Parameter Restoration
+        if (valueStruct) {
+          if (valueStruct != valueOrig) {
+            // The value was changed.
+            value = CefClientCToCpp::Wrap(valueStruct);
+          }
+        } else {
+          value = NULL;
+        }
+      }
+
+   Smart pointer type different boundary side (refptr_diff):
+      C++:   CefRefPtr<CefBrowser> value
+      C API: cef_browser_t* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_browser_t* value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Execution
+        CefFunction(CefBrowserCToCpp::Wrap(value));
+      }
+
+      // CToCpp Example
+      void CefFunction(CefRefPtr<CefBrowser> value)
+      {
+        // Execution
+        cef_function(CefBrowserCppToC::Wrap(value));
+      }
+
+   Smart pointer type different boundary side by reference (refptr_diff_byref):
+      C++:   CefRefPtr<CefClient>& value
+      C API: cef_client_t** value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_client_t** value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        CefRefPtr<CefClient> valuePtr;
+        if (value && *value)
+          valuePtr = CefClientCToCpp::Wrap(*value);
+        CefClient* valueOrig = valuePtr.get();
+
+        // Execution
+        CefFunction(valuePtr);
+
+        // Parameter Restoration
+        if (value) {
+          if (valuePtr.get()) {
+            if (valuePtr.get() != valueOrig) {
+              // The value has been changed.
+              *value = CefClientCToCpp::Unwrap(valuePtr);
+            }
+          } else {
+            *value = NULL;
+          }
+        }
+      }
+
+      // CToCpp Example
+      void CefFunction(CefRefPtr<CefClient>& value)
+      {
+        // Parameter Translation
+        cef_client_t* valueStruct = NULL;
+        if(value.get())
+          valueStruct = CefClientCppToC::Wrap(value);
+        cef_client_t* valueOrig = valueStruct;
+
+        // Execution
+        cef_function(valueStuct);
+        
+        // Parameter Restoration
+        if (valueStruct) {
+          if (valueStruct != valueOrig) {
+            // The value was changed.
+            value = CefClientCppToC::Unwrap(valueStruct);
+          }
+        } else {
+          value = NULL;
+        }
+      }
+
+   String vector type by reference (string_vec_byref):
+      C++:   std::vector<CefString>& value
+      C API: cef_string_list_t value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_string_list_t value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        std::vector<CefString> valueList;
+        transfer_string_list_contents(value, valueList);
+
+        // Execution
+        CefFunction(valueList);
+        
+        // Parameter Restoration
+        cef_string_list_clear(value);
+        transfer_string_list_contents(valueList, value);
+      }
+
+      // CToCpp Example
+      void CefFunction(std::vector<CefString>& value)
+      {
+        // Parameter Translation
+        cef_string_list_t valueList = cef_string_list_alloc();
+        DCHECK(valueList);
+        if (valueList)
+          transfer_string_list_contents(value, valueList);
+
+        // Execution
+        cef_function(valueList);
+        
+        // Parameter Restoration
+        if (valueList) {
+          value.clear();
+          transfer_string_list_contents(valueList, value);
+          cef_string_list_free(valueList);
+        }
+      }
+
+   String vector const type by reference (string_vec_byref_const):
+      C++:   const std::vector<CefString>& value
+      C API: cef_string_list_t value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_string_list_t value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        std::vector<CefString> valueList;
+        transfer_string_list_contents(value, valueList);
+
+        // Execution
+        CefFunction(valueList);
+      }
+
+      // CToCpp Example
+      void CefFunction(const std::vector<CefString>& value)
+      {
+        // Parameter Translation
+        cef_string_list_t valueList = cef_string_list_alloc();
+        DCHECK(valueList);
+        if (valueList)
+          transfer_string_list_contents(value, valueList);
+
+        // Execution
+        cef_function(valueList);
+        
+        // Parameter Restoration
+        if (valueList)
+          cef_string_list_free(valueList);
+      }
+
+   String-to-string single map type by reference (string_map_single_byref):
+      C++:   std::map<CefString,CefString>& value
+      C API: cef_string_map_t value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_string_map_t value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        std::map<CefString,CefString> valueMap;
+        transfer_string_map_contents(value, valueMap);
+
+        // Execution
+        CefFunction(valueMap);
+        
+        // Parameter Restoration
+        cef_string_map_clear(value);
+        transfer_string_map_contents(valueMap, value);
+      }
+
+      // CToCpp Example
+      void CefFunction(std::map<CefString,CefString>& value)
+      {
+        // Parameter Translation
+        cef_string_map_t valueMap = cef_string_map_alloc();
+        DCHECK(valueMap);
+        if (valueMap)
+          transfer_string_map_contents(value, valueMap);
+
+        // Execution
+        cef_function(valueMap);
+        
+        // Parameter Restoration
+        if (valueMap) {
+          value.clear();
+          transfer_string_map_contents(valueMap, value);
+          cef_string_map_free(valueMap);
+        }
+      }
+
+   String-to-string single map const type by reference
+     (string_map_single_byref_const):
+      C++:   const std::map<CefString,CefString>& value
+      C API: cef_string_map_t value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_string_map_t value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        std::map<CefString,CefString> valueMap;
+        transfer_string_map_contents(value, valueMap);
+
+        // Execution
+        CefFunction(valueMap);
+      }
+
+      // CToCpp Example
+      void CefFunction(const std::map<CefString,CefString>& value)
+      {
+        // Parameter Translation
+        cef_string_map_t valueMap = cef_string_map_alloc();
+        DCHECK(valueMap);
+        if (valueMap)
+          transfer_string_map_contents(value, valueMap);
+
+        // Execution
+        cef_function(valueMap);
+        
+        // Parameter Restoration
+        if (valueMap)
+          cef_string_map_free(valueMap);
+      }
+
+   String-to-string multi map type by reference (string_map_multi_byref):
+      C++:   std::multimap<CefString,CefString>& value
+      C API: cef_string_multimap_t value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_string_multimap_t value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        std::multimap<CefString,CefString> valueMultimap;
+        transfer_string_multimap_contents(value, valueMultimap);
+
+        // Execution
+        CefFunction(valueMultimap);
+        
+        // Parameter Restoration
+        cef_string_multimap_clear(value);
+        transfer_string_multimap_contents(valueMultimap, value);
+      }
+
+      // CToCpp Example
+      void CefFunction(std::multimap<CefString,CefString>& value)
+      {
+        // Parameter Translation
+        cef_string_multimap_t valueMultimap = cef_string_multimap_alloc();
+        DCHECK(valueMultimap);
+        if (valueMultimap)
+          transfer_string_multimap_contents(value, valueMultimap);
+
+        // Execution
+        cef_function(valueMultimap);
+        
+        // Parameter Restoration
+        if (valueMultimap) {
+          value.clear();
+          transfer_string_multimap_contents(valueMultimap, value);
+          cef_string_multimap_free(valueMultimap);
+        }
+      }
+   
+   String-to-string multi map const type by reference
+   (string_map_multi_byref_const):
+      C++:   const std::multimap<CefString,CefString>& value
+      C API: cef_string_multimap_t value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(cef_string_multimap_t value)
+      {
+        // Parameter Verification
+        DHECK(value);
+        if (!value)
+          return;
+
+        // Parameter Translation
+        std::multimap<CefString,CefString> valueMultimap;
+        transfer_string_multimap_contents(value, valueMultimap);
+
+        // Execution
+        CefFunction(valueMultimap);
+      }
+
+      // CToCpp Example
+      void CefFunction(const std::multimap<CefString,CefString>& value)
+      {
+        // Parameter Translation
+        cef_string_multimap_t valueMultimap = cef_string_multimap_alloc();
+        DCHECK(valueMultimap);
+        if (valueMultimap)
+          transfer_string_multimap_contents(value, valueMultimap);
+
+        // Execution
+        cef_function(valueMultimap);
+        
+        // Parameter Restoration
+        if (valueMultimap)
+          cef_string_multimap_free(valueMultimap);
+      }
+   
+   Simple/Enumeration vector non-const type by reference (simple_vec_byref):
+      C++:   std::vector<int>& value
+      C API: size_t* valueCount, int* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t* valueCount, int* value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount && (*valueCount == 0 || value));
+        if (!valueCount || (*valueCount > 0 && !value))
+          return;
+        
+        // Parameter Translation
+        std::vector<int> valueList;
+        if (valueCount && *valueCount > 0 && value) {
+          for (size_t i = 0; i < *valueCount; ++i)
+            valueList.push_back(value[i]);
+        }
+        
+        // Execution
+        CefFunction(valueList);
+        
+        // Parameter Restoration
+        if (valueCount && value) {
+          *valueCount = std::min(valueList.size(), *valueCount);
+          if (*valueCount > 0) {
+            for (size_t i = 0; i < *valueCount; ++i)
+              value[i] = valueList[i];
+          }
+        }
+      }
+
+      // CToCpp Example
+      void CefFunction(std::vector<int>& value)
+      {
+        // Parameter Translation
+        // Function identified by the "count_func" method attribute.
+        size_t valueSize = value.size();
+        size_t valueCount = std::max(GetFunctionCount(), valueSize);
+        int* valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new int[valueCount];
+          DCHECK(valueList);
+          if (valueList)
+            memset(valueList, 0, sizeof(int)*valueCount);
+          if (valueList && valueSize > 0) {
+            for (size_t i = 0; i < valueSize; ++i) {
+              valueList[i] = value[i];
+            }
+          }
+        }
+
+        // Execution
+        cef_function(&valueCount, valueList);
+        
+        // Parameter Restoration
+        value.clear();
+        if (valueCount > 0 && valueList) {
+          for (size_t i = 0; i < valueCount; ++i)
+            value.push_back(valueList[i]);
+          delete [] valueList;
+        }
+      }
+
+   Simple/Enumeration vector const type by reference (simple_vec_byref_const):
+      C++:   const std::vector<int>& value
+      C API: size_t valueCount, int const* value
+
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t valueCount, int const* value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount == 0 || value);
+        if (valueCount > 0 && !value)
+          return;
+        
+        // Parameter Translation
+        std::vector<int> valueList;
+        if (valueCount > 0) {
+          for (size_t i = 0; i < valueCount; ++i)
+            valueList.push_back(value[i]);
+        }
+
+        // Execution
+        CefFunction(valueList);
+      }
+
+      // CToCpp Example
+      void CefFunction(const std::vector<int>& value)
+      {
+        // Parameter Translation
+        const size_t valueCount = value.size();
+        int* valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new int[valueCount];
+          DCHECK(valueList);
+          if (valueList) {
+            for (size_t i = 0; i < valueCount; ++i)
+              valueList[i] = value[i];
+          }
+        }
+        
+        // Execution
+        cef_function(valueCount, valueList);
+
+        // Parameter Restoration
+        if (valueList)
+          delete [] valueList;
+      }
+
+   Boolean vector non-const type by reference (bool_vec_byref):
+      C++:   std::vector<bool>& value
+      C API: size_t* valueCount, int* value
+      
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t* valueCount, int* value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount && (*valueCount == 0 || value));
+        if (!valueCount || (*valueCount > 0 && !value))
+          return;
+        
+        // Parameter Translation
+        std::vector<bool> valueList;
+        if (valueCount && *valueCount > 0 && value) {
+          for (size_t i = 0; i < *valueCount; ++i)
+            valueList.push_back(value[i]?true:false);
+        }
+        
+        // Execution
+        CefFunction(valueList);
+        
+        // Parameter Restoration
+        if (valueCount && value) {
+          *valueCount = std::min(valueList.size(), *valueCount);
+          if (*valueCount > 0) {
+            for (size_t i = 0; i < *valueCount; ++i)
+              value[i] = valueList[i];
+          }
+        }
+      }
+
+      // CToCpp Example
+      void CefFunction(std::vector<bool>& value)
+      {
+        // Parameter Translation
+        // Function identified by the "count_func" method attribute.
+        size_t valueSize = value.size();
+        size_t valueCount = std::max(GetFunctionCount(), valueSize);
+        int* valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new int[valueCount];
+          DCHECK(valueList);
+          if (valueList)
+            memset(valueList, 0, sizeof(int)*valueCount);
+          if (valueList && valueSize > 0) {
+            for (size_t i = 0; i < valueSize; ++i) {
+              valueList[i] = value[i];
+            }
+          }
+        }
+
+        // Execution
+        cef_function(&valueCount, valueList);
+        
+        // Parameter Restoration
+        value.clear();
+        if (valueCount > 0 && valueList) {
+          for (size_t i = 0; i < valueCount; ++i)
+            value.push_back(valueList[i]?true:false);
+          delete [] valueList;
+        }
+      }
+
+   Boolean vector const type by reference (bool_vec_byref_const):
+      C++:   const std::vector<bool>& value
+      C API: size_t valueCount, int const* value
+      
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t valueCount, int const* value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount == 0 || value);
+        if (valueCount > 0 && !value)
+          return;
+        
+        // Parameter Translation
+        std::vector<bool> valueList;
+        if (valueCount > 0) {
+          for (size_t i = 0; i < valueCount; ++i)
+            valueList.push_back(value[i]?true:false);
+        }
+
+        // Execution
+        CefFunction(valueList);
+      }
+
+      // CToCpp Example
+      void CefFunction(const std::vector<bool>& value)
+      {
+        // Parameter Translation
+        const size_t valueCount = value.size();
+        int* valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new int[valueCount];
+          DCHECK(valueList)
+          if (valueList) {
+            for (size_t i = 0; i < valueCount; ++i)
+              valueList[i] = value[i];
+          }
+        }
+        
+        // Execution
+        cef_function(valueCount, valueList);
+
+        // Parameter Restoration
+        if (valueList)
+          delete [] valueList;
+      }
+
+   Smart pointer vector non-const type same boundary side by reference
+      (refptr_vec_same_byref):
+      C++:   std::vector<CefRefPtr<CefPostDataElement>>& value
+      C API: size_t* valueCount, cef_post_data_element_t** value
+      
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t* valueCount,
+                                   cef_post_data_element_t** value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount && (*valueCount == 0 || value));
+        if (!valueCount || (*valueCount > 0 && !value))
+          return;
+        
+        // Parameter Translation
+        std::vector<CefRefPtr<CefPostDataElement>> valueList;
+        if (valueCount && *valueCount > 0 && value) {
+          for (size_t i = 0; i < *valueCount; ++i)
+            valueList.push_back(CefPostDataElementCppToC::Unwrap(value[i]));
+        }
+        
+        // Execution
+        CefFunction(valueList);
+        
+        // Parameter Restoration
+        if (valueCount && value) {
+          *valueCount = std::min(valueList.size(), *valueCount);
+          if (*valueCount > 0) {
+            for (size_t i = 0; i < *valueCount; ++i)
+              value[i] = CefPostDataElementCppToC::Wrap(valueList[i]);
+          }
+        }
+      }
+
+      // CToCpp Example
+      void CefFunction(std::vector<bool>& value)
+      {
+        // Parameter Translation
+        // Function identified by the "count_func" method attribute.
+        size_t valueSize = value.size();
+        size_t valueCount = std::max(GetFunctionCount(), valueSize);
+        cef_post_data_element_t** valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new cef_post_data_element_t*[valueCount];
+          DCHECK(valueList);
+          if (valueList)
+            memset(valueList, 0, sizeof(cef_post_data_element_t*)*valueCount);
+          if (valueList && valueSize > 0) {
+            for (size_t i = 0; i < valueSize; ++i) {
+              valueList[i] = CefPostDataElementCToCpp::Unwrap(value[i]);
+            }
+          }
+        }
+
+        // Execution
+        cef_function(&valueCount, valueList);
+        
+        // Parameter Restoration
+        value.clear();
+        if (valueCount > 0 && valueList) {
+          for (size_t i = 0; i < valueCount; ++i)
+            value.push_back(CefPostDataElementCToCpp::Wrap(valueList[i]));
+          delete [] valueList;
+        }
+      }
+
+   Smart pointer vector const type same boundary side by reference
+      (refptr_vec_same_byref_const):
+      C++:   const std::vector<CefRefPtr<CefV8Value>>& value
+      C API: size_t valueCount, const cef_v8value_t** value
+      
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t valueCount,
+                                   const cef_v8value_t** value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount == 0 || value);
+        if (valueCount > 0 && !value)
+          return;
+        
+        // Parameter Translation
+        std::vector<CefRefPtr<CefV8Value>> valueList;
+        if (valueCount > 0) {
+          for (size_t i = 0; i < valueCount; ++i)
+            valueList.push_back(CefV8ValueCppToC::Unwrap(value[i]));
+        }
+
+        // Execution
+        CefFunction(valueList);
+      }
+
+      // CToCpp Example
+      void CefFunction(const std::vector<bool>& value)
+      {
+        // Parameter Translation
+        const size_t valueCount = value.size();
+        cef_v8value_t** valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new int[valueCount];
+          DCHECK(valueList);
+          if (valueList) {
+            for (size_t i = 0; i < valueCount; ++i)
+              valueList[i] = CefV8ValueCToCpp::Unwrap(value[i]);
+          }
+        }
+        
+        // Execution
+        cef_function(valueCount, valueList);
+
+        // Parameter Restoration
+        if (valueList)
+          delete [] valueList;
+      }
+
+   Smart pointer vector non-const type different boundary side by reference
+      (refptr_vec_diff_byref):
+      C++:   std::vector<CefRefPtr<CefPostDataElement>>& value
+      C API: size_t* valueCount, cef_post_data_element_t** value
+      
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t* valueCount,
+                                   cef_post_data_element_t** value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount && (*valueCount == 0 || value));
+        if (!valueCount || (*valueCount > 0 && !value))
+          return;
+        
+        // Parameter Translation
+        std::vector<CefRefPtr<CefPostDataElement>> valueList;
+        if (valueCount && *valueCount > 0 && value) {
+          for (size_t i = 0; i < *valueCount; ++i)
+            valueList.push_back(CefPostDataElementCToCpp::Wrap(value[i]));
+        }
+        
+        // Execution
+        CefFunction(valueList);
+        
+        // Parameter Restoration
+        if (valueCount && value) {
+          *valueCount = std::min(valueList.size(), *valueCount);
+          if (*valueCount > 0) {
+            for (size_t i = 0; i < *valueCount; ++i)
+              value[i] = CefPostDataElementCToCpp::Unwrap(valueList[i]);
+          }
+        }
+      }
+
+      // CToCpp Example
+      void CefFunction(std::vector<bool>& value)
+      {
+        // Parameter Translation
+        // Function identified by the "count_func" method attribute.
+        size_t valueSize = value.size();
+        size_t valueCount = std::max(GetFunctionCount(), valueSize);
+        cef_post_data_element_t** valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new cef_post_data_element_t*[valueCount];
+          DCHECK(valueList);
+          if (valueList)
+            memset(valueList, 0, sizeof(cef_post_data_element_t*)*valueCount);
+          if (valueList && valueSize > 0) {
+            for (size_t i = 0; i < valueSize; ++i) {
+              valueList[i] = CefPostDataElementCppToC::Wrap(value[i]);
+            }
+          }
+        }
+
+        // Execution
+        cef_function(&valueCount, valueList);
+        
+        // Parameter Restoration
+        value.clear();
+        if (valueCount > 0 && valueList) {
+          for (size_t i = 0; i < valueCount; ++i)
+            value.push_back(CefPostDataElementCppToC::Unwrap(valueList[i]));
+          delete [] valueList;
+        }
+      }
+
+   Smart pointer vector const type different boundary side by reference
+      (refptr_vec_diff_byref_const):
+      C++:   const std::vector<CefRefPtr<CefV8Value>>& value
+      C API: size_t valueCount, const cef_v8value_t** value
+      
+      // CppToC Example
+      CEF_EXPORT void cef_function(size_t valueCount,
+                                   const cef_v8value_t** value)
+      {
+        // Parameter Verification
+        DCHECK(valueCount == 0 || value);
+        if (valueCount > 0 && !value)
+          return;
+        
+        // Parameter Translation
+        std::vector<CefRefPtr<CefV8Value>> valueList;
+        if (valueCount > 0) {
+          for (size_t i = 0; i < valueCount; ++i)
+            valueList.push_back(CefV8ValueCToCpp::Wrap(value[i]));
+        }
+
+        // Execution
+        CefFunction(valueList);
+      }
+
+      // CToCpp Example
+      void CefFunction(const std::vector<bool>& value)
+      {
+        // Parameter Translation
+        const size_t valueCount = value.size();
+        cef_v8value_t** valueList = NULL;
+        if (valueCount > 0) {
+          valueList = new int[valueCount];
+          DCHECK(valueList);
+          if (valueList) {
+            for (size_t i = 0; i < valueCount; ++i)
+              valueList[i] = CefV8ValueCppToC::Wrap(value[i]);
+          }
+        }
+        
+        // Execution
+        cef_function(valueCount, valueList);
+
+        // Parameter Restoration
+        if (valueList)
+          delete [] valueList;
+      }
+
+Return Values:
+
+   Simple/Enumeration type (simple):
+      C++:   int
+      C API: int
+
+      // CppToC Example
+      CEF_EXPORT int cef_function()
+      {
+        // Execution
+        int _rv = CefFunction();
+        
+        // Return Translation
+        return _rv;
+      }
+
+      // CToCpp Example
+      int CefFunction()
+      {
+        // Execution
+        int _rv = cef_function();
+        
+        // Return Translation
+        return _rv;
+      }
+
+   Boolean type (bool):
+      C++:   bool
+      C API: int
+
+      // CppToC Example
+      CEF_EXPORT int cef_function()
+      {
+        // Execution
+        bool _rv = CefFunction();
+        
+        // Return Translation
+        return _rv;
+      }
+
+      // CToCpp Example
+      bool CefFunction()
+      {
+        // Execution
+        int _rv = cef_function();
+        
+        // Return Translation
+        return _rv?true:false;
+      }
+
+   String non-const by reference type (string):
+      C++:   CefString
+      C API: cef_string_userfree_t
+
+      // CppToC Example
+      CEF_EXPORT cef_string_userfree_t cef_function()
+      {
+        // Execution
+        CefString _rv = CefFunction();
+        
+        // Return Translation
+        return _rv.DetachToUserFree();
+      }
+
+      // CToCpp Example
+      CefString CefFunction()
+      {
+        // Execution
+        cef_string_userfree_t _rv = cef_function();
+        
+        // Return Translation
+        CefString _rvStr;
+        _rvStr.AttachToUserFree(_rv);
+        return _rvStr;
+      }
+
+   Smart pointer type same boundary side (refptr_same):
+      C++:   CefRefPtr<CefBrowser>
+      C API: cef_browser_t*
+
+      // CppToC Example
+      CEF_EXPORT cef_browser_t* cef_function()
+      {
+        // Execution
+        CefRefPtr<CefBrowser> _rv = CefFunction();
+        
+        // Return Translation
+        return CefBrowserCppToC::Wrap(_rv);
+      }
+
+      // CToCpp Example
+      CefString CefFunction()
+      {
+        // Execution
+        cef_browser_t* _rv = cef_function();
+        
+        // Return Translation
+        return CefBrowserCToCpp::Wrap(_rv);
+      }
+
+   Smart pointer type different boundary side (refptr_diff):
+      C++:   CefRefPtr<CefBrowser>
+      C API: cef_browser_t*
+
+      // CppToC Example
+      CEF_EXPORT cef_browser_t* cef_function()
+      {
+        // Execution
+        CefRefPtr<CefBrowser> _rv = CefFunction();
+        
+        // Return Translation
+        return CefBrowserCToCpp::Unwrap(_rv);
+      }
+
+      // CToCpp Example
+      CefString CefFunction()
+      {
+        // Execution
+        cef_browser_t* _rv = cef_function();
+        
+        // Return Translation
+        return CefBrowserCppToC::Unwrap(_rv);
+      }
+
+
+Translating Comments
+--------------------
+
+Comments from the CEF header file are reproduced in the C API header file with
+any referenced C++ types and terminology changed to reflect C API types and
+terminology.
+
+C++:
+// Create a new CefV8Value object of the specified type.  These methods
+// should only be called from within the JavaScript context -- either in a
+// CefV8Handler::Execute() callback or a CefHandler::HandleJSBinding()
+// callback.
+
+C API:
+// Create a new cef_v8value_t object of the specified type.  These functions
+// should only be called from within the JavaScript context -- either in a
+// cef_v8handler_t::execute() callback or a cef_handler_t::handle_jsbinding()
+// callback.
+
+Situations where the user is responsible for freeing strings allocated and
+returned by the library are also noted by comments in the C API header file.
+
+C API:
+   // The resulting string must be freed by calling cef_string_free().
+
+A comment must occur immediately before the function, class or method that it
+documents with no extra space in between.  Comments may span multiple lines
+but each line must start with the '//' comment identifier.
+
+C++:
+   // Set focus for the browser window.  If |enable| is true focus will be set
+   // to the window.  Otherwise, focus will be removed.
+   /*--cef()--*/
+   virtual void SetFocus(bool enable) =0;
+
+If two comments are separated by an empty line it will be assumed that the
+higher comment represents a section header and additional space will be added
+before it in the translated output.
+
+C++:
+   // ARRAY METHODS - These methods are only available on arrays.
+   
+   // Returns the number of elements in the array.
+   /*--cef()--*/
+   virtual int GetArrayLength() =0;
+
+Empty lines and lines with the comment identifier but no content are considered
+paragraph breaks for the purposes of wrapping the translated text. Any content
+indented more than one space is reproduced as-is without content translation
+or wrapping.
+
+C++:
+// Register a new V8 extension with the specified JavaScript extension code and
+// handler. Functions implemented by the handler are prototyped using the
+// keyword 'native'. The calling of a native function is restricted to the scope
+// in which the prototype of the native function is defined.
+//
+// Example JavaScript extension code:
+//
+//   // create the 'example' global object if it doesn't already exist.
+//   if (!example)
+//     example = {};
diff --git a/src/tools/translator.bat b/src/tools/translator.bat
new file mode 100644
index 0000000..348700a
--- /dev/null
+++ b/src/tools/translator.bat
@@ -0,0 +1,3 @@
+@echo off

+call python.bat %~dp0\translator.py --root-dir %~dp0\.. %*

+pause
\ No newline at end of file
diff --git a/src/tools/translator.py b/src/tools/translator.py
new file mode 100644
index 0000000..a2bfd89
--- /dev/null
+++ b/src/tools/translator.py
@@ -0,0 +1,249 @@
+# Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+import sys
+from cef_parser import *
+from clang_util import clang_format
+from file_util import *
+import hashlib
+from make_api_hash_header import *
+from make_capi_header import *
+from make_cpptoc_header import *
+from make_cpptoc_impl import *
+from make_ctocpp_header import *
+from make_ctocpp_impl import *
+from make_gypi_file import *
+from make_libcef_dll_dylib_impl import *
+from make_views_stub_impl import *
+from make_wrapper_types_header import *
+from optparse import OptionParser
+
+# cannot be loaded as a module
+if __name__ != "__main__":
+  sys.stderr.write('This file cannot be loaded as a module!')
+  sys.exit()
+
+# parse command-line options
+disc = """
+This utility generates files for the CEF C++ to C API translation layer.
+"""
+
+parser = OptionParser(description=disc)
+parser.add_option(
+    '--root-dir',
+    dest='rootdir',
+    metavar='DIR',
+    help='CEF root directory [required]')
+parser.add_option(
+    '--backup',
+    action='store_true',
+    dest='backup',
+    default=False,
+    help='create a backup of modified files')
+parser.add_option(
+    '--force',
+    action='store_true',
+    dest='force',
+    default=False,
+    help='force rewrite of the file')
+parser.add_option(
+    '-c',
+    '--classes',
+    dest='classes',
+    action='append',
+    help='only translate the specified classes')
+parser.add_option(
+    '-q',
+    '--quiet',
+    action='store_true',
+    dest='quiet',
+    default=False,
+    help='do not output detailed status information')
+(options, args) = parser.parse_args()
+
+# the rootdir option is required
+if options.rootdir is None:
+  parser.print_help(sys.stdout)
+  sys.exit()
+
+# determine the paths
+root_dir = os.path.abspath(options.rootdir)
+cpp_header_dir = os.path.join(root_dir, 'include')
+cpp_header_test_dir = os.path.join(cpp_header_dir, 'test')
+cpp_header_views_dir = os.path.join(cpp_header_dir, 'views')
+capi_header_dir = os.path.join(cpp_header_dir, 'capi')
+api_hash_header = os.path.join(cpp_header_dir, 'cef_api_hash.h')
+libcef_dll_dir = os.path.join(root_dir, 'libcef_dll')
+cpptoc_global_impl = os.path.join(libcef_dll_dir, 'libcef_dll.cc')
+ctocpp_global_impl = os.path.join(libcef_dll_dir, 'wrapper',
+                                  'libcef_dll_wrapper.cc')
+wrapper_types_header = os.path.join(libcef_dll_dir, 'wrapper_types.h')
+cpptoc_dir = os.path.join(libcef_dll_dir, 'cpptoc')
+ctocpp_dir = os.path.join(libcef_dll_dir, 'ctocpp')
+gypi_file = os.path.join(root_dir, 'cef_paths.gypi')
+views_stub_impl = os.path.join(libcef_dll_dir, 'views_stub.cc')
+libcef_dll_dylib_impl = os.path.join(libcef_dll_dir, 'wrapper',
+                                     'libcef_dll_dylib.cc')
+
+# make sure the header directory exists
+if not path_exists(cpp_header_dir):
+  sys.stderr.write('Directory ' + cpp_header_dir + ' does not exist.')
+  sys.exit()
+
+# create the header object
+if not options.quiet:
+  sys.stdout.write('Parsing C++ headers from ' + cpp_header_dir + '...\n')
+header = obj_header()
+
+# add include files to be processed
+header.set_root_directory(cpp_header_dir)
+excluded_files = ['cef_api_hash.h', 'cef_application_mac.h', 'cef_version.h']
+header.add_directory(cpp_header_dir, excluded_files)
+header.add_directory(cpp_header_test_dir)
+header.add_directory(cpp_header_views_dir)
+
+# Track the number of files that were written.
+writect = 0
+
+
+def update_file(file, newcontents):
+  """ Replaces the contents of |file| with |newcontents| if necessary. """
+  oldcontents = ''
+  oldhash = ''
+
+  if newcontents[-1:] != "\n":
+    # Add newline at end of file.
+    newcontents += "\n"
+
+  # clang-format is slow so we don't want to apply it if the pre-formatted
+  # content hasn't changed. To check for changes we embed a hash of the pre-
+  # formatted content in the resulting file.
+  hash_start = "$hash="
+  hash_end = "$"
+  hash_token = "$$HASH$$"
+
+  if not options.force and path_exists(file):
+    oldcontents = read_file(file)
+
+    # Extract the existing hash.
+    start = oldcontents.find(hash_start)
+    if start > 0:
+      end = oldcontents.find(hash_end, start + len(hash_start))
+      if end > 0:
+        oldhash = oldcontents[start + len(hash_start):end]
+
+  # Compute the new hash.
+  newhash = hashlib.sha1(newcontents.encode('utf-8')).hexdigest()
+
+  if oldhash == newhash:
+    # Pre-formatted contents have not changed.
+    return
+
+  newcontents = newcontents.replace(hash_token, newhash, 1)
+
+  # Apply clang-format for C/C++ files.
+  if os.path.splitext(file)[1][1:] in ('c', 'cc', 'cpp', 'h'):
+    result = clang_format(file, newcontents)
+    if result != None:
+      newcontents = result
+    else:
+      raise Exception("Call to clang-format failed")
+
+  if options.backup and oldcontents != '':
+    backup_file(file)
+
+  filedir = os.path.split(file)[0]
+  if not os.path.isdir(filedir):
+    make_dir(filedir)
+
+  write_file(file, newcontents)
+
+  global writect
+  writect += 1
+
+
+# output the C API header
+if not options.quiet:
+  sys.stdout.write('In C API header directory ' + capi_header_dir + '...\n')
+filenames = sorted(header.get_file_names())
+for filename in filenames:
+  if not options.quiet:
+    sys.stdout.write('Generating ' + filename + ' C API header...\n')
+  update_file(*write_capi_header(header, capi_header_dir, filename))
+
+# output the wrapper types header
+if not options.quiet:
+  sys.stdout.write('Generating wrapper types header...\n')
+update_file(*write_wrapper_types_header(header, wrapper_types_header))
+
+# build the list of classes to parse
+allclasses = header.get_class_names()
+if not options.classes is None:
+  for cls in options.classes:
+    if not cls in allclasses:
+      sys.stderr.write('ERROR: Unknown class: ' + cls)
+      sys.exit()
+  classes = options.classes
+else:
+  classes = allclasses
+
+classes = sorted(classes)
+
+# output CppToC global file
+if not options.quiet:
+  sys.stdout.write('Generating CppToC global implementation...\n')
+update_file(*write_cpptoc_impl(header, None, cpptoc_global_impl))
+
+# output CToCpp global file
+if not options.quiet:
+  sys.stdout.write('Generating CToCpp global implementation...\n')
+update_file(*write_ctocpp_impl(header, None, ctocpp_global_impl))
+
+# output CppToC class files
+if not options.quiet:
+  sys.stdout.write('In CppToC directory ' + cpptoc_dir + '...\n')
+for cls in classes:
+  if not options.quiet:
+    sys.stdout.write('Generating ' + cls + 'CppToC class header...\n')
+  update_file(*write_cpptoc_header(header, cls, cpptoc_dir))
+  if not options.quiet:
+    sys.stdout.write('Generating ' + cls + 'CppToC class implementation...\n')
+  update_file(*write_cpptoc_impl(header, cls, cpptoc_dir))
+
+# output CppToC class files
+if not options.quiet:
+  sys.stdout.write('In CToCpp directory ' + ctocpp_dir + '...\n')
+for cls in classes:
+  if not options.quiet:
+    sys.stdout.write('Generating ' + cls + 'CToCpp class header...\n')
+  update_file(*write_ctocpp_header(header, cls, ctocpp_dir))
+  if not options.quiet:
+    sys.stdout.write('Generating ' + cls + 'CToCpp class implementation...\n')
+  update_file(*write_ctocpp_impl(header, cls, ctocpp_dir))
+
+# output the gypi file
+if not options.quiet:
+  sys.stdout.write('Generating ' + gypi_file + ' file...\n')
+update_file(*write_gypi_file(header, gypi_file))
+
+# output the views stub file
+if not options.quiet:
+  sys.stdout.write('Generating ' + views_stub_impl + ' file...\n')
+update_file(*write_views_stub_impl(header, views_stub_impl))
+
+# output the libcef dll dylib file
+if not options.quiet:
+  sys.stdout.write('Generating ' + libcef_dll_dylib_impl + ' file...\n')
+update_file(*write_libcef_dll_dylib_impl(header, libcef_dll_dylib_impl))
+
+# Update the API hash header file if necessary. This must be done last because
+# it reads files that were potentially written by proceeding operations.
+if not options.quiet:
+  sys.stdout.write('Generating API hash header...\n')
+if write_api_hash_header(api_hash_header, cpp_header_dir):
+  writect += 1
+
+if not options.quiet:
+  sys.stdout.write('Done - Wrote ' + str(writect) + ' files.\n')
diff --git a/src/tools/translator.sh b/src/tools/translator.sh
new file mode 100755
index 0000000..9304a9f
--- /dev/null
+++ b/src/tools/translator.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python translator.py --root-dir .. $@
diff --git a/src/tools/yapf/LICENSE b/src/tools/yapf/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/src/tools/yapf/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/src/tools/yapf/README.cef b/src/tools/yapf/README.cef
new file mode 100644
index 0000000..44e7ced
--- /dev/null
+++ b/src/tools/yapf/README.cef
@@ -0,0 +1,14 @@
+Name: yapf
+Short Name: yapf
+URL: https://github.com/google/yapf
+Date: 28 May 2017
+Version: 0.16.2
+Revision: 9f168a12
+License: Apache 2.0
+License File: LICENSE
+
+Description:
+A formatter for Python files.
+
+Local Modifications:
+None
diff --git a/src/tools/yapf/__main__.py b/src/tools/yapf/__main__.py
new file mode 100644
index 0000000..88f1ec6
--- /dev/null
+++ b/src/tools/yapf/__main__.py
@@ -0,0 +1,16 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import yapf
+
+yapf.run_main()
diff --git a/src/tools/yapf/yapf/__init__.py b/src/tools/yapf/yapf/__init__.py
new file mode 100644
index 0000000..9258052
--- /dev/null
+++ b/src/tools/yapf/yapf/__init__.py
@@ -0,0 +1,303 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""YAPF.
+
+YAPF uses the algorithm in clang-format to figure out the "best" formatting for
+Python code. It looks at the program as a series of "unwrappable lines" ---
+i.e., lines which, if there were no column limit, we would place all tokens on
+that line. It then uses a priority queue to figure out what the best formatting
+is --- i.e., the formatting with the least penalty.
+
+It differs from tools like autopep8 and pep8ify in that it doesn't just look for
+violations of the style guide, but looks at the module as a whole, making
+formatting decisions based on what's the best format for each line.
+
+If no filenames are specified, YAPF reads the code from stdin.
+"""
+from __future__ import print_function
+
+import argparse
+import logging
+import os
+import sys
+
+from yapf.yapflib import errors
+from yapf.yapflib import file_resources
+from yapf.yapflib import py3compat
+from yapf.yapflib import style
+from yapf.yapflib import yapf_api
+
+__version__ = '0.16.2'
+
+
+def main(argv):
+  """Main program.
+
+  Arguments:
+    argv: command-line arguments, such as sys.argv (including the program name
+      in argv[0]).
+
+  Returns:
+    0 if there were no changes, non-zero otherwise.
+
+  Raises:
+    YapfError: if none of the supplied files were Python files.
+  """
+  parser = argparse.ArgumentParser(description='Formatter for Python code.')
+  parser.add_argument(
+      '-v',
+      '--version',
+      action='store_true',
+      help='show version number and exit')
+
+  diff_inplace_group = parser.add_mutually_exclusive_group()
+  diff_inplace_group.add_argument(
+      '-d',
+      '--diff',
+      action='store_true',
+      help='print the diff for the fixed source')
+  diff_inplace_group.add_argument(
+      '-i',
+      '--in-place',
+      action='store_true',
+      help='make changes to files in place')
+
+  lines_recursive_group = parser.add_mutually_exclusive_group()
+  lines_recursive_group.add_argument(
+      '-r',
+      '--recursive',
+      action='store_true',
+      help='run recursively over directories')
+  lines_recursive_group.add_argument(
+      '-l',
+      '--lines',
+      metavar='START-END',
+      action='append',
+      default=None,
+      help='range of lines to reformat, one-based')
+
+  parser.add_argument(
+      '-e',
+      '--exclude',
+      metavar='PATTERN',
+      action='append',
+      default=None,
+      help='patterns for files to exclude from formatting')
+  parser.add_argument(
+      '--style',
+      action='store',
+      help=('specify formatting style: either a style name (for example "pep8" '
+            'or "google"), or the name of a file with style settings. The '
+            'default is pep8 unless a %s or %s file located in one of the '
+            'parent directories of the source file (or current directory for '
+            'stdin)' % (style.LOCAL_STYLE, style.SETUP_CONFIG)))
+  parser.add_argument(
+      '--style-help',
+      action='store_true',
+      help=('show style settings and exit; this output can be '
+            'saved to .style.yapf to make your settings '
+            'permanent'))
+  parser.add_argument(
+      '--no-local-style',
+      action='store_true',
+      help="don't search for local style definition")
+  parser.add_argument('--verify', action='store_true', help=argparse.SUPPRESS)
+  parser.add_argument(
+      '-p',
+      '--parallel',
+      action='store_true',
+      help=('Run yapf in parallel when formatting multiple files. Requires '
+            'concurrent.futures in Python 2.X'))
+
+  parser.add_argument('files', nargs='*')
+  args = parser.parse_args(argv[1:])
+
+  if args.version:
+    print('yapf {}'.format(__version__))
+    return 0
+
+  if args.style_help:
+    style.SetGlobalStyle(style.CreateStyleFromConfig(args.style))
+    print('[style]')
+    for option, docstring in sorted(style.Help().items()):
+      for line in docstring.splitlines():
+        print('#', line and ' ' or '', line, sep='')
+      print(option.lower(), '=', style.Get(option), sep='')
+      print()
+    return 0
+
+  if args.lines and len(args.files) > 1:
+    parser.error('cannot use -l/--lines with more than one file')
+
+  lines = _GetLines(args.lines) if args.lines is not None else None
+  if not args.files:
+    # No arguments specified. Read code from stdin.
+    if args.in_place or args.diff:
+      parser.error('cannot use --in-place or --diff flags when reading '
+                   'from stdin')
+
+    original_source = []
+    while True:
+      try:
+        # Use 'raw_input' instead of 'sys.stdin.read', because otherwise the
+        # user will need to hit 'Ctrl-D' more than once if they're inputting
+        # the program by hand. 'raw_input' throws an EOFError exception if
+        # 'Ctrl-D' is pressed, which makes it easy to bail out of this loop.
+        original_source.append(py3compat.raw_input())
+      except EOFError:
+        break
+
+    style_config = args.style
+    if style_config is None and not args.no_local_style:
+      style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
+
+    source = [line.rstrip() for line in original_source]
+    reformatted_source, _ = yapf_api.FormatCode(
+        py3compat.unicode('\n'.join(source) + '\n'),
+        filename='<stdin>',
+        style_config=style_config,
+        lines=lines,
+        verify=args.verify)
+    file_resources.WriteReformattedCode('<stdout>', reformatted_source)
+    return 0
+
+  files = file_resources.GetCommandLineFiles(args.files, args.recursive,
+                                             args.exclude)
+  if not files:
+    raise errors.YapfError('Input filenames did not match any python files')
+
+  FormatFiles(
+      files,
+      lines,
+      style_config=args.style,
+      no_local_style=args.no_local_style,
+      in_place=args.in_place,
+      print_diff=args.diff,
+      verify=args.verify,
+      parallel=args.parallel)
+  return 0
+
+
+def FormatFiles(filenames,
+                lines,
+                style_config=None,
+                no_local_style=False,
+                in_place=False,
+                print_diff=False,
+                verify=True,
+                parallel=False):
+  """Format a list of files.
+
+  Arguments:
+    filenames: (list of unicode) A list of files to reformat.
+    lines: (list of tuples of integers) A list of tuples of lines, [start, end],
+      that we want to format. The lines are 1-based indexed. This argument
+      overrides the 'args.lines'. It can be used by third-party code (e.g.,
+      IDEs) when reformatting a snippet of code.
+    style_config: (string) Style name or file path.
+    no_local_style: (string) If style_config is None don't search for
+      directory-local style configuration.
+    in_place: (bool) Modify the files in place.
+    print_diff: (bool) Instead of returning the reformatted source, return a
+      diff that turns the formatted source into reformatter source.
+    verify: (bool) True if reformatted code should be verified for syntax.
+    parallel: (bool) True if should format multiple files in parallel.
+
+  Returns:
+    True if the source code changed in any of the files being formatted.
+  """
+  changed = False
+  if parallel:
+    import multiprocessing  # pylint: disable=g-import-not-at-top
+    import concurrent.futures  # pylint: disable=g-import-not-at-top
+    workers = min(multiprocessing.cpu_count(), len(filenames))
+    with concurrent.futures.ProcessPoolExecutor(workers) as executor:
+      future_formats = [
+          executor.submit(_FormatFile, filename, lines, style_config,
+                          no_local_style, in_place, print_diff, verify)
+          for filename in filenames
+      ]
+      for future in concurrent.futures.as_completed(future_formats):
+        changed |= future.result()
+  else:
+    for filename in filenames:
+      changed |= _FormatFile(filename, lines, style_config, no_local_style,
+                             in_place, print_diff, verify)
+  return changed
+
+
+def _FormatFile(filename,
+                lines,
+                style_config=None,
+                no_local_style=False,
+                in_place=False,
+                print_diff=False,
+                verify=True):
+  logging.info('Reformatting %s', filename)
+  if style_config is None and not no_local_style:
+    style_config = (
+        file_resources.GetDefaultStyleForDir(os.path.dirname(filename)))
+  try:
+    reformatted_code, encoding, has_change = yapf_api.FormatFile(
+        filename,
+        in_place=in_place,
+        style_config=style_config,
+        lines=lines,
+        print_diff=print_diff,
+        verify=verify,
+        logger=logging.warning)
+    if not in_place and reformatted_code:
+      file_resources.WriteReformattedCode(filename, reformatted_code, in_place,
+                                          encoding)
+    return has_change
+  except SyntaxError as e:
+    e.filename = filename
+    raise
+
+
+def _GetLines(line_strings):
+  """Parses the start and end lines from a line string like 'start-end'.
+
+  Arguments:
+    line_strings: (array of string) A list of strings representing a line
+      range like 'start-end'.
+
+  Returns:
+    A list of tuples of the start and end line numbers.
+
+  Raises:
+    ValueError: If the line string failed to parse or was an invalid line range.
+  """
+  lines = []
+  for line_string in line_strings:
+    # The 'list' here is needed by Python 3.
+    line = list(map(int, line_string.split('-', 1)))
+    if line[0] < 1:
+      raise errors.YapfError('invalid start of line range: %r' % line)
+    if line[0] > line[1]:
+      raise errors.YapfError('end comes before start in line range: %r', line)
+    lines.append(tuple(line))
+  return lines
+
+
+def run_main():  # pylint: disable=invalid-name
+  try:
+    sys.exit(main(sys.argv))
+  except errors.YapfError as e:
+    sys.stderr.write('yapf: ' + str(e) + '\n')
+    sys.exit(1)
+
+
+if __name__ == '__main__':
+  run_main()
diff --git a/src/tools/yapf/yapf/yapflib/__init__.py b/src/tools/yapf/yapf/yapflib/__init__.py
new file mode 100644
index 0000000..80217ac
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/src/tools/yapf/yapf/yapflib/blank_line_calculator.py b/src/tools/yapf/yapf/yapflib/blank_line_calculator.py
new file mode 100644
index 0000000..bcd7a86
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/blank_line_calculator.py
@@ -0,0 +1,183 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Calculate the number of blank lines between top-level entities.
+
+Calculates how many blank lines we need between classes, functions, and other
+entities at the same level.
+
+  CalculateBlankLines(): the main function exported by this module.
+
+Annotations:
+  newlines: The number of newlines required before the node.
+"""
+
+from lib2to3 import pytree
+
+from yapf.yapflib import py3compat
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import pytree_visitor
+
+_NO_BLANK_LINES = 1
+_ONE_BLANK_LINE = 2
+_TWO_BLANK_LINES = 3
+
+_PYTHON_STATEMENTS = frozenset({
+    'small_stmt', 'expr_stmt', 'print_stmt', 'del_stmt', 'pass_stmt',
+    'break_stmt', 'continue_stmt', 'return_stmt', 'raise_stmt', 'yield_stmt',
+    'import_stmt', 'global_stmt', 'exec_stmt', 'assert_stmt', 'if_stmt',
+    'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt', 'nonlocal_stmt',
+    'async_stmt', 'simple_stmt'
+})
+
+
+def CalculateBlankLines(tree):
+  """Run the blank line calculator visitor over the tree.
+
+  This modifies the tree in place.
+
+  Arguments:
+    tree: the top-level pytree node to annotate with subtypes.
+  """
+  blank_line_calculator = _BlankLineCalculator()
+  blank_line_calculator.Visit(tree)
+
+
+class _BlankLineCalculator(pytree_visitor.PyTreeVisitor):
+  """_BlankLineCalculator - see file-level docstring for a description."""
+
+  def __init__(self):
+    self.class_level = 0
+    self.function_level = 0
+    self.last_comment_lineno = 0
+    self.last_was_decorator = False
+    self.last_was_class_or_function = False
+
+  def Visit_simple_stmt(self, node):  # pylint: disable=invalid-name
+    self.DefaultNodeVisit(node)
+    if pytree_utils.NodeName(node.children[0]) == 'COMMENT':
+      self.last_comment_lineno = node.children[0].lineno
+
+  def Visit_decorator(self, node):  # pylint: disable=invalid-name
+    if (self.last_comment_lineno and
+        self.last_comment_lineno == node.children[0].lineno - 1):
+      self._SetNumNewlines(node.children[0], _NO_BLANK_LINES)
+    else:
+      self._SetNumNewlines(node.children[0], self._GetNumNewlines(node))
+    for child in node.children:
+      self.Visit(child)
+    self.last_was_decorator = True
+
+  def Visit_classdef(self, node):  # pylint: disable=invalid-name
+    self.last_was_class_or_function = False
+    index = self._SetBlankLinesBetweenCommentAndClassFunc(node)
+    self.last_was_decorator = False
+    self.class_level += 1
+    for child in node.children[index:]:
+      self.Visit(child)
+    self.class_level -= 1
+    self.last_was_class_or_function = True
+
+  def Visit_funcdef(self, node):  # pylint: disable=invalid-name
+    self.last_was_class_or_function = False
+    index = self._SetBlankLinesBetweenCommentAndClassFunc(node)
+    if _AsyncFunction(node):
+      index = self._SetBlankLinesBetweenCommentAndClassFunc(
+          node.prev_sibling.parent)
+      self._SetNumNewlines(node.children[0], None)
+    else:
+      index = self._SetBlankLinesBetweenCommentAndClassFunc(node)
+    self.last_was_decorator = False
+    self.function_level += 1
+    for child in node.children[index:]:
+      self.Visit(child)
+    self.function_level -= 1
+    self.last_was_class_or_function = True
+
+  def DefaultNodeVisit(self, node):
+    """Override the default visitor for Node.
+
+    This will set the blank lines required if the last entity was a class or
+    function.
+
+    Arguments:
+      node: (pytree.Node) The node to visit.
+    """
+    if self.last_was_class_or_function:
+      if pytree_utils.NodeName(node) in _PYTHON_STATEMENTS:
+        leaf = _GetFirstChildLeaf(node)
+        self._SetNumNewlines(leaf, self._GetNumNewlines(leaf))
+    self.last_was_class_or_function = False
+    super(_BlankLineCalculator, self).DefaultNodeVisit(node)
+
+  def _SetBlankLinesBetweenCommentAndClassFunc(self, node):
+    """Set the number of blanks between a comment and class or func definition.
+
+    Class and function definitions have leading comments as children of the
+    classdef and functdef nodes.
+
+    Arguments:
+      node: (pytree.Node) The classdef or funcdef node.
+
+    Returns:
+      The index of the first child past the comment nodes.
+    """
+    index = 0
+    while pytree_utils.IsCommentStatement(node.children[index]):
+      # Standalone comments are wrapped in a simple_stmt node with the comment
+      # node as its only child.
+      self.Visit(node.children[index].children[0])
+      if not self.last_was_decorator:
+        self._SetNumNewlines(node.children[index].children[0], _ONE_BLANK_LINE)
+      index += 1
+    if (index and node.children[index].lineno -
+        1 == node.children[index - 1].children[0].lineno):
+      self._SetNumNewlines(node.children[index], _NO_BLANK_LINES)
+    else:
+      if self.last_comment_lineno + 1 == node.children[index].lineno:
+        num_newlines = _NO_BLANK_LINES
+      else:
+        num_newlines = self._GetNumNewlines(node)
+      self._SetNumNewlines(node.children[index], num_newlines)
+    return index
+
+  def _GetNumNewlines(self, node):
+    if self.last_was_decorator:
+      return _NO_BLANK_LINES
+    elif self._IsTopLevel(node):
+      return _TWO_BLANK_LINES
+    return _ONE_BLANK_LINE
+
+  def _SetNumNewlines(self, node, num_newlines):
+    pytree_utils.SetNodeAnnotation(node, pytree_utils.Annotation.NEWLINES,
+                                   num_newlines)
+
+  def _IsTopLevel(self, node):
+    return (not (self.class_level or self.function_level) and
+            _StartsInZerothColumn(node))
+
+
+def _StartsInZerothColumn(node):
+  return (_GetFirstChildLeaf(node).column == 0 or
+          (_AsyncFunction(node) and node.prev_sibling.column == 0))
+
+
+def _AsyncFunction(node):
+  return (py3compat.PY3 and node.prev_sibling and
+          pytree_utils.NodeName(node.prev_sibling) == 'ASYNC')
+
+
+def _GetFirstChildLeaf(node):
+  if isinstance(node, pytree.Leaf):
+    return node
+  return _GetFirstChildLeaf(node.children[0])
diff --git a/src/tools/yapf/yapf/yapflib/comment_splicer.py b/src/tools/yapf/yapf/yapflib/comment_splicer.py
new file mode 100644
index 0000000..7c79e80
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/comment_splicer.py
@@ -0,0 +1,374 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Comment splicer for lib2to3 trees.
+
+The lib2to3 syntax tree produced by the parser holds comments and whitespace in
+prefix attributes of nodes, rather than nodes themselves. This module provides
+functionality to splice comments out of prefixes and into nodes of their own,
+making them easier to process.
+
+  SpliceComments(): the main function exported by this module.
+"""
+
+from lib2to3 import pygram
+from lib2to3 import pytree
+from lib2to3.pgen2 import token
+
+from yapf.yapflib import pytree_utils
+
+
+def SpliceComments(tree):
+  """Given a pytree, splice comments into nodes of their own right.
+
+  Extract comments from the prefixes where they are housed after parsing.
+  The prefixes that previously housed the comments become empty.
+
+  Args:
+    tree: a pytree.Node - the tree to work on. The tree is modified by this
+        function.
+  """
+  # The previous leaf node encountered in the traversal.
+  # This is a list because Python 2.x doesn't have 'nonlocal' :)
+  prev_leaf = [None]
+  _AnnotateIndents(tree)
+
+  def _VisitNodeRec(node):
+    # This loop may insert into node.children, so we'll iterate over a copy.
+    for child in node.children[:]:
+      if isinstance(child, pytree.Node):
+        # Nodes don't have prefixes.
+        _VisitNodeRec(child)
+      else:
+        if child.prefix.lstrip().startswith('#'):
+          # We have a comment prefix in this child, so splicing is needed.
+          comment_prefix = child.prefix
+          comment_lineno = child.lineno - comment_prefix.count('\n')
+          comment_column = child.column
+
+          # Remember the leading indentation of this prefix and clear it.
+          # Mopping up the prefix is important because we may go over this same
+          # child in the next iteration...
+          child_prefix = child.prefix.lstrip('\n')
+          prefix_indent = child_prefix[:child_prefix.find('#')]
+          if '\n' in prefix_indent:
+            prefix_indent = prefix_indent[prefix_indent.rfind('\n') + 1:]
+          child.prefix = ''
+
+          if child.type == token.NEWLINE:
+            # If the prefix was on a NEWLINE leaf, it's part of the line so it
+            # will be inserted after the previously encountered leaf.
+            # We can't just insert it before the NEWLINE node, because as a
+            # result of the way pytrees are organized, this node can be under
+            # an inappropriate parent.
+            comment_column -= len(comment_prefix)
+            comment_column += len(comment_prefix) - len(comment_prefix.lstrip())
+            pytree_utils.InsertNodesAfter(
+                _CreateCommentsFromPrefix(
+                    comment_prefix,
+                    comment_lineno,
+                    comment_column,
+                    standalone=False), prev_leaf[0])
+          elif child.type == token.DEDENT:
+            # Comment prefixes on DEDENT nodes also deserve special treatment,
+            # because their final placement depends on their prefix.
+            # We'll look for an ancestor of this child with a matching
+            # indentation, and insert the comment after it.
+            ancestor_at_indent = _FindAncestorAtIndent(child, prefix_indent)
+            if ancestor_at_indent.type == token.DEDENT:
+              comments = comment_prefix.split('\n')
+
+              # lib2to3 places comments that should be separated into the same
+              # DEDENT node. For example, "comment 1" and "comment 2" will be
+              # combined.
+              #
+              #   def _():
+              #     for x in y:
+              #       pass
+              #       # comment 1
+              #
+              #     # comment 2
+              #     pass
+              #
+              # In this case, we need to split them up ourselves.
+              before = []
+              after = []
+              after_lineno = comment_lineno
+
+              index = 0
+              while index < len(comments):
+                cmt = comments[index]
+                if not cmt.strip() or cmt.startswith(prefix_indent + '#'):
+                  before.append(cmt)
+                else:
+                  after_lineno += index
+                  after.extend(comments[index:])
+                  break
+                index += 1
+
+              # Special case where the comment is inserted in the same
+              # indentation level as the DEDENT it was originally attached to.
+              pytree_utils.InsertNodesBefore(
+                  _CreateCommentsFromPrefix(
+                      '\n'.join(before) + '\n',
+                      comment_lineno,
+                      comment_column,
+                      standalone=True), ancestor_at_indent)
+              if after:
+                after_column = len(after[0]) - len(after[0].lstrip())
+                comment_column -= comment_column - after_column
+                pytree_utils.InsertNodesAfter(
+                    _CreateCommentsFromPrefix(
+                        '\n'.join(after) + '\n',
+                        after_lineno,
+                        comment_column,
+                        standalone=True), _FindNextAncestor(ancestor_at_indent))
+            else:
+              pytree_utils.InsertNodesAfter(
+                  _CreateCommentsFromPrefix(
+                      comment_prefix,
+                      comment_lineno,
+                      comment_column,
+                      standalone=True), ancestor_at_indent)
+          else:
+            # Otherwise there are two cases.
+            #
+            # 1. The comment is on its own line
+            # 2. The comment is part of an expression.
+            #
+            # Unfortunately, it's fairly difficult to distinguish between the
+            # two in lib2to3 trees. The algorithm here is to determine whether
+            # child is the first leaf in the statement it belongs to. If it is,
+            # then the comment (which is a prefix) belongs on a separate line.
+            # If it is not, it means the comment is buried deep in the statement
+            # and is part of some expression.
+            stmt_parent = _FindStmtParent(child)
+
+            for leaf_in_parent in stmt_parent.leaves():
+              if leaf_in_parent.type == token.NEWLINE:
+                continue
+              elif id(leaf_in_parent) == id(child):
+                # This comment stands on its own line, and it has to be inserted
+                # into the appropriate parent. We'll have to find a suitable
+                # parent to insert into. See comments above
+                # _STANDALONE_LINE_NODES for more details.
+                node_with_line_parent = _FindNodeWithStandaloneLineParent(child)
+                pytree_utils.InsertNodesBefore(
+                    _CreateCommentsFromPrefix(
+                        comment_prefix, comment_lineno, 0, standalone=True),
+                    node_with_line_parent)
+                break
+              else:
+                if comment_lineno == prev_leaf[0].lineno:
+                  comment_lines = comment_prefix.splitlines()
+                  value = comment_lines[0].lstrip()
+                  if value.rstrip('\n'):
+                    comment_column = prev_leaf[0].column
+                    comment_column += len(prev_leaf[0].value)
+                    comment_column += (
+                        len(comment_lines[0]) - len(comment_lines[0].lstrip()))
+                    comment_leaf = pytree.Leaf(
+                        type=token.COMMENT,
+                        value=value.rstrip('\n'),
+                        context=('', (comment_lineno, comment_column)))
+                    pytree_utils.InsertNodesAfter([comment_leaf], prev_leaf[0])
+                    comment_prefix = '\n'.join(comment_lines[1:])
+                    comment_lineno += 1
+
+                rindex = (0 if '\n' not in comment_prefix.rstrip() else
+                          comment_prefix.rstrip().rindex('\n') + 1)
+                comment_column = (len(comment_prefix[rindex:]) -
+                                  len(comment_prefix[rindex:].lstrip()))
+                comments = _CreateCommentsFromPrefix(
+                    comment_prefix,
+                    comment_lineno,
+                    comment_column,
+                    standalone=False)
+                pytree_utils.InsertNodesBefore(comments, child)
+                break
+
+        prev_leaf[0] = child
+
+  _VisitNodeRec(tree)
+
+
+def _CreateCommentsFromPrefix(comment_prefix,
+                              comment_lineno,
+                              comment_column,
+                              standalone=False):
+  """Create pytree nodes to represent the given comment prefix.
+
+  Args:
+    comment_prefix: (unicode) the text of the comment from the node's prefix.
+    comment_lineno: (int) the line number for the start of the comment.
+    comment_column: (int) the column for the start of the comment.
+    standalone: (bool) determines if the comment is standalone or not.
+
+  Returns:
+    The simple_stmt nodes if this is a standalone comment, otherwise a list of
+    new COMMENT leafs. The prefix may consist of multiple comment blocks,
+    separated by blank lines. Each block gets its own leaf.
+  """
+  # The comment is stored in the prefix attribute, with no lineno of its
+  # own. So we only know at which line it ends. To find out at which line it
+  # starts, look at how many newlines the comment itself contains.
+  comments = []
+
+  lines = comment_prefix.split('\n')
+  index = 0
+  while index < len(lines):
+    comment_block = []
+    while index < len(lines) and lines[index].lstrip().startswith('#'):
+      comment_block.append(lines[index].strip())
+      index += 1
+
+    if comment_block:
+      new_lineno = comment_lineno + index - 1
+      comment_block[0] = comment_block[0].strip()
+      comment_block[-1] = comment_block[-1].strip()
+      comment_leaf = pytree.Leaf(
+          type=token.COMMENT,
+          value='\n'.join(comment_block),
+          context=('', (new_lineno, comment_column)))
+      comment_node = comment_leaf if not standalone else pytree.Node(
+          pygram.python_symbols.simple_stmt, [comment_leaf])
+      comments.append(comment_node)
+
+    while index < len(lines) and not lines[index].lstrip():
+      index += 1
+
+  return comments
+
+
+# "Standalone line nodes" are tree nodes that have to start a new line in Python
+# code (and cannot follow a ';' or ':'). Other nodes, like 'expr_stmt', serve as
+# parents of other nodes but can come later in a line. This is a list of
+# standalone line nodes in the grammar. It is meant to be exhaustive
+# *eventually*, and we'll modify it with time as we discover more corner cases
+# in the parse tree.
+#
+# When splicing a standalone comment (i.e. a comment that appears on its own
+# line, not on the same line with other code), it's important to insert it into
+# an appropriate parent of the node it's attached to. An appropriate parent
+# is the first "standaline line node" in the parent chain of a node.
+_STANDALONE_LINE_NODES = frozenset([
+    'suite', 'if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt',
+    'funcdef', 'classdef', 'decorated', 'file_input'
+])
+
+
+def _FindNodeWithStandaloneLineParent(node):
+  """Find a node whose parent is a 'standalone line' node.
+
+  See the comment above _STANDALONE_LINE_NODES for more details.
+
+  Arguments:
+    node: node to start from
+
+  Returns:
+    Suitable node that's either the node itself or one of its ancestors.
+  """
+  if pytree_utils.NodeName(node.parent) in _STANDALONE_LINE_NODES:
+    return node
+  else:
+    # This is guaranteed to terminate because 'file_input' is the root node of
+    # any pytree.
+    return _FindNodeWithStandaloneLineParent(node.parent)
+
+
+# "Statement nodes" are standalone statements. The don't have to start a new
+# line.
+_STATEMENT_NODES = frozenset(['simple_stmt']) | _STANDALONE_LINE_NODES
+
+
+def _FindStmtParent(node):
+  """Find the nearest parent of node that is a statement node.
+
+  Arguments:
+    node: node to start from
+
+  Returns:
+    Nearest parent (or node itself, if suitable).
+  """
+  if pytree_utils.NodeName(node) in _STATEMENT_NODES:
+    return node
+  else:
+    return _FindStmtParent(node.parent)
+
+
+def _FindAncestorAtIndent(node, indent):
+  """Find an ancestor of node with the given indentation.
+
+  Arguments:
+    node: node to start from. This must not be the tree root.
+    indent: indentation string for the ancestor we're looking for.
+        See _AnnotateIndents for more details.
+
+  Returns:
+    An ancestor node with suitable indentation. If no suitable ancestor is
+    found, the closest ancestor to the tree root is returned.
+  """
+  if node.parent.parent is None:
+    # Our parent is the tree root, so there's nowhere else to go.
+    return node
+
+  # If the parent has an indent annotation, and it's shorter than node's
+  # indent, this is a suitable ancestor.
+  # The reason for "shorter" rather than "equal" is that comments may be
+  # improperly indented (i.e. by three spaces, where surrounding statements
+  # have either zero or two or four), and we don't want to propagate them all
+  # the way to the root.
+  parent_indent = pytree_utils.GetNodeAnnotation(
+      node.parent, pytree_utils.Annotation.CHILD_INDENT)
+  if parent_indent is not None and indent.startswith(parent_indent):
+    return node
+  else:
+    # Keep looking up the tree.
+    return _FindAncestorAtIndent(node.parent, indent)
+
+
+def _FindNextAncestor(node):
+  if node.parent is None:
+    return node
+
+  if node.parent.next_sibling is not None:
+    return node.parent.next_sibling
+
+  return _FindNextAncestor(node.parent)
+
+
+def _AnnotateIndents(tree):
+  """Annotate the tree with child_indent annotations.
+
+  A child_indent annotation on a node specifies the indentation (as a string,
+  like "  ") of its children. It is inferred from the INDENT child of a node.
+
+  Arguments:
+    tree: root of a pytree. The pytree is modified to add annotations to nodes.
+
+  Raises:
+    RuntimeError: if the tree is malformed.
+  """
+  # Annotate the root of the tree with zero indent.
+  if tree.parent is None:
+    pytree_utils.SetNodeAnnotation(tree, pytree_utils.Annotation.CHILD_INDENT,
+                                   '')
+  for child in tree.children:
+    if child.type == token.INDENT:
+      child_indent = pytree_utils.GetNodeAnnotation(
+          tree, pytree_utils.Annotation.CHILD_INDENT)
+      if child_indent is not None and child_indent != child.value:
+        raise RuntimeError('inconsistent indentation for child', (tree, child))
+      pytree_utils.SetNodeAnnotation(tree, pytree_utils.Annotation.CHILD_INDENT,
+                                     child.value)
+    _AnnotateIndents(child)
diff --git a/src/tools/yapf/yapf/yapflib/continuation_splicer.py b/src/tools/yapf/yapf/yapflib/continuation_splicer.py
new file mode 100644
index 0000000..74ea1a0
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/continuation_splicer.py
@@ -0,0 +1,52 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Insert "continuation" nodes into lib2to3 tree.
+
+The "backslash-newline" continuation marker is shoved into the node's prefix.
+Pull them out and make it into nodes of their own.
+
+  SpliceContinuations(): the main funciton exported by this module.
+"""
+
+from lib2to3 import pytree
+
+from yapf.yapflib import format_token
+
+
+def SpliceContinuations(tree):
+  """Given a pytree, splice the continuation marker into nodes.
+
+  Arguments:
+    tree: (pytree.Node) The tree to work on. The tree is modified by this
+      function.
+  """
+
+  def RecSplicer(node):
+    """Inserts a continuation marker into the node."""
+    if isinstance(node, pytree.Leaf):
+      if node.prefix.lstrip().startswith('\\\n'):
+        new_lineno = node.lineno - node.prefix.count('\n')
+        return pytree.Leaf(
+            type=format_token.CONTINUATION,
+            value=node.prefix,
+            context=('', (new_lineno, 0)))
+      return None
+    num_inserted = 0
+    for index, child in enumerate(node.children[:]):
+      continuation_node = RecSplicer(child)
+      if continuation_node:
+        node.children.insert(index + num_inserted, continuation_node)
+        num_inserted += 1
+
+  RecSplicer(tree)
diff --git a/src/tools/yapf/yapf/yapflib/errors.py b/src/tools/yapf/yapf/yapflib/errors.py
new file mode 100644
index 0000000..aa8f3ea
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/errors.py
@@ -0,0 +1,23 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""YAPF error object."""
+
+
+class YapfError(Exception):
+  """Parent class for user errors or input errors.
+
+  Exceptions of this type are handled by the command line tool
+  and result in clear error messages, as opposed to backtraces.
+  """
+  pass
diff --git a/src/tools/yapf/yapf/yapflib/file_resources.py b/src/tools/yapf/yapf/yapflib/file_resources.py
new file mode 100644
index 0000000..e7f9acd
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/file_resources.py
@@ -0,0 +1,169 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Interface to file resources.
+
+This module provides functions for interfacing with files: opening, writing, and
+querying.
+"""
+
+import fnmatch
+import os
+import re
+
+from lib2to3.pgen2 import tokenize
+
+from yapf.yapflib import errors
+from yapf.yapflib import py3compat
+from yapf.yapflib import style
+
+CR = '\r'
+LF = '\n'
+CRLF = '\r\n'
+
+
+def GetDefaultStyleForDir(dirname):
+  """Return default style name for a given directory.
+
+  Looks for .style.yapf or setup.cfg in the parent directories.
+
+  Arguments:
+    dirname: (unicode) The name of the directory.
+
+  Returns:
+    The filename if found, otherwise return the global default (pep8).
+  """
+  dirname = os.path.abspath(dirname)
+  while True:
+    # See if we have a .style.yapf file.
+    style_file = os.path.join(dirname, style.LOCAL_STYLE)
+    if os.path.exists(style_file):
+      return style_file
+
+    # See if we have a setup.cfg file with a '[yapf]' section.
+    config_file = os.path.join(dirname, style.SETUP_CONFIG)
+    if os.path.exists(config_file):
+      with open(config_file) as fd:
+        config = py3compat.ConfigParser()
+        config.read_file(fd)
+        if config.has_section('yapf'):
+          return config_file
+
+    dirname = os.path.dirname(dirname)
+    if (not dirname or not os.path.basename(dirname) or
+        dirname == os.path.abspath(os.path.sep)):
+      break
+
+  global_file = os.path.expanduser(style.GLOBAL_STYLE)
+  if os.path.exists(global_file):
+    return global_file
+
+  return style.DEFAULT_STYLE
+
+
+def GetCommandLineFiles(command_line_file_list, recursive, exclude):
+  """Return the list of files specified on the command line."""
+  return _FindPythonFiles(command_line_file_list, recursive, exclude)
+
+
+def WriteReformattedCode(filename,
+                         reformatted_code,
+                         in_place=False,
+                         encoding=''):
+  """Emit the reformatted code.
+
+  Write the reformatted code into the file, if in_place is True. Otherwise,
+  write to stdout.
+
+  Arguments:
+    filename: (unicode) The name of the unformatted file.
+    reformatted_code: (unicode) The reformatted code.
+    in_place: (bool) If True, then write the reformatted code to the file.
+    encoding: (unicode) The encoding of the file.
+  """
+  if in_place:
+    with py3compat.open_with_encoding(
+        filename, mode='w', encoding=encoding, newline='') as fd:
+      fd.write(reformatted_code)
+  else:
+    py3compat.EncodeAndWriteToStdout(reformatted_code)
+
+
+def LineEnding(lines):
+  """Retrieve the line ending of the original source."""
+  endings = {CRLF: 0, CR: 0, LF: 0}
+  for line in lines:
+    if line.endswith(CRLF):
+      endings[CRLF] += 1
+    elif line.endswith(CR):
+      endings[CR] += 1
+    elif line.endswith(LF):
+      endings[LF] += 1
+  return (sorted(endings, key=endings.get, reverse=True) or [LF])[0]
+
+
+def _FindPythonFiles(filenames, recursive, exclude):
+  """Find all Python files."""
+  python_files = []
+  for filename in filenames:
+    if os.path.isdir(filename):
+      if recursive:
+        # TODO(morbo): Look into a version of os.walk that can handle recursion.
+        python_files.extend(
+            os.path.join(dirpath, f)
+            for dirpath, _, filelist in os.walk(filename) for f in filelist
+            if IsPythonFile(os.path.join(dirpath, f)))
+      else:
+        raise errors.YapfError(
+            "directory specified without '--recursive' flag: %s" % filename)
+    elif os.path.isfile(filename):
+      python_files.append(filename)
+
+  if exclude:
+    return [
+        f for f in python_files
+        if not any(fnmatch.fnmatch(f, p) for p in exclude)
+    ]
+
+  return python_files
+
+
+def IsPythonFile(filename):
+  """Return True if filename is a Python file."""
+  if os.path.splitext(filename)[1] == '.py':
+    return True
+
+  try:
+    with open(filename, 'rb') as fd:
+      encoding = tokenize.detect_encoding(fd.readline)[0]
+
+    # Check for correctness of encoding.
+    with py3compat.open_with_encoding(
+        filename, mode='r', encoding=encoding) as fd:
+      fd.read()
+  except UnicodeDecodeError:
+    encoding = 'latin-1'
+  except (IOError, SyntaxError):
+    # If we fail to detect encoding (or the encoding cookie is incorrect - which
+    # will make detect_encoding raise SyntaxError), assume it's not a Python
+    # file.
+    return False
+
+  try:
+    with py3compat.open_with_encoding(
+        filename, mode='r', encoding=encoding) as fd:
+      first_line = fd.readlines()[0]
+  except (IOError, IndexError):
+    return False
+
+  return re.match(r'^#!.*\bpython[23]?\b', first_line)
diff --git a/src/tools/yapf/yapf/yapflib/format_decision_state.py b/src/tools/yapf/yapf/yapflib/format_decision_state.py
new file mode 100644
index 0000000..3c17dc4
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/format_decision_state.py
@@ -0,0 +1,799 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Implements a format decision state object that manages whitespace decisions.
+
+Each token is processed one at a time, at which point its whitespace formatting
+decisions are made. A graph of potential whitespace formattings is created,
+where each node in the graph is a format decision state object. The heuristic
+tries formatting the token with and without a newline before it to determine
+which one has the least penalty. Therefore, the format decision state object for
+each decision needs to be its own unique copy.
+
+Once the heuristic determines the best formatting, it makes a non-dry run pass
+through the code to commit the whitespace formatting.
+
+  FormatDecisionState: main class exported by this module.
+"""
+
+from yapf.yapflib import format_token
+from yapf.yapflib import split_penalty
+from yapf.yapflib import style
+from yapf.yapflib import unwrapped_line
+
+_COMPOUND_STMTS = frozenset(
+    {'for', 'while', 'if', 'elif', 'with', 'except', 'def', 'class'})
+
+
+class FormatDecisionState(object):
+  """The current state when indenting an unwrapped line.
+
+  The FormatDecisionState object is meant to be copied instead of referenced.
+
+  Attributes:
+    first_indent: The indent of the first token.
+    column: The number of used columns in the current line.
+    next_token: The next token to be formatted.
+    paren_level: The level of nesting inside (), [], and {}.
+    start_of_line_level: The paren_level at the start of this line.
+    lowest_level_on_line: The lowest paren_level on the current line.
+    newline: Indicates if a newline is added along the edge to this format
+      decision state node.
+    previous: The previous format decision state in the decision tree.
+    stack: A stack (of _ParenState) keeping track of properties applying to
+      parenthesis levels.
+    ignore_stack_for_comparison: Ignore the stack of _ParenState for state
+      comparison.
+  """
+
+  def __init__(self, line, first_indent):
+    """Initializer.
+
+    Initializes to the state after placing the first token from 'line' at
+    'first_indent'.
+
+    Arguments:
+      line: (UnwrappedLine) The unwrapped line we're currently processing.
+      first_indent: (int) The indent of the first token.
+    """
+    self.next_token = line.first
+    self.column = first_indent
+    self.line = line
+    self.paren_level = 0
+    self.start_of_line_level = 0
+    self.lowest_level_on_line = 0
+    self.ignore_stack_for_comparison = False
+    self.stack = [_ParenState(first_indent, first_indent)]
+    self.first_indent = first_indent
+    self.newline = False
+    self.previous = None
+    self.column_limit = style.Get('COLUMN_LIMIT')
+
+  def Clone(self):
+    """Clones a FormatDecisionState object."""
+    new = FormatDecisionState(self.line, self.first_indent)
+    new.next_token = self.next_token
+    new.column = self.column
+    new.line = self.line
+    new.paren_level = self.paren_level
+    new.start_of_line_level = self.start_of_line_level
+    new.lowest_level_on_line = self.lowest_level_on_line
+    new.ignore_stack_for_comparison = self.ignore_stack_for_comparison
+    new.first_indent = self.first_indent
+    new.newline = self.newline
+    new.previous = self.previous
+    new.stack = [state.Clone() for state in self.stack]
+    return new
+
+  def __eq__(self, other):
+    # Note: 'first_indent' is implicit in the stack. Also, we ignore 'previous',
+    # because it shouldn't have a bearing on this comparison. (I.e., it will
+    # report equal if 'next_token' does.)
+    return (self.next_token == other.next_token and
+            self.column == other.column and
+            self.paren_level == other.paren_level and
+            self.start_of_line_level == other.start_of_line_level and
+            self.lowest_level_on_line == other.lowest_level_on_line and
+            (self.ignore_stack_for_comparison or
+             other.ignore_stack_for_comparison or self.stack == other.stack))
+
+  def __ne__(self, other):
+    return not self == other
+
+  def __hash__(self):
+    return hash((self.next_token, self.column, self.paren_level,
+                 self.start_of_line_level, self.lowest_level_on_line))
+
+  def __repr__(self):
+    return ('column::%d, next_token::%s, paren_level::%d, stack::[\n\t%s' %
+            (self.column, repr(self.next_token), self.paren_level,
+             '\n\t'.join(repr(s) for s in self.stack) + ']'))
+
+  def CanSplit(self, must_split):
+    """Determine if we can split before the next token.
+
+    Arguments:
+      must_split: (bool) A newline was required before this token.
+
+    Returns:
+      True if the line can be split before the next token.
+    """
+    current = self.next_token
+
+    if current.is_pseudo_paren:
+      return False
+
+    if (not must_split and
+        format_token.Subtype.DICTIONARY_KEY_PART in current.subtypes and
+        format_token.Subtype.DICTIONARY_KEY not in current.subtypes and
+        not style.Get('ALLOW_MULTILINE_DICTIONARY_KEYS')):
+      # In some situations, a dictionary may be multiline, but pylint doesn't
+      # like it. So don't allow it unless forced to.
+      return False
+
+    return current.can_break_before
+
+  def MustSplit(self):
+    """Returns True if the line must split before the next token."""
+    current = self.next_token
+    previous = current.previous_token
+
+    if current.is_pseudo_paren:
+      return False
+
+    if current.must_break_before:
+      return True
+
+    if not previous:
+      return False
+
+    if self.stack[-1].split_before_closing_bracket and current.value in '}]':
+      # Split before the closing bracket if we can.
+      return current.node_split_penalty != split_penalty.UNBREAKABLE
+
+    # Prevent splitting before the first argument in compound statements
+    # with the exception of function declarations.
+    if (style.Get('SPLIT_BEFORE_FIRST_ARGUMENT') and
+        self.line.first.value != 'def' and
+        self.line.first.value in _COMPOUND_STMTS):
+      return False
+
+    ###########################################################################
+    # List Splitting
+    if (style.Get('DEDENT_CLOSING_BRACKETS') or
+        style.Get('SPLIT_BEFORE_FIRST_ARGUMENT')):
+      bracket = current if current.ClosesScope() else previous
+      if format_token.Subtype.SUBSCRIPT_BRACKET not in bracket.subtypes:
+        if bracket.OpensScope():
+          if style.Get('COALESCE_BRACKETS'):
+            if current.OpensScope():
+              # Prefer to keep all opening brackets together.
+              return False
+
+          if (not _IsLastScopeInLine(bracket) or
+              unwrapped_line.IsSurroundedByBrackets(bracket)):
+            last_token = bracket.matching_bracket
+          else:
+            last_token = _LastTokenInLine(bracket.matching_bracket)
+
+          if not self._FitsOnLine(bracket, last_token):
+            # Split before the first element if the whole list can't fit on a
+            # single line.
+            self.stack[-1].split_before_closing_bracket = True
+            return True
+
+        elif style.Get('DEDENT_CLOSING_BRACKETS') and current.ClosesScope():
+          # Split before and dedent the closing bracket.
+          return self.stack[-1].split_before_closing_bracket
+
+    if (current.is_name or current.is_string) and previous.value == ',':
+      # If the list has function calls in it and the full list itself cannot
+      # fit on the line, then we want to split. Otherwise, we'll get something
+      # like this:
+      #
+      #     X = [
+      #         Bar(xxx='some string',
+      #             yyy='another long string',
+      #             zzz='a third long string'), Bar(
+      #                 xxx='some string',
+      #                 yyy='another long string',
+      #                 zzz='a third long string')
+      #     ]
+      #
+      # or when a string formatting syntax.
+      func_call_or_string_format = False
+      if current.is_name:
+        tok = current.next_token
+        while tok and (tok.is_name or tok.value == '.'):
+          tok = tok.next_token
+        func_call_or_string_format = tok and tok.value == '('
+      elif current.is_string:
+        tok = current.next_token
+        while tok and tok.is_string:
+          tok = tok.next_token
+        func_call_or_string_format = tok and tok.value == '%'
+      if func_call_or_string_format:
+        open_bracket = unwrapped_line.IsSurroundedByBrackets(current)
+        if open_bracket and open_bracket.value in '[{':
+          if not self._FitsOnLine(open_bracket, open_bracket.matching_bracket):
+            return True
+
+    ###########################################################################
+    # Dict/Set Splitting
+    if (style.Get('EACH_DICT_ENTRY_ON_SEPARATE_LINE') and
+        format_token.Subtype.DICTIONARY_KEY in current.subtypes and
+        not current.is_comment):
+      # Place each dictionary entry onto its own line.
+      if previous.value == '{' and previous.previous_token:
+        opening = _GetOpeningBracket(previous.previous_token)
+        if (opening and opening.value == '(' and opening.previous_token and
+            opening.previous_token.is_name):
+          # This is a dictionary that's an argument to a function.
+          if self._FitsOnLine(previous, previous.matching_bracket):
+            return False
+      return True
+
+    if (style.Get('SPLIT_BEFORE_DICT_SET_GENERATOR') and
+        format_token.Subtype.DICT_SET_GENERATOR in current.subtypes):
+      # Split before a dict/set generator.
+      return True
+
+    if (format_token.Subtype.DICTIONARY_VALUE in current.subtypes or
+        (previous.is_pseudo_paren and previous.value == '(' and
+         not current.is_comment)):
+      # Split before the dictionary value if we can't fit every dictionary
+      # entry on its own line.
+      if not current.OpensScope():
+        opening = _GetOpeningBracket(current)
+        if not self._EachDictEntryFitsOnOneLine(opening):
+          return True
+
+    if previous.value == '{':
+      # Split if the dict/set cannot fit on one line and ends in a comma.
+      closing = previous.matching_bracket
+      if (not self._FitsOnLine(previous, closing) and
+          closing.previous_token.value == ','):
+        self.stack[-1].split_before_closing_bracket = True
+        return True
+
+    ###########################################################################
+    # Argument List Splitting
+    if (style.Get('SPLIT_BEFORE_NAMED_ASSIGNS') and not current.is_comment and
+        format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST in
+        current.subtypes):
+      if (previous.value not in {'=', ':', '*', '**'} and
+          current.value not in ':=,)' and not _IsFunctionDefinition(previous)):
+        # If we're going to split the lines because of named arguments, then we
+        # want to split after the opening bracket as well. But not when this is
+        # part of a function definition.
+        if previous.value == '(':
+          # Make sure we don't split after the opening bracket if the
+          # continuation indent is greater than the opening bracket:
+          #
+          #  a(
+          #      b=1,
+          #      c=2)
+          if (self._FitsOnLine(previous, previous.matching_bracket) and
+              unwrapped_line.IsSurroundedByBrackets(previous)):
+            # An argument to a function is a function call with named
+            # assigns.
+            return False
+
+          column = self.column - self.stack[-1].last_space
+          return column > style.Get('CONTINUATION_INDENT_WIDTH')
+
+        opening = _GetOpeningBracket(current)
+        if opening:
+          arglist_length = (opening.matching_bracket.total_length -
+                            opening.total_length + self.stack[-1].indent)
+          return arglist_length > self.column_limit
+
+    if style.Get('SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED'):
+      # Split before arguments in a function call or definition if the
+      # arguments are terminated by a comma.
+      opening = _GetOpeningBracket(current)
+      if opening and opening.previous_token and opening.previous_token.is_name:
+        if previous.value in '(,':
+          if opening.matching_bracket.previous_token.value == ',':
+            return True
+
+    if ((current.is_name or current.value in {'*', '**'}) and
+        previous.value == ','):
+      # If we have a function call within an argument list and it won't fit on
+      # the remaining line, but it will fit on a line by itself, then go ahead
+      # and split before the call.
+      opening = _GetOpeningBracket(current)
+      if (opening and opening.value == '(' and opening.previous_token and
+          (opening.previous_token.is_name or
+           opening.previous_token.value in {'*', '**'})):
+        is_func_call = False
+        token = current
+        while token:
+          if token.value == '(':
+            is_func_call = True
+            break
+          if (not (token.is_name or token.value in {'*', '**'}) and
+              token.value != '.'):
+            break
+          token = token.next_token
+
+        if is_func_call:
+          if not self._FitsOnLine(current, opening.matching_bracket):
+            return True
+
+    pprevious = previous.previous_token
+    if (current.is_name and pprevious and pprevious.is_name and
+        previous.value == '('):
+      if (not self._FitsOnLine(previous, previous.matching_bracket) and
+          _IsFunctionCallWithArguments(current)):
+        # There is a function call, with more than 1 argument, where the first
+        # argument is itself a function call with arguments.  In this specific
+        # case, if we split after the first argument's opening '(', then the
+        # formatting will look bad for the rest of the arguments. E.g.:
+        #
+        #     outer_function_call(inner_function_call(
+        #         inner_arg1, inner_arg2),
+        #                         outer_arg1, outer_arg2)
+        #
+        # Instead, enforce a split before that argument to keep things looking
+        # good.
+        return True
+
+    if (previous.OpensScope() and not current.OpensScope() and
+        format_token.Subtype.SUBSCRIPT_BRACKET not in previous.subtypes):
+      if not current.is_comment:
+        if pprevious and not pprevious.is_keyword and not pprevious.is_name:
+          # We want to split if there's a comment in the container.
+          token = current
+          while token != previous.matching_bracket:
+            if token.is_comment:
+              return True
+            token = token.next_token
+
+      if previous.value == '(':
+        pptoken = previous.previous_token
+        if not pptoken or not pptoken.is_name:
+          # Split after the opening of a tuple if it doesn't fit on the current
+          # line and it's not a function call.
+          if self._FitsOnLine(previous, previous.matching_bracket):
+            return False
+        elif not self._FitsOnLine(previous, previous.matching_bracket):
+          if (self.column_limit - self.column) / float(self.column_limit) < 0.3:
+            # Try not to squish all of the arguments off to the right.
+            return current.next_token != previous.matching_bracket
+      else:
+        # Split after the opening of a container if it doesn't fit on the
+        # current line or if it has a comment.
+        if not self._FitsOnLine(previous, previous.matching_bracket):
+          return True
+
+    ###########################################################################
+    # List Comprehension Splitting
+    if (format_token.Subtype.COMP_FOR in current.subtypes and
+        format_token.Subtype.COMP_FOR not in previous.subtypes):
+      # Split at the beginning of a list comprehension.
+      length = _GetLengthOfSubtype(current, format_token.Subtype.COMP_FOR,
+                                   format_token.Subtype.COMP_IF)
+      if length + self.column > self.column_limit:
+        return True
+
+    if (format_token.Subtype.COMP_IF in current.subtypes and
+        format_token.Subtype.COMP_IF not in previous.subtypes):
+      # Split at the beginning of an if expression.
+      length = _GetLengthOfSubtype(current, format_token.Subtype.COMP_IF)
+      if length + self.column > self.column_limit:
+        return True
+
+    ###########################################################################
+    # Original Formatting Splitting
+    # These checks rely upon the original formatting. This is in order to
+    # attempt to keep hand-written code in the same condition as it was before.
+    # However, this may cause the formatter to fail to be idempotent.
+    if (style.Get('SPLIT_BEFORE_BITWISE_OPERATOR') and current.value in '&|' and
+        previous.lineno < current.lineno):
+      # Retain the split before a bitwise operator.
+      return True
+
+    if (current.is_comment and
+        previous.lineno < current.lineno - current.value.count('\n')):
+      # If a comment comes in the middle of an unwrapped line (like an if
+      # conditional with comments interspersed), then we want to split if the
+      # original comments were on a separate line.
+      return True
+
+    return False
+
+  def AddTokenToState(self, newline, dry_run, must_split=False):
+    """Add a token to the format decision state.
+
+    Allow the heuristic to try out adding the token with and without a newline.
+    Later on, the algorithm will determine which one has the lowest penalty.
+
+    Arguments:
+      newline: (bool) Add the token on a new line if True.
+      dry_run: (bool) Don't commit whitespace changes to the FormatToken if
+        True.
+      must_split: (bool) A newline was required before this token.
+
+    Returns:
+      The penalty of splitting after the current token.
+    """
+    penalty = 0
+    if newline:
+      penalty = self._AddTokenOnNewline(dry_run, must_split)
+    else:
+      self._AddTokenOnCurrentLine(dry_run)
+
+    return self.MoveStateToNextToken() + penalty
+
+  def _AddTokenOnCurrentLine(self, dry_run):
+    """Puts the token on the current line.
+
+    Appends the next token to the state and updates information necessary for
+    indentation.
+
+    Arguments:
+      dry_run: (bool) Commit whitespace changes to the FormatToken if True.
+    """
+    current = self.next_token
+    previous = current.previous_token
+
+    spaces = current.spaces_required_before
+    if not dry_run:
+      current.AddWhitespacePrefix(newlines_before=0, spaces=spaces)
+
+    if previous.OpensScope():
+      if not current.is_comment:
+        # Align closing scopes that are on a newline with the opening scope:
+        #
+        #     foo = [a,
+        #            b,
+        #           ]
+        self.stack[-1].closing_scope_indent = self.column - 1
+        if style.Get('ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'):
+          self.stack[-1].closing_scope_indent += 1
+        self.stack[-1].indent = self.column + spaces
+      else:
+        self.stack[-1].closing_scope_indent = (
+            self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH'))
+
+    self.column += spaces
+
+  def _AddTokenOnNewline(self, dry_run, must_split):
+    """Adds a line break and necessary indentation.
+
+    Appends the next token to the state and updates information necessary for
+    indentation.
+
+    Arguments:
+      dry_run: (bool) Don't commit whitespace changes to the FormatToken if
+        True.
+      must_split: (bool) A newline was required before this token.
+
+    Returns:
+      The split penalty for splitting after the current state.
+    """
+    current = self.next_token
+    previous = current.previous_token
+
+    self.column = self._GetNewlineColumn()
+
+    if not dry_run:
+      current.AddWhitespacePrefix(newlines_before=1, spaces=self.column)
+
+    if not current.is_comment:
+      self.stack[-1].last_space = self.column
+    self.start_of_line_level = self.paren_level
+    self.lowest_level_on_line = self.paren_level
+
+    if (previous.OpensScope() or
+        (previous.is_comment and previous.previous_token is not None and
+         previous.previous_token.OpensScope())):
+      self.stack[-1].closing_scope_indent = max(
+          0, self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH'))
+
+      split_before_closing_bracket = True
+      if style.Get('COALESCE_BRACKETS'):
+        split_before_closing_bracket = False
+
+      self.stack[-1].split_before_closing_bracket = split_before_closing_bracket
+
+    # Calculate the split penalty.
+    penalty = current.split_penalty
+
+    if must_split:
+      # Don't penalize for a must split.
+      return penalty
+
+    if previous.is_pseudo_paren and previous.value == '(':
+      # Small penalty for splitting after a pseudo paren.
+      penalty += 50
+
+    # Add a penalty for each increasing newline we add, but don't penalize for
+    # splitting before an if-expression or list comprehension.
+    if current.value not in {'if', 'for'}:
+      last = self.stack[-1]
+      last.num_line_splits += 1
+      penalty += (style.Get('SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT') *
+                  last.num_line_splits)
+
+    if current.OpensScope() and previous.OpensScope():
+      # Prefer to keep opening brackets coalesced (unless it's at the beginning
+      # of a function call).
+      pprev = previous.previous_token
+      if not pprev or not pprev.is_name:
+        penalty += 10
+
+    return penalty + 10
+
+  def _GetNewlineColumn(self):
+    """Return the new column on the newline."""
+    current = self.next_token
+    previous = current.previous_token
+    top_of_stack = self.stack[-1]
+
+    if current.spaces_required_before > 2 or self.line.disable:
+      return current.spaces_required_before
+
+    if current.OpensScope():
+      return top_of_stack.indent if self.paren_level else self.first_indent
+
+    if current.ClosesScope():
+      if (previous.OpensScope() or
+          (previous.is_comment and previous.previous_token is not None and
+           previous.previous_token.OpensScope())):
+        return max(0,
+                   top_of_stack.indent - style.Get('CONTINUATION_INDENT_WIDTH'))
+      return top_of_stack.closing_scope_indent
+
+    if (previous and previous.is_string and current.is_string and
+        format_token.Subtype.DICTIONARY_VALUE in current.subtypes):
+      return previous.column
+
+    if style.Get('INDENT_DICTIONARY_VALUE'):
+      if previous and (previous.value == ':' or previous.is_pseudo_paren):
+        if format_token.Subtype.DICTIONARY_VALUE in current.subtypes:
+          return top_of_stack.indent
+
+    if (self.line.first.value in _COMPOUND_STMTS and
+        (not style.Get('DEDENT_CLOSING_BRACKETS') or
+         style.Get('SPLIT_BEFORE_FIRST_ARGUMENT'))):
+      token_indent = (len(self.line.first.whitespace_prefix.split('\n')[-1]) +
+                      style.Get('INDENT_WIDTH'))
+      if token_indent == top_of_stack.indent:
+        return top_of_stack.indent + style.Get('CONTINUATION_INDENT_WIDTH')
+
+    return top_of_stack.indent
+
+  def MoveStateToNextToken(self):
+    """Calculate format decision state information and move onto the next token.
+
+    Before moving onto the next token, we first calculate the format decision
+    state given the current token and its formatting decisions. Then the format
+    decision state is set up so that the next token can be added.
+
+    Returns:
+      The penalty for the number of characters over the column limit.
+    """
+    current = self.next_token
+    if not current.OpensScope() and not current.ClosesScope():
+      self.lowest_level_on_line = min(self.lowest_level_on_line,
+                                      self.paren_level)
+
+    # If we encounter an opening bracket, we add a level to our stack to prepare
+    # for the subsequent tokens.
+    if current.OpensScope():
+      last = self.stack[-1]
+      new_indent = style.Get('CONTINUATION_INDENT_WIDTH') + last.last_space
+
+      self.stack.append(_ParenState(new_indent, self.stack[-1].last_space))
+      self.paren_level += 1
+
+    # If we encounter a closing bracket, we can remove a level from our
+    # parenthesis stack.
+    if len(self.stack) > 1 and current.ClosesScope():
+      self.stack[-2].last_space = self.stack[-1].last_space
+      self.stack.pop()
+      self.paren_level -= 1
+
+    is_multiline_string = current.is_string and '\n' in current.value
+    if is_multiline_string:
+      # This is a multiline string. Only look at the first line.
+      self.column += len(current.value.split('\n')[0])
+    elif not current.is_pseudo_paren:
+      self.column += len(current.value)
+
+    self.next_token = self.next_token.next_token
+
+    # Calculate the penalty for overflowing the column limit.
+    penalty = 0
+    if not current.is_pylint_comment and self.column > self.column_limit:
+      excess_characters = self.column - self.column_limit
+      penalty += style.Get('SPLIT_PENALTY_EXCESS_CHARACTER') * excess_characters
+
+    if is_multiline_string:
+      # If this is a multiline string, the column is actually the
+      # end of the last line in the string.
+      self.column = len(current.value.split('\n')[-1])
+
+    return penalty
+
+  def _FitsOnLine(self, start, end):
+    """Determines if line between start and end can fit on the current line."""
+    length = end.total_length - start.total_length
+    if not start.is_pseudo_paren:
+      length += len(start.value)
+    return length + self.column <= self.column_limit
+
+  def _EachDictEntryFitsOnOneLine(self, opening):
+    """Determine if each dict elems can fit on one line."""
+
+    def PreviousNonCommentToken(tok):
+      tok = tok.previous_token
+      while tok.is_comment:
+        tok = tok.previous_token
+      return tok
+
+    def ImplicitStringConcatenation(tok):
+      num_strings = 0
+      if tok.is_pseudo_paren:
+        tok = tok.next_token
+      while tok.is_string:
+        num_strings += 1
+        tok = tok.next_token
+      return num_strings > 1
+
+    closing = opening.matching_bracket
+    entry_start = opening.next_token
+    current = opening.next_token.next_token
+
+    while current and current != closing:
+      if format_token.Subtype.DICTIONARY_KEY in current.subtypes:
+        prev = PreviousNonCommentToken(current)
+        length = prev.total_length - entry_start.total_length
+        length += len(entry_start.value)
+        if length + self.stack[-2].indent >= self.column_limit:
+          return False
+        entry_start = current
+      if current.OpensScope():
+        if ((current.value == '{' or
+             (current.is_pseudo_paren and current.next_token.value == '{') and
+             format_token.Subtype.DICTIONARY_VALUE in current.subtypes) or
+            ImplicitStringConcatenation(current)):
+          # A dictionary entry that cannot fit on a single line shouldn't matter
+          # to this calcuation. If it can't fit on a single line, then the
+          # opening should be on the same line as the key and the rest on
+          # newlines after it. But the other entries should be on single lines
+          # if possible.
+          if current.matching_bracket:
+            current = current.matching_bracket
+          while current:
+            if current == closing:
+              return True
+            if format_token.Subtype.DICTIONARY_KEY in current.subtypes:
+              entry_start = current
+              break
+            current = current.next_token
+        else:
+          current = current.matching_bracket
+      else:
+        current = current.next_token
+
+    # At this point, current is the closing bracket. Go back one to get the the
+    # end of the dictionary entry.
+    current = PreviousNonCommentToken(current)
+    length = current.total_length - entry_start.total_length
+    length += len(entry_start.value)
+    return length + self.stack[-2].indent <= self.column_limit
+
+
+def _IsFunctionCallWithArguments(token):
+  while token:
+    if token.value == '(':
+      token = token.next_token
+      return token and token.value != ')'
+    elif token.name not in {'NAME', 'DOT'}:
+      break
+    token = token.next_token
+  return False
+
+
+def _GetLengthOfSubtype(token, subtype, exclude=None):
+  current = token
+  while (current.next_token and subtype in current.subtypes and
+         (exclude is None or exclude not in current.subtypes)):
+    current = current.next_token
+  return current.total_length - token.total_length + 1
+
+
+def _GetOpeningBracket(current):
+  """Get the opening bracket containing the current token."""
+  if current.matching_bracket and not current.is_pseudo_paren:
+    return current.matching_bracket
+  while current:
+    if current.ClosesScope():
+      current = current.matching_bracket
+    elif current.is_pseudo_paren:
+      current = current.previous_token
+    elif current.OpensScope():
+      return current
+    current = current.previous_token
+  return None
+
+
+def _LastTokenInLine(current):
+  while not current.is_comment and current.next_token:
+    current = current.next_token
+  return current
+
+
+def _IsFunctionDefinition(current):
+  prev = current.previous_token
+  return (current.value == '(' and prev and
+          format_token.Subtype.FUNC_DEF in prev.subtypes)
+
+
+def _IsLastScopeInLine(current):
+  while current:
+    current = current.next_token
+    if current and current.OpensScope():
+      return False
+  return True
+
+
+class _ParenState(object):
+  """Maintains the state of the bracket enclosures.
+
+  A stack of _ParenState objects are kept so that we know how to indent relative
+  to the brackets.
+
+  Attributes:
+    indent: The column position to which a specified parenthesis level needs to
+      be indented.
+    last_space: The column position of the last space on each level.
+    split_before_closing_bracket: Whether a newline needs to be inserted before
+      the closing bracket. We only want to insert a newline before the closing
+      bracket if there also was a newline after the beginning left bracket.
+    num_line_splits: Number of line splits this _ParenState contains already.
+      Each subsequent line split gets an increasing penalty.
+  """
+
+  # TODO(morbo): This doesn't track "bin packing."
+
+  def __init__(self, indent, last_space):
+    self.indent = indent
+    self.last_space = last_space
+    self.closing_scope_indent = 0
+    self.split_before_closing_bracket = False
+    self.num_line_splits = 0
+
+  def Clone(self):
+    state = _ParenState(self.indent, self.last_space)
+    state.closing_scope_indent = self.closing_scope_indent
+    state.split_before_closing_bracket = self.split_before_closing_bracket
+    state.num_line_splits = self.num_line_splits
+    return state
+
+  def __repr__(self):
+    return '[indent::%d, last_space::%d, closing_scope_indent::%d]' % (
+        self.indent, self.last_space, self.closing_scope_indent)
+
+  def __eq__(self, other):
+    return hash(self) == hash(other)
+
+  def __ne__(self, other):
+    return not self == other
+
+  def __hash__(self, *args, **kwargs):
+    return hash((self.indent, self.last_space, self.closing_scope_indent,
+                 self.split_before_closing_bracket, self.num_line_splits))
diff --git a/src/tools/yapf/yapf/yapflib/format_token.py b/src/tools/yapf/yapf/yapflib/format_token.py
new file mode 100644
index 0000000..de270cf
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/format_token.py
@@ -0,0 +1,283 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Pytree nodes with extra formatting information.
+
+This is a thin wrapper around a pytree.Leaf node.
+"""
+
+import keyword
+import re
+
+from lib2to3.pgen2 import token
+
+from yapf.yapflib import py3compat
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import style
+
+CONTINUATION = token.N_TOKENS
+token.N_TOKENS += 1
+
+
+class Subtype(object):
+  """Subtype information about tokens.
+
+  Gleaned from parsing the code. Helps determine the best formatting.
+  """
+  NONE = 0
+  UNARY_OPERATOR = 1
+  BINARY_OPERATOR = 2
+  SUBSCRIPT_COLON = 3
+  SUBSCRIPT_BRACKET = 4
+  DEFAULT_OR_NAMED_ASSIGN = 5
+  DEFAULT_OR_NAMED_ASSIGN_ARG_LIST = 6
+  VARARGS_LIST = 7
+  VARARGS_STAR = 8
+  KWARGS_STAR_STAR = 9
+  ASSIGN_OPERATOR = 10
+  DICTIONARY_KEY = 11
+  DICTIONARY_KEY_PART = 12
+  DICTIONARY_VALUE = 13
+  DICT_SET_GENERATOR = 14
+  COMP_FOR = 15
+  COMP_IF = 16
+  FUNC_DEF = 17
+  DECORATOR = 18
+
+
+class FormatToken(object):
+  """A wrapper around pytree Leaf nodes.
+
+  This represents the token plus additional information useful for reformatting
+  the code.
+
+  Attributes:
+    next_token: The token in the unwrapped line after this token or None if this
+      is the last token in the unwrapped line.
+    previous_token: The token in the unwrapped line before this token or None if
+      this is the first token in the unwrapped line.
+    matching_bracket: If a bracket token ('[', '{', or '(') the matching
+      bracket.
+    whitespace_prefix: The prefix for the whitespace.
+    spaces_required_before: The number of spaces required before a token. This
+      is a lower-bound for the formatter and not a hard requirement. For
+      instance, a comment may have n required spaces before it. But the
+      formatter won't place n spaces before all comments. Only those that are
+      moved to the end of a line of code. The formatter may use different
+      spacing when appropriate.
+    can_break_before: True if we're allowed to break before this token.
+    must_break_before: True if we're required to break before this token.
+    total_length: The total length of the unwrapped line up to and including
+      whitespace and this token. However, this doesn't include the initial
+      indentation amount.
+    split_penalty: The penalty for splitting the line before this token.
+  """
+
+  def __init__(self, node):
+    """Constructor.
+
+    Arguments:
+      node: (pytree.Leaf) The node that's being wrapped.
+    """
+    self.node = node
+    self.next_token = None
+    self.previous_token = None
+    self.matching_bracket = None
+    self.whitespace_prefix = ''
+    self.can_break_before = False
+    self.must_break_before = False
+    self.total_length = 0  # TODO(morbo): Think up a better name.
+    self.split_penalty = 0
+
+    if self.is_comment:
+      self.spaces_required_before = style.Get('SPACES_BEFORE_COMMENT')
+    else:
+      self.spaces_required_before = 0
+
+    if self.is_continuation:
+      self.value = self.node.value.rstrip()
+    else:
+      self.value = self.node.value
+
+  def AddWhitespacePrefix(self, newlines_before, spaces=0, indent_level=0):
+    """Register a token's whitespace prefix.
+
+    This is the whitespace that will be output before a token's string.
+
+    Arguments:
+      newlines_before: (int) The number of newlines to place before the token.
+      spaces: (int) The number of spaces to place before the token.
+      indent_level: (int) The indentation level.
+    """
+    indent_char = '\t' if style.Get('USE_TABS') else ' '
+    token_indent_char = indent_char if newlines_before > 0 else ' '
+    indent_before = (indent_char * indent_level * style.Get('INDENT_WIDTH') +
+                     token_indent_char * spaces)
+
+    if self.is_comment:
+      comment_lines = [s.lstrip() for s in self.value.splitlines()]
+      self.node.value = ('\n' + indent_before).join(comment_lines)
+
+      # Update our own value since we are changing node value
+      self.value = self.node.value
+
+    if not self.whitespace_prefix:
+      self.whitespace_prefix = (
+          '\n' * (self.newlines or newlines_before) + indent_before)
+    else:
+      self.whitespace_prefix += indent_before
+
+  def AdjustNewlinesBefore(self, newlines_before):
+    """Change the number of newlines before this token."""
+    self.whitespace_prefix = (
+        '\n' * newlines_before + self.whitespace_prefix.lstrip('\n'))
+
+  def RetainHorizontalSpacing(self, first_column, depth):
+    """Retains a token's horizontal spacing."""
+    previous = self.previous_token
+    if previous is None:
+      return
+
+    cur_lineno = self.lineno
+    prev_lineno = previous.lineno
+    if previous.is_multiline_string:
+      prev_lineno += previous.value.count('\n')
+
+    if (cur_lineno != prev_lineno or
+        (previous.is_pseudo_paren and previous.value != ')' and
+         cur_lineno != previous.previous_token.lineno)):
+      self.spaces_required_before = (
+          self.column - first_column + depth * style.Get('INDENT_WIDTH'))
+      return
+
+    cur_column = self.node.column
+    prev_column = previous.node.column
+    prev_len = len(previous.value)
+
+    if previous.is_pseudo_paren and previous.value == ')':
+      prev_column -= 1
+      prev_len = 0
+
+    if previous.is_multiline_string:
+      prev_len = len(previous.value.split('\n')[-1])
+      if '\n' in previous.value:
+        prev_column = 0  # Last line starts in column 0.
+    self.spaces_required_before = cur_column - (prev_column + prev_len)
+
+  def OpensScope(self):
+    return self.value in pytree_utils.OPENING_BRACKETS
+
+  def ClosesScope(self):
+    return self.value in pytree_utils.CLOSING_BRACKETS
+
+  def __repr__(self):
+    msg = 'FormatToken(name={0}, value={1}'.format(self.name, self.value)
+    msg += ', pseudo)' if self.is_pseudo_paren else ')'
+    return msg
+
+  @property
+  @py3compat.lru_cache()
+  def node_split_penalty(self):
+    """Split penalty attached to the pytree node of this token."""
+    return pytree_utils.GetNodeAnnotation(
+        self.node, pytree_utils.Annotation.SPLIT_PENALTY, default=0)
+
+  @property
+  def newlines(self):
+    """The number of newlines needed before this token."""
+    return pytree_utils.GetNodeAnnotation(self.node,
+                                          pytree_utils.Annotation.NEWLINES)
+
+  @property
+  def must_split(self):
+    """Return true if the token requires a split before it."""
+    return pytree_utils.GetNodeAnnotation(self.node,
+                                          pytree_utils.Annotation.MUST_SPLIT)
+
+  @property
+  def column(self):
+    """The original column number of the node in the source."""
+    return self.node.column
+
+  @property
+  def lineno(self):
+    """The original line number of the node in the source."""
+    return self.node.lineno
+
+  @property
+  @py3compat.lru_cache()
+  def subtypes(self):
+    """Extra type information for directing formatting."""
+    value = pytree_utils.GetNodeAnnotation(self.node,
+                                           pytree_utils.Annotation.SUBTYPE)
+    return [Subtype.NONE] if value is None else value
+
+  @property
+  @py3compat.lru_cache()
+  def is_binary_op(self):
+    """Token is a binary operator."""
+    return Subtype.BINARY_OPERATOR in self.subtypes
+
+  @property
+  @py3compat.lru_cache()
+  def name(self):
+    """A string representation of the node's name."""
+    return pytree_utils.NodeName(self.node)
+
+  @property
+  def is_comment(self):
+    return self.node.type == token.COMMENT
+
+  @property
+  def is_continuation(self):
+    return self.node.type == CONTINUATION
+
+  @property
+  @py3compat.lru_cache()
+  def is_keyword(self):
+    return keyword.iskeyword(self.value)
+
+  @property
+  @py3compat.lru_cache()
+  def is_name(self):
+    return self.node.type == token.NAME and not self.is_keyword
+
+  @property
+  def is_number(self):
+    return self.node.type == token.NUMBER
+
+  @property
+  def is_string(self):
+    return self.node.type == token.STRING
+
+  @property
+  @py3compat.lru_cache()
+  def is_multiline_string(self):
+    return (self.is_string and
+            re.match(r'^[uUbB]?[rR]?(?P<delim>"""|\'\'\').*(?P=delim)$',
+                     self.value, re.DOTALL) is not None)
+
+  @property
+  @py3compat.lru_cache()
+  def is_docstring(self):
+    return self.is_multiline_string and not self.node.prev_sibling
+
+  @property
+  @py3compat.lru_cache()
+  def is_pseudo_paren(self):
+    return hasattr(self.node, 'is_pseudo') and self.node.is_pseudo
+
+  @property
+  def is_pylint_comment(self):
+    return self.is_comment and re.match(r'#.*\bpylint:\s*(disable|enable)=',
+                                        self.value)
diff --git a/src/tools/yapf/yapf/yapflib/line_joiner.py b/src/tools/yapf/yapf/yapflib/line_joiner.py
new file mode 100644
index 0000000..860fce7
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/line_joiner.py
@@ -0,0 +1,109 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Join unwrapped lines together.
+
+Determine how many lines can be joined into one line. For instance, we could
+join these statements into one line:
+
+  if a == 42:
+    continue
+
+like this:
+
+  if a == 42: continue
+
+There are a few restrictions:
+
+  1. The lines should have been joined in the original source.
+  2. The joined lines must not go over the column boundary if placed on the same
+     line.
+  3. They need to be very simple statements.
+
+Note: Because we don't allow the use of a semicolon to separate statements, it
+follows that there can only be at most two lines to join.
+"""
+
+from yapf.yapflib import style
+
+_CLASS_OR_FUNC = frozenset({'def', 'class'})
+
+
+def CanMergeMultipleLines(lines, last_was_merged=False):
+  """Determine if multiple lines can be joined into one.
+
+  Arguments:
+    lines: (list of UnwrappedLine) This is a splice of UnwrappedLines from the
+      full code base.
+    last_was_merged: (bool) The last line was merged.
+
+  Returns:
+    True if two consecutive lines can be joined together. In reality, this will
+    only happen if two consecutive lines can be joined, due to the style guide.
+  """
+  # The indentation amount for the starting line (number of spaces).
+  indent_amt = lines[0].depth * style.Get('INDENT_WIDTH')
+  if len(lines) == 1 or indent_amt > style.Get('COLUMN_LIMIT'):
+    return False
+
+  if (len(lines) >= 3 and lines[2].depth >= lines[1].depth and
+      lines[0].depth != lines[2].depth):
+    # If lines[2]'s depth is greater than or equal to line[1]'s depth, we're not
+    # looking at a single statement (e.g., if-then, while, etc.). A following
+    # line with the same depth as the first line isn't part of the lines we
+    # would want to combine.
+    return False  # Don't merge more than two lines together.
+
+  if lines[0].first.value in _CLASS_OR_FUNC:
+    # Don't join lines onto the starting line of a class or function.
+    return False
+
+  limit = style.Get('COLUMN_LIMIT') - indent_amt
+  if lines[0].last.total_length < limit:
+    limit -= lines[0].last.total_length
+
+    if lines[0].first.value == 'if':
+      return _CanMergeLineIntoIfStatement(lines, limit)
+    if last_was_merged and lines[0].first.value in {'elif', 'else'}:
+      return _CanMergeLineIntoIfStatement(lines, limit)
+
+  # TODO(morbo): Other control statements?
+
+  return False
+
+
+def _CanMergeLineIntoIfStatement(lines, limit):
+  """Determine if we can merge a short if-then statement into one line.
+
+  Two lines of an if-then statement can be merged if they were that way in the
+  original source, fit on the line without going over the column limit, and are
+  considered "simple" statements --- typically statements like 'pass',
+  'continue', and 'break'.
+
+  Arguments:
+    lines: (list of UnwrappedLine) The lines we are wanting to merge.
+    limit: (int) The amount of space remaining on the line.
+
+  Returns:
+    True if the lines can be merged, False otherwise.
+  """
+  if len(lines[1].tokens) == 1 and lines[1].last.is_multiline_string:
+    # This might be part of a multiline shebang.
+    return True
+  if lines[0].lineno != lines[1].lineno:
+    # Don't merge lines if the original lines weren't merged.
+    return False
+  if lines[1].last.total_length >= limit:
+    # Don't merge lines if the result goes over the column limit.
+    return False
+  return style.Get('JOIN_MULTIPLE_LINES')
diff --git a/src/tools/yapf/yapf/yapflib/py3compat.py b/src/tools/yapf/yapf/yapflib/py3compat.py
new file mode 100644
index 0000000..2886c38
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/py3compat.py
@@ -0,0 +1,113 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Utilities for Python2 / Python3 compatibility."""
+
+import io
+import os
+import sys
+
+PY3 = sys.version_info[0] >= 3
+PY36 = sys.version_info[0] >= 3 and sys.version_info[1] >= 6
+
+if PY3:
+  StringIO = io.StringIO
+  BytesIO = io.BytesIO
+
+  import codecs
+
+  def open_with_encoding(filename, mode, encoding, newline=''):  # pylint: disable=unused-argument
+    return codecs.open(filename, mode=mode, encoding=encoding)
+
+  import functools
+  lru_cache = functools.lru_cache
+
+  range = range
+  ifilter = filter
+  raw_input = input
+
+  import configparser
+
+  # Mappings from strings to booleans (such as '1' to True, 'false' to False,
+  # etc.)
+  CONFIGPARSER_BOOLEAN_STATES = configparser.ConfigParser.BOOLEAN_STATES
+else:
+  import __builtin__
+  import cStringIO
+  StringIO = BytesIO = cStringIO.StringIO
+
+  open_with_encoding = io.open
+
+  # Python 2.7 doesn't have a native LRU cache, so do nothing.
+  def lru_cache(maxsize=128, typed=False):
+
+    def fake_wrapper(user_function):
+      return user_function
+
+    return fake_wrapper
+
+  range = xrange
+
+  from itertools import ifilter
+  raw_input = raw_input
+
+  import ConfigParser as configparser
+  CONFIGPARSER_BOOLEAN_STATES = configparser.ConfigParser._boolean_states  # pylint: disable=protected-access
+
+
+def EncodeAndWriteToStdout(s, encoding='utf-8'):
+  """Encode the given string and emit to stdout.
+
+  The string may contain non-ascii characters. This is a problem when stdout is
+  redirected, because then Python doesn't know the encoding and we may get a
+  UnicodeEncodeError.
+
+  Arguments:
+    s: (string) The string to encode.
+    encoding: (string) The encoding of the string.
+  """
+  if PY3:
+    sys.stdout.buffer.write(s.encode(encoding))
+  elif sys.platform == 'win32':
+    # On python 2 and Windows universal newline transformation will be in
+    # effect on stdout. Python 2 will not let us avoid the easily because
+    # it happens based on whether the file handle is opened in O_BINARY or
+    # O_TEXT state. However we can tell Windows itself to change the current
+    # mode, and python 2 will follow suit. However we must take care to change
+    # the mode on the actual external stdout not just the current sys.stdout
+    # which may have been monkey-patched inside the python environment.
+    import msvcrt  # pylint: disable=g-import-not-at-top
+    if sys.__stdout__ is sys.stdout:
+      msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+    sys.stdout.write(s.encode(encoding))
+  else:
+    sys.stdout.write(s.encode(encoding))
+
+
+if PY3:
+  unicode = str  # pylint: disable=redefined-builtin,invalid-name
+else:
+
+  def unicode(s):  # pylint: disable=invalid-name
+    """Force conversion of s to unicode."""
+    return __builtin__.unicode(s, 'utf-8')
+
+
+# In Python 3.2+, readfp is deprecated in favor of read_file, which doesn't
+# exist in Python 2 yet. To avoid deprecation warnings, subclass ConfigParser to
+# fix this - now read_file works across all Python versions we care about.
+class ConfigParser(configparser.ConfigParser):
+  if not PY3:
+
+    def read_file(self, fp, source=None):
+      self.readfp(fp, filename=source)
diff --git a/src/tools/yapf/yapf/yapflib/pytree_unwrapper.py b/src/tools/yapf/yapf/yapflib/pytree_unwrapper.py
new file mode 100644
index 0000000..c67c1c6
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/pytree_unwrapper.py
@@ -0,0 +1,376 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTreeUnwrapper - produces a list of unwrapped lines from a pytree.
+
+[for a description of what an unwrapped line is, see unwrapped_line.py]
+
+This is a pytree visitor that goes over a parse tree and produces a list of
+UnwrappedLine containers from it, each with its own depth and containing all
+the tokens that could fit on the line if there were no maximal line-length
+limitations.
+
+Note: a precondition to running this visitor and obtaining correct results is
+for the tree to have its comments spliced in as nodes. Prefixes are ignored.
+
+For most uses, the convenience function UnwrapPyTree should be sufficient.
+"""
+
+# The word "token" is overloaded within this module, so for clarity rename
+# the imported pgen2.token module.
+from lib2to3 import pytree
+from lib2to3.pgen2 import token as grammar_token
+
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import pytree_visitor
+from yapf.yapflib import split_penalty
+from yapf.yapflib import unwrapped_line
+
+
+def UnwrapPyTree(tree):
+  """Create and return a list of unwrapped lines from the given pytree.
+
+  Arguments:
+    tree: the top-level pytree node to unwrap.
+
+  Returns:
+    A list of UnwrappedLine objects.
+  """
+  unwrapper = PyTreeUnwrapper()
+  unwrapper.Visit(tree)
+  uwlines = unwrapper.GetUnwrappedLines()
+  uwlines.sort(key=lambda x: x.lineno)
+  return uwlines
+
+
+# Grammar tokens considered as whitespace for the purpose of unwrapping.
+_WHITESPACE_TOKENS = frozenset([
+    grammar_token.NEWLINE, grammar_token.DEDENT, grammar_token.INDENT,
+    grammar_token.ENDMARKER
+])
+
+
+class PyTreeUnwrapper(pytree_visitor.PyTreeVisitor):
+  """PyTreeUnwrapper - see file-level docstring for detailed description.
+
+  Note: since this implements PyTreeVisitor and node names in lib2to3 are
+  underscore_separated, the visiting methods of this class are named as
+  Visit_node_name. invalid-name pragmas are added to each such method to silence
+  a style warning. This is forced on us by the usage of lib2to3, and re-munging
+  method names to make them different from actual node names sounded like a
+  confusing and brittle affair that wasn't worth it for this small & controlled
+  deviation from the style guide.
+
+  To understand the connection between visitor methods in this class, some
+  familiarity with the Python grammar is required.
+  """
+
+  def __init__(self):
+    # A list of all unwrapped lines finished visiting so far.
+    self._unwrapped_lines = []
+
+    # Builds up a "current" unwrapped line while visiting pytree nodes. Some
+    # nodes will finish a line and start a new one.
+    self._cur_unwrapped_line = unwrapped_line.UnwrappedLine(0)
+
+    # Current indentation depth.
+    self._cur_depth = 0
+
+  def GetUnwrappedLines(self):
+    """Fetch the result of the tree walk.
+
+    Note: only call this after visiting the whole tree.
+
+    Returns:
+      A list of UnwrappedLine objects.
+    """
+    # Make sure the last line that was being populated is flushed.
+    self._StartNewLine()
+    return self._unwrapped_lines
+
+  def _StartNewLine(self):
+    """Finish current line and start a new one.
+
+    Place the currently accumulated line into the _unwrapped_lines list and
+    start a new one.
+    """
+    if self._cur_unwrapped_line.tokens:
+      self._unwrapped_lines.append(self._cur_unwrapped_line)
+      _MatchBrackets(self._cur_unwrapped_line)
+      _AdjustSplitPenalty(self._cur_unwrapped_line)
+    self._cur_unwrapped_line = unwrapped_line.UnwrappedLine(self._cur_depth)
+
+  _STMT_TYPES = frozenset({
+      'if_stmt',
+      'while_stmt',
+      'for_stmt',
+      'try_stmt',
+      'expect_clause',
+      'with_stmt',
+      'funcdef',
+      'classdef',
+  })
+
+  # pylint: disable=invalid-name,missing-docstring
+  def Visit_simple_stmt(self, node):
+    # A 'simple_stmt' conveniently represents a non-compound Python statement,
+    # i.e. a statement that does not contain other statements.
+
+    # When compound nodes have a single statement as their suite, the parser
+    # can leave it in the tree directly without creating a suite. But we have
+    # to increase depth in these cases as well. However, don't increase the
+    # depth of we have a simple_stmt that's a comment node. This represents a
+    # standalone comment and in the case of it coming directly after the
+    # funcdef, it is a "top" comment for the whole function.
+    # TODO(eliben): add more relevant compound statements here.
+    single_stmt_suite = (node.parent and
+                         pytree_utils.NodeName(node.parent) in self._STMT_TYPES)
+    is_comment_stmt = pytree_utils.IsCommentStatement(node)
+    if single_stmt_suite and not is_comment_stmt:
+      self._cur_depth += 1
+    self._StartNewLine()
+    self.DefaultNodeVisit(node)
+    if single_stmt_suite and not is_comment_stmt:
+      self._cur_depth -= 1
+
+  def _VisitCompoundStatement(self, node, substatement_names):
+    """Helper for visiting compound statements.
+
+    Python compound statements serve as containers for other statements. Thus,
+    when we encounter a new compound statement we start a new unwrapped line.
+
+    Arguments:
+      node: the node to visit.
+      substatement_names: set of node names. A compound statement will be
+        recognized as a NAME node with a name in this set.
+    """
+    for child in node.children:
+      # A pytree is structured in such a way that a single 'if_stmt' node will
+      # contain all the 'if', 'elif' and 'else' nodes as children (similar
+      # structure applies to 'while' statements, 'try' blocks, etc). Therefore,
+      # we visit all children here and create a new line before the requested
+      # set of nodes.
+      if (child.type == grammar_token.NAME and
+          child.value in substatement_names):
+        self._StartNewLine()
+      self.Visit(child)
+
+  _IF_STMT_ELEMS = frozenset({'if', 'else', 'elif'})
+
+  def Visit_if_stmt(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._IF_STMT_ELEMS)
+
+  _WHILE_STMT_ELEMS = frozenset({'while', 'else'})
+
+  def Visit_while_stmt(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._WHILE_STMT_ELEMS)
+
+  _FOR_STMT_ELEMS = frozenset({'for', 'else'})
+
+  def Visit_for_stmt(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._FOR_STMT_ELEMS)
+
+  _TRY_STMT_ELEMS = frozenset({'try', 'except', 'else', 'finally'})
+
+  def Visit_try_stmt(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._TRY_STMT_ELEMS)
+
+  _EXCEPT_STMT_ELEMS = frozenset({'except'})
+
+  def Visit_except_clause(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._EXCEPT_STMT_ELEMS)
+
+  _FUNC_DEF_ELEMS = frozenset({'def'})
+
+  def Visit_funcdef(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._FUNC_DEF_ELEMS)
+
+  def Visit_async_funcdef(self, node):  # pylint: disable=invalid-name
+    self._StartNewLine()
+    index = 0
+    for child in node.children:
+      index += 1
+      self.Visit(child)
+      if pytree_utils.NodeName(child) == 'ASYNC':
+        break
+    for child in node.children[index].children:
+      self.Visit(child)
+
+  _CLASS_DEF_ELEMS = frozenset({'class'})
+
+  def Visit_classdef(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._CLASS_DEF_ELEMS)
+
+  def Visit_async_stmt(self, node):  # pylint: disable=invalid-name
+    self._StartNewLine()
+    index = 0
+    for child in node.children:
+      index += 1
+      self.Visit(child)
+      if pytree_utils.NodeName(child) == 'ASYNC':
+        break
+    for child in node.children[index].children:
+      self.Visit(child)
+
+  def Visit_decorators(self, node):  # pylint: disable=invalid-name
+    for child in node.children:
+      self._StartNewLine()
+      self.Visit(child)
+
+  def Visit_decorated(self, node):  # pylint: disable=invalid-name
+    for child in node.children:
+      self._StartNewLine()
+      self.Visit(child)
+
+  _WITH_STMT_ELEMS = frozenset({'with'})
+
+  def Visit_with_stmt(self, node):  # pylint: disable=invalid-name
+    self._VisitCompoundStatement(node, self._WITH_STMT_ELEMS)
+
+  def Visit_suite(self, node):  # pylint: disable=invalid-name
+    # A 'suite' starts a new indentation level in Python.
+    self._cur_depth += 1
+    self._StartNewLine()
+    self.DefaultNodeVisit(node)
+    self._cur_depth -= 1
+
+  def Visit_listmaker(self, node):  # pylint: disable=invalid-name
+    _DetermineMustSplitAnnotation(node)
+    self.DefaultNodeVisit(node)
+
+  def Visit_dictsetmaker(self, node):  # pylint: disable=invalid-name
+    _DetermineMustSplitAnnotation(node)
+    self.DefaultNodeVisit(node)
+
+  def Visit_import_as_names(self, node):  # pylint: disable=invalid-name
+    if node.prev_sibling.value == '(':
+      _DetermineMustSplitAnnotation(node)
+    self.DefaultNodeVisit(node)
+
+  def Visit_testlist_gexp(self, node):  # pylint: disable=invalid-name
+    if _ContainsComments(node):
+      _DetermineMustSplitAnnotation(node)
+    self.DefaultNodeVisit(node)
+
+  def Visit_arglist(self, node):  # pylint: disable=invalid-name
+    _DetermineMustSplitAnnotation(node)
+    self.DefaultNodeVisit(node)
+
+  def Visit_typedargslist(self, node):  # pylint: disable=invalid-name
+    _DetermineMustSplitAnnotation(node)
+    self.DefaultNodeVisit(node)
+
+  def DefaultLeafVisit(self, leaf):
+    """Default visitor for tree leaves.
+
+    A tree leaf is always just gets appended to the current unwrapped line.
+
+    Arguments:
+      leaf: the leaf to visit.
+    """
+    if leaf.type in _WHITESPACE_TOKENS:
+      self._StartNewLine()
+    elif leaf.type != grammar_token.COMMENT or leaf.value.strip():
+      if leaf.value == ';':
+        # Split up multiple statements on one line.
+        self._StartNewLine()
+      else:
+        # Add non-whitespace tokens and comments that aren't empty.
+        self._cur_unwrapped_line.AppendNode(leaf)
+
+
+_BRACKET_MATCH = {')': '(', '}': '{', ']': '['}
+
+
+def _MatchBrackets(uwline):
+  """Visit the node and match the brackets.
+
+  For every open bracket ('[', '{', or '('), find the associated closing bracket
+  and "match" them up. I.e., save in the token a pointer to its associated open
+  or close bracket.
+
+  Arguments:
+    uwline: (UnwrappedLine) An unwrapped line.
+  """
+  bracket_stack = []
+  for token in uwline.tokens:
+    if token.value in pytree_utils.OPENING_BRACKETS:
+      bracket_stack.append(token)
+    elif token.value in pytree_utils.CLOSING_BRACKETS:
+      bracket_stack[-1].matching_bracket = token
+      token.matching_bracket = bracket_stack[-1]
+      bracket_stack.pop()
+
+
+def _AdjustSplitPenalty(uwline):
+  """Visit the node and adjust the split penalties if needed.
+
+  A token shouldn't be split if it's not within a bracket pair. Mark any token
+  that's not within a bracket pair as "unbreakable".
+
+  Arguments:
+    uwline: (UnwrappedLine) An unwrapped line.
+  """
+  bracket_level = 0
+  for index, token in enumerate(uwline.tokens):
+    if index and not bracket_level:
+      pytree_utils.SetNodeAnnotation(token.node,
+                                     pytree_utils.Annotation.SPLIT_PENALTY,
+                                     split_penalty.UNBREAKABLE)
+    if token.value in pytree_utils.OPENING_BRACKETS:
+      bracket_level += 1
+    elif token.value in pytree_utils.CLOSING_BRACKETS:
+      bracket_level -= 1
+
+
+def _DetermineMustSplitAnnotation(node):
+  """Enforce a split in the list if the list ends with a comma."""
+  if not _ContainsComments(node):
+    if (not isinstance(node.children[-1], pytree.Leaf) or
+        node.children[-1].value != ','):
+      return
+  num_children = len(node.children)
+  index = 0
+  _SetMustSplitOnFirstLeaf(node.children[0])
+  while index < num_children - 1:
+    child = node.children[index]
+    if isinstance(child, pytree.Leaf) and child.value == ',':
+      next_child = node.children[index + 1]
+      if next_child.type == grammar_token.COMMENT:
+        index += 1
+        if index >= num_children - 1:
+          break
+      _SetMustSplitOnFirstLeaf(node.children[index + 1])
+    index += 1
+
+
+def _ContainsComments(node):
+  """Return True if the list has a comment in it."""
+  if isinstance(node, pytree.Leaf):
+    return node.type == grammar_token.COMMENT
+  for child in node.children:
+    if _ContainsComments(child):
+      return True
+  return False
+
+
+def _SetMustSplitOnFirstLeaf(node):
+  """Set the "must split" annotation on the first leaf node."""
+
+  def FindFirstLeaf(node):
+    if isinstance(node, pytree.Leaf):
+      return node
+    return FindFirstLeaf(node.children[0])
+
+  pytree_utils.SetNodeAnnotation(
+      FindFirstLeaf(node), pytree_utils.Annotation.MUST_SPLIT, True)
diff --git a/src/tools/yapf/yapf/yapflib/pytree_utils.py b/src/tools/yapf/yapf/yapflib/pytree_utils.py
new file mode 100644
index 0000000..60fd955
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/pytree_utils.py
@@ -0,0 +1,297 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""pytree-related utilities.
+
+This module collects various utilities related to the parse trees produced by
+the lib2to3 library.
+
+  NodeName(): produces a string name for pytree nodes.
+  ParseCodeToTree(): convenience wrapper around lib2to3 interfaces to parse
+                     a given string with code to a pytree.
+  InsertNodeBefore(): insert a node before another in a pytree.
+  InsertNodeAfter(): insert a node after another in a pytree.
+  {Get,Set}NodeAnnotation(): manage custom annotations on pytree nodes.
+"""
+
+import ast
+from lib2to3 import pygram
+from lib2to3 import pytree
+from lib2to3.pgen2 import driver
+from lib2to3.pgen2 import parse
+from lib2to3.pgen2 import token
+
+# TODO(eliben): We may want to get rid of this filtering at some point once we
+# have a better understanding of what information we need from the tree. Then,
+# these tokens may be filtered out from the tree before the tree gets to the
+# unwrapper.
+NONSEMANTIC_TOKENS = frozenset(['DEDENT', 'INDENT', 'NEWLINE', 'ENDMARKER'])
+
+OPENING_BRACKETS = frozenset({'(', '[', '{'})
+CLOSING_BRACKETS = frozenset({')', ']', '}'})
+
+
+class Annotation(object):
+  """Annotation names associated with pytrees."""
+  CHILD_INDENT = 'child_indent'
+  NEWLINES = 'newlines'
+  MUST_SPLIT = 'must_split'
+  SPLIT_PENALTY = 'split_penalty'
+  SUBTYPE = 'subtype'
+
+
+def NodeName(node):
+  """Produce a string name for a given node.
+
+  For a Leaf this is the token name, and for a Node this is the type.
+
+  Arguments:
+    node: a tree node
+
+  Returns:
+    Name as a string.
+  """
+  # Nodes with values < 256 are tokens. Values >= 256 are grammar symbols.
+  if node.type < 256:
+    return token.tok_name[node.type]
+  else:
+    return pygram.python_grammar.number2symbol[node.type]
+
+
+# lib2to3 thoughtfully provides pygram.python_grammar_no_print_statement for
+# parsing Python 3 code that wouldn't parse otherwise (when 'print' is used in a
+# context where a keyword is disallowed).
+# It forgets to do the same for 'exec' though. Luckily, Python is amenable to
+# monkey-patching.
+_GRAMMAR_FOR_PY3 = pygram.python_grammar_no_print_statement.copy()
+del _GRAMMAR_FOR_PY3.keywords['exec']
+
+_GRAMMAR_FOR_PY2 = pygram.python_grammar.copy()
+del _GRAMMAR_FOR_PY2.keywords['nonlocal']
+
+
+def ParseCodeToTree(code):
+  """Parse the given code to a lib2to3 pytree.
+
+  Arguments:
+    code: a string with the code to parse.
+
+  Raises:
+    SyntaxError if the code is invalid syntax.
+    parse.ParseError if some other parsing failure.
+
+  Returns:
+    The root node of the parsed tree.
+  """
+  # This function is tiny, but the incantation for invoking the parser correctly
+  # is sufficiently magical to be worth abstracting away.
+  try:
+    # Try to parse using a Python 3 grammar, which is more permissive (print and
+    # exec are not keywords).
+    parser_driver = driver.Driver(_GRAMMAR_FOR_PY3, convert=pytree.convert)
+    tree = parser_driver.parse_string(code, debug=False)
+  except parse.ParseError:
+    # Now try to parse using a Python 2 grammar; If this fails, then
+    # there's something else wrong with the code.
+    try:
+      parser_driver = driver.Driver(_GRAMMAR_FOR_PY2, convert=pytree.convert)
+      tree = parser_driver.parse_string(code, debug=False)
+    except parse.ParseError:
+      # Raise a syntax error if the code is invalid python syntax.
+      try:
+        ast.parse(code)
+      except SyntaxError as e:
+        raise e
+      else:
+        raise
+  return _WrapEndMarker(tree)
+
+
+def _WrapEndMarker(tree):
+  """Wrap a single ENDMARKER token in a "file_input" node.
+
+  Arguments:
+    tree: (pytree.Node) The root node of the parsed tree.
+
+  Returns:
+    The root node of the parsed tree. If the tree is a single ENDMARKER node,
+    then that node is wrapped in a "file_input" node. That will ensure we don't
+    skip comments attached to that node.
+  """
+  if isinstance(tree, pytree.Leaf) and tree.type == token.ENDMARKER:
+    return pytree.Node(pygram.python_symbols.file_input, [tree])
+  return tree
+
+
+def InsertNodesBefore(new_nodes, target):
+  """Insert new_nodes before the given target location in the tree.
+
+  Arguments:
+    new_nodes: a sequence of new nodes to insert (the nodes should not be in the
+      tree).
+    target: the target node before which the new node node will be inserted.
+
+  Raises:
+    RuntimeError: if the tree is corrupted, or the insertion would corrupt it.
+  """
+  for node in new_nodes:
+    _InsertNodeAt(node, target, after=False)
+
+
+def InsertNodesAfter(new_nodes, target):
+  """Insert new_nodes after the given target location in the tree.
+
+  Arguments:
+    new_nodes: a sequence of new nodes to insert (the nodes should not be in the
+      tree).
+    target: the target node after which the new node node will be inserted.
+
+  Raises:
+    RuntimeError: if the tree is corrupted, or the insertion would corrupt it.
+  """
+  for node in reversed(new_nodes):
+    _InsertNodeAt(node, target, after=True)
+
+
+def _InsertNodeAt(new_node, target, after=False):
+  """Underlying implementation for node insertion.
+
+  Arguments:
+    new_node: a new node to insert (this node should not be in the tree).
+    target: the target node.
+    after: if True, new_node is inserted after target. Otherwise, it's inserted
+      before target.
+
+  Returns:
+    nothing
+
+  Raises:
+    RuntimeError: if the tree is corrupted, or the insertion would corrupt it.
+  """
+
+  # Protect against attempts to insert nodes which already belong to some tree.
+  if new_node.parent is not None:
+    raise RuntimeError('inserting node which already has a parent',
+                       (new_node, new_node.parent))
+
+  # The code here is based on pytree.Base.next_sibling
+  parent_of_target = target.parent
+  if parent_of_target is None:
+    raise RuntimeError('expected target node to have a parent', (target,))
+
+  for i, child in enumerate(parent_of_target.children):
+    if child is target:
+      insertion_index = i + 1 if after else i
+      parent_of_target.insert_child(insertion_index, new_node)
+      return
+
+  raise RuntimeError('unable to find insertion point for target node',
+                     (target,))
+
+
+# The following constant and functions implement a simple custom annotation
+# mechanism for pytree nodes. We attach new attributes to nodes. Each attribute
+# is prefixed with _NODE_ANNOTATION_PREFIX. These annotations should only be
+# managed through GetNodeAnnotation and SetNodeAnnotation.
+_NODE_ANNOTATION_PREFIX = '_yapf_annotation_'
+
+
+def GetNodeAnnotation(node, annotation, default=None):
+  """Get annotation value from a node.
+
+  Arguments:
+    node: the node.
+    annotation: annotation name - a string.
+    default: the default value to return if there's no annotation.
+
+  Returns:
+    Value of the annotation in the given node. If the node doesn't have this
+    particular annotation name yet, returns default.
+  """
+  return getattr(node, _NODE_ANNOTATION_PREFIX + annotation, default)
+
+
+def SetNodeAnnotation(node, annotation, value):
+  """Set annotation value on a node.
+
+  Arguments:
+    node: the node.
+    annotation: annotation name - a string.
+    value: annotation value to set.
+  """
+  setattr(node, _NODE_ANNOTATION_PREFIX + annotation, value)
+
+
+def AppendNodeAnnotation(node, annotation, value):
+  """Appends an annotation value to a list of annotations on the node.
+
+  Arguments:
+    node: the node.
+    annotation: annotation name - a string.
+    value: annotation value to set.
+  """
+  attr = GetNodeAnnotation(node, annotation, set())
+  attr.add(value)
+  SetNodeAnnotation(node, annotation, attr)
+
+
+def RemoveSubtypeAnnotation(node, value):
+  """Removes an annotation value from the subtype annotations on the node.
+
+  Arguments:
+    node: the node.
+    value: annotation value to remove.
+  """
+  attr = GetNodeAnnotation(node, Annotation.SUBTYPE)
+  if attr and value in attr:
+    attr.remove(value)
+    SetNodeAnnotation(node, Annotation.SUBTYPE, attr)
+
+
+def DumpNodeToString(node):
+  """Dump a string representation of the given node. For debugging.
+
+  Arguments:
+    node: the node.
+
+  Returns:
+    The string representation.
+  """
+  if isinstance(node, pytree.Leaf):
+    fmt = '{name}({value}) [lineno={lineno}, column={column}, prefix={prefix}]'
+    return fmt.format(
+        name=NodeName(node),
+        value=_PytreeNodeRepr(node),
+        lineno=node.lineno,
+        column=node.column,
+        prefix=repr(node.prefix))
+  else:
+    fmt = '{node} [{len} children] [child_indent="{indent}"]'
+    return fmt.format(
+        node=NodeName(node),
+        len=len(node.children),
+        indent=GetNodeAnnotation(node, Annotation.CHILD_INDENT))
+
+
+def _PytreeNodeRepr(node):
+  """Like pytree.Node.__repr__, but names instead of numbers for tokens."""
+  if isinstance(node, pytree.Node):
+    return '%s(%s, %r)' % (node.__class__.__name__, NodeName(node),
+                           [_PytreeNodeRepr(c) for c in node.children])
+  if isinstance(node, pytree.Leaf):
+    return '%s(%s, %r)' % (node.__class__.__name__, NodeName(node), node.value)
+
+
+def IsCommentStatement(node):
+  return (NodeName(node) == 'simple_stmt' and
+          node.children[0].type == token.COMMENT)
diff --git a/src/tools/yapf/yapf/yapflib/pytree_visitor.py b/src/tools/yapf/yapf/yapflib/pytree_visitor.py
new file mode 100644
index 0000000..3f1ab0b
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/pytree_visitor.py
@@ -0,0 +1,135 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generic visitor pattern for pytrees.
+
+The lib2to3 parser produces a "pytree" - syntax tree consisting of Node
+and Leaf types. This module implements a visitor pattern for such trees.
+
+It also exports a basic "dumping" visitor that dumps a textual representation of
+a pytree into a stream.
+
+  PyTreeVisitor: a generic visitor pattern fo pytrees.
+  PyTreeDumper: a configurable "dumper" for displaying pytrees.
+  DumpPyTree(): a convenience function to dump a pytree.
+"""
+
+import sys
+
+from lib2to3 import pytree
+
+from yapf.yapflib import pytree_utils
+
+
+class PyTreeVisitor(object):
+  """Visitor pattern for pytree trees.
+
+  Methods named Visit_XXX will be invoked when a node with type XXX is
+  encountered in the tree. The type is either a token type (for Leaf nodes) or
+  grammar symbols (for Node nodes). The return value of Visit_XXX methods is
+  ignored by the visitor.
+
+  Visitors can modify node contents but must not change the tree structure
+  (e.g. add/remove children and move nodes around).
+
+  This is a very common visitor pattern in Python code; it's also used in the
+  Python standard library ast module for providing AST visitors.
+
+  Note: this makes names that aren't style conformant, so such visitor methods
+  need to be marked with # pylint: disable=invalid-name We don't have a choice
+  here, because lib2to3 nodes have under_separated names.
+
+  For more complex behavior, the visit, DefaultNodeVisit and DefaultLeafVisit
+  methods can be overridden. Don't forget to invoke DefaultNodeVisit for nodes
+  that may have children - otherwise the children will not be visited.
+  """
+
+  def Visit(self, node):
+    """Visit a node."""
+    method = 'Visit_{0}'.format(pytree_utils.NodeName(node))
+    if hasattr(self, method):
+      # Found a specific visitor for this node
+      getattr(self, method)(node)
+    else:
+      if isinstance(node, pytree.Leaf):
+        self.DefaultLeafVisit(node)
+      else:
+        self.DefaultNodeVisit(node)
+
+  def DefaultNodeVisit(self, node):
+    """Default visitor for Node: visits the node's children depth-first.
+
+    This method is invoked when no specific visitor for the node is defined.
+
+    Arguments:
+      node: the node to visit
+    """
+    for child in node.children:
+      self.Visit(child)
+
+  def DefaultLeafVisit(self, leaf):
+    """Default visitor for Leaf: no-op.
+
+    This method is invoked when no specific visitor for the leaf is defined.
+
+    Arguments:
+      leaf: the leaf to visit
+    """
+    pass
+
+
+def DumpPyTree(tree, target_stream=sys.stdout):
+  """Convenience function for dumping a given pytree.
+
+  This function presents a very minimal interface. For more configurability (for
+  example, controlling how specific node types are displayed), use PyTreeDumper
+  directly.
+
+  Arguments:
+    tree: the tree to dump.
+    target_stream: the stream to dump the tree to. A file-like object. By
+      default will dump into stdout.
+  """
+  dumper = PyTreeDumper(target_stream)
+  dumper.Visit(tree)
+
+
+class PyTreeDumper(PyTreeVisitor):
+  """Visitor that dumps the tree to a stream.
+
+  Implements the PyTreeVisitor interface.
+  """
+
+  def __init__(self, target_stream=sys.stdout):
+    """Create a tree dumper.
+
+    Arguments:
+      target_stream: the stream to dump the tree to. A file-like object. By
+        default will dump into stdout.
+    """
+    self._target_stream = target_stream
+    self._current_indent = 0
+
+  def _DumpString(self, s):
+    self._target_stream.write('{0}{1}\n'.format(' ' * self._current_indent, s))
+
+  def DefaultNodeVisit(self, node):
+    # Dump information about the current node, and then use the generic
+    # DefaultNodeVisit visitor to dump each of its children.
+    self._DumpString(pytree_utils.DumpNodeToString(node))
+    self._current_indent += 2
+    super(PyTreeDumper, self).DefaultNodeVisit(node)
+    self._current_indent -= 2
+
+  def DefaultLeafVisit(self, leaf):
+    self._DumpString(pytree_utils.DumpNodeToString(leaf))
diff --git a/src/tools/yapf/yapf/yapflib/reformatter.py b/src/tools/yapf/yapf/yapflib/reformatter.py
new file mode 100644
index 0000000..fd8f317
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/reformatter.py
@@ -0,0 +1,588 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Decide what the format for the code should be.
+
+The `unwrapped_line.UnwrappedLine`s are now ready to be formatted.
+UnwrappedLines that can be merged together are. The best formatting is returned
+as a string.
+
+  Reformat(): the main function exported by this module.
+"""
+
+from __future__ import unicode_literals
+import collections
+import heapq
+import re
+
+from lib2to3 import pytree
+from lib2to3.pgen2 import token
+
+from yapf.yapflib import format_decision_state
+from yapf.yapflib import format_token
+from yapf.yapflib import line_joiner
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import style
+from yapf.yapflib import verifier
+
+
+def Reformat(uwlines, verify=False):
+  """Reformat the unwrapped lines.
+
+  Arguments:
+    uwlines: (list of unwrapped_line.UnwrappedLine) Lines we want to format.
+    verify: (bool) True if reformatted code should be verified for syntax.
+
+  Returns:
+    A string representing the reformatted code.
+  """
+  final_lines = []
+  prev_uwline = None  # The previous line.
+  indent_width = style.Get('INDENT_WIDTH')
+
+  for uwline in _SingleOrMergedLines(uwlines):
+    first_token = uwline.first
+    _FormatFirstToken(first_token, uwline.depth, prev_uwline, final_lines)
+
+    indent_amt = indent_width * uwline.depth
+    state = format_decision_state.FormatDecisionState(uwline, indent_amt)
+    state.MoveStateToNextToken()
+
+    if not uwline.disable:
+      if uwline.first.is_comment:
+        uwline.first.node.value = uwline.first.node.value.rstrip()
+      elif uwline.last.is_comment:
+        uwline.last.node.value = uwline.last.node.value.rstrip()
+      if prev_uwline and prev_uwline.disable:
+        # Keep the vertical spacing between a disabled and enabled formatting
+        # region.
+        _RetainVerticalSpacingBetweenTokens(uwline.first, prev_uwline.last)
+      if any(tok.is_comment for tok in uwline.tokens):
+        _RetainVerticalSpacingBeforeComments(uwline)
+
+    if (_LineContainsI18n(uwline) or uwline.disable or
+        _LineHasContinuationMarkers(uwline)):
+      _RetainHorizontalSpacing(uwline)
+      _RetainVerticalSpacing(uwline, prev_uwline)
+      _EmitLineUnformatted(state)
+    elif _CanPlaceOnSingleLine(uwline) and not any(tok.must_split
+                                                   for tok in uwline.tokens):
+      # The unwrapped line fits on one line.
+      while state.next_token:
+        state.AddTokenToState(newline=False, dry_run=False)
+    else:
+      if not _AnalyzeSolutionSpace(state):
+        # Failsafe mode. If there isn't a solution to the line, then just emit
+        # it as is.
+        state = format_decision_state.FormatDecisionState(uwline, indent_amt)
+        state.MoveStateToNextToken()
+        _RetainHorizontalSpacing(uwline)
+        _RetainVerticalSpacing(uwline, prev_uwline)
+        _EmitLineUnformatted(state)
+
+    final_lines.append(uwline)
+    prev_uwline = uwline
+  return _FormatFinalLines(final_lines, verify)
+
+
+def _RetainHorizontalSpacing(uwline):
+  """Retain all horizontal spacing between tokens."""
+  for tok in uwline.tokens:
+    tok.RetainHorizontalSpacing(uwline.first.column, uwline.depth)
+
+
+def _RetainVerticalSpacing(cur_uwline, prev_uwline):
+  prev_tok = None
+  if prev_uwline is not None:
+    prev_tok = prev_uwline.last
+  for cur_tok in cur_uwline.tokens:
+    _RetainVerticalSpacingBetweenTokens(cur_tok, prev_tok)
+    prev_tok = cur_tok
+
+
+def _RetainVerticalSpacingBetweenTokens(cur_tok, prev_tok):
+  """Retain vertical spacing between two tokens."""
+  if prev_tok is None:
+    return
+
+  if prev_tok.is_string:
+    prev_lineno = prev_tok.lineno + prev_tok.value.count('\n')
+  elif prev_tok.is_pseudo_paren:
+    if not prev_tok.previous_token.is_multiline_string:
+      prev_lineno = prev_tok.previous_token.lineno
+    else:
+      prev_lineno = prev_tok.lineno
+  else:
+    prev_lineno = prev_tok.lineno
+
+  if cur_tok.is_comment:
+    cur_lineno = cur_tok.lineno - cur_tok.value.count('\n')
+  else:
+    cur_lineno = cur_tok.lineno
+
+  cur_tok.AdjustNewlinesBefore(cur_lineno - prev_lineno)
+
+
+def _RetainVerticalSpacingBeforeComments(uwline):
+  """Retain vertical spacing before comments."""
+  prev_token = None
+  for tok in uwline.tokens:
+    if tok.is_comment and prev_token:
+      if tok.lineno - tok.value.count('\n') - prev_token.lineno > 1:
+        tok.AdjustNewlinesBefore(ONE_BLANK_LINE)
+
+    prev_token = tok
+
+
+def _EmitLineUnformatted(state):
+  """Emit the line without formatting.
+
+  The line contains code that if reformatted would break a non-syntactic
+  convention. E.g., i18n comments and function calls are tightly bound by
+  convention. Instead, we calculate when / if a newline should occur and honor
+  that. But otherwise the code emitted will be the same as the original code.
+
+  Arguments:
+    state: (format_decision_state.FormatDecisionState) The format decision
+      state.
+  """
+  prev_lineno = None
+  while state.next_token:
+    previous_token = state.next_token.previous_token
+    previous_lineno = previous_token.lineno
+
+    if previous_token.is_multiline_string:
+      previous_lineno += previous_token.value.count('\n')
+
+    if previous_token.is_continuation:
+      newline = False
+    else:
+      newline = (prev_lineno is not None and
+                 state.next_token.lineno > previous_lineno)
+
+    prev_lineno = state.next_token.lineno
+    state.AddTokenToState(newline=newline, dry_run=False)
+
+
+def _LineContainsI18n(uwline):
+  """Return true if there are i18n comments or function calls in the line.
+
+  I18n comments and pseudo-function calls are closely related. They cannot
+  be moved apart without breaking i18n.
+
+  Arguments:
+    uwline: (unwrapped_line.UnwrappedLine) The line currently being formatted.
+
+  Returns:
+    True if the line contains i18n comments or function calls. False otherwise.
+  """
+  if style.Get('I18N_COMMENT'):
+    for tok in uwline.tokens:
+      if tok.is_comment and re.match(style.Get('I18N_COMMENT'), tok.value):
+        # Contains an i18n comment.
+        return True
+
+  if style.Get('I18N_FUNCTION_CALL'):
+    length = len(uwline.tokens)
+    index = 0
+    while index < length - 1:
+      if (uwline.tokens[index + 1].value == '(' and
+          uwline.tokens[index].value in style.Get('I18N_FUNCTION_CALL')):
+        return True
+      index += 1
+
+  return False
+
+
+def _LineHasContinuationMarkers(uwline):
+  """Return true if the line has continuation markers in it."""
+  return any(tok.is_continuation for tok in uwline.tokens)
+
+
+def _CanPlaceOnSingleLine(uwline):
+  """Determine if the unwrapped line can go on a single line.
+
+  Arguments:
+    uwline: (unwrapped_line.UnwrappedLine) The line currently being formatted.
+
+  Returns:
+    True if the line can or should be added to a single line. False otherwise.
+  """
+  indent_amt = style.Get('INDENT_WIDTH') * uwline.depth
+  last = uwline.last
+  last_index = -1
+  if last.is_pylint_comment:
+    last = last.previous_token
+    last_index = -2
+  if last is None:
+    return True
+  return (last.total_length + indent_amt <= style.Get('COLUMN_LIMIT') and
+          not any(tok.is_comment for tok in uwline.tokens[:last_index]))
+
+
+def _FormatFinalLines(final_lines, verify):
+  """Compose the final output from the finalized lines."""
+  formatted_code = []
+  for line in final_lines:
+    formatted_line = []
+    for tok in line.tokens:
+      if not tok.is_pseudo_paren:
+        formatted_line.append(tok.whitespace_prefix)
+        formatted_line.append(tok.value)
+      else:
+        if (not tok.next_token.whitespace_prefix.startswith('\n') and
+            not tok.next_token.whitespace_prefix.startswith(' ')):
+          if (tok.previous_token.value == ':' or
+              tok.next_token.value not in ',}])'):
+            formatted_line.append(' ')
+
+    formatted_code.append(''.join(formatted_line))
+    if verify:
+      verifier.VerifyCode(formatted_code[-1])
+
+  return ''.join(formatted_code) + '\n'
+
+
+class _StateNode(object):
+  """An edge in the solution space from 'previous.state' to 'state'.
+
+  Attributes:
+    state: (format_decision_state.FormatDecisionState) The format decision state
+      for this node.
+    newline: If True, then on the edge from 'previous.state' to 'state' a
+      newline is inserted.
+    previous: (_StateNode) The previous state node in the graph.
+  """
+
+  # TODO(morbo): Add a '__cmp__' method.
+
+  def __init__(self, state, newline, previous):
+    self.state = state.Clone()
+    self.newline = newline
+    self.previous = previous
+
+  def __repr__(self):  # pragma: no cover
+    return 'StateNode(state=[\n{0}\n], newline={1})'.format(
+        self.state, self.newline)
+
+
+# A tuple of (penalty, count) that is used to prioritize the BFS. In case of
+# equal penalties, we prefer states that were inserted first. During state
+# generation, we make sure that we insert states first that break the line as
+# late as possible.
+_OrderedPenalty = collections.namedtuple('OrderedPenalty', ['penalty', 'count'])
+
+# An item in the prioritized BFS search queue. The 'StateNode's 'state' has
+# the given '_OrderedPenalty'.
+_QueueItem = collections.namedtuple('QueueItem',
+                                    ['ordered_penalty', 'state_node'])
+
+
+def _AnalyzeSolutionSpace(initial_state):
+  """Analyze the entire solution space starting from initial_state.
+
+  This implements a variant of Dijkstra's algorithm on the graph that spans
+  the solution space (LineStates are the nodes). The algorithm tries to find
+  the shortest path (the one with the lowest penalty) from 'initial_state' to
+  the state where all tokens are placed.
+
+  Arguments:
+    initial_state: (format_decision_state.FormatDecisionState) The initial state
+      to start the search from.
+
+  Returns:
+    True if a formatting solution was found. False otherwise.
+  """
+  count = 0
+  seen = set()
+  p_queue = []
+
+  # Insert start element.
+  node = _StateNode(initial_state, False, None)
+  heapq.heappush(p_queue, _QueueItem(_OrderedPenalty(0, count), node))
+
+  count += 1
+  while p_queue:
+    item = p_queue[0]
+    penalty = item.ordered_penalty.penalty
+    node = item.state_node
+    if not node.state.next_token:
+      break
+    heapq.heappop(p_queue)
+
+    if count > 10000:
+      node.state.ignore_stack_for_comparison = True
+
+    if node.state in seen:
+      continue
+
+    seen.add(node.state)
+
+    # FIXME(morbo): Add a 'decision' element?
+
+    count = _AddNextStateToQueue(penalty, node, False, count, p_queue)
+    count = _AddNextStateToQueue(penalty, node, True, count, p_queue)
+
+  if not p_queue:
+    # We weren't able to find a solution. Do nothing.
+    return False
+
+  _ReconstructPath(initial_state, heapq.heappop(p_queue).state_node)
+  return True
+
+
+def _AddNextStateToQueue(penalty, previous_node, newline, count, p_queue):
+  """Add the following state to the analysis queue.
+
+  Assume the current state is 'previous_node' and has been reached with a
+  penalty of 'penalty'. Insert a line break if 'newline' is True.
+
+  Arguments:
+    penalty: (int) The penalty associated with the path up to this point.
+    previous_node: (_StateNode) The last _StateNode inserted into the priority
+      queue.
+    newline: (bool) Add a newline if True.
+    count: (int) The number of elements in the queue.
+    p_queue: (heapq) The priority queue representing the solution space.
+
+  Returns:
+    The updated number of elements in the queue.
+  """
+  must_split = previous_node.state.MustSplit()
+  if newline and not previous_node.state.CanSplit(must_split):
+    # Don't add a newline if the token cannot be split.
+    return count
+  if not newline and must_split:
+    # Don't add a token we must split but where we aren't splitting.
+    return count
+
+  node = _StateNode(previous_node.state, newline, previous_node)
+  penalty += node.state.AddTokenToState(
+      newline=newline, dry_run=True, must_split=must_split)
+  heapq.heappush(p_queue, _QueueItem(_OrderedPenalty(penalty, count), node))
+  return count + 1
+
+
+def _ReconstructPath(initial_state, current):
+  """Reconstruct the path through the queue with lowest penalty.
+
+  Arguments:
+    initial_state: (format_decision_state.FormatDecisionState) The initial state
+      to start the search from.
+    current: (_StateNode) The node in the decision graph that is the end point
+      of the path with the least penalty.
+  """
+  path = collections.deque()
+
+  while current.previous:
+    path.appendleft(current)
+    current = current.previous
+
+  for node in path:
+    initial_state.AddTokenToState(newline=node.newline, dry_run=False)
+
+
+def _FormatFirstToken(first_token, indent_depth, prev_uwline, final_lines):
+  """Format the first token in the unwrapped line.
+
+  Add a newline and the required indent before the first token of the unwrapped
+  line.
+
+  Arguments:
+    first_token: (format_token.FormatToken) The first token in the unwrapped
+      line.
+    indent_depth: (int) The line's indentation depth.
+    prev_uwline: (list of unwrapped_line.UnwrappedLine) The unwrapped line
+      previous to this line.
+    final_lines: (list of unwrapped_line.UnwrappedLine) The unwrapped lines
+      that have already been processed.
+  """
+  first_token.AddWhitespacePrefix(
+      _CalculateNumberOfNewlines(first_token, indent_depth, prev_uwline,
+                                 final_lines),
+      indent_level=indent_depth)
+
+
+NO_BLANK_LINES = 1
+ONE_BLANK_LINE = 2
+TWO_BLANK_LINES = 3
+
+
+def _CalculateNumberOfNewlines(first_token, indent_depth, prev_uwline,
+                               final_lines):
+  """Calculate the number of newlines we need to add.
+
+  Arguments:
+    first_token: (format_token.FormatToken) The first token in the unwrapped
+      line.
+    indent_depth: (int) The line's indentation depth.
+    prev_uwline: (list of unwrapped_line.UnwrappedLine) The unwrapped line
+      previous to this line.
+    final_lines: (list of unwrapped_line.UnwrappedLine) The unwrapped lines
+      that have already been processed.
+
+  Returns:
+    The number of newlines needed before the first token.
+  """
+  # TODO(morbo): Special handling for imports.
+  # TODO(morbo): Create a knob that can tune these.
+  if prev_uwline is None:
+    # The first line in the file. Don't add blank lines.
+    # FIXME(morbo): Is this correct?
+    if first_token.newlines is not None:
+      pytree_utils.SetNodeAnnotation(first_token.node,
+                                     pytree_utils.Annotation.NEWLINES, None)
+    return 0
+
+  if first_token.is_docstring:
+    if (prev_uwline.first.value == 'class' and
+        style.Get('BLANK_LINE_BEFORE_CLASS_DOCSTRING')):
+      # Enforce a blank line before a class's docstring.
+      return ONE_BLANK_LINE
+    # The docstring shouldn't have a newline before it.
+    return NO_BLANK_LINES
+
+  prev_last_token = prev_uwline.last
+  if prev_last_token.is_docstring:
+    if (not indent_depth and first_token.value in {'class', 'def', 'async'}):
+      # Separate a class or function from the module-level docstring with two
+      # blank lines.
+      return TWO_BLANK_LINES
+    if _NoBlankLinesBeforeCurrentToken(prev_last_token.value, first_token,
+                                       prev_last_token):
+      return NO_BLANK_LINES
+    else:
+      return ONE_BLANK_LINE
+
+  if first_token.value in {'class', 'def', 'async', '@'}:
+    # TODO(morbo): This can go once the blank line calculator is more
+    # sophisticated.
+    if not indent_depth:
+      # This is a top-level class or function.
+      is_inline_comment = prev_last_token.whitespace_prefix.count('\n') == 0
+      if (not prev_uwline.disable and prev_last_token.is_comment and
+          not is_inline_comment):
+        # This token follows a non-inline comment.
+        if _NoBlankLinesBeforeCurrentToken(prev_last_token.value, first_token,
+                                           prev_last_token):
+          # Assume that the comment is "attached" to the current line.
+          # Therefore, we want two blank lines before the comment.
+          index = len(final_lines) - 1
+          while index > 0:
+            if not final_lines[index - 1].is_comment:
+              break
+            index -= 1
+          if final_lines[index - 1].first.value == '@':
+            final_lines[index].first.AdjustNewlinesBefore(NO_BLANK_LINES)
+          else:
+            prev_last_token.AdjustNewlinesBefore(TWO_BLANK_LINES)
+          if first_token.newlines is not None:
+            pytree_utils.SetNodeAnnotation(
+                first_token.node, pytree_utils.Annotation.NEWLINES, None)
+          return NO_BLANK_LINES
+    elif prev_uwline.first.value in {'class', 'def', 'async'}:
+      if not style.Get('BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'):
+        pytree_utils.SetNodeAnnotation(first_token.node,
+                                       pytree_utils.Annotation.NEWLINES, None)
+        return NO_BLANK_LINES
+
+  # Calculate how many newlines were between the original lines. We want to
+  # retain that formatting if it doesn't violate one of the style guide rules.
+  if first_token.is_comment:
+    first_token_lineno = first_token.lineno - first_token.value.count('\n')
+  else:
+    first_token_lineno = first_token.lineno
+
+  prev_last_token_lineno = prev_last_token.lineno
+  if prev_last_token.is_multiline_string:
+    prev_last_token_lineno += prev_last_token.value.count('\n')
+
+  if first_token_lineno - prev_last_token_lineno > 1:
+    return ONE_BLANK_LINE
+
+  return NO_BLANK_LINES
+
+
+def _SingleOrMergedLines(uwlines):
+  """Generate the lines we want to format.
+
+  Arguments:
+    uwlines: (list of unwrapped_line.UnwrappedLine) Lines we want to format.
+
+  Yields:
+    Either a single line, if the current line cannot be merged with the
+    succeeding line, or the next two lines merged into one line.
+  """
+  index = 0
+  last_was_merged = False
+  while index < len(uwlines):
+    if uwlines[index].disable:
+      uwline = uwlines[index]
+      index += 1
+      while index < len(uwlines):
+        column = uwline.last.column + 2
+        if uwlines[index].lineno != uwline.lineno:
+          break
+        if uwline.last.value != ':':
+          leaf = pytree.Leaf(
+              type=token.SEMI, value=';', context=('', (uwline.lineno, column)))
+          uwline.AppendToken(format_token.FormatToken(leaf))
+        for tok in uwlines[index].tokens:
+          uwline.AppendToken(tok)
+        index += 1
+      yield uwline
+    elif line_joiner.CanMergeMultipleLines(uwlines[index:], last_was_merged):
+      # TODO(morbo): This splice is potentially very slow. Come up with a more
+      # performance-friendly way of determining if two lines can be merged.
+      next_uwline = uwlines[index + 1]
+      for tok in next_uwline.tokens:
+        uwlines[index].AppendToken(tok)
+      if (len(next_uwline.tokens) == 1 and
+          next_uwline.first.is_multiline_string):
+        # This may be a multiline shebang. In that case, we want to retain the
+        # formatting. Otherwise, it could mess up the shell script's syntax.
+        uwlines[index].disable = True
+      yield uwlines[index]
+      index += 2
+      last_was_merged = True
+    else:
+      yield uwlines[index]
+      index += 1
+      last_was_merged = False
+
+
+def _NoBlankLinesBeforeCurrentToken(text, cur_token, prev_token):
+  """Determine if there are no blank lines before the current token.
+
+  The previous token is a docstring or comment. The prev_token_lineno is the
+  start of the text of that token. Counting the number of newlines in its text
+  gives us the extent and thus where the line number of the end of the
+  docstring or comment. After that, we just compare it to the current token's
+  line number to see if there are blank lines between them.
+
+  Arguments:
+    text: (unicode) The text of the docstring or comment before the current
+      token.
+    cur_token: (format_token.FormatToken) The current token in the unwrapped
+      line.
+    prev_token: (format_token.FormatToken) The previous token in the unwrapped
+      line.
+
+  Returns:
+    True if there is no blank line before the current token.
+  """
+  cur_token_lineno = cur_token.lineno
+  if cur_token.is_comment:
+    cur_token_lineno -= cur_token.value.count('\n')
+  num_newlines = text.count('\n') if not prev_token.is_comment else 0
+  return prev_token.lineno + num_newlines == cur_token_lineno - 1
diff --git a/src/tools/yapf/yapf/yapflib/split_penalty.py b/src/tools/yapf/yapf/yapflib/split_penalty.py
new file mode 100644
index 0000000..3ef4d8c
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/split_penalty.py
@@ -0,0 +1,559 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Computation of split penalties before/between tokens."""
+
+from lib2to3 import pytree
+
+from yapf.yapflib import format_token
+from yapf.yapflib import py3compat
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import pytree_visitor
+from yapf.yapflib import style
+
+# TODO(morbo): Document the annotations in a centralized place. E.g., the
+# README file.
+UNBREAKABLE = 1000 * 1000
+NAMED_ASSIGN = 8500
+DOTTED_NAME = 4000
+VERY_STRONGLY_CONNECTED = 3500
+STRONGLY_CONNECTED = 3000
+
+OR_TEST = 1000
+AND_TEST = 1100
+NOT_TEST = 1200
+COMPARISON = 1300
+STAR_EXPR = 1300
+EXPR = 1400
+XOR_EXPR = 1500
+AND_EXPR = 1700
+SHIFT_EXPR = 1800
+ARITH_EXPR = 1900
+TERM = 2000
+FACTOR = 2100
+POWER = 2200
+ATOM = 2300
+ONE_ELEMENT_ARGUMENT = 2500
+
+
+def ComputeSplitPenalties(tree):
+  """Compute split penalties on tokens in the given parse tree.
+
+  Arguments:
+    tree: the top-level pytree node to annotate with penalties.
+  """
+  _SplitPenaltyAssigner().Visit(tree)
+
+
+class _SplitPenaltyAssigner(pytree_visitor.PyTreeVisitor):
+  """Assigns split penalties to tokens, based on parse tree structure.
+
+  Split penalties are attached as annotations to tokens.
+  """
+
+  def Visit_import_as_names(self, node):  # pyline: disable=invalid-name
+    # import_as_names ::= import_as_name (',' import_as_name)* [',']
+    self.DefaultNodeVisit(node)
+    prev_child = None
+    for child in node.children:
+      if (prev_child and isinstance(prev_child, pytree.Leaf) and
+          prev_child.value == ','):
+        _SetSplitPenalty(child, style.Get('SPLIT_PENALTY_IMPORT_NAMES'))
+      prev_child = child
+
+  def Visit_classdef(self, node):  # pylint: disable=invalid-name
+    # classdef ::= 'class' NAME ['(' [arglist] ')'] ':' suite
+    #
+    # NAME
+    _SetUnbreakable(node.children[1])
+    if len(node.children) > 4:
+      # opening '('
+      _SetUnbreakable(node.children[2])
+    # ':'
+    _SetUnbreakable(node.children[-2])
+    self.DefaultNodeVisit(node)
+
+  def Visit_funcdef(self, node):  # pylint: disable=invalid-name
+    # funcdef ::= 'def' NAME parameters ['->' test] ':' suite
+    #
+    # Can't break before the function name and before the colon. The parameters
+    # are handled by child iteration.
+    colon_idx = 1
+    while pytree_utils.NodeName(node.children[colon_idx]) == 'simple_stmt':
+      colon_idx += 1
+    _SetUnbreakable(node.children[colon_idx])
+    arrow_idx = -1
+    while colon_idx < len(node.children):
+      if isinstance(node.children[colon_idx], pytree.Leaf):
+        if node.children[colon_idx].value == ':':
+          break
+        if node.children[colon_idx].value == '->':
+          arrow_idx = colon_idx
+      colon_idx += 1
+    _SetUnbreakable(node.children[colon_idx])
+    self.DefaultNodeVisit(node)
+    if arrow_idx > 0:
+      _SetSplitPenalty(_LastChildNode(node.children[arrow_idx - 1]), 0)
+      _SetUnbreakable(node.children[arrow_idx])
+      _SetStronglyConnected(node.children[arrow_idx + 1])
+
+  def Visit_lambdef(self, node):  # pylint: disable=invalid-name
+    # lambdef ::= 'lambda' [varargslist] ':' test
+    # Loop over the lambda up to and including the colon.
+    if style.Get('ALLOW_MULTILINE_LAMBDAS'):
+      _SetStronglyConnected(node)
+    else:
+      self._SetUnbreakableOnChildren(node)
+
+  def Visit_parameters(self, node):  # pylint: disable=invalid-name
+    # parameters ::= '(' [typedargslist] ')'
+    self.DefaultNodeVisit(node)
+
+    # Can't break before the opening paren of a parameter list.
+    _SetUnbreakable(node.children[0])
+    if not style.Get('DEDENT_CLOSING_BRACKETS'):
+      _SetStronglyConnected(node.children[-1])
+
+  def Visit_arglist(self, node):  # pylint: disable=invalid-name
+    # arglist ::= argument (',' argument)* [',']
+    self.DefaultNodeVisit(node)
+    index = 1
+    while index < len(node.children):
+      child = node.children[index]
+      if isinstance(child, pytree.Leaf) and child.value == ',':
+        _SetUnbreakable(child)
+      index += 1
+
+  def Visit_argument(self, node):  # pylint: disable=invalid-name
+    # argument ::= test [comp_for] | test '=' test  # Really [keyword '='] test
+    self.DefaultNodeVisit(node)
+
+    index = 1
+    while index < len(node.children) - 1:
+      child = node.children[index]
+      if isinstance(child, pytree.Leaf) and child.value == '=':
+        _SetSplitPenalty(_FirstChildNode(node.children[index]), NAMED_ASSIGN)
+        _SetSplitPenalty(
+            _FirstChildNode(node.children[index + 1]), NAMED_ASSIGN)
+      index += 1
+
+  def Visit_dotted_name(self, node):  # pylint: disable=invalid-name
+    # dotted_name ::= NAME ('.' NAME)*
+    self._SetUnbreakableOnChildren(node)
+
+  def Visit_dictsetmaker(self, node):  # pylint: disable=invalid-name
+    # dictsetmaker ::= ( (test ':' test
+    #                      (comp_for | (',' test ':' test)* [','])) |
+    #                    (test (comp_for | (',' test)* [','])) )
+    for child in node.children:
+      self.Visit(child)
+      if pytree_utils.NodeName(child) == 'COLON':
+        # This is a key to a dictionary. We don't want to split the key if at
+        # all possible.
+        _SetStronglyConnected(child)
+
+  def Visit_trailer(self, node):  # pylint: disable=invalid-name
+    # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
+    self.DefaultNodeVisit(node)
+    if node.children[0].value == '.':
+      self._SetUnbreakableOnChildren(node)
+      _SetSplitPenalty(node.children[1], DOTTED_NAME)
+    elif len(node.children) == 2:
+      # Don't split an empty argument list if at all possible.
+      _SetSplitPenalty(node.children[1], VERY_STRONGLY_CONNECTED)
+    elif len(node.children) == 3:
+      name = pytree_utils.NodeName(node.children[1])
+      if name == 'power':
+        if pytree_utils.NodeName(node.children[1].children[0]) != 'atom':
+          # Don't split an argument list with one element if at all possible.
+          _SetStronglyConnected(node.children[1], node.children[2])
+          _SetSplitPenalty(
+              _FirstChildNode(node.children[1]), ONE_ELEMENT_ARGUMENT)
+      elif (pytree_utils.NodeName(node.children[0]) == 'LSQB' and
+            len(node.children[1].children) > 2 and
+            (name.endswith('_test') or name.endswith('_expr'))):
+        _SetStronglyConnected(node.children[1].children[0])
+        _SetStronglyConnected(node.children[1].children[2])
+
+        # Still allow splitting around the operator.
+        split_before = ((name.endswith('_test') and
+                         style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR')) or
+                        (name.endswith('_expr') and
+                         style.Get('SPLIT_BEFORE_BITWISE_OPERATOR')))
+        if split_before:
+          _SetSplitPenalty(_LastChildNode(node.children[1].children[1]), 0)
+        else:
+          _SetSplitPenalty(_FirstChildNode(node.children[1].children[2]), 0)
+
+        # Don't split the ending bracket of a subscript list.
+        _SetVeryStronglyConnected(node.children[-1])
+      elif name not in {
+          'arglist', 'argument', 'term', 'or_test', 'and_test', 'comparison',
+          'atom'
+      }:
+        # Don't split an argument list with one element if at all possible.
+        _SetStronglyConnected(node.children[1], node.children[2])
+
+  def Visit_power(self, node):  # pylint: disable=invalid-name,missing-docstring
+    # power ::= atom trailer* ['**' factor]
+    self.DefaultNodeVisit(node)
+
+    # When atom is followed by a trailer, we can not break between them.
+    # E.g. arr[idx] - no break allowed between 'arr' and '['.
+    if (len(node.children) > 1 and
+        pytree_utils.NodeName(node.children[1]) == 'trailer'):
+      # children[1] itself is a whole trailer: we don't want to
+      # mark all of it as unbreakable, only its first token: (, [ or .
+      _SetUnbreakable(node.children[1].children[0])
+
+      # A special case when there are more trailers in the sequence. Given:
+      #   atom tr1 tr2
+      # The last token of tr1 and the first token of tr2 comprise an unbreakable
+      # region. For example: foo.bar.baz(1)
+      # We can't put breaks between either of the '.', '(', or '[' and the names
+      # *preceding* them.
+      prev_trailer_idx = 1
+      while prev_trailer_idx < len(node.children) - 1:
+        cur_trailer_idx = prev_trailer_idx + 1
+        cur_trailer = node.children[cur_trailer_idx]
+        if pytree_utils.NodeName(cur_trailer) == 'trailer':
+          # Now we know we have two trailers one after the other
+          prev_trailer = node.children[prev_trailer_idx]
+          if prev_trailer.children[-1].value != ')':
+            # Set the previous node unbreakable if it's not a function call:
+            #   atom tr1() tr2
+            # It may be necessary (though undesirable) to split up a previous
+            # function call's parentheses to the next line.
+            _SetStronglyConnected(prev_trailer.children[-1])
+          _SetStronglyConnected(cur_trailer.children[0])
+          prev_trailer_idx = cur_trailer_idx
+        else:
+          break
+
+    # We don't want to split before the last ')' of a function call. This also
+    # takes care of the special case of:
+    #   atom tr1 tr2 ... trn
+    # where the 'tr#' are trailers that may end in a ')'.
+    for trailer in node.children[1:]:
+      if pytree_utils.NodeName(trailer) != 'trailer':
+        break
+      if trailer.children[0].value in '([':
+        if len(trailer.children) > 2:
+          subtypes = pytree_utils.GetNodeAnnotation(
+              trailer.children[0], pytree_utils.Annotation.SUBTYPE)
+          if subtypes and format_token.Subtype.SUBSCRIPT_BRACKET in subtypes:
+            _SetStronglyConnected(_FirstChildNode(trailer.children[1]))
+
+          last_child_node = _LastChildNode(trailer)
+          if last_child_node.value.strip().startswith('#'):
+            last_child_node = last_child_node.prev_sibling
+          if not style.Get('DEDENT_CLOSING_BRACKETS'):
+            if _LastChildNode(last_child_node.prev_sibling).value != ',':
+              if last_child_node.value == ']':
+                _SetUnbreakable(last_child_node)
+              else:
+                _SetSplitPenalty(last_child_node, VERY_STRONGLY_CONNECTED)
+        else:
+          # If the trailer's children are '()', then make it a strongly
+          # connected region.  It's sometimes necessary, though undesirable, to
+          # split the two.
+          _SetStronglyConnected(trailer.children[-1])
+
+    # If the original source has a "builder" style calls, then we should allow
+    # the reformatter to retain that.
+    _AllowBuilderStyleCalls(node)
+
+  def Visit_subscript(self, node):  # pylint: disable=invalid-name
+    # subscript ::= test | [test] ':' [test] [sliceop]
+    _SetStronglyConnected(*node.children)
+    self.DefaultNodeVisit(node)
+
+  def Visit_comp_for(self, node):  # pylint: disable=invalid-name
+    # comp_for ::= 'for' exprlist 'in' testlist_safe [comp_iter]
+    _SetSplitPenalty(_FirstChildNode(node), 0)
+    _SetStronglyConnected(*node.children[1:])
+    self.DefaultNodeVisit(node)
+
+  def Visit_comp_if(self, node):  # pylint: disable=invalid-name
+    # comp_if ::= 'if' old_test [comp_iter]
+    _SetSplitPenalty(node.children[0],
+                     style.Get('SPLIT_PENALTY_BEFORE_IF_EXPR'))
+    _SetStronglyConnected(*node.children[1:])
+    self.DefaultNodeVisit(node)
+
+  def Visit_or_test(self, node):  # pylint: disable=invalid-name
+    # or_test ::= and_test ('or' and_test)*
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, OR_TEST)
+    index = 1
+    while index + 1 < len(node.children):
+      if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
+        _DecrementSplitPenalty(_FirstChildNode(node.children[index]), OR_TEST)
+      else:
+        _DecrementSplitPenalty(
+            _FirstChildNode(node.children[index + 1]), OR_TEST)
+      index += 2
+
+  def Visit_and_test(self, node):  # pylint: disable=invalid-name
+    # and_test ::= not_test ('and' not_test)*
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, AND_TEST)
+    index = 1
+    while index + 1 < len(node.children):
+      if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
+        _DecrementSplitPenalty(_FirstChildNode(node.children[index]), AND_TEST)
+      else:
+        _DecrementSplitPenalty(
+            _FirstChildNode(node.children[index + 1]), AND_TEST)
+      index += 2
+
+  def Visit_not_test(self, node):  # pylint: disable=invalid-name
+    # not_test ::= 'not' not_test | comparison
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, NOT_TEST)
+
+  def Visit_comparison(self, node):  # pylint: disable=invalid-name
+    # comparison ::= expr (comp_op expr)*
+    self.DefaultNodeVisit(node)
+    if len(node.children) == 3 and _StronglyConnectedCompOp(node):
+      _SetSplitPenalty(_FirstChildNode(node.children[1]), STRONGLY_CONNECTED)
+      _SetSplitPenalty(_FirstChildNode(node.children[2]), STRONGLY_CONNECTED)
+    else:
+      _IncreasePenalty(node, COMPARISON)
+
+  def Visit_star_expr(self, node):  # pylint: disable=invalid-name
+    # star_expr ::= '*' expr
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, STAR_EXPR)
+
+  def Visit_expr(self, node):  # pylint: disable=invalid-name
+    # expr ::= xor_expr ('|' xor_expr)*
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, EXPR)
+    index = 1
+    while index < len(node.children) - 1:
+      child = node.children[index]
+      if isinstance(child, pytree.Leaf) and child.value == '|':
+        if style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'):
+          _SetSplitPenalty(child, style.Get('SPLIT_PENALTY_BITWISE_OPERATOR'))
+        else:
+          _SetSplitPenalty(
+              _FirstChildNode(node.children[index + 1]),
+              style.Get('SPLIT_PENALTY_BITWISE_OPERATOR'))
+      index += 1
+
+  def Visit_xor_expr(self, node):  # pylint: disable=invalid-name
+    # xor_expr ::= and_expr ('^' and_expr)*
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, XOR_EXPR)
+
+  def Visit_and_expr(self, node):  # pylint: disable=invalid-name
+    # and_expr ::= shift_expr ('&' shift_expr)*
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, AND_EXPR)
+
+  def Visit_shift_expr(self, node):  # pylint: disable=invalid-name
+    # shift_expr ::= arith_expr (('<<'|'>>') arith_expr)*
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, SHIFT_EXPR)
+
+  def Visit_arith_expr(self, node):  # pylint: disable=invalid-name
+    # arith_expr ::= term (('+'|'-') term)*
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, ARITH_EXPR)
+
+  def Visit_term(self, node):  # pylint: disable=invalid-name
+    # term ::= factor (('*'|'@'|'/'|'%'|'//') factor)*
+    _IncreasePenalty(node, TERM)
+    self.DefaultNodeVisit(node)
+
+  def Visit_factor(self, node):  # pyline: disable=invalid-name
+    # factor ::= ('+'|'-'|'~') factor | power
+    self.DefaultNodeVisit(node)
+    _IncreasePenalty(node, FACTOR)
+
+  def Visit_atom(self, node):  # pylint: disable=invalid-name
+    # atom ::= ('(' [yield_expr|testlist_gexp] ')'
+    #           '[' [listmaker] ']' |
+    #           '{' [dictsetmaker] '}')
+    self.DefaultNodeVisit(node)
+    if node.children[0].value == '(':
+      if node.children[-1].value == ')':
+        if pytree_utils.NodeName(node.parent) == 'if_stmt':
+          _SetSplitPenalty(node.children[-1], UNBREAKABLE)
+        else:
+          if len(node.children) > 2:
+            _SetSplitPenalty(_FirstChildNode(node.children[1]), EXPR)
+          _SetSplitPenalty(node.children[-1], ATOM)
+    elif node.children[0].value in '[{' and len(node.children) == 2:
+      # Keep empty containers together if we can.
+      _SetUnbreakable(node.children[-1])
+
+  def Visit_testlist_gexp(self, node):  # pylint: disable=invalid-name
+    self.DefaultNodeVisit(node)
+    prev_was_comma = False
+    for child in node.children:
+      if isinstance(child, pytree.Leaf) and child.value == ',':
+        _SetUnbreakable(child)
+        prev_was_comma = True
+      else:
+        if prev_was_comma:
+          _SetSplitPenalty(_FirstChildNode(child), 0)
+        prev_was_comma = False
+
+  ############################################################################
+  # Helper methods that set the annotations.
+
+  def _SetUnbreakableOnChildren(self, node):
+    """Set an UNBREAKABLE penalty annotation on children of node."""
+    for child in node.children:
+      self.Visit(child)
+    start = 2 if hasattr(node.children[0], 'is_pseudo') else 1
+    for i in py3compat.range(start, len(node.children)):
+      _SetUnbreakable(node.children[i])
+
+
+def _SetUnbreakable(node):
+  """Set an UNBREAKABLE penalty annotation for the given node."""
+  _RecAnnotate(node, pytree_utils.Annotation.SPLIT_PENALTY, UNBREAKABLE)
+
+
+def _SetStronglyConnected(*nodes):
+  """Set a STRONGLY_CONNECTED penalty annotation for the given nodes."""
+  for node in nodes:
+    _RecAnnotate(node, pytree_utils.Annotation.SPLIT_PENALTY,
+                 STRONGLY_CONNECTED)
+
+
+def _SetVeryStronglyConnected(*nodes):
+  """Set a VERY_STRONGLY_CONNECTED penalty annotation for the given nodes."""
+  for node in nodes:
+    _RecAnnotate(node, pytree_utils.Annotation.SPLIT_PENALTY,
+                 VERY_STRONGLY_CONNECTED)
+
+
+def _SetExpressionPenalty(node, penalty):
+  """Set a penalty annotation on children nodes."""
+
+  def RecExpression(node, first_child_leaf):
+    if node is first_child_leaf:
+      return
+
+    if isinstance(node, pytree.Leaf):
+      if node.value in {'(', 'for', 'if'}:
+        return
+      penalty_annotation = pytree_utils.GetNodeAnnotation(
+          node, pytree_utils.Annotation.SPLIT_PENALTY, default=0)
+      if penalty_annotation < penalty:
+        _SetSplitPenalty(node, penalty)
+    else:
+      for child in node.children:
+        RecExpression(child, first_child_leaf)
+
+  RecExpression(node, _FirstChildNode(node))
+
+
+def _IncreasePenalty(node, amt):
+  """Increase a penalty annotation on children nodes."""
+
+  def RecExpression(node, first_child_leaf):
+    if node is first_child_leaf:
+      return
+
+    if isinstance(node, pytree.Leaf):
+      if node.value in {'(', 'for', 'if'}:
+        return
+      penalty = pytree_utils.GetNodeAnnotation(
+          node, pytree_utils.Annotation.SPLIT_PENALTY, default=0)
+      _SetSplitPenalty(node, penalty + amt)
+    else:
+      for child in node.children:
+        RecExpression(child, first_child_leaf)
+
+  RecExpression(node, _FirstChildNode(node))
+
+
+def _RecAnnotate(tree, annotate_name, annotate_value):
+  """Recursively set the given annotation on all leafs of the subtree.
+
+  Takes care to only increase the penalty. If the node already has a higher
+  or equal penalty associated with it, this is a no-op.
+
+  Args:
+    tree: subtree to annotate
+    annotate_name: name of the annotation to set
+    annotate_value: value of the annotation to set
+  """
+  for child in tree.children:
+    _RecAnnotate(child, annotate_name, annotate_value)
+  if isinstance(tree, pytree.Leaf):
+    cur_annotate = pytree_utils.GetNodeAnnotation(
+        tree, annotate_name, default=0)
+    if cur_annotate < annotate_value:
+      pytree_utils.SetNodeAnnotation(tree, annotate_name, annotate_value)
+
+
+def _StronglyConnectedCompOp(op):
+  if (len(op.children[1].children) == 2 and
+      pytree_utils.NodeName(op.children[1]) == 'comp_op' and
+      _FirstChildNode(op.children[1]).value == 'not' and
+      _LastChildNode(op.children[1]).value == 'in'):
+    return True
+  if (isinstance(op.children[1], pytree.Leaf) and
+      op.children[1].value in {'==', 'in'}):
+    return True
+  return False
+
+
+def _DecrementSplitPenalty(node, amt):
+  penalty = pytree_utils.GetNodeAnnotation(
+      node, pytree_utils.Annotation.SPLIT_PENALTY, default=amt)
+  penalty = penalty - amt if amt < penalty else 0
+  _SetSplitPenalty(node, penalty)
+
+
+def _SetSplitPenalty(node, penalty):
+  pytree_utils.SetNodeAnnotation(node, pytree_utils.Annotation.SPLIT_PENALTY,
+                                 penalty)
+
+
+def _AllowBuilderStyleCalls(node):
+  """Allow splitting before '.' if it's a builder style function call."""
+
+  def RecGetLeaves(node):
+    if isinstance(node, pytree.Leaf):
+      return [node]
+    children = []
+    for child in node.children:
+      children += RecGetLeaves(child)
+    return children
+
+  list_of_children = RecGetLeaves(node)
+  prev_child = None
+  for child in list_of_children:
+    if child.value == '.':
+      if prev_child.lineno != child.lineno:
+        _SetSplitPenalty(child, 0)
+    prev_child = child
+
+
+def _FirstChildNode(node):
+  if isinstance(node, pytree.Leaf):
+    return node
+  return _FirstChildNode(node.children[0])
+
+
+def _LastChildNode(node):
+  if isinstance(node, pytree.Leaf):
+    return node
+  return _LastChildNode(node.children[-1])
diff --git a/src/tools/yapf/yapf/yapflib/style.py b/src/tools/yapf/yapf/yapflib/style.py
new file mode 100644
index 0000000..14be59e
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/style.py
@@ -0,0 +1,489 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Python formatting style settings."""
+
+import os
+import re
+import textwrap
+
+from yapf.yapflib import errors
+from yapf.yapflib import py3compat
+
+
+class StyleConfigError(errors.YapfError):
+  """Raised when there's a problem reading the style configuration."""
+  pass
+
+
+def Get(setting_name):
+  """Get a style setting."""
+  return _style[setting_name]
+
+
+def Help():
+  """Return dict mapping style names to help strings."""
+  return _STYLE_HELP
+
+
+def SetGlobalStyle(style):
+  """Set a style dict."""
+  global _style
+  global _GLOBAL_STYLE_FACTORY
+  factory = _GetStyleFactory(style)
+  if factory:
+    _GLOBAL_STYLE_FACTORY = factory
+  _style = style
+
+
+_STYLE_HELP = dict(
+    ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=textwrap.dedent("""\
+      Align closing bracket with visual indentation."""),
+    ALLOW_MULTILINE_LAMBDAS=textwrap.dedent("""\
+      Allow lambdas to be formatted on more than one line."""),
+    ALLOW_MULTILINE_DICTIONARY_KEYS=textwrap.dedent("""\
+      Allow dictionary keys to exist on multiple lines. For example:
+
+        x = {
+            ('this is the first element of a tuple',
+             'this is the second element of a tuple'):
+                 value,
+        }"""),
+    BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=textwrap.dedent("""\
+      Insert a blank line before a 'def' or 'class' immediately nested
+      within another 'def' or 'class'. For example:
+
+        class Foo:
+                           # <------ this blank line
+          def method():
+            ..."""),
+    BLANK_LINE_BEFORE_CLASS_DOCSTRING=textwrap.dedent("""\
+      Insert a blank line before a class-level docstring."""),
+    COALESCE_BRACKETS=textwrap.dedent("""\
+      Do not split consecutive brackets. Only relevant when
+      dedent_closing_brackets is set. For example:
+
+         call_func_that_takes_a_dict(
+             {
+                 'key1': 'value1',
+                 'key2': 'value2',
+             }
+         )
+
+      would reformat to:
+
+         call_func_that_takes_a_dict({
+             'key1': 'value1',
+             'key2': 'value2',
+         })"""),
+    COLUMN_LIMIT=textwrap.dedent("""\
+      The column limit."""),
+    CONTINUATION_INDENT_WIDTH=textwrap.dedent("""\
+      Indent width used for line continuations."""),
+    DEDENT_CLOSING_BRACKETS=textwrap.dedent("""\
+      Put closing brackets on a separate line, dedented, if the bracketed
+      expression can't fit in a single line. Applies to all kinds of brackets,
+      including function definitions and calls. For example:
+
+        config = {
+            'key1': 'value1',
+            'key2': 'value2',
+        }        # <--- this bracket is dedented and on a separate line
+
+        time_series = self.remote_client.query_entity_counters(
+            entity='dev3246.region1',
+            key='dns.query_latency_tcp',
+            transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
+            start_ts=now()-timedelta(days=3),
+            end_ts=now(),
+        )        # <--- this bracket is dedented and on a separate line"""),
+    EACH_DICT_ENTRY_ON_SEPARATE_LINE=textwrap.dedent("""\
+      Place each dictionary entry onto its own line."""),
+    I18N_COMMENT=textwrap.dedent("""\
+      The regex for an i18n comment. The presence of this comment stops
+      reformatting of that line, because the comments are required to be
+      next to the string they translate."""),
+    I18N_FUNCTION_CALL=textwrap.dedent("""\
+      The i18n function call names. The presence of this function stops
+      reformattting on that line, because the string it has cannot be moved
+      away from the i18n comment."""),
+    INDENT_DICTIONARY_VALUE=textwrap.dedent("""\
+      Indent the dictionary value if it cannot fit on the same line as the
+      dictionary key. For example:
+
+        config = {
+            'key1':
+                'value1',
+            'key2': value1 +
+                    value2,
+        }"""),
+    INDENT_WIDTH=textwrap.dedent("""\
+      The number of columns to use for indentation."""),
+    JOIN_MULTIPLE_LINES=textwrap.dedent("""\
+      Join short lines into one line. E.g., single line 'if' statements."""),
+    SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=textwrap.dedent("""\
+      Insert a space between the ending comma and closing bracket of a list,
+      etc."""),
+    SPACES_AROUND_POWER_OPERATOR=textwrap.dedent("""\
+      Use spaces around the power operator."""),
+    SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=textwrap.dedent("""\
+      Use spaces around default or named assigns."""),
+    SPACES_BEFORE_COMMENT=textwrap.dedent("""\
+      The number of spaces required before a trailing comment."""),
+    SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=textwrap.dedent("""\
+      Split before arguments if the argument list is terminated by a
+      comma."""),
+    SPLIT_BEFORE_BITWISE_OPERATOR=textwrap.dedent("""\
+      Set to True to prefer splitting before '&', '|' or '^' rather than
+      after."""),
+    SPLIT_BEFORE_DICT_SET_GENERATOR=textwrap.dedent("""\
+      Split before a dictionary or set generator (comp_for). For example, note
+      the split before the 'for':
+
+        foo = {
+            variable: 'Hello world, have a nice day!'
+            for variable in bar if variable != 42
+        }"""),
+    SPLIT_BEFORE_FIRST_ARGUMENT=textwrap.dedent("""\
+      If an argument / parameter list is going to be split, then split before
+      the first argument."""),
+    SPLIT_BEFORE_LOGICAL_OPERATOR=textwrap.dedent("""\
+      Set to True to prefer splitting before 'and' or 'or' rather than
+      after."""),
+    SPLIT_BEFORE_NAMED_ASSIGNS=textwrap.dedent("""\
+      Split named assignments onto individual lines."""),
+    SPLIT_PENALTY_AFTER_OPENING_BRACKET=textwrap.dedent("""\
+      The penalty for splitting right after the opening bracket."""),
+    SPLIT_PENALTY_AFTER_UNARY_OPERATOR=textwrap.dedent("""\
+      The penalty for splitting the line after a unary operator."""),
+    SPLIT_PENALTY_BEFORE_IF_EXPR=textwrap.dedent("""\
+      The penalty for splitting right before an if expression."""),
+    SPLIT_PENALTY_BITWISE_OPERATOR=textwrap.dedent("""\
+      The penalty of splitting the line around the '&', '|', and '^'
+      operators."""),
+    SPLIT_PENALTY_EXCESS_CHARACTER=textwrap.dedent("""\
+      The penalty for characters over the column limit."""),
+    SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=textwrap.dedent("""\
+      The penalty incurred by adding a line split to the unwrapped line. The
+      more line splits added the higher the penalty."""),
+    SPLIT_PENALTY_IMPORT_NAMES=textwrap.dedent("""\
+      The penalty of splitting a list of "import as" names. For example:
+
+        from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
+                                                                  long_argument_2,
+                                                                  long_argument_3)
+
+      would reformat to something like:
+
+        from a_very_long_or_indented_module_name_yada_yad import (
+            long_argument_1, long_argument_2, long_argument_3)
+      """),
+    SPLIT_PENALTY_LOGICAL_OPERATOR=textwrap.dedent("""\
+      The penalty of splitting the line around the 'and' and 'or'
+      operators."""),
+    USE_TABS=textwrap.dedent("""\
+      Use the Tab character for indentation."""),
+    # BASED_ON_STYLE='Which predefined style this style is based on',
+)
+
+
+def CreatePEP8Style():
+  return dict(
+      ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=True,
+      ALLOW_MULTILINE_LAMBDAS=False,
+      ALLOW_MULTILINE_DICTIONARY_KEYS=False,
+      BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=False,
+      BLANK_LINE_BEFORE_CLASS_DOCSTRING=False,
+      COALESCE_BRACKETS=False,
+      COLUMN_LIMIT=79,
+      CONTINUATION_INDENT_WIDTH=4,
+      DEDENT_CLOSING_BRACKETS=False,
+      EACH_DICT_ENTRY_ON_SEPARATE_LINE=True,
+      I18N_COMMENT='',
+      I18N_FUNCTION_CALL='',
+      INDENT_DICTIONARY_VALUE=False,
+      INDENT_WIDTH=4,
+      JOIN_MULTIPLE_LINES=True,
+      SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=True,
+      SPACES_AROUND_POWER_OPERATOR=False,
+      SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=False,
+      SPACES_BEFORE_COMMENT=2,
+      SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=False,
+      SPLIT_BEFORE_BITWISE_OPERATOR=False,
+      SPLIT_BEFORE_DICT_SET_GENERATOR=True,
+      SPLIT_BEFORE_FIRST_ARGUMENT=False,
+      SPLIT_BEFORE_LOGICAL_OPERATOR=False,
+      SPLIT_BEFORE_NAMED_ASSIGNS=True,
+      SPLIT_PENALTY_AFTER_OPENING_BRACKET=30,
+      SPLIT_PENALTY_AFTER_UNARY_OPERATOR=10000,
+      SPLIT_PENALTY_BEFORE_IF_EXPR=0,
+      SPLIT_PENALTY_BITWISE_OPERATOR=300,
+      SPLIT_PENALTY_EXCESS_CHARACTER=4500,
+      SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=30,
+      SPLIT_PENALTY_IMPORT_NAMES=0,
+      SPLIT_PENALTY_LOGICAL_OPERATOR=300,
+      USE_TABS=False,)
+
+
+def CreateGoogleStyle():
+  style = CreatePEP8Style()
+  style['ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'] = False
+  style['BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'] = True
+  style['COLUMN_LIMIT'] = 80
+  style['INDENT_WIDTH'] = 4
+  style['I18N_COMMENT'] = r'#\..*'
+  style['I18N_FUNCTION_CALL'] = ['N_', '_']
+  style['SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET'] = False
+  return style
+
+
+def CreateChromiumStyle():
+  style = CreateGoogleStyle()
+  style['ALLOW_MULTILINE_DICTIONARY_KEYS'] = True
+  style['INDENT_DICTIONARY_VALUE'] = True
+  style['INDENT_WIDTH'] = 2
+  style['JOIN_MULTIPLE_LINES'] = False
+  style['SPLIT_BEFORE_BITWISE_OPERATOR'] = True
+  return style
+
+
+def CreateFacebookStyle():
+  style = CreatePEP8Style()
+  style['ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'] = False
+  style['COLUMN_LIMIT'] = 80
+  style['DEDENT_CLOSING_BRACKETS'] = True
+  style['JOIN_MULTIPLE_LINES'] = False
+  style['SPACES_BEFORE_COMMENT'] = 2
+  style['SPLIT_PENALTY_AFTER_OPENING_BRACKET'] = 0
+  style['SPLIT_PENALTY_BEFORE_IF_EXPR'] = 30
+  style['SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT'] = 30
+  return style
+
+
+_STYLE_NAME_TO_FACTORY = dict(
+    pep8=CreatePEP8Style,
+    chromium=CreateChromiumStyle,
+    google=CreateGoogleStyle,
+    facebook=CreateFacebookStyle,)
+
+_DEFAULT_STYLE_TO_FACTORY = [
+    (CreateChromiumStyle(), CreateChromiumStyle),
+    (CreateFacebookStyle(), CreateFacebookStyle),
+    (CreateGoogleStyle(), CreateGoogleStyle),
+    (CreatePEP8Style(), CreatePEP8Style),
+]
+
+
+def _GetStyleFactory(style):
+  for def_style, factory in _DEFAULT_STYLE_TO_FACTORY:
+    if style == def_style:
+      return factory
+  return None
+
+
+def _StringListConverter(s):
+  """Option value converter for a comma-separated list of strings."""
+  return [part.strip() for part in s.split(',')]
+
+
+def _BoolConverter(s):
+  """Option value converter for a boolean."""
+  return py3compat.CONFIGPARSER_BOOLEAN_STATES[s.lower()]
+
+
+# Different style options need to have their values interpreted differently when
+# read from the config file. This dict maps an option name to a "converter"
+# function that accepts the string read for the option's value from the file and
+# returns it wrapper in actual Python type that's going to be meaningful to
+# yapf.
+#
+# Note: this dict has to map all the supported style options.
+_STYLE_OPTION_VALUE_CONVERTER = dict(
+    ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=_BoolConverter,
+    ALLOW_MULTILINE_LAMBDAS=_BoolConverter,
+    ALLOW_MULTILINE_DICTIONARY_KEYS=_BoolConverter,
+    BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=_BoolConverter,
+    BLANK_LINE_BEFORE_CLASS_DOCSTRING=_BoolConverter,
+    COALESCE_BRACKETS=_BoolConverter,
+    COLUMN_LIMIT=int,
+    CONTINUATION_INDENT_WIDTH=int,
+    DEDENT_CLOSING_BRACKETS=_BoolConverter,
+    EACH_DICT_ENTRY_ON_SEPARATE_LINE=_BoolConverter,
+    I18N_COMMENT=str,
+    I18N_FUNCTION_CALL=_StringListConverter,
+    INDENT_DICTIONARY_VALUE=_BoolConverter,
+    INDENT_WIDTH=int,
+    JOIN_MULTIPLE_LINES=_BoolConverter,
+    SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=_BoolConverter,
+    SPACES_AROUND_POWER_OPERATOR=_BoolConverter,
+    SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=_BoolConverter,
+    SPACES_BEFORE_COMMENT=int,
+    SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=_BoolConverter,
+    SPLIT_BEFORE_BITWISE_OPERATOR=_BoolConverter,
+    SPLIT_BEFORE_DICT_SET_GENERATOR=_BoolConverter,
+    SPLIT_BEFORE_FIRST_ARGUMENT=_BoolConverter,
+    SPLIT_BEFORE_LOGICAL_OPERATOR=_BoolConverter,
+    SPLIT_BEFORE_NAMED_ASSIGNS=_BoolConverter,
+    SPLIT_PENALTY_AFTER_OPENING_BRACKET=int,
+    SPLIT_PENALTY_AFTER_UNARY_OPERATOR=int,
+    SPLIT_PENALTY_BEFORE_IF_EXPR=int,
+    SPLIT_PENALTY_BITWISE_OPERATOR=int,
+    SPLIT_PENALTY_EXCESS_CHARACTER=int,
+    SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=int,
+    SPLIT_PENALTY_IMPORT_NAMES=int,
+    SPLIT_PENALTY_LOGICAL_OPERATOR=int,
+    USE_TABS=_BoolConverter,)
+
+
+def CreateStyleFromConfig(style_config):
+  """Create a style dict from the given config.
+
+  Arguments:
+    style_config: either a style name or a file name. The file is expected to
+      contain settings. It can have a special BASED_ON_STYLE setting naming the
+      style which it derives from. If no such setting is found, it derives from
+      the default style. When style_config is None, the _GLOBAL_STYLE_FACTORY
+      config is created.
+
+  Returns:
+    A style dict.
+
+  Raises:
+    StyleConfigError: if an unknown style option was encountered.
+  """
+
+  def GlobalStyles():
+    for style, _ in _DEFAULT_STYLE_TO_FACTORY:
+      yield style
+
+  def_style = False
+  if style_config is None:
+    for style in GlobalStyles():
+      if _style == style:
+        def_style = True
+        break
+    if not def_style:
+      return _style
+    return _GLOBAL_STYLE_FACTORY()
+  style_factory = _STYLE_NAME_TO_FACTORY.get(style_config.lower())
+  if style_factory is not None:
+    return style_factory()
+  if style_config.startswith('{'):
+    # Most likely a style specification from the command line.
+    config = _CreateConfigParserFromConfigString(style_config)
+  else:
+    # Unknown config name: assume it's a file name then.
+    config = _CreateConfigParserFromConfigFile(style_config)
+  return _CreateStyleFromConfigParser(config)
+
+
+def _CreateConfigParserFromConfigString(config_string):
+  """Given a config string from the command line, return a config parser."""
+  if config_string[0] != '{' or config_string[-1] != '}':
+    raise StyleConfigError(
+        "Invalid style dict syntax: '{}'.".format(config_string))
+  config = py3compat.ConfigParser()
+  config.add_section('style')
+  for key, value in re.findall(r'([a-zA-Z0-9_]+)\s*[:=]\s*([a-zA-Z0-9_]+)',
+                               config_string):
+    config.set('style', key, value)
+  return config
+
+
+def _CreateConfigParserFromConfigFile(config_filename):
+  """Read the file and return a ConfigParser object."""
+  if not os.path.exists(config_filename):
+    # Provide a more meaningful error here.
+    raise StyleConfigError(
+        '"{0}" is not a valid style or file path'.format(config_filename))
+  with open(config_filename) as style_file:
+    config = py3compat.ConfigParser()
+    config.read_file(style_file)
+    if config_filename.endswith(SETUP_CONFIG):
+      if not config.has_section('yapf'):
+        raise StyleConfigError(
+            'Unable to find section [yapf] in {0}'.format(config_filename))
+    elif config_filename.endswith(LOCAL_STYLE):
+      if not config.has_section('style'):
+        raise StyleConfigError(
+            'Unable to find section [style] in {0}'.format(config_filename))
+    else:
+      if not config.has_section('style'):
+        raise StyleConfigError(
+            'Unable to find section [style] in {0}'.format(config_filename))
+    return config
+
+
+def _CreateStyleFromConfigParser(config):
+  """Create a style dict from a configuration file.
+
+  Arguments:
+    config: a ConfigParser object.
+
+  Returns:
+    A style dict.
+
+  Raises:
+    StyleConfigError: if an unknown style option was encountered.
+  """
+  # Initialize the base style.
+  section = 'yapf' if config.has_section('yapf') else 'style'
+  if config.has_option('style', 'based_on_style'):
+    based_on = config.get('style', 'based_on_style').lower()
+    base_style = _STYLE_NAME_TO_FACTORY[based_on]()
+  elif config.has_option('yapf', 'based_on_style'):
+    based_on = config.get('yapf', 'based_on_style').lower()
+    base_style = _STYLE_NAME_TO_FACTORY[based_on]()
+  else:
+    base_style = _GLOBAL_STYLE_FACTORY()
+
+  # Read all options specified in the file and update the style.
+  for option, value in config.items(section):
+    if option.lower() == 'based_on_style':
+      # Now skip this one - we've already handled it and it's not one of the
+      # recognized style options.
+      continue
+    option = option.upper()
+    if option not in _STYLE_OPTION_VALUE_CONVERTER:
+      raise StyleConfigError('Unknown style option "{0}"'.format(option))
+    try:
+      base_style[option] = _STYLE_OPTION_VALUE_CONVERTER[option](value)
+    except ValueError:
+      raise StyleConfigError(
+          "'{}' is not a valid setting for {}.".format(value, option))
+  return base_style
+
+
+# The default style - used if yapf is not invoked without specifically
+# requesting a formatting style.
+DEFAULT_STYLE = 'pep8'
+DEFAULT_STYLE_FACTORY = CreatePEP8Style
+_GLOBAL_STYLE_FACTORY = CreatePEP8Style
+
+# The name of the file to use for global style definition.
+GLOBAL_STYLE = (os.path.join(
+    os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'), 'yapf',
+    'style'))
+
+# The name of the file to use for directory-local style definition.
+LOCAL_STYLE = '.style.yapf'
+
+# Alternative place for directory-local style definition. Style should be
+# specified in the '[yapf]' section.
+SETUP_CONFIG = 'setup.cfg'
+
+# TODO(eliben): For now we're preserving the global presence of a style dict.
+# Refactor this so that the style is passed around through yapf rather than
+# being global.
+_style = None
+SetGlobalStyle(_GLOBAL_STYLE_FACTORY())
diff --git a/src/tools/yapf/yapf/yapflib/subtype_assigner.py b/src/tools/yapf/yapf/yapflib/subtype_assigner.py
new file mode 100644
index 0000000..646cdc8
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/subtype_assigner.py
@@ -0,0 +1,416 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Subtype assigner for lib2to3 trees.
+
+This module assigns extra type information to the lib2to3 trees. This
+information is more specific than whether something is an operator or an
+identifier. For instance, it can specify if a node in the tree is part of a
+subscript.
+
+  AssignSubtypes(): the main function exported by this module.
+
+Annotations:
+  subtype: The subtype of a pytree token. See 'format_token' module for a list
+      of subtypes.
+"""
+
+from lib2to3 import pytree
+from lib2to3.pgen2 import token
+from lib2to3.pygram import python_symbols as syms
+
+from yapf.yapflib import format_token
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import pytree_visitor
+from yapf.yapflib import style
+
+
+def AssignSubtypes(tree):
+  """Run the subtype assigner visitor over the tree, modifying it in place.
+
+  Arguments:
+    tree: the top-level pytree node to annotate with subtypes.
+  """
+  subtype_assigner = _SubtypeAssigner()
+  subtype_assigner.Visit(tree)
+
+
+# Map tokens in argument lists to their respective subtype.
+_ARGLIST_TOKEN_TO_SUBTYPE = {
+    '=': format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN,
+    ':': format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN,
+    '*': format_token.Subtype.VARARGS_STAR,
+    '**': format_token.Subtype.KWARGS_STAR_STAR,
+}
+
+
+class _SubtypeAssigner(pytree_visitor.PyTreeVisitor):
+  """_SubtypeAssigner - see file-level docstring for detailed description.
+
+  The subtype is added as an annotation to the pytree token.
+  """
+
+  def Visit_dictsetmaker(self, node):  # pylint: disable=invalid-name
+    # dictsetmaker ::= (test ':' test (comp_for |
+    #                                   (',' test ':' test)* [','])) |
+    #                  (test (comp_for | (',' test)* [',']))
+    for child in node.children:
+      self.Visit(child)
+
+    comp_for = False
+    dict_maker = False
+
+    for child in node.children:
+      if pytree_utils.NodeName(child) == 'comp_for':
+        comp_for = True
+        _AppendFirstLeafTokenSubtype(child,
+                                     format_token.Subtype.DICT_SET_GENERATOR)
+      elif pytree_utils.NodeName(child) in ('COLON', 'DOUBLESTAR'):
+        dict_maker = True
+
+    if not comp_for and dict_maker:
+      last_was_colon = False
+      for child in node.children:
+        if dict_maker:
+          if pytree_utils.NodeName(child) == 'DOUBLESTAR':
+            _AppendFirstLeafTokenSubtype(child,
+                                         format_token.Subtype.KWARGS_STAR_STAR)
+          if last_was_colon:
+            if style.Get('INDENT_DICTIONARY_VALUE'):
+              _InsertPseudoParentheses(child)
+            else:
+              _AppendFirstLeafTokenSubtype(
+                  child, format_token.Subtype.DICTIONARY_VALUE)
+          elif (
+              child is not None and
+              (isinstance(child, pytree.Node) or
+               (not child.value.startswith('#') and child.value not in '{:,'))):
+            # Mark the first leaf of a key entry as a DICTIONARY_KEY. We
+            # normally want to split before them if the dictionary cannot exist
+            # on a single line.
+            _AppendFirstLeafTokenSubtype(child,
+                                         format_token.Subtype.DICTIONARY_KEY)
+            _AppendSubtypeRec(child, format_token.Subtype.DICTIONARY_KEY_PART)
+        last_was_colon = pytree_utils.NodeName(child) == 'COLON'
+
+  def Visit_expr_stmt(self, node):  # pylint: disable=invalid-name
+    # expr_stmt ::= testlist_star_expr (augassign (yield_expr|testlist)
+    #               | ('=' (yield_expr|testlist_star_expr))*)
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == '=':
+        _AppendTokenSubtype(child, format_token.Subtype.ASSIGN_OPERATOR)
+
+  def Visit_or_test(self, node):  # pylint: disable=invalid-name
+    # or_test ::= and_test ('or' and_test)*
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == 'or':
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_and_test(self, node):  # pylint: disable=invalid-name
+    # and_test ::= not_test ('and' not_test)*
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == 'and':
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_not_test(self, node):  # pylint: disable=invalid-name
+    # not_test ::= 'not' not_test | comparison
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == 'not':
+        _AppendTokenSubtype(child, format_token.Subtype.UNARY_OPERATOR)
+
+  def Visit_comparison(self, node):  # pylint: disable=invalid-name
+    # comparison ::= expr (comp_op expr)*
+    # comp_op ::= '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not in'|'is'|'is not'
+    for child in node.children:
+      self.Visit(child)
+      if (isinstance(child, pytree.Leaf) and
+          child.value in {'<', '>', '==', '>=', '<=', '<>', '!=', 'in', 'is'}):
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+      elif pytree_utils.NodeName(child) == 'comp_op':
+        for grandchild in child.children:
+          _AppendTokenSubtype(grandchild, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_star_expr(self, node):  # pylint: disable=invalid-name
+    # star_expr ::= '*' expr
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == '*':
+        _AppendTokenSubtype(child, format_token.Subtype.UNARY_OPERATOR)
+
+  def Visit_expr(self, node):  # pylint: disable=invalid-name
+    # expr ::= xor_expr ('|' xor_expr)*
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == '|':
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_xor_expr(self, node):  # pylint: disable=invalid-name
+    # xor_expr ::= and_expr ('^' and_expr)*
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == '^':
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_and_expr(self, node):  # pylint: disable=invalid-name
+    # and_expr ::= shift_expr ('&' shift_expr)*
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == '&':
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_shift_expr(self, node):  # pylint: disable=invalid-name
+    # shift_expr ::= arith_expr (('<<'|'>>') arith_expr)*
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value in {'<<', '>>'}:
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_arith_expr(self, node):  # pylint: disable=invalid-name
+    # arith_expr ::= term (('+'|'-') term)*
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value in '+-':
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_term(self, node):  # pylint: disable=invalid-name
+    # term ::= factor (('*'|'/'|'%'|'//') factor)*
+    for child in node.children:
+      self.Visit(child)
+      if (isinstance(child, pytree.Leaf) and
+          child.value in {'*', '/', '%', '//'}):
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_factor(self, node):  # pylint: disable=invalid-name
+    # factor ::= ('+'|'-'|'~') factor | power
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value in '+-~':
+        _AppendTokenSubtype(child, format_token.Subtype.UNARY_OPERATOR)
+
+  def Visit_power(self, node):  # pylint: disable=invalid-name
+    # power ::= atom trailer* ['**' factor]
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == '**':
+        _AppendTokenSubtype(child, format_token.Subtype.BINARY_OPERATOR)
+
+  def Visit_trailer(self, node):  # pylint: disable=invalid-name
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value in '[]':
+        _AppendTokenSubtype(child, format_token.Subtype.SUBSCRIPT_BRACKET)
+
+  def Visit_subscript(self, node):  # pylint: disable=invalid-name
+    # subscript ::= test | [test] ':' [test] [sliceop]
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == ':':
+        _AppendTokenSubtype(child, format_token.Subtype.SUBSCRIPT_COLON)
+
+  def Visit_sliceop(self, node):  # pylint: disable=invalid-name
+    # sliceop ::= ':' [test]
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == ':':
+        _AppendTokenSubtype(child, format_token.Subtype.SUBSCRIPT_COLON)
+
+  def Visit_argument(self, node):  # pylint: disable=invalid-name
+    # argument ::=
+    #     test [comp_for] | test '=' test
+    self._ProcessArgLists(node)
+
+  def Visit_arglist(self, node):  # pylint: disable=invalid-name
+    # arglist ::=
+    #     (argument ',')* (argument [',']
+    #                     | '*' test (',' argument)* [',' '**' test]
+    #                     | '**' test)
+    self._ProcessArgLists(node)
+    _SetDefaultOrNamedAssignArgListSubtype(node)
+
+  def Visit_tname(self, node):  # pylint: disable=invalid-name
+    self._ProcessArgLists(node)
+    _SetDefaultOrNamedAssignArgListSubtype(node)
+
+  def Visit_decorator(self, node):  # pylint: disable=invalid-name
+    # decorator ::=
+    #     '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
+    for child in node.children:
+      if isinstance(child, pytree.Leaf) and child.value == '@':
+        _AppendTokenSubtype(child, subtype=format_token.Subtype.DECORATOR)
+      self.Visit(child)
+
+  def Visit_funcdef(self, node):  # pylint: disable=invalid-name
+    # funcdef ::=
+    #     'def' NAME parameters ['->' test] ':' suite
+    for child in node.children:
+      if pytree_utils.NodeName(child) == 'NAME' and child.value != 'def':
+        _AppendTokenSubtype(child, format_token.Subtype.FUNC_DEF)
+        break
+    for child in node.children:
+      self.Visit(child)
+
+  def Visit_typedargslist(self, node):  # pylint: disable=invalid-name
+    # typedargslist ::=
+    #     ((tfpdef ['=' test] ',')*
+    #          ('*' [tname] (',' tname ['=' test])* [',' '**' tname]
+    #           | '**' tname)
+    #     | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
+    self._ProcessArgLists(node)
+    _SetDefaultOrNamedAssignArgListSubtype(node)
+
+  def Visit_varargslist(self, node):  # pylint: disable=invalid-name
+    # varargslist ::=
+    #     ((vfpdef ['=' test] ',')*
+    #          ('*' [vname] (',' vname ['=' test])*  [',' '**' vname]
+    #           | '**' vname)
+    #      | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
+    self._ProcessArgLists(node)
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf) and child.value == '=':
+        _AppendTokenSubtype(child, format_token.Subtype.VARARGS_LIST)
+
+  def Visit_comp_for(self, node):  # pylint: disable=invalid-name
+    # comp_for ::= 'for' exprlist 'in' testlist_safe [comp_iter]
+    _AppendSubtypeRec(node, format_token.Subtype.COMP_FOR)
+    self.DefaultNodeVisit(node)
+
+  def Visit_comp_if(self, node):  # pylint: disable=invalid-name
+    # comp_if ::= 'if' old_test [comp_iter]
+    _AppendSubtypeRec(node, format_token.Subtype.COMP_IF)
+    self.DefaultNodeVisit(node)
+
+  def _ProcessArgLists(self, node):
+    """Common method for processing argument lists."""
+    for child in node.children:
+      self.Visit(child)
+      if isinstance(child, pytree.Leaf):
+        _AppendTokenSubtype(
+            child,
+            subtype=_ARGLIST_TOKEN_TO_SUBTYPE.get(child.value,
+                                                  format_token.Subtype.NONE))
+
+
+def _SetDefaultOrNamedAssignArgListSubtype(node):
+  """Set named assign subtype on elements in a arg list."""
+
+  def HasDefaultOrNamedAssignSubtype(node):
+    """Return True if the arg list has a named assign subtype."""
+    if isinstance(node, pytree.Leaf):
+      if (format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in
+          pytree_utils.GetNodeAnnotation(node, pytree_utils.Annotation.SUBTYPE,
+                                         set())):
+        return True
+      return False
+    has_subtype = False
+    for child in node.children:
+      if pytree_utils.NodeName(child) != 'arglist':
+        has_subtype |= HasDefaultOrNamedAssignSubtype(child)
+    return has_subtype
+
+  if HasDefaultOrNamedAssignSubtype(node):
+    for child in node.children:
+      if pytree_utils.NodeName(child) != 'COMMA':
+        _AppendFirstLeafTokenSubtype(
+            child, format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST)
+
+
+def _AppendTokenSubtype(node, subtype):
+  """Append the token's subtype only if it's not already set."""
+  pytree_utils.AppendNodeAnnotation(node, pytree_utils.Annotation.SUBTYPE,
+                                    subtype)
+
+
+def _AppendFirstLeafTokenSubtype(node, subtype):
+  """Append the first leaf token's subtypes."""
+  if isinstance(node, pytree.Leaf):
+    _AppendTokenSubtype(node, subtype)
+    return
+  _AppendFirstLeafTokenSubtype(node.children[0], subtype)
+
+
+def _AppendSubtypeRec(node, subtype, force=True):
+  """Append the leafs in the node to the given subtype."""
+  if isinstance(node, pytree.Leaf):
+    _AppendTokenSubtype(node, subtype)
+    return
+  for child in node.children:
+    _AppendSubtypeRec(child, subtype, force=force)
+
+
+def _InsertPseudoParentheses(node):
+  """Insert pseudo parentheses so that dicts can be formatted correctly."""
+  comment_node = None
+  if isinstance(node, pytree.Node):
+    if node.children[-1].type == token.COMMENT:
+      comment_node = node.children[-1].clone()
+      node.children[-1].remove()
+
+  first = _GetFirstLeafNode(node)
+  last = _GetLastLeafNode(node)
+
+  if first == last and first.type == token.COMMENT:
+    # A comment was inserted before the value, which is a pytree.Leaf.
+    # Encompass the dictionary's value into an ATOM node.
+    last = first.next_sibling
+    new_node = pytree.Node(syms.atom, [first.clone(), last.clone()])
+    node.replace(new_node)
+    node = new_node
+    last.remove()
+
+    first = _GetFirstLeafNode(node)
+    last = _GetLastLeafNode(node)
+
+  lparen = pytree.Leaf(
+      token.LPAR, u'(', context=('', (first.get_lineno(), first.column - 1)))
+  last_lineno = last.get_lineno()
+  if last.type == token.STRING and '\n' in last.value:
+    last_lineno += last.value.count('\n')
+
+  if last.type == token.STRING and '\n' in last.value:
+    last_column = len(last.value.split('\n')[-1]) + 1
+  else:
+    last_column = last.column + len(last.value) + 1
+  rparen = pytree.Leaf(
+      token.RPAR, u')', context=('', (last_lineno, last_column)))
+
+  lparen.is_pseudo = True
+  rparen.is_pseudo = True
+
+  if isinstance(node, pytree.Node):
+    node.insert_child(0, lparen)
+    node.append_child(rparen)
+    if comment_node:
+      node.append_child(comment_node)
+    _AppendFirstLeafTokenSubtype(node, format_token.Subtype.DICTIONARY_VALUE)
+  else:
+    clone = node.clone()
+    new_node = pytree.Node(syms.atom, [lparen, clone, rparen])
+    node.replace(new_node)
+    _AppendFirstLeafTokenSubtype(clone, format_token.Subtype.DICTIONARY_VALUE)
+
+
+def _GetFirstLeafNode(node):
+  if isinstance(node, pytree.Leaf):
+    return node
+  return _GetFirstLeafNode(node.children[0])
+
+
+def _GetLastLeafNode(node):
+  if isinstance(node, pytree.Leaf):
+    return node
+  return _GetLastLeafNode(node.children[-1])
diff --git a/src/tools/yapf/yapf/yapflib/unwrapped_line.py b/src/tools/yapf/yapf/yapflib/unwrapped_line.py
new file mode 100644
index 0000000..dc782a9
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/unwrapped_line.py
@@ -0,0 +1,497 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""UnwrappedLine primitive for formatting.
+
+An unwrapped line is the containing data structure produced by the parser. It
+collects all nodes (stored in FormatToken objects) that could appear on a
+single line if there were no line length restrictions. It's then used by the
+parser to perform the wrapping required to comply with the style guide.
+"""
+
+from yapf.yapflib import format_token
+from yapf.yapflib import py3compat
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import split_penalty
+from yapf.yapflib import style
+
+
+class UnwrappedLine(object):
+  """Represents a single unwrapped line in the output.
+
+  Attributes:
+    depth: indentation depth of this line. This is just a numeric value used to
+      distinguish lines that are more deeply nested than others. It is not the
+      actual amount of spaces, which is style-dependent.
+  """
+
+  def __init__(self, depth, tokens=None):
+    """Constructor.
+
+    Creates a new unwrapped line with the given depth an initial list of tokens.
+    Constructs the doubly-linked lists for format tokens using their built-in
+    next_token and previous_token attributes.
+
+    Arguments:
+      depth: indentation depth of this line
+      tokens: initial list of tokens
+    """
+    self.depth = depth
+    self._tokens = tokens or []
+    self.disable = False
+
+    if self._tokens:
+      # Set up a doubly linked list.
+      for index, tok in enumerate(self._tokens[1:]):
+        # Note, 'index' is the index to the previous token.
+        tok.previous_token = self._tokens[index]
+        self._tokens[index].next_token = tok
+
+  def CalculateFormattingInformation(self):
+    """Calculate the split penalty and total length for the tokens."""
+    # Say that the first token in the line should have a space before it. This
+    # means only that if this unwrapped line is joined with a predecessor line,
+    # then there will be a space between them.
+    self.first.spaces_required_before = 1
+    self.first.total_length = len(self.first.value)
+
+    prev_token = self.first
+    prev_length = self.first.total_length
+    for token in self._tokens[1:]:
+      if (token.spaces_required_before == 0 and
+          _SpaceRequiredBetween(prev_token, token)):
+        token.spaces_required_before = 1
+
+      tok_len = len(token.value) if not token.is_pseudo_paren else 0
+      token.total_length = prev_length + tok_len + token.spaces_required_before
+
+      # The split penalty has to be computed before {must|can}_break_before,
+      # because these may use it for their decision.
+      token.split_penalty += _SplitPenalty(prev_token, token)
+      token.must_break_before = _MustBreakBefore(prev_token, token)
+      token.can_break_before = (token.must_break_before or
+                                _CanBreakBefore(prev_token, token))
+
+      prev_length = token.total_length
+      prev_token = token
+
+  ############################################################################
+  # Token Access and Manipulation Methods                                    #
+  ############################################################################
+
+  def AppendToken(self, token):
+    """Append a new FormatToken to the tokens contained in this line."""
+    if self._tokens:
+      token.previous_token = self.last
+      self.last.next_token = token
+    self._tokens.append(token)
+
+  def AppendNode(self, node):
+    """Convenience method to append a pytree node directly.
+
+    Wraps the node with a FormatToken.
+
+    Arguments:
+      node: the node to append
+    """
+    self.AppendToken(format_token.FormatToken(node))
+
+  @property
+  def first(self):
+    """Returns the first non-whitespace token."""
+    return self._tokens[0]
+
+  @property
+  def last(self):
+    """Returns the last non-whitespace token."""
+    return self._tokens[-1]
+
+  ############################################################################
+  # Token -> String Methods                                                  #
+  ############################################################################
+
+  def AsCode(self, indent_per_depth=2):
+    """Return a "code" representation of this line.
+
+    The code representation shows how the line would be printed out as code.
+
+    TODO(eliben): for now this is rudimentary for debugging - once we add
+    formatting capabilities, this method will have other uses (not all tokens
+    have spaces around them, for example).
+
+    Arguments:
+      indent_per_depth: how much spaces to indend per depth level.
+
+    Returns:
+      A string representing the line as code.
+    """
+    indent = ' ' * indent_per_depth * self.depth
+    tokens_str = ' '.join(tok.value for tok in self._tokens)
+    return indent + tokens_str
+
+  def __str__(self):  # pragma: no cover
+    return self.AsCode()
+
+  def __repr__(self):  # pragma: no cover
+    tokens_repr = ','.join(
+        ['{0}({1!r})'.format(tok.name, tok.value) for tok in self._tokens])
+    return 'UnwrappedLine(depth={0}, tokens=[{1}])'.format(
+        self.depth, tokens_repr)
+
+  ############################################################################
+  # Properties                                                               #
+  ############################################################################
+
+  @property
+  def tokens(self):
+    """Access the tokens contained within this line.
+
+    The caller must not modify the tokens list returned by this method.
+
+    Returns:
+      List of tokens in this line.
+    """
+    return self._tokens
+
+  @property
+  def lineno(self):
+    """Return the line number of this unwrapped line.
+
+    Returns:
+      The line number of the first token in this unwrapped line.
+    """
+    return self.first.lineno
+
+  @property
+  def is_comment(self):
+    return self.first.is_comment
+
+
+def _IsIdNumberStringToken(tok):
+  return tok.is_keyword or tok.is_name or tok.is_number or tok.is_string
+
+
+def _IsUnaryOperator(tok):
+  return format_token.Subtype.UNARY_OPERATOR in tok.subtypes
+
+
+def _SpaceRequiredBetween(left, right):
+  """Return True if a space is required between the left and right token."""
+  lval = left.value
+  rval = right.value
+  if (left.is_pseudo_paren and _IsIdNumberStringToken(right) and
+      left.previous_token and _IsIdNumberStringToken(left.previous_token)):
+    # Space between keyword... tokens and pseudo parens.
+    return True
+  if left.is_pseudo_paren or right.is_pseudo_paren:
+    # There should be a space after the ':' in a dictionary.
+    if left.OpensScope():
+      return True
+    # The closing pseudo-paren shouldn't affect spacing.
+    return False
+  if left.is_continuation or right.is_continuation:
+    # The continuation node's value has all of the spaces it needs.
+    return False
+  if right.name in pytree_utils.NONSEMANTIC_TOKENS:
+    # No space before a non-semantic token.
+    return False
+  if _IsIdNumberStringToken(left) and _IsIdNumberStringToken(right):
+    # Spaces between keyword, string, number, and identifier tokens.
+    return True
+  if lval == ',' and rval == ':':
+    # We do want a space between a comma and colon.
+    return True
+  if rval in ':,':
+    # Otherwise, we never want a space before a colon or comma.
+    return False
+  if lval == ',' and rval in ']})':
+    # Add a space between ending ',' and closing bracket if requested.
+    return style.Get('SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET')
+  if lval == ',':
+    # We want a space after a comma.
+    return True
+  if lval == 'from' and rval == '.':
+    # Space before the '.' in an import statement.
+    return True
+  if lval == '.' and rval == 'import':
+    # Space after the '.' in an import statement.
+    return True
+  if lval == '=' and rval == '.':
+    # Space between equal and '.' as in "X = ...".
+    return True
+  if ((right.is_keyword or right.is_name) and
+      (left.is_keyword or left.is_name)):
+    # Don't merge two keywords/identifiers.
+    return True
+  if left.is_string:
+    if (rval == '=' and format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST in
+        right.subtypes):
+      # If there is a type hint, then we don't want to add a space between the
+      # equal sign and the hint.
+      return False
+    if rval not in '[)]}.':
+      # A string followed by something other than a subscript, closing bracket,
+      # or dot should have a space after it.
+      return True
+  if left.is_binary_op and lval != '**' and _IsUnaryOperator(right):
+    # Space between the binary opertor and the unary operator.
+    return True
+  if _IsUnaryOperator(left) and _IsUnaryOperator(right):
+    # No space between two unary operators.
+    return False
+  if left.is_binary_op or right.is_binary_op:
+    if lval == '**' or rval == '**':
+      # Space around the "power" operator.
+      return style.Get('SPACES_AROUND_POWER_OPERATOR')
+    # Enforce spaces around binary operators.
+    return True
+  if (_IsUnaryOperator(left) and lval != 'not' and
+      (right.is_name or right.is_number or rval == '(')):
+    # The previous token was a unary op. No space is desired between it and
+    # the current token.
+    return False
+  if (format_token.Subtype.SUBSCRIPT_COLON in left.subtypes or
+      format_token.Subtype.SUBSCRIPT_COLON in right.subtypes):
+    # A subscript shouldn't have spaces separating its colons.
+    return False
+  if (format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in left.subtypes or
+      format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in right.subtypes):
+    # A named argument or default parameter shouldn't have spaces around it.
+    # However, a typed argument should have a space after the colon.
+    return lval == ':' or style.Get('SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN')
+  if (format_token.Subtype.VARARGS_LIST in left.subtypes or
+      format_token.Subtype.VARARGS_LIST in right.subtypes):
+    return False
+  if (format_token.Subtype.VARARGS_STAR in left.subtypes or
+      format_token.Subtype.KWARGS_STAR_STAR in left.subtypes):
+    # Don't add a space after a vararg's star or a keyword's star-star.
+    return False
+  if lval == '@' and format_token.Subtype.DECORATOR in left.subtypes:
+    # Decorators shouldn't be separated from the 'at' sign.
+    return False
+  if lval == '.' or rval == '.':
+    # Don't place spaces between dots.
+    return False
+  if ((lval == '(' and rval == ')') or (lval == '[' and rval == ']') or
+      (lval == '{' and rval == '}')):
+    # Empty objects shouldn't be separted by spaces.
+    return False
+  if (lval in pytree_utils.OPENING_BRACKETS and
+      rval in pytree_utils.OPENING_BRACKETS):
+    # Nested objects' opening brackets shouldn't be separated.
+    return False
+  if (lval in pytree_utils.CLOSING_BRACKETS and
+      rval in pytree_utils.CLOSING_BRACKETS):
+    # Nested objects' closing brackets shouldn't be separated.
+    return False
+  if lval in pytree_utils.CLOSING_BRACKETS and rval in '([':
+    # A call, set, dictionary, or subscript that has a call or subscript after
+    # it shouldn't have a space between them.
+    return False
+  if lval in pytree_utils.OPENING_BRACKETS and _IsIdNumberStringToken(right):
+    # Don't separate the opening bracket from the first item.
+    return False
+  if left.is_name and rval in '([':
+    # Don't separate a call or array access from the name.
+    return False
+  if rval in pytree_utils.CLOSING_BRACKETS:
+    # Don't separate the closing bracket from the last item.
+    # FIXME(morbo): This might be too permissive.
+    return False
+  if lval == 'print' and rval == '(':
+    # Special support for the 'print' function.
+    return False
+  if lval in pytree_utils.OPENING_BRACKETS and _IsUnaryOperator(right):
+    # Don't separate a unary operator from the opening bracket.
+    return False
+  if (lval in pytree_utils.OPENING_BRACKETS and
+      (format_token.Subtype.VARARGS_STAR in right.subtypes or
+       format_token.Subtype.KWARGS_STAR_STAR in right.subtypes)):
+    # Don't separate a '*' or '**' from the opening bracket.
+    return False
+  if rval == ';':
+    # Avoid spaces before a semicolon. (Why is there a semicolon?!)
+    return False
+  if lval == '(' and rval == 'await':
+    # Special support for the 'await' keyword. Don't separate the 'await'
+    # keyword from an opening paren.
+    return False
+  return True
+
+
+def _MustBreakBefore(prev_token, cur_token):
+  """Return True if a line break is required before the current token."""
+  if prev_token.is_comment:
+    # Must break if the previous token was a comment.
+    return True
+  if (cur_token.is_string and prev_token.is_string and
+      IsSurroundedByBrackets(cur_token)):
+    # We want consecutive strings to be on separate lines. This is a
+    # reasonable assumption, because otherwise they should have written them
+    # all on the same line, or with a '+'.
+    return True
+  return pytree_utils.GetNodeAnnotation(
+      cur_token.node, pytree_utils.Annotation.MUST_SPLIT, default=False)
+
+
+def _CanBreakBefore(prev_token, cur_token):
+  """Return True if a line break may occur before the current token."""
+  pval = prev_token.value
+  cval = cur_token.value
+  if py3compat.PY3:
+    if pval == 'yield' and cval == 'from':
+      # Don't break before a yield argument.
+      return False
+    if pval in {'async', 'await'} and cval in {'def', 'with', 'for'}:
+      # Don't break after sync keywords.
+      return False
+  if cur_token.split_penalty >= split_penalty.UNBREAKABLE:
+    return False
+  if pval == '@':
+    # Don't break right after the beginning of a decorator.
+    return False
+  if cval == ':':
+    # Don't break before the start of a block of code.
+    return False
+  if cval == ',':
+    # Don't break before a comma.
+    return False
+  if prev_token.is_name and cval == '(':
+    # Don't break in the middle of a function definition or call.
+    return False
+  if prev_token.is_name and cval == '[':
+    # Don't break in the middle of an array dereference.
+    return False
+  if prev_token.is_name and cval == '.':
+    # Don't break before the '.' in a dotted name.
+    return False
+  if cur_token.is_comment and prev_token.lineno == cur_token.lineno:
+    # Don't break a comment at the end of the line.
+    return False
+  if format_token.Subtype.UNARY_OPERATOR in prev_token.subtypes:
+    # Don't break after a unary token.
+    return False
+  return True
+
+
+def IsSurroundedByBrackets(tok):
+  """Return True if the token is surrounded by brackets."""
+  paren_count = 0
+  brace_count = 0
+  sq_bracket_count = 0
+  previous_token = tok.previous_token
+  while previous_token:
+    if previous_token.value == ')':
+      paren_count -= 1
+    elif previous_token.value == '}':
+      brace_count -= 1
+    elif previous_token.value == ']':
+      sq_bracket_count -= 1
+
+    if previous_token.value == '(':
+      if paren_count == 0:
+        return previous_token
+      paren_count += 1
+    elif previous_token.value == '{':
+      if brace_count == 0:
+        return previous_token
+      brace_count += 1
+    elif previous_token.value == '[':
+      if sq_bracket_count == 0:
+        return previous_token
+      sq_bracket_count += 1
+
+    previous_token = previous_token.previous_token
+  return None
+
+
+_LOGICAL_OPERATORS = frozenset({'and', 'or'})
+_BITWISE_OPERATORS = frozenset({'&', '|', '^'})
+_TERM_OPERATORS = frozenset({'*', '/', '%', '//'})
+
+
+def _SplitPenalty(prev_token, cur_token):
+  """Return the penalty for breaking the line before the current token."""
+  pval = prev_token.value
+  cval = cur_token.value
+  if pval == 'not':
+    return split_penalty.UNBREAKABLE
+
+  if cur_token.node_split_penalty > 0:
+    return cur_token.node_split_penalty
+
+  if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
+    # Prefer to split before 'and' and 'or'.
+    if pval in _LOGICAL_OPERATORS:
+      return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')
+    if cval in _LOGICAL_OPERATORS:
+      return 0
+  else:
+    # Prefer to split after 'and' and 'or'.
+    if pval in _LOGICAL_OPERATORS:
+      return 0
+    if cval in _LOGICAL_OPERATORS:
+      return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')
+
+  if style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'):
+    # Prefer to split before '&', '|', and '^'.
+    if pval in _BITWISE_OPERATORS:
+      return style.Get('SPLIT_PENALTY_BITWISE_OPERATOR')
+    if cval in _BITWISE_OPERATORS:
+      return 0
+  else:
+    # Prefer to split after '&', '|', and '^'.
+    if pval in _BITWISE_OPERATORS:
+      return 0
+    if cval in _BITWISE_OPERATORS:
+      return style.Get('SPLIT_PENALTY_BITWISE_OPERATOR')
+
+  if (format_token.Subtype.COMP_FOR in cur_token.subtypes or
+      format_token.Subtype.COMP_IF in cur_token.subtypes):
+    # We don't mind breaking before the 'for' or 'if' of a list comprehension.
+    return 0
+  if format_token.Subtype.UNARY_OPERATOR in prev_token.subtypes:
+    # Try not to break after a unary operator.
+    return style.Get('SPLIT_PENALTY_AFTER_UNARY_OPERATOR')
+  if pval == ',':
+    # Breaking after a comma is fine, if need be.
+    return 0
+  if prev_token.is_binary_op:
+    # We would rather not split after an equality operator.
+    return 20
+  if (format_token.Subtype.VARARGS_STAR in prev_token.subtypes or
+      format_token.Subtype.KWARGS_STAR_STAR in prev_token.subtypes):
+    # Don't split after a varargs * or kwargs **.
+    return split_penalty.UNBREAKABLE
+  if prev_token.OpensScope() and cval != '(':
+    # Slightly prefer
+    return style.Get('SPLIT_PENALTY_AFTER_OPENING_BRACKET')
+  if cval == ':':
+    # Don't split before a colon.
+    return split_penalty.UNBREAKABLE
+  if cval == '=':
+    # Don't split before an assignment.
+    return split_penalty.UNBREAKABLE
+  if (format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in prev_token.subtypes or
+      format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in cur_token.subtypes):
+    # Don't break before or after an default or named assignment.
+    return split_penalty.UNBREAKABLE
+  if cval == '==':
+    # We would rather not split before an equality operator.
+    return split_penalty.STRONGLY_CONNECTED
+  if cur_token.ClosesScope():
+    # Give a slight penalty for splitting before the closing scope.
+    return 100
+  if pval in _TERM_OPERATORS or cval in _TERM_OPERATORS:
+    return 50
+  return 0
diff --git a/src/tools/yapf/yapf/yapflib/verifier.py b/src/tools/yapf/yapf/yapflib/verifier.py
new file mode 100644
index 0000000..b16aefb
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/verifier.py
@@ -0,0 +1,93 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Verify that the generated code is valid code.
+
+This takes a line of code and "normalizes" it. I.e., it transforms the snippet
+into something that has the potential to compile.
+
+    VerifyCode(): the main function exported by this module.
+"""
+
+import ast
+import re
+import sys
+import textwrap
+
+
+class InternalError(Exception):
+  """Internal error in verifying formatted code."""
+  pass
+
+
+def VerifyCode(code):
+  """Verify that the reformatted code is syntactically correct.
+
+  Arguments:
+    code: (unicode) The reformatted code snippet.
+
+  Raises:
+    SyntaxError if the code was reformatted incorrectly.
+  """
+  try:
+    compile(textwrap.dedent(code).encode('UTF-8'), '<string>', 'exec')
+  except SyntaxError:
+    try:
+      ast.parse(textwrap.dedent(code.lstrip('\n')).lstrip(), '<string>', 'exec')
+    except SyntaxError:
+      try:
+        normalized_code = _NormalizeCode(code)
+        compile(normalized_code.encode('UTF-8'), '<string>', 'exec')
+      except SyntaxError:
+        raise InternalError(sys.exc_info()[1])
+
+
+def _NormalizeCode(code):
+  """Make sure that the code snippet is compilable."""
+  code = textwrap.dedent(code.lstrip('\n')).lstrip()
+
+  # Split the code to lines and get rid of all leading full-comment lines as
+  # they can mess up the normalization attempt.
+  lines = code.split('\n')
+  i = 0
+  for i, line in enumerate(lines):
+    line = line.strip()
+    if line and not line.startswith('#'):
+      break
+  code = '\n'.join(lines[i:]) + '\n'
+
+  if re.match(r'(if|while|for|with|def|class|async|await)\b', code):
+    code += '\n    pass'
+  elif re.match(r'(elif|else)\b', code):
+    try:
+      try_code = 'if True:\n    pass\n' + code + '\n    pass'
+      ast.parse(
+          textwrap.dedent(try_code.lstrip('\n')).lstrip(), '<string>', 'exec')
+      code = try_code
+    except SyntaxError:
+      # The assumption here is that the code is on a single line.
+      code = 'if True: pass\n' + code
+  elif code.startswith('@'):
+    code += '\ndef _():\n    pass'
+  elif re.match(r'try\b', code):
+    code += '\n    pass\nexcept:\n    pass'
+  elif re.match(r'(except|finally)\b', code):
+    code = 'try:\n    pass\n' + code + '\n    pass'
+  elif re.match(r'(return|yield)\b', code):
+    code = 'def _():\n    ' + code
+  elif re.match(r'(continue|break)\b', code):
+    code = 'while True:\n    ' + code
+  elif re.match(r'print\b', code):
+    code = 'from __future__ import print_function\n' + code
+
+  return code + '\n'
diff --git a/src/tools/yapf/yapf/yapflib/yapf_api.py b/src/tools/yapf/yapf/yapflib/yapf_api.py
new file mode 100644
index 0000000..dd91849
--- /dev/null
+++ b/src/tools/yapf/yapf/yapflib/yapf_api.py
@@ -0,0 +1,295 @@
+# Copyright 2015-2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Entry points for YAPF.
+
+The main APIs that YAPF exposes to drive the reformatting.
+
+  FormatFile(): reformat a file.
+  FormatCode(): reformat a string of code.
+
+These APIs have some common arguments:
+
+  style_config: (string) Either a style name or a path to a file that contains
+    formatting style settings. If None is specified, use the default style
+    as set in style.DEFAULT_STYLE_FACTORY
+  lines: (list of tuples of integers) A list of tuples of lines, [start, end],
+    that we want to format. The lines are 1-based indexed. It can be used by
+    third-party code (e.g., IDEs) when reformatting a snippet of code rather
+    than a whole file.
+  print_diff: (bool) Instead of returning the reformatted source, return a
+    diff that turns the formatted source into reformatter source.
+  verify: (bool) True if reformatted code should be verified for syntax.
+"""
+
+import difflib
+import re
+import sys
+
+from lib2to3.pgen2 import tokenize
+
+from yapf.yapflib import blank_line_calculator
+from yapf.yapflib import comment_splicer
+from yapf.yapflib import continuation_splicer
+from yapf.yapflib import file_resources
+from yapf.yapflib import py3compat
+from yapf.yapflib import pytree_unwrapper
+from yapf.yapflib import pytree_utils
+from yapf.yapflib import reformatter
+from yapf.yapflib import split_penalty
+from yapf.yapflib import style
+from yapf.yapflib import subtype_assigner
+
+
+def FormatFile(filename,
+               style_config=None,
+               lines=None,
+               print_diff=False,
+               verify=False,
+               in_place=False,
+               logger=None):
+  """Format a single Python file and return the formatted code.
+
+  Arguments:
+    filename: (unicode) The file to reformat.
+    in_place: (bool) If True, write the reformatted code back to the file.
+    logger: (io streamer) A stream to output logging.
+    remaining arguments: see comment at the top of this module.
+
+  Returns:
+    Tuple of (reformatted_code, encoding, changed). reformatted_code is None if
+    the file is sucessfully written to (having used in_place). reformatted_code
+    is a diff if print_diff is True.
+
+  Raises:
+    IOError: raised if there was an error reading the file.
+    ValueError: raised if in_place and print_diff are both specified.
+  """
+  _CheckPythonVersion()
+
+  if in_place and print_diff:
+    raise ValueError('Cannot pass both in_place and print_diff.')
+
+  original_source, newline, encoding = ReadFile(filename, logger)
+  reformatted_source, changed = FormatCode(
+      original_source,
+      style_config=style_config,
+      filename=filename,
+      lines=lines,
+      print_diff=print_diff,
+      verify=verify)
+  if reformatted_source.rstrip('\n'):
+    lines = reformatted_source.rstrip('\n').split('\n')
+    reformatted_source = newline.join(line for line in lines) + newline
+  if in_place:
+    if original_source and original_source != reformatted_source:
+      file_resources.WriteReformattedCode(filename, reformatted_source,
+                                          in_place, encoding)
+    return None, encoding, changed
+
+  return reformatted_source, encoding, changed
+
+
+def FormatCode(unformatted_source,
+               filename='<unknown>',
+               style_config=None,
+               lines=None,
+               print_diff=False,
+               verify=False):
+  """Format a string of Python code.
+
+  This provides an alternative entry point to YAPF.
+
+  Arguments:
+    unformatted_source: (unicode) The code to format.
+    filename: (unicode) The name of the file being reformatted.
+    remaining arguments: see comment at the top of this module.
+
+  Returns:
+    Tuple of (reformatted_source, changed). reformatted_source conforms to the
+    desired formatting style. changed is True if the source changed.
+  """
+  _CheckPythonVersion()
+  style.SetGlobalStyle(style.CreateStyleFromConfig(style_config))
+  if not unformatted_source.endswith('\n'):
+    unformatted_source += '\n'
+  tree = pytree_utils.ParseCodeToTree(unformatted_source)
+
+  # Run passes on the tree, modifying it in place.
+  comment_splicer.SpliceComments(tree)
+  continuation_splicer.SpliceContinuations(tree)
+  subtype_assigner.AssignSubtypes(tree)
+  split_penalty.ComputeSplitPenalties(tree)
+  blank_line_calculator.CalculateBlankLines(tree)
+
+  uwlines = pytree_unwrapper.UnwrapPyTree(tree)
+  for uwl in uwlines:
+    uwl.CalculateFormattingInformation()
+
+  _MarkLinesToFormat(uwlines, lines)
+  reformatted_source = reformatter.Reformat(uwlines, verify)
+
+  if unformatted_source == reformatted_source:
+    return '' if print_diff else reformatted_source, False
+
+  code_diff = _GetUnifiedDiff(
+      unformatted_source, reformatted_source, filename=filename)
+
+  if print_diff:
+    return code_diff, code_diff != ''
+
+  return reformatted_source, True
+
+
+def _CheckPythonVersion():  # pragma: no cover
+  errmsg = 'yapf is only supported for Python 2.7 or 3.4+'
+  if sys.version_info[0] == 2:
+    if sys.version_info[1] < 7:
+      raise RuntimeError(errmsg)
+  elif sys.version_info[0] == 3:
+    if sys.version_info[1] < 4:
+      raise RuntimeError(errmsg)
+
+
+def ReadFile(filename, logger=None):
+  """Read the contents of the file.
+
+  An optional logger can be specified to emit messages to your favorite logging
+  stream. If specified, then no exception is raised. This is external so that it
+  can be used by third-party applications.
+
+  Arguments:
+    filename: (unicode) The name of the file.
+    logger: (function) A function or lambda that takes a string and emits it.
+
+  Returns:
+    The contents of filename.
+
+  Raises:
+    IOError: raised if there was an error reading the file.
+  """
+  try:
+    with open(filename, 'rb') as fd:
+      encoding = tokenize.detect_encoding(fd.readline)[0]
+  except IOError as err:
+    if logger:
+      logger(err)
+    raise
+
+  try:
+    # Preserves line endings.
+    with py3compat.open_with_encoding(
+        filename, mode='r', encoding=encoding, newline='') as fd:
+      lines = fd.readlines()
+
+    line_ending = file_resources.LineEnding(lines)
+    source = '\n'.join(line.rstrip('\r\n') for line in lines) + '\n'
+    return source, line_ending, encoding
+  except IOError as err:  # pragma: no cover
+    if logger:
+      logger(err)
+    raise
+
+
+DISABLE_PATTERN = r'^#.*\byapf:\s*disable\b'
+ENABLE_PATTERN = r'^#.*\byapf:\s*enable\b'
+
+
+def _MarkLinesToFormat(uwlines, lines):
+  """Skip sections of code that we shouldn't reformat."""
+  if lines:
+    for uwline in uwlines:
+      uwline.disable = True
+
+    # Sort and combine overlapping ranges.
+    lines = sorted(lines)
+    line_ranges = [lines[0]] if len(lines[0]) else []
+    index = 1
+    while index < len(lines):
+      current = line_ranges[-1]
+      if lines[index][0] <= current[1]:
+        # The ranges overlap, so combine them.
+        line_ranges[-1] = (current[0], max(lines[index][1], current[1]))
+      else:
+        line_ranges.append(lines[index])
+      index += 1
+
+    # Mark lines to format as not being disabled.
+    index = 0
+    for start, end in sorted(line_ranges):
+      while index < len(uwlines) and uwlines[index].last.lineno < start:
+        index += 1
+      if index >= len(uwlines):
+        break
+
+      while index < len(uwlines):
+        if uwlines[index].lineno > end:
+          break
+        if (uwlines[index].lineno >= start or
+            uwlines[index].last.lineno >= start):
+          uwlines[index].disable = False
+        index += 1
+
+  # Now go through the lines and disable any lines explicitly marked as
+  # disabled.
+  index = 0
+  while index < len(uwlines):
+    uwline = uwlines[index]
+    if uwline.is_comment:
+      if _DisableYAPF(uwline.first.value.strip()):
+        index += 1
+        while index < len(uwlines):
+          uwline = uwlines[index]
+          if uwline.is_comment and _EnableYAPF(uwline.first.value.strip()):
+            break
+          uwline.disable = True
+          index += 1
+    elif re.search(DISABLE_PATTERN, uwline.last.value.strip(), re.IGNORECASE):
+      uwline.disable = True
+    index += 1
+
+
+def _DisableYAPF(line):
+  return (
+      re.search(DISABLE_PATTERN, line.split('\n')[0].strip(), re.IGNORECASE) or
+      re.search(DISABLE_PATTERN, line.split('\n')[-1].strip(), re.IGNORECASE))
+
+
+def _EnableYAPF(line):
+  return (
+      re.search(ENABLE_PATTERN, line.split('\n')[0].strip(), re.IGNORECASE) or
+      re.search(ENABLE_PATTERN, line.split('\n')[-1].strip(), re.IGNORECASE))
+
+
+def _GetUnifiedDiff(before, after, filename='code'):
+  """Get a unified diff of the changes.
+
+  Arguments:
+    before: (unicode) The original source code.
+    after: (unicode) The reformatted source code.
+    filename: (unicode) The code's filename.
+
+  Returns:
+    The unified diff text.
+  """
+  before = before.splitlines()
+  after = after.splitlines()
+  return '\n'.join(
+      difflib.unified_diff(
+          before,
+          after,
+          filename,
+          filename,
+          '(original)',
+          '(reformatted)',
+          lineterm='')) + '\n'
diff --git a/src/tools/yapf_util.py b/src/tools/yapf_util.py
new file mode 100644
index 0000000..c5add7e
--- /dev/null
+++ b/src/tools/yapf_util.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file
+
+from __future__ import absolute_import
+from __future__ import print_function
+from exec_util import exec_cmd
+import os
+import sys
+
+# Script directory.
+script_dir = os.path.dirname(__file__)
+root_dir = os.path.join(script_dir, os.pardir)
+
+
+def yapf_format(file_name, file_contents):
+  # Reads .style.yapf in the root_dir when specifying contents via stdin.
+  result = exec_cmd("%s %s/yapf" % (sys.executable, script_dir), root_dir,
+                    file_contents.encode('utf-8'))
+  if result['err'] != '':
+    print("yapf error: %s" % result['err'])
+  if result['out'] != '':
+    output = result['out']
+    if sys.platform == 'win32':
+      # Convert to Unix line endings.
+      output = output.replace("\r", "")
+    return output
+  return None
diff --git a/tools/automate-git.py b/tools/automate-git.py
new file mode 100644
index 0000000..d878e53
--- /dev/null
+++ b/tools/automate-git.py
@@ -0,0 +1,1740 @@
+# Based on ef7e98c6169e48023315ccdc14e0215669c2ca6c
+
+# Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+# reserved. Use of this source code is governed by a BSD-style license that
+# can be found in the LICENSE file.
+
+from __future__ import absolute_import
+from __future__ import print_function
+import ast
+from datetime import datetime
+import json
+from io import open
+from optparse import OptionParser
+import os
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import tempfile
+import zipfile
+
+is_python2 = sys.version_info.major == 2
+
+if is_python2:
+  from urllib import FancyURLopener
+  from urllib2 import urlopen
+else:
+  from urllib.request import FancyURLopener, urlopen
+
+##
+# Default URLs.
+##
+
+depot_tools_url = 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
+depot_tools_archive_url = 'https://storage.googleapis.com/chrome-infra/depot_tools.zip'
+
+cef_git_url = 'https://bitbucket.org/chromiumembedded/cef.git'
+
+chromium_channel_json_url = 'https://omahaproxy.appspot.com/all.json'
+
+##
+# Global system variables.
+##
+
+# Script directory.
+script_dir = os.path.dirname(__file__)
+
+##
+# Helper functions.
+##
+
+
+def msg(message):
+  """ Output a message. """
+  sys.stdout.write('--> ' + message + "\n")
+
+
+def run(command_line, working_dir, depot_tools_dir=None, output_file=None):
+  """ Runs the specified command. """
+  # add depot_tools to the path
+  env = os.environ
+  if not depot_tools_dir is None:
+    env['PATH'] = depot_tools_dir + os.pathsep + env['PATH']
+
+  sys.stdout.write('-------- Running "'+command_line+'" in "'+\
+                   working_dir+'"...'+"\n")
+  if not options.dryrun:
+    args = shlex.split(command_line.replace('\\', '\\\\'))
+
+    if not output_file:
+      return subprocess.check_call(
+          args, cwd=working_dir, env=env, shell=(sys.platform == 'win32'))
+    try:
+      msg('Writing %s' % output_file)
+      with open(output_file, 'w', encoding='utf-8') as fp:
+        return subprocess.check_call(
+            args,
+            cwd=working_dir,
+            env=env,
+            shell=(sys.platform == 'win32'),
+            stderr=subprocess.STDOUT,
+            stdout=fp)
+    except subprocess.CalledProcessError:
+      msg('ERROR Run failed. See %s for output.' % output_file)
+      raise
+
+
+def create_directory(path):
+  """ Creates a directory if it doesn't already exist. """
+  if not os.path.exists(path):
+    msg("Creating directory %s" % (path))
+    if not options.dryrun:
+      os.makedirs(path)
+
+
+def delete_directory(path):
+  """ Removes an existing directory. """
+  if os.path.exists(path):
+    msg("Removing directory %s" % (path))
+    if not options.dryrun:
+      shutil.rmtree(path, onerror=onerror)
+
+
+def copy_directory(source, target, allow_overwrite=False):
+  """ Copies a directory from source to target. """
+  if not options.dryrun and os.path.exists(target):
+    if not allow_overwrite:
+      raise Exception("Directory %s already exists" % (target))
+    remove_directory(target)
+  if os.path.exists(source):
+    msg("Copying directory %s to %s" % (source, target))
+    if not options.dryrun:
+      shutil.copytree(source, target)
+
+
+def move_directory(source, target, allow_overwrite=False):
+  """ Copies a directory from source to target. """
+  if not options.dryrun and os.path.exists(target):
+    if not allow_overwrite:
+      raise Exception("Directory %s already exists" % (target))
+    remove_directory(target)
+  if os.path.exists(source):
+    msg("Moving directory %s to %s" % (source, target))
+    if not options.dryrun:
+      shutil.move(source, target)
+
+
+def is_git_checkout(path):
+  """ Returns true if the path represents a git checkout. """
+  return os.path.exists(os.path.join(path, '.git'))
+
+
+def exec_cmd(cmd, path):
+  """ Execute the specified command and return the result. """
+  out = ''
+  err = ''
+  sys.stdout.write("-------- Running \"%s\" in \"%s\"...\n" % (cmd, path))
+  parts = cmd.split()
+  try:
+    process = subprocess.Popen(
+        parts,
+        cwd=path,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        shell=(sys.platform == 'win32'))
+    out, err = process.communicate()
+  except IOError as e:
+    (errno, strerror) = e.args
+    raise
+  except:
+    raise
+  return {'out': out.decode('utf-8'), 'err': err.decode('utf-8')}
+
+
+def get_git_hash(path, branch):
+  """ Returns the git hash for the specified branch/tag/hash. """
+  cmd = "%s rev-parse %s" % (git_exe, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return result['out'].strip()
+  return 'Unknown'
+
+
+def get_git_date(path, branch):
+  """ Returns the date for the specified branch/tag/hash. """
+  cmd = "%s show -s --format=%%ct %s" % (git_exe, branch)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return datetime.utcfromtimestamp(
+        int(result['out'].strip())).strftime('%Y-%m-%d %H:%M:%S UTC')
+  return 'Unknown'
+
+
+def get_git_url(path):
+  """ Returns the origin url for the specified path. """
+  cmd = "%s config --get remote.origin.url" % (git_exe)
+  result = exec_cmd(cmd, path)
+  if result['out'] != '':
+    return result['out'].strip()
+  return 'Unknown'
+
+
+def download_and_extract(src, target):
+  """ Extracts the contents of src, which may be a URL or local file, to the
+      target directory. """
+  temporary = False
+
+  if src[:4] == 'http':
+    # Attempt to download a URL.
+    opener = FancyURLopener({})
+    response = opener.open(src)
+
+    temporary = True
+    handle, archive_path = tempfile.mkstemp(suffix='.zip')
+    os.write(handle, response.read())
+    os.close(handle)
+  elif os.path.exists(src):
+    # Use a local file.
+    archive_path = src
+  else:
+    raise Exception('Path type is unsupported or does not exist: ' + src)
+
+  if not zipfile.is_zipfile(archive_path):
+    raise Exception('Not a valid zip archive: ' + src)
+
+  # Attempt to extract the archive file.
+  try:
+    os.makedirs(target)
+    zf = zipfile.ZipFile(archive_path, 'r')
+    zf.extractall(target)
+  except:
+    shutil.rmtree(target, onerror=onerror)
+    raise
+  zf.close()
+
+  # Delete the archive file if temporary.
+  if temporary and os.path.exists(archive_path):
+    os.remove(archive_path)
+
+
+def read_file(path):
+  """ Read a file. """
+  if os.path.exists(path):
+    with open(path, 'r', encoding='utf-8') as fp:
+      return fp.read()
+  else:
+    raise Exception("Path does not exist: %s" % (path))
+
+
+def write_fp(fp, data):
+  if is_python2:
+    fp.write(data.decode('utf-8'))
+  else:
+    fp.write(data)
+
+
+def write_file(path, data):
+  """ Write a file. """
+  msg('Writing %s' % path)
+  if not options.dryrun:
+    with open(path, 'w', encoding='utf-8') as fp:
+      write_fp(fp, data)
+
+
+def read_config_file(path):
+  """ Read a configuration file. """
+  # Parse the contents.
+  return ast.literal_eval(read_file(path))
+
+
+def write_config_file(path, contents):
+  """ Write a configuration file. """
+  data = "{\n"
+  for key in sorted(contents.keys()):
+    data += "  '%s': '%s',\n" % (key, contents[key])
+  data += "}\n"
+  write_file(path, data)
+
+
+def read_branch_config_file(path):
+  """ Read the CEF branch from the specified path. """
+  config_file = os.path.join(path, 'cef.branch')
+  if os.path.isfile(config_file):
+    contents = read_config_file(config_file)
+    if 'branch' in contents:
+      return contents['branch']
+  return ''
+
+
+def write_branch_config_file(path, branch):
+  """ Write the CEF branch to the specified path. """
+  config_file = os.path.join(path, 'cef.branch')
+  if not os.path.isfile(config_file):
+    write_config_file(config_file, {'branch': branch})
+
+
+def apply_patch(name):
+  patch_file = os.path.join(cef_dir, 'patch', 'patches', name)
+  if os.path.exists(patch_file + ".patch"):
+    # Attempt to apply the patch file.
+    patch_tool = os.path.join(cef_dir, 'tools', 'patcher.py')
+    run('%s %s --patch-file "%s" --patch-dir "%s"' %
+        (python_exe, patch_tool, patch_file,
+         chromium_src_dir), chromium_src_dir, depot_tools_dir)
+
+
+def apply_deps_patch():
+  """ Patch the Chromium DEPS file before `gclient sync` if necessary. """
+  deps_path = os.path.join(chromium_src_dir, deps_file)
+  if os.path.isfile(deps_path):
+    msg("Chromium DEPS file: %s" % (deps_path))
+    apply_patch(deps_file)
+  else:
+    raise Exception("Path does not exist: %s" % (deps_path))
+
+
+def apply_runhooks_patch():
+  """ Patch the Chromium runhooks files before `gclient runhooks` if necessary. """
+  apply_patch('runhooks')
+
+
+def run_patch_updater(args='', output_file=None):
+  """ Run the patch updater script. """
+  tool = os.path.join(cef_src_dir, 'tools', 'patch_updater.py')
+  if len(args) > 0:
+    args = ' ' + args
+  run('%s %s%s' % (python_exe, tool, args), cef_src_dir, depot_tools_dir,
+      output_file)
+
+
+def replace_in_file(pattern, repl, path, count=0, flags=0):
+  """ Replaces a RE pattern in a file. """
+  with open(path, 'r') as fp:
+    content = fp.read()
+  content = re.sub(pattern, repl, content, count=count, flags=flags)
+  with open(path, 'w') as fp:
+    fp.write(content)
+
+
+def add_google_patches():
+  """ Hooks the build to also apply Google private patches to CEF. """
+  # Root location of Google-specific patches.
+  google_patch_path = os.path.abspath(
+      os.path.join(os.path.dirname(__file__), '..', 'patch'))
+
+  # Append a script to patch.cfg that will register Google-specific patches.
+  # This file is a Python script, even though it has a .cfg extension. It sets
+  # up a global list (`patches`) that contains information about each patch that
+  # CEF applies during the build process. We append a script fragment to the end
+  # of this file to extend the global list of patches with Google-specific ones.
+  cef_patch_config_path = os.path.join(cef_dir, 'patch', 'patch.cfg')
+  with open(cef_patch_config_path, 'a') as fp:
+    fp.write(u"""
+# Google-specific code starts here.
+import os
+
+# Location of Google-specific patches.
+google_patch_path = %s
+google_patch_config_file = os.path.join(google_patch_path, 'patch.cfg')
+
+def register_google_patches(*args):
+  for arg in args:
+    # The CEF scripts that manipulate patches append .patch to 'name' and then
+    # look for a file with that name in the CEF patches directory. However, if
+    # 'name' is an absolute path, those scripts will look for a file at that
+    # absolute path instead (after appending .patch to it). Since the Google-
+    # specific patches are not in the CEF patches directory, prefix the
+    # name here with google_patch_path (which is already an absolute path).
+    arg['name'] = os.path.join(google_patch_path, 'patches', arg['name'])
+    patches.append(arg)
+
+# Execute the Google-specific patch config file in the current scope. The config
+# file will call register_google_patches one or more times to register patches.
+if os.path.isfile(google_patch_config_file):
+  execfile(google_patch_config_file)
+# Google-specific code ends here.
+""" % repr(google_patch_path))
+
+  # Add new gclient hooks to run the make_version_header.py and translator.py
+  # tools to generate a new CEF version header and a new CEF C API after
+  # applying all patches. This is needed because the Google-specific patches may
+  # change the version header or CEF API, but we do not want the patches to
+  # themselves contain the updated version header or C API code. Including the
+  # generated C API in particular in the patches would cause conflicts on every
+  # CEF update because an API hash is generated and written into the generated
+  # files.
+  gclient_hook_path = os.path.join(cef_dir, 'tools', 'gclient_hook.py')
+  new_gclient_hook = u"""# Google-specific code starts here.
+print("\\\\nGenerating CEF version header file (again)...")
+cmd = [
+    'python', 'tools/make_version_header.py', 'include/cef_version.h'
+]
+RunAction(cef_dir, cmd)
+
+print("\\\\nGenerating CAPI...")
+cmd = ['python', 'tools/translator.py', '--root-dir', cef_dir]
+RunAction(cef_dir, cmd)
+# Google-specific code ends here.
+
+"""
+  replace_in_file(
+      r'(?=^print\("\\nGenerating CEF project files\.\.\."\)$)',
+      new_gclient_hook,
+      gclient_hook_path,
+      flags=re.MULTILINE)
+
+
+def remove_google_patches():
+  """ Unhooks the build to stop applying Google private patches to CEF. """
+  pattern = re.compile(r'(\r?\n){,2}\# Google-specific code starts here\..*?'
+                       r'^\# Google-specific code ends here\.',
+                       flags=re.DOTALL | re.MULTILINE)
+
+  cef_patch_config_path = os.path.join(cef_dir, 'patch', 'patch.cfg')
+  replace_in_file(pattern, '', cef_patch_config_path)
+
+  gclient_hook_path = os.path.join(cef_dir, 'tools', 'gclient_hook.py')
+  replace_in_file(pattern, '', gclient_hook_path)
+
+
+def onerror(func, path, exc_info):
+  """
+  Error handler for ``shutil.rmtree``.
+
+  If the error is due to an access error (read only file)
+  it attempts to add write permission and then retries.
+
+  If the error is for another reason it re-raises the error.
+
+  Usage : ``shutil.rmtree(path, onerror=onerror)``
+  """
+  import stat
+  if not os.access(path, os.W_OK):
+    # Is the error an access error ?
+    os.chmod(path, stat.S_IWUSR)
+    func(path)
+  else:
+    raise
+
+
+def read_json_url(url):
+  """ Read a JSON URL. """
+  msg('Downloading %s' % url)
+  return json.loads(urlopen(url).read())
+
+
+g_channel_data = None
+
+
+def get_chromium_channel_data(os, channel, param=None):
+  """ Returns all data for the specified Chromium channel. """
+  global g_channel_data
+
+  if g_channel_data is None:
+    g_channel_data = read_json_url(chromium_channel_json_url)
+    assert len(g_channel_data) > 0, 'Failed to load Chromium channel data'
+
+  for oses in g_channel_data:
+    if oses['os'] == os:
+      for version in oses['versions']:
+        if version['channel'] == channel:
+          assert version['os'] == os
+          assert version['channel'] == channel
+          if param is None:
+            return version
+          else:
+            assert param in version, 'Missing parameter %s for Chromium channel %s %s' % (
+                param, os, channel)
+            return version[param]
+      raise Exception("Invalid Chromium channel value: %s" % channel)
+  raise Exception("Invalid Chromium os value: %s" % os)
+
+
+def get_chromium_channel_commit(os, channel):
+  """ Returns the current branch commit for the specified Chromium channel. """
+  return get_chromium_channel_data(os, channel, 'branch_commit')
+
+
+def get_chromium_channel_version(os, channel):
+  """ Returns the current version for the specified Chromium channel. """
+  return get_chromium_channel_data(os, channel, 'current_version')
+
+
+def get_chromium_master_position(commit):
+  """ Returns the closest master position for the specified Chromium commit. """
+  # Using -2 because a "Publish DEPS" commit which does not have a master
+  # position may be first.
+  cmd = "%s log -2 %s" % (git_exe, commit)
+  result = exec_cmd(cmd, chromium_src_dir)
+  if result['out'] != '':
+    match = re.search(r'refs/heads/master@{#([\d]+)}', result['out'])
+    assert match != None, 'Failed to find position'
+    return int(match.groups()[0])
+  return None
+
+
+def get_chromium_master_commit(position):
+  """ Returns the master commit for the specified Chromium commit position. """
+  cmd = '%s log -1 --grep=refs/heads/master@{#%s} origin/master' % (
+      git_exe, str(position))
+  result = exec_cmd(cmd, chromium_src_dir)
+  if result['out'] != '':
+    match = re.search(r'^commit ([a-f0-9]+)', result['out'])
+    assert match != None, 'Failed to find commit'
+    return match.groups()[0]
+  return None
+
+
+def get_chromium_versions(commit):
+  """ Returns the list of Chromium versions that contain the specified commit.
+      Versions are listed oldest to newest. """
+  cmd = '%s tag --contains %s' % (git_exe, commit)
+  result = exec_cmd(cmd, chromium_src_dir)
+  if result['out'] != '':
+    return [line.strip() for line in result['out'].strip().split('\n')]
+  return None
+
+
+def get_build_compat_versions():
+  """ Returns the compatible Chromium and (optionally) depot_tools versions
+      specified by the CEF checkout. """
+  compat_path = os.path.join(cef_dir, 'CHROMIUM_BUILD_COMPATIBILITY.txt')
+  msg("Reading %s" % compat_path)
+  config = read_config_file(compat_path)
+
+  if not 'chromium_checkout' in config:
+    raise Exception("Missing chromium_checkout value in %s" % (compat_path))
+  return config
+
+
+def get_chromium_target_version(os='win', channel='canary', target_distance=0):
+  """ Returns the target Chromium version based on a heuristic. """
+  # The current compatible version from CEF.
+  compat_version = chromium_compat_version
+  compat_commit = get_git_hash(chromium_src_dir, compat_version)
+  if compat_version == compat_commit:
+    versions = get_chromium_versions(compat_commit)
+    if len(versions) > 0:
+      compat_version = 'refs/tags/' + versions[0]
+      # Closest version may not align with the compat position, so adjust the
+      # commit to match.
+      compat_commit = get_git_hash(chromium_src_dir, compat_version)
+  compat_position = get_chromium_master_position(compat_commit)
+  compat_date = get_git_date(chromium_src_dir, compat_commit)
+
+  # The most recent channel version from the Chromium website.
+  channel_version = 'refs/tags/' + get_chromium_channel_version(os, channel)
+  channel_commit = get_chromium_channel_commit(os, channel)
+  channel_position = get_chromium_master_position(channel_commit)
+  channel_date = get_git_date(chromium_src_dir, channel_commit)
+
+  if compat_position >= channel_position:
+    # Already compatible with the channel version or newer.
+    target_version = compat_version
+    target_commit = compat_commit
+    target_position = compat_position
+    target_date = compat_date
+  elif target_distance <= 0 or compat_position + target_distance >= channel_position:
+    # Channel version is within the target distance.
+    target_version = channel_version
+    target_commit = channel_commit
+    target_position = channel_position
+    target_date = channel_date
+  else:
+    # Find an intermediary version that's within the target distance.
+    target_position = compat_position + target_distance
+    target_commit = get_chromium_master_commit(target_position)
+    versions = get_chromium_versions(target_commit)
+    if len(versions) > 0:
+      target_version = 'refs/tags/' + versions[0]
+      # Closest version may not align with the target position, so adjust the
+      # commit and position to match.
+      target_commit = get_git_hash(chromium_src_dir, target_version)
+      target_position = get_chromium_master_position(target_commit)
+    else:
+      target_version = target_commit
+    target_date = get_git_date(chromium_src_dir, target_commit)
+
+  msg("")
+  msg("Computed Chromium update for %s %s at distance %d" % (os, channel,
+                                                             target_distance))
+  msg("Compat:  %s %s %s (#%d)" % (compat_date, compat_version, compat_commit,
+                                   compat_position))
+  msg("Target:  %s %s %s (#%d)" % (target_date, target_version, target_commit,
+                                   target_position))
+  msg("Channel: %s %s %s (#%d)" % (channel_date, channel_version,
+                                   channel_commit, channel_position))
+  msg("")
+
+  return target_version
+
+
+def get_build_directory_name(is_debug):
+  build_dir = ('Debug' if is_debug else 'Release') + '_'
+
+  # CEF uses a consistent directory naming scheme for GN via
+  # GetAllPlatformConfigs in tools/gn_args.py.
+  if options.x64build:
+    build_dir += 'GN_x64'
+  elif options.armbuild:
+    build_dir += 'GN_arm'
+  elif options.arm64build:
+    build_dir += 'GN_arm64'
+  else:
+    build_dir += 'GN_x86'
+  return build_dir
+
+
+def read_update_file():
+  update_path = os.path.join(cef_src_dir, 'CHROMIUM_UPDATE.txt')
+  if not os.path.exists(update_path):
+    msg("Missing file: %s" % update_path)
+    return None
+
+  msg("Reading %s" % update_path)
+  return read_config_file(update_path)
+
+
+def log_chromium_changes():
+  """ Evaluate the Chromium checkout for changes. """
+  config = read_update_file()
+  if config is None:
+    msg("Skipping Chromium changes log.")
+    return
+
+  if 'files' in config:
+    out_file = os.path.join(download_dir, 'chromium_update_changes.diff')
+    if os.path.exists(out_file):
+      os.remove(out_file)
+
+    old_commit = get_chromium_master_commit(
+        get_chromium_master_position(chromium_compat_version))
+    new_commit = get_chromium_master_commit(
+        get_chromium_master_position(chromium_checkout))
+
+    cmd = '%s diff --relative --no-prefix %s..%s -- %s' % (
+        git_exe, old_commit, new_commit, ' '.join(config['files']))
+    result = exec_cmd(cmd, chromium_src_dir)
+    if result['out'] != '':
+      write_file(out_file, result['out'])
+
+
+def check_pattern_matches(output_file=None):
+  """ Evaluate the Chromium checkout for pattern matches. """
+  config = read_update_file()
+  if config is None:
+    msg("Skipping Chromium pattern matching.")
+    return
+
+  if 'patterns' in config:
+    if output_file is None:
+      fp = sys.stdout
+    else:
+      msg('Writing %s' % output_file)
+      fp = open(output_file, 'w', encoding='utf-8')
+
+    has_output = False
+    for entry in config['patterns']:
+      msg("Evaluating pattern: %s" % entry['pattern'])
+
+      # Read patterns from a file to avoid formatting problems.
+      pattern_handle, pattern_file = tempfile.mkstemp()
+      os.write(pattern_handle, entry['pattern'])
+      os.close(pattern_handle)
+
+      cmd = '%s grep -n -f %s' % (git_exe, pattern_file)
+      result = exec_cmd(cmd, chromium_src_dir)
+      os.remove(pattern_file)
+
+      if result['out'] != '':
+        write_msg = True
+        re_exclude = re.compile(
+            entry['exclude_matches']) if 'exclude_matches' in entry else None
+
+        for line in result['out'].split('\n'):
+          line = line.strip()
+          if len(line) == 0:
+            continue
+          skip = not re_exclude is None and re_exclude.match(line) != None
+          if not skip:
+            if write_msg:
+              if has_output:
+                write_fp(fp, '\n')
+              write_fp(fp,
+                       '!!!! WARNING: FOUND PATTERN: %s\n' % entry['pattern'])
+              if 'message' in entry:
+                write_fp(fp, entry['message'] + '\n')
+              write_fp(fp, '\n')
+              write_msg = False
+            write_fp(fp, line + '\n')
+            has_output = True
+
+    if not output_file is None:
+      if has_output:
+        msg('ERROR Matches found. See %s for output.' % out_file)
+      else:
+        write_fp(fp, 'Good news! No matches.\n')
+      fp.close()
+
+    if has_output:
+      # Don't continue when we know the build will be wrong.
+      sys.exit(1)
+
+
+##
+# Program entry point.
+##
+
+# Cannot be loaded as a module.
+if __name__ != "__main__":
+  sys.stderr.write('This file cannot be loaded as a module!')
+  sys.exit()
+
+# Parse command-line options.
+disc = """
+This utility implements automation for the download, update, build and
+distribution of CEF.
+"""
+
+parser = OptionParser(description=disc)
+
+# Setup options.
+parser.add_option(
+    '--download-dir',
+    dest='downloaddir',
+    metavar='DIR',
+    help='Download directory with no spaces [required].')
+parser.add_option(
+    '--depot-tools-dir',
+    dest='depottoolsdir',
+    metavar='DIR',
+    help='Download directory for depot_tools.',
+    default='')
+parser.add_option('--depot-tools-archive', dest='depottoolsarchive',
+                  help='Zip archive file that contains a single top-level '+\
+                       'depot_tools directory.', default='')
+parser.add_option('--branch', dest='branch',
+                  help='Branch of CEF to build (master, 3987, ...). This '+\
+                       'will be used to name the CEF download directory and '+\
+                       'to identify the correct URL if --url is not '+\
+                       'specified. The default value is master.',
+                  default='master')
+parser.add_option('--url', dest='url',
+                  help='CEF download URL. If not specified the default URL '+\
+                       'will be used.',
+                  default='')
+parser.add_option('--chromium-url', dest='chromiumurl',
+                  help='Chromium download URL. If not specified the default '+\
+                       'URL will be used.',
+                  default='')
+parser.add_option('--checkout', dest='checkout',
+                  help='Version of CEF to checkout. If not specified the '+\
+                       'most recent remote version of the branch will be used.',
+                  default='')
+parser.add_option('--chromium-checkout', dest='chromiumcheckout',
+                  help='Version of Chromium to checkout (Git '+\
+                       'branch/hash/tag). This overrides the value specified '+\
+                       'by CEF in CHROMIUM_BUILD_COMPATIBILITY.txt.',
+                  default='')
+parser.add_option('--chromium-channel', dest='chromiumchannel',
+                  help='Chromium channel to check out (canary, dev, beta or '+\
+                       'stable). This overrides the value specified by CEF '+\
+                       'in CHROMIUM_BUILD_COMPATIBILITY.txt.',
+                  default='')
+parser.add_option('--chromium-channel-distance', dest='chromiumchanneldistance',
+                  help='The target number of commits to step in the '+\
+                       'channel, or 0 to use the newest channel version. '+\
+                       'Used in combination with --chromium-channel.',
+                  default='')
+
+# Miscellaneous options.
+parser.add_option(
+    '--force-config',
+    action='store_true',
+    dest='forceconfig',
+    default=False,
+    help='Force creation of a new gclient config file.')
+parser.add_option('--force-clean',
+                  action='store_true', dest='forceclean', default=False,
+                  help='Force a clean checkout of Chromium and CEF. This will'+\
+                       ' trigger a new update, build and distribution.')
+parser.add_option('--force-clean-deps',
+                  action='store_true', dest='forcecleandeps', default=False,
+                  help='Force a clean checkout of Chromium dependencies. Used'+\
+                       ' in combination with --force-clean.')
+parser.add_option(
+    '--dry-run',
+    action='store_true',
+    dest='dryrun',
+    default=False,
+    help="Output commands without executing them.")
+parser.add_option('--dry-run-platform', dest='dryrunplatform', default=None,
+                  help='Simulate a dry run on the specified platform '+\
+                       '(windows, macosx, linux). Must be used in combination'+\
+                       ' with the --dry-run flag.')
+
+# Update-related options.
+parser.add_option('--force-update',
+                  action='store_true', dest='forceupdate', default=False,
+                  help='Force a Chromium and CEF update. This will trigger a '+\
+                       'new build and distribution.')
+parser.add_option('--no-update',
+                  action='store_true', dest='noupdate', default=False,
+                  help='Do not update Chromium or CEF. Pass --force-build or '+\
+                       '--force-distrib if you desire a new build or '+\
+                       'distribution.')
+parser.add_option('--no-cef-update',
+                  action='store_true', dest='nocefupdate', default=False,
+                  help='Do not update CEF. Pass --force-build or '+\
+                       '--force-distrib if you desire a new build or '+\
+                       'distribution.')
+parser.add_option('--force-cef-update',
+                  action='store_true', dest='forcecefupdate', default=False,
+                  help='Force a CEF update. This will cause local changes in '+\
+                       'the CEF checkout to be discarded and patch files to '+\
+                       'be reapplied.')
+parser.add_option(
+    '--no-chromium-update',
+    action='store_true',
+    dest='nochromiumupdate',
+    default=False,
+    help='Do not update Chromium.')
+parser.add_option(
+    '--no-depot-tools-update',
+    action='store_true',
+    dest='nodepottoolsupdate',
+    default=False,
+    help='Do not update depot_tools.')
+parser.add_option(
+    '--install-build-deps',
+    action='store_true',
+    dest='installbuilddeps',
+    default=False,
+    help='Install build dependencies on platforms that support this.')
+parser.add_option('--fast-update',
+                  action='store_true', dest='fastupdate', default=False,
+                  help='Update existing Chromium/CEF checkouts for fast incremental '+\
+                       'builds by attempting to minimize the number of modified files. '+\
+                       'The update will fail if there are unstaged CEF changes or if '+\
+                       'Chromium changes are not included in a patch file.')
+parser.add_option(
+    '--force-patch-update',
+    action='store_true',
+    dest='forcepatchupdate',
+    default=False,
+    help='Force update of patch files.')
+parser.add_option(
+    '--resave',
+    action='store_true',
+    dest='resave',
+    default=False,
+    help='Resave patch files.')
+parser.add_option(
+    '--log-chromium-changes',
+    action='store_true',
+    dest='logchromiumchanges',
+    default=False,
+    help='Create a log of the Chromium changes.')
+
+# Build-related options.
+parser.add_option('--force-build',
+                  action='store_true', dest='forcebuild', default=False,
+                  help='Force CEF debug and release builds. This builds '+\
+                       '[build-target] on all platforms and chrome_sandbox '+\
+                       'on Linux.')
+parser.add_option(
+    '--no-build',
+    action='store_true',
+    dest='nobuild',
+    default=False,
+    help='Do not build CEF.')
+parser.add_option(
+    '--build-target',
+    dest='buildtarget',
+    default='cefclient',
+    help='Target name(s) to build (defaults to "cefclient").')
+parser.add_option(
+    '--build-tests',
+    action='store_true',
+    dest='buildtests',
+    default=False,
+    help='Also build the test target specified via --test-target.')
+parser.add_option(
+    '--no-debug-build',
+    action='store_true',
+    dest='nodebugbuild',
+    default=False,
+    help="Don't perform the CEF debug build.")
+parser.add_option(
+    '--no-release-build',
+    action='store_true',
+    dest='noreleasebuild',
+    default=False,
+    help="Don't perform the CEF release build.")
+parser.add_option(
+    '--verbose-build',
+    action='store_true',
+    dest='verbosebuild',
+    default=False,
+    help='Show all command lines while building.')
+parser.add_option(
+    '--build-failure-limit',
+    dest='buildfailurelimit',
+    default=1,
+    type="int",
+    help='Keep going until N jobs fail.')
+parser.add_option('--build-log-file',
+                  action='store_true', dest='buildlogfile', default=False,
+                  help='Write build logs to file. The file will be named '+\
+                       '"build-[branch]-[debug|release].log" in the download '+\
+                       'directory.')
+parser.add_option(
+    '--x64-build',
+    action='store_true',
+    dest='x64build',
+    default=False,
+    help='Create a 64-bit build.')
+parser.add_option(
+    '--arm-build',
+    action='store_true',
+    dest='armbuild',
+    default=False,
+    help='Create an ARM build.')
+parser.add_option(
+    '--arm64-build',
+    action='store_true',
+    dest='arm64build',
+    default=False,
+    help='Create an ARM64 build.')
+
+# Test-related options.
+parser.add_option(
+    '--run-tests',
+    action='store_true',
+    dest='runtests',
+    default=False,
+    help='Run the ceftests target.')
+parser.add_option(
+    '--no-debug-tests',
+    action='store_true',
+    dest='nodebugtests',
+    default=False,
+    help="Don't run debug build tests.")
+parser.add_option(
+    '--no-release-tests',
+    action='store_true',
+    dest='noreleasetests',
+    default=False,
+    help="Don't run release build tests.")
+parser.add_option(
+    '--test-target',
+    dest='testtarget',
+    default='ceftests',
+    help='Test target name to build (defaults to "ceftests").')
+parser.add_option(
+    '--test-prefix',
+    dest='testprefix',
+    default='',
+    help='Prefix for running the test executable (e.g. `xvfb-run` on Linux).')
+parser.add_option(
+    '--test-args',
+    dest='testargs',
+    default='',
+    help='Arguments that will be passed to the test executable.')
+
+# Distribution-related options.
+parser.add_option(
+    '--force-distrib',
+    action='store_true',
+    dest='forcedistrib',
+    default=False,
+    help='Force creation of a CEF binary distribution.')
+parser.add_option(
+    '--no-distrib',
+    action='store_true',
+    dest='nodistrib',
+    default=False,
+    help="Don't create a CEF binary distribution.")
+parser.add_option(
+    '--minimal-distrib',
+    action='store_true',
+    dest='minimaldistrib',
+    default=False,
+    help='Create a minimal CEF binary distribution.')
+parser.add_option(
+    '--minimal-distrib-only',
+    action='store_true',
+    dest='minimaldistribonly',
+    default=False,
+    help='Create a minimal CEF binary distribution only.')
+parser.add_option(
+    '--client-distrib',
+    action='store_true',
+    dest='clientdistrib',
+    default=False,
+    help='Create a client CEF binary distribution.')
+parser.add_option(
+    '--client-distrib-only',
+    action='store_true',
+    dest='clientdistribonly',
+    default=False,
+    help='Create a client CEF binary distribution only.')
+parser.add_option(
+    '--sandbox-distrib',
+    action='store_true',
+    dest='sandboxdistrib',
+    default=False,
+    help='Create a cef_sandbox static library distribution.')
+parser.add_option(
+    '--sandbox-distrib-only',
+    action='store_true',
+    dest='sandboxdistribonly',
+    default=False,
+    help='Create a cef_sandbox static library distribution only.')
+parser.add_option(
+    '--no-distrib-docs',
+    action='store_true',
+    dest='nodistribdocs',
+    default=False,
+    help="Don't create CEF documentation.")
+parser.add_option(
+    '--no-distrib-archive',
+    action='store_true',
+    dest='nodistribarchive',
+    default=False,
+    help="Don't create archives for output directories.")
+parser.add_option(
+    '--clean-artifacts',
+    action='store_true',
+    dest='cleanartifacts',
+    default=False,
+    help='Clean the artifacts output directory.')
+parser.add_option('--distrib-subdir', dest='distribsubdir',
+                  help='CEF distrib dir name, child of '+\
+                       'chromium/src/cef/binary_distrib',
+                  default='')
+
+(options, args) = parser.parse_args()
+
+if options.downloaddir is None:
+  print("The --download-dir option is required.")
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+# Opt into component-specific flags for later use.
+if options.noupdate:
+  options.nocefupdate = True
+  options.nochromiumupdate = True
+  options.nodepottoolsupdate = True
+
+if options.runtests:
+  options.buildtests = True
+
+if (options.nochromiumupdate and options.forceupdate) or \
+   (options.nocefupdate and options.forceupdate) or \
+   (options.nobuild and options.forcebuild) or \
+   (options.nodistrib and options.forcedistrib) or \
+   ((options.forceclean or options.forcecleandeps) and options.fastupdate) or \
+   (options.chromiumcheckout and options.chromiumchannel):
+  print("Invalid combination of options.")
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if (options.noreleasebuild and \
+     (options.minimaldistrib or options.minimaldistribonly or \
+      options.clientdistrib or options.clientdistribonly)) or \
+   (options.minimaldistribonly + options.clientdistribonly + options.sandboxdistribonly > 1):
+  print('Invalid combination of options.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if options.x64build + options.armbuild + options.arm64build > 1:
+  print('Invalid combination of options.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+if (options.buildtests or options.runtests) and len(options.testtarget) == 0:
+  print("A test target must be specified via --test-target.")
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+# Operating system.
+if options.dryrun and options.dryrunplatform is not None:
+  platform = options.dryrunplatform
+  if not platform in ['windows', 'macosx', 'linux']:
+    print('Invalid dry-run-platform value: %s' % (platform))
+    sys.exit()
+elif sys.platform == 'win32':
+  platform = 'windows'
+elif sys.platform == 'darwin':
+  platform = 'macosx'
+elif sys.platform.startswith('linux'):
+  platform = 'linux'
+else:
+  print('Unknown operating system platform')
+  sys.exit()
+
+if options.clientdistrib or options.clientdistribonly:
+  if platform == 'linux':
+    client_app = 'cefsimple'
+  else:
+    client_app = 'cefclient'
+  if options.buildtarget.find(client_app) == -1:
+    print('A client distribution cannot be generated if --build-target ' +
+          'excludes %s.' % client_app)
+    parser.print_help(sys.stderr)
+    sys.exit()
+
+# CEF branch.
+cef_branch = options.branch
+
+branch_is_master = (cef_branch == 'master' or cef_branch == 'trunk')
+if not branch_is_master:
+  # Verify that the branch value is numeric.
+  if not cef_branch.isdigit():
+    print('Invalid branch value: %s' % cef_branch)
+    sys.exit()
+
+  # Verify the minimum supported branch number.
+  if int(cef_branch) < 3071:
+    print('The requested branch (%s) is too old to build using this tool. ' +
+          'The minimum supported branch is 3071.' % cef_branch)
+    sys.exit()
+
+# True if the requested branch is 3538 or newer.
+branch_is_3538_or_newer = (branch_is_master or int(cef_branch) >= 3538)
+
+# True if the requested branch is 3945 or newer.
+branch_is_3945_or_newer = (branch_is_master or int(cef_branch) >= 3945)
+
+# Enable Python 3 usage in Chromium for branches 3945 and newer.
+if branch_is_3945_or_newer and not is_python2 and \
+    not 'GCLIENT_PY3' in os.environ.keys():
+  os.environ['GCLIENT_PY3'] = '1'
+
+if not branch_is_3945_or_newer and \
+  (not is_python2 or bool(int(os.environ.get('GCLIENT_PY3', '0')))):
+  print('Python 3 is not supported with branch 3904 and older ' +
+        '(set GCLIENT_PY3=0 and run with Python 2 executable).')
+  sys.exit()
+
+if options.armbuild:
+  if platform != 'linux':
+    print('The ARM build option is only supported on Linux.')
+    sys.exit()
+
+if options.arm64build:
+  if platform != 'linux' and platform != 'windows':
+    print('The ARM64 build option is only supported on Linux and Windows.')
+    sys.exit()
+
+deps_file = 'DEPS'
+
+if platform == 'macosx' and not options.x64build:
+  print('32-bit Mac OS X builds are not supported. ' +
+        'Add --x64-build flag to generate a 64-bit build.')
+  sys.exit()
+
+# Platforms that build a cef_sandbox library.
+sandbox_lib_platforms = ['windows']
+if branch_is_3538_or_newer:
+  sandbox_lib_platforms.append('macosx')
+
+if not platform in sandbox_lib_platforms and (options.sandboxdistrib or
+                                              options.sandboxdistribonly):
+  print('The sandbox distribution is not supported on this platform.')
+  sys.exit()
+
+# Options that force the sources to change.
+force_change = options.forceclean or options.forceupdate
+
+# Options that cause local changes to be discarded.
+discard_local_changes = force_change or options.forcecefupdate
+
+if options.resave and (options.forcepatchupdate or discard_local_changes):
+  print('--resave cannot be combined with options that modify or discard ' +
+        'patches.')
+  parser.print_help(sys.stderr)
+  sys.exit()
+
+download_dir = os.path.abspath(options.downloaddir)
+chromium_dir = os.path.join(download_dir, 'chromium')
+chromium_src_dir = os.path.join(chromium_dir, 'src')
+out_src_dir = os.path.join(chromium_src_dir, 'out')
+cef_src_dir = os.path.join(chromium_src_dir, 'cef')
+
+if options.fastupdate and os.path.exists(cef_src_dir):
+  cef_dir = cef_src_dir
+else:
+  cef_dir = os.path.join(download_dir, 'cef')
+
+##
+# Manage the download directory.
+##
+
+# Create the download directory if necessary.
+create_directory(download_dir)
+
+msg("Download Directory: %s" % (download_dir))
+
+##
+# Manage the depot_tools directory.
+##
+
+# Check if the depot_tools directory exists.
+if options.depottoolsdir != '':
+  depot_tools_dir = os.path.abspath(options.depottoolsdir)
+else:
+  depot_tools_dir = os.path.join(download_dir, 'depot_tools')
+
+msg("Depot Tools Directory: %s" % (depot_tools_dir))
+
+if not os.path.exists(depot_tools_dir):
+  if platform == 'windows' and options.depottoolsarchive == '':
+    # On Windows download depot_tools as an archive file since we can't assume
+    # that git is already installed.
+    options.depottoolsarchive = depot_tools_archive_url
+
+  if options.depottoolsarchive != '':
+    # Extract depot_tools from an archive file.
+    msg('Extracting %s to %s.' % \
+        (options.depottoolsarchive, depot_tools_dir))
+    if not options.dryrun:
+      download_and_extract(options.depottoolsarchive, depot_tools_dir)
+  else:
+    # On Linux and OS X check out depot_tools using Git.
+    run('git clone ' + depot_tools_url + ' ' + depot_tools_dir, download_dir)
+
+if not options.nodepottoolsupdate:
+  # Update depot_tools.
+  # On Windows this will download required python and git binaries.
+  msg('Updating depot_tools')
+  if platform == 'windows':
+    run('update_depot_tools.bat', depot_tools_dir, depot_tools_dir)
+  else:
+    run('update_depot_tools', depot_tools_dir, depot_tools_dir)
+
+# Determine the executables to use.
+if platform == 'windows':
+  # Force use of the version bundled with depot_tools.
+  git_exe = os.path.join(depot_tools_dir, 'git.bat')
+  python_bat = 'python.bat' if is_python2 else 'python3.bat'
+  python_exe = os.path.join(depot_tools_dir, python_bat)
+  if options.dryrun and not os.path.exists(git_exe):
+    sys.stdout.write("WARNING: --dry-run assumes that depot_tools" \
+                     " is already in your PATH. If it isn't\nplease" \
+                     " specify a --depot-tools-dir value.\n")
+    git_exe = 'git.bat'
+    python_exe = python_bat
+else:
+  git_exe = 'git'
+  python_exe = sys.executable
+
+##
+# Manage the cef directory.
+##
+
+# Delete the existing CEF directory if requested.
+if options.forceclean and os.path.exists(cef_dir):
+  delete_directory(cef_dir)
+
+# Determine the type of CEF checkout to use.
+if os.path.exists(cef_dir) and not is_git_checkout(cef_dir):
+  raise Exception("Not a valid CEF Git checkout: %s" % (cef_dir))
+
+# Determine the CEF download URL to use.
+cef_url = options.url.strip()
+if cef_url == '':
+  cef_url = cef_git_url
+
+# Verify that the requested CEF URL matches the existing checkout.
+if not options.nocefupdate and os.path.exists(cef_dir):
+  cef_existing_url = get_git_url(cef_dir)
+  if cef_url != cef_existing_url:
+    raise Exception(
+        'Requested CEF checkout URL %s does not match existing URL %s' %
+        (cef_url, cef_existing_url))
+
+msg("CEF Branch: %s" % (cef_branch))
+msg("CEF URL: %s" % (cef_url))
+msg("CEF Source Directory: %s" % (cef_dir))
+
+# Determine the CEF Git branch to use.
+if options.checkout == '':
+  # Target the most recent branch commit from the remote repo.
+  if branch_is_master:
+    cef_checkout = 'origin/master'
+  else:
+    cef_checkout = 'origin/' + cef_branch
+else:
+  cef_checkout = options.checkout
+
+# Create the CEF checkout if necessary.
+if not options.nocefupdate and not os.path.exists(cef_dir):
+  cef_checkout_new = True
+  run(
+      '%s clone --config core.autocrlf=false %s %s' %
+      (git_exe, cef_url, cef_dir), download_dir, depot_tools_dir)
+  add_google_patches()
+else:
+  cef_checkout_new = False
+
+# Determine if the CEF checkout needs to change.
+if not options.nocefupdate and os.path.exists(cef_dir):
+  cef_current_hash = get_git_hash(cef_dir, 'HEAD')
+
+  if not cef_checkout_new:
+    # Fetch updated sources.
+    run('%s fetch' % (git_exe), cef_dir, depot_tools_dir)
+
+  cef_desired_hash = get_git_hash(cef_dir, cef_checkout)
+  cef_checkout_changed = cef_checkout_new or force_change or \
+                         options.forcecefupdate or \
+                         cef_current_hash != cef_desired_hash
+
+  msg("CEF Current Checkout: %s" % (cef_current_hash))
+  msg("CEF Desired Checkout: %s (%s)" % (cef_desired_hash, cef_checkout))
+
+  if cef_checkout_changed:
+    if cef_dir == cef_src_dir:
+      # Running in fast update mode. Backup and revert the patched files before
+      # changing the CEF checkout.
+      run_patch_updater("--backup --revert")
+
+    # Unregister Google-specific patches so that checkout can succeed.
+    remove_google_patches()
+
+    # Update the CEF checkout.
+    run('%s checkout %s%s' %
+      (git_exe, '--force ' if discard_local_changes else '', cef_checkout), \
+      cef_dir, depot_tools_dir)
+
+    # Reregister Google-specific patches.
+    add_google_patches()
+else:
+  cef_checkout_changed = False
+
+build_compat_versions = get_build_compat_versions()
+
+if not options.nodepottoolsupdate and \
+    'depot_tools_checkout' in build_compat_versions:
+  # Update the depot_tools checkout.
+  depot_tools_compat_version = build_compat_versions['depot_tools_checkout']
+  run('%s checkout %s%s' %
+      (git_exe, '--force ' if discard_local_changes else '', depot_tools_compat_version), \
+      depot_tools_dir, depot_tools_dir)
+
+# Disable further depot_tools updates.
+os.environ['DEPOT_TOOLS_UPDATE'] = '0'
+
+##
+# Manage the out directory.
+##
+
+out_dir = os.path.join(download_dir, 'out_' + cef_branch)
+
+# Delete the existing out directory if requested.
+if options.forceclean and os.path.exists(out_dir):
+  delete_directory(out_dir)
+
+msg("CEF Output Directory: %s" % (out_dir))
+
+##
+# Manage the chromium directory.
+##
+
+# Create the chromium directory if necessary.
+create_directory(chromium_dir)
+
+if options.chromiumurl != '':
+  chromium_url = options.chromiumurl
+else:
+  chromium_url = 'https://chromium.googlesource.com/chromium/src.git'
+
+# Create gclient configuration file.
+gclient_file = os.path.join(chromium_dir, '.gclient')
+if not os.path.exists(gclient_file) or options.forceconfig:
+  # Exclude unnecessary directories. Intentionally written without newlines.
+  gclient_spec = \
+      "solutions = [{"+\
+        "'managed': False,"+\
+        "'name': 'src', "+\
+        "'url': '" + chromium_url + "', "+\
+        "'custom_deps': {"+\
+          "'build': None, "+\
+          "'build/scripts/command_wrapper/bin': None, "+\
+          "'build/scripts/gsd_generate_index': None, "+\
+          "'build/scripts/private/data/reliability': None, "+\
+          "'build/scripts/tools/deps2git': None, "+\
+          "'build/third_party/lighttpd': None, "+\
+          "'commit-queue': None, "+\
+          "'depot_tools': None, "+\
+          "'src/chrome_frame/tools/test/reference_build/chrome': None, "+\
+          "'src/chrome/tools/test/reference_build/chrome_linux': None, "+\
+          "'src/chrome/tools/test/reference_build/chrome_mac': None, "+\
+          "'src/chrome/tools/test/reference_build/chrome_win': None, "+\
+        "}, "+\
+        "'deps_file': '" + deps_file + "', "+\
+        "'safesync_url': ''"+\
+      "}]"
+
+  msg('Writing %s' % gclient_file)
+  if not options.dryrun:
+    with open(gclient_file, 'w', encoding='utf-8') as fp:
+      write_fp(fp, gclient_spec)
+
+# Initial Chromium checkout.
+if not options.nochromiumupdate and not os.path.exists(chromium_src_dir):
+  chromium_checkout_new = True
+  run("gclient sync --nohooks --with_branch_heads --jobs 16", \
+      chromium_dir, depot_tools_dir)
+else:
+  chromium_checkout_new = False
+
+# Verify the Chromium checkout.
+if not options.dryrun and not is_git_checkout(chromium_src_dir):
+  raise Exception('Not a valid git checkout: %s' % (chromium_src_dir))
+
+if os.path.exists(chromium_src_dir):
+  msg("Chromium URL: %s" % (get_git_url(chromium_src_dir)))
+
+# Fetch Chromium changes so that we can perform the necessary calculations using
+# local history.
+if not options.nochromiumupdate and os.path.exists(chromium_src_dir):
+  # Fetch updated sources.
+  run("%s fetch" % (git_exe), chromium_src_dir, depot_tools_dir)
+  # Also fetch tags, which are required for release branch builds.
+  run("%s fetch --tags" % (git_exe), chromium_src_dir, depot_tools_dir)
+
+# Determine the Chromium checkout options required by CEF.
+chromium_compat_version = build_compat_versions['chromium_checkout']
+if len(options.chromiumcheckout) > 0:
+  chromium_checkout = options.chromiumcheckout
+elif len(options.chromiumchannel) > 0:
+  target_distance = int(options.chromiumchanneldistance
+                       ) if len(options.chromiumchanneldistance) > 0 else 0
+  chromium_checkout = get_chromium_target_version(
+      channel=options.chromiumchannel, target_distance=target_distance)
+else:
+  chromium_checkout = chromium_compat_version
+
+# Determine if the Chromium checkout needs to change.
+if not options.nochromiumupdate and os.path.exists(chromium_src_dir):
+  chromium_current_hash = get_git_hash(chromium_src_dir, 'HEAD')
+  chromium_desired_hash = get_git_hash(chromium_src_dir, chromium_checkout)
+  chromium_checkout_changed = chromium_checkout_new or force_change or \
+                              chromium_current_hash != chromium_desired_hash
+
+  msg("Chromium Current Checkout: %s" % (chromium_current_hash))
+  msg("Chromium Desired Checkout: %s (%s)" % \
+      (chromium_desired_hash, chromium_checkout))
+else:
+  chromium_checkout_changed = options.dryrun
+
+if cef_checkout_changed:
+  if cef_dir != cef_src_dir and os.path.exists(cef_src_dir):
+    # Delete the existing src/cef directory. It will be re-copied from the
+    # download directory later.
+    delete_directory(cef_src_dir)
+elif chromium_checkout_changed and cef_dir == cef_src_dir:
+  # Running in fast update mode. Backup and revert the patched files before
+  # changing the Chromium checkout.
+  run_patch_updater("--backup --revert")
+
+# Delete the existing src/out directory if requested.
+if options.forceclean and os.path.exists(out_src_dir):
+  delete_directory(out_src_dir)
+
+# Move the existing src/out directory to the correct location in the download
+# directory. It will be moved back from the download directory later.
+if os.path.exists(out_src_dir):
+  old_branch = read_branch_config_file(out_src_dir)
+  if old_branch != '' and (chromium_checkout_changed or
+                           old_branch != cef_branch):
+    old_out_dir = os.path.join(download_dir, 'out_' + old_branch)
+    move_directory(out_src_dir, old_out_dir)
+
+# Update the Chromium checkout.
+if chromium_checkout_changed:
+  if not chromium_checkout_new and not options.fastupdate:
+    if options.forceclean and options.forcecleandeps:
+      # Remove all local changes including third-party git checkouts managed by
+      # gclient.
+      run("%s clean -dffx" % (git_exe), chromium_src_dir, depot_tools_dir)
+    else:
+      # Revert all changes in the Chromium checkout.
+      run("gclient revert --nohooks", chromium_dir, depot_tools_dir)
+
+  # Checkout the requested branch.
+  run("%s checkout %s%s" % \
+    (git_exe, '--force ' if discard_local_changes else '', chromium_checkout), \
+    chromium_src_dir, depot_tools_dir)
+
+  # Patch the Chromium DEPS file if necessary.
+  apply_deps_patch()
+
+  # Update third-party dependencies including branch/tag information.
+  run("gclient sync %s--nohooks --with_branch_heads --jobs 16" % \
+      ('--reset ' if discard_local_changes else ''), chromium_dir, depot_tools_dir)
+
+  # Patch the Chromium runhooks scripts if necessary.
+  apply_runhooks_patch()
+
+  # Runs hooks for files that have been modified in the local working copy.
+  run("gclient runhooks --jobs 16", chromium_dir, depot_tools_dir)
+
+  # Delete the src/out directory created by `gclient sync`.
+  delete_directory(out_src_dir)
+
+if cef_dir == cef_src_dir:
+  # Running in fast update mode.
+  if cef_checkout_changed or chromium_checkout_changed:
+    # Check and restore the patched files.
+    run_patch_updater("--reapply --restore")
+elif os.path.exists(cef_dir) and not os.path.exists(cef_src_dir):
+  # Restore the src/cef directory.
+  copy_directory(cef_dir, cef_src_dir)
+
+# Restore the src/out directory.
+out_src_dir_exists = os.path.exists(out_src_dir)
+if os.path.exists(out_dir) and not out_src_dir_exists:
+  move_directory(out_dir, out_src_dir)
+  out_src_dir_exists = True
+elif not out_src_dir_exists:
+  create_directory(out_src_dir)
+
+# Write the config file for identifying the branch.
+write_branch_config_file(out_src_dir, cef_branch)
+
+if options.logchromiumchanges and chromium_checkout != chromium_compat_version:
+  log_chromium_changes()
+
+if options.forcepatchupdate or ((chromium_checkout_new or not options.fastupdate) and \
+                                chromium_checkout_changed and \
+                                chromium_checkout != chromium_compat_version):
+  # Not using the known-compatible Chromium version. Try to update patch files.
+  if options.logchromiumchanges:
+    out_file = os.path.join(download_dir, 'chromium_update_patches.txt')
+    if os.path.exists(out_file):
+      os.remove(out_file)
+  else:
+    out_file = None
+  run_patch_updater(output_file=out_file)
+elif options.resave:
+  # Resave patch files.
+  run_patch_updater("--resave")
+
+if chromium_checkout != chromium_compat_version:
+  if options.logchromiumchanges:
+    out_file = os.path.join(download_dir, 'chromium_update_patterns.txt')
+    if os.path.exists(out_file):
+      os.remove(out_file)
+  else:
+    out_file = None
+  check_pattern_matches(output_file=out_file)
+
+##
+# Install dependencies.
+##
+
+if options.installbuilddeps and platform == 'linux':
+  deps_script = os.path.join(chromium_src_dir, 'build/install-build-deps.sh')
+  if options.x64build:
+    cmd = 'sudo %s --no-prompt --no-chromeos-fonts' % deps_script
+  else:
+    cmd = 'sudo %s --no-prompt --no-chromeos-fonts --lib32' % deps_script
+  run(cmd, chromium_src_dir)
+
+##
+# Build CEF.
+##
+
+if not options.nobuild and (chromium_checkout_changed or \
+                            cef_checkout_changed or options.forcebuild or \
+                            not out_src_dir_exists):
+  # Building should also force a distribution.
+  options.forcedistrib = True
+
+  # Make sure the GN configuration exists.
+  if not options.dryrun and \
+    not os.path.exists(os.path.join(cef_src_dir, 'BUILD.gn')):
+    raise Exception('GN configuration does not exist.')
+
+  # Print all build-related environment variables including any that were set
+  # previously.
+  for key in os.environ.keys():
+    if key.startswith('CEF_') or key.startswith('GCLIENT_') or \
+       key.startswith('GN_') or key.startswith('GYP_') or \
+       key.startswith('DEPOT_TOOLS_'):
+      msg('%s=%s' % (key, os.environ[key]))
+
+  # Generate project files.
+  tool = os.path.join(cef_src_dir, 'tools', 'gclient_hook.py')
+  run('%s %s' % (python_exe, tool), cef_src_dir, depot_tools_dir)
+
+  # Build using Ninja.
+  command = 'autoninja '
+  if options.verbosebuild:
+    command += '-v '
+  if options.buildfailurelimit != 1:
+    command += '-k %d ' % options.buildfailurelimit
+  command += '-C '
+  target = ' ' + options.buildtarget
+  if options.buildtests:
+    target += ' ' + options.testtarget
+  if platform == 'linux':
+    target += ' chrome_sandbox'
+
+  # Make a CEF Debug build.
+  if not options.nodebugbuild:
+    build_path = os.path.join('out', get_build_directory_name(True))
+    args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+    msg(args_path + ' contents:\n' + read_file(args_path))
+
+    run(command + build_path + target, chromium_src_dir, depot_tools_dir,
+        os.path.join(download_dir, 'build-%s-debug.log' % (cef_branch)) \
+          if options.buildlogfile else None)
+
+    if platform in sandbox_lib_platforms:
+      # Make the separate cef_sandbox build when GN is_official_build=true.
+      build_path += '_sandbox'
+      if os.path.exists(os.path.join(chromium_src_dir, build_path)):
+        args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+        msg(args_path + ' contents:\n' + read_file(args_path))
+
+        run(command + build_path + ' cef_sandbox', chromium_src_dir, depot_tools_dir,
+            os.path.join(download_dir, 'build-%s-debug-sandbox.log' % (cef_branch)) \
+              if options.buildlogfile else None)
+
+  # Make a CEF Release build.
+  if not options.noreleasebuild:
+    build_path = os.path.join('out', get_build_directory_name(False))
+    args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+    msg(args_path + ' contents:\n' + read_file(args_path))
+
+    run(command + build_path + target, chromium_src_dir, depot_tools_dir,
+        os.path.join(download_dir, 'build-%s-release.log' % (cef_branch)) \
+          if options.buildlogfile else None)
+
+    if platform in sandbox_lib_platforms:
+      # Make the separate cef_sandbox build when GN is_official_build=true.
+      build_path += '_sandbox'
+      if os.path.exists(os.path.join(chromium_src_dir, build_path)):
+        args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
+        msg(args_path + ' contents:\n' + read_file(args_path))
+
+        run(command + build_path + ' cef_sandbox', chromium_src_dir, depot_tools_dir,
+            os.path.join(download_dir, 'build-%s-release-sandbox.log' % (cef_branch)) \
+              if options.buildlogfile else None)
+
+elif not options.nobuild:
+  msg('Not building. The source hashes have not changed and ' +
+      'the output folder "%s" already exists' % (out_src_dir))
+
+##
+# Run CEF tests.
+##
+
+if options.runtests:
+  if platform == 'windows':
+    test_exe = '%s.exe' % options.testtarget
+  elif platform == 'macosx':
+    test_exe = '%s.app/Contents/MacOS/%s' % (options.testtarget,
+                                             options.testtarget)
+  elif platform == 'linux':
+    test_exe = options.testtarget
+
+  test_prefix = options.testprefix
+  if len(test_prefix) > 0:
+    test_prefix += ' '
+
+  test_args = options.testargs
+  if len(test_args) > 0:
+    test_args = ' ' + test_args
+
+  if not options.nodebugtests:
+    build_path = os.path.join(out_src_dir, get_build_directory_name(True))
+    test_path = os.path.join(build_path, test_exe)
+    if os.path.exists(test_path):
+      run(test_prefix + test_path + test_args, build_path, depot_tools_dir)
+    else:
+      msg('Not running debug tests. Missing executable: %s' % test_path)
+
+  if not options.noreleasetests:
+    build_path = os.path.join(out_src_dir, get_build_directory_name(False))
+    test_path = os.path.join(build_path, test_exe)
+    if os.path.exists(test_path):
+      run(test_prefix + test_path + test_args, build_path, depot_tools_dir)
+    else:
+      msg('Not running release tests. Missing executable: %s' % test_path)
+
+##
+# Create the CEF binary distribution.
+##
+
+if not options.nodistrib and (chromium_checkout_changed or \
+                              cef_checkout_changed or options.forcedistrib):
+  if not options.forceclean and options.cleanartifacts:
+    # Clean the artifacts output directory.
+    artifacts_path = os.path.join(cef_src_dir, 'binary_distrib')
+    delete_directory(artifacts_path)
+
+  # Determine the requested distribution types.
+  distrib_types = []
+  if options.minimaldistribonly:
+    distrib_types.append('minimal')
+  elif options.clientdistribonly:
+    distrib_types.append('client')
+  elif options.sandboxdistribonly:
+    distrib_types.append('sandbox')
+  else:
+    distrib_types.append('standard')
+    if options.minimaldistrib:
+      distrib_types.append('minimal')
+    if options.clientdistrib:
+      distrib_types.append('client')
+    if options.sandboxdistrib:
+      distrib_types.append('sandbox')
+
+  cef_tools_dir = os.path.join(cef_src_dir, 'tools')
+
+  # Create the requested distribution types.
+  first_type = True
+  for type in distrib_types:
+    path = '%s make_distrib.py --output-dir=../binary_distrib/' % python_exe
+
+    if options.nodebugbuild or options.noreleasebuild or type != 'standard':
+      path += ' --allow-partial'
+    path = path + ' --ninja-build'
+    if options.x64build:
+      path += ' --x64-build'
+    elif options.armbuild:
+      path += ' --arm-build'
+    elif options.arm64build:
+      path += ' --arm64-build'
+
+    if type == 'minimal':
+      path += ' --minimal'
+    elif type == 'client':
+      path += ' --client'
+    elif type == 'sandbox':
+      path += ' --sandbox'
+
+    if first_type:
+      if options.nodistribdocs:
+        path += ' --no-docs'
+      if options.nodistribarchive:
+        path += ' --no-archive'
+      first_type = False
+    else:
+      # Don't create the symbol archives or documentation more than once.
+      path += ' --no-symbols --no-docs'
+
+    # Override the subdirectory name of binary_distrib if the caller requested.
+    if options.distribsubdir != '':
+      path += ' --distrib-subdir=' + options.distribsubdir
+
+    # Create the distribution.
+    run(path, cef_tools_dir, depot_tools_dir)